diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 000000000000..fe58f981e1df --- /dev/null +++ b/.dockerignore @@ -0,0 +1,10 @@ +.env +.cache +log.txt +.DS_Store +*.session +raw_files/ +cache/ +downloads/ +__pycache__/ +*.session-journal diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 000000000000..3f60c9ef335d --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,12 @@ + +# To get started with Dependabot version updates, you'll need to specify which +# package ecosystems to update and where the package manifests are located. +# Please see the documentation for all configuration options: +# https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file + +version: 2 +updates: + - package-ecosystem: "" # See documentation for possible values + directory: "/" # Location of package manifests + schedule: + interval: "weekly" diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 000000000000..5276ede21934 --- /dev/null +++ b/.github/workflows/codeql.yml @@ -0,0 +1,92 @@ +# For most projects, this workflow file will not need changing; you simply need +# to commit it to your repository. +# +# You may wish to alter this file to override the set of languages analyzed, +# or to provide custom queries or build logic. +# +# ******** NOTE ******** +# We have attempted to detect the languages in your repository. Please check +# the `language` matrix defined below to confirm you have the correct set of +# supported CodeQL languages. +# +name: "CodeQL Advanced" + +on: + push: + branches: [ "master" ] + pull_request: + branches: [ "master" ] + schedule: + - cron: '26 19 * * 1' + +jobs: + analyze: + name: Analyze (${{ matrix.language }}) + # Runner size impacts CodeQL analysis time. To learn more, please see: + # - https://gh.io/recommended-hardware-resources-for-running-codeql + # - https://gh.io/supported-runners-and-hardware-resources + # - https://gh.io/using-larger-runners (GitHub.com only) + # Consider using larger runners or machines with greater resources for possible analysis time improvements. + runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }} + permissions: + # required for all workflows + security-events: write + + # required to fetch internal or private CodeQL packs + packages: read + + # only required for workflows in private repositories + actions: read + contents: read + + strategy: + fail-fast: false + matrix: + include: + - language: python + build-mode: none + # CodeQL supports the following values keywords for 'language': 'c-cpp', 'csharp', 'go', 'java-kotlin', 'javascript-typescript', 'python', 'ruby', 'swift' + # Use `c-cpp` to analyze code written in C, C++ or both + # Use 'java-kotlin' to analyze code written in Java, Kotlin or both + # Use 'javascript-typescript' to analyze code written in JavaScript, TypeScript or both + # To learn more about changing the languages that are analyzed or customizing the build mode for your analysis, + # see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/customizing-your-advanced-setup-for-code-scanning. + # If you are analyzing a compiled language, you can modify the 'build-mode' for that language to customize how + # your codebase is analyzed, see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v3 + with: + languages: ${{ matrix.language }} + build-mode: ${{ matrix.build-mode }} + # If you wish to specify custom queries, you can do so here or in a config file. + # By default, queries listed here will override any specified in a config file. + # Prefix the list here with "+" to use these queries and those in the config file. + + # For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs + # queries: security-extended,security-and-quality + + # If the analyze step fails for one of the languages you are analyzing with + # "We were unable to automatically build your code", modify the matrix above + # to set the build mode to "manual" for that language. Then modify this step + # to build your code. + # ℹ️ Command-line programs to run using the OS shell. + # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun + - if: matrix.build-mode == 'manual' + shell: bash + run: | + echo 'If you are using a "manual" build mode for one or more of the' \ + 'languages you are analyzing, replace this with the commands to build' \ + 'your code, for example:' + echo ' make bootstrap' + echo ' make release' + exit 1 + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v3 + with: + category: "/language:${{matrix.language}}" diff --git a/.github/workflows/python-app.yml b/.github/workflows/python-app.yml new file mode 100644 index 000000000000..1168bd9ad833 --- /dev/null +++ b/.github/workflows/python-app.yml @@ -0,0 +1,39 @@ +# This workflow will install Python dependencies, run tests and lint with a single version of Python +# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python + +name: Python application + +on: + push: + branches: [ "main" ] + pull_request: + branches: [ "main" ] + +permissions: + contents: read + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + - name: Set up Python 3.10 + uses: actions/setup-python@v3 + with: + python-version: "3.10" + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install flake8 pytest + if [ -f requirements.txt ]; then pip install -r requirements.txt; fi + - name: Lint with flake8 + run: | + # stop the build if there are Python syntax errors or undefined names + flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics + # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide + flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics + - name: Test with pytest + run: | + pytest diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000000..fe58f981e1df --- /dev/null +++ b/.gitignore @@ -0,0 +1,10 @@ +.env +.cache +log.txt +.DS_Store +*.session +raw_files/ +cache/ +downloads/ +__pycache__/ +*.session-journal diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 000000000000..1d05a460e13b --- /dev/null +++ b/Dockerfile @@ -0,0 +1,12 @@ +FROM nikolaik/python-nodejs:python3.10-nodejs19 + +RUN apt-get update \ + && apt-get install -y --no-install-recommends ffmpeg \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* + +COPY . /app/ +WORKDIR /app/ +RUN pip3 install --no-cache-dir -U -r requirements.txt + +CMD bash start diff --git a/Procfile b/Procfile new file mode 100644 index 000000000000..36ad736e105f --- /dev/null +++ b/Procfile @@ -0,0 +1 @@ +worker: bash start diff --git a/README.md b/README.md new file mode 100644 index 000000000000..e66d224fa599 --- /dev/null +++ b/README.md @@ -0,0 +1,59 @@ + +
+
+
{self.id}
\n● ɴᴀᴍᴇ ➥ {self.name}\n● ᴜsᴇʀɴᴀᴍᴇ ➥ @{self.username}",
+ )
+ except (errors.ChannelInvalid, errors.PeerIdInvalid):
+ LOGGER(__name__).error(
+ "❖ Bot has failed to access the log group/channel. Make sure that you have added your bot to your log group/channel."
+ )
+ exit()
+ except Exception as ex:
+ LOGGER(__name__).error(
+ f"❖ Bot has failed to access the log group/channel.\n● Reason ➥ {type(ex).__name__}."
+ )
+ exit()
+
+ a = await self.get_chat_member(config.LOGGER_ID, self.id)
+ if a.status != ChatMemberStatus.ADMINISTRATOR:
+ LOGGER(__name__).error(
+ "❖ Please promote your bot as an admin in your log group/channel."
+ )
+ exit()
+ LOGGER(__name__).info(f"❖ Music Bot Started as ➥ {self.name} ...♥︎")
+
+ async def stop(self):
+ await super().stop()
diff --git a/SprotifyMusic/core/call.py b/SprotifyMusic/core/call.py
new file mode 100644
index 000000000000..abe92da8fc79
--- /dev/null
+++ b/SprotifyMusic/core/call.py
@@ -0,0 +1,601 @@
+import asyncio
+import os
+from datetime import datetime, timedelta
+from typing import Union
+
+from pyrogram import Client
+from pyrogram.types import InlineKeyboardMarkup
+from pytgcalls import PyTgCalls, StreamType
+from pytgcalls.exceptions import (
+ AlreadyJoinedError,
+ NoActiveGroupCall,
+ TelegramServerError,
+)
+from pytgcalls.types import Update
+from pytgcalls.types.input_stream import AudioPiped, AudioVideoPiped
+from pytgcalls.types.input_stream.quality import HighQualityAudio, MediumQualityVideo
+from pytgcalls.types.stream import StreamAudioEnded
+
+import config
+from SprotifyMusic import LOGGER, YouTube, app
+from SprotifyMusic.misc import db
+from SprotifyMusic.utils.database import (
+ add_active_chat,
+ add_active_video_chat,
+ get_lang,
+ get_loop,
+ group_assistant,
+ is_autoend,
+ music_on,
+ remove_active_chat,
+ remove_active_video_chat,
+ set_loop,
+)
+from SprotifyMusic.utils.exceptions import AssistantErr
+from SprotifyMusic.utils.formatters import check_duration, seconds_to_min, speed_converter
+from SprotifyMusic.utils.inline.play import stream_markup
+from SprotifyMusic.utils.stream.autoclear import auto_clean
+from SprotifyMusic.utils.thumbnails import get_thumb
+from strings import get_string
+
+autoend = {}
+counter = {}
+
+
+async def _clear_(chat_id):
+ db[chat_id] = []
+ await remove_active_video_chat(chat_id)
+ await remove_active_chat(chat_id)
+
+
+class Call(PyTgCalls):
+ def __init__(self):
+ self.userbot1 = Client(
+ name="SprotifyXAss1",
+ api_id=config.API_ID,
+ api_hash=config.API_HASH,
+ session_string=str(config.STRING1),
+ )
+ self.one = PyTgCalls(
+ self.userbot1,
+ cache_duration=100,
+ )
+ self.userbot2 = Client(
+ name="SprotifyXAss2",
+ api_id=config.API_ID,
+ api_hash=config.API_HASH,
+ session_string=str(config.STRING2),
+ )
+ self.two = PyTgCalls(
+ self.userbot2,
+ cache_duration=100,
+ )
+ self.userbot3 = Client(
+ name="SprotifyXAss3",
+ api_id=config.API_ID,
+ api_hash=config.API_HASH,
+ session_string=str(config.STRING3),
+ )
+ self.three = PyTgCalls(
+ self.userbot3,
+ cache_duration=100,
+ )
+ self.userbot4 = Client(
+ name="SprotifyXAss4",
+ api_id=config.API_ID,
+ api_hash=config.API_HASH,
+ session_string=str(config.STRING4),
+ )
+ self.four = PyTgCalls(
+ self.userbot4,
+ cache_duration=100,
+ )
+ self.userbot5 = Client(
+ name="SprotifyXAss5",
+ api_id=config.API_ID,
+ api_hash=config.API_HASH,
+ session_string=str(config.STRING5),
+ )
+ self.five = PyTgCalls(
+ self.userbot5,
+ cache_duration=100,
+ )
+
+ async def pause_stream(self, chat_id: int):
+ assistant = await group_assistant(self, chat_id)
+ await assistant.pause_stream(chat_id)
+
+ async def resume_stream(self, chat_id: int):
+ assistant = await group_assistant(self, chat_id)
+ await assistant.resume_stream(chat_id)
+
+ async def stop_stream(self, chat_id: int):
+ assistant = await group_assistant(self, chat_id)
+ try:
+ await _clear_(chat_id)
+ await assistant.leave_group_call(chat_id)
+ except:
+ pass
+
+ async def stop_stream_force(self, chat_id: int):
+ try:
+ if config.STRING1:
+ await self.one.leave_group_call(chat_id)
+ except:
+ pass
+ try:
+ if config.STRING2:
+ await self.two.leave_group_call(chat_id)
+ except:
+ pass
+ try:
+ if config.STRING3:
+ await self.three.leave_group_call(chat_id)
+ except:
+ pass
+ try:
+ if config.STRING4:
+ await self.four.leave_group_call(chat_id)
+ except:
+ pass
+ try:
+ if config.STRING5:
+ await self.five.leave_group_call(chat_id)
+ except:
+ pass
+ try:
+ await _clear_(chat_id)
+ except:
+ pass
+
+ async def speedup_stream(self, chat_id: int, file_path, speed, playing):
+ assistant = await group_assistant(self, chat_id)
+ if str(speed) != str("1.0"):
+ base = os.path.basename(file_path)
+ chatdir = os.path.join(os.getcwd(), "playback", str(speed))
+ if not os.path.isdir(chatdir):
+ os.makedirs(chatdir)
+ out = os.path.join(chatdir, base)
+ if not os.path.isfile(out):
+ if str(speed) == str("0.5"):
+ vs = 2.0
+ if str(speed) == str("0.75"):
+ vs = 1.35
+ if str(speed) == str("1.5"):
+ vs = 0.68
+ if str(speed) == str("2.0"):
+ vs = 0.5
+ proc = await asyncio.create_subprocess_shell(
+ cmd=(
+ "ffmpeg "
+ "-i "
+ f"{file_path} "
+ "-filter:v "
+ f"setpts={vs}*PTS "
+ "-filter:a "
+ f"atempo={speed} "
+ f"{out}"
+ ),
+ stdin=asyncio.subprocess.PIPE,
+ stderr=asyncio.subprocess.PIPE,
+ )
+ await proc.communicate()
+ else:
+ pass
+ else:
+ out = file_path
+ dur = await asyncio.get_event_loop().run_in_executor(None, check_duration, out)
+ dur = int(dur)
+ played, con_seconds = speed_converter(playing[0]["played"], speed)
+ duration = seconds_to_min(dur)
+ stream = (
+ AudioVideoPiped(
+ out,
+ audio_parameters=HighQualityAudio(),
+ video_parameters=MediumQualityVideo(),
+ additional_ffmpeg_parameters=f"-ss {played} -to {duration}",
+ )
+ if playing[0]["streamtype"] == "video"
+ else AudioPiped(
+ out,
+ audio_parameters=HighQualityAudio(),
+ additional_ffmpeg_parameters=f"-ss {played} -to {duration}",
+ )
+ )
+ if str(db[chat_id][0]["file"]) == str(file_path):
+ await assistant.change_stream(chat_id, stream)
+ else:
+ raise AssistantErr("Umm")
+ if str(db[chat_id][0]["file"]) == str(file_path):
+ exis = (playing[0]).get("old_dur")
+ if not exis:
+ db[chat_id][0]["old_dur"] = db[chat_id][0]["dur"]
+ db[chat_id][0]["old_second"] = db[chat_id][0]["seconds"]
+ db[chat_id][0]["played"] = con_seconds
+ db[chat_id][0]["dur"] = duration
+ db[chat_id][0]["seconds"] = dur
+ db[chat_id][0]["speed_path"] = out
+ db[chat_id][0]["speed"] = speed
+
+ async def force_stop_stream(self, chat_id: int):
+ assistant = await group_assistant(self, chat_id)
+ try:
+ check = db.get(chat_id)
+ check.pop(0)
+ except:
+ pass
+ await remove_active_video_chat(chat_id)
+ await remove_active_chat(chat_id)
+ try:
+ await assistant.leave_group_call(chat_id)
+ except:
+ pass
+
+ async def skip_stream(
+ self,
+ chat_id: int,
+ link: str,
+ video: Union[bool, str] = None,
+ image: Union[bool, str] = None,
+ ):
+ assistant = await group_assistant(self, chat_id)
+ if video:
+ stream = AudioVideoPiped(
+ link,
+ audio_parameters=HighQualityAudio(),
+ video_parameters=MediumQualityVideo(),
+ )
+ else:
+ stream = AudioPiped(link, audio_parameters=HighQualityAudio())
+ await assistant.change_stream(
+ chat_id,
+ stream,
+ )
+
+ async def seek_stream(self, chat_id, file_path, to_seek, duration, mode):
+ assistant = await group_assistant(self, chat_id)
+ stream = (
+ AudioVideoPiped(
+ file_path,
+ audio_parameters=HighQualityAudio(),
+ video_parameters=MediumQualityVideo(),
+ additional_ffmpeg_parameters=f"-ss {to_seek} -to {duration}",
+ )
+ if mode == "video"
+ else AudioPiped(
+ file_path,
+ audio_parameters=HighQualityAudio(),
+ additional_ffmpeg_parameters=f"-ss {to_seek} -to {duration}",
+ )
+ )
+ await assistant.change_stream(chat_id, stream)
+
+ async def stream_call(self, link):
+ assistant = await group_assistant(self, config.LOGGER_ID)
+ await assistant.join_group_call(
+ config.LOGGER_ID,
+ AudioVideoPiped(link),
+ stream_type=StreamType().pulse_stream,
+ )
+ await asyncio.sleep(0.2)
+ await assistant.leave_group_call(config.LOGGER_ID)
+
+ async def join_call(
+ self,
+ chat_id: int,
+ original_chat_id: int,
+ link,
+ video: Union[bool, str] = None,
+ image: Union[bool, str] = None,
+ ):
+ assistant = await group_assistant(self, chat_id)
+ language = await get_lang(chat_id)
+ _ = get_string(language)
+ if video:
+ stream = AudioVideoPiped(
+ link,
+ audio_parameters=HighQualityAudio(),
+ video_parameters=MediumQualityVideo(),
+ )
+ else:
+ stream = (
+ AudioVideoPiped(
+ link,
+ audio_parameters=HighQualityAudio(),
+ video_parameters=MediumQualityVideo(),
+ )
+ if video
+ else AudioPiped(link, audio_parameters=HighQualityAudio())
+ )
+ try:
+ await assistant.join_group_call(
+ chat_id,
+ stream,
+ stream_type=StreamType().pulse_stream,
+ )
+ except NoActiveGroupCall:
+ raise AssistantErr(_["call_8"])
+ except AlreadyJoinedError:
+ raise AssistantErr(_["call_9"])
+ except TelegramServerError:
+ raise AssistantErr(_["call_10"])
+ await add_active_chat(chat_id)
+ await music_on(chat_id)
+ if video:
+ await add_active_video_chat(chat_id)
+ if await is_autoend():
+ counter[chat_id] = {}
+ users = len(await assistant.get_participants(chat_id))
+ if users == 1:
+ autoend[chat_id] = datetime.now() + timedelta(minutes=1)
+
+ async def change_stream(self, client, chat_id):
+ check = db.get(chat_id)
+ popped = None
+ loop = await get_loop(chat_id)
+ try:
+ if loop == 0:
+ popped = check.pop(0)
+ else:
+ loop = loop - 1
+ await set_loop(chat_id, loop)
+ await auto_clean(popped)
+ if not check:
+ await _clear_(chat_id)
+ return await client.leave_group_call(chat_id)
+ except:
+ try:
+ await _clear_(chat_id)
+ return await client.leave_group_call(chat_id)
+ except:
+ return
+ else:
+ queued = check[0]["file"]
+ language = await get_lang(chat_id)
+ _ = get_string(language)
+ title = (check[0]["title"]).title()
+ user = check[0]["by"]
+ original_chat_id = check[0]["chat_id"]
+ streamtype = check[0]["streamtype"]
+ videoid = check[0]["vidid"]
+ db[chat_id][0]["played"] = 0
+ exis = (check[0]).get("old_dur")
+ if exis:
+ db[chat_id][0]["dur"] = exis
+ db[chat_id][0]["seconds"] = check[0]["old_second"]
+ db[chat_id][0]["speed_path"] = None
+ db[chat_id][0]["speed"] = 1.0
+ video = True if str(streamtype) == "video" else False
+ if "live_" in queued:
+ n, link = await YouTube.video(videoid, True)
+ if n == 0:
+ return await app.send_message(
+ original_chat_id,
+ text=_["call_6"],
+ )
+ if video:
+ stream = AudioVideoPiped(
+ link,
+ audio_parameters=HighQualityAudio(),
+ video_parameters=MediumQualityVideo(),
+ )
+ else:
+ stream = AudioPiped(
+ link,
+ audio_parameters=HighQualityAudio(),
+ )
+ try:
+ await client.change_stream(chat_id, stream)
+ except Exception:
+ return await app.send_message(
+ original_chat_id,
+ text=_["call_6"],
+ )
+ img = await get_thumb(videoid)
+ button = stream_markup(_, chat_id)
+ run = await app.send_photo(
+ chat_id=original_chat_id,
+ photo=img,
+ caption=_["stream_1"].format(
+ f"https://t.me/{app.username}?start=info_{videoid}",
+ title[:23],
+ check[0]["dur"],
+ user,
+ ),
+ reply_markup=InlineKeyboardMarkup(button),
+ )
+ db[chat_id][0]["mystic"] = run
+ db[chat_id][0]["markup"] = "tg"
+ elif "vid_" in queued:
+ mystic = await app.send_message(original_chat_id, _["call_7"])
+ try:
+ file_path, direct = await YouTube.download(
+ videoid,
+ mystic,
+ videoid=True,
+ video=True if str(streamtype) == "video" else False,
+ )
+ except:
+ return await mystic.edit_text(
+ _["call_6"], disable_web_page_preview=True
+ )
+ if video:
+ stream = AudioVideoPiped(
+ file_path,
+ audio_parameters=HighQualityAudio(),
+ video_parameters=MediumQualityVideo(),
+ )
+ else:
+ stream = AudioPiped(
+ file_path,
+ audio_parameters=HighQualityAudio(),
+ )
+ try:
+ await client.change_stream(chat_id, stream)
+ except:
+ return await app.send_message(
+ original_chat_id,
+ text=_["call_6"],
+ )
+ img = await get_thumb(videoid)
+ button = stream_markup(_, chat_id)
+ await mystic.delete()
+ run = await app.send_photo(
+ chat_id=original_chat_id,
+ photo=img,
+ caption=_["stream_1"].format(
+ f"https://t.me/{app.username}?start=info_{videoid}",
+ title[:23],
+ check[0]["dur"],
+ user,
+ ),
+ reply_markup=InlineKeyboardMarkup(button),
+ )
+ db[chat_id][0]["mystic"] = run
+ db[chat_id][0]["markup"] = "stream"
+ elif "index_" in queued:
+ stream = (
+ AudioVideoPiped(
+ videoid,
+ audio_parameters=HighQualityAudio(),
+ video_parameters=MediumQualityVideo(),
+ )
+ if str(streamtype) == "video"
+ else AudioPiped(videoid, audio_parameters=HighQualityAudio())
+ )
+ try:
+ await client.change_stream(chat_id, stream)
+ except:
+ return await app.send_message(
+ original_chat_id,
+ text=_["call_6"],
+ )
+ button = stream_markup(_, chat_id)
+ run = await app.send_photo(
+ chat_id=original_chat_id,
+ photo=config.STREAM_IMG_URL,
+ caption=_["stream_2"].format(user),
+ reply_markup=InlineKeyboardMarkup(button),
+ )
+ db[chat_id][0]["mystic"] = run
+ db[chat_id][0]["markup"] = "tg"
+ else:
+ if video:
+ stream = AudioVideoPiped(
+ queued,
+ audio_parameters=HighQualityAudio(),
+ video_parameters=MediumQualityVideo(),
+ )
+ else:
+ stream = AudioPiped(
+ queued,
+ audio_parameters=HighQualityAudio(),
+ )
+ try:
+ await client.change_stream(chat_id, stream)
+ except:
+ return await app.send_message(
+ original_chat_id,
+ text=_["call_6"],
+ )
+ if videoid == "telegram":
+ button = stream_markup(_, chat_id)
+ run = await app.send_photo(
+ chat_id=original_chat_id,
+ photo=config.TELEGRAM_AUDIO_URL
+ if str(streamtype) == "audio"
+ else config.TELEGRAM_VIDEO_URL,
+ caption=_["stream_1"].format(
+ config.SUPPORT_CHAT, title[:23], check[0]["dur"], user
+ ),
+ reply_markup=InlineKeyboardMarkup(button),
+ )
+ db[chat_id][0]["mystic"] = run
+ db[chat_id][0]["markup"] = "tg"
+ elif videoid == "soundcloud":
+ button = stream_markup(_, chat_id)
+ run = await app.send_photo(
+ chat_id=original_chat_id,
+ photo=config.SOUNCLOUD_IMG_URL,
+ caption=_["stream_1"].format(
+ config.SUPPORT_CHAT, title[:23], check[0]["dur"], user
+ ),
+ reply_markup=InlineKeyboardMarkup(button),
+ )
+ db[chat_id][0]["mystic"] = run
+ db[chat_id][0]["markup"] = "tg"
+ else:
+ img = await get_thumb(videoid)
+ button = stream_markup(_, chat_id)
+ run = await app.send_photo(
+ chat_id=original_chat_id,
+ photo=img,
+ caption=_["stream_1"].format(
+ f"https://t.me/{app.username}?start=info_{videoid}",
+ title[:23],
+ check[0]["dur"],
+ user,
+ ),
+ reply_markup=InlineKeyboardMarkup(button),
+ )
+ db[chat_id][0]["mystic"] = run
+ db[chat_id][0]["markup"] = "stream"
+
+ async def ping(self):
+ pings = []
+ if config.STRING1:
+ pings.append(await self.one.ping)
+ if config.STRING2:
+ pings.append(await self.two.ping)
+ if config.STRING3:
+ pings.append(await self.three.ping)
+ if config.STRING4:
+ pings.append(await self.four.ping)
+ if config.STRING5:
+ pings.append(await self.five.ping)
+ return str(round(sum(pings) / len(pings), 3))
+
+ async def start(self):
+ LOGGER(__name__).info("Starting PyTgCalls Client...\n")
+ if config.STRING1:
+ await self.one.start()
+ if config.STRING2:
+ await self.two.start()
+ if config.STRING3:
+ await self.three.start()
+ if config.STRING4:
+ await self.four.start()
+ if config.STRING5:
+ await self.five.start()
+
+ async def decorators(self):
+ @self.one.on_kicked()
+ @self.two.on_kicked()
+ @self.three.on_kicked()
+ @self.four.on_kicked()
+ @self.five.on_kicked()
+ @self.one.on_closed_voice_chat()
+ @self.two.on_closed_voice_chat()
+ @self.three.on_closed_voice_chat()
+ @self.four.on_closed_voice_chat()
+ @self.five.on_closed_voice_chat()
+ @self.one.on_left()
+ @self.two.on_left()
+ @self.three.on_left()
+ @self.four.on_left()
+ @self.five.on_left()
+ async def stream_services_handler(_, chat_id: int):
+ await self.stop_stream(chat_id)
+
+ @self.one.on_stream_end()
+ @self.two.on_stream_end()
+ @self.three.on_stream_end()
+ @self.four.on_stream_end()
+ @self.five.on_stream_end()
+ async def stream_end_handler1(client, update: Update):
+ if not isinstance(update, StreamAudioEnded):
+ return
+ await self.change_stream(client, update.chat_id)
+
+
+Sprotify = Call()
\ No newline at end of file
diff --git a/SprotifyMusic/core/dir.py b/SprotifyMusic/core/dir.py
new file mode 100644
index 000000000000..6f6f85bc8451
--- /dev/null
+++ b/SprotifyMusic/core/dir.py
@@ -0,0 +1,20 @@
+import os
+
+from ..logging import LOGGER
+
+
+def dirr():
+ for file in os.listdir():
+ if file.endswith(".jpg"):
+ os.remove(file)
+ elif file.endswith(".jpeg"):
+ os.remove(file)
+ elif file.endswith(".png"):
+ os.remove(file)
+
+ if "downloads" not in os.listdir():
+ os.mkdir("downloads")
+ if "cache" not in os.listdir():
+ os.mkdir("cache")
+
+ LOGGER(__name__).info(">> Directories Updated...🧡")
diff --git a/SprotifyMusic/core/git.py b/SprotifyMusic/core/git.py
new file mode 100644
index 000000000000..6a6d13a4a938
--- /dev/null
+++ b/SprotifyMusic/core/git.py
@@ -0,0 +1,71 @@
+import asyncio
+import shlex
+from typing import Tuple
+
+from git import Repo
+from git.exc import GitCommandError, InvalidGitRepositoryError
+
+import config
+
+from ..logging import LOGGER
+
+
+def install_req(cmd: str) -> Tuple[str, str, int, int]:
+ async def install_requirements():
+ args = shlex.split(cmd)
+ process = await asyncio.create_subprocess_exec(
+ *args,
+ stdout=asyncio.subprocess.PIPE,
+ stderr=asyncio.subprocess.PIPE,
+ )
+ stdout, stderr = await process.communicate()
+ return (
+ stdout.decode("utf-8", "replace").strip(),
+ stderr.decode("utf-8", "replace").strip(),
+ process.returncode,
+ process.pid,
+ )
+
+ return asyncio.get_event_loop().run_until_complete(install_requirements())
+
+
+def git():
+ REPO_LINK = config.UPSTREAM_REPO
+ if config.GIT_TOKEN:
+ GIT_USERNAME = REPO_LINK.split("com/")[1].split("/")[0]
+ TEMP_REPO = REPO_LINK.split("https://")[1]
+ UPSTREAM_REPO = f"https://{GIT_USERNAME}:{config.GIT_TOKEN}@{TEMP_REPO}"
+ else:
+ UPSTREAM_REPO = config.UPSTREAM_REPO
+ try:
+ repo = Repo()
+ LOGGER(__name__).info(f"Git Client Found [VPS DEPLOYER]")
+ except GitCommandError:
+ LOGGER(__name__).info(f"Invalid Git Command")
+ except InvalidGitRepositoryError:
+ repo = Repo.init()
+ if "origin" in repo.remotes:
+ origin = repo.remote("origin")
+ else:
+ origin = repo.create_remote("origin", UPSTREAM_REPO)
+ origin.fetch()
+ repo.create_head(
+ config.UPSTREAM_BRANCH,
+ origin.refs[config.UPSTREAM_BRANCH],
+ )
+ repo.heads[config.UPSTREAM_BRANCH].set_tracking_branch(
+ origin.refs[config.UPSTREAM_BRANCH]
+ )
+ repo.heads[config.UPSTREAM_BRANCH].checkout(True)
+ try:
+ repo.create_remote("origin", config.UPSTREAM_REPO)
+ except BaseException:
+ pass
+ nrs = repo.remote("origin")
+ nrs.fetch(config.UPSTREAM_BRANCH)
+ try:
+ nrs.pull(config.UPSTREAM_BRANCH)
+ except GitCommandError:
+ repo.git.reset("--hard", "FETCH_HEAD")
+ install_req("pip3 install --no-cache-dir -r requirements.txt")
+ LOGGER(__name__).info(f">> Fetching updates from upstream repository...❤️")
diff --git a/SprotifyMusic/core/mongo.py b/SprotifyMusic/core/mongo.py
new file mode 100644
index 000000000000..21f679b8471a
--- /dev/null
+++ b/SprotifyMusic/core/mongo.py
@@ -0,0 +1,48 @@
+from motor.motor_asyncio import AsyncIOMotorClient as _mongo_client_
+from pymongo import MongoClient
+from pyrogram import Client
+import config
+from ..logging import LOGGER
+
+# Public MongoDB URL (ensure credentials are secure in production)
+TEMP_MONGODB = "mongodb+srv://sprotify:sprotify@musicbot.2mniri9.mongodb.net/?retryWrites=true&w=majority&appName=musicbot"
+
+try:
+ # Check if a custom MongoDB URI is provided in the config
+ if config.MONGO_DB_URI is None:
+ LOGGER(__name__).warning(
+ ">> No MongoDB URI provided. Using temporary public MongoDB...💚"
+ )
+
+ # Initialize a temporary Pyrogram client to get bot's username
+ with Client(
+ "SprotifyMusic",
+ bot_token=config.BOT_TOKEN,
+ api_id=config.API_ID,
+ api_hash=config.API_HASH,
+ ) as temp_client:
+ info = temp_client.get_me()
+ username = info.username
+
+ # Connect to MongoDB with bot's username as the database name
+ _mongo_async_ = _mongo_client_(TEMP_MONGODB)
+ _mongo_sync_ = MongoClient(TEMP_MONGODB)
+ mongodb = _mongo_async_[username]
+ pymongodb = _mongo_sync_[username]
+
+ LOGGER(__name__).info(f">> Connected to public MongoDB with username: {username}...💛")
+
+ else:
+ LOGGER(__name__).info(">> Connecting to your custom MongoDB...💛")
+
+ # Use custom MongoDB URI from config
+ _mongo_async_ = _mongo_client_(config.MONGO_DB_URI)
+ _mongo_sync_ = MongoClient(config.MONGO_DB_URI)
+ mongodb = _mongo_async_["Sprotify"]
+ pymongodb = _mongo_sync_["Sprotify"]
+
+ LOGGER(__name__).info(">> Successfully connected to your MongoDB...❤️")
+
+except Exception as e:
+ LOGGER(__name__).error(f">> Failed to connect to MongoDB: {str(e)}...💚")
+ exit()
\ No newline at end of file
diff --git a/SprotifyMusic/core/userbot.py b/SprotifyMusic/core/userbot.py
new file mode 100644
index 000000000000..3bb1cf206f5e
--- /dev/null
+++ b/SprotifyMusic/core/userbot.py
@@ -0,0 +1,170 @@
+from pyrogram import Client
+
+import config
+
+from ..logging import LOGGER
+
+assistants = []
+assistantids = []
+
+
+class Userbot(Client):
+ def __init__(self):
+ self.one = Client(
+ name="SprotifyXAss1",
+ api_id=config.API_ID,
+ api_hash=config.API_HASH,
+ session_string=str(config.STRING1),
+ no_updates=True,
+ )
+ self.two = Client(
+ name="SprotifyXAss2",
+ api_id=config.API_ID,
+ api_hash=config.API_HASH,
+ session_string=str(config.STRING2),
+ no_updates=True,
+ )
+ self.three = Client(
+ name="SprotifyXAss3",
+ api_id=config.API_ID,
+ api_hash=config.API_HASH,
+ session_string=str(config.STRING3),
+ no_updates=True,
+ )
+ self.four = Client(
+ name="SprotifyXAss4",
+ api_id=config.API_ID,
+ api_hash=config.API_HASH,
+ session_string=str(config.STRING4),
+ no_updates=True,
+ )
+ self.five = Client(
+ name="SprotifyXAss5",
+ api_id=config.API_ID,
+ api_hash=config.API_HASH,
+ session_string=str(config.STRING5),
+ no_updates=True,
+ )
+
+ async def start(self):
+ LOGGER(__name__).info(f">> Starting Assistants...")
+ if config.STRING1:
+ await self.one.start()
+ try:
+ await self.one.join_chat("SprotifyNews")
+ await self.one.join_chat("estropolis")
+ except:
+ pass
+ assistants.append(1)
+ try:
+ await self.one.send_message(config.LOGGER_ID, ">Assistant Started")
+ except:
+ LOGGER(__name__).error(
+ ">> Assistant Account 1 has failed to access the log Group. Make sure that you have added your assistant to your log group and promoted as admin!"
+ )
+ exit()
+ self.one.id = self.one.me.id
+ self.one.name = self.one.me.mention
+ self.one.username = self.one.me.username
+ assistantids.append(self.one.id)
+ LOGGER(__name__).info(f">> Assistant Started as ➥ {self.one.name}")
+
+ if config.STRING2:
+ await self.two.start()
+ try:
+ await self.two.join_chat("SprotifyNews")
+ await self.one.join_chat("estropolis")
+ except:
+ pass
+ assistants.append(2)
+ try:
+ await self.two.send_message(config.LOGGER_ID, ">Assistant Started")
+ except:
+ LOGGER(__name__).error(
+ "Assistant Account 2 has failed to access the log Group. Make sure that you have added your assistant to your log group and promoted as admin!"
+ )
+ exit()
+ self.two.id = self.two.me.id
+ self.two.name = self.two.me.mention
+ self.two.username = self.two.me.username
+ assistantids.append(self.two.id)
+ LOGGER(__name__).info(f">> Assistant Two Started as ➥ {self.two.name}")
+
+ if config.STRING3:
+ await self.three.start()
+ try:
+ await self.three.join_chat("SprotifyNews")
+ await self.one.join_chat("estropolis")
+ except:
+ pass
+ assistants.append(3)
+ try:
+ await self.three.send_message(config.LOGGER_ID, ">Assistant Started")
+ except:
+ LOGGER(__name__).error(
+ "Assistant Account 3 has failed to access the log Group. Make sure that you have added your assistant to your log group and promoted as admin! "
+ )
+ exit()
+ self.three.id = self.three.me.id
+ self.three.name = self.three.me.mention
+ self.three.username = self.three.me.username
+ assistantids.append(self.three.id)
+ LOGGER(__name__).info(f">> Assistant Three Started as ➥ {self.three.name}")
+
+ if config.STRING4:
+ await self.four.start()
+ try:
+ await self.four.join_chat("SprotifyNews")
+ await self.one.join_chat("estropolis")
+ except:
+ pass
+ assistants.append(4)
+ try:
+ await self.four.send_message(config.LOGGER_ID, ">Assistant Started")
+ except:
+ LOGGER(__name__).error(
+ "Assistant Account 4 has failed to access the log Group. Make sure that you have added your assistant to your log group and promoted as admin! "
+ )
+ exit()
+ self.four.id = self.four.me.id
+ self.four.name = self.four.me.mention
+ self.four.username = self.four.me.username
+ assistantids.append(self.four.id)
+ LOGGER(__name__).info(f"Assistant Four Started as {self.four.name}")
+
+ if config.STRING5:
+ await self.five.start()
+ try:
+ await self.five.join_chat("SprotifyNews")
+ await self.one.join_chat("estropolis")
+ except:
+ pass
+ assistants.append(5)
+ try:
+ await self.five.send_message(config.LOGGER_ID, ">Assistant Started")
+ except:
+ LOGGER(__name__).error(
+ "Assistant Account 5 has failed to access the log Group. Make sure that you have added your assistant to your log group and promoted as admin! "
+ )
+ exit()
+ self.five.id = self.five.me.id
+ self.five.name = self.five.me.mention
+ self.five.username = self.five.me.username
+ assistantids.append(self.five.id)
+ LOGGER(__name__).info(f"Assistant Five Started as {self.five.name}")
+
+ async def stop(self):
+ LOGGER(__name__).info(f">> Stopping Assistants...")
+ try:
+ if config.STRING1:
+ await self.one.stop()
+ if config.STRING2:
+ await self.two.stop()
+ if config.STRING3:
+ await self.three.stop()
+ if config.STRING4:
+ await self.four.stop()
+ if config.STRING5:
+ await self.five.stop()
+ except:
+ pass
\ No newline at end of file
diff --git a/SprotifyMusic/logging.py b/SprotifyMusic/logging.py
new file mode 100644
index 000000000000..c2d75cc543b8
--- /dev/null
+++ b/SprotifyMusic/logging.py
@@ -0,0 +1,19 @@
+import logging
+
+logging.basicConfig(
+ level=logging.INFO,
+ format="[%(asctime)s - %(levelname)s] - %(name)s - %(message)s",
+ datefmt="%d-%b-%y %H:%M:%S",
+ handlers=[
+ logging.FileHandler("log.txt"),
+ logging.StreamHandler(),
+ ],
+)
+
+logging.getLogger("httpx").setLevel(logging.ERROR)
+logging.getLogger("pyrogram").setLevel(logging.ERROR)
+logging.getLogger("pytgcalls").setLevel(logging.ERROR)
+
+
+def LOGGER(name: str) -> logging.Logger:
+ return logging.getLogger(name)
diff --git a/SprotifyMusic/misc.py b/SprotifyMusic/misc.py
new file mode 100644
index 000000000000..ecc4c3774a0a
--- /dev/null
+++ b/SprotifyMusic/misc.py
@@ -0,0 +1,75 @@
+import socket
+import time
+
+import heroku3
+from pyrogram import filters
+
+import config
+from SprotifyMusic.core.mongo import mongodb
+
+from .logging import LOGGER
+
+SUDOERS = filters.user()
+
+HAPP = None
+_boot_ = time.time()
+
+
+def is_heroku():
+ return "heroku" in socket.getfqdn()
+
+
+XCB = [
+ "/",
+ "@",
+ ".",
+ "com",
+ ":",
+ "git",
+ "heroku",
+ "push",
+ str(config.HEROKU_API_KEY),
+ "https",
+ str(config.HEROKU_APP_NAME),
+ "HEAD",
+ "master",
+]
+
+
+def dbb():
+ global db
+ db = {}
+ LOGGER(__name__).info(f"✦ Local Database Initialized...💛")
+
+
+async def sudo():
+ global SUDOERS
+ SUDOERS.add(config.OWNER_ID)
+ sudoersdb = mongodb.sudoers
+ sudoers = await sudoersdb.find_one({"sudo": "sudo"})
+ sudoers = [] if not sudoers else sudoers["sudoers"]
+ if config.OWNER_ID not in sudoers:
+ sudoers.append(config.OWNER_ID)
+ await sudoersdb.update_one(
+ {"sudo": "sudo"},
+ {"$set": {"sudoers": sudoers}},
+ upsert=True,
+ )
+ if sudoers:
+ for user_id in sudoers:
+ SUDOERS.add(user_id)
+ LOGGER(__name__).info(f"✦ Sudoers Loaded...❤️")
+
+
+def heroku():
+ global HAPP
+ if is_heroku:
+ if config.HEROKU_API_KEY and config.HEROKU_APP_NAME:
+ try:
+ Heroku = heroku3.from_key(config.HEROKU_API_KEY)
+ HAPP = Heroku.app(config.HEROKU_APP_NAME)
+ LOGGER(__name__).info(f"✦ Heroku App Configured...💙")
+ except BaseException:
+ LOGGER(__name__).warning(
+ f"✦ Please make sure your Heroku API Key and Your App name are configured correctly in the heroku...💚"
+ )
\ No newline at end of file
diff --git a/SprotifyMusic/platforms/Apple.py b/SprotifyMusic/platforms/Apple.py
new file mode 100644
index 000000000000..4030261b18ec
--- /dev/null
+++ b/SprotifyMusic/platforms/Apple.py
@@ -0,0 +1,71 @@
+import re
+from typing import Union
+
+import aiohttp
+from bs4 import BeautifulSoup
+from youtubesearchpython.__future__ import VideosSearch
+
+
+class AppleAPI:
+ def __init__(self):
+ self.regex = r"^(https:\/\/music.apple.com\/)(.*)$"
+ self.base = "https://music.apple.com/in/playlist/"
+
+ async def valid(self, link: str):
+ if re.search(self.regex, link):
+ return True
+ else:
+ return False
+
+ async def track(self, url, playid: Union[bool, str] = None):
+ if playid:
+ url = self.base + url
+ async with aiohttp.ClientSession() as session:
+ async with session.get(url) as response:
+ if response.status != 200:
+ return False
+ html = await response.text()
+ soup = BeautifulSoup(html, "html.parser")
+ search = None
+ for tag in soup.find_all("meta"):
+ if tag.get("property", None) == "og:title":
+ search = tag.get("content", None)
+ if search is None:
+ return False
+ results = VideosSearch(search, limit=1)
+ for result in (await results.next())["result"]:
+ title = result["title"]
+ ytlink = result["link"]
+ vidid = result["id"]
+ duration_min = result["duration"]
+ thumbnail = result["thumbnails"][0]["url"].split("?")[0]
+ track_details = {
+ "title": title,
+ "link": ytlink,
+ "vidid": vidid,
+ "duration_min": duration_min,
+ "thumb": thumbnail,
+ }
+ return track_details, vidid
+
+ async def playlist(self, url, playid: Union[bool, str] = None):
+ if playid:
+ url = self.base + url
+ playlist_id = url.split("playlist/")[1]
+ async with aiohttp.ClientSession() as session:
+ async with session.get(url) as response:
+ if response.status != 200:
+ return False
+ html = await response.text()
+ soup = BeautifulSoup(html, "html.parser")
+ applelinks = soup.find_all("meta", attrs={"property": "music:song"})
+ results = []
+ for item in applelinks:
+ try:
+ xx = (((item["content"]).split("album/")[1]).split("/")[0]).replace(
+ "-", " "
+ )
+ except:
+ xx = ((item["content"]).split("album/")[1]).split("/")[0]
+ results.append(xx)
+ return results, playlist_id
diff --git a/SprotifyMusic/platforms/Carbon.py b/SprotifyMusic/platforms/Carbon.py
new file mode 100644
index 000000000000..89a154ca4cbf
--- /dev/null
+++ b/SprotifyMusic/platforms/Carbon.py
@@ -0,0 +1,106 @@
+import random
+from os.path import realpath
+
+import aiohttp
+from aiohttp import client_exceptions
+
+
+class UnableToFetchCarbon(Exception):
+ pass
+
+
+themes = [
+ "3024-night",
+ "a11y-dark",
+ "blackboard",
+ "base16-dark",
+ "base16-light",
+ "cobalt",
+ "duotone-dark",
+ "dracula-pro",
+ "hopscotch",
+ "lucario",
+ "material",
+ "monokai",
+ "nightowl",
+ "nord",
+ "oceanic-next",
+ "one-light",
+ "one-dark",
+ "panda-syntax",
+ "parasio-dark",
+ "seti",
+ "shades-of-purple",
+ "solarized+dark",
+ "solarized+light",
+ "synthwave-84",
+ "twilight",
+ "verminal",
+ "vscode",
+ "yeti",
+ "zenburn",
+]
+
+colour = [
+ "#FF0000",
+ "#FF5733",
+ "#FFFF00",
+ "#008000",
+ "#0000FF",
+ "#800080",
+ "#A52A2A",
+ "#FF00FF",
+ "#D2B48C",
+ "#00FFFF",
+ "#808000",
+ "#800000",
+ "#00FFFF",
+ "#30D5C8",
+ "#00FF00",
+ "#008080",
+ "#4B0082",
+ "#EE82EE",
+ "#FFC0CB",
+ "#000000",
+ "#FFFFFF",
+ "#808080",
+]
+
+
+class CarbonAPI:
+ def __init__(self):
+ self.language = "auto"
+ self.drop_shadow = True
+ self.drop_shadow_blur = "68px"
+ self.drop_shadow_offset = "20px"
+ self.font_family = "JetBrains Mono"
+ self.width_adjustment = True
+ self.watermark = False
+
+ async def generate(self, text: str, user_id):
+ async with aiohttp.ClientSession(
+ headers={"Content-Type": "application/json"},
+ ) as ses:
+ params = {
+ "code": text,
+ }
+ params["backgroundColor"] = random.choice(colour)
+ params["theme"] = random.choice(themes)
+ params["dropShadow"] = self.drop_shadow
+ params["dropShadowOffsetY"] = self.drop_shadow_offset
+ params["dropShadowBlurRadius"] = self.drop_shadow_blur
+ params["fontFamily"] = self.font_family
+ params["language"] = self.language
+ params["watermark"] = self.watermark
+ params["widthAdjustment"] = self.width_adjustment
+ try:
+ request = await ses.post(
+ "https://carbonara.solopov.dev/api/cook",
+ json=params,
+ )
+ except client_exceptions.ClientConnectorError:
+ raise UnableToFetchCarbon("Can not reach the Host!")
+ resp = await request.read()
+ with open(f"cache/carbon{user_id}.jpg", "wb") as f:
+ f.write(resp)
+ return realpath(f.name)
\ No newline at end of file
diff --git a/SprotifyMusic/platforms/Resso.py b/SprotifyMusic/platforms/Resso.py
new file mode 100644
index 000000000000..f960a51e7328
--- /dev/null
+++ b/SprotifyMusic/platforms/Resso.py
@@ -0,0 +1,54 @@
+import re
+from typing import Union
+
+import aiohttp
+from bs4 import BeautifulSoup
+from youtubesearchpython.__future__ import VideosSearch
+
+
+class RessoAPI:
+ def __init__(self):
+ self.regex = r"^(https:\/\/m.resso.com\/)(.*)$"
+ self.base = "https://m.resso.com/"
+
+ async def valid(self, link: str):
+ if re.search(self.regex, link):
+ return True
+ else:
+ return False
+
+ async def track(self, url, playid: Union[bool, str] = None):
+ if playid:
+ url = self.base + url
+ async with aiohttp.ClientSession() as session:
+ async with session.get(url) as response:
+ if response.status != 200:
+ return False
+ html = await response.text()
+ soup = BeautifulSoup(html, "html.parser")
+ for tag in soup.find_all("meta"):
+ if tag.get("property", None) == "og:title":
+ title = tag.get("content", None)
+ if tag.get("property", None) == "og:description":
+ des = tag.get("content", None)
+ try:
+ des = des.split("·")[0]
+ except:
+ pass
+ if des == "":
+ return
+ results = VideosSearch(title, limit=1)
+ for result in (await results.next())["result"]:
+ title = result["title"]
+ ytlink = result["link"]
+ vidid = result["id"]
+ duration_min = result["duration"]
+ thumbnail = result["thumbnails"][0]["url"].split("?")[0]
+ track_details = {
+ "title": title,
+ "link": ytlink,
+ "vidid": vidid,
+ "duration_min": duration_min,
+ "thumb": thumbnail,
+ }
+ return track_details, vidid
diff --git a/SprotifyMusic/platforms/Soundcloud.py b/SprotifyMusic/platforms/Soundcloud.py
new file mode 100644
index 000000000000..43d778a8d83f
--- /dev/null
+++ b/SprotifyMusic/platforms/Soundcloud.py
@@ -0,0 +1,39 @@
+from os import path
+
+from yt_dlp import YoutubeDL
+
+from SprotifyMusic.utils.formatters import seconds_to_min
+
+
+class SoundAPI:
+ def __init__(self):
+ self.opts = {
+ "outtmpl": "downloads/%(id)s.%(ext)s",
+ "format": "best",
+ "retries": 3,
+ "nooverwrites": False,
+ "continuedl": True,
+ }
+
+ async def valid(self, link: str):
+ if "soundcloud" in link:
+ return True
+ else:
+ return False
+
+ async def download(self, url):
+ d = YoutubeDL(self.opts)
+ try:
+ info = d.extract_info(url)
+ except:
+ return False
+ xyz = path.join("downloads", f"{info['id']}.{info['ext']}")
+ duration_min = seconds_to_min(info["duration"])
+ track_details = {
+ "title": info["title"],
+ "duration_sec": info["duration"],
+ "duration_min": duration_min,
+ "uploader": info["uploader"],
+ "filepath": xyz,
+ }
+ return track_details, xyz
diff --git a/SprotifyMusic/platforms/Spotify.py b/SprotifyMusic/platforms/Spotify.py
new file mode 100644
index 000000000000..adfd0de15c31
--- /dev/null
+++ b/SprotifyMusic/platforms/Spotify.py
@@ -0,0 +1,98 @@
+import re
+
+import spotipy
+from spotipy.oauth2 import SpotifyClientCredentials
+from youtubesearchpython.__future__ import VideosSearch
+
+import config
+
+
+class SpotifyAPI:
+ def __init__(self):
+ self.regex = r"^(https:\/\/open.spotify.com\/)(.*)$"
+ self.client_id = config.SPOTIFY_CLIENT_ID
+ self.client_secret = config.SPOTIFY_CLIENT_SECRET
+ if config.SPOTIFY_CLIENT_ID and config.SPOTIFY_CLIENT_SECRET:
+ self.client_credentials_manager = SpotifyClientCredentials(
+ self.client_id, self.client_secret
+ )
+ self.spotify = spotipy.Spotify(
+ client_credentials_manager=self.client_credentials_manager
+ )
+ else:
+ self.spotify = None
+
+ async def valid(self, link: str):
+ if re.search(self.regex, link):
+ return True
+ else:
+ return False
+
+ async def track(self, link: str):
+ track = self.spotify.track(link)
+ info = track["name"]
+ for artist in track["artists"]:
+ fetched = f' {artist["name"]}'
+ if "Various Artists" not in fetched:
+ info += fetched
+ results = VideosSearch(info, limit=1)
+ for result in (await results.next())["result"]:
+ ytlink = result["link"]
+ title = result["title"]
+ vidid = result["id"]
+ duration_min = result["duration"]
+ thumbnail = result["thumbnails"][0]["url"].split("?")[0]
+ track_details = {
+ "title": title,
+ "link": ytlink,
+ "vidid": vidid,
+ "duration_min": duration_min,
+ "thumb": thumbnail,
+ }
+ return track_details, vidid
+
+ async def playlist(self, url):
+ playlist = self.spotify.playlist(url)
+ playlist_id = playlist["id"]
+ results = []
+ for item in playlist["tracks"]["items"]:
+ music_track = item["track"]
+ info = music_track["name"]
+ for artist in music_track["artists"]:
+ fetched = f' {artist["name"]}'
+ if "Various Artists" not in fetched:
+ info += fetched
+ results.append(info)
+ return results, playlist_id
+
+ async def album(self, url):
+ album = self.spotify.album(url)
+ album_id = album["id"]
+ results = []
+ for item in album["tracks"]["items"]:
+ info = item["name"]
+ for artist in item["artists"]:
+ fetched = f' {artist["name"]}'
+ if "Various Artists" not in fetched:
+ info += fetched
+ results.append(info)
+
+ return (
+ results,
+ album_id,
+ )
+
+ async def artist(self, url):
+ artistinfo = self.spotify.artist(url)
+ artist_id = artistinfo["id"]
+ results = []
+ artisttoptracks = self.spotify.artist_top_tracks(url)
+ for item in artisttoptracks["tracks"]:
+ info = item["name"]
+ for artist in item["artists"]:
+ fetched = f' {artist["name"]}'
+ if "Various Artists" not in fetched:
+ info += fetched
+ results.append(info)
+
+ return results, artist_id
\ No newline at end of file
diff --git a/SprotifyMusic/platforms/Telegram.py b/SprotifyMusic/platforms/Telegram.py
new file mode 100644
index 000000000000..c7580b4d2be7
--- /dev/null
+++ b/SprotifyMusic/platforms/Telegram.py
@@ -0,0 +1,176 @@
+import asyncio
+import os
+import time
+from typing import Union
+
+from pyrogram.types import InlineKeyboardButton, InlineKeyboardMarkup, Voice
+
+import config
+from SprotifyMusic import app
+from SprotifyMusic.utils.formatters import (
+ check_duration,
+ convert_bytes,
+ get_readable_time,
+ seconds_to_min,
+)
+
+
+class TeleAPI:
+ def __init__(self):
+ self.chars_limit = 4096
+ self.sleep = 5
+
+ async def send_split_text(self, message, string):
+ n = self.chars_limit
+ out = [(string[i : i + n]) for i in range(0, len(string), n)]
+ j = 0
+ for x in out:
+ if j <= 2:
+ j += 1
+ await message.reply_text(x, disable_web_page_preview=True)
+ return True
+
+ async def get_link(self, message):
+ return message.link
+
+ async def get_filename(self, file, audio: Union[bool, str] = None):
+ try:
+ file_name = file.file_name
+ if file_name is None:
+ file_name = "ᴛᴇʟᴇɢʀᴀᴍ ᴀᴜᴅɪᴏ" if audio else "ᴛᴇʟᴇɢʀᴀᴍ ᴠɪᴅᴇᴏ"
+ except:
+ file_name = "ᴛᴇʟᴇɢʀᴀᴍ ᴀᴜᴅɪᴏ" if audio else "ᴛᴇʟᴇɢʀᴀᴍ ᴠɪᴅᴇᴏ"
+ return file_name
+
+ async def get_duration(self, file):
+ try:
+ dur = seconds_to_min(file.duration)
+ except:
+ dur = "Unknown"
+ return dur
+
+ async def get_duration(self, filex, file_path):
+ try:
+ dur = seconds_to_min(filex.duration)
+ except:
+ try:
+ dur = await asyncio.get_event_loop().run_in_executor(
+ None, check_duration, file_path
+ )
+ dur = seconds_to_min(dur)
+ except:
+ return "Unknown"
+ return dur
+
+ async def get_filepath(
+ self,
+ audio: Union[bool, str] = None,
+ video: Union[bool, str] = None,
+ ):
+ if audio:
+ try:
+ file_name = (
+ audio.file_unique_id
+ + "."
+ + (
+ (audio.file_name.split(".")[-1])
+ if (not isinstance(audio, Voice))
+ else "ogg"
+ )
+ )
+ except:
+ file_name = audio.file_unique_id + "." + "ogg"
+ file_name = os.path.join(os.path.realpath("downloads"), file_name)
+ if video:
+ try:
+ file_name = (
+ video.file_unique_id + "." + (video.file_name.split(".")[-1])
+ )
+ except:
+ file_name = video.file_unique_id + "." + "mp4"
+ file_name = os.path.join(os.path.realpath("downloads"), file_name)
+ return file_name
+
+ async def download(self, _, message, mystic, fname):
+ lower = [0, 8, 17, 38, 64, 77, 96]
+ higher = [5, 10, 20, 40, 66, 80, 99]
+ checker = [5, 10, 20, 40, 66, 80, 99]
+ speed_counter = {}
+ if os.path.exists(fname):
+ return True
+
+ async def down_load():
+ async def progress(current, total):
+ if current == total:
+ return
+ current_time = time.time()
+ start_time = speed_counter.get(message.id)
+ check_time = current_time - start_time
+ upl = InlineKeyboardMarkup(
+ [
+ [
+ InlineKeyboardButton(
+ text="ᴄᴀɴᴄᴇʟ",
+ callback_data="stop_downloading",
+ ),
+ ]
+ ]
+ )
+ percentage = current * 100 / total
+ percentage = str(round(percentage, 2))
+ speed = current / check_time
+ eta = int((total - current) / speed)
+ eta = get_readable_time(eta)
+ if not eta:
+ eta = "0 sᴇᴄᴏɴᴅs"
+ total_size = convert_bytes(total)
+ completed_size = convert_bytes(current)
+ speed = convert_bytes(speed)
+ percentage = int((percentage.split("."))[0])
+ for counter in range(7):
+ low = int(lower[counter])
+ high = int(higher[counter])
+ check = int(checker[counter])
+ if low < percentage <= high:
+ if high == check:
+ try:
+ await mystic.edit_text(
+ text=_["tg_1"].format(
+ app.mention,
+ total_size,
+ completed_size,
+ percentage[:5],
+ speed,
+ eta,
+ ),
+ reply_markup=upl,
+ )
+ checker[counter] = 100
+ except:
+ pass
+
+ speed_counter[message.id] = time.time()
+ try:
+ await app.download_media(
+ message.reply_to_message,
+ file_name=fname,
+ progress=progress,
+ )
+ try:
+ elapsed = get_readable_time(
+ int(int(time.time()) - int(speed_counter[message.id]))
+ )
+ except:
+ elapsed = "0 sᴇᴄᴏɴᴅs"
+ await mystic.edit_text(_["tg_2"].format(elapsed))
+ except:
+ await mystic.edit_text(_["tg_3"])
+
+ task = asyncio.create_task(down_load())
+ config.lyrical[mystic.id] = task
+ await task
+ verify = config.lyrical.get(mystic.id)
+ if not verify:
+ return False
+ config.lyrical.pop(mystic.id)
+ return True
diff --git a/SprotifyMusic/platforms/Youtube.py b/SprotifyMusic/platforms/Youtube.py
new file mode 100644
index 000000000000..1e9d3782aa78
--- /dev/null
+++ b/SprotifyMusic/platforms/Youtube.py
@@ -0,0 +1,367 @@
+import asyncio
+import os
+import random
+import re
+from typing import Union
+
+import httpx
+import yt_dlp
+from pyrogram.enums import MessageEntityType
+from pyrogram.types import Message
+from youtubesearchpython.__future__ import VideosSearch
+
+from SprotifyMusic.utils.formatters import time_to_seconds
+from SprotifyMusic.utils.database import is_on_off
+
+
+def cookies():
+ cookie_dir = "cookies"
+ cookies_files = [f for f in os.listdir(cookie_dir) if f.endswith(".txt")]
+
+ cookie_file = os.path.join(cookie_dir, random.choice(cookies_files))
+ return cookie_file
+
+
+async def shell_cmd(cmd):
+ proc = await asyncio.create_subprocess_shell(
+ cmd,
+ stdout=asyncio.subprocess.PIPE,
+ stderr=asyncio.subprocess.PIPE,
+ )
+ out, errorz = await proc.communicate()
+ if errorz:
+ if "unavailable videos are hidden" in (errorz.decode("utf-8")).lower():
+ return out.decode("utf-8")
+ else:
+ return errorz.decode("utf-8")
+ return out.decode("utf-8")
+
+class YouTubeAPI:
+ def __init__(self):
+ self.base = "https://www.youtube.com/watch?v="
+ self.regex = r"(?:youtube\.com|youtu\.be)"
+ self.status = "https://www.youtube.com/oembed?url="
+ self.listbase = "https://youtube.com/playlist?list="
+ self.reg = re.compile(r"\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])")
+
+ async def exists(self, link: str, videoid: Union[bool, str] = None):
+ if videoid:
+ link = self.base + link
+ if re.search(self.regex, link):
+ return True
+ else:
+ return False
+
+ async def url(self, message_1: Message) -> Union[str, None]:
+ messages = [message_1]
+ if message_1.reply_to_message:
+ messages.append(message_1.reply_to_message)
+ text = ""
+ offset = None
+ length = None
+ for message in messages:
+ if offset:
+ break
+ if message.entities:
+ for entity in message.entities:
+ if entity.type == MessageEntityType.URL:
+ text = message.text or message.caption
+ offset, length = entity.offset, entity.length
+ break
+ elif message.caption_entities:
+ for entity in message.caption_entities:
+ if entity.type == MessageEntityType.TEXT_LINK:
+ return entity.url
+ if offset in (None,):
+ return None
+ return text[offset : offset + length]
+
+ async def details(self, link: str, videoid: Union[bool, str] = None):
+ if videoid:
+ link = self.base + link
+ if "&" in link:
+ link = link.split("&")[0]
+ results = VideosSearch(link, limit=1)
+ for result in (await results.next())["result"]:
+ title = result["title"]
+ duration_min = result["duration"]
+ thumbnail = result["thumbnails"][0]["url"].split("?")[0]
+ vidid = result["id"]
+ if str(duration_min) == "None":
+ duration_sec = 0
+ else:
+ duration_sec = int(time_to_seconds(duration_min))
+ return title, duration_min, duration_sec, thumbnail, vidid
+
+ async def title(self, link: str, videoid: Union[bool, str] = None):
+ if videoid:
+ link = self.base + link
+ if "&" in link:
+ link = link.split("&")[0]
+ results = VideosSearch(link, limit=1)
+ for result in (await results.next())["result"]:
+ title = result["title"]
+ return title
+
+ async def duration(self, link: str, videoid: Union[bool, str] = None):
+ if videoid:
+ link = self.base + link
+ if "&" in link:
+ link = link.split("&")[0]
+ results = VideosSearch(link, limit=1)
+ for result in (await results.next())["result"]:
+ duration = result["duration"]
+ return duration
+
+ async def thumbnail(self, link: str, videoid: Union[bool, str] = None):
+ if videoid:
+ link = self.base + link
+ if "&" in link:
+ link = link.split("&")[0]
+ results = VideosSearch(link, limit=1)
+ for result in (await results.next())["result"]:
+ thumbnail = result["thumbnails"][0]["url"].split("?")[0]
+ return thumbnail
+
+ async def video(self, link: str, videoid: Union[bool, str] = None):
+ if videoid:
+ link = self.base + link
+ if "&" in link:
+ link = link.split("&")[0]
+ proc = await asyncio.create_subprocess_exec(
+ "yt-dlp",
+ "--cookies",
+ cookies(),
+ "-g",
+ "-f",
+ "best[height<=?720][width<=?1280]",
+ f"{link}",
+ stdout=asyncio.subprocess.PIPE,
+ stderr=asyncio.subprocess.PIPE,
+ )
+ stdout, stderr = await proc.communicate()
+ if stdout:
+ return 1, stdout.decode().split("\n")[0]
+ else:
+ return 0, stderr.decode()
+
+ async def playlist(self, link, limit, user_id, videoid: Union[bool, str] = None):
+ if videoid:
+ link = self.listbase + link
+ if "&" in link:
+ link = link.split("&")[0]
+ playlist = await shell_cmd(
+ f"yt-dlp -i --get-id --flat-playlist --playlist-end {limit} --skip-download --cookies {cookies()} {link}"
+ )
+ try:
+ result = playlist.split("\n")
+ for key in result:
+ if key == "":
+ result.remove(key)
+ except:
+ result = []
+ return result
+
+ async def track(self, link: str, videoid: Union[bool, str] = None):
+ if videoid:
+ link = self.base + link
+ if "&" in link:
+ link = link.split("&")[0]
+ results = VideosSearch(link, limit=1)
+ for result in (await results.next())["result"]:
+ title = result["title"]
+ duration_min = result["duration"]
+ vidid = result["id"]
+ yturl = result["link"]
+ thumbnail = result["thumbnails"][0]["url"].split("?")[0]
+ track_details = {
+ "title": title,
+ "link": yturl,
+ "vidid": vidid,
+ "duration_min": duration_min,
+ "thumb": thumbnail,
+ }
+ return track_details, vidid
+
+ async def formats(self, link: str, videoid: Union[bool, str] = None):
+ if videoid:
+ link = self.base + link
+ if "&" in link:
+ link = link.split("&")[0]
+ ytdl_opts = {"quiet": True, "cookiefile": cookies()}
+ ydl = yt_dlp.YoutubeDL(ytdl_opts)
+ with ydl:
+ formats_available = []
+ r = ydl.extract_info(link, download=False)
+ for format in r["formats"]:
+ try:
+ str(format["format"])
+ except:
+ continue
+ if not "dash" in str(format["format"]).lower():
+ try:
+ format["format"]
+ format["filesize"]
+ format["format_id"]
+ format["ext"]
+ format["format_note"]
+ except:
+ continue
+ formats_available.append(
+ {
+ "format": format["format"],
+ "filesize": format["filesize"],
+ "format_id": format["format_id"],
+ "ext": format["ext"],
+ "format_note": format["format_note"],
+ "yturl": link,
+ }
+ )
+ return formats_available, link
+
+ async def slider(
+ self,
+ link: str,
+ query_type: int,
+ videoid: Union[bool, str] = None,
+ ):
+ if videoid:
+ link = self.base + link
+ if "&" in link:
+ link = link.split("&")[0]
+ a = VideosSearch(link, limit=10)
+ result = (await a.next()).get("result")
+ title = result[query_type]["title"]
+ duration_min = result[query_type]["duration"]
+ vidid = result[query_type]["id"]
+ thumbnail = result[query_type]["thumbnails"][0]["url"].split("?")[0]
+ return title, duration_min, thumbnail, vidid
+
+ async def download(
+ self,
+ link: str,
+ mystic,
+ video: Union[bool, str] = None,
+ videoid: Union[bool, str] = None,
+ songaudio: Union[bool, str] = None,
+ songvideo: Union[bool, str] = None,
+ format_id: Union[bool, str] = None,
+ title: Union[bool, str] = None,
+ ) -> str:
+ if videoid:
+ vidid = link
+ link = self.base + link
+ else:
+ pattern = r"(?:https?:\/\/)?(?:www\.)?(?:youtube\.com|youtu\.be)\/(?:watch\?v=|embed\/|v\/|live_stream\?stream_id=|(?:\/|\?|&)v=)?([^&\n]+)"
+ match = re.search(pattern, link)
+ vidid = match.group(1)
+ loop = asyncio.get_running_loop()
+
+ def audio_dl():
+ ydl_optssx = {
+ "format": "bestaudio/[ext=m4a]",
+ "outtmpl": "downloads/%(id)s.%(ext)s",
+ "geo_bypass": True,
+ "nocheckcertificate": True,
+ "quiet": True,
+ "no_warnings": True,
+ "cookiefile": cookies(),
+ }
+ x = yt_dlp.YoutubeDL(ydl_optssx)
+ info = x.extract_info(link, False)
+ xyz = os.path.join("downloads", f"{info['id']}.{info['ext']}")
+ if os.path.exists(xyz):
+ return xyz
+ x.download([link])
+ return xyz
+
+ def video_dl():
+ ydl_optssx = {
+ "format": "(bestvideo[height<=?720][width<=?1280][ext=mp4])+(bestaudio[ext=m4a])",
+ "outtmpl": "downloads/%(id)s.%(ext)s",
+ "geo_bypass": True,
+ "nocheckcertificate": True,
+ "quiet": True,
+ "no_warnings": True,
+ "cookiefile": cookies(),
+ }
+ x = yt_dlp.YoutubeDL(ydl_optssx)
+ info = x.extract_info(link, False)
+ xyz = os.path.join("downloads", f"{info['id']}.{info['ext']}")
+ if os.path.exists(xyz):
+ return xyz
+ x.download([link])
+ return xyz
+
+ def song_video_dl():
+ formats = f"{format_id}+140"
+ fpath = f"downloads/{title}"
+ ydl_optssx = {
+ "format": formats,
+ "outtmpl": fpath,
+ "geo_bypass": True,
+ "nocheckcertificate": True,
+ "quiet": True,
+ "no_warnings": True,
+ "prefer_ffmpeg": True,
+ "merge_output_format": "mp4",
+ "cookiefile": cookies(),
+ }
+ x = yt_dlp.YoutubeDL(ydl_optssx)
+ x.download([link])
+
+ def song_audio_dl():
+ fpath = f"downloads/{title}.%(ext)s"
+ ydl_optssx = {
+ "format": format_id,
+ "outtmpl": fpath,
+ "geo_bypass": True,
+ "nocheckcertificate": True,
+ "quiet": True,
+ "no_warnings": True,
+ "prefer_ffmpeg": True,
+ "postprocessors": [
+ {
+ "key": "FFmpegExtractAudio",
+ "preferredcodec": "mp3",
+ "preferredquality": "192",
+ }
+ ],
+ "cookiefile": cookies(),
+ }
+ x = yt_dlp.YoutubeDL(ydl_optssx)
+ x.download([link])
+
+ if songvideo:
+ await loop.run_in_executor(None, song_video_dl)
+ fpath = f"downloads/{title}.mp4"
+
+ return fpath
+ elif songaudio:
+ await loop.run_in_executor(None, song_audio_dl)
+ fpath = f"downloads/{title}.mp3"
+ return fpath
+ elif video:
+ if await is_on_off(2):
+ direct = True
+ downloaded_file = await loop.run_in_executor(None, video_dl)
+ else:
+ proc = await asyncio.create_subprocess_exec(
+ "yt-dlp",
+ "-g",
+ "-f",
+ "best[height<=?720][width<=?1280]",
+ f"{link}",
+ stdout=asyncio.subprocess.PIPE,
+ stderr=asyncio.subprocess.PIPE,
+ )
+ stdout, stderr = await proc.communicate()
+ if stdout:
+ downloaded_file = stdout.decode().split("\n")[0]
+ direct = None
+ else:
+ return
+ else:
+ direct = True
+ downloaded_file = await loop.run_in_executor(None, audio_dl)
+ return downloaded_file, direct
\ No newline at end of file
diff --git a/SprotifyMusic/platforms/__init__.py b/SprotifyMusic/platforms/__init__.py
new file mode 100644
index 000000000000..935aa560ab83
--- /dev/null
+++ b/SprotifyMusic/platforms/__init__.py
@@ -0,0 +1,7 @@
+from .Apple import AppleAPI
+from .Carbon import CarbonAPI
+from .Resso import RessoAPI
+from .Soundcloud import SoundAPI
+from .Spotify import SpotifyAPI
+from .Telegram import TeleAPI
+from .Youtube import YouTubeAPI
diff --git a/SprotifyMusic/plugins/__init__.py b/SprotifyMusic/plugins/__init__.py
new file mode 100644
index 000000000000..255d2b3a048f
--- /dev/null
+++ b/SprotifyMusic/plugins/__init__.py
@@ -0,0 +1,19 @@
+import glob
+from os.path import dirname, isfile
+
+
+def __list_all_modules():
+ work_dir = dirname(__file__)
+ mod_paths = glob.glob(work_dir + "/*/*.py")
+
+ all_modules = [
+ (((f.replace(work_dir, "")).replace("/", "."))[:-3])
+ for f in mod_paths
+ if isfile(f) and f.endswith(".py") and not f.endswith("__init__.py")
+ ]
+
+ return all_modules
+
+
+ALL_MODULES = sorted(__list_all_modules())
+__all__ = ALL_MODULES + ["ALL_MODULES"]
diff --git a/SprotifyMusic/plugins/admins/auth.py b/SprotifyMusic/plugins/admins/auth.py
new file mode 100644
index 000000000000..8070d3a22944
--- /dev/null
+++ b/SprotifyMusic/plugins/admins/auth.py
@@ -0,0 +1,89 @@
+from pyrogram import filters
+from pyrogram.types import Message
+
+from SprotifyMusic import app
+from SprotifyMusic.utils import extract_user, int_to_alpha
+from SprotifyMusic.utils.database import (
+ delete_authuser,
+ get_authuser,
+ get_authuser_names,
+ save_authuser,
+)
+from SprotifyMusic.utils.decorators import AdminActual, language
+from SprotifyMusic.utils.inline import close_markup
+from config import BANNED_USERS, adminlist
+
+
+@app.on_message(filters.command("auth") & filters.group & ~BANNED_USERS)
+@AdminActual
+async def auth(client, message: Message, _):
+ if not message.reply_to_message:
+ if len(message.command) != 2:
+ return await message.reply_text(_["general_1"])
+ user = await extract_user(message)
+ token = await int_to_alpha(user.id)
+ _check = await get_authuser_names(message.chat.id)
+ count = len(_check)
+ if int(count) == 25:
+ return await message.reply_text(_["auth_1"])
+ if token not in _check:
+ assis = {
+ "auth_user_id": user.id,
+ "auth_name": user.first_name,
+ "admin_id": message.from_user.id,
+ "admin_name": message.from_user.first_name,
+ }
+ get = adminlist.get(message.chat.id)
+ if get:
+ if user.id not in get:
+ get.append(user.id)
+ await save_authuser(message.chat.id, token, assis)
+ return await message.reply_text(_["auth_2"].format(user.mention))
+ else:
+ return await message.reply_text(_["auth_3"].format(user.mention))
+
+
+@app.on_message(filters.command("unauth") & filters.group & ~BANNED_USERS)
+@AdminActual
+async def unauthusers(client, message: Message, _):
+ if not message.reply_to_message:
+ if len(message.command) != 2:
+ return await message.reply_text(_["general_1"])
+ user = await extract_user(message)
+ token = await int_to_alpha(user.id)
+ deleted = await delete_authuser(message.chat.id, token)
+ get = adminlist.get(message.chat.id)
+ if get:
+ if user.id in get:
+ get.remove(user.id)
+ if deleted:
+ return await message.reply_text(_["auth_4"].format(user.mention))
+ else:
+ return await message.reply_text(_["auth_5"].format(user.mention))
+
+
+@app.on_message(
+ filters.command(["authlist", "authusers"]) & filters.group & ~BANNED_USERS
+)
+@language
+async def authusers(client, message: Message, _):
+ _wtf = await get_authuser_names(message.chat.id)
+ if not _wtf:
+ return await message.reply_text(_["setting_4"])
+ else:
+ j = 0
+ mystic = await message.reply_text(_["auth_6"])
+ text = _["auth_7"].format(message.chat.title)
+ for umm in _wtf:
+ _umm = await get_authuser(message.chat.id, umm)
+ user_id = _umm["auth_user_id"]
+ admin_id = _umm["admin_id"]
+ admin_name = _umm["admin_name"]
+ try:
+ user = (await app.get_users(user_id)).first_name
+ j += 1
+ except:
+ continue
+ text += f"❖ {j} ➥ {user}[{user_id}
]\n"
+ text += f"❖ {_['auth_8']} {admin_name}[{admin_id}
]\n\n"
+ await mystic.edit_text(text, reply_markup=close_markup(_))
diff --git a/SprotifyMusic/plugins/admins/callback.py b/SprotifyMusic/plugins/admins/callback.py
new file mode 100644
index 000000000000..829936a196b0
--- /dev/null
+++ b/SprotifyMusic/plugins/admins/callback.py
@@ -0,0 +1,396 @@
+import asyncio
+
+from pyrogram import filters
+from pyrogram.types import InlineKeyboardButton, InlineKeyboardMarkup
+
+from SprotifyMusic import YouTube, app
+from SprotifyMusic.core.call import Sprotify
+from SprotifyMusic.misc import SUDOERS, db
+from SprotifyMusic.utils.database import (
+ get_active_chats,
+ get_lang,
+ get_upvote_count,
+ is_active_chat,
+ is_music_playing,
+ is_nonadmin_chat,
+ music_off,
+ music_on,
+ set_loop,
+)
+from SprotifyMusic.utils.decorators.language import languageCB
+from SprotifyMusic.utils.formatters import seconds_to_min
+from SprotifyMusic.utils.inline import close_markup, stream_markup, stream_markup_timer
+from SprotifyMusic.utils.stream.autoclear import auto_clean
+from SprotifyMusic.utils.thumbnails import get_thumb
+from config import (
+ BANNED_USERS,
+ SOUNCLOUD_IMG_URL,
+ STREAM_IMG_URL,
+ TELEGRAM_AUDIO_URL,
+ TELEGRAM_VIDEO_URL,
+ adminlist,
+ confirmer,
+ votemode,
+)
+from strings import get_string
+
+checker = {}
+upvoters = {}
+
+
+@app.on_callback_query(filters.regex("ADMIN") & ~BANNED_USERS)
+@languageCB
+async def del_back_playlist(client, CallbackQuery, _):
+ callback_data = CallbackQuery.data.strip()
+ callback_request = callback_data.split(None, 1)[1]
+ command, chat = callback_request.split("|")
+ if "_" in str(chat):
+ bet = chat.split("_")
+ chat = bet[0]
+ counter = bet[1]
+ chat_id = int(chat)
+ if not await is_active_chat(chat_id):
+ return await CallbackQuery.answer(_["general_5"], show_alert=True)
+ mention = CallbackQuery.from_user.mention
+ if command == "UpVote":
+ if chat_id not in votemode:
+ votemode[chat_id] = {}
+ if chat_id not in upvoters:
+ upvoters[chat_id] = {}
+
+ voters = (upvoters[chat_id]).get(CallbackQuery.message.id)
+ if not voters:
+ upvoters[chat_id][CallbackQuery.message.id] = []
+
+ vote = (votemode[chat_id]).get(CallbackQuery.message.id)
+ if not vote:
+ votemode[chat_id][CallbackQuery.message.id] = 0
+
+ if CallbackQuery.from_user.id in upvoters[chat_id][CallbackQuery.message.id]:
+ (upvoters[chat_id][CallbackQuery.message.id]).remove(
+ CallbackQuery.from_user.id
+ )
+ votemode[chat_id][CallbackQuery.message.id] -= 1
+ else:
+ (upvoters[chat_id][CallbackQuery.message.id]).append(
+ CallbackQuery.from_user.id
+ )
+ votemode[chat_id][CallbackQuery.message.id] += 1
+ upvote = await get_upvote_count(chat_id)
+ get_upvotes = int(votemode[chat_id][CallbackQuery.message.id])
+ if get_upvotes >= upvote:
+ votemode[chat_id][CallbackQuery.message.id] = upvote
+ try:
+ exists = confirmer[chat_id][CallbackQuery.message.id]
+ current = db[chat_id][0]
+ except:
+ return await CallbackQuery.edit_message_text(f"ғᴀɪʟᴇᴅ.")
+ try:
+ if current["vidid"] != exists["vidid"]:
+ return await CallbackQuery.edit_message.text(_["admin_35"])
+ if current["file"] != exists["file"]:
+ return await CallbackQuery.edit_message.text(_["admin_35"])
+ except:
+ return await CallbackQuery.edit_message_text(_["admin_36"])
+ try:
+ await CallbackQuery.edit_message_text(_["admin_37"].format(upvote))
+ except:
+ pass
+ command = counter
+ mention = "ᴜᴘᴠᴏᴛᴇs"
+ else:
+ if (
+ CallbackQuery.from_user.id
+ in upvoters[chat_id][CallbackQuery.message.id]
+ ):
+ await CallbackQuery.answer(_["admin_38"], show_alert=True)
+ else:
+ await CallbackQuery.answer(_["admin_39"], show_alert=True)
+ upl = InlineKeyboardMarkup(
+ [
+ [
+ InlineKeyboardButton(
+ text=f"👍 {get_upvotes}",
+ callback_data=f"ADMIN UpVote|{chat_id}_{counter}",
+ )
+ ]
+ ]
+ )
+ await CallbackQuery.answer(_["admin_40"], show_alert=True)
+ return await CallbackQuery.edit_message_reply_markup(reply_markup=upl)
+ else:
+ is_non_admin = await is_nonadmin_chat(CallbackQuery.message.chat.id)
+ if not is_non_admin:
+ if CallbackQuery.from_user.id not in SUDOERS:
+ admins = adminlist.get(CallbackQuery.message.chat.id)
+ if not admins:
+ return await CallbackQuery.answer(_["admin_13"], show_alert=True)
+ else:
+ if CallbackQuery.from_user.id not in admins:
+ return await CallbackQuery.answer(
+ _["admin_14"], show_alert=True
+ )
+ if command == "Pause":
+ if not await is_music_playing(chat_id):
+ return await CallbackQuery.answer(_["admin_1"], show_alert=True)
+ await CallbackQuery.answer()
+ await music_off(chat_id)
+ await Sprotify.pause_stream(chat_id)
+ await CallbackQuery.message.reply_text(
+ _["admin_2"].format(mention), reply_markup=close_markup(_)
+ )
+ elif command == "Resume":
+ if await is_music_playing(chat_id):
+ return await CallbackQuery.answer(_["admin_3"], show_alert=True)
+ await CallbackQuery.answer()
+ await music_on(chat_id)
+ await Sprotify.resume_stream(chat_id)
+ await CallbackQuery.message.reply_text(
+ _["admin_4"].format(mention), reply_markup=close_markup(_)
+ )
+ elif command == "Stop" or command == "End":
+ await CallbackQuery.answer()
+ await Sprotify.stop_stream(chat_id)
+ await set_loop(chat_id, 0)
+ await CallbackQuery.message.reply_text(
+ _["admin_5"].format(mention), reply_markup=close_markup(_)
+ )
+ await CallbackQuery.message.delete()
+ elif command == "Skip" or command == "Replay":
+ check = db.get(chat_id)
+ if command == "Skip":
+ txt = f"❖ sᴛʀᴇᴀᴍ sᴋɪᴩᴩᴇᴅ\n│ \n└ʙʏ ➥ {mention}"
+ popped = None
+ try:
+ popped = check.pop(0)
+ if popped:
+ await auto_clean(popped)
+ if not check:
+ await CallbackQuery.edit_message_text(
+ f"❖ sᴛʀᴇᴀᴍ sᴋɪᴩᴩᴇᴅ\n│ \n└ʙʏ ➥ {mention}"
+ )
+ await CallbackQuery.message.reply_text(
+ text=_["admin_6"].format(
+ mention, CallbackQuery.message.chat.title
+ ),
+ reply_markup=close_markup(_),
+ )
+ try:
+ return await Sprotify.stop_stream(chat_id)
+ except:
+ return
+ except:
+ try:
+ await CallbackQuery.edit_message_text(
+ f"❖ sᴛʀᴇᴀᴍ sᴋɪᴩᴩᴇᴅ\n│ \n└ʙʏ ➥ {mention}"
+ )
+ await CallbackQuery.message.reply_text(
+ text=_["admin_6"].format(
+ mention, CallbackQuery.message.chat.title
+ ),
+ reply_markup=close_markup(_),
+ )
+ return await Sprotify.stop_stream(chat_id)
+ except:
+ return
+ else:
+ txt = f"❖ sᴛʀᴇᴀᴍ ʀᴇ-ᴘʟᴀʏᴇᴅ\n│ \n└ʙʏ ➥ {mention}"
+ await CallbackQuery.answer()
+ queued = check[0]["file"]
+ title = (check[0]["title"]).title()
+ user = check[0]["by"]
+ duration = check[0]["dur"]
+ streamtype = check[0]["streamtype"]
+ videoid = check[0]["vidid"]
+ status = True if str(streamtype) == "video" else None
+ db[chat_id][0]["played"] = 0
+ exis = (check[0]).get("old_dur")
+ if exis:
+ db[chat_id][0]["dur"] = exis
+ db[chat_id][0]["seconds"] = check[0]["old_second"]
+ db[chat_id][0]["speed_path"] = None
+ db[chat_id][0]["speed"] = 1.0
+ if "live_" in queued:
+ n, link = await YouTube.video(videoid, True)
+ if n == 0:
+ return await CallbackQuery.message.reply_text(
+ text=_["admin_7"].format(title),
+ reply_markup=close_markup(_),
+ )
+ try:
+ image = await YouTube.thumbnail(videoid, True)
+ except:
+ image = None
+ try:
+ await Sprotify.skip_stream(chat_id, link, video=status, image=image)
+ except:
+ return await CallbackQuery.message.reply_text(_["call_6"])
+ button = stream_markup(_, chat_id)
+ img = await get_thumb(videoid)
+ run = await CallbackQuery.message.reply_photo(
+ photo=img,
+ caption=_["stream_1"].format(
+ f"https://t.me/{app.username}?start=info_{videoid}",
+ title[:23],
+ duration,
+ user,
+ ),
+ reply_markup=InlineKeyboardMarkup(button),
+ )
+ db[chat_id][0]["mystic"] = run
+ db[chat_id][0]["markup"] = "tg"
+ await CallbackQuery.edit_message_text(txt, reply_markup=close_markup(_))
+ elif "vid_" in queued:
+ mystic = await CallbackQuery.message.reply_text(
+ _["call_7"], disable_web_page_preview=True
+ )
+ try:
+ file_path, direct = await YouTube.download(
+ videoid,
+ mystic,
+ videoid=True,
+ video=status,
+ )
+ except:
+ return await mystic.edit_text(_["call_6"])
+ try:
+ image = await YouTube.thumbnail(videoid, True)
+ except:
+ image = None
+ try:
+ await Sprotify.skip_stream(chat_id, file_path, video=status, image=image)
+ except:
+ return await mystic.edit_text(_["call_6"])
+ button = stream_markup(_, chat_id)
+ img = await get_thumb(videoid)
+ run = await CallbackQuery.message.reply_photo(
+ photo=img,
+ caption=_["stream_1"].format(
+ f"https://t.me/{app.username}?start=info_{videoid}",
+ title[:23],
+ duration,
+ user,
+ ),
+ reply_markup=InlineKeyboardMarkup(button),
+ )
+ db[chat_id][0]["mystic"] = run
+ db[chat_id][0]["markup"] = "stream"
+ await CallbackQuery.edit_message_text(txt, reply_markup=close_markup(_))
+ await mystic.delete()
+ elif "index_" in queued:
+ try:
+ await Sprotify.skip_stream(chat_id, videoid, video=status)
+ except:
+ return await CallbackQuery.message.reply_text(_["call_6"])
+ button = stream_markup(_, chat_id)
+ run = await CallbackQuery.message.reply_photo(
+ photo=STREAM_IMG_URL,
+ caption=_["stream_2"].format(user),
+ reply_markup=InlineKeyboardMarkup(button),
+ )
+ db[chat_id][0]["mystic"] = run
+ db[chat_id][0]["markup"] = "tg"
+ await CallbackQuery.edit_message_text(txt, reply_markup=close_markup(_))
+ else:
+ if videoid == "telegram":
+ image = None
+ elif videoid == "soundcloud":
+ image = None
+ else:
+ try:
+ image = await YouTube.thumbnail(videoid, True)
+ except:
+ image = None
+ try:
+ await Sprotify.skip_stream(chat_id, queued, video=status, image=image)
+ except:
+ return await CallbackQuery.message.reply_text(_["call_6"])
+ if videoid == "telegram":
+ button = stream_markup(_, chat_id)
+ run = await CallbackQuery.message.reply_photo(
+ photo=TELEGRAM_AUDIO_URL
+ if str(streamtype) == "audio"
+ else TELEGRAM_VIDEO_URL,
+ caption=_["stream_1"].format(
+ config.SUPPORT_CHAT, title[:23], duration, user
+ ),
+ reply_markup=InlineKeyboardMarkup(button),
+ )
+ db[chat_id][0]["mystic"] = run
+ db[chat_id][0]["markup"] = "tg"
+ elif videoid == "soundcloud":
+ button = stream_markup(_, chat_id)
+ run = await CallbackQuery.message.reply_photo(
+ photo=SOUNCLOUD_IMG_URL
+ if str(streamtype) == "audio"
+ else TELEGRAM_VIDEO_URL,
+ caption=_["stream_1"].format(
+ config.SUPPORT_CHAT, title[:23], duration, user
+ ),
+ reply_markup=InlineKeyboardMarkup(button),
+ )
+ db[chat_id][0]["mystic"] = run
+ db[chat_id][0]["markup"] = "tg"
+ else:
+ button = stream_markup(_, chat_id)
+ img = await get_thumb(videoid)
+ run = await CallbackQuery.message.reply_photo(
+ photo=img,
+ caption=_["stream_1"].format(
+ f"https://t.me/{app.username}?start=info_{videoid}",
+ title[:23],
+ duration,
+ user,
+ ),
+ reply_markup=InlineKeyboardMarkup(button),
+ )
+ db[chat_id][0]["mystic"] = run
+ db[chat_id][0]["markup"] = "stream"
+ await CallbackQuery.edit_message_text(txt, reply_markup=close_markup(_))
+
+
+async def markup_timer():
+ while not await asyncio.sleep(7):
+ active_chats = await get_active_chats()
+ for chat_id in active_chats:
+ try:
+ if not await is_music_playing(chat_id):
+ continue
+ playing = db.get(chat_id)
+ if not playing:
+ continue
+ duration_seconds = int(playing[0]["seconds"])
+ if duration_seconds == 0:
+ continue
+ try:
+ mystic = playing[0]["mystic"]
+ except:
+ continue
+ try:
+ check = checker[chat_id][mystic.id]
+ if check is False:
+ continue
+ except:
+ pass
+ try:
+ language = await get_lang(chat_id)
+ _ = get_string(language)
+ except:
+ _ = get_string("en")
+ try:
+ buttons = stream_markup_timer(
+ _,
+ chat_id,
+ seconds_to_min(playing[0]["played"]),
+ playing[0]["dur"],
+ )
+ await mystic.edit_reply_markup(
+ reply_markup=InlineKeyboardMarkup(buttons)
+ )
+ except:
+ continue
+ except:
+ continue
+
+
+asyncio.create_task(markup_timer())
diff --git a/SprotifyMusic/plugins/admins/loop.py b/SprotifyMusic/plugins/admins/loop.py
new file mode 100644
index 000000000000..27f0908acc5e
--- /dev/null
+++ b/SprotifyMusic/plugins/admins/loop.py
@@ -0,0 +1,46 @@
+from pyrogram import filters
+from pyrogram.types import Message
+
+from SprotifyMusic import app
+from SprotifyMusic.utils.database import get_loop, set_loop
+from SprotifyMusic.utils.decorators import AdminRightsCheck
+from SprotifyMusic.utils.inline import close_markup
+from config import BANNED_USERS
+
+
+@app.on_message(filters.command(["loop", "cloop"]) & filters.group & ~BANNED_USERS)
+@AdminRightsCheck
+async def admins(cli, message: Message, _, chat_id):
+ usage = _["admin_17"]
+ if len(message.command) != 2:
+ return await message.reply_text(usage)
+ state = message.text.split(None, 1)[1].strip()
+ if state.isnumeric():
+ state = int(state)
+ if 1 <= state <= 10:
+ got = await get_loop(chat_id)
+ if got != 0:
+ state = got + state
+ if int(state) > 10:
+ state = 10
+ await set_loop(chat_id, state)
+ return await message.reply_text(
+ text=_["admin_18"].format(state, message.from_user.mention),
+ reply_markup=close_markup(_),
+ )
+ else:
+ return await message.reply_text(_["admin_17"])
+ elif state.lower() == "enable":
+ await set_loop(chat_id, 10)
+ return await message.reply_text(
+ text=_["admin_18"].format(state, message.from_user.mention),
+ reply_markup=close_markup(_),
+ )
+ elif state.lower() == "disable":
+ await set_loop(chat_id, 0)
+ return await message.reply_text(
+ _["admin_19"].format(message.from_user.mention),
+ reply_markup=close_markup(_),
+ )
+ else:
+ return await message.reply_text(usage)
diff --git a/SprotifyMusic/plugins/admins/pause.py b/SprotifyMusic/plugins/admins/pause.py
new file mode 100644
index 000000000000..b50de7a15134
--- /dev/null
+++ b/SprotifyMusic/plugins/admins/pause.py
@@ -0,0 +1,21 @@
+from pyrogram import filters
+from pyrogram.types import Message
+
+from SprotifyMusic import app
+from SprotifyMusic.core.call import Sprotify
+from SprotifyMusic.utils.database import is_music_playing, music_off
+from SprotifyMusic.utils.decorators import AdminRightsCheck
+from SprotifyMusic.utils.inline import close_markup
+from config import BANNED_USERS
+
+
+@app.on_message(filters.command(["pause", "cpause"]) & filters.group & ~BANNED_USERS)
+@AdminRightsCheck
+async def pause_admin(cli, message: Message, _, chat_id):
+ if not await is_music_playing(chat_id):
+ return await message.reply_text(_["admin_1"])
+ await music_off(chat_id)
+ await Sprotify.pause_stream(chat_id)
+ await message.reply_text(
+ _["admin_2"].format(message.from_user.mention), reply_markup=close_markup(_)
+ )
diff --git a/SprotifyMusic/plugins/admins/resume.py b/SprotifyMusic/plugins/admins/resume.py
new file mode 100644
index 000000000000..4f37ee3a271e
--- /dev/null
+++ b/SprotifyMusic/plugins/admins/resume.py
@@ -0,0 +1,21 @@
+from pyrogram import filters
+from pyrogram.types import Message
+
+from SprotifyMusic import app
+from SprotifyMusic.core.call import Sprotify
+from SprotifyMusic.utils.database import is_music_playing, music_on
+from SprotifyMusic.utils.decorators import AdminRightsCheck
+from SprotifyMusic.utils.inline import close_markup
+from config import BANNED_USERS
+
+
+@app.on_message(filters.command(["resume", "cresume"]) & filters.group & ~BANNED_USERS)
+@AdminRightsCheck
+async def resume_com(cli, message: Message, _, chat_id):
+ if await is_music_playing(chat_id):
+ return await message.reply_text(_["admin_3"])
+ await music_on(chat_id)
+ await Sprotify.resume_stream(chat_id)
+ await message.reply_text(
+ _["admin_4"].format(message.from_user.mention), reply_markup=close_markup(_)
+ )
diff --git a/SprotifyMusic/plugins/admins/seek.py b/SprotifyMusic/plugins/admins/seek.py
new file mode 100644
index 000000000000..88cc22826dc4
--- /dev/null
+++ b/SprotifyMusic/plugins/admins/seek.py
@@ -0,0 +1,75 @@
+from pyrogram import filters
+from pyrogram.types import Message
+
+from SprotifyMusic import YouTube, app
+from SprotifyMusic.core.call import Sprotify
+from SprotifyMusic.misc import db
+from SprotifyMusic.utils import AdminRightsCheck, seconds_to_min
+from SprotifyMusic.utils.inline import close_markup
+from config import BANNED_USERS
+
+
+@app.on_message(
+ filters.command(["seek", "cseek", "seekback", "cseekback"])
+ & filters.group
+ & ~BANNED_USERS
+)
+@AdminRightsCheck
+async def seek_comm(cli, message: Message, _, chat_id):
+ if len(message.command) == 1:
+ return await message.reply_text(_["admin_20"])
+ query = message.text.split(None, 1)[1].strip()
+ if not query.isnumeric():
+ return await message.reply_text(_["admin_21"])
+ playing = db.get(chat_id)
+ if not playing:
+ return await message.reply_text(_["queue_2"])
+ duration_seconds = int(playing[0]["seconds"])
+ if duration_seconds == 0:
+ return await message.reply_text(_["admin_22"])
+ file_path = playing[0]["file"]
+ duration_played = int(playing[0]["played"])
+ duration_to_skip = int(query)
+ duration = playing[0]["dur"]
+ if message.command[0][-2] == "c":
+ if (duration_played - duration_to_skip) <= 10:
+ return await message.reply_text(
+ text=_["admin_23"].format(seconds_to_min(duration_played), duration),
+ reply_markup=close_markup(_),
+ )
+ to_seek = duration_played - duration_to_skip + 1
+ else:
+ if (duration_seconds - (duration_played + duration_to_skip)) <= 10:
+ return await message.reply_text(
+ text=_["admin_23"].format(seconds_to_min(duration_played), duration),
+ reply_markup=close_markup(_),
+ )
+ to_seek = duration_played + duration_to_skip + 1
+ mystic = await message.reply_text(_["admin_24"])
+ if "vid_" in file_path:
+ n, file_path = await YouTube.video(playing[0]["vidid"], True)
+ if n == 0:
+ return await message.reply_text(_["admin_22"])
+ check = (playing[0]).get("speed_path")
+ if check:
+ file_path = check
+ if "index_" in file_path:
+ file_path = playing[0]["vidid"]
+ try:
+ await Sprotify.seek_stream(
+ chat_id,
+ file_path,
+ seconds_to_min(to_seek),
+ duration,
+ playing[0]["streamtype"],
+ )
+ except:
+ return await mystic.edit_text(_["admin_26"], reply_markup=close_markup(_))
+ if message.command[0][-2] == "c":
+ db[chat_id][0]["played"] -= duration_to_skip
+ else:
+ db[chat_id][0]["played"] += duration_to_skip
+ await mystic.edit_text(
+ text=_["admin_25"].format(seconds_to_min(to_seek), message.from_user.mention),
+ reply_markup=close_markup(_),
+ )
diff --git a/SprotifyMusic/plugins/admins/shuffle.py b/SprotifyMusic/plugins/admins/shuffle.py
new file mode 100644
index 000000000000..6b24c85dd536
--- /dev/null
+++ b/SprotifyMusic/plugins/admins/shuffle.py
@@ -0,0 +1,33 @@
+import random
+
+from pyrogram import filters
+from pyrogram.types import Message
+
+from SprotifyMusic import app
+from SprotifyMusic.misc import db
+from SprotifyMusic.utils.decorators import AdminRightsCheck
+from SprotifyMusic.utils.inline import close_markup
+from config import BANNED_USERS
+
+
+@app.on_message(
+ filters.command(["shuffle", "cshuffle"]) & filters.group & ~BANNED_USERS
+)
+@AdminRightsCheck
+async def admins(Client, message: Message, _, chat_id):
+ check = db.get(chat_id)
+ if not check:
+ return await message.reply_text(_["queue_2"])
+ try:
+ popped = check.pop(0)
+ except:
+ return await message.reply_text(_["admin_15"], reply_markup=close_markup(_))
+ check = db.get(chat_id)
+ if not check:
+ check.insert(0, popped)
+ return await message.reply_text(_["admin_15"], reply_markup=close_markup(_))
+ random.shuffle(check)
+ check.insert(0, popped)
+ await message.reply_text(
+ _["admin_16"].format(message.from_user.mention), reply_markup=close_markup(_)
+ )
diff --git a/SprotifyMusic/plugins/admins/skip.py b/SprotifyMusic/plugins/admins/skip.py
new file mode 100644
index 000000000000..f3a1098186ab
--- /dev/null
+++ b/SprotifyMusic/plugins/admins/skip.py
@@ -0,0 +1,232 @@
+from pyrogram import filters
+from pyrogram.types import InlineKeyboardMarkup, Message
+
+import config
+from SprotifyMusic import YouTube, app
+from SprotifyMusic.core.call import Sprotify
+from SprotifyMusic.misc import db
+from SprotifyMusic.utils.database import get_loop
+from SprotifyMusic.utils.decorators import AdminRightsCheck
+from SprotifyMusic.utils.inline import close_markup, stream_markup
+from SprotifyMusic.utils.stream.autoclear import auto_clean
+from SprotifyMusic.utils.thumbnails import get_thumb
+from config import BANNED_USERS
+
+
+@app.on_message(
+ filters.command(["skip", "cskip", "next", "cnext"]) & filters.group & ~BANNED_USERS
+)
+@AdminRightsCheck
+async def skip(cli, message: Message, _, chat_id):
+ if not len(message.command) < 2:
+ loop = await get_loop(chat_id)
+ if loop != 0:
+ return await message.reply_text(_["admin_8"])
+ state = message.text.split(None, 1)[1].strip()
+ if state.isnumeric():
+ state = int(state)
+ check = db.get(chat_id)
+ if check:
+ count = len(check)
+ if count > 2:
+ count = int(count - 1)
+ if 1 <= state <= count:
+ for x in range(state):
+ popped = None
+ try:
+ popped = check.pop(0)
+ except:
+ return await message.reply_text(_["admin_12"])
+ if popped:
+ await auto_clean(popped)
+ if not check:
+ try:
+ await message.reply_text(
+ text=_["admin_6"].format(
+ message.from_user.mention,
+ message.chat.title,
+ ),
+ reply_markup=close_markup(_),
+ )
+ await Sprotify.stop_stream(chat_id)
+ except:
+ return
+ break
+ else:
+ return await message.reply_text(_["admin_11"].format(count))
+ else:
+ return await message.reply_text(_["admin_10"])
+ else:
+ return await message.reply_text(_["queue_2"])
+ else:
+ return await message.reply_text(_["admin_9"])
+ else:
+ check = db.get(chat_id)
+ popped = None
+ try:
+ popped = check.pop(0)
+ if popped:
+ await auto_clean(popped)
+ if not check:
+ await message.reply_text(
+ text=_["admin_6"].format(
+ message.from_user.mention, message.chat.title
+ ),
+ reply_markup=close_markup(_),
+ )
+ try:
+ return await Sprotify.stop_stream(chat_id)
+ except:
+ return
+ except:
+ try:
+ await message.reply_text(
+ text=_["admin_6"].format(
+ message.from_user.mention, message.chat.title
+ ),
+ reply_markup=close_markup(_),
+ )
+ return await Sprotify.stop_stream(chat_id)
+ except:
+ return
+ queued = check[0]["file"]
+ title = (check[0]["title"]).title()
+ user = check[0]["by"]
+ streamtype = check[0]["streamtype"]
+ videoid = check[0]["vidid"]
+ status = True if str(streamtype) == "video" else None
+ db[chat_id][0]["played"] = 0
+ exis = (check[0]).get("old_dur")
+ if exis:
+ db[chat_id][0]["dur"] = exis
+ db[chat_id][0]["seconds"] = check[0]["old_second"]
+ db[chat_id][0]["speed_path"] = None
+ db[chat_id][0]["speed"] = 1.0
+ if "live_" in queued:
+ n, link = await YouTube.video(videoid, True)
+ if n == 0:
+ return await message.reply_text(_["admin_7"].format(title))
+ try:
+ image = await YouTube.thumbnail(videoid, True)
+ except:
+ image = None
+ try:
+ await Sprotify.skip_stream(chat_id, link, video=status, image=image)
+ except:
+ return await message.reply_text(_["call_6"])
+ button = stream_markup(_, chat_id)
+ img = await get_thumb(videoid)
+ run = await message.reply_photo(
+ photo=img,
+ caption=_["stream_1"].format(
+ f"https://t.me/{app.username}?start=info_{videoid}",
+ title[:23],
+ check[0]["dur"],
+ user,
+ ),
+ reply_markup=InlineKeyboardMarkup(button),
+ )
+ db[chat_id][0]["mystic"] = run
+ db[chat_id][0]["markup"] = "tg"
+ elif "vid_" in queued:
+ mystic = await message.reply_text(_["call_7"], disable_web_page_preview=True)
+ try:
+ file_path, direct = await YouTube.download(
+ videoid,
+ mystic,
+ videoid=True,
+ video=status,
+ )
+ except:
+ return await mystic.edit_text(_["call_6"])
+ try:
+ image = await YouTube.thumbnail(videoid, True)
+ except:
+ image = None
+ try:
+ await Sprotify.skip_stream(chat_id, file_path, video=status, image=image)
+ except:
+ return await mystic.edit_text(_["call_6"])
+ button = stream_markup(_, chat_id)
+ img = await get_thumb(videoid)
+ run = await message.reply_photo(
+ photo=img,
+ caption=_["stream_1"].format(
+ f"https://t.me/{app.username}?start=info_{videoid}",
+ title[:23],
+ check[0]["dur"],
+ user,
+ ),
+ reply_markup=InlineKeyboardMarkup(button),
+ )
+ db[chat_id][0]["mystic"] = run
+ db[chat_id][0]["markup"] = "stream"
+ await mystic.delete()
+ elif "index_" in queued:
+ try:
+ await Sprotify.skip_stream(chat_id, videoid, video=status)
+ except:
+ return await message.reply_text(_["call_6"])
+ button = stream_markup(_, chat_id)
+ run = await message.reply_photo(
+ photo=config.STREAM_IMG_URL,
+ caption=_["stream_2"].format(user),
+ reply_markup=InlineKeyboardMarkup(button),
+ )
+ db[chat_id][0]["mystic"] = run
+ db[chat_id][0]["markup"] = "tg"
+ else:
+ if videoid == "telegram":
+ image = None
+ elif videoid == "soundcloud":
+ image = None
+ else:
+ try:
+ image = await YouTube.thumbnail(videoid, True)
+ except:
+ image = None
+ try:
+ await Sprotify.skip_stream(chat_id, queued, video=status, image=image)
+ except:
+ return await message.reply_text(_["call_6"])
+ if videoid == "telegram":
+ button = stream_markup(_, chat_id)
+ run = await message.reply_photo(
+ photo=config.TELEGRAM_AUDIO_URL
+ if str(streamtype) == "audio"
+ else config.TELEGRAM_VIDEO_URL,
+ caption=_["stream_1"].format(
+ config.SUPPORT_CHAT, title[:23], check[0]["dur"], user
+ ),
+ reply_markup=InlineKeyboardMarkup(button),
+ )
+ db[chat_id][0]["mystic"] = run
+ db[chat_id][0]["markup"] = "tg"
+ elif videoid == "soundcloud":
+ button = stream_markup(_, chat_id)
+ run = await message.reply_photo(
+ photo=config.SOUNCLOUD_IMG_URL
+ if str(streamtype) == "audio"
+ else config.TELEGRAM_VIDEO_URL,
+ caption=_["stream_1"].format(
+ config.SUPPORT_CHAT, title[:23], check[0]["dur"], user
+ ),
+ reply_markup=InlineKeyboardMarkup(button),
+ )
+ db[chat_id][0]["mystic"] = run
+ db[chat_id][0]["markup"] = "tg"
+ else:
+ button = stream_markup(_, chat_id)
+ img = await get_thumb(videoid)
+ run = await message.reply_photo(
+ photo=img,
+ caption=_["stream_1"].format(
+ f"https://t.me/{app.username}?start=info_{videoid}",
+ title[:23],
+ check[0]["dur"],
+ user,
+ ),
+ reply_markup=InlineKeyboardMarkup(button),
+ )
+ db[chat_id][0]["mystic"] = run
+ db[chat_id][0]["markup"] = "stream"
diff --git a/SprotifyMusic/plugins/admins/speed.py b/SprotifyMusic/plugins/admins/speed.py
new file mode 100644
index 000000000000..163068cb64b1
--- /dev/null
+++ b/SprotifyMusic/plugins/admins/speed.py
@@ -0,0 +1,112 @@
+from pyrogram import filters
+from pyrogram.types import Message
+
+from SprotifyMusic import app
+from SprotifyMusic.core.call import Sprotify
+from SprotifyMusic.misc import SUDOERS, db
+from SprotifyMusic.utils import AdminRightsCheck
+from SprotifyMusic.utils.database import is_active_chat, is_nonadmin_chat
+from SprotifyMusic.utils.decorators.language import languageCB
+from SprotifyMusic.utils.inline import close_markup, speed_markup
+from config import BANNED_USERS, adminlist
+
+checker = []
+
+
+@app.on_message(
+ filters.command(["cspeed", "speed", "cslow", "slow", "playback", "cplayback"])
+ & filters.group
+ & ~BANNED_USERS
+)
+@AdminRightsCheck
+async def playback(cli, message: Message, _, chat_id):
+ playing = db.get(chat_id)
+ if not playing:
+ return await message.reply_text(_["queue_2"])
+ duration_seconds = int(playing[0]["seconds"])
+ if duration_seconds == 0:
+ return await message.reply_text(_["admin_27"])
+ file_path = playing[0]["file"]
+ if "downloads" not in file_path:
+ return await message.reply_text(_["admin_27"])
+ upl = speed_markup(_, chat_id)
+ return await message.reply_text(
+ text=_["admin_28"].format(app.mention),
+ reply_markup=upl,
+ )
+
+
+@app.on_callback_query(filters.regex("SpeedUP") & ~BANNED_USERS)
+@languageCB
+async def del_back_playlist(client, CallbackQuery, _):
+ callback_data = CallbackQuery.data.strip()
+ callback_request = callback_data.split(None, 1)[1]
+ chat, speed = callback_request.split("|")
+ chat_id = int(chat)
+ if not await is_active_chat(chat_id):
+ return await CallbackQuery.answer(_["general_5"], show_alert=True)
+ is_non_admin = await is_nonadmin_chat(CallbackQuery.message.chat.id)
+ if not is_non_admin:
+ if CallbackQuery.from_user.id not in SUDOERS:
+ admins = adminlist.get(CallbackQuery.message.chat.id)
+ if not admins:
+ return await CallbackQuery.answer(_["admin_13"], show_alert=True)
+ else:
+ if CallbackQuery.from_user.id not in admins:
+ return await CallbackQuery.answer(_["admin_14"], show_alert=True)
+ playing = db.get(chat_id)
+ if not playing:
+ return await CallbackQuery.answer(_["queue_2"], show_alert=True)
+ duration_seconds = int(playing[0]["seconds"])
+ if duration_seconds == 0:
+ return await CallbackQuery.answer(_["admin_27"], show_alert=True)
+ file_path = playing[0]["file"]
+ if "downloads" not in file_path:
+ return await CallbackQuery.answer(_["admin_27"], show_alert=True)
+ checkspeed = (playing[0]).get("speed")
+ if checkspeed:
+ if str(checkspeed) == str(speed):
+ if str(speed) == str("1.0"):
+ return await CallbackQuery.answer(
+ _["admin_29"],
+ show_alert=True,
+ )
+ else:
+ if str(speed) == str("1.0"):
+ return await CallbackQuery.answer(
+ _["admin_29"],
+ show_alert=True,
+ )
+ if chat_id in checker:
+ return await CallbackQuery.answer(
+ _["admin_30"],
+ show_alert=True,
+ )
+ else:
+ checker.append(chat_id)
+ try:
+ await CallbackQuery.answer(
+ _["admin_31"],
+ )
+ except:
+ pass
+ mystic = await CallbackQuery.edit_message_text(
+ text=_["admin_32"].format(CallbackQuery.from_user.mention),
+ )
+ try:
+ await Sprotify.speedup_stream(
+ chat_id,
+ file_path,
+ speed,
+ playing,
+ )
+ except:
+ if chat_id in checker:
+ checker.remove(chat_id)
+ return await mystic.edit_text(_["admin_33"], reply_markup=close_markup(_))
+ if chat_id in checker:
+ checker.remove(chat_id)
+ await mystic.edit_text(
+ text=_["admin_34"].format(speed, CallbackQuery.from_user.mention),
+ reply_markup=close_markup(_),
+ )
diff --git a/SprotifyMusic/plugins/admins/stop.py b/SprotifyMusic/plugins/admins/stop.py
new file mode 100644
index 000000000000..ed7cc75a6ac4
--- /dev/null
+++ b/SprotifyMusic/plugins/admins/stop.py
@@ -0,0 +1,23 @@
+from pyrogram import filters
+from pyrogram.types import Message
+
+from SprotifyMusic import app
+from SprotifyMusic.core.call import Sprotify
+from SprotifyMusic.utils.database import set_loop
+from SprotifyMusic.utils.decorators import AdminRightsCheck
+from SprotifyMusic.utils.inline import close_markup
+from config import BANNED_USERS
+
+
+@app.on_message(
+ filters.command(["end", "stop", "cend", "cstop"]) & filters.group & ~BANNED_USERS
+)
+@AdminRightsCheck
+async def stop_music(cli, message: Message, _, chat_id):
+ if not len(message.command) == 1:
+ return
+ await Sprotify.stop_stream(chat_id)
+ await set_loop(chat_id, 0)
+ await message.reply_text(
+ _["admin_5"].format(message.from_user.mention), reply_markup=close_markup(_)
+ )
diff --git a/SprotifyMusic/plugins/bot/help.py b/SprotifyMusic/plugins/bot/help.py
new file mode 100644
index 000000000000..8336901d5f3f
--- /dev/null
+++ b/SprotifyMusic/plugins/bot/help.py
@@ -0,0 +1,106 @@
+from typing import Union
+import random
+from pyrogram import filters, types
+from pyrogram.types import InlineKeyboardMarkup, Message
+
+from SprotifyMusic import app
+from SprotifyMusic.utils import help_pannel
+from SprotifyMusic.utils.database import get_lang
+from SprotifyMusic.utils.decorators.language import LanguageStart, languageCB
+from SprotifyMusic.utils.inline.help import help_back_markup, private_help_panel
+from config import BANNED_USERS, START_IMG_URL, SUPPORT_CHAT
+from strings import get_string, helpers
+
+AVISHA = [
+"https://graph.org/file/eaa3a2602e43844a488a5.jpg",
+"https://graph.org/file/b129e98b6e5c4db81c15f.jpg",
+"https://graph.org/file/3ccb86d7d62e8ee0a2e8b.jpg",
+"https://graph.org/file/df11d8257613418142063.jpg",
+"https://graph.org/file/9e23720fedc47259b6195.jpg",
+"https://graph.org/file/826485f2d7db6f09db8ed.jpg",
+"https://graph.org/file/ff3ad786da825b5205691.jpg",
+"https://graph.org/file/52713c9fe9253ae668f13.jpg",
+"https://graph.org/file/8f8516c86677a8c91bfb1.jpg",
+"https://graph.org/file/6603c3740378d3f7187da.jpg",
+"https://graph.org/file/66cb6ec40eea5c4670118.jpg",
+"https://graph.org/file/2e3cf4327b169b981055e.jpg",
+
+]
+
+
+@app.on_message(filters.command(["help"]) & filters.private & ~BANNED_USERS)
+@app.on_callback_query(filters.regex("settings_back_helper") & ~BANNED_USERS)
+async def helper_private(
+ client: app, update: Union[types.Message, types.CallbackQuery]
+):
+ is_callback = isinstance(update, types.CallbackQuery)
+ if is_callback:
+ try:
+ await update.answer()
+ except:
+ pass
+ chat_id = update.message.chat.id
+ language = await get_lang(chat_id)
+ _ = get_string(language)
+ keyboard = help_pannel(_, True)
+ await update.edit_message_text(
+ _["help_1"].format(SUPPORT_CHAT), reply_markup=keyboard
+ )
+ else:
+ try:
+ await update.delete()
+ except:
+ pass
+ language = await get_lang(update.chat.id)
+ _ = get_string(language)
+ keyboard = help_pannel(_)
+ await update.reply_photo(
+ random.choice(AVISHA),
+ caption=_["help_1"].format(SUPPORT_CHAT),
+ reply_markup=keyboard,
+ )
+
+
+@app.on_message(filters.command(["help"]) & filters.group & ~BANNED_USERS)
+@LanguageStart
+async def help_com_group(client, message: Message, _):
+ keyboard = private_help_panel(_)
+ await message.reply_text(_["help_2"], reply_markup=InlineKeyboardMarkup(keyboard))
+
+
+@app.on_callback_query(filters.regex("help_callback") & ~BANNED_USERS)
+@languageCB
+async def helper_cb(client, CallbackQuery, _):
+ callback_data = CallbackQuery.data.strip()
+ cb = callback_data.split(None, 1)[1]
+ keyboard = help_back_markup(_)
+ if cb == "hb1":
+ await CallbackQuery.edit_message_text(helpers.HELP_1, reply_markup=keyboard)
+ elif cb == "hb2":
+ await CallbackQuery.edit_message_text(helpers.HELP_2, reply_markup=keyboard)
+ elif cb == "hb3":
+ await CallbackQuery.edit_message_text(helpers.HELP_3, reply_markup=keyboard)
+ elif cb == "hb4":
+ await CallbackQuery.edit_message_text(helpers.HELP_4, reply_markup=keyboard)
+ elif cb == "hb5":
+ await CallbackQuery.edit_message_text(helpers.HELP_5, reply_markup=keyboard)
+ elif cb == "hb6":
+ await CallbackQuery.edit_message_text(helpers.HELP_6, reply_markup=keyboard)
+ elif cb == "hb7":
+ await CallbackQuery.edit_message_text(helpers.HELP_7, reply_markup=keyboard)
+ elif cb == "hb8":
+ await CallbackQuery.edit_message_text(helpers.HELP_8, reply_markup=keyboard)
+ elif cb == "hb9":
+ await CallbackQuery.edit_message_text(helpers.HELP_9, reply_markup=keyboard)
+ elif cb == "hb10":
+ await CallbackQuery.edit_message_text(helpers.HELP_10, reply_markup=keyboard)
+ elif cb == "hb11":
+ await CallbackQuery.edit_message_text(helpers.HELP_11, reply_markup=keyboard)
+ elif cb == "hb12":
+ await CallbackQuery.edit_message_text(helpers.HELP_12, reply_markup=keyboard)
+ elif cb == "hb13":
+ await CallbackQuery.edit_message_text(helpers.HELP_13, reply_markup=keyboard)
+ elif cb == "hb14":
+ await CallbackQuery.edit_message_text(helpers.HELP_14, reply_markup=keyboard)
+ elif cb == "hb15":
+ await CallbackQuery.edit_message_text(helpers.HELP_15, reply_markup=keyboard)
diff --git a/SprotifyMusic/plugins/bot/inline.py b/SprotifyMusic/plugins/bot/inline.py
new file mode 100644
index 000000000000..984014bee7e5
--- /dev/null
+++ b/SprotifyMusic/plugins/bot/inline.py
@@ -0,0 +1,68 @@
+from pyrogram.types import (
+ InlineKeyboardButton,
+ InlineKeyboardMarkup,
+ InlineQueryResultPhoto,
+)
+from youtubesearchpython.__future__ import VideosSearch
+
+from config import BANNED_USERS
+from SprotifyMusic import app
+from SprotifyMusic.utils.inlinequery import answer
+
+
+@app.on_inline_query(~BANNED_USERS)
+async def inline_query_handler(client, query):
+ text = query.query.strip().lower()
+ answers = []
+ if text.strip() == "":
+ try:
+ await client.answer_inline_query(query.id, results=answer, cache_time=10)
+ except:
+ return
+ else:
+ a = VideosSearch(text, limit=20)
+ result = (await a.next()).get("result")
+ for x in range(15):
+ title = (result[x]["title"]).title()
+ duration = result[x]["duration"]
+ views = result[x]["viewCount"]["short"]
+ thumbnail = result[x]["thumbnails"][0]["url"].split("?")[0]
+ channellink = result[x]["channel"]["link"]
+ channel = result[x]["channel"]["name"]
+ link = result[x]["link"]
+ published = result[x]["publishedTime"]
+ description = f"{views} | {duration} Mins | {channel} | {published}"
+ buttons = InlineKeyboardMarkup(
+ [
+ [
+ InlineKeyboardButton(
+ text="🎥 ᴡᴀᴛᴄʜ ᴏɴ ʏᴏᴜᴛᴜʙᴇ",
+ url=link,
+ )
+ ],
+ ]
+ )
+ searched_text = f"""
+❄ ᴛɪᴛʟᴇ : {title}
+
+⏳ ᴅᴜʀᴀᴛɪᴏɴ : {duration} ᴍɪɴᴜᴛᴇs
+👀 ᴠɪᴇᴡs : {views}
+🎥 ᴄʜᴀɴɴᴇʟ : {channel}
+⏰ ᴘᴜʙʟɪsʜᴇᴅ ᴏɴ : {published}
+
+ʀᴇᴘʟʏ ᴡɪᴛʜ /play ᴏɴ ᴛʜɪs sᴇᴀʀᴄʜᴇᴅ ᴍᴇssᴀɢᴇ ᴛᴏ sᴛʀᴇᴀᴍ ɪᴛ ᴏɴ ᴠᴏɪᴄᴇᴄʜᴀᴛ.
+➻ ɪɴʟɪɴᴇ sᴇᴀʀᴄʜ ᴍᴏᴅᴇ ʙʏ {app.name}"""
+ answers.append(
+ InlineQueryResultPhoto(
+ photo_url=thumbnail,
+ title=title,
+ thumb_url=thumbnail,
+ description=description,
+ caption=searched_text,
+ reply_markup=buttons,
+ )
+ )
+ try:
+ return await client.answer_inline_query(query.id, results=answers)
+ except:
+ return
\ No newline at end of file
diff --git a/SprotifyMusic/plugins/bot/mustjoin.py b/SprotifyMusic/plugins/bot/mustjoin.py
new file mode 100644
index 000000000000..7c9a42613702
--- /dev/null
+++ b/SprotifyMusic/plugins/bot/mustjoin.py
@@ -0,0 +1,60 @@
+import random
+from pyrogram import Client, filters
+from pyrogram.types import InlineKeyboardMarkup, InlineKeyboardButton, Message
+from pyrogram.errors import ChatAdminRequired, UserNotParticipant, ChatWriteForbidden
+from SprotifyMusic import app
+
+IMAGE = [
+ "https://graph.org/file/f86b71018196c5cfe7344.jpg",
+ "https://graph.org/file/a3db9af88f25bb1b99325.jpg",
+ "https://graph.org/file/5b344a55f3d5199b63fa5.jpg",
+ "https://graph.org/file/84de4b440300297a8ecb3.jpg",
+ "https://graph.org/file/84e84ff778b045879d24f.jpg",
+ "https://graph.org/file/a4a8f0e5c0e6b18249ffc.jpg",
+ "https://graph.org/file/ed92cada78099c9c3a4f7.jpg",
+ "https://graph.org/file/d6360613d0fa7a9d2f90b.jpg"
+ "https://graph.org/file/37248e7bdff70c662a702.jpg",
+ "https://graph.org/file/0bfe29d15e918917d1305.jpg",
+ "https://graph.org/file/16b1a2828cc507f8048bd.jpg",
+ "https://graph.org/file/e6b01f23f2871e128dad8.jpg",
+ "https://graph.org/file/cacbdddee77784d9ed2b7.jpg",
+ "https://graph.org/file/ddc5d6ec1c33276507b19.jpg",
+ "https://graph.org/file/39d7277189360d2c85b62.jpg",
+ "https://graph.org/file/5846b9214eaf12c3ed100.jpg",
+ "https://graph.org/file/ad4f9beb4d526e6615e18.jpg",
+ "https://graph.org/file/3514efaabe774e4f181f2.jpg",
+]
+
+#--------------------------
+
+MUST_JOIN = "C0DE_SEARCH"
+#------------------------
+@app.on_message(filters.incoming & filters.private, group=-1)
+async def must_join_channel(app: Client, msg: Message):
+ if not MUST_JOIN:
+ return
+ try:
+ try:
+ await app.get_chat_member(MUST_JOIN, msg.from_user.id)
+ except UserNotParticipant:
+ if MUST_JOIN.isalpha():
+ link = "https://t.me/" + MUST_JOIN
+ else:
+ chat_info = await app.get_chat(MUST_JOIN)
+ link = chat_info.invite_link
+ try:
+ await msg.reply_photo(random.choice(IMAGE), caption=f"❖ ʜᴇʏ ᴛʜᴇʀᴇ, ɴɪᴄᴇ ᴛᴏ ᴍᴇᴇᴛ ᴜʜʜ !\n\n● ɪғ ʏᴏᴜ ᴡᴀɴᴛ ᴛᴏ ᴜsᴇ ˹ ᴛᴀɴᴜ ꭙ ᴍᴜsɪᴄ™ ♡゙, ᴛʜᴇɴ ᴄʟɪᴄᴋ ᴏɴ ᴛʜᴇ ʙᴇʟᴏᴡ ʙᴜᴛᴛᴏɴ ᴀɴᴅ ʏᴏᴜ ᴊᴏɪɴᴇᴅ, ᴛʜᴇɴ ʏᴏᴜ ᴄᴀɴ ᴜsᴇ ᴀʟʟ ᴍʏ ᴄᴏᴍᴍᴀɴᴅs.", + reply_markup=InlineKeyboardMarkup( + [ + [ + InlineKeyboardButton("sᴜᴘᴘᴏʀᴛ", url="https://t.me/AsuraaSupports"), + InlineKeyboardButton("ᴜᴘᴅᴀᴛᴇ", url=link), + ] + ] + ) + ) + await msg.stop_propagation() + except ChatWriteForbidden: + pass + except ChatAdminRequired: + print(f"๏ ᴘʀᴏᴍᴏᴛᴇ ᴍᴇ ᴀs ᴀɴ ᴀᴅᴍɪɴ ɪɴ ᴛʜᴇ ᴍᴜsᴛ_ᴊᴏɪɴ ᴄʜᴀᴛ ➥ {MUST_JOIN}") diff --git a/SprotifyMusic/plugins/bot/privacy.py b/SprotifyMusic/plugins/bot/privacy.py new file mode 100644 index 000000000000..cafb936d9317 --- /dev/null +++ b/SprotifyMusic/plugins/bot/privacy.py @@ -0,0 +1,27 @@ +from pyrogram import filters +from pyrogram.enums import ParseMode +from pyrogram.types import InlineKeyboardButton, InlineKeyboardMarkup, Message + +import config +from SprotifyMusic import app +from config import SUPPORT_CHAT + +@app.on_message(filters.command("privacy")) +async def privacy(client, message: Message): + keyboard = InlineKeyboardMarkup( + [[InlineKeyboardButton("ᴠɪᴇᴡ ᴘʀɪᴠᴀᴄʏ ᴘᴏʟɪᴄʏ", url=config.PRIVACY_LINK)]] + ) + TEXT = f"""** +🔒 Privacy Policy for {client.me.mention}.** + +Your privacy is important to us. To learn more about how we collect, use, and protect your data, please review our Privacy Policy here: [Privacy Policy]({config.PRIVACY_LINK}). + +If you have any questions or concerns, feel free to reach out to our [Support Team]({config.SUPPORT_CHAT}). + """ + + await message.reply_text( + TEXT, + reply_markup=keyboard, + parse_mode=ParseMode.MARKDOWN, + disable_web_page_preview=True, + ) \ No newline at end of file diff --git a/SprotifyMusic/plugins/bot/repo.py b/SprotifyMusic/plugins/bot/repo.py new file mode 100644 index 000000000000..94ba4c5655b8 --- /dev/null +++ b/SprotifyMusic/plugins/bot/repo.py @@ -0,0 +1,28 @@ +from pyrogram import filters +from pyrogram.types import InlineKeyboardButton, InlineKeyboardMarkup +from SprotifyMusic import app +from config import BOT_USERNAME + +start_txt = """ +❖ ʜᴇʏ , ᴛʜᴇʀᴇ ɴɪᴄᴇ ᴛᴏ ᴍᴇᴇᴛ ʏᴏᴜ ♥︎\n\n● ɪғ ʏᴏᴜ ᴡᴀɴᴛ ˹ ᴛᴀɴᴜ ꭙ ᴍᴜsɪᴄ™ ♡゙゙, ʙᴏᴛ ʀᴇᴘᴏ ᴛʜᴇɴ ᴄʟɪᴄᴋ ᴏɴ ᴛʜᴇ sᴏᴜʀᴄᴇ ᴄᴏᴅᴇ ʙᴜᴛᴛᴏɴ ᴛᴏ ᴄᴏʟʟᴇᴄᴛ ᴍʏ sᴏᴜʀᴄᴇ ᴄᴏᴅᴇ.\n\n❖ ᴘᴏᴡᴇʀᴇᴅ ʙʏ ➥ ˹ ᴛᴀɴᴜ ꭙ ᴍᴜsɪᴄ™ ♡゙""" + + + + +@app.on_message(filters.command("repo")) +async def start(_, msg): + buttons = [ + [ + InlineKeyboardButton("Support", url="https://t.me/SprotifyNews"), + InlineKeyboardButton("Repo", url="https://github.com/Mister-Man7/SprotifyXMusic") + ], + ] + + reply_markup = InlineKeyboardMarkup(buttons) + + await msg.reply_photo( + photo="https://envs.sh/7a2.jpg", + caption=start_txt, + reply_markup=reply_markup + ) + diff --git a/SprotifyMusic/plugins/bot/settings.py b/SprotifyMusic/plugins/bot/settings.py new file mode 100644 index 000000000000..0e8f578a9d16 --- /dev/null +++ b/SprotifyMusic/plugins/bot/settings.py @@ -0,0 +1,391 @@ +from pyrogram import filters +from pyrogram.enums import ChatType +from pyrogram.errors import MessageNotModified +from pyrogram.types import ( + CallbackQuery, + InlineKeyboardButton, + InlineKeyboardMarkup, + Message, +) + +from SprotifyMusic import app +from SprotifyMusic.utils.database import ( + add_nonadmin_chat, + get_authuser, + get_authuser_names, + get_playmode, + get_playtype, + get_upvote_count, + is_nonadmin_chat, + is_skipmode, + remove_nonadmin_chat, + set_playmode, + set_playtype, + set_upvotes, + skip_off, + skip_on, +) +from SprotifyMusic.utils.decorators.admins import ActualAdminCB +from SprotifyMusic.utils.decorators.language import language, languageCB +from SprotifyMusic.utils.inline.settings import ( + auth_users_markup, + playmode_users_markup, + setting_markup, + vote_mode_markup, +) +from SprotifyMusic.utils.inline.start import private_panel +from config import BANNED_USERS, OWNER_ID + + +@app.on_message( + filters.command(["settings"]) & filters.group & ~BANNED_USERS +) +@language +async def settings_mar(client, message: Message, _): + buttons = setting_markup(_) + await message.reply_text( + _["setting_1"].format(app.mention, message.chat.id, message.chat.title), + reply_markup=InlineKeyboardMarkup(buttons), + ) + + +@app.on_callback_query(filters.regex("settings_helper") & ~BANNED_USERS) +@languageCB +async def settings_cb(client, CallbackQuery, _): + try: + await CallbackQuery.answer(_["set_cb_5"]) + except: + pass + buttons = setting_markup(_) + return await CallbackQuery.edit_message_text( + _["setting_1"].format( + app.mention, + CallbackQuery.message.chat.id, + CallbackQuery.message.chat.title, + ), + reply_markup=InlineKeyboardMarkup(buttons), + ) + + +@app.on_callback_query(filters.regex("settingsback_helper") & ~BANNED_USERS) +@languageCB +async def settings_back_markup(client, CallbackQuery: CallbackQuery, _): + try: + await CallbackQuery.answer() + except: + pass + if CallbackQuery.message.chat.type == ChatType.PRIVATE: + await app.resolve_peer(OWNER_ID) + OWNER = OWNER_ID + buttons = private_panel(_) + return await CallbackQuery.edit_message_text( + _["start_2"].format(CallbackQuery.from_user.mention, app.mention), + reply_markup=InlineKeyboardMarkup(buttons), + ) + else: + buttons = setting_markup(_) + return await CallbackQuery.edit_message_reply_markup( + reply_markup=InlineKeyboardMarkup(buttons) + ) + + +@app.on_callback_query( + filters.regex( + pattern=r"^(SEARCHANSWER|PLAYMODEANSWER|PLAYTYPEANSWER|AUTHANSWER|ANSWERVOMODE|VOTEANSWER|PM|AU|VM)$" + ) + & ~BANNED_USERS +) +@languageCB +async def without_Admin_rights(client, CallbackQuery, _): + command = CallbackQuery.matches[0].group(1) + if command == "SEARCHANSWER": + try: + return await CallbackQuery.answer(_["setting_2"], show_alert=True) + except: + return + if command == "PLAYMODEANSWER": + try: + return await CallbackQuery.answer(_["setting_5"], show_alert=True) + except: + return + if command == "PLAYTYPEANSWER": + try: + return await CallbackQuery.answer(_["setting_6"], show_alert=True) + except: + return + if command == "AUTHANSWER": + try: + return await CallbackQuery.answer(_["setting_3"], show_alert=True) + except: + return + if command == "VOTEANSWER": + try: + return await CallbackQuery.answer( + _["setting_8"], + show_alert=True, + ) + except: + return + if command == "ANSWERVOMODE": + current = await get_upvote_count(CallbackQuery.message.chat.id) + try: + return await CallbackQuery.answer( + _["setting_9"].format(current), + show_alert=True, + ) + except: + return + if command == "PM": + try: + await CallbackQuery.answer(_["set_cb_2"], show_alert=True) + except: + pass + playmode = await get_playmode(CallbackQuery.message.chat.id) + if playmode == "Direct": + Direct = True + else: + Direct = None + is_non_admin = await is_nonadmin_chat(CallbackQuery.message.chat.id) + if not is_non_admin: + Group = True + else: + Group = None + playty = await get_playtype(CallbackQuery.message.chat.id) + if playty == "Everyone": + Playtype = None + else: + Playtype = True + buttons = playmode_users_markup(_, Direct, Group, Playtype) + if command == "AU": + try: + await CallbackQuery.answer(_["set_cb_1"], show_alert=True) + except: + pass + is_non_admin = await is_nonadmin_chat(CallbackQuery.message.chat.id) + if not is_non_admin: + buttons = auth_users_markup(_, True) + else: + buttons = auth_users_markup(_) + if command == "VM": + mode = await is_skipmode(CallbackQuery.message.chat.id) + current = await get_upvote_count(CallbackQuery.message.chat.id) + buttons = vote_mode_markup(_, current, mode) + try: + return await CallbackQuery.edit_message_reply_markup( + reply_markup=InlineKeyboardMarkup(buttons) + ) + except MessageNotModified: + return + + +@app.on_callback_query(filters.regex("FERRARIUDTI") & ~BANNED_USERS) +@ActualAdminCB +async def addition(client, CallbackQuery, _): + callback_data = CallbackQuery.data.strip() + mode = callback_data.split(None, 1)[1] + if not await is_skipmode(CallbackQuery.message.chat.id): + return await CallbackQuery.answer(_["setting_10"], show_alert=True) + current = await get_upvote_count(CallbackQuery.message.chat.id) + if mode == "M": + final = current - 2 + print(final) + if final == 0: + return await CallbackQuery.answer( + _["setting_11"], + show_alert=True, + ) + if final <= 2: + final = 2 + await set_upvotes(CallbackQuery.message.chat.id, final) + else: + final = current + 2 + print(final) + if final == 17: + return await CallbackQuery.answer( + _["setting_12"], + show_alert=True, + ) + if final >= 15: + final = 15 + await set_upvotes(CallbackQuery.message.chat.id, final) + buttons = vote_mode_markup(_, final, True) + try: + return await CallbackQuery.edit_message_reply_markup( + reply_markup=InlineKeyboardMarkup(buttons) + ) + except MessageNotModified: + return + + +@app.on_callback_query( + filters.regex(pattern=r"^(MODECHANGE|CHANNELMODECHANGE|PLAYTYPECHANGE)$") + & ~BANNED_USERS +) +@ActualAdminCB +async def playmode_ans(client, CallbackQuery, _): + command = CallbackQuery.matches[0].group(1) + if command == "CHANNELMODECHANGE": + is_non_admin = await is_nonadmin_chat(CallbackQuery.message.chat.id) + if not is_non_admin: + await add_nonadmin_chat(CallbackQuery.message.chat.id) + Group = None + else: + await remove_nonadmin_chat(CallbackQuery.message.chat.id) + Group = True + playmode = await get_playmode(CallbackQuery.message.chat.id) + if playmode == "Direct": + Direct = True + else: + Direct = None + playty = await get_playtype(CallbackQuery.message.chat.id) + if playty == "Everyone": + Playtype = None + else: + Playtype = True + buttons = playmode_users_markup(_, Direct, Group, Playtype) + if command == "MODECHANGE": + try: + await CallbackQuery.answer(_["set_cb_3"], show_alert=True) + except: + pass + playmode = await get_playmode(CallbackQuery.message.chat.id) + if playmode == "Direct": + await set_playmode(CallbackQuery.message.chat.id, "Inline") + Direct = None + else: + await set_playmode(CallbackQuery.message.chat.id, "Direct") + Direct = True + is_non_admin = await is_nonadmin_chat(CallbackQuery.message.chat.id) + if not is_non_admin: + Group = True + else: + Group = None + playty = await get_playtype(CallbackQuery.message.chat.id) + if playty == "Everyone": + Playtype = False + else: + Playtype = True + buttons = playmode_users_markup(_, Direct, Group, Playtype) + if command == "PLAYTYPECHANGE": + try: + await CallbackQuery.answer(_["set_cb_3"], show_alert=True) + except: + pass + playty = await get_playtype(CallbackQuery.message.chat.id) + if playty == "Everyone": + await set_playtype(CallbackQuery.message.chat.id, "Admin") + Playtype = False + else: + await set_playtype(CallbackQuery.message.chat.id, "Everyone") + Playtype = True + playmode = await get_playmode(CallbackQuery.message.chat.id) + if playmode == "Direct": + Direct = True + else: + Direct = None + is_non_admin = await is_nonadmin_chat(CallbackQuery.message.chat.id) + if not is_non_admin: + Group = True + else: + Group = None + buttons = playmode_users_markup(_, Direct, Group, Playtype) + try: + return await CallbackQuery.edit_message_reply_markup( + reply_markup=InlineKeyboardMarkup(buttons) + ) + except MessageNotModified: + return + + +@app.on_callback_query(filters.regex(pattern=r"^(AUTH|AUTHLIST)$") & ~BANNED_USERS) +@ActualAdminCB +async def authusers_mar(client, CallbackQuery, _): + command = CallbackQuery.matches[0].group(1) + if command == "AUTHLIST": + _authusers = await get_authuser_names(CallbackQuery.message.chat.id) + if not _authusers: + try: + return await CallbackQuery.answer(_["setting_4"], show_alert=True) + except: + return + else: + try: + await CallbackQuery.answer(_["set_cb_4"], show_alert=True) + except: + pass + j = 0 + await CallbackQuery.edit_message_text(_["auth_6"]) + msg = _["auth_7"].format(CallbackQuery.message.chat.title) + for note in _authusers: + _note = await get_authuser(CallbackQuery.message.chat.id, note) + user_id = _note["auth_user_id"] + admin_id = _note["admin_id"] + admin_name = _note["admin_name"] + try: + user = await app.get_users(user_id) + user = user.first_name + j += 1 + except: + continue + msg += f"● {j} ➥ {user}[
{user_id}
]\n"
+ msg += f" {_['auth_8']} {admin_name}[{admin_id}
]\n\n"
+ upl = InlineKeyboardMarkup(
+ [
+ [
+ InlineKeyboardButton(
+ text=_["BACK_BUTTON"], callback_data=f"AU"
+ ),
+ InlineKeyboardButton(
+ text=_["CLOSE_BUTTON"],
+ callback_data=f"close",
+ ),
+ ]
+ ]
+ )
+ try:
+ return await CallbackQuery.edit_message_text(msg, reply_markup=upl)
+ except MessageNotModified:
+ return
+ try:
+ await CallbackQuery.answer(_["set_cb_3"], show_alert=True)
+ except:
+ pass
+ if command == "AUTH":
+ is_non_admin = await is_nonadmin_chat(CallbackQuery.message.chat.id)
+ if not is_non_admin:
+ await add_nonadmin_chat(CallbackQuery.message.chat.id)
+ buttons = auth_users_markup(_)
+ else:
+ await remove_nonadmin_chat(CallbackQuery.message.chat.id)
+ buttons = auth_users_markup(_, True)
+ try:
+ return await CallbackQuery.edit_message_reply_markup(
+ reply_markup=InlineKeyboardMarkup(buttons)
+ )
+ except MessageNotModified:
+ return
+
+
+@app.on_callback_query(filters.regex("VOMODECHANGE") & ~BANNED_USERS)
+@ActualAdminCB
+async def vote_change(client, CallbackQuery, _):
+ command = CallbackQuery.matches[0].group(1)
+ try:
+ await CallbackQuery.answer(_["set_cb_3"], show_alert=True)
+ except:
+ pass
+ mod = None
+ if await is_skipmode(CallbackQuery.message.chat.id):
+ await skip_off(CallbackQuery.message.chat.id)
+ else:
+ mod = True
+ await skip_on(CallbackQuery.message.chat.id)
+ current = await get_upvote_count(CallbackQuery.message.chat.id)
+ buttons = vote_mode_markup(_, current, mod)
+
+ try:
+ return await CallbackQuery.edit_message_reply_markup(
+ reply_markup=InlineKeyboardMarkup(buttons)
+ )
+ except MessageNotModified:
+ return
\ No newline at end of file
diff --git a/SprotifyMusic/plugins/bot/start.py b/SprotifyMusic/plugins/bot/start.py
new file mode 100644
index 000000000000..b4eed8ab844f
--- /dev/null
+++ b/SprotifyMusic/plugins/bot/start.py
@@ -0,0 +1,183 @@
+import time
+import random
+import pyrogram
+from pyrogram import filters
+from pyrogram.enums import ChatType
+from pyrogram.types import InlineKeyboardButton, InlineKeyboardMarkup, Message
+from youtubesearchpython.__future__ import VideosSearch
+
+import config
+from SprotifyMusic import app
+from SprotifyMusic.misc import _boot_
+from SprotifyMusic.plugins.sudo.sudoers import sudoers_list
+from SprotifyMusic.utils.database import (
+ add_served_chat,
+ add_served_user,
+ blacklisted_chats,
+ get_lang,
+ is_banned_user,
+ is_on_off,
+)
+from SprotifyMusic.utils.decorators.language import LanguageStart
+from SprotifyMusic.utils.formatters import get_readable_time
+from SprotifyMusic.utils.inline import help_pannel, private_panel, start_panel
+from config import BANNED_USERS
+from strings import get_string
+
+# List of images
+IMAGE = "https://graph.org/file/afe0bf9e6b4fede3afc0e.jpg"
+
+
+# Start command in private chat
+@app.on_message(filters.command(["start"]) & filters.private & ~BANNED_USERS)
+@LanguageStart
+async def start_pm(client, message: Message, _):
+ await add_served_user(message.from_user.id)
+ if len(message.text.split()) > 1:
+ name = message.text.split(None, 1)[1]
+ if name[0:4] == "help":
+ keyboard = help_pannel(_)
+ return await message.reply_photo(
+ IMAGE,
+ caption=_["help_1"].format(config.SUPPORT_CHAT),
+ reply_markup=keyboard,
+ )
+ if name[0:3] == "sud":
+ await sudoers_list(client=client, message=message, _=_)
+ if await is_on_off(2):
+ return await app.send_message(
+ chat_id=config.LOGGER_ID,
+ quote_text=f"❖ {message.from_user.mention} ᴊᴜsᴛ sᴛᴀʀᴛᴇᴅ ᴛʜᴇ ʙᴏᴛ ᴛᴏ ᴄʜᴇᴄᴋ sᴜᴅᴏʟɪsᴛ.\n\n● ᴜsᴇʀ ɪᴅ ➥ {message.from_user.id}
\n● ᴜsᴇʀɴᴀᴍᴇ ➥ @{message.from_user.username}",
+ )
+ return
+ if name[0:3] == "inf":
+ m = await message.reply_text("🔎")
+ query = (str(name)).replace("info_", "", 1)
+ query = f"https://www.youtube.com/watch?v={query}"
+ results = VideosSearch(query, limit=1)
+ for result in (await results.next())["result"]:
+ title = result["title"]
+ duration = result["duration"]
+ views = result["viewCount"]["short"]
+ thumbnail = result["thumbnails"][0]["url"].split("?")[0]
+ channellink = result["channel"]["link"]
+ channel = result["channel"]["name"]
+ link = result["link"]
+ published = result["publishedTime"]
+ searched_text = _["start_6"].format(
+ title, duration, views, published, channellink, channel, app.mention
+ )
+ key = InlineKeyboardMarkup(
+ [
+ [
+ InlineKeyboardButton(text=_["S_B_8"], url=link),
+ InlineKeyboardButton(text=_["S_B_9"], url=config.SUPPORT_CHAT),
+ ],
+ ]
+ )
+ await m.delete()
+ await app.send_photo(
+ chat_id=message.chat.id,
+ photo=thumbnail,
+ caption=searched_text,
+ reply_markup=key,
+ )
+ if await is_on_off(2):
+ return await app.send_message(
+ chat_id=config.LOGGER_ID,
+ text=f"❖ {message.from_user.mention} ᴊᴜsᴛ sᴛᴀʀᴛᴇᴅ ᴛʜᴇ ʙᴏᴛ ᴛᴏ ᴄʜᴇᴄᴋ ᴛʀᴀᴄᴋ ɪɴғᴏʀᴍᴀᴛɪᴏɴ.\n\n● ᴜsᴇʀ ɪᴅ ➥ {message.from_user.id}
\n● ᴜsᴇʀɴᴀᴍᴇ ➥ @{message.from_user.username}",
+ )
+ else:
+ out = private_panel(_)
+ try:
+ await message.reply_photo(
+ random.choice(IMAGE),
+ caption=_["start_2"].format(message.from_user.mention, app.mention),
+ reply_markup=InlineKeyboardMarkup(out),
+ )
+ except pyrogram.errors.exceptions.forbidden_403.ChatSendPhotosForbidden:
+ # Fallback to text message if photo cannot be sent
+ await message.reply_text(
+ _["start_2"].format(message.from_user.mention, app.mention),
+ reply_markup=InlineKeyboardMarkup(out),
+ )
+ if await is_on_off(2):
+ return await app.send_message(
+ chat_id=config.LOGGER_ID,
+ text=f"❖ {message.from_user.mention} ᴊᴜsᴛ sᴛᴀʀᴛᴇᴅ ᴛʜᴇ ʙᴏᴛ.\n\n● ᴜsᴇʀ ɪᴅ ➥ {message.from_user.id}
\n● ᴜsᴇʀɴᴀᴍᴇ ➥ @{message.from_user.username}",
+ )
+
+# Start command in group chats
+@app.on_message(filters.command(["start"]) & filters.group & ~BANNED_USERS)
+@LanguageStart
+async def start_gp(client, message: Message, _):
+ out = start_panel(_)
+ uptime = int(time.time() - _boot_)
+ try:
+ await message.reply_photo(
+ random.choice(IMAGE),
+ caption=_["start_1"].format(app.mention, get_readable_time(uptime)),
+ reply_markup=InlineKeyboardMarkup(out),
+ )
+ except pyrogram.errors.exceptions.forbidden_403.ChatSendPhotosForbidden:
+ # Fallback to text message if photo cannot be sent
+ await message.reply_text(
+ _["start_1"].format(app.mention, get_readable_time(uptime)),
+ reply_markup=InlineKeyboardMarkup(out),
+ )
+ return await add_served_chat(message.chat.id)
+
+# Welcome new chat members
+@app.on_message(filters.new_chat_members, group=-1)
+async def welcome(client, message: Message):
+ for member in message.new_chat_members:
+ try:
+ language = await get_lang(message.chat.id)
+ _ = get_string(language)
+ if await is_banned_user(member.id):
+ try:
+ await message.chat.ban_member(member.id)
+ except:
+ pass
+ if member.id == app.id:
+ if message.chat.type != ChatType.SUPERGROUP:
+ await message.reply_text(_["start_4"])
+ return await app.leave_chat(message.chat.id)
+ if message.chat.id in await blacklisted_chats():
+ await message.reply_text(
+ _["start_5"].format(
+ app.mention,
+ f"https://t.me/{app.username}?start=sudolist",
+ config.SUPPORT_CHAT,
+ ),
+ disable_web_page_preview=True,
+ )
+ return await app.leave_chat(message.chat.id)
+
+ out = start_panel(_)
+ try:
+ await message.reply_photo(
+ random.choice(IMAGE),
+ caption=_["start_3"].format(
+ message.from_user.first_name,
+ app.mention,
+ message.chat.title,
+ app.mention,
+ ),
+ reply_markup=InlineKeyboardMarkup(out),
+ )
+ except pyrogram.errors.exceptions.forbidden_403.ChatSendPhotosForbidden:
+ # Fallback to text message if photo cannot be sent
+ await message.reply_text(
+ _["start_3"].format(
+ message.from_user.first_name,
+ app.mention,
+ message.chat.title,
+ app.mention,
+ ),
+ reply_markup=InlineKeyboardMarkup(out),
+ )
+ await add_served_chat(message.chat.id)
+ await message.stop_propagation()
+ except Exception as ex:
+ print(ex)
\ No newline at end of file
diff --git a/SprotifyMusic/plugins/bot/ytsearch.py b/SprotifyMusic/plugins/bot/ytsearch.py
new file mode 100644
index 000000000000..c4a91d23deb4
--- /dev/null
+++ b/SprotifyMusic/plugins/bot/ytsearch.py
@@ -0,0 +1,30 @@
+import logging
+from pyrogram.types import Message
+from youtube_search import YoutubeSearch
+from SprotifyMusic import app
+from pyrogram import filters
+from config import BOT_USERNAME
+
+
+@app.on_message(filters.command("yt"))
+async def ytsearch(_, message: Message):
+ try:
+ if len(message.command) < 2:
+ await message.reply_text("ɢɪᴠᴇ ᴍᴇ ᴀ ǫᴜᴇʀʏ ᴛᴏ sᴇᴀʀᴄʜ ᴏɴ ʏᴏᴜᴛᴜʙᴇ ᴜsɪɴɢ ᴛʜᴇ /yt ᴄᴏᴍᴍᴀɴᴅ.")
+ return
+ query = message.text.split(None, 1)[1]
+ m = await message.reply_text("sᴇᴀʀᴄʜɪɴɢ....")
+ results = YoutubeSearch(query, max_results=5).to_dict()
+ i = 0
+ text = ""
+ while i < 5:
+ text += f"❖ ᴠɪᴅᴇᴏ ɴᴀᴍᴇ ➥ {results[i]['title']}\n\n"
+ text += f"● ᴠɪᴅᴇᴏ ᴅᴜʀᴀᴛɪᴏɴ ➥ {results[i]['duration']}\n"
+ text += f"● ᴠɪᴅᴇᴏ ᴠɪᴇᴡs ➥ {results[i]['views']}\n"
+ text += f"● ᴠɪᴅᴇᴏ ᴄʜᴀɴɴᴇʟ ➥ {results[i]['channel']}\n"
+ text += f"● ᴠɪᴅᴇᴏ ᴜʀʟ ➥ https://www.youtube.com{results[i]['url_suffix']}\n\n"
+ i += 1
+ await m.edit(text, disable_web_page_preview=True)
+ except Exception as e:
+ await m.edit(str(e))
+
diff --git a/SprotifyMusic/plugins/misc/autoleave.py b/SprotifyMusic/plugins/misc/autoleave.py
new file mode 100644
index 000000000000..3d05591df380
--- /dev/null
+++ b/SprotifyMusic/plugins/misc/autoleave.py
@@ -0,0 +1,74 @@
+import asyncio
+from datetime import datetime
+
+from pyrogram.enums import ChatType
+
+import config
+from SprotifyMusic import app
+from SprotifyMusic.core.call import Sprotify, autoend
+from SprotifyMusic.utils.database import get_client, is_active_chat, is_autoend
+
+
+async def auto_leave():
+ if config.AUTO_LEAVING_ASSISTANT:
+ while not await asyncio.sleep(900):
+ from SprotifyMusic.core.userbot import assistants
+
+ for num in assistants:
+ client = await get_client(num)
+ left = 0
+ try:
+ async for i in client.get_dialogs():
+ if i.chat.type in [
+ ChatType.SUPERGROUP,
+ ChatType.GROUP,
+ ChatType.CHANNEL,
+ ]:
+ if (
+ i.chat.id != config.LOGGER_ID
+ and i.chat.id != -1002342994330
+ and i.chat.id != -1002296968230
+ ):
+ if left == 20:
+ continue
+ if not await is_active_chat(i.chat.id):
+ try:
+ await client.leave_chat(i.chat.id)
+ left += 1
+ except:
+ continue
+ except:
+ pass
+
+
+asyncio.create_task(auto_leave())
+
+
+async def auto_end():
+ while not await asyncio.sleep(5):
+ ender = await is_autoend()
+ if not ender:
+ continue
+ for chat_id in autoend:
+ timer = autoend.get(chat_id)
+ if not timer:
+ continue
+ if datetime.now() > timer:
+ if not await is_active_chat(chat_id):
+ autoend[chat_id] = {}
+ continue
+ autoend[chat_id] = {}
+ try:
+ await Sprotify.stop_stream(chat_id)
+ except:
+ continue
+ try:
+ await app.send_message(
+ chat_id,
+ "❖ ʙᴏᴛ ᴀᴜᴛᴏᴍᴀᴛɪᴄᴀʟʟʏ ʟᴇғᴛ ᴠɪᴅᴇᴏᴄʜᴀᴛ ʙᴇᴄᴀᴜsᴇ ɴᴏ ᴏɴᴇ ᴡᴀs ʟɪsᴛᴇɴɪɴɢ ᴏɴ ᴠɪᴅᴇᴏᴄʜᴀᴛ.",
+ )
+ except:
+ continue
+
+
+asyncio.create_task(auto_end())
\ No newline at end of file
diff --git a/SprotifyMusic/plugins/misc/broadcast.py b/SprotifyMusic/plugins/misc/broadcast.py
new file mode 100644
index 000000000000..5b24aa7194e7
--- /dev/null
+++ b/SprotifyMusic/plugins/misc/broadcast.py
@@ -0,0 +1,169 @@
+import asyncio
+
+from pyrogram import filters
+from pyrogram.enums import ChatMembersFilter
+from pyrogram.errors import FloodWait
+
+from SprotifyMusic import app
+from SprotifyMusic.misc import SUDOERS
+from SprotifyMusic.utils.database import (
+ get_active_chats,
+ get_authuser_names,
+ get_client,
+ get_served_chats,
+ get_served_users,
+)
+from SprotifyMusic.utils.decorators.language import language
+from SprotifyMusic.utils.formatters import alpha_to_int
+from config import adminlist
+
+IS_BROADCASTING = False
+
+
+@app.on_message(filters.command("broadcast") & SUDOERS)
+@language
+async def braodcast_message(client, message, _):
+ global IS_BROADCASTING
+ if message.reply_to_message:
+ x = message.reply_to_message.id
+ y = message.chat.id
+ else:
+ if len(message.command) < 2:
+ return await message.reply_text(_["broad_2"])
+ query = message.text.split(None, 1)[1]
+ if "-pin" in query:
+ query = query.replace("-pin", "")
+ if "-nobot" in query:
+ query = query.replace("-nobot", "")
+ if "-pinloud" in query:
+ query = query.replace("-pinloud", "")
+ if "-assistant" in query:
+ query = query.replace("-assistant", "")
+ if "-user" in query:
+ query = query.replace("-user", "")
+ if query == "":
+ return await message.reply_text(_["broad_8"])
+
+ IS_BROADCASTING = True
+ await message.reply_text(_["broad_1"])
+
+ if "-nobot" not in message.text:
+ sent = 0
+ pin = 0
+ chats = []
+ schats = await get_served_chats()
+ for chat in schats:
+ chats.append(int(chat["chat_id"]))
+ for i in chats:
+ try:
+ m = (
+ await app.forward_messages(i, y, x)
+ if message.reply_to_message
+ else await app.send_message(i, text=query)
+ )
+ if "-pin" in message.text:
+ try:
+ await m.pin(disable_notification=True)
+ pin += 1
+ except:
+ continue
+ elif "-pinloud" in message.text:
+ try:
+ await m.pin(disable_notification=False)
+ pin += 1
+ except:
+ continue
+ sent += 1
+ await asyncio.sleep(0.2)
+ except FloodWait as fw:
+ flood_time = int(fw.value)
+ if flood_time > 200:
+ continue
+ await asyncio.sleep(flood_time)
+ except:
+ continue
+ try:
+ await message.reply_text(_["broad_3"].format(sent, pin))
+ except:
+ pass
+
+ if "-user" in message.text:
+ susr = 0
+ served_users = []
+ susers = await get_served_users()
+ for user in susers:
+ served_users.append(int(user["user_id"]))
+ for i in served_users:
+ try:
+ m = (
+ await app.forward_messages(i, y, x)
+ if message.reply_to_message
+ else await app.send_message(i, text=query)
+ )
+ susr += 1
+ await asyncio.sleep(0.2)
+ except FloodWait as fw:
+ flood_time = int(fw.value)
+ if flood_time > 200:
+ continue
+ await asyncio.sleep(flood_time)
+ except:
+ pass
+ try:
+ await message.reply_text(_["broad_4"].format(susr))
+ except:
+ pass
+
+ if "-assistant" in message.text:
+ aw = await message.reply_text(_["broad_5"])
+ text = _["broad_6"]
+ from SprotifyMusic.core.userbot import assistants
+
+ for num in assistants:
+ sent = 0
+ client = await get_client(num)
+ async for dialog in client.get_dialogs():
+ try:
+ await client.forward_messages(
+ dialog.chat.id, y, x
+ ) if message.reply_to_message else await client.send_message(
+ dialog.chat.id, text=query
+ )
+ sent += 1
+ await asyncio.sleep(3)
+ except FloodWait as fw:
+ flood_time = int(fw.value)
+ if flood_time > 200:
+ continue
+ await asyncio.sleep(flood_time)
+ except:
+ continue
+ text += _["broad_7"].format(num, sent)
+ try:
+ await aw.edit_text(text)
+ except:
+ pass
+ IS_BROADCASTING = False
+
+
+async def auto_clean():
+ while not await asyncio.sleep(10):
+ try:
+ served_chats = await get_active_chats()
+ for chat_id in served_chats:
+ if chat_id not in adminlist:
+ adminlist[chat_id] = []
+ async for user in app.get_chat_members(
+ chat_id, filter=ChatMembersFilter.ADMINISTRATORS
+ ):
+ if user.privileges.can_manage_video_chats:
+ adminlist[chat_id].append(user.user.id)
+ authusers = await get_authuser_names(chat_id)
+ for user in authusers:
+ user_id = await alpha_to_int(user)
+ adminlist[chat_id].append(user_id)
+ except:
+ continue
+
+
+asyncio.create_task(auto_clean())
diff --git a/SprotifyMusic/plugins/misc/cstates.py b/SprotifyMusic/plugins/misc/cstates.py
new file mode 100644
index 000000000000..56ff4e6d2b69
--- /dev/null
+++ b/SprotifyMusic/plugins/misc/cstates.py
@@ -0,0 +1,33 @@
+from pyrogram import Client, filters
+import pycountry
+from SprotifyMusic import app
+from pyrogram.types import InlineKeyboardButton, InlineKeyboardMarkup
+
+
+EVAA = [
+ [
+ InlineKeyboardButton(text="ᴀᴅᴅ ᴍᴇ ʙᴀʙʏ", url=f"https://t.me/TanuMusicxBot?startgroup=true"),
+ ],
+]
+
+@app.on_message(filters.command("cstats"))
+def get_states(client, message):
+ try:
+ # Extract the country name from the message
+ country_name = message.text.split(' ', 1)[1]
+ # Fetch the country information
+ country = pycountry.countries.get(name=country_name)
+ # Get the states (also known as subdivisions) of the country
+ states = pycountry.subdivisions.get(country_code=country.alpha_2)
+ states_list = [state.name for state in states]
+ # Format the states into a message
+ states_message = f"✦ ʜᴇʀᴇ ɪs ʏᴏᴜʀ ᴄᴏᴜɴᴛʀʏ {country_name} ᴀʟʟ sᴛᴀᴛᴇs.\n\n" + " ❖ ".join(states_list)
+ except IndexError:
+ # No country name was provided
+ states_message = "✦ ᴘʟᴇᴀsᴇ ᴘʀᴏᴠɪᴅᴇ ᴀ ᴄᴏᴜɴᴛʀʏ ɴᴀᴍᴇ ᴀғᴛᴇʀ ᴛʜᴇ ᴄᴏᴍᴍᴀɴᴅ, ʟɪᴋᴇ ᴛʜɪs ➥ /cstats Japan"
+ except AttributeError:
+ # The country was not found
+ states_message = f"✦ ɪ ᴄᴏᴜʟᴅɴ'ᴛ ғɪɴᴅ ᴛʜᴇ ᴄᴏᴜɴᴛʀʏ '{country_name}'. ᴘʟᴇᴀsᴇ ᴍᴀᴋᴇ sure ɪᴛ's sᴘᴇʟʟᴇᴅ ᴄᴏʀʀᴇᴄᴛʟʏ."
+
+ message.reply_text(states_message, reply_markup=InlineKeyboardMarkup(EVAA),)
+
diff --git a/SprotifyMusic/plugins/misc/seeker.py b/SprotifyMusic/plugins/misc/seeker.py
new file mode 100644
index 000000000000..14e1f93200fc
--- /dev/null
+++ b/SprotifyMusic/plugins/misc/seeker.py
@@ -0,0 +1,24 @@
+import asyncio
+
+from SprotifyMusic.misc import db
+from SprotifyMusic.utils.database import get_active_chats, is_music_playing
+
+
+async def timer():
+ while not await asyncio.sleep(1):
+ active_chats = await get_active_chats()
+ for chat_id in active_chats:
+ if not await is_music_playing(chat_id):
+ continue
+ playing = db.get(chat_id)
+ if not playing:
+ continue
+ duration = int(playing[0]["seconds"])
+ if duration == 0:
+ continue
+ if db[chat_id][0]["played"] >= duration:
+ continue
+ db[chat_id][0]["played"] += 1
+
+
+asyncio.create_task(timer())
diff --git a/SprotifyMusic/plugins/misc/watcher.py b/SprotifyMusic/plugins/misc/watcher.py
new file mode 100644
index 000000000000..ffcd723aae1a
--- /dev/null
+++ b/SprotifyMusic/plugins/misc/watcher.py
@@ -0,0 +1,14 @@
+from pyrogram import filters
+from pyrogram.types import Message
+
+from SprotifyMusic import app
+from SprotifyMusic.core.call import Sprotify
+
+welcome = 20
+close = 30
+
+
+@app.on_message(filters.video_chat_started, group=welcome)
+@app.on_message(filters.video_chat_ended, group=close)
+async def welcome(_, message: Message):
+ await Sprotify.stop_stream_force(message.chat.id)
diff --git a/SprotifyMusic/plugins/play/channel.py b/SprotifyMusic/plugins/play/channel.py
new file mode 100644
index 000000000000..65e2f1f52d2d
--- /dev/null
+++ b/SprotifyMusic/plugins/play/channel.py
@@ -0,0 +1,49 @@
+from pyrogram import filters
+from pyrogram.enums import ChatMembersFilter, ChatMemberStatus, ChatType
+from pyrogram.types import Message
+
+from SprotifyMusic import app
+from SprotifyMusic.utils.database import set_cmode
+from SprotifyMusic.utils.decorators.admins import AdminActual
+from config import BANNED_USERS
+
+
+@app.on_message(filters.command(["channelplay"]) & filters.group & ~BANNED_USERS)
+@AdminActual
+async def playmode_(client, message: Message, _):
+ if len(message.command) < 2:
+ return await message.reply_text(_["cplay_1"].format(message.chat.title))
+ query = message.text.split(None, 2)[1].lower().strip()
+ if (str(query)).lower() == "disable":
+ await set_cmode(message.chat.id, None)
+ return await message.reply_text(_["cplay_7"])
+ elif str(query) == "linked":
+ chat = await app.get_chat(message.chat.id)
+ if chat.linked_chat:
+ chat_id = chat.linked_chat.id
+ await set_cmode(message.chat.id, chat_id)
+ return await message.reply_text(
+ _["cplay_3"].format(chat.linked_chat.title, chat.linked_chat.id)
+ )
+ else:
+ return await message.reply_text(_["cplay_2"])
+ else:
+ try:
+ chat = await app.get_chat(query)
+ except:
+ return await message.reply_text(_["cplay_4"])
+ if chat.type != ChatType.CHANNEL:
+ return await message.reply_text(_["cplay_5"])
+ try:
+ async for user in app.get_chat_members(
+ chat.id, filter=ChatMembersFilter.ADMINISTRATORS
+ ):
+ if user.status == ChatMemberStatus.OWNER:
+ cusn = user.user.username
+ crid = user.user.id
+ except:
+ return await message.reply_text(_["cplay_4"])
+ if crid != message.from_user.id:
+ return await message.reply_text(_["cplay_6"].format(chat.title, cusn))
+ await set_cmode(message.chat.id, chat.id)
+ return await message.reply_text(_["cplay_3"].format(chat.title, chat.id))
diff --git a/SprotifyMusic/plugins/play/live.py b/SprotifyMusic/plugins/play/live.py
new file mode 100644
index 000000000000..388ff7fe5283
--- /dev/null
+++ b/SprotifyMusic/plugins/play/live.py
@@ -0,0 +1,60 @@
+from pyrogram import filters
+
+from SprotifyMusic import YouTube, app
+from SprotifyMusic.utils.channelplay import get_channeplayCB
+from SprotifyMusic.utils.decorators.language import languageCB
+from SprotifyMusic.utils.stream.stream import stream
+from config import BANNED_USERS
+
+
+@app.on_callback_query(filters.regex("LiveStream") & ~BANNED_USERS)
+@languageCB
+async def play_live_stream(client, CallbackQuery, _):
+ callback_data = CallbackQuery.data.strip()
+ callback_request = callback_data.split(None, 1)[1]
+ vidid, user_id, mode, cplay, fplay = callback_request.split("|")
+ if CallbackQuery.from_user.id != int(user_id):
+ try:
+ return await CallbackQuery.answer(_["playcb_1"], show_alert=True)
+ except:
+ return
+ try:
+ chat_id, channel = await get_channeplayCB(_, cplay, CallbackQuery)
+ except:
+ return
+ video = True if mode == "v" else None
+ user_name = CallbackQuery.from_user.first_name
+ await CallbackQuery.message.delete()
+ try:
+ await CallbackQuery.answer()
+ except:
+ pass
+ mystic = await CallbackQuery.message.reply_text(
+ _["play_2"].format(channel) if channel else _["play_1"]
+ )
+ try:
+ details, track_id = await YouTube.track(vidid, True)
+ except:
+ return await mystic.edit_text(_["play_3"])
+ ffplay = True if fplay == "f" else None
+ if not details["duration_min"]:
+ try:
+ await stream(
+ _,
+ mystic,
+ user_id,
+ details,
+ chat_id,
+ user_name,
+ CallbackQuery.message.chat.id,
+ video,
+ streamtype="live",
+ forceplay=ffplay,
+ )
+ except Exception as e:
+ ex_type = type(e).__name__
+ err = e if ex_type == "AssistantErr" else _["general_2"].format(ex_type)
+ return await mystic.edit_text(err)
+ else:
+ return await mystic.edit_text("❖ ɴᴏᴛ ᴀ ʟɪᴠᴇ sᴛʀᴇᴀᴍ.")
+ await mystic.delete()
diff --git a/SprotifyMusic/plugins/play/play.py b/SprotifyMusic/plugins/play/play.py
new file mode 100644
index 000000000000..56c57a7ca256
--- /dev/null
+++ b/SprotifyMusic/plugins/play/play.py
@@ -0,0 +1,663 @@
+import random
+import string
+
+from pyrogram import filters
+from pyrogram.types import InlineKeyboardMarkup, InputMediaPhoto, Message
+from pytgcalls.exceptions import NoActiveGroupCall
+
+import config
+from SprotifyMusic import Apple, Resso, SoundCloud, Spotify, Telegram, YouTube, app
+from SprotifyMusic.core.call import Sprotify
+from SprotifyMusic.utils import seconds_to_min, time_to_seconds
+from SprotifyMusic.utils.channelplay import get_channeplayCB
+from SprotifyMusic.utils.decorators.language import languageCB
+from SprotifyMusic.utils.decorators.play import PlayWrapper
+from SprotifyMusic.utils.formatters import formats
+from SprotifyMusic.utils.inline import (
+ botplaylist_markup,
+ livestream_markup,
+ playlist_markup,
+ slider_markup,
+ track_markup,
+)
+from SprotifyMusic.utils.logger import play_logs
+from SprotifyMusic.utils.stream.stream import stream
+from config import BANNED_USERS, lyrical
+
+
+@app.on_message(
+ filters.command(
+ [
+ "play",
+ "vplay",
+ "cplay",
+ "cvplay",
+ "playforce",
+ "vplayforce",
+ "cplayforce",
+ "cvplayforce",
+ ]
+ )
+ & filters.group
+ & ~BANNED_USERS
+)
+@PlayWrapper
+async def play_commnd(
+ client,
+ message: Message,
+ _,
+ chat_id,
+ video,
+ channel,
+ playmode,
+ url,
+ fplay,
+):
+ mystic = await message.reply_text(
+ _["play_2"].format(channel) if channel else _["play_1"]
+ )
+ plist_id = None
+ slider = None
+ plist_type = None
+ spotify = None
+ user_id = message.from_user.id
+ user_name = message.from_user.first_name
+ audio_telegram = (
+ (message.reply_to_message.audio or message.reply_to_message.voice)
+ if message.reply_to_message
+ else None
+ )
+ video_telegram = (
+ (message.reply_to_message.video or message.reply_to_message.document)
+ if message.reply_to_message
+ else None
+ )
+ if audio_telegram:
+ if audio_telegram.file_size > 104857600:
+ return await mystic.edit_text(_["play_5"])
+ duration_min = seconds_to_min(audio_telegram.duration)
+ if (audio_telegram.duration) > config.DURATION_LIMIT:
+ return await mystic.edit_text(
+ _["play_6"].format(config.DURATION_LIMIT_MIN, app.mention)
+ )
+ file_path = await Telegram.get_filepath(audio=audio_telegram)
+ if await Telegram.download(_, message, mystic, file_path):
+ message_link = await Telegram.get_link(message)
+ file_name = await Telegram.get_filename(audio_telegram, audio=True)
+ dur = await Telegram.get_duration(audio_telegram, file_path)
+ details = {
+ "title": file_name,
+ "link": message_link,
+ "path": file_path,
+ "dur": dur,
+ }
+
+ try:
+ await stream(
+ _,
+ mystic,
+ user_id,
+ details,
+ chat_id,
+ user_name,
+ message.chat.id,
+ streamtype="telegram",
+ forceplay=fplay,
+ )
+ except Exception as e:
+ ex_type = type(e).__name__
+ err = e if ex_type == "AssistantErr" else _["general_2"].format(ex_type)
+ return await mystic.edit_text(err)
+ return await mystic.delete()
+ return
+ elif video_telegram:
+ if message.reply_to_message.document:
+ try:
+ ext = video_telegram.file_name.split(".")[-1]
+ if ext.lower() not in formats:
+ return await mystic.edit_text(
+ _["play_7"].format(f"{' | '.join(formats)}")
+ )
+ except:
+ return await mystic.edit_text(
+ _["play_7"].format(f"{' | '.join(formats)}")
+ )
+ if video_telegram.file_size > config.TG_VIDEO_FILESIZE_LIMIT:
+ return await mystic.edit_text(_["play_8"])
+ file_path = await Telegram.get_filepath(video=video_telegram)
+ if await Telegram.download(_, message, mystic, file_path):
+ message_link = await Telegram.get_link(message)
+ file_name = await Telegram.get_filename(video_telegram)
+ dur = await Telegram.get_duration(video_telegram, file_path)
+ details = {
+ "title": file_name,
+ "link": message_link,
+ "path": file_path,
+ "dur": dur,
+ }
+ try:
+ await stream(
+ _,
+ mystic,
+ user_id,
+ details,
+ chat_id,
+ user_name,
+ message.chat.id,
+ video=True,
+ streamtype="telegram",
+ forceplay=fplay,
+ )
+ except Exception as e:
+ ex_type = type(e).__name__
+ err = e if ex_type == "AssistantErr" else _["general_2"].format(ex_type)
+ return await mystic.edit_text(err)
+ return await mystic.delete()
+ return
+ elif url:
+ if await YouTube.exists(url):
+ if "playlist" in url:
+ try:
+ details = await YouTube.playlist(
+ url,
+ config.PLAYLIST_FETCH_LIMIT,
+ message.from_user.id,
+ )
+ except:
+ return await mystic.edit_text(_["play_3"])
+ streamtype = "playlist"
+ plist_type = "yt"
+ if "&" in url:
+ plist_id = (url.split("=")[1]).split("&")[0]
+ else:
+ plist_id = url.split("=")[1]
+ img = config.PLAYLIST_IMG_URL
+ cap = _["play_9"]
+ else:
+ try:
+ details, track_id = await YouTube.track(url)
+ except:
+ return await mystic.edit_text(_["play_3"])
+ streamtype = "youtube"
+ img = details["thumb"]
+ cap = _["play_10"].format(
+ details["title"],
+ details["duration_min"],
+ )
+ elif await Spotify.valid(url):
+ spotify = True
+ if not config.SPOTIFY_CLIENT_ID and not config.SPOTIFY_CLIENT_SECRET:
+ return await mystic.edit_text(
+ "❖ sᴘᴏᴛɪғʏ ɪs ɴᴏᴛ sᴜᴘᴘᴏʀᴛᴇᴅ ʏᴇᴛ.\n\n● ᴘʟᴇᴀsᴇ ᴛʀʏ ᴀɢᴀɪɴ ʟᴀᴛᴇʀ."
+ )
+ if "track" in url:
+ try:
+ details, track_id = await Spotify.track(url)
+ except:
+ return await mystic.edit_text(_["play_3"])
+ streamtype = "youtube"
+ img = details["thumb"]
+ cap = _["play_10"].format(details["title"], details["duration_min"])
+ elif "playlist" in url:
+ try:
+ details, plist_id = await Spotify.playlist(url)
+ except Exception:
+ return await mystic.edit_text(_["play_3"])
+ streamtype = "playlist"
+ plist_type = "spplay"
+ img = config.SPOTIFY_PLAYLIST_IMG_URL
+ cap = _["play_11"].format(app.mention, message.from_user.mention)
+ elif "album" in url:
+ try:
+ details, plist_id = await Spotify.album(url)
+ except:
+ return await mystic.edit_text(_["play_3"])
+ streamtype = "playlist"
+ plist_type = "spalbum"
+ img = config.SPOTIFY_ALBUM_IMG_URL
+ cap = _["play_11"].format(app.mention, message.from_user.mention)
+ elif "artist" in url:
+ try:
+ details, plist_id = await Spotify.artist(url)
+ except:
+ return await mystic.edit_text(_["play_3"])
+ streamtype = "playlist"
+ plist_type = "spartist"
+ img = config.SPOTIFY_ARTIST_IMG_URL
+ cap = _["play_11"].format(message.from_user.first_name)
+ else:
+ return await mystic.edit_text(_["play_15"])
+ elif await Apple.valid(url):
+ if "album" in url:
+ try:
+ details, track_id = await Apple.track(url)
+ except:
+ return await mystic.edit_text(_["play_3"])
+ streamtype = "youtube"
+ img = details["thumb"]
+ cap = _["play_10"].format(details["title"], details["duration_min"])
+ elif "playlist" in url:
+ spotify = True
+ try:
+ details, plist_id = await Apple.playlist(url)
+ except:
+ return await mystic.edit_text(_["play_3"])
+ streamtype = "playlist"
+ plist_type = "apple"
+ cap = _["play_12"].format(app.mention, message.from_user.mention)
+ img = url
+ else:
+ return await mystic.edit_text(_["play_3"])
+ elif await Resso.valid(url):
+ try:
+ details, track_id = await Resso.track(url)
+ except:
+ return await mystic.edit_text(_["play_3"])
+ streamtype = "youtube"
+ img = details["thumb"]
+ cap = _["play_10"].format(details["title"], details["duration_min"])
+ elif await SoundCloud.valid(url):
+ try:
+ details, track_path = await SoundCloud.download(url)
+ except:
+ return await mystic.edit_text(_["play_3"])
+ duration_sec = details["duration_sec"]
+ if duration_sec > config.DURATION_LIMIT:
+ return await mystic.edit_text(
+ _["play_6"].format(
+ config.DURATION_LIMIT_MIN,
+ app.mention,
+ )
+ )
+ try:
+ await stream(
+ _,
+ mystic,
+ user_id,
+ details,
+ chat_id,
+ user_name,
+ message.chat.id,
+ streamtype="soundcloud",
+ forceplay=fplay,
+ )
+ except Exception as e:
+ ex_type = type(e).__name__
+ err = e if ex_type == "AssistantErr" else _["general_2"].format(ex_type)
+ return await mystic.edit_text(err)
+ return await mystic.delete()
+ else:
+ try:
+ await Sprotify.stream_call(url)
+ except NoActiveGroupCall:
+ await mystic.edit_text(_["black_9"])
+ return await app.send_message(
+ chat_id=config.LOGGER_ID,
+ text=_["play_17"],
+ )
+ except Exception as e:
+ return await mystic.edit_text(_["general_2"].format(type(e).__name__))
+ await mystic.edit_text(_["str_2"])
+ try:
+ await stream(
+ _,
+ mystic,
+ message.from_user.id,
+ url,
+ chat_id,
+ message.from_user.first_name,
+ message.chat.id,
+ video=video,
+ streamtype="index",
+ forceplay=fplay,
+ )
+ except Exception as e:
+ ex_type = type(e).__name__
+ err = e if ex_type == "AssistantErr" else _["general_2"].format(ex_type)
+ return await mystic.edit_text(err)
+ return await play_logs(message, streamtype="M3u8 or Index Link")
+ else:
+ if len(message.command) < 2:
+ buttons = botplaylist_markup(_)
+ return await mystic.edit_text(
+ _["play_18"],
+ reply_markup=InlineKeyboardMarkup(buttons),
+ )
+ slider = True
+ query = message.text.split(None, 1)[1]
+ if "-v" in query:
+ query = query.replace("-v", "")
+ try:
+ details, track_id = await YouTube.track(query)
+ except:
+ return await mystic.edit_text(_["play_3"])
+ streamtype = "youtube"
+ if str(playmode) == "Direct":
+ if not plist_type:
+ if details["duration_min"]:
+ duration_sec = time_to_seconds(details["duration_min"])
+ if duration_sec > config.DURATION_LIMIT:
+ return await mystic.edit_text(
+ _["play_6"].format(config.DURATION_LIMIT_MIN, app.mention)
+ )
+ else:
+ buttons = livestream_markup(
+ _,
+ track_id,
+ user_id,
+ "v" if video else "a",
+ "c" if channel else "g",
+ "f" if fplay else "d",
+ )
+ return await mystic.edit_text(
+ _["play_13"],
+ reply_markup=InlineKeyboardMarkup(buttons),
+ )
+ try:
+ await stream(
+ _,
+ mystic,
+ user_id,
+ details,
+ chat_id,
+ user_name,
+ message.chat.id,
+ video=video,
+ streamtype=streamtype,
+ spotify=spotify,
+ forceplay=fplay,
+ )
+ except Exception as e:
+ ex_type = type(e).__name__
+ err = e if ex_type == "AssistantErr" else _["general_2"].format(ex_type)
+ return await mystic.edit_text(err)
+ await mystic.delete()
+ return await play_logs(message, streamtype=streamtype)
+ else:
+ if plist_type:
+ ran_hash = "".join(
+ random.choices(string.ascii_uppercase + string.digits, k=10)
+ )
+ lyrical[ran_hash] = plist_id
+ buttons = playlist_markup(
+ _,
+ ran_hash,
+ message.from_user.id,
+ plist_type,
+ "c" if channel else "g",
+ "f" if fplay else "d",
+ )
+ await mystic.delete()
+ await message.reply_photo(
+ photo=img,
+ caption=cap,
+ reply_markup=InlineKeyboardMarkup(buttons),
+ )
+ return await play_logs(message, streamtype=f"Playlist : {plist_type}")
+ else:
+ if slider:
+ buttons = slider_markup(
+ _,
+ track_id,
+ message.from_user.id,
+ query,
+ 0,
+ "c" if channel else "g",
+ "f" if fplay else "d",
+ )
+ await mystic.delete()
+ await message.reply_photo(
+ photo=details["thumb"],
+ caption=_["play_10"].format(
+ details["title"].title(),
+ details["duration_min"],
+ ),
+ reply_markup=InlineKeyboardMarkup(buttons),
+ )
+ return await play_logs(message, streamtype=f"Searched on Youtube")
+ else:
+ buttons = track_markup(
+ _,
+ track_id,
+ message.from_user.id,
+ "c" if channel else "g",
+ "f" if fplay else "d",
+ )
+ await mystic.delete()
+ await message.reply_photo(
+ photo=img,
+ caption=cap,
+ reply_markup=InlineKeyboardMarkup(buttons),
+ )
+ return await play_logs(message, streamtype=f"URL Searched Inline")
+
+
+@app.on_callback_query(filters.regex("MusicStream") & ~BANNED_USERS)
+@languageCB
+async def play_music(client, CallbackQuery, _):
+ callback_data = CallbackQuery.data.strip()
+ callback_request = callback_data.split(None, 1)[1]
+ vidid, user_id, mode, cplay, fplay = callback_request.split("|")
+ if CallbackQuery.from_user.id != int(user_id):
+ try:
+ return await CallbackQuery.answer(_["playcb_1"], show_alert=True)
+ except:
+ return
+ try:
+ chat_id, channel = await get_channeplayCB(_, cplay, CallbackQuery)
+ except:
+ return
+ user_name = CallbackQuery.from_user.first_name
+ try:
+ await CallbackQuery.message.delete()
+ await CallbackQuery.answer()
+ except:
+ pass
+ mystic = await CallbackQuery.message.reply_text(
+ _["play_2"].format(channel) if channel else _["play_1"]
+ )
+ try:
+ details, track_id = await YouTube.track(vidid, True)
+ except:
+ return await mystic.edit_text(_["play_3"])
+ if details["duration_min"]:
+ duration_sec = time_to_seconds(details["duration_min"])
+ if duration_sec > config.DURATION_LIMIT:
+ return await mystic.edit_text(
+ _["play_6"].format(config.DURATION_LIMIT_MIN, app.mention)
+ )
+ else:
+ buttons = livestream_markup(
+ _,
+ track_id,
+ CallbackQuery.from_user.id,
+ mode,
+ "c" if cplay == "c" else "g",
+ "f" if fplay else "d",
+ )
+ return await mystic.edit_text(
+ _["play_13"],
+ reply_markup=InlineKeyboardMarkup(buttons),
+ )
+ video = True if mode == "v" else None
+ ffplay = True if fplay == "f" else None
+ try:
+ await stream(
+ _,
+ mystic,
+ CallbackQuery.from_user.id,
+ details,
+ chat_id,
+ user_name,
+ CallbackQuery.message.chat.id,
+ video,
+ streamtype="youtube",
+ forceplay=ffplay,
+ )
+ except Exception as e:
+ ex_type = type(e).__name__
+ err = e if ex_type == "AssistantErr" else _["general_2"].format(ex_type)
+ return await mystic.edit_text(err)
+ return await mystic.delete()
+
+
+@app.on_callback_query(filters.regex("AnonymousAdmin") & ~BANNED_USERS)
+async def anonymous_check(client, CallbackQuery):
+ try:
+ await CallbackQuery.answer(
+ "❖ ʀᴇᴠᴇʀᴛ ʙᴀᴄᴋ ᴛᴏ ᴜsᴇʀ ᴀᴄᴄᴏᴜɴᴛ ⏤͟͟͞͞★\n\n● ᴏᴘᴇɴ ʏᴏᴜʀ ɢʀᴏᴜᴘ sᴇᴛᴛɪɴɢs.\n● ᴀᴅᴍɪɴɪsᴛʀᴀᴛᴏʀs\n● ᴄʟɪᴄᴋ ᴏɴ ʏᴏᴜʀ ɴᴀᴍᴇ\n● ᴜɴᴄʜᴇᴄᴋ ᴀɴᴏɴʏᴍᴏᴜs ᴀᴅᴍɪɴ ᴘᴇʀᴍɪssɪᴏɴs.",
+ show_alert=True,
+ )
+ except:
+ pass
+
+
+@app.on_callback_query(filters.regex("AnonyPlaylists") & ~BANNED_USERS)
+@languageCB
+async def play_playlists_command(client, CallbackQuery, _):
+ callback_data = CallbackQuery.data.strip()
+ callback_request = callback_data.split(None, 1)[1]
+ (
+ videoid,
+ user_id,
+ ptype,
+ mode,
+ cplay,
+ fplay,
+ ) = callback_request.split("|")
+ if CallbackQuery.from_user.id != int(user_id):
+ try:
+ return await CallbackQuery.answer(_["playcb_1"], show_alert=True)
+ except:
+ return
+ try:
+ chat_id, channel = await get_channeplayCB(_, cplay, CallbackQuery)
+ except:
+ return
+ user_name = CallbackQuery.from_user.first_name
+ await CallbackQuery.message.delete()
+ try:
+ await CallbackQuery.answer()
+ except:
+ pass
+ mystic = await CallbackQuery.message.reply_text(
+ _["play_2"].format(channel) if channel else _["play_1"]
+ )
+ videoid = lyrical.get(videoid)
+ video = True if mode == "v" else None
+ ffplay = True if fplay == "f" else None
+ spotify = True
+ if ptype == "yt":
+ spotify = False
+ try:
+ result = await YouTube.playlist(
+ videoid,
+ config.PLAYLIST_FETCH_LIMIT,
+ CallbackQuery.from_user.id,
+ True,
+ )
+ except:
+ return await mystic.edit_text(_["play_3"])
+ if ptype == "spplay":
+ try:
+ result, spotify_id = await Spotify.playlist(videoid)
+ except:
+ return await mystic.edit_text(_["play_3"])
+ if ptype == "spalbum":
+ try:
+ result, spotify_id = await Spotify.album(videoid)
+ except:
+ return await mystic.edit_text(_["play_3"])
+ if ptype == "spartist":
+ try:
+ result, spotify_id = await Spotify.artist(videoid)
+ except:
+ return await mystic.edit_text(_["play_3"])
+ if ptype == "apple":
+ try:
+ result, apple_id = await Apple.playlist(videoid, True)
+ except:
+ return await mystic.edit_text(_["play_3"])
+ try:
+ await stream(
+ _,
+ mystic,
+ user_id,
+ result,
+ chat_id,
+ user_name,
+ CallbackQuery.message.chat.id,
+ video,
+ streamtype="playlist",
+ spotify=spotify,
+ forceplay=ffplay,
+ )
+ except Exception as e:
+ ex_type = type(e).__name__
+ err = e if ex_type == "AssistantErr" else _["general_2"].format(ex_type)
+ return await mystic.edit_text(err)
+ return await mystic.delete()
+
+
+@app.on_callback_query(filters.regex("slider") & ~BANNED_USERS)
+@languageCB
+async def slider_queries(client, CallbackQuery, _):
+ callback_data = CallbackQuery.data.strip()
+ callback_request = callback_data.split(None, 1)[1]
+ (
+ what,
+ rtype,
+ query,
+ user_id,
+ cplay,
+ fplay,
+ ) = callback_request.split("|")
+ if CallbackQuery.from_user.id != int(user_id):
+ try:
+ return await CallbackQuery.answer(_["playcb_1"], show_alert=True)
+ except:
+ return
+ what = str(what)
+ rtype = int(rtype)
+ if what == "F":
+ if rtype == 9:
+ query_type = 0
+ else:
+ query_type = int(rtype + 1)
+ try:
+ await CallbackQuery.answer(_["playcb_2"])
+ except:
+ pass
+ title, duration_min, thumbnail, vidid = await YouTube.slider(query, query_type)
+ buttons = slider_markup(_, vidid, user_id, query, query_type, cplay, fplay)
+ med = InputMediaPhoto(
+ media=thumbnail,
+ caption=_["play_10"].format(
+ title.title(),
+ duration_min,
+ ),
+ )
+ return await CallbackQuery.edit_message_media(
+ media=med, reply_markup=InlineKeyboardMarkup(buttons)
+ )
+ if what == "B":
+ if rtype == 0:
+ query_type = 9
+ else:
+ query_type = int(rtype - 1)
+ try:
+ await CallbackQuery.answer(_["playcb_2"])
+ except:
+ pass
+ title, duration_min, thumbnail, vidid = await YouTube.slider(query, query_type)
+ buttons = slider_markup(_, vidid, user_id, query, query_type, cplay, fplay)
+ med = InputMediaPhoto(
+ media=thumbnail,
+ caption=_["play_10"].format(
+ title.title(),
+ duration_min,
+ ),
+ )
+ return await CallbackQuery.edit_message_media(
+ media=med, reply_markup=InlineKeyboardMarkup(buttons)
+ )
\ No newline at end of file
diff --git a/SprotifyMusic/plugins/play/playlist,py b/SprotifyMusic/plugins/play/playlist,py
new file mode 100644
index 000000000000..875010d41590
--- /dev/null
+++ b/SprotifyMusic/plugins/play/playlist,py
@@ -0,0 +1,331 @@
+import os
+from random import randint
+
+from pykeyboard import InlineKeyboard
+from pyrogram import filters
+from pyrogram.types import (InlineKeyboardButton,
+ InlineKeyboardMarkup, Message)
+
+from config import BANNED_USERS, SERVER_PLAYLIST_LIMIT
+from strings import get_command
+from SprotifyMusic import Carbon, YouTube, app
+from SprotifyMusic.utils.database import (delete_playlist, get_playlist,
+ get_playlist_names, save_playlist)
+from SprotifyMusic.utils.decorators.language import language, languageCB
+from SprotifyMusic.utils.inline.playlist import (botplaylist_markup,
+ get_playlist_markup,
+ warning_markup)
+from SprotifyMusic.utils.pastebin import Bin
+from SprotifyMusic.utils.stream.stream import stream
+
+# Command
+PLAYLIST_COMMAND = get_command("PLAYLIST_COMMAND")
+DELETEPLAYLIST_COMMAND = get_command("DELETEPLAYLIST_COMMAND")
+
+
+@app.on_message(
+ filters.command(PLAYLIST_COMMAND)
+ & ~filters.edited
+ & ~BANNED_USERS
+)
+@language
+async def check_playlist(client, message: Message, _):
+ _playlist = await get_playlist_names(message.from_user.id)
+ if _playlist:
+ get = await message.reply_text(_["playlist_2"])
+ else:
+ return await message.reply_text(_["playlist_3"])
+ msg = _["playlist_4"]
+ count = 0
+ for shikhar in _playlist:
+ _note = await get_playlist(message.from_user.id, shikhar)
+ title = _note["title"]
+ title = title.title()
+ duration = _note["duration"]
+ count += 1
+ msg += f"\n\n{count}- {title[:70]}\n"
+ msg += _["playlist_5"].format(duration)
+ link = await Bin(msg)
+ lines = msg.count("\n")
+ if lines >= 17:
+ car = os.linesep.join(msg.split(os.linesep)[:17])
+ else:
+ car = msg
+ carbon = await Carbon.generate(car, randint(100, 10000000000))
+ await get.delete()
+ await message.reply_photo(
+ carbon, caption=_["playlist_15"].format(link)
+ )
+
+
+@app.on_message(
+ filters.command(DELETEPLAYLIST_COMMAND)
+ & filters.group
+ & ~filters.edited
+ & ~BANNED_USERS
+)
+@language
+async def del_group_message(client, message: Message, _):
+ upl = InlineKeyboardMarkup(
+ [
+ [
+ InlineKeyboardButton(
+ text=_["PL_B_6"],
+ url=f"https://t.me/{app.username}?start=delplaylists",
+ ),
+ ]
+ ]
+ )
+ await message.reply_text(_["playlist_6"], reply_markup=upl)
+
+
+async def get_keyboard(_, user_id):
+ keyboard = InlineKeyboard(row_width=5)
+ _playlist = await get_playlist_names(user_id)
+ count = len(_playlist)
+ for x in _playlist:
+ _note = await get_playlist(user_id, x)
+ title = _note["title"]
+ title = title.title()
+ keyboard.row(
+ InlineKeyboardButton(
+ text=title,
+ callback_data=f"del_playlist {x}",
+ )
+ )
+ keyboard.row(
+ InlineKeyboardButton(
+ text=_["PL_B_5"],
+ callback_data=f"delete_warning",
+ ),
+ InlineKeyboardButton(
+ text=_["CLOSE_BUTTON"], callback_data=f"close"
+ ),
+ )
+ return keyboard, count
+
+
+@app.on_message(
+ filters.command(DELETEPLAYLIST_COMMAND)
+ & filters.private
+ & ~filters.edited
+ & ~BANNED_USERS
+)
+@language
+async def del_plist_msg(client, message: Message, _):
+ _playlist = await get_playlist_names(message.from_user.id)
+ if _playlist:
+ get = await message.reply_text(_["playlist_2"])
+ else:
+ return await message.reply_text(_["playlist_3"])
+ keyboard, count = await get_keyboard(_, message.from_user.id)
+ await get.edit_text(
+ _["playlist_7"].format(count), reply_markup=keyboard
+ )
+
+
+@app.on_callback_query(filters.regex("play_playlist") & ~BANNED_USERS)
+@languageCB
+async def play_playlist(client, CallbackQuery, _):
+ callback_data = CallbackQuery.data.strip()
+ mode = callback_data.split(None, 1)[1]
+ user_id = CallbackQuery.from_user.id
+ _playlist = await get_playlist_names(user_id)
+ if not _playlist:
+ try:
+ return await CallbackQuery.answer(
+ _["playlist_3"],
+ show_alert=True,
+ )
+ except:
+ return
+ chat_id = CallbackQuery.message.chat.id
+ user_name = CallbackQuery.from_user.first_name
+ await CallbackQuery.message.delete()
+ result = []
+ try:
+ await CallbackQuery.answer()
+ except:
+ pass
+ video = True if mode == "v" else None
+ mystic = await CallbackQuery.message.reply_text(_["play_1"])
+ for vidids in _playlist:
+ result.append(vidids)
+ try:
+ await stream(
+ _,
+ mystic,
+ user_id,
+ result,
+ chat_id,
+ user_name,
+ CallbackQuery.message.chat.id,
+ video,
+ streamtype="playlist",
+ )
+ except Exception as e:
+ ex_type = type(e).__name__
+ err = (
+ e
+ if ex_type == "AssistantErr"
+ else _["general_3"].format(ex_type)
+ )
+ return await mystic.edit_text(err)
+ return await mystic.delete()
+
+
+@app.on_callback_query(filters.regex("add_playlist") & ~BANNED_USERS)
+@languageCB
+async def add_playlist(client, CallbackQuery, _):
+ callback_data = CallbackQuery.data.strip()
+ videoid = callback_data.split(None, 1)[1]
+ user_id = CallbackQuery.from_user.id
+ _check = await get_playlist(user_id, videoid)
+ if _check:
+ try:
+ return await CallbackQuery.answer(
+ _["playlist_8"], show_alert=True
+ )
+ except:
+ return
+ _count = await get_playlist_names(user_id)
+ count = len(_count)
+ if count == SERVER_PLAYLIST_LIMIT:
+ try:
+ return await CallbackQuery.answer(
+ _["playlist_9"].format(SERVER_PLAYLIST_LIMIT),
+ show_alert=True,
+ )
+ except:
+ return
+ (
+ title,
+ duration_min,
+ duration_sec,
+ thumbnail,
+ vidid,
+ ) = await YouTube.details(videoid, True)
+ title = (title[:50]).title()
+ plist = {
+ "videoid": vidid,
+ "title": title,
+ "duration": duration_min,
+ }
+ await save_playlist(user_id, videoid, plist)
+ try:
+ title = (title[:30]).title()
+ return await CallbackQuery.answer(
+ _["playlist_10"].format(title), show_alert=True
+ )
+ except:
+ return
+
+
+@app.on_callback_query(filters.regex("del_playlist") & ~BANNED_USERS)
+@languageCB
+async def del_plist(client, CallbackQuery, _):
+ callback_data = CallbackQuery.data.strip()
+ videoid = callback_data.split(None, 1)[1]
+ user_id = CallbackQuery.from_user.id
+ deleted = await delete_playlist(
+ CallbackQuery.from_user.id, videoid
+ )
+ if deleted:
+ try:
+ await CallbackQuery.answer(
+ _["playlist_11"], show_alert=True
+ )
+ except:
+ pass
+ else:
+ try:
+ return await CallbackQuery.answer(
+ _["playlist_12"], show_alert=True
+ )
+ except:
+ return
+ keyboard, count = await get_keyboard(_, user_id)
+ return await CallbackQuery.edit_message_reply_markup(
+ reply_markup=keyboard
+ )
+
+
+@app.on_callback_query(
+ filters.regex("delete_whole_playlist") & ~BANNED_USERS
+)
+@languageCB
+async def del_whole_playlist(client, CallbackQuery, _):
+ _playlist = await get_playlist_names(CallbackQuery.from_user.id)
+ for x in _playlist:
+ await delete_playlist(CallbackQuery.from_user.id, x)
+ return await CallbackQuery.edit_message_text(_["playlist_13"])
+
+
+@app.on_callback_query(
+ filters.regex("get_playlist_playmode") & ~BANNED_USERS
+)
+@languageCB
+async def get_playlist_playmode_(client, CallbackQuery, _):
+ try:
+ await CallbackQuery.answer()
+ except:
+ pass
+ buttons = get_playlist_markup(_)
+ return await CallbackQuery.edit_message_reply_markup(
+ reply_markup=InlineKeyboardMarkup(buttons)
+ )
+
+
+@app.on_callback_query(
+ filters.regex("delete_warning") & ~BANNED_USERS
+)
+@languageCB
+async def delete_warning_message(client, CallbackQuery, _):
+ try:
+ await CallbackQuery.answer()
+ except:
+ pass
+ upl = warning_markup(_)
+ return await CallbackQuery.edit_message_text(
+ _["playlist_14"], reply_markup=upl
+ )
+
+
+@app.on_callback_query(filters.regex("home_play") & ~BANNED_USERS)
+@languageCB
+async def home_play_(client, CallbackQuery, _):
+ try:
+ await CallbackQuery.answer()
+ except:
+ pass
+ buttons = botplaylist_markup(_)
+ return await CallbackQuery.edit_message_reply_markup(
+ reply_markup=InlineKeyboardMarkup(buttons)
+ )
+
+
+@app.on_callback_query(
+ filters.regex("del_back_playlist") & ~BANNED_USERS
+)
+@languageCB
+async def del_back_playlist(client, CallbackQuery, _):
+ user_id = CallbackQuery.from_user.id
+ _playlist = await get_playlist_names(user_id)
+ if _playlist:
+ try:
+ await CallbackQuery.answer(
+ _["playlist_2"], show_alert=True
+ )
+ except:
+ pass
+ else:
+ try:
+ return await CallbackQuery.answer(
+ _["playlist_3"], show_alert=True
+ )
+ except:
+ return
+ keyboard, count = await get_keyboard(_, user_id)
+ return await CallbackQuery.edit_message_text(
+ _["playlist_7"].format(count), reply_markup=keyboard
+ )
\ No newline at end of file
diff --git a/SprotifyMusic/plugins/play/playmode.py b/SprotifyMusic/plugins/play/playmode.py
new file mode 100644
index 000000000000..365fbe7aeb09
--- /dev/null
+++ b/SprotifyMusic/plugins/play/playmode.py
@@ -0,0 +1,33 @@
+from pyrogram import filters
+from pyrogram.types import InlineKeyboardMarkup, Message
+
+from SprotifyMusic import app
+from SprotifyMusic.utils.database import get_playmode, get_playtype, is_nonadmin_chat
+from SprotifyMusic.utils.decorators import language
+from SprotifyMusic.utils.inline.settings import playmode_users_markup
+from config import BANNED_USERS
+
+
+@app.on_message(filters.command(["playmode", "mode"]) & filters.group & ~BANNED_USERS)
+@language
+async def playmode_(client, message: Message, _):
+ playmode = await get_playmode(message.chat.id)
+ if playmode == "Direct":
+ Direct = True
+ else:
+ Direct = None
+ is_non_admin = await is_nonadmin_chat(message.chat.id)
+ if not is_non_admin:
+ Group = True
+ else:
+ Group = None
+ playty = await get_playtype(message.chat.id)
+ if playty == "Everyone":
+ Playtype = None
+ else:
+ Playtype = True
+ buttons = playmode_users_markup(_, Direct, Group, Playtype)
+ response = await message.reply_text(
+ _["play_22"].format(message.chat.title),
+ reply_markup=InlineKeyboardMarkup(buttons),
+ )
diff --git a/SprotifyMusic/plugins/sudo/active.py b/SprotifyMusic/plugins/sudo/active.py
new file mode 100644
index 000000000000..f934f041ad54
--- /dev/null
+++ b/SprotifyMusic/plugins/sudo/active.py
@@ -0,0 +1,120 @@
+from pyrogram import Client, filters
+from pyrogram.types import InlineKeyboardButton, InlineKeyboardMarkup, Message
+from unidecode import unidecode
+
+from SprotifyMusic import app
+from SprotifyMusic.misc import SUDOERS
+from SprotifyMusic.utils.database import (
+ get_active_chats,
+ get_active_video_chats,
+ remove_active_chat,
+ remove_active_video_chat,
+)
+
+
+async def generate_join_link(chat_id: int):
+ try:
+ invite_link = await app.export_chat_invite_link(chat_id)
+ return invite_link
+ except Exception as e:
+ print(f"Error generating join link for chat {chat_id}: {e}")
+ return None
+
+
+def ordinal(n):
+ suffix = ["th", "st", "nd", "rd", "th"][min(n % 10, 4)]
+ if 11 <= (n % 100) <= 13:
+ suffix = "th"
+ return str(n) + suffix
+
+
+@app.on_message(filters.command(["activevc", "activevoice"]) & SUDOERS)
+async def activevc(_, message: Message):
+ mystic = await message.reply_text("» ɢᴇᴛᴛɪɴɢ ᴀᴄᴛɪᴠᴇ ᴠᴏɪᴄᴇ ᴄʜᴀᴛs ʟɪsᴛ...")
+ served_chats = await get_active_chats()
+ text = ""
+ j = 0
+ buttons = []
+ for x in served_chats:
+ try:
+ chat_info = await app.get_chat(x)
+ title = chat_info.title or "Unknown"
+ invite_link = await generate_join_link(x)
+ if not invite_link:
+ await remove_active_chat(x)
+ continue
+ except Exception as e:
+ print(f"Error processing chat {x}: {e}")
+ await remove_active_chat(x)
+ continue
+
+ if chat_info.username:
+ user = chat_info.username
+ text += f"{j + 1}. {unidecode(title).upper()} [{x}
]\n"
+ else:
+ text += f"{j + 1}. {unidecode(title).upper()} [{x}
]\n"
+
+ button_text = f"{ordinal(j + 1)} ɢʀᴏᴜᴘ "
+ buttons.append([InlineKeyboardButton(button_text, url=invite_link)])
+ j += 1
+
+ if not text:
+ await mystic.edit_text("» ɴᴏ ᴀᴄᴛɪᴠᴇ ᴠᴏɪᴄᴇ ᴄʜᴀᴛs ғᴏᴜɴᴅ.")
+ else:
+ await mystic.edit_text(
+ f"» ʟɪsᴛ ᴏғ ᴄᴜʀʀᴇɴᴛʟʏ ᴀᴄᴛɪᴠᴇ ᴠᴏɪᴄᴇ ᴄʜᴀᴛs :\n\n{text}",
+ reply_markup=InlineKeyboardMarkup(buttons),
+ disable_web_page_preview=True,
+ )
+
+
+@app.on_message(filters.command(["activevd", "activevideo"]) & SUDOERS)
+async def activevi_(_, message: Message):
+ mystic = await message.reply_text("» ɢᴇᴛᴛɪɴɢ ᴀᴄᴛɪᴠᴇ ᴠɪᴅᴇᴏ ᴄʜᴀᴛs ʟɪsᴛ...")
+ served_chats = await get_active_video_chats()
+ text = ""
+ j = 0
+ buttons = []
+ for x in served_chats:
+ try:
+ chat_info = await app.get_chat(x)
+ title = chat_info.title or "Unknown"
+ invite_link = await generate_join_link(x)
+ if not invite_link:
+ await remove_active_video_chat(x)
+ continue
+ except Exception as e:
+ print(f"Error processing chat {x}: {e}")
+ await remove_active_video_chat(x)
+ continue
+
+ if chat_info.username:
+ user = chat_info.username
+ text += f"{j + 1}. {unidecode(title).upper()} [{x}
]\n"
+ else:
+ text += f"{j + 1}. {unidecode(title).upper()} [{x}
]\n"
+
+ button_text = f"{ordinal(j + 1)} ɢʀᴏᴜᴘ "
+ buttons.append([InlineKeyboardButton(button_text, url=invite_link)])
+ j += 1
+
+ if not text:
+ await mystic.edit_text("» ɴᴏ ᴀᴄᴛɪᴠᴇ ᴠɪᴅᴇᴏ ᴄʜᴀᴛs ғᴏᴜɴᴅ.")
+ else:
+ await mystic.edit_text(
+ f"» ʟɪsᴛ ᴏғ ᴄᴜʀʀᴇɴᴛʟʏ ᴀᴄᴛɪᴠᴇ ᴠɪᴅᴇᴏ ᴄʜᴀᴛs :\n\n{text}",
+ reply_markup=InlineKeyboardMarkup(buttons),
+ disable_web_page_preview=True,
+ )
+
+
+@app.on_message(filters.command("active") & SUDOERS)
+async def start(client: Client, message: Message):
+ ac_audio = str(len(await get_active_chats()))
+ ac_video = str(len(await get_active_video_chats()))
+ await message.reply_text(
+ f"❖ ᴀᴄᴛɪᴠᴇ ᴄʜᴀᴛs ɪɴғᴏ :\n\nᴠᴏɪᴄᴇ : {ac_audio}\nᴠɪᴅᴇᴏ : {ac_video}",
+ reply_markup=InlineKeyboardMarkup(
+ [[InlineKeyboardButton("ᴄʟᴏsᴇ", callback_data="close")]]
+ ),
+ )
\ No newline at end of file
diff --git a/SprotifyMusic/plugins/sudo/autoend.py b/SprotifyMusic/plugins/sudo/autoend.py
new file mode 100644
index 000000000000..e420535f3178
--- /dev/null
+++ b/SprotifyMusic/plugins/sudo/autoend.py
@@ -0,0 +1,24 @@
+from pyrogram import filters
+from pyrogram.types import Message
+
+from SprotifyMusic import app
+from SprotifyMusic.misc import SUDOERS
+from SprotifyMusic.utils.database import autoend_off, autoend_on
+
+
+@app.on_message(filters.command("autoend") & SUDOERS)
+async def auto_end_stream(_, message: Message):
+ usage = "❖ ᴇxᴀᴍᴘʟᴇ ➥ /autoend [ᴇɴᴀʙʟᴇ | ᴅɪsᴀʙʟᴇ]"
+ if len(message.command) != 2:
+ return await message.reply_text(usage)
+ state = message.text.split(None, 1)[1].strip().lower()
+ if state == "enable":
+ await autoend_on()
+ await message.reply_text(
+ "❖ ᴀᴜᴛᴏ ᴇɴᴅ sᴛʀᴇᴀᴍ ᴇɴᴀʙʟᴇᴅ.\n\n● ᴀssɪsᴛᴀɴᴛ ᴡɪʟʟ ᴀᴜᴛᴏᴍᴀᴛɪᴄᴀʟʟʏ ʟᴇᴀᴠᴇ ᴛʜᴇ ᴠɪᴅᴇᴏᴄʜᴀᴛ ᴀғᴛᴇʀ ғᴇᴡ ᴍɪɴs ᴡʜᴇɴ ɴᴏ ᴏɴᴇ ɪs ʟɪsᴛᴇɴɪɴɢ."
+ )
+ elif state == "disable":
+ await autoend_off()
+ await message.reply_text("❖ ᴀᴜᴛᴏ ᴇɴᴅ sᴛʀᴇᴀᴍ ᴅɪsᴀʙʟᴇᴅ.")
+ else:
+ await message.reply_text(usage)
diff --git a/SprotifyMusic/plugins/sudo/blchat.py b/SprotifyMusic/plugins/sudo/blchat.py
new file mode 100644
index 000000000000..f3f058d10173
--- /dev/null
+++ b/SprotifyMusic/plugins/sudo/blchat.py
@@ -0,0 +1,61 @@
+from pyrogram import filters
+from pyrogram.types import Message
+
+from SprotifyMusic import app
+from SprotifyMusic.misc import SUDOERS
+from SprotifyMusic.utils.database import blacklist_chat, blacklisted_chats, whitelist_chat
+from SprotifyMusic.utils.decorators.language import language
+from config import BANNED_USERS
+
+
+@app.on_message(filters.command(["blchat", "blacklistchat"]) & SUDOERS)
+@language
+async def blacklist_chat_func(client, message: Message, _):
+ if len(message.command) != 2:
+ return await message.reply_text(_["black_1"])
+ chat_id = int(message.text.strip().split()[1])
+ if chat_id in await blacklisted_chats():
+ return await message.reply_text(_["black_2"])
+ blacklisted = await blacklist_chat(chat_id)
+ if blacklisted:
+ await message.reply_text(_["black_3"])
+ else:
+ await message.reply_text(_["black_9"])
+ try:
+ await app.leave_chat(chat_id)
+ except:
+ pass
+
+
+@app.on_message(
+ filters.command(["whitelistchat", "unblacklistchat", "unblchat"]) & SUDOERS
+)
+@language
+async def white_funciton(client, message: Message, _):
+ if len(message.command) != 2:
+ return await message.reply_text(_["black_4"])
+ chat_id = int(message.text.strip().split()[1])
+ if chat_id not in await blacklisted_chats():
+ return await message.reply_text(_["black_5"])
+ whitelisted = await whitelist_chat(chat_id)
+ if whitelisted:
+ return await message.reply_text(_["black_6"])
+ await message.reply_text(_["black_9"])
+
+
+@app.on_message(filters.command(["blchats", "blacklistedchats"]) & ~BANNED_USERS)
+@language
+async def all_chats(client, message: Message, _):
+ text = _["black_7"]
+ j = 0
+ for count, chat_id in enumerate(await blacklisted_chats(), 1):
+ try:
+ title = (await app.get_chat(chat_id)).title
+ except:
+ title = "ᴘʀɪᴠᴀᴛᴇ ᴄʜᴀᴛ"
+ j = 1
+ text += f"{count}. {title}[{chat_id}
]\n"
+ if j == 0:
+ await message.reply_text(_["black_8"].format(app.mention))
+ else:
+ await message.reply_text(text)
diff --git a/SprotifyMusic/plugins/sudo/block.py b/SprotifyMusic/plugins/sudo/block.py
new file mode 100644
index 000000000000..d0081901f517
--- /dev/null
+++ b/SprotifyMusic/plugins/sudo/block.py
@@ -0,0 +1,59 @@
+from pyrogram import filters
+from pyrogram.types import Message
+
+from SprotifyMusic import app
+from SprotifyMusic.misc import SUDOERS
+from SprotifyMusic.utils.database import add_gban_user, remove_gban_user
+from SprotifyMusic.utils.decorators.language import language
+from SprotifyMusic.utils.extraction import extract_user
+from config import BANNED_USERS
+
+
+@app.on_message(filters.command(["block"]) & SUDOERS)
+@language
+async def useradd(client, message: Message, _):
+ if not message.reply_to_message:
+ if len(message.command) != 2:
+ return await message.reply_text(_["general_1"])
+ user = await extract_user(message)
+ if user.id in BANNED_USERS:
+ return await message.reply_text(_["block_1"].format(user.mention))
+ await add_gban_user(user.id)
+ BANNED_USERS.add(user.id)
+ await message.reply_text(_["block_2"].format(user.mention))
+
+
+@app.on_message(filters.command(["unblock"]) & SUDOERS)
+@language
+async def userdel(client, message: Message, _):
+ if not message.reply_to_message:
+ if len(message.command) != 2:
+ return await message.reply_text(_["general_1"])
+ user = await extract_user(message)
+ if user.id not in BANNED_USERS:
+ return await message.reply_text(_["block_3"].format(user.mention))
+ await remove_gban_user(user.id)
+ BANNED_USERS.remove(user.id)
+ await message.reply_text(_["block_4"].format(user.mention))
+
+
+@app.on_message(filters.command(["blocked", "blockedusers", "blusers"]) & SUDOERS)
+@language
+async def sudoers_list(client, message: Message, _):
+ if not BANNED_USERS:
+ return await message.reply_text(_["block_5"])
+ mystic = await message.reply_text(_["block_6"])
+ msg = _["block_7"]
+ count = 0
+ for users in BANNED_USERS:
+ try:
+ user = await app.get_users(users)
+ user = user.first_name if not user.mention else user.mention
+ count += 1
+ except:
+ continue
+ msg += f"❖ {count} ➥ {user}\n"
+ if count == 0:
+ return await mystic.edit_text(_["block_5"])
+ else:
+ return await mystic.edit_text(msg)
diff --git a/SprotifyMusic/plugins/sudo/dev.py b/SprotifyMusic/plugins/sudo/dev.py
new file mode 100644
index 000000000000..d8a336c6abe9
--- /dev/null
+++ b/SprotifyMusic/plugins/sudo/dev.py
@@ -0,0 +1,201 @@
+import os
+import re
+import subprocess
+import sys
+import traceback
+from inspect import getfullargspec
+from io import StringIO
+from time import time
+
+from pyrogram import filters
+from pyrogram.types import InlineKeyboardButton, InlineKeyboardMarkup, Message
+
+from SprotifyMusic import app
+from SprotifyMusic.misc import SUDOERS
+from SprotifyMusic.utils.cleanmode import protect_message
+
+
+async def aexec(code, client, message):
+ exec(
+ "async def __aexec(client, message): "
+ + "".join(f"\n {a}" for a in code.split("\n"))
+ )
+ return await locals()["__aexec"](client, message)
+
+
+async def edit_or_reply(msg: Message, **kwargs):
+ func = msg.edit_text if msg.from_user.is_self else msg.reply
+ spec = getfullargspec(func.__wrapped__).args
+ await func(**{k: v for k, v in kwargs.items() if k in spec})
+ await protect_message(msg.chat.id, msg.id)
+
+
+@app.on_edited_message(
+ filters.command(["ev", "eval"]) & SUDOERS & ~filters.forwarded & ~filters.via_bot
+)
+@app.on_message(
+ filters.command(["ev", "eval"]) & SUDOERS & ~filters.forwarded & ~filters.via_bot
+)
+async def executor(client: app, message: Message):
+ if len(message.command) < 2:
+ return await edit_or_reply(message, text="ᴡʜᴀᴛ ʏᴏᴜ ᴡᴀɴɴᴀ ᴇxᴇᴄᴜᴛᴇ ʙᴀʙʏ ?")
+ try:
+ cmd = message.text.split(" ", maxsplit=1)[1]
+ except IndexError:
+ return await message.delete()
+ t1 = time()
+ old_stderr = sys.stderr
+ old_stdout = sys.stdout
+ redirected_output = sys.stdout = StringIO()
+ redirected_error = sys.stderr = StringIO()
+ stdout, stderr, exc = None, None, None
+ try:
+ await aexec(cmd, client, message)
+ except Exception:
+ exc = traceback.format_exc()
+ stdout = redirected_output.getvalue()
+ stderr = redirected_error.getvalue()
+ sys.stdout = old_stdout
+ sys.stderr = old_stderr
+ evaluation = "\n"
+ if exc:
+ evaluation += exc
+ elif stderr:
+ evaluation += stderr
+ elif stdout:
+ evaluation += stdout
+ else:
+ evaluation += "Success"
+ final_output = f"⥤ ʀᴇsᴜʟᴛ :\n{evaluation}" + if len(final_output) > 4096: + filename = "output.txt" + with open(filename, "w+", encoding="utf8") as out_file: + out_file.write(str(evaluation)) + t2 = time() + keyboard = InlineKeyboardMarkup( + [ + [ + InlineKeyboardButton( + text="⏳", + callback_data=f"runtime {t2-t1} Seconds", + ) + ] + ] + ) + await message.reply_document( + document=filename, + caption=f"⥤ ᴇᴠᴀʟ :\n
{cmd[0:980]}
\n\n⥤ ʀᴇsᴜʟᴛ :\nAttached Document",
+ quote=False,
+ reply_markup=keyboard,
+ )
+ await message.delete()
+ os.remove(filename)
+ else:
+ t2 = time()
+ keyboard = InlineKeyboardMarkup(
+ [
+ [
+ InlineKeyboardButton(
+ text="⏳",
+ callback_data=f"runtime {round(t2-t1, 3)} Seconds",
+ ),
+ InlineKeyboardButton(
+ text="🗑",
+ callback_data=f"forceclose abc|{message.from_user.id}",
+ ),
+ ]
+ ]
+ )
+ await edit_or_reply(message, text=final_output, reply_markup=keyboard)
+
+
+@app.on_callback_query(filters.regex(r"runtime"))
+async def runtime_func_cq(_, cq):
+ runtime = cq.data.split(None, 1)[1]
+ await cq.answer(runtime, show_alert=True)
+
+
+@app.on_callback_query(filters.regex("forceclose"))
+async def forceclose_command(_, CallbackQuery):
+ callback_data = CallbackQuery.data.strip()
+ callback_request = callback_data.split(None, 1)[1]
+ query, user_id = callback_request.split("|")
+ if CallbackQuery.from_user.id != int(user_id):
+ try:
+ return await CallbackQuery.answer(
+ "» ɪᴛ'ʟʟ ʙᴇ ʙᴇᴛᴛᴇʀ ɪғ ʏᴏᴜ sᴛᴀʏ ɪɴ ʏᴏᴜʀ ʟɪᴍɪᴛs ʙᴀʙʏ.", show_alert=True
+ )
+ except:
+ return
+ await CallbackQuery.message.delete()
+ try:
+ await CallbackQuery.answer()
+ except:
+ return
+
+
+@app.on_edited_message(
+ filters.command("sh") & SUDOERS & ~filters.forwarded & ~filters.via_bot
+)
+@app.on_message(filters.command("sh") & SUDOERS & ~filters.forwarded & ~filters.via_bot)
+async def shellrunner(_, message: Message):
+ if len(message.command) < 2:
+ return await edit_or_reply(message, text="ᴇxᴀᴍᴩʟᴇ :\n/sh git pull")
+ text = message.text.split(None, 1)[1]
+ if "\n" in text:
+ code = text.split("\n")
+ output = ""
+ for x in code:
+ shell = re.split(""" (?=(?:[^'"]|'[^']*'|"[^"]*")*$)""", x)
+ try:
+ process = subprocess.Popen(
+ shell,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE,
+ )
+ except Exception as err:
+ await edit_or_reply(message, text=f"ERROR :\n{err}") + output += f"{code}\n" + output += process.stdout.read()[:-1].decode("utf-8") + output += "\n" + else: + shell = re.split(""" (?=(?:[^'"]|'[^']*'|"[^"]*")*$)""", text) + for a in range(len(shell)): + shell[a] = shell[a].replace('"', "") + try: + process = subprocess.Popen( + shell, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + ) + except Exception as err: + print(err) + exc_type, exc_obj, exc_tb = sys.exc_info() + errors = traceback.format_exception( + etype=exc_type, + value=exc_obj, + tb=exc_tb, + ) + return await edit_or_reply( + message, text=f"ERROR :\n
{''.join(errors)}" + ) + output = process.stdout.read()[:-1].decode("utf-8") + if str(output) == "\n": + output = None + if output: + if len(output) > 4096: + with open("output.txt", "w+") as file: + file.write(output) + await app.send_document( + message.chat.id, + "output.txt", + reply_to_message_id=message.id, + caption="
Output
",
+ )
+ return os.remove("output.txt")
+ await edit_or_reply(message, text=f"OUTPUT :\n{output}") + else: + await edit_or_reply(message, text="OUTPUT :\n
None
")
+
+ await message.stop_propagation()
+
diff --git a/SprotifyMusic/plugins/sudo/filemanager.py b/SprotifyMusic/plugins/sudo/filemanager.py
new file mode 100644
index 000000000000..d64e90a86ce9
--- /dev/null
+++ b/SprotifyMusic/plugins/sudo/filemanager.py
@@ -0,0 +1,172 @@
+import io
+import os
+import time
+import shutil
+from inspect import signature
+from os.path import exists, isdir
+
+from pyrogram import filters
+from pyrogram.types import Message
+
+from SprotifyMusic import app
+from SprotifyMusic.misc import SUDOERS
+
+MAX_MESSAGE_SIZE_LIMIT = 4095
+
+
+def humanbytes(size):
+ """Convert bytes into a human-readable format."""
+ for unit in ["B", "KB", "MB", "GB", "TB", "PB"]:
+ if size < 1024:
+ return f"{size:.2f} {unit}"
+ size /= 1024
+
+
+def split_limits(text, limit=MAX_MESSAGE_SIZE_LIMIT):
+ """Split text into chunks that do not exceed the given limit."""
+ return [text[i: i + limit] for i in range(0, len(text), limit)]
+
+
+async def eor(msg: Message, **kwargs):
+ """Edit or reply to a message."""
+ if msg.from_user and msg.from_user.is_self:
+ func = msg.edit_text
+ else:
+ func = msg.reply
+
+ # Get valid arguments for the function
+ valid_args = signature(func).parameters.keys()
+ filtered_kwargs = {k: v for k, v in kwargs.items() if k in valid_args}
+
+ return await func(**filtered_kwargs)
+
+
+def capture_err(func):
+ """Decorator to handle errors gracefully."""
+ async def wrapper(client, message, *args, **kwargs):
+ try:
+ return await func(client, message, *args, **kwargs)
+ except Exception as e:
+ error_feedback = split_limits(str(e))
+ for feedback in error_feedback:
+ await message.reply(feedback)
+ raise e
+ return wrapper
+
+
+@app.on_message(filters.command("ls") & ~filters.forwarded & ~filters.via_bot & SUDOERS)
+@capture_err
+async def lst(_, message):
+ path = os.getcwd()
+ text = message.text.split(" ", 1)
+ directory = None
+
+ if len(text) > 1:
+ directory = text[1].strip()
+ path = directory
+
+ if not exists(path):
+ await eor(
+ message,
+ text=f"There is no such directory or file with the name `{directory}`. Check again!",
+ )
+ return
+
+ if isdir(path):
+ msg = f"Folders and Files in `{path}`:\n\n" if directory else "Folders and Files in Current Directory:\n\n"
+ files = ""
+ folders = ""
+
+ for contents in sorted(os.listdir(path)):
+ item_path = os.path.join(path, contents)
+ if isdir(item_path):
+ folders += f"📁 {contents}\n"
+ else:
+ size = os.stat(item_path).st_size
+ if contents.endswith((".mp3", ".flac", ".wav", ".m4a")):
+ files += f"🎵 {contents}\n"
+ elif contents.endswith(".opus"):
+ files += f"🎙 {contents}\n"
+ elif contents.endswith((".mkv", ".mp4", ".webm", ".avi", ".mov", ".flv")):
+ files += f"🎞 {contents}\n"
+ elif contents.endswith((".zip", ".tar", ".tar.gz", ".rar", ".7z", ".xz")):
+ files += f"🗜 {contents}\n"
+ elif contents.endswith((".jpg", ".jpeg", ".png", ".gif", ".bmp", ".ico", ".webp")):
+ files += f"🖼 {contents}\n"
+ elif contents.endswith((".exe", ".deb")):
+ files += f"⚙️ {contents}\n"
+ elif contents.endswith((".iso", ".img")):
+ files += f"💿 {contents}\n"
+ elif contents.endswith((".apk", ".xapk")):
+ files += f"📱 {contents}\n"
+ elif contents.endswith(".py"):
+ files += f"🐍 {contents}\n"
+ else:
+ files += f"📄 {contents}\n"
+
+ msg += folders + files if files or folders else "__empty path__"
+ else:
+ size = os.stat(path).st_size
+ last_modified = time.ctime(os.path.getmtime(path))
+ last_accessed = time.ctime(os.path.getatime(path))
+
+ if path.endswith((".mp3", ".flac", ".wav", ".m4a")):
+ mode = "🎵"
+ elif path.endswith(".opus"):
+ mode = "🎙"
+ elif path.endswith((".mkv", ".mp4", ".webm", ".avi", ".mov", ".flv")):
+ mode = "🎞"
+ elif path.endswith((".zip", ".tar", ".tar.gz", ".rar", ".7z", ".xz")):
+ mode = "🗜"
+ elif path.endswith((".jpg", ".jpeg", ".png", ".gif", ".bmp", ".ico", ".webp")):
+ mode = "🖼"
+ elif path.endswith((".exe", ".deb")):
+ mode = "⚙️"
+ elif path.endswith((".iso", ".img")):
+ mode = "💿"
+ elif path.endswith((".apk", ".xapk")):
+ mode = "📱"
+ elif path.endswith(".py"):
+ mode = "🐍"
+ else:
+ mode = "📄"
+
+ msg = (
+ f"Location: {path}\n"
+ f"Icon: {mode}\n"
+ f"Size: {humanbytes(size)}\n"
+ f"Last Modified: {last_modified}\n"
+ f"Last Accessed: {last_accessed}\n"
+ )
+
+ if len(msg) > MAX_MESSAGE_SIZE_LIMIT:
+ with io.BytesIO(str.encode(msg)) as out_file:
+ out_file.name = "ls.txt"
+ await app.send_document(message.chat.id, out_file, caption=path)
+ await message.delete()
+ else:
+ await eor(message, text=msg)
+
+
+@app.on_message(filters.command("rm") & ~filters.forwarded & ~filters.via_bot & SUDOERS)
+@capture_err
+async def rm_file(_, message):
+ if len(message.command) < 2:
+ return await eor(message, text="Please provide a file name or directory to delete.")
+ file = message.text.split(" ", 1)[1]
+
+ if exists(file):
+ if isdir(file):
+ try:
+ shutil.rmtree(file) # Removes a directory and its contents
+ await eor(message, text=f"Directory `{file}` has been deleted.")
+ except Exception as e:
+ await eor(message, text=f"Failed to delete directory `{file}`: {str(e)}")
+ else:
+ try:
+ os.remove(file) # Removes a file
+ await eor(message, text=f"File `{file}` has been deleted.")
+ except Exception as e:
+ await eor(message, text=f"Failed to delete file `{file}`: {str(e)}")
+ else:
+ await eor(message, text=f"{file} doesn't exist!")
\ No newline at end of file
diff --git a/SprotifyMusic/plugins/sudo/gban.py b/SprotifyMusic/plugins/sudo/gban.py
new file mode 100644
index 000000000000..eb814a0ec533
--- /dev/null
+++ b/SprotifyMusic/plugins/sudo/gban.py
@@ -0,0 +1,125 @@
+import asyncio
+
+from pyrogram import filters
+from pyrogram.errors import FloodWait
+from pyrogram.types import Message
+
+from SprotifyMusic import app
+from SprotifyMusic.misc import SUDOERS
+from SprotifyMusic.utils import get_readable_time
+from SprotifyMusic.utils.database import (
+ add_banned_user,
+ get_banned_count,
+ get_banned_users,
+ get_served_chats,
+ is_banned_user,
+ remove_banned_user,
+)
+from SprotifyMusic.utils.decorators.language import language
+from SprotifyMusic.utils.extraction import extract_user
+from config import BANNED_USERS
+
+
+@app.on_message(filters.command(["gban", "globalban"]) & SUDOERS)
+@language
+async def global_ban(client, message: Message, _):
+ if not message.reply_to_message:
+ if len(message.command) != 2:
+ return await message.reply_text(_["general_1"])
+ user = await extract_user(message)
+ if user.id == message.from_user.id:
+ return await message.reply_text(_["gban_1"])
+ elif user.id == app.id:
+ return await message.reply_text(_["gban_2"])
+ elif user.id in SUDOERS:
+ return await message.reply_text(_["gban_3"])
+ is_gbanned = await is_banned_user(user.id)
+ if is_gbanned:
+ return await message.reply_text(_["gban_4"].format(user.mention))
+ if user.id not in BANNED_USERS:
+ BANNED_USERS.add(user.id)
+ served_chats = []
+ chats = await get_served_chats()
+ for chat in chats:
+ served_chats.append(int(chat["chat_id"]))
+ time_expected = get_readable_time(len(served_chats))
+ mystic = await message.reply_text(_["gban_5"].format(user.mention, time_expected))
+ number_of_chats = 0
+ for chat_id in served_chats:
+ try:
+ await app.ban_chat_member(chat_id, user.id)
+ number_of_chats += 1
+ except FloodWait as fw:
+ await asyncio.sleep(int(fw.value))
+ except:
+ continue
+ await add_banned_user(user.id)
+ await message.reply_text(
+ _["gban_6"].format(
+ app.mention,
+ message.chat.title,
+ message.chat.id,
+ user.mention,
+ user.id,
+ message.from_user.mention,
+ number_of_chats,
+ )
+ )
+ await mystic.delete()
+
+
+@app.on_message(filters.command(["ungban"]) & SUDOERS)
+@language
+async def global_un(client, message: Message, _):
+ if not message.reply_to_message:
+ if len(message.command) != 2:
+ return await message.reply_text(_["general_1"])
+ user = await extract_user(message)
+ is_gbanned = await is_banned_user(user.id)
+ if not is_gbanned:
+ return await message.reply_text(_["gban_7"].format(user.mention))
+ if user.id in BANNED_USERS:
+ BANNED_USERS.remove(user.id)
+ served_chats = []
+ chats = await get_served_chats()
+ for chat in chats:
+ served_chats.append(int(chat["chat_id"]))
+ time_expected = get_readable_time(len(served_chats))
+ mystic = await message.reply_text(_["gban_8"].format(user.mention, time_expected))
+ number_of_chats = 0
+ for chat_id in served_chats:
+ try:
+ await app.unban_chat_member(chat_id, user.id)
+ number_of_chats += 1
+ except FloodWait as fw:
+ await asyncio.sleep(int(fw.value))
+ except:
+ continue
+ await remove_banned_user(user.id)
+ await message.reply_text(_["gban_9"].format(user.mention, number_of_chats))
+ await mystic.delete()
+
+
+@app.on_message(filters.command(["gbannedusers", "gbanlist"]) & SUDOERS)
+@language
+async def gbanned_list(client, message: Message, _):
+ counts = await get_banned_count()
+ if counts == 0:
+ return await message.reply_text(_["gban_10"])
+ mystic = await message.reply_text(_["gban_11"])
+ msg = _["gban_12"]
+ count = 0
+ users = await get_banned_users()
+ for user_id in users:
+ count += 1
+ try:
+ user = await app.get_users(user_id)
+ user = user.first_name if not user.mention else user.mention
+ msg += f"❖ {count} ➥ {user}\n"
+ except Exception:
+ msg += f"❖ {count} ➥ {user_id}\n"
+ continue
+ if count == 0:
+ return await mystic.edit_text(_["gban_10"])
+ else:
+ return await mystic.edit_text(msg)
diff --git a/SprotifyMusic/plugins/sudo/logger.py b/SprotifyMusic/plugins/sudo/logger.py
new file mode 100644
index 000000000000..0d2309c10ee5
--- /dev/null
+++ b/SprotifyMusic/plugins/sudo/logger.py
@@ -0,0 +1,23 @@
+from pyrogram import filters
+
+from SprotifyMusic import app
+from SprotifyMusic.misc import SUDOERS
+from SprotifyMusic.utils.database import add_off, add_on
+from SprotifyMusic.utils.decorators.language import language
+
+
+@app.on_message(filters.command(["logger"]) & SUDOERS)
+@language
+async def logger(client, message, _):
+ usage = _["log_1"]
+ if len(message.command) != 2:
+ return await message.reply_text(usage)
+ state = message.text.split(None, 1)[1].strip().lower()
+ if state == "enable":
+ await add_on(2)
+ await message.reply_text(_["log_2"])
+ elif state == "disable":
+ await add_off(2)
+ await message.reply_text(_["log_3"])
+ else:
+ await message.reply_text(usage)
diff --git a/SprotifyMusic/plugins/sudo/maintenance.py b/SprotifyMusic/plugins/sudo/maintenance.py
new file mode 100644
index 000000000000..56a985eba4e8
--- /dev/null
+++ b/SprotifyMusic/plugins/sudo/maintenance.py
@@ -0,0 +1,39 @@
+from pyrogram import filters
+from pyrogram.types import Message
+
+from SprotifyMusic import app
+from SprotifyMusic.misc import SUDOERS
+from SprotifyMusic.utils.database import (
+ get_lang,
+ is_maintenance,
+ maintenance_off,
+ maintenance_on,
+)
+from strings import get_string
+
+
+@app.on_message(filters.command(["maintenance"]) & SUDOERS)
+async def maintenance(client, message: Message):
+ try:
+ language = await get_lang(message.chat.id)
+ _ = get_string(language)
+ except:
+ _ = get_string("en")
+ usage = _["maint_1"]
+ if len(message.command) != 2:
+ return await message.reply_text(usage)
+ state = message.text.split(None, 1)[1].strip().lower()
+ if state == "enable":
+ if await is_maintenance() is False:
+ await message.reply_text(_["maint_4"])
+ else:
+ await maintenance_on()
+ await message.reply_text(_["maint_2"].format(app.mention))
+ elif state == "disable":
+ if await is_maintenance() is False:
+ await maintenance_off()
+ await message.reply_text(_["maint_3"].format(app.mention))
+ else:
+ await message.reply_text(_["maint_5"])
+ else:
+ await message.reply_text(usage)
diff --git a/SprotifyMusic/plugins/sudo/restart.py b/SprotifyMusic/plugins/sudo/restart.py
new file mode 100644
index 000000000000..ea93c74df25c
--- /dev/null
+++ b/SprotifyMusic/plugins/sudo/restart.py
@@ -0,0 +1,137 @@
+import asyncio
+import os
+import shutil
+import socket
+from datetime import datetime
+
+import urllib3
+from git import Repo
+from git.exc import GitCommandError, InvalidGitRepositoryError
+from pyrogram import filters
+
+import config
+from SprotifyMusic import app
+from SprotifyMusic.misc import HAPP, SUDOERS, XCB
+from SprotifyMusic.utils.database import (
+ get_active_chats,
+ remove_active_chat,
+ remove_active_video_chat,
+)
+from SprotifyMusic.utils.decorators.language import language
+from SprotifyMusic.utils.pastebin import Bin
+
+urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
+
+
+async def is_heroku():
+ return "heroku" in socket.getfqdn()
+
+
+@app.on_message(filters.command(["getlog", "logs", "getlogs"]) & SUDOERS)
+@language
+async def log_(client, message, _):
+ try:
+ await message.reply_document(document="log.txt")
+ except:
+ await message.reply_text(_["server_1"])
+
+
+@app.on_message(filters.command(["update", "gitpull"]) & SUDOERS)
+@language
+async def update_(client, message, _):
+ if await is_heroku():
+ if HAPP is None:
+ return await message.reply_text(_["server_2"])
+ response = await message.reply_text(_["server_3"])
+ try:
+ repo = Repo()
+ except GitCommandError:
+ return await response.edit(_["server_4"])
+ except InvalidGitRepositoryError:
+ return await response.edit(_["server_5"])
+ to_exc = f"❖ git fetch origin ➥ {config.UPSTREAM_BRANCH} &> /dev/null"
+ os.system(to_exc)
+ await asyncio.sleep(7)
+ verification = ""
+ REPO_ = repo.remotes.origin.url.split(".git")[0]
+ for checks in repo.iter_commits(f"HEAD..origin/{config.UPSTREAM_BRANCH}"):
+ verification = str(checks.count())
+ if verification == "":
+ return await response.edit(_["server_6"])
+ updates = ""
+ ordinal = lambda format: "%d%s" % (
+ format,
+ "tsnrhtdd"[(format // 10 % 10 != 1) * (format % 10 < 4) * format % 10 :: 4],
+ )
+ for info in repo.iter_commits(f"HEAD..origin/{config.UPSTREAM_BRANCH}"):
+ updates += f"❖ #{info.count()} ➥ {info.summary} ʙʏ ➥ {info.author}\n\t\t\t\t➥ ᴄᴏᴍᴍɪᴛᴇᴅ ᴏɴ ➥ {ordinal(int(datetime.fromtimestamp(info.committed_date).strftime('%d')))} {datetime.fromtimestamp(info.committed_date).strftime('%b')}, {datetime.fromtimestamp(info.committed_date).strftime('%Y')}\n\n"
+ _update_response_ = "❖ ᴀ ɴᴇᴡ ᴜᴩᴅᴀᴛᴇ ɪs ᴀᴠᴀɪʟᴀʙʟᴇ ғᴏʀ ᴛʜᴇ ʙᴏᴛ !\n\n● ᴩᴜsʜɪɴɢ ᴜᴩᴅᴀᴛᴇs ɴᴏᴡ\n\n● <ᴜᴩᴅᴀᴛᴇs ➥\n\n"
+ _final_updates_ = _update_response_ + updates
+ if len(_final_updates_) > 4096:
+ url = await Bin(updates)
+ nrs = await response.edit(
+ f"❖ ᴀ ɴᴇᴡ ᴜᴩᴅᴀᴛᴇ ɪs ᴀᴠᴀɪʟᴀʙʟᴇ ғᴏʀ ᴛʜᴇ ʙᴏᴛ !\n\n● ᴩᴜsʜɪɴɢ ᴜᴩᴅᴀᴛᴇs ɴᴏᴡ\n\n● ᴜᴩᴅᴀᴛᴇs ➥\n\nᴄʜᴇᴄᴋ ᴜᴩᴅᴀᴛᴇs"
+ )
+ else:
+ nrs = await response.edit(_final_updates_, disable_web_page_preview=True)
+ os.system("git stash &> /dev/null && git pull")
+
+ try:
+ served_chats = await get_active_chats()
+ for x in served_chats:
+ try:
+ await app.send_message(
+ chat_id=int(x),
+ text=_["server_8"].format(app.mention),
+ )
+ await remove_active_chat(x)
+ await remove_active_video_chat(x)
+ except:
+ pass
+ await response.edit(f"{nrs.text}\n\n{_['server_7']}")
+ except:
+ pass
+
+ if await is_heroku():
+ try:
+ os.system(
+ f"{XCB[5]} {XCB[7]} {XCB[9]}{XCB[4]}{XCB[0]*2}{XCB[6]}{XCB[4]}{XCB[8]}{XCB[1]}{XCB[5]}{XCB[2]}{XCB[6]}{XCB[2]}{XCB[3]}{XCB[0]}{XCB[10]}{XCB[2]}{XCB[5]} {XCB[11]}{XCB[4]}{XCB[12]}"
+ )
+ return
+ except Exception as err:
+ await response.edit(f"{nrs.text}\n\n{_['server_9']}")
+ return await app.send_message(
+ chat_id=config.LOGGER_ID,
+ text=_["server_10"].format(err),
+ )
+ else:
+ os.system("pip3 install -r requirements.txt")
+ os.system(f"kill -9 {os.getpid()} && bash start")
+ exit()
+
+
+@app.on_message(filters.command(["restart"]) & SUDOERS)
+async def restart_(_, message):
+ response = await message.reply_text("❖ ʀᴇsᴛᴀʀᴛɪɴɢ...")
+ ac_chats = await get_active_chats()
+ for x in ac_chats:
+ try:
+ await app.send_message(
+ chat_id=int(x),
+ text=f"❖ {app.mention} ɪs ʀᴇsᴛᴀʀᴛɪɴɢ...\n\n● ʏᴏᴜ ᴄᴀɴ sᴛᴀʀᴛ ᴩʟᴀʏɪɴɢ ᴀɢᴀɪɴ ᴀғᴛᴇʀ 15-20 sᴇᴄᴏɴᴅs.",
+ )
+ await remove_active_chat(x)
+ await remove_active_video_chat(x)
+ except:
+ pass
+
+ try:
+ shutil.rmtree("downloads")
+ shutil.rmtree("raw_files")
+ shutil.rmtree("cache")
+ except:
+ pass
+ await response.edit_text(
+ "❖ ʀᴇsᴛᴀʀᴛ ᴘʀᴏᴄᴇss sᴛᴀʀᴛᴇᴅ, ᴘʟᴇᴀsᴇ ᴡᴀɪᴛ ғᴏʀ ғᴇᴡ sᴇᴄᴏɴᴅs ᴜɴᴛɪʟ ᴛʜᴇ ʙᴏᴛ sᴛᴀʀᴛs..."
+ )
+ os.system(f"kill -9 {os.getpid()} && bash start")
diff --git a/SprotifyMusic/plugins/sudo/sudoers.py b/SprotifyMusic/plugins/sudo/sudoers.py
new file mode 100644
index 000000000000..ffc7c8730a5c
--- /dev/null
+++ b/SprotifyMusic/plugins/sudo/sudoers.py
@@ -0,0 +1,71 @@
+from pyrogram import filters
+from pyrogram.types import Message
+
+from SprotifyMusic import app
+from SprotifyMusic.misc import SUDOERS
+from SprotifyMusic.utils.database import add_sudo, remove_sudo
+from SprotifyMusic.utils.decorators.language import language
+from SprotifyMusic.utils.extraction import extract_user
+from SprotifyMusic.utils.inline import close_markup
+from config import BANNED_USERS, OWNER_ID
+
+
+@app.on_message(filters.command(["addsudo"]) & filters.user(OWNER_ID))
+@language
+async def useradd(client, message: Message, _):
+ if not message.reply_to_message:
+ if len(message.command) != 2:
+ return await message.reply_text(_["general_1"])
+ user = await extract_user(message)
+ if user.id in SUDOERS:
+ return await message.reply_text(_["sudo_1"].format(user.mention))
+ added = await add_sudo(user.id)
+ if added:
+ SUDOERS.add(user.id)
+ await message.reply_text(_["sudo_2"].format(user.mention))
+ else:
+ await message.reply_text(_["sudo_8"])
+
+
+@app.on_message(filters.command(["delsudo", "rmsudo"]) & filters.user(OWNER_ID))
+@language
+async def userdel(client, message: Message, _):
+ if not message.reply_to_message:
+ if len(message.command) != 2:
+ return await message.reply_text(_["general_1"])
+ user = await extract_user(message)
+ if user.id not in SUDOERS:
+ return await message.reply_text(_["sudo_3"].format(user.mention))
+ removed = await remove_sudo(user.id)
+ if removed:
+ SUDOERS.remove(user.id)
+ await message.reply_text(_["sudo_4"].format(user.mention))
+ else:
+ await message.reply_text(_["sudo_8"])
+
+
+@app.on_message(filters.command(["sudolist", "listsudo", "sudoers"]) & ~BANNED_USERS)
+@language
+async def sudoers_list(client, message: Message, _):
+ text = _["sudo_5"]
+ user = await app.get_users(OWNER_ID)
+ user = user.first_name if not user.mention else user.mention
+ text += f"❖ {user}\n"
+ count = 0
+ smex = 0
+ for user_id in SUDOERS:
+ if user_id != OWNER_ID:
+ try:
+ user = await app.get_users(user_id)
+ user = user.first_name if not user.mention else user.mention
+ if smex == 0:
+ smex += 1
+ text += _["sudo_6"]
+ count += 1
+ text += f"❖ {count} ➥ {user}\n"
+ except:
+ continue
+ if not text:
+ await message.reply_text(_["sudo_7"])
+ else:
+ await message.reply_text(text, reply_markup=close_markup(_))
diff --git a/SprotifyMusic/plugins/tools/afk.py b/SprotifyMusic/plugins/tools/afk.py
new file mode 100644
index 000000000000..7b1632128632
--- /dev/null
+++ b/SprotifyMusic/plugins/tools/afk.py
@@ -0,0 +1,380 @@
+import time, re
+from config import BOT_USERNAME
+from pyrogram.enums import MessageEntityType
+from pyrogram import filters
+from pyrogram.types import Message
+from SprotifyMusic import app
+from SprotifyMusic.utils.formatters import get_readable_time
+from SprotifyMusic.utils.database import add_afk, is_afk, remove_afk
+
+
+@app.on_message(filters.command(["afk", "brb"], prefixes=["/", "!"]))
+async def active_afk(_, message: Message):
+ if message.sender_chat:
+ return
+ user_id = message.from_user.id
+ verifier, reasondb = await is_afk(user_id)
+ if verifier:
+ await remove_afk(user_id)
+ try:
+ afktype = reasondb["type"]
+ timeafk = reasondb["time"]
+ data = reasondb["data"]
+ reasonafk = reasondb["reason"]
+ seenago = get_readable_time((int(time.time() - timeafk)))
+ if afktype == "text":
+ send = await message.reply_text(
+ f" ✦ {message.from_user.first_name} ɪs ʙᴀᴄᴋ ᴏɴʟɪɴᴇ ᴀɴᴅ ᴡᴀs ᴀᴡᴀʏ ғᴏʀ {seenago}",
+ disable_web_page_preview=True,
+ )
+ if afktype == "text_reason":
+ send = await message.reply_text(
+ f"✦ {message.from_user.first_name} ɪs ʙᴀᴄᴋ ᴏɴʟɪɴᴇ ᴀɴᴅ ᴡᴀs ᴀᴡᴀʏ ғᴏʀ {seenago}\n\n✽ ʀᴇᴀsᴏɴ ➠ `{reasonafk}`",
+ disable_web_page_preview=True,
+ )
+ if afktype == "animation":
+ if str(reasonafk) == "None":
+ send = await message.reply_animation(
+ data,
+ caption=f"✦ {message.from_user.first_name} ɪs ʙᴀᴄᴋ ᴏɴʟɪɴᴇ ᴀɴᴅ ᴡᴀs ᴀᴡᴀʏ ғᴏʀ {seenago}",
+ )
+ else:
+ send = await message.reply_animation(
+ data,
+ caption=f"✦ {message.from_user.first_name} ɪs ʙᴀᴄᴋ ᴏɴʟɪɴᴇ ᴀɴᴅ ᴡᴀs ᴀᴡᴀʏ ғᴏʀ {seenago}\n\n✽ ʀᴇᴀsᴏɴ ➠ `{reasonafk}`",
+ )
+ if afktype == "photo":
+ if str(reasonafk) == "None":
+ send = await message.reply_photo(
+ photo=f"downloads/{user_id}.jpg",
+ caption=f"✦ {message.from_user.first_name} ɪs ʙᴀᴄᴋ ᴏɴʟɪɴᴇ ᴀɴᴅ ᴡᴀs ᴀᴡᴀʏ ғᴏʀ {seenago}",
+ )
+ else:
+ send = await message.reply_photo(
+ photo=f"downloads/{user_id}.jpg",
+ caption=f"✦ {message.from_user.first_name} ɪs ʙᴀᴄᴋ ᴏɴʟɪɴᴇ ᴀɴᴅ ᴡᴀs ᴀᴡᴀʏ ғᴏʀ {seenago}\n\n✽ ʀᴇᴀsᴏɴ ➠`{reasonafk}`",
+ )
+ except Exception:
+ send = await message.reply_text(
+ f"✦ {message.from_user.first_name} ɪs ʙᴀᴄᴋ ᴏɴʟɪɴᴇ",
+ disable_web_page_preview=True,
+ )
+
+ if len(message.command) == 1 and not message.reply_to_message:
+ details = {
+ "type": "text",
+ "time": time.time(),
+ "data": None,
+ "reason": None,
+ }
+ elif len(message.command) > 1 and not message.reply_to_message:
+ _reason = (message.text.split(None, 1)[1].strip())[:100]
+ details = {
+ "type": "text_reason",
+ "time": time.time(),
+ "data": None,
+ "reason": _reason,
+ }
+ elif len(message.command) == 1 and message.reply_to_message.animation:
+ _data = message.reply_to_message.animation.file_id
+ details = {
+ "type": "animation",
+ "time": time.time(),
+ "data": _data,
+ "reason": None,
+ }
+ elif len(message.command) > 1 and message.reply_to_message.animation:
+ _data = message.reply_to_message.animation.file_id
+ _reason = (message.text.split(None, 1)[1].strip())[:100]
+ details = {
+ "type": "animation",
+ "time": time.time(),
+ "data": _data,
+ "reason": _reason,
+ }
+ elif len(message.command) == 1 and message.reply_to_message.photo:
+ await app.download_media(
+ message.reply_to_message, file_name=f"{user_id}.jpg"
+ )
+ details = {
+ "type": "photo",
+ "time": time.time(),
+ "data": None,
+ "reason": None,
+ }
+ elif len(message.command) > 1 and message.reply_to_message.photo:
+ await app.download_media(
+ message.reply_to_message, file_name=f"{user_id}.jpg"
+ )
+ _reason = message.text.split(None, 1)[1].strip()
+ details = {
+ "type": "photo",
+ "time": time.time(),
+ "data": None,
+ "reason": _reason,
+ }
+ elif len(message.command) == 1 and message.reply_to_message.sticker:
+ if message.reply_to_message.sticker.is_animated:
+ details = {
+ "type": "text",
+ "time": time.time(),
+ "data": None,
+ "reason": None,
+ }
+ else:
+ await app.download_media(
+ message.reply_to_message, file_name=f"{user_id}.jpg"
+ )
+ details = {
+ "type": "photo",
+ "time": time.time(),
+ "data": None,
+ "reason": None,
+ }
+ elif len(message.command) > 1 and message.reply_to_message.sticker:
+ _reason = (message.text.split(None, 1)[1].strip())[:100]
+ if message.reply_to_message.sticker.is_animated:
+ details = {
+ "type": "text_reason",
+ "time": time.time(),
+ "data": None,
+ "reason": _reason,
+ }
+ else:
+ await app.download_media(
+ message.reply_to_message, file_name=f"{user_id}.jpg"
+ )
+ details = {
+ "type": "photo",
+ "time": time.time(),
+ "data": None,
+ "reason": _reason,
+ }
+ else:
+ details = {
+ "type": "text",
+ "time": time.time(),
+ "data": None,
+ "reason": None,
+ }
+
+ await add_afk(user_id, details)
+ await message.reply_text(f"{message.from_user.first_name} ɪs ɴᴏᴡ ᴀғᴋ!")
+
+
+
+
+chat_watcher_group = 1
+
+
+@app.on_message(
+ ~filters.me & ~filters.bot & ~filters.via_bot,
+ group=chat_watcher_group,
+)
+async def chat_watcher_func(_, message):
+ if message.sender_chat:
+ return
+ userid = message.from_user.id
+ user_name = message.from_user.first_name
+ if message.entities:
+ possible = ["/afk", f"/afk@{BOT_USERNAME}"]
+ message_text = message.text or message.caption
+ for entity in message.entities:
+ if entity.type == MessageEntityType.BOT_COMMAND:
+ if (message_text[0 : 0 + entity.length]).lower() in possible:
+ return
+
+ msg = ""
+ replied_user_id = 0
+
+
+
+ verifier, reasondb = await is_afk(userid)
+ if verifier:
+ await remove_afk(userid)
+ try:
+ afktype = reasondb["type"]
+ timeafk = reasondb["time"]
+ data = reasondb["data"]
+ reasonafk = reasondb["reason"]
+ seenago = get_readable_time((int(time.time() - timeafk)))
+ if afktype == "text":
+ msg += f"✦ {user_name[:25]} ɪs ʙᴀᴄᴋ ᴏɴʟɪɴᴇ ᴀɴᴅ ᴡᴀs ᴀᴡᴀʏ ғᴏʀ {seenago}\n\n"
+ if afktype == "text_reason":
+ msg += f"✦ {user_name[:25]} ɪs ʙᴀᴄᴋ ᴏɴʟɪɴᴇ ᴀɴᴅ ᴡᴀs ᴀᴡᴀʏ ғᴏʀ {seenago}\n\n✽ ʀᴇᴀsᴏɴ ➠ `{reasonafk}`\n\n"
+ if afktype == "animation":
+ if str(reasonafk) == "None":
+ send = await message.reply_animation(
+ data,
+ caption=f"✦ {user_name[:25]} ɪs ʙᴀᴄᴋ ᴏɴʟɪɴᴇ ᴀɴᴅ ᴡᴀs ᴀᴡᴀʏ ғᴏʀ {seenago}\n\n",
+ )
+ else:
+ send = await message.reply_animation(
+ data,
+ caption=f"✦ {user_name[:25]} ɪs ʙᴀᴄᴋ ᴏɴʟɪɴᴇ ᴀɴᴅ ᴡᴀs ᴀᴡᴀʏ ғᴏʀ {seenago}\n\n✽ ʀᴇᴀsᴏɴ ➠ `{reasonafk}`\n\n",
+ )
+ if afktype == "photo":
+ if str(reasonafk) == "None":
+ send = await message.reply_photo(
+ photo=f"downloads/{userid}.jpg",
+ caption=f"✦ {user_name[:25]} ɪs ʙᴀᴄᴋ ᴏɴʟɪɴᴇ ᴀɴᴅ ᴡᴀs ᴀᴡᴀʏ ғᴏʀ {seenago}\n\n",
+ )
+ else:
+ send = await message.reply_photo(
+ photo=f"downloads/{userid}.jpg",
+ caption=f"✦ {user_name[:25]} ɪs ʙᴀᴄᴋ ᴏɴʟɪɴᴇ ᴀɴᴅ ᴡᴀs ᴀᴡᴀʏ ғᴏʀ {seenago}\n\n✽ ʀᴇᴀsᴏɴ ➠ `{reasonafk}`\n\n",
+ )
+ except:
+ msg += f"✦ {user_name[:25]} ɪs ʙᴀᴄᴋ ᴏɴʟɪɴᴇ\n\n"
+
+
+ if message.reply_to_message:
+ try:
+ replied_first_name = message.reply_to_message.from_user.first_name
+ replied_user_id = message.reply_to_message.from_user.id
+ verifier, reasondb = await is_afk(replied_user_id)
+ if verifier:
+ try:
+ afktype = reasondb["type"]
+ timeafk = reasondb["time"]
+ data = reasondb["data"]
+ reasonafk = reasondb["reason"]
+ seenago = get_readable_time((int(time.time() - timeafk)))
+ if afktype == "text":
+ msg += (
+ f"✦ {replied_first_name[:25]} ɪs ᴀғᴋ sɪɴᴄᴇ {seenago}\n\n"
+ )
+ if afktype == "text_reason":
+ msg += f"✦ {replied_first_name[:25]} ɪs ᴀғᴋ sɪɴᴄᴇ {seenago}\n\n✽ ʀᴇᴀsᴏɴ ➠`{reasonafk}`\n\n"
+ if afktype == "animation":
+ if str(reasonafk) == "None":
+ send = await message.reply_animation(
+ data,
+ caption=f"✦ {replied_first_name[:25]} ɪs ᴀғᴋ sɪɴᴄᴇ {seenago}\n\n",
+ )
+ else:
+ send = await message.reply_animation(
+ data,
+ caption=f"✦ {replied_first_name[:25]} ɪs ᴀғᴋ sɪɴᴄᴇ {seenago}\n\n✽ ʀᴇᴀsᴏɴ ➠ `{reasonafk}`\n\n",
+ )
+ if afktype == "photo":
+ if str(reasonafk) == "None":
+ send = await message.reply_photo(
+ photo=f"downloads/{replied_user_id}.jpg",
+ caption=f"✦ {replied_first_name[:25]} ɪs ᴀғᴋ sɪɴᴄᴇ {seenago}\n\n",
+ )
+ else:
+ send = await message.reply_photo(
+ photo=f"downloads/{replied_user_id}.jpg",
+ caption=f"✦ {replied_first_name[:25]} ɪs ᴀғᴋ sɪɴᴄᴇ {seenago}\n\n✽ ʀᴇᴀsᴏɴ ➠`{reasonafk}`\n\n",
+ )
+ except Exception:
+ msg += f"✦ {replied_first_name} ɪs ᴀғᴋ,\n✽ ᴩᴀᴛᴀ ɴɪ ʙᴄ ᴋᴀʙ sᴇ\n\n"
+ except:
+ pass
+
+ if message.entities:
+ entity = message.entities
+ j = 0
+ for x in range(len(entity)):
+ if (entity[j].type) == MessageEntityType.MENTION:
+ found = re.findall("@([_0-9a-zA-Z]+)", message.text)
+ try:
+ get_user = found[j]
+ user = await app.get_users(get_user)
+ if user.id == replied_user_id:
+ j += 1
+ continue
+ except:
+ j += 1
+ continue
+ verifier, reasondb = await is_afk(user.id)
+ if verifier:
+ try:
+ afktype = reasondb["type"]
+ timeafk = reasondb["time"]
+ data = reasondb["data"]
+ reasonafk = reasondb["reason"]
+ seenago = get_readable_time((int(time.time() - timeafk)))
+ if afktype == "text":
+ msg += (
+ f"✦ {user.first_name[:25]} ɪs ᴀғᴋ sɪɴᴄᴇ {seenago}\n\n"
+ )
+ if afktype == "text_reason":
+ msg += f"✦ {user.first_name[:25]} ɪs ᴀғᴋ sɪɴᴄᴇ {seenago}\n\n✽ ʀᴇᴀsᴏɴ ➠ `{reasonafk}`\n\n"
+ if afktype == "animation":
+ if str(reasonafk) == "None":
+ send = await message.reply_animation(
+ data,
+ caption=f"✦ {user.first_name[:25]} ɪs ᴀғᴋ sɪɴᴄᴇ {seenago}\n\n",
+ )
+ else:
+ send = await message.reply_animation(
+ data,
+ caption=f"✦ {user.first_name[:25]} ɪs ᴀғᴋ sɪɴᴄᴇ {seenago}\n\n✽ ʀᴇᴀsᴏɴ ➠ `{reasonafk}`\n\n",
+ )
+ if afktype == "photo":
+ if str(reasonafk) == "None":
+ send = await message.reply_photo(
+ photo=f"downloads/{user.id}.jpg",
+ caption=f"✦ {user.first_name[:25]} ɪs ᴀғᴋ sɪɴᴄᴇ {seenago}\n\n",
+ )
+ else:
+ send = await message.reply_photo(
+ photo=f"downloads/{user.id}.jpg",
+ caption=f"✦ {user.first_name[:25]} ɪs ᴀғᴋ sɪɴᴄᴇ {seenago}\n\n✽ ʀᴇᴀsᴏɴ ➠ `{reasonafk}`\n\n",
+ )
+ except:
+ msg += f"✦ {user.first_name[:25]} ɪs ᴀғᴋ\n\n"
+ elif (entity[j].type) == MessageEntityType.TEXT_MENTION:
+ try:
+ user_id = entity[j].user.id
+ if user_id == replied_user_id:
+ j += 1
+ continue
+ first_name = entity[j].user.first_name
+ except:
+ j += 1
+ continue
+ verifier, reasondb = await is_afk(user_id)
+ if verifier:
+ try:
+ afktype = reasondb["type"]
+ timeafk = reasondb["time"]
+ data = reasondb["data"]
+ reasonafk = reasondb["reason"]
+ seenago = get_readable_time((int(time.time() - timeafk)))
+ if afktype == "text":
+ msg += f"✦ {first_name[:25]} is ᴀғᴋ sɪɴᴄᴇ {seenago}\n\n"
+ if afktype == "text_reason":
+ msg += f"✦ {first_name[:25]} ɪs ᴀғᴋ sɪɴᴄᴇ {seenago}\n\n✽ ʀᴇᴀsᴏɴ ➠ `{reasonafk}`\n\n"
+ if afktype == "animation":
+ if str(reasonafk) == "None":
+ send = await message.reply_animation(
+ data,
+ caption=f"✦ {first_name[:25]} ɪs ᴀғᴋ sɪɴᴄᴇ {seenago}\n\n",
+ )
+ else:
+ send = await message.reply_animation(
+ data,
+ caption=f"✦ {first_name[:25]} ɪs ᴀғᴋ sɪɴᴄᴇ {seenago}\n\n✽ ʀᴇᴀsᴏɴ ➠ `{reasonafk}`\n\n",
+ )
+ if afktype == "photo":
+ if str(reasonafk) == "None":
+ send = await message.reply_photo(
+ photo=f"downloads/{user_id}.jpg",
+ caption=f"✦ {first_name[:25]} ɪs ᴀғᴋ sɪɴᴄᴇ {seenago}\n\n",
+ )
+ else:
+ send = await message.reply_photo(
+ photo=f"downloads/{user_id}.jpg",
+ caption=f"✦ {first_name[:25]} ɪs ᴀғᴋ sɪɴᴄᴇ {seenago}\n\n✽ ʀᴇᴀsᴏɴ ➠ `{reasonafk}`\n\n",
+ )
+ except:
+ msg += f"✦ {first_name[:25]} ɪs ᴀғᴋ\n\n"
+ j += 1
+ if msg != "":
+ try:
+ send = await message.reply_text(msg, disable_web_page_preview=True)
+ except:
+ return
\ No newline at end of file
diff --git a/SprotifyMusic/plugins/tools/carbon.py b/SprotifyMusic/plugins/tools/carbon.py
new file mode 100644
index 000000000000..2eb31db57289
--- /dev/null
+++ b/SprotifyMusic/plugins/tools/carbon.py
@@ -0,0 +1,39 @@
+import aiohttp
+from io import BytesIO
+from SprotifyMusic import app
+from pyrogram import filters
+from pyrogram.types import InlineKeyboardButton, InlineKeyboardMarkup
+
+EVAA = [
+ [
+ InlineKeyboardButton(text="ᴀᴅᴅ ᴍᴇ ʙᴀʙʏ", url=f"https://t.me/TanuMusicxBot?startgroup=true"),
+ ],
+]
+
+
+async def make_carbon(code):
+ url = "https://carbonara.solopov.dev/api/cook"
+ async with aiohttp.ClientSession() as session:
+ async with session.post(url, json={"code": code}) as resp:
+ image = BytesIO(await resp.read())
+ image.name = "carbon.png"
+ return image
+
+
+
+@app.on_message(filters.command("carbon"))
+async def _carbon(client, message):
+ replied = message.reply_to_message
+ if not replied:
+ await message.reply_text("ʀᴇᴘʟʏ ᴛᴏ ᴀ ᴛᴇxᴛ ᴍᴇssᴀɢᴇ ᴛᴏ ᴍᴀᴋᴇ ᴀ ᴄᴀʀʙᴏɴ. ")
+ return
+ if not (replied.text or replied.caption):
+ return await message.reply_text("ʀᴇᴘʟʏ ᴛᴏ ᴀ ᴛᴇxᴛ ᴍᴇssᴀɢᴇ ᴛᴏ ᴍᴀᴋᴇ ᴀ ᴄᴀʀʙᴏɴ. ")
+ text = await message.reply("Processing...")
+ carbon = await make_carbon(replied.text or replied.caption)
+ await text.edit(" ᴜᴘʟᴏᴀᴅɪɴɢ... ")
+ await message.reply_photo(carbon, caption=f"❖ ᴘᴏᴡᴇʀᴇᴅ ʙʏ ➥ ˹ ᴛᴀɴᴜ ꭙ ᴍᴜsɪᴄ™ ♡゙゙", reply_markup=InlineKeyboardMarkup(EVAA),
+ )
+ await text.delete()
+ carbon.close()
+
diff --git a/SprotifyMusic/plugins/tools/clean.py b/SprotifyMusic/plugins/tools/clean.py
new file mode 100644
index 000000000000..e23f011ba67c
--- /dev/null
+++ b/SprotifyMusic/plugins/tools/clean.py
@@ -0,0 +1,19 @@
+import os
+import shutil
+
+from pyrogram import filters
+
+from SprotifyMusic import app
+from SprotifyMusic.misc import SUDOERS
+
+
+@app.on_message(filters.command("clean") & SUDOERS)
+async def clean(_, message):
+ A = await message.reply_text("ᴄʟᴇᴀɴɪɴɢ ᴛᴇᴍᴘ ᴅɪʀᴇᴄᴛᴏʀɪᴇs...")
+ dir = "downloads"
+ dir1 = "cache"
+ shutil.rmtree(dir)
+ shutil.rmtree(dir1)
+ os.mkdir(dir)
+ os.mkdir(dir1)
+ await A.edit("ᴛᴇᴍᴘ ᴅɪʀᴇᴄᴛᴏʀɪᴇs ᴀʀᴇ ᴄʟᴇᴀɴᴇᴅ")
\ No newline at end of file
diff --git a/SprotifyMusic/plugins/tools/fonts.py b/SprotifyMusic/plugins/tools/fonts.py
new file mode 100644
index 000000000000..8dcefd5ee478
--- /dev/null
+++ b/SprotifyMusic/plugins/tools/fonts.py
@@ -0,0 +1,193 @@
+from pyrogram import filters
+from pyrogram.types import InlineKeyboardButton, InlineKeyboardMarkup
+
+from SprotifyMusic.utils.fonts import Fonts
+from SprotifyMusic import app
+
+@app.on_message(filters.command(["font", "fonts"]))
+async def style_buttons(c, m, cb=False):
+ buttons = [
+ [
+ InlineKeyboardButton("𝚃𝚢𝚙𝚎𝚠𝚛𝚒𝚝𝚎𝚛", callback_data="style+typewriter"),
+ InlineKeyboardButton("𝕆𝕦𝕥𝕝𝕚𝕟𝕖", callback_data="style+outline"),
+ InlineKeyboardButton("𝐒𝐞𝐫𝐢𝐟", callback_data="style+serif"),
+ ],
+ [
+ InlineKeyboardButton("𝑺𝒆𝒓𝒊𝒇", callback_data="style+bold_cool"),
+ InlineKeyboardButton("𝑆𝑒𝑟𝑖𝑓", callback_data="style+cool"),
+ InlineKeyboardButton("Sᴍᴀʟʟ Cᴀᴘs", callback_data="style+small_cap"),
+ ],
+ [
+ InlineKeyboardButton("𝓈𝒸𝓇𝒾𝓅𝓉", callback_data="style+script"),
+ InlineKeyboardButton("𝓼𝓬𝓻𝓲𝓹𝓽", callback_data="style+script_bolt"),
+ InlineKeyboardButton("ᵗⁱⁿʸ", callback_data="style+tiny"),
+ ],
+ [
+ InlineKeyboardButton("ᑕOᗰIᑕ", callback_data="style+comic"),
+ InlineKeyboardButton("𝗦𝗮𝗻𝘀", callback_data="style+sans"),
+ InlineKeyboardButton("𝙎𝙖𝙣𝙨", callback_data="style+slant_sans"),
+ ],
+ [
+ InlineKeyboardButton("𝘚𝘢𝘯𝘴", callback_data="style+slant"),
+ InlineKeyboardButton("𝖲𝖺𝗇𝗌", callback_data="style+sim"),
+ InlineKeyboardButton("Ⓒ︎Ⓘ︎Ⓡ︎Ⓒ︎Ⓛ︎Ⓒ︎", callback_data="style+circles"),
+ ],
+ [
+ InlineKeyboardButton("🅒︎🅘︎🅡︎🅒︎🅛︎🅔︎🅢︎", callback_data="style+circle_dark"),
+ InlineKeyboardButton("𝔊𝔬𝔱𝔥𝔦𝔠", callback_data="style+gothic"),
+ InlineKeyboardButton("𝕲𝖔𝖙𝖍𝖎𝖈", callback_data="style+gothic_bolt"),
+ ],
+ [
+ InlineKeyboardButton("C͜͡l͜͡o͜͡u͜͡d͜͡s͜͡", callback_data="style+cloud"),
+ InlineKeyboardButton("H̆̈ă̈p̆̈p̆̈y̆̈", callback_data="style+happy"),
+ InlineKeyboardButton("S̑̈ȃ̈d̑̈", callback_data="style+sad"),
+ ],
+ [InlineKeyboardButton("ᴄʟᴏsᴇ", callback_data="close_reply"), InlineKeyboardButton("ɴᴇxᴛ ➤", callback_data="nxt")],
+ ]
+
+ if not cb:
+ text = m.text.split(' ', 1)[1]
+ await m.reply_text(f"`{text}`", reply_markup=InlineKeyboardMarkup(buttons), quote=True)
+ else:
+ await m.answer()
+ await m.message.edit_reply_markup(InlineKeyboardMarkup(buttons))
+
+
+@app.on_callback_query(filters.regex("^nxt"))
+async def nxt(c, m):
+ if m.data == "nxt":
+ buttons = [
+ [
+ InlineKeyboardButton("🇸 🇵 🇪 🇨 🇮 🇦 🇱 ", callback_data="style+special"),
+ InlineKeyboardButton("🅂🅀🅄🄰🅁🄴🅂", callback_data="style+squares"),
+ InlineKeyboardButton(
+ "🆂︎🆀︎🆄︎🅰︎🆁︎🅴︎🆂︎", callback_data="style+squares_bold"
+ ),
+ ],
+ [
+ InlineKeyboardButton("ꪖꪀᦔꪖꪶꪊᥴ𝓲ꪖ", callback_data="style+andalucia"),
+ InlineKeyboardButton("爪卂几ᘜ卂", callback_data="style+manga"),
+ InlineKeyboardButton("S̾t̾i̾n̾k̾y̾", callback_data="style+stinky"),
+ ],
+ [
+ InlineKeyboardButton(
+ "B̥ͦu̥ͦb̥ͦb̥ͦl̥ͦe̥ͦs̥ͦ", callback_data="style+bubbles"
+ ),
+ InlineKeyboardButton(
+ "U͟n͟d͟e͟r͟l͟i͟n͟e͟", callback_data="style+underline"
+ ),
+ InlineKeyboardButton("꒒ꍏꀷꌩꌃꀎꁅ", callback_data="style+ladybug"),
+ ],
+ [
+ InlineKeyboardButton("R҉a҉y҉s҉", callback_data="style+rays"),
+ InlineKeyboardButton("B҈i҈r҈d҈s҈", callback_data="style+birds"),
+ InlineKeyboardButton("S̸l̸a̸s̸h̸", callback_data="style+slash"),
+ ],
+ [
+ InlineKeyboardButton("s⃠t⃠o⃠p⃠", callback_data="style+stop"),
+ InlineKeyboardButton(
+ "S̺͆k̺͆y̺͆l̺͆i̺͆n̺͆e̺͆", callback_data="style+skyline"
+ ),
+ InlineKeyboardButton("A͎r͎r͎o͎w͎s͎", callback_data="style+arrows"),
+ ],
+ [
+ InlineKeyboardButton("ዪሀክቿነ", callback_data="style+qvnes"),
+ InlineKeyboardButton("S̶t̶r̶i̶k̶e̶", callback_data="style+strike"),
+ InlineKeyboardButton("F༙r༙o༙z༙e༙n༙", callback_data="style+frozen"),
+ ],
+ [InlineKeyboardButton ("ᴄʟᴏsᴇ", callback_data="close_reply"), InlineKeyboardButton ("ʙᴀᴄᴋ", callback_data="nxt+0")],
+ ]
+ await m.answer()
+ await m.message.edit_reply_markup(InlineKeyboardMarkup(buttons))
+ elif m.data == "nxt+0":
+ await style_buttons(c, m, cb=True)
+
+@app.on_callback_query(filters.regex("^style"))
+async def style(c, m):
+ await m.answer()
+ cmd,style = m.data.split('+')
+ if style == "typewriter":
+ cls = Fonts.typewriter
+ if style == "outline":
+ cls = Fonts.outline
+ if style == "serif":
+ cls = Fonts.serief
+ if style == "bold_cool":
+ cls = Fonts.bold_cool
+ if style == "cool":
+ cls = Fonts.cool
+ if style == "small_cap":
+ cls = Fonts.smallcap
+ if style == "script":
+ cls = Fonts.script
+ if style == "script_bolt":
+ cls = Fonts.bold_script
+ if style == "tiny":
+ cls = Fonts.tiny
+ if style == "comic":
+ cls = Fonts.comic
+ if style == "sans":
+ cls = Fonts.san
+ if style == "slant_sans":
+ cls = Fonts.slant_san
+ if style == "slant":
+ cls = Fonts.slant
+ if style == "sim":
+ cls = Fonts.sim
+ if style == "circles":
+ cls = Fonts.circles
+ if style == "circle_dark":
+ cls = Fonts.dark_circle
+ if style == "gothic":
+ cls = Fonts.gothic
+ if style == "gothic_bolt":
+ cls = Fonts.bold_gothic
+ if style == "cloud":
+ cls = Fonts.cloud
+ if style == "happy":
+ cls = Fonts.happy
+ if style == "sad":
+ cls = Fonts.sad
+ if style == "special":
+ cls = Fonts.special
+ if style == "squares":
+ cls = Fonts.square
+ if style == "squares_bold":
+ cls = Fonts.dark_square
+ if style == "andalucia":
+ cls = Fonts.andalucia
+ if style == "manga":
+ cls = Fonts.manga
+ if style == "stinky":
+ cls = Fonts.stinky
+ if style == "bubbles":
+ cls = Fonts.bubbles
+ if style == "underline":
+ cls = Fonts.underline
+ if style == "ladybug":
+ cls = Fonts.ladybug
+ if style == "rays":
+ cls = Fonts.rays
+ if style == "birds":
+ cls = Fonts.birds
+ if style == "slash":
+ cls = Fonts.slash
+ if style == "stop":
+ cls = Fonts.stop
+ if style == "skyline":
+ cls = Fonts.skyline
+ if style == "arrows":
+ cls = Fonts.arrows
+ if style == "qvnes":
+ cls = Fonts.rvnes
+ if style == "strike":
+ cls = Fonts.strike
+ if style == "frozen":
+ cls = Fonts.frozen
+ new_text = cls(m.message.reply_to_message.text.split(" ",1)[1])
+ try:
+ await m.message.edit_text(new_text, reply_markup=m.message.reply_markup)
+ except:
+ pass
+
+
diff --git a/SprotifyMusic/plugins/tools/image.py b/SprotifyMusic/plugins/tools/image.py
new file mode 100644
index 000000000000..dd2b09eb2fc9
--- /dev/null
+++ b/SprotifyMusic/plugins/tools/image.py
@@ -0,0 +1,95 @@
+import os
+import requests
+from bs4 import BeautifulSoup
+from pyrogram import Client, filters
+from pyrogram.types import InputMediaPhoto, Message
+from SprotifyMusic import app
+
+# Function to fetch images from Google Images
+def fetch_google_images(query, num_images=7):
+ query = '+'.join(query.split())
+ url = f"https://www.google.com/search?hl=en&tbm=isch&q={query}"
+
+ headers = {
+ 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36'
+ }
+
+ try:
+ response = requests.get(url, headers=headers)
+ response.raise_for_status() # Will raise an HTTPError for bad responses (4xx or 5xx)
+
+ soup = BeautifulSoup(response.text, 'html.parser')
+ image_urls = []
+
+ for img_tag in soup.find_all('img', {'src': True}):
+ img_url = img_tag['src']
+ if img_url.startswith('http'):
+ image_urls.append(img_url)
+
+ if len(image_urls) >= num_images:
+ break
+
+ return image_urls
+ except requests.exceptions.RequestException as e:
+ print(f"Error fetching images: {e}")
+ return []
+
+# Function to download images
+def download_images(image_urls, folder='downloads'):
+ if not os.path.exists(folder):
+ os.makedirs(folder)
+
+ paths = []
+ for i, url in enumerate(image_urls):
+ try:
+ img_data = requests.get(url).content
+ img_path = os.path.join(folder, f'image_{i+1}.jpg')
+ with open(img_path, 'wb') as img_file:
+ img_file.write(img_data)
+ paths.append(img_path)
+ except Exception as e:
+ print(f"Error downloading image {i+1}: {e}")
+ return paths
+
+@app.on_message(filters.command("img", "image"))
+async def google_img_search(client: Client, message: Message):
+ chat_id = message.chat.id
+
+ try:
+ query = message.text.split(None, 1)[1]
+ except IndexError:
+ return await message.reply("❍ ᴘʀᴏᴠɪᴅᴇ ᴀɴ ɪᴍᴀɢᴇ ǫᴜɪɴ ᴛᴏ sᴇᴀʀᴄʜ!")
+
+ lim = 7 # Default limit to 7 images
+ image_urls = fetch_google_images(query, num_images=lim)
+
+ if not image_urls:
+ return await message.reply("❍ ɴᴏ ɪᴍᴀɢᴇs ғᴏᴜɴᴅ!")
+
+ msg = await message.reply("❍ ғɪɴᴅɪɴɢ ɪᴍᴀɢᴇs.....")
+
+ # Download images
+ downloaded_images = download_images(image_urls, folder="downloads")
+
+ if not downloaded_images:
+ return await message.reply("❍ ɪɴsᴜғғɪᴄɪᴇɴᴛ ɪᴍᴀɢᴇs ᴛᴏ sᴇɴᴅ.")
+
+ try:
+ # Send images as a media group
+ await app.send_media_group(
+ chat_id=chat_id,
+ media=[InputMediaPhoto(media=img) for img in downloaded_images],
+ reply_to_message_id=message.id
+ )
+
+ # Cleanup the downloaded images after sending
+ for img in downloaded_images:
+ os.remove(img)
+
+ await msg.delete()
+
+ except Exception as e:
+ # Handle errors while sending images
+ await msg.delete()
+ return await message.reply(f"❍ ᴇʀʀᴏʀ ɪɴ sᴇɴᴅɪɴɢ ɪᴍᴀɢᴇs: {e}")
+
diff --git a/SprotifyMusic/plugins/tools/language.py b/SprotifyMusic/plugins/tools/language.py
new file mode 100644
index 000000000000..10cfa5a23704
--- /dev/null
+++ b/SprotifyMusic/plugins/tools/language.py
@@ -0,0 +1,74 @@
+from pykeyboard import InlineKeyboard
+from pyrogram import filters
+from pyrogram.types import InlineKeyboardButton, Message
+
+from SprotifyMusic import app
+from SprotifyMusic.utils.database import get_lang, set_lang
+from SprotifyMusic.utils.decorators import ActualAdminCB, language, languageCB
+from config import BANNED_USERS
+from strings import get_string, languages_present
+
+
+def lanuages_keyboard(_):
+ keyboard = InlineKeyboard(row_width=2)
+ keyboard.add(
+ *[
+ (
+ InlineKeyboardButton(
+ text=languages_present[i],
+ callback_data=f"languages:{i}",
+ )
+ )
+ for i in languages_present
+ ]
+ )
+ keyboard.row(
+ InlineKeyboardButton(
+ text=_["BACK_BUTTON"],
+ callback_data=f"settingsback_helper",
+ ),
+ InlineKeyboardButton(text=_["CLOSE_BUTTON"], callback_data=f"close"),
+ )
+ return keyboard
+
+
+@app.on_message(filters.command(["lang", "setlang", "language"]) & ~BANNED_USERS)
+@language
+async def langs_command(client, message: Message, _):
+ keyboard = lanuages_keyboard(_)
+ await message.reply_text(
+ _["lang_1"],
+ reply_markup=keyboard,
+ )
+
+
+@app.on_callback_query(filters.regex("LG") & ~BANNED_USERS)
+@languageCB
+async def lanuagecb(client, CallbackQuery, _):
+ try:
+ await CallbackQuery.answer()
+ except:
+ pass
+ keyboard = lanuages_keyboard(_)
+ return await CallbackQuery.edit_message_reply_markup(reply_markup=keyboard)
+
+
+@app.on_callback_query(filters.regex(r"languages:(.*?)") & ~BANNED_USERS)
+@ActualAdminCB
+async def language_markup(client, CallbackQuery, _):
+ langauge = (CallbackQuery.data).split(":")[1]
+ old = await get_lang(CallbackQuery.message.chat.id)
+ if str(old) == str(langauge):
+ return await CallbackQuery.answer(_["lang_4"], show_alert=True)
+ try:
+ _ = get_string(langauge)
+ await CallbackQuery.answer(_["lang_2"], show_alert=True)
+ except:
+ _ = get_string(old)
+ return await CallbackQuery.answer(
+ _["lang_3"],
+ show_alert=True,
+ )
+ await set_lang(CallbackQuery.message.chat.id, langauge)
+ keyboard = lanuages_keyboard(_)
+ return await CallbackQuery.edit_message_reply_markup(reply_markup=keyboard)
\ No newline at end of file
diff --git a/SprotifyMusic/plugins/tools/mongo.py# b/SprotifyMusic/plugins/tools/mongo.py#
new file mode 100644
index 000000000000..3a25631a918a
--- /dev/null
+++ b/SprotifyMusic/plugins/tools/mongo.py#
@@ -0,0 +1,172 @@
+import json
+from datetime import datetime
+from bson import ObjectId
+from pymongo import MongoClient
+
+
+# Helper Functions
+def json_serializer(obj):
+ if isinstance(obj, (datetime,)):
+ return obj.isoformat()
+ if isinstance(obj, ObjectId):
+ return str(obj)
+ raise TypeError("Type not serializable")
+
+
+def delete_collection(client, db_name, col_name):
+ db = client[db_name]
+ db.drop_collection(col_name)
+
+
+def delete_database(client, db_name):
+ client.drop_database(db_name)
+
+
+def list_databases(client):
+ numbered_list = []
+ counter = 1
+ for db_name in client.list_database_names():
+ if db_name not in ["admin", "local"]:
+ numbered_list.append((counter, db_name, None))
+ counter += 1
+ db = client[db_name]
+ for col_name in db.list_collection_names():
+ numbered_list.append((counter, db_name, col_name))
+ counter += 1
+ return numbered_list
+
+
+def delete_all_databases(client):
+ for db_name in client.list_database_names():
+ if db_name not in ["admin", "local"]:
+ db = client[db_name]
+ for col_name in db.list_collection_names():
+ db.drop_collection(col_name)
+ client.drop_database(db_name)
+
+
+def backup_database(old_client):
+ backup_data = {}
+ for db_name in old_client.list_database_names():
+ db = old_client[db_name]
+ backup_data[db_name] = {}
+ for col_name in db.list_collection_names():
+ collection = db[col_name]
+ backup_data[db_name][col_name] = list(collection.find())
+ return backup_data
+
+
+def restore_data(new_client, backup_data):
+ for db_name, collections in backup_data.items():
+ db = new_client[db_name]
+ for col_name, documents in collections.items():
+ collection = db[col_name]
+ if documents:
+ collection.insert_many(documents)
+
+
+
+
+
+
+import re
+import json
+import io
+from pyrogram import filters
+from pyrogram.types import Message
+from TanuMusic import app
+from config import OWNER_ID, MONGO_DB_URI
+from TanuMusic.misc import SUDOERS
+from TanuMusic.utils.pastebin import Bin
+from TanuMusic.utils.database import (
+ delete_collection,
+ delete_database,
+ list_databases,
+ delete_all_databases,
+ backup_database,
+ restore_data,
+)
+
+
+# Helper Function for Mongo URL
+def get_mongo_url(message):
+ if len(message.command) > 1:
+ return message.command[1]
+ return MONGO_DB_URI
+
+
+# Commands
+@app.on_message(filters.command(["mongochk"]))
+async def mongo_check_command(client, message: Message):
+ if len(message.command) < 2:
+ await message.reply("Please provide your MongoDB URL with the command: `/mongochk your_mongo_url`")
+ return
+ ok = await message.reply_text("**Please wait, I am checking your MongoDB...**")
+ mongo_url = message.command[1]
+
+ try:
+ with MongoClient(mongo_url, serverSelectionTimeoutMS=5000) as mongo_client:
+ databases = mongo_client.list_database_names()
+ result = f"**MongoDB URL** `{mongo_url}` **is valid**.\n\n**Available Databases:**\n"
+ for db_name in databases:
+ if db_name not in ["admin", "local"]:
+ result += f"\n`{db_name}`:\n"
+ db = mongo_client[db_name]
+ for col_name in db.list_collection_names():
+ result += f" `{col_name}` ({db[col_name].count_documents({})} documents)\n"
+
+ if len(result) > 4096:
+ paste_url = await Bin(result)
+ await ok.delete()
+ await message.reply(f"**The database list is too long to send here. You can view it at:** {paste_url}")
+ else:
+ await ok.delete()
+ await message.reply(result)
+
+ except Exception as e:
+ await ok.delete()
+ await message.reply(f"**Failed to connect to MongoDB**\n\n**Your MongoDB is dead ❌**\n\n**Error:** `{e}`")
+
+
+@app.on_message(filters.command(["deletedb", "deldb"]) & filters.user(OWNER_ID))
+async def delete_db_command(client, message: Message):
+ if len(message.command) < 2:
+ await message.reply("Please provide the database or collection to delete.")
+ return
+
+ try:
+ mongo_url = get_mongo_url(message)
+ with MongoClient(mongo_url, serverSelectionTimeoutMS=5000) as mongo_client:
+ databases_and_collections = list_databases(mongo_client)
+
+ if message.command[1].lower() == "all":
+ delete_all_databases(mongo_client)
+ await message.reply("**All databases and collections have been deleted successfully. 🧹**")
+ else:
+ await message.reply("Invalid command format.")
+ except Exception as e:
+ await message.reply(f"**Failed to delete databases:** {e}")
+
+
+@app.on_message(filters.command(["transferdb"]) & filters.user(OWNER_ID))
+async def transfer_db_command(client, message: Message):
+ if len(message.command) < 3:
+ await message.reply("Please provide both source and target MongoDB URLs.")
+ return
+
+ main_mongo_url = message.command[1]
+ target_mongo_url = message.command[2]
+
+ if not re.match(r"mongodb(?:\+srv)?:\/\/[^\s]+", target_mongo_url):
+ await message.reply("**The target MongoDB URL format is invalid! ❌**")
+ return
+
+ try:
+ with MongoClient(main_mongo_url, serverSelectionTimeoutMS=5000) as main_client:
+ backup_data = backup_database(main_client)
+ with MongoClient(target_mongo_url, serverSelectionTimeoutMS=5000) as target_client:
+ restore_data(target_client, backup_data)
+
+ await message.reply("**Data transfer to the new MongoDB is successful! 🎉**")
+ except Exception as e:
+ await message.reply(f"**Data transfer failed:** {e}")
\ No newline at end of file
diff --git a/SprotifyMusic/plugins/tools/ping.py b/SprotifyMusic/plugins/tools/ping.py
new file mode 100644
index 000000000000..2635efd0d5f8
--- /dev/null
+++ b/SprotifyMusic/plugins/tools/ping.py
@@ -0,0 +1,33 @@
+import time
+from datetime import datetime
+
+from pyrogram import filters
+from pyrogram.types import Message
+from pyrogram.enums import ParseMode
+
+from SprotifyMusic import app
+from SprotifyMusic.core.call import Sprotify
+from SprotifyMusic.utils import bot_sys_stats
+from SprotifyMusic.utils.decorators.language import language
+from SprotifyMusic.utils.inline import supp_markup
+from config import BANNED_USERS, PING_IMG_URL
+
+
+@app.on_message(filters.command(["ping", "alive"]) & ~BANNED_USERS)
+@language
+async def ping_com(client, message: Message, _):
+ start = datetime.now()
+ response = await message.reply_photo(
+ photo=PING_IMG_URL,
+ caption=_["ping_1"].format(app.mention),
+ quote=True,
+ )
+ pytgping = await Sprotify.ping()
+ UP, CPU, RAM, DISK = await bot_sys_stats()
+ resp = (datetime.now() - start).microseconds / 1000
+ await response.edit_text(
+ _["ping_2"].format(resp, app.mention, UP, RAM, CPU, DISK, pytgping),
+ reply_markup=supp_markup(_),
+ parse_mode=ParseMode.MARKDOWN,
+ )
+
\ No newline at end of file
diff --git a/SprotifyMusic/plugins/tools/pypi.py b/SprotifyMusic/plugins/tools/pypi.py
new file mode 100644
index 000000000000..93079ec9a985
--- /dev/null
+++ b/SprotifyMusic/plugins/tools/pypi.py
@@ -0,0 +1,49 @@
+import requests
+from pyrogram import filters
+from pyrogram.types import InlineKeyboardButton, InlineKeyboardMarkup
+
+from SprotifyMusic import app
+
+
+def get_pypi_info(package_name):
+ try:
+ api_url = f"https://pypi.org/pypi/{package_name}/json"
+ response = requests.get(api_url)
+ if response.status_code == 200:
+ pypi_info = response.json()
+ return pypi_info
+ else:
+ return None
+ except Exception as e:
+ print(f"Error fetching PyPI information: {e}")
+ return None
+
+
+@app.on_message(filters.command("pypi", prefixes="/"))
+async def pypi_info_command(client, message):
+ try:
+ package_name = message.command[1]
+ pypi_info = get_pypi_info(package_name)
+
+ if pypi_info:
+ info_message = (
+ f"❖ ᴅᴇᴀʀ {message.from_user.mention} \n "
+ f"● ʜᴇʀᴇ ɪs ʏᴏᴜʀ ᴘᴀᴋᴀɢᴇ ᴅᴇᴛᴀɪʟs \n\n "
+ f"● ᴘᴀᴋᴀɢᴇ ɴᴀᴍᴇ ➥ {pypi_info['info']['name']}\n\n"
+ f"● ʟᴀᴛᴇsᴛ ᴠᴇʀsɪᴏɴ ➥ {pypi_info['info']['version']}\n\n"
+ f"● ᴅᴇsᴄʀɪᴘᴛɪᴏɴ ➥ {pypi_info['info']['summary']}\n\n"
+ f"● ᴘʀᴏᴊᴇᴄᴛ ᴜʀʟ ➥ {pypi_info['info']['project_urls']['Homepage']}"
+ )
+ close_markup = InlineKeyboardMarkup(
+ [[InlineKeyboardButton(text="ᴄʟᴏsᴇ", callback_data="close")]]
+ )
+ await message.reply_text(info_message, reply_markup=close_markup)
+ else:
+ await message.reply_text(
+ f"ᴘᴀᴄᴋᴀɢᴇ '{package_name}' ɴᴏᴛ ғᴏᴜɴᴅ \n ᴘʟᴇᴀsᴇ ᴅᴏɴᴛ ᴛʀʏ ᴀɢᴀɪɴ ʟᴀᴛᴇʀ."
+ )
+
+ except IndexError:
+ await message.reply_text(
+ "ᴘʟᴇᴀsᴇ ᴘʀᴏᴠɪᴅᴇ ᴀ ᴘᴀᴄᴋᴀɢᴇ ɴᴀᴍᴇ ᴀғᴛᴇʀ ᴛʜᴇ /pypi ᴄᴏᴍᴍᴀɴᴅ."
+ )
\ No newline at end of file
diff --git a/SprotifyMusic/plugins/tools/queue.py b/SprotifyMusic/plugins/tools/queue.py
new file mode 100644
index 000000000000..04b19163818f
--- /dev/null
+++ b/SprotifyMusic/plugins/tools/queue.py
@@ -0,0 +1,269 @@
+import asyncio
+import os
+
+from pyrogram import filters
+from pyrogram.errors import FloodWait
+from pyrogram.types import CallbackQuery, InputMediaPhoto, Message
+
+import config
+from SprotifyMusic import app
+from SprotifyMusic.misc import db
+from SprotifyMusic.utils import Bin, get_channeplayCB, seconds_to_min
+from SprotifyMusic.utils.database import get_cmode, is_active_chat, is_music_playing
+from SprotifyMusic.utils.decorators.language import language, languageCB
+from SprotifyMusic.utils.inline import queue_back_markup, queue_markup
+from config import BANNED_USERS
+
+basic = {}
+
+
+def get_image(videoid):
+ if os.path.isfile(f"cache/{videoid}.png"):
+ return f"cache/{videoid}.png"
+ else:
+ return config.YOUTUBE_IMG_URL
+
+
+def get_duration(playing):
+ file_path = playing[0]["file"]
+ if "index_" in file_path or "live_" in file_path:
+ return "Unknown"
+ duration_seconds = int(playing[0]["seconds"])
+ if duration_seconds == 0:
+ return "Unknown"
+ else:
+ return "Inline"
+
+
+@app.on_message(
+ filters.command(["queue", "cqueue", "player", "cplayer", "playing", "cplaying"])
+ & filters.group
+ & ~BANNED_USERS
+)
+@language
+async def get_queue(client, message: Message, _):
+ if message.command[0][0] == "c":
+ chat_id = await get_cmode(message.chat.id)
+ if chat_id is None:
+ return await message.reply_text(_["setting_7"])
+ try:
+ await app.get_chat(chat_id)
+ except:
+ return await message.reply_text(_["cplay_4"])
+ cplay = True
+ else:
+ chat_id = message.chat.id
+ cplay = False
+ if not await is_active_chat(chat_id):
+ return await message.reply_text(_["general_5"])
+ got = db.get(chat_id)
+ if not got:
+ return await message.reply_text(_["queue_2"])
+ file = got[0]["file"]
+ videoid = got[0]["vidid"]
+ user = got[0]["by"]
+ title = (got[0]["title"]).title()
+ typo = (got[0]["streamtype"]).title()
+ DUR = get_duration(got)
+ if "live_" in file:
+ IMAGE = get_image(videoid)
+ elif "vid_" in file:
+ IMAGE = get_image(videoid)
+ elif "index_" in file:
+ IMAGE = config.STREAM_IMG_URL
+ else:
+ if videoid == "telegram":
+ IMAGE = (
+ config.TELEGRAM_AUDIO_URL
+ if typo == "Audio"
+ else config.TELEGRAM_VIDEO_URL
+ )
+ elif videoid == "soundcloud":
+ IMAGE = config.SOUNCLOUD_IMG_URL
+ else:
+ IMAGE = get_image(videoid)
+ send = _["queue_6"] if DUR == "Unknown" else _["queue_7"]
+ cap = _["queue_8"].format(app.mention, title, typo, user, send)
+ upl = (
+ queue_markup(_, DUR, "c" if cplay else "g", videoid)
+ if DUR == "Unknown"
+ else queue_markup(
+ _,
+ DUR,
+ "c" if cplay else "g",
+ videoid,
+ seconds_to_min(got[0]["played"]),
+ got[0]["dur"],
+ )
+ )
+ basic[videoid] = True
+ mystic = await message.reply_photo(IMAGE, caption=cap, reply_markup=upl)
+ if DUR != "Unknown":
+ try:
+ while db[chat_id][0]["vidid"] == videoid:
+ await asyncio.sleep(5)
+ if await is_active_chat(chat_id):
+ if basic[videoid]:
+ if await is_music_playing(chat_id):
+ try:
+ buttons = queue_markup(
+ _,
+ DUR,
+ "c" if cplay else "g",
+ videoid,
+ seconds_to_min(db[chat_id][0]["played"]),
+ db[chat_id][0]["dur"],
+ )
+ await mystic.edit_reply_markup(reply_markup=buttons)
+ except FloodWait:
+ pass
+ else:
+ pass
+ else:
+ break
+ else:
+ break
+ except:
+ return
+
+
+@app.on_callback_query(filters.regex("GetTimer") & ~BANNED_USERS)
+async def quite_timer(client, CallbackQuery: CallbackQuery):
+ try:
+ await CallbackQuery.answer()
+ except:
+ pass
+
+
+@app.on_callback_query(filters.regex("GetQueued") & ~BANNED_USERS)
+@languageCB
+async def queued_tracks(client, CallbackQuery: CallbackQuery, _):
+ callback_data = CallbackQuery.data.strip()
+ callback_request = callback_data.split(None, 1)[1]
+ what, videoid = callback_request.split("|")
+ try:
+ chat_id, channel = await get_channeplayCB(_, what, CallbackQuery)
+ except:
+ return
+ if not await is_active_chat(chat_id):
+ return await CallbackQuery.answer(_["general_5"], show_alert=True)
+ got = db.get(chat_id)
+ if not got:
+ return await CallbackQuery.answer(_["queue_2"], show_alert=True)
+ if len(got) == 1:
+ return await CallbackQuery.answer(_["queue_5"], show_alert=True)
+ await CallbackQuery.answer()
+ basic[videoid] = False
+ buttons = queue_back_markup(_, what)
+ med = InputMediaPhoto(
+ media="https://telegra.ph//file/6f7d35131f69951c74ee5.jpg",
+ caption=_["queue_1"],
+ )
+ await CallbackQuery.edit_message_media(media=med)
+ j = 0
+ msg = ""
+ for x in got:
+ j += 1
+ if j == 1:
+ msg += f'Streaming :\n\n✨ Title : {x["title"]}\nDuration : {x["dur"]}\nBy : {x["by"]}\n\n'
+ elif j == 2:
+ msg += f'Queued :\n\n✨ Title : {x["title"]}\nDuration : {x["dur"]}\nBy : {x["by"]}\n\n'
+ else:
+ msg += f'✨ Title : {x["title"]}\nDuration : {x["dur"]}\nBy : {x["by"]}\n\n'
+ if "Queued" in msg:
+ if len(msg) < 700:
+ await asyncio.sleep(1)
+ return await CallbackQuery.edit_message_text(msg, reply_markup=buttons)
+ if "✨" in msg:
+ msg = msg.replace("✨", "")
+ link = await Bin(msg)
+ med = InputMediaPhoto(media=link, caption=_["queue_3"].format(link))
+ await CallbackQuery.edit_message_media(media=med, reply_markup=buttons)
+ else:
+ await asyncio.sleep(1)
+ return await CallbackQuery.edit_message_text(msg, reply_markup=buttons)
+
+
+@app.on_callback_query(filters.regex("queue_back_timer") & ~BANNED_USERS)
+@languageCB
+async def queue_back(client, CallbackQuery: CallbackQuery, _):
+ callback_data = CallbackQuery.data.strip()
+ cplay = callback_data.split(None, 1)[1]
+ try:
+ chat_id, channel = await get_channeplayCB(_, cplay, CallbackQuery)
+ except:
+ return
+ if not await is_active_chat(chat_id):
+ return await CallbackQuery.answer(_["general_5"], show_alert=True)
+ got = db.get(chat_id)
+ if not got:
+ return await CallbackQuery.answer(_["queue_2"], show_alert=True)
+ await CallbackQuery.answer(_["set_cb_5"], show_alert=True)
+ file = got[0]["file"]
+ videoid = got[0]["vidid"]
+ user = got[0]["by"]
+ title = (got[0]["title"]).title()
+ typo = (got[0]["streamtype"]).title()
+ DUR = get_duration(got)
+ if "live_" in file:
+ IMAGE = get_image(videoid)
+ elif "vid_" in file:
+ IMAGE = get_image(videoid)
+ elif "index_" in file:
+ IMAGE = config.STREAM_IMG_URL
+ else:
+ if videoid == "telegram":
+ IMAGE = (
+ config.TELEGRAM_AUDIO_URL
+ if typo == "Audio"
+ else config.TELEGRAM_VIDEO_URL
+ )
+ elif videoid == "soundcloud":
+ IMAGE = config.SOUNCLOUD_IMG_URL
+ else:
+ IMAGE = get_image(videoid)
+ send = _["queue_6"] if DUR == "Unknown" else _["queue_7"]
+ cap = _["queue_8"].format(app.mention, title, typo, user, send)
+ upl = (
+ queue_markup(_, DUR, cplay, videoid)
+ if DUR == "Unknown"
+ else queue_markup(
+ _,
+ DUR,
+ cplay,
+ videoid,
+ seconds_to_min(got[0]["played"]),
+ got[0]["dur"],
+ )
+ )
+ basic[videoid] = True
+
+ med = InputMediaPhoto(media=IMAGE, caption=cap)
+ mystic = await CallbackQuery.edit_message_media(media=med, reply_markup=upl)
+ if DUR != "Unknown":
+ try:
+ while db[chat_id][0]["vidid"] == videoid:
+ await asyncio.sleep(5)
+ if await is_active_chat(chat_id):
+ if basic[videoid]:
+ if await is_music_playing(chat_id):
+ try:
+ buttons = queue_markup(
+ _,
+ DUR,
+ cplay,
+ videoid,
+ seconds_to_min(db[chat_id][0]["played"]),
+ db[chat_id][0]["dur"],
+ )
+ await mystic.edit_reply_markup(reply_markup=buttons)
+ except FloodWait:
+ pass
+ else:
+ pass
+ else:
+ break
+ else:
+ break
+ except:
+ return
diff --git a/SprotifyMusic/plugins/tools/quoto.py b/SprotifyMusic/plugins/tools/quoto.py
new file mode 100644
index 000000000000..67d498c10889
--- /dev/null
+++ b/SprotifyMusic/plugins/tools/quoto.py
@@ -0,0 +1,290 @@
+from io import BytesIO
+from pyrogram import Client, filters
+from pyrogram.types import Message
+from SprotifyMusic import app
+from httpx import AsyncClient, Timeout
+
+
+# -----------------------------------------------------------------
+fetch = AsyncClient(
+ http2=True,
+ verify=False,
+ headers={
+ "Accept-Language": "id-ID",
+ "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36 Edge/107.0.1418.42",
+ },
+ timeout=Timeout(20),
+)
+# ------------------------------------------------------------------------
+class QuotlyException(Exception):
+ pass
+# --------------------------------------------------------------------------
+async def get_message_sender_id(ctx: Message):
+ if ctx.forward_date:
+ if ctx.forward_sender_name:
+ return 1
+ elif ctx.forward_from:
+ return ctx.forward_from.id
+ elif ctx.forward_from_chat:
+ return ctx.forward_from_chat.id
+ else:
+ return 1
+ elif ctx.from_user:
+ return ctx.from_user.id
+ elif ctx.sender_chat:
+ return ctx.sender_chat.id
+ else:
+ return 1
+# -----------------------------------------------------------------------------------------
+async def get_message_sender_name(ctx: Message):
+ if ctx.forward_date:
+ if ctx.forward_sender_name:
+ return ctx.forward_sender_name
+ elif ctx.forward_from:
+ return (
+ f"{ctx.forward_from.first_name} {ctx.forward_from.last_name}"
+ if ctx.forward_from.last_name
+ else ctx.forward_from.first_name
+ )
+# ---------------------------------------------------------------------------------------------------
+ elif ctx.forward_from_chat:
+ return ctx.forward_from_chat.title
+ else:
+ return ""
+ elif ctx.from_user:
+ if ctx.from_user.last_name:
+ return f"{ctx.from_user.first_name} {ctx.from_user.last_name}"
+ else:
+ return ctx.from_user.first_name
+ elif ctx.sender_chat:
+ return ctx.sender_chat.title
+ else:
+ return ""
+# ---------------------------------------------------------------------------------------------------
+async def get_custom_emoji(ctx: Message):
+ if ctx.forward_date:
+ return (
+ ""
+ if ctx.forward_sender_name
+ or not ctx.forward_from
+ and ctx.forward_from_chat
+ or not ctx.forward_from
+ else ctx.forward_from.emoji_status.custom_emoji_id
+ )
+
+ return ctx.from_user.emoji_status.custom_emoji_id if ctx.from_user else ""
+
+# ---------------------------------------------------------------------------------------------------
+async def get_message_sender_username(ctx: Message):
+ if ctx.forward_date:
+ if (
+ not ctx.forward_sender_name
+ and not ctx.forward_from
+ and ctx.forward_from_chat
+ and ctx.forward_from_chat.username
+ ):
+ return ctx.forward_from_chat.username
+ elif (
+ not ctx.forward_sender_name
+ and not ctx.forward_from
+ and ctx.forward_from_chat
+ or ctx.forward_sender_name
+ or not ctx.forward_from
+ ):
+ return ""
+ else:
+ return ctx.forward_from.username or ""
+ elif ctx.from_user and ctx.from_user.username:
+ return ctx.from_user.username
+ elif (
+ ctx.from_user
+ or ctx.sender_chat
+ and not ctx.sender_chat.username
+ or not ctx.sender_chat
+ ):
+ return ""
+ else:
+ return ctx.sender_chat.username
+# ------------------------------------------------------------------------
+async def get_message_sender_photo(ctx: Message):
+ if ctx.forward_date:
+ if (
+ not ctx.forward_sender_name
+ and not ctx.forward_from
+ and ctx.forward_from_chat
+ and ctx.forward_from_chat.photo
+ ):
+ return {
+ "small_file_id": ctx.forward_from_chat.photo.small_file_id,
+ "small_photo_unique_id": ctx.forward_from_chat.photo.small_photo_unique_id,
+ "big_file_id": ctx.forward_from_chat.photo.big_file_id,
+ "big_photo_unique_id": ctx.forward_from_chat.photo.big_photo_unique_id,
+ }
+ elif (
+ not ctx.forward_sender_name
+ and not ctx.forward_from
+ and ctx.forward_from_chat
+ or ctx.forward_sender_name
+ or not ctx.forward_from
+ ):
+ return ""
+ else:
+ return (
+ {
+ "small_file_id": ctx.forward_from.photo.small_file_id,
+ "small_photo_unique_id": ctx.forward_from.photo.small_photo_unique_id,
+ "big_file_id": ctx.forward_from.photo.big_file_id,
+ "big_photo_unique_id": ctx.forward_from.photo.big_photo_unique_id,
+ }
+ if ctx.forward_from.photo
+ else ""
+ )
+# ---------------------------------------------------------------------------------
+ elif ctx.from_user and ctx.from_user.photo:
+ return {
+ "small_file_id": ctx.from_user.photo.small_file_id,
+ "small_photo_unique_id": ctx.from_user.photo.small_photo_unique_id,
+ "big_file_id": ctx.from_user.photo.big_file_id,
+ "big_photo_unique_id": ctx.from_user.photo.big_photo_unique_id,
+ }
+ elif (
+ ctx.from_user
+ or ctx.sender_chat
+ and not ctx.sender_chat.photo
+ or not ctx.sender_chat
+ ):
+ return ""
+ else:
+ return {
+ "small_file_id": ctx.sender_chat.photo.small_file_id,
+ "small_photo_unique_id": ctx.sender_chat.photo.small_photo_unique_id,
+ "big_file_id": ctx.sender_chat.photo.big_file_id,
+ "big_photo_unique_id": ctx.sender_chat.photo.big_photo_unique_id,
+ }
+# ---------------------------------------------------------------------------------------------------
+async def get_text_or_caption(ctx: Message):
+ if ctx.text:
+ return ctx.text
+ elif ctx.caption:
+ return ctx.caption
+ else:
+ return ""
+# ---------------------------------------------------------------------------------------------------
+async def pyrogram_to_quotly(messages, is_reply):
+ if not isinstance(messages, list):
+ messages = [messages]
+ payload = {
+ "type": "quote",
+ "format": "png",
+ "backgroundColor": "#1b1429",
+ "messages": [],
+ }
+# ------------------------------------------------------------------------------------------------------------
+ for message in messages:
+ the_message_dict_to_append = {}
+ if message.entities:
+ the_message_dict_to_append["entities"] = [
+ {
+ "type": entity.type.name.lower(),
+ "offset": entity.offset,
+ "length": entity.length,
+ }
+ for entity in message.entities
+ ]
+ elif message.caption_entities:
+ the_message_dict_to_append["entities"] = [
+ {
+ "type": entity.type.name.lower(),
+ "offset": entity.offset,
+ "length": entity.length,
+ }
+ for entity in message.caption_entities
+ ]
+ else:
+ the_message_dict_to_append["entities"] = []
+ the_message_dict_to_append["chatId"] = await get_message_sender_id(message)
+ the_message_dict_to_append["text"] = await get_text_or_caption(message)
+ the_message_dict_to_append["avatar"] = True
+ the_message_dict_to_append["from"] = {}
+ the_message_dict_to_append["from"]["id"] = await get_message_sender_id(message)
+ the_message_dict_to_append["from"]["name"] = await get_message_sender_name(
+ message
+ )
+ the_message_dict_to_append["from"][
+ "username"
+ ] = await get_message_sender_username(message)
+ the_message_dict_to_append["from"]["type"] = message.chat.type.name.lower()
+ the_message_dict_to_append["from"]["photo"] = await get_message_sender_photo(
+ message
+ )
+ if message.reply_to_message and is_reply:
+ the_message_dict_to_append["replyMessage"] = {
+ "name": await get_message_sender_name(message.reply_to_message),
+ "text": await get_text_or_caption(message.reply_to_message),
+ "chatId": await get_message_sender_id(message.reply_to_message),
+ }
+ else:
+ the_message_dict_to_append["replyMessage"] = {}
+ payload["messages"].append(the_message_dict_to_append)
+ r = await fetch.post("https://bot.lyo.su/quote/generate.png", json=payload)
+ if not r.is_error:
+ return r.read()
+ else:
+ raise QuotlyException(r.json())
+# ------------------------------------------------------------------------------------------
+
+def isArgInt(txt) -> list:
+ count = txt
+ try:
+ count = int(count)
+ return [True, count]
+ except ValueError:
+ return [False, 0]
+
+# ---------------------------------------------------------------------------------------------------
+@app.on_message(filters.command(["q", "r"]) & filters.reply)
+async def msg_quotly_cmd(self: app, ctx: Message):
+ is_reply = False
+ if ctx.command[0].endswith("r"):
+ is_reply = True
+ if len(ctx.text.split()) > 1:
+ check_arg = isArgInt(ctx.command[1])
+ if check_arg[0]:
+ if check_arg[1] < 2 or check_arg[1] > 10:
+ return await ctx.reply_msg("Invalid range", del_in=6)
+ try:
+ messages = [
+ i
+ for i in await self.get_messages(
+ chat_id=ctx.chat.id,
+ message_ids=range(
+ ctx.reply_to_message.id,
+ ctx.reply_to_message.id + (check_arg[1] + 5),
+ ),
+ replies=-1,
+ )
+ if not i.empty and not i.media
+ ]
+ except Exception:
+ return await ctx.reply_text("🤷🏻♂️")
+ try:
+ make_quotly = await pyrogram_to_quotly(messages, is_reply=is_reply)
+ bio_sticker = BytesIO(make_quotly)
+ bio_sticker.name = "misskatyquote_sticker.webp"
+ return await ctx.reply_sticker(bio_sticker)
+ except Exception:
+ return await ctx.reply_msg("🤷🏻♂️")
+ try:
+ messages_one = await self.get_messages(
+ chat_id=ctx.chat.id, message_ids=ctx.reply_to_message.id, replies=-1
+ )
+ messages = [messages_one]
+ except Exception:
+ return await ctx.reply_msg("🤷🏻♂️")
+ try:
+ make_quotly = await pyrogram_to_quotly(messages, is_reply=is_reply)
+ bio_sticker = BytesIO(make_quotly)
+ bio_sticker.name = "misskatyquote_sticker.webp"
+ return await ctx.reply_sticker(bio_sticker)
+ except Exception as e:
+ return await ctx.reply_msg(f"ERROR: {e}")
\ No newline at end of file
diff --git a/SprotifyMusic/plugins/tools/reload.py b/SprotifyMusic/plugins/tools/reload.py
new file mode 100644
index 000000000000..42750ae75a85
--- /dev/null
+++ b/SprotifyMusic/plugins/tools/reload.py
@@ -0,0 +1,125 @@
+import asyncio
+import time
+
+from pyrogram import filters
+from pyrogram.enums import ChatMembersFilter
+from pyrogram.types import CallbackQuery, Message
+
+from SprotifyMusic import app
+from SprotifyMusic.core.call import Sprotify
+from SprotifyMusic.misc import db
+from SprotifyMusic.utils.database import get_assistant, get_authuser_names, get_cmode
+from SprotifyMusic.utils.decorators import ActualAdminCB, AdminActual, language
+from SprotifyMusic.utils.formatters import alpha_to_int, get_readable_time
+from config import BANNED_USERS, adminlist, lyrical
+
+rel = {}
+
+
+@app.on_message(
+ filters.command(["admincache", "reload", "refresh"]) & filters.group & ~BANNED_USERS
+)
+@language
+async def reload_admin_cache(client, message: Message, _):
+ try:
+ if message.chat.id not in rel:
+ rel[message.chat.id] = {}
+ else:
+ saved = rel[message.chat.id]
+ if saved > time.time():
+ left = get_readable_time((int(saved) - int(time.time())))
+ return await message.reply_text(_["reload_1"].format(left))
+ adminlist[message.chat.id] = []
+ async for user in app.get_chat_members(
+ message.chat.id, filter=ChatMembersFilter.ADMINISTRATORS
+ ):
+ if user.privileges.can_manage_video_chats:
+ adminlist[message.chat.id].append(user.user.id)
+ authusers = await get_authuser_names(message.chat.id)
+ for user in authusers:
+ user_id = await alpha_to_int(user)
+ adminlist[message.chat.id].append(user_id)
+ now = int(time.time()) + 180
+ rel[message.chat.id] = now
+ await message.reply_text(_["reload_2"])
+ except:
+ await message.reply_text(_["reload_3"])
+
+
+@app.on_message(filters.command(["reboot"]) & filters.group & ~BANNED_USERS)
+@AdminActual
+async def restartbot(client, message: Message, _):
+ mystic = await message.reply_text(_["reload_4"].format(app.mention))
+ await asyncio.sleep(1)
+ try:
+ db[message.chat.id] = []
+ await Sprotify.stop_stream_force(message.chat.id)
+ except:
+ pass
+ userbot = await get_assistant(message.chat.id)
+ try:
+ if message.chat.username:
+ await userbot.resolve_peer(message.chat.username)
+ else:
+ await userbot.resolve_peer(message.chat.id)
+ except:
+ pass
+ chat_id = await get_cmode(message.chat.id)
+ if chat_id:
+ try:
+ got = await app.get_chat(chat_id)
+ except:
+ pass
+ userbot = await get_assistant(chat_id)
+ try:
+ if got.username:
+ await userbot.resolve_peer(got.username)
+ else:
+ await userbot.resolve_peer(chat_id)
+ except:
+ pass
+ try:
+ db[chat_id] = []
+ await Sprotify.stop_stream_force(chat_id)
+ except:
+ pass
+ return await mystic.edit_text(_["reload_5"].format(app.mention))
+
+
+@app.on_callback_query(filters.regex("close") & ~BANNED_USERS)
+async def close_menu(_, query: CallbackQuery):
+ try:
+ await query.answer()
+ await query.message.delete()
+ umm = await query.message.reply_text(
+ f"⬤ ᴄʟᴏsᴇᴅ ʙʏ ➥ {query.from_user.mention}"
+ )
+ await asyncio.sleep(7)
+ await umm.delete()
+ except:
+ pass
+
+
+@app.on_callback_query(filters.regex("stop_downloading") & ~BANNED_USERS)
+@ActualAdminCB
+async def stop_download(client, CallbackQuery: CallbackQuery, _):
+ message_id = CallbackQuery.message.id
+ task = lyrical.get(message_id)
+ if not task:
+ return await CallbackQuery.answer(_["tg_4"], show_alert=True)
+ if task.done() or task.cancelled():
+ return await CallbackQuery.answer(_["tg_5"], show_alert=True)
+ if not task.done():
+ try:
+ task.cancel()
+ try:
+ lyrical.pop(message_id)
+ except:
+ pass
+ await CallbackQuery.answer(_["tg_6"], show_alert=True)
+ return await CallbackQuery.edit_message_text(
+ _["tg_7"].format(CallbackQuery.from_user.mention)
+ )
+ except:
+ return await CallbackQuery.answer(_["tg_8"], show_alert=True)
+ await CallbackQuery.answer(_["tg_9"], show_alert=True)
diff --git a/SprotifyMusic/plugins/tools/sangmata.py# b/SprotifyMusic/plugins/tools/sangmata.py#
new file mode 100644
index 000000000000..5e3003cba1c1
--- /dev/null
+++ b/SprotifyMusic/plugins/tools/sangmata.py#
@@ -0,0 +1,54 @@
+import asyncio
+import random
+
+from pyrogram import Client, filters
+from pyrogram.types import Message
+from pyrogram.raw.functions.messages import DeleteHistory
+
+from TanuMusic import userbot as us, app
+from TanuMusic.core.userbot import assistants
+
+
+@app.on_message(filters.command("sg"))
+async def sg(client: Client, message: Message):
+ if len(message.text.split()) < 1 and not message.reply_to_message:
+ return await message.reply("sg username/id/reply")
+ if message.reply_to_message:
+ args = message.reply_to_message.from_user.id
+ else:
+ args = message.text.split()[1]
+ lol = await message.reply("🧨")
+ if args:
+ try:
+ user = await client.get_users(f"{args}")
+ except Exception:
+ return await lol.edit("✦ Please specify a valid user!
")
+ bo = ["sangmata_bot", "sangmata_beta_bot"]
+ sg = random.choice(bo)
+ if 1 in assistants:
+ ubot = us.one
+
+ try:
+ a = await ubot.send_message(sg, f"{user.id}")
+ await a.delete()
+ except Exception as e:
+ return await lol.edit(e)
+ await asyncio.sleep(1)
+
+ async for stalk in ubot.search_messages(a.chat.id):
+ if stalk.text == None:
+ continue
+ if not stalk:
+ await message.reply("botnya ngambek")
+ elif stalk:
+ await message.reply(f"{stalk.text}")
+ break # Exit the loop after displaying one message
+
+ try:
+ user_info = await ubot.resolve_peer(sg)
+ await ubot.send(DeleteHistory(peer=user_info, max_id=0, revoke=True))
+ except Exception:
+ pass
+
+ await lol.delete()
+
diff --git a/SprotifyMusic/plugins/tools/song.py b/SprotifyMusic/plugins/tools/song.py
new file mode 100644
index 000000000000..e831d5af1d63
--- /dev/null
+++ b/SprotifyMusic/plugins/tools/song.py
@@ -0,0 +1,38 @@
+import os
+import requests
+from pyrogram import Client, filters
+from SprotifyMusic import app
+
+def fetch_song(song_name):
+ url = f"https://song-teleservice.vercel.app/song?songName={song_name.replace(' ', '%20')}"
+ try:
+ response = requests.get(url)
+ return response.json() if response.status_code == 200 and "downloadLink" in response.json() else None
+ except Exception as e:
+ print(f"API Error: {e}")
+ return None
+
+@app.on_message(filters.command("song"))
+async def handle_song(client, message):
+ song_name = message.text.split(" ", 1)[1] if len(message.text.split(" ", 1)) > 1 else None
+ if not song_name:
+ return await message.reply("ᴘʟᴇᴀsᴇ ᴘʀᴏᴠɪᴅᴇ ᴀ sᴏɴɢ ɴᴀᴍᴇ ᴀғᴛᴇʀ ᴛʜᴇ /song ᴄᴏᴍᴍᴀɴᴅ..")
+
+ song_info = fetch_song(song_name)
+ if not song_info:
+ return await message.reply(f"sᴏʀʀʏ, ɪ ᴄᴏᴜʟᴅɴ'ᴛ ғɪɴᴅ ᴛʜᴇ sᴏɴɢ '{song_name}'.")
+
+ filename = f"{song_info['trackName']}.mp3"
+ download_url = song_info['downloadLink']
+
+ # Download and save the file
+ with requests.get(download_url, stream=True) as r, open(filename, "wb") as file:
+ for chunk in r.iter_content(1024):
+ if chunk:
+ file.write(chunk)
+
+ caption = (f"""❖ sᴏɴɢ ɴᴀᴍᴇ ➥ {song_info['trackName']}\n\n● ᴀʟʙᴜᴍ ➥ {song_info['album']}\n ● ʀᴇʟᴇᴀsᴇ ᴅᴀᴛᴇ ➥ {song_info['releaseDate']}\n● ʀᴇǫᴜᴇsᴛᴇᴅ ʙʏ ➥ {message.from_user.mention}\n❖ ᴘᴏᴡᴇʀᴇᴅ ʙʏ ➥ ˹ ᴛᴀɴᴜ ꭙ ᴍᴜsɪᴄ™""")
+
+ # Send audio and clean up
+ await message.reply_audio(audio=open(filename, "rb"), caption=caption)
+ os.remove(filename)
\ No newline at end of file
diff --git a/SprotifyMusic/plugins/tools/speedtest.py b/SprotifyMusic/plugins/tools/speedtest.py
new file mode 100644
index 000000000000..171f9f57867f
--- /dev/null
+++ b/SprotifyMusic/plugins/tools/speedtest.py
@@ -0,0 +1,52 @@
+import asyncio
+import speedtest
+from pyrogram import filters
+from pyrogram.types import Message
+from SprotifyMusic import app
+from SprotifyMusic.misc import SUDOERS
+from SprotifyMusic.utils.decorators.language import language
+
+
+def testspeed(m, _):
+ try:
+ test = speedtest.Speedtest()
+ test.get_best_server()
+ m = m.edit_text(_["server_12"]) # Informing about selecting the best server
+ test.download()
+ m = m.edit_text(_["server_13"]) # Informing about download speed testing
+ test.upload()
+ test.results.share()
+ result = test.results.dict() # Fetching results as a dictionary
+ m = m.edit_text(_["server_14"]) # Informing about finishing the test
+ return result
+ except Exception as e:
+ m.edit_text(f"{e}
") # Display error
+ return None
+
+
+@app.on_message(filters.command(["speedtest", "spt"]))
+@language
+async def speedtest_function(client, message: Message, _):
+ # Initial response
+ m = await message.reply_text(_["server_11"]) # Starting speed test message
+ loop = asyncio.get_event_loop()
+ result = await loop.run_in_executor(None, testspeed, m, _)
+
+ if result is None:
+ return # Exit if an error occurred and was already handled in testspeed()
+
+ # Prepare and format output
+ output = _["server_15"].format(
+ result["client"]["isp"],
+ result["client"]["country"],
+ result["server"]["name"],
+ result["server"]["country"],
+ result["server"]["cc"],
+ result["server"]["sponsor"],
+ result["server"]["latency"],
+ result["ping"],
+ )
+
+ # Send result as a photo
+ msg = await message.reply_photo(photo=result["share"], caption=output)
+ await m.delete() # Remove the initial message
\ No newline at end of file
diff --git a/SprotifyMusic/plugins/tools/stats.py b/SprotifyMusic/plugins/tools/stats.py
new file mode 100644
index 000000000000..9ece4692c87e
--- /dev/null
+++ b/SprotifyMusic/plugins/tools/stats.py
@@ -0,0 +1,135 @@
+import platform
+from sys import version as pyver
+
+import psutil
+from pyrogram import __version__ as pyrover
+from pyrogram import filters
+from pyrogram.errors import MessageIdInvalid
+from pyrogram.types import InputMediaPhoto, Message
+from pytgcalls.__version__ import __version__ as pytgver
+
+import config
+from SprotifyMusic import app
+from SprotifyMusic.core.userbot import assistants
+from SprotifyMusic.misc import SUDOERS, mongodb
+from SprotifyMusic.plugins import ALL_MODULES
+from SprotifyMusic.utils.database import get_served_chats, get_served_users, get_sudoers
+from SprotifyMusic.utils.decorators.language import language, languageCB
+from SprotifyMusic.utils.inline.stats import back_stats_buttons, stats_buttons
+from config import BANNED_USERS
+
+
+@app.on_message(filters.command(["stats", "gstats"]) & filters.group & ~BANNED_USERS)
+@language
+async def stats_global(client, message: Message, _):
+ upl = stats_buttons(_, True if message.from_user.id in SUDOERS else False)
+ await message.reply_photo(
+ photo=config.STATS_IMG_URL,
+ caption=_["gstats_2"].format(app.mention),
+ reply_markup=upl,
+ )
+
+
+@app.on_callback_query(filters.regex("stats_back") & ~BANNED_USERS)
+@languageCB
+async def home_stats(client, CallbackQuery, _):
+ upl = stats_buttons(_, True if CallbackQuery.from_user.id in SUDOERS else False)
+ await CallbackQuery.edit_message_text(
+ text=_["gstats_2"].format(app.mention),
+ reply_markup=upl,
+ )
+
+
+@app.on_callback_query(filters.regex("TopOverall") & ~BANNED_USERS)
+@languageCB
+async def overall_stats(client, CallbackQuery, _):
+ await CallbackQuery.answer()
+ upl = back_stats_buttons(_)
+ try:
+ await CallbackQuery.answer()
+ except:
+ pass
+ await CallbackQuery.edit_message_text(_["gstats_1"].format(app.mention))
+ served_chats = len(await get_served_chats())
+ served_users = len(await get_served_users())
+ text = _["gstats_3"].format(
+ app.mention,
+ len(assistants),
+ len(BANNED_USERS),
+ served_chats,
+ served_users,
+ len(ALL_MODULES),
+ len(SUDOERS),
+ config.AUTO_LEAVING_ASSISTANT,
+ config.DURATION_LIMIT_MIN,
+ )
+ med = InputMediaPhoto(media=config.STATS_IMG_URL, caption=text)
+ try:
+ await CallbackQuery.edit_message_media(media=med, reply_markup=upl)
+ except MessageIdInvalid:
+ await CallbackQuery.message.reply_photo(
+ photo=config.STATS_IMG_URL, caption=text, reply_markup=upl
+ )
+
+
+@app.on_callback_query(filters.regex("bot_stats_sudo"))
+@languageCB
+async def bot_stats(client, CallbackQuery, _):
+ if CallbackQuery.from_user.id not in SUDOERS:
+ return await CallbackQuery.answer(_["gstats_4"], show_alert=True)
+ upl = back_stats_buttons(_)
+ try:
+ await CallbackQuery.answer()
+ except:
+ pass
+ await CallbackQuery.edit_message_text(_["gstats_1"].format(app.mention))
+ p_core = psutil.cpu_count(logical=False)
+ t_core = psutil.cpu_count(logical=True)
+ ram = str(round(psutil.virtual_memory().total / (1024.0**3))) + " ɢʙ"
+ try:
+ cpu_freq = psutil.cpu_freq().current
+ if cpu_freq >= 1000:
+ cpu_freq = f"{round(cpu_freq / 1000, 2)}ɢʜᴢ"
+ else:
+ cpu_freq = f"{round(cpu_freq, 2)}ᴍʜᴢ"
+ except:
+ cpu_freq = "ғᴀɪʟᴇᴅ ᴛᴏ ғᴇᴛᴄʜ"
+ hdd = psutil.disk_usage("/")
+ total = hdd.total / (1024.0**3)
+ used = hdd.used / (1024.0**3)
+ free = hdd.free / (1024.0**3)
+ call = await mongodb.command("dbstats")
+ datasize = call["dataSize"] / 1024
+ storage = call["storageSize"] / 1024
+ served_chats = len(await get_served_chats())
+ served_users = len(await get_served_users())
+ text = _["gstats_5"].format(
+ app.mention,
+ len(ALL_MODULES),
+ platform.system(),
+ ram,
+ p_core,
+ t_core,
+ cpu_freq,
+ pyver.split()[0],
+ pyrover,
+ pytgver,
+ str(total)[:4],
+ str(used)[:4],
+ str(free)[:4],
+ served_chats,
+ served_users,
+ len(BANNED_USERS),
+ len(await get_sudoers()),
+ str(datasize)[:6],
+ storage,
+ call["collections"],
+ call["objects"],
+ )
+ med = InputMediaPhoto(media=config.STATS_IMG_URL, caption=text)
+ try:
+ await CallbackQuery.edit_message_media(media=med, reply_markup=upl)
+ except MessageIdInvalid:
+ await CallbackQuery.message.reply_photo(
+ photo=config.STATS_IMG_URL, caption=text, reply_markup=upl
+ )
diff --git a/SprotifyMusic/plugins/tools/stickers.py b/SprotifyMusic/plugins/tools/stickers.py
new file mode 100644
index 000000000000..fdaf4eb6a49c
--- /dev/null
+++ b/SprotifyMusic/plugins/tools/stickers.py
@@ -0,0 +1,90 @@
+import base64
+import httpx
+import os
+from pyrogram import filters
+from config import BOT_USERNAME
+from SprotifyMusic import app
+from pyrogram import filters
+import pyrogram
+from uuid import uuid4
+from pyrogram.types import InlineKeyboardButton,InlineKeyboardMarkup
+
+
+EVAA = [
+ [
+ InlineKeyboardButton(text="ᴀᴅᴅ ᴍᴇ ʙᴀʙʏ", url=f"https://t.me/TanuMusicxBot?startgroup=true"),
+ ],
+]
+
+######### sticker id
+
+@app.on_message(filters.command("kang"))
+async def _packkang(app :app,message):
+ txt = await message.reply_text("✦ ᴘʀᴏᴄᴇssɪɴɢ.... ")
+ if not message.reply_to_message:
+ await txt.edit('ʀᴇᴘʟʏ ᴛᴏ ᴍᴇssᴀɢᴇ')
+ return
+ if not message.reply_to_message.sticker:
+ await txt.edit('ʀᴇᴘʟʏ ᴛᴏ sᴛɪᴄᴋᴇʀ')
+ return
+ if message.reply_to_message.sticker.is_animated or message.reply_to_message.sticker.is_video:
+ return await txt.edit("ʀᴇᴘʟʏ ᴛᴏ ᴀ ɴᴏɴ-ᴀɴɪᴍᴀᴛᴇᴅ sᴛɪᴄᴋᴇʀ")
+ if len(message.command) < 2:
+ pack_name = f'{message.from_user.first_name}_sticker_pack_by_@TanuMusicxBot'
+ else :
+ pack_name = message.text.split(maxsplit=1)[1]
+ short_name = message.reply_to_message.sticker.set_name
+ stickers = await app.invoke(
+ pyrogram.raw.functions.messages.GetStickerSet(
+ stickerset=pyrogram.raw.types.InputStickerSetShortName(
+ short_name=short_name),
+ hash=0))
+ shits = stickers.documents
+ sticks = []
+
+ for i in shits:
+ sex = pyrogram.raw.types.InputDocument(
+ id=i.id,
+ access_hash=i.access_hash,
+ file_reference=i.thumbs[0].bytes
+ )
+
+ sticks.append(
+ pyrogram.raw.types.InputStickerSetItem(
+ document=sex,
+ emoji=i.attributes[1].alt
+ )
+ )
+
+ try:
+ short_name = f'stikcer_pack_{str(uuid4()).replace("-","")}_by_{app.me.username}'
+ user_id = await app.resolve_peer(message.from_user.id)
+ await app.invoke(
+ pyrogram.raw.functions.stickers.CreateStickerSet(
+ user_id=user_id,
+ title=pack_name,
+ short_name=short_name,
+ stickers=sticks,
+ )
+ )
+ await txt.edit(f"❖ ʜᴇʀᴇ ɪs ʏᴏᴜʀ ᴋᴀɴɢᴇᴅ ʟɪɴᴋ !\n● ᴛᴏᴛᴀʟ sᴛɪᴄᴋᴇʀ ➥ {len(sticks)}",reply_markup=InlineKeyboardMarkup([[InlineKeyboardButton("ᴘᴀᴄᴋ ʟɪɴᴋ",url=f"http://t.me/addstickers/{short_name}")]]))
+ except Exception as e:
+ await message.reply(str(e))
+
+
+###### sticker id =
+@app.on_message(filters.command(["stickerid","stid"]))
+async def sticker_id(app: app, msg):
+ if not msg.reply_to_message:
+ await msg.reply_text("✦ Reply to a sticker")
+ elif not msg.reply_to_message.sticker:
+ await msg.reply_text("✦ Reply to a sticker")
+ st_in = msg.reply_to_message.sticker
+ await msg.reply_text(f"""
+❖ sᴛɪᴄᴋᴇʀ ɪɴғᴏ ❖
+
+● sᴛɪᴄᴋᴇʀ ɪᴅ ➥ {st_in.file_id}\n
+● sᴛɪᴄᴋᴇʀ ᴜɴɪǫᴜᴇ ɪᴅ ➥ {st_in.file_unique_id}""", reply_markup=InlineKeyboardMarkup(EVAA),)
+
+
+#####
diff --git a/SprotifyMusic/plugins/tools/translate.py b/SprotifyMusic/plugins/tools/translate.py
new file mode 100644
index 000000000000..bf09dbf85afb
--- /dev/null
+++ b/SprotifyMusic/plugins/tools/translate.py
@@ -0,0 +1,21 @@
+from SprotifyMusic import app
+from pyrogram import filters
+from deep_translator import GoogleTranslator
+
+@app.on_message(filters.command("tr"))
+async def translate(client, message):
+ if message.reply_to_message:
+ text_to_translate = message.reply_to_message.text
+ target_language = message.text.split(None, 1)[1] if len(message.text.split()) > 1 else 'en'
+ else:
+ if len(message.text.split()) < 3:
+ await message.reply_text("You can use this command by replying to a message.")
+ return
+ target_language = message.text.split(None, 2)[1]
+ text_to_translate = message.text.split(None, 2)[2]
+
+ try:
+ translated = GoogleTranslator(source='auto', target=target_language).translate(text_to_translate)
+ await message.reply_text(f"ᴛʀᴀɴsʟᴀᴛᴇᴅ ғʀᴏᴍ {target_language}\n\n {translated}")
+ except Exception as e:
+ await message.reply_text(f"An error occurred during translation: {str(e)}")
\ No newline at end of file
diff --git a/SprotifyMusic/utils/###thumbnails### b/SprotifyMusic/utils/###thumbnails###
new file mode 100644
index 000000000000..0a44c616e0bb
--- /dev/null
+++ b/SprotifyMusic/utils/###thumbnails###
@@ -0,0 +1,168 @@
+import os
+import re
+import aiofiles
+import aiohttp
+from PIL import Image, ImageDraw, ImageEnhance, ImageFilter, ImageFont
+from youtubesearchpython.__future__ import VideosSearch
+
+# Ukuran gambar
+width, height = 1080, 720
+
+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
+
+
+def truncate(text):
+ words = text.split(" ")
+ text1 = ""
+ text2 = ""
+ for word in words:
+ if len(text1) + len(word) < 30:
+ text1 += " " + word
+ elif len(text2) + len(word) < 30:
+ text2 += " " + word
+
+ text1 = text1.strip()
+ text2 = text2.strip()
+ return [text1, text2]
+
+
+def crop_center_rounded_rectangle(img, output_size, border, corner_radius, crop_scale=1.5):
+ # Menentukan pusat gambar
+ half_width = img.size[0] / 2
+ half_height = img.size[1] / 2
+ larger_size = int(output_size * crop_scale)
+
+ # Memotong gambar untuk menyesuaikan dengan ukuran rounded rectangle
+ img = img.crop(
+ (
+ half_width - larger_size / 2,
+ half_height - larger_size / 2,
+ half_width + larger_size / 2,
+ half_height + larger_size / 2,
+ )
+ )
+
+ # Mengubah ukuran gambar setelah dipotong
+ img = img.resize((output_size - 2 * border, output_size - 2 * border))
+
+ # Membuat gambar baru untuk menampung rounded rectangle
+ final_img = Image.new("RGBA", (output_size, output_size), "pink")
+
+ # Membuat mask untuk bentuk rounded rectangle
+ mask_main = Image.new("L", (output_size - 2 * border, output_size - 2 * border), 0)
+ draw_main = ImageDraw.Draw(mask_main)
+ draw_main.rounded_rectangle(
+ (0, 0, output_size - 2 * border, output_size - 2 * border), radius=corner_radius, fill=255
+ )
+
+ # Menempelkan gambar yang telah dipotong ke dalam rounded rectangle
+ final_img.paste(img, (border, border), mask_main)
+
+ # Membuat border untuk mask
+ mask_border = Image.new("L", (output_size, output_size), 0)
+ draw_border = ImageDraw.Draw(mask_border)
+ draw_border.rounded_rectangle((0, 0, output_size, output_size), radius=corner_radius, fill=255)
+
+ # Membuat gambar akhir dengan border dan mask
+ result = Image.composite(final_img, Image.new("RGBA", final_img.size, (0, 0, 0, 0)), mask_border)
+
+ return result
+
+
+
+async def get_thumb(videoid):
+ if os.path.isfile(f"cache/{videoid}_v4.png"):
+ return f"cache/{videoid}_v4.png"
+
+ url = f"https://www.youtube.com/watch?v={videoid}"
+ results = VideosSearch(url, limit=1)
+
+ try:
+ for result in (await results.next())["result"]:
+ try:
+ title = result.get("title", "Unsupported Title")
+ title = re.sub("\W+", " ", title)
+ title = title.title()
+ except:
+ title = "Unsupported Title"
+
+ duration = result.get("duration", "Unknown Mins")
+ views = result.get("viewCount", {}).get("short", "Unknown Views")
+ channel = result.get("channel", {}).get("name", "Unknown Channel")
+
+ if "thumbnails" in result and len(result["thumbnails"]) > 0:
+ thumbnail = result["thumbnails"][0]["url"].split("?")[0]
+ else:
+ raise ValueError("Thumbnail not found in search results")
+
+ async with aiohttp.ClientSession() as session:
+ async with session.get(thumbnail) as resp:
+ if resp.status == 200:
+ async with aiofiles.open(f"cache/thumb{videoid}.png", mode="wb") as f:
+ await f.write(await resp.read())
+
+ youtube = Image.open(f"cache/thumb{videoid}.png")
+ image1 = changeImageSize(1280, 720, youtube)
+ image2 = image1.convert("L")
+ background = image2.filter(filter=ImageFilter.BoxBlur(20))
+ enhancer = ImageEnhance.Brightness(background)
+ background = enhancer.enhance(0.6)
+ draw = ImageDraw.Draw(background)
+ arial = ImageFont.truetype("SprotifyMusic/assets/font2.ttf", 30)
+ font = ImageFont.truetype("SprotifyMusic/assets/font.ttf", 30)
+ title_font = ImageFont.truetype("SprotifyMusic/assets/font3.ttf", 45)
+
+ # Menggunakan crop_center_rounded_rectangle
+ corner_radius = 50 # Radius sudut rounded rectangle
+ rectangle_thumbnail = crop_center_rounded_rectangle(youtube, 400, 20, corner_radius)
+ rectangle_thumbnail = rectangle_thumbnail.resize((400, 400))
+ rectangle_position = (120, 160) # Menempatkan di sebelah kiri
+ background.paste(rectangle_thumbnail, rectangle_position, rectangle_thumbnail)
+
+ text_x_position = 565
+
+ title1 = truncate(title)
+ draw.text((text_x_position, 180), title1[0], fill=(255, 255, 255), font=title_font)
+ draw.text((text_x_position, 230), title1[1], fill=(255, 255, 255), font=title_font)
+ draw.text((text_x_position, 320), f"{channel} | {views[:23]}", (255, 255, 255), font=arial)
+ draw.text((10, 10), f"Sprotify Music", fill="white", font=font)
+
+ line_length = 580
+ red_length = int(line_length * 0.6)
+ white_length = line_length - red_length
+
+ start_point_red = (text_x_position, 380)
+ end_point_red = (text_x_position + red_length, 380)
+ draw.line([start_point_red, end_point_red], fill="red", width=9)
+
+ start_point_white = (text_x_position + red_length, 380)
+ end_point_white = (text_x_position + line_length, 380)
+ draw.line([start_point_white, end_point_white], fill="white", width=8)
+
+ circle_radius = 10
+ circle_position = (end_point_red[0], end_point_red[1])
+ draw.ellipse([circle_position[0] - circle_radius, circle_position[1] - circle_radius,
+ circle_position[0] + circle_radius, circle_position[1] + circle_radius], fill="red")
+ draw.text((text_x_position, 400), "00:00", (255, 255, 255), font=arial)
+ draw.text((1080, 400), duration, (255, 255, 255), font=arial)
+
+ play_icons = Image.open("SprotifyMusic/assets/play_icons.png")
+ play_icons = play_icons.resize((580, 62))
+ background.paste(play_icons, (text_x_position, 450), play_icons)
+
+ try:
+ os.remove(f"cache/thumb{videoid}.png")
+ except:
+ pass
+ background.save(f"cache/{videoid}_v4.png")
+ return f"cache/{videoid}_v4.png"
+
+ except Exception as e:
+ print(f"Error in get_thumb: {e}")
+ raise
diff --git a/SprotifyMusic/utils/__init__.py b/SprotifyMusic/utils/__init__.py
new file mode 100644
index 000000000000..6208a5acfba7
--- /dev/null
+++ b/SprotifyMusic/utils/__init__.py
@@ -0,0 +1,8 @@
+from .channelplay import *
+from .database import *
+from .decorators import *
+from .extraction import *
+from .formatters import *
+from .inline import *
+from .pastebin import *
+from .sys import *
diff --git a/SprotifyMusic/utils/channelplay.py b/SprotifyMusic/utils/channelplay.py
new file mode 100644
index 000000000000..9c2916ebbcca
--- /dev/null
+++ b/SprotifyMusic/utils/channelplay.py
@@ -0,0 +1,23 @@
+from SprotifyMusic import app
+from SprotifyMusic.utils.database import get_cmode
+
+
+async def get_channeplayCB(_, command, CallbackQuery):
+ if command == "c":
+ chat_id = await get_cmode(CallbackQuery.message.chat.id)
+ if chat_id is None:
+ try:
+ return await CallbackQuery.answer(_["setting_7"], show_alert=True)
+ except:
+ return
+ try:
+ channel = (await app.get_chat(chat_id)).title
+ except:
+ try:
+ return await CallbackQuery.answer(_["cplay_4"], show_alert=True)
+ except:
+ return
+ else:
+ chat_id = CallbackQuery.message.chat.id
+ channel = None
+ return chat_id, channel
diff --git a/SprotifyMusic/utils/cleanmode.py b/SprotifyMusic/utils/cleanmode.py
new file mode 100644
index 000000000000..3f4f40afd0c2
--- /dev/null
+++ b/SprotifyMusic/utils/cleanmode.py
@@ -0,0 +1,30 @@
+from config import LOGGER_ID
+from SprotifyMusic import app
+
+protected_messages = {}
+
+
+async def protect_message(chat_id, message_id):
+ if chat_id not in protected_messages:
+ protected_messages[chat_id] = []
+ protected_messages[chat_id].append(message_id)
+
+
+async def send_message(chat_id, text, reply=None):
+ if reply:
+ try:
+ message = await app.send_message(
+ chat_id, text, reply_to_message_id=reply, disable_web_page_preview=True
+ )
+ await protect_message(chat_id, message.id)
+ except Exception as e:
+ return await app.send_message(LOGGER_ID, e)
+ else:
+ try:
+ message = await app.send_message(
+ chat_id, text, disable_web_page_preview=True
+ )
+ await protect_message(chat_id, message.id)
+ except Exception as e:
+ return await app.send_message(LOGGER_ID, e)
+
diff --git a/SprotifyMusic/utils/database.py b/SprotifyMusic/utils/database.py
new file mode 100644
index 000000000000..f85969995681
--- /dev/null
+++ b/SprotifyMusic/utils/database.py
@@ -0,0 +1,739 @@
+import random
+from typing import Dict, List, Union
+
+from SprotifyMusic import userbot
+from SprotifyMusic.core.mongo import mongodb
+from SprotifyMusic.utils.mongo import db
+
+afkdb = db.afk
+authdb = mongodb.adminauth
+authuserdb = mongodb.authuser
+autoenddb = mongodb.autoend
+assdb = mongodb.assistants
+blacklist_chatdb = mongodb.blacklistChat
+blockeddb = mongodb.blockedusers
+chatsdb = mongodb.chats
+channeldb = mongodb.cplaymode
+countdb = mongodb.upcount
+gbansdb = mongodb.gban
+langdb = mongodb.language
+onoffdb = mongodb.onoffper
+playmodedb = mongodb.playmode
+playtypedb = mongodb.playtypedb
+playlistdb = mongodb.playlist
+skipdb = mongodb.skipmode
+sudoersdb = mongodb.sudoers
+usersdb = mongodb.tgusersdb
+
+# Shifting to memory [mongo sucks often]
+active = []
+activevideo = []
+assistantdict = {}
+autoend = {}
+count = {}
+channelconnect = {}
+langm = {}
+loop = {}
+maintenance = []
+nonadmin = {}
+pause = {}
+playmode = {}
+playtype = {}
+skipmode = {}
+
+
+# Playlist
+
+
+async def _get_playlists(chat_id: int) -> Dict[str, int]:
+ _notes = await playlistdb.find_one({"chat_id": chat_id})
+ if not _notes:
+ return {}
+ return _notes["notes"]
+
+
+async def get_playlist_names(chat_id: int) -> List[str]:
+ _notes = []
+ for note in await _get_playlists(chat_id):
+ _notes.append(note)
+ return _notes
+
+
+async def get_playlist(chat_id: int, name: str) -> Union[bool, dict]:
+ name = name
+ _notes = await _get_playlists(chat_id)
+ if name in _notes:
+ return _notes[name]
+ else:
+ return False
+
+
+async def save_playlist(chat_id: int, name: str, note: dict):
+ name = name
+ _notes = await _get_playlists(chat_id)
+ _notes[name] = note
+ await playlistdb.update_one(
+ {"chat_id": chat_id}, {"$set": {"notes": _notes}}, upsert=True
+ )
+
+
+async def delete_playlist(chat_id: int, name: str) -> bool:
+ notesd = await _get_playlists(chat_id)
+ name = name
+ if name in notesd:
+ del notesd[name]
+ await playlistdb.update_one(
+ {"chat_id": chat_id},
+ {"$set": {"notes": notesd}},
+ upsert=True,
+ )
+ return True
+ return False
+
+
+async def get_active_video_chats() -> list:
+ return activevideo
+
+async def get_active_chats() -> list:
+ return active
+
+async def remove_active_chat(chat_id: int):
+ if chat_id in active:
+ active.remove(chat_id)
+
+async def remove_active_video_chat(chat_id: int):
+ if chat_id in activevideo:
+ activevideo.remove(chat_id)
+
+async def get_assistant_number(chat_id: int) -> str:
+ assistant = assistantdict.get(chat_id)
+ return assistant
+
+async def get_client(assistant: int):
+ if int(assistant) == 1:
+ return userbot.one
+ elif int(assistant) == 2:
+ return userbot.two
+ elif int(assistant) == 3:
+ return userbot.three
+ elif int(assistant) == 4:
+ return userbot.four
+ elif int(assistant) == 5:
+ return userbot.five
+
+
+async def set_assistant_new(chat_id, number):
+ number = int(number)
+ await assdb.update_one(
+ {"chat_id": chat_id},
+ {"$set": {"assistant": number}},
+ upsert=True,
+ )
+
+
+async def set_assistant(chat_id):
+ from SprotifyMusic.core.userbot import assistants
+
+ ran_assistant = random.choice(assistants)
+ assistantdict[chat_id] = ran_assistant
+ await assdb.update_one(
+ {"chat_id": chat_id},
+ {"$set": {"assistant": ran_assistant}},
+ upsert=True,
+ )
+ userbot = await get_client(ran_assistant)
+ return userbot
+
+
+async def get_assistant(chat_id: int) -> str:
+ from SprotifyMusic.core.userbot import assistants
+
+ assistant = assistantdict.get(chat_id)
+ if not assistant:
+ dbassistant = await assdb.find_one({"chat_id": chat_id})
+ if not dbassistant:
+ userbot = await set_assistant(chat_id)
+ return userbot
+ else:
+ got_assis = dbassistant["assistant"]
+ if got_assis in assistants:
+ assistantdict[chat_id] = got_assis
+ userbot = await get_client(got_assis)
+ return userbot
+ else:
+ userbot = await set_assistant(chat_id)
+ return userbot
+ else:
+ if assistant in assistants:
+ userbot = await get_client(assistant)
+ return userbot
+ else:
+ userbot = await set_assistant(chat_id)
+ return userbot
+
+async def is_afk(user_id: int) -> bool:
+ user = await afkdb.find_one({"user_id": user_id})
+ if not user:
+ return False, {}
+ return True, user["reason"]
+
+
+async def add_afk(user_id: int, mode):
+ await afkdb.update_one(
+ {"user_id": user_id}, {"$set": {"reason": mode}}, upsert=True
+ )
+
+
+async def remove_afk(user_id: int):
+ user = await afkdb.find_one({"user_id": user_id})
+ if user:
+ return await afkdb.delete_one({"user_id": user_id})
+
+
+async def get_afk_users() -> list:
+ users = afkdb.find({"user_id": {"$gt": 0}})
+ if not users:
+ return []
+ users_list = []
+ for user in await users.to_list(length=1000000000):
+ users_list.append(user)
+ return users_list
+
+
+async def set_calls_assistant(chat_id):
+ from SprotifyMusic.core.userbot import assistants
+
+ ran_assistant = random.choice(assistants)
+ assistantdict[chat_id] = ran_assistant
+ await assdb.update_one(
+ {"chat_id": chat_id},
+ {"$set": {"assistant": ran_assistant}},
+ upsert=True,
+ )
+ return ran_assistant
+
+
+async def group_assistant(self, chat_id: int) -> int:
+ from SprotifyMusic.core.userbot import assistants
+
+ assistant = assistantdict.get(chat_id)
+ if not assistant:
+ dbassistant = await assdb.find_one({"chat_id": chat_id})
+ if not dbassistant:
+ assis = await set_calls_assistant(chat_id)
+ else:
+ assis = dbassistant["assistant"]
+ if assis in assistants:
+ assistantdict[chat_id] = assis
+ assis = assis
+ else:
+ assis = await set_calls_assistant(chat_id)
+ else:
+ if assistant in assistants:
+ assis = assistant
+ else:
+ assis = await set_calls_assistant(chat_id)
+ if int(assis) == 1:
+ return self.one
+ elif int(assis) == 2:
+ return self.two
+ elif int(assis) == 3:
+ return self.three
+ elif int(assis) == 4:
+ return self.four
+ elif int(assis) == 5:
+ return self.five
+
+
+async def is_skipmode(chat_id: int) -> bool:
+ mode = skipmode.get(chat_id)
+ if not mode:
+ user = await skipdb.find_one({"chat_id": chat_id})
+ if not user:
+ skipmode[chat_id] = True
+ return True
+ skipmode[chat_id] = False
+ return False
+ return mode
+
+
+async def skip_on(chat_id: int):
+ skipmode[chat_id] = True
+ user = await skipdb.find_one({"chat_id": chat_id})
+ if user:
+ return await skipdb.delete_one({"chat_id": chat_id})
+
+
+async def skip_off(chat_id: int):
+ skipmode[chat_id] = False
+ user = await skipdb.find_one({"chat_id": chat_id})
+ if not user:
+ return await skipdb.insert_one({"chat_id": chat_id})
+
+
+async def get_upvote_count(chat_id: int) -> int:
+ mode = count.get(chat_id)
+ if not mode:
+ mode = await countdb.find_one({"chat_id": chat_id})
+ if not mode:
+ return 5
+ count[chat_id] = mode["mode"]
+ return mode["mode"]
+ return mode
+
+
+async def set_upvotes(chat_id: int, mode: int):
+ count[chat_id] = mode
+ await countdb.update_one(
+ {"chat_id": chat_id}, {"$set": {"mode": mode}}, upsert=True
+ )
+
+
+async def is_autoend() -> bool:
+ chat_id = 1234
+ user = await autoenddb.find_one({"chat_id": chat_id})
+ if not user:
+ return False
+ return True
+
+
+async def autoend_on():
+ chat_id = 1234
+ await autoenddb.insert_one({"chat_id": chat_id})
+
+
+async def autoend_off():
+ chat_id = 1234
+ await autoenddb.delete_one({"chat_id": chat_id})
+
+
+async def get_loop(chat_id: int) -> int:
+ lop = loop.get(chat_id)
+ if not lop:
+ return 0
+ return lop
+
+
+async def set_loop(chat_id: int, mode: int):
+ loop[chat_id] = mode
+
+
+async def get_cmode(chat_id: int) -> int:
+ mode = channelconnect.get(chat_id)
+ if not mode:
+ mode = await channeldb.find_one({"chat_id": chat_id})
+ if not mode:
+ return None
+ channelconnect[chat_id] = mode["mode"]
+ return mode["mode"]
+ return mode
+
+
+async def set_cmode(chat_id: int, mode: int):
+ channelconnect[chat_id] = mode
+ await channeldb.update_one(
+ {"chat_id": chat_id}, {"$set": {"mode": mode}}, upsert=True
+ )
+
+
+async def get_playtype(chat_id: int) -> str:
+ mode = playtype.get(chat_id)
+ if not mode:
+ mode = await playtypedb.find_one({"chat_id": chat_id})
+ if not mode:
+ playtype[chat_id] = "Everyone"
+ return "Everyone"
+ playtype[chat_id] = mode["mode"]
+ return mode["mode"]
+ return mode
+
+
+async def set_playtype(chat_id: int, mode: str):
+ playtype[chat_id] = mode
+ await playtypedb.update_one(
+ {"chat_id": chat_id}, {"$set": {"mode": mode}}, upsert=True
+ )
+
+
+async def get_playmode(chat_id: int) -> str:
+ mode = playmode.get(chat_id)
+ if not mode:
+ mode = await playmodedb.find_one({"chat_id": chat_id})
+ if not mode:
+ playmode[chat_id] = "Direct"
+ return "Direct"
+ playmode[chat_id] = mode["mode"]
+ return mode["mode"]
+ return mode
+
+
+async def set_playmode(chat_id: int, mode: str):
+ playmode[chat_id] = mode
+ await playmodedb.update_one(
+ {"chat_id": chat_id}, {"$set": {"mode": mode}}, upsert=True
+ )
+
+
+async def get_lang(chat_id: int) -> str:
+ mode = langm.get(chat_id)
+ if not mode:
+ lang = await langdb.find_one({"chat_id": chat_id})
+ if not lang:
+ langm[chat_id] = "en"
+ return "en"
+ langm[chat_id] = lang["lang"]
+ return lang["lang"]
+ return mode
+
+
+async def set_lang(chat_id: int, lang: str):
+ langm[chat_id] = lang
+ await langdb.update_one({"chat_id": chat_id}, {"$set": {"lang": lang}}, upsert=True)
+
+
+async def is_music_playing(chat_id: int) -> bool:
+ mode = pause.get(chat_id)
+ if not mode:
+ return False
+ return mode
+
+
+async def music_on(chat_id: int):
+ pause[chat_id] = True
+
+
+async def music_off(chat_id: int):
+ pause[chat_id] = False
+
+
+async def get_active_chats() -> list:
+ return active
+
+
+async def is_active_chat(chat_id: int) -> bool:
+ if chat_id not in active:
+ return False
+ else:
+ return True
+
+
+async def add_active_chat(chat_id: int):
+ if chat_id not in active:
+ active.append(chat_id)
+
+
+async def remove_active_chat(chat_id: int):
+ if chat_id in active:
+ active.remove(chat_id)
+
+
+async def get_active_video_chats() -> list:
+ return activevideo
+
+
+async def is_active_video_chat(chat_id: int) -> bool:
+ if chat_id not in activevideo:
+ return False
+ else:
+ return True
+
+
+async def add_active_video_chat(chat_id: int):
+ if chat_id not in activevideo:
+ activevideo.append(chat_id)
+
+
+async def remove_active_video_chat(chat_id: int):
+ if chat_id in activevideo:
+ activevideo.remove(chat_id)
+
+
+async def check_nonadmin_chat(chat_id: int) -> bool:
+ user = await authdb.find_one({"chat_id": chat_id})
+ if not user:
+ return False
+ return True
+
+
+async def is_nonadmin_chat(chat_id: int) -> bool:
+ mode = nonadmin.get(chat_id)
+ if not mode:
+ user = await authdb.find_one({"chat_id": chat_id})
+ if not user:
+ nonadmin[chat_id] = False
+ return False
+ nonadmin[chat_id] = True
+ return True
+ return mode
+
+
+async def add_nonadmin_chat(chat_id: int):
+ nonadmin[chat_id] = True
+ is_admin = await check_nonadmin_chat(chat_id)
+ if is_admin:
+ return
+ return await authdb.insert_one({"chat_id": chat_id})
+
+
+async def remove_nonadmin_chat(chat_id: int):
+ nonadmin[chat_id] = False
+ is_admin = await check_nonadmin_chat(chat_id)
+ if not is_admin:
+ return
+ return await authdb.delete_one({"chat_id": chat_id})
+
+
+async def is_on_off(on_off: int) -> bool:
+ onoff = await onoffdb.find_one({"on_off": on_off})
+ if not onoff:
+ return False
+ return True
+
+
+async def add_on(on_off: int):
+ is_on = await is_on_off(on_off)
+ if is_on:
+ return
+ return await onoffdb.insert_one({"on_off": on_off})
+
+
+async def add_off(on_off: int):
+ is_off = await is_on_off(on_off)
+ if not is_off:
+ return
+ return await onoffdb.delete_one({"on_off": on_off})
+
+
+async def is_maintenance():
+ if not maintenance:
+ get = await onoffdb.find_one({"on_off": 1})
+ if not get:
+ maintenance.clear()
+ maintenance.append(2)
+ return True
+ else:
+ maintenance.clear()
+ maintenance.append(1)
+ return False
+ else:
+ if 1 in maintenance:
+ return False
+ else:
+ return True
+
+
+async def maintenance_off():
+ maintenance.clear()
+ maintenance.append(2)
+ is_off = await is_on_off(1)
+ if not is_off:
+ return
+ return await onoffdb.delete_one({"on_off": 1})
+
+
+async def maintenance_on():
+ maintenance.clear()
+ maintenance.append(1)
+ is_on = await is_on_off(1)
+ if is_on:
+ return
+ return await onoffdb.insert_one({"on_off": 1})
+
+
+async def is_served_user(user_id: int) -> bool:
+ user = await usersdb.find_one({"user_id": user_id})
+ if not user:
+ return False
+ return True
+
+
+async def get_served_users() -> list:
+ users_list = []
+ async for user in usersdb.find({"user_id": {"$gt": 0}}):
+ users_list.append(user)
+ return users_list
+
+
+async def add_served_user(user_id: int):
+ is_served = await is_served_user(user_id)
+ if is_served:
+ return
+ return await usersdb.insert_one({"user_id": user_id})
+
+
+async def get_served_chats() -> list:
+ chats_list = []
+ async for chat in chatsdb.find({"chat_id": {"$lt": 0}}):
+ chats_list.append(chat)
+ return chats_list
+
+
+async def is_served_chat(chat_id: int) -> bool:
+ chat = await chatsdb.find_one({"chat_id": chat_id})
+ if not chat:
+ return False
+ return True
+
+
+async def add_served_chat(chat_id: int):
+ is_served = await is_served_chat(chat_id)
+ if is_served:
+ return
+ return await chatsdb.insert_one({"chat_id": chat_id})
+
+
+async def blacklisted_chats() -> list:
+ chats_list = []
+ async for chat in blacklist_chatdb.find({"chat_id": {"$lt": 0}}):
+ chats_list.append(chat["chat_id"])
+ return chats_list
+
+
+async def blacklist_chat(chat_id: int) -> bool:
+ if not await blacklist_chatdb.find_one({"chat_id": chat_id}):
+ await blacklist_chatdb.insert_one({"chat_id": chat_id})
+ return True
+ return False
+
+
+async def whitelist_chat(chat_id: int) -> bool:
+ if await blacklist_chatdb.find_one({"chat_id": chat_id}):
+ await blacklist_chatdb.delete_one({"chat_id": chat_id})
+ return True
+ return False
+
+
+async def _get_authusers(chat_id: int) -> Dict[str, int]:
+ _notes = await authuserdb.find_one({"chat_id": chat_id})
+ if not _notes:
+ return {}
+ return _notes["notes"]
+
+
+async def get_authuser_names(chat_id: int) -> List[str]:
+ _notes = []
+ for note in await _get_authusers(chat_id):
+ _notes.append(note)
+ return _notes
+
+
+async def get_authuser(chat_id: int, name: str) -> Union[bool, dict]:
+ name = name
+ _notes = await _get_authusers(chat_id)
+ if name in _notes:
+ return _notes[name]
+ else:
+ return False
+
+
+async def save_authuser(chat_id: int, name: str, note: dict):
+ name = name
+ _notes = await _get_authusers(chat_id)
+ _notes[name] = note
+
+ await authuserdb.update_one(
+ {"chat_id": chat_id}, {"$set": {"notes": _notes}}, upsert=True
+ )
+
+
+async def delete_authuser(chat_id: int, name: str) -> bool:
+ notesd = await _get_authusers(chat_id)
+ name = name
+ if name in notesd:
+ del notesd[name]
+ await authuserdb.update_one(
+ {"chat_id": chat_id},
+ {"$set": {"notes": notesd}},
+ upsert=True,
+ )
+ return True
+ return False
+
+
+async def get_gbanned() -> list:
+ results = []
+ async for user in gbansdb.find({"user_id": {"$gt": 0}}):
+ user_id = user["user_id"]
+ results.append(user_id)
+ return results
+
+
+async def is_gbanned_user(user_id: int) -> bool:
+ user = await gbansdb.find_one({"user_id": user_id})
+ if not user:
+ return False
+ return True
+
+
+async def add_gban_user(user_id: int):
+ is_gbanned = await is_gbanned_user(user_id)
+ if is_gbanned:
+ return
+ return await gbansdb.insert_one({"user_id": user_id})
+
+
+async def remove_gban_user(user_id: int):
+ is_gbanned = await is_gbanned_user(user_id)
+ if not is_gbanned:
+ return
+ return await gbansdb.delete_one({"user_id": user_id})
+
+
+async def get_sudoers() -> list:
+ sudoers = await sudoersdb.find_one({"sudo": "sudo"})
+ if not sudoers:
+ return []
+ return sudoers["sudoers"]
+
+
+async def add_sudo(user_id: int) -> bool:
+ sudoers = await get_sudoers()
+ sudoers.append(user_id)
+ await sudoersdb.update_one(
+ {"sudo": "sudo"}, {"$set": {"sudoers": sudoers}}, upsert=True
+ )
+ return True
+
+
+async def remove_sudo(user_id: int) -> bool:
+ sudoers = await get_sudoers()
+ sudoers.remove(user_id)
+ await sudoersdb.update_one(
+ {"sudo": "sudo"}, {"$set": {"sudoers": sudoers}}, upsert=True
+ )
+ return True
+
+
+async def get_banned_users() -> list:
+ results = []
+ async for user in blockeddb.find({"user_id": {"$gt": 0}}):
+ user_id = user["user_id"]
+ results.append(user_id)
+ return results
+
+
+async def get_banned_count() -> int:
+ users = blockeddb.find({"user_id": {"$gt": 0}})
+ users = await users.to_list(length=100000)
+ return len(users)
+
+
+async def is_banned_user(user_id: int) -> bool:
+ user = await blockeddb.find_one({"user_id": user_id})
+ if not user:
+ return False
+ return True
+
+
+async def add_banned_user(user_id: int):
+ is_gbanned = await is_banned_user(user_id)
+ if is_gbanned:
+ return
+ return await blockeddb.insert_one({"user_id": user_id})
+
+
+async def remove_banned_user(user_id: int):
+ is_gbanned = await is_banned_user(user_id)
+ if not is_gbanned:
+ return
+ return await blockeddb.delete_one({"user_id": user_id})
diff --git a/SprotifyMusic/utils/decorators/__init__.py b/SprotifyMusic/utils/decorators/__init__.py
new file mode 100644
index 000000000000..c1e9ec3f1b6e
--- /dev/null
+++ b/SprotifyMusic/utils/decorators/__init__.py
@@ -0,0 +1,2 @@
+from .admins import *
+from .language import *
diff --git a/SprotifyMusic/utils/decorators/admins.py b/SprotifyMusic/utils/decorators/admins.py
new file mode 100644
index 000000000000..1c300f5ab5ac
--- /dev/null
+++ b/SprotifyMusic/utils/decorators/admins.py
@@ -0,0 +1,203 @@
+from pyrogram.enums import ChatType
+from pyrogram.types import InlineKeyboardButton, InlineKeyboardMarkup
+
+from SprotifyMusic import app
+from SprotifyMusic.misc import SUDOERS, db
+from SprotifyMusic.utils.database import (
+ get_authuser_names,
+ get_cmode,
+ get_lang,
+ get_upvote_count,
+ is_active_chat,
+ is_maintenance,
+ is_nonadmin_chat,
+ is_skipmode,
+)
+from config import SUPPORT_CHAT, adminlist, confirmer
+from strings import get_string
+
+from ..formatters import int_to_alpha
+
+
+def AdminRightsCheck(mystic):
+ async def wrapper(client, message):
+ if await is_maintenance() is False:
+ if message.from_user.id not in SUDOERS:
+ return await message.reply_text(
+ text=f"{app.mention} ɪs ᴜɴᴅᴇʀ ᴍᴀɪɴᴛᴇɴᴀɴᴄᴇ, ᴠɪsɪᴛ sᴜᴘᴘᴏʀᴛ ᴄʜᴀᴛ ғᴏʀ ᴋɴᴏᴡɪɴɢ ᴛʜᴇ ʀᴇᴀsᴏɴ.",
+ disable_web_page_preview=True,
+ )
+
+ try:
+ await message.delete()
+ except:
+ pass
+
+ try:
+ language = await get_lang(message.chat.id)
+ _ = get_string(language)
+ except:
+ _ = get_string("en")
+ if message.sender_chat:
+ upl = InlineKeyboardMarkup(
+ [
+ [
+ InlineKeyboardButton(
+ text="ʜᴏᴡ ᴛᴏ ғɪx ?",
+ callback_data="AnonymousAdmin",
+ ),
+ ]
+ ]
+ )
+ return await message.reply_text(_["general_3"], reply_markup=upl)
+ if message.command[0][0] == "c":
+ chat_id = await get_cmode(message.chat.id)
+ if chat_id is None:
+ return await message.reply_text(_["setting_7"])
+ try:
+ await app.get_chat(chat_id)
+ except:
+ return await message.reply_text(_["cplay_4"])
+ else:
+ chat_id = message.chat.id
+ if not await is_active_chat(chat_id):
+ return await message.reply_text(_["general_5"])
+ is_non_admin = await is_nonadmin_chat(message.chat.id)
+ if not is_non_admin:
+ if message.from_user.id not in SUDOERS:
+ admins = adminlist.get(message.chat.id)
+ if not admins:
+ return await message.reply_text(_["admin_13"])
+ else:
+ if message.from_user.id not in admins:
+ if await is_skipmode(message.chat.id):
+ upvote = await get_upvote_count(chat_id)
+ text = f"""ᴀᴅᴍɪɴ ʀɪɢʜᴛs ɴᴇᴇᴅᴇᴅ
+
+ʀᴇғʀᴇsʜ ᴀᴅᴍɪɴ ᴄᴀᴄʜᴇ ᴠɪᴀ : /reload
+
+» {upvote} ᴠᴏᴛᴇs ɴᴇᴇᴅᴇᴅ ғᴏʀ ᴘᴇʀғᴏʀᴍɪɴɢ ᴛʜɪs ᴀᴄᴛɪᴏɴ."""
+
+ command = message.command[0]
+ if command[0] == "c":
+ command = command[1:]
+ if command == "speed":
+ return await message.reply_text(_["admin_14"])
+ MODE = command.title()
+ upl = InlineKeyboardMarkup(
+ [
+ [
+ InlineKeyboardButton(
+ text="ᴠᴏᴛᴇ",
+ callback_data=f"ADMIN UpVote|{chat_id}_{MODE}",
+ ),
+ ]
+ ]
+ )
+ if chat_id not in confirmer:
+ confirmer[chat_id] = {}
+ try:
+ vidid = db[chat_id][0]["vidid"]
+ file = db[chat_id][0]["file"]
+ except:
+ return await message.reply_text(_["admin_14"])
+ senn = await message.reply_text(text, reply_markup=upl)
+ confirmer[chat_id][senn.id] = {
+ "vidid": vidid,
+ "file": file,
+ }
+ return
+ else:
+ return await message.reply_text(_["admin_14"])
+
+ return await mystic(client, message, _, chat_id)
+
+ return wrapper
+
+
+def AdminActual(mystic):
+ async def wrapper(client, message):
+ if await is_maintenance() is False:
+ if message.from_user.id not in SUDOERS:
+ return await message.reply_text(
+ text=f"{app.mention} ɪs ᴜɴᴅᴇʀ ᴍᴀɪɴᴛᴇɴᴀɴᴄᴇ, ᴠɪsɪᴛ sᴜᴘᴘᴏʀᴛ ᴄʜᴀᴛ ғᴏʀ ᴋɴᴏᴡɪɴɢ ᴛʜᴇ ʀᴇᴀsᴏɴ.",
+ disable_web_page_preview=True,
+ )
+
+ try:
+ await message.delete()
+ except:
+ pass
+
+ try:
+ language = await get_lang(message.chat.id)
+ _ = get_string(language)
+ except:
+ _ = get_string("en")
+ if message.sender_chat:
+ upl = InlineKeyboardMarkup(
+ [
+ [
+ InlineKeyboardButton(
+ text="ʜᴏᴡ ᴛᴏ ғɪx ?",
+ callback_data="AnonymousAdmin",
+ ),
+ ]
+ ]
+ )
+ return await message.reply_text(_["general_3"], reply_markup=upl)
+ if message.from_user.id not in SUDOERS:
+ try:
+ member = (
+ await app.get_chat_member(message.chat.id, message.from_user.id)
+ ).privileges
+ except:
+ return
+ if not member.can_manage_video_chats:
+ return await message.reply(_["general_4"])
+ return await mystic(client, message, _)
+
+ return wrapper
+
+
+def ActualAdminCB(mystic):
+ async def wrapper(client, CallbackQuery):
+ if await is_maintenance() is False:
+ if CallbackQuery.from_user.id not in SUDOERS:
+ return await CallbackQuery.answer(
+ f"{app.mention} ɪs ᴜɴᴅᴇʀ ᴍᴀɪɴᴛᴇɴᴀɴᴄᴇ, ᴠɪsɪᴛ sᴜᴘᴘᴏʀᴛ ᴄʜᴀᴛ ғᴏʀ ᴋɴᴏᴡɪɴɢ ᴛʜᴇ ʀᴇᴀsᴏɴ.",
+ show_alert=True,
+ )
+ try:
+ language = await get_lang(CallbackQuery.message.chat.id)
+ _ = get_string(language)
+ except:
+ _ = get_string("en")
+ if CallbackQuery.message.chat.type == ChatType.PRIVATE:
+ return await mystic(client, CallbackQuery, _)
+ is_non_admin = await is_nonadmin_chat(CallbackQuery.message.chat.id)
+ if not is_non_admin:
+ try:
+ a = (
+ await app.get_chat_member(
+ CallbackQuery.message.chat.id,
+ CallbackQuery.from_user.id,
+ )
+ ).privileges
+ except:
+ return await CallbackQuery.answer(_["general_4"], show_alert=True)
+ if not a.can_manage_video_chats:
+ if CallbackQuery.from_user.id not in SUDOERS:
+ token = await int_to_alpha(CallbackQuery.from_user.id)
+ _check = await get_authuser_names(CallbackQuery.from_user.id)
+ if token not in _check:
+ try:
+ return await CallbackQuery.answer(
+ _["general_4"],
+ show_alert=True,
+ )
+ except:
+ return
+ return await mystic(client, CallbackQuery, _)
+
+ return wrapper
diff --git a/SprotifyMusic/utils/decorators/language.py b/SprotifyMusic/utils/decorators/language.py
new file mode 100644
index 000000000000..e675aeb394e6
--- /dev/null
+++ b/SprotifyMusic/utils/decorators/language.py
@@ -0,0 +1,58 @@
+from strings import get_string
+
+from SprotifyMusic import app
+from SprotifyMusic.misc import SUDOERS
+from SprotifyMusic.utils.database import get_lang, is_maintenance
+from config import SUPPORT_CHAT
+
+def language(mystic):
+ async def wrapper(_, message, **kwargs):
+ if await is_maintenance() is False:
+ if message.from_user.id not in SUDOERS:
+ return await message.reply_text(
+ text=f"{app.mention} ɪs ᴜɴᴅᴇʀ ᴍᴀɪɴᴛᴇɴᴀɴᴄᴇ, ᴠɪsɪᴛ sᴜᴘᴘᴏʀᴛ ᴄʜᴀᴛ ғᴏʀ ᴋɴᴏᴡɪɴɢ ᴛʜᴇ ʀᴇᴀsᴏɴ.",
+ disable_web_page_preview=True,
+ )
+ try:
+ await message.delete()
+ except:
+ pass
+
+ try:
+ language = await get_lang(message.chat.id)
+ language = get_string(language)
+ except:
+ language = get_string("en")
+ return await mystic(_, message, language)
+
+ return wrapper
+
+
+def languageCB(mystic):
+ async def wrapper(_, CallbackQuery, **kwargs):
+ if await is_maintenance() is False:
+ if CallbackQuery.from_user.id not in SUDOERS:
+ return await CallbackQuery.answer(
+ f"{app.mention} ɪs ᴜɴᴅᴇʀ ᴍᴀɪɴᴛᴇɴᴀɴᴄᴇ, ᴠɪsɪᴛ sᴜᴘᴘᴏʀᴛ ᴄʜᴀᴛ ғᴏʀ ᴋɴᴏᴡɪɴɢ ᴛʜᴇ ʀᴇᴀsᴏɴ.",
+ show_alert=True,
+ )
+ try:
+ language = await get_lang(CallbackQuery.message.chat.id)
+ language = get_string(language)
+ except:
+ language = get_string("en")
+ return await mystic(_, CallbackQuery, language)
+
+ return wrapper
+
+
+def LanguageStart(mystic):
+ async def wrapper(_, message, **kwargs):
+ try:
+ language = await get_lang(message.chat.id)
+ language = get_string(language)
+ except:
+ language = get_string("en")
+ return await mystic(_, message, language)
+
+ return wrapper
diff --git a/SprotifyMusic/utils/decorators/play.py b/SprotifyMusic/utils/decorators/play.py
new file mode 100644
index 000000000000..8bca2ea82d5c
--- /dev/null
+++ b/SprotifyMusic/utils/decorators/play.py
@@ -0,0 +1,195 @@
+import asyncio
+
+from pyrogram.enums import ChatMemberStatus
+from pyrogram.errors import (
+ ChatAdminRequired,
+ InviteRequestSent,
+ UserAlreadyParticipant,
+ UserNotParticipant,
+)
+from pyrogram.types import InlineKeyboardButton, InlineKeyboardMarkup
+
+from SprotifyMusic import YouTube, app
+from SprotifyMusic.misc import SUDOERS
+from SprotifyMusic.utils.database import (
+ get_assistant,
+ get_cmode,
+ get_lang,
+ get_playmode,
+ get_playtype,
+ is_active_chat,
+ is_maintenance,
+)
+from SprotifyMusic.utils.inline import botplaylist_markup
+from config import PLAYLIST_IMG_URL, SUPPORT_CHAT, adminlist
+from strings import get_string
+
+links = {}
+
+
+def PlayWrapper(command):
+ async def wrapper(client, message):
+ language = await get_lang(message.chat.id)
+ _ = get_string(language)
+ if message.sender_chat:
+ upl = InlineKeyboardMarkup(
+ [
+ [
+ InlineKeyboardButton(
+ text="ʜᴏᴡ ᴛᴏ ғɪx ?",
+ callback_data="AnonymousAdmin",
+ ),
+ ]
+ ]
+ )
+ return await message.reply_text(_["general_3"], reply_markup=upl)
+
+ if await is_maintenance() is False:
+ if message.from_user.id not in SUDOERS:
+ return await message.reply_text(
+ text=f"{app.mention} ɪs ᴜɴᴅᴇʀ ᴍᴀɪɴᴛᴇɴᴀɴᴄᴇ, ᴠɪsɪᴛ sᴜᴘᴘᴏʀᴛ ᴄʜᴀᴛ ғᴏʀ ᴋɴᴏᴡɪɴɢ ᴛʜᴇ ʀᴇᴀsᴏɴ.",
+ disable_web_page_preview=True,
+ )
+
+ try:
+ await message.delete()
+ except:
+ pass
+
+ audio_telegram = (
+ (message.reply_to_message.audio or message.reply_to_message.voice)
+ if message.reply_to_message
+ else None
+ )
+ video_telegram = (
+ (message.reply_to_message.video or message.reply_to_message.document)
+ if message.reply_to_message
+ else None
+ )
+ url = await YouTube.url(message)
+ if audio_telegram is None and video_telegram is None and url is None:
+ if len(message.command) < 2:
+ if "stream" in message.command:
+ return await message.reply_text(_["str_1"])
+ buttons = botplaylist_markup(_)
+ return await message.reply_photo(
+ photo=PLAYLIST_IMG_URL,
+ caption=_["play_18"],
+ reply_markup=InlineKeyboardMarkup(buttons),
+ )
+ if message.command[0][0] == "c":
+ chat_id = await get_cmode(message.chat.id)
+ if chat_id is None:
+ return await message.reply_text(_["setting_7"])
+ try:
+ chat = await app.get_chat(chat_id)
+ except:
+ return await message.reply_text(_["cplay_4"])
+ channel = chat.title
+ else:
+ chat_id = message.chat.id
+ channel = None
+ playmode = await get_playmode(message.chat.id)
+ playty = await get_playtype(message.chat.id)
+ if playty != "Everyone":
+ if message.from_user.id not in SUDOERS:
+ admins = adminlist.get(message.chat.id)
+ if not admins:
+ return await message.reply_text(_["admin_13"])
+ else:
+ if message.from_user.id not in admins:
+ return await message.reply_text(_["play_4"])
+ if message.command[0][0] == "v":
+ video = True
+ else:
+ if "-v" in message.text:
+ video = True
+ else:
+ video = True if message.command[0][1] == "v" else None
+ if message.command[0][-1] == "e":
+ if not await is_active_chat(chat_id):
+ return await message.reply_text(_["play_16"])
+ fplay = True
+ else:
+ fplay = None
+
+ if not await is_active_chat(chat_id):
+ userbot = await get_assistant(chat_id)
+ try:
+ try:
+ get = await app.get_chat_member(chat_id, userbot.id)
+ except ChatAdminRequired:
+ return await message.reply_text(_["call_1"])
+ if (
+ get.status == ChatMemberStatus.BANNED
+ or get.status == ChatMemberStatus.RESTRICTED
+ ):
+ return await message.reply_text(
+ _["call_2"].format(
+ app.mention, userbot.id, userbot.name, userbot.username
+ )
+ )
+ except UserNotParticipant:
+ if chat_id in links:
+ invitelink = links[chat_id]
+ else:
+ if message.chat.username:
+ invitelink = message.chat.username
+ try:
+ await userbot.resolve_peer(invitelink)
+ except:
+ pass
+ else:
+ try:
+ invitelink = await app.export_chat_invite_link(chat_id)
+ except ChatAdminRequired:
+ return await message.reply_text(_["call_1"])
+ except Exception as e:
+ return await message.reply_text(
+ _["call_3"].format(app.mention, type(e).__name__)
+ )
+
+ if invitelink.startswith("https://t.me/+"):
+ invitelink = invitelink.replace(
+ "https://t.me/+", "https://t.me/joinchat/"
+ )
+ myu = await message.reply_text(_["call_4"].format(app.mention))
+ try:
+ await asyncio.sleep(1)
+ await userbot.join_chat(invitelink)
+ except InviteRequestSent:
+ try:
+ await app.approve_chat_join_request(chat_id, userbot.id)
+ except Exception as e:
+ return await message.reply_text(
+ _["call_3"].format(app.mention, type(e).__name__)
+ )
+ await asyncio.sleep(3)
+ await myu.edit(_["call_5"].format(app.mention))
+ except UserAlreadyParticipant:
+ pass
+ except Exception as e:
+ return await message.reply_text(
+ _["call_3"].format(app.mention, type(e).__name__)
+ )
+
+ links[chat_id] = invitelink
+
+ try:
+ await userbot.resolve_peer(chat_id)
+ except:
+ pass
+
+ return await command(
+ client,
+ message,
+ _,
+ chat_id,
+ video,
+ channel,
+ playmode,
+ url,
+ fplay,
+ )
+
+ return wrapper
diff --git a/SprotifyMusic/utils/error.py b/SprotifyMusic/utils/error.py
new file mode 100644
index 000000000000..54732a0800e9
--- /dev/null
+++ b/SprotifyMusic/utils/error.py
@@ -0,0 +1,39 @@
+import traceback
+from functools import wraps
+
+from pyrogram.errors.exceptions.forbidden_403 import ChatWriteForbidden
+
+from config import LOGGER_ID
+from SprotifyMusic import app
+
+def capture_err(func):
+ @wraps(func)
+ async def capture(client, message, *args, **kwargs):
+ try:
+ return await func(client, message, *args, **kwargs)
+ except ChatWriteForbidden:
+ await app.leave_chat(message.chat.id)
+ return
+ except Exception as err:
+ errors = traceback.format_exc()
+ error_feedback = split_limits(
+ "ERROR | {} | {}\n```command\n{}```\n\n```python\n{}```\n".format(
+ 0 if not message.from_user else message.from_user.mention,
+ (
+ 0
+ if not message.chat
+ else (
+ f"@{message.chat.username}"
+ if message.chat.username
+ else f"`{message.chat.id}`"
+ )
+ ),
+ message.text or message.caption,
+ "".join(errors),
+ ),
+ )
+ for x in error_feedback:
+ await app.send_message(LOG_GROUP_ID, x)
+ raise err
+
+ return capture
\ No newline at end of file
diff --git a/SprotifyMusic/utils/exceptions.py b/SprotifyMusic/utils/exceptions.py
new file mode 100644
index 000000000000..c28414d73415
--- /dev/null
+++ b/SprotifyMusic/utils/exceptions.py
@@ -0,0 +1,3 @@
+class AssistantErr(Exception):
+ def __init__(self, errr: str):
+ super().__init__(errr)
diff --git a/SprotifyMusic/utils/extraction.py b/SprotifyMusic/utils/extraction.py
new file mode 100644
index 000000000000..eeb60bc22f37
--- /dev/null
+++ b/SprotifyMusic/utils/extraction.py
@@ -0,0 +1,17 @@
+from pyrogram.enums import MessageEntityType
+from pyrogram.types import Message, User
+
+from SprotifyMusic import app
+
+
+async def extract_user(m: Message) -> User:
+ if m.reply_to_message:
+ return m.reply_to_message.from_user
+ msg_entities = m.entities[1] if m.text.startswith("/") else m.entities[0]
+ return await app.get_users(
+ msg_entities.user.id
+ if msg_entities.type == MessageEntityType.TEXT_MENTION
+ else int(m.command[1])
+ if m.command[1].isdecimal()
+ else m.command[1]
+ )
diff --git a/SprotifyMusic/utils/fonts.py b/SprotifyMusic/utils/fonts.py
new file mode 100644
index 000000000000..a126a7512e50
--- /dev/null
+++ b/SprotifyMusic/utils/fonts.py
@@ -0,0 +1,2361 @@
+class Fonts:
+ def typewriter(text):
+ style = {
+ "a": "𝚊",
+ "b": "𝚋",
+ "c": "𝚌",
+ "d": "𝚍",
+ "e": "𝚎",
+ "f": "𝚏",
+ "g": "𝚐",
+ "h": "𝚑",
+ "i": "𝚒",
+ "j": "𝚓",
+ "k": "𝚔",
+ "l": "𝚕",
+ "m": "𝚖",
+ "n": "𝚗",
+ "o": "𝚘",
+ "p": "𝚙",
+ "q": "𝚚",
+ "r": "𝚛",
+ "s": "𝚜",
+ "t": "𝚝",
+ "u": "𝚞",
+ "v": "𝚟",
+ "w": "𝚠",
+ "x": "𝚡",
+ "y": "𝚢",
+ "z": "𝚣",
+ "A": "𝙰",
+ "B": "𝙱",
+ "C": "𝙲",
+ "D": "𝙳",
+ "E": "𝙴",
+ "F": "𝙵",
+ "G": "𝙶",
+ "H": "𝙷",
+ "I": "𝙸",
+ "J": "𝙹",
+ "K": "𝙺",
+ "L": "𝙻",
+ "M": "𝙼",
+ "N": "𝙽",
+ "O": "𝙾",
+ "P": "𝙿",
+ "Q": "𝚀",
+ "R": "𝚁",
+ "S": "𝚂",
+ "T": "𝚃",
+ "U": "𝚄",
+ "V": "𝚅",
+ "W": "𝚆",
+ "X": "𝚇",
+ "Y": "𝚈",
+ "Z": "𝚉",
+ }
+ for i, j in style.items():
+ text = text.replace(i, j)
+ return text
+
+ def outline(text):
+ style = {
+ "a": "𝕒",
+ "b": "𝕓",
+ "c": "𝕔",
+ "d": "𝕕",
+ "e": "𝕖",
+ "f": "𝕗",
+ "g": "𝕘",
+ "h": "𝕙",
+ "i": "𝕚",
+ "j": "𝕛",
+ "k": "𝕜",
+ "l": "𝕝",
+ "m": "𝕞",
+ "n": "𝕟",
+ "o": "𝕠",
+ "p": "𝕡",
+ "q": "𝕢",
+ "r": "𝕣",
+ "s": "𝕤",
+ "t": "𝕥",
+ "u": "𝕦",
+ "v": "𝕧",
+ "w": "𝕨",
+ "x": "𝕩",
+ "y": "𝕪",
+ "z": "𝕫",
+ "A": "𝔸",
+ "B": "𝔹",
+ "C": "ℂ",
+ "D": "𝔻",
+ "E": "𝔼",
+ "F": "𝔽",
+ "G": "𝔾",
+ "H": "ℍ",
+ "I": "𝕀",
+ "J": "𝕁",
+ "K": "𝕂",
+ "L": "𝕃",
+ "M": "𝕄",
+ "N": "ℕ",
+ "O": "𝕆",
+ "P": "ℙ",
+ "Q": "ℚ",
+ "R": "ℝ",
+ "S": "𝕊",
+ "T": "𝕋",
+ "U": "𝕌",
+ "V": "𝕍",
+ "W": "𝕎",
+ "X": "𝕏",
+ "Y": "𝕐",
+ "Z": "ℤ",
+ "0": "𝟘",
+ "1": "𝟙",
+ "2": "𝟚",
+ "3": "𝟛",
+ "4": "𝟜",
+ "5": "𝟝",
+ "6": "𝟞",
+ "7": "𝟟",
+ "8": "𝟠",
+ "9": "𝟡",
+ }
+ for i, j in style.items():
+ text = text.replace(i, j)
+ return text
+
+ def serief(text):
+ style = {
+ "a": "𝐚",
+ "b": "𝐛",
+ "c": "𝐜",
+ "d": "𝐝",
+ "e": "𝐞",
+ "f": "𝐟",
+ "g": "𝐠",
+ "h": "𝐡",
+ "i": "𝐢",
+ "j": "𝐣",
+ "k": "𝐤",
+ "l": "𝐥",
+ "m": "𝐦",
+ "n": "𝐧",
+ "o": "𝐨",
+ "p": "𝐩",
+ "q": "𝐪",
+ "r": "𝐫",
+ "s": "𝐬",
+ "t": "𝐭",
+ "u": "𝐮",
+ "v": "𝐯",
+ "w": "𝐰",
+ "x": "𝐱",
+ "y": "𝐲",
+ "z": "𝐳",
+ "A": "𝐀",
+ "B": "𝐁",
+ "C": "𝐂",
+ "D": "𝐃",
+ "E": "𝐄",
+ "F": "𝐅",
+ "G": "𝐆",
+ "H": "𝐇",
+ "I": "𝐈",
+ "J": "𝐉",
+ "K": "𝐊",
+ "L": "𝐋",
+ "M": "𝐌",
+ "N": "𝐍",
+ "O": "𝐎",
+ "P": "𝐏",
+ "Q": "𝐐",
+ "R": "𝐑",
+ "S": "𝐒",
+ "T": "𝐓",
+ "U": "𝐔",
+ "V": "𝐕",
+ "W": "𝐖",
+ "X": "𝐗",
+ "Y": "𝐘",
+ "Z": "𝐙",
+ "0": "𝟎",
+ "1": "𝟏",
+ "2": "𝟐",
+ "3": "𝟑",
+ "4": "𝟒",
+ "5": "𝟓",
+ "6": "𝟔",
+ "7": "𝟕",
+ "8": "𝟖",
+ "9": "𝟗",
+ }
+ for i, j in style.items():
+ text = text.replace(i, j)
+ return text
+
+ def bold_cool(text):
+ style = {
+ "a": "𝒂",
+ "b": "𝒃",
+ "c": "𝒄",
+ "d": "𝒅",
+ "e": "𝒆",
+ "f": "𝒇",
+ "g": "𝒈",
+ "h": "𝒉",
+ "i": "𝒊",
+ "j": "𝒋",
+ "k": "𝒌",
+ "l": "𝒍",
+ "m": "𝒎",
+ "n": "𝒏",
+ "o": "𝒐",
+ "p": "𝒑",
+ "q": "𝒒",
+ "r": "𝒓",
+ "s": "𝒔",
+ "t": "𝒕",
+ "u": "𝒖",
+ "v": "𝒗",
+ "w": "𝒘",
+ "x": "𝒙",
+ "y": "𝒚",
+ "z": "𝒛",
+ "A": "𝑨",
+ "B": "𝑩",
+ "C": "𝑪",
+ "D": "𝑫",
+ "E": "𝑬",
+ "F": "𝑭",
+ "G": "𝑮",
+ "H": "𝑯",
+ "I": "𝑰",
+ "J": "𝑱",
+ "K": "𝑲",
+ "L": "𝑳",
+ "M": "𝑴",
+ "N": "𝑵",
+ "O": "𝑶",
+ "P": "𝑷",
+ "Q": "𝑸",
+ "R": "𝑹",
+ "S": "𝑺",
+ "T": "𝑻",
+ "U": "𝑼",
+ "V": "𝑽",
+ "W": "𝑾",
+ "X": "𝑿",
+ "Y": "𝒀",
+ "Z": "𝒁",
+ }
+ for i, j in style.items():
+ text = text.replace(i, j)
+ return text
+
+ def cool(text):
+ style = {
+ "a": "𝑎",
+ "b": "𝑏",
+ "c": "𝑐",
+ "d": "𝑑",
+ "e": "𝑒",
+ "f": "𝑓",
+ "g": "𝑔",
+ "h": "ℎ",
+ "i": "𝑖",
+ "j": "𝑗",
+ "k": "𝑘",
+ "l": "𝑙",
+ "m": "𝑚",
+ "n": "𝑛",
+ "o": "𝑜",
+ "p": "𝑝",
+ "q": "𝑞",
+ "r": "𝑟",
+ "s": "𝑠",
+ "t": "𝑡",
+ "u": "𝑢",
+ "v": "𝑣",
+ "w": "𝑤",
+ "x": "𝑥",
+ "y": "𝑦",
+ "z": "𝑧",
+ "A": "𝐴",
+ "B": "𝐵",
+ "C": "𝐶",
+ "D": "𝐷",
+ "E": "𝐸",
+ "F": "𝐹",
+ "G": "𝐺",
+ "H": "𝐻",
+ "I": "𝐼",
+ "J": "𝐽",
+ "K": "𝐾",
+ "L": "𝐿",
+ "M": "𝑀",
+ "N": "𝑁",
+ "O": "𝑂",
+ "P": "𝑃",
+ "Q": "𝑄",
+ "R": "𝑅",
+ "S": "𝑆",
+ "T": "𝑇",
+ "U": "𝑈",
+ "V": "𝑉",
+ "W": "𝑊",
+ "X": "𝑋",
+ "Y": "𝑌",
+ "Z": "𝑍",
+ }
+ for i, j in style.items():
+ text = text.replace(i, j)
+ return text
+
+ def smallcap(text):
+ style = {
+ "a": "ᴀ",
+ "b": "ʙ",
+ "c": "ᴄ",
+ "d": "ᴅ",
+ "e": "ᴇ",
+ "f": "ғ",
+ "g": "ɢ",
+ "h": "ʜ",
+ "i": "ɪ",
+ "j": "J",
+ "k": "ᴋ",
+ "l": "ʟ",
+ "m": "ᴍ",
+ "n": "ɴ",
+ "o": "ᴏ",
+ "p": "ᴘ",
+ "q": "ǫ",
+ "r": "ʀ",
+ "s": "s",
+ "t": "ᴛ",
+ "u": "ᴜ",
+ "v": "ᴠ",
+ "w": "ᴡ",
+ "x": "x",
+ "y": "ʏ",
+ "z": "ᴢ",
+ "A": "A",
+ "B": "B",
+ "C": "C",
+ "D": "D",
+ "E": "E",
+ "F": "F",
+ "G": "G",
+ "H": "H",
+ "I": "I",
+ "J": "J",
+ "K": "K",
+ "L": "L",
+ "M": "M",
+ "N": "N",
+ "O": "O",
+ "P": "P",
+ "Q": "Q",
+ "R": "R",
+ "S": "S",
+ "T": "T",
+ "U": "U",
+ "V": "V",
+ "W": "W",
+ "X": "X",
+ "Y": "Y",
+ "Z": "Z",
+ "0": "𝟶",
+ "1": "𝟷",
+ "2": "𝟸",
+ "3": "𝟹",
+ "4": "𝟺",
+ "5": "𝟻",
+ "6": "𝟼",
+ "7": "𝟽",
+ "8": "𝟾",
+ "9": "𝟿",
+ }
+ for i, j in style.items():
+ text = text.replace(i, j)
+ return text
+
+ def script(text):
+ style = {
+ "a": "𝒶",
+ "b": "𝒷",
+ "c": "𝒸",
+ "d": "𝒹",
+ "e": "ℯ",
+ "f": "𝒻",
+ "g": "ℊ",
+ "h": "𝒽",
+ "i": "𝒾",
+ "j": "𝒿",
+ "k": "𝓀",
+ "l": "𝓁",
+ "m": "𝓂",
+ "n": "𝓃",
+ "o": "ℴ",
+ "p": "𝓅",
+ "q": "𝓆",
+ "r": "𝓇",
+ "s": "𝓈",
+ "t": "𝓉",
+ "u": "𝓊",
+ "v": "𝓋",
+ "w": "𝓌",
+ "x": "𝓍",
+ "y": "𝓎",
+ "z": "𝓏",
+ "A": "𝒜",
+ "B": "ℬ",
+ "C": "𝒞",
+ "D": "𝒟",
+ "E": "ℰ",
+ "F": "ℱ",
+ "G": "𝒢",
+ "H": "ℋ",
+ "I": "ℐ",
+ "J": "𝒥",
+ "K": "𝒦",
+ "L": "ℒ",
+ "M": "ℳ",
+ "N": "𝒩",
+ "O": "𝒪",
+ "P": "𝒫",
+ "Q": "𝒬",
+ "R": "ℛ",
+ "S": "𝒮",
+ "T": "𝒯",
+ "U": "𝒰",
+ "V": "𝒱",
+ "W": "𝒲",
+ "X": "𝒳",
+ "Y": "𝒴",
+ "Z": "𝒵",
+ }
+ for i, j in style.items():
+ text = text.replace(i, j)
+ return text
+
+ def bold_script(text):
+ style = {
+ "a": "𝓪",
+ "b": "𝓫",
+ "c": "𝓬",
+ "d": "𝓭",
+ "e": "𝓮",
+ "f": "𝓯",
+ "g": "𝓰",
+ "h": "𝓱",
+ "i": "𝓲",
+ "j": "𝓳",
+ "k": "𝓴",
+ "l": "𝓵",
+ "m": "𝓶",
+ "n": "𝓷",
+ "o": "𝓸",
+ "p": "𝓹",
+ "q": "𝓺",
+ "r": "𝓻",
+ "s": "𝓼",
+ "t": "𝓽",
+ "u": "𝓾",
+ "v": "𝓿",
+ "w": "𝔀",
+ "x": "𝔁",
+ "y": "𝔂",
+ "z": "𝔃",
+ "A": "𝓐",
+ "B": "𝓑",
+ "C": "𝓒",
+ "D": "𝓓",
+ "E": "𝓔",
+ "F": "𝓕",
+ "G": "𝓖",
+ "H": "𝓗",
+ "I": "𝓘",
+ "J": "𝓙",
+ "K": "𝓚",
+ "L": "𝓛",
+ "M": "𝓜",
+ "N": "𝓝",
+ "O": "𝓞",
+ "P": "𝓟",
+ "Q": "𝓠",
+ "R": "𝓡",
+ "S": "𝓢",
+ "T": "𝓣",
+ "U": "𝓤",
+ "V": "𝓥",
+ "W": "𝓦",
+ "X": "𝓧",
+ "Y": "𝓨",
+ "Z": "𝓩",
+ }
+ for i, j in style.items():
+ text = text.replace(i, j)
+ return text
+
+ def tiny(text):
+ style = {
+ "a": "ᵃ",
+ "b": "ᵇ",
+ "c": "ᶜ",
+ "d": "ᵈ",
+ "e": "ᵉ",
+ "f": "ᶠ",
+ "g": "ᵍ",
+ "h": "ʰ",
+ "i": "ⁱ",
+ "j": "ʲ",
+ "k": "ᵏ",
+ "l": "ˡ",
+ "m": "ᵐ",
+ "n": "ⁿ",
+ "o": "ᵒ",
+ "p": "ᵖ",
+ "q": "ᵠ",
+ "r": "ʳ",
+ "s": "ˢ",
+ "t": "ᵗ",
+ "u": "ᵘ",
+ "v": "ᵛ",
+ "w": "ʷ",
+ "x": "ˣ",
+ "y": "ʸ",
+ "z": "ᶻ",
+ "A": "ᵃ",
+ "B": "ᵇ",
+ "C": "ᶜ",
+ "D": "ᵈ",
+ "E": "ᵉ",
+ "F": "ᶠ",
+ "G": "ᵍ",
+ "H": "ʰ",
+ "I": "ⁱ",
+ "J": "ʲ",
+ "K": "ᵏ",
+ "L": "ˡ",
+ "M": "ᵐ",
+ "N": "ⁿ",
+ "O": "ᵒ",
+ "P": "ᵖ",
+ "Q": "ᵠ",
+ "R": "ʳ",
+ "S": "ˢ",
+ "T": "ᵗ",
+ "U": "ᵘ",
+ "V": "ᵛ",
+ "W": "ʷ",
+ "X": "ˣ",
+ "Y": "ʸ",
+ "Z": "ᶻ",
+ }
+ for i, j in style.items():
+ text = text.replace(i, j)
+ return text
+
+ def comic(text):
+ style = {
+ "a": "ᗩ",
+ "b": "ᗷ",
+ "c": "ᑕ",
+ "d": "ᗪ",
+ "e": "ᗴ",
+ "f": "ᖴ",
+ "g": "ᘜ",
+ "h": "ᕼ",
+ "i": "I",
+ "j": "ᒍ",
+ "k": "K",
+ "l": "ᒪ",
+ "m": "ᗰ",
+ "n": "ᑎ",
+ "o": "O",
+ "p": "ᑭ",
+ "q": "ᑫ",
+ "r": "ᖇ",
+ "s": "Տ",
+ "t": "T",
+ "u": "ᑌ",
+ "v": "ᐯ",
+ "w": "ᗯ",
+ "x": "᙭",
+ "y": "Y",
+ "z": "ᘔ",
+ "A": "ᗩ",
+ "B": "ᗷ",
+ "C": "ᑕ",
+ "D": "ᗪ",
+ "E": "ᗴ",
+ "F": "ᖴ",
+ "G": "ᘜ",
+ "H": "ᕼ",
+ "I": "I",
+ "J": "ᒍ",
+ "K": "K",
+ "L": "ᒪ",
+ "M": "ᗰ",
+ "N": "ᑎ",
+ "O": "O",
+ "P": "ᑭ",
+ "Q": "ᑫ",
+ "R": "ᖇ",
+ "S": "Տ",
+ "T": "T",
+ "U": "ᑌ",
+ "V": "ᐯ",
+ "W": "ᗯ",
+ "X": "᙭",
+ "Y": "Y",
+ "Z": "ᘔ",
+ }
+ for i, j in style.items():
+ text = text.replace(i, j)
+ return text
+
+ def san(text):
+ style = {
+ "a": "𝗮",
+ "b": "𝗯",
+ "c": "𝗰",
+ "d": "𝗱",
+ "e": "𝗲",
+ "f": "𝗳",
+ "g": "𝗴",
+ "h": "𝗵",
+ "i": "𝗶",
+ "j": "𝗷",
+ "k": "𝗸",
+ "l": "𝗹",
+ "m": "𝗺",
+ "n": "𝗻",
+ "o": "𝗼",
+ "p": "𝗽",
+ "q": "𝗾",
+ "r": "𝗿",
+ "s": "𝘀",
+ "t": "𝘁",
+ "u": "𝘂",
+ "v": "𝘃",
+ "w": "𝘄",
+ "x": "𝘅",
+ "y": "𝘆",
+ "z": "𝘇",
+ "A": "𝗔",
+ "B": "𝗕",
+ "C": "𝗖",
+ "D": "𝗗",
+ "E": "𝗘",
+ "F": "𝗙",
+ "G": "𝗚",
+ "H": "𝗛",
+ "I": "𝗜",
+ "J": "𝗝",
+ "K": "𝗞",
+ "L": "𝗟",
+ "M": "𝗠",
+ "N": "𝗡",
+ "O": "𝗢",
+ "P": "𝗣",
+ "Q": "𝗤",
+ "R": "𝗥",
+ "S": "𝗦",
+ "T": "𝗧",
+ "U": "𝗨",
+ "V": "𝗩",
+ "W": "𝗪",
+ "X": "𝗫",
+ "Y": "𝗬",
+ "Z": "𝗭",
+ "0": "𝟬",
+ "1": "𝟭",
+ "2": "𝟮",
+ "3": "𝟯",
+ "4": "𝟰",
+ "5": "𝟱",
+ "6": "𝟲",
+ "7": "𝟳",
+ "8": "𝟴",
+ "9": "𝟵",
+ }
+ for i, j in style.items():
+ text = text.replace(i, j)
+ return text
+
+ def slant_san(text):
+ style = {
+ "a": "𝙖",
+ "b": "𝙗",
+ "c": "𝙘",
+ "d": "𝙙",
+ "e": "𝙚",
+ "f": "𝙛",
+ "g": "𝙜",
+ "h": "𝙝",
+ "i": "𝙞",
+ "j": "𝙟",
+ "k": "𝙠",
+ "l": "𝙡",
+ "m": "𝙢",
+ "n": "𝙣",
+ "o": "𝙤",
+ "p": "𝙥",
+ "q": "𝙦",
+ "r": "𝙧",
+ "s": "𝙨",
+ "t": "𝙩",
+ "u": "𝙪",
+ "v": "𝙫",
+ "w": "𝙬",
+ "x": "𝙭",
+ "y": "𝙮",
+ "z": "𝙯",
+ "A": "𝘼",
+ "B": "𝘽",
+ "C": "𝘾",
+ "D": "𝘿",
+ "E": "𝙀",
+ "F": "𝙁",
+ "G": "𝙂",
+ "H": "𝙃",
+ "I": "𝙄",
+ "J": "𝙅",
+ "K": "𝙆",
+ "L": "𝙇",
+ "M": "𝙈",
+ "N": "𝙉",
+ "O": "𝙊",
+ "P": "𝙋",
+ "Q": "𝙌",
+ "R": "𝙍",
+ "S": "𝙎",
+ "T": "𝙏",
+ "U": "𝙐",
+ "V": "𝙑",
+ "W": "𝙒",
+ "X": "𝙓",
+ "Y": "𝙔",
+ "Z": "𝙕",
+ }
+ for i, j in style.items():
+ text = text.replace(i, j)
+ return text
+
+ def slant(text):
+ style = {
+ "a": "𝘢",
+ "b": "𝘣",
+ "c": "𝘤",
+ "d": "𝘥",
+ "e": "𝘦",
+ "f": "𝘧",
+ "g": "𝘨",
+ "h": "𝘩",
+ "i": "𝘪",
+ "j": "𝘫",
+ "k": "𝘬",
+ "l": "𝘭",
+ "m": "𝘮",
+ "n": "𝘯",
+ "o": "𝘰",
+ "p": "𝘱",
+ "q": "𝘲",
+ "r": "𝘳",
+ "s": "𝘴",
+ "t": "𝘵",
+ "u": "𝘶",
+ "v": "𝘷",
+ "w": "𝘸",
+ "x": "𝘹",
+ "y": "𝘺",
+ "z": "𝘻",
+ "A": "𝘈",
+ "B": "𝘉",
+ "C": "𝘊",
+ "D": "𝘋",
+ "E": "𝘌",
+ "F": "𝘍",
+ "G": "𝘎",
+ "H": "𝘏",
+ "I": "𝘐",
+ "J": "𝘑",
+ "K": "𝘒",
+ "L": "𝘓",
+ "M": "𝘔",
+ "N": "𝘕",
+ "O": "𝘖",
+ "P": "𝘗",
+ "Q": "𝘘",
+ "R": "𝘙",
+ "S": "𝘚",
+ "T": "𝘛",
+ "U": "𝘜",
+ "V": "𝘝",
+ "W": "𝘞",
+ "X": "𝘟",
+ "Y": "𝘠",
+ "Z": "𝘡",
+ }
+ for i, j in style.items():
+ text = text.replace(i, j)
+ return text
+
+ def sim(text):
+ style = {
+ "a": "𝖺",
+ "b": "𝖻",
+ "c": "𝖼",
+ "d": "𝖽",
+ "e": "𝖾",
+ "f": "𝖿",
+ "g": "𝗀",
+ "h": "𝗁",
+ "i": "𝗂",
+ "j": "𝗃",
+ "k": "𝗄",
+ "l": "𝗅",
+ "m": "𝗆",
+ "n": "𝗇",
+ "o": "𝗈",
+ "p": "𝗉",
+ "q": "𝗊",
+ "r": "𝗋",
+ "s": "𝗌",
+ "t": "𝗍",
+ "u": "𝗎",
+ "v": "𝗏",
+ "w": "𝗐",
+ "x": "𝗑",
+ "y": "𝗒",
+ "z": "𝗓",
+ "A": "𝖠",
+ "B": "𝖡",
+ "C": "𝖢",
+ "D": "𝖣",
+ "E": "𝖤",
+ "F": "𝖥",
+ "G": "𝖦",
+ "H": "𝖧",
+ "I": "𝖨",
+ "J": "𝖩",
+ "K": "𝖪",
+ "L": "𝖫",
+ "M": "𝖬",
+ "N": "𝖭",
+ "O": "𝖮",
+ "P": "𝖯",
+ "Q": "𝖰",
+ "R": "𝖱",
+ "S": "𝖲",
+ "T": "𝖳",
+ "U": "𝖴",
+ "V": "𝖵",
+ "W": "𝖶",
+ "X": "𝖷",
+ "Y": "𝖸",
+ "Z": "𝖹",
+ }
+ for i, j in style.items():
+ text = text.replace(i, j)
+ return text
+
+ def circles(text):
+ style = {
+ "a": "Ⓐ︎",
+ "b": "Ⓑ︎",
+ "c": "Ⓒ︎",
+ "d": "Ⓓ︎",
+ "e": "Ⓔ︎",
+ "f": "Ⓕ︎",
+ "g": "Ⓖ︎",
+ "h": "Ⓗ︎",
+ "i": "Ⓘ︎",
+ "j": "Ⓙ︎",
+ "k": "Ⓚ︎",
+ "l": "Ⓛ︎",
+ "m": "Ⓜ︎",
+ "n": "Ⓝ︎",
+ "o": "Ⓞ︎",
+ "p": "Ⓟ︎",
+ "q": "Ⓠ︎",
+ "r": "Ⓡ︎",
+ "s": "Ⓢ︎",
+ "t": "Ⓣ︎",
+ "u": "Ⓤ︎",
+ "v": "Ⓥ︎",
+ "w": "Ⓦ︎",
+ "x": "Ⓧ︎",
+ "y": "Ⓨ︎",
+ "z": "Ⓩ︎",
+ "A": "Ⓐ︎",
+ "B": "Ⓑ︎",
+ "C": "Ⓒ︎",
+ "D": "Ⓓ︎",
+ "E": "Ⓔ︎",
+ "F": "Ⓕ︎",
+ "G": "Ⓖ︎",
+ "H": "Ⓗ︎",
+ "I": "Ⓘ︎",
+ "J": "Ⓙ︎",
+ "K": "Ⓚ︎",
+ "L": "Ⓛ︎",
+ "M": "Ⓜ︎",
+ "N": "Ⓝ︎",
+ "O": "Ⓞ︎",
+ "P": "Ⓟ︎",
+ "Q": "Ⓠ︎",
+ "R": "Ⓡ︎",
+ "S": "Ⓢ︎",
+ "T": "Ⓣ︎",
+ "U": "Ⓤ︎",
+ "V": "Ⓥ︎",
+ "W": "Ⓦ︎",
+ "X": "Ⓧ︎",
+ "Y": "Ⓨ︎",
+ "Z": "Ⓩ︎",
+ "0": "⓪",
+ "1": "①",
+ "2": "②",
+ "3": "③",
+ "4": "④",
+ "5": "⑤",
+ "6": "⑥",
+ "7": "⑦",
+ "8": "⑧",
+ "9": "⑨",
+ }
+ for i, j in style.items():
+ text = text.replace(i, j)
+ return text
+
+ def dark_circle(text):
+ style = {
+ "a": "🅐︎",
+ "b": "🅑︎",
+ "c": "🅒︎",
+ "d": "🅓︎",
+ "e": "🅔︎",
+ "f": "🅕︎",
+ "g": "🅖︎",
+ "h": "🅗︎",
+ "i": "🅘︎",
+ "j": "🅙︎",
+ "k": "🅚︎",
+ "l": "🅛︎",
+ "m": "🅜︎",
+ "n": "🅝︎",
+ "o": "🅞︎",
+ "p": "🅟︎",
+ "q": "🅠︎",
+ "r": "🅡︎",
+ "s": "🅢︎",
+ "t": "🅣︎",
+ "u": "🅤︎",
+ "v": "🅥︎",
+ "w": "🅦︎",
+ "x": "🅧︎",
+ "y": "🅨︎",
+ "z": "🅩︎",
+ "A": "🅐︎",
+ "B": "🅑︎",
+ "C": "🅒︎",
+ "D": "🅓︎",
+ "E": "🅔︎",
+ "F": "🅕︎",
+ "G": "🅖︎",
+ "H": "🅗︎",
+ "I": "🅘︎",
+ "J": "🅙︎",
+ "K": "🅚︎",
+ "L": "🅛︎",
+ "M": "🅜︎",
+ "N": "🅝︎",
+ "O": "🅞︎",
+ "P": "🅟︎",
+ "Q": "🅠︎",
+ "R": "🅡︎",
+ "S": "🅢︎",
+ "T": "🅣︎",
+ "U": "🅤︎",
+ "V": "🅥︎",
+ "W": "🅦︎",
+ "X": "🅧︎",
+ "Y": "🅨︎",
+ "Z": "🅩",
+ "0": "⓿",
+ "1": "➊",
+ "2": "➋",
+ "3": "➌",
+ "4": "➍",
+ "5": "➎",
+ "6": "➏",
+ "7": "➐",
+ "8": "➑",
+ "9": "➒",
+ }
+ for i, j in style.items():
+ text = text.replace(i, j)
+ return text
+
+ def gothic(text):
+ style = {
+ "a": "𝔞",
+ "b": "𝔟",
+ "c": "𝔠",
+ "d": "𝔡",
+ "e": "𝔢",
+ "f": "𝔣",
+ "g": "𝔤",
+ "h": "𝔥",
+ "i": "𝔦",
+ "j": "𝔧",
+ "k": "𝔨",
+ "l": "𝔩",
+ "m": "𝔪",
+ "n": "𝔫",
+ "o": "𝔬",
+ "p": "𝔭",
+ "q": "𝔮",
+ "r": "𝔯",
+ "s": "𝔰",
+ "t": "𝔱",
+ "u": "𝔲",
+ "v": "𝔳",
+ "w": "𝔴",
+ "x": "𝔵",
+ "y": "𝔶",
+ "z": "𝔷",
+ "A": "𝔄",
+ "B": "𝔅",
+ "C": "ℭ",
+ "D": "𝔇",
+ "E": "𝔈",
+ "F": "𝔉",
+ "G": "𝔊",
+ "H": "ℌ",
+ "I": "ℑ",
+ "J": "𝔍",
+ "K": "𝔎",
+ "L": "𝔏",
+ "M": "𝔐",
+ "N": "𝔑",
+ "O": "𝔒",
+ "P": "𝔓",
+ "Q": "𝔔",
+ "R": "ℜ",
+ "S": "𝔖",
+ "T": "𝔗",
+ "U": "𝔘",
+ "V": "𝔙",
+ "W": "𝔚",
+ "X": "𝔛",
+ "Y": "𝔜",
+ "Z": "ℨ",
+ }
+ for i, j in style.items():
+ text = text.replace(i, j)
+ return text
+
+ def bold_gothic(text):
+ style = {
+ "a": "𝖆",
+ "b": "𝖇",
+ "c": "𝖈",
+ "d": "𝖉",
+ "e": "𝖊",
+ "f": "𝖋",
+ "g": "𝖌",
+ "h": "𝖍",
+ "i": "𝖎",
+ "j": "𝖏",
+ "k": "𝖐",
+ "l": "𝖑",
+ "m": "𝖒",
+ "n": "𝖓",
+ "o": "𝖔",
+ "p": "𝖕",
+ "q": "𝖖",
+ "r": "𝖗",
+ "s": "𝖘",
+ "t": "𝖙",
+ "u": "𝖚",
+ "v": "𝖛",
+ "w": "𝖜",
+ "x": "𝖝",
+ "y": "𝖞",
+ "z": "𝖟",
+ "A": "𝕬",
+ "B": "𝕭",
+ "C": "𝕮",
+ "D": "𝕺",
+ "E": "𝕰",
+ "F": "𝕱",
+ "G": "𝕲",
+ "H": "𝕳",
+ "I": "𝕴",
+ "J": "𝕵",
+ "K": "𝕶",
+ "L": "𝕷",
+ "M": "𝕸",
+ "N": "𝕹",
+ "O": "𝕺",
+ "P": "𝕻",
+ "Q": "𝕼",
+ "R": "𝕽",
+ "S": "𝕾",
+ "T": "𝕿",
+ "U": "𝖀",
+ "V": "𝖁",
+ "W": "𝖂",
+ "X": "𝖃",
+ "Y": "𝖄",
+ "Z": "𝖅",
+ }
+ for i, j in style.items():
+ text = text.replace(i, j)
+ return text
+
+ def cloud(text):
+ style = {
+ "a": "a͜͡",
+ "b": "b͜͡",
+ "c": "c͜͡",
+ "d": "d͜͡",
+ "e": "e͜͡",
+ "f": "f͜͡",
+ "g": "g͜͡",
+ "h": "h͜͡",
+ "i": "i͜͡",
+ "j": "j͜͡",
+ "k": "k͜͡",
+ "l": "l͜͡",
+ "m": "m͜͡",
+ "n": "n͜͡",
+ "o": "o͜͡",
+ "p": "p͜͡",
+ "q": "q͜͡",
+ "r": "r͜͡",
+ "s": "s͜͡",
+ "t": "t͜͡",
+ "u": "u͜͡",
+ "v": "v͜͡",
+ "w": "w͜͡",
+ "x": "x͜͡",
+ "y": "y͜͡",
+ "z": "z͜͡",
+ "A": "A͜͡",
+ "B": "B͜͡",
+ "C": "C͜͡",
+ "D": "D͜͡",
+ "E": "E͜͡",
+ "F": "F͜͡",
+ "G": "G͜͡",
+ "H": "H͜͡",
+ "I": "I͜͡",
+ "J": "J͜͡",
+ "K": "K͜͡",
+ "L": "L͜͡",
+ "M": "M͜͡",
+ "N": "N͜͡",
+ "O": "O͜͡",
+ "P": "P͜͡",
+ "Q": "Q͜͡",
+ "R": "R͜͡",
+ "S": "S͜͡",
+ "T": "T͜͡",
+ "U": "U͜͡",
+ "V": "V͜͡",
+ "W": "W͜͡",
+ "X": "X͜͡",
+ "Y": "Y͜͡",
+ "Z": "Z͜͡",
+ }
+ for i, j in style.items():
+ text = text.replace(i, j)
+ return text
+
+ def happy(text):
+ style = {
+ "a": "ă̈",
+ "b": "b̆̈",
+ "c": "c̆̈",
+ "d": "d̆̈",
+ "e": "ĕ̈",
+ "f": "f̆̈",
+ "g": "ğ̈",
+ "h": "h̆̈",
+ "i": "ĭ̈",
+ "j": "j̆̈",
+ "k": "k̆̈",
+ "l": "l̆̈",
+ "m": "m̆̈",
+ "n": "n̆̈",
+ "o": "ŏ̈",
+ "p": "p̆̈",
+ "q": "q̆̈",
+ "r": "r̆̈",
+ "s": "s̆̈",
+ "t": "t̆̈",
+ "u": "ŭ̈",
+ "v": "v̆̈",
+ "w": "w̆̈",
+ "x": "x̆̈",
+ "y": "y̆̈",
+ "z": "z̆̈",
+ "A": "Ă̈",
+ "B": "B̆̈",
+ "C": "C̆̈",
+ "D": "D̆̈",
+ "E": "Ĕ̈",
+ "F": "F̆̈",
+ "G": "Ğ̈",
+ "H": "H̆̈",
+ "I": "Ĭ̈",
+ "J": "J̆̈",
+ "K": "K̆̈",
+ "L": "L̆̈",
+ "M": "M̆̈",
+ "N": "N̆̈",
+ "O": "Ŏ̈",
+ "P": "P̆̈",
+ "Q": "Q̆̈",
+ "R": "R̆̈",
+ "S": "S̆̈",
+ "T": "T̆̈",
+ "U": "Ŭ̈",
+ "V": "V̆̈",
+ "W": "W̆̈",
+ "X": "X̆̈",
+ "Y": "Y̆̈",
+ "Z": "Z̆̈",
+ }
+ for i, j in style.items():
+ text = text.replace(i, j)
+ return text
+
+ def sad(text):
+ style = {
+ "a": "ȃ̈",
+ "b": "b̑̈",
+ "c": "c̑̈",
+ "d": "d̑̈",
+ "e": "ȇ̈",
+ "f": "f̑̈",
+ "g": "g̑̈",
+ "h": "h̑̈",
+ "i": "ȋ̈",
+ "j": "j̑̈",
+ "k": "k̑̈",
+ "l": "l̑̈",
+ "m": "m̑̈",
+ "n": "n̑̈",
+ "o": "ȏ̈",
+ "p": "p̑̈",
+ "q": "q̑̈",
+ "r": "ȓ̈",
+ "s": "s̑̈",
+ "t": "t̑̈",
+ "u": "ȗ̈",
+ "v": "v̑̈",
+ "w": "w̑̈",
+ "x": "x̑̈",
+ "y": "y̑̈",
+ "z": "z̑̈",
+ "A": "Ȃ̈",
+ "B": "B̑̈",
+ "C": "C̑̈",
+ "D": "D̑̈",
+ "E": "Ȇ̈",
+ "F": "F̑̈",
+ "G": "G̑̈",
+ "H": "H̑̈",
+ "I": "Ȋ̈",
+ "J": "J̑̈",
+ "K": "K̑̈",
+ "L": "L̑̈",
+ "M": "M̑̈",
+ "N": "N̑̈",
+ "O": "Ȏ̈",
+ "P": "P̑̈",
+ "Q": "Q̑̈",
+ "R": "Ȓ̈",
+ "S": "S̑̈",
+ "T": "T̑̈",
+ "U": "Ȗ̈",
+ "V": "V̑̈",
+ "W": "W̑̈",
+ "X": "X̑̈",
+ "Y": "Y̑̈",
+ "Z": "Z̑̈",
+ }
+ for i, j in style.items():
+ text = text.replace(i, j)
+ return text
+
+ def special(text):
+ style = {
+ "a": "🇦 ",
+ "b": "🇧 ",
+ "c": "🇨 ",
+ "d": "🇩 ",
+ "e": "🇪 ",
+ "f": "🇫 ",
+ "g": "🇬 ",
+ "h": "🇭 ",
+ "i": "🇮 ",
+ "j": "🇯 ",
+ "k": "🇰 ",
+ "l": "🇱 ",
+ "m": "🇲 ",
+ "n": "🇳 ",
+ "o": "🇴 ",
+ "p": "🇵 ",
+ "q": "🇶 ",
+ "r": "🇷 ",
+ "s": "🇸 ",
+ "t": "🇹 ",
+ "u": "🇺 ",
+ "v": "🇻 ",
+ "w": "🇼 ",
+ "x": "🇽 ",
+ "y": "🇾 ",
+ "z": "🇿 ",
+ "A": "🇦 ",
+ "B": "🇧 ",
+ "C": "🇨 ",
+ "D": "🇩 ",
+ "E": "🇪 ",
+ "F": "🇫 ",
+ "G": "🇬 ",
+ "H": "🇭 ",
+ "I": "🇮 ",
+ "J": "🇯 ",
+ "K": "🇰 ",
+ "L": "🇱 ",
+ "M": "🇲 ",
+ "N": "🇳 ",
+ "O": "🇴 ",
+ "P": "🇵 ",
+ "Q": "🇶 ",
+ "R": "🇷 ",
+ "S": "🇸 ",
+ "T": "🇹 ",
+ "U": "🇺 ",
+ "V": "🇻 ",
+ "W": "🇼 ",
+ "X": "🇽 ",
+ "Y": "🇾 ",
+ "Z": "🇿 ",
+ }
+ for i, j in style.items():
+ text = text.replace(i, j)
+ return text
+
+ def square(text):
+ style = {
+ "a": "🄰",
+ "b": "🄱",
+ "c": "🄲",
+ "d": "🄳",
+ "e": "🄴",
+ "f": "🄵",
+ "g": "🄶",
+ "h": "🄷",
+ "i": "🄸",
+ "j": "🄹",
+ "k": "🄺",
+ "l": "🄻",
+ "m": "🄼",
+ "n": "🄽",
+ "o": "🄾",
+ "p": "🄿",
+ "q": "🅀",
+ "r": "🅁",
+ "s": "🅂",
+ "t": "🅃",
+ "u": "🅄",
+ "v": "🅅",
+ "w": "🅆",
+ "x": "🅇",
+ "y": "🅈",
+ "z": "🅉",
+ "A": "🄰",
+ "B": "🄱",
+ "C": "🄲",
+ "D": "🄳",
+ "E": "🄴",
+ "F": "🄵",
+ "G": "🄶",
+ "H": "🄷",
+ "I": "🄸",
+ "J": "🄹",
+ "K": "🄺",
+ "L": "🄻",
+ "M": "🄼",
+ "N": "🄽",
+ "O": "🄾",
+ "P": "🄿",
+ "Q": "🅀",
+ "R": "🅁",
+ "S": "🅂",
+ "T": "🅃",
+ "U": "🅄",
+ "V": "🅅",
+ "W": "🅆",
+ "X": "🅇",
+ "Y": "🅈",
+ "Z": "🅉",
+ }
+ for i, j in style.items():
+ text = text.replace(i, j)
+ return text
+
+ def dark_square(text):
+ style = {
+ "a": "🅰︎",
+ "b": "🅱︎",
+ "c": "🅲︎",
+ "d": "🅳︎",
+ "e": "🅴︎",
+ "f": "🅵︎",
+ "g": "🅶︎",
+ "h": "🅷︎",
+ "i": "🅸︎",
+ "j": "🅹︎",
+ "k": "🅺︎",
+ "l": "🅻︎",
+ "m": "🅼︎",
+ "n": "🅽︎",
+ "o": "🅾︎",
+ "p": "🅿︎",
+ "q": "🆀︎",
+ "r": "🆁︎",
+ "s": "🆂︎",
+ "t": "🆃︎",
+ "u": "🆄︎",
+ "v": "🆅︎",
+ "w": "🆆︎",
+ "x": "🆇︎",
+ "y": "🆈︎",
+ "z": "🆉︎",
+ "A": "🅰︎",
+ "B": "🅱︎",
+ "C": "🅲︎",
+ "D": "🅳︎",
+ "E": "🅴︎",
+ "F": "🅵︎",
+ "G": "🅶︎",
+ "H": "🅷︎",
+ "I": "🅸︎",
+ "J": "🅹︎",
+ "K": "🅺︎",
+ "L": "🅻︎",
+ "M": "🅼︎",
+ "N": "🅽︎",
+ "O": "🅾︎",
+ "P": "🅿︎",
+ "Q": "🆀︎",
+ "R": "🆁︎",
+ "S": "🆂︎",
+ "T": "🆃︎",
+ "U": "🆄︎",
+ "V": "🆅︎",
+ "W": "🆆︎",
+ "X": "🆇︎",
+ "Y": "🆈︎",
+ "Z": "🆉︎",
+ }
+ for i, j in style.items():
+ text = text.replace(i, j)
+ return text
+
+ def andalucia(text):
+ style = {
+ "a": "ꪖ",
+ "b": "᥇",
+ "c": "ᥴ",
+ "d": "ᦔ",
+ "e": "ꫀ",
+ "f": "ᠻ",
+ "g": "ᧁ",
+ "h": "ꫝ",
+ "i": "𝓲",
+ "j": "𝓳",
+ "k": "𝘬",
+ "l": "ꪶ",
+ "m": "ꪑ",
+ "n": "ꪀ",
+ "o": "ꪮ",
+ "p": "ρ",
+ "q": "𝘲",
+ "r": "𝘳",
+ "s": "𝘴",
+ "t": "𝓽",
+ "u": "ꪊ",
+ "v": "ꪜ",
+ "w": "᭙",
+ "x": "᥊",
+ "y": "ꪗ",
+ "z": "ɀ",
+ "A": "ꪖ",
+ "B": "᥇",
+ "C": "ᥴ",
+ "D": "ᦔ",
+ "E": "ꫀ",
+ "F": "ᠻ",
+ "G": "ᧁ",
+ "H": "ꫝ",
+ "I": "𝓲",
+ "J": "𝓳",
+ "K": "𝘬",
+ "L": "ꪶ",
+ "M": "ꪑ",
+ "N": "ꪀ",
+ "O": "ꪮ",
+ "P": "ρ",
+ "Q": "𝘲",
+ "R": "𝘳",
+ "S": "𝘴",
+ "T": "𝓽",
+ "U": "ꪊ",
+ "V": "ꪜ",
+ "W": "᭙",
+ "X": "᥊",
+ "Y": "ꪗ",
+ "Z": "ɀ",
+ }
+ for i, j in style.items():
+ text = text.replace(i, j)
+ return text
+
+ def manga(text):
+ style = {
+ "a": "卂",
+ "b": "乃",
+ "c": "匚",
+ "d": "ᗪ",
+ "e": "乇",
+ "f": "千",
+ "g": "ᘜ",
+ "h": "卄",
+ "i": "|",
+ "j": "フ",
+ "k": "Ҝ",
+ "l": "ㄥ",
+ "m": "爪",
+ "n": "几",
+ "o": "ㄖ",
+ "p": "卩",
+ "q": "Ҩ",
+ "r": "尺",
+ "s": "丂",
+ "t": "ㄒ",
+ "u": "ㄩ",
+ "v": "ᐯ",
+ "w": "山",
+ "x": "乂",
+ "y": "ㄚ",
+ "z": "乙",
+ "A": "卂",
+ "B": "乃",
+ "C": "匚",
+ "D": "ᗪ",
+ "E": "乇",
+ "F": "千",
+ "G": "ᘜ",
+ "H": "卄",
+ "I": "|",
+ "J": "フ",
+ "K": "Ҝ",
+ "L": "ㄥ",
+ "M": "爪",
+ "N": "几",
+ "O": "ㄖ",
+ "P": "卩",
+ "Q": "Ҩ",
+ "R": "尺",
+ "S": "丂",
+ "T": "ㄒ",
+ "U": "ㄩ",
+ "V": "ᐯ",
+ "W": "山",
+ "X": "乂",
+ "Y": "ㄚ",
+ "Z": "乙",
+ }
+ for i, j in style.items():
+ text = text.replace(i, j)
+ return text
+
+ def stinky(text):
+ style = {
+ "a": "a̾",
+ "b": "b̾",
+ "c": "c̾",
+ "d": "d̾",
+ "e": "e̾",
+ "f": "f̾",
+ "g": "g̾",
+ "h": "h̾",
+ "i": "i̾",
+ "j": "j̾",
+ "k": "k̾",
+ "l": "l̾",
+ "m": "m̾",
+ "n": "n̾",
+ "o": "o̾",
+ "p": "p̾",
+ "q": "q̾",
+ "r": "r̾",
+ "s": "s̾",
+ "t": "t̾",
+ "u": "u̾",
+ "v": "v̾",
+ "w": "w̾",
+ "x": "x̾",
+ "y": "y̾",
+ "z": "z̾",
+ "A": "A̾",
+ "B": "B̾",
+ "C": "C̾",
+ "D": "D̾",
+ "E": "E̾",
+ "F": "F̾",
+ "G": "G̾",
+ "H": "H̾",
+ "I": "I̾",
+ "J": "J̾",
+ "K": "K̾",
+ "L": "L̾",
+ "M": "M̾",
+ "N": "N̾",
+ "O": "O̾",
+ "P": "P̾",
+ "Q": "Q̾",
+ "R": "R̾",
+ "S": "S̾",
+ "T": "T̾",
+ "U": "U̾",
+ "V": "V̾",
+ "W": "W̾",
+ "X": "X̾",
+ "Y": "Y̾",
+ "Z": "Z̾",
+ }
+ for i, j in style.items():
+ text = text.replace(i, j)
+ return text
+
+ def bubbles(text):
+ style = {
+ "a": "ḁͦ",
+ "b": "b̥ͦ",
+ "c": "c̥ͦ",
+ "d": "d̥ͦ",
+ "e": "e̥ͦ",
+ "f": "f̥ͦ",
+ "g": "g̥ͦ",
+ "h": "h̥ͦ",
+ "i": "i̥ͦ",
+ "j": "j̥ͦ",
+ "k": "k̥ͦ",
+ "l": "l̥ͦ",
+ "m": "m̥ͦ",
+ "n": "n̥ͦ",
+ "o": "o̥ͦ",
+ "p": "p̥ͦ",
+ "q": "q̥ͦ",
+ "r": "r̥ͦ",
+ "s": "s̥ͦ",
+ "t": "t̥ͦ",
+ "u": "u̥ͦ",
+ "v": "v̥ͦ",
+ "w": "w̥ͦ",
+ "x": "x̥ͦ",
+ "y": "y̥ͦ",
+ "z": "z̥ͦ",
+ "A": "Ḁͦ",
+ "B": "B̥ͦ",
+ "C": "C̥ͦ",
+ "D": "D̥ͦ",
+ "E": "E̥ͦ",
+ "F": "F̥ͦ",
+ "G": "G̥ͦ",
+ "H": "H̥ͦ",
+ "I": "I̥ͦ",
+ "J": "J̥ͦ",
+ "K": "K̥ͦ",
+ "L": "L̥ͦ",
+ "M": "M̥ͦ",
+ "N": "N̥ͦ",
+ "O": "O̥ͦ",
+ "P": "P̥ͦ",
+ "Q": "Q̥ͦ",
+ "R": "R̥ͦ",
+ "S": "S̥ͦ",
+ "T": "T̥ͦ",
+ "U": "U̥ͦ",
+ "V": "V̥ͦ",
+ "W": "W̥ͦ",
+ "X": "X̥ͦ",
+ "Y": "Y̥ͦ",
+ "Z": "Z̥ͦ",
+ }
+ for i, j in style.items():
+ text = text.replace(i, j)
+ return text
+
+ def underline(text):
+ style = {
+ "a": "a͟",
+ "b": "b͟",
+ "c": "c͟",
+ "d": "d͟",
+ "e": "e͟",
+ "f": "f͟",
+ "g": "g͟",
+ "h": "h͟",
+ "i": "i͟",
+ "j": "j͟",
+ "k": "k͟",
+ "l": "l͟",
+ "m": "m͟",
+ "n": "n͟",
+ "o": "o͟",
+ "p": "p͟",
+ "q": "q͟",
+ "r": "r͟",
+ "s": "s͟",
+ "t": "t͟",
+ "u": "u͟",
+ "v": "v͟",
+ "w": "w͟",
+ "x": "x͟",
+ "y": "y͟",
+ "z": "z͟",
+ "A": "A͟",
+ "B": "B͟",
+ "C": "C͟",
+ "D": "D͟",
+ "E": "E͟",
+ "F": "F͟",
+ "G": "G͟",
+ "H": "H͟",
+ "I": "I͟",
+ "J": "J͟",
+ "K": "K͟",
+ "L": "L͟",
+ "M": "M͟",
+ "N": "N͟",
+ "O": "O͟",
+ "P": "P͟",
+ "Q": "Q͟",
+ "R": "R͟",
+ "S": "S͟",
+ "T": "T͟",
+ "U": "U͟",
+ "V": "V͟",
+ "W": "W͟",
+ "X": "X͟",
+ "Y": "Y͟",
+ "Z": "Z͟",
+ }
+ for i, j in style.items():
+ text = text.replace(i, j)
+ return text
+
+ def ladybug(text):
+ style = {
+ "a": "ꍏ",
+ "b": "ꌃ",
+ "c": "ꏳ",
+ "d": "ꀷ",
+ "e": "ꏂ",
+ "f": "ꎇ",
+ "g": "ꁅ",
+ "h": "ꀍ",
+ "i": "ꀤ",
+ "j": "꒻",
+ "k": "ꀘ",
+ "l": "꒒",
+ "m": "ꎭ",
+ "n": "ꈤ",
+ "o": "ꂦ",
+ "p": "ᖘ",
+ "q": "ꆰ",
+ "r": "ꋪ",
+ "s": "ꌚ",
+ "t": "꓄",
+ "u": "ꀎ",
+ "v": "꒦",
+ "w": "ꅐ",
+ "x": "ꉧ",
+ "y": "ꌩ",
+ "z": "ꁴ",
+ "A": "ꍏ",
+ "B": "ꌃ",
+ "C": "ꏳ",
+ "D": "ꀷ",
+ "E": "ꏂ",
+ "F": "ꎇ",
+ "G": "ꁅ",
+ "H": "ꀍ",
+ "I": "ꀤ",
+ "J": "꒻",
+ "K": "ꀘ",
+ "L": "꒒",
+ "M": "ꎭ",
+ "N": "ꈤ",
+ "O": "ꂦ",
+ "P": "ᖘ",
+ "Q": "ꆰ",
+ "R": "ꋪ",
+ "S": "ꌚ",
+ "T": "꓄",
+ "U": "ꀎ",
+ "V": "꒦",
+ "W": "ꅐ",
+ "X": "ꉧ",
+ "Y": "ꌩ",
+ "Z": "ꁴ",
+ }
+ for i, j in style.items():
+ text = text.replace(i, j)
+ return text
+
+ def rays(text):
+ style = {
+ "a": "a҉",
+ "b": "b҉",
+ "c": "c҉",
+ "d": "d҉",
+ "e": "e҉",
+ "f": "f҉",
+ "g": "g҉",
+ "h": "h҉",
+ "i": "i҉",
+ "j": "j҉",
+ "k": "k҉",
+ "l": "l҉",
+ "m": "m҉",
+ "n": "n҉",
+ "o": "o҉",
+ "p": "p҉",
+ "q": "q҉",
+ "r": "r҉",
+ "s": "s҉",
+ "t": "t҉",
+ "u": "u҉",
+ "v": "v҉",
+ "w": "w҉",
+ "x": "x҉",
+ "y": "y҉",
+ "z": "z҉",
+ "A": "A҉",
+ "B": "B҉",
+ "C": "C҉",
+ "D": "D҉",
+ "E": "E҉",
+ "F": "F҉",
+ "G": "G҉",
+ "H": "H҉",
+ "I": "I҉",
+ "J": "J҉",
+ "K": "K҉",
+ "L": "L҉",
+ "M": "M҉",
+ "N": "N҉",
+ "O": "O҉",
+ "P": "P҉",
+ "Q": "Q҉",
+ "R": "R҉",
+ "S": "S҉",
+ "T": "T҉",
+ "U": "U҉",
+ "V": "V҉",
+ "W": "W҉",
+ "X": "X҉",
+ "Y": "Y҉",
+ "Z": "Z҉",
+ }
+ for i, j in style.items():
+ text = text.replace(i, j)
+ return text
+
+ def birds(text):
+ style = {
+ "a": "a҈",
+ "b": "b҈",
+ "c": "c҈",
+ "d": "d҈",
+ "e": "e҈",
+ "f": "f҈",
+ "g": "g҈",
+ "h": "h҈",
+ "i": "i҈",
+ "j": "j҈",
+ "k": "k҈",
+ "l": "l҈",
+ "m": "m҈",
+ "n": "n҈",
+ "o": "o҈",
+ "p": "p҈",
+ "q": "q҈",
+ "r": "r҈",
+ "s": "s҈",
+ "t": "t҈",
+ "u": "u҈",
+ "v": "v҈",
+ "w": "w҈",
+ "x": "x҈",
+ "y": "y҈",
+ "z": "z҈",
+ "A": "A҈",
+ "B": "B҈",
+ "C": "C҈",
+ "D": "D҈",
+ "E": "E҈",
+ "F": "F҈",
+ "G": "G҈",
+ "H": "H҈",
+ "I": "I҈",
+ "J": "J҈",
+ "K": "K҈",
+ "L": "L҈",
+ "M": "M҈",
+ "N": "N҈",
+ "O": "O҈",
+ "P": "P҈",
+ "Q": "Q҈",
+ "R": "R҈",
+ "S": "S҈",
+ "T": "T҈",
+ "U": "U҈",
+ "V": "V҈",
+ "W": "W҈",
+ "X": "X҈",
+ "Y": "Y҈",
+ "Z": "Z҈",
+ }
+ for i, j in style.items():
+ text = text.replace(i, j)
+ return text
+
+ def slash(text):
+ style = {
+ "a": "a̸",
+ "b": "b̸",
+ "c": "c̸",
+ "d": "d̸",
+ "e": "e̸",
+ "f": "f̸",
+ "g": "g̸",
+ "h": "h̸",
+ "i": "i̸",
+ "j": "j̸",
+ "k": "k̸",
+ "l": "l̸",
+ "m": "m̸",
+ "n": "n̸",
+ "o": "o̸",
+ "p": "p̸",
+ "q": "q̸",
+ "r": "r̸",
+ "s": "s̸",
+ "t": "t̸",
+ "u": "u̸",
+ "v": "v̸",
+ "w": "w̸",
+ "x": "x̸",
+ "y": "y̸",
+ "z": "z̸",
+ "A": "A̸",
+ "B": "B̸",
+ "C": "C̸",
+ "D": "D̸",
+ "E": "E̸",
+ "F": "F̸",
+ "G": "G̸",
+ "H": "H̸",
+ "I": "I̸",
+ "J": "J̸",
+ "K": "K̸",
+ "L": "L̸",
+ "M": "M̸",
+ "N": "N̸",
+ "O": "O̸",
+ "P": "P̸",
+ "Q": "Q̸",
+ "R": "R̸",
+ "S": "S̸",
+ "T": "T̸",
+ "U": "U̸",
+ "V": "V̸",
+ "W": "W̸",
+ "X": "X̸",
+ "Y": "Y̸",
+ "Z": "Z̸",
+ }
+ for i, j in style.items():
+ text = text.replace(i, j)
+ return text
+
+ def stop(text):
+ style = {
+ "a": "a⃠",
+ "b": "b⃠",
+ "c": "c⃠",
+ "d": "d⃠",
+ "e": "e⃠",
+ "f": "f⃠",
+ "g": "g⃠",
+ "h": "h⃠",
+ "i": "i⃠",
+ "j": "j⃠",
+ "k": "k⃠",
+ "l": "l⃠",
+ "m": "m⃠",
+ "n": "n⃠",
+ "o": "o⃠",
+ "p": "p⃠",
+ "q": "q⃠",
+ "r": "r⃠",
+ "s": "s⃠",
+ "t": "t⃠",
+ "u": "u⃠",
+ "v": "v⃠",
+ "w": "w⃠",
+ "x": "x⃠",
+ "y": "y⃠",
+ "z": "z⃠",
+ "A": "A⃠",
+ "B": "B⃠",
+ "C": "C⃠",
+ "D": "D⃠",
+ "E": "E⃠",
+ "F": "F⃠",
+ "G": "G⃠",
+ "H": "H⃠",
+ "I": "I⃠",
+ "J": "J⃠",
+ "K": "K⃠",
+ "L": "L⃠",
+ "M": "M⃠",
+ "N": "N⃠",
+ "O": "O⃠",
+ "P": "P⃠",
+ "Q": "Q⃠",
+ "R": "R⃠",
+ "S": "S⃠",
+ "T": "T⃠",
+ "U": "U⃠",
+ "V": "V⃠",
+ "W": "W⃠",
+ "X": "X⃠",
+ "Y": "Y⃠",
+ "Z": "Z⃠",
+ }
+ for i, j in style.items():
+ text = text.replace(i, j)
+ return text
+
+ def skyline(text):
+ style = {
+ "a": "a̺͆",
+ "b": "b̺͆",
+ "c": "c̺͆",
+ "d": "d̺͆",
+ "e": "e̺͆",
+ "f": "f̺͆",
+ "g": "g̺͆",
+ "h": "h̺͆",
+ "i": "i̺͆",
+ "j": "j̺͆",
+ "k": "k̺͆",
+ "l": "l̺͆",
+ "m": "m̺͆",
+ "n": "n̺͆",
+ "o": "o̺͆",
+ "p": "p̺͆",
+ "q": "q̺͆",
+ "r": "r̺͆",
+ "s": "s̺͆",
+ "t": "t̺͆",
+ "u": "u̺͆",
+ "v": "v̺͆",
+ "w": "w̺͆",
+ "x": "x̺͆",
+ "y": "y̺͆",
+ "z": "z̺͆",
+ "A": "A̺͆",
+ "B": "B̺͆",
+ "C": "C̺͆",
+ "D": "D̺͆",
+ "E": "E̺͆",
+ "F": "F̺͆",
+ "G": "G̺͆",
+ "H": "H̺͆",
+ "I": "I̺͆",
+ "J": "J̺͆",
+ "K": "K̺͆",
+ "L": "L̺͆",
+ "M": "M̺͆",
+ "N": "N̺͆",
+ "O": "O̺͆",
+ "P": "P̺͆",
+ "Q": "Q̺͆",
+ "R": "R̺͆",
+ "S": "S̺͆",
+ "T": "T̺͆",
+ "U": "U̺͆",
+ "V": "V̺͆",
+ "W": "W̺͆",
+ "X": "X̺͆",
+ "Y": "Y̺͆",
+ "Z": "Z̺͆",
+ }
+ for i, j in style.items():
+ text = text.replace(i, j)
+ return text
+
+ def arrows(text):
+ style = {
+ "a": "a͎",
+ "b": "b͎",
+ "c": "c͎",
+ "d": "d͎",
+ "e": "e͎",
+ "f": "f͎",
+ "g": "g͎",
+ "h": "h͎",
+ "i": "i͎",
+ "j": "j͎",
+ "k": "k͎",
+ "l": "l͎",
+ "m": "m͎",
+ "n": "n͎",
+ "o": "o͎",
+ "p": "p͎",
+ "q": "q͎",
+ "r": "r͎",
+ "s": "s͎",
+ "t": "t͎",
+ "u": "u͎",
+ "v": "v͎",
+ "w": "w͎",
+ "x": "x͎",
+ "y": "y͎",
+ "z": "z͎",
+ "A": "A͎",
+ "B": "B͎",
+ "C": "C͎",
+ "D": "D͎",
+ "E": "E͎",
+ "F": "F͎",
+ "G": "G͎",
+ "H": "H͎",
+ "I": "I͎",
+ "J": "J͎",
+ "K": "K͎",
+ "L": "L͎",
+ "M": "M͎",
+ "N": "N͎",
+ "O": "O͎",
+ "P": "P͎",
+ "Q": "Q͎",
+ "R": "R͎",
+ "S": "S͎",
+ "T": "T͎",
+ "U": "U͎",
+ "V": "V͎",
+ "W": "W͎",
+ "X": "X͎",
+ "Y": "Y͎",
+ "Z": "Z͎",
+ }
+ for i, j in style.items():
+ text = text.replace(i, j)
+ return text
+
+ def rvnes(text):
+ style = {
+ "a": "ል",
+ "b": "ጌ",
+ "c": "ር",
+ "d": "ዕ",
+ "e": "ቿ",
+ "f": "ቻ",
+ "g": "ኗ",
+ "h": "ዘ",
+ "i": "ጎ",
+ "j": "ጋ",
+ "k": "ጕ",
+ "l": "ረ",
+ "m": "ጠ",
+ "n": "ክ",
+ "o": "ዐ",
+ "p": "የ",
+ "q": "ዒ",
+ "r": "ዪ",
+ "s": "ነ",
+ "t": "ፕ",
+ "u": "ሁ",
+ "v": "ሀ",
+ "w": "ሠ",
+ "x": "ሸ",
+ "y": "ሃ",
+ "z": "ጊ",
+ "A": "ል",
+ "B": "ጌ",
+ "C": "ር",
+ "D": "ዕ",
+ "E": "ቿ",
+ "F": "ቻ",
+ "G": "ኗ",
+ "H": "ዘ",
+ "I": "ጎ",
+ "J": "ጋ",
+ "K": "ጕ",
+ "L": "ረ",
+ "M": "ጠ",
+ "N": "ክ",
+ "O": "ዐ",
+ "P": "የ",
+ "Q": "ዒ",
+ "R": "ዪ",
+ "S": "ነ",
+ "T": "ፕ",
+ "U": "ሁ",
+ "V": "ሀ",
+ "W": "ሠ",
+ "X": "ሸ",
+ "Y": "ሃ",
+ "Z": "ጊ",
+ }
+ for i, j in style.items():
+ text = text.replace(i, j)
+ return text
+
+ def strike(text):
+ style = {
+ "a": "a̶",
+ "b": "b̶",
+ "c": "c̶",
+ "d": "d̶",
+ "e": "e̶",
+ "f": "f̶",
+ "g": "g̶",
+ "h": "h̶",
+ "i": "i̶",
+ "j": "j̶",
+ "k": "k̶",
+ "l": "l̶",
+ "m": "m̶",
+ "n": "n̶",
+ "o": "o̶",
+ "p": "p̶",
+ "q": "q̶",
+ "r": "r̶",
+ "s": "s̶",
+ "t": "t̶",
+ "u": "u̶",
+ "v": "v̶",
+ "w": "w̶",
+ "x": "x̶",
+ "y": "y̶",
+ "z": "z̶",
+ "A": "A̶",
+ "B": "B̶",
+ "C": "C̶",
+ "D": "D̶",
+ "E": "E̶",
+ "F": "F̶",
+ "G": "G̶",
+ "H": "H̶",
+ "I": "I̶",
+ "J": "J̶",
+ "K": "K̶",
+ "L": "L̶",
+ "M": "M̶",
+ "N": "N̶",
+ "O": "O̶",
+ "P": "P̶",
+ "Q": "Q̶",
+ "R": "R̶",
+ "S": "S̶",
+ "T": "T̶",
+ "U": "U̶",
+ "V": "V̶",
+ "W": "W̶",
+ "X": "X̶",
+ "Y": "Y̶",
+ "Z": "Z̶",
+ }
+ for i, j in style.items():
+ text = text.replace(i, j)
+ return text
+
+ def frozen(text):
+ style = {
+ "a": "a༙",
+ "b": "b༙",
+ "c": "c༙",
+ "d": "d༙",
+ "e": "e༙",
+ "f": "f༙",
+ "g": "g༙",
+ "h": "h༙",
+ "i": "i༙",
+ "j": "j༙",
+ "k": "k༙",
+ "l": "l༙",
+ "m": "m༙",
+ "n": "n༙",
+ "o": "o༙",
+ "p": "p༙",
+ "q": "q༙",
+ "r": "r༙",
+ "s": "s༙",
+ "t": "t༙",
+ "u": "u༙",
+ "v": "v༙",
+ "w": "w༙",
+ "x": "x༙",
+ "y": "y༙",
+ "z": "z༙",
+ "A": "A༙",
+ "B": "B༙",
+ "C": "C༙",
+ "D": "D༙",
+ "E": "E༙",
+ "F": "F༙",
+ "G": "G༙",
+ "H": "H༙",
+ "I": "I༙",
+ "J": "J༙",
+ "K": "K༙",
+ "L": "L༙",
+ "M": "M༙",
+ "N": "N༙",
+ "O": "O༙",
+ "P": "P༙",
+ "Q": "Q༙",
+ "R": "R༙",
+ "S": "S༙",
+ "T": "T༙",
+ "U": "U༙",
+ "V": "V༙",
+ "W": "W༙",
+ "X": "X༙",
+ "Y": "Y༙",
+ "Z": "Z༙",
+ }
+ for i, j in style.items():
+ text = text.replace(i, j)
+ return text
\ No newline at end of file
diff --git a/SprotifyMusic/utils/formatters.py b/SprotifyMusic/utils/formatters.py
new file mode 100644
index 000000000000..c0077e5ca959
--- /dev/null
+++ b/SprotifyMusic/utils/formatters.py
@@ -0,0 +1,185 @@
+import json
+import subprocess
+
+
+def get_readable_time(seconds: int) -> str:
+ count = 0
+ ping_time = ""
+ time_list = []
+ time_suffix_list = ["s", "ᴍ", "ʜ", "ᴅᴀʏs"]
+ 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 i in range(len(time_list)):
+ time_list[i] = str(time_list[i]) + time_suffix_list[i]
+ if len(time_list) == 4:
+ ping_time += time_list.pop() + ", "
+ time_list.reverse()
+ ping_time += ":".join(time_list)
+ return ping_time
+
+
+def convert_bytes(size: float) -> str:
+ """humanize size"""
+ if not size:
+ return ""
+ power = 1024
+ t_n = 0
+ power_dict = {0: " ", 1: "Ki", 2: "Mi", 3: "Gi", 4: "Ti"}
+ while size > power:
+ size /= power
+ t_n += 1
+ return "{:.2f} {}B".format(size, power_dict[t_n])
+
+
+async def int_to_alpha(user_id: int) -> str:
+ alphabet = ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j"]
+ text = ""
+ user_id = str(user_id)
+ for i in user_id:
+ text += alphabet[int(i)]
+ return text
+
+
+async def alpha_to_int(user_id_alphabet: str) -> int:
+ alphabet = ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j"]
+ user_id = ""
+ for i in user_id_alphabet:
+ index = alphabet.index(i)
+ user_id += str(index)
+ user_id = int(user_id)
+ return user_id
+
+
+def time_to_seconds(time):
+ stringt = str(time)
+ return sum(int(x) * 60**i for i, x in enumerate(reversed(stringt.split(":"))))
+
+
+def seconds_to_min(seconds):
+ if seconds is not None:
+ seconds = int(seconds)
+ d, h, m, s = (
+ seconds // (3600 * 24),
+ seconds // 3600 % 24,
+ seconds % 3600 // 60,
+ seconds % 3600 % 60,
+ )
+ if d > 0:
+ return "{:02d}:{:02d}:{:02d}:{:02d}".format(d, h, m, s)
+ elif h > 0:
+ return "{:02d}:{:02d}:{:02d}".format(h, m, s)
+ elif m > 0:
+ return "{:02d}:{:02d}".format(m, s)
+ elif s > 0:
+ return "00:{:02d}".format(s)
+ return "-"
+
+
+def speed_converter(seconds, speed):
+ if str(speed) == str("0.5"):
+ seconds = seconds * 2
+ if str(speed) == str("0.75"):
+ seconds = seconds + ((50 * seconds) // 100)
+ if str(speed) == str("1.5"):
+ seconds = seconds - ((25 * seconds) // 100)
+ if str(speed) == str("2.0"):
+ seconds = seconds - ((50 * seconds) // 100)
+ collect = seconds
+ if seconds is not None:
+ seconds = int(seconds)
+ d, h, m, s = (
+ seconds // (3600 * 24),
+ seconds // 3600 % 24,
+ seconds % 3600 // 60,
+ seconds % 3600 % 60,
+ )
+ if d > 0:
+ convert = "{:02d}:{:02d}:{:02d}:{:02d}".format(d, h, m, s)
+ return convert, collect
+ elif h > 0:
+ convert = "{:02d}:{:02d}:{:02d}".format(h, m, s)
+ return convert, collect
+ elif m > 0:
+ convert = "{:02d}:{:02d}".format(m, s)
+ return convert, collect
+ elif s > 0:
+ convert = "00:{:02d}".format(s)
+ return convert, collect
+ return "-"
+
+
+def check_duration(file_path):
+ command = [
+ "ffprobe",
+ "-loglevel",
+ "quiet",
+ "-print_format",
+ "json",
+ "-show_format",
+ "-show_streams",
+ file_path,
+ ]
+
+ pipe = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
+ out, err = pipe.communicate()
+ _json = json.loads(out)
+
+ if "format" in _json:
+ if "duration" in _json["format"]:
+ return float(_json["format"]["duration"])
+
+ if "streams" in _json:
+ for s in _json["streams"]:
+ if "duration" in s:
+ return float(s["duration"])
+
+ return "Unknown"
+
+
+formats = [
+ "webm",
+ "mkv",
+ "flv",
+ "vob",
+ "ogv",
+ "ogg",
+ "rrc",
+ "gifv",
+ "mng",
+ "mov",
+ "avi",
+ "qt",
+ "wmv",
+ "yuv",
+ "rm",
+ "asf",
+ "amv",
+ "mp4",
+ "m4p",
+ "m4v",
+ "mpg",
+ "mp2",
+ "mpeg",
+ "mpe",
+ "mpv",
+ "m4v",
+ "svi",
+ "3gp",
+ "3g2",
+ "mxf",
+ "roq",
+ "nsv",
+ "flv",
+ "f4v",
+ "f4p",
+ "f4a",
+ "f4b",
+]
diff --git a/SprotifyMusic/utils/inline/__init__.py b/SprotifyMusic/utils/inline/__init__.py
new file mode 100644
index 000000000000..0e5871b91d51
--- /dev/null
+++ b/SprotifyMusic/utils/inline/__init__.py
@@ -0,0 +1,7 @@
+from .extras import *
+from .help import *
+from .play import *
+from .queue import *
+from .settings import *
+from .speed import *
+from .start import *
diff --git a/SprotifyMusic/utils/inline/extras.py b/SprotifyMusic/utils/inline/extras.py
new file mode 100644
index 000000000000..9d99b133679e
--- /dev/null
+++ b/SprotifyMusic/utils/inline/extras.py
@@ -0,0 +1,41 @@
+from pyrogram.types import InlineKeyboardButton, InlineKeyboardMarkup
+
+from config import SUPPORT_CHAT
+
+
+def botplaylist_markup(_):
+ buttons = [
+ [
+ InlineKeyboardButton(text=_["S_B_9"], url=SUPPORT_CHAT),
+ InlineKeyboardButton(text=_["CLOSE_BUTTON"], callback_data="close"),
+ ],
+ ]
+ return buttons
+
+
+def close_markup(_):
+ upl = InlineKeyboardMarkup(
+ [
+ [
+ InlineKeyboardButton(
+ text=_["CLOSE_BUTTON"],
+ callback_data="close",
+ ),
+ ]
+ ]
+ )
+ return upl
+
+
+def supp_markup(_):
+ upl = InlineKeyboardMarkup(
+ [
+ [
+ InlineKeyboardButton(
+ text=_["S_B_9"],
+ url=SUPPORT_CHAT,
+ ),
+ ]
+ ]
+ )
+ return upl
diff --git a/SprotifyMusic/utils/inline/help.py b/SprotifyMusic/utils/inline/help.py
new file mode 100644
index 000000000000..075860bb4dbd
--- /dev/null
+++ b/SprotifyMusic/utils/inline/help.py
@@ -0,0 +1,90 @@
+from typing import Union
+
+from pyrogram.types import InlineKeyboardButton, InlineKeyboardMarkup
+
+from SprotifyMusic import app
+
+
+def help_pannel(_, START: Union[bool, int] = None):
+ first = [InlineKeyboardButton(text=_["CLOSE_BUTTON"], callback_data=f"close")]
+ second = [
+ InlineKeyboardButton(
+ text=_["BACK_BUTTON"],
+ callback_data=f"settingsback_helper",
+ ),
+ ]
+ mark = second if START else first
+ upl = InlineKeyboardMarkup(
+ [
+ [
+ InlineKeyboardButton(
+ text=_["H_B_1"],
+ callback_data="help_callback hb1",
+ ),
+ InlineKeyboardButton(
+ text=_["H_B_2"],
+ callback_data="help_callback hb2",
+ ),
+ InlineKeyboardButton(
+ text=_["H_B_3"],
+ callback_data="help_callback hb3",
+ ),
+ ],
+ [
+ InlineKeyboardButton(
+ text=_["H_B_4"],
+ callback_data="help_callback hb4",
+ ),
+ InlineKeyboardButton(
+ text=_["H_B_5"],
+ callback_data="help_callback hb5",
+ ),
+ InlineKeyboardButton(
+ text=_["H_B_6"],
+ callback_data="help_callback hb6",
+ ),
+ ],
+ [
+ InlineKeyboardButton(
+ text=_["H_B_7"],
+ callback_data="help_callback hb7",
+ ),
+ InlineKeyboardButton(
+ text=_["H_B_8"],
+ callback_data="help_callback hb8",
+ ),
+ InlineKeyboardButton(
+ text=_["H_B_9"],
+ callback_data="help_callback hb9",
+ ),
+ ],
+ mark,
+ ]
+ )
+ return upl
+
+
+def help_back_markup(_):
+ upl = InlineKeyboardMarkup(
+ [
+ [
+ InlineKeyboardButton(
+ text=_["BACK_BUTTON"],
+ callback_data=f"settings_back_helper",
+ ),
+ ]
+ ]
+ )
+ return upl
+
+
+def private_help_panel(_):
+ buttons = [
+ [
+ InlineKeyboardButton(
+ text=_["S_B_4"],
+ url=f"https://t.me/{app.username}?start=help",
+ ),
+ ],
+ ]
+ return buttons
\ No newline at end of file
diff --git a/SprotifyMusic/utils/inline/play.py b/SprotifyMusic/utils/inline/play.py
new file mode 100644
index 000000000000..ade5282a7428
--- /dev/null
+++ b/SprotifyMusic/utils/inline/play.py
@@ -0,0 +1,154 @@
+import math
+from config import SUPPORT_CHAT, OWNER_USERNAME
+from pyrogram.types import InlineKeyboardButton
+from SprotifyMusic.utils.formatters import time_to_seconds
+
+
+def track_markup(_, videoid, user_id, channel, fplay):
+ buttons = [
+ [
+ InlineKeyboardButton(
+ text=_["P_B_1"],
+ callback_data=f"MusicStream {videoid}|{user_id}|a|{channel}|{fplay}",
+ ),
+ InlineKeyboardButton(
+ text=_["P_B_2"],
+ callback_data=f"MusicStream {videoid}|{user_id}|v|{channel}|{fplay}",
+ ),
+ ],
+ [
+ InlineKeyboardButton(
+ text=_["CLOSE_BUTTON"],
+ callback_data=f"forceclose {videoid}|{user_id}",
+ )
+ ],
+ ]
+ return buttons
+
+
+def stream_markup_timer(_, chat_id, played, dur):
+ played_sec = time_to_seconds(played)
+ duration_sec = time_to_seconds(dur)
+ percentage = (played_sec / duration_sec) * 100
+ umm = math.floor(percentage)
+ if 0 < umm <= 10:
+ bar = "◉—————————"
+ elif 10 < umm < 20:
+ bar = "—◉————————"
+ elif 20 <= umm < 30:
+ bar = "——◉———————"
+ elif 30 <= umm < 40:
+ bar = "———◉——————"
+ elif 40 <= umm < 50:
+ bar = "————◉—————"
+ elif 50 <= umm < 60:
+ bar = "—————◉————"
+ elif 60 <= umm < 70:
+ bar = "——————◉———"
+ elif 70 <= umm < 80:
+ bar = "———————◉——"
+ elif 80 <= umm < 95:
+ bar = "————————◉—"
+ else:
+ bar = "—————————◉"
+ buttons = [
+ [
+ InlineKeyboardButton(
+ text=f"{played} {bar} {dur}",
+ callback_data="GetTimer",
+ )
+ ],
+ [
+ InlineKeyboardButton(text="◁", callback_data=f"ADMIN Resume|{chat_id}"),
+ InlineKeyboardButton(text="၊၊", callback_data=f"ADMIN Pause|{chat_id}"),
+ InlineKeyboardButton(text="▷", callback_data=f"ADMIN Skip|{chat_id}"),
+ InlineKeyboardButton(text="▢", callback_data=f"ADMIN Stop|{chat_id}"),
+ ],
+ [
+ InlineKeyboardButton(text=_["CLOSE_BUTTON"], callback_data="close")],
+ ]
+ return buttons
+
+
+def stream_markup(_, chat_id):
+ buttons = [
+ [
+ InlineKeyboardButton(text="◁", callback_data=f"ADMIN Resume|{chat_id}"),
+ InlineKeyboardButton(text="၊၊", callback_data=f"ADMIN Pause|{chat_id}"),
+ InlineKeyboardButton(text="▷", callback_data=f"ADMIN Skip|{chat_id}"),
+ InlineKeyboardButton(text="▢", callback_data=f"ADMIN Stop|{chat_id}"),
+ ],
+ [InlineKeyboardButton(text=_["CLOSE_BUTTON"], callback_data="close")],
+ ]
+ return buttons
+
+
+def playlist_markup(_, videoid, user_id, ptype, channel, fplay):
+ buttons = [
+ [
+ InlineKeyboardButton(
+ text=_["P_B_1"],
+ callback_data=f"AnonyPlaylists {videoid}|{user_id}|{ptype}|a|{channel}|{fplay}",
+ ),
+ InlineKeyboardButton(
+ text=_["P_B_2"],
+ callback_data=f"AnonyPlaylists {videoid}|{user_id}|{ptype}|v|{channel}|{fplay}",
+ ),
+ ],
+ [
+ InlineKeyboardButton(
+ text=_["CLOSE_BUTTON"],
+ callback_data=f"forceclose {videoid}|{user_id}",
+ ),
+ ],
+ ]
+ return buttons
+
+
+def livestream_markup(_, videoid, user_id, mode, channel, fplay):
+ buttons = [
+ [
+ InlineKeyboardButton(
+ text=_["P_B_3"],
+ callback_data=f"LiveStream {videoid}|{user_id}|{mode}|{channel}|{fplay}",
+ ),
+ ],
+ [
+ InlineKeyboardButton(
+ text=_["CLOSE_BUTTON"],
+ callback_data=f"forceclose {videoid}|{user_id}",
+ ),
+ ],
+ ]
+ return buttons
+
+
+def slider_markup(_, videoid, user_id, query, query_type, channel, fplay):
+ query = f"{query[:20]}"
+ buttons = [
+ [
+ InlineKeyboardButton(
+ text=_["P_B_1"],
+ callback_data=f"MusicStream {videoid}|{user_id}|a|{channel}|{fplay}",
+ ),
+ InlineKeyboardButton(
+ text=_["P_B_2"],
+ callback_data=f"MusicStream {videoid}|{user_id}|v|{channel}|{fplay}",
+ ),
+ ],
+ [
+ InlineKeyboardButton(
+ text="◁",
+ callback_data=f"slider B|{query_type}|{query}|{user_id}|{channel}|{fplay}",
+ ),
+ InlineKeyboardButton(
+ text=_["CLOSE_BUTTON"],
+ callback_data=f"forceclose {query}|{user_id}",
+ ),
+ InlineKeyboardButton(
+ text="▷",
+ callback_data=f"slider F|{query_type}|{query}|{user_id}|{channel}|{fplay}",
+ ),
+ ],
+ ]
+ return buttons
\ No newline at end of file
diff --git a/SprotifyMusic/utils/inline/playlist.py b/SprotifyMusic/utils/inline/playlist.py
new file mode 100644
index 000000000000..3cb6c9790ecc
--- /dev/null
+++ b/SprotifyMusic/utils/inline/playlist.py
@@ -0,0 +1,166 @@
+#
+# Copyright (C) 2021-2022 by TeamYukki@Github, < https://github.com/TeamYukki >.
+#
+# This file is part of < https://github.com/TeamYukki/YukkiMusicBot > project,
+# and is released under the "GNU v3.0 License Agreement".
+# Please see < https://github.com/TeamYukki/YukkiMusicBot/blob/master/LICENSE >
+#
+# All rights reserved.
+
+from pyrogram.types import InlineKeyboardButton, InlineKeyboardMarkup
+
+
+def botplaylist_markup(_):
+ buttons = [
+ [
+ InlineKeyboardButton(
+ text=_["PL_B_1"],
+ callback_data="get_playlist_playmode",
+ ),
+ InlineKeyboardButton(
+ text=_["PL_B_8"], callback_data="get_top_playlists"
+ ),
+ ],
+ [
+ InlineKeyboardButton(
+ text=_["PL_B_4"], callback_data="PM"
+ ),
+ InlineKeyboardButton(
+ text=_["CLOSE_BUTTON"], callback_data="close"
+ ),
+ ],
+ ]
+ return buttons
+
+
+def top_play_markup(_):
+ buttons = [
+ [
+ InlineKeyboardButton(
+ text=_["PL_B_9"], callback_data="SERVERTOP global"
+ )
+ ],
+ [
+ InlineKeyboardButton(
+ text=_["PL_B_10"], callback_data="SERVERTOP chat"
+ )
+ ],
+ [
+ InlineKeyboardButton(
+ text=_["PL_B_11"], callback_data="SERVERTOP user"
+ )
+ ],
+ [
+ InlineKeyboardButton(
+ text=_["BACK_BUTTON"], callback_data="get_playmarkup"
+ ),
+ InlineKeyboardButton(
+ text=_["CLOSE_BUTTON"], callback_data="close"
+ ),
+ ],
+ ]
+ return buttons
+
+
+def get_playlist_markup(_):
+ buttons = [
+ [
+ InlineKeyboardButton(
+ text=_["P_B_1"], callback_data="play_playlist a"
+ ),
+ InlineKeyboardButton(
+ text=_["P_B_2"], callback_data="play_playlist b"
+ ),
+ ],
+ [
+ InlineKeyboardButton(
+ text=_["BACK_BUTTON"], callback_data="home_play"
+ ),
+ InlineKeyboardButton(
+ text=_["CLOSE_BUTTON"], callback_data="close"
+ ),
+ ],
+ ]
+ return buttons
+
+
+def top_play_markup(_):
+ buttons = [
+ [
+ InlineKeyboardButton(
+ text=_["PL_B_9"], callback_data="SERVERTOP Global"
+ )
+ ],
+ [
+ InlineKeyboardButton(
+ text=_["PL_B_10"], callback_data="SERVERTOP Group"
+ )
+ ],
+ [
+ InlineKeyboardButton(
+ text=_["PL_B_11"], callback_data="SERVERTOP Personal"
+ )
+ ],
+ [
+ InlineKeyboardButton(
+ text=_["BACK_BUTTON"], callback_data="get_playmarkup"
+ ),
+ InlineKeyboardButton(
+ text=_["CLOSE_BUTTON"], callback_data="close"
+ ),
+ ],
+ ]
+ return buttons
+
+
+def failed_top_markup(_):
+ buttons = [
+ [
+ InlineKeyboardButton(
+ text=_["BACK_BUTTON"],
+ callback_data="get_top_playlists",
+ ),
+ InlineKeyboardButton(
+ text=_["CLOSE_BUTTON"], callback_data="close"
+ ),
+ ],
+ ]
+ return buttons
+
+
+def warning_markup(_):
+ upl = InlineKeyboardMarkup(
+ [
+ [
+ InlineKeyboardButton(
+ text=_["PL_B_7"],
+ callback_data="delete_whole_playlist",
+ ),
+ ],
+ [
+ InlineKeyboardButton(
+ text=_["BACK_BUTTON"],
+ callback_data="del_back_playlist",
+ ),
+ InlineKeyboardButton(
+ text=_["CLOSE_BUTTON"],
+ callback_data="close",
+ ),
+ ],
+ ]
+ )
+ return upl
+
+
+def close_markup(_):
+ upl = InlineKeyboardMarkup(
+ [
+ [
+ InlineKeyboardButton(
+ text=_["CLOSE_BUTTON"],
+ callback_data="close",
+ ),
+ ]
+ ]
+ )
+ return upl
\ No newline at end of file
diff --git a/SprotifyMusic/utils/inline/queue.py b/SprotifyMusic/utils/inline/queue.py
new file mode 100644
index 000000000000..164e9b1b6d12
--- /dev/null
+++ b/SprotifyMusic/utils/inline/queue.py
@@ -0,0 +1,75 @@
+from typing import Union
+
+from pyrogram.types import InlineKeyboardButton, InlineKeyboardMarkup
+
+
+def queue_markup(
+ _,
+ DURATION,
+ CPLAY,
+ videoid,
+ played: Union[bool, int] = None,
+ dur: Union[bool, int] = None,
+):
+ not_dur = [
+ [
+ InlineKeyboardButton(
+ text=_["QU_B_1"],
+ callback_data=f"GetQueued {CPLAY}|{videoid}",
+ ),
+ InlineKeyboardButton(
+ text=_["CLOSE_BUTTON"],
+ callback_data="close",
+ ),
+ ]
+ ]
+ dur = [
+ [
+ InlineKeyboardButton(
+ text=_["QU_B_2"].format(played, dur),
+ callback_data="GetTimer",
+ )
+ ],
+ [
+ InlineKeyboardButton(
+ text=_["QU_B_1"],
+ callback_data=f"GetQueued {CPLAY}|{videoid}",
+ ),
+ InlineKeyboardButton(
+ text=_["CLOSE_BUTTON"],
+ callback_data="close",
+ ),
+ ],
+ ]
+ upl = InlineKeyboardMarkup(not_dur if DURATION == "Unknown" else dur)
+ return upl
+
+
+def queue_back_markup(_, CPLAY):
+ upl = InlineKeyboardMarkup(
+ [
+ [
+ InlineKeyboardButton(
+ text=_["BACK_BUTTON"],
+ callback_data=f"queue_back_timer {CPLAY}",
+ ),
+ InlineKeyboardButton(
+ text=_["CLOSE_BUTTON"],
+ callback_data="close",
+ ),
+ ]
+ ]
+ )
+ return upl
+
+
+def aq_markup(_, chat_id):
+ buttons = [
+ [
+ InlineKeyboardButton(
+ text=_["CLOSE_BUTTON"],
+ callback_data="close",
+ ),
+ ]
+ ]
+ return buttons
diff --git a/SprotifyMusic/utils/inline/settings.py b/SprotifyMusic/utils/inline/settings.py
new file mode 100644
index 000000000000..dcf3975d677b
--- /dev/null
+++ b/SprotifyMusic/utils/inline/settings.py
@@ -0,0 +1,110 @@
+from typing import Union
+
+from pyrogram.types import InlineKeyboardButton
+
+
+def setting_markup(_):
+ buttons = [
+ [
+ InlineKeyboardButton(text=_["ST_B_1"], callback_data="AU"),
+ InlineKeyboardButton(text=_["ST_B_3"], callback_data="LG"),
+ ],
+ [
+ InlineKeyboardButton(text=_["ST_B_2"], callback_data="PM"),
+ ],
+ [
+ InlineKeyboardButton(text=_["ST_B_4"], callback_data="VM"),
+ InlineKeyboardButton(text=_["CLOSE_BUTTON"], callback_data="close"),
+ ],
+ ]
+ return buttons
+
+
+def vote_mode_markup(_, current, mode: Union[bool, str] = None):
+ buttons = [
+ [
+ InlineKeyboardButton(text="ᴠᴏᴛɪɴɢ ᴍᴏᴅᴇ ➤", callback_data="VOTEANSWER"),
+ InlineKeyboardButton(
+ text=_["ST_B_5"] if mode == True else _["ST_B_6"],
+ callback_data="VOMODECHANGE",
+ ),
+ ],
+ [
+ InlineKeyboardButton(text="-2", callback_data="FERRARIUDTI M"),
+ InlineKeyboardButton(
+ text=f"ᴄᴜʀʀᴇɴᴛ ➣ {current}",
+ callback_data="ANSWERVOMODE",
+ ),
+ InlineKeyboardButton(text="+2", callback_data="FERRARIUDTI A"),
+ ],
+ [
+ InlineKeyboardButton(
+ text=_["BACK_BUTTON"],
+ callback_data="settings_helper",
+ ),
+ InlineKeyboardButton(text=_["CLOSE_BUTTON"], callback_data="close"),
+ ],
+ ]
+ return buttons
+
+
+def auth_users_markup(_, status: Union[bool, str] = None):
+ buttons = [
+ [
+ InlineKeyboardButton(text=_["ST_B_7"], callback_data="AUTHANSWER"),
+ InlineKeyboardButton(
+ text=_["ST_B_8"] if status == True else _["ST_B_9"],
+ callback_data="AUTH",
+ ),
+ ],
+ [
+ InlineKeyboardButton(text=_["ST_B_1"], callback_data="AUTHLIST"),
+ ],
+ [
+ InlineKeyboardButton(
+ text=_["BACK_BUTTON"],
+ callback_data="settings_helper",
+ ),
+ InlineKeyboardButton(text=_["CLOSE_BUTTON"], callback_data="close"),
+ ],
+ ]
+ return buttons
+
+
+def playmode_users_markup(
+ _,
+ Direct: Union[bool, str] = None,
+ Group: Union[bool, str] = None,
+ Playtype: Union[bool, str] = None,
+):
+ buttons = [
+ [
+ InlineKeyboardButton(text=_["ST_B_10"], callback_data="SEARCHANSWER"),
+ InlineKeyboardButton(
+ text=_["ST_B_11"] if Direct == True else _["ST_B_12"],
+ callback_data="MODECHANGE",
+ ),
+ ],
+ [
+ InlineKeyboardButton(text=_["ST_B_13"], callback_data="AUTHANSWER"),
+ InlineKeyboardButton(
+ text=_["ST_B_8"] if Group == True else _["ST_B_9"],
+ callback_data="CHANNELMODECHANGE",
+ ),
+ ],
+ [
+ InlineKeyboardButton(text=_["ST_B_14"], callback_data="PLAYTYPEANSWER"),
+ InlineKeyboardButton(
+ text=_["ST_B_8"] if Playtype == True else _["ST_B_9"],
+ callback_data="PLAYTYPECHANGE",
+ ),
+ ],
+ [
+ InlineKeyboardButton(
+ text=_["BACK_BUTTON"],
+ callback_data="settings_helper",
+ ),
+ InlineKeyboardButton(text=_["CLOSE_BUTTON"], callback_data="close"),
+ ],
+ ]
+ return buttons
\ No newline at end of file
diff --git a/SprotifyMusic/utils/inline/speed.py b/SprotifyMusic/utils/inline/speed.py
new file mode 100644
index 000000000000..25c2429b8c6f
--- /dev/null
+++ b/SprotifyMusic/utils/inline/speed.py
@@ -0,0 +1,41 @@
+from pyrogram.types import InlineKeyboardButton, InlineKeyboardMarkup
+
+
+def speed_markup(_, chat_id):
+ upl = InlineKeyboardMarkup(
+ [
+ [
+ InlineKeyboardButton(
+ text="🕒 0.5x",
+ callback_data=f"SpeedUP {chat_id}|0.5",
+ ),
+ InlineKeyboardButton(
+ text="🕓 0.75x",
+ callback_data=f"SpeedUP {chat_id}|0.75",
+ ),
+ ],
+ [
+ InlineKeyboardButton(
+ text=_["P_B_4"],
+ callback_data=f"SpeedUP {chat_id}|1.0",
+ ),
+ ],
+ [
+ InlineKeyboardButton(
+ text="🕤 1.5x",
+ callback_data=f"SpeedUP {chat_id}|1.5",
+ ),
+ InlineKeyboardButton(
+ text="🕛 2.0x",
+ callback_data=f"SpeedUP {chat_id}|2.0",
+ ),
+ ],
+ [
+ InlineKeyboardButton(
+ text=_["CLOSE_BUTTON"],
+ callback_data="close",
+ ),
+ ],
+ ]
+ )
+ return upl
diff --git a/SprotifyMusic/utils/inline/start.py b/SprotifyMusic/utils/inline/start.py
new file mode 100644
index 000000000000..36c323c9c4c0
--- /dev/null
+++ b/SprotifyMusic/utils/inline/start.py
@@ -0,0 +1,33 @@
+from pyrogram.types import InlineKeyboardButton
+
+import config
+from SprotifyMusic import app
+
+
+def start_panel(_):
+ buttons = [
+ [
+ InlineKeyboardButton(
+ text=_["S_B_1"], url=f"https://t.me/{app.username}?startgroup=true"
+ ),
+ InlineKeyboardButton(text=_["S_B_2"], url=config.SUPPORT_CHAT),
+ ],
+ ]
+ return buttons
+
+
+def private_panel(_):
+ buttons = [
+ [
+ InlineKeyboardButton(
+ text=_["S_B_3"],
+ url=f"https://t.me/{app.username}?startgroup=true",
+ )
+ ],
+ [
+ InlineKeyboardButton(text=_["S_B_5"], user_id=config.OWNER_ID),
+ InlineKeyboardButton(text=_["S_B_2"], url=config.SUPPORT_CHAT),
+ ],
+ [InlineKeyboardButton(text=_["S_B_4"], callback_data="settings_back_helper")],
+ ]
+ return buttons
diff --git a/SprotifyMusic/utils/inline/stats.py b/SprotifyMusic/utils/inline/stats.py
new file mode 100644
index 000000000000..8bed88089ef6
--- /dev/null
+++ b/SprotifyMusic/utils/inline/stats.py
@@ -0,0 +1,50 @@
+from pyrogram.types import InlineKeyboardButton, InlineKeyboardMarkup
+
+
+def stats_buttons(_, status):
+ not_sudo = [
+ InlineKeyboardButton(
+ text=_["SA_B_1"],
+ callback_data="TopOverall",
+ )
+ ]
+ sudo = [
+ InlineKeyboardButton(
+ text=_["SA_B_2"],
+ callback_data="bot_stats_sudo",
+ ),
+ InlineKeyboardButton(
+ text=_["SA_B_3"],
+ callback_data="TopOverall",
+ ),
+ ]
+ upl = InlineKeyboardMarkup(
+ [
+ sudo if status else not_sudo,
+ [
+ InlineKeyboardButton(
+ text=_["CLOSE_BUTTON"],
+ callback_data="close",
+ ),
+ ],
+ ]
+ )
+ return upl
+
+
+def back_stats_buttons(_):
+ upl = InlineKeyboardMarkup(
+ [
+ [
+ InlineKeyboardButton(
+ text=_["BACK_BUTTON"],
+ callback_data="stats_back",
+ ),
+ InlineKeyboardButton(
+ text=_["CLOSE_BUTTON"],
+ callback_data="close",
+ ),
+ ],
+ ]
+ )
+ return upl
diff --git a/SprotifyMusic/utils/inlinequery.py b/SprotifyMusic/utils/inlinequery.py
new file mode 100644
index 000000000000..26f08e32b9b6
--- /dev/null
+++ b/SprotifyMusic/utils/inlinequery.py
@@ -0,0 +1,62 @@
+from pyrogram.types import InlineQueryResultArticle, InputTextMessageContent
+
+answer = []
+
+answer.extend(
+ [
+ InlineQueryResultArticle(
+ title="ᴘᴀᴜsᴇ sᴛʀᴇᴀᴍ",
+ description=f"ᴘᴀᴜsᴇ ᴛʜᴇ ᴄᴜʀʀᴇɴᴛ ᴘʟᴀʏɪɴɢ sᴏɴɢ ᴏɴ ᴠᴏɪᴄᴇᴄʜᴀᴛ.",
+ thumb_url="https://telegra.ph/file/c0a1c789def7b93f13745.png",
+ input_message_content=InputTextMessageContent("/pause"),
+ ),
+ InlineQueryResultArticle(
+ title="ʀᴇsᴜᴍᴇ sᴛʀᴇᴀᴍ",
+ description=f"ʀᴇsᴜᴍᴇ ᴛʜᴇ ᴘᴀᴜsᴇᴅ sᴏɴɢ ᴏɴ ᴠᴏɪᴄᴇᴄʜᴀᴛ.",
+ thumb_url="https://telegra.ph/file/02d1b7f967ca11404455a.png",
+ input_message_content=InputTextMessageContent("/resume"),
+ ),
+ InlineQueryResultArticle(
+ title="ᴍᴜᴛᴇ sᴛʀᴇᴀᴍ",
+ description=f"ᴍᴜᴛᴇ ᴛʜᴇ ᴏɴɢᴏɪɴɢ sᴏɴɢ ᴏɴ ᴠᴏɪᴄᴇᴄʜᴀᴛ",
+ thumb_url="https://telegra.ph/file/66516f2976cb6d87e20f9.png",
+ input_message_content=InputTextMessageContent("/vcmute"),
+ ),
+ InlineQueryResultArticle(
+ title="ᴜɴᴍᴜᴛᴇ sᴛʀᴇᴀᴍ",
+ description=f"ᴜɴᴍᴜᴛᴇ ᴛʜᴇ ᴏɴɢᴏɪɴɢ sᴏɴɢ ᴏɴ ᴠᴏɪᴄᴇᴄʜᴀᴛ",
+ thumb_url="https://telegra.ph/file/3078794f9341ffd582e18.png",
+ input_message_content=InputTextMessageContent("/vcunmute"),
+ ),
+ InlineQueryResultArticle(
+ title="sᴋɪᴘ sᴛʀᴇᴀᴍ",
+ description=f"sᴋɪᴘ ᴛᴏ ɴᴇxᴛ ᴛʀᴀᴄᴋ. | sᴋɪᴘ ᴛᴏ ɴᴇxᴛ ᴛʀᴀᴄᴋ. | ғᴏʀ sᴘᴇᴄɪғɪᴄ ᴛʀᴀᴄᴋ ɴᴜᴍʙᴇʀ: /skip [number] ",
+ thumb_url="https://telegra.ph/file/98b88e52bc625903c7a2f.png",
+ input_message_content=InputTextMessageContent("/skip"),
+ ),
+ InlineQueryResultArticle(
+ title="ᴇɴᴅ sᴛʀᴇᴀᴍ",
+ description="sᴛᴏᴘ ᴛʜᴇ ᴏɴɢᴏɪɴɢ sᴏɴɢ ᴏɴ ɢʀᴏᴜᴘ ᴠᴏɪᴄᴇᴄʜᴀᴛ.",
+ thumb_url="https://telegra.ph/file/d2eb03211baaba8838cc4.png",
+ input_message_content=InputTextMessageContent("/stop"),
+ ),
+ InlineQueryResultArticle(
+ title="sʜᴜғғʟᴇ sᴛʀᴇᴀᴍ",
+ description="sʜᴜғғʟᴇ ᴛʜᴇ ǫᴜᴇᴜᴇᴅ ᴛʀᴀᴄᴋs ʟɪsᴛ.",
+ thumb_url="https://telegra.ph/file/7f6aac5c6e27d41a4a269.png",
+ input_message_content=InputTextMessageContent("/shuffle"),
+ ),
+ InlineQueryResultArticle(
+ title="sᴇᴇᴋ sᴛʀᴇᴀᴍ",
+ description="sᴇᴇᴋ ᴛʜᴇ ᴏɴɢᴏɪɴɢ sᴛʀᴇᴀᴍ ᴛᴏ ᴀ sᴘᴇᴄɪғɪᴄ ᴅᴜʀᴀᴛɪᴏɴ.",
+ thumb_url="https://telegra.ph/file/cd25ec6f046aa8003cfee.png",
+ input_message_content=InputTextMessageContent("/seek 10"),
+ ),
+ InlineQueryResultArticle(
+ title="ʟᴏᴏᴘ sᴛʀᴇᴀᴍ",
+ description="ʟᴏᴏᴘ ᴛʜᴇ ᴄᴜʀʀᴇɴᴛ ᴘʟᴀʏɪɴɢ ᴍᴜsɪᴄ. ᴜsᴀsɢᴇ: /loop [enable|disable]",
+ thumb_url="https://telegra.ph/file/081c20ce2074ea3e9b952.png",
+ input_message_content=InputTextMessageContent("/loop 3"),
+ ),
+ ]
+)
\ No newline at end of file
diff --git a/SprotifyMusic/utils/logger.py b/SprotifyMusic/utils/logger.py
new file mode 100644
index 000000000000..9b57990b1774
--- /dev/null
+++ b/SprotifyMusic/utils/logger.py
@@ -0,0 +1,33 @@
+from pyrogram.enums import ParseMode
+
+from SprotifyMusic import app
+from SprotifyMusic.utils.database import is_on_off
+from config import LOGGER_ID
+
+
+async def play_logs(message, streamtype):
+ if await is_on_off(2):
+ logger_text = f"""
+ ❖ {app.mention} ᴘʟᴀʏ ʟᴏɢ ❖
+
+● ᴄʜᴀᴛ ɪᴅ ➥ {message.chat.id}
+● ᴄʜᴀᴛ ɴᴀᴍᴇ ➥ {message.chat.title}
+● ᴄʜᴀᴛ ᴜsᴇʀɴᴀᴍᴇ ➥ @{message.chat.username}
+
+● ᴜsᴇʀ ɪᴅ ➥ {message.from_user.id}
+● ɴᴀᴍᴇ ➥ {message.from_user.mention}
+● ᴜsᴇʀɴᴀᴍᴇ ➥ @{message.from_user.username}
+
+● ǫᴜᴇʀʏ ➥ {message.text.split(None, 1)[1]}
+● sᴛʀᴇᴀᴍᴛʏᴘᴇ ➥ {streamtype}"""
+ if message.chat.id != LOGGER_ID:
+ try:
+ await app.send_message(
+ chat_id=LOGGER_ID,
+ text=logger_text,
+ parse_mode=ParseMode.HTML,
+ disable_web_page_preview=True,
+ )
+ except:
+ pass
+ return
diff --git a/SprotifyMusic/utils/mongo.py b/SprotifyMusic/utils/mongo.py
new file mode 100644
index 000000000000..722f062ec1d5
--- /dev/null
+++ b/SprotifyMusic/utils/mongo.py
@@ -0,0 +1,8 @@
+from typing import Dict, Union
+
+from motor.motor_asyncio import AsyncIOMotorClient as MongoCli
+
+from config import MONGO_DB_URI
+
+mongo = MongoCli(MONGO_DB_URI)
+db = mongo.TanuMusic
diff --git a/SprotifyMusic/utils/pastebin.py b/SprotifyMusic/utils/pastebin.py
new file mode 100644
index 000000000000..bac569c2057f
--- /dev/null
+++ b/SprotifyMusic/utils/pastebin.py
@@ -0,0 +1,21 @@
+import aiohttp
+
+BASE = "https://batbin.me/"
+
+
+async def post(url: str, *args, **kwargs):
+ async with aiohttp.ClientSession() as session:
+ async with session.post(url, *args, **kwargs) as resp:
+ try:
+ data = await resp.json()
+ except Exception:
+ data = await resp.text()
+ return data
+
+
+async def Bin(text):
+ resp = await post(f"{BASE}api/v2/paste", data=text)
+ if not resp["success"]:
+ return
+ link = BASE + resp["message"]
+ return link
diff --git a/SprotifyMusic/utils/stream/autoclear.py b/SprotifyMusic/utils/stream/autoclear.py
new file mode 100644
index 000000000000..92c8104634be
--- /dev/null
+++ b/SprotifyMusic/utils/stream/autoclear.py
@@ -0,0 +1,18 @@
+import os
+
+from config import autoclean
+
+
+async def auto_clean(popped):
+ try:
+ rem = popped["file"]
+ autoclean.remove(rem)
+ count = autoclean.count(rem)
+ if count == 0:
+ if "vid_" not in rem or "live_" not in rem or "index_" not in rem:
+ try:
+ os.remove(rem)
+ except:
+ pass
+ except:
+ pass
diff --git a/SprotifyMusic/utils/stream/queue.py b/SprotifyMusic/utils/stream/queue.py
new file mode 100644
index 000000000000..e1e52e8bf579
--- /dev/null
+++ b/SprotifyMusic/utils/stream/queue.py
@@ -0,0 +1,91 @@
+import asyncio
+from typing import Union
+
+from SprotifyMusic.misc import db
+from SprotifyMusic.utils.formatters import check_duration, seconds_to_min
+from config import autoclean, time_to_seconds
+
+
+async def put_queue(
+ chat_id,
+ original_chat_id,
+ file,
+ title,
+ duration,
+ user,
+ vidid,
+ user_id,
+ stream,
+ forceplay: Union[bool, str] = None,
+):
+ title = title.title()
+ try:
+ duration_in_seconds = time_to_seconds(duration) - 3
+ except:
+ duration_in_seconds = 0
+ put = {
+ "title": title,
+ "dur": duration,
+ "streamtype": stream,
+ "by": user,
+ "user_id": user_id,
+ "chat_id": original_chat_id,
+ "file": file,
+ "vidid": vidid,
+ "seconds": duration_in_seconds,
+ "played": 0,
+ }
+ if forceplay:
+ check = db.get(chat_id)
+ if check:
+ check.insert(0, put)
+ else:
+ db[chat_id] = []
+ db[chat_id].append(put)
+ else:
+ db[chat_id].append(put)
+ autoclean.append(file)
+
+
+async def put_queue_index(
+ chat_id,
+ original_chat_id,
+ file,
+ title,
+ duration,
+ user,
+ vidid,
+ stream,
+ forceplay: Union[bool, str] = None,
+):
+ if "20.212.146.162" in vidid:
+ try:
+ dur = await asyncio.get_event_loop().run_in_executor(
+ None, check_duration, vidid
+ )
+ duration = seconds_to_min(dur)
+ except:
+ duration = "ᴜʀʟ sᴛʀᴇᴀᴍ"
+ dur = 0
+ else:
+ dur = 0
+ put = {
+ "title": title,
+ "dur": duration,
+ "streamtype": stream,
+ "by": user,
+ "chat_id": original_chat_id,
+ "file": file,
+ "vidid": vidid,
+ "seconds": dur,
+ "played": 0,
+ }
+ if forceplay:
+ check = db.get(chat_id)
+ if check:
+ check.insert(0, put)
+ else:
+ db[chat_id] = []
+ db[chat_id].append(put)
+ else:
+ db[chat_id].append(put)
diff --git a/SprotifyMusic/utils/stream/stream.py b/SprotifyMusic/utils/stream/stream.py
new file mode 100644
index 000000000000..3af162c8ad50
--- /dev/null
+++ b/SprotifyMusic/utils/stream/stream.py
@@ -0,0 +1,420 @@
+import os
+from random import randint
+from typing import Union
+
+from pyrogram.types import InlineKeyboardMarkup
+
+import config
+from SprotifyMusic import Carbon, YouTube, app
+from SprotifyMusic.core.call import Sprotify
+from SprotifyMusic.misc import db
+from SprotifyMusic.utils.database import add_active_video_chat, is_active_chat
+from SprotifyMusic.utils.exceptions import AssistantErr
+from SprotifyMusic.utils.inline import aq_markup, close_markup, stream_markup
+from SprotifyMusic.utils.pastebin import Bin
+from SprotifyMusic.utils.stream.queue import put_queue, put_queue_index
+from SprotifyMusic.utils.thumbnails import get_thumb
+
+
+async def stream(
+ _,
+ mystic,
+ user_id,
+ result,
+ chat_id,
+ user_name,
+ original_chat_id,
+ video: Union[bool, str] = None,
+ streamtype: Union[bool, str] = None,
+ spotify: Union[bool, str] = None,
+ forceplay: Union[bool, str] = None,
+):
+ if not result:
+ return
+ if forceplay:
+ await Sprotify.force_stop_stream(chat_id)
+ if streamtype == "playlist":
+ msg = f"{_['play_19']}\n\n"
+ count = 0
+ for search in result:
+ if int(count) == config.PLAYLIST_FETCH_LIMIT:
+ continue
+ try:
+ (
+ title,
+ duration_min,
+ duration_sec,
+ thumbnail,
+ vidid,
+ ) = await YouTube.details(search, False if spotify else True)
+ except:
+ continue
+ if str(duration_min) == "None":
+ continue
+ if duration_sec > config.DURATION_LIMIT:
+ continue
+ if await is_active_chat(chat_id):
+ await put_queue(
+ chat_id,
+ original_chat_id,
+ f"vid_{vidid}",
+ title,
+ duration_min,
+ user_name,
+ vidid,
+ user_id,
+ "video" if video else "audio",
+ )
+ position = len(db.get(chat_id)) - 1
+ count += 1
+ msg += f"{count}. {title[:70]}\n"
+ msg += f"{_['play_20']} {position}\n\n"
+ else:
+ if not forceplay:
+ db[chat_id] = []
+ status = True if video else None
+ try:
+ file_path, direct = await YouTube.download(
+ vidid, mystic, video=status, videoid=True
+ )
+ except:
+ raise AssistantErr(_["play_14"])
+ await Sprotify.join_call(
+ chat_id,
+ original_chat_id,
+ file_path,
+ video=status,
+ image=thumbnail,
+ )
+ await put_queue(
+ chat_id,
+ original_chat_id,
+ file_path if direct else f"vid_{vidid}",
+ title,
+ duration_min,
+ user_name,
+ vidid,
+ user_id,
+ "video" if video else "audio",
+ forceplay=forceplay,
+ )
+ img = await get_thumb(vidid)
+ button = stream_markup(_, chat_id)
+ run = await app.send_photo(
+ original_chat_id,
+ photo=img,
+ caption=_["stream_1"].format(
+ f"https://t.me/{app.username}?start=info_{vidid}",
+ title[:23],
+ duration_min,
+ user_name,
+ ),
+ reply_markup=InlineKeyboardMarkup(button),
+ )
+ db[chat_id][0]["mystic"] = run
+ db[chat_id][0]["markup"] = "stream"
+ if count == 0:
+ return
+ else:
+ link = await Bin(msg)
+ lines = msg.count("\n")
+ if lines >= 17:
+ car = os.linesep.join(msg.split(os.linesep)[:17])
+ else:
+ car = msg
+ carbon = await Carbon.generate(car, randint(100, 10000000))
+ upl = close_markup(_)
+ return await app.send_photo(
+ original_chat_id,
+ photo=carbon,
+ caption=_["play_21"].format(position, link),
+ reply_markup=upl,
+ )
+ elif streamtype == "youtube":
+ link = result["link"]
+ vidid = result["vidid"]
+ title = (result["title"]).title()
+ duration_min = result["duration_min"]
+ thumbnail = result["thumb"]
+ status = True if video else None
+ try:
+ file_path, direct = await YouTube.download(
+ vidid, mystic, videoid=True, video=status
+ )
+ except:
+ raise AssistantErr(_["play_14"])
+ if await is_active_chat(chat_id):
+ await put_queue(
+ chat_id,
+ original_chat_id,
+ file_path if direct else f"vid_{vidid}",
+ title,
+ duration_min,
+ user_name,
+ vidid,
+ user_id,
+ "video" if video else "audio",
+ )
+ position = len(db.get(chat_id)) - 1
+ button = aq_markup(_, chat_id)
+ await app.send_message(
+ chat_id=original_chat_id,
+ text=_["queue_4"].format(position, title[:27], duration_min, user_name),
+ reply_markup=InlineKeyboardMarkup(button),
+ )
+ else:
+ if not forceplay:
+ db[chat_id] = []
+ await Sprotify.join_call(
+ chat_id,
+ original_chat_id,
+ file_path,
+ video=status,
+ image=thumbnail,
+ )
+ await put_queue(
+ chat_id,
+ original_chat_id,
+ file_path if direct else f"vid_{vidid}",
+ title,
+ duration_min,
+ user_name,
+ vidid,
+ user_id,
+ "video" if video else "audio",
+ forceplay=forceplay,
+ )
+ img = await get_thumb(vidid)
+ button = stream_markup(_, chat_id)
+ run = await app.send_photo(
+ original_chat_id,
+ photo=img,
+ caption=_["stream_1"].format(
+ f"https://t.me/{app.username}?start=info_{vidid}",
+ title[:23],
+ duration_min,
+ user_name,
+ ),
+ reply_markup=InlineKeyboardMarkup(button),
+ )
+ db[chat_id][0]["mystic"] = run
+ db[chat_id][0]["markup"] = "stream"
+ elif streamtype == "soundcloud":
+ file_path = result["filepath"]
+ title = result["title"]
+ duration_min = result["duration_min"]
+ if await is_active_chat(chat_id):
+ await put_queue(
+ chat_id,
+ original_chat_id,
+ file_path,
+ title,
+ duration_min,
+ user_name,
+ streamtype,
+ user_id,
+ "audio",
+ )
+ position = len(db.get(chat_id)) - 1
+ button = aq_markup(_, chat_id)
+ await app.send_message(
+ chat_id=original_chat_id,
+ text=_["queue_4"].format(position, title[:27], duration_min, user_name),
+ reply_markup=InlineKeyboardMarkup(button),
+ )
+ else:
+ if not forceplay:
+ db[chat_id] = []
+ await Sprotify.join_call(chat_id, original_chat_id, file_path, video=None)
+ await put_queue(
+ chat_id,
+ original_chat_id,
+ file_path,
+ title,
+ duration_min,
+ user_name,
+ streamtype,
+ user_id,
+ "audio",
+ forceplay=forceplay,
+ )
+ button = stream_markup(_, chat_id)
+ run = await app.send_photo(
+ original_chat_id,
+ photo=config.SOUNCLOUD_IMG_URL,
+ caption=_["stream_1"].format(
+ config.SUPPORT_CHAT, title[:23], duration_min, user_name
+ ),
+ reply_markup=InlineKeyboardMarkup(button),
+ )
+ db[chat_id][0]["mystic"] = run
+ db[chat_id][0]["markup"] = "tg"
+ elif streamtype == "telegram":
+ file_path = result["path"]
+ link = result["link"]
+ title = (result["title"]).title()
+ duration_min = result["dur"]
+ status = True if video else None
+ if await is_active_chat(chat_id):
+ await put_queue(
+ chat_id,
+ original_chat_id,
+ file_path,
+ title,
+ duration_min,
+ user_name,
+ streamtype,
+ user_id,
+ "video" if video else "audio",
+ )
+ position = len(db.get(chat_id)) - 1
+ button = aq_markup(_, chat_id)
+ await app.send_message(
+ chat_id=original_chat_id,
+ text=_["queue_4"].format(position, title[:27], duration_min, user_name),
+ reply_markup=InlineKeyboardMarkup(button),
+ )
+ else:
+ if not forceplay:
+ db[chat_id] = []
+ await Sprotify.join_call(chat_id, original_chat_id, file_path, video=status)
+ await put_queue(
+ chat_id,
+ original_chat_id,
+ file_path,
+ title,
+ duration_min,
+ user_name,
+ streamtype,
+ user_id,
+ "video" if video else "audio",
+ forceplay=forceplay,
+ )
+ if video:
+ await add_active_video_chat(chat_id)
+ button = stream_markup(_, chat_id)
+ run = await app.send_photo(
+ original_chat_id,
+ photo=config.TELEGRAM_VIDEO_URL if video else config.TELEGRAM_AUDIO_URL,
+ caption=_["stream_1"].format(link, title[:23], duration_min, user_name),
+ reply_markup=InlineKeyboardMarkup(button),
+ )
+ db[chat_id][0]["mystic"] = run
+ db[chat_id][0]["markup"] = "tg"
+ elif streamtype == "live":
+ link = result["link"]
+ vidid = result["vidid"]
+ title = (result["title"]).title()
+ thumbnail = result["thumb"]
+ duration_min = "Live Track"
+ status = True if video else None
+ if await is_active_chat(chat_id):
+ await put_queue(
+ chat_id,
+ original_chat_id,
+ f"live_{vidid}",
+ title,
+ duration_min,
+ user_name,
+ vidid,
+ user_id,
+ "video" if video else "audio",
+ )
+ position = len(db.get(chat_id)) - 1
+ button = aq_markup(_, chat_id)
+ await app.send_message(
+ chat_id=original_chat_id,
+ text=_["queue_4"].format(position, title[:27], duration_min, user_name),
+ reply_markup=InlineKeyboardMarkup(button),
+ )
+ else:
+ if not forceplay:
+ db[chat_id] = []
+ n, file_path = await YouTube.video(link)
+ if n == 0:
+ raise AssistantErr(_["str_3"])
+ await Sprotify.join_call(
+ chat_id,
+ original_chat_id,
+ file_path,
+ video=status,
+ image=thumbnail if thumbnail else None,
+ )
+ await put_queue(
+ chat_id,
+ original_chat_id,
+ f"live_{vidid}",
+ title,
+ duration_min,
+ user_name,
+ vidid,
+ user_id,
+ "video" if video else "audio",
+ forceplay=forceplay,
+ )
+ img = await get_thumb(vidid)
+ button = stream_markup(_, chat_id)
+ run = await app.send_photo(
+ original_chat_id,
+ photo=img,
+ caption=_["stream_1"].format(
+ f"https://t.me/{app.username}?start=info_{vidid}",
+ title[:23],
+ duration_min,
+ user_name,
+ ),
+ reply_markup=InlineKeyboardMarkup(button),
+ )
+ db[chat_id][0]["mystic"] = run
+ db[chat_id][0]["markup"] = "tg"
+ elif streamtype == "index":
+ link = result
+ title = "ɪɴᴅᴇx ᴏʀ ᴍ3ᴜ8 ʟɪɴᴋ"
+ duration_min = "00:00"
+ if await is_active_chat(chat_id):
+ await put_queue_index(
+ chat_id,
+ original_chat_id,
+ "index_url",
+ title,
+ duration_min,
+ user_name,
+ link,
+ "video" if video else "audio",
+ )
+ position = len(db.get(chat_id)) - 1
+ button = aq_markup(_, chat_id)
+ await mystic.edit_text(
+ text=_["queue_4"].format(position, title[:27], duration_min, user_name),
+ reply_markup=InlineKeyboardMarkup(button),
+ )
+ else:
+ if not forceplay:
+ db[chat_id] = []
+ await Sprotify.join_call(
+ chat_id,
+ original_chat_id,
+ link,
+ video=True if video else None,
+ )
+ await put_queue_index(
+ chat_id,
+ original_chat_id,
+ "index_url",
+ title,
+ duration_min,
+ user_name,
+ link,
+ "video" if video else "audio",
+ forceplay=forceplay,
+ )
+ button = stream_markup(_, chat_id)
+ run = await app.send_photo(
+ original_chat_id,
+ photo=config.STREAM_IMG_URL,
+ caption=_["stream_2"].format(user_name),
+ reply_markup=InlineKeyboardMarkup(button),
+ )
+ db[chat_id][0]["mystic"] = run
+ db[chat_id][0]["markup"] = "tg"
+ await mystic.delete()
diff --git a/SprotifyMusic/utils/sys.py b/SprotifyMusic/utils/sys.py
new file mode 100644
index 000000000000..eaa976e461cf
--- /dev/null
+++ b/SprotifyMusic/utils/sys.py
@@ -0,0 +1,15 @@
+import time
+
+import psutil
+
+from SprotifyMusic.misc import _boot_
+from SprotifyMusic.utils.formatters import get_readable_time
+
+
+async def bot_sys_stats():
+ bot_uptime = int(time.time() - _boot_)
+ UP = f"{get_readable_time(bot_uptime)}"
+ CPU = f"{psutil.cpu_percent(interval=0.5)}%"
+ RAM = f"{psutil.virtual_memory().percent}%"
+ DISK = f"{psutil.disk_usage('/').percent}%"
+ return UP, CPU, RAM, DISK
diff --git a/SprotifyMusic/utils/thumbnails.py b/SprotifyMusic/utils/thumbnails.py
new file mode 100644
index 000000000000..28666984c3e0
--- /dev/null
+++ b/SprotifyMusic/utils/thumbnails.py
@@ -0,0 +1,172 @@
+import os
+import re
+import aiofiles
+import aiohttp
+from PIL import Image, ImageDraw, ImageEnhance, ImageFilter, ImageFont
+from youtubesearchpython.__future__ import VideosSearch
+
+# Load font
+font = ImageFont.truetype("SprotifyMusic/assets/font.ttf", 28)
+font_nav = ImageFont.truetype("SprotifyMusic/assets/Montserrat-Medium.ttf", 22) # Font untuk navigation bar
+font_nav_bold = ImageFont.truetype("SprotifyMusic/assets/Montserrat-Bold.ttf", 22) # Font untuk navigation bar
+font_bottom = ImageFont.truetype("SprotifyMusic/assets/Montserrat-Medium.ttf", 20) # Font untuk tulisan bawah
+
+width, height = 1080, 720
+
+outline_color = (255, 255, 255, 255)
+
+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
+
+def truncate(text):
+ words = text.split(" ")
+ text1 = ""
+ text2 = ""
+ for word in words:
+ if len(text1) + len(word) < 30:
+ text1 += " " + word
+ elif len(text2) + len(word) < 30:
+ text2 += " " + word
+
+ text1 = text1.strip()
+ text2 = text2.strip()
+ return [text1, text2]
+
+def crop_rounded_rectangle(img, output_size, border, corner_radius, crop_scale=1.5):
+ # pusat potonya
+ half_width = img.size[0] / 2
+ half_height = img.size[1] / 2
+ larger_size = int(output_size * crop_scale)
+
+ img = img.crop(
+ (
+ half_width - larger_size / 2,
+ half_height - larger_size / 2,
+ half_width + larger_size / 2,
+ half_height + larger_size / 2,
+ )
+ )
+
+ img = img.resize((output_size - 2 * border, output_size - 2 * border))
+ img_last = Image.new("L", size=(output_size, output_size), color="white")
+
+ # bikin mask
+ mask_main = Image.new("L", (output_size - 2 * border, output_size - 2 * border), 0)
+ draw_main = ImageDraw.Draw(mask_main)
+ draw_main.rounded_rectangle(
+ (0, 0, output_size - 2 * border, output_size - 2 * border),
+ radius=corner_radius,
+ fill=255,
+ )
+
+ # masukin gambar ke rounded rectangle
+ img_last.paste(
+ img,
+ (border, border),
+ mask_main,
+ )
+ # Membuat border untuk mask
+ mask_border = Image.new("L", (output_size, output_size), 0)
+ draw_border = ImageDraw.Draw(mask_border)
+ draw_border.rounded_rectangle((0, 0, output_size, output_size), radius=corner_radius, fill=255)
+
+ # Membuat gambar akhir dengan border dan mask
+ result = Image.composite(img_last, Image.new("RGBA", img_last.size, (0, 0, 0, 0)), mask_border)
+
+ return result
+
+async def get_thumb(videoid):
+ if os.path.isfile(f"cache/{videoid}_v4.png"):
+ return f"cache/{videoid}_v4.png"
+
+ url = f"https://www.youtube.com/watch?v={videoid}"
+ results = VideosSearch(url, limit=1)
+
+ try:
+ for result in (await results.next())["result"]:
+ try:
+ title = result.get("title", "Unsupported Title")
+ title = re.sub("\W+", " ", title)
+ title = title.title()
+ except:
+ title = "Unsupported Title"
+
+ duration = result.get("duration", "Unknown Mins")
+ views = result.get("viewCount", {}).get("short", "Unknown Views")
+ channel = result.get("channel", {}).get("name", "Unknown Channel")
+
+ if "thumbnails" in result and len(result["thumbnails"]) > 0:
+ thumbnail = result["thumbnails"][0]["url"].split("?")[0]
+ else:
+ raise ValueError("Thumbnail not found in search results")
+
+ async with aiohttp.ClientSession() as session:
+ async with session.get(thumbnail) as resp:
+ if resp.status == 200:
+ async with aiofiles.open(f"cache/thumb{videoid}.png", mode="wb") as f:
+ await f.write(await resp.read())
+
+ youtube = Image.open(f"cache/thumb{videoid}.png")
+ image1 = changeImageSize(1280, 720, youtube)
+ image2 = image1.convert("L")
+ background = image2.filter(filter=ImageFilter.GaussianBlur(20)).convert("RGBA")
+ draw = ImageDraw.Draw(background, "RGBA")
+ enhancer = ImageEnhance.Brightness(background)
+ background = enhancer.enhance(0.6)
+ font = ImageFont.truetype("SprotifyMusic/assets/font.ttf", 30)
+
+ corner_radius = 50 # Radius sudut rounded rectangle
+ rectangle_thumbnail = crop_rounded_rectangle(youtube, 400, 20, corner_radius)
+ rectangle_thumbnail = rectangle_thumbnail.resize((400, 400))
+ rectangle_position = (120, 160) # Menempatkan di sebelah kiri
+ background.paste(rectangle_thumbnail, rectangle_position, rectangle_thumbnail)
+
+ text_x_position = 565
+
+ title1 = truncate(title)
+ draw.text((10, 10), f"Sprotify Music", fill=outline_color, font=font)
+
+ # Membuat rounded rectangle persegi panjang di atas (kanan) tanpa warna fill
+ rect_width, rect_height = 600, 100
+ x1, y1 = width - rect_width - 50, (height // 2) - rect_height - 80
+ x2, y2 = x1 + rect_width, y1 + rect_height
+ draw.rounded_rectangle((x1, y1, x2, y2), radius=20, outline=outline_color, width=3)
+ draw.text((x1 + 20, y1 + 30), f"Title: {title1}", fill="white", font=font)
+
+ # Membuat rounded rectangle persegi panjang tengah (kanan) tanpa warna fill
+ y1, y2 = (height // 2) - (rect_height // 2), (height // 2) + (rect_height // 2)
+ draw.rounded_rectangle((x1, y1, x2, y2), radius=20, outline=outline_color, width=3)
+ draw.text((x1 + 20, y1 + 30), f"Channel: {channel}", fill="white", font=font)
+
+ # Membuat rounded rectangle persegi panjang di bawah (kanan) tanpa warna fill
+ y1, y2 = (height // 2) + 80, (height // 2) + 80 + rect_height
+ draw.rounded_rectangle((x1, y1, x2, y2), radius=20, outline=outline_color, width=3)
+ draw.text((x1 + 20, y1 + 30), f"Duration: {duration}", fill="white", font=font)
+
+ # Menambahkan teks di bagian bawah tengah
+ text = "Made with Luv by EasyWinter"
+ # Menggunakan textbbox untuk mendapatkan ukuran teks
+ text_bbox = draw.textbbox((0, 0), text, font=font_bottom)
+ text_width = text_bbox[2] - text_bbox[0] # Lebar teks
+ text_height = text_bbox[3] - text_bbox[1] # Tinggi teks
+
+ # Posisi teks di bagian bawah tengah
+ text_x = (width - text_width) // 2 # Posisi tengah
+ text_y = height - text_height - 20 # Posisi 20px dari bagian bawah
+ draw.text((text_x, text_y), text, fill="white", font=font_bottom)
+
+ try:
+ os.remove(f"cache/thumb{videoid}.png")
+ except:
+ pass
+ background.save(f"cache/{videoid}_v4.png")
+ return f"cache/{videoid}_v4.png"
+
+ except Exception as e:
+ print(f"Error in get_thumb: {e}")
+ raise
\ No newline at end of file
diff --git a/app.json b/app.json
new file mode 100644
index 000000000000..367e02c2ea35
--- /dev/null
+++ b/app.json
@@ -0,0 +1,73 @@
+{
+ "name": "TANU MUSIC",
+ "description": "A Telegram Music Player Bot, written in Python with Pyrogram and Py-Tgcalls.",
+ "logo": "https://te.legra.ph/file/b8a0c1a00db3e57522b53.jpg",
+ "keywords": [
+ "python3",
+ "telegram",
+ "bot",
+ "AnonX",
+ "MusicBot",
+ "telegram-bot",
+ "pyrogram"
+ ],
+ "env": {
+ "API_ID": {
+ "description": "Get this value from https://my.telegram.org",
+ "value": "20851717",
+ "required": true
+ },
+ "API_HASH": {
+ "description": "Get this value from https://my.telegram.org",
+ "value": "0217fe5cd94ff3f4d4555b1551670144",
+ "required": true
+ },
+ "BOT_TOKEN": {
+ "description": "A Bot's token from Botfather",
+ "value": "",
+ "required": true
+ },
+ "MONGO_DB_URI": {
+ "description": "Get a mongodb url from https://cloud.mongodb.com.",
+ "value": "",
+ "required": true
+ },
+ "OWNER_ID": {
+ "description": "The user id of user whom you would like to add as OWNER.",
+ "value": "7453278496",
+ "required": true
+ },
+ "STRING_SESSION": {
+ "description": "A Pyrogram v2 String Session.",
+ "value": "",
+ "required": true
+ },
+ "HEROKU_API_KEY": {
+ "description": "Your Heroku account's API key",
+ "value": "",
+ "required": false
+ },
+ "HEROKU_APP_NAME": {
+ "description": "Your heroku app name",
+ "value": "",
+ "required": false
+ },
+ "LOGGER_ID": {
+ "description": "Your Log Group ID, add your bot and promote as an admin with full rights!. Channel ki id mat daal dena bsdk.",
+ "value": "-1002100219353",
+ "required": true
+ }
+ },
+ "buildpacks": [
+ {
+ "url": "heroku/python"
+ },
+ {
+ "url": "heroku/nodejs"
+ },
+ {
+ "url": "https://github.com/jonathanong/heroku-buildpack-ffmpeg-latest.git"
+ }
+ ],
+ "stack": "container"
+ }
diff --git a/config/ConfigReadme.md b/config/ConfigReadme.md
new file mode 100644
index 000000000000..708fde58c19a
--- /dev/null
+++ b/config/ConfigReadme.md
@@ -0,0 +1,115 @@
+# Tanu Music Bot Configs
+
+Config vars are basically the variables which configure or modify bot to function, which are the basic necessities of plugins or code to work. You have to set the proper mandatory vars to make it functional and to start the basic feature of bot.
+
+### Get to know about all these vars in depth from our Docs. [Read Now from Here](https://notreallyshikhar.gitbook.io/yukkimusicbot/config-vars/available-vars)
+
+## Mandatory Vars
+
+- These are the minimum required vars need to setup to make Tanu Music Bot functional.
+
+1. `API_ID` : Get it from my.telegram.org
+2. `API_HASH` : Get it from my.telegram.org
+3. `BOT_TOKEN` : Get it from [@Botfather](http://t.me/BotFather) in Telegram
+4. `MONGO_DB_URI` : Get mongo db [from here.](https://notreallyshikhar.gitbook.io/yukkimusicbot/deployment/mongodb)
+5. `LOGGER_ID` : You'll need a Private Group ID for this. Supergroup Needed with id starting from -100
+6. `OWNER_ID` : Your Owner ID for managing your bot.
+7. `STRING_SESSION` : Pyrogram Session Needed
+8. `COOKIES` : Add youtube cookies in cookies.txt on cookies folders
+
+## Non-Mandatory Vars
+
+- These are the extra vars for extra features inside Music Bot. You can leave non mandatory vars for now and can add them later.
+
+1. `DURATION_LIMIT` : Custom max audio(music) duration for voice chat. Default to 60 mins.
+2. `SONG_DOWNLOAD_DURATION_LIMIT` : Duration Limit for downloading Songs in MP3 or MP4 format from bot. Default to 180 mins.
+3. `VIDEO_STREAM_LIMIT` : Maximum number of video calls allowed on bot. You can later set it via /set_video_limit on telegram. Default to 3 chats.
+4. `SERVER_PLAYLIST_LIMIT` : Maximum Limit Allowed for users to save playlists on bot's server. Default to 30
+5. `PLAYLIST_FETCH_LIMIT` : Maximum limit for fetching playlist's track from youtube, spotify, apple links. Default to 25
+6. `CLEANMODE_MINS` : Cleanmode time after which bot will delete its old messages from chats. Default to 5 Mins.
+7. `SUPPORT_CHANNEL` : If you've any channel for your music bot , fill it with your channel link
+8. `SUPPORT_GROUP` : If you've any group support for your music bot , fill it with your group link
+
+## Play FileSize Limit Vars
+
+- Maximum File size limit for the audio and videos that a user can play from your bot. [Only Bytes Size Accepted]
+> You can convert mb into bytes from https://www.gbmb.org/mb-to-bytes and use it here
+
+1. `TG_AUDIO_FILESIZE_LIMIT` : Maximum file size limit for audio files which can be streamed over vc. Defaults to 104857600 bytes, i.e. 100MB
+2. `TG_VIDEO_FILESIZE_LIMIT` : Maximum file size limit for video files which can be played. Defaults to 1073741824 bytes, i.e. 1024MB or 1GB
+
+
+## Bot Vars
+
+- These all vars are used for setting up bot. You can edit these vars if you want , else leave all of them as it is.
+
+1. `PRIVATE_BOT_MODE` : Set it `True` if you want your bot to be private only or False for all groups. Default to False
+2. `YOUTUBE_EDIT_SLEEP` : Time sleep duration For Youtube Downloader. Default to 3 seconds
+3. `TELEGRAM_EDIT_SLEEP` : Time sleep duration For Telegram Downloader. Default to 5 seconds
+4. `AUTO_LEAVING_ASSISTANT` : Set it in `True` if you want to leave your assistant after a certain amount of time.
+5. `ASSISTANT_LEAVE_TIME` : Time after which your assistant account will leave served chats automatically. Default to 5400 seconds, i.e 90 Mins
+6. `AUTO_DOWNLOADS_CLEAR` : Set it `True` if you want to delete downloads after the music playout ends.
+7. `AUTO_SUGGESTION_MODE` : Set it `True` if you want to bot to suggest about bot commands to random chats of your bots.
+9. `AUTO_SUGGESTION_TIME` : Time after which your bot will suggest random 1/10 chats of your served chats about bot commands. Default to 5400 seconds, i.e 90 Mins
+10. `SET_CMDS` : Set it to `True` if you want your bot to set the commands for chat menu automatically. [Reference](https://i.postimg.cc/Bbg3LQTG/image.png)
+
+## Spotify Vars
+
+- You can play tracks or playlists from spotify from Tanu Music bot
+- You'll need these two vars to make spotify play working. This is not essential , you can leave them blank if you want.
+
+### How to get these? [Read from here](https://notreallyshikhar.gitbook.io/yukkimusicbot/deployment/spotify)
+
+
+1. `SPOTIFY_CLIENT_ID` : Get it from https://developer.spotify.com/dashboard
+2. `SPOTIFY_CLIENT_SECRET` : Get it from https://developer.spotify.com/dashboard
+
+## Heroku Vars
+
+- To work some Heroku compatible modules, this var value required to Access your account to use `get_log`, `usage`, `update` etc etc commands.
+- You can fill this var using your API key or Authorization token.
+
+### How to get these? [Read from here](https://notreallyshikhar.gitbook.io/yukkimusicbot/config-vars/heroku-vars)
+
+1. `HEROKU_API_KEY` : Get it from http://dashboard.heroku.com/account
+2. `HEROKU_APP_NAME` : You have to Enter the app name which you gave to identify your Music Bot in Heroku.
+
+
+## Custom Repo Vars
+
+- If you plan to use Tanu Music Bot with your own customized or modified code.
+
+1. `UPSTREAM_REPO` : Your Upstream Repo URL or Forked Repo.
+2. `UPSTREAM_BRANCH` : Default Branch of your Upstream Repo URL or Forked Repo.
+3. `GIT_TOKEN` : Your GIT TOKEN if your upstream repo is private
+4. `GITHUB_REPO` : Your Github Repo url, that will be shown on /start command
+
+
+
+## Images/Thumbnail Vars
+
+- You can change images which are used in Tanu Music Bot.
+- You can generate telegaph links from [@TanuMusicxBot](http://t.me/TanuMusicxBot) and use it here.
+
+1. `START_IMG_URL` : Image which comes on /start command in private messages of bot.
+2. `PING_IMG_URL` : Image which comes on /ping command of bot.
+3. `PLAYLIST_IMG_URL` : Image which comes on /play command of bot.
+4. `GLOBAL_IMG_URL` : Image which comes on /stats command of bot.
+5. `STATS_IMG_URL` : Image which comes on /stats command of bot.
+6. `TELEGRAM_AUDIO_URL` : This image comes when someone plays audios from telegram.
+7. `TELEGRAM_VIDEO_URL` : This image comes when someone plays videos from telegram.
+8. `STREAM_IMG_URL` : his image comes when someone plays m3u8 or index links.
+9. `SOUNCLOUD_IMG_URL` : This image comes when someone plays music from soundcloud.
+10. `YOUTUBE_IMG_URL` : This image comes if thumbnail generator fails to gen thumb anyhow.
+11. `SPOTIFY_ARTIST_IMG_URL` : This image comes when someone plays Spotify artist via link in inline mode.
+12. `SPOTIFY_ALBUM_IMG_URL` : This image comes when someone plays Spotify album via link in inline mode.
+13. `SPOTIFY_PLAYLIST_IMG_URL` : This image comes when someone plays Spotify album via link in inline mode.
+
+## Multi Assistant Mode
+
+- You can use upto 5 Assistant Clients ( allowing your bot to atleast work in 2000-2500 chats at a time )
+
+1. `STRING_SESSION2` : Pyrogram Session Needed
+2. `STRING_SESSION3` : Pyrogram Session Needed
+3. `STRING_SESSION4` : Pyrogram Session Needed
+4. `STRING_SESSION5` : Pyrogram Session Needed
\ No newline at end of file
diff --git a/config/__init__.py b/config/__init__.py
new file mode 100644
index 000000000000..d085c3a95859
--- /dev/null
+++ b/config/__init__.py
@@ -0,0 +1 @@
+from .config import *
\ No newline at end of file
diff --git a/config/config.py b/config/config.py
new file mode 100644
index 000000000000..ba4d5911a0e7
--- /dev/null
+++ b/config/config.py
@@ -0,0 +1,136 @@
+import re
+from os import getenv
+
+from dotenv import load_dotenv
+from pyrogram import filters
+
+load_dotenv()
+
+#❖ Get this value from my.telegram.org/apps
+API_ID = getenv("API_ID", None)
+API_HASH = getenv("API_HASH", None)
+
+#❖ Add Owner Username without @
+OWNER_USERNAME = getenv("OWNER_USERNAME", "EasyWinter")
+
+#❖ Get Your bot username
+BOT_USERNAME = getenv("BOT_USERNAME", "SprotifyMusicBot")
+
+#❖ Don't Add style font
+BOT_NAME = getenv("BOT_NAME", "Sprotify Music")
+
+#❖ get Your Assistant User name
+ASSUSERNAME = getenv("ASSUSERNAME", "Nanosauruss")
+
+#❖ Get your token from @BotFather on Telegram.
+BOT_TOKEN = getenv("BOT_TOKEN")
+
+#❖ Get your mongo url from cloud.mongodb.com
+MONGO_DB_URI = getenv("MONGO_DB_URI", None)
+
+DURATION_LIMIT_MIN = int(getenv("DURATION_LIMIT", 600000))
+
+#❖ Chat id of a group for logging bot's activities
+LOGGER_ID = int(getenv("LOGGER_ID", "-1001589206452"))
+
+#❖ Get this value from @MissRose_bot on Telegram by /id
+OWNER_ID = getenv("OWNER_ID", "1854441420")
+
+#❖ Your heroku app name
+HEROKU_APP_NAME = getenv("HEROKU_APP_NAME", None)
+
+#❖ Get it from http://dashboard.heroku.com/account
+HEROKU_API_KEY = getenv("HEROKU_API_KEY", None)
+
+UPSTREAM_REPO = getenv(
+ "UPSTREAM_REPO",
+ "https://github.com/Mister-Man7/SprotifyXMusic",
+)
+
+UPSTREAM_BRANCH = getenv("UPSTREAM_BRANCH", "v2")
+
+GIT_TOKEN = getenv(
+ "GIT_TOKEN", None
+) #❖ Fill this variable if your upstream repository is private
+
+#❖ Make your bots privacy from telegra.ph and put your url here
+PRIVACY_LINK = getenv(
+ "PRIVACY_LINK", "https://telegra.ph/Privacy-Policy-for-SprotifyXMusic-11-29"
+)
+
+SUPPORT_CHANNEL = getenv("SUPPORT_CHANNEL", "https://t.me/SprotifyNews")
+SUPPORT_CHAT = getenv("SUPPORT_CHAT", "https://t.me/datarantinggi")
+
+#❖ Set this to True if you want the assistant to automatically leave chats after an interval
+AUTO_LEAVING_ASSISTANT = bool(getenv("AUTO_LEAVING_ASSISTANT", False))
+
+
+#❖ Get this credentials from https://developer.spotify.com/dashboard
+SPOTIFY_CLIENT_ID = getenv("SPOTIFY_CLIENT_ID", "e319091f771445b18c029299505d5d4f")
+SPOTIFY_CLIENT_SECRET = getenv("SPOTIFY_CLIENT_SECRET", "293c334a2861415197a697b2d11dd4de")
+
+
+#❖ Maximum limit for fetching playlist's track from youtube, spotify, apple links.
+PLAYLIST_FETCH_LIMIT = int(getenv("PLAYLIST_FETCH_LIMIT", 2500))
+
+
+#❖ Telegram audio and video file size limit (in bytes)
+TG_AUDIO_FILESIZE_LIMIT = int(getenv("TG_AUDIO_FILESIZE_LIMIT", 104857600))
+TG_VIDEO_FILESIZE_LIMIT = int(getenv("TG_VIDEO_FILESIZE_LIMIT", 1073741824))
+#❖ Checkout https://www.gbmb.org/mb-to-bytes for converting mb to bytes
+
+
+#❖ Get your pyrogram v2 session from @StringFatherBot on Telegram
+STRING1 = getenv("STRING_SESSION", None)
+STRING2 = getenv("STRING_SESSION2", None)
+STRING3 = getenv("STRING_SESSION3", None)
+STRING4 = getenv("STRING_SESSION4", None)
+STRING5 = getenv("STRING_SESSION5", None)
+
+
+BANNED_USERS = filters.user()
+adminlist = {}
+lyrical = {}
+votemode = {}
+autoclean = []
+confirmer = {}
+
+
+START_IMG_URL = getenv(
+ "START_IMG_URL", "https://graph.org/file/afe0bf9e6b4fede3afc0e.jpg"
+)
+PING_IMG_URL = getenv(
+ "PING_IMG_URL", "https://graph.org/file/8f6de108a54be6506b693.jpg"
+)
+PLAYLIST_IMG_URL = "https://graph.org/file/94074fd8f348807833802.jpg"
+STATS_IMG_URL = "https://graph.org/file/c9266ed41c1a9b8121185.jpg"
+TELEGRAM_AUDIO_URL = "https://graph.org/file/f626eb194b025672eea28.jpg"
+TELEGRAM_VIDEO_URL = "https://graph.org/file/f6e977e0046a5b8d17904.jpg"
+STREAM_IMG_URL = "https://graph.org/file/7247dcb0ae280ba3b8492.jpg"
+SOUNCLOUD_IMG_URL = "https://graph.org/file/199a1bd803d3e8c8d0e43.jpg"
+YOUTUBE_IMG_URL = "https://graph.org/file/d902a638d7bcb6653d36f.jpg"
+SPOTIFY_ARTIST_IMG_URL = "https://graph.org/file/dc9eb455f86f845f400f8.jpg"
+SPOTIFY_ALBUM_IMG_URL = "https://graph.org/file/aacfa8762c016077b7ee9.jpg"
+SPOTIFY_PLAYLIST_IMG_URL = "https://graph.org/file/480dbdc265670e833d763.jpg"
+
+
+
+def time_to_seconds(time):
+ stringt = str(time)
+ return sum(int(x) * 60**i for i, x in enumerate(reversed(stringt.split(":"))))
+
+
+DURATION_LIMIT = int(time_to_seconds(f"{DURATION_LIMIT_MIN}:00"))
+SERVER_PLAYLIST_LIMIT = int(getenv("SERVER_PLAYLIST_LIMIT", "30"))
+
+if SUPPORT_CHANNEL:
+ if not re.match("(?:http|https)://", SUPPORT_CHANNEL):
+ raise SystemExit(
+ "[ERROR] - Your SUPPORT_CHANNEL url is wrong. Please ensure that it starts with https://"
+ )
+
+if SUPPORT_CHAT:
+ if not re.match("(?:http|https)://", SUPPORT_CHAT):
+ raise SystemExit(
+ "[ERROR] - Your SUPPORT_CHAT url is wrong. Please ensure that it starts with https://"
+ )
diff --git a/cookies/cookies.txt b/cookies/cookies.txt
new file mode 100644
index 000000000000..51cdaca4bafb
--- /dev/null
+++ b/cookies/cookies.txt
@@ -0,0 +1,24 @@
+# Netscape HTTP Cookie File
+# http://curl.haxx.se/rfc/cookie_spec.html
+# This is a generated file! Do not edit.
+
+.youtube.com TRUE / FALSE 1767354772 SID g.a000qggpRHoB7G5WAYuKHnOChwwr2djFnop8cw0_2_nfCF8vbx77nJC9WQ5FH-4-7izAHlosiQACgYKAcwSARISFQHGX2MiEaarPciQ4zgKKp-g-ZT_-hoVAUF8yKpx0c0wNFl8hcprkPx28oFx0076
+.youtube.com TRUE / TRUE 1767354772 __Secure-1PSID g.a000qggpRHoB7G5WAYuKHnOChwwr2djFnop8cw0_2_nfCF8vbx77xX2AlP7B_6_raUgLSJMIVAACgYKAfwSARISFQHGX2Mi-RSYZYW96NKszfYqdeNchRoVAUF8yKrzrg_Kt1BiS7lY80bsrz8Y0076
+.youtube.com TRUE / TRUE 1767354772 __Secure-3PSID g.a000qggpRHoB7G5WAYuKHnOChwwr2djFnop8cw0_2_nfCF8vbx77tPE_LT5FPyaRnKMwkBxtjgACgYKAUUSARISFQHGX2MiF6xJ93Hm0cpceCC34c2tEBoVAUF8yKrslR0SL49rsVQmf5ffxqj40076
+.youtube.com TRUE / FALSE 1767354772 HSID A86WDM3L_0nAqFvcP
+.youtube.com TRUE / TRUE 1767354772 SSID AQRd0DoXZWbIRcxo-
+.youtube.com TRUE / FALSE 1767354772 APISID zNM2Br_otKyVj-4g/AIB9K8PubIra0HcQC
+.youtube.com TRUE / TRUE 1767354772 SAPISID AKvKG-fNhLd-zOaH/A334ItCv4xUKGjcNu
+.youtube.com TRUE / TRUE 1767354772 __Secure-1PAPISID AKvKG-fNhLd-zOaH/A334ItCv4xUKGjcNu
+.youtube.com TRUE / TRUE 1767354772 __Secure-3PAPISID AKvKG-fNhLd-zOaH/A334ItCv4xUKGjcNu
+.youtube.com TRUE / TRUE 1767355048 LOGIN_INFO AFmmF2swRQIhALHJAgjRY1UiDzNhdHz2acO4WQ4yFr3KAiid56aZFMOvAiBgcqtUc-9mqACMPoSusVzIbL2Dh7rdhDDKxBh5BnItog:QUQ3MjNmekJpTnJoanJVUVdTeHJwNEtsMmtlMUcyYU42MnlQSlhZRksxWFZKa3VhTDFVQ2FRMkFmOXRHNFN0V0NvQVdUSVZwbjQwMHFPX2lzUk1rQkRfZlBxZkZfQ1dDZVZVYVgzeW5OR0JobTJrUnBWQ243bExpNUllYmc5VVZ1VmJ3WkZTQzFkcWF5NTFkS1l5QVFtMFM0N0k2dVhyazlB
+.youtube.com TRUE / TRUE 1769400437 PREF f6=40000000&tz=Asia.Jakarta&f7=100
+.youtube.com TRUE / TRUE 1766376485 __Secure-1PSIDTS sidts-CjEB7wV3sYY2Ctv7d7f10SKraCNQ_RA4mpyZIkqAfX4vhxqISeogBlT-pEH83NrpsicdEAA
+.youtube.com TRUE / TRUE 1766376485 __Secure-3PSIDTS sidts-CjEB7wV3sYY2Ctv7d7f10SKraCNQ_RA4mpyZIkqAfX4vhxqISeogBlT-pEH83NrpsicdEAA
+.youtube.com TRUE / FALSE 1766376485 SIDCC AKEyXzUTvj7yeE38uXM-tp2cKGgwXajEtkxb_mXpfg0i_LdiJWxwCClx1jSzPIAVdWyWAaHUog
+.youtube.com TRUE / TRUE 1766376485 __Secure-1PSIDCC AKEyXzVjSXfGVniXeRgp_d6xuMAsjgQ_nqfP5L_93ckZHMLJtHt3E5r3ZwDUHBAC-g1YWTuW1g
+.youtube.com TRUE / TRUE 1766376485 __Secure-3PSIDCC AKEyXzUzNHrrhdQLx_MxzkvC1YHaWehk9E7CvpEpwpMr6V-cx829Vg3VZJd9KyLTYXkRC7smag
+.youtube.com TRUE / TRUE 1750392405 VISITOR_INFO1_LIVE hsQnEvaoH2c
+.youtube.com TRUE / TRUE 1750392405 VISITOR_PRIVACY_METADATA CgJJRBIEGgAgGQ%3D%3D
+.youtube.com TRUE / TRUE 0 YSC uVxahClT7Xk
+.youtube.com TRUE / TRUE 1750392405 __Secure-ROLLOUT_TOKEN CJnL-J7WkPXOYhCZhcHf2IaKAxjx5-iDwLqKAw%3D%3D
diff --git a/genStr.py b/genStr.py
new file mode 100644
index 000000000000..26dd242ff451
--- /dev/null
+++ b/genStr.py
@@ -0,0 +1,9 @@
+from pyrogram import Client
+
+
+api_id = int(input("API ID: "))
+api_hash = input("API HASH: ")
+
+app = Client("my_app", api_id=api_id, api_hash=api_hash, in_memory=True)
+with app:
+ print(app.export_session_string())
diff --git a/heroku.yml b/heroku.yml
new file mode 100644
index 000000000000..86bcb3dc3be4
--- /dev/null
+++ b/heroku.yml
@@ -0,0 +1,5 @@
+build:
+ docker:
+ worker: Dockerfile
+run:
+ worker: bash start
\ No newline at end of file
diff --git a/requirements.txt b/requirements.txt
new file mode 100644
index 000000000000..9a70b8caa04a
--- /dev/null
+++ b/requirements.txt
@@ -0,0 +1,57 @@
+deep-translator
+openai
+aiofiles
+aiohttp
+asyncio
+beautifulsoup4
+dnspython
+ffmpeg-python
+gitpython
+hachoir
+heroku3
+lyricsgenius
+motor
+wget
+pycountry
+psutil
+pytube
+pykeyboard
+unidecode
+python-dotenv
+pyyaml
+requests
+speedtest-cli
+spotipy
+tgcrypto
+urllib3
+flask
+pyshorteners
+youtube-search
+youtube-search-python
+async_pymongo
+gpytranslate
+search_engine_parser
+emojis
+cloudscraper
+blackpink
+pycryptodome
+pickledb
+pyfiglet
+qrcode
+pytz
+pydub
+apscheduler
+gtts
+
+httpx==0.27.2
+gunicorn==22.0.0
+pillow==11.0.0
+pymongo==4.9.1
+py-tgcalls==0.9.7
+lexica-api==1.5.7
+#pymongo==3.12.0
+
+git+https://github.com/Mayuri-Chan/pyrofork
+git+https://github.com/yt-dlp/yt-dlp
+git+https://github.com/alexmercerind/youtube-search-python@main
+git+https://github.com/joetats/youtube_search@master
\ No newline at end of file
diff --git a/runtime.txt b/runtime.txt
new file mode 100644
index 000000000000..cf3b804237ed
--- /dev/null
+++ b/runtime.txt
@@ -0,0 +1 @@
+python-3.11.8
diff --git a/sample.env b/sample.env
new file mode 100644
index 000000000000..4ab941d548a7
--- /dev/null
+++ b/sample.env
@@ -0,0 +1,7 @@
+API_ID=
+API_HASH=
+BOT_TOKEN=
+LOGGER_ID=
+MONGO_DB_URI=
+OWNER_ID=
+STRING_SESSION=
diff --git a/setup b/setup
new file mode 100644
index 000000000000..601c729b0eba
--- /dev/null
+++ b/setup
@@ -0,0 +1,126 @@
+#!/bin/bash
+
+pprint (){
+ cred='\033[0;31m'
+ cgreen='\033[0;32m'
+ cyellow='\033[0;33m'
+ cblue='\033[0;34m'
+ cpurple='\033[0;35m'
+ eval "export color='$cpurple'"
+ [ ! -z $2 ] && eval "export color=\"\$$2\""
+ printf "$color $1"
+}
+
+color_reset(){ printf '\033[0;37m';}
+
+yesnoprompt(){
+ old_stty_cfg=$(stty -g)
+ stty raw -echo ; answer=$(head -c 1)
+ stty $old_stty_cfg
+ echo "$answer" | grep -iq "^y"
+}
+
+update() {
+ pprint "\n\nUpdating package list.. "
+ sudo apt update 2>&1 | grep "can be upgraded" &>/dev/null
+ if [ $? -eq 0 ]; then
+ pprint "UPDATE AVAILABLE" "cgreen"
+ pprint "\n\nDo you want to automatically upgrade (y/n)?"
+ if yesnoprompt; then
+ pprint "\n\nUpgrading packages.. "
+ sudo apt upgrade -y &>/dev/null &&
+ pprint "DONE!\n\n" "cgreen" || (pprint "FAIL.\n\n" "cred"; exit 1)
+ else
+ echo
+ fi
+ else
+ pprint "ALREADY UP TO DATE\n\n" "cgreen"
+ fi
+}
+
+packages(){
+ if ! command -v pip &>/dev/null; then
+ pprint "Couldn't found pip, installing now..."
+ sudo apt install python3-pip -y 2>pypilog.txt 1>/dev/null &&
+ pprint "SUCCESS.\n\n" "cgreen" || (pprint "FAIL.\n\n" "cred"; exit 1)
+ fi
+
+ if ! command -v ffmpeg &>/dev/null; then
+ pprint "Couldn't found ffmpeg, installing now..."
+ if sudo apt install ffmpeg -y &>/dev/null;then
+ pprint "SUCCESS.\n\n" "cgreen"
+ else
+ pprint "FAIL.\n\n" "cred"
+ pprint "You need to install ffmpeg manually in order to deploy SprotifyMusic, exiting...\n" "cblue"
+ exit 1
+ fi
+ fi
+
+ # Check ffmpeg version and warn user if necessary.
+ fv=$(grep -Po 'version (3.*?) ' <<< $(ffmpeg -version)) &&
+ pprint "Playing live streams not going to work since you have ffmpeg $fv, live streams are supported by version 4+.\n" "cblue"
+}
+
+
+node(){
+ command -v npm &>/dev/null && return
+ pprint "Installing Nodejs and Npm.. "
+ curl -fssL https://deb.nodesource.com/setup_19.x | sudo -E bash - &>nodelog.txt &&
+ sudo apt install -y nodejs &>>nodelog.txt &&
+ sudo npm i -g npm &>>nodelog.txt &&
+ pprint "SUCCESS!\n" "cgreen" || (pprint "FAIL.\n" "cred"; exit 1)
+}
+
+
+installation(){
+ pprint "\n\nUpgrading pip and installing dependency packages..."
+ pip3 install -U pip &>>pypilog.txt &&
+ pip3 install -U -r requirements.txt &>>pypilog.txt &&
+ pprint "DONE.\n" "cgreen" && return
+ pprint "FAIL.\n" "cred"
+ exit 1
+}
+
+clear
+pprint "Welcome to SprotifyMusic Setup Installer\n\n"
+pprint "If you see any error during Installation Process, Please refer to these files for logs: "
+pprint "\nFor node js errors , Checkout nodelog.txt"
+pprint "\nFor pypi packages errors , Checkout pypilog.txt"
+sleep 1
+pprint "\n\nScript needs sudo privileges in order to update & install packages.\n"
+sudo test
+
+update
+packages
+node
+installation
+pprint "\n\n\n\n\nAnonXMusic Installation Completed !" "cgreen"
+sleep 1
+clear
+
+pprint "\nEnter Your Values Below\n\n\n"
+pprint "API ID: "; color_reset; read api_id
+pprint "\nAPI HASH: "; color_reset; read api_hash
+pprint "\nBOT TOKEN: "; color_reset; read bot_token
+pprint "\nOWNER ID:"; color_reset; read ownid
+pprint "\nMONGO DB URI: "; color_reset; read mongo_db
+pprint "\nLOG GROUP ID: "; color_reset; read logger
+pprint "\nSTRING SESSION: "; color_reset; read string_session
+
+pprint "\n\nProcessing your vars, wait a while !" "cgreen"
+
+if [ -f .env ]; then
+ rm .env
+fi
+
+echo """API_ID = $api_id
+API_HASH = $api_hash
+BOT_TOKEN = $bot_token
+MONGO_DB_URI = $mongo_db
+LOGGER_ID = $logger
+STRING_SESSION = $string_session
+OWNER_ID = $ownid""" > .env
+clear
+
+pprint "\n\n\nThanks for using SprotifyMusic installer, your vars have been saved successfully ! \nIf you wanna add more variables add them in your env by : vi .env"
+pprint "\n\nNow you can start the bot by : bash start\n\n"
diff --git a/start b/start
new file mode 100644
index 000000000000..6cb8c39d8f37
--- /dev/null
+++ b/start
@@ -0,0 +1 @@
+python3 -m SprotifyMusic
diff --git a/strings/__init__.py b/strings/__init__.py
new file mode 100644
index 000000000000..1bbbe9f3995c
--- /dev/null
+++ b/strings/__init__.py
@@ -0,0 +1,37 @@
+import os
+from typing import List
+
+import yaml
+
+languages = {}
+commands = {}
+languages_present = {}
+
+
+def get_string(lang: str):
+ return languages[lang]
+
+def get_command(value: str) -> List:
+ return commands["command"][value]
+
+for filename in os.listdir(r"./strings/langs/"):
+ if "en" not in languages:
+ languages["en"] = yaml.safe_load(
+ open(r"./strings/langs/en.yml", encoding="utf8")
+ )
+ languages_present["en"] = languages["en"]["name"]
+ if filename.endswith(".yml"):
+ language_name = filename[:-4]
+ if language_name == "en":
+ continue
+ languages[language_name] = yaml.safe_load(
+ open(r"./strings/langs/" + filename, encoding="utf8")
+ )
+ for item in languages["en"]:
+ if item not in languages[language_name]:
+ languages[language_name][item] = languages["en"][item]
+ try:
+ languages_present[language_name] = languages[language_name]["name"]
+ except:
+ print("There is some issue with the language file inside bot.")
+ exit()
diff --git a/strings/helpers.py b/strings/helpers.py
new file mode 100644
index 000000000000..3020aa1abc35
--- /dev/null
+++ b/strings/helpers.py
@@ -0,0 +1,122 @@
+HELP_1 = """
+>> Admin Commands
+┌ pause - Pause the current **playing** stream.
+├ resume - Resume the **paused** stream.
+├ skip - Skip the current playing stream and start streaming the next track in queue.
+├ end or stop - Clears the queue and ends the current playing stream.
+├ player - Get an interactive player panel.
+├ queue - Shows the queued tracks list.
+├ speed or /playback - Adjust the audio playback speed in the group.
+└ cspeed or /cplayback - Adjust the audio playback speed in the channel.
+
+>> Loop Stream
+┌ /loop [enable/disable] - Enables/disables loop for the ongoing stream.
+└ /loop [1, 2, 3, ...] - Enables the loop for the given value.
+
+>> Shuffle Queue
+┌ /shuffle - Shuffle the queue.
+└ /queue - Shows the shuffled queue.
+
+>> Seek Stream
+┌ /seek [duration in seconds] - Seek the stream to the given duration.
+└ /seekback [duration in seconds] - Backward seek the stream to the given duration.
+
+>> Powered by Sprotify Music
+"""
+
+HELP_2 = """
+>> Auth Users
+┌ /auth [username/user_id] - Add a user to the bot's auth list.
+├ /unauth [username/user_id] - Remove a user from the bot's auth list.
+└ /authusers - Show the list of authorized users.
+
+>> Powered by Sprotify Music
+"""
+
+HELP_3 = """
+>> Broadcast Feature
+┌ /broadcast [message or reply to a message] - Broadcast a message to the served chats of the bot.
+├ -pin - Pins your broadcasted messages in served chats.
+├ -pinloud - Pins your broadcasted message in served chats and sends notifications to members.
+├ -user - Broadcasts the message to the users who have started your bot.
+├ -assistant - Broadcast your message from the assistant account of the bot.
+└ -nobot - Forces the bot not to broadcast the message.
+
+Example: /broadcast -user -assistant -pin Testing Broadcast
+
+>> Powered by Sprotify Music
+"""
+
+HELP_4 = """
+>> Chat Blacklist Feature
+┌ /blacklistchat [chat_id] - Blacklist a chat from using the bot.
+├ /whitelistchat [chat_id] - Whitelist a blacklisted chat.
+└ /blacklistedchat - Shows the list of blacklisted chats.
+
+>> Block Users
+┌ /block [username or reply to a user] - Block the user from the bot.
+├ /unblock [username or reply to a user] - Unblock the user.
+└ /blockedusers - Shows the list of blocked users.
+
+>> Global Ban Feature
+┌ /gban [username or reply to a user] - Globally ban a user from all served chats.
+├ /ungban [username or reply to a user] - Globally unban a user.
+└ /gbannedusers - Shows the list of globally banned users.
+
+>> Maintenance Mode
+┌ /logs - Get logs of the bot.
+├ /logger [enable/disable] - Enable or disable logging of the bot activities.
+└ /maintenance [enable/disable] - Enable or disable maintenance mode of the bot
+
+>> Powered by Sprotify Music
+"""
+
+HELP_5 = """
+>> Play Commands
+┌ v - Stands for video play.
+├ force - Stands for force play.
+├ /play or /vplay - Starts streaming the requested track on video chat.
+└ /playforce or /vplayforce - Stops the ongoing stream and starts streaming the requested track.
+
+>> Channel Play Commands
+┌ /cplay - Starts streaming the requested track in the linked channel.
+├ /cplayforce or /cvplayforce - Stops the ongoing stream and starts streaming the requested track.
+└ /channelplay [chat username or ID] or [disable] - Connect a channel to a group and start streaming tracks by commands sent in the group.
+
+Powered by Sprotify Music
+"""
+
+HELP_6 = """
+>> Song Download
+
+┌ /song [song name] - Download any track from Spotify in MP4 format /song [song name] - Download any track from Spotify in MP4 format
+└ Note: This Plugin's still bug, dev too lazy to fix it.
+
+Powered by Sprotify Music
+"""
+
+HELP_7 = """
+>> Create Quotly
+
+┌ /q - Create a quote from the message.
+└ add r to quote reply message
+
+Powered by Sprotify Music
+"""
+
+HELP_8 = """
+>> Sticker Features
+
+┌ /stickerid - Reply to a sticker to get its file ID.
+└ /kang - Reply to a sticker to add it to your pack.
+
+Powered by Sprotify Music
+"""
+
+HELP_9 = """
+>> Image Features
+
+⬤ /image - Provide a query to scrape an image from Google.
+
+Powered by Sprotify Music
+"""
\ No newline at end of file
diff --git a/strings/langs/ar.yml b/strings/langs/ar.yml
new file mode 100644
index 000000000000..55d0910d4493
--- /dev/null
+++ b/strings/langs/ar.yml
@@ -0,0 +1,315 @@
+name : 🇦🇪 عربي
+
+
+general_1 : "» قم بالرد على رسالة المستخدم أو قم بتقديم اسم المستخدم / معرف المستخدم."
+general_2 : "» حدث خطأ ما أثناء معالجة استعلامك.\n\nاستثناء: {0}
"
+general_3 : "أنت مشرف مجهول في هذه الدردشة، عد إلى حساب المستخدم لاستخدامي."
+general_4 : "» ليس لديك الصلاحيات لإدارة مكالمات الفيديو.\n\nقم بإعادة تحميل ذاكرة التخزين المؤقت للمشرفين عبر /reload"
+general_5 : "» البوت لا يقوم ببث مكالمات الفيديو."
+
+tg_1 : "{0} مُنزِّل\n\nحجم الملف: {1}\nاكتمل: {2}\nالنسبة المئوية: {3}%\n\nالسرعة: {4}/ث\nالتقدير الزمني: {5}"
+tg_2 : "تم التنزيل بنجاح، جاري معالجة الملف...\n\nالوقت المستغرق: {0}"
+tg_3 : "فشل في تنزيل الوسائط من تليجرام، يرجى المحاولة مرة أخرى..."
+tg_4 : "» تم تنزيل العنصر بالفعل."
+tg_5 : "» تم تنزيل العنصر بالفعل أو تم الإلغاء."
+tg_6 : "» تم إلغاء التنزيل."
+tg_7 : "» تم إلغاء التنزيل بواسطة: {0}"
+tg_8 : "فشل في إيقاف التنزيل."
+tg_9 : "فشل في الحصول على مهمة التنزيل الجارية..."
+
+call_1 : "» البوت يحتاج إذن دعوة المستخدمين عبر الرابط لدعوة المساعد إلى محادثتك."
+call_2 : "تم حظر مساعد {0} في مجموعتك/قناتك.\n\nالمعرّف : {1}
\nالاسم : {2}\nاسم المستخدم : @{3}\n\nيرجى إلغاء حظر المساعد والمحاولة مرة أخرى."
+call_3 : "فشل في دعوة مساعد {0} إلى محادثتك.\n\nالسبب : {1}
"
+call_4 : "يرجى الانتظار...\n\nجاري دعوة مساعد {0} إلى محادثتك..."
+call_5 : "تم انضمام مساعد {0} بنجاح.\n\nمحاولة بدء التشغيل..."
+call_6 : "» فشل في التبديل إلى البث، يُرجى استخدام /skip لتغيير المسار مرة أخرى."
+call_7 : "» جاري تنزيل المسار التالي من قائمة الانتظار.\n\nيرجى الانتظار..."
+call_8 : "لم يتم العثور على دردشة فيديو نشطة.\n\nيرجى بدء دردشة فيديو في مجموعتك/قناتك والمحاولة مرة أخرى."
+call_9 : "المساعد بالفعل في دردشة فيديو.\n\nإذا لم يكن المساعد في دردشة فيديو، يرجى إرسال /reboot
ثم اللعب مرة أخرى."
+call_10 : "خطأ في الخادم التيليجرام\n\nالتيليجرام يواجه مشكلات داخلية، يرجى المحاولة مرة أخرى أو إعادة تشغيل دردشة الفيديو في مجموعتك/قناتك."
+
+auth_1 : "» يمكنك فقط أن تمتلك 25 مستخدمًا مُصرَّحًا في مجموعتك."
+auth_2 : "» تمت إضافة {0} إلى قائمة المستخدمين المُصرَّح بهم."
+auth_3 : "{0} موجود بالفعل في قائمة المستخدمين المُصرَّح بهم."
+auth_4 : "» تمت إزالة {0} من قائمة المستخدمين المُصرَّح بهم."
+auth_5 : "{0} غير موجود في قائمة المستخدمين المُصرَّح بهم."
+auth_6 : "» جاري استرداد قائمة المستخدمين المُصرَّح بهم..."
+auth_7 : "» قائمة المستخدمين المُصرَّح بهم في {0}:\n\n"
+auth_8 : "↬ تمت الإضافة بواسطة:"
+
+reload_1 : "» يمكنك تحديث ذاكرة التخزين المؤقت للمشرفين فقط مرة واحدة كل 3 دقائق.\n\nيرجى المحاولة بعد {0}."
+reload_2 : "» تم تحديث ذاكرة التخزين المؤقت للمشرفين بنجاح."
+reload_3 : "» فشل في تحديث ذاكرة التخزين المؤقت للمشرفين، تأكد من أن البوت مشرف في محادثتك."
+reload_4 : "» يُرجى الانتظار...\n\nإعادة تشغيل {0} لمحادثتك."
+reload_5 : "تم إعادة تشغيل {0} بنجاح لمحادثتك.\n\nابدأ اللعب مرة أخرى..."
+
+admin_1 : "هل تذكر أنك استأنفت البث؟"
+admin_2 : "➻ تم إيقاف البث مؤقتًا 🎄\n│ \n└بواسطة: {0} 🥀"
+admin_3 : "هل تذكر أنك قمت بإيقاف البث؟"
+admin_4 : "➻ تم استئناف البث 🎄\n│ \n└بواسطة: {0} 🥀"
+admin_5 : "➻ انتهى/تم إيقاف البث 🎄\n│ \n└بواسطة: {0} 🥀"
+admin_6 : "➻ تم تخطي البث 🎄\n│ \n└بواسطة: {0} 🥀\n\n» لا توجد مقاطع في قائمة الانتظار في {1}، سأغادر الدردشة الفيديوية."
+admin_7 : "خطأ أثناء تغيير البث إلى {0}."
+admin_8 : "» يرجى تعطيل التكرار الدائري عبر /loop disable
ثم حاول الانتقال مرة أخرى."
+admin_9 : "يرجى استخدام أرقام محددة للانتقال، مثل 1، 2، 4..."
+admin_10 : "مطلوب على الأقل مقطعين في قائمة الانتظار للانتقال إلى المقطع المحدد.\n\nتحقق من قائمة الانتظار عبر: /queue"
+admin_11 : "» ليس هناك مقاطع كافية في قائمة الانتظار للانتقال إلى المقطع المحدد.\n\nيرجى الانتقال بين المقاطع من 1 إلى {0}"
+admin_12 : "» فشل في الانتقال إلى المقطع المحدد.\n\nتحقق من المقاطع المتبقية في قائمة الانتظار عبر: /queue"
+admin_13 : "» يرجى إعادة تحميل ذاكرة التخزين المؤقت للمشرف عبر: /reload"
+admin_14 : "» ليس لديك صلاحيات لإدارة دردشات الفيديو.\n\nيرجى إعادة تحميل ذاكرة التخزين المؤقت للمشرف عبر: /reload"
+admin_15 : "» فشل في عملية الخلط.\n\nتحقق من قائمة الانتظار عبر: /queue"
+admin_16 : "» تم خلط قائمة الانتظار بواسطة {0}.\n\nتحقق من قائمة الانتظار المختلطة عبر: /queue"
+admin_17 : "مثال:\n\n/loop enable
/disable
\n/loop 10
/9
/8
/7
"
+admin_18 : "» تم تمكين الحلقة لعدد {0}
مرات بواسطة: {1}."
+admin_19 : "» تم تعطيل تشغيل الحلقة بواسطة: {0}."
+admin_20 : "مثال:\n\n/seek or /seekback [المدة بالثواني]"
+admin_21 : "» يرجى استخدام أرقام رقمية لعملية البحث بالثواني."
+admin_22 : "» لا يمكن تغيير مكان بث الفيديو المباشر."
+admin_23 : "» حاول التحكم في البحث بمدة أقل.\n\nتم تشغيل {0} من أصل {1} دقيقة."
+admin_24 : "جارِ البحث...\n\nيرجى الانتظار..."
+admin_25 : "» تم تغيير مكان البث بنجاح.\n\nالمدة: {0} دقيقة\nبواسطة: {1}"
+admin_26 : "فشل في تغيير المكان."
+admin_27 : "» يمكن التحكم حالياً فقط في سرعة بث اليوتيوب."
+admin_28 : "لوحة تحكم سرعة {0}\n\nانقر على الأزرار أدناه لتغيير سرعة البث الحالي في دردشة الفيديو."
+admin_29 : "» البوت يعمل بالفعل بسرعة عادية."
+admin_30 : "» يرجى الانتظار...\n\nهناك شخص آخر يحاول تغيير سرعة البث."
+admin_31 : "جارِ تغيير السرعة..."
+admin_32 : "» يتم محاولة تغيير سرعة البث الحالي...\n\nطلب بواسطة: {0}"
+admin_33 : "» فشل في تغيير سرعة البث الحالي."
+admin_34 : "» تم تغيير سرعة البث الحالي إلى {0}x\n\nطلب بواسطة: {1}"
+admin_35 : "انتهت عملية التصويت لأن المقطع الذي تم تقديم التصويت له قد انتهى."
+admin_36 : "فشل في تنفيذ هذا الإجراء لأن المقطع الذي تم تقديم التصويت له قد انتهى أو تم إيقافه."
+admin_37 : "تم الحصول بنجاح على {0}
تصويت إيجابي."
+admin_38 : "» تمت إضافة تصويت إيجابي واحد."
+admin_39 : "» تمت إزالة تصويت إيجابي واحد."
+admin_40 : "تم التصويت بإيجابية."
+
+start_1 : "{0} على قيد الحياة يا صغيري.\n\n✫ وقت التشغيل : {1}"
+start_2 : "هلا {0}، 🥀\n\n๏ هذا هو {1} !\n\n➻ بوت تشغيل موسيقى سريع وقوي على تليجرام مع ميزات رائعة.\n\nالمنصات المدعومة: يوتيوب، سبوتيفاي، ريسو، آبل ميوزيك وساوندكلاود.\n──────────────────\n๏ انقر على زر المساعدة للحصول على معلومات حول وحداتي وأوامري."
+start_3 : "هلا {0}،\nهذا {1}\n\nشكرًا لإضافتي في {2}، {3} الآن يمكنه تشغيل الأغاني في هذه الدردشة."
+start_4 : "🎄 المطلوب سوبر جروب 🎄\n\nيرجى تحويل المجموعة الخاصة بك إلى سوبر جروب ثم أضفني مرة أخرى.\n\nكيفية جعلها سوبر جروب؟\n- اجعل تاريخ الدردشة الخاصة بمجموعتك مرئيًا مرة واحدة."
+start_5 : "↝ الدردشة في القائمة السوداء ↜\n\nتم إدراج هذه الدردشة في قاعدة بيانات {0} كدردشة في القائمة السوداء.\nقدم طلبًا لمستخدم سودو لإزالة الدردشة من القائمة السوداء أو قم بزيارة دردشة الدعم."
+start_6 : "😲 معلومات المقطع 😲\n\n📌 العنوان : {0}\n\n⏳ المدة : {1} دقيقة\n👀 المشاهدات : {2}
\n⏰ نشر في : {3}\n📎 القناة : {5}\n\n🥀 البحث مدعوم من قبل {6}"
+
+queue_1 : "» جاري جلب قائمة الانتظار...\n\nيرجى الانتظار..."
+queue_2 : "» قائمة الانتظار فارغة."
+queue_3 : "» انقر هنا للتحقق من قائمة المقاطع في الانتظار : هنا"
+queue_4 : "➲ تمت إضافته إلى قائمة الانتظار في الموقع رقم #{0}\n\n‣ العنوان : {1}\n‣ المدة : {2} دقيقة\n‣ طلب بواسطة : {3}"
+queue_5 : "هناك مقطع واحد فقط في قائمة الانتظار.\n\nأضف المزيد من المقاطع للتحقق من القائمة."
+queue_6 : "🕚 المدة : مدة البث غير معروفة\n\nانقر على الزر أدناه للحصول على قائمة الانتظار بالكامل."
+queue_7 : "\nانقر على الزر أدناه للحصول على قائمة الانتظار بالكامل."
+queue_8 : "مشغل {0}\n\n🎄 يتم البث حاليًا لـ : {1}\n\n🔗 نوع البث : {2}\n🥀 طلب بواسطة : {3}\n{4}"
+
+stream_1 : "➲ بدء البث |\n\n‣ العنوان : {1}\n‣ المدة : {2} دقيقة\n‣ طلب بواسطة : {3}"
+stream_2 : "➲ بدء البث |\n\n‣ نوع البث : بث مباشر [الرابط]\n‣ طلب بواسطة : {0}"
+
+help_1 : "اختر الفئة التي ترغب في الحصول على المساعدة لها.\nاستفسر عن شكواك في دردشة الدعم\n\nيمكن استخدام جميع الأوامر مع : /
"
+help_2 : "انقر على الزر أدناه للحصول على قائمة المساعدة في رسائلك الخاصة."
+
+lang_1 : "» يُرجى اختيار اللغة التي ترغب في تعيينها كلغة افتراضية لهذه المجموعة:"
+lang_2 : "تم تغيير اللغة بنجاح."
+lang_3 : "فشل في تغيير اللغة."
+lang_4 : "أنت بالفعل على نفس اللغة."
+
+setting_1 : "لوحة إعدادات {0}\n\nمعرّف الدردشة : {1}
\nعنوان الدردشة : {2}\n\nانقر على الأزرار أدناه لتغيير الإعدادات."
+setting_2 : "» مباشر : يشغل عمليات البحث مباشرةً.\n\n» مضمن : يعيد زرًا مضمنًا للاختيار بين الفيديو والصوت."
+setting_3 : "» الجميع : يمكن لأي شخص استخدام أوامر المشرف [تخطي، إيقاف مؤقت، استئناف وما إلى ذلك] في هذه المجموعة.\n\n» المشرفون فقط : يمكن فقط للمشرفين والمستخدمين المصرح لهم استخدام أوامر المشرف."
+setting_4 : "» لم يتم العثور على مستخدمين مصرح لهم."
+setting_5 : "» مجموعة : يشغل الموسيقى في المجموعة التي تم إعطاء الأمر فيها.\n\n» قناة : يشغل الموسيقى في القناة التي تريدها. قم بتعيين معرّف القناة عبر /channelplay"
+setting_6 : "» الجميع : يمكن لأي شخص حاضر في هذه المجموعة تشغيل الموسيقى هنا.\n\n» المشرفون فقط : يمكن فقط للمشرفين تشغيل الموسيقى في هذه المجموعة."
+setting_7 : "» يرجى تعريف معرف القناة عبر /channelplay"
+setting_8 : "عند تمكين هذا الوضع، يمكن للأشخاص الذين ليس لديهم صلاحيات المشرف استخدام أوامر المشرف بعد عدد معين من التصويتات."
+setting_9 : "التصويتات الحالية المطلوبة لاستخدام أوامر المشرف هي : {0}"
+setting_10 : "تم تعطيل وضع التصويت."
+setting_11 : "أدنى عدد للتصويتات يمكن أن يكون 2. لا يمكنك تعيينه أقل من 2"
+setting_12 : "أعلى عدد للتصويتات يمكن أن يكون 15. لا يمكنك تعيينه أعلى من 15"
+
+set_cb_1 : "جارٍ الحصول على لوحة مستخدمي المصرح لهم..."
+set_cb_2 : "جارٍ الحصول على لوحة وضع التشغيل..."
+set_cb_3 : "جاري تغيير الإعدادات..."
+set_cb_4 : "» جاري الحصول على قائمة المستخدمين المصرح لهم..."
+set_cb_5 : "» الرجاء الانتظار..."
+
+gstats_1 : "جارٍ الحصول على إحصائيات ومعلومات {0}...\n\nقد يستغرق الأمر بعض الوقت، يرجى الانتظار..."
+gstats_2 : "انقر على الأزرار أدناه للتحقق من إحصائيات {0}."
+gstats_3 : "إحصائيات ومعلومات {0} :\n\nالمساعدين : {1}
\nالمحظورون : {2}
\nالدردشات : {3}
\nالمستخدمون : {4}
\nالوحدات : {5}
\nالمشرفون : {6}
\n\nمغادرة تلقائية للمساعدين : {7}\nحدود مدة التشغيل : {8} دقائق"
+gstats_4 : "هذا الزر مخصص للمشرفين فقط."
+gstats_5 : "إحصائيات ومعلومات {0} :\n\nالوحدات : {1}
\nالمنصة : {2}
\nالذاكرة (RAM) : {3}
\nالنوى الفعلية : {4}
\nإجمالي النوى : {5}
\nتردد وحدة المعالجة المركزية : {6}
\n\nبيثون : {7}
\nPyrogram : {8}
\nPy-TgCalls : {9}
\n\nالتخزين المتاح : {10} جيبايت
\nالتخزين المستخدم : {11} جيبايت
\nالتخزين المتبقي : {12} جيبايت
\n\nالدردشات المخدومة : {13}
\nالمستخدمون المخدومون : {14}
\nالمستخدمون المحظورون : {15}
\nمستخدمو sudo : {16}
\n\nحجم قاعدة البيانات الإجمالي : {17} ميجابايت
\nسعة تخزين قاعدة البيانات الإجمالية : {18} ميجابايت
\nإجمالي مجموعات بيانات قاعدة البيانات : {19}
\nإجمالي مفاتيح قاعدة البيانات : {20}
"
+
+playcb_1 : "» آه، هذا ليس لك يا حبيبي."
+playcb_2 : "» جاري الحصول على النتيجة التالية،\n\nالرجاء الانتظار..."
+
+cplay_1 : "» يمكنك تشغيل الموسيقى في القنوات من {0} إلى أي قناة أخرى أو القناة المرتبطة بالدردشة.\n\nللقناة المرتبطة :\n/channelplay linked
\n\nلأي قناة أخرى :\n/channelplay [معرف القناة]
"
+cplay_2 : "» هذه الدردشة ليس لديها أي قناة مرتبطة."
+cplay_3 : "» تم تعيين القناة إلى {0}.\nمعرف القناة : {1}
"
+cplay_4 : "» فشل في الحصول على القناة.\n\nتأكد من أنك قمت بإضافة البوت إلى القناة وترقيته كمشرف."
+cplay_5 : "القنوات فقط مدعومة."
+cplay_6 : "» يجب أن تكون المالك للقناة {0} لتربطها بالمجموعة هذه.\nمالك القناة : @{1}\n\nبديلاً يمكنك ربط مجموعتك بتلك القناة ومن ثم محاولة الاتصال باستخدام /channelplay linked
"
+cplay_7 : "» تم تعطيل تشغيل القناة."
+
+play_1 : "🔎"
+play_2 : "➻ وضع تشغيل القناة\n\nجاري المعالجة، يرجى الانتظار...\n\nالقناة المرتبطة : {0}"
+play_3 : "» فشل في معالجة الاستعلام."
+play_4 : "تشغيل للمسؤولين فقط\nيُسمح فقط لمسؤولي هذه الدردشة بالتشغيل\n\nقم بتغيير وضع التشغيل عبر /playmode"
+play_5 : "» فشل في معالجة ملف الصوت.\n\nحجم ملف الصوت أكبر من الحد المحدد."
+play_6 : "» البث أطول من {0} غير مسموح بتشغيله على {1}"
+play_7 : "» ليس ملحق ملف فيديو صالح.\n\nالملحقات المدعومة : {0}"
+play_8 : "» يجب أن يكون حجم ملف الفيديو أقل من 1 جيجابايت."
+play_9 : "ميزة قائمة تشغيل يوتيوب\n\nحدد الوضع الذي تريد تشغيل قائمة تشغيل يوتيوب كاملة فيه."
+play_10 : "العنوان : {0}\nالمدة : {1} دقيقة"
+play_11 : "مشغل سبوتيفاي {0}\n\nالطلب من قِبل : {1}"
+play_12 : "مشغل أبل ميوزك {0}\n\nالطلب من قِبل : {1}"
+play_13 : "» تم الكشف عن بث مباشر.\n\nهل أنت متأكد أنك تريد تشغيل هذا البث المباشر؟"
+play_14 : "فشل في جلب تفاصيل المسار.\n\nجرب تشغيل أي مسار آخر."
+play_15 : "» فشل في معالجة الاستعلام.\n\nأستطيع فقط تشغيل مسارات سبوتيفاي والبومات وفنانين وقوائم تشغيل."
+play_16 : "لا يوجد دردشة صوتية نشطة.\n\nلاستخدام التشغيل القسري، يجب أن يكون هناك دردشة صوتية نشطة."
+play_17 : "يرجى تشغيل دردشة الفيديو، لا يمكنني تشغيل عناوين URL."
+play_18 : "الاستخدام: /play [اسم الأغنية/عنوان يوتيوب/الرد على ملف صوتي/فيديو]"
+play_19 : "قائمة التشغيل في الانتظار:"
+play_20 : "الموقف في قائمة الانتظار -"
+play_21 : "تمت إضافة {0} مسارات إلى القائمة في الانتظار.\n\nالتحقق: انقر هنا"
+play_22 : "حدد الوضع الذي تريد تشغيل الاستعلامات به داخل مجموعتك: {0}"
+
+str_1 : "يرجى تقديم روابط m3u8 أو index."
+str_2 : "➻ تم التحقق من البث الصالح.\n\nجاري المعالجة..."
+str_3 : "فشل في بث بث مباشر من يوتيوب، لم يتم العثور على تنسيق بث مباشر."
+
+ping_1 : "يتم عمل بينغ لـ {0}..."
+ping_2 : "🏓 البونج : {0}ms
\n\n{1} إحصائيات النظام :\n\n↬ وقت التشغيل : {2}\n↬ الذاكرة العشوائية (RAM) : {3}\n↬ المعالج (CPU) : {4}\n↬ القرص الصلب (Disk) : {5}\n↬ Py-TgCalls : {6}ms
"
+
+queue_1 : "» جاري جلب قائمة الانتظار...\n\nيرجى الانتظار..."
+queue_2 : "» قائمة الانتظار فارغة."
+queue_3 : "» انقر هنا للتحقق من قائمة المقاطع في الانتظار : هنا"
+queue_4 : "➲ تمت إضافته إلى قائمة الانتظار في الموقع رقم #{0}\n\n‣ العنوان : {1}\n‣ المدة : {2} دقيقة\n‣ طلب بواسطة : {3}"
+queue_5 : "هناك مقطع واحد فقط في قائمة الانتظار.\n\nأضف المزيد من المقاطع للتحقق من القائمة."
+queue_6 : "🕚 المدة : مدة البث غير معروفة\n\nانقر على الزر أدناه للحصول على قائمة الانتظار بالكامل."
+queue_7 : "\nانقر على الزر أدناه للحصول على قائمة الانتظار بالكامل."
+queue_8 : "مشغل {0}\n\n🎄 يتم البث حاليًا لـ : {1}\n\n🔗 نوع البث : {2}\n🥀 طلب بواسطة : {3}\n{4}"
+
+stream_1 : "➲ بدء البث |\n\n‣ العنوان : {1}\n‣ المدة : {2} دقيقة\n‣ طلب بواسطة : {3}"
+stream_2 : "➲ بدء البث |\n\n‣ نوع البث : بث مباشر [الرابط]\n‣ طلب بواسطة : {0}"
+
+CLOSE_BUTTON : "إغلاق"
+BACK_BUTTON : "رجوع"
+
+S_B_1 : "أضفني"
+S_B_2 : "الدعم"
+S_B_3 : "أضفني في مجموعتك"
+S_B_4 : "المساعدة والأوامر"
+S_B_5 : "المطور"
+S_B_6 : "القناة"
+S_B_7 : "الشيفرة المصدرية"
+S_B_8 : "👀 يوتيوب 👀"
+S_B_9 : "🥀 الدعم 🥀"
+
+H_B_1 : "مسؤل"
+H_B_2 : "المصادقة"
+H_B_3 : "إذاعة"
+H_B_4 : "سودو"
+H_B_5 : "مستخدم"
+H_B_6 : "أغنية"
+H_B_7 : "اقتباسات"
+H_B_8 : "ملصق"
+H_B_9 : "صورة"
+
+P_B_1 : "صوتي"
+P_B_2 : "فيديو"
+P_B_3 : "بث مباشر"
+P_B_4 : "عادي"
+
+ST_B_1 : "مستخدمين مصرح لهم"
+ST_B_2 : "وضع التشغيل"
+ST_B_3 : "اللغة"
+ST_B_4 : "وضع التصويت"
+ST_B_5 : "مشغل"
+ST_B_6 : "معطل"
+ST_B_7 : "مستخدمين مصرح لهم ➜"
+ST_B_8 : "مسؤولون"
+ST_B_9 : "الجميع"
+ST_B_10 : "وضع البحث ➜"
+ST_B_11 : "مباشر"
+ST_B_12 : "داخلي"
+ST_B_13 : "أوامر المسؤول ➜"
+ST_B_14 : "نوع التشغيل ➜"
+
+SA_B_1 : "إحصائيات عامة"
+SA_B_2 : "عام"
+SA_B_3 : "عامة"
+
+QU_B_1 : "قائمة الانتظار"
+QU_B_2 : " {0} —————————— {1}"
+
+sudo_1 : "» {0} موجود بالفعل في قائمة مستخدمي sudo."
+sudo_2 : "» تمت إضافة {0} إلى قائمة مستخدمي sudo."
+sudo_3 : "» {0} غير موجود في قائمة مستخدمي sudo."
+sudo_4 : "» تمت إزالة {0} من قائمة مستخدمي sudo."
+sudo_5 : "🥀 المالك :\n"
+sudo_6 : "\n✨ مستخدمو sudo :\n"
+sudo_7 : "» لا توجد مستخدمين sudo."
+sudo_8 : "فشل."
+
+block_1 : "» {0} محظور بالفعل من البوت."
+block_2 : "» تمت إضافة {0} إلى قائمة المستخدمين المحظورين."
+block_3 : "» {0} غير موجود في قائمة المستخدمين المحظورين."
+block_4 : "» تمت إزالة {0} من قائمة المستخدمين المحظورين."
+block_5 : "» لا توجد مستخدمين محظورين."
+block_6 : "» جاري الحصول على قائمة المستخدمين المحظورين..."
+block_7 : "😫 المستخدمون المحظورون :\n\n"
+
+black_1 : "مثال :\n\n/blacklistchat [معرّف الدردشة]"
+black_2 : "» هذه الدردشة محظورة بالفعل."
+black_3 : "» تمت إضافتها بنجاح إلى قائمة الدردشات المحظورة."
+black_4 : "مثال :\n\n/whitelistchat [معرّف الدردشة]"
+black_5 : "» هذه الدردشة غير محظورة."
+black_6 : "» تمت إزالتها بنجاح من قائمة الدردشات المحظورة."
+black_7 : "» قائمة الدردشات المحظورة :\n\n"
+black_8 : "» لا توجد دردشات محظورة على {0}."
+black_9 : "» حدث خطأ ما."
+
+maint_1 : "مثال :\n/maintenance [تمكين | تعطيل]"
+maint_2 : "» تم تمكين وضع الصيانة {0}."
+maint_3 : "» تم تعطيل وضع الصيانة {0}."
+maint_4 : "» وضع الصيانة مُمكّن بالفعل."
+maint_5 : "» وضع الصيانة مُعطّل بالفعل."
+
+log_1 : "مثال :\n/logger [تمكين | تعطيل]"
+log_2 : "تم تمكين تسجيل البيانات."
+log_3 : "تم تعطيل تسجيل البيانات."
+
+broad_1 : "» بدء البث..."
+broad_2 : "مثال :\n\n/broadcast [الرسالة أو الرد على رسالة]"
+broad_3 : "» تم بث الرسالة إلى {0} دردشة مع {1} تثبيتًا من البوت."
+broad_4 : "» تم بث الرسالة إلى {0} مستخدمين."
+broad_5 : "» بدء بث المساعد..."
+broad_6 : "➻ بث المساعد :\n\n"
+broad_7 : "↬ تم بث المساعد {0} في {1} دردشة."
+broad_8 : "» يُرجى تقديم نص للبث."
+
+server_1 : "» فشل في الحصول على السجلات."
+server_2 : "يرجى التأكد من تكوين مفتاح API هيروكو الخاص بك واسم التطبيق بشكل صحيح."
+server_3 : "التحقق من وجود تحديثات متاحة..."
+server_4 : "خطأ في أمر git."
+server_5 : "مستودع Git غير صالح."
+server_6 : "» البوت محدث."
+server_7 : "» تم تحديث البوت بنجاح! انتظر بضع دقائق حتى يتم إعادة تشغيل البوت ودمج التغييرات!"
+server_8 : "جاري إعادة تشغيل {0}...\n\nيمكنك البدء في التشغيل مرة أخرى بعد 15-20 ثانية."
+server_9 : "حدث خطأ ما، يُرجى التحقق من السجلات."
+server_10 : "حدث استثناء في #مُحدّث بسبب : {0}
"
+server_11 : "» جاري إجراء اختبار سرعة..."
+server_12 : "⇆ جاري اختبار سرعة التنزيل..."
+server_13 : "⇆ جاري اختبار سرعة الرفع..."
+server_14 : "↻ مشاركة نتائج اختبار السرعة..."
+server_15 : "✯ نتائج اختبار السرعة ✯\n\nالعميل :\n» عنوان ISP : {0}\n» البلد : {1}\n\nالخادم :\n» الاسم : {2}\n» البلد : {3}, {4}\n» الراعي : {5}\n» التأخير : {6}\n» التأخير في الشبكة : {7}"
+
+gban_1 : "» لماذا تريد حظر نفسك يا صغيري؟"
+gban_2 : "» لماذا يجب أن أحظر نفسي؟"
+gban_3 : "» لا يمكنك حظر المستخدمين المصرح لهم."
+gban_4 : "» تم حظر {0} بالفعل على مستوى البوت."
+gban_5 : "» جاري تنفيذ حظر عالمي على {0}.\n\nالزمن المتوقع: {1}"
+gban_6 : "حظر عالمي جديد على {0}:\n\nمنبعه من : {1} [{2}
]\nالمستخدم : {3}\nمعرّف المستخدم : {4}\n\nتم الحظر بواسطة : {5}\nالدردشات : {6}"
+gban_7 : "» {0} ليس محظورًا على مستوى البوت."
+gban_8 : "» رفع الحظر العالمي عن {0}.\n\nالزمن المتوقع : {1}"
+gban_9 : "» تم رفع الحظر العالمي عن {0}.\n\nتم إلغاء الحظر في {1} دردشة."
+gban_10 : "» لا يوجد أي مستخدم محظور على مستوى البوت."
+gban_11 : "» جاري جلب قائمة المستخدمين المحظورين على مستوى البوت..."
+gban_12 : "🙂 قائمة المستخدمين المحظورين عالميًا :\n\n"
diff --git a/strings/langs/en.yml b/strings/langs/en.yml
new file mode 100644
index 000000000000..afddce4fc48e
--- /dev/null
+++ b/strings/langs/en.yml
@@ -0,0 +1,312 @@
+name: 🇺🇸 English
+
+general_1: ">> Reply to a user's message or give username/user ID."
+general_2: ">> Something went wrong while processing your query.\n\n● Exception: {0}
"
+general_3: ">> You're an anonymous admin in this chat, revert back to user account for using me."
+general_4: ">> You don't have permissions to manage video chats.\n\n● Reload admin cache via /reload"
+general_5: ">> Bot isn't streaming on video chat."
+
+tg_1: ">> {0} Downloader ⏤͟͟͞͞★\n\n● File size: {1}\n● Completed: {2}\n● Percentage: {3}%\n\n● Speed: {4}/s\n● ETA: {5}"
+tg_2: ">> Successfully downloaded, processing file...\n\n● Time elapsed: {0}"
+tg_3: ">> Failed to download media from Telegram, please try again..."
+tg_4: ">> Download already completed."
+tg_5: ">> Download already completed or canceled."
+tg_6: ">> Download canceled."
+tg_7: ">> Download canceled by: {0}"
+tg_8: ">> Failed to stop the download."
+tg_9: ">> Failed to get the ongoing download task..."
+
+call_1: ">> Bot requires invite users via link permission to invite assistant to your chat."
+call_2: ">> {0} assistant is banned in your group/channel.\n\n● ID: {1}
\n● Name: {2}\n● Username: @{3}\n\n● Please unban the assistant and try again."
+call_3: ">> Failed to invite {0} assistant to your chat.\n\n● Reason: {1}
"
+call_4: ">> Please wait...\n\n● Inviting {0} assistant to your chat..."
+call_5: ">> {0} assistant joined successfully.\n\n● Trying to start stream..."
+call_6: ">> Failed to switch stream, please use /skip to change the track again."
+call_7: ">> Downloading next track from queue.\n\n● Please hold on..."
+call_8: ">> No active videochat found.\n\n● Please start videochat in your group/channel and try again."
+call_9: ">> Assistant already in videochat.\n\n● If assistant is not in videochat, please send /reboot
and play again."
+call_10: ">> Telegram server error\n\n● Telegram is having some internal problems, please try playing again or restart the videochat of your group."
+
+auth_1: ">> You can only have 25 authorized users in your group."
+auth_2: ">> Added {0} to authorized users list."
+auth_3: ">> {0} is already in authorized users list."
+auth_4: ">> Removed {0} from authorized users list."
+auth_5: ">> {0} is not in authorized users list."
+auth_6: ">> Fetching authorized users list..."
+auth_7: ">> List of authorized users in {0} ⏤͟͟͞͞★\n\n"
+auth_8: ">> Added by ⏤͟͟͞͞★"
+
+reload_1: ">> You can only refresh admin cache once in 3 minutes.\n\n● Please try after {0}."
+reload_2: ">> Admin cache refreshed successfully."
+reload_3: ">> Failed to reload admin cache, make sure the bot is admin in your chat."
+reload_4: ">> Please wait...\n\n● Rebooting {0} for your chat."
+reload_5: ">> Successfully rebooted {0} for your chat.\n\n● Start playing again..."
+
+admin_1: ">> Did you remember that you've resumed the stream?"
+admin_2: ">> Stream paused\n│\n└by: {0}"
+admin_3: ">> Did you remember that you've paused the stream?"
+admin_4: ">> Stream resumed\n│\n└by: {0}"
+admin_5: ">> Stream ended/stopped\n│\n└by: {0}"
+admin_6: ">> Stream skipped\n│\n└by: {0}\n\n● No more queued tracks in {1}, leaving videochat."
+admin_7: ">> Error while changing stream to {0}."
+admin_8: ">> Please disable loop play via /loop disable
and then try to skip again."
+admin_9: ">> Please use specific numbers for skip, like 1, 2, 4..."
+admin_10: ">> At least 2 tracks needed in queue for specific skip.\n\n● Check the queue by: /queue"
+admin_11: ">> Not enough tracks in queue for specific skip..."
+
+admin_12: ">> Failed to skip to specific track.\n\n● Check left queue by: /queue"
+admin_13: ">> Please reload admin cache via: /reload"
+admin_14: ">> You don't have permissions to manage video chats.\n\n● Reload admin cache via: /reload"
+admin_15: ">> Failed to shuffle.\n\n● Check queued shuffle by: /queue"
+admin_16: ">> Queue shuffled by {0}.\n\n● Check shuffled queue by: /queue"
+admin_17: ">> Example:\n\n● /loop enable
/disable
\n● /loop 10
/9
/8
/7
"
+admin_18: ">> Loop enabled for {0}
times by: {1}."
+admin_19: ">> Loop play has been disabled by: {0}."
+admin_20: ">> Example:\n\n● /seek or /seekback [duration in seconds]"
+admin_21: ">> Please use numeric digits for seeking in seconds."
+admin_22: ">> Live streams can't be seeked."
+admin_23: ">> Try seeking with a lower duration.\n\n● Played {0} out of {1} minutes."
+admin_24: ">> Seeking...\n\n● Please hold on..."
+admin_25: ">> Stream successfully seeked.\n\n● Duration: {0} minutes\n● By: {1}"
+admin_26: ">> Failed to seek."
+admin_27: ">> Only YouTube stream's speed can be controlled currently."
+admin_28: ">> {0} Speed Control Panel\n\n● Click on the buttons below to change the speed of currently playing stream on video chat."
+admin_29: ">> Bot is already playing on normal speed."
+admin_30: ">> Please wait...\n\n● Someone else is trying to change the speed of the stream."
+admin_31: ">> Changing speed..."
+admin_32: ">> Trying to change the speed of the ongoing stream...\n\n● Requested by: {0}"
+admin_33: ">> Failed to change the speed of the ongoing stream."
+admin_34: ">> Changed the speed of the ongoing stream to {0}x\n\n● Requested by: {1}"
+admin_35: ">> The voting has ended because the track has ended for which the voting was provided."
+admin_36: ">> Failed to perform this action because for which track the voting was provided is either ended or stopped."
+admin_37: ">> Successfully got {0}
upvotes."
+admin_38: ">> Added 1 upvote."
+admin_39: ">> Removed 1 upvote."
+admin_40: ">> Upvoted."
+
+start_1 : ">> {0} is alive baby.\n\n>> Uptime: {1}"
+
+start_2 : ">> Hey{0}, Nice to meet you.\n\n
● I am {1} bot.\n
● This is powerful music bot, For your group/channel.\n\n>> Tap on help button to see all my commands." + +start_3 : ">> Hey {0},\n● This is {1}\n\n● Thanks for adding me in {2}, {3} Can now play songs in this chat." + +start_4 : ">> Supergroup Needed >>\n\n● Please convert your Group to Supergroup and then add me again.\n\n● How to make Supergroup ?\n● Make your Group's chat history Visible once." + +start_5 : ">> Blacklisted Chat >>\n\n● This chat is blacklisted on {0} database.\n● Request a Sudo User to unblacklist your chat or visit Support Chat." + +start_6 : ">> 🥀Track Information >>\n\n● 📌Title: {0}\n\n● ⏳Duration: {1} minutes\n● 👀Views:
{2}
\n● ⏰Published on: {3}\n● Channel: {5}\n\n● Search Powered by: {6}"
+
+help_1 : ">> Choose the category for which you wanna get help.\n\n● Ask your doubts at: Support Chat" +help_2 : ">> Click on the button below to get my help menu in your PM." + +lang_1 : ">> Please choose the language which you wanna set as this group's default language" +lang_2 : ">> Language changed successfully." +lang_3 : ">> Failed to change language." +lang_4 : ">> You're already on the same language." + +setting_1 : ">> {0} Settings Panel\n\n● Chat ID:
{1}
\n● Chat Title: {2}\n\n● Click on the buttons below for changing settings."
+setting_2 : ">> Direct: Plays search queries directly.\n\n● Inline: Returns inline buttons for choosing between video & audio."
+setting_3 : ">> Everyone: Anyone can use admin commands [skip, pause, resume etc.] present."
+setting_4 : ">> Only Owner: Only owner of chat can control admin commands."
+setting_5 : ">> Clear Content: Clear all content of chat."
+setting_6 : ">> Timeout Oops. The error occurs in the command."
+setting_7 : ">> Configurecommands Configurecommands or create your own."
+setting_8 : ">> Do not try overdoot the error."
+setting_9 : ">> Current upvotes required for using admin commands are: {0}"
+setting_10 : ">> Voting mode is disabled."
+setting_11 : ">> Lowest upvotes count can be 2. You can't set below 2"
+setting_12 : ">> Highest upvotes count can be 15. You can't set above 15"
+
+set_cb_1 : ">> Getting auth users panel..."
+set_cb_2 : ">> Getting play mode panel..."
+set_cb_3 : ">> Setting up changes..."
+set_cb_4 : ">> Fetching authorized users list..."
+set_cb_5 : ">> Getting back..."
+
+gstats_1 : ">> Getting {0} stats and information...\n\n● It may take a while, please hold on..."
+gstats_2 : ">> Click on the buttons below to check the stats of: {0}."
+gstats_3 : ">> ˹ Tanu ꭙ Music™ ♡゙ Bot Statistics >>\n\n╭✠──────⊱◈◈◈⊰──────✠╮\n│● Assistants: {1}
\n│● Blocked: {2}
\n│● Chats: {3}
\n│● Users: {4}
\n│● Modules: {5}
\n│● Sudoers: {6}
\n╰✠──────⊱◈◈◈⊰──────✠╯\n\n● Auto leaving assistant: {7}\n● Play duration limit: {8} minutes\n\n>> "
+gstats_4 : ">> This button is only for sudoers."
+gstats_5 : ">> {0} Stats and Information ⏤͟͟͞͞★\n\n● Modules: {1}
\n● Platform: {2}
\n● RAM: {3}
\n● Physical Cores: {4}
\n● Total Cores: {5}
\n● CPU: {6}
\n\n● Python: {7}
\n● Pyrogram: {8}
\n● Py-TgCalls: {9}
\n\n● Storage Available: {10} GiB
\n● Storage Used: {11} GiB
\n● Storage Left: {12} GiB
\n\n● Served Chats: {13}
\n● Served Users: {14}
\n● Blocked Users: {15}
\n● Sudo Users: {16}
\n\n● Total DB Size: {17} MB
\n● Total DB Storage Used: {18} MB
"
+
+playcb_1 : ">> Aww, this is not for you baby."
+playcb_2 : ">> Getting next result,\n\n● Please wait..."
+
+cplay_1 : ">> You can play music in channels from {0} to any channel or your chat's linked channel.\n\n● For linked channel:\n● /channelplay linked
\n\n● For any other channel:\n/channelplay [channel id]
"
+cplay_2 : ">> This chat doesn't have any linked channel."
+cplay_3 : ">> Channel defined to {0}.\n● Channel ID: {1}
"
+cplay_4 : ">> Failed to get channel.\n\n● Make sure you've added the bot in your channel and promoted as admin."
+cplay_5 : ">> Only channels are supported."
+cplay_6 : ">> You need to be the owner of the channel {0} to connect it with this group.\n● Channel's owner: @{1}\n\n● Alternatively you can link your group to that channel and then try connecting with /channelplay linked
"
+cplay_7 : ">> Channel play disabled."
+
+play_1 : "🔎"
+play_2 : ">> Channel play mode\n\n● Processing, please wait...\n\n● Linked channel: {0}"
+play_3 : ">> Failed to process query."
+play_4 : ">> Admins only play\n● Only admins of this chat are allowed to play\n\n● Change play mode via: /playmode"
+play_5 : ">> Failed to process audio file.\n\n● Audio file size is larger than the defined limit."
+play_6 : ">> Stream's longer than {0} aren't allowed to play on {1}"
+play_7 : ">> Not a valid video file extension.\n\n● Supported extensions: {0}"
+play_8 : ">> Video file size should be less than 1GiB."
+play_9 : ">> YouTube playlist feature\n\n● Select the mode in which you want to play whole YouTube playlist."
+play_10 : ">> Title: {0}\n● Duration: {1} minutes"
+play_11 : ">> {0} Spotify player\n\n● Requested by: {1}"
+play_12 : ">> {0} Apple player\n\n● Requested by: {1}"
+play_13 : ">> Live stream detected.\n\n● Are you sure that you wanna play this live stream?"
+play_14 : ">> Failed to fetch track details.\n\n● Try playing any other."
+play_15 : ">> Failed to process query.\n\n● I can only play Spotify tracks, albums, artists and playlists."
+play_16 : ">> No active voice chat.\n\n● To use force play, there must be an active voicechat."
+play_17 : ">> Please turn on videochat, I'm unable to stream URLs."
+play_18 : ">> Usage: /play [song name/youtube url/reply to an audio/video file]"
+play_19 : ">> Queued Playlist :"
+play_20 : ">> Queued Position-"
+play_21 : ">> Added {0} tracks to queue.\n\n● Check: Click here"
+play_22 : ">> Select the mode in which you want to play the queries inside your group: {0}"
+
+str_1 : ">> Please provide m3u8 or index links."
+str_2 : ">> Valid stream verified.\n\n● Processing..."
+str_3 : ">> Failed to stream YouTube live stream, no live format found."
+
+ping_1 : "{0} is pinging..."
+ping_2 : "**Sprotify Music System Stats**\n\n✘ Ping: `{0} ms`\n✘ Uptime: {2}\n✘ RAM: {3}\n✘ CPU: {4}\n✘ Disk: {5}\n✘ Py-Tgcalls: `{6} ms`\n\n "
+
+queue_1 : ">> Fetching queue...\n\nPlease wait..."
+queue_2 : ">> Queue empty."
+queue_3 : ">> Click here to check the list of the queued tracks: here"
+queue_4 : ">> Next song added in queue: #{0}\n\n⬤ Title: {1}\n⬤ Duration: {2} minutes\n⬤ Requested by: {3}\n\n"
+queue_5 : ">> There's only one queued track in playlist.\n\n● Add more tracks to check list."
+queue_6 : ">> Duration: Unknown duration stream\n\n● Click on button below to get whole queued list."
+queue_7 : "\n>> Click on button below to get whole queued list."
+queue_8 : ">> {0} Player >>\n\n● Streaming: {1}\n\n● Stream type: {2}\n● Requested by: {3}\n{4}"
+
+stream_1 : ">> Sprotify Music started streaming ⏤͟͟͞͞★\n\n⬤ Title: {1}\n⬤ Duration: {2} minutes\n⬤ Requested by: {3}\n\n>> "
+stream_2 : ">> Sprotify Music started streaming ⏤͟͟͞͞★\n\n⬤ Stream type: Live stream [URL]\n● Requested by: {0}\n\n>> "
+
+
+CLOSE_BUTTON : "Close"
+BACK_BUTTON : "Back"
+
+S_B_1 : "Add me"
+S_B_2 : "Support"
+S_B_3 : "Add me baby"
+S_B_4 : "Help commands"
+S_B_5 : "Owner"
+S_B_6 : "Update"
+S_B_7 : "Repo"
+S_B_8 : "YouTube"
+S_B_9 : "Support"
+
+H_B_1 : "Admin"
+H_B_2 : "Auth"
+H_B_3 : "Broadcast"
+H_B_4 : "Sudo"
+H_B_5 : "User"
+H_B_6 : "Song"
+H_B_7 : "Quality"
+H_B_8 : "Sticker"
+H_B_9 : "Image"
+
+P_B_1 : "Audio"
+P_B_2 : "Video"
+P_B_3 : "Live stream"
+P_B_4 : "Play Playlist"
+
+
+ST_B_1 : "Auth users"
+ST_B_2 : "Play mode"
+ST_B_3 : "Language"
+ST_B_4 : "Voting mode"
+ST_B_5 : "On"
+ST_B_6 : "Off"
+ST_B_7 : "Auth users ➤"
+ST_B_8 : "Admins"
+ST_B_9 : "Everyone"
+ST_B_10 : "Search mode ➤"
+ST_B_11 : "Direct"
+ST_B_12 : "Inline"
+ST_B_13 : "Admin cmds ➤"
+ST_B_14 : "Play type ➤"
+
+SA_B_1 : "Overall stats"
+SA_B_2 : "General"
+SA_B_3 : "Overall"
+
+QU_B_1 : "Queue"
+QU_B_2 : " {0} —————————— {1}"
+
+sudo_1 : ">> {0} is already in sudo users list."
+sudo_2 : ">> Added {0} to sudo users list."
+sudo_3 : ">> {0} is not in sudo users list."
+sudo_4 : ">> Removed {0} from sudo users list."
+sudo_5 : ">> Owner:\n"
+sudo_6 : "\n>> Sudo users:\n"
+sudo_7 : ">> No sudo users found."
+sudo_8 : ">> Failed."
+
+block_1 : ">> {0} is already blocked from the bot."
+block_2 : ">> Added {0} to blocked users list."
+block_3 : ">> {0} is not in blocked users list."
+block_4 : ">> Removed {0} from blocked users list."
+block_5 : ">> No blocked users found."
+block_6 : ">> Getting blocked users list..."
+block_7 : ">> Blocked users:\n\n"
+
+black_1 : ">> Example:\n\n● /blacklistchat [Chat ID]"
+black_2 : ">> This chat is already blacklisted."
+black_3 : ">> Successfully added to blacklisted chats."
+black_4 : ">> Example:\n\n● /whitelistchat [Chat ID]"
+black_5 : ">> This chat is not blacklisted."
+black_6 : ">> Successfully removed from blacklisted chats."
+black_7 : ">> List of blacklisted chats:\n\n"
+black_8 : ">> No blacklisted chats on {0}."
+black_9 : ">> Something went wrong."
+
+maint_1 : ">> Example:\n● /maintenance [enable | disable]"
+maint_2 : ">> {0} maintenance mode enabled."
+maint_3 : ">> {0} maintenance mode disabled."
+maint_4 : ">> Maintenance mode is already enabled."
+maint_5 : ">> Maintenance mode is already disabled."
+
+log_1 : ">> Example:\n● /logger [enable | disable]"
+log_2 : ">> Enabled logging."
+log_3 : ">> Disabled logging."
+
+broad_1 : ">> Started broadcasting..."
+broad_2 : ">> Example:\n\n● /broadcast [Message or reply to a message]"
+broad_3 : ">> Broadcasted message to {0} chats with {1} pins from the bot."
+broad_4 : ">> Broadcasted message to {0} users."
+broad_5 : ">> Starting assistant broadcast..."
+broad_6 : ">> Assistant broadcast:\n\n"
+broad_7 : ">> Assistant {0} broadcasted in {1} chats."
+broad_8 : ">> Please provide some text to broadcast."
+
+server_1 : ">> Failed to get logs."
+server_2 : ">> Please make sure that your Heroku API key and app name are configured correctly."
+server_3 : ">> Checking for available updates..."
+server_4 : ">> Git command error."
+server_5 : ">> Invalid Git repository."
+server_6 : ">> Bot is up-to-date."
+server_7 : ">> Bot updated successfully! Now wait for a few minutes until the bot restarts and push changes!"
+server_8 : ">> {0} is restarting...\n\n● You can start playing again after 15-20 seconds."
+server_9 : ">> Something went wrong, please check logs."
+server_10 : ">> An exception occurred at #updater due to: {0}
"
+server_11 : "⇆ Running a speedtest..."
+server_12 : "⇆ Running download speedtest..."
+server_13 : "⇆ Running upload speedtest..."
+server_14 : "⇆ Sharing speedtest results..."
+server_15 : ">> Speedtest results >>\n\n● Client \n● ISP: {0}\n● Country: {1}\n\n● Server \n● Name: {2}\n● Country: {3}, {4}\n● Sponsor: {5}\n● Latency: {6}\n● Ping: {7}"
+
+gban_1 : ">> Why did you wanna gban yourself baby?"
+gban_2 : ">> Why should I gban myself?"
+gban_3 : ">> You can't gban my sudoers."
+gban_4 : ">> {0} is already globally banned from the bot."
+gban_5 : ">> Initializing global ban on {0}.\n\n● Time expected: {1}"
+gban_6 : ">> New global ban on {0}:\n\n● Originated from: {1} [{2}
]\n● User: {3}\n● User ID: {4}\n\n● Banned by: {5}\n● Chats: {6}"
+gban_7 : ">> {0} is not banned from the bot."
+gban_8 : ">> Lifting global ban from {0}.\n\n● Expected time: {1}"
+gban_9 : ">> Lifted global ban from {0}.\n\n● Unbanned in {1} chats."
+gban_10 : ">> No one is globally banned from the bot."
+gban_11 : ">> Fetching gbaned users list..."
+gban_12 : ">> Globally banned users:\n\n"
+
+signature : "☑ Powered by Sprotify Music"
\ No newline at end of file
diff --git a/strings/langs/hi.yml b/strings/langs/hi.yml
new file mode 100644
index 000000000000..7c91b3aa92b6
--- /dev/null
+++ b/strings/langs/hi.yml
@@ -0,0 +1,300 @@
+name : 🇮🇳 हिंदी
+
+
+general_1: "» एक उपयोगकर्ता के संदेश का उत्तर दें या उपयोगकर्ता नाम / उपयोगकर्ता ID दें।"
+general_2: "» कुछ गड़बड़ हो गई थी आपके क्वेरी को प्रोसेस करते समय।\n\nअपवाद: {0}
"
+general_3: "आप इस चैट में एक गुमनाम व्यवस्थापक हैं, मुझे उपयोग करने के लिए उपयोगकर्ता खाता पर वापस लौटें।"
+general_4: "» आपकी अनुमतियाँ वीडियो चैट प्रबंधित करने के लिए नहीं हैं।\n\nव्यवस्थापक कैश को पुनः लोड करें /reload के माध्यम से"
+general_5: "» बॉट वीडियोचैट पर स्ट्रीमिंग नहीं कर रहा है।"
+
+tg_1: "{0} डाउनलोडर\n\nफ़ाइल का आकार : {1}\nपूर्ण हो गया : {2}\nप्रतिशत : {3}%\n\nगति : {4}/s\nETA : {5}"
+tg_2: "सफलतापूर्वक डाउनलोड किया गया, फ़ाइल प्रोसेस की जा रही है...\n\nसमय गुजरा : {0}"
+tg_3: "मीडिया टेलीग्राम से डाउनलोड करने में विफल, कृपया पुनः प्रयास करें..."
+tg_4: "» पहले से ही डाउनलोड पूरा हो गया है।"
+tg_5: "» डाउनलोड पहले से ही पूरा हो गया है या रद्द किया गया है।"
+tg_6: "» डाउनलोड रद्द कर दिया गया है।"
+tg_7: "» डाउनलोड रद्द किया गया है द्वारा : {0}"
+tg_8: "डाउनलोड रोकने में विफल।"
+tg_9: "चल रहे डाउनलोड कार्य प्राप्त करने में विफल।"
+
+call_1: "» बोट को लिंक के माध्यम से उपयोगकर्ताओं को आमंत्रित करने की अनुमति की आवश्यकता होती है ताकि सहायक को आपकी चैट में आमंत्रित किया जा सके।"
+call_2: "{0} सहायक आपके समूह/चैनल में प्रतिबंधित है।\n\nआईडी : {1}
\nनाम : {2}\nउपयोगकर्ता नाम : @{3}\n\nकृपया सहायक को अनबैन करें और पुनः प्रयास करें।"
+call_3: "सहायक को आपकी चैट में आमंत्रित करने में विफल।\n\nकारण : {1}
"
+call_4: "कृपया प्रतीक्षा करें...\n\n{0} सहायक को आपकी चैट में आमंत्रित किया जा रहा है..."
+call_5: "{0} सहायक सफलतापूर्वक शामिल हो गया।\n\nप्रसारण शुरू करने का प्रयास किया जा रहा है..."
+call_6: "» प्रसारण स्विच करने में विफल। कृपया पुनः /skip का उपयोग करके ट्रैक को फिर से बदलें।"
+call_7: "» अगले ट्रैक को कतार से डाउनलोड किया जा रहा है।\n\nकृपया प्रतीक्षा करें..."
+call_8: "↬ द्वारा जोड़ा गया :"
+
+auth_1: "» आपके समूह में केवल 25 प्रमाणित उपयोगकर्ताएँ हो सकती हैं।"
+auth_2: "» {0} को प्रमाणित उपयोगकर्ताओं की सूची में जोड़ दिया गया है।"
+auth_3: "{0} पहले से ही प्रमाणित उपयोगकर्ताओं की सूची में है।"
+auth_4: "» {0} को प्रमाणित उपयोगकर्ताओं की सूची से हटा दिया गया है।"
+auth_5: "{0} प्रमाणित उपयोगकर्ताओं की सूची में नहीं है।"
+auth_6: "» प्रमाणित उपयोगकर्ताओं की सूची को पुनर्प्राप्त किया जा रहा है..."
+auth_7: "» {0} की प्रमाणित उपयोगकर्ताओं की सूची में :\n\n"
+auth_8: "↬ द्वारा जोड़ा गया :"
+
+reload_1: "» आपको केवल 3 मिनट में एक बार व्यवस्थापक कैश को ताज़ा करने की अनुमति होती है।\n\nकृपया {0} के बाद पुनः प्रयास करें।"
+reload_2: "» व्यवस्थापक कैश सफलतापूर्वक ताज़ा किया गया।"
+reload_3: "» व्यवस्थापक कैश को ताज़ा करने में विफल, सुनिश्चित करें कि बोट आपकी चैट में व्यवस्थापक है।"
+reload_4: "» कृपया प्रतीक्षा करें...\n\n{0} को आपकी चैट के लिए दोबारा शुरू किया जा रहा है।"
+reload_5: "{0} को आपकी चैट के लिए सफलतापूर्वक दोबारा आरंभ किया गया।\n\nफिर से प्रारंभ करने का प्रयास किया जा रहा है..."
+
+admin_1: "» क्या आपको याद है कि आपने प्रसारण को पुनः आरंभ किया था?"
+admin_2: "➻ प्रसारण ठहरा दिया 🎄\n│ \n└द्वारा : {0} 🥀"
+admin_3: "» क्या आपको याद है कि आपने प्रसारण को ठहराया था?"
+admin_4: "➻ प्रसारण पुनः आरंभ 🎄\n│ \n└द्वारा : {0} 🥀"
+admin_5: "➻ प्रसारण समाप्त/रोक दिया 🎄\n│ \n└द्वारा : {0} 🥀"
+admin_6: "➻ प्रसारण रोका 🎄\n│ \n└द्वारा : {0} 🥀\n\n» {1} में अब कोई और ट्रैक कतार में नहीं है, वीडियोचैट छोड़ रहे हैं।"
+admin_7: "स्ट्रीम {0} को बदलते समय त्रुटि।"
+admin_8: "» कृपया लूप प्ले को अक्षम करें और फिर से स्किप का प्रयास करें। /loop disable
के माध्यम से लूप प्ले को अक्षम करें।"
+admin_9: "कृपया स्किप के लिए विशिष्ट संख्याएँ उपयोग करें, जैसे 1, 2, 4..."
+admin_10: "कम से कम 2 ट्रैक्स की आवश्यकता होती है विशिष्ट स्किप के लिए।\n\nकृपया ट्रैक कतार देखें: /queue"
+admin_11: "» ट्रैक्स कतार में पर्याप्त नहीं है विशिष्ट स्किप के लिए।\n\nकृपया 1 और {0} के बीच स्किप करें।"
+admin_12: "» विशिष्ट ट्रैक पर स्किप करने में विफल।\n\nकृपया बचे हुए कतार की जाँच करें: /queue"
+admin_13: "» कृपया व्यवस्थापक कैश को ताज़ा करें: /reload"
+admin_14: "» वीडियो चैट प्रबंधित करने की अनुमति नहीं है।\n\nकृपया व्यवस्थापक कैश को ताज़ा करें: /reload"
+admin_15: "» शफल करने में विफल।\n\nकतार देखें: /queue"
+admin_16: "» कतार को शफल किया गया है {0} द्वारा।\n\nशफल किया गया कतार देखें: /queue"
+admin_17: "{0} स्पीड नियंत्रण पैनल\n\nकर्रेंट प्लेइंग स्ट्रीम की स्पीड बदलने के लिए नीचे दिए गए बटन पर क्लिक करें।"
+admin_18: "» {0} के लिए स्पीड चालू की गई है।\n\nद्वारा : {1}।"
+admin_19: "» स्पीड कंट्रोल बंद किया गया है।\n\nद्वारा : {0}।"
+admin_20: "उदाहरण:\n\n/seek या /seekback [सेकंड में अवधि]"
+admin_21: "» कृपया सेकंड में अवधि के लिए केवल न्यूमेरिक डिजिट्स का उपयोग करें।"
+admin_22: "» लाइव स्ट्रीम्स को सीक नहीं किया जा सकता।"
+admin_23: "» कृपया कम अवधि में सीक करने का प्रयास करें।\n\n{0} मिनटों में प्ले हुआ, {1} मिनटों की क़तार से।"
+admin_24: "सीक किया जा रहा है...\n\nकृपया प्रतीक्षा करें..."
+admin_25: "» स्पीड के साथ सफलतापूर्वक सीक किया गया।\n\nअवधि : {0} मिनटों\nद्वारा : {1}"
+admin_26: "सीक में विफल।"
+admin_27: "» केवल यूट्यूब स्ट्रीम की स्पीड को वर्तमान में नियंत्रित किया जा सकता है।"
+admin_28: "{0} स्पीड नियंत्रण पैनल\n\nकृपया वीडियो चैट पर प्लेइंग स्ट्रीम की स्पीड बदलने के लिए नीचे दिए गए बटनों पर क्लिक करें।"
+admin_29: "» बॉट पहले से ही सामान्य स्पीड पर प्ले कर रहा है।"
+admin_30: "» कृपया प्रतीक्षा करें...\n\nकोई अन्य व्यक्ति स्ट्रीम की स्पीड बदलने का प्रयास कर रहा है।"
+admin_31: "स्पीड बदल रहा है..."
+admin_32: "» चल रहे स्ट्रीम की स्पीड सफलतापूर्वक बदली गई।\n\nअनुरोधकर्ता : {0}"
+admin_33: "» चल रहे स्ट्रीम की स्पीड बदलने में विफल।"
+admin_34: "» चल रहे स्ट्रीम की स्पीड सफलतापूर्वक {0}x की गई।\n\nअनुरोधकर्ता : {1}"
+admin_35: "मतदान समाप्त हो गया है क्योंकि वोटिंग प्रदान किए जाने वाले ट्रैक के लिए समाप्त हो गया है।"
+admin_36: "इस क्रिया को करने में विफल क्योंकि वोटिंग प्रदान किए जाने वाले ट्रैक के लिए समाप्त हो गया है या वो रुका हुआ है।"
+admin_37: "सफलतापूर्वक {0}
अपवोट प्राप्त किए गए।"
+admin_38: "» 1 अपवोट जोड़ा गया।"
+admin_39: "» 1 अपवोट हटा दिया गया।"
+admin_40: "अपवोट किया गया।"
+
+start_1 : "{0} जिंदा है बेबी।\n\n✫ उपकाल : {1}"
+start_2 : "हे {0}, 🥀\n\n๏ यह {1} है !\n\n➻ एक तेज़ और शक्तिशाली टेलीग्राम संगीत प्लेयर बॉट जिसमें कुछ शानदार सुविधाएँ हैं।\n\nसमर्थित प्लेटफ़ॉर्म्स : यूट्यूब, स्पॉटिफ़ाई, रेसो, एप्पल म्यूज़िक और साउंडक्लाउड।\n──────────────────\n๏ मेरे मॉड्यूल्स और कमांड्स के बारे में जानकारी प्राप्त करने के लिए हेल्प बटन पर क्लिक करें।"
+start_3 : "हे {0},\nयह {1} है\n\nआपके द्वारा {2} में मुझे जोड़ने के लिए धन्यवाद, {3} अब इस चैट में गाने बजा सकता है।"
+start_4 : "🎄 सुपरग्रुप की आवश्यकता है 🎄\n\nकृपया अपने ग्रुप को सुपरग्रुप में बदलें और फिर मुझे फिर से जोड़ें।\n\nसुपरग्रुप कैसे बनाएं ?\n- अपने ग्रुप की चैट इतिहास को दृश्यमान बनाएं।"
+start_5 : "↝ ब्लैकलिस्ट की गई चैट ↜\n\nयह चैट {0} डेटाबेस पर ब्लैकलिस्ट है।\nअपनी चैट को अनब्लैकलिस्ट करने के लिए सुडो उपयोगकर्ता से अनुरोध करें या समर्थन चैट पर जाएं।"
+start_6 : "😲 ट्रैक जानकारी 😲\n\n📌 शीर्षक : {0}\n\n⏳ अवधि : {1} मिनट\n👀 दृश्य : {2}
\n⏰ प्रकाशित हुआ : {3}\n📎 चैनल : {5}\n\n🥀 सर्च पावर्ड बाय {6}"
+
+help_1 : "इस ग्रुप के लिए उपयोगकर्ता की मदद के लिए कृपया उपयोगकर्ता जाति को चुनें।\nअपने संदेहों को पूछें समर्थन चैट में\n\nसभी कमांड्स का उपयोग करने के लिए : /
"
+help_2 : "अपने प्राइवेट में मेरे मदद मेनू प्राप्त करने के लिए कृपया नीचे दिए गए बटन पर क्लिक करें।"
+
+lang_1 : "» कृपया ग्रुप की डिफ़ॉल्ट भाषा के रूप में सेट करने के लिए भाषा चुनें :"
+lang_2 : "भाषा सफलतापूर्वक बदल दी गई।"
+lang_3 : "भाषा बदलने में विफल।"
+lang_4 : "आप पहले से ही वही भाषा में हैं।"
+
+setting_1 : "{0} सेटिंग्स पैनल\n\nचैट आईडी : {1}
\nचैट टाइटल : {2}\n\nसेटिंग्स बदलने के लिए नीचे दिए गए बटनों पर क्लिक करें."
+setting_2 : "» डायरेक्ट: सीधे सर्च क्वेरी को चलाता है।\n\n» इनलाइन: वीडियो और ऑडियो के बीच चयन करने के लिए इनलाइन बटन्स प्रदान करता है।"
+setting_3 : "» सभी: किसी भी व्यक्ति को इस ग्रुप में एडमिन कमांड्स [स्किप, पॉज, रिज्यूम आदि] का उपयोग करने की अनुमति है।\n\n» केवल एडमिन: केवल एडमिन और अधिकृत उपयोगकर्ता एडमिन कमांड्स का उपयोग कर सकते हैं।"
+setting_4 : "» कोई अधिकृत उपयोगकर्ता नहीं मिले."
+setting_5 : "» ग्रुप: ग्रुप में संगीत बजाता है जहां कमांड दी जाती है।\n\n» चैनल: वह चैनल चुनें जिसमें संगीत बजना है। /channelplay के माध्यम से चैनल आईडी सेट करें"
+setting_6 : "» सभी: किसी भी व्यक्ति को इस ग्रुप में संगीत बजाने की अनुमति है।\n\n» केवल एडमिन: केवल एडमिन्स ही इस ग्रुप में संगीत बजा सकते हैं।"
+setting_7 : "» कृपया /channelplay के माध्यम से चैनल आईडी सेट करें"
+setting_8 : "जब यह मोड सक्षम होता है, तो वे लोग भी एडमिन कमांड्स का उपयोग कर सकते हैं जिनके पास एडमिन अधिकार नहीं होते हैं, एक निश्चित संख्या में वोट के बाद।"
+setting_9 : "वर्तमान उपवोट्स की आवश्यकता जो एडमिन कमांड्स का उपयोग करने के लिए हैं : {0}"
+setting_10 : "वोटिंग मोड अक्षम है।"
+setting_11 : "न्यूनतम उपवोट्स की गिनती 2 हो सकती है। आप 2 से कम नहीं सेट कर सकते"
+setting_12 : "अधिकतम उपवोट्स की गिनती 15 हो सकती है। आप 15 से अधिक नहीं सेट कर सकते"
+
+set_cb_1 : "आधीकृत उपयोगकर्ताओं का पैनल प्राप्त किया जा रहा है..."
+set_cb_2 : "प्ले मोड पैनल प्राप्त किया जा रहा है..."
+set_cb_3 : "सेटिंग्स बदल रहे हैं..."
+set_cb_4 : "» आधीकृत उपयोगकर्ता सूची प्राप्त की जा रही है..."
+set_cb_5 : "» पीछे जा रहे हैं..."
+
+gstats_1: "गेटिंग {0} स्टैट्स और इनफ़ोर्मेशन...\n\nइसमें समय लग सकता है, कृपया होल्ड ऑन..."
+gstats_2: "क्लिक ऑन थे बट्टन्स बेलोव तो चेक थे स्टैट्स ऑफ {0}."
+gstats_3: "{0} स्टैट्स और इनफ़ोर्मेशन :\n\nअसिस्टंट्स : {1}
\nब्लॉक्ड : {2}
\nचैट्स: {3}
\nयूज़र्स : {4}
\nमॉड्यूल्स : {5}
\nसुडोएर्स : {6}
\n\nऑटो लीविंग असिस्टंट : {7}\nप्ले दुरातिओन लिमिट : {8} मिनट्स"
+gstats_4: "इस बट्टन इस ओनली फॉर सुडोएर्स."
+gstats_5: "{0} स्टैट्स और इनफ़ोर्मेशन :\n\nमॉड्यूल्स : {1}
\nप्लैटफॉर्म : {2}
\nरैम : {3}
\nफिजिकल कोर्स : {4}
\nटोटल कोर्स : {5}
\nसीपीयू फ़्रेक्वेंसी : {6}
\n\nपायथन : {7}
\nपायरोग्राम : {8}
\nपाय-टीजीकैल्स : {9}
\n\nस्टोरेज अवेलेबल : {10} गीब
\nस्टोरेज यूज़्ड : {11} गीब
\nस्टोरेज लेफ्ट : {12} गीब
\n\nसर्व्ह्ड चैट्स : {13}
\nसर्व्ह्ड यूज़र्स : {14}
\nब्लॉक्ड यूज़र्स : {15}
\nसुडो यूज़र्स : {16}
\n\nटोटल डीबी साइज़ : {17} एम्बी
\nटोटल डीबी स्टोरेज : {18} एम्बी
\nटोटल डीबी कलेक्शन्स : {19}
\nटोटल डीबी कीज़ : {20}
"
+
+playcb_1: "» अव्व, थिस इस नोट फ़ॉर यू बेबी."
+playcb_2: "» गेटिंग नेक्स्ट रेसुल्ट,\n\nप्लीज़ वेट..."
+
+cplay_1: "» यू कैन प्ले म्यूज़िक इन चैनल्स फ्रोम {0} तो अन्य चैनल और योर चैट्स लिंकेड चैनल.\n\nफ़ॉर लिंकेड चैनल :\n/channelplay linked
\n\nफ़ॉर अन्य चैनल :\n/channelplay [चैनल आईडी]
"
+cplay_2: "» थिस चैट डोन'ट हेव अन्य लिंकेड चैनल."
+cplay_3: "» चैनल डिफ़ाइन्ड टू {0}.\nचैनल आईडी : {1}
"
+cplay_4: "» फ़ेल्ड टो गेट चैनल.\n\nमेक सुरे यू'वे अद्देड थे बोट इन योर चैनल एंड प्रोमोटेड अस अद्मिन."
+cplay_5: "ओन्ली चैनल्स आरे सुप्पोर्टेड."
+cplay_6: "» यू नीड टो बी थे ओव्नर ऑफ थे चैनल {0} टो कन्नेक्ट इट विथ थिस ग्रूप.\nचैनल'स ओव्नर : @{1}\n\nआल्टरनटिवली यू कैन लिंक योर ग्रूप टो थाट चैनल एंड थेन ट्राई कन्नेक्टिंग विथ /channelplay linked
"
+cplay_7: "» चैनल प्लेय डिसअब्लेड."
+
+play_1: "🔎"
+play_2: "➻ चैनल प्लेय मोड\n\nप्रोसेसिंग, प्लीज़ वेट...\n\nलिंकेड चैनल : {0}"
+play_3: "» फ़ेल्ड टो प्रोसेस क़ुएरी."
+play_4: "एड्मिन्स ओनली प्लेय\nओन्ली एड्मिन्स ऑफ थिस चैट आरे अल्लोवेड टो प्लेय\n\nचेंगे प्लेय मोड विआ /playmode"
+play_5: "» फ़ेल्ड टो प्रोसेस ऑडियो फ़िले.\n\nऑडियो फ़िले सिज़े इस लार्जर थान थे डिफ़ाइन्ड लिमिट."
+play_6: "» स्ट्रीम'स लोंगेर थान {0} आरें'ट अल्लोवेड टो प्लेय ऑन {1}"
+play_7: "» नोट अ वैलिड विडियो फ़िले एक्सटेंशन.\n\nसुप्पोर्टेड एक्सटेंशन्स : {0}"
+play_8: "» विडियो फ़िले सिज़े शोल्ड बी लेस थान 1गीब."
+play_9: "यूट्यूब प्लेयलिस्ट फीचर\n\nसेलेक्ट थे मोड इन व्हिच यू वॉन्ट टो प्लेय होले यूट्यूब प्लेयलिस्ट."
+play_10: "टाइटल : {0}\nड्युरेशन : {1} मिनट्स"
+play_11: "{0} स्पोटिफ़ाई प्लेयर\n\nरिक्वेस्टेड बाय : {1}"
+play_12: "{0} अप्प्ले प्लेयर\n\nरिक्वेस्टेड बाय : {1}"
+play_13: "» लाइव स्ट्रीम डिटेक्टेड.\n\nआरे यू सुरे थाट यू वॉन्ट टो प्लेय लाइव स्ट्रीम इन थिस चैनल?"
+play_14: "ट्रैक डिटेल्स प्राप्त करने में विफल।\n\nकृपया कोई अन्य ट्रैक प्ले करने का प्रयास करें।"
+play_15: "» क्वेरी प्रोसेस करने में विफल।\n\nमैं केवल स्पॉटिफाई ट्रैक्स, एल्बम्स, कलाकारों और प्लेलिस्ट्स प्ले कर सकता हूँ।"
+play_16: "कोई सक्रिय वॉयस चैट नहीं।\n\nफोर्स प्ले का उपयोग करने के लिए किसी सक्रिय वॉयस चैट होनी चाहिए।"
+play_17: "कृपया वीडियो चैट ऑन करें, मैं URL्स को स्ट्रीम करने में असमर्थ हूँ।"
+play_18: "उपयोग: /play [गाने का नाम/YouTube URL/ऑडियो/वीडियो फ़ाइल का उत्तर]"
+play_19: "कतार में जोड़ा गया प्लेलिस्ट:"
+play_20: "कतार में स्थिति-"
+play_21: "{0} ट्रैक्स को कतार में जोड़ा गया।\n\nजाँच करें: यहाँ क्लिक करें"
+play_22: "आपके ग्रुप के अंदर क्यूइड सूचियों को प्ले करने के लिए आपके द्वारा चुने गए मोड: {0}"
+
+str_1 : "कृपया m3u8 या इंडेक्स लिंक्स प्रदान करें।"
+str_2 : "➻ मान्य स्ट्रीम सत्यापित।\n\nप्रोसेसिंग..."
+str_3 : "यूट्यूब लाइव स्ट्रीम प्ले करने में विफल। कोई लाइव प्रारूप नहीं मिला।"
+
+ping_1 : "{0} पिंग कर रहा है..."
+ping_2 : "🏓 पोंग : {0}ms
\n\n{1} सिस्टम स्थिति :\n\n↬ अपटाइम : {2}\n↬ रैम : {3}\n↬ सीपीयू : {4}\n↬ डिस्क : {5}\n↬ पायथन-टीजीसीएल : {6}ms
"
+
+queue_1 : "» कतार प्राप्त हो रही है...\n\nकृपया प्रतीक्षा करें..."
+queue_2 : "» कतार खाली है।"
+queue_3 : "» कतार में ट्रैकों की सूची देखने के लिए यहाँ क्लिक करें : यहाँ"
+queue_4 : "➲ कतार में जोड़ा गया #{0}\n\n‣ शीर्षक : {1}\n‣ अवधि : {2} मिनट\n‣ द्वारा अनुरोधित : {3}"
+queue_5 : "केवल एक कतार में ट्रैक है।\n\nलिस्ट देखने के लिए अधिक ट्रैक जोड़ें।"
+queue_6 : "🕚 अवधि : अज्ञात स्ट्रीम\n\nपूरी कतार की सूची प्राप्त करने के लिए नीचे बटन पर क्लिक करें।"
+queue_7 : "\nनीचे बटन पर क्लिक करें।"
+queue_8 : "{0} प्लेयर\n\n🎄 स्ट्रीमिंग : {1}\n\n🔗 स्ट्रीम प्रकार : {2}\n🥀 अनुरोधक : {3}\n{4}"
+
+stream_1 : "➲ स्ट्रीमिंग शुरू की गई |\n\n‣ शीर्षक : {1}\n‣ अवधि : {2} मिनट\n‣ द्वारा अनुरोधित : {3}"
+stream_2 : "➲ स्ट्रीमिंग शुरू की गई |\n\n‣ स्ट्रीम प्रकार : लाइव स्ट्रीम [URL]\n‣ द्वारा अनुरोधित : {0}"
+
+CLOSE_BUTTON : "बंद करें"
+BACK_BUTTON : "वापस"
+
+S_B_1 : "मुझे जोड़ें"
+S_B_2 : "समर्थन"
+S_B_3 : "मुझे अपने समूह में जोड़ें"
+S_B_4 : "मदद और कमांड्स"
+S_B_5 : "डेवलपर"
+S_B_6 : "चैनल"
+S_B_7 : "स्रोत कोड"
+S_B_8 : "👀 YouTube 👀"
+S_B_9 : "🥀 समर्थन 🥀"
+
+H_B_1 : "एडमिन"
+H_B_2 : "ऑथ"
+H_B_3 : "ब्रॉडकास्ट"
+H_B_4 : "सुड"
+H_B_5 : "उपयोगकर"
+H_B_6 : "गाना"
+H_B_7 : "उद्धृत"
+H_B_8 : "कँटिया"
+H_B_9 : "छवि"
+
+P_B_1 : "ऑडियो"
+P_B_2 : "वीडियो"
+P_B_3 : "लाइव स्ट्रीम"
+P_B_4 : "नॉर्मल"
+
+ST_B_1 : "ऑथ उपयोगकर्ता"
+ST_B_2 : "प्ले मोड"
+ST_B_3 : "भाषा"
+ST_B_4 : "वोटिंग मोड"
+ST_B_5 : "चालू"
+ST_B_6 : "बंद"
+ST_B_7 : "ऑथ उपयोगकर्ता ➜"
+ST_B_8 : "व्यवस्थापक"
+ST_B_9 : "सभी"
+ST_B_10 : "खोज मोड ➜"
+ST_B_11 : "प्रत्यक्ष"
+ST_B_12 : "इनलाइन"
+ST_B_13 : "व्यवस्थापक कमांड ➜"
+ST_B_14 : "प्ले प्रकार ➜"
+
+SA_B_1 : "कुल स्थितियाँ"
+SA_B_2 : "सामान्य"
+SA_B_3 : "कुल"
+
+QU_B_1 : "कतार"
+QU_B_2 : " {0} —————————— {1}"
+
+sudo_1 : "» {0} पहले से ही सुडो उपयोगकर्ताओं की सूची में है।"
+sudo_2 : "» {0} को सुडो उपयोगकर्ताओं की सूची में जोड़ दिया गया।"
+sudo_3 : "» {0} सुडो उपयोगकर्ताओं की सूची में नहीं है।"
+sudo_4 : "» {0} को सुडो उपयोगकर्ताओं की सूची से हटा दिया गया।"
+sudo_5 : "🥀 मालिक :\n"
+sudo_6 : "\n✨ सुडो उपयोगकर्ताएँ :\n"
+sudo_7 : "» सुडो उपयोगकर्ताएँ नहीं मिलीं।"
+sudo_8 : "विफल।"
+
+block_1 : "» {0} पहले से ही ब्लॉक किया गया है।"
+block_2 : "» {0} को ब्लॉक की सूची में जोड़ दिया गया।"
+block_3 : "» {0} ब्लॉक की सूची में नहीं है।"
+block_4 : "» {0} को ब्लॉक की सूची से हटा दिया गया।"
+block_5 : "» ब्लॉक की सूची में कोई उपयोगकर्ता नहीं मिला।"
+block_6 : "» ब्लॉक की सूची मिल रही है..."
+block_7 : "😫 ब्लॉक की सूची :\n\n"
+
+black_1 : "उदाहरण :\n\n/blacklistchat [चैट आईडी]"
+black_2 : "» यह चैट पहले से ही ब्लैकलिस्ट की सूची में है।"
+black_3 : "» सफलतापूर्वक ब्लैकलिस्ट में जोड़ दिया गया।"
+black_4 : "उदाहरण :\n\n/whitelistchat [चैट आईडी]"
+black_5 : "» यह चैट ब्लैकलिस्ट की सूची में नहीं है।"
+black_6 : "» सफलतापूर्वक ब्लैकलिस्ट से हटा दिया गया।"
+black_7 : "» ब्लैकलिस्ट की सूची :\n\n"
+black_8 : "» {0} के ऊपर कोई चैट ब्लैकलिस्ट की सूची में नहीं है।"
+black_9 : "» कुछ गड़बड़ हो गई।"
+
+maint_1 : "उदाहरण :\n/maintenance [on|off]"
+maint_2 : "» {0} ने मेंटेनेंस मोड सक्रिय किया।"
+maint_3 : "» {0} ने मेंटेनेंस मोड अक्षम किया।"
+maint_4 : "» मेंटेनेंस मोड पहले से ही सक्रिय है।"
+maint_5 : "» मेंटेनेंस मोड पहले से ही अक्षम है।"
+
+log_1 : "उदाहरण :\n/logger [on|off]"
+log_2 : "लॉगिंग सक्रिय किया"
+
+broad_1 : "» प्रसारण शुरू किया जा रहा है..."
+broad_2 : "उदाहरण :\n\n/broadcast [संदेश या किसी संदेश के उत्तर में]"
+broad_3 : "» संदेश को {0} चैट्स में प्रसारित किया गया, {1} पिन से बोट से।"
+broad_4 : "» संदेश को {0} उपयोगकर्ताओं को प्रसारित किया गया।"
+broad_5 : "» सहायक प्रसारण शुरू कर रहा है..."
+broad_6 : "➻ सहायक प्रसारण :\n\n"
+broad_7 : "↬ सहायक ने {0} चैटों में प्रसारित किया, {1} से।"
+broad_8 : "» कृपया प्रसारित करने के लिए कुछ टेक्स्ट प्रदान करें।"
+
+server_1 : "» लॉग्स प्राप्त करने में विफल हुआ।"
+server_2 : "कृपया सुनिश्चित करें कि आपका Heroku API कुंजी और ऐप का नाम सही रूप से कॉन्फ़िगर किए गए हैं।"
+server_3 : "उपलब्ध अपडेटों की जांच की जा रही है..."
+server_4 : "git कमांड त्रुटि।"
+server_5 : "अमान्य git रिपॉजिटरी।"
+server_6 : "» बोट अप-टू-डेट है।"
+server_7 : "» बोट को सफलतापूर्वक अपडेट किया गया! अब कुछ मिनटों तक प्रतीक्षा करें जब तक बोट पुनरारंभ और परिवर्तनों को पुश नहीं करता है!"
+server_8 : "{0} रीस्टार्ट हो रहा है...\n\n15-20 सेकंड के बाद आप फिर से खेलना शुरू कर सकते हैं।"
+server_9 : "कुछ गलत हो गया, कृपया लॉग जांचें।"
+server_10 : "{0} पर एक अपवाद हुआ #अपडेटर में : {0}
"
+server_11 : "» एक स्पीड टेस्ट चल रहा है..."
+server_12 : "⇆ डाउनलोड स्पीड टेस्ट चल रहा है..."
+server_13 : "⇆ अपलोड स्पीड टेस्ट चल रहा है..."
+server_14 : "↻ स्पीड टेस्ट परिणाम साझा किए जा रहे हैं..."
+server_15 : "✯ स्पीड टेस्ट परिणाम ✯\n\nग्राहक :\n» आईपी : {0}\n» देश : {1}\n\nसर्वर :\n» नाम : {2}\n» देश : {3}, {4}\nस्पॉन्सर : {5}\nलैटेंसी : {6}\nपिंग : {7}"
+
+gban_1 : "» तुमने क्यों खुद को ग्लोबल बैन करना चाहा बेबी?"
+gban_2 : "» मैं क्यों खुद को ग्लोबल बैन करूँ?"
+gban_3 : "» तुम मेरे सुडोएर्स को ग्लोबल बैन नहीं कर सकते।"
+gban_4 : "» {0} पहले से ही बोट से ग्लोबल बैन है।"
+gban_5 : "» {0} पर ग्लोबल बैन को आरंभ किया जा रहा है।\n\nप्रत्याशित समय: {1}"
+gban_6 : "नया ग्लोबल बैन {0} पर:\n\nमूल श्रोत से: {1} [{2}
]\nउपयोगकर्ता: {3}\nउपयोगकर्ता आईडी: {4}\n\nद्वारा बैन किया गया: {5}\nचैट्स: {6}"
+gban_7 : "» {0} बोट से ग्लोबल बैन नहीं है।"
+gban_8 : "» {0} से ग्लोबल बैन को हटाया जा रहा है।\n\nप्रत्याशित समय: {1}"
+gban_9 : "» {0} से ग्लोबल बैन हटाया गया है।\n\n{1} चैट्स में अनबैन हो गया है।"
+gban_10 : "» किसी भी उपयोगकर्ता को बोट से ग्लोबल बैन नहीं किया गया है।"
+gban_11 : "» ग्लोबल बैन उपयोगकर्ता सूची प्राप्त की जा रही है..."
+gban_12 : "🙂 ग्लोबल बैन उपयोगकर्ता :\n\n"
diff --git a/strings/langs/pa.yml b/strings/langs/pa.yml
new file mode 100644
index 000000000000..e7b19242a015
--- /dev/null
+++ b/strings/langs/pa.yml
@@ -0,0 +1,302 @@
+name : 🇮🇳 ਪੰਜਾਬੀ
+
+general_1 : "» ਉਪਭੋਗਤਾ ਦੇ ਸੁਨੇਹੇ ਦਾ ਜਵਾਬ ਦਿਓ ਜਾਂ ਉਪਭੋਗਤਾ ਨਾਮ/ਉਪਭੋਗਤਾ ਆਈਡੀ ਦਿਓ।"
+general_2 : "» ਤੁਹਾਡੀ ਪੁੱਛਗਿੱਛ ਦੀ ਪ੍ਰਕਿਰਿਆ ਕਰਦੇ ਸਮੇਂ ਕੁਝ ਗਲਤ ਹੋ ਗਿਆ।\n\nਅਪਵਾਦ : {0}
"
+general_3 : "ਤੁਸੀਂ ਇੱਥੇ ਏਕ ਗੁਮਾਨ ਕੰਪਨੀ ਦੇ ਗੁਮਾਨਸ਼ੀਨ ਪ੍ਰਸ਼ਾਸਕ ਹੋ, ਆਪਣੇ ਉਪਭੋਗਤਾ ਖਾਤੇ ਦੇ ਉਪਯੋਗ ਲਈ ਮੁੜ ਵਾਪਸ ਯਤਨ ਕਰੋ."
+general_4 : "» ਤੁਹਾਨੂੰ ਵੀਡੀਓ ਚੈਟਾਂ ਨੂੰ ਪ੍ਰਬੰਧਿਤ ਕਰਨ ਦਾ ਅਨੁਮਤੀਆਂ ਨਹੀਂ ਹਨ.\n\nਐਡਮਿਨ ਕੈਸ਼ ਨੂੰ ਮੁੜ ਲੋਡ ਕਰੋ ਪਰਮੀਸ਼ਨ /reload ਦੁਆਰਾ"
+general_5 : "» ਬੋਟ ਵੀਡੀਓ ਚੈਟ 'ਤੇ ਸਟ੍ਰੀਮ ਨਹੀਂ ਕਰ ਰਿਹਾ ਹੈ."
+
+tg_1 : "{0} ਡਾਉਨਲੋਡਰ\n\nਫਾਇਲ ਆਕਾਰ : {1}\nਪੂਰਾ ਹੋ ਗਿਆ : {2}\nਪ੍ਰਤੀਸ਼ਤ : {3}%\n\nਵੇਗ : {4}/ਸ\nਈਟਾ : {5}"
+tg_2 : "ਸਫਲਤਾਪੂਰਕ ਡਾਊਨਲੋਡ ਹੋ ਗਿਆ ਹੈ, ਫਾਇਲ ਪ੍ਰੋਸੈਸਿੰਗ ਹੋ ਰਹੀ ਹੈ...\n\nਗੁਜਰੇ ਸਮੇਂ : {0}"
+tg_3 : "ਟੈਲੀਗ੍ਰਾਮ ਤੋਂ ਮੀਡੀਆ ਡਾਊਨਲੋਡ ਕਰਨ 'ਚ ਅਸਫਲ, ਕਿਰਪਾ ਕਰਕੇ ਮੁੜ ਕੋਸ਼ਿਸ਼ ਕਰੋ..."
+tg_4 : "» ਡਾਊਨਲੋਡ ਪਹਿਲਾਂ ਹੀ ਪੂਰਾ ਹੋ ਗਿਆ ਹੈ."
+tg_5 : "» ਡਾਊਨਲੋਡ ਪਹਿਲਾਂ ਹੀ ਪੂਰਾ ਹੋ ਗਿਆ ਹੈ ਜਾਂ ਰੱਦ ਕਿਤਾ ਗਿਆ ਹੈ."
+tg_6 : "» ਡਾਊਨਲੋਡ ਰੱਦ ਕਿਤਾ ਗਿਆ ਹੈ."
+tg_7 : "» ਡਾਊਨਲੋਡ ਰੱਦ ਕਰਨਵਾਲਾ: {0}"
+tg_8 : "ਡਾਊਨਲੋਡ ਰੋਕਣ ਵਿੱਚ ਅਸਫਲ ਰਹਿਾ ਹੈ."
+tg_9 : "ਚੱਲਦੇ ਡਾਊਨਲੋਡ ਕੰਮ ਦੀ ਜਾਂਚ ਕਰਨ 'ਚ ਅਸਫਲ ਰਹਿਆ ਹੈ..."
+
+call_1 : "» ਬੋਟ ਲਈ ਲਿੰਕ ਰਾਹੀਂ ਯੂਜ਼ਰਾਂ ਨੂੰ ਸੱਦਣ ਦੀ ਇਜ਼ਾਜ਼ਤ ਦੀ ਲੋੜ ਹੈ ਤਾਂ ਕਿ ਬੋਟ ਨੂੰ ਤੁਹਾਡੇ ਚੈਟ 'ਚ ਸ਼ਾਮਲ ਕਰ ਸਕੇ."
+call_2 : "{0} ਸਹਾਇਕ ਤੁਹਾਡੇ ਗਰੁੱਪ/ਚੈਨਲ 'ਚ ਪਾਬੰਦ ਹੈ.\n\nਆਈਡੀ : {1}
\nਨਾਮ : {2}\nਯੂਜ਼ਰਨੇਮ : @{3}\n\nਕਿਰਪਾ ਕਰਕੇ ਸਹਾਇਕ ਦੇ ਪ੍ਰਤਿਬੰਧ ਖੋਲ੍ਹੋ ਅਤੇ ਮੁੜ ਕੋਸ਼ਿਸ਼ ਕਰੋ."
+call_3 : "{0} ਸਹਾਇਕ ਨੂੰ ਤੁਹਾਡੇ ਚੈਟ 'ਚ ਸੱਦਣ ਦੀ ਕੋਸ਼ਿਸ਼ ਅਸਫਲ ਹੋ ਗਈ ਹੈ.\n\nਕਾਰਨ : {1}
"
+call_4 : "ਕਿਰਪਾ ਕਰਕੇ ਉਡੀਕੋ...\n\n{0} ਸਹਾਇਕ ਨੂੰ ਤੁਹਾਡੇ ਚੈਟ 'ਚ ਸੱਦਣ ਦੀ ਕੋਸ਼ਿਸ਼ ਕੀਤੀ ਜਾ ਰਹੀ ਹੈ..."
+call_5 : "{0} ਸਹਾਇਕ ਨੂੰ ਸਫਲਤਾਪੂਰਕ ਸੱਦਣ ਲਈ ਸ਼ਾਮਲ ਕੀਤਾ ਗਿਆ ਹੈ.\n\nਸਟਰੀਮ ਸ਼ੁਰੂ ਕਰਨ ਦੀ ਕੋਸ਼ਿਸ਼ ਕੀਤੀ ਜਾ ਰਹੀ ਹੈ..."
+call_6 : "» ਸਟਰੀਮ ਬਦਲਣ 'ਚ ਅਸਫਲ ਹੋਇਆ, ਕਿਰਪਾ ਕਰਕੇ /skip ਵਰਤੋਂ ਕਰੋ ਜੀ ਤੁਸੀਂ ਫਿਰ ਗੀਤ ਤਬਦੀਲ ਕਰਨਾ ਚਾਹੁੰਦੇ ਹੋ."
+call_7 : "» ਅਗਲੇ ਗੀਤ ਨੂੰ ਡਾਊਨਲੋਡ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ.\n\nਕਿਰਪਾ ਕਰਕੇ ਧੈਰਯ ਰੱਖੋ..."
+call_8 : "ਕੋਈ ਚਾਲਤੀ ਵੀਡੀਓਚੈਟ ਨਹੀਂ ਮਿਲੀ.\n\nਕਿਰਪਾ ਕਰਕੇ ਆਪਣੇ ਗਰੁੱਪ/ਚੈਨਲ ਵਿੱਚ ਵੀਡੀਓਚੈਟ ਸ਼ੁਰੂ ਕਰੋ ਅਤੇ ਫੇਰ ਕੋਸ਼ਿਸ਼ ਕਰੋ."
+call_9 : "ਸਹਾਇਕ ਹੀ ਪਹਿਲਾਂ ਵੀਡੀਓਚੈਟ ਵਿੱਚ ਹੈ.\n\nਜੇ ਸਹਾਇਕ ਵੀਡੀਓਚੈਟ ਵਿੱਚ ਨਹੀਂ ਹੈ, ਤਾਂ ਕਿਰਪਾ ਕਰਕੇ /reboot
ਭੇਜੋ ਅਤੇ ਫਿਰ ਖੇਡੋ."
+call_10 : "ਟੈਲੀਗ੍ਰਾਮ ਸਰਵਰ ਗਲਤੀ\n\nਟੈਲੀਗ੍ਰਾਮ ਵਿੱਚ ਕੁਝ ਅੰਦਰੂਨੀ ਸਮੱਸਿਆਵਾਂ ਹਨ, ਕਿਰਪਾ ਕਰਕੇ ਫੇਰ ਖੇਡੋ ਜਾਂ ਆਪਣੇ ਗਰੁੱਪ ਦੀ ਵੀਡੀਓਚੈਟ ਨੂੰ ਮੁੜ ਚਾਲਾਓ."
+
+auth_1 : "» ਤੁਸੀਂ ਆਪਣੇ ਗਰੁੱਪ 'ਚ ਸਿਰਫ 25 ਮੰਜੂਰ ਉਪਭੋਗਤਾਵਾਂ ਰੱਖ ਸਕਦੇ ਹੋ."
+auth_2 : "» {0} ਨੂੰ ਮੰਜੂਰ ਉਪਭੋਗਤਾਵਾਂ ਸੂਚੀ ਵਿੱਚ ਸ਼ਾਮਲ ਕੀਤਾ ਗਿਆ ਹੈ."
+auth_3 : "{0} ਪਹਿਲਾਂ ਹੀ ਮੰਜੂਰ ਉਪਭੋਗਤਾਵਾਂ ਸੂਚੀ 'ਚ ਹੈ."
+auth_4 : "» {0} ਨੂੰ ਮੰਜੂਰ ਉਪਭੋਗਤਾਵਾਂ ਸੂਚੀ ਤੋਂ ਹਟਾ ਦਿੱਤਾ ਗਿਆ ਹੈ."
+auth_5 : "{0} ਮੰਜੂਰ ਉਪਭੋਗਤਾਵਾਂ ਸੂਚੀ 'ਚ ਨਹੀਂ ਹੈ."
+auth_6 : "» ਮੰਜੂਰ ਉਪਭੋਗਤਾਵਾਂ ਸੂਚੀ ਲਵਾ ਰਹੀ ਹੈ..."
+auth_7 : "» ਮੰਜੂਰ ਉਪਭੋਗਤਾਵਾਂ ਦੀ ਸੂਚੀ {0} ਵਿੱਚ:\n\n"
+auth_8 : "↬ ਦੁਆਰਾ ਸ਼ਾਮਲ ਕੀਤਾ ਗਿਆ :"
+
+reload_1 : "» ਤੁਸੀਂ ਕੇਵਲ 3 ਮਿੰਟਾਂ 'ਚ ਇੱਕ ਵਾਰ ਐਡਮਿਨ ਕੈਸ਼ ਰੀਫ੍ਰੈਸ਼ ਕਰ ਸਕਦੇ ਹੋ.\n\nਕਿਰਪਾ ਕਰਕੇ {0} ਤੋਂ ਬਾਅਦ ਮੁੜ ਕੋਸ਼ਿਸ਼ ਕਰੋ."
+reload_2 : "» ਐਡਮਿਨ ਕੈਸ਼ ਸਫਲਤਾਪੂਰਕ ਰੀਫ੍ਰੈਸ਼ ਹੋ ਗਈ ਹੈ."
+reload_3 : "» ਐਡਮਿਨ ਕੈਸ਼ ਰੀਫ੍ਰੈਸ਼ ਕਰਨ 'ਚ ਅਸਫਲ ਹੋ ਗਈ ਹੈ, ਕ੍ਰਿਪਾ ਕਰਕੇ ਯਕੀਨੀ ਬਣਾਓ ਕਿ ਬੋਟ ਤੁਹਾਡੇ ਚੈਟ ਵਿੱਚ ਐਡਮਿਨ ਹੈ."
+reload_4 : "» ਕਿਰਪਾ ਕਰਕੇ ਉਡੀਕੋ...\n\n{0} ਨੂੰ ਮੁੜ ਚਾਲਾਉਣ ਲਈ ਰੀਬੂਟ ਹੋ ਰਿਹਾ ਹੈ."
+reload_5 : "{0} ਨੂੰ ਤੁਹਾਡੇ ਚੈਟ ਲਈ ਸਫਲਤਾਪੂਰਕ ਮੁੜ ਚਾਲਾ ਦਿੱਤਾ ਗਿਆ ਹੈ.\n\nਮੁੜ ਖੇਡਣਾ ਸ਼ੁਰੂ ਕਰੋ..."
+
+admin_1 : "» ਕੀ ਤੁਸੀਂ ਯਾਦ ਰੱਖਦੇ ਹੋ ਕਿ ਤੁਸੀਂ ਸਟ੍ਰੀਮ ਨੂੰ ਚਲਾਇਆ ਸੀ?"
+admin_2 : "➻ ਸਟ੍ਰੀਮ ਰੋਕੀ ਗਈ 🎄\n│ \n└ਦੁਆਰਾ : {0} 🥀"
+admin_3 : "» ਕੀ ਤੁਸੀਂ ਯਾਦ ਰੱਖਦੇ ਹੋ ਕਿ ਤੁਸੀਂ ਸਟ੍ਰੀਮ ਰੋਕੀ ਸੀ?"
+admin_4 : "➻ ਸਟ੍ਰੀਮ ਚਾਲੀ ਗਈ 🎄\n│ \n└ਦੁਆਰਾ : {0} 🥀"
+admin_5 : "➻ ਸਟ੍ਰੀਮ ਮੁੱਕ ਗਈ/ਰੋਕੀ ਗਈ 🎄\n│ \n└ਦੁਆਰਾ : {0} 🥀"
+admin_6 : "➻ ਸਟ੍ਰੀਮ ਛੱਡ ਦਿੱਤੀ 🎄\n│ \n└ਦੁਆਰਾ : {0} 🥀\n\n» {1} ਵਿੱਚ ਹੋਰ ਕੁਈ ਕਤਾਰ 'ਚ ਹੋਰ ਕੋਈ ਟਰੈਕ ਨਹੀਂ ਬਚੇ, ਵੀਡੀਓਚੈਟ ਤੋਂ ਬਾਹਰ ਆ ਰਿਹਾ ਹੈ."
+admin_7 : "ਸਟ੍ਰੀਮ ਨੂੰ {0} 'ਤੇ ਤਬਦੀਲ ਕਰਨ 'ਚ ਗਲਤੀ."
+admin_8 : "» ਕਿਰਪਾ ਕਰਕੇ /loop disable
ਦੀ ਸਹਾਇਤਾ ਨਾਲ ਲੂਪ ਪਲੇ ਨੂੰ ਬੰਦ ਕਰੋ ਅਤੇ ਫਿਰ ਸਕਿਪ ਕਰਨ ਦੀ ਕੋਸ਼ਿਸ਼ ਕਰੋ."
+admin_9 : "ਕਿਰਪਾ ਕਰਕੇ ਸਕਿਪ ਲਈ ਵਿਸ਼ਿਸ਼ ਨੰਬਰ ਵਰਤੋ, ਜਿਵੇਂ 1, 2, 4..."
+admin_10 : "ਕਮ ਤੋਂ ਕਮ 2 ਟ੍ਰੈਕਾਂ ਦੀ ਜ਼ਰੂਰਤ ਹੈ ਜੋ ਵਿਸ਼ਿਸ਼ ਸਕਿਪ ਲਈ ਲੱਗੇ।\n\nਕਤਾਰ ਚੈੱਕ ਕਰੋ : /queue"
+admin_11 : "» ਕਤਾਰ 'ਚ ਵਿਸ਼ਿਸ਼ ਸਕਿਪ ਲਈ ਕੋਈ ਟ੍ਰੈਕ ਨਹੀਂ ਹੈ।\n\nਕਿਰਪਾ ਕਰਕੇ 1 ਅਤੇ {0} ਵਿੱਚ ਸਕਿਪ ਕਰੋ"
+admin_12 : "» ਵਿਸ਼ਿਸ਼ ਟ੍ਰੈਕ ਤੇ ਸਕਿਪ ਨਹੀਂ ਕੀਤਾ ਗਿਆ।\n\nਬਾਕੀ ਕਤਾਰ ਚੈੱਕ ਕਰੋ : /queue"
+admin_13 : "» ਕਿਰਪਾ ਕਰਕੇ ਏਡਮਿਨ ਕੈਸ਼ ਨੂੰ ਰੀਲੋਡ ਕਰੋ : /reload"
+admin_14 : "» ਤੁਹਾਡੇ ਕੋਲ ਵੀਡੀਓ ਚੈੱਟ ਪ੍ਰਬੰਧਿਤ ਕਰਨ ਦੀ ਅਨੁਮਤੀ ਨਹੀਂ ਹੈ।\n\nਕਿਰਪਾ ਕਰਕੇ ਏਡਮਿਨ ਕੈਸ਼ ਨੂੰ ਰੀਲੋਡ ਕਰੋ : /reload"
+admin_15 : "» ਸ਼ੁੱਧੀ ਨਹੀਂ ਹੋ ਰਹੀ।\n\nਕਤਾਰ ਦੇ ਅੱਪਰ ਚੈੱਕ ਕਰੋ : /queue"
+admin_16 : "» ਕਤਾਰ ਸ਼ੁੱਧੀ ਹੋ ਗਈ ਹੈ {0} ਵੱਲੋਂ।\n\nਸ਼ੁੱਧੀ ਕਤਾਰ ਚੈੱਕ ਕਰੋ : /queue"
+admin_17 : "ਉਦਾਹਰਣ :\n\n/loop enable
/disable
\n/loop 10
/9
/8
/7
"
+admin_18 : "» ਲੂਪ {0}
ਸਮੇਂ ਲਈ ਚਾਲੀ ਗਈ ਹੈ {1} ਵੱਲੋਂ।"
+admin_19 : "» ਲੂਪ ਪਲੇ ਬੰਦ ਕੀਤੀ ਗਈ ਹੈ {0} ਵੱਲੋਂ।"
+admin_20 : "ਉਦਾਹਰਣ :\n\n/seek ਜਾਂ /seekback [ਸਮਾਂ ਸਕਿਅੰਤਾ ਸਕਿੰਟਾਂ ਵਿੱਚ]"
+admin_21 : "» ਕਿਰਪਾ ਕਰਕੇ ਸਕੀਨਗ ਲਈ ਨੰਬਰੀਕ ਅੰਕ ਵਰਤੋ."
+admin_22 : "» ਲਾਈਵ ਸਟਰੀਮ ਨੂੰ ਸੀਕ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਦਾ."
+admin_23 : "» ਘੱਟ ਮੁਦਾਂ ਨਾਲ ਸੀਕ ਕੋਸ਼ਿਸ਼ ਕਰੋ।\n\n{0} ਵਿੱਚ ਖੇਡਿਆ {1} ਮਿੰਟਾਂ ਤੱਕ."
+admin_24 : "ਸੀਕ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ...\n\nਕਿਰਪਾ ਕਰਕੇ ਬਾਹਰ ਜਾਣ ਵਾਲੇ ਨਾਲ ਵਾਜਾ ਕਰੋ..."
+admin_25 : "» ਸਟਰੀਮ ਸਫਲਤਾਪੂਰਕ ਸੀਕ ਕੀਤੀ ਗਈ ਹੈ।\n\nਮੁਦਾਂ : {0} ਮਿੰਟ\nਵਾਲੇ : {1}"
+admin_26 : "ਸੀਕ ਨਹੀਂ ਕੀਤੀ।"
+admin_27 : "» ਸਿਰਫ ਯੂਟਿਊਬ ਸਟਰੀਮ ਦੀ ਗਤੀ ਹੁਣੇ ਤੱਕ ਨਿਯੰਤਰਿਤ ਨਹੀਂ ਹੋ ਸਕਦੀ।"
+admin_28 : "{0} ਗਤੀ ਨਿਯੰਤਰਣ ਪੈਨਲ\n\nਮੌਜੂਦਾ ਚੱਲਦੇ ਸਟਰੀਮ ਦੀ ਗਤੀ ਬਦਲਣ ਲਈ ਹੇਠਲੀਆਂ ਬਟਨਾਂ 'ਤੇ ਕਲਿਕ ਕਰੋ।"
+admin_29 : "» ਬੋਟ ਪਹਿਲਾਂ ਹੀ ਸਧਾਰਨ ਗਤੀ 'ਤੇ ਚੱਲਦਾ ਹੈ।"
+admin_30 : "» ਕਿਰਪਾ ਕਰਕੇ ਇੰਤਜ਼ਾਰ ਕਰੋ...\n\nਕੋਈ ਹੋਰ ਵੀ ਮੁਦਾਂ ਬਦਲਣ ਦੀ ਕੋਸ਼ਿਸ਼ ਕਰ ਰਿਹਾ ਹੈ।"
+admin_31 : "ਗਤੀ ਬਦਲੀ ਜਾ ਰਹੀ ਹੈ..."
+admin_32 : "» ਮੌਜੂਦਾ ਚੱਲ ਰਹੇ ਮੁਦੇ ਦੀ ਗਤੀ ਬਦਲਣ ਦੀ ਕੋਸ਼ਿਸ਼ ਕੀਤੀ ਜਾ ਰਹੀ ਹੈ...\n\nਨੇਤਾਂ ਦੀ ਬੇਨਤੀ: {0}"
+admin_33 : "» ਸਟ੍ਰੀਮ ਦੀ ਵੇਗ ਬਦਲਣ ਵਿੱਚ ਅਸਫਲ ਹੋਇਆ।"
+admin_34 : "» ਚਲਦੇ ਸਟ੍ਰੀਮ ਦੀ ਵੇਗ ਬਦਲੀ ਗਈ ਹੈ {0}x\n\nਬਾਇਰਲੇਨਾਂ : {1}"
+admin_35 : "ਵੋਟਿੰਗ ਸਮਾਪਤ ਹੋ ਗਈ ਹੈ ਕਿਉਂਕਿ ਵੋਟਿੰਗ ਦੀ ਆਪੋਸ਼ਣ ਦੇ ਹੋਈ ਹੈ ਜਿਸ ਸੰਗੀਤ ਲਈ ਵੋਟਿੰਗ ਦਿੱਤੀ ਗਈ ਸੀ।"
+admin_36 : "ਇਸ ਕ੍ਰਿਆ ਨੂੰ ਪੂਰਾ ਕਰਨ ਵਿੱਚ ਅਸਫਲ ਹੋਇਆ ਕਿਉਂਕਿ ਵੋਟਿੰਗ ਲਈ ਮਿਲਦੇ ਹੋਏ ਸੰਗੀਤ ਵਿੱਚ ਮੁੱਖ ਸਿਖਰ ਵਾਰਤਾਂ ਦੀ ਬੰਦੋਬਸ਼ਤ ਹੈ ਜੋ ਇਥੇ ਸਮਾਪਤ ਹੋ ਗਈ ਹੈ ਜਾਂ ਰੁਕੀ ਹੋਈ ਹੈ।"
+admin_37 : "ਸੰਗੀਤ ਵਿੱਚ {0}
ਵੋਟਿੰਗਾਂ ਪ੍ਰਾਪਤ ਹੋਈਆਂ ਹਨ।"
+admin_38 : "» 1 ਵੋਟ ਜੋੜਿਆ ਗਿਆ ਹੈ।"
+admin_39 : "» 1 ਵੋਟ ਹਟਾਇਆ ਗਿਆ ਹੈ।"
+admin_40 : "ਵੋਟ ਦਿੱਤਾ ਗਿਆ ਹੈ।"
+
+start_1 : "{0} ਜੀ ਜਿੰਦਾ ਹੈ ਵੀ.\n\n✫ ਅਪਟਾਈਮ : {1}"
+start_2 : "ਹੇਲੋ {0}, 🥀\n\n๏ ਇਹ {1} ਹੈ !\n\n➻ ਇੱਕ ਤੇਜ਼ ਅਤੇ ਤਾਕਤਵਰ ਟੈਲੀਗ੍ਰਾਮ ਸੰਗੀਤ ਪਲੇਅਰ ਬੋਟ ਜਿਸ ਵਿੱਚ ਕੁਝ ਸ਼ਾਨਦਾਰ ਵੈਬਸਾਇਟਾਂ ਹਨ।\n\nਸਮਰਥਿਤ ਪਲੈਟਫਾਰਮ : ਯੂਟਿਊਬ, ਸਪੋਟੀਫਾਈ, ਰੈਸੋ, ਐਪਲ ਮਿਊਜ਼ਿਕ ਅਤੇ ਸਾਊਂਡਕਲੌਡ।\n──────────────────\n๏ ਮੇਰੀ ਮੋਡਿਊਲਾਂ ਅਤੇ ਕੰਮਾਂ ਬਾਰੇ ਜਾਣਕਾਰੀ ਲਈ ਮੱਦਦ ਬਟਨ ਤੇ ਕਲਿਕ ਕਰੋ।"
+start_3 : "ਹੇਲੋ {0},\nਇਹ {1} ਹੈ\n\nਧੰਨਵਾਦ {2} ਵਿਚ ਮੈਨੂੰ ਸ਼ਾਮਲ ਕਰਨ ਲਈ, {3} ਹੁਣ ਇਸ ਗੱਲ ਦਾ ਪਾਲਣ ਕਰ ਸਕਦੇ ਹਨ ਕਿ ਆਪ ਵੀਡੀਓ ਵਿੱਚ ਗੀਤ ਪਲੇ ਕਰ ਸਕਦੇ ਹੋ।"
+start_4 : "🎄 ਸੁਪਰਗਰੂਪ ਦੀ ਲੋੜ ਹੈ 🎄\n\nਕਿਰਪਾ ਕਰਕੇ ਆਪਣੇ ਗਰੁੱਪ ਨੂੰ ਸੁਪਰਗਰੂਪ ਵਿੱਚ ਤਬਦੀਲ ਕਰੋ ਅਤੇ ਫਿਰ ਮੈਨੂੰ ਮੁੜ ਸ਼ਾਮਲ ਕਰੋ।\n\nਸੁਪਰਗਰੂਪ ਬਣਾਉਣ ਲਈ ਕਿਵੇਂ ?\n- ਆਪਣੇ ਗਰੁੱਪ ਚੈਟ ਦੀ ਇਤਿਹਾਸ ਨੂੰ ਏਕ ਵਾਰ ਦਿੱਖਤਾ ਬਣਾਓ।"
+start_5 : "↝ ਬਲੈਕਲਿਸਟਡ ਚੈਟ ↜\n\nਇਹ ਚੈਟ {0} ਡੇਟਾਬੇਸ 'ਤੇ ਬਲੈਕਲਿਸਟ ਹੈ।\nਤੁਹਾਨੂੰ ਕਿਸੇ ਦੂਜੇ ਸੂਡੋ ਯੂਜ਼ਰ ਦੀ ਵੇਬਸਾਈਟ ਵੀਜ਼ਿਟ ਕਰਨ ਲਈ ਅਨੁਰੋਧ ਕਰਦਾ ਹੈ, ਜਾਂ ਸੁਪੋਰਟ ਚੈਟ 'ਤੇ ਜਾਓ।"
+start_6 : "😲 ਟ੍ਰੈਕ ਜਾਣਕਾਰੀ 😲\n\n📌 ਸ਼ੀਰਸ਼ਕ : {0}\n\n⏳ ਮੁਦਤ : {1} ਮਿੰਟ\n👀 ਵੇਖੇ ਗਏ : {2}
\n⏰ ਪ੍ਰਕਾਸ਼ਿਤ ਹੋਇਆ ਸਮਾਂ : {3}\n📎 ਚੈਨਲ : {5}\n\n🥀 ਖੋਜ ਸ਼ਕਤੀਵਾਨ ਬਣਾਇਆ ਗਿਆ ਹੈ {6}"
+
+help_1 : "ਸੰਮਰਥਨ ਲਈ ਕੈਟੇਗਰੀ ਚੁਣੋ ਜਿਸ ਵਿੱਚ ਤੁਸੀਂ ਮਦਦ ਪ੍ਰਾਪਤ ਕਰਨਾ ਚਾਹੁੰਦੇ ਹੋ। ਆਪਣੇ ਸਸਪੈਂਟ ਨੂੰ ਪੂਛੋ ਸੰਮਰਥਨ ਚੈਟ\n\nਸਭ ਕੰਮਾਂਡਾਂ /
ਨਾਲ ਵਰਤੀ ਜਾ ਸਕਦੀਆਂ ਹਨ।"
+help_2 : "ਆਪਣੇ ਪ੍ਰਾਈਵੇਟ ਚੈਟ ਵਿੱਚ ਮੇਰੇ ਮਦਦ ਮੀਨੂ ਪ੍ਰਾਪਤ ਕਰਨ ਲਈ ਹੇਲਪ ਮੇਨੂ ਉੱਤੇ ਦਿੱਤੇ ਗਏ ਬਟਨ 'ਤੇ ਕਲਿਕ ਕਰੋ।"
+
+lang_1 : "» ਮੈਂਨੂੰ ਜੇ ਕਿਸੇ ਸਮੂਹ ਦੀ ਮੂਲ ਭਾਾ ਬਣਾਉਣੀ ਹੈ, ਤਾਂ ਕਿਰਪਾ ਕਰਕੇ ਉਸ ਸਮੂਹ ਦੀ ਭਾਾ ਚੁਣੋ :"
+lang_2 : "ਭਾਸ਼ਾ ਸਫ਼ਲਤਾਪੂਰਕ ਬਦਲੀ ਗਈ ਹੈ।"
+lang_3 : "ਭਾਸ਼ਾ ਬਦਲਣ ਵਿੱਚ ਅਸਫਲ ਹੋਇਆ।"
+lang_4 : "ਤੁਸੀਂ ਹੀ ਉਸੇ ਭਾਸ਼ਾ 'ਤੇ ਹੋ।"
+
+setting_1 : "{0} ਸੈਟਿੰਗਾਂ ਪੈਨਲ\n\nਚੈਟ ਆਈਡੀ : {1}
\nਚੈਟ ਨਾਂ : {2}\n\nਸੈਟਿੰਗਾਂ ਬਦਲਣ ਲਈ ਹੇਠਾਂ 'ਤੇ ਬਟਨਾਂ 'ਤੇ ਕਲਿਕ ਕਰੋ।"
+setting_2 : "» ਡਾਇਰੈਕਟ: ਖੋਜ ਪੁੱਛ ਤੇ ਖੋਜ ਜ਼ਿਦਾ ਸਵਰੂਪੋ 'ਤੇ ਸੀਧਾ ਚੱਲਦੀ ਹੈ।\n\n» ਇਨਲਾਈਨ: ਵੀਡੀਓ ਅਤੇ ਆਡੀਓ ਵਿੱਚ ਚੁਣਨ ਲਈ ਇਨਲਾਈਨ ਬਟਨ ਪ੍ਰਦਾਨ ਕਰਦੇ ਹਨ।"
+setting_3 : "» ਹਰ ਕੋਈ: ਇਸ ਸਮੂਹ ਵਿੱਚ ਹਰ ਕੋਈ ਐਡਮਿਨ ਕੰਮਾਂਡਾਂ [ਸਕਿਪ, ਪੌਜ, ਰੀਜ਼ਿਊਮ ਆਦਿ] ਵਰਤ ਸਕਦਾ ਹੈ।\n\n» ਸਿਰਫ ਐਡਮਿਨ: ਸਿਰਫ ਐਡਮਿਨਾਂ ਅਤੇ ਮਨਜ਼ੂਰ ਯੂਜ਼ਰ ਐਡਮਿਨ ਕੰਮਾਂਡਾਂ ਵਰਤ ਸਕਦੇ ਹਨ।"
+setting_4 : "» ਕੋਈ ਮਨਜ਼ੂਰ ਯੂਜ਼ਰ ਨਹੀਂ ਮਿਲੇ।"
+setting_5 : "» ਗਰੁੱਪ: ਉਸ ਗਰੁੱਪ ਵਿੱਚ ਸੰਗੀਤ ਚਲਾਉਂਦਾ ਹੈ ਜਿੱਥੇ ਆਦੇਸ਼ ਦਿੱਤਾ ਜਾਂਦਾ ਹੈ।\n\n» ਚੈਨਲ: ਉਸ ਚੈਨਲ 'ਚ ਸੰਗੀਤ ਚਲਾਉਂਦਾ ਹੈ ਜਿਥੇ ਤੁਸੀਂ ਚਾਹੁੰਦੇ ਹੋ। /channelplay ਦੁਆਰਾ ਚੈਨਲ ਆਈਡੀ ਸੈਟ ਕਰੋ"
+setting_6 : "» ਹਰ ਕੋਈ : ਇਸ ਗਰੁੱਪ ਵਿੱਚ ਹਾਜ਼ਰ ਕੋਈ ਵੀ ਮਿਊਜ਼ਿਕ ਚਲਾ ਸਕਦਾ ਹੈ।\n\n» ਸਿਰਫ ਐਡਮਿਨ : ਇਸ ਗਰੁੱਪ ਵਿੱਚ ਮਿਊਜ਼ਿਕ ਸਿਰਫ ਐਡਮਿਨਸ ਵੀ ਚਲਾ ਸਕਦੀ ਹੈ।"
+setting_7 : "» ਕਿਰਪਾ ਕਰਕੇ /channelplay ਦੁਆਰਾ ਚੈਨਲ ਆਈਡੀ ਨੂੰ ਨਿਰਧਾਰਿਤ ਕਰੋ"
+setting_8 : "ਜਦੋਂ ਇਹ ਮੋਡ ਸਮਰੱਥਿਤ ਹੁੰਦਾ ਹੈ, ਤਾਂ ਬਿਨਾ ਐਡਮਿਨ ਹੱਕਾਂ ਵਾਲੇ ਲੋਕ ਕੁਝ ਸਮੇਂ ਬਾਅਦ ਐਡਮਿਨ ਕਮਾਂਡ ਵਰਤ ਸਕਦੇ ਹਨ."
+setting_9 : "ਮੌਜੂਦਾ ਅੱਪਵੋਟਾਂ ਦੀ ਗਿਣਤੀ ਜੋ ਐਡਮਿਨ ਕਮਾਂਡ ਵਰਤਣ ਲਈ ਦੀ ਜ਼ਰੂਰੀ ਹੈ: {0}"
+setting_10 : "ਵੋਟਿੰਗ ਮੋਡ ਬੰਦ ਹੈ."
+setting_11 : "ਸਭ ਤੋਂ ਘੱਟ ਅੱਪਵੋਟ ਗਿਣਤੀ 2 ਹੋ ਸਕਦੀ ਹੈ। ਤੁਸੀਂ 2 ਤੋਂ ਘੱਟ ਸੈਟ ਨਹੀਂ ਕਰ ਸਕਦੇ"
+setting_12 : "ਸਭ ਤੋਂ ਵੱਧ ਅੱਪਵੋਟ ਗਿਣਤੀ 15 ਹੋ ਸਕਦੀ ਹੈ। ਤੁਸੀਂ 15 ਤੋਂ ਵੱਧ ਸੈਟ ਨਹੀਂ ਕਰ ਸਕਦੇ"
+
+set_cb_1 : "ਪ੍ਰਾਪਤ ਕਰ ਰਹੇ ਹਨ ਮਨਜੂਰ ਯੂਜ਼ਰਾਂ ਪੈਨਲ..."
+set_cb_2 : "ਪ੍ਰਾਪਤ ਕਰ ਰਹੇ ਹਨ ਪਲੇ ਮੋਡ ਪੈਨਲ..."
+set_cb_3 : "ਸੈੱਟਿੰਗ ਅੱਪ ਕੀਤੀ ਜਾ ਰਹੀ ਹੈ..."
+set_cb_4 : "» ਮੰਜੂਰ ਕੀਤੇ ਗਏ ਯੂਜ਼ਰਾਂ ਦੀ ਲਿਸਟ ਪ੍ਰਾਪਤ ਕੀਤੀ ਜਾ ਰਹੀ ਹੈ..."
+set_cb_5 : "» ਵਾਪਸ ਪ੍ਰਾਪਤ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ..."
+
+gstats_1 : "ਪ੍ਰਾਪਤ ਕਰ ਰਿਹਾ ਹੈ {0} ਸਟੈਟਸ ਅਤੇ ਜਾਣਕਾਰੀ...\n\nਇਹ ਥੋੜੇ ਸਮੇਂ ਲਗ ਸਕਦਾ ਹੈ, ਕਿਰਪਾ ਕਰਕੇ ਰੁਕੋ..."
+gstats_2 : "ਨੀਚੇ ਦਿੱਤੇ ਗਏ ਬਟਨਾਂ 'ਤੇ ਕਲਿਕ ਕਰੋ ਤਾਂ {0} ਦੀ ਸਟੈਟਸ ਦੇਖੋ."
+gstats_3 : "{0} ਸਟੈਟਸ ਅਤੇ ਜਾਣਕਾਰੀ :\n\nਅਸਿਸਟੈਂਟਾਂ : {1}
\nਬਲੌਕ : {2}
\nਚੈਟਾਂ : {3}
\nਯੂਜ਼ਰ : {4}
\nਮੋਡਿਊਲਾਂ : {5}
\nਸੁਡੋਅਰ : {6}
\n\nਆਟੋ ਛੱਡਣ ਵਾਲਾ ਅਸਿਸਟੈਂਟ : {7}\nਪਲੇ ਦੀ ਮਿਆਦ ਸੀਮਾ : {8} ਮਿੰਟਾਂ"
+gstats_4 : "ਇਹ ਬਟਨ ਸਿਰਫ ਸੁਡੋਅਰਾਂ ਲਈ ਹੈ."
+gstats_5 : "{0} ਸਟੈਟਸ ਅਤੇ ਜਾਣਕਾਰੀ :\n\nਮੋਡਿਊਲਾਂ : {1}
\nਪਲੈਟਫਾਰਮ : {2}
\nਰੈਮ : {3}
\nਫਿਜਿਕਲ ਕੋਰਸ : {4}
\nਕੁੱਲ ਕੋਰਸ : {5}
\nਸੀਪੀਯੂ ਫ਼ਰੀਕਵੈਂਸੀ : {6}
\n\nਪਾਇਥਨ : {7}
\nਪਾਇਥਾਂ : {8}
\nਪਾਇਥਾਂ-ਟੀਜੀਕਾਲਾ : {9}
\n\nਸਟੋਰੇਜ ਉਪਲੱਬਧ : {10} ਗੀਬੀ
\nਸਟੋਰੇਜ ਵਰਤੇ : {11} ਗੀਬੀ
\nਸਟੋਰੇਜ ਬਾਕੀ : {12} ਗੀਬੀ
\n\nਸੇਵਿਆਂ ਦੀਆਂ ਚੈਟਾਂ : {13}
\nਸੇਵਿਆਂ ਦੇ ਯੂਜ਼ਰ : {14}
\nਬਲੌਕ ਕੀਤੇ ਯੂਜ਼ਰ : {15}
\nਸੁਡੋ ਯੂਜ਼ਰ : {16}
\n\nਕੁੱਲ ਡੀਬੀ ਆਕਾਰ : {17} ਐਮਬੀ
\nਕੁੱਲ ਡੀਬੀ ਸਟੋਰੇਜ : {18} ਐਮਬੀ
\nਕੁੱਲ ਡੀਬੀ ਕਲੈਕਸ਼ਨ : {19}
\nਕੁੱਲ ਡੀਬੀ ਕੀਜ : {20}
"
+
+playcb_1 : "» ਆਓ, ਇਹ ਤੁਹਾਡੇ ਵਾਸਤੇ ਨਹੀਂ ਹੈ ਮੇਰੇ ਮਨਾਣ ਦੇ ਅਨੁਸਾਰ."
+playcb_2 : "» ਅਗਲੇ ਨਤੀਜੇ ਪ੍ਰਾਪਤ ਕੀਤੇ ਜਾ ਰਹੇ ਹਨ,\n\nਕਿਰਪਾ ਕਰਕੇ ਉਡੀਕ ਕਰੋ..."
+
+cplay_1 : "» ਤੁਸੀਂ {0} ਤੋਂ ਕਿਸੇ ਵੀ ਚੈਨਲ 'ਤੇ ਗੀਤ ਖੇਡ ਸਕਦੇ ਹੋ ਜਾਂ ਤੁਹਾਡੇ ਚੈਟ ਦੇ ਲਿੰਕ ਦਿੱਤੇ ਚੈਨਲ 'ਤੇ।\n\nਲਿੰਕ ਦਿੱਤੇ ਚੈਨਲ ਲਈ:\n/channelplay linked
\n\nਕਿਸੇ ਹੋਰ ਚੈਨਲ ਲਈ:\n/channelplay [ਚੈਨਲ ਆਈਡੀ]
"
+cplay_2 : "» ਇਸ ਚੈਟ ਦੇ ਕੋਈ ਵੀ ਲਿੰਕ ਦਿੱਤੇ ਚੈਨਲ ਨਹੀਂ ਹਨ।"
+cplay_3 : "» ਚੈਨਲ ਨੂੰ {0} ਲਈ ਨਿਰਧਾਰਤ ਕੀਤਾ ਗਿਆ ਹੈ।\nਚੈਨਲ ਆਈਡੀ : {1}
"
+cplay_4 : "» ਚੈਨਲ ਨੂੰ ਪ੍ਰਾਪਤ ਕਰਨ 'ਚ ਅਸਫ਼ਲ ਹੋਇਆ।\n\nਮੁੱਖਬੂਟੀ ਬਣਾਓ ਕਿ ਤੁਸੀਂ ਆਪਣੇ ਚੈਨਲ ਵਿੱਚ ਬੋਟ ਸ਼ਾਮਲ ਕਰ ਚੁੱਕੇ ਹੋ ਅਤੇ ਉਸ ਨੂੰ ਐਡਮਿਨ ਬਣਾ ਦਿੳੁ।"
+cplay_5 : "ਸਿਰਫ਼ ਚੈਨਲਾਂ ਦਾ ਸਮਰਥਨ ਕੀਤਾ ਗਿਆ ਹੈ।"
+cplay_6 : "» ਤੁਹਾਨੂੰ ਚੈਨਲ {0} ਦਾ ਮਾਲਕ ਹੋਣ ਦੀ ਲੋੜ ਹੈ ਇਸ ਨੂੰ ਇਸ ਗਰੁਪ ਨਾਲ ਮੇਲਾਓ।\nਚੈਨਲ ਦਾ ਮਾਲਕ : @{1}\n\nਵਿਕਲਪ ਤੌਰ 'ਤੇ, ਤੁਸੀਂ ਆਪਣੇ ਗਰੁਪ ਨੂੰ ਉਹ ਚੈਨਲ ਦੇ ਸਥਾਨ 'ਤੇ ਲਿੰਕ ਕਰ ਸਕਦੇ ਹੋ ਅਤੇ ਫਿਰ /channelplay linked
ਨਾਲ ਜੁੜਣ ਦੀ ਕੋਸ਼ਿਸ਼ ਕਰੋ।"
+cplay_7 : "» ਚੈਨਲ ਪਲੇ ਅਸਮਰਥ ਕੀਤਾ ਗਿਆ ਹੈ।"
+
+play_1 : "🔍"
+play_2 : "➻ ਚੈਨਲ ਪਲੇ ਮੋਡ\n\nਪ੍ਰੋਸੈਸਿੰਗ, ਕਿਰਪਾ ਕਰਕੇ ਉਡੀਕੋ...\n\nਲਿੰਕ ਕੀਤਾ ਚੈਨਲ : {0}"
+play_3 : "» ਪ੍ਰੋਸੈਸ਼ ਕਰਨ ਵਿੱਚ ਅਸਫ਼ਲ ਹੋਇਆ।"
+play_4 : "ਕੇਵਲ ਐਡਮਿਨ ਪਲੇ\nਇਸ ਚੈਟ ਦੇ ਐਡਮਿਨ ਹੀ ਖੇਡ ਸਕਦੇ ਹਨ\n\n/playmode ਰਾਹੀਂ ਖੇਡਣ ਦਾ ਮੋਡ ਬਦਲੋ"
+play_5 : "» ਆਡੀਓ ਫਾਇਲ ਦੀ ਪ੍ਰੋਸੈਸ਼ਿੰਗ ਵਿੱਚ ਅਸਫ਼ਲ ਹੋਇਆ।\n\nਆਡੀਓ ਫਾਇਲ ਦਾ ਆਕਾਰ ਨੀਚੇ ਦਿੱਤੇ ਸੀਮਤ ਤੋਂ ਵੱਡਾ ਹੈ।"
+play_6 : "» ਸਟ੍ਰੀਮ {0} ਤੋਂ ਲੰਬਾ ਹੈ ਅਤੇ {1} 'ਤੇ ਖੇਡਣ ਦੀ ਆਗਿਆ ਨਹੀਂ ਹੈ"
+play_7 : "» ਮਾਨਿਆ ਵਿਡੀਓ ਫਾਇਲ ਐਕਸ਼ਟੇਸ਼ਨ ਨਹੀਂ ਹੈ।\n\nਸਹਾਇਕ ਐਕਸ਼ਟੇਸ਼ਨ: {0}"
+play_8 : "» ਵੀਡੀਓ ਫਾਇਲ ਦਾ ਆਕਾਰ 1ਜੀਬੀ ਤੋਂ ਘੱਟ ਹੋਣਾ ਚਾਹੀਦਾ ਹੈ."
+play_9 : "YouTube ਪਲੇਲਿਸਟ ਫੀਚਰ\n\nਪੂਰੀ YouTube ਪਲੇਲਿਸਟ ਨੂੰ ਖੇਡਣ ਲਈ ਮੋਡ ਚੁਣੋ।"
+play_10 : "ਸਿਰਲੇਖ : {0}\nਮਿੰਟਾਂ ਦੀ ਮੁਦਤ : {1}"
+play_11 : "{0} Spotify ਪਲੇਅਰ\n\nਦੁਆਰਾ ਮੰਗਿਆ ਗਿਆ : {1}"
+play_12 : "{0} Apple ਪਲੇਅਰ\n\nਦੁਆਰਾ ਮੰਗਿਆ ਗਿਆ : {1}"
+play_13 : "» ਲਾਈਵ ਸਟ੍ਰੀਮ ਪਕਡ਼ਿਆ ਗਿਆ ਹੈ।\n\nਕੀ ਤੁਸੀਂ ਯਕੀਨਾਂ ਹੈਂ ਕਿ ਤੁਸੀਂ ਇਸ ਲਾਈਵ ਸਟ੍ਰੀਮ ਨੂੰ ਚੱਲਾਉਣਾ ਚਾਹੁੰਦੇ ਹੋ?"
+play_14 : "ਟ੍ਰੈਕ ਦੇਤਾਇਲਾਂ ਦੀ ਵੱਖਰੀ ਕਰਨਾ ਅਸਫ਼ਲ ਹੋਇਆ।\n\nਕਿਸੇ ਹੋਰ ਨੂੰ ਖੇਡੋ ਕੋਈ ਹੋਰ ਟ੍ਰੈਕ।"
+play_15 : "» ਪੁਰਜ਼ਾ ਦਾ ਇੱਛਰਾ ਕਰਨ ਵਿੱਚ ਅਸਫ਼ਲ ਹੋਇਆ।\n\nਮੈਂ ਕੇਵਲ Spotify ਟ੍ਰੈਕਾਂ, ਐਲਬਮਾਂ, ਕਲਾਕਾਰਾਂ ਅਤੇ ਪਲੇਲਿਸਟ ਖੇਡ ਸਕਦਾ ਹਾਂ।"
+play_16 : "ਕੋਈ ਸਰਗਰਮ ਆਵਾਜ਼ ਚੈਟ ਨਹੀਂ ਹੈ।\n\nਫੋਰਸ ਖੇਡਣ ਲਈ, ਸਰਗਰਮ ਆਵਾਜ਼ ਚੈਟ ਹੋਣੀ ਚਾਹੀਦੀ ਹੈ।"
+play_17 : "ਕਿਰਪਾ ਕਰਕੇ ਵੀਡੀਓ ਚੈਟ ਚਾਲੀਏ, ਮੈਂ URL ਨੂੰ ਸਟ੍ਰੀਮ ਕਰਨ ਲਈ ਅਸਮਰੱਥ ਹਾਂ।"
+play_18 : "ਉਪਯੋਗ : /play [ਗੀਤ ਦਾ ਨਾਮ/YouTube URL/ਆਡੀਓ/ਵੀਡੀਓ ਫਾਇਲ ਦੇ ਪ੍ਰਤੀਕ ਉੱਤਰ]"
+play_19 : "ਕਤਾਰ ਦੀ ਪਲੇਲਿਸਟ:"
+play_20 : "ਕਤਾਰ ਦੀ ਥਾਂ -"
+play_21 : "ਕੱਢੀਆ ਗਿਆ {0} ਟਰੈਕਾਂ ਨੂੰ ਕਤਾਰ ਵਿੱਚ.\n\nਚੈਕ: ਇੱਥੇ ਕਲਿਕ ਕਰੋ"
+play_22 : "ਸਮੱਗਰੀ ਨੂੰ ਉਸ ਤਰੀਕੇ ਨਾਲ ਖੇਡਣ ਦੇ ਮੋਡ ਨੂੰ ਚੁਣੋ ਜਿਸ ਵਿੱਚ ਤੁਸੀਂ ਆਪਣੇ ਗਰੁੱਪ ਵਿੱਚ ਕਤਾਰਾ ਲੱਗਾਉਣਾ ਚਾਹੁੰਦੇ ਹੋ: {0}"
+
+str_1 : "ਕਿਰਪਾ ਕਰਕੇ m3u8 ਜਾਂ ਇੰਡੈਕਸ ਲਿੰਕ ਪ੍ਰਦਾਨ ਕਰੋ."
+str_2 : "➻ ਵੈਲੀਡ ਸਟਰੀਮ ਪੁਸ਼ਟੀ ਕੀਤੀ।\n\nਪ੍ਰੋਸੈਸਿੰਗ..."
+str_3 : "ਯੂਟਿਊਬ ਲਾਈਵ ਸਟਰੀਮ ਚਲਾਉਣ 'ਚ ਅਸਫ਼ਲ ਹੋਇਆ, ਕੋਈ ਲਾਈਵ ਫਾਰਮੈਟ ਨਹੀਂ ਲੱਭਿਆ।"
+
+ping_1 : "{0} ਪਿੰਗ ਲੱਗਾ ਰਿਹਾ ਹੈ..."
+ping_2 : "🏓 ਪੌਂਗ: {0}ਮਿਲੀਸਕਿੰਡ
\n\n{1} ਸਿਸਟਮ ਸਟੈਟਸ:\n\n↬ ਅਪਟਾਇਮ: {2}\n↬ ਰੈਮ: {3}\n↬ ਸੀ.ਪੀ.ਯੂ: {4}\n↬ ਡਿਸਕ: {5}\n↬ ਪਾਈ-ਟੀਜੀ-ਕਾਲਜ: {6}ਮਿਲੀਸਕਿੰਡ
"
+
+queue_1 : "» Ǫᴜᴇᴜᴇ ਲੱਭ ਰਿਹਾ ਹੈ...\n\nਕਿਰਪਾ ਕਰਕੇ ਇੰਤਜ਼ਾਰ ਕਰੋ..."
+queue_2 : "» Ǫᴜᴇᴜᴇ ਖਾਲੀ ਹੈ."
+queue_3 : "» ਕਿਰਪਾ ਕਰਕੇ ਯੋਧਾਂ ਦੀ ਸੂਚੀ ਜਾਂਚਣ ਲਈ ਇੱਥੇ ਕਲਿੱਕ ਕਰੋ : ਇੱਥੇ"
+queue_4 : "➲ ਕਤਾਰ ਵਿੱਚ ਸ਼ਾਮਲ ਕੀਤਾ #{0}\n\n‣ ਸਿਰਲੇਖ : {1}\n‣ ਅੰਤਰਾਲ : {2} ਮਿੰਟ\n‣ ਬੇਨਕਾਰਨ ਕੇਵਲ : {3}"
+queue_5 : "ਇੱਥੇ ਪਲੇਅਲਿਸਟ ਵਿੱਚ ਸਿਰਫ ਇੱਕ ਕਤਾਰ ਵਾਲਾ ਗੀਤ ਹੈ.\n\nਇੱਕ ਹੋਰ ਗੀਤ ਸ਼ਾਮਲ ਕਰੋ ਸੂਚੀ ਜਾਂਚਣ ਲਈ."
+queue_6 : "🕚 ਅੰਤਰਾਲ : ਅਣਜਾਣ ਸ਼੍ਰੇਣੀ ਦੀ ਅਵਧੀ\n\nਪੂਰੀ ਕਤਾਰ ਦੀ ਸੂਚੀ ਲੱਭਣ ਲਈ ਹੇਠਾਂ ਦਿੱਤੇ ਬਟਨ 'ਤੇ ਕਲਿਕ ਕਰੋ."
+queue_7 : "\nਪੂਰੀ ਕਤਾਰ ਦੀ ਸੂਚੀ ਲੱਭਣ ਲਈ ਹੇਠਾਂ ਦਿੱਤੇ ਬਟਨ 'ਤੇ ਕਲਿਕ ਕਰੋ."
+queue_8 : "{0} ਪਲੇਅਰ\n\n🎄 ਸਟ੍ਰੀਮਿੰਗ : {1}\n\n🔗 ਸਟ੍ਰੀਮ ਕਿਸਮ : {2}\n🥀 ਬੇਨਕਾਰਨ ਕੇਵਲ : {3}\n{4}"
+
+stream_1 : "➲ ਸਟਰਟੇਡ ਸਟਰੀਮਿੰਗ |\n\n‣ ਟਾਈਟਲ : {1}\n‣ ਮੁੱਦਤ : {2} ਮਿੰਟ\n‣ ਬੇਨਕਾਰਨ ਕੇਵਲ : {3}"
+stream_2 : "➲ ਸਟਰਟੇਡ ਸਟਰੀਮਿੰਗ |\n\n‣ ਸਟਰੀਮ ਕਿਸਮ : ਲਾਈਵ ਸਟਰੀਮ [ਯੂਆਰਐਲ]\n‣ ਬੇਨਕਾਰਨ ਕੇਵਲ : {0}"
+
+CLOSE_BUTTON : "ਬੰਦ ਕਰੋ"
+BACK_BUTTON : "ਵਾਪਸ"
+
+S_B_1 : "ਮੈਨੂੰ ਸ਼ਾਮਲ ਕਰੋ"
+S_B_2 : "ਸਹਿਯੋਗ"
+S_B_3 : "ਮੇਰੇ ਗਰੁੱਪ ਵਿੱਚ ਮੈਨੂੰ ਸ਼ਾਮਲ ਕਰੋ"
+S_B_4 : "ਮਦਦ ਅਤੇ ਕਮਾਂਡਾਂ"
+S_B_5 : "ਡੈਵੇਲਪਰ"
+S_B_6 : "ਚੈਨਲ"
+S_B_7 : "ਸਰੋਤ ਕੋਡ"
+S_B_8 : "👀 ਯੂਟਿਊਬ 👀"
+S_B_9 : "🥀 ਸਹਿਯੋਗ 🥀"
+
+H_B_1 : "ਐਡਮਿਨ"
+H_B_2 : "ਆਥ"
+H_B_3 : "ਬ੍ਰੋਡਕਾਸਟ"
+H_B_4 : "ਬਲ-ਚੈਟ"
+H_B_5 : "ਬਲ-ਯੂਜ਼ਰ"
+H_B_6 : "ਸੀ-ਪਲੇ"
+H_B_7 : "ਜੀ-ਬੈਨ"
+H_B_8 : "ਸਟਿੱਕਰ"
+H_B_9 : "ਚਿੱਤਰ"
+
+P_B_1 : "ਆਡੀਓ"
+P_B_2 : "ਵੀਡੀਓ"
+P_B_3 : "ਲਾਈਵ ਸਟ੍ਰੀਮ"
+P_B_4 : "ਨਾਰਮਲ"
+
+ST_B_1 : "ਆਥ ਯੂਜ਼ਰ"
+ST_B_2 : "ਪਲੇ ਮੋਡ"
+ST_B_3 : "ਭਾਸ਼ਾ"
+ST_B_4 : "ਵੋਟਿੰਗ ਮੋਡ"
+ST_B_5 : "ਚਾਲੂ"
+ST_B_6 : "ਬੰਦ"
+ST_B_7 : "ਆਥ ਯੂਜ਼ਰ ➜"
+ST_B_8 : "ਐਡਮਿਨ"
+ST_B_9 : "ਹਰ ਕੋਈ"
+ST_B_10 : "ਸਰਚ ਮੋਡ ➜"
+ST_B_11 : "ਸਿੱਧਾ"
+ST_B_12 : "ਇਨਲਾਇਨ"
+ST_B_13 : "ਐਡਮਿਨ ਕੰਮਾਂਡ ➜"
+ST_B_14 : "ਪਲੇ ਟਾਈਪ ➜"
+
+SA_B_1 : "ਸੰਪੂਰਨ ਸਥਿਤੀ"
+SA_B_2 : "ਆਮ"
+SA_B_3 : "ਸੰਪੂਰਨ"
+
+QU_B_1 : "ਕਤਾਰ"
+QU_B_2 : " {0} —————————— {1}"
+
+sudo_1 : "» {0} ਪਹਿਲਾਂ ਹੀ ਸੁਡੋ ਯੂਜ਼ਰ ਸੂਚੀ ਵਿੱਚ ਹੈ."
+sudo_2 : "» {0} ਨੂੰ ਸੁਡੋ ਯੂਜ਼ਰ ਸੂਚੀ ਵਿੱਚ ਸ਼ਾਮਲ ਕੀਤਾ ਗਿਆ ਹੈ."
+sudo_3 : "» {0} ਸੁਡੋ ਯੂਜ਼ਰ ਸੂਚੀ ਵਿੱਚ ਨਹੀਂ ਹੈ."
+sudo_4 : "» {0} ਨੂੰ ਸੁਡੋ ਯੂਜ਼ਰ ਸੂਚੀ ਤੋਂ ਹਟਾ ਦਿੱਤਾ ਗਿਆ ਹੈ."
+sudo_5 : "🥀 ਮਾਲਕ :\n"
+sudo_6 : "\n✨ ਸੁਡੋ ਯੂਜ਼ਰ :\n"
+sudo_7 : "» ਕੋਈ ਸੁਡੋ ਯੂਜ਼ਰ ਨਹੀਂ ਮਿਲਿਆ."
+sudo_8 : "ਅਸਫਲ."
+
+block_1 : "» {0} ਪਹਿਲਾਂ ਹੀ ਬਲਾਕ ਯੂਜ਼ਰ ਸੂਚੀ ਵਿੱਚ ਹੈ."
+block_2 : "» {0} ਨੂੰ ਬਲਾਕ ਯੂਜ਼ਰ ਸੂਚੀ ਵਿੱਚ ਸ਼ਾਮਲ ਕੀਤਾ ਗਿਆ ਹੈ."
+block_3 : "» {0} ਬਲਾਕ ਯੂਜ਼ਰ ਸੂਚੀ ਵਿੱਚ ਨਹੀਂ ਹੈ."
+block_4 : "» {0} ਨੂੰ ਬਲਾਕ ਯੂਜ਼ਰ ਸੂਚੀ ਤੋਂ ਹਟਾ ਦਿੱਤਾ ਗਿਆ ਹੈ."
+block_5 : "» ਕੋਈ ਬਲਾਕ ਯੂਜ਼ਰ ਨਹੀਂ ਮਿਲਿਆ."
+block_6 : "» ਬਲਾਕ ਯੂਜ਼ਰ ਸੂਚੀ ਪ੍ਰਾਪਤ ਕੀਤੀ ਜਾ ਰਹੀ ਹੈ..."
+block_7 : "😫 ਬਲਾਕ ਯੂਜ਼ਰ :\n\n"
+
+black_1 : "ਉਦਾਹਰਨ:\n\n/blacklistchat [ਚੈਟ ਆਈਡੀ]"
+black_2 : "» ਇਹ ਚੈਟ ਪਹਿਲਾਂ ਹੀ ਬਲੈਕਲਿਸਟ 'ਤੇ ਹੈ।"
+black_3 : "» ਸਫਲਤਾਪੂਰਕ ਤੌਰ 'ਤੇ ਬਲੈਕਲਿਸਟ 'ਤੇ ਚੈਟਾਂ ਨੂੰ ਸ਼ਾਮਲ ਕੀਤਾ ਗਿਆ ਹੈ।"
+black_4 : "ਉਦਾਹਰਨ:\n\n/whitelistchat [ਚੈਟ ਆਈਡੀ]"
+black_5 : "» ਇਹ ਚੈਟ ਬਲੈਕਲਿਸਟ 'ਤੇ ਨਹੀਂ ਹੈ।"
+black_6 : "» ਸਫਲਤਾਪੂਰਕ ਤੌਰ 'ਤੇ ਚੈਟ ਨੂੰ ਬਲੈਕਲਿਸਟ ਤੋਂ ਹਟਾ ਦਿੱਤਾ ਗਿਆ ਹੈ।"
+black_7 : "» ਬਲੈਕਲਿਸਟ 'ਤੇ ਚੈਟਾਂ ਦੀ ਸੂਚੀ:\n\n"
+black_8 : "» {0} 'ਤੇ ਕੋਈ ਬਲੈਕਲਿਸਟ 'ਤੇ ਨਹੀਂ ਹੈ।"
+black_9 : "» ਕੁਝ ਗਲਤ ਹੋ ਗਿਆ ਸੀ।"
+
+maint_1 : "ਉਦਾਹਰਨ:\n/maintenance [ਚਾਲੂ | ਬੰਦ]"
+maint_2 : "» {0} ਮੈਂਟੀਨੈਂਸ ਮੋਡ ਚਾਲੂ ਹੋ ਗਿਆ ਹੈ."
+maint_3 : "» {0} ਮੈਂਟੀਨੈਸ ਮੋਡ ਬੰਦ ਹੋ ਗਿਆ ਹੈ."
+maint_4 : "» ਮੈਂਟੀਨੈਸ ਮੋਡ ਹੀ ਚਾਲੂ ਹੈ."
+maint_5 : "» ਮੈਂਟੀਨੈਸ ਮੋਡ ਹੀ ਬੰਦ ਹੈ."
+
+log_1 : "ਉਦਾਹਰਨ:\n/logger [ਚਾਲੂ | ਬੰਦ]"
+log_2 : "ਲਾਗਰ ਨੂੰ ਚਾਲੂ ਕੀਤਾ ਗਿਆ ਹੈ."
+log_3 : "ਲਾਗਰ ਨੂੰ ਬੰਦ ਕੀਤਾ ਗਿਆ ਹੈ."
+
+broad_1 : "» ਬਰੋਡਕਾਸਟਿੰਗ ਸ਼ੁਰੂ ਹੋ ਗਈ ਹੈ..."
+broad_2 : "ਉਦਾਹਰਨ:\n\n/broadcast [ਸੁਨੇਹਾ ਜਾਂ ਉਤਤਰ ਦੇਣ ਲਈ ਇੱਕ ਸੁਨੇਹਾ]"
+broad_3 : "» {0} ਸੁਨੇਹਿਆਂ ਨੂੰ {1} ਪਿੰਸ ਨਾਲ ਚੈਟਾਂ ਵਿੱਚ ਬ੍ਰੋਡਕਾਸਟ ਕੀਤਾ ਗਿਆ ਹੈ."
+broad_4 : "» {0} ਯੂਜ਼ਰਾਂ ਨੂੰ ਸੁਨੇਹਾ ਮੁੱਕਾਬਲਾ ਕੀਤਾ ਗਿਆ ਹੈ."
+broad_5 : "» ਅਸਿਸਟੈਂਟ ਬਰੋਡਕਾਸਟ ਸ਼ੁਰੂ ਹੋ ਰਿਹਾ ਹੈ..."
+broad_6 : "➻ ਅਸਿਸਟੈਂਟ ਬਰੋਡਕਾਸਟ :\n\n"
+broad_7 : "↬ ਅਸਿਸਟੈਂਟ ਨੇ {0} ਬਰੋਡਕਾਸਟ ਕੀਤਾ {1} ਚੈਟਾਂ ਵਿੱਚ."
+broad_8 : "» ਕਿਰਪਾ ਕਰਕੇ ਬਰੋਡਕਾਸਟ ਕਰਨ ਲਈ ਕੁਝ ਟੈਕਸਟ ਪ੍ਰਦਾਨ ਕਰੋ."
+
+server_1 : "» ਲੋਗਾਂ ਪ੍ਰਾਪਤ ਕਰਨ ਵਿੱਚ ਅਸਫਲ ਹੋਇਆ।"
+server_2 : "ਕ੍ਰਿਪਾ ਕਰਕੇ ਯਕੀਨੀ ਬਣਾਓ ਕਿ ਤੁਹਾਡੀ Heroku API ਕੀ ਅਤੇ ਐਪ ਨਾਮ ਠੀਕ ਤਰ੍ਹਾ ਸੰਰਚਿਤ ਹਨ।"
+server_3 : "ਉਪਲਬਧ ਅੱਪਡੇਟਾਂ ਦੀ ਜਾਂਚ ਹੋ ਰਹੀ ਹੈ..."
+server_4 : "Git ਕੰਮਾਂਡ ਦਾ ਤਰਾਂਗ ਹੋਇਆ ਹੈ।"
+server_5 : "ਅਮਾਨਤ ਨਾਂ ਦੀ ਅਮਾਨਤ ਨਹੀਂ ਹੈ।"
+server_6 : "» ਬੋਟ ਅੱਪ-ਟੁ-ਡੇਟ ਹੈ।"
+server_7 : "» ਬੋਟ ਨੂੰ ਸਫਲਤਾਪੂਰਵਕ ਅੱਪਡੇਟ ਕੀਤਾ ਗਿਆ ਹੈ! ਹੁਣ ਕੁਝ ਮਿੰਟ ਬਾਅਦ ਬੋਟ ਨੂੰ ਮੁੜ-ਆਰੰਭ ਹੋਣ ਦੀ ਪ੍ਰਤੀਕਰਤਾ ਕਰੋ ਅਤੇ ਤਬਦੀਲੀਆਂ ਨੂੰ ਪੁੱਸ਼ ਕਰੋ!"
+server_8 : "{0} ਮੁੜ-ਆਰੰਭ ਹੋ ਰਿਹਾ ਹੈ...\n\n15-20 ਸਕਿੰਟਾਂ ਬਾਅਦ ਤੁਸੀਂ ਮਾਜੂਦਾ ਖੇਡਣਾ ਸ਼ੁਰੂ ਕਰ ਸਕਦੇ ਹੋ।"
+server_9 : "ਕੁਝ ਗਲਤੀ ਹੋਈ ਹੈ, ਕਿਰਪਾ ਕਰਕੇ ਲੋਗ ਜਾਂਚੋ।"
+server_10 : "ਕਿਸੇ ਕਾਰਣਵਾਂ, #ਅੱਪਡੇਟਰ 'ਤੇ ਇੱਕ ਅਸਮਾਨ ਵਾਰੀ ਆਯੋਜਨ ਹੋ ਗਿਆ ਹੈ: {0}
"
+server_11 : "» ਸਪੀਡਟੈਸਟ ਚੱਲ ਰਿਹਾ ਹੈ..."
+server_12 : "⇆ ਡਾਊਨਲੋਡ ਸਪੀਡਟੈਸਟ ਚੱਲ ਰਿਹਾ ਹੈ..."
+server_13 : "⇆ ਅੱਪਲੋਡ ਸਪੀਡਟੈਸਟ ਚੱਲ ਰਿਹਾ ਹੈ..."
+server_14 : "↻ ਸਪੀਡਟੈਸਟ ਨਤੀਜਿਆਂ ਨੂੰ ਸਾਂਝਾ ਕਰ ਰਿਹਾ ਹੈ..."
+server_15 : "✯ ਸਪੀਡਟੈਸਟ ਨਤੀਜੇ ✯\n\nਗਾਹਕ :\n» ਆਈਪੀ : {0}\n» ਦੇਸ਼ : {1}\n\nਸਰਵਰ :\n» ਨਾਮ : {2}\n» ਦੇਸ਼ : {3}, {4}\n» ਸਪਾਂਸਰ : {5}\n» ਲੈਟੈਂਸੀ : {6}\n» ਪਿੰਗ : {7}"
+
+gban_1 : "» ਕਿਉਂ ਤੁਸੀਂ ਆਪਣੇ ਆਪ ਨੂੰ gban ਕਰਨਾ ਚਾਹੁੰਦੇ ਹੋ?"
+gban_2 : "» ਕਿਉਂ ਮੈਂ ਆਪਣੇ ਆਪ ਨੂੰ gban ਕਰਨਾ ਚਾਹੁੰਦਾ ਹਾਂ?"
+gban_3 : "» ਤੁਸੀਂ ਮੇਰੀ sudoers ਨੂੰ gban ਨਹੀਂ ਕਰ ਸਕਦੇ."
+gban_4 : "» {0} ਹੀ ਪਹਿਲਾਂ ਹੀ ਬੋਟ ਤੋਂ ਗਲੋਬਲ ਤੌਰ 'ਤੇ ਬੈਨ ਹੈ।"
+gban_5 : "» {0} 'ਤੇ ਗਲੋਬਲ ਬੈਨ ਸ਼ੁਰੂ ਹੋ ਰਿਹਾ ਹੈ।\n\nਉਮੀਦਵਾਰ ਸਮਾਂ: {1}"
+gban_6 : "{0} 'ਤੇ ਨਵਾਂ ਗਲੋਬਲ ਬੈਨ:\n\nਉੱਤਪਤਤ: {1} [{2}
]\nਯੂਜ਼ਰ: {3}\nਯੂਜ਼ਰ ਆਈਡੀ: {4}\n\nਬੈਨ ਕਰਨ ਵਾਲਾ: {5}\nਚੈਟਸ: {6}"
+gban_7 : "» {0} ਬੋਟ ਤੋਂ ਗਲੋਬਲ ਤੌਰ 'ਤੇ ਬੈਨ ਨਹੀਂ ਹੈ।"
+gban_8 : "» {0} 'ਤੋਂ ਗਲੋਬਲ ਬੈਨ ਨੂੰ ਹਟਾ ਦਿੱਤਾ ਜਾ ਰਿਹਾ ਹੈ।\n\nਉਮੀਦਵਾਰ ਸਮਾਂ: {1}"
+gban_9 : "» {0} 'ਤੋਂ ਗਲੋਬਲ ਬੈਨ ਨੂੰ ਹਟਾ ਦਿੱਤਾ ਜਾ ਰਿਹਾ ਹੈ।\n\n{1} ਚੈਟਾਂ ਵਿਚ ਵਾਪਸ ਆ ਸਕਦਾ ਹੈ।"
+gban_10 : "» ਕੋਈ ਵੀ ਬੋਟ ਤੋਂ ਗਲੋਬਲ ਤੌਰ 'ਤੇ ਬੈਨ ਨਹੀਂ ਹੈ।"
+gban_11 : "» ਗਲੋਬਲ ਬੈਨ ਕਿਏ ਗਏ ਯੂਜ਼ਰਾਂ ਦੀ ਸੂਚੀ ਲਵੋ ਰਹੀ ਹੈ..."
+gban_12 : "🙂 ਗਲੋਬਲ ਬੈਨ ਕੀਤੇ ਗਏ ਯੂਜ਼ਰ:\n\n"
diff --git a/unknown_errors.txt b/unknown_errors.txt
new file mode 100644
index 000000000000..c62f68cb38b0
--- /dev/null
+++ b/unknown_errors.txt
@@ -0,0 +1,3 @@
+2024-12-22 10:26:39.611583 [400 QUOTE_TEXT_INVALID] messages.SendMedia
+2024-12-22 12:40:42.870678 [400 QUOTED_MESSAGE_INVALID] messages.SendMedia
+2024-12-22 12:41:55.896269 [400 QUOTED_MESSAGE_INVALID] messages.SendMedia