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