diff --git a/.gitattributes b/.gitattributes
index a6344aac8c09253b3b630fb776ae94478aa0275b..a3534199e081cc8b583d64846220650a6e9b7995 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -33,3 +33,13 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
*.zip filter=lfs diff=lfs merge=lfs -text
*.zst filter=lfs diff=lfs merge=lfs -text
*tfevents* filter=lfs diff=lfs merge=lfs -text
+resources/extras/logo_readme.jpg filter=lfs diff=lfs merge=lfs -text
+resources/extras/ultroid.jpg filter=lfs diff=lfs merge=lfs -text
+resources/fonts/11.otf filter=lfs diff=lfs merge=lfs -text
+resources/fonts/3.ttf filter=lfs diff=lfs merge=lfs -text
+resources/fonts/default.ttf filter=lfs diff=lfs merge=lfs -text
+resources/fonts/DroidSansMono.ttf filter=lfs diff=lfs merge=lfs -text
+resources/fonts/Quivira.otf filter=lfs diff=lfs merge=lfs -text
+resources/fonts/Roboto-Italic.ttf filter=lfs diff=lfs merge=lfs -text
+resources/fonts/Roboto-Medium.ttf filter=lfs diff=lfs merge=lfs -text
+resources/fonts/Roboto-Regular.ttf filter=lfs diff=lfs merge=lfs -text
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..ce5d8ae1e0e50fb836a0b1f846cf923f4e17a60f
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,47 @@
+.env
+*.session-journal
+*.session
+build
+test*
+*.mp3
+*.webm
+*.webp
+*.mp4
+*.tgs
+*.txt
+/*.jpg
+/*.png
+/*.mp4
+*.log
+target/npmlist.json
+package-lock.json
+ultroid.json
+resources/extras/thumbnail.jpg
+resources/auth/
+
+# Directories
+addons/
+vcbot/
+__pycache__/
+venv/
+node_modules/
+glitch_me/
+src/glitch-me
+.idea/
+.vscode/
+temp/
+bin-debug/
+bin-release/
+[Oo]bj/
+[Bb]in/
+.settings/
+*.swf
+*.air
+*.ipa
+*.apk
+
+# temporary files
+*.raw
+
+# fly.io configs
+fly.toml
\ No newline at end of file
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000000000000000000000000000000000000..27ae6e6771aadda08f1b70b4ab21d6b6377beef3
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,20 @@
+# Ultroid - UserBot
+# Copyright (C) 2021-2025 TeamUltroid
+# This file is a part of < https://github.com/TeamUltroid/Ultroid/ >
+# PLease read the GNU Affero General Public License in .
+
+FROM theteamultroid/ultroid:main
+
+# set timezone
+ENV TZ=Asia/Karachi
+RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
+
+COPY installer.sh .
+
+RUN bash installer.sh
+
+# changing workdir
+WORKDIR "/root/TeamUltroid"
+
+# start the bot.
+CMD ["bash", "startup"]
diff --git a/assistant/__init__.py b/assistant/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..2a2a775b4dcf83af340339e56d4b8c8b94f1d951
--- /dev/null
+++ b/assistant/__init__.py
@@ -0,0 +1,34 @@
+# Ultroid - UserBot
+# Copyright (C) 2021-2025 TeamUltroid
+#
+# This file is a part of < https://github.com/TeamUltroid/Ultroid/ >
+# PLease read the GNU Affero General Public License in
+# .
+
+from telethon import Button, custom
+
+from plugins import ATRA_COL, InlinePlugin
+from pyUltroid import *
+from pyUltroid import _ult_cache
+from pyUltroid._misc import owner_and_sudos
+from pyUltroid._misc._assistant import asst_cmd, callback, in_pattern
+from pyUltroid.fns.helper import *
+from pyUltroid.fns.tools import get_stored_file
+from strings import get_languages, get_string
+
+OWNER_NAME = ultroid_bot.full_name
+OWNER_ID = ultroid_bot.uid
+
+AST_PLUGINS = {}
+
+
+async def setit(event, name, value):
+ try:
+ udB.set_key(name, value)
+ except BaseException as er:
+ LOGS.exception(er)
+ return await event.edit("`Something Went Wrong`")
+
+
+def get_back_button(name):
+ return [Button.inline("« Bᴀᴄᴋ", data=f"{name}")]
diff --git a/assistant/callbackstuffs.py b/assistant/callbackstuffs.py
new file mode 100644
index 0000000000000000000000000000000000000000..15598fee2c9f5017cbd8140ed19718d3d6d6eb61
--- /dev/null
+++ b/assistant/callbackstuffs.py
@@ -0,0 +1,1318 @@
+# Ultroid - UserBot
+# Copyright (C) 2021-2025 TeamUltroid
+#
+# This file is a part of < https://github.com/TeamUltroid/Ultroid/ >
+# PLease read the GNU Affero General Public License in
+# .
+
+import ast
+import asyncio
+import re
+import sys
+import time
+from asyncio.exceptions import TimeoutError as AsyncTimeOut
+from os import execl, remove
+from random import choice
+
+from bs4 import BeautifulSoup as bs
+
+try:
+ from pyUltroid.fns.gDrive import GDriveManager
+except ImportError:
+ GDriveManager = None
+from telethon import Button, events
+from catbox import CatboxUploader
+from telethon.tl.types import MessageMediaWebPage
+from telethon.utils import get_peer_id
+
+from pyUltroid.fns.helper import fast_download, progress
+from pyUltroid.fns.tools import Carbon, async_searcher, get_paste, telegraph_client
+from pyUltroid.startup.loader import Loader
+
+from . import *
+
+# --------------------------------------------------------------------#
+telegraph = telegraph_client()
+GDrive = GDriveManager() if GDriveManager else None
+uploader = CatboxUploader()
+# --------------------------------------------------------------------#
+
+def text_to_url(event):
+ """function to get media url (with|without) Webpage"""
+ if isinstance(event.media, MessageMediaWebPage):
+ webpage = event.media.webpage
+ if not isinstance(webpage, types.WebPageEmpty) and webpage.type in ["photo"]:
+ return webpage.display_url
+ return event.text
+
+
+# --------------------------------------------------------------------#
+
+_buttons = {
+ "otvars": {
+ "text": "Other Variables to set for @TeamUltroid:",
+ "buttons": [
+ [
+ Button.inline("Tᴀɢ Lᴏɢɢᴇʀ", data="taglog"),
+ Button.inline("SᴜᴘᴇʀFʙᴀɴ", data="cbs_sfban"),
+ ],
+ [
+ Button.inline("Sᴜᴅᴏ Mᴏᴅᴇ", data="sudo"),
+ Button.inline("Hᴀɴᴅʟᴇʀ", data="hhndlr"),
+ ],
+ [
+ Button.inline("Exᴛʀᴀ Pʟᴜɢɪɴs", data="plg"),
+ Button.inline("Aᴅᴅᴏɴs", data="eaddon"),
+ ],
+ [
+ Button.inline("Eᴍᴏᴊɪ ɪɴ Hᴇʟᴘ", data="emoj"),
+ Button.inline("Sᴇᴛ ɢDʀɪᴠᴇ", data="gdrive"),
+ ],
+ [
+ Button.inline("Iɴʟɪɴᴇ Pɪᴄ", data="inli_pic"),
+ Button.inline("Sᴜᴅᴏ HNDLR", data="shndlr"),
+ ],
+ [Button.inline("Dᴜᴀʟ Mᴏᴅᴇ", "cbs_oofdm")],
+ [Button.inline("« Bᴀᴄᴋ", data="setter")],
+ ],
+ },
+ "sfban": {
+ "text": "SuperFban Settings:",
+ "buttons": [
+ [Button.inline("FBᴀɴ Gʀᴏᴜᴘ", data="sfgrp")],
+ [Button.inline("Exᴄʟᴜᴅᴇ Fᴇᴅs", data="abs_sfexf")],
+ [Button.inline("« Bᴀᴄᴋ", data="cbs_otvars")],
+ ],
+ },
+ "apauto": {
+ "text": "This'll auto approve on outgoing messages",
+ "buttons": [
+ [Button.inline("Aᴜᴛᴏ Aᴘᴘʀᴏᴠᴇ ON", data="apon")],
+ [Button.inline("Aᴜᴛᴏ Aᴘᴘʀᴏᴠᴇ OFF", data="apof")],
+ [Button.inline("« Bᴀᴄᴋ", data="cbs_pmcstm")],
+ ],
+ },
+ "alvcstm": {
+ "text": f"Customise your {HNDLR}alive. Choose from the below options -",
+ "buttons": [
+ [Button.inline("Aʟɪᴠᴇ Tᴇxᴛ", data="abs_alvtx")],
+ [Button.inline("Aʟɪᴠᴇ ᴍᴇᴅɪᴀ", data="alvmed")],
+ [Button.inline("Dᴇʟᴇᴛᴇ Aʟɪᴠᴇ Mᴇᴅɪᴀ", data="delmed")],
+ [Button.inline("« Bᴀᴄᴋ", data="setter")],
+ ],
+ },
+ "pmcstm": {
+ "text": "Customise your PMPERMIT Settings -",
+ "buttons": [
+ [
+ Button.inline("Pᴍ Tᴇxᴛ", data="pmtxt"),
+ Button.inline("Pᴍ Mᴇᴅɪᴀ", data="pmmed"),
+ ],
+ [
+ Button.inline("Aᴜᴛᴏ Aᴘᴘʀᴏᴠᴇ", data="cbs_apauto"),
+ Button.inline("PMLOGGER", data="pml"),
+ ],
+ [
+ Button.inline("Sᴇᴛ Wᴀʀɴs", data="swarn"),
+ Button.inline("Dᴇʟᴇᴛᴇ Pᴍ Mᴇᴅɪᴀ", data="delpmmed"),
+ ],
+ [Button.inline("PMPermit Type", data="cbs_pmtype")],
+ [Button.inline("« Bᴀᴄᴋ", data="cbs_ppmset")],
+ ],
+ },
+ "pmtype": {
+ "text": "Select the type of PMPermit needed.",
+ "buttons": [
+ [Button.inline("Inline", data="inpm_in")],
+ [Button.inline("Normal", data="inpm_no")],
+ [Button.inline("« Bᴀᴄᴋ", data="cbs_pmcstm")],
+ ],
+ },
+ "ppmset": {
+ "text": "PMPermit Settings:",
+ "buttons": [
+ [Button.inline("Tᴜʀɴ PMPᴇʀᴍɪᴛ Oɴ", data="pmon")],
+ [Button.inline("Tᴜʀɴ PMPᴇʀᴍɪᴛ Oғғ", data="pmoff")],
+ [Button.inline("Cᴜsᴛᴏᴍɪᴢᴇ PMPᴇʀᴍɪᴛ", data="cbs_pmcstm")],
+ [Button.inline("« Bᴀᴄᴋ", data="setter")],
+ ],
+ },
+ "chatbot": {
+ "text": "From This Feature U can chat with ppls Via ur Assistant Bot.\n[More info](https://t.me/UltroidUpdates/2)",
+ "buttons": [
+ [
+ Button.inline("Cʜᴀᴛ Bᴏᴛ Oɴ", data="onchbot"),
+ Button.inline("Cʜᴀᴛ Bᴏᴛ Oғғ", data="ofchbot"),
+ ],
+ [
+ Button.inline("Bᴏᴛ Wᴇʟᴄᴏᴍᴇ", data="bwel"),
+ Button.inline("Bᴏᴛ Wᴇʟᴄᴏᴍᴇ Mᴇᴅɪᴀ", data="botmew"),
+ ],
+ [Button.inline("Bᴏᴛ Iɴғᴏ Tᴇxᴛ", data="botinfe")],
+ [Button.inline("Fᴏʀᴄᴇ Sᴜʙsᴄʀɪʙᴇ", data="pmfs")],
+ [Button.inline("« Bᴀᴄᴋ", data="setter")],
+ ],
+ },
+ "vcb": {
+ "text": "From This Feature U can play songs in group voice chat\n\n[moreinfo](https://t.me/UltroidUpdates/4)",
+ "buttons": [
+ [Button.inline("VC Sᴇssɪᴏɴ", data="abs_vcs")],
+ [Button.inline("« Bᴀᴄᴋ", data="setter")],
+ ],
+ },
+ "oofdm": {
+ "text": "About [Dual Mode](https://t.me/UltroidUpdates/18)",
+ "buttons": [
+ [
+ Button.inline("Dᴜᴀʟ Mᴏᴅᴇ Oɴ", "dmof"),
+ Button.inline("Dᴜᴀʟ Mᴏᴅᴇ Oғғ", "dmof"),
+ ],
+ [Button.inline("Dᴜᴀʟ Mᴏᴅᴇ Hɴᴅʟʀ", "dmhn")],
+ [Button.inline("« Back", data="cbs_otvars")],
+ ],
+ },
+ "apiset": {
+ "text": get_string("ast_1"),
+ "buttons": [
+ [Button.inline("Remove.bg API", data="abs_rmbg")],
+ [Button.inline("DEEP API", data="abs_dapi")],
+ [Button.inline("OCR API", data="abs_oapi")],
+ [Button.inline("« Back", data="setter")],
+ ],
+ },
+}
+
+_convo = {
+ "rmbg": {
+ "var": "RMBG_API",
+ "name": "Remove.bg API Key",
+ "text": get_string("ast_2"),
+ "back": "cbs_apiset",
+ },
+ "dapi": {
+ "var": "DEEP_AI",
+ "name": "Deep AI Api Key",
+ "text": "Get Your Deep Api from deepai.org and send here.",
+ "back": "cbs_apiset",
+ },
+ "oapi": {
+ "var": "OCR_API",
+ "name": "Ocr Api Key",
+ "text": "Get Your OCR api from ocr.space and send that Here.",
+ "back": "cbs_apiset",
+ },
+ "pmlgg": {
+ "var": "PMLOGGROUP",
+ "name": "Pm Log Group",
+ "text": "Send chat id of chat which you want to save as Pm log Group.",
+ "back": "pml",
+ },
+ "vcs": {
+ "var": "VC_SESSION",
+ "name": "Vc Session",
+ "text": "**Vc session**\nEnter the New session u generated for vc bot.\n\nUse /cancel to terminate the operation.",
+ "back": "cbs_vcb",
+ },
+ "settag": {
+ "var": "TAG_LOG",
+ "name": "Tag Log Group",
+ "text": f"Make a group, add your assistant and make it admin.\nGet the `{HNDLR}id` of that group and send it here for tag logs.\n\nUse /cancel to cancel.",
+ "back": "taglog",
+ },
+ "alvtx": {
+ "var": "ALIVE_TEXT",
+ "name": "Alive Text",
+ "text": "**Alive Text**\nEnter the new alive text.\n\nUse /cancel to terminate the operation.",
+ "back": "cbs_alvcstm",
+ },
+ "sfexf": {
+ "var": "EXCLUDE_FED",
+ "name": "Excluded Fed",
+ "text": "Send the Fed IDs you want to exclude in the ban. Split by a space.\neg`id1 id2 id3`\nSet is as `None` if you dont want any.\nUse /cancel to go back.",
+ "back": "cbs_sfban",
+ },
+}
+
+
+TOKEN_FILE = "resources/auths/auth_token.txt"
+
+
+@callback(
+ re.compile(
+ "sndplug_(.*)",
+ ),
+ owner=True,
+)
+async def send(eve):
+ key, name = (eve.data_match.group(1)).decode("UTF-8").split("_")
+ thumb = "resources/extras/inline.jpg"
+ await eve.answer("■ Sending ■")
+ data = f"uh_{key}_"
+ index = None
+ if "|" in name:
+ name, index = name.split("|")
+ key = "plugins" if key == "Official" else key.lower()
+ plugin = f"{key}/{name}.py"
+ _ = f"pasta-{plugin}"
+ if index is not None:
+ data += f"|{index}"
+ _ += f"|{index}"
+ buttons = [
+ [
+ Button.inline(
+ "« Pᴀsᴛᴇ »",
+ data=_,
+ )
+ ],
+ [
+ Button.inline("« Bᴀᴄᴋ", data=data),
+ ],
+ ]
+ try:
+ await eve.edit(file=plugin, thumb=thumb, buttons=buttons)
+ except Exception as er:
+ await eve.answer(str(er), alert=True)
+
+
+heroku_api, app_name = Var.HEROKU_API, Var.HEROKU_APP_NAME
+
+
+@callback("updatenow", owner=True)
+async def update(eve):
+ repo = Repo()
+ ac_br = repo.active_branch
+ ups_rem = repo.remote("upstream")
+ if heroku_api:
+ import heroku3
+
+ try:
+ heroku = heroku3.from_key(heroku_api)
+ heroku_app = None
+ heroku_applications = heroku.apps()
+ except BaseException as er:
+ LOGS.exception(er)
+ return await eve.edit("`Wrong HEROKU_API.`")
+ for app in heroku_applications:
+ if app.name == app_name:
+ heroku_app = app
+ if not heroku_app:
+ await eve.edit("`Wrong HEROKU_APP_NAME.`")
+ repo.__del__()
+ return
+ await eve.edit(get_string("clst_1"))
+ ups_rem.fetch(ac_br)
+ repo.git.reset("--hard", "FETCH_HEAD")
+ heroku_git_url = heroku_app.git_url.replace(
+ "https://", f"https://api:{heroku_api}@"
+ )
+
+ if "heroku" in repo.remotes:
+ remote = repo.remote("heroku")
+ remote.set_url(heroku_git_url)
+ else:
+ remote = repo.create_remote("heroku", heroku_git_url)
+ try:
+ remote.push(refspec=f"HEAD:refs/heads/{ac_br}", force=True)
+ except GitCommandError as error:
+ await eve.edit(f"`Here is the error log:\n{error}`")
+ repo.__del__()
+ return
+ await eve.edit("`Successfully Updated!\nRestarting, please wait...`")
+ else:
+ await eve.edit(get_string("clst_1"))
+ call_back()
+ await bash("git pull && pip3 install -r requirements.txt")
+ await bash("pip3 install -r requirements.txt --break-system-packages")
+ execl(sys.executable, sys.executable, "-m", "pyUltroid")
+
+@callback(re.compile("changes(.*)"), owner=True)
+async def changes(okk):
+ match = okk.data_match.group(1).decode("utf-8")
+ await okk.answer(get_string("clst_3"))
+ repo = Repo.init()
+ button = [[Button.inline("Update Now", data="updatenow")]]
+ changelog, tl_chnglog = await gen_chlog(
+ repo, f"HEAD..upstream/{repo.active_branch}"
+ )
+ cli = "\n\nClick the below button to update!"
+ if not match:
+ try:
+ if len(tl_chnglog) > 700:
+ tl_chnglog = f"{tl_chnglog[:700]}..."
+ button.append([Button.inline("View Complete", "changesall")])
+ await okk.edit("• Writing Changelogs 📝 •")
+ img = await Carbon(
+ file_name="changelog",
+ code=tl_chnglog,
+ backgroundColor=choice(ATRA_COL),
+ language="md",
+ )
+ return await okk.edit(
+ f"**• Ultroid Userbot •**{cli}", file=img, buttons=button
+ )
+ except Exception as er:
+ LOGS.exception(er)
+ changelog_str = changelog + cli
+ if len(changelog_str) > 1024:
+ await okk.edit(get_string("upd_4"))
+ await asyncio.sleep(2)
+ with open("ultroid_updates.txt", "w+") as file:
+ file.write(tl_chnglog)
+ await okk.edit(
+ get_string("upd_5"),
+ file="ultroid_updates.txt",
+ buttons=button,
+ )
+ remove("ultroid_updates.txt")
+ return
+ await okk.edit(
+ changelog_str,
+ buttons=button,
+ parse_mode="html",
+ )
+
+
+@callback(
+ re.compile(
+ "pasta-(.*)",
+ ),
+ owner=True,
+)
+async def _(e):
+ ok = (e.data_match.group(1)).decode("UTF-8")
+ index = None
+ if "|" in ok:
+ ok, index = ok.split("|")
+ with open(ok, "r") as hmm:
+ _, data = await get_paste(hmm.read())
+ if not data.get("link"):
+ return await e.answer(key[:30], alert=True)
+ if not key.startswith("http"):
+ link, raw = data["link"], data["raw"]
+ else:
+ link = key
+ raw = f"{key}/raw"
+ if ok.startswith("addons"):
+ key = "Addons"
+ elif ok.startswith("vcbot"):
+ key = "VCBot"
+ else:
+ key = "Official"
+ data = f"uh_{key}_"
+ if index is not None:
+ data += f"|{index}"
+ await e.edit(
+ "",
+ buttons=[
+ [Button.url("Lɪɴᴋ", link), Button.url("Rᴀᴡ", raw)],
+ [Button.inline("« Bᴀᴄᴋ", data=data)],
+ ],
+ )
+
+
+@callback(re.compile("cbs_(.*)"), owner=True)
+async def _edit_to(event):
+ match = event.data_match.group(1).decode("utf-8")
+ data = _buttons.get(match)
+ if not data:
+ return
+ await event.edit(data["text"], buttons=data["buttons"], link_preview=False)
+
+
+@callback(re.compile("abs_(.*)"), owner=True)
+async def convo_handler(event: events.CallbackQuery):
+ match = event.data_match.group(1).decode("utf-8")
+ if not _convo.get(match):
+ return
+ await event.delete()
+ get_ = _convo[match]
+ back = get_["back"]
+ async with event.client.conversation(event.sender_id) as conv:
+ await conv.send_message(get_["text"])
+ response = await conv.get_response()
+ themssg = response.message
+ try:
+ themssg = ast.literal_eval(themssg)
+ except Exception:
+ pass
+ if themssg == "/cancel":
+ return await conv.send_message(
+ "Cancelled!!",
+ buttons=get_back_button(back),
+ )
+ await setit(event, get_["var"], themssg)
+ await conv.send_message(
+ f"{get_['name']} changed to `{themssg}`",
+ buttons=get_back_button(back),
+ )
+
+
+@callback("authorise", owner=True)
+async def _(e):
+ if not e.is_private:
+ return
+ url = GDrive._create_token_file()
+ await e.edit("Go to the below link and send the code!")
+ async with asst.conversation(e.sender_id) as conv:
+ await conv.send_message(url)
+ code = await conv.get_response()
+ if GDrive._create_token_file(code=code.text):
+ await conv.send_message(
+ "`Success!\nYou are all set to use Google Drive with Ultroid Userbot.`",
+ buttons=Button.inline("Main Menu", data="setter"),
+ )
+ else:
+ await conv.send_message("Wrong code! Click authorise again.")
+
+
+@callback("folderid", owner=True, func=lambda x: x.is_private)
+async def _(e):
+ if not e.is_private:
+ return
+ msg = (
+ "Send your FOLDER ID\n\n"
+ + "For FOLDER ID:\n"
+ + "1. Open Google Drive App.\n"
+ + "2. Create Folder.\n"
+ + "3. Make that folder public.\n"
+ + "4. Send link of that folder."
+ )
+ await e.delete()
+ async with asst.conversation(e.sender_id, timeout=150) as conv:
+ await conv.send_message(msg)
+ repl = await conv.get_response()
+ id = repl.text
+ if id.startswith("https"):
+ id = id.split("?id=")[-1]
+ udB.set_key("GDRIVE_FOLDER_ID", id)
+ await repl.reply(
+ "`Success.`",
+ buttons=get_back_button("gdrive"),
+ )
+
+
+@callback("gdrive", owner=True)
+async def _(e):
+ if not e.is_private:
+ return
+ await e.edit(
+ "Click Authorise and send the code.\n\nYou can use your own CLIENT ID and SECRET by [this](https://t.me/UltroidUpdates/37)",
+ buttons=[
+ [
+ Button.inline("Folder ID", data="folderid"),
+ Button.inline("Authorise", data="authorise"),
+ ],
+ [Button.inline("« Back", data="cbs_otvars")],
+ ],
+ link_preview=False,
+ )
+
+
+@callback("dmof", owner=True)
+async def rhwhe(e):
+ if udB.get_key("DUAL_MODE"):
+ udB.del_key("DUAL_MODE")
+ key = "Off"
+ else:
+ udB.set_key("DUAL_MODE", "True")
+ key = "On"
+ Msg = f"Dual Mode : {key}"
+ await e.edit(Msg, buttons=get_back_button("cbs_otvars"))
+
+
+@callback("dmhn", owner=True)
+async def hndlrr(event):
+ await event.delete()
+ pru = event.sender_id
+ var = "DUAL_HNDLR"
+ name = "Dual Handler"
+ CH = udB.get_key(var) or "/"
+ async with event.client.conversation(pru) as conv:
+ await conv.send_message(
+ f"Send The Symbol Which u want as Handler/Trigger to use your Assistant bot\nUr Current Handler is [ `{CH}` ]\n\n use /cancel to cancel.",
+ )
+ response = conv.wait_event(events.NewMessage(chats=pru))
+ response = await response
+ themssg = response.message.message
+ if themssg == "/cancel":
+ await conv.send_message(
+ "Cancelled!!",
+ buttons=get_back_button("cbs_otvars"),
+ )
+ elif len(themssg) > 1:
+ await conv.send_message(
+ "Incorrect Handler",
+ buttons=get_back_button("cbs_otvars"),
+ )
+ else:
+ await setit(event, var, themssg)
+ await conv.send_message(
+ f"{name} changed to {themssg}",
+ buttons=get_back_button("cbs_otvars"),
+ )
+
+
+@callback("emoj", owner=True)
+async def emoji(event):
+ await event.delete()
+ pru = event.sender_id
+ var = "EMOJI_IN_HELP"
+ name = f"Emoji in `{HNDLR}help` menu"
+ async with event.client.conversation(pru) as conv:
+ await conv.send_message("Send emoji u want to set 🙃.\n\nUse /cancel to cancel.")
+ response = conv.wait_event(events.NewMessage(chats=pru))
+ response = await response
+ themssg = response.message.message
+ if themssg == "/cancel":
+ await conv.send_message(
+ "Cancelled!!",
+ buttons=get_back_button("cbs_otvars"),
+ )
+ elif themssg.startswith(("/", HNDLR)):
+ await conv.send_message(
+ "Incorrect Emoji",
+ buttons=get_back_button("cbs_otvars"),
+ )
+ else:
+ await setit(event, var, themssg)
+ await conv.send_message(
+ f"{name} changed to {themssg}\n",
+ buttons=get_back_button("cbs_otvars"),
+ )
+
+
+@callback("plg", owner=True)
+async def pluginch(event):
+ await event.delete()
+ pru = event.sender_id
+ var = "PLUGIN_CHANNEL"
+ name = "Plugin Channel"
+ async with event.client.conversation(pru) as conv:
+ await conv.send_message(
+ "Send id or username of a channel from where u want to install all plugins\n\nOur Channel~ @ultroidplugins\n\nUse /cancel to cancel.",
+ )
+ response = conv.wait_event(events.NewMessage(chats=pru))
+ response = await response
+ themssg = response.message.message
+ if themssg == "/cancel":
+ await conv.send_message(
+ "Cancelled!!",
+ buttons=get_back_button("cbs_otvars"),
+ )
+ elif themssg.startswith(("/", HNDLR)):
+ await conv.send_message(
+ "Incorrect channel",
+ buttons=get_back_button("cbs_otvars"),
+ )
+ else:
+ await setit(event, var, themssg)
+ await conv.send_message(
+ f"{name} changed to {themssg}\n After Setting All Things Do Restart",
+ buttons=get_back_button("cbs_otvars"),
+ )
+
+
+@callback("hhndlr", owner=True)
+async def hndlrr(event):
+ await event.delete()
+ pru = event.sender_id
+ var = "HNDLR"
+ name = "Handler/ Trigger"
+ async with event.client.conversation(pru) as conv:
+ await conv.send_message(
+ f"Send The Symbol Which u want as Handler/Trigger to use bot\nUr Current Handler is [ `{HNDLR}` ]\n\n use /cancel to cancel.",
+ )
+ response = conv.wait_event(events.NewMessage(chats=pru))
+ response = await response
+ themssg = response.message.message
+ if themssg == "/cancel":
+ await conv.send_message(
+ "Cancelled!!",
+ buttons=get_back_button("cbs_otvars"),
+ )
+ elif len(themssg) > 1:
+ await conv.send_message(
+ "Incorrect Handler",
+ buttons=get_back_button("cbs_otvars"),
+ )
+ elif themssg.startswith(("/", "#", "@")):
+ await conv.send_message(
+ "This cannot be used as handler",
+ buttons=get_back_button("cbs_otvars"),
+ )
+ else:
+ await setit(event, var, themssg)
+ await conv.send_message(
+ f"{name} changed to {themssg}",
+ buttons=get_back_button("cbs_otvars"),
+ )
+
+
+@callback("shndlr", owner=True)
+async def hndlrr(event):
+ await event.delete()
+ pru = event.sender_id
+ var = "SUDO_HNDLR"
+ name = "Sudo Handler"
+ async with event.client.conversation(pru) as conv:
+ await conv.send_message(
+ "Send The Symbol Which u want as Sudo Handler/Trigger to use bot\n\n use /cancel to cancel."
+ )
+
+ response = conv.wait_event(events.NewMessage(chats=pru))
+ response = await response
+ themssg = response.message.message
+ if themssg == "/cancel":
+ await conv.send_message(
+ "Cancelled!!",
+ buttons=get_back_button("cbs_otvars"),
+ )
+ elif len(themssg) > 1:
+ await conv.send_message(
+ "Incorrect Handler",
+ buttons=get_back_button("cbs_otvars"),
+ )
+ elif themssg.startswith(("/", "#", "@")):
+ await conv.send_message(
+ "This cannot be used as handler",
+ buttons=get_back_button("cbs_otvars"),
+ )
+ else:
+ await setit(event, var, themssg)
+ await conv.send_message(
+ f"{name} changed to {themssg}",
+ buttons=get_back_button("cbs_otvars"),
+ )
+
+
+@callback("taglog", owner=True)
+async def tagloggrr(e):
+ BUTTON = [
+ [Button.inline("SET TAG LOG", data="abs_settag")],
+ [Button.inline("DELETE TAG LOG", data="deltag")],
+ get_back_button("cbs_otvars"),
+ ]
+ await e.edit(
+ "Choose Options",
+ buttons=BUTTON,
+ )
+
+
+@callback("deltag", owner=True)
+async def _(e):
+ udB.del_key("TAG_LOG")
+ await e.answer("Done!!! Tag Logger has been turned Off")
+
+
+@callback("eaddon", owner=True)
+async def pmset(event):
+ BT = (
+ [Button.inline("Aᴅᴅᴏɴs Oғғ", data="edof")]
+ if udB.get_key("ADDONS")
+ else [Button.inline("Aᴅᴅᴏɴs Oɴ", data="edon")]
+ )
+
+ await event.edit(
+ "ADDONS~ Extra Plugins:",
+ buttons=[
+ BT,
+ [Button.inline("« Bᴀᴄᴋ", data="cbs_otvars")],
+ ],
+ )
+
+
+@callback("edon", owner=True)
+async def eddon(event):
+ var = "ADDONS"
+ await setit(event, var, "True")
+ await event.edit(
+ "Done! ADDONS has been turned on!!\n\n After Setting All Things Do Restart",
+ buttons=get_back_button("eaddon"),
+ )
+
+
+@callback("edof", owner=True)
+async def eddof(event):
+ udB.set_key("ADDONS", "False")
+ await event.edit(
+ "Done! ADDONS has been turned off!! After Setting All Things Do Restart",
+ buttons=get_back_button("eaddon"),
+ )
+
+
+@callback("sudo", owner=True)
+async def pmset(event):
+ BT = (
+ [Button.inline("Sᴜᴅᴏ Mᴏᴅᴇ Oғғ", data="ofsudo")]
+ if udB.get_key("SUDO")
+ else [Button.inline("Sᴜᴅᴏ Mᴏᴅᴇ Oɴ", data="onsudo")]
+ )
+
+ await event.edit(
+ f"SUDO MODE ~ Some peoples can use ur Bot which u selected. To know More use `{HNDLR}help sudo`",
+ buttons=[
+ BT,
+ [Button.inline("« Bᴀᴄᴋ", data="cbs_otvars")],
+ ],
+ )
+
+
+@callback("onsudo", owner=True)
+async def eddon(event):
+ var = "SUDO"
+ await setit(event, var, "True")
+ await event.edit(
+ "Done! SUDO MODE has been turned on!!\n\n After Setting All Things Do Restart",
+ buttons=get_back_button("sudo"),
+ )
+
+
+@callback("ofsudo", owner=True)
+async def eddof(event):
+ var = "SUDO"
+ await setit(event, var, "False")
+ await event.edit(
+ "Done! SUDO MODE has been turned off!! After Setting All Things Do Restart",
+ buttons=get_back_button("sudo"),
+ )
+
+
+@callback("sfgrp", owner=True)
+async def sfgrp(event):
+ await event.delete()
+ name = "FBan Group ID"
+ var = "FBAN_GROUP_ID"
+ pru = event.sender_id
+ async with asst.conversation(pru) as conv:
+ await conv.send_message(
+ f"Make a group, add @MissRose_Bot, send `{HNDLR}id`, copy that and send it here.\nUse /cancel to go back.",
+ )
+ response = conv.wait_event(events.NewMessage(chats=pru))
+ response = await response
+ themssg = response.message.message
+ if themssg == "/cancel":
+ return await conv.send_message(
+ "Cancelled!!",
+ buttons=get_back_button("cbs_sfban"),
+ )
+ await setit(event, var, themssg)
+ await conv.send_message(
+ f"{name} changed to {themssg}",
+ buttons=get_back_button("cbs_sfban"),
+ )
+
+
+@callback("alvmed", owner=True)
+async def media(event):
+ await event.delete()
+ pru = event.sender_id
+ var = "ALIVE_PIC"
+ name = "Alive Media"
+ async with event.client.conversation(pru) as conv:
+ await conv.send_message(
+ "**Alive Media**\nSend me a pic/gif/media to set as alive media.\n\nUse /cancel to terminate the operation.",
+ )
+ response = await conv.get_response()
+ try:
+ themssg = response.message
+ if themssg == "/cancel":
+ return await conv.send_message(
+ "Operation cancelled!!",
+ buttons=get_back_button("cbs_alvcstm"),
+ )
+ except BaseException as er:
+ LOGS.exception(er)
+ if (
+ not (response.text).startswith("/")
+ and response.text != ""
+ and (not response.media or isinstance(response.media, MessageMediaWebPage))
+ ):
+ url = text_to_url(response)
+ elif response.sticker:
+ url = response.file.id
+ else:
+ media = await event.client.download_media(response, "alvpc")
+ try:
+ url = uploader.upload_file(media)
+ remove(media)
+ except BaseException as er:
+ LOGS.exception(er)
+ return await conv.send_message(
+ "Terminated.",
+ buttons=get_back_button("cbs_alvcstm"),
+ )
+ await setit(event, var, url)
+ await conv.send_message(
+ f"{name} has been set.",
+ buttons=get_back_button("cbs_alvcstm"),
+ )
+
+
+@callback("delmed", owner=True)
+async def dell(event):
+ try:
+ udB.del_key("ALIVE_PIC")
+ return await event.edit(
+ get_string("clst_5"), buttons=get_back_button("cbs_alabs_vcstm")
+ )
+ except BaseException as er:
+ LOGS.exception(er)
+ return await event.edit(
+ get_string("clst_4"),
+ buttons=get_back_button("cbs_alabs_vcstm"),
+ )
+
+
+@callback("inpm_in", owner=True)
+async def inl_on(event):
+ var = "INLINE_PM"
+ await setit(event, var, "True")
+ await event.edit(
+ "Done!! PMPermit type has been set to inline!",
+ buttons=[[Button.inline("« Bᴀᴄᴋ", data="cbs_pmtype")]],
+ )
+
+
+@callback("inpm_no", owner=True)
+async def inl_on(event):
+ var = "INLINE_PM"
+ await setit(event, var, "False")
+ await event.edit(
+ "Done!! PMPermit type has been set to normal!",
+ buttons=[[Button.inline("« Bᴀᴄᴋ", data="cbs_pmtype")]],
+ )
+
+
+@callback("pmtxt", owner=True)
+async def name(event):
+ await event.delete()
+ pru = event.sender_id
+ var = "PM_TEXT"
+ name = "PM Text"
+ async with event.client.conversation(pru) as conv:
+ await conv.send_message(
+ "**PM Text**\nEnter the new Pmpermit text.\n\nu can use `{name}` `{fullname}` `{count}` `{mention}` `{username}` to get this from user Too\n\nUse /cancel to terminate the operation.",
+ )
+ response = conv.wait_event(events.NewMessage(chats=pru))
+ response = await response
+ themssg = response.message.message
+ if themssg == "/cancel":
+ return await conv.send_message(
+ "Cancelled!!",
+ buttons=get_back_button("cbs_pmcstm"),
+ )
+ if len(themssg) > 4090:
+ return await conv.send_message(
+ "Message too long!\nGive a shorter message please!!",
+ buttons=get_back_button("cbs_pmcstm"),
+ )
+ await setit(event, var, themssg)
+ await conv.send_message(
+ f"{name} changed to {themssg}\n\nAfter Setting All Things Do restart",
+ buttons=get_back_button("cbs_pmcstm"),
+ )
+
+
+@callback("swarn", owner=True)
+async def name(event):
+ m = range(1, 10)
+ tultd = [Button.inline(f"{x}", data=f"wrns_{x}") for x in m]
+ lst = list(zip(tultd[::3], tultd[1::3], tultd[2::3]))
+ lst.append([Button.inline("« Bᴀᴄᴋ", data="cbs_pmcstm")])
+ await event.edit(
+ "Select the number of warnings for a user before getting blocked in PMs.",
+ buttons=lst,
+ )
+
+
+@callback(re.compile(b"wrns_(.*)"), owner=True)
+async def set_wrns(event):
+ value = int(event.data_match.group(1).decode("UTF-8"))
+ if dn := udB.set_key("PMWARNS", value):
+ await event.edit(
+ f"PM Warns Set to {value}.\nNew users will have {value} chances in PMs before getting banned.",
+ buttons=get_back_button("cbs_pmcstm"),
+ )
+ else:
+ await event.edit(
+ f"Something went wrong, please check your {HNDLR}logs!",
+ buttons=get_back_button("cbs_pmcstm"),
+ )
+
+
+@callback("pmmed", owner=True)
+async def media(event):
+ await event.delete()
+ pru = event.sender_id
+ var = "PMPIC"
+ name = "PM Media"
+ async with event.client.conversation(pru) as conv:
+ await conv.send_message(
+ "**PM Media**\nSend me a pic/gif/sticker/link to set as pmpermit media.\n\nUse /cancel to terminate the operation.",
+ )
+ response = await conv.get_response()
+ try:
+ themssg = response.message
+ if themssg == "/cancel":
+ return await conv.send_message(
+ "Operation cancelled!!",
+ buttons=get_back_button("cbs_pmcstm"),
+ )
+ except BaseException as er:
+ LOGS.exception(er)
+ media = await event.client.download_media(response, "pmpc")
+ if (
+ not (response.text).startswith("/")
+ and response.text != ""
+ and (not response.media or isinstance(response.media, MessageMediaWebPage))
+ ):
+ url = text_to_url(response)
+ elif response.sticker:
+ url = response.file.id
+ else:
+ try:
+ url = uploader.upload_file(media)
+ remove(media)
+ except BaseException as er:
+ LOGS.exception(er)
+ return await conv.send_message(
+ "Terminated.",
+ buttons=get_back_button("cbs_pmcstm"),
+ )
+ await setit(event, var, url)
+ await conv.send_message(
+ f"{name} has been set.",
+ buttons=get_back_button("cbs_pmcstm"),
+ )
+
+
+@callback("delpmmed", owner=True)
+async def dell(event):
+ try:
+ udB.del_key("PMPIC")
+ return await event.edit(
+ get_string("clst_5"), buttons=get_back_button("cbs_pmcstm")
+ )
+ except BaseException as er:
+ LOGS.exception(er)
+ return await event.edit(
+ get_string("clst_4"),
+ buttons=[[Button.inline("« Sᴇᴛᴛɪɴɢs", data="setter")]],
+ )
+
+
+@callback("apon", owner=True)
+async def apon(event):
+ var = "AUTOAPPROVE"
+ await setit(event, var, "True")
+ await event.edit(
+ "Done!! AUTOAPPROVE Started!!",
+ buttons=[[Button.inline("« Bᴀᴄᴋ", data="cbs_apauto")]],
+ )
+
+
+@callback("apof", owner=True)
+async def apof(event):
+ try:
+ udB.set_key("AUTOAPPROVE", "False")
+ return await event.edit(
+ "Done! AUTOAPPROVE Stopped!!",
+ buttons=[[Button.inline("« Bᴀᴄᴋ", data="cbs_apauto")]],
+ )
+ except BaseException as er:
+ LOGS.exception(er)
+ return await event.edit(
+ get_string("clst_4"),
+ buttons=[[Button.inline("« Sᴇᴛᴛɪɴɢs", data="setter")]],
+ )
+
+
+@callback("pml", owner=True)
+async def l_vcs(event):
+ BT = (
+ [Button.inline("PMLOGGER OFF", data="pmlogof")]
+ if udB.get_key("PMLOG")
+ else [Button.inline("PMLOGGER ON", data="pmlog")]
+ )
+
+ await event.edit(
+ "PMLOGGER This Will Forward Ur Pm to Ur Private Group -",
+ buttons=[
+ BT,
+ [Button.inline("PᴍLᴏɢɢᴇʀ Gʀᴏᴜᴘ", "abs_pmlgg")],
+ [Button.inline("« Bᴀᴄᴋ", data="cbs_pmcstm")],
+ ],
+ )
+
+
+@callback("pmlog", owner=True)
+async def pmlog(event):
+ await setit(event, "PMLOG", "True")
+ await event.edit(
+ "Done!! PMLOGGER Started!!",
+ buttons=[[Button.inline("« Bᴀᴄᴋ", data="pml")]],
+ )
+
+
+@callback("pmlogof", owner=True)
+async def pmlogof(event):
+ try:
+ udB.del_key("PMLOG")
+ return await event.edit(
+ "Done! PMLOGGER Stopped!!",
+ buttons=[[Button.inline("« Bᴀᴄᴋ", data="pml")]],
+ )
+ except BaseException as er:
+ LOGS.exception(er)
+ return await event.edit(
+ get_string("clst_4"),
+ buttons=[[Button.inline("« Sᴇᴛᴛɪɴɢs", data="setter")]],
+ )
+
+
+@callback("pmon", owner=True)
+async def pmonn(event):
+ var = "PMSETTING"
+ await setit(event, var, "True")
+ await event.edit(
+ "Done! PMPermit has been turned on!!",
+ buttons=[[Button.inline("« Bᴀᴄᴋ", data="cbs_ppmset")]],
+ )
+
+
+@callback("pmoff", owner=True)
+async def pmofff(event):
+ var = "PMSETTING"
+ await setit(event, var, "False")
+ await event.edit(
+ "Done! PMPermit has been turned off!!",
+ buttons=[[Button.inline("« Bᴀᴄᴋ", data="cbs_ppmset")]],
+ )
+
+
+@callback("botmew", owner=True)
+async def hhh(e):
+ async with e.client.conversation(e.chat_id) as conv:
+ await conv.send_message("Send Any Media to keep at your Bot's welcome ")
+ msg = await conv.get_response()
+ if not msg.media or msg.text.startswith("/"):
+ return await conv.send_message(
+ "Terminated!", buttons=get_back_button("cbs_chatbot")
+ )
+ udB.set_key("STARTMEDIA", msg.file.id)
+ await conv.send_message("Done!", buttons=get_back_button("cbs_chatbot"))
+
+
+@callback("botinfe", owner=True)
+async def hhh(e):
+ async with e.client.conversation(e.chat_id) as conv:
+ await conv.send_message(
+ "Send message to set to Display, when user Press Info button in Bot Welcome!\n\nsend `False` to completely remove that button.."
+ )
+ msg = await conv.get_response()
+ if msg.media or msg.text.startswith("/"):
+ return await conv.send_message(
+ "Terminated!", buttons=get_back_button("cbs_chatbot")
+ )
+ udB.set_key("BOT_INFO_START", msg.text)
+ await conv.send_message("Done!", buttons=get_back_button("cbs_chatbot"))
+
+
+@callback("pmfs", owner=True)
+async def heheh(event):
+ Ll = []
+ err = ""
+ async with event.client.conversation(event.chat_id) as conv:
+ await conv.send_message(
+ "• Send The Chat Id(s), which you want user to Join Before using Chat/Pm Bot\n\n• Send /clear to disable PmBot Force sub..\n• • Send /cancel to stop this process.."
+ )
+ await conv.send_message(
+ "Example : \n`-1001234567\n-100778888`\n\nFor Multiple Chat(s)."
+ )
+ try:
+ msg = await conv.get_response()
+ except AsyncTimeOut:
+ return await conv.send_message("**• TimeUp!**\nStart from /start back.")
+ if not msg.text or msg.text.startswith("/"):
+ timyork = "Cancelled!"
+ if msg.text == "/clear":
+ udB.del_key("PMBOT_FSUB")
+ timyork = "Done! Force Subscribe Stopped\nRestart your Bot!"
+ return await conv.send_message(
+ "Cancelled!", buttons=get_back_button("cbs_chatbot")
+ )
+ for chat in msg.message.split("\n"):
+ if chat.startswith("-") or chat.isdigit():
+ chat = int(chat)
+ try:
+ CHSJSHS = await event.client.get_entity(chat)
+ Ll.append(get_peer_id(CHSJSHS))
+ except Exception as er:
+ err += f"**{chat}** : {er}\n"
+ if err:
+ return await conv.send_message(err)
+ udB.set_key("PMBOT_FSUB", str(Ll))
+ await conv.send_message(
+ "Done!\nRestart Your Bot.", buttons=get_back_button("cbs_chatbot")
+ )
+
+
+@callback("bwel", owner=True)
+async def name(event):
+ await event.delete()
+ pru = event.sender_id
+ var = "STARTMSG"
+ name = "Bot Welcome Message:"
+ async with event.client.conversation(pru) as conv:
+ await conv.send_message(
+ "**BOT WELCOME MSG**\nEnter the msg which u want to show when someone start your assistant Bot.\nYou Can use `{me}` , `{mention}` Parameters Too\nUse /cancel to terminate the operation.",
+ )
+ response = conv.wait_event(events.NewMessage(chats=pru))
+ response = await response
+ themssg = response.message.message
+ if themssg == "/cancel":
+ return await conv.send_message(
+ "Cancelled!!",
+ buttons=get_back_button("cbs_chatbot"),
+ )
+ await setit(event, var, themssg)
+ await conv.send_message(
+ f"{name} changed to {themssg}",
+ buttons=get_back_button("cbs_chatbot"),
+ )
+
+
+@callback("onchbot", owner=True)
+async def chon(event):
+ var = "PMBOT"
+ await setit(event, var, "True")
+ Loader(path="assistant/pmbot.py", key="PM Bot").load()
+ if AST_PLUGINS.get("pmbot"):
+ for i, e in AST_PLUGINS["pmbot"]:
+ event.client.remove_event_handler(i)
+ for i, e in AST_PLUGINS["pmbot"]:
+ event.client.add_event_handler(i, events.NewMessage(**e))
+ await event.edit(
+ "Done! Now u Can Chat With People Via This Bot",
+ buttons=[Button.inline("« Bᴀᴄᴋ", data="cbs_chatbot")],
+ )
+
+
+@callback("ofchbot", owner=True)
+async def chon(event):
+ var = "PMBOT"
+ await setit(event, var, "False")
+ if AST_PLUGINS.get("pmbot"):
+ for i, e in AST_PLUGINS["pmbot"]:
+ event.client.remove_event_handler(i)
+ await event.edit(
+ "Done! Chat People Via This Bot Stopped.",
+ buttons=[Button.inline("« Bᴀᴄᴋ", data="cbs_chatbot")],
+ )
+
+
+@callback("inli_pic", owner=True)
+async def media(event):
+ await event.delete()
+ pru = event.sender_id
+ var = "INLINE_PIC"
+ name = "Inline Media"
+ async with event.client.conversation(pru) as conv:
+ await conv.send_message(
+ "**Inline Media**\nSend me a pic/gif/ or link to set as inline media.\n\nUse /cancel to terminate the operation.",
+ )
+ response = await conv.get_response()
+ try:
+ themssg = response.message
+ if themssg == "/cancel":
+ return await conv.send_message(
+ "Operation cancelled!!",
+ buttons=get_back_button("setter"),
+ )
+ except BaseException as er:
+ LOGS.exception(er)
+ media = await event.client.download_media(response, "inlpic")
+ if (
+ not (response.text).startswith("/")
+ and response.text != ""
+ and (not response.media or isinstance(response.media, MessageMediaWebPage))
+ ):
+ url = text_to_url(response)
+ else:
+ try:
+ url = uploader.upload_file(media)
+ remove(media)
+ except BaseException as er:
+ LOGS.exception(er)
+ return await conv.send_message(
+ "Terminated.",
+ buttons=get_back_button("setter"),
+ )
+ await setit(event, var, url)
+ await conv.send_message(
+ f"{name} has been set.",
+ buttons=get_back_button("setter"),
+ )
+
+
+FD_MEDIA = {}
+
+
+@callback(re.compile("fd(.*)"), owner=True)
+async def fdroid_dler(event):
+ uri = event.data_match.group(1).decode("utf-8")
+ if FD_MEDIA.get(uri):
+ return await event.edit(file=FD_MEDIA[uri])
+ await event.answer("• Starting Download •", alert=True)
+ await event.edit("• Downloading.. •")
+ URL = f"https://f-droid.org/packages/{uri}"
+ conte = await async_searcher(URL, re_content=True)
+ BSC = bs(conte, "html.parser", from_encoding="utf-8")
+ dl_ = BSC.find("p", "package-version-download").find("a")["href"]
+ title = BSC.find("h3", "package-name").text.strip()
+ thumb = BSC.find("img", "package-icon")["src"]
+ if thumb.startswith("/"):
+ thumb = f"https://f-droid.org{thumb}"
+ thumb, _ = await fast_download(thumb, filename=f"{uri}.png")
+ s_time = time.time()
+ file, _ = await fast_download(
+ dl_,
+ filename=f"{title}.apk",
+ progress_callback=lambda d, t: asyncio.get_event_loop().create_task(
+ progress(
+ d,
+ t,
+ event,
+ s_time,
+ "Downloading...",
+ )
+ ),
+ )
+
+ time.time()
+ n_file = await event.client.fast_uploader(
+ file, show_progress=True, event=event, message="Uploading...", to_delete=True
+ )
+ buttons = Button.switch_inline("Search Back", query="fdroid", same_peer=True)
+ try:
+ msg = await event.edit(
+ f"**• [{title}]({URL}) •**", file=n_file, thumb=thumb, buttons=buttons
+ )
+ except Exception as er:
+ LOGS.exception(er)
+ try:
+ msg = await event.client.edit_message(
+ await event.get_input_chat(),
+ event.message_id,
+ f"**• [{title}]({URL}) •**",
+ buttons=buttons,
+ thumb=thumb,
+ file=n_file,
+ )
+ except Exception as er:
+ os.remove(thumb)
+ LOGS.exception(er)
+ return await event.edit(f"**ERROR**: `{er}`", buttons=buttons)
+ if msg and hasattr(msg, "media"):
+ FD_MEDIA.update({uri: msg.media})
+ os.remove(thumb)
diff --git a/assistant/games.py b/assistant/games.py
new file mode 100644
index 0000000000000000000000000000000000000000..e2b86eacca915dd16d948a4c46268f3d8472db2a
--- /dev/null
+++ b/assistant/games.py
@@ -0,0 +1,331 @@
+# Ultroid - UserBot
+# Copyright (C) 2021-2025 TeamUltroid
+#
+# This file is a part of < https://github.com/TeamUltroid/Ultroid/ >
+# PLease read the GNU Affero General Public License in
+# .
+
+"""
+• `{i}akinator` | `/akinator`
+ Start akinator game from Userbot/Assistant
+
+• `/startgame`
+ Open Portal for Games
+"""
+
+import asyncio
+import re, uuid, operator
+from random import choice, shuffle
+
+from akipy.async_akipy import Akinator
+from telethon.errors.rpcerrorlist import BotMethodInvalidError
+from telethon.events import Raw
+from telethon.tl.types import InputMediaPoll, Poll, PollAnswer, UpdateMessagePollVote
+
+from pyUltroid._misc._decorators import ultroid_cmd
+from logging import getLogger
+from html import unescape
+from telethon.tl.types import TextWithEntities
+from pyUltroid.fns.helper import inline_mention
+from pyUltroid.fns.tools import async_searcher
+from telethon.errors import ChatSendStickersForbiddenError
+
+from . import * # Ensure this import matches your project structure
+
+games = {}
+aki_photo = "https://graph.org/file/3cc8825c029fd0cab9edc.jpg"
+
+akipyLOGS = getLogger("akipy")
+
+@ultroid_cmd(pattern="akinator")
+async def akina(e):
+ sta = Akinator()
+ games[e.chat_id] = {e.id: sta}
+ LOGS.info(f"Game started for chat {e.chat_id} with ID {e.id}.")
+ try:
+ m = await e.client.inline_query(asst.me.username, f"aki_{e.chat_id}_{e.id}")
+ await m[0].click(e.chat_id)
+ akipyLOGS.info(f"Clicked inline result for chat {e.chat_id}")
+ except BotMethodInvalidError as err:
+ akipyLOGS.error(f"BotMethodInvalidError: {err}")
+ await asst.send_file(
+ e.chat_id,
+ aki_photo,
+ buttons=Button.inline(get_string("aki_2"), data=f"aki_{e.chat_id}_{e.id}"),
+ )
+ except Exception as er:
+ akipyLOGS.error(f"Unexpected error: {er}")
+ return await e.eor(f"ERROR : {er}")
+ if e.out:
+ await e.delete()
+
+
+@asst_cmd(pattern="akinator", owner=True)
+async def _akokk(e):
+ await akina(e)
+
+
+@callback(re.compile("aki_(.*)"), owner=True)
+async def doai(e):
+ adt = e.pattern_match.group(1).strip().decode("utf-8")
+ dt = adt.split("_")
+ ch = int(dt[0])
+ mid = int(dt[1])
+ await e.edit(get_string("com_1"))
+ try:
+ await games[ch][mid].start_game(child_mode=False)
+ bts = [Button.inline(o, f"aka_{adt}_{o}") for o in ["Yes", "No", "Idk"]]
+ cts = [Button.inline(o, f"aka_{adt}_{o}") for o in ["Probably", "Probably Not"]]
+ bts = [bts, cts]
+ await e.edit(f"Q. {games[ch][mid].question}", buttons=bts)
+ except KeyError:
+ return await e.answer(get_string("aki_1"), alert=True)
+
+
+@callback(re.compile("aka_(.*)"), owner=True)
+async def okah(e):
+ try:
+ mk = e.pattern_match.group(1).decode("utf-8").split("_")
+ #akipyLOGS.info(f"Parsed values: {mk}")
+
+ if len(mk) < 3:
+ akipyLOGS.error("Pattern match did not return enough parts.")
+ return await e.answer("Invalid data received.", alert=True)
+
+ ch = int(mk[0])
+ mid = int(mk[1])
+ ans = mk[2]
+
+ gm = games[ch][mid]
+ await gm.answer(ans)
+
+ # Check for the final guess in the API response
+ if gm.name_proposition and gm.description_proposition:
+ gm.win = True
+ text = f"It's {gm.name_proposition}\n{gm.description_proposition}"
+ await e.edit(text, file=gm.photo)
+ else:
+ # Game is not won yet, continue asking questions
+ buttons = [
+ [Button.inline(o, f"aka_{ch}_{mid}_{o}") for o in ["Yes", "No", "Idk"]],
+ [Button.inline(o, f"aka_{ch}_{mid}_{o}") for o in ["Probably", "Probably Not"]],
+ ]
+ await e.edit(gm.question, buttons=buttons)
+
+ except KeyError:
+ await e.answer(get_string("aki_3"))
+ except Exception as ex:
+ akipyLOGS.error(f"An unexpected error occurred: {ex}")
+
+
+@in_pattern(re.compile("aki_?(.*)"), owner=True)
+async def eiagx(e):
+ bts = Button.inline(get_string("aki_2"), data=e.text)
+ ci = types.InputWebDocument(aki_photo, 0, "image/jpeg", [])
+ ans = [
+ await e.builder.article(
+ "Akinator",
+ type="photo",
+ content=ci,
+ text="Akinator",
+ thumb=ci,
+ buttons=bts,
+ include_media=True,
+ )
+ ]
+ await e.answer(ans)
+
+
+# ----------------------- Main Command ------------------- #
+
+GIMAGE = "https://graph.org/file/1c51015bae5205a65fd69.jpg"
+
+@asst_cmd(pattern="startgame", owner=True)
+async def magic(event):
+ buttons = [
+ [Button.inline("Trivia Quiz", "trzia")],
+ [Button.inline("Cancel ❌", "delit")],
+ ]
+ await event.reply(
+ get_string("games_1"),
+ file=GIMAGE,
+ buttons=buttons,
+ )
+
+
+# -------------------------- Trivia ----------------------- #
+
+TR_BTS = {}
+DIFI_KEYS = ["Easy", "Medium", "Hard"]
+TRIVIA_CHATS = {}
+POLLS = {}
+CONGO_STICKER = [
+ "CAADAgADSgIAAladvQrJasZoYBh68AI",
+ "CAADAgADXhIAAuyZKUl879mlR_dkOwI",
+ "CAADAgADpQAD9wLID-xfZCDwOI5LAg",
+ "CAADAgADjAADECECEFZM-SrKO9GgAg",
+ "CAADAgADSwIAAj-VzArAzNCDiGWAHAI",
+ "CAADAgADhQADwZxgDIuMHR9IU10iAg",
+ "CAADAgADiwMAAsSraAuoe2BwYu1sdQI",
+]
+
+
+@callback("delit", owner=True)
+async def delete_it(event):
+ await event.delete()
+
+
+@callback(re.compile("ctdown(.*)"), owner=True)
+async def ct_spam(e):
+ n = e.data_match.group(1).decode("utf-8")
+ await e.answer(f"Wait {n} seconds..", alert=True)
+
+
+@callback(re.compile("trzia(.*)"), owner=True)
+async def choose_cata(event):
+ match = event.data_match.group(1).decode("utf-8")
+ if not match:
+ if TR_BTS.get("category"):
+ buttons = TR_BTS["category"]
+ else:
+ req = (
+ await async_searcher(
+ "https://opentdb.com/api_category.php", re_json=True
+ )
+ )["trivia_categories"]
+ btt = []
+ for i in req:
+ name = i["name"]
+ if ":" in name:
+ name = name.split(":")[1]
+ btt.append(Button.inline(name, f"trziad_{i['id']}"))
+ buttons = list(zip(btt[::2], btt[1::2]))
+ if len(btt) % 2 == 1:
+ buttons.append((btt[-1],))
+ buttons.append([Button.inline("Cancel ❌", "delit")])
+ TR_BTS.update({"category": buttons})
+ text = get_string("games_2")
+ elif match[0] == "d":
+ cat = match[1:]
+ buttons = [[Button.inline(i, f"trziac{cat}_{i}") for i in DIFI_KEYS]]
+ buttons.append(get_back_button("trzia"))
+ text = get_string("games_3")
+ elif match[0] == "c":
+ m = match[1:]
+ buttons = [[Button.inline(str(i), f"trziat{m}_{i}") for i in range(10, 70, 20)]]
+ text = get_string("games_4")
+ elif match[0] == "t":
+ m_ = match[1:]
+ buttons = [
+ [Button.inline(str(i), f"trzias{m_}_{i}") for i in [10, 30, 60, 120]]
+ ]
+ text = get_string("games_5")
+ elif match[0] == "s":
+ chat = event.chat_id
+ cat, le, nu, in_ = match[2:].split("_")
+ msg = await event.edit(get_string("games_6").format(le, nu))
+ for i in reversed(range(5)):
+ msg = await msg.edit(buttons=Button.inline(f"{i} ⏰", f"ctdown{i}"))
+ await asyncio.sleep(1)
+ await msg.edit(
+ msg.text + "\n\n• Send /cancel to stop the Quiz...", buttons=None
+ )
+ qsss = await async_searcher(
+ f"https://opentdb.com/api.php?amount={nu}&category={cat}&difficulty={le.lower()}",
+ re_json=True,
+ )
+ qs = qsss["results"]
+ if not qs:
+ await event.respond("Sorry, No Question Found for the given Criteria..")
+ await event.delete()
+ return
+ TRIVIA_CHATS.update({chat: {}})
+ for copper, q in enumerate(qs):
+ if TRIVIA_CHATS[chat].get("cancel") is not None:
+ break
+ ansi = str(uuid.uuid1()).split("-")[0].encode()
+ opts = [PollAnswer(TextWithEntities(unescape(q["correct_answer"]), entities=[]), ansi)]
+ [
+ opts.append(
+ PollAnswer(TextWithEntities(unescape(a), entities=[]), str(uuid.uuid1()).split("-")[0].encode())
+ )
+ for a in q["incorrect_answers"]
+ ]
+ shuffle(opts)
+ poll = InputMediaPoll(
+ Poll(
+ 0,
+ TextWithEntities(
+ f"[{copper+1}]. " + unescape(q["question"]),
+ entities=[]
+ ),
+ answers=opts,
+ public_voters=True,
+ quiz=True,
+ close_period=int(in_),
+ ),
+ correct_answers=[ansi],
+ solution="Join @TeamUltroid",
+ solution_entities=[],
+ )
+ m_ = await event.client.send_message(chat, file=poll)
+ POLLS.update({m_.poll.poll.id: {"chat": m_.chat_id, "answer": ansi}})
+ await asyncio.sleep(int(in_))
+ if not TRIVIA_CHATS[chat]:
+ await event.respond(
+ "No-One Got Any Score in the Quiz!\nBetter Luck Next Time!"
+ )
+ else:
+ try:
+ await event.respond(file=choice(CONGO_STICKER))
+ except ChatSendStickersForbiddenError:
+ pass
+ LBD = "🎯 **Scoreboard of the Quiz.**\n\n"
+ TRC = TRIVIA_CHATS[chat]
+ if "cancel" in TRC.keys():
+ del TRC["cancel"]
+ for userid, user_score in dict(
+ sorted(TRC.items(), key=operator.itemgetter(1), reverse=True)
+ ).items():
+ user = inline_mention(await event.client.get_entity(userid))
+ LBD += f"••• {user} - {user_score}\n"
+ await event.respond(LBD)
+ del TRIVIA_CHATS[chat]
+ list_ = list(POLLS.copy().keys())
+ for key in list_:
+ if POLLS[key]["chat"] == chat:
+ del POLLS[key]
+ return
+ await event.edit(text, buttons=buttons)
+
+
+@asst.on(
+ Raw(UpdateMessagePollVote, func=lambda x: TRIVIA_CHATS and POLLS.get(x.poll_id))
+)
+async def pollish(eve: UpdateMessagePollVote):
+ if POLLS.get(eve.poll_id)["chat"] not in TRIVIA_CHATS.keys():
+ return
+ if not eve.options:
+ # Consider as correct answer if no options selected
+ chat = POLLS.get(eve.poll_id)["chat"]
+ user = eve.peer.user_id
+ if not TRIVIA_CHATS.get(chat, {}).get(user):
+ TRIVIA_CHATS[chat][user] = 1
+ else:
+ TRIVIA_CHATS[chat][user] += 1
+ return
+ if POLLS[eve.poll_id]["answer"] != eve.options[0]:
+ return
+ chat = POLLS.get(eve.poll_id)["chat"]
+ user = eve.peer.user_id
+ if not TRIVIA_CHATS.get(chat, {}).get(user):
+ TRIVIA_CHATS[chat][user] = 1
+ else:
+ TRIVIA_CHATS[chat][user] += 1
+
+
+@asst_cmd("cancel", owner=True, func=lambda x: TRIVIA_CHATS.get(x.chat_id))
+async def cancelish(event):
+ chat = TRIVIA_CHATS.get(event.chat_id)
+ chat.update({"cancel": True})
+ await event.respond("Cancelled!")
diff --git a/assistant/initial.py b/assistant/initial.py
new file mode 100644
index 0000000000000000000000000000000000000000..c126bbcf193dd5a76f00e86b539a7bde91ac53b0
--- /dev/null
+++ b/assistant/initial.py
@@ -0,0 +1,84 @@
+# Ultroid - UserBot
+# Copyright (C) 2021-2025 TeamUltroid
+#
+# This file is a part of < https://github.com/TeamUltroid/Ultroid/ >
+# PLease read the GNU Affero General Public License in
+# .
+
+import re
+
+from . import *
+
+STRINGS = {
+ 1: """🎇 **Thanks for Deploying Ultroid Userbot!**
+
+• Here, are the Some Basic stuff from, where you can Know, about its Usage.""",
+ 2: """🎉** About Ultroid**
+
+🧿 Ultroid is Pluggable and powerful Telethon Userbot, made in Python from Scratch. It is Aimed to Increase Security along with Addition of Other Useful Features.
+
+❣ Made by **@TeamUltroid**""",
+ 3: """**💡• FAQs •**
+
+-> [Username Tracker](https://t.me/UltroidUpdates/24)
+-> [Keeping Custom Addons Repo](https://t.me/UltroidUpdates/28)
+-> [Disabling Deploy message](https://t.me/UltroidUpdates/27)
+-> [Setting up TimeZone](https://t.me/UltroidUpdates/22)
+-> [About Inline PmPermit](https://t.me/UltroidUpdates/21)
+-> [About Dual Mode](https://t.me/UltroidUpdates/18)
+-> [Custom Thumbnail](https://t.me/UltroidUpdates/13)
+-> [About FullSudo](https://t.me/UltroidUpdates/11)
+-> [Setting Up PmBot](https://t.me/UltroidUpdates/2)
+-> [Also Check](https://t.me/UltroidUpdates/14)
+
+**• To Know About Updates**
+ - Join @TeamUltroid.""",
+ 4: f"""• `To Know All Available Commands`
+
+ - `{HNDLR}help`
+ - `{HNDLR}cmds`""",
+ 5: """• **For Any Other Query or Suggestion**
+ - Move to **@UltroidSupportChat**.
+
+• Thanks for Reaching till END.""",
+}
+
+
+@callback(re.compile("initft_(\\d+)"))
+async def init_depl(e):
+ CURRENT = int(e.data_match.group(1))
+ if CURRENT == 5:
+ return await e.edit(
+ STRINGS[5],
+ buttons=Button.inline("<< Back", "initbk_4"),
+ link_preview=False,
+ )
+
+ await e.edit(
+ STRINGS[CURRENT],
+ buttons=[
+ Button.inline("<<", f"initbk_{str(CURRENT - 1)}"),
+ Button.inline(">>", f"initft_{str(CURRENT + 1)}"),
+ ],
+ link_preview=False,
+ )
+
+
+@callback(re.compile("initbk_(\\d+)"))
+async def ineiq(e):
+ CURRENT = int(e.data_match.group(1))
+ if CURRENT == 1:
+ return await e.edit(
+ STRINGS[1],
+ buttons=Button.inline("Start Back >>", "initft_2"),
+ link_preview=False,
+ )
+
+ await e.edit(
+ STRINGS[CURRENT],
+ buttons=[
+ Button.inline("<<", f"initbk_{str(CURRENT - 1)}"),
+ Button.inline(">>", f"initft_{str(CURRENT + 1)}"),
+ ],
+ link_preview=False,
+ )
diff --git a/assistant/inlinestuff.py b/assistant/inlinestuff.py
new file mode 100644
index 0000000000000000000000000000000000000000..ae097f119a86234c214267359bf23b1ca0bb0648
--- /dev/null
+++ b/assistant/inlinestuff.py
@@ -0,0 +1,623 @@
+# Ultroid - UserBot
+# Copyright (C) 2021-2025 TeamUltroid
+#
+# This file is a part of < https://github.com/TeamUltroid/Ultroid/ >
+# PLease read the GNU Affero General Public License in
+# .
+
+import base64
+import inspect
+from datetime import datetime
+from html import unescape
+from random import choice
+from re import compile as re_compile
+
+from bs4 import BeautifulSoup as bs
+from telethon import Button
+from telethon.tl.alltlobjects import LAYER, tlobjects
+from telethon.tl.types import DocumentAttributeAudio as Audio
+from telethon.tl.types import InputWebDocument as wb
+
+from pyUltroid.fns.misc import google_search
+from pyUltroid.fns.tools import (
+ _webupload_cache,
+ async_searcher,
+ get_ofox,
+ saavn_search,
+ webuploader,
+)
+
+from . import *
+from . import _ult_cache
+
+SUP_BUTTONS = [
+ [
+ Button.url("• Repo •", url="https://github.com/TeamUltroid/Ultroid"),
+ Button.url("• Support •", url="t.me/UltroidSupportChat"),
+ ],
+]
+
+ofox = "https://graph.org/file/231f0049fcd722824f13b.jpg"
+gugirl = "https://graph.org/file/0df54ae4541abca96aa11.jpg"
+ultpic = "https://graph.org/file/4136aa1650bc9d4109cc5.jpg"
+
+apis = [
+ "QUl6YVN5QXlEQnNZM1dSdEI1WVBDNmFCX3c4SkF5NlpkWE5jNkZV",
+ "QUl6YVN5QkYwenhMbFlsUE1wOXh3TVFxVktDUVJxOERnZHJMWHNn",
+ "QUl6YVN5RGRPS253blB3VklRX2xiSDVzWUU0Rm9YakFLSVFWMERR",
+]
+
+
+@in_pattern("ofox", owner=True)
+async def _(e):
+ try:
+ match = e.text.split(" ", maxsplit=1)[1]
+ except IndexError:
+ kkkk = e.builder.article(
+ title="Enter Device Codename",
+ thumb=wb(ofox, 0, "image/jpeg", []),
+ text="**OFᴏx🦊Rᴇᴄᴏᴠᴇʀʏ**\n\nYou didn't search anything",
+ buttons=Button.switch_inline("Sᴇᴀʀᴄʜ Aɢᴀɪɴ", query="ofox ", same_peer=True),
+ )
+ return await e.answer([kkkk])
+ device, releases = await get_ofox(match)
+ if device.get("detail") is None:
+ fox = []
+ fullname = device["full_name"]
+ codename = device["codename"]
+ str(device["supported"])
+ maintainer = device["maintainer"]["name"]
+ link = f"https://orangefox.download/device/{codename}"
+ for data in releases["data"]:
+ release = data["type"]
+ version = data["version"]
+ size = humanbytes(data["size"])
+ release_date = datetime.utcfromtimestamp(data["date"]).strftime("%Y-%m-%d")
+ text = f"[\xad]({ofox})**OʀᴀɴɢᴇFᴏx Rᴇᴄᴏᴠᴇʀʏ Fᴏʀ**\n\n"
+ text += f"` Fᴜʟʟ Nᴀᴍᴇ: {fullname}`\n"
+ text += f"` Cᴏᴅᴇɴᴀᴍᴇ: {codename}`\n"
+ text += f"` Mᴀɪɴᴛᴀɪɴᴇʀ: {maintainer}`\n"
+ text += f"` Bᴜɪʟᴅ Tʏᴘᴇ: {release}`\n"
+ text += f"` Vᴇʀsɪᴏɴ: {version}`\n"
+ text += f"` Sɪᴢᴇ: {size}`\n"
+ text += f"` Bᴜɪʟᴅ Dᴀᴛᴇ: {release_date}`"
+ fox.append(
+ await e.builder.article(
+ title=f"{fullname}",
+ description=f"{version}\n{release_date}",
+ text=text,
+ thumb=wb(ofox, 0, "image/jpeg", []),
+ link_preview=True,
+ buttons=[
+ Button.url("Dᴏᴡɴʟᴏᴀᴅ", url=f"{link}"),
+ Button.switch_inline(
+ "Sᴇᴀʀᴄʜ Aɢᴀɪɴ", query="ofox ", same_peer=True
+ ),
+ ],
+ )
+ )
+ await e.answer(
+ fox, switch_pm="OrangeFox Recovery Search.", switch_pm_param="start"
+ )
+ else:
+ await e.answer(
+ [], switch_pm="OrangeFox Recovery Search.", switch_pm_param="start"
+ )
+
+
+@in_pattern("fl2lnk ?(.*)", owner=True)
+async def _(e):
+ match = e.pattern_match.group(1)
+ chat_id, msg_id = match.split(":")
+ filename = _webupload_cache[int(chat_id)][int(msg_id)]
+ if "/" in filename:
+ filename = filename.split("/")[-1]
+ __cache = f"{chat_id}:{msg_id}"
+ buttons = [
+ [
+ Button.inline("anonfiles", data=f"flanonfiles//{__cache}"),
+ Button.inline("transfer", data=f"fltransfer//{__cache}"),
+ ],
+ [
+ Button.inline("bayfiles", data=f"flbayfiles//{__cache}"),
+ Button.inline("x0.at", data=f"flx0.at//{__cache}"),
+ ],
+ [
+ Button.inline("file.io", data=f"flfile.io//{__cache}"),
+ Button.inline("siasky", data=f"flsiasky//{__cache}"),
+ ],
+ ]
+ try:
+ lnk = [
+ await e.builder.article(
+ title=f"Upload {filename}",
+ text=f"**File:**\n{filename}",
+ buttons=buttons,
+ )
+ ]
+ except BaseException as er:
+ LOGS.exception(er)
+ lnk = [
+ await e.builder.article(
+ title="fl2lnk",
+ text="File not found",
+ )
+ ]
+ await e.answer(lnk, switch_pm="File to Link.", switch_pm_param="start")
+
+
+@callback(
+ re_compile(
+ "fl(.*)",
+ ),
+ owner=True,
+)
+async def _(e):
+ t = (e.data).decode("UTF-8")
+ data = t[2:]
+ host = data.split("//")[0]
+ chat_id, msg_id = data.split("//")[1].split(":")
+ filename = _webupload_cache[int(chat_id)][int(msg_id)]
+ if "/" in filename:
+ filename = filename.split("/")[-1]
+ await e.edit(f"Uploading `{filename}` on {host}")
+ link = (await webuploader(chat_id, msg_id, host)).strip().replace("\n", "")
+ await e.edit(f"Uploaded `{filename}` on {host}.", buttons=Button.url("View", link))
+
+
+@in_pattern("repo", owner=True)
+async def repo(e):
+ res = [
+ await e.builder.article(
+ title="Ultroid Userbot",
+ description="Userbot | Telethon",
+ thumb=wb(ultpic, 0, "image/jpeg", []),
+ text="• **ULTROID USERBOT** •",
+ buttons=SUP_BUTTONS,
+ ),
+ ]
+ await e.answer(res, switch_pm="Ultroid Repo.", switch_pm_param="start")
+
+
+@in_pattern("go", owner=True)
+async def gsearch(q_event):
+ try:
+ match = q_event.text.split(maxsplit=1)[1]
+ except IndexError:
+ return await q_event.answer(
+ [], switch_pm="Google Search. Enter a query!", switch_pm_param="start"
+ )
+ searcher = []
+ gresults = await google_search(match)
+ for i in gresults:
+ try:
+ title = i["title"]
+ link = i["link"]
+ desc = i["description"]
+ searcher.append(
+ await q_event.builder.article(
+ title=title,
+ description=desc,
+ thumb=wb(gugirl, 0, "image/jpeg", []),
+ text=f"**Gᴏᴏɢʟᴇ Sᴇᴀʀᴄʜ**\n\n**••Tɪᴛʟᴇ••**\n`{title}`\n\n**••Dᴇsᴄʀɪᴘᴛɪᴏɴ••**\n`{desc}`",
+ link_preview=False,
+ buttons=[
+ [Button.url("Lɪɴᴋ", url=f"{link}")],
+ [
+ Button.switch_inline(
+ "Sᴇᴀʀᴄʜ Aɢᴀɪɴ",
+ query="go ",
+ same_peer=True,
+ ),
+ Button.switch_inline(
+ "Sʜᴀʀᴇ",
+ query=f"go {match}",
+ same_peer=False,
+ ),
+ ],
+ ],
+ ),
+ )
+ except IndexError:
+ break
+ await q_event.answer(searcher, switch_pm="Google Search.", switch_pm_param="start")
+
+
+@in_pattern("mods", owner=True)
+async def _(e):
+ try:
+ quer = e.text.split(" ", maxsplit=1)[1]
+ except IndexError:
+ return await e.answer(
+ [], switch_pm="Mod Apps Search. Enter app name!", switch_pm_param="start"
+ )
+ start = 0 * 3 + 1
+ da = base64.b64decode(choice(apis)).decode("ascii")
+ url = f"https://www.googleapis.com/customsearch/v1?key={da}&cx=25b3b50edb928435b&q={quer}&start={start}"
+ data = await async_searcher(url, re_json=True)
+ search_items = data.get("items", [])
+ modss = []
+ for a in search_items:
+ title = a.get("title")
+ desc = a.get("snippet")
+ link = a.get("link")
+ text = f"**••Tɪᴛʟᴇ••** `{title}`\n\n"
+ text += f"**Dᴇsᴄʀɪᴘᴛɪᴏɴ** `{desc}`"
+ modss.append(
+ await e.builder.article(
+ title=title,
+ description=desc,
+ text=text,
+ link_preview=True,
+ buttons=[
+ [Button.url("Dᴏᴡɴʟᴏᴀᴅ", url=f"{link}")],
+ [
+ Button.switch_inline(
+ "Mᴏʀᴇ Mᴏᴅs",
+ query="mods ",
+ same_peer=True,
+ ),
+ Button.switch_inline(
+ "Sʜᴀʀᴇ",
+ query=f"mods {quer}",
+ same_peer=False,
+ ),
+ ],
+ ],
+ ),
+ )
+ await e.answer(modss, switch_pm="Search Mod Applications.", switch_pm_param="start")
+
+
+APP_CACHE = {}
+RECENTS = {}
+PLAY_API = "https://googleplay.onrender.com/api/apps?q="
+
+
+@in_pattern("app", owner=True)
+async def _(e):
+ try:
+ f = e.text.split(maxsplit=1)[1].lower()
+ except IndexError:
+ get_string("instu_1")
+ res = []
+ if APP_CACHE and RECENTS.get(e.sender_id):
+ res.extend(
+ APP_CACHE[a][0] for a in RECENTS[e.sender_id] if APP_CACHE.get(a)
+ )
+ return await e.answer(
+ res, switch_pm=get_string("instu_2"), switch_pm_param="start"
+ )
+ try:
+ return await e.answer(
+ APP_CACHE[f], switch_pm="Application Searcher.", switch_pm_param="start"
+ )
+ except KeyError:
+ pass
+ foles = []
+ url = PLAY_API + f.replace(" ", "+")
+ aap = await async_searcher(url, re_json=True)
+ for z in aap["results"][:50]:
+ url = "https://play.google.com/store/apps/details?id=" + z["appId"]
+ name = z["title"]
+ desc = unescape(z["summary"])[:300].replace("
", "\n") + "..."
+ dev = z["developer"]["devId"]
+ text = f"**••Aᴘᴘ Nᴀᴍᴇ••** [{name}]({url})\n"
+ text += f"**••Dᴇᴠᴇʟᴏᴘᴇʀ••** `{dev}`\n"
+ text += f"**••Dᴇsᴄʀɪᴘᴛɪᴏɴ••**\n`{desc}`"
+ foles.append(
+ await e.builder.article(
+ title=name,
+ description=dev,
+ thumb=wb(z["icon"], 0, "image/jpeg", []),
+ text=text,
+ link_preview=True,
+ buttons=[
+ [Button.url("Lɪɴᴋ", url=url)],
+ [
+ Button.switch_inline(
+ "Mᴏʀᴇ Aᴘᴘs",
+ query="app ",
+ same_peer=True,
+ ),
+ Button.switch_inline(
+ "Sʜᴀʀᴇ",
+ query=f"app {f}",
+ same_peer=False,
+ ),
+ ],
+ ],
+ ),
+ )
+ APP_CACHE.update({f: foles})
+ if RECENTS.get(e.sender_id):
+ RECENTS[e.sender_id].append(f)
+ else:
+ RECENTS.update({e.sender_id: [f]})
+ await e.answer(foles, switch_pm="Application Searcher.", switch_pm_param="start")
+
+
+PISTON_URI = "https://emkc.org/api/v2/piston/"
+PISTON_LANGS = {}
+
+
+@in_pattern("run", owner=True)
+async def piston_run(event):
+ try:
+ lang = event.text.split()[1]
+ code = event.text.split(maxsplit=2)[2]
+ except IndexError:
+ result = await event.builder.article(
+ title="Bad Query",
+ description="Usage: [Language] [code]",
+ thumb=wb(
+ "https://graph.org/file/e33c57fc5f1044547e4d8.jpg", 0, "image/jpeg", []
+ ),
+ text=f'**Inline Usage**\n\n`@{asst.me.username} run python print("hello world")`\n\n[Language List](https://graph.org/Ultroid-09-01-6)',
+ )
+ return await event.answer([result])
+ if not PISTON_LANGS:
+ se = await async_searcher(f"{PISTON_URI}runtimes", re_json=True)
+ PISTON_LANGS.update({lang.pop("language"): lang for lang in se})
+ if lang in PISTON_LANGS.keys():
+ version = PISTON_LANGS[lang]["version"]
+ else:
+ result = await event.builder.article(
+ title="Unsupported Language",
+ description="Usage: [Language] [code]",
+ thumb=wb(
+ "https://graph.org/file/e33c57fc5f1044547e4d8.jpg", 0, "image/jpeg", []
+ ),
+ text=f'**Inline Usage**\n\n`@{asst.me.username} run python print("hello world")`\n\n[Language List](https://graph.org/Ultroid-09-01-6)',
+ )
+ return await event.answer([result])
+ output = await async_searcher(
+ f"{PISTON_URI}execute",
+ post=True,
+ json={
+ "language": lang,
+ "version": version,
+ "files": [{"content": code}],
+ },
+ re_json=True,
+ )
+
+ output = output["run"]["output"] or get_string("instu_4")
+ if len(output) > 3000:
+ output = f"{output[:3000]}..."
+ result = await event.builder.article(
+ title="Result",
+ description=output,
+ text=f"• **Language:**\n`{lang}`\n\n• **Code:**\n`{code}`\n\n• **Result:**\n`{output}`",
+ thumb=wb(
+ "https://graph.org/file/871ee4a481f58117dccc4.jpg", 0, "image/jpeg", []
+ ),
+ buttons=Button.switch_inline("Fork", query=event.text, same_peer=True),
+ )
+ await event.answer([result], switch_pm="• Piston •", switch_pm_param="start")
+
+
+FDROID_ = {}
+
+
+@in_pattern("fdroid", owner=True)
+async def do_magic(event):
+ try:
+ match = event.text.split(" ", maxsplit=1)[1].lower()
+ except IndexError:
+ return await event.answer(
+ [], switch_pm="Enter Query to Search", switch_pm_param="start"
+ )
+ if FDROID_.get(match):
+ return await event.answer(
+ FDROID_[match], switch_pm=f"• Results for {match}", switch_pm_param="start"
+ )
+ link = "https://search.f-droid.org/?q=" + match.replace(" ", "+")
+ content = await async_searcher(link, re_content=True)
+ BSC = bs(content, "html.parser", from_encoding="utf-8")
+ ress = []
+ for dat in BSC.find_all("a", "package-header")[:10]:
+ image = dat.find("img", "package-icon")["src"]
+ if image.endswith("/"):
+ image = "https://graph.org/file/a8dd4a92c5a53a89d0eff.jpg"
+ title = dat.find("h4", "package-name").text.strip()
+ desc = dat.find("span", "package-summary").text.strip()
+ text = f"• **Name :** `{title}`\n\n"
+ text += f"• **Description :** `{desc}`\n"
+ text += f"• **License :** `{dat.find('span', 'package-license').text.strip()}`"
+ imga = wb(image, 0, "image/jpeg", [])
+ ress.append(
+ await event.builder.article(
+ title=title,
+ type="photo",
+ description=desc,
+ text=text,
+ content=imga,
+ thumb=imga,
+ include_media=True,
+ buttons=[
+ Button.inline(
+ "• Download •", "fd" + dat["href"].split("packages/")[-1]
+ ),
+ Button.switch_inline("• Share •", query=event.text),
+ ],
+ )
+ )
+ msg = f"Showing {len(ress)} Results!" if ress else "No Results Found"
+ FDROID_.update({match: ress})
+ await event.answer(ress, switch_pm=msg, switch_pm_param="start")
+
+
+# Thanks to OpenSource
+_bearer_collected = [
+ "AAAAAAAAAAAAAAAAAAAAALIKKgEAAAAA1DRuS%2BI7ZRKiagD6KHYmreaXomo%3DP5Vaje4UTtEkODg0fX7nCh5laSrchhtLxeyEqxXpv0w9ZKspLD",
+ "AAAAAAAAAAAAAAAAAAAAAL5iUAEAAAAAmo6FYRjqdKlI3cNziIm%2BHUQB9Xs%3DS31pj0mxARMTOk2g9dvQ1yP9wknvY4FPBPUlE00smJcncw4dPR",
+ "AAAAAAAAAAAAAAAAAAAAAN6sVgEAAAAAMMjMMWrwgGyv7YQOWN%2FSAsO5SGM%3Dg8MG9Jq93Rlllaok6eht7HvRCruN4Vpzp4NaVsZaaHHWSTzKI8",
+]
+
+
+@in_pattern("twitter", owner=True)
+async def twitter_search(event):
+ try:
+ match = event.text.split(maxsplit=1)[1].lower()
+ except IndexError:
+ return await event.answer(
+ [], switch_pm="Enter Query to Search", switch_pm_param="start"
+ )
+ try:
+ return await event.answer(
+ _ult_cache["twitter"][match],
+ switch_pm="• Twitter Search •",
+ switch_pm_param="start",
+ )
+ except KeyError:
+ pass
+ headers = {"Authorization": f"bearer {choice(_bearer_collected)}"}
+ res = await async_searcher(
+ f"https://api.twitter.com/1.1/users/search.json?q={match}",
+ headers=headers,
+ re_json=True,
+ )
+ reso = []
+ for user in res:
+ thumb = wb(user["profile_image_url_https"], 0, "image/jpeg", [])
+ if user.get("profile_banner_url"):
+ url = user["profile_banner_url"]
+ text = f"[\xad]({url})• **Name :** `{user['name']}`\n"
+ else:
+ text = f"• **Name :** `{user['name']}`\n"
+ text += f"• **Description :** `{user['description']}`\n"
+ text += f"• **Username :** `@{user['screen_name']}`\n"
+ text += f"• **Followers :** `{user['followers_count']}` • **Following :** `{user['friends_count']}`\n"
+ pro_ = "https://twitter.com/" + user["screen_name"]
+ text += f"• **Link :** [Click Here]({pro_})\n_"
+ reso.append(
+ await event.builder.article(
+ title=user["name"],
+ description=user["description"],
+ url=pro_,
+ text=text,
+ thumb=thumb,
+ )
+ )
+ swi_ = f"🐦 Showing {len(reso)} Results!" if reso else "No User Found :("
+ await event.answer(reso, switch_pm=swi_, switch_pm_param="start")
+ if _ult_cache.get("twitter"):
+ _ult_cache["twitter"].update({match: reso})
+ else:
+ _ult_cache.update({"twitter": {match: reso}})
+
+
+_savn_cache = {}
+
+
+@in_pattern("saavn", owner=True)
+async def savn_s(event):
+ try:
+ query = event.text.split(maxsplit=1)[1].lower()
+ except IndexError:
+ return await event.answer(
+ [], switch_pm="Enter Query to search 🔍", switch_pm_param="start"
+ )
+ if query in _savn_cache:
+ return await event.answer(
+ _savn_cache[query],
+ switch_pm=f"Showing Results for {query}",
+ switch_pm_param="start",
+ )
+ results = await saavn_search(query)
+ swi = "🎵 Saavn Search" if results else "No Results Found!"
+ res = []
+ for song in results:
+ thumb = wb(song["image"], 0, "image/jpeg", [])
+ text = f"• **Title :** {song['title']}"
+ text += f"\n• **Year :** {song['year']}"
+ text += f"\n• **Lang :** {song['language']}"
+ text += f"\n• **Artist :** {song['artists']}"
+ text += f"\n• **Release Date :** {song['release_date']}"
+ res.append(
+ await event.builder.article(
+ title=song["title"],
+ description=song["artists"],
+ type="audio",
+ text=text,
+ include_media=True,
+ buttons=Button.switch_inline(
+ "Search Again 🔍", query="saavn", same_peer=True
+ ),
+ thumb=thumb,
+ content=wb(
+ song["url"],
+ 0,
+ "audio/mp4",
+ [
+ Audio(
+ title=song["title"],
+ duration=int(song["duration"]),
+ performer=song["artists"],
+ )
+ ],
+ ),
+ )
+ )
+ await event.answer(res, switch_pm=swi, switch_pm_param="start")
+ _savn_cache.update({query: res})
+
+
+@in_pattern("tl", owner=True)
+async def inline_tl(ult):
+ try:
+ match = ult.text.split(maxsplit=1)[1]
+ except IndexError:
+ text = f"**Telegram TlObjects Searcher.**\n__(Don't use if you don't know what it is!)__\n\n• Example Usage\n`@{asst.me.username} tl GetFullUserRequest`"
+ return await ult.answer(
+ [
+ await ult.builder.article(
+ title="How to Use?",
+ description="Tl Searcher by Ultroid",
+ url="https://t.me/TeamUltroid",
+ text=text,
+ )
+ ],
+ switch_pm="Tl Search 🔍",
+ switch_pm_param="start",
+ )
+ res = []
+ for key in tlobjects.values():
+ if match.lower() in key.__name__.lower():
+ tyyp = "Function" if "tl.functions." in str(key) else "Type"
+ text = f"**Name:** `{key.__name__}`\n"
+ text += f"**Category:** `{tyyp}`\n"
+ text += f"\n`from {key.__module__} import {key.__name__}`\n\n"
+ if args := str(inspect.signature(key))[1:][:-1]:
+ text += "**Parameter:**\n"
+ for para in args.split(","):
+ text += " " * 4 + "`" + para + "`\n"
+ text += f"\n**Layer:** `{LAYER}`"
+ res.append(
+ await ult.builder.article(
+ title=key.__name__,
+ description=tyyp,
+ url="https://t.me/TeamUltroid",
+ text=text[:4000],
+ )
+ )
+ mo = f"Showing {len(res)} results!" if res else f"No Results for {match}!"
+ await ult.answer(res[:50], switch_pm=mo, switch_pm_param="start")
+
+
+InlinePlugin.update(
+ {
+ "Pʟᴀʏ Sᴛᴏʀᴇ Aᴘᴘs": "app telegram",
+ "Mᴏᴅᴅᴇᴅ Aᴘᴘs": "mods minecraft",
+ "Sᴇᴀʀᴄʜ Oɴ Gᴏᴏɢʟᴇ": "go TeamUltroid",
+ "WʜɪSᴘᴇʀ": "wspr @username Hello🎉",
+ "YᴏᴜTᴜʙᴇ Dᴏᴡɴʟᴏᴀᴅᴇʀ": "yt Ed Sheeran Perfect",
+ "Piston Eval": "run javascript console.log('Hello Ultroid')",
+ "OʀᴀɴɢᴇFᴏx🦊": "ofox beryllium",
+ "Tᴡɪᴛᴛᴇʀ Usᴇʀ": "twitter theultroid",
+ "Fᴅʀᴏɪᴅ Sᴇᴀʀᴄʜ": "fdroid telegram",
+ "Sᴀᴀᴠɴ sᴇᴀʀᴄʜ": "saavn",
+ "Tʟ Sᴇᴀʀᴄʜ": "tl",
+ }
+)
diff --git a/assistant/localization.py b/assistant/localization.py
new file mode 100644
index 0000000000000000000000000000000000000000..9f1c18ab5bbc73677511d77493058756c89a6e0b
--- /dev/null
+++ b/assistant/localization.py
@@ -0,0 +1,47 @@
+# Ultroid - UserBot
+# Copyright (C) 2021-2025 TeamUltroid
+#
+# This file is a part of < https://github.com/TeamUltroid/Ultroid/ >
+# PLease read the GNU Affero General Public License in
+# .
+
+import re
+
+from . import (
+ Button,
+ ULTConfig,
+ callback,
+ get_back_button,
+ get_languages,
+ get_string,
+ udB,
+)
+
+
+@callback("lang", owner=True)
+async def setlang(event):
+ languages = get_languages()
+ tultd = [
+ Button.inline(
+ f"{languages[ult]['natively']} [{ult.lower()}]",
+ data=f"set_{ult}",
+ )
+ for ult in languages
+ ]
+ buttons = list(zip(tultd[::2], tultd[1::2]))
+ if len(tultd) % 2 == 1:
+ buttons.append((tultd[-1],))
+ buttons.append([Button.inline("« Back", data="mainmenu")])
+ await event.edit(get_string("ast_4"), buttons=buttons)
+
+
+@callback(re.compile(b"set_(.*)"), owner=True)
+async def settt(event):
+ lang = event.data_match.group(1).decode("UTF-8")
+ languages = get_languages()
+ ULTConfig.lang = lang
+ udB.del_key("language") if lang == "en" else udB.set_key("language", lang)
+ await event.edit(
+ f"Your language has been set to {languages[lang]['natively']} [{lang}].",
+ buttons=get_back_button("lang"),
+ )
diff --git a/assistant/manager/__init__.py b/assistant/manager/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..ca0dfd62baac13102dd2eaf0f5ba5d1af21b637b
--- /dev/null
+++ b/assistant/manager/__init__.py
@@ -0,0 +1,11 @@
+# Ultroid - UserBot
+# Copyright (C) 2021-2025 TeamUltroid
+#
+# This file is a part of < https://github.com/TeamUltroid/Ultroid/ >
+# PLease read the GNU Affero General Public License in
+# .
+
+from pyUltroid._misc._decorators import ultroid_cmd
+from pyUltroid.fns.helper import inline_mention
+
+from .. import *
diff --git a/assistant/manager/_help.py b/assistant/manager/_help.py
new file mode 100644
index 0000000000000000000000000000000000000000..bd3fe06d7dc6fe4bf5d31baa274fdde56a792c6f
--- /dev/null
+++ b/assistant/manager/_help.py
@@ -0,0 +1,134 @@
+# Ultroid - UserBot
+# Copyright (C) 2021-2025 TeamUltroid
+#
+# This file is a part of < https://github.com/TeamUltroid/Ultroid/ >
+# PLease read the GNU Affero General Public License in
+# .
+
+from . import *
+
+START = """
+🪅 **Help Menu** 🪅
+
+✘ /start : Check I am Alive or not.
+✘ /help : Get This Message.
+✘ /repo : Get Bot's Repo..
+
+🧑💻 Join **@TeamUltroid**
+"""
+
+ADMINTOOLS = """✘ **AdminTools** ✘
+
+• /pin : Pins the Replied Message
+• /pinned : Get Pinned message in chat.
+• /unpin : Unpin the Replied message
+• /unpin all : Unpin all Pinned Messages.
+
+• /ban (username/id/reply) : Ban the User
+• /unban (username/id/reply) : UnBan the User.
+
+• /mute (username/id/reply) : Mute the User.
+• /unmute (username/id/reply) : Unmute the User.
+
+• /tban (username/id/reply) (time) : Temporary ban a user
+• /tmute (username/id/reply) (time) : temporary Mutes a User.
+
+• /purge (purge messages)
+
+• /setgpic (reply photo) : keep Chat Photo of Group.
+• /delgpic : remove current chat Photo."""
+
+UTILITIES = """
+✘ ** Utilities ** ✘
+
+• /info (reply/username/id) : get detailed info of user.
+• /id : get chat/user id.
+• /tr : Translate Languages..
+• /q : Create Quotes.
+
+• /paste (reply file/text) : paste content on Spaceb.in
+• /meaning (text) : Get Meaning of that Word.
+• /google (query) : Search Something on Google..
+
+• /suggest (query/reply) : Creates a Yes / No Poll.
+"""
+
+LOCKS = """
+✘ ** Locks ** ✘
+
+• /lock (query) : lock particular content in chat.
+• /unlock (query) : Unlock some content.
+
+• All Queries
+- `msgs` : for messages.
+- `inlines` : for inline queries.
+- `media` : for all medias.
+- `games` : for games.
+- `sticker` : for stickers.
+- `polls` : for polls.
+- `gif` : for gifs.
+- `pin` : for pins.
+- `changeinfo` : for change info right.
+"""
+
+MISC = """
+✘ **Misc** ✘
+
+• /joke : Get Random Jokes.
+• /decide : Decide Something..
+
+**✘ Stickertools ✘**
+• /kang : add sticker to your pack.
+• /listpack : get all of yours pack..
+"""
+
+STRINGS = {"Admintools": ADMINTOOLS, "locks": LOCKS, "Utils": UTILITIES, "Misc": MISC}
+
+MNGE = udB.get_key("MNGR_EMOJI") or "•"
+
+
+def get_buttons():
+ BTTS = []
+ keys = STRINGS.copy()
+ while keys:
+ BT = []
+ for i in list(keys)[:2]:
+ text = f"{MNGE} {i} {MNGE}"
+ BT.append(Button.inline(text, f"hlp_{i}"))
+ del keys[i]
+ BTTS.append(BT)
+ url = f"https://t.me/{asst.me.username}?startgroup=true"
+ BTTS.append([Button.url("Add me to Group", url)])
+ return BTTS
+
+
+@asst_cmd(pattern="help")
+async def helpish(event):
+ if not event.is_private:
+ url = f"https://t.me/{asst.me.username}?start=start"
+ return await event.reply(
+ "Contact me in PM for help!", buttons=Button.url("Click me for Help", url)
+ )
+ if str(event.sender_id) in owner_and_sudos() and (
+ udB.get_key("DUAL_MODE") and (udB.get_key("DUAL_HNDLR") == "/")
+ ):
+ return
+ await event.reply(START, buttons=get_buttons())
+
+
+@callback("mngbtn", owner=True)
+async def ehwhshd(e):
+ buttons = get_buttons()
+ buttons.append([Button.inline("<< Back", "open")])
+ await e.edit(buttons=buttons)
+
+
+@callback("mnghome")
+async def home_aja(e):
+ await e.edit(START, buttons=get_buttons())
+
+
+@callback(re.compile("hlp_(.*)"))
+async def do_something(event):
+ match = event.pattern_match.group(1).strip().decode("utf-8")
+ await event.edit(STRINGS[match], buttons=Button.inline("<< Back", "mnghome"))
diff --git a/assistant/manager/_on_adds.py b/assistant/manager/_on_adds.py
new file mode 100644
index 0000000000000000000000000000000000000000..d788f37ae91cb307fbeef2c1ea02a9c16fe50f18
--- /dev/null
+++ b/assistant/manager/_on_adds.py
@@ -0,0 +1,23 @@
+# Ultroid - UserBot
+# Copyright (C) 2021-2025 TeamUltroid
+#
+# This file is a part of < https://github.com/TeamUltroid/Ultroid/ >
+# PLease read the GNU Affero General Public License in
+# .
+
+from telethon import events
+
+from . import *
+
+
+@asst.on(events.ChatAction(func=lambda x: x.user_added))
+async def dueha(e):
+ user = await e.get_user()
+ if not user.is_self:
+ return
+ sm = udB.get_key("ON_MNGR_ADD")
+ if sm == "OFF":
+ return
+ if not sm:
+ sm = "Thanks for Adding me :)"
+ await e.reply(sm, link_preview=False)
diff --git a/assistant/manager/admins.py b/assistant/manager/admins.py
new file mode 100644
index 0000000000000000000000000000000000000000..d8b896543ce4c5c0a61c64121db558201f076884
--- /dev/null
+++ b/assistant/manager/admins.py
@@ -0,0 +1,50 @@
+# Ultroid - UserBot
+# Copyright (C) 2021-2025 TeamUltroid
+#
+# This file is a part of < https://github.com/TeamUltroid/Ultroid/ >
+# PLease read the GNU Affero General Public License in
+# .
+
+import re
+
+from telethon.errors.rpcerrorlist import UserNotParticipantError
+
+from pyUltroid import _ult_cache
+
+from . import *
+
+
+@ultroid_cmd(pattern="d(kick|ban)", manager=True, require="ban_users")
+async def dowj(e):
+ replied = await e.get_reply_message()
+ if replied:
+ user = replied.sender_id
+ else:
+ return await e.eor("Reply to a message...")
+ try:
+ await replied.delete()
+ if e.pattern_match.group(1).strip() == "kick":
+ await e.client.kick_participant(e.chat_id, user)
+ te = "Kicked"
+ else:
+ await e.client.edit_permissions(e.chat_id, user, view_messages=False)
+ te = "Banned"
+ await e.eor(f"{te} Successfully!")
+ except Exception as E:
+ await e.eor(str(E))
+
+
+@callback(re.compile("cc_(.*)"), func=_ult_cache.get("admin_callback"))
+async def callback_(event):
+ data = event.data_match.group(1).decode("utf-8")
+ if data not in _ult_cache.get("admin_callback", {}):
+ return
+ try:
+ perm = await event.client.get_permissions(event.chat_id, event.sender_id)
+ except UserNotParticipantError:
+ return await event.answer("Join the Group First!", alert=True)
+ if not perm.is_admin:
+ return await event.answer("You are not an Admin!", alert=True)
+ _ult_cache["admin_callback"].update({data: (event.sender, perm)})
+ await event.answer("Verification Done!")
+ await event.delete()
diff --git a/assistant/manager/afk.py b/assistant/manager/afk.py
new file mode 100644
index 0000000000000000000000000000000000000000..505a81d958f886397b4f8471897c22c36f8fa570
--- /dev/null
+++ b/assistant/manager/afk.py
@@ -0,0 +1,110 @@
+# Ultroid - UserBot
+# Copyright (C) 2021-2025 TeamUltroid
+#
+# This file is a part of < https://github.com/TeamUltroid/Ultroid/ >
+# PLease read the GNU Affero General Public License in
+# .
+
+from datetime import datetime as dt
+
+from telethon.events import NewMessage
+from telethon.tl.types import (
+ Message,
+ MessageEntityMention,
+ MessageEntityMentionName,
+ User,
+)
+from telethon.utils import get_display_name
+
+from pyUltroid.fns.helper import inline_mention, time_formatter
+
+from . import asst, asst_cmd
+
+AFK = {}
+
+
+@asst_cmd(pattern="afk", func=lambda x: not x.is_private)
+async def go_afk(event):
+ sender = await event.get_sender()
+ if (not isinstance(sender, User)) or sender.bot:
+ return
+ try:
+ reason = event.text.split(" ", maxsplit=1)[1]
+ except IndexError:
+ reason = None
+ if event.is_reply and not reason:
+ replied = await event.get_reply_message()
+ if not reason and replied.text and not replied.media:
+ reason = replied.text
+ else:
+ reason = replied
+ time_ = dt.now()
+ if AFK.get(event.chat_id):
+ AFK[event.chat_id].update({event.sender_id: {"reason": reason, "time": time_}})
+ else:
+ AFK.update(
+ {event.chat_id: {event.sender_id: {"reason": reason, "time": time_}}}
+ )
+ mention = inline_mention(sender)
+ msg = f"**{mention} went AFK Now!**"
+ if reason and not isinstance(reason, str):
+ await event.reply(reason)
+ else:
+ msg += f"\n\n**Reason : ** `{reason}`"
+ await event.reply(msg)
+
+
+@asst.on(NewMessage(func=lambda x: AFK.get(x.chat_id) and not x.is_private))
+async def make_change(event):
+ if event.text.startswith("/afk"):
+ return
+ sender = await event.get_sender()
+ if (not isinstance(sender, User)) or sender.bot:
+ return
+ chat_ = AFK[event.chat_id]
+ if event.sender_id in chat_.keys():
+ name = get_display_name(event.sender)
+ cha_send = chat_[event.sender_id]
+ time_ = time_formatter((dt.now() - cha_send["time"]).seconds * 1000)
+ msg = f"**{name}** is No Longer AFK!\n**Was AFK for** {time_}"
+ await event.reply(msg)
+ del chat_[event.sender_id]
+ if not chat_:
+ del AFK[event.chat_id]
+ ST_SPAM = []
+ replied = await event.get_reply_message()
+ if replied:
+ name = get_display_name(replied.sender)
+ if replied.sender_id in chat_.keys():
+ s_der = chat_[replied.sender_id]
+ res_ = s_der["reason"]
+ time_ = time_formatter((dt.now() - s_der["time"]).seconds * 1000)
+ msg = f"**{name}** is AFK Currently!\n**From :** {time_}"
+ if res_ and isinstance(res_, str):
+ msg += f"\n**Reason :** {res_}"
+ elif res_ and isinstance(res_, Message):
+ await event.reply(res_)
+ await event.reply(msg)
+ ST_SPAM.append(replied.sender_id)
+ for ent, text in event.get_entities_text():
+ dont_send, entity = None, None
+ if isinstance(ent, MessageEntityMentionName):
+ c_id = ent.user_id
+ elif isinstance(ent, MessageEntityMention):
+ c_id = text
+ else:
+ c_id = None
+ if c_id:
+ entity = await event.client.get_entity(c_id)
+ if entity and entity.id in chat_.keys() and entity.id not in ST_SPAM:
+ ST_SPAM.append(entity.id)
+ s_der = chat_[entity.id]
+ name = get_display_name(entity)
+ res_ = s_der["reason"]
+ time_ = time_formatter((dt.now() - s_der["time"]).seconds * 1000)
+ msg = f"**{name}** is AFK Currently!\n**From :** {time_}"
+ if res_ and isinstance(res_, str):
+ msg += f"\n**Reason :** {res_}"
+ elif res_ and isinstance(res_, Message):
+ await event.reply(res_)
+ await event.reply(msg)
diff --git a/assistant/manager/misc.py b/assistant/manager/misc.py
new file mode 100644
index 0000000000000000000000000000000000000000..0133df8bfd8ab5745b2eba05298c1140af721bc3
--- /dev/null
+++ b/assistant/manager/misc.py
@@ -0,0 +1,64 @@
+# Ultroid - UserBot
+# Copyright (C) 2021-2025 TeamUltroid
+#
+# This file is a part of < https://github.com/TeamUltroid/Ultroid/ >
+# PLease read the GNU Affero General Public License in
+# .
+
+
+import random
+
+import aiohttp
+
+from pyUltroid.dB import DEVLIST
+from pyUltroid.fns.admins import admin_check
+
+from . import *
+
+
+@asst_cmd(pattern="decide")
+async def dheh(e):
+ text = ["Yes", "NoU", "Maybe", "IDK"]
+ text = random.choice(text)
+ ri = e.reply_to_msg_id or e.id
+ await e.client.send_message(e.chat_id, text, reply_to=ri)
+
+
+@asst_cmd(pattern="echo( (.*)|$)")
+async def oqha(e):
+ if not await admin_check(e):
+ return
+ if match := e.pattern_match.group(1).strip():
+ text = match
+ reply_to = e
+ elif e.is_reply:
+ text = (await e.get_reply_message()).text
+ reply_to = e.reply_to_msg_id
+ else:
+ return await e.eor("What to Echo?", time=5)
+ try:
+ await e.delete()
+ except BaseException as ex:
+ LOGS.error(ex)
+ await e.client.send_message(e.chat_id, text, reply_to=reply_to)
+
+
+@asst_cmd(pattern="kickme$")
+async def doit(e):
+ if e.sender_id in DEVLIST:
+ return await eod(e, "`I will Not Kick You, my Developer..`")
+ try:
+ await e.client.kick_participant(e.chat_id, e.sender_id)
+ except Exception as Fe:
+ return await e.eor(str(Fe), time=5)
+ await e.eor("Yes, You are right, get out.", time=5)
+
+
+@asst_cmd(pattern="joke$")
+async def do_joke(e):
+ e = await e.get_reply_message() if e.is_reply else e
+ link = "https://v2.jokeapi.dev/joke/Any?blacklistFlags=nsfw,religious,political,racist,sexist,explicit&type=single"
+ async with aiohttp.ClientSession() as ses:
+ async with ses.get(link) as out:
+ out = await out.json()
+ await e.reply(out["joke"])
diff --git a/assistant/manager/stickermanager.py b/assistant/manager/stickermanager.py
new file mode 100644
index 0000000000000000000000000000000000000000..1d8e5fe26a3c633b5f22269f58656a1e5c31930e
--- /dev/null
+++ b/assistant/manager/stickermanager.py
@@ -0,0 +1,180 @@
+# Ultroid - UserBot
+# Copyright (C) 2021-2025 TeamUltroid
+#
+# This file is a part of < https://github.com/TeamUltroid/Ultroid/ >
+# PLease read the GNU Affero General Public License in
+# .
+
+import random
+
+from telethon import errors
+from telethon.errors.rpcerrorlist import StickersetInvalidError
+from telethon.tl.functions.messages import GetStickerSetRequest as GetSticker
+from telethon.tl.functions.messages import UploadMediaRequest
+from telethon.tl.functions.stickers import AddStickerToSetRequest as AddSticker
+from telethon.tl.functions.stickers import CreateStickerSetRequest
+from telethon.tl.types import InputPeerSelf
+from telethon.tl.types import InputStickerSetItem as SetItem
+from telethon.tl.types import InputStickerSetShortName, User
+from telethon.utils import get_display_name, get_input_document
+
+from pyUltroid.fns.misc import Quotly
+from pyUltroid.fns.tools import TgConverter
+
+from . import LOGS, asst, asst_cmd, udB
+
+
+@asst_cmd(
+ pattern="kang",
+)
+async def kang_cmd(ult):
+ sender = await ult.get_sender()
+ if not isinstance(sender, User):
+ return
+ if not ult.is_reply:
+ return await ult.eor("`Reply to a sticker/photo..`", time=5)
+ reply = await ult.get_reply_message()
+ if sender.username:
+ pre = sender.username[:4]
+ else:
+ pre = random.random_string(length=3)
+ animated, dl, video = None, None, None
+ try:
+ emoji = ult.text.split(maxsplit=1)[1]
+ except IndexError:
+ emoji = None
+ if reply.sticker:
+ file = get_input_document(reply.sticker)
+ emoji = emoji or reply.file.emoji
+ name = reply.file.name
+ if name.endswith(".tgs"):
+ animated = True
+ dl = await reply.download_media()
+ elif name.endswith(".webm"):
+ video = True
+ dl = await reply.download_media()
+ elif reply.photo:
+ dl = await reply.download_media()
+ name = "sticker.webp"
+ image = TgConverter.resize_photo_sticker(dl)
+ image.save(name, "WEBP")
+ elif reply.text:
+ dl = await Quotly().create_quotly(reply)
+ else:
+ return await ult.eor("`Reply to sticker or text to add it in your pack...`")
+ if not emoji:
+ emoji = "🏵"
+ if dl:
+ upl = await ult.client.upload_file(dl)
+ file = get_input_document(
+ await ult.client(UploadMediaRequest(InputPeerSelf(), upl))
+ )
+ get_ = udB.get_key("STICKERS") or {}
+ type_ = "anim" if animated else "static"
+ if not get_.get(ult.sender_id) or not get_.get(ult.sender_id, {}).get(type_):
+ sn = f"{pre}_{ult.sender_id}"
+ title = f"{get_display_name(sender)}'s Kang Pack"
+ if animated:
+ type_ = "anim"
+ sn += "_anim"
+ title += " (Animated)"
+ elif video:
+ type_ = "vid"
+ sn += "_vid"
+ title += " (Video)"
+ sn += f"_by_{asst.me.username}"
+ try:
+ await asst(GetSticker(InputStickerSetShortName(sn), hash=0))
+ sn = sn.replace(str(ult.sender_id), f"{ult.sender_id}_{ult.id}")
+ except StickersetInvalidError:
+ pass
+ try:
+ pack = await ult.client(
+ CreateStickerSetRequest(
+ user_id=sender.id,
+ title=title,
+ short_name=sn,
+ stickers=[SetItem(file, emoji=emoji)],
+ videos=video,
+ animated=animated,
+ software="@TeamUltroid",
+ )
+ )
+ except Exception as er:
+ return await ult.eor(str(er))
+ sn = pack.set.short_name
+ if not get_.get(ult.sender_id):
+ get_.update({ult.sender_id: {type_: [sn]}})
+ else:
+ get_[ult.sender_id].update({type_: [sn]})
+ udB.set_key("STICKERS", get_)
+ return await ult.reply(
+ f"**Kanged Successfully!\nEmoji :** {emoji}\n**Link :** [Click Here](https://t.me/addstickers/{sn})"
+ )
+ name = get_[ult.sender_id][type_][-1]
+ try:
+ await asst(GetSticker(InputStickerSetShortName(name), hash=0))
+ except StickersetInvalidError:
+ get_[ult.sender_id][type_].remove(name)
+ try:
+ await asst(
+ AddSticker(InputStickerSetShortName(name), SetItem(file, emoji=emoji))
+ )
+ except (errors.StickerpackStickersTooMuchError, errors.StickersTooMuchError):
+ sn = f"{pre}{ult.sender_id}_{ult.id}"
+ title = f"{get_display_name(sender)}'s Kang Pack"
+ if animated:
+ sn += "_anim"
+ title += " (Animated)"
+ elif video:
+ sn += "_vid"
+ title += "(Video)"
+ sn += f"_by_{asst.me.username}"
+ try:
+ pack = await ult.client(
+ CreateStickerSetRequest(
+ user_id=sender.id,
+ title=title,
+ short_name=sn,
+ stickers=[SetItem(file, emoji=emoji)],
+ animated=animated,
+ )
+ )
+ except Exception as er:
+ return await ult.eor(str(er))
+ get_[ult.sender_id][type_].append(pack.set.short_name)
+ udB.set_key("STICKERS", get_)
+ return await ult.reply(
+ f"**Created New Kang Pack!\nEmoji :** {emoji}\n**Link :** [Click Here](https://t.me/addstickers/{sn})"
+ )
+ except Exception as er:
+ LOGS.exception(er)
+ return await ult.reply(str(er))
+ await ult.reply(
+ f"Sticker Added to Pack Successfully\n**Link :** [Click Here](https://t.me/addstickers/{name})"
+ )
+
+
+@asst_cmd(pattern="listpack")
+async def do_magic(ult):
+ ko = udB.get_key("STICKERS") or {}
+ if not ko.get(ult.sender_id):
+ return await ult.reply("No Sticker Pack Found!")
+ al_ = []
+ ul = ko[ult.sender_id]
+ for _ in ul.keys():
+ al_.extend(ul[_])
+ msg = "• **Stickers Owned by You!**\n\n"
+ for _ in al_:
+ try:
+ pack = await ult.client(GetSticker(InputStickerSetShortName(_), hash=0))
+ msg += f"• [{pack.set.title}](https://t.me/addstickers/{_})\n"
+ except StickerSetInvalidError:
+ if ul.get("anim") and _ in ul["anim"]:
+ ul["anim"].remove(_)
+ elif ul.get("vid") and _ in ul["vid"]:
+ ul["vid"].remove(_)
+ else:
+ ul["static"].remove(_)
+ udB.set_key("STICKERS", ko)
+ await ult.reply(msg)
diff --git a/assistant/pmbot.py b/assistant/pmbot.py
new file mode 100644
index 0000000000000000000000000000000000000000..961c2956ec376fa9fa163635d7dd75f94e004a2e
--- /dev/null
+++ b/assistant/pmbot.py
@@ -0,0 +1,150 @@
+# Ultroid - UserBot
+# Copyright (C) 2021-2025 TeamUltroid
+#
+# This file is a part of < https://github.com/TeamUltroid/Ultroid/ >
+# PLease read the GNU Affero General Public License in
+# .
+
+# https://github.com/xditya/TeleBot/blob/master/telebot/plugins/mybot/pmbot/incoming.py
+
+# --------------------------------------- Imports -------------------------------------------- #
+
+import os
+
+from telethon.errors.rpcerrorlist import UserNotParticipantError
+from telethon.tl.custom import Button
+from telethon.tl.functions.channels import GetFullChannelRequest
+from telethon.tl.functions.messages import GetFullChatRequest
+from telethon.tl.types import Channel, Chat
+from telethon.utils import get_display_name
+
+from pyUltroid.dB.base import KeyManager
+from pyUltroid.dB.botchat_db import *
+from pyUltroid.fns.helper import inline_mention
+
+from . import *
+
+botb = KeyManager("BOTBLS", cast=list)
+FSUB = udB.get_key("PMBOT_FSUB")
+CACHE = {}
+# --------------------------------------- Incoming -------------------------------------------- #
+
+
+@asst_cmd(
+ load=AST_PLUGINS,
+ incoming=True,
+ func=lambda e: e.is_private and not botb.contains(e.sender_id),
+)
+async def on_new_mssg(event):
+ who = event.sender_id
+ # doesn't reply to that user anymore
+ if event.text.startswith("/") or who == OWNER_ID:
+ return
+ if FSUB:
+ MSG = ""
+ BTTS = []
+ for chat in FSUB:
+ try:
+ await event.client.get_permissions(chat, event.sender_id)
+ except UserNotParticipantError:
+ if not MSG:
+ MSG += get_string("pmbot_1")
+ try:
+ uri = ""
+ TAHC_ = await event.client.get_entity(chat)
+ if hasattr(TAHC_, "username") and TAHC_.username:
+ uri = f"t.me/{TAHC_.username}"
+ elif CACHE.get(chat):
+ uri = CACHE[chat]
+ else:
+ if isinstance(TAHC_, Channel):
+ FUGB = await event.client(GetFullChannelRequest(chat))
+ elif isinstance(TAHC_, Chat):
+ FUGB = await event.client(GetFullChatRequest(chat))
+ else:
+ return
+ if FUGB.full_chat.exported_invite:
+ CACHE[chat] = FUGB.full_chat.exported_invite.link
+ uri = CACHE[chat]
+ BTTS.append(Button.url(get_display_name(TAHC_), uri))
+ except Exception as er:
+ LOGS.exception(f"Error On PmBot Force Sub!\n - {chat} \n{er}")
+ if MSG and BTTS:
+ return await event.reply(MSG, buttons=BTTS)
+ xx = await event.forward_to(OWNER_ID)
+ if event.fwd_from:
+ await xx.reply(f"From {inline_mention(event.sender)} [`{event.sender_id}`]")
+ add_stuff(xx.id, who)
+
+
+# --------------------------------------- Outgoing -------------------------------------------- #
+
+
+@asst_cmd(
+ load=AST_PLUGINS,
+ from_users=[OWNER_ID],
+ incoming=True,
+ func=lambda e: e.is_private and e.is_reply,
+)
+async def on_out_mssg(event):
+ x = event.reply_to_msg_id
+ to_user = get_who(x)
+ if event.text.startswith("/who"):
+ try:
+ k = await asst.get_entity(to_user)
+ photu = await event.client.download_profile_photo(k.id)
+ await event.reply(
+ f"• **Name :** {get_display_name(k)}\n• **ID :** `{k.id}`\n• **Link :** {inline_mention(k)}",
+ file=photu,
+ )
+ if photu:
+ os.remove(photu)
+ return
+ except BaseException as er:
+ return await event.reply(f"**ERROR : **{str(er)}")
+ elif event.text.startswith("/"):
+ return
+ if to_user:
+ await asst.send_message(to_user, event.message)
+
+
+# --------------------------------------- Ban/Unban -------------------------------------------- #
+
+
+@asst_cmd(
+ pattern="ban",
+ load=AST_PLUGINS,
+ from_users=[OWNER_ID],
+ func=lambda x: x.is_private,
+)
+async def banhammer(event):
+ if not event.is_reply:
+ return await event.reply(get_string("pmbot_2"))
+ target = get_who(event.reply_to_msg_id)
+ if botb.contains(target):
+ return await event.reply(get_string("pmbot_3"))
+
+ botb.add(target)
+ await event.reply(f"#BAN\nUser : {target}")
+ await asst.send_message(target, get_string("pmbot_4"))
+
+
+@asst_cmd(
+ pattern="unban",
+ load=AST_PLUGINS,
+ from_users=[OWNER_ID],
+ func=lambda x: x.is_private,
+)
+async def unbanhammer(event):
+ if not event.is_reply:
+ return await event.reply(get_string("pmbot_5"))
+ target = get_who(event.reply_to_msg_id)
+ if not botb.contains(target):
+ return await event.reply(get_string("pmbot_6"))
+
+ botb.remove(target)
+ await event.reply(f"#UNBAN\nUser : {target}")
+ await asst.send_message(target, get_string("pmbot_7"))
+
+
+# --------------------------------------- END -------------------------------------------- #
diff --git a/assistant/start.py b/assistant/start.py
new file mode 100644
index 0000000000000000000000000000000000000000..3dd246dc35a6e9b59f7b4170077ea0bad0442b34
--- /dev/null
+++ b/assistant/start.py
@@ -0,0 +1,240 @@
+# Ultroid - UserBot
+# Copyright (C) 2021-2025 TeamUltroid
+#
+# This file is a part of < https://github.com/TeamUltroid/Ultroid/ >
+# PLease read the GNU Affero General Public License in
+# .
+
+from datetime import datetime
+
+from pytz import timezone as tz
+from telethon import Button, events
+from telethon.errors.rpcerrorlist import MessageDeleteForbiddenError
+from telethon.utils import get_display_name
+
+from pyUltroid._misc import SUDO_M, owner_and_sudos
+from pyUltroid.dB.base import KeyManager
+from pyUltroid.fns.helper import inline_mention
+from strings import get_string
+
+from . import *
+
+Owner_info_msg = udB.get_key("BOT_INFO_START")
+custom_info = True
+if Owner_info_msg is None:
+ custom_info = False
+ Owner_info_msg = f"""
+**Owner** - {OWNER_NAME}
+**OwnerID** - `{OWNER_ID}`
+
+**Message Forwards** - {udB.get_key("PMBOT")}
+
+**Ultroid [v{ultroid_version}](https://github.com/TeamUltroid/Ultroid), powered by @TeamUltroid**
+"""
+
+
+_settings = [
+ [
+ Button.inline("API Kᴇʏs", data="cbs_apiset"),
+ Button.inline("Pᴍ Bᴏᴛ", data="cbs_chatbot"),
+ ],
+ [
+ Button.inline("Aʟɪᴠᴇ", data="cbs_alvcstm"),
+ Button.inline("PᴍPᴇʀᴍɪᴛ", data="cbs_ppmset"),
+ ],
+ [
+ Button.inline("Fᴇᴀᴛᴜʀᴇs", data="cbs_otvars"),
+ Button.inline("VC Sᴏɴɢ Bᴏᴛ", data="cbs_vcb"),
+ ],
+ [Button.inline("« Bᴀᴄᴋ", data="mainmenu")],
+]
+
+_start = [
+ [
+ Button.inline("Lᴀɴɢᴜᴀɢᴇ 🌐", data="lang"),
+ Button.inline("Sᴇᴛᴛɪɴɢs ⚙️", data="setter"),
+ ],
+ [
+ Button.inline("Sᴛᴀᴛs ✨", data="stat"),
+ Button.inline("Bʀᴏᴀᴅᴄᴀsᴛ 📻", data="bcast"),
+ ],
+ [Button.inline("TɪᴍᴇZᴏɴᴇ 🌎", data="tz")],
+]
+
+
+@callback("ownerinfo")
+async def own(event):
+ msg = Owner_info_msg.format(
+ mention=event.sender.mention, me=inline_mention(ultroid_bot.me)
+ )
+ if custom_info:
+ msg += "\n\n• Powered by **@TeamUltroid**"
+ await event.edit(
+ msg,
+ buttons=[Button.inline("Close", data="closeit")],
+ link_preview=False,
+ )
+
+
+@callback("closeit")
+async def closet(lol):
+ try:
+ await lol.delete()
+ except MessageDeleteForbiddenError:
+ await lol.answer("MESSAGE_TOO_OLD", alert=True)
+
+
+@asst_cmd(pattern="start( (.*)|$)", forwards=False, func=lambda x: not x.is_group)
+async def ultroid(event):
+ args = event.pattern_match.group(1).strip()
+ keym = KeyManager("BOT_USERS", cast=list)
+ if not keym.contains(event.sender_id) and event.sender_id not in owner_and_sudos():
+ keym.add(event.sender_id)
+ kak_uiw = udB.get_key("OFF_START_LOG")
+ if not kak_uiw or kak_uiw != True:
+ msg = f"{inline_mention(event.sender)} `[{event.sender_id}]` started your [Assistant bot](@{asst.me.username})."
+ buttons = [[Button.inline("Info", "itkkstyo")]]
+ if event.sender.username:
+ buttons[0].append(
+ Button.mention(
+ "User", await event.client.get_input_entity(event.sender_id)
+ )
+ )
+ await event.client.send_message(
+ udB.get_key("LOG_CHANNEL"), msg, buttons=buttons
+ )
+ if event.sender_id not in SUDO_M.fullsudos:
+ ok = ""
+ me = inline_mention(ultroid_bot.me)
+ mention = inline_mention(event.sender)
+ if args and args != "set":
+ await get_stored_file(event, args)
+ if not udB.get_key("STARTMSG"):
+ if udB.get_key("PMBOT"):
+ ok = "You can contact my master using this bot!!\n\nSend your Message, I will Deliver it To Master."
+ await event.reply(
+ f"Hey there {mention}, this is Ultroid Assistant of {me}!\n\n{ok}",
+ file=udB.get_key("STARTMEDIA"),
+ buttons=[Button.inline("Info.", data="ownerinfo")]
+ if Owner_info_msg
+ else None,
+ )
+ else:
+ await event.reply(
+ udB.get_key("STARTMSG").format(me=me, mention=mention),
+ file=udB.get_key("STARTMEDIA"),
+ buttons=[Button.inline("Info.", data="ownerinfo")]
+ if Owner_info_msg
+ else None,
+ )
+ else:
+ name = get_display_name(event.sender)
+ if args == "set":
+ await event.reply(
+ "Choose from the below options -",
+ buttons=_settings,
+ )
+ elif args:
+ await get_stored_file(event, args)
+ else:
+ await event.reply(
+ get_string("ast_3").format(name),
+ buttons=_start,
+ )
+
+
+@callback("itkkstyo", owner=True)
+async def ekekdhdb(e):
+ text = f"When New Visitor will visit your Assistant Bot. You will get this log message!\n\nTo Disable : {HNDLR}setdb OFF_START_LOG True"
+ await e.answer(text, alert=True)
+
+
+@callback("mainmenu", owner=True, func=lambda x: not x.is_group)
+async def ultroid(event):
+ await event.edit(
+ get_string("ast_3").format(OWNER_NAME),
+ buttons=_start,
+ )
+
+
+@callback("stat", owner=True)
+async def botstat(event):
+ ok = len(udB.get_key("BOT_USERS") or [])
+ msg = """Ultroid Assistant - Stats
+Total Users - {}""".format(
+ ok,
+ )
+ await event.answer(msg, cache_time=0, alert=True)
+
+
+@callback("bcast", owner=True)
+async def bdcast(event):
+ keym = KeyManager("BOT_USERS", cast=list)
+ total = keym.count()
+ await event.edit(f"• Broadcast to {total} users.")
+ async with event.client.conversation(OWNER_ID) as conv:
+ await conv.send_message(
+ "Enter your broadcast message.\nUse /cancel to stop the broadcast.",
+ )
+ response = await conv.get_response()
+ if response.message == "/cancel":
+ return await conv.send_message("Cancelled!!")
+ success = 0
+ fail = 0
+ await conv.send_message(f"Starting a broadcast to {total} users...")
+ start = datetime.now()
+ for i in keym.get():
+ try:
+ await asst.send_message(int(i), response)
+ success += 1
+ except BaseException:
+ fail += 1
+ end = datetime.now()
+ time_taken = (end - start).seconds
+ await conv.send_message(
+ f"""
+**Broadcast completed in {time_taken} seconds.**
+Total Users in Bot - {total}
+**Sent to** : `{success} users.`
+**Failed for** : `{fail} user(s).`""",
+ )
+
+
+@callback("setter", owner=True)
+async def setting(event):
+ await event.edit(
+ "Choose from the below options -",
+ buttons=_settings,
+ )
+
+
+@callback("tz", owner=True)
+async def timezone_(event):
+ await event.delete()
+ pru = event.sender_id
+ var = "TIMEZONE"
+ name = "Timezone"
+ async with event.client.conversation(pru) as conv:
+ await conv.send_message(
+ "Send Your TimeZone From This List [Check From Here](http://www.timezoneconverter.com/cgi-bin/findzone.tzc)"
+ )
+ response = conv.wait_event(events.NewMessage(chats=pru))
+ response = await response
+ themssg = response.message.message
+ if themssg == "/cancel":
+ return await conv.send_message(
+ "Cancelled!!",
+ buttons=get_back_button("mainmenu"),
+ )
+ try:
+ tz(themssg)
+ await setit(event, var, themssg)
+ await conv.send_message(
+ f"{name} changed to {themssg}\n",
+ buttons=get_back_button("mainmenu"),
+ )
+ except BaseException:
+ await conv.send_message(
+ "Wrong TimeZone, Try again",
+ buttons=get_back_button("mainmenu"),
+ )
diff --git a/assistant/ytdl.py b/assistant/ytdl.py
new file mode 100644
index 0000000000000000000000000000000000000000..1099690996eb9e057624dc3a845cd01d4d7de21f
--- /dev/null
+++ b/assistant/ytdl.py
@@ -0,0 +1,307 @@
+# Ultroid - UserBot
+# Copyright (C) 2021-2025 TeamUltroid
+#
+# This file is a part of < https://github.com/TeamUltroid/Ultroid/ >
+# PLease read the GNU Affero General Public License in
+# .
+
+
+import os
+import re
+
+try:
+ from PIL import Image
+except ImportError:
+ Image = None
+from telethon import Button
+from telethon.errors.rpcerrorlist import FilePartLengthInvalidError, MediaEmptyError
+from telethon.tl.types import DocumentAttributeAudio, DocumentAttributeVideo
+from telethon.tl.types import InputWebDocument as wb
+
+from pyUltroid.fns.helper import (
+ bash,
+ fast_download,
+ humanbytes,
+ numerize,
+ time_formatter,
+)
+from pyUltroid.fns.ytdl import dler, get_buttons, get_formats
+
+from . import LOGS, asst, callback, in_pattern, udB
+
+try:
+ from youtubesearchpython import VideosSearch
+except ImportError:
+ LOGS.info("'youtubesearchpython' not installed!")
+ VideosSearch = None
+
+
+ytt = "https://graph.org/file/afd04510c13914a06dd03.jpg"
+_yt_base_url = "https://www.youtube.com/watch?v="
+BACK_BUTTON = {}
+
+
+@in_pattern("yt", owner=True)
+async def _(event):
+ try:
+ string = event.text.split(" ", maxsplit=1)[1]
+ except IndexError:
+ fuk = event.builder.article(
+ title="Search Something",
+ thumb=wb(ytt, 0, "image/jpeg", []),
+ text="**YᴏᴜTᴜʙᴇ Sᴇᴀʀᴄʜ**\n\nYou didn't search anything",
+ buttons=Button.switch_inline(
+ "Sᴇᴀʀᴄʜ Aɢᴀɪɴ",
+ query="yt ",
+ same_peer=True,
+ ),
+ )
+ await event.answer([fuk])
+ return
+ results = []
+ search = VideosSearch(string, limit=50)
+ nub = search.result()
+ nibba = nub["result"]
+ for v in nibba:
+ ids = v["id"]
+ link = _yt_base_url + ids
+ title = v["title"]
+ duration = v["duration"]
+ views = v["viewCount"]["short"]
+ publisher = v["channel"]["name"]
+ published_on = v["publishedTime"]
+ description = (
+ v["descriptionSnippet"][0]["text"]
+ if v.get("descriptionSnippet")
+ and len(v["descriptionSnippet"][0]["text"]) < 500
+ else "None"
+ )
+ thumb = f"https://i.ytimg.com/vi/{ids}/hqdefault.jpg"
+ text = f"**Title: [{title}]({link})**\n\n"
+ text += f"`Description: {description}\n\n"
+ text += f"「 Duration: {duration} 」\n"
+ text += f"「 Views: {views} 」\n"
+ text += f"「 Publisher: {publisher} 」\n"
+ text += f"「 Published on: {published_on} 」`"
+ desc = f"{title}\n{duration}"
+ file = wb(thumb, 0, "image/jpeg", [])
+ buttons = [
+ [
+ Button.inline("Audio", data=f"ytdl:audio:{ids}"),
+ Button.inline("Video", data=f"ytdl:video:{ids}"),
+ ],
+ [
+ Button.switch_inline(
+ "Sᴇᴀʀᴄʜ Aɢᴀɪɴ",
+ query="yt ",
+ same_peer=True,
+ ),
+ Button.switch_inline(
+ "Sʜᴀʀᴇ",
+ query=f"yt {string}",
+ same_peer=False,
+ ),
+ ],
+ ]
+ BACK_BUTTON.update({ids: {"text": text, "buttons": buttons}})
+ results.append(
+ await event.builder.article(
+ type="photo",
+ title=title,
+ description=desc,
+ thumb=file,
+ content=file,
+ text=text,
+ include_media=True,
+ buttons=buttons,
+ ),
+ )
+ await event.answer(results[:50])
+
+
+@callback(
+ re.compile(
+ "ytdl:(.*)",
+ ),
+ owner=True,
+)
+async def _(e):
+ _e = e.pattern_match.group(1).strip().decode("UTF-8")
+ _lets_split = _e.split(":")
+ _ytdl_data = await dler(e, _yt_base_url + _lets_split[1])
+ _data = get_formats(_lets_split[0], _lets_split[1], _ytdl_data)
+ _buttons = get_buttons(_data)
+ _text = (
+ "`Select Your Format.`"
+ if _buttons
+ else "`Error downloading from YouTube.\nTry Restarting your bot.`"
+ )
+
+ await e.edit(_text, buttons=_buttons)
+
+
+@callback(
+ re.compile(
+ "ytdownload:(.*)",
+ ),
+ owner=True,
+)
+async def _(event):
+ url = event.pattern_match.group(1).strip().decode("UTF-8")
+ lets_split = url.split(":")
+ vid_id = lets_split[2]
+ link = _yt_base_url + vid_id
+ format = lets_split[1]
+ try:
+ ext = lets_split[3]
+ except IndexError:
+ ext = "mp3"
+ if lets_split[0] == "audio":
+ opts = {
+ "format": "bestaudio",
+ "addmetadata": True,
+ "key": "FFmpegMetadata",
+ "prefer_ffmpeg": True,
+ "geo_bypass": True,
+ "outtmpl": f"%(id)s.{ext}",
+ "logtostderr": False,
+ "postprocessors": [
+ {
+ "key": "FFmpegExtractAudio",
+ "preferredcodec": ext,
+ "preferredquality": format,
+ },
+ {"key": "FFmpegMetadata"},
+ ],
+ }
+
+ ytdl_data = await dler(event, link, opts, True)
+ title = ytdl_data["title"]
+ if ytdl_data.get("artist"):
+ artist = ytdl_data["artist"]
+ elif ytdl_data.get("creator"):
+ artist = ytdl_data["creator"]
+ elif ytdl_data.get("channel"):
+ artist = ytdl_data["channel"]
+ views = numerize(ytdl_data.get("view_count")) or 0
+ thumb, _ = await fast_download(ytdl_data["thumbnail"], filename=f"{vid_id}.jpg")
+
+ likes = numerize(ytdl_data.get("like_count")) or 0
+ duration = ytdl_data.get("duration") or 0
+ description = (
+ ytdl_data["description"]
+ if len(ytdl_data["description"]) < 100
+ else ytdl_data["description"][:100]
+ )
+ description = description or "None"
+ filepath = f"{vid_id}.{ext}"
+ if not os.path.exists(filepath):
+ filepath = f"{filepath}.{ext}"
+ size = os.path.getsize(filepath)
+ file, _ = await event.client.fast_uploader(
+ filepath,
+ filename=f"{title}.{ext}",
+ show_progress=True,
+ event=event,
+ to_delete=True,
+ )
+
+ attributes = [
+ DocumentAttributeAudio(
+ duration=int(duration),
+ title=title,
+ performer=artist,
+ ),
+ ]
+ elif lets_split[0] == "video":
+ opts = {
+ "format": str(format),
+ "addmetadata": True,
+ "key": "FFmpegMetadata",
+ "prefer_ffmpeg": True,
+ "geo_bypass": True,
+ "outtmpl": f"%(id)s.{ext}",
+ "logtostderr": False,
+ "postprocessors": [{"key": "FFmpegMetadata"}],
+ }
+
+ ytdl_data = await dler(event, link, opts, True)
+ title = ytdl_data["title"]
+ if ytdl_data.get("artist"):
+ artist = ytdl_data["artist"]
+ elif ytdl_data.get("creator"):
+ artist = ytdl_data["creator"]
+ elif ytdl_data.get("channel"):
+ artist = ytdl_data["channel"]
+ views = numerize(ytdl_data.get("view_count")) or 0
+ thumb, _ = await fast_download(ytdl_data["thumbnail"], filename=f"{vid_id}.jpg")
+
+ try:
+ Image.open(thumb).save(thumb, "JPEG")
+ except Exception as er:
+ LOGS.exception(er)
+ thumb = None
+ description = (
+ ytdl_data["description"]
+ if len(ytdl_data["description"]) < 100
+ else ytdl_data["description"][:100]
+ )
+ likes = numerize(ytdl_data.get("like_count")) or 0
+ hi, wi = ytdl_data.get("height") or 720, ytdl_data.get("width") or 1280
+ duration = ytdl_data.get("duration") or 0
+ filepath = f"{vid_id}.mkv"
+ if not os.path.exists(filepath):
+ filepath = f"{filepath}.webm"
+ size = os.path.getsize(filepath)
+ file, _ = await event.client.fast_uploader(
+ filepath,
+ filename=f"{title}.mkv",
+ show_progress=True,
+ event=event,
+ to_delete=True,
+ )
+
+ attributes = [
+ DocumentAttributeVideo(
+ duration=int(duration),
+ w=wi,
+ h=hi,
+ supports_streaming=True,
+ ),
+ ]
+ description = description if description != "" else "None"
+ text = f"**Title: [{title}]({_yt_base_url}{vid_id})**\n\n"
+ text += f"`📝 Description: {description}\n\n"
+ text += f"「 Duration: {time_formatter(int(duration)*1000)} 」\n"
+ text += f"「 Artist: {artist} 」\n"
+ text += f"「 Views: {views} 」\n"
+ text += f"「 Likes: {likes} 」\n"
+ text += f"「 Size: {humanbytes(size)} 」`"
+ button = Button.switch_inline("Search More", query="yt ", same_peer=True)
+ try:
+ await event.edit(
+ text,
+ file=file,
+ buttons=button,
+ attributes=attributes,
+ thumb=thumb,
+ )
+ except (FilePartLengthInvalidError, MediaEmptyError):
+ file = await asst.send_message(
+ udB.get_key("LOG_CHANNEL"),
+ text,
+ file=file,
+ buttons=button,
+ attributes=attributes,
+ thumb=thumb,
+ )
+ await event.edit(text, file=file.media, buttons=button)
+ await bash(f"rm {vid_id}.jpg")
+
+
+@callback(re.compile("ytdl_back:(.*)"), owner=True)
+async def ytdl_back(event):
+ id_ = event.data_match.group(1).decode("utf-8")
+ if not BACK_BUTTON.get(id_):
+ return await event.answer("Query Expired! Search again 🔍")
+ await event.edit(**BACK_BUTTON[id_])
diff --git a/docker-compose.yml b/docker-compose.yml
new file mode 100644
index 0000000000000000000000000000000000000000..a35d69622415dcf1b34a253e18f6ec16fcf6d0cc
--- /dev/null
+++ b/docker-compose.yml
@@ -0,0 +1,14 @@
+services:
+ worker:
+ build: .
+ environment:
+ REDIS_URI: $REDIS_URI
+ REDIS_PASSWORD: $REDIS_PASSWORD
+ SESSION: $SESSION
+ API_ID: $API_ID # defaults to None
+ API_HASH: $API_HASH # defaults to None
+ MONGO_URI: $MONGO_URI # defaults to None
+ BOT_TOKEN: $BOT_TOKEN # Not mandatory
+ LOG_CHANNEL: $LOG_CHANNEL # Not mandatory
+ DATABASE_URL: $DATABASE_URL # defaults to None
+ OKTETO_TOKEN: $OKTETO_TOKEN
diff --git a/installer.sh b/installer.sh
new file mode 100644
index 0000000000000000000000000000000000000000..5c2b8f00f3a7532327adc6a64ed1571ff4e71030
--- /dev/null
+++ b/installer.sh
@@ -0,0 +1,241 @@
+#!/usr/bin/env bash
+
+REPO="https://github.com/TeamUltroid/Ultroid.git"
+CURRENT_DIR="$(pwd)"
+ENV_FILE_PATH=".env"
+DIR="/root/TeamUltroid"
+
+while [ $# -gt 0 ]; do
+ case "$1" in
+ --dir=*)
+ DIR="${1#*=}" || DIR="/root/TeamUltroid"
+ ;;
+ --branch=*)
+ BRANCH="${1#*=}" || BRANCH="main"
+ ;;
+ --env-file=*)
+ ENV_FILE_PATH="${1#*=}" || ENV_FILE_PATH=".env"
+ ;;
+ --no-root)
+ NO_ROOT=true
+ ;;
+ *)
+ echo "Unknown parameter passed: $1"
+ exit 1
+ ;;
+ esac
+ shift
+done
+
+check_dependencies() {
+ # check if debian
+ echo "Checking dependencies..."
+ # read file with root access
+ if ! [[ $(ls -l "/etc/sudoers" | cut -d " " -f1) =~ "r" ]]; then
+ # check dependencies if installed
+ echo -e "Root access not found. Checking if dependencies are installed." >&2
+ if ! [ -x "$(command -v python3)" ] || ! [ -x "$(command -v python)" ]; then
+ echo -e "Python3 isn't installed. Please install python3.8 or higher to run this bot." >&2
+ exit 1
+ fi
+ if [ $(python3 -c "import sys; print(sys.version_info[1])") -lt 8 ] || [ $(python -c "import sys; print(sys.version_info[1])") -lt 8 ]; then
+ echo -e "Python 3.8 or higher is required to run this bot." >&2
+ exit 1
+ fi
+ # check if any of ffmpeg, mediainfo, neofetch, git is not installed
+ if ! command -v ffmpeg &>/dev/null || ! command -v mediainfo &>/dev/null || ! command -v neofetch &>/dev/null || ! command -v git &>/dev/null; then
+ echo -e "Some dependencies aren't installed. Please install ffmpeg, mediainfo, neofetch and git to run this bot." >&2
+ exit 1
+ fi
+ fi
+ if [ -x "$(command -v apt-get)" ]; then
+ echo -e "Installing dependencies..."
+ # check if any of ffmpeg, mediainfo, neofetch, git is not installed via dpkg
+ if dpkg -l | grep -q ffmpeg || dpkg -l | grep -q mediainfo || dpkg -l | grep -q neofetch || dpkg -l | grep -q git; then
+ sudo apt-get -qq -o=Dpkg::Use-Pty=0 update
+ sudo apt-get install -qq -o=Dpkg::Use-Pty=0 python3 python3-pip ffmpeg mediainfo neofetch git -y
+ fi
+ elif [ -x "$(command -v pacman)" ]; then
+ echo -e "Installing dependencies..."
+ if pacman -Q | grep -q ffmpeg || pacman -Q | grep -q mediainfo || pacman -Q | grep -q neofetch || pacman -Q | grep -q git; then
+ sudo pacman -Sy python python-pip git ffmpeg mediainfo neofetch --noconfirm
+ fi
+ else
+ echo -e "Unknown OS. Checking if dependecies are installed" >&2
+ if ! [ -x "$(command -v python3)" ] || ! [ -x "$(command -v python)" ]; then
+ echo -e "Python3 isn't installed. Please install python3.8 or higher to run this bot." >&2
+ exit 1
+ fi
+ if [ $(python3 -c "import sys; print(sys.version_info[1])") -lt 8 ] || [ $(python -c "import sys; print(sys.version_info[1])") -lt 8 ]; then
+ echo -e "Python 3.8 or higher is required to run this bot." >&2
+ exit 1
+ fi
+ if ! command -v ffmpeg &>/dev/null || ! command -v mediainfo &>/dev/null || ! command -v neofetch &>/dev/null || ! command -v git &>/dev/null; then
+ echo -e "Some dependencies aren't installed. Please install ffmpeg, mediainfo, neofetch and git to run this bot." >&2
+ exit 1
+ fi
+ fi
+}
+
+check_python() {
+ # check if python is installed
+ if ! command -v python3 &>/dev/null; then
+ echo -e "Python3 isn't installed. Please install python3.8 or higher to run this bot."
+ exit 1
+ elif ! command -v python &>/dev/null; then
+ echo -e "Python3 isn't installed. Please install python3.8 or higher to run this bot."
+ exit 1
+ fi
+ if [ $(python3 -c "import sys; print(sys.version_info[1])") -lt 8 ]; then
+ echo -e "Python 3.8 or higher is required to run this bot."
+ exit 1
+ elif [ $(python -c "import sys; print(sys.version_info[1])") -lt 3 ]; then
+ if [ $(python -c "import sys; print(sys.version_info[1])") -lt 8 ]; then
+ echo -e "Python 3.8 or higher is required to run this bot."
+ exit 1
+ fi
+ fi
+}
+
+clone_repo() {
+ # check if pyultroid, startup, plugins folders exist
+ cd $DIR
+ if [ -d $DIR ]; then
+ if [ -d $DIR/.git ]; then
+ echo -e "Updating Ultroid ${BRANCH}... "
+ cd $DIR
+ git pull
+ currentbranch="$(git rev-parse --abbrev-ref HEAD)"
+ if [ ! $BRANCH ]; then
+ export BRANCH=$currentbranch
+ fi
+ case $currentbranch in
+ $BRANCH)
+ # do nothing
+ ;;
+ *)
+ echo -e "Switching to branch ${BRANCH}... "
+ echo -e $currentbranch
+ git checkout $BRANCH
+ ;;
+ esac
+ else
+ rm -rf $DIR
+ exit 1
+ fi
+ if [ -d "addons" ]; then
+ cd addons
+ git pull
+ fi
+ return
+ else
+ if [ ! $BRANCH ]; then
+ export BRANCH="main"
+ fi
+ mkdir -p $DIR
+ echo -e "Cloning Ultroid ${BRANCH}... "
+ git clone -b $BRANCH $REPO $DIR
+ fi
+}
+
+install_requirements() {
+ pip3 install -q --upgrade pip
+ echo -e "\n\nInstalling requirements... "
+ pip3 install -q --no-cache-dir -r $DIR/requirements.txt
+ pip3 install -q -r $DIR/resources/startup/optional-requirements.txt
+}
+
+railways_dep() {
+ if [ $RAILWAY_STATIC_URL ]; then
+ echo -e "Installing YouTube dependency... "
+ pip3 install -q yt-dlp
+ fi
+}
+
+misc_install() {
+ if [ $SETUP_PLAYWRIGHT ]
+ then
+ echo -e "Installing playwright."
+ pip3 install playwright
+ playwright install
+ fi
+ if [ $OKTETO_TOKEN ]; then
+ echo -e "Installing Okteto-CLI... "
+ curl https://get.okteto.com -sSfL | sh
+ elif [ $VCBOT ]; then
+ if [ -d $DIR/vcbot ]; then
+ cd $DIR/vcbot
+ git pull
+ else
+ echo -e "Cloning VCBOT.."
+ git clone https://github.com/TeamUltroid/VcBot $DIR/vcbot
+ fi
+ pip3 install pytgcalls==3.0.0.dev23 && pip3 install av -q --no-binary av
+ fi
+}
+
+dep_install() {
+ echo -e "\n\nInstalling DB Requirement..."
+ if [ $MONGO_URI ]; then
+ echo -e " Installing MongoDB Requirements..."
+ pip3 install -q pymongo[srv]
+ elif [ $DATABASE_URL ]; then
+ echo -e " Installing PostgreSQL Requirements..."
+ pip3 install -q psycopg2-binary
+ elif [ $REDIS_URI ]; then
+ echo -e " Installing Redis Requirements..."
+ pip3 install -q redis hiredis
+ fi
+}
+
+main() {
+ echo -e "Starting Ultroid Setup..."
+ if [ -d "pyUltroid" ] && [ -d "resources" ] && [ -d "plugins" ]; then
+ DIR=$CURRENT_DIR
+ fi
+ if [ -f $ENV_FILE_PATH ]
+ then
+ set -a
+ source <(cat $ENV_FILE_PATH | sed -e '/^#/d;/^\s*$/d' -e "s/'/'\\\''/g" -e "s/=\(.*\)/='\1'/g")
+ set +a
+ cp $ENV_FILE_PATH .env
+ fi
+ (check_dependencies)
+ (check_python)
+ (clone_repo)
+ (install_requirements)
+ (railways_dep)
+ (dep_install)
+ (misc_install)
+ echo -e "\n\nSetup Completed."
+}
+
+if [ $NO_ROOT ]; then
+ echo -e "Running with non root"
+ main
+ return 0
+elif [ -t 0 ]; then
+ unameOut="$(uname -s)"
+ case "${unameOut}" in
+ Linux*) machine=Linux;;
+ Darwin*) machine=Mac;;
+ CYGWIN*) machine=Cygwin;;
+ MINGW*) machine=MinGw;;
+ *) machine="UNKNOWN:${unameOut}"
+ esac
+ if machine != "Linux"; then
+ echo -e "This script is only for Linux. Please use the Windows installer."
+ exit 1
+ fi
+ # check if sudo is installed
+ if ! command -v sudo &>/dev/null; then
+ echo -e "Sudo isn't installed. Please install sudo to run this bot."
+ exit 1
+ fi
+ sudo echo "Sudo permission granted."
+ main
+else
+ echo "Not an interactive terminal, skipping sudo."
+ # run main function
+ main
+fi
diff --git a/plugins/__init__.py b/plugins/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..82ef3187a45a4100014444ce48b56c98335f7b3a
--- /dev/null
+++ b/plugins/__init__.py
@@ -0,0 +1,105 @@
+# Ultroid - UserBot
+# Copyright (C) 2021-2025 TeamUltroid
+#
+# This file is a part of < https://github.com/TeamUltroid/Ultroid/ >
+# PLease read the GNU Affero General Public License in
+# .
+
+import asyncio
+import os
+import time
+from random import choice
+
+import requests
+from telethon import Button, events
+from telethon.tl import functions, types # pylint:ignore
+
+from pyUltroid import *
+from pyUltroid._misc._assistant import asst_cmd, callback, in_pattern
+from pyUltroid._misc._decorators import ultroid_cmd
+from pyUltroid._misc._wrappers import eod, eor
+from pyUltroid.dB import DEVLIST, ULTROID_IMAGES
+from pyUltroid.fns.helper import *
+from pyUltroid.fns.misc import *
+from pyUltroid.fns.tools import *
+from pyUltroid.startup._database import _BaseDatabase as Database
+from pyUltroid.version import __version__, ultroid_version
+from strings import get_help, get_string
+from catbox import CatboxUploader
+
+udB: Database
+
+Redis = udB.get_key
+con = TgConverter
+quotly = Quotly()
+OWNER_NAME = ultroid_bot.full_name
+OWNER_ID = ultroid_bot.uid
+
+ultroid_bot: UltroidClient
+asst: UltroidClient
+
+LOG_CHANNEL = udB.get_key("LOG_CHANNEL")
+
+
+def inline_pic():
+ INLINE_PIC = udB.get_key("INLINE_PIC")
+ if INLINE_PIC is None:
+ INLINE_PIC = choice(ULTROID_IMAGES)
+ elif INLINE_PIC == False:
+ INLINE_PIC = None
+ return INLINE_PIC
+
+
+Telegraph = telegraph_client()
+cat_uploader = CatboxUploader()
+
+upload_file = cat_uploader.upload_file
+
+List = []
+Dict = {}
+InlinePlugin = {}
+N = 0
+cmd = ultroid_cmd
+STUFF = {}
+
+# Chats, which needs to be ignore for some cases
+# Considerably, there can be many
+# Feel Free to Add Any other...
+
+NOSPAM_CHAT = [
+ -1001361294038, # UltroidSupportChat
+ -1001387666944, # PyrogramChat
+ -1001109500936, # TelethonChat
+ -1001050982793, # Python
+ -1001256902287, # DurovsChat
+ -1001473548283, # SharingUserbot
+]
+
+KANGING_STR = [
+ "Using Witchery to kang this sticker...",
+ "Plagiarising hehe...",
+ "Inviting this sticker over to my pack...",
+ "Kanging this sticker...",
+ "Hey that's a nice sticker!\nMind if I kang?!..",
+ "Hehe me stel ur stiker...",
+ "Ay look over there (☉。☉)!→\nWhile I kang this...",
+ "Roses are red violets are blue, kanging this sticker so my pack looks cool",
+ "Imprisoning this sticker...",
+ "Mr.Steal-Your-Sticker is stealing this sticker... ",
+]
+
+
+ATRA_COL = [
+ "DarkCyan",
+ "DeepSkyBlue",
+ "DarkTurquoise",
+ "Cyan",
+ "LightSkyBlue",
+ "Turquoise",
+ "MediumVioletRed",
+ "Aquamarine",
+ "Lightcyan",
+ "Azure",
+ "Moccasin",
+ "PowderBlue",
+]
diff --git a/plugins/_chatactions.py b/plugins/_chatactions.py
new file mode 100644
index 0000000000000000000000000000000000000000..3ae1c901117322793bc842925d1b1e2224650b93
--- /dev/null
+++ b/plugins/_chatactions.py
@@ -0,0 +1,253 @@
+# Ultroid - UserBot
+# Copyright (C) 2021-2025 TeamUltroid
+#
+# This file is a part of < https://github.com/TeamUltroid/Ultroid/ >
+# PLease read the GNU Affero General Public License in
+# .
+
+import asyncio
+
+from telethon import events
+from telethon.errors.rpcerrorlist import UserNotParticipantError
+from telethon.tl.functions.channels import GetParticipantRequest
+from telethon.utils import get_display_name
+
+from pyUltroid.dB import stickers
+from pyUltroid.dB.echo_db import check_echo
+from pyUltroid.dB.forcesub_db import get_forcesetting
+from pyUltroid.dB.gban_mute_db import is_gbanned
+from pyUltroid.dB.greetings_db import get_goodbye, get_welcome, must_thank
+from pyUltroid.dB.nsfw_db import is_profan
+from pyUltroid.fns.helper import inline_mention
+from pyUltroid.fns.tools import async_searcher, create_tl_btn, get_chatbot_reply
+
+try:
+ from ProfanityDetector import detector
+except ImportError:
+ detector = None
+from . import LOG_CHANNEL, LOGS, asst, get_string, types, udB, ultroid_bot
+from ._inline import something
+
+
+@ultroid_bot.on(events.ChatAction())
+async def Function(event):
+ try:
+ await DummyHandler(event)
+ except Exception as er:
+ LOGS.exception(er)
+
+
+async def DummyHandler(ult):
+ # clean chat actions
+ key = udB.get_key("CLEANCHAT") or []
+ if ult.chat_id in key:
+ try:
+ await ult.delete()
+ except BaseException:
+ pass
+
+ # thank members
+ if must_thank(ult.chat_id):
+ chat_count = (await ult.client.get_participants(ult.chat_id, limit=0)).total
+ if chat_count % 100 == 0:
+ stik_id = chat_count / 100 - 1
+ sticker = stickers[stik_id]
+ await ult.respond(file=sticker)
+ # force subscribe
+ if (
+ udB.get_key("FORCESUB")
+ and ((ult.user_joined or ult.user_added))
+ and get_forcesetting(ult.chat_id)
+ ):
+ user = await ult.get_user()
+ if not user.bot:
+ joinchat = get_forcesetting(ult.chat_id)
+ try:
+ await ultroid_bot(GetParticipantRequest(int(joinchat), user.id))
+ except UserNotParticipantError:
+ await ultroid_bot.edit_permissions(
+ ult.chat_id, user.id, send_messages=False
+ )
+ res = await ultroid_bot.inline_query(
+ asst.me.username, f"fsub {user.id}_{joinchat}"
+ )
+ await res[0].click(ult.chat_id, reply_to=ult.action_message.id)
+
+ if ult.user_joined or ult.added_by:
+ user = await ult.get_user()
+ chat = await ult.get_chat()
+ # gbans and @UltroidBans checks
+ if udB.get_key("ULTROID_BANS"):
+ try:
+ is_banned = await async_searcher(
+ "https://bans.ultroid.tech/api/status",
+ json={"userId": user.id},
+ post=True,
+ re_json=True,
+ )
+ if is_banned["is_banned"]:
+ await ult.client.edit_permissions(
+ chat.id,
+ user.id,
+ view_messages=False,
+ )
+ await ult.respond(
+ f'**@UltroidBans:** Banned user detected and banned!\n`{str(is_banned)}`.\nBan reason: {is_banned["reason"]}',
+ )
+
+ except BaseException:
+ pass
+ reason = is_gbanned(user.id)
+ if reason and chat.admin_rights:
+ try:
+ await ult.client.edit_permissions(
+ chat.id,
+ user.id,
+ view_messages=False,
+ )
+ gban_watch = get_string("can_1").format(inline_mention(user), reason)
+ await ult.reply(gban_watch)
+ except Exception as er:
+ LOGS.exception(er)
+
+ # greetings
+ elif get_welcome(ult.chat_id):
+ user = await ult.get_user()
+ chat = await ult.get_chat()
+ title = chat.title or "this chat"
+ count = (
+ chat.participants_count
+ or (await ult.client.get_participants(chat, limit=0)).total
+ )
+ mention = inline_mention(user)
+ name = user.first_name
+ fullname = get_display_name(user)
+ uu = user.username
+ username = f"@{uu}" if uu else mention
+ wel = get_welcome(ult.chat_id)
+ msgg = wel["welcome"]
+ med = wel["media"] or None
+ userid = user.id
+ msg = None
+ if msgg:
+ msg = msgg.format(
+ mention=mention,
+ group=title,
+ count=count,
+ name=name,
+ fullname=fullname,
+ username=username,
+ userid=userid,
+ )
+ if wel.get("button"):
+ btn = create_tl_btn(wel["button"])
+ await something(ult, msg, med, btn)
+ elif msg:
+ send = await ult.reply(
+ msg,
+ file=med,
+ )
+ await asyncio.sleep(150)
+ await send.delete()
+ else:
+ await ult.reply(file=med)
+ elif (ult.user_left or ult.user_kicked) and get_goodbye(ult.chat_id):
+ user = await ult.get_user()
+ chat = await ult.get_chat()
+ title = chat.title or "this chat"
+ count = (
+ chat.participants_count
+ or (await ult.client.get_participants(chat, limit=0)).total
+ )
+ mention = inline_mention(user)
+ name = user.first_name
+ fullname = get_display_name(user)
+ uu = user.username
+ username = f"@{uu}" if uu else mention
+ wel = get_goodbye(ult.chat_id)
+ msgg = wel["goodbye"]
+ med = wel["media"]
+ userid = user.id
+ msg = None
+ if msgg:
+ msg = msgg.format(
+ mention=mention,
+ group=title,
+ count=count,
+ name=name,
+ fullname=fullname,
+ username=username,
+ userid=userid,
+ )
+ if wel.get("button"):
+ btn = create_tl_btn(wel["button"])
+ await something(ult, msg, med, btn)
+ elif msg:
+ send = await ult.reply(
+ msg,
+ file=med,
+ )
+ await asyncio.sleep(150)
+ await send.delete()
+ else:
+ await ult.reply(file=med)
+
+
+@ultroid_bot.on(events.NewMessage(incoming=True))
+async def chatBot_replies(e):
+ sender = await e.get_sender()
+ if not isinstance(sender, types.User) or sender.bot:
+ return
+ if check_echo(e.chat_id, e.sender_id):
+ try:
+ await e.respond(e.message)
+ except Exception as er:
+ LOGS.exception(er)
+ key = udB.get_key("CHATBOT_USERS") or {}
+ if e.text and key.get(e.chat_id) and sender.id in key[e.chat_id]:
+ msg = await get_chatbot_reply(e.message.message)
+ if msg:
+ sleep = udB.get_key("CHATBOT_SLEEP") or 1.5
+ await asyncio.sleep(sleep)
+ await e.reply(msg)
+ chat = await e.get_chat()
+ if e.is_group and sender.username:
+ await uname_stuff(e.sender_id, sender.username, sender.first_name)
+ elif e.is_private and chat.username:
+ await uname_stuff(e.sender_id, chat.username, chat.first_name)
+ if detector and is_profan(e.chat_id) and e.text:
+ x, y = detector(e.text)
+ if y:
+ await e.delete()
+
+
+@ultroid_bot.on(events.Raw(types.UpdateUserName))
+async def uname_change(e):
+ await uname_stuff(e.user_id, e.usernames[0] if e.usernames else None, e.first_name)
+
+
+async def uname_stuff(id, uname, name):
+ if udB.get_key("USERNAME_LOG"):
+ old_ = udB.get_key("USERNAME_DB") or {}
+ old = old_.get(id)
+ # Ignore Name Logs
+ if old and old == uname:
+ return
+ if old and uname:
+ await asst.send_message(
+ LOG_CHANNEL,
+ get_string("can_2").format(old, uname),
+ )
+ elif old:
+ await asst.send_message(
+ LOG_CHANNEL,
+ get_string("can_3").format(f"[{name}](tg://user?id={id})", old),
+ )
+ elif uname:
+ await asst.send_message(
+ LOG_CHANNEL,
+ get_string("can_4").format(f"[{name}](tg://user?id={id})", uname),
+ )
+
+ old_[id] = uname
+ udB.set_key("USERNAME_DB", old_)
diff --git a/plugins/_help.py b/plugins/_help.py
new file mode 100644
index 0000000000000000000000000000000000000000..1f7d4e6e5298178f96a99708993b25f7ce60cb6a
--- /dev/null
+++ b/plugins/_help.py
@@ -0,0 +1,136 @@
+# Ultroid - UserBot
+# Copyright (C) 2021-2025 TeamUltroid
+#
+# This file is a part of < https://github.com/TeamUltroid/Ultroid/ >
+# PLease read the GNU Affero General Public License in
+# .
+
+from telethon.errors.rpcerrorlist import (
+ BotInlineDisabledError,
+ BotMethodInvalidError,
+ BotResponseTimeoutError,
+)
+from telethon.tl.custom import Button
+
+from pyUltroid.dB._core import HELP, LIST
+from pyUltroid.fns.tools import cmd_regex_replace
+
+from . import HNDLR, LOGS, OWNER_NAME, asst, get_string, inline_pic, udB, ultroid_cmd
+
+_main_help_menu = [
+ [
+ Button.inline(get_string("help_4"), data="uh_Official_"),
+ Button.inline(get_string("help_5"), data="uh_Addons_"),
+ ],
+ [
+ Button.inline(get_string("help_6"), data="uh_VCBot_"),
+ Button.inline(get_string("help_7"), data="inlone"),
+ ],
+ [
+ Button.inline(get_string("help_8"), data="ownr"),
+ Button.url(
+ get_string("help_9"), url=f"https://t.me/{asst.me.username}?start=set"
+ ),
+ ],
+ [Button.inline(get_string("help_10"), data="close")],
+]
+
+
+@ultroid_cmd(pattern="help( (.*)|$)")
+async def _help(ult):
+ plug = ult.pattern_match.group(1).strip()
+ chat = await ult.get_chat()
+ if plug:
+ try:
+ if plug in HELP["Official"]:
+ output = f"**Plugin** - `{plug}`\n"
+ for i in HELP["Official"][plug]:
+ output += i
+ output += "\n© @TeamUltroid"
+ await ult.eor(output)
+ elif HELP.get("Addons") and plug in HELP["Addons"]:
+ output = f"**Plugin** - `{plug}`\n"
+ for i in HELP["Addons"][plug]:
+ output += i
+ output += "\n© @TeamUltroid"
+ await ult.eor(output)
+ elif HELP.get("VCBot") and plug in HELP["VCBot"]:
+ output = f"**Plugin** - `{plug}`\n"
+ for i in HELP["VCBot"][plug]:
+ output += i
+ output += "\n© @TeamUltroid"
+ await ult.eor(output)
+ else:
+ try:
+ x = get_string("help_11").format(plug)
+ for d in LIST[plug]:
+ x += HNDLR + d
+ x += "\n"
+ x += "\n© @TeamUltroid"
+ await ult.eor(x)
+ except BaseException:
+ file = None
+ compare_strings = []
+ for file_name in LIST:
+ compare_strings.append(file_name)
+ value = LIST[file_name]
+ for j in value:
+ j = cmd_regex_replace(j)
+ compare_strings.append(j)
+ if j.strip() == plug:
+ file = file_name
+ break
+ if not file:
+ # the enter command/plugin name is not found
+ text = f"`{plug}` is not a valid plugin!"
+ best_match = None
+ for _ in compare_strings:
+ if plug in _ and not _.startswith("_"):
+ best_match = _
+ break
+ if best_match:
+ text += f"\nDid you mean `{best_match}`?"
+ return await ult.eor(text)
+ output = f"**Command** `{plug}` **found in plugin** - `{file}`\n"
+ if file in HELP["Official"]:
+ for i in HELP["Official"][file]:
+ output += i
+ elif HELP.get("Addons") and file in HELP["Addons"]:
+ for i in HELP["Addons"][file]:
+ output += i
+ elif HELP.get("VCBot") and file in HELP["VCBot"]:
+ for i in HELP["VCBot"][file]:
+ output += i
+ output += "\n© @TeamUltroid"
+ await ult.eor(output)
+ except BaseException as er:
+ LOGS.exception(er)
+ await ult.eor("Error 🤔 occured.")
+ else:
+ try:
+ results = await ult.client.inline_query(asst.me.username, "ultd")
+ except BotMethodInvalidError:
+ z = []
+ for x in LIST.values():
+ z.extend(x)
+ cmd = len(z) + 10
+ if udB.get_key("MANAGER") and udB.get_key("DUAL_HNDLR") == "/":
+ _main_help_menu[2:3] = [[Button.inline("• Manager Help •", "mngbtn")]]
+ return await ult.reply(
+ get_string("inline_4").format(
+ OWNER_NAME,
+ len(HELP["Official"]),
+ len(HELP["Addons"] if "Addons" in HELP else []),
+ cmd,
+ ),
+ file=inline_pic(),
+ buttons=_main_help_menu,
+ )
+ except BotResponseTimeoutError:
+ return await ult.eor(
+ get_string("help_2").format(HNDLR),
+ )
+ except BotInlineDisabledError:
+ return await ult.eor(get_string("help_3"))
+ await results[0].click(chat.id, reply_to=ult.reply_to_msg_id, hide_via=True)
+ await ult.delete()
diff --git a/plugins/_inline.py b/plugins/_inline.py
new file mode 100644
index 0000000000000000000000000000000000000000..211e723ffe809e1cba022506d63f01876b2828c6
--- /dev/null
+++ b/plugins/_inline.py
@@ -0,0 +1,455 @@
+# Ultroid - UserBot
+# Copyright (C) 2021-2025 TeamUltroid
+#
+# This file is a part of < https://github.com/TeamUltroid/Ultroid/ >
+# PLease read the GNU Affero General Public License in
+# .
+
+import re
+import time
+from datetime import datetime
+from os import remove
+
+from git import Repo
+from telethon import Button
+from telethon.tl.types import InputWebDocument, Message
+from telethon.utils import resolve_bot_file_id
+
+from pyUltroid._misc._assistant import callback, in_pattern
+from pyUltroid.dB._core import HELP, LIST
+from pyUltroid.fns.helper import gen_chlog, time_formatter, updater
+from pyUltroid.fns.misc import split_list
+
+from . import (
+ HNDLR,
+ LOGS,
+ OWNER_NAME,
+ InlinePlugin,
+ asst,
+ get_string,
+ inline_pic,
+ split_list,
+ start_time,
+ udB,
+)
+from ._help import _main_help_menu
+
+# ================================================#
+
+helps = get_string("inline_1")
+
+add_ons = udB.get_key("ADDONS")
+
+zhelps = get_string("inline_3") if add_ons is False else get_string("inline_2")
+PLUGINS = HELP.get("Official", [])
+ADDONS = HELP.get("Addons", [])
+upage = 0
+# ============================================#
+
+# --------------------BUTTONS--------------------#
+
+SUP_BUTTONS = [
+ [
+ Button.url("• Repo •", url="https://github.com/TeamUltroid/Ultroid"),
+ Button.url("• Support •", url="t.me/UltroidSupportChat"),
+ ],
+]
+
+# --------------------BUTTONS--------------------#
+
+
+@in_pattern(owner=True, func=lambda x: not x.text)
+async def inline_alive(o):
+ TLINK = inline_pic() or "https://graph.org/file/74d6259983e0642923fdb.jpg"
+ MSG = "• **Ultroid Userbot •**"
+ WEB0 = InputWebDocument(
+ "https://graph.org/file/acd4f5d61369f74c5e7a7.jpg", 0, "image/jpg", []
+ )
+ RES = [
+ await o.builder.article(
+ type="photo",
+ text=MSG,
+ include_media=True,
+ buttons=SUP_BUTTONS,
+ title="Ultroid Userbot",
+ description="Userbot | Telethon",
+ url=TLINK,
+ thumb=WEB0,
+ content=InputWebDocument(TLINK, 0, "image/jpg", []),
+ )
+ ]
+ await o.answer(
+ RES,
+ private=True,
+ cache_time=300,
+ switch_pm="👥 ULTROID PORTAL",
+ switch_pm_param="start",
+ )
+
+
+@in_pattern("ultd", owner=True)
+async def inline_handler(event):
+ z = []
+ for x in LIST.values():
+ z.extend(x)
+ text = get_string("inline_4").format(
+ OWNER_NAME,
+ len(HELP.get("Official", [])),
+ len(HELP.get("Addons", [])),
+ len(z),
+ )
+ if inline_pic():
+ result = await event.builder.photo(
+ file=inline_pic(),
+ link_preview=False,
+ text=text,
+ buttons=_main_help_menu,
+ )
+ else:
+ result = await event.builder.article(
+ title="Ultroid Help Menu", text=text, buttons=_main_help_menu
+ )
+ await event.answer([result], private=True, cache_time=300, gallery=True)
+
+
+@in_pattern("pasta", owner=True)
+async def _(event):
+ ok = event.text.split("-")[1]
+ if not ok.startswith("http"):
+ link = f"https://spaceb.in/{ok}"
+ raw = f"https://spaceb.in/api/v1/documents/{ok}/raw"
+ else:
+ link = ok
+ raw = f"{ok}/raw"
+ result = await event.builder.article(
+ title="Paste",
+ text="Pasted to Spacebin 🌌",
+ buttons=[
+ [
+ Button.url("SpaceBin", url=link),
+ Button.url("Raw", url=raw),
+ ],
+ ],
+ )
+ await event.answer([result])
+
+
+@callback("ownr", owner=True)
+async def setting(event):
+ z = []
+ for x in LIST.values():
+ z.extend(x)
+ await event.edit(
+ get_string("inline_4").format(
+ OWNER_NAME,
+ len(HELP.get("Official", [])),
+ len(HELP.get("Addons", [])),
+ len(z),
+ ),
+ file=inline_pic(),
+ link_preview=False,
+ buttons=[
+ [
+ Button.inline("•Pɪɴɢ•", data="pkng"),
+ Button.inline("•Uᴘᴛɪᴍᴇ•", data="upp"),
+ ],
+ [
+ Button.inline("•Stats•", data="alive"),
+ Button.inline("•Uᴘᴅᴀᴛᴇ•", data="doupdate"),
+ ],
+ [Button.inline("« Bᴀᴄᴋ", data="open")],
+ ],
+ )
+
+
+_strings = {"Official": helps, "Addons": zhelps, "VCBot": get_string("inline_6")}
+
+
+@callback(re.compile("uh_(.*)"), owner=True)
+async def help_func(ult):
+ key, count = ult.data_match.group(1).decode("utf-8").split("_")
+ if key == "VCBot" and HELP.get("VCBot") is None:
+ return await ult.answer(get_string("help_12"), alert=True)
+ elif key == "Addons" and HELP.get("Addons") is None:
+ return await ult.answer(get_string("help_13").format(HNDLR), alert=True)
+ if "|" in count:
+ _, count = count.split("|")
+ count = int(count) if count else 0
+ text = _strings.get(key, "").format(OWNER_NAME, len(HELP.get(key)))
+ await ult.edit(text, buttons=page_num(count, key), link_preview=False)
+
+
+@callback(re.compile("uplugin_(.*)"), owner=True)
+async def uptd_plugin(event):
+ key, file = event.data_match.group(1).decode("utf-8").split("_")
+ index = None
+ if "|" in file:
+ file, index = file.split("|")
+ key_ = HELP.get(key, [])
+ hel_p = f"Plugin Name - `{file}`\n"
+ help_ = ""
+ try:
+ for i in key_[file]:
+ help_ += i
+ except BaseException:
+ if file in LIST:
+ help_ = get_string("help_11").format(file)
+ for d in LIST[file]:
+ help_ += HNDLR + d
+ help_ += "\n"
+ if not help_:
+ help_ = f"{file} has no Detailed Help!"
+ help_ += "\n© @TeamUltroid"
+ buttons = []
+ if inline_pic():
+ data = f"sndplug_{key}_{file}"
+ if index is not None:
+ data += f"|{index}"
+ buttons.append(
+ [
+ Button.inline(
+ "« Sᴇɴᴅ Pʟᴜɢɪɴ »",
+ data=data,
+ )
+ ]
+ )
+ data = f"uh_{key}_"
+ if index is not None:
+ data += f"|{index}"
+ buttons.append(
+ [
+ Button.inline("« Bᴀᴄᴋ", data=data),
+ ]
+ )
+ try:
+ await event.edit(help_, buttons=buttons)
+ except Exception as er:
+ LOGS.exception(er)
+ help = f"Do `{HNDLR}help {key}` to get list of commands."
+ await event.edit(help, buttons=buttons)
+
+
+@callback(data="doupdate", owner=True)
+async def _(event):
+ if not await updater():
+ return await event.answer(get_string("inline_9"), cache_time=0, alert=True)
+ if not inline_pic():
+ return await event.answer(f"Do '{HNDLR}update' to update..")
+ repo = Repo.init()
+ changelog, tl_chnglog = await gen_chlog(
+ repo, f"HEAD..upstream/{repo.active_branch}"
+ )
+ changelog_str = changelog + "\n\n" + get_string("inline_8")
+ if len(changelog_str) > 1024:
+ await event.edit(get_string("upd_4"))
+ with open("ultroid_updates.txt", "w+") as file:
+ file.write(tl_chnglog)
+ await event.edit(
+ get_string("upd_5"),
+ file="ultroid_updates.txt",
+ buttons=[
+ [Button.inline("• Uᴘᴅᴀᴛᴇ Nᴏᴡ •", data="updatenow")],
+ [Button.inline("« Bᴀᴄᴋ", data="ownr")],
+ ],
+ )
+ remove("ultroid_updates.txt")
+ else:
+ await event.edit(
+ changelog_str,
+ buttons=[
+ [Button.inline("Update Now", data="updatenow")],
+ [Button.inline("« Bᴀᴄᴋ", data="ownr")],
+ ],
+ parse_mode="html",
+ )
+
+
+@callback(data="pkng", owner=True)
+async def _(event):
+ start = datetime.now()
+ end = datetime.now()
+ ms = (end - start).microseconds
+ pin = f"🌋Pɪɴɢ = {ms} microseconds"
+ await event.answer(pin, cache_time=0, alert=True)
+
+
+@callback(data="upp", owner=True)
+async def _(event):
+ uptime = time_formatter((time.time() - start_time) * 1000)
+ pin = f"🙋Uᴘᴛɪᴍᴇ = {uptime}"
+ await event.answer(pin, cache_time=0, alert=True)
+
+
+@callback(data="inlone", owner=True)
+async def _(e):
+ _InButtons = [
+ Button.switch_inline(_, query=InlinePlugin[_], same_peer=True)
+ for _ in list(InlinePlugin.keys())
+ ]
+ InButtons = split_list(_InButtons, 2)
+
+ button = InButtons.copy()
+ button.append(
+ [
+ Button.inline("« Bᴀᴄᴋ", data="open"),
+ ],
+ )
+ await e.edit(buttons=button, link_preview=False)
+
+
+@callback(data="open", owner=True)
+async def opner(event):
+ z = []
+ for x in LIST.values():
+ z.extend(x)
+ await event.edit(
+ get_string("inline_4").format(
+ OWNER_NAME,
+ len(HELP.get("Official", [])),
+ len(HELP.get("Addons", [])),
+ len(z),
+ ),
+ buttons=_main_help_menu,
+ link_preview=False,
+ )
+
+
+@callback(data="close", owner=True)
+async def on_plug_in_callback_query_handler(event):
+ await event.edit(
+ get_string("inline_5"),
+ buttons=Button.inline("Oᴘᴇɴ Aɢᴀɪɴ", data="open"),
+ )
+
+
+def page_num(index, key):
+ rows = udB.get_key("HELP_ROWS") or 5
+ cols = udB.get_key("HELP_COLUMNS") or 2
+ loaded = HELP.get(key, [])
+ emoji = udB.get_key("EMOJI_IN_HELP") or "✘"
+ List = [
+ Button.inline(f"{emoji} {x} {emoji}", data=f"uplugin_{key}_{x}|{index}")
+ for x in sorted(loaded)
+ ]
+ all_ = split_list(List, cols)
+ fl_ = split_list(all_, rows)
+ try:
+ new_ = fl_[index]
+ except IndexError:
+ new_ = fl_[0] if fl_ else []
+ index = 0
+ if index == 0 and len(fl_) == 1:
+ new_.append([Button.inline("« Bᴀᴄᴋ »", data="open")])
+ else:
+ new_.append(
+ [
+ Button.inline(
+ "« Pʀᴇᴠɪᴏᴜs",
+ data=f"uh_{key}_{index-1}",
+ ),
+ Button.inline("« Bᴀᴄᴋ »", data="open"),
+ Button.inline(
+ "Nᴇxᴛ »",
+ data=f"uh_{key}_{index+1}",
+ ),
+ ]
+ )
+ return new_
+
+
+# --------------------------------------------------------------------------------- #
+
+
+STUFF = {}
+
+
+@in_pattern("stf(.*)", owner=True)
+async def ibuild(e):
+ n = e.pattern_match.group(1).strip()
+ builder = e.builder
+ if not (n and n.isdigit()):
+ return
+ ok = STUFF.get(int(n))
+ txt = ok.get("msg")
+ pic = ok.get("media")
+ btn = ok.get("button")
+ if not (pic or txt):
+ txt = "Hey!"
+ if pic:
+ try:
+ include_media = True
+ mime_type, _pic = None, None
+ cont, results = None, None
+ try:
+ ext = str(pic).split(".")[-1].lower()
+ except BaseException:
+ ext = None
+ if ext in ["img", "jpg", "png"]:
+ _type = "photo"
+ mime_type = "image/jpg"
+ elif ext in ["mp4", "mkv", "gif"]:
+ mime_type = "video/mp4"
+ _type = "gif"
+ else:
+ try:
+ if "telethon.tl.types" in str(type(pic)):
+ _pic = pic
+ else:
+ _pic = resolve_bot_file_id(pic)
+ except BaseException:
+ pass
+ if _pic:
+ results = [
+ await builder.document(
+ _pic,
+ title="Ultroid Op",
+ text=txt,
+ description="@TeamUltroid",
+ buttons=btn,
+ link_preview=False,
+ )
+ ]
+ else:
+ _type = "article"
+ include_media = False
+ if not results:
+ if include_media:
+ cont = InputWebDocument(pic, 0, mime_type, [])
+ results = [
+ await builder.article(
+ title="Ultroid Op",
+ type=_type,
+ text=txt,
+ description="@TeamUltroid",
+ include_media=include_media,
+ buttons=btn,
+ thumb=cont,
+ content=cont,
+ link_preview=False,
+ )
+ ]
+ return await e.answer(results)
+ except Exception as er:
+ LOGS.exception(er)
+ result = [
+ await builder.article("Ultroid Op", text=txt, link_preview=False, buttons=btn)
+ ]
+ await e.answer(result)
+
+
+async def something(e, msg, media, button, reply=True, chat=None):
+ if e.client._bot:
+ return await e.reply(msg, file=media, buttons=button)
+ num = len(STUFF) + 1
+ STUFF.update({num: {"msg": msg, "media": media, "button": button}})
+ try:
+ res = await e.client.inline_query(asst.me.username, f"stf{num}")
+ return await res[0].click(
+ chat or e.chat_id,
+ reply_to=bool(isinstance(e, Message) and reply),
+ hide_via=True,
+ silent=True,
+ )
+
+ except Exception as er:
+ LOGS.exception(er)
diff --git a/plugins/_ultroid.py b/plugins/_ultroid.py
new file mode 100644
index 0000000000000000000000000000000000000000..3d7261a3e7eb97e25861c0a483ad3e02b96c37dc
--- /dev/null
+++ b/plugins/_ultroid.py
@@ -0,0 +1,66 @@
+# Ultroid - UserBot
+# Copyright (C) 2021-2025 TeamUltroid
+#
+# This file is a part of < https://github.com/TeamUltroid/Ultroid/ >
+# PLease read the GNU Affero General Public License in
+# .
+
+from telethon.errors import (
+ BotMethodInvalidError,
+ ChatSendInlineForbiddenError,
+ ChatSendMediaForbiddenError,
+)
+
+from . import LOG_CHANNEL, LOGS, Button, asst, eor, get_string, ultroid_cmd
+
+REPOMSG = """
+• **ULTROID USERBOT** •\n
+• Repo - [Click Here](https://github.com/TeamUltroid/Ultroid)
+• Addons - [Click Here](https://github.com/TeamUltroid/UltroidAddons)
+• Support - @UltroidSupportChat
+"""
+
+RP_BUTTONS = [
+ [
+ Button.url(get_string("bot_3"), "https://github.com/TeamUltroid/Ultroid"),
+ Button.url("Addons", "https://github.com/TeamUltroid/UltroidAddons"),
+ ],
+ [Button.url("Support Group", "t.me/UltroidSupportChat")],
+]
+
+ULTSTRING = """🎇 **Thanks for Deploying Ultroid Userbot!**
+
+• Here, are the Some Basic stuff from, where you can Know, about its Usage."""
+
+
+@ultroid_cmd(
+ pattern="repo$",
+ manager=True,
+)
+async def repify(e):
+ try:
+ q = await e.client.inline_query(asst.me.username, "")
+ await q[0].click(e.chat_id)
+ return await e.delete()
+ except (
+ ChatSendInlineForbiddenError,
+ ChatSendMediaForbiddenError,
+ BotMethodInvalidError,
+ ):
+ pass
+ except Exception as er:
+ LOGS.info(f"Error while repo command : {str(er)}")
+ await e.eor(REPOMSG)
+
+
+@ultroid_cmd(pattern="ultroid$")
+async def useUltroid(rs):
+ button = Button.inline("Start >>", "initft_2")
+ msg = await asst.send_message(
+ LOG_CHANNEL,
+ ULTSTRING,
+ file="https://graph.org/file/54a917cc9dbb94733ea5f.jpg",
+ buttons=button,
+ )
+ if not (rs.chat_id == LOG_CHANNEL and rs.client._bot):
+ await eor(rs, f"**[Click Here]({msg.message_link})**")
diff --git a/plugins/_userlogs.py b/plugins/_userlogs.py
new file mode 100644
index 0000000000000000000000000000000000000000..876718001f4bf1bf3b150af463ea3758a9a360de
--- /dev/null
+++ b/plugins/_userlogs.py
@@ -0,0 +1,299 @@
+# Ultroid - UserBot
+# Copyright (C) 2021-2025 TeamUltroid
+#
+# This file is a part of < https://github.com/TeamUltroid/Ultroid/ >
+# PLease read the GNU Affero General Public License in
+# .
+
+import os
+import re
+
+from telethon.errors.rpcerrorlist import (
+ ChannelPrivateError,
+ ChatWriteForbiddenError,
+ MediaCaptionTooLongError,
+ MediaEmptyError,
+ MessageTooLongError,
+ PeerIdInvalidError,
+ UserNotParticipantError,
+)
+from telethon.tl.types import MessageEntityMention, MessageEntityMentionName, User
+from telethon.utils import get_display_name
+
+from pyUltroid.dB.botchat_db import tag_add, who_tag
+
+from . import (
+ LOG_CHANNEL,
+ LOGS,
+ Button,
+ asst,
+ callback,
+ events,
+ get_string,
+ inline_mention,
+ udB,
+ ultroid_bot,
+)
+
+CACHE_SPAM = {}
+TAG_EDITS = {}
+
+
+@ultroid_bot.on(
+ events.NewMessage(
+ incoming=True,
+ func=lambda e: (e.mentioned),
+ ),
+)
+async def all_messages_catcher(e):
+ x = await e.get_sender()
+ if isinstance(x, User) and (x.bot or x.verified):
+ return
+ if not udB.get_key("TAG_LOG"):
+ return
+ NEEDTOLOG = udB.get_key("TAG_LOG")
+ if e.chat_id == NEEDTOLOG:
+ return
+ buttons = await parse_buttons(e)
+ try:
+ sent = await asst.send_message(NEEDTOLOG, e.message, buttons=buttons)
+ if TAG_EDITS.get(e.chat_id):
+ TAG_EDITS[e.chat_id].update({e.id: {"id": sent.id, "msg": e}})
+ else:
+ TAG_EDITS.update({e.chat_id: {e.id: {"id": sent.id, "msg": e}}})
+ tag_add(sent.id, e.chat_id, e.id)
+ except MediaEmptyError as er:
+ LOGS.debug(f"handling {er}.")
+ try:
+ msg = await asst.get_messages(e.chat_id, ids=e.id)
+ sent = await asst.send_message(NEEDTOLOG, msg, buttons=buttons)
+ if TAG_EDITS.get(e.chat_id):
+ TAG_EDITS[e.chat_id].update({e.id: {"id": sent.id, "msg": e}})
+ else:
+ TAG_EDITS.update({e.chat_id: {e.id: {"id": sent.id, "msg": e}}})
+ tag_add(sent.id, e.chat_id, e.id)
+ except Exception as me:
+ if not isinstance(me, (PeerIdInvalidError, ValueError)):
+ LOGS.exception(me)
+ if e.photo or e.sticker or e.gif:
+ try:
+ media = await e.download_media()
+ sent = await asst.send_message(
+ NEEDTOLOG, e.message.text, file=media, buttons=buttons
+ )
+ if TAG_EDITS.get(e.chat_id):
+ TAG_EDITS[e.chat_id].update({e.id: {"id": sent.id, "msg": e}})
+ else:
+ TAG_EDITS.update({e.chat_id: {e.id: {"id": sent.id, "msg": e}}})
+ return os.remove(media)
+ except Exception as er:
+ LOGS.exception(er)
+ await asst.send_message(NEEDTOLOG, get_string("com_4"), buttons=buttons)
+ except (PeerIdInvalidError, ValueError) as er:
+ LOGS.exception(er)
+ try:
+ CACHE_SPAM[NEEDTOLOG]
+ except KeyError:
+ await asst.send_message(
+ udB.get_key("LOG_CHANNEL"), get_string("userlogs_1")
+ )
+ CACHE_SPAM.update({NEEDTOLOG: True})
+ except ChatWriteForbiddenError:
+ try:
+ await asst.get_permissions(NEEDTOLOG, "me")
+ MSG = get_string("userlogs_4")
+ except UserNotParticipantError:
+ MSG = get_string("userlogs_2")
+ try:
+ CACHE_SPAM[NEEDTOLOG]
+ except KeyError:
+ await asst.send_message(LOG_CHANNEL, MSG)
+ CACHE_SPAM.update({NEEDTOLOG: True})
+ except Exception as er:
+ LOGS.exception(er)
+
+
+if udB.get_key("TAG_LOG"):
+
+ @ultroid_bot.on(events.MessageEdited(func=lambda x: not x.out))
+ async def upd_edits(event):
+ x = event.sender
+ if isinstance(x, User) and (x.bot or x.verified):
+ return
+ if event.chat_id not in TAG_EDITS:
+ if event.sender_id == udB.get_key("TAG_LOG"):
+ return
+ if event.is_private:
+ return
+ if entities := event.get_entities_text():
+ is_self = False
+ username = event.client.me.username
+ if username:
+ username = username.lower()
+ for ent, text in entities:
+ if isinstance(ent, MessageEntityMention):
+ is_self = text[1:].lower() == username
+ elif isinstance(ent, MessageEntityMentionName):
+ is_self = ent.user_id == event.client.me.id
+ if is_self:
+ text = f"**#Edited & #Mentioned**\n\n{event.text}"
+ try:
+ sent = await asst.send_message(
+ udB.get_key("TAG_LOG"),
+ text,
+ buttons=await parse_buttons(event),
+ )
+ except Exception as er:
+ return LOGS.exception(er)
+ if TAG_EDITS.get(event.chat_id):
+ TAG_EDITS[event.chat_id].update({event.id: {"id": sent.id}})
+ else:
+ TAG_EDITS.update({event.chat_id: {event.id: {"id": sent.id}}})
+ return
+ d_ = TAG_EDITS[event.chat_id]
+ if not d_.get(event.id):
+ return
+ d_ = d_[event.id]
+ if d_["msg"].text == event.text:
+ return
+ msg = None
+ if d_.get("count"):
+ d_["count"] += 1
+ else:
+ msg = True
+ d_.update({"count": 1})
+ if d_["count"] > 10:
+ return # some limit to take edits
+ try:
+ MSG = await asst.get_messages(udB.get_key("TAG_LOG"), ids=d_["id"])
+ except Exception as er:
+ return LOGS.exception(er)
+ TEXT = MSG.text
+ if msg:
+ TEXT += "\n\n🖋 **Later Edited to !**"
+ strf = event.edit_date.strftime("%H:%M:%S")
+ if "\n" not in event.text:
+ TEXT += f"\n• `{strf}` : {event.text}"
+ else:
+ TEXT += f"\n• `{strf}` :\n-> {event.text}"
+ if d_["count"] == 10:
+ TEXT += "\n\n• __Only the first 10 Edits are shown.__"
+ try:
+ msg = await MSG.edit(TEXT, buttons=await parse_buttons(event))
+ d_["msg"] = msg
+ except (MessageTooLongError, MediaCaptionTooLongError):
+ del TAG_EDITS[event.chat_id][event.id]
+ except Exception as er:
+ LOGS.exception(er)
+
+ @ultroid_bot.on(
+ events.NewMessage(
+ outgoing=True,
+ chats=[udB.get_key("TAG_LOG")],
+ func=lambda e: e.reply_to,
+ )
+ )
+ async def idk(e):
+ id = e.reply_to_msg_id
+ chat, msg = who_tag(id)
+ if chat and msg:
+ try:
+ await ultroid_bot.send_message(chat, e.message, reply_to=msg)
+ except BaseException as er:
+ LOGS.exception(er)
+
+
+# log for assistant/user joins/add
+
+
+async def when_added_or_joined(event):
+ user = await event.get_user()
+ chat = await event.get_chat()
+ if not (user and user.is_self):
+ return
+ if getattr(chat, "username", None):
+ chat = f"[{chat.title}](https://t.me/{chat.username}/{event.action_message.id})"
+ else:
+ chat = f"[{chat.title}](https://t.me/c/{chat.id}/{event.action_message.id})"
+ key = "bot" if event.client._bot else "user"
+ buttons = Button.inline(
+ get_string("userlogs_3"), data=f"leave_ch_{event.chat_id}|{key}"
+ )
+ if event.user_added:
+ tmp = event.added_by
+ text = f"#ADD_LOG\n\n{inline_mention(tmp)} just added {inline_mention(user)} to {chat}."
+ elif event.from_request:
+ text = f"#APPROVAL_LOG\n\n{inline_mention(user)} just got Chat Join Approval to {chat}."
+ else:
+ text = f"#JOIN_LOG\n\n{inline_mention(user)} just joined {chat}."
+ await asst.send_message(udB.get_key("LOG_CHANNEL"), text, buttons=buttons)
+
+
+asst.add_event_handler(
+ when_added_or_joined, events.ChatAction(func=lambda x: x.user_added)
+)
+ultroid_bot.add_event_handler(
+ when_added_or_joined,
+ events.ChatAction(func=lambda x: x.user_added or x.user_joined),
+)
+_client = {"bot": asst, "user": ultroid_bot}
+
+
+@callback(
+ re.compile(
+ "leave_ch_(.*)",
+ ),
+ from_users=[ultroid_bot.uid],
+)
+async def leave_ch_at(event):
+ cht = event.data_match.group(1).decode("UTF-8")
+ ch_id, client = cht.split("|")
+ try:
+ client = _client[client]
+ except KeyError:
+ return
+ try:
+ name = (await client.get_entity(int(ch_id))).title
+ await client.delete_dialog(int(ch_id))
+ except UserNotParticipantError:
+ pass
+ except ChannelPrivateError:
+ return await event.edit(
+ "`[CANT_ACCESS_CHAT]` `Maybe already left or got banned.`"
+ )
+ except Exception as er:
+ LOGS.exception(er)
+ return await event.answer(str(er))
+ await event.edit(get_string("userlogs_5").format(name))
+
+
+@callback("do_nothing")
+async def _(event):
+ await event.answer()
+
+
+async def parse_buttons(event):
+ y, x = event.chat, event.sender
+ where_n, who_n = get_display_name(y), get_display_name(x)
+ where_l = event.message_link
+ buttons = [[Button.url(where_n, where_l)]]
+ if isinstance(x, User) and x.username:
+ try:
+ buttons.append(
+ [Button.mention(who_n, await asst.get_input_entity(x.username))]
+ )
+ except Exception as er:
+ LOGS.exception(er)
+ buttons.append([Button.url(who_n, f"t.me/{x.username}")])
+ elif getattr(x, "username"):
+ buttons.append([Button.url(who_n, f"t.me/{x.username}")])
+ else:
+ buttons.append([Button.url(who_n, where_l)])
+ replied = await event.get_reply_message()
+ if replied and replied.out:
+ button = Button.url("Replied to", replied.message_link)
+ if len(who_n) > 7:
+ buttons.append([button])
+ else:
+ buttons[-1].append(button)
+ return buttons
diff --git a/plugins/_wspr.py b/plugins/_wspr.py
new file mode 100644
index 0000000000000000000000000000000000000000..462a34e696eace9bf49534214aaf32a2fb417401
--- /dev/null
+++ b/plugins/_wspr.py
@@ -0,0 +1,204 @@
+# Ultroid - UserBot
+# Copyright (C) 2021-2025 TeamUltroid
+#
+# This file is a part of < https://github.com/TeamUltroid/Ultroid/ >
+# PLease read the GNU Affero General Public License in
+# .
+
+import re
+
+from telethon import Button
+from telethon.errors.rpcerrorlist import (
+ BotInlineDisabledError,
+ BotResponseTimeoutError,
+ MessageNotModifiedError,
+)
+from telethon.tl import types
+from telethon.tl.functions.users import GetFullUserRequest as gu
+
+from . import (
+ HNDLR,
+ LOGS,
+ asst,
+ callback,
+ get_string,
+ in_pattern,
+ inline_mention,
+ ultroid_bot,
+ ultroid_cmd,
+)
+
+buddhhu = {}
+
+
+@ultroid_cmd(
+ pattern="wspr( (.*)|$)",
+)
+async def _(e):
+ if e.reply_to_msg_id:
+ okk = await e.get_reply_message()
+ put = f"@{okk.sender.username}" if okk.sender.username else okk.sender_id
+ else:
+ put = e.pattern_match.group(1).strip()
+ if put:
+ try:
+ results = await e.client.inline_query(asst.me.username, f"msg {put}")
+ except BotResponseTimeoutError:
+ return await e.eor(
+ get_string("help_2").format(HNDLR),
+ )
+ except BotInlineDisabledError:
+ return await e.eor(get_string("help_3"))
+ await results[0].click(e.chat_id, reply_to=e.reply_to_msg_id, hide_via=True)
+ return await e.delete()
+ await e.eor(get_string("wspr_3"))
+
+
+@in_pattern("wspr", owner=True)
+async def _(e):
+ iuser = e.query.user_id
+ zzz = e.text.split(maxsplit=2)
+ try:
+ query = zzz[1]
+ if query.isdigit():
+ query = int(query)
+ logi = await ultroid_bot.get_entity(query)
+ if not isinstance(logi, types.User):
+ raise ValueError("Invalid Username.")
+ except IndexError:
+ sur = await e.builder.article(
+ title="Give Username",
+ description="You Didn't Type Username or id.",
+ text="You Didn't Type Username or id.",
+ )
+ return await e.answer([sur])
+ except ValueError as er:
+ LOGS.exception(er)
+ sur = await e.builder.article(
+ title="User Not Found",
+ description="Make sure username or id is correct.",
+ text="Make sure username or id is correct.",
+ )
+ return await e.answer([sur])
+ try:
+ desc = zzz[2]
+ except IndexError:
+ sur = await e.builder.article(
+ title="Type ur msg", text="You Didn't Type Your Msg"
+ )
+ return await e.answer([sur])
+ button = [
+ [
+ Button.inline("Secret Msg", data=f"dd_{e.id}"),
+ Button.inline("Delete Msg", data=f"del_{e.id}"),
+ ],
+ [
+ Button.switch_inline(
+ "New", query=f"wspr {logi.username or logi.id}", same_peer=True
+ )
+ ],
+ ]
+ us = logi.username or logi.first_name
+ sur = await e.builder.article(
+ title=logi.first_name,
+ description=desc,
+ text=get_string("wspr_1").format(us),
+ buttons=button,
+ )
+ buddhhu.update({e.id: [logi.id, iuser, desc]})
+ await e.answer([sur])
+
+
+@in_pattern("msg", owner=True)
+async def _(e):
+ zzz = e.text.split(maxsplit=1)
+ desc = "Touch me"
+ try:
+ query = zzz[1]
+ if query.isdigit():
+ query = int(query)
+ logi = await ultroid_bot(gu(id=query))
+ user = logi.users[0]
+ mention = inline_mention(user)
+ x = user.status
+ if isinstance(x, types.UserStatusOnline):
+ status = "Online"
+ elif isinstance(x, types.UserStatusOffline):
+ status = "Offline"
+ elif isinstance(x, types.UserStatusRecently):
+ status = "Last Seen Recently"
+ elif isinstance(x, types.UserStatusLastMonth):
+ status = "Last seen months ago"
+ elif isinstance(x, types.UserStatusLastWeek):
+ status = "Last seen weeks ago"
+ else:
+ status = "Can't Tell"
+ text = f"**Name:** `{user.first_name}`\n"
+ text += f"**Id:** `{user.id}`\n"
+ if user.username:
+ text += f"**Username:** `{user.username}`\n"
+ url = f"https://t.me/{user.username}"
+ else:
+ text += f"**Mention:** `{mention}`\n"
+ url = f"tg://user?id={user.id}"
+ text += f"**Status:** `{status}`\n"
+ text += f"**About:** `{logi.full_user.about}`"
+ button = [
+ Button.url("Private", url=url),
+ Button.switch_inline(
+ "Secret msg",
+ query=f"wspr {query} Hello 👋",
+ same_peer=True,
+ ),
+ ]
+ sur = e.builder.article(
+ title=user.first_name,
+ description=desc,
+ text=text,
+ buttons=button,
+ )
+ except IndexError:
+ sur = e.builder.article(
+ title="Give Username",
+ description="You Didn't Type Username or id.",
+ text="You Didn't Type Username or id.",
+ )
+ except BaseException as er:
+ LOGS.exception(er)
+ name = get_string("wspr_4").format(query)
+ sur = e.builder.article(
+ title=name,
+ text=name,
+ )
+
+ await e.answer([sur])
+
+
+@callback(
+ re.compile(
+ "dd_(.*)",
+ ),
+)
+async def _(e):
+ ids = int(e.pattern_match.group(1).strip().decode("UTF-8"))
+ if buddhhu.get(ids):
+ if e.sender_id in buddhhu[ids]:
+ await e.answer(buddhhu[ids][-1], alert=True)
+ else:
+ await e.answer("Not For You", alert=True)
+ else:
+ await e.answer(get_string("wspr_2"), alert=True)
+
+
+@callback(re.compile("del_(.*)"))
+async def _(e):
+ ids = int(e.pattern_match.group(1).strip().decode("UTF-8"))
+ if buddhhu.get(ids):
+ if e.sender_id in buddhhu[ids]:
+ buddhhu.pop(ids)
+ try:
+ await e.edit(get_string("wspr_2"))
+ except MessageNotModifiedError:
+ pass
+ else:
+ await e.answer(get_string("wspr_5"), alert=True)
diff --git a/plugins/admintools.py b/plugins/admintools.py
new file mode 100644
index 0000000000000000000000000000000000000000..a6f8e6380121075e660fa2c424df0349844cb82e
--- /dev/null
+++ b/plugins/admintools.py
@@ -0,0 +1,471 @@
+# Ultroid - UserBot
+# Copyright (C) 2021-2025 TeamUltroid
+#
+# This file is a part of < https://github.com/TeamUltroid/Ultroid/ >
+# PLease read the GNU Affero General Public License in
+# .
+
+from . import get_help
+
+__doc__ = get_help("help_admintools")
+
+import asyncio
+
+from telethon.errors import BadRequestError
+from telethon.errors.rpcerrorlist import ChatNotModifiedError, UserIdInvalidError
+from telethon.tl.functions.channels import EditAdminRequest, GetFullChannelRequest
+from telethon.tl.functions.messages import GetFullChatRequest, SetHistoryTTLRequest
+from telethon.tl.types import InputMessagesFilterPinned
+from telethon.utils import get_display_name
+
+from pyUltroid.dB import DEVLIST
+from pyUltroid.fns.admins import ban_time
+from pyUltroid.fns.info import get_uinfo
+
+from . import HNDLR, LOGS, eod, eor, get_string, inline_mention, types, ultroid_cmd
+
+
+@ultroid_cmd(
+ pattern="promote( (.*)|$)",
+ admins_only=True,
+ manager=True,
+ require="add_admins",
+ fullsudo=True,
+)
+async def prmte(ult):
+ xx = await ult.eor(get_string("com_1"))
+ user, rank = await get_uinfo(ult)
+ rank = rank or "Admin"
+ FullRight = False
+ if not user:
+ return await xx.edit(get_string("pro_1"))
+ if rank.split()[0] == "-f":
+ try:
+ rank = rank.split(maxsplit=1)[1]
+ except IndexError:
+ rank = "Admin"
+ FullRight = True
+ try:
+ if FullRight:
+ await ult.client(
+ EditAdminRequest(ult.chat_id, user.id, ult.chat.admin_rights, rank)
+ )
+ else:
+ await ult.client.edit_admin(
+ ult.chat_id,
+ user.id,
+ invite_users=True,
+ ban_users=True,
+ delete_messages=True,
+ pin_messages=True,
+ manage_call=True,
+ title=rank,
+ )
+ await eod(
+ xx, get_string("pro_2").format(inline_mention(user), ult.chat.title, rank)
+ )
+ except Exception as ex:
+ return await xx.edit(f"`{ex}`")
+
+
+@ultroid_cmd(
+ pattern="demote( (.*)|$)",
+ admins_only=True,
+ manager=True,
+ require="add_admins",
+ fullsudo=True,
+)
+async def dmote(ult):
+ xx = await ult.eor(get_string("com_1"))
+ user, rank = await get_uinfo(ult)
+ if not rank:
+ rank = "Not Admin"
+ if not user:
+ return await xx.edit(get_string("de_1"))
+ try:
+ await ult.client.edit_admin(
+ ult.chat_id,
+ user.id,
+ invite_users=None,
+ ban_users=None,
+ delete_messages=None,
+ pin_messages=None,
+ manage_call=None,
+ title=rank,
+ )
+ await eod(xx, get_string("de_2").format(inline_mention(user), ult.chat.title))
+ except Exception as ex:
+ return await xx.edit(f"`{ex}`")
+
+
+@ultroid_cmd(
+ pattern="ban( (.*)|$)",
+ admins_only=True,
+ manager=True,
+ require="ban_users",
+ fullsudo=True,
+)
+async def bban(ult):
+ something = await get_uinfo(ult)
+ if not something:
+ return
+ user, reason = something
+ if not user:
+ return await eod(ult, get_string("ban_1"))
+ if user.id in DEVLIST:
+ return await eod(ult, get_string("ban_2"))
+ try:
+ await ult.client.edit_permissions(ult.chat_id, user.id, view_messages=False)
+ except UserIdInvalidError:
+ return await eod(ult, get_string("adm_1"))
+ except BadRequestError:
+ return await eod(ult, get_string("ban_3"))
+ senderme = inline_mention(await ult.get_sender())
+ userme = inline_mention(user)
+ text = get_string("ban_4").format(userme, senderme, ult.chat.title)
+ if reason:
+ text += get_string("ban_5").format(reason)
+ await eod(ult, text)
+
+
+@ultroid_cmd(
+ pattern="unban( (.*)|$)",
+ admins_only=True,
+ manager=True,
+ require="ban_users",
+ fullsudo=True,
+)
+async def uunban(ult):
+ xx = await ult.eor(get_string("com_1"))
+ if ult.text[1:].startswith("unbanall"):
+ return
+ something = await get_uinfo(ult)
+ if not something:
+ return
+ user, reason = something
+ if not user:
+ return await xx.edit(get_string("unban_1"))
+ try:
+ await ult.client.edit_permissions(ult.chat_id, user.id, view_messages=True)
+ except UserIdInvalidError:
+ return await eod(ult, get_string("adm_1"))
+ except BadRequestError:
+ return await xx.edit(get_string("adm_2"))
+ sender = inline_mention(await ult.get_sender())
+ text = get_string("unban_3").format(inline_mention(user), sender, ult.chat.title)
+ if reason:
+ text += get_string("ban_5").format(reason)
+ await xx.edit(text)
+
+
+@ultroid_cmd(
+ pattern="kick( (.*)|$)",
+ manager=True,
+ require="ban_users",
+ fullsudo=True,
+)
+async def kck(ult):
+ if "kickme" in ult.text:
+ return
+ if ult.is_private:
+ return await ult.eor("`Use this in Group/Channel.`", time=5)
+ ml = ult.text.split(" ", maxsplit=1)[0]
+ xx = await ult.eor(get_string("com_1"))
+ something = await get_uinfo(ult)
+ if not something:
+ return
+ user, reason = something
+ if not user:
+ return await xx.edit(get_string("adm_1"))
+ if user.id in DEVLIST:
+ return await xx.edit(get_string("kick_2"))
+ if getattr(user, "is_self", False):
+ return await xx.edit(get_string("kick_3"))
+ try:
+ await ult.client.kick_participant(ult.chat_id, user.id)
+ except BadRequestError as er:
+ LOGS.info(er)
+ return await xx.edit(get_string("kick_1"))
+ except Exception as e:
+ LOGS.exception(e)
+ return
+ text = get_string("kick_4").format(
+ inline_mention(user), inline_mention(await ult.get_sender()), ult.chat.title
+ )
+ if reason:
+ text += get_string("ban_5").format(reason)
+ await xx.edit(text)
+
+
+@ultroid_cmd(
+ pattern="tban( (.*)|$)",
+ admins_only=True,
+ manager=True,
+ require="ban_users",
+ fullsudo=True,
+)
+async def tkicki(e):
+ huh = e.text.split()
+ inputt = None
+ try:
+ tme = huh[1]
+ except IndexError:
+ return await e.eor(get_string("adm_3"), time=15)
+ try:
+ inputt = huh[2]
+ except IndexError:
+ if e.reply_to_msg_id:
+ inputt = (await e.get_reply_message()).sender_id
+ if not inputt:
+ return await e.eor(get_string("tban_1"))
+ userid = await e.client.parse_id(inputt)
+ try:
+ user = await e.client.get_entity(userid)
+ except Exception as ex:
+ return await eor(e, f"`{ex}`")
+ try:
+ bun = ban_time(tme)
+ await e.client.edit_permissions(
+ e.chat_id, user.id, until_date=bun, view_messages=False
+ )
+ await eod(
+ e,
+ get_string("tban_2").format(inline_mention(user), e.chat.title, tme),
+ time=15,
+ )
+ except Exception as m:
+ return await e.eor(str(m))
+
+
+@ultroid_cmd(pattern="pin$", manager=True, require="pin_messages", fullsudo=True)
+async def pin(msg):
+ if not msg.is_reply:
+ return await eor(msg, get_string("pin_1"))
+ me = await msg.get_reply_message()
+ if me.is_private:
+ text = "`Pinned.`"
+ else:
+ text = f"Pinned [This Message]({me.message_link}) !"
+ try:
+ await msg.client.pin_message(msg.chat_id, me.id, notify=False)
+ except BadRequestError:
+ return await eor(msg, get_string("adm_2"))
+ except Exception as e:
+ return await eor(msg, f"**ERROR:**`{e}`")
+ await eor(msg, text)
+
+
+@ultroid_cmd(
+ pattern="unpin($| (.*))",
+ manager=True,
+ require="pin_messages",
+ fullsudo=True,
+)
+async def unp(ult):
+ xx = await ult.eor(get_string("com_1"))
+ ch = (ult.pattern_match.group(1).strip()).strip()
+ msg = None
+ if ult.is_reply:
+ msg = ult.reply_to_msg_id
+ elif ch != "all":
+ return await xx.edit(get_string("unpin_1").format(HNDLR))
+ try:
+ await ult.client.unpin_message(ult.chat_id, msg)
+ except BadRequestError:
+ return await xx.edit(get_string("adm_2"))
+ except Exception as e:
+ return await xx.edit(f"**ERROR:**`{e}`")
+ await xx.edit("`Unpinned!`")
+
+
+@ultroid_cmd(
+ pattern="tpin( (.*)|$)",
+ admins_only=True,
+ manager=True,
+ require="pin_messages",
+ fullsudo=True,
+)
+async def pin_message(ult):
+ match = ult.pattern_match.group(1).strip()
+ if not ult.is_reply:
+ return await ult.eor("`Reply to message..`", time=6)
+ if not match:
+ return await ult.eor("`Please provide time..`", time=8)
+ msg = await ult.eor(get_string("com_1"))
+ msg_id = ult.reply_to_msg_id
+ try:
+ time = ban_time(match)
+ await ult.client.pin_message(ult.chat_id, msg_id)
+ await msg.eor(f"`pinned for time` `{time}`", time=8)
+ except Exception as er:
+ return await msg.edit(str(er))
+ await asyncio.sleep(time)
+ try:
+ await ult.client.unpin_message(ult.chat_id, msg_id)
+ except Exception as er:
+ LOGS.exception(er)
+
+
+@ultroid_cmd(pattern="purge( (.*)|$)", manager=True, require="delete_messages")
+async def fastpurger(purg):
+ match = purg.pattern_match.group(1).strip()
+ try:
+ ABC = purg.text[6]
+ except IndexError:
+ ABC = None
+ if ABC and purg.text[6] in ["m", "a"]:
+ return
+ if not purg._client._bot and (
+ (match)
+ or (purg.is_reply and (purg.is_private or isinstance(purg.chat, types.Chat)))
+ ):
+ p = 0
+ async for msg in purg.client.iter_messages(
+ purg.chat_id,
+ limit=int(match) if match else None,
+ min_id=purg.reply_to_msg_id if purg.is_reply else None,
+ ):
+ await msg.delete()
+ p += 0
+ return await eor(purg, f"Purged {p} Messages! ", time=5)
+ if not purg.reply_to_msg_id:
+ return await eor(purg, get_string("purge_1"), time=10)
+ try:
+ await purg.client.delete_messages(
+ purg.chat_id, list(range(purg.reply_to_msg_id, purg.id))
+ )
+
+ except Exception as er:
+ LOGS.info(er)
+ await purg.eor("__Fast purge complete!__", time=5)
+
+
+@ultroid_cmd(
+ pattern="purgeme( (.*)|$)",
+)
+async def fastpurgerme(purg):
+ if num := purg.pattern_match.group(1).strip():
+ try:
+ nnt = int(num)
+ except BaseException:
+ await eor(purg, get_string("com_3"), time=5)
+ return
+ mp = 0
+ async for mm in purg.client.iter_messages(
+ purg.chat_id, limit=nnt, from_user="me"
+ ):
+ await mm.delete()
+ mp += 1
+ await eor(purg, f"Purged {mp} Messages!", time=5)
+ return
+ elif not purg.reply_to_msg_id:
+ return await eod(
+ purg,
+ "`Reply to a message to purge from or use it like ``purgeme `",
+ time=10,
+ )
+ chat = await purg.get_input_chat()
+ msgs = []
+ async for msg in purg.client.iter_messages(
+ chat,
+ from_user="me",
+ min_id=purg.reply_to_msg_id,
+ ):
+ msgs.append(msg)
+ if msgs:
+ await purg.client.delete_messages(chat, msgs)
+ await purg.eor(
+ "__Fast purge complete!__\n**Purged** `" + str(len(msgs)) + "` **messages.**",
+ time=5,
+ )
+
+
+@ultroid_cmd(
+ pattern="purgeall$",
+)
+async def _(e):
+ if not e.is_reply:
+ return await eod(
+ e,
+ get_string("purgeall_1"),
+ )
+
+ msg = await e.get_reply_message()
+ name = msg.sender
+ try:
+ await e.client.delete_messages(e.chat_id, from_user=msg.sender_id)
+ await e.eor(get_string("purgeall_2").format(name.first_name), time=5)
+ except Exception as er:
+ return await e.eor(str(er), time=5)
+
+@ultroid_cmd(pattern="pinned", manager=True, groups_only=True)
+async def djshsh(event):
+ chat = await event.get_chat()
+ if isinstance(chat, types.Chat):
+ FChat = await event.client(GetFullChatRequest(chat.id))
+ elif isinstance(chat, types.Channel):
+ FChat = await event.client(GetFullChannelRequest(chat.id))
+ else:
+ return
+ msg_id = FChat.full_chat.pinned_msg_id
+ if not msg_id:
+ return await event.eor(get_string("pinned_1"))
+ msg = await event.client.get_messages(chat.id, ids=msg_id)
+ if msg:
+ await event.eor(get_string("pinned_2").format(msg.message_link))
+
+
+@ultroid_cmd(
+ pattern="listpinned$",
+)
+async def get_all_pinned(event):
+ x = await event.eor(get_string("com_1"))
+ chat_id = (str(event.chat_id)).replace("-100", "")
+ chat_name = get_display_name(event.chat)
+ a = ""
+ c = 1
+ async for i in event.client.iter_messages(
+ event.chat_id, filter=InputMessagesFilterPinned
+ ):
+ if i.message:
+ t = " ".join(i.message.split()[:4])
+ txt = f"{t}...."
+ else:
+ txt = "Go to message."
+ a += f"{c}. {txt}\n"
+ c += 1
+
+ if c == 1:
+ m = f"The pinned message in {chat_name}:\n\n"
+ else:
+ m = f"List of pinned message(s) in {chat_name}:\n\n"
+
+ if not a:
+ return await eor(x, get_string("listpin_1"), time=5)
+
+ await x.edit(m + a, parse_mode="html")
+
+
+@ultroid_cmd(
+ pattern="autodelete( (.*)|$)",
+ admins_only=True,
+)
+async def autodelte(ult):
+ match = ult.pattern_match.group(1).strip()
+ if not match or match not in ["24h", "7d", "1m", "off"]:
+ return await ult.eor("`Please Use in Proper Format..`", time=5)
+ if match == "24h":
+ tt = 3600 * 24
+ elif match == "7d":
+ tt = 3600 * 24 * 7
+ elif match == "1m":
+ tt = 3600 * 24 * 31
+ else:
+ tt = 0
+ try:
+ await ult.client(SetHistoryTTLRequest(ult.chat_id, period=tt))
+ except ChatNotModifiedError:
+ return await ult.eor(
+ f"Auto Delete Setting is Already same to `{match}`", time=5
+ )
+ await ult.eor(f"Auto Delete Status Changed to `{match}` !")
diff --git a/plugins/afk.py b/plugins/afk.py
new file mode 100644
index 0000000000000000000000000000000000000000..74e06eb7748295ffb070bd591206b1149325d704
--- /dev/null
+++ b/plugins/afk.py
@@ -0,0 +1,165 @@
+# Ultroid - UserBot
+# Copyright (C) 2021-2025 TeamUltroid
+#
+# This file is a part of < https://github.com/TeamUltroid/Ultroid/ >
+# PLease read the GNU Affero General Public License in
+# .
+
+from . import get_help
+
+__doc__ = get_help("help_afk")
+
+
+import asyncio
+
+from telethon import events
+
+from pyUltroid.dB.afk_db import add_afk, del_afk, is_afk
+from pyUltroid.dB.base import KeyManager
+
+from . import (
+ LOG_CHANNEL,
+ NOSPAM_CHAT,
+ Redis,
+ asst,
+ get_string,
+ mediainfo,
+ udB,
+ ultroid_bot,
+ ultroid_cmd,
+ upload_file
+)
+
+old_afk_msg = []
+
+is_approved = KeyManager("PMPERMIT", cast=list).contains
+
+
+@ultroid_cmd(pattern="afk( (.*)|$)", owner_only=True)
+async def set_afk(event):
+ if event.client._bot or is_afk():
+ return
+ text, media, media_type = None, None, None
+ if event.pattern_match.group(1).strip():
+ text = event.text.split(maxsplit=1)[1]
+ reply = await event.get_reply_message()
+ if reply:
+ if reply.text and not text:
+ text = reply.text
+ if reply.media:
+ media_type = mediainfo(reply.media)
+ if media_type.startswith(("pic", "gif")):
+ file = await event.client.download_media(reply.media)
+ media = upload_file(file)
+ else:
+ media = reply.file.id
+ await event.eor("`Done`", time=2)
+ add_afk(text, media_type, media)
+ ultroid_bot.add_handler(remove_afk, events.NewMessage(outgoing=True))
+ ultroid_bot.add_handler(
+ on_afk,
+ events.NewMessage(
+ incoming=True, func=lambda e: bool(e.mentioned or e.is_private)
+ ),
+ )
+ msg1, msg2 = None, None
+ if text and media:
+ if "sticker" in media_type:
+ msg1 = await ultroid_bot.send_file(event.chat_id, file=media)
+ msg2 = await ultroid_bot.send_message(
+ event.chat_id, get_string("afk_5").format(text)
+ )
+ else:
+ msg1 = await ultroid_bot.send_message(
+ event.chat_id, get_string("afk_5").format(text), file=media
+ )
+ elif media:
+ if "sticker" in media_type:
+ msg1 = await ultroid_bot.send_file(event.chat_id, file=media)
+ msg2 = await ultroid_bot.send_message(event.chat_id, get_string("afk_6"))
+ else:
+ msg1 = await ultroid_bot.send_message(
+ event.chat_id, get_string("afk_6"), file=media
+ )
+ elif text:
+ msg1 = await event.respond(get_string("afk_5").format(text))
+ else:
+ msg1 = await event.respond(get_string("afk_6"))
+ old_afk_msg.append(msg1)
+ if msg2:
+ old_afk_msg.append(msg2)
+ return await asst.send_message(LOG_CHANNEL, msg2.text)
+ await asst.send_message(LOG_CHANNEL, msg1.text)
+
+
+async def remove_afk(event):
+ if event.is_private and udB.get_key("PMSETTING") and not is_approved(event.chat_id):
+ return
+ elif "afk" in event.text.lower():
+ return
+ elif event.chat_id in NOSPAM_CHAT:
+ return
+ if is_afk():
+ _, _, _, afk_time = is_afk()
+ del_afk()
+ off = await event.reply(get_string("afk_1").format(afk_time))
+ await asst.send_message(LOG_CHANNEL, get_string("afk_2").format(afk_time))
+ for x in old_afk_msg:
+ try:
+ await x.delete()
+ except BaseException:
+ pass
+ await asyncio.sleep(10)
+ await off.delete()
+
+
+async def on_afk(event):
+ if event.is_private and Redis("PMSETTING") and not is_approved(event.chat_id):
+ return
+ elif "afk" in event.text.lower():
+ return
+ elif not is_afk():
+ return
+ if event.chat_id in NOSPAM_CHAT:
+ return
+ sender = await event.get_sender()
+ if sender.bot or sender.verified:
+ return
+ text, media_type, media, afk_time = is_afk()
+ msg1, msg2 = None, None
+ if text and media:
+ if "sticker" in media_type:
+ msg1 = await event.reply(file=media)
+ msg2 = await event.reply(get_string("afk_3").format(afk_time, text))
+ else:
+ msg1 = await event.reply(
+ get_string("afk_3").format(afk_time, text), file=media
+ )
+ elif media:
+ if "sticker" in media_type:
+ msg1 = await event.reply(file=media)
+ msg2 = await event.reply(get_string("afk_4").format(afk_time))
+ else:
+ msg1 = await event.reply(get_string("afk_4").format(afk_time), file=media)
+ elif text:
+ msg1 = await event.reply(get_string("afk_3").format(afk_time, text))
+ else:
+ msg1 = await event.reply(get_string("afk_4").format(afk_time))
+ for x in old_afk_msg:
+ try:
+ await x.delete()
+ except BaseException:
+ pass
+ old_afk_msg.append(msg1)
+ if msg2:
+ old_afk_msg.append(msg2)
+
+
+if udB.get_key("AFK_DB"):
+ ultroid_bot.add_handler(remove_afk, events.NewMessage(outgoing=True))
+ ultroid_bot.add_handler(
+ on_afk,
+ events.NewMessage(
+ incoming=True, func=lambda e: bool(e.mentioned or e.is_private)
+ ),
+ )
diff --git a/plugins/aiwrapper.py b/plugins/aiwrapper.py
new file mode 100644
index 0000000000000000000000000000000000000000..c5fa34cf76ae84aa00a559a553671c8a4f02e8bf
--- /dev/null
+++ b/plugins/aiwrapper.py
@@ -0,0 +1,396 @@
+# Ultroid - UserBot
+# Copyright (C) 2021-2025 TeamUltroid
+#
+# This file is a part of < https://github.com/TeamUltroid/Ultroid/ >
+# PLease read the GNU Affero General Public License in
+# .
+
+"""
+✘ Commands Available -
+
+• `{i}gemini `
+ Get response from Google Gemini.
+
+• `{i}antr `
+ Get response from Anthropic Claude.
+
+• `{i}gpt `
+ Get response from OpenAI GPT.
+
+• `{i}deepseek `
+ Get response from DeepSeek AI.
+
+Set custom models using:
+ • OPENAI_MODEL
+ • ANTHROPIC_MODEL
+ • GEMINI_MODEL
+ • DEEPSEEK_MODEL
+"""
+
+import json
+from . import LOGS, eor, get_string, udB, ultroid_cmd, async_searcher
+import aiohttp
+import asyncio
+
+
+ENDPOINTS = {
+ "gpt": "https://api.openai.com/v1/chat/completions",
+ "antr": "https://api.anthropic.com/v1/messages",
+ "gemini": "https://generativelanguage.googleapis.com/v1/models/gemini-pro:generateContent",
+ "deepseek": "https://api.deepseek.com/chat/completions"
+}
+
+DEFAULT_MODELS = {
+ "gpt": "gpt-3.5-turbo",
+ "antr": "claude-3-opus-20240229",
+ "gemini": "gemini-pro",
+ "deepseek": "deepseek-chat"
+}
+
+
+def get_model(provider):
+ """Get model name from database or use default"""
+ model_keys = {
+ "gpt": "OPENAI_MODEL",
+ "antr": "ANTHROPIC_MODEL",
+ "gemini": "GEMINI_MODEL",
+ "deepseek": "DEEPSEEK_MODEL"
+ }
+ return udB.get_key(model_keys[provider]) or DEFAULT_MODELS[provider]
+
+
+async def stream_response(msg, text):
+ """Stream response by editing message"""
+ current = ""
+ # Split into chunks of ~100 characters at word boundaries
+ words = text.split()
+ chunks = []
+ current_chunk = []
+
+ for word in words:
+ current_chunk.append(word)
+ if len(" ".join(current_chunk)) > 100:
+ chunks.append(" ".join(current_chunk[:-1]))
+ current_chunk = [word]
+ if current_chunk:
+ chunks.append(" ".join(current_chunk))
+
+ for chunk in chunks:
+ current += chunk + " "
+ try:
+ await msg.edit(current)
+ except Exception:
+ pass
+ await asyncio.sleep(0.5)
+ return current
+
+
+async def get_ai_response(provider, prompt, api_key, stream=False):
+ """Get response from AI provider"""
+ try:
+ headers = {"Content-Type": "application/json"}
+ model = get_model(provider)
+
+ if provider == "gpt":
+ headers["Authorization"] = f"Bearer {api_key}"
+ data = {
+ "model": model,
+ "messages": [{"role": "user", "content": prompt}],
+ "stream": stream
+ }
+ if not stream:
+ response = await async_searcher(
+ ENDPOINTS[provider],
+ headers=headers,
+ post=True,
+ json=data,
+ re_json=True
+ )
+ yield response["choices"][0]["message"]["content"]
+ return
+
+ async with aiohttp.ClientSession() as session:
+ async with session.post(
+ ENDPOINTS[provider],
+ headers=headers,
+ json=data
+ ) as resp:
+ async for line in resp.content:
+ if line:
+ try:
+ json_line = json.loads(line.decode('utf-8').strip().strip('data:').strip())
+ if 'choices' in json_line and json_line['choices']:
+ content = json_line['choices'][0].get('delta', {}).get('content', '')
+ if content:
+ yield content
+ except Exception:
+ continue
+
+ elif provider == "antr":
+ headers["x-api-key"] = api_key
+ headers["anthropic-version"] = "2023-06-01"
+ data = {
+ "model": model,
+ "messages": [{"role": "user", "content": prompt}],
+ "stream": stream
+ }
+ if not stream:
+ response = await async_searcher(
+ ENDPOINTS[provider],
+ headers=headers,
+ post=True,
+ json=data,
+ re_json=True
+ )
+ yield response["content"][0]["text"]
+ return
+
+ async with aiohttp.ClientSession() as session:
+ async with session.post(
+ ENDPOINTS[provider],
+ headers=headers,
+ json=data
+ ) as resp:
+ async for line in resp.content:
+ if line:
+ try:
+ json_line = json.loads(line.decode('utf-8').strip())
+ if 'content' in json_line:
+ content = json_line['content'][0]['text']
+ if content:
+ yield content
+ except Exception:
+ continue
+
+ elif provider == "gemini":
+ params = {"key": api_key}
+ data = {
+ "contents": [{
+ "parts": [{"text": prompt}]
+ }]
+ }
+ response = await async_searcher(
+ ENDPOINTS[provider],
+ params=params,
+ headers=headers,
+ post=True,
+ json=data,
+ re_json=True
+ )
+ text = response["candidates"][0]["content"]["parts"][0]["text"]
+ if not stream:
+ yield text
+ return
+
+ # Simulate streaming by yielding chunks
+ words = text.split()
+ buffer = []
+ for word in words:
+ buffer.append(word)
+ if len(' '.join(buffer)) > 20: # Adjust chunk size as needed
+ yield ' '.join(buffer) + ' '
+ buffer = []
+ if buffer:
+ yield ' '.join(buffer)
+
+ elif provider == "deepseek":
+ headers["Authorization"] = f"Bearer {api_key}"
+ data = {
+ "model": model,
+ "messages": [{"role": "user", "content": prompt}],
+ "stream": stream
+ }
+ if not stream:
+ response = await async_searcher(
+ ENDPOINTS[provider],
+ headers=headers,
+ post=True,
+ json=data,
+ re_json=True
+ )
+ yield response["choices"][0]["message"]["content"]
+ return
+
+ async with aiohttp.ClientSession() as session:
+ async with session.post(
+ ENDPOINTS[provider],
+ headers=headers,
+ json=data
+ ) as resp:
+ async for line in resp.content:
+ if line:
+ try:
+ json_line = json.loads(line.decode('utf-8').strip())
+ if 'choices' in json_line and json_line['choices']:
+ content = json_line['choices'][0].get('delta', {}).get('content', '')
+ if content:
+ yield content
+ except Exception:
+ continue
+
+ except Exception as e:
+ LOGS.exception(e)
+ yield f"Error: {str(e)}"
+
+
+@ultroid_cmd(pattern="gemini( (.*)|$)")
+async def gemini_ai(event):
+ """Use Google Gemini"""
+ prompt = event.pattern_match.group(1).strip()
+ if not prompt:
+ return await event.eor("❌ Please provide a prompt!")
+
+ api_key = udB.get_key("GEMINI_API_KEY")
+ if not api_key:
+ return await event.eor("⚠️ Please set Gemini API key using `setdb GEMINI_API_KEY your_api_key`")
+
+ msg = await event.eor("🤔 Thinking...")
+ model = get_model("gemini")
+
+ header = (
+ "🤖 **Google Gemini**\n"
+ f"**Model:** `{model}`\n"
+ "➖➖➖➖➖➖➖➖➖➖\n\n"
+ f"**🔍 Prompt:**\n{prompt}\n\n"
+ "**💡 Response:**\n"
+ )
+
+ if event.client.me.bot:
+ await msg.edit(header)
+ response = ""
+ async for chunk in get_ai_response("gemini", prompt, api_key, stream=True):
+ response += chunk
+ try:
+ await msg.edit(header + response)
+ except Exception:
+ pass
+ else:
+ response = ""
+ async for chunk in get_ai_response("gemini", prompt, api_key, stream=True):
+ response += chunk
+ try:
+ await msg.edit(header + response)
+ except Exception:
+ pass
+
+@ultroid_cmd(pattern="antr( (.*)|$)")
+async def anthropic_ai(event):
+ """Use Anthropic Claude"""
+ prompt = event.pattern_match.group(1).strip()
+ if not prompt:
+ return await event.eor("❌ Please provide a prompt!")
+
+ api_key = udB.get_key("ANTHROPIC_KEY")
+ if not api_key:
+ return await event.eor("⚠️ Please set Anthropic API key using `setdb ANTHROPIC_KEY your_api_key`")
+
+ msg = await event.eor("🤔 Thinking...")
+ model = get_model("antr")
+
+ formatted_response = (
+ "🧠 **Anthropic Claude**\n"
+ f"**Model:** `{model}`\n"
+ "➖➖➖➖➖➖➖➖➖➖\n\n"
+ f"**🔍 Prompt:**\n{prompt}\n\n"
+ f"**💡 Response:**\n"
+ )
+
+ if event.client.me.bot:
+ await msg.edit(formatted_response)
+ response = ""
+ async for chunk in get_ai_response("antr", prompt, api_key, stream=True):
+ response += chunk
+ try:
+ await msg.edit(formatted_response + response)
+ except Exception:
+ pass
+ else:
+ response = ""
+ async for chunk in get_ai_response("antr", prompt, api_key, stream=True):
+ response += chunk
+ try:
+ await msg.edit(formatted_response + response)
+ except Exception:
+ pass
+
+@ultroid_cmd(pattern="gpt( (.*)|$)")
+async def openai_ai(event):
+ """Use OpenAI GPT"""
+ prompt = event.pattern_match.group(1).strip()
+ if not prompt:
+ return await event.eor("❌ Please provide a prompt!")
+
+ api_key = udB.get_key("OPENAI_API_KEY")
+ if not api_key:
+ return await event.eor("⚠️ Please set GPT API key using `setdb OPENAI_API_KEY your_api_key`")
+
+ msg = await event.eor("🤔 Thinking...")
+ model = get_model("gpt")
+
+ header = (
+ "🌟 **OpenAI GPT**\n"
+ f"**Model:** `{model}`\n"
+ "➖➖➖➖➖➖➖➖➖➖\n\n"
+ f"**🔍 Prompt:**\n{prompt}\n\n"
+ "**💡 Response:**\n"
+ )
+
+ if event.client.me.bot:
+ await msg.edit(header)
+ response = ""
+ async for chunk in get_ai_response("gpt", prompt, api_key, stream=True):
+ response += chunk
+ try:
+ await msg.edit(header + response)
+ except Exception:
+ pass
+ else:
+ response =""
+ async for chunk in get_ai_response("gpt", prompt, api_key, stream=True):
+ response += chunk
+ try:
+ await msg.edit(header + response)
+ except Exception:
+ pass
+
+@ultroid_cmd(pattern="deepseek( (.*)|$)")
+async def deepseek_ai(event):
+ """Use DeepSeek AI"""
+ prompt = event.pattern_match.group(1).strip()
+ if not prompt:
+ return await event.eor("❌ Please provide a prompt!")
+
+ api_key = udB.get_key("DEEPSEEK_API_KEY")
+ if not api_key:
+ return await event.eor("⚠️ Please set DeepSeek API key using `setdb DEEPSEEK_API_KEY your_api_key`")
+
+ msg = await event.eor("🤔 Thinking...")
+ model = get_model("deepseek")
+
+ formatted_response = (
+ "🤖 **DeepSeek AI**\n"
+ f"**Model:** `{model}`\n"
+ "➖➖➖➖➖➖➖➖➖➖\n\n"
+ f"**🔍 Prompt:**\n{prompt}\n\n"
+ f"**💡 Response:**\n"
+ )
+
+ if event.client.me.bot:
+ await msg.edit(formatted_response)
+ response = ""
+ async for chunk in get_ai_response("deepseek", prompt, api_key, stream=True):
+ response += chunk
+ try:
+ await msg.edit(formatted_response + response)
+ except Exception:
+ pass
+ else:
+ response = ""
+ async for chunk in get_ai_response("deepseek", prompt, api_key, stream=True):
+ response += chunk
+
+ try:
+ await msg.edit(formatted_response + response)
+ except Exception:
+ pass
+
diff --git a/plugins/antiflood.py b/plugins/antiflood.py
new file mode 100644
index 0000000000000000000000000000000000000000..3eb9d19dc0ed803fe23c97c79c86fe067d3d588c
--- /dev/null
+++ b/plugins/antiflood.py
@@ -0,0 +1,121 @@
+# Ultroid - UserBot
+# Copyright (C) 2021-2025 TeamUltroid
+#
+# This file is a part of < https://github.com/TeamUltroid/Ultroid/ >
+# PLease read the GNU Affero General Public License in
+# .
+
+from . import get_help
+
+__doc__ = get_help("help_antiflood")
+
+
+import re
+
+from telethon.events import NewMessage as NewMsg
+
+from pyUltroid.dB import DEVLIST
+from pyUltroid.dB.antiflood_db import get_flood, get_flood_limit, rem_flood, set_flood
+from pyUltroid.fns.admins import admin_check
+
+from . import Button, Redis, asst, callback, eod, get_string, ultroid_bot, ultroid_cmd
+
+_check_flood = {}
+
+if Redis("ANTIFLOOD"):
+
+ @ultroid_bot.on(
+ NewMsg(
+ chats=list(get_flood().keys()),
+ ),
+ )
+ async def flood_checm(event):
+ count = 1
+ chat = (await event.get_chat()).title
+ if event.chat_id in _check_flood.keys():
+ if event.sender_id == list(_check_flood[event.chat_id].keys())[0]:
+ count = _check_flood[event.chat_id][event.sender_id]
+ _check_flood[event.chat_id] = {event.sender_id: count + 1}
+ else:
+ _check_flood[event.chat_id] = {event.sender_id: count}
+ else:
+ _check_flood[event.chat_id] = {event.sender_id: count}
+ if await admin_check(event, silent=True) or getattr(event.sender, "bot", None):
+ return
+ if event.sender_id in DEVLIST:
+ return
+ if _check_flood[event.chat_id][event.sender_id] >= int(
+ get_flood_limit(event.chat_id)
+ ):
+ try:
+ name = event.sender.first_name
+ await event.client.edit_permissions(
+ event.chat_id, event.sender_id, send_messages=False
+ )
+ del _check_flood[event.chat_id]
+ await event.reply(f"#AntiFlood\n\n{get_string('antiflood_3')}")
+ await asst.send_message(
+ int(Redis("LOG_CHANNEL")),
+ f"#Antiflood\n\n`Muted `[{name}](tg://user?id={event.sender_id})` in {chat}`",
+ buttons=Button.inline(
+ "Unmute", data=f"anti_{event.sender_id}_{event.chat_id}"
+ ),
+ )
+ except BaseException:
+ pass
+
+
+@callback(
+ re.compile(
+ "anti_(.*)",
+ ),
+)
+async def unmuting(e):
+ ino = (e.data_match.group(1)).decode("UTF-8").split("_")
+ user = int(ino[0])
+ chat = int(ino[1])
+ user_name = (await ultroid_bot.get_entity(user)).first_name
+ chat_title = (await ultroid_bot.get_entity(chat)).title
+ await ultroid_bot.edit_permissions(chat, user, send_messages=True)
+ await e.edit(
+ f"#Antiflood\n\n`Unmuted `[{user_name}](tg://user?id={user})` in {chat_title}`"
+ )
+
+
+@ultroid_cmd(
+ pattern="setflood ?(\\d+)",
+ admins_only=True,
+)
+async def setflood(e):
+ input_ = e.pattern_match.group(1).strip()
+ if not input_:
+ return await e.eor("`What?`", time=5)
+ if not input_.isdigit():
+ return await e.eor(get_string("com_3"), time=5)
+ if m := set_flood(e.chat_id, input_):
+ return await eod(e, get_string("antiflood_4").format(input_))
+
+
+@ultroid_cmd(
+ pattern="remflood$",
+ admins_only=True,
+)
+async def remove_flood(e):
+ hmm = rem_flood(e.chat_id)
+ try:
+ del _check_flood[e.chat_id]
+ except BaseException:
+ pass
+ if hmm:
+ return await e.eor(get_string("antiflood_1"), time=5)
+ await e.eor(get_string("antiflood_2"), time=5)
+
+
+@ultroid_cmd(
+ pattern="getflood$",
+ admins_only=True,
+)
+async def getflood(e):
+ if ok := get_flood_limit(e.chat_id):
+ return await e.eor(get_string("antiflood_5").format(ok), time=5)
+ await e.eor(get_string("antiflood_2"), time=5)
diff --git a/plugins/asstcmd.py b/plugins/asstcmd.py
new file mode 100644
index 0000000000000000000000000000000000000000..02a9945c1ca04026343c2d915a86fe4c8f42bfd3
--- /dev/null
+++ b/plugins/asstcmd.py
@@ -0,0 +1,100 @@
+# Ultroid - UserBot
+# Copyright (C) 2021-2025 TeamUltroid
+#
+# This file is a part of < https://github.com/TeamUltroid/Ultroid/ >
+# PLease read the GNU Affero General Public License in
+# .
+
+from . import get_help
+
+__doc__ = get_help("help_asstcmd")
+
+import os
+
+from pyUltroid.dB.asstcmd_db import add_cmd, cmd_reply, list_cmds, rem_cmd
+from pyUltroid.fns.tools import create_tl_btn, format_btn, get_msg_button
+from telethon import events, utils
+
+from . import asst, get_string, mediainfo, udB, ultroid_cmd, upload_file
+
+
+@ultroid_cmd(pattern="addcmd( (.*)|$)")
+async def ac(e):
+ wrd = (e.pattern_match.group(1).strip()).lower()
+ wt = await e.get_reply_message()
+ if not (wt and wrd):
+ return await e.eor(get_string("asstcmd_1"), time=5)
+ if "/" in wrd:
+ wrd = wrd.replace("/", "")
+ btn = format_btn(wt.buttons) if wt.buttons else None
+ if wt and wt.media:
+ wut = mediainfo(wt.media)
+ if wut.startswith(("pic", "gif")):
+ dl = await e.client.download_media(wt.media)
+ m = upload_file(dl)
+ os.remove(dl)
+ elif wut == "video":
+ if wt.media.document.size > 8 * 1000 * 1000:
+ return await e.eor(get_string("com_4"), time=5)
+ dl = await e.client.download_media(wt.media)
+ m = upload_file(dl)
+ os.remove(dl)
+ else:
+ m = utils.pack_bot_file_id(wt.media)
+ if wt.text:
+ txt = wt.text
+ if not btn:
+ txt, btn = get_msg_button(wt.text)
+ add_cmd(wrd, txt, m, btn)
+ else:
+ add_cmd(wrd, None, m, btn)
+ else:
+ txt = wt.text
+ if not btn:
+ txt, btn = get_msg_button(wt.text)
+ add_cmd(wrd, txt, None, btn)
+ asst.add_handler(
+ astcmds,
+ events.NewMessage(
+ func=lambda x: x.text.startswith("/") and x.text[1:] in list(list_cmds())
+ ),
+ )
+ await e.eor(get_string("asstcmd_4").format(wrd))
+
+
+@ultroid_cmd(pattern="remcmd( (.*)|$)")
+async def rc(e):
+ wrd = (e.pattern_match.group(1).strip()).lower()
+ if not wrd:
+ return await e.eor(get_string("asstcmd_2"), time=5)
+ wrd = wrd.replace("/", "")
+ rem_cmd(wrd)
+ await e.eor(get_string("asstcmd_3").format(wrd))
+
+
+@ultroid_cmd(pattern="listcmd$")
+async def lscmd(e):
+ if list_cmds():
+ ok = get_string("asstcmd_6")
+ for x in list_cmds():
+ ok += f"/{x}" + "\n"
+ return await e.eor(ok)
+ return await e.eor(get_string("asstcmd_5"))
+
+
+async def astcmds(e):
+ xx = (e.text.replace("/", "")).lower().split()[0]
+ if cmd_reply(xx):
+ msg, media, bt = cmd_reply(xx)
+ if bt:
+ bt = create_tl_btn(bt)
+ await e.reply(msg, file=media, buttons=bt)
+
+
+if udB.get_key("ASST_CMDS"):
+ asst.add_handler(
+ astcmds,
+ events.NewMessage(
+ func=lambda x: x.text.startswith("/") and x.text[1:] in list(list_cmds())
+ ),
+ )
diff --git a/plugins/audiotools.py b/plugins/audiotools.py
new file mode 100644
index 0000000000000000000000000000000000000000..40343bbc8c2b59189d6e6b48081d76090a3a5b85
--- /dev/null
+++ b/plugins/audiotools.py
@@ -0,0 +1,163 @@
+# Ultroid - UserBot
+# Copyright (C) 2021-2025 TeamUltroid
+#
+# This file is a part of < https://github.com/TeamUltroid/Ultroid/ >
+# PLease read the GNU Affero General Public License in
+# .
+
+
+import os
+import time
+from datetime import datetime as dt
+
+from pyUltroid.fns.tools import set_attributes
+
+from . import (
+ LOGS,
+ ULTConfig,
+ bash,
+ downloader,
+ eod,
+ eor,
+ genss,
+ get_help,
+ get_string,
+ humanbytes,
+ mediainfo,
+ stdr,
+ time_formatter,
+ ultroid_cmd
+)
+
+__doc__ = get_help("help_audiotools")
+
+
+@ultroid_cmd(pattern="makevoice$")
+async def vnc(e):
+ if not e.reply_to:
+ return await eod(e, get_string("audiotools_1"))
+ r = await e.get_reply_message()
+ if not mediainfo(r.media).startswith(("audio", "video")):
+ return await eod(e, get_string("spcltool_1"))
+ xxx = await e.eor(get_string("com_1"))
+ file, _ = await e.client.fast_downloader(
+ r.document,
+ )
+ await xxx.edit(get_string("audiotools_2"))
+ await bash(
+ f"ffmpeg -i '{file.name}' -map 0:a -codec:a libopus -b:a 100k -vbr on out.opus"
+ )
+ try:
+ await e.client.send_message(
+ e.chat_id, file="out.opus", force_document=False, reply_to=r
+ )
+ except Exception as er:
+ LOGS.exception(er)
+ return await xxx.edit("`Failed to convert in Voice...`")
+ await xxx.delete()
+ os.remove(file.name)
+ os.remove("out.opus")
+
+
+@ultroid_cmd(pattern="atrim( (.*)|$)")
+async def trim_aud(e):
+ sec = e.pattern_match.group(1).strip()
+ if not sec or "-" not in sec:
+ return await eod(e, get_string("audiotools_3"))
+ a, b = sec.split("-")
+ if int(a) >= int(b):
+ return await eod(e, get_string("audiotools_4"))
+ vido = await e.get_reply_message()
+ if vido and vido.media and mediainfo(vido.media).startswith(("video", "audio")):
+ if hasattr(vido.media, "document"):
+ vfile = vido.media.document
+ name = vido.file.name
+ else:
+ vfile = vido.media
+ name = ""
+ if not name:
+ name = dt.now().isoformat("_", "seconds") + ".mp4"
+ xxx = await e.eor(get_string("audiotools_5"))
+ c_time = time.time()
+ file = await downloader(
+ f"resources/downloads/{name}",
+ vfile,
+ xxx,
+ c_time,
+ f"Downloading {name}...",
+ )
+
+ o_size = os.path.getsize(file.name)
+ d_time = time.time()
+ diff = time_formatter((d_time - c_time) * 1000)
+ file_name = (file.name).split("/")[-1]
+ out = file_name.replace(file_name.split(".")[-1], "_trimmed.aac")
+ if int(b) > int(await genss(file.name)):
+ os.remove(file.name)
+ return await eod(xxx, get_string("audiotools_6"))
+ ss, dd = stdr(int(a)), stdr(int(b))
+ xxx = await xxx.edit(
+ f"Downloaded `{file.name}` of `{humanbytes(o_size)}` in `{diff}`.\n\nNow Trimming Audio from `{ss}` to `{dd}`..."
+ )
+ cmd = f'ffmpeg -i "{file.name}" -preset ultrafast -ss {ss} -to {dd} -vn -acodec copy "{out}" -y'
+ await bash(cmd)
+ os.remove(file.name)
+ f_time = time.time()
+ n_file, _ = await e.client.fast_uploader(
+ out, show_progress=True, event=e, message="Uploading...", to_delete=True
+ )
+ attributes = await set_attributes(out)
+
+ caption = get_string("audiotools_7").format(ss, dd)
+ await e.client.send_file(
+ e.chat_id,
+ n_file,
+ thumb=ULTConfig.thumb,
+ caption=caption,
+ attributes=attributes,
+ force_document=False,
+ reply_to=e.reply_to_msg_id,
+ )
+ await xxx.delete()
+ else:
+ await e.eor(get_string("audiotools_1"), time=5)
+
+
+@ultroid_cmd(pattern="extractaudio$")
+async def ex_aud(e):
+ reply = await e.get_reply_message()
+ if not (reply and reply.media and mediainfo(reply.media).startswith("video")):
+ return await e.eor(get_string("audiotools_8"))
+ name = reply.file.name or "video.mp4"
+ vfile = reply.media.document
+ msg = await e.eor(get_string("com_1"))
+ c_time = time.time()
+ file = await downloader(
+ f"resources/downloads/{name}",
+ vfile,
+ msg,
+ c_time,
+ f"Downloading {name}...",
+ )
+
+ out_file = f"{file.name}.aac"
+ cmd = f"ffmpeg -i {file.name} -vn -acodec copy {out_file}"
+ o, err = await bash(cmd)
+ os.remove(file.name)
+ attributes = await set_attributes(out_file)
+
+ f_time = time.time()
+ try:
+ n_file, _ = await e.client.fast_uploader(
+ out_file, show_progress=True, event=e, message="Uploading...", to_delete=True
+ )
+
+ except FileNotFoundError:
+ return await eor(msg, get_string("audiotools_9"))
+ await e.reply(
+ get_string("audiotools_10"),
+ file=n_file,
+ thumb=ULTConfig.thumb,
+ attributes=attributes,
+ )
+ await msg.delete()
diff --git a/plugins/autoban.py b/plugins/autoban.py
new file mode 100644
index 0000000000000000000000000000000000000000..ab002ba64a5e19a11700ec2c7e528430524ff595
--- /dev/null
+++ b/plugins/autoban.py
@@ -0,0 +1,59 @@
+# Ultroid - UserBot
+# Copyright (C) 2021-2025 TeamUltroid
+#
+# This file is a part of < https://github.com/TeamUltroid/Ultroid/ >
+# PLease read the GNU Affero General Public License in
+# .
+
+from . import get_help
+
+__doc__ = get_help("help_autoban")
+
+from telethon import events
+
+from pyUltroid.dB.base import KeyManager
+
+from . import LOGS, asst, ultroid_bot, ultroid_cmd
+
+Keym = KeyManager("DND_CHATS", cast=list)
+
+
+def join_func(e):
+ return e.user_joined and Keym.contains(e.chat_id)
+
+
+async def dnd_func(event):
+ for user in event.users:
+ try:
+ await (await event.client.kick_participant(event.chat_id, user)).delete()
+ except Exception as ex:
+ LOGS.error("Error in DND:")
+ LOGS.exception(ex)
+ await event.delete()
+
+
+@ultroid_cmd(
+ pattern="autokick (on|off)$",
+ admins_only=True,
+ manager=True,
+ require="ban_users",
+ fullsudo=True,
+)
+async def _(event):
+ match = event.pattern_match.group(1)
+ if match == "on":
+ if Keym.contains(event.chat_id):
+ return await event.eor("`Chat already in do not disturb mode.`", time=3)
+ Keym.add(event.chat_id)
+ event.client.add_handler(dnd_func, events.ChatAction(func=join_func))
+ await event.eor("`Do not disturb mode activated for this chat.`", time=3)
+ elif match == "off":
+ if not Keym.contains(event.chat_id):
+ return await event.eor("`Chat is not in do not disturb mode.`", time=3)
+ Keym.remove(event.chat_id)
+ await event.eor("`Do not disturb mode deactivated for this chat.`", time=3)
+
+
+if Keym.get():
+ ultroid_bot.add_handler(dnd_func, events.ChatAction(func=join_func))
+ asst.add_handler(dnd_func, events.ChatAction(func=join_func))
diff --git a/plugins/autopic.py b/plugins/autopic.py
new file mode 100644
index 0000000000000000000000000000000000000000..e206a98094d7d722771e5d6b8868703e2c806d11
--- /dev/null
+++ b/plugins/autopic.py
@@ -0,0 +1,84 @@
+# Ultroid - UserBot
+# Copyright (C) 2021-2025 TeamUltroid
+#
+# This file is a part of < https://github.com/TeamUltroid/Ultroid/ >
+# PLease read the GNU Affero General Public License in
+# .
+
+
+import asyncio
+import os
+import random
+from random import shuffle
+
+from telethon.tl.functions.photos import UploadProfilePhotoRequest
+
+from pyUltroid.fns.helper import download_file
+from pyUltroid.fns.tools import get_google_images
+
+from . import LOGS, get_help, get_string, udB, ultroid_bot, ultroid_cmd
+
+__doc__ = get_help("help_autopic")
+
+
+@ultroid_cmd(pattern="autopic( (.*)|$)")
+async def autopic(e):
+ search = e.pattern_match.group(1).strip()
+ if udB.get_key("AUTOPIC") and not search:
+ udB.del_key("AUTOPIC")
+ return await e.eor(get_string("autopic_5"))
+ if not search:
+ return await e.eor(get_string("autopic_1"), time=5)
+ e = await e.eor(get_string("com_1"))
+ gi = googleimagesdownload()
+ args = {
+ "keywords": search,
+ "limit": 50,
+ "format": "jpg",
+ "output_directory": "./resources/downloads/",
+ }
+ try:
+ pth = await gi.download(args)
+ ok = pth[0][search]
+ except Exception as er:
+ LOGS.exception(er)
+ return await e.eor(str(er))
+ if not ok:
+ return await e.eor(get_string("autopic_2").format(search), time=5)
+ await e.eor(get_string("autopic_3").format(search))
+ udB.set_key("AUTOPIC", search)
+ SLEEP_TIME = udB.get_key("SLEEP_TIME") or 1221
+ while True:
+ for lie in ok:
+ if udB.get_key("AUTOPIC") != search:
+ return
+ file = await e.client.upload_file(lie)
+ await e.client(UploadProfilePhotoRequest(file))
+ await asyncio.sleep(SLEEP_TIME)
+ shuffle(ok)
+
+
+if search := udB.get_key("AUTOPIC"):
+ images = {}
+ sleep = udB.get_key("SLEEP_TIME") or 1221
+
+ async def autopic_func():
+ search = udB.get_key("AUTOPIC")
+ if images.get(search) is None:
+ images[search] = await get_google_images(search)
+ if not images.get(search):
+ return
+ img = random.choice(images[search])
+ filee = await download_file(img["original"], "resources/downloads/autopic.jpg")
+ file = await ultroid_bot.upload_file(filee)
+ await ultroid_bot(UploadProfilePhotoRequest(file))
+ os.remove(filee)
+
+ try:
+ from apscheduler.schedulers.asyncio import AsyncIOScheduler
+
+ schedule = AsyncIOScheduler()
+ schedule.add_job(autopic_func, "interval", seconds=sleep)
+ schedule.start()
+ except ModuleNotFoundError as er:
+ LOGS.error(f"autopic: '{er.name}' not installed.")
diff --git a/plugins/beautify.py b/plugins/beautify.py
new file mode 100644
index 0000000000000000000000000000000000000000..e8ecf61756768a3ec9618e7d95b79ee11976a5cf
--- /dev/null
+++ b/plugins/beautify.py
@@ -0,0 +1,197 @@
+# Ultroid - UserBot
+# Copyright (C) 2021-2025 TeamUltroid
+#
+# This file is a part of < https://github.com/TeamUltroid/Ultroid/ >
+# PLease read the GNU Affero General Public License in
+# .
+
+from . import get_help
+
+__doc__ = get_help("help_beautify")
+
+
+import os
+import random
+
+from telethon.utils import get_display_name
+from urllib.parse import urlencode
+from . import Carbon, ultroid_cmd, get_string, inline_mention
+from secrets import token_hex
+
+_colorspath = "resources/colorlist.txt"
+
+if os.path.exists(_colorspath):
+ with open(_colorspath, "r") as f:
+ all_col = f.read().split()
+else:
+ all_col = []
+
+
+@ultroid_cmd(
+ pattern="(rc|c)arbon",
+)
+async def cr_bn(event):
+ xxxx = await event.eor(get_string("com_1"))
+ te = event.pattern_match.group(1)
+ col = random.choice(all_col) if te[0] == "r" else "White"
+ if event.reply_to_msg_id:
+ temp = await event.get_reply_message()
+ if temp.media:
+ b = await event.client.download_media(temp)
+ with open(b) as a:
+ code = a.read()
+ os.remove(b)
+ else:
+ code = temp.message
+ else:
+ try:
+ code = event.text.split(" ", maxsplit=1)[1]
+ except IndexError:
+ return await xxxx.eor(get_string("carbon_2"))
+ xx = await Carbon(code=code, file_name="ultroid_carbon", backgroundColor=col)
+ if isinstance(xx, dict):
+ await xxxx.edit(f"`{xx}`")
+ return
+ await xxxx.delete()
+ await event.reply(
+ f"Carbonised by {inline_mention(event.sender)}",
+ file=xx,
+ )
+
+
+@ultroid_cmd(
+ pattern="ccarbon( (.*)|$)",
+)
+async def crbn(event):
+ match = event.pattern_match.group(1).strip()
+ if not match:
+ return await event.eor(get_string("carbon_3"))
+ msg = await event.eor(get_string("com_1"))
+ if event.reply_to_msg_id:
+ temp = await event.get_reply_message()
+ if temp.media:
+ b = await event.client.download_media(temp)
+ with open(b) as a:
+ code = a.read()
+ os.remove(b)
+ else:
+ code = temp.message
+ else:
+ try:
+ match = match.split(" ", maxsplit=1)
+ code = match[1]
+ match = match[0]
+ except IndexError:
+ return await msg.eor(get_string("carbon_2"))
+ xx = await Carbon(code=code, backgroundColor=match)
+ await msg.delete()
+ await event.reply(
+ f"Carbonised by {inline_mention(event.sender)}",
+ file=xx,
+ )
+
+
+RaySoTheme = [
+ "meadow",
+ "breeze",
+ "raindrop",
+ "candy",
+ "crimson",
+ "falcon",
+ "sunset",
+ "noir",
+ "midnight",
+ "bitmap",
+ "ice",
+ "sand",
+ "forest",
+ "mono",
+]
+
+
+@ultroid_cmd(pattern="rayso")
+async def pass_on(ult):
+ try:
+ from playwright.async_api import async_playwright
+ except ImportError:
+ await ult.eor(
+ "`playwright` is not installed!\nPlease install it to use this command.."
+ )
+ return
+
+ proc = await ult.eor(get_string("com_1"))
+ spli = ult.text.split()
+ theme, dark, title, text = None, True, get_display_name(ult.chat), None
+ if len(spli) > 1:
+ if spli[1] in RaySoTheme:
+ theme = spli[1]
+ if len(spli) > 2:
+ text = " ".join(spli[2:])
+ else:
+ text = " ".join(spli[1:])
+ if ult.is_reply:
+ try:
+ msg = await ult.get_reply_message()
+ text = msg.message if not text else text
+ title = get_display_name(msg.sender)
+ if not theme and spli[1] in RaySoTheme:
+ theme = spli[1]
+ except Exception as sam:
+ LOGS.exception(sam)
+ if not text:
+ await proc.eor("No text to beautify!")
+ return
+ if not theme:
+ theme = random.choice(RaySoTheme)
+ cleaned_text = "\n".join([line.strip() for line in text.splitlines()])
+ name = token_hex(8) + ".png"
+ data = {"darkMode": dark, "theme": theme, "title": title}
+ url = f"https://ray.so/#{urlencode(data)}"
+ async with async_playwright() as play:
+ try:
+ browser = await play.chromium.launch()
+ page = await browser.new_page()
+ await page.goto(url)
+ await page.wait_for_load_state("networkidle")
+ try:
+ await page.wait_for_selector(
+ "div[class*='Editor_editor__']", timeout=60000
+ )
+ editor = await page.query_selector("div[class*='Editor_editor__']")
+ await editor.focus()
+ await editor.click()
+
+ for line in cleaned_text.split("\n"):
+ await page.keyboard.type(line)
+ await page.keyboard.press("Enter")
+
+ await page.evaluate(
+ """() => {
+ const button = document.querySelector('button[aria-label="Export as PNG"]');
+ button.click();
+ }"""
+ )
+
+ async with page.expect_download() as download_info:
+ download = await download_info.value
+ await download.save_as(name)
+ except playwright._impl._errors.TimeoutError:
+ LOGS.error("Timeout error: Selector not found within 60 seconds.")
+ await proc.eor("Failed to find the editor within 60 seconds.")
+ return
+ except Exception as e:
+ LOGS.error(f"Error occurred during playwright operation: {e}")
+ await proc.eor("An error occurred during the operation.")
+ return
+ finally:
+ if os.path.exists(name):
+ try:
+ await ult.reply(file=name)
+ await proc.try_delete()
+ os.remove(name)
+ except Exception as e:
+ LOGS.error(f"Error occurred while replying with the file: {e}")
+ await proc.eor("Failed to send the file.")
+ else:
+ LOGS.error(f"Error: File {name} not found or inaccessible.")
+ await proc.eor("Failed to save the file.")
diff --git a/plugins/blacklist.py b/plugins/blacklist.py
new file mode 100644
index 0000000000000000000000000000000000000000..e621a37e8f579898b593d6b10f9b2879d2d6afe3
--- /dev/null
+++ b/plugins/blacklist.py
@@ -0,0 +1,69 @@
+# Ultroid - UserBot
+# Copyright (C) 2021-2025 TeamUltroid
+#
+# This file is a part of < https://github.com/TeamUltroid/Ultroid/ >
+# PLease read the GNU Affero General Public License in
+# .
+
+from . import get_help
+
+__doc__ = get_help("help_blacklist")
+
+
+from pyUltroid.dB.blacklist_db import (
+ add_blacklist,
+ get_blacklist,
+ list_blacklist,
+ rem_blacklist,
+)
+
+from . import events, get_string, udB, ultroid_bot, ultroid_cmd
+
+
+@ultroid_cmd(pattern="blacklist( (.*)|$)", admins_only=True)
+async def af(e):
+ wrd = e.pattern_match.group(1).strip()
+ chat = e.chat_id
+ if not (wrd):
+ return await e.eor(get_string("blk_1"), time=5)
+ wrd = e.text[11:]
+ heh = wrd.split(" ")
+ for z in heh:
+ add_blacklist(int(chat), z.lower())
+ ultroid_bot.add_handler(blacklist, events.NewMessage(incoming=True))
+ await e.eor(get_string("blk_2").format(wrd))
+
+
+@ultroid_cmd(pattern="remblacklist( (.*)|$)", admins_only=True)
+async def rf(e):
+ wrd = e.pattern_match.group(1).strip()
+ chat = e.chat_id
+ if not wrd:
+ return await e.eor(get_string("blk_3"), time=5)
+ wrd = e.text[14:]
+ heh = wrd.split(" ")
+ for z in heh:
+ rem_blacklist(int(chat), z.lower())
+ await e.eor(get_string("blk_4").format(wrd))
+
+
+@ultroid_cmd(pattern="listblacklist$", admins_only=True)
+async def lsnote(e):
+ if x := list_blacklist(e.chat_id):
+ sd = get_string("blk_5")
+ return await e.eor(sd + x)
+ await e.eor(get_string("blk_6"))
+
+
+async def blacklist(e):
+ if x := get_blacklist(e.chat_id):
+ text = e.text.lower().split()
+ if any((z in text) for z in x):
+ try:
+ await e.delete()
+ except BaseException:
+ pass
+
+
+if udB.get_key("BLACKLIST_DB"):
+ ultroid_bot.add_handler(blacklist, events.NewMessage(incoming=True))
diff --git a/plugins/bot.py b/plugins/bot.py
new file mode 100644
index 0000000000000000000000000000000000000000..a4febdc9090aa3fadfcb95d479d61ea062831d47
--- /dev/null
+++ b/plugins/bot.py
@@ -0,0 +1,366 @@
+# Ultroid - UserBot
+# Copyright (C) 2021-2025 TeamUltroid
+#
+# This file is a part of < https://github.com/TeamUltroid/Ultroid/ >
+# PLease read the GNU Affero General Public License in
+# .
+
+from . import get_help
+
+__doc__ = get_help("help_bot")
+
+import os
+import sys
+import time
+from platform import python_version as pyver
+from random import choice
+
+from telethon import __version__
+from telethon.errors.rpcerrorlist import (
+ BotMethodInvalidError,
+ ChatSendMediaForbiddenError,
+)
+
+from pyUltroid.version import __version__ as UltVer
+
+from . import HOSTED_ON, LOGS
+
+try:
+ from git import Repo
+except ImportError:
+ LOGS.error("bot: 'gitpython' module not found!")
+ Repo = None
+
+from telethon.utils import resolve_bot_file_id
+
+from . import (
+ ATRA_COL,
+ LOGS,
+ OWNER_NAME,
+ ULTROID_IMAGES,
+ Button,
+ Carbon,
+ Telegraph,
+ Var,
+ allcmds,
+ asst,
+ bash,
+ call_back,
+ callback,
+ def_logs,
+ eor,
+ get_string,
+ heroku_logs,
+ in_pattern,
+ inline_pic,
+ restart,
+ shutdown,
+ start_time,
+ time_formatter,
+ udB,
+ ultroid_cmd,
+ ultroid_version,
+ updater,
+)
+
+
+def ULTPIC():
+ return inline_pic() or choice(ULTROID_IMAGES)
+
+
+buttons = [
+ [
+ Button.url(get_string("bot_3"), "https://github.com/TeamUltroid/Ultroid"),
+ Button.url(get_string("bot_4"), "t.me/UltroidSupportChat"),
+ ]
+]
+
+# Will move to strings
+alive_txt = """
+The Ultroid Userbot
+
+ ◍ Version - {}
+ ◍ Py-Ultroid - {}
+ ◍ Telethon - {}
+"""
+
+in_alive = "{}\n\n🌀 Ultroid Version -> {}
\n🌀 PyUltroid -> {}
\n🌀 Python -> {}
\n🌀 Uptime -> {}
\n🌀 Branch ->[ {} ]\n\n• Join @TeamUltroid"
+
+
+@callback("alive")
+async def alive(event):
+ text = alive_txt.format(ultroid_version, UltVer, __version__)
+ await event.answer(text, alert=True)
+
+
+@ultroid_cmd(
+ pattern="alive( (.*)|$)",
+)
+async def lol(ult):
+ match = ult.pattern_match.group(1).strip()
+ inline = None
+ if match in ["inline", "i"]:
+ try:
+ res = await ult.client.inline_query(asst.me.username, "alive")
+ return await res[0].click(ult.chat_id)
+ except BotMethodInvalidError:
+ pass
+ except BaseException as er:
+ LOGS.exception(er)
+ inline = True
+ pic = udB.get_key("ALIVE_PIC")
+ if isinstance(pic, list):
+ pic = choice(pic)
+ uptime = time_formatter((time.time() - start_time) * 1000)
+ header = udB.get_key("ALIVE_TEXT") or get_string("bot_1")
+ y = Repo().active_branch
+ xx = Repo().remotes[0].config_reader.get("url")
+ rep = xx.replace(".git", f"/tree/{y}")
+ kk = f" `[{y}]({rep})` "
+ if inline:
+ kk = f"{y}"
+ parse = "html"
+ als = in_alive.format(
+ header,
+ f"{ultroid_version} [{HOSTED_ON}]",
+ UltVer,
+ pyver(),
+ uptime,
+ kk,
+ )
+
+ if _e := udB.get_key("ALIVE_EMOJI"):
+ als = als.replace("🌀", _e)
+ else:
+ parse = "md"
+ als = (get_string("alive_1")).format(
+ header,
+ OWNER_NAME,
+ f"{ultroid_version} [{HOSTED_ON}]",
+ UltVer,
+ uptime,
+ pyver(),
+ __version__,
+ kk,
+ )
+
+ if a := udB.get_key("ALIVE_EMOJI"):
+ als = als.replace("✵", a)
+ if pic:
+ try:
+ await ult.reply(
+ als,
+ file=pic,
+ parse_mode=parse,
+ link_preview=False,
+ buttons=buttons if inline else None,
+ )
+ return await ult.try_delete()
+ except ChatSendMediaForbiddenError:
+ pass
+ except BaseException as er:
+ LOGS.exception(er)
+ try:
+ await ult.reply(file=pic)
+ await ult.reply(
+ als,
+ parse_mode=parse,
+ buttons=buttons if inline else None,
+ link_preview=False,
+ )
+ return await ult.try_delete()
+ except BaseException as er:
+ LOGS.exception(er)
+ await eor(
+ ult,
+ als,
+ parse_mode=parse,
+ link_preview=False,
+ buttons=buttons if inline else None,
+ )
+
+
+@ultroid_cmd(pattern="ping$", chats=[], type=["official", "assistant"])
+async def _(event):
+ start = time.time()
+ x = await event.eor("Pong !")
+ end = round((time.time() - start) * 1000)
+ uptime = time_formatter((time.time() - start_time) * 1000)
+ await x.edit(get_string("ping").format(end, uptime))
+
+
+@ultroid_cmd(
+ pattern="cmds$",
+)
+async def cmds(event):
+ await allcmds(event, Telegraph)
+
+
+heroku_api = Var.HEROKU_API
+
+
+@ultroid_cmd(
+ pattern="restart$",
+ fullsudo=True,
+)
+async def restartbt(ult):
+ ok = await ult.eor(get_string("bot_5"))
+ call_back()
+ who = "bot" if ult.client._bot else "user"
+ udB.set_key("_RESTART", f"{who}_{ult.chat_id}_{ok.id}")
+ if heroku_api:
+ return await restart(ok)
+ await bash("git pull && pip3 install -r requirements.txt")
+ await bash("pip3 install -r requirements.txt --break-system-packages")
+ if len(sys.argv) > 1:
+ os.execl(sys.executable, sys.executable, "main.py")
+ else:
+ os.execl(sys.executable, sys.executable, "-m", "pyUltroid")
+
+
+@ultroid_cmd(
+ pattern="shutdown$",
+ fullsudo=True,
+)
+async def shutdownbot(ult):
+ await shutdown(ult)
+
+
+@ultroid_cmd(
+ pattern="logs( (.*)|$)",
+ chats=[],
+)
+async def _(event):
+ opt = event.pattern_match.group(1).strip()
+ file = f"ultroid{sys.argv[-1]}.log" if len(sys.argv) > 1 else "ultroid.log"
+ if opt == "heroku":
+ await heroku_logs(event)
+ elif opt == "carbon" and Carbon:
+ event = await event.eor(get_string("com_1"))
+ with open(file, "r") as f:
+ code = f.read()[-2500:]
+ file = await Carbon(
+ file_name="ultroid-logs",
+ code=code,
+ backgroundColor=choice(ATRA_COL),
+ )
+ if isinstance(file, dict):
+ await event.eor(f"`{file}`")
+ return
+ await event.reply("**Ultroid Logs.**", file=file)
+ elif opt == "open":
+ with open("ultroid.log", "r") as f:
+ file = f.read()[-4000:]
+ return await event.eor(f"`{file}`")
+ elif (
+ opt.isdigit() and 5 <= int(opt) <= 100
+ ): # Check if input is a number between 10 and 100
+ num_lines = int(opt)
+ with open("ultroid.log", "r") as f:
+ lines = f.readlines()[-num_lines:]
+ file = "".join(lines)
+ return await event.eor(f"`{file}`")
+ else:
+ await def_logs(event, file)
+ await event.try_delete()
+
+
+@in_pattern("alive", owner=True)
+async def inline_alive(ult):
+ pic = udB.get_key("ALIVE_PIC")
+ if isinstance(pic, list):
+ pic = choice(pic)
+ uptime = time_formatter((time.time() - start_time) * 1000)
+ header = udB.get_key("ALIVE_TEXT") or get_string("bot_1")
+ y = Repo().active_branch
+ xx = Repo().remotes[0].config_reader.get("url")
+ rep = xx.replace(".git", f"/tree/{y}")
+ kk = f"{y}"
+ als = in_alive.format(
+ header, f"{ultroid_version} [{HOSTED_ON}]", UltVer, pyver(), uptime, kk
+ )
+
+ if _e := udB.get_key("ALIVE_EMOJI"):
+ als = als.replace("🌀", _e)
+ builder = ult.builder
+ if pic:
+ try:
+ if ".jpg" in pic:
+ results = [
+ await builder.photo(
+ pic, text=als, parse_mode="html", buttons=buttons
+ )
+ ]
+ else:
+ if _pic := resolve_bot_file_id(pic):
+ pic = _pic
+ buttons.insert(
+ 0, [Button.inline(get_string("bot_2"), data="alive")]
+ )
+ results = [
+ await builder.document(
+ pic,
+ title="Inline Alive",
+ description="@TeamUltroid",
+ parse_mode="html",
+ buttons=buttons,
+ )
+ ]
+ return await ult.answer(results)
+ except BaseException as er:
+ LOGS.exception(er)
+ result = [
+ await builder.article(
+ "Alive", text=als, parse_mode="html", link_preview=False, buttons=buttons
+ )
+ ]
+ await ult.answer(result)
+
+
+@ultroid_cmd(pattern="update( (.*)|$)")
+async def _(e):
+ xx = await e.eor(get_string("upd_1"))
+ if e.pattern_match.group(1).strip() and (
+ "fast" in e.pattern_match.group(1).strip()
+ or "soft" in e.pattern_match.group(1).strip()
+ ):
+ await bash("git pull -f && pip3 install -r requirements.txt")
+ await bash("pip3 install -r requirements.txt --break-system-packages")
+ call_back()
+ await xx.edit(get_string("upd_7"))
+ os.execl(sys.executable, "python3", "-m", "pyUltroid")
+ # return
+ m = await updater()
+ branch = (Repo.init()).active_branch
+ if m:
+ x = await asst.send_file(
+ udB.get_key("LOG_CHANNEL"),
+ ULTPIC(),
+ caption="• **Update Available** •",
+ force_document=False,
+ buttons=Button.inline("Changelogs", data="changes"),
+ )
+ Link = x.message_link
+ await xx.edit(
+ f'[ChangeLogs]',
+ parse_mode="html",
+ link_preview=False,
+ )
+ else:
+ await xx.edit(
+ f'Your BOT is
up-to-date with
[{branch}]',
+ parse_mode="html",
+ link_preview=False,
+ )
+
+
+@callback("updtavail", owner=True)
+async def updava(event):
+ await event.delete()
+ await asst.send_file(
+ udB.get_key("LOG_CHANNEL"),
+ ULTPIC(),
+ caption="• **Update Available** •",
+ force_document=False,
+ buttons=Button.inline("Changelogs", data="changes"),
+ )
diff --git a/plugins/broadcast.py b/plugins/broadcast.py
new file mode 100644
index 0000000000000000000000000000000000000000..4eb1d3672116b08a74441b45de4e7f7681da7f73
--- /dev/null
+++ b/plugins/broadcast.py
@@ -0,0 +1,216 @@
+# Ultroid - UserBot
+# Copyright (C) 2021-2025 TeamUltroid
+#
+# This file is a part of < https://github.com/TeamUltroid/Ultroid/ >
+# PLease read the GNU Affero General Public License in
+# .
+
+
+from . import get_help
+
+__doc__ = get_help("help_broadcast")
+
+import asyncio
+import io
+
+from telethon.utils import get_display_name
+
+from pyUltroid.dB.base import KeyManager
+
+from . import HNDLR, LOGS, eor, get_string, udB, ultroid_bot, ultroid_cmd
+
+KeyM = KeyManager("BROADCAST", cast=list)
+
+
+@ultroid_cmd(
+ pattern="addch( (.*)|$)",
+ allow_sudo=False,
+)
+async def broadcast_adder(event):
+ msgg = event.pattern_match.group(1).strip()
+ x = await event.eor(get_string("bd_1"))
+ if msgg == "all":
+ await x.edit(get_string("bd_2"))
+ chats = [
+ e.entity
+ for e in await event.client.get_dialogs()
+ if (e.is_group or e.is_channel)
+ ]
+ new = 0
+ for i in chats:
+ try:
+ if (
+ i.broadcast
+ and (i.creator or i.admin_rights)
+ and not KeyM.contains(i.id)
+ ):
+ new += 1
+ cid = f"-100{i.id}"
+ KeyM.add(int(cid))
+ except Exception as Ex:
+ LOGS.exception(Ex)
+ await x.edit(get_string("bd_3").format(KeyM.count(), new))
+ return
+ if event.reply_to_msg_id:
+ previous_message = await event.get_reply_message()
+ raw_text = previous_message.text
+ lines = raw_text.split("\n")
+ length = len(lines)
+ for line_number in range(1, length - 2):
+ channel_id = lines[line_number][4:-1]
+ if not KeyM.contains(channel_id):
+ KeyM.add(channel_id)
+ await x.edit(get_string("bd_4"))
+ await asyncio.sleep(3)
+ await event.delete()
+ return
+ chat_id = event.chat_id
+ if chat_id == udB.get_key("LOG_CHANNEL"):
+ return
+ if KeyM.contains(chat_id):
+ await x.edit(get_string("bd_6"))
+ elif xx := KeyM.add(chat_id):
+ await x.edit(get_string("bd_5"))
+ else:
+ await x.edit(get_string("sf_8"))
+ await asyncio.sleep(3)
+ await x.delete()
+
+
+@ultroid_cmd(
+ pattern="remch( (.*)|$)",
+ allow_sudo=False,
+)
+async def broadcast_remover(event):
+ chat_id = event.pattern_match.group(1).strip() or event.chat_id
+ x = await event.eor(get_string("com_1"))
+ if chat_id == "all":
+ await x.edit(get_string("bd_8"))
+ udB.del_key("BROADCAST")
+ await x.edit("Database cleared.")
+ return
+ if KeyM.contains(chat_id):
+ KeyM.remove(chat_id)
+ await x.edit(get_string("bd_7"))
+ else:
+ await x.edit(get_string("bd_9"))
+ await asyncio.sleep(3)
+ await x.delete()
+
+
+@ultroid_cmd(
+ pattern="listchannels$",
+)
+async def list_all(event):
+ x = await event.eor(get_string("com_1"))
+ channels = KeyM.get()
+ num = KeyM.count()
+ if not channels:
+ return await eor(x, "No chats were added.", time=5)
+ msg = "Channels in database:\n"
+ for channel in channels:
+ name = ""
+ try:
+ name = get_display_name(await event.client.get_entity(channel))
+ except ValueError:
+ name = ""
+ msg += f"=> **{name}** [`{channel}`]\n"
+ msg += f"\nTotal {num} channels."
+ if len(msg) > 4096:
+ MSG = msg.replace("*", "").replace("`", "")
+ with io.BytesIO(str.encode(MSG)) as out_file:
+ out_file.name = "channels.txt"
+ await event.reply(
+ "Channels in Database",
+ file=out_file,
+ force_document=True,
+ allow_cache=False,
+ )
+ await x.delete()
+ else:
+ await x.edit(msg)
+
+
+@ultroid_cmd(
+ pattern="forward$",
+ allow_sudo=False,
+)
+async def forw(event):
+ if not event.is_reply:
+ return await event.eor(get_string("ex_1"))
+ ultroid_bot = event.client
+ channels = KeyM.get()
+ x = await event.eor("Sending...")
+ if not channels:
+ return await x.edit(f"Please add channels by using `{HNDLR}add` in them.")
+ error_count = 0
+ sent_count = 0
+ previous_message = await event.get_reply_message()
+ error_count = 0
+ for channel in channels:
+ try:
+ await ultroid_bot.forward_messages(channel, previous_message)
+ sent_count += 1
+ await x.edit(
+ f"Sent : {sent_count}\nError : {error_count}\nTotal : {len(channels)}",
+ )
+ except Exception:
+ try:
+ await ultroid_bot.send_message(
+ udB.get_key("LOG_CHANNEL"),
+ f"Error in sending at {channel}.",
+ )
+ except Exception as Em:
+ LOGS.info(Em)
+ error_count += 1
+ await x.edit(
+ f"Sent : {sent_count}\nError : {error_count}\nTotal : {len(channels)}",
+ )
+ await x.edit(f"{sent_count} messages sent with {error_count} errors.")
+ if error_count > 0:
+ await ultroid_bot.send_message(
+ udB.get_key("LOG_CHANNEL"), f"{error_count} Errors"
+ )
+
+
+@ultroid_cmd(
+ pattern="broadcast( (.*)|$)",
+ allow_sudo=False,
+)
+async def sending(event):
+ x = await event.eor(get_string("com_1"))
+ if not event.is_reply:
+ return await x.edit(get_string("ex_1"))
+ channels = KeyM.get()
+ if not channels:
+ return await x.edit(f"Please add channels by using `{HNDLR}add` in them.")
+ await x.edit("Sending....")
+ if event.reply_to_msg_id:
+ previous_message = await event.get_reply_message()
+ if previous_message.poll:
+ return await x.edit(f"Reply `{HNDLR}forward` for polls.")
+ if previous_message:
+ error_count = 0
+ sent_count = 0
+ for channel in channels:
+ try:
+ await ultroid_bot.send_message(channel, previous_message)
+ sent_count += 1
+ await x.edit(
+ f"Sent : {sent_count}\nError : {error_count}\nTotal : {len(channels)}",
+ )
+ except Exception as error:
+ await ultroid_bot.send_message(
+ udB.get_key("LOG_CHANNEL"),
+ f"Error in sending at {channel}.\n\n{error}",
+ )
+ error_count += 1
+ await x.edit(
+ f"Sent : {sent_count}\nError : {error_count}\nTotal : {len(channels)}",
+ )
+ await x.edit(f"{sent_count} messages sent with {error_count} errors.")
+ if error_count > 0:
+ await ultroid_bot.send_message(
+ udB.get_key("LOG_CHANNEL"),
+ f"{error_count} Errors",
+ )
diff --git a/plugins/button.py b/plugins/button.py
new file mode 100644
index 0000000000000000000000000000000000000000..ef1fbb2a84c22b4b5369ab0cfaadc7a3c08a5ae5
--- /dev/null
+++ b/plugins/button.py
@@ -0,0 +1,54 @@
+# Ultroid - UserBot
+# Copyright (C) 2021-2025 TeamUltroid
+#
+# This file is a part of < https://github.com/TeamUltroid/Ultroid/ >
+# PLease read the GNU Affero General Public License in
+# .
+
+from . import get_help
+
+__doc__ = get_help("help_button")
+
+import os
+
+from . import upload_file as uf
+from telethon.utils import pack_bot_file_id
+
+from pyUltroid.fns.tools import create_tl_btn, get_msg_button
+
+from . import HNDLR, get_string, mediainfo, ultroid_cmd
+from ._inline import something
+
+
+@ultroid_cmd(pattern="button")
+async def butt(event):
+ media, wut, text = None, None, None
+ if event.reply_to:
+ wt = await event.get_reply_message()
+ if wt.text:
+ text = wt.text
+ if wt.media:
+ wut = mediainfo(wt.media)
+ if wut and wut.startswith(("pic", "gif")):
+ dl = await wt.download_media()
+ media = uf(dl)
+ elif wut == "video":
+ if wt.media.document.size > 8 * 1000 * 1000:
+ return await event.eor(get_string("com_4"), time=5)
+ dl = await wt.download_media()
+ media = uf(dl)
+ os.remove(dl)
+ else:
+ media = pack_bot_file_id(wt.media)
+ try:
+ text = event.text.split(maxsplit=1)[1]
+ except IndexError:
+ if not text:
+ return await event.eor(
+ f"**Please give some text in correct format.**\n\n`{HNDLR}help button`",
+ )
+ text, buttons = get_msg_button(text)
+ if buttons:
+ buttons = create_tl_btn(buttons)
+ await something(event, text, media, buttons)
+ await event.delete()
diff --git a/plugins/calculator.py b/plugins/calculator.py
new file mode 100644
index 0000000000000000000000000000000000000000..3514c2963e82bea78f059c63b2314d465d750624
--- /dev/null
+++ b/plugins/calculator.py
@@ -0,0 +1,153 @@
+# Ultroid - UserBot
+# Copyright (C) 2021-2025 TeamUltroid
+#
+# This file is a part of < https://github.com/TeamUltroid/Ultroid/ >
+# PLease read the GNU Affero General Public License in
+# .
+
+
+from . import get_help
+
+__doc__ = get_help("help_calculator")
+
+import re
+
+from . import Button, asst, callback, get_string, in_pattern, udB, ultroid_cmd
+
+CALC = {}
+
+m = [
+ "AC",
+ "C",
+ "⌫",
+ "%",
+ "7",
+ "8",
+ "9",
+ "+",
+ "4",
+ "5",
+ "6",
+ "-",
+ "1",
+ "2",
+ "3",
+ "x",
+ "00",
+ "0",
+ ".",
+ "÷",
+]
+tultd = [Button.inline(f"{x}", data=f"calc{x}") for x in m]
+lst = list(zip(tultd[::4], tultd[1::4], tultd[2::4], tultd[3::4]))
+lst.append([Button.inline("=", data="calc=")])
+
+
+@ultroid_cmd(pattern="calc")
+async def icalc(e):
+ udB.del_key("calc")
+ if e.client._bot:
+ return await e.reply(get_string("calc_1"), buttons=lst)
+ results = await e.client.inline_query(asst.me.username, "calc")
+ await results[0].click(e.chat_id, silent=True, hide_via=True)
+ await e.delete()
+
+
+@in_pattern("calc", owner=True)
+async def _(e):
+ calc = e.builder.article("Calc", text=get_string("calc_1"), buttons=lst)
+ await e.answer([calc])
+
+
+@callback(re.compile("calc(.*)"), owner=True)
+async def _(e):
+ x = (e.data_match.group(1)).decode()
+ user = e.query.user_id
+ get = None
+ if x == "AC":
+ if CALC.get(user):
+ CALC.pop(user)
+ await e.edit(
+ get_string("calc_1"),
+ buttons=[Button.inline(get_string("calc_2"), data="recalc")],
+ )
+ elif x == "C":
+ if CALC.get(user):
+ CALC.pop(user)
+ await e.answer("cleared")
+ elif x == "⌫":
+ if CALC.get(user):
+ get = CALC[user]
+ if get:
+ CALC.update({user: get[:-1]})
+ await e.answer(str(get[:-1]))
+ elif x == "%":
+ if CALC.get(user):
+ get = CALC[user]
+ if get:
+ CALC.update({user: f"{get}/100"})
+ await e.answer(str(f"{get}/100"))
+ elif x == "÷":
+ if CALC.get(user):
+ get = CALC[user]
+ if get:
+ CALC.update({user: f"{get}/"})
+ await e.answer(str(f"{get}/"))
+ elif x == "x":
+ if CALC.get(user):
+ get = CALC[user]
+ if get:
+ CALC.update({user: f"{get}*"})
+ await e.answer(str(f"{get}*"))
+ elif x == "=":
+ if CALC.get(user):
+ get = CALC[user]
+ if get:
+ if get.endswith(("*", ".", "/", "-", "+")):
+ get = get[:-1]
+ out = eval(get)
+ try:
+ num = float(out)
+ await e.answer(f"Answer : {num}", cache_time=0, alert=True)
+ except BaseException:
+ CALC.pop(user)
+ await e.answer(get_string("sf_8"), cache_time=0, alert=True)
+ await e.answer("None")
+ else:
+ if CALC.get(user):
+ get = CALC[user]
+ if get:
+ CALC.update({user: get + x})
+ return await e.answer(str(get + x))
+ CALC.update({user: x})
+ await e.answer(str(x))
+
+
+@callback("recalc", owner=True)
+async def _(e):
+ m = [
+ "AC",
+ "C",
+ "⌫",
+ "%",
+ "7",
+ "8",
+ "9",
+ "+",
+ "4",
+ "5",
+ "6",
+ "-",
+ "1",
+ "2",
+ "3",
+ "x",
+ "00",
+ "0",
+ ".",
+ "÷",
+ ]
+ tultd = [Button.inline(f"{x}", data=f"calc{x}") for x in m]
+ lst = list(zip(tultd[::4], tultd[1::4], tultd[2::4], tultd[3::4]))
+ lst.append([Button.inline("=", data="calc=")])
+ await e.edit(get_string("calc_1"), buttons=lst)
diff --git a/plugins/channelhacks.py b/plugins/channelhacks.py
new file mode 100644
index 0000000000000000000000000000000000000000..561517a435541f50461d07af206e476a0df95f0c
--- /dev/null
+++ b/plugins/channelhacks.py
@@ -0,0 +1,224 @@
+# Ultroid - UserBot
+# Copyright (C) 2021-2025 TeamUltroid
+#
+# This file is a part of < https://github.com/TeamUltroid/Ultroid/ >
+# PLease read the GNU Affero General Public License in
+# .
+from . import get_help
+
+__doc__ = get_help("help_channelhacks")
+
+
+import asyncio
+import io
+
+from telethon.errors.rpcerrorlist import FloodWaitError
+from telethon.utils import get_display_name, get_peer_id
+
+from pyUltroid.dB.base import KeyManager
+
+from . import LOGS, asst, eor, events, get_string, udB, ultroid_bot, ultroid_cmd
+
+ERROR = {}
+SourceM = KeyManager("CH_SOURCE", cast=list)
+DestiM = KeyManager("CH_DESTINATIONS", cast=list)
+
+
+async def autopost_func(e):
+ if not udB.get_key("AUTOPOST"):
+ return
+ x = SourceM.get()
+ th = await e.get_chat()
+ if get_peer_id(th) not in x:
+ return
+ y = DestiM.get()
+ for ys in y:
+ try:
+ await e.client.send_message(int(ys), e.message)
+ except Exception as ex:
+ try:
+ ERROR[str(ex)]
+ except KeyError:
+ ERROR.update({str(ex): ex})
+ Error = f"**Error on AUTOPOST**\n\n`{ex}`"
+ await asst.send_message(udB.get_key("LOG_CHANNEL"), Error)
+
+
+@ultroid_cmd(pattern="shift (.*)")
+async def _(e):
+ x = e.pattern_match.group(1).strip()
+ z = await e.eor(get_string("com_1"))
+ a, b = x.split("|")
+ try:
+ c = await e.client.parse_id(a)
+ except Exception:
+ await z.edit(get_string("cha_1"))
+ return
+ try:
+ d = await e.client.parse_id(b)
+ except Exception as er:
+ LOGS.exception(er)
+ await z.edit(get_string("cha_1"))
+ return
+ async for msg in e.client.iter_messages(int(c), reverse=True):
+ try:
+ await asyncio.sleep(2)
+ await e.client.send_message(int(d), msg)
+ except FloodWaitError as er:
+ await asyncio.sleep(er.seconds + 5)
+ await e.client.send_message(int(d), msg)
+ except BaseException as er:
+ LOGS.exception(er)
+ await z.edit("Done")
+
+
+@ultroid_cmd(pattern="asource (.*)")
+async def source(e):
+ if x := e.pattern_match.group(1).strip():
+ try:
+ y = await e.client.parse_id(x)
+ except Exception as er:
+ LOGS.exception(er)
+ return
+ else:
+ y = e.chat_id
+ if not SourceM.contains(y):
+ SourceM.add(y)
+ await e.eor(get_string("cha_2"))
+ ultroid_bot.add_handler(autopost_func, events.NewMessage())
+ else:
+ await e.eor(get_string("cha_3"))
+
+
+@ultroid_cmd(pattern="dsource( (.*)|$)")
+async def dd(event):
+ chat_id = event.pattern_match.group(1).strip()
+ x = await event.eor(get_string("com_1"))
+ if chat_id == "all":
+ await x.edit(get_string("bd_8"))
+ udB.del_key("CH_SOURCE")
+ await x.edit(get_string("cha_4"))
+ return
+ if chat_id:
+ try:
+ y = await event.client.parse_id(chat_id)
+ except Exception as er:
+ LOGS.exception(er)
+ return
+ else:
+ y = event.chat_id
+ if SourceM.contains(y):
+ SourceM.remove(y)
+ await eor(x, get_string("cha_5"), time=5)
+ else:
+ await eor(x, "Source channel is already removed from database. ", time=3)
+
+
+@ultroid_cmd(pattern="listsource")
+async def list_all(event):
+ x = await event.eor(get_string("com_1"))
+ num = SourceM.count()
+ if not num:
+ return await eor(x, "No chats were added.", time=5)
+ msg = get_string("cha_8")
+ channels = SourceM.get()
+ for channel in channels:
+ name = ""
+ try:
+ name = get_display_name(await event.client.get_entity(int(channel)))
+ except BaseException:
+ name = ""
+ msg += f"\n=> **{name}** [`{channel}`]"
+ msg += f"\nTotal {num} channels."
+ if len(msg) > 4096:
+ MSG = msg.replace("*", "").replace("`", "")
+ with io.BytesIO(str.encode(MSG)) as out_file:
+ out_file.name = "channels.txt"
+ await event.reply(
+ "Channels in database",
+ file=out_file,
+ force_document=True,
+ allow_cache=False,
+ )
+ await x.delete()
+ else:
+ await x.edit(msg)
+
+
+@ultroid_cmd(pattern="adest (.*)")
+async def destination(e):
+ if x := e.pattern_match.group(1).strip():
+ try:
+ y = await e.client.parse_id(x)
+ except Exception as er:
+ LOGS.exception(er)
+ return
+ else:
+ y = e.chat_id
+ if not DestiM.contains(y):
+ DestiM.add(y)
+ await e.eor("Destination added succesfully")
+ else:
+ await e.eor("Destination channel already added")
+
+
+@ultroid_cmd(pattern="ddest( (.*)|$)")
+async def dd(event):
+ chat_id = event.pattern_match.group(1).strip()
+ x = await event.eor(get_string("com_1"))
+ if chat_id == "all":
+ await x.edit(get_string("bd_8"))
+ udB.del_key("CH_DESTINATION")
+ await x.edit("Destinations database cleared.")
+ return
+ if chat_id:
+ try:
+ y = await event.client.parse_id(chat_id)
+ except Exception as er:
+ LOGS.exception(er)
+ return
+ else:
+ y = event.chat_id
+ if DestiM.contains(y):
+ DestiM.remove(y)
+ await eor(x, "Destination removed from database")
+ else:
+ await eor(x, "Destination channel is already removed from database. ", time=5)
+
+
+@ultroid_cmd(pattern="listdest")
+async def list_all(event):
+ ultroid_bot = event.client
+ x = await event.eor(get_string("com_1"))
+ channels = DestiM.get()
+ num = len(channels)
+ if not num:
+ return await eor(x, "No chats were added.", time=5)
+ msg = get_string("cha_7")
+ for channel in channels:
+ name = ""
+ try:
+ name = get_display_name(await ultroid_bot.get_entity(int(channel)))
+ except BaseException:
+ name = ""
+ msg += f"\n=> **{name}** [`{channel}`]"
+ msg += f"\nTotal {num} channels."
+ if len(msg) > 4096:
+ MSG = msg.replace("*", "").replace("`", "")
+ with io.BytesIO(str.encode(MSG)) as out_file:
+ out_file.name = "channels.txt"
+ await ultroid_bot.send_file(
+ event.chat_id,
+ out_file,
+ force_document=True,
+ allow_cache=False,
+ caption="Destination channels in database",
+ reply_to=event,
+ )
+ await x.delete()
+ else:
+ await x.edit(msg)
+
+
+if udB.get_key("AUTOPOST"):
+ ultroid_bot.add_handler(autopost_func, events.NewMessage())
diff --git a/plugins/chatbot.py b/plugins/chatbot.py
new file mode 100644
index 0000000000000000000000000000000000000000..00c96e03e5aebec65c0d18f569cb9dc2dad51fac
--- /dev/null
+++ b/plugins/chatbot.py
@@ -0,0 +1,89 @@
+# Ultroid - UserBot
+# Copyright (C) 2021-2025 TeamUltroid
+#
+# This file is a part of < https://github.com/TeamUltroid/Ultroid/ >
+# PLease read the GNU Affero General Public License in
+# .
+
+from . import get_help
+
+__doc__ = get_help("help_chatbot")
+
+
+from pyUltroid.fns.tools import get_chatbot_reply
+
+from . import LOGS, eod, get_string, inline_mention, udB, ultroid_cmd
+
+
+@ultroid_cmd(pattern="repai")
+async def im_lonely_chat_with_me(event):
+ if event.reply_to:
+ message = (await event.get_reply_message()).message
+ else:
+ try:
+ message = event.text.split(" ", 1)[1]
+ except IndexError:
+ return await eod(event, get_string("tban_1"), time=10)
+ reply_ = await get_chatbot_reply(message=message)
+ await event.eor(reply_)
+
+
+@ultroid_cmd(pattern="addai")
+async def add_chatBot(event):
+ await chat_bot_fn(event, type_="add")
+
+
+@ultroid_cmd(pattern="remai")
+async def rem_chatBot(event):
+ await chat_bot_fn(event, type_="remov")
+
+
+@ultroid_cmd(pattern="listai")
+async def lister(event):
+ key = udB.get_key("CHATBOT_USERS") or {}
+ users = key.get(event.chat_id, [])
+ if not users:
+ return await event.eor(get_string("chab_2"), time=5)
+ msg = "**Total List Of AI Enabled Users In This Chat :**\n\n"
+ for i in users:
+ try:
+ user = await event.client.get_entity(int(i))
+ user = inline_mention(user)
+ except BaseException:
+ user = f"`{i}`"
+ msg += f"• {user}\n"
+ await event.eor(msg, link_preview=False)
+
+
+async def chat_bot_fn(event, type_):
+ if event.reply_to:
+ user_ = (await event.get_reply_message()).sender
+ else:
+ temp = event.text.split(maxsplit=1)
+ try:
+ user_ = await event.client.get_entity(await event.client.parse_id(temp[1]))
+ except BaseException as er:
+ LOGS.exception(er)
+ user_ = event.chat if event.is_private else None
+ if not user_:
+ return await eod(
+ event,
+ get_string("chab_1"),
+ )
+ key = udB.get_key("CHATBOT_USERS") or {}
+ chat = event.chat_id
+ user = user_.id
+ if type_ == "add":
+ if key.get(chat):
+ if user not in key[chat]:
+ key[chat].append(user)
+ else:
+ key.update({chat: [user]})
+ elif type_ == "remov":
+ if key.get(chat):
+ if user in key[chat]:
+ key[chat].remove(user)
+ if chat in key and not key[chat]:
+ del key[chat]
+ udB.set_key("CHATBOT_USERS", key)
+ await event.eor(f"**ChatBot:**\n{type_}ed {inline_mention(user_)}")
diff --git a/plugins/chats.py b/plugins/chats.py
new file mode 100644
index 0000000000000000000000000000000000000000..0651cd7d72b7db7724830e43e1304483caf65109
--- /dev/null
+++ b/plugins/chats.py
@@ -0,0 +1,368 @@
+# Ultroid - UserBot
+# Copyright (C) 2021-2025 TeamUltroid
+#
+# This file is a part of < https://github.com/TeamUltroid/Ultroid/ >
+# PLease read the GNU Affero General Public License in
+# .
+from . import get_help
+
+__doc__ = get_help("help_chats")
+
+
+from telethon.errors import ChatAdminRequiredError as no_admin
+from telethon.tl.functions.channels import (
+ CreateChannelRequest,
+ DeleteChannelRequest,
+ EditPhotoRequest,
+ GetFullChannelRequest,
+ UpdateUsernameRequest,
+)
+from telethon.tl.functions.messages import (
+ CreateChatRequest,
+ ExportChatInviteRequest,
+ GetFullChatRequest,
+)
+from telethon.tl.types import (
+ ChannelParticipantsKicked,
+ User,
+ UserStatusEmpty,
+ UserStatusLastMonth,
+ UserStatusLastWeek,
+ UserStatusOffline,
+ UserStatusOnline,
+ UserStatusRecently,
+)
+
+from . import HNDLR, LOGS, asst, con, get_string, mediainfo, os, types, udB, ultroid_cmd
+
+
+@ultroid_cmd(
+ pattern="delchat",
+ groups_only=True,
+)
+async def _(e):
+ xx = await e.eor(get_string("com_1"))
+ try:
+ match = e.text.split(" ", maxsplit=1)[1]
+ chat = await e.client.parse_id(match)
+ except IndexError:
+ chat = e.chat_id
+ try:
+ await e.client(DeleteChannelRequest(chat))
+ except TypeError:
+ return await xx.eor(get_string("chats_1"), time=10)
+ except no_admin:
+ return await xx.eor(get_string("chats_2"), time=10)
+ await e.client.send_message(
+ int(udB.get_key("LOG_CHANNEL")), get_string("chats_6").format(e.chat_id)
+ )
+
+
+@ultroid_cmd(
+ pattern="getlink( (.*)|$)",
+ groups_only=True,
+ manager=True,
+)
+async def _(e):
+ reply = await e.get_reply_message()
+ match = e.pattern_match.group(1).strip()
+ if reply and not isinstance(reply.sender, User):
+ chat = await reply.get_sender()
+ else:
+ chat = await e.get_chat()
+ if hasattr(chat, "username") and chat.username:
+ return await e.eor(f"Username: @{chat.username}")
+ request, usage, title, link = None, None, None, None
+ if match:
+ split = match.split(maxsplit=1)
+ request = split[0] in ["r", "request"]
+ title = "Created by Ultroid"
+ if len(split) > 1:
+ match = split[1]
+ spli = match.split(maxsplit=1)
+ if spli[0].isdigit():
+ usage = int(spli[0])
+ if len(spli) > 1:
+ title = spli[1]
+ elif not request:
+ if match.isdigit():
+ usage = int(match)
+ else:
+ title = match
+ if request and usage:
+ usage = 0
+ if request or title:
+ try:
+ r = await e.client(
+ ExportChatInviteRequest(
+ e.chat_id,
+ request_needed=request,
+ usage_limit=usage,
+ title=title,
+ ),
+ )
+ except no_admin:
+ return await e.eor(get_string("chats_2"), time=10)
+ link = r.link
+ else:
+ if isinstance(chat, types.Chat):
+ FC = await e.client(GetFullChatRequest(chat.id))
+ elif isinstance(chat, types.Channel):
+ FC = await e.client(GetFullChannelRequest(chat.id))
+ else:
+ return
+ Inv = FC.full_chat.exported_invite
+ if Inv and not Inv.revoked:
+ link = Inv.link
+ if link:
+ return await e.eor(f"Link:- {link}")
+ await e.eor("`Failed to getlink!\nSeems like link is inaccessible to you...`")
+
+
+@ultroid_cmd(
+ pattern="create (b|g|c)(?: |$)(.*)",
+)
+async def _(e):
+ type_of_group = e.pattern_match.group(1).strip()
+ group_name = e.pattern_match.group(2)
+ username = None
+ if " ; " in group_name:
+ group_ = group_name.split(" ; ", maxsplit=1)
+ group_name = group_[0]
+ username = group_[1]
+ xx = await e.eor(get_string("com_1"))
+ if type_of_group == "b":
+ try:
+ r = await e.client(
+ CreateChatRequest(
+ users=[asst.me.username],
+ title=group_name,
+ ),
+ )
+ created_chat_id = r.chats[0].id
+ result = await e.client(
+ ExportChatInviteRequest(
+ peer=created_chat_id,
+ ),
+ )
+ await xx.edit(
+ get_string("chats_4").format(group_name, result.link),
+ link_preview=False,
+ )
+ except Exception as ex:
+ await xx.edit(str(ex))
+ elif type_of_group in ["g", "c"]:
+ try:
+ r = await e.client(
+ CreateChannelRequest(
+ title=group_name,
+ about=get_string("chats_5"),
+ megagroup=type_of_group != "c",
+ )
+ )
+
+ created_chat_id = r.chats[0].id
+ if username:
+ await e.client(UpdateUsernameRequest(created_chat_id, username))
+ result = f"https://t.me/{username}"
+ else:
+ result = (
+ await e.client(
+ ExportChatInviteRequest(
+ peer=created_chat_id,
+ ),
+ )
+ ).link
+ await xx.edit(
+ get_string("chats_6").format(f"[{group_name}]({result})"),
+ link_preview=False,
+ )
+ except Exception as ex:
+ await xx.edit(str(ex))
+
+
+# ---------------------------------------------------------------- #
+
+
+@ultroid_cmd(
+ pattern="setgpic( (.*)|$)", admins_only=True, manager=True, require="change_info"
+)
+async def _(ult):
+ if not ult.is_reply:
+ return await ult.eor("`Reply to a Media..`", time=5)
+ match = ult.pattern_match.group(1).strip()
+ if not ult.client._bot and match:
+ try:
+ chat = await ult.client.parse_id(match)
+ except Exception as ok:
+ return await ult.eor(str(ok))
+ else:
+ chat = ult.chat_id
+ reply = await ult.get_reply_message()
+ if reply.photo or reply.sticker or reply.video:
+ replfile = await reply.download_media()
+ elif reply.document and reply.document.thumbs:
+ replfile = await reply.download_media(thumb=-1)
+ else:
+ return await ult.eor("Reply to a Photo or Video..")
+ mediain = mediainfo(reply.media)
+ if "animated" in mediain:
+ replfile = await con.convert(replfile, convert_to="mp4")
+ else:
+ replfile = await con.convert(
+ replfile, outname="chatphoto", allowed_formats=["jpg", "png", "mp4"]
+ )
+ file = await ult.client.upload_file(replfile)
+ try:
+ if "pic" not in mediain:
+ file = types.InputChatUploadedPhoto(video=file)
+ await ult.client(EditPhotoRequest(chat, file))
+ await ult.eor("`Group Photo has Successfully Changed !`", time=5)
+ except Exception as ex:
+ await ult.eor(f"Error occured.\n`{str(ex)}`", time=5)
+ os.remove(replfile)
+
+
+@ultroid_cmd(
+ pattern="delgpic( (.*)|$)", admins_only=True, manager=True, require="change_info"
+)
+async def _(ult):
+ match = ult.pattern_match.group(1).strip()
+ chat = ult.chat_id
+ if not ult.client._bot and match:
+ chat = match
+ try:
+ await ult.client(EditPhotoRequest(chat, types.InputChatPhotoEmpty()))
+ text = "`Removed Chat Photo..`"
+ except Exception as E:
+ text = str(E)
+ return await ult.eor(text, time=5)
+
+
+@ultroid_cmd(pattern="unbanall$", manager=True, admins_only=True, require="ban_users")
+async def _(event):
+ xx = await event.eor("Searching Participant Lists.")
+ p = 0
+ title = (await event.get_chat()).title
+ async for i in event.client.iter_participants(
+ event.chat_id,
+ filter=ChannelParticipantsKicked,
+ aggressive=True,
+ ):
+ try:
+ await event.client.edit_permissions(event.chat_id, i, view_messages=True)
+ p += 1
+ except no_admin:
+ pass
+ except BaseException as er:
+ LOGS.exception(er)
+ await xx.eor(f"{title}: {p} unbanned", time=5)
+
+
+@ultroid_cmd(
+ pattern="rmusers( (.*)|$)",
+ groups_only=True,
+ admins_only=True,
+ fullsudo=True,
+)
+async def _(event):
+ xx = await event.eor(get_string("com_1"))
+ input_str = event.pattern_match.group(1).strip()
+ p, a, b, c, d, m, n, y, w, o, q, r = 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ async for i in event.client.iter_participants(event.chat_id):
+ p += 1 # Total Count
+ if isinstance(i.status, UserStatusEmpty):
+ if "empty" in input_str:
+ try:
+ await event.client.kick_participant(event.chat_id, i)
+ c += 1
+ except BaseException:
+ pass
+ else:
+ y += 1
+ if isinstance(i.status, UserStatusLastMonth):
+ if "month" in input_str:
+ try:
+ await event.client.kick_participant(event.chat_id, i)
+ c += 1
+ except BaseException:
+ pass
+ else:
+ m += 1
+ if isinstance(i.status, UserStatusLastWeek):
+ if "week" in input_str:
+ try:
+ await event.client.kick_participant(event.chat_id, i)
+ c += 1
+ except BaseException:
+ pass
+ else:
+ w += 1
+ if isinstance(i.status, UserStatusOffline):
+ if "offline" in input_str:
+ try:
+ await event.client.kick_participant(event.chat_id, i)
+ c += 1
+ except BaseException:
+ pass
+ else:
+ o += 1
+ if isinstance(i.status, UserStatusOnline):
+ if "online" in input_str:
+ try:
+ await event.client.kick_participant(event.chat_id, i)
+ c += 1
+ except BaseException:
+ pass
+ else:
+ q += 1
+ if isinstance(i.status, UserStatusRecently):
+ if "recently" in input_str:
+ try:
+ await event.client.kick_participant(event.chat_id, i)
+ c += 1
+ except BaseException:
+ pass
+ else:
+ r += 1
+ if i.bot:
+ if "bot" in input_str:
+ try:
+ await event.client.kick_participant(event.chat_id, i)
+ c += 1
+ except BaseException:
+ pass
+ else:
+ b += 1
+ elif i.deleted:
+ if "deleted" in input_str:
+ try:
+ await event.client.kick_participant(event.chat_id, i)
+ c += 1
+ except BaseException:
+ pass
+ else:
+ d += 1
+ elif i.status is None:
+ if "none" in input_str:
+ try:
+ await event.client.kick_participant(event.chat_id, i)
+ c += 1
+ except BaseException:
+ pass
+ else:
+ n += 1
+ if input_str:
+ required_string = f"**>> Kicked** `{c} / {p}` **users**\n\n"
+ else:
+ required_string = f"**>> Total** `{p}` **users**\n\n"
+ required_string += f" `{HNDLR}rmusers deleted` **••** `{d}`\n"
+ required_string += f" `{HNDLR}rmusers empty` **••** `{y}`\n"
+ required_string += f" `{HNDLR}rmusers month` **••** `{m}`\n"
+ required_string += f" `{HNDLR}rmusers week` **••** `{w}`\n"
+ required_string += f" `{HNDLR}rmusers offline` **••** `{o}`\n"
+ required_string += f" `{HNDLR}rmusers online` **••** `{q}`\n"
+ required_string += f" `{HNDLR}rmusers recently` **••** `{r}`\n"
+ required_string += f" `{HNDLR}rmusers bot` **••** `{b}`\n"
+ required_string += f" `{HNDLR}rmusers none` **••** `{n}`"
+ await xx.eor(required_string)
diff --git a/plugins/cleanaction.py b/plugins/cleanaction.py
new file mode 100644
index 0000000000000000000000000000000000000000..361e0947626e4b64b5b0dfe83c687c8ccd067403
--- /dev/null
+++ b/plugins/cleanaction.py
@@ -0,0 +1,48 @@
+# Ultroid - UserBot
+# Copyright (C) 2021-2025 TeamUltroid
+#
+# This file is a part of < https://github.com/TeamUltroid/Ultroid/ >
+# PLease read the GNU Affero General Public License in
+# .
+
+from . import get_help
+
+__doc__ = get_help("help_cleanaction")
+
+
+from telethon.utils import get_display_name
+
+from . import get_string, udB, ultroid_cmd
+
+
+@ultroid_cmd(pattern="addclean$", admins_only=True)
+async def _(e):
+ key = udB.get_key("CLEANCHAT") or []
+ if e.chat_id in key:
+ return await eod(e, get_string("clan_5"))
+ key.append(e.chat_id)
+ udB.set_key("CLEANCHAT", key)
+ await e.eor(get_string("clan_1"), time=5)
+
+
+@ultroid_cmd(pattern="remclean$")
+async def _(e):
+ key = udB.get_key("CLEANCHAT") or []
+ if e.chat_id in key:
+ key.remove(e.chat_id)
+ udB.set_key("CLEANCHAT", key)
+ await e.eor(get_string("clan_2"), time=5)
+
+
+@ultroid_cmd(pattern="listclean$")
+async def _(e):
+ if k := udB.get_key("CLEANCHAT"):
+ o = ""
+ for x in k:
+ try:
+ title = get_display_name(await e.client.get_entity(x))
+ except BaseException:
+ title = get_string("clan_3")
+ o += f"{x} {title}\n"
+ return await e.eor(o)
+ await e.eor(get_string("clan_4"), time=5)
diff --git a/plugins/compressor.py b/plugins/compressor.py
new file mode 100644
index 0000000000000000000000000000000000000000..a221d8f1619f0a13da9a4cf341abb7b64840dc80
--- /dev/null
+++ b/plugins/compressor.py
@@ -0,0 +1,177 @@
+# Ultroid - UserBot
+# Copyright (C) 2021-2025 TeamUltroid
+#
+# This file is a part of < https://github.com/TeamUltroid/Ultroid/ >
+# PLease read the GNU Affero General Public License in
+# .
+
+from . import get_help
+
+__doc__ = get_help("help_compressor")
+
+
+import asyncio
+import os
+import re
+import time
+from datetime import datetime as dt
+
+from telethon.errors.rpcerrorlist import MessageNotModifiedError
+from telethon.tl.types import DocumentAttributeVideo
+
+from pyUltroid.fns.tools import metadata
+
+from . import (
+ ULTConfig,
+ bash,
+ downloader,
+ get_string,
+ humanbytes,
+ math,
+ mediainfo,
+ time_formatter,
+ ultroid_cmd,
+ uploader,
+)
+
+
+@ultroid_cmd(pattern="compress( (.*)|$)")
+async def _(e):
+ cr = e.pattern_match.group(1).strip()
+ crf = 27
+ to_stream = False
+ if cr:
+ k = e.text.split()
+ if len(k) == 2:
+ crf = int(k[1]) if k[1].isdigit() else 27
+ elif len(k) > 2:
+ crf = int(k[1]) if k[1].isdigit() else 27
+ to_stream = "stream" in k[2]
+ vido = await e.get_reply_message()
+ if vido and vido.media and "video" in mediainfo(vido.media):
+ if hasattr(vido.media, "document"):
+ vfile = vido.media.document
+ name = vido.file.name
+ else:
+ vfile = vido.media
+ name = ""
+ if not name:
+ name = "video_" + dt.now().isoformat("_", "seconds") + ".mp4"
+ xxx = await e.eor(get_string("audiotools_5"))
+ c_time = time.time()
+ file = await downloader(
+ f"resources/downloads/{name}",
+ vfile,
+ xxx,
+ c_time,
+ f"Downloading {name}...",
+ )
+
+ o_size = os.path.getsize(file.name)
+ d_time = time.time()
+ diff = time_formatter((d_time - c_time) * 1000)
+ file_name = (file.name).split("/")[-1]
+ out = file_name.replace(file_name.split(".")[-1], "compressed.mkv")
+ await xxx.edit(
+ f"`Downloaded {file.name} of {humanbytes(o_size)} in {diff}.\nNow Compressing...`"
+ )
+ x, y = await bash(
+ f'mediainfo --fullscan """{file.name}""" | grep "Frame count"'
+ )
+ if y and y.endswith("NOT_FOUND"):
+ return await xxx.edit(f"ERROR: `{y}`")
+ total_frames = x.split(":")[1].split("\n")[0]
+ progress = f"progress-{c_time}.txt"
+ with open(progress, "w"):
+ pass
+ proce = await asyncio.create_subprocess_shell(
+ f'ffmpeg -hide_banner -loglevel quiet -progress {progress} -i """{file.name}""" -preset ultrafast -vcodec libx265 -crf {crf} -c:a copy """{out}""" -y',
+ stdout=asyncio.subprocess.PIPE,
+ stderr=asyncio.subprocess.PIPE,
+ )
+ while proce.returncode != 0:
+ await asyncio.sleep(3)
+ with open(progress, "r+") as fil:
+ text = fil.read()
+ frames = re.findall("frame=(\\d+)", text)
+ size = re.findall("total_size=(\\d+)", text)
+ speed = 0
+ if len(frames):
+ elapse = int(frames[-1])
+ if len(size):
+ size = int(size[-1])
+ per = elapse * 100 / int(total_frames)
+ time_diff = time.time() - int(d_time)
+ speed = round(elapse / time_diff, 2)
+ if int(speed) != 0:
+ some_eta = ((int(total_frames) - elapse) / speed) * 1000
+ text = f"`Compressing {file_name} at {crf} CRF.\n`"
+ progress_str = "`[{0}{1}] {2}%\n\n`".format(
+ "".join("●" for _ in range(math.floor(per / 5))),
+ "".join("" for _ in range(20 - math.floor(per / 5))),
+ round(per, 2),
+ )
+
+ e_size = f"{humanbytes(size)} of ~{humanbytes((size / per) * 100)}"
+ eta = f"~{time_formatter(some_eta)}"
+ try:
+ await xxx.edit(
+ text
+ + progress_str
+ + "`"
+ + e_size
+ + "`"
+ + "\n\n`"
+ + eta
+ + "`"
+ )
+ except MessageNotModifiedError:
+ pass
+ os.remove(file.name)
+ c_size = os.path.getsize(out)
+ f_time = time.time()
+ difff = time_formatter((f_time - d_time) * 1000)
+ await xxx.edit(
+ f"`Compressed {humanbytes(o_size)} to {humanbytes(c_size)} in {difff}\nTrying to Upload...`"
+ )
+ differ = 100 - ((c_size / o_size) * 100)
+ caption = f"**Original Size: **`{humanbytes(o_size)}`\n"
+ caption += f"**Compressed Size: **`{humanbytes(c_size)}`\n"
+ caption += f"**Compression Ratio: **`{differ:.2f}%`\n"
+ caption += f"\n**Time Taken To Compress: **`{difff}`"
+ n_file, _ = await e.client.fast_uploader(
+ out, show_progress=True, event=e, message="Uploading...", to_delete=True
+ )
+ if to_stream:
+ data = await metadata(out)
+ width = data["width"]
+ height = data["height"]
+ duration = data["duration"]
+ attributes = [
+ DocumentAttributeVideo(
+ duration=duration, w=width, h=height, supports_streaming=True
+ )
+ ]
+ await e.client.send_file(
+ e.chat_id,
+ n_file,
+ thumb=ULTConfig.thumb,
+ caption=caption,
+ attributes=attributes,
+ force_document=False,
+ reply_to=e.reply_to_msg_id,
+ )
+ else:
+ await e.client.send_file(
+ e.chat_id,
+ n_file,
+ thumb=ULTConfig.thumb,
+ caption=caption,
+ force_document=True,
+ reply_to=e.reply_to_msg_id,
+ )
+ await xxx.delete()
+ os.remove(out)
+ os.remove(progress)
+ else:
+ await e.eor(get_string("audiotools_8"), time=5)
diff --git a/plugins/converter.py b/plugins/converter.py
new file mode 100644
index 0000000000000000000000000000000000000000..201ff76b96b040cc403608774e1fd2166c9564d2
--- /dev/null
+++ b/plugins/converter.py
@@ -0,0 +1,196 @@
+# Ultroid - UserBot
+# Copyright (C) 2021-2025 TeamUltroid
+#
+# This file is a part of < https://github.com/TeamUltroid/Ultroid/ >
+# PLease read the GNU Affero General Public License in
+# .
+
+from . import get_help
+
+__doc__ = get_help("help_converter")
+
+import os
+import time
+
+from . import LOGS
+
+try:
+ import cv2
+except ImportError:
+ cv2 = None
+
+try:
+ from PIL import Image
+except ImportError:
+ LOGS.info(f"{__file__}: PIL not Installed.")
+ Image = None
+
+from . import upload_file as uf
+
+from . import (
+ ULTConfig,
+ bash,
+ con,
+ downloader,
+ get_paste,
+ get_string,
+ udB,
+ ultroid_cmd,
+ uploader,
+)
+
+opn = []
+
+
+@ultroid_cmd(
+ pattern="thumbnail$",
+)
+async def _(e):
+ r = await e.get_reply_message()
+ if r.photo:
+ dl = await r.download_media()
+ elif r.document and r.document.thumbs:
+ dl = await r.download_media(thumb=-1)
+ else:
+ return await e.eor("`Reply to Photo or media with thumb...`")
+ nn = uf(dl)
+ os.remove(dl)
+ udB.set_key("CUSTOM_THUMBNAIL", str(nn))
+ await bash(f"wget {nn} -O resources/extras/ultroid.jpg")
+ await e.eor(get_string("cvt_6").format(nn), link_preview=False)
+
+
+@ultroid_cmd(
+ pattern="rename( (.*)|$)",
+)
+async def imak(event):
+ reply = await event.get_reply_message()
+ t = time.time()
+ if not reply:
+ return await event.eor(get_string("cvt_1"))
+ inp = event.pattern_match.group(1).strip()
+ if not inp:
+ return await event.eor(get_string("cvt_2"))
+ xx = await event.eor(get_string("com_1"))
+ if reply.media:
+ if hasattr(reply.media, "document"):
+ file = reply.media.document
+ image = await downloader(
+ reply.file.name or str(time.time()),
+ reply.media.document,
+ xx,
+ t,
+ get_string("com_5"),
+ )
+
+ file = image.name
+ else:
+ file = await event.client.download_media(reply.media)
+ if os.path.exists(inp):
+ os.remove(inp)
+ await bash(f'mv """{file}""" """{inp}"""')
+ if not os.path.exists(inp) or os.path.exists(inp) and not os.path.getsize(inp):
+ os.rename(file, inp)
+ k = time.time()
+ n_file, _ = await event.client.fast_uploader(
+ inp, show_progress=True, event=event, message="Uploading...", to_delete=True
+ )
+ await event.reply(
+ f"`{n_file.name}`",
+ file=n_file,
+ force_document=True,
+ thumb=ULTConfig.thumb,
+ )
+ os.remove(inp)
+ await xx.delete()
+
+
+conv_keys = {
+ "img": "png",
+ "sticker": "webp",
+ "webp": "webp",
+ "image": "png",
+ "webm": "webm",
+ "gif": "gif",
+ "json": "json",
+ "tgs": "tgs",
+}
+
+
+@ultroid_cmd(
+ pattern="convert( (.*)|$)",
+)
+async def uconverter(event):
+ xx = await event.eor(get_string("com_1"))
+ a = await event.get_reply_message()
+ if a is None:
+ return await event.eor("`Reply to Photo or media with thumb...`")
+ input_ = event.pattern_match.group(1).strip()
+ b = await a.download_media("resources/downloads/")
+ if not b and (a.document and a.document.thumbs):
+ b = await a.download_media(thumb=-1)
+ if not b:
+ return await xx.edit(get_string("cvt_3"))
+ try:
+ convert = conv_keys[input_]
+ except KeyError:
+ return await xx.edit(get_string("sts_3").format("gif/img/sticker/webm"))
+ file = await con.convert(b, outname="ultroid", convert_to=convert)
+ print(file)
+
+ if file:
+ await event.client.send_file(
+ event.chat_id, file, reply_to=event.reply_to_msg_id or event.id
+ )
+ os.remove(file)
+ else:
+ await xx.edit("`Failed to convert`")
+ return
+ await xx.delete()
+
+@ultroid_cmd(
+ pattern="doc( (.*)|$)",
+)
+async def _(event):
+ input_str = event.pattern_match.group(1).strip()
+ if not (input_str and event.is_reply):
+ return await event.eor(get_string("cvt_1"), time=5)
+ xx = await event.eor(get_string("com_1"))
+ a = await event.get_reply_message()
+ if not a.message:
+ return await xx.edit(get_string("ex_1"))
+ with open(input_str, "w") as b:
+ b.write(str(a.message))
+ await xx.edit(f"**Packing into** `{input_str}`")
+ await event.reply(file=input_str, thumb=ULTConfig.thumb)
+ await xx.delete()
+ os.remove(input_str)
+
+
+@ultroid_cmd(
+ pattern="open( (.*)|$)",
+)
+async def _(event):
+ a = await event.get_reply_message()
+ b = event.pattern_match.group(1).strip()
+ if not ((a and a.media) or (b and os.path.exists(b))):
+ return await event.eor(get_string("cvt_7"), time=5)
+ xx = await event.eor(get_string("com_1"))
+ rem = None
+ if not b:
+ b = await a.download_media()
+ rem = True
+ try:
+ with open(b) as c:
+ d = c.read()
+ except UnicodeDecodeError:
+ return await xx.eor(get_string("cvt_8"), time=5)
+ try:
+ await xx.edit(f"```{d}```")
+ except BaseException:
+ what, data = await get_paste(d)
+ await xx.edit(
+ f"**MESSAGE EXCEEDS TELEGRAM LIMITS**\n\nSo Pasted It On [SPACEBIN]({data['link']})"
+ )
+ if rem:
+ os.remove(b)
diff --git a/plugins/core.py b/plugins/core.py
new file mode 100644
index 0000000000000000000000000000000000000000..b93f659a2f8decf5e1bd7d4339413113f0c28df9
--- /dev/null
+++ b/plugins/core.py
@@ -0,0 +1,124 @@
+# Ultroid - UserBot
+# Copyright (C) 2021-2025 TeamUltroid
+#
+# This file is a part of < https://github.com/TeamUltroid/Ultroid/ >
+# PLease read the GNU Affero General Public License in
+# .
+
+
+from . import get_help
+
+__doc__ = get_help("help_core")
+
+
+import os
+
+from pyUltroid.startup.loader import load_addons
+
+from . import LOGS, async_searcher, eod, get_string, safeinstall, ultroid_cmd, un_plug
+
+
+@ultroid_cmd(pattern="install", fullsudo=True)
+async def install(event):
+ await safeinstall(event)
+
+
+@ultroid_cmd(
+ pattern=r"unload( (.*)|$)",
+)
+async def unload(event):
+ shortname = event.pattern_match.group(1).strip()
+ if not shortname:
+ await event.eor(get_string("core_9"))
+ return
+ lsd = os.listdir("addons")
+ zym = f"{shortname}.py"
+ if zym in lsd:
+ try:
+ un_plug(shortname)
+ await event.eor(f"**Uɴʟᴏᴀᴅᴇᴅ** `{shortname}` **Sᴜᴄᴄᴇssғᴜʟʟʏ.**", time=3)
+ except Exception as ex:
+ LOGS.exception(ex)
+ return await event.eor(str(ex))
+ elif zym in os.listdir("plugins"):
+ return await event.eor(get_string("core_11"), time=3)
+ else:
+ await event.eor(f"**Nᴏ Pʟᴜɢɪɴ Nᴀᴍᴇᴅ** `{shortname}`", time=3)
+
+
+@ultroid_cmd(
+ pattern=r"uninstall( (.*)|$)",
+)
+async def uninstall(event):
+ shortname = event.pattern_match.group(1).strip()
+ if not shortname:
+ await event.eor(get_string("core_13"))
+ return
+ lsd = os.listdir("addons")
+ zym = f"{shortname}.py"
+ if zym in lsd:
+ try:
+ un_plug(shortname)
+ await event.eor(f"**Uɴɪɴsᴛᴀʟʟᴇᴅ** `{shortname}` **Sᴜᴄᴄᴇssғᴜʟʟʏ.**", time=3)
+ os.remove(f"addons/{shortname}.py")
+ except Exception as ex:
+ return await event.eor(str(ex))
+ elif zym in os.listdir("plugins"):
+ return await event.eor(get_string("core_15"), time=3)
+ else:
+ return await event.eor(f"**Nᴏ Pʟᴜɢɪɴ Nᴀᴍᴇᴅ** `{shortname}`", time=3)
+
+
+@ultroid_cmd(
+ pattern=r"load( (.*)|$)",
+ fullsudo=True,
+)
+async def load(event):
+ shortname = event.pattern_match.group(1).strip()
+ if not shortname:
+ await event.eor(get_string("core_16"))
+ return
+ try:
+ try:
+ un_plug(shortname)
+ except BaseException:
+ pass
+ load_addons(f"addons/{shortname}.py")
+ await event.eor(get_string("core_17").format(shortname), time=3)
+ except Exception as e:
+ LOGS.exception(e)
+ await eod(
+ event,
+ get_string("core_18").format(shortname, e),
+ time=3,
+ )
+
+
+@ultroid_cmd(pattern="getaddons( (.*)|$)", fullsudo=True)
+async def get_the_addons_lol(event):
+ thelink = event.pattern_match.group(1).strip()
+ xx = await event.eor(get_string("com_1"))
+ fool = get_string("gas_1")
+ if thelink is None:
+ return await xx.eor(fool, time=10)
+ split_thelink = thelink.split("/")
+ if not ("raw" in thelink and thelink.endswith(".py")):
+ return await xx.eor(fool, time=10)
+ name_of_it = split_thelink[-1]
+ plug = await async_searcher(thelink)
+ fil = f"addons/{name_of_it}"
+ await xx.edit("Packing the codes...")
+ with open(fil, "w", encoding="utf-8") as uult:
+ uult.write(plug)
+ await xx.edit("Packed. Now loading the plugin..")
+ shortname = name_of_it.split(".")[0]
+ try:
+ load_addons(fil)
+ await xx.eor(get_string("core_17").format(shortname), time=15)
+ except Exception as e:
+ LOGS.exception(e)
+ await eod(
+ xx,
+ get_string("core_18").format(shortname, e),
+ time=3,
+ )
diff --git a/plugins/database.py b/plugins/database.py
new file mode 100644
index 0000000000000000000000000000000000000000..0e6c79e33ac9df148e1579c71a9948eca366a488
--- /dev/null
+++ b/plugins/database.py
@@ -0,0 +1,77 @@
+# Ultroid - UserBot
+# Copyright (C) 2021-2025 TeamUltroid
+#
+# This file is a part of < https://github.com/TeamUltroid/Ultroid/ >
+# PLease read the GNU Affero General Public License in
+# .
+
+
+from . import get_help
+
+__doc__ = get_help("help_database")
+
+
+import re
+
+from . import Redis, eor, get_string, udB, ultroid_cmd
+
+
+@ultroid_cmd(pattern="setdb( (.*)|$)", fullsudo=True)
+async def _(ult):
+ match = ult.pattern_match.group(1).strip()
+ if not match:
+ return await ult.eor("Provide key and value to set!")
+ try:
+ delim = " " if re.search("[|]", match) is None else " | "
+ data = match.split(delim, maxsplit=1)
+ if data[0] in ["--extend", "-e"]:
+ data = data[1].split(maxsplit=1)
+ data[1] = f"{str(udB.get_key(data[0]))} {data[1]}"
+ udB.set_key(data[0], data[1])
+ await ult.eor(
+ f"**DB Key Value Pair Updated\nKey :** `{data[0]}`\n**Value :** `{data[1]}`"
+ )
+
+ except BaseException:
+ await ult.eor(get_string("com_7"))
+
+
+@ultroid_cmd(pattern="deldb( (.*)|$)", fullsudo=True)
+async def _(ult):
+ key = ult.pattern_match.group(1).strip()
+ if not key:
+ return await ult.eor("Give me a key name to delete!", time=5)
+ _ = key.split(maxsplit=1)
+ try:
+ if _[0] == "-m":
+ for key in _[1].split():
+ k = udB.del_key(key)
+ key = _[1]
+ else:
+ k = udB.del_key(key)
+ if k == 0:
+ return await ult.eor("`No Such Key.`")
+ await ult.eor(f"`Successfully deleted key {key}`")
+ except BaseException:
+ await ult.eor(get_string("com_7"))
+
+
+@ultroid_cmd(pattern="rendb( (.*)|$)", fullsudo=True)
+async def _(ult):
+ match = ult.pattern_match.group(1).strip()
+ if not match:
+ return await ult.eor("`Provide Keys name to rename..`")
+ delim = " " if re.search("[|]", match) is None else " | "
+ data = match.split(delim)
+ if Redis(data[0]):
+ try:
+ udB.rename(data[0], data[1])
+ await eor(
+ ult,
+ f"**DB Key Rename Successful\nOld Key :** `{data[0]}`\n**New Key :** `{data[1]}`",
+ )
+
+ except BaseException:
+ await ult.eor(get_string("com_7"))
+ else:
+ await ult.eor("Key not found")
diff --git a/plugins/devtools.py b/plugins/devtools.py
new file mode 100644
index 0000000000000000000000000000000000000000..40af9a79872475ecac5339a3946c0c87ccf72d9c
--- /dev/null
+++ b/plugins/devtools.py
@@ -0,0 +1,384 @@
+# Ultroid - UserBot
+# Copyright (C) 2021-2025 TeamUltroid
+#
+# This file is a part of < https://github.com/TeamUltroid/Ultroid/ >
+# PLease read the GNU Affero General Public License in
+# .
+
+from . import get_help
+
+__doc__ = get_help("help_devtools")
+
+import inspect
+import sys
+import traceback
+from io import BytesIO, StringIO
+from os import remove
+from pprint import pprint
+
+from telethon.utils import get_display_name
+
+from pyUltroid import _ignore_eval
+
+from . import *
+
+# Used for Formatting Eval Code, if installed
+try:
+ import black
+except ImportError:
+ black = None
+from random import choice
+
+try:
+ from yaml import safe_load
+except ImportError:
+ from pyUltroid.fns.tools import safe_load
+
+from . import upload_file as uf
+from telethon.tl import functions
+
+fn = functions
+
+
+@ultroid_cmd(
+ pattern="sysinfo$",
+)
+async def _(e):
+ xx = await e.eor(get_string("com_1"))
+ x, y = await bash("neofetch|sed 's/\x1B\\[[0-9;\\?]*[a-zA-Z]//g' >> neo.txt")
+ if y and y.endswith("NOT_FOUND"):
+ return await xx.edit(f"Error: `{y}`")
+ with open("neo.txt", "r", encoding="utf-8") as neo:
+ p = (neo.read()).replace("\n\n", "")
+ haa = await Carbon(code=p, file_name="neofetch", backgroundColor=choice(ATRA_COL))
+ if isinstance(haa, dict):
+ await xx.edit(f"`{haa}`")
+ else:
+ await e.reply(file=haa)
+ await xx.delete()
+ remove("neo.txt")
+
+
+@ultroid_cmd(pattern="bash", fullsudo=True, only_devs=True)
+async def _(event):
+ carb, rayso, yamlf = None, None, False
+ try:
+ cmd = event.text.split(" ", maxsplit=1)[1]
+ if cmd.split()[0] in ["-c", "--carbon"]:
+ cmd = cmd.split(maxsplit=1)[1]
+ carb = True
+ if cmd.split()[0] in ["-r", "--rayso"]:
+ cmd = cmd.split(maxsplit=1)[1]
+ rayso = True
+ except IndexError:
+ return await event.eor(get_string("devs_1"), time=10)
+ xx = await event.eor(get_string("com_1"))
+ reply_to_id = event.reply_to_msg_id or event.id
+ stdout, stderr = await bash(cmd, run_code=1)
+ OUT = f"**☞ BASH\n\n• COMMAND:**\n`{cmd}` \n\n"
+ err, out = "", ""
+ if stderr:
+ err = f"**• ERROR:** \n`{stderr}`\n\n"
+ if stdout:
+ if (carb or udB.get_key("CARBON_ON_BASH")) and (
+ event.is_private
+ or event.chat.admin_rights
+ or event.chat.creator
+ or event.chat.default_banned_rights.embed_links
+ ):
+ li = await Carbon(
+ code=stdout,
+ file_name="bash",
+ download=True,
+ backgroundColor=choice(ATRA_COL),
+ )
+ if isinstance(li, dict):
+ await xx.edit(
+ f"Unknown Response from Carbon: `{li}`\n\nstdout`:{stdout}`\nstderr: `{stderr}`"
+ )
+ return
+ url = uf(li)
+ OUT = f"[\xad]({url}){OUT}"
+ out = "**• OUTPUT:**"
+ remove(li)
+ elif (rayso or udB.get_key("RAYSO_ON_BASH")) and (
+ event.is_private
+ or event.chat.admin_rights
+ or event.chat.creator
+ or event.chat.default_banned_rights.embed_links
+ ):
+ li = await Carbon(
+ code=stdout,
+ file_name="bash",
+ download=True,
+ backgroundColor=choice(ATRA_COL),
+ rayso=True,
+ )
+ if isinstance(li, dict):
+ await xx.edit(
+ f"Unknown Response from Carbon: `{li}`\n\nstdout`:{stdout}`\nstderr: `{stderr}`"
+ )
+ return
+ url = uf(li)
+ OUT = f"[\xad]({url}){OUT}"
+ out = "**• OUTPUT:**"
+ remove(li)
+ else:
+ if "pip" in cmd and all(":" in line for line in stdout.split("\n")):
+ try:
+ load = safe_load(stdout)
+ stdout = ""
+ for data in list(load.keys()):
+ res = load[data] or ""
+ if res and "http" not in str(res):
+ res = f"`{res}`"
+ stdout += f"**{data}** : {res}\n"
+ yamlf = True
+ except Exception as er:
+ stdout = f"`{stdout}`"
+ LOGS.exception(er)
+ else:
+ stdout = f"`{stdout}`"
+ out = f"**• OUTPUT:**\n{stdout}"
+ if not stderr and not stdout:
+ out = "**• OUTPUT:**\n`Success`"
+ OUT += err + out
+ if len(OUT) > 4096:
+ ultd = err + out
+ with BytesIO(str.encode(ultd)) as out_file:
+ out_file.name = "bash.txt"
+ await event.client.send_file(
+ event.chat_id,
+ out_file,
+ force_document=True,
+ thumb=ULTConfig.thumb,
+ allow_cache=False,
+ caption=f"`{cmd}`" if len(cmd) < 998 else None,
+ reply_to=reply_to_id,
+ )
+
+ await xx.delete()
+ else:
+ await xx.edit(OUT, link_preview=not yamlf)
+
+
+pp = pprint # ignore: pylint
+bot = ultroid = ultroid_bot
+
+
+class u:
+ _ = ""
+
+
+def _parse_eval(value=None):
+ if not value:
+ return value
+ if hasattr(value, "stringify"):
+ try:
+ return value.stringify()
+ except TypeError:
+ pass
+ elif isinstance(value, dict):
+ try:
+ return json_parser(value, indent=1)
+ except BaseException:
+ pass
+ elif isinstance(value, list):
+ newlist = "["
+ for index, child in enumerate(value):
+ newlist += "\n " + str(_parse_eval(child))
+ if index < len(value) - 1:
+ newlist += ","
+ newlist += "\n]"
+ return newlist
+ return str(value)
+
+
+@ultroid_cmd(pattern="eval", fullsudo=True, only_devs=True)
+async def _(event):
+ try:
+ cmd = event.text.split(maxsplit=1)[1]
+ except IndexError:
+ return await event.eor(get_string("devs_2"), time=5)
+ xx = None
+ mode = ""
+ spli = cmd.split()
+
+ async def get_():
+ try:
+ cm = cmd.split(maxsplit=1)[1]
+ except IndexError:
+ await event.eor("->> Wrong Format <<-")
+ cm = None
+ return cm
+
+ if spli[0] in ["-s", "--silent"]:
+ await event.delete()
+ mode = "silent"
+ elif spli[0] in ["-n", "-noedit"]:
+ mode = "no-edit"
+ xx = await event.reply(get_string("com_1"))
+ elif spli[0] in ["-gs", "--source"]:
+ mode = "gsource"
+ elif spli[0] in ["-ga", "--args"]:
+ mode = "g-args"
+ if mode:
+ cmd = await get_()
+ if not cmd:
+ return
+ if not mode == "silent" and not xx:
+ xx = await event.eor(get_string("com_1"))
+ if black:
+ try:
+ cmd = black.format_str(cmd, mode=black.Mode())
+ except BaseException:
+ # Consider it as Code Error, and move on to be shown ahead.
+ pass
+ reply_to_id = event.reply_to_msg_id or event
+ if any(item in cmd for item in KEEP_SAFE().All) and (
+ not (event.out or event.sender_id == ultroid_bot.uid)
+ ):
+ warning = await event.forward_to(udB.get_key("LOG_CHANNEL"))
+ await warning.reply(
+ f"Malicious Activities suspected by {inline_mention(await event.get_sender())}"
+ )
+ _ignore_eval.append(event.sender_id)
+ return await xx.edit(
+ "`Malicious Activities suspected⚠️!\nReported to owner. Aborted this request!`"
+ )
+ old_stderr = sys.stderr
+ old_stdout = sys.stdout
+ redirected_output = sys.stdout = StringIO()
+ redirected_error = sys.stderr = StringIO()
+ stdout, stderr, exc, timeg = None, None, None, None
+ tima = time.time()
+ try:
+ value = await aexec(cmd, event)
+ except Exception:
+ value = None
+ exc = traceback.format_exc()
+ tima = time.time() - tima
+ stdout = redirected_output.getvalue()
+ stderr = redirected_error.getvalue()
+ sys.stdout = old_stdout
+ sys.stderr = old_stderr
+ if value:
+ try:
+ if mode == "gsource":
+ exc = inspect.getsource(value)
+ elif mode == "g-args":
+ args = inspect.signature(value).parameters.values()
+ name = ""
+ if hasattr(value, "__name__"):
+ name = value.__name__
+ exc = f"**{name}**\n\n" + "\n ".join([str(arg) for arg in args])
+ except Exception:
+ exc = traceback.format_exc()
+ evaluation = exc or stderr or stdout or _parse_eval(value) or get_string("instu_4")
+ if mode == "silent":
+ if exc:
+ msg = f"• EVAL ERROR\n\n• CHAT: {get_display_name(event.chat)}
[{event.chat_id}
]"
+ msg += f"\n\n∆ CODE:\n{cmd}
\n\n∆ ERROR:\n{exc}
"
+ log_chat = udB.get_key("LOG_CHANNEL")
+ if len(msg) > 4000:
+ with BytesIO(msg.encode()) as out_file:
+ out_file.name = "Eval-Error.txt"
+ return await event.client.send_message(
+ log_chat, f"`{cmd}`", file=out_file
+ )
+ await event.client.send_message(log_chat, msg, parse_mode="html")
+ return
+ tmt = tima * 1000
+ timef = time_formatter(tmt)
+ timeform = timef if not timef == "0s" else f"{tmt:.3f}ms"
+ final_output = "__►__ **EVAL** (__in {}__)\n```{}``` \n\n __►__ **OUTPUT**: \n```{}``` \n".format(
+ timeform,
+ cmd,
+ evaluation,
+ )
+ if len(final_output) > 4096:
+ final_output = evaluation
+ with BytesIO(str.encode(final_output)) as out_file:
+ out_file.name = "eval.txt"
+ await event.client.send_file(
+ event.chat_id,
+ out_file,
+ force_document=True,
+ thumb=ULTConfig.thumb,
+ allow_cache=False,
+ caption=f"```{cmd}```" if len(cmd) < 998 else None,
+ reply_to=reply_to_id,
+ )
+ return await xx.delete()
+ await xx.edit(final_output)
+
+
+def _stringify(text=None, *args, **kwargs):
+ if text:
+ u._ = text
+ text = _parse_eval(text)
+ return print(text, *args, **kwargs)
+
+
+async def aexec(code, event):
+ exec(
+ (
+ "async def __aexec(e, client): "
+ + "\n print = p = _stringify"
+ + "\n message = event = e"
+ + "\n u.r = reply = await event.get_reply_message()"
+ + "\n chat = event.chat_id"
+ + "\n u.lr = locals()"
+ )
+ + "".join(f"\n {l}" for l in code.split("\n"))
+ )
+
+ return await locals()["__aexec"](event, event.client)
+
+
+DUMMY_CPP = """#include
+using namespace std;
+
+int main(){
+!code
+}
+"""
+
+
+@ultroid_cmd(pattern="cpp", only_devs=True)
+async def doie(e):
+ match = e.text.split(" ", maxsplit=1)
+ try:
+ match = match[1]
+ except IndexError:
+ return await e.eor(get_string("devs_3"))
+ msg = await e.eor(get_string("com_1"))
+ if "main(" not in match:
+ new_m = "".join(" " * 4 + i + "\n" for i in match.split("\n"))
+ match = DUMMY_CPP.replace("!code", new_m)
+ open("cpp-ultroid.cpp", "w").write(match)
+ m = await bash("g++ -o CppUltroid cpp-ultroid.cpp")
+ o_cpp = f"• **Eval-Cpp**\n`{match}`"
+ if m[1]:
+ o_cpp += f"\n\n**• Error :**\n`{m[1]}`"
+ if len(o_cpp) > 3000:
+ os.remove("cpp-ultroid.cpp")
+ if os.path.exists("CppUltroid"):
+ os.remove("CppUltroid")
+ with BytesIO(str.encode(o_cpp)) as out_file:
+ out_file.name = "error.txt"
+ return await msg.reply(f"`{match}`", file=out_file)
+ return await eor(msg, o_cpp)
+ m = await bash("./CppUltroid")
+ if m[0] != "":
+ o_cpp += f"\n\n**• Output :**\n`{m[0]}`"
+ if m[1]:
+ o_cpp += f"\n\n**• Error :**\n`{m[1]}`"
+ if len(o_cpp) > 3000:
+ with BytesIO(str.encode(o_cpp)) as out_file:
+ out_file.name = "eval.txt"
+ await msg.reply(f"`{match}`", file=out_file)
+ else:
+ await eor(msg, o_cpp)
+ os.remove("CppUltroid")
+ os.remove("cpp-ultroid.cpp")
diff --git a/plugins/downloadupload.py b/plugins/downloadupload.py
new file mode 100644
index 0000000000000000000000000000000000000000..d5f5e304f8ed2f714f58954958e25e9e376e1a66
--- /dev/null
+++ b/plugins/downloadupload.py
@@ -0,0 +1,222 @@
+# Ultroid - UserBot
+# Copyright (C) 2021-2025 TeamUltroid
+#
+# This file is a part of < https://github.com/TeamUltroid/Ultroid/ >
+# PLease read the GNU Affero General Public License in
+# .
+
+from . import get_help
+
+__doc__ = get_help("help_downloadupload")
+
+import asyncio
+import glob
+import os
+import time
+from datetime import datetime as dt
+
+from aiohttp.client_exceptions import InvalidURL
+from telethon.errors.rpcerrorlist import MessageNotModifiedError
+
+from pyUltroid.fns.helper import time_formatter
+from pyUltroid.fns.tools import get_chat_and_msgid, set_attributes
+
+from . import (
+ LOGS,
+ ULTConfig,
+ downloader,
+ eor,
+ fast_download,
+ get_all_files,
+ get_string,
+ progress,
+ time_formatter,
+ ultroid_cmd,
+)
+
+
+@ultroid_cmd(
+ pattern="download( (.*)|$)",
+)
+async def down(event):
+ matched = event.pattern_match.group(1).strip()
+ msg = await event.eor(get_string("udl_4"))
+ if not matched:
+ return await eor(msg, get_string("udl_5"), time=5)
+ try:
+ splited = matched.split(" | ")
+ link = splited[0]
+ filename = splited[1]
+ except IndexError:
+ filename = None
+ s_time = time.time()
+ try:
+ filename, d = await fast_download(
+ link,
+ filename,
+ progress_callback=lambda d, t: asyncio.get_event_loop().create_task(
+ progress(
+ d,
+ t,
+ msg,
+ s_time,
+ f"Downloading from {link}",
+ )
+ ),
+ )
+ except InvalidURL:
+ return await msg.eor("`Invalid URL provided :(`", time=5)
+ await msg.eor(f"`{filename}` `downloaded in {time_formatter(d*1000)}.`")
+
+
+@ultroid_cmd(
+ pattern="dl( (.*)|$)",
+)
+async def download(event):
+ match = event.pattern_match.group(1).strip()
+ if match and "t.me/" in match:
+ chat, msg = get_chat_and_msgid(match)
+ if not (chat and msg):
+ return await event.eor(get_string("gms_1"))
+ match = ""
+ ok = await event.client.get_messages(chat, ids=msg)
+ elif event.reply_to_msg_id:
+ ok = await event.get_reply_message()
+ else:
+ return await event.eor(get_string("cvt_3"), time=8)
+ xx = await event.eor(get_string("com_1"))
+ if not (ok and ok.media):
+ return await xx.eor(get_string("udl_1"), time=5)
+ s = dt.now()
+ k = time.time()
+ if hasattr(ok.media, "document"):
+ file = ok.media.document
+ mime_type = file.mime_type
+ filename = match or ok.file.name
+ if not filename:
+ if "audio" in mime_type:
+ filename = "audio_" + dt.now().isoformat("_", "seconds") + ".ogg"
+ elif "video" in mime_type:
+ filename = "video_" + dt.now().isoformat("_", "seconds") + ".mp4"
+ try:
+ result = await downloader(
+ f"resources/downloads/{filename}",
+ file,
+ xx,
+ k,
+ f"Downloading {filename}...",
+ )
+
+ except MessageNotModifiedError as err:
+ return await xx.edit(str(err))
+ file_name = result.name
+ else:
+ d = "resources/downloads/"
+ file_name = await event.client.download_media(
+ ok,
+ d,
+ progress_callback=lambda d, t: asyncio.get_event_loop().create_task(
+ progress(
+ d,
+ t,
+ xx,
+ k,
+ get_string("com_5"),
+ ),
+ ),
+ )
+ e = dt.now()
+ t = time_formatter(((e - s).seconds) * 1000)
+ await xx.eor(get_string("udl_2").format(file_name, t))
+
+
+@ultroid_cmd(
+ pattern="ul( (.*)|$)",
+)
+async def _(event):
+ msg = await event.eor(get_string("com_1"))
+ match = event.pattern_match.group(1)
+ if match:
+ match = match.strip()
+ if not event.out and match == ".env":
+ return await event.reply("`You can't do this...`")
+ stream, force_doc, delete, thumb = (
+ False,
+ True,
+ False,
+ ULTConfig.thumb,
+ )
+ if "--stream" in match:
+ stream = True
+ force_doc = False
+ if "--delete" in match:
+ delete = True
+ if "--no-thumb" in match:
+ thumb = None
+ arguments = ["--stream", "--delete", "--no-thumb"]
+ if any(item in match for item in arguments):
+ match = (
+ match.replace("--stream", "")
+ .replace("--delete", "")
+ .replace("--no-thumb", "")
+ .strip()
+ )
+ if match.endswith("/"):
+ match += "*"
+ results = glob.glob(match)
+ if not results and os.path.exists(match):
+ results = [match]
+ if not results:
+ try:
+ await event.reply(file=match)
+ return await event.try_delete()
+ except Exception as er:
+ LOGS.exception(er)
+ return await msg.eor(get_string("ls1"))
+ for result in results:
+ if os.path.isdir(result):
+ c, s = 0, 0
+ for files in get_all_files(result):
+ attributes = None
+ if stream:
+ try:
+ attributes = await set_attributes(files)
+ except KeyError as er:
+ LOGS.exception(er)
+ try:
+ file, _ = await event.client.fast_uploader(
+ files, show_progress=True, event=msg, to_delete=delete
+ )
+ await event.client.send_file(
+ event.chat_id,
+ file,
+ supports_streaming=stream,
+ force_document=force_doc,
+ thumb=thumb,
+ attributes=attributes,
+ caption=f"`Uploaded` `{files}` `in {time_formatter(_*1000)}`",
+ reply_to=event.reply_to_msg_id or event,
+ )
+ s += 1
+ except (ValueError, IsADirectoryError):
+ c += 1
+ break
+ attributes = None
+ if stream:
+ try:
+ attributes = await set_attributes(result)
+ except KeyError as er:
+ LOGS.exception(er)
+ file, _ = await event.client.fast_uploader(
+ result, show_progress=True, event=msg, to_delete=delete
+ )
+ await event.client.send_file(
+ event.chat_id,
+ file,
+ supports_streaming=stream,
+ force_document=force_doc,
+ thumb=thumb,
+ attributes=attributes,
+ caption=f"`Uploaded` `{result}` `in {time_formatter(_*1000)}`",
+ )
+ await msg.try_delete()
diff --git a/plugins/echo.py b/plugins/echo.py
new file mode 100644
index 0000000000000000000000000000000000000000..bda8fff45e607ac72c0787746f021eed4798aeee
--- /dev/null
+++ b/plugins/echo.py
@@ -0,0 +1,76 @@
+# Ultroid - UserBot
+# Copyright (C) 2021-2025 TeamUltroid
+#
+# This file is a part of < https://github.com/TeamUltroid/Ultroid/ >
+# PLease read the GNU Affero General Public License in
+# .
+
+from . import get_help
+
+__doc__ = get_help("help_echo")
+
+
+from telethon.utils import get_display_name
+
+from pyUltroid.dB.echo_db import add_echo, check_echo, list_echo, rem_echo
+
+from . import inline_mention, ultroid_cmd
+
+
+@ultroid_cmd(pattern="addecho( (.*)|$)")
+async def echo(e):
+ r = await e.get_reply_message()
+ if r:
+ user = r.sender_id
+ else:
+ try:
+ user = e.text.split()[1]
+ if user.startswith("@"):
+ ok = await e.client.get_entity(user)
+ user = ok.id
+ else:
+ user = int(user)
+ except BaseException:
+ return await e.eor("Reply To A user.", time=5)
+ if check_echo(e.chat_id, user):
+ return await e.eor("Echo already activated for this user.", time=5)
+ add_echo(e.chat_id, user)
+ ok = await e.client.get_entity(user)
+ user = inline_mention(ok)
+ await e.eor(f"Activated Echo For {user}.")
+
+
+@ultroid_cmd(pattern="remecho( (.*)|$)")
+async def rm(e):
+ r = await e.get_reply_message()
+ if r:
+ user = r.sender_id
+ else:
+ try:
+ user = e.text.split()[1]
+ if user.startswith("@"):
+ ok = await e.client.get_entity(user)
+ user = ok.id
+ else:
+ user = int(user)
+ except BaseException:
+ return await e.eor("Reply To A User.", time=5)
+ if check_echo(e.chat_id, user):
+ rem_echo(e.chat_id, user)
+ ok = await e.client.get_entity(user)
+ user = f"[{get_display_name(ok)}](tg://user?id={ok.id})"
+ return await e.eor(f"Deactivated Echo For {user}.")
+ await e.eor("Echo not activated for this user")
+
+
+@ultroid_cmd(pattern="listecho$")
+async def lstecho(e):
+ if k := list_echo(e.chat_id):
+ user = "**Activated Echo For Users:**\n\n"
+ for x in k:
+ ok = await e.client.get_entity(int(x))
+ kk = f"[{get_display_name(ok)}](tg://user?id={ok.id})"
+ user += f"•{kk}" + "\n"
+ await e.eor(user)
+ else:
+ await e.eor("`List is Empty, For echo`", time=5)
diff --git a/plugins/extra.py b/plugins/extra.py
new file mode 100644
index 0000000000000000000000000000000000000000..7fa9ac8177977f3deaadec9e2bd3bee43a7e6598
--- /dev/null
+++ b/plugins/extra.py
@@ -0,0 +1,85 @@
+# Ultroid - UserBot
+# Copyright (C) 2021-2025 TeamUltroid
+#
+# This file is a part of < https://github.com/TeamUltroid/Ultroid/ >
+# PLease read the GNU Affero General Public License in
+# .
+
+from . import get_help
+
+__doc__ = get_help("extra")
+
+import asyncio
+
+from . import get_string, ultroid_cmd
+
+
+@ultroid_cmd(
+ pattern="del$",
+ manager=True,
+)
+async def delete_it(delme):
+ msg_src = await delme.get_reply_message()
+ if not msg_src:
+ return
+ await msg_src.try_delete()
+ await delme.try_delete()
+
+
+@ultroid_cmd(
+ pattern="copy$",
+)
+async def copy(e):
+ reply = await e.get_reply_message()
+ if reply:
+ await reply.reply(reply)
+ return await e.try_delete()
+ await e.eor(get_string("ex_1"), time=5)
+
+
+@ultroid_cmd(
+ pattern="edit",
+)
+async def editer(edit):
+ message = edit.text
+ chat = await edit.get_input_chat()
+ string = str(message[6:])
+ reply = await edit.get_reply_message()
+ if reply and reply.text:
+ try:
+ await reply.edit(string)
+ await edit.delete()
+ except BaseException:
+ pass
+ else:
+ i = 1
+ async for message in edit.client.iter_messages(chat, from_user="me", limit=2):
+ if i == 2:
+ await message.edit(string)
+ await edit.delete()
+ break
+ i += 1
+
+
+@ultroid_cmd(
+ pattern="reply$",
+)
+async def _(e):
+ if e.reply_to_msg_id:
+ chat = e.chat_id
+ try:
+ msg = (await e.client.get_messages(e.chat_id, limit=1, max_id=e.id))[0]
+ except IndexError:
+ return await e.eor(
+ "`You have previously sent no message to reply again...`", time=5
+ )
+ except BaseException as er:
+ return await e.eor(f"**ERROR:** `{er}`")
+ await asyncio.wait(
+ [
+ e.client.delete_messages(chat, [e.id, msg.id]),
+ e.client.send_message(chat, msg, reply_to=e.reply_to_msg_id),
+ ]
+ )
+ else:
+ await e.try_delete()
diff --git a/plugins/fakeaction.py b/plugins/fakeaction.py
new file mode 100644
index 0000000000000000000000000000000000000000..7e889419595c2d5a3a7c9ae898090dbf3967a50a
--- /dev/null
+++ b/plugins/fakeaction.py
@@ -0,0 +1,36 @@
+# Ultroid - UserBot
+# Copyright (C) 2021-2025 TeamUltroid
+#
+# This file is a part of < https://github.com/TeamUltroid/Ultroid/ >
+# PLease read the GNU Affero General Public License in
+# .
+
+from . import get_help
+
+__doc__ = get_help("help_fakeaction")
+
+import math
+import time
+
+from pyUltroid.fns.admins import ban_time
+
+from . import asyncio, get_string, ultroid_cmd
+
+
+@ultroid_cmd(
+ pattern="f(typing|audio|contact|document|game|location|sticker|photo|round|video)( (.*)|$)"
+)
+async def _(e):
+ act = e.pattern_match.group(1).strip()
+ t = e.pattern_match.group(2)
+ if act in ["audio", "round", "video"]:
+ act = f"record-{act}"
+ if t.isdigit():
+ t = int(t)
+ elif t.endswith(("s", "h", "d", "m")):
+ t = math.ceil((ban_time(t)) - time.time())
+ else:
+ t = 60
+ await e.eor(get_string("fka_1").format(str(t)), time=5)
+ async with e.client.action(e.chat_id, act):
+ await asyncio.sleep(t)
diff --git a/plugins/fileshare.py b/plugins/fileshare.py
new file mode 100644
index 0000000000000000000000000000000000000000..62497d581356bef40aaea6940c9cf8ec1df04981
--- /dev/null
+++ b/plugins/fileshare.py
@@ -0,0 +1,95 @@
+# Ultroid - UserBot
+# Copyright (C) 2021-2025 TeamUltroid
+#
+# This file is a part of < https://github.com/TeamUltroid/Ultroid/ >
+# PLease read the GNU Affero General Public License in
+# .
+
+from . import get_help
+
+__doc__ = get_help("help_fileshare")
+
+import os
+
+from pyUltroid.dB.filestore_db import del_stored, get_stored_msg, list_all_stored_msgs
+from pyUltroid.fns.tools import get_file_link
+
+from . import HNDLR, asst, get_string, in_pattern, udB, ultroid_bot, ultroid_cmd
+
+
+@ultroid_cmd(pattern="store$")
+async def filestoreplg(event):
+ msg = await event.get_reply_message()
+ if not msg:
+ return await event.eor(get_string("fsh_3"), time=10)
+ # allow storing both messages and media.
+ filehash = await get_file_link(msg)
+ link_to_file = f"https://t.me/{asst.me.username}?start={filehash}"
+ await event.eor(
+ get_string("fsh_2").format(link_to_file),
+ link_preview=False,
+ )
+
+
+@ultroid_cmd("delstored ?(.*)")
+async def _(event):
+ match = event.pattern_match.group(1)
+ if not match:
+ return await event.eor("`Give stored film's link to delete.`", time=5)
+ match = match.split("?start=")
+ botusername = match[0].split("/")[-1]
+ if botusername != asst.me.username:
+ return await event.eor(
+ "`Message/Media of provided link was not stored by this bot.`", time=5
+ )
+ msg_id = get_stored_msg(match[1])
+ if not msg_id:
+ return await event.eor(
+ "`Message/Media of provided link was already deleted.`", time=5
+ )
+ del_stored(match[1])
+ await ultroid_bot.delete_messages(udB.get_key("LOG_CHANNEL"), int(msg_id))
+ await event.eor("__Deleted__")
+
+
+@ultroid_cmd("liststored$")
+async def liststored(event):
+ files = list_all_stored_msgs()
+ if not files:
+ return await event.eor(get_string("fsh_4"), time=5)
+ msg = "**Stored files:**\n"
+ for c, i in enumerate(files, start=1):
+ msg += f"`{c}`. https://t.me/{asst.me.username}?start={i}\n"
+ if len(msg) > 4095:
+ with open("liststored.txt", "w") as f:
+ f.write(msg.replace("**", "").replace("`", ""))
+ await event.reply(get_string("fsh_1"), file="liststored.txt")
+ os.remove("liststored.txt")
+ return
+ await event.eor(msg, link_preview=False)
+
+
+@in_pattern("filestore", owner=True)
+async def file_short(event):
+ all_ = list_all_stored_msgs()
+ res = []
+ if all_:
+ LOG_CHA = udB.get_key("LOG_CHANNEL")
+ for msg in all_[:50]:
+ m_id = get_stored_msg(msg)
+ if not m_id:
+ continue
+ message = await asst.get_messages(LOG_CHA, ids=m_id)
+ if not message:
+ continue
+ if message.media:
+ res.append(await event.builder.document(title=msg, file=message.media))
+ elif message.text:
+ res.append(
+ await event.builder.article(title=message.text, text=message.text)
+ )
+ if not res:
+ title = "You have no stored file :("
+ text = f"{title}\n\nRead `{HNDLR}help fileshare` to know how to store."
+ return await event.answer([await event.builder.article(title=title, text=text)])
+ await event.answer(res, switch_pm="• File Store •", switch_pm_param="start")
diff --git a/plugins/filter.py b/plugins/filter.py
new file mode 100644
index 0000000000000000000000000000000000000000..b330769135fbc413adfa2dede3fe668b2ebd0335
--- /dev/null
+++ b/plugins/filter.py
@@ -0,0 +1,100 @@
+# Ultroid - UserBot
+# Copyright (C) 2021-2025 TeamUltroid
+#
+# This file is a part of < https://github.com/TeamUltroid/Ultroid/ >
+# PLease read the GNU Affero General Public License in
+# .
+
+from . import get_help
+
+__doc__ = get_help("help_filter")
+
+import os
+import re
+
+from telethon.tl.types import User
+from telethon.utils import pack_bot_file_id
+
+from pyUltroid.dB.filter_db import add_filter, get_filter, list_filter, rem_filter
+from pyUltroid.fns.tools import create_tl_btn, format_btn, get_msg_button
+
+from . import events, get_string, mediainfo, udB, ultroid_bot, ultroid_cmd, upload_file
+from ._inline import something
+
+
+@ultroid_cmd(pattern="addfilter( (.*)|$)")
+async def af(e):
+ wrd = (e.pattern_match.group(1).strip()).lower()
+ wt = await e.get_reply_message()
+ chat = e.chat_id
+ if not (wt and wrd):
+ return await e.eor(get_string("flr_1"))
+ btn = format_btn(wt.buttons) if wt.buttons else None
+ if wt and wt.media:
+ wut = mediainfo(wt.media)
+ if wut.startswith(("pic", "gif")):
+ dl = await wt.download_media()
+ m = upload_file(dl)
+ os.remove(dl)
+ elif wut == "video":
+ if wt.media.document.size > 8 * 1000 * 1000:
+ return await e.eor(get_string("com_4"), time=5)
+ dl = await wt.download_media()
+ m = upload_file(dl)
+ os.remove(dl)
+ else:
+ m = pack_bot_file_id(wt.media)
+ if wt.text:
+ txt = wt.text
+ if not btn:
+ txt, btn = get_msg_button(wt.text)
+ add_filter(chat, wrd, txt, m, btn)
+ else:
+ add_filter(chat, wrd, None, m, btn)
+ else:
+ txt = wt.text
+ if not btn:
+ txt, btn = get_msg_button(wt.text)
+ add_filter(chat, wrd, txt, None, btn)
+ await e.eor(get_string("flr_4").format(wrd))
+ ultroid_bot.add_handler(filter_func, events.NewMessage())
+
+
+@ultroid_cmd(pattern="remfilter( (.*)|$)")
+async def rf(e):
+ wrd = (e.pattern_match.group(1).strip()).lower()
+ chat = e.chat_id
+ if not wrd:
+ return await e.eor(get_string("flr_3"))
+ rem_filter(int(chat), wrd)
+ await e.eor(get_string("flr_5").format(wrd))
+
+
+@ultroid_cmd(pattern="listfilter$")
+async def lsnote(e):
+ if x := list_filter(e.chat_id):
+ sd = "Filters Found In This Chats Are\n\n"
+ return await e.eor(sd + x)
+ await e.eor(get_string("flr_6"))
+
+
+async def filter_func(e):
+ if isinstance(e.sender, User) and e.sender.bot:
+ return
+ xx = (e.text).lower()
+ chat = e.chat_id
+ if x := get_filter(chat):
+ for c in x:
+ pat = r"( |^|[^\w])" + re.escape(c) + r"( |$|[^\w])"
+ if re.search(pat, xx):
+ if k := x.get(c):
+ msg = k["msg"]
+ media = k["media"]
+ if k.get("button"):
+ btn = create_tl_btn(k["button"])
+ return await something(e, msg, media, btn)
+ await e.reply(msg, file=media)
+
+
+if udB.get_key("FILTERS"):
+ ultroid_bot.add_handler(filter_func, events.NewMessage())
diff --git a/plugins/fontgen.py b/plugins/fontgen.py
new file mode 100644
index 0000000000000000000000000000000000000000..d686c75f1fbbf9281a8ed4775e2256d16ab6e647
--- /dev/null
+++ b/plugins/fontgen.py
@@ -0,0 +1,60 @@
+# Ultroid - UserBot
+# Copyright (C) 2021-2025 TeamUltroid
+#
+# This file is a part of < https://github.com/TeamUltroid/Ultroid/ >
+# PLease read the GNU Affero General Public License in
+# .
+
+from . import get_help
+
+__doc__ = get_help("help_fontgen")
+
+import string
+
+from . import eod, ultroid_cmd
+
+_default = string.ascii_letters
+Fonts = {
+ "small caps": "ᴀʙᴄᴅᴇғɢʜɪᴊᴋʟᴍɴᴏᴘϙʀsᴛᴜᴠᴡxʏᴢABCDEFGHIJKLMNOPQRSTUVWXYZ",
+ "monospace": "𝚊𝚋𝚌𝚍𝚎𝚏𝚐𝚑𝚒𝚓𝚔𝚕𝚖𝚗𝚘𝚙𝚚𝚛𝚜𝚝𝚞𝚟𝚠𝚡𝚢𝚣𝙰𝙱𝙲𝙳𝙴𝙵𝙶𝙷𝙸𝙹𝙺𝙻𝙼𝙽𝙾𝙿𝚀𝚁𝚂𝚃𝚄𝚅𝚆𝚇𝚈𝚉",
+ "double stroke": "𝕒𝕓𝕔𝕕𝕖𝕗𝕘𝕙𝕚𝕛𝕜𝕝𝕞𝕟𝕠𝕡𝕢𝕣𝕤𝕥𝕦𝕧𝕨𝕩𝕪𝕫𝔸𝔹ℂ𝔻𝔼𝔽𝔾ℍ𝕀𝕁𝕂𝕃𝕄ℕ𝕆ℙℚℝ𝕊𝕋𝕌𝕍𝕎𝕏𝕐ℤ",
+ "script royal": "𝒶𝒷𝒸𝒹𝑒𝒻𝑔𝒽𝒾𝒿𝓀𝓁𝓂𝓃𝑜𝓅𝓆𝓇𝓈𝓉𝓊𝓋𝓌𝓍𝓎𝓏𝒜ℬ𝒞𝒟ℰℱ𝒢ℋℐ𝒥𝒦ℒℳ𝒩𝒪𝒫𝒬ℛ𝒮𝒯𝒰𝒱𝒲𝒳𝒴𝒵",
+}
+
+
+@ultroid_cmd(
+ pattern="font( (.*)|$)",
+)
+async def _(e):
+ input = e.pattern_match.group(1).strip()
+ reply = await e.get_reply_message()
+ if not input:
+ m = "**Available Fonts**\n\n"
+ for x in Fonts.keys():
+ m += f"• `{x}`\n"
+ return await e.eor(m, time=5)
+ if not reply:
+ try:
+ _ = input.split(":", maxsplit=1)
+ font = _[0][:-1]
+ text = _[1]
+ except IndexError:
+ return await eod(e, help)
+ elif not input:
+ return await eod(e, "`Give font dude :/`")
+ else:
+ font = input
+ text = reply.message
+ if font not in Fonts.keys():
+ return await e.eor(f"`{font} not in font list`.", time=5)
+ msg = gen_font(text, Fonts[font])
+ await e.eor(msg)
+
+
+def gen_font(text, new_font):
+ new_font = " ".join(new_font).split()
+ for q in text:
+ if q in _default:
+ new = new_font[_default.index(q)]
+ text = text.replace(q, new)
+ return text
diff --git a/plugins/forcesubscribe.py b/plugins/forcesubscribe.py
new file mode 100644
index 0000000000000000000000000000000000000000..bc0a01f85f07b7b9185b07b452b9579706be916d
--- /dev/null
+++ b/plugins/forcesubscribe.py
@@ -0,0 +1,179 @@
+# Ultroid - UserBot
+# Copyright (C) 2021-2025 TeamUltroid
+#
+# This file is a part of < https://github.com/TeamUltroid/Ultroid/ >
+# PLease read the GNU Affero General Public License in
+# .
+"""
+✘ Commands Available -
+
+• `{i}fsub `
+ Enable ForceSub in Used Chat !
+
+• `{i}checkfsub`
+ Check/Get Active ForceSub Setting of Used Chat.
+
+• `{i}remfsub`
+ Remove ForceSub from Used Chat !
+
+ Note - You Need to be Admin in Both Channel/Chats
+ in order to Use ForceSubscribe.
+"""
+
+import re
+
+from telethon.errors.rpcerrorlist import ChatAdminRequiredError, UserNotParticipantError
+from telethon.tl.custom import Button
+from telethon.tl.functions.channels import GetParticipantRequest
+from telethon.tl.functions.messages import ExportChatInviteRequest
+from telethon.tl.types import (
+ Channel,
+ ChannelParticipantBanned,
+ ChannelParticipantLeft,
+ User,
+)
+
+from pyUltroid.dB.forcesub_db import add_forcesub, get_forcesetting, rem_forcesub
+
+from . import (
+ LOGS,
+ asst,
+ callback,
+ events,
+ get_string,
+ in_pattern,
+ inline_mention,
+ udB,
+ ultroid_bot,
+ ultroid_cmd,
+)
+
+CACHE = {}
+
+
+@ultroid_cmd(pattern="fsub( (.*)|$)", admins_only=True, groups_only=True)
+async def addfor(e):
+ match = e.pattern_match.group(1).strip()
+ if not match:
+ return await e.eor(get_string("fsub_1"), time=5)
+ try:
+ match = await e.client.parse_id(match)
+ except BaseException:
+ return await e.eor(get_string("fsub_2"), time=5)
+ add_forcesub(e.chat_id, match)
+ await e.eor("Added ForceSub in This Chat !")
+ ultroid_bot.add_handler(force_sub, events.NewMessage(incoming=True))
+
+
+@ultroid_cmd(pattern="remfsub$")
+async def remor(e):
+ res = rem_forcesub(e.chat_id)
+ if not res:
+ return await e.eor(get_string("fsub_3"), time=5)
+ await e.eor("Removed ForceSub...")
+
+
+@ultroid_cmd(pattern="checkfsub$")
+async def getfsr(e):
+ res = get_forcesetting(e.chat_id)
+ if not res:
+ return await e.eor("ForceSub is Not Active In This Chat !", time=5)
+ cha = await e.client.get_entity(int(res))
+ await e.eor(f"**ForceSub Status** : `Active`\n- **{cha.title}** `({res})`")
+
+
+@in_pattern("fsub( (.*)|$)", owner=True)
+async def fcall(e):
+ match = e.pattern_match.group(1).strip()
+ spli = match.split("_")
+ user = await ultroid_bot.get_entity(int(spli[0]))
+ cl = await ultroid_bot.get_entity(int(spli[1]))
+ text = f"Hi {inline_mention(user)}, You Need to Join"
+ text += f" {cl.title} in order to Chat in this Group."
+ el = (
+ f"https://t.me/{cl.username}"
+ if cl.username
+ else (await ultroid_bot(ExportChatInviteRequest(cl))).link
+ )
+
+ res = [
+ await e.builder.article(
+ title="forcesub",
+ text=text,
+ buttons=[
+ [Button.url(text=get_string("fsub_4"), url=el)],
+ [Button.inline(get_string("fsub_5"), data=f"unm_{match}")],
+ ],
+ )
+ ]
+ await e.answer(res)
+
+
+@callback(re.compile("unm_(.*)"))
+async def diesoon(e):
+ match = (e.data_match.group(1)).decode("UTF-8")
+ spli = match.split("_")
+ if e.sender_id != int(spli[0]):
+ return await e.answer(get_string("fsub_7"), alert=True)
+ try:
+ values = await ultroid_bot(GetParticipantRequest(int(spli[1]), int(spli[0])))
+ if isinstance(values.participant, ChannelParticipantLeft) or (
+ isinstance(values.participant, ChannelParticipantBanned) and values.left
+ ):
+ raise UserNotParticipantError("")
+ except UserNotParticipantError:
+ return await e.answer(
+ "Please Join That Channel !\nThen Click This Button !", alert=True
+ )
+ await ultroid_bot.edit_permissions(
+ e.chat_id, int(spli[0]), send_messages=True, until_date=None
+ )
+ await e.edit(get_string("fsub_8"))
+
+
+async def force_sub(ult):
+ if not udB.get_key("FORCESUB"):
+ return
+ user = await ult.get_sender()
+ joinchat = get_forcesetting(ult.chat_id)
+ if (not joinchat) or (isinstance(user, User) and user.bot):
+ return
+ if CACHE.get(ult.chat_id):
+ if CACHE[ult.chat_id].get(user.id):
+ CACHE[ult.chat_id].update({user.id: CACHE[ult.chat_id][user.id] + 1})
+ else:
+ CACHE[ult.chat_id].update({user.id: 1})
+ else:
+ CACHE.update({ult.chat_id: {user.id: 1}})
+ count = CACHE[ult.chat_id][user.id]
+ if count == 11:
+ CACHE[ult.chat_id][user.id] = 1
+ return
+ if count in range(2, 11):
+ return
+ try:
+ await ultroid_bot.get_permissions(int(joinchat), user.id)
+ return
+ except UserNotParticipantError:
+ pass
+ if isinstance(user, Channel):
+ try:
+ await ultroid_bot.edit_permissions(
+ ult.chat_id, user.id, view_messages=False
+ )
+ return
+ except BaseException as er:
+ LOGS.exception(er)
+ try:
+ await ultroid_bot.edit_permissions(ult.chat_id, user.id, send_messages=False)
+ except ChatAdminRequiredError:
+ return
+ except Exception as e:
+ await ult.delete()
+ LOGS.info(e)
+ res = await ultroid_bot.inline_query(asst.me.username, f"fsub {user.id}_{joinchat}")
+ await res[0].click(ult.chat_id, reply_to=ult.id)
+
+
+if udB.get_key("FORCESUB"):
+ ultroid_bot.add_handler(force_sub, events.NewMessage(incoming=True))
diff --git a/plugins/gdrive.py b/plugins/gdrive.py
new file mode 100644
index 0000000000000000000000000000000000000000..bafffbd293df157725419efdbeaba95df1da6a05
--- /dev/null
+++ b/plugins/gdrive.py
@@ -0,0 +1,232 @@
+# Ultroid - UserBot
+# Copyright (C) 2021-2025 TeamUltroid
+#
+# This file is a part of < https://github.com/TeamUltroid/Ultroid/ >
+# PLease read the GNU Affero General Public License in
+# .
+"""
+✘ Commands Available
+
+• `{i}gdul `
+ Reply to file to upload on Google Drive.
+ Add file name to upload on Google Drive.
+
+• `{i}gdown | `
+ Download from Gdrive link or file id.
+
+• `{i}gdsearch `
+ Search file name on Google Drive and get link.
+
+• `{i}gdlist`
+ List all GDrive files.
+
+• `{i}gdfolder`
+ Link to your Google Drive Folder.
+ If added then all files will be uploaded in this folder.
+"""
+
+import os
+import time
+
+from telethon.tl.types import Message
+
+from pyUltroid.fns.gDrive import GDriveManager
+from pyUltroid.fns.helper import time_formatter
+
+from . import ULTConfig, asst, eod, eor, get_string, ultroid_cmd
+
+
+@ultroid_cmd(
+ pattern="gdown( (.*)|$)",
+ fullsudo=True,
+)
+async def gdown(event):
+ GDrive = GDriveManager()
+ match = event.pattern_match.group(1).strip()
+ if not match:
+ return await eod(event, "`Give file id or Gdrive link to download from!`")
+ filename = match.split(" | ")[1].strip() if " | " in match else None
+ eve = await event.eor(get_string("com_1"))
+ _start = time.time()
+ status, response = await GDrive._download_file(eve, match, filename)
+ if not status:
+ return await eve.edit(response)
+ await eve.edit(
+ f"`Downloaded ``{response}`` in {time_formatter((time.time() - _start)*1000)}`"
+ )
+
+
+@ultroid_cmd(
+ pattern="gdlist$",
+ fullsudo=True,
+)
+async def files(event):
+ GDrive = GDriveManager()
+ if not os.path.exists(GDrive.token_file):
+ return await event.eor(get_string("gdrive_6").format(asst.me.username))
+ eve = await event.eor(get_string("com_1"))
+ msg = ""
+ if files := GDrive._list_files:
+ msg += f"{len(files.keys())} files found in gdrive.\n\n"
+ for _ in files:
+ msg += f"> [{files[_]}]({_})\n"
+ else:
+ msg += "Nothing in Gdrive"
+ if len(msg) < 4096:
+ await eve.edit(msg, link_preview=False)
+ else:
+ with open("drive-files.txt", "w") as f:
+ f.write(
+ msg.replace("[", "File Name: ")
+ .replace("](", "\n» Link: ")
+ .replace(")\n", "\n\n")
+ )
+ try:
+ await eve.delete()
+ except BaseException:
+ pass
+ await event.client.send_file(
+ event.chat_id,
+ "drive-files.txt",
+ thumb=ULTConfig.thumb,
+ reply_to=event,
+ )
+ os.remove("drive-files.txt")
+
+
+@ultroid_cmd(
+ pattern="gdul( (.*)|$)",
+ fullsudo=True,
+)
+async def _(event):
+ GDrive = GDriveManager()
+ if not os.path.exists(GDrive.token_file):
+ return await eod(event, get_string("gdrive_6").format(asst.me.username))
+ input_file = event.pattern_match.group(1).strip() or await event.get_reply_message()
+ if not input_file:
+ return await eod(event, "`Reply to file or give its location.`")
+ mone = await event.eor(get_string("com_1"))
+ if isinstance(input_file, Message):
+ location = "resources/downloads"
+ if input_file.photo:
+ filename = await input_file.download_media(location)
+ else:
+ filename = input_file.file.name
+ if not filename:
+ filename = str(round(time.time()))
+ filename = f"{location}/{filename}"
+ try:
+ filename, downloaded_in = await event.client.fast_downloader(
+ file=input_file.media.document,
+ filename=filename,
+ show_progress=True,
+ event=mone,
+ message=get_string("com_5"),
+ )
+ filename = filename.name
+ except Exception as e:
+ return await eor(mone, str(e), time=10)
+ await mone.edit(
+ f"`Downloaded to ``{filename}`.`",
+ )
+ else:
+ filename = input_file.strip()
+ if not os.path.exists(filename):
+ return await eod(
+ mone,
+ "File Not found in local server. Give me a file path :((",
+ time=5,
+ )
+ folder_id = None
+ if os.path.isdir(filename):
+ files = os.listdir(filename)
+ if not files:
+ return await eod(
+ mone, "`Requested directory is empty. Can't create empty directory.`"
+ )
+ folder_id = GDrive.create_directory(filename)
+ c = 0
+ for files in sorted(files):
+ file = f"{filename}/{files}"
+ if not os.path.isdir(file):
+ try:
+ await GDrive._upload_file(mone, path=file, folder_id=folder_id)
+ c += 1
+ except Exception as e:
+ return await mone.edit(
+ f"Exception occurred while uploading to gDrive {e}"
+ )
+ return await mone.edit(
+ f"`Uploaded `[{filename}](https://drive.google.com/folderview?id={folder_id})` with {c} files.`"
+ )
+ try:
+ g_drive_link = await GDrive._upload_file(
+ mone,
+ filename,
+ )
+ await mone.edit(
+ get_string("gdrive_7").format(filename.split("/")[-1], g_drive_link)
+ )
+ except Exception as e:
+ await mone.edit(f"Exception occurred while uploading to gDrive {e}")
+
+
+@ultroid_cmd(
+ pattern="gdsearch( (.*)|$)",
+ fullsudo=True,
+)
+async def _(event):
+ GDrive = GDriveManager()
+ if not os.path.exists(GDrive.token_file):
+ return await event.eor(get_string("gdrive_6").format(asst.me.username))
+ input_str = event.pattern_match.group(1).strip()
+ if not input_str:
+ return await event.eor("`Give filename to search on GDrive...`")
+ eve = await event.eor(f"`Searching for {input_str} in G-Drive...`")
+ files = GDrive.search(input_str)
+ msg = ""
+ if files:
+ msg += (
+ f"{len(files.keys())} files with {input_str} in title found in GDrive.\n\n"
+ )
+ for _ in files:
+ msg += f"> [{files[_]}]({_})\n"
+ else:
+ msg += f"`No files with title {input_str}`"
+ if len(msg) < 4096:
+ await eve.eor(msg, link_preview=False)
+ else:
+ with open("drive-files.txt", "w") as f:
+ f.write(
+ msg.replace("[", "File Name: ")
+ .replace("](", "\n» Link: ")
+ .replace(")\n", "\n\n")
+ )
+ try:
+ await eve.delete()
+ except BaseException:
+ pass
+ await event.client.send_file(
+ event.chat_id,
+ f"{input_str}.txt",
+ thumb=ULTConfig.thumb,
+ reply_to=event,
+ )
+ os.remove(f"{input_str}.txt")
+
+
+@ultroid_cmd(
+ pattern="gdfolder$",
+ fullsudo=True,
+)
+async def _(event):
+ GDrive = GDriveManager()
+ if not os.path.exists(GDrive.token_file):
+ return await event.eor(get_string("gdrive_6").format(asst.me.username))
+ if GDrive.folder_id:
+ await event.eor(
+ "`Your G-Drive Folder link : `\n"
+ + GDrive._create_folder_link(GDrive.folder_id)
+ )
+ else:
+ await eod(event, "Set FOLDERID from your Assistant bot's Settings ")
diff --git a/plugins/giftools.py b/plugins/giftools.py
new file mode 100644
index 0000000000000000000000000000000000000000..5c8d0d228392ed289d3fcb9ab30e28de94077985
--- /dev/null
+++ b/plugins/giftools.py
@@ -0,0 +1,128 @@
+# Ultroid - UserBot
+# Copyright (C) 2021-2025 TeamUltroid
+#
+# This file is a part of < https://github.com/TeamUltroid/Ultroid/ >
+# PLease read the GNU Affero General Public License in
+# .
+"""
+✘ Commands Available
+
+•`{i}invertgif`
+ Make Gif Inverted(negative).
+
+•`{i}bwgif`
+ Make Gif black and white
+
+•`{i}rvgif`
+ Reverse a gif
+
+•`{i}vtog`
+ Reply To Video , It will Create Gif
+ Video to Gif
+
+•`{i}gif `
+ Send video regarding to query.
+"""
+import os
+import random
+import time
+from datetime import datetime as dt
+
+from . import HNDLR, LOGS, bash, downloader, get_string, mediainfo, ultroid_cmd
+
+
+@ultroid_cmd(pattern="(bw|invert)gif$")
+async def igif(e):
+ match = e.pattern_match.group(1).strip()
+ a = await e.get_reply_message()
+ if not (a and a.media):
+ return await e.eor("`Reply To gif only`", time=5)
+ wut = mediainfo(a.media)
+ if "gif" not in wut:
+ return await e.eor("`Reply To Gif Only`", time=5)
+ xx = await e.eor(get_string("com_1"))
+ z = await a.download_media()
+ if match == "bw":
+ cmd = f'ffmpeg -i "{z}" -vf format=gray ult.gif -y'
+ else:
+ cmd = f'ffmpeg -i "{z}" -vf lutyuv="y=negval:u=negval:v=negval" ult.gif -y'
+ try:
+ await bash(cmd)
+ await e.client.send_file(e.chat_id, "ult.gif", supports_streaming=True)
+ os.remove(z)
+ os.remove("ult.gif")
+ await xx.delete()
+ except Exception as er:
+ LOGS.info(er)
+
+
+@ultroid_cmd(pattern="rvgif$")
+async def reverse_gif(event):
+ a = await event.get_reply_message()
+ if not (a and a.media) and "video" not in mediainfo(a.media):
+ return await event.eor("`Reply To Video only`", time=5)
+ msg = await event.eor(get_string("com_1"))
+ file = await a.download_media()
+ await bash(f'ffmpeg -i "{file}" -vf reverse -af areverse reversed.mp4 -y')
+ await event.respond("- **Reversed Video/GIF**", file="reversed.mp4")
+ await msg.delete()
+ os.remove(file)
+ os.remove("reversed.mp4")
+
+
+@ultroid_cmd(pattern="gif( (.*)|$)")
+async def gifs(ult):
+ get = ult.pattern_match.group(1).strip()
+ xx = random.randint(0, 5)
+ n = 0
+ if ";" in get:
+ try:
+ n = int(get.split(";")[-1])
+ except IndexError:
+ pass
+ if not get:
+ return await ult.eor(f"`{HNDLR}gif `")
+ m = await ult.eor(get_string("com_2"))
+ gifs = await ult.client.inline_query("gif", get)
+ if not n:
+ await gifs[xx].click(
+ ult.chat_id, reply_to=ult.reply_to_msg_id, silent=True, hide_via=True
+ )
+ else:
+ for x in range(n):
+ await gifs[x].click(
+ ult.chat_id, reply_to=ult.reply_to_msg_id, silent=True, hide_via=True
+ )
+ await m.delete()
+
+
+@ultroid_cmd(pattern="vtog$")
+async def vtogif(e):
+ a = await e.get_reply_message()
+ if not (a and a.media):
+ return await e.eor("`Reply To video only`", time=5)
+ wut = mediainfo(a.media)
+ if "video" not in wut:
+ return await e.eor("`Reply To Video Only`", time=5)
+ xx = await e.eor(get_string("com_1"))
+ dur = a.media.document.attributes[0].duration
+ tt = time.time()
+ if int(dur) < 120:
+ z = await a.download_media()
+ await bash(
+ f'ffmpeg -i {z} -vf "fps=10,scale=320:-1:flags=lanczos,split[s0][s1];[s0]palettegen[p];[s1][p]paletteuse" -loop 0 ult.gif -y'
+ )
+ else:
+ filename = a.file.name
+ if not filename:
+ filename = "video_" + dt.now().isoformat("_", "seconds") + ".mp4"
+ vid = await downloader(filename, a.media.document, xx, tt, get_string("com_5"))
+ z = vid.name
+ await bash(
+ f'ffmpeg -ss 3 -t 100 -i {z} -vf "fps=10,scale=320:-1:flags=lanczos,split[s0][s1];[s0]palettegen[p];[s1][p]paletteuse" -loop 0 ult.gif'
+ )
+
+ await e.client.send_file(e.chat_id, "ult.gif", support_stream=True)
+ os.remove(z)
+ os.remove("ult.gif")
+ await xx.delete()
diff --git a/plugins/glitch.py b/plugins/glitch.py
new file mode 100644
index 0000000000000000000000000000000000000000..e4ee4df41b9b8813a7be7de8a5f5167a7bfcdbfb
--- /dev/null
+++ b/plugins/glitch.py
@@ -0,0 +1,42 @@
+# Ultroid - UserBot
+# Copyright (C) 2021-2025 TeamUltroid
+#
+# This file is a part of < https://github.com/TeamUltroid/Ultroid/ >
+# PLease read the GNU Affero General Public License in
+# .
+"""
+✘ Commands Available -
+
+•`{i}glitch `
+ gives a glitchy gif.
+"""
+import os
+
+from . import bash, get_string, mediainfo, ultroid_cmd
+
+
+@ultroid_cmd(pattern="glitch$")
+async def _(e):
+ try:
+ import glitch_me # ignore :pylint
+ except ModuleNotFoundError:
+ await bash(
+ "pip install -e git+https://github.com/1Danish-00/glitch_me.git#egg=glitch_me"
+ )
+ reply = await e.get_reply_message()
+ if not reply or not reply.media:
+ return await e.eor(get_string("cvt_3"))
+ xx = await e.eor(get_string("glitch_1"))
+ wut = mediainfo(reply.media)
+ if wut.startswith(("pic", "sticker")):
+ ok = await reply.download_media()
+ elif reply.document and reply.document.thumbs:
+ ok = await reply.download_media(thumb=-1)
+ else:
+ return await xx.eor(get_string("com_4"))
+ cmd = f"glitch_me gif --line_count 200 -f 10 -d 50 '{ok}' ult.gif"
+ await bash(cmd)
+ await e.reply(file="ult.gif", force_document=False)
+ await xx.delete()
+ os.remove(ok)
+ os.remove("ult.gif")
diff --git a/plugins/globaltools.py b/plugins/globaltools.py
new file mode 100644
index 0000000000000000000000000000000000000000..a81744abd98d113de877984ac7522b4d22dede45
--- /dev/null
+++ b/plugins/globaltools.py
@@ -0,0 +1,753 @@
+# Ultroid - UserBot
+# Copyright (C) 2021-2025 TeamUltroid
+#
+# This file is a part of < https://github.com/TeamUltroid/Ultroid/ >
+# PLease read the GNU Affero General Public License in
+# .
+"""
+✘ Commands Available -
+
+• `{i}gban `
+• `{i}ungban`
+ Ban/Unban Globally.
+
+• `{i}gstat `
+ Check if user is GBanned.
+
+• `{i}listgban` : List all GBanned users.
+
+• `{i}gmute` | `{i}ungmute`
+ Mute/UnMute Globally.
+
+• `{i}gkick ` `Globally Kick User`
+• `{i}gcast ` `Globally Send msg in all grps`
+
+• `{i}gadmincast ` `Globally broadcast in your admin chats`
+• `{i}gucast ` `Globally send msg in all pm users`
+
+• `{i}gblacklist `
+ globally promote user where you are admin
+ - Set whether To promote only in groups/channels/all.
+ Eg- `gpromote group boss` ~ promotes user in all grps.
+ `gpromote @username all sar` ~ promote the user in all group & channel
+• `{i}gdemote` - `demote user globally`
+"""
+import asyncio
+import os
+
+from telethon.errors.rpcerrorlist import ChatAdminRequiredError, FloodWaitError
+from telethon.tl.functions.channels import EditAdminRequest
+from telethon.tl.functions.contacts import BlockRequest, UnblockRequest
+from telethon.tl.types import ChatAdminRights, User
+
+from pyUltroid.dB import DEVLIST
+from pyUltroid.dB.base import KeyManager
+from pyUltroid.dB.gban_mute_db import (
+ gban,
+ gmute,
+ is_gbanned,
+ is_gmuted,
+ list_gbanned,
+ ungban,
+ ungmute,
+)
+from pyUltroid.fns.tools import create_tl_btn, format_btn, get_msg_button
+
+from . import (
+ HNDLR,
+ LOGS,
+ NOSPAM_CHAT,
+ OWNER_NAME,
+ eod,
+ eor,
+ get_string,
+ inline_mention,
+ ultroid_bot,
+ ultroid_cmd,
+)
+from ._inline import something
+
+_gpromote_rights = ChatAdminRights(
+ add_admins=False,
+ invite_users=True,
+ change_info=False,
+ ban_users=True,
+ delete_messages=True,
+ pin_messages=True,
+)
+
+_gdemote_rights = ChatAdminRights(
+ add_admins=False,
+ invite_users=False,
+ change_info=False,
+ ban_users=False,
+ delete_messages=False,
+ pin_messages=False,
+)
+
+keym = KeyManager("GBLACKLISTS", cast=list)
+
+
+@ultroid_cmd(pattern="gpromote( (.*)|$)", fullsudo=True)
+async def _(e):
+ x = e.pattern_match.group(1).strip()
+ ultroid_bot = e.client
+ if not x:
+ return await e.eor(get_string("schdl_2"), time=5)
+ user = await e.get_reply_message()
+ if user:
+ ev = await e.eor("`Promoting Replied User Globally`")
+ ok = e.text.split()
+ key = "all"
+ if len(ok) > 1 and (("group" in ok[1]) or ("channel" in ok[1])):
+ key = ok[1]
+ rank = ok[2] if len(ok) > 2 else "AdMin"
+ c = 0
+ user.id = user.peer_id.user_id if e.is_private else user.from_id.user_id
+ async for x in e.client.iter_dialogs():
+ if (
+ "group" in key.lower()
+ and x.is_group
+ or "group" not in key.lower()
+ and "channel" in key.lower()
+ and x.is_channel
+ ):
+ try:
+ await e.client(
+ EditAdminRequest(
+ x.id,
+ user.id,
+ _gpromote_rights,
+ rank,
+ ),
+ )
+ c += 1
+ except BaseException:
+ pass
+ elif (
+ ("group" not in key.lower() or x.is_group)
+ and (
+ "group" in key.lower()
+ or "channel" not in key.lower()
+ or x.is_channel
+ )
+ and (
+ "group" in key.lower()
+ or "channel" in key.lower()
+ or x.is_group
+ or x.is_channel
+ )
+ ):
+ try:
+ await e.client(
+ EditAdminRequest(
+ x.id,
+ user.id,
+ _gpromote_rights,
+ rank,
+ ),
+ )
+ c += 1
+ except Exception as er:
+ LOGS.info(er)
+ await eor(ev, f"Promoted The Replied Users in Total : {c} {key} chats")
+ else:
+ k = e.text.split()
+ if not k[1]:
+ return await eor(
+ e, "`Give someone's username/id or replied to user.", time=5
+ )
+ user = k[1]
+ if user.isdigit():
+ user = int(user)
+ try:
+ name = await e.client.get_entity(user)
+ except BaseException:
+ return await e.eor(f"`No User Found Regarding {user}`", time=5)
+ ev = await e.eor(f"`Promoting {name.first_name} globally.`")
+ key = "all"
+ if len(k) > 2 and (("group" in k[2]) or ("channel" in k[2])):
+ key = k[2]
+ rank = k[3] if len(k) > 3 else "AdMin"
+ c = 0
+ async for x in e.client.iter_dialogs():
+ if (
+ "group" in key.lower()
+ and x.is_group
+ or "group" not in key.lower()
+ and "channel" in key.lower()
+ and x.is_channel
+ or "group" not in key.lower()
+ and "channel" not in key.lower()
+ and (x.is_group or x.is_channel)
+ ):
+ try:
+ await ultroid_bot(
+ EditAdminRequest(
+ x.id,
+ user,
+ _gpromote_rights,
+ rank,
+ ),
+ )
+ c += 1
+ except BaseException:
+ pass
+ await eor(ev, f"Promoted {name.first_name} in Total : {c} {key} chats.")
+
+
+@ultroid_cmd(pattern="gdemote( (.*)|$)", fullsudo=True)
+async def _(e):
+ x = e.pattern_match.group(1).strip()
+ ultroid_bot = e.client
+ if not x:
+ return await e.eor(get_string("schdl_2"), time=5)
+ user = await e.get_reply_message()
+ if user:
+ user.id = user.peer_id.user_id if e.is_private else user.from_id.user_id
+ ev = await e.eor("`Demoting Replied User Globally`")
+ ok = e.text.split()
+ key = "all"
+ if len(ok) > 1 and (("group" in ok[1]) or ("channel" in ok[1])):
+ key = ok[1]
+ rank = "Not AdMin"
+ c = 0
+ async for x in e.client.iter_dialogs():
+ if (
+ "group" in key.lower()
+ and x.is_group
+ or "group" not in key.lower()
+ and "channel" in key.lower()
+ and x.is_channel
+ or "group" not in key.lower()
+ and "channel" not in key.lower()
+ and (x.is_group or x.is_channel)
+ ):
+ try:
+ await ultroid_bot(
+ EditAdminRequest(
+ x.id,
+ user.id,
+ _gdemote_rights,
+ rank,
+ ),
+ )
+ c += 1
+ except BaseException:
+ pass
+ await eor(ev, f"Demoted The Replied Users in Total : {c} {key} chats")
+ else:
+ k = e.text.split()
+ if not k[1]:
+ return await eor(
+ e, "`Give someone's username/id or replied to user.", time=5
+ )
+ user = k[1]
+ if user.isdigit():
+ user = int(user)
+ try:
+ name = await ultroid_bot.get_entity(user)
+ except BaseException:
+ return await e.eor(f"`No User Found Regarding {user}`", time=5)
+ ev = await e.eor(f"`Demoting {name.first_name} globally.`")
+ key = "all"
+ if len(k) > 2 and (("group" in k[2]) or ("channel" in k[2])):
+ key = k[2]
+ rank = "Not AdMin"
+ c = 0
+ async for x in ultroid_bot.iter_dialogs():
+ if (
+ "group" in key.lower()
+ and x.is_group
+ or "group" not in key.lower()
+ and "channel" in key.lower()
+ and x.is_channel
+ or "group" not in key.lower()
+ and "channel" not in key.lower()
+ and (x.is_group or x.is_channel)
+ ):
+ try:
+ await ultroid_bot(
+ EditAdminRequest(
+ x.id,
+ user,
+ _gdemote_rights,
+ rank,
+ ),
+ )
+ c += 1
+ except BaseException:
+ pass
+ await eor(ev, f"Demoted {name.first_name} in Total : {c} {key} chats.")
+
+
+@ultroid_cmd(pattern="ungban( (.*)|$)", fullsudo=True)
+async def _(e):
+ xx = await e.eor("`UnGbanning...`")
+ match = e.pattern_match.group(1).strip()
+ peer = None
+ if e.reply_to_msg_id:
+ userid = (await e.get_reply_message()).sender_id
+ elif match:
+ try:
+ userid = int(match)
+ except ValueError:
+ userid = match
+ try:
+ userid = (await e.client.get_entity(userid)).id
+ except Exception as er:
+ return await xx.edit(f"Failed to get User...\nError: {er}")
+ elif e.is_private:
+ userid = e.chat_id
+ else:
+ return await xx.eor("`Reply to some msg or add their id.`", time=5)
+ if not is_gbanned(userid):
+ return await xx.edit("`User/Channel is not Gbanned...`")
+ try:
+ if not peer:
+ peer = await e.client.get_entity(userid)
+ name = inline_mention(peer)
+ except BaseException:
+ userid = int(userid)
+ name = str(userid)
+ chats = 0
+ if e.client._dialogs:
+ dialog = e.client._dialogs
+ else:
+ dialog = await e.client.get_dialogs()
+ e.client._dialogs.extend(dialog)
+ for ggban in dialog:
+ if ggban.is_group or ggban.is_channel:
+ try:
+ await e.client.edit_permissions(ggban.id, userid, view_messages=True)
+ chats += 1
+ except FloodWaitError as fw:
+ LOGS.info(
+ f"[FLOOD_WAIT_ERROR] : on Ungban\nSleeping for {fw.seconds+10}"
+ )
+ await asyncio.sleep(fw.seconds + 10)
+ try:
+ await e.client.edit_permissions(
+ ggban.id, userid, view_messages=True
+ )
+ chats += 1
+ except BaseException as er:
+ LOGS.exception(er)
+ except (ChatAdminRequiredError, ValueError):
+ pass
+ except BaseException as er:
+ LOGS.exception(er)
+ ungban(userid)
+ if isinstance(peer, User):
+ await e.client(UnblockRequest(userid))
+ await xx.edit(
+ f"`Ungbaned` {name} in {chats} `chats.\nRemoved from gbanwatch.`",
+ )
+
+
+@ultroid_cmd(pattern="gban( (.*)|$)", fullsudo=True)
+async def _(e):
+ xx = await e.eor("`Gbanning...`")
+ reason = ""
+ if e.reply_to_msg_id:
+ userid = (await e.get_reply_message()).sender_id
+ try:
+ reason = e.text.split(" ", maxsplit=1)[1]
+ except IndexError:
+ pass
+ elif e.pattern_match.group(1).strip():
+ usr = e.text.split(maxsplit=2)[1]
+ try:
+ userid = await e.client.parse_id(usr)
+ except ValueError:
+ userid = usr
+ try:
+ reason = e.text.split(maxsplit=2)[2]
+ except IndexError:
+ pass
+ elif e.is_private:
+ userid = e.chat_id
+ try:
+ reason = e.text.split(" ", maxsplit=1)[1]
+ except IndexError:
+ pass
+ else:
+ return await xx.eor("`Reply to some msg or add their id.`", time=5)
+ user = None
+ try:
+ user = await e.client.get_entity(userid)
+ name = inline_mention(user)
+ except BaseException:
+ userid = int(userid)
+ name = str(userid)
+ chats = 0
+ if userid == ultroid_bot.uid:
+ return await xx.eor("`I can't gban myself.`", time=3)
+ elif userid in DEVLIST:
+ return await xx.eor("`I can't gban my Developers.`", time=3)
+ elif is_gbanned(userid):
+ return await eod(
+ xx,
+ "`User is already gbanned and added to gbanwatch.`",
+ time=4,
+ )
+ if e.client._dialogs:
+ dialog = e.client._dialogs
+ else:
+ dialog = await e.client.get_dialogs()
+ e.client._dialogs.extend(dialog)
+ for ggban in dialog:
+ if ggban.is_group or ggban.is_channel:
+ try:
+ await e.client.edit_permissions(ggban.id, userid, view_messages=False)
+ chats += 1
+ except FloodWaitError as fw:
+ LOGS.info(
+ f"[FLOOD_WAIT_ERROR] : on GBAN Command\nSleeping for {fw.seconds+10}"
+ )
+ await asyncio.sleep(fw.seconds + 10)
+ try:
+ await e.client.edit_permissions(
+ ggban.id, userid, view_messages=False
+ )
+ chats += 1
+ except BaseException as er:
+ LOGS.exception(er)
+ except (ChatAdminRequiredError, ValueError):
+ pass
+ except BaseException as er:
+ LOGS.exception(er)
+ gban(userid, reason)
+ if isinstance(user, User):
+ await e.client(BlockRequest(userid))
+ gb_msg = f"**#Gbanned** {name} `in {chats} chats and added to gbanwatch!`"
+ if reason:
+ gb_msg += f"\n**Reason** : {reason}"
+ await xx.edit(gb_msg)
+
+
+@ultroid_cmd(pattern="g(admin|)cast( (.*)|$)", fullsudo=True)
+async def gcast(event):
+ text, btn, reply = "", None, None
+ if xx := event.pattern_match.group(2):
+ msg, btn = get_msg_button(event.text.split(maxsplit=1)[1])
+ elif event.is_reply:
+ reply = await event.get_reply_message()
+ msg = reply.text
+ if reply.buttons:
+ btn = format_btn(reply.buttons)
+ else:
+ msg, btn = get_msg_button(msg)
+ else:
+ return await eor(
+ event, "`Give some text to Globally Broadcast or reply a message..`"
+ )
+
+ kk = await event.eor("`Globally Broadcasting Msg...`")
+ er = 0
+ done = 0
+ err = ""
+ if event.client._dialogs:
+ dialog = event.client._dialogs
+ else:
+ dialog = await event.client.get_dialogs()
+ event.client._dialogs.extend(dialog)
+ for x in dialog:
+ if x.is_group:
+ chat = x.entity.id
+ if (
+ not keym.contains(chat)
+ and int(f"-100{str(chat)}") not in NOSPAM_CHAT
+ and (
+ (
+ event.text[2:7] != "admin"
+ or (x.entity.admin_rights or x.entity.creator)
+ )
+ )
+ ):
+ try:
+ if btn:
+ bt = create_tl_btn(btn)
+ await something(
+ event,
+ msg,
+ reply.media if reply else None,
+ bt,
+ chat=chat,
+ reply=False,
+ )
+ else:
+ await event.client.send_message(
+ chat, msg, file=reply.media if reply else None
+ )
+ done += 1
+ except FloodWaitError as fw:
+ await asyncio.sleep(fw.seconds + 10)
+ try:
+ if btn:
+ bt = create_tl_btn(btn)
+ await something(
+ event,
+ msg,
+ reply.media if reply else None,
+ bt,
+ chat=chat,
+ reply=False,
+ )
+ else:
+ await event.client.send_message(
+ chat, msg, file=reply.media if reply else None
+ )
+ done += 1
+ except Exception as rr:
+ err += f"• {rr}\n"
+ er += 1
+ except BaseException as h:
+ err += f"• {str(h)}" + "\n"
+ er += 1
+ text += f"Done in {done} chats, error in {er} chat(s)"
+ if err != "":
+ open("gcast-error.log", "w+").write(err)
+ text += f"\nYou can do `{HNDLR}ul gcast-error.log` to know error report."
+ await kk.edit(text)
+
+
+@ultroid_cmd(pattern="gucast( (.*)|$)", fullsudo=True)
+async def gucast(event):
+ msg, btn, reply = "", None, None
+ if xx := event.pattern_match.group(1).strip():
+ msg, btn = get_msg_button(event.text.split(maxsplit=1)[1])
+ elif event.is_reply:
+ reply = await event.get_reply_message()
+ msg = reply.text
+ if reply.buttons:
+ btn = format_btn(reply.buttons)
+ else:
+ msg, btn = get_msg_button(msg)
+ else:
+ return await eor(
+ event, "`Give some text to Globally Broadcast or reply a message..`"
+ )
+ kk = await event.eor("`Globally Broadcasting Msg...`")
+ er = 0
+ done = 0
+ if event.client._dialogs:
+ dialog = event.client._dialogs
+ else:
+ dialog = await event.client.get_dialogs()
+ event.client._dialogs.extend(dialog)
+ for x in dialog:
+ if x.is_user and not x.entity.bot:
+ chat = x.id
+ if not keym.contains(chat):
+ try:
+ if btn:
+ bt = create_tl_btn(btn)
+ await something(
+ event,
+ msg,
+ reply.media if reply else None,
+ bt,
+ chat=chat,
+ reply=False,
+ )
+ else:
+ await event.client.send_message(
+ chat, msg, file=reply.media if reply else None
+ )
+ done += 1
+ except BaseException:
+ er += 1
+ await kk.edit(f"Done in {done} chats, error in {er} chat(s)")
+
+
+@ultroid_cmd(pattern="gkick( (.*)|$)", fullsudo=True)
+async def gkick(e):
+ xx = await e.eor("`Gkicking...`")
+ if e.reply_to_msg_id:
+ userid = (await e.get_reply_message()).sender_id
+ elif e.pattern_match.group(1).strip():
+ userid = await e.client.parse_id(e.pattern_match.group(1).strip())
+ elif e.is_private:
+ userid = e.chat_id
+ else:
+ return await xx.edit("`Reply to some msg or add their id.`", time=5)
+ name = (await e.client.get_entity(userid)).first_name
+ chats = 0
+ if userid == ultroid_bot.uid:
+ return await xx.eor("`I can't gkick myself.`", time=3)
+ if userid in DEVLIST:
+ return await xx.eor("`I can't gkick my Developers.`", time=3)
+ if e.client._dialogs:
+ dialog = e.client._dialogs
+ else:
+ dialog = await e.client.get_dialogs()
+ e.client._dialogs.extend(dialog)
+ for gkick in dialog:
+ if gkick.is_group or gkick.is_channel:
+ try:
+ await e.client.kick_participant(gkick.id, userid)
+ chats += 1
+ except BaseException:
+ pass
+ await xx.edit(f"`Gkicked` [{name}](tg://user?id={userid}) `in {chats} chats.`")
+
+
+@ultroid_cmd(pattern="gmute( (.*)|$)", fullsudo=True)
+async def _(e):
+ xx = await e.eor("`Gmuting...`")
+ if e.reply_to_msg_id:
+ userid = (await e.get_reply_message()).sender_id
+ elif e.pattern_match.group(1).strip():
+ userid = await e.client.parse_id(e.pattern_match.group(1).strip())
+ elif e.is_private:
+ userid = e.chat_id
+ else:
+ return await xx.eor("`Reply to some msg or add their id.`", tome=5, time=5)
+ name = await e.client.get_entity(userid)
+ chats = 0
+ if userid == ultroid_bot.uid:
+ return await xx.eor("`I can't gmute myself.`", time=3)
+ if userid in DEVLIST:
+ return await xx.eor("`I can't gmute my Developers.`", time=3)
+ if is_gmuted(userid):
+ return await xx.eor("`User is already gmuted.`", time=4)
+ if e.client._dialogs:
+ dialog = e.client._dialogs
+ else:
+ dialog = await e.client.get_dialogs()
+ e.client._dialogs.extend(dialog)
+ for onmute in dialog:
+ if onmute.is_group:
+ try:
+ await e.client.edit_permissions(onmute.id, userid, send_messages=False)
+ chats += 1
+ except BaseException:
+ pass
+ gmute(userid)
+ await xx.edit(f"`Gmuted` {inline_mention(name)} `in {chats} chats.`")
+
+
+@ultroid_cmd(pattern="ungmute( (.*)|$)", fullsudo=True)
+async def _(e):
+ xx = await e.eor("`UnGmuting...`")
+ if e.reply_to_msg_id:
+ userid = (await e.get_reply_message()).sender_id
+ elif e.pattern_match.group(1).strip():
+ userid = await e.client.parse_id(e.pattern_match.group(1).strip())
+ elif e.is_private:
+ userid = e.chat_id
+ else:
+ return await xx.eor("`Reply to some msg or add their id.`", time=5)
+ name = (await e.client.get_entity(userid)).first_name
+ chats = 0
+ if not is_gmuted(userid):
+ return await xx.eor("`User is not gmuted.`", time=3)
+ if e.client._dialogs:
+ dialog = e.client._dialogs
+ else:
+ dialog = await e.client.get_dialogs()
+ e.client._dialogs.extend(dialog)
+ for hurr in dialog:
+ if hurr.is_group:
+ try:
+ await e.client.edit_permissions(hurr.id, userid, send_messages=True)
+ chats += 1
+ except BaseException:
+ pass
+ ungmute(userid)
+ await xx.edit(f"`Ungmuted` {inline_mention(name)} `in {chats} chats.`")
+
+
+@ultroid_cmd(
+ pattern="listgban$",
+)
+async def list_gengbanned(event):
+ users = list_gbanned()
+ x = await event.eor(get_string("com_1"))
+ msg = ""
+ if not users:
+ return await x.edit("`You haven't GBanned anyone!`")
+ for i in users:
+ try:
+ name = await event.client.get_entity(int(i))
+ except BaseException:
+ name = i
+ msg += f"User: {inline_mention(name, html=True)}\n"
+ reason = users[i]
+ msg += f"Reason: {reason}\n\n" if reason is not None else "\n"
+ gbanned_users = f"List of users GBanned by {OWNER_NAME}:\n\n{msg}"
+ if len(gbanned_users) > 4096:
+ with open("gbanned.txt", "w") as f:
+ f.write(
+ gbanned_users.replace("", "")
+ .replace("", "")
+ .replace("", "")
+ )
+ await x.reply(
+ file="gbanned.txt",
+ message=f"List of users GBanned by {inline_mention(ultroid_bot.me)}",
+ )
+ os.remove("gbanned.txt")
+ await x.delete()
+ else:
+ await x.edit(gbanned_users, parse_mode="html")
+
+
+@ultroid_cmd(
+ pattern="gstat( (.*)|$)",
+)
+async def gstat_(e):
+ xx = await e.eor(get_string("com_1"))
+ if e.is_private:
+ userid = (await e.get_chat()).id
+ elif e.reply_to_msg_id:
+ userid = (await e.get_reply_message()).sender_id
+ elif e.pattern_match.group(1).strip():
+ try:
+ userid = await e.client.parse_id(e.pattern_match.group(1).strip())
+ except Exception as err:
+ return await xx.eor(f"{err}", time=10)
+ else:
+ return await xx.eor("`Reply to some msg or add their id.`", time=5)
+ name = (await e.client.get_entity(userid)).first_name
+ msg = f"**{name} is "
+ is_banned = is_gbanned(userid)
+ reason = list_gbanned().get(userid)
+ if is_banned:
+ msg += "Globally Banned"
+ msg += f" with reason** `{reason}`" if reason else ".**"
+ else:
+ msg += "not Globally Banned.**"
+ await xx.edit(msg)
+
+
+@ultroid_cmd(pattern="gblacklist$")
+async def blacklist_(event):
+ await gblacker(event, "add")
+
+
+@ultroid_cmd(pattern="ungblacklist$")
+async def ungblacker(event):
+ await gblacker(event, "remove")
+
+
+async def gblacker(event, type_):
+ try:
+ chat_id = int(event.text.split(maxsplit=1)[1])
+ try:
+ chat_id = (await event.client.get_entity(chat_id)).id
+ except Exception as e:
+ return await event.eor(f"**ERROR**\n`{str(e)}`")
+ except IndexError:
+ chat_id = event.chat_id
+ if type_ == "add":
+ keym.add(chat_id)
+ elif type_ == "remove":
+ keym.remove(chat_id)
+ await event.eor(f"Global Broadcasts: \n{type_}ed {chat_id}")
diff --git a/plugins/greetings.py b/plugins/greetings.py
new file mode 100644
index 0000000000000000000000000000000000000000..27053eff30ebca6569c7b1ea962e686248c0ea46
--- /dev/null
+++ b/plugins/greetings.py
@@ -0,0 +1,201 @@
+# Ultroid - UserBot
+# Copyright (C) 2021-2025 TeamUltroid
+#
+# This file is a part of < https://github.com/TeamUltroid/Ultroid/ >
+# PLease read the GNU Affero General Public License in
+# .
+"""
+✘ Commands Available -
+
+---- Welcomes ----
+• `{i}setwelcome `
+ Set welcome message in the current chat.
+
+• `{i}clearwelcome`
+ Delete the welcome in the current chat.
+
+• `{i}getwelcome`
+ Get the welcome message in the current chat.
+
+---- GoodByes ----
+• `{i}setgoodbye `
+ Set goodbye message in the current chat.
+
+• `{i}cleargoodbye`
+ Delete the goodbye in the current chat.
+
+• `{i}getgoodbye`
+ Get the goodbye message in the current chat.
+
+• `{i}thankmembers on/off`
+ Send a thank you sticker on hitting a members count of 100*x in your groups.
+"""
+import os
+
+from . import upload_file as uf
+from telethon.utils import pack_bot_file_id
+
+from pyUltroid.dB.greetings_db import (
+ add_goodbye,
+ add_thanks,
+ add_welcome,
+ delete_goodbye,
+ delete_welcome,
+ get_goodbye,
+ get_welcome,
+ must_thank,
+ remove_thanks,
+)
+from pyUltroid.fns.tools import create_tl_btn, format_btn, get_msg_button
+
+from . import HNDLR, eor, get_string, mediainfo, ultroid_cmd
+from ._inline import something
+
+Note = "\n\nNote: `{mention}`, `{group}`, `{count}`, `{name}`, `{fullname}`, `{username}`, `{userid}` can be used as formatting parameters.\n\n"
+
+
+@ultroid_cmd(pattern="setwelcome", groups_only=True)
+async def setwel(event):
+ x = await event.eor(get_string("com_1"))
+ r = await event.get_reply_message()
+ btn = format_btn(r.buttons) if (r and r.buttons) else None
+ try:
+ text = event.text.split(maxsplit=1)[1]
+ except IndexError:
+ text = r.text if r else None
+ if r and r.media:
+ wut = mediainfo(r.media)
+ if wut.startswith(("pic", "gif")):
+ dl = await r.download_media()
+ m = uf(dl)
+ os.remove(dl)
+ elif wut == "video":
+ if r.media.document.size > 8 * 1000 * 1000:
+ return await eor(x, get_string("com_4"), time=5)
+ dl = await r.download_media()
+ m = uf(dl)
+ os.remove(dl)
+ elif wut == "web":
+ m = None
+ else:
+ m = pack_bot_file_id(r.media)
+ if r.text:
+ txt = r.text
+ if not btn:
+ txt, btn = get_msg_button(r.text)
+ add_welcome(event.chat_id, txt, m, btn)
+ else:
+ add_welcome(event.chat_id, None, m, btn)
+ await eor(x, get_string("grt_1"))
+ elif text:
+ if not btn:
+ txt, btn = get_msg_button(text)
+ add_welcome(event.chat_id, txt, None, btn)
+ await eor(x, get_string("grt_1"))
+ else:
+ await eor(x, get_string("grt_3"), time=5)
+
+
+@ultroid_cmd(pattern="clearwelcome$", groups_only=True)
+async def clearwel(event):
+ if not get_welcome(event.chat_id):
+ return await event.eor(get_string("grt_4"), time=5)
+ delete_welcome(event.chat_id)
+ await event.eor(get_string("grt_5"), time=5)
+
+
+@ultroid_cmd(pattern="getwelcome$", groups_only=True)
+async def listwel(event):
+ wel = get_welcome(event.chat_id)
+ if not wel:
+ return await event.eor(get_string("grt_4"), time=5)
+ msgg, med = wel["welcome"], wel["media"]
+ if wel.get("button"):
+ btn = create_tl_btn(wel["button"])
+ return await something(event, msgg, med, btn)
+ await event.reply(f"**Welcome Note in this chat**\n\n`{msgg}`", file=med)
+ await event.delete()
+
+
+@ultroid_cmd(pattern="setgoodbye", groups_only=True)
+async def setgb(event):
+ x = await event.eor(get_string("com_1"))
+ r = await event.get_reply_message()
+ btn = format_btn(r.buttons) if (r and r.buttons) else None
+ try:
+ text = event.text.split(maxsplit=1)[1]
+ except IndexError:
+ text = r.text if r else None
+ if r and r.media:
+ wut = mediainfo(r.media)
+ if wut.startswith(("pic", "gif")):
+ dl = await r.download_media()
+ m = uf(dl)
+ os.remove(dl)
+ elif wut == "video":
+ if r.media.document.size > 8 * 1000 * 1000:
+ return await eor(x, get_string("com_4"), time=5)
+ dl = await r.download_media()
+ m = uf(dl)
+ os.remove(dl)
+ elif wut == "web":
+ m = None
+ else:
+ m = pack_bot_file_id(r.media)
+ if r.text:
+ txt = r.text
+ if not btn:
+ txt, btn = get_msg_button(r.text)
+ add_goodbye(event.chat_id, txt, m, btn)
+ else:
+ add_goodbye(event.chat_id, None, m, btn)
+ await eor(x, "`Goodbye note saved`")
+ elif text:
+ if not btn:
+ txt, btn = get_msg_button(text)
+ add_goodbye(event.chat_id, txt, None, btn)
+ await eor(x, "`Goodbye note saved`")
+ else:
+ await eor(x, get_string("grt_7"), time=5)
+
+
+@ultroid_cmd(pattern="cleargoodbye$", groups_only=True)
+async def clearwgb(event):
+ if not get_goodbye(event.chat_id):
+ return await event.eor(get_string("grt_6"), time=5)
+ delete_goodbye(event.chat_id)
+ await event.eor("`Goodbye Note Deleted`", time=5)
+
+
+@ultroid_cmd(pattern="getgoodbye$", groups_only=True)
+async def listgd(event):
+ wel = get_goodbye(event.chat_id)
+ if not wel:
+ return await event.eor(get_string("grt_6"), time=5)
+ msgg = wel["goodbye"]
+ med = wel["media"]
+ if wel.get("button"):
+ btn = create_tl_btn(wel["button"])
+ return await something(event, msgg, med, btn)
+ await event.reply(f"**Goodbye Note in this chat**\n\n`{msgg}`", file=med)
+ await event.delete()
+
+
+@ultroid_cmd(pattern="thankmembers (on|off)", groups_only=True)
+async def thank_set(event):
+ type_ = event.pattern_match.group(1).strip()
+ if not type_ or type_ == "":
+ await eor(
+ event,
+ f"**Current Chat Settings:**\n**Thanking Members:** `{must_thank(event.chat_id)}`\n\nUse `{HNDLR}thankmembers on` or `{HNDLR}thankmembers off` to toggle current settings!",
+ )
+ return
+ chat = event.chat_id
+ if type_.lower() == "on":
+ add_thanks(chat)
+ elif type_.lower() == "off":
+ remove_thanks(chat)
+ await eor(
+ event,
+ f"**Done! Thank you members has been turned** `{type_.lower()}` **for this chat**!",
+ )
diff --git a/plugins/imagetools.py b/plugins/imagetools.py
new file mode 100644
index 0000000000000000000000000000000000000000..22fd011823e0a96db45b9632f1ad82ebb6bdc5b1
--- /dev/null
+++ b/plugins/imagetools.py
@@ -0,0 +1,292 @@
+# Ultroid - UserBot
+# Copyright (C) 2021-2025 TeamUltroid
+#
+# This file is a part of < https://github.com/TeamUltroid/Ultroid/ >
+# PLease read the GNU Affero General Public License in
+# .
+"""
+✘ Commands Available -
+
+• `{i}border `
+ To create border around that media..
+ Ex - `{i}border 12,22,23`
+ - `{i}border 12,22,23 ; width (in number)`
+
+• `{i}grey `
+ To make it black nd white.
+
+• `{i}color `
+ To make it Colorfull.
+
+• `{i}toon `
+ To make it toon.
+
+• `{i}danger `
+ To make it look Danger.
+
+• `{i}negative `
+ To make negative image.
+
+• `{i}blur `
+ To make it blurry.
+
+• `{i}quad `
+ create a Vortex.
+
+• `{i}mirror `
+ To create mirror pic.
+
+• `{i}flip `
+ To make it flip.
+
+• `{i}sketch `
+ To draw its sketch.
+
+• `{i}blue `
+ just cool.
+
+• `{i}csample `
+ example : `{i}csample red`
+ `{i}csample #ffffff`
+
+• `{i}pixelator `
+ Create a Pixelated Image..
+"""
+import os
+
+from . import LOGS, con
+
+try:
+ import cv2
+except ImportError:
+ LOGS.error(f"{__file__}: OpenCv not Installed.")
+
+import numpy as np
+
+try:
+ from PIL import Image
+except ImportError:
+ Image = None
+ LOGS.info(f"{__file__}: PIL not Installed.")
+
+from . import upload_file as upf
+from telethon.errors.rpcerrorlist import (
+ ChatSendMediaForbiddenError,
+ MessageDeleteForbiddenError,
+)
+
+from . import (
+ Redis,
+ async_searcher,
+ download_file,
+ get_string,
+ requests,
+ udB,
+ ultroid_cmd,
+)
+
+
+@ultroid_cmd(pattern="color$")
+async def _(event):
+ reply = await event.get_reply_message()
+ if not (reply and reply.media):
+ return await event.eor("`Reply To a Black and White Image`")
+ xx = await event.eor("`Coloring image 🎨🖌️...`")
+ image = await reply.download_media()
+ img = cv2.VideoCapture(image)
+ ret, frame = img.read()
+ cv2.imwrite("ult.jpg", frame)
+ if udB.get_key("DEEP_API"):
+ key = Redis("DEEP_API")
+ else:
+ key = "quickstart-QUdJIGlzIGNvbWluZy4uLi4K"
+ r = requests.post(
+ "https://api.deepai.org/api/colorizer",
+ files={"image": open("ult.jpg", "rb")},
+ headers={"api-key": key},
+ )
+ os.remove("ult.jpg")
+ os.remove(image)
+ if "status" in r.json():
+ return await event.edit(
+ r.json()["status"] + "\nGet api nd set `{i}setdb DEEP_API key`"
+ )
+ r_json = r.json()["output_url"]
+ await event.client.send_file(event.chat_id, r_json, reply_to=reply)
+ await xx.delete()
+
+
+@ultroid_cmd(pattern="(grey|blur|negative|danger|mirror|quad|sketch|flip|toon)$")
+async def ult_tools(event):
+ match = event.pattern_match.group(1)
+ ureply = await event.get_reply_message()
+ if not (ureply and (ureply.media)):
+ await event.eor(get_string("cvt_3"))
+ return
+ ultt = await ureply.download_media()
+ xx = await event.eor(get_string("com_1"))
+ if ultt.endswith(".tgs"):
+ xx = await xx.edit(get_string("sts_9"))
+ file = await con.convert(ultt, convert_to="png", outname="ult")
+ ult = cv2.imread(file)
+ if match == "grey":
+ ultroid = cv2.cvtColor(ult, cv2.COLOR_BGR2GRAY)
+ elif match == "blur":
+ ultroid = cv2.GaussianBlur(ult, (35, 35), 0)
+ elif match == "negative":
+ ultroid = cv2.bitwise_not(ult)
+ elif match == "danger":
+ dan = cv2.cvtColor(ult, cv2.COLOR_BGR2RGB)
+ ultroid = cv2.cvtColor(dan, cv2.COLOR_HSV2BGR)
+ elif match == "mirror":
+ ish = cv2.flip(ult, 1)
+ ultroid = cv2.hconcat([ult, ish])
+ elif match == "flip":
+ trn = cv2.flip(ult, 1)
+ ish = cv2.rotate(trn, cv2.ROTATE_180)
+ ultroid = cv2.vconcat([ult, ish])
+ elif match == "quad":
+ ult = cv2.imread(file)
+ roid = cv2.flip(ult, 1)
+ mici = cv2.hconcat([ult, roid])
+ fr = cv2.flip(mici, 1)
+ trn = cv2.rotate(fr, cv2.ROTATE_180)
+ ultroid = cv2.vconcat([mici, trn])
+ elif match == "sketch":
+ gray_image = cv2.cvtColor(ult, cv2.COLOR_BGR2GRAY)
+ inverted_gray_image = 255 - gray_image
+ blurred_img = cv2.GaussianBlur(inverted_gray_image, (21, 21), 0)
+ inverted_blurred_img = 255 - blurred_img
+ ultroid = cv2.divide(gray_image, inverted_blurred_img, scale=256.0)
+ elif match == "toon":
+ height, width, _ = ult.shape
+ samples = np.zeros([height * width, 3], dtype=np.float32)
+ count = 0
+ for x in range(height):
+ for y in range(width):
+ samples[count] = ult[x][y]
+ count += 1
+ _, labels, centers = cv2.kmeans(
+ samples,
+ 12,
+ None,
+ (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 10000, 0.0001),
+ 5,
+ cv2.KMEANS_PP_CENTERS,
+ )
+ centers = np.uint8(centers)
+ ish = centers[labels.flatten()]
+ ultroid = ish.reshape(ult.shape)
+ cv2.imwrite("ult.jpg", ultroid)
+ await ureply.reply(
+ file="ult.jpg",
+ force_document=False,
+ )
+ await xx.delete()
+ os.remove("ult.jpg")
+ os.remove(file)
+
+
+@ultroid_cmd(pattern="csample (.*)")
+async def sampl(ult):
+ if color := ult.pattern_match.group(1).strip():
+ img = Image.new("RGB", (200, 100), f"{color}")
+ img.save("csample.png")
+ try:
+ try:
+ await ult.delete()
+ await ult.respond(f"Colour Sample for `{color}` !", file="csample.png")
+ except MessageDeleteForbiddenError:
+ await ult.reply(f"Colour Sample for `{color}` !", file="csample.png")
+ except ChatSendMediaForbiddenError:
+ await ult.eor("Umm! Sending Media is disabled here!")
+
+ else:
+ await ult.eor("Wrong Color Name/Hex Code specified!")
+
+
+@ultroid_cmd(
+ pattern="blue$",
+)
+async def ultd(event):
+ ureply = await event.get_reply_message()
+ xx = await event.eor("`...`")
+ if not (ureply and (ureply.media)):
+ await xx.edit(get_string("cvt_3"))
+ return
+ ultt = await ureply.download_media()
+ if ultt.endswith(".tgs"):
+ await xx.edit(get_string("sts_9"))
+ file = await con.convert(ultt, convert_to="png", outname="ult")
+ lnk = upf(file)
+ r = await async_searcher(
+ f"https://nekobot.xyz/api/imagegen?type=blurpify&image={lnk}", re_json=True
+ )
+ ms = r.get("message")
+ if not r["success"]:
+ return await xx.edit(ms)
+ await download_file(ms, "ult.png")
+ img = Image.open("ult.png").convert("RGB")
+ img.save("ult.webp", "webp")
+ await event.client.send_file(
+ event.chat_id,
+ "ult.webp",
+ force_document=False,
+ reply_to=event.reply_to_msg_id,
+ )
+ await xx.delete()
+ os.remove("ult.png")
+ os.remove("ult.webp")
+ os.remove(ultt)
+
+
+@ultroid_cmd(pattern="border( (.*)|$)")
+async def ok(event):
+ hm = await event.get_reply_message()
+ if not (hm and (hm.photo or hm.sticker)):
+ return await event.eor("`Reply to Sticker or Photo..`")
+ col = event.pattern_match.group(1).strip()
+ wh = 20
+ if not col:
+ col = [255, 255, 255]
+ else:
+ try:
+ if ";" in col:
+ col_ = col.split(";", maxsplit=1)
+ wh = int(col_[1])
+ col = col_[0]
+ col = [int(col) for col in col.split(",")[:2]]
+ except ValueError:
+ return await event.eor("`Not a Valid Input...`")
+ okla = await hm.download_media()
+ img1 = cv2.imread(okla)
+ constant = cv2.copyMakeBorder(img1, wh, wh, wh, wh, cv2.BORDER_CONSTANT, value=col)
+ cv2.imwrite("output.png", constant)
+ await event.client.send_file(event.chat.id, "output.png")
+ os.remove("output.png")
+ os.remove(okla)
+ await event.delete()
+
+
+@ultroid_cmd(pattern="pixelator( (.*)|$)")
+async def pixelator(event):
+ reply_message = await event.get_reply_message()
+ if not (reply_message and (reply_message.photo or reply_message.sticker)):
+ return await event.eor("`Reply to a photo`")
+ hw = 50
+ try:
+ hw = int(event.pattern_match.group(1).strip())
+ except (ValueError, TypeError):
+ pass
+ msg = await event.eor(get_string("com_1"))
+ image = await reply_message.download_media()
+ input_ = cv2.imread(image)
+ height, width = input_.shape[:2]
+ w, h = (hw, hw)
+ temp = cv2.resize(input_, (w, h), interpolation=cv2.INTER_LINEAR)
+ output = cv2.resize(temp, (width, height), interpolation=cv2.INTER_NEAREST)
+ cv2.imwrite("output.jpg", output)
+ await msg.respond("• Pixelated by Ultroid", file="output.jpg")
+ await msg.delete()
+ os.remove("output.jpg")
+ os.remove(image)
diff --git a/plugins/locks.py b/plugins/locks.py
new file mode 100644
index 0000000000000000000000000000000000000000..edbdc6b48d0c276fbd315d4377d4d4fe3e258d9b
--- /dev/null
+++ b/plugins/locks.py
@@ -0,0 +1,39 @@
+# Ultroid - UserBot
+# Copyright (C) 2021-2025 TeamUltroid
+#
+# This file is a part of < https://github.com/TeamUltroid/Ultroid/ >
+# PLease read the GNU Affero General Public License in
+# .
+"""
+✘ Commands Available -
+
+• `{i}lock `
+ Lock the Used Setting in Used Group.
+
+• `{i}unlock `
+ UNLOCK the Used Setting in Used Group.
+"""
+from telethon.tl.functions.messages import EditChatDefaultBannedRightsRequest
+
+from pyUltroid.fns.admins import lock_unlock
+
+from . import ultroid_cmd
+
+
+@ultroid_cmd(
+ pattern="(un|)lock( (.*)|$)", admins_only=True, manager=True, require="change_info"
+)
+async def un_lock(e):
+ mat = e.pattern_match.group(2).strip()
+ if not mat:
+ return await e.eor("`Give some Proper Input..`", time=5)
+ lock = e.pattern_match.group(1) == ""
+ ml = lock_unlock(mat, lock)
+ if not ml:
+ return await e.eor("`Incorrect Input`", time=5)
+ msg = "Locked" if lock else "Unlocked"
+ try:
+ await e.client(EditChatDefaultBannedRightsRequest(e.chat_id, ml))
+ except Exception as er:
+ return await e.eor(str(er))
+ await e.eor(f"**{msg}** - `{mat}` ! ")
diff --git a/plugins/logo.py b/plugins/logo.py
new file mode 100644
index 0000000000000000000000000000000000000000..551104f6d548fd4321f36dd4aa2d37d58dcf44a1
--- /dev/null
+++ b/plugins/logo.py
@@ -0,0 +1,101 @@
+# Ultroid - UserBot
+# Copyright (C) 2021-2025 TeamUltroid
+#
+# This file is a part of < https://github.com/TeamUltroid/Ultroid/ >
+# PLease read the GNU Affero General Public License in
+# .
+"""
+✘ Commands Available -
+
+• `{i}logo `
+ Generate a logo of the given Text
+ Or Reply To image , to write ur text on it.
+ Or Reply To Font File, To write with that font.
+
+"""
+import glob
+import os
+import random
+
+from telethon.tl.types import InputMessagesFilterPhotos
+
+try:
+ from PIL import Image
+except ImportError:
+ Image = None
+from pyUltroid.fns.misc import unsplashsearch
+from pyUltroid.fns.tools import LogoHelper
+
+from . import OWNER_ID, OWNER_NAME, download_file, get_string, mediainfo, ultroid_cmd
+
+
+@ultroid_cmd(pattern="logo( (.*)|$)")
+async def logo_gen(event):
+ xx = await event.eor(get_string("com_1"))
+ name = event.pattern_match.group(1).strip()
+ if not name:
+ return await xx.eor("`Give a name too!`", time=5)
+ bg_, font_ = None, None
+ if event.reply_to_msg_id:
+ temp = await event.get_reply_message()
+ if temp.media:
+ if hasattr(temp.media, "document") and (
+ ("font" in temp.file.mime_type)
+ or (".ttf" in temp.file.name)
+ or (".otf" in temp.file.name)
+ ):
+ font_ = await temp.download_media("resources/fonts/")
+ elif "pic" in mediainfo(temp.media):
+ bg_ = await temp.download_media()
+ if not bg_:
+ SRCH = [
+ "background",
+ "neon",
+ "anime",
+ "art",
+ "bridges",
+ "streets",
+ "computer",
+ "cyberpunk",
+ "nature",
+ "abstract",
+ "exoplanet",
+ "magic",
+ "3d render",
+ ]
+ res = await unsplashsearch(random.choice(SRCH), limit=1)
+ bg_, _ = await download_file(res[0], "resources/downloads/logo.png")
+ newimg = "resources/downloads/unsplash-temp.jpg"
+ img_ = Image.open(bg_)
+ img_.save(newimg)
+ os.remove(bg_)
+ bg_ = newimg
+
+ if not font_:
+ fpath_ = glob.glob("resources/fonts/*")
+ font_ = random.choice(fpath_)
+ if len(name) <= 8:
+ strke = 10
+ elif len(name) >= 9:
+ strke = 5
+ else:
+ strke = 20
+ name = LogoHelper.make_logo(
+ bg_,
+ name,
+ font_,
+ fill="white",
+ stroke_width=strke,
+ stroke_fill="black",
+ )
+ await xx.edit("`Done!`")
+ await event.client.send_file(
+ event.chat_id,
+ file=name,
+ caption=f"Logo by [{OWNER_NAME}](tg://user?id={OWNER_ID})",
+ force_document=True,
+ )
+ os.remove(name)
+ await xx.delete()
+ if os.path.exists(bg_):
+ os.remove(bg_)
diff --git a/plugins/mediatools.py b/plugins/mediatools.py
new file mode 100644
index 0000000000000000000000000000000000000000..4e8667b8f51c9c50612ebe6fd47b41122393569d
--- /dev/null
+++ b/plugins/mediatools.py
@@ -0,0 +1,147 @@
+# Ultroid - UserBot
+# Copyright (C) 2021-2025 TeamUltroid
+#
+# This file is a part of < https://github.com/TeamUltroid/Ultroid/ >
+# PLease read the GNU Affero General Public License in
+# .
+"""
+✘ Commands Available -
+
+• `{i}mediainfo //`
+ To get info about it.
+
+• `{i}rotate `
+ Rotate any video/photo/media..
+ Note : for video it should be angle of 90's
+"""
+import os
+import time
+from datetime import datetime as dt
+
+from pyUltroid.fns.misc import rotate_image
+from pyUltroid.fns.tools import make_html_telegraph
+
+from . import (
+ LOGS,
+ Telegraph,
+ bash,
+ downloader,
+ get_string,
+ upload_file,
+ is_url_ok,
+ mediainfo,
+ ultroid_cmd,
+)
+
+try:
+ import cv2
+except ImportError:
+ LOGS.info("WARNING: 'cv2' not found!")
+ cv2 = None
+
+
+@ultroid_cmd(pattern="mediainfo( (.*)|$)")
+async def mi(e):
+ r = await e.get_reply_message()
+ match = e.pattern_match.group(1).strip()
+ taime = time.time()
+ extra = ""
+ if r and r.media:
+ xx = mediainfo(r.media)
+ murl = r.media.stringify()
+ url = await make_html_telegraph("Mediainfo", f"{murl}
")
+ extra = f"**[{xx}]({url})**\n\n"
+ e = await e.eor(f"{extra}`Loading More...`", link_preview=False)
+
+ if hasattr(r.media, "document"):
+ file = r.media.document
+ mime_type = file.mime_type
+ filename = r.file.name
+ if not filename:
+ if "audio" in mime_type:
+ filename = "audio_" + dt.now().isoformat("_", "seconds") + ".ogg"
+ elif "video" in mime_type:
+ filename = "video_" + dt.now().isoformat("_", "seconds") + ".mp4"
+ dl = await downloader(
+ f"resources/downloads/{filename}",
+ file,
+ e,
+ taime,
+ f"{extra}`Loading More...`",
+ )
+
+ naam = dl.name
+ else:
+ naam = await r.download_media()
+ elif match and (
+ os.path.isfile(match)
+ or (match.startswith("https://") and (await is_url_ok(match)))
+ ):
+ naam, xx = match, "file"
+ else:
+ return await e.eor(get_string("cvt_3"), time=5)
+ out, er = await bash(f"mediainfo '{naam}'")
+ if er:
+ LOGS.info(er)
+ out = extra or str(er)
+ return await e.edit(out, link_preview=False)
+ makehtml = ""
+ if naam.endswith((".jpg", ".png")):
+ if os.path.exists(naam):
+ med = upload_file(naam)
+ else:
+ med = match
+ makehtml += f"
"
+ for line in out.split("\n"):
+ line = line.strip()
+ if not line:
+ makehtml += "
"
+ elif ":" not in line:
+ makehtml += f"{line}
"
+ else:
+ makehtml += f"{line}
"
+ try:
+ urll = await make_html_telegraph("Mediainfo", makehtml)
+ except Exception as er:
+ LOGS.exception(er)
+ return
+ await e.eor(f"{extra}[{get_string('mdi_1')}]({urll})", link_preview=False)
+ if not match:
+ os.remove(naam)
+
+
+@ultroid_cmd(pattern="rotate( (.*)|$)")
+async def rotate_(ult):
+ match = ult.pattern_match.group(1).strip()
+ if not ult.is_reply:
+ return await ult.eor("`Reply to a media...`")
+ if match:
+ try:
+ match = int(match)
+ except ValueError:
+ match = None
+ if not match:
+ return await ult.eor("`Please provide a valid angle to rotate media..`")
+ reply = await ult.get_reply_message()
+ msg = await ult.eor(get_string("com_1"))
+ photo = reply.game.photo if reply.game else None
+ if reply.video:
+ media = await reply.download_media()
+ file = f"{media}.mp4"
+ await bash(
+ f'ffmpeg -i "{media}" -c copy -metadata:s:v:0 rotate={match} "{file}" -y'
+ )
+ elif photo or reply.photo or reply.sticker:
+ media = await ult.client.download_media(photo or reply)
+ img = cv2.imread(media)
+ new_ = rotate_image(img, match)
+ file = "ult.png"
+ cv2.imwrite(file, new_)
+ else:
+ return await msg.edit("`Unsupported Media..\nReply to Photo/Video`")
+ if os.path.exists(file):
+ await ult.client.send_file(
+ ult.chat_id, file=file, video_note=bool(reply.video_note), reply_to=reply.id
+ )
+ os.remove(media)
+ await msg.try_delete()
diff --git a/plugins/misc.py b/plugins/misc.py
new file mode 100644
index 0000000000000000000000000000000000000000..e82b6702e97262c19c7e95bfc2055c0dcc1e5d19
--- /dev/null
+++ b/plugins/misc.py
@@ -0,0 +1,140 @@
+# Ultroid - UserBot
+#
+# This file is a part of < https://github.com/TeamUltroid/Ultroid/ >
+# PLease read the GNU Affero General Public License in
+# .
+"""
+✘ Commands Available -
+
+• `{i}eod`
+ `Get Event of the Today`
+
+• `{i}pntrst `
+ Download and send pinterest pins
+
+• `{i}gadget `
+ Gadget Search from Telegram.
+
+• `{i}randomuser`
+ Generate details about a random user.
+
+• `{i}ascii `
+ Convert replied image into html.
+"""
+
+import os
+from datetime import datetime as dt
+
+from bs4 import BeautifulSoup as bs
+
+try:
+ from htmlwebshot import WebShot
+except ImportError:
+ WebShot = None
+try:
+ from img2html.converter import Img2HTMLConverter
+except ImportError:
+ Img2HTMLConverter = None
+
+from . import async_searcher, get_random_user_data, get_string, re, ultroid_cmd
+
+
+@ultroid_cmd(pattern="eod$")
+async def diela(e):
+ m = await e.eor(get_string("com_1"))
+ li = "https://daysoftheyear.com"
+ te = "🎊 **Events of the Day**\n\n"
+ da = dt.now()
+ month = da.strftime("%b")
+ li += f"/days/{month}/" + da.strftime("%F").split("-")[2]
+ ct = await async_searcher(li, re_content=True)
+ bt = bs(ct, "html.parser", from_encoding="utf-8")
+ ml = bt.find_all("a", "js-link-target", href=re.compile("daysoftheyear.com/days"))
+ for eve in ml[:5]:
+ te += f'• [{eve.text}]({eve["href"]})\n'
+ await m.edit(te, link_preview=False)
+
+
+@ultroid_cmd(
+ pattern="pntrst( (.*)|$)",
+)
+async def pinterest(e):
+ m = e.pattern_match.group(1).strip()
+ if not m:
+ return await e.eor("`Give pinterest link.`", time=3)
+ soup = await async_searcher(
+ "https://www.expertstool.com/download-pinterest-video/",
+ data={"url": m},
+ post=True,
+ )
+ try:
+ _soup = bs(soup, "html.parser").find("table").tbody.find_all("tr")
+ except BaseException:
+ return await e.eor("`Wrong link or private pin.`", time=5)
+ file = _soup[1] if len(_soup) > 1 else _soup[0]
+ file = file.td.a["href"]
+ await e.client.send_file(e.chat_id, file, caption=f"Pin:- {m}")
+
+
+@ultroid_cmd(pattern="gadget( (.*)|$)")
+async def mobs(e):
+ mat = e.pattern_match.group(1).strip()
+ if not mat:
+ await e.eor("Please Give a Mobile Name to look for.")
+ query = mat.replace(" ", "%20")
+ jwala = f"https://gadgets.ndtv.com/search?searchtext={query}"
+ c = await async_searcher(jwala)
+ b = bs(c, "html.parser", from_encoding="utf-8")
+ co = b.find_all("div", "rvw-imgbox")
+ if not co:
+ return await e.eor("No Results Found!")
+ bt = await e.eor(get_string("com_1"))
+ out = "**📱 Mobile / Gadgets Search**\n\n"
+ li = co[0].find("a")
+ imu, title = None, li.find("img")["title"]
+ cont = await async_searcher(li["href"])
+ nu = bs(cont, "html.parser", from_encoding="utf-8")
+ req = nu.find_all("div", "_pdsd")
+ imu = nu.find_all(
+ "img", src=re.compile("https://i.gadgets360cdn.com/products/large/")
+ )
+ if imu:
+ imu = imu[0]["src"].split("?")[0] + "?downsize=*:420&output-quality=80"
+ out += f"☑️ **[{title}]({li['href']})**\n\n"
+ for fp in req:
+ ty = fp.findNext()
+ out += f"- **{ty.text}** - `{ty.findNext().text}`\n"
+ out += "_"
+ if imu == []:
+ imu = None
+ await e.reply(out, file=imu, link_preview=False)
+ await bt.delete()
+
+
+@ultroid_cmd(pattern="randomuser")
+async def _gen_data(event):
+ x = await event.eor(get_string("com_1"))
+ msg, pic = await get_random_user_data()
+ await event.reply(file=pic, message=msg)
+ await x.delete()
+
+
+@ultroid_cmd(
+ pattern="ascii( (.*)|$)",
+)
+async def _(e):
+ if not Img2HTMLConverter:
+ return await e.eor("'img2html-converter' not installed!")
+ if not e.reply_to_msg_id:
+ return await e.eor(get_string("ascii_1"))
+ m = await e.eor(get_string("ascii_2"))
+ img = await (await e.get_reply_message()).download_media()
+ char = e.pattern_match.group(1).strip() or "■"
+ converter = Img2HTMLConverter(char=char)
+ html = converter.convert(img)
+ shot = WebShot(quality=85)
+ pic = await shot.create_pic_async(html=html)
+ await m.delete()
+ await e.reply(file=pic)
+ os.remove(pic)
+ os.remove(img)
diff --git a/plugins/mute.py b/plugins/mute.py
new file mode 100644
index 0000000000000000000000000000000000000000..cb7c3d6937fd4c2814720480b582a63b52c8c1c1
--- /dev/null
+++ b/plugins/mute.py
@@ -0,0 +1,210 @@
+# Ultroid - UserBot
+# Copyright (C) 2021-2025 TeamUltroid
+#
+# This file is a part of < https://github.com/TeamUltroid/Ultroid/ >
+# PLease read the GNU Affero General Public License in
+# .
+"""
+✘ Commands Available -
+
+• `{i}mute `
+ Mute user in current chat.
+
+• `{i}unmute `
+ Unmute user in current chat.
+
+• `{i}dmute `
+ Mute user in current chat by deleting msgs.
+
+• `{i}undmute `
+ Unmute dmuted user in current chat.
+
+• `{i}tmute