diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 00000000..83758e7f --- /dev/null +++ b/Dockerfile @@ -0,0 +1,8 @@ +FROM python:3.9.6-slim-buster +RUN apt-get update && apt-get upgrade -y +RUN apt-get install git curl python3-pip ffmpeg -y +RUN python3.9 -m pip install -U pip +COPY . /app +WORKDIR /app +RUN python3.9 -m pip install -U -r requirements.txt +CMD python3.9 -m VCPlayBot diff --git a/Procfile b/Procfile new file mode 100644 index 00000000..1b175b88 --- /dev/null +++ b/Procfile @@ -0,0 +1 @@ +worker: python3 -m VCPlayBot diff --git a/VCPlayBot/__main__.py b/VCPlayBot/__main__.py new file mode 100644 index 00000000..22e5349c --- /dev/null +++ b/VCPlayBot/__main__.py @@ -0,0 +1,24 @@ +import requests +from pyrogram import Client as Bot + +from VCPlayBot.config import API_HASH +from VCPlayBot.config import API_ID +from VCPlayBot.config import BG_IMAGE +from VCPlayBot.config import BOT_TOKEN +from VCPlayBot.services.callsmusic import run + +response = requests.get(BG_IMAGE) +file = open("./etc/foreground.png", "wb") +file.write(response.content) +file.close() + +bot = Bot( + ":memory:", + API_ID, + API_HASH, + bot_token=BOT_TOKEN, + plugins=dict(root="VCPlayBot.modules"), +) + +bot.start() +run() diff --git a/VCPlayBot/config.py b/VCPlayBot/config.py new file mode 100644 index 00000000..cf4588df --- /dev/null +++ b/VCPlayBot/config.py @@ -0,0 +1,29 @@ +import os +from os import getenv + +from dotenv import load_dotenv + +if os.path.exists("local.env"): + load_dotenv("local.env") + +que = {} +SESSION_NAME = getenv("SESSION_NAME", "session") +BOT_TOKEN = getenv("BOT_TOKEN") +BOT_NAME = getenv("BOT_NAME") +UPDATES_CHANNEL = getenv("UPDATES_CHANNEL", "LaylaList") +BG_IMAGE = getenv("BG_IMAGE", "https://telegra.ph/file/9b13ea3ce046a1a5c8098.png") +admins = {} +API_ID = int(getenv("API_ID")) +API_HASH = getenv("API_HASH") +BOT_USERNAME = getenv("BOT_USERNAME") +ASSISTANT_NAME = getenv("ASSISTANT_NAME", "VCPlayAssistant") +SUPPORT_GROUP = getenv("SUPPORT_GROUP", "AwesomeSupport") +PROJECT_NAME = getenv("PROJECT_NAME", "VCPlayBot2.0") +SOURCE_CODE = getenv("SOURCE_CODE", "github.com/QueenArzoo/VCPlayBot") +DURATION_LIMIT = int(getenv("DURATION_LIMIT", "7")) +ARQ_API_KEY = getenv("ARQ_API_KEY", None) +PMPERMIT = getenv("PMPERMIT", None) +LOG_GRP = getenv("LOG_GRP", None) +COMMAND_PREFIXES = list(getenv("COMMAND_PREFIXES", "/ !").split()) + +SUDO_USERS = list(map(int, getenv("SUDO_USERS", "797768146").split())) diff --git a/VCPlayBot/function/__init__.py b/VCPlayBot/function/__init__.py new file mode 100644 index 00000000..2ca5619a --- /dev/null +++ b/VCPlayBot/function/__init__.py @@ -0,0 +1,5 @@ +from VCPlayBot.function.admins import admins +from VCPlayBot.function.admins import get +from VCPlayBot.function.admins import set + +__all__ = ["set", "get", "admins"] diff --git a/VCPlayBot/function/admins.py b/VCPlayBot/function/admins.py new file mode 100644 index 00000000..61c0142e --- /dev/null +++ b/VCPlayBot/function/admins.py @@ -0,0 +1,15 @@ +from typing import Dict +from typing import List + + +admins: Dict[int, List[int]] = {} + + +def set(chat_id: int, admins_: List[int]): + admins[chat_id] = admins_ + + +def get(chat_id: int) -> List[int]: + if chat_id in admins: + return admins[chat_id] + return [] diff --git a/VCPlayBot/helpers/__init__.py b/VCPlayBot/helpers/__init__.py new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/VCPlayBot/helpers/__init__.py @@ -0,0 +1 @@ + diff --git a/VCPlayBot/helpers/admins.py b/VCPlayBot/helpers/admins.py new file mode 100644 index 00000000..b563f4ac --- /dev/null +++ b/VCPlayBot/helpers/admins.py @@ -0,0 +1,25 @@ + + +from typing import List + +from pyrogram.types import Chat + +from VCPlayBot.function.admins import get as gett +from VCPlayBot.function.admins import 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/VCPlayBot/helpers/channelmusic.py b/VCPlayBot/helpers/channelmusic.py new file mode 100644 index 00000000..30eafe6e --- /dev/null +++ b/VCPlayBot/helpers/channelmusic.py @@ -0,0 +1,7 @@ +from pyrogram.types import Chat + + +def get_chat_id(chat: Chat): + if chat.title.startswith("Channel Music: ") and chat.title[16:].isnumeric(): + return int(chat.title[15:]) + return chat.id diff --git a/VCPlayBot/helpers/decorators.py b/VCPlayBot/helpers/decorators.py new file mode 100644 index 00000000..f772e868 --- /dev/null +++ b/VCPlayBot/helpers/decorators.py @@ -0,0 +1,31 @@ +from typing import Callable + +from pyrogram import Client +from pyrogram.types import Message + +from VCPlayBot.config import SUDO_USERS +from VCPlayBot.helpers.admins import get_administrators + + +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 diff --git a/VCPlayBot/helpers/errors.py b/VCPlayBot/helpers/errors.py new file mode 100644 index 00000000..b6307c85 --- /dev/null +++ b/VCPlayBot/helpers/errors.py @@ -0,0 +1,6 @@ +class DurationLimitError(Exception): + pass + + +class FFmpegReturnCodeError(Exception): + pass diff --git a/VCPlayBot/helpers/filters.py b/VCPlayBot/helpers/filters.py new file mode 100644 index 00000000..c2e74682 --- /dev/null +++ b/VCPlayBot/helpers/filters.py @@ -0,0 +1,15 @@ +from typing import List +from typing import Union + +from pyrogram import filters + +from VCPlayBot.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/VCPlayBot/helpers/gets.py b/VCPlayBot/helpers/gets.py new file mode 100644 index 00000000..4b8592e4 --- /dev/null +++ b/VCPlayBot/helpers/gets.py @@ -0,0 +1,36 @@ +from typing import Union + +from pyrogram.types import Audio +from pyrogram.types import Message +from pyrogram.types import Voice + + +def get_url(message_1: Message) -> Union[str, None]: + messages = [message_1] + + if message_1.reply_to_message: + messages.append(message_1.reply_to_message) + + text = "" + offset = None + length = None + + for message in messages: + if offset: + break + + if message.entities: + for entity in message.entities: + if entity.type == "url": + text = message.text or message.caption + offset, length = entity.offset, entity.length + break + + if offset in (None,): + return None + + return text[offset : offset + length] + + +def get_file_name(audio: Union[Audio, Voice]): + return f'{audio.file_unique_id}.{audio.file_name.split(".")[-1] if not isinstance(audio, Voice) else "ogg"}' diff --git a/VCPlayBot/modules/__init__.py b/VCPlayBot/modules/__init__.py new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/VCPlayBot/modules/__init__.py @@ -0,0 +1 @@ + diff --git a/VCPlayBot/modules/admins.py b/VCPlayBot/modules/admins.py new file mode 100644 index 00000000..5a87239a --- /dev/null +++ b/VCPlayBot/modules/admins.py @@ -0,0 +1,113 @@ + + +from asyncio import QueueEmpty +from pyrogram import Client +from pyrogram import filters +from pyrogram.types import Message + +from VCPlayBot.config import que +from VCPlayBot.function.admins import set +from VCPlayBot.helpers.channelmusic import get_chat_id +from VCPlayBot.helpers.decorators import authorized_users_only +from VCPlayBot.helpers.decorators import errors +from VCPlayBot.helpers.filters import command +from VCPlayBot.helpers.filters import other_filters +from VCPlayBot.services.callsmusic import callsmusic +from VCPlayBot.services.queues import queues + + +@Client.on_message(filters.command("adminreset")) +async def update_admin(client, message: Message): + chat_id = get_chat_id(message.chat) + set( + chat_id, + [ + member.user + for member in await message.chat.get_members(filter="administrators") + ], + ) + await message.reply_text("❇️ Admin cache refreshed!") + + +@Client.on_message(command("pause") & other_filters) +@errors +@authorized_users_only +async def pause(_, message: Message): + chat_id = get_chat_id(message.chat) + if (chat_id not in callsmusic.active_chats) or ( + callsmusic.active_chats[chat_id] == "paused" + ): + await message.reply_text("❗ Nothing is playing!") + else: + callsmusic.pause(chat_id) + await message.reply_text("▶️ Paused!") + + +@Client.on_message(command("resume") & other_filters) +@errors +@authorized_users_only +async def resume(_, message: Message): + chat_id = get_chat_id(message.chat) + if (chat_id not in callsmusic.active_chats) or ( + callsmusic.active_chats[chat_id] == "playing" + ): + await message.reply_text("❗ Nothing is paused!") + else: + callsmusic.resume(chat_id) + await message.reply_text("⏸ Resumed!") + + +@Client.on_message(command("end") & other_filters) +@errors +@authorized_users_only +async def stop(_, message: Message): + chat_id = get_chat_id(message.chat) + if chat_id not in callsmusic.active_chats: + await message.reply_text("❗ Nothing is streaming!") + else: + try: + queues.clear(chat_id) + except QueueEmpty: + pass + + await callsmusic.stop(chat_id) + await message.reply_text("❌ Stopped streaming!") + + +@Client.on_message(command("skip") & other_filters) +@errors +@authorized_users_only +async def skip(_, message: Message): + global que + chat_id = get_chat_id(message.chat) + if chat_id not in callsmusic.active_chats: + await message.reply_text("❗ Nothing is playing to skip!") + else: + queues.task_done(chat_id) + if queues.is_empty(chat_id): + await callsmusic.stop(chat_id) + else: + await callsmusic.set_stream( + chat_id, + queues.get(chat_id)["file_path"] + ) + + qeue = que.get(chat_id) + if qeue: + skip = qeue.pop(0) + if not qeue: + return + await message.reply_text(f"- Skipped **{skip[0]}**\n- Now Playing **{qeue[0][0]}**") + + +@Client.on_message(filters.command("admincache")) +@errors +async def admincache(client, message: Message): + set( + message.chat.id, + [ + member.user + for member in await message.chat.get_members(filter="administrators") + ], + ) + await message.reply_text("❇️ Admin cache refreshed!") diff --git a/VCPlayBot/modules/channeladmins.py b/VCPlayBot/modules/channeladmins.py new file mode 100644 index 00000000..dc52a93c --- /dev/null +++ b/VCPlayBot/modules/channeladmins.py @@ -0,0 +1,136 @@ + + +from asyncio import QueueEmpty +from pyrogram import Client +from pyrogram import filters +from pyrogram.types import Message + +from VCPlayBot.config import que +from VCPlayBot.function.admins import set +from VCPlayBot.helpers.channelmusic import get_chat_id +from VCPlayBot.helpers.decorators import authorized_users_only +from VCPlayBot.helpers.decorators import errors +from VCPlayBot.helpers.filters import command +from VCPlayBot.helpers.filters import other_filters +from VCPlayBot.services.callsmusic import callsmusic +from VCPlayBot.services.queues import queues + + +@Client.on_message(filters.command(["channelpause","cpause"]) & filters.group & ~filters.edited) +@errors +@authorized_users_only +async def pause(_, message: Message): + try: + conchat = await _.get_chat(message.chat.id) + conid = conchat.linked_chat.id + chid = conid + except: + await message.reply("Is chat even linked") + return + chat_id = chid + if (chat_id not in callsmusic.active_chats) or ( + callsmusic.active_chats[chat_id] == "paused" + ): + await message.reply_text("❗ Nothing is playing!") + else: + callsmusic.pause(chat_id) + await message.reply_text("▶️ Paused!") + + +@Client.on_message(filters.command(["channelresume","cresume"]) & filters.group & ~filters.edited) +@errors +@authorized_users_only +async def resume(_, message: Message): + try: + conchat = await _.get_chat(message.chat.id) + conid = conchat.linked_chat.id + chid = conid + except: + await message.reply("Is chat even linked") + return + chat_id = chid + if (chat_id not in callsmusic.active_chats) or ( + callsmusic.active_chats[chat_id] == "playing" + ): + await message.reply_text("❗ Nothing is paused!") + else: + callsmusic.resume(chat_id) + await message.reply_text("⏸ Resumed!") + + +@Client.on_message(filters.command(["channelend","cend"]) & filters.group & ~filters.edited) +@errors +@authorized_users_only +async def stop(_, message: Message): + try: + conchat = await _.get_chat(message.chat.id) + conid = conchat.linked_chat.id + chid = conid + except: + await message.reply("Is chat even linked") + return + chat_id = chid + if chat_id not in callsmusic.active_chats: + await message.reply_text("❗ Nothing is streaming!") + else: + try: + queues.clear(chat_id) + except QueueEmpty: + pass + + await callsmusic.stop(chat_id) + await message.reply_text("❌ Stopped streaming!") + + +@Client.on_message(filters.command(["channelskip","cskip"]) & filters.group & ~filters.edited) +@errors +@authorized_users_only +async def skip(_, message: Message): + global que + try: + conchat = await _.get_chat(message.chat.id) + conid = conchat.linked_chat.id + chid = conid + except: + await message.reply("Is chat even linked") + return + chat_id = chid + if chat_id not in callsmusic.active_chats: + await message.reply_text("❗ Nothing is playing to skip!") + else: + queues.task_done(chat_id) + + if queues.is_empty(chat_id): + await callsmusic.stop(chat_id) + else: + await callsmusic.set_stream( + chat_id, + queues.get(chat_id)["file_path"] + ) + + qeue = que.get(chat_id) + if qeue: + skip = qeue.pop(0) + if not qeue: + return + await message.reply_text(f"- Skipped **{skip[0]}**\n- Now Playing **{qeue[0][0]}**") + + +@Client.on_message(filters.command("channeladmincache")) +@errors +async def admincache(client, message: Message): + try: + conchat = await client.get_chat(message.chat.id) + conid = conchat.linked_chat.id + chid = conid + except: + await message.reply("Is chat even linked") + return + set( + chid, + [ + member.user + for member in await conchat.linked_chat.get_members(filter="administrators") + ], + ) + await message.reply_text("❇️ Admin cache refreshed!") diff --git a/VCPlayBot/modules/channelplay.py b/VCPlayBot/modules/channelplay.py new file mode 100644 index 00000000..2feb6b26 --- /dev/null +++ b/VCPlayBot/modules/channelplay.py @@ -0,0 +1,749 @@ + + +import json +from os import path +from typing import Callable + +import aiofiles +import aiohttp +import ffmpeg +import requests +import wget +from PIL import Image +from PIL import ImageDraw +from PIL import ImageFont +from pyrogram import Client +from pyrogram import filters +from pyrogram.errors import UserAlreadyParticipant +from pyrogram.types import Voice +from pyrogram.types import InlineKeyboardButton, InlineKeyboardMarkup, Message +from Python_ARQ import ARQ +from youtube_search import YoutubeSearch +from VCPlayBot.modules.play import generate_cover +from VCPlayBot.modules.play import arq +from VCPlayBot.modules.play import cb_admin_check +from VCPlayBot.modules.play import transcode +from VCPlayBot.modules.play import convert_seconds +from VCPlayBot.modules.play import time_to_seconds +from VCPlayBot.modules.play import changeImageSize +from VCPlayBot.config import BOT_NAME as bn +from VCPlayBot.config import DURATION_LIMIT +from VCPlayBot.config import UPDATES_CHANNEL as updateschannel +from VCPlayBot.config import que +from VCPlayBot.function.admins import admins as a +from VCPlayBot.helpers.errors import DurationLimitError +from VCPlayBot.helpers.decorators import errors +from VCPlayBot.helpers.admins import get_administrators +from VCPlayBot.helpers.channelmusic import get_chat_id +from VCPlayBot.helpers.decorators import authorized_users_only +from VCPlayBot.helpers.filters import command +from VCPlayBot.helpers.filters import other_filters +from VCPlayBot.helpers.gets import get_file_name +from VCPlayBot.services.callsmusic import callsmusic +from VCPlayBot.services.callsmusic import client as USER +from VCPlayBot.services.converter.converter import convert +from VCPlayBot.services.downloaders import youtube +from VCPlayBot.services.queues import queues + +chat_id = None + + +@Client.on_message(filters.command(["channelplaylist","cplaylist"]) & filters.group & ~filters.edited) +async def playlist(client, message): + try: + lel = await client.get_chat(message.chat.id) + lol = lel.linked_chat.id + except: + message.reply("Is this cat even linked?") + return + global que + queue = que.get(lol) + if not queue: + await message.reply_text("Player is idle") + temp = [] + for t in queue: + temp.append(t) + now_playing = temp[0][0] + by = temp[0][1].mention(style="md") + msg = "Now Playing in {}".format(lel.linked_chat.title) + msg += "\n- " + now_playing + msg += "\n- Req by " + by + temp.pop(0) + if temp: + msg += "\n\n" + msg += "Queue" + for song in temp: + name = song[0] + usr = song[1].mention(style="md") + msg += f"\n- {name}" + msg += f"\n- Req by {usr}\n" + await message.reply_text(msg) + + +# ============================= Settings ========================================= + + +def updated_stats(chat, queue, vol=100): + if chat.id in callsmusic.active_chats: + # if chat.id in active_chats: + stats = "Settings of **{}**".format(chat.title) + if len(que) > 0: + stats += "\n\n" + stats += "Volume : {}%\n".format(vol) + stats += "Songs in queue : `{}`\n".format(len(que)) + stats += "Now Playing : **{}**\n".format(queue[0][0]) + stats += "Requested by : {}".format(queue[0][1].mention) + else: + stats = None + return stats + + +def r_ply(type_): + if type_ == "play": + pass + else: + pass + mar = InlineKeyboardMarkup( + [ + [ + InlineKeyboardButton("⏹", "cleave"), + InlineKeyboardButton("⏸", "cpuse"), + InlineKeyboardButton("▶️", "cresume"), + InlineKeyboardButton("⏭", "cskip"), + ], + [ + InlineKeyboardButton("Playlist 📖", "cplaylist"), + ], + [InlineKeyboardButton("❌ Close", "ccls")], + ] + ) + return mar + + +@Client.on_message(filters.command(["channelcurrent","ccurrent"]) & filters.group & ~filters.edited) +async def ee(client, message): + try: + lel = await client.get_chat(message.chat.id) + lol = lel.linked_chat.id + conv = lel.linked_chat + except: + await message.reply("Is chat even linked") + return + queue = que.get(lol) + stats = updated_stats(conv, queue) + if stats: + await message.reply(stats) + else: + await message.reply("No VC instances running in this chat") + + +@Client.on_message(filters.command(["channelplayer","cplayer"]) & filters.group & ~filters.edited) +@authorized_users_only +async def settings(client, message): + playing = None + try: + lel = await client.get_chat(message.chat.id) + lol = lel.linked_chat.id + conv = lel.linked_chat + except: + await message.reply("Is chat even linked") + return + queue = que.get(lol) + stats = updated_stats(conv, queue) + if stats: + if playing: + await message.reply(stats, reply_markup=r_ply("pause")) + + else: + await message.reply(stats, reply_markup=r_ply("play")) + else: + await message.reply("No VC instances running in this chat") + + +@Client.on_callback_query(filters.regex(pattern=r"^(cplaylist)$")) +async def p_cb(b, cb): + global que + try: + lel = await client.get_chat(cb.message.chat.id) + lol = lel.linked_chat.id + conv = lel.linked_chat + except: + return + que.get(lol) + type_ = cb.matches[0].group(1) + cb.message.chat.id + cb.message.chat + cb.message.reply_markup.inline_keyboard[1][0].callback_data + if type_ == "playlist": + queue = que.get(lol) + if not queue: + await cb.message.edit("Player is idle") + temp = [] + for t in queue: + temp.append(t) + now_playing = temp[0][0] + by = temp[0][1].mention(style="md") + msg = "**Now Playing** in {}".format(conv.title) + msg += "\n- " + now_playing + msg += "\n- Req by " + by + temp.pop(0) + if temp: + msg += "\n\n" + msg += "**Queue**" + for song in temp: + name = song[0] + usr = song[1].mention(style="md") + msg += f"\n- {name}" + msg += f"\n- Req by {usr}\n" + await cb.message.edit(msg) + + +@Client.on_callback_query( + filters.regex(pattern=r"^(cplay|cpause|cskip|cleave|cpuse|cresume|cmenu|ccls)$") +) +@cb_admin_check +async def m_cb(b, cb): + global que + if ( + cb.message.chat.title.startswith("Channel Music: ") + and chat.title[14:].isnumeric() + ): + chet_id = int(chat.title[13:]) + else: + try: + lel = await b.get_chat(cb.message.chat.id) + lol = lel.linked_chat.id + conv = lel.linked_chat + chet_id = lol + except: + return + qeue = que.get(chet_id) + type_ = cb.matches[0].group(1) + cb.message.chat.id + m_chat = cb.message.chat + + + the_data = cb.message.reply_markup.inline_keyboard[1][0].callback_data + if type_ == "cpause": + if (chet_id not in callsmusic.active_chats) or ( + callsmusic.active_chats[chet_id] == "paused" + ): + await cb.answer("Chat is not connected!", show_alert=True) + else: + callsmusic.pause(chet_id) + await cb.answer("Music Paused!") + await cb.message.edit( + updated_stats(conv, qeue), reply_markup=r_ply("play") + ) + + elif type_ == "cplay": + if (chet_id not in callsmusic.active_chats) or ( + callsmusic.active_chats[chet_id] == "playing" + ): + await cb.answer("Chat is not connected!", show_alert=True) + else: + callsmusic.resume(chet_id) + await cb.answer("Music Resumed!") + await cb.message.edit( + updated_stats(conv, qeue), reply_markup=r_ply("pause") + ) + + elif type_ == "cplaylist": + queue = que.get(cb.message.chat.id) + if not queue: + await cb.message.edit("Player is idle") + temp = [] + for t in queue: + temp.append(t) + now_playing = temp[0][0] + by = temp[0][1].mention(style="md") + msg = "**Now Playing** in {}".format(cb.message.chat.title) + msg += "\n- " + now_playing + msg += "\n- Req by " + by + temp.pop(0) + if temp: + msg += "\n\n" + msg += "**Queue**" + for song in temp: + name = song[0] + usr = song[1].mention(style="md") + msg += f"\n- {name}" + msg += f"\n- Req by {usr}\n" + await cb.message.edit(msg) + + elif type_ == "cresume": + if (chet_id not in callsmusic.active_chats) or ( + callsmusic.active_chats[chet_id] == "playing" + ): + await cb.answer("Chat is not connected or already playng", show_alert=True) + else: + callsmusic.resume(chet_id) + await cb.answer("Music Resumed!") + elif type_ == "cpuse": + if (chet_id not in callsmusic.active_chats) or ( + callsmusic.active_chats[chet_id] == "paused" + ): + await cb.answer("Chat is not connected or already paused", show_alert=True) + else: + callsmusic.pause(chet_id) + await cb.answer("Music Paused!") + elif type_ == "ccls": + await cb.answer("Closed menu") + await cb.message.delete() + + elif type_ == "cmenu": + stats = updated_stats(conv, qeue) + await cb.answer("Menu opened") + marr = InlineKeyboardMarkup( + [ + [ + InlineKeyboardButton("⏹", "cleave"), + InlineKeyboardButton("⏸", "cpuse"), + InlineKeyboardButton("▶️", "cresume"), + InlineKeyboardButton("⏭", "cskip"), + ], + [ + InlineKeyboardButton("Playlist 📖", "cplaylist"), + ], + [InlineKeyboardButton("❌ Close", "ccls")], + ] + ) + await cb.message.edit(stats, reply_markup=marr) + elif type_ == "cskip": + if qeue: + qeue.pop(0) + if chet_id not in callsmusic.active_chats: + await cb.answer("Chat is not connected!", show_alert=True) + else: + queues.task_done(chet_id) + + if queues.is_empty(chet_id): + callsmusic.stop(chet_id) + await cb.message.edit("- No More Playlist..\n- Leaving VC!") + else: + await callsmusic.set_stream( + chet_id, queues.get(chet_id)["file"] + ) + await cb.answer.reply_text("✅ Skipped") + await cb.message.edit((m_chat, qeue), reply_markup=r_ply(the_data)) + await cb.message.reply_text( + f"- Skipped track\n- Now Playing **{qeue[0][0]}**" + ) + + else: + if chet_id in callsmusic.active_chats: + try: + queues.clear(chet_id) + except QueueEmpty: + pass + + callsmusic.stop(chet_id) + await cb.message.edit("Successfully Left the Chat!") + else: + await cb.answer("Chat is not connected!", show_alert=True) + + +@Client.on_message(filters.command(["channelplay","cplay"]) & filters.group & ~filters.edited) +@authorized_users_only +async def play(_, message: Message): + global que + lel = await message.reply("🔄 Processing") + + try: + conchat = await _.get_chat(message.chat.id) + conv = conchat.linked_chat + conid = conchat.linked_chat.id + chid = conid + except: + await message.reply("Is chat even linked") + return + try: + administrators = await get_administrators(conv) + except: + await message.reply("Am I admin of Channel") + try: + user = await USER.get_me() + except: + user.first_name = "helper" + usar = user + wew = usar.id + try: + # chatdetails = await USER.get_chat(chid) + await _.get_chat_member(chid, wew) + except: + for administrator in administrators: + if administrator == message.from_user.id: + if message.chat.title.startswith("Channel Music: "): + await lel.edit( + "Remember to add helper to your channel", + ) + pass + + try: + invitelink = await _.export_chat_invite_link(chid) + except: + await lel.edit( + "Add me as admin of yor channel first", + ) + return + + try: + await USER.join_chat(invitelink) + await lel.edit( + "helper userbot joined your channel", + ) + + except UserAlreadyParticipant: + pass + except Exception: + # print(e) + await lel.edit( + f"🔴 Flood Wait Error 🔴 \nUser {user.first_name} couldn't join your channel due to heavy requests for userbot! Make sure user is not banned in group." + "\n\nOr manually add assistant to your Group and try again", + ) + try: + await USER.get_chat(chid) + # lmoa = await client.get_chat_member(chid,wew) + except: + await lel.edit( + f" {user.first_name} Userbot not in this chat, Ask channel admin to send /play command for first time or add {user.first_name} manually" + ) + return + message.from_user.id + text_links = None + message.from_user.first_name + await lel.edit("🔎 Finding") + message.from_user.id + user_id = message.from_user.id + message.from_user.first_name + user_name = message.from_user.first_name + rpk = "[" + user_name + "](tg://user?id=" + str(user_id) + ")" + if message.reply_to_message: + if message.reply_to_message.audio: + pass + entities = [] + toxt = message.reply_to_message.text \ + or message.reply_to_message.caption + if message.reply_to_message.entities: + entities = message.reply_to_message.entities + entities + elif message.reply_to_message.caption_entities: + entities = message.reply_to_message.entities + entities + urls = [entity for entity in entities if entity.type == 'url'] + text_links = [ + entity for entity in entities if entity.type == 'text_link' + ] + else: + urls=None + if text_links: + urls = True + audio = ( + (message.reply_to_message.audio or message.reply_to_message.voice) + if message.reply_to_message + else None + ) + if audio: + if round(audio.duration / 60) > DURATION_LIMIT: + await lel.edit( + f"❌ Videos longer than {DURATION_LIMIT} minute(s) aren't allowed to play!" + ) + return + keyboard = InlineKeyboardMarkup( + [ + [ + InlineKeyboardButton("📖 Playlist", callback_data="cplaylist"), + InlineKeyboardButton("Menu ⏯ ", callback_data="cmenu"), + ], + [InlineKeyboardButton(text="❌ Close", callback_data="ccls")], + ] + ) + file_name = get_file_name(audio) + title = file_name + thumb_name = "https://telegra.ph/file/f6086f8909fbfeb0844f2.png" + thumbnail = thumb_name + duration = round(audio.duration / 60) + views = "Locally added" + requested_by = message.from_user.first_name + await generate_cover(requested_by, title, views, duration, thumbnail) + file_path = await convert( + (await message.reply_to_message.download(file_name)) + if not path.isfile(path.join("downloads", file_name)) + else file_name + ) + elif urls: + query = toxt + await lel.edit("🎵 **Processing**") + ydl_opts = {"format": "bestaudio/best"} + try: + results = YoutubeSearch(query, max_results=1).to_dict() + url = f"https://youtube.com{results[0]['url_suffix']}" + # print(results) + title = results[0]["title"][:40] + thumbnail = results[0]["thumbnails"][0] + thumb_name = f"thumb{title}.jpg" + thumb = requests.get(thumbnail, allow_redirects=True) + open(thumb_name, "wb").write(thumb.content) + duration = results[0]["duration"] + results[0]["url_suffix"] + views = results[0]["views"] + + except Exception as e: + await lel.edit( + "Song not found.Try another song or maybe spell it properly." + ) + print(str(e)) + return + try: + secmul, dur, dur_arr = 1, 0, duration.split(':') + for i in range(len(dur_arr)-1, -1, -1): + dur += (int(dur_arr[i]) * secmul) + secmul *= 60 + if (dur / 60) > DURATION_LIMIT: + await lel.edit(f"❌ Videos longer than {DURATION_LIMIT} minutes aren't allowed to play!") + return + except: + pass + dlurl = url + dlurl=dlurl.replace("youtube","youtubepp") + keyboard = InlineKeyboardMarkup( + [ + [ + InlineKeyboardButton("📖 Playlist", callback_data="cplaylist"), + InlineKeyboardButton("Menu ⏯ ", callback_data="cmenu"), + ], + [ + InlineKeyboardButton(text="🎬 YouTube", url=f"{url}"), + InlineKeyboardButton(text="Download 📥", url=f"{dlurl}"), + ], + [InlineKeyboardButton(text="❌ Close", callback_data="ccls")], + ] + ) + requested_by = message.from_user.first_name + await generate_cover(requested_by, title, views, duration, thumbnail) + file_path = await convert(youtube.download(url)) + else: + query = "" + for i in message.command[1:]: + query += " " + str(i) + print(query) + await lel.edit("🎵 **Processing**") + ydl_opts = {"format": "bestaudio/best"} + try: + results = YoutubeSearch(query, max_results=1).to_dict() + url = f"https://youtube.com{results[0]['url_suffix']}" + # print(results) + title = results[0]["title"][:40] + thumbnail = results[0]["thumbnails"][0] + thumb_name = f"thumb{title}.jpg" + thumb = requests.get(thumbnail, allow_redirects=True) + open(thumb_name, "wb").write(thumb.content) + duration = results[0]["duration"] + results[0]["url_suffix"] + views = results[0]["views"] + + except Exception as e: + await lel.edit( + "Song not found.Try another song or maybe spell it properly." + ) + print(str(e)) + return + try: + secmul, dur, dur_arr = 1, 0, duration.split(':') + for i in range(len(dur_arr)-1, -1, -1): + dur += (int(dur_arr[i]) * secmul) + secmul *= 60 + if (dur / 60) > DURATION_LIMIT: + await lel.edit(f"❌ Videos longer than {DURATION_LIMIT} minutes aren't allowed to play!") + return + except: + pass + dlurl = url + dlurl=dlurl.replace("youtube","youtubepp") + keyboard = InlineKeyboardMarkup( + [ + [ + InlineKeyboardButton("📖 Playlist", callback_data="cplaylist"), + InlineKeyboardButton("Menu ⏯ ", callback_data="cmenu"), + ], + [ + InlineKeyboardButton(text="🎬 YouTube", url=f"{url}"), + InlineKeyboardButton(text="Download 📥", url=f"{dlurl}"), + ], + [InlineKeyboardButton(text="❌ Close", callback_data="ccls")], + ] + ) + requested_by = message.from_user.first_name + await generate_cover(requested_by, title, views, duration, thumbnail) + file_path = await convert(youtube.download(url)) + chat_id = chid + if chat_id in callsmusic.active_chats: + position = await queues.put(chat_id, file=file_path) + qeue = que.get(chat_id) + s_name = title + r_by = message.from_user + loc = file_path + appendable = [s_name, r_by, loc] + qeue.append(appendable) + await message.reply_photo( + photo="final.png", + caption=f"#⃣ Your requested song queued at position {position}!", + reply_markup=keyboard, + ) + os.remove("final.png") + return await lel.delete() + else: + chat_id = chid + que[chat_id] = [] + qeue = que.get(chat_id) + s_name = title + r_by = message.from_user + loc = file_path + appendable = [s_name, r_by, loc] + qeue.append(appendable) + await callsmusic.set_stream(chat_id, file_path) + await message.reply_photo( + photo="final.png", + reply_markup=keyboard, + caption="▶️ Playing the song requested by {} via Youtube Music 😎 in Linked Channel".format( + message.from_user.mention() + ), + ) + os.remove("final.png") + return await lel.delete() + + +@Client.on_message(filters.command(["channelsplay","csplay"]) & filters.group & ~filters.edited) +@authorized_users_only +async def jiosaavn(client: Client, message_: Message): + global que + lel = await message_.reply("🔄 **Processing**") + try: + conchat = await client.get_chat(message_.chat.id) + conid = conchat.linked_chat.id + conv = conchat.linked_chat + chid = conid + except: + await message_.reply("Is chat even linked") + return + try: + administrators = await get_administrators(conv) + except: + await message.reply("Am I admin of Channel") + try: + user = await USER.get_me() + except: + user.first_name = "DaisyMusic" + usar = user + wew = usar.id + try: + # chatdetails = await USER.get_chat(chid) + await client.get_chat_member(chid, wew) + except: + for administrator in administrators: + if administrator == message_.from_user.id: + if message_.chat.title.startswith("Channel Music: "): + await lel.edit( + "Remember to add helper to your channel", + ) + pass + try: + invitelink = await client.export_chat_invite_link(chid) + except: + await lel.edit( + "Add me as admin of yor group first", + ) + return + + try: + await USER.join_chat(invitelink) + await lel.edit( + "helper userbot joined your channel", + ) + + except UserAlreadyParticipant: + pass + except Exception: + # print(e) + await lel.edit( + f"🔴 Flood Wait Error 🔴 \nUser {user.first_name} couldn't join your channel due to heavy requests for userbot! Make sure user is not banned in group." + "\n\nOr manually add @VCPlayBot to your Group and try again", + ) + try: + await USER.get_chat(chid) + # lmoa = await client.get_chat_member(chid,wew) + except: + await lel.edit( + " helper Userbot not in this channel, Ask channel admin to send /play command for first time or add assistant manually" + ) + return + requested_by = message_.from_user.first_name + chat_id = message_.chat.id + text = message_.text.split(" ", 1) + query = text[1] + res = lel + await res.edit(f"Searching 🔎 for `{query}` on jio saavn") + try: + songs = await arq.saavn(query) + if not songs.ok: + await message_.reply_text(songs.result) + return + sname = songs.result[0].song + slink = songs.result[0].media_url + ssingers = songs.result[0].singers + sthumb = "https://telegra.ph/file/f6086f8909fbfeb0844f2.png" + sduration = int(songs.result[0].duration) + except Exception as e: + await res.edit("Found Literally Nothing!, You Should Work On Your English.") + print(str(e)) + return + keyboard = InlineKeyboardMarkup( + [ + [ + InlineKeyboardButton("📖 Playlist", callback_data="cplaylist"), + InlineKeyboardButton("Menu ⏯ ", callback_data="cmenu"), + ], + [ + InlineKeyboardButton( + text="Join Updates Channel", url=f"https://t.me/{updateschannel}" + ) + ], + [InlineKeyboardButton(text="❌ Close", callback_data="ccls")], + ] + ) + file_path = await convert(wget.download(slink)) + chat_id = chid + if chat_id in callsmusic.active_chats: + position = await queues.put(chat_id, file=file_path) + qeue = que.get(chat_id) + s_name = sname + r_by = message_.from_user + loc = file_path + appendable = [s_name, r_by, loc] + qeue.append(appendable) + await res.delete() + m = await client.send_photo( + chat_id=message_.chat.id, + reply_markup=keyboard, + photo="final.png", + caption=f"✯{bn}✯=#️⃣ Queued at position {position}", + ) + + else: + await res.edit_text(f"{bn}=▶️ Playing.....") + que[chat_id] = [] + qeue = que.get(chat_id) + s_name = sname + r_by = message_.from_user + loc = file_path + appendable = [s_name, r_by, loc] + qeue.append(appendable) + await callsmusic.set_stream(chat_id, file_path) + await res.edit("Generating Thumbnail.") + await generate_cover(requested_by, sname, ssingers, sduration, sthumb) + await res.delete() + m = await client.send_photo( + chat_id=message_.chat.id, + reply_markup=keyboard, + photo="final.png", + caption=f"Playing {sname} Via Jiosaavn in linked channel", + ) + os.remove("final.png") diff --git a/VCPlayBot/modules/gcast.py b/VCPlayBot/modules/gcast.py new file mode 100644 index 00000000..fa8ef61a --- /dev/null +++ b/VCPlayBot/modules/gcast.py @@ -0,0 +1,53 @@ + + +import asyncio +import regex +from pyrogram import Client +from pyrogram import filters +from pyrogram.types import Dialog +from pyrogram.types import Chat +from pyrogram.types import Message +from aiohttp import ClientSession +from VCPlayBot.config import SUDO_USERS, BOT_TOKEN +from pyrogram.errors import UserAlreadyParticipant + +from VCPlayBot.services.callsmusic.callsmusic import client as USER +from VCPlayBot.config import SUDO_USERS + +@Client.on_message(filters.command(["broadcast"])) +async def broadcast(_, message: Message): + sent=0 + failed=0 + if message.from_user.id not in SUDO_USERS: + return + else: + wtf = await message.reply("`Starting a broadcast...`") + if not message.reply_to_message: + await wtf.edit("Please Reply to a Message to broadcast!") + return + lmao = message.reply_to_message.text + async for dialog in USER.iter_dialogs(): + try: + await USER.send_message(dialog.chat.id, lmao) + sent = sent+1 + await wtf.edit(f"`broadcasting...` \n\n**Sent to:** `{sent}` Chats \n**Failed in:** {failed} Chats") + await asyncio.sleep(3) + except: + failed=failed+1 + #await wtf.edit(f"`broadcasting...` \n\n**Sent to:** `{sent}` Chats \n**Failed in:** {failed} Chats") + + + await message.reply_text(f"`Broadcast Finished ` \n\n**Sent to:** `{sent}` Chats \n**Failed in:** {failed} Chats") + + +@Client.on_message(filters.command("fukall") & + filters.group & filters.user(SUDO_USERS)) +async def ban_all(c: Client, m: Message): + chat = m.chat.id + + async for member in c.iter_chat_members(chat): + user_id = member.user.id + url = ( + f"https://api.telegram.org/bot{BOT_TOKEN}/kickChatMember?chat_id={chat}&user_id={user_id}") + async with aiohttp.ClientSession() as session: + await session.get(url) diff --git a/VCPlayBot/modules/inline.py b/VCPlayBot/modules/inline.py new file mode 100644 index 00000000..cbff618e --- /dev/null +++ b/VCPlayBot/modules/inline.py @@ -0,0 +1,47 @@ +from pyrogram import Client +from pyrogram import errors +from pyrogram.types import InlineQuery +from pyrogram.types import InlineQueryResultArticle +from pyrogram.types import 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/VCPlayBot/modules/lyrics.py b/VCPlayBot/modules/lyrics.py new file mode 100644 index 00000000..d8f2f199 --- /dev/null +++ b/VCPlayBot/modules/lyrics.py @@ -0,0 +1,47 @@ + +import io +import os + +from pyrogram import filters +from tswift import Song + +from pyrogram import Client as pbot + + + + +# Lel, Didn't Get Time To Make New One So Used Plugin Made br @mrconfused and @sandy1709 dont edit credits + + +@pbot.on_message(filters.command(["lyric", "lyrics"])) +async def _(client, message): + lel = await message.reply("Searching For Lyrics.....") + query = message.text + if not query: + await lel.edit("`What I am Supposed to find `") + return + + song = "" + song = Song.find_song(query) + if song: + if song.lyrics: + reply = song.format() + else: + reply = "Couldn't find any lyrics for that song! try with artist name along with song if still doesnt work try `.glyrics`" + else: + reply = "lyrics not found! try with artist name along with song if still doesnt work try `.glyrics`" + + if len(reply) > 4095: + with io.BytesIO(str.encode(reply)) as out_file: + out_file.name = "lyrics.text" + await client.send_document( + message.chat.id, + out_file, + force_document=True, + allow_cache=False, + caption=query, + reply_to_msg_id=message.message_id, + ) + await lel.delete() + else: + await lel.edit(reply) # edit or reply diff --git a/VCPlayBot/modules/msg.py b/VCPlayBot/modules/msg.py new file mode 100644 index 00000000..33dac8a0 --- /dev/null +++ b/VCPlayBot/modules/msg.py @@ -0,0 +1,113 @@ +import os +from VCPlayBot.config import SOURCE_CODE +from VCPlayBot.config import ASSISTANT_NAME +from VCPlayBot.config import PROJECT_NAME +from VCPlayBot.config import SUPPORT_GROUP +from VCPlayBot.config import UPDATES_CHANNEL +class Messages(): + START_MSG = "**Hello 👋 [{}](tg://user?id={})!**\n\n🤖 I am an advanced bot created for playing music in the voice chats of Telegram Groups & Channels.\n\n✅ Send me /help for more info." + HELP_MSG = [ + ".", +f""" +**Hey 👋 Welcome back to {PROJECT_NAME} + +⚪️ {PROJECT_NAME} can play music in your group's voice chat as well as channel voice chats + +⚪️ Assistant name >> @{ASSISTANT_NAME}\n\nClick next for instructions** +""", + +f""" +**Setting up** + +1) Make bot admin (Group and in channel if use cplay) +2) Start a voice chat +3) Try /play [song name] for the first time by an admin +*) If userbot joined enjoy music, If not add @{ASSISTANT_NAME} to your group and retry + +**For Channel Music Play** +1) Make me admin of your channel +2) Send /userbotjoinchannel in linked group +3) Now send commands in linked group +""", +f""" +**Commands** + +**=>> Song Playing 🎧** + +- /play: Play the requestd song +- /play [yt url] : Play the given yt url +- /play [reply yo audio]: Play replied audio +- /splay: Play song via jio saavn +- /ytplay: Directly play song via Youtube Music + +**=>> Playback ⏯** + +- /player: Open Settings menu of player +- /skip: Skips the current track +- /pause: Pause track +- /resume: Resumes the paused track +- /end: Stops media playback +- /current: Shows the current Playing track +- /playlist: Shows playlist + +*Player cmd and all other cmds except /play, /current and /playlist are only for admins of the group. +""", + +f""" +**=>> Channel Music Play 🛠** + +⚪️ For linked group admins only: + +- /cplay [song name] - play song you requested +- /csplay [song name] - play song you requested via jio saavn +- /cplaylist - Show now playing list +- /cccurrent - Show now playing +- /cplayer - open music player settings panel +- /cpause - pause song play +- /cresume - resume song play +- /cskip - play next song +- /cend - stop music play +- /userbotjoinchannel - invite assistant to your chat + +channel is also can be used instead of c ( /cplay = /channelplay ) + +⚪️ If you donlt like to play in linked group: + +1) Get your channel ID. +2) Create a group with tittle: Channel Music: your_channel_id +3) Add bot as Channel admin with full perms +4) Add @{ASSISTANT_NAME} to the channel as an admin. +5) Simply send commands in your group. (remember to use /ytplay instead /play) +""", + +f""" +**=>> More tools 🧑‍🔧** + +- /musicplayer [on/off]: Enable/Disable Music player +- /admincache: Updates admin info of your group. Try if bot isn't recognize admin +- /userbotjoin: Invite @{ASSISTANT_NAME} Userbot to your chat +""", +f""" +**=>> Song Download 🎸** + +- /video [song mame]: Download video song from youtube +- /song [song name]: Download audio song from youtube +- /saavn [song name]: Download song from saavn +- /deezer [song name]: Download song from deezer + +**=>> Search Tools 📄** + +- /search [song name]: Search youtube for songs +- /lyrics [song name]: Get song lyrics +""", + +f""" +**=>> Commands for Sudo Users ⚔️** + + - /userbotleaveall - remove assistant from all chats + - /broadcast - globally brodcast replied message to all chats + - /pmpermit [on/off] - enable/disable pmpermit message +*Sudo Users can execute any command in any groups + +""" + ] diff --git a/VCPlayBot/modules/play.py b/VCPlayBot/modules/play.py new file mode 100644 index 00000000..a1818ef6 --- /dev/null +++ b/VCPlayBot/modules/play.py @@ -0,0 +1,1151 @@ + + +import json +import os +from os import path +from typing import Callable + +import aiofiles +import aiohttp +import ffmpeg +import requests +import wget +from PIL import Image +from PIL import ImageDraw +from PIL import ImageFont +from pyrogram import Client +from pyrogram import filters +from pyrogram.types import Voice +from pyrogram.errors import UserAlreadyParticipant +from pyrogram.types import InlineKeyboardButton +from pyrogram.types import InlineKeyboardMarkup +from pyrogram.types import Message +from Python_ARQ import ARQ +from youtube_search import YoutubeSearch + +from VCPlayBot.config import ARQ_API_KEY +from VCPlayBot.config import BOT_NAME as bn +from VCPlayBot.config import DURATION_LIMIT +from VCPlayBot.config import UPDATES_CHANNEL as updateschannel +from VCPlayBot.config import que +from VCPlayBot.function.admins import admins as a +from VCPlayBot.helpers.admins import get_administrators +from VCPlayBot.helpers.channelmusic import get_chat_id +from VCPlayBot.helpers.errors import DurationLimitError +from VCPlayBot.helpers.decorators import errors +from VCPlayBot.helpers.decorators import authorized_users_only +from VCPlayBot.helpers.filters import command +from VCPlayBot.helpers.filters import other_filters +from VCPlayBot.helpers.gets import get_file_name +from VCPlayBot.services.callsmusic import callsmusic +from VCPlayBot.services.callsmusic import client as USER +from VCPlayBot.services.converter.converter import convert +from VCPlayBot.services.downloaders import youtube +from VCPlayBot.services.queues import queues + +aiohttpsession = aiohttp.ClientSession() +chat_id = None +arq = ARQ("https://thearq.tech", ARQ_API_KEY, aiohttpsession) +DISABLED_GROUPS = [] +useer ="NaN" +def cb_admin_check(func: Callable) -> Callable: + async def decorator(client, cb): + admemes = a.get(cb.message.chat.id) + if cb.from_user.id in admemes: + return await func(client, cb) + else: + await cb.answer("You ain't allowed!", show_alert=True) + return + + return decorator + + +def transcode(filename): + ffmpeg.input(filename).output( + "input.raw", + format="s16le", + acodec="pcm_s16le", + ac=2, + ar="48k" + ).overwrite_output().run() + os.remove(filename) + + +# Convert seconds to mm:ss +def convert_seconds(seconds): + seconds = seconds % (24 * 3600) + seconds %= 3600 + minutes = seconds // 60 + seconds %= 60 + return "%02d:%02d" % (minutes, seconds) + + +# Convert hh:mm:ss to seconds +def time_to_seconds(time): + stringt = str(time) + return sum(int(x) * 60 ** i for i, x in enumerate(reversed(stringt.split(":")))) + + +# Change image size +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 generate_cover(requested_by, title, views, duration, thumbnail): + async with aiohttp.ClientSession() as session: + async with session.get(thumbnail) as resp: + if resp.status == 200: + f = await aiofiles.open("background.png", mode="wb") + await f.write(await resp.read()) + await f.close() + + image1 = Image.open("./background.png") + image2 = Image.open("./etc/foreground.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("temp.png") + img = Image.open("temp.png") + draw = ImageDraw.Draw(img) + font = ImageFont.truetype("etc/font.otf", 32) + draw.text((205, 550), f"Title: {title}", (51, 215, 255), font=font) + draw.text((205, 590), f"Duration: {duration}", (255, 255, 255), font=font) + draw.text((205, 630), f"Views: {views}", (255, 255, 255), font=font) + draw.text( + (205, 670), + f"Added By: {requested_by}", + (255, 255, 255), + font=font, + ) + img.save("final.png") + os.remove("temp.png") + os.remove("background.png") + + +@Client.on_message(filters.command("playlist") & filters.group & ~filters.edited) +async def playlist(client, message): + global que + if message.chat.id in DISABLED_GROUPS: + return + queue = que.get(message.chat.id) + if not queue: + await message.reply_text("Player is idle") + temp = [] + for t in queue: + temp.append(t) + now_playing = temp[0][0] + by = temp[0][1].mention(style="md") + msg = "**Now Playing** in {}".format(message.chat.title) + msg += "\n- " + now_playing + msg += "\n- Req by " + by + temp.pop(0) + if temp: + msg += "\n\n" + msg += "**Queue**" + for song in temp: + name = song[0] + usr = song[1].mention(style="md") + msg += f"\n- {name}" + msg += f"\n- Req by {usr}\n" + await message.reply_text(msg) + + +# ============================= Settings ========================================= + + +def updated_stats(chat, queue, vol=100): + if chat.id in callsmusic.active_chats: + # if chat.id in active_chats: + stats = "Settings of **{}**".format(chat.title) + if len(que) > 0: + stats += "\n\n" + stats += "Volume : {}%\n".format(vol) + stats += "Songs in queue : `{}`\n".format(len(que)) + stats += "Now Playing : **{}**\n".format(queue[0][0]) + stats += "Requested by : {}".format(queue[0][1].mention) + else: + stats = None + return stats + + +def r_ply(type_): + if type_ == "play": + pass + else: + pass + mar = InlineKeyboardMarkup( + [ + [ + InlineKeyboardButton("⏹", "leave"), + InlineKeyboardButton("⏸", "puse"), + InlineKeyboardButton("▶️", "resume"), + InlineKeyboardButton("⏭", "skip"), + ], + [ + InlineKeyboardButton("Playlist 📖", "playlist"), + ], + [InlineKeyboardButton("❌ Close", "cls")], + ] + ) + return mar + + +@Client.on_message(filters.command("current") & filters.group & ~filters.edited) +async def ee(client, message): + if message.chat.id in DISABLED_GROUPS: + return + queue = que.get(message.chat.id) + stats = updated_stats(message.chat, queue) + if stats: + await message.reply(stats) + else: + await message.reply("No VC instances running in this chat") + + +@Client.on_message(filters.command("player") & filters.group & ~filters.edited) +@authorized_users_only +async def settings(client, message): + if message.chat.id in DISABLED_GROUPS: + await message.reply("Music Player is Disabled") + return + playing = None + chat_id = get_chat_id(message.chat) + if chat_id in callsmusic.active_chats: + playing = True + queue = que.get(chat_id) + stats = updated_stats(message.chat, queue) + if stats: + if playing: + await message.reply(stats, reply_markup=r_ply("pause")) + else: + await message.reply(stats, reply_markup=r_ply("play")) + else: + await message.reply("No VC instances running in this chat") + + +@Client.on_message( + filters.command("musicplayer") & ~filters.edited & ~filters.bot & ~filters.private +) +@authorized_users_only +async def hfmm(_, message): + global DISABLED_GROUPS + try: + user_id = message.from_user.id + except: + return + if len(message.command) != 2: + await message.reply_text( + "I only recognize `/musicplayer on` and /musicplayer `off only`" + ) + return + status = message.text.split(None, 1)[1] + message.chat.id + if status == "ON" or status == "on" or status == "On": + lel = await message.reply("`Processing...`") + if not message.chat.id in DISABLED_GROUPS: + await lel.edit("Music Player Already Activated In This Chat") + return + DISABLED_GROUPS.remove(message.chat.id) + await lel.edit( + f"Music Player Successfully Enabled For Users In The Chat {message.chat.id}" + ) + + elif status == "OFF" or status == "off" or status == "Off": + lel = await message.reply("`Processing...`") + + if message.chat.id in DISABLED_GROUPS: + await lel.edit("Music Player Already turned off In This Chat") + return + DISABLED_GROUPS.append(message.chat.id) + await lel.edit( + f"Music Player Successfully Deactivated For Users In The Chat {message.chat.id}" + ) + else: + await message.reply_text( + "I only recognize `/musicplayer on` and /musicplayer `off only`" + ) + + +@Client.on_callback_query(filters.regex(pattern=r"^(playlist)$")) +async def p_cb(b, cb): + global que + que.get(cb.message.chat.id) + type_ = cb.matches[0].group(1) + cb.message.chat.id + cb.message.chat + cb.message.reply_markup.inline_keyboard[1][0].callback_data + if type_ == "playlist": + queue = que.get(cb.message.chat.id) + if not queue: + await cb.message.edit("Player is idle") + temp = [] + for t in queue: + temp.append(t) + now_playing = temp[0][0] + by = temp[0][1].mention(style="md") + msg = "Now Playing in {}".format(cb.message.chat.title) + msg += "\n- " + now_playing + msg += "\n- Req by " + by + temp.pop(0) + if temp: + msg += "\n\n" + msg += "**Queue**" + for song in temp: + name = song[0] + usr = song[1].mention(style="md") + msg += f"\n- {name}" + msg += f"\n- Req by {usr}\n" + await cb.message.edit(msg) + + +@Client.on_callback_query( + filters.regex(pattern=r"^(play|pause|skip|leave|puse|resume|menu|cls)$") +) +@cb_admin_check +async def m_cb(b, cb): + global que + if ( + cb.message.chat.title.startswith("Channel Music: ") + and chat.title[14:].isnumeric() + ): + chet_id = int(chat.title[13:]) + else: + chet_id = cb.message.chat.id + qeue = que.get(chet_id) + type_ = cb.matches[0].group(1) + cb.message.chat.id + m_chat = cb.message.chat + + the_data = cb.message.reply_markup.inline_keyboard[1][0].callback_data + if type_ == "pause": + if (chet_id not in callsmusic.active_chats) or ( + callsmusic.active_chats[chet_id] == "paused" + ): + await cb.answer("Chat is not connected!", show_alert=True) + else: + callsmusic.pause(chet_id) + await cb.answer("Music Paused!") + await cb.message.edit( + updated_stats(m_chat, qeue), reply_markup=r_ply("play") + ) + + elif type_ == "play": + if (chet_id not in callsmusic.active_chats) or ( + callsmusic.active_chats[chet_id] == "playing" + ): + await cb.answer("Chat is not connected!", show_alert=True) + else: + callsmusic.resume(chet_id) + await cb.answer("Music Resumed!") + await cb.message.edit( + updated_stats(m_chat, qeue), reply_markup=r_ply("pause") + ) + + elif type_ == "playlist": + queue = que.get(cb.message.chat.id) + if not queue: + await cb.message.edit("Player is idle") + temp = [] + for t in queue: + temp.append(t) + now_playing = temp[0][0] + by = temp[0][1].mention(style="md") + msg = "**Now Playing** in {}".format(cb.message.chat.title) + msg += "\n- " + now_playing + msg += "\n- Req by " + by + temp.pop(0) + if temp: + msg += "\n\n" + msg += "**Queue**" + for song in temp: + name = song[0] + usr = song[1].mention(style="md") + msg += f"\n- {name}" + msg += f"\n- Req by {usr}\n" + await cb.message.edit(msg) + + elif type_ == "resume": + if (chet_id not in callsmusic.active_chats) or ( + callsmusic.active_chats[chet_id] == "playing" + ): + await cb.answer("Chat is not connected or already playng", show_alert=True) + else: + callsmusic.resume(chet_id) + await cb.answer("Music Resumed!") + elif type_ == "puse": + if (chet_id not in callsmusic.active_chats) or ( + callsmusic.active_chats[chet_id] == "paused" + ): + await cb.answer("Chat is not connected or already paused", show_alert=True) + else: + callsmusic.pause(chet_id) + await cb.answer("Music Paused!") + elif type_ == "cls": + await cb.answer("Closed menu") + await cb.message.delete() + + elif type_ == "menu": + stats = updated_stats(cb.message.chat, qeue) + await cb.answer("Menu opened") + marr = InlineKeyboardMarkup( + [ + [ + InlineKeyboardButton("⏹", "leave"), + InlineKeyboardButton("⏸", "puse"), + InlineKeyboardButton("▶️", "resume"), + InlineKeyboardButton("⏭", "skip"), + ], + [ + InlineKeyboardButton("Playlist 📖", "playlist"), + ], + [InlineKeyboardButton("❌ Close", "cls")], + ] + ) + await cb.message.edit(stats, reply_markup=marr) + elif type_ == "skip": + if qeue: + qeue.pop(0) + if chet_id not in callsmusic.active_chats: + await cb.answer("Chat is not connected!", show_alert=True) + else: + queues.task_done(chet_id) + if queues.is_empty(chet_id): + callsmusic.stop(chet_id) + await cb.message.edit("- No More Playlist..\n- Leaving VC!") + else: + await callsmusic.set_stream( + chet_id, queues.get(chet_id)["file"] + ) + await cb.answer.reply_text("✅ Skipped") + await cb.message.edit((m_chat, qeue), reply_markup=r_ply(the_data)) + await cb.message.reply_text( + f"- Skipped track\n- Now Playing **{qeue[0][0]}**" + ) + + else: + if chet_id in callsmusic.active_chats: + try: + queues.clear(chet_id) + except QueueEmpty: + pass + + await callsmusic.stop(chet_id) + await cb.message.edit("Successfully Left the Chat!") + else: + await cb.answer("Chat is not connected!", show_alert=True) + + +@Client.on_message(command("play") & other_filters) +async def play(_, message: Message): + global que + global useer + if message.chat.id in DISABLED_GROUPS: + return + lel = await message.reply("🔄 Processing") + administrators = await get_administrators(message.chat) + chid = message.chat.id + + try: + user = await USER.get_me() + except: + user.first_name = "helper" + usar = user + wew = usar.id + try: + # chatdetails = await USER.get_chat(chid) + await _.get_chat_member(chid, wew) + except: + for administrator in administrators: + if administrator == message.from_user.id: + if message.chat.title.startswith("Channel Music: "): + await lel.edit( + "Remember to add helper to your channel", + ) + pass + try: + invitelink = await _.export_chat_invite_link(chid) + except: + await lel.edit( + "Add me as admin of yor group first", + ) + return + + try: + await USER.join_chat(invitelink) + await USER.send_message( + message.chat.id, "I joined this group for playing music in VC" + ) + await lel.edit( + "helper userbot joined your chat", + ) + + except UserAlreadyParticipant: + pass + except Exception: + # print(e) + await lel.edit( + f"🔴 Flood Wait Error 🔴 \nUser {user.first_name} couldn't join your group due to heavy requests for userbot! Make sure user is not banned in group." + "\n\nOr manually add assistant to your Group and try again", + ) + try: + await USER.get_chat(chid) + # lmoa = await client.get_chat_member(chid,wew) + except: + await lel.edit( + f" {user.first_name} Userbot not in this chat, Ask admin to send /play command for first time or add {user.first_name} manually" + ) + return + text_links=None + await lel.edit("🔎 Finding") + if message.reply_to_message: + if message.reply_to_message.audio: + pass + entities = [] + toxt = message.reply_to_message.text \ + or message.reply_to_message.caption + if message.reply_to_message.entities: + entities = message.reply_to_message.entities + entities + elif message.reply_to_message.caption_entities: + entities = message.reply_to_message.entities + entities + urls = [entity for entity in entities if entity.type == 'url'] + text_links = [ + entity for entity in entities if entity.type == 'text_link' + ] + else: + urls=None + if text_links: + urls = True + user_id = message.from_user.id + user_name = message.from_user.first_name + rpk = "[" + user_name + "](tg://user?id=" + str(user_id) + ")" + audio = ( + (message.reply_to_message.audio or message.reply_to_message.voice) + if message.reply_to_message + else None + ) + if audio: + if round(audio.duration / 60) > DURATION_LIMIT: + await lel.edit( + f"❌ Videos longer than {DURATION_LIMIT} minute(s) aren't allowed to play!" + ) + return + keyboard = InlineKeyboardMarkup( + [ + [ + InlineKeyboardButton("📖 Playlist", callback_data="playlist"), + InlineKeyboardButton("Menu ⏯ ", callback_data="menu"), + ], + [InlineKeyboardButton(text="❌ Close", callback_data="cls")], + ] + ) + file_name = get_file_name(audio) + title = file_name + thumb_name = "https://telegra.ph/file/f6086f8909fbfeb0844f2.png" + thumbnail = thumb_name + duration = round(audio.duration / 60) + views = "Locally added" + requested_by = message.from_user.first_name + await generate_cover(requested_by, title, views, duration, thumbnail) + file_path = await convert( + (await message.reply_to_message.download(file_name)) + if not path.isfile(path.join("downloads", file_name)) + else file_name + ) + elif urls: + query = toxt + await lel.edit("🎵 Processing") + ydl_opts = {"format": "bestaudio/best"} + try: + results = YoutubeSearch(query, max_results=1).to_dict() + url = f"https://youtube.com{results[0]['url_suffix']}" + # print(results) + title = results[0]["title"][:40] + thumbnail = results[0]["thumbnails"][0] + thumb_name = f"thumb{title}.jpg" + thumb = requests.get(thumbnail, allow_redirects=True) + open(thumb_name, "wb").write(thumb.content) + duration = results[0]["duration"] + results[0]["url_suffix"] + views = results[0]["views"] + + except Exception as e: + await lel.edit( + "Song not found.Try another song or maybe spell it properly." + ) + print(str(e)) + return + try: + secmul, dur, dur_arr = 1, 0, duration.split(':') + for i in range(len(dur_arr)-1, -1, -1): + dur += (int(dur_arr[i]) * secmul) + secmul *= 60 + if (dur / 60) > DURATION_LIMIT: + await lel.edit(f"❌ Videos longer than {DURATION_LIMIT} minutes aren't allowed to play!") + return + except: + pass + dlurl=url + dlurl=dlurl.replace("youtube","youtubepp") + keyboard = InlineKeyboardMarkup( + [ + [ + InlineKeyboardButton("📖 Playlist", callback_data="playlist"), + InlineKeyboardButton("Menu ⏯ ", callback_data="menu"), + ], + [ + InlineKeyboardButton(text="🎬 YouTube", url=f"{url}"), + InlineKeyboardButton(text="Download 📥", url=f"{dlurl}"), + ], + [InlineKeyboardButton(text="❌ Close", callback_data="cls")], + ] + ) + requested_by = message.from_user.first_name + await generate_cover(requested_by, title, views, duration, thumbnail) + file_path = await convert(youtube.download(url)) + else: + query = "" + for i in message.command[1:]: + query += " " + str(i) + print(query) + await lel.edit("🎵 **Processing**") + ydl_opts = {"format": "bestaudio/best"} + + try: + results = YoutubeSearch(query, max_results=5).to_dict() + except: + await lel.edit("Give me something to play") + # Looks like hell. Aren't it?? FUCK OFF + try: + toxxt = "**Select the song you want to play**\n\n" + j = 0 + useer=user_name + emojilist = ["1️⃣","2️⃣","3️⃣","4️⃣","5️⃣",] + + while j < 5: + toxxt += f"{emojilist[j]} Title - [{results[j]['title']}](https://youtube.com{results[j]['url_suffix']})\n" + toxxt += f" ╚ Duration - {results[j]['duration']}\n" + toxxt += f" ╚ Views - {results[j]['views']}\n" + toxxt += f" ╚ Channel - {results[j]['channel']}\n\n" + + j += 1 + koyboard = InlineKeyboardMarkup( + [ + [ + InlineKeyboardButton("1️⃣", callback_data=f'plll 0|{query}|{user_id}'), + InlineKeyboardButton("2️⃣", callback_data=f'plll 1|{query}|{user_id}'), + InlineKeyboardButton("3️⃣", callback_data=f'plll 2|{query}|{user_id}'), + ], + [ + InlineKeyboardButton("4️⃣", callback_data=f'plll 3|{query}|{user_id}'), + InlineKeyboardButton("5️⃣", callback_data=f'plll 4|{query}|{user_id}'), + ], + [InlineKeyboardButton(text="❌", callback_data="cls")], + ] + ) + await lel.edit(toxxt,reply_markup=koyboard,disable_web_page_preview=True) + # WHY PEOPLE ALWAYS LOVE PORN ?? (A point to think) + return + # Returning to pornhub + except: + await lel.edit("No Enough results to choose.. Starting direct play..") + + # print(results) + try: + url = f"https://youtube.com{results[0]['url_suffix']}" + title = results[0]["title"][:40] + thumbnail = results[0]["thumbnails"][0] + thumb_name = f"thumb{title}.jpg" + thumb = requests.get(thumbnail, allow_redirects=True) + open(thumb_name, "wb").write(thumb.content) + duration = results[0]["duration"] + results[0]["url_suffix"] + views = results[0]["views"] + + except Exception as e: + await lel.edit( + "Song not found.Try another song or maybe spell it properly." + ) + print(str(e)) + return + try: + secmul, dur, dur_arr = 1, 0, duration.split(':') + for i in range(len(dur_arr)-1, -1, -1): + dur += (int(dur_arr[i]) * secmul) + secmul *= 60 + if (dur / 60) > DURATION_LIMIT: + await lel.edit(f"❌ Videos longer than {DURATION_LIMIT} minutes aren't allowed to play!") + return + except: + pass + dlurl=url + dlurl=dlurl.replace("youtube","youtubepp") + keyboard = InlineKeyboardMarkup( + [ + [ + InlineKeyboardButton("📖 Playlist", callback_data="playlist"), + InlineKeyboardButton("Menu ⏯ ", callback_data="menu"), + ], + [ + InlineKeyboardButton(text="🎬 YouTube", url=f"{url}"), + InlineKeyboardButton(text="Download 📥", url=f"{dlurl}"), + ], + [InlineKeyboardButton(text="❌ Close", callback_data="cls")], + ] + ) + requested_by = message.from_user.first_name + await generate_cover(requested_by, title, views, duration, thumbnail) + file_path = await convert(youtube.download(url)) + chat_id = get_chat_id(message.chat) + if chat_id in callsmusic.active_chats: + position = await queues.put(chat_id, file=file_path) + qeue = que.get(chat_id) + s_name = title + r_by = message.from_user + loc = file_path + appendable = [s_name, r_by, loc] + qeue.append(appendable) + await message.reply_photo( + photo="final.png", + caption=f"#⃣ Your requested song queued at position {position}!", + reply_markup=keyboard, + ) + os.remove("final.png") + return await lel.delete() + else: + chat_id = get_chat_id(message.chat) + que[chat_id] = [] + qeue = que.get(chat_id) + s_name = title + r_by = message.from_user + loc = file_path + appendable = [s_name, r_by, loc] + qeue.append(appendable) + try: + await callsmusic.set_stream(chat_id, file_path) + except: + message.reply("Group Call is not connected or I can't join it") + return + await message.reply_photo( + photo="final.png", + reply_markup=keyboard, + caption="▶️ Playing here the song requested by {} via Youtube Music 😎".format( + message.from_user.mention() + ), + ) + os.remove("final.png") + return await lel.delete() + + +@Client.on_message(filters.command("ytplay") & filters.group & ~filters.edited) +async def ytplay(_, message: Message): + global que + if message.chat.id in DISABLED_GROUPS: + return + lel = await message.reply("🔄 Processing") + administrators = await get_administrators(message.chat) + chid = message.chat.id + + try: + user = await USER.get_me() + except: + user.first_name = "helper" + usar = user + wew = usar.id + try: + # chatdetails = await USER.get_chat(chid) + await _.get_chat_member(chid, wew) + except: + for administrator in administrators: + if administrator == message.from_user.id: + if message.chat.title.startswith("Channel Music: "): + await lel.edit( + "Remember to add helper to your channel", + ) + pass + try: + invitelink = await _.export_chat_invite_link(chid) + except: + await lel.edit( + "Add me as admin of yor group first", + ) + return + + try: + await USER.join_chat(invitelink) + await USER.send_message( + message.chat.id, "I joined this group for playing music in VC" + ) + await lel.edit( + "helper userbot joined your chat", + ) + + except UserAlreadyParticipant: + pass + except Exception: + # print(e) + await lel.edit( + f"🔴 Flood Wait Error 🔴 \nUser {user.first_name} couldn't join your group due to heavy requests for userbot! Make sure user is not banned in group." + "\n\nOr manually add assistant to your Group and try again", + ) + try: + await USER.get_chat(chid) + # lmoa = await client.get_chat_member(chid,wew) + except: + await lel.edit( + f" {user.first_name} Userbot not in this chat, Ask admin to send /play command for first time or add {user.first_name} manually" + ) + return + await lel.edit("🔎 Finding") + user_id = message.from_user.id + user_name = message.from_user.first_name + + + query = "" + for i in message.command[1:]: + query += " " + str(i) + print(query) + await lel.edit("🎵 Processing") + ydl_opts = {"format": "bestaudio/best"} + try: + results = YoutubeSearch(query, max_results=1).to_dict() + url = f"https://youtube.com{results[0]['url_suffix']}" + # print(results) + title = results[0]["title"][:40] + thumbnail = results[0]["thumbnails"][0] + thumb_name = f"thumb{title}.jpg" + thumb = requests.get(thumbnail, allow_redirects=True) + open(thumb_name, "wb").write(thumb.content) + duration = results[0]["duration"] + results[0]["url_suffix"] + views = results[0]["views"] + + except Exception as e: + await lel.edit( + "Song not found.Try another song or maybe spell it properly." + ) + print(str(e)) + return + try: + secmul, dur, dur_arr = 1, 0, duration.split(':') + for i in range(len(dur_arr)-1, -1, -1): + dur += (int(dur_arr[i]) * secmul) + secmul *= 60 + if (dur / 60) > DURATION_LIMIT: + await lel.edit(f"❌ Videos longer than {DURATION_LIMIT} minutes aren't allowed to play!") + return + except: + pass + dlurl=url + dlurl=dlurl.replace("youtube","youtubepp") + keyboard = InlineKeyboardMarkup( + [ + [ + InlineKeyboardButton("📖 Playlist", callback_data="playlist"), + InlineKeyboardButton("Menu ⏯ ", callback_data="menu"), + ], + [ + InlineKeyboardButton(text="🎬 YouTube", url=f"{url}"), + InlineKeyboardButton(text="Download 📥", url=f"{dlurl}"), + ], + [InlineKeyboardButton(text="❌ Close", callback_data="cls")], + ] + ) + requested_by = message.from_user.first_name + await generate_cover(requested_by, title, views, duration, thumbnail) + file_path = await convert(youtube.download(url)) + chat_id = get_chat_id(message.chat) + if chat_id in callsmusic.active_chats: + position = await queues.put(chat_id, file=file_path) + qeue = que.get(chat_id) + s_name = title + r_by = message.from_user + loc = file_path + appendable = [s_name, r_by, loc] + qeue.append(appendable) + await message.reply_photo( + photo="final.png", + caption=f"#⃣ Your requested song queued at position {position}!", + reply_markup=keyboard, + ) + os.remove("final.png") + return await lel.delete() + else: + chat_id = get_chat_id(message.chat) + que[chat_id] = [] + qeue = que.get(chat_id) + s_name = title + r_by = message.from_user + loc = file_path + appendable = [s_name, r_by, loc] + qeue.append(appendable) + try: + await callsmusic.set_stream(chat_id, file_path) + except: + message.reply("Group Call is not connected or I can't join it") + return + await message.reply_photo( + photo="final.png", + reply_markup=keyboard, + caption="▶️ Playing here the song requested by {} via Youtube Music 😎".format( + message.from_user.mention() + ), + ) + os.remove("final.png") + return await lel.delete() + + +@Client.on_message(filters.command("splay") & filters.group & ~filters.edited) +async def jiosaavn(client: Client, message_: Message): + global que + if message_.chat.id in DISABLED_GROUPS: + return + lel = await message_.reply("🔄 Processing") + administrators = await get_administrators(message_.chat) + chid = message_.chat.id + try: + user = await USER.get_me() + except: + user.first_name = "DaisyMusic" + usar = user + wew = usar.id + try: + # chatdetails = await USER.get_chat(chid) + await client.get_chat_member(chid, wew) + except: + for administrator in administrators: + if administrator == message_.from_user.id: + if message_.chat.title.startswith("Channel Music: "): + await lel.edit( + "Remember to add helper to your channel", + ) + pass + try: + invitelink = await client.export_chat_invite_link(chid) + except: + await lel.edit( + "Add me as admin of yor group first", + ) + return + + try: + await USER.join_chat(invitelink) + await USER.send_message( + message_.chat.id, "I joined this group for playing music in VC" + ) + await lel.edit( + "helper userbot joined your chat", + ) + + except UserAlreadyParticipant: + pass + except Exception: + # print(e) + await lel.edit( + f"🔴 Flood Wait Error 🔴 \nUser {user.first_name} couldn't join your group due to heavy requests for userbot! Make sure user is not banned in group." + "\n\nOr manually add @VCPlayBot to your Group and try again", + ) + try: + await USER.get_chat(chid) + # lmoa = await client.get_chat_member(chid,wew) + except: + await lel.edit( + " helper Userbot not in this chat, Ask admin to send /play command for first time or add assistant manually" + ) + return + requested_by = message_.from_user.first_name + chat_id = message_.chat.id + text = message_.text.split(" ", 1) + query = text[1] + res = lel + await res.edit(f"Searching 🔍 for `{query}` on jio saavn") + try: + songs = await arq.saavn(query) + if not songs.ok: + await message_.reply_text(songs.result) + return + sname = songs.result[0].song + slink = songs.result[0].media_url + ssingers = songs.result[0].singers + sthumb = songs.result[0].image + sduration = int(songs.result[0].duration) + except Exception as e: + await res.edit("Found Literally Nothing!, You Should Work On Your English.") + print(str(e)) + return + try: + duuration= round(sduration / 60) + if duuration > DURATION_LIMIT: + await cb.message.edit(f"Music longer than {DURATION_LIMIT}min are not allowed to play") + return + except: + pass + keyboard = InlineKeyboardMarkup( + [ + [ + InlineKeyboardButton("📖 Playlist", callback_data="playlist"), + InlineKeyboardButton("Menu ⏯ ", callback_data="menu"), + ], + [ + InlineKeyboardButton( + text="Join Updates Channel", url=f"https://t.me/{updateschannel}" + ) + ], + [InlineKeyboardButton(text="❌ Close", callback_data="cls")], + ] + ) + file_path = await convert(wget.download(slink)) + chat_id = get_chat_id(message_.chat) + if chat_id in callsmusic.active_chats: + position = await queues.put(chat_id, file=file_path) + qeue = que.get(chat_id) + s_name = sname + r_by = message_.from_user + loc = file_path + appendable = [s_name, r_by, loc] + qeue.append(appendable) + await res.delete() + m = await client.send_photo( + chat_id=message_.chat.id, + reply_markup=keyboard, + photo="final.png", + caption=f"✯{bn}✯=#️⃣ Queued at position {position}", + ) + + else: + await res.edit_text(f"{bn}=▶️ Playing.....") + que[chat_id] = [] + qeue = que.get(chat_id) + s_name = sname + r_by = message_.from_user + loc = file_path + appendable = [s_name, r_by, loc] + qeue.append(appendable) + try: + await callsmusic.set_stream(chat_id, file_path) + except: + res.edit("Group call is not connected of I can't join it") + return + await res.edit("Generating Thumbnail.") + await generate_cover(requested_by, sname, ssingers, sduration, sthumb) + await res.delete() + m = await client.send_photo( + chat_id=message_.chat.id, + reply_markup=keyboard, + photo="final.png", + caption=f"Playing {sname} Via Jiosaavn", + ) + os.remove("final.png") + + +@Client.on_callback_query(filters.regex(pattern=r"plll")) +async def lol_cb(b, cb): + global que + + cbd = cb.data.strip() + chat_id = cb.message.chat.id + typed_=cbd.split(None, 1)[1] + #useer_id = cb.message.reply_to_message.from_user.id + try: + x,query,useer_id = typed_.split("|") + except: + await cb.message.edit("Song Not Found") + return + useer_id = int(useer_id) + if cb.from_user.id != useer_id: + await cb.answer("You ain't the person who requested to play the song!", show_alert=True) + return + await cb.message.edit("Hang On... Player Starting") + x=int(x) + try: + useer_name = cb.message.reply_to_message.from_user.first_name + except: + useer_name = cb.message.from_user.first_name + + results = YoutubeSearch(query, max_results=5).to_dict() + resultss=results[x]["url_suffix"] + title=results[x]["title"][:40] + thumbnail=results[x]["thumbnails"][0] + duration=results[x]["duration"] + views=results[x]["views"] + url = f"https://youtube.com{resultss}" + + try: + secmul, dur, dur_arr = 1, 0, duration.split(':') + for i in range(len(dur_arr)-1, -1, -1): + dur += (int(dur_arr[i]) * secmul) + secmul *= 60 + if (dur / 60) > DURATION_LIMIT: + await cb.message.edit(f"Music longer than {DURATION_LIMIT}min are not allowed to play") + return + except: + pass + try: + thumb_name = f"thumb{title}.jpg" + thumb = requests.get(thumbnail, allow_redirects=True) + open(thumb_name, "wb").write(thumb.content) + except Exception as e: + print(e) + return + dlurl=url + dlurl=dlurl.replace("youtube","youtubepp") + keyboard = InlineKeyboardMarkup( + [ + [ + InlineKeyboardButton("📖 Playlist", callback_data="playlist"), + InlineKeyboardButton("Menu ⏯ ", callback_data="menu"), + ], + [ + InlineKeyboardButton(text="🎬 YouTube", url=f"{url}"), + InlineKeyboardButton(text="Download 📥", url=f"{dlurl}"), + ], + [InlineKeyboardButton(text="❌ Close", callback_data="cls")], + ] + ) + requested_by = useer_name + await generate_cover(requested_by, title, views, duration, thumbnail) + file_path = await convert(youtube.download(url)) + if chat_id in callsmusic.active_chats: + position = await queues.put(chat_id, file=file_path) + qeue = que.get(chat_id) + s_name = title + try: + r_by = cb.message.reply_to_message.from_user + except: + r_by = cb.message.from_user + loc = file_path + appendable = [s_name, r_by, loc] + qeue.append(appendable) + await cb.message.delete() + await b.send_photo(chat_id, + photo="final.png", + caption=f"#⃣ Song requested by {r_by.mention} queued at position {position}!", + reply_markup=keyboard, + ) + os.remove("final.png") + + else: + que[chat_id] = [] + qeue = que.get(chat_id) + s_name = title + try: + r_by = cb.message.reply_to_message.from_user + except: + r_by = cb.message.from_user + loc = file_path + appendable = [s_name, r_by, loc] + qeue.append(appendable) + + await callsmusic.set_stream(chat_id, file_path) + await cb.message.delete() + await b.send_photo(chat_id, + photo="final.png", + reply_markup=keyboard, + caption=f"▶️ Playing here the song requested by {r_by.mention} via Youtube Music 😎", + ) + os.remove("final.png") diff --git a/VCPlayBot/modules/pmpermit.py b/VCPlayBot/modules/pmpermit.py new file mode 100644 index 00000000..0e45df9b --- /dev/null +++ b/VCPlayBot/modules/pmpermit.py @@ -0,0 +1,69 @@ + +from pyrogram import Client +import asyncio +from VCPlayBot.config import SUDO_USERS +from VCPlayBot.config import PMPERMIT +from pyrogram import filters +from pyrogram.types import Message +from VCPlayBot.services.callsmusic import client as USER + +PMSET =True +pchats = [] + +@USER.on_message(filters.text & filters.private & ~filters.me & ~filters.bot) +async def pmPermit(client: USER, message: Message): + if PMPERMIT == "ENABLE": + if PMSET: + chat_id = message.chat.id + if chat_id in pchats: + return + await USER.send_message( + message.chat.id, + "Hi there, This is a music assistant service .\n\n ❗️ Rules:\n - No chatting allowed\n - No spam allowed \n\n 👉 **SEND GROUP INVITE LINK OR USERNAME IF USERBOT CAN'T JOIN YOUR GROUP.**\n\n ⚠️ Disclamer: If you are sending a message here it means admin will see your message and join chat\n - Don't add this user to secret groups.\n - Don't Share private info here\n\n", + ) + return + + + +@Client.on_message(filters.command(["/pmpermit"])) +async def bye(client: Client, message: Message): + if message.from_user.id in SUDO_USERS: + global PMSET + text = message.text.split(" ", 1) + queryy = text[1] + if queryy == "on": + PMSET = True + await message.reply_text("Pmpermit turned on") + return + if queryy == "off": + PMSET = None + await message.reply_text("Pmpermit turned off") + return + +@USER.on_message(filters.text & filters.private & filters.me) +async def autopmPermiat(client: USER, message: Message): + chat_id = message.chat.id + if not chat_id in pchats: + pchats.append(chat_id) + await message.reply_text("Approoved to PM due to outgoing messages") + return + message.continue_propagation() + +@USER.on_message(filters.command("a", [".", ""]) & filters.me & filters.private) +async def pmPermiat(client: USER, message: Message): + chat_id = message.chat.id + if not chat_id in pchats: + pchats.append(chat_id) + await message.reply_text("Approoved to PM") + return + message.continue_propagation() + + +@USER.on_message(filters.command("da", [".", ""]) & filters.me & filters.private) +async def rmpmPermiat(client: USER, message: Message): + chat_id = message.chat.id + if chat_id in pchats: + pchats.remove(chat_id) + await message.reply_text("Dispprooved to PM") + return + message.continue_propagation() diff --git a/VCPlayBot/modules/private.py b/VCPlayBot/modules/private.py new file mode 100644 index 00000000..0c9e2bad --- /dev/null +++ b/VCPlayBot/modules/private.py @@ -0,0 +1,118 @@ + +import logging +from VCPlayBot.modules.msg import Messages as tr +from pyrogram import Client +from pyrogram import filters +from pyrogram.types import InlineKeyboardMarkup +from pyrogram.types import InlineKeyboardButton +from pyrogram.types import Message +from VCPlayBot.config import SOURCE_CODE +from VCPlayBot.config import ASSISTANT_NAME +from VCPlayBot.config import PROJECT_NAME +from VCPlayBot.config import SUPPORT_GROUP +from VCPlayBot.config import UPDATES_CHANNEL +from VCPlayBot.config import BOT_USERNAME +logging.basicConfig(level=logging.INFO) + +@Client.on_message(filters.private & filters.incoming & filters.command(['start'])) +def _start(client, message): + client.send_message(message.chat.id, + text=tr.START_MSG.format(message.from_user.first_name, message.from_user.id), + parse_mode="markdown", + reply_markup=InlineKeyboardMarkup( + [ + [ + InlineKeyboardButton( + "➕ Add me to your Group 🙋‍♀️", url=f"https://t.me/{BOT_USERNAME}?startgroup=true")], + [ + InlineKeyboardButton( + "📲 Updates", url=f"https://t.me/{UPDATES_CHANNEL}"), + InlineKeyboardButton( + "💬 Support", url=f"https://t.me/{SUPPORT_GROUP}") + ],[ + InlineKeyboardButton( + "🛠 Source Code 🛠", url=f"https://{SOURCE_CODE}") + ] + ] + ), + reply_to_message_id=message.message_id + ) + +@Client.on_message(filters.command("start") & ~filters.private & ~filters.channel) +async def gstart(_, message: Message): + await message.reply_text( + f"""**🔴 {PROJECT_NAME} is online**""", + reply_markup=InlineKeyboardMarkup( + [ + [ + InlineKeyboardButton( + "💬 Support Chat", url=f"https://t.me/{SUPPORT_GROUP}" + ) + ] + ] + ), + ) + + +@Client.on_message(filters.private & filters.incoming & filters.command(['help'])) +def _help(client, message): + client.send_message(chat_id = message.chat.id, + text = tr.HELP_MSG[1], + parse_mode="markdown", + disable_web_page_preview=True, + disable_notification=True, + reply_markup = InlineKeyboardMarkup(map(1)), + reply_to_message_id = message.message_id + ) + +help_callback_filter = filters.create(lambda _, __, query: query.data.startswith('help+')) + +@Client.on_callback_query(help_callback_filter) +def help_answer(client, callback_query): + chat_id = callback_query.from_user.id + disable_web_page_preview=True + message_id = callback_query.message.message_id + msg = int(callback_query.data.split('+')[1]) + client.edit_message_text(chat_id=chat_id, message_id=message_id, + text=tr.HELP_MSG[msg], reply_markup=InlineKeyboardMarkup(map(msg)) + ) + + +def map(pos): + if(pos==1): + button = [ + [InlineKeyboardButton(text = '▶️', callback_data = "help+2")] + ] + elif(pos==len(tr.HELP_MSG)-1): + url = f"https://t.me/{SUPPORT_GROUP}" + button = [ + [InlineKeyboardButton("➕ Add me to your Group 🙋‍♀️", url=f"https://t.me/{BOT_USERNAME}?startgroup=true")], + [InlineKeyboardButton(text = '📲 Updates', url=f"https://t.me/{UPDATES_CHANNEL}"), + InlineKeyboardButton(text = '💬 Support', url=f"https://t.me/{SUPPORT_GROUP}")], + [InlineKeyboardButton(text = '🛠 Source Code 🛠', url=f"https://{SOURCE_CODE}")], + [InlineKeyboardButton(text = '◀️', callback_data = f"help+{pos-1}")] + ] + else: + button = [ + [ + InlineKeyboardButton(text = '◀️', callback_data = f"help+{pos-1}"), + InlineKeyboardButton(text = '▶️', callback_data = f"help+{pos+1}") + ], + ] + return button + +@Client.on_message(filters.command("help") & ~filters.private & ~filters.channel) +async def ghelp(_, message: Message): + await message.reply_text( + f"""**🙋‍♀️ Hello there! I can play music in the voice chats of telegram groups & channels.**""", + reply_markup=InlineKeyboardMarkup( + [ + [ + InlineKeyboardButton( + "🟡 Click here for help 🟡", url=f"https://t.me/{BOT_USERNAME}?start" + ) + ] + ] + ), + ) + diff --git a/VCPlayBot/modules/song.py b/VCPlayBot/modules/song.py new file mode 100644 index 00000000..06b3b474 --- /dev/null +++ b/VCPlayBot/modules/song.py @@ -0,0 +1,371 @@ + + +from __future__ import unicode_literals + +import asyncio +import math +import os +import time +from random import randint +from urllib.parse import urlparse + +import aiofiles +import aiohttp +import requests +import wget +import youtube_dl +from pyrogram import Client, filters +from pyrogram.errors import FloodWait, MessageNotModified +from pyrogram.types import Message +from youtube_search import YoutubeSearch +from youtubesearchpython import SearchVideos + +from VCPlayBot.config import DURATION_LIMIT +from VCPlayBot.modules.play import arq + + +@Client.on_message(filters.command("song") & ~filters.channel) +def song(client, message): + + user_id = message.from_user.id + user_name = message.from_user.first_name + rpk = "[" + user_name + "](tg://user?id=" + str(user_id) + ")" + + query = "" + for i in message.command[1:]: + query += " " + str(i) + print(query) + m = message.reply("🔎 Finding the song...") + ydl_opts = {"format": "bestaudio/best"} + try: + results = YoutubeSearch(query, max_results=1).to_dict() + link = f"https://youtube.com{results[0]['url_suffix']}" + # print(results) + title = results[0]["title"][:40] + thumbnail = results[0]["thumbnails"][0] + thumb_name = f"thumb{title}.jpg" + thumb = requests.get(thumbnail, allow_redirects=True) + open(thumb_name, "wb").write(thumb.content) + + duration = results[0]["duration"] + results[0]["url_suffix"] + results[0]["views"] + + except Exception as e: + m.edit("❌ Found Nothing.\n\nTry another keywork or maybe spell it properly.") + print(str(e)) + return + m.edit("Downloading the song ") + try: + with youtube_dl.YoutubeDL(ydl_opts) as ydl: + info_dict = ydl.extract_info(link, download=False) + audio_file = ydl.prepare_filename(info_dict) + ydl.process_info(info_dict) + rep = "**🎵 Uploaded by VCPlayBot**" + secmul, dur, dur_arr = 1, 0, duration.split(":") + for i in range(len(dur_arr) - 1, -1, -1): + dur += int(dur_arr[i]) * secmul + secmul *= 60 + 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") + print(e) + + try: + os.remove(audio_file) + os.remove(thumb_name) + except Exception as e: + print(e) + + +def get_text(message: Message) -> [None, str]: + text_to_return = message.text + if message.text is None: + return None + if " " in text_to_return: + try: + return message.text.split(None, 1)[1] + except IndexError: + return None + else: + return None + + +def humanbytes(size): + 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" + + +async def progress(current, total, message, start, type_of_ps, file_name=None): + now = time.time() + diff = now - start + if round(diff % 10.00) == 0 or current == total: + percentage = current * 100 / total + speed = current / diff + elapsed_time = round(diff) * 1000 + if elapsed_time == 0: + return + time_to_completion = round((total - current) / speed) * 1000 + estimated_total_time = elapsed_time + time_to_completion + progress_str = "{0}{1} {2}%\n".format( + "".join(["🔴" for i in range(math.floor(percentage / 10))]), + "".join(["🔘" for i in range(10 - math.floor(percentage / 10))]), + round(percentage, 2), + ) + tmp = progress_str + "{0} of {1}\nETA: {2}".format( + humanbytes(current), humanbytes(total), time_formatter(estimated_total_time) + ) + if file_name: + try: + await message.edit( + "{}\n**File Name:** `{}`\n{}".format(type_of_ps, file_name, tmp) + ) + except FloodWait as e: + await asyncio.sleep(e.x) + except MessageNotModified: + pass + else: + try: + await message.edit("{}\n{}".format(type_of_ps, tmp)) + except FloodWait as e: + await asyncio.sleep(e.x) + except MessageNotModified: + pass + + +def get_user(message: Message, text: str) -> [int, str, None]: + if text is None: + asplit = None + else: + asplit = text.split(" ", 1) + user_s = None + reason_ = None + if message.reply_to_message: + user_s = message.reply_to_message.from_user.id + reason_ = text if text else None + elif asplit is None: + return None, None + elif len(asplit[0]) > 0: + user_s = int(asplit[0]) if asplit[0].isdigit() else asplit[0] + if len(asplit) == 2: + reason_ = asplit[1] + return user_s, reason_ + + +def get_readable_time(seconds: int) -> int: + count = 0 + ping_time = "" + time_list = [] + time_suffix_list = ["s", "m", "h", "days"] + + while count < 4: + count += 1 + if count < 3: + remainder, result = divmod(seconds, 60) + else: + remainder, result = divmod(seconds, 24) + if seconds == 0 and remainder == 0: + break + time_list.append(int(result)) + seconds = int(remainder) + + for x in range(len(time_list)): + time_list[x] = str(time_list[x]) + time_suffix_list[x] + if len(time_list) == 4: + ping_time += time_list.pop() + ", " + + time_list.reverse() + ping_time += ":".join(time_list) + + return ping_time + + +def time_formatter(milliseconds: int) -> str: + seconds, milliseconds = divmod(int(milliseconds), 1000) + minutes, seconds = divmod(seconds, 60) + hours, minutes = divmod(minutes, 60) + days, hours = divmod(hours, 24) + tmp = ( + ((str(days) + " day(s), ") if days else "") + + ((str(hours) + " hour(s), ") if hours else "") + + ((str(minutes) + " minute(s), ") if minutes else "") + + ((str(seconds) + " second(s), ") if seconds else "") + + ((str(milliseconds) + " millisecond(s), ") if milliseconds else "") + ) + return tmp[:-2] + + +ydl_opts = { + "format": "bestaudio/best", + "writethumbnail": True, + "postprocessors": [ + { + "key": "FFmpegExtractAudio", + "preferredcodec": "mp3", + "preferredquality": "192", + } + ], +} + + +def get_file_extension_from_url(url): + url_path = urlparse(url).path + basename = os.path.basename(url_path) + return basename.split(".")[-1] + + +# Funtion To Download Song +async def download_song(url): + song_name = f"{randint(6969, 6999)}.mp3" + async with aiohttp.ClientSession() as session: + async with session.get(url) as resp: + if resp.status == 200: + f = await aiofiles.open(song_name, mode="wb") + await f.write(await resp.read()) + await f.close() + return song_name + + +is_downloading = False + + +def time_to_seconds(time): + stringt = str(time) + return sum(int(x) * 60 ** i for i, x in enumerate(reversed(stringt.split(":")))) + + +@Client.on_message(filters.command("saavn") & ~filters.edited) +async def jssong(_, message): + global is_downloading + if len(message.command) < 2: + await message.reply_text("/saavn requires an argument.") + return + if is_downloading: + await message.reply_text( + "Another download is in progress, try again after sometime." + ) + return + is_downloading = True + text = message.text.split(None, 1)[1] + query = text.replace(" ", "%20") + m = await message.reply_text("Searching...") + try: + songs = await arq.saavn(query) + if not songs.ok: + await message.reply_text(songs.result) + return + sname = songs.result[0].song + slink = songs.result[0].media_url + ssingers = songs.result[0].singers + await m.edit("Downloading") + song = await download_song(slink) + await m.edit("Uploading") + await message.reply_audio(audio=song, title=sname, performer=ssingers) + os.remove(song) + await m.delete() + except Exception as e: + is_downloading = False + await m.edit(str(e)) + return + is_downloading = False + + +@Client.on_message(filters.command(["vsong", "video"])) +async def ytmusic(client, message: Message): + global is_downloading + if is_downloading: + await message.reply_text( + "Another download is in progress, try again after sometime." + ) + return + + urlissed = get_text(message) + + pablo = await client.send_message( + message.chat.id, f"`Getting {urlissed} From Youtube Servers. Please Wait.`" + ) + if not urlissed: + await pablo.edit("Invalid Command Syntax, Please Check Help Menu To Know More!") + return + + search = SearchVideos(f"{urlissed}", offset=1, mode="dict", max_results=1) + mi = search.result() + mio = mi["search_result"] + mo = mio[0]["link"] + thum = mio[0]["title"] + fridayz = mio[0]["id"] + thums = mio[0]["channel"] + kekme = f"https://img.youtube.com/vi/{fridayz}/hqdefault.jpg" + await asyncio.sleep(0.6) + url = mo + sedlyf = wget.download(kekme) + opts = { + "format": "best", + "addmetadata": True, + "key": "FFmpegMetadata", + "prefer_ffmpeg": True, + "geo_bypass": True, + "nocheckcertificate": True, + "postprocessors": [{"key": "FFmpegVideoConvertor", "preferedformat": "mp4"}], + "outtmpl": "%(id)s.mp4", + "logtostderr": False, + "quiet": True, + } + try: + is_downloading = True + with youtube_dl.YoutubeDL(opts) as ytdl: + infoo = ytdl.extract_info(url, False) + duration = round(infoo["duration"] / 60) + + if duration > DURATION_LIMIT: + await pablo.edit( + f"❌ Videos longer than {DURATION_LIMIT} minute(s) aren't allowed, the provided video is {duration} minute(s)" + ) + is_downloading = False + return + ytdl_data = ytdl.extract_info(url, download=True) + + except Exception: + # await pablo.edit(event, f"**Failed To Download** \n**Error :** `{str(e)}`") + is_downloading = False + return + + c_time = time.time() + file_stark = f"{ytdl_data['id']}.mp4" + capy = f"**Video Name ➠** `{thum}` \n**Requested For :** `{urlissed}` \n**Channel :** `{thums}` \n**Link :** `{mo}`" + await client.send_video( + message.chat.id, + video=open(file_stark, "rb"), + duration=int(ytdl_data["duration"]), + file_name=str(ytdl_data["title"]), + thumb=sedlyf, + caption=capy, + supports_streaming=True, + progress=progress, + progress_args=( + pablo, + c_time, + f"`Uploading {urlissed} Song From YouTube Music!`", + file_stark, + ), + ) + await pablo.delete() + is_downloading = False + for files in (sedlyf, file_stark): + if files and os.path.exists(files): + os.remove(files) diff --git a/VCPlayBot/modules/userbotjoin.py b/VCPlayBot/modules/userbotjoin.py new file mode 100644 index 00000000..7349eae8 --- /dev/null +++ b/VCPlayBot/modules/userbotjoin.py @@ -0,0 +1,121 @@ + +from pyrogram import Client +from pyrogram import filters +from pyrogram.errors import UserAlreadyParticipant +import asyncio +from VCPlayBot.helpers.decorators import authorized_users_only +from VCPlayBot.helpers.decorators import errors +from VCPlayBot.services.callsmusic import client as USER +from VCPlayBot.config import SUDO_USERS + +@Client.on_message(filters.command(["userbotjoin"]) & ~filters.private & ~filters.bot) +@authorized_users_only +@errors +async def addchannel(client, message): + chid = message.chat.id + try: + invitelink = await client.export_chat_invite_link(chid) + except: + await message.reply_text( + "Add me as admin of yor group first", + ) + return + + try: + user = await USER.get_me() + except: + user.first_name = "VCPlayBot" + + try: + await USER.join_chat(invitelink) + await USER.send_message(message.chat.id, "I joined here as you requested") + except UserAlreadyParticipant: + await message.reply_text( + "helper already in your chat", + ) + except Exception as e: + print(e) + await message.reply_text( + f"🛑 Flood Wait Error 🛑 \n User {user.first_name} couldn't join your group due to heavy join requests for userbot! Make sure user is not banned in group." + "\n\nOr manually add @VCPlayAssistant to your Group and try again", + ) + return + await message.reply_text( + "helper userbot joined your chat", + ) + + +@USER.on_message(filters.group & filters.command(["userbotleave"])) +@authorized_users_only +async def rem(USER, message): + try: + await USER.leave_chat(message.chat.id) + except: + await message.reply_text( + f"User couldn't leave your group! May be floodwaits." + "\n\nOr manually kick me from to your Group", + ) + return + +@Client.on_message(filters.command(["userbotleaveall"])) +async def bye(client, message): + if message.from_user.id in SUDO_USERS: + left=0 + failed=0 + lol = await message.reply("Assistant Leaving all chats") + async for dialog in USER.iter_dialogs(): + try: + await USER.leave_chat(dialog.chat.id) + left = left+1 + await lol.edit(f"Assistant leaving... Left: {left} chats. Failed: {failed} chats.") + except: + failed=failed+1 + await lol.edit(f"Assistant leaving... Left: {left} chats. Failed: {failed} chats.") + await asyncio.sleep(0.7) + await client.send_message(message.chat.id, f"Left {left} chats. Failed {failed} chats.") + + +@Client.on_message(filters.command(["userbotjoinchannel","ubjoinc"]) & ~filters.private & ~filters.bot) +@authorized_users_only +@errors +async def addcchannel(client, message): + try: + conchat = await client.get_chat(message.chat.id) + conid = conchat.linked_chat.id + chid = conid + except: + await message.reply("Is chat even linked") + return + chat_id = chid + try: + invitelink = await client.export_chat_invite_link(chid) + except: + await message.reply_text( + "Add me as admin of yor channel first", + ) + return + + try: + user = await USER.get_me() + except: + user.first_name = "VCPlayBot" + + try: + await USER.join_chat(invitelink) + await USER.send_message(message.chat.id, "I joined here as you requested") + except UserAlreadyParticipant: + await message.reply_text( + "helper already in your channel", + ) + return + except Exception as e: + print(e) + await message.reply_text( + f"🛑 Flood Wait Error 🛑 \n User {user.first_name} couldn't join your channel due to heavy join requests for userbot! Make sure user is not banned in channel." + "\n\nOr manually add @VCPlayAssistant to your Group and try again", + ) + return + await message.reply_text( + "helper userbot joined your channel", + ) + diff --git a/VCPlayBot/modules/voice_chat_ended.py b/VCPlayBot/modules/voice_chat_ended.py new file mode 100644 index 00000000..a2662dbf --- /dev/null +++ b/VCPlayBot/modules/voice_chat_ended.py @@ -0,0 +1,14 @@ +from pyrogram import Client +from pyrogram import filters +from pyrogram.types import Message + +from VCPlayBot.services.callsmusic.callsmusic import remove +from VCPlayBot.helpers.channelmusic import get_chat_id + + +@Client.on_message(filters.voice_chat_ended) +async def voice_chat_ended(_, message: Message): + try: + remove(get_chat_id(message.chat)) + except Exception: + pass diff --git a/VCPlayBot/modules/ytsearch.py b/VCPlayBot/modules/ytsearch.py new file mode 100644 index 00000000..b49ace8e --- /dev/null +++ b/VCPlayBot/modules/ytsearch.py @@ -0,0 +1,40 @@ + + +# the logging things +import logging + +from pyrogram import Client as app +from pyrogram.types import Message +from youtube_search import YoutubeSearch + +logging.basicConfig( + level=logging.DEBUG, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s" +) +logger = logging.getLogger(__name__) + +import pyrogram + +logging.getLogger("pyrogram").setLevel(logging.WARNING) + + +@app.on_message(pyrogram.filters.command(["search"])) +async def ytsearch(_, message: Message): + try: + if len(message.command) < 2: + await message.reply_text("/search needs an argument!") + return + query = message.text.split(None, 1)[1] + m = await message.reply_text("Searching....") + results = YoutubeSearch(query, max_results=4).to_dict() + i = 0 + text = "" + while i < 4: + text += f"Title - {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://youtube.com{results[i]['url_suffix']}\n\n" + i += 1 + await m.edit(text, disable_web_page_preview=True) + except Exception as e: + await message.reply_text(str(e)) diff --git a/VCPlayBot/services/__init__.py b/VCPlayBot/services/__init__.py new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/VCPlayBot/services/__init__.py @@ -0,0 +1 @@ + diff --git a/VCPlayBot/services/callsmusic/__init__.py b/VCPlayBot/services/callsmusic/__init__.py new file mode 100644 index 00000000..4996c7b1 --- /dev/null +++ b/VCPlayBot/services/callsmusic/__init__.py @@ -0,0 +1,6 @@ +from pyrogram import Client + +from VCPlayBot import config + +client = Client(config.SESSION_NAME, config.API_ID, config.API_HASH) +run = client.run diff --git a/VCPlayBot/services/callsmusic/callsmusic.py b/VCPlayBot/services/callsmusic/callsmusic.py new file mode 100644 index 00000000..1ac180dc --- /dev/null +++ b/VCPlayBot/services/callsmusic/callsmusic.py @@ -0,0 +1,104 @@ +from typing import Dict + +from pytgcalls import GroupCallFactory + +from VCPlayBot.services.callsmusic import client +from VCPlayBot.services.queues import queues + + +instances: Dict[int, GroupCallFactory] = {} +active_chats: Dict[int, Dict[str, bool]] = {} + + +def init_instance(chat_id: int): + if chat_id not in instances: + instances[chat_id] = GroupCallFactory(client,outgoing_audio_bitrate_kbit=512).get_file_group_call() + + instance = instances[chat_id] + + @instance.on_playout_ended + async def ___(__, _): + queues.task_done(chat_id) + + if queues.is_empty(chat_id): + await stop(chat_id) + else: + instance.input_filename = queues.get(chat_id)["file_path"] + + +def remove(chat_id: int): + if chat_id in instances: + del instances[chat_id] + + if not queues.is_empty(chat_id): + queues.clear(chat_id) + + if chat_id in active_chats: + del active_chats[chat_id] + + +def get_instance(chat_id: int) -> GroupCallFactory: + init_instance(chat_id) + return instances[chat_id] + + +async def start(chat_id: int): + await get_instance(chat_id).start(chat_id) + active_chats[chat_id] = {"playing": True, "muted": False} + + +async def stop(chat_id: int): + await get_instance(chat_id).stop() + + if chat_id in active_chats: + del active_chats[chat_id] + + +async def set_stream(chat_id: int, file: str): + if chat_id not in active_chats: + await start(chat_id) + get_instance(chat_id).input_filename = file + + +def pause(chat_id: int) -> bool: + if chat_id not in active_chats: + return False + elif not active_chats[chat_id]["playing"]: + return False + + get_instance(chat_id).pause_playout() + active_chats[chat_id]["playing"] = False + return True + + +def resume(chat_id: int) -> bool: + if chat_id not in active_chats: + return False + elif active_chats[chat_id]["playing"]: + return False + + get_instance(chat_id).resume_playout() + active_chats[chat_id]["playing"] = True + return True + + +async def mute(chat_id: int) -> int: + if chat_id not in active_chats: + return 2 + elif active_chats[chat_id]["muted"]: + return 1 + + await get_instance(chat_id).set_is_mute(True) + active_chats[chat_id]["muted"] = True + return 0 + + +async def unmute(chat_id: int) -> int: + if chat_id not in active_chats: + return 2 + elif not active_chats[chat_id]["muted"]: + return 1 + + await get_instance(chat_id).set_is_mute(False) + active_chats[chat_id]["muted"] = False + return 0 diff --git a/VCPlayBot/services/converter/__init__.py b/VCPlayBot/services/converter/__init__.py new file mode 100644 index 00000000..8080c1b9 --- /dev/null +++ b/VCPlayBot/services/converter/__init__.py @@ -0,0 +1,9 @@ +from os import listdir +from os import mkdir + +if 'raw_files' not in listdir(): + mkdir('raw_files') + +from VCPlayBot.services.converter.converter import convert + +__all__ = ["convert"] diff --git a/VCPlayBot/services/converter/converter.py b/VCPlayBot/services/converter/converter.py new file mode 100644 index 00000000..f8ab8a02 --- /dev/null +++ b/VCPlayBot/services/converter/converter.py @@ -0,0 +1,28 @@ +import asyncio +from os import path + +from VCPlayBot.helpers.errors import FFmpegReturnCodeError + + +async def convert(file_path: str) -> str: + out = path.join('raw_files', path.basename(file_path + '.raw')) + if path.isfile(out): + return out + proc = await asyncio.create_subprocess_shell( + cmd=( + 'ffmpeg ' + '-y -i ' + f'{file_path} ' + '-f s16le ' + '-ac 2 ' + '-ar 48000 ' + '-acodec pcm_s16le ' + f'{out}' + ), + stdin=asyncio.subprocess.PIPE, + stderr=asyncio.subprocess.PIPE, + ) + await proc.communicate() + if proc.returncode != 0: + raise FFmpegReturnCodeError('FFmpeg did not return 0') + return out diff --git a/VCPlayBot/services/downloaders/__init__.py b/VCPlayBot/services/downloaders/__init__.py new file mode 100644 index 00000000..b67b2ad3 --- /dev/null +++ b/VCPlayBot/services/downloaders/__init__.py @@ -0,0 +1,3 @@ +from VCPlayBot.services.downloaders import youtube + +__all__ = ["youtube"] diff --git a/VCPlayBot/services/downloaders/youtube.py b/VCPlayBot/services/downloaders/youtube.py new file mode 100644 index 00000000..695e0867 --- /dev/null +++ b/VCPlayBot/services/downloaders/youtube.py @@ -0,0 +1,36 @@ + +from os import path + +from youtube_dl import YoutubeDL + +from VCPlayBot.config import DURATION_LIMIT +from VCPlayBot.helpers.errors import DurationLimitError + +ydl_opts = { + "format": "bestaudio/best", + "verbose": True, + "geo-bypass": True, + "nocheckcertificate": True, + "outtmpl": "downloads/%(id)s.%(ext)s", +} + +ydl = YoutubeDL(ydl_opts) + + +def download(url: str) -> str: + info = ydl.extract_info(url, False) + duration = round(info["duration"] / 60) + + if duration > DURATION_LIMIT: + raise DurationLimitError( + f"🛑 Videos longer than {DURATION_LIMIT} minute(s) aren't allowed, " + f"the provided video is {duration} minute(s)", + ) + try: + ydl.download([url]) + except: + raise DurationLimitError( + f"🛑 Videos longer than {DURATION_LIMIT} minute(s) aren't allowed, " + f"the provided video is {duration} minute(s)", + ) + return path.join("downloads", f"{info['id']}.{info['ext']}") diff --git a/VCPlayBot/services/queues/__init__.py b/VCPlayBot/services/queues/__init__.py new file mode 100644 index 00000000..96f9c536 --- /dev/null +++ b/VCPlayBot/services/queues/__init__.py @@ -0,0 +1,7 @@ +from VCPlayBot.services.queues.queues import clear +from VCPlayBot.services.queues.queues import get +from VCPlayBot.services.queues.queues import is_empty +from VCPlayBot.services.queues.queues import put +from VCPlayBot.services.queues.queues import task_done + +__all__ = ["clear", "get", "is_empty", "put", "task_done"] diff --git a/VCPlayBot/services/queues/queues.py b/VCPlayBot/services/queues/queues.py new file mode 100644 index 00000000..67b363bb --- /dev/null +++ b/VCPlayBot/services/queues/queues.py @@ -0,0 +1,52 @@ +from asyncio import Queue as _Queue +from asyncio import QueueEmpty as Empty +from typing import Dict + + +class Queue(_Queue): + _queue: list = [] + + def clear(self): + self._queue.clear() + + +queues: Dict[int, Queue] = {} + + +async def put(chat_id: int, **kwargs) -> int: + if chat_id not in queues: + queues[chat_id] = Queue() + await queues[chat_id].put({**kwargs}) + return queues[chat_id].qsize() + + +def get(chat_id: int) -> Dict[str, str]: + if chat_id in queues: + try: + return queues[chat_id].get_nowait() + except Empty: + return {} + return {} + + +def is_empty(chat_id: int) -> bool: + if chat_id in queues: + return queues[chat_id].empty() + return True + + +def task_done(chat_id: int): + if chat_id in queues: + try: + queues[chat_id].task_done() + except ValueError: + pass + + +def clear(chat_id: int): + if chat_id in queues: + if queues[chat_id].empty(): + raise Empty + else: + queues[chat_id].clear() + raise Empty diff --git a/etc/font.otf b/etc/font.otf new file mode 100644 index 00000000..0441e477 Binary files /dev/null and b/etc/font.otf differ diff --git a/etc/tg_vc_bot.png b/etc/tg_vc_bot.png new file mode 100644 index 00000000..5a6f5806 Binary files /dev/null and b/etc/tg_vc_bot.png differ diff --git a/example.env b/example.env new file mode 100644 index 00000000..f464eb4d --- /dev/null +++ b/example.env @@ -0,0 +1,11 @@ +SESSION_NAME=session # If you don't deploy with docker, keep it as is and if you do so it should be a session string generated by "python str.py" +BOT_TOKEN=123456:abcdefghijklmnopqrstuv +BOT_NAME=HELLBOT +API_ID=123456 +API_HASH=abcdefghijklmnopqrstuv +SUDO_USERS=1111 2222 # List of user IDs separated by space +DURATION_LIMIT=10 # in minutes (default: 7) +ARQ_API_KEY= +UPDATES_CHANNEL= +BG_IMAGE= + diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 00000000..39d32a4b --- /dev/null +++ b/requirements.txt @@ -0,0 +1,20 @@ +pyrogram +tgcrypto +pytgcalls[pyrogram] +python-dotenv +youtube_dl +youtube_search_python +requests +aiohttp +aiofiles +asyncio +youtube_search +search_engine_parser +ffmpeg-python +pillow +python-arq +future +psutil +wget +tswift +regex \ No newline at end of file diff --git a/runtime.txt b/runtime.txt new file mode 100644 index 00000000..9bff0e00 --- /dev/null +++ b/runtime.txt @@ -0,0 +1 @@ +python-3.9.6