diff --git a/Hellbot/__init__.py b/Hellbot/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..2ea14e419335e4f3d1cd29ba3b98caf9f1c30b73
--- /dev/null
+++ b/Hellbot/__init__.py
@@ -0,0 +1,60 @@
+import os
+import time
+from platform import python_version
+
+import heroku3
+from pyrogram import __version__ as pyrogram_version
+
+from .core import LOGS, Config
+
+START_TIME = time.time()
+
+
+__version__ = {
+ "hellbot": "3.0",
+ "pyrogram": pyrogram_version,
+ "python": python_version(),
+}
+
+
+try:
+ if Config.HEROKU_APIKEY is not None and Config.HEROKU_APPNAME is not None:
+ HEROKU_APP = heroku3.from_key(Config.HEROKU_APIKEY).apps()[
+ Config.HEROKU_APPNAME
+ ]
+ else:
+ HEROKU_APP = None
+except Exception as e:
+ LOGS.error(f"Heroku Api - {e}")
+ HEROKU_APP = None
+
+
+if Config.API_HASH is None:
+ LOGS.error("Please set your API_HASH !")
+ quit(1)
+
+if Config.API_ID == 0:
+ LOGS.error("Please set your API_ID !")
+ quit(1)
+
+if Config.BOT_TOKEN is None:
+ LOGS.error("Please set your BOT_TOKEN !")
+ quit(1)
+
+if Config.DATABASE_URL is None:
+ LOGS.error("Please set your DATABASE_URL !")
+ quit(1)
+
+if Config.LOGGER_ID == 0:
+ LOGS.error("Please set your LOGGER_ID !")
+ quit(1)
+
+if Config.OWNER_ID == 0:
+ LOGS.error("Please set your OWNER_ID !")
+ quit(1)
+
+if not os.path.isdir(Config.DWL_DIR):
+ os.makedirs(Config.DWL_DIR)
+
+if not os.path.isdir(Config.TEMP_DIR):
+ os.makedirs(Config.TEMP_DIR)
diff --git a/Hellbot/__main__.py b/Hellbot/__main__.py
new file mode 100644
index 0000000000000000000000000000000000000000..6e2c3ca34e775fc6267041cfb7c8f6a24f8dd89c
--- /dev/null
+++ b/Hellbot/__main__.py
@@ -0,0 +1,33 @@
+from pyrogram import idle
+
+from Hellbot import __version__
+from Hellbot.core import (
+ Config,
+ ForcesubSetup,
+ GachaBotsSetup,
+ TemplateSetup,
+ UserSetup,
+ db,
+ hellbot,
+)
+from Hellbot.functions.tools import initialize_git
+from Hellbot.functions.utility import BList, Flood, TGraph
+
+
+async def main():
+ await hellbot.startup()
+ await db.connect()
+ await UserSetup()
+ await ForcesubSetup()
+ await GachaBotsSetup()
+ await TemplateSetup()
+ await Flood.updateFromDB()
+ await BList.updateBlacklists()
+ await TGraph.setup()
+ await initialize_git(Config.PLUGINS_REPO)
+ await hellbot.start_message(__version__)
+ await idle()
+
+
+if __name__ == "__main__":
+ hellbot.run(main())
diff --git a/Hellbot/core/__init__.py b/Hellbot/core/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..caa4b0445d0dd0df78506363b2da664cf16e120f
--- /dev/null
+++ b/Hellbot/core/__init__.py
@@ -0,0 +1,19 @@
+from .clients import hellbot
+from .config import ENV, Config, Limits, Symbols
+from .database import db
+from .initializer import ForcesubSetup, GachaBotsSetup, TemplateSetup, UserSetup
+from .logger import LOGS
+
+__all__ = [
+ "hellbot",
+ "ENV",
+ "Config",
+ "Limits",
+ "Symbols",
+ "db",
+ "ForcesubSetup",
+ "GachaBotsSetup",
+ "TemplateSetup",
+ "UserSetup",
+ "LOGS",
+]
diff --git a/Hellbot/core/clients.py b/Hellbot/core/clients.py
new file mode 100644
index 0000000000000000000000000000000000000000..f9871785ad507f4ff0f676816a28542ed574d707
--- /dev/null
+++ b/Hellbot/core/clients.py
@@ -0,0 +1,232 @@
+import asyncio
+import glob
+import importlib
+import os
+import sys
+from pathlib import Path
+
+import pyroaddon # pylint: disable=unused-import
+from pyrogram import Client
+from pyrogram.enums import ParseMode
+from pyrogram.types import InlineKeyboardButton, InlineKeyboardMarkup, Message
+
+from .config import ENV, Config, Symbols
+from .database import db
+from .logger import LOGS
+
+
+class HellClient(Client):
+ def __init__(self) -> None:
+ self.users: list[Client] = []
+ self.bot: Client = Client(
+ name="HellBot",
+ api_id=Config.API_ID,
+ api_hash=Config.API_HASH,
+ bot_token=Config.BOT_TOKEN,
+ plugins=dict(root="Hellbot.plugins.bot"),
+ )
+
+ async def start_user(self) -> None:
+ sessions = await db.get_all_sessions()
+ for i, session in enumerate(sessions):
+ try:
+ client = Client(
+ name=f"HellUser#{i + 1}",
+ api_id=Config.API_ID,
+ api_hash=Config.API_HASH,
+ session_string=session["session"],
+ )
+ await client.start()
+ me = await client.get_me()
+ self.users.append(client)
+ LOGS.info(
+ f"{Symbols.arrow_right * 2} Started User {i + 1}: '{me.first_name}' {Symbols.arrow_left * 2}"
+ )
+ is_in_logger = await self.validate_logger(client)
+ if not is_in_logger:
+ LOGS.warning(
+ f"Client #{i+1}: '{me.first_name}' is not in Logger Group! Check and add manually for proper functioning."
+ )
+ try:
+ await client.join_chat("https://t.me/+wQyUMn4891Q2OTVh") # Channel
+ except:
+ pass
+ # try:
+ # await client.join_chat("https://t.me/+P4Ekwk7P7Rk3NzA9") # Group
+ # except:
+ # pass
+ except Exception as e:
+ LOGS.error(f"{i + 1}: {e}")
+ continue
+
+ async def start_bot(self) -> None:
+ await self.bot.start()
+ me = await self.bot.get_me()
+ LOGS.info(
+ f"{Symbols.arrow_right * 2} Started HellBot Client: '{me.username}' {Symbols.arrow_left * 2}"
+ )
+
+ async def load_plugin(self) -> None:
+ count = 0
+ files = glob.glob("Hellbot/plugins/user/*.py")
+ unload = await db.get_env(ENV.unload_plugins) or ""
+ unload = unload.split(" ")
+ for file in files:
+ with open(file) as f:
+ path = Path(f.name)
+ shortname = path.stem.replace(".py", "")
+ if shortname in unload:
+ os.remove(Path(f"Hellbot/plugins/user/{shortname}.py"))
+ continue
+ if shortname.startswith("__"):
+ continue
+ fpath = Path(f"Hellbot/plugins/user/{shortname}.py")
+ name = "Hellbot.plugins.user." + shortname
+ spec = importlib.util.spec_from_file_location(name, fpath)
+ load = importlib.util.module_from_spec(spec)
+ spec.loader.exec_module(load)
+ sys.modules["Hellbot.plugins.user." + shortname] = load
+ count += 1
+ f.close()
+ LOGS.info(
+ f"{Symbols.bullet * 3} Loaded User Plugin: '{count}' {Symbols.bullet * 3}"
+ )
+
+ async def validate_logger(self, client: Client) -> bool:
+ try:
+ await client.get_chat_member(Config.LOGGER_ID, "me")
+ return True
+ except Exception:
+ return await self.join_logger(client)
+
+ async def join_logger(self, client: Client) -> bool:
+ try:
+ invite_link = await self.bot.export_chat_invite_link(Config.LOGGER_ID)
+ await client.join_chat(invite_link)
+ return True
+ except Exception:
+ return False
+
+ async def start_message(self, version: dict) -> None:
+ await self.bot.send_animation(
+ Config.LOGGER_ID,
+ "https://te.legra.ph/file/8deca5343c64d9db9401f.mp4",
+ f"**{Symbols.check_mark} ๐ง๐พ๐
๐
๐ก๐๐ ๐๐ ๐๐๐ ๐ฎ๐๐
๐๐๐พ!**\n\n"
+ f"**{Symbols.triangle_right} ๐ข๐
๐๐พ๐๐๐:** `{len(self.users)}`\n"
+ f"**{Symbols.triangle_right} ๐ฏ๐
๐๐๐๐๐:** `{len(Config.CMD_MENU)}`\n"
+ f"**{Symbols.triangle_right} ๐ข๐๐๐๐บ๐๐ฝ๐:** `{len(Config.CMD_INFO)}`\n"
+ f"**{Symbols.triangle_right} ๐ฒ๐๐บ๐ ๐ด๐๐พ๐๐:** `{len(Config.STAN_USERS)}`\n"
+ f"**{Symbols.triangle_right} ๐ ๐๐๐ ๐ด๐๐พ๐๐:** `{len(Config.AUTH_USERS)}`\n\n"
+ f"**{Symbols.triangle_right} ๐ง๐พ๐
๐
๐ก๐๐ ๐ต๐พ๐๐๐๐๐:** `{version['hellbot']}`\n"
+ f"**{Symbols.triangle_right} ๐ฏ๐๐๐๐๐๐บ๐ ๐ต๐พ๐๐๐๐๐:** `{version['pyrogram']}`\n"
+ f"**{Symbols.triangle_right} ๐ฏ๐๐๐๐๐ ๐ต๐พ๐๐๐๐๐:** `{version['python']}`\n\n"
+ f"**> @HellBot_Networks**",
+ parse_mode=ParseMode.MARKDOWN,
+ disable_notification=True,
+ reply_markup=InlineKeyboardMarkup(
+ [
+ [
+ InlineKeyboardButton("๐ซ Start Me", url=f"https://t.me/{self.bot.me.username}?start=start"),
+ InlineKeyboardButton("๐ Repo", url="https://github.com/The-HellBot/HellBot"),
+ ],
+ [
+ InlineKeyboardButton("๐ HellBot Networks ๐", url="https://t.me/hellbot_networks"),
+ ],
+ ]
+ ),
+ )
+
+ async def startup(self) -> None:
+ LOGS.info(
+ f"{Symbols.bullet * 3} Starting HellBot Client & User {Symbols.bullet * 3}"
+ )
+ await self.start_bot()
+ await self.start_user()
+ await self.load_plugin()
+
+
+class CustomMethods(HellClient):
+ async def input(self, message: Message) -> str:
+ """Get the input from the user"""
+ if len(message.command) < 2:
+ output = ""
+
+ else:
+ try:
+ output = message.text.split(" ", 1)[1].strip() or ""
+ except IndexError:
+ output = ""
+
+ return output
+
+ async def edit(
+ self,
+ message: Message,
+ text: str,
+ parse_mode: ParseMode = ParseMode.DEFAULT,
+ no_link_preview: bool = True,
+ ) -> Message:
+ """Edit or Reply to a message, if possible"""
+ if message.from_user and message.from_user.id in Config.STAN_USERS:
+ if message.reply_to_message:
+ return await message.reply_to_message.reply_text(
+ text,
+ parse_mode=parse_mode,
+ disable_web_page_preview=no_link_preview,
+ )
+ return await message.reply_text(
+ text, parse_mode=parse_mode, disable_web_page_preview=no_link_preview
+ )
+ return await message.edit_text(
+ text, parse_mode=parse_mode, disable_web_page_preview=no_link_preview
+ )
+
+ async def _delete(self, message: Message, delay: int = 0) -> None:
+ """Delete a message after a certain period of time"""
+ await asyncio.sleep(delay)
+ await message.delete()
+
+ async def delete(
+ self, message: Message, text: str, delete: int = 10, in_background: bool = True
+ ) -> None:
+ """Edit a message and delete it after a certain period of time"""
+ to_del = await self.edit(message, text)
+ if in_background:
+ asyncio.create_task(self._delete(to_del, delete))
+ else:
+ await self._delete(to_del, delete)
+
+ async def error(self, message: Message, text: str, delete: int = 10) -> None:
+ """Edit an error message and delete it after a certain period of time if mentioned"""
+ to_del = await self.edit(message, f"{Symbols.cross_mark} **Error:** \n\n{text}")
+ if delete:
+ asyncio.create_task(self._delete(to_del, delete))
+
+ async def _log(self, tag: str, text: str, file: str = None) -> None:
+ """Log a message to the Logger Group"""
+ msg = f"**#{tag.upper()}**\n\n{text}"
+ try:
+ if file:
+ try:
+ await self.bot.send_document(Config.LOGGER_ID, file, caption=msg)
+ except:
+ await self.bot.send_message(
+ Config.LOGGER_ID, msg, disable_web_page_preview=True
+ )
+ else:
+ await self.bot.send_message(
+ Config.LOGGER_ID, msg, disable_web_page_preview=True
+ )
+ except Exception as e:
+ raise Exception(f"{Symbols.cross_mark} LogErr: {e}")
+
+ async def check_and_log(self, tag: str, text: str, file: str = None) -> None:
+ """Check if :
+ \n-> the Logger Group is available
+ \n-> the logging is enabled"""
+ status = await db.get_env(ENV.is_logger)
+ if status and status.lower() == "true":
+ await self._log(tag, text, file)
+
+
+hellbot = CustomMethods()
diff --git a/Hellbot/core/config.py b/Hellbot/core/config.py
new file mode 100644
index 0000000000000000000000000000000000000000..ee069dfa299f46c18f739ed1ab95ebd6a1e715e7
--- /dev/null
+++ b/Hellbot/core/config.py
@@ -0,0 +1,155 @@
+from os import getenv
+
+from dotenv import load_dotenv
+from pyrogram import filters
+
+load_dotenv()
+
+
+class Config:
+ # editable configs
+ API_HASH = getenv("API_HASH", None)
+ API_ID = int(getenv("API_ID", 0))
+ BOT_TOKEN = getenv("BOT_TOKEN", None)
+ DATABASE_URL = getenv("DATABASE_URL", None)
+ HANDLERS = getenv("HANDLERS", ". ! ?").strip().split()
+ LOGGER_ID = int(getenv("LOGGER_ID", 0))
+ OWNER_ID = int(getenv("OWNER_ID", 0))
+
+ # heroku related configs
+ HEROKU_APPNAME = getenv("HEROKU_APPNAME", None)
+ HEROKU_APIKEY = getenv("HEROKU_APIKEY", None)
+
+ # github related configs
+ PLUGINS_REPO = getenv("PLUGINS_REPO", "The-HellBot/Plugins")
+ DEPLOY_REPO = getenv("DEPLOY_REPO", "The-HellBot/Hellbot")
+
+ # storage dir: you may or may not edit
+ DWL_DIR = "./downloads/"
+ TEMP_DIR = "./temp/"
+ CHROME_BIN = getenv("CHROME_BIN", "/app/.chrome-for-testing/chrome-linux64/chrome")
+ CHROME_DRIVER = getenv(
+ "CHROME_DRIVER", "/app/.chrome-for-testing/chromedriver-linux64/chromedriver"
+ )
+ FONT_PATH = "./Hellbot/resources/fonts/Montserrat.ttf"
+
+ # users config: do not edit
+ AUTH_USERS = filters.user()
+ BANNED_USERS = filters.user()
+ GACHA_BOTS = filters.user()
+ MUTED_USERS = filters.user()
+ DEVS = filters.user([1432756163, 1874070588, 1533682758])
+ STAN_USERS = filters.user()
+ FORCESUBS = filters.chat()
+
+ # Global config: do not edit
+ AFK_CACHE = {}
+ BOT_CMD_INFO = {}
+ BOT_CMD_MENU = {}
+ BOT_HELP = {}
+ CMD_INFO = {}
+ CMD_MENU = {}
+ HELP_DICT = {}
+ TEMPLATES = {}
+
+
+class ENV:
+ """Database ENV Names"""
+
+ airing_template = "AIRING_TEMPLATE"
+ airpollution_template = "AIRPOLLUTION_TEMPLATE"
+ alive_pic = "ALIVE_PIC"
+ alive_template = "ALIVE_TEMPLATE"
+ anilist_user_template = "ANILIST_USER_TEMPLATE"
+ anime_template = "ANIME_TEMPLATE"
+ btn_in_help = "BUTTONS_IN_HELP"
+ character_template = "CHARACTER_TEMPLATE"
+ chat_info_template = "CHAT_INFO_TEMPLATE"
+ climate_api = "CLIMATE_API"
+ climate_template = "CLIMATE_TEMPLATE"
+ command_template = "COMMAND_TEMPLATE"
+ currency_api = "CURRENCY_API"
+ custom_pmpermit = "CUSTOM_PMPERMIT"
+ gban_template = "GBAN_TEMPLATE"
+ github_user_template = "GITHUB_USER_TEMPLATE"
+ help_emoji = "HELP_EMOJI"
+ help_template = "HELP_TEMPLATE"
+ is_logger = "IS_LOGGER"
+ lyrics_api = "LYRICS_API"
+ manga_template = "MANGA_TEMPLATE"
+ ocr_api = "OCR_API"
+ ping_pic = "PING_PIC"
+ ping_template = "PING_TEMPLATE"
+ pm_logger = "PM_LOGGER"
+ pm_max_spam = "PM_MAX_SPAM"
+ pmpermit = "PMPERMIT"
+ pmpermit_pic = "PMPERMIT_PIC"
+ remove_bg_api = "REMOVE_BG_API"
+ thumbnail_url = "THUMBNAIL_URL"
+ statistics_template = "STATISTICS_TEMPLATE"
+ sticker_packname = "STICKER_PACKNAME"
+ tag_logger = "TAG_LOGGER"
+ telegraph_account = "TELEGRAPH_ACCOUNT"
+ time_zone = "TIME_ZONE"
+ unload_plugins = "UNLOAD_PLUGINS"
+ unsplash_api = "UNSPLASH_API"
+ usage_template = "USAGE_TEMPLATE"
+ user_info_template = "USER_INFO_TEMPLATE"
+
+
+class Limits:
+ AdminRoleLength = 16
+ AdminsLimit = 50
+ BioLength = 70
+ BotDescriptionLength = 512
+ BotInfoLength = 120
+ BotsLimit = 20
+ CaptionLength = 1024
+ ChannelGroupsLimit = 500
+ ChatTitleLength = 128
+ FileNameLength = 60
+ MessageLength = 4096
+ NameLength = 64
+ PremiumBioLength = 140
+ PremiumCaptionLength = 2048
+ PremiumChannelGroupsLimit = 1000
+ StickerAniamtedLimit = 50
+ StickerPackNameLength = 64
+ StickerStaticLimit = 120
+
+
+class Symbols:
+ anchor = "โ"
+ arrow_left = "ยซ"
+ arrow_right = "ยป"
+ back = "๐ back"
+ bullet = "โข"
+ check_mark = "โ"
+ close = "๐๏ธ"
+ cross_mark = "โ"
+ diamond_1 = "โ"
+ diamond_2 = "โ"
+ next = "โค next"
+ previous = "prev โค"
+ radio_select = "โ"
+ radio_unselect = "ใ"
+ triangle_left = "โ"
+ triangle_right = "โธ"
+
+
+os_configs = [
+ "API_HASH",
+ "API_ID",
+ "BOT_TOKEN",
+ "DATABASE_URL",
+ "DEPLOY_REPO",
+ "HANDLERS",
+ "HEROKU_APIKEY",
+ "HEROKU_APPNAME",
+ "LOGGER_ID",
+ "OWNER_ID",
+ "PLUGINS_REPO",
+]
+all_env: list[str] = [
+ value for key, value in ENV.__dict__.items() if not key.startswith("__")
+]
diff --git a/Hellbot/core/database.py b/Hellbot/core/database.py
new file mode 100644
index 0000000000000000000000000000000000000000..4f133c3e7a1ad5cec4e1bc436733af31a402ada9
--- /dev/null
+++ b/Hellbot/core/database.py
@@ -0,0 +1,584 @@
+import datetime
+import time
+
+from motor import motor_asyncio
+from motor.core import AgnosticClient
+
+from .config import Config, Symbols
+from .logger import LOGS
+
+
+class Database:
+ def __init__(self, uri: str) -> None:
+ self.client: AgnosticClient = motor_asyncio.AsyncIOMotorClient(uri)
+ self.db = self.client["Hellbot"]
+
+ self.afk = self.db["afk"]
+ self.antiflood = self.db["antiflood"]
+ self.autopost = self.db["autopost"]
+ self.blacklist = self.db["blacklist"]
+ self.echo = self.db["echo"]
+ self.env = self.db["env"]
+ self.filter = self.db["filter"]
+ self.forcesub = self.db["forcesub"]
+ self.gachabots = self.db["gachabots"]
+ self.gban = self.db["gban"]
+ self.gmute = self.db["gmute"]
+ self.greetings = self.db["greetings"]
+ self.mute = self.db["mute"]
+ self.pmpermit = self.db["pmpermit"]
+ self.session = self.db["session"]
+ self.snips = self.db["snips"]
+ self.stan_users = self.db["stan_users"]
+
+ async def connect(self):
+ try:
+ await self.client.admin.command("ping")
+ LOGS.info(
+ f"{Symbols.bullet * 3} Database Connection Established! {Symbols.bullet * 3}"
+ )
+ except Exception as e:
+ LOGS.info(f"{Symbols.cross_mark} DatabaseErr: {e} ")
+ quit(1)
+
+ def get_datetime(self) -> str:
+ return datetime.datetime.now().strftime("%d/%m/%Y - %H:%M")
+
+ async def set_env(self, name: str, value: str) -> None:
+ await self.env.update_one(
+ {"name": name}, {"$set": {"value": value}}, upsert=True
+ )
+
+ async def get_env(self, name: str) -> str | None:
+ if await self.is_env(name):
+ data = await self.env.find_one({"name": name})
+ return data["value"]
+ return None
+
+ async def rm_env(self, name: str) -> None:
+ await self.env.delete_one({"name": name})
+
+ async def is_env(self, name: str) -> bool:
+ if await self.env.find_one({"name": name}):
+ return True
+ return False
+
+ async def get_all_env(self) -> list:
+ return [i async for i in self.env.find({})]
+
+ async def is_stan(self, client: int, user_id: int) -> bool:
+ if await self.stan_users.find_one({"client": client, "user_id": user_id}):
+ return True
+ return False
+
+ async def add_stan(self, client: int, user_id: int) -> bool:
+ if await self.is_stan(client, user_id):
+ return False
+ await self.stan_users.insert_one(
+ {"client": client, "user_id": user_id, "date": self.get_datetime()}
+ )
+ return True
+
+ async def rm_stan(self, client: int, user_id: int) -> bool:
+ if not await self.is_stan(client, user_id):
+ return False
+ await self.stan_users.delete_one({"client": client, "user_id": user_id})
+ return True
+
+ async def get_stans(self, client: int) -> list:
+ return [i async for i in self.stan_users.find({"client": client})]
+
+ async def get_all_stans(self) -> list:
+ return [i async for i in self.stan_users.find({})]
+
+ async def is_session(self, user_id: int) -> bool:
+ if await self.session.find_one({"user_id": user_id}):
+ return True
+ return False
+
+ async def update_session(self, user_id: int, session: str) -> None:
+ await self.session.update_one(
+ {"user_id": user_id},
+ {"$set": {"session": session, "date": self.get_datetime()}},
+ upsert=True,
+ )
+
+ async def rm_session(self, user_id: int) -> None:
+ await self.session.delete_one({"user_id": user_id})
+
+ async def get_session(self, user_id: int):
+ if not await self.is_session(user_id):
+ return False
+ data = await self.session.find_one({"user_id": user_id})
+ return data
+
+ async def get_all_sessions(self) -> list:
+ return [i async for i in self.session.find({})]
+
+ async def is_gbanned(self, user_id: int) -> bool:
+ if await self.gban.find_one({"user_id": user_id}):
+ return True
+ return False
+
+ async def add_gban(self, user_id: int, reason: str) -> bool:
+ if await self.is_gbanned(user_id):
+ return False
+ await self.gban.insert_one(
+ {"user_id": user_id, "reason": reason, "date": self.get_datetime()}
+ )
+ return True
+
+ async def rm_gban(self, user_id: int):
+ if not await self.is_gbanned(user_id):
+ return None
+ reason = (await self.gban.find_one({"user_id": user_id}))["reason"]
+ await self.gban.delete_one({"user_id": user_id})
+ return reason
+
+ async def get_gban(self) -> list:
+ return [i async for i in self.gban.find({})]
+
+ async def get_gban_user(self, user_id: int) -> dict | None:
+ if not await self.is_gbanned(user_id):
+ return None
+ return await self.gban.find_one({"user_id": user_id})
+
+ async def is_gmuted(self, user_id: int) -> bool:
+ if await self.gmute.find_one({"user_id": user_id}):
+ return True
+ return False
+
+ async def add_gmute(self, user_id: int, reason: str) -> bool:
+ if await self.is_gmuted(user_id):
+ return False
+ await self.gmute.insert_one(
+ {"user_id": user_id, "reason": reason, "date": self.get_datetime()}
+ )
+ return True
+
+ async def rm_gmute(self, user_id: int):
+ if not await self.is_gmuted(user_id):
+ return None
+ reason = (await self.gmute.find_one({"user_id": user_id}))["reason"]
+ await self.gmute.delete_one({"user_id": user_id})
+ return reason
+
+ async def get_gmute(self) -> list:
+ return [i async for i in self.gmute.find({})]
+
+ async def add_mute(self, client: int, user_id: int, chat_id: int, reason: str):
+ await self.mute.update_one(
+ {"client": client, "user_id": user_id, "chat_id": chat_id},
+ {"$set": {"reason": reason, "date": self.get_datetime()}},
+ upsert=True,
+ )
+
+ async def rm_mute(self, client: int, user_id: int, chat_id: int) -> str:
+ reason = (await self.get_mute(client, user_id, chat_id))["reason"]
+ await self.mute.delete_one({"client": client, "user_id": user_id, "chat_id": chat_id})
+ return reason
+
+ async def is_muted(self, client: int, user_id: int, chat_id: int) -> bool:
+ if await self.get_mute(client, user_id, chat_id):
+ return True
+ return False
+
+ async def get_mute(self, client: int, user_id: int, chat_id: int):
+ data = await self.mute.find_one({"client": client, "user_id": user_id, "chat_id": chat_id})
+ return data
+
+ async def set_afk(
+ self, user_id: int, reason: str, media: int, media_type: str
+ ) -> None:
+ await self.afk.update_one(
+ {"user_id": user_id},
+ {
+ "$set": {
+ "reason": reason,
+ "time": time.time(),
+ "media": media,
+ "media_type": media_type,
+ }
+ },
+ upsert=True,
+ )
+
+ async def get_afk(self, user_id: int):
+ data = await self.afk.find_one({"user_id": user_id})
+ return data
+
+ async def is_afk(self, user_id: int) -> bool:
+ if await self.afk.find_one({"user_id": user_id}):
+ return True
+ return False
+
+ async def rm_afk(self, user_id: int) -> None:
+ await self.afk.delete_one({"user_id": user_id})
+
+ async def set_flood(self, client_chat: tuple[int, int], settings: dict):
+ await self.antiflood.update_one(
+ {"client": client_chat[0], "chat": client_chat[1]},
+ {"$set": settings},
+ upsert=True,
+ )
+
+ async def get_flood(self, client_chat: tuple[int, int]):
+ data = await self.antiflood.find_one(
+ {"client": client_chat[0], "chat": client_chat[1]}
+ )
+ return data or {}
+
+ async def is_flood(self, client_chat: tuple[int, int]) -> bool:
+ data = await self.get_flood(client_chat)
+
+ if not data:
+ return False
+
+ if data["limit"] == 0:
+ return False
+
+ return True
+
+ async def get_all_floods(self) -> list:
+ return [i async for i in self.antiflood.find({})]
+
+ async def set_autopost(self, client: int, from_channel: int, to_channel: int):
+ await self.autopost.update_one(
+ {"client": client},
+ {
+ "$push": {
+ "autopost": {
+ "from_channel": from_channel,
+ "to_channel": to_channel,
+ "date": self.get_datetime(),
+ }
+ }
+ },
+ upsert=True,
+ )
+
+ async def get_autopost(self, client: int, from_channel: int):
+ data = await self.autopost.find_one(
+ {
+ "client": client,
+ "autopost": {"$elemMatch": {"from_channel": from_channel}},
+ }
+ )
+ return data
+
+ async def is_autopost(
+ self, client: int, from_channel: int, to_channel: int = None
+ ) -> bool:
+ if to_channel:
+ data = await self.autopost.find_one(
+ {
+ "client": client,
+ "autopost": {
+ "$elemMatch": {
+ "from_channel": from_channel,
+ "to_channel": to_channel,
+ }
+ },
+ }
+ )
+ else:
+ data = await self.autopost.find_one(
+ {
+ "client": client,
+ "autopost": {"$elemMatch": {"from_channel": from_channel}},
+ }
+ )
+ return True if data else False
+
+ async def rm_autopost(self, client: int, from_channel: int, to_channel: int):
+ await self.autopost.update_one(
+ {"client": client},
+ {
+ "$pull": {
+ "autopost": {
+ "from_channel": from_channel,
+ "to_channel": to_channel,
+ }
+ }
+ },
+ )
+
+ async def get_all_autoposts(self, client: int) -> list:
+ return [i async for i in self.autopost.find({"client": client})]
+
+ async def add_blacklist(self, client: int, chat: int, blacklist: str):
+ await self.blacklist.update_one(
+ {"client": client, "chat": chat},
+ {"$push": {"blacklist": blacklist}},
+ upsert=True,
+ )
+
+ async def rm_blacklist(self, client: int, chat: int, blacklist: str):
+ await self.blacklist.update_one(
+ {"client": client, "chat": chat},
+ {"$pull": {"blacklist": blacklist}},
+ )
+
+ async def is_blacklist(self, client: int, chat: int, blacklist: str) -> bool:
+ blacklists = await self.get_all_blacklists(client, chat)
+ if blacklist in blacklists:
+ return True
+ return False
+
+ async def get_all_blacklists(self, client: int, chat: int) -> list:
+ data = await self.blacklist.find_one({"client": client, "chat": chat})
+
+ if not data:
+ return []
+
+ return data["blacklist"]
+
+ async def get_blacklist_clients(self) -> list:
+ return [i async for i in self.blacklist.find({})]
+
+ async def set_echo(self, client: int, chat: int, user: int):
+ await self.echo.update_one(
+ {"client": client, "chat": chat},
+ {"$push": {"echo": user}},
+ upsert=True,
+ )
+
+ async def rm_echo(self, client: int, chat: int, user: int):
+ await self.echo.update_one(
+ {"client": client, "chat": chat},
+ {"$pull": {"echo": user}},
+ )
+
+ async def is_echo(self, client: int, chat: int, user: int) -> bool:
+ data = await self.get_all_echo(client, chat)
+ if user in data:
+ return True
+ return False
+
+ async def get_all_echo(self, client: int, chat: int) -> list:
+ data = await self.echo.find_one({"client": client, "chat": chat})
+
+ if not data:
+ return []
+
+ return data["echo"]
+
+ async def set_filter(self, client: int, chat: int, keyword: str, msgid: int):
+ await self.filter.update_one(
+ {"client": client, "chat": chat},
+ {"$push": {"filter": {"keyword": keyword, "msgid": msgid}}},
+ upsert=True,
+ )
+
+ async def rm_filter(self, client: int, chat: int, keyword: str):
+ await self.filter.update_one(
+ {"client": client, "chat": chat},
+ {"$pull": {"filter": {"keyword": keyword}}},
+ )
+
+ async def rm_all_filters(self, client: int, chat: int):
+ await self.filter.delete_one({"client": client, "chat": chat})
+
+ async def is_filter(self, client: int, chat: int, keyword: str) -> bool:
+ data = await self.get_filter(client, chat, keyword)
+ return True if data else False
+
+ async def get_filter(self, client: int, chat: int, keyword: str):
+ data = await self.filter.find_one(
+ {
+ "client": client,
+ "chat": chat,
+ "filter": {"$elemMatch": {"keyword": keyword}},
+ }
+ )
+ return data
+
+ async def get_all_filters(self, client: int, chat: int) -> list:
+ data = await self.filter.find_one({"client": client, "chat": chat})
+
+ if not data:
+ return []
+
+ return data["filter"]
+
+ async def set_snip(self, client: int, chat: int, keyword: str, msgid: int):
+ await self.snips.update_one(
+ {"client": client, "chat": chat},
+ {"$push": {"snips": {"keyword": keyword, "msgid": msgid}}},
+ upsert=True,
+ )
+
+ async def rm_snip(self, client: int, chat: int, keyword: str):
+ await self.snips.update_one(
+ {"client": client, "chat": chat},
+ {"$pull": {"snips": {"keyword": keyword}}},
+ )
+
+ async def rm_all_snips(self, client: int, chat: int):
+ await self.snips.delete_one({"client": client, "chat": chat})
+
+ async def is_snip(self, client: int, chat: int, keyword: str) -> bool:
+ data = await self.get_snip(client, chat, keyword)
+ return True if data else False
+
+ async def get_snip(self, client: int, chat: int, keyword: str):
+ data = await self.snips.find_one(
+ {
+ "client": client,
+ "chat": chat,
+ "snips": {"$elemMatch": {"keyword": keyword}},
+ }
+ )
+ return data
+
+ async def get_all_snips(self, client: int, chat: int) -> list:
+ data = await self.snips.find_one({"client": client, "chat": chat})
+
+ if not data:
+ return []
+
+ return data["snips"]
+
+ async def add_pmpermit(self, client: int, user: int):
+ await self.pmpermit.update_one(
+ {"client": client, "user": user},
+ {"$set": {"date": self.get_datetime()}},
+ upsert=True,
+ )
+
+ async def rm_pmpermit(self, client: int, user: int):
+ await self.pmpermit.delete_one({"client": client, "user": user})
+
+ async def is_pmpermit(self, client: int, user: int) -> bool:
+ data = await self.get_pmpermit(client, user)
+ return True if data else False
+
+ async def get_pmpermit(self, client: int, user: int):
+ data = await self.pmpermit.find_one({"client": client, "user": user})
+ return data
+
+ async def get_all_pmpermits(self, client: int) -> list:
+ return [i async for i in self.pmpermit.find({"client": client})]
+
+ async def set_welcome(self, client: int, chat: int, message: int):
+ await self.greetings.update_one(
+ {"client": client, "chat": chat, "welcome": True},
+ {"$set": {"message": message}},
+ upsert=True,
+ )
+
+ async def rm_welcome(self, client: int, chat: int):
+ await self.greetings.delete_one(
+ {"client": client, "chat": chat, "welcome": True}
+ )
+
+ async def is_welcome(self, client: int, chat: int) -> bool:
+ data = await self.get_welcome(client, chat)
+ return True if data else False
+
+ async def get_welcome(self, client: int, chat: int):
+ data = await self.greetings.find_one(
+ {"client": client, "chat": chat, "welcome": True}
+ )
+ return data
+
+ async def set_goodbye(self, client: int, chat: int, message: int):
+ await self.greetings.update_one(
+ {"client": client, "chat": chat, "welcome": False},
+ {"$set": {"message": message}},
+ upsert=True,
+ )
+
+ async def rm_goodbye(self, client: int, chat: int):
+ await self.greetings.delete_one(
+ {"client": client, "chat": chat, "welcome": False}
+ )
+
+ async def is_goodbye(self, client: int, chat: int) -> bool:
+ data = await self.get_goodbye(client, chat)
+ return True if data else False
+
+ async def get_goodbye(self, client: int, chat: int):
+ data = await self.greetings.find_one(
+ {"client": client, "chat": chat, "welcome": False}
+ )
+ return data
+
+ async def get_all_greetings(self, client: int) -> list:
+ return [i async for i in self.greetings.find({"client": client})]
+
+ async def add_forcesub(self, chat: int, must_join: int):
+ await self.forcesub.update_one(
+ {"chat": chat},
+ {"$push": {"must_join": must_join}},
+ upsert=True,
+ )
+
+ async def rm_forcesub(self, chat: int, must_join: int) -> int:
+ await self.forcesub.update_one(
+ {"chat": chat},
+ {"$pull": {"must_join": must_join}},
+ )
+ data = await self.forcesub.find_one({"chat": chat})
+ return len(data["must_join"])
+
+ async def rm_all_forcesub(self, in_chat: int):
+ await self.forcesub.delete_one({"chat": in_chat})
+
+ async def is_forcesub(self, chat: int, must_join: int) -> bool:
+ data = await self.get_forcesub(chat)
+ if must_join in data["must_join"]:
+ return True
+ return False
+
+ async def get_forcesub(self, in_chat: int):
+ data = await self.forcesub.find_one({"chat": in_chat})
+ return data
+
+ async def get_all_forcesubs(self) -> list:
+ return [i async for i in self.forcesub.find({})]
+
+ async def add_gachabot(
+ self, client: int, bot: tuple[int, str], catch_command: str, chat_id: int
+ ):
+ await self.gachabots.update_one(
+ {"client": client, "bot": bot[0]},
+ {
+ "$set": {
+ "username": bot[1],
+ "catch_command": catch_command,
+ "chat_id": chat_id,
+ "date": self.get_datetime(),
+ }
+ },
+ upsert=True,
+ )
+
+ async def rm_gachabot(self, client: int, bot: int, chat_id: int = None):
+ if chat_id:
+ await self.gachabots.delete_one(
+ {"client": client, "bot": bot, "chat_id": chat_id}
+ )
+ else:
+ await self.gachabots.delete_one({"client": client, "bot": bot})
+
+ async def is_gachabot(self, client: int, bot: int, chat_id: int) -> bool:
+ data = await self.get_gachabot(client, bot, chat_id)
+ return True if data else False
+
+ async def get_gachabot(self, client: int, bot: int, chat_id: int):
+ data = await self.gachabots.find_one(
+ {"client": client, "bot": bot, "chat_id": chat_id}
+ )
+
+ return data
+
+ async def get_all_gachabots(self, client: int) -> list:
+ return [i async for i in self.gachabots.find({"client": client})]
+
+ async def get_all_gachabots_id(self) -> list:
+ data = await self.gachabots.distinct("bot")
+ return data
+
+
+db = Database(Config.DATABASE_URL)
diff --git a/Hellbot/core/initializer.py b/Hellbot/core/initializer.py
new file mode 100644
index 0000000000000000000000000000000000000000..d4fba9642c9cd415537c088f3abd8ebb46ae6fc5
--- /dev/null
+++ b/Hellbot/core/initializer.py
@@ -0,0 +1,94 @@
+import sys
+from .clients import hellbot
+from .config import Config, Symbols
+from .database import db
+from .logger import LOGS
+
+
+async def _AuthUsers() -> None:
+ temp_list = []
+ temp_list.append(Config.OWNER_ID)
+ temp_list.extend([(await client.get_me()).id for client in hellbot.users])
+
+ stan_users = await db.get_all_stans()
+ for user in stan_users:
+ temp_list.append(user["user_id"])
+
+ users = list(set(temp_list))
+ for user in users:
+ Config.AUTH_USERS.add(user)
+
+ temp_list = None
+ LOGS.info(
+ f"{Symbols.arrow_right * 2} Added Authorized Users {Symbols.arrow_left * 2}"
+ )
+
+
+async def _StanUsers() -> None:
+ users = await db.get_all_stans()
+ for user in users:
+ Config.STAN_USERS.add(user["user_id"])
+
+ LOGS.info(f"{Symbols.arrow_right * 2} Added Stan Users {Symbols.arrow_left * 2}")
+
+
+async def _GbanUsers() -> None:
+ users = await db.get_gban()
+ for user in users:
+ Config.BANNED_USERS.add(user["user_id"])
+
+ LOGS.info(
+ f"{Symbols.arrow_right * 2} Added {len(users)} Gbanned Users {Symbols.arrow_left * 2}"
+ )
+
+ musers = await db.get_gmute()
+ for user in musers:
+ Config.MUTED_USERS.add(user["user_id"])
+
+ LOGS.info(
+ f"{Symbols.arrow_right * 2} Added {len(musers)} Gmuted Users {Symbols.arrow_left * 2}"
+ )
+
+
+async def UserSetup() -> None:
+ """Initialize Users Config"""
+ LOGS.info(f"{Symbols.bullet * 3} Setting Up Users {Symbols.bullet * 3}")
+ await _AuthUsers()
+ await _StanUsers()
+ await _GbanUsers()
+
+
+async def ForcesubSetup() -> None:
+ """Initialize Forcesub Config"""
+ chats = await db.get_all_forcesubs()
+ for chat in chats:
+ if chat not in Config.FORCESUBS:
+ Config.FORCESUBS.add(chat["chat"])
+
+
+async def GachaBotsSetup() -> None:
+ """Initialize GachaBots Config"""
+ bots = await db.get_all_gachabots_id()
+ for bot in bots:
+ Config.GACHA_BOTS.add(bot)
+
+
+async def TemplateSetup() -> None:
+ """Initialize Templates Config"""
+ module_name = "temp_module"
+ module = sys.modules.get(module_name)
+ if module is None:
+ module = type(sys)(module_name)
+
+ with open("Hellbot/functions/templates.py", "r", encoding="utf-8") as file:
+ exec(file.read(), module.__dict__)
+
+ global_vars = module.__dict__
+
+ var_n_value: dict[str, str] = {
+ var_name: global_vars[var_name][0]
+ for var_name in global_vars
+ if var_name.isupper() and not callable(global_vars[var_name])
+ }
+
+ Config.TEMPLATES = var_n_value
diff --git a/Hellbot/core/logger.py b/Hellbot/core/logger.py
new file mode 100644
index 0000000000000000000000000000000000000000..c0cfdc03af6abb4f95fc0db8007d2ef84dbf6ba4
--- /dev/null
+++ b/Hellbot/core/logger.py
@@ -0,0 +1,19 @@
+import logging
+from logging.handlers import RotatingFileHandler
+
+logging.basicConfig(
+ format="[%(asctime)s]:[%(name)s]:[%(levelname)s] - %(message)s",
+ level=logging.INFO,
+ datefmt="%H:%M:%S",
+ handlers=[
+ RotatingFileHandler(
+ "HellBot.log", maxBytes=(1024 * 1024 * 5), backupCount=10, encoding="utf-8"
+ ),
+ logging.StreamHandler(),
+ ],
+)
+
+
+logging.getLogger("pyrogram").setLevel(logging.ERROR)
+
+LOGS = logging.getLogger("HellBot")
diff --git a/Hellbot/functions/__init__.py b/Hellbot/functions/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/Hellbot/functions/admins.py b/Hellbot/functions/admins.py
new file mode 100644
index 0000000000000000000000000000000000000000..934dfecc8e375bb85f85276af690dd73a4256076
--- /dev/null
+++ b/Hellbot/functions/admins.py
@@ -0,0 +1,24 @@
+from pyrogram.enums import ChatMembersFilter, ChatMemberStatus, ChatType
+from pyrogram.types import Chat
+
+from Hellbot.core import hellbot
+
+
+async def get_admins(chat_id: int) -> list:
+ admins = []
+ async for x in hellbot.bot.get_chat_members(
+ chat_id, filter=ChatMembersFilter.ADMINISTRATORS
+ ):
+ admins.append(x.user.id)
+ return admins
+
+
+async def is_user_admin(chat: Chat, user_id: int) -> bool:
+ if chat.type in [ChatType.PRIVATE, ChatType.BOT]:
+ return True
+
+ status = (await chat.get_member(user_id)).status
+ if status in [ChatMemberStatus.OWNER, ChatMemberStatus.ADMINISTRATOR]:
+ return True
+
+ return False
diff --git a/Hellbot/functions/convert.py b/Hellbot/functions/convert.py
new file mode 100644
index 0000000000000000000000000000000000000000..74b8832b531efdf03ee5a8c8b248008e52d2cc7f
--- /dev/null
+++ b/Hellbot/functions/convert.py
@@ -0,0 +1,98 @@
+import os
+import time
+
+from pyrogram.types import Message
+from PIL import Image
+from Hellbot.core import Config
+
+from .tools import runcmd
+
+
+async def convert_to_gif(file: str, is_video: bool = False) -> str:
+ resultFileName = f"gif_{round(time.time())}.mp4"
+
+ if is_video:
+ cmd = f"ffmpeg -i '{file}' -c copy '{resultFileName}'"
+ else:
+ cmd = f"lottie_convert.py '{file}' '{resultFileName}'"
+
+ await runcmd(cmd)
+
+ return resultFileName
+
+
+async def tgs_to_png(file: str) -> str:
+ resultFileName = f"png_{round(time.time())}.png"
+
+ cmd = f"lottie_convert.py '{file}' '{resultFileName}'"
+
+ await runcmd(cmd)
+
+ return resultFileName
+
+
+async def image_to_sticker(file: str, max_size: tuple = (512, 512)) -> tuple[bool, str]:
+ try:
+ with Image.open(file) as img:
+ original_width, original_height = img.size
+
+ new_width = min(original_width, max_size[0])
+ new_height = min(original_height, max_size[1])
+
+ if original_width > max_size[0] or original_height > max_size[1]:
+ img = img.resize((new_width, new_height), Image.LANCZOS)
+
+ file_name = f"sticker_{int(time.time())}.png"
+ img.save(file_name, "PNG")
+
+ return True, file_name
+
+ except Exception as e:
+ return False, str(e)
+
+
+async def video_to_png(
+ file: str, duration: float, output: str = None
+) -> tuple[str, bool]:
+ resultFileName = output or f"{os.path.basename(file)}.png"
+ cut_at = duration // 2
+
+ cmd = f"ffmpeg -ss {cut_at} -i '{file}' -vframes 1 '{resultFileName}'"
+
+ _, err, _, _ = await runcmd(cmd)
+ if err:
+ return err, False
+
+ return resultFileName, True
+
+
+async def video_to_sticker(file: Message) -> tuple[str, bool]:
+ try:
+ if file.animation:
+ width, height = file.animation.width, file.animation.height
+ elif file.video:
+ width, height = file.video.width, file.video.height
+ else:
+ return "Unsupported media type.", False
+
+ file_path = await file.download(Config.TEMP_DIR)
+ output_path = os.path.join(Config.TEMP_DIR, "videoSticker.webm")
+
+ if height > width:
+ scale_params = f"scale=-1:512"
+ else:
+ scale_params = f"scale=512:-1"
+
+ cmd = (
+ f"ffmpeg -i {file_path} "
+ f"-vf fps=30,{scale_params} -t 3 -c:v libvpx-vp9 -b:v 256k -an -pix_fmt yuv420p -auto-alt-ref 0 -loop 0 "
+ f"-f webm {output_path}"
+ )
+
+ await runcmd(cmd)
+ os.remove(file_path)
+
+ return output_path, True
+
+ except Exception as e:
+ return f"Error during conversion: {e}", False
diff --git a/Hellbot/functions/driver.py b/Hellbot/functions/driver.py
new file mode 100644
index 0000000000000000000000000000000000000000..f461154258888621b0931b8327cd4afd84c493b8
--- /dev/null
+++ b/Hellbot/functions/driver.py
@@ -0,0 +1,318 @@
+import datetime
+import json
+import random
+import re
+import time
+import urllib.parse
+from urllib.parse import quote_plus
+
+import httpx
+import requests
+from pytz import country_names, country_timezones, timezone
+from selenium import webdriver
+from selenium.webdriver.chrome.options import Options
+from selenium.webdriver.chrome.service import Service
+from selenium.webdriver.common.by import By
+
+from Hellbot.core import ENV, Config, db
+
+from .formatter import format_text
+
+
+class ChromeDriver:
+ def __init__(self) -> None:
+ self.carbon_theme = [
+ "3024-night",
+ "a11y-dark",
+ "blackboard",
+ "base16-dark",
+ "base16-light",
+ "cobalt",
+ "duotone-dark",
+ "hopscotch",
+ "lucario",
+ "material",
+ "monokai",
+ "night-owl",
+ "nord",
+ "oceanic-next",
+ "one-light",
+ "one-dark",
+ "panda-syntax",
+ "paraiso-dark",
+ "seti",
+ "shades-of-purple",
+ "solarized+dark",
+ "solarized+light",
+ "synthwave-84",
+ "twilight",
+ "verminal",
+ "vscode",
+ "yeti",
+ "zenburn",
+ ]
+
+ def get(self):
+ if not Config.CHROME_BIN:
+ return (
+ None,
+ "ChromeBinaryErr: No binary path found! Install Chromium or Google Chrome.",
+ )
+
+ try:
+ options = Options()
+ options.binary_location = Config.CHROME_BIN
+ options.add_argument("--disable-dev-shm-usage")
+ options.add_argument("--ignore-certificate-errors")
+ options.add_argument("--disable-gpu")
+ options.add_argument("--headless=new")
+ options.add_argument("--test-type")
+ options.add_argument("--no-sandbox")
+ options.add_argument("--window-size=1920x1080")
+ options.add_experimental_option(
+ "prefs", {"download.default_directory": "./"}
+ )
+ service = Service(Config.CHROME_DRIVER)
+ driver = webdriver.Chrome(options, service)
+ return driver, None
+ except Exception as e:
+ return None, f"ChromeDriverErr: {e}"
+
+ def close(self, driver: webdriver.Chrome):
+ driver.close()
+ driver.quit()
+
+ @property
+ def get_random_carbon(self) -> str:
+ url = "https://carbon.now.sh/?l=auto"
+ url += f"&t={random.choice(self.carbon_theme)}"
+ url += f"&bg=rgba%28{random.randint(1, 255)}%2C{random.randint(1, 255)}%2C{random.randint(1, 255)}%2C1%29"
+ url += "&code="
+ return url
+
+ async def generate_carbon(
+ self, driver: webdriver.Chrome, code: str, is_random: bool = False
+ ) -> str:
+ filename = f"{round(time.time())}"
+ BASE_URL = (
+ self.get_random_carbon
+ if is_random
+ else "https://carbon.now.sh/?l=auto&code="
+ )
+
+ driver.get(BASE_URL + format_text(quote_plus(code)))
+ driver.command_executor._commands["send_command"] = (
+ "POST",
+ "/session/$sessionId/chromium/send_command",
+ )
+ params = {
+ "cmd": "Page.setDownloadBehavior",
+ "params": {"behavior": "allow", "downloadPath": Config.DWL_DIR},
+ }
+ driver.execute("send_command", params)
+
+ driver.find_element(By.XPATH, "//button[@id='export-menu']").click()
+ driver.find_element(By.XPATH, "//input[@title='filename']").send_keys(filename)
+ driver.find_element(By.XPATH, "//button[@id='export-png']").click()
+
+ return f"{Config.DWL_DIR}/{filename}.png"
+
+
+class ClimateDriver:
+ def __init__(self) -> None:
+ self.weather_api = "https://api.openweathermap.org/data/2.5/weather?lat={0}&lon={1}&appid={2}&units=metric"
+ self.location_api = (
+ "https://api.openweathermap.org/geo/1.0/direct?q={0}&limit=1&appid={1}"
+ )
+ self.pollution_api = "http://api.openweathermap.org/data/2.5/air_pollution?lat={0}&lon={1}&appid={2}"
+ self.AQI_DICT = {
+ 1: "Good",
+ 2: "Fair",
+ 3: "Moderate",
+ 4: "Poor",
+ 5: "Very Poor",
+ }
+
+ async def fetchLocation(self, city: str, apiKey: str):
+ response = httpx.get(self.location_api.format(city, apiKey))
+ if response.status_code == 200:
+ data = response.json()
+ if data:
+ return data[0]["lat"], data[0]["lon"]
+ return None, None
+
+ async def fetchWeather(self, city: str, apiKey: str):
+ lattitude, longitude = await self.fetchLocation(city, apiKey)
+ if not lattitude and not longitude:
+ return None
+
+ response = httpx.get(self.weather_api.format(lattitude, longitude, apiKey))
+ if response.status_code == 200:
+ return response.json()
+ return None
+
+ async def fetchAirPollution(self, city: str, apiKey: str):
+ lattitude, longitude = await self.fetchLocation(city, apiKey)
+ if not lattitude and not longitude:
+ return None
+
+ response = httpx.get(self.pollution_api.format(lattitude, longitude, apiKey))
+ if response.status_code == 200:
+ return response.json()
+ return None
+
+ async def getTime(self, timestamp: int) -> str:
+ tz = await db.get_env(ENV.time_zone) or "Asia/Kolkata"
+ tz = timezone(tz)
+ return datetime.datetime.fromtimestamp(timestamp, tz=tz).strftime("%I:%M %p")
+
+ def getCountry(self, country_code: str) -> str:
+ return country_names.get(country_code, "Unknown")
+
+ def getCountryTimezone(self, country_code: str) -> str:
+ timezones = country_timezones.get(country_code, [])
+ if timezones:
+ return ", ".join(timezones)
+ return "Unknown"
+
+ def getWindData(self, windSpeed: str, windDegree: str) -> str:
+ dirs = ["N", "NE", "E", "SE", "S", "SW", "W", "NW"]
+ ix = round(windDegree / (360.00 / len(dirs)))
+ kmph = str(float(windSpeed) * 3.6) + " km/h"
+ return f"[{dirs[ix % len(dirs)]}] {kmph}"
+
+
+class YoutubeDriver:
+ def __init__(self, search_terms: str, max_results: int = 5):
+ self.base_url = "https://youtube.com/results?search_query={0}"
+ self.search_terms = search_terms
+ self.max_results = max_results
+ self.videos = self._search()
+
+ def _search(self):
+ encoded_search = urllib.parse.quote_plus(self.search_terms)
+ response = requests.get(self.base_url.format(encoded_search)).text
+
+ while "ytInitialData" not in response:
+ response = requests.get(self.base_url.format(encoded_search)).text
+
+ results = self._parse_html(response)
+
+ if self.max_results is not None and len(results) > self.max_results:
+ return results[: self.max_results]
+
+ return results
+
+ def _parse_html(self, response: str):
+ results = []
+ start = response.index("ytInitialData") + len("ytInitialData") + 3
+ end = response.index("};", start) + 1
+ json_str = response[start:end]
+ data = json.loads(json_str)
+
+ videos = data["contents"]["twoColumnSearchResultsRenderer"]["primaryContents"][
+ "sectionListRenderer"
+ ]["contents"][0]["itemSectionRenderer"]["contents"]
+
+ for video in videos:
+ res = {}
+ if "videoRenderer" in video.keys():
+ video_data = video.get("videoRenderer", {})
+ _id = video_data.get("videoId", None)
+
+ res["id"] = _id
+ res["thumbnail"] = f"https://i.ytimg.com/vi/{_id}/hqdefault.jpg"
+ res["title"] = (
+ video_data.get("title", {}).get("runs", [[{}]])[0].get("text", None)
+ )
+ res["channel"] = (
+ video_data.get("longBylineText", {})
+ .get("runs", [[{}]])[0]
+ .get("text", None)
+ )
+ res["duration"] = video_data.get("lengthText", {}).get("simpleText", 0)
+ res["views"] = video_data.get("viewCountText", {}).get(
+ "simpleText", "Unknown"
+ )
+ res["publish_time"] = video_data.get("publishedTimeText", {}).get(
+ "simpleText", "Unknown"
+ )
+ res["url_suffix"] = (
+ video_data.get("navigationEndpoint", {})
+ .get("commandMetadata", {})
+ .get("webCommandMetadata", {})
+ .get("url", None)
+ )
+
+ results.append(res)
+ return results
+
+ def to_dict(self, clear_cache=True) -> list[dict]:
+ result = self.videos
+ if clear_cache:
+ self.videos = []
+ return result
+
+ @staticmethod
+ def check_url(url: str) -> tuple[bool, str]:
+ if "&" in url:
+ url = url[: url.index("&")]
+
+ if "?si=" in url:
+ url = url[: url.index("?si=")]
+
+ youtube_regex = (
+ r"(https?://)?(www\.)?"
+ r"(youtube|youtu|youtube-nocookie)\.(com|be)/"
+ r'(video|embed|shorts/|watch\?v=|v/|e/|u/\\w+/|\\w+/)?([^"&?\\s]{11})'
+ )
+ match = re.match(youtube_regex, url)
+ if match:
+ return True, match.group(6)
+ else:
+ return False, "Invalid YouTube URL!"
+
+ @staticmethod
+ def song_options() -> dict:
+ return {
+ "format": "bestaudio",
+ "addmetadata": True,
+ "key": "FFmpegMetadata",
+ "prefer_ffmpeg": True,
+ "geo_bypass": True,
+ "nocheckcertificate": True,
+ "postprocessors": [
+ {
+ "key": "FFmpegExtractAudio",
+ "preferredcodec": "mp3",
+ "preferredquality": "480",
+ }
+ ],
+ "outtmpl": "%(id)s",
+ "quiet": True,
+ "logtostderr": False,
+ }
+
+ @staticmethod
+ def video_options() -> dict:
+ return {
+ "format": "best",
+ "addmetadata": True,
+ "key": "FFmpegMetadata",
+ "prefer_ffmpeg": True,
+ "geo_bypass": True,
+ "nocheckcertificate": True,
+ "postprocessors": [
+ {
+ "key": "FFmpegVideoConvertor",
+ "preferedformat": "mp4",
+ }
+ ],
+ "outtmpl": "%(id)s.mp4",
+ "quiet": True,
+ "logtostderr": False,
+ }
+
+
+Driver = ChromeDriver()
+Climate = ClimateDriver()
diff --git a/Hellbot/functions/formatter.py b/Hellbot/functions/formatter.py
new file mode 100644
index 0000000000000000000000000000000000000000..1b6ca654c87e07b085d34c096be9cd723457c82f
--- /dev/null
+++ b/Hellbot/functions/formatter.py
@@ -0,0 +1,94 @@
+import math
+import re
+
+
+def format_text(text: str) -> str:
+ emoji_pattern = re.compile(
+ "["
+ "\U0001F600-\U0001F64F" # emoticons
+ "\U0001F300-\U0001F5FF" # symbols & pictographs
+ "\U0001F680-\U0001F6FF" # transport & map symbols
+ "\U0001F700-\U0001F77F" # alchemical symbols
+ "\U0001F780-\U0001F7FF" # Geometric Shapes Extended
+ "\U0001F800-\U0001F8FF" # Supplemental Arrows-C
+ "\U0001F900-\U0001F9FF" # Supplemental Symbols and Pictographs
+ "\U0001FA00-\U0001FA6F" # Chess Symbols
+ "\U0001FA70-\U0001FAFF" # Symbols and Pictographs Extended-A
+ "\U00002702-\U000027B0" # Dingbats
+ "\U000024C2-\U0001F251" # enclosed characters
+ "]+",
+ flags=re.UNICODE,
+ )
+
+ return re.sub(emoji_pattern, "", text)
+
+
+def superscript(text: str) -> str:
+ superscript_digits = str.maketrans("0123456789", "โฐยนยฒยณโดโตโถโทโธโน")
+ return text.translate(superscript_digits)
+
+
+def subscript(text: str) -> str:
+ subscript_digits = str.maketrans("0123456789", "โโโโโโ
โโโโ")
+ return text.translate(subscript_digits)
+
+
+def readable_time(seconds: int) -> str:
+ count = 0
+ out_time = ""
+ time_list = []
+ time_suffix_list = ["secs", "mins", "hrs", "days"]
+
+ while count < 4:
+ count += 1
+ remainder, result = divmod(seconds, 60) if count < 3 else divmod(seconds, 24)
+ if seconds == 0 and remainder == 0:
+ break
+ time_list.append(int(result))
+ seconds = int(remainder)
+
+ for x in range(len(time_list)):
+ time_list[x] = str(time_list[x]) + time_suffix_list[x]
+
+ if len(time_list) == 4:
+ out_time += time_list.pop() + ", "
+
+ time_list.reverse()
+ out_time += " ".join(time_list)
+
+ return out_time or "0 secs"
+
+
+def humanbytes(size: int):
+ if not size:
+ return ""
+ power = 2**10
+ number = 0
+ dict_power_n = {0: " ", 1: "Ki", 2: "Mi", 3: "Gi", 4: "Ti"}
+ while size > power:
+ size /= power
+ number += 1
+ return str(round(size, 2)) + " " + dict_power_n[number] + "B"
+
+
+def add_to_dict(data: dict, keys: list, value: str | int | bool = None) -> None:
+ current_level = data
+ for key in keys[:-1]:
+ current_level = current_level.setdefault(key, {})
+ current_level[keys[-1]] = value
+
+
+def get_from_dict(data: dict, key: list):
+ current_level = data
+ for k in key:
+ current_level = current_level[k]
+ return current_level
+
+
+def limit_per_page(limit: int) -> int:
+ return math.ceil(limit / 10)
+
+
+def secs_to_mins(secs: int) -> str:
+ mins, secs = divmod(secs, 60)
+ return f"{mins}:{secs}"
diff --git a/Hellbot/functions/images.py b/Hellbot/functions/images.py
new file mode 100644
index 0000000000000000000000000000000000000000..017b60bb9ae79774596e99f3e8b24e0cf215c78a
--- /dev/null
+++ b/Hellbot/functions/images.py
@@ -0,0 +1,389 @@
+import calendar
+import logging
+import os
+import random
+import textwrap
+import time
+
+import httpx
+from icrawler.builtin import BingImageCrawler
+from PIL import Image, ImageDraw, ImageEnhance, ImageFont, ImageOps
+from unidecode import unidecode
+
+from .formatter import format_text, limit_per_page
+
+
+def convert_to_png(image: str) -> str:
+ output_img = f"png_{round(time.time())}.png"
+
+ img = Image.open(image)
+ img.save(output_img, "PNG")
+ img.close()
+
+ os.remove(image)
+ return output_img
+
+
+def add_rounded_corners(img: Image.Image, radius: int = 80):
+ circle = Image.new("L", (radius * 2, radius * 2), 0)
+
+ draw = ImageDraw.Draw(circle)
+ draw.ellipse((0, 0, radius * 2, radius * 2), fill=255)
+
+ alpha = Image.new("L", img.size, 255)
+ w, h = img.size
+
+ alpha.paste(circle.crop((0, 0, radius, radius)), (0, 0))
+ alpha.paste(circle.crop((radius, 0, radius * 2, radius)), (w - radius, 0))
+ alpha.paste(circle.crop((0, radius, radius, radius * 2)), (0, h - radius))
+ alpha.paste(
+ circle.crop((radius, radius, radius * 2, radius * 2)), (w - radius, h - radius)
+ )
+
+ img.putalpha(alpha)
+
+ return img
+
+
+def generate_alive_image(
+ username: str, profile_pic: str, del_img: bool, font_path: str
+) -> str:
+ if not profile_pic.endswith(".png"):
+ profile_pic = convert_to_png(profile_pic)
+
+ img = Image.open(profile_pic).convert("RGBA")
+ img_rotated = img.rotate(45, expand=True)
+
+ width, height = img_rotated.size
+ left = width / 2 - 480 / 2
+ top = height / 2 - 480 / 2
+ right = width / 2 + 480 / 2
+ bottom = height / 2 + 480 / 2
+
+ cropped_img = img_rotated.crop((left, top, right, bottom))
+
+ img_rotated = ImageOps.fit(
+ cropped_img, (480, 480), method=0, bleed=0.0, centering=(0.5, 0.5)
+ )
+
+ img_rounded = add_rounded_corners(img_rotated)
+
+ img = img_rounded.rotate(-45, expand=True)
+
+ background = Image.open("./Hellbot/resources/images/hellbot_alive.png").convert(
+ "RGBA"
+ )
+
+ background.paste(img, (383, 445), img)
+ draw = ImageDraw.Draw(background)
+
+ text = format_text(username[:25] + ("..." if len(username) > 25 else ""))
+
+ font_size = width // 15
+ font = ImageFont.truetype(font_path, font_size, encoding="utf-8")
+
+ text_length = draw.textlength(text, font)
+ position = ((background.width - text_length) / 2, background.height - 145)
+ draw.text(
+ position,
+ unidecode(text),
+ (255, 255, 255),
+ font,
+ )
+
+ output_img = f"alive_{int(time.time())}.png"
+ background.save(output_img, "PNG")
+ background.close()
+
+ if del_img:
+ os.remove(profile_pic)
+
+ return output_img
+
+
+async def get_wallpapers(
+ access: str,
+ limit: int,
+ query: str = "",
+ isRandom: bool = False,
+) -> list[str]:
+ headers = {"Authorization": f"Client-ID {access}"}
+
+ if isRandom:
+ api = f"https://api.unsplash.com/photos/random?count={limit}"
+ response = httpx.get(api, headers=headers)
+ results = response.json()
+ urls = [i["urls"]["raw"] for i in results]
+ else:
+ api = f"https://api.unsplash.com/search/photos?query={query}&page={limit_per_page(limit)}"
+ response = httpx.get(api, headers=headers)
+ result = response.json()
+ urls = [i["urls"]["raw"] for i in result["results"]]
+
+ random.shuffle(urls)
+
+ return urls[:limit]
+
+
+async def deep_fry(img: Image.Image) -> Image.Image:
+ colours = (
+ (random.randint(50, 200), random.randint(40, 170), random.randint(40, 190)),
+ (random.randint(190, 255), random.randint(170, 240), random.randint(180, 250)),
+ )
+
+ img = img.copy().convert("RGB")
+ img = img.convert("RGB")
+
+ width, height = img.width, img.height
+
+ img = img.resize(
+ (
+ int(width ** random.uniform(0.8, 0.9)),
+ int(height ** random.uniform(0.8, 0.9)),
+ ),
+ resample=Image.LANCZOS,
+ )
+
+ img = img.resize(
+ (
+ int(width ** random.uniform(0.85, 0.95)),
+ int(height ** random.uniform(0.85, 0.95)),
+ ),
+ resample=Image.BILINEAR,
+ )
+
+ img = img.resize(
+ (
+ int(width ** random.uniform(0.89, 0.98)),
+ int(height ** random.uniform(0.89, 0.98)),
+ ),
+ resample=Image.BICUBIC,
+ )
+
+ img = img.resize((width, height), resample=Image.BICUBIC)
+ img = ImageOps.posterize(img, random.randint(3, 7))
+
+ overlay = img.split()[0]
+ overlay = ImageEnhance.Contrast(overlay).enhance(random.uniform(1.0, 2.0))
+ overlay = ImageEnhance.Brightness(overlay).enhance(random.uniform(1.0, 2.0))
+ overlay = ImageOps.colorize(overlay, colours[0], colours[1])
+
+ img = Image.blend(img, overlay, random.uniform(0.1, 0.4))
+ img = ImageEnhance.Sharpness(img).enhance(random.randint(5, 300))
+
+ return img
+
+
+async def make_logo(background: str, text: str, font_path: str) -> str:
+ if not background.endswith(".png"):
+ background = convert_to_png(background)
+
+ bg = Image.open(background).convert("RGBA")
+ bgWidth, bgHeight = bg.size
+
+ text = format_text(text)
+ font_size = bgWidth // len(text)
+ font = ImageFont.truetype(font_path, font_size, encoding="utf-8")
+
+ draw = ImageDraw.Draw(bg)
+ text_length = draw.textlength(text, font)
+
+ x = (bgWidth - text_length) // 2
+ y = (bgHeight - font_size) // 2
+
+ draw.text(
+ (x, y),
+ unidecode(text),
+ (255, 255, 255),
+ font,
+ stroke_fill=(0, 0, 0),
+ stroke_width=2,
+ )
+
+ output_img = f"logo_{int(time.time())}.png"
+ bg.save(output_img, "PNG")
+ bg.close()
+
+ os.remove(background)
+
+ return output_img
+
+
+async def draw_meme(
+ image_path: str, upper_text: str = "", lower_text: str = ""
+) -> list[str]:
+ image = Image.open(image_path)
+ width, height = image.size
+
+ draw = ImageDraw.Draw(image)
+ font_size = int((30 / 500) * width)
+ font = ImageFont.truetype("./Hellbot/resources/fonts/Montserrat.ttf", font_size)
+
+ curr_height, padding = 20, 5
+ for utext in textwrap.wrap(upper_text, 25):
+ upper_width = draw.textlength(utext, font=font)
+ draw.text(
+ ((width - upper_width) / 2, curr_height),
+ unidecode(utext),
+ (255, 255, 255),
+ font,
+ stroke_width=3,
+ stroke_fill=(0, 0, 0),
+ )
+ curr_height += font_size + padding
+
+ curr_height = height - font_size
+ for ltext in reversed(textwrap.wrap(lower_text, 25)):
+ lower_width = draw.textlength(ltext, font=font)
+ draw.text(
+ ((width - lower_width) / 2, curr_height - font_size),
+ ltext,
+ (255, 255, 255),
+ font,
+ stroke_width=3,
+ stroke_fill=(0, 0, 0),
+ )
+ curr_height -= font_size + padding
+
+ filename = f"meme_{int(time.time())}"
+ image.save(f"{filename}.png", "PNG", optimize=True)
+ image.save(f"{filename}.webp", "WEBP", optimize=True)
+ image.close()
+
+ return [f"{filename}.png", f"{filename}.webp"]
+
+
+async def remove_bg(api_key: str, image: str) -> str:
+ response = httpx.post(
+ "https://api.remove.bg/v1.0/removebg",
+ files={"image_file": open(image, "rb")},
+ data={"size": "auto"},
+ headers={"X-Api-Key": api_key},
+ )
+ filename = f"removedbg_{int(time.time())}.png"
+
+ if response.is_success:
+ with open(filename, "wb") as f:
+ f.write(response.content)
+ else:
+ raise Exception(
+ f"RemoveBGError: [{response.status_code}] {response.content.decode('utf-8')}"
+ )
+
+ return filename
+
+
+def create_gradient(
+ size: tuple[int, int],
+ color_start: tuple[int, int, int],
+ color_end: tuple[int, int, int],
+) -> Image.Image:
+ gradient = Image.new("RGB", (size))
+ draw = ImageDraw.Draw(gradient)
+
+ for x in range(size[0]):
+ r = int(color_start[0] + (color_end[0] - color_start[0]) * (x / size[0]))
+ g = int(color_start[1] + (color_end[1] - color_start[1]) * (x / size[0]))
+ b = int(color_start[2] + (color_end[2] - color_start[2]) * (x / size[0]))
+
+ draw.line([(x, 0), (x, size[1])], fill=(r, g, b))
+
+ return gradient
+
+
+async def create_calendar(year: int, month: int) -> str:
+ cal = calendar.monthcalendar(year, month)
+ month_name = calendar.month_name[month]
+
+ calendar_image = create_gradient((500, 500), (140, 200, 250), (0, 150, 200))
+ draw = ImageDraw.Draw(calendar_image)
+
+ month_font = ImageFont.truetype("./Hellbot/resources/fonts/Montserrat.ttf", 40)
+ month_x = (
+ calendar_image.width - draw.textlength(f"{month_name} {year}", month_font)
+ ) // 2
+ month_y = 30
+ draw.text(
+ (month_x, month_y),
+ f"{month_name} {year}",
+ (43, 255, 136),
+ month_font,
+ stroke_width=2,
+ stroke_fill=(255, 40, 40),
+ )
+
+ week_font = ImageFont.truetype("./Hellbot/resources/fonts/Montserrat.ttf", 23)
+ weekdays_text = " ".join([day[:3] for day in calendar.day_name])
+ textsize = draw.textlength(weekdays_text, week_font)
+ draw.text(
+ ((calendar_image.width - textsize) // 2, month_y + 80),
+ weekdays_text,
+ (150, 190, 200),
+ week_font,
+ stroke_width=2,
+ stroke_fill=(200, 150, 250),
+ )
+
+ scale_factor = 1.5
+ cell_size = 30
+ padding = 15
+
+ font = ImageFont.truetype("./Hellbot/resources/fonts/Montserrat.ttf", 30)
+
+ for week_num, week in enumerate(cal):
+ for day_num, day in enumerate(week):
+ x = int(day_num * (cell_size + padding) * scale_factor)
+ y = int((week_num + 3) * (cell_size + padding) * scale_factor)
+
+ cell_width = int(cell_size * scale_factor)
+ cell_height = int(cell_size * scale_factor)
+
+ text_x = (
+ int(x + (cell_width - draw.textlength(str(day), font=font)) // 2)
+ + cell_size
+ )
+ text_y = (
+ int(y + (cell_height - draw.textlength(str(day), font=font)) // 2) - 55
+ )
+
+ if day != 0:
+ draw.text(
+ (text_x, text_y),
+ str(day),
+ (240, 200, 100),
+ font,
+ stroke_width=1,
+ stroke_fill=(0, 0, 0),
+ )
+
+ filename = f"calendar_{int(time.time())}.png"
+ calendar_image.save(filename, "PNG")
+ calendar_image.close()
+
+ return filename
+
+
+async def create_thumbnail(photo: str, xy: tuple[int, int], file_size: int):
+ img = Image.open(photo)
+ img.thumbnail(xy)
+
+ size_in_bytes = file_size * 1024
+ quality = 90
+
+ while True:
+ img.save(photo, "JPEG", quality=quality, optimize=True)
+ if os.path.getsize(photo) <= size_in_bytes:
+ break
+
+ quality -= 5
+
+ return photo
+
+
+async def download_images(query: str, limit: int) -> list[str]:
+ offset = random.randint(0, 20)
+
+ crawler = BingImageCrawler(log_level=logging.ERROR)
+ crawler.crawl(query, offset=offset, max_num=limit)
+
+ return [os.path.join("images", image) for image in os.listdir("images")]
diff --git a/Hellbot/functions/media.py b/Hellbot/functions/media.py
new file mode 100644
index 0000000000000000000000000000000000000000..fb7f604d621befd0afaa3b597c7fd50af4597fc4
--- /dev/null
+++ b/Hellbot/functions/media.py
@@ -0,0 +1,192 @@
+import os
+from typing import Union
+
+import requests
+from pyrogram import Client
+from pyrogram.file_id import FileId
+from pyrogram.raw.functions.messages import UploadMedia
+from pyrogram.raw.types import (
+ DocumentAttributeFilename,
+ InputDocument,
+ InputMediaUploadedDocument,
+)
+from pyrogram.types import Animation, Audio, Document, Message, Photo, Sticker, Video
+
+from Hellbot.core import Symbols
+
+
+async def get_metedata(media: Union[Animation, Audio, Document, Photo, Sticker, Video]):
+ output = "๐ MetaData:\n\n"
+ if isinstance(media, Animation):
+ output += f"{Symbols.diamond_2} File ID: {media.file_id}
\n"
+ output += f"{Symbols.diamond_2} Width: {media.width}
\n"
+ output += f"{Symbols.diamond_2} Height: {media.height}
\n"
+ output += (
+ f"{Symbols.diamond_2} Duration: {media.duration}
\n"
+ )
+ output += (
+ f"{Symbols.diamond_2} File Name: {media.file_name}
\n"
+ )
+ output += (
+ f"{Symbols.diamond_2} Mime Type: {media.mime_type}
\n"
+ )
+ output += (
+ f"{Symbols.diamond_2} File Size: {media.file_size}
\n"
+ )
+ output += f"{Symbols.diamond_2} Date: {media.date}
\n"
+ output += f"{Symbols.diamond_2} File Type: Animation
\n"
+ elif isinstance(media, Audio):
+ output += f"{Symbols.diamond_2} File ID: {media.file_id}
\n"
+ output += (
+ f"{Symbols.diamond_2} Duration: {media.duration}
\n"
+ )
+ output += (
+ f"{Symbols.diamond_2} Performer: {media.performer}
\n"
+ )
+ output += f"{Symbols.diamond_2} Title: {media.title}
\n"
+ output += (
+ f"{Symbols.diamond_2} File Name: {media.file_name}
\n"
+ )
+ output += (
+ f"{Symbols.diamond_2} Mime Type: {media.mime_type}
\n"
+ )
+ output += (
+ f"{Symbols.diamond_2} File Size: {media.file_size}
\n"
+ )
+ output += f"{Symbols.diamond_2} Date: {media.date}
\n"
+ output += f"{Symbols.diamond_2} File Type: Audio
\n"
+ elif isinstance(media, Document):
+ output += f"{Symbols.diamond_2} File ID: {media.file_id}
\n"
+ output += (
+ f"{Symbols.diamond_2} File Name: {media.file_name}
\n"
+ )
+ output += (
+ f"{Symbols.diamond_2} Mime Type: {media.mime_type}
\n"
+ )
+ output += (
+ f"{Symbols.diamond_2} File Size: {media.file_size}
\n"
+ )
+ output += f"{Symbols.diamond_2} Date: {media.date}
\n"
+ output += f"{Symbols.diamond_2} File Type: Document
\n"
+ elif isinstance(media, Photo):
+ output += f"{Symbols.diamond_2} File ID: {media.file_id}
\n"
+ output += f"{Symbols.diamond_2} Width: {media.width}
\n"
+ output += f"{Symbols.diamond_2} Height: {media.height}
\n"
+ output += f"{Symbols.diamond_2} File Name: photo.jpg
\n"
+ output += f"{Symbols.diamond_2} Mime Type: image/jpeg
\n"
+ output += (
+ f"{Symbols.diamond_2} File Size: {media.file_size}
\n"
+ )
+ output += f"{Symbols.diamond_2} Date: {media.date}
\n"
+ output += f"{Symbols.diamond_2} File Type: Photo
\n"
+ elif isinstance(media, Sticker):
+ output += f"{Symbols.diamond_2} File ID: {media.file_id}
\n"
+ output += f"{Symbols.diamond_2} Width: {media.width}
\n"
+ output += f"{Symbols.diamond_2} Height: {media.height}
\n"
+ output += (
+ f"{Symbols.diamond_2} File Name: {media.file_name}
\n"
+ )
+ output += (
+ f"{Symbols.diamond_2} Mime Type: {media.mime_type}
\n"
+ )
+ output += (
+ f"{Symbols.diamond_2} File Size: {media.file_size}
\n"
+ )
+ output += f"{Symbols.diamond_2} Date: {media.date}
\n"
+ output += f"{Symbols.diamond_2} Emoji: {media.emoji}
\n"
+ output += (
+ f"{Symbols.diamond_2} Set Name: {media.set_name}
\n"
+ )
+ output += f"{Symbols.diamond_2} File Type: Sticker
\n"
+ elif isinstance(media, Video):
+ output += f"{Symbols.diamond_2} File ID: {media.file_id}
\n"
+ output += f"{Symbols.diamond_2} Width: {media.width}
\n"
+ output += f"{Symbols.diamond_2} Height: {media.height}
\n"
+ output += (
+ f"{Symbols.diamond_2} Duration: {media.duration}
\n"
+ )
+ output += (
+ f"{Symbols.diamond_2} File Name: {media.file_name}
\n"
+ )
+ output += (
+ f"{Symbols.diamond_2} Mime Type: {media.mime_type}
\n"
+ )
+ output += (
+ f"{Symbols.diamond_2} File Size: {media.file_size}
\n"
+ )
+ output += f"{Symbols.diamond_2} Date: {media.date}
\n"
+ output += f"{Symbols.diamond_2} File Type: Video
\n"
+ else:
+ return None
+
+ return output
+
+
+def get_media_text_ocr(filename: str, api_key: str, language: str = "eng") -> dict:
+ payload = {
+ "isOverlayRequired": False,
+ "apikey": api_key,
+ "language": language,
+ }
+
+ with open(filename, "rb") as f:
+ r = requests.post(
+ "https://api.ocr.space/parse/image",
+ files={filename: f},
+ data=payload,
+ )
+
+ return r.json()
+
+
+async def upload_media(client: Client, chat_id: int, file: str) -> InputDocument:
+ media = await client.invoke(
+ UploadMedia(
+ peer=(await client.resolve_peer(chat_id)),
+ media=InputMediaUploadedDocument(
+ file=(await client.save_file(file)),
+ mime_type=client.guess_mime_type(file) or "application/zip",
+ attributes=[
+ DocumentAttributeFilename(file_name=os.path.basename(file))
+ ],
+ force_file=True,
+ ),
+ ),
+ )
+
+ return InputDocument(
+ id=media.document.id,
+ access_hash=media.document.access_hash,
+ file_reference=media.document.file_reference,
+ )
+
+
+async def get_media_from_id(file_id: str) -> InputDocument:
+ file = FileId.decode(file_id)
+
+ return InputDocument(
+ id=file.media_id,
+ access_hash=file.access_hash,
+ file_reference=file.file_reference,
+ )
+
+
+async def get_media_fileid(message: Message) -> str | None:
+ file_id = None
+ if message.photo:
+ file_id = message.photo.file_id
+ elif message.animation:
+ file_id = message.animation.file_id
+ elif message.audio:
+ file_id = message.audio.file_id
+ elif message.document:
+ file_id = message.document.file_id
+ elif message.video:
+ file_id = message.video.file_id
+ elif message.sticker:
+ file_id = message.sticker.file_id
+ elif message.video_note:
+ file_id = message.video_note.file_id
+ elif message.voice:
+ file_id = message.voice.file_id
+ return file_id
diff --git a/Hellbot/functions/paste.py b/Hellbot/functions/paste.py
new file mode 100644
index 0000000000000000000000000000000000000000..1aec968c654c27746a2d454f982d3428abe7fa89
--- /dev/null
+++ b/Hellbot/functions/paste.py
@@ -0,0 +1,49 @@
+import uuid
+
+import requests
+
+from .utility import TGraph
+
+
+def post_to_telegraph(
+ title: str,
+ content: str,
+ author: str = "[ ๐ง๐พ๐
๐
๐ก๐๐ ]",
+ url: str = "https://t.me/Its_HellBot",
+) -> str:
+ content = content.replace("\n", "
")
+ try:
+ response = TGraph.telegraph.create_page(
+ title=title,
+ html_content=content,
+ author_name=author,
+ author_url=url,
+ )
+ except Exception:
+ rnd_key = uuid.uuid4().hex[:8]
+ title = f"{title}_{rnd_key}"
+ response = TGraph.telegraph.create_page(
+ title=title,
+ html_content=content,
+ author_name=author,
+ author_url=url,
+ )
+
+ return f"https://te.legra.ph/{response['path']}"
+
+
+def spaceBin(data: str, extension: str = "none") -> str:
+ data = {
+ "content": data,
+ "extension": extension,
+ }
+
+ resp = requests.post("https://spaceb.in/api/v1/documents/", data)
+
+ try:
+ result = resp.json()
+ url = f"https://spaceb.in/{result['payload']['id']}"
+ except Exception:
+ url = ""
+
+ return url
diff --git a/Hellbot/functions/scraping.py b/Hellbot/functions/scraping.py
new file mode 100644
index 0000000000000000000000000000000000000000..aa366a904e508654f451ad2dd634105bbe26eaa6
--- /dev/null
+++ b/Hellbot/functions/scraping.py
@@ -0,0 +1,528 @@
+import calendar
+import time
+
+import httpx
+from bs4 import BeautifulSoup
+from urllib.parse import quote, urlparse
+from Hellbot.core import Symbols
+
+from .paste import post_to_telegraph
+from .templates import (
+ airing_templates,
+ anilist_user_templates,
+ anime_template,
+ character_templates,
+ manga_templates,
+)
+
+anime_query = """query ($id: Int,$search: String) {
+ Page (perPage: 10) {
+ media (id: $id, type: ANIME,search: $search) {
+ id
+ title {
+ romaji
+ english
+ native
+ }
+ type
+ format
+ status
+ description (asHtml: false)
+ episodes
+ duration
+ countryOfOrigin
+ source
+ trailer{
+ id
+ site
+ }
+ genres
+ tags {
+ name
+ }
+ isAdult
+ averageScore
+ studios (isMain: true){
+ nodes{
+ name
+ }
+ }
+ nextAiringEpisode{
+ episode
+ }
+ siteUrl
+ }
+ }
+}"""
+
+
+manga_query = """query ($id: Int,$search: String) {
+ Page (perPage: 10) {
+ media (id: $id, type: MANGA,search: $search) {
+ id
+ title {
+ romaji
+ english
+ native
+ }
+ type
+ format
+ status
+ description (asHtml: false)
+ chapters
+ volumes
+ countryOfOrigin
+ source
+ genres
+ isAdult
+ averageScore
+ siteUrl
+ }
+ }
+}"""
+
+
+character_query = """query ($id: Int, $search: String) {
+ Page {
+ characters (id: $id, search: $search) {
+ id
+ name {
+ full
+ native
+ }
+ image {
+ large
+ }
+ description
+ gender
+ dateOfBirth {
+ year
+ month
+ day
+ }
+ age
+ bloodType
+ siteUrl
+ favourites
+ media {
+ nodes {
+ title {
+ romaji
+ english
+ native
+ }
+ type
+ format
+ siteUrl
+ }
+ }
+ }
+ }
+}"""
+
+
+airing_query = """query ($id: Int, $idMal:Int, $search: String) {
+ Media (id: $id, idMal: $idMal, search: $search, type: ANIME) {
+ id
+ title {
+ romaji
+ english
+ native
+ }
+ status
+ episodes
+ countryOfOrigin
+ nextAiringEpisode {
+ airingAt
+ timeUntilAiring
+ episode
+ }
+ }
+}"""
+
+
+anilist_user_query = """query($id: Int, $search: String) {
+ User(id: $id, name: $search) {
+ id
+ name
+ siteUrl
+ statistics {
+ anime {
+ count
+ meanScore
+ minutesWatched
+ episodesWatched
+ }
+ manga {
+ count
+ meanScore
+ chaptersRead
+ volumesRead
+ }
+ }
+ }
+}"""
+
+
+def is_valid_url(text: str) -> bool:
+ try:
+ result = urlparse(text)
+ return all([result.scheme, result.netloc])
+ except ValueError:
+ return False
+
+
+def post_request(query: str, search_term: str):
+ url = "https://graphql.anilist.co"
+ variables = {"search": search_term}
+ r = httpx.post(url, json={"query": query, "variables": variables})
+ if r.status_code != 200:
+ return None
+ return r.json()
+
+
+def get_country_flag(country: str) -> str:
+ base = ord('๐ฆ')
+ emoji_flag = "".join(chr(base + ord(letter) - ord("A")) for letter in country)
+ return emoji_flag
+
+
+def get_date(data: dict) -> str:
+ try:
+ year = data["year"] or ""
+ if not year and not data["month"]:
+ return "N/A"
+ day = data["day"]
+ if 10 <= day % 100 <= 20:
+ day = f"{day}th"
+ else:
+ day_dict = {1: "st", 2: "nd", 3: "rd"}
+ day = f"{day}{day_dict.get(day % 10, 'th')}"
+ month_name = calendar.month_name[int(data["month"])]
+ return f"{day} {month_name} {year}"
+ except:
+ return "N/A"
+
+
+def search_anime_filler(search_term: str):
+ BASE = "https://www.animefillerlist.com/shows/"
+
+ response = httpx.get(BASE).text
+ soup = BeautifulSoup(response, "html.parser")
+ div = soup.findAll("div", {"class": "Group"})
+
+ index = {}
+ for i in div:
+ li = i.findAll("li")
+ for j in li:
+ index[j.text] = j.a["href"].split("/")[-1]
+
+ results = {}
+ keys = list(index.keys())
+ for i in range(len(keys)):
+ if search_term.lower() in keys[i].lower():
+ results[keys[i]] = index[keys[i]]
+
+ for result in results.keys():
+ data = []
+ response = httpx.get(BASE + results[result]).text
+ soup = BeautifulSoup(response, "html.parser")
+ base_div = soup.find("div", {"id": "Condensed"})
+
+ if not base_div:
+ continue
+
+ divs = base_div.findAll("div")
+ for div in divs:
+ heading = div.find("span", {"class": "Label"}).text
+ episodes = div.find("span", {"class": "Episodes"}).text
+ data.append((heading, episodes))
+
+ yield result, data
+
+
+async def get_filler_info(search_term: str) -> str:
+ animes = search_anime_filler(search_term)
+ message = ""
+
+ for anime in animes:
+ html_message = f"{Symbols.check_mark} {anime[0]} Filler Guide:\n\n"
+
+ for data in anime[1]:
+ html_message += (
+ f"
{Symbols.bullet} {data[0]}:
\n{data[1]}
\n\n"
+ )
+
+ paste = post_to_telegraph(anime[0] + " Filler Guide", html_message)
+ message += f"{Symbols.anchor} [{anime[0]}]({paste})\n"
+
+ return message
+
+
+def search_watch_order(anime: str):
+ response = httpx.get(f"https://www.animechrono.com/search?q={quote(anime)}")
+ soup = BeautifulSoup(response.text, "html.parser")
+
+ item_div = soup.find("div", {"class": "search-result-items"})
+ animes = item_div.find_all("a", {"class": "list-item search w-inline-block"})
+
+ for anime in animes:
+ name = anime.find("h1", "h1 search").text
+ yield name, anime["href"]
+
+
+async def get_watch_order(search_term: str) -> str:
+ animes = search_watch_order(search_term)
+ message = ""
+
+ for anime in animes:
+ message += f"**{Symbols.anchor} {anime[0]}:** \n"
+ response = httpx.get("https://www.animechrono.com" + anime[1]).text
+ soup = BeautifulSoup(response, "html.parser")
+
+ elements = soup.find_all("h2", {"class": "heading-5"})
+ for element in elements:
+ message += f" {Symbols.bullet} `{element.text}`\n"
+ message += "\n"
+
+ return message
+
+
+async def get_anime_info(search_term: str) -> tuple[str, str]:
+ data = post_request(anime_query, search_term)
+ if not data:
+ return "", ""
+
+ data = data["data"]["Page"]["media"][0]
+ english_title = data["title"]["english"]
+ native_title = data["title"]["native"]
+ if not english_title:
+ english_title = data["title"]["romaji"]
+ flag = get_country_flag(data["countryOfOrigin"])
+ name = f"**[{flag}] {english_title} ({native_title})**"
+
+ anime_id = data["id"]
+ score = data["averageScore"] if data["averageScore"] else "N/A"
+ source = str(data["source"]).title() if data["source"] else "N/A"
+ mtype = str(data["type"]).title() if data["type"] else "N/A"
+ synopsis = data["description"]
+
+ episodes = data["episodes"]
+ if not episodes:
+ try:
+ episodes = data["nextAiringEpisode"]["episode"] - 1
+ except:
+ episodes = "N/A"
+
+ duration = data["duration"] if data["duration"] else "N/A"
+ status = str(data["status"]).title() if data["status"] else "N/A"
+ format = str(data["format"]).title() if data["format"] else "N/A"
+ genre = ", ".join(data["genres"]) if data["genres"] else "N/A"
+ tags = ", ".join([i["name"] for i in data["tags"][:5]]) if data["tags"] else "N/A"
+ studio = data["studios"]["nodes"][0]["name"] if data["studios"]["nodes"] else "N/A"
+ siteurl = f"[Anilist Website]({data['siteUrl']})" if data["siteUrl"] else "N/A"
+ isAdult = data["isAdult"]
+
+ trailer = "N/A"
+ if data["trailer"] and data["trailer"]["site"] == "youtube":
+ trailer = f"[Youtube](https://youtu.be/{data['trailer']['id']})"
+
+ response = httpx.get(f"https://img.anili.st/media/{anime_id}").content
+ banner = f"anime_{anime_id}.jpg"
+ with open(banner, "wb") as f:
+ f.write(response)
+
+ description = post_to_telegraph(name, synopsis)
+
+ message = await anime_template(
+ name=name,
+ score=score,
+ source=source,
+ mtype=mtype,
+ episodes=episodes,
+ duration=duration,
+ status=status,
+ format=format,
+ genre=genre,
+ studio=studio,
+ trailer=trailer,
+ siteurl=siteurl,
+ description=description,
+ tags=tags,
+ isAdult=isAdult,
+ )
+
+ return message, banner
+
+
+async def get_manga_info(search_term: str) -> tuple[str, str]:
+ data = post_request(manga_query, search_term)
+ if not data:
+ return "", ""
+
+ data = data["data"]["Page"]["media"][0]
+ english_title = data["title"]["english"]
+ native_title = data["title"]["native"]
+ if not english_title:
+ english_title = data["title"]["romaji"]
+ flag = get_country_flag(data["countryOfOrigin"])
+ name = f"**[{flag}] {english_title} ({native_title})**"
+
+ manga_id = data["id"]
+ score = data["averageScore"] if data["averageScore"] else "N/A"
+ source = str(data["source"]).title() if data["source"] else "N/A"
+ mtype = str(data["type"]).title() if data["type"] else "N/A"
+ synopsis = data["description"]
+
+ chapters = data["chapters"] if data["chapters"] else "N/A"
+ volumes = data["volumes"] if data["volumes"] else "N/A"
+ status = str(data["status"]).title() if data["status"] else "N/A"
+ format = str(data["format"]).title() if data["format"] else "N/A"
+ genre = ", ".join(data["genres"]) if data["genres"] else "N/A"
+ siteurl = f"[Anilist Website]({data['siteUrl']})" if data["siteUrl"] else "N/A"
+ isAdult = data["isAdult"]
+
+ response = httpx.get(f"https://img.anili.st/media/{manga_id}").content
+ banner = f"manga_{manga_id}.jpg"
+ with open(banner, "wb") as f:
+ f.write(response)
+
+ description = post_to_telegraph(name, synopsis)
+
+ message = await manga_templates(
+ name=name,
+ score=score,
+ source=source,
+ mtype=mtype,
+ chapters=chapters,
+ volumes=volumes,
+ status=status,
+ format=format,
+ genre=genre,
+ siteurl=siteurl,
+ description=description,
+ isAdult=isAdult,
+ )
+
+ return message, banner
+
+
+async def get_character_info(search_term: str) -> tuple[str, str]:
+ data = post_request(character_query, search_term)
+ if not data:
+ return "", ""
+
+ data = data["data"]["Page"]["characters"][0]
+ name = f"**{data['name']['full']} ({data['name']['native']})**"
+ char_id = data["id"]
+
+ description = data["description"].split("\n\n", 1)[0]
+ gender = data["gender"] if data["gender"] else "N/A"
+ date_of_birth = get_date(data["dateOfBirth"])
+ age = data["age"] if data["age"] else "N/A"
+ blood_type = data["bloodType"] if data["bloodType"] else "N/A"
+ siteurl = f"[Anilist Website]({data['siteUrl']})" if data["siteUrl"] else "N/A"
+ favorites = data["favourites"] if data["favourites"] else "N/A"
+
+ cameo = data["media"]["nodes"][0] if data["media"]["nodes"] else {}
+ if cameo:
+ role_in = f"\nโฐโข **๐ฑ๐๐
๐พ ๐จ๐:** [{cameo['title']['romaji']}]({cameo['siteUrl']})"
+ else:
+ role_in = ""
+
+ response = httpx.get(data["image"]["large"]).content
+ banner = f"character_{char_id}.jpg"
+ with open(banner, "wb") as f:
+ f.write(response)
+
+ message = await character_templates(
+ name=name,
+ gender=gender,
+ date_of_birth=date_of_birth,
+ age=age,
+ blood_type=blood_type,
+ favorites=favorites,
+ siteurl=siteurl,
+ role_in=role_in,
+ description=description,
+ )
+
+ return message, banner
+
+
+async def get_airing_info(search_term: str) -> tuple[str, str]:
+ data = post_request(airing_query, search_term)
+ if not data:
+ return "", ""
+
+ data = data["data"]["Media"]
+ english_title = data["title"]["english"]
+ native_title = data["title"]["native"]
+ if not english_title:
+ english_title = data["title"]["romaji"]
+ flag = get_country_flag(data["countryOfOrigin"])
+ name = f"**[{flag}] {english_title} ({native_title})**"
+
+ episode = data["episodes"]
+ if not episode:
+ try:
+ episode = data["nextAiringEpisode"]["episode"]
+ except:
+ episode = "N/A"
+
+ response = httpx.get(f"https://img.anili.st/media/{data['id']}").content
+ banner = f"airing_{data['id']}.jpg"
+ with open(banner, "wb") as f:
+ f.write(response)
+
+ status = str(data["status"]).title() if data["status"] else "N/A"
+ next_date = data["nextAiringEpisode"]["airingAt"] if data["nextAiringEpisode"] else ""
+
+ airing_info = ""
+ if next_date:
+ airing_info = f"\n**๐๏ธ {time.ctime(next_date)}**"
+
+ message = await airing_templates(
+ name=name,
+ status=status,
+ episode=episode,
+ airing_info=airing_info,
+ )
+
+ return message, banner
+
+
+async def get_anilist_user_info(search_term: str) -> tuple[str, str]:
+ data = post_request(anilist_user_query, search_term)
+ if not data:
+ return "", ""
+
+ data = data["data"]["User"]
+ user_id = data["id"]
+ name = data['name']
+ siteurl = f"[Anilist Website]({data['siteUrl']})" if data["siteUrl"] else "N/A"
+
+ response = httpx.get(f"https://img.anili.st/user/{user_id}").content
+ banner = f"aniuser_{user_id}.jpg"
+ with open(banner, "wb") as f:
+ f.write(response)
+
+ anime_stats = data["statistics"]["anime"]
+ manga_stats = data["statistics"]["manga"]
+
+ anime = (
+ anime_stats["count"] or 0,
+ anime_stats["meanScore"] or 0,
+ anime_stats["minutesWatched"] or 0,
+ anime_stats["episodesWatched"] or 0,
+ )
+ manga = (
+ manga_stats["count"] or 0,
+ manga_stats["meanScore"] or 0,
+ manga_stats["chaptersRead"] or 0,
+ manga_stats["volumesRead"] or 0,
+ )
+
+ message = await anilist_user_templates(name, anime, manga, siteurl)
+
+ return message, banner
diff --git a/Hellbot/functions/sticker.py b/Hellbot/functions/sticker.py
new file mode 100644
index 0000000000000000000000000000000000000000..3e37e531eef76cf994b1748c319cdbc19d1868d8
--- /dev/null
+++ b/Hellbot/functions/sticker.py
@@ -0,0 +1,135 @@
+from typing import Tuple
+
+from emoji import EMOJI_DATA
+from pyrogram import Client
+from pyrogram.raw.functions.messages import GetStickerSet
+from pyrogram.raw.functions.stickers import AddStickerToSet, CreateStickerSet, RemoveStickerFromSet
+from pyrogram.raw import base, types
+from pyrogram.types import Message
+
+from .media import get_media_from_id, upload_media
+
+
+def is_emoji(text: str) -> bool:
+ return any(c in EMOJI_DATA for c in text)
+
+
+def get_emoji_and_id(message: Message) -> Tuple[int, str]:
+ pack_id = None
+ pack_emoji = None
+
+ for command in message.command:
+ if command.isdigit():
+ pack_id = int(command)
+ elif is_emoji(command):
+ pack_emoji = command
+
+ if pack_id is None:
+ pack_id = 1
+
+ if pack_emoji is None:
+ sticker = message.reply_to_message.sticker
+ try:
+ pack_emoji = sticker.emoji if sticker and sticker.emoji else "๐"
+ except:
+ pack_emoji = "๐"
+
+ return pack_id, pack_emoji
+
+
+def check_sticker_data(replied: Message) -> Tuple[str | None, bool, bool, bool, int]:
+ pack_type = None
+ is_animated = False
+ is_video = False
+ is_static = False
+ pack_limit = 50
+
+ if replied.sticker:
+ if replied.sticker.is_animated:
+ pack_type, is_animated = "animated", True
+ elif replied.sticker.is_video:
+ pack_type, is_video = "video", True
+ else:
+ pack_type, is_static, pack_limit = "static", True, 120
+
+ elif replied.photo:
+ pack_type, is_static, pack_limit = "static", True, 120
+
+ elif replied.video or replied.animation:
+ pack_type, is_video = "video", True
+
+ elif replied.document:
+ mime_type = replied.document.mime_type.lower()
+ if mime_type.startswith("video/"):
+ pack_type, is_video = "video", True
+ elif mime_type.startswith("image/"):
+ pack_type, is_static, pack_limit = "static", True, 120
+ elif mime_type in ["application/x-tgsticker", "application/x-bad-tgsticker"]:
+ pack_type, is_animated = "animated", True
+
+ return pack_type, is_animated, is_video, is_static, pack_limit
+
+
+async def create_sticker(
+ client: Client,
+ chat_id: int,
+ file: str,
+ emoji: str,
+) -> types.InputStickerSetItem:
+ sticker = await upload_media(client, chat_id, file)
+
+ return types.InputStickerSetItem(
+ document=sticker,
+ emoji=emoji,
+ )
+
+
+async def remove_sticker(client: Client, stickerid: str) -> base.messages.StickerSet:
+ sticker = await get_media_from_id(stickerid)
+ return await client.invoke(RemoveStickerFromSet(sticker=sticker))
+
+
+async def get_sticker_set(client: Client, name: str) -> base.messages.StickerSet | None:
+ try:
+ return await client.invoke(
+ GetStickerSet(
+ stickerset=types.InputStickerSetShortName(short_name=name),
+ hash=0,
+ )
+ )
+ except:
+ return None
+
+
+async def add_sticker(
+ client: Client,
+ stickerset: base.messages.StickerSet,
+ sticker: base.InputStickerSetItem,
+) -> base.messages.StickerSet:
+ return await client.invoke(
+ AddStickerToSet(
+ stickerset=types.InputStickerSetShortName(short_name=stickerset.set.short_name),
+ sticker=sticker,
+ )
+ )
+
+
+async def new_sticker_set(
+ client: Client,
+ user_id: int,
+ title: str,
+ short_name: str,
+ stickers: list[base.InputStickerSetItem],
+ animated: bool,
+ video: bool,
+) -> base.messages.StickerSet:
+ return await client.invoke(
+ CreateStickerSet(
+ user_id=(await client.resolve_peer(user_id)),
+ title=title,
+ short_name=short_name,
+ stickers=stickers,
+ animated=animated,
+ videos=video,
+ )
+ )
diff --git a/Hellbot/functions/templates.py b/Hellbot/functions/templates.py
new file mode 100644
index 0000000000000000000000000000000000000000..e9e89aa060a1c7c0592ac9fd9695281e2c942a40
--- /dev/null
+++ b/Hellbot/functions/templates.py
@@ -0,0 +1,466 @@
+import random
+
+from Hellbot import __version__
+from Hellbot.core import ENV, db
+
+ALIVE_TEMPLATES = [
+ (
+ "โขโโโโโโโโโโโโโโโโโข\n"
+ "โข ๐แดสส๐แดแด ๐s ๐สษชแด แด โข\n"
+ "โญโโโโโโโโโโโโโโโโโข\n"
+ "โฐโข แดแดกษดแดส ยป {owner}\n"
+ "โฐโข แดสสแดษขสแดแด ยป {pyrogram}\n"
+ "โฐโข สแดสสสแดแด ยป {hellbot}\n"
+ "โฐโข แดสแดสแดษด ยป {python}\n"
+ "โฐโข แดแดแดษชแดแด ยป {uptime}\n"
+ "โฐโโโโโโโโโโโโโโโโโข\n"
+ "๐ก๐ ยฉ @HellBot_Networks\n"
+ "โขโโโโโโโโโโโโโโโโโข\n"
+ ),
+]
+
+PING_TEMPLATES = [
+ """**๐ ๐ฏ๐๐๐!**
+
+ โ **ัฯััโ:** {speed} m/s
+ โ **ฯ
ฯัฮนะผั:** {uptime}
+ โ **ฯฯฮทัั:** {owner}""",
+]
+
+HELP_MENU_TEMPLATES = [
+ """**๐ ๐ง๐พ๐
๐ ๐ฌ๐พ๐๐ ๐ฟ๐๐:** {owner}
+
+__๐ ๐ซ๐๐บ๐ฝ๐พ๐ฝ__ **{plugins} ๐๐
๐๐๐๐๐** __๐๐๐๐ ๐บ ๐๐๐๐บ๐
๐๐ฟ__ **{commands} ๐ผ๐๐๐๐บ๐๐ฝ๐.**
+
+**๐ Page:** __{current}/{last}__""",
+]
+
+COMMAND_MENU_TEMPLATES = [
+ """**๐ฏ๐
๐๐๐๐ ๐ฅ๐๐
๐พ:** `{file}`
+**๐ฏ๐
๐๐๐๐ ๐จ๐๐ฟ๐:** __{info} ๐__
+
+**๐ ๐ซ๐๐บ๐ฝ๐พ๐ฝ ๐ข๐๐๐๐บ๐๐ฝ๐:** `{commands}`""",
+]
+
+ANIME_TEMPLATES = [
+ """
+{name}
+
+โญโโโโโโโโโโโโโโโโโข
+โฐโข **๐ฒ๐ผ๐๐๐พ:** `{score}`
+โฐโข **๐ฒ๐๐๐๐ผ๐พ:** `{source}`
+โฐโข **๐ณ๐๐๐พ:** `{mtype}`
+โฐโข **๐ค๐๐๐๐๐ฝ๐พ๐:** `{episodes}`
+โฐโข **๐ฃ๐๐๐บ๐๐๐๐:** `{duration} minutes`
+โฐโข **๐ฒ๐๐บ๐๐๐:** `{status}`
+โฐโข **๐ฅ๐๐๐๐บ๐:** `{format}`
+โฐโข **๐ฆ๐พ๐๐๐พ:** `{genre}`
+โฐโข **๐ณ๐บ๐๐:** `{tags}`
+โฐโข **๐ ๐ฝ๐๐
๐ ๐ฑ๐บ๐๐พ๐ฝ:** `{isAdult}`
+โฐโข **๐ฒ๐๐๐ฝ๐๐:** `{studio}`
+โฐโข **๐ณ๐๐บ๐๐
๐พ๐:** {trailer}
+โฐโข **๐ถ๐พ๐ป๐๐๐๐พ:** {siteurl}
+โฐโข **๐ฒ๐๐๐๐๐๐๐:** [๐ข๐
๐๐ผ๐ ๐ง๐พ๐๐พ]({description})
+โฐโโโโโโโโโโโโโโโโโข
+"""
+]
+
+MANGA_TEMPLATES = [
+ """
+{name}
+
+โญโโโโโโโโโโโโโโโโโข
+โฐโข **๐ฒ๐ผ๐๐๐พ:** `{score}`
+โฐโข **๐ฒ๐๐๐๐ผ๐พ:** `{source}`
+โฐโข **๐ณ๐๐๐พ:** `{mtype}`
+โฐโข **๐ข๐๐บ๐๐๐พ๐๐:** `{chapters}`
+โฐโข **๐ต๐๐
๐๐๐พ๐:** `{volumes}`
+โฐโข **๐ฒ๐๐บ๐๐๐:** `{status}`
+โฐโข **๐ฅ๐๐๐๐บ๐:** `{format}`
+โฐโข **๐ฆ๐พ๐๐๐พ:** `{genre}`
+โฐโข **๐ ๐ฝ๐๐
๐ ๐ฑ๐บ๐๐พ๐ฝ:** `{isAdult}`
+โฐโข **๐ถ๐พ๐ป๐๐๐๐พ:** {siteurl}
+โฐโข **๐ฒ๐๐๐๐๐๐๐:** [๐ข๐
๐๐ผ๐ ๐ง๐พ๐๐พ]({description})
+โฐโโโโโโโโโโโโโโโโโข
+"""
+]
+
+CHARACTER_TEMPLATES = [
+ """
+{name}
+
+โญโโโโโโโโโโโโโโโโโข
+โฐโข **๐ฆ๐พ๐๐ฝ๐พ๐:** `{gender}`
+โฐโข **๐ฃ๐บ๐๐พ ๐๐ฟ ๐ก๐๐๐๐:** `{date_of_birth}`
+โฐโข **๐ ๐๐พ:** `{age}`
+โฐโข **๐ก๐
๐๐๐ฝ ๐ณ๐๐๐พ:** `{blood_type}`
+โฐโข **๐ฅ๐บ๐๐๐๐๐๐๐พ๐:** `{favorites}`
+โฐโข **๐ถ๐พ๐ป๐๐๐๐พ:** {siteurl}{role_in}
+โฐโโโโโโโโโโโโโโโโโข
+{description}
+"""
+]
+
+AIRING_TEMPLATES = [
+ """
+{name}
+
+โญโโโโโโโโโโโโโโโโโข
+โฐโข **๐ฒ๐๐บ๐๐๐:** `{status}`
+โฐโข **๐ค๐๐๐๐๐ฝ๐พ:** `{episode}`
+โฐโโโโโโโโโโโโโโโโโข{airing_info}
+"""
+]
+
+ANILIST_USER_TEMPLATES = [
+ """
+**๐ซ {name}**
+
+โญโโโโ ๐ ๐๐๐๐พ โโโโโโข
+โฐโข **๐ข๐๐๐๐:** `{anime_count}`
+โฐโข **๐ฒ๐ผ๐๐๐พ:** `{anime_score}`
+โฐโข **๐ฌ๐๐๐๐๐พ๐ ๐ฒ๐๐พ๐๐:** `{minutes}`
+โฐโข **๐ค๐๐๐๐๐ฝ๐พ๐ ๐ถ๐บ๐๐ผ๐๐พ๐ฝ:** `{episodes}`
+โฐโโโโโโโโโโโโโโโโโข
+โญโโโโ ๐ฌ๐บ๐๐๐บ โโโโโโข
+โฐโข **๐ข๐๐๐๐:** `{manga_count}`
+โฐโข **๐ฒ๐ผ๐๐๐พ:** `{manga_score}`
+โฐโข **๐ข๐๐บ๐๐๐พ๐๐:** `{chapters}`
+โฐโข **๐ต๐๐
๐๐๐พ๐:** `{volumes}`
+โฐโโโโโโโโโโโโโโโโโข
+
+๐ถ๐พ๐ป๐๐๐๐พ: {siteurl}
+"""
+]
+
+CLIMATE_TEMPLATES = [
+ """
+๐ {city_name}, {country}
+
+โญโโโโโโโโโโโโโโโโโข
+โฐโข **๐ถ๐พ๐บ๐๐๐พ๐:** {weather}
+โฐโข **๐ณ๐๐๐พ๐๐๐๐พ:** {timezone}
+โฐโข **๐ฒ๐๐๐๐๐๐พ:** {sunrise}
+โฐโข **๐ฒ๐๐๐๐พ๐:** {sunset}
+โฐโข **๐ถ๐๐๐ฝ:** {wind}
+โฐโข **๐ณ๐พ๐๐๐พ๐๐บ๐๐๐๐พ:** {temperature}ยฐC
+โฐโข **๐ฅ๐พ๐พ๐
๐ ๐
๐๐๐พ:** {feels_like}ยฐC
+โฐโข **๐ฌ๐๐๐๐๐๐:** {temp_min}ยฐC
+โฐโข **๐ฌ๐บ๐๐๐๐๐:** {temp_max}ยฐC
+โฐโข **๐ฏ๐๐พ๐๐๐๐๐พ:** {pressure} hPa
+โฐโข **๐ง๐๐๐๐ฝ๐๐๐:** {humidity}%
+โฐโข **๐ต๐๐๐๐ป๐๐
๐๐๐:** {visibility} m
+โฐโข **๐ข๐
๐๐๐ฝ๐:** {clouds}%
+โฐโโโโโโโโโโโโโโโโโข
+"""
+]
+
+AIR_POLLUTION_TEMPLATES = [
+ """
+๐ {city_name}
+
+โญโโโโโโโโโโโโโโโโโข
+โฐโข **๐ ๐ฐ๐จ:** {aqi}
+โฐโข **๐ข๐บ๐๐ป๐๐ ๐ฌ๐๐๐๐๐๐ฝ๐พ:** {co}
+โฐโข **๐ญ๐๐๐๐๐๐๐พ๐ ๐ฌ๐๐๐๐๐๐ฝ๐พ:** {no}
+โฐโข **๐ญ๐๐๐๐๐๐พ๐ ๐ฃ๐๐๐๐๐ฝ๐พ:** {no2}
+โฐโข **๐ฎ๐๐๐๐พ:** {o3}
+โฐโข **๐ฒ๐๐
๐๐๐๐ ๐ฃ๐๐๐๐๐ฝ๐พ:** {so2}
+โฐโข **๐ ๐๐๐๐๐๐บ:** {nh3}
+โฐโข **๐ฅ๐๐๐พ ๐ฏ๐บ๐๐๐๐ผ๐
๐พ๐ (PM{sub2_5}):** {pm2_5}
+โฐโข **๐ข๐๐บ๐๐๐พ ๐ฏ๐บ๐๐๐๐ผ๐
๐พ๐ (PM{sub10}):** {pm10}
+โฐโโโโโโโโโโโโโโโโโข
+"""
+]
+
+GITHUB_USER_TEMPLATES = [
+ """
+๐ {username} ({git_id})
+
+โญโโโโโโโโ {id_type} โโโโโโโโโข
+โฐโข **๐ญ๐บ๐๐พ:** [{name}]({profile_url})
+โฐโข **๐ก๐
๐๐:** {blog}
+โฐโข **๐ข๐๐๐๐บ๐๐:** {company}
+โฐโข **๐ค๐๐บ๐๐
:** {email}
+โฐโข **๐ซ๐๐ผ๐บ๐๐๐๐:** {location}
+โฐโข **๐ฑ๐พ๐๐:** {public_repos}
+โฐโข **๐ฆ๐๐๐๐:** {public_gists}
+โฐโข **๐ฅ๐๐
๐
๐๐๐พ๐๐:** {followers}
+โฐโข **๐ฅ๐๐
๐
๐๐๐๐๐:** {following}
+โฐโข **๐ ๐ผ๐ผ๐๐๐๐ ๐ผ๐๐พ๐บ๐๐พ๐ฝ:** {created_at}
+โฐโโโโโโโโโโโโโโโโโข
+
+**๐ซ ๐ก๐๐:** {bio}
+"""
+]
+
+STATISTICS_TEMPLATES = [
+ """
+๐ {name}
+
+โญโโโโโโโโ ๐ข๐๐บ๐๐๐พ๐
๐ โโโโโโโโโข
+โฐโข **๐ณ๐๐๐บ๐
:** `{channels}`
+โฐโข **๐ ๐ฝ๐๐๐:** `{ch_admin}`
+โฐโข **๐ฎ๐๐๐พ๐:** `{ch_owner}`
+
+โญโโโโโโโโ ๐ฆ๐๐๐๐๐ โโโโโโโโโข
+โฐโข **๐ณ๐๐๐บ๐
:** `{groups}`
+โฐโข **๐ ๐ฝ๐๐๐:** `{gc_admin}`
+โฐโข **๐ฎ๐๐๐พ๐:** `{gc_owner}`
+
+โญโโโโโโโโ ๐ฎ๐๐๐พ๐๐ โโโโโโโโโข
+โฐโข **๐ฏ๐๐๐๐บ๐๐พ:** `{users}`
+โฐโข **๐ก๐๐๐:** `{bots}`
+โฐโข **๐ด๐๐๐พ๐บ๐ฝ ๐ฌ๐พ๐๐๐บ๐๐พ๐:** `{unread_msg}`
+โฐโข **๐ด๐๐๐พ๐บ๐ฝ ๐ฌ๐พ๐๐๐๐๐๐:** `{unread_mention}`
+
+โ **๐ณ๐๐๐พ ๐ณ๐บ๐๐พ๐:** `{time_taken}`
+"""
+]
+
+GBAN_TEMPLATES = [
+ """
+โญโโโโโโโโ {gtype} โโโโโโโโโข
+โฐโข **๐ต๐๐ผ๐๐๐:** {name}
+โฐโข **๐ฒ๐๐ผ๐ผ๐พ๐๐:** {success}
+โฐโข **๐ฅ๐บ๐๐
๐พ๐ฝ:** {failed}
+โฐโข **๐ฑ๐พ๐บ๐๐๐:** {reason}
+โฐโโโโโโโโโโโโโโโโโข
+"""
+]
+
+USAGE_TEMPLATES = [
+ """
+**๐ ๐ฃ๐๐๐ & ๐ฃ๐๐๐ ๐ด๐๐บ๐๐พ:**
+
+**โข ๐ฃ๐๐๐ ๐ด๐๐บ๐๐พ ๐ฟ๐๐** `{appName}`
+ โ __{appHours}hrs {appMinutes}mins__ | __{appPercentage}%__
+
+**โข ๐ฃ๐๐๐ ๐๐พ๐๐บ๐๐๐๐๐ ๐๐๐๐ ๐๐๐๐๐:**
+ โ __{hours}hrs {minutes}mins__ | __{percentage}%__
+
+**โข ๐ฃ๐๐๐ ๐ด๐๐บ๐๐พ:**
+ โ __{diskUsed}GB__ / __{diskTotal}GB__ | __{diskPercent}%__
+
+**โข ๐ฌ๐พ๐๐๐๐ ๐ด๐๐บ๐๐พ:**
+ โ __{memoryUsed}GB__ / __{memoryTotal}GB__ | __{memoryPercent}%__
+"""
+]
+
+USER_INFO_TEMPLATES = [
+ """
+**๐ ๐ด๐๐พ๐ ๐จ๐๐ฟ๐ ๐๐ฟ {mention}:**
+
+**โข ๐ฅ๐๐๐๐ ๐ญ๐บ๐๐พ:** `{firstName}`
+**โข ๐ซ๐บ๐๐ ๐ญ๐บ๐๐พ:** `{lastName}`
+**โข ๐ด๐๐พ๐๐จ๐ฃ:** `{userId}`
+
+**โข ๐ข๐๐๐๐๐ ๐ฆ๐๐๐๐๐:** `{commonGroups}`
+**โข ๐ฃ๐ข-๐จ๐ฃ:** `{dcId}`
+**โข ๐ฏ๐๐ผ๐๐๐๐พ๐:** `{totalPictures}`
+**โข ๐ฑ๐พ๐๐๐๐๐ผ๐๐พ๐ฝ:** `{isRestricted}`
+**โข ๐ต๐พ๐๐๐ฟ๐๐พ๐ฝ:** `{isVerified}`
+**โข ๐ก๐๐:** `{isBot}`
+**โข ๐ก๐๐:** `{bio}`
+
+**> @HellBot_Networks**
+"""
+]
+
+CHAT_INFO_TEMPLATES = [
+ """
+**๐ ๐ข๐๐บ๐ ๐จ๐๐ฟ๐:**
+
+**โข ๐ข๐๐บ๐ ๐ญ๐บ๐๐พ:** `{chatName}`
+**โข ๐ข๐๐บ๐ ๐จ๐ฃ:** `{chatId}`
+**โข ๐ข๐๐บ๐ ๐ซ๐๐๐:** {chatLink}
+**โข ๐ฎ๐๐๐พ๐:** {chatOwner}
+**โข ๐ฃ๐ข-๐จ๐ฃ:** `{dcId}`
+**โข ๐ฌ๐พ๐๐ป๐พ๐๐:** `{membersCount}`
+**โข ๐ ๐ฝ๐๐๐๐:** `{adminsCount}`
+**โข ๐ก๐๐๐:** `{botsCount}`
+**โข ๐ฃ๐พ๐๐ผ๐๐๐๐๐๐๐:** `{description}`
+
+**> @HellBot_Networks**
+"""
+]
+
+
+async def alive_template(owner: str, uptime: str) -> str:
+ template = await db.get_env(ENV.alive_template)
+ if template:
+ message = template
+ else:
+ message = random.choice(ALIVE_TEMPLATES)
+ return message.format(
+ owner=owner,
+ pyrogram=__version__["pyrogram"],
+ hellbot=__version__["hellbot"],
+ python=__version__["python"],
+ uptime=uptime,
+ )
+
+
+async def ping_template(speed: float, uptime: str, owner: str) -> str:
+ template = await db.get_env(ENV.ping_template)
+ if template:
+ message = template
+ else:
+ message = random.choice(PING_TEMPLATES)
+ return message.format(speed=speed, uptime=uptime, owner=owner)
+
+
+async def help_template(
+ owner: str, cmd_n_plgn: tuple[int, int], page: tuple[int, int]
+) -> str:
+ template = await db.get_env(ENV.help_template)
+ if template:
+ message = template
+ else:
+ message = random.choice(HELP_MENU_TEMPLATES)
+ return message.format(
+ owner=owner,
+ commands=cmd_n_plgn[0],
+ plugins=cmd_n_plgn[1],
+ current=page[0],
+ last=page[1],
+ )
+
+
+async def command_template(file: str, info: str, commands: str) -> str:
+ template = await db.get_env(ENV.command_template)
+ if template:
+ message = template
+ else:
+ message = random.choice(COMMAND_MENU_TEMPLATES)
+ return message.format(file=file, info=info, commands=commands)
+
+
+async def anime_template(**kwargs) -> str:
+ template = await db.get_env(ENV.anime_template)
+ if template:
+ message = template
+ else:
+ message = random.choice(ANIME_TEMPLATES)
+ return message.format(**kwargs)
+
+
+async def manga_templates(**kwargs) -> str:
+ template = await db.get_env(ENV.manga_template)
+ if template:
+ message = template
+ else:
+ message = random.choice(MANGA_TEMPLATES)
+ return message.format(**kwargs)
+
+
+async def character_templates(**kwargs) -> str:
+ template = await db.get_env(ENV.character_template)
+ if template:
+ message = template
+ else:
+ message = random.choice(CHARACTER_TEMPLATES)
+ return message.format(**kwargs)
+
+
+async def airing_templates(**kwargs) -> str:
+ template = await db.get_env(ENV.airing_template)
+ if template:
+ message = template
+ else:
+ message = random.choice(AIRING_TEMPLATES)
+ return message.format(**kwargs)
+
+
+async def anilist_user_templates(
+ name: str, anime: tuple, manga: tuple, siteurl: str
+) -> str:
+ template = await db.get_env(ENV.anilist_user_template)
+ if template:
+ message = template
+ else:
+ message = random.choice(ANILIST_USER_TEMPLATES)
+ return message.format(
+ name=name,
+ anime_count=anime[0],
+ anime_score=anime[1],
+ minutes=anime[2],
+ episodes=anime[3],
+ manga_count=manga[0],
+ manga_score=manga[1],
+ chapters=manga[2],
+ volumes=manga[3],
+ siteurl=siteurl,
+ )
+
+
+async def climate_templates(**kwargs) -> str:
+ template = await db.get_env(ENV.climate_template)
+ if template:
+ message = template
+ else:
+ message = random.choice(CLIMATE_TEMPLATES)
+ return message.format(**kwargs)
+
+
+async def airpollution_templates(**kwargs) -> str:
+ template = await db.get_env(ENV.airpollution_template)
+ if template:
+ message = template
+ else:
+ message = random.choice(AIR_POLLUTION_TEMPLATES)
+ return message.format(**kwargs)
+
+
+async def statistics_templates(**kwargs) -> str:
+ template = await db.get_env(ENV.statistics_template)
+ if template:
+ message = template
+ else:
+ message = random.choice(STATISTICS_TEMPLATES)
+ return message.format(**kwargs)
+
+
+async def github_user_templates(**kwargs) -> str:
+ template = await db.get_env(ENV.github_user_template)
+ if template:
+ message = template
+ else:
+ message = random.choice(GITHUB_USER_TEMPLATES)
+ return message.format(**kwargs)
+
+
+async def gban_templates(**kwargs) -> str:
+ template = await db.get_env(ENV.gban_template)
+ if template:
+ message = template
+ else:
+ message = random.choice(GBAN_TEMPLATES)
+ return message.format(**kwargs)
+
+
+async def usage_templates(**kwargs) -> str:
+ template = await db.get_env(ENV.usage_template)
+ if template:
+ message = template
+ else:
+ message = random.choice(USAGE_TEMPLATES)
+ return message.format(**kwargs)
+
+
+async def user_info_templates(**kwargs) -> str:
+ template = await db.get_env(ENV.user_info_template)
+ if template:
+ message = template
+ else:
+ message = random.choice(USER_INFO_TEMPLATES)
+ return message.format(**kwargs)
+
+
+async def chat_info_templates(**kwargs) -> str:
+ template = await db.get_env(ENV.chat_info_template)
+ if template:
+ message = template
+ else:
+ message = random.choice(CHAT_INFO_TEMPLATES)
+ return message.format(**kwargs)
diff --git a/Hellbot/functions/tools.py b/Hellbot/functions/tools.py
new file mode 100644
index 0000000000000000000000000000000000000000..9c6ee11ff647797da7ab2e05ccd788a3a5f3f8ff
--- /dev/null
+++ b/Hellbot/functions/tools.py
@@ -0,0 +1,139 @@
+import asyncio
+import contextlib
+import math
+import os
+import shlex
+import shutil
+import time
+
+from git import Repo
+from git.exc import GitCommandError, InvalidGitRepositoryError, NoSuchPathError
+from pyrogram.types import Message
+
+from Hellbot.core import Config, Symbols
+
+from .formatter import humanbytes, readable_time
+
+
+async def progress(
+ current: int, total: int, message: Message, start: float, process: str
+):
+ now = time.time()
+ diff = now - start
+ if round(diff % 10.00) == 0 or current == total:
+ percentage = current * 100 / total
+ speed = current / diff
+ elapsed_time = round(diff) * 1000
+ complete_time = round((total - current) / speed) * 1000
+ estimated_total_time = elapsed_time + complete_time
+ progress_str = "**[{0}{1}] : {2}%\n**".format(
+ "".join(["โ" for i in range(math.floor(percentage / 10))]),
+ "".join(["โ" for i in range(10 - math.floor(percentage / 10))]),
+ round(percentage, 2),
+ )
+ msg = (
+ progress_str
+ + "__{0}__ **๐๐ฟ** __{1}__\n**๐ฒ๐๐พ๐พ๐ฝ:** __{2}/s__\n**๐ค๐ณ๐ :** __{3}__".format(
+ humanbytes(current),
+ humanbytes(total),
+ humanbytes(speed),
+ readable_time(estimated_total_time / 1000),
+ )
+ )
+ await message.edit_text(f"**{process} ...**\n\n{msg}")
+
+
+async def get_files_from_directory(directory: str) -> list:
+ all_files = []
+ for path, _, files in os.walk(directory):
+ for file in files:
+ all_files.append(os.path.join(path, file))
+ return all_files
+
+
+async def runcmd(cmd: str) -> tuple[str, str, int, int]:
+ args = shlex.split(cmd)
+ process = await asyncio.create_subprocess_exec(
+ *args, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE
+ )
+ stdout, stderr = await process.communicate()
+ return (
+ stdout.decode("utf-8", "replace").strip(),
+ stderr.decode("utf-8", "replace").strip(),
+ process.returncode,
+ process.pid,
+ )
+
+
+async def update_dotenv(key: str, value: str) -> None:
+ with open(".env", "r") as file:
+ data = file.readlines()
+
+ for index, line in enumerate(data):
+ if line.startswith(f"{key}="):
+ data[index] = f"{key}={value}\n"
+ break
+
+ with open(".env", "w") as file:
+ file.writelines(data)
+
+
+async def restart(
+ update: bool = False,
+ clean_up: bool = False,
+ shutdown: bool = False,
+):
+ try:
+ shutil.rmtree(Config.DWL_DIR)
+ shutil.rmtree(Config.TEMP_DIR)
+ except BaseException:
+ pass
+
+ if clean_up:
+ os.system(f"mkdir {Config.DWL_DIR}")
+ os.system(f"mkdir {Config.TEMP_DIR}")
+ return
+
+ if shutdown:
+ return os.system(f"kill -9 {os.getpid()}")
+
+ cmd = (
+ "git pull && pip3 install -U -r requirements.txt && bash start.sh"
+ if update
+ else "bash start.sh"
+ )
+
+ os.system(f"kill -9 {os.getpid()} && {cmd}")
+
+
+async def gen_changelogs(repo: Repo, branch: str) -> str:
+ changelogs = ""
+ commits = list(repo.iter_commits(branch))[:5]
+ for index, commit in enumerate(commits):
+ changelogs += f"**{Symbols.triangle_right} {index + 1}.** `{commit.summary}`\n"
+
+ return changelogs
+
+
+async def initialize_git(git_repo: str):
+ force = False
+ try:
+ repo = Repo()
+ except NoSuchPathError as pathErr:
+ repo.__del__()
+ return False, pathErr, force
+ except GitCommandError as gitErr:
+ repo.__del__()
+ return False, gitErr, force
+ except InvalidGitRepositoryError:
+ repo = Repo.init()
+ origin = repo.create_remote("upstream", f"https://github.com/{git_repo}")
+ origin.fetch()
+ repo.create_head("master", origin.refs.master)
+ repo.heads.master.set_tracking_branch(origin.refs.master)
+ repo.heads.master.checkout(True)
+ force = True
+ with contextlib.suppress(BaseException):
+ repo.create_remote("upstream", f"https://github.com/{git_repo}")
+
+ return True, repo, force
diff --git a/Hellbot/functions/utility.py b/Hellbot/functions/utility.py
new file mode 100644
index 0000000000000000000000000000000000000000..792ee116363e2c1aca578e00fa92383bb314fa3a
--- /dev/null
+++ b/Hellbot/functions/utility.py
@@ -0,0 +1,241 @@
+import asyncio
+import os
+import time
+
+from pyrogram import Client
+from pyrogram.enums import ChatType
+from pyrogram.errors import FloodWait
+from pyrogram.types import Message
+from telegraph import Telegraph
+
+from Hellbot.core import ENV, LOGS, db
+
+from .formatter import readable_time
+
+
+class TelegraphAPI:
+ def __init__(self) -> None:
+ self.shortname: str = "TheHellbot"
+ self.telegraph: Telegraph = None
+
+ async def setup(self):
+ shortname = await db.get_env(ENV.telegraph_account) or self.shortname
+
+ try:
+ self.telegraph = Telegraph(domain="telegra.ph")
+ self.telegraph.create_account(shortname)
+ except:
+ LOGS.warning("Failed to setup Telegraph API")
+
+
+class Gcast:
+ def __init__(self) -> None:
+ self.file_name = "gcast_{0}.txt"
+ self.complete_msg = "**๐ ๐ฆ๐ผ๐บ๐๐ ๐ข๐๐๐๐
๐พ๐๐พ๐ฝ!** \n\n**๐ฌ๐พ๐๐๐บ๐๐พ:** [click here]({0})\n**๐ข๐๐๐๐:** `{1} {2}`\n**๐ฅ๐๐๐๐บ๐๐ฝ ๐๐บ๐:** `{3}`\n**๐ณ๐๐๐พ ๐๐บ๐๐พ๐:** `{4}`"
+
+ async def _send_msg(self, chat_id: int, msg: Message, tag: bool):
+ await msg.forward(chat_id) if tag else await msg.copy(chat_id)
+
+ async def start(self, message: Message, client: Client, mode: str, tag: bool):
+ link = message.link
+ status = "Enabled" if tag else "Removed"
+ start = time.time()
+
+ if mode == "all":
+ uCount, uFileName = await self.users(message, client, tag)
+ gCount, gFileName = await self.groups(message, client, tag)
+ count = uCount + gCount
+ with open(uFileName, "a", encoding="utf-8") as file1, open(
+ gFileName, "r", encoding="utf-8"
+ ) as file2:
+ file1.write(file2.read())
+ file2.close()
+ file1.close()
+ os.remove(gFileName)
+ fileName = uFileName
+ elif mode == "groups":
+ count, fileName = await self.groups(message, client, tag)
+ elif mode == "users":
+ count, fileName = await self.users(message, client, tag)
+ else:
+ return None
+
+ end = time.time()
+ outStr = self.complete_msg.format(
+ link, count, mode, status, readable_time(int(end - start))
+ )
+
+ return fileName, outStr
+
+ async def groups(self, message: Message, client: Client, tag: bool):
+ filename = self.file_name.format(round(time.time()))
+ count = 0
+
+ with open(filename, "w", encoding="utf-8") as f:
+ f.write("Group ID | Error\n\n")
+ async for dialog in client.get_dialogs():
+ if dialog.chat.type == ChatType.SUPERGROUP:
+ try:
+ await self._send_msg(dialog.chat.id, message, tag)
+ count += 1
+ except FloodWait as fw:
+ await asyncio.sleep(fw.value)
+ await self._send_msg(dialog.chat.id, message, tag)
+ count += 1
+ except Exception as e:
+ f.write(f"{dialog.chat.id} | {e}\n")
+
+ f.close()
+
+ return count, filename
+
+ async def users(self, message: Message, client: Client, tag: bool):
+ filename = self.file_name.format(round(time.time()))
+ count = 0
+
+ with open(filename, "w", encoding="utf-8") as f:
+ f.write("User ID | Error\n\n")
+ async for dialog in client.get_dialogs():
+ if dialog.chat.type == ChatType.PRIVATE:
+ try:
+ await self._send_msg(dialog.chat.id, message, tag)
+ count += 1
+ except FloodWait as fw:
+ await asyncio.sleep(fw.value)
+ await self._send_msg(dialog.chat.id, message, tag)
+ count += 1
+ except Exception as e:
+ f.write(f"{dialog.chat.id} | {e}\n")
+
+ f.close()
+
+ return count, filename
+
+
+class AntiFlood:
+ def __init__(self) -> None:
+ self.FloodCount = {}
+ self.settings = {}
+ self.client_chats = {}
+
+ def updateSettings(self, client: int, chat: int, data: dict):
+ mode = data.get("mode", "mute")
+ mtime = data.get("time", 0)
+ limit = data.get("limit", 5)
+
+ self.settings[client] = {chat: {"mode": mode, "time": mtime, "limit": limit}}
+
+ def getSettings(self, client: int, chat: int) -> tuple[str, int, int]:
+ mode = "mute"
+ mtime = 0
+ limit = 5
+
+ cli_settings: dict = self.settings.get(client, None)
+ if cli_settings:
+ chat_settings: dict = cli_settings.get(chat, None)
+ if chat_settings:
+ mode = chat_settings.get("mode", "mute")
+ mtime = chat_settings.get("time", 0)
+ limit = chat_settings.get("limit", 5)
+
+ return mode, int(mtime), limit
+
+ def updateFlood(self, client: int, chat: int, user: int, count: int):
+ self.FloodCount[client] = {chat: {"last_user": user, "count": count}}
+
+ def getLastUser(self, client: int, chat: int) -> tuple[int, int]:
+ try:
+ cli_dict: dict = self.FloodCount[client]
+ except KeyError:
+ self.FloodCount[client] = {}
+ cli_dict: dict = self.FloodCount[client]
+
+ try:
+ chat_dict: dict = cli_dict[chat]
+ except KeyError:
+ cli_dict[chat] = {}
+ chat_dict: dict = cli_dict[chat]
+
+ last_user: int = chat_dict.get("last_user", 0)
+ count: int = chat_dict.get("count", 0)
+
+ return last_user, count
+
+ async def updateFromDB(self):
+ floods = await db.get_all_floods()
+ for flood in floods:
+ client = flood["client"]
+ chat = flood["chat"]
+ mode = flood.get("mode", "mute")
+ mtime = flood.get("time", 0)
+ limit = flood.get("limit", 5)
+ settings = {"mode": mode, "time": mtime, "limit": limit}
+
+ self.updateSettings(client, chat, settings)
+ try:
+ self.client_chats[client].append(chat)
+ except KeyError:
+ self.client_chats[client] = [chat]
+
+ def check_client_chat(self, client: int, chat: int) -> bool:
+ try:
+ chats = self.client_chats[client]
+ except KeyError:
+ return False
+
+ if chat in chats:
+ return True
+
+ return False
+
+
+class Blacklists:
+ def __init__(self) -> None:
+ self.blacklists = {}
+
+ async def updateBlacklists(self):
+ datas = await db.get_blacklist_clients()
+ for data in datas:
+ client = data["client"]
+ chats = data.get("chats", [])
+ for chat in chats:
+ blacklists = data["blacklist"]
+ self.blacklists[client] = {chat: blacklists}
+
+ async def addBlacklist(self, client: int, chat: int, text: str):
+ try:
+ self.blacklists[client][chat].append(text)
+ except KeyError:
+ self.blacklists[client] = {chat: [text]}
+
+ await db.add_blacklist(client, chat, text)
+
+ async def rmBlacklist(self, client: int, chat: int, text: str):
+ try:
+ self.blacklists[client][chat].remove(text)
+ except KeyError:
+ return
+
+ await db.rm_blacklist(client, chat, text)
+
+ def getBlacklists(self, client: int, chat: int) -> list:
+ try:
+ return self.blacklists[client][chat]
+ except KeyError:
+ return []
+
+ def check_client_chat(self, client: int, chat: int) -> bool:
+ try:
+ chats = self.blacklists[client]
+ except KeyError:
+ return False
+
+ if chat in chats:
+ return True
+
+ return False
+
+
+Flood = AntiFlood()
+BList = Blacklists()
+TGraph = TelegraphAPI()
diff --git a/Hellbot/plugins/__init__.py b/Hellbot/plugins/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/Hellbot/plugins/bot/__init__.py b/Hellbot/plugins/bot/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..d67965dd753207baec52c25d32835f8442c25c56
--- /dev/null
+++ b/Hellbot/plugins/bot/__init__.py
@@ -0,0 +1,29 @@
+from Hellbot.core.clients import hellbot
+from Hellbot.core.config import Config, Symbols
+from Hellbot.core.database import db
+from Hellbot.plugins.help import BotHelp
+
+
+START_MSG = """
+๐ **๐ฆ๐๐พ๐พ๐๐๐๐๐, {0} - ๐๐บ๐๐๐๐๐๐ ๐๐ฟ ๐ง๐พ๐
๐
๐ป๐๐!** ๐น ๐จ ๐บ๐ ๐๐๐๐ ๐๐๐๐๐๐ ๐ผ๐๐๐๐บ๐๐๐๐, ๐๐๐พ **๐ง๐พ๐
๐
๐ป๐๐ ๐ ๐๐๐๐๐๐บ๐๐!** ๐
+
+๐ง๐พ๐๐พ ๐๐ ๐๐พ๐๐๐พ, ๐๐๐๐ฝ๐พ, ๐บ๐๐ฝ ๐ช ๐๐๐
๐พ๐บ๐๐ ๐๐๐พ ๐๐๐๐พ๐ ๐๐ฟ ๐ง๐พ๐
๐
๐ก๐๐ ๐บ๐ ๐๐๐๐ ๐ผ๐๐๐๐บ๐๐ฝ! ๐
+๐ถ๐๐พ๐๐๐พ๐ ๐๐'๐ ๐ผ๐๐พ๐บ๐๐๐๐, ๐ฝ๐พ๐
๐พ๐๐๐๐, ๐๐ ๐๐๐ฝ๐บ๐๐๐๐ ๐๐๐๐ ๐๐๐พ๐๐ป๐๐, ๐จ'๐๐พ ๐๐๐ ๐๐๐๐ ๐ป๐บ๐ผ๐.
+๐ข๐๐๐๐๐ฝ๐พ๐ ๐๐พ ๐๐๐๐ ๐๐พ๐๐๐๐๐บ๐
๐๐๐ฝ๐พ๐๐๐ผ๐ ๐คญ ๐๐ ๐๐๐พ ๐๐พ๐บ๐
๐ ๐๐ฟ ๐๐
๐๐๐๐บ๐๐พ ๐๐๐พ๐๐ป๐๐ ๐๐บ๐๐๐พ๐๐.
+
+๐ ๐ซ๐พ๐'๐ ๐พ๐๐ป๐บ๐๐ ๐๐ ๐๐๐๐ ๐พ๐๐๐ผ ๐๐๐๐๐๐พ๐ ๐๐๐๐พ๐๐๐พ๐!
+๐จ๐ฟ ๐๐๐ ๐พ๐๐พ๐ ๐๐พ๐พ๐ฝ ๐บ๐๐๐๐๐๐บ๐๐ผ๐พ ๐๐ ๐ผ๐๐บ๐๐พ โจ ๐๐๐พ ๐๐๐๐๐
๐
๐๐ฟ ๐๐๐
๐พ๐บ๐๐๐๐๐ ๐ง๐พ๐
๐
๐ป๐๐'๐ ๐๐๐๐๐, ๐๐๐๐ ๐๐๐๐๐๐ ๐๐พ.
+๐ถ๐พ'๐๐พ ๐บ๐ป๐๐๐ ๐๐ ๐ผ๐๐๐๐๐พ๐ ๐๐พ๐ ๐๐พ๐๐๐๐๐ ๐ ๐๐ ๐๐๐พ ๐๐๐พ๐๐ป๐๐ ๐๐๐๐๐พ๐๐๐พ!
+
+๐ซ ๐ฌ๐บ๐ ๐๐๐๐ ๐ผ๐๐๐๐บ๐๐ฝ๐ ๐ป๐พ ๐๐๐๐ฟ๐ ๐บ๐๐ฝ ๐๐๐๐ ๐๐พ๐๐๐๐๐๐ ๐
๐พ๐๐พ๐๐ฝ๐บ๐๐.
+**๐ถ๐พ๐
๐ผ๐๐๐พ ๐๐ ๐ง๐พ๐
๐
๐ป๐๐ ๐ ๐๐๐๐๐๐บ๐๐ โ ๐๐๐พ๐๐พ ๐ง๐พ๐
๐
๐ป๐๐'๐ ๐
๐พ๐๐บ๐ผ๐ ๐
๐๐๐พ๐ ๐๐ ๐ค!**
+"""
+
+HELP_MSG = """
+**โ๏ธ ๐ง๐พ๐
๐:**
+
+__ยป All commands are categorized and you can use these buttons below to navigate each category and get respective commands.__
+__ยป Feel free to contact us if you need any help regarding the bot.__
+
+**โค๏ธ @HellBot_Networks ๐ฎ๐ณ**
+"""
diff --git a/Hellbot/plugins/bot/bot.py b/Hellbot/plugins/bot/bot.py
new file mode 100644
index 0000000000000000000000000000000000000000..43bb5fef68d923ef4ba479247d16e15b36fd6998
--- /dev/null
+++ b/Hellbot/plugins/bot/bot.py
@@ -0,0 +1,60 @@
+import heroku3
+from pyrogram import filters
+from pyrogram.types import InlineKeyboardMarkup, Message
+
+from Hellbot import HEROKU_APP
+from Hellbot.core import LOGS
+from Hellbot.functions.tools import restart
+
+from ..btnsG import gen_bot_help_buttons, start_button
+from . import HELP_MSG, START_MSG, BotHelp, Config, hellbot
+
+
+@hellbot.bot.on_message(filters.command("start") & Config.AUTH_USERS)
+async def start_pm(_, message: Message):
+ btns = start_button()
+
+ await message.reply_text(
+ START_MSG.format(message.from_user.mention),
+ disable_web_page_preview=True,
+ reply_markup=InlineKeyboardMarkup(btns),
+ )
+
+
+@hellbot.bot.on_message(filters.command("help") & Config.AUTH_USERS)
+async def help_pm(_, message: Message):
+ btns = await gen_bot_help_buttons()
+
+ await message.reply_text(
+ HELP_MSG,
+ disable_web_page_preview=True,
+ reply_markup=InlineKeyboardMarkup(btns),
+ )
+
+
+@hellbot.bot.on_message(filters.command("restart") & Config.AUTH_USERS)
+async def restart_clients(_, message: Message):
+ await message.reply_text("Restarted Bot Successfully โ
")
+ try:
+ if HEROKU_APP:
+ try:
+ heroku = heroku3.from_key(Config.HEROKU_APIKEY)
+ app = heroku.apps()[Config.HEROKU_APPNAME]
+ app.restart()
+ except:
+ await restart()
+ else:
+ await restart()
+ except Exception as e:
+ LOGS.error(e)
+
+
+BotHelp("Others").add(
+ "start", "To start the bot and get the main menu."
+).add(
+ "help", "To get the help menu with all the command for this assistant bot."
+).add(
+ "restart", "To restart the bot."
+).info(
+ "Some basic commands of the bot."
+).done()
diff --git a/Hellbot/plugins/bot/callbacks.py b/Hellbot/plugins/bot/callbacks.py
new file mode 100644
index 0000000000000000000000000000000000000000..6479fdb4847b64e73402f3ba7ea90cb9132d5f54
--- /dev/null
+++ b/Hellbot/plugins/bot/callbacks.py
@@ -0,0 +1,279 @@
+from pyrogram import filters
+from pyrogram.enums import ParseMode
+from pyrogram.types import CallbackQuery, InlineKeyboardButton, InlineKeyboardMarkup
+
+from Hellbot.functions.templates import command_template, help_template
+
+from ..btnsG import gen_bot_help_buttons, gen_inline_help_buttons, start_button
+from . import HELP_MSG, START_MSG, Config, Symbols, hellbot
+
+
+async def check_auth_click(cb: CallbackQuery) -> bool:
+ if cb.from_user.id not in Config.AUTH_USERS:
+ await cb.answer(
+ "You are not authorized to use this bot. \n\n> @Its_HellBot",
+ show_alert=True,
+ )
+ return False
+ return True
+
+
+@hellbot.bot.on_callback_query(filters.regex(r"auth_close"))
+async def auth_close_cb(_, cb: CallbackQuery):
+ if await check_auth_click(cb):
+ await cb.message.delete()
+
+
+@hellbot.bot.on_callback_query(filters.regex(r"close"))
+async def close_cb(_, cb: CallbackQuery):
+ await cb.message.delete()
+
+
+@hellbot.bot.on_callback_query(filters.regex(r"bot_help_menu"))
+async def bot_help_menu_cb(_, cb: CallbackQuery):
+ if not await check_auth_click(cb):
+ return
+
+ plugin = str(cb.data.split(":")[1])
+
+ try:
+ buttons = [
+ InlineKeyboardButton(f"{Symbols.bullet} {i}", f"bot_help_cmd:{plugin}:{i}")
+ for i in sorted(Config.BOT_HELP[plugin]["commands"])
+ ]
+ except KeyError:
+ await cb.answer("No description provided for this plugin!", show_alert=True)
+ return
+
+ buttons = [buttons[i : i + 2] for i in range(0, len(buttons), 2)]
+ buttons.append([InlineKeyboardButton(Symbols.back, "help_data:bothelp")])
+
+ caption = (
+ f"**๐ฏ๐
๐๐๐๐ ๐ฅ๐๐
๐พ:** `{plugin}`\n"
+ f"**๐ฏ๐
๐๐๐๐ ๐จ๐๐ฟ๐:** __{Config.BOT_HELP[plugin]['info']} ๐__\n\n"
+ f"**๐ ๐ซ๐๐บ๐ฝ๐พ๐ฝ ๐ข๐๐๐๐บ๐๐ฝ๐:** `{len(sorted(Config.BOT_HELP[plugin]['commands']))}`"
+ )
+
+ try:
+ await cb.edit_message_text(
+ caption,
+ disable_web_page_preview=True,
+ reply_markup=InlineKeyboardMarkup(buttons),
+ )
+ except Exception:
+ # handles MessageNotModified error
+ pass
+
+
+@hellbot.bot.on_callback_query(filters.regex(r"bot_help_cmd"))
+async def bot_help_cmd_cb(_, cb: CallbackQuery):
+ if not await check_auth_click(cb):
+ return
+
+ result = ""
+ plugin = str(cb.data.split(":")[1])
+ command = str(cb.data.split(":")[2])
+ cmd_dict = Config.BOT_HELP[plugin]["commands"][command]
+
+ result += f"**{Symbols.radio_select} ๐ข๐๐๐๐บ๐๐ฝ:** `/{cmd_dict['command']}`"
+ result += (
+ f"\n\n**{Symbols.arrow_right} ๐ฃ๐พ๐๐ผ๐๐๐๐๐๐๐:** __{cmd_dict['description']}__"
+ )
+ result += f"\n\n**<\\> @Its_HellBot ๐**"
+
+ buttons = [
+ [
+ InlineKeyboardButton(Symbols.back, f"bot_help_menu:{plugin}"),
+ InlineKeyboardButton(Symbols.close, "help_data:botclose"),
+ ]
+ ]
+
+ try:
+ await cb.edit_message_text(
+ result,
+ ParseMode.MARKDOWN,
+ True,
+ InlineKeyboardMarkup(buttons),
+ )
+ except Exception:
+ # handles MessageNotModified error
+ pass
+
+
+@hellbot.bot.on_callback_query(filters.regex(r"help_page"))
+async def help_page_cb(_, cb: CallbackQuery):
+ if not await check_auth_click(cb):
+ return
+
+ page = int(cb.data.split(":")[1])
+ buttons, max_page = await gen_inline_help_buttons(page, sorted(Config.CMD_MENU))
+
+ caption = await help_template(
+ cb.from_user.mention,
+ (len(Config.CMD_INFO), len(Config.CMD_MENU)),
+ (page + 1, max_page),
+ )
+
+ try:
+ await cb.edit_message_text(
+ caption,
+ reply_markup=InlineKeyboardMarkup(buttons),
+ )
+ except Exception:
+ # handles MessageNotModified error
+ pass
+
+
+@hellbot.bot.on_callback_query(filters.regex(r"help_menu"))
+async def help_menu_cb(_, cb: CallbackQuery):
+ if not await check_auth_click(cb):
+ return
+
+ page = int(cb.data.split(":")[1])
+ plugin = str(cb.data.split(":")[2])
+
+ try:
+ buttons = [
+ InlineKeyboardButton(
+ f"{Symbols.bullet} {i}", f"help_cmd:{page}:{plugin}:{i}"
+ )
+ for i in sorted(Config.HELP_DICT[plugin]["commands"])
+ ]
+ except KeyError:
+ await cb.answer("No description provided for this plugin!", show_alert=True)
+ return
+
+ buttons = [buttons[i : i + 2] for i in range(0, len(buttons), 2)]
+ buttons.append([InlineKeyboardButton(Symbols.back, f"help_page:{page}")])
+
+ caption = await command_template(
+ plugin,
+ Config.HELP_DICT[plugin]["info"],
+ len(sorted(Config.HELP_DICT[plugin]["commands"])),
+ )
+
+ try:
+ await cb.edit_message_text(
+ caption,
+ reply_markup=InlineKeyboardMarkup(buttons),
+ )
+ except Exception:
+ # handles MessageNotModified error
+ pass
+
+
+@hellbot.bot.on_callback_query(filters.regex(r"help_cmd"))
+async def help_cmd_cb(_, cb: CallbackQuery):
+ if not await check_auth_click(cb):
+ return
+
+ page = int(cb.data.split(":")[1])
+ plugin = str(cb.data.split(":")[2])
+ command = str(cb.data.split(":")[3])
+ result = ""
+ cmd_dict = Config.HELP_DICT[plugin]["commands"][command]
+
+ if cmd_dict["parameters"] is None:
+ result += f"**{Symbols.radio_select} ๐ข๐๐๐๐บ๐๐ฝ:** `{Config.HANDLERS[0]}{cmd_dict['command']}`"
+ else:
+ result += f"**{Symbols.radio_select} ๐ข๐๐๐๐บ๐๐ฝ:** `{Config.HANDLERS[0]}{cmd_dict['command']} {cmd_dict['parameters']}`"
+
+ if cmd_dict["description"]:
+ result += (
+ f"\n\n**{Symbols.arrow_right} ๐ฃ๐พ๐๐ผ๐๐๐๐๐๐๐:** __{cmd_dict['description']}__"
+ )
+
+ if cmd_dict["example"]:
+ result += f"\n\n**{Symbols.arrow_right} ๐ค๐๐บ๐๐๐
๐พ:** `{Config.HANDLERS[0]}{cmd_dict['example']}`"
+
+ if cmd_dict["note"]:
+ result += f"\n\n**{Symbols.arrow_right} ๐ญ๐๐๐พ:** __{cmd_dict['note']}__"
+
+ result += f"\n\n**<\\> @Its_HellBot ๐**"
+
+ buttons = [
+ [
+ InlineKeyboardButton(Symbols.back, f"help_menu:{page}:{plugin}"),
+ InlineKeyboardButton(Symbols.close, "help_data:c"),
+ ]
+ ]
+
+ try:
+ await cb.edit_message_text(
+ result,
+ ParseMode.MARKDOWN,
+ reply_markup=InlineKeyboardMarkup(buttons),
+ )
+ except Exception:
+ # handles MessageNotModified error
+ pass
+
+
+@hellbot.bot.on_callback_query(filters.regex(r"help_data"))
+async def help_close_cb(_, cb: CallbackQuery):
+ if not await check_auth_click(cb):
+ return
+
+ action = str(cb.data.split(":")[1])
+ if action == "c":
+ await cb.edit_message_text(
+ "**๐ง๐พ๐
๐ ๐ฌ๐พ๐๐ ๐ข๐
๐๐๐พ๐ฝ!**",
+ reply_markup=InlineKeyboardMarkup(
+ [[InlineKeyboardButton("Reopen", "help_data:reopen")]]
+ ),
+ )
+ elif action == "reopen":
+ buttons, pages = await gen_inline_help_buttons(0, sorted(Config.CMD_MENU))
+ caption = await help_template(
+ cb.from_user.mention,
+ (len(Config.CMD_INFO), len(Config.CMD_MENU)),
+ (1, pages),
+ )
+ await cb.edit_message_text(
+ caption,
+ reply_markup=InlineKeyboardMarkup(buttons),
+ )
+ elif action == "botclose":
+ await cb.message.delete()
+ elif action == "bothelp":
+ buttons = await gen_bot_help_buttons()
+ await cb.edit_message_text(
+ HELP_MSG,
+ disable_web_page_preview=True,
+ reply_markup=InlineKeyboardMarkup(buttons),
+ )
+ elif action == "source":
+ buttons = [
+ [
+ InlineKeyboardButton("๐ Deploy", url="https://github.com/The-HellBot/HellBot"),
+ InlineKeyboardButton("Plugins ๐", url="https://github.com/The-HellBot/Plugins"),
+ ],
+ [
+ InlineKeyboardButton("ะฝัโโะฒฯั ฮทััฯฯัะบ ๐ฎ๐ณ", url="https://t.me/HellBot_Networks"),
+ ],
+ [
+ InlineKeyboardButton("๐๏ธ Support", url="https://t.me/HellBot_Chats"),
+ InlineKeyboardButton("Updates ๐ฃ", url="https://t.me/Its_HellBot"),
+ ],
+ [
+ InlineKeyboardButton("๐", "help_data:start"),
+ InlineKeyboardButton(Symbols.close, "help_data:botclose"),
+ ],
+ ]
+ await cb.edit_message_text(
+ "__ยป The source code is available on GitHub. You can find the link below.__\n"
+ "__ยป Every project available under The-HellBot are open-source and free to use and modify to your needs.__\n"
+ "__ยป Anyone pretending to be the developer of this bot and selling the code, is a scammer.__\n\n"
+ "__ยป Please consider giving a star to the repository if you liked the project.__\n"
+ "__ยป Feel free to contact us if you need any help regarding the source code.__\n\n"
+ "**โค๏ธ @HellBot_Networks ๐ฎ๐ณ**",
+ disable_web_page_preview=True,
+ reply_markup=InlineKeyboardMarkup(buttons),
+ )
+ elif action == "start":
+ buttons = start_button()
+ await cb.edit_message_text(
+ START_MSG.format(cb.from_user.mention),
+ disable_web_page_preview=True,
+ reply_markup=InlineKeyboardMarkup(buttons),
+ )
diff --git a/Hellbot/plugins/bot/forcesub.py b/Hellbot/plugins/bot/forcesub.py
new file mode 100644
index 0000000000000000000000000000000000000000..ea4a60fd25ab0119a4277abee89ca3a0a5419d28
--- /dev/null
+++ b/Hellbot/plugins/bot/forcesub.py
@@ -0,0 +1,226 @@
+from pyrogram import Client, filters
+from pyrogram.errors import ChatAdminRequired, UserNotParticipant
+from pyrogram.types import (
+ CallbackQuery,
+ ChatPermissions,
+ InlineKeyboardButton,
+ InlineKeyboardMarkup,
+ Message,
+)
+
+from Hellbot.core import LOGS
+from Hellbot.functions.admins import is_user_admin
+
+from ..btnsG import gen_inline_keyboard
+from . import BotHelp, Config, Symbols, db, hellbot
+
+
+@hellbot.bot.on_message(filters.command("forcesub") & Config.AUTH_USERS & filters.group)
+async def force_sub(client: Client, message: Message):
+ if len(message.command) < 2:
+ return await message.reply_text("Give a channel username with command!")
+
+ try:
+ is_admin = await is_user_admin(message.chat, client.me.id)
+ if not is_admin:
+ return await message.reply_text(f"To use forcesub i must be an admin in {must_join}!")
+ except UserNotParticipant:
+ return await message.reply_text(f"To use forcesub i must be an admin in {must_join}!")
+
+ must_join = message.command[1]
+ try:
+ chat = await client.get_chat(must_join)
+ except Exception as e:
+ return await message.reply_text(f"**Error:**\n`{e}`")
+
+ if not await is_user_admin(chat, client.me.id):
+ return await message.reply_text("Make me admin in that channel first!")
+
+ await db.add_forcesub(message.chat.id, chat.id)
+ await message.reply_text(
+ f"**๐ ๐ข๐๐บ๐ ๐ฅ๐๐๐ผ๐พ๐๐๐ป ๐ค๐๐บ๐ป๐
๐พ๐ฝ!** \n\n"
+ f"__Users must join__ {chat.title} (`{chat.id}`) __to chat here!__"
+ )
+
+ if message.chat.id not in Config.FORCESUBS:
+ Config.FORCESUBS.add(message.chat.id)
+
+
+@hellbot.bot.on_message(filters.command("unforcesub") & Config.AUTH_USERS)
+async def unforce_sub(client: Client, message: Message):
+ if len(message.command) < 2:
+ return await message.reply_text(
+ "Give a channel username with command or give 'all' to remove all forcesubs from this chat!"
+ )
+
+ if not await is_user_admin(message.chat, client.me.id):
+ return await message.reply_text("To use forcesub i must be an admin!")
+
+ if "all" == message.command[1].lower():
+ await db.rm_all_forcesub(message.chat.id)
+ Config.FORCESUBS.remove(message.chat.id)
+ return await message.reply_text(f"**๐ Forcesub disabled!**")
+
+ try:
+ if await db.is_forcesub(message.chat.id, int(message.command[1])):
+ remaining = await db.rm_forcesub(message.chat.id, int(message.command[1]))
+ if remaining:
+ return await message.reply_text(
+ f"**๐ Removed Forcesub `{message.command[1]}`!**\n\n**Remaining Forcesub(s) in this chat:** `{remaining}`"
+ )
+ else:
+ Config.FORCESUBS.remove(message.chat.id)
+ return await message.reply_text(
+ f"**๐ Removed Forcesub `{message.command[1]}`!**"
+ )
+ else:
+ return await message.reply_text(f"**๐ This chat is not forcesub enabled!**")
+ except Exception as e:
+ return await message.reply_text(f"**Error:**\n`{e}`")
+
+
+@hellbot.bot.on_message(filters.command("listforcesub") & Config.AUTH_USERS)
+async def list_force_subs(client: Client, message: Message):
+ if not await is_user_admin(message.chat, client.me.id):
+ return await message.reply_text("To use forcesub i must be an admin!")
+
+ all_forcesubs = Config.FORCESUBS
+
+ text = ""
+ if len(all_forcesubs) > 0:
+ for forcesub in all_forcesubs:
+ try:
+ chat = await client.get_chat(forcesub["chat"])
+ text += f"**๐ {chat.title}** (`{chat.id}`)\n"
+ except:
+ text += f"**๐ {forcesub['chat']}** - `Invalid Chat!`\n"
+ else:
+ text = "**๐ No Forcesub Enabled in Bot!**"
+
+ await message.reply_text(text)
+
+
+@hellbot.bot.on_message(filters.command("getforcesub") & Config.AUTH_USERS)
+async def getforcesub(client: Client, message: Message):
+ if len(message.command) < 2:
+ chat = message.chat
+ else:
+ try:
+ chat = await client.get_chat(message.command[1])
+ except:
+ return await message.reply_text(f"**Invalid Channel Username/ID!**")
+
+ mustjoins = await db.get_forcesub(chat.id)
+ if mustjoins:
+ text = f"**This chat has {len(mustjoins['must_join'])} forcesub(s):**\n"
+ for must_join in mustjoins["must_join"]:
+ try:
+ chat = await client.get_chat(must_join)
+ text += f"**๐ {chat.title}** (`{chat.id}`)\n"
+ except:
+ text += f"**๐ {must_join}** - `Invalid Chat!`\n"
+ else:
+ text = "**๐ No Forcesub Enabled in This Chat!**"
+
+ await message.reply_text(text)
+
+
+@hellbot.bot.on_message(
+ filters.group
+ & filters.incoming
+ & filters.new_chat_members
+ & ~filters.bot
+ & ~filters.service
+ & ~Config.AUTH_USERS
+ & ~filters.me
+)
+async def handle_force_sub(client: Client, message: Message):
+ if message.chat.id not in Config.FORCESUBS:
+ return
+
+ if not is_user_admin(message.chat, client.me.id):
+ return
+
+ btns_list = []
+ mustjoins = await db.get_forcesub(message.chat.id)
+
+ for i, must_join in enumerate(mustjoins["must_join"]):
+ try:
+ await client.get_chat_member(must_join, message.from_user.id)
+ except UserNotParticipant:
+ invite_link = await client.export_chat_invite_link(must_join)
+ btns_list.append((f"Join {i}", invite_link, "url"))
+ continue
+ except ChatAdminRequired:
+ continue
+ except Exception as e:
+ LOGS.warning(e)
+ continue
+
+ if len(btns_list) == 0:
+ return
+
+ join_btns = gen_inline_keyboard(btns_list, 2)
+ join_btns.append(
+ [
+ InlineKeyboardButton("Unmute ๐ฃ๏ธ", f"forcesub:unmute:{message.from_user.id}:{message.chat.id}")
+ ]
+ )
+ await message.reply_text(
+ f"**๐ Welcome to {message.chat.title}!**\n\n"
+ f"To be able to chat here, you must follow the instructions below:\n"
+ f" {Symbols.anchor} __Click the buttons below to join our important channels.__"
+ f" {Symbols.anchor} __After joining all channels, press the unmute button below.__"
+ f" {Symbols.anchor} __Then you can chat here.__",
+ disable_web_page_preview=True,
+ reply_markup=InlineKeyboardMarkup(join_btns),
+ )
+
+
+@hellbot.bot.on_callback_query(filters.regex(r"forcesub"))
+async def forcesub_cb(client: Client, cb: CallbackQuery):
+ data = cb.data.split(":")
+ if data[1] == "unmute":
+ try:
+ if not int(data[3]) == cb.message.chat.id:
+ return await cb.answer(
+ "**This is not for this chat!**", show_alert=True
+ )
+
+ must_join = await db.get_forcesub(cb.message.chat.id)
+ for chat in must_join["must_join"]:
+ try:
+ await client.get_chat_member(int(chat), cb.from_user.id)
+ except UserNotParticipant:
+ return await cb.answer(
+ "**You must join all channels first!**", show_alert=True
+ )
+ except ChatAdminRequired:
+ return await cb.answer(
+ "I'm not admin in some of the channels! Ask owner to make me admin.",
+ show_alert=True,
+ )
+ except Exception as e:
+ return await cb.answer(f"**Error:**\n`{e}`")
+
+ permissions = ChatPermissions(can_send_messages=True)
+ await cb.message.chat.restrict_member(int(data[2]), permissions)
+ except Exception as e:
+ return await cb.answer(f"**Error:**\n`{e}`")
+
+ await cb.answer("**๐ Unmuted!**", show_alert=True)
+ return await cb.message.delete()
+
+
+BotHelp("ForceSub").add(
+ "forcesub",
+ "This command is used to force users to join some channels to chat in group.",
+).add(
+ "unforcesub", "This command is used to remove channels from forcesub in group."
+).add(
+ "listforcesub", "This command is used to list all forcesub in bot."
+).add(
+ "getforcesub", "This command is used to get forcesub in group."
+).info(
+ "ForceSub ๐"
+).done()
diff --git a/Hellbot/plugins/bot/inline.py b/Hellbot/plugins/bot/inline.py
new file mode 100644
index 0000000000000000000000000000000000000000..1871f235c396627e675283101d5ff658f4ab5c0b
--- /dev/null
+++ b/Hellbot/plugins/bot/inline.py
@@ -0,0 +1,39 @@
+from pyrogram import filters
+from pyrogram.types import (
+ InlineKeyboardMarkup,
+ InlineQuery,
+ InlineQueryResultArticle,
+ InputTextMessageContent,
+)
+
+from Hellbot.functions.templates import help_template
+
+from ..btnsG import gen_inline_help_buttons
+from . import Config, hellbot
+
+
+@hellbot.bot.on_inline_query(filters.regex(r"help_menu"))
+async def help_inline(_, query: InlineQuery):
+ if not query.from_user.id in Config.AUTH_USERS:
+ return
+ no_of_plugins = len(Config.CMD_MENU)
+ no_of_commands = len(Config.CMD_INFO)
+ buttons, pages = await gen_inline_help_buttons(0, sorted(Config.CMD_MENU))
+ caption = await help_template(
+ query.from_user.mention, (no_of_commands, no_of_plugins), (1, pages)
+ )
+ await query.answer(
+ results=[
+ (
+ InlineQueryResultArticle(
+ "HellBot Help Menu ๐",
+ InputTextMessageContent(
+ caption,
+ disable_web_page_preview=True,
+ ),
+ description="Inline Query for Help Menu of HellBot",
+ reply_markup=InlineKeyboardMarkup(buttons),
+ )
+ )
+ ],
+ )
diff --git a/Hellbot/plugins/bot/sessions.py b/Hellbot/plugins/bot/sessions.py
new file mode 100644
index 0000000000000000000000000000000000000000..3d93b2d7ebc62a459a9acf0c5a7be6f330ba5dbe
--- /dev/null
+++ b/Hellbot/plugins/bot/sessions.py
@@ -0,0 +1,183 @@
+from pyrogram import Client, filters
+from pyrogram.errors import SessionPasswordNeeded
+from pyrogram.types import (
+ CallbackQuery,
+ InlineKeyboardButton,
+ InlineKeyboardMarkup,
+ Message,
+ ReplyKeyboardRemove,
+)
+
+from ..btnsG import gen_inline_keyboard, start_button
+from ..btnsK import session_keyboard
+from . import START_MSG, BotHelp, Config, Symbols, db, hellbot
+
+
+@hellbot.bot.on_message(
+ filters.command("session") & Config.AUTH_USERS & filters.private
+)
+async def session_menu(_, message: Message):
+ await message.reply_text(
+ "**๐ ๐ฏ๐
๐พ๐บ๐๐พ ๐ผ๐๐๐๐๐พ ๐บ๐ ๐๐๐๐๐๐ ๐ฟ๐๐๐ ๐ป๐พ๐
๐๐:**",
+ reply_markup=session_keyboard(),
+ )
+
+
+@hellbot.bot.on_message(filters.regex(r"New ๐ซ") & Config.AUTH_USERS & filters.private)
+async def new_session(_, message: Message):
+ await message.reply_text(
+ "**๐ฎ๐๐บ๐!** ๐ซ๐พ๐'๐ ๐๐พ๐๐๐ ๐บ ๐๐พ๐ ๐๐พ๐๐๐๐๐",
+ reply_markup=ReplyKeyboardRemove(),
+ )
+
+ phone_number = await hellbot.bot.ask(
+ message.chat.id,
+ "**1.** ๐ค๐๐๐พ๐ ๐๐๐๐ ๐๐พ๐
๐พ๐๐๐บ๐ ๐บ๐ผ๐ผ๐๐๐๐ ๐๐๐๐๐พ ๐๐๐๐ป๐พ๐ ๐๐ ๐บ๐ฝ๐ฝ ๐๐๐พ ๐๐พ๐๐๐๐๐: \n\n__๐ฒ๐พ๐๐ฝ /cancel ๐๐ ๐ผ๐บ๐๐ผ๐พ๐
๐๐๐พ ๐๐๐พ๐๐บ๐๐๐๐.__",
+ filters=filters.text,
+ timeout=120,
+ )
+
+ if phone_number.text == "/cancel":
+ return await message.reply_text("**๐ข๐บ๐๐ผ๐พ๐
๐
๐พ๐ฝ!**")
+ elif not phone_number.text.startswith("+") and not phone_number.text[1:].isdigit():
+ return await message.reply_text(
+ "**๐ค๐๐๐๐!** ๐ฏ๐๐๐๐พ ๐๐๐๐ป๐พ๐ ๐๐๐๐ ๐ป๐พ ๐๐ ๐ฝ๐๐๐๐๐ ๐บ๐๐ฝ ๐๐๐๐๐
๐ฝ ๐ผ๐๐๐๐บ๐๐ ๐ผ๐๐๐๐๐๐ ๐ผ๐๐ฝ๐พ."
+ )
+
+ try:
+ client = Client(
+ name="Hellbot",
+ api_id=Config.API_ID,
+ api_hash=Config.API_HASH,
+ in_memory=True,
+ )
+ await client.connect()
+
+ code = await client.send_code(phone_number.text)
+ ask_otp = await hellbot.bot.ask(
+ message.chat.id,
+ "**2.** ๐ค๐๐๐พ๐ ๐๐๐พ ๐ฎ๐ณ๐ฏ ๐๐พ๐๐ ๐๐ ๐๐๐๐ ๐๐พ๐
๐พ๐๐๐บ๐ ๐บ๐ผ๐ผ๐๐๐๐ ๐ป๐ ๐๐พ๐๐บ๐๐บ๐๐๐๐ ๐พ๐๐พ๐๐ ๐๐๐๐ป๐พ๐ ๐๐๐๐ ๐บ ๐๐๐บ๐ผ๐พ. \n\n**๐ค๐๐บ๐๐๐
๐พ:** `2 4 1 7 4`\n\n__๐ฒ๐พ๐๐ฝ /cancel ๐๐ ๐ผ๐บ๐๐ผ๐พ๐
๐๐๐พ ๐๐๐พ๐๐บ๐๐๐๐.__",
+ filters=filters.text,
+ timeout=300,
+ )
+ if ask_otp.text == "/cancel":
+ return await message.reply_text("**๐ข๐บ๐๐ผ๐พ๐
๐
๐พ๐ฝ!**")
+ otp = ask_otp.text.replace(" ", "")
+
+ try:
+ await client.sign_in(phone_number.text, code.phone_code_hash, otp)
+ except SessionPasswordNeeded:
+ two_step_pass = await hellbot.bot.ask(
+ message.chat.id,
+ "**3.** ๐ค๐๐๐พ๐ ๐๐๐๐ ๐๐๐ ๐๐๐พ๐ ๐๐พ๐๐๐ฟ๐๐ผ๐บ๐๐๐๐ ๐๐บ๐๐๐๐๐๐ฝ: \n\n__๐ฒ๐พ๐๐ฝ /cancel ๐๐ ๐ผ๐บ๐๐ผ๐พ๐
๐๐๐พ ๐๐๐พ๐๐บ๐๐๐๐.__",
+ filters=filters.text,
+ timeout=120,
+ )
+ if two_step_pass.text == "/cancel":
+ return await message.reply_text("**๐ข๐บ๐๐ผ๐พ๐
๐
๐พ๐ฝ!**")
+ await client.check_password(two_step_pass.text)
+
+ session_string = await client.export_session_string()
+ await message.reply_text(
+ f"**๐ฒ๐๐ผ๐ผ๐พ๐๐!** ๐ธ๐๐๐ ๐๐พ๐๐๐๐๐ ๐๐๐๐๐๐ ๐๐ ๐๐พ๐๐พ๐๐บ๐๐พ๐ฝ. ๐ ๐ฝ๐ฝ๐๐๐ ๐๐ ๐๐ ๐ฝ๐บ๐๐บ๐ป๐บ๐๐พ..."
+ )
+ user_id = (await client.get_me()).id
+ await db.update_session(user_id, session_string)
+ await client.disconnect()
+ await message.reply_text(
+ "**๐ฒ๐๐ผ๐ผ๐พ๐๐!** ๐ฒ๐พ๐๐๐๐๐ ๐๐๐๐๐๐ ๐บ๐ฝ๐ฝ๐พ๐ฝ ๐๐ ๐ฝ๐บ๐๐บ๐ป๐บ๐๐พ. ๐ธ๐๐ ๐ผ๐บ๐ ๐๐๐ ๐๐๐พ ๐ง๐พ๐
๐
๐ก๐๐ ๐๐ ๐๐๐๐ ๐บ๐ผ๐ผ๐๐๐๐ ๐บ๐ฟ๐๐พ๐ ๐๐พ๐๐๐บ๐๐๐๐๐ ๐๐๐พ ๐ป๐๐.\n\n**๐ญ๐ฎ๐ณ๐ค:** ๐ฅ๐๐ ๐๐พ๐ผ๐๐๐๐๐ ๐๐๐๐๐๐๐พ๐ ๐๐๐ป๐๐ฝ๐ ๐๐๐
๐
๐๐บ๐๐พ ๐๐๐พ ๐บ๐ผ๐ผ๐พ๐๐ ๐๐ ๐๐๐๐ ๐๐พ๐๐๐๐๐ ๐๐๐๐๐๐. ๐ญ๐๐ ๐พ๐๐พ๐ ๐๐๐ ๐๐ ๐๐๐พ ๐ป๐๐."
+ )
+ except TimeoutError:
+ await message.reply_text(
+ "**๐ณ๐๐๐พ๐๐๐๐ค๐๐๐๐!** ๐ธ๐๐ ๐๐๐๐ ๐
๐๐๐๐พ๐ ๐๐๐บ๐ ๐พ๐๐ผ๐๐พ๐ผ๐๐พ๐ฝ ๐๐ ๐ผ๐๐๐๐
๐พ๐๐พ ๐๐๐พ ๐๐๐๐ผ๐พ๐๐. ๐ฏ๐
๐พ๐บ๐๐พ ๐๐๐ ๐บ๐๐บ๐๐."
+ )
+ except Exception as e:
+ await message.reply_text(f"**๐ค๐๐๐๐!** {e}")
+
+
+@hellbot.bot.on_message(
+ filters.regex(r"Delete โ") & Config.AUTH_USERS & filters.private
+)
+async def delete_session(_, message: Message):
+ all_sessions = await db.get_all_sessions()
+ if not all_sessions:
+ return await message.reply_text("๐ญ๐ ๐๐พ๐๐๐๐๐๐ ๐ฟ๐๐๐๐ฝ ๐๐ ๐ฝ๐บ๐๐บ๐ป๐บ๐๐พ.")
+
+ collection = []
+ for i in all_sessions:
+ collection.append((i["user_id"], f"rm_session:{i['user_id']}"))
+
+ buttons = gen_inline_keyboard(collection, 2)
+ buttons.append([InlineKeyboardButton("Cancel โ", "auth_close")])
+
+ await message.reply_text(
+ "**๐ข๐๐๐๐๐พ ๐บ ๐๐พ๐๐๐๐๐ ๐๐ ๐ฝ๐พ๐
๐พ๐๐พ:**",
+ reply_markup=InlineKeyboardMarkup(buttons),
+ )
+
+
+@hellbot.bot.on_callback_query(filters.regex(r"rm_session"))
+async def rm_session_cb(client: Client, cb: CallbackQuery):
+ collection = []
+ user_id = int(cb.data.split(":")[1])
+ all_sessions = await db.get_all_sessions()
+
+ if not all_sessions:
+ return await cb.message.delete()
+
+ try:
+ owner = await client.get_users(Config.OWNER_ID)
+ owner_id = owner.id
+ owner_name = owner.first_name
+ except:
+ owner_id = Config.OWNER_ID
+ owner_name = "๐ฎ๐๐๐พ๐"
+ if cb.from_user.id not in [user_id, owner_id]:
+ return await cb.answer(
+ f"๐ ๐ผ๐ผ๐พ๐๐ ๐๐พ๐๐๐๐๐ผ๐๐พ๐ฝ ๐๐ ๐บ๐๐๐๐๐พ๐ ๐๐๐พ๐๐. Only {owner_name} and session client can delete this session!",
+ show_alert=True,
+ )
+
+ await db.rm_session(user_id)
+ await cb.answer("**๐ฒ๐๐ผ๐ผ๐พ๐๐!** ๐ฒ๐พ๐๐๐๐๐ ๐ฝ๐พ๐
๐พ๐๐พ๐ฝ ๐ฟ๐๐๐ ๐ฝ๐บ๐๐บ๐ป๐บ๐๐พ. \n__Restart the bot to apply changes.__", show_alert=True)
+
+ for i in all_sessions:
+ collection.append((i["user_id"], f"rm_session:{i['user_id']}"))
+
+ buttons = gen_inline_keyboard(collection, 2)
+ buttons.append([InlineKeyboardButton("Cancel โ", "auth_close")])
+
+ await cb.message.edit_reply_markup(InlineKeyboardMarkup(buttons))
+
+
+@hellbot.bot.on_message(filters.regex(r"List ๐") & Config.AUTH_USERS & filters.private)
+async def list_sessions(_, message: Message):
+ all_sessions = await db.get_all_sessions()
+ if not all_sessions:
+ return await message.reply_text("๐ญ๐ ๐๐พ๐๐๐๐๐๐ ๐ฟ๐๐๐๐ฝ ๐๐ ๐ฝ๐บ๐๐บ๐ป๐บ๐๐พ.")
+
+ text = f"**{Symbols.cross_mark} ๐ซ๐๐๐ ๐๐ฟ ๐๐พ๐๐๐๐๐๐:**\n\n"
+ for i, session in enumerate(all_sessions):
+ text += f"[{'0' if i <= 9 else ''}{i+1}] {Symbols.bullet} **๐ด๐๐พ๐ ๐จ๐ฃ:** `{session['user_id']}`\n"
+
+ await message.reply_text(text)
+
+
+@hellbot.bot.on_message(filters.regex(r"Home ๐ ") & filters.private & Config.AUTH_USERS)
+async def go_home(_, message: Message):
+ await message.reply_text(
+ "**Home ๐ **",
+ reply_markup=ReplyKeyboardRemove(),
+ )
+ await message.reply_text(
+ START_MSG.format(message.from_user.mention),
+ disable_web_page_preview=True,
+ reply_markup=InlineKeyboardMarkup(start_button()),
+ )
+
+
+BotHelp("Sessions").add(
+ "session", "This command is packed with tools to manage userbot sessions."
+).info(
+ "Session ๐"
+).done()
diff --git a/Hellbot/plugins/bot/users.py b/Hellbot/plugins/bot/users.py
new file mode 100644
index 0000000000000000000000000000000000000000..0ebed55b41175e9b4afff036bd8e054f5d187b58
--- /dev/null
+++ b/Hellbot/plugins/bot/users.py
@@ -0,0 +1,82 @@
+from pyrogram import Client, filters
+from pyrogram.types import Message
+
+from . import BotHelp, Config, Symbols, hellbot
+
+
+@hellbot.bot.on_message(
+ filters.command("addauth") & Config.AUTH_USERS
+)
+async def addauth(client: Client, message: Message):
+ if not message.reply_to_message:
+ if len(message.command) < 2:
+ return await message.reply_text(
+ "Reply to a user or give me a userid/username to add them as an auth user!"
+ )
+ try:
+ user = await client.get_users(message.command[1])
+ except Exception:
+ return await message.reply_text(
+ "Give me a valid userid/username to add them as an auth user!"
+ )
+ else:
+ user = message.reply_to_message.from_user
+
+ if user.is_self:
+ return await message.reply_text("I can't add myself as an auth user!")
+
+ if user.id in Config.AUTH_USERS:
+ return await message.reply_text(f"**{user.mention} is already authorized**")
+
+ Config.AUTH_USERS.add(user.id)
+ await message.reply_text(f"**Added {user.mention} to auth users!**")
+
+
+@hellbot.bot.on_message(
+ filters.command("delauth") & Config.AUTH_USERS
+)
+async def delauth(client: Client, message: Message):
+ if not message.reply_to_message:
+ if len(message.command) < 2:
+ return await message.reply_text(
+ "Reply to a user or give me a userid/username to add them as an auth user!"
+ )
+ try:
+ user = await client.get_users(message.command[1])
+ except Exception:
+ return await message.reply_text(
+ "Give me a valid userid/username to add them as an auth user!"
+ )
+ else:
+ user = message.reply_to_message.from_user
+
+ if user.id in Config.AUTH_USERS:
+ Config.AUTH_USERS.remove(user.id)
+ await message.reply_text(f"**Removed {user.mention} from auth users!**")
+ else:
+ await message.reply_text(f"**{user.mention} is not authorized**")
+
+
+@hellbot.bot.on_message(
+ filters.command("authlist") & Config.AUTH_USERS
+)
+async def authlist(client: Client, message: Message):
+ text = "**๐ Authorized Users:**\n\n"
+ for i, userid in enumerate(Config.AUTH_USERS):
+ try:
+ user = await client.get_users(userid)
+ text += f" {Symbols.anchor} {user.mention} (`{user.id}`)\n"
+ except:
+ text += f" {Symbols.anchor} Auth User #{i+1} (`{userid}`)\n"
+
+ await message.reply_text(text)
+
+
+BotHelp("Users").add(
+ "addauth",
+ "This command is used to add a user as an authorized user. An authorized user can create and manage userbot session!",
+).add("delauth", "This command is used to remove a user from authorized users.").add(
+ "authlist", "This command is used to list all authorized users."
+).info(
+ "Users Command ๐"
+).done()
diff --git a/Hellbot/plugins/btnsG.py b/Hellbot/plugins/btnsG.py
new file mode 100644
index 0000000000000000000000000000000000000000..f4954e986e88b92aa4f1e0a0d0fae2da7370d720
--- /dev/null
+++ b/Hellbot/plugins/btnsG.py
@@ -0,0 +1,106 @@
+# G: Glass Buttons
+
+from math import ceil
+
+from pyrogram.types import InlineKeyboardButton
+
+from Hellbot.core import ENV, Symbols, db, Config
+
+
+def gen_inline_keyboard(collection: list, row: int = 2) -> list[list[InlineKeyboardButton]]:
+ keyboard = []
+ for i in range(0, len(collection), row):
+ kyb = []
+ for x in collection[i : i + row]:
+ button = btn(*x)
+ kyb.append(button)
+ keyboard.append(kyb)
+ return keyboard
+
+
+def btn(text, value, type="callback_data") -> InlineKeyboardButton:
+ return InlineKeyboardButton(text, **{type: value})
+
+
+async def gen_inline_help_buttons(page: int, plugins: list) -> tuple[list, int]:
+ buttons = []
+ column = await db.get_env(ENV.btn_in_help) or 5
+ column = int(column)
+ emoji = await db.get_env(ENV.help_emoji) or "โง"
+ pairs = list(map(list, zip(plugins[::2], plugins[1::2])))
+
+ if len(plugins) % 2 == 1:
+ pairs.append([plugins[-1]])
+
+ max_pages = ceil(len(pairs) / column)
+ pairs = [pairs[i : i + column] for i in range(0, len(pairs), column)]
+
+ for pair in pairs[page]:
+ btn_pair = []
+ for i, plugin in enumerate(pair):
+ if i % 2 == 0:
+ btn_pair.append(
+ InlineKeyboardButton(f"{emoji} {plugin}", f"help_menu:{page}:{plugin}")
+ )
+ else:
+ btn_pair.append(
+ InlineKeyboardButton(f"{plugin} {emoji}", f"help_menu:{page}:{plugin}")
+ )
+ buttons.append(btn_pair)
+
+ buttons.append(
+ [
+ InlineKeyboardButton(
+ Symbols.previous, f"help_page:{(max_pages - 1) if page == 0 else (page - 1)}",
+ ),
+ InlineKeyboardButton(
+ Symbols.close, "help_data:c"
+ ),
+ InlineKeyboardButton(
+ Symbols.next, f"help_page:{0 if page == (max_pages - 1) else (page + 1)}",
+ ),
+ ]
+ )
+
+ return buttons, max_pages
+
+
+async def gen_bot_help_buttons() -> list[list[InlineKeyboardButton]]:
+ buttons = []
+ plugins = sorted(Config.BOT_CMD_MENU)
+ emoji = await db.get_env(ENV.help_emoji) or "โง"
+ pairs = list(map(list, zip(plugins[::2], plugins[1::2])))
+
+ if len(plugins) % 2 == 1:
+ pairs.append([plugins[-1]])
+
+ for pair in pairs:
+ btn_pair = []
+ for i, plugin in enumerate(pair):
+ if i % 2 == 0:
+ btn_pair.append(
+ InlineKeyboardButton(f"{emoji} {plugin}", f"bot_help_menu:{plugin}")
+ )
+ else:
+ btn_pair.append(
+ InlineKeyboardButton(f"{plugin} {emoji}", f"bot_help_menu:{plugin}")
+ )
+ buttons.append(btn_pair)
+
+ buttons.append(
+ [
+ InlineKeyboardButton("๐ ", "help_data:start"),
+ InlineKeyboardButton(Symbols.close, "help_data:botclose"),
+ ]
+ )
+
+ return buttons
+
+
+def start_button() -> list[list[InlineKeyboardButton]]:
+ return [
+ [
+ InlineKeyboardButton("โ๏ธ Help", "help_data:bothelp"),
+ InlineKeyboardButton("Source ๐ฆ", "help_data:source"),
+ ]
+ ]
diff --git a/Hellbot/plugins/btnsK.py b/Hellbot/plugins/btnsK.py
new file mode 100644
index 0000000000000000000000000000000000000000..47aad6ac13a20ed91e5cababebb871ea648a5945
--- /dev/null
+++ b/Hellbot/plugins/btnsK.py
@@ -0,0 +1,45 @@
+# K: Keyboard Buttons
+
+from pyrogram.types import KeyboardButton, ReplyKeyboardMarkup
+
+
+def gen_keyboard(collection: list, row: int = 2) -> list[list[KeyboardButton]]:
+ keyboard = []
+ for i in range(0, len(collection), row):
+ kyb = []
+ for x in collection[i : i + row]:
+ kyb.append(KeyboardButton(x))
+ keyboard.append(kyb)
+ return keyboard
+
+
+def session_keyboard() -> ReplyKeyboardMarkup:
+ return ReplyKeyboardMarkup(
+ [
+ [
+ KeyboardButton("New ๐ซ"),
+ KeyboardButton("Delete โ"),
+ ],
+ [
+ KeyboardButton("List ๐"),
+ KeyboardButton("Home ๐ "),
+ ],
+ ],
+ resize_keyboard=True,
+ )
+
+
+def start_keyboard() -> ReplyKeyboardMarkup:
+ return ReplyKeyboardMarkup(
+ [
+ [
+ KeyboardButton("๐ Session"),
+ KeyboardButton("Force Sub โจ"),
+ ],
+ [
+ KeyboardButton("๐ฅ Users"),
+ KeyboardButton("Others ๐ฃ"),
+ ],
+ ],
+ resize_keyboard=True,
+ )
diff --git a/Hellbot/plugins/decorator.py b/Hellbot/plugins/decorator.py
new file mode 100644
index 0000000000000000000000000000000000000000..b0e36b84fba19ca7591958f8284d992bd120c2ff
--- /dev/null
+++ b/Hellbot/plugins/decorator.py
@@ -0,0 +1,67 @@
+from pyrogram import Client, filters
+from pyrogram.enums import ChatType
+from pyrogram.handlers import MessageHandler
+from pyrogram.types import Message
+
+from Hellbot.core import Config, db, hellbot
+from Hellbot.functions.admins import is_user_admin
+
+
+def on_message(
+ command: str | list[str],
+ group: int = 0,
+ chat_type: list[ChatType] = None,
+ admin_only: bool = False,
+ allow_stan: bool = False,
+):
+ if allow_stan:
+ _filter = (
+ filters.command(command, Config.HANDLERS)
+ & (filters.me | Config.STAN_USERS)
+ & ~filters.forwarded
+ & ~filters.via_bot
+ )
+ else:
+ _filter = (
+ filters.command(command, Config.HANDLERS)
+ & filters.me
+ & ~filters.forwarded
+ & ~filters.via_bot
+ )
+
+ def decorator(func):
+ async def wrapper(client: Client, message: Message):
+ if client.me.id != message.from_user.id:
+ if not await db.is_stan(client.me.id, message.from_user.id):
+ return
+
+ if admin_only and not message.chat.type == ChatType.PRIVATE:
+ if not await is_user_admin(message.chat, client.me.id):
+ return await hellbot.edit(message, "๐จ ๐บ๐ ๐๐๐ ๐บ๐ ๐บ๐ฝ๐๐๐ ๐๐พ๐๐พ!")
+
+ if chat_type and message.chat.type not in chat_type:
+ return await hellbot.edit(message, "๐ข๐บ๐'๐ ๐๐๐พ ๐๐๐๐ ๐ผ๐๐๐๐บ๐๐ฝ ๐๐พ๐๐พ!")
+
+ await func(client, message)
+ message.continue_propagation()
+
+ for user in hellbot.users:
+ user.add_handler(MessageHandler(wrapper, _filter), group)
+
+ return wrapper
+
+ return decorator
+
+
+def custom_handler(filters: filters.Filter, group: int = 0):
+ def decorator(func):
+ async def wrapper(client: Client, message: Message):
+ await func(client, message)
+ message.continue_propagation()
+
+ for user in hellbot.users:
+ user.add_handler(MessageHandler(wrapper, filters), group)
+
+ return wrapper
+
+ return decorator
diff --git a/Hellbot/plugins/help.py b/Hellbot/plugins/help.py
new file mode 100644
index 0000000000000000000000000000000000000000..e8f5c0a5ed2f9e3d214b7a8777fce820f40a3a4b
--- /dev/null
+++ b/Hellbot/plugins/help.py
@@ -0,0 +1,132 @@
+from Hellbot.core.config import Config, Symbols
+
+
+class HelpMenu:
+ def __init__(self, file: str) -> None:
+ self.filename = file
+ self.command_dict = {}
+ self.command_info = ""
+
+ def add(
+ self,
+ command: str,
+ parameters: str = None,
+ description: str = None,
+ example: str = None,
+ note: str = None,
+ ):
+ self.command_dict[command] = {
+ "command": command,
+ "parameters": parameters,
+ "description": description,
+ "example": example,
+ "note": note,
+ }
+ return self
+
+ def info(self, command_info: str):
+ self.command_info = command_info
+ return self
+
+ def get_menu(self) -> str:
+ result = f"**๐ฏ๐
๐๐๐๐ ๐ฅ๐๐
๐พ:** `{self.filename}`"
+ if self.command_info:
+ result += f"\n**๐ฏ๐
๐๐๐๐ ๐จ๐๐ฟ๐:** __{self.command_info} ๐__"
+ result += "\n\n"
+ for command in self.command_dict:
+ command = self.command_dict[command]
+ result += f"**{Symbols.radio_select} ๐ข๐๐๐๐บ๐๐ฝ:** `{Config.HANDLERS[0]}{command['command']}"
+ if command["parameters"]:
+ result += f" {command['parameters']}`\n"
+ else:
+ result += "`\n"
+ if command["description"]:
+ result += (
+ f"**{Symbols.arrow_right} ๐ฃ๐พ๐๐ผ๐๐๐๐๐๐๐:** __{command['description']}__\n"
+ )
+ if command["example"]:
+ result += f"**{Symbols.arrow_right} ๐ค๐๐บ๐๐๐
๐พ:** `{Config.HANDLERS[0]}{command['example']}`\n"
+ if command["note"]:
+ result += f"**{Symbols.arrow_right} ๐ญ๐๐๐พ:** __{command['note']}__\n"
+
+ result += "\n"
+
+ Config.CMD_INFO[command["command"]] = {
+ "command": f"{command['command']} {command['parameters'] if command['parameters'] else ''}",
+ "description": command["description"],
+ "example": command["example"],
+ "note": command["note"],
+ "plugin": self.filename,
+ }
+
+ return result
+
+ def done(self) -> None:
+ Config.HELP_DICT[self.filename] = {
+ "commands": self.command_dict,
+ "info": self.command_info,
+ }
+ Config.CMD_MENU[self.filename] = self.get_menu()
+
+
+class BotHelp:
+ def __init__(self, file: str) -> None:
+ self.category = file
+ self.command_dict = {}
+ self.command_info = ""
+
+ def add(self, command: str, description: str):
+ self.command_dict[command] = {"command": command, "description": description}
+ return self
+
+ def info(self, command_info: str):
+ self.command_info = command_info
+ return self
+
+ def get_menu(self) -> str:
+ result = f"**๐ฏ๐
๐๐๐๐ ๐ข๐บ๐๐พ๐๐๐๐:** `{self.category}`"
+ if self.command_info:
+ result += f"\n**๐ฏ๐
๐๐๐๐ ๐จ๐๐ฟ๐:** __{self.command_info}__"
+ result += "\n\n"
+ for command in self.command_dict:
+ command = self.command_dict[command]
+ result += f"**{Symbols.radio_select} ๐ข๐๐๐๐บ๐๐ฝ:** `/{command['command']}`\n"
+ if command["description"]:
+ result += (
+ f"**{Symbols.arrow_right} ๐ฃ๐พ๐๐ผ๐๐๐๐๐๐๐:** __{command['description']}__\n"
+ )
+ result += "\n"
+
+ Config.BOT_CMD_INFO[command["command"]] = {
+ "command": command["command"],
+ "description": command["description"],
+ "category": self.category,
+ }
+
+ return result
+
+ def done(self) -> None:
+ Config.BOT_HELP[self.category] = {
+ "commands": self.command_dict,
+ "info": self.command_info,
+ }
+ Config.BOT_CMD_MENU[self.category] = self.get_menu()
+
+
+# example usage of HelpMenu class
+"""
+HelpMenu("example").add(
+ "example", "", "description of command", "example of command", "note of command"
+).info(
+ "information of plugin"
+).done()
+"""
+
+# example usage of BotHelp class
+"""
+BotHelp("example").add(
+ "example", "description of command"
+).info(
+ "information of category"
+).done()
+"""
diff --git a/Hellbot/plugins/user/__init__.py b/Hellbot/plugins/user/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..6303c70a008653f924fba34a7f9cde94c12c987a
--- /dev/null
+++ b/Hellbot/plugins/user/__init__.py
@@ -0,0 +1,16 @@
+from pyrogram.enums import ChatType
+
+from Hellbot.core.clients import hellbot
+from Hellbot.core.config import Config, Symbols
+from Hellbot.core.database import db
+from Hellbot.plugins.decorator import custom_handler, on_message
+from Hellbot.plugins.help import HelpMenu
+
+handler = Config.HANDLERS[0]
+bot = hellbot.bot
+
+bot_only = [ChatType.BOT]
+group_n_channel = [ChatType.GROUP, ChatType.SUPERGROUP, ChatType.CHANNEL]
+group_only = [ChatType.GROUP, ChatType.SUPERGROUP]
+private_n_bot = [ChatType.PRIVATE, ChatType.BOT]
+private_only = [ChatType.PRIVATE]
diff --git a/Hellbot/plugins/user/admins.py b/Hellbot/plugins/user/admins.py
new file mode 100644
index 0000000000000000000000000000000000000000..453dd3a486b83c7ac0d0f85362c34a4c4d057baa
--- /dev/null
+++ b/Hellbot/plugins/user/admins.py
@@ -0,0 +1,510 @@
+import asyncio
+
+from pyrogram import Client, filters
+from pyrogram.types import ChatPermissions, ChatPrivileges, Message
+
+from Hellbot.core import LOGS
+
+from . import HelpMenu, custom_handler, db, group_only, handler, hellbot, on_message
+
+
+@on_message(
+ "promote",
+ chat_type=group_only,
+ admin_only=True,
+ allow_stan=True,
+)
+async def promote(client: Client, message: Message):
+ if len(message.command) < 2 and not message.reply_to_message:
+ return await hellbot.delete(
+ message, "๐ญ๐พ๐พ๐ฝ ๐บ ๐๐๐พ๐๐๐บ๐๐พ/๐๐ฝ ๐๐ ๐๐พ๐๐
๐ ๐๐ ๐บ ๐๐๐พ๐ ๐๐ ๐๐๐๐๐๐๐พ ๐๐๐พ๐!"
+ )
+
+ if message.reply_to_message:
+ user = message.reply_to_message.from_user
+ title = await hellbot.input(message)
+ else:
+ user = await client.get_users(message.command[1])
+ title = (await hellbot.input(message)).split(" ", 1)[1].strip() or ""
+
+ try:
+ privileges = ChatPrivileges(
+ can_manage_chat=True,
+ can_delete_messages=True,
+ can_manage_video_chats=True,
+ can_restrict_members=False,
+ can_promote_members=False,
+ can_change_info=False,
+ can_invite_users=True,
+ can_pin_messages=True,
+ is_anonymous=False,
+ )
+ await message.chat.promote_member(user.id, privileges)
+ await client.set_administrator_title(message.chat.id, user.id, title)
+ except Exception as e:
+ return await hellbot.error(message, e)
+
+ await hellbot.delete(message, f"**๐ซ ๐ฏ๐๐๐๐๐๐พ๐ฝ {user.mention} ๐๐๐ผ๐ผ๐พ๐๐๐ฟ๐๐
๐
๐!**")
+ await hellbot.check_and_log(
+ "promote",
+ f"**Promoted User**\n\n**User:** {user.mention}\n**User ID:** `{user.id}`\n**Admin:** `{message.from_user.mention}`\n**Group:** `{message.chat.title}`\n**Group ID:** `{message.chat.id}`",
+ )
+
+
+@on_message(
+ "demote",
+ chat_type=group_only,
+ admin_only=True,
+ allow_stan=True,
+)
+async def demote(client: Client, message: Message):
+ if len(message.command) < 2 and not message.reply_to_message:
+ return await hellbot.delete(
+ message, "๐ญ๐พ๐พ๐ฝ ๐บ ๐๐๐พ๐๐๐บ๐๐พ/๐๐ฝ ๐๐ ๐๐พ๐๐
๐ ๐๐ ๐บ ๐๐๐พ๐ ๐๐ ๐ฝ๐พ๐๐๐๐พ ๐๐๐พ๐!"
+ )
+
+ if message.reply_to_message:
+ user = message.reply_to_message.from_user
+ else:
+ user = await client.get_users(message.command[1])
+ try:
+ privileges = ChatPrivileges(
+ can_manage_chat=False,
+ can_delete_messages=False,
+ can_manage_video_chats=False,
+ can_restrict_members=False,
+ can_promote_members=False,
+ can_change_info=False,
+ can_invite_users=False,
+ can_pin_messages=False,
+ is_anonymous=False,
+ )
+ await message.chat.promote_member(user.id, privileges)
+ except Exception as e:
+ return await hellbot.error(message, e)
+
+ await hellbot.delete(message, f"**๐ ๐ฃ๐พ๐๐๐๐พ๐ฝ {user.mention} ๐๐๐ผ๐ผ๐พ๐๐๐ฟ๐๐
๐
๐!**")
+ await hellbot.check_and_log(
+ "demote",
+ f"**Demoted User**\n\n**User:** {user.mention}\n**User ID:** `{user.id}`\n**Admin:** `{message.from_user.mention}`\n**Group:** `{message.chat.title}`\n**Group ID:** `{message.chat.id}`",
+ )
+
+
+@on_message(
+ ["ban", "dban"],
+ chat_type=group_only,
+ admin_only=True,
+ allow_stan=True,
+)
+async def ban(client: Client, message: Message):
+ if message.reply_to_message:
+ user = message.reply_to_message.from_user
+ if len(message.command) < 2:
+ reason = None
+ else:
+ reason = await hellbot.input(message)
+ if message.command[0][0].lower() == "d":
+ await message.reply_to_message.delete()
+ elif len(message.command) == 2:
+ user = await client.get_users(message.command[1])
+ reason = None
+ elif len(message.command) > 2:
+ user = await client.get_users(message.command[1])
+ reason = (await hellbot.input(message)).split(" ", 1)[1].strip()
+ else:
+ return await hellbot.delete(
+ message, "๐ญ๐พ๐พ๐ฝ ๐บ ๐๐๐พ๐๐๐บ๐๐พ/๐๐ฝ ๐๐ ๐๐พ๐๐
๐ ๐๐ ๐บ ๐๐๐พ๐ ๐๐ ๐ป๐บ๐ ๐๐๐พ๐!"
+ )
+
+ try:
+ await message.chat.ban_member(user.id)
+ except Exception as e:
+ return await hellbot.error(message, e)
+
+ reason = reason if reason else "Not Specified"
+ await hellbot.delete(
+ message,
+ f"**โ ๏ธ ๐ก๐บ๐๐๐พ๐ฝ {user.mention} ๐๐๐ผ๐ผ๐พ๐๐๐ฟ๐๐
๐
๐!**\n**๐ฑ๐พ๐บ๐๐๐:** `{reason}`",
+ 30,
+ )
+ await hellbot.check_and_log(
+ "ban",
+ f"**Banned User**\n\n**User:** {user.mention}\n**User ID:** `{user.id}`\n**Reason:** `{reason}`\n**Admin:** `{message.from_user.mention}`\n**Group:** `{message.chat.title}`\n**Group ID:** `{message.chat.id}`",
+ )
+
+
+@on_message(
+ "unban",
+ chat_type=group_only,
+ admin_only=True,
+ allow_stan=True,
+)
+async def unban(client: Client, message: Message):
+ if len(message.command) < 2 and not message.reply_to_message:
+ return await hellbot.delete(
+ message, "๐ญ๐พ๐พ๐ฝ ๐บ ๐๐๐พ๐๐๐บ๐๐พ/๐๐ฝ ๐๐ ๐๐พ๐๐
๐ ๐๐ ๐บ ๐๐๐พ๐ ๐๐ ๐๐๐ป๐บ๐ ๐๐๐พ๐!"
+ )
+
+ if message.reply_to_message:
+ user = message.reply_to_message.from_user
+ else:
+ user = await client.get_users(message.command[1])
+
+ try:
+ await message.chat.unban_member(user.id)
+ except Exception as e:
+ return await hellbot.error(message, e)
+
+ await hellbot.delete(message, f"**๐ค ๐ด๐๐ป๐บ๐๐๐พ๐ฝ {user.mention} ๐ฒ๐๐ผ๐ผ๐พ๐๐๐ฟ๐๐
๐
๐!**", 30)
+ await hellbot.check_and_log(
+ "unban",
+ f"**Unbanned User**\n\n**User:** {user.mention}\n**User ID:** `{user.id}`\n**Admin:** `{message.from_user.mention}`\n**Group:** `{message.chat.title}`\n**Group ID:** `{message.chat.id}`",
+ )
+
+
+@on_message(
+ ["kick", "dkick"],
+ chat_type=group_only,
+ admin_only=True,
+ allow_stan=True,
+)
+async def kick(client: Client, message: Message):
+ if message.reply_to_message:
+ user = message.reply_to_message.from_user
+ if len(message.command) < 2:
+ reason = None
+ else:
+ reason = await hellbot.input(message)
+ if message.command[0][0].lower() == "d":
+ await message.reply_to_message.delete()
+ elif len(message.command) == 2:
+ user = await client.get_users(message.command[1])
+ reason = None
+ elif len(message.command) > 2:
+ user = await client.get_users(message.command[1])
+ reason = (await hellbot.input(message)).split(" ", 1)[1].strip()
+ else:
+ return await hellbot.delete(
+ message, "๐ญ๐พ๐พ๐ฝ ๐บ ๐๐๐พ๐๐๐บ๐๐พ/๐๐ฝ ๐๐ ๐๐พ๐๐
๐ ๐๐ ๐บ ๐๐๐พ๐ ๐๐ ๐๐๐ผ๐ ๐๐๐พ๐!"
+ )
+
+ try:
+ await message.chat.ban_member(user.id)
+ except Exception as e:
+ return await hellbot.error(message, e)
+
+ reason = reason if reason else "Not Specified"
+ await hellbot.delete(
+ message,
+ f"**๐ ๐ช๐๐ผ๐๐พ๐ฝ {user.mention} ๐ฒ๐๐ผ๐ผ๐พ๐๐๐ฟ๐๐
๐
๐!**\n**๐ฑ๐พ๐บ๐๐๐:** `{reason}`",
+ 30,
+ )
+ await hellbot.check_and_log(
+ "kick",
+ f"**Kicked User**\n\n**User:** {user.mention}\n**User ID:** `{user.id}`\n**Reason:** `{reason}`\n**Admin:** `{message.from_user.mention}`\n**Group:** `{message.chat.title}`\n**Group ID:** `{message.chat.id}`",
+ )
+ await asyncio.sleep(5)
+ await message.chat.unban_member(user.id)
+
+
+@on_message(
+ "mute",
+ chat_type=group_only,
+ admin_only=True,
+ allow_stan=True,
+)
+async def mute(client: Client, message: Message):
+ if message.reply_to_message:
+ user = message.reply_to_message.from_user
+ if len(message.command) < 2:
+ reason = None
+ else:
+ reason = await hellbot.input(message)
+ elif len(message.command) == 2:
+ user = await client.get_users(message.command[1])
+ reason = None
+ elif len(message.command) > 2:
+ user = await client.get_users(message.command[1])
+ reason = (await hellbot.input(message)).split(" ", 1)[1].strip()
+ else:
+ return await hellbot.delete(
+ message, "๐ญ๐พ๐พ๐ฝ ๐บ ๐๐๐พ๐๐๐บ๐๐พ/๐๐ฝ ๐๐ ๐๐พ๐๐
๐ ๐๐ ๐บ ๐๐๐พ๐ ๐๐ ๐๐๐๐พ ๐๐๐พ๐!"
+ )
+
+ try:
+ permissions = ChatPermissions(
+ can_send_messages=False,
+ )
+ await message.chat.restrict_member(user.id, permissions)
+ except Exception as e:
+ return await hellbot.error(message, e)
+
+ reason = reason if reason else "Not Specified"
+ await hellbot.delete(
+ message,
+ f"**๐ค ๐ฌ๐๐๐พ๐ฝ {user.mention} ๐ฒ๐๐ผ๐ผ๐พ๐๐๐ฟ๐๐
๐
๐!**\n**๐ฑ๐พ๐บ๐๐๐:** `{reason}`",
+ 30,
+ )
+ await hellbot.check_and_log(
+ "mute",
+ f"**Muted User**\n\n**User:** {user.mention}\n**User ID:** `{user.id}`\n**Reason:** `{reason}`\n**Admin:** `{message.from_user.mention}`\n**Group:** `{message.chat.title}`\n**Group ID:** `{message.chat.id}`",
+ )
+
+
+@on_message(
+ "unmute",
+ chat_type=group_only,
+ admin_only=True,
+ allow_stan=True,
+)
+async def unmute(client: Client, message: Message):
+ if len(message.command) < 2 and not message.reply_to_message:
+ return await hellbot.delete(
+ message, "๐ญ๐พ๐พ๐ฝ ๐บ ๐๐๐พ๐๐๐บ๐๐พ/๐๐ฝ ๐๐ ๐๐พ๐๐
๐ ๐๐ ๐บ ๐๐๐พ๐ ๐๐ ๐๐๐๐๐๐พ ๐๐๐พ๐!"
+ )
+
+ if message.reply_to_message:
+ user = message.reply_to_message.from_user
+ else:
+ user = await client.get_users(message.command[1])
+
+ try:
+ permissions = ChatPermissions(
+ can_send_messages=True,
+ )
+ await message.chat.restrict_member(user.id, permissions)
+ except Exception as e:
+ return await hellbot.error(message, e)
+
+ await hellbot.delete(message, f"**๐ ๐ด๐๐๐๐๐พ๐ฝ {user.mention} ๐ฒ๐๐ผ๐ผ๐พ๐๐๐ฟ๐๐
๐
๐!**", 30)
+ await hellbot.check_and_log(
+ "unmute",
+ f"**Unmuted User**\n\n**User:** {user.mention}\n**User ID:** `{user.id}`\n**Admin:** `{message.from_user.mention}`\n**Group:** `{message.chat.title}`\n**Group ID:** `{message.chat.id}`",
+ )
+
+
+@on_message("dmute", allow_stan=True)
+async def dmute(client: Client, message: Message):
+ if message.reply_to_message:
+ user = message.reply_to_message.from_user
+ if len(message.command) < 2:
+ reason = None
+ else:
+ reason = await hellbot.input(message)
+ elif len(message.command) == 2:
+ user = await client.get_users(message.command[1])
+ reason = None
+ elif len(message.command) > 2:
+ user = await client.get_users(message.command[1])
+ reason = (await hellbot.input(message)).split(" ", 1)[1].strip()
+ else:
+ return await hellbot.delete(
+ message, "๐ญ๐พ๐พ๐ฝ ๐บ ๐๐๐พ๐๐๐บ๐๐พ/๐๐ฝ ๐๐ ๐๐พ๐๐
๐ ๐๐ ๐บ ๐๐๐พ๐ ๐๐ ๐๐๐๐พ ๐๐๐พ๐!"
+ )
+
+ if await db.is_muted(client.me.id, user.id, message.chat.id):
+ return await hellbot.delete(message, "This user is already dmuted.")
+
+ reason = reason if reason else "Not Specified"
+ await db.add_mute(client.me.id, user.id, message.chat.id, reason)
+ await hellbot.delete(
+ message,
+ f"**๐ค ๐ฌ๐๐๐พ๐ฝ {user.mention} ๐ฒ๐๐ผ๐ผ๐พ๐๐๐ฟ๐๐
๐
๐!**\n**๐ฑ๐พ๐บ๐๐๐:** `{reason}`",
+ 30,
+ )
+ await hellbot.check_and_log(
+ "dmute",
+ f"**D-Muted User**\n\n**User:** {user.mention}\n**User ID:** `{user.id}`\n**Reason:** `{reason}`\n**Admin:** `{message.from_user.mention}`\n**Group:** `{message.chat.title or message.chat.first_name}`\n**Group ID:** `{message.chat.id}`",
+ )
+
+
+@on_message("undmute", allow_stan=True)
+async def undmute(client: Client, message: Message):
+ if len(message.command) < 2 and not message.reply_to_message:
+ return await hellbot.delete(
+ message, "๐ญ๐พ๐พ๐ฝ ๐บ ๐๐๐พ๐๐๐บ๐๐พ/๐๐ฝ ๐๐ ๐๐พ๐๐
๐ ๐๐ ๐บ ๐๐๐พ๐ ๐๐ ๐๐๐๐๐๐พ ๐๐๐พ๐!"
+ )
+
+ if message.reply_to_message:
+ user = message.reply_to_message.from_user
+ else:
+ user = await client.get_users(message.command[1])
+
+ if not await db.is_muted(client.me.id, user.id, message.chat.id):
+ return await hellbot.delete(message, "๐ณ๐๐พ ๐๐๐พ๐ ๐๐ ๐๐๐ ๐๐๐๐พ๐ฝ!")
+
+ reason = await db.rm_mute(client.me.id, user.id, message.chat.id)
+ await hellbot.delete(
+ message,
+ f"**๐ ๐ด๐๐๐๐๐พ๐ฝ {user.mention} ๐ฒ๐๐ผ๐ผ๐พ๐๐๐ฟ๐๐
๐
๐!**\n\n**Mute reason was:** `{reason}`",
+ 30,
+ )
+ await hellbot.check_and_log(
+ "unmute",
+ f"**D-Unmuted User**\n\n**User:** {user.mention}\n**User ID:** `{user.id}`\n**Admin:** `{message.from_user.mention}`\n**Group:** `{message.chat.title}`\n**Group ID:** `{message.chat.id}`",
+ )
+
+
+@on_message(
+ "pin",
+ chat_type=group_only,
+ admin_only=True,
+ allow_stan=True,
+)
+async def pin(_, message: Message):
+ if not message.reply_to_message:
+ return await hellbot.delete(message, "๐ญ๐พ๐พ๐ฝ ๐บ ๐๐พ๐๐
๐ ๐๐ ๐๐๐ ๐บ ๐๐พ๐๐๐บ๐๐พ!")
+
+ try:
+ await message.reply_to_message.pin()
+ except Exception as e:
+ return await hellbot.error(message, e)
+
+ await hellbot.delete(
+ message,
+ f"**๐ ๐ฏ๐๐๐๐พ๐ฝ [๐ฌ๐พ๐๐๐บ๐๐พ]({message.reply_to_message.link}) ๐๐ {message.chat.title}!**",
+ 30,
+ )
+ await hellbot.check_and_log(
+ "pin",
+ f"**Pinned Message**\n\n**Message:** [Click Here]({message.reply_to_message.link})\n**Admin:** `{message.from_user.mention}`\n**Group:** `{message.chat.title}`\n**Group ID:** `{message.chat.id}`",
+ )
+
+
+@on_message(
+ "unpin",
+ chat_type=group_only,
+ admin_only=True,
+ allow_stan=True,
+)
+async def unpin(_, message: Message):
+ if not message.reply_to_message:
+ return await hellbot.delete(message, "๐ญ๐พ๐พ๐ฝ ๐บ ๐๐พ๐๐
๐ ๐๐ ๐๐๐๐๐ ๐บ ๐๐พ๐๐๐บ๐๐พ!")
+
+ try:
+ await message.reply_to_message.unpin()
+ except Exception as e:
+ return await hellbot.error(message, e)
+
+ await hellbot.delete(
+ message,
+ f"**๐ ๐ด๐๐๐๐๐๐พ๐ฝ [๐ฌ๐พ๐๐๐บ๐๐พ]({message.reply_to_message.link}) ๐๐ {message.chat.title}!**",
+ 30,
+ )
+ await hellbot.check_and_log(
+ "unpin",
+ f"**Unpinned Message**\n\n**Message:** [Click Here]({message.reply_to_message.link})\n**Admin:** `{message.from_user.mention}`\n**Group:** `{message.chat.title}`\n**Group ID:** `{message.chat.id}`",
+ )
+
+
+@on_message(
+ "zombies",
+ chat_type=group_only,
+ admin_only=True,
+ allow_stan=True,
+)
+async def zombies(_, message: Message):
+ hell = await hellbot.edit(message, "โ ๏ธ ๐ฃ๐พ๐๐พ๐ผ๐๐๐๐ ๐๐๐๐ป๐๐พ๐...")
+ ded_users = []
+ async for members in message.chat.get_members():
+ if members.user.is_deleted:
+ ded_users.append(members.user.id)
+
+ if not ded_users:
+ return await hell.edit(
+ "๐ซก ๐ฃ๐๐'๐ ๐๐บ๐๐พ ๐บ๐๐ ๐๐๐๐ป๐๐พ๐ ๐๐ ๐๐๐๐ ๐๐๐๐๐. **๐ฆ๐๐๐๐๐' ๐ผ๐
๐พ๐บ๐ ๐ ๐ฅ!**"
+ )
+
+ if len(message.command) > 1 and message.command[1].lower() == "clean":
+ await hell.edit(
+ f"โ ๏ธ ๐ฅ๐๐๐๐ฝ {len(ded_users)} ๐๐๐๐ป๐๐พ๐... **๐ซ ๐ณ๐๐๐พ ๐๐ ๐๐๐๐๐พ ๐๐๐พ๐!**"
+ )
+ failed = 0
+ success = 0
+ for user in ded_users:
+ try:
+ await message.chat.ban_member(user)
+ success += 1
+ except Exception as e:
+ LOGS.error(e)
+ failed += 1
+
+ await hell.edit(f"**๐ฏ๐๐๐๐พ๐ฝ {success} ๐๐๐๐ป๐๐พ๐!**\n`{failed}` holds immunity!")
+ else:
+ await hell.edit(
+ f"**โ ๏ธ ๐ฅ๐๐๐๐ฝ {len(ded_users)} ๐๐๐๐ป๐๐พ๐!**\n\n__Use__ `{handler}zombies clean` __to kill them!__"
+ )
+
+
+@custom_handler(filters.incoming)
+async def multiple_handler(client: Client, message: Message):
+ if not message.from_user:
+ return
+
+ if await db.is_muted(client.me.id, message.from_user.id, message.chat.id):
+ try:
+ await message.delete()
+ except:
+ pass
+
+ elif await db.is_gmuted(message.from_user.id):
+ try:
+ await message.delete()
+ except:
+ pass
+
+ elif await db.is_echo(client.me.id, message.chat.id, message.from_user.id):
+ await message.copy(message.chat.id, reply_to_message_id=message.id)
+
+
+HelpMenu("admin").add(
+ "promote",
+ "<๐๐๐พ๐๐๐บ๐๐พ/๐๐ฝ/reply> <๐๐๐๐
๐พ>",
+ "Promote a user to admin.",
+ "promote @ForGo10God hellboy",
+).add(
+ "demote", "", "Demote a user from admin.", "demote @ForGo10God"
+).add(
+ "ban",
+ " ",
+ "Ban a user from the group.",
+ "ban @ForGo10God",
+ "You can also use dban to delete the message of the user.",
+).add(
+ "unban", "", "Unban a user from the group.", "unban @ForGo10God"
+).add(
+ "kick",
+ " ",
+ "Kick a user from the group.",
+ "kick @ForGo10God",
+ "You can also use dkick to delete the message of the user.",
+).add(
+ "mute",
+ " ",
+ "Mute a user in the group",
+ "mute @ForGo10God",
+ "You can also use dmute to delete the message of the user.",
+).add(
+ "unmute", "", "Unmute a user in the group.", "unmute @ForGo10God"
+).add(
+ "dmute",
+ "",
+ "Mute a user by deleting their new messages in the group.",
+ "dmute @ForGo10God",
+ "Need delete message permission for proper functioning.",
+).add(
+ "undmute",
+ "",
+ "Unmute a user who's muted using 'dmute' command in the group.",
+ "undmute @ForGo10God",
+).add(
+ "pin", "", "Pin the replied message in the group."
+).add(
+ "unpin", "", "Unpin the replied pinned message in the group."
+).add(
+ "zombies",
+ "clean",
+ "Finds the total number of deleted users present in that group and ban them.",
+).info(
+ "Admin Menu"
+).done()
diff --git a/Hellbot/plugins/user/afk.py b/Hellbot/plugins/user/afk.py
new file mode 100644
index 0000000000000000000000000000000000000000..4f67181865ec43a5145507073e21692a33c54909
--- /dev/null
+++ b/Hellbot/plugins/user/afk.py
@@ -0,0 +1,157 @@
+import os
+import random
+import time
+
+from pyrogram import Client, filters
+from pyrogram.enums import MessageMediaType
+from pyrogram.types import Message
+
+from Hellbot.core import Config, db, hellbot
+from Hellbot.functions.formatter import add_to_dict, get_from_dict, readable_time
+
+from . import HelpMenu, custom_handler, group_only, on_message
+
+afk_quotes = [
+ "๐ถโโ๏ธ Taking a break, be back soon!",
+ "โณ AFK - Away From the Keyboard momentarily.",
+ "๐ Stepped away, but I'll return shortly.",
+ "๐ Gone for a moment, not forgotten.",
+ "๐ฟ Taking a breather, back in a bit.",
+ "๐ต Away for a while, feel free to leave a message!",
+ "โฐ On a short break, back shortly.",
+ "๐ Away from the screen, catching a breath.",
+ "๐ค Offline for a moment, but still here in spirit.",
+ "๐ Exploring the real world, back in a moment!",
+ "๐ต Taking a tea break, back shortly!",
+ "๐ Resting my keyboard, back after a short nap.",
+ "๐ถโโ๏ธ Stepping away for a moment of peace.",
+ "๐ต AFK but humming along, back shortly!",
+ "๐ Taking a sunshine break, back soon!",
+ "๐ Away, catching some waves of relaxation.",
+ "๐ช Temporarily closed, be back in a bit!",
+ "๐ธ Taking a moment to smell the digital roses.",
+ "๐ Stepped into the real world for a while.",
+]
+
+
+@on_message("afk")
+async def afk(_, message: Message):
+ if await db.is_afk(message.from_user.id):
+ return await hellbot.delete(message, "๐ ๐จ'๐ ๐บ๐
๐๐พ๐บ๐ฝ๐ ๐ ๐ฅ๐ช!")
+
+ media_type = None
+ media = None
+
+ if message.reply_to_message and message.reply_to_message.media:
+ if message.reply_to_message.media == MessageMediaType.ANIMATION:
+ media_type = "animation"
+ elif message.reply_to_message.media == MessageMediaType.AUDIO:
+ media_type = "audio"
+ elif message.reply_to_message.media == MessageMediaType.PHOTO:
+ media_type = "photo"
+ elif message.reply_to_message.media == MessageMediaType.STICKER:
+ media_type = "sticker"
+ elif message.reply_to_message.media == MessageMediaType.VIDEO:
+ media_type = "video"
+ elif message.reply_to_message.media == MessageMediaType.VOICE:
+ media_type = "voice"
+
+ media = await message.reply_to_message.forward(Config.LOGGER_ID)
+
+ reason = await hellbot.input(message)
+ reason = reason if reason else "Not specified"
+
+ await db.set_afk(
+ message.from_user.id, reason, media.id if media else None, media_type
+ )
+ await hellbot.delete(message, "๐ซก ๐ฆ๐๐๐๐ ๐ ๐ฅ๐ช! ๐ฒ๐พ๐พ ๐๐บ'๐
๐
๐
๐บ๐๐พ๐.")
+ await hellbot.check_and_log(
+ "afk",
+ f"Going AFK! \n\n**Reason:** `{reason}`",
+ )
+ add_to_dict(Config.AFK_CACHE, [message.from_user.id, message.chat.id])
+
+
+@custom_handler(filters.incoming & ~filters.bot & ~filters.service)
+async def afk_watch(client: Client, message: Message):
+ afk_data = await db.get_afk(client.me.id)
+ if not afk_data:
+ return
+
+ if message.from_user.id == afk_data["user_id"]:
+ return
+
+ if message.chat.type in group_only:
+ if not message.mentioned:
+ return
+
+ afk_time = readable_time(round(time.time() - afk_data["time"]))
+ caption = f"**{random.choice(afk_quotes)}**\n\n**๐ซ ๐ฑ๐พ๐บ๐๐๐:** {afk_data['reason']}\n**โฐ ๐ ๐ฅ๐ช ๐ฅ๐๐๐:** `{afk_time}`"
+
+ if afk_data["media_type"] == "animation":
+ media = await client.get_messages(Config.LOGGER_ID, afk_data["media"])
+ sent = await client.send_animation(
+ message.chat.id, media.animation.file_id, caption, True
+ )
+
+ elif afk_data["media_type"] in ["audio", "photo", "video", "voice"]:
+ sent = await client.copy_message(
+ message.chat.id,
+ Config.LOGGER_ID,
+ afk_data["media"],
+ caption,
+ reply_to_message_id=message.id,
+ )
+
+ elif afk_data["media_type"] == "sticker":
+ media = await client.get_messages(Config.LOGGER_ID, afk_data["media"])
+ await client.download_media(media, "afk.png")
+ sent = await message.reply_photo("afk.png", caption=caption)
+ os.remove("afk.png")
+
+ else:
+ sent = await message.reply_text(caption)
+
+ link = message.link if message.chat.type in group_only else "No DM Link"
+
+ await hellbot.check_and_log(
+ "afk",
+ f"{message.from_user.mention} mentioned you when you were AFK! \n\n**Link:** {link}",
+ )
+ try:
+ data = get_from_dict(Config.AFK_CACHE, [afk_data["user_id"], message.chat.id])
+ if data:
+ await client.delete_messages(message.chat.id, data)
+ add_to_dict(Config.AFK_CACHE, [afk_data["user_id"], message.chat.id], sent.id)
+ except KeyError:
+ add_to_dict(Config.AFK_CACHE, [afk_data["user_id"], message.chat.id], sent.id)
+
+
+@custom_handler(filters.outgoing, 2)
+async def remove_afk(_, message: Message):
+ if await db.is_afk(message.from_user.id):
+ if "afk" in message.text:
+ return
+
+ data = await db.get_afk(message.from_user.id)
+ total_afk_time = readable_time(round(time.time() - data["time"]))
+
+ hell = await message.reply_text(
+ f"๐ซก **๐ก๐บ๐ผ๐ ๐๐ ๐๐๐๐๐๐บ๐
๐๐๐๐
๐ฝ! \n\nโ Was away for:** `{total_afk_time}`"
+ )
+ await message.delete()
+
+ await db.rm_afk(message.from_user.id)
+ await hellbot.check_and_log(
+ "afk",
+ f"Returned from AFK! \n\n**Time:** `{total_afk_time}`\n**Link:** {hell.link}",
+ )
+
+
+HelpMenu("afk").add(
+ "afk",
+ "",
+ "Set your status as AFK. When someone mentions' you, the bot will tell them you're currently Offline! You can also use a media by replying to it.",
+ "afk good night!",
+ "To unset afk you can send a message to any chat and it'll automaticslly get disabled! You can use 'afk' in your message to bypass automatic disabling of afk.",
+).info("Away From Keyboard").done()
diff --git a/Hellbot/plugins/user/anime.py b/Hellbot/plugins/user/anime.py
new file mode 100644
index 0000000000000000000000000000000000000000..34fb52fefc4b5c0720eb332991d8217056a1346b
--- /dev/null
+++ b/Hellbot/plugins/user/anime.py
@@ -0,0 +1,184 @@
+import os
+
+from pyrogram.errors import ChatSendMediaForbidden
+from pyrogram.types import Message
+
+from Hellbot.core import hellbot
+from Hellbot.functions.scraping import (
+ get_airing_info,
+ get_anilist_user_info,
+ get_anime_info,
+ get_character_info,
+ get_filler_info,
+ get_manga_info,
+ get_watch_order,
+)
+
+from . import HelpMenu, on_message
+
+
+@on_message("anime", allow_stan=True)
+async def anime(_, message: Message):
+ if len(message.command) < 2:
+ return await hellbot.delete(message, "Give me an anime name to search!")
+
+ query = await hellbot.input(message)
+ hell = await hellbot.edit(message, "Searching ...")
+ caption, photo = await get_anime_info(query)
+
+ try:
+ await message.reply_photo(photo, caption=caption)
+ await hell.delete()
+ except ChatSendMediaForbidden:
+ await hell.edit(caption, disable_web_page_preview=True)
+
+ if os.path.exists(photo):
+ os.remove(photo)
+
+
+@on_message("manga", allow_stan=True)
+async def manga(_, message: Message):
+ if len(message.command) < 2:
+ return await hellbot.delete(message, "Give me a manga name to search!")
+
+ query = await hellbot.input(message)
+ hell = await hellbot.edit(message, "Searching ...")
+ caption, photo = await get_manga_info(query)
+
+ try:
+ await message.reply_photo(photo, caption=caption)
+ await hell.delete()
+ except ChatSendMediaForbidden:
+ await hell.edit(caption, disable_web_page_preview=True)
+
+ if os.path.exists(photo):
+ os.remove(photo)
+
+
+@on_message("character", allow_stan=True)
+async def character(_, message: Message):
+ if len(message.command) < 2:
+ return await hellbot.delete(message, "Give me a character name to search!")
+
+ query = await hellbot.input(message)
+ hell = await hellbot.edit(message, "Searching ...")
+ caption, photo = await get_character_info(query)
+
+ try:
+ await message.reply_photo(photo, caption=caption)
+ await hell.delete()
+ except ChatSendMediaForbidden:
+ await hell.edit(caption, disable_web_page_preview=True)
+
+ if os.path.exists(photo):
+ os.remove(photo)
+
+
+@on_message("airing", allow_stan=True)
+async def airing(_, message: Message):
+ if len(message.command) < 2:
+ return await hellbot.delete(message, "Give me an anime name to search!")
+
+ query = await hellbot.input(message)
+ hell = await hellbot.edit(message, "Searching ...")
+ caption, photo = await get_airing_info(query)
+
+ try:
+ await message.reply_photo(photo, caption=caption)
+ await hell.delete()
+ except ChatSendMediaForbidden:
+ await hell.edit(caption, disable_web_page_preview=True)
+
+ if os.path.exists(photo):
+ os.remove(photo)
+
+
+@on_message(["anilistuser", "aniuser"], allow_stan=True)
+async def anilist_user(_, message: Message):
+ if len(message.command) < 2:
+ return await hellbot.delete(message, "Give me an anilist username to search!")
+
+ query = await hellbot.input(message)
+ hell = await hellbot.edit(message, "Searching ...")
+ caption, photo = await get_anilist_user_info(query)
+
+ try:
+ await message.reply_photo(photo, caption=caption)
+ await hell.delete()
+ except ChatSendMediaForbidden:
+ await hell.edit(caption, disable_web_page_preview=True)
+
+ if os.path.exists(photo):
+ os.remove(photo)
+
+
+@on_message(["filler", "canon"], allow_stan=True)
+async def fillers(_, message: Message):
+ if len(message.command) < 2:
+ return await hellbot.delete(message, "Give me an anime name to search!")
+
+ query = await hellbot.input(message)
+ hell = await hellbot.edit(message, "Searching ...")
+
+ caption = await get_filler_info(query)
+ if caption == "":
+ return await hellbot.delete(hell, "No results found!")
+
+ await hell.edit(caption, disable_web_page_preview=True)
+
+
+@on_message("watchorder", allow_stan=True)
+async def watch_order(_, message: Message):
+ if len(message.command) < 2:
+ return await hellbot.delete(message, "Give me an anime name to search!")
+
+ query = await hellbot.input(message)
+ hell = await hellbot.edit(message, "Searching ...")
+
+ caption = await get_watch_order(query)
+ if caption == "":
+ return await hellbot.delete(hell, "No results found!")
+
+ await hell.edit(caption, disable_web_page_preview=True)
+
+
+HelpMenu("anime").add(
+ "anime",
+ "",
+ "Get a detailed information about the mentioned anime.",
+ "anime one piece",
+).add(
+ "manga",
+ "",
+ "Get a detailed information about the mentioned manga.",
+ "manga one piece",
+).add(
+ "character",
+ "",
+ "Get a detailed information about the mentioned character.",
+ "character monkey d luffy",
+).add(
+ "airing",
+ "",
+ "Get a detailed airing information about the mentioned anime.",
+ "airing one piece",
+).add(
+ "anilistuser",
+ "",
+ "Get a detailed information about the mentioned anilist user.",
+ "anilistuser meizhellboy",
+ "You can also use 'aniuser' as alias",
+).add(
+ "filler",
+ "",
+ "Get the list of filler/canon episodes about the mentioned anime.",
+ "filler one piece",
+ "You can also use 'canon' as alias",
+).add(
+ "watchorder",
+ "",
+ "Get the watch order about the mentioned anime.",
+ "watchorder one piece",
+).info(
+ "Anime Menu"
+).done()
diff --git a/Hellbot/plugins/user/antiflood.py b/Hellbot/plugins/user/antiflood.py
new file mode 100644
index 0000000000000000000000000000000000000000..bf790aa4d52d72606327dd6ff3b4a81010aeaa4d
--- /dev/null
+++ b/Hellbot/plugins/user/antiflood.py
@@ -0,0 +1,200 @@
+import asyncio
+import datetime
+import time
+
+from pyrogram import Client, filters
+from pyrogram.types import ChatPermissions, Message
+
+from Hellbot.core import Config, Symbols, db
+from Hellbot.functions.utility import Flood
+
+from . import HelpMenu, custom_handler, group_only, hellbot, on_message
+
+
+@on_message("setflood", chat_type=group_only, admin_only=True, allow_stan=True)
+async def setflood(client: Client, message: Message):
+ count = 5
+ mtime = 0
+ mode = "mute"
+
+ try:
+ time_data = "N/A"
+ if len(message.command) == 2:
+ count = int(message.command[1])
+ elif len(message.command) == 3:
+ count = int(message.command[1])
+ mode = message.command[2]
+ elif len(message.command) >= 4:
+ count = int(message.command[1])
+ mode = message.command[2]
+ time_data = message.command[3]
+ if time_data.endswith(("d", "day", "days")):
+ mtime = int(time_data.split("d")[0].strip()) * 24 * 60 * 60
+ elif time_data.endswith(("h", "hrs", "hour", "hours")):
+ mtime = int(time_data.split("h")[0].strip()) * 60 * 60
+ elif time_data.endswith(("m", "mins", "minute", "minutes")):
+ mtime = int(time_data.split("m")[0].strip()) * 60
+ else:
+ return await hellbot.error(
+ message,
+ "Please pass time in correct format!\n\nExample: 12d or 12h or 12m",
+ )
+ except Exception as e:
+ return await hellbot.error(message, str(e))
+
+ if mode.lower() not in ["mute", "kick", "ban"]:
+ return await hellbot.error(
+ message, "**Invalid mode! Choose one: **\n`mute`, `kick`, `ban`"
+ )
+
+ settings = {
+ "mode": mode,
+ "limit": count,
+ "time": mtime,
+ }
+
+ await db.set_flood((client.me.id, message.chat.id), settings)
+ Flood.updateSettings(client.me.id, message.chat.id, settings)
+
+ if count == 0:
+ return await hellbot.delete(message, "Antiflood disabled!")
+
+ await hellbot.delete(
+ message,
+ f"**Antiflood enabled!**\n\n**{Symbols.triangle_right} Mode:** `{mode}`\n**{Symbols.triangle_right} Limit:** `{count}`\n**{Symbols.triangle_right} Time:** `{time_data}`",
+ 20,
+ )
+
+
+@custom_handler(
+ filters.all
+ & filters.group
+ & filters.incoming
+ & ~filters.bot
+ & ~Config.AUTH_USERS
+ & ~filters.me
+ & ~filters.service
+)
+async def antiflood(client: Client, message: Message):
+ mode, mtime, limit = Flood.getSettings(client.me.id, message.chat.id)
+
+ if limit == 0:
+ return
+ if not Flood.check_client_chat(client.me.id, message.chat.id):
+ return
+
+ last_user, count = Flood.getLastUser(client.me.id, message.chat.id)
+
+ if last_user == message.from_user.id:
+ if (count + 1) >= limit:
+ template = (
+ "**๐คซ ๐ ๐๐๐๐ฅ๐
๐๐๐ฝ {mode}!!** \n\n"
+ "**{symbol} ๐ด๐๐พ๐:** `{mention}`\n"
+ "**{symbol} ๐ณ๐๐
๐
๐ฃ๐บ๐๐พ:** `๐๏ธ {till_date}`\n"
+ )
+ hell = await message.reply_text("Flood Detected!")
+
+ if mode == "mute":
+ permission = ChatPermissions(can_send_messages=False)
+ until_date = datetime.datetime.fromtimestamp(time.time() + mtime)
+ try:
+ await client.restrict_chat_member(
+ message.chat.id,
+ message.from_user.id,
+ permission,
+ until_date,
+ )
+ except Exception as e:
+ return await hellbot.error(
+ hell, f"__Error in Antiflood while trying to mute!__\n{str(e)}"
+ )
+
+ Flood.updateFlood(
+ client.me.id, message.chat.id, message.from_user.id, 0
+ )
+ till_date = "Forever" if mtime == 0 else until_date.ctime()
+
+ return await hell.edit(
+ template.format(
+ mode=mode.title(),
+ symbol=Symbols.triangle_right,
+ mention=message.from_user.mention,
+ till_date=till_date,
+ )
+ )
+
+ elif mode == "kick":
+ try:
+ await client.ban_chat_member(message.chat.id, message.from_user.id)
+ except Exception as e:
+ return await hellbot.error(
+ hell, f"__Error in Antiflood while trying to kick!__\n{str(e)}"
+ )
+
+ await hell.edit(
+ template.format(
+ mode=mode.title(),
+ symbol=Symbols.triangle_right,
+ mention=message.from_user.mention,
+ till_date="Kicked Users can join back after 5 seconds!",
+ )
+ )
+ Flood.updateFlood(
+ client.me.id, message.chat.id, message.from_user.id, 0
+ )
+ await asyncio.sleep(5)
+ await client.unban_chat_member(message.chat.id, message.from_user.id)
+ return
+
+ elif mode == "ban":
+ until_date = datetime.datetime.fromtimestamp(time.time() + mtime)
+ try:
+ await client.ban_chat_member(
+ message.chat.id,
+ message.from_user.id,
+ until_date,
+ )
+ except Exception as e:
+ return await hellbot.error(
+ hell, f"__Error in Antiflood while trying to ban!__\n{str(e)}"
+ )
+
+ Flood.updateFlood(
+ client.me.id, message.chat.id, message.from_user.id, 0
+ )
+ till_date = "Forever" if mtime == 0 else until_date.ctime()
+
+ return await hell.edit(
+ template.format(
+ mode=mode.title(),
+ symbol=Symbols.triangle_right,
+ mention=message.from_user.mention,
+ till_date=till_date,
+ )
+ )
+ else:
+ return
+ else:
+ count += 1
+ Flood.updateFlood(
+ client.me.id, message.chat.id, message.from_user.id, count
+ )
+ return
+ else:
+ Flood.updateFlood(client.me.id, message.chat.id, message.from_user.id, 1)
+
+
+HelpMenu("antiflood").add(
+ "setflood",
+ "