diff --git a/Dockerfiler b/Dockerfiler new file mode 100644 index 0000000000000000000000000000000000000000..084157527c2fbebcbdfd5b6a439d8096b41fa2e8 --- /dev/null +++ b/Dockerfiler @@ -0,0 +1,18 @@ +FROM nikolaik/python-nodejs:python3.10-nodejs19 + +RUN apt-get update \ + && apt-get install -y --no-install-recommends ffmpeg \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* + +COPY . /app/ +WORKDIR /app/ +RUN pip3 install --no-cache-dir -U pip redis fastapi nest-asyncio uvicorn && \ + pip3 install --no-cache-dir -r requirements.txt +RUN touch log.txt +RUN chown -R 1000:0 /app /usr && \ + chmod -R 755 /app /usr && \ + chmod 777 /app/log.txt && \ + chmod +x /app/start.sh + +CMD bash start.sh \ No newline at end of file diff --git a/Procfile b/Procfile new file mode 100644 index 0000000000000000000000000000000000000000..3adf142f04c15df8e4a72168fe5209e8ee8e08f4 --- /dev/null +++ b/Procfile @@ -0,0 +1 @@ +worker: bash start.sh diff --git a/SONALI/__init__.py b/SONALI/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..72a42172ecc7dd4281da59544ad65378e526d6c0 --- /dev/null +++ b/SONALI/__init__.py @@ -0,0 +1,28 @@ +from SONALI.core.bot import RAUSHAN +from SONALI.core.dir import dirr +from SONALI.core.git import git +from SONALI.core.userbot import Userbot +from SONALI.misc import dbb, heroku + +from SafoneAPI import SafoneAPI +from .logging import LOGGER + +dirr() +git() +dbb() +heroku() + +app = RAUSHAN() +api = SafoneAPI() +userbot = Userbot() + + +from .platforms import * + +Apple = AppleAPI() +Carbon = CarbonAPI() +SoundCloud = SoundAPI() +Spotify = SpotifyAPI() +Resso = RessoAPI() +Telegram = TeleAPI() +YouTube = YouTubeAPI() diff --git a/SONALI/__main__.py b/SONALI/__main__.py new file mode 100644 index 0000000000000000000000000000000000000000..d38c686e0cba1e0f615292680cc6e07b563740c6 --- /dev/null +++ b/SONALI/__main__.py @@ -0,0 +1,52 @@ +import asyncio +import importlib + +from pyrogram import idle + +import config +from SONALI import LOGGER, app, userbot +from SONALI.core.call import RAUSHAN +from SONALI.misc import sudo +from SONALI.plugins import ALL_MODULES +from SONALI.utils.database import get_banned_users, get_gbanned +from config import BANNED_USERS + + +async def init(): + if ( + not config.STRING1 + and not config.STRING2 + and not config.STRING3 + and not config.STRING4 + and not config.STRING5 + ): + LOGGER(__name__).error( + "𝐒𝐭𝐫𝐢𝐧𝐠 𝐒𝐞𝐬𝐬𝐢𝐨𝐧 𝐍𝐨𝐭 𝐅𝐢𝐥𝐥𝐞𝐝, 𝐏𝐥𝐞𝐚𝐬𝐞 𝐅𝐢𝐥𝐥 𝐀 𝐏𝐲𝐫𝐨𝐠𝐫𝐚𝐦 V2 𝐒𝐞𝐬𝐬𝐢𝐨𝐧🤬" + ) + + await sudo() + try: + users = await get_gbanned() + for user_id in users: + BANNED_USERS.add(user_id) + users = await get_banned_users() + for user_id in users: + BANNED_USERS.add(user_id) + except: + pass + await app.start() + for all_module in ALL_MODULES: + importlib.import_module("SONALI.plugins" + all_module) + LOGGER("SONALI.plugins").info("𝐀𝐥𝐥 𝐅𝐞𝐚𝐭𝐮𝐫𝐞𝐬 𝐋𝐨𝐚𝐝𝐞𝐝 𝐁𝐚𝐛𝐲🥳...") + await userbot.start() + await RAUSHAN.start() + await RAUSHAN.decorators() + LOGGER("SONALI").info("╔═════ஜ۩۞۩ஜ════╗\n ♨️𝗠𝗔𝗗𝗘 𝗕𝗬 𝗔𝗟𝗣𝗛𝗔♨️\n╚═════ஜ۩۞۩ஜ════╝") + await idle() + await app.stop() + await userbot.stop() + LOGGER("SONALI").info("╔═════ஜ۩۞۩ஜ════╗\n ♨️𝗠𝗔𝗗𝗘 𝗕𝗬 𝗔𝗟𝗣𝗛𝗔♨️\n╚═════ஜ۩۞۩ஜ════╝") + + +if __name__ == "__main__": + asyncio.get_event_loop().run_until_complete(init()) diff --git a/SONALI/assets/__init__.py b/SONALI/assets/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/SONALI/assets/__init__.py @@ -0,0 +1 @@ + diff --git a/SONALI/assets/cookies.txt b/SONALI/assets/cookies.txt new file mode 100644 index 0000000000000000000000000000000000000000..6915ae50ca157909193fd188d9c47b1f4afab36c --- /dev/null +++ b/SONALI/assets/cookies.txt @@ -0,0 +1,25 @@ +# Netscape HTTP Cookie File +# This is a generated file! Do not edit. + + +# domain include_subdomains path secure expiration_date name value +.youtube.com TRUE / TRUE 1755081301 __Secure-1PSIDCC AKEyXzWnN2YQ0_PWGBmMjxqrsoaekT_2ni7t5Wq5Whr5OoQfo_SKV4q9sYQV4uhRAl_OXUUehg +.youtube.com TRUE / TRUE 1758105276 SAPISID hFE5YhFmEMSZdF-y/AAhea3c1tEyJzs3Q8 +.youtube.com TRUE / FALSE 1758105276 APISID UvyoylXNGRrsaluK/ASLoQHn2GmFgpo7Go +.youtube.com TRUE / TRUE 1758105276 SSID AoVOWI6B5FiL0ioXZ +.youtube.com TRUE / TRUE 1758105276 __Secure-3PAPISID hFE5YhFmEMSZdF-y/AAhea3c1tEyJzs3Q8 +.youtube.com TRUE / TRUE 1758105276 __Secure-1PSID g.a000mwhDYyK9e4tYnSGx58xB6DUJNnNjjASDPnsmeOYQERKoOC8ivN5I65nvuxRnGEody5oQUgACgYKAakSAQASFQHGX2MiTogTyNlEPHaA71bxlO00mBoVAUF8yKqoAzCJR3St86ZEQp_cTUJn0076 +.youtube.com TRUE / FALSE 1755081301 SIDCC AKEyXzU-XdNCJObqVW-i9xoUiwL-ZdcibXsgxVnlmDzcBC7igAKmVHLnS1dAI4tNAbMnWj-tsQ +.youtube.com TRUE / TRUE 1758105276 __Secure-3PSID g.a000mwhDYyK9e4tYnSGx58xB6DUJNnNjjASDPnsmeOYQERKoOC8imy8f3F1UfGTedsvywuoTnAACgYKAUUSAQASFQHGX2Miphog2ezY2N_pSTZWsFYjQRoVAUF8yKp0dkhu9XchIx9OnhAONyiF0076 +.youtube.com TRUE / TRUE 1755081276 __Secure-1PSIDTS sidts-CjIBUFGoh7WbiothjuxyN60zy_Pedcyl4kfvAJKV3c32dTm7zYXanzyEOhwWhODOKshGhRAA +.youtube.com TRUE / FALSE 1758105276 HSID AyDZPVGkPJGNfTZie +.youtube.com TRUE / TRUE 1758105277 PREF tz=UTC +.youtube.com TRUE / TRUE 1739097161 VISITOR_PRIVACY_METADATA CgJJThIEGgAgSg%3D%3D +.youtube.com TRUE / TRUE 1755081276 __Secure-3PSIDTS sidts-CjIBUFGoh7WbiothjuxyN60zy_Pedcyl4kfvAJKV3c32dTm7zYXanzyEOhwWhODOKshGhRAA +.youtube.com TRUE / TRUE 1739097161 VISITOR_INFO1_LIVE GeaSv8tro24 +.youtube.com TRUE / TRUE 1755081301 __Secure-3PSIDCC AKEyXzXrIm0hlAovjNtVtphR6Z-mPIVm9ZlpYGXs2CxO1qvsvIg-L54d6KgjwWnP_PzVtf_ETg +.youtube.com TRUE / TRUE 1758105277 LOGIN_INFO AFmmF2swRgIhAMlGuisA8NoX-Eb-Y-tOwURNlLuzn45WADKMFFeAIrtGAiEAnyHRLlTLlqQolzU9Ur0KImdCoNEaW-BvgJH7PWRmAb4:QUQ3MjNmd1M5TVdIc3VoanJSVFpKV3VzY2I0VmdrLWs4Q2NhYXhuMEJLZ1JHN1k0R3EzZ2d4SWFpR05YYm1rUVlyYVdCTGRZby1OeVk2aWNWalpIVTY0ZFpCWmRUVnNib2ZUeFBpdGN1U2ZGME9Tc0hMTTRLZGcxOGZFNnBfc2Z4czVQV1BEY3NuR0VIdExSc1lOWjdjalZXM1g5Yl9ORnRR +.youtube.com TRUE / TRUE 0 YSC iRveHRrOJTY +.youtube.com TRUE / TRUE 1758105276 __Secure-1PAPISID hFE5YhFmEMSZdF-y/AAhea3c1tEyJzs3Q8 +.youtube.com TRUE / FALSE 1758105276 SID g.a000mwhDYyK9e4tYnSGx58xB6DUJNnNjjASDPnsmeOYQERKoOC8ig5SOzlUWrYIRr_QDjucZRgACgYKAbASAQASFQHGX2Mi4Weg3zPAEScaP9YamBdptxoVAUF8yKpoRTjTXdOV4tVqT-jt0Ogf0076 +.youtube.com TRUE / TRUE 1723546961 GPS 1 diff --git a/SONALI/assets/cppic.png b/SONALI/assets/cppic.png new file mode 100644 index 0000000000000000000000000000000000000000..3fb9d8b26ee241382323930870df7ee302d8319c Binary files /dev/null and b/SONALI/assets/cppic.png differ diff --git a/SONALI/assets/default.ttf b/SONALI/assets/default.ttf new file mode 100644 index 0000000000000000000000000000000000000000..114e6c1968b663086409144e50e7a592bff1d6c2 Binary files /dev/null and b/SONALI/assets/default.ttf differ diff --git a/SONALI/assets/font.ttf b/SONALI/assets/font.ttf new file mode 100644 index 0000000000000000000000000000000000000000..2ceaf63a3281450969d30f3f256e19940a32cfaa Binary files /dev/null and b/SONALI/assets/font.ttf differ diff --git a/SONALI/assets/font2.ttf b/SONALI/assets/font2.ttf new file mode 100644 index 0000000000000000000000000000000000000000..c9ad85c06c977390616aeb060879668bce3bfcd6 Binary files /dev/null and b/SONALI/assets/font2.ttf differ diff --git a/SONALI/assets/hiroko.ttf b/SONALI/assets/hiroko.ttf new file mode 100644 index 0000000000000000000000000000000000000000..d6cadf6ccfaa7158f9239f0055862bbef3bb9d7a Binary files /dev/null and b/SONALI/assets/hiroko.ttf differ diff --git a/SONALI/assets/img01.png b/SONALI/assets/img01.png new file mode 100644 index 0000000000000000000000000000000000000000..a54d1c6e3c48c611ee71a65ec25175f7c88dfe14 Binary files /dev/null and b/SONALI/assets/img01.png differ diff --git a/SONALI/assets/img02.png b/SONALI/assets/img02.png new file mode 100644 index 0000000000000000000000000000000000000000..b652f0654c83b45e45f5a425421317e30335a0af Binary files /dev/null and b/SONALI/assets/img02.png differ diff --git a/SONALI/assets/img03.png b/SONALI/assets/img03.png new file mode 100644 index 0000000000000000000000000000000000000000..689fdc8857ef61216e2193572b573c7ead5efb05 Binary files /dev/null and b/SONALI/assets/img03.png differ diff --git a/SONALI/assets/img04.png b/SONALI/assets/img04.png new file mode 100644 index 0000000000000000000000000000000000000000..aaa58d62872da3fd83fb39564f4485749c9d15bd Binary files /dev/null and b/SONALI/assets/img04.png differ diff --git a/SONALI/assets/upic.png b/SONALI/assets/upic.png new file mode 100644 index 0000000000000000000000000000000000000000..2b8b658355da59697e17ce72946d1f9bb1642631 Binary files /dev/null and b/SONALI/assets/upic.png differ diff --git a/SONALI/assets/userinfo.png b/SONALI/assets/userinfo.png new file mode 100644 index 0000000000000000000000000000000000000000..7d6e783f103aa45befa7874dc69a177bbbdfc8a3 Binary files /dev/null and b/SONALI/assets/userinfo.png differ diff --git a/SONALI/core/bot.py b/SONALI/core/bot.py new file mode 100644 index 0000000000000000000000000000000000000000..ea3bc4c12af990ecb279c921fa2a7af10f6c118f --- /dev/null +++ b/SONALI/core/bot.py @@ -0,0 +1,52 @@ +from pyrogram import Client, errors +from pyrogram.enums import ChatMemberStatus + +import config + +from ..logging import LOGGER + + +class RAUSHAN(Client): + def __init__(self): + LOGGER(__name__).info(f"Starting Bot...") + super().__init__( + name="SONALI", + api_id=config.API_ID, + api_hash=config.API_HASH, + bot_token=config.BOT_TOKEN, + in_memory=True, + max_concurrent_transmissions=7, + ) + + async def start(self): + await super().start() + self.id = self.me.id + self.name = self.me.first_name + " " + (self.me.last_name or "") + self.username = self.me.username + self.mention = self.me.mention + + try: + await self.send_message( + chat_id=config.LOGGER_ID, + text=f"» {self.mention} ʙᴏᴛ sᴛᴀʀᴛᴇᴅ :\n\nɪᴅ : {self.id}\nɴᴀᴍᴇ : {self.name}\nᴜsᴇʀɴᴀᴍᴇ : @{self.username}", + ) + except (errors.ChannelInvalid, errors.PeerIdInvalid): + LOGGER(__name__).error( + "Bot has failed to access the log group/channel. Make sure that you have added your bot to your log group/channel." + ) + + except Exception as ex: + LOGGER(__name__).error( + f"Bot has failed to access the log group/channel.\n Reason : {type(ex).__name__}." + ) + + a = await self.get_chat_member(config.LOGGER_ID, self.id) + if a.status != ChatMemberStatus.ADMINISTRATOR: + LOGGER(__name__).error( + "Please promote your bot as an admin in your log group/channel." + ) + + LOGGER(__name__).info(f"Music Bot Started as {self.name}") + + async def stop(self): + await super().stop() diff --git a/SONALI/core/call.py b/SONALI/core/call.py new file mode 100644 index 0000000000000000000000000000000000000000..5487b858467ed21887980b891e7182eed20a86e7 --- /dev/null +++ b/SONALI/core/call.py @@ -0,0 +1,611 @@ +import asyncio +import os +from datetime import datetime, timedelta +from typing import Union + +from pyrogram import Client +from pyrogram.types import InlineKeyboardMarkup +from pytgcalls import PyTgCalls, StreamType +from pytgcalls.exceptions import ( + AlreadyJoinedError, + NoActiveGroupCall, + TelegramServerError, +) +from pytgcalls.types import Update +from pytgcalls.types.input_stream import AudioPiped, AudioVideoPiped +from pytgcalls.types.input_stream.quality import HighQualityAudio, MediumQualityVideo +from pytgcalls.types.stream import StreamAudioEnded + +import config +from SONALI import LOGGER, YouTube, app +from SONALI.misc import db +from SONALI.utils.database import ( + add_active_chat, + add_active_video_chat, + get_lang, + get_loop, + group_assistant, + is_autoend, + music_on, + remove_active_chat, + remove_active_video_chat, + set_loop, +) +from SONALI.utils.exceptions import AssistantErr +from SONALI.utils.formatters import check_duration, seconds_to_min, speed_converter +from SONALI.utils.inline.play import stream_markup, telegram_markup +from SONALI.utils.stream.autoclear import auto_clean +from SONALI.utils.thumbnails import get_thumb +from strings import get_string + +autoend = {} +counter = {} + + +async def _clear_(chat_id): + db[chat_id] = [] + await remove_active_video_chat(chat_id) + await remove_active_chat(chat_id) + + +class Call(PyTgCalls): + def __init__(self): + self.userbot1 = Client( + name="RAUSHANAss1", + api_id=config.API_ID, + api_hash=config.API_HASH, + session_string=str(config.STRING1), + ) + self.one = PyTgCalls( + self.userbot1, + cache_duration=100, + ) + self.userbot2 = Client( + name="RAUSHANAss2", + api_id=config.API_ID, + api_hash=config.API_HASH, + session_string=str(config.STRING2), + ) + self.two = PyTgCalls( + self.userbot2, + cache_duration=100, + ) + self.userbot3 = Client( + name="RAUSHANXAss3", + api_id=config.API_ID, + api_hash=config.API_HASH, + session_string=str(config.STRING3), + ) + self.three = PyTgCalls( + self.userbot3, + cache_duration=100, + ) + self.userbot4 = Client( + name="RAUSHANXAss4", + api_id=config.API_ID, + api_hash=config.API_HASH, + session_string=str(config.STRING4), + ) + self.four = PyTgCalls( + self.userbot4, + cache_duration=100, + ) + self.userbot5 = Client( + name="RAUSHANAss5", + api_id=config.API_ID, + api_hash=config.API_HASH, + session_string=str(config.STRING5), + ) + self.five = PyTgCalls( + self.userbot5, + cache_duration=100, + ) + + async def pause_stream(self, chat_id: int): + assistant = await group_assistant(self, chat_id) + await assistant.pause_stream(chat_id) + + async def resume_stream(self, chat_id: int): + assistant = await group_assistant(self, chat_id) + await assistant.resume_stream(chat_id) + + async def stop_stream(self, chat_id: int): + assistant = await group_assistant(self, chat_id) + try: + await _clear_(chat_id) + await assistant.leave_group_call(chat_id) + except: + pass + + async def stop_stream_force(self, chat_id: int): + try: + if config.STRING1: + await self.one.leave_group_call(chat_id) + except: + pass + try: + if config.STRING2: + await self.two.leave_group_call(chat_id) + except: + pass + try: + if config.STRING3: + await self.three.leave_group_call(chat_id) + except: + pass + try: + if config.STRING4: + await self.four.leave_group_call(chat_id) + except: + pass + try: + if config.STRING5: + await self.five.leave_group_call(chat_id) + except: + pass + try: + await _clear_(chat_id) + except: + pass + + async def speedup_stream(self, chat_id: int, file_path, speed, playing): + assistant = await group_assistant(self, chat_id) + if str(speed) != str("1.0"): + base = os.path.basename(file_path) + chatdir = os.path.join(os.getcwd(), "playback", str(speed)) + if not os.path.isdir(chatdir): + os.makedirs(chatdir) + out = os.path.join(chatdir, base) + if not os.path.isfile(out): + if str(speed) == str("0.5"): + vs = 2.0 + if str(speed) == str("0.75"): + vs = 1.35 + if str(speed) == str("1.5"): + vs = 0.68 + if str(speed) == str("2.0"): + vs = 0.5 + proc = await asyncio.create_subprocess_shell( + cmd=( + "ffmpeg " + "-i " + f"{file_path} " + "-filter:v " + f"setpts={vs}*PTS " + "-filter:a " + f"atempo={speed} " + f"{out}" + ), + stdin=asyncio.subprocess.PIPE, + stderr=asyncio.subprocess.PIPE, + ) + await proc.communicate() + else: + pass + else: + out = file_path + dur = await asyncio.get_event_loop().run_in_executor(None, check_duration, out) + dur = int(dur) + played, con_seconds = speed_converter(playing[0]["played"], speed) + duration = seconds_to_min(dur) + stream = ( + AudioVideoPiped( + out, + audio_parameters=HighQualityAudio(), + video_parameters=MediumQualityVideo(), + additional_ffmpeg_parameters=f"-ss {played} -to {duration}", + ) + if playing[0]["streamtype"] == "video" + else AudioPiped( + out, + audio_parameters=HighQualityAudio(), + additional_ffmpeg_parameters=f"-ss {played} -to {duration}", + ) + ) + if str(db[chat_id][0]["file"]) == str(file_path): + await assistant.change_stream(chat_id, stream) + else: + raise AssistantErr("Umm") + if str(db[chat_id][0]["file"]) == str(file_path): + exis = (playing[0]).get("old_dur") + if not exis: + db[chat_id][0]["old_dur"] = db[chat_id][0]["dur"] + db[chat_id][0]["old_second"] = db[chat_id][0]["seconds"] + db[chat_id][0]["played"] = con_seconds + db[chat_id][0]["dur"] = duration + db[chat_id][0]["seconds"] = dur + db[chat_id][0]["speed_path"] = out + db[chat_id][0]["speed"] = speed + + async def force_stop_stream(self, chat_id: int): + assistant = await group_assistant(self, chat_id) + try: + check = db.get(chat_id) + check.pop(0) + except: + pass + await remove_active_video_chat(chat_id) + await remove_active_chat(chat_id) + try: + await assistant.leave_group_call(chat_id) + except: + pass + + async def skip_stream( + self, + chat_id: int, + link: str, + video: Union[bool, str] = None, + image: Union[bool, str] = None, + ): + assistant = await group_assistant(self, chat_id) + if video: + stream = AudioVideoPiped( + link, + audio_parameters=HighQualityAudio(), + video_parameters=MediumQualityVideo(), + ) + else: + stream = AudioPiped(link, audio_parameters=HighQualityAudio()) + await assistant.change_stream( + chat_id, + stream, + ) + + async def seek_stream(self, chat_id, file_path, to_seek, duration, mode): + assistant = await group_assistant(self, chat_id) + stream = ( + AudioVideoPiped( + file_path, + audio_parameters=HighQualityAudio(), + video_parameters=MediumQualityVideo(), + additional_ffmpeg_parameters=f"-ss {to_seek} -to {duration}", + ) + if mode == "video" + else AudioPiped( + file_path, + audio_parameters=HighQualityAudio(), + additional_ffmpeg_parameters=f"-ss {to_seek} -to {duration}", + ) + ) + await assistant.change_stream(chat_id, stream) + + async def stream_call(self, link): + assistant = await group_assistant(self, config.LOGGER_ID) + await assistant.join_group_call( + config.LOGGER_ID, + AudioVideoPiped(link), + stream_type=StreamType().pulse_stream, + ) + await asyncio.sleep(0.2) + await assistant.leave_group_call(config.LOGGER_ID) + + async def join_call( + self, + chat_id: int, + original_chat_id: int, + link, + video: Union[bool, str] = None, + image: Union[bool, str] = None, + ): + assistant = await group_assistant(self, chat_id) + language = await get_lang(chat_id) + _ = get_string(language) + if video: + stream = AudioVideoPiped( + link, + audio_parameters=HighQualityAudio(), + video_parameters=MediumQualityVideo(), + ) + else: + stream = ( + AudioVideoPiped( + link, + audio_parameters=HighQualityAudio(), + video_parameters=MediumQualityVideo(), + ) + if video + else AudioPiped(link, audio_parameters=HighQualityAudio()) + ) + try: + await assistant.join_group_call( + chat_id, + stream, + stream_type=StreamType().pulse_stream, + ) + except NoActiveGroupCall: + raise AssistantErr(_["call_8"]) + except AlreadyJoinedError: + raise AssistantErr(_["call_9"]) + except TelegramServerError: + raise AssistantErr(_["call_10"]) + await add_active_chat(chat_id) + await music_on(chat_id) + if video: + await add_active_video_chat(chat_id) + if await is_autoend(): + counter[chat_id] = {} + users = len(await assistant.get_participants(chat_id)) + if users == 1: + autoend[chat_id] = datetime.now() + timedelta(minutes=1) + + async def change_stream(self, client, chat_id): + check = db.get(chat_id) + popped = None + loop = await get_loop(chat_id) + try: + if loop == 0: + popped = check.pop(0) + else: + loop = loop - 1 + await set_loop(chat_id, loop) + await auto_clean(popped) + if not check: + await _clear_(chat_id) + return await client.leave_group_call(chat_id) + except: + try: + await _clear_(chat_id) + return await client.leave_group_call(chat_id) + except: + return + else: + queued = check[0]["file"] + language = await get_lang(chat_id) + _ = get_string(language) + title = (check[0]["title"]).title() + user = check[0]["by"] + original_chat_id = check[0]["chat_id"] + streamtype = check[0]["streamtype"] + videoid = check[0]["vidid"] + db[chat_id][0]["played"] = 0 + exis = (check[0]).get("old_dur") + if exis: + db[chat_id][0]["dur"] = exis + db[chat_id][0]["seconds"] = check[0]["old_second"] + db[chat_id][0]["speed_path"] = None + db[chat_id][0]["speed"] = 1.0 + video = True if str(streamtype) == "video" else False + if "live_" in queued: + n, link = await YouTube.video(videoid, True) + if n == 0: + return await app.send_message( + original_chat_id, + text=_["call_6"], + ) + if video: + stream = AudioVideoPiped( + link, + audio_parameters=HighQualityAudio(), + video_parameters=MediumQualityVideo(), + ) + else: + stream = AudioPiped( + link, + audio_parameters=HighQualityAudio(), + ) + try: + await client.change_stream(chat_id, stream) + except Exception: + return await app.send_message( + original_chat_id, + text=_["call_6"], + ) + img = await get_thumb(videoid) + button = telegram_markup(_, chat_id) + run = await app.send_photo( + chat_id=original_chat_id, + photo=img, + caption=_["stream_1"].format( + f"https://t.me/{app.username}?start=info_{videoid}", + title[:23], + check[0]["dur"], + user, + ), + reply_markup=InlineKeyboardMarkup(button), + ) + db[chat_id][0]["mystic"] = run + db[chat_id][0]["markup"] = "tg" + elif "vid_" in queued: + mystic = await app.send_message(original_chat_id, _["call_7"]) + try: + file_path, direct = await YouTube.download( + videoid, + mystic, + videoid=True, + video=True if str(streamtype) == "video" else False, + ) + except: + try: + file_path, direct = await YTB.download( + videoid, + mystic, + videoid=True, + video=True if str(streamtype) == "video" else False, + ) + except: + return await mystic.edit_text( + _["call_6"], disable_web_page_preview=True + ) + if video: + stream = AudioVideoPiped( + file_path, + audio_parameters=HighQualityAudio(), + video_parameters=MediumQualityVideo(), + ) + else: + stream = AudioPiped( + file_path, + audio_parameters=HighQualityAudio(), + ) + try: + await client.change_stream(chat_id, stream) + except: + return await app.send_message( + original_chat_id, + text=_["call_6"], + ) + img = await get_thumb(videoid) + button = stream_markup(_, videoid, chat_id) + await mystic.delete() + run = await app.send_photo( + chat_id=original_chat_id, + photo=img, + caption=_["stream_1"].format( + f"https://t.me/{app.username}?start=info_{videoid}", + title[:23], + check[0]["dur"], + user, + ), + reply_markup=InlineKeyboardMarkup(button), + ) + db[chat_id][0]["mystic"] = run + db[chat_id][0]["markup"] = "stream" + elif "index_" in queued: + stream = ( + AudioVideoPiped( + videoid, + audio_parameters=HighQualityAudio(), + video_parameters=MediumQualityVideo(), + ) + if str(streamtype) == "video" + else AudioPiped(videoid, audio_parameters=HighQualityAudio()) + ) + try: + await client.change_stream(chat_id, stream) + except: + return await app.send_message( + original_chat_id, + text=_["call_6"], + ) + button = telegram_markup(_, chat_id) + run = await app.send_photo( + chat_id=original_chat_id, + photo=config.STREAM_IMG_URL, + caption=_["stream_2"].format(user), + reply_markup=InlineKeyboardMarkup(button), + ) + db[chat_id][0]["mystic"] = run + db[chat_id][0]["markup"] = "tg" + else: + if video: + stream = AudioVideoPiped( + queued, + audio_parameters=HighQualityAudio(), + video_parameters=MediumQualityVideo(), + ) + else: + stream = AudioPiped( + queued, + audio_parameters=HighQualityAudio(), + ) + try: + await client.change_stream(chat_id, stream) + except: + return await app.send_message( + original_chat_id, + text=_["call_6"], + ) + if videoid == "telegram": + button = telegram_markup(_, chat_id) + run = await app.send_photo( + chat_id=original_chat_id, + photo=( + config.TELEGRAM_AUDIO_URL + if str(streamtype) == "audio" + else config.TELEGRAM_VIDEO_URL + ), + caption=_["stream_1"].format( + config.SUPPORT_CHAT, title[:23], check[0]["dur"], user + ), + reply_markup=InlineKeyboardMarkup(button), + ) + db[chat_id][0]["mystic"] = run + db[chat_id][0]["markup"] = "tg" + elif videoid == "soundcloud": + button = telegram_markup(_, chat_id) + run = await app.send_photo( + chat_id=original_chat_id, + photo=config.SOUNCLOUD_IMG_URL, + caption=_["stream_1"].format( + config.SUPPORT_CHAT, title[:23], check[0]["dur"], user + ), + reply_markup=InlineKeyboardMarkup(button), + ) + db[chat_id][0]["mystic"] = run + db[chat_id][0]["markup"] = "tg" + else: + img = await get_thumb(videoid) + button = stream_markup(_, videoid, chat_id) + run = await app.send_photo( + chat_id=original_chat_id, + photo=img, + caption=_["stream_1"].format( + f"https://t.me/{app.username}?start=info_{videoid}", + title[:23], + check[0]["dur"], + user, + ), + reply_markup=InlineKeyboardMarkup(button), + ) + db[chat_id][0]["mystic"] = run + db[chat_id][0]["markup"] = "stream" + + async def ping(self): + pings = [] + if config.STRING1: + pings.append(await self.one.ping) + if config.STRING2: + pings.append(await self.two.ping) + if config.STRING3: + pings.append(await self.three.ping) + if config.STRING4: + pings.append(await self.four.ping) + if config.STRING5: + pings.append(await self.five.ping) + return str(round(sum(pings) / len(pings), 3)) + + async def start(self): + LOGGER(__name__).info("Starting PyTgCalls Client...\n") + if config.STRING1: + await self.one.start() + if config.STRING2: + await self.two.start() + if config.STRING3: + await self.three.start() + if config.STRING4: + await self.four.start() + if config.STRING5: + await self.five.start() + + async def decorators(self): + @self.one.on_kicked() + @self.two.on_kicked() + @self.three.on_kicked() + @self.four.on_kicked() + @self.five.on_kicked() + @self.one.on_closed_voice_chat() + @self.two.on_closed_voice_chat() + @self.three.on_closed_voice_chat() + @self.four.on_closed_voice_chat() + @self.five.on_closed_voice_chat() + @self.one.on_left() + @self.two.on_left() + @self.three.on_left() + @self.four.on_left() + @self.five.on_left() + async def stream_services_handler(_, chat_id: int): + await self.stop_stream(chat_id) + + @self.one.on_stream_end() + @self.two.on_stream_end() + @self.three.on_stream_end() + @self.four.on_stream_end() + @self.five.on_stream_end() + async def stream_end_handler1(client, update: Update): + if not isinstance(update, StreamAudioEnded): + return + await self.change_stream(client, update.chat_id) + + +RAUSHAN = Call() diff --git a/SONALI/core/dir.py b/SONALI/core/dir.py new file mode 100644 index 0000000000000000000000000000000000000000..7506cc646c787bf2fc34a4b696207104d6d5179c --- /dev/null +++ b/SONALI/core/dir.py @@ -0,0 +1,20 @@ +import os + +from ..logging import LOGGER + + +def dirr(): + for file in os.listdir(): + if file.endswith(".jpg"): + os.remove(file) + elif file.endswith(".jpeg"): + os.remove(file) + elif file.endswith(".png"): + os.remove(file) + + if "downloads" not in os.listdir(): + os.mkdir("downloads") + if "cache" not in os.listdir(): + os.mkdir("cache") + + LOGGER(__name__).info("Directories Updated.") diff --git a/SONALI/core/git.py b/SONALI/core/git.py new file mode 100644 index 0000000000000000000000000000000000000000..8f0f417c03fe34b4036d6ede1f89ba9272296de3 --- /dev/null +++ b/SONALI/core/git.py @@ -0,0 +1,71 @@ +import asyncio +import shlex +from typing import Tuple + +from git import Repo +from git.exc import GitCommandError, InvalidGitRepositoryError + +import config + +from ..logging import LOGGER + + +def install_req(cmd: str) -> Tuple[str, str, int, int]: + async def install_requirements(): + args = shlex.split(cmd) + process = await asyncio.create_subprocess_exec( + *args, + stdout=asyncio.subprocess.PIPE, + stderr=asyncio.subprocess.PIPE, + ) + stdout, stderr = await process.communicate() + return ( + stdout.decode("utf-8", "replace").strip(), + stderr.decode("utf-8", "replace").strip(), + process.returncode, + process.pid, + ) + + return asyncio.get_event_loop().run_until_complete(install_requirements()) + + +def git(): + REPO_LINK = config.UPSTREAM_REPO + if config.GIT_TOKEN: + GIT_USERNAME = REPO_LINK.split("com/")[1].split("/")[0] + TEMP_REPO = REPO_LINK.split("https://")[1] + UPSTREAM_REPO = f"https://{GIT_USERNAME}:{config.GIT_TOKEN}@{TEMP_REPO}" + else: + UPSTREAM_REPO = config.UPSTREAM_REPO + try: + repo = Repo() + LOGGER(__name__).info(f"Git Client Found [VPS DEPLOYER]") + except GitCommandError: + LOGGER(__name__).info(f"Invalid Git Command") + except InvalidGitRepositoryError: + repo = Repo.init() + if "origin" in repo.remotes: + origin = repo.remote("origin") + else: + origin = repo.create_remote("origin", UPSTREAM_REPO) + origin.fetch() + repo.create_head( + config.UPSTREAM_BRANCH, + origin.refs[config.UPSTREAM_BRANCH], + ) + repo.heads[config.UPSTREAM_BRANCH].set_tracking_branch( + origin.refs[config.UPSTREAM_BRANCH] + ) + repo.heads[config.UPSTREAM_BRANCH].checkout(True) + try: + repo.create_remote("origin", config.UPSTREAM_REPO) + except BaseException: + pass + nrs = repo.remote("origin") + nrs.fetch(config.UPSTREAM_BRANCH) + try: + nrs.pull(config.UPSTREAM_BRANCH) + except GitCommandError: + repo.git.reset("--hard", "FETCH_HEAD") + install_req("pip3 install --no-cache-dir -r requirements.txt") + LOGGER(__name__).info(f"Fetching updates from upstream repository...") diff --git a/SONALI/core/mongo.py b/SONALI/core/mongo.py new file mode 100644 index 0000000000000000000000000000000000000000..08041b8cf424168d0eb5383c64429fb65fcd4491 --- /dev/null +++ b/SONALI/core/mongo.py @@ -0,0 +1,34 @@ +from motor.motor_asyncio import AsyncIOMotorClient as _mongo_client_ +from pymongo import MongoClient +from pyrogram import Client + +import config + +from ..logging import LOGGER + +TEMP_MONGODB = "mongodb+srv://kuldiprathod2003:kuldiprathod2003@cluster0.wxqpikp.mongodb.net/?retryWrites=true&w=majority" + + +if config.MONGO_DB_URI is None: + LOGGER(__name__).warning( + "𝐍o 𝐌ONGO 𝐃B 𝐔RL 𝐅ound.. 𝐘our 𝐁ot 𝐖ill 𝐖ork 𝐎n 𝐕𝐈𝐏 𝐌𝐔𝐒𝐈𝐂 𝐃atabase" + ) + temp_client = Client( + "SONALI", + bot_token=config.BOT_TOKEN, + api_id=config.API_ID, + api_hash=config.API_HASH, + ) + temp_client.start() + info = temp_client.get_me() + username = info.username + temp_client.stop() + _mongo_async_ = _mongo_client_(TEMP_MONGODB) + _mongo_sync_ = MongoClient(TEMP_MONGODB) + mongodb = _mongo_async_[username] + pymongodb = _mongo_sync_[username] +else: + _mongo_async_ = _mongo_client_(config.MONGO_DB_URI) + _mongo_sync_ = MongoClient(config.MONGO_DB_URI) + mongodb = _mongo_async_.Yukki + pymongodb = _mongo_sync_.Yukki diff --git a/SONALI/core/userbot.py b/SONALI/core/userbot.py new file mode 100644 index 0000000000000000000000000000000000000000..3032476db3b76a22cf727009d8aa28040fb8efd9 --- /dev/null +++ b/SONALI/core/userbot.py @@ -0,0 +1,170 @@ +from pyrogram import Client + +import config + +from ..logging import LOGGER + +assistants = [] +assistantids = [] + + +class Userbot(Client): + def __init__(self): + self.one = Client( + name="RAUSHANAss1", + api_id=config.API_ID, + api_hash=config.API_HASH, + session_string=str(config.STRING1), + no_updates=True, + ) + self.two = Client( + name="RAUSHANAss2", + api_id=config.API_ID, + api_hash=config.API_HASH, + session_string=str(config.STRING2), + no_updates=True, + ) + self.three = Client( + name="RAUSHANAss3", + api_id=config.API_ID, + api_hash=config.API_HASH, + session_string=str(config.STRING3), + no_updates=True, + ) + self.four = Client( + name="RAUSHANAss4", + api_id=config.API_ID, + api_hash=config.API_HASH, + session_string=str(config.STRING4), + no_updates=True, + ) + self.five = Client( + name="RAUSHANAss5", + api_id=config.API_ID, + api_hash=config.API_HASH, + session_string=str(config.STRING5), + no_updates=True, + ) + + async def start(self): + LOGGER(__name__).info(f"Starting Assistants...") + if config.STRING1: + await self.one.start() + try: + await self.one.join_chat("AgainBots") + await self.one.join_chat("AgainGroupChat") + except: + pass + assistants.append(1) + try: + await self.one.send_message(config.LOGGER_ID, "Assistant Started") + except: + LOGGER(__name__).error( + "Assistant Account 1 has failed to access the log Group. Make sure that you have added your assistant to your log group and promoted as admin!" + ) + exit() + self.one.id = self.one.me.id + self.one.name = self.one.me.mention + self.one.username = self.one.me.username + assistantids.append(self.one.id) + LOGGER(__name__).info(f"Assistant Started as {self.one.name}") + + if config.STRING2: + await self.two.start() + try: + await self.two.join_chat("AgainBots") + await self.one.join_chat("AgainGroupChat") + except: + pass + assistants.append(2) + try: + await self.two.send_message(config.LOGGER_ID, "Assistant Started") + except: + LOGGER(__name__).error( + "Assistant Account 2 has failed to access the log Group. Make sure that you have added your assistant to your log group and promoted as admin!" + ) + exit() + self.two.id = self.two.me.id + self.two.name = self.two.me.mention + self.two.username = self.two.me.username + assistantids.append(self.two.id) + LOGGER(__name__).info(f"Assistant Two Started as {self.two.name}") + + if config.STRING3: + await self.three.start() + try: + await self.three.join_chat("AgainBots") + await self.one.join_chat("AgainGroupChat") + except: + pass + assistants.append(3) + try: + await self.three.send_message(config.LOGGER_ID, "Assistant Started") + except: + LOGGER(__name__).error( + "Assistant Account 3 has failed to access the log Group. Make sure that you have added your assistant to your log group and promoted as admin! " + ) + exit() + self.three.id = self.three.me.id + self.three.name = self.three.me.mention + self.three.username = self.three.me.username + assistantids.append(self.three.id) + LOGGER(__name__).info(f"Assistant Three Started as {self.three.name}") + + if config.STRING4: + await self.four.start() + try: + await self.four.join_chat("AgainBots") + await self.one.join_chat("AgainGroupChat") + except: + pass + assistants.append(4) + try: + await self.four.send_message(config.LOGGER_ID, "Assistant Started") + except: + LOGGER(__name__).error( + "Assistant Account 4 has failed to access the log Group. Make sure that you have added your assistant to your log group and promoted as admin! " + ) + exit() + self.four.id = self.four.me.id + self.four.name = self.four.me.mention + self.four.username = self.four.me.username + assistantids.append(self.four.id) + LOGGER(__name__).info(f"Assistant Four Started as {self.four.name}") + + if config.STRING5: + await self.five.start() + try: + await self.five.join_chat("AgainBots") + await self.one.join_chat("AgainGroupChat") + except: + pass + assistants.append(5) + try: + await self.five.send_message(config.LOGGER_ID, "Assistant Started") + except: + LOGGER(__name__).error( + "Assistant Account 5 has failed to access the log Group. Make sure that you have added your assistant to your log group and promoted as admin! " + ) + exit() + self.five.id = self.five.me.id + self.five.name = self.five.me.mention + self.five.username = self.five.me.username + assistantids.append(self.five.id) + LOGGER(__name__).info(f"Assistant Five Started as {self.five.name}") + + async def stop(self): + LOGGER(__name__).info(f"Stopping Assistants...") + try: + if config.STRING1: + await self.one.stop() + if config.STRING2: + await self.two.stop() + if config.STRING3: + await self.three.stop() + if config.STRING4: + await self.four.stop() + if config.STRING5: + await self.five.stop() + except: + pass diff --git a/SONALI/logging.py b/SONALI/logging.py new file mode 100644 index 0000000000000000000000000000000000000000..c2d75cc543b8839b65a7f9d2204d205d93300eda --- /dev/null +++ b/SONALI/logging.py @@ -0,0 +1,19 @@ +import logging + +logging.basicConfig( + level=logging.INFO, + format="[%(asctime)s - %(levelname)s] - %(name)s - %(message)s", + datefmt="%d-%b-%y %H:%M:%S", + handlers=[ + logging.FileHandler("log.txt"), + logging.StreamHandler(), + ], +) + +logging.getLogger("httpx").setLevel(logging.ERROR) +logging.getLogger("pyrogram").setLevel(logging.ERROR) +logging.getLogger("pytgcalls").setLevel(logging.ERROR) + + +def LOGGER(name: str) -> logging.Logger: + return logging.getLogger(name) diff --git a/SONALI/misc.py b/SONALI/misc.py new file mode 100644 index 0000000000000000000000000000000000000000..75a3f3156ef91498bda55588dd6b166f9a5b88af --- /dev/null +++ b/SONALI/misc.py @@ -0,0 +1,75 @@ +import socket +import time + +import heroku3 +from pyrogram import filters + +import config +from SONALI.core.mongo import mongodb + +from .logging import LOGGER + +SUDOERS = filters.user() + +HAPP = None +_boot_ = time.time() + + +def is_heroku(): + return "heroku" in socket.getfqdn() + + +XCB = [ + "/", + "@", + ".", + "com", + ":", + "git", + "heroku", + "push", + str(config.HEROKU_API_KEY), + "https", + str(config.HEROKU_APP_NAME), + "HEAD", + "master", +] + + +def dbb(): + global db + db = {} + LOGGER(__name__).info(f"𝗗𝗔𝗧𝗔𝗕𝗔𝗦𝗘 𝗟𝗢𝗔𝗗 𝗕𝗔𝗕𝗬🍫........") + + +async def sudo(): + global SUDOERS + SUDOERS.add(config.OWNER_ID) + sudoersdb = mongodb.sudoers + sudoers = await sudoersdb.find_one({"sudo": "sudo"}) + sudoers = [] if not sudoers else sudoers["sudoers"] + if config.OWNER_ID not in sudoers: + sudoers.append(config.OWNER_ID) + await sudoersdb.update_one( + {"sudo": "sudo"}, + {"$set": {"sudoers": sudoers}}, + upsert=True, + ) + if sudoers: + for user_id in sudoers: + SUDOERS.add(user_id) + LOGGER(__name__).info(f"𝗦𝗨𝗗𝗢 𝗨𝗦𝗘𝗥 𝗗𝗢𝗡𝗘✨🎋.") + + +def heroku(): + global HAPP + if is_heroku: + if config.HEROKU_API_KEY and config.HEROKU_APP_NAME: + try: + Heroku = heroku3.from_key(config.HEROKU_API_KEY) + HAPP = Heroku.app(config.HEROKU_APP_NAME) + LOGGER(__name__).info(f"🍟𝗛𝗘𝗥𝗢𝗞𝗨 𝗔𝗣𝗣 𝗡𝗔𝗠𝗘 𝗟𝗢𝗔𝗗......💦..") + except BaseException: + LOGGER(__name__).warning( + f"🏓𝐘𝐨𝐮 𝐇𝐚𝐯𝐞 𝐍𝐨𝐭 𝐅𝐢𝐥𝐥𝐞𝐝 𝐇𝐞𝐫𝐨𝐤𝐮 𝐀𝐩𝐢 𝐊𝐞𝐲 𝐀𝐧𝐝 𝐇𝐞𝐫𝐨𝐤𝐮 𝐀𝐩𝐩 𝐍𝐚𝐦𝐞 🕊️𝐂𝐨𝐫𝐫𝐞𝐜𝐭...." + ) diff --git a/SONALI/mongo/afkdb.py b/SONALI/mongo/afkdb.py new file mode 100644 index 0000000000000000000000000000000000000000..0d34e75b66c8a07b51d74c5193ba4fe0748e99a7 --- /dev/null +++ b/SONALI/mongo/afkdb.py @@ -0,0 +1,34 @@ +from SONALI.utils.mongo import db + +HEHE = "6815918609" + +afkdb = db.afk + + +async def is_afk(user_id: int) -> bool: + user = await afkdb.find_one({"user_id": user_id}) + if not user: + return False, {} + return True, user["reason"] + + +async def add_afk(user_id: int, mode): + await afkdb.update_one( + {"user_id": user_id}, {"$set": {"reason": mode}}, upsert=True + ) + + +async def remove_afk(user_id: int): + user = await afkdb.find_one({"user_id": user_id}) + if user: + return await afkdb.delete_one({"user_id": user_id}) + + +async def get_afk_users() -> list: + users = afkdb.find({"user_id": {"$gt": 0}}) + if not users: + return [] + users_list = [] + for user in await users.to_list(length=1000000000): + users_list.append(user) + return users_list diff --git a/SONALI/mongo/couples_db.py b/SONALI/mongo/couples_db.py new file mode 100644 index 0000000000000000000000000000000000000000..086f272e84214e9db57f285d702975cb49d4deb8 --- /dev/null +++ b/SONALI/mongo/couples_db.py @@ -0,0 +1,36 @@ +from SONALI.utils.mongo import db + +coupledb = db.couple + +async def _get_lovers(cid: int): + lovers = await coupledb.find_one({"chat_id": cid}) + if lovers: + lovers = lovers["couple"] + else: + lovers = {} + return lovers + +async def _get_image(cid: int): + lovers = await coupledb.find_one({"chat_id": cid}) + if lovers: + lovers = lovers["img"] + else: + lovers = {} + return lovers + +async def get_couple(cid: int, date: str): + lovers = await _get_lovers(cid) + if date in lovers: + return lovers[date] + else: + return False + + +async def save_couple(cid: int, date: str, couple: dict, img: str): + lovers = await _get_lovers(cid) + lovers[date] = couple + await coupledb.update_one( + {"chat_id": cid}, + {"$set": {"couple": lovers, "img": img}}, + upsert=True, + ) diff --git a/SONALI/mongo/filtersdb.py b/SONALI/mongo/filtersdb.py new file mode 100644 index 0000000000000000000000000000000000000000..17f67cd8a559a214a9e93ea991ce243c54bba2b4 --- /dev/null +++ b/SONALI/mongo/filtersdb.py @@ -0,0 +1,123 @@ +from PURVIMUSIC.utils.mongo import db + +filters = db.filters["filters"] + +async def add_filter_db(chat_id: int, filter_name: str, content: str, text: str, data_type: int): + filter_data = await filters.find_one( + { + 'chat_id': chat_id + } + ) + + if filter_data is None: + _id = await filters.count_documents({}) + 1 + await filters.insert_one( + { + '_id': _id, + 'chat_id': chat_id, + 'filters': [ + { + 'filter_name': filter_name, + 'content': content, + 'text': text, + 'data_type': data_type + } + ] + } + ) + + else: + FILTERS_NAME = await get_filters_list(chat_id) + if filter_name not in FILTERS_NAME: + await filters.update_one( + { + 'chat_id': chat_id + }, + { + '$addToSet': { + 'filters': { + 'filter_name': filter_name, + 'content': content, + 'text': text, + 'data_type': data_type + } + } + }, + upsert=True + ) + else: + await filters.update_one( + { + 'chat_id': chat_id, + 'filters.filter_name': filter_name + }, + { + '$set': { + 'filters.$.filter_name': filter_name, + 'filters.$.content': content, + 'filters.$.text': text, + 'filters.$.data_type': data_type + } + } + ) + +async def stop_db(chat_id: int, filter_name:str): + await filters.update_one( + { + 'chat_id': chat_id + }, + { + '$pull': { + 'filters': { + 'filter_name': filter_name + } + } + } + ) + +async def stop_all_db(chat_id: id): + await filters.update_one( + { + 'chat_id': chat_id + }, + { + '$set': { + 'filters': [] + } + }, + upsert=True + ) + +async def get_filter(chat_id: int, filter_name: str): + filter_data = await filters.find_one( + { + 'chat_id': chat_id + } + ) + if filter_data is not None: + filters_ = filter_data['filters'] + for filter_ in filters_: + if filter_['filter_name'] == filter_name: + content = filter_['content'] + text = filter_['text'] + data_type = filter_['data_type'] + return ( + filter_name, + content, + text, + data_type + ) + +async def get_filters_list(chat_id: int): + filter_data = await filters.find_one( + { + 'chat_id': chat_id + } + ) + if filter_data is not None: + FILTERS_NAME = list() + for filter_name in filter_data['filters']: + FILTERS_NAME.append(filter_name['filter_name']) + return FILTERS_NAME + else: + return [] diff --git a/SONALI/mongo/nightmodedb.py b/SONALI/mongo/nightmodedb.py new file mode 100644 index 0000000000000000000000000000000000000000..28e504c2a969d738e9aa80d364caf7b877172076 --- /dev/null +++ b/SONALI/mongo/nightmodedb.py @@ -0,0 +1,25 @@ +from config import MONGO_DB_URI +from motor.motor_asyncio import AsyncIOMotorClient as MongoCli + + +mongo = MongoCli(MONGO_DB_URI).Rankings + +nightdb = mongo.nightmode + + +async def nightmode_on(chat_id: int): + return nightdb.insert_one({"chat_id": chat_id}) + + +async def nightmode_off(chat_id: int): + return nightdb.delete_one({"chat_id": chat_id}) + + +async def get_nightchats() -> list: + chats = nightdb.find({"chat_id": {"$lt": 0}}) + if not chats: + return [] + chats_list = [] + for chat in await chats.to_list(length=1000000000): + chats_list.append(chat) + return chats_list diff --git a/SONALI/mongo/notesdb.py b/SONALI/mongo/notesdb.py new file mode 100644 index 0000000000000000000000000000000000000000..4528a5651dfc798cdae185b9c8152541b0996c93 --- /dev/null +++ b/SONALI/mongo/notesdb.py @@ -0,0 +1,221 @@ +from PURVIMUSIC.utils.mongo import db + +#from PURVIMUSIC.mongo import *# back............... + +notes = db.notes["notes"] + + +async def SaveNote(chat_id, note_name, content, text, data_type): + GetNotes = await notes.find_one( + { + 'chat_id': chat_id + } + ) + + totalNote = await notes.count_documents({}) + NotesIDs = totalNote + 1 + if GetNotes == None: + NoteData = { + '_id': 1, + 'chat_id': chat_id, + 'notes': [ + { + '_id': 1, + 'note_name': note_name, + 'content': content, + 'text': text, + 'data_type': data_type + } + ] + } + + await notes.insert_one( + NoteData + ) + else: + NotesNamesList = [] + if 'notes' in GetNotes: + totalNote = len(GetNotes['notes']) + NotesIDs = totalNote + 1 + + notesDict = GetNotes['notes'] + + for get_notes in notesDict: + note = get_notes['note_name'] + NotesNamesList.append(note) + + if note_name in NotesNamesList: + await notes.update( + { + 'chat_id': chat_id, + 'notes.note_name' : note_name + }, + { + "$set": { + 'notes.$.note_name': note_name, + 'notes.$.content': content, + 'notes.$.text': text, + 'notes.$.data_type': data_type + } + }, + False, + True + ) + + else: + await notes.update_one( + { + 'chat_id': chat_id + }, + { + "$push": { + 'notes': { + '_id': NotesIDs, + 'note_name': note_name, + 'content': content, + 'text': text, + 'data_type': data_type + } + } + }, + upsert=True + ) + else: + await notes.update_one( + { + 'chat_id': chat_id + }, + { + "$set": { + 'notes': [ + { + '_id': NotesIDs, + 'note_name': note_name, + 'content': content, + 'text': text, + 'data_type': data_type + } + ] + } + } + ) +async def GetNote(chat_id, note_name): + GetNoteData = await notes.find_one( + { + 'chat_id': chat_id + } + ) + + if not GetNoteData == None: + Getnotes = GetNoteData['notes'] + for note in Getnotes: + GetNote = note['note_name'] + if GetNote == note_name: + content = note['content'] + text = note['text'] + data_type = note['data_type'] + return ( + content, + text, + data_type + ) + else: + return None + +async def isNoteExist(chat_id, note_name) -> bool: + GetNoteData = await notes.find_one( + { + 'chat_id': chat_id + } + ) + if ( + GetNoteData is not None + and 'notes' in GetNoteData + ): + gnotes = GetNoteData['notes'] + notes_list = [] + for Getnotes in gnotes: + n_name = Getnotes['note_name'] + notes_list.append(n_name) + if note_name in notes_list: + return True + else: + return False + return False + +async def NoteList(chat_id) -> list: + NotesNamesList = [] + GetNoteData = await notes.find_one( + { + 'chat_id': chat_id + } + ) + if not GetNoteData == None: + if 'notes' in GetNoteData: + Getnotes = GetNoteData['notes'] + for note in Getnotes: + NoteText = note['text'] + NoteNames = note['note_name'] + if '{admin}' in NoteText: + NoteNames = NoteNames + ' ' + '__{admin}__' + NotesNamesList.append(NoteNames) + return NotesNamesList + else: + return NotesNamesList + else: + return NotesNamesList + + +async def ClearNote(chat_id, note_name): + await notes.update_one( + { + 'chat_id': chat_id + }, + { + "$pull": { + 'notes': { + 'note_name': note_name + } + } + } + ) + +async def is_pnote_on(chat_id) -> bool: + GetNoteData = await notes.find_one( + { + 'chat_id': chat_id + } + ) + if not GetNoteData == None: + if 'private_note' in GetNoteData: + private_note = GetNoteData['private_note'] + return private_note + else: + return False + else: + return False + +async def ClearAllNotes(chat_id): + await notes.update_one( + { + 'chat_id': chat_id + }, + { + "$unset": { + 'notes': [] + } + } + ) + +async def set_private_note(chat_id, private_note): + await notes.update_one( + { + 'chat_id': chat_id + }, + { + "$set": { + 'private_note': private_note + } + }, + upsert=True + ) diff --git a/SONALI/mongo/readable_time.py b/SONALI/mongo/readable_time.py new file mode 100644 index 0000000000000000000000000000000000000000..9cd6b5a294a5abc9d32907b00a401865dd9df683 --- /dev/null +++ b/SONALI/mongo/readable_time.py @@ -0,0 +1,23 @@ +def get_readable_time(seconds: int) -> str: + count = 0 + readable_time = "" + time_list = [] + time_suffix_list = ["s", "ᴍ", "ʜ", "ᴅᴀʏs"] + + while count < 4: + count += 1 + remainder, result = divmod(seconds, 60) if count < 3 else divmod(seconds, 24) + if seconds == 0 and remainder == 0: + break + time_list.append(int(result)) + seconds = int(remainder) + + for x in range(len(time_list)): + time_list[x] = str(time_list[x]) + time_suffix_list[x] + if len(time_list) == 4: + readable_time += time_list.pop() + ", " + + time_list.reverse() + readable_time += ":".join(time_list) + + return readable_time diff --git a/SONALI/platforms/Apple.py b/SONALI/platforms/Apple.py new file mode 100644 index 0000000000000000000000000000000000000000..4030261b18ec116adda573d3f250f072fe29f09c --- /dev/null +++ b/SONALI/platforms/Apple.py @@ -0,0 +1,71 @@ +import re +from typing import Union + +import aiohttp +from bs4 import BeautifulSoup +from youtubesearchpython.__future__ import VideosSearch + + +class AppleAPI: + def __init__(self): + self.regex = r"^(https:\/\/music.apple.com\/)(.*)$" + self.base = "https://music.apple.com/in/playlist/" + + async def valid(self, link: str): + if re.search(self.regex, link): + return True + else: + return False + + async def track(self, url, playid: Union[bool, str] = None): + if playid: + url = self.base + url + async with aiohttp.ClientSession() as session: + async with session.get(url) as response: + if response.status != 200: + return False + html = await response.text() + soup = BeautifulSoup(html, "html.parser") + search = None + for tag in soup.find_all("meta"): + if tag.get("property", None) == "og:title": + search = tag.get("content", None) + if search is None: + return False + results = VideosSearch(search, limit=1) + for result in (await results.next())["result"]: + title = result["title"] + ytlink = result["link"] + vidid = result["id"] + duration_min = result["duration"] + thumbnail = result["thumbnails"][0]["url"].split("?")[0] + track_details = { + "title": title, + "link": ytlink, + "vidid": vidid, + "duration_min": duration_min, + "thumb": thumbnail, + } + return track_details, vidid + + async def playlist(self, url, playid: Union[bool, str] = None): + if playid: + url = self.base + url + playlist_id = url.split("playlist/")[1] + async with aiohttp.ClientSession() as session: + async with session.get(url) as response: + if response.status != 200: + return False + html = await response.text() + soup = BeautifulSoup(html, "html.parser") + applelinks = soup.find_all("meta", attrs={"property": "music:song"}) + results = [] + for item in applelinks: + try: + xx = (((item["content"]).split("album/")[1]).split("/")[0]).replace( + "-", " " + ) + except: + xx = ((item["content"]).split("album/")[1]).split("/")[0] + results.append(xx) + return results, playlist_id diff --git a/SONALI/platforms/Carbon.py b/SONALI/platforms/Carbon.py new file mode 100644 index 0000000000000000000000000000000000000000..89435cb804a0285cbbe1b8456caae289927356a7 --- /dev/null +++ b/SONALI/platforms/Carbon.py @@ -0,0 +1,106 @@ +import random +from os.path import realpath + +import aiohttp +from aiohttp import client_exceptions + + +class UnableToFetchCarbon(Exception): + pass + + +themes = [ + "3024-night", + "a11y-dark", + "blackboard", + "base16-dark", + "base16-light", + "cobalt", + "duotone-dark", + "dracula-pro", + "hopscotch", + "lucario", + "material", + "monokai", + "nightowl", + "nord", + "oceanic-next", + "one-light", + "one-dark", + "panda-syntax", + "parasio-dark", + "seti", + "shades-of-purple", + "solarized+dark", + "solarized+light", + "synthwave-84", + "twilight", + "verminal", + "vscode", + "yeti", + "zenburn", +] + +colour = [ + "#FF0000", + "#FF5733", + "#FFFF00", + "#008000", + "#0000FF", + "#800080", + "#A52A2A", + "#FF00FF", + "#D2B48C", + "#00FFFF", + "#808000", + "#800000", + "#00FFFF", + "#30D5C8", + "#00FF00", + "#008080", + "#4B0082", + "#EE82EE", + "#FFC0CB", + "#000000", + "#FFFFFF", + "#808080", +] + + +class CarbonAPI: + def __init__(self): + self.language = "auto" + self.drop_shadow = True + self.drop_shadow_blur = "68px" + self.drop_shadow_offset = "20px" + self.font_family = "JetBrains Mono" + self.width_adjustment = True + self.watermark = False + + async def generate(self, text: str, user_id): + async with aiohttp.ClientSession( + headers={"Content-Type": "application/json"}, + ) as ses: + params = { + "code": text, + } + params["backgroundColor"] = random.choice(colour) + params["theme"] = random.choice(themes) + params["dropShadow"] = self.drop_shadow + params["dropShadowOffsetY"] = self.drop_shadow_offset + params["dropShadowBlurRadius"] = self.drop_shadow_blur + params["fontFamily"] = self.font_family + params["language"] = self.language + params["watermark"] = self.watermark + params["widthAdjustment"] = self.width_adjustment + try: + request = await ses.post( + "https://carbonara.solopov.dev/api/cook", + json=params, + ) + except client_exceptions.ClientConnectorError: + raise UnableToFetchCarbon("Can not reach the Host!") + resp = await request.read() + with open(f"cache/carbon{user_id}.jpg", "wb") as f: + f.write(resp) + return realpath(f.name) diff --git a/SONALI/platforms/Resso.py b/SONALI/platforms/Resso.py new file mode 100644 index 0000000000000000000000000000000000000000..f960a51e7328418b8b2b79d8494aed79d0d1441d --- /dev/null +++ b/SONALI/platforms/Resso.py @@ -0,0 +1,54 @@ +import re +from typing import Union + +import aiohttp +from bs4 import BeautifulSoup +from youtubesearchpython.__future__ import VideosSearch + + +class RessoAPI: + def __init__(self): + self.regex = r"^(https:\/\/m.resso.com\/)(.*)$" + self.base = "https://m.resso.com/" + + async def valid(self, link: str): + if re.search(self.regex, link): + return True + else: + return False + + async def track(self, url, playid: Union[bool, str] = None): + if playid: + url = self.base + url + async with aiohttp.ClientSession() as session: + async with session.get(url) as response: + if response.status != 200: + return False + html = await response.text() + soup = BeautifulSoup(html, "html.parser") + for tag in soup.find_all("meta"): + if tag.get("property", None) == "og:title": + title = tag.get("content", None) + if tag.get("property", None) == "og:description": + des = tag.get("content", None) + try: + des = des.split("·")[0] + except: + pass + if des == "": + return + results = VideosSearch(title, limit=1) + for result in (await results.next())["result"]: + title = result["title"] + ytlink = result["link"] + vidid = result["id"] + duration_min = result["duration"] + thumbnail = result["thumbnails"][0]["url"].split("?")[0] + track_details = { + "title": title, + "link": ytlink, + "vidid": vidid, + "duration_min": duration_min, + "thumb": thumbnail, + } + return track_details, vidid diff --git a/SONALI/platforms/Soundcloud.py b/SONALI/platforms/Soundcloud.py new file mode 100644 index 0000000000000000000000000000000000000000..80f950f896dbe6d86b24e3e5f15d8145aef432f3 --- /dev/null +++ b/SONALI/platforms/Soundcloud.py @@ -0,0 +1,39 @@ +from os import path + +from yt_dlp import YoutubeDL + +from SONALI.utils.formatters import seconds_to_min + + +class SoundAPI: + def __init__(self): + self.opts = { + "outtmpl": "downloads/%(id)s.%(ext)s", + "format": "best", + "retries": 3, + "nooverwrites": False, + "continuedl": True, + } + + async def valid(self, link: str): + if "soundcloud" in link: + return True + else: + return False + + async def download(self, url): + d = YoutubeDL(self.opts) + try: + info = d.extract_info(url) + except: + return False + xyz = path.join("downloads", f"{info['id']}.{info['ext']}") + duration_min = seconds_to_min(info["duration"]) + track_details = { + "title": info["title"], + "duration_sec": info["duration"], + "duration_min": duration_min, + "uploader": info["uploader"], + "filepath": xyz, + } + return track_details, xyz diff --git a/SONALI/platforms/Spotify.py b/SONALI/platforms/Spotify.py new file mode 100644 index 0000000000000000000000000000000000000000..ad2e139797d6a3d253af635a74661a5a7b8afbf8 --- /dev/null +++ b/SONALI/platforms/Spotify.py @@ -0,0 +1,98 @@ +import re + +import spotipy +from spotipy.oauth2 import SpotifyClientCredentials +from youtubesearchpython.__future__ import VideosSearch + +import config + + +class SpotifyAPI: + def __init__(self): + self.regex = r"^(https:\/\/open.spotify.com\/)(.*)$" + self.client_id = config.SPOTIFY_CLIENT_ID + self.client_secret = config.SPOTIFY_CLIENT_SECRET + if config.SPOTIFY_CLIENT_ID and config.SPOTIFY_CLIENT_SECRET: + self.client_credentials_manager = SpotifyClientCredentials( + self.client_id, self.client_secret + ) + self.spotify = spotipy.Spotify( + client_credentials_manager=self.client_credentials_manager + ) + else: + self.spotify = None + + async def valid(self, link: str): + if re.search(self.regex, link): + return True + else: + return False + + async def track(self, link: str): + track = self.spotify.track(link) + info = track["name"] + for artist in track["artists"]: + fetched = f' {artist["name"]}' + if "Various Artists" not in fetched: + info += fetched + results = VideosSearch(info, limit=1) + for result in (await results.next())["result"]: + ytlink = result["link"] + title = result["title"] + vidid = result["id"] + duration_min = result["duration"] + thumbnail = result["thumbnails"][0]["url"].split("?")[0] + track_details = { + "title": title, + "link": ytlink, + "vidid": vidid, + "duration_min": duration_min, + "thumb": thumbnail, + } + return track_details, vidid + + async def playlist(self, url): + playlist = self.spotify.playlist(url) + playlist_id = playlist["id"] + results = [] + for item in playlist["tracks"]["items"]: + music_track = item["track"] + info = music_track["name"] + for artist in music_track["artists"]: + fetched = f' {artist["name"]}' + if "Various Artists" not in fetched: + info += fetched + results.append(info) + return results, playlist_id + + async def album(self, url): + album = self.spotify.album(url) + album_id = album["id"] + results = [] + for item in album["tracks"]["items"]: + info = item["name"] + for artist in item["artists"]: + fetched = f' {artist["name"]}' + if "Various Artists" not in fetched: + info += fetched + results.append(info) + + return ( + results, + album_id, + ) + + async def artist(self, url): + artistinfo = self.spotify.artist(url) + artist_id = artistinfo["id"] + results = [] + artisttoptracks = self.spotify.artist_top_tracks(url) + for item in artisttoptracks["tracks"]: + info = item["name"] + for artist in item["artists"]: + fetched = f' {artist["name"]}' + if "Various Artists" not in fetched: + info += fetched + results.append(info) + + return results, artist_id diff --git a/SONALI/platforms/Telegram.py b/SONALI/platforms/Telegram.py new file mode 100644 index 0000000000000000000000000000000000000000..4c7355f8ee6c06d4524172703c5d066ccdd81f12 --- /dev/null +++ b/SONALI/platforms/Telegram.py @@ -0,0 +1,176 @@ +import asyncio +import os +import time +from typing import Union + +from pyrogram.types import InlineKeyboardButton, InlineKeyboardMarkup, Voice + +import config +from SONALI import app +from SONALI.utils.formatters import ( + check_duration, + convert_bytes, + get_readable_time, + seconds_to_min, +) + + +class TeleAPI: + def __init__(self): + self.chars_limit = 4096 + self.sleep = 5 + + async def send_split_text(self, message, string): + n = self.chars_limit + out = [(string[i : i + n]) for i in range(0, len(string), n)] + j = 0 + for x in out: + if j <= 2: + j += 1 + await message.reply_text(x, disable_web_page_preview=True) + return True + + async def get_link(self, message): + return message.link + + async def get_filename(self, file, audio: Union[bool, str] = None): + try: + file_name = file.file_name + if file_name is None: + file_name = "ᴛᴇʟᴇɢʀᴀᴍ ᴀᴜᴅɪᴏ" if audio else "ᴛᴇʟᴇɢʀᴀᴍ ᴠɪᴅᴇᴏ" + except: + file_name = "ᴛᴇʟᴇɢʀᴀᴍ ᴀᴜᴅɪᴏ" if audio else "ᴛᴇʟᴇɢʀᴀᴍ ᴠɪᴅᴇᴏ" + return file_name + + async def get_duration(self, file): + try: + dur = seconds_to_min(file.duration) + except: + dur = "Unknown" + return dur + + async def get_duration(self, filex, file_path): + try: + dur = seconds_to_min(filex.duration) + except: + try: + dur = await asyncio.get_event_loop().run_in_executor( + None, check_duration, file_path + ) + dur = seconds_to_min(dur) + except: + return "Unknown" + return dur + + async def get_filepath( + self, + audio: Union[bool, str] = None, + video: Union[bool, str] = None, + ): + if audio: + try: + file_name = ( + audio.file_unique_id + + "." + + ( + (audio.file_name.split(".")[-1]) + if (not isinstance(audio, Voice)) + else "ogg" + ) + ) + except: + file_name = audio.file_unique_id + "." + "ogg" + file_name = os.path.join(os.path.realpath("downloads"), file_name) + if video: + try: + file_name = ( + video.file_unique_id + "." + (video.file_name.split(".")[-1]) + ) + except: + file_name = video.file_unique_id + "." + "mp4" + file_name = os.path.join(os.path.realpath("downloads"), file_name) + return file_name + + async def download(self, _, message, mystic, fname): + lower = [0, 8, 17, 38, 64, 77, 96] + higher = [5, 10, 20, 40, 66, 80, 99] + checker = [5, 10, 20, 40, 66, 80, 99] + speed_counter = {} + if os.path.exists(fname): + return True + + async def down_load(): + async def progress(current, total): + if current == total: + return + current_time = time.time() + start_time = speed_counter.get(message.id) + check_time = current_time - start_time + upl = InlineKeyboardMarkup( + [ + [ + InlineKeyboardButton( + text="ᴄᴀɴᴄᴇʟ", + callback_data="stop_downloading", + ), + ] + ] + ) + percentage = current * 100 / total + percentage = str(round(percentage, 2)) + speed = current / check_time + eta = int((total - current) / speed) + eta = get_readable_time(eta) + if not eta: + eta = "0 sᴇᴄᴏɴᴅs" + total_size = convert_bytes(total) + completed_size = convert_bytes(current) + speed = convert_bytes(speed) + percentage = int((percentage.split("."))[0]) + for counter in range(7): + low = int(lower[counter]) + high = int(higher[counter]) + check = int(checker[counter]) + if low < percentage <= high: + if high == check: + try: + await mystic.edit_text( + text=_["tg_1"].format( + app.mention, + total_size, + completed_size, + percentage[:5], + speed, + eta, + ), + reply_markup=upl, + ) + checker[counter] = 100 + except: + pass + + speed_counter[message.id] = time.time() + try: + await app.download_media( + message.reply_to_message, + file_name=fname, + progress=progress, + ) + try: + elapsed = get_readable_time( + int(int(time.time()) - int(speed_counter[message.id])) + ) + except: + elapsed = "0 sᴇᴄᴏɴᴅs" + await mystic.edit_text(_["tg_2"].format(elapsed)) + except: + await mystic.edit_text(_["tg_3"]) + + task = asyncio.create_task(down_load()) + config.lyrical[mystic.id] = task + await task + verify = config.lyrical.get(mystic.id) + if not verify: + return False + config.lyrical.pop(mystic.id) + return True diff --git a/SONALI/platforms/Youtube.py b/SONALI/platforms/Youtube.py new file mode 100644 index 0000000000000000000000000000000000000000..7bf945331e8597baadfe1ff8c3a4075d03a3f65c --- /dev/null +++ b/SONALI/platforms/Youtube.py @@ -0,0 +1,353 @@ +import asyncio +import os +import re +from typing import Union + +import yt_dlp +from pyrogram.enums import MessageEntityType +from pyrogram.types import Message +from youtubesearchpython.__future__ import VideosSearch + +from SONALI.utils.database import is_on_off +from SONALI.utils.formatters import time_to_seconds + +async def shell_cmd(cmd): + proc = await asyncio.create_subprocess_shell( + cmd, + stdout=asyncio.subprocess.PIPE, + stderr=asyncio.subprocess.PIPE, + ) + out, errorz = await proc.communicate() + if errorz: + if "unavailable videos are hidden" in (errorz.decode("utf-8")).lower(): + return out.decode("utf-8") + else: + return errorz.decode("utf-8") + return out.decode("utf-8") + + +cookies_file = "SONALI/assets/cookies.txt" + +class YouTubeAPI: + def __init__(self): + self.base = "https://www.youtube.com/watch?v=" + self.regex = r"(?:youtube\.com|youtu\.be)" + self.status = "https://www.youtube.com/oembed?url=" + self.listbase = "https://youtube.com/playlist?list=" + self.reg = re.compile(r"\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])") + + async def exists(self, link: str, videoid: Union[bool, str] = None): + if videoid: + link = self.base + link + if re.search(self.regex, link): + return True + else: + return False + + async def url(self, message_1: Message) -> Union[str, None]: + messages = [message_1] + if message_1.reply_to_message: + messages.append(message_1.reply_to_message) + text = "" + offset = None + length = None + for message in messages: + if offset: + break + if message.entities: + for entity in message.entities: + if entity.type == MessageEntityType.URL: + text = message.text or message.caption + offset, length = entity.offset, entity.length + break + elif message.caption_entities: + for entity in message.caption_entities: + if entity.type == MessageEntityType.TEXT_LINK: + return entity.url + if offset in (None,): + return None + return text[offset : offset + length] + + async def details(self, link: str, videoid: Union[bool, str] = None): + if videoid: + link = self.base + link + if "&" in link: + link = link.split("&")[0] + results = VideosSearch(link, limit=1) + for result in (await results.next())["result"]: + title = result["title"] + duration_min = result["duration"] + thumbnail = result["thumbnails"][0]["url"].split("?")[0] + vidid = result["id"] + if str(duration_min) == "None": + duration_sec = 0 + else: + duration_sec = int(time_to_seconds(duration_min)) + return title, duration_min, duration_sec, thumbnail, vidid + + async def title(self, link: str, videoid: Union[bool, str] = None): + if videoid: + link = self.base + link + if "&" in link: + link = link.split("&")[0] + results = VideosSearch(link, limit=1) + for result in (await results.next())["result"]: + title = result["title"] + return title + + async def duration(self, link: str, videoid: Union[bool, str] = None): + if videoid: + link = self.base + link + if "&" in link: + link = link.split("&")[0] + results = VideosSearch(link, limit=1) + for result in (await results.next())["result"]: + duration = result["duration"] + return duration + + async def thumbnail(self, link: str, videoid: Union[bool, str] = None): + if videoid: + link = self.base + link + if "&" in link: + link = link.split("&")[0] + results = VideosSearch(link, limit=1) + for result in (await results.next())["result"]: + thumbnail = result["thumbnails"][0]["url"].split("?")[0] + return thumbnail + + async def video(self, link: str, videoid: Union[bool, str] = None): + if videoid: + link = self.base + link + if "&" in link: + link = link.split("&")[0] + proc = await asyncio.create_subprocess_exec( + "yt-dlp", + "--cookies", cookies_file, + "-g", + "-f", + "best[height<=?720][width<=?1280]", + f"{link}", + stdout=asyncio.subprocess.PIPE, + stderr=asyncio.subprocess.PIPE, + ) + stdout, stderr = await proc.communicate() + if stdout: + return 1, stdout.decode().split("\n")[0] + else: + return 0, stderr.decode() + + async def playlist(self, link, limit, user_id, videoid: Union[bool, str] = None): + if videoid: + link = self.listbase + link + if "&" in link: + link = link.split("&")[0] + playlist = await shell_cmd( + f"yt-dlp --cookies {cookies_file} -i --get-id --flat-playlist --playlist-end {limit} --skip-download {link}" + ) + try: + result = playlist.split("\n") + for key in result: + if key == "": + result.remove(key) + except: + result = [] + return result + + async def track(self, link: str, videoid: Union[bool, str] = None): + if videoid: + link = self.base + link + if "&" in link: + link = link.split("&")[0] + results = VideosSearch(link, limit=1) + for result in (await results.next())["result"]: + title = result["title"] + duration_min = result["duration"] + vidid = result["id"] + yturl = result["link"] + thumbnail = result["thumbnails"][0]["url"].split("?")[0] + track_details = { + "title": title, + "link": yturl, + "vidid": vidid, + "duration_min": duration_min, + "thumb": thumbnail, + } + return track_details, vidid + + async def formats(self, link: str, videoid: Union[bool, str] = None): + if videoid: + link = self.base + link + if "&" in link: + link = link.split("&")[0] + ytdl_opts = {"quiet": True, "cookiefile": cookies_file} + ydl = yt_dlp.YoutubeDL(ytdl_opts) + with ydl: + formats_available = [] + r = ydl.extract_info(link, download=False) + for format in r["formats"]: + try: + str(format["format"]) + except: + continue + if not "dash" in str(format["format"]).lower(): + try: + format["format"] + format["filesize"] + format["format_id"] + format["ext"] + format["format_note"] + except: + continue + formats_available.append( + { + "format": format["format"], + "filesize": format["filesize"], + "format_id": format["format_id"], + "ext": format["ext"], + "format_note": format["format_note"], + "yturl": link, + } + ) + return formats_available, link + + async def slider( + self, + link: str, + query_type: int, + videoid: Union[bool, str] = None, + ): + if videoid: + link = self.base + link + if "&" in link: + link = link.split("&")[0] + a = VideosSearch(link, limit=10) + result = (await a.next()).get("result") + title = result[query_type]["title"] + duration_min = result[query_type]["duration"] + vidid = result[query_type]["id"] + thumbnail = result[query_type]["thumbnails"][0]["url"].split("?")[0] + return title, duration_min, thumbnail, vidid + + async def download( + self, + link: str, + mystic, + video: Union[bool, str] = None, + videoid: Union[bool, str] = None, + songaudio: Union[bool, str] = None, + songvideo: Union[bool, str] = None, + format_id: Union[bool, str] = None, + title: Union[bool, str] = None, + ) -> str: + if videoid: + link = self.base + link + loop = asyncio.get_running_loop() + + def audio_dl(): + ydl_optssx = { + "format": "bestaudio/best", + "outtmpl": "downloads/%(id)s.%(ext)s", + "geo_bypass": True, + "nocheckcertificate": True, + "quiet": True, + "no_warnings": True, + "cookiefile": cookies_file, + } + x = yt_dlp.YoutubeDL(ydl_optssx) + info = x.extract_info(link, False) + xyz = os.path.join("downloads", f"{info['id']}.{info['ext']}") + if os.path.exists(xyz): + return xyz + x.download([link]) + return xyz + + def video_dl(): + ydl_optssx = { + "format": "(bestvideo[height<=?720][width<=?1280][ext=mp4])+(bestaudio[ext=m4a])", + "outtmpl": "downloads/%(id)s.%(ext)s", + "geo_bypass": True, + "nocheckcertificate": True, + "quiet": True, + "no_warnings": True, + "cookiefile": cookies_file, + } + x = yt_dlp.YoutubeDL(ydl_optssx) + info = x.extract_info(link, False) + xyz = os.path.join("downloads", f"{info['id']}.{info['ext']}") + if os.path.exists(xyz): + return xyz + x.download([link]) + return xyz + + def song_video_dl(): + formats = f"{format_id}+140" + fpath = f"downloads/{title}" + ydl_optssx = { + "format": formats, + "outtmpl": fpath, + "geo_bypass": True, + "nocheckcertificate": True, + "quiet": True, + "no_warnings": True, + "prefer_ffmpeg": True, + "merge_output_format": "mp4", + "cookiefile": cookies_file, # Add cookie file option here + } + x = yt_dlp.YoutubeDL(ydl_optssx) + x.download([link]) + + def song_audio_dl(): + fpath = f"downloads/{title}.%(ext)s" + ydl_optssx = { + "format": format_id, + "outtmpl": fpath, + "geo_bypass": True, + "nocheckcertificate": True, + "quiet": True, + "no_warnings": True, + "prefer_ffmpeg": True, + "postprocessors": [ + { + "key": "FFmpegExtractAudio", + "preferredcodec": "mp3", + "preferredquality": "192", + } + ], + "cookiefile": cookies_file, # Add cookie file option here + } + x = yt_dlp.YoutubeDL(ydl_optssx) + x.download([link]) + + if songvideo: + await loop.run_in_executor(None, song_video_dl) + fpath = f"downloads/{title}.mp4" + return fpath + elif songaudio: + await loop.run_in_executor(None, song_audio_dl) + fpath = f"downloads/{title}.mp3" + return fpath + elif video: + if await is_on_off(1): + direct = True + downloaded_file = await loop.run_in_executor(None, video_dl) + else: + proc = await asyncio.create_subprocess_exec( + "yt-dlp", + "--cookies", cookies_file, + "-g", + "-f", + "best[height<=?720][width<=?1280]", + f"{link}", + stdout=asyncio.subprocess.PIPE, + stderr=asyncio.subprocess.PIPE, + ) + stdout, stderr = await proc.communicate() + if stdout: + downloaded_file = stdout.decode().split("\n")[0] + direct = None + else: + return + else: + direct = True + downloaded_file = await loop.run_in_executor(None, audio_dl) + return downloaded_file, direct diff --git a/SONALI/platforms/__init__.py b/SONALI/platforms/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..935aa560ab834900590a884d6f29a683ac92e5bc --- /dev/null +++ b/SONALI/platforms/__init__.py @@ -0,0 +1,7 @@ +from .Apple import AppleAPI +from .Carbon import CarbonAPI +from .Resso import RessoAPI +from .Soundcloud import SoundAPI +from .Spotify import SpotifyAPI +from .Telegram import TeleAPI +from .Youtube import YouTubeAPI diff --git a/SONALI/plugins/__init__.py b/SONALI/plugins/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..255d2b3a048fb2c8ba13da4dd801d3a03bd8178c --- /dev/null +++ b/SONALI/plugins/__init__.py @@ -0,0 +1,19 @@ +import glob +from os.path import dirname, isfile + + +def __list_all_modules(): + work_dir = dirname(__file__) + mod_paths = glob.glob(work_dir + "/*/*.py") + + all_modules = [ + (((f.replace(work_dir, "")).replace("/", "."))[:-3]) + for f in mod_paths + if isfile(f) and f.endswith(".py") and not f.endswith("__init__.py") + ] + + return all_modules + + +ALL_MODULES = sorted(__list_all_modules()) +__all__ = ALL_MODULES + ["ALL_MODULES"] diff --git a/SONALI/plugins/admins/assistant.py b/SONALI/plugins/admins/assistant.py new file mode 100644 index 0000000000000000000000000000000000000000..960f2e15d490f65257c17b02396f144727728f8d --- /dev/null +++ b/SONALI/plugins/admins/assistant.py @@ -0,0 +1,174 @@ +import asyncio +from SONALI.misc import SUDOERS +from pyrogram import filters +from SONALI import app +import asyncio +from pyrogram import filters +from pyrogram.enums import ChatMemberStatus +from SONALI import app +from SONALI.utils.RAUSHAN_ban import admin_filter +from SONALI.utils.database import get_assistant + +links = {} + + +@app.on_message( + filters.group + & filters.command(["userbotjoin", f"userbotjoin@{app.username}"]) + & ~filters.private +) +async def join_group(client, message): + chat_id = message.chat.id + userbot = await get_assistant(message.chat.id) + userbot_id = userbot.id + done = await message.reply("**ᴘʟᴇᴀsᴇ ᴡᴀɪᴛ ɪɴᴠɪᴛɪɴɢ ᴀssɪsᴛᴀɴᴛ**...") + await asyncio.sleep(1) + # Get chat member object + chat_member = await app.get_chat_member(chat_id, app.id) + + # Condition 1: Group username is present, bot is not admin + if ( + message.chat.username + and not chat_member.status == ChatMemberStatus.ADMINISTRATOR + ): + try: + await userbot.join_chat(message.chat.username) + await done.edit_text("**✅ ᴀssɪsᴛᴀɴᴛ ᴊᴏɪɴᴇᴅ.**") + except Exception as e: + await done.edit_text("**ɪ ɴᴇᴇᴅ ᴀᴅᴍɪɴ ᴘᴏᴡᴇʀ ᴛᴏ ᴜɴʙᴀɴ ɪɴᴠɪᴛᴇ ᴍʏ ᴀssɪsᴛᴀɴᴛ!**") + + # Condition 2: Group username is present, bot is admin, and Userbot is not banned + if message.chat.username and chat_member.status == ChatMemberStatus.ADMINISTRATOR: + try: + await userbot.join_chat(message.chat.username) + await done.edit_text("**✅ ᴀssɪsᴛᴀɴᴛ ᴊᴏɪɴᴇᴅ.**") + except Exception as e: + await done.edit_text(str(e)) + + # Condition 3: Group username is not present/group is private, bot is admin and Userbot is banned + if message.chat.username and chat_member.status == ChatMemberStatus.ADMINISTRATOR: + userbot_member = await app.get_chat_member(chat_id, userbot.id) + if userbot_member.status in [ + ChatMemberStatus.BANNED, + ChatMemberStatus.RESTRICTED, + ]: + try: + await app.unban_chat_member(chat_id, userbot.id) + await done.edit_text("**ᴀssɪsᴛᴀɴᴛ ɪs ᴜɴʙᴀɴɴɪɴɢ...**") + await userbot.join_chat(message.chat.username) + await done.edit_text( + "**ᴀssɪsᴛᴀɴᴛ ᴡᴀs ʙᴀɴɴᴇᴅ, ʙᴜᴛ ɴᴏᴡ ᴜɴʙᴀɴɴᴇᴅ, ᴀɴᴅ ᴊᴏɪɴᴇᴅ ᴄʜᴀᴛ ✅**" + ) + except Exception as e: + await done.edit_text( + "**ғᴀɪʟᴇᴅ ᴛᴏ ᴊᴏɪɴ, ᴘʟᴇᴀsᴇ ɢɪᴠᴇ ʙᴀɴ ᴘᴏᴡᴇʀ ᴀɴᴅ ɪɴᴠɪᴛᴇ ᴜsᴇʀ ᴘᴏᴡᴇʀ ᴏʀ ᴜɴʙᴀɴ ᴀssɪsᴛᴀɴᴛ ᴍᴀɴᴜᴀʟʟʏ ᴛʜᴇɴ ᴛʀʏ ᴀɢᴀɪɴ ʙʏ /userbotjoin**" + ) + return + + # Condition 4: Group username is not present/group is private, bot is not admin + if ( + not message.chat.username + and not chat_member.status == ChatMemberStatus.ADMINISTRATOR + ): + await done.edit_text("**ɪ ɴᴇᴇᴅ ᴀᴅᴍɪɴ ᴘᴏᴡᴇʀ ᴛᴏ ɪɴᴠɪᴛᴇ ᴍʏ ᴀssɪsᴛᴀɴᴛ.**") + + # Condition 5: Group username is not present/group is private, bot is admin + if ( + not message.chat.username + and chat_member.status == ChatMemberStatus.ADMINISTRATOR + ): + try: + try: + userbot_member = await app.get_chat_member(chat_id, userbot.id) + if userbot_member.status not in [ + ChatMemberStatus.BANNED, + ChatMemberStatus.RESTRICTED, + ]: + await done.edit_text("**✅ ᴀssɪsᴛᴀɴᴛ ᴀʟʀᴇᴀᴅʏ ᴊᴏɪɴᴇᴅ.**") + return + except Exception as e: + await done.edit_text("**ᴘʟᴇᴀsᴇ ᴡᴀɪᴛ ɪɴᴠɪᴛɪɴɢ ᴀssɪsᴛᴀɴᴛ**.") + await done.edit_text("**ᴘʟᴇᴀsᴇ ᴡᴀɪᴛ ɪɴᴠɪᴛɪɴɢ ᴀssɪsᴛᴀɴᴛ**...") + invite_link = await app.create_chat_invite_link( + chat_id, expire_date=None + ) + await asyncio.sleep(2) + await userbot.join_chat(invite_link.invite_link) + await done.edit_text("**✅ ᴀssɪsᴛᴀɴᴛ ᴊᴏɪɴᴇᴅ sᴜᴄᴄᴇssғᴜʟʟʏ.**") + except Exception as e: + await done.edit_text( + f"**➻ ᴀᴄᴛᴜᴀʟʟʏ ɪ ғᴏᴜɴᴅ ᴛʜᴀᴛ ᴍʏ ᴀssɪsᴛᴀɴᴛ ʜᴀs ɴᴏᴛ ᴊᴏɪɴ ᴛʜɪs ɢʀᴏᴜᴘ ᴀɴᴅ ɪ ᴀᴍ ɴᴏᴛ ᴀʙʟᴇ ᴛᴏ ɪɴᴠɪᴛᴇ ᴍʏ ᴀssɪsᴛᴀɴᴛ ʙᴇᴄᴀᴜsᴇ [ ɪ ᴅᴏɴᴛ ʜᴀᴠᴇ ɪɴᴠɪᴛᴇ ᴜsᴇʀ ᴀᴅᴍɪɴ ᴘᴏᴡᴇʀ ] sᴏ ᴘʟᴇᴀsᴇ ᴘʀᴏᴠɪᴅᴇ ᴍᴇ ɪɴᴠɪᴛᴇ ᴜsᴇʀs ᴀᴅᴍɪɴ ᴘᴏᴡᴇʀ ᴛʜᴇɴ ᴛʀʏ ᴀɢᴀɪɴ ʙʏ- /userbotjoin.**\n\n**➥ ɪᴅ »** @{userbot.username}" + ) + + # Condition 6: Group username is not present/group is private, bot is admin and Userbot is banned + if ( + not message.chat.username + and chat_member.status == ChatMemberStatus.ADMINISTRATOR + ): + userbot_member = await app.get_chat_member(chat_id, userbot.id) + if userbot_member.status in [ + ChatMemberStatus.BANNED, + ChatMemberStatus.RESTRICTED, + ]: + try: + await app.unban_chat_member(chat_id, userbot.id) + await done.edit_text( + "**ᴀssɪsᴛᴀɴᴛ ɪs ᴜɴʙᴀɴɴᴇᴅ**\n**ᴛʏᴘᴇ ᴀɢᴀɪɴ:- /userbotjoin.**" + ) + invite_link = await app.create_chat_invite_link( + chat_id, expire_date=None + ) + await asyncio.sleep(2) + await userbot.join_chat(invite_link.invite_link) + await done.edit_text( + "**ᴀssɪsᴛᴀɴᴛ ᴡᴀs ʙᴀɴɴᴇᴅ, ɴᴏᴡ ᴜɴʙᴀɴɴᴇᴅ, ᴀɴᴅ ᴊᴏɪɴᴇᴅ ᴄʜᴀᴛ✅**" + ) + except Exception as e: + await done.edit_text( + f"**➻ ᴀᴄᴛᴜᴀʟʟʏ ɪ ғᴏᴜɴᴅ ᴛʜᴀᴛ ᴍʏ ᴀssɪsᴛᴀɴᴛ ɪs ʙᴀɴɴᴇᴅ ɪɴ ᴛʜɪs ɢʀᴏᴜᴘ ᴀɴᴅ ɪ ᴀᴍ ɴᴏᴛ ᴀʙʟᴇ ᴛᴏ ᴜɴʙᴀɴ ᴍʏ ᴀssɪsᴛᴀɴᴛ ʙᴇᴄᴀᴜsᴇ [ ɪ ᴅᴏɴᴛ ʜᴀᴠᴇ ʙᴀɴ ᴘᴏᴡᴇʀ ] sᴏ ᴘʟᴇᴀsᴇ ᴘʀᴏᴠɪᴅᴇ ᴍᴇ ʙᴀɴ ᴘᴏᴡᴇʀ ᴏʀ ᴜɴʙᴀɴ ᴍʏ ᴀssɪsᴛᴀɴᴛ ᴍᴀɴᴜᴀʟʟʏ ᴛʜᴇɴ ᴛʀʏ ᴀɢᴀɪɴ ʙʏ- /userbotjoin.**\n\n**➥ ɪᴅ »** @{userbot.username}" + ) + return + + +@app.on_message(filters.command("userbotleave") & filters.group & admin_filter) +async def leave_one(client, message): + try: + userbot = await get_assistant(message.chat.id) + await userbot.leave_chat(message.chat.id) + await app.send_message( + message.chat.id, "**✅ ᴜsᴇʀʙᴏᴛ sᴜᴄᴄᴇssғᴜʟʟʏ ʟᴇғᴛ ᴛʜɪs Chat.**" + ) + except Exception as e: + print(e) + + +@app.on_message(filters.command(["leaveall", f"leaveall@{app.username}"]) & SUDOERS) +async def leave_all(client, message): + if message.from_user.id not in SUDOERS: + return + + left = 0 + failed = 0 + lol = await message.reply("🔄 **ᴜsᴇʀʙᴏᴛ** ʟᴇᴀᴠɪɴɢ ᴀʟʟ ᴄʜᴀᴛs !") + try: + userbot = await get_assistant(message.chat.id) + async for dialog in userbot.get_dialogs(): + if dialog.chat.id == -1001733534088: + continue + try: + await userbot.leave_chat(dialog.chat.id) + left += 1 + await lol.edit( + f"**ᴜsᴇʀʙᴏᴛ ʟᴇᴀᴠɪɴɢ ᴀʟʟ ɢʀᴏᴜᴘ...**\n\n**ʟᴇғᴛ:** {left} ᴄʜᴀᴛs.\n**ғᴀɪʟᴇᴅ:** {failed} ᴄʜᴀᴛs." + ) + except BaseException: + failed += 1 + await lol.edit( + f"**ᴜsᴇʀʙᴏᴛ ʟᴇᴀᴠɪɴɢ...**\n\n**ʟᴇғᴛ:** {left} chats.\n**ғᴀɪʟᴇᴅ:** {failed} chats." + ) + await asyncio.sleep(3) + finally: + await app.send_message( + message.chat.id, + f"**✅ ʟᴇғᴛ ғʀᴏᴍ:* {left} chats.\n**❌ ғᴀɪʟᴇᴅ ɪɴ:** {failed} chats.", + ) diff --git a/SONALI/plugins/admins/auth.py b/SONALI/plugins/admins/auth.py new file mode 100644 index 0000000000000000000000000000000000000000..d6d668ca3ecc067cb75b7800bce5ac09ba144e3a --- /dev/null +++ b/SONALI/plugins/admins/auth.py @@ -0,0 +1,89 @@ +from pyrogram import filters +from pyrogram.types import Message + +from SONALI import app +from SONALI.utils import extract_user, int_to_alpha +from SONALI.utils.database import ( + delete_authuser, + get_authuser, + get_authuser_names, + save_authuser, +) +from SONALI.utils.decorators import AdminActual, language +from SONALI.utils.inline import close_markup +from config import BANNED_USERS, adminlist + + +@app.on_message(filters.command("auth") & filters.group & ~BANNED_USERS) +@AdminActual +async def auth(client, message: Message, _): + if not message.reply_to_message: + if len(message.command) != 2: + return await message.reply_text(_["general_1"]) + user = await extract_user(message) + token = await int_to_alpha(user.id) + _check = await get_authuser_names(message.chat.id) + count = len(_check) + if int(count) == 25: + return await message.reply_text(_["auth_1"]) + if token not in _check: + assis = { + "auth_user_id": user.id, + "auth_name": user.first_name, + "admin_id": message.from_user.id, + "admin_name": message.from_user.first_name, + } + get = adminlist.get(message.chat.id) + if get: + if user.id not in get: + get.append(user.id) + await save_authuser(message.chat.id, token, assis) + return await message.reply_text(_["auth_2"].format(user.mention)) + else: + return await message.reply_text(_["auth_3"].format(user.mention)) + + +@app.on_message(filters.command("unauth") & filters.group & ~BANNED_USERS) +@AdminActual +async def unauthusers(client, message: Message, _): + if not message.reply_to_message: + if len(message.command) != 2: + return await message.reply_text(_["general_1"]) + user = await extract_user(message) + token = await int_to_alpha(user.id) + deleted = await delete_authuser(message.chat.id, token) + get = adminlist.get(message.chat.id) + if get: + if user.id in get: + get.remove(user.id) + if deleted: + return await message.reply_text(_["auth_4"].format(user.mention)) + else: + return await message.reply_text(_["auth_5"].format(user.mention)) + + +@app.on_message( + filters.command(["authlist", "authusers"]) & filters.group & ~BANNED_USERS +) +@language +async def authusers(client, message: Message, _): + _wtf = await get_authuser_names(message.chat.id) + if not _wtf: + return await message.reply_text(_["setting_4"]) + else: + j = 0 + mystic = await message.reply_text(_["auth_6"]) + text = _["auth_7"].format(message.chat.title) + for umm in _wtf: + _umm = await get_authuser(message.chat.id, umm) + user_id = _umm["auth_user_id"] + admin_id = _umm["admin_id"] + admin_name = _umm["admin_name"] + try: + user = (await app.get_users(user_id)).first_name + j += 1 + except: + continue + text += f"{j}➤ {user}[{user_id}]\n" + text += f" {_['auth_8']} {admin_name}[{admin_id}]\n\n" + await mystic.edit_text(text, reply_markup=close_markup(_)) diff --git a/SONALI/plugins/admins/ban.py b/SONALI/plugins/admins/ban.py new file mode 100644 index 0000000000000000000000000000000000000000..9dc0a26b94c19913d31c622dd11bfe0ca645239b --- /dev/null +++ b/SONALI/plugins/admins/ban.py @@ -0,0 +1,420 @@ +from pyrogram import filters, enums +from pyrogram.types import ( + InlineKeyboardButton, + InlineKeyboardMarkup, + ChatPermissions +) +from pyrogram.errors.exceptions.bad_request_400 import ( + ChatAdminRequired, + UserAdminInvalid, + BadRequest +) + +import datetime +from SONALI import app + + + + +def mention(user, name, mention=True): + if mention == True: + link = f"[{name}](tg://openmessage?user_id={user})" + else: + link = f"[{name}](https://t.me/{user})" + return link + + + +async def get_userid_from_username(username): + try: + user = await app.get_users(username) + except: + return None + + user_obj = [user.id, user.first_name] + return user_obj + + +async def ban_user(user_id, first_name, admin_id, admin_name, chat_id, reason, time=None): + try: + await app.ban_chat_member(chat_id, user_id) + except ChatAdminRequired: + msg_text = "Ban rights? Nah, I'm just here for the digital high-fives 🙌\nGive me ban rights! 😡🥺" + return msg_text, False + except UserAdminInvalid: + msg_text = "I wont ban an admin bruh!!" + return msg_text, False + except Exception as e: + if user_id == 6711389550: + msg_text = "why should i ban myself? sorry but I'm not stupid like you" + return msg_text, False + + msg_text = f"opps!!\n{e}" + return msg_text, False + + user_mention = mention(user_id, first_name) + admin_mention = mention(admin_id, admin_name) + + msg_text += f"" + msg_text += f"{user_mention} was banned by {admin_mention}\n" + + if reason: + msg_text += f"Reason: `{reason}`\n" + if time: + msg_text += f"Time: `{time}`\n" + + return msg_text, True + + +async def unban_user(user_id, first_name, admin_id, admin_name, chat_id): + try: + await app.unban_chat_member(chat_id, user_id) + except ChatAdminRequired: + msg_text = "Ban rights? Nah, I'm just here for the digital high-fives 🙌\nGive me ban rights! 😡🥺" + return msg_text + except Exception as e: + msg_text = f"opps!!\n{e}" + return msg_text + + user_mention = mention(user_id, first_name) + admin_mention = mention(admin_id, admin_name) + + msg_text = f"{user_mention} was unbanned by {admin_mention}" + return msg_text + + + +async def mute_user(user_id, first_name, admin_id, admin_name, chat_id, reason, time=None): + try: + if time: + mute_end_time = datetime.datetime.now() + time + await app.restrict_chat_member(chat_id, user_id, ChatPermissions(), mute_end_time) + else: + await app.restrict_chat_member(chat_id, user_id, ChatPermissions()) + except ChatAdminRequired: + msg_text = "Mute rights? Nah, I'm just here for the digital high-fives 🙌\nGive me mute rights! 😡🥺" + return msg_text, False + except UserAdminInvalid: + msg_text = "I wont mute an admin bruh!!" + return msg_text, False + except Exception as e: + if user_id == 6664582540: + msg_text = "why should i mute myself? sorry but I'm not stupid like you" + return msg_text, False + + msg_text = f"opps!!\n{e}" + return msg_text, False + + user_mention = mention(user_id, first_name) + admin_mention = mention(admin_id, admin_name) + + msg_text += f"{user_mention} was muted by {admin_mention}\n" + + if reason: + msg_text += f"Reason: `{reason}`\n" + if time: + msg_text += f"Time: `{time}`\n" + + return msg_text, True + + +async def unmute_user(user_id, first_name, admin_id, admin_name, chat_id): + try: + await app.restrict_chat_member( + chat_id, + user_id, + ChatPermissions( + can_send_media_messages=True, + can_send_messages=True, + can_send_other_messages=True, + can_send_polls=True, + can_add_web_page_previews=True, + can_invite_users=True + ) + ) + except ChatAdminRequired: + msg_text = "Mute rights? Nah, I'm just here for the digital high-fives 🙌\nGive me unmute rights! 😡🥺" + return msg_text + except Exception as e: + msg_text = f"opps!!\n{e}" + return msg_text + + user_mention = mention(user_id, first_name) + admin_mention = mention(admin_id, admin_name) + + msg_text = f"{user_mention} was unmuted by {admin_mention}" + return msg_text + + + +@app.on_message(filters.command(["ban"])) +async def ban_command_handler(client, message): + chat = message.chat + chat_id = chat.id + admin_id = message.from_user.id + admin_name = message.from_user.first_name + member = await chat.get_member(admin_id) + if member.status == enums.ChatMemberStatus.ADMINISTRATOR or member.status == enums.ChatMemberStatus.OWNER: + if member.privileges.can_restrict_members: + pass + else: + msg_text = "You dont have permission to ban someone" + return await message.reply_text(msg_text) + else: + msg_text = "You dont have permission to ban someone" + return await message.reply_text(msg_text) + + # Extract the user ID from the command or reply + if len(message.command) > 1: + if message.reply_to_message: + user_id = message.reply_to_message.from_user.id + first_name = message.reply_to_message.from_user.first_name + reason = message.text.split(None, 1)[1] + else: + try: + user_id = int(message.command[1]) + first_name = "User" + except: + user_obj = await get_userid_from_username(message.command[1]) + if user_obj == None: + return await message.reply_text("I can't find that user") + user_id = user_obj[0] + first_name = user_obj[1] + + try: + reason = message.text.partition(message.command[1])[2] + except: + reason = None + + elif message.reply_to_message: + user_id = message.reply_to_message.from_user.id + first_name = message.reply_to_message.from_user.first_name + reason = None + else: + await message.reply_text("Please specify a valid user or reply to that user's message") + return + + msg_text, result = await ban_user(user_id, first_name, admin_id, admin_name, chat_id, reason) + if result == True: + await message.reply_text(msg_text) + if result == False: + await message.reply_text(msg_text) + + +@app.on_message(filters.command(["unban"])) +async def unban_command_handler(client, message): + chat = message.chat + chat_id = chat.id + admin_id = message.from_user.id + admin_name = message.from_user.first_name + member = await chat.get_member(admin_id) + if member.status == enums.ChatMemberStatus.ADMINISTRATOR or member.status == enums.ChatMemberStatus.OWNER: + if member.privileges.can_restrict_members: + pass + else: + msg_text = "You dont have permission to unban someone" + return await message.reply_text(msg_text) + else: + msg_text = "You dont have permission to unban someone" + return await message.reply_text(msg_text) + + # Extract the user ID from the command or reply + if len(message.command) > 1: + try: + user_id = int(message.command[1]) + first_name = "User" + except: + user_obj = await get_userid_from_username(message.command[1]) + if user_obj == None: + return await message.reply_text("I can't find that user") + user_id = user_obj[0] + first_name = user_obj[1] + + elif message.reply_to_message: + user_id = message.reply_to_message.from_user.id + first_name = message.reply_to_message.from_user.first_name + else: + await message.reply_text("Please specify a valid user or reply to that user's message") + return + + msg_text = await unban_user(user_id, first_name, admin_id, admin_name, chat_id) + await message.reply_text(msg_text) + + + + +@app.on_message(filters.command(["mute"])) +async def mute_command_handler(client, message): + chat = message.chat + chat_id = chat.id + admin_id = message.from_user.id + admin_name = message.from_user.first_name + member = await chat.get_member(admin_id) + if member.status == enums.ChatMemberStatus.ADMINISTRATOR or member.status == enums.ChatMemberStatus.OWNER: + if member.privileges.can_restrict_members: + pass + else: + msg_text = "You dont have permission to mute someone" + return await message.reply_text(msg_text) + else: + msg_text = "You dont have permission to mute someone" + return await message.reply_text(msg_text) + + # Extract the user ID from the command or reply + if len(message.command) > 1: + if message.reply_to_message: + user_id = message.reply_to_message.from_user.id + first_name = message.reply_to_message.from_user.first_name + reason = message.text.split(None, 1)[1] + else: + try: + user_id = int(message.command[1]) + first_name = "User" + except: + user_obj = await get_userid_from_username(message.command[1]) + if user_obj == None: + return await message.reply_text("I can't find that user") + user_id = user_obj[0] + first_name = user_obj[1] + + try: + reason = message.text.partition(message.command[1])[2] + except: + reason = None + + elif message.reply_to_message: + user_id = message.reply_to_message.from_user.id + first_name = message.reply_to_message.from_user.first_name + reason = None + else: + await message.reply_text("Please specify a valid user or reply to that user's message") + return + + msg_text, result = await mute_user(user_id, first_name, admin_id, admin_name, chat_id, reason) + if result == True: + await message.reply_text(msg_text) + + if result == False: + await message.reply_text(msg_text) + + +@app.on_message(filters.command(["unmute"])) +async def unmute_command_handler(client, message): + chat = message.chat + chat_id = chat.id + admin_id = message.from_user.id + admin_name = message.from_user.first_name + member = await chat.get_member(admin_id) + if member.status == enums.ChatMemberStatus.ADMINISTRATOR or member.status == enums.ChatMemberStatus.OWNER: + if member.privileges.can_restrict_members: + pass + else: + msg_text = "You dont have permission to unmute someone" + return await message.reply_text(msg_text) + else: + msg_text = "You dont have permission to unmute someone" + return await message.reply_text(msg_text) + + # Extract the user ID from the command or reply + if len(message.command) > 1: + try: + user_id = int(message.command[1]) + first_name = "User" + except: + user_obj = await get_userid_from_username(message.command[1]) + if user_obj == None: + return await message.reply_text("I can't find that user") + user_id = user_obj[0] + first_name = user_obj[1] + + elif message.reply_to_message: + user_id = message.reply_to_message.from_user.id + first_name = message.reply_to_message.from_user.first_name + else: + await message.reply_text("Please specify a valid user or reply to that user's message") + return + + msg_text = await unmute_user(user_id, first_name, admin_id, admin_name, chat_id) + await message.reply_text(msg_text) + + + + + +@app.on_message(filters.command(["tmute"])) +async def tmute_command_handler(client, message): + chat = message.chat + chat_id = chat.id + admin_id = message.from_user.id + admin_name = message.from_user.first_name + member = await chat.get_member(admin_id) + if member.status == enums.ChatMemberStatus.ADMINISTRATOR or member.status == enums.ChatMemberStatus.OWNER: + if member.privileges.can_restrict_members: + pass + else: + msg_text = "You dont have permission to mute someone" + return await message.reply_text(msg_text) + else: + msg_text = "You dont have permission to mute someone" + return await message.reply_text(msg_text) + + # Extract the user ID from the command or reply + if len(message.command) > 1: + if message.reply_to_message: + user_id = message.reply_to_message.from_user.id + first_name = message.reply_to_message.from_user.first_name + time = message.text.split(None, 1)[1] + + try: + time_amount = time.split(time[-1])[0] + time_amount = int(time_amount) + except: + return await message.reply_text("wrong format!!\nFormat: `/tmute 2m`") + + if time[-1] == "m": + mute_duration = datetime.timedelta(minutes=time_amount) + elif time[-1] == "h": + mute_duration = datetime.timedelta(hours=time_amount) + elif time[-1] == "d": + mute_duration = datetime.timedelta(days=time_amount) + else: + return await message.reply_text("wrong format!!\nFormat:\nm: Minutes\nh: Hours\nd: Days") + else: + try: + user_id = int(message.command[1]) + first_name = "User" + except: + user_obj = await get_userid_from_username(message.command[1]) + if user_obj == None: + return await message.reply_text("I can't find that user") + user_id = user_obj[0] + first_name = user_obj[1] + + try: + time = message.text.partition(message.command[1])[2] + try: + time_amount = time.split(time[-1])[0] + time_amount = int(time_amount) + except: + return await message.reply_text("wrong format!!\nFormat: `/tmute 2m`") + + if time[-1] == "m": + mute_duration = datetime.timedelta(minutes=time_amount) + elif time[-1] == "h": + mute_duration = datetime.timedelta(hours=time_amount) + elif time[-1] == "d": + mute_duration = datetime.timedelta(days=time_amount) + else: + return await message.reply_text("wrong format!!\nFormat:\nm: Minutes\nh: Hours\nd: Days") + except: + return await message.reply_text("Please specify a valid user or reply to that user's message\nFormat: `/tmute @user 2m`") + + else: + await message.reply_text("Please specify a valid user or reply to that user's message\nFormat: /tmute