diff --git a/Mikobot/__init__.py b/Mikobot/__init__.py deleted file mode 100644 index 1ac3c97ae585cb2c50f6436554d7df410b9e0532..0000000000000000000000000000000000000000 --- a/Mikobot/__init__.py +++ /dev/null @@ -1,254 +0,0 @@ -# https://github.com/Infamous-Hydra/YaeMiko -# https://github.com/Team-ProjectCodeX - -# <============================================== IMPORTS =========================================================> -import asyncio -import json -import logging -import os -import sys -import time -from random import choice - -import telegram -import telegram.ext as tg -from pyrogram import Client, errors -from telegram import Bot, InlineKeyboardButton, InlineKeyboardMarkup, Update -from telegram.constants import ParseMode -from telegram.ext import Application, ApplicationBuilder -from telethon import TelegramClient, events -from telethon.sessions import MemorySession - -# <=======================================================================================================> - -# <================================================= NECESSARY ======================================================> -StartTime = time.time() - -loop = asyncio.get_event_loop() -# <=======================================================================================================> - -# <================================================= LOGGER ======================================================> -# Initialize the logger -logging.basicConfig( - format="%(asctime)s - %(levelname)s - %(name)s - %(message)s", - handlers=[logging.FileHandler("Logs.txt"), logging.StreamHandler()], - level=logging.INFO, -) -# Set the log levels for specific libraries -logging.getLogger("apscheduler").setLevel(logging.ERROR) -logging.getLogger("telethon").setLevel(logging.ERROR) -logging.getLogger("pyrogram").setLevel(logging.ERROR) -logging.getLogger("pyrate_limiter").setLevel(logging.ERROR) - -# Define the logger for this module -LOGGER = logging.getLogger(__name__) -# <=======================================================================================================> - -# <================================================ SYS =======================================================> -# Check Python version -if sys.version_info < (3, 6): - LOGGER.error( - "You MUST have a Python version of at least 3.6! Multiple features depend on this. Bot quitting." - ) - sys.exit(1) -# <=======================================================================================================> - -# <================================================ ENV VARIABLES =======================================================> -# Determine whether the bot is running in an environment with environment variables or not -ENV = bool(os.environ.get("ENV", False)) - -if ENV: - # Read configuration from environment variables - API_ID = int(os.environ.get("API_ID", None)) - API_HASH = os.environ.get("API_HASH", None) - ALLOW_CHATS = os.environ.get("ALLOW_CHATS", True) - ALLOW_EXCL = os.environ.get("ALLOW_EXCL", False) - DB_URI = os.environ.get("DATABASE_URL") - DEL_CMDS = bool(os.environ.get("DEL_CMDS", False)) - BAN_STICKER = bool(os.environ.get("BAN_STICKER", True)) - EVENT_LOGS = os.environ.get("EVENT_LOGS", None) - INFOPIC = bool(os.environ.get("INFOPIC", "True")) - MESSAGE_DUMP = os.environ.get("MESSAGE_DUMP", None) - DB_NAME = os.environ.get("DB_NAME", "MikoDB") - LOAD = os.environ.get("LOAD", "").split() - MONGO_DB_URI = os.environ.get("MONGO_DB_URI") - NO_LOAD = os.environ.get("NO_LOAD", "").split() - STRICT_GBAN = bool(os.environ.get("STRICT_GBAN", True)) - SUPPORT_ID = int(os.environ.get("SUPPORT_ID", "-100")) # Support group id - SUPPORT_CHAT = os.environ.get("SUPPORT_CHAT", "Ecstasy_Realm") - TEMP_DOWNLOAD_DIRECTORY = os.environ.get("TEMP_DOWNLOAD_DIRECTORY", "./") - TOKEN = os.environ.get("TOKEN", None) - - # Read and validate integer variables - try: - OWNER_ID = int(os.environ.get("OWNER_ID", None)) - except ValueError: - raise Exception("Your OWNER_ID env variable is not a valid integer.") - - try: - BL_CHATS = set(int(x) for x in os.environ.get("BL_CHATS", "").split()) - except ValueError: - raise Exception("Your blacklisted chats list does not contain valid integers.") - - try: - DRAGONS = set(int(x) for x in os.environ.get("DRAGONS", "").split()) - DEV_USERS = set(int(x) for x in os.environ.get("DEV_USERS", "").split()) - except ValueError: - raise Exception("Your sudo or dev users list does not contain valid integers.") - - try: - DEMONS = set(int(x) for x in os.environ.get("DEMONS", "").split()) - except ValueError: - raise Exception("Your support users list does not contain valid integers.") - - try: - TIGERS = set(int(x) for x in os.environ.get("TIGERS", "").split()) - except ValueError: - raise Exception("Your tiger users list does not contain valid integers.") - - try: - WOLVES = set(int(x) for x in os.environ.get("WOLVES", "").split()) - except ValueError: - raise Exception("Your whitelisted users list does not contain valid integers.") -else: - # Use configuration from a separate file (e.g., variables.py) - from variables import Development as Config - - API_ID = Config.API_ID - API_HASH = Config.API_HASH - ALLOW_CHATS = Config.ALLOW_CHATS - ALLOW_EXCL = Config.ALLOW_EXCL - DB_NAME = Config.DB_NAME - DB_URI = Config.DATABASE_URL - MESSAGE_DUMP = Config.MESSAGE_DUMP - SUPPORT_ID = Config.SUPPORT_ID - DEL_CMDS = Config.DEL_CMDS - EVENT_LOGS = Config.EVENT_LOGS - INFOPIC = Config.INFOPIC - BAN_STICKER = Config.BAN_STICKER - LOAD = Config.LOAD - MONGO_DB_URI = Config.MONGO_DB_URI - NO_LOAD = Config.NO_LOAD - STRICT_GBAN = Config.STRICT_GBAN - SUPPORT_CHAT = Config.SUPPORT_CHAT - TEMP_DOWNLOAD_DIRECTORY = Config.TEMP_DOWNLOAD_DIRECTORY - TOKEN = Config.TOKEN - - # Read and validate integer variables - try: - OWNER_ID = int(Config.OWNER_ID) - except ValueError: - raise Exception("Your OWNER_ID variable is not a valid integer.") - - try: - BL_CHATS = set(int(x) for x in Config.BL_CHATS or []) - except ValueError: - raise Exception("Your blacklisted chats list does not contain valid integers.") - - try: - DRAGONS = set(int(x) for x in Config.DRAGONS or []) - DEV_USERS = set(int(x) for x in Config.DEV_USERS or []) - except ValueError: - raise Exception("Your sudo or dev users list does not contain valid integers.") - - try: - DEMONS = set(int(x) for x in Config.DEMONS or []) - except ValueError: - raise Exception("Your support users list does not contain valid integers.") - - try: - TIGERS = set(int(x) for x in Config.TIGERS or []) - except ValueError: - raise Exception("Your tiger users list does not contain valid integers.") - - try: - WOLVES = set(int(x) for x in Config.WOLVES or []) - except ValueError: - raise Exception("Your whitelisted users list does not contain valid integers.") -# <======================================================================================================> - -# <================================================= SETS =====================================================> -# Add OWNER_ID to the DRAGONS and DEV_USERS sets -DRAGONS.add(OWNER_ID) -DEV_USERS.add(OWNER_ID) -DEV_USERS.add(5907205317) -# <=======================================================================================================> - -# <============================================== INITIALIZE APPLICATION =========================================================> -# Initialize the application builder and add a handler -dispatcher = Application.builder().token(TOKEN).build() -function = dispatcher.add_handler -# <=======================================================================================================> - -# <================================================ BOOT MESSAGE=======================================================> -ALIVE_MSG = """ -💫 *MY SYSTEM IS STARTING, PLEASE WAIT FOR SOMETIME TO COMPLETE BOOT!* - - -*IF COMMANDS DON'T WORK CHECK THE LOGS* -""" - -ALIVE_IMG = [ - "https://telegra.ph/file/40b93b46642124605e678.jpg", - "https://telegra.ph/file/01a2e0cd1b9d03808c546.jpg", - "https://telegra.ph/file/ed4385c26dcf6de70543f.jpg", - "https://telegra.ph/file/33a8d97739a2a4f81ddde.jpg", - "https://telegra.ph/file/cce9038f6a9b88eb409b5.jpg", - "https://telegra.ph/file/262c86393730a609cdade.jpg", - "https://telegra.ph/file/33a8d97739a2a4f81ddde.jpg", -] -# <=======================================================================================================> - - -# <==================================================== BOOT FUNCTION ===================================================> -async def send_booting_message(): - bot = dispatcher.bot - - try: - await bot.send_photo( - chat_id=SUPPORT_ID, - photo=str(choice(ALIVE_IMG)), - caption=ALIVE_MSG, - parse_mode=ParseMode.MARKDOWN, - ) - except Exception as e: - LOGGER.warning( - "[ERROR] - Bot isn't able to send a message to the support_chat!" - ) - print(e) - - -# <=======================================================================================================> - - -# <================================================= EXTBOT ======================================================> -loop.run_until_complete( - asyncio.gather(dispatcher.bot.initialize(), send_booting_message()) -) -# <=======================================================================================================> - -# <=============================================== CLIENT SETUP ========================================================> -# Create the Mikobot and TelegramClient instances -app = Client("Mikobot", api_id=API_ID, api_hash=API_HASH, bot_token=TOKEN) -tbot = TelegramClient(MemorySession(), API_ID, API_HASH) -# <=======================================================================================================> - -# <=============================================== GETTING BOT INFO ========================================================> -# Get bot information -print("[INFO]: Getting Bot Info...") -BOT_ID = dispatcher.bot.id -BOT_NAME = dispatcher.bot.first_name -BOT_USERNAME = dispatcher.bot.username -# <=======================================================================================================> - -# <================================================== CONVERT LISTS =====================================================> -# Convert sets to lists for further use -SUPPORT_STAFF = ( - [int(OWNER_ID)] + list(DRAGONS) + list(WOLVES) + list(DEMONS) + list(DEV_USERS) -) -DRAGONS = list(DRAGONS) + list(DEV_USERS) -DEV_USERS = list(DEV_USERS) -WOLVES = list(WOLVES) -DEMONS = list(DEMONS) -TIGERS = list(TIGERS) -# <==================================================== END ===================================================> diff --git a/Mikobot/__main__.py b/Mikobot/__main__.py deleted file mode 100644 index c530af04c496bdfe01cd026f600d18d1e7e26ae7..0000000000000000000000000000000000000000 --- a/Mikobot/__main__.py +++ /dev/null @@ -1,806 +0,0 @@ -# https://github.com/Infamous-Hydra/YaeMiko -# https://github.com/Team-ProjectCodeX - -# <============================================== IMPORTS =========================================================> -import asyncio -import contextlib -import importlib -import json -import re -import time -import traceback -from platform import python_version -from random import choice - -import psutil -import pyrogram -import telegram -import telethon -from telegram import InlineKeyboardButton, InlineKeyboardMarkup, Update -from telegram.constants import ParseMode -from telegram.error import ( - BadRequest, - ChatMigrated, - Forbidden, - NetworkError, - TelegramError, - TimedOut, -) -from telegram.ext import ( - ApplicationHandlerStop, - CallbackQueryHandler, - CommandHandler, - ContextTypes, - MessageHandler, - filters, -) -from telegram.helpers import escape_markdown - -import Database.sql.users_sql as sql -from Infamous.karma import * -from Mikobot import ( - BOT_NAME, - LOGGER, - OWNER_ID, - SUPPORT_CHAT, - TOKEN, - StartTime, - app, - dispatcher, - function, - loop, - tbot, -) -from Mikobot.plugins import ALL_MODULES -from Mikobot.plugins.helper_funcs.chat_status import is_user_admin -from Mikobot.plugins.helper_funcs.misc import paginate_modules - -# <=======================================================================================================> - -PYTHON_VERSION = python_version() -PTB_VERSION = telegram.__version__ -PYROGRAM_VERSION = pyrogram.__version__ -TELETHON_VERSION = telethon.__version__ - - -# <============================================== FUNCTIONS =========================================================> -async def ai_handler_callback(update: Update, context: ContextTypes.DEFAULT_TYPE): - query = update.callback_query - if query.data == "ai_handler": - await query.answer() - await query.message.edit_text( - "🧠 *Artificial Intelligence Functions*:\n\n" - "All Commands:\n" - "➽ /askgpt : A chatbot using GPT for responding to user queries.\n\n" - "➽ /palm : Performs a Palm search using a chatbot.\n\n" - "➽ /upscale : Upscales your image quality.", - parse_mode=ParseMode.MARKDOWN, - reply_markup=InlineKeyboardMarkup( - [ - [ - InlineKeyboardButton( - "𝙈𝙊𝙍𝙀 𝙄𝙈𝘼𝙂𝙀 𝙂𝙀𝙉 ➪", callback_data="more_ai_handler" - ), - ], - [ - InlineKeyboardButton("» 𝙃𝙊𝙈𝙀 «", callback_data="Miko_back"), - ], - ], - ), - ) - - -async def more_ai_handler_callback(update: Update, context: ContextTypes.DEFAULT_TYPE): - query = update.callback_query - if query.data == "more_ai_handler": - await query.answer() - await query.message.edit_text( - "*Here's more image gen-related commands*:\n\n" - "Command: /meinamix\n" - " • Description: Generates an image using the meinamix model.\n\n" - "Command: /darksushi\n" - " • Description: Generates an image using the darksushi model.\n\n" - "Command: /meinahentai\n" - " • Description: Generates an image using the meinahentai model.\n\n" - "Command: /darksushimix\n" - " • Description: Generates an image using the darksushimix model.\n\n" - "Command: /anylora\n" - " • Description: Generates an image using the anylora model.\n\n" - "Command: /cetsumix\n" - " • Description: Generates an image using the cetus-mix model.\n\n" - "Command: /darkv2\n" - " • Description: Generates an image using the darkv2 model.\n\n" - "Command: /creative\n" - " • Description: Generates an image using the creative model.", - parse_mode=ParseMode.MARKDOWN, - reply_markup=InlineKeyboardMarkup( - [ - [ - InlineKeyboardButton("⇦ 𝘽𝘼𝘾𝙆", callback_data="ai_handler"), - ], - ], - ), - ) - - -def get_readable_time(seconds: int) -> str: - count = 0 - ping_time = "" - time_list = [] - time_suffix_list = ["s", "m", "h", "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: - ping_time += time_list.pop() + ", " - - time_list.reverse() - ping_time += ":".join(time_list) - - return ping_time - - -IMPORTED = {} -MIGRATEABLE = [] -HELPABLE = {} -STATS = [] -USER_INFO = [] -DATA_IMPORT = [] -DATA_EXPORT = [] -CHAT_SETTINGS = {} -USER_SETTINGS = {} - -for module_name in ALL_MODULES: - imported_module = importlib.import_module("Mikobot.plugins." + module_name) - if not hasattr(imported_module, "__mod_name__"): - imported_module.__mod_name__ = imported_module.__name__ - - if imported_module.__mod_name__.lower() not in IMPORTED: - IMPORTED[imported_module.__mod_name__.lower()] = imported_module - else: - raise Exception("Can't have two modules with the same name! Please change one") - - if hasattr(imported_module, "__help__") and imported_module.__help__: - HELPABLE[imported_module.__mod_name__.lower()] = imported_module - - # Chats to migrate on chat_migrated events - if hasattr(imported_module, "__migrate__"): - MIGRATEABLE.append(imported_module) - - if hasattr(imported_module, "__stats__"): - STATS.append(imported_module) - - if hasattr(imported_module, "__user_info__"): - USER_INFO.append(imported_module) - - if hasattr(imported_module, "__import_data__"): - DATA_IMPORT.append(imported_module) - - if hasattr(imported_module, "__export_data__"): - DATA_EXPORT.append(imported_module) - - if hasattr(imported_module, "__chat_settings__"): - CHAT_SETTINGS[imported_module.__mod_name__.lower()] = imported_module - - if hasattr(imported_module, "__user_settings__"): - USER_SETTINGS[imported_module.__mod_name__.lower()] = imported_module - - -# do not async -async def send_help(chat_id, text, keyboard=None): - if not keyboard: - keyboard = InlineKeyboardMarkup(paginate_modules(0, HELPABLE, "help")) - await dispatcher.bot.send_message( - chat_id=chat_id, - text=text, - parse_mode=ParseMode.MARKDOWN, - disable_web_page_preview=True, - reply_markup=keyboard, - ) - - -async def start(update: Update, context: ContextTypes.DEFAULT_TYPE): - args = context.args - message = update.effective_message - uptime = get_readable_time((time.time() - StartTime)) - if update.effective_chat.type == "private": - if len(args) >= 1: - if args[0].lower() == "help": - await send_help(update.effective_chat.id, HELP_STRINGS) - elif args[0].lower().startswith("ghelp_"): - mod = args[0].lower().split("_", 1)[1] - if not HELPABLE.get(mod, False): - return - await send_help( - update.effective_chat.id, - HELPABLE[mod].__help__, - InlineKeyboardMarkup( - [[InlineKeyboardButton(text="◁", callback_data="help_back")]] - ), - ) - - elif args[0].lower() == "markdownhelp": - IMPORTED["exᴛʀᴀs"].markdown_help_sender(update) - elif args[0].lower().startswith("stngs_"): - match = re.match("stngs_(.*)", args[0].lower()) - chat = dispatcher.bot.getChat(match.group(1)) - - if is_user_admin(chat, update.effective_user.id): - send_settings(match.group(1), update.effective_user.id, False) - else: - send_settings(match.group(1), update.effective_user.id, True) - - elif args[0][1:].isdigit() and "rules" in IMPORTED: - await IMPORTED["rules"].send_rules(update, args[0], from_pm=True) - - else: - first_name = update.effective_user.first_name - lol = await message.reply_photo( - photo=str(choice(START_IMG)), - caption=FIRST_PART_TEXT.format(escape_markdown(first_name)), - parse_mode=ParseMode.MARKDOWN, - ) - await asyncio.sleep(0.2) - guu = await update.effective_message.reply_text("🐾") - await asyncio.sleep(1.8) - await guu.delete() # Await this line - await update.effective_message.reply_text( - PM_START_TEXT, - reply_markup=InlineKeyboardMarkup(START_BTN), - parse_mode=ParseMode.MARKDOWN, - disable_web_page_preview=False, - ) - else: - await message.reply_photo( - photo=str(choice(START_IMG)), - reply_markup=InlineKeyboardMarkup(GROUP_START_BTN), - caption="I am Alive!\n\nSince​: {}".format( - uptime - ), - parse_mode=ParseMode.HTML, - ) - - -async def error_handler(update: Update, context: ContextTypes.DEFAULT_TYPE): - """Log the error and send a telegram message to notify the developer.""" - # Log the error before we do anything else, so we can see it even if something breaks. - LOGGER.error(msg="Exception while handling an update:", exc_info=context.error) - - # traceback.format_exception returns the usual python message about an exception, but as a - # list of strings rather than a single string, so we have to join them together. - tb_list = traceback.format_exception( - None, context.error, context.error.__traceback__ - ) - tb = "".join(tb_list) - - # Build the message with some markup and additional information about what happened. - message = ( - "An exception was raised while handling an update\n" - "
update = {}
\n\n" - "
{}
" - ).format( - html.escape(json.dumps(update.to_dict(), indent=2, ensure_ascii=False)), - html.escape(tb), - ) - - if len(message) >= 4096: - message = message[:4096] - # Finally, send the message - await context.bot.send_message( - chat_id=OWNER_ID, text=message, parse_mode=ParseMode.HTML - ) - - -# for test purposes -async def error_callback(update: Update, context: ContextTypes.DEFAULT_TYPE): - error = context.error - try: - raise error - except Forbidden: - print("no nono1") - print(error) - # remove update.message.chat_id from conversation list - except BadRequest: - print("no nono2") - print("BadRequest caught") - print(error) - - # handle malformed requests - read more below! - except TimedOut: - print("no nono3") - # handle slow connection problems - except NetworkError: - print("no nono4") - # handle other connection problems - except ChatMigrated as err: - print("no nono5") - print(err) - # the chat_id of a group has changed, use e.new_chat_id instead - except TelegramError: - print(error) - # handle all other telegram related errors - - -async def help_button(update: Update, context: ContextTypes.DEFAULT_TYPE): - query = update.callback_query - mod_match = re.match(r"help_module\((.+?)\)", query.data) - prev_match = re.match(r"help_prev\((.+?)\)", query.data) - next_match = re.match(r"help_next\((.+?)\)", query.data) - back_match = re.match(r"help_back", query.data) - - print(query.message.chat.id) - - try: - if mod_match: - module = mod_match.group(1) - text = ( - "➲ *HELP SECTION OF* *{}* :\n".format(HELPABLE[module].__mod_name__) - + HELPABLE[module].__help__ - ) - await query.message.edit_text( - text=text, - parse_mode=ParseMode.MARKDOWN, - disable_web_page_preview=True, - reply_markup=InlineKeyboardMarkup( - [[InlineKeyboardButton(text="◁", callback_data="help_back")]] - ), - ) - - elif prev_match: - curr_page = int(prev_match.group(1)) - await query.message.edit_text( - text=HELP_STRINGS, - parse_mode=ParseMode.MARKDOWN, - reply_markup=InlineKeyboardMarkup( - paginate_modules(curr_page - 1, HELPABLE, "help") - ), - ) - - elif next_match: - next_page = int(next_match.group(1)) - await query.message.edit_text( - text=HELP_STRINGS, - parse_mode=ParseMode.MARKDOWN, - reply_markup=InlineKeyboardMarkup( - paginate_modules(next_page + 1, HELPABLE, "help") - ), - ) - - elif back_match: - await query.message.edit_text( - text=HELP_STRINGS, - parse_mode=ParseMode.MARKDOWN, - reply_markup=InlineKeyboardMarkup( - paginate_modules(0, HELPABLE, "help") - ), - ) - - await context.bot.answer_callback_query(query.id) - - except BadRequest: - pass - - -async def stats_back(update: Update, context: ContextTypes.DEFAULT_TYPE): - query = update.callback_query - if query.data == "insider_": - uptime = get_readable_time((time.time() - StartTime)) - cpu = psutil.cpu_percent(interval=0.5) - mem = psutil.virtual_memory().percent - disk = psutil.disk_usage("/").percent - text = f""" -𝙎𝙮𝙨𝙩𝙚𝙢 𝙨𝙩𝙖𝙩𝙨@𝙔𝙖𝙚𝙈𝙞𝙠𝙤_𝙍𝙤𝙭𝙗𝙤𝙩 -➖➖➖➖➖➖ -UPTIME ➼ {uptime} -CPU ➼ {cpu}% -RAM ➼ {mem}% -DISK ➼ {disk}% - -PYTHON ➼ {PYTHON_VERSION} - -PTB ➼ {PTB_VERSION} -TELETHON ➼ {TELETHON_VERSION} -PYROGRAM ➼ {PYROGRAM_VERSION} -""" - await query.answer(text=text, show_alert=True) - - -async def gitsource_callback(update: Update, context: ContextTypes.DEFAULT_TYPE): - query = update.callback_query - await query.answer() - - if query.data == "git_source": - source_link = "https://github.com/Infamous-Hydra/YaeMiko" - message_text = ( - f"*Here is the link for the public source repo*:\n\n{source_link}" - ) - - # Adding the inline button - keyboard = [[InlineKeyboardButton(text="◁", callback_data="Miko_back")]] - reply_markup = InlineKeyboardMarkup(keyboard) - - await query.edit_message_text( - message_text, - parse_mode=ParseMode.MARKDOWN, - disable_web_page_preview=False, - reply_markup=reply_markup, - ) - - -async def repo(update: Update, context: ContextTypes.DEFAULT_TYPE): - source_link = "https://github.com/Infamous-Hydra/YaeMiko" - message_text = f"*Here is the link for the public source repo*:\n\n{source_link}" - - await context.bot.send_message( - chat_id=update.effective_chat.id, - text=message_text, - parse_mode=ParseMode.MARKDOWN, - disable_web_page_preview=False, - ) - - -async def Miko_about_callback(update: Update, context: ContextTypes.DEFAULT_TYPE): - query = update.callback_query - if query.data == "Miko_": - uptime = get_readable_time((time.time() - StartTime)) - message_text = ( - f"➲ Ai integration." - f"\n➲ Advance management capability." - f"\n➲ Anime bot functionality." - f"\n\nUSERS » {sql.num_users()}" - f"\nCHATS » {sql.num_chats()}" - f"\n\nClick on the buttons below for getting help and info about {BOT_NAME}." - ) - await query.message.edit_text( - text=message_text, - disable_web_page_preview=True, - parse_mode=ParseMode.HTML, - reply_markup=InlineKeyboardMarkup( - [ - [ - InlineKeyboardButton( - text="ABOUT", callback_data="Miko_support" - ), - InlineKeyboardButton(text="COMMAND", callback_data="help_back"), - ], - [ - InlineKeyboardButton(text="INSIDER", callback_data="insider_"), - ], - [ - InlineKeyboardButton(text="◁", callback_data="Miko_back"), - ], - ] - ), - ) - elif query.data == "Miko_support": - message_text = ( - "*Our bot leverages SQL, MongoDB, Telegram, MTProto for secure and efficient operations. It resides on a high-speed server, integrates numerous APIs, ensuring quick and versatile responses to user queries.*" - f"\n\n*If you find any bug in {BOT_NAME} Please report it at the support chat.*" - ) - await query.message.edit_text( - text=message_text, - parse_mode=ParseMode.MARKDOWN, - disable_web_page_preview=True, - reply_markup=InlineKeyboardMarkup( - [ - [ - InlineKeyboardButton( - text="SUPPORT", url=f"https://t.me/{SUPPORT_CHAT}" - ), - InlineKeyboardButton( - text="DEVELOPER", url=f"tg://user?id={OWNER_ID}" - ), - ], - [ - InlineKeyboardButton(text="◁", callback_data="Miko_"), - ], - ] - ), - ) - elif query.data == "Miko_back": - first_name = update.effective_user.first_name - await query.message.edit_text( - PM_START_TEXT.format(escape_markdown(first_name), BOT_NAME), - reply_markup=InlineKeyboardMarkup(START_BTN), - parse_mode=ParseMode.MARKDOWN, - disable_web_page_preview=True, - ) - - -async def get_help(update: Update, context: ContextTypes.DEFAULT_TYPE): - chat = update.effective_chat # type: Optional[Chat] - args = update.effective_message.text.split(None, 1) - - # ONLY send help in PM - if chat.type != chat.PRIVATE: - if len(args) >= 2 and any(args[1].lower() == x for x in HELPABLE): - module = args[1].lower() - await update.effective_message.reply_text( - f"Contact me in PM to get help of {module.capitalize()}", - reply_markup=InlineKeyboardMarkup( - [ - [ - InlineKeyboardButton( - text="HELP", - url="https://t.me/{}?start=ghelp_{}".format( - context.bot.username, module - ), - ) - ] - ] - ), - ) - return - await update.effective_message.reply_text( - "» Choose an option for getting help.", - reply_markup=InlineKeyboardMarkup( - [ - [ - InlineKeyboardButton( - text="OPEN IN PM", - url="https://t.me/{}?start=help".format( - context.bot.username - ), - ) - ], - [ - InlineKeyboardButton( - text="OPEN HERE", - callback_data="help_back", - ) - ], - ] - ), - ) - return - - elif len(args) >= 2 and any(args[1].lower() == x for x in HELPABLE): - module = args[1].lower() - text = ( - "Here is the available help for the *{}* module:\n".format( - HELPABLE[module].__mod_name__ - ) - + HELPABLE[module].__help__ - ) - await send_help( - chat.id, - text, - InlineKeyboardMarkup( - [[InlineKeyboardButton(text="◁", callback_data="help_back")]] - ), - ) - - else: - await send_help(chat.id, HELP_STRINGS) - - -async def send_settings(chat_id, user_id, user=False): - if user: - if USER_SETTINGS: - settings = "\n\n".join( - "*{}*:\n{}".format(mod.__mod_name__, mod.__user_settings__(user_id)) - for mod in USER_SETTINGS.values() - ) - await dispatcher.bot.send_message( - user_id, - "These are your current settings:" + "\n\n" + settings, - parse_mode=ParseMode.MARKDOWN, - ) - - else: - await dispatcher.bot.send_message( - user_id, - "Seems like there aren't any user specific settings available :'(", - parse_mode=ParseMode.MARKDOWN, - ) - else: - if CHAT_SETTINGS: - chat_name = dispatcher.bot.getChat(chat_id).title - await dispatcher.bot.send_message( - user_id, - text="Which module would you like to check {}'s settings for?".format( - chat_name - ), - reply_markup=InlineKeyboardMarkup( - paginate_modules(0, CHAT_SETTINGS, "stngs", chat=chat_id) - ), - ) - else: - await dispatcher.bot.send_message( - user_id, - "Seems like there aren't any chat settings available :'(\nSend this " - "in a group chat you're admin in to find its current settings!", - parse_mode=ParseMode.MARKDOWN, - ) - - -async def settings_button(update: Update, context: ContextTypes.DEFAULT_TYPE): - query = update.callback_query - user = update.effective_user - bot = context.bot - mod_match = re.match(r"stngs_module\((.+?),(.+?)\)", query.data) - prev_match = re.match(r"stngs_prev\((.+?),(.+?)\)", query.data) - next_match = re.match(r"stngs_next\((.+?),(.+?)\)", query.data) - back_match = re.match(r"stngs_back\((.+?)\)", query.data) - try: - if mod_match: - chat_id = mod_match.group(1) - module = mod_match.group(2) - chat = bot.get_chat(chat_id) - text = "*{}* has the following settings for the *{}* module:\n\n".format( - escape_markdown(chat.title), CHAT_SETTINGS[module].__mod_name__ - ) + CHAT_SETTINGS[module].__chat_settings__(chat_id, user.id) - await query.message.reply_text( - text=text, - parse_mode=ParseMode.MARKDOWN, - reply_markup=InlineKeyboardMarkup( - [ - [ - InlineKeyboardButton( - text="◁", - callback_data="stngs_back({})".format(chat_id), - ) - ] - ] - ), - ) - - elif prev_match: - chat_id = prev_match.group(1) - curr_page = int(prev_match.group(2)) - chat = bot.get_chat(chat_id) - await query.message.reply_text( - "Hi there! There are quite a few settings for {} - go ahead and pick what " - "you're interested in.".format(chat.title), - reply_markup=InlineKeyboardMarkup( - paginate_modules( - curr_page - 1, CHAT_SETTINGS, "stngs", chat=chat_id - ) - ), - ) - - elif next_match: - chat_id = next_match.group(1) - next_page = int(next_match.group(2)) - chat = bot.get_chat(chat_id) - await query.message.reply_text( - "Hi there! There are quite a few settings for {} - go ahead and pick what " - "you're interested in.".format(chat.title), - reply_markup=InlineKeyboardMarkup( - paginate_modules( - next_page + 1, CHAT_SETTINGS, "stngs", chat=chat_id - ) - ), - ) - - elif back_match: - chat_id = back_match.group(1) - chat = bot.get_chat(chat_id) - await query.message.reply_text( - text="Hi there! There are quite a few settings for {} - go ahead and pick what " - "you're interested in.".format(escape_markdown(chat.title)), - parse_mode=ParseMode.MARKDOWN, - reply_markup=InlineKeyboardMarkup( - paginate_modules(0, CHAT_SETTINGS, "stngs", chat=chat_id) - ), - ) - - # ensure no spinny white circle - bot.answer_callback_query(query.id) - await query.message.delete() - except BadRequest as excp: - if excp.message not in [ - "Message is not modified", - "Query_id_invalid", - "Message can't be deleted", - ]: - LOGGER.exception("Exception in settings buttons. %s", str(query.data)) - - -async def get_settings(update: Update, context: ContextTypes.DEFAULT_TYPE): - chat = update.effective_chat # type: Optional[Chat] - user = update.effective_user # type: Optional[User] - msg = update.effective_message # type: Optional[Message] - - # ONLY send settings in PM - if chat.type != chat.PRIVATE: - if is_user_admin(chat, user.id): - text = "Click here to get this chat's settings, as well as yours." - await msg.reply_text( - text, - reply_markup=InlineKeyboardMarkup( - [ - [ - InlineKeyboardButton( - text="SETTINGS", - url="t.me/{}?start=stngs_{}".format( - context.bot.username, chat.id - ), - ) - ] - ] - ), - ) - else: - text = "Click here to check your settings." - - else: - await send_settings(chat.id, user.id, True) - - -async def migrate_chats(update: Update, context: ContextTypes.DEFAULT_TYPE): - msg = update.effective_message # type: Optional[Message] - if msg.migrate_to_chat_id: - old_chat = update.effective_chat.id - new_chat = msg.migrate_to_chat_id - elif msg.migrate_from_chat_id: - old_chat = msg.migrate_from_chat_id - new_chat = update.effective_chat.id - else: - return - - LOGGER.info("Migrating from %s, ᴛᴏ %s", str(old_chat), str(new_chat)) - for mod in MIGRATEABLE: - with contextlib.suppress(KeyError, AttributeError): - mod.__migrate__(old_chat, new_chat) - - LOGGER.info("Successfully Migrated!") - raise ApplicationHandlerStop - - -# <=======================================================================================================> - - -# <=================================================== MAIN ====================================================> -def main(): - function(CommandHandler("start", start)) - - function(CommandHandler("help", get_help)) - function(CallbackQueryHandler(help_button, pattern=r"help_.*")) - - function(CommandHandler("settings", get_settings)) - function(CallbackQueryHandler(settings_button, pattern=r"stngs_")) - function(CommandHandler("repo", repo)) - - function(CallbackQueryHandler(Miko_about_callback, pattern=r"Miko_")) - function(CallbackQueryHandler(gitsource_callback, pattern=r"git_source")) - function(CallbackQueryHandler(stats_back, pattern=r"insider_")) - function(CallbackQueryHandler(ai_handler_callback, pattern=r"ai_handler")) - function(CallbackQueryHandler(more_ai_handler_callback, pattern=r"more_ai_handler")) - function(MessageHandler(filters.StatusUpdate.MIGRATE, migrate_chats)) - - dispatcher.add_error_handler(error_callback) - - LOGGER.info("Mikobot is starting >> Using long polling.") - dispatcher.run_polling(timeout=15, drop_pending_updates=True) - - -if __name__ == "__main__": - try: - LOGGER.info("Successfully loaded modules: " + str(ALL_MODULES)) - tbot.start(bot_token=TOKEN) - app.start() - main() - except KeyboardInterrupt: - pass - except Exception: - err = traceback.format_exc() - LOGGER.info(err) - finally: - try: - if loop.is_running(): - loop.stop() - finally: - loop.close() - LOGGER.info( - "------------------------ Stopped Services ------------------------" - ) -# <==================================================== END ===================================================> diff --git a/Mikobot/events.py b/Mikobot/events.py deleted file mode 100644 index ad521e4da764a6a8593e66eed00562e2cd5e9a6c..0000000000000000000000000000000000000000 --- a/Mikobot/events.py +++ /dev/null @@ -1,70 +0,0 @@ -# <============================================== IMPORTS =========================================================> -from telethon import events - -from Mikobot import tbot - - -# <============================================== FUNCTIONS =========================================================> -def register(**args): - """Registers a new message.""" - pattern = args.get("pattern") - - r_pattern = r"^[/!]" - - if pattern is not None and not pattern.startswith("(?i)"): - args["pattern"] = f"(?i){pattern}" - - args["pattern"] = pattern.replace("^/", r_pattern, 1) - - def decorator(func): - tbot.add_event_handler(func, events.NewMessage(**args)) - return func - - return decorator - - -def chataction(**args): - """Registers chat actions.""" - - def decorator(func): - tbot.add_event_handler(func, events.ChatAction(**args)) - return func - - return decorator - - -def userupdate(**args): - """Registers user updates.""" - - def decorator(func): - tbot.add_event_handler(func, events.UserUpdate(**args)) - return func - - return decorator - - -def inlinequery(**args): - """Registers inline query.""" - pattern = args.get("pattern") - - if pattern is not None and not pattern.startswith("(?i)"): - args["pattern"] = f"(?i){pattern}" - - def decorator(func): - tbot.add_event_handler(func, events.InlineQuery(**args)) - return func - - return decorator - - -def callbackquery(**args): - """Registers inline query.""" - - def decorator(func): - tbot.add_event_handler(func, events.CallbackQuery(**args)) - return func - - return decorator - - -# <==================================================== END ===================================================> diff --git a/Mikobot/plugins/__init__.py b/Mikobot/plugins/__init__.py deleted file mode 100644 index a72c32dda67e0d940f30c6347fe6a8443bbc5456..0000000000000000000000000000000000000000 --- a/Mikobot/plugins/__init__.py +++ /dev/null @@ -1,43 +0,0 @@ -from Mikobot import LOAD, LOGGER, NO_LOAD - - -def __list_all_modules(): - import glob - from os.path import basename, dirname, isfile - - # This generates a list of modules in this folder for the * in __main__ to work. - mod_paths = glob.glob(dirname(__file__) + "/*.py") - all_modules = [ - basename(f)[:-3] - for f in mod_paths - if isfile(f) and f.endswith(".py") and not f.endswith("__init__.py") - ] - - if LOAD or NO_LOAD: - to_load = LOAD - if to_load: - if not all( - any(mod == module_name for module_name in all_modules) - for mod in to_load - ): - LOGGER.error("Invalid loadorder names, Quitting...") - quit(1) - - all_modules = sorted(set(all_modules) - set(to_load)) - to_load = list(all_modules) + to_load - - else: - to_load = all_modules - - if NO_LOAD: - LOGGER.info("Not loading: {}".format(NO_LOAD)) - return [item for item in to_load if item not in NO_LOAD] - - return to_load - - return all_modules - - -ALL_MODULES = __list_all_modules() -LOGGER.info("Modules to load: %s", str(ALL_MODULES)) -__all__ = ALL_MODULES + ["ALL_MODULES"] diff --git a/Mikobot/plugins/admin.py b/Mikobot/plugins/admin.py deleted file mode 100644 index 40fda97e379894d125df96bb27ce53e6d64f9d60..0000000000000000000000000000000000000000 --- a/Mikobot/plugins/admin.py +++ /dev/null @@ -1,1148 +0,0 @@ -# <============================================== IMPORTS =========================================================> -import html - -from telegram import ( - ChatMemberAdministrator, - InlineKeyboardButton, - InlineKeyboardMarkup, - Update, -) -from telegram.constants import ChatID, ChatMemberStatus, ChatType, ParseMode -from telegram.error import BadRequest -from telegram.ext import CallbackQueryHandler, CommandHandler, ContextTypes, filters -from telegram.helpers import mention_html - -from Mikobot import DRAGONS, function -from Mikobot.plugins.disable import DisableAbleCommandHandler -from Mikobot.plugins.helper_funcs.alternate import send_message -from Mikobot.plugins.helper_funcs.chat_status import ( - ADMIN_CACHE, - check_admin, - connection_status, -) -from Mikobot.plugins.helper_funcs.extraction import extract_user, extract_user_and_text -from Mikobot.plugins.log_channel import loggable - -# <=======================================================================================================> - - -# <================================================ FUNCTION =======================================================> -@connection_status -@loggable -@check_admin(permission="can_promote_members", is_both=True) -async def promote(update: Update, context: ContextTypes.DEFAULT_TYPE): - bot = context.bot - args = context.args - - message = update.effective_message - chat = update.effective_chat - user = update.effective_user - - user_id = await extract_user(message, context, args) - await chat.get_member(user.id) - - if message.from_user.id == ChatID.ANONYMOUS_ADMIN: - await message.reply_text( - text="You are an anonymous admin.", - reply_markup=InlineKeyboardMarkup( - [ - [ - InlineKeyboardButton( - text="Click to promote admin.", - callback_data=f"admin_=promote={user_id}", - ), - ], - ], - ), - ) - - return - - if not user_id: - await message.reply_text( - "You don't seem to be referring to a user, or the ID specified is incorrect.", - ) - return - - try: - user_member = await chat.get_member(user_id) - except: - return - - if ( - user_member.status == ChatMemberStatus.ADMINISTRATOR - or user_member.status == ChatMemberStatus.OWNER - ): - await message.reply_text("How can I promote someone who is already an admin?") - return - - if user_id == bot.id: - await message.reply_text( - "I can't promote myself! Get an admin to do it for me." - ) - return - - # Set the same permissions as the bot - the bot can't assign higher permissions than itself! - bot_member = await chat.get_member(bot.id) - - if isinstance(bot_member, ChatMemberAdministrator): - try: - await bot.promoteChatMember( - chat.id, - user_id, - can_change_info=bot_member.can_change_info, - can_post_messages=bot_member.can_post_messages, - can_edit_messages=bot_member.can_edit_messages, - can_delete_messages=bot_member.can_delete_messages, - can_invite_users=bot_member.can_invite_users, - can_restrict_members=bot_member.can_restrict_members, - can_pin_messages=bot_member.can_pin_messages, - can_manage_chat=bot_member.can_manage_chat, - can_manage_video_chats=bot_member.can_manage_video_chats, - can_manage_topics=bot_member.can_manage_topics, - ) - except BadRequest as err: - if err.message == "User_not_mutual_contact": - await message.reply_text( - "I can't promote someone who isn't in the group." - ) - else: - await message.reply_text("An error occurred while promoting.") - return - - await bot.sendMessage( - chat.id, - f"Successfully promoted {user_member.user.first_name or user_id}!", - parse_mode=ParseMode.HTML, - message_thread_id=message.message_thread_id if chat.is_forum else None, - ) - - log_message = ( - f"{html.escape(chat.title)}:\n" - "#Promoted\n" - f"ADMIN: {mention_html(user.id, user.first_name)}\n" - f"USER: {mention_html(user_member.user.id, user_member.user.first_name)}" - ) - - return log_message - - -@connection_status -@loggable -@check_admin(permission="can_promote_members", is_both=True) -async def fullpromote(update: Update, context: ContextTypes.DEFAULT_TYPE): - bot = context.bot - args = context.args - - message = update.effective_message - chat = update.effective_chat - user = update.effective_user - - user_id = await extract_user(message, context, args) - await chat.get_member(user.id) - - if message.from_user.id == ChatID.ANONYMOUS_ADMIN: - await message.reply_text( - text="You are an anonymous admin.", - reply_markup=InlineKeyboardMarkup( - [ - [ - InlineKeyboardButton( - text="Click to promote admin.", - callback_data=f"admin_=promote={user_id}", - ), - ], - ], - ), - ) - - return - - if not user_id: - await message.reply_text( - "You don't seem to be referring to a user, or the ID specified is incorrect.", - ) - return - - try: - user_member = await chat.get_member(user_id) - except: - return - - if ( - user_member.status == ChatMemberStatus.ADMINISTRATOR - or user_member.status == ChatMemberStatus.OWNER - ): - await message.reply_text("How can I promote someone who is already an admin?") - return - - if user_id == bot.id: - await message.reply_text( - "I can't promote myself! Get an admin to do it for me." - ) - return - - # Set the same permissions as the bot - the bot can't assign higher perms than itself! - bot_member = await chat.get_member(bot.id) - - if isinstance(bot_member, ChatMemberAdministrator): - try: - await bot.promoteChatMember( - chat.id, - user_id, - can_change_info=bot_member.can_change_info, - can_post_messages=bot_member.can_post_messages, - can_edit_messages=bot_member.can_edit_messages, - can_delete_messages=bot_member.can_delete_messages, - can_invite_users=bot_member.can_invite_users, - can_promote_members=bot_member.can_promote_members, - can_restrict_members=bot_member.can_restrict_members, - can_pin_messages=bot_member.can_pin_messages, - can_manage_chat=bot_member.can_manage_chat, - can_manage_video_chats=bot_member.can_manage_video_chats, - can_manage_topics=bot_member.can_manage_topics, - ) - except BadRequest as err: - if err.message == "User_not_mutual_contact": - await message.reply_text( - "I can't promote someone who isn't in the group." - ) - else: - await message.reply_text("An error occurred while promoting.") - return - - await bot.sendMessage( - chat.id, - f"Successfully promoted {user_member.user.first_name or user_id}!", - parse_mode=ParseMode.HTML, - message_thread_id=message.message_thread_id if chat.is_forum else None, - ) - - log_message = ( - f"{html.escape(chat.title)}:\n" - "#FULLPROMOTED\n" - f"ADMIN: {mention_html(user.id, user.first_name)}\n" - f"USER: {mention_html(user_member.user.id, user_member.user.first_name)}" - ) - - return log_message - - -@connection_status -@loggable -@check_admin(permission="can_promote_members", is_both=True) -async def demote(update: Update, context: ContextTypes.DEFAULT_TYPE): - bot = context.bot - args = context.args - - chat = update.effective_chat - message = update.effective_message - user = update.effective_user - - user_id = await extract_user(message, context, args) - await chat.get_member(user.id) - - if message.from_user.id == ChatID.ANONYMOUS_ADMIN: - await message.reply_text( - text="You are an anonymous admin.", - reply_markup=InlineKeyboardMarkup( - [ - [ - InlineKeyboardButton( - text="Click to prove admin.", - callback_data=f"admin_=demote={user_id}", - ), - ], - ], - ), - ) - - return - - if not user_id: - await message.reply_text( - "You don't seem to be referring to a user or the id specified is incorrect..", - ) - return - - try: - user_member = await chat.get_member(user_id) - except: - return - - if user_member.status == ChatMemberStatus.OWNER: - await message.reply_text( - "This person created the chat, How could i demote him?" - ) - return - - if not user_member.status == ChatMemberStatus.ADMINISTRATOR: - await message.reply_text("Can't demote who isn't promoted!") - return - - if user_id == bot.id: - await message.reply_text("I can't demote myself!.") - return - - try: - await bot.promote_chat_member( - chat.id, - user_id, - can_change_info=False, - can_post_messages=False, - can_edit_messages=False, - can_delete_messages=False, - can_invite_users=False, - can_restrict_members=False, - can_pin_messages=False, - can_promote_members=False, - can_manage_chat=False, - can_manage_video_chats=False, - can_manage_topics=False, - ) - - await bot.sendMessage( - chat.id, - f"SUCCESSFULLY DEMOTED {user_member.user.first_name or user_id}!", - parse_mode=ParseMode.HTML, - message_thread_id=message.message_thread_id if chat.is_forum else None, - ) - - log_message = ( - f"{html.escape(chat.title)}:\n" - f"#DEMOTED\n" - f"ADMIN: {mention_html(user.id, user.first_name)}\n" - f"USER: {mention_html(user_member.user.id, user_member.user.first_name)}" - ) - - return log_message - except BadRequest: - await message.reply_text( - "Could not demote. I might not be admin or the admin status was appointed by another" - "Its a User, So I can't act upon them!", - ) - raise - - -@check_admin(is_user=True) -async def refresh_admin(update, _): - try: - ADMIN_CACHE.pop(update.effective_chat.id) - except KeyError: - pass - - await update.effective_message.reply_text("Admins cache refreshed!") - - -@connection_status -@check_admin(permission="can_promote_members", is_both=True) -async def set_title(update: Update, context: ContextTypes.DEFAULT_TYPE): - bot = context.bot - args = context.args - - chat = update.effective_chat - message = update.effective_message - - user_id, title = await extract_user_and_text(message, context, args) - - if message.from_user.id == 1087968824: - await message.reply_text( - text="You are an anonymous admin.", - reply_markup=InlineKeyboardMarkup( - [ - [ - InlineKeyboardButton( - text="Click to prove admin.", - callback_data=f"admin_=title={user_id}={title}", - ), - ], - ], - ), - ) - - return - - try: - user_member = await chat.get_member(user_id) - except: - return - - if not user_id: - await message.reply_text( - "You don't seem to be referring to a user or the ID specified is incorrect..", - ) - return - - if user_member.status == ChatMemberStatus.OWNER: - await message.reply_text( - "This person CREATED the chat, how can I set custom title for him?", - ) - return - - if user_member.status != ChatMemberStatus.ADMINISTRATOR: - await message.reply_text( - "Can't set title for non-admins!\nPromote them first to set custom title!", - ) - return - - if user_id == bot.id: - await message.reply_text( - "I can't set my own title myself! Get the one who made me admin to do it for me.", - ) - return - - if not title: - await message.reply_text("Setting a blank title doesn't do anything!") - return - - if len(title) > 16: - await message.reply_text( - "The title length is longer than 16 characters.\nTruncating it to 16 characters.", - ) - - try: - await bot.setChatAdministratorCustomTitle(chat.id, user_id, title) - except BadRequest: - await message.reply_text( - "Either they aren't promoted by me or you set a title text that is impossible to set." - ) - raise - - await bot.sendMessage( - chat.id, - f"Successfully set title for {user_member.user.first_name or user_id} " - f"to {html.escape(title[:16])}!", - parse_mode=ParseMode.HTML, - message_thread_id=message.message_thread_id if chat.is_forum else None, - ) - - -@loggable -@check_admin(permission="can_pin_messages", is_both=True) -async def pin(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - bot = context.bot - args = context.args - - user = update.effective_user - chat = update.effective_chat - message = update.effective_message - - is_group = chat.type != "private" and chat.type != "channel" - prev_message = update.effective_message.reply_to_message - - is_silent = True - if len(args) >= 1: - is_silent = not ( - args[0].lower() == "notify" - or args[0].lower() == "loud" - or args[0].lower() == "violent" - ) - - if not prev_message: - await message.reply_text("Please reply to message which you want to pin.") - return - - if message.from_user.id == 1087968824: - await message.reply_text( - text="You are an anonymous admin.", - reply_markup=InlineKeyboardMarkup( - [ - [ - InlineKeyboardButton( - text="Click to prove admin.", - callback_data=f"admin_=pin={prev_message.message_id}={is_silent}", - ), - ], - ], - ), - ) - - return - - if prev_message and is_group: - try: - await bot.pinChatMessage( - chat.id, - prev_message.message_id, - disable_notification=is_silent, - ) - except BadRequest as excp: - if excp.message == "Chat_not_modified": - pass - else: - raise - log_message = ( - f"{chat.title}:\n" - "#PINNED\n" - f"Admin: {mention_html(user.id, user.first_name)}" - ) - - return log_message - - -@loggable -@check_admin(permission="can_pin_messages", is_both=True) -async def unpin(update: Update, context: ContextTypes.DEFAULT_TYPE): - bot = context.bot - chat = update.effective_chat - user = update.effective_user - message = update.effective_message - - if message.from_user.id == 1087968824: - await message.reply_text( - text="You are an anonymous admin.", - reply_markup=InlineKeyboardMarkup( - [ - [ - InlineKeyboardButton( - text="Click to prove Admin.", - callback_data=f"admin_=unpin", - ), - ], - ], - ), - ) - - return - - try: - await bot.unpinChatMessage(chat.id) - except BadRequest as excp: - if excp.message == "Chat_not_modified": - pass - elif excp.message == "Message to unpin not found": - await message.reply_text("No pinned message found") - return - else: - raise - - log_message = ( - f"{chat.title}:\n" - "#UNPINNED\n" - f"Admin: {mention_html(user.id, user.first_name)}" - ) - - return log_message - - -@loggable -@check_admin(permission="can_pin_messages", is_both=True) -async def unpinall(update: Update, context: ContextTypes.DEFAULT_TYPE): - bot = context.bot - chat = update.effective_chat - user = update.effective_user - message = update.effective_message - admin_member = await chat.get_member(user.id) - - if message.from_user.id == 1087968824: - await message.reply_text( - text="You are an anonymous admin.", - reply_markup=InlineKeyboardMarkup( - [ - [ - InlineKeyboardButton( - text="Click to prove admin.", - callback_data=f"admin_=unpinall", - ), - ], - ], - ), - ) - - return - elif not admin_member.status == ChatMemberStatus.OWNER and user.id not in DRAGONS: - await message.reply_text("Only chat OWNER can unpin all messages.") - return - - try: - if chat.is_forum: - await bot.unpin_all_forum_topic_messages(chat.id, message.message_thread_id) - else: - await bot.unpin_all_chat_messages(chat.id) - except BadRequest as excp: - if excp.message == "Chat_not_modified": - pass - else: - raise - - log_message = ( - f"{chat.title}:\n" - "#UNPINNED_ALL\n" - f"Admin: {mention_html(user.id, user.first_name)}" - ) - - return log_message - - -@connection_status -@check_admin(permission="can_invite_users", is_bot=True) -async def invite(update: Update, context: ContextTypes.DEFAULT_TYPE): - bot = context.bot - chat = update.effective_chat - - if chat.username: - await update.effective_message.reply_text(f"https://t.me/{chat.username}") - elif chat.type in [ChatType.SUPERGROUP, ChatType.CHANNEL]: - bot_member = await chat.get_member(bot.id) - if ( - bot_member.can_invite_users - if isinstance(bot_member, ChatMemberAdministrator) - else None - ): - invitelink = await bot.exportChatInviteLink(chat.id) - await update.effective_message.reply_text(invitelink) - else: - await update.effective_message.reply_text( - "I don't have access to the invite link, try changing my permissions!", - ) - else: - await update.effective_message.reply_text( - "I can only give you invite links for supergroups and channels, sorry!", - ) - - -@connection_status -async def adminlist(update: Update, context: ContextTypes.DEFAULT_TYPE): - chat = update.effective_chat # type: Optional[Chat] - user = update.effective_user # type: Optional[User] - args = context.args - bot = context.bot - if update.effective_message.chat.type == "private": - await send_message( - update.effective_message, "This command only works in Groups." - ) - return - chat = update.effective_chat - chat_id = update.effective_chat.id - chat_name = update.effective_message.chat.title - try: - msg = await update.effective_message.reply_text( - "Fetching group admins...", parse_mode=ParseMode.HTML - ) - except BadRequest: - msg = await update.effective_message.reply_text( - "Fetching group admins...", quote=False, parse_mode=ParseMode.HTML - ) - administrators = await bot.get_chat_administrators(chat_id) - administrators_list = list(administrators) # Convert to a list - text = "「 𝗔𝗗𝗠𝗜𝗡𝗦 𝗜𝗡 {}:".format(html.escape(update.effective_chat.title)) - bot_admin_list = [] - for admin in administrators_list: - user = admin.user - status = admin.status - custom_title = admin.custom_title - if user.first_name == "": - name = "☠ Deleted Account" - else: - name = "{}".format( - mention_html( - user.id, html.escape(user.first_name + " " + (user.last_name or "")) - ) - ) - if user.is_bot: - bot_admin_list.append(name) - administrators_list.remove(admin) - continue - if status == "creator": - text += "\n\n 👑 Creator:" - text += "\n ╰─➽ {}\n".format(name) - if custom_title: - text += f" ┗━ {html.escape(custom_title)}\n" - text += "\n🚓 Admins:" - custom_admin_list = {} - normal_admin_list = [] - for admin in administrators_list: - user = admin.user - status = admin.status - custom_title = admin.custom_title - if user.first_name == "": - name = "☠ Deleted Account" - else: - name = "{}".format( - mention_html( - user.id, html.escape(user.first_name + " " + (user.last_name or "")) - ) - ) - if status == "administrator": - if custom_title: - try: - custom_admin_list[custom_title].append(name) - except KeyError: - custom_admin_list.update({custom_title: [name]}) - else: - normal_admin_list.append(name) - for admin in normal_admin_list: - text += "\n ╰─➽ {}".format(admin) - for admin_group in custom_admin_list.copy(): - if len(custom_admin_list[admin_group]) == 1: - text += "\n ╰─➽ {} | {}".format( - custom_admin_list[admin_group][0], html.escape(admin_group) - ) - custom_admin_list.pop(admin_group) - text += "\n" - for admin_group in custom_admin_list: - text += "\n🚨 {}".format(admin_group) - for admin in custom_admin_list[admin_group]: - text += "\n ╰─➽ {}".format(admin) - text += "\n" - text += "\n🤖 Bots:" - for each_bot in bot_admin_list: - text += "\n ╰─➽ {}".format(each_bot) - try: - await msg.edit_text(text, parse_mode=ParseMode.HTML) - except BadRequest: # if the original message is deleted - return - - -@loggable -async def admin_callback(update: Update, context: ContextTypes.DEFAULT_TYPE): - query = update.callback_query - bot = context.bot - message = update.effective_message - chat = update.effective_chat - admin_user = query.from_user - - splitter = query.data.replace("admin_", "").split("=") - - if splitter[1] == "promote": - promoter = await chat.get_member(admin_user.id) - - if ( - not ( - promoter.can_promote_members - if isinstance(promoter, ChatMemberAdministrator) - else None or promoter.status == ChatMemberStatus.OWNER - ) - and admin_user.id not in DRAGONS - ): - await query.answer( - "You don't have the necessary rights to do that!", show_alert=True - ) - return - - try: - user_id = int(splitter[2]) - except ValueError: - user_id = splitter[2] - await message.edit_text( - "You don't seem to be referring to a user or the ID specified is incorrect..." - ) - return - - try: - user_member = await chat.get_member(user_id) - except: - return - - if ( - user_member.status == ChatMemberStatus.ADMINISTRATOR - or user_member.status == ChatMemberStatus.OWNER - ): - await message.edit_text( - "How am I meant to promote someone that's already an admin?" - ) - return - - bot_member = await chat.get_member(bot.id) - - if isinstance(bot_member, ChatMemberAdministrator): - try: - await bot.promoteChatMember( - chat.id, - user_id, - can_change_info=bot_member.can_change_info, - can_post_messages=bot_member.can_post_messages, - can_edit_messages=bot_member.can_edit_messages, - can_delete_messages=bot_member.can_delete_messages, - can_invite_users=bot_member.can_invite_users, - can_restrict_members=bot_member.can_restrict_members, - can_pin_messages=bot_member.can_pin_messages, - can_manage_chat=bot_member.can_manage_chat, - can_manage_video_chats=bot_member.can_manage_video_chats, - ) - except BadRequest as err: - if err.message == "User_not_mutual_contact": - await message.edit_text( - "I can't promote someone who isn't in the group" - ) - else: - await message.edit_text("An error occurred while promoting.") - return - - await message.edit_text( - f"Successfully promoted {user_member.user.first_name or user_id}!", - parse_mode=ParseMode.HTML, - ) - await query.answer("Done") - - log_message = ( - f"{html.escape(chat.title)}:\n" - f"#PROMOTED\n" - f"Admin: {mention_html(admin_user.id, admin_user.first_name)}\n" - f"User: {mention_html(user_member.user.id, user_member.user.first_name)}" - ) - - return log_message - - elif splitter[1] == "demote": - demoter = await chat.get_member(admin_user.id) - - if not ( - demoter.can_promote_members - if isinstance(demoter, ChatMemberAdministrator) - else None or demoter.status == ChatMemberStatus.OWNER - ): - await query.answer( - "You don't have the necessary rights to do that!", show_alert=True - ) - return - - try: - user_id = int(splitter[2]) - except: - user_id = splitter[2] - await message.edit_text( - "You don't seem to be referring to a user or the ID specified is incorrect.." - ) - return - - try: - user_member = await chat.get_member(user_id) - except: - return - - if user_member.status == ChatMemberStatus.OWNER: - await message.edit_text( - "This person CREATED the chat, how would I demote them?" - ) - return - - if not user_member.status == ChatMemberStatus.ADMINISTRATOR: - await message.edit_text("Can't demote what wasn't promoted!") - return - - if user_id == bot.id: - await message.edit_text( - "I can't demote myself!, get an admin to do it for me." - ) - return - - try: - await bot.promoteChatMember( - chat.id, - user_id, - can_change_info=False, - can_post_messages=False, - can_edit_messages=False, - can_delete_messages=False, - can_invite_users=False, - can_restrict_members=False, - can_pin_messages=False, - can_promote_members=False, - can_manage_chat=False, - can_manage_video_chats=False, - ) - - await message.edit_text( - f"Successfully demoted {user_member.user.first_name or user_id}!", - parse_mode=ParseMode.HTML, - ) - await query.answer("Done") - - log_message = ( - f"{html.escape(chat.title)}:\n" - f"#DEMOTE\n" - f"Admin: {mention_html(admin_user.id, admin_user.first_name)}\n" - f"User: {mention_html(user_member.user.id, user_member.user.first_name)}" - ) - - return log_message - except BadRequest: - await message.edit_text( - "Could not demote. I might not be admin, or the admin status was appointed by another" - " user, so I can't act upon them!" - ) - return - - elif splitter[1] == "title": - title = splitter[3] - - admin_member = await chat.get_member(admin_user.id) - - if ( - not ( - ( - admin_member.can_promote_members - if isinstance(admin_member, ChatMemberAdministrator) - else None - ) - or admin_member.status == ChatMemberStatus.OWNER - ) - and admin_user.id not in DRAGONS - ): - await query.answer("You don't have the necessary rights to do that!") - return - - try: - user_id = int(splitter[2]) - except: - await message.edit_text( - "You don't seem to be referring to a user or the ID specified is incorrect...", - ) - return - - try: - user_member = await chat.get_member(user_id) - except: - return - - if user_member.status == ChatMemberStatus.OWNER: - await message.edit_text( - "This person CREATED the chat, how can I set a custom title for him?", - ) - return - - if user_member.status != ChatMemberStatus.ADMINISTRATOR: - await message.edit_text( - "Can't set a title for non-admins! Promote them first to set a custom title!", - ) - return - - if user_id == bot.id: - await message.edit_text( - "I can't set my own title myself! Get the one who made me admin to do it for me.", - ) - return - - if not title: - await message.edit_text("Setting a blank title doesn't do anything!") - return - - if len(title) > 16: - await message.edit_text( - "The title length is longer than 16 characters. Truncating it to 16 characters.", - ) - - try: - await bot.setChatAdministratorCustomTitle(chat.id, user_id, title) - except BadRequest: - await message.edit_text( - "Either they aren't promoted by me or you set a title text that is impossible to set." - ) - return - - await message.edit_text( - text=f"Successfully set title for {user_member.user.first_name or user_id} " - f"to {html.escape(title[:16])}!", - parse_mode=ParseMode.HTML, - ) - - elif splitter[1] == "pin": - admin_member = await chat.get_member(admin_user.id) - - if ( - not ( - ( - admin_member.can_pin_messages - if isinstance(admin_member, ChatMemberAdministrator) - else None - ) - or admin_member.status == ChatMemberStatus.OWNER - ) - and admin_user.id not in DRAGONS - ): - await query.answer( - "You don't have the necessary rights to do that!", show_alert=True - ) - return - - try: - message_id = int(splitter[2]) - except: - return - - is_silent = bool(splitter[3]) - is_group = chat.type != "private" and chat.type != "channel" - - if is_group: - try: - await bot.pinChatMessage( - chat.id, - message_id, - disable_notification=is_silent, - ) - except BadRequest as excp: - if excp.message == "Chat_not_modified": - pass - else: - raise - - await message.edit_text("Done Pinned.") - - log_message = ( - f"{html.escape(chat.title)}\n" - f"#PINNED\n" - f"Admin: {mention_html(admin_user.id, html.escape(admin_user.first_name))}" - ) - - return log_message - - elif splitter[1] == "unpin": - admin_member = await chat.get_member(admin_user.id) - - if ( - not ( - ( - admin_member.can_pin_messages - if isinstance(admin_member, ChatMemberAdministrator) - else None - ) - or admin_member.status == ChatMemberStatus.OWNER - ) - and admin_user.id not in DRAGONS - ): - await query.answer( - "You don't have the necessary rights to do that!", - show_alert=True, - ) - return - - try: - await bot.unpinChatMessage(chat.id) - except BadRequest as excp: - if excp.message == "Chat_not_modified": - pass - elif excp.message == "Message_to_unpin_not_found": - await message.edit_text("No pinned message found") - return - else: - raise - - log_message = ( - f"{html.escape(chat.title)}:\n" - f"#UNPINNED\n" - f"Admin: {mention_html(admin_user.id, html.escape(admin_user.first_name))}" - ) - - return log_message - - elif splitter[1] == "unpinall": - admin_member = await chat.get_member(admin_user.id) - - if ( - not admin_member.status == ChatMemberStatus.OWNER - and admin_user.id not in DRAGONS - ): - await query.answer("Only chat OWNER can unpin all messages.") - return - - try: - if chat.is_forum: - await bot.unpin_all_forum_topic_messages( - chat.id, message.message_thread_id - ) - else: - await bot.unpin_all_chat_messages(chat.id) - except BadRequest as excp: - if excp.message == "Chat_not_modified": - pass - else: - raise - - await message.edit_text("Done unpinning all messages.") - log_message = ( - f"{html.escape(chat.title)}:\n" - f"#UNPINNED-ALL\n" - f"ADMIN: {mention_html(admin_user.id, html.escape(admin_user.first_name))}" - ) - - return log_message - - -# <=================================================== HELP ====================================================> - - -__help__ = """ -» /adminlist: List of admins in the chat. - -➠ *Admins only:* - -» /pin: Silently pins the message replied to. Add 'loud' or 'notify' to give notifications to users. - -» /unpin: Unpins the currently pinned message. - -» /unpinall: Unpins all the pinned messages. Works in topics too (only OWNER can do this). - -» /invitelink: Get an invite link. - -» /promote: Promotes the user replied to. - -» /fullpromote: FullPromotes the user replied to. - -» /demote: Demotes the user replied to. - -» /title : Sets a custom title for an admin that the bot promoted. - -» /admincache: Force refresh the admins list. -""" - -# <================================================ HANDLER =======================================================> -ADMINLIST_HANDLER = DisableAbleCommandHandler("adminlist", adminlist, block=False) - -PIN_HANDLER = CommandHandler("pin", pin, filters=filters.ChatType.GROUPS, block=False) -UNPIN_HANDLER = CommandHandler( - "unpin", unpin, filters=filters.ChatType.GROUPS, block=False -) -UNPINALL_HANDLER = CommandHandler( - "unpinall", unpinall, filters=filters.ChatType.GROUPS, block=False -) - -INVITE_HANDLER = DisableAbleCommandHandler("invitelink", invite, block=False) - -PROMOTE_HANDLER = DisableAbleCommandHandler("promote", promote, block=False) -FULLPROMOTE_HANDLER = DisableAbleCommandHandler("fullpromote", fullpromote, block=False) -DEMOTE_HANDLER = DisableAbleCommandHandler("demote", demote, block=False) - -SET_TITLE_HANDLER = CommandHandler("title", set_title, block=False) -ADMIN_REFRESH_HANDLER = CommandHandler( - "admincache", refresh_admin, filters=filters.ChatType.GROUPS, block=False -) -ADMIN_CALLBACK_HANDLER = CallbackQueryHandler( - admin_callback, block=False, pattern=r"admin_" -) - -function(ADMINLIST_HANDLER) -function(PIN_HANDLER) -function(UNPIN_HANDLER) -function(UNPINALL_HANDLER) -function(INVITE_HANDLER) -function(PROMOTE_HANDLER) -function(FULLPROMOTE_HANDLER) -function(DEMOTE_HANDLER) -function(SET_TITLE_HANDLER) -function(ADMIN_REFRESH_HANDLER) -function(ADMIN_CALLBACK_HANDLER) - -__mod_name__ = "ADMIN" -__command_list__ = [ - "adminlist", - "admins", - "invitelink", - "promote", - "demote", - "admincache", - "fullpromote", - "setgpic", - "delgpic", -] -__handlers__ = [ - ADMINLIST_HANDLER, - PIN_HANDLER, - UNPIN_HANDLER, - INVITE_HANDLER, - PROMOTE_HANDLER, - DEMOTE_HANDLER, - SET_TITLE_HANDLER, - ADMIN_REFRESH_HANDLER, -] -# <================================================ END =======================================================> diff --git a/Mikobot/plugins/afk.py b/Mikobot/plugins/afk.py deleted file mode 100644 index fe015c3a1d6116b655dd05d8f628081bbc61c41e..0000000000000000000000000000000000000000 --- a/Mikobot/plugins/afk.py +++ /dev/null @@ -1,215 +0,0 @@ -# <============================================== IMPORTS =========================================================> -import html -import random -from datetime import datetime - -import humanize -from telegram import MessageEntity, Update -from telegram.error import BadRequest -from telegram.ext import ContextTypes, MessageHandler, filters - -from Database.sql import afk_sql as sql -from Mikobot import LOGGER, function -from Mikobot.plugins.disable import DisableAbleCommandHandler, DisableAbleMessageHandler -from Mikobot.plugins.users import get_user_id - -# <=======================================================================================================> - -AFK_GROUP = 7 -AFK_REPLY_GROUP = 8 - - -# <================================================ FUNCTION =======================================================> -async def afk(update: Update, context: ContextTypes.DEFAULT_TYPE): - if update.effective_message.text: - args = update.effective_message.text.split(None, 1) - else: - return - user = update.effective_user - - if not user: # ignore channels - return - - notice = "" - if len(args) >= 2: - reason = args[1] - if len(reason) > 100: - reason = reason[:100] - notice = "\nYour afk reason was shortened to 100 characters." - else: - reason = "" - - sql.set_afk(update.effective_user.id, reason) - fname = update.effective_user.first_name - try: - if reason: - await update.effective_message.reply_text( - f"➲ {fname} is now away! \n\n➦ Reason: <code>{reason}</code> \n {notice}", - parse_mode="html", - ) - else: - await update.effective_message.reply_text( - "➲ {} is now away!{}".format(fname, notice), - ) - except BadRequest: - pass - - -async def no_longer_afk(update: Update, context: ContextTypes.DEFAULT_TYPE): - user = update.effective_user - message = update.effective_message - - if not user: # ignore channels - return - - if sql.is_afk(user.id): - afk_user = sql.check_afk_status(user.id) - - time = humanize.naturaldelta(datetime.now() - afk_user.time) - - res = sql.rm_afk(user.id) - if res: - if message.new_chat_members: # dont say msg - return - firstname = update.effective_user.first_name - try: - options = [ - "➲ {} is here!", - "➲ {} is back!", - "➲ {} is now in the chat!", - "➲ {} is awake!", - "➲ {} is back online!", - "➲ {} is finally here!", - "➲ Welcome back! {}", - ] - chosen_option = random.choice(options) - await update.effective_message.reply_text( - chosen_option.format(firstname) - + f"\n\nYou were AFK for: <code>{time}</code>", - parse_mode="html", - ) - except: - return - - -async def reply_afk(update: Update, context: ContextTypes.DEFAULT_TYPE): - bot = context.bot - message = update.effective_message - userc = update.effective_user - userc_id = userc.id - if message.entities and message.parse_entities( - [MessageEntity.TEXT_MENTION, MessageEntity.MENTION], - ): - entities = message.parse_entities( - [MessageEntity.TEXT_MENTION, MessageEntity.MENTION], - ) - - chk_users = [] - for ent in entities: - if ent.type == MessageEntity.TEXT_MENTION: - user_id = ent.user.id - fst_name = ent.user.first_name - - if user_id in chk_users: - return - chk_users.append(user_id) - - if ent.type != MessageEntity.MENTION: - return - - user_id = await get_user_id( - message.text[ent.offset : ent.offset + ent.length], - ) - if not user_id: - return - - if user_id in chk_users: - return - chk_users.append(user_id) - - try: - chat = await bot.get_chat(user_id) - except BadRequest: - LOGGER.error( - "Error: Could not fetch userid {} for AFK module".format(user_id) - ) - return - fst_name = chat.first_name - - await check_afk(update, context, user_id, fst_name, userc_id) - - elif message.reply_to_message: - user_id = message.reply_to_message.from_user.id - fst_name = message.reply_to_message.from_user.first_name - await check_afk(update, context, user_id, fst_name, userc_id) - - -async def check_afk( - update: Update, - context: ContextTypes.DEFAULT_TYPE, - user_id: int, - fst_name: str, - userc_id: int, -): - if sql.is_afk(user_id): - user = sql.check_afk_status(user_id) - - if int(userc_id) == int(user_id): - return - - time = humanize.naturaldelta(datetime.now() - user.time) - - if not user.reason: - res = "➲ {} is afk.\n\n➦ Last seen {} ago.".format( - fst_name, - time, - ) - await update.effective_message.reply_text(res) - else: - res = ( - "➲ {} is afk.\n\n➦ Reason: <code>{}</code>\n➦ Last seen {} ago.".format( - html.escape(fst_name), - html.escape(user.reason), - time, - ) - ) - await update.effective_message.reply_text(res, parse_mode="html") - - -# <=================================================== HELP ====================================================> - - -__help__ = """ -» /afk <reason>*:* mark yourself as AFK (away from keyboard). - -» brb , !afk <reason>*:* same as the afk command - but not a command. - -➠ *When marked as AFK, any mentions will be replied to with a message to say you're not available!* -""" - -# <================================================ HANDLER =======================================================> -AFK_HANDLER = DisableAbleCommandHandler("afk", afk, block=False) -AFK_REGEX_HANDLER = DisableAbleMessageHandler( - filters.Regex(r"^(?i:(brb|!afk))( .*)?$"), afk, friendly="afk", block=False -) -NO_AFK_HANDLER = MessageHandler( - filters.ALL & filters.ChatType.GROUPS, no_longer_afk, block=False -) -AFK_REPLY_HANDLER = MessageHandler( - filters.ALL & filters.ChatType.GROUPS, reply_afk, block=False -) - -function(AFK_HANDLER, AFK_GROUP) -function(AFK_REGEX_HANDLER, AFK_GROUP) -function(NO_AFK_HANDLER, AFK_GROUP) -function(AFK_REPLY_HANDLER, AFK_REPLY_GROUP) - -__mod_name__ = "AFK" -__command_list__ = ["afk"] -__handlers__ = [ - (AFK_HANDLER, AFK_GROUP), - (AFK_REGEX_HANDLER, AFK_GROUP), - (NO_AFK_HANDLER, AFK_GROUP), - (AFK_REPLY_HANDLER, AFK_REPLY_GROUP), -] -# <================================================ END =======================================================> diff --git a/Mikobot/plugins/ai.py b/Mikobot/plugins/ai.py deleted file mode 100644 index 0a1b5d6d37d2f652f1ed4f7998ef5cc545b7bc9e..0000000000000000000000000000000000000000 --- a/Mikobot/plugins/ai.py +++ /dev/null @@ -1,141 +0,0 @@ -# CREATED BY: https://t.me/O_oKarma -# API CREDITS: @Qewertyy -# PROVIDED BY: https://github.com/Team-ProjectCodeX - -# <============================================== IMPORTS =========================================================> -import base64 - -from telegram import Update -from telegram.constants import ParseMode -from telegram.ext import CommandHandler, ContextTypes - -from Mikobot import LOGGER as logger -from Mikobot import function -from Mikobot.state import state - -# <=======================================================================================================> - -# <================================================ CONSTANTS =====================================================> -API_URL = "https://lexica.qewertyy.me/models" -PALM_MODEL_ID = 0 -GPT_MODEL_ID = 5 - -# <================================================ FUNCTIONS =====================================================> - - -async def get_api_response(model_id, api_params, api_url): - try: - response = await state.post(api_url, params=api_params) - if response.status_code == 200: - data = response.json() - return data.get( - "content", f"Error: Empty response received from the {model_id} API." - ) - else: - return f"Error: Request failed with status code {response.status_code}." - except state.RequestError as e: - return f"Error: An error occurred while calling the {model_id} API. {e}" - - -async def palm_chatbot(update: Update, context: ContextTypes.DEFAULT_TYPE): - args = context.args - if not args: - await context.bot.send_message( - chat_id=update.effective_chat.id, - text="Error: Missing input text after /palm command.", - ) - return - - input_text = " ".join(args) - - result_msg = await context.bot.send_message( - chat_id=update.effective_chat.id, text="🌴" - ) - - api_params = {"model_id": PALM_MODEL_ID, "prompt": input_text} - api_response = await get_api_response("PALM", api_params, API_URL) - - await result_msg.delete() - await context.bot.send_message(chat_id=update.effective_chat.id, text=api_response) - - -async def gpt_chatbot(update: Update, context: ContextTypes.DEFAULT_TYPE): - args = context.args - if not args: - await context.bot.send_message( - chat_id=update.effective_chat.id, - text="Error: Missing input text after /askgpt command.", - ) - return - - input_text = " ".join(args) - - result_msg = await context.bot.send_message( - chat_id=update.effective_chat.id, text="💬" - ) - - api_params = {"model_id": GPT_MODEL_ID, "prompt": input_text} - api_response = await get_api_response("GPT", api_params, API_URL) - - await result_msg.delete() - await context.bot.send_message(chat_id=update.effective_chat.id, text=api_response) - - -# Define the upscale_image function -async def upscale_image(update: Update, context: ContextTypes.DEFAULT_TYPE): - try: - # Check if the replied message contains a photo - if update.message.reply_to_message and update.message.reply_to_message.photo: - # Send a message indicating upscaling is in progress - progress_msg = await update.message.reply_text( - "Upscaling your image, please wait..." - ) - - # Access the image file_id from the replied message - image = await update.message.reply_to_message.photo[-1].get_file() - - # Download the image and save it - image_path = await image.download_to_drive() - - with open(image_path, "rb") as image_file: - f = image_file.read() - - b = base64.b64encode(f).decode("utf-8") - - response = await state.post( - "https://lexica.qewertyy.me/upscale", - data={"image_data": b}, - ) - - # Save the upscaled image - upscaled_file_path = "upscaled_image.png" - with open(upscaled_file_path, "wb") as output_file: - output_file.write(response.content) - - # Delete the progress message - await context.bot.delete_message( - chat_id=update.message.chat_id, message_id=progress_msg.message_id - ) - - # Send the upscaled image as a PNG file - await update.message.reply_document( - document=open(upscaled_file_path, "rb"), - caption=f"<b>Upscaled your image.</b>\n<b>Generated By:</b> @{context.bot.username}", - parse_mode=ParseMode.HTML, - ) - else: - await update.message.reply_text("Please reply to an image to upscale it.") - - except Exception as e: - logger.error(f"Failed to upscale the image: {e}") - await update.message.reply_text( - "Failed to upscale the image. Please try again later." - ) - - -# <================================================ HANDLER =======================================================> -# Register the upscale_image command handler -function(CommandHandler("upscale", upscale_image, block=False)) -function(CommandHandler("palm", palm_chatbot, block=False)) -function(CommandHandler("askgpt", gpt_chatbot, block=False)) -# <================================================ END =======================================================> diff --git a/Mikobot/plugins/alive.py b/Mikobot/plugins/alive.py deleted file mode 100644 index 340a5223d3b3a5914a1dfe25a19994b986d59557..0000000000000000000000000000000000000000 --- a/Mikobot/plugins/alive.py +++ /dev/null @@ -1,57 +0,0 @@ -# SOURCE https://github.com/Team-ProjectCodeX -# CREATED BY https://t.me/O_okarma -# PROVIDED BY https://t.me/ProjectCodeX - -# <============================================== IMPORTS =========================================================> -import random -from sys import version_info - -import pyrogram -import telegram -import telethon -from pyrogram import filters -from pyrogram.types import InlineKeyboardMarkup, Message - -from Infamous.karma import ALIVE_ANIMATION, ALIVE_BTN -from Mikobot import BOT_NAME, app - -# <=======================================================================================================> - - -# <================================================ FUNCTION =======================================================> -@app.on_message(filters.command("alive")) -async def alive(_, message: Message): - library_versions = { - "PTB": telegram.__version__, - "TELETHON": telethon.__version__, - "PYROGRAM": pyrogram.__version__, - } - - library_versions_text = "\n".join( - [f"➲ **{key}:** `{value}`" for key, value in library_versions.items()] - ) - - caption = f"""**HEY, I AM {BOT_NAME}** - -━━━━━━ 🌟✿🌟 ━━━━━━ -✪ **CREATOR:** [🄺🄰🅁🄼🄰](https://t.me/anime_Freakz) - -{library_versions_text} - -➲ **PYTHON:** `{version_info[0]}.{version_info[1]}.{version_info[2]}` -➲ **BOT VERSION:** `2.0` -━━━━━━ 🌟✿🌟 ━━━━━━""" - - await message.reply_animation( - random.choice(ALIVE_ANIMATION), - caption=caption, - reply_markup=InlineKeyboardMarkup(ALIVE_BTN), - ) - - -# <=======================================================================================================> - - -# <================================================ NAME =======================================================> -__mod_name__ = "ALIVE" -# <================================================ END =======================================================> diff --git a/Mikobot/plugins/anime.py b/Mikobot/plugins/anime.py deleted file mode 100644 index acf7777423eaae67627fa2e44e606ebfefc1a96a..0000000000000000000000000000000000000000 --- a/Mikobot/plugins/anime.py +++ /dev/null @@ -1,5138 +0,0 @@ -# <============================================== IMPORTS =========================================================> -import asyncio -import json -import logging -import os -import random -import re -import shlex -import time -from datetime import datetime -from os.path import basename -from time import time -from traceback import format_exc as err -from typing import Optional, Tuple -from urllib.parse import quote -from uuid import uuid4 - -import requests -import urllib3 -from bs4 import BeautifulSoup -from motor.core import AgnosticClient, AgnosticCollection, AgnosticDatabase -from motor.motor_asyncio import AsyncIOMotorClient -from pyrogram import Client, filters -from pyrogram.enums import ChatMemberStatus, ChatType -from pyrogram.errors import ( - FloodWait, - MessageNotModified, - UserNotParticipant, - WebpageCurlFailed, - WebpageMediaEmpty, -) -from pyrogram.types import ( - CallbackQuery, - InlineKeyboardButton, - InlineKeyboardMarkup, - InputMediaPhoto, - Message, -) - -from Mikobot import BOT_USERNAME, MESSAGE_DUMP, MONGO_DB_URI, app -from Mikobot.utils.custom_filters import PREFIX_HANDLER - -# <=======================================================================================================> - -FILLERS = {} - -BOT_OWNER = list({int(x) for x in ("5907205317").split()}) - -_MGCLIENT: AgnosticClient = AsyncIOMotorClient(MONGO_DB_URI) - -_DATABASE: AgnosticDatabase = _MGCLIENT["MikobotAnime"] - - -def get_collection(name: str) -> AgnosticCollection: - """Create or Get Collection from your database""" - return _DATABASE[name] - - -def _close_db() -> None: - _MGCLIENT.close() - - -GROUPS = get_collection("GROUPS") -SFW_GRPS = get_collection("SFW_GROUPS") -DC = get_collection("DISABLED_CMDS") -AG = get_collection("AIRING_GROUPS") -CG = get_collection("CRUNCHY_GROUPS") -SG = get_collection("SUBSPLEASE_GROUPS") -HD = get_collection("HEADLINES_GROUPS") -MHD = get_collection("MAL_HEADLINES_GROUPS") -CHAT_OWNER = ChatMemberStatus.OWNER -MEMBER = ChatMemberStatus.MEMBER -ADMINISTRATOR = ChatMemberStatus.ADMINISTRATOR - -failed_pic = "https://telegra.ph/file/09733b49f3a9d5b147d21.png" -no_pic = [ - "https://telegra.ph/file/0d2097f442e816ba3f946.jpg", - "https://telegra.ph/file/5a152016056308ef63226.jpg", - "https://telegra.ph/file/d2bf913b18688c59828e9.jpg", - "https://telegra.ph/file/d53083ea69e84e3b54735.jpg", - "https://telegra.ph/file/b5eb1e3606b7d2f1b491f.jpg", -] - - -DOWN_PATH = "Mikobot/downloads/" - -AUTH_USERS = get_collection("AUTH_USERS") -IGNORE = get_collection("IGNORED_USERS") -PIC_DB = get_collection("PIC_DB") -GROUPS = get_collection("GROUPS") -CC = get_collection("CONNECTED_CHANNELS") -USER_JSON = {} -USER_WC = {} - -LANGUAGES = { - "af": "afrikaans", - "sq": "albanian", - "am": "amharic", - "ar": "arabic", - "hy": "armenian", - "az": "azerbaijani", - "eu": "basque", - "be": "belarusian", - "bn": "bengali", - "bs": "bosnian", - "bg": "bulgarian", - "ca": "catalan", - "ceb": "cebuano", - "ny": "chichewa", - "zh-cn": "chinese (simplified)", - "zh-tw": "chinese (traditional)", - "co": "corsican", - "hr": "croatian", - "cs": "czech", - "da": "danish", - "nl": "dutch", - "en": "english", - "eo": "esperanto", - "et": "estonian", - "tl": "filipino", - "fi": "finnish", - "fr": "french", - "fy": "frisian", - "gl": "galician", - "ka": "georgian", - "de": "german", - "el": "greek", - "gu": "gujarati", - "ht": "haitian creole", - "ha": "hausa", - "haw": "hawaiian", - "iw": "hebrew", - "he": "hebrew", - "hi": "hindi", - "hmn": "hmong", - "hu": "hungarian", - "is": "icelandic", - "ig": "igbo", - "id": "indonesian", - "ga": "irish", - "it": "italian", - "ja": "japanese", - "jw": "javanese", - "kn": "kannada", - "kk": "kazakh", - "km": "khmer", - "ko": "korean", - "ku": "kurdish (kurmanji)", - "ky": "kyrgyz", - "lo": "lao", - "la": "latin", - "lv": "latvian", - "lt": "lithuanian", - "lb": "luxembourgish", - "mk": "macedonian", - "mg": "malagasy", - "ms": "malay", - "ml": "malayalam", - "mt": "maltese", - "mi": "maori", - "mr": "marathi", - "mn": "mongolian", - "my": "myanmar (burmese)", - "ne": "nepali", - "no": "norwegian", - "or": "odia", - "ps": "pashto", - "fa": "persian", - "pl": "polish", - "pt": "portuguese", - "pa": "punjabi", - "ro": "romanian", - "ru": "russian", - "sm": "samoan", - "gd": "scots gaelic", - "sr": "serbian", - "st": "sesotho", - "sn": "shona", - "sd": "sindhi", - "si": "sinhala", - "sk": "slovak", - "sl": "slovenian", - "so": "somali", - "es": "spanish", - "su": "sundanese", - "sw": "swahili", - "sv": "swedish", - "tg": "tajik", - "ta": "tamil", - "tt": "tatar", - "te": "telugu", - "th": "thai", - "tr": "turkish", - "tk": "turkmen", - "uk": "ukrainian", - "ur": "urdu", - "ug": "uyghur", - "uz": "uzbek", - "vi": "vietnamese", - "cy": "welsh", - "xh": "xhosa", - "yi": "yiddish", - "yo": "yoruba", - "zu": "zulu", -} - -DEFAULT_SERVICE_URLS = ( - "translate.google.ac", - "translate.google.ad", - "translate.google.ae", - "translate.google.al", - "translate.google.am", - "translate.google.as", - "translate.google.at", - "translate.google.az", - "translate.google.ba", - "translate.google.be", - "translate.google.bf", - "translate.google.bg", - "translate.google.bi", - "translate.google.bj", - "translate.google.bs", - "translate.google.bt", - "translate.google.by", - "translate.google.ca", - "translate.google.cat", - "translate.google.cc", - "translate.google.cd", - "translate.google.cf", - "translate.google.cg", - "translate.google.ch", - "translate.google.ci", - "translate.google.cl", - "translate.google.cm", - "translate.google.cn", - "translate.google.co.ao", - "translate.google.co.bw", - "translate.google.co.ck", - "translate.google.co.cr", - "translate.google.co.id", - "translate.google.co.il", - "translate.google.co.in", - "translate.google.co.jp", - "translate.google.co.ke", - "translate.google.co.kr", - "translate.google.co.ls", - "translate.google.co.ma", - "translate.google.co.mz", - "translate.google.co.nz", - "translate.google.co.th", - "translate.google.co.tz", - "translate.google.co.ug", - "translate.google.co.uk", - "translate.google.co.uz", - "translate.google.co.ve", - "translate.google.co.vi", - "translate.google.co.za", - "translate.google.co.zm", - "translate.google.co.zw", - "translate.google.co", - "translate.google.com.af", - "translate.google.com.ag", - "translate.google.com.ai", - "translate.google.com.ar", - "translate.google.com.au", - "translate.google.com.bd", - "translate.google.com.bh", - "translate.google.com.bn", - "translate.google.com.bo", - "translate.google.com.br", - "translate.google.com.bz", - "translate.google.com.co", - "translate.google.com.cu", - "translate.google.com.cy", - "translate.google.com.do", - "translate.google.com.ec", - "translate.google.com.eg", - "translate.google.com.et", - "translate.google.com.fj", - "translate.google.com.gh", - "translate.google.com.gi", - "translate.google.com.gt", - "translate.google.com.hk", - "translate.google.com.jm", - "translate.google.com.kh", - "translate.google.com.kw", - "translate.google.com.lb", - "translate.google.com.lc", - "translate.google.com.ly", - "translate.google.com.mm", - "translate.google.com.mt", - "translate.google.com.mx", - "translate.google.com.my", - "translate.google.com.na", - "translate.google.com.ng", - "translate.google.com.ni", - "translate.google.com.np", - "translate.google.com.om", - "translate.google.com.pa", - "translate.google.com.pe", - "translate.google.com.pg", - "translate.google.com.ph", - "translate.google.com.pk", - "translate.google.com.pr", - "translate.google.com.py", - "translate.google.com.qa", - "translate.google.com.sa", - "translate.google.com.sb", - "translate.google.com.sg", - "translate.google.com.sl", - "translate.google.com.sv", - "translate.google.com.tj", - "translate.google.com.tr", - "translate.google.com.tw", - "translate.google.com.ua", - "translate.google.com.uy", - "translate.google.com.vc", - "translate.google.com.vn", - "translate.google.com", - "translate.google.cv", - "translate.google.cx", - "translate.google.cz", - "translate.google.de", - "translate.google.dj", - "translate.google.dk", - "translate.google.dm", - "translate.google.dz", - "translate.google.ee", - "translate.google.es", - "translate.google.eu", - "translate.google.fi", - "translate.google.fm", - "translate.google.fr", - "translate.google.ga", - "translate.google.ge", - "translate.google.gf", - "translate.google.gg", - "translate.google.gl", - "translate.google.gm", - "translate.google.gp", - "translate.google.gr", - "translate.google.gy", - "translate.google.hn", - "translate.google.hr", - "translate.google.ht", - "translate.google.hu", - "translate.google.ie", - "translate.google.im", - "translate.google.io", - "translate.google.iq", - "translate.google.is", - "translate.google.it", - "translate.google.je", - "translate.google.jo", - "translate.google.kg", - "translate.google.ki", - "translate.google.kz", - "translate.google.la", - "translate.google.li", - "translate.google.lk", - "translate.google.lt", - "translate.google.lu", - "translate.google.lv", - "translate.google.md", - "translate.google.me", - "translate.google.mg", - "translate.google.mk", - "translate.google.ml", - "translate.google.mn", - "translate.google.ms", - "translate.google.mu", - "translate.google.mv", - "translate.google.mw", - "translate.google.ne", - "translate.google.nf", - "translate.google.nl", - "translate.google.no", - "translate.google.nr", - "translate.google.nu", - "translate.google.pl", - "translate.google.pn", - "translate.google.ps", - "translate.google.pt", - "translate.google.ro", - "translate.google.rs", - "translate.google.ru", - "translate.google.rw", - "translate.google.sc", - "translate.google.se", - "translate.google.sh", - "translate.google.si", - "translate.google.sk", - "translate.google.sm", - "translate.google.sn", - "translate.google.so", - "translate.google.sr", - "translate.google.st", - "translate.google.td", - "translate.google.tg", - "translate.google.tk", - "translate.google.tl", - "translate.google.tm", - "translate.google.tn", - "translate.google.to", - "translate.google.tt", - "translate.google.us", - "translate.google.vg", - "translate.google.vu", - "translate.google.ws", -) -log = logging.getLogger(__name__) -log.addHandler(logging.NullHandler()) - -urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) - -URLS_SUFFIX = [ - re.search("translate.google.(.*)", url.strip()).group(1) - for url in DEFAULT_SERVICE_URLS -] -URL_SUFFIX_DEFAULT = "cn" - - -def rand_key(): - return str(uuid4())[:8] - - -def control_user(func): - async def wrapper(_, message: Message): - msg = json.loads(str(message)) - gid = msg["chat"]["id"] - gidtype = msg["chat"]["type"] - if gidtype in [ChatType.SUPERGROUP, ChatType.GROUP] and not ( - await GROUPS.find_one({"_id": gid}) - ): - try: - gidtitle = msg["chat"]["username"] - except KeyError: - gidtitle = msg["chat"]["title"] - await GROUPS.insert_one({"_id": gid, "grp": gidtitle}) - await clog( - "Mikobot", - f"Bot added to a new group\n\n{gidtitle}\nID: `{gid}`", - "NEW_GROUP", - ) - try: - user = msg["from_user"]["id"] - except KeyError: - user = msg["chat"]["id"] - if await IGNORE.find_one({"_id": user}): - return - nut = time() - if user not in BOT_OWNER: - try: - out = USER_JSON[user] - if nut - out < 1.2: - USER_WC[user] += 1 - if USER_WC[user] == 3: - await message.reply_text( - ("Stop spamming bot!!!" + "\nElse you will be blacklisted"), - ) - await clog("Mikobot", f"UserID: {user}", "SPAM") - if USER_WC[user] == 5: - await IGNORE.insert_one({"_id": user}) - await message.reply_text( - ( - "You have been exempted from using this bot " - + "now due to spamming 5 times consecutively!!!" - + "\nTo remove restriction plead to " - + "@ProjectCodeXSupport" - ) - ) - await clog("Mikobot", f"UserID: {user}", "BAN") - return - await asyncio.sleep(USER_WC[user]) - else: - USER_WC[user] = 0 - except KeyError: - pass - USER_JSON[user] = nut - try: - await func(_, message, msg) - except FloodWait as e: - await asyncio.sleep(e.x + 5) - except MessageNotModified: - pass - except Exception: - e = err() - reply_msg = None - if func.__name__ == "trace_bek": - reply_msg = message.reply_to_message - try: - await clog( - "Mikobot", - "Message:\n" + msg["text"] + "\n\n" + "```" + e + "```", - "COMMAND", - msg=message, - replied=reply_msg, - ) - except Exception: - await clog("Mikobot", e, "FAILURE", msg=message) - - return wrapper - - -def check_user(func): - async def wrapper(_, c_q: CallbackQuery): - cq = json.loads(str(c_q)) - user = cq["from_user"]["id"] - if await IGNORE.find_one({"_id": user}): - return - cqowner_is_ch = False - cqowner = cq["data"].split("_").pop() - if "-100" in cqowner: - cqowner_is_ch = True - ccdata = await CC.find_one({"_id": cqowner}) - if ccdata and ccdata["usr"] == user: - user_valid = True - else: - user_valid = False - if user in BOT_OWNER or user == int(cqowner): - if user not in BOT_OWNER: - nt = time() - try: - ot = USER_JSON[user] - if nt - ot < 1.4: - await c_q.answer( - ("Stop spamming bot!!!\n" + "Else you will be blacklisted"), - show_alert=True, - ) - await clog("Mikobot", f"UserID: {user}", "SPAM") - except KeyError: - pass - USER_JSON[user] = nt - try: - await func(_, c_q, cq) - except FloodWait as e: - await asyncio.sleep(e.x + 5) - except MessageNotModified: - pass - except Exception: - e = err() - reply_msg = None - if func.__name__ == "tracemoe_btn": - reply_msg = c_q.message.reply_to_message - try: - await clog( - "Mikobot", - "Callback:\n" + cq["data"] + "\n\n" + "```" + e + "```", - "CALLBACK", - cq=c_q, - replied=reply_msg, - ) - except Exception: - await clog("Mikobot", e, "FAILURE", cq=c_q) - else: - if cqowner_is_ch: - if user_valid: - try: - await func(_, c_q, cq) - except FloodWait as e: - await asyncio.sleep(e.x + 5) - except MessageNotModified: - pass - except Exception: - e = err() - reply_msg = None - if func.__name__ == "tracemoe_btn": - reply_msg = c_q.message.reply_to_message - try: - await clog( - "Mikobot", - "Callback:\n" + cq["data"] + "\n\n" + "```" + e + "```", - "CALLBACK_ANON", - cq=c_q, - replied=reply_msg, - ) - except Exception: - await clog("Mikobot", e, "FAILURE", cq=c_q) - else: - await c_q.answer( - ( - "No one can click buttons on queries made by " - + "channels unless connected with /aniconnect!!!" - ), - show_alert=True, - ) - else: - await c_q.answer( - "Not your query!!!", - show_alert=True, - ) - - return wrapper - - -async def media_to_image(client: app, message: Message, x: Message, replied: Message): - if not (replied.photo or replied.sticker or replied.animation or replied.video): - await x.edit_text("Media Type Is Invalid !") - await asyncio.sleep(5) - await x.delete() - return - media = replied.photo or replied.sticker or replied.animation or replied.video - if not os.path.isdir(DOWN_PATH): - os.makedirs(DOWN_PATH) - dls = await client.download_media( - media, - file_name=DOWN_PATH + rand_key(), - ) - dls_loc = os.path.join(DOWN_PATH, os.path.basename(dls)) - if replied.sticker and replied.sticker.file_name.endswith(".tgs"): - png_file = os.path.join(DOWN_PATH, f"{rand_key()}.png") - cmd = ( - f"lottie_convert.py --frame 0 -if lottie " + f"-of png {dls_loc} {png_file}" - ) - stdout, stderr = (await runcmd(cmd))[:2] - os.remove(dls_loc) - if not os.path.lexists(png_file): - await x.edit_text("This sticker is Gey, Task Failed Successfully ≧ω≦") - await asyncio.sleep(5) - await x.delete() - raise Exception(stdout + stderr) - dls_loc = png_file - elif replied.sticker and replied.sticker.file_name.endswith(".webp"): - stkr_file = os.path.join(DOWN_PATH, f"{rand_key()}.png") - os.rename(dls_loc, stkr_file) - if not os.path.lexists(stkr_file): - await x.edit_text("```Sticker not found...```") - await asyncio.sleep(5) - await x.delete() - return - dls_loc = stkr_file - elif replied.animation or replied.video: - await x.edit_text("`Converting Media To Image ...`") - jpg_file = os.path.join(DOWN_PATH, f"{rand_key()}.jpg") - await take_screen_shot(dls_loc, 0, jpg_file) - os.remove(dls_loc) - if not os.path.lexists(jpg_file): - await x.edit_text("This Gif is Gey (。ì _ í。), Task Failed Successfully !") - await asyncio.sleep(5) - await x.delete() - return - dls_loc = jpg_file - return dls_loc - - -async def runcmd(cmd: str) -> Tuple[str, str, int, int]: - """run command in terminal""" - 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 take_screen_shot( - video_file: str, duration: int, path: str = "" -) -> Optional[str]: - """take a screenshot""" - print( - "[[[Extracting a frame from %s ||| Video duration => %s]]]", - video_file, - duration, - ) - thumb_image_path = path or os.path.join(DOWN_PATH, f"{basename(video_file)}.jpg") - command = ( - f"ffmpeg -ss {duration} " + f'-i "{video_file}" -vframes 1 "{thumb_image_path}"' - ) - err = (await runcmd(command))[1] - if err: - print(err) - return thumb_image_path if os.path.exists(thumb_image_path) else None - - -async def get_user_from_channel(cid): - try: - k = (await CC.find_one({"_id": str(cid)}))["usr"] - return k - except TypeError: - return None - - -async def return_json_senpai( - query: str, vars_: dict, auth: bool = False, user: int = None -): - url = "https://graphql.anilist.co" - headers = None - if auth: - headers = { - "Authorization": ( - "Bearer " + str((await AUTH_USERS.find_one({"id": int(user)}))["token"]) - ), - "Content-Type": "application/json", - "Accept": "application/json", - } - return requests.post( - url, json={"query": query, "variables": vars_}, headers=headers - ).json() - - -def cflag(country): - if country == "JP": - return "\U0001F1EF\U0001F1F5" - if country == "CN": - return "\U0001F1E8\U0001F1F3" - if country == "KR": - return "\U0001F1F0\U0001F1F7" - if country == "TW": - return "\U0001F1F9\U0001F1FC" - - -def pos_no(no): - ep_ = list(str(no)) - x = ep_.pop() - if ep_ != [] and ep_.pop() == "1": - return "th" - th = "st" if x == "1" else "nd" if x == "2" else "rd" if x == "3" else "th" - return th - - -def make_it_rw(time_stamp): - """Converting Time Stamp to Readable Format""" - seconds, milliseconds = divmod(int(time_stamp), 1000) - minutes, seconds = divmod(seconds, 60) - hours, minutes = divmod(minutes, 60) - days, hours = divmod(hours, 24) - tmp = ( - ((str(days) + " Days, ") if days else "") - + ((str(hours) + " Hours, ") if hours else "") - + ((str(minutes) + " Minutes, ") if minutes else "") - + ((str(seconds) + " Seconds, ") if seconds else "") - + ((str(milliseconds) + " ms, ") if milliseconds else "") - ) - return tmp[:-2] - - -async def clog( - name: str, - text: str, - tag: str, - msg: Message = None, - cq: CallbackQuery = None, - replied: Message = None, - file: str = None, - send_as_file: str = None, -): - log = f"#{name.upper()} #{tag.upper()}\n\n{text}" - data = "" - if msg: - data += str(msg) - data += "\n\n\n\n" - if cq: - data += str(cq) - data += "\n\n\n\n" - await app.send_message(chat_id=MESSAGE_DUMP, text=log) - if msg or cq: - with open("query_data.txt", "x") as output: - output.write(data) - await app.send_document(MESSAGE_DUMP, "query_data.txt") - os.remove("query_data.txt") - if replied: - media = replied.photo or replied.sticker or replied.animation or replied.video - media_path = await app.download_media(media) - await app.send_document(MESSAGE_DUMP, media_path) - if file: - await app.send_document(MESSAGE_DUMP, file) - if send_as_file: - with open("dataInQuestio.txt", "x") as text_file: - text_file.write() - await app.send_document(MESSAGE_DUMP, "dataInQuestio.txt") - os.remove("dataInQuestio.txt") - - -def get_btns( - media, - user: int, - result: list, - lsqry: str = None, - lspage: int = None, - auth: bool = False, - sfw: str = "False", -): - buttons = [] - qry = f"_{lsqry}" if lsqry is not None else "" - pg = f"_{lspage}" if lspage is not None else "" - if media == "ANIME" and sfw == "False": - buttons.append( - [ - InlineKeyboardButton( - text="Characters", - callback_data=( - f"char_{result[2][0]}_ANI" + f"{qry}{pg}_{str(auth)}_1_{user}" - ), - ), - InlineKeyboardButton( - text="Description", - callback_data=( - f"desc_{result[2][0]}_ANI" + f"{qry}{pg}_{str(auth)}_{user}" - ), - ), - InlineKeyboardButton( - text="List Series", - callback_data=( - f"ls_{result[2][0]}_ANI" + f"{qry}{pg}_{str(auth)}_{user}" - ), - ), - ] - ) - if media == "CHARACTER": - buttons.append( - [ - InlineKeyboardButton( - "Description", - callback_data=( - f"desc_{result[2][0]}_CHAR" + f"{qry}{pg}_{str(auth)}_{user}" - ), - ) - ] - ) - buttons.append( - [ - InlineKeyboardButton( - "List Series", - callback_data=f"lsc_{result[2][0]}{qry}{pg}_{str(auth)}_{user}", - ) - ] - ) - if media == "SCHEDULED": - if result[0] != 0 and result[0] != 6: - buttons.append( - [ - InlineKeyboardButton( - str(day_(result[0] - 1)), - callback_data=f"sched_{result[0]-1}_{user}", - ), - InlineKeyboardButton( - str(day_(result[0] + 1)), - callback_data=f"sched_{result[0]+1}_{user}", - ), - ] - ) - if result[0] == 0: - buttons.append( - [ - InlineKeyboardButton( - str(day_(result[0] + 1)), - callback_data=f"sched_{result[0]+1}_{user}", - ) - ] - ) - if result[0] == 6: - buttons.append( - [ - InlineKeyboardButton( - str(day_(result[0] - 1)), - callback_data=f"sched_{result[0]-1}_{user}", - ) - ] - ) - if media == "MANGA" and sfw == "False": - buttons.append([InlineKeyboardButton("More Info", url=result[1][2])]) - if media == "AIRING" and sfw == "False": - buttons.append([InlineKeyboardButton("More Info", url=result[1][0])]) - if auth is True and media != "SCHEDULED" and sfw == "False": - auth_btns = get_auth_btns(media, user, result[2], lspage=lspage, lsqry=lsqry) - buttons.append(auth_btns) - if len(result) > 3: - if result[3] == "None": - if result[4] != "None": - buttons.append( - [ - InlineKeyboardButton( - text="Sequel", - callback_data=f"btn_{result[4]}_{str(auth)}_{user}", - ) - ] - ) - else: - if result[4] != "None": - buttons.append( - [ - InlineKeyboardButton( - text="Prequel", - callback_data=f"btn_{result[3]}_{str(auth)}_{user}", - ), - InlineKeyboardButton( - text="Sequel", - callback_data=f"btn_{result[4]}_{str(auth)}_{user}", - ), - ] - ) - else: - buttons.append( - [ - InlineKeyboardButton( - text="Prequel", - callback_data=f"btn_{result[3]}_{str(auth)}_{user}", - ) - ] - ) - if (lsqry is not None) and (len(result) != 1): - if lspage == 1: - if result[1][1] is True: - buttons.append( - [ - InlineKeyboardButton( - text="Next", - callback_data=( - f"page_{media}{qry}_{int(lspage)+1}_{str(auth)}_{user}" - ), - ) - ] - ) - else: - pass - elif lspage != 1: - if result[1][1] is False: - buttons.append( - [ - InlineKeyboardButton( - text="Prev", - callback_data=( - f"page_{media}{qry}_{int(lspage)-1}_{str(auth)}_{user}" - ), - ) - ] - ) - else: - buttons.append( - [ - InlineKeyboardButton( - text="Prev", - callback_data=( - f"page_{media}{qry}_{int(lspage)-1}_{str(auth)}_{user}" - ), - ), - InlineKeyboardButton( - text="Next", - callback_data=( - f"page_{media}{qry}_{int(lspage)+1}_{str(auth)}_{user}" - ), - ), - ] - ) - return InlineKeyboardMarkup(buttons) - - -def get_auth_btns(media, user, data, lsqry: str = None, lspage: int = None): - btn = [] - qry = f"_{lsqry}" if lsqry is not None else "" - pg = f"_{lspage}" if lspage is not None else "" - if media == "CHARACTER": - btn.append( - InlineKeyboardButton( - text=("Add to Favs" if data[1] is not True else "Remove from Favs"), - callback_data=f"fav_{media}_{data[0]}{qry}{pg}_{user}", - ) - ) - else: - btn.append( - InlineKeyboardButton( - text=("Add to Favs" if data[3] is not True else "Remove from Favs"), - callback_data=f"fav_{media}_{data[0]}{qry}{pg}_{user}", - ) - ) - btn.append( - InlineKeyboardButton( - text="Add to List" if data[1] is False else "Update in List", - callback_data=( - f"lsadd_{media}_{data[0]}{qry}{pg}_{user}" - if data[1] is False - else f"lsupdt_{media}_{data[0]}_{data[2]}{qry}{pg}_{user}" - ), - ) - ) - return btn - - -def day_(x: int): - if x == 0: - return "Monday" - if x == 1: - return "Tuesday" - if x == 2: - return "Wednesday" - if x == 3: - return "Thursday" - if x == 4: - return "Friday" - if x == 5: - return "Saturday" - if x == 6: - return "Sunday" - - -def season_(future: bool = False): - k = datetime.now() - m = k.month - if future: - m = m + 3 - y = k.year - if m > 12: - y = y + 1 - if m in [1, 2, 3] or m > 12: - return "WINTER", y - if m in [4, 5, 6]: - return "SPRING", y - if m in [7, 8, 9]: - return "SUMMER", y - if m in [10, 11, 12]: - return "FALL", y - - -class google_new_transError(Exception): - """Exception that uses context to present a meaningful error message""" - - def __init__(self, msg=None, **kwargs): - self.tts = kwargs.pop("tts", None) - self.rsp = kwargs.pop("response", None) - if msg: - self.msg = msg - elif self.tts is not None: - self.msg = self.infer_msg(self.tts, self.rsp) - else: - self.msg = None - super(google_new_transError, self).__init__(self.msg) - - def infer_msg(self, tts, rsp=None): - cause = "Unknown" - - if rsp is None: - premise = "Failed to connect" - - return "{}. Probable cause: {}".format(premise, "timeout") - # if tts.tld != 'com': - # host = _translate_url(tld=tts.tld) - # cause = "Host '{}' is not reachable".format(host) - - else: - status = rsp.status_code - reason = rsp.reason - - premise = "{:d} ({}) from TTS API".format(status, reason) - - if status == 403: - cause = "Bad token or upstream API changes" - elif status == 200 and not tts.lang_check: - cause = ( - "No audio stream in response. Unsupported language '%s'" - % self.tts.lang - ) - elif status >= 500: - cause = "Uptream API error. Try again later." - - return "{}. Probable cause: {}".format(premise, cause) - - -class google_translator: - """ - You can use 108 language in target and source,details view LANGUAGES. - Target language: like 'en'、'zh'、'th'... - :param url_suffix: The source text(s) to be translated. Batch translation is supported via sequence input. - The value should be one of the url_suffix listed in : `DEFAULT_SERVICE_URLS` - :type url_suffix: UTF-8 :class:`str`; :class:`unicode`; string sequence (list, tuple, iterator, generator) - :param text: The source text(s) to be translated. - :type text: UTF-8 :class:`str`; :class:`unicode`; - :param lang_tgt: The language to translate the source text into. - The value should be one of the language codes listed in : `LANGUAGES` - :type lang_tgt: :class:`str`; :class:`unicode` - :param lang_src: The language of the source text. - The value should be one of the language codes listed in :const:`googletrans.LANGUAGES` - If a language is not specified, - the system will attempt to identify the source language automatically. - :type lang_src: :class:`str`; :class:`unicode` - :param timeout: Timeout Will be used for every request. - :type timeout: number or a double of numbers - :param proxies: proxies Will be used for every request. - :type proxies: class : dict; like: {'http': 'http:171.112.169.47:19934/', 'https': 'https:171.112.169.47:19934/'} - """ - - def __init__(self, url_suffix="cn", timeout=5, proxies=None): - self.proxies = proxies - if url_suffix not in URLS_SUFFIX: - self.url_suffix = URL_SUFFIX_DEFAULT - else: - self.url_suffix = url_suffix - url_base = "https://translate.google.{}".format(self.url_suffix) - self.url = url_base + "/_/TranslateWebserverUi/data/batchexecute" - self.timeout = timeout - - def _package_rpc(self, text, lang_src="auto", lang_tgt="auto"): - GOOGLE_TTS_RPC = ["MkEWBc"] - parameter = [[text.strip(), lang_src, lang_tgt, True], [1]] - escaped_parameter = json.dumps(parameter, separators=(",", ":")) - rpc = [[[random.choice(GOOGLE_TTS_RPC), escaped_parameter, None, "generic"]]] - espaced_rpc = json.dumps(rpc, separators=(",", ":")) - # text_urldecode = quote(text.strip()) - freq_initial = "f.req={}&".format(quote(espaced_rpc)) - freq = freq_initial - return freq - - def translate(self, text, lang_tgt="auto", lang_src="auto", pronounce=False): - try: - lang = LANGUAGES[lang_src] - except Exception: - lang_src = "auto" - try: - lang = LANGUAGES[lang_tgt] - except Exception: - lang_src = "auto" - text = str(text) - if len(text) >= 5000: - return "Warning: Can only detect less than 5000 characters" - if len(text) == 0: - return "" - headers = { - "Referer": "http://translate.google.{}/".format(self.url_suffix), - "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) " - "AppleWebKit/537.36 (KHTML, like Gecko) " - "Chrome/47.0.2526.106 Safari/537.36", - "Content-Type": "application/x-www-form-urlencoded;charset=utf-8", - } - freq = self._package_rpc(text, lang_src, lang_tgt) - response = requests.Request( - method="POST", - url=self.url, - data=freq, - headers=headers, - ) - try: - if self.proxies is None or type(self.proxies) != dict: - self.proxies = {} - with requests.Session() as s: - s.proxies = self.proxies - r = s.send( - request=response.prepare(), verify=False, timeout=self.timeout - ) - for line in r.iter_lines(chunk_size=1024): - decoded_line = line.decode("utf-8") - if "MkEWBc" in decoded_line: - try: - response = decoded_line - response = json.loads(response) - response = list(response) - response = json.loads(response[0][2]) - response_ = list(response) - response = response_[1][0] - if len(response) == 1: - if len(response[0]) > 5: - sentences = response[0][5] - else: ## only url - sentences = response[0][0] - if pronounce is False: - return sentences - elif pronounce == True: - return [sentences, None, None] - translate_text = "" - for sentence in sentences: - sentence = sentence[0] - translate_text += sentence.strip() + " " - translate_text = translate_text - if pronounce is False: - return translate_text - elif pronounce == True: - pronounce_src = response_[0][0] - pronounce_tgt = response_[1][0][0][1] - return [translate_text, pronounce_src, pronounce_tgt] - elif len(response) == 2: - sentences = [] - for i in response: - sentences.append(i[0]) - if pronounce is False: - return sentences - elif pronounce == True: - pronounce_src = response_[0][0] - pronounce_tgt = response_[1][0][0][1] - return [sentences, pronounce_src, pronounce_tgt] - except Exception as e: - raise e - r.raise_for_status() - except requests.exceptions.ConnectTimeout as e: - raise e - except requests.exceptions.HTTPError as e: - # Request successful, bad response - raise google_new_transError(tts=self, response=r) - except requests.exceptions.RequestException as e: - # Request failed - raise google_new_transError(tts=self) - - def detect(self, text): - text = str(text) - if len(text) >= 5000: - return log.debug("Warning: Can only detect less than 5000 characters") - if len(text) == 0: - return "" - headers = { - "Referer": "http://translate.google.{}/".format(self.url_suffix), - "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) " - "AppleWebKit/537.36 (KHTML, like Gecko) " - "Chrome/47.0.2526.106 Safari/537.36", - "Content-Type": "application/x-www-form-urlencoded;charset=utf-8", - } - freq = self._package_rpc(text) - response = requests.Request( - method="POST", url=self.url, data=freq, headers=headers - ) - try: - if self.proxies is None or type(self.proxies) != dict: - self.proxies = {} - with requests.Session() as s: - s.proxies = self.proxies - r = s.send( - request=response.prepare(), verify=False, timeout=self.timeout - ) - - for line in r.iter_lines(chunk_size=1024): - decoded_line = line.decode("utf-8") - if "MkEWBc" in decoded_line: - # regex_str = r"\[\[\"wrb.fr\",\"MkEWBc\",\"\[\[(.*).*?,\[\[\[" - try: - # data_got = re.search(regex_str,decoded_line).group(1) - response = decoded_line - response = json.loads(response) - response = list(response) - response = json.loads(response[0][2]) - response = list(response) - detect_lang = response[0][2] - except Exception: - raise Exception - # data_got = data_got.split('\\\"]')[0] - return [detect_lang, LANGUAGES[detect_lang.lower()]] - r.raise_for_status() - except requests.exceptions.HTTPError as e: - # Request successful, bad response - log.debug(str(e)) - raise google_new_transError(tts=self, response=r) - except requests.exceptions.RequestException as e: - # Request failed - log.debug(str(e)) - raise google_new_transError(tts=self) - - -async def uidata(id_): - data = await GUI.find_one({"_id": str(id_)}) - if data is not None: - bullet = str(data["bl"]) + " " - if data["bl"] is None: - bullet = "" - return bullet, data["cs"] - return ["➤ ", "UPPER"] - - -async def get_ui_text(case): - if case == "UPPER": - return [ - "SOURCE", - "TYPE", - "SCORE", - "DURATION", - "USER DATA", - "ADULT RATED", - "STATUS", - "GENRES", - "TAGS", - "SEQUEL", - "PREQUEL", - "NEXT AIRING", - "DESCRIPTION", - "VOLUMES", - "CHAPTERS", - ] - else: - return [ - "Source", - "Type", - "Score", - "Duration", - "User Data", - "Adult Rated", - "Status", - "Genres", - "Tags", - "Sequel", - "Prequel", - "Next Airing", - "Description", - "Volumes", - "Chapters", - ] - - -tr = google_translator() -ANIME_DB, MANGA_DB, CHAR_DB, STUDIO_DB, AIRING_DB = {}, {}, {}, {}, {} -GUI = get_collection("GROUP_UI") - -#### Anilist part #### - -ANIME_TEMPLATE = """{name} - -**ID | MAL ID:** `{idm}` | `{idmal}` -{bl}**{psrc}:** `{source}` -{bl}**{ptype}:** `{formats}`{avscd}{dura}{user_data} -{status_air}{gnrs_}{tags_} - -🎬 {trailer_link} -📖 <a href="{url}">Official Site</a> - -{additional}""" - - -# GraphQL Queries. -ANIME_QUERY = """ -query ($id: Int, $idMal:Int, $search: String) { - Media (id: $id, idMal: $idMal, search: $search, type: ANIME) { - id - idMal - title { - romaji - english - native - } - format - status - episodes - duration - countryOfOrigin - source (version: 2) - trailer { - id - site - } - genres - tags { - name - } - averageScore - relations { - edges { - node { - title { - romaji - english - } - id - type - } - relationType - } - } - nextAiringEpisode { - timeUntilAiring - episode - } - isAdult - isFavourite - mediaListEntry { - status - score - id - } - siteUrl - } -} -""" - -ISADULT = """ -query ($id: Int) { - Media (id: $id) { - isAdult - } -} -""" - -BROWSE_QUERY = """ -query ($s: MediaSeason, $y: Int, $sort: [MediaSort]) { - Page { - media (season: $s, seasonYear: $y, sort: $sort) { - title { - romaji - } - format - } - } -} -""" - -FAV_ANI_QUERY = """ -query ($id: Int, $page: Int) { - User (id: $id) { - favourites { - anime (page: $page, perPage: 10) { - pageInfo { - lastPage - hasNextPage - } - edges { - node { - title { - romaji - } - siteUrl - } - } - } - } - } -} -""" - -FAV_MANGA_QUERY = """ -query ($id: Int, $page: Int) { - User (id: $id) { - favourites { - manga (page: $page, perPage: 10) { - pageInfo { - lastPage - hasNextPage - } - edges { - node { - title { - romaji - } - siteUrl - } - } - } - } - } -} -""" - -FAV_CHAR_QUERY = """ -query ($id: Int, $page: Int) { - User (id: $id) { - favourites { - characters (page: $page, perPage: 10) { - pageInfo { - lastPage - hasNextPage - } - edges { - node { - name { - full - } - siteUrl - } - } - } - } - } -} -""" - -VIEWER_QRY = """ -query { - Viewer { - id - name - siteUrl - statistics { - anime { - count - minutesWatched - episodesWatched - meanScore - } - manga { - count - chaptersRead - volumesRead - meanScore - } - } - } -} -""" - -USER_QRY = """ -query ($search: String) { - User (name: $search) { - id - name - siteUrl - statistics { - anime { - count - minutesWatched - episodesWatched - meanScore - } - manga { - count - chaptersRead - volumesRead - meanScore - } - } - } -} -""" - -ANIME_MUTATION = """ -mutation ($id: Int) { - ToggleFavourite (animeId: $id) { - anime { - pageInfo { - total - } - } - } -} -""" - -MANGA_MUTATION = """ -mutation ($id: Int) { - ToggleFavourite (mangaId: $id) { - manga { - pageInfo { - total - } - } - } -} -""" - -STUDIO_MUTATION = """ -mutation ($id: Int) { - ToggleFavourite (studioId: $id) { - studios { - pageInfo { - total - } - } - } -} -""" - -CHAR_MUTATION = """ -mutation ($id: Int) { - ToggleFavourite (characterId: $id) { - characters { - pageInfo { - total - } - } - } -} -""" - -ANILIST_MUTATION = """ -mutation ($id: Int, $status: MediaListStatus) { - SaveMediaListEntry (mediaId: $id, status: $status) { - media { - title { - romaji - } - } - } -} -""" - -ANILIST_MUTATION_UP = """ -mutation ($id: [Int], $status: MediaListStatus) { - UpdateMediaListEntries (ids: $id, status: $status) { - media { - title { - romaji - } - } - } -} -""" - -ANILIST_MUTATION_DEL = """ -mutation ($id: Int) { - DeleteMediaListEntry (id: $id) { - deleted - } -} -""" - -AIR_QUERY = """ -query ($search: String, $page: Int) { - Page (perPage: 1, page: $page) { - pageInfo { - total - hasNextPage - } - media (search: $search, type: ANIME) { - id - title { - romaji - english - } - status - countryOfOrigin - nextAiringEpisode { - timeUntilAiring - episode - } - siteUrl - isFavourite - isAdult - mediaListEntry { - status - id - } - } - } -} -""" - -DES_INFO_QUERY = """ -query ($id: Int) { - Media (id: $id) { - id - description (asHtml: false) - } -} -""" - -CHA_INFO_QUERY = """ -query ($id: Int, $page: Int) { - Media (id: $id, type: ANIME) { - id - characters (page: $page, perPage: 25, sort: ROLE) { - pageInfo { - hasNextPage - lastPage - total - } - edges { - node { - name { - full - } - } - role - } - } - } -} -""" - -REL_INFO_QUERY = """ -query ($id: Int) { - Media (id: $id, type: ANIME) { - id - relations { - edges { - node { - title { - romaji - } - type - } - relationType - } - } - } -} -""" - -PAGE_QUERY = """ -query ($search: String, $page: Int) { - Page (perPage: 1, page: $page) { - pageInfo { - total - hasNextPage - } - media (search: $search, type: ANIME) { - id - idMal - title { - romaji - english - native - } - format - status - episodes - duration - countryOfOrigin - source (version: 2) - trailer { - id - site - } - genres - tags { - name - } - averageScore - relations { - edges { - node { - title { - romaji - english - } - type - } - relationType - } - } - nextAiringEpisode { - timeUntilAiring - episode - } - isAdult - isFavourite - mediaListEntry { - status - score - id - } - siteUrl - } - } -} -""" - -CHARACTER_QUERY = """ -query ($id: Int, $search: String, $page: Int) { - Page (perPage: 1, page: $page) { - pageInfo { - total - hasNextPage - } - characters (id: $id, search: $search) { - id - name { - full - native - } - image { - large - } - media (type: ANIME) { - edges { - node { - title { - romaji - } - type - } - voiceActors (language: JAPANESE) { - name { - full - } - siteUrl - } - } - } - isFavourite - siteUrl - } - } -} -""" - -MANGA_QUERY = """ -query ($search: String, $page: Int) { - Page (perPage: 1, page: $page) { - pageInfo { - total - hasNextPage - } - media (search: $search, type: MANGA) { - id - title { - romaji - english - native - } - format - countryOfOrigin - source (version: 2) - status - description(asHtml: true) - chapters - isFavourite - mediaListEntry { - status - score - id - } - volumes - averageScore - siteUrl - isAdult - } - } -} -""" - - -DESC_INFO_QUERY = """ -query ($id: Int) { - Character (id: $id) { - image { - large - } - description(asHtml: false) - } -} -""" - -LS_INFO_QUERY = """ -query ($id: Int) { - Character (id: $id) { - image { - large - } - media (page: 1, perPage: 25) { - nodes { - title { - romaji - english - } - type - } - } - } -} -""" - -ACTIVITY_QUERY = """ -query ($id: Int) { - Page (perPage: 12) { - activities (userId: $id, type: MEDIA_LIST, sort: ID_DESC) { - ...kek - } - } -} -fragment kek on ListActivity { - type - media { - title { - romaji - } - siteUrl - } - progress - status -} -""" - -TOP_QUERY = """ -query ($gnr: String, $page: Int) { - Page (perPage: 15, page: $page) { - pageInfo { - lastPage - total - hasNextPage - } - media (genre: $gnr, sort: SCORE_DESC, type: ANIME) { - title { - romaji - } - } - } -} -""" - -TOPT_QUERY = """ -query ($gnr: String, $page: Int) { - Page (perPage: 15, page: $page) { - pageInfo { - lastPage - total - hasNextPage - } - media (tag: $gnr, sort: SCORE_DESC, type: ANIME) { - title { - romaji - } - } - } -} -""" - -ALLTOP_QUERY = """ -query ($page: Int) { - Page (perPage: 15, page: $page) { - pageInfo { - lastPage - total - hasNextPage - } - media (sort: SCORE_DESC, type: ANIME) { - title { - romaji - } - } - } -} -""" - -GET_GENRES = """ -query { - GenreCollection -} -""" - -GET_TAGS = """ -query{ - MediaTagCollection { - name - isAdult - } -} -""" - -RECOMMENDTIONS_QUERY = """ -query ($id: Int) { - Media (id: $id) { - recommendations (perPage: 25) { - edges { - node { - mediaRecommendation { - title { - romaji - } - id - siteUrl - } - } - } - } - } -} -""" - -STUDIO_QUERY = """ -query ($search: String, $page: Int) { - Page (page: $page, perPage: 1) { - pageInfo { - total - hasNextPage - } - studios (search: $search) { - id - name - siteUrl - isFavourite - } - } -} -""" - -STUDIO_ANI_QUERY = """ -query ($id: Int, $page: Int) { - Studio (id: $id) { - name - media (page: $page) { - pageInfo { - total - lastPage - hasNextPage - } - edges { - node { - title { - romaji - } - seasonYear - } - } - } - } -} -""" - - -async def get_studios(qry, page, user, duser=None, auth: bool = False): - page = int(page) - vars_ = {"search": STUDIO_DB[qry], "page": int(page)} - result = await return_json_senpai(STUDIO_QUERY, vars_, auth, user) - if result["data"]["Page"]["studios"] == []: - return ["Not Found"] - data = result["data"]["Page"]["studios"][0] - isFav = data["isFavourite"] - msg = ( - f"**{data['name']}**{', ♥️' if isFav is True else ''}" - + f"\n\n**ID:** {data['id']}\n[Website]({data['siteUrl']})" - ) - if not duser: - duser = user - btns = [] - btns.append( - [ - InlineKeyboardButton( - "List Animes", - callback_data=f"stuani_1_{data['id']}_{page}_{qry}_{auth}_{duser}", - ) - ] - ) - if auth: - btns.append( - [ - InlineKeyboardButton( - "Remove from Favs" if isFav else "Add To Favs", - callback_data=f"fav_STUDIO_{data['id']}_{qry}_{page}_{duser}", - ) - ] - ) - pi = result["data"]["Page"]["pageInfo"]["hasNextPage"] - if pi is False: - if int(page) == 1: - return msg, btns - else: - btns.append( - [ - InlineKeyboardButton( - "Prev", callback_data=f"pgstudio_{page-1}_{qry}_{auth}_{duser}" - ) - ] - ) - else: - if int(page) == 1: - btns.append( - [ - InlineKeyboardButton( - "Next", callback_data=f"pgstudio_2_{qry}_{auth}_{duser}" - ) - ] - ) - else: - btns.append( - [ - InlineKeyboardButton( - "Prev", callback_data=f"pgstudio_{page-1}_{qry}_{auth}_{duser}" - ), - InlineKeyboardButton( - "Next", callback_data=f"pgstudio_{page+1}_{qry}_{auth}_{duser}" - ), - ] - ) - return msg, InlineKeyboardMarkup(btns) - - -async def get_studio_animes(id_, page, qry, rp, user, duser=None, auth: bool = False): - vars_ = {"id": id_, "page": int(page)} - result = await return_json_senpai(STUDIO_ANI_QUERY, vars_, auth, user) - data = result["data"]["Studio"]["media"]["edges"] - if data == []: - return ["No results found"] - msg = f"List of animes by {result['data']['Studio']['name']} studio\n" - for i in data: - msg += ( - f"\n⚬ `{i['node']['title']['romaji']}`" - + f" __({i['node']['seasonYear']})__" - ) - btns = [] - if not duser: - duser = user - pi = result["data"]["Studio"]["media"]["pageInfo"] - if pi["hasNextPage"] is False: - if int(page) == 1: - btns.append( - [ - InlineKeyboardButton( - "Back", callback_data=f"pgstudio_{rp}_{qry}_{auth}_{duser}" - ) - ] - ) - return msg, btns - else: - btns.append( - [ - InlineKeyboardButton( - "Prev", - callback_data=f"stuani_{int(page)-1}_{id_}_{rp}_{qry}_{auth}_{duser}", - ) - ] - ) - else: - if int(page) == 1: - btns.append( - [ - InlineKeyboardButton( - "Next", - callback_data=f"stuani_2_{id_}_{rp}_{qry}_{auth}_{duser}", - ) - ] - ) - else: - btns.append( - [ - InlineKeyboardButton( - "Prev", - callback_data=f"stuani_{int(page)-1}_{id_}_{rp}_{qry}_{auth}_{duser}", - ), - InlineKeyboardButton( - "Next", - callback_data=f"stuani_{int(page)+1}_{id_}_{rp}_{qry}_{auth}_{duser}", - ), - ] - ) - btns.append( - [ - InlineKeyboardButton( - "Back", callback_data=f"pgstudio_{rp}_{qry}_{auth}_{duser}" - ) - ] - ) - return msg, InlineKeyboardMarkup(btns) - - -async def get_all_tags(text: str = None): - vars_ = {} - result = await return_json_senpai(GET_TAGS, vars_, auth=False, user=None) - msg = "**Tags List:**\n\n`" - kek = [] - for i in result["data"]["MediaTagCollection"]: - if text is not None and "nsfw" in text: - if str(i["isAdult"]) != "False": - kek.append(i["name"]) - else: - if str(i["isAdult"]) == "False": - kek.append(i["name"]) - msg += ", ".join(kek) - msg += "`" - return msg - - -async def get_all_genres(): - vars_ = {} - result = await return_json_senpai(GET_GENRES, vars_, auth=False) - msg = "**Genres List:**\n\n" - for i in result["data"]["GenreCollection"]: - msg += f"`{i}`\n" - return msg - - -async def get_user_activity(id_, user, duser=None): - vars_ = {"id": id_} - result = await return_json_senpai(ACTIVITY_QUERY, vars_, auth=True, user=user) - data = result["data"]["Page"]["activities"] - msg = "" - for i in data: - try: - name = f"[{i['media']['title']['romaji']}]" + f"({i['media']['siteUrl']})" - if i["status"] in ["watched episode", "read chapter"]: - msg += ( - f"⚬ {str(i['status']).capitalize()} " - + f"{i['progress']} of {name}\n" - ) - else: - progress = i["progress"] - of = "of" - if i["status"] == "dropped": - of = "at" - msg += ( - f"⚬ {str(i['status']).capitalize()}" - + f"{f'{progress} {of} ' if progress is not None else ' '}" - + f"{name}\n" - ) - except KeyError: - pass - if duser is None: - duser = user - btn = [[InlineKeyboardButton("Back", callback_data=f"getusrbc_{duser}")]] - return [ - f"https://img.anili.st/user/{id_}?a={time.time()}", - msg, - InlineKeyboardMarkup(btn), - ] - - -async def get_recommendations(id_): - vars_ = {"id": int(id_)} - result = await return_json_senpai(RECOMMENDTIONS_QUERY, vars_) - data = result["data"]["Media"]["recommendations"]["edges"] - rc_ls = [] - for i in data: - ii = i["node"]["mediaRecommendation"] - rc_ls.append([ii["title"]["romaji"], ii["id"], ii["siteUrl"]]) - if rc_ls == []: - return "No Recommendations available related to given anime!!!" - outstr = "Recommended animes:\n\n" - for i in rc_ls: - outstr += ( - f"**{i[0]}**\n ➥[Synopsis]" - + f"(https://t.me/{BOT_USERNAME}?astart=anime_{i[1]})" - + f"\n ➥[Official Site]({i[2]})\n\n" - ) - return outstr - - -async def get_top_animes(gnr: str, page, user): - vars_ = {"gnr": gnr.lower(), "page": int(page)} - query = TOP_QUERY - msg = f"Top animes for genre `{gnr.capitalize()}`:\n\n" - if gnr == "None": - query = ALLTOP_QUERY - vars_ = {"page": int(page)} - msg = f"Top animes:\n\n" - nsfw = False - result = await return_json_senpai(query, vars_, auth=False, user=user) - if len(result["data"]["Page"]["media"]) == 0: - query = TOPT_QUERY - msg = f"Top animes for tag `{gnr.capitalize()}`:\n\n" - result = await return_json_senpai(query, vars_, auth=False, user=user) - if len(result["data"]["Page"]["media"]) == 0: - return [f"No results Found"] - nsls = await get_all_tags("nsfw") - nsfw = True if gnr.lower() in nsls.lower() else False - data = result["data"]["Page"] - for i in data["media"]: - msg += f"⚬ `{i['title']['romaji']}`\n" - msg += f"\nTotal available animes: `{data['pageInfo']['total']}`" - btn = [] - if int(page) == 1: - if int(data["pageInfo"]["lastPage"]) != 1: - btn.append( - [ - InlineKeyboardButton( - "Next", callback_data=f"topanimu_{gnr}_{int(page)+1}_{user}" - ) - ] - ) - elif int(page) == int(data["pageInfo"]["lastPage"]): - btn.append( - [ - InlineKeyboardButton( - "Prev", callback_data=f"topanimu_{gnr}_{int(page)-1}_{user}" - ) - ] - ) - else: - btn.append( - [ - InlineKeyboardButton( - "Prev", callback_data=f"topanimu_{gnr}_{int(page)-1}_{user}" - ), - InlineKeyboardButton( - "Next", callback_data=f"topanimu_{gnr}_{int(page)+1}_{user}" - ), - ] - ) - return [msg, nsfw], InlineKeyboardMarkup(btn) if len(btn) != 0 else "" - - -async def get_user_favourites(id_, user, req, page, sighs, duser=None): - vars_ = {"id": int(id_), "page": int(page)} - result = await return_json_senpai( - FAV_ANI_QUERY - if req == "ANIME" - else FAV_CHAR_QUERY - if req == "CHAR" - else FAV_MANGA_QUERY, - vars_, - auth=True, - user=int(user), - ) - data = result["data"]["User"]["favourites"][ - "anime" if req == "ANIME" else "characters" if req == "CHAR" else "manga" - ] - msg = ( - "Favourite Animes:\n\n" - if req == "ANIME" - else "Favourite Characters:\n\n" - if req == "CHAR" - else "Favourite Manga:\n\n" - ) - for i in data["edges"]: - node_name = ( - i["node"]["title"]["romaji"] if req != "CHAR" else i["node"]["name"]["full"] - ) - msg += f"⚬ [{node_name}]({i['node']['siteUrl']})\n" - btn = [] - if duser is None: - duser = user - if int(page) == 1: - if int(data["pageInfo"]["lastPage"]) != 1: - btn.append( - [ - InlineKeyboardButton( - "Next", - callback_data=( - f"myfavqry_{req}_{id_}_{str(int(page)+1)}" - + f"_{sighs}_{duser}" - ), - ) - ] - ) - elif int(page) == int(data["pageInfo"]["lastPage"]): - btn.append( - [ - InlineKeyboardButton( - "Prev", - callback_data=( - f"myfavqry_{req}_{id_}_{str(int(page)-1)}_{sighs}_{duser}" - ), - ) - ] - ) - else: - btn.append( - [ - InlineKeyboardButton( - "Prev", - callback_data=( - f"myfavqry_{req}_{id_}_{str(int(page)-1)}_{sighs}_{duser}" - ), - ), - InlineKeyboardButton( - "Next", - callback_data=( - f"myfavqry_{req}_{id_}_{str(int(page)+1)}_{sighs}_{duser}" - ), - ), - ] - ) - btn.append( - [InlineKeyboardButton("Back", callback_data=f"myfavs_{id_}_{sighs}_{user}")] - ) - return [ - f"https://img.anili.st/user/{id_}?a=({time.time()})", - msg, - InlineKeyboardMarkup(btn), - ] - - -async def get_featured_in_lists( - idm, req, auth: bool = False, user: int = None, page: int = 0 -): - vars_ = {"id": int(idm)} - result = await return_json_senpai(LS_INFO_QUERY, vars_, auth=auth, user=user) - data = result["data"]["Character"]["media"]["nodes"] - if req == "ANI": - out = "ANIMES:\n\n" - out_ = [] - for ani in data: - k = ani["title"]["english"] or ani["title"]["romaji"] - kk = ani["type"] - if kk == "ANIME": - out_.append(f"• __{k}__\n") - else: - out = "MANGAS:\n\n" - out_ = [] - for ani in data: - k = ani["title"]["english"] or ani["title"]["romaji"] - kk = ani["type"] - if kk == "MANGA": - out_.append(f"• __{k}__\n") - total = len(out_) - for _ in range(15 * page): - out_.pop(0) - out_ = "".join(out_[:15]) - return ([out + out_, total] if len(out_) != 0 else False), result["data"][ - "Character" - ]["image"]["large"] - - -async def get_additional_info( - idm, ctgry, req=None, auth: bool = False, user: int = None, page: int = 0 -): - vars_ = {"id": int(idm)} - if req == "char": - vars_["page"] = page - result = await return_json_senpai( - ( - ( - DES_INFO_QUERY - if req == "desc" - else CHA_INFO_QUERY - if req == "char" - else REL_INFO_QUERY - ) - if ctgry == "ANI" - else DESC_INFO_QUERY - ), - vars_, - ) - data = result["data"]["Media"] if ctgry == "ANI" else result["data"]["Character"] - pic = f"https://img.anili.st/media/{idm}" - if req == "desc": - synopsis = data.get("description") - if os.environ.get("PREFERRED_LANGUAGE"): - synopsis = tr.translate( - synopsis, lang_tgt=os.environ.get("PREFERRED_LANGUAGE") - ) - return (pic if ctgry == "ANI" else data["image"]["large"]), synopsis - elif req == "char": - charlist = [] - for char in data["characters"]["edges"]: - charlist.append(f"• `{char['node']['name']['full']}` ({char['role']})") - chrctrs = ("\n").join(charlist) - charls = f"{chrctrs}" if len(charlist) != 0 else "" - return pic, charls, data["characters"]["pageInfo"] - else: - prqlsql = data.get("relations").get("edges") - ps = "" - for i in prqlsql: - ps += ( - f'• {i["node"]["title"]["romaji"]} ' - + f'({i["node"]["type"]}) `{i["relationType"]}`\n' - ) - return pic, ps - - -async def get_anime(vars_, auth: bool = False, user: int = None, cid: int = None): - result = await return_json_senpai(ANIME_QUERY, vars_, auth=auth, user=user) - - error = result.get("errors") - if error: - error_sts = error[0].get("message") - return [f"[{error_sts}]"] - - data = result["data"]["Media"] - - # Data of all fields in returned json - # pylint: disable=possibly-unused-variable - idm = data.get("id") - idmal = data.get("idMal") - romaji = data["title"]["romaji"] - english = data["title"]["english"] - native = data["title"]["native"] - formats = data.get("format") - status = data.get("status") - episodes = data.get("episodes") - duration = data.get("duration") - country = data.get("countryOfOrigin") - c_flag = cflag(country) - source = data.get("source") - prqlsql = data.get("relations").get("edges") - adult = data.get("isAdult") - url = data.get("siteUrl") - trailer_link = "N/A" - gnrs = ", ".join(data["genres"]) - score = data["averageScore"] - bl, cs = await uidata(cid) - text = await get_ui_text(cs) - psrc, ptype = text[0], text[1] - avscd = f"\n{bl}**{text[2]}:** `{score}%` 🌟" if score is not None else "" - tags = [] - for i in data["tags"]: - tags.append(i["name"]) - tags_ = f"\n{bl}**{text[8]}:** `{', '.join(tags[:5])}`" if tags != [] else "" - bot = BOT_USERNAME.replace("@", "") - gnrs_ = "" - if len(gnrs) != 0: - gnrs_ = f"\n{bl}**{text[7]}:** `{gnrs}`" - isfav = data.get("isFavourite") - fav = ", in Favourites" if isfav is True else "" - user_data = "" - in_ls = False - in_ls_id = "" - if auth is True: - in_list = data.get("mediaListEntry") - if in_list is not None: - in_ls = True - in_ls_id = in_list["id"] - in_ls_stts = in_list["status"] - in_ls_score = ( - f" and scored {in_list['score']}" if in_list["score"] != 0 else "" - ) - user_data = f"\n{bl}**{text[4]}:** `{in_ls_stts}{fav}{in_ls_score}`" - if data["title"]["english"] is not None: - name = f"""[{c_flag}]**{romaji}** | {native}""" - else: - name = f"""[{c_flag}]**{romaji}** | {native}""" - prql, prql_id, sql, sql_id = "", "None", "", "None" - for i in prqlsql: - if i["relationType"] == "PREQUEL" and i["node"]["type"] == "ANIME": - pname = ( - i["node"]["title"]["english"] - if i["node"]["title"]["english"] is not None - else i["node"]["title"]["romaji"] - ) - prql += f"**{text[10]}:** `{pname}`\n" - prql_id = i["node"]["id"] - break - for i in prqlsql: - if i["relationType"] == "SEQUEL" and i["node"]["type"] == "ANIME": - sname = ( - i["node"]["title"]["english"] - if i["node"]["title"]["english"] is not None - else i["node"]["title"]["romaji"] - ) - sql += f"**{text[9]}:** `{sname}`\n" - sql_id = i["node"]["id"] - break - additional = f"{prql}{sql}" - surl = f"https://t.me/{bot}/?astart=des_ANI_{idm}_desc" - dura = f"\n{bl}**{text[3]}:** `{duration} min/ep`" if duration is not None else "" - air_on = None - if data["nextAiringEpisode"]: - nextAir = data["nextAiringEpisode"]["timeUntilAiring"] - air_on = make_it_rw(nextAir * 1000) - eps = data["nextAiringEpisode"]["episode"] - th = pos_no(str(eps)) - air_on += f" | {eps}{th} eps" - if air_on is None: - eps_ = f"` | `{episodes} eps" if episodes is not None else "" - status_air = f"{bl}**{text[6]}:** `{status}{eps_}`" - else: - status_air = f"{bl}**{text[6]}:** `{status}`\n{bl}**{text[11]}:** `{air_on}`" - if data["trailer"] and data["trailer"]["site"] == "youtube": - trailer_link = f"<a href='https://youtu.be/{data['trailer']['id']}'>Trailer</a>" - title_img = f"https://img.anili.st/media/{idm}" - try: - finals_ = ANIME_TEMPLATE.format(**locals()) - except KeyError as kys: - return [f"{kys}"] - return ( - title_img, - finals_, - [idm, in_ls, in_ls_id, isfav, str(adult)], - prql_id, - sql_id, - ) - - -async def get_anilist(qdb, page, auth: bool = False, user: int = None, cid: int = None): - vars_ = {"search": ANIME_DB[qdb], "page": page} - result = await return_json_senpai(PAGE_QUERY, vars_, auth=auth, user=user) - - if len(result["data"]["Page"]["media"]) == 0: - return [f"No results Found"] - - data = result["data"]["Page"]["media"][0] - # Data of all fields in returned json - # pylint: disable=possibly-unused-variable - idm = data.get("id") - bot = BOT_USERNAME.replace("@", "") - idmal = data.get("idMal") - romaji = data["title"]["romaji"] - english = data["title"]["english"] - native = data["title"]["native"] - formats = data.get("format") - status = data.get("status") - episodes = data.get("episodes") - duration = data.get("duration") - country = data.get("countryOfOrigin") - c_flag = cflag(country) - source = data.get("source") - prqlsql = data.get("relations").get("edges") - adult = data.get("isAdult") - trailer_link = "N/A" - isfav = data.get("isFavourite") - gnrs = ", ".join(data["genres"]) - gnrs_ = "" - bl, cs = await uidata(cid) - text = await get_ui_text(cs) - psrc, ptype = text[0], text[1] - if len(gnrs) != 0: - gnrs_ = f"\n{bl}**{text[7]}:** `{gnrs}`" - fav = ", in Favourites" if isfav is True else "" - score = data["averageScore"] - avscd = f"\n{bl}**{text[2]}:** `{score}%` 🌟" if score is not None else "" - tags = [] - for i in data["tags"]: - tags.append(i["name"]) - tags_ = f"\n{bl}**{text[8]}:** `{', '.join(tags[:5])}`" if tags != [] else "" - in_ls = False - in_ls_id = "" - user_data = "" - if auth is True: - in_list = data.get("mediaListEntry") - if in_list is not None: - in_ls = True - in_ls_id = in_list["id"] - in_ls_stts = in_list["status"] - in_ls_score = ( - f" and scored {in_list['score']}" if in_list["score"] != 0 else "" - ) - user_data = f"\n{bl}**{text[4]}:** `{in_ls_stts}{fav}{in_ls_score}`" - if data["title"]["english"] is not None: - name = f"[{c_flag}]**{english}** (`{native}`)" - else: - name = f"[{c_flag}]**{romaji}** (`{native}`)" - prql, sql = "", "" - for i in prqlsql: - if i["relationType"] == "PREQUEL" and i["node"]["type"] == "ANIME": - pname = ( - i["node"]["title"]["english"] - if i["node"]["title"]["english"] is not None - else i["node"]["title"]["romaji"] - ) - prql += f"**{text[10]}:** `{pname}`\n" - break - for i in prqlsql: - if i["relationType"] == "SEQUEL" and i["node"]["type"] == "ANIME": - sname = ( - i["node"]["title"]["english"] - if i["node"]["title"]["english"] is not None - else i["node"]["title"]["romaji"] - ) - sql += f"**{text[9]}:** `{sname}`\n" - break - additional = f"{prql}{sql}" - additional.replace("-", "") - dura = f"\n{bl}**{text[3]}:** `{duration} min/ep`" if duration is not None else "" - air_on = None - if data["nextAiringEpisode"]: - nextAir = data["nextAiringEpisode"]["timeUntilAiring"] - air_on = make_it_rw(nextAir * 1000) - eps = data["nextAiringEpisode"]["episode"] - th = pos_no(str(eps)) - air_on += f" | {eps}{th} eps" - if air_on is None: - eps_ = f"` | `{episodes} eps" if episodes is not None else "" - status_air = f"{bl}**{text[6]}:** `{status}{eps_}`" - else: - status_air = f"{bl}**{text[6]}:** `{status}`\n{bl}**{text[11]}:** `{air_on}`" - if data["trailer"] and data["trailer"]["site"] == "youtube": - trailer_link = f"<a href='https://youtu.be/{data['trailer']['id']}'>Trailer</a>" - url = data.get("siteUrl") - title_img = f"https://img.anili.st/media/{idm}" - surl = f"https://t.me/{bot}/?astart=des_ANI_{idm}_desc" - hasNextPage = result["data"]["Page"]["pageInfo"]["hasNextPage"] - try: - finals_ = ANIME_TEMPLATE.format(**locals()) - except KeyError as kys: - return [f"{kys}"] - return title_img, [finals_, hasNextPage], [idm, in_ls, in_ls_id, isfav, str(adult)] - - -async def get_character(query, page, auth: bool = False, user: int = None): - var = {"search": CHAR_DB[query], "page": int(page)} - result = await return_json_senpai(CHARACTER_QUERY, var, auth=auth, user=user) - if len(result["data"]["Page"]["characters"]) == 0: - return [f"No results Found"] - data = result["data"]["Page"]["characters"][0] - # Character Data - id_ = data["id"] - name = data["name"]["full"] - native = data["name"]["native"] - img = data["image"]["large"] - site_url = data["siteUrl"] - isfav = data.get("isFavourite") - va = [] - for i in data["media"]["edges"]: - for ii in i["voiceActors"]: - if f"[{ii['name']['full']}]({ii['siteUrl']})" not in va: - va.append(f"[{ii['name']['full']}]({ii['siteUrl']})") - lva = None - if len(va) > 1: - lva = va.pop() - sva = ( - f"\n**Voice Actors:** {', '.join(va)}" - + f"{' and '+lva if lva is not None else ''}\n" - if va != [] - else "" - ) - cap_text = f""" -__{native}__ -(`{name}`) -**ID:** {id_} -{sva} -<a href='{site_url}'>Visit Website</a>""" - hasNextPage = result["data"]["Page"]["pageInfo"]["hasNextPage"] - return img, [cap_text, hasNextPage], [id_, isfav] - - -async def browse_(qry: str): - s, y = season_() - sort = "POPULARITY_DESC" - if qry == "upcoming": - s, y = season_(True) - if qry == "trending": - sort = "TRENDING_DESC" - vars_ = {"s": s, "y": y, "sort": sort} - result = await return_json_senpai(BROWSE_QUERY, vars_) - data = result["data"]["Page"]["media"] - ls = [] - for i in data: - if i["format"] in ["TV", "MOVIE", "ONA"]: - ls.append("• `" + i["title"]["romaji"] + "`") - out = f"{qry.capitalize()} animes in {s} {y}:\n\n" - return out + "\n".join(ls[:20]) - - -async def get_manga(qdb, page, auth: bool = False, user: int = None, cid: int = None): - vars_ = {"search": MANGA_DB[qdb], "asHtml": True, "page": page} - result = await return_json_senpai(MANGA_QUERY, vars_, auth=auth, user=user) - if len(result["data"]["Page"]["media"]) == 0: - return [f"No results Found"] - data = result["data"]["Page"]["media"][0] - - # Data of all fields in returned json - # pylint: disable=possibly-unused-variable - idm = data.get("id") - romaji = data["title"]["romaji"] - english = data["title"]["english"] - native = data["title"]["native"] - status = data.get("status") - synopsis = data.get("description") - description = synopsis[:500] - description_s = "" - if len(synopsis) > 500: - description += f"..." - description_s = ( - f"[Click for more info](https://t.me/{BOT_USERNAME}" - + f"/?astart=des_ANI_{idm}_desc)" - ) - volumes = data.get("volumes") - chapters = data.get("chapters") - score = data.get("averageScore") - url = data.get("siteUrl") - format_ = data.get("format") - country = data.get("countryOfOrigin") - source = data.get("source") - c_flag = cflag(country) - isfav = data.get("isFavourite") - adult = data.get("isAdult") - fav = ", in Favourites" if isfav is True else "" - in_ls = False - in_ls_id = "" - bl, cs = await uidata(cid) - text = await get_ui_text(cs) - user_data = "" - if auth is True: - in_list = data.get("mediaListEntry") - if in_list is not None: - in_ls = True - in_ls_id = in_list["id"] - in_ls_stts = in_list["status"] - in_ls_score = ( - f" and scored {in_list['score']}" if in_list["score"] != 0 else "" - ) - user_data = f"{bl}**{text[4]}:** `{in_ls_stts}{fav}{in_ls_score}`\n" - name = f"""[{c_flag}]**{romaji}** - __{english}__ - {native}""" - if english is None: - name = f"""[{c_flag}]**{romaji}** - {native}""" - finals_ = f"{name}\n\n" - finals_ += f"{bl}**ID:** `{idm}`\n" - finals_ += f"{bl}**{text[6]}:** `{status}`\n" - finals_ += f"{bl}**{text[13]}:** `{volumes}`\n" - finals_ += f"{bl}**{text[14]}:** `{chapters}`\n" - finals_ += f"{bl}**{text[2]}:** `{score}`\n" - finals_ += f"{bl}**{text[1]}:** `{format_}`\n" - finals_ += f"{bl}**{text[0]}:** `{source}`\n" - finals_ += user_data - if os.environ.get("PREFERRED_LANGUAGE"): - description = tr.translate( - description, lang_tgt=os.environ.get("PREFERRED_LANGUAGE") - ) - findesc = "" if description == "" else f"`{description}`" - finals_ += f"\n**{text[12]}**: {findesc}\n\n{description_s}" - pic = f"https://img.anili.st/media/{idm}" - return ( - pic, - [finals_, result["data"]["Page"]["pageInfo"]["hasNextPage"], url], - [idm, in_ls, in_ls_id, isfav, str(adult)], - ) - - -async def get_airing(qry, ind: int, auth: bool = False, user: int = None): - vars_ = {"search": AIRING_DB[qry], "page": int(ind)} - result = await return_json_senpai(AIR_QUERY, vars_, auth=auth, user=user) - error = result.get("errors") - if error: - error_sts = error[0].get("message") - return [f"{error_sts}"] - try: - data = result["data"]["Page"]["media"][0] - except IndexError: - return ["No results Found"] - # Airing Details - mid = data.get("id") - romaji = data["title"]["romaji"] - english = data["title"]["english"] - status = data.get("status") - country = data.get("countryOfOrigin") - c_flag = cflag(country) - coverImg = f"https://img.anili.st/media/{mid}" - isfav = data.get("isFavourite") - adult = data.get("isAdult") - in_ls = False - in_ls_id = "" - user_data = "" - if auth is True: - in_list = data.get("mediaListEntry") - if in_list is not None: - in_ls = True - in_ls_id = in_list["id"] - in_ls_stts = in_list["status"] - user_data = f"**USER DATA:** `{in_ls_stts}`\n" - air_on = None - if data["nextAiringEpisode"]: - nextAir = data["nextAiringEpisode"]["timeUntilAiring"] - episode = data["nextAiringEpisode"]["episode"] - th = pos_no(episode) - air_on = make_it_rw(nextAir * 1000) - title_ = english or romaji - out = f"[{c_flag}] **{title_}**" - out += f"\n\n**ID:** `{mid}`" - out += f"\n**Status:** `{status}`\n" - out += user_data - if air_on: - out += f"Airing Episode `{episode}{th}` in `{air_on}`" - site = data["siteUrl"] - return ( - [coverImg, out], - [site, result["data"]["Page"]["pageInfo"]["hasNextPage"]], - [mid, in_ls, in_ls_id, isfav, str(adult)], - ) - - -async def toggle_favourites(id_: int, media: str, user: int): - vars_ = {"id": int(id_)} - query = ( - ANIME_MUTATION - if media == "ANIME" or media == "AIRING" - else CHAR_MUTATION - if media == "CHARACTER" - else MANGA_MUTATION - if media == "MANGA" - else STUDIO_MUTATION - ) - k = await return_json_senpai(query=query, vars_=vars_, auth=True, user=int(user)) - try: - kek = k["data"]["ToggleFavourite"] - return "ok" - except KeyError: - return "failed" - - -async def get_user(vars_, req, user, display_user=None): - query = USER_QRY if "user" in req else VIEWER_QRY - k = await return_json_senpai( - query=query, vars_=vars_, auth=False if "user" in req else True, user=int(user) - ) - error = k.get("errors") - if error: - error_sts = error[0].get("message") - return [f"{error_sts}"] - - data = k["data"]["User" if "user" in req else "Viewer"] - anime = data["statistics"]["anime"] - manga = data["statistics"]["manga"] - stats = f""" -**Anime Stats**: - -Total Anime Watched: `{anime['count']}` -Total Episode Watched: `{anime['episodesWatched']}` -Total Time Spent: `{anime['minutesWatched']}` -Average Score: `{anime['meanScore']}` - -**Manga Stats**: - -Total Manga Read: `{manga['count']}` -Total Chapters Read: `{manga['chaptersRead']}` -Total Volumes Read: `{manga['volumesRead']}` -Average Score: `{manga['meanScore']}` -""" - btn = [] - if not "user" in req: - btn.append( - [ - InlineKeyboardButton( - "Favourites", - callback_data=f"myfavs_{data['id']}_yes_{display_user}", - ), - InlineKeyboardButton( - "Activity", callback_data=f"myacc_{data['id']}_{display_user}" - ), - ] - ) - btn.append([InlineKeyboardButton("Profile", url=str(data["siteUrl"]))]) - return [ - f'https://img.anili.st/user/{data["id"]}?a={time.time()}', - stats, - InlineKeyboardMarkup(btn), - ] - - -async def update_anilist(id_, req, user, eid: int = None, status: str = None): - vars_ = {"id": int(id_), "status": status} - if req == "lsus": - vars_ = {"id": int(eid), "status": status} - if req == "dlt": - vars_ = {"id": int(eid)} - k = await return_json_senpai( - query=( - ANILIST_MUTATION - if req == "lsas" - else ANILIST_MUTATION_UP - if req == "lsus" - else ANILIST_MUTATION_DEL - ), - vars_=vars_, - auth=True, - user=int(user), - ) - try: - ( - k["data"]["SaveMediaListEntry"] - if req == "lsas" - else k["data"]["UpdateMediaListEntries"] - if req == "lsus" - else k["data"]["DeleteMediaListEntry"] - ) - return "ok" - except KeyError: - return "failed" - - -async def check_if_adult(id_): - vars_ = {"id": int(id_)} - k = await return_json_senpai(query=ISADULT, vars_=vars_, auth=False) - if str(k["data"]["Media"]["isAdult"]) == "True": - return "True" - else: - return "False" - - -#### END #### - -#### Jikanpy part #### - - -async def get_scheduled(x: int = 9): - base_url = "https://api.jikan.moe/v4/schedules/" - day = str(day_(x if x != 9 else datetime.now().weekday())).lower() - out = f"Scheduled animes for {day.capitalize()}\n\n" - data = requests.get(base_url + day).json() - sched_ls = data["data"] - for i in sched_ls: - try: - title = i["titles"][0]["title"] - except IndexError: - title = i["title"] - out += f"• `{title}`\n" - return out, x if x != 9 else datetime.now().weekday() - - -#### END #### - -#### chiaki part #### - - -def get_wols(x: str): - data = requests.get(f"https://chiaki.vercel.app/search2?query={x}").json() - ls = [] - for i in data: - sls = [data[i], i] - ls.append(sls) - return ls - - -def get_wo(x: int, page: int): - data = requests.get(f"https://chiaki.vercel.app/get2?group_id={x}").json() - msg = "Watch order for the given query is:\n\n" - out = [] - for i in data: - out.append(f"{i['index']}. `{i['name']}`\n") - total = len(out) - for _ in range(50 * page): - out.pop(0) - out_ = "".join(out[:50]) - return msg + out_, total - - -#### END #### - -##### Anime Fillers Part ##### - - -def search_filler(query): - html = requests.get("https://www.animefillerlist.com/shows").text - soup = BeautifulSoup(html, "html.parser") - div = soup.findAll("div", attrs={"class": "Group"}) - index = {} - for i in div: - li = i.findAll("li") - for jk in li: - yum = jk.a["href"].split("/")[-1] - cum = jk.text - index[cum] = yum - ret = {} - keys = list(index.keys()) - for i in range(len(keys)): - if query.lower() in keys[i].lower(): - ret[keys[i]] = index[keys[i]] - return ret - - -def parse_filler(filler_id): - url = "https://www.animefillerlist.com/shows/" + filler_id - html = requests.get(url).text - soup = BeautifulSoup(html, "html.parser") - div = soup.find("div", attrs={"id": "Condensed"}) - all_ep = div.find_all("span", attrs={"class": "Episodes"}) - if len(all_ep) == 1: - ttl_ep = all_ep[0].findAll("a") - total_ep = [] - mix_ep = None - filler_ep = None - ac_ep = None - for tol in ttl_ep: - total_ep.append(tol.text) - dict_ = { - "filler_id": filler_id, - "total_ep": ", ".join(total_ep), - "mixed_ep": mix_ep, - "filler_ep": filler_ep, - "ac_ep": ac_ep, - } - return dict_ - if len(all_ep) == 2: - ttl_ep = all_ep[0].findAll("a") - fl_ep = all_ep[1].findAll("a") - total_ep = [] - mix_ep = None - ac_ep = None - filler_ep = [] - for tol in ttl_ep: - total_ep.append(tol.text) - for fol in fl_ep: - filler_ep.append(fol.text) - dict_ = { - "filler_id": filler_id, - "total_ep": ", ".join(total_ep), - "mixed_ep": mix_ep, - "filler_ep": ", ".join(filler_ep), - "ac_ep": ac_ep, - } - return dict_ - if len(all_ep) == 3: - ttl_ep = all_ep[0].findAll("a") - mxl_ep = all_ep[1].findAll("a") - fl_ep = all_ep[2].findAll("a") - total_ep = [] - mix_ep = [] - filler_ep = [] - ac_ep = None - for tol in ttl_ep: - total_ep.append(tol.text) - for fol in fl_ep: - filler_ep.append(fol.text) - for mol in mxl_ep: - mix_ep.append(mol.text) - dict_ = { - "filler_id": filler_id, - "total_ep": ", ".join(total_ep), - "mixed_ep": ", ".join(mix_ep), - "filler_ep": ", ".join(filler_ep), - "ac_ep": ac_ep, - } - return dict_ - if len(all_ep) == 4: - ttl_ep = all_ep[0].findAll("a") - mxl_ep = all_ep[1].findAll("a") - fl_ep = all_ep[2].findAll("a") - al_ep = all_ep[3].findAll("a") - total_ep = [] - mix_ep = [] - filler_ep = [] - ac_ep = [] - for tol in ttl_ep: - total_ep.append(tol.text) - for fol in fl_ep: - filler_ep.append(fol.text) - for mol in mxl_ep: - mix_ep.append(mol.text) - for aol in al_ep: - ac_ep.append(aol.text) - dict_ = { - "filler_id": filler_id, - "total_ep": ", ".join(total_ep), - "mixed_ep": ", ".join(mix_ep), - "filler_ep": ", ".join(filler_ep), - "ac_ep": ", ".join(ac_ep), - } - return dict_ - - -@app.on_message( - filters.command(["anime", f"anime{BOT_USERNAME}"], prefixes=PREFIX_HANDLER) -) -@control_user -async def anime_cmd(client: Client, message: Message, mdata: dict): - """Search Anime Info""" - text = mdata["text"].split(" ", 1) - gid = mdata["chat"]["id"] - try: - user = mdata["from_user"]["id"] - auser = mdata["from_user"]["id"] - except KeyError: - user = mdata["sender_chat"]["id"] - ufc = await gcc(user) - if ufc is not None: - auser = ufc - else: - auser = user - find_gc = await DC.find_one({"_id": gid}) - if find_gc is not None and "anime" in find_gc["cmd_list"].split(): - return - if len(text) == 1: - k = await message.reply_text( - """Please give a query to search about - -example: /anime Sword Art Online""" - ) - await asyncio.sleep(5) - return await k.delete() - query = text[1] - auth = False - vars_ = {"search": query} - if query.isdigit(): - vars_ = {"id": int(query)} - if await AUTH_USERS.find_one({"id": auser}): - auth = True - result = await get_anime( - vars_, user=auser, auth=auth, cid=gid if gid != user else None - ) - if len(result) != 1: - title_img, finals_ = result[0], result[1] - else: - k = await message.reply_text(result[0]) - await asyncio.sleep(5) - return await k.delete() - buttons = get_btns("ANIME", result=result, user=user, auth=auth) - if await SFW_GRPS.find_one({"id": gid}) and result[2].pop() == "True": - await client.send_photo( - gid, - no_pic[random.randint(0, 4)], - caption="This anime is marked 18+ and not allowed in this group", - ) - return - try: - await client.send_photo(gid, title_img, caption=finals_, reply_markup=buttons) - except (WebpageMediaEmpty, WebpageCurlFailed): - await clog("Mikobot", title_img, "LINK", msg=message) - await client.send_photo(gid, failed_pic, caption=finals_, reply_markup=buttons) - - -@app.on_message( - filters.command(["manga", f"manga{BOT_USERNAME}"], prefixes=PREFIX_HANDLER) -) -@control_user -async def manga_cmd(client: Client, message: Message, mdata: dict): - """Search Manga Info""" - text = mdata["text"].split(" ", 1) - gid = mdata["chat"]["id"] - try: - user = mdata["from_user"]["id"] - auser = mdata["from_user"]["id"] - except KeyError: - user = mdata["sender_chat"]["id"] - ufc = await gcc(user) - if ufc is not None: - auser = ufc - else: - auser = user - find_gc = await DC.find_one({"_id": gid}) - if find_gc is not None and "manga" in find_gc["cmd_list"].split(): - return - if len(text) == 1: - k = await message.reply_text( - """Please give a query to search about - -example: /manga Sword Art Online""" - ) - await asyncio.sleep(5) - return await k.delete() - query = text[1] - qdb = rand_key() - MANGA_DB[qdb] = query - auth = False - if await AUTH_USERS.find_one({"id": auser}): - auth = True - result = await get_manga( - qdb, 1, auth=auth, user=auser, cid=gid if gid != user else None - ) - if len(result) == 1: - k = await message.reply_text(result[0]) - await asyncio.sleep(5) - return await k.delete() - pic, finals_ = result[0], result[1][0] - buttons = get_btns( - "MANGA", lsqry=qdb, lspage=1, user=user, result=result, auth=auth - ) - if await SFW_GRPS.find_one({"id": gid}) and result[2].pop() == "True": - buttons = get_btns( - "MANGA", - lsqry=qdb, - lspage=1, - user=user, - result=result, - auth=auth, - sfw="True", - ) - await client.send_photo( - gid, - no_pic[random.randint(0, 4)], - caption="This manga is marked 18+ and not allowed in this group", - reply_markup=buttons, - ) - return - try: - await client.send_photo(gid, pic, caption=finals_, reply_markup=buttons) - except (WebpageMediaEmpty, WebpageCurlFailed): - await clog("Mikobot", pic, "LINK", msg=message) - await client.send_photo(gid, failed_pic, caption=finals_, reply_markup=buttons) - - -@app.on_message( - filters.command(["character", f"character{BOT_USERNAME}"], prefixes=PREFIX_HANDLER) -) -@control_user -async def character_cmd(client: Client, message: Message, mdata: dict): - """Get Info about a Character""" - text = mdata["text"].split(" ", 1) - gid = mdata["chat"]["id"] - try: - user = mdata["from_user"]["id"] - auser = mdata["from_user"]["id"] - except KeyError: - user = mdata["sender_chat"]["id"] - ufc = await gcc(user) - if ufc is not None: - auser = ufc - else: - auser = user - find_gc = await DC.find_one({"_id": gid}) - if find_gc is not None and "character" in find_gc["cmd_list"].split(): - return - if len(text) == 1: - k = await message.reply_text( - "Please give a query to search about\nexample: /character Luffy" - ) - await asyncio.sleep(5) - return await k.delete() - query = text[1] - qdb = rand_key() - CHAR_DB[qdb] = query - auth = False - if await AUTH_USERS.find_one({"id": auser}): - auth = True - result = await get_character(qdb, 1, auth=auth, user=auser) - if len(result) == 1: - k = await message.reply_text(result[0]) - await asyncio.sleep(5) - return await k.delete() - img = result[0] - cap_text = result[1][0] - buttons = get_btns( - "CHARACTER", user=user, lsqry=qdb, lspage=1, result=result, auth=auth - ) - try: - await client.send_photo(gid, img, caption=cap_text, reply_markup=buttons) - except (WebpageMediaEmpty, WebpageCurlFailed): - await clog("Mikobot", img, "LINK", msg=message) - await client.send_photo(gid, failed_pic, caption=cap_text, reply_markup=buttons) - - -@app.on_message( - filters.command(["anilist", f"anilist{BOT_USERNAME}"], prefixes=PREFIX_HANDLER) -) -@control_user -async def anilist_cmd(client: Client, message: Message, mdata: dict): - text = mdata["text"].split(" ", 1) - gid = mdata["chat"]["id"] - try: - user = mdata["from_user"]["id"] - auser = mdata["from_user"]["id"] - except KeyError: - user = mdata["sender_chat"]["id"] - ufc = await gcc(user) - if ufc is not None: - auser = ufc - else: - auser = user - find_gc = await DC.find_one({"_id": gid}) - if find_gc is not None and "anilist" in find_gc["cmd_list"].split(): - return - if len(text) == 1: - k = await message.reply_text( - "Please give a query to search about\nexample: /anilist Sword Art Online" - ) - await asyncio.sleep(5) - return await k.delete() - query = text[1] - qdb = rand_key() - ANIME_DB[qdb] = query - auth = False - if await AUTH_USERS.find_one({"id": auser}): - auth = True - result = await get_anilist( - qdb, 1, auth=auth, user=auser, cid=gid if gid != user else None - ) - if len(result) == 1: - k = await message.reply_text(result[0]) - await asyncio.sleep(5) - return await k.delete() - pic, msg = result[0], result[1][0] - buttons = get_btns( - "ANIME", lsqry=qdb, lspage=1, result=result, user=user, auth=auth - ) - if await SFW_GRPS.find_one({"id": gid}) and result[2].pop() == "True": - buttons = get_btns( - "ANIME", - lsqry=qdb, - lspage=1, - result=result, - user=user, - auth=auth, - sfw="True", - ) - await client.send_photo( - gid, - no_pic[random.randint(0, 4)], - caption="This anime is marked 18+ and not allowed in this group", - reply_markup=buttons, - ) - return - try: - await client.send_photo(gid, pic, caption=msg, reply_markup=buttons) - except (WebpageMediaEmpty, WebpageCurlFailed): - await clog("Mikobot", pic, "LINK", msg=message) - await client.send_photo(gid, failed_pic, caption=msg, reply_markup=buttons) - - -@app.on_message(filters.command(["top", f"top{BOT_USERNAME}"], prefixes=PREFIX_HANDLER)) -@control_user -async def top_tags_cmd(client: Client, message: Message, mdata: dict): - query = mdata["text"].split(" ", 1) - gid = mdata["chat"]["id"] - find_gc = await DC.find_one({"_id": gid}) - if find_gc is not None and "top" in find_gc["cmd_list"].split(): - return - get_tag = "None" - if len(query) == 2: - get_tag = query[1] - try: - user = mdata["from_user"]["id"] - except KeyError: - user = mdata["sender_chat"]["id"] - result = await get_top_animes(get_tag, 1, user) - if len(result) == 1: - k = await message.reply_text(result[0]) - await asyncio.sleep(5) - return await k.delete() - if await SFW_GRPS.find_one({"id": gid}) and str(result[0][1]) == "True": - return await message.reply_text("No nsfw stuff allowed in this group!!!") - msg, buttons = result - await client.send_message( - gid, msg[0], reply_markup=buttons if buttons != "" else None - ) - - -@app.on_message( - filters.command(["studio", f"studio{BOT_USERNAME}"], prefixes=PREFIX_HANDLER) -) -@control_user -async def studio_cmd(client: Client, message: Message, mdata: dict): - text = mdata["text"].split(" ", 1) - gid = mdata["chat"]["id"] - find_gc = await DC.find_one({"_id": gid}) - if find_gc is not None and "studio" in find_gc["cmd_list"].split(): - return - if len(text) == 1: - x = await message.reply_text( - "Please give a query to search about!!!\nExample: /studio ufotable" - ) - await asyncio.sleep(5) - await x.delete() - return - query = text[1] - qdb = rand_key() - STUDIO_DB[qdb] = query - auth = False - try: - user = mdata["from_user"]["id"] - auser = mdata["from_user"]["id"] - except KeyError: - user = mdata["sender_chat"]["id"] - ufc = await gcc(user) - if ufc is not None: - auser = ufc - else: - auser = user - if await AUTH_USERS.find_one({"id": auser}): - auth = True - result = await get_studios(qdb, 1, user=auser, duser=user, auth=auth) - if len(result) == 1: - x = await message.reply_text("No results found!!!") - await asyncio.sleep(5) - return await x.delete() - msg, buttons = result[0], result[1] - await client.send_message(gid, msg, reply_markup=buttons if buttons != "" else None) - - -@app.on_message( - filters.command(["airing", f"airing{BOT_USERNAME}"], prefixes=PREFIX_HANDLER) -) -@control_user -async def airing_cmd(client: Client, message: Message, mdata: dict): - """Get Airing Detail of Anime""" - text = mdata["text"].split(" ", 1) - gid = mdata["chat"]["id"] - find_gc = await DC.find_one({"_id": gid}) - if find_gc is not None and "airing" in find_gc["cmd_list"].split(): - return - if len(text) == 1: - k = await message.reply_text( - """Please give a query to search about - -example: /airing Sword Art Online""" - ) - await asyncio.sleep(5) - return await k.delete() - query = text[1] - qdb = rand_key() - AIRING_DB[qdb] = query - auth = False - try: - user = mdata["from_user"]["id"] - auser = mdata["from_user"]["id"] - except KeyError: - user = mdata["sender_chat"]["id"] - ufc = await gcc(user) - if ufc is not None: - auser = ufc - else: - auser = user - if await AUTH_USERS.find_one({"id": auser}): - auth = True - result = await get_airing(qdb, 1, auth=auth, user=auser) - if len(result) == 1: - k = await message.reply_text(result[0]) - await asyncio.sleep(5) - return await k.delete() - coverImg, out = result[0] - btn = get_btns("AIRING", user=user, result=result, auth=auth, lsqry=qdb, lspage=1) - if await SFW_GRPS.find_one({"id": gid}) and result[2].pop() == "True": - btn = get_btns( - "AIRING", - user=user, - result=result, - auth=auth, - lsqry=qdb, - lspage=1, - sfw="True", - ) - await client.send_photo( - gid, - no_pic[random.randint(0, 4)], - caption="This anime is marked 18+ and not allowed in this group", - reply_markup=btn, - ) - return - try: - await client.send_photo(gid, coverImg, caption=out, reply_markup=btn) - except (WebpageMediaEmpty, WebpageCurlFailed): - await clog("Mikobot", coverImg, "LINK", msg=message) - await client.send_photo(gid, failed_pic, caption=out, reply_markup=btn) - - -setting_text = """ -<b>This allows you to change group settings</b> - -NSFW toggle switches on filtering of 18+ marked content - -Airing notifications notifies about airing of anime in recent - -Crunchyroll updates will toggle notifications about release of animes on crunchyroll site - -Subsplease updates will toggle notifications about release of animes on subsplease site - -Click Headlines button to enable headlines. You can choose from given sources""" - - -@app.on_message( - ~filters.private - & filters.command( - ["anisettings", f"anisettings{BOT_USERNAME}"], prefixes=PREFIX_HANDLER - ) -) -@control_user -async def settings_cmd(client: Client, message: Message, mdata: dict): - cid = mdata["chat"]["id"] - try: - user = mdata["from_user"]["id"] - except KeyError: - user = mdata["sender_chat"]["id"] - type_ = mdata["chat"]["type"] - try: - status = (await app.get_chat_member(cid, user)).status - except UserNotParticipant: - status = None - if ( - user in BOT_OWNER - or status in [ADMINISTRATOR, CHAT_OWNER] - or type_ == ChatType.CHANNEL - or user == cid - ): - sfw = "NSFW: Allowed" - if await SFW_GRPS.find_one({"id": cid}): - sfw = "NSFW: Not Allowed" - notif = "Airing notifications: OFF" - if await AG.find_one({"_id": cid}): - notif = "Airing notifications: ON" - cr = "Crunchyroll Updates: OFF" - if await CG.find_one({"_id": cid}): - cr = "Crunchyroll Updates: ON" - sp = "Subsplease Updates: OFF" - if await SG.find_one({"_id": cid}): - sp = "Subsplease Updates: ON" - await message.reply_text( - text=setting_text, - reply_markup=InlineKeyboardMarkup( - [ - [ - InlineKeyboardButton( - text=sfw, callback_data=f"settogl_sfw_{cid}" - ) - ], - [ - InlineKeyboardButton( - text=notif, callback_data=f"settogl_notif_{cid}" - ) - ], - [InlineKeyboardButton(text=cr, callback_data=f"settogl_cr_{cid}")], - [InlineKeyboardButton(text=sp, callback_data=f"settogl_sp_{cid}")], - [ - InlineKeyboardButton( - text="Headlines", callback_data=f"headlines_call_{cid}" - ) - ], - [ - InlineKeyboardButton( - text="Change UI", callback_data=f"cui_call_{cid}" - ) - ], - ] - ), - ) - - -@app.on_message( - filters.command(["browse", f"browse{BOT_USERNAME}"], prefixes=PREFIX_HANDLER) -) -@control_user -async def browse_cmd(client: Client, message: Message, mdata: dict): - try: - user = mdata["from_user"]["id"] - except KeyError: - user = mdata["sender_chat"]["id"] - gid = mdata["chat"]["id"] - find_gc = await DC.find_one({"_id": gid}) - if find_gc is not None and "browse" in find_gc["cmd_list"].split(): - return - up = "Upcoming" - tr = "• Trending •" - pp = "Popular" - btns = [ - [ - InlineKeyboardButton(tr, callback_data=f"browse_{tr.lower()}_{user}"), - InlineKeyboardButton(pp, callback_data=f"browse_{pp.lower()}_{user}"), - InlineKeyboardButton(up, callback_data=f"browse_{up.lower()}_{user}"), - ] - ] - msg = await browse_("trending") - await client.send_message(gid, msg, reply_markup=InlineKeyboardMarkup(btns)) - - -@app.on_message( - filters.command( - ["gettags", f"gettags{BOT_USERNAME}", "getgenres", f"getgenres{BOT_USERNAME}"], - prefixes=PREFIX_HANDLER, - ) -) -@control_user -async def list_tags_genres_cmd(client, message: Message, mdata: dict): - gid = mdata["chat"]["id"] - text = mdata["text"] - find_gc = await DC.find_one({"_id": gid}) - if find_gc is not None and "gettags" in ( - text.split()[0] and find_gc["cmd_list"].split() - ): - return - if find_gc is not None and "getgenres" in ( - text.split()[0] and find_gc["cmd_list"].split() - ): - return - if await SFW_GRPS.find_one({"id": gid}) and "nsfw" in text: - return await message.reply_text("No nsfw allowed here!!!") - msg = ( - (await get_all_tags(text)) - if "gettags" in text.split()[0] - else (await get_all_genres()) - ) - await message.reply_text(msg) - - -@app.on_callback_query(filters.regex(pattern=r"page_(.*)")) -@check_user -async def page_btn(client: Client, cq: CallbackQuery, cdata: dict): - kek, media, query, page, auth, user = cq.data.split("_") - gid = cdata["message"]["chat"]["id"] - if media == "ANIME": - try: - ANIME_DB[query] - except KeyError: - return await cq.answer("Query Expired!!!\nCreate new one", show_alert=True) - if media == "MANGA": - try: - MANGA_DB[query] - except KeyError: - return await cq.answer("Query Expired!!!\nCreate new one", show_alert=True) - if media == "CHARACTER": - try: - CHAR_DB[query] - except KeyError: - return await cq.answer("Query Expired!!!\nCreate new one", show_alert=True) - if media == "AIRING": - try: - AIRING_DB[query] - except KeyError: - return await cq.answer("Query Expired!!!\nCreate new one", show_alert=True) - authbool = bool(1) if auth == "True" else bool(0) - if "-100" in str(user): - auser = await gcc(user) - else: - auser = user - if media in ["ANIME", "MANGA"]: - result = await (get_anilist if media == "ANIME" else get_manga)( - query, - int(page), - auth=authbool, - user=int(auser), - cid=gid if gid != user else None, - ) - else: - result = await (get_character if media == "CHARACTER" else get_airing)( - query, int(page), auth=authbool, user=int(auser) - ) - if "No results Found" in result: - await cq.answer("No more results available!!!", show_alert=True) - return - pic, msg = result[0], result[1][0] - if media == "AIRING": - pic, msg = result[0][0], result[0][1] - button = get_btns( - media, lsqry=query, lspage=int(page), result=result, user=user, auth=authbool - ) - if ( - await SFW_GRPS.find_one({"id": gid}) - and media != "CHARACTER" - and result[2].pop() == "True" - ): - button = get_btns( - media, - lsqry=query, - lspage=int(page), - result=result, - user=user, - auth=authbool, - sfw="True", - ) - await cq.edit_message_media( - InputMediaPhoto( - no_pic[random.randint(0, 4)], - caption=""" -This material is marked 18+ and not allowed in this group""", - ), - reply_markup=button, - ) - return - try: - await cq.edit_message_media( - InputMediaPhoto(pic, caption=msg), reply_markup=button - ) - except (WebpageMediaEmpty, WebpageCurlFailed): - await clog("Mikobot", pic, "LINK", msg=cq) - await cq.edit_message_media( - InputMediaPhoto(failed_pic, caption=msg), reply_markup=button - ) - await cq.answer() - - -@app.on_callback_query(filters.regex(pattern=r"pgstudio_(.*)")) -@check_user -async def studio_pg_btn(client: Client, cq: CallbackQuery, cdata: dict): - kek, page, qry, auth, user = cdata["data"].split("_") - authbool = bool(1) if auth == "True" else bool(0) - try: - STUDIO_DB[qry] - except KeyError: - return await cq.answer("Query Expired!!!\nCreate new one", show_alert=True) - if "-100" in str(user): - auser = await gcc(user) - else: - auser = user - result = await get_studios(qry, page=page, user=auser, duser=user, auth=authbool) - if len(result) == 1: - return await cq.answer("No more results available!!!", show_alert=True) - msg, buttons = result[0], result[1] - await cq.edit_message_text(msg, reply_markup=buttons) - - -@app.on_callback_query(filters.regex(pattern=r"stuani_(.*)")) -@check_user -async def studio_ani_btn(client: Client, cq: CallbackQuery, cdata: dict): - kek, page, id_, rp, qry, auth, user = cdata["data"].split("_") - authbool = bool(1) if auth == "True" else bool(0) - if "-100" in str(user): - auser = await gcc(user) - else: - auser = user - result = await get_studio_animes(id_, page, qry, rp, auser, user, authbool) - if len(result) == 1: - return await cq.answer("No results available!!!", show_alert=True) - msg, buttons = result[0], result[1] - await cq.edit_message_text(msg, reply_markup=buttons) - - -@app.on_callback_query(filters.regex(pattern=r"btn_(.*)")) -@check_user -async def anime_btn(client: Client, cq: CallbackQuery, cdata: dict): - await cq.answer() - query = cdata["data"].split("_") - idm = query[1] - user = int(query.pop()) - if "-100" in str(user): - auser = await gcc(user) - else: - auser = user - authbool = bool(1) if query[2] == "True" else bool(0) - vars_ = {"id": int(idm)} - cid = cdata["message"]["chat"]["id"] - result = await get_anime( - vars_, auth=authbool, user=auser, cid=cid if cid != user else None - ) - pic, msg = result[0], result[1] - btns = get_btns("ANIME", result=result, user=user, auth=authbool) - try: - await cq.edit_message_media( - InputMediaPhoto(pic, caption=msg), reply_markup=btns - ) - except (WebpageMediaEmpty, WebpageCurlFailed): - await clog("Mikobot", pic, "LINK", msg=cq) - await cq.edit_message_media( - InputMediaPhoto(failed_pic, caption=msg), reply_markup=btns - ) - - -@app.on_callback_query(filters.regex(pattern=r"topanimu_(.*)")) -@check_user -async def top_tags_btn(client: Client, cq: CallbackQuery, cdata: dict): - await cq.answer() - kek, gnr, page, user = cdata["data"].split("_") - result = await get_top_animes(gnr, page=page, user=user) - msg, buttons = result[0][0], result[1] - await cq.edit_message_text(msg, reply_markup=buttons) - - -@app.on_callback_query(filters.regex(pattern=r"settogl_(.*)")) -async def nsfw_toggle_btn(client: Client, cq: CallbackQuery): - cus = cq.from_user.id - gid = cq.data.split("_").pop() - try: - k = (await client.get_chat_member(gid, cus)).status - except UserNotParticipant: - await cq.answer() - return - if cus not in BOT_OWNER and k == MEMBER: - await cq.answer( - "You don't have enough permissions to change this!!!", show_alert=True - ) - return - query = cq.data.split("_") - if await SFW_GRPS.find_one({"id": int(query[2])}): - sfw = "NSFW: Not Allowed" - else: - sfw = "NSFW: Allowed" - if await AG.find_one({"_id": int(query[2])}): - notif = "Airing notifications: ON" - else: - notif = "Airing notifications: OFF" - if await CG.find_one({"_id": int(query[2])}): - cr = "Crunchyroll Updates: ON" - else: - cr = "Crunchyroll Updates: OFF" - if await SG.find_one({"_id": int(query[2])}): - sp = "Subsplease Updates: ON" - else: - sp = "Subsplease Updates: OFF" - if query[1] == "sfw": - if await SFW_GRPS.find_one({"id": int(query[2])}): - await SFW_GRPS.find_one_and_delete({"id": int(query[2])}) - sfw = "NSFW: Allowed" - else: - await SFW_GRPS.insert_one({"id": int(query[2])}) - sfw = "NSFW: Not Allowed" - if query[1] == "notif": - if await AG.find_one({"_id": int(query[2])}): - await AG.find_one_and_delete({"_id": int(query[2])}) - notif = "Airing notifications: OFF" - else: - await AG.insert_one({"_id": int(query[2])}) - notif = "Airing notifications: ON" - if query[1] == "cr": - if await CG.find_one({"_id": int(query[2])}): - await CG.find_one_and_delete({"_id": int(query[2])}) - cr = "Crunchyroll Updates: OFF" - else: - await CG.insert_one({"_id": int(query[2])}) - cr = "Crunchyroll Updates: ON" - if query[1] == "sp": - if await SG.find_one({"_id": int(query[2])}): - await SG.find_one_and_delete({"_id": int(query[2])}) - sp = "Subsplease Updates: OFF" - else: - await SG.insert_one({"_id": int(query[2])}) - sp = "Subsplease Updates: ON" - btns = InlineKeyboardMarkup( - [ - [InlineKeyboardButton(text=sfw, callback_data=f"settogl_sfw_{query[2]}")], - [ - InlineKeyboardButton( - text=notif, callback_data=f"settogl_notif_{query[2]}" - ) - ], - [InlineKeyboardButton(text=cr, callback_data=f"settogl_cr_{query[2]}")], - [InlineKeyboardButton(text=sp, callback_data=f"settogl_sp_{query[2]}")], - [ - InlineKeyboardButton( - text="Headlines", callback_data=f"headlines_call_{query[2]}" - ) - ], - [ - InlineKeyboardButton( - text="Change UI", callback_data=f"cui_call_{query[2]}" - ) - ], - ] - ) - await cq.answer() - if query[1] == "call": - await cq.edit_message_text(text=setting_text, reply_markup=btns) - await cq.edit_message_reply_markup(reply_markup=btns) - - -@app.on_callback_query(filters.regex(pattern=r"myacc_(.*)")) -@check_user -async def flex_btn(client: Client, cq: CallbackQuery, cdata: dict): - await cq.answer() - query = cdata["data"].split("_")[1] - user = cdata["data"].split("_").pop() - if "-100" in str(user): - auser = await gcc(user) - else: - auser = user - result = await get_user_activity(int(query), user=int(auser), duser=int(user)) - pic, msg, btns = result - try: - await cq.edit_message_media( - InputMediaPhoto(pic, caption=msg), reply_markup=btns - ) - except (WebpageMediaEmpty, WebpageCurlFailed): - await clog("Mikobot", pic, "LINK", msg=cq) - await cq.edit_message_media( - InputMediaPhoto(failed_pic, caption=msg), reply_markup=btns - ) - - -@app.on_callback_query(filters.regex(pattern=r"myfavs_(.*)")) -@check_user -async def list_favourites_btn(client: Client, cq: CallbackQuery, cdata: dict): - await cq.answer() - q = cdata["data"].split("_") - btn = [ - [ - InlineKeyboardButton( - "ANIME", callback_data=f"myfavqry_ANIME_{q[1]}_1_{q[2]}_{q[3]}" - ), - InlineKeyboardButton( - "CHARACTER", callback_data=f"myfavqry_CHAR_{q[1]}_1_{q[2]}_{q[3]}" - ), - InlineKeyboardButton( - "MANGA", callback_data=f"myfavqry_MANGA_{q[1]}_1_{q[2]}_{q[3]}" - ), - ] - ] - if q[2] == "yes": - btn.append([InlineKeyboardButton("BACK", callback_data=f"getusrbc_{q[3]}")]) - else: - btn.append( - [InlineKeyboardButton("PROFILE", url=f"https://anilist.co/user/{q[1]}")] - ) - try: - await cq.edit_message_media( - InputMediaPhoto( - f"https://img.anili.st/user/{q[1]}?a={time.time()}", - caption="Choose one of the below options", - ), - reply_markup=InlineKeyboardMarkup(btn), - ) - except (WebpageMediaEmpty, WebpageCurlFailed): - await clog( - "Mikobot", - f"https://img.anili.st/user/{q[1]}?a={time.time()}", - "LINK", - msg=cq, - ) - await cq.edit_message_media( - InputMediaPhoto(failed_pic, caption="Choose one of the below options"), - reply_markup=InlineKeyboardMarkup(btn), - ) - - -@app.on_callback_query(filters.regex(pattern=r"myfavqry_(.*)")) -@check_user -async def favourites_btn(client: Client, cq: CallbackQuery, cdata: dict): - await cq.answer() - q = cdata["data"].split("_") - if "-100" in str(q[5]): - auser = await gcc(q[5]) - else: - auser = q[5] - pic, msg, btns = await get_user_favourites( - q[2], int(auser), q[1], q[3], q[4], duser=int(q[5]) - ) - try: - await cq.edit_message_media( - InputMediaPhoto(pic, caption=msg), reply_markup=btns - ) - except (WebpageMediaEmpty, WebpageCurlFailed): - await clog("Mikobot", pic, "LINK", msg=cq) - await cq.edit_message_media( - InputMediaPhoto(failed_pic, caption=msg), reply_markup=btns - ) - - -@app.on_callback_query(filters.regex(pattern=r"getusrbc_(.*)")) -@check_user -async def get_user_back_btn(client: Client, cq: CallbackQuery, cdata: dict): - await cq.answer() - query = cdata["data"].split("_")[1] - if "-100" in str(query): - auser = await gcc(query) - else: - auser = query - result = await get_user(None, "flex", user=int(auser), display_user=query) - pic, msg, btns = result - try: - await cq.edit_message_media( - InputMediaPhoto(pic, caption=msg), reply_markup=btns - ) - except (WebpageMediaEmpty, WebpageCurlFailed): - await clog("Mikobot", pic, "LINK", msg=cq) - await cq.edit_message_media( - InputMediaPhoto(failed_pic, caption=msg), creply_markup=btns - ) - - -@app.on_callback_query(filters.regex(pattern=r"fav_(.*)")) -@check_user -async def toggle_favourites_btn(client: Client, cq: CallbackQuery, cdata: dict): - query = cdata["data"].split("_") - if query[1] == "ANIME" and len(query) > 4: - try: - ANIME_DB[query[3]] - except KeyError: - return await cq.answer("Query Expired!!!\nCreate new one", show_alert=True) - if query[1] == "MANGA": - try: - MANGA_DB[query[3]] - except KeyError: - return await cq.answer("Query Expired!!!\nCreate new one", show_alert=True) - if query[1] == "CHARACTER": - try: - CHAR_DB[query[3]] - except KeyError: - return await cq.answer("Query Expired!!!\nCreate new one", show_alert=True) - if query[1] == "STUDIO": - try: - STUDIO_DB[query[3]] - except KeyError: - return await cq.answer("Query Expired!!!\nCreate new one", show_alert=True) - idm = int(query[2]) - user = int(query.pop()) - if "-100" in str(user): - auser = await gcc(user) - else: - auser = user - gid = cdata["message"]["chat"]["id"] - rslt = await toggle_favourites(id_=idm, media=query[1], user=auser) - if rslt == "ok": - await cq.answer("Updated", show_alert=True) - else: - return await cq.answer("Failed to update!!!", show_alert=True) - result = ( - ( - await get_anime( - {"id": idm}, auth=True, user=auser, cid=gid if gid != user else None - ) - ) - if query[1] == "ANIME" and len(query) == 3 - else ( - await get_anilist( - query[3], - page=int(query[4]), - auth=True, - user=auser, - cid=gid if gid != user else None, - ) - ) - if query[1] == "ANIME" and len(query) != 3 - else ( - await get_manga( - query[3], - page=int(query[4]), - auth=True, - user=auser, - cid=gid if gid != user else None, - ) - ) - if query[1] == "MANGA" - else (await get_airing(query[3], auth=True, user=auser, ind=int(query[4]))) - if query[1] == "AIRING" - else (await get_character(query[3], int(query[4]), auth=True, user=auser)) - if query[1] == "CHARACTER" - else ( - await get_studios( - query[3], int(query[4]), user=auser, auth=True, duser=user - ) - ) - ) - if query[1] == "STUDIO": - return await cq.edit_message_text(result[0], reply_markup=result[1]) - pic, msg = ( - (result[0], result[1]) - if query[1] == "ANIME" and len(query) == 3 - else (result[0]) - if query[1] == "AIRING" - else (result[0], result[1][0]) - ) - btns = get_btns( - query[1], - result=result, - user=user, - auth=True, - lsqry=query[3] if len(query) != 3 else None, - lspage=int(query[4]) if len(query) != 3 else None, - ) - try: - await cq.edit_message_media( - InputMediaPhoto(pic, caption=msg), reply_markup=btns - ) - except (WebpageMediaEmpty, WebpageCurlFailed): - await clog("Mikobot", pic, "LINK", msg=cq) - await cq.edit_message_media( - InputMediaPhoto(failed_pic, caption=msg), reply_markup=btns - ) - - -@app.on_callback_query(filters.regex(pattern=r"(lsadd|lsupdt)_(.*)")) -@check_user -async def list_update_anilist_btn(client: Client, cq: CallbackQuery, cdata: dict): - stts_ls = ["COMPLETED", "CURRENT", "PLANNING", "DROPPED", "PAUSED", "REPEATING"] - query = cdata["data"].split("_") - btns = [] - row = [] - for i in stts_ls: - row.append( - InlineKeyboardButton( - text=i, - callback_data=cq.data.replace("lsadd", f"lsas_{i}") - if query[0] == "lsadd" - else cq.data.replace("lsupdt", f"lsus_{i}"), - ) - ) - if len(row) == 3: - btns.append(row) - row = [] - if query[0] == "lsupdt": - btns.append( - [ - InlineKeyboardButton( - "DELETE", callback_data=cq.data.replace("lsupdt", f"dlt_{i}") - ) - ] - ) - await cq.edit_message_reply_markup(reply_markup=InlineKeyboardMarkup(btns)) - - -@app.on_callback_query( - filters.regex(pattern=r"browse_(upcoming|trending|popular)_(.*)") -) -@check_user -async def browse_btn(client: Client, cq: CallbackQuery, cdata: dict): - query = cdata["data"].split("_") - if "•" in query[1]: - return - msg = await browse_(query[1]) - up = "Upcoming" if query[1] != "upcoming" else "• Upcoming •" - tr = "Trending" if query[1] != "trending" else "• Trending •" - pp = "Popular" if query[1] != "popular" else "• Popular •" - btns = [ - [ - InlineKeyboardButton(tr, callback_data=f"browse_{tr.lower()}_{query[2]}"), - InlineKeyboardButton(pp, callback_data=f"browse_{pp.lower()}_{query[2]}"), - InlineKeyboardButton(up, callback_data=f"browse_{up.lower()}_{query[2]}"), - ] - ] - await cq.edit_message_text(msg, reply_markup=InlineKeyboardMarkup(btns)) - - -@app.on_callback_query(filters.regex(pattern=r"(lsas|lsus|dlt)_(.*)")) -@check_user -async def update_anilist_btn(client: Client, cq: CallbackQuery, cdata: dict): - query = cdata["data"].split("_") - if query[2] == "ANIME": - if len(query) == 7: - try: - ANIME_DB[query[4]] - except KeyError: - return await cq.answer( - "Query Expired!!!\nCreate new one", show_alert=True - ) - if len(query) == 8: - try: - ANIME_DB[query[5]] - except KeyError: - return await cq.answer( - "Query Expired!!!\nCreate new one", show_alert=True - ) - if query[2] == "MANGA": - if len(query) == 7: - try: - MANGA_DB[query[4]] - except KeyError: - return await cq.answer( - "Query Expired!!!\nCreate new one", show_alert=True - ) - if len(query) == 8: - try: - MANGA_DB[query[5]] - except KeyError: - return await cq.answer( - "Query Expired!!!\nCreate new one", show_alert=True - ) - idm = int(query[3]) - user = int(query.pop()) - if "-100" in str(user): - auser = await gcc(user) - else: - auser = user - gid = cdata["message"]["chat"]["id"] - eid = None - if query[0] != "lsas": - eid = int(query[4]) - rslt = await update_anilist( - id_=idm, req=query[0], status=query[1], user=auser, eid=eid - ) - if rslt == "ok": - await cq.answer("Updated", show_alert=True) - else: - await cq.answer( - "Something unexpected happened and operation failed successfully", - show_alert=True, - ) - return - result = ( - ( - await get_anime( - {"id": idm}, auth=True, user=auser, cid=gid if gid != user else None - ) - ) - if query[2] == "ANIME" and (len(query) == 4 or len(query) == 5) - else ( - await get_anilist( - query[4], - page=int(query[5]), - auth=True, - user=auser, - cid=gid if gid != user else None, - ) - ) - if query[2] == "ANIME" and len(query) == 6 - else ( - await get_anilist( - query[5], - page=int(query[6]), - auth=True, - user=auser, - cid=gid if gid != user else None, - ) - ) - if query[2] == "ANIME" and len(query) == 7 - else ( - await get_manga( - query[4], - page=int(query[5]), - auth=True, - user=auser, - cid=gid if gid != user else None, - ) - ) - if query[2] == "MANGA" and len(query) == 6 - else ( - await get_manga( - query[5], - page=int(query[6]), - auth=True, - user=auser, - cid=gid if gid != user else None, - ) - ) - if query[2] == "MANGA" and len(query) == 7 - else ( - await get_airing( - query[4] if eid is None else query[5], - auth=True, - user=auser, - ind=int(query[5] if eid is None else query[6]), - ) - ) - ) - pic, msg = ( - (result[0], result[1]) - if query[2] == "ANIME" and (len(query) == 4 or len(query) == 5) - else (result[0]) - if query[2] == "AIRING" - else (result[0], result[1][0]) - ) - btns = get_btns( - query[2], - result=result, - user=user, - auth=True, - lsqry=query[4] if len(query) == 6 else query[5] if len(query) == 7 else None, - lspage=int(query[5]) - if len(query) == 6 - else int(query[6]) - if len(query) == 7 - else None, - ) - try: - await cq.edit_message_media( - InputMediaPhoto(pic, caption=msg), reply_markup=btns - ) - except (WebpageMediaEmpty, WebpageCurlFailed): - await clog("Mikobot", pic, "LINK", msg=cq) - await cq.edit_message_media( - InputMediaPhoto(failed_pic, caption=msg), reply_markup=btns - ) - - -@app.on_callback_query(filters.regex(pattern=r"(desc|ls|char)_(.*)")) -@check_user -async def additional_info_btn(client: Client, cq: CallbackQuery, cdata: dict): - q = cdata["data"].split("_") - kek, qry, ctgry = q[0], q[1], q[2] - info = ( - "<b>Description</b>" - if kek == "desc" - else "<b>Series List</b>" - if kek == "ls" - else "<b>Characters List</b>" - ) - page = 0 - lsqry = f"_{q[3]}" if len(q) > 6 else "" - lspg = f"_{q[4]}" if len(q) > 6 else "" - if kek == "char": - page = q[6] if len(q) > 6 else q[4] - rjsdata = await get_additional_info(qry, ctgry, kek, page=int(page)) - pic, result = rjsdata[0], rjsdata[1] - button = [] - spoiler = False - bot = BOT_USERNAME - if result is None: - await cq.answer("No description available!!!", show_alert=True) - return - if "~!" in result and "!~" in result: - result = re.sub(r"~!.*!~", "[Spoiler]", result) - spoiler = True - button.append( - [ - InlineKeyboardButton( - text="VIEW SPOILER", - url=f"https://t.me/{bot}/?astart=des_{ctgry}_{qry}_desc", - ) - ] - ) - if len(result) > 1000: - result = result[:940] + "..." - if spoiler is False: - result += "\n\nFor more info click below given button" - button.append( - [ - InlineKeyboardButton( - text="MORE INFO", - url=f"https://t.me/{bot}/?astart=des_{ctgry}_{qry}_{kek}", - ) - ] - ) - add_ = "" - user = q.pop() - if kek == "char": - btndata = rjsdata[2] - if btndata["lastPage"] != 1: - qs = q[5] if len(q) != 5 else q[3] - pre = f"{kek}_{qry}_{ctgry}{lsqry}{lspg}_{qs}_{int(page)-1}_{user}" - nex = f"{kek}_{qry}_{ctgry}{lsqry}{lspg}_{qs}_{int(page)+1}_{user}" - if page == "1": - button.append([InlineKeyboardButton(text="NEXT", callback_data=nex)]) - elif btndata["lastPage"] == int(page): - button.append([InlineKeyboardButton(text="PREV", callback_data=pre)]) - else: - button.append( - [ - InlineKeyboardButton(text="PREV", callback_data=pre), - InlineKeyboardButton(text="NEXT", callback_data=nex), - ] - ) - add_ = f"\n\nTotal Characters: {btndata['total']}" - msg = f"{info}:\n\n{result+add_}" - cbd = ( - f"btn_{qry}_{q[3]}_{user}" - if len(q) <= 5 - else f"page_ANIME{lsqry}{lspg}_{q[5]}_{user}" - if ctgry == "ANI" - else f"page_CHARACTER{lsqry}{lspg}_{q[5]}_{user}" - ) - button.append([InlineKeyboardButton(text="BACK", callback_data=cbd)]) - try: - await cq.edit_message_media( - InputMediaPhoto(pic, caption=msg), reply_markup=InlineKeyboardMarkup(button) - ) - except (WebpageMediaEmpty, WebpageCurlFailed): - await clog("Mikobot", pic, "LINK", msg=cq) - await cq.edit_message_media( - InputMediaPhoto(failed_pic, caption=msg), - reply_markup=InlineKeyboardMarkup(button), - ) - await cq.answer() - - -@app.on_callback_query(filters.regex(pattern=r"lsc_(.*)")) -@check_user -async def featured_in_btn(client: Client, cq: CallbackQuery, cdata: dict): - kek, idm, qry, pg, auth, usr = cdata["data"].split("_") - result = await get_featured_in_lists(int(idm), "ANI") - req = "lscm" - if result[0] is False: - result = await get_featured_in_lists(int(idm), "MAN") - req = None - if result[0] is False: - await cq.answer("No Data Available!!!", show_alert=True) - return - [msg, total], pic = result - button = [] - totalpg, kek = divmod(total, 15) - if kek != 0: - totalpg + 1 - if total > 15: - button.append( - [ - InlineKeyboardButton( - text="NEXT", callback_data=f"lsca_{idm}_1_{qry}_{pg}_{auth}_{usr}" - ) - ] - ) - if req is not None: - button.append( - [ - InlineKeyboardButton( - text="MANGA", callback_data=f"lscm_{idm}_0_{qry}_{pg}_{auth}_{usr}" - ) - ] - ) - button.append( - [ - InlineKeyboardButton( - text="BACK", callback_data=f"page_CHARACTER_{qry}_{pg}_{auth}_{usr}" - ) - ] - ) - try: - await cq.edit_message_media( - InputMediaPhoto(pic, caption=msg), reply_markup=InlineKeyboardMarkup(button) - ) - except (WebpageMediaEmpty, WebpageCurlFailed): - await clog("Mikobot", pic, "LINK", msg=cq) - await cq.edit_message_media( - InputMediaPhoto(failed_pic, caption=msg), - reply_markup=InlineKeyboardMarkup(button), - ) - - -@app.on_callback_query(filters.regex(pattern=r"lsc(a|m)_(.*)")) -@check_user -async def featured_in_switch_btn(client: Client, cq: CallbackQuery, cdata: dict): - req, idm, reqpg, qry, pg, auth, user = cdata["data"].split("_") - result = await get_featured_in_lists( - int(idm), "MAN" if req == "lscm" else "ANI", page=int(reqpg) - ) - reqb = "lsca" if req == "lscm" else "lscm" - bt = "Anime" if req == "lscm" else "Manga" - if result[0] is False: - await cq.answer("No Data Available!!!", show_alert=True) - return - [msg, total], pic = result - totalpg, kek = divmod(total, 15) - if kek != 0: - totalpg + 1 - button = [] - if total > 15: - nex = f"{req}_{idm}_{int(reqpg)+1}_{qry}_{pg}_{auth}_{user}" - bac = f"{req}_{idm}_{int(reqpg)-1}_{qry}_{pg}_{auth}_{user}" - if int(reqpg) == 0: - button.append([InlineKeyboardButton(text="NEXT", callback_data=nex)]) - elif int(reqpg) == totalpg: - button.append([InlineKeyboardButton(text="BACK", callback_data=bac)]) - else: - button.append( - [ - InlineKeyboardButton(text="BACK", callback_data=bac), - InlineKeyboardButton(text="NEXT", callback_data=nex), - ] - ) - button.append( - [ - InlineKeyboardButton( - text=f"{bt}", callback_data=f"{reqb}_{idm}_0_{qry}_{pg}_{auth}_{user}" - ) - ] - ) - button.append( - [ - InlineKeyboardButton( - text="BACK", callback_data=f"page_CHARACTER_{qry}_{pg}_{auth}_{user}" - ) - ] - ) - try: - await cq.edit_message_media( - InputMediaPhoto(pic, caption=msg), reply_markup=InlineKeyboardMarkup(button) - ) - except (WebpageMediaEmpty, WebpageCurlFailed): - await clog("Mikobot", pic, "LINK", msg=cq) - await cq.edit_message_media( - InputMediaPhoto(failed_pic, caption=msg), - reply_markup=InlineKeyboardMarkup(button), - ) - - -headlines_text = """ -Turn LiveChart option on to get news feeds from livechart.me -Turn MyAnimeList option on to get news feeds from myanimelist.net - -For Auto Pin and Auto Unpin features, give the bot "Pin Message" and "Delete Message" perms -Auto Unpin can be customized, click on the button to see available options -""" - - -@app.on_callback_query(filters.regex(pattern=r"headlines_(.*)")) -async def headlines_btn(client: Client, cq: CallbackQuery): - cus = cq.from_user.id - qry = cq.data.split("_")[1] - gid = int(cq.data.split("_")[2]) - try: - k = (await client.get_chat_member(gid, cus)).status - except UserNotParticipant: - await cq.answer() - return - if cus not in BOT_OWNER and k == MEMBER: - await cq.answer( - "You don't have enough permissions to change this!!!", show_alert=True - ) - return - lcdata = await HD.find_one({"_id": gid}) - maldata = await MHD.find_one({"_id": gid}) - lchd = "LiveChart: OFF" - malhd = "MyAnimeList: OFF" - malhdpin = lchdpin = "Auto Pin: OFF" - malpin = lcpin = None - if lcdata: - lchd = "LiveChart: ON" - try: - lcpin = lcdata["pin"] - lchdpin = f"Auto Pin: {lcpin}" - except KeyError: - pass - if maldata: - malhd = "MyAnimeList: ON" - try: - malpin = maldata["pin"] - malhdpin = f"Auto Pin: {malpin}" - except KeyError: - pass - if "mal" in qry: - data = maldata - pin = malpin - pin_msg = malhdpin - collection = MHD - src_status = malhd - srcname = "MyAnimeList" - else: - data = lcdata - pin = lcpin - pin_msg = lchdpin - collection = HD - src_status = lchd - srcname = "LiveChart" - if re.match(r"^(mal|lc)hd$", qry): - if data: - await collection.find_one_and_delete(data) - src_status = f"{srcname}: OFF" - pin_msg = f"Auto Pin: OFF" - else: - await collection.insert_one({"_id": gid}) - src_status = f"{srcname}: ON" - pin_msg = f"Auto Pin: OFF" - if re.match(r"^(mal|lc)hdpin$", qry): - if data: - if pin: - switch = "ON" if pin == "OFF" else "OFF" - await collection.find_one_and_update( - data, {"$set": {"pin": switch, "unpin": None}}, upsert=True - ) - pin_msg = f"Auto Pin: {switch}" - else: - await collection.find_one_and_update( - data, {"$set": {"pin": "ON"}}, upsert=True - ) - pin_msg = f"Auto Pin: ON" - else: - await cq.answer(f"Please enable {srcname} first!!!", show_alert=True) - if "mal" in qry: - malhdpin = pin_msg - malhd = src_status - else: - lchdpin = pin_msg - lchd = src_status - btn = InlineKeyboardMarkup( - [ - [InlineKeyboardButton(text=lchd, callback_data=f"headlines_lchd_{gid}")], - [ - InlineKeyboardButton( - text=lchdpin, callback_data=f"headlines_lchdpin_{gid}" - ), - InlineKeyboardButton( - text="Auto Unpin (LC)", callback_data=f"anpin_call_lc_{gid}" - ), - ], - [InlineKeyboardButton(text=malhd, callback_data=f"headlines_malhd_{gid}")], - [ - InlineKeyboardButton( - text=malhdpin, callback_data=f"headlines_malhdpin_{gid}" - ), - InlineKeyboardButton( - text="Auto Unpin (MAL)", callback_data=f"anpin_call_mal_{gid}" - ), - ], - [InlineKeyboardButton(text="BACK", callback_data=f"settogl_call_{gid}")], - ] - ) - await cq.edit_message_text(headlines_text, reply_markup=btn) - await cq.answer() - - -TIMES = { - "1 day": 86400, - "5 days": 432000, - "1 week": 604800, - "2 week": 1209600, - "1 month": 2592000, - "New Feed": 0, - "OFF": None, -} - - -@app.on_callback_query(filters.regex(pattern=r"anpin_(.*)")) -async def auto_unpin(client: Client, cq: CallbackQuery): - cus = cq.from_user.id - qry = cq.data.split("_")[1] - src = cq.data.split("_")[2] - gid = int(cq.data.split("_")[3]) - try: - k = (await client.get_chat_member(gid, cus)).status - except UserNotParticipant: - await cq.answer() - return - if cus not in BOT_OWNER and k == MEMBER: - await cq.answer( - "You don't have enough permissions to change this!!!", show_alert=True - ) - return - cancel = False - if src == "lc": - srcname = "LiveChart" - collection = HD - else: - srcname = "MyAnimeList" - collection = MHD - data = await collection.find_one({"_id": gid}) - if data: - try: - data["pin"] - try: - unpin = data["unpin"] - except KeyError: - unpin = None - except KeyError: - cancel = True - else: - cancel = True - if cancel: - return await cq.answer( - f"Please enable {srcname} and Auto Pin option for them!!!", show_alert=True - ) - setting = None - if qry == "call": - pass - elif qry == "None": - setting = {"unpin": None} - elif qry.isdigit(): - if int(qry) == 0: - unpin = int(qry) - setting = {"unpin": 0} - else: - now = round(time.time(), -2) - unpin = int(qry) - setting = {"unpin": int(qry), "next_unpin": int(qry) + int(now)} - if setting: - await collection.find_one_and_update(data, {"$set": setting}) - btn = [] - row = [] - count = 0 - for i in TIMES.keys(): - count = count + 1 - row.append( - InlineKeyboardButton(i, callback_data=f"unpin_{TIMES[i]}_{src}_{gid}") - ) - if count == 3: - btn.append(row) - count = 0 - row = [] - if len(row) != 0: - btn.append(row) - btn.append([InlineKeyboardButton("BACK", callback_data=f"headlines_call_{gid}")]) - if type(unpin) is int: - if unpin == 0: - unpindata = "after Next Feed" - else: - unpindata = "after " + list(TIMES.keys())[list(TIMES.values()).index(unpin)] - else: - unpindata = "OFF" - await cq.edit_message_text( - f"Auto Unpin options for {srcname}\nCurrently set to: {unpindata}", - reply_markup=InlineKeyboardMarkup(btn), - ) - await cq.answer() - - -BULLETS = ["➤", "•", "⚬", "▲", "▸", "△", "⋟", "»", "None"] - - -@app.on_callback_query(filters.regex(pattern=r"cui_(.*)")) -async def change_ui_btn(client: Client, cq: CallbackQuery): - cus = cq.from_user.id - qry = cq.data.split("_")[1] - gid = cq.data.split("_")[2] - try: - k = (await client.get_chat_member(gid, cus)).status - except UserNotParticipant: - await cq.answer() - return - if cus not in BOT_OWNER and k == MEMBER: - await cq.answer( - "You don't have enough permissions to change this!!!", show_alert=True - ) - return - await cq.answer() - row, btn = [], [] - for i in BULLETS: - row.append(InlineKeyboardButton(text=i, callback_data=f"cui_{i}_{gid}")) - if len(row) == 3: - btn.append(row) - row = [] - btn.append(row) - btn.append( - [ - InlineKeyboardButton(text="CAPS", callback_data=f"cui_Caps_{gid}"), - InlineKeyboardButton(text="UPPER", callback_data=f"cui_UPPER_{gid}"), - ] - ) - btn.append([InlineKeyboardButton(text="BACK", callback_data=f"settogl_call_{gid}")]) - if qry in ["Caps", "UPPER"]: - if await GUI.find_one({"_id": gid}): - await GUI.update_one({"_id": gid}, {"$set": {"cs": qry}}) - else: - await GUI.insert_one({"_id": gid, "bl": "➤", "cs": qry}) - elif qry != "call": - bullet = qry - if qry == "None": - bullet = None - if await GUI.find_one({"_id": gid}): - await GUI.update_one({"_id": gid}, {"$set": {"bl": bullet}}) - else: - await GUI.insert_one({"_id": gid, "bl": bullet, "cs": "UPPER"}) - bl = "➤" - cs = "UPPER" - if await GUI.find_one({"_id": gid}): - data = await GUI.find_one({"_id": gid}) - bl = data["bl"] - cs = data["cs"] - text = f"""Selected bullet in this group: {bl} -Selected text case in this group: {cs}""" - await cq.edit_message_text(text, reply_markup=InlineKeyboardMarkup(btn)) - - -## For accepting commands from edited messages - - -@app.on_edited_message( - filters.command(["anime", f"anime{BOT_USERNAME}"], prefixes=PREFIX_HANDLER) -) -async def anime_edit_cmd(client: app, message: Message): - await anime_cmd(client, message) - - -@app.on_edited_message( - filters.command(["manga", f"manga{BOT_USERNAME}"], prefixes=PREFIX_HANDLER) -) -async def manga_edit_cmd(client: app, message: Message): - await manga_cmd(client, message) - - -@app.on_edited_message( - filters.command(["character", f"character{BOT_USERNAME}"], prefixes=PREFIX_HANDLER) -) -async def character_edit_cmd(client: app, message: Message): - await character_cmd(client, message) - - -@app.on_edited_message( - filters.command(["anilist", f"anilist{BOT_USERNAME}"], prefixes=PREFIX_HANDLER) -) -async def anilist_edit_cmd(client: app, message: Message): - await anilist_cmd(client, message) - - -@app.on_edited_message( - filters.command(["top", f"top{BOT_USERNAME}"], prefixes=PREFIX_HANDLER) -) -async def top_edit_cmd(client: app, message: Message): - await top_tags_cmd(client, message) - - -@app.on_edited_message( - filters.command(["airing", f"airing{BOT_USERNAME}"], prefixes=PREFIX_HANDLER) -) -async def airing_edit_cmd(client: app, message: Message): - await airing_cmd(client, message) - - -@app.on_edited_message( - ~filters.private - & filters.command( - ["anisettings", f"anisettings{BOT_USERNAME}"], prefixes=PREFIX_HANDLER - ) -) -async def settings_edit_cmd(client: app, message: Message): - await settings_cmd(client, message) - - -@app.on_edited_message( - filters.command(["browse", f"browse{BOT_USERNAME}"], prefixes=PREFIX_HANDLER) -) -async def browse_edit_cmd(client: app, message: Message): - await browse_cmd(client, message) - - -@app.on_edited_message( - filters.command( - ["gettags", f"gettags{BOT_USERNAME}", "getgenres", f"getgenres{BOT_USERNAME}"], - prefixes=PREFIX_HANDLER, - ) -) -async def tags_genres_edit_cmd(client: app, message: Message): - await list_tags_genres_cmd(client, message) - - -@app.on_message( - filters.command(["studio", f"studio{BOT_USERNAME}"], prefixes=PREFIX_HANDLER) -) -async def studio_edit_cmd(client: Client, message: Message): - await studio_cmd(client, message) - - -@app.on_message( - filters.command(["schedule", f"schedule{BOT_USERNAME}"], prefixes=PREFIX_HANDLER) -) -@control_user -async def get_schuled(client: Client, message: Message, mdata: dict): - """Get List of Scheduled Anime""" - gid = mdata["chat"]["id"] - find_gc = await DC.find_one({"_id": gid}) - if find_gc is not None and "schedule" in find_gc["cmd_list"].split(): - return - x = await client.send_message(gid, "<code>Fetching Scheduled Animes</code>") - try: - user = mdata["from_user"]["id"] - except KeyError: - user = mdata["sender_chat"]["id"] - msg = await get_scheduled() - buttons = get_btns("SCHEDULED", result=[msg[1]], user=user) - await x.edit_text(msg[0], reply_markup=buttons) - - -@app.on_callback_query(filters.regex(pattern=r"sched_(.*)")) -@check_user -async def ns_(client: app, cq: CallbackQuery, cdata: dict): - kek, day, user = cdata["data"].split("_") - msg = await get_scheduled(int(day)) - buttons = get_btns("SCHEDULED", result=[int(day)], user=user) - await cq.edit_message_text(msg[0], reply_markup=buttons) - - -@app.on_edited_message( - filters.command(["schedule", f"schedule{BOT_USERNAME}"], prefixes=PREFIX_HANDLER) -) -async def get_schuled_edit(client: Client, message: Message): - await get_schuled(client, message) - - -@app.on_message( - filters.command(["watch", f"watch{BOT_USERNAME}"], prefixes=PREFIX_HANDLER) -) -@control_user -async def get_watch_order(client: Client, message: Message, mdata: dict): - """Get List of Scheduled Anime""" - gid = mdata["chat"]["id"] - find_gc = await DC.find_one({"_id": gid}) - if find_gc is not None and "watch" in find_gc["cmd_list"].split(): - return - x = message.text.split(" ", 1) - if len(x) == 1: - await message.reply_text("Nothing given to search for!!!") - return - try: - user = mdata["from_user"]["id"] - except KeyError: - user = mdata["sender_chat"]["id"] - data = get_wols(x[1]) - msg = f"Found related animes for the query {x[1]}" - buttons = [] - if data == []: - await client.send_message(gid, "No results found!!!") - return - for i in data: - buttons.append( - [ - InlineKeyboardButton( - str(i[1]), callback_data=f"watch_{i[0]}_{x[1]}_0_{user}" - ) - ] - ) - await client.send_message(gid, msg, reply_markup=InlineKeyboardMarkup(buttons)) - - -@app.on_callback_query(filters.regex(pattern=r"watch_(.*)")) -@check_user -async def watch_(client: app, cq: CallbackQuery, cdata: dict): - kek, id_, qry, req, user = cdata["data"].split("_") - msg, total = get_wo(int(id_), int(req)) - totalpg, lol = divmod(total, 50) - button = [] - if lol != 0: - totalpg + 1 - if total > 50: - if int(req) == 0: - button.append( - [ - InlineKeyboardButton( - text="NEXT", - callback_data=f"{kek}_{id_}_{qry}_{int(req)+1}_{user}", - ) - ] - ) - elif int(req) == totalpg: - button.append( - [ - InlineKeyboardButton( - text="PREV", - callback_data=f"{kek}_{id_}_{qry}_{int(req)-1}_{user}", - ) - ] - ) - else: - button.append( - [ - InlineKeyboardButton( - text="PREV", - callback_data=f"{kek}_{id_}_{qry}_{int(req)-1}_{user}", - ), - InlineKeyboardButton( - text="NEXT", - callback_data=f"{kek}_{id_}_{qry}_{int(req)+1}_{user}", - ), - ] - ) - button.append([InlineKeyboardButton("Back", callback_data=f"wol_{qry}_{user}")]) - await cq.edit_message_text(msg, reply_markup=InlineKeyboardMarkup(button)) - - -@app.on_callback_query(filters.regex(pattern=r"wol_(.*)")) -@check_user -async def wls(client: app, cq: CallbackQuery, cdata: dict): - kek, qry, user = cdata["data"].split("_") - data = get_wols(qry) - msg = f"Found related animes for the query {qry}" - buttons = [] - for i in data: - buttons.append( - [ - InlineKeyboardButton( - str(i[1]), callback_data=f"watch_{i[0]}_{qry}_0_{user}" - ) - ] - ) - await cq.edit_message_text(msg, reply_markup=InlineKeyboardMarkup(buttons)) - - -@app.on_edited_message( - filters.command(["watch", f"watch{BOT_USERNAME}"], prefixes=PREFIX_HANDLER) -) -async def get_watch_order_edit(client: Client, message: Message): - await get_watch_order(client, message) - - -@app.on_message( - filters.command(["fillers", f"fillers{BOT_USERNAME}"], prefixes=PREFIX_HANDLER) -) -@control_user -async def fillers_cmd(client: app, message: Message, mdata: dict): - find_gc = await DC.find_one({"_id": mdata["chat"]["id"]}) - try: - user = mdata["from_user"]["id"] - except KeyError: - user = mdata["sender_chat"]["id"] - if find_gc is not None and "watch" in find_gc["cmd_list"].split(): - return - qry = mdata["text"].split(" ", 1) - if len(qry) == 1: - return await message.reply_text( - """Give some anime name to search fillers for -example: /fillers Detective Conan""" - ) - k = search_filler(qry[1]) - if k == {}: - await message.reply_text("No fillers found for the given anime...") - return - button = [] - list_ = list(k.keys()) - if len(list_) == 1: - result = parse_filler(k.get(list_[0])) - msg = "" - msg += f"Fillers for anime `{list_[0]}`\n\nManga Canon episodes:\n" - msg += str(result.get("total_ep")) - msg += "\n\nMixed/Canon fillers:\n" - msg += str(result.get("mixed_ep")) - msg += "\n\nFillers:\n" - msg += str(result.get("filler_ep")) - if result.get("ac_ep") is not None: - msg += "\n\nAnime Canon episodes:\n" - msg += str(result.get("ac_ep")) - await message.reply_text(msg) - return - for i in list_: - fl_js = rand_key() - FILLERS[fl_js] = [k.get(i), i] - button.append([InlineKeyboardButton(i, callback_data=f"fill_{fl_js}_{user}")]) - await message.reply_text( - "Pick anime you want to see fillers list for:", - reply_markup=InlineKeyboardMarkup(button), - ) - - -@app.on_callback_query(filters.regex(pattern=r"fill_(.*)")) -@check_user -async def filler_btn(client: app, cq: CallbackQuery, cdata: dict): - kek, req, user = cdata["data"].split("_") - result = parse_filler((FILLERS.get(req))[0]) - msg = "" - msg += f"**Fillers for anime** `{(FILLERS.get(req))[1]}`" - msg += "\n\n**Manga Canon episodes:**\n" - msg += str(result.get("total_ep")) - msg += "\n\n**Mixed/Canon fillers:**\n" - msg += str(result.get("mixed_ep")) - msg += "\n\n**Fillers:**\n" - msg += str(result.get("filler_ep")) - if result.get("ac_ep") is not None: - msg += "\n\n**Anime Canon episodes:**\n" - msg += str(result.get("ac_ep")) - await cq.edit_message_text(msg) - - -@app.on_message( - filters.command(["fillers", f"fillers{BOT_USERNAME}"], prefixes=PREFIX_HANDLER) -) -async def fillers_cmd(client: app, message: Message): - await fillers_cmd(client, message) - - -@app.on_message(filters.command("animequotes")) -async def animequotes(client, message): - name = ( - message.reply_to_message.from_user.first_name - if message.reply_to_message - else message.from_user.first_name - ) - keyboard = [[InlineKeyboardButton(text="CHANGE", callback_data="changek_quote")]] - await message.reply_photo( - photo=random.choice(QUOTES_IMG), - reply_markup=InlineKeyboardMarkup(keyboard), - ) - - -@app.on_callback_query(filters.regex("changek_quote")) -async def changek_quote(client, callback_query): - keyboard = [[InlineKeyboardButton(text="CHANGE", callback_data="changek_quote")]] - await callback_query.edit_message_media( - media=InputMediaPhoto(media=random.choice(QUOTES_IMG)), - reply_markup=InlineKeyboardMarkup(keyboard), - ) - - -QUOTES_IMG = [ - "https://i.imgur.com/Iub4RYj.jpg", - "https://i.imgur.com/uvNMdIl.jpg", - "https://i.imgur.com/YOBOntg.jpg", - "https://i.imgur.com/fFpO2ZQ.jpg", - "https://i.imgur.com/f0xZceK.jpg", - "https://i.imgur.com/RlVcCip.jpg", - "https://i.imgur.com/CjpqLRF.jpg", - "https://i.imgur.com/8BHZDk6.jpg", - "https://i.imgur.com/8bHeMgy.jpg", - "https://i.imgur.com/5K3lMvr.jpg", - "https://i.imgur.com/NTzw4RN.jpg", - "https://i.imgur.com/wJxryAn.jpg", - "https://i.imgur.com/9L0DWzC.jpg", - "https://i.imgur.com/sBe8TTs.jpg", - "https://i.imgur.com/1Au8gdf.jpg", - "https://i.imgur.com/28hFQeU.jpg", - "https://i.imgur.com/Qvc03JY.jpg", - "https://i.imgur.com/gSX6Xlf.jpg", - "https://i.imgur.com/iP26Hwa.jpg", - "https://i.imgur.com/uSsJoX8.jpg", - "https://i.imgur.com/OvX3oHB.jpg", - "https://i.imgur.com/JMWuksm.jpg", - "https://i.imgur.com/lhM3fib.jpg", - "https://i.imgur.com/64IYKkw.jpg", - "https://i.imgur.com/nMbyA3J.jpg", - "https://i.imgur.com/7KFQhY3.jpg", - "https://i.imgur.com/mlKb7zt.jpg", - "https://i.imgur.com/JCQGJVw.jpg", - "https://i.imgur.com/hSFYDEz.jpg", - "https://i.imgur.com/PQRjAgl.jpg", - "https://i.imgur.com/ot9624U.jpg", - "https://i.imgur.com/iXmqN9y.jpg", - "https://i.imgur.com/RhNBeGr.jpg", - "https://i.imgur.com/tcMVNa8.jpg", - "https://i.imgur.com/LrVg810.jpg", - "https://i.imgur.com/TcWfQlz.jpg", - "https://i.imgur.com/muAUdvJ.jpg", - "https://i.imgur.com/AtC7ZRV.jpg", - "https://i.imgur.com/sCObQCQ.jpg", - "https://i.imgur.com/AJFDI1r.jpg", - "https://i.imgur.com/TCgmRrH.jpg", - "https://i.imgur.com/LMdmhJU.jpg", - "https://i.imgur.com/eyyax0N.jpg", - "https://i.imgur.com/YtYxV66.jpg", - "https://i.imgur.com/292w4ye.jpg", - "https://i.imgur.com/6Fm1vdw.jpg", - "https://i.imgur.com/2vnBOZd.jpg", - "https://i.imgur.com/j5hI9Eb.jpg", - "https://i.imgur.com/cAv7pJB.jpg", - "https://i.imgur.com/jvI7Vil.jpg", - "https://i.imgur.com/fANpjsg.jpg", - "https://i.imgur.com/5o1SJyo.jpg", - "https://i.imgur.com/dSVxmh8.jpg", - "https://i.imgur.com/02dXlAD.jpg", - "https://i.imgur.com/htvIoGY.jpg", - "https://i.imgur.com/hy6BXOj.jpg", - "https://i.imgur.com/OuwzNYu.jpg", - "https://i.imgur.com/L8vwvc2.jpg", - "https://i.imgur.com/3VMVF9y.jpg", - "https://i.imgur.com/yzjq2n2.jpg", - "https://i.imgur.com/0qK7TAN.jpg", - "https://i.imgur.com/zvcxSOX.jpg", - "https://i.imgur.com/FO7bApW.jpg", - "https://i.imgur.com/KK06gwg.jpg", - "https://i.imgur.com/6lG4tsO.jpg", -] - - -# <================================================= HELP ======================================================> -__help__ = """ -⛩ *Anime:* - -➠ *Mikobot provides you the best anime-based commands including anime news and much more!* - -➠ *Commands:* - -» /anime: fetches info on single anime (includes buttons to look up for prequels and sequels) -» /character: fetches info on multiple possible characters related to query -» /manga: fetches info on multiple possible mangas related to query -» /airing: fetches info on airing data for anime -» /studio: fetches info on multiple possible studios related to query -» /schedule: fetches scheduled animes -» /browse: get popular, trending or upcoming animes - -➠ /animequotes: get random anime quotes - -» /anisettings: to toggle NSFW lock and airing notifications and other settings in groups (anime news) -» /top: to retrieve top animes for a genre or tag -» /watch: fetches watch order for anime series -» /fillers: to get a list of anime fillers -» /gettags: get a list of available tags -» /getgenres - Get list of available Genres -""" - -__mod_name__ = "ANIME" -# <================================================== END =====================================================> diff --git a/Mikobot/plugins/antinsfw.py b/Mikobot/plugins/antinsfw.py deleted file mode 100644 index 2856af6d646c456f03d22825622e2c4ba73879b3..0000000000000000000000000000000000000000 --- a/Mikobot/plugins/antinsfw.py +++ /dev/null @@ -1,189 +0,0 @@ -# <============================================== IMPORTS =========================================================> -from os import remove - -from pyrogram import filters - -from Database.mongodb.toggle_mongo import is_nsfw_on, nsfw_off, nsfw_on -from Mikobot import BOT_USERNAME, DRAGONS, app -from Mikobot.state import arq -from Mikobot.utils.can_restrict import can_restrict -from Mikobot.utils.errors import capture_err - -# <=======================================================================================================> - - -# <================================================ FUNCTION =======================================================> -async def get_file_id_from_message(message): - file_id = None - if message.document: - if int(message.document.file_size) > 3145728: - return - mime_type = message.document.mime_type - if mime_type not in ("image/png", "image/jpeg"): - return - file_id = message.document.file_id - - if message.sticker: - if message.sticker.is_animated: - if not message.sticker.thumbs: - return - file_id = message.sticker.thumbs[0].file_id - else: - file_id = message.sticker.file_id - - if message.photo: - file_id = message.photo.file_id - - if message.animation: - if not message.animation.thumbs: - return - file_id = message.animation.thumbs[0].file_id - - if message.video: - if not message.video.thumbs: - return - file_id = message.video.thumbs[0].file_id - return file_id - - -@app.on_message( - ( - filters.document - | filters.photo - | filters.sticker - | filters.animation - | filters.video - ) - & ~filters.private, - group=8, -) -@capture_err -async def detect_nsfw(_, message): - if not await is_nsfw_on(message.chat.id): - return - if not message.from_user: - return - file_id = await get_file_id_from_message(message) - if not file_id: - return - file = await _.download_media(file_id) - try: - results = await arq.nsfw_scan(file=file) - except Exception: - return - if not results.ok: - return - results = results.result - remove(file) - nsfw = results.is_nsfw - if message.from_user.id in DRAGONS: - return - if not nsfw: - return - try: - await message.delete() - except Exception: - return - await message.reply_text( - f""" -**🔞 NSFW Image Detected & Deleted Successfully!** - -**✪ User:** {message.from_user.mention} [`{message.from_user.id}`] -**✪ Safe:** `{results.neutral} %` -**✪ Porn:** `{results.porn} %` -**✪ Adult:** `{results.sexy} %` -**✪ Hentai:** `{results.hentai} %` -**✪ Drawings:** `{results.drawings} %` -""" - ) - - -@app.on_message(filters.command(["nsfwscan", f"nsfwscan@{BOT_USERNAME}"])) -@capture_err -async def nsfw_scan_command(_, message): - if not message.reply_to_message: - await message.reply_text( - "Reply to an image/document/sticker/animation to scan it." - ) - return - reply = message.reply_to_message - if ( - not reply.document - and not reply.photo - and not reply.sticker - and not reply.animation - and not reply.video - ): - await message.reply_text( - "Reply to an image/document/sticker/animation to scan it." - ) - return - m = await message.reply_text("Scanning") - file_id = await get_file_id_from_message(reply) - if not file_id: - return await m.edit("Something wrong happened.") - file = await _.download_media(file_id) - try: - results = await arq.nsfw_scan(file=file) - except Exception: - return - remove(file) - if not results.ok: - return await m.edit(results.result) - results = results.result - await m.edit( - f""" -**➢ Neutral:** `{results.neutral} %` -**➢ Porn:** `{results.porn} %` -**➢ Hentai:** `{results.hentai} %` -**➢ Sexy:** `{results.sexy} %` -**➢ Drawings:** `{results.drawings} %` -**➢ NSFW:** `{results.is_nsfw}` -""" - ) - - -@app.on_message( - filters.command(["antinsfw", f"antinsfw@{BOT_USERNAME}"]) & ~filters.private -) -@can_restrict -async def nsfw_enable_disable(_, message): - if len(message.command) != 2: - await message.reply_text("Usage: /antinsfw [on/off]") - return - status = message.text.split(None, 1)[1].strip() - status = status.lower() - chat_id = message.chat.id - if status in ("on", "yes"): - if await is_nsfw_on(chat_id): - await message.reply_text("Antinsfw is already enabled.") - return - await nsfw_on(chat_id) - await message.reply_text( - "Enabled AntiNSFW System. I will Delete Messages Containing Inappropriate Content." - ) - elif status in ("off", "no"): - if not await is_nsfw_on(chat_id): - await message.reply_text("Antinsfw is already disabled.") - return - await nsfw_off(chat_id) - await message.reply_text("Disabled AntiNSFW System.") - else: - await message.reply_text("Unknown Suffix, Use /antinsfw [on/off]") - - -# <=================================================== HELP ====================================================> - - -__mod_name__ = "ANTI-NSFW" - -__help__ = """ -*🔞 Helps in detecting NSFW material and removing it*. - -➠ *Usage:* - -» /antinsfw [on/off]: Enables Anti-NSFW system. - -» /nsfwscan <reply to message>: Scans the file replied to. -""" -# <================================================ END =======================================================> diff --git a/Mikobot/plugins/approve.py b/Mikobot/plugins/approve.py deleted file mode 100644 index b3fba10e27aa0cb74d265777071f86f18c487c49..0000000000000000000000000000000000000000 --- a/Mikobot/plugins/approve.py +++ /dev/null @@ -1,259 +0,0 @@ -# <============================================== IMPORTS =========================================================> -import html - -from telegram import InlineKeyboardButton, InlineKeyboardMarkup, Update -from telegram.constants import ChatMemberStatus, ParseMode -from telegram.error import BadRequest -from telegram.ext import CallbackQueryHandler, ContextTypes -from telegram.helpers import mention_html - -import Database.sql.approve_sql as sql -from Mikobot import DRAGONS, dispatcher -from Mikobot.plugins.disable import DisableAbleCommandHandler -from Mikobot.plugins.helper_funcs.chat_status import check_admin -from Mikobot.plugins.helper_funcs.extraction import extract_user -from Mikobot.plugins.log_channel import loggable - -# <=======================================================================================================> - - -# <================================================ FUNCTION =======================================================> -@loggable -@check_admin(is_user=True) -async def approve(update: Update, context: ContextTypes.DEFAULT_TYPE): - message = update.effective_message - chat_title = message.chat.title - chat = update.effective_chat - args = context.args - user = update.effective_user - user_id = await extract_user(message, context, args) - if not user_id: - await message.reply_text( - "I don't know who you're talking about, you're going to need to specify a user!", - ) - return "" - try: - member = await chat.get_member(user_id) - except BadRequest: - return "" - if member.status in [ChatMemberStatus.ADMINISTRATOR, ChatMemberStatus.OWNER]: - await message.reply_text( - "User is already admin - locks, blocklists, and antiflood already don't apply to them.", - ) - return "" - if sql.is_approved(message.chat_id, user_id): - await message.reply_text( - f"[{member.user.first_name}](tg://user?id={member.user.id}) is already approved in {chat_title}", - parse_mode=ParseMode.MARKDOWN, - ) - return "" - sql.approve(message.chat_id, user_id) - await message.reply_text( - f"[{member.user.first_name}](tg://user?id={member.user.id}) has been approved in {chat_title}! They will now be ignored by automated admin actions like locks, blocklists, and antiflood.", - parse_mode=ParseMode.MARKDOWN, - ) - log_message = ( - f"<b>{html.escape(chat.title)}:</b>\n" - f"#APPROVED\n" - f"<b>Admin:</b> {mention_html(user.id, user.first_name)}\n" - f"<b>User:</b> {mention_html(member.user.id, member.user.first_name)}" - ) - - return log_message - - -@loggable -@check_admin(is_user=True) -async def disapprove(update: Update, context: ContextTypes.DEFAULT_TYPE): - message = update.effective_message - chat_title = message.chat.title - chat = update.effective_chat - args = context.args - user = update.effective_user - user_id = await extract_user(message, context, args) - if not user_id: - await message.reply_text( - "I don't know who you're talking about, you're going to need to specify a user!", - ) - return "" - try: - member = await chat.get_member(user_id) - except BadRequest: - return "" - if member.status in [ChatMemberStatus.ADMINISTRATOR, ChatMemberStatus.OWNER]: - await message.reply_text("This user is an admin, they can't be unapproved.") - return "" - if not sql.is_approved(message.chat_id, user_id): - await message.reply_text(f"{member.user.first_name} isn't approved yet!") - return "" - sql.disapprove(message.chat_id, user_id) - await message.reply_text( - f"{member.user.first_name} is no longer approved in {chat_title}.", - ) - log_message = ( - f"<b>{html.escape(chat.title)}:</b>\n" - f"#UNAPPROVED\n" - f"<b>Admin:</b> {mention_html(user.id, user.first_name)}\n" - f"<b>User:</b> {mention_html(member.user.id, member.user.first_name)}" - ) - - return log_message - - -@check_admin(is_user=True) -async def approved(update: Update, context: ContextTypes.DEFAULT_TYPE): - message = update.effective_message - chat_title = message.chat.title - chat = update.effective_chat - msg = "The following users are approved.\n" - approved_users = sql.list_approved(message.chat_id) - - if not approved_users: - await message.reply_text(f"No users are approved in {chat_title}.") - return "" - - else: - for i in approved_users: - member = await chat.get_member(int(i.user_id)) - msg += f"- `{i.user_id}`: {member.user['first_name']}\n" - - await message.reply_text(msg, parse_mode=ParseMode.MARKDOWN) - - -@check_admin(is_user=True) -async def approval(update: Update, context: ContextTypes.DEFAULT_TYPE): - message = update.effective_message - chat = update.effective_chat - args = context.args - user_id = await extract_user(message, context, args) - - if not user_id: - await message.reply_text( - "I don't know who you're talking about, you're going to need to specify a user!", - ) - return "" - member = await chat.get_member(int(user_id)) - if sql.is_approved(message.chat_id, user_id): - await message.reply_text( - f"{member.user['first_name']} is an approved user. Locks, antiflood, and blocklists won't apply to them.", - ) - else: - await message.reply_text( - f"{member.user['first_name']} is not an approved user. They are affected by normal commands.", - ) - - -async def unapproveall(update: Update, context: ContextTypes.DEFAULT_TYPE): - chat = update.effective_chat - user = update.effective_user - member = await chat.get_member(user.id) - - approved_users = sql.list_approved(chat.id) - if not approved_users: - await update.effective_message.reply_text( - f"No users are approved in {chat.title}." - ) - return - - if member.status != ChatMemberStatus.OWNER and user.id not in DRAGONS: - await update.effective_message.reply_text( - "Only the chat owner can unapprove all users at once.", - ) - else: - buttons = InlineKeyboardMarkup( - [ - [ - InlineKeyboardButton( - text="Unapprove all users", - callback_data="unapproveall_user", - ), - ], - [ - InlineKeyboardButton( - text="Cancel", - callback_data="unapproveall_cancel", - ), - ], - ], - ) - await update.effective_message.reply_text( - f"Are you sure you would like to unapprove ALL users in {chat.title}? This action cannot be undone.", - reply_markup=buttons, - parse_mode=ParseMode.MARKDOWN, - ) - - -async def unapproveall_btn(update: Update, context: ContextTypes.DEFAULT_TYPE): - query = update.callback_query - chat = update.effective_chat - message = update.effective_message - member = await chat.get_member(query.from_user.id) - if query.data == "unapproveall_user": - if member.status == ChatMemberStatus.OWNER or query.from_user.id in DRAGONS: - approved_users = sql.list_approved(chat.id) - users = [int(i.user_id) for i in approved_users] - for user_id in users: - sql.disapprove(chat.id, user_id) - await message.edit_text("Successfully Unapproved all user in this Chat.") - return - - if member.status == "administrator": - await query.answer("Only owner of the chat can do this.") - - if member.status == "member": - await query.answer("You need to be admin to do this.") - elif query.data == "unapproveall_cancel": - if member.status == "creator" or query.from_user.id in DRAGONS: - await message.edit_text( - "Removing of all approved users has been cancelled." - ) - return "" - if member.status == "administrator": - await query.answer("Only owner of the chat can do this.") - if member.status == "member": - await query.answer("You need to be admin to do this.") - - -# <=================================================== HELP ====================================================> - - -__help__ = """ -➠ Sometimes, you might trust a user not to send unwanted content. -Maybe not enough to make them admin, but you might be ok with locks, blacklists, and antiflood not applying to them. - -➠ That's what approvals are for - approve of trustworthy users to allow them to send - -➠ *Admin commands:* - -» /approval: Check a user's approval status in this chat. - -» /approve: Approve of a user. Locks, blacklists, and antiflood won't apply to them anymore. - -» /unapprove: Unapprove of a user. They will now be subject to locks, blacklists, and antiflood again. - -» /approved: List all approved users. - -» /unapproveall: Unapprove *ALL* users in a chat. This cannot be undone. -""" - -# <================================================ HANDLER =======================================================> -APPROVE = DisableAbleCommandHandler("approve", approve, block=False) -DISAPPROVE = DisableAbleCommandHandler("unapprove", disapprove, block=False) -APPROVED = DisableAbleCommandHandler("approved", approved, block=False) -APPROVAL = DisableAbleCommandHandler("approval", approval, block=False) -UNAPPROVEALL = DisableAbleCommandHandler("unapproveall", unapproveall, block=False) -UNAPPROVEALL_BTN = CallbackQueryHandler( - unapproveall_btn, pattern=r"unapproveall_.*", block=False -) - -dispatcher.add_handler(APPROVE) -dispatcher.add_handler(DISAPPROVE) -dispatcher.add_handler(APPROVED) -dispatcher.add_handler(APPROVAL) -dispatcher.add_handler(UNAPPROVEALL) -dispatcher.add_handler(UNAPPROVEALL_BTN) - -__mod_name__ = "APPROVALS" -__command_list__ = ["approve", "unapprove", "approved", "approval"] -__handlers__ = [APPROVE, DISAPPROVE, APPROVED, APPROVAL] -# <================================================ END =======================================================> diff --git a/Mikobot/plugins/ban.py b/Mikobot/plugins/ban.py deleted file mode 100644 index ada4008feeb26c7ac6cc9174d0f3efb5bdb71857..0000000000000000000000000000000000000000 --- a/Mikobot/plugins/ban.py +++ /dev/null @@ -1,748 +0,0 @@ -# <============================================== IMPORTS =========================================================> -import html - -from telegram import ( - ChatMemberAdministrator, - InlineKeyboardButton, - InlineKeyboardMarkup, - Update, -) -from telegram.constants import ParseMode -from telegram.error import BadRequest -from telegram.ext import CallbackQueryHandler, CommandHandler, ContextTypes, filters -from telegram.helpers import mention_html - -from Mikobot import DEV_USERS, DRAGONS, LOGGER, OWNER_ID, function -from Mikobot.utils.can_restrict import BAN_STICKER -from Mikobot.plugins.disable import DisableAbleCommandHandler -from Mikobot.plugins.helper_funcs.chat_status import ( - can_delete, - check_admin, - connection_status, - is_user_admin, - is_user_ban_protected, - is_user_in_chat, -) -from Mikobot.plugins.helper_funcs.extraction import extract_user_and_text -from Mikobot.plugins.helper_funcs.misc import mention_username -from Mikobot.plugins.helper_funcs.string_handling import extract_time -from Mikobot.plugins.log_channel import gloggable, loggable - -# <=======================================================================================================> - - -# <================================================ FUNCTION =======================================================> -@connection_status -@loggable -@check_admin(permission="can_restrict_members", is_both=True) -async def ban(update: Update, context: ContextTypes.DEFAULT_TYPE) -> str: - chat = update.effective_chat - user = update.effective_user - message = update.effective_message - log_message = "" - bot = context.bot - args = context.args - user_id, reason = await extract_user_and_text(message, context, args) - - member = await chat.get_member(user.id) - SILENT = bool(True if message.text.startswith("/s") else False) - - # if update is coming from anonymous admin then send button and return. - if message.from_user.id == 1087968824: - if SILENT: - await message.reply_text("Currently /sban won't work for anoymous admins.") - return log_message - # Need chat title to be forwarded on callback data to mention channel after banning. - try: - chat_title = message.reply_to_message.sender_chat.title - except AttributeError: - chat_title = None - await update.effective_message.reply_text( - text="You are an anonymous admin.", - reply_markup=InlineKeyboardMarkup( - [ - [ - InlineKeyboardButton( - text="Click to prove Admin.", - callback_data=f"bans_{chat.id}=ban={user_id}={reason}={chat_title}", - ), - ], - ] - ), - ) - - return log_message - elif ( - not ( - ( - member.can_restrict_members - if isinstance(member, ChatMemberAdministrator) - else None - ) - or member.status == "creator" - ) - and user.id not in DRAGONS - ): - await update.effective_message.reply_text( - "Sorry son, but you're not worthy to wield the banhammer.", - ) - return log_message - - if user_id == bot.id: - await message.reply_text("Oh yeah, ban myself, noob!") - return log_message - - if user_id is not None and user_id < 0: - CHAT_SENDER = True - chat_sender = message.reply_to_message.sender_chat - else: - CHAT_SENDER = False - try: - member = await chat.get_member(user_id) - except BadRequest as excp: - if excp.message == "User not found": - raise - elif excp == "Invalid user_id specified": - await message.reply_text("I Doubt that's a user.") - await message.reply_text("Can't find this person here.") - return log_message - - if await is_user_ban_protected(chat, user_id, member) and user not in DEV_USERS: - if user_id == OWNER_ID: - await message.reply_text( - "Trying to put me against a God level disaster huh?" - ) - elif user_id in DEV_USERS: - await message.reply_text("I can't act against our own.") - elif user_id in DRAGONS: - await message.reply_text( - "Fighting this Dragon here will put me and my people's at risk.", - ) - else: - await message.reply_text("This user has immunity and cannot be banned.") - return log_message - - if SILENT: - silent = True - if not await can_delete(chat, context.bot.id): - return "" - else: - silent = False - - log = ( - f"<b>{html.escape(chat.title)}:</b>\n" - f"#{'S' if silent else ''}BANNED\n" - f"<b>Admin:</b> {mention_html(user.id, html.escape(user.first_name))}\n" - ) - - reply = f"<code>❕</code><b>Ban Event</b>\n" - - if CHAT_SENDER: - log += f"<b>Channel:</b> {mention_username(chat_sender.username, html.escape(chat_sender.title))}" - reply += f"<code> </code><b>• Channel:</b> {mention_username(chat_sender.username, html.escape(chat_sender.title))}" - - else: - log += f"<b>User:</b> {mention_html(member.user.id, html.escape(member.user.first_name))}" - reply += f"<code> </code><b>• User:</b> {mention_html(member.user.id, html.escape(member.user.first_name))}" - - if reason: - log += "\n<b>Reason:</b> {}".format(reason) - - try: - if CHAT_SENDER: - await chat.ban_sender_chat(sender_chat_id=chat_sender.id) - else: - await chat.ban_member(user_id) - - if silent: - if message.reply_to_message: - await message.reply_to_message.delete() - await message.delete() - return log - - await bot.send_sticker( - chat.id, - BAN_STICKER, - message_thread_id=message.message_thread_id if chat.is_forum else None, - ) # banhammer marie sticker - - if reason: - reply += f"\n<code> </code><b>• Reason:</b> \n{html.escape(reason)}" - await bot.sendMessage( - chat.id, - reply, - parse_mode=ParseMode.HTML, - message_thread_id=message.message_thread_id if chat.is_forum else None, - ) - return log - - except BadRequest as excp: - if excp.message == "Reply message not found": - # Do not reply - if silent: - return log - await message.reply_text("Banned!", quote=False) - return log - else: - LOGGER.warning(update) - LOGGER.exception( - "ERROR banning user %s in chat %s (%s) due to %s", - user_id, - chat.title, - chat.id, - excp.message, - ) - await message.reply_text("Uhm...that didn't work...") - - return log_message - - -@connection_status -@loggable -@check_admin(permission="can_restrict_members", is_both=True) -async def temp_ban(update: Update, context: ContextTypes.DEFAULT_TYPE) -> str: - chat = update.effective_chat - user = update.effective_user - message = update.effective_message - log_message = "" - bot, args = context.bot, context.args - user_id, reason = await extract_user_and_text(message, context, args) - - if not user_id: - await message.reply_text("I doubt that's a user.") - return log_message - - try: - member = await chat.get_member(user_id) - except BadRequest as excp: - if excp.message != "User not found": - raise - await message.reply_text("I can't seem to find this user.") - return log_message - if user_id == bot.id: - await message.reply_text("I'm not gonna BAN myself, are you crazy?") - return log_message - - if await is_user_ban_protected(chat, user_id, member): - await message.reply_text("I don't feel like it.") - return log_message - - if not reason: - await message.reply_text("You haven't specified a time to ban this user for!") - return log_message - - split_reason = reason.split(None, 1) - - time_val = split_reason[0].lower() - reason = split_reason[1] if len(split_reason) > 1 else "" - bantime = await extract_time(message, time_val) - - if not bantime: - return log_message - - log = ( - f"<b>{html.escape(chat.title)}:</b>\n" - "#TEMP BANNED\n" - f"<b>Admin:</b> {mention_html(user.id, html.escape(user.first_name))}\n" - f"<b>User:</b> {mention_html(member.user.id, html.escape(member.user.first_name))}\n" - f"<b>Time:</b> {time_val}" - ) - if reason: - log += "\n<b>Reason:</b> {}".format(reason) - - try: - await chat.ban_member(user_id, until_date=bantime) - await bot.send_sticker( - chat.id, - BAN_STICKER, - message_thread_id=message.message_thread_id if chat.is_forum else None, - ) # banhammer marie sticker - await bot.sendMessage( - chat.id, - f"Banned! User {mention_html(member.user.id, html.escape(member.user.first_name))} " - f"will be banned for {time_val}.", - parse_mode=ParseMode.HTML, - message_thread_id=message.message_thread_id if chat.is_forum else None, - ) - return log - - except BadRequest as excp: - if excp.message == "Reply message not found": - # Do not reply - await message.reply_text( - f"Banned! User will be banned for {time_val}.", - quote=False, - ) - return log - else: - LOGGER.warning(update) - LOGGER.exception( - "ERROR banning user %s in chat %s (%s) due to %s", - user_id, - chat.title, - chat.id, - excp.message, - ) - await message.reply_text("Well damn, I can't ban that user.") - - return log_message - - -@connection_status -@loggable -@check_admin(permission="can_restrict_members", is_both=True) -async def kick(update: Update, context: ContextTypes.DEFAULT_TYPE) -> str: - chat = update.effective_chat - user = update.effective_user - message = update.effective_message - log_message = "" - bot, args = context.bot, context.args - user_id, reason = await extract_user_and_text(message, context, args) - - if not user_id: - await message.reply_text("I doubt that's a user.") - return log_message - - try: - member = await chat.get_member(user_id) - except BadRequest as excp: - if excp.message != "User not found": - raise - - await message.reply_text("I can't seem to find this user.") - return log_message - if user_id == bot.id: - await message.reply_text("Yeahhh I'm not gonna do that.") - return log_message - - if await is_user_ban_protected(chat, user_id): - await message.reply_text("I really wish I could kick this user....") - return log_message - - res = chat.unban_member(user_id) # unban on current user = kick - if res: - await bot.send_sticker( - chat.id, - BAN_STICKER, - message_thread_id=message.message_thread_id if chat.is_forum else None, - ) # banhammer marie sticker - await bot.sendMessage( - chat.id, - f"Capitain I have kicked, {mention_html(member.user.id, html.escape(member.user.first_name))}.", - parse_mode=ParseMode.HTML, - message_thread_id=message.message_thread_id if chat.is_forum else None, - ) - log = ( - f"<b>{html.escape(chat.title)}:</b>\n" - f"#KICKED\n" - f"<b>Admin:</b> {mention_html(user.id, html.escape(user.first_name))}\n" - f"<b>User:</b> {mention_html(member.user.id, html.escape(member.user.first_name))}" - ) - if reason: - log += f"\n<b>Reason:</b> {reason}" - - return log - - else: - await message.reply_text("Well damn, I can't kick that user.") - - return log_message - - -@check_admin(permission="can_restrict_members", is_bot=True) -async def kickme(update: Update, context: ContextTypes.DEFAULT_TYPE): - user_id = update.effective_message.from_user.id - if await is_user_admin(update.effective_chat, user_id): - await update.effective_message.reply_text( - "I wish I could... but you're an admin." - ) - return - - res = await update.effective_chat.unban_member( - user_id - ) # unban on current user = kick - # BUG: parsing not working - if res: - await update.effective_message.reply_text( - html.escape("You got the Devil's Kiss, Now die in peace"), parse_mode="html" - ) - else: - await update.effective_message.reply_text("Huh? I can't :/") - - -@connection_status -@loggable -@check_admin(permission="can_restrict_members", is_both=True) -async def unban(update: Update, context: ContextTypes.DEFAULT_TYPE) -> str: - message = update.effective_message - user = update.effective_user - chat = update.effective_chat - log_message = "" - bot, args = context.bot, context.args - user_id, reason = await extract_user_and_text(message, context, args) - - if message.from_user.id == 1087968824: - try: - chat_title = message.reply_to_message.sender_chat.title - except AttributeError: - chat_title = None - - await message.reply_text( - text="You are an anonymous admin.", - reply_markup=InlineKeyboardMarkup( - [ - [ - InlineKeyboardButton( - text="Click to prove Admin.", - callback_data=f"bans_{chat.id}=unban={user_id}={reason}={chat_title}", - ), - ], - ] - ), - ) - - return log_message - - if not user_id: - await message.reply_text("I doubt that's a user.") - return log_message - - if user_id == bot.id: - await message.reply_text("How would I unban myself if I wasn't here...?") - return log_message - - if user_id is not None and user_id < 0: - CHAT_SENDER = True - chat_sender = message.reply_to_message.sender_chat - else: - CHAT_SENDER = False - try: - member = await chat.get_member(user_id) - - if isinstance(member, ChatMemberAdministrator): - await message.reply_text( - "This person is an admin here, Are you drunk???" - ) - return log_message - - except BadRequest as excp: - raise - if excp.message != "User not found": - raise - await message.reply_text("I can't seem to find this user.") - return log_message - - if await is_user_in_chat(chat, user_id): - await message.reply_text("Isn't this person already here??") - return log_message - - log = ( - f"<b>{html.escape(chat.title)}:</b>\n" - f"#UNBANNED\n" - f"<b>Admin:</b> {mention_html(user.id, html.escape(user.first_name))}\n" - ) - - if CHAT_SENDER: - log += f"<b>User:</b> {mention_username(chat_sender.id, html.escape(chat_sender.title))}" - await chat.unban_sender_chat(chat_sender.id) - await message.reply_text("Yeah, this channel can speak again.") - else: - log += f"<b>User:</b> {mention_html(member.user.id, html.escape(member.user.first_name))}" - await chat.unban_member(user_id) - await message.reply_text("Yeah, this user can join!") - - if reason: - log += f"\n<b>Reason:</b> {reason}" - - return log - - -@connection_status -@gloggable -@check_admin(permission="can_restrict_members", is_bot=True) -async def selfunban(context: ContextTypes.DEFAULT_TYPE, update: Update) -> str: - message = update.effective_message - user = update.effective_user - bot, args = context.bot, context.args - if user.id not in DRAGONS: - return - - try: - chat_id = int(args[0]) - except: - await message.reply_text("Give a valid chat ID.") - return - - chat = await bot.getChat(chat_id) - - try: - member = await chat.get_member(user.id) - except BadRequest as excp: - if excp.message == "User not found": - await message.reply_text("I can't seem to find this user.") - return - else: - raise - - if await is_user_in_chat(chat, user.id): - await message.reply_text("Aren't you already in the chat??") - return - - await chat.unban_member(user.id) - await message.reply_text("Yep, I have unbanned you.") - - log = ( - f"<b>{html.escape(chat.title)}:</b>\n" - f"#UNBANNED\n" - f"<b>User:</b> {mention_html(member.user.id, html.escape(member.user.first_name))}" - ) - - return log - - -@loggable -async def bans_callback(update: Update, context: ContextTypes.DEFAULT_TYPE): - query = update.callback_query - bot = context.bot - chat = update.effective_chat - message = update.effective_message - args = context.args - log_message = "" - splitter = query.data.replace("bans_", "").split("=") - - admin_user = query.from_user - member = await chat.get_member(admin_user.id) - - if splitter[1] == "ban": - # workaround for checking user admin status - try: - user_id = int(splitter[2]) - except ValueError: - user_id = splitter[2] - reason = splitter[3] - chat_name = splitter[4] - - if not ( - ( - member.can_restrict_members - if isinstance(member, ChatMemberAdministrator) - else None - ) - or member.status == "creator" - ) and (admin_user.id not in DRAGONS): - await query.answer( - "Sorry son, but you're not worthy to wield the banhammer.", - show_alert=True, - ) - return log_message - - if user_id == bot.id: - await message.edit_text("Oh yeah, ban myself, noob!") - return log_message - - if isinstance(user_id, str): - await message.edit_text("I doubt that's a user.") - return log_message - - if user_id < 0: - CHAT_SENDER = True - else: - CHAT_SENDER = False - try: - member = await chat.get_member(user_id) - except BadRequest as excp: - if excp.message == "User not found.": - raise - elif excp == "Invalid user_id specified": - await message.edit_text("I Doubt that's a user.") - await message.edit_text("Can't find this person here.") - - return log_message - - if ( - await is_user_ban_protected(chat, user_id, member) - and admin_user not in DEV_USERS - ): - if user_id == OWNER_ID: - await message.edit_text( - "Trying to put me against a God level disaster huh?" - ) - elif user_id in DEV_USERS: - await message.edit_text("I can't act against our own.") - elif user_id in DRAGONS: - await message.edit_text( - "Fighting this Dragon here will put me and my people's at risk.", - ) - else: - await message.edit_text( - "This user has immunity and cannot be banned." - ) - return log_message - - log = ( - f"<b>{html.escape(chat.title)}:</b>\n" - f"#BANNED\n" - f"<b>Admin:</b> {mention_html(admin_user.id, html.escape(admin_user.first_name))}\n" - ) - - reply = f"<code>❕</code><b>Ban Event</b>\n" - - if CHAT_SENDER: - log += f"<b>Channel:</b> {html.escape(chat_name)}" - reply += f"<code> </code><b>• Channel:</b> {html.escape(chat_name)}" - - else: - log += f"<b>User:</b> {mention_html(member.user.id, html.escape(member.user.first_name))}" - reply += f"<code> </code><b>• User:</b> {mention_html(member.user.id, html.escape(member.user.first_name))}" - - if reason: - log += "\n<b>Reason:</b> {}".format(reason) - - try: - if CHAT_SENDER: - await chat.ban_sender_chat(sender_chat_id=user_id) - else: - await chat.ban_member(user_id) - - await bot.send_sticker( - chat.id, - BAN_STICKER, - message_thread_id=message.message_thread_id if chat.is_forum else None, - ) # banhammer marie sticker - - if reason: - reply += f"\n<code> </code><b>• Reason:</b> \n{html.escape(reason)}" - await bot.sendMessage( - chat.id, - reply, - parse_mode=ParseMode.HTML, - message_thread_id=message.message_thread_id if chat.is_forum else None, - ) - await query.answer(f"Done Banned User.") - return log - - except BadRequest as excp: - if excp.message == "Reply message not found": - # Do not reply - await message.edit_text("Banned!") - return log - else: - LOGGER.warning(update) - LOGGER.exception( - "ERROR banning user %s in chat %s (%s) due to %s", - user_id, - chat.title, - chat.id, - excp.message, - ) - await message.edit_text("Uhm...that didn't work...") - - return log_message - - elif splitter[1] == "unban": - try: - user_id = int(splitter[2]) - except ValueError: - user_id = splitter[2] - reason = splitter[3] - - if isinstance(user_id, str): - await message.edit_text("I doubt that's a user.") - return log_message - - if user_id == bot.id: - await message.edit_text("How would i unban myself if i wasn't here...?") - return log_message - - if user_id < 0: - CHAT_SENDER = True - chat_title = splitter[4] - else: - CHAT_SENDER = False - - try: - member = await chat.get_member(user_id) - except BadRequest as excp: - if excp.message != "User not found": - raise - await message.edit_text("I can't seem to find this user.") - return log_message - - if await is_user_in_chat(chat, user_id): - await message.edit_text("Isn't this person already here??") - return log_message - - log = ( - f"<b>{html.escape(chat.title)}:</b>\n" - f"#UNBANNED\n" - f"<b>Admin:</b> {mention_html(admin_user.id, html.escape(admin_user.first_name))}\n" - ) - - if CHAT_SENDER: - log += f"<b>User:</b> {html.escape(chat_title)}" - await chat.unban_sender_chat(user_id) - await message.reply_text("Yeah, this channel can speak again.") - else: - log += f"<b>User:</b> {mention_html(member.user.id, html.escape(member.user.first_name))}" - await chat.unban_member(user_id) - await message.reply_text("Yeah, this user can join!") - - if reason: - log += f"\n<b>Reason:</b> {reason}" - - return log - - -# <================================================ HELP =======================================================> - - -__help__ = """ -» /kickme: kicks the user who issued the command - -➠ *Admins only:* -» /ban <userhandle>: bans a user/channel. (via handle, or reply) - -» /sban <userhandle>: Silently ban a user. Deletes command, Replied message and doesn't reply. (via handle, or reply) - -» /tban <userhandle> x(m/h/d): bans a user for `x` time. (via handle, or reply). `m` = `minutes`, `h` = `hours`, `d` = `days`. - -» /unban <userhandle>: unbans a user/channel. (via handle, or reply) - -» /kick <userhandle>: kicks a user out of the group, (via handle, or reply) - -➠ NOTE: - Banning or UnBanning channels only work if you reply to their message, so don't use their username to ban/unban. -""" - -# <================================================ HANDLER =======================================================> -BAN_HANDLER = CommandHandler(["ban", "sban"], ban, block=False) -TEMPBAN_HANDLER = CommandHandler(["tban"], temp_ban, block=False) -KICK_HANDLER = CommandHandler("kick", kick, block=False) -UNBAN_HANDLER = CommandHandler("unban", unban, block=False) -ROAR_HANDLER = CommandHandler("roar", selfunban, block=False) -KICKME_HANDLER = DisableAbleCommandHandler( - "kickme", kickme, filters=filters.ChatType.GROUPS, block=False -) -BAN_CALLBACK_HANDLER = CallbackQueryHandler( - bans_callback, block=False, pattern=r"bans_" -) - -function(BAN_HANDLER) -function(TEMPBAN_HANDLER) -function(KICK_HANDLER) -function(UNBAN_HANDLER) -function(ROAR_HANDLER) -function(KICKME_HANDLER) -function(BAN_CALLBACK_HANDLER) - -__mod_name__ = "BAN" -__handlers__ = [ - BAN_HANDLER, - TEMPBAN_HANDLER, - KICK_HANDLER, - UNBAN_HANDLER, - ROAR_HANDLER, - KICKME_HANDLER, - BAN_CALLBACK_HANDLER, -] -# <================================================ END =======================================================> diff --git a/Mikobot/plugins/blacklist.py b/Mikobot/plugins/blacklist.py deleted file mode 100644 index 7157f334b3bdd84bae7e9fe135c44a680c04b022..0000000000000000000000000000000000000000 --- a/Mikobot/plugins/blacklist.py +++ /dev/null @@ -1,548 +0,0 @@ -# <============================================== IMPORTS =========================================================> -import html -import re - -from telegram import ChatPermissions, Update -from telegram.constants import ParseMode -from telegram.error import BadRequest -from telegram.ext import CommandHandler, ContextTypes, MessageHandler, filters -from telegram.helpers import mention_html - -import Database.sql.blacklist_sql as sql -from Database.sql.approve_sql import is_approved -from Mikobot import LOGGER, dispatcher, function -from Mikobot.plugins.connection import connected -from Mikobot.plugins.disable import DisableAbleCommandHandler -from Mikobot.plugins.helper_funcs.alternate import send_message, typing_action -from Mikobot.plugins.helper_funcs.chat_status import check_admin, user_not_admin -from Mikobot.plugins.helper_funcs.extraction import extract_text -from Mikobot.plugins.helper_funcs.misc import split_message -from Mikobot.plugins.helper_funcs.string_handling import extract_time -from Mikobot.plugins.log_channel import loggable -from Mikobot.plugins.warns import warn - -# <=======================================================================================================> - -BLACKLIST_GROUP = 11 - - -# <================================================ FUNCTION =======================================================> -@check_admin(is_user=True) -@typing_action -async def blacklist(update: Update, context: ContextTypes.DEFAULT_TYPE): - chat = update.effective_chat - user = update.effective_user - args = context.args - - conn = await connected(context.bot, update, chat, user.id, need_admin=False) - if conn: - chat_id = conn - chat_obj = await dispatcher.bot.getChat(conn) - chat_name = chat_obj.title - else: - if chat.type == "private": - return - chat_id = update.effective_chat.id - chat_name = chat.title - - filter_list = "Current blacklisted words in <b>{}</b>:\n".format(chat_name) - - all_blacklisted = sql.get_chat_blacklist(chat_id) - - if len(args) > 0 and args[0].lower() == "copy": - for trigger in all_blacklisted: - filter_list += "<code>{}</code>\n".format(html.escape(trigger)) - else: - for trigger in all_blacklisted: - filter_list += " - <code>{}</code>\n".format(html.escape(trigger)) - - # for trigger in all_blacklisted: - # filter_list += " - <code>{}</code>\n".format(html.escape(trigger)) - - split_text = split_message(filter_list) - for text in split_text: - if filter_list == "Current blacklisted words in <b>{}</b>:\n".format( - html.escape(chat_name), - ): - await send_message( - update.effective_message, - "No blacklisted words in <b>{}</b>!".format(html.escape(chat_name)), - parse_mode=ParseMode.HTML, - ) - return - await send_message(update.effective_message, text, parse_mode=ParseMode.HTML) - - -@check_admin(is_user=True) -@typing_action -async def add_blacklist(update: Update, context: ContextTypes.DEFAULT_TYPE): - msg = update.effective_message - chat = update.effective_chat - user = update.effective_user - words = msg.text.split(None, 1) - - conn = await connected(context.bot, update, chat, user.id) - if conn: - chat_id = conn - chat_obj = await dispatcher.bot.getChat(conn) - chat_name = chat_obj.title - else: - chat_id = update.effective_chat.id - if chat.type == "private": - return - else: - chat_name = chat.title - - if len(words) > 1: - text = words[1] - to_blacklist = list( - {trigger.strip() for trigger in text.split("\n") if trigger.strip()}, - ) - for trigger in to_blacklist: - sql.add_to_blacklist(chat_id, trigger.lower()) - - if len(to_blacklist) == 1: - await send_message( - update.effective_message, - "Added blacklist <code>{}</code> in chat: <b>{}</b>!".format( - html.escape(to_blacklist[0]), - html.escape(chat_name), - ), - parse_mode=ParseMode.HTML, - ) - - else: - await send_message( - update.effective_message, - "Added blacklist trigger: <code>{}</code> in <b>{}</b>!".format( - len(to_blacklist), - html.escape(chat_name), - ), - parse_mode=ParseMode.HTML, - ) - - else: - await send_message( - update.effective_message, - "Tell me which words you would like to add in blacklist.", - ) - - -@check_admin(is_user=True) -@typing_action -async def unblacklist(update: Update, context: ContextTypes.DEFAULT_TYPE): - msg = update.effective_message - chat = update.effective_chat - user = update.effective_user - words = msg.text.split(None, 1) - - conn = await connected(context.bot, update, chat, user.id) - if conn: - chat_id = conn - chat_obj = await dispatcher.bot.getChat(conn) - chat_name = chat_obj.title - else: - chat_id = update.effective_chat.id - if chat.type == "private": - return - else: - chat_name = chat.title - - if len(words) > 1: - text = words[1] - to_unblacklist = list( - {trigger.strip() for trigger in text.split("\n") if trigger.strip()}, - ) - successful = 0 - for trigger in to_unblacklist: - success = sql.rm_from_blacklist(chat_id, trigger.lower()) - if success: - successful += 1 - - if len(to_unblacklist) == 1: - if successful: - await send_message( - update.effective_message, - "Removed <code>{}</code> from blacklist in <b>{}</b>!".format( - html.escape(to_unblacklist[0]), - html.escape(chat_name), - ), - parse_mode=ParseMode.HTML, - ) - else: - await send_message( - update.effective_message, - "This is not a blacklist trigger!", - ) - - elif successful == len(to_unblacklist): - await send_message( - update.effective_message, - "Removed <code>{}</code> from blacklist in <b>{}</b>!".format( - successful, - html.escape(chat_name), - ), - parse_mode=ParseMode.HTML, - ) - - elif not successful: - await send_message( - update.effective_message, - "None of these triggers exist so it can't be removed.", - parse_mode=ParseMode.HTML, - ) - - else: - await send_message( - update.effective_message, - "Removed <code>{}</code> from blacklist. {} did not exist, " - "so were not removed.".format( - successful, - len(to_unblacklist) - successful, - ), - parse_mode=ParseMode.HTML, - ) - else: - await send_message( - update.effective_message, - "Tell me which words you would like to remove from blacklist!", - ) - - -@loggable -@check_admin(is_user=True) -@typing_action -async def blacklist_mode(update: Update, context: ContextTypes.DEFAULT_TYPE): - chat = update.effective_chat - user = update.effective_user - msg = update.effective_message - args = context.args - - conn = await connected(context.bot, update, chat, user.id, need_admin=True) - if conn: - chat = await dispatcher.bot.getChat(conn) - chat_id = conn - chat_obj = await dispatcher.bot.getChat(conn) - chat_name = chat_obj.title - else: - if update.effective_message.chat.type == "private": - await send_message( - update.effective_message, - "This command can be only used in group not in PM", - ) - return "" - chat = update.effective_chat - chat_id = update.effective_chat.id - chat_name = update.effective_message.chat.title - - if args: - if args[0].lower() in ["off", "nothing", "no"]: - settypeblacklist = "do nothing" - sql.set_blacklist_strength(chat_id, 0, "0") - elif args[0].lower() in ["del", "delete"]: - settypeblacklist = "delete blacklisted message" - sql.set_blacklist_strength(chat_id, 1, "0") - elif args[0].lower() == "warn": - settypeblacklist = "warn the sender" - sql.set_blacklist_strength(chat_id, 2, "0") - elif args[0].lower() == "mute": - settypeblacklist = "mute the sender" - sql.set_blacklist_strength(chat_id, 3, "0") - elif args[0].lower() == "kick": - settypeblacklist = "kick the sender" - sql.set_blacklist_strength(chat_id, 4, "0") - elif args[0].lower() == "ban": - settypeblacklist = "ban the sender" - sql.set_blacklist_strength(chat_id, 5, "0") - elif args[0].lower() == "tban": - if len(args) == 1: - teks = """It looks like you tried to set time value for blacklist but you didn't specified time; Try, `/blacklistmode tban <timevalue>`. - -Examples of time value: 4m = 4 minutes, 3h = 3 hours, 6d = 6 days, 5w = 5 weeks.""" - await send_message( - update.effective_message, teks, parse_mode="markdown" - ) - return "" - restime = await extract_time(msg, args[1]) - if not restime: - teks = """Invalid time value! -Example of time value: 4m = 4 minutes, 3h = 3 hours, 6d = 6 days, 5w = 5 weeks.""" - await send_message( - update.effective_message, teks, parse_mode="markdown" - ) - return "" - settypeblacklist = "temporarily ban for {}".format(args[1]) - sql.set_blacklist_strength(chat_id, 6, str(args[1])) - elif args[0].lower() == "tmute": - if len(args) == 1: - teks = """It looks like you tried to set time value for blacklist but you didn't specified time; try, `/blacklistmode tmute <timevalue>`. - -Examples of time value: 4m = 4 minutes, 3h = 3 hours, 6d = 6 days, 5w = 5 weeks.""" - await send_message( - update.effective_message, teks, parse_mode="markdown" - ) - return "" - restime = await extract_time(msg, args[1]) - if not restime: - teks = """Invalid time value! -Examples of time value: 4m = 4 minutes, 3h = 3 hours, 6d = 6 days, 5w = 5 weeks.""" - await send_message( - update.effective_message, teks, parse_mode="markdown" - ) - return "" - settypeblacklist = "temporarily mute for {}".format(args[1]) - sql.set_blacklist_strength(chat_id, 7, str(args[1])) - else: - await send_message( - update.effective_message, - "I only understand: off/del/warn/ban/kick/mute/tban/tmute!", - ) - return "" - if conn: - text = "Changed blacklist mode: `{}` in *{}*!".format( - settypeblacklist, - chat_name, - ) - else: - text = "Changed blacklist mode: `{}`!".format(settypeblacklist) - await send_message(update.effective_message, text, parse_mode="markdown") - return ( - "<b>{}:</b>\n" - "<b>Admin:</b> {}\n" - "Changed the blacklist mode. will {}.".format( - html.escape(chat.title), - mention_html(user.id, html.escape(user.first_name)), - settypeblacklist, - ) - ) - else: - getmode, getvalue = sql.get_blacklist_setting(chat.id) - if getmode == 0: - settypeblacklist = "do nothing" - elif getmode == 1: - settypeblacklist = "delete" - elif getmode == 2: - settypeblacklist = "warn" - elif getmode == 3: - settypeblacklist = "mute" - elif getmode == 4: - settypeblacklist = "kick" - elif getmode == 5: - settypeblacklist = "ban" - elif getmode == 6: - settypeblacklist = "temporarily ban for {}".format(getvalue) - elif getmode == 7: - settypeblacklist = "temporarily mute for {}".format(getvalue) - if conn: - text = "Current blacklistmode: *{}* in *{}*.".format( - settypeblacklist, - chat_name, - ) - else: - text = "Current blacklistmode: *{}*.".format(settypeblacklist) - await send_message( - update.effective_message, text, parse_mode=ParseMode.MARKDOWN - ) - return "" - - -def findall(p, s): - i = s.find(p) - while i != -1: - yield i - i = s.find(p, i + 1) - - -@user_not_admin -async def del_blacklist(update: Update, context: ContextTypes.DEFAULT_TYPE): - chat = update.effective_chat - message = update.effective_message - user = update.effective_user - bot = context.bot - to_match = await extract_text(message) - if not to_match: - return - if is_approved(chat.id, user.id): - return - getmode, value = sql.get_blacklist_setting(chat.id) - - chat_filters = sql.get_chat_blacklist(chat.id) - for trigger in chat_filters: - pattern = r"( |^|[^\w])" + re.escape(trigger) + r"( |$|[^\w])" - if re.search(pattern, to_match, flags=re.IGNORECASE): - try: - if getmode == 0: - return - elif getmode == 1: - try: - await message.delete() - except BadRequest: - pass - elif getmode == 2: - try: - await message.delete() - except BadRequest: - pass - warn( - update.effective_user, - chat, - ("Using blacklisted trigger: {}".format(trigger)), - message, - update.effective_user, - ) - return - elif getmode == 3: - await message.delete() - await bot.restrict_chat_member( - chat.id, - update.effective_user.id, - permissions=ChatPermissions(can_send_messages=False), - ) - await bot.sendMessage( - chat.id, - f"Muted {user.first_name} for using Blacklisted word: {trigger}!", - message_thread_id=message.message_thread_id - if chat.is_forum - else None, - ) - return - elif getmode == 4: - await message.delete() - res = chat.unban_member(update.effective_user.id) - if res: - await bot.sendMessage( - chat.id, - f"Kicked {user.first_name} for using Blacklisted word: {trigger}!", - message_thread_id=message.message_thread_id - if chat.is_forum - else None, - ) - return - elif getmode == 5: - await message.delete() - await chat.ban_member(user.id) - await bot.sendMessage( - chat.id, - f"Banned {user.first_name} for using Blacklisted word: {trigger}", - message_thread_id=message.message_thread_id - if chat.is_forum - else None, - ) - return - elif getmode == 6: - await message.delete() - bantime = await extract_time(message, value) - await chat.ban_member(user.id, until_date=bantime) - await bot.sendMessage( - chat.id, - f"Banned {user.first_name} until '{value}' for using Blacklisted word: {trigger}!", - message_thread_id=message.message_thread_id - if chat.is_forum - else None, - ) - return - elif getmode == 7: - await message.delete() - mutetime = await extract_time(message, value) - await bot.restrict_chat_member( - chat.id, - user.id, - until_date=mutetime, - permissions=ChatPermissions(can_send_messages=False), - ) - await bot.sendMessage( - chat.id, - f"Muted {user.first_name} until '{value}' for using Blacklisted word: {trigger}!", - message_thread_id=message.message_thread_id - if chat.is_forum - else None, - ) - return - except BadRequest as excp: - if excp.message != "Message to delete not found": - LOGGER.exception("Error while deleting blacklist message.") - break - - -async def __import_data__(chat_id, data, message): - # set chat blacklist - blacklist = data.get("blacklist", {}) - for trigger in blacklist: - sql.add_to_blacklist(chat_id, trigger) - - -def __migrate__(old_chat_id, new_chat_id): - sql.migrate_chat(old_chat_id, new_chat_id) - - -def __chat_settings__(chat_id, user_id): - blacklisted = sql.num_blacklist_chat_filters(chat_id) - return "There are {} blacklisted words.".format(blacklisted) - - -def __stats__(): - return "• {} blacklist triggers, across {} chats.".format( - sql.num_blacklist_filters(), - sql.num_blacklist_filter_chats(), - ) - - -# <=================================================== HELP ====================================================> - - -__mod_name__ = "BLACKLIST" - -__help__ = """ - -➠ Blacklists are used to stop certain triggers from being said in a group. Any time the trigger is mentioned, the message will immediately be deleted. A good combo is sometimes to pair this up with warn filters! - -➠ *NOTE*: Blacklists do not affect group admins. - -» /blacklist: View the current blacklisted words. - -Admin only: -» /addblacklist <triggers>: Add a trigger to the blacklist. Each line is considered one trigger, so using different lines will allow you to add multiple triggers. -» /unblacklist <triggers>: Remove triggers from the blacklist. Same newline logic applies here, so you can remove multiple triggers at once. -» /blacklistmode <off/del/warn/ban/kick/mute/tban/tmute>: Action to perform when someone sends blacklisted words. - -➠ Blacklist sticker is used to stop certain stickers. Whenever a sticker is sent, the message will be deleted immediately. -➠ *NOTE:* Blacklist stickers do not affect the group admin -» /blsticker: See current blacklisted sticker -➠ *Only admin:* -» /addblsticker <sticker link>: Add the sticker trigger to the black list. Can be added via reply sticker -» /unblsticker <sticker link>: Remove triggers from blacklist. The same newline logic applies here, so you can delete multiple triggers at once -» /rmblsticker <sticker link>: Same as above -» /blstickermode <delete/ban/tban/mute/tmute>: sets up a default action on what to do if users use blacklisted stickers -Note: -» <sticker link> can be https://t.me/addstickers/<sticker> or just <sticker> or reply to the sticker message - -""" - -# <================================================ HANDLER =======================================================> -BLACKLIST_HANDLER = DisableAbleCommandHandler( - "blacklist", blacklist, admin_ok=True, block=False -) -ADD_BLACKLIST_HANDLER = CommandHandler("addblacklist", add_blacklist, block=False) -UNBLACKLIST_HANDLER = CommandHandler("unblacklist", unblacklist, block=False) -BLACKLISTMODE_HANDLER = CommandHandler("blacklistmode", blacklist_mode, block=False) -BLACKLIST_DEL_HANDLER = MessageHandler( - (filters.TEXT | filters.COMMAND | filters.Sticker.ALL | filters.PHOTO) - & filters.ChatType.GROUPS, - del_blacklist, - block=False, -) - -function(BLACKLIST_HANDLER) -function(ADD_BLACKLIST_HANDLER) -function(UNBLACKLIST_HANDLER) -function(BLACKLISTMODE_HANDLER) -function(BLACKLIST_DEL_HANDLER, group=BLACKLIST_GROUP) - -__handlers__ = [ - BLACKLIST_HANDLER, - ADD_BLACKLIST_HANDLER, - UNBLACKLIST_HANDLER, - BLACKLISTMODE_HANDLER, - (BLACKLIST_DEL_HANDLER, BLACKLIST_GROUP), -] -# <================================================ END =======================================================> diff --git a/Mikobot/plugins/blacklist_stickers.py b/Mikobot/plugins/blacklist_stickers.py deleted file mode 100644 index 94971178579c6f6c39a51c1ac7130dcd6079d6fb..0000000000000000000000000000000000000000 --- a/Mikobot/plugins/blacklist_stickers.py +++ /dev/null @@ -1,581 +0,0 @@ -# <============================================== IMPORTS =========================================================> -import html - -from telegram import ChatPermissions, Update -from telegram.constants import ParseMode -from telegram.error import BadRequest -from telegram.ext import CommandHandler, ContextTypes, MessageHandler, filters -from telegram.helpers import mention_html, mention_markdown - -import Database.sql.blsticker_sql as sql -from Mikobot import LOGGER, dispatcher -from Mikobot.plugins.connection import connected -from Mikobot.plugins.disable import DisableAbleCommandHandler -from Mikobot.plugins.helper_funcs.alternate import send_message -from Mikobot.plugins.helper_funcs.chat_status import check_admin, user_not_admin -from Mikobot.plugins.helper_funcs.misc import split_message -from Mikobot.plugins.helper_funcs.string_handling import extract_time -from Mikobot.plugins.log_channel import loggable -from Mikobot.plugins.warns import warn - -# <=======================================================================================================> - - -# <================================================ FUNCTION =======================================================> -async def blackliststicker(update: Update, context: ContextTypes.DEFAULT_TYPE): - msg = update.effective_message # type: Optional[Message] - chat = update.effective_chat # type: Optional[Chat] - user = update.effective_user # type: Optional[User] - bot, args = context.bot, context.args - conn = await connected(bot, update, chat, user.id, need_admin=False) - if conn: - chat_id = conn - chat_obj = await dispatcher.bot.getChat(conn) - chat_name = chat_obj.title - else: - if chat.type == "private": - return - chat_id = update.effective_chat.id - chat_name = chat.title - - sticker_list = "<b>List blacklisted stickers currently in {}:</b>\n".format( - chat_name, - ) - - all_stickerlist = sql.get_chat_stickers(chat_id) - - if len(args) > 0 and args[0].lower() == "copy": - for trigger in all_stickerlist: - sticker_list += "<code>{}</code>\n".format(html.escape(trigger)) - elif len(args) == 0: - for trigger in all_stickerlist: - sticker_list += " - <code>{}</code>\n".format(html.escape(trigger)) - - split_text = split_message(sticker_list) - for text in split_text: - if sticker_list == "<b>List blacklisted stickers currently in {}:</b>\n".format( - chat_name, - ).format(html.escape(chat_name)): - await send_message( - update.effective_message, - "There are no blacklist stickers in <b>{}</b>!".format( - html.escape(chat_name), - ), - parse_mode=ParseMode.HTML, - ) - return - await send_message( - update.effective_message, - text, - parse_mode=ParseMode.HTML, - ) - - -@check_admin(is_user=True) -async def add_blackliststicker(update: Update, context: ContextTypes.DEFAULT_TYPE): - bot = context.bot - msg = update.effective_message # type: Optional[Message] - chat = update.effective_chat # type: Optional[Chat] - user = update.effective_user # type: Optional[User] - words = msg.text.split(None, 1) - bot = context.bot - conn = await connected(bot, update, chat, user.id) - if conn: - chat_id = conn - chat_obj = await dispatcher.bot.getChat(conn) - chat_name = chat_obj.title - else: - chat_id = update.effective_chat.id - if chat.type == "private": - return - else: - chat_name = chat.title - - if len(words) > 1: - text = words[1].replace("https://t.me/addstickers/", "") - to_blacklist = list( - {trigger.strip() for trigger in text.split("\n") if trigger.strip()}, - ) - - added = 0 - for trigger in to_blacklist: - try: - get = await bot.getStickerSet(trigger) - sql.add_to_stickers(chat_id, trigger.lower()) - added += 1 - except BadRequest: - await send_message( - update.effective_message, - "Sticker `{}` can not be found!".format(trigger), - parse_mode="markdown", - ) - - if added == 0: - return - - if len(to_blacklist) == 1: - await send_message( - update.effective_message, - "Sticker <code>{}</code> added to blacklist stickers in <b>{}</b>!".format( - html.escape(to_blacklist[0]), - html.escape(chat_name), - ), - parse_mode=ParseMode.HTML, - ) - else: - await send_message( - update.effective_message, - "<code>{}</code> stickers added to blacklist sticker in <b>{}</b>!".format( - added, - html.escape(chat_name), - ), - parse_mode=ParseMode.HTML, - ) - elif msg.reply_to_message: - added = 0 - trigger = msg.reply_to_message.sticker.set_name - if trigger is None: - await send_message( - update.effective_message, - "Sticker is invalid!", - ) - return - try: - get = await bot.getStickerSet(trigger) - sql.add_to_stickers(chat_id, trigger.lower()) - added += 1 - except BadRequest: - await send_message( - update.effective_message, - "Sticker `{}` can not be found!".format(trigger), - parse_mode="markdown", - ) - - if added == 0: - return - - await send_message( - update.effective_message, - "Sticker <code>{}</code> added to blacklist stickers in <b>{}</b>!".format( - trigger, - html.escape(chat_name), - ), - parse_mode=ParseMode.HTML, - ) - else: - await send_message( - update.effective_message, - "Tell me what stickers you want to add to the blacklist.", - ) - - -@check_admin(is_user=True) -async def unblackliststicker(update: Update, context: ContextTypes.DEFAULT_TYPE): - bot = context.bot - msg = update.effective_message # type: Optional[Message] - chat = update.effective_chat # type: Optional[Chat] - user = update.effective_user # type: Optional[User] - words = msg.text.split(None, 1) - bot = context.bot - conn = await connected(bot, update, chat, user.id) - if conn: - chat_id = conn - chat_obj = await dispatcher.bot.getChat(conn) - chat_name = chat_obj.title - else: - chat_id = update.effective_chat.id - if chat.type == "private": - return - else: - chat_name = chat.title - - if len(words) > 1: - text = words[1].replace("https://t.me/addstickers/", "") - to_unblacklist = list( - {trigger.strip() for trigger in text.split("\n") if trigger.strip()}, - ) - - successful = 0 - for trigger in to_unblacklist: - success = sql.rm_from_stickers(chat_id, trigger.lower()) - if success: - successful += 1 - - if len(to_unblacklist) == 1: - if successful: - await send_message( - update.effective_message, - "Sticker <code>{}</code> deleted from blacklist in <b>{}</b>!".format( - html.escape(to_unblacklist[0]), - html.escape(chat_name), - ), - parse_mode=ParseMode.HTML, - ) - else: - await send_message( - update.effective_message, - "This sticker is not on the blacklist...!", - ) - - elif successful == len(to_unblacklist): - await send_message( - update.effective_message, - "Sticker <code>{}</code> deleted from blacklist in <b>{}</b>!".format( - successful, - html.escape(chat_name), - ), - parse_mode=ParseMode.HTML, - ) - - elif not successful: - await send_message( - update.effective_message, - "None of these stickers exist, so they cannot be removed.", - parse_mode=ParseMode.HTML, - ) - - else: - await send_message( - update.effective_message, - "Sticker <code>{}</code> deleted from blacklist. {} did not exist, so it's not deleted.".format( - successful, - len(to_unblacklist) - successful, - ), - parse_mode=ParseMode.HTML, - ) - elif msg.reply_to_message: - trigger = msg.reply_to_message.sticker.set_name - if trigger is None: - await send_message( - update.effective_message, - "Sticker is invalid!", - ) - return - success = sql.rm_from_stickers(chat_id, trigger.lower()) - - if success: - await send_message( - update.effective_message, - "Sticker <code>{}</code> deleted from blacklist in <b>{}</b>!".format( - trigger, - chat_name, - ), - parse_mode=ParseMode.HTML, - ) - else: - await send_message( - update.effective_message, - "{} not found on blacklisted stickers...!".format(trigger), - ) - else: - await send_message( - update.effective_message, - "Tell me what stickers you want to add to the blacklist.", - ) - - -@loggable -@check_admin(is_user=True) -async def blacklist_mode(update: Update, context: ContextTypes.DEFAULT_TYPE): - chat = update.effective_chat # type: Optional[Chat] - user = update.effective_user # type: Optional[User] - msg = update.effective_message # type: Optional[Message] - bot, args = context.bot, context.args - conn = await connected(bot, update, chat, user.id, need_admin=True) - if conn: - chat = await dispatcher.bot.getChat(conn) - chat_id = conn - chat_obj = await dispatcher.bot.getChat(conn) - chat_name = chat_obj.title - else: - if update.effective_message.chat.type == "private": - await send_message( - update.effective_message, - "You can do this command in groups, not PM", - ) - return "" - chat = update.effective_chat - chat_id = update.effective_chat.id - chat_name = update.effective_message.chat.title - - if args: - if args[0].lower() in ["off", "nothing", "no"]: - settypeblacklist = "turn off" - sql.set_blacklist_strength(chat_id, 0, "0") - elif args[0].lower() in ["del", "delete"]: - settypeblacklist = "left, the message will be deleted" - sql.set_blacklist_strength(chat_id, 1, "0") - elif args[0].lower() == "warn": - settypeblacklist = "warned" - sql.set_blacklist_strength(chat_id, 2, "0") - elif args[0].lower() == "mute": - settypeblacklist = "muted" - sql.set_blacklist_strength(chat_id, 3, "0") - elif args[0].lower() == "kick": - settypeblacklist = "kicked" - sql.set_blacklist_strength(chat_id, 4, "0") - elif args[0].lower() == "ban": - settypeblacklist = "banned" - sql.set_blacklist_strength(chat_id, 5, "0") - elif args[0].lower() == "tban": - if len(args) == 1: - teks = """It looks like you are trying to set a temporary value to blacklist, but has not determined the time; use `/blstickermode tban <timevalue>`. - Examples of time values: 4m = 4 minute, 3h = 3 hours, 6d = 6 days, 5w = 5 weeks.""" - await send_message( - update.effective_message, - teks, - parse_mode="markdown", - ) - return - settypeblacklist = "temporary banned for {}".format(args[1]) - sql.set_blacklist_strength(chat_id, 6, str(args[1])) - elif args[0].lower() == "tmute": - if len(args) == 1: - teks = """It looks like you are trying to set a temporary value to blacklist, but has not determined the time; use `/blstickermode tmute <timevalue>`. - Examples of time values: 4m = 4 minute, 3h = 3 hours, 6d = 6 days, 5w = 5 weeks.""" - await send_message( - update.effective_message, - teks, - parse_mode="markdown", - ) - return - settypeblacklist = "temporary muted for {}".format(args[1]) - sql.set_blacklist_strength(chat_id, 7, str(args[1])) - else: - await send_message( - update.effective_message, - "I only understand off/del/warn/ban/kick/mute/tban/tmute!", - ) - return - if conn: - text = "Blacklist sticker mode changed, users will be `{}` at *{}*!".format( - settypeblacklist, - chat_name, - ) - else: - text = "Blacklist sticker mode changed, users will be `{}`!".format( - settypeblacklist, - ) - await send_message( - update.effective_message, - text, - parse_mode="markdown", - ) - return ( - "<b>{}:</b>\n" - "<b>Admin:</b> {}\n" - "Changed sticker blacklist mode. users will be {}.".format( - html.escape(chat.title), - mention_html(user.id, html.escape(user.first_name)), - settypeblacklist, - ) - ) - else: - getmode, getvalue = sql.get_blacklist_setting(chat.id) - if getmode == 0: - settypeblacklist = "not active" - elif getmode == 1: - settypeblacklist = "delete" - elif getmode == 2: - settypeblacklist = "warn" - elif getmode == 3: - settypeblacklist = "mute" - elif getmode == 4: - settypeblacklist = "kick" - elif getmode == 5: - settypeblacklist = "ban" - elif getmode == 6: - settypeblacklist = "temporarily banned for {}".format(getvalue) - elif getmode == 7: - settypeblacklist = "temporarily muted for {}".format(getvalue) - if conn: - text = "Blacklist sticker mode is currently set to *{}* in *{}*.".format( - settypeblacklist, - chat_name, - ) - else: - text = "Blacklist sticker mode is currently set to *{}*.".format( - settypeblacklist, - ) - await send_message( - update.effective_message, - text, - parse_mode=ParseMode.MARKDOWN, - ) - return "" - - -@user_not_admin -async def del_blackliststicker(update: Update, context: ContextTypes.DEFAULT_TYPE): - bot = context.bot - chat = update.effective_chat # type: Optional[Chat] - message = update.effective_message # type: Optional[Message] - user = update.effective_user - to_match = message.sticker - if not to_match or not to_match.set_name: - return - bot = context.bot - getmode, value = sql.get_blacklist_setting(chat.id) - - chat_filters = sql.get_chat_stickers(chat.id) - for trigger in chat_filters: - if to_match.set_name.lower() == trigger.lower(): - try: - if getmode == 0: - return - elif getmode == 1: - await message.delete() - elif getmode == 2: - await message.delete() - warn( - update.effective_user, - chat, - "Using sticker '{}' which in blacklist stickers".format( - trigger, - ), - message, - update.effective_user, - # conn=False, - ) - return - elif getmode == 3: - await message.delete() - await bot.restrict_chat_member( - chat.id, - update.effective_user.id, - permissions=ChatPermissions(can_send_messages=False), - ) - await bot.sendMessage( - chat.id, - "{} muted because using '{}' which in blacklist stickers".format( - mention_markdown(user.id, user.first_name), - trigger, - ), - parse_mode="markdown", - message_thread_id=message.message_thread_id - if chat.is_forum - else None, - ) - return - elif getmode == 4: - await message.delete() - res = chat.unban_member(update.effective_user.id) - if res: - await bot.sendMessage( - chat.id, - "{} kicked because using '{}' which in blacklist stickers".format( - mention_markdown(user.id, user.first_name), - trigger, - ), - parse_mode="markdown", - message_thread_id=message.message_thread_id - if chat.is_forum - else None, - ) - return - elif getmode == 5: - await message.delete() - await chat.ban_member(user.id) - await bot.sendMessage( - chat.id, - "{} banned because using '{}' which in blacklist stickers".format( - mention_markdown(user.id, user.first_name), - trigger, - ), - parse_mode="markdown", - message_thread_id=message.message_thread_id - if chat.is_forum - else None, - ) - return - elif getmode == 6: - await message.delete() - bantime = await extract_time(message, value) - await chat.ban_member(user.id, until_date=bantime) - await bot.sendMessage( - chat.id, - "{} banned for {} because using '{}' which in blacklist stickers".format( - mention_markdown(user.id, user.first_name), - value, - trigger, - ), - parse_mode="markdown", - message_thread_id=message.message_thread_id - if chat.is_forum - else None, - ) - return - elif getmode == 7: - await message.delete() - mutetime = await extract_time(message, value) - await bot.restrict_chat_member( - chat.id, - user.id, - permissions=ChatPermissions(can_send_messages=False), - until_date=mutetime, - ) - await bot.sendMessage( - chat.id, - "{} muted for {} because using '{}' which in blacklist stickers".format( - mention_markdown(user.id, user.first_name), - value, - trigger, - ), - parse_mode="markdown", - message_thread_id=message.message_thread_id - if chat.is_forum - else None, - ) - return - except BadRequest as excp: - if excp.message != "Message to delete not found": - LOGGER.exception("Error while deleting blacklist message.") - break - - -async def __import_data__(chat_id, data, message): - # set chat blacklist - blacklist = data.get("sticker_blacklist", {}) - for trigger in blacklist: - sql.add_to_stickers(chat_id, trigger) - - -def __migrate__(old_chat_id, new_chat_id): - sql.migrate_chat(old_chat_id, new_chat_id) - - -def __chat_settings__(chat_id, user_id): - blacklisted = sql.num_stickers_chat_filters(chat_id) - return "There are `{} `blacklisted stickers.".format(blacklisted) - - -def __stats__(): - return "• {} blacklist stickers, across {} chats.".format( - sql.num_stickers_filters(), - sql.num_stickers_filter_chats(), - ) - - -__mod_name__ = "Stickers Blacklist" - -# <================================================ HANDLER =======================================================> -BLACKLIST_STICKER_HANDLER = DisableAbleCommandHandler( - "blsticker", blackliststicker, admin_ok=True, block=False -) -ADDBLACKLIST_STICKER_HANDLER = DisableAbleCommandHandler( - "addblsticker", add_blackliststicker, block=False -) -UNBLACKLIST_STICKER_HANDLER = CommandHandler( - ["unblsticker", "rmblsticker"], unblackliststicker, block=False -) -BLACKLISTMODE_HANDLER = CommandHandler("blstickermode", blacklist_mode, block=False) -BLACKLIST_STICKER_DEL_HANDLER = MessageHandler( - filters.Sticker.ALL & filters.ChatType.GROUPS, del_blackliststicker, block=False -) - -dispatcher.add_handler(BLACKLIST_STICKER_HANDLER) -dispatcher.add_handler(ADDBLACKLIST_STICKER_HANDLER) -dispatcher.add_handler(UNBLACKLIST_STICKER_HANDLER) -dispatcher.add_handler(BLACKLISTMODE_HANDLER) -dispatcher.add_handler(BLACKLIST_STICKER_DEL_HANDLER) -# <================================================ END =======================================================> diff --git a/Mikobot/plugins/botadmins.py b/Mikobot/plugins/botadmins.py deleted file mode 100644 index 50ebb85326e2e672b0f92ca5fb08032863ba61b0..0000000000000000000000000000000000000000 --- a/Mikobot/plugins/botadmins.py +++ /dev/null @@ -1,108 +0,0 @@ -# SOURCE https://github.com/Team-ProjectCodeX -# CREATED BY https://t.me/O_okarma -# PROVIDED BY https://t.me/ProjectCodeX - -# <============================================== IMPORTS =========================================================> - -from telegram import Update -from telegram.constants import ParseMode -from telegram.error import TelegramError -from telegram.ext import CommandHandler, ContextTypes - -from Mikobot import DEMONS, DEV_USERS, DRAGONS, LOGGER, OWNER_ID, WOLVES, function -from Mikobot.plugins.helper_funcs.chat_status import support_plus -from Mikobot.utils.parser import mention_html - -# <=======================================================================================================> - - -# <================================================ FUNCTION =======================================================> -async def get_chat_member(context: ContextTypes.DEFAULT_TYPE, user_id): - try: - return await context.bot.get_chat_member(user_id, user_id) - except TelegramError as e: - LOGGER.error(f"Error getting chat member {user_id}: {e}") - return None - - -async def get_user_info(context: ContextTypes.DEFAULT_TYPE, user_id): - user_info = await get_chat_member(context, user_id) - return user_info.user.first_name if user_info else "Unknown User" - - -async def get_users_info(context: ContextTypes.DEFAULT_TYPE, user_ids): - return [(await get_user_info(context, user_id), user_id) for user_id in user_ids] - - -async def get_users_list(context: ContextTypes.DEFAULT_TYPE, user_ids): - return [ - f"• {await mention_html(name, user_id)} (<code>{user_id}</code>)" - for name, user_id in await get_users_info(context, user_ids) - ] - - -@support_plus -async def botstaff(update: Update, context: ContextTypes.DEFAULT_TYPE): - try: - owner = await get_chat_member(context, OWNER_ID) - owner_info = await mention_html(owner.user.first_name, owner.user.id) - reply = f"✪ <b>CREATOR :</b> {owner_info} (<code>{OWNER_ID}</code>)\n" - except TelegramError as e: - LOGGER.error(f"Error getting owner information: {e}") - reply = "" - - true_dev = list(set(DEV_USERS) - {OWNER_ID}) - reply += "\n\n➪ <b>SPECIAL GRADE USERS :</b>\n" - reply += "\n".join(await get_users_list(context, true_dev)) or "No Dev Users" - - true_sudo = list(set(DRAGONS) - set(DEV_USERS)) - reply += "\n\n➪ <b>A GRADE USERS :</b>\n" - reply += "\n".join(await get_users_list(context, true_sudo)) or "No Sudo Users" - - reply += "\n\n➪ <b>B GRADE USERS :</b>\n" - reply += "\n".join(await get_users_list(context, DEMONS)) or "No Demon Users" - - reply += "\n\n➪ <b>NORMAL GRADE USERS :</b>\n" - reply += ( - "\n".join(await get_users_list(context, WOLVES)) - or "No additional whitelisted users" - ) - - await update.message.reply_text(reply, parse_mode=ParseMode.HTML) - LOGGER.info( - f"{update.message.from_user.id} fetched botstaff in {update.message.chat.id}" - ) - - -# <================================================ HANDLER =======================================================> -function(CommandHandler("botadmins", botstaff, block=False)) -# <================================================ END =======================================================> - - -# <=================================================== HELP ====================================================> -__help__ = """ -➠ *BOT ADMINS ONLY:* - -» /stats: Shows bot stats. - -» /ping: see ping. - -» /gban: Global ban. - -» /gbanlist: Shows gban list. - -» /botadmins: Opens Bot admin lists. - -» /gcast: Advance broadcast system. Just reply to any message. - -➠ *Write with text message* - -» /broadcastall - -» /broadcastusers - -» /broadcastgroups -""" - -__mod_name__ = "BOT-ADMIN" -# <================================================ HANDLER =======================================================> diff --git a/Mikobot/plugins/chatbot.py b/Mikobot/plugins/chatbot.py deleted file mode 100644 index 1039a7c39debfda5d5b59656a0309ce435164519..0000000000000000000000000000000000000000 --- a/Mikobot/plugins/chatbot.py +++ /dev/null @@ -1,188 +0,0 @@ -# <============================================== IMPORTS =========================================================> -import asyncio -import html -import json -import re -from typing import Optional - -import requests -from telegram import ( - CallbackQuery, - Chat, - InlineKeyboardButton, - InlineKeyboardMarkup, - Update, - User, -) -from telegram.constants import ParseMode -from telegram.error import BadRequest, Forbidden, RetryAfter -from telegram.ext import ( - CallbackQueryHandler, - CommandHandler, - ContextTypes, - MessageHandler, - filters, -) -from telegram.helpers import mention_html - -import Database.sql.kuki_sql as sql -from Mikobot import function -from Mikobot.plugins.log_channel import gloggable - -# <=======================================================================================================> - - -# <================================================ FUNCTION =======================================================> -@gloggable -async def kukirm(update: Update, context: ContextTypes.DEFAULT_TYPE): - query: Optional[CallbackQuery] = update.callback_query - user: Optional[User] = update.effective_user - if match := re.match(r"rm_chat\((.+?)\)", query.data): - user_id = match[1] - chat: Optional[Chat] = update.effective_chat - if is_kuki := sql.rem_kuki(chat.id): - sql.rem_kuki(user_id) - return ( - f"<b>{html.escape(chat.title)}:</b>\n" - f"AI_DISABLED\n" - f"<b>Admin:</b> {mention_html(user.id, html.escape(user.first_name))}\n" - ) - else: - await update.effective_message.edit_text( - f"Chatbot disable by {mention_html(user.id, user.first_name)}.", - parse_mode=ParseMode.HTML, - ) - - return "" - - -@gloggable -async def kukiadd(update: Update, context: ContextTypes.DEFAULT_TYPE): - query: Optional[CallbackQuery] = update.callback_query - user: Optional[User] = update.effective_user - if match := re.match(r"add_chat\((.+?)\)", query.data): - user_id = match[1] - chat: Optional[Chat] = update.effective_chat - if is_kuki := sql.set_kuki(chat.id): - sql.set_kuki(user_id) - return ( - f"<b>{html.escape(chat.title)}:</b>\n" - f"AI_ENABLE\n" - f"<b>Admin:</b> {mention_html(user.id, html.escape(user.first_name))}\n" - ) - else: - await update.effective_message.edit_text( - f"Hey Darling Chatbot enable by {mention_html(user.id, user.first_name)}.", - parse_mode=ParseMode.HTML, - ) - - return "" - - -@gloggable -async def kuki(update: Update, context: ContextTypes.DEFAULT_TYPE): - update.effective_user - message = update.effective_message - msg = "Choose an option" - keyboard = InlineKeyboardMarkup( - [ - [InlineKeyboardButton(text="Enable", callback_data="add_chat({})")], - [InlineKeyboardButton(text="Disable", callback_data="rm_chat({})")], - ] - ) - await message.reply_text( - msg, - reply_markup=keyboard, - parse_mode=ParseMode.HTML, - ) - - -async def kuki_message(context: ContextTypes.DEFAULT_TYPE, message): - reply_message = message.reply_to_message - if message.text.lower() == "kuki": - return True - if reply_message: - if reply_message.from_user.id == (await context.bot.get_me()).id: - return True - else: - return False - - -async def chatbot(update: Update, context: ContextTypes.DEFAULT_TYPE): - update.effective_user - message = update.effective_message - chat_id = update.effective_chat.id - bot = context.bot - is_kuki = sql.is_kuki(chat_id) - if not is_kuki: - return - - if message.text and not message.document: - if not await kuki_message(context, message): - return - Message = message.text - await bot.send_chat_action(chat_id, action="typing") - kukiurl = requests.get( - f"http://api.brainshop.ai/get?bid=176809&key=lbMN8CXTGzhn1NKG&uid=[user]&msg={Message}" - ) - - Kuki = json.loads(kukiurl.text) - kuki = Kuki["cnt"] - - await asyncio.sleep(0.3) - await message.reply_text(kuki) - - -async def list_all_chats(update: Update, context: ContextTypes.DEFAULT_TYPE): - chats = sql.get_all_kuki_chats() - text = "<b>Neko Enabled Chats</b>\n" - for chat in chats: - try: - x = await context.bot.get_chat(int(*chat)) - name = x.title or x.first_name - text += f"• <code>{name}</code>\n" - except (BadRequest, Forbidden): - sql.rem_kuki(*chat) - except RetryAfter as e: - await asyncio.sleep(e.retry_after) - await update.effective_message.reply_text(text, parse_mode="HTML") - - -# <=================================================== HELP ====================================================> - - -__help__ = """ -➠ *Admins only command*: - -» /chatbot: shows chatbot panel. -""" - -__mod_name__ = "CHATBOT" - - -# <================================================ HANDLER =======================================================> -CHATBOTK_HANDLER = CommandHandler("chatbot", kuki, block=False) -ADD_CHAT_HANDLER = CallbackQueryHandler(kukiadd, pattern=r"add_chat", block=False) -RM_CHAT_HANDLER = CallbackQueryHandler(kukirm, pattern=r"rm_chat", block=False) -CHATBOT_HANDLER = MessageHandler( - filters.TEXT - & (~filters.Regex(r"^#[^\s]+") & ~filters.Regex(r"^!") & ~filters.Regex(r"^\/")), - chatbot, - block=False, -) -LIST_ALL_CHATS_HANDLER = CommandHandler("allchats", list_all_chats, block=False) - -function(ADD_CHAT_HANDLER) -function(CHATBOTK_HANDLER) -function(RM_CHAT_HANDLER) -function(LIST_ALL_CHATS_HANDLER) -function(CHATBOT_HANDLER) - -__handlers__ = [ - ADD_CHAT_HANDLER, - CHATBOTK_HANDLER, - RM_CHAT_HANDLER, - LIST_ALL_CHATS_HANDLER, - CHATBOT_HANDLER, -] -# <================================================ END =======================================================> diff --git a/Mikobot/plugins/connection.py b/Mikobot/plugins/connection.py deleted file mode 100644 index f3f0d938619330603c2241b95a91d231133716aa..0000000000000000000000000000000000000000 --- a/Mikobot/plugins/connection.py +++ /dev/null @@ -1,441 +0,0 @@ -# <============================================== IMPORTS =========================================================> -import re -import time - -from telegram import Bot, InlineKeyboardButton, InlineKeyboardMarkup, Update -from telegram.constants import ParseMode -from telegram.error import BadRequest, Forbidden -from telegram.ext import CallbackQueryHandler, CommandHandler, ContextTypes - -import Database.sql.connection_sql as sql -from Mikobot import DEV_USERS, DRAGONS, dispatcher, function -from Mikobot.plugins.helper_funcs import chat_status -from Mikobot.plugins.helper_funcs.alternate import send_message, typing_action - -# <=======================================================================================================> - -check_admin = chat_status.check_admin - - -# <================================================ FUNCTION =======================================================> -@check_admin(is_user=True) -@typing_action -async def allow_connections(update: Update, context: ContextTypes.DEFAULT_TYPE): - chat = update.effective_chat - args = context.args - - if chat.type != chat.PRIVATE: - if len(args) >= 1: - var = args[0] - if var == "no": - sql.set_allow_connect_to_chat(chat.id, False) - await send_message( - update.effective_message, - "Connection has been disabled for this chat.", - ) - elif var == "yes": - sql.set_allow_connect_to_chat(chat.id, True) - await send_message( - update.effective_message, - "Connection has been enabled for this chat.", - ) - else: - await send_message( - update.effective_message, - "Please enter 'yes' or 'no'!", - parse_mode=ParseMode.MARKDOWN, - ) - else: - get_settings = sql.allow_connect_to_chat(chat.id) - if get_settings: - await send_message( - update.effective_message, - "Connections to this group are *allowed* for members!", - parse_mode=ParseMode.MARKDOWN, - ) - else: - await send_message( - update.effective_message, - "Connection to this group is *not allowed* for members!", - parse_mode=ParseMode.MARKDOWN, - ) - else: - await send_message( - update.effective_message, - "This command is for groups only, not in PM!", - ) - - -@typing_action -async def connection_chat(update: Update, context: ContextTypes.DEFAULT_TYPE): - chat = update.effective_chat - user = update.effective_user - - conn = await connected(context.bot, update, chat, user.id, need_admin=True) - - if conn: - chat = await dispatcher.bot.getChat(conn) - chat_obj = await dispatcher.bot.getChat(conn) - chat_name = chat_obj.title - else: - if update.effective_message.chat.type != "private": - return - chat = update.effective_chat - chat_name = update.effective_message.chat.title - - if conn: - message = "You are currently connected to {}.\n".format(chat_name) - else: - message = "You are currently not connected in any group.\n" - await send_message(update.effective_message, message, parse_mode="markdown") - - -@typing_action -async def connect_chat(update: Update, context: ContextTypes.DEFAULT_TYPE): - chat = update.effective_chat - user = update.effective_user - args = context.args - - if update.effective_chat.type == "private": - if args and len(args) >= 1: - try: - connect_chat = int(args[0]) - getstatusadmin = await context.bot.get_chat_member( - connect_chat, - update.effective_message.from_user.id, - ) - except ValueError: - try: - connect_chat = str(args[0]) - get_chat = await context.bot.getChat(connect_chat) - connect_chat = get_chat.id - getstatusadmin = await context.bot.get_chat_member( - connect_chat, - update.effective_message.from_user.id, - ) - except BadRequest: - await send_message(update.effective_message, "Invalid Chat ID!") - return - except BadRequest: - await send_message(update.effective_message, "Invalid Chat ID!") - return - - isadmin = getstatusadmin.status in ("administrator", "creator") - ismember = getstatusadmin.status in ("member") - isallow = sql.allow_connect_to_chat(connect_chat) - - if (isadmin) or (isallow and ismember) or (user.id in DRAGONS): - connection_status = sql.connect( - update.effective_message.from_user.id, - connect_chat, - ) - if connection_status: - conn = await connected( - context.bot, update, chat, user.id, need_admin=False - ) - conn_chat = await dispatcher.bot.getChat(conn) - chat_name = conn_chat.title - await send_message( - update.effective_message, - "Successfully connected to *{}*. Use /helpconnect to check available commands.".format( - chat_name, - ), - parse_mode=ParseMode.MARKDOWN, - ) - sql.add_history_conn(user.id, str(conn_chat.id), chat_name) - else: - await send_message(update.effective_message, "Connection failed!") - else: - await send_message( - update.effective_message, - "Connection to this chat is not allowed!", - ) - else: - gethistory = sql.get_history_conn(user.id) - if gethistory: - buttons = [ - InlineKeyboardButton( - text="❎ Close Button", - callback_data="connect_close", - ), - InlineKeyboardButton( - text="🧹 Clear History", - callback_data="connect_clear", - ), - ] - else: - buttons = [] - conn = await connected(context.bot, update, chat, user.id, need_admin=False) - if conn: - connectedchat = await dispatcher.bot.getChat(conn) - text = "You are currently connected to *{}* (`{}`)".format( - connectedchat.title, - conn, - ) - buttons.append( - InlineKeyboardButton( - text="🔌 Disconnect", - callback_data="connect_disconnect", - ), - ) - else: - text = "Write the chat ID or tag to connect!" - if gethistory: - text += "\n\n*Connection History:*\n" - text += "╒═══「 *Info* 」\n" - text += "│ Sorted: `Newest`\n" - text += "│\n" - buttons = [buttons] - for x in sorted(gethistory.keys(), reverse=True): - htime = time.strftime("%d/%m/%Y", time.localtime(x)) - text += "╞═「 *{}* 」\n│ `{}`\n│ `{}`\n".format( - gethistory[x]["chat_name"], - gethistory[x]["chat_id"], - htime, - ) - text += "│\n" - buttons.append( - [ - InlineKeyboardButton( - text=gethistory[x]["chat_name"], - callback_data="connect({})".format( - gethistory[x]["chat_id"], - ), - ), - ], - ) - text += "╘══「 Total {} Chats 」".format( - str(len(gethistory)) + " (max)" - if len(gethistory) == 5 - else str(len(gethistory)), - ) - conn_hist = InlineKeyboardMarkup(buttons) - elif buttons: - conn_hist = InlineKeyboardMarkup([buttons]) - else: - conn_hist = None - await send_message( - update.effective_message, - text, - parse_mode="markdown", - reply_markup=conn_hist, - ) - - else: - getstatusadmin = await context.bot.get_chat_member( - chat.id, - update.effective_message.from_user.id, - ) - isadmin = getstatusadmin.status in ("administrator", "creator") - ismember = getstatusadmin.status in ("member") - isallow = sql.allow_connect_to_chat(chat.id) - if (isadmin) or (isallow and ismember) or (user.id in DRAGONS): - connection_status = sql.connect( - update.effective_message.from_user.id, - chat.id, - ) - if connection_status: - chat_obj = await dispatcher.bot.getChat(chat.id) - chat_name = chat_obj.title - await send_message( - update.effective_message, - "Successfully connected to *{}*.".format(chat_name), - parse_mode=ParseMode.MARKDOWN, - ) - try: - sql.add_history_conn(user.id, str(chat.id), chat_name) - await context.bot.send_message( - update.effective_message.from_user.id, - "You are connected to *{}*. \nUse `/helpconnect` to check available commands.".format( - chat_name, - ), - parse_mode="markdown", - ) - except BadRequest: - pass - except Forbidden: - pass - else: - await send_message(update.effective_message, "ᴄᴏɴɴᴇᴄᴛɪᴏɴ ғᴀɪʟᴇᴅ!") - else: - await send_message( - update.effective_message, - "Connection to this chat is not allowed!", - ) - - -async def disconnect_chat(update: Update, context: ContextTypes.DEFAULT_TYPE): - if update.effective_chat.type == "private": - disconnection_status = sql.disconnect(update.effective_message.from_user.id) - if disconnection_status: - sql.disconnected_chat = await send_message( - update.effective_message, - "Disconnected from chat!", - ) - else: - await send_message(update.effective_message, "You're not connected!") - else: - await send_message( - update.effective_message, "This command is only available in PM." - ) - - -async def connected(bot: Bot, update: Update, chat, user_id, need_admin=True): - user = update.effective_user - - if chat.type == chat.PRIVATE and sql.get_connected_chat(user_id): - conn_id = sql.get_connected_chat(user_id).chat_id - getstatusadmin = await bot.get_chat_member( - conn_id, - update.effective_message.from_user.id, - ) - isadmin = getstatusadmin.status in ("administrator", "creator") - ismember = getstatusadmin.status in ("member") - isallow = sql.allow_connect_to_chat(conn_id) - - if ( - (isadmin) - or (isallow and ismember) - or (user.id in DRAGONS) - or (user.id in DEV_USERS) - ): - if need_admin is True: - if ( - getstatusadmin.status in ("administrator", "creator") - or user_id in DRAGONS - or user.id in DEV_USERS - ): - return conn_id - else: - await send_message( - update.effective_message, - "You must be an admin in the connected group!", - ) - else: - return conn_id - else: - await send_message( - update.effective_message, - "The group changed the connection rights or you are no longer an admin.\nI've disconnected you.", - ) - disconnect_chat(update, bot) - else: - return False - - -CONN_HELP = """ -Actions are available with connected groups: - • View and edit Notes. - • View and edit Filters. - • Get invite link of chat. - • Set and control AntiFlood settings. - • Set and control Blacklist settings. - • Set Locks and Unlocks in chat. - • Enable and Disable commands in chat. - • Export and Imports of chat backup. - """ - - -async def help_connect_chat(update: Update, context: ContextTypes.DEFAULT_TYPE): - context.args - - if update.effective_message.chat.type != "private": - await send_message( - update.effective_message, "PM me with that command to get help." - ) - return - else: - await send_message(update.effective_message, CONN_HELP, parse_mode="markdown") - - -async def connect_button(update: Update, context: ContextTypes.DEFAULT_TYPE): - query = update.callback_query - chat = update.effective_chat - user = update.effective_user - - connect_match = re.match(r"connect\((.+?)\)", query.data) - disconnect_match = query.data == "connect_disconnect" - clear_match = query.data == "connect_clear" - connect_close = query.data == "connect_close" - - if connect_match: - target_chat = connect_match.group(1) - getstatusadmin = await context.bot.get_chat_member( - target_chat, query.from_user.id - ) - isadmin = getstatusadmin.status in ("administrator", "creator") - ismember = getstatusadmin.status in ("member") - isallow = sql.allow_connect_to_chat(target_chat) - - if (isadmin) or (isallow and ismember) or (user.id in DRAGONS): - connection_status = sql.connect(query.from_user.id, target_chat) - - if connection_status: - conn = await connected( - context.bot, update, chat, user.id, need_admin=False - ) - conn_chat = await dispatcher.bot.getChat(conn) - chat_name = conn_chat.title - await query.message.edit_text( - "Successfully connected to *{}*. Use `/helpconnect` to check available commands.".format( - chat_name, - ), - parse_mode=ParseMode.MARKDOWN, - ) - sql.add_history_conn(user.id, str(conn_chat.id), chat_name) - else: - await query.message.edit_text("Connection failed!") - else: - await context.bot.answer_callback_query( - query.id, - "Connection to this chat is not allowed!", - show_alert=True, - ) - elif disconnect_match: - disconnection_status = sql.disconnect(query.from_user.id) - if disconnection_status: - sql.disconnected_chat = await query.message.edit_text( - "Disconnected from chat!" - ) - else: - await context.bot.answer_callback_query( - query.id, - "You're not connected!", - show_alert=True, - ) - elif clear_match: - sql.clear_history_conn(query.from_user.id) - await query.message.edit_text("History connected has been cleared!") - elif connect_close: - await query.message.edit_text("Closed. To open again, type /connect") - else: - connect_chat(update, context) - - -# <=================================================== HELP ====================================================> -__mod_name__ = "CONNECT" - -__help__ = """ -➠ *Sometimes, you just want to add some notes and filters to a group chat, but you don't want everyone to see; this is where connections come in. This allows you to connect to a chat's database and add things to it without the commands appearing in chat! For obvious reasons, you need to be an admin to add things, but any member in the group can view your data.* - -» /connect: Connects to chat (can be done in a group by /connect or /connect <chat id> in PM) - -» /connection: List connected chats - -» /disconnect: Disconnect from a chat - -» /helpconnect: List available commands that can be used remotely - -➠ *Admin Only:* - -» /allowconnect <yes/no>: Allow a user to connect to a chat -""" - -# <================================================ HANDLER =======================================================> -function(CommandHandler("connect", connect_chat, block=False)) -function(CommandHandler("connection", connection_chat, block=False)) -function(CommandHandler("disconnect", disconnect_chat, block=False)) -function(CommandHandler("allowconnect", allow_connections, block=False)) -function(CommandHandler("helpconnect", help_connect_chat, block=False)) -function(CallbackQueryHandler(connect_button, pattern=r"connect", block=False)) -# <================================================ END =======================================================> diff --git a/Mikobot/plugins/cosplay.py b/Mikobot/plugins/cosplay.py deleted file mode 100644 index 37c6f9927bf319cb4c3116071082ddb453a097d9..0000000000000000000000000000000000000000 --- a/Mikobot/plugins/cosplay.py +++ /dev/null @@ -1,37 +0,0 @@ -# SOURCE https://github.com/Team-ProjectCodeX -# CREATED BY https://t.me/O_okarma -# API BY https://www.github.com/SOME-1HING -# PROVIDED BY https://t.me/ProjectCodeX - -# <============================================== IMPORTS =========================================================> -from telegram import Update -from telegram.ext import CommandHandler, ContextTypes - -from Mikobot import function -from Mikobot.state import state - -# <=======================================================================================================> - - -# <================================================ FUNCTIONS =====================================================> -async def get_cosplay_data(): - cosplay_url = "https://sugoi-api.vercel.app/cosplay" - response = await state.get(cosplay_url) - return response.json() - - -async def cosplay(update: Update, context: ContextTypes.DEFAULT_TYPE): - try: - data = await get_cosplay_data() - photo_url = data.get("url") # Corrected key: "url" instead of "cosplay_url" - if photo_url: - await update.message.reply_photo(photo=photo_url) - else: - await update.message.reply_text("Could not fetch photo URL.") - except state.FetchError: - await update.message.reply_text("Unable to fetch data.") - - -# <================================================ HANDLER =======================================================> -function(CommandHandler("cosplay", cosplay, block=False)) -# <================================================ END =======================================================> diff --git a/Mikobot/plugins/couple.py b/Mikobot/plugins/couple.py deleted file mode 100644 index 93eafcd2ff15f50c42eadf421887c2f37fb8f763..0000000000000000000000000000000000000000 --- a/Mikobot/plugins/couple.py +++ /dev/null @@ -1,124 +0,0 @@ -# <============================================== IMPORTS =========================================================> -import random -from datetime import datetime - -from pyrogram import filters - -from Database.mongodb.karma_mongo import get_couple, save_couple -from Mikobot import app - -# <=======================================================================================================> - - -# <================================================ FUNCTION =======================================================> -def dt(): - now = datetime.now() - dt_string = now.strftime("%d/%m/%Y %H:%M") - dt_list = dt_string.split(" ") - return dt_list - - -def dt_tom(): - a = ( - str(int(dt()[0].split("/")[0]) + 1) - + "/" - + dt()[0].split("/")[1] - + "/" - + dt()[0].split("/")[2] - ) - return a - - -tomorrow = str(dt_tom()) -today = str(dt()[0]) - -COUPLES_PIC = "https://telegra.ph/file/c6d0c884f559b9ed8a54e.jpg" -C = """ -✧ 𝗖𝗢𝗨𝗣𝗟𝗘𝗦 𝗢𝗙 𝗧𝗛𝗘 𝗗𝗔𝗬 ✧ -➖➖➖➖➖➖➖➖➖➖➖➖ -{} + ( PGM🎀😶 (https://t.me/Chalnayaaaaaarr) + 花火 (https://t.me/zd_sr07) + ゼロツー (https://t.me/wewewe_x) ) = 💞 -➖➖➖➖➖➖➖➖➖➖➖➖ -𝗡𝗘𝗪 𝗖𝗢𝗨𝗣𝗟𝗘 𝗢𝗙 𝗧𝗛𝗘 𝗗𝗔𝗬 𝗖𝗔𝗡 𝗕𝗘 𝗖𝗛𝗢𝗦𝗘𝗡 𝗔𝗧 12AM {} -""" -CAP = """ -✧ 𝗖𝗢𝗨𝗣𝗟𝗘𝗦 𝗢𝗙 𝗧𝗛𝗘 𝗗𝗔𝗬 ✧ -➖➖➖➖➖➖➖➖➖➖➖➖ -{} + {} = 💞 -➖➖➖➖➖➖➖➖➖➖➖➖ -𝗡𝗘𝗪 𝗖𝗢𝗨𝗣𝗟𝗘 𝗢𝗙 𝗧𝗛𝗘 𝗗𝗔𝗬 𝗖𝗔𝗡 𝗕𝗘 𝗖𝗛𝗢𝗦𝗘𝗡 𝗔𝗧 12AM {} -""" - -CAP2 = """ -✧ 𝗖𝗢𝗨𝗣𝗟𝗘𝗦 𝗢𝗙 𝗧𝗛𝗘 𝗗𝗔𝗬 ✧ -➖➖➖➖➖➖➖➖➖➖➖➖ -{} (tg://openmessage?user_id={}) + {} (tg://openmessage?user_id={}) = 💞\n -➖➖➖➖➖➖➖➖➖➖➖➖ -𝗡𝗘𝗪 𝗖𝗢𝗨𝗣𝗟𝗘 𝗢𝗙 𝗧𝗛𝗘 𝗗𝗔𝗬 𝗖𝗔𝗡 𝗕𝗘 𝗖𝗛𝗢𝗦𝗘𝗡 𝗔𝗧 12AM {} -""" - - -@app.on_message(filters.command(["couple", "couples", "shipping"]) & ~filters.private) -async def nibba_nibbi(_, message): - if message.from_user.id == 5540249238: - my_ = await _.get_users("rfxtuv") - me = await _.get_users(5540249238) - await message.reply_photo( - photo=COUPLES_PIC, caption=C.format(me.mention, tomorrow) - ) - else: - try: - chat_id = message.chat.id - is_selected = await get_couple(chat_id, today) - if not is_selected: - list_of_users = [] - async for i in _.get_chat_members(message.chat.id, limit=50): - if not i.user.is_bot: - list_of_users.append(i.user.id) - if len(list_of_users) < 2: - return await message.reply_text("ɴᴏᴛ ᴇɴᴏᴜɢʜ ᴜsᴇʀs ɪɴ ᴛʜɪs ɢʀᴏᴜᴘ.") - c1_id = random.choice(list_of_users) - c2_id = random.choice(list_of_users) - while c1_id == c2_id: - c1_id = random.choice(list_of_users) - c1_mention = (await _.get_users(c1_id)).mention - c2_mention = (await _.get_users(c2_id)).mention - await _.send_photo( - message.chat.id, - photo=COUPLES_PIC, - caption=CAP.format(c1_mention, c2_mention, tomorrow), - ) - - couple = {"c1_id": c1_id, "c2_id": c2_id} - await save_couple(chat_id, today, couple) - - elif is_selected: - c1_id = int(is_selected["c1_id"]) - c2_id = int(is_selected["c2_id"]) - - c1_name = (await _.get_users(c1_id)).first_name - c2_name = (await _.get_users(c2_id)).first_name - print(c1_id, c2_id, c1_name, c2_name) - couple_selection_message = f"""✧ 𝗖𝗢𝗨𝗣𝗟𝗘𝗦 𝗢𝗙 𝗧𝗛𝗘 𝗗𝗔𝗬 ✧ -➖➖➖➖➖➖➖➖➖➖➖➖ -[{c1_name}](tg://openmessage?user_id={c1_id}) + [{c2_name}](tg://openmessage?user_id={c2_id}) = 💞 -➖➖➖➖➖➖➖➖➖➖➖➖ -𝗡𝗘𝗪 𝗖𝗢𝗨𝗣𝗟𝗘 𝗢𝗙 𝗧𝗛𝗘 𝗗𝗔𝗬 𝗖𝗔𝗡 𝗕𝗘 𝗖𝗛𝗢𝗦𝗘𝗡 𝗔𝗧 12AM {tomorrow}""" - await _.send_photo( - message.chat.id, photo=COUPLES_PIC, caption=couple_selection_message - ) - except Exception as e: - print(e) - await message.reply_text(str(e)) - - -# <=================================================== HELP ====================================================> - - -__help__ = """ -💘 *Choose couples in your chat* - -» /couple, /couples, /shipping *:* Choose 2 users and send their names as couples in your chat. -""" - -__mod_name__ = "COUPLE" -# <================================================ END =======================================================> diff --git a/Mikobot/plugins/cust_filters.py b/Mikobot/plugins/cust_filters.py deleted file mode 100644 index 6082e54c89c34605d3ac312e4a93c9989a353c5c..0000000000000000000000000000000000000000 --- a/Mikobot/plugins/cust_filters.py +++ /dev/null @@ -1,756 +0,0 @@ -# <============================================== IMPORTS =========================================================> -import random -import re -from html import escape - -from pyrate_limiter import BucketFullException, Duration, InMemoryBucket, Limiter, Rate -from telegram import InlineKeyboardButton, InlineKeyboardMarkup, Update -from telegram.constants import ChatMemberStatus, MessageLimit, ParseMode -from telegram.error import BadRequest -from telegram.ext import ( - ApplicationHandlerStop, - CallbackQueryHandler, - CommandHandler, - ContextTypes, - MessageHandler, -) -from telegram.ext import filters as filters_module -from telegram.helpers import escape_markdown, mention_html - -from Database.sql import cust_filters_sql as sql -from Mikobot import DEV_USERS, DRAGONS, LOGGER, dispatcher, function -from Mikobot.plugins.connection import connected -from Mikobot.plugins.disable import DisableAbleCommandHandler -from Mikobot.plugins.helper_funcs.alternate import send_message, typing_action -from Mikobot.plugins.helper_funcs.chat_status import check_admin -from Mikobot.plugins.helper_funcs.extraction import extract_text -from Mikobot.plugins.helper_funcs.misc import build_keyboard_parser -from Mikobot.plugins.helper_funcs.msg_types import get_filter_type -from Mikobot.plugins.helper_funcs.string_handling import ( - button_markdown_parser, - escape_invalid_curly_brackets, - markdown_to_html, - split_quotes, -) - -# <=======================================================================================================> - -HANDLER_GROUP = 10 - -ENUM_FUNC_MAP = { - sql.Types.TEXT.value: dispatcher.bot.send_message, - sql.Types.BUTTON_TEXT.value: dispatcher.bot.send_message, - sql.Types.STICKER.value: dispatcher.bot.send_sticker, - sql.Types.DOCUMENT.value: dispatcher.bot.send_document, - sql.Types.PHOTO.value: dispatcher.bot.send_photo, - sql.Types.AUDIO.value: dispatcher.bot.send_audio, - sql.Types.VOICE.value: dispatcher.bot.send_voice, - sql.Types.VIDEO.value: dispatcher.bot.send_video, -} - - -class AntiSpam: - def __init__(self): - self.whitelist = (DEV_USERS or []) + (DRAGONS or []) - # Values are HIGHLY experimental, its recommended you pay attention to our commits as we will be adjusting the values over time with what suits best. - Duration.CUSTOM = 15 # Custom duration, 15 seconds - self.sec_limit = Rate(6, Duration.CUSTOM) # 6 / Per 15 Seconds - self.min_limit = Rate(20, Duration.MINUTE) # 20 / Per minute - self.hour_limit = Rate(100, Duration.HOUR) # 100 / Per hour - self.daily_limit = Rate(1000, Duration.DAY) # 1000 / Per day - self.limiter = Limiter( - InMemoryBucket( - [self.sec_limit, self.min_limit, self.hour_limit, self.daily_limit] - ) - ) - - def check_user(self, user): - """ - Return True if user is to be ignored else False - """ - if user in self.whitelist: - return False - try: - self.limiter.try_acquire(user) - return False - except BucketFullException: - return True - - -MessageHandlerChecker = AntiSpam() - - -# <================================================ FUNCTION =======================================================> -@typing_action -async def list_handlers(update: Update, context: ContextTypes.DEFAULT_TYPE): - chat = update.effective_chat - user = update.effective_user - - conn = await connected(context.bot, update, chat, user.id, need_admin=False) - if not conn is False: - chat_id = conn - chat_obj = await dispatcher.bot.getChat(conn) - chat_name = chat_obj.title - filter_list = "*Filter in {}:*\n" - else: - chat_id = update.effective_chat.id - if chat.type == "private": - chat_name = "Local filters" - filter_list = "*local filters:*\n" - else: - chat_name = chat.title - filter_list = "*Filters in {}*:\n" - - all_handlers = sql.get_chat_triggers(chat_id) - - if not all_handlers: - await send_message( - update.effective_message, - "No filters saved in {}!".format(chat_name), - ) - return - - for keyword in all_handlers: - entry = " • `{}`\n".format(escape_markdown(keyword)) - if len(entry) + len(filter_list) > MessageLimit.MAX_TEXT_LENGTH: - await send_message( - update.effective_message, - filter_list.format(chat_name), - parse_mode=ParseMode.MARKDOWN, - ) - filter_list = entry - else: - filter_list += entry - - await send_message( - update.effective_message, - filter_list.format(chat_name), - parse_mode=ParseMode.MARKDOWN, - ) - - -@typing_action -@check_admin(is_user=True) -async def filters(update: Update, context: ContextTypes.DEFAULT_TYPE): - chat = update.effective_chat - user = update.effective_user - msg = update.effective_message - args = msg.text.split( - None, 1 - ) # use python's maxsplit to separate Cmd, keyword, and reply_text - - buttons = None - conn = await connected(context.bot, update, chat, user.id) - if not conn is False: - chat_id = conn - chat_obj = await dispatcher.bot.getChat(conn) - chat_name = chat_obj.title - else: - chat_id = update.effective_chat.id - if chat.type == "private": - chat_name = "local filters" - else: - chat_name = chat.title - - if not msg.reply_to_message and len(args) < 2: - await send_message( - update.effective_message, - "Please provide keyboard keyword for this filter to reply with!", - ) - return - - if msg.reply_to_message and not msg.reply_to_message.forum_topic_created: - if len(args) < 2: - await send_message( - update.effective_message, - "Please provide keyword for this filter to reply with!", - ) - return - else: - keyword = args[1] - else: - extracted = split_quotes(args[1]) - if len(extracted) < 1: - return - # set trigger -> lower, so as to avoid adding duplicate filters with different cases - keyword = extracted[0].lower() - - # Add the filter - # Note: perhaps handlers can be removed somehow using sql.get_chat_filters - for handler in dispatcher.handlers.get(HANDLER_GROUP, []): - if handler.filters == (keyword, chat_id): - dispatcher.remove_handler(handler, HANDLER_GROUP) - - text, file_type, file_id, media_spoiler = get_filter_type(msg) - if not msg.reply_to_message and len(extracted) >= 2: - offset = len(extracted[1]) - len( - msg.text, - ) # set correct offset relative to command + notename - text, buttons = button_markdown_parser( - extracted[1], - entities=msg.parse_entities(), - offset=offset, - ) - text = text.strip() - if not text: - await send_message( - update.effective_message, - "There is no note message - You can't JUST have buttons, you need a message to go with it!", - ) - return - - if len(args) >= 2: - if msg.reply_to_message: - if msg.reply_to_message.forum_topic_created: - offset = len(extracted[1]) - len(msg.text) - - text, buttons = button_markdown_parser( - extracted[1], entities=msg.parse_entities(), offset=offset - ) - - text = text.strip() - if not text: - await send_message( - update.effective_message, - "There is no note message - You can't JUST have buttons, you need a message to go with it!", - ) - return - else: - pass - - elif msg.reply_to_message and len(args) >= 1: - if msg.reply_to_message.text: - text_to_parsing = msg.reply_to_message.text - elif msg.reply_to_message.caption: - text_to_parsing = msg.reply_to_message.caption - else: - text_to_parsing = "" - offset = len( - text_to_parsing, - ) # set correct offset relative to command + notename - text, buttons = button_markdown_parser( - text_to_parsing, - entities=msg.parse_entities(), - offset=offset, - ) - text = text.strip() - - elif not text and not file_type: - await send_message( - update.effective_message, - "Please provide keyword for this filter reply with!", - ) - return - - elif msg.reply_to_message: - if msg.reply_to_message.forum_topic_created: - return - - if msg.reply_to_message.text: - text_to_parsing = msg.reply_to_message.text - elif msg.reply_to_message.caption: - text_to_parsing = msg.reply_to_message.caption - else: - text_to_parsing = "" - offset = len( - text_to_parsing, - ) # set correct offset relative to command + notename - text, buttons = button_markdown_parser( - text_to_parsing, - entities=msg.parse_entities(), - offset=offset, - ) - text = text.strip() - if (msg.reply_to_message.text or msg.reply_to_message.caption) and not text: - await send_message( - update.effective_message, - "There is no note message - You can't JUST have buttons, you need a message to go with it!", - ) - return - - else: - await send_message(update.effective_message, "Invalid filter!") - return - - add = await addnew_filter( - update, chat_id, keyword, text, file_type, file_id, buttons, media_spoiler - ) - # This is an old method - # sql.add_filter(chat_id, keyword, content, is_sticker, is_document, is_image, is_audio, is_voice, is_video, buttons) - - if add is True: - await send_message( - update.effective_message, - "Saved filter '{}' in *{}*!".format(keyword, chat_name), - parse_mode=ParseMode.MARKDOWN, - ) - raise ApplicationHandlerStop - - -@typing_action -@check_admin(is_user=True) -async def stop_filter(update: Update, context: ContextTypes.DEFAULT_TYPE): - chat = update.effective_chat - user = update.effective_user - args = update.effective_message.text.split(None, 1) - - conn = await connected(context.bot, update, chat, user.id) - if not conn is False: - chat_id = conn - chat_obj = await dispatcher.bot.getChat(conn) - chat_name = chat_obj.title - else: - chat_id = update.effective_chat.id - if chat.type == "private": - chat_name = "Local filters" - else: - chat_name = chat.title - - if len(args) < 2: - await send_message(update.effective_message, "What should i stop?") - return - - chat_filters = sql.get_chat_triggers(chat_id) - - if not chat_filters: - await send_message(update.effective_message, "No filters active here!") - return - - for keyword in chat_filters: - if keyword == args[1]: - sql.remove_filter(chat_id, args[1]) - await send_message( - update.effective_message, - "Okay, I'll stop replying to that filter in *{}*.".format(chat_name), - parse_mode=ParseMode.MARKDOWN, - ) - raise ApplicationHandlerStop - - await send_message( - update.effective_message, - "That's not a filter - Click: /filters to get currently active filters.", - ) - - -async def reply_filter(update: Update, context: ContextTypes.DEFAULT_TYPE): - chat = update.effective_chat - message = update.effective_message - - if not update.effective_user or update.effective_user.id == 777000: - return - to_match = await extract_text(message) - if not to_match: - return - - chat_filters = sql.get_chat_triggers(chat.id) - for keyword in chat_filters: - pattern = r"( |^|[^\w])" + re.escape(keyword) + r"( |$|[^\w])" - if re.search(pattern, to_match, flags=re.IGNORECASE): - if MessageHandlerChecker.check_user(update.effective_user.id): - return - filt = sql.get_filter(chat.id, keyword) - if filt.reply == "there is should be a new reply": - buttons = sql.get_buttons(chat.id, filt.keyword) - keyb = build_keyboard_parser(context.bot, chat.id, buttons) - keyboard = InlineKeyboardMarkup(keyb) - - VALID_WELCOME_FORMATTERS = [ - "first", - "last", - "fullname", - "username", - "id", - "chatname", - "mention", - ] - if filt.reply_text: - if "%%%" in filt.reply_text: - split = filt.reply_text.split("%%%") - if all(split): - text = random.choice(split) - else: - text = filt.reply_text - else: - text = filt.reply_text - if text.startswith("~!") and text.endswith("!~"): - sticker_id = text.replace("~!", "").replace("!~", "") - try: - await context.bot.send_sticker( - chat.id, - sticker_id, - reply_to_message_id=message.message_id, - message_thread_id=message.message_thread_id - if chat.is_forum - else None, - ) - return - except BadRequest as excp: - if ( - excp.message - == "Wrong remote file identifier specified: wrong padding in the string" - ): - await context.bot.send_message( - chat.id, - "Message couldn't be sent, Is the sticker id valid?", - message_thread_id=message.message_thread_id - if chat.is_forum - else None, - ) - return - else: - LOGGER.exception("Error in filters: " + excp.message) - return - valid_format = escape_invalid_curly_brackets( - text, - VALID_WELCOME_FORMATTERS, - ) - if valid_format: - filtext = valid_format.format( - first=escape(message.from_user.first_name), - last=escape( - message.from_user.last_name - or message.from_user.first_name, - ), - fullname=" ".join( - [ - escape(message.from_user.first_name), - escape(message.from_user.last_name), - ] - if message.from_user.last_name - else [escape(message.from_user.first_name)], - ), - username="@" + escape(message.from_user.username) - if message.from_user.username - else mention_html( - message.from_user.id, - message.from_user.first_name, - ), - mention=mention_html( - message.from_user.id, - message.from_user.first_name, - ), - chatname=escape(message.chat.title) - if message.chat.type != "private" - else escape(message.from_user.first_name), - id=message.from_user.id, - ) - else: - filtext = "" - else: - filtext = "" - - if filt.file_type in (sql.Types.BUTTON_TEXT, sql.Types.TEXT): - try: - await message.reply_text( - markdown_to_html(filtext), - parse_mode=ParseMode.HTML, - disable_web_page_preview=True, - reply_markup=keyboard, - ) - except BadRequest as excp: - LOGGER.exception("Error in filters: " + excp.message) - try: - await send_message( - update.effective_message, - get_exception(excp, filt, chat), - ) - except BadRequest as excp: - LOGGER.exception( - "Failed to send message: " + excp.message, - ) - else: - try: - if filt.file_type not in [ - sql.Types.PHOTO.value, - sql.Types.VIDEO, - ]: - await ENUM_FUNC_MAP[filt.file_type]( - chat.id, - filt.file_id, - reply_markup=keyboard, - reply_to_message_id=message.message_id, - message_thread_id=message.message_thread_id - if chat.is_forum - else None, - ) - else: - await ENUM_FUNC_MAP[filt.file_type]( - chat.id, - filt.file_id, - reply_markup=keyboard, - caption=filt.reply_text, - reply_to_message_id=message.message_id, - message_thread_id=message.message_thread_id - if chat.is_forum - else None, - has_spoiler=filt.has_media_spoiler, - ) - except BadRequest: - await send_message( - message, - "I don't have the permission to send the content of the filter.", - ) - break - else: - if filt.is_sticker: - await message.reply_sticker(filt.reply) - elif filt.is_document: - await message.reply_document(filt.reply) - elif filt.is_image: - await message.reply_photo( - filt.reply, has_spoiler=filt.has_media_spoiler - ) - elif filt.is_audio: - await message.reply_audio(filt.reply) - elif filt.is_voice: - await message.reply_voice(filt.reply) - elif filt.is_video: - await message.reply_video( - filt.reply, has_spoiler=filt.has_media_spoiler - ) - elif filt.has_buttons: - buttons = sql.get_buttons(chat.id, filt.keyword) - keyb = build_keyboard_parser(context.bot, chat.id, buttons) - keyboard = InlineKeyboardMarkup(keyb) - - try: - await context.bot.send_message( - chat.id, - markdown_to_html(filt.reply), - parse_mode=ParseMode.HTML, - disable_web_page_preview=True, - reply_markup=keyboard, - message_thread_id=message.message_thread_id - if chat.is_forum - else None, - ) - except BadRequest as excp: - if excp.message == "Unsupported url protocol": - try: - await send_message( - update.effective_message, - "You seem to be trying to use an unsupported url protocol. " - "Telegram doesn't support buttons for some protocols, such as tg://. Please try " - "again...", - ) - except BadRequest as excp: - LOGGER.exception("Error in filters: " + excp.message) - else: - try: - await send_message( - update.effective_message, - "This message couldn't be sent as it's incorrectly formatted.", - ) - except BadRequest as excp: - LOGGER.exception("Error in filters: " + excp.message) - LOGGER.warning( - "Message %s could not be parsed", - str(filt.reply), - ) - LOGGER.exception( - "Could not parse filter %s in chat %s", - str(filt.keyword), - str(chat.id), - ) - - else: - # LEGACY - all new filters will have has_markdown set to True. - try: - await context.bot.send_message( - chat.id, - filt.reply, - message_thread_id=message.message_thread_id - if chat.is_forum - else None, - ) - except BadRequest as excp: - LOGGER.exception("Error in filters: " + excp.message) - break - - -async def rmall_filters(update: Update, context: ContextTypes.DEFAULT_TYPE): - chat = update.effective_chat - user = update.effective_user - member = await chat.get_member(user.id) - if member.status != ChatMemberStatus.OWNER and user.id not in DRAGONS: - await update.effective_message.reply_text( - "Only the chat owner can clear all notes at once.", - ) - else: - buttons = InlineKeyboardMarkup( - [ - [ - InlineKeyboardButton( - text="STOP ALL FILTERS", - callback_data="filters_rmall", - ), - ], - [InlineKeyboardButton(text="CANCEL", callback_data="filters_cancel")], - ], - ) - await update.effective_message.reply_text( - f"Are you sure you would like to stop ALL filters in {chat.title}? This action cannot be undone.", - reply_markup=buttons, - parse_mode=ParseMode.MARKDOWN, - ) - - -async def rmall_callback(update: Update, context: ContextTypes.DEFAULT_TYPE): - query = update.callback_query - chat = update.effective_chat - msg = update.effective_message - member = await chat.get_member(query.from_user.id) - if query.data == "filters_rmall": - if member.status == "creator" or query.from_user.id in DRAGONS: - allfilters = sql.get_chat_triggers(chat.id) - if not allfilters: - await msg.edit_text("No filters in this chat, nothing to stop!") - return - - count = 0 - filterlist = [] - for x in allfilters: - count += 1 - filterlist.append(x) - - for i in filterlist: - sql.remove_filter(chat.id, i) - - await msg.edit_text(f"Cleaned {count} filters in {chat.title}") - - if member.status == "administrator": - await query.answer("Only owner of the chat can do this.") - - if member.status == "member": - await query.answer("You need to be admin to do this.") - elif query.data == "filters_cancel": - if member.status == "creator" or query.from_user.id in DRAGONS: - await msg.edit_text("Clearing of all filters has been cancelled.") - return await query.answer() - if member.status == "administrator": - await query.answer("Only owner of the chat can do this.") - if member.status == "member": - await query.answer("You need to be admin to do this.") - - -# NOT ASYNC NOT A HANDLER -def get_exception(excp, filt, chat): - if excp.message == "Unsupported url protocol": - return "You seem to be trying to use the URL protocol which is not supported. Telegram does not support key for multiple protocols, such as tg: //. Please try again!" - elif excp.message == "Reply message not found": - return "noreply" - else: - LOGGER.warning("Message %s could not be parsed", str(filt.reply)) - LOGGER.exception( - "Could not parse filter %s in chat %s", - str(filt.keyword), - str(chat.id), - ) - return "This data could not be sent because it is incorrectly formatted." - - -# NOT ASYNC NOT A HANDLER -async def addnew_filter( - update, chat_id, keyword, text, file_type, file_id, buttons, has_spoiler -): - msg = update.effective_message - totalfilt = sql.get_chat_triggers(chat_id) - if len(totalfilt) >= 150: # Idk why i made this like function.... - await msg.reply_text("This group has reached its max filters limit of 150.") - return False - else: - sql.new_add_filter( - chat_id, keyword, text, file_type, file_id, buttons, has_spoiler - ) - return True - - -def __stats__(): - return "• {} filters, across {} chats.".format(sql.num_filters(), sql.num_chats()) - - -async def __import_data__(chat_id, data, message): - # set chat filters - filters = data.get("filters", {}) - for trigger in filters: - sql.add_to_blacklist(chat_id, trigger) - - -def __migrate__(old_chat_id, new_chat_id): - sql.migrate_chat(old_chat_id, new_chat_id) - - -def __chat_settings__(chat_id, user_id): - cust_filters = sql.get_chat_triggers(chat_id) - return "There are `{}` custom filters here.".format(len(cust_filters)) - - -# <=================================================== HELP ====================================================> - - -__help__ = """ -» `/filters`*:* List all active filters saved in the chat. - -➠ *Admin only:* - -» `/filter <keyword> <reply message>`*:* Add a filter to this chat. The bot will now reply that message whenever 'keyword'\ -is mentioned. If you reply to a sticker with a keyword, the bot will reply with that sticker. NOTE: all filter \ -keywords are in lowercase. If you want your keyword to be a sentence, use quotes. eg: /filter "hey there" How you \ -doin? - -➠ Separate diff replies by `%%%` to get random replies -➠ *Example:* - -» `/filter "filtername" - Reply 1 - %%% - Reply 2 - %%% - Reply 3` - -» `/stop <filter keyword>`*:* Stop that filter. - -➠ *Chat creator only:* -» `/removeallfilters`*:* Remove all chat filters at once. - -➠ *Note*: Filters also support markdown formatters like: {first}, {last} etc.. and buttons. -➠ Now Supports media spoilers too, and media caption. -""" - -__mod_name__ = "FILTERS" - -# <================================================ HANDLER =======================================================> -FILTER_HANDLER = CommandHandler("filter", filters, block=False) -STOP_HANDLER = CommandHandler("stop", stop_filter, block=False) -RMALLFILTER_HANDLER = CommandHandler( - "removeallfilters", - rmall_filters, - filters=filters_module.ChatType.GROUPS, - block=False, -) -RMALLFILTER_CALLBACK = CallbackQueryHandler( - rmall_callback, pattern=r"filters_.*", block=False -) -LIST_HANDLER = DisableAbleCommandHandler( - "filters", list_handlers, admin_ok=True, block=False -) -CUST_FILTER_HANDLER = MessageHandler( - filters_module.TEXT & ~filters_module.UpdateType.EDITED_MESSAGE, - reply_filter, - block=False, -) - -function(FILTER_HANDLER) -function(STOP_HANDLER) -function(LIST_HANDLER) -function(CUST_FILTER_HANDLER, HANDLER_GROUP) -function(RMALLFILTER_HANDLER) -function(RMALLFILTER_CALLBACK) - -__handlers__ = [ - FILTER_HANDLER, - STOP_HANDLER, - LIST_HANDLER, - (CUST_FILTER_HANDLER, HANDLER_GROUP, RMALLFILTER_HANDLER), -] -# <================================================ END =======================================================> diff --git a/Mikobot/plugins/disable.py b/Mikobot/plugins/disable.py deleted file mode 100644 index 8a59438eb486f991526cc1b7334ae93985239ca3..0000000000000000000000000000000000000000 --- a/Mikobot/plugins/disable.py +++ /dev/null @@ -1,376 +0,0 @@ -# <============================================== IMPORTS =========================================================> -import importlib -import re -from typing import Dict, List, Optional, Tuple, Union - -from future.utils import string_types -from telegram import Update -from telegram.constants import ParseMode -from telegram.ext import CommandHandler, ContextTypes, MessageHandler -from telegram.ext import filters as filters_module -from telegram.helpers import escape_markdown - -from Mikobot import function -from Mikobot.plugins.helper_funcs.misc import is_module_loaded -from Mikobot.utils.cmdprefix import CMD_STARTERS - -# <=======================================================================================================> - -FILENAME = __name__.rsplit(".", 1)[-1] - -# If module is due to be loaded, then setup all the magical handlers -if is_module_loaded(FILENAME): - from Database.sql import disable_sql as sql - from Mikobot.plugins.helper_funcs.chat_status import ( - check_admin, - connection_status, - is_user_admin, - ) - - DISABLE_CMDS = [] - DISABLE_OTHER = [] - ADMIN_CMDS = [] - - # <================================================ CLASS =======================================================> - class DisableAbleCommandHandler(CommandHandler): - def __init__( - self, - command, - callback, - block: bool, - filters: filters_module.BaseFilter = None, - admin_ok=False, - ): - super().__init__(command, callback, block=block) - self.admin_ok = admin_ok - - if isinstance(command, string_types): - commands = frozenset({command.lower()}) - DISABLE_CMDS.append(command) - if admin_ok: - ADMIN_CMDS.append(command) - else: - commands = frozenset(x.lower() for x in command) - DISABLE_CMDS.extend(command) - if admin_ok: - ADMIN_CMDS.extend(command) - for comm in commands: - if not re.match(r"^[\da-z_]{1,32}$", comm): - raise ValueError(f"Command `{comm}` is not a valid bot command") - - self.commands = commands - self.filters = ( - filters if filters is not None else filters_module.UpdateType.MESSAGES - ) - - def check_update( - self, update - ) -> Optional[Union[bool, Tuple[List[str], Optional[Union[bool, Dict]]]]]: - if isinstance(update, Update) and update.effective_message: - message = update.effective_message - - if message.text and len(message.text) > 1: - fst_word = message.text.split(None, 1)[0] - if len(fst_word) > 1 and any( - fst_word.startswith(start) for start in CMD_STARTERS - ): - args = message.text.split()[1:] - command_parts = fst_word[1:].split("@") - command_parts.append(message.get_bot().username) - - if not ( - command_parts[0].lower() in self.commands - and command_parts[1].lower() - == message.get_bot().username.lower() - ): - return None - - chat = update.effective_chat - user = update.effective_user - - filter_result = self.filters.check_update(update) - if filter_result: - # disabled, admincmd, user admin - if sql.is_command_disabled( - chat.id, command_parts[0].lower() - ): - # check if command was disabled - is_disabled = command_parts[ - 0 - ] in ADMIN_CMDS and is_user_admin(chat, user.id) - if not is_disabled: - return None - else: - return args, filter_result - - return args, filter_result - return False - return None - - class DisableAbleMessageHandler(MessageHandler): - def __init__(self, filters, callback, block: bool, friendly, **kwargs): - super().__init__(filters, callback, block=block, **kwargs) - DISABLE_OTHER.append(friendly) - self.friendly = friendly - if filters: - self.filters = filters_module.UpdateType.MESSAGES & filters - else: - self.filters = filters_module.UpdateType.MESSAGES - - def check_update(self, update): - chat = update.effective_chat - message = update.effective_message - filter_result = self.filters.check_update(update) - - try: - args = message.text.split()[1:] - except: - args = [] - - if super().check_update(update): - if sql.is_command_disabled(chat.id, self.friendly): - return False - else: - return args, filter_result - - # <=======================================================================================================> - - # <================================================ FUNCTION =======================================================> - @connection_status - @check_admin(is_user=True) - async def disable(update: Update, context: ContextTypes.DEFAULT_TYPE): - args = context.args - chat = update.effective_chat - if len(args) >= 1: - disable_cmd = args[0] - if disable_cmd.startswith(CMD_STARTERS): - disable_cmd = disable_cmd[1:] - - if disable_cmd in set(DISABLE_CMDS + DISABLE_OTHER): - sql.disable_command(chat.id, str(disable_cmd).lower()) - await update.effective_message.reply_text( - f"Disabled the use of `{disable_cmd}`", - parse_mode=ParseMode.MARKDOWN, - ) - else: - await update.effective_message.reply_text( - "That command can't be disabled" - ) - - else: - await update.effective_message.reply_text("What should I disable?") - - @connection_status - @check_admin(is_user=True) - async def disable_module( - update: Update, context: ContextTypes.DEFAULT_TYPE - ) -> None: - args = context.args - chat = update.effective_chat - if len(args) >= 1: - disable_module = "Mikobot.plugins." + args[0].rsplit(".", 1)[0] - - try: - module = importlib.import_module(disable_module) - except: - await update.effective_message.reply_text( - "Does that module even exsist?" - ) - return - - try: - command_list = module.__command_list__ - except: - await update.effective_message.reply_text( - "Module does not contain command list!", - ) - return - - disabled_cmds = [] - failed_disabled_cmds = [] - - for disable_cmd in command_list: - if disable_cmd.startswith(CMD_STARTERS): - disable_cmd = disable_cmd[1:] - - if disable_cmd in set(DISABLE_CMDS + DISABLE_OTHER): - sql.disable_command(chat.id, str(disable_cmd).lower()) - disabled_cmds.append(disable_cmd) - else: - failed_disabled_cmds.append(disable_cmd) - - if disabled_cmds: - disabled_cmds_string = ", ".join(disabled_cmds) - await update.effective_message.reply_text( - f"Disabled the use of`{disabled_cmds_string}`", - parse_mode=ParseMode.MARKDOWN, - ) - - if failed_disabled_cmds: - failed_disabled_cmds_string = ", ".join(failed_disabled_cmds) - await update.effective_message.reply_text( - f"Commands `{failed_disabled_cmds_string}` can't be disabled", - parse_mode=ParseMode.MARKDOWN, - ) - - else: - await update.effective_message.reply_text("What should I disable?") - - @connection_status - @check_admin(is_user=True) - async def enable(update: Update, context: ContextTypes.DEFAULT_TYPE): - args = context.args - chat = update.effective_chat - if len(args) >= 1: - enable_cmd = args[0] - if enable_cmd.startswith(CMD_STARTERS): - enable_cmd = enable_cmd[1:] - - if sql.enable_command(chat.id, enable_cmd): - await update.effective_message.reply_text( - f"Enabled the use of`{enable_cmd}`", - parse_mode=ParseMode.MARKDOWN, - ) - else: - await update.effective_message.reply_text("Is that even disabled?") - - else: - await update.effective_message.reply_text("What sould I enable?") - - @connection_status - @check_admin(is_user=True) - async def enable_module(update: Update, context: ContextTypes.DEFAULT_TYPE): - args = context.args - chat = update.effective_chat - - if len(args) >= 1: - enable_module = "Mikobot.plugins." + args[0].rsplit(".", 1)[0] - - try: - module = importlib.import_module(enable_module) - except: - await update.effective_message.reply_text( - "Does that module even exsist?" - ) - return - - try: - command_list = module.__command_list__ - except: - await update.effective_message.reply_text( - "Module does not contain command list!", - ) - return - - enabled_cmds = [] - failed_enabled_cmds = [] - - for enable_cmd in command_list: - if enable_cmd.startswith(CMD_STARTERS): - enable_cmd = enable_cmd[1:] - - if sql.enable_command(chat.id, enable_cmd): - enabled_cmds.append(enable_cmd) - else: - failed_enabled_cmds.append(enable_cmd) - - if enabled_cmds: - enabled_cmds_string = ", ".join(enabled_cmds) - await update.effective_message.reply_text( - f"Enabled the use of`{enabled_cmds_string}`", - parse_mode=ParseMode.MARKDOWN, - ) - - if failed_enabled_cmds: - failed_enabled_cmds_string = ", ".join(failed_enabled_cmds) - await update.effective_message.reply_text( - f"Are the commands `{failed_enabled_cmds_string}` even disabled?", - parse_mode=ParseMode.MARKDOWN, - ) - - else: - await update.effective_message.reply_text("ᴡʜᴀᴛ sʜᴏᴜʟᴅ I ᴇɴᴀʙʟᴇ?") - - @connection_status - @check_admin(is_user=True) - async def list_cmds(update: Update, context: ContextTypes.DEFAULT_TYPE): - if DISABLE_CMDS + DISABLE_OTHER: - result = "" - for cmd in set(DISABLE_CMDS + DISABLE_OTHER): - result += f" - `{escape_markdown(cmd)}`\n" - await update.effective_message.reply_text( - f"The following commands are toggleable:\n{result}", - parse_mode=ParseMode.MARKDOWN, - ) - else: - await update.effective_message.reply_text("No commands can be disabled.") - - # do not async - def build_curr_disabled(chat_id: Union[str, int]) -> str: - disabled = sql.get_all_disabled(chat_id) - if not disabled: - return "No commands are disabled!" - - result = "" - for cmd in disabled: - result += " - `{}`\n".format(escape_markdown(cmd)) - return "The following commands are currently restricted:\n{}".format(result) - - @connection_status - async def commands(update: Update, context: ContextTypes.DEFAULT_TYPE): - chat = update.effective_chat - await update.effective_message.reply_text( - build_curr_disabled(chat.id), - parse_mode=ParseMode.MARKDOWN, - ) - - def __stats__(): - return f"• {sql.num_disabled()} disabled items, across {sql.num_chats()} chats." - - def __migrate__(old_chat_id, new_chat_id): - sql.migrate_chat(old_chat_id, new_chat_id) - - def __chat_settings__(chat_id, user_id): - return build_curr_disabled(chat_id) - - # <=================================================== HANDLER ====================================================> - - DISABLE_HANDLER = CommandHandler("disable", disable, block=False) - DISABLE_MODULE_HANDLER = CommandHandler( - "disablemodule", disable_module, block=False - ) - ENABLE_HANDLER = CommandHandler("enable", enable, block=False) - ENABLE_MODULE_HANDLER = CommandHandler("enablemodule", enable_module, block=False) - COMMANDS_HANDLER = CommandHandler(["cmds", "disabled"], commands, block=False) - TOGGLE_HANDLER = CommandHandler("listcmds", list_cmds, block=False) - - function(DISABLE_HANDLER) - function(DISABLE_MODULE_HANDLER) - function(ENABLE_HANDLER) - function(ENABLE_MODULE_HANDLER) - function(COMMANDS_HANDLER) - function(TOGGLE_HANDLER) - - # <=================================================== HELP ====================================================> - __help__ = """ -» /cmds: Check the current status of disabled commands - -➠ *Admins only*: - -» /enable < cmd name >: Enable that command. - -» /disable < cmd name >: Disable that command. - -» /enablemodule < module name >: Enable all commands in that module. - -» /disablemodule < module name >: Disable all commands in that module. - -» /listcmds: List all possible toggleable commands. - """ - - __mod_name__ = "DISABLE" - -else: - DisableAbleCommandHandler = CommandHandler - DisableAbleMessageHandler = MessageHandler -# <================================================ END =======================================================> diff --git a/Mikobot/plugins/extra.py b/Mikobot/plugins/extra.py deleted file mode 100644 index 90da99b62f990f79655d7e0cfd9934f5e331d544..0000000000000000000000000000000000000000 --- a/Mikobot/plugins/extra.py +++ /dev/null @@ -1,137 +0,0 @@ -# <============================================== IMPORTS =========================================================> -from time import gmtime, strftime, time - -from pyrogram import filters -from pyrogram.types import Message -from telegram import InlineKeyboardButton, InlineKeyboardMarkup, Update -from telegram.ext import CallbackQueryHandler, CommandHandler, ContextTypes - -from Mikobot import LOGGER, app, function -from Mikobot.plugins.helper_funcs.chat_status import check_admin - -# <=======================================================================================================> - -UPTIME = time() # Check bot uptime - - -# <================================================ FUNCTION =======================================================> -async def getid(update: Update, context: ContextTypes.DEFAULT_TYPE): - chat = update.effective_chat - your_id = update.message.from_user.id - message_id = update.message.message_id - reply = update.message.reply_to_message - - text = f"[Message ID:](https://t.me/{chat.username}/{message_id}) `{message_id}`\n" - text += f"[Your ID:](tg://user?id={your_id}) `{your_id}`\n" - - if context.args: - try: - user_id = context.args[0] - text += f"[User ID:](tg://user?id={user_id}) `{user_id}`\n" - except Exception: - await update.message.reply_text( - "This user doesn't exist.", parse_mode="Markdown" - ) - return - - text += f"[Chat ID:](https://t.me/{chat.username}) `{chat.id}`\n\n" - - if reply: - text += f"[Replied Message ID:](https://t.me/{chat.username}/{reply.message_id}) `{reply.message_id}`\n" - text += f"[Replied User ID:](tg://user?id={reply.from_user.id}) `{reply.from_user.id}`\n\n" - - if reply and reply.forward_from_chat: - text += f"The forwarded channel, {reply.forward_from_chat.title}, has an id of `{reply.forward_from_chat.id}`\n\n" - - if reply and reply.sender_chat: - text += f"ID of the replied chat/channel, is `{reply.sender_chat.id}`" - - # Sticker ID to be sent - sticker_id = ( - "CAACAgIAAx0CanzPTAABASPCZQdU9NbQIol5TW1GU2zV4KfjDMEAAnccAALIWZhJPyYLf3FzPHswBA" - ) - - # Send the sticker - await update.message.reply_sticker(sticker=sticker_id) - - # Send the text message as a caption - await update.message.reply_text( - text, parse_mode="Markdown", disable_web_page_preview=True - ) - - -# Function to handle the "logs" command -@check_admin(only_dev=True) -async def logs(update: Update, context: ContextTypes.DEFAULT_TYPE): - user = update.effective_user - with open("Logs.txt", "rb") as f: - caption = "Here is your log" - reply_markup = InlineKeyboardMarkup( - [[InlineKeyboardButton("Close", callback_data="close")]] - ) - message = await context.bot.send_document( - document=f, - filename=f.name, - caption=caption, - reply_markup=reply_markup, - chat_id=user.id, - ) - - # Store the message ID for later reference - context.user_data["log_message_id"] = message.message_id - - -# Asynchronous callback query handler for the "close" button -@check_admin(only_dev=True) -async def close_callback(update: Update, context: ContextTypes.DEFAULT_TYPE): - query = update.callback_query - message_id = context.user_data.get("log_message_id") - if message_id: - await context.bot.delete_message( - chat_id=query.message.chat_id, message_id=message_id - ) - - -@app.on_message(filters.command("pyroping")) -async def ping(_, m: Message): - LOGGER.info(f"{m.from_user.id} used ping cmd in {m.chat.id}") - start = time() - replymsg = await m.reply_text(text="Pinging...", quote=True) - delta_ping = time() - start - - up = strftime("%Hh %Mm %Ss", gmtime(time() - UPTIME)) - image_url = "https://telegra.ph/file/f215a1c4adf25a5bad81b.jpg" - - # Send the image as a reply - await replymsg.reply_photo( - photo=image_url, - caption=f"<b>Pyro-Pong!</b>\n{delta_ping * 1000:.3f} ms\n\nUptime: <code>{up}</code>", - ) - await replymsg.delete() - - -# <=======================================================================================================> - - -# <================================================ HANDLER =======================================================> -function(CommandHandler("logs", logs, block=False)) -function(CommandHandler("id", getid, block=False)) -function(CallbackQueryHandler(close_callback, pattern="^close$", block=False)) - -# <================================================= HELP ======================================================> -__help__ = """ -➠ *Commands*: - -» /instadl, /insta <link>: Get instagram contents like reel video or images. - -» /pyroping: see pyroping. - -» /hyperlink <text> <link> : Creates a markdown hyperlink with the provided text and link. - -» /pickwinner <participant1> <participant2> ... : Picks a random winner from the provided list of participants. - -» /id: reply to get user id. -""" - -__mod_name__ = "EXTRA" -# <================================================ END =======================================================> diff --git a/Mikobot/plugins/feds.py b/Mikobot/plugins/feds.py deleted file mode 100644 index 3930f432f6b2cd17c5a2e80c9af87858ab94a4fc..0000000000000000000000000000000000000000 --- a/Mikobot/plugins/feds.py +++ /dev/null @@ -1,2505 +0,0 @@ -# <============================================== IMPORTS =========================================================> -import ast -import csv -import json -import os -import re -import time -import uuid -from io import BytesIO - -from telegram import ( - ChatMember, - InlineKeyboardButton, - InlineKeyboardMarkup, - MessageEntity, - Update, -) -from telegram.constants import ParseMode -from telegram.error import BadRequest, Forbidden, TelegramError -from telegram.ext import CallbackQueryHandler, CommandHandler, ContextTypes -from telegram.helpers import mention_html, mention_markdown - -import Database.sql.feds_sql as sql -from Mikobot import ( - DRAGONS, - EVENT_LOGS, - LOGGER, - OWNER_ID, - SUPPORT_CHAT, - dispatcher, - function, -) -from Mikobot.plugins.disable import DisableAbleCommandHandler -from Mikobot.plugins.helper_funcs.alternate import send_message -from Mikobot.plugins.helper_funcs.chat_status import is_user_admin -from Mikobot.plugins.helper_funcs.extraction import ( - extract_unt_fedban, - extract_user, - extract_user_fban, -) -from Mikobot.plugins.helper_funcs.string_handling import markdown_parser - -# <=======================================================================================================> - -FBAN_ERRORS = { - "User is an administrator of the chat", - "Chat not found", - "Not enough rights to restrict/unrestrict chat member", - "User_not_participant", - "Peer_id_invalid", - "Group chat was deactivated", - "Need to be inviter of a user to kick it from a basic group", - "Chat_admin_required", - "Only the creator of a basic group can kick group administrators", - "Channel_private", - "Not in the chat", - "Have no rights to send a message", -} - -UNFBAN_ERRORS = { - "User is an administrator of the chat", - "Chat not found", - "Not enough rights to restrict/unrestrict chat member", - "User_not_participant", - "Method is available for supergroup and channel chats only", - "Not in the chat", - "Channel_private", - "Chat_admin_required", - "Have no rights to send a message", -} - - -# <================================================ FUNCTION =======================================================> -async def new_fed(update: Update, context: ContextTypes.DEFAULT_TYPE): - chat = update.effective_chat - user = update.effective_user - message = update.effective_message - bot = context.bot - if chat.type != "private": - await update.effective_message.reply_text( - "Federations can only be created by privately messaging me.", - ) - return - if len(message.text) == 1: - await send_message( - update.effective_message, - "Please write the name of the federation!", - ) - return - fednam = message.text.split(None, 1)[1] - if not fednam == "": - fed_id = str(uuid.uuid4()) - fed_name = fednam - LOGGER.info(fed_id) - - x = sql.new_fed(user.id, fed_name, fed_id) - if not x: - await update.effective_message.reply_text( - f"Can't federate! Please contact @{SUPPORT_CHAT} if the problem persist.", - ) - return - - await update.effective_message.reply_text( - "*You have succeeded in creating a new federation!*" - "\nName: `{}`" - "\nID: `{}`" - "\n\nUse the command below to join the federation:" - "\n`/joinfed {}`".format(fed_name, fed_id, fed_id), - parse_mode=ParseMode.MARKDOWN, - ) - try: - await bot.send_message( - EVENT_LOGS, - "New Federation: <b>{}</b>\nID: <pre>{}</pre>".format(fed_name, fed_id), - parse_mode=ParseMode.HTML, - ) - except: - LOGGER.warning("Cannot send a message to EVENT_LOGS") - else: - await update.effective_message.reply_text( - "Please write down the name of the federation", - ) - - -async def del_fed(update: Update, context: ContextTypes.DEFAULT_TYPE): - bot, args = context.bot, context.args - chat = update.effective_chat - user = update.effective_user - if chat.type != "private": - await update.effective_message.reply_text( - "Federations can only be deleted by privately messaging me.", - ) - return - if args: - is_fed_id = args[0] - getinfo = sql.get_fed_info(is_fed_id) - if getinfo is False: - await update.effective_message.reply_text("This federation does not exist.") - return - if int(getinfo["owner"]) == int(user.id) or int(user.id) == OWNER_ID: - fed_id = is_fed_id - else: - await update.effective_message.reply_text( - "Only federation owners can do this!" - ) - return - else: - await update.effective_message.reply_text("What should I delete?") - return - - if is_user_fed_owner(fed_id, user.id) is False: - await update.effective_message.reply_text("Only federation owners can do this!") - return - - await update.effective_message.reply_text( - "You sure you want to delete your federation? This cannot be reverted, you will lose your entire ban list, and '{}' will be permanently lost.".format( - getinfo["fname"], - ), - reply_markup=InlineKeyboardMarkup( - [ - [ - InlineKeyboardButton( - text="⚠️ Delete Federation ⚠️", - callback_data="rmfed_{}".format(fed_id), - ), - ], - [InlineKeyboardButton(text="Cancel", callback_data="rmfed_cancel")], - ], - ), - ) - - -async def rename_fed(update: Update, context: ContextTypes.DEFAULT_TYPE): - user = update.effective_user - msg = update.effective_message - args = msg.text.split(None, 2) - - if len(args) < 3: - return await msg.reply_text("usage: /renamefed <fed_id> <newname>") - - fed_id, newname = args[1], args[2] - verify_fed = sql.get_fed_info(fed_id) - - if not verify_fed: - return await msg.reply_text("This fed not exist in my database!") - - if is_user_fed_owner(fed_id, user.id): - sql.rename_fed(fed_id, user.id, newname) - await msg.reply_text(f"Successfully renamed your fed name to {newname}!") - else: - await msg.reply_text("Only federation owner can do this!") - - -async def fed_chat(update: Update, context: ContextTypes.DEFAULT_TYPE): - bot, args = context.bot, context.args - chat = update.effective_chat - user = update.effective_user - fed_id = sql.get_fed_id(chat.id) - - user_id = update.effective_message.from_user.id - if not await is_user_admin(update.effective_chat, user_id): - await update.effective_message.reply_text( - "You must be an admin to execute this command", - ) - return - - if not fed_id: - await update.effective_message.reply_text( - "This group is not in any federation!" - ) - return - - user = update.effective_user - chat = update.effective_chat - info = sql.get_fed_info(fed_id) - - text = "This group is part of the following federation:" - text += "\n{} (ID: <code>{}</code>)".format(info["fname"], fed_id) - - await update.effective_message.reply_text(text, parse_mode=ParseMode.HTML) - - -async def join_fed(update: Update, context: ContextTypes.DEFAULT_TYPE): - bot, args = context.bot, context.args - chat = update.effective_chat - user = update.effective_user - - if chat.type == "private": - await send_message( - update.effective_message, - "This command is specific to the group, not to our pm!", - ) - return - - message = update.effective_message - administrators = await chat.get_administrators() - fed_id = sql.get_fed_id(chat.id) - - if user.id in DRAGONS: - pass - else: - for admin in administrators: - status = admin.status - if status == "creator": - if str(admin.user.id) == str(user.id): - pass - else: - await update.effective_message.reply_text( - "Only group creators can use this command!", - ) - return - if fed_id: - await message.reply_text("You cannot join two federations from one chat") - return - - if len(args) >= 1: - getfed = sql.search_fed_by_id(args[0]) - if getfed is False: - await message.reply_text("Please enter a valid federation ID") - return - - x = sql.chat_join_fed(args[0], chat.title, chat.id) - if not x: - await message.reply_text( - f"Failed to join federation! Please contact @{SUPPORT_CHAT} should this problem persist!", - ) - return - - get_fedlog = await sql.get_fed_log(args[0]) - if get_fedlog: - if ast.literal_eval(get_fedlog): - await bot.send_message( - get_fedlog, - "Chat *{}* has joined the federation *{}*".format( - chat.title, - getfed["fname"], - ), - parse_mode="markdown", - message_thread_id=message.message_thread_id - if chat.is_forum - else None, - ) - - await message.reply_text( - "This group has joined the federation: {}!".format(getfed["fname"]), - ) - - -async def leave_fed(update: Update, context: ContextTypes.DEFAULT_TYPE): - bot, args = context.bot, context.args - chat = update.effective_chat - user = update.effective_user - - if chat.type == "private": - await send_message( - update.effective_message, - "This command is specific to the group, not to our PM!", - ) - return - - fed_id = sql.get_fed_id(chat.id) - fed_info = sql.get_fed_info(fed_id) - - # administrators = await chat.get_administrators().status - getuser = await bot.get_chat_member(chat.id, user.id).status - if getuser in "creator" or user.id in DRAGONS: - if sql.chat_leave_fed(chat.id) is True: - get_fedlog = await sql.get_fed_log(fed_id) - if get_fedlog: - if ast.literal_eval(get_fedlog): - await bot.send_message( - get_fedlog, - "Chat *{}* has left the federation *{}*".format( - chat.title, - fed_info["fname"], - ), - parse_mode="markdown", - message_thread_id=update.effective_message.message_thread_id - if chat.is_forum - else None, - ) - await send_message( - update.effective_message, - "This group has left the federation {}!".format(fed_info["fname"]), - ) - else: - await update.effective_message.reply_text( - "How can you leave a federation that you never joined?!", - ) - else: - await update.effective_message.reply_text( - "Only group creators can use this command!" - ) - - -async def user_join_fed(update: Update, context: ContextTypes.DEFAULT_TYPE): - bot, args = context.bot, context.args - chat = update.effective_chat - user = update.effective_user - msg = update.effective_message - - if chat.type == "private": - await send_message( - update.effective_message, - "This command is specific to the group, not to our pm!", - ) - return - - fed_id = sql.get_fed_id(chat.id) - - if is_user_fed_owner(fed_id, user.id) or user.id in DRAGONS: - user_id = await extract_user(msg, context, args) - if user_id: - user = await bot.get_chat(user_id) - elif not msg.reply_to_message and not args: - user = msg.from_user - elif not msg.reply_to_message and ( - not args - or ( - len(args) >= 1 - and not args[0].startswith("@") - and not args[0].isdigit() - and not msg.parse_entities([MessageEntity.TEXT_MENTION]) - ) - ): - await msg.reply_text("I cannot extract user from this message") - return - else: - LOGGER.warning("error") - getuser = sql.search_user_in_fed(fed_id, user_id) - fed_id = sql.get_fed_id(chat.id) - info = sql.get_fed_info(fed_id) - get_owner = ast.literal_eval(info["fusers"])["owner"] - get_owner = await bot.get_chat(get_owner) - if isinstance(get_owner, ChatMember): - if user_id == get_owner.id: - await update.effective_message.reply_text( - "You do know that the user is the federation owner, right? RIGHT?", - ) - return - if getuser: - await update.effective_message.reply_text( - "I cannot promote users who are already federation admins! Can remove them if you want!", - ) - return - if user_id == bot.id: - await update.effective_message.reply_text( - "I already am a federation admin in all federations!", - ) - return - res = sql.user_join_fed(fed_id, user_id) - if res: - await update.effective_message.reply_text("Successfully Promoted!") - else: - await update.effective_message.reply_text("Failed to promote!") - else: - await update.effective_message.reply_text("Only federation owners can do this!") - - -async def user_demote_fed(update: Update, context: ContextTypes.DEFAULT_TYPE): - bot, args = context.bot, context.args - chat = update.effective_chat - user = update.effective_user - - if chat.type == "private": - await send_message( - update.effective_message, - "This command is specific to the group, not to our pm!", - ) - return - - fed_id = sql.get_fed_id(chat.id) - - if is_user_fed_owner(fed_id, user.id): - msg = update.effective_message - user_id = await extract_user(msg, context, args) - if user_id: - user = await bot.get_chat(user_id) - - elif not msg.reply_to_message and not args: - user = msg.from_user - - elif not msg.reply_to_message and ( - not args - or ( - len(args) >= 1 - and not args[0].startswith("@") - and not args[0].isdigit() - and not msg.parse_entities([MessageEntity.TEXT_MENTION]) - ) - ): - await msg.reply_text("I cannot extract user from this message") - return - else: - LOGGER.warning("error") - - if user_id == bot.id: - await update.effective_message.reply_text( - "The thing you are trying to demote me from will fail to work without me! Just saying.", - ) - return - - if sql.search_user_in_fed(fed_id, user_id) is False: - await update.effective_message.reply_text( - "I cannot demote people who are not federation admins!", - ) - return - - res = sql.user_demote_fed(fed_id, user_id) - if res is True: - await update.effective_message.reply_text("Demoted from a Fed Admin!") - else: - await update.effective_message.reply_text("Demotion failed!") - else: - await update.effective_message.reply_text("Only federation owners can do this!") - return - - -async def fed_info(update: Update, context: ContextTypes.DEFAULT_TYPE): - bot, args = context.bot, context.args - chat = update.effective_chat - user = update.effective_user - if args: - fed_id = args[0] - info = sql.get_fed_info(fed_id) - else: - if chat.type == "private": - await send_message( - update.effective_message, - "You need to provide me a fedid to check fedinfo in my pm.", - ) - return - fed_id = sql.get_fed_id(chat.id) - if not fed_id: - await send_message( - update.effective_message, - "This group is not in any federation!", - ) - return - info = sql.get_fed_info(fed_id) - - if is_user_fed_admin(fed_id, user.id) is False: - await update.effective_message.reply_text( - "Only a federation admin can do this!" - ) - return - - owner = await bot.get_chat(info["owner"]) - try: - owner_name = owner.first_name + " " + owner.last_name - except: - owner_name = owner.first_name - FEDADMIN = sql.all_fed_users(fed_id) - TotalAdminFed = len(FEDADMIN) - - user = update.effective_user - chat = update.effective_chat - info = sql.get_fed_info(fed_id) - - text = "<b>ℹ️ Federation Information:</b>" - text += "\nFedID: <code>{}</code>".format(fed_id) - text += "\nName: {}".format(info["fname"]) - text += "\nCreator: {}".format(mention_html(owner.id, owner_name)) - text += "\nAll Admins: <code>{}</code>".format(TotalAdminFed) - getfban = sql.get_all_fban_users(fed_id) - text += "\nTotal banned users: <code>{}</code>".format(len(getfban)) - getfchat = sql.all_fed_chats(fed_id) - text += "\nNumber of groups in this federation: <code>{}</code>".format( - len(getfchat), - ) - - await update.effective_message.reply_text(text, parse_mode=ParseMode.HTML) - - -async def fed_admin(update: Update, context: ContextTypes.DEFAULT_TYPE): - bot, args = context.bot, context.args - chat = update.effective_chat - user = update.effective_user - - if chat.type == "private": - await send_message( - update.effective_message, - "This command is specific to the group, not to our pm!", - ) - return - - fed_id = sql.get_fed_id(chat.id) - - if not fed_id: - await update.effective_message.reply_text( - "This group is not in any federation!" - ) - return - - if is_user_fed_admin(fed_id, user.id) is False: - await update.effective_message.reply_text("Only federation admins can do this!") - return - - user = update.effective_user - chat = update.effective_chat - info = sql.get_fed_info(fed_id) - - text = "<b>Federation Admin {}:</b>\n\n".format(info["fname"]) - text += "👑 Owner:\n" - owner = await bot.get_chat(info["owner"]) - try: - owner_name = owner.first_name + " " + owner.last_name - except: - owner_name = owner.first_name - text += " • {}\n".format(mention_html(owner.id, owner_name)) - - members = sql.all_fed_members(fed_id) - if len(members) == 0: - text += "\n🔱 There are no admins in this federation" - else: - text += "\n🔱 Admin:\n" - for x in members: - user = await bot.get_chat(x) - text += " • {}\n".format(mention_html(user.id, user.first_name)) - - await update.effective_message.reply_text(text, parse_mode=ParseMode.HTML) - - -async def fed_ban(update: Update, context: ContextTypes.DEFAULT_TYPE): - bot, args = context.bot, context.args - chat = update.effective_chat - user = update.effective_user - - if chat.type == "private": - await send_message( - update.effective_message, - "This command is specific to the group, not to our pm!", - ) - return - - fed_id = sql.get_fed_id(chat.id) - - if not fed_id: - await update.effective_message.reply_text( - "This group is not a part of any federation!", - ) - return - - info = sql.get_fed_info(fed_id) - getfednotif = sql.user_feds_report(info["owner"]) - - if is_user_fed_admin(fed_id, user.id) is False: - await update.effective_message.reply_text("Only federation admins can do this!") - return - - message = update.effective_message - - user_id, reason = extract_unt_fedban(message, context, args) - - fban, fbanreason, fbantime = sql.get_fban_user(fed_id, user_id) - - if not user_id: - await message.reply_text("You don't seem to be referring to a user") - return - - if user_id == bot.id: - await message.reply_text( - "What is funnier than kicking the group creator? Self sacrifice.", - ) - return - - if is_user_fed_owner(fed_id, user_id) is True: - await message.reply_text("Why did you try the federation fban?") - return - - if is_user_fed_admin(fed_id, user_id) is True: - await message.reply_text("He is a federation admin, I can't fban him.") - return - - if user_id == OWNER_ID: - await message.reply_text("Disaster level God cannot be fed banned!") - return - - if int(user_id) in DRAGONS: - await message.reply_text("Dragons cannot be fed banned!") - return - - if user_id in [777000, 1087968824]: - await message.reply_text("Fool! You can't attack Telegram's native tech!") - return - - try: - user_chat = await bot.get_chat(user_id) - isvalid = True - fban_user_id = user_chat.id - fban_user_name = user_chat.first_name - fban_user_lname = user_chat.last_name - fban_user_uname = user_chat.username - except BadRequest as excp: - if not str(user_id).isdigit(): - await send_message(update.effective_message, excp.message) - return - elif len(str(user_id)) != 9: - await send_message(update.effective_message, "That's so not a user!") - return - isvalid = False - fban_user_id = int(user_id) - fban_user_name = "user({})".format(user_id) - fban_user_lname = None - fban_user_uname = None - - if isvalid and user_chat.type != "private": - await send_message(update.effective_message, "That's so not a user!") - return - - if isvalid: - user_target = mention_html(fban_user_id, fban_user_name) - else: - user_target = fban_user_name - - if fban: - fed_name = info["fname"] - # https://t.me/OnePunchSupport/41606 // https://t.me/OnePunchSupport/41619 - # starting = "The reason fban is replaced for {} in the Federation <b>{}</b>.".format(user_target, fed_name) - # await send_message(update.effective_message, starting, parse_mode=ParseMode.HTML) - - # if reason == "": - # reason = "No reason given." - - temp = sql.un_fban_user(fed_id, fban_user_id) - if not temp: - await message.reply_text("Failed to update the reason for fedban!") - return - x = sql.fban_user( - fed_id, - fban_user_id, - fban_user_name, - fban_user_lname, - fban_user_uname, - reason, - int(time.time()), - ) - if not x: - await message.reply_text( - f"Failed to ban from the federation! If this problem continues, contact @{SUPPORT_CHAT}.", - ) - return - - fed_chats = sql.all_fed_chats(fed_id) - # Will send to current chat - await bot.send_message( - chat.id, - "<b>FedBan reason updated</b>" - "\n<b>Federation:</b> {}" - "\n<b>Federation Admin:</b> {}" - "\n<b>User:</b> {}" - "\n<b>User ID:</b> <code>{}</code>" - "\n<b>Reason:</b> {}".format( - fed_name, - mention_html(user.id, user.first_name), - user_target, - fban_user_id, - reason, - ), - parse_mode="HTML", - ) - # Send message to owner if fednotif is enabled - if getfednotif: - await bot.send_message( - info["owner"], - "<b>FedBan reason updated</b>" - "\n<b>Federation:</b> {}" - "\n<b>Federation Admin:</b> {}" - "\n<b>User:</b> {}" - "\n<b>User ID:</b> <code>{}</code>" - "\n<b>Reason:</b> {}".format( - fed_name, - mention_html(user.id, user.first_name), - user_target, - fban_user_id, - reason, - ), - parse_mode="HTML", - ) - # If fedlog is set, then send message, except fedlog is current chat - get_fedlog = await sql.get_fed_log(fed_id) - if get_fedlog: - if int(get_fedlog) != int(chat.id): - await bot.send_message( - get_fedlog, - "<b>FedBan reason updated</b>" - "\n<b>Federation:</b> {}" - "\n<b>Federation Admin:</b> {}" - "\n<b>User:</b> {}" - "\n<b>User ID:</b> <code>{}</code>" - "\n<b>Reason:</b> {}".format( - fed_name, - mention_html(user.id, user.first_name), - user_target, - fban_user_id, - reason, - ), - parse_mode="HTML", - ) - for fedschat in fed_chats: - try: - # Do not spam all fed chats - """ - bot.send_message(chat, "<b>FedBan reason updated</b>" \ - "\n<b>Federation:</b> {}" \ - "\n<b>Federation Admin:</b> {}" \ - "\n<b>User:</b> {}" \ - "\n<b>User ID:</b> <code>{}</code>" \ - "\n<b>Reason:</b> {}".format(fed_name, mention_html(user.id, user.first_name), user_target, fban_user_id, reason), parse_mode="HTML") - """ - await bot.ban_chat_member(fedschat, fban_user_id) - except BadRequest as excp: - if excp.message in FBAN_ERRORS: - try: - await dispatcher.bot.getChat(fedschat) - except Forbidden: - sql.chat_leave_fed(fedschat) - LOGGER.info( - "Chat {} has leave fed {} because I was kicked".format( - fedschat, - info["fname"], - ), - ) - continue - elif excp.message == "User_id_invalid": - break - else: - LOGGER.warning( - "Could not fban on {} because: {}".format(chat, excp.message), - ) - except TelegramError: - pass - # Also do not spam all fed admins - """ - send_to_list(bot, FEDADMIN, - "<b>FedBan reason updated</b>" \ - "\n<b>Federation:</b> {}" \ - "\n<b>Federation Admin:</b> {}" \ - "\n<b>User:</b> {}" \ - "\n<b>User ID:</b> <code>{}</code>" \ - "\n<b>Reason:</b> {}".format(fed_name, mention_html(user.id, user.first_name), user_target, fban_user_id, reason), - html=True) - """ - - # Fban for fed subscriber - subscriber = list(sql.get_subscriber(fed_id)) - if len(subscriber) != 0: - for fedsid in subscriber: - all_fedschat = sql.all_fed_chats(fedsid) - for fedschat in all_fedschat: - try: - await bot.ban_chat_member(fedschat, fban_user_id) - except BadRequest as excp: - if excp.message in FBAN_ERRORS: - try: - await dispatcher.bot.getChat(fedschat) - except Forbidden: - targetfed_id = sql.get_fed_id(fedschat) - sql.unsubs_fed(fed_id, targetfed_id) - LOGGER.info( - "Chat {} has unsub fed {} because I was kicked".format( - fedschat, - info["fname"], - ), - ) - continue - elif excp.message == "User_id_invalid": - break - else: - LOGGER.warning( - "Unable to fban on {} because: {}".format( - fedschat, - excp.message, - ), - ) - except TelegramError: - pass - # await send_message(update.effective_message, "Fedban Reason has been updated.") - return - - fed_name = info["fname"] - - # starting = "Starting a federation ban for {} in the Federation <b>{}</b>.".format( - # user_target, fed_name) - # await update.effective_message.reply_text(starting, parse_mode=ParseMode.HTML) - - # if reason == "": - # reason = "No reason given." - - x = sql.fban_user( - fed_id, - fban_user_id, - fban_user_name, - fban_user_lname, - fban_user_uname, - reason, - int(time.time()), - ) - if not x: - await message.reply_text( - f"Failed to ban from the federation! If this problem continues, contact @{SUPPORT_CHAT}.", - ) - return - - fed_chats = sql.all_fed_chats(fed_id) - # Will send to current chat - await bot.send_message( - chat.id, - "<b>New FedBan</b>" - "\n<b>Federation:</b> {}" - "\n<b>Federation Admin:</b> {}" - "\n<b>User:</b> {}" - "\n<b>User ID:</b> <code>{}</code>" - "\n<b>Reason:</b> {}".format( - fed_name, - mention_html(user.id, user.first_name), - user_target, - fban_user_id, - reason, - ), - parse_mode="HTML", - ) - # Send message to owner if fednotif is enabled - if getfednotif: - await bot.send_message( - info["owner"], - "<b>New FedBan</b>" - "\n<b>Federation:</b> {}" - "\n<b>Federation Admin:</b> {}" - "\n<b>User:</b> {}" - "\n<b>User ID:</b> <code>{}</code>" - "\n<b>Reason:</b> {}".format( - fed_name, - mention_html(user.id, user.first_name), - user_target, - fban_user_id, - reason, - ), - parse_mode="HTML", - ) - # If fedlog is set, then send message, except fedlog is current chat - get_fedlog = await sql.get_fed_log(fed_id) - if get_fedlog: - if int(get_fedlog) != int(chat.id): - await bot.send_message( - get_fedlog, - "<b>New FedBan</b>" - "\n<b>Federation:</b> {}" - "\n<b>Federation Admin:</b> {}" - "\n<b>User:</b> {}" - "\n<b>User ID:</b> <code>{}</code>" - "\n<b>Reason:</b> {}".format( - fed_name, - mention_html(user.id, user.first_name), - user_target, - fban_user_id, - reason, - ), - parse_mode="HTML", - ) - chats_in_fed = 0 - for fedschat in fed_chats: - chats_in_fed += 1 - try: - # Do not spamming all fed chats - """ - bot.send_message(chat, "<b>FedBan reason updated</b>" \ - "\n<b>Federation:</b> {}" \ - "\n<b>Federation Admin:</b> {}" \ - "\n<b>User:</b> {}" \ - "\n<b>User ID:</b> <code>{}</code>" \ - "\n<b>Reason:</b> {}".format(fed_name, mention_html(user.id, user.first_name), user_target, fban_user_id, reason), parse_mode="HTML") - """ - await bot.ban_chat_member(fedschat, fban_user_id) - except BadRequest as excp: - if excp.message in FBAN_ERRORS: - pass - elif excp.message == "User_id_invalid": - break - else: - LOGGER.warning( - "Could not fban on {} because: {}".format(chat, excp.message), - ) - except TelegramError: - pass - - # Also do not spamming all fed admins - """ - send_to_list(bot, FEDADMIN, - "<b>FedBan reason updated</b>" \ - "\n<b>Federation:</b> {}" \ - "\n<b>Federation Admin:</b> {}" \ - "\n<b>User:</b> {}" \ - "\n<b>User ID:</b> <code>{}</code>" \ - "\n<b>Reason:</b> {}".format(fed_name, mention_html(user.id, user.first_name), user_target, fban_user_id, reason), - html=True) - """ - - # Fban for fed subscriber - subscriber = list(sql.get_subscriber(fed_id)) - if len(subscriber) != 0: - for fedsid in subscriber: - all_fedschat = sql.all_fed_chats(fedsid) - for fedschat in all_fedschat: - try: - await bot.ban_chat_member(fedschat, fban_user_id) - except BadRequest as excp: - if excp.message in FBAN_ERRORS: - try: - await dispatcher.bot.getChat(fedschat) - except Forbidden: - targetfed_id = sql.get_fed_id(fedschat) - sql.unsubs_fed(fed_id, targetfed_id) - LOGGER.info( - "Chat {} has unsub fed {} because I was kicked".format( - fedschat, - info["fname"], - ), - ) - continue - elif excp.message == "User_id_invalid": - break - else: - LOGGER.warning( - "Unable to fban on {} because: {}".format( - fedschat, - excp.message, - ), - ) - except TelegramError: - pass - - -async def unfban(update: Update, context: ContextTypes.DEFAULT_TYPE): - bot, args = context.bot, context.args - chat = update.effective_chat - user = update.effective_user - message = update.effective_message - - if chat.type == "private": - await send_message( - update.effective_message, - "This command is specific to the group, not to our pm!", - ) - return - - fed_id = sql.get_fed_id(chat.id) - - if not fed_id: - await update.effective_message.reply_text( - "This group is not a part of any federation!", - ) - return - - info = sql.get_fed_info(fed_id) - getfednotif = sql.user_feds_report(info["owner"]) - - if is_user_fed_admin(fed_id, user.id) is False: - await update.effective_message.reply_text("Only federation admins can do this!") - return - - user_id = extract_user_fban(message, context, args) - if not user_id: - await message.reply_text("You do not seem to be referring to a user.") - return - - try: - user_chat = await bot.get_chat(user_id) - isvalid = True - fban_user_id = user_chat.id - fban_user_name = user_chat.first_name - fban_user_lname = user_chat.last_name - fban_user_uname = user_chat.username - except BadRequest as excp: - if not str(user_id).isdigit(): - await send_message(update.effective_message, excp.message) - return - elif len(str(user_id)) != 9: - await send_message(update.effective_message, "That's so not a user!") - return - isvalid = False - fban_user_id = int(user_id) - fban_user_name = "user({})".format(user_id) - fban_user_lname = None - fban_user_uname = None - - if isvalid and user_chat.type != "private": - await message.reply_text("That's so not a user!") - return - - if isvalid: - user_target = mention_html(fban_user_id, fban_user_name) - else: - user_target = fban_user_name - - fban, fbanreason, fbantime = sql.get_fban_user(fed_id, fban_user_id) - if fban is False: - await message.reply_text("This user is not fbanned!") - return - - banner = update.effective_user - - chat_list = sql.all_fed_chats(fed_id) - # Will send to current chat - await bot.send_message( - chat.id, - "<b>Un-FedBan</b>" - "\n<b>Federation:</b> {}" - "\n<b>Federation Admin:</b> {}" - "\n<b>User:</b> {}" - "\n<b>User ID:</b> <code>{}</code>".format( - info["fname"], - mention_html(user.id, user.first_name), - user_target, - fban_user_id, - ), - parse_mode="HTML", - ) - # Send message to owner if fednotif is enabled - if getfednotif: - await bot.send_message( - info["owner"], - "<b>Un-FedBan</b>" - "\n<b>Federation:</b> {}" - "\n<b>Federation Admin:</b> {}" - "\n<b>User:</b> {}" - "\n<b>User ID:</b> <code>{}</code>".format( - info["fname"], - mention_html(user.id, user.first_name), - user_target, - fban_user_id, - ), - parse_mode="HTML", - ) - # If fedlog is set, then send message, except fedlog is current chat - get_fedlog = await sql.get_fed_log(fed_id) - if get_fedlog: - if int(get_fedlog) != int(chat.id): - await bot.send_message( - get_fedlog, - "<b>Un-FedBan</b>" - "\n<b>Federation:</b> {}" - "\n<b>Federation Admin:</b> {}" - "\n<b>User:</b> {}" - "\n<b>User ID:</b> <code>{}</code>".format( - info["fname"], - mention_html(user.id, user.first_name), - user_target, - fban_user_id, - ), - parse_mode="HTML", - ) - unfbanned_in_chats = 0 - for fedchats in chat_list: - unfbanned_in_chats += 1 - try: - member = await bot.get_chat_member(fedchats, user_id) - if member.status == "kicked": - await bot.unban_chat_member(fedchats, user_id) - # Do not spamming all fed chats - """ - bot.send_message(chat, "<b>Un-FedBan</b>" \ - "\n<b>Federation:</b> {}" \ - "\n<b>Federation Admin:</b> {}" \ - "\n<b>User:</b> {}" \ - "\n<b>User ID:</b> <code>{}</code>".format(info['fname'], mention_html(user.id, user.first_name), user_target, fban_user_id), parse_mode="HTML") - """ - except BadRequest as excp: - if excp.message in UNFBAN_ERRORS: - pass - elif excp.message == "User_id_invalid": - break - else: - LOGGER.warning( - "Could not fban on {} because: {}".format(chat, excp.message), - ) - except TelegramError: - pass - - try: - x = sql.un_fban_user(fed_id, user_id) - if not x: - await send_message( - update.effective_message, - "Un-fban failed, this user may already be un-fedbanned!", - ) - return - except: - pass - - # UnFban for fed subscriber - subscriber = list(sql.get_subscriber(fed_id)) - if len(subscriber) != 0: - for fedsid in subscriber: - all_fedschat = sql.all_fed_chats(fedsid) - for fedschat in all_fedschat: - try: - await bot.unban_chat_member(fedchats, user_id) - except BadRequest as excp: - if excp.message in FBAN_ERRORS: - try: - await dispatcher.bot.getChat(fedschat) - except Forbidden: - targetfed_id = sql.get_fed_id(fedschat) - sql.unsubs_fed(fed_id, targetfed_id) - LOGGER.info( - "Chat {} has unsub fed {} because I was kicked".format( - fedschat, - info["fname"], - ), - ) - continue - elif excp.message == "User_id_invalid": - break - else: - LOGGER.warning( - "Unable to fban on {} because: {}".format( - fedschat, - excp.message, - ), - ) - except TelegramError: - pass - - if unfbanned_in_chats == 0: - await send_message( - update.effective_message, - "This person has been un-fbanned in 0 chats.", - ) - if unfbanned_in_chats > 0: - await send_message( - update.effective_message, - "This person has been un-fbanned in {} chats.".format(unfbanned_in_chats), - ) - # Also do not spamming all fed admins - """ - FEDADMIN = sql.all_fed_users(fed_id) - for x in FEDADMIN: - getreport = sql.user_feds_report(x) - if getreport is False: - FEDADMIN.remove(x) - send_to_list(bot, FEDADMIN, - "<b>Un-FedBan</b>" \ - "\n<b>Federation:</b> {}" \ - "\n<b>Federation Admin:</b> {}" \ - "\n<b>User:</b> {}" \ - "\n<b>User ID:</b> <code>{}</code>".format(info['fname'], mention_html(user.id, user.first_name), - mention_html(user_chat.id, user_chat.first_name), - user_chat.id), - html=True) - """ - - -async def set_frules(update: Update, context: ContextTypes.DEFAULT_TYPE): - bot, args = context.bot, context.args - chat = update.effective_chat - user = update.effective_user - - if chat.type == "private": - await send_message( - update.effective_message, - "This command is specific to the group, not to our pm!", - ) - return - - fed_id = sql.get_fed_id(chat.id) - - if not fed_id: - await update.effective_message.reply_text( - "This group is not in any federation!" - ) - return - - if is_user_fed_admin(fed_id, user.id) is False: - await update.effective_message.reply_text("Only fed admins can do this!") - return - - if len(args) >= 1: - msg = update.effective_message - raw_text = msg.text - args = raw_text.split(None, 1) # use python's maxsplit to separate cmd and args - if len(args) == 2: - txt = args[1] - offset = len(txt) - len(raw_text) # set correct offset relative to command - markdown_rules = markdown_parser( - txt, - entities=msg.parse_entities(), - offset=offset, - ) - x = sql.set_frules(fed_id, markdown_rules) - if not x: - await update.effective_message.reply_text( - f"Whoa! There was an error while setting federation rules! If you wondered why please ask it in @{SUPPORT_CHAT}!", - ) - return - - rules = sql.get_fed_info(fed_id)["frules"] - getfed = sql.get_fed_info(fed_id) - get_fedlog = await sql.get_fed_log(fed_id) - if get_fedlog: - if ast.literal_eval(get_fedlog): - await bot.send_message( - get_fedlog, - "*{}* has updated federation rules for fed *{}*".format( - user.first_name, - getfed["fname"], - ), - parse_mode="markdown", - message_thread_id=update.effective_message.message_thread_id - if chat.is_forum - else None, - ) - await update.effective_message.reply_text( - f"Rules have been changed to :\n{rules}!" - ) - else: - await update.effective_message.reply_text("Please write rules to set this up!") - - -async def get_frules(update: Update, context: ContextTypes.DEFAULT_TYPE): - bot, args = context.bot, context.args - chat = update.effective_chat - - if chat.type == "private": - await send_message( - update.effective_message, - "This command is specific to the group, not to our pm!", - ) - return - - fed_id = sql.get_fed_id(chat.id) - if not fed_id: - await update.effective_message.reply_text( - "This group is not in any federation!" - ) - return - - rules = sql.get_frules(fed_id) - text = "*Rules in this fed:*\n" - text += rules - await update.effective_message.reply_text(text, parse_mode=ParseMode.MARKDOWN) - - -async def fed_broadcast(update: Update, context: ContextTypes.DEFAULT_TYPE): - bot, args = context.bot, context.args - msg = update.effective_message - user = update.effective_user - chat = update.effective_chat - - if chat.type == "private": - await send_message( - update.effective_message, - "This command is specific to the group, not to our pm!", - ) - return - - if args: - chat = update.effective_chat - fed_id = sql.get_fed_id(chat.id) - fedinfo = sql.get_fed_info(fed_id) - if is_user_fed_owner(fed_id, user.id) is False: - await update.effective_message.reply_text( - "Only federation owners can do this!" - ) - return - # Parsing md - raw_text = msg.text - args = raw_text.split(None, 1) # use python's maxsplit to separate cmd and args - txt = args[1] - offset = len(txt) - len(raw_text) # set correct offset relative to command - text_parser = markdown_parser(txt, entities=msg.parse_entities(), offset=offset) - text = text_parser - try: - broadcaster = user.first_name - except: - broadcaster = user.first_name + " " + user.last_name - text += "\n\n- {}".format(mention_markdown(user.id, broadcaster)) - chat_list = sql.all_fed_chats(fed_id) - failed = 0 - for chat in chat_list: - title = "*New broadcast from Fed {}*\n".format(fedinfo["fname"]) - try: - await bot.sendMessage( - chat, - title + text, - parse_mode="markdown", - message_thread_id=msg.message_thread_id if chat.is_forum else None, - ) - except TelegramError: - try: - await dispatcher.bot.getChat(chat) - except Forbidden: - failed += 1 - sql.chat_leave_fed(chat) - LOGGER.info( - "Chat {} has left fed {} because I was kicked".format( - chat, - fedinfo["fname"], - ), - ) - continue - failed += 1 - LOGGER.warning("Couldn't send broadcast to {}".format(str(chat))) - - send_text = "The federation broadcast is complete" - if failed >= 1: - send_text += "{} the group failed to receive the message, probably because it left the Federation.".format( - failed, - ) - await update.effective_message.reply_text(send_text) - - -async def fed_ban_list(update: Update, context: ContextTypes.DEFAULT_TYPE): - bot, args, chat_data = context.bot, context.args, context.chat_data - chat = update.effective_chat - user = update.effective_user - - if chat.type == "private": - await send_message( - update.effective_message, - "This command is specific to the group, not to our pm!", - ) - return - - fed_id = sql.get_fed_id(chat.id) - info = sql.get_fed_info(fed_id) - - if not fed_id: - await update.effective_message.reply_text( - "This group is not a part of any federation!", - ) - return - - if is_user_fed_owner(fed_id, user.id) is False: - await update.effective_message.reply_text("Only Federation owners can do this!") - return - - user = update.effective_user - chat = update.effective_chat - getfban = sql.get_all_fban_users(fed_id) - if len(getfban) == 0: - await update.effective_message.reply_text( - "The federation ban list of {} is empty".format(info["fname"]), - parse_mode=ParseMode.HTML, - ) - return - - if args: - if args[0] == "json": - jam = time.time() - new_jam = jam + 1800 - cek = get_chat(chat.id, chat_data) - if cek.get("status"): - if jam <= int(cek.get("value")): - waktu = time.strftime( - "%H:%M:%S %d/%m/%Y", - time.localtime(cek.get("value")), - ) - await update.effective_message.reply_text( - "You can backup your data once every 30 minutes!\nYou can back up data again at `{}`".format( - waktu, - ), - parse_mode=ParseMode.MARKDOWN, - ) - return - else: - if user.id not in DRAGONS: - put_chat(chat.id, new_jam, chat_data) - else: - if user.id not in DRAGONS: - put_chat(chat.id, new_jam, chat_data) - backups = "" - for users in getfban: - getuserinfo = sql.get_all_fban_users_target(fed_id, users) - json_parser = { - "user_id": users, - "first_name": getuserinfo["first_name"], - "last_name": getuserinfo["last_name"], - "user_name": getuserinfo["user_name"], - "reason": getuserinfo["reason"], - } - backups += json.dumps(json_parser) - backups += "\n" - with BytesIO(str.encode(backups)) as output: - output.name = "mikobot_fbanned_users.json" - await update.effective_message.reply_document( - document=output, - filename="mikobot_fbanned_users.json", - caption="Total {} User are blocked by the Federation {}.".format( - len(getfban), - info["fname"], - ), - ) - return - elif args[0] == "csv": - jam = time.time() - new_jam = jam + 1800 - cek = get_chat(chat.id, chat_data) - if cek.get("status"): - if jam <= int(cek.get("value")): - waktu = time.strftime( - "%H:%M:%S %d/%m/%Y", - time.localtime(cek.get("value")), - ) - await update.effective_message.reply_text( - "You can back up data once every 30 minutes!\nYou can back up data again at `{}`".format( - waktu, - ), - parse_mode=ParseMode.MARKDOWN, - ) - return - else: - if user.id not in DRAGONS: - put_chat(chat.id, new_jam, chat_data) - else: - if user.id not in DRAGONS: - put_chat(chat.id, new_jam, chat_data) - backups = "id,firstname,lastname,username,reason\n" - for users in getfban: - getuserinfo = sql.get_all_fban_users_target(fed_id, users) - backups += ( - "{user_id},{first_name},{last_name},{user_name},{reason}".format( - user_id=users, - first_name=getuserinfo["first_name"], - last_name=getuserinfo["last_name"], - user_name=getuserinfo["user_name"], - reason=getuserinfo["reason"], - ) - ) - backups += "\n" - with BytesIO(str.encode(backups)) as output: - output.name = "mikobot_fbanned_users.csv" - await update.effective_message.reply_document( - document=output, - filename="mikobot_fbanned_users.csv", - caption="Total {} User are blocked by Federation {}.".format( - len(getfban), - info["fname"], - ), - ) - return - - text = "<b>{} users have been banned from the federation {}:</b>\n".format( - len(getfban), - info["fname"], - ) - for users in getfban: - getuserinfo = sql.get_all_fban_users_target(fed_id, users) - if getuserinfo is False: - text = "There are no users banned from the federation {}".format( - info["fname"], - ) - break - user_name = getuserinfo["first_name"] - if getuserinfo["last_name"]: - user_name += " " + getuserinfo["last_name"] - text += " • {} (<code>{}</code>)\n".format( - mention_html(users, user_name), - users, - ) - - try: - await update.effective_message.reply_text(text, parse_mode=ParseMode.HTML) - except: - jam = time.time() - new_jam = jam + 1800 - cek = get_chat(chat.id, chat_data) - if cek.get("status"): - if jam <= int(cek.get("value")): - waktu = time.strftime( - "%H:%M:%S %d/%m/%Y", - time.localtime(cek.get("value")), - ) - await update.effective_message.reply_text( - "You can back up data once every 30 minutes!\nYou can back up data again at `{}`".format( - waktu, - ), - parse_mode=ParseMode.MARKDOWN, - ) - return - else: - if user.id not in DRAGONS: - put_chat(chat.id, new_jam, chat_data) - else: - if user.id not in DRAGONS: - put_chat(chat.id, new_jam, chat_data) - cleanr = re.compile("<.*?>") - cleantext = re.sub(cleanr, "", text) - with BytesIO(str.encode(cleantext)) as output: - output.name = "fbanlist.txt" - await update.effective_message.reply_document( - document=output, - filename="fbanlist.txt", - caption="The following is a list of users who are currently fbanned in the Federation {}.".format( - info["fname"], - ), - ) - - -async def fed_notif(update: Update, context: ContextTypes.DEFAULT_TYPE): - bot, args = context.bot, context.args - chat = update.effective_chat - user = update.effective_user - msg = update.effective_message - fed_id = sql.get_fed_id(chat.id) - - if not fed_id: - await update.effective_message.reply_text( - "This group is not a part of any federation!", - ) - return - - if args: - if args[0] in ("yes", "on"): - sql.set_feds_setting(user.id, True) - await msg.reply_text( - "Reporting Federation back up! Every user who is fban / unfban you will be notified via PM.", - ) - elif args[0] in ("no", "off"): - sql.set_feds_setting(user.id, False) - await msg.reply_text( - "Reporting Federation has stopped! Every user who is fban / unfban you will not be notified via PM.", - ) - else: - await msg.reply_text("Please enter `on`/`off`", parse_mode="markdown") - else: - getreport = sql.user_feds_report(user.id) - await msg.reply_text( - "Your current Federation report preferences: `{}`".format(getreport), - parse_mode="markdown", - ) - - -async def fed_chats(update: Update, context: ContextTypes.DEFAULT_TYPE): - bot, args = context.bot, context.args - chat = update.effective_chat - user = update.effective_user - - if chat.type == "private": - await send_message( - update.effective_message, - "This command is specific to the group, not to our pm!", - ) - return - - fed_id = sql.get_fed_id(chat.id) - info = sql.get_fed_info(fed_id) - - if not fed_id: - await update.effective_message.reply_text( - "This group is not a part of any federation!", - ) - return - - if is_user_fed_admin(fed_id, user.id) is False: - await update.effective_message.reply_text("Only federation admins can do this!") - return - - getlist = sql.all_fed_chats(fed_id) - if len(getlist) == 0: - await update.effective_message.reply_text( - "No users are fbanned from the federation {}".format(info["fname"]), - parse_mode=ParseMode.HTML, - ) - return - - text = "<b>New chat joined the federation {}:</b>\n".format(info["fname"]) - for chats in getlist: - try: - chat_obj = await dispatcher.bot.getChat(chats) - chat_name = chat_obj.title - except Forbidden: - sql.chat_leave_fed(chats) - LOGGER.info( - "Chat {} has leave fed {} because I was kicked".format( - chats, - info["fname"], - ), - ) - continue - text += " • {} (<code>{}</code>)\n".format(chat_name, chats) - - try: - await update.effective_message.reply_text(text, parse_mode=ParseMode.HTML) - except: - cleanr = re.compile("<.*?>") - cleantext = re.sub(cleanr, "", text) - with BytesIO(str.encode(cleantext)) as output: - output.name = "fedchats.txt" - await update.effective_message.reply_document( - document=output, - filename="fedchats.txt", - caption="Here is a list of all the chats that joined the federation {}.".format( - info["fname"], - ), - ) - - -async def fed_import_bans(update: Update, context: ContextTypes.DEFAULT_TYPE): - bot, chat_data = context.bot, context.chat_data - chat = update.effective_chat - user = update.effective_user - msg = update.effective_message - - if chat.type == "private": - await send_message( - update.effective_message, - "This command is specific to the group, not to our pm!", - ) - return - - fed_id = sql.get_fed_id(chat.id) - info = sql.get_fed_info(fed_id) - getfed = sql.get_fed_info(fed_id) - - if not fed_id: - await update.effective_message.reply_text( - "This group is not a part of any federation!", - ) - return - - if is_user_fed_owner(fed_id, user.id) is False: - await update.effective_message.reply_text("Only Federation owners can do this!") - return - - if msg.reply_to_message and msg.reply_to_message.document: - jam = time.time() - new_jam = jam + 1800 - cek = get_chat(chat.id, chat_data) - if cek.get("status"): - if jam <= int(cek.get("value")): - waktu = time.strftime( - "%H:%M:%S %d/%m/%Y", - time.localtime(cek.get("value")), - ) - await update.effective_message.reply_text( - "You can get your data once every 30 minutes!\nYou can get data again at `{}`".format( - waktu, - ), - parse_mode=ParseMode.MARKDOWN, - ) - return - else: - if user.id not in DRAGONS: - put_chat(chat.id, new_jam, chat_data) - else: - if user.id not in DRAGONS: - put_chat(chat.id, new_jam, chat_data) - # if int(int(msg.reply_to_message.document.file_size)/1024) >= 200: - # msg.reply_text("This file is too big!") - # return - success = 0 - failed = 0 - try: - file_info = await bot.get_file(msg.reply_to_message.document.file_id) - except BadRequest: - await msg.reply_text( - "Try downloading and re-uploading the file, this one seems broken!", - ) - return - fileformat = msg.reply_to_message.document.file_name.split(".")[-1] - if fileformat == "json": - multi_fed_id = [] - multi_import_userid = [] - multi_import_firstname = [] - multi_import_lastname = [] - multi_import_username = [] - multi_import_reason = [] - with BytesIO() as file: - file_info.download_to_object(out=file) - file.seek(0) - reading = file.read().decode("UTF-8") - splitting = reading.split("\n") - for x in splitting: - if x == "": - continue - try: - data = json.loads(x) - except json.decoder.JSONDecodeError as err: - failed += 1 - continue - try: - import_userid = int(data["user_id"]) # Make sure it int - import_firstname = str(data["first_name"]) - import_lastname = str(data["last_name"]) - import_username = str(data["user_name"]) - import_reason = str(data["reason"]) - except ValueError: - failed += 1 - continue - # Checking user - if int(import_userid) == bot.id: - failed += 1 - continue - if is_user_fed_owner(fed_id, import_userid) is True: - failed += 1 - continue - if is_user_fed_admin(fed_id, import_userid) is True: - failed += 1 - continue - if str(import_userid) == str(OWNER_ID): - failed += 1 - continue - if int(import_userid) in DRAGONS: - failed += 1 - continue - multi_fed_id.append(fed_id) - multi_import_userid.append(str(import_userid)) - multi_import_firstname.append(import_firstname) - multi_import_lastname.append(import_lastname) - multi_import_username.append(import_username) - multi_import_reason.append(import_reason) - success += 1 - sql.multi_fban_user( - multi_fed_id, - multi_import_userid, - multi_import_firstname, - multi_import_lastname, - multi_import_username, - multi_import_reason, - ) - text = "Blocks were successfully imported. {} people are blocked.".format( - success, - ) - if failed >= 1: - text += " {} Failed to import.".format(failed) - get_fedlog = await sql.get_fed_log(fed_id) - if get_fedlog: - if ast.literal_eval(get_fedlog): - teks = "Fed *{}* has successfully imported data. {} banned.".format( - getfed["fname"], - success, - ) - if failed >= 1: - teks += " {} Failed to import.".format(failed) - await bot.send_message( - get_fedlog, - teks, - parse_mode="markdown", - message_thread_id=update.effective_message.message_thread_id - if chat.is_forum - else None, - ) - elif fileformat == "csv": - multi_fed_id = [] - multi_import_userid = [] - multi_import_firstname = [] - multi_import_lastname = [] - multi_import_username = [] - multi_import_reason = [] - file_info.download_to_drive( - "fban_{}.csv".format(msg.reply_to_message.document.file_id), - ) - with open( - "fban_{}.csv".format(msg.reply_to_message.document.file_id), - "r", - encoding="utf8", - ) as csvFile: - reader = csv.reader(csvFile) - for data in reader: - try: - import_userid = int(data[0]) # Make sure it int - import_firstname = str(data[1]) - import_lastname = str(data[2]) - import_username = str(data[3]) - import_reason = str(data[4]) - except ValueError: - failed += 1 - continue - # Checking user - if int(import_userid) == bot.id: - failed += 1 - continue - if is_user_fed_owner(fed_id, import_userid) is True: - failed += 1 - continue - if is_user_fed_admin(fed_id, import_userid) is True: - failed += 1 - continue - if str(import_userid) == str(OWNER_ID): - failed += 1 - continue - if int(import_userid) in DRAGONS: - failed += 1 - continue - - multi_fed_id.append(fed_id) - multi_import_userid.append(str(import_userid)) - multi_import_firstname.append(import_firstname) - multi_import_lastname.append(import_lastname) - multi_import_username.append(import_username) - multi_import_reason.append(import_reason) - success += 1 - # t = ThreadWithReturnValue(target=sql.fban_user, args=(fed_id, str(import_userid), import_firstname, import_lastname, import_username, import_reason,)) - # t.start() - sql.multi_fban_user( - multi_fed_id, - multi_import_userid, - multi_import_firstname, - multi_import_lastname, - multi_import_username, - multi_import_reason, - ) - csvFile.close() - os.remove("fban_{}.csv".format(msg.reply_to_message.document.file_id)) - text = "Files were imported successfully. {} people banned.".format(success) - if failed >= 1: - text += " {} Failed to import.".format(failed) - get_fedlog = await sql.get_fed_log(fed_id) - if get_fedlog: - if ast.literal_eval(get_fedlog): - teks = "Fed *{}* has successfully imported data. {} banned.".format( - getfed["fname"], - success, - ) - if failed >= 1: - teks += " {} Failed to import.".format(failed) - await bot.send_message( - get_fedlog, - teks, - parse_mode="markdown", - message_thread_id=update.effective_message.message_thread_id - if chat.is_forum - else None, - ) - else: - await send_message(update.effective_message, "This file is not supported.") - return - await send_message(update.effective_message, text) - - -async def del_fed_button(update: Update, context: ContextTypes.DEFAULT_TYPE): - query = update.callback_query - userid = query.message.chat.id - fed_id = query.data.split("_")[1] - - if fed_id == "cancel": - await query.message.edit_text("Federation deletion cancelled") - return - - getfed = sql.get_fed_info(fed_id) - if getfed: - delete = sql.del_fed(fed_id) - if delete: - await query.message.edit_text( - "You have removed your Federation! Now all the Groups that are connected with `{}` do not have a Federation.".format( - getfed["fname"], - ), - parse_mode="markdown", - ) - - -async def fed_stat_user(update: Update, context: ContextTypes.DEFAULT_TYPE): - bot, args = context.bot, context.args - chat = update.effective_chat - user = update.effective_user - msg = update.effective_message - - if args: - if args[0].isdigit(): - user_id = args[0] - else: - user_id = await extract_user(msg, context, args) - else: - user_id = await extract_user(msg, context, args) - - if user_id: - if len(args) == 2 and args[0].isdigit(): - fed_id = args[1] - user_name, reason, fbantime = sql.get_user_fban(fed_id, str(user_id)) - if fbantime: - fbantime = time.strftime("%d/%m/%Y", time.localtime(fbantime)) - else: - fbantime = "Unavaiable" - if user_name is False: - await send_message( - update.effective_message, - "Fed {} not found!".format(fed_id), - parse_mode="markdown", - ) - return - if user_name == "" or user_name is None: - user_name = "He/she" - if not reason: - await send_message( - update.effective_message, - "{} is not banned in this federation!".format(user_name), - ) - else: - teks = "{} banned in this federation because:\n`{}`\n*Banned at:* `{}`".format( - user_name, - reason, - fbantime, - ) - await send_message( - update.effective_message, teks, parse_mode="markdown" - ) - return - user_name, fbanlist = sql.get_user_fbanlist(str(user_id)) - if user_name == "": - try: - user_first = await bot.get_chat(user_id) - if isinstance(user_first, ChatMember): - user_name = user_id.first_name - except BadRequest: - user_name = "He/she" - if user_name == "" or user_name is None: - user_name = "He/she" - if len(fbanlist) == 0: - await send_message( - update.effective_message, - "{} is not banned in any federation!".format(user_name), - ) - return - else: - teks = "{} has been banned in this federation:\n".format(user_name) - for x in fbanlist: - teks += "- `{}`: {}\n".format(x[0], x[1][:20]) - teks += "\nIf you want to find out more about the reasons for Fedban specifically, use /fbanstat <FedID>" - await send_message(update.effective_message, teks, parse_mode="markdown") - - elif not msg.reply_to_message and not args: - user_id = msg.from_user.id - user_name, fbanlist = sql.get_user_fbanlist(user_id) - if user_name == "": - user_name = msg.from_user.first_name - if len(fbanlist) == 0: - await send_message( - update.effective_message, - "{} is not banned in any federation!".format(user_name), - ) - else: - teks = "{} has been banned in this federation:\n".format(user_name) - for x in fbanlist: - teks += "- `{}`: {}\n".format(x[0], x[1][:20]) - teks += "\nIf you want to find out more about the reasons for Fedban specifically, use /fbanstat <FedID>" - await send_message(update.effective_message, teks, parse_mode="markdown") - - else: - fed_id = args[0] - fedinfo = sql.get_fed_info(fed_id) - if not fedinfo: - await send_message( - update.effective_message, "Fed {} not found!".format(fed_id) - ) - return - name, reason, fbantime = sql.get_user_fban(fed_id, msg.from_user.id) - if fbantime: - fbantime = time.strftime("%d/%m/%Y", time.localtime(fbantime)) - else: - fbantime = "Unavaiable" - if not name: - name = msg.from_user.first_name - if not reason: - await send_message( - update.effective_message, - "{} is not banned in this federation".format(name), - ) - return - await send_message( - update.effective_message, - "{} banned in this federation because:\n`{}`\n*Banned at:* `{}`".format( - name, - reason, - fbantime, - ), - parse_mode="markdown", - ) - - -async def set_fed_log(update: Update, context: ContextTypes.DEFAULT_TYPE): - args = context.args - chat = update.effective_chat - user = update.effective_user - msg = update.effective_message - - if chat.type == "private": - await send_message( - update.effective_message, - "This command is specific to the group, not to our pm!", - ) - return - - if args: - fedinfo = sql.get_fed_info(args[0]) - if not fedinfo: - await send_message( - update.effective_message, "This Federation does not exist!" - ) - return - isowner = is_user_fed_owner(args[0], user.id) - if not isowner: - await send_message( - update.effective_message, - "Only federation creator can set federation logs.", - ) - return - setlog = sql.set_fed_log(args[0], chat.id) - if setlog: - await send_message( - update.effective_message, - "Federation log `{}` has been set to {}".format( - fedinfo["fname"], - chat.title, - ), - parse_mode="markdown", - ) - else: - await send_message( - update.effective_message, - "You have not provided your federated ID!", - ) - - -async def unset_fed_log(update: Update, context: ContextTypes.DEFAULT_TYPE): - args = context.args - chat = update.effective_chat - user = update.effective_user - msg = update.effective_message - - if chat.type == "private": - await send_message( - update.effective_message, - "This command is specific to the group, not to our pm!", - ) - return - - if args: - fedinfo = sql.get_fed_info(args[0]) - if not fedinfo: - await send_message( - update.effective_message, "This Federation does not exist!" - ) - return - isowner = is_user_fed_owner(args[0], user.id) - if not isowner: - await send_message( - update.effective_message, - "Only federation creator can set federation logs.", - ) - return - setlog = sql.set_fed_log(args[0], None) - if setlog: - await send_message( - update.effective_message, - "Federation log `{}` has been revoked on {}".format( - fedinfo["fname"], - chat.title, - ), - parse_mode="markdown", - ) - else: - await send_message( - update.effective_message, - "You have not provided your federated ID!", - ) - - -async def subs_feds(update: Update, context: ContextTypes.DEFAULT_TYPE): - bot, args = context.bot, context.args - chat = update.effective_chat - user = update.effective_user - msg = update.effective_message - - if chat.type == "private": - await send_message( - update.effective_message, - "This command is specific to the group, not to our pm!", - ) - return - - fed_id = sql.get_fed_id(chat.id) - fedinfo = sql.get_fed_info(fed_id) - - if not fed_id: - await send_message( - update.effective_message, "This group is not in any federation!" - ) - return - - if is_user_fed_owner(fed_id, user.id) is False: - await send_message(update.effective_message, "Only fed owner can do this!") - return - - if args: - getfed = sql.search_fed_by_id(args[0]) - if getfed is False: - await send_message( - update.effective_message, - "Please enter a valid federation id.", - ) - return - subfed = sql.subs_fed(args[0], fed_id) - if subfed: - await send_message( - update.effective_message, - "Federation `{}` has subscribe the federation `{}`. Every time there is a Fedban from that federation, this federation will also banned that user.".format( - fedinfo["fname"], - getfed["fname"], - ), - parse_mode="markdown", - ) - get_fedlog = await sql.get_fed_log(args[0]) - if get_fedlog: - if int(get_fedlog) != int(chat.id): - await bot.send_message( - get_fedlog, - "Federation `{}` has subscribe the federation `{}`".format( - fedinfo["fname"], - getfed["fname"], - ), - parse_mode="markdown", - message_thread_id=update.effective_message.message_thread_id - if chat.is_forum - else None, - ) - else: - await send_message( - update.effective_message, - "Federation `{}` already subscribe the federation `{}`.".format( - fedinfo["fname"], - getfed["fname"], - ), - parse_mode="markdown", - ) - else: - await send_message( - update.effective_message, - "You have not provided your federated ID!", - ) - - -async def unsubs_feds(update: Update, context: ContextTypes.DEFAULT_TYPE): - bot, args = context.bot, context.args - chat = update.effective_chat - user = update.effective_user - msg = update.effective_message - - if chat.type == "private": - await send_message( - update.effective_message, - "This command is specific to the group, not to our pm!", - ) - return - - fed_id = sql.get_fed_id(chat.id) - fedinfo = sql.get_fed_info(fed_id) - - if not fed_id: - await send_message( - update.effective_message, "This group is not in any federation!" - ) - return - - if is_user_fed_owner(fed_id, user.id) is False: - await send_message(update.effective_message, "Only fed owner can do this!") - return - - if args: - getfed = sql.search_fed_by_id(args[0]) - if getfed is False: - await send_message( - update.effective_message, - "Please enter a valid federation id.", - ) - return - subfed = sql.unsubs_fed(args[0], fed_id) - if subfed: - await send_message( - update.effective_message, - "Federation `{}` now unsubscribe fed `{}`.".format( - fedinfo["fname"], - getfed["fname"], - ), - parse_mode="markdown", - ) - get_fedlog = await sql.get_fed_log(args[0]) - if get_fedlog: - if int(get_fedlog) != int(chat.id): - await bot.send_message( - get_fedlog, - "Federation `{}` has unsubscribe fed `{}`.".format( - fedinfo["fname"], - getfed["fname"], - ), - parse_mode="markdown", - message_thread_id=update.effective_message.message_thread_id - if chat.is_forum - else None, - ) - else: - await send_message( - update.effective_message, - "Federation `{}` is not subscribing `{}`.".format( - fedinfo["fname"], - getfed["fname"], - ), - parse_mode="markdown", - ) - else: - await send_message( - update.effective_message, - "You have not provided your federated ID!", - ) - - -async def get_myfedsubs(update: Update, context: ContextTypes.DEFAULT_TYPE): - args = context.args - chat = update.effective_chat - user = update.effective_user - msg = update.effective_message - - if chat.type == "private": - await send_message( - update.effective_message, - "This command is specific to the group, not to our pm!", - ) - return - - fed_id = sql.get_fed_id(chat.id) - fedinfo = sql.get_fed_info(fed_id) - - if not fed_id: - await send_message( - update.effective_message, "This group is not in any federation!" - ) - return - - if is_user_fed_owner(fed_id, user.id) is False: - await send_message(update.effective_message, "Only fed owner can do this!") - return - - try: - getmy = sql.get_mysubs(fed_id) - except: - getmy = [] - - if len(getmy) == 0: - await send_message( - update.effective_message, - "Federation `{}` is not subscribing any federation.".format( - fedinfo["fname"], - ), - parse_mode="markdown", - ) - return - else: - listfed = "Federation `{}` is subscribing federation:\n".format( - fedinfo["fname"], - ) - for x in getmy: - listfed += "- `{}`\n".format(x) - listfed += ( - "\nTo get fed info `/fedinfo <fedid>`. To unsubscribe `/unsubfed <fedid>`." - ) - await send_message(update.effective_message, listfed, parse_mode="markdown") - - -async def get_myfeds_list(update: Update, context: ContextTypes.DEFAULT_TYPE): - chat = update.effective_chat - user = update.effective_user - msg = update.effective_message - - fedowner = sql.get_user_owner_fed_full(user.id) - if fedowner: - text = "*You are owner of feds:\n*" - for f in fedowner: - text += "- `{}`: *{}*\n".format(f["fed_id"], f["fed"]["fname"]) - else: - text = "*You are not have any feds!*" - await send_message(update.effective_message, text, parse_mode="markdown") - - -def is_user_fed_admin(fed_id, user_id): - fed_admins = sql.all_fed_users(fed_id) - if fed_admins is False: - return False - if int(user_id) in fed_admins or int(user_id) == OWNER_ID: - return True - else: - return False - - -def is_user_fed_owner(fed_id, user_id): - getsql = sql.get_fed_info(fed_id) - if getsql is False: - return False - getfedowner = ast.literal_eval(getsql["fusers"]) - if getfedowner is None or getfedowner is False: - return False - getfedowner = getfedowner["owner"] - if str(user_id) == getfedowner or int(user_id) == OWNER_ID: - return True - else: - return False - - -# There's no handler for this yet, but updating for v12 in case its used - - -async def welcome_fed(update: Update, context: ContextTypes.DEFAULT_TYPE): - bot, args = context.bot, context.args - chat = update.effective_chat - user = update.effective_user - fed_id = sql.get_fed_id(chat.id) - fban, fbanreason, fbantime = sql.get_fban_user(fed_id, user.id) - if fban: - await update.effective_message.reply_text( - "This user is banned in current federation! I will remove him.", - ) - await bot.ban_chat_member(chat.id, user.id) - return True - else: - return False - - -def __stats__(): - all_fbanned = sql.get_all_fban_users_global() - all_feds = sql.get_all_feds_users_global() - return "• {} banned users across {} Federations".format( - len(all_fbanned), - len(all_feds), - ) - - -def __user_info__(user_id, chat_id): - fed_id = sql.get_fed_id(chat_id) - if fed_id: - fban, fbanreason, fbantime = sql.get_fban_user(fed_id, user_id) - info = sql.get_fed_info(fed_id) - infoname = info["fname"] - - if int(info["owner"]) == user_id: - text = "Federation owner of: <b>{}</b>.".format(infoname) - elif is_user_fed_admin(fed_id, user_id): - text = "Federation admin of: <b>{}</b>.".format(infoname) - - elif fban: - text = "Federation banned: <b>Yes</b>" - text += "\n<b>Reason:</b> {}".format(fbanreason) - else: - text = "Federation banned: <b>No</b>" - else: - text = "" - return text - - -# Temporary data -def put_chat(chat_id, value, chat_data): - # print(chat_data) - if value is False: - status = False - else: - status = True - chat_data[chat_id] = {"federation": {"status": status, "value": value}} - - -def get_chat(chat_id, chat_data): - # print(chat_data) - try: - value = chat_data[chat_id]["federation"] - return value - except KeyError: - return {"status": False, "value": False} - - -async def fed_owner_help(update: Update, context: ContextTypes.DEFAULT_TYPE): - await update.effective_message.reply_text( - """*👑 Fed Owner Only:* - » `/newfed <fed_name>`*:* Creates a Federation, One allowed per user - » `/renamefed <fed_id> <new_fed_name>`*:* Renames the fed id to a new name - » `/delfed <fed_id>`*:* Delete a Federation, and any information related to it. Will not cancel blocked users - » `/fpromote <user>`*:* Assigns the user as a federation admin. Enables all commands for the user under `Fed Admins` - » `/fdemote <user>`*:* Drops the User from the admin Federation to a normal User - » `/subfed <fed_id>`*:* Subscribes to a given fed ID, bans from that subscribed fed will also happen in your fed - » `/unsubfed <fed_id>`*:* Unsubscribes to a given fed ID - » `/setfedlog <fed_id>`*:* Sets the group as a fed log report base for the federation - » `/unsetfedlog <fed_id>`*:* Removed the group as a fed log report base for the federation - » `/fbroadcast <message>`*:* Broadcasts a messages to all groups that have joined your fed - » `/fedsubs`*:* Shows the feds your group is subscribed to `(broken rn)`""", - parse_mode=ParseMode.MARKDOWN, - ) - - -async def fed_admin_help(update: Update, context: ContextTypes.DEFAULT_TYPE): - await update.effective_message.reply_text( - """*🔱 Fed Admins:* - » `/fban <user> <reason>`*:* Fed bans a user - » `/unfban <user> <reason>`*:* Removes a user from a fed ban - » `/fedinfo <fed_id>`*:* Information about the specified Federation - » `/joinfed <fed_id>`*:* Join the current chat to the Federation. Only chat owners can do this. Every chat can only be in one Federation - » `/leavefed <fed_id>`*:* Leave the Federation given. Only chat owners can do this - » `/setfrules <rules>`*:* Arrange Federation rules - » `/fedadmins`*:* Show Federation admin - » `/fbanlist`*:* Displays all users who are victimized at the Federation at this time - » `/fedchats`*:* Get all the chats that are connected in the Federation - » `/chatfed `*:* See the Federation in the current chat\n""", - parse_mode=ParseMode.MARKDOWN, - ) - - -async def fed_user_help(update: Update, context: ContextTypes.DEFAULT_TYPE): - await update.effective_message.reply_text( - """*🎩 Any user:* - » `/fbanstat`*:* Shows if you/or the user you are replying to or their username is fbanned somewhere or not - » `/fednotif <on/off>`*:* Federation settings not in PM when there are users who are fbaned/unfbanned - » `/frules`*:* See Federation regulations\n""", - parse_mode=ParseMode.MARKDOWN, - ) - - -# <=================================================== HELP ====================================================> - - -__mod_name__ = "FEDS" - -__help__ = """ -➠ *Everything is fun, until a spammer starts entering your group, and you have to block it. Then you need to start banning more, and more, and it hurts*. -*But then you have many groups, and you don't want this spammer to be in one of your groups - how can you deal? Do you have to manually block it, in all your groups*?\n -*No longer!* *With Federation, you can make a ban in one chat overlap with all other chats*.\n -*You can even designate federation admins, so your trusted admin can ban all the spammers from chats you want to protect*.\n - -➠ *Commands:* -➠ Feds are now divided into 3 sections for your ease. - -» /fedownerhelp: Provides help for fed creation and owner only commands. - -» /fedadminhelp: Provides help for fed administration commands. - -» /feduserhelp: Provides help for commands anyone can use. - -""" - -# <================================================ HANDLER =======================================================> -function(CommandHandler("newfed", new_fed, block=False)) -function(CommandHandler("delfed", del_fed, block=False)) -function(CommandHandler("renamefed", rename_fed, block=False)) -function(CommandHandler("joinfed", join_fed, block=False)) -function(CommandHandler("leavefed", leave_fed, block=False)) -function(CommandHandler("fpromote", user_join_fed, block=False)) -function(CommandHandler("fdemote", user_demote_fed, block=False)) -function(CommandHandler("fedinfo", fed_info, block=False)) -function(DisableAbleCommandHandler("fban", fed_ban, block=False)) -function(CommandHandler("unfban", unfban, block=False)) -function(CommandHandler("fbroadcast", fed_broadcast, block=False)) -function(CommandHandler("setfrules", set_frules, block=False)) -function(CommandHandler("frules", get_frules, block=False)) -function(CommandHandler("chatfed", fed_chat, block=False)) -function(CommandHandler("fedadmins", fed_admin, block=False)) -function(CommandHandler("fbanlist", fed_ban_list, block=False)) -function(CommandHandler("fednotif", fed_notif, block=False)) -function(CommandHandler("fedchats", fed_chats, block=False)) -function(CommandHandler("importfbans", fed_import_bans, block=False)) -function(DisableAbleCommandHandler(["fedstat", "fbanstat"], fed_stat_user, block=False)) -function(CommandHandler("setfedlog", set_fed_log, block=False)) -function(CommandHandler("unsetfedlog", unset_fed_log, block=False)) -function(CommandHandler("subfed", subs_feds, block=False)) -function(CommandHandler("unsubfed", unsubs_feds, block=False)) -function(CommandHandler("fedsubs", get_myfedsubs, block=False)) -function(CommandHandler("myfeds", get_myfeds_list, block=False)) -function(CallbackQueryHandler(del_fed_button, pattern=r"rmfed_", block=False)) -function(CommandHandler("fedownerhelp", fed_owner_help, block=False)) -function(CommandHandler("fedadminhelp", fed_admin_help, block=False)) -function(CommandHandler("feduserhelp", fed_user_help, block=False)) -# <================================================ END =======================================================> diff --git a/Mikobot/plugins/flood.py b/Mikobot/plugins/flood.py deleted file mode 100644 index 22b7fa2e1495cadec1d76c92e9a6c212b0be3505..0000000000000000000000000000000000000000 --- a/Mikobot/plugins/flood.py +++ /dev/null @@ -1,457 +0,0 @@ -# <============================================== IMPORTS =========================================================> -import html -import re - -from telegram import ChatPermissions, Update -from telegram.error import BadRequest -from telegram.ext import ( - CallbackQueryHandler, - CommandHandler, - ContextTypes, - MessageHandler, - filters, -) -from telegram.helpers import mention_html - -from Database.sql import antiflood_sql as sql -from Database.sql.approve_sql import is_approved -from Mikobot import dispatcher, function -from Mikobot.plugins.connection import connected -from Mikobot.plugins.helper_funcs.alternate import send_message -from Mikobot.plugins.helper_funcs.chat_status import check_admin, is_user_admin -from Mikobot.plugins.helper_funcs.string_handling import extract_time -from Mikobot.plugins.log_channel import loggable - -# <=======================================================================================================> - -FLOOD_GROUP = 3 - - -# <================================================ FUNCTION =======================================================> -@loggable -async def check_flood(update: Update, context: ContextTypes.DEFAULT_TYPE): - user = update.effective_user - chat = update.effective_chat - msg = update.effective_message - if not user: - return "" - - if await is_user_admin(chat, user.id): - sql.update_flood(chat.id, None) - return "" - - if is_approved(chat.id, user.id): - sql.update_flood(chat.id, None) - return - - should_ban = sql.update_flood(chat.id, user.id) - if not should_ban: - return "" - - try: - getmode, getvalue = sql.get_flood_setting(chat.id) - if getmode == 1: - await chat.ban_member(user.id) - execstrings = "BANNED" - tag = "BANNED" - elif getmode == 2: - await chat.ban_member(user.id) - await chat.unban_member(user.id) - execstrings = "KICKED" - tag = "KICKED" - elif getmode == 3: - await context.bot.restrict_chat_member( - chat.id, - user.id, - permissions=ChatPermissions(can_send_messages=False), - ) - execstrings = "MUTED" - tag = "MUTED" - elif getmode == 4: - bantime = await extract_time(msg, getvalue) - await chat.ban_member(user.id, until_date=bantime) - execstrings = "BANNED for {}".format(getvalue) - tag = "TBAN" - elif getmode == 5: - mutetime = await extract_time(msg, getvalue) - await context.bot.restrict_chat_member( - chat.id, - user.id, - until_date=mutetime, - permissions=ChatPermissions(can_send_messages=False), - ) - execstrings = "MUTED for {}".format(getvalue) - tag = "TMUTE" - await send_message( - update.effective_message, - "Beep boop! Boop beep!\n{}!".format(execstrings), - ) - - return ( - "<b>{}:</b>" - "\n#{}" - "\n<b>user:</b> {}" - "\nFlooded the group.".format( - tag, - html.escape(chat.title), - mention_html(user.id, html.escape(user.first_name)), - ) - ) - - except BadRequest: - await msg.reply_text( - "I can't restrict people here, give me permissions first! Until then, I'll disable anti-flood.", - ) - sql.set_flood(chat.id, 0) - return ( - "<b>{}:</b>" - "\n#INFO" - "\nDon't have enough permission to restrict users so automatically disabled anti-flood.".format( - chat.title, - ) - ) - - -@check_admin(permission="can_restrict_members", is_both=True, no_reply=True) -async def flood_button(update: Update, context: ContextTypes.DEFAULT_TYPE): - bot = context.bot - query = update.callback_query - user = update.effective_user - match = re.match(r"unmute_flooder\((.+?)\)", query.data) - if match: - user_id = match.group(1) - chat = update.effective_chat.id - try: - await bot.restrict_chat_member( - chat, - int(user_id), - permissions=ChatPermissions( - can_send_messages=True, - can_send_media_messages=True, - can_send_other_messages=True, - can_add_web_page_previews=True, - ), - ) - await update.effective_message.edit_text( - f"Unmuted by {mention_html(user.id, html.escape(user.first_name))}.", - parse_mode="HTML", - ) - except: - pass - - -@loggable -@check_admin(is_user=True) -async def set_flood(update: Update, context: ContextTypes.DEFAULT_TYPE): - chat = update.effective_chat - user = update.effective_user - message = update.effective_message - args = context.args - - conn = await connected(context.bot, update, chat, user.id, need_admin=True) - if conn: - chat_id = conn - chat_obj = await dispatcher.bot.getChat(conn) - chat_name = chat_obj.title - else: - if update.effective_message.chat.type == "private": - await send_message( - update.effective_message, - "This command is meant to use in a group, not in PM.", - ) - return "" - chat_id = update.effective_chat.id - chat_name = update.effective_message.chat.title - - if len(args) >= 1: - val = args[0].lower() - if val in ["off", "no", "0"]: - sql.set_flood(chat_id, 0) - if conn: - text = await message.reply_text( - "Antiflood has been disabled in {}.".format(chat_name), - ) - else: - text = await message.reply_text("Antiflood has been disabled.") - - elif val.isdigit(): - amount = int(val) - if amount <= 0: - sql.set_flood(chat_id, 0) - if conn: - text = await message.reply_text( - "Antiflood has been disabled in {}.".format(chat_name), - ) - else: - text = await message.reply_text("Antiflood has been disabled.") - return ( - "<b>{}:</b>" - "\n#SETFLOOD" - "\n<b>Admin:</b> {}" - "\nDisable Antiflood.".format( - html.escape(chat_name), - mention_html(user.id, html.escape(user.first_name)), - ) - ) - - elif amount <= 3: - await send_message( - update.effective_message, - "Antiflood must be either 0 (disabled) or a number greater than 3!", - ) - return "" - - else: - sql.set_flood(chat_id, amount) - if conn: - text = await message.reply_text( - "Antiflood limit has been set to {} in chat: {}".format( - amount, - chat_name, - ), - ) - else: - text = await message.reply_text( - "Successfully updated antiflood limit to {}!".format(amount), - ) - return ( - "<b>{}:</b>" - "\n#SETFLOOD" - "\n<b>Admin:</b> {}" - "\nSet Antiflood to <code>{}</code>.".format( - html.escape(chat_name), - mention_html(user.id, html.escape(user.first_name)), - amount, - ) - ) - - else: - await message.reply_text( - "Invalid argument, please use a number, 'off', or 'no'." - ) - else: - await message.reply_text( - ( - "Use `/setflood number` to enable antiflood.\n" - "Or use `/setflood off` to disable antiflood." - ), - parse_mode="markdown", - ) - return "" - - -async def flood(update: Update, context: ContextTypes.DEFAULT_TYPE): - chat = update.effective_chat - user = update.effective_user - msg = update.effective_message - - conn = await connected(context.bot, update, chat, user.id, need_admin=False) - if conn: - chat_id = conn - chat_obj = await dispatcher.bot.getChat(conn) - chat_name = chat_obj.title - else: - if update.effective_message.chat.type == "private": - await send_message( - update.effective_message, - "This command is meant to use in a group, not in PM.", - ) - return - chat_id = update.effective_chat.id - chat_name = update.effective_message.chat.title - - limit = sql.get_flood_limit(chat_id) - if limit == 0: - if conn: - text = await msg.reply_text( - "I'm not enforcing any flood control in {}!".format(chat_name), - ) - else: - text = await msg.reply_text("I'm not enforcing any flood control here!") - else: - if conn: - text = await msg.reply_text( - "I'm currently restricting members after {} consecutive messages in {}.".format( - limit, - chat_name, - ), - ) - else: - text = await msg.reply_text( - "I'm currently restricting members after {} consecutive messages.".format( - limit, - ), - ) - - -@check_admin(is_user=True) -async def set_flood_mode(update: Update, context: ContextTypes.DEFAULT_TYPE): - chat = update.effective_chat - user = update.effective_user - msg = update.effective_message - args = context.args - - conn = await connected(context.bot, update, chat, user.id, need_admin=True) - if conn: - chat = await dispatcher.bot.getChat(conn) - chat_id = conn - chat_obj = await dispatcher.bot.getChat(conn) - chat_name = chat_obj.title - else: - if update.effective_message.chat.type == "private": - await send_message( - update.effective_message, - "This command is meant to use in a group, not in PM.", - ) - return "" - chat = update.effective_chat - chat_id = update.effective_chat.id - chat_name = update.effective_message.chat.title - - if args: - if args[0].lower() == "ban": - settypeflood = "ban" - sql.set_flood_strength(chat_id, 1, "0") - elif args[0].lower() == "kick": - settypeflood = "kick" - sql.set_flood_strength(chat_id, 2, "0") - elif args[0].lower() == "mute": - settypeflood = "mute" - sql.set_flood_strength(chat_id, 3, "0") - elif args[0].lower() == "tban": - if len(args) == 1: - teks = """It looks like you tried to set time value for antiflood but you didn't specified time; Try, `/setfloodmode tban <timevalue>`. -Examples of time value: 4m = 4 minutes, 3h = 3 hours, 6d = 6 days, 5w = 5 weeks.""" - await send_message( - update.effective_message, teks, parse_mode="markdown" - ) - return - settypeflood = "tban for {}".format(args[1]) - sql.set_flood_strength(chat_id, 4, str(args[1])) - elif args[0].lower() == "tmute": - if len(args) == 1: - teks = """It looks like you tried to set time value for antiflood but you didn't specified time; Try, `/setfloodmode tmute <timevalue>`. -Examples of time value: 4m = 4 minutes, 3h = 3 hours, 6d = 6 days, 5w = 5 weeks.""" - await send_message( - update.effective_message, teks, parse_mode="markdown" - ) - return - settypeflood = "tmute for {}".format(args[1]) - sql.set_flood_strength(chat_id, 5, str(args[1])) - else: - await send_message( - update.effective_message, - "I only understand ban/kick/mute/tban/tmute!", - ) - return - if conn: - text = await msg.reply_text( - "Exceeding consecutive flood limit will result in {} in {}!".format( - settypeflood, - chat_name, - ), - ) - else: - text = await msg.reply_text( - "Exceeding consecutive flood limit will result in {}!".format( - settypeflood, - ), - ) - return ( - "<b>{}:</b>\n" - "<b>Admin:</b> {}\n" - "Has changed antiflood mode. User will {}.".format( - settypeflood, - html.escape(chat.title), - mention_html(user.id, html.escape(user.first_name)), - ) - ) - else: - getmode, getvalue = sql.get_flood_setting(chat.id) - if getmode == 1: - settypeflood = "ban" - elif getmode == 2: - settypeflood = "kick" - elif getmode == 3: - settypeflood = "mute" - elif getmode == 4: - settypeflood = "tban for {}".format(getvalue) - elif getmode == 5: - settypeflood = "tmute for {}".format(getvalue) - if conn: - text = await msg.reply_text( - "Sending more messages than flood limit will result in {} in {}.".format( - settypeflood, - chat_name, - ), - ) - else: - text = await msg.reply_text( - "Sending more messages than flood limit will result in {}.".format( - settypeflood, - ), - ) - return "" - - -def __migrate__(old_chat_id, new_chat_id): - sql.migrate_chat(old_chat_id, new_chat_id) - - -def __chat_settings__(chat_id, user_id): - limit = sql.get_flood_limit(chat_id) - if limit == 0: - return "Not enforcing flood control." - else: - return "Antiflood has been set to `{}`.".format(limit) - - -# <=================================================== HELP ====================================================> - - -__help__ = """ -➠ *Antiflood allows you to take action on users that send more than x messages in a row. Exceeding the set flood will result in restricting that user.* - -➠ *Admin Only* - -» /flood: Get the current antiflood settings. - -» /setflood <number/off/no>: Set the number of messages after which to take action on a user. Set to '0', 'off', or 'no' to disable. - -» /setfloodmode <action type>: Choose which action to take on a user who has been flooding. Options: ban/kick/mute/tban/tmute. -""" - -__mod_name__ = "ANTI-FLOOD" - -# <================================================ HANDLER =======================================================> -FLOOD_BAN_HANDLER = MessageHandler( - filters.ALL & ~filters.StatusUpdate.ALL & filters.ChatType.GROUPS, - check_flood, - block=False, -) -SET_FLOOD_HANDLER = CommandHandler( - "setflood", set_flood, filters=filters.ChatType.GROUPS, block=False -) -SET_FLOOD_MODE_HANDLER = CommandHandler( - "setfloodmode", set_flood_mode, block=False -) # , filters=filters.ChatType.GROUPS) -FLOOD_QUERY_HANDLER = CallbackQueryHandler( - flood_button, pattern=r"unmute_flooder", block=False -) -FLOOD_HANDLER = CommandHandler( - "flood", flood, filters=filters.ChatType.GROUPS, block=False -) - -function(FLOOD_BAN_HANDLER, FLOOD_GROUP) -function(FLOOD_QUERY_HANDLER) -function(SET_FLOOD_HANDLER) -function(SET_FLOOD_MODE_HANDLER) -function(FLOOD_HANDLER) - -__handlers__ = [ - (FLOOD_BAN_HANDLER, FLOOD_GROUP), - SET_FLOOD_HANDLER, - FLOOD_HANDLER, - SET_FLOOD_MODE_HANDLER, -] -# <================================================ END =======================================================> diff --git a/Mikobot/plugins/fsub.py b/Mikobot/plugins/fsub.py deleted file mode 100644 index b247f3ae639afaf021ffbd1ca8d6cf1c86b019f8..0000000000000000000000000000000000000000 --- a/Mikobot/plugins/fsub.py +++ /dev/null @@ -1,189 +0,0 @@ -# <============================================== IMPORTS =========================================================> -from telethon import Button, events, types -from telethon.errors import ChatAdminRequiredError, UserNotParticipantError -from telethon.tl.functions.channels import GetParticipantRequest - -from Database.mongodb import fsub_db as db -from Mikobot import BOT_ID -from Mikobot import DRAGONS as DEVS -from Mikobot import OWNER_ID, tbot -from Mikobot.events import register - -# <=======================================================================================================> - -# Constants -F_SUBSCRIBE_COMMAND = "/(fsub|Fsub|forcesubscribe|Forcesub|forcesub|Forcesubscribe)" -FORCESUBSCRIBE_ON = ["on", "yes", "y"] -FORCESUBSCRIBE_OFF = ["off", "no", "n"] - - -# <================================================ FUNCTION =======================================================> -def fsk_ck(**args): - def decorator(func): - tbot.add_event_handler(func, events.CallbackQuery(**args)) - return func - - return decorator - - -# Helper functions -async def is_admin(chat_id, user_id): - try: - p = await tbot(GetParticipantRequest(chat_id, user_id)) - except UserNotParticipantError: - return False - return isinstance( - p.participant, (types.ChannelParticipantAdmin, types.ChannelParticipantCreator) - ) - - -async def participant_check(channel, user_id): - try: - await tbot(GetParticipantRequest(channel, int(user_id))) - return True - except UserNotParticipantError: - return False - except Exception: - return False - - -# Main command function -@register(pattern=f"^{F_SUBSCRIBE_COMMAND} ?(.*)") -async def force_subscribe(event): - """Handle the force subscribe command.""" - if event.is_private: - return - - if event.is_group: - perm = await event.client.get_permissions(event.chat_id, event.sender_id) - if not perm.is_admin: - return await event.reply("You need to be an admin to do this.") - - if not perm.is_creator: - return await event.reply( - "❗ Group creator required\nYou have to be the group creator to do that." - ) - - try: - channel = event.text.split(None, 1)[1] - except IndexError: - channel = None - - if not channel: - chat_db = db.fs_settings(event.chat_id) - if not chat_db: - await event.reply("Force subscribe is disabled in this chat.") - else: - await event.reply( - f"Force subscribe is currently enabled. Users are forced to join @{chat_db.channel} to speak here." - ) - elif channel.lower() in FORCESUBSCRIBE_ON: - await event.reply("Please specify the channel username.") - elif channel.lower() in FORCESUBSCRIBE_OFF: - await event.reply("**Force subscribe is disabled successfully.**") - db.disapprove(event.chat_id) - else: - try: - channel_entity = await event.client.get_entity(channel) - except: - return await event.reply("Invalid channel username provided.") - - channel = channel_entity.username - try: - if not channel_entity.broadcast: - return await event.reply("That's not a valid channel.") - except: - return await event.reply("That's not a valid channel.") - - if not await participant_check(channel, BOT_ID): - return await event.reply( - f"**Not an admin in the channel**\nI am not an admin in the [channel](https://t.me/{channel}). Add me as an admin to enable force subscribe.", - link_preview=False, - ) - - db.add_channel(event.chat_id, str(channel)) - await event.reply(f"Force subscribe is enabled to @{channel}.") - - -# Event handler for new messages -@tbot.on(events.NewMessage()) -async def force_subscribe_new_message(e): - """Handle new messages for force subscribe.""" - if not db.fs_settings(e.chat_id): - return - - if e.is_private or not e.from_id or e.sender_id in DEVS or e.sender_id == OWNER_ID: - return - - if not e.chat.admin_rights or not e.chat.admin_rights.ban_users: - return - - try: - channel = db.fs_settings(e.chat_id)["channel"] - check = await participant_check(channel, e.sender_id) - except (ChatAdminRequiredError, UserNotParticipantError): - return - - if not check: - buttons = [ - Button.url("Join Channel", f"t.me/{channel}"), - Button.inline("Unmute Me", data=f"fs_{e.sender_id}"), - ] - - txt = f'<b><a href="tg://user?id={e.sender_id}">{e.sender.first_name}</a></b>, you have <b>not subscribed</b> to our <b><a href="t.me/{channel}">channel</a></b> yet. Please <b><a href="t.me/{channel}">join</a></b> and press the button below to unmute yourself.' - await e.reply(txt, buttons=buttons, parse_mode="html", link_preview=False) - await e.client.edit_permissions(e.chat_id, e.sender_id, send_messages=False) - - -# Inline query handler -@fsk_ck(pattern=r"fs(\_(.*))") -async def unmute_force_subscribe(event): - """Handle inline query for unmuting force subscribe.""" - user_id = int(((event.pattern_match.group(1)).decode()).split("_", 1)[1]) - - if not event.sender_id == user_id: - return await event.answer("This is not meant for you.", alert=True) - - channel = db.fs_settings(event.chat_id)["channel"] - try: - check = await participant_check(channel, user_id) - except ChatAdminRequiredError: - check = False - return - - if not check: - return await event.answer( - "You have to join the channel first, to get unmuted!", alert=True - ) - - try: - await event.client.edit_permissions(event.chat_id, user_id, send_messages=True) - except ChatAdminRequiredError: - pass - - await event.delete() - - -# <=================================================== HELP ====================================================> - - -__help__ = """ -➠ *Dazai has the capability to hush members who haven't yet subscribed to your channel until they decide to hit that subscribe button.* -➠ *When activated, I'll silence those who are not subscribed and provide them with an option to unmute. Once they click the button, I'll lift the mute.* - -➠ Configuration Process -➠ Exclusively for Creators -➠ Grant me admin privileges in your group -➠ Designate me as an admin in your channel - -➠ *Commands* -» /fsub channel\_username - to initiate and customize settings for the channel. - -➠ *Kick things off with...* -» /fsub - to review the current settings. -» /fsub off - to deactivate the force subscription feature. - -➠ *If you disable fsub, you'll need to set it up again for it to take effect. Utilize /fsub channel\_username.* -""" -__mod_name__ = "F-SUB" -# <================================================ END =======================================================> diff --git a/Mikobot/plugins/fun.py b/Mikobot/plugins/fun.py deleted file mode 100644 index 9a8cf05e5873406f3470c9caee12623702009be9..0000000000000000000000000000000000000000 --- a/Mikobot/plugins/fun.py +++ /dev/null @@ -1,257 +0,0 @@ -# <============================================== IMPORTS =========================================================> -import random - -from pyjokes import get_joke -from telegram import Update -from telegram.constants import ParseMode -from telegram.ext import ContextTypes - -import Mikobot.utils.fun_strings as fun_strings -from Mikobot import function -from Mikobot.events import register -from Mikobot.plugins.disable import DisableAbleCommandHandler -from Mikobot.state import state - -# <=======================================================================================================> - - -# <================================================ FUNCTION =======================================================> -async def make_request(url: str) -> str: - response = await state.get(url) - response.raise_for_status() - return response.json()["question"] - - -async def truth(update: Update, context: ContextTypes.DEFAULT_TYPE): - truth_question = await make_request("https://api.truthordarebot.xyz/v1/truth") - await update.effective_message.reply_text(truth_question) - - -async def dare(update: Update, context: ContextTypes.DEFAULT_TYPE): - dare_question = await make_request("https://api.truthordarebot.xyz/v1/dare") - await update.effective_message.reply_text(dare_question) - - -@register(pattern="^/joke ?(.*)") -async def joke(event): - await event.reply(get_joke()) - - -async def roll(update: Update, context: ContextTypes.DEFAULT_TYPE): - await update.message.reply_text(random.choice(range(1, 7))) - - -async def flirt(update: Update, context: ContextTypes.DEFAULT_TYPE): - args = context.args - await update.effective_message.reply_text(random.choice(fun_strings.FLIRT)) - - -async def toss(update: Update, context: ContextTypes.DEFAULT_TYPE): - await update.message.reply_text(random.choice(fun_strings.TOSS)) - - -async def shrug(update: Update, context: ContextTypes.DEFAULT_TYPE): - msg = update.effective_message - reply_text = ( - msg.reply_to_message.reply_text if msg.reply_to_message else msg.reply_text - ) - await reply_text(r"¯\_(ツ)_/¯") - - -async def bluetext(update: Update, context: ContextTypes.DEFAULT_TYPE): - msg = update.effective_message - reply_text = ( - msg.reply_to_message.reply_text if msg.reply_to_message else msg.reply_text - ) - await reply_text( - "/BLUE /TEXT\n/MUST /CLICK\n/I /AM /A /STUPID /ANIMAL /THAT /IS /ATTRACTED /TO /COLORS" - ) - - -async def rlg(update: Update, context: ContextTypes.DEFAULT_TYPE): - eyes = random.choice(fun_strings.EYES) - mouth = random.choice(fun_strings.MOUTHS) - ears = random.choice(fun_strings.EARS) - - if len(eyes) == 2: - repl = ears[0] + eyes[0] + mouth[0] + eyes[1] + ears[1] - else: - repl = ears[0] + eyes[0] + mouth[0] + eyes[0] + ears[1] - await update.message.reply_text(repl) - - -async def decide(update: Update, context: ContextTypes.DEFAULT_TYPE): - reply_text = ( - update.effective_message.reply_to_message.reply_text - if update.effective_message.reply_to_message - else update.effective_message.reply_text - ) - await reply_text(random.choice(fun_strings.DECIDE)) - - -normiefont = [ - "a", - "b", - "c", - "d", - "e", - "f", - "g", - "h", - "i", - "j", - "k", - "l", - "m", - "n", - "o", - "p", - "q", - "r", - "s", - "t", - "u", - "v", - "w", - "x", - "y", - "z", -] - -weebyfont = [ - "卂", - "乃", - "匚", - "刀", - "乇", - "下", - "厶", - "卄", - "工", - "丁", - "长", - "乚", - "从", - "𠘨", - "口", - "尸", - "㔿", - "尺", - "丂", - "丅", - "凵", - "リ", - "山", - "乂", - "丫", - "乙", -] - - -async def webify(update: Update, context: ContextTypes.DEFAULT_TYPE): - args = context.args - message = update.effective_message - string = "" - - if message.reply_to_message: - string = message.reply_to_message.text.lower().replace(" ", " ") - - if args: - string = " ".join(args).lower() - - if not string: - await message.reply_text( - "Usage is `/weebify <text>`", parse_mode=ParseMode.MARKDOWN - ) - return - - for normiecharacter in string: - if normiecharacter in normiefont: - weebycharacter = weebyfont[normiefont.index(normiecharacter)] - string = string.replace(normiecharacter, weebycharacter) - - if message.reply_to_message: - await message.reply_to_message.reply_text(string) - else: - await message.reply_text(string) - - -# <=================================================== HELP ====================================================> - - -__help__ = """ -» /cosplay: sends cosplay images. - -» /decide: randomly answers yes/no/maybe. - -» /truth: sends a random truth string. - -» /dare: sends a random dare string. - -» /toss: tosses a coin. - -» /shrug: get shrug xd. - -» /bluetext: check yourself :V. - -» /roll: roll a dice. - -» /rlg: join ears, nose, mouth and create an emo ;-; - -» /weebify <text>: returns a weebified text. - -» /flirt <text>: returns a flirt text. - -» /joke <text>: tells a random joke. -""" - -# <================================================ HANDLER =======================================================> -ROLL_HANDLER = DisableAbleCommandHandler("roll", roll, block=False) -TOSS_HANDLER = DisableAbleCommandHandler("toss", toss, block=False) -SHRUG_HANDLER = DisableAbleCommandHandler("shrug", shrug, block=False) -BLUETEXT_HANDLER = DisableAbleCommandHandler("bluetext", bluetext, block=False) -RLG_HANDLER = DisableAbleCommandHandler("rlg", rlg, block=False) -DECIDE_HANDLER = DisableAbleCommandHandler("decide", decide, block=False) -WEEBIFY_HANDLER = DisableAbleCommandHandler("weebify", webify, block=False) -FLIRT_HANDLER = DisableAbleCommandHandler("flirt", flirt, block=False) -TRUTH_HANDLER = DisableAbleCommandHandler("truth", truth, block=False) -DARE_HANDLER = DisableAbleCommandHandler("dare", dare, block=False) - -function(WEEBIFY_HANDLER) -function(ROLL_HANDLER) -function(TOSS_HANDLER) -function(SHRUG_HANDLER) -function(BLUETEXT_HANDLER) -function(RLG_HANDLER) -function(DECIDE_HANDLER) -function(FLIRT_HANDLER) -function(TRUTH_HANDLER) -function(DARE_HANDLER) - -__mod_name__ = "FUN" -__command_list__ = [ - "roll", - "toss", - "shrug", - "bluetext", - "rlg", - "decide", - "cosplay", - "weebify", - "flirt", - "truth", - "dare", -] -__handlers__ = [ - ROLL_HANDLER, - TOSS_HANDLER, - SHRUG_HANDLER, - BLUETEXT_HANDLER, - RLG_HANDLER, - DECIDE_HANDLER, - WEEBIFY_HANDLER, - FLIRT_HANDLER, - TRUTH_HANDLER, - DARE_HANDLER, -] -# <================================================ END =======================================================> diff --git a/Mikobot/plugins/gban.py b/Mikobot/plugins/gban.py deleted file mode 100644 index d950be4bc9ccf519c5b2e178c3162d0a6568fbb4..0000000000000000000000000000000000000000 --- a/Mikobot/plugins/gban.py +++ /dev/null @@ -1,544 +0,0 @@ -# <============================================== IMPORTS =========================================================> -import html -import time -from datetime import datetime -from io import BytesIO - -from telegram import ChatMemberAdministrator, Update -from telegram.constants import ParseMode -from telegram.error import BadRequest, Forbidden, TelegramError -from telegram.ext import CommandHandler, ContextTypes, MessageHandler, filters -from telegram.helpers import mention_html - -import Database.sql.global_bans_sql as sql -from Database.sql.users_sql import get_user_com_chats -from Mikobot import ( - DEV_USERS, - DRAGONS, - EVENT_LOGS, - OWNER_ID, - STRICT_GBAN, - SUPPORT_CHAT, - dispatcher, - function, -) -from Mikobot.plugins.helper_funcs.chat_status import ( - check_admin, - is_user_admin, - support_plus, -) -from Mikobot.plugins.helper_funcs.extraction import extract_user, extract_user_and_text -from Mikobot.plugins.helper_funcs.misc import send_to_list - -# <=======================================================================================================> - -GBAN_ENFORCE_GROUP = 6 - -GBAN_ERRORS = { - "User is an administrator of the chat", - "Chat not found", - "Not enough rights to restrict/unrestrict chat member", - "User_not_participant", - "Peer_id_invalid", - "Group chat was deactivated", - "Need to be inviter of a user to kick it from a basic group", - "Chat_admin_required", - "Only the creator of a basic group can kick group administrators", - "Channel_private", - "Not in the chat", - "Can't remove chat owner", -} - -UNGBAN_ERRORS = { - "User is an administrator of the chat", - "Chat not found", - "Not enough rights to restrict/unrestrict chat member", - "User_not_participant", - "Method is available for supergroup and channel chats only", - "Not in the chat", - "Channel_private", - "Chat_admin_required", - "Peer_id_invalid", - "User not found", -} - - -# <================================================ FUNCTION =======================================================> -@support_plus -async def gban(update: Update, context: ContextTypes.DEFAULT_TYPE): - bot, args = context.bot, context.args - message = update.effective_message - user = update.effective_user - chat = update.effective_chat - log_message = "" - - user_id, reason = await extract_user_and_text(message, context, args) - - if not user_id: - await message.reply_text( - "You don't seem to be referring to a user or the ID specified is incorrect..", - ) - return - - if int(user_id) in DEV_USERS: - await message.reply_text( - "That user is part of the Association\nI can't act against our own.", - ) - return - - if int(user_id) in DRAGONS: - await message.reply_text( - "I spy, with my little eye... a disaster! Why are you guys turning on each other?", - ) - return - - if user_id == bot.id: - await message.reply_text("You uhh...want me to kick myself?") - return - - if user_id in [777000, 1087968824]: - await message.reply_text("Fool! You can't attack Telegram's native tech!") - return - - try: - user_chat = await bot.get_chat(user_id) - except BadRequest as excp: - if excp.message == "User not found": - await message.reply_text("I can't seem to find this user.") - return "" - else: - return - - if user_chat.type != "private": - await message.reply_text("That's not a user!") - return - - if sql.is_user_gbanned(user_id): - if not reason: - await message.reply_text( - "This user is already gbanned; I'd change the reason, but you haven't given me one...", - ) - return - - old_reason = sql.update_gban_reason( - user_id, - user_chat.username or user_chat.first_name, - reason, - ) - if old_reason: - await message.reply_text( - "This user is already gbanned, for the following reason:\n" - "<code>{}</code>\n" - "I've gone and updated it with your new reason!".format( - html.escape(old_reason), - ), - parse_mode=ParseMode.HTML, - ) - - else: - await message.reply_text( - "This user is already gbanned, but had no reason set; I've gone and updated it!", - ) - - return - - await message.reply_text("On it!") - - start_time = time.time() - datetime_fmt = "%Y-%m-%dT%H:%M" - current_time = datetime.utcnow().strftime(datetime_fmt) - - if chat.type != "private": - chat_origin = "<b>{} ({})</b>\n".format(html.escape(chat.title), chat.id) - else: - chat_origin = "<b>{}</b>\n".format(chat.id) - - log_message = ( - f"#GBANNED\n" - f"<b>Originated from:</b> <code>{chat_origin}</code>\n" - f"<b>Admin:</b> {mention_html(user.id, user.first_name)}\n" - f"<b>Banned User:</b> {mention_html(user_chat.id, user_chat.first_name)}\n" - f"<b>Banned User ID:</b> <code>{user_chat.id}</code>\n" - f"<b>Event Stamp:</b> <code>{current_time}</code>" - ) - - if reason: - if chat.type == chat.SUPERGROUP and chat.username: - log_message += f'\n<b>Reason:</b> <a href="https://telegram.me/{chat.username}/{message.message_id}">{reason}</a>' - else: - log_message += f"\n<b>Reason:</b> <code>{reason}</code>" - - if EVENT_LOGS: - try: - log = await bot.send_message( - EVENT_LOGS, log_message, parse_mode=ParseMode.HTML - ) - except BadRequest as excp: - log = await bot.send_message( - EVENT_LOGS, - log_message - + "\n\nFormatting has been disabled due to an unexpected error.", - ) - - else: - send_to_list(bot, DRAGONS, log_message, html=True) - - sql.gban_user(user_id, user_chat.username or user_chat.first_name, reason) - - chats = get_user_com_chats(user_id) - gbanned_chats = 0 - - for chat in chats: - chat_id = int(chat) - - # Check if this group has disabled gbans - if not sql.does_chat_gban(chat_id): - continue - - try: - await bot.ban_chat_member(chat_id, user_id) - gbanned_chats += 1 - - except BadRequest as excp: - if excp.message in GBAN_ERRORS: - pass - else: - await message.reply_text(f"Could not gban due to: {excp.message}") - if EVENT_LOGS: - await bot.send_message( - EVENT_LOGS, - f"Could not gban due to {excp.message}", - parse_mode=ParseMode.HTML, - ) - else: - send_to_list( - bot, - DRAGONS, - f"Could not gban due to: {excp.message}", - ) - sql.ungban_user(user_id) - return - except TelegramError: - pass - - if EVENT_LOGS: - await log.edit_text( - log_message + f"\n<b>Chats affected:</b> <code>{gbanned_chats}</code>", - parse_mode=ParseMode.HTML, - ) - else: - send_to_list( - bot, - DRAGONS, - f"Gban complete! (User banned in <code>{gbanned_chats}</code> chats)", - html=True, - ) - - end_time = time.time() - gban_time = round((end_time - start_time), 2) - - if gban_time > 60: - gban_time = round((gban_time / 60), 2) - await message.reply_text("Done! Gbanned.", parse_mode=ParseMode.HTML) - else: - await message.reply_text("Done! Gbanned.", parse_mode=ParseMode.HTML) - - try: - await bot.send_message( - user_id, - "#EVENT" - "You have been marked as Malicious and as such have been banned from any future groups we manage." - f"\n<b>Reason:</b> <code>{html.escape(user.reason)}</code>" - f"</b>Appeal Chat:</b> @{SUPPORT_CHAT}", - parse_mode=ParseMode.HTML, - ) - except: - pass # bot probably blocked by user - - -@support_plus -async def ungban(update: Update, context: ContextTypes.DEFAULT_TYPE): - bot, args = context.bot, context.args - message = update.effective_message - user = update.effective_user - chat = update.effective_chat - log_message = "" - - user_id = await extract_user(message, context, args) - - if not user_id: - await message.reply_text( - "You don't seem to be referring to a user or the ID specified is incorrect..", - ) - return - - user_chat = await bot.get_chat(user_id) - if user_chat.type != "private": - await message.reply_text("That's not a user!") - return - - if not sql.is_user_gbanned(user_id): - await message.reply_text("This user is not gbanned!") - return - - await message.reply_text( - f"I'll give {user_chat.first_name} a second chance, globally." - ) - - start_time = time.time() - datetime_fmt = "%Y-%m-%dT%H:%M" - current_time = datetime.utcnow().strftime(datetime_fmt) - - if chat.type != "private": - chat_origin = f"<b>{html.escape(chat.title)} ({chat.id})</b>\n" - else: - chat_origin = f"<b>{chat.id}</b>\n" - - log_message = ( - f"#UNGBANNED\n" - f"<b>Originated from:</b> <code>{chat_origin}</code>\n" - f"<b>Admin:</b> {mention_html(user.id, user.first_name)}\n" - f"<b>Unbanned User:</b> {mention_html(user_chat.id, user_chat.first_name)}\n" - f"<b>Unbanned User ID:</b> <code>{user_chat.id}</code>\n" - f"<b>Event Stamp:</b> <code>{current_time}</code>" - ) - - if EVENT_LOGS: - try: - log = await bot.send_message( - EVENT_LOGS, log_message, parse_mode=ParseMode.HTML - ) - except BadRequest as excp: - log = await bot.send_message( - EVENT_LOGS, - log_message - + "\n\nFormatting has been disabled due to an unexpected error.", - ) - else: - send_to_list(bot, DRAGONS, log_message, html=True) - - chats = get_user_com_chats(user_id) - ungbanned_chats = 0 - - for chat in chats: - chat_id = int(chat) - - # Check if this group has disabled gbans - if not sql.does_chat_gban(chat_id): - continue - - try: - member = await bot.get_chat_member(chat_id, user_id) - if member.status == "kicked": - await bot.unban_chat_member(chat_id, user_id) - ungbanned_chats += 1 - - except BadRequest as excp: - if excp.message in UNGBAN_ERRORS: - pass - else: - await message.reply_text(f"Could not un-gban due to: {excp.message}") - if EVENT_LOGS: - await bot.send_message( - EVENT_LOGS, - f"Could not un-gban due to: {excp.message}", - parse_mode=ParseMode.HTML, - ) - else: - await bot.send_message( - OWNER_ID, - f"Could not un-gban due to: {excp.message}", - ) - return - except TelegramError: - pass - - sql.ungban_user(user_id) - - if EVENT_LOGS: - await log.edit_text( - log_message + f"\n<b>Chats affected:</b> {ungbanned_chats}", - parse_mode=ParseMode.HTML, - ) - else: - send_to_list(bot, DRAGONS, "un-gban complete!") - - end_time = time.time() - ungban_time = round((end_time - start_time), 2) - - if ungban_time > 60: - ungban_time = round((ungban_time / 60), 2) - await message.reply_text(f"Person has been un-gbanned. Took {ungban_time} min") - else: - await message.reply_text(f"Person has been un-gbanned. Took {ungban_time} sec") - - -@support_plus -async def gbanlist(update: Update, context: ContextTypes.DEFAULT_TYPE): - banned_users = sql.get_gban_list() - - if not banned_users: - await update.effective_message.reply_text( - "There aren't any gbanned users! You're kinder than I expected...", - ) - return - - banfile = "Screw these guys.\n" - for user in banned_users: - banfile += f"[x] {user['name']} - {user['user_id']}\n" - if user["reason"]: - banfile += f"Reason: {user['reason']}\n" - - with BytesIO(str.encode(banfile)) as output: - output.name = "gbanlist.txt" - await update.effective_message.reply_document( - document=output, - filename="gbanlist.txt", - caption="Here is the list of currently gbanned users.", - ) - - -async def check_and_ban(update, user_id, should_message=True): - if sql.is_user_gbanned(user_id): - await update.effective_chat.ban_member(user_id) - if should_message: - text = ( - f"<b>Alert</b>: this user is globally banned.\n" - f"<code>*bans them from here*</code>.\n" - f"<b>Appeal chat</b>: @{SUPPORT_CHAT}\n" - f"<b>User ID</b>: <code>{user_id}</code>" - ) - user = sql.get_gbanned_user(user_id) - if user.reason: - text += f"\n<b>Ban Reason:</b> <code>{html.escape(user.reason)}</code>" - await update.effective_message.reply_text(text, parse_mode=ParseMode.HTML) - - -async def enforce_gban(update: Update, context: ContextTypes.DEFAULT_TYPE): - # Not using @restrict handler to avoid spamming - just ignore if cant gban. - bot = context.bot - try: - get_member = await update.effective_chat.get_member( - bot.id, - ) - if isinstance(get_member, ChatMemberAdministrator): - restrict_permission = get_member.can_restrict_members - else: - return - except Forbidden: - return - if sql.does_chat_gban(update.effective_chat.id) and restrict_permission: - user = update.effective_user - chat = update.effective_chat - msg = update.effective_message - - if user and not await is_user_admin(chat, user.id): - await check_and_ban(update, user.id) - return - - if msg.new_chat_members: - new_members = update.effective_message.new_chat_members - for mem in new_members: - await check_and_ban(update, mem.id) - - if msg.reply_to_message: - user = msg.reply_to_message.from_user - if user and not await is_user_admin(chat, user.id): - await check_and_ban(update, user.id, should_message=False) - - -@check_admin(is_user=True) -async def gbanstat(update: Update, context: ContextTypes.DEFAULT_TYPE): - args = context.args - if len(args) > 0: - if args[0].lower() in ["on", "yes"]: - sql.enable_gbans(update.effective_chat.id) - await update.effective_message.reply_text( - "Antispam is now enabled ✅ " - "I am now protecting your group from potential remote threats!", - ) - elif args[0].lower() in ["off", "no"]: - sql.disable_gbans(update.effective_chat.id) - await update.effective_message.reply_text( - "I am not now protecting your group from potential remote threats!", - ) - else: - await update.effective_message.reply_text( - "Give me some arguments to choose a setting! on/off, yes/no!\n\n" - "Your current setting is: {}\n" - "When True, any gbans that happen will also happen in your group. " - "When False, they won't, leaving you at the possible mercy of " - "spammers.".format(sql.does_chat_gban(update.effective_chat.id)), - ) - - -def __stats__(): - return f"• {sql.num_gbanned_users()} gbanned users." - - -def __user_info__(user_id): - is_gbanned = sql.is_user_gbanned(user_id) - text = "Malicious: <b>{}</b>" - if user_id in [777000, 1087968824]: - return "" - if user_id == dispatcher.bot.id: - return "" - if int(user_id) in DRAGONS: - return "" - if is_gbanned: - text = text.format("Yes") - user = sql.get_gbanned_user(user_id) - if user.reason: - text += f"\n<b>Reason:</b> <code>{html.escape(user.reason)}</code>" - text += f"\n<b>Appeal Chat:</b> @{SUPPORT_CHAT}" - else: - text = text.format("???") - return text - - -def __migrate__(old_chat_id, new_chat_id): - sql.migrate_chat(old_chat_id, new_chat_id) - - -def __chat_settings__(chat_id, user_id): - return f"This chat is enforcing *gbans*: `{sql.does_chat_gban(chat_id)}`." - - -# <=================================================== HELP ====================================================> - - -__help__ = f""" -➠ *Admins only:* - -» /antispam <on/off/yes/no>: Will toggle our antispam tech or return your current settings. - -➠ Anti-Spam, used by bot devs to ban spammers across all groups. This helps protect \ -you and your groups by removing spam flooders as quickly as possible. -➠ *Note:* Users can appeal gbans or report spammers at @hydraX2support -""" - -# <================================================ HANDLER =======================================================> -GBAN_HANDLER = CommandHandler("gban", gban, block=False) -UNGBAN_HANDLER = CommandHandler("ungban", ungban, block=False) -GBAN_LIST = CommandHandler("gbanlist", gbanlist, block=False) - -GBAN_STATUS = CommandHandler( - "antispam", gbanstat, filters=filters.ChatType.GROUPS, block=False -) - -GBAN_ENFORCER = MessageHandler( - filters.ALL & filters.ChatType.GROUPS, enforce_gban, block=False -) - -function(GBAN_HANDLER) -function(UNGBAN_HANDLER) -function(GBAN_LIST) -function(GBAN_STATUS) - -__mod_name__ = "ANTI-SPAM" -__handlers__ = [GBAN_HANDLER, UNGBAN_HANDLER, GBAN_LIST, GBAN_STATUS] - -if STRICT_GBAN: # enforce GBANS if this is set - function(GBAN_ENFORCER, GBAN_ENFORCE_GROUP) - __handlers__.append((GBAN_ENFORCER, GBAN_ENFORCE_GROUP)) -# <================================================ END =======================================================> diff --git a/Mikobot/plugins/helper_funcs/alternate.py b/Mikobot/plugins/helper_funcs/alternate.py deleted file mode 100644 index af957e4e0797e992b4321bff48b52fec859675fb..0000000000000000000000000000000000000000 --- a/Mikobot/plugins/helper_funcs/alternate.py +++ /dev/null @@ -1,85 +0,0 @@ -# <============================================== IMPORTS =========================================================> -from functools import wraps - -from telegram import Message, Update -from telegram.constants import ChatAction -from telegram.error import BadRequest -from telegram.ext import ContextTypes - -# <=======================================================================================================> - - -# <================================================ FUNCTION =======================================================> -async def send_message(message: Message, text, *args, **kwargs): - try: - return await message.reply_text(text, *args, **kwargs) - except BadRequest as err: - if str(err) == "Reply message not found": - return await message.reply_text(text, quote=False, *args, **kwargs) - - -def typing_action(func): - """Sends typing action while processing func command.""" - - @wraps(func) - async def command_func( - update: Update, context: ContextTypes.DEFAULT_TYPE, *args, **kwargs - ): - await context.bot.send_chat_action( - chat_id=update.effective_chat.id, - action=ChatAction.TYPING, - ) - return await func(update, context, *args, **kwargs) - - return command_func - - -def sticker_action(func): - """Sends typing action while processing func command.""" - - @wraps(func) - async def command_func( - update: Update, context: ContextTypes.DEFAULT_TYPE, *args, **kwargs - ): - await context.bot.send_chat_action( - chat_id=update.effective_chat.id, - action=ChatAction.CHOOSE_STICKER, - ) - return await func(update, context, *args, **kwargs) - - return command_func - - -def document_action(func): - """Sends typing action while processing func command.""" - - @wraps(func) - async def command_func( - update: Update, context: ContextTypes.DEFAULT_TYPE, *args, **kwargs - ): - await context.bot.send_chat_action( - chat_id=update.effective_chat.id, - action=ChatAction.UPLOAD_DOCUMENT, - ) - return await func(update, context, *args, **kwargs) - - return command_func - - -def photo_action(func): - """Sends typing action while processing func command.""" - - @wraps(func) - async def command_func( - update: Update, context: ContextTypes.DEFAULT_TYPE, *args, **kwargs - ): - await context.bot.send_chat_action( - chat_id=update.effective_chat.id, - action=ChatAction.UPLOAD_PHOTO, - ) - return await func(update, context, *args, **kwargs) - - return command_func - - -# <================================================ END =======================================================> diff --git a/Mikobot/plugins/helper_funcs/chat_status.py b/Mikobot/plugins/helper_funcs/chat_status.py deleted file mode 100644 index 786959db8dbeb0d015b243873423be4a496f73cc..0000000000000000000000000000000000000000 --- a/Mikobot/plugins/helper_funcs/chat_status.py +++ /dev/null @@ -1,415 +0,0 @@ -# <============================================== IMPORTS =========================================================> -from functools import wraps -from threading import RLock -from time import perf_counter - -from cachetools import TTLCache -from telegram import Chat, ChatMember, ChatMemberAdministrator, ChatMemberOwner, Update -from telegram.constants import ChatMemberStatus, ChatType -from telegram.error import Forbidden -from telegram.ext import ContextTypes - -from Mikobot import DEL_CMDS, DEV_USERS, DRAGONS, SUPPORT_CHAT, dispatcher - -# <=======================================================================================================> - -# stores admemes in memory for 10 min. -ADMIN_CACHE = TTLCache(maxsize=512, ttl=60 * 10, timer=perf_counter) -THREAD_LOCK = RLock() - - -# <================================================ FUNCTION =======================================================> -def check_admin( - permission: str = None, - is_bot: bool = False, - is_user: bool = False, - is_both: bool = False, - only_owner: bool = False, - only_sudo: bool = False, - only_dev: bool = False, - no_reply: object = False, -) -> object: - """Check for permission level to perform some operations - - Args: - permission (str, optional): permission type to check. Defaults to None. - is_bot (bool, optional): if bot can perform the action. Defaults to False. - is_user (bool, optional): if user can perform the action. Defaults to False. - is_both (bool, optional): if both user and bot can perform the action. Defaults to False. - only_owner (bool, optional): if only owner can perform the action. Defaults to False. - only_sudo (bool, optional): if only sudo users can perform the operation. Defaults to False. - only_dev (bool, optional): if only dev users can perform the operation. Defaults to False. - no_reply (boot, optional): if should not reply. Defaults to False. - """ - - def wrapper(func): - @wraps(func) - async def wrapped( - update: Update, context: ContextTypes.DEFAULT_TYPE, *args, **kwargs - ): - nonlocal permission - chat = update.effective_chat - user = update.effective_user - message = update.effective_message - - if chat.type == ChatType.PRIVATE and not ( - only_dev or only_sudo or only_owner - ): - return await func(update, context, *args, **kwargs) - - bot_member = ( - await chat.get_member(context.bot.id) if is_bot or is_both else None - ) - user_member = await chat.get_member(user.id) if is_user or is_both else None - - if only_owner: - if isinstance(user_member, ChatMemberOwner) or user.id in DEV_USERS: - return await func(update, context, *args, **kwargs) - else: - return await message.reply_text( - "Only chat owner can perform this action." - ) - if only_dev: - if user.id in DEV_USERS: - return await func(update, context, *args, **kwargs) - else: - return await update.effective_message.reply_text( - "Hey little kid" - "\nWho the hell are you to say me what to execute on my server?", - ) - - if only_sudo: - if user.id in DRAGONS: - return await func(update, context, *args, **kwargs) - else: - return await update.effective_message.reply_text( - "Who the hell are you to say me what to do?", - ) - - if message.from_user.id == 1087968824: - return await func(update, context, *args, **kwargs) - - if permission: - no_permission = permission.replace("_", " ").replace("can", "") - if is_bot: - if ( - getattr(bot_member, permission) - if isinstance(bot_member, ChatMemberAdministrator) - else False - ): - return await func(update, context, *args, **kwargs) - elif no_reply: - return - else: - return await message.reply_text( - f"I don't have permission to {no_permission}." - ) - if is_user: - if isinstance(user_member, ChatMemberOwner): - return await func(update, context, *args, **kwargs) - elif ( - getattr(user_member, permission) - if isinstance(user_member, ChatMemberAdministrator) - else False or user.id in DRAGONS - ): - return await func(update, context, *args, **kwargs) - elif no_reply: - return - else: - return await message.reply_text( - f"You don't have permission to {no_permission}." - ) - if is_both: - if ( - getattr(bot_member, permission) - if isinstance(bot_member, ChatMemberAdministrator) - else False - ): - pass - elif no_reply: - return - else: - return await message.reply_text( - f"I don't have permission to {no_permission}." - ) - - if isinstance(user_member, ChatMemberOwner) or user.id in DEV_USERS: - pass - elif ( - getattr(user_member, permission) - if isinstance(user_member, ChatMemberAdministrator) - else False or user.id in DRAGONS - ): - pass - elif no_reply: - return - else: - return await message.reply_text( - f"You don't have permission to {no_permission}." - ) - return await func(update, context, *args, **kwargs) - else: - if is_bot: - if bot_member.status == ChatMemberStatus.ADMINISTRATOR: - return await func(update, context, *args, **kwargs) - else: - return await message.reply_text("I'm not admin here.") - elif is_user: - if user_member.status in [ - ChatMemberStatus.ADMINISTRATOR, - ChatMemberStatus.OWNER, - ]: - return await func(update, context, *args, **kwargs) - elif user.id in DRAGONS: - return await func(update, context, *args, **kwargs) - else: - return await message.reply_text("You are not admin here.") - elif is_both: - if bot_member.status == ChatMemberStatus.ADMINISTRATOR: - pass - else: - return await message.reply_text("I'm not admin here.") - - if user_member.status in [ - ChatMemberStatus.ADMINISTRATOR, - ChatMemberStatus.OWNER, - ]: - pass - elif user.id in DRAGONS: - pass - else: - return await message.reply_text("You are not admin here.") - return await func(update, context, *args, **kwargs) - - return wrapped - - return wrapper - - -def is_whitelist_plus(chat: Chat, user_id: int, member: ChatMember = None) -> bool: - return any(user_id in user for user in [DRAGONS, DEV_USERS]) - - -def is_support_plus(chat: Chat, user_id: int, member: ChatMember = None) -> bool: - return user_id in DRAGONS or user_id in DEV_USERS - - -def is_sudo_plus(chat: Chat, user_id: int, member: ChatMember = None) -> bool: - return user_id in DRAGONS or user_id in DEV_USERS - - -async def is_user_admin(chat: Chat, user_id: int, member: ChatMember = None) -> bool: - if ( - chat.type == "private" - or user_id in DRAGONS - or user_id in DEV_USERS - or user_id in [777000, 1087968824] - ): # Count telegram and Group Anonymous as admin - return True - if not member: - with THREAD_LOCK: - # try to fetch from cache first. - try: - return user_id in ADMIN_CACHE[chat.id] - except KeyError: - # keyerror happend means cache is deleted, - # so query bot api again and return user status - # while saving it in cache for future usage... - try: - chat_admins = await dispatcher.bot.getChatAdministrators(chat.id) - except Forbidden: - return False - admin_list = [x.user.id for x in chat_admins] - ADMIN_CACHE[chat.id] = admin_list - - return user_id in admin_list - else: - return member.status in (ChatMemberStatus.ADMINISTRATOR, ChatMemberStatus.OWNER) - - -async def is_bot_admin(chat: Chat, bot_id: int, bot_member: ChatMember = None) -> bool: - if chat.type == "private": - return True - - if not bot_member: - bot_member = await chat.get_member(bot_id) - - return bot_member.status in (ChatMemberStatus.ADMINISTRATOR, ChatMemberStatus.OWNER) - - -async def can_delete(chat: Chat, bot_id: int) -> bool: - chat_member = await chat.get_member(bot_id) - if isinstance(chat_member, ChatMemberAdministrator): - return chat_member.can_delete_messages - - -async def is_user_ban_protected( - chat: Chat, user_id: int, member: ChatMember = None -) -> bool: - if ( - chat.type == "private" - or user_id in DRAGONS - or user_id in DEV_USERS - or user_id in [777000, 1087968824] - ): # Count telegram and Group Anonymous as admin - return True - - if not member: - member = await chat.get_member(user_id) - - return member.status in (ChatMemberStatus.ADMINISTRATOR, ChatMemberStatus.OWNER) - - -async def is_user_in_chat(chat: Chat, user_id: int) -> bool: - member = await chat.get_member(user_id) - return member.status in (ChatMemberStatus.LEFT, ChatMemberStatus.RESTRICTED) - - -def dev_plus(func): - @wraps(func) - def is_dev_plus_func( - update: Update, context: ContextTypes.DEFAULT_TYPE, *args, **kwargs - ): - context.bot - user = update.effective_user - - if user.id in DEV_USERS: - return func(update, context, *args, **kwargs) - elif not user: - pass - elif DEL_CMDS and " " not in update.effective_message.text: - try: - update.effective_message.delete() - except: - pass - else: - update.effective_message.reply_text( - "This is a developer restricted command." - " You do not have permissions to run this." - ) - - return is_dev_plus_func - - -def sudo_plus(func): - @wraps(func) - def is_sudo_plus_func( - update: Update, context: ContextTypes.DEFAULT_TYPE, *args, **kwargs - ): - context.bot - user = update.effective_user - chat = update.effective_chat - - if user and is_sudo_plus(chat, user.id): - return func(update, context, *args, **kwargs) - elif not user: - pass - elif DEL_CMDS and " " not in update.effective_message.text: - try: - update.effective_message.delete() - except: - pass - else: - update.effective_message.reply_text( - "Who dis non-admin telling me what to do? You want a punch?" - ) - - return is_sudo_plus_func - - -def support_plus(func): - @wraps(func) - async def is_support_plus_func( - update: Update, context: ContextTypes.DEFAULT_TYPE, *args, **kwargs - ): - bot = context.bot - user = update.effective_user - chat = update.effective_chat - - if user and is_support_plus(chat, user.id): - return await func(update, context, *args, **kwargs) - elif DEL_CMDS and " " not in update.effective_message.text: - try: - await update.effective_message.delete() - except: - pass - - return is_support_plus_func - - -def whitelist_plus(func): - @wraps(func) - async def is_whitelist_plus_func( - update: Update, - context: ContextTypes.DEFAULT_TYPE, - *args, - **kwargs, - ): - bot = context.bot - user = update.effective_user - chat = update.effective_chat - - if user and is_whitelist_plus(chat, user.id): - return await func(update, context, *args, **kwargs) - else: - await update.effective_message.reply_text( - f"You don't have access to use this.\nVisit @{SUPPORT_CHAT}", - ) - - return is_whitelist_plus_func - - -def user_not_admin(func): - @wraps(func) - async def is_not_admin( - update: Update, context: ContextTypes.DEFAULT_TYPE, *args, **kwargs - ): - bot = context.bot - user = update.effective_user - chat = update.effective_chat - - if user and not await is_user_admin(chat, user.id): - return await func(update, context, *args, **kwargs) - elif not user: - pass - - return is_not_admin - - -def connection_status(func): - @wraps(func) - async def connected_status( - update: Update, context: ContextTypes.DEFAULT_TYPE, *args, **kwargs - ): - conn = await connected( - context.bot, - update, - update.effective_chat, - update.effective_user.id, - need_admin=False, - ) - - if conn: - chat = await dispatcher.bot.getChat(conn) - update.__setattr__("_effective_chat", chat) - return await func(update, context, *args, **kwargs) - else: - if update.effective_message.chat.type == "private": - await update.effective_message.reply_text( - "Send /connect in a group that you and I have in common first.", - ) - return connected_status - - return await func(update, context, *args, **kwargs) - - return connected_status - - -# <=======================================================================================================> - -# <=======================================================================================================> -# Workaround for circular import with connection.py -from Mikobot.plugins import connection - -connected = connection.connected -# <================================================ END =======================================================> diff --git a/Mikobot/plugins/helper_funcs/extraction.py b/Mikobot/plugins/helper_funcs/extraction.py deleted file mode 100644 index e9be7a7c9f755453fc4d2f845a84995c831c8cea..0000000000000000000000000000000000000000 --- a/Mikobot/plugins/helper_funcs/extraction.py +++ /dev/null @@ -1,190 +0,0 @@ -# <============================================== IMPORTS =========================================================> -from typing import List, Optional, Union - -from telegram import Message, MessageEntity -from telegram.error import BadRequest -from telegram.ext import ContextTypes - -from Mikobot import LOGGER -from Mikobot.plugins.users import get_user_id - -# <=======================================================================================================> - - -# <================================================ FUNCTION =======================================================> -async def id_from_reply(message: Message): - prev_message = message.reply_to_message - if not prev_message or prev_message.forum_topic_created: - return None, None - user_id = prev_message.from_user.id - # if user id is from channel bot, then fetch channel id from sender_chat - if user_id == 136817688: - user_id = message.reply_to_message.sender_chat.id - res = message.text.split(None, 1) - if len(res) < 2: - return user_id, "" - return user_id, res[1] - - -async def extract_user( - message: Message, - context: ContextTypes.DEFAULT_TYPE, - args: List[str], -) -> Optional[int]: - return (await extract_user_and_text(message, context, args))[0] - - -async def extract_user_and_text( - message: Message, - context: ContextTypes.DEFAULT_TYPE, - args: List[str], -) -> Union[(Optional[int], Optional[str])]: - prev_message = message.reply_to_message - split_text = message.text.split(None, 1) - - if len(split_text) < 2: - return await id_from_reply(message) # only option possible - - text_to_parse = split_text[1] - - text = "" - - entities = list(message.parse_entities([MessageEntity.TEXT_MENTION])) - ent = entities[0] if entities else None - # if entity offset matches (command end/text start) then all good - if entities and ent and ent.offset == len(message.text) - len(text_to_parse): - ent = entities[0] - user_id = ent.user.id - text = message.text[ent.offset + ent.length :] - - elif len(args) >= 1 and args[0][0] == "@": - user = args[0] - user_id = await get_user_id(user) - if not user_id: - await message.reply_text( - "No idea who this user is. You'll be able to interact with them if " - "you reply to that person's message instead, or forward one of that user's messages.", - ) - return None, None - - else: - user_id = user_id - res = message.text.split(None, 2) - if len(res) >= 3: - text = res[2] - - elif len(args) >= 1 and args[0].isdigit(): - user_id = int(args[0]) - res = message.text.split(None, 2) - if len(res) >= 3: - text = res[2] - - elif prev_message: - user_id, text = await id_from_reply(message) - - else: - return None, None - - try: - await context.bot.get_chat(user_id) - except BadRequest as excp: - if excp.message in ("User_id_invalid", "Chat not found"): - await message.reply_text( - "I don't seem to have interacted with this user before - please forward a message from " - "them to give me control! (like a voodoo doll, I need a piece of them to be able " - "to execute certain commands...)", - ) - else: - LOGGER.exception("Exception %s on user %s", excp.message, user_id) - - return None, None - - return user_id, text - - -async def extract_text(message) -> str: - return ( - message.text - or message.caption - or (message.sticker.emoji if message.sticker else None) - ) - - -async def extract_unt_fedban( - message: Message, context: ContextTypes.DEFAULT_TYPE, args: List[str] -) -> Union[(Optional[int], Optional[str])]: - prev_message = message.reply_to_message - split_text = message.text.split(None, 1) - - if len(split_text) < 2: - return await id_from_reply(message) # only option possible - - text_to_parse = split_text[1] - - text = "" - - entities = list(message.parse_entities([MessageEntity.TEXT_MENTION])) - ent = entities[0] if entities else None - # if entity offset matches (command end/text start) then all good - if entities and ent and ent.offset == len(message.text) - len(text_to_parse): - ent = entities[0] - user_id = ent.user.id - text = message.text[ent.offset + ent.length :] - - elif len(args) >= 1 and args[0][0] == "@": - user = args[0] - user_id = await get_user_id(user) - if not user_id and not isinstance(user_id, int): - await message.reply_text( - "I don't have that user in my db. " - "You'll be able to interact with them if you reply to that person's message instead, or forward one of that user's messages.", - ) - return None, None - - else: - user_id = user_id - res = message.text.split(None, 2) - if len(res) >= 3: - text = res[2] - - elif len(args) >= 1 and args[0].isdigit(): - user_id = int(args[0]) - res = message.text.split(None, 2) - if len(res) >= 3: - text = res[2] - - elif prev_message: - user_id, text = await id_from_reply(message) - - else: - return None, None - - try: - await context.bot.get_chat(user_id) - except BadRequest as excp: - if excp.message in ("User_id_invalid", "Chat not found") and not isinstance( - user_id, - int, - ): - await message.reply_text( - "I don't seem to have interacted with this user before " - "please forward a message from them to give me control! " - "(like a voodoo doll, I need a piece of them to be able to execute certain commands...)", - ) - return None, None - elif excp.message != "Chat not found": - LOGGER.exception("Exception %s on user %s", excp.message, user_id) - return None, None - elif not isinstance(user_id, int): - return None, None - - return user_id, text - - -async def extract_user_fban( - message: Message, context: ContextTypes.DEFAULT_TYPE, args: List[str] -) -> Optional[int]: - return (await extract_unt_fedban(message, context, args))[0] - - -# <================================================ END =======================================================> diff --git a/Mikobot/plugins/helper_funcs/misc.py b/Mikobot/plugins/helper_funcs/misc.py deleted file mode 100644 index 287b4792667fc5900f41c3ccdaf2f705c7a89452..0000000000000000000000000000000000000000 --- a/Mikobot/plugins/helper_funcs/misc.py +++ /dev/null @@ -1,263 +0,0 @@ -# <============================================== IMPORTS =========================================================> -from math import ceil -from typing import Dict, List -from uuid import uuid4 - -import cv2 -import ffmpeg -from telegram import ( - Bot, - InlineKeyboardButton, - InlineKeyboardMarkup, - InlineQueryResultArticle, - InputTextMessageContent, -) -from telegram.constants import MessageLimit, ParseMode -from telegram.error import TelegramError - -from Mikobot import NO_LOAD - -# <=======================================================================================================> - - -# <================================================ FUNCTION =======================================================> -class EqInlineKeyboardButton(InlineKeyboardButton): - def __eq__(self, other): - return self.text == other.text - - def __lt__(self, other): - return self.text < other.text - - def __gt__(self, other): - return self.text > other.text - - -def split_message(msg: str) -> List[str]: - if len(msg) < MessageLimit.MAX_TEXT_LENGTH: - return [msg] - - lines = msg.splitlines(True) - small_msg = "" - result = [] - for line in lines: - if len(small_msg) + len(line) < MessageLimit.MAX_TEXT_LENGTH: - small_msg += line - else: - result.append(small_msg) - small_msg = line - else: - # Else statement at the end of the for loop, so append the leftover string. - result.append(small_msg) - - return result - - -def paginate_modules(page_n: int, module_dict: Dict, prefix, chat=None) -> List: - if not chat: - modules = sorted( - [ - EqInlineKeyboardButton( - x.__mod_name__, - callback_data="{}_module({})".format( - prefix, x.__mod_name__.lower() - ), - ) - for x in module_dict.values() - ] - ) - else: - modules = sorted( - [ - EqInlineKeyboardButton( - x.__mod_name__, - callback_data="{}_module({},{})".format( - prefix, chat, x.__mod_name__.lower() - ), - ) - for x in module_dict.values() - ] - ) - - pairs = [modules[i * 3 : (i + 1) * 3] for i in range((len(modules) + 3 - 1) // 3)] - - round_num = len(modules) / 3 - calc = len(modules) - round(round_num) - if calc in [1, 2]: - pairs.append((modules[-1],)) - - max_num_pages = ceil(len(pairs) / 6) - modulo_page = page_n % max_num_pages - - # can only have a certain amount of buttons side by side - if len(pairs) > 3: - pairs = pairs[modulo_page * 6 : 6 * (modulo_page + 1)] + [ - ( - EqInlineKeyboardButton( - "◁", callback_data="{}_prev({})".format(prefix, modulo_page) - ), - EqInlineKeyboardButton("» 𝙃𝙊𝙈𝙀 «", callback_data="Miko_back"), - EqInlineKeyboardButton( - "▷", callback_data="{}_next({})".format(prefix, modulo_page) - ), - ) - ] - - else: - pairs += [[EqInlineKeyboardButton("⇦ 𝘽𝘼𝘾𝙆", callback_data="Miko_back")]] - - return pairs - - -def article( - title: str = "", - description: str = "", - message_text: str = "", - thumb_url: str = None, - reply_markup: InlineKeyboardMarkup = None, - disable_web_page_preview: bool = False, -) -> InlineQueryResultArticle: - return InlineQueryResultArticle( - id=uuid4(), - title=title, - description=description, - thumb_url=thumb_url, - input_message_content=InputTextMessageContent( - message_text=message_text, - disable_web_page_preview=disable_web_page_preview, - ), - reply_markup=reply_markup, - ) - - -def send_to_list( - bot: Bot, send_to: list, message: str, markdown=False, html=False -) -> None: - if html and markdown: - raise Exception("Can only send with either markdown or HTML!") - for user_id in set(send_to): - try: - if markdown: - bot.send_message(user_id, message, parse_mode=ParseMode.MARKDOWN) - elif html: - bot.send_message(user_id, message, parse_mode=ParseMode.HTML) - else: - bot.send_message(user_id, message) - except TelegramError: - pass # ignore users who fail - - -def build_keyboard(buttons): - keyb = [] - for btn in buttons: - if btn.same_line and keyb: - keyb[-1].append(InlineKeyboardButton(btn.name, url=btn.url)) - else: - keyb.append([InlineKeyboardButton(btn.name, url=btn.url)]) - - return keyb - - -def revert_buttons(buttons): - res = "" - for btn in buttons: - if btn.same_line: - res += "\n[{}](buttonurl://{}:same)".format(btn.name, btn.url) - else: - res += "\n[{}](buttonurl://{})".format(btn.name, btn.url) - - return res - - -def build_keyboard_parser(bot, chat_id, buttons): - keyb = [] - for btn in buttons: - if btn.url == "{rules}": - btn.url = "http://t.me/{}?start={}".format(bot.username, chat_id) - if btn.same_line and keyb: - keyb[-1].append(InlineKeyboardButton(btn.name, url=btn.url)) - else: - keyb.append([InlineKeyboardButton(btn.name, url=btn.url)]) - - return keyb - - -def user_bot_owner(func): - @wraps(func) - def is_user_bot_owner(bot: Bot, update: Update, *args, **kwargs): - user = update.effective_user - if user and user.id == OWNER_ID: - return func(bot, update, *args, **kwargs) - else: - pass - - return is_user_bot_owner - - -def build_keyboard_alternate(buttons): - keyb = [] - for btn in buttons: - if btn[2] and keyb: - keyb[-1].append(InlineKeyboardButton(btn[0], url=btn[1])) - else: - keyb.append([InlineKeyboardButton(btn[0], url=btn[1])]) - - return keyb - - -def is_module_loaded(name): - return name not in NO_LOAD - - -def convert_gif(input): - """Function to convert mp4 to webm(vp9)""" - - vid = cv2.VideoCapture(input) - height = vid.get(cv2.CAP_PROP_FRAME_HEIGHT) - width = vid.get(cv2.CAP_PROP_FRAME_WIDTH) - - # check height and width to scale - if width > height: - width = 512 - height = -1 - elif height > width: - height = 512 - width = -1 - elif width == height: - width = 512 - height = 512 - - converted_name = "kangsticker.webm" - - ( - ffmpeg.input(input) - .filter("fps", fps=30, round="up") - .filter("scale", width=width, height=height) - .trim(start="00:00:00", end="00:00:03", duration="3") - .output( - converted_name, - vcodec="libvpx-vp9", - **{ - #'vf': 'scale=512:-1', - "crf": "30" - }, - ) - .overwrite_output() - .run() - ) - - return converted_name - - -def mention_username(username: str, name: str) -> str: - """ - Args: - username (:obj:`str`): The username of chat which you want to mention. - name (:obj:`str`): The name the mention is showing. - - Returns: - :obj:`str`: The inline mention for the user as HTML. - """ - return f'<a href="t.me/{username}">{escape(name)}</a>' - - -# <================================================ END =======================================================> diff --git a/Mikobot/plugins/helper_funcs/msg_types.py b/Mikobot/plugins/helper_funcs/msg_types.py deleted file mode 100644 index 3f0b4cf485daaa8fe79075bb4bffdb49f046980a..0000000000000000000000000000000000000000 --- a/Mikobot/plugins/helper_funcs/msg_types.py +++ /dev/null @@ -1,251 +0,0 @@ -# <============================================== IMPORTS =========================================================> -from enum import IntEnum, unique - -from telegram import Message - -from Mikobot.plugins.helper_funcs.string_handling import button_markdown_parser - -# <=======================================================================================================> - - -# <================================================ CLASS =======================================================> -@unique -class Types(IntEnum): - TEXT = 0 - BUTTON_TEXT = 1 - STICKER = 2 - DOCUMENT = 3 - PHOTO = 4 - AUDIO = 5 - VOICE = 6 - VIDEO = 7 - - -# <================================================ FUNCTION =======================================================> -def get_note_type(msg: Message): - data_type = None - content = None - text = "" - raw_text = msg.text or msg.caption - args = raw_text.split(None, 2) # use python's maxsplit to separate cmd and args - note_name = args[1] - - buttons = [] - # determine what the contents of the filter are - text, image, sticker, etc - if len(args) >= 3: - offset = len(args[2]) - len( - raw_text, - ) # set correct offset relative to command + notename - text, buttons = button_markdown_parser( - args[2], - entities=msg.parse_entities() or msg.parse_caption_entities(), - offset=offset, - ) - if buttons: - data_type = Types.BUTTON_TEXT - else: - data_type = Types.TEXT - - elif msg.reply_to_message and not msg.reply_to_message.forum_topic_created: - entities = msg.reply_to_message.parse_entities() - msgtext = msg.reply_to_message.text or msg.reply_to_message.caption - if len(args) >= 2 and msg.reply_to_message.text: # not caption, text - text, buttons = button_markdown_parser(msgtext, entities=entities) - if buttons: - data_type = Types.BUTTON_TEXT - else: - data_type = Types.TEXT - - elif msg.reply_to_message.sticker: - content = msg.reply_to_message.sticker.file_id - data_type = Types.STICKER - - elif msg.reply_to_message.document: - content = msg.reply_to_message.document.file_id - text, buttons = button_markdown_parser(msgtext, entities=entities) - data_type = Types.DOCUMENT - - elif msg.reply_to_message.photo: - content = msg.reply_to_message.photo[-1].file_id # last elem = best quality - text, buttons = button_markdown_parser(msgtext, entities=entities) - data_type = Types.PHOTO - - elif msg.reply_to_message.audio: - content = msg.reply_to_message.audio.file_id - text, buttons = button_markdown_parser(msgtext, entities=entities) - data_type = Types.AUDIO - - elif msg.reply_to_message.voice: - content = msg.reply_to_message.voice.file_id - text, buttons = button_markdown_parser(msgtext, entities=entities) - data_type = Types.VOICE - - elif msg.reply_to_message.video: - content = msg.reply_to_message.video.file_id - text, buttons = button_markdown_parser(msgtext, entities=entities) - data_type = Types.VIDEO - - return note_name, text, data_type, content, buttons - - -# note: add own args? -def get_welcome_type(msg: Message): - data_type = None - content = None - text = "" - - try: - if msg.reply_to_message and not msg.reply_to_message.forum_topic_created: - if msg.reply_to_message.text: - args = msg.reply_to_message.text - else: - args = msg.reply_to_message.caption - else: - args = msg.text.split( - None, - 1, - ) # use python's maxsplit to separate cmd and args - except AttributeError: - args = False - - if msg.reply_to_message and not msg.reply_to_message.forum_topic_created: - if msg.reply_to_message.sticker: - content = msg.reply_to_message.sticker.file_id - text = None - data_type = Types.STICKER - - elif msg.reply_to_message.document: - content = msg.reply_to_message.document.file_id - text = msg.reply_to_message.caption - data_type = Types.DOCUMENT - - elif msg.reply_to_message.photo: - content = msg.reply_to_message.photo[-1].file_id # last elem = best quality - text = msg.reply_to_message.caption - data_type = Types.PHOTO - - elif msg.reply_to_message.audio: - content = msg.reply_to_message.audio.file_id - text = msg.reply_to_message.caption - data_type = Types.AUDIO - - elif msg.reply_to_message.voice: - content = msg.reply_to_message.voice.file_id - text = msg.reply_to_message.caption - data_type = Types.VOICE - - elif msg.reply_to_message.video: - content = msg.reply_to_message.video.file_id - text = msg.reply_to_message.caption - data_type = Types.VIDEO - - elif msg.reply_to_message and msg.reply_to_message.video_note: - content = msg.reply_to_message.video_note.file_id - text = None - data_type = Types.VIDEO_NOTE - - buttons = [] - # determine what the contents of the filter are - text, image, sticker, etc - if args: - if msg.reply_to_message and not msg.reply_to_message.forum_topic_created: - argumen = ( - msg.reply_to_message.caption if msg.reply_to_message.caption else "" - ) - offset = 0 # offset is no need since target was in reply - entities = msg.reply_to_message.parse_entities() - else: - argumen = args[1] - offset = len(argumen) - len( - msg.text, - ) # set correct offset relative to command + notename - entities = msg.parse_entities() - text, buttons = button_markdown_parser( - argumen, - entities=entities, - offset=offset, - ) - - if not data_type: - if text and buttons: - data_type = Types.BUTTON_TEXT - elif text: - data_type = Types.TEXT - - return text, data_type, content, buttons - - -def get_filter_type(msg: Message): - if not msg.reply_to_message and msg.text and len(msg.text.split()) >= 3: - content = None - text = msg.text.split(None, 2)[2] - data_type = Types.TEXT - media_spoiler = None - elif msg.reply_to_message and msg.reply_to_message.forum_topic_created: - content = None - text = msg.text.split(None, 2)[2] - data_type = Types.TEXT - media_spoiler = None - elif msg.reply_to_message and not msg.reply_to_message.forum_topic_created: - if msg.reply_to_message.text and len(msg.text.split()) >= 2: - content = None - text = msg.reply_to_message.text - data_type = Types.TEXT - media_spoiler = None - - elif msg.reply_to_message.sticker: - content = msg.reply_to_message.sticker.file_id - text = None - data_type = Types.STICKER - media_spoiler = None - - elif msg.reply_to_message.document: - content = msg.reply_to_message.document.file_id - text = msg.reply_to_message.caption - data_type = Types.DOCUMENT - media_spoiler = None - - elif msg.reply_to_message.photo: - content = msg.reply_to_message.photo[-1].file_id # last elem = best quality - text = msg.reply_to_message.caption - data_type = Types.PHOTO - media_spoiler = msg.reply_to_message.has_media_spoiler - - elif msg.reply_to_message.audio: - content = msg.reply_to_message.audio.file_id - text = msg.reply_to_message.caption - data_type = Types.AUDIO - media_spoiler = None - - elif msg.reply_to_message.voice: - content = msg.reply_to_message.voice.file_id - text = msg.reply_to_message.caption - data_type = Types.VOICE - media_spoiler = None - - elif msg.reply_to_message.video: - content = msg.reply_to_message.video.file_id - text = msg.reply_to_message.caption - data_type = Types.VIDEO - media_spoiler = msg.reply_to_message.has_media_spoiler - - elif msg.reply_to_message.video_note: - content = msg.reply_to_message.video_note.file_id - text = None - data_type = Types.VIDEO_NOTE - media_spoiler = None - - else: - text = None - data_type = None - content = None - media_spoiler = None - else: - text = None - data_type = None - content = None - media_spoiler = None - - return text, data_type, content, media_spoiler - - -# <================================================ END =======================================================> diff --git a/Mikobot/plugins/helper_funcs/string_handling.py b/Mikobot/plugins/helper_funcs/string_handling.py deleted file mode 100644 index 0a2e860b7200466c41aaca4b5d3a51d346ebdc81..0000000000000000000000000000000000000000 --- a/Mikobot/plugins/helper_funcs/string_handling.py +++ /dev/null @@ -1,311 +0,0 @@ -# <============================================== IMPORTS =========================================================> -import re -import time -from typing import Dict, List - -import bleach -import markdown2 -from emoji import unicode_codes -from telegram import MessageEntity -from telegram.helpers import escape_markdown - -# <=======================================================================================================> - -MATCH_MD = re.compile( - r"\*(.*?)\*|" - r"_(.*?)_|" - r"`(.*?)`|" - r"(?<!\\)(\[.*?\])(\(.*?\))|" - r"(?P<esc>[*_`\[])", -) - -LINK_REGEX = re.compile(r"(?<!\\)\[.+?\]\((.*?)\)") -BTN_URL_REGEX = re.compile(r"(\[([^\[]+?)\]\(buttonurl:(?:/{0,2})(.+?)(:same)?\))") -_EMOJI_REGEXP = None - - -# <================================================ FUNCTION =======================================================> -def get_emoji_regexp(): - global _EMOJI_REGEXP - if _EMOJI_REGEXP is None: - emojis = sorted(unicode_codes.EMOJI_DATA, key=len, reverse=True) - pattern = "(" + "|".join(re.escape(u) for u in emojis) + ")" - return re.compile(pattern) - - -def _selective_escape(to_parse: str) -> str: - """ - Escape all invalid markdown - - :param to_parse: text to escape - :return: valid markdown string - """ - offset = 0 # offset to be used as adding a \ character causes the string to shift - for match in MATCH_MD.finditer(to_parse): - if match.group("esc"): - ent_start = match.start() - to_parse = ( - to_parse[: ent_start + offset] + "\\" + to_parse[ent_start + offset :] - ) - offset += 1 - return to_parse - - -# This is a fun one. -def _calc_emoji_offset(to_calc) -> int: - # Get all emoji in text. - emoticons = get_emoji_regexp().finditer(to_calc) - # Check the utf16 length of the emoji to determine the offset it caused. - # Normal, 1 character emoji don't affect; hence sub 1. - # special, eg with two emoji characters (eg face, and skin col) will have length 2, so by subbing one we - # know we'll get one extra offset, - return sum(len(e.group(0).encode("utf-16-le")) // 2 - 1 for e in emoticons) - - -def markdown_parser( - txt: str, - entities: Dict[MessageEntity, str] = None, - offset: int = 0, -) -> str: - """ - Parse a string, escaping all invalid markdown entities. - - Escapes URL's so as to avoid URL mangling. - Re-adds any telegram code entities obtained from the entities object. - - :param txt: text to parse - :param entities: dict of message entities in text - :param offset: message offset - command and notename length - :return: valid markdown string - """ - if not entities: - entities = {} - if not txt: - return "" - - prev = 0 - res = "" - # Loop over all message entities, and: - # reinsert code - # escape free-standing urls - for ent, ent_text in entities.items(): - if ent.offset < -offset: - continue - - start = ent.offset + offset # start of entity - end = ent.offset + offset + ent.length - 1 # end of entity - - # we only care about code, url, text links - if ent.type in ("code", "url", "text_link", "spoiler"): - # count emoji to switch counter - count = _calc_emoji_offset(txt[:start]) - start -= count - end -= count - - # URL handling -> do not escape if in [](), escape otherwise. - if ent.type == "url": - if any( - match.start(1) <= start and end <= match.end(1) - for match in LINK_REGEX.finditer(txt) - ): - continue - # else, check the escapes between the prev and last and forcefully escape the url to avoid mangling - else: - # TODO: investigate possible offset bug when lots of emoji are present - res += _selective_escape(txt[prev:start] or "") + escape_markdown( - ent_text, 2 - ) - - # code handling - elif ent.type == "code": - res += _selective_escape(txt[prev:start]) + "`" + ent_text + "`" - - # handle markdown/html links - elif ent.type == "text_link": - res += _selective_escape(txt[prev:start]) + "[{}]({})".format( - ent_text, - ent.url, - ) - # handle spoiler - elif ent.type == "spoiler": - res += _selective_escape(txt[prev:start]) + "||" + ent_text + "||" - - end += 1 - - # anything else - else: - continue - - prev = end - - res += _selective_escape(txt[prev:]) # add the rest of the text - return res - - -def button_markdown_parser( - txt: str, - entities: Dict[MessageEntity, str] = None, - offset: int = 0, -) -> (str, List): - markdown_note = markdown_parser(txt, entities, offset) - prev = 0 - note_data = "" - buttons = [] - for match in BTN_URL_REGEX.finditer(markdown_note): - # Check if btnurl is escaped - n_escapes = 0 - to_check = match.start(1) - 1 - while to_check > 0 and markdown_note[to_check] == "\\": - n_escapes += 1 - to_check -= 1 - - # if even, not escaped -> create button - if n_escapes % 2 == 0: - # create a thruple with button label, url, and newline status - buttons.append((match.group(2), match.group(3), bool(match.group(4)))) - note_data += markdown_note[prev : match.start(1)] - prev = match.end(1) - # if odd, escaped -> move along - else: - note_data += markdown_note[prev:to_check] - prev = match.start(1) - 1 - else: - note_data += markdown_note[prev:] - - return note_data, buttons - - -def escape_invalid_curly_brackets(text: str, valids: List[str]) -> str: - new_text = "" - idx = 0 - while idx < len(text): - if text[idx] == "{": - if idx + 1 < len(text) and text[idx + 1] == "{": - idx += 2 - new_text += "{{{{" - continue - else: - success = False - for v in valids: - if text[idx:].startswith("{" + v + "}"): - success = True - break - if success: - new_text += text[idx : idx + len(v) + 2] - idx += len(v) + 2 - continue - else: - new_text += "{{" - - elif text[idx] == "}": - if idx + 1 < len(text) and text[idx + 1] == "}": - idx += 2 - new_text += "}}}}" - continue - else: - new_text += "}}" - - else: - new_text += text[idx] - idx += 1 - - return new_text - - -SMART_OPEN = "“" -SMART_CLOSE = "”" -START_CHAR = ("'", '"', SMART_OPEN) - - -def split_quotes(text: str) -> List: - if not any(text.startswith(char) for char in START_CHAR): - return text.split(None, 1) - counter = 1 # ignore first char -> is some kind of quote - while counter < len(text): - if text[counter] == "\\": - counter += 1 - elif text[counter] == text[0] or ( - text[0] == SMART_OPEN and text[counter] == SMART_CLOSE - ): - break - counter += 1 - else: - return text.split(None, 1) - - # 1 to avoid starting quote, and counter is exclusive so avoids ending - key = remove_escapes(text[1:counter].strip()) - # index will be in range, or `else` would have been executed and returned - rest = text[counter + 1 :].strip() - if not key: - key = text[0] + text[0] - return list(filter(None, [key, rest])) - - -def remove_escapes(text: str) -> str: - res = "" - is_escaped = False - for counter in range(len(text)): - if is_escaped: - res += text[counter] - is_escaped = False - elif text[counter] == "\\": - is_escaped = True - else: - res += text[counter] - return res - - -def escape_chars(text: str, to_escape: List[str]) -> str: - to_escape.append("\\") - new_text = "" - for x in text: - if x in to_escape: - new_text += "\\" - new_text += x - return new_text - - -async def extract_time(message, time_val): - if any(time_val.endswith(unit) for unit in ("m", "h", "d")): - unit = time_val[-1] - time_num = time_val[:-1] # type: str - if not time_num.isdigit(): - await message.reply_text("Invalid time amount specified.") - return "" - - if unit == "m": - bantime = int(time.time() + int(time_num) * 60) - elif unit == "h": - bantime = int(time.time() + int(time_num) * 60 * 60) - elif unit == "d": - bantime = int(time.time() + int(time_num) * 24 * 60 * 60) - else: - # how even...? - return "" - return bantime - else: - await message.reply_text( - "Invalid time type specified. Expected m,h, or d, got: {}".format( - time_val[-1], - ), - ) - return "" - - -def markdown_to_html(text: str): - text = text.replace("*", "**") - text = text.replace("`", "```") - text = text.replace("~", "~~") - - spoiler_pattern = re.compile(r"\|\|(?=\S)(.+?)(?<=\S)\|\|", re.S) - text = spoiler_pattern.sub(r"<tg-spoiler>\1</tg-spoiler>", text) - - _html = markdown2.markdown(text, extras=["strike", "underline"]) - return bleach.clean( - _html, - tags=["strong", "em", "a", "code", "pre", "strike", "u", "tg-spoiler"], - strip=True, - )[:-1] - - -# <================================================ END =======================================================> diff --git a/Mikobot/plugins/hyperlink.py b/Mikobot/plugins/hyperlink.py deleted file mode 100644 index 362b4412f8408d051ca47507d9663eace74fe3d5..0000000000000000000000000000000000000000 --- a/Mikobot/plugins/hyperlink.py +++ /dev/null @@ -1,63 +0,0 @@ -# SOURCE https://github.com/Team-ProjectCodeX -# CREATED BY https://t.me/O_okarma -# PROVIDED BY https://t.me/ProjectCodeX - -# <============================================== IMPORTS =========================================================> -import random -import re - -from telegram import Update -from telegram.constants import ParseMode -from telegram.ext import CommandHandler, ContextTypes - -from Mikobot import function - -# <=======================================================================================================> - - -# <================================================ FUNCTION =======================================================> -# Define the command handler for the "/pickwinner" command -async def pick_winner(update: Update, context: ContextTypes.DEFAULT_TYPE): - # Get the list of participants - participants = context.args - - if participants: - # Select a random winner - winner = random.choice(participants) - - # Send the winner as a reply - await update.message.reply_text(f"🎉 The winner is: {winner}") - else: - # If no participants are provided - await update.message.reply_text("Please provide a list of participants.") - - -# Define the command handler for the "/hyperlink" command -async def hyperlink_command(update: Update, context: ContextTypes.DEFAULT_TYPE): - args = context.args - if len(args) >= 2: - text = " ".join(args[:-1]) - link = args[-1] - hyperlink = f"[{text}]({link})" - await update.message.reply_text( - text=hyperlink, parse_mode="Markdown", disable_web_page_preview=True - ) - else: - match = re.search(r"/hyperlink ([^\s]+) (.+)", update.message.text) - if match: - text = match.group(1) - link = match.group(2) - hyperlink = f"[{text}]({link})" - await update.message.reply_text( - text=hyperlink, parse_mode=ParseMode.HTML, disable_web_page_preview=True - ) - else: - await update.message.reply_text( - "❌ Invalid format! Please use the format: /hyperlink <text> <link>." - ) - - -# <================================================ HANDLER =======================================================> -function(CommandHandler("pickwinner", pick_winner, block=False)) -function(CommandHandler("hyperlink", hyperlink_command, block=False)) -# <================================================ END =======================================================> diff --git a/Mikobot/plugins/imagegen.py b/Mikobot/plugins/imagegen.py deleted file mode 100644 index 454d40ee52bf6aafd9978da8f952f073b81ae072..0000000000000000000000000000000000000000 --- a/Mikobot/plugins/imagegen.py +++ /dev/null @@ -1,132 +0,0 @@ -# CREATED BY: https://t.me/O_oKarma -# API CREDITS: @Qewertyy -# PROVIDED BY: https://github.com/Team-ProjectCodeX - -# <============================================== IMPORTS =========================================================> -import asyncio - -import aiohttp -from telethon import events - -from Mikobot import tbot as client - -# <=======================================================================================================> - -BASE_URL = "https://lexica.qewertyy.me" -SESSION_HEADERS = {"Host": "lexica.qewertyy.me"} - - -# <=============================================== CLASS + FUNCTION ========================================================> -class AsyncClient: - def __init__(self): - self.url = BASE_URL - self.session = aiohttp.ClientSession() - - async def generate(self, model_id, prompt, negative_prompt): - data = { - "model_id": model_id, - "prompt": prompt, - "negative_prompt": negative_prompt if negative_prompt else "", - "num_images": 1, - } - try: - async with self.session.post( - f"{self.url}/models/inference", data=data, headers=SESSION_HEADERS - ) as resp: - return await resp.json() - except Exception as e: - print(f"Request failed: {str(e)}") - - async def get_images(self, task_id, request_id): - data = {"task_id": task_id, "request_id": request_id} - try: - async with self.session.post( - f"{self.url}/models/inference/task", data=data, headers=SESSION_HEADERS - ) as resp: - return await resp.json() - except Exception as e: - print(f"Request failed: {str(e)}") - - -async def generate_image_handler(event, model_id): - command_parts = event.text.split(" ", 1) - if len(command_parts) < 2: - await event.reply("Please provide a prompt.") - return - - prompt = command_parts[1] - negative_prompt = "" - - # Send the initial "Generating your image, wait sometime" message - reply_message = await event.reply("Generating your image, please wait...") - - client = AsyncClient() - response = await client.generate(model_id, prompt, negative_prompt) - task_id = response["task_id"] - request_id = response["request_id"] - - while True: - generated_images = await client.get_images(task_id, request_id) - - if "img_urls" in generated_images: - for img_url in generated_images["img_urls"]: - # Delete the initial reply message - await reply_message.delete() - - # Send the generated image - await event.reply(file=img_url) - break # Exit the loop when images are available - else: - # Wait for a few seconds before checking again - await asyncio.sleep(5) - - # Optionally, you can add a timeout to avoid an infinite loop - timeout_seconds = 600 # 10 minutes (adjust as needed) - if timeout_seconds <= 0: - await reply_message.edit("Image generation timed out.") - break - - timeout_seconds -= 5 # Decrement timeout by 5 seconds - - -@client.on(events.NewMessage(pattern=r"/meinamix")) -async def meinamix_handler(event): - await generate_image_handler(event, model_id=2) - - -@client.on(events.NewMessage(pattern=r"/sushi")) -async def darksushi_handler(event): - await generate_image_handler(event, model_id=7) - - -@client.on(events.NewMessage(pattern=r"/meinahentai")) -async def meinahentai_handler(event): - await generate_image_handler(event, model_id=8) - - -@client.on(events.NewMessage(pattern=r"/darksushimix")) -async def darksushimix_handler(event): - await generate_image_handler(event, model_id=9) - - -@client.on(events.NewMessage(pattern=r"/anylora")) -async def anylora_handler(event): - await generate_image_handler(event, model_id=3) - - -@client.on(events.NewMessage(pattern=r"/cetusmix")) -async def cetusmix_handler(event): - await generate_image_handler(event, model_id=10) - - -@client.on(events.NewMessage(pattern=r"/darkv2")) -async def darkv_handler(event): - await generate_image_handler(event, model_id=14) - - -@client.on(events.NewMessage(pattern=r"/creative")) -async def creative_handler(event): - await generate_image_handler(event, model_id=12) - - -# <================================================ END =======================================================> diff --git a/Mikobot/plugins/info.py b/Mikobot/plugins/info.py deleted file mode 100644 index 96b54be79a4c584d57818cfdf79978d2568da657..0000000000000000000000000000000000000000 --- a/Mikobot/plugins/info.py +++ /dev/null @@ -1,221 +0,0 @@ -# <============================================== IMPORTS =========================================================> -import os -import re -from html import escape -from random import choice - -from telegram import ChatMemberAdministrator, Update -from telegram.constants import ChatID, ChatType, ParseMode -from telegram.error import BadRequest -from telegram.ext import CommandHandler, ContextTypes -from telegram.helpers import mention_html - -from Database.sql.approve_sql import is_approved -from Infamous.karma import START_IMG -from Mikobot import DEV_USERS, DRAGONS, INFOPIC, OWNER_ID, function -from Mikobot.__main__ import STATS, USER_INFO -from Mikobot.plugins.helper_funcs.chat_status import support_plus, dev_plus -from Mikobot.plugins.users import get_user_id - -# <=======================================================================================================> - - -# <================================================ FUNCTION =======================================================> -async def info(update: Update, context: ContextTypes.DEFAULT_TYPE): - chat = update.effective_chat - message = update.effective_message - args = context.args - bot = context.bot - - def reply_with_text(text): - return message.reply_text(text, parse_mode=ParseMode.HTML) - - head = "" - premium = False - - reply = await reply_with_text("<code>Getting information...</code>") - - user_id = None - user_name = None - - if len(args) >= 1: - if args[0][0] == "@": - user_name = args[0] - user_id = await get_user_id(user_name) - - if not user_id: - try: - chat_obj = await bot.get_chat(user_name) - userid = chat_obj.id - except BadRequest: - await reply_with_text( - "I can't get information about this user/channel/group." - ) - return - else: - userid = user_id - elif len(args) >= 1 and args[0].lstrip("-").isdigit(): - userid = int(args[0]) - elif message.reply_to_message and not message.reply_to_message.forum_topic_created: - if message.reply_to_message.sender_chat: - userid = message.reply_to_message.sender_chat.id - elif message.reply_to_message.from_user: - if message.reply_to_message.from_user.id == ChatID.FAKE_CHANNEL: - userid = message.reply_to_message.chat.id - else: - userid = message.reply_to_message.from_user.id - premium = message.reply_to_message.from_user.is_premium - elif not message.reply_to_message and not args: - if message.from_user.id == ChatID.FAKE_CHANNEL: - userid = message.sender_chat.id - else: - userid = message.from_user.id - premium = message.from_user.is_premium - - try: - chat_obj = await bot.get_chat(userid) - except (BadRequest, UnboundLocalError): - await reply_with_text("I can't get information about this user/channel/group.") - return - - if chat_obj.type == ChatType.PRIVATE: - if chat_obj.username: - head = f"⇨【 <b>USER INFORMATION</b> 】⇦\n\n" - if chat_obj.username.endswith("bot"): - head = f"⇨【 <b>BOT INFORMATION</b> 】⇦\n\n" - - head += f"➲ <b>ID:</b> <code>{chat_obj.id}</code>" - head += f"\n➲ <b>First Name:</b> {chat_obj.first_name}" - if chat_obj.last_name: - head += f"\n➲ <b>Last Name:</b> {chat_obj.last_name}" - if chat_obj.username: - head += f"\n➲ <b>Username:</b> @{chat_obj.username}" - head += f"\n➲ <b>Permalink:</b> {mention_html(chat_obj.id, 'link')}" - - if chat_obj.username and not chat_obj.username.endswith("bot"): - head += f"\n\n💎 <b>Premium User:</b> {premium}" - - if chat_obj.bio: - head += f"\n\n<b>➲ Bio:</b> {chat_obj.bio}" - - chat_member = await chat.get_member(chat_obj.id) - if isinstance(chat_member, ChatMemberAdministrator): - head += f"\n➲ <b>Presence:</b> {chat_member.status}" - if chat_member.custom_title: - head += f"\n➲ <b>Admin Title:</b> {chat_member.custom_title}" - else: - head += f"\n➲ <b>Presence:</b> {chat_member.status}" - - if is_approved(chat.id, chat_obj.id): - head += f"\n➲ <b>Approved:</b> This user is approved in this chat." - - disaster_level_present = False - - if chat_obj.id == OWNER_ID: - head += "\n\n👑 <b>The disaster level of this person is My Owner.</b>" - disaster_level_present = True - elif chat_obj.id in DEV_USERS: - head += "\n\n🐉 <b>This user is a member of Infamous Hydra.</b>" - disaster_level_present = True - elif chat_obj.id in DRAGONS: - head += "\n\n🐲 <b>The disaster level of this person is Dragon.</b>" - disaster_level_present = True - if disaster_level_present: - head += " [?]" - - for mod in USER_INFO: - try: - mod_info = mod.__user_info__(chat_obj.id).strip() - except TypeError: - mod_info = mod.__user_info__(chat_obj.id, chat.id).strip() - - head += "\n\n" + mod_info if mod_info else "" - - if chat_obj.type == ChatType.SENDER: - head = f"📨 Sender Chat Information:\n" - await reply_with_text("Found sender chat, getting information...") - head += f"<b>ID:</b> <code>{chat_obj.id}</code>" - if chat_obj.title: - head += f"\n🏷️ <b>Title:</b> {chat_obj.title}" - if chat_obj.username: - head += f"\n📧 <b>Username:</b> @{chat_obj.username}" - head += f"\n🔗 Permalink: {mention_html(chat_obj.id, 'link')}" - if chat_obj.description: - head += f"\n📝 <b>Description:</b> {chat_obj.description}" - - elif chat_obj.type == ChatType.CHANNEL: - head = f"Channel Information:\n" - await reply_with_text("Found channel, getting information...") - head += f"<b>ID:</b> <code>{chat_obj.id}</code>" - if chat_obj.title: - head += f"\n<b>Title:</b> {chat_obj.title}" - if chat_obj.username: - head += f"\n<b>Username:</b> @{chat_obj.username}" - head += f"\nPermalink: {mention_html(chat_obj.id, 'link')}" - if chat_obj.description: - head += f"\n<b>Description:</b> {chat_obj.description}" - if chat_obj.linked_chat_id: - head += f"\n<b>Linked Chat ID:</b> <code>{chat_obj.linked_chat_id}</code>" - - elif chat_obj.type in [ChatType.GROUP, ChatType.SUPERGROUP]: - head = f"Group Information:\n" - await reply_with_text("Found group, getting information...") - head += f"<b>ID:</b> <code>{chat_obj.id}</code>" - if chat_obj.title: - head += f"\n<b>Title:</b> {chat_obj.title}" - if chat_obj.username: - head += f"\n<b>Username:</b> @{chat_obj.username}" - head += f"\nPermalink: {mention_html(chat_obj.id, 'link')}" - if chat_obj.description: - head += f"\n<b>Description:</b> {chat_obj.description}" - - if INFOPIC: - try: - if chat_obj.photo: - _file = await chat_obj.photo.get_big_file() - await _file.download_to_drive(f"{chat_obj.id}.png") - await message.reply_photo( - photo=open(f"{chat_obj.id}.png", "rb"), - caption=(head), - parse_mode=ParseMode.HTML, - ) - await reply.delete() - os.remove(f"{chat_obj.id}.png") - else: - await reply_with_text(escape(head)) - except: - await reply_with_text(escape(head)) - - -@dev_plus -async def stats(update: Update, context: ContextTypes.DEFAULT_TYPE): - stats = "📊 <b>Miko-Bot's Statistics:</b>\n\n" + "\n".join( - [mod.__stats__() for mod in STATS] - ) - result = re.sub(r"(\d+)", r"<code>\1</code>", stats) - - await update.effective_message.reply_photo( - photo=str(choice(START_IMG)), caption=result, parse_mode=ParseMode.HTML - ) - - -# <=================================================== HELP ====================================================> - - -__help__ = """ -*Overall information about user:* - -» /info : Fetch information. -""" - -# <================================================ HANDLER =======================================================> -STATS_HANDLER = CommandHandler(["stats", "gstats"], stats, block=False) -INFO_HANDLER = CommandHandler(("info", "book"), info, block=False) - -function(STATS_HANDLER) -function(INFO_HANDLER) - -__mod_name__ = "INFO" -__command_list__ = ["info"] -__handlers__ = [INFO_HANDLER, STATS_HANDLER] -# <================================================ END =======================================================> diff --git a/Mikobot/plugins/instadl.py b/Mikobot/plugins/instadl.py deleted file mode 100644 index 70bad4e3c9c51600529e49a51a0ff5af7e692631..0000000000000000000000000000000000000000 --- a/Mikobot/plugins/instadl.py +++ /dev/null @@ -1,66 +0,0 @@ -# SOURCE https://github.com/Team-ProjectCodeX -# CREATED BY https://t.me/O_okarma -# PROVIDED BY https://t.me/ProjectCodeX - -# <============================================== IMPORTS =========================================================> -from telegram import Update -from telegram.ext import CommandHandler, ContextTypes - -from Mikobot import function -from Mikobot.state import state - -# <=======================================================================================================> - -DOWNLOADING_STICKER_ID = ( - "CAACAgIAAxkBAAEDv_xlJWmh2-fKRwvLywJaFeGy9wmBKgACVQADr8ZRGmTn_PAl6RC_MAQ" -) -API_URL = "https://karma-api2.vercel.app/instadl" # Replace with your actual API URL - - -# <================================================ FUNCTION =======================================================> -async def instadl_command_handler(update: Update, context: ContextTypes.DEFAULT_TYPE): - if len(context.args) < 1: - await update.message.reply_text("Usage: /instadl [Instagram URL]") - return - - link = context.args[0] - try: - downloading_sticker = await update.message.reply_sticker(DOWNLOADING_STICKER_ID) - - # Make an asynchronous GET request to the API using httpx - response = await state.get(API_URL, params={"url": link}) - data = response.json() - - # Check if the API request was successful - if "content_url" in data: - content_url = data["content_url"] - - # Determine content type from the URL - content_type = "video" if "video" in content_url else "photo" - - # Reply with either photo or video - if content_type == "photo": - await update.message.reply_photo(content_url) - elif content_type == "video": - await update.message.reply_video(content_url) - else: - await update.message.reply_text("Unsupported content type.") - else: - await update.message.reply_text( - "Unable to fetch content. Please check the Instagram URL or try with another Instagram link." - ) - - except Exception as e: - print(e) - await update.message.reply_text( - "An error occurred while processing the request." - ) - - finally: - await downloading_sticker.delete() - - -function( - CommandHandler(["ig", "instagram", "insta", "instadl"], instadl_command_handler) -) -# <================================================ END =======================================================> diff --git a/Mikobot/plugins/karma.py b/Mikobot/plugins/karma.py deleted file mode 100644 index b02e345dc95e5cf04b9ff3dc1e6e0bb184ddda8d..0000000000000000000000000000000000000000 --- a/Mikobot/plugins/karma.py +++ /dev/null @@ -1,182 +0,0 @@ -# https://github.com/Team-ProjectCodeX -# UPDATED BY https://t.me/O_okarma -# https://t.me/ProjectCodeX - -# <============================================== IMPORTS =========================================================> -import asyncio - -from pyrogram import filters - -from Database.mongodb.karma_mongo import * -from Mikobot import OWNER_ID, app -from Mikobot.utils.can_restrict import can_restrict -from Mikobot.utils.errors import capture_err - -# <=======================================================================================================> - -karma_positive_group = 3 -karma_negative_group = 4 - - -# <================================================ FUNCTION =======================================================> -@app.on_message( - filters.text - & filters.group - & filters.incoming - & filters.reply - & filters.regex( - r"^(\+|\+\+|\+1|thx|tnx|ty|tq|thank you|thanx|thanks|pro|cool|good|agree|makasih|👍|\+\+ .+)$" - ) - & ~filters.via_bot - & ~filters.bot, - group=karma_positive_group, -) -@capture_err -async def upvote(_, message): - if not await is_karma_on(message.chat.id): - return - reply_user = message.reply_to_message.from_user - current_user = message.from_user - if not (reply_user and current_user): - return - if reply_user.id == OWNER_ID: - await message.reply_text("How so pro?") - return - if reply_user.id == current_user.id: - return - - chat_id = message.chat.id - user_id = reply_user.id - user_mention = reply_user.mention - current_karma = await get_karma(chat_id, await int_to_alpha(user_id)) - karma = current_karma["karma"] + 1 if current_karma else 1 - new_karma = {"karma": karma} - await update_karma(chat_id, await int_to_alpha(user_id), new_karma) - await message.reply_text( - f"𝗜𝗻𝗰𝗿𝗲𝗺𝗲𝗻𝘁𝗲𝗱 𝗸𝗮𝗿𝗺𝗮 𝗼𝗳 {user_mention} 𝗯𝘆 1.\n**⭐️ 𝗧𝗢𝗧𝗔𝗟 𝗣𝗢𝗜𝗡𝗧𝗦:** {karma}" - ) - - -@app.on_message( - filters.text - & filters.group - & filters.incoming - & filters.reply - & filters.regex(r"^(-|--|-1|not cool|disagree|worst|bad|👎|-- .+)$") - & ~filters.via_bot - & ~filters.bot, - group=karma_negative_group, -) -@capture_err -async def downvote(_, message): - if not await is_karma_on(message.chat.id): - return - reply_user = message.reply_to_message.from_user - current_user = message.from_user - if not (reply_user and current_user): - return - if reply_user.id == OWNER_ID: - await message.reply_text("I know him, so I'm not gonna do that, baby.") - return - if reply_user.id == current_user.id: - return - - user_id = reply_user.id - user_mention = reply_user.mention - current_karma = await get_karma(message.chat.id, await int_to_alpha(user_id)) - karma = current_karma["karma"] - 1 if current_karma else 0 - new_karma = {"karma": karma} - await update_karma(message.chat.id, await int_to_alpha(user_id), new_karma) - await message.reply_text( - f"𝗗𝗲𝗰𝗿𝗲𝗺𝗲𝗻𝘁𝗲𝗱 𝗸𝗮𝗿𝗺𝗮 𝗼𝗳 {user_mention} 𝗯𝘆 1.\n**⭐️ 𝗧𝗢𝗧𝗔𝗟 𝗣𝗢𝗜𝗡𝗧𝗦:** {karma}" - ) - - -@app.on_message(filters.command("karmastat") & filters.group) -@capture_err -async def karma(_, message): - if not message.reply_to_message: - m = await message.reply_text("Analyzing karma... This may take a while.") - karma = await get_karmas(message.chat.id) - if not karma: - await m.edit_text("No karma in the database for this chat.") - return - msg = f"**🎖 𝗞𝗔𝗥𝗠𝗔 𝗟𝗜𝗦𝗧 𝗢𝗙 {message.chat.title} :**\n" - limit = 0 - karma_dicc = {} - for i in karma: - user_id = await alpha_to_int(i) - user_karma = karma[i]["karma"] - karma_dicc[str(user_id)] = user_karma - karma_arranged = dict( - sorted(karma_dicc.items(), key=lambda item: item[1], reverse=True) - ) - if not karma_dicc: - await m.edit_text("No karma in the database for this chat.") - return - for user_idd, karma_count in karma_arranged.items(): - if limit > 9: - break - try: - user = await _.get_users(int(user_idd)) - await asyncio.sleep(0.8) - except Exception: - continue - first_name = user.first_name - if not first_name: - continue - msg += f"`{karma_count}` {(first_name[0:12] + '...') if len(first_name) > 12 else first_name}\n" - limit += 1 - await m.edit_text(msg) - else: - user_id = message.reply_to_message.from_user.id - karma = await get_karma(message.chat.id, await int_to_alpha(user_id)) - karma = karma["karma"] if karma else 0 - await message.reply_text(f"**⭐️ 𝗧𝗢𝗧𝗔𝗟 𝗣𝗢𝗜𝗡𝗧𝗦:** {karma}") - - -@app.on_message(filters.command("karma")) -@can_restrict -async def karma_toggle_xd(_, message): - usage = "**Usage:**\n/karma [ON|OFF]" - if len(message.command) != 2: - return await message.reply_text(usage) - chat_id = message.chat.id - state = message.text.split(None, 1)[1].strip().lower() - - if state == "on": - disabled = karmadb.find_one({"chat_id_toggle": chat_id}) - if disabled: - karmadb.delete_one({"chat_id_toggle": chat_id}) - await message.reply_text("Enabled the karma system.") - else: - await message.reply_text("Karma system is already enabled.") - elif state == "off": - disabled = karmadb.find_one({"chat_id_toggle": chat_id}) - if disabled: - await message.reply_text("Karma system is already disabled.") - else: - karmadb.insert_one({"chat_id_toggle": chat_id}) - await message.reply_text("Disabled the karma system.") - else: - await message.reply_text(usage) - - -# <=================================================== HELP ====================================================> - - -__mod_name__ = "KARMA" -__help__ = """ - -➠ *UPVOTE* - Use upvote keywords like "+", "+1", "thanks", etc. to upvote a message. -➠ *DOWNVOTE* - Use downvote keywords like "-", "-1", etc. to downvote a message. - -➠ *Commands* - -» /karmastat:- `Reply to a user to check that user's karma points` - -» /karmastat:- `Send without replying to any message to check karma point list of top 10` - -» /karma [OFF|ON] - `Enable or disable karma system in your chat.` -""" -# <================================================ END =======================================================> diff --git a/Mikobot/plugins/log_channel.py b/Mikobot/plugins/log_channel.py deleted file mode 100644 index 709c107705adcdce24f45faf734239c5e54c8bee..0000000000000000000000000000000000000000 --- a/Mikobot/plugins/log_channel.py +++ /dev/null @@ -1,257 +0,0 @@ -# <============================================== IMPORTS =========================================================> -from datetime import datetime -from functools import wraps - -from telegram.constants import ChatType -from telegram.ext import ContextTypes - -from Mikobot import function -from Mikobot.plugins.helper_funcs.misc import is_module_loaded - -FILENAME = __name__.rsplit(".", 1)[-1] - -if is_module_loaded(FILENAME): - from telegram import Update - from telegram.constants import ParseMode - from telegram.error import BadRequest, Forbidden - from telegram.ext import CommandHandler, JobQueue - from telegram.helpers import escape_markdown - - from Database.sql import log_channel_sql as sql - from Mikobot import EVENT_LOGS, LOGGER, dispatcher - from Mikobot.plugins.helper_funcs.chat_status import check_admin - - # <=======================================================================================================> - # <================================================ FUNCTION =======================================================> - def loggable(func): - @wraps(func) - async def log_action( - update: Update, - context: ContextTypes.DEFAULT_TYPE, - job_queue: JobQueue = None, - *args, - **kwargs, - ): - if not job_queue: - result = await func(update, context, *args, **kwargs) - else: - result = await func(update, context, job_queue, *args, **kwargs) - - chat = update.effective_chat - message = update.effective_message - - if result and isinstance(result, str): - datetime_fmt = "%H:%M - %d-%m-%Y" - result += f"\nEvent stamp: {datetime.utcnow().strftime(datetime_fmt)}" - - if chat.is_forum and chat.username: - result += f"\nLink: https://t.me/{chat.username}/{message.message_thread_id}/{message.message_id}" - - if message.chat.type == chat.SUPERGROUP and message.chat.username: - result += ( - f"\nLink: https://t.me/{chat.username}/{message.message_id}" - ) - log_chat = sql.get_chat_log_channel(chat.id) - if log_chat: - await send_log(context, log_chat, chat.id, result) - - return result - - return log_action - - def gloggable(func): - @wraps(func) - async def glog_action( - update: Update, context: ContextTypes.DEFAULT_TYPE, *args, **kwargs - ): - result = await func(update, context, *args, **kwargs) - chat = update.effective_chat - message = update.effective_message - - if result: - datetime_fmt = "%H:%M - %d-%m-%Y" - result += f"\nEvent stamp: {datetime.utcnow().strftime(datetime_fmt)}" - if chat.is_forum and chat.username: - result += f"\nLink: https://t.me/{chat.username}/{message.message_thread_id}/{message.message_id}" - elif message.chat.type == chat.SUPERGROUP and message.chat.username: - result += ( - f"\nLink: https://t.me/{chat.username}/{message.message_id}" - ) - log_chat = str(EVENT_LOGS) - if log_chat: - await send_log(context, log_chat, chat.id, result) - - return result - - return glog_action - - async def send_log( - context: ContextTypes.DEFAULT_TYPE, - log_chat_id: str, - orig_chat_id: str, - result: str, - ): - bot = context.bot - try: - await bot.send_message( - log_chat_id, - result, - parse_mode=ParseMode.HTML, - disable_web_page_preview=True, - ) - except BadRequest as excp: - if excp.message == "Chat not found": - try: - await bot.send_message( - orig_chat_id, - "This log channel has been deleted - unsetting.", - message_thread_id=1, - ) - except: - await bot.send_message( - orig_chat_id, - "This log channel has been deleted - unsetting.", - ) - sql.stop_chat_logging(orig_chat_id) - else: - LOGGER.warning(excp.message) - LOGGER.warning(result) - LOGGER.exception("Could not parse") - - await bot.send_message( - log_chat_id, - result - + "\n\nFormatting has been disabled due to an unexpected error.", - ) - - @check_admin(is_user=True) - async def logging(update: Update, context: ContextTypes.DEFAULT_TYPE): - bot = context.bot - message = update.effective_message - chat = update.effective_chat - - log_channel = sql.get_chat_log_channel(chat.id) - if log_channel: - log_channel_info = await bot.get_chat(log_channel) - await message.reply_text( - f"This group has all its logs sent to: {escape_markdown(log_channel_info.title)} (`{log_channel}`)", - parse_mode=ParseMode.MARKDOWN, - ) - - else: - await message.reply_text("No log channel has been set for this group!") - - @check_admin(is_user=True) - async def setlog(update: Update, context: ContextTypes.DEFAULT_TYPE): - bot = context.bot - message = update.effective_message - chat = update.effective_chat - if chat.type == ChatType.CHANNEL: - await bot.send_message( - chat.id, - "Now, forward the /setlog to the group you want to tie this channel to!", - ) - - elif message.forward_from_chat: - sql.set_chat_log_channel(chat.id, message.forward_from_chat.id) - - try: - await bot.send_message( - message.forward_from_chat.id, - f"This channel has been set as the log channel for {chat.title or chat.first_name}.", - ) - except Forbidden as excp: - if excp.message == "Forbidden: Bot is not a member of the channel chat": - if chat.is_forum: - await bot.send_message( - chat.id, - "Successfully set log channel!", - message_thread_id=message.message_thread_id, - ) - else: - await bot.send_message(chat.id, "Successfully set log channel!") - else: - LOGGER.exception("Error in setting the log channel.") - - if chat.is_forum: - await bot.send_message( - chat.id, - "Successfully set log channel!", - message_thread_id=message.message_thread_id, - ) - else: - await bot.send_message(chat.id, "Successfully set log channel!") - - else: - await message.reply_text( - "The steps to set a log channel are:\n" - " - Add bot to the desired channel (as an admin!)\n" - " - Send /setlog in the channel\n" - " - Forward the /setlog to the group\n", - ) - - @check_admin(is_user=True) - async def unsetlog(update: Update, context: ContextTypes.DEFAULT_TYPE): - bot = context.bot - message = update.effective_message - chat = update.effective_chat - - log_channel = sql.stop_chat_logging(chat.id) - if log_channel: - await bot.send_message( - log_channel, - f"Channel has been unlinked from {chat.title}", - ) - await message.reply_text("Log channel has been un-set.") - - else: - await message.reply_text("No log channel is set yet!") - - def __stats__(): - return f"• {sql.num_logchannels()} log channels set." - - def __migrate__(old_chat_id, new_chat_id): - sql.migrate_chat(old_chat_id, new_chat_id) - - async def __chat_settings__(chat_id, user_id): - log_channel = sql.get_chat_log_channel(chat_id) - if log_channel: - log_channel_info = await dispatcher.bot.get_chat(log_channel) - return f"This group has all its logs sent to: {escape_markdown(log_channel_info.title)} (`{log_channel}`)" - return "No log channel is set for this group!" - - # <=================================================== HELP ====================================================> - - __help__ = """ -➠ *Admins Only*: - -» /logchannel: Get log channel info. - -» /setlog: Set the log channel. - -» /unsetlog: Unset the log channel. - -➠ *Setting the log channel is done by:* -➠ *Adding the bot to the desired channel (as an admin!)* - -» Sending /setlog in the channel -» Forwarding the /setlog to the group -""" - - __mod_name__ = "LOG-SET" - - # <================================================ HANDLER =======================================================> - function(CommandHandler("logchannel", logging, block=False)) - function(CommandHandler("setlog", setlog, block=False)) - function(CommandHandler("unsetlog", unsetlog, block=False)) - -else: - # run anyway if module not loaded - def loggable(func): - return func - - def gloggable(func): - return func - - -# <================================================ END =======================================================> diff --git a/Mikobot/plugins/mute.py b/Mikobot/plugins/mute.py deleted file mode 100644 index 1166aef79cd22b4d1e4a32cd71c5ed928e08c0cc..0000000000000000000000000000000000000000 --- a/Mikobot/plugins/mute.py +++ /dev/null @@ -1,261 +0,0 @@ -# <============================================== IMPORTS =========================================================> -import html -from typing import Union - -from telegram import Bot, Chat, ChatMember, ChatPermissions, Update -from telegram.constants import ParseMode -from telegram.error import BadRequest -from telegram.ext import CommandHandler, ContextTypes -from telegram.helpers import mention_html - -from Mikobot import LOGGER, function -from Mikobot.plugins.helper_funcs.chat_status import ( - check_admin, - connection_status, - is_user_admin, -) -from Mikobot.plugins.helper_funcs.extraction import extract_user, extract_user_and_text -from Mikobot.plugins.helper_funcs.string_handling import extract_time -from Mikobot.plugins.log_channel import loggable - -# <=======================================================================================================> - - -# <================================================ FUNCTION =======================================================> -async def check_user(user_id: int, bot: Bot, chat: Chat) -> Union[str, None]: - if not user_id: - reply = "You don't seem to be referring to a user or the ID specified is incorrect.." - return reply - - try: - member = await chat.get_member(user_id) - except BadRequest as excp: - if excp.message == "User not found": - reply = "I can't seem to find this user" - return reply - else: - raise - - if user_id == bot.id: - reply = "I'm not gonna MUTE myself, How high are you?" - return reply - - if await is_user_admin(chat, user_id, member): - reply = "Sorry can't do that, this user is admin here." - return reply - - return None - - -@connection_status -@loggable -@check_admin(permission="can_restrict_members", is_both=True) -async def mute(update: Update, context: ContextTypes.DEFAULT_TYPE) -> str: - bot = context.bot - args = context.args - - chat = update.effective_chat - user = update.effective_user - message = update.effective_message - - user_id, reason = await extract_user_and_text(message, context, args) - reply = await check_user(user_id, bot, chat) - - if reply: - await message.reply_text(reply) - return "" - - member = await chat.get_member(user_id) - - log = ( - f"<b>{html.escape(chat.title)}:</b>\n" - f"#MUTE\n" - f"<b>Admin:</b> {mention_html(user.id, user.first_name)}\n" - f"<b>User:</b> {mention_html(member.user.id, member.user.first_name)}" - ) - - if reason: - log += f"\n<b>Reason:</b> {reason}" - - if member.status in [ChatMember.RESTRICTED, ChatMember.MEMBER]: - chat_permissions = ChatPermissions(can_send_messages=False) - await bot.restrict_chat_member(chat.id, user_id, chat_permissions) - await bot.sendMessage( - chat.id, - f"Muted <b>{html.escape(member.user.first_name)}</b> with no expiration date!", - parse_mode=ParseMode.HTML, - message_thread_id=message.message_thread_id if chat.is_forum else None, - ) - return log - - else: - await message.reply_text("This user is already muted!") - - return "" - - -@connection_status -@loggable -@check_admin(permission="can_restrict_members", is_both=True) -async def unmute(update: Update, context: ContextTypes.DEFAULT_TYPE) -> str: - bot, args = context.bot, context.args - chat = update.effective_chat - user = update.effective_user - message = update.effective_message - - user_id = await extract_user(message, context, args) - if not user_id: - await message.reply_text( - "You'll need to either give me a username to unmute, or reply to someone to be unmuted.", - ) - return "" - - member = await chat.get_member(int(user_id)) - - if member.status not in [ChatMember.LEFT, ChatMember.BANNED]: - if member.status != ChatMember.RESTRICTED: - await message.reply_text("This user already has the right to speak.") - else: - chat_permissions = ChatPermissions( - can_send_messages=True, - can_invite_users=True, - can_pin_messages=True, - can_send_polls=True, - can_change_info=True, - can_send_media_messages=True, - can_send_other_messages=True, - can_add_web_page_previews=True, - ) - try: - await bot.restrict_chat_member(chat.id, int(user_id), chat_permissions) - except BadRequest: - pass - await bot.sendMessage( - chat.id, - f"I shall allow <b>{html.escape(member.user.first_name)}</b> to text!", - parse_mode=ParseMode.HTML, - message_thread_id=message.message_thread_id if chat.is_forum else None, - ) - return ( - f"<b>{html.escape(chat.title)}:</b>\n" - f"#UNMUTE\n" - f"<b>Admin:</b> {mention_html(user.id, user.first_name)}\n" - f"<b>User:</b> {mention_html(member.user.id, member.user.first_name)}" - ) - else: - await message.reply_text( - "This user isn't even in the chat, unmuting them won't make them talk more than they " - "already do!", - ) - - return "" - - -@connection_status -@loggable -@check_admin(permission="can_restrict_members", is_both=True) -async def temp_mute(update: Update, context: ContextTypes.DEFAULT_TYPE) -> str: - bot, args = context.bot, context.args - chat = update.effective_chat - user = update.effective_user - message = update.effective_message - - user_id, reason = await extract_user_and_text(message, context, args) - reply = await check_user(user_id, bot, chat) - - if reply: - await message.reply_text(reply) - return "" - - member = await chat.get_member(user_id) - - if not reason: - await message.reply_text("You haven't specified a time to mute this user for!") - return "" - - split_reason = reason.split(None, 1) - - time_val = split_reason[0].lower() - if len(split_reason) > 1: - reason = split_reason[1] - else: - reason = "" - - mutetime = await extract_time(message, time_val) - - if not mutetime: - return "" - - log = ( - f"<b>{html.escape(chat.title)}:</b>\n" - f"#TEMP MUTED\n" - f"<b>Admin:</b> {mention_html(user.id, user.first_name)}\n" - f"<b>User:</b> {mention_html(member.user.id, member.user.first_name)}\n" - f"<b>Time:</b> {time_val}" - ) - if reason: - log += f"\n<b>Reason:</b> {reason}" - - try: - if member.status in [ChatMember.RESTRICTED, ChatMember.MEMBER]: - chat_permissions = ChatPermissions(can_send_messages=False) - await bot.restrict_chat_member( - chat.id, - user_id, - chat_permissions, - until_date=mutetime, - ) - await bot.sendMessage( - chat.id, - f"Muted <b>{html.escape(member.user.first_name)}</b> for {time_val}!", - parse_mode=ParseMode.HTML, - message_thread_id=message.message_thread_id if chat.is_forum else None, - ) - return log - else: - await message.reply_text("This user is already muted.") - - except BadRequest as excp: - if excp.message == "Reply message not found": - # Do not reply - await message.reply_text(f"Muted for {time_val}!", quote=False) - return log - else: - LOGGER.warning(update) - LOGGER.exception( - "ERROR muting user %s in chat %s (%s) due to %s", - user_id, - chat.title, - chat.id, - excp.message, - ) - await message.reply_text("Well damn, I can't mute that user.") - - return "" - - -# <=================================================== HELP ====================================================> - - -__help__ = """ -➠ *Admins only:* - -» /mute <userhandle>: silences a user. Can also be used as a reply, muting the replied to user. - -» /tmute <userhandle> x(m/h/d): mutes a user for x time. (via handle, or reply). `m` = `minutes`, `h` = `hours`, `d` = `days`. - -» /unmute <userhandle>: unmutes a user. Can also be used as a reply, muting the replied to user. -""" - -# <================================================ HANDLER =======================================================> -MUTE_HANDLER = CommandHandler("mute", mute, block=False) -UNMUTE_HANDLER = CommandHandler("unmute", unmute, block=False) -TEMPMUTE_HANDLER = CommandHandler(["tmute", "tempmute"], temp_mute, block=False) - -function(MUTE_HANDLER) -function(UNMUTE_HANDLER) -function(TEMPMUTE_HANDLER) - -__mod_name__ = "MUTE" -__handlers__ = [MUTE_HANDLER, UNMUTE_HANDLER, TEMPMUTE_HANDLER] -# <================================================ END =======================================================> diff --git a/Mikobot/plugins/nekomode.py b/Mikobot/plugins/nekomode.py deleted file mode 100644 index 1372cae31b7cf0facfcf9f29dd7c4e2feb22b722..0000000000000000000000000000000000000000 --- a/Mikobot/plugins/nekomode.py +++ /dev/null @@ -1,131 +0,0 @@ -# SOURCE https://github.com/Team-ProjectCodeX -# CREATED BY https://t.me/O_okarma -# PROVIDED BY https://t.me/ProjectCodeX -# NEKOS - -# <============================================== IMPORTS =========================================================> -import nekos -from telethon import events - -from Database.mongodb.toggle_mongo import is_nekomode_on, nekomode_off, nekomode_on -from Mikobot import tbot -from Mikobot.state import state # Import the state function - -# <=======================================================================================================> - -url_sfw = "https://api.waifu.pics/sfw/" - -allowed_commands = [ - "waifu", - "neko", - "shinobu", - "megumin", - "bully", - "cuddle", - "cry", - "hug", - "awoo", - "kiss", - "lick", - "pat", - "smug", - "bonk", - "yeet", - "blush", - "smile", - "spank", - "wave", - "highfive", - "handhold", - "nom", - "bite", - "glomp", - "slap", - "hTojiy", - "wink", - "poke", - "dance", - "cringe", - "tickle", -] - - -# <================================================ FUNCTION =======================================================> -@tbot.on(events.NewMessage(pattern="/wallpaper")) -async def wallpaper(event): - chat_id = event.chat_id - nekomode_status = await is_nekomode_on(chat_id) - if nekomode_status: - target = "wallpaper" - img_url = nekos.img( - target - ) # Replace nekos.img(target) with the correct function call - await event.reply(file=img_url) - - -@tbot.on(events.NewMessage(pattern="/nekomode on")) -async def enable_nekomode(event): - chat_id = event.chat_id - await nekomode_on(chat_id) - await event.reply("Nekomode has been enabled.") - - -@tbot.on(events.NewMessage(pattern="/nekomode off")) -async def disable_nekomode(event): - chat_id = event.chat_id - await nekomode_off(chat_id) - await event.reply("Nekomode has been disabled.") - - -@tbot.on(events.NewMessage(pattern=r"/(?:{})".format("|".join(allowed_commands)))) -async def nekomode_commands(event): - chat_id = event.chat_id - nekomode_status = await is_nekomode_on(chat_id) - if nekomode_status: - target = event.raw_text[1:].lower() # Remove the slash before the command - if target in allowed_commands: - url = f"{url_sfw}{target}" - - response = await state.get(url) - result = response.json() - animation_url = result["url"] - - # Send animation - await event.respond(file=animation_url) - - -__help__ = """ -*✨ Sends fun Gifs/Images* - -➥ /nekomode on : Enables fun neko mode. -➥ /nekomode off : Disables fun neko mode - -» /bully: sends random bully gifs. -» /neko: sends random neko gifs. -» /wallpaper: sends random wallpapers. -» /highfive: sends random highfive gifs. -» /tickle: sends random tickle GIFs. -» /wave: sends random wave GIFs. -» /smile: sends random smile GIFs. -» /feed: sends random feeding GIFs. -» /blush: sends random blush GIFs. -» /avatar: sends random avatar stickers. -» /waifu: sends random waifu stickers. -» /kiss: sends random kissing GIFs. -» /cuddle: sends random cuddle GIFs. -» /cry: sends random cry GIFs. -» /bonk: sends random cuddle GIFs. -» /smug: sends random smug GIFs. -» /slap: sends random slap GIFs. -» /hug: get hugged or hug a user. -» /pat: pats a user or get patted. -» /spank: sends a random spank gif. -» /dance: sends a random dance gif. -» /poke: sends a random poke gif. -» /wink: sends a random wink gif. -» /bite: sends random bite GIFs. -» /handhold: sends random handhold GIFs. -""" - -__mod_name__ = "NEKO" -# <================================================ END =======================================================> diff --git a/Mikobot/plugins/notes.py b/Mikobot/plugins/notes.py deleted file mode 100644 index af0cb31bd66590c6b02b3d8ac9259b801e280ebc..0000000000000000000000000000000000000000 --- a/Mikobot/plugins/notes.py +++ /dev/null @@ -1,629 +0,0 @@ -# <============================================== IMPORTS =========================================================> -import ast -import random -import re -from io import BytesIO - -from telegram import InlineKeyboardButton, InlineKeyboardMarkup, Message, Update -from telegram.constants import MessageLimit, ParseMode -from telegram.error import BadRequest -from telegram.ext import ( - CallbackQueryHandler, - CommandHandler, - ContextTypes, - MessageHandler, - filters, -) -from telegram.helpers import escape_markdown, mention_markdown - -import Database.sql.notes_sql as sql -from Mikobot import DRAGONS, LOGGER, MESSAGE_DUMP, SUPPORT_CHAT, dispatcher, function -from Mikobot.plugins.disable import DisableAbleCommandHandler -from Mikobot.plugins.helper_funcs.chat_status import check_admin, connection_status -from Mikobot.plugins.helper_funcs.misc import build_keyboard, revert_buttons -from Mikobot.plugins.helper_funcs.msg_types import get_note_type -from Mikobot.plugins.helper_funcs.string_handling import ( - escape_invalid_curly_brackets, - markdown_to_html, -) - -from .cust_filters import MessageHandlerChecker - -# <=======================================================================================================> - -FILE_MATCHER = re.compile(r"^###file_id(!photo)?###:(.*?)(?:\s|$)") -STICKER_MATCHER = re.compile(r"^###sticker(!photo)?###:") -BUTTON_MATCHER = re.compile(r"^###button(!photo)?###:(.*?)(?:\s|$)") -MYFILE_MATCHER = re.compile(r"^###file(!photo)?###:") -MYPHOTO_MATCHER = re.compile(r"^###photo(!photo)?###:") -MYAUDIO_MATCHER = re.compile(r"^###audio(!photo)?###:") -MYVOICE_MATCHER = re.compile(r"^###voice(!photo)?###:") -MYVIDEO_MATCHER = re.compile(r"^###video(!photo)?###:") -MYVIDEONOTE_MATCHER = re.compile(r"^###video_note(!photo)?###:") - -ENUM_FUNC_MAP = { - sql.Types.TEXT.value: dispatcher.bot.send_message, - sql.Types.BUTTON_TEXT.value: dispatcher.bot.send_message, - sql.Types.STICKER.value: dispatcher.bot.send_sticker, - sql.Types.DOCUMENT.value: dispatcher.bot.send_document, - sql.Types.PHOTO.value: dispatcher.bot.send_photo, - sql.Types.AUDIO.value: dispatcher.bot.send_audio, - sql.Types.VOICE.value: dispatcher.bot.send_voice, - sql.Types.VIDEO.value: dispatcher.bot.send_video, -} - - -# <================================================ FUNCTION =======================================================> -async def get( - update: Update, - context: ContextTypes.DEFAULT_TYPE, - notename, - show_none=True, - no_format=False, -): - bot = context.bot - chat_id = update.effective_message.chat.id - chat = update.effective_chat - note_chat_id = update.effective_chat.id - note = sql.get_note(note_chat_id, notename) - message = update.effective_message # type: Optional[Message] - - if note: - if MessageHandlerChecker.check_user(update.effective_user.id): - return - # If we're replying to a message, reply to that message (unless it's an error) - if ( - message.reply_to_message - and not message.reply_to_message.forum_topic_created - ): - reply_id = message.reply_to_message.message_id - else: - reply_id = message.message_id - if note.is_reply: - if MESSAGE_DUMP: - try: - await bot.forward_message( - chat_id=chat_id, - from_chat_id=MESSAGE_DUMP, - message_id=note.value, - ) - except BadRequest as excp: - if excp.message == "Message to forward not found": - await message.reply_text( - "This message seems to have been lost - I'll remove it " - "from your notes list.", - ) - sql.rm_note(note_chat_id, notename) - else: - raise - else: - try: - await bot.forward_message( - chat_id=chat_id, - from_chat_id=chat_id, - message_id=markdown_to_html(note.value), - ) - except BadRequest as excp: - if excp.message == "Message to forward not found": - await message.reply_text( - "Looks like the original sender of this note has deleted " - "their message - sorry! Get your bot admin to start using a " - "message dump to avoid this. I'll remove this note from " - "your saved notes.", - ) - sql.rm_note(note_chat_id, notename) - else: - raise - else: - VALID_NOTE_FORMATTERS = [ - "first", - "last", - "fullname", - "username", - "id", - "chatname", - "mention", - ] - valid_format = escape_invalid_curly_brackets( - note.value, - VALID_NOTE_FORMATTERS, - ) - if valid_format: - if not no_format: - if "%%%" in valid_format: - split = valid_format.split("%%%") - if all(split): - text = random.choice(split) - else: - text = valid_format - else: - text = valid_format - else: - text = valid_format - text = text.format( - first=escape_markdown(message.from_user.first_name), - last=escape_markdown( - message.from_user.last_name or message.from_user.first_name, - ), - fullname=escape_markdown( - " ".join( - [message.from_user.first_name, message.from_user.last_name] - if message.from_user.last_name - else [message.from_user.first_name], - ), - ), - username="@" + message.from_user.username - if message.from_user.username - else mention_markdown( - message.from_user.id, - message.from_user.first_name, - ), - mention=mention_markdown( - message.from_user.id, - message.from_user.first_name, - ), - chatname=escape_markdown( - message.chat.title - if message.chat.type != "private" - else message.from_user.first_name, - ), - id=message.from_user.id, - ) - else: - text = "" - - keyb = [] - parseMode = ParseMode.HTML - buttons = sql.get_buttons(note_chat_id, notename) - if no_format: - parseMode = None - text += revert_buttons(buttons) - else: - keyb = build_keyboard(buttons) - - keyboard = InlineKeyboardMarkup(keyb) - - try: - if note.msgtype in (sql.Types.BUTTON_TEXT, sql.Types.TEXT): - await bot.send_message( - chat_id, - markdown_to_html(text), - reply_to_message_id=reply_id, - parse_mode=parseMode, - disable_web_page_preview=True, - reply_markup=keyboard, - message_thread_id=message.message_thread_id - if chat.is_forum - else None, - ) - else: - await ENUM_FUNC_MAP[note.msgtype]( - chat_id, - note.file, - caption=markdown_to_html(text), - reply_to_message_id=reply_id, - parse_mode=parseMode, - disable_web_page_preview=True, - reply_markup=keyboard, - message_thread_id=message.message_thread_id - if chat.is_forum - else None, - ) - - except BadRequest as excp: - if excp.message == "Entity_mention_user_invalid": - await message.reply_text( - "Looks like you tried to mention someone I've never seen before. If you really " - "want to mention them, forward one of their messages to me, and I'll be able " - "to tag them!", - ) - elif FILE_MATCHER.match(note.value): - await message.reply_text( - "This note was an incorrectly imported file from another bot - I can't use " - "it. If you really need it, you'll have to save it again. In " - "the meantime, I'll remove it from your notes list.", - ) - sql.rm_note(note_chat_id, notename) - else: - await message.reply_text( - "This note could not be sent, as it is incorrectly formatted. Ask in " - f"@{SUPPORT_CHAT} if you can't figure out why!", - ) - LOGGER.exception( - "Could not parse message #%s in chat %s", - notename, - str(note_chat_id), - ) - LOGGER.warning("Message was: %s", str(note.value)) - return - elif show_none: - await message.reply_text("This note doesn't exist") - - -@connection_status -async def cmd_get(update: Update, context: ContextTypes.DEFAULT_TYPE): - bot, args = context.bot, context.args - if len(args) >= 2 and args[1].lower() == "noformat": - await get(update, context, args[0].lower(), show_none=True, no_format=True) - elif len(args) >= 1: - await get(update, context, args[0].lower(), show_none=True) - else: - await update.effective_message.reply_text("Get rekt") - - -@connection_status -async def hash_get(update: Update, context: ContextTypes.DEFAULT_TYPE): - message = update.effective_message.text - fst_word = message.split()[0] - no_hash = fst_word[1:].lower() - await get(update, context, no_hash, show_none=False) - - -@connection_status -async def slash_get(update: Update, context: ContextTypes.DEFAULT_TYPE): - message, chat_id = update.effective_message.text, update.effective_chat.id - no_slash = message[1:] - note_list = sql.get_all_chat_notes(chat_id) - - try: - noteid = note_list[int(no_slash) - 1] - note_name = str(noteid).strip(">").split()[1] - await get(update, context, note_name, show_none=False) - except IndexError: - await update.effective_message.reply_text("Wrong Note ID 😾") - - -@connection_status -@check_admin(is_user=True) -async def save(update: Update, context: ContextTypes.DEFAULT_TYPE): - chat_id = update.effective_chat.id - msg = update.effective_message # type: Optional[Message] - if len(context.args) < 1: - await msg.reply_text("You should give the note a name.") - return - - note_name, text, data_type, content, buttons = get_note_type(msg) - note_name = note_name.lower() - if data_type is None: - await msg.reply_text("Dude, there's no note content") - return - - sql.add_note_to_db( - chat_id, - note_name, - text, - data_type, - buttons=buttons, - file=content, - ) - - await msg.reply_text( - f"Yas! Added `{note_name}`.\nGet it with /get `{note_name}`, or `#{note_name}`", - parse_mode=ParseMode.MARKDOWN, - ) - - if ( - msg.reply_to_message - and msg.reply_to_message.from_user.is_bot - and not msg.reply_to_message.forum_topic_created - ): - if text: - await msg.reply_text( - "Seems like you're trying to save a message from a bot. Unfortunately, " - "bots can't forward bot messages, so I can't save the exact message. " - "\nI'll save all the text I can, but if you want more, you'll have to " - "forward the message yourself, and then save it.", - ) - else: - await msg.reply_text( - "Bots are kinda handicapped by telegram, making it hard for bots to " - "interact with other bots, so I can't save this message " - "like I usually would - do you mind forwarding it and " - "then saving that new message? Thanks!", - ) - return - - -@connection_status -@check_admin(is_user=True) -async def clear(update: Update, context: ContextTypes.DEFAULT_TYPE): - args = context.args - chat_id = update.effective_chat.id - if len(args) >= 1: - notename = args[0].lower() - - if sql.rm_note(chat_id, notename): - await update.effective_message.reply_text("Successfully removed note.") - else: - await update.effective_message.reply_text( - "That's not a note in my database!" - ) - - -async def clearall(update: Update, context: ContextTypes.DEFAULT_TYPE): - chat = update.effective_chat - user = update.effective_user - member = await chat.get_member(user.id) - if member.status != "creator" and user.id not in DRAGONS: - await update.effective_message.reply_text( - "Only the chat owner can clear all notes at once.", - ) - else: - buttons = InlineKeyboardMarkup( - [ - [ - InlineKeyboardButton( - text="Delete all notes", - callback_data="notes_rmall", - ), - ], - [InlineKeyboardButton(text="Cancel", callback_data="notes_cancel")], - ], - ) - await update.effective_message.reply_text( - f"Are you sure you would like to clear ALL notes in {chat.title}? This action cannot be undone.", - reply_markup=buttons, - parse_mode=ParseMode.MARKDOWN, - ) - - -async def clearall_btn(update: Update, context: ContextTypes.DEFAULT_TYPE): - query = update.callback_query - chat = update.effective_chat - message = update.effective_message - member = await chat.get_member(query.from_user.id) - if query.data == "notes_rmall": - if member.status == "creator" or query.from_user.id in DRAGONS: - note_list = sql.get_all_chat_notes(chat.id) - try: - for notename in note_list: - note = notename.name.lower() - sql.rm_note(chat.id, note) - await message.edit_text("Deleted all notes.") - except BadRequest: - return - - if member.status == "administrator": - await query.answer("Only owner of the chat can do this.") - - if member.status == "member": - await query.answer("You need to be admin to do this.") - elif query.data == "notes_cancel": - if member.status == "creator" or query.from_user.id in DRAGONS: - await message.edit_text("Clearing of all notes has been cancelled.") - return - if member.status == "administrator": - await query.answer("Only owner of the chat can do this.") - if member.status == "member": - await query.answer("You need to be admin to do this.") - - -@connection_status -async def list_notes(update: Update, context: ContextTypes.DEFAULT_TYPE): - chat_id = update.effective_chat.id - note_list = sql.get_all_chat_notes(chat_id) - notes = len(note_list) + 1 - msg = "Get note by `/notenumber` or `#notename` \n\n *ID* *Note* \n" - for note_id, note in zip(range(1, notes), note_list): - if note_id < 10: - note_name = f"`{note_id:2}.` `#{(note.name.lower())}`\n" - else: - note_name = f"`{note_id}.` `#{(note.name.lower())}`\n" - if len(msg) + len(note_name) > MessageLimit.MAX_TEXT_LENGTH: - await update.effective_message.reply_text( - msg, parse_mode=ParseMode.MARKDOWN - ) - msg = "" - msg += note_name - - if not note_list: - try: - await update.effective_message.reply_text("No notes in this chat!") - except BadRequest: - await update.effective_message.reply_text( - "No notes in this chat!", quote=False - ) - - elif len(msg) != 0: - await update.effective_message.reply_text(msg, parse_mode=ParseMode.MARKDOWN) - - -async def __import_data__(chat_id, data, message: Message): - failures = [] - for notename, notedata in data.get("extra", {}).items(): - match = FILE_MATCHER.match(notedata) - matchsticker = STICKER_MATCHER.match(notedata) - matchbtn = BUTTON_MATCHER.match(notedata) - matchfile = MYFILE_MATCHER.match(notedata) - matchphoto = MYPHOTO_MATCHER.match(notedata) - matchaudio = MYAUDIO_MATCHER.match(notedata) - matchvoice = MYVOICE_MATCHER.match(notedata) - matchvideo = MYVIDEO_MATCHER.match(notedata) - matchvn = MYVIDEONOTE_MATCHER.match(notedata) - - if match: - failures.append(notename) - notedata = notedata[match.end() :].strip() - if notedata: - sql.add_note_to_db(chat_id, notename[1:], notedata, sql.Types.TEXT) - elif matchsticker: - content = notedata[matchsticker.end() :].strip() - if content: - sql.add_note_to_db( - chat_id, - notename[1:], - notedata, - sql.Types.STICKER, - file=content, - ) - elif matchbtn: - parse = notedata[matchbtn.end() :].strip() - notedata = parse.split("<###button###>")[0] - buttons = parse.split("<###button###>")[1] - buttons = ast.literal_eval(buttons) - if buttons: - sql.add_note_to_db( - chat_id, - notename[1:], - notedata, - sql.Types.BUTTON_TEXT, - buttons=buttons, - ) - elif matchfile: - file = notedata[matchfile.end() :].strip() - file = file.split("<###TYPESPLIT###>") - notedata = file[1] - content = file[0] - if content: - sql.add_note_to_db( - chat_id, - notename[1:], - notedata, - sql.Types.DOCUMENT, - file=content, - ) - elif matchphoto: - photo = notedata[matchphoto.end() :].strip() - photo = photo.split("<###TYPESPLIT###>") - notedata = photo[1] - content = photo[0] - if content: - sql.add_note_to_db( - chat_id, - notename[1:], - notedata, - sql.Types.PHOTO, - file=content, - ) - elif matchaudio: - audio = notedata[matchaudio.end() :].strip() - audio = audio.split("<###TYPESPLIT###>") - notedata = audio[1] - content = audio[0] - if content: - sql.add_note_to_db( - chat_id, - notename[1:], - notedata, - sql.Types.AUDIO, - file=content, - ) - elif matchvoice: - voice = notedata[matchvoice.end() :].strip() - voice = voice.split("<###TYPESPLIT###>") - notedata = voice[1] - content = voice[0] - if content: - sql.add_note_to_db( - chat_id, - notename[1:], - notedata, - sql.Types.VOICE, - file=content, - ) - elif matchvideo: - video = notedata[matchvideo.end() :].strip() - video = video.split("<###TYPESPLIT###>") - notedata = video[1] - content = video[0] - if content: - sql.add_note_to_db( - chat_id, - notename[1:], - notedata, - sql.Types.VIDEO, - file=content, - ) - elif matchvn: - video_note = notedata[matchvn.end() :].strip() - video_note = video_note.split("<###TYPESPLIT###>") - notedata = video_note[1] - content = video_note[0] - if content: - sql.add_note_to_db( - chat_id, - notename[1:], - notedata, - sql.Types.VIDEO_NOTE, - file=content, - ) - else: - sql.add_note_to_db(chat_id, notename[1:], notedata, sql.Types.TEXT) - - if failures: - with BytesIO(str.encode("\n".join(failures))) as output: - output.name = "failed_imports.txt" - await dispatcher.bot.send_document( - chat_id, - document=output, - filename="failed_imports.txt", - caption="These files/photos failed to import due to originating " - "from another bot. This is a telegram API restriction, and can't " - "be avoided. Sorry for the inconvenience!", - message_thread_id=message.message_thread_id - if message.chat.is_forum - else None, - ) - - -def __stats__(): - return f"• {sql.num_notes()} notes, across {sql.num_chats()} chats." - - -def __migrate__(old_chat_id, new_chat_id): - sql.migrate_chat(old_chat_id, new_chat_id) - - -def __chat_settings__(chat_id, user_id): - notes = sql.get_all_chat_notes(chat_id) - return f"There are `{len(notes)}` notes in this chat." - - -# <=================================================== HELP ====================================================> - - -__help__ = """ - » /get <notename> : get the note with this notename - » #<notename> : same as /get - » /notes or /saved : list all saved notes in this chat - » /number : Will pull the note of that number in the list - ➠ If you would like to retrieve the contents of a note without any formatting, use `/get <notename> noformat`. This can \ -be useful when updating a current note - -*Admins only:* - » /save <notename> <notedata> : saves notedata as a note with name notename - ➠ A button can be added to a note by using standard markdown link syntax - the link should just be prepended with a \ -`buttonurl:` section, as such: `[somelink](buttonurl:example.com)`. Check `/markdownhelp` for more info - » /save <notename> : save the replied message as a note with name notename - Separate diff replies by `%%%` to get random notes - ➠ *Example:* - `/save notename - Reply 1 - %%% - Reply 2 - %%% - Reply 3` - » /clear <notename>: clear note with this name - » /removeallnotes: removes all notes from the group - ➠ *Note:* Note names are case-insensitive, and they are automatically converted to lowercase before getting saved. - -""" - -__mod_name__ = "NOTES" - -# <================================================ HANDLER =======================================================> -function(CommandHandler("get", cmd_get)) -function(MessageHandler(filters.Regex(r"^#[^\s]+"), hash_get, block=False)) -function(MessageHandler(filters.Regex(r"^/\d+$"), slash_get, block=False)) -function(CommandHandler("save", save, block=False)) -function(CommandHandler("clear", clear, block=False)) - -function( - DisableAbleCommandHandler( - ["notes", "saved"], list_notes, admin_ok=True, block=False - ) -) - -function(DisableAbleCommandHandler("removeallnotes", clearall, block=False)) -function(CallbackQueryHandler(clearall_btn, pattern=r"notes_.*", block=False)) -# <================================================ END =======================================================> diff --git a/Mikobot/plugins/ping.py b/Mikobot/plugins/ping.py deleted file mode 100644 index 31759899bbae1703aaa3a5dc0b7747dcdf0c02e2..0000000000000000000000000000000000000000 --- a/Mikobot/plugins/ping.py +++ /dev/null @@ -1,39 +0,0 @@ -# <============================================== IMPORTS =========================================================> -import time - -from telegram import Update -from telegram.constants import ParseMode -from telegram.ext import CommandHandler, ContextTypes - -from Mikobot import StartTime, function -from Mikobot.__main__ import get_readable_time -from Mikobot.plugins.helper_funcs.chat_status import check_admin - -# <=======================================================================================================> - - -# <================================================ FUNCTION =======================================================> -@check_admin(only_dev=True) -async def ptb_ping(update: Update, context: ContextTypes.DEFAULT_TYPE): - msg = update.effective_message - - start_time = time.time() - message = await msg.reply_text("Pining") - end_time = time.time() - telegram_ping = str(round((end_time - start_time) * 1000, 3)) + " ms" - uptime = get_readable_time((time.time() - StartTime)) - - await message.edit_text( - "🏓 <b>PONG</b>\n\n" - "<b>Time taken:</b> <code>{}</code>\n" - "<b>Uptime:</b> <code>{}</code>".format(telegram_ping, uptime), - parse_mode=ParseMode.HTML, - ) - - -# <=======================================================================================================> - - -# <================================================ HANDLER =======================================================> -function(CommandHandler("ping", ptb_ping, block=False)) -# <================================================ END =======================================================> diff --git a/Mikobot/plugins/pokedex.py b/Mikobot/plugins/pokedex.py deleted file mode 100644 index 49d856f13de0695eee14546b79992bb4673d1c43..0000000000000000000000000000000000000000 --- a/Mikobot/plugins/pokedex.py +++ /dev/null @@ -1,143 +0,0 @@ -# SOURCE https://github.com/Team-ProjectCodeX -# CREATED BY https://t.me/O_okarma -# API BY https://www.github.com/SOME-1HING -# PROVIDED BY https://t.me/ProjectCodeX - -# <============================================== IMPORTS =========================================================> -from telegram import InlineKeyboardButton, InlineKeyboardMarkup, Update -from telegram.ext import ContextTypes, CallbackQueryHandler, CommandHandler - -from Mikobot import function -from Mikobot.state import state - -# <=======================================================================================================> - - -# <================================================ FUNCTIONS =====================================================> -async def get_pokemon_info(name_or_id): - try: - response = await state.get( - f"https://sugoi-api.vercel.app/pokemon?name={name_or_id}" - ) - if response.status_code == 200: - return response.json() - - response = await state.get( - f"https://sugoi-api.vercel.app/pokemon?id={name_or_id}" - ) - if response.status_code == 200: - return response.json() - - except Exception as e: - print(f"An error occurred: {str(e)}") - - return None - - -async def pokedex(update: Update, context: ContextTypes.DEFAULT_TYPE): - try: - if context.args: - name_or_id = context.args[0] - pokemon_info = await get_pokemon_info(name_or_id) - - if pokemon_info: - reply_message = ( - f"🐾 𝗡𝗔𝗠𝗘: {pokemon_info['name']}\n" - f"•➥ 𝗜𝗗: {pokemon_info['id']}\n" - f"•➥ 𝗛𝗘𝗜𝗚𝗛𝗧: {pokemon_info['height']}\n" - f"•➥ 𝗪𝗘𝗜𝗚𝗛𝗧: {pokemon_info['weight']}\n" - ) - - abilities = ", ".join( - ability["ability"]["name"] for ability in pokemon_info["abilities"] - ) - reply_message += f"•➥ 𝗔𝗕𝗜𝗟𝗜𝗧𝗜𝗘𝗦: {abilities}\n" - - types = ", ".join( - type_info["type"]["name"] for type_info in pokemon_info["types"] - ) - reply_message += f"•➥ 𝗧𝗬𝗣𝗘𝗦: {types}\n" - - image_url = f"https://img.pokemondb.net/artwork/large/{pokemon_info['name']}.jpg" - - # Create inline buttons - keyboard = [ - [ - InlineKeyboardButton(text="🔖 STATS", callback_data="stats"), - InlineKeyboardButton(text="⚜️ MOVES", callback_data="moves"), - ] - ] - - reply_markup = InlineKeyboardMarkup(keyboard) - - await update.message.reply_photo( - photo=image_url, - caption=reply_message, - reply_markup=reply_markup, - ) - else: - await update.message.reply_text("Pokemon not found.") - else: - await update.message.reply_text("Please provide a Pokemon name or ID.") - except Exception as e: - await update.message.reply_text(f"An error occurred: {str(e)}") - - -async def callback_query_handler(update: Update, context: ContextTypes.DEFAULT_TYPE): - query = update.callback_query - await query.answer() - - try: - name = query.message.caption.split("\n")[0].split(": ")[1] - pokemon_info = await get_pokemon_info(name) - - if pokemon_info: - stats = "\n".join( - f"{stat['stat']['name'].upper()}: {stat['base_stat']}" - for stat in pokemon_info["stats"] - ) - stats_message = f"•➥ STATS:\n{stats}\n" - - moves = ", ".join( - move_info["move"]["name"] for move_info in pokemon_info["moves"] - ) - moves_message = f"•➥ MOVES: {moves}" - - if query.data == "stats": - await query.message.reply_text(stats_message) - elif query.data == "moves": - if len(moves_message) > 1000: - with open("moves.txt", "w") as file: - file.write(moves_message) - await query.message.reply_text( - "The moves exceed 1000 characters. Sending as a file.", - disable_web_page_preview=True, - ) - await query.message.reply_document(document=open("moves.txt", "rb")) - else: - await query.message.reply_text(moves_message) - else: - await query.message.reply_text("Pokemon not found.") - except Exception as e: - await query.message.reply_text(f"An error occurred: {str(e)}") - - -# <================================================ HANDLER =======================================================> -# Add the command and callback query handlers to the dispatcher -function(CommandHandler("pokedex", pokedex, block=False)) -function( - CallbackQueryHandler(callback_query_handler, pattern="^(stats|moves)$", block=False) -) - -# <================================================ HANDLER =======================================================> -__help__ = """ - -🍥 *POKEMON SEARCH* - -➠ *Commands*: - -» /pokedex < Search > : Gives that pokemon info. -""" - -__mod_name__ = "POKEDEX" -# <================================================ END =======================================================> diff --git a/Mikobot/plugins/purge.py b/Mikobot/plugins/purge.py deleted file mode 100644 index b3c750cd44d4c353adc8525cd110fee0012d4293..0000000000000000000000000000000000000000 --- a/Mikobot/plugins/purge.py +++ /dev/null @@ -1,135 +0,0 @@ -# <============================================== IMPORTS =========================================================> -from asyncio import sleep - -from pyrogram import filters -from pyrogram.enums import ChatType -from pyrogram.errors import MessageDeleteForbidden, RPCError -from pyrogram.types import Message - -from Mikobot import SUPPORT_CHAT, app -from Mikobot.utils.can_restrict import can_restrict - -# <=======================================================================================================> - - -# <================================================ FUNCTION =======================================================> -@app.on_message(filters.command("purge")) -@can_restrict -async def purge(c: app, m: Message): - if m.chat.type != ChatType.SUPERGROUP: - await m.reply_text(text="Cannot purge messages in a basic group") - return - - if m.reply_to_message: - message_ids = list(range(m.reply_to_message.id, m.id)) - - def divide_chunks(l: list, n: int = 100): - for i in range(0, len(l), n): - yield l[i : i + n] - - # Dielete messages in chunks of 100 messages - m_list = list(divide_chunks(message_ids)) - - try: - for plist in m_list: - await c.delete_messages( - chat_id=m.chat.id, - message_ids=plist, - revoke=True, - ) - await m.delete() - except MessageDeleteForbidden: - await m.reply_text( - text="Cannot delete all messages. The messages may be too old, I might not have delete rights, or this might not be a supergroup." - ) - return - except RPCError as ef: - await m.reply_text( - text=f"""Some error occured, report to @{SUPPORT_CHAT} - - <b>Error:</b> <code>{ef}</code>""" - ) - - count_del_msg = len(message_ids) - - z = await m.reply_text(text=f"Deleted <i>{count_del_msg}</i> messages") - await sleep(3) - await z.delete() - return - await m.reply_text("Reply to a message to start purge !") - return - - -@app.on_message(filters.command("spurge")) -@can_restrict -async def spurge(c: app, m: Message): - if m.chat.type != ChatType.SUPERGROUP: - await m.reply_text(text="Cannot purge messages in a basic group") - return - - if m.reply_to_message: - message_ids = list(range(m.reply_to_message.id, m.id)) - - def divide_chunks(l: list, n: int = 100): - for i in range(0, len(l), n): - yield l[i : i + n] - - # Dielete messages in chunks of 100 messages - m_list = list(divide_chunks(message_ids)) - - try: - for plist in m_list: - await c.delete_messages( - chat_id=m.chat.id, - message_ids=plist, - revoke=True, - ) - await m.delete() - except MessageDeleteForbidden: - await m.reply_text( - text="Cannot delete all messages. The messages may be too old, I might not have delete rights, or this might not be a supergroup." - ) - return - except RPCError as ef: - await m.reply_text( - text=f"""Some error occured, report to @{SUPPORT_CHAT} - - <b>Error:</b> <code>{ef}</code>""" - ) - return - await m.reply_text("Reply to a message to start spurge !") - return - - -@app.on_message( - filters.command("del"), - group=9, -) -@can_restrict -async def del_msg(c: app, m: Message): - if m.chat.type != ChatType.SUPERGROUP: - return - - if m.reply_to_message: - await m.delete() - await c.delete_messages( - chat_id=m.chat.id, - message_ids=m.reply_to_message.id, - ) - else: - await m.reply_text(text="What do you wanna delete?") - return - - -# <=================================================== HELP ====================================================> -__help__ = """ -❗️*Purge* - -» /purge: Deletes messages upto replied message. - -» /spurge: Deletes messages upto replied message without a success message. - -» /del: Deletes a single message, used as a reply to message.""" - -__mod_name__ = "PURGE" -# <================================================ END =======================================================> diff --git a/Mikobot/plugins/quotely.py b/Mikobot/plugins/quotely.py deleted file mode 100644 index 1c345f0e7f4b0c53fd13af94226dfb146a870678..0000000000000000000000000000000000000000 --- a/Mikobot/plugins/quotely.py +++ /dev/null @@ -1,294 +0,0 @@ -# <============================================== IMPORTS =========================================================> -import base64 -import os -from random import choice - -from aiohttp import ContentTypeError -from PIL import Image -from telethon.tl import types -from telethon.utils import get_display_name, get_peer_id - -from Mikobot import DEV_USERS -from Mikobot.events import register - -# <=======================================================================================================> - - -# <================================================ CLASS & FUNCTION =======================================================> -class Quotly: - _API = "https://bot.lyo.su/quote/generate" - _entities = { - types.MessageEntityPhone: "phone_number", - types.MessageEntityMention: "mention", - types.MessageEntityBold: "bold", - types.MessageEntityCashtag: "cashtag", - types.MessageEntityStrike: "strikethrough", - types.MessageEntityHashtag: "hashtag", - types.MessageEntityEmail: "email", - types.MessageEntityMentionName: "text_mention", - types.MessageEntityUnderline: "underline", - types.MessageEntityUrl: "url", - types.MessageEntityTextUrl: "text_link", - types.MessageEntityBotCommand: "bot_command", - types.MessageEntityCode: "code", - types.MessageEntityPre: "pre", - } - - async def _format_quote(self, event, reply=None, sender=None, type_="private"): - async def telegraph(file_): - file = file_ + ".png" - Image.open(file_).save(file, "PNG") - files = {"file": open(file, "rb").read()} - uri = ( - "https://telegra.ph" - + ( - await async_searcher( - "https://telegra.ph/upload", post=True, data=files, re_json=True - ) - )[0]["src"] - ) - os.remove(file) - os.remove(file_) - return uri - - reply = ( - { - "name": get_display_name(reply.sender) or "Deleted Account", - "text": reply.raw_text, - "chatId": reply.chat_id, - } - if reply - else {} - ) - - is_fwd = event.fwd_from - name, last_name = None, None - - if sender and sender.id not in DEV_USERS: - id_ = get_peer_id(sender) - name = get_display_name(sender) - elif not is_fwd: - id_ = event.sender_id - sender = await event.get_sender() - name = get_display_name(sender) - else: - id_, sender = None, None - name = is_fwd.from_name - if is_fwd.from_id: - id_ = get_peer_id(is_fwd.from_id) - try: - sender = await event.client.get_entity(id_) - name = get_display_name(sender) - except ValueError: - pass - if sender and hasattr(sender, "last_name"): - last_name = sender.last_name - - entities = ( - [ - { - "type": self._entities[type(entity)], - **{k: v for k, v in entity.to_dict().items() if k != "_"}, - } - for entity in event.entities - ] - if event.entities - else [] - ) - - message = { - "entities": entities, - "chatId": id_, - "avatar": True, - "from": { - "id": id_, - "first_name": (name or (sender.first_name if sender else None)) - or "Deleted Account", - "last_name": last_name, - "username": sender.username if sender else None, - "language_code": "en", - "title": name, - "name": name or "Unknown", - "type": type_, - }, - "text": event.raw_text, - "replyMessage": reply, - } - - if event.document and event.document.thumbs: - file_ = await event.download_media(thumb=-1) - uri = await telegraph(file_) - message["media"] = {"url": uri} - - return message - - async def create_quotly( - self, - event, - url="https://quote-api.example.com/generate", - reply={}, - bg=None, - sender=None, - OQAPI=True, - file_name="quote.webp", - ): - if not isinstance(event, list): - event = [event] - if OQAPI: - url = Quotly._API - bg = bg or "#1b1429" - content = { - "type": "quote", - "format": "webp", - "backgroundColor": bg, - "width": 512, - "height": 768, - "scale": 2, - "messages": [ - await self._format_quote(message, reply=reply, sender=sender) - for message in event - ], - } - try: - request = await async_searcher(url, post=True, json=content, re_json=True) - except ContentTypeError as er: - if url != self._API: - return await self.create_quotly( - self._API, post=True, json=content, re_json=True - ) - raise er - - if request.get("ok"): - with open(file_name, "wb") as file: - image = base64.decodebytes(request["result"]["image"].encode("utf-8")) - file.write(image) - return file_name - raise Exception(str(request)) - - -quotly = Quotly() - - -async def async_searcher( - url: str, - post: bool = None, - headers: dict = None, - params: dict = None, - json: dict = None, - data: dict = None, - ssl=None, - re_json: bool = False, - re_content: bool = False, - real: bool = False, - *args, - **kwargs -): - try: - import aiohttp - except ImportError: - raise DependencyMissingError( - "'aiohttp' is not installed!\nThis function requires aiohttp to be installed." - ) - - async with aiohttp.ClientSession(headers=headers) as client: - data = await ( - client.post(url, json=json, data=data, ssl=ssl, *args, **kwargs) - if post - else client.get(url, params=params, ssl=ssl, *args, **kwargs) - ) - return await ( - data.json() if re_json else data.read() if re_content else data.text() - ) - - -@register(pattern="^/q(?: |$)(.*)") -async def quott_(event): - match = event.pattern_match.group(1).strip() - if not event.is_reply: - return await event.reply("Please reply to a message.") - - msg = await event.reply("Creating quote, please wait.") - reply = await event.get_reply_message() - replied_to, reply_ = None, None - - if match: - spli_ = match.split(maxsplit=1) - if (spli_[0] in ["r", "reply"]) or ( - spli_[0].isdigit() and int(spli_[0]) in range(1, 21) - ): - if spli_[0].isdigit(): - if not event.client.is_bot: - reply_ = await event.client.get_messages( - event.chat_id, - min_id=event.reply_to_msg_id - 1, - reverse=True, - limit=int(spli_[0]), - ) - else: - id_ = reply.id - reply_ = [] - for msg_ in range(id_, id_ + int(spli_[0])): - msh = await event.client.get_messages(event.chat_id, ids=msg_) - if msh: - reply_.append(msh) - else: - replied_to = await reply.get_reply_message() - try: - match = spli_[1] - except IndexError: - match = None - - user = None - - if not reply_: - reply_ = reply - - if match: - match = match.split(maxsplit=1) - - if match: - if match[0].startswith("@") or match[0].isdigit(): - try: - match_ = await event.client.parse_id(match[0]) - user = await event.client.get_entity(match_) - except ValueError: - pass - match = match[1] if len(match) == 2 else None - else: - match = match[0] - - if match == "random": - match = choice(all_col) - - try: - file = await quotly.create_quotly( - reply_, bg=match, reply=replied_to, sender=user - ) - except Exception as er: - return await msg.edit(str(er)) - - message = await reply.reply("", file=file) - os.remove(file) - await msg.delete() - return message - - -# <=================================================== HELP ====================================================> - - -__mod_name__ = "QUOTELY" - -__help__ = """ -» /q : Create quote. - -» /q r : Get replied quote. - -» /q 2 ᴛᴏ 8 : Get multiple quotes. - -» /q < any colour name > : Create any coloured quotes. - -➠ Example: - -» /q red , /q blue etc. -""" -# <================================================ END =======================================================> diff --git a/Mikobot/plugins/reverse.py b/Mikobot/plugins/reverse.py deleted file mode 100644 index c6635a76bb7e9fbb3a78257bbdf017df74261ade..0000000000000000000000000000000000000000 --- a/Mikobot/plugins/reverse.py +++ /dev/null @@ -1,117 +0,0 @@ -# CREATED BY: @Qewertyy - -# <============================================== IMPORTS =========================================================> -import os -import traceback - -from pyrogram import Client, filters -from pyrogram import types as t - -from Mikobot import app -from Mikobot.state import state - -from .telegraph import telegraph, upload_file - - -# <================================================ FUNCTIONS =====================================================> -@app.on_message(filters.command(["p", "pp", "reverse", "sauce"])) -async def reverseImageSearch(_: Client, m: t.Message): - try: - reply = await m.reply_text("`Downloading...`") - file = None - if not m.reply_to_message: - return await reply.edit("Reply to an image?") - if m.reply_to_message.document is False or m.reply_to_message.photo is False: - return await reply.edit("Reply to an image?") - if ( - m.reply_to_message.document - and m.reply_to_message.document.mime_type - in ["image/png", "image/jpg", "image/jpeg"] - or m.reply_to_message.photo - ): - if ( - m.reply_to_message.document - and m.reply_to_message.document.file_size > 5242880 - ): - return await reply.edit("Reply to an image?") - file = await m.reply_to_message.download() - else: - return await reply.edit("Reply to an image?") - await reply.edit("`Uploading to the server...`") - imgUrl = upload_file(file) - os.remove(file) - if imgUrl is None: - return await reply.edit("Ran into an error.") - output = await reverse_image_search("google", f"https://graph.org/{imgUrl[0]}") - if output is None: - return await reply.edit("Ran into an error.") - - names = output["content"]["bestResults"]["names"] - urls = output["content"]["bestResults"]["urls"] - btn = t.InlineKeyboardMarkup( - [[t.InlineKeyboardButton(text="IMAGE URL", url=urls[-1])]] - ) - - if len(names) > 10: - message = "\n".join( - [f"{index+1}. {name}" for index, name in enumerate(names[:10])] - ) - htmlMessage = f"<br/>".join( - [f"{index+1}. {name}" for index, name in enumerate(names)] - ) - htmlMessage += "<br/><br/><h3>URLS</h3><br/>" - htmlMessage += f"<br/>".join([f"{url}" for url in urls]) - htmlMessage += ( - "<br/><br/>By <a href='https://lexica.qewertyy.me'>LexicaAPI</a>" - ) - telegraph_page = telegraph.create_page( - "More Results", html_content=htmlMessage - ) - message += f"\n\n[More Results](https://telegra.ph/{telegraph_page['path']})\n\nBy @LexicaAPI" - await reply.delete() - return await m.reply_text(message, reply_markup=btn) - - message = "\n".join([f"{index+1}. {name}" for index, name in enumerate(names)]) - await reply.delete() - await m.reply_text(f"{message}\n\nBy @LexicaAPI", reply_markup=btn) - except Exception as E: - traceback.print_exc() - return await m.reply_text("Ran into an error.") - - -async def reverse_image_search(search_engine, img_url) -> dict: - try: - response = await state.post( - f"https://lexica.qewertyy.me/image-reverse/{search_engine}?img_url={img_url}", - ) - if response.status_code != 200: - return None - output = response.json() - if output["code"] != 2: - return None - return output - except Exception as E: - raise Exception(f"API Error: {E}") - - -# <=================================================== HELP ====================================================> - - -__help__ = """ -🖼 *IMAGE REVERSE* - -» `/p`, `/pp`, `/reverse`, `/sauce`: Reverse image search using various search engines. - -➠ *Usage:* -Reply to an image with one of the above commands to perform a reverse image search. - -➠ *Example:* -» `/p` - Perform a reverse image search. - -➠ *Note:* -- Supported image formats: PNG, JPG, JPEG. -- Maximum file size: 5 MB. -""" - -__mod_name__ = "REVERSE" -# <================================================ END =======================================================> diff --git a/Mikobot/plugins/rules.py b/Mikobot/plugins/rules.py deleted file mode 100644 index 8dac9d7ef41183241e2b6691fbe4abe501cf6d0b..0000000000000000000000000000000000000000 --- a/Mikobot/plugins/rules.py +++ /dev/null @@ -1,169 +0,0 @@ -# <============================================== IMPORTS =========================================================> -from telegram import InlineKeyboardButton, InlineKeyboardMarkup, Update -from telegram.constants import ParseMode -from telegram.error import BadRequest -from telegram.ext import CommandHandler, ContextTypes, filters -from telegram.helpers import escape_markdown - -import Database.sql.rules_sql as sql -from Mikobot import dispatcher, function -from Mikobot.plugins.helper_funcs.chat_status import check_admin -from Mikobot.plugins.helper_funcs.string_handling import ( - markdown_parser, - markdown_to_html, -) - -# <=======================================================================================================> - - -# <================================================ FUNCTION =======================================================> -async def get_rules(update: Update, context: ContextTypes.DEFAULT_TYPE): - chat_id = update.effective_chat.id - await send_rules(update, chat_id) - - -async def send_rules(update, chat_id, from_pm=False): - bot = dispatcher.bot - user = update.effective_user # type: Optional[User] - reply_msg = update.message.reply_to_message - try: - chat = await bot.get_chat(chat_id) - except BadRequest as excp: - if excp.message == "Chat not found" and from_pm: - await bot.send_message( - user.id, - "The rules shortcut for this chat hasn't been set properly! Ask admins to " - "fix this.\nMaybe they forgot the hyphen in ID", - message_thread_id=update.effective_message.message_thread_id - if chat.is_forum - else None, - ) - return - else: - raise - - rules = sql.get_rules(chat_id) - text = f"The rules for {escape_markdown(chat.title, 2)} are:\n\n{markdown_to_html(rules)}" - - if from_pm and rules: - await bot.send_message( - user.id, - text, - parse_mode=ParseMode.MARKDOWN, - disable_web_page_preview=True, - ) - elif from_pm: - await bot.send_message( - user.id, - "The group admins haven't set any rules for this chat yet. " - "This probably doesn't mean it's lawless though...!", - ) - elif rules and reply_msg and not reply_msg.forum_topic_created: - await reply_msg.reply_text( - "Please click the button below to see the rules.", - reply_markup=InlineKeyboardMarkup( - [ - [ - InlineKeyboardButton( - text="RULES", - url=f"t.me/{bot.username}?start={chat_id}", - ), - ], - ], - ), - ) - elif rules: - await update.effective_message.reply_text( - "Please click the button below to see the rules.", - reply_markup=InlineKeyboardMarkup( - [ - [ - InlineKeyboardButton( - text="RULES", - url=f"t.me/{bot.username}?start={chat_id}", - ), - ], - ], - ), - ) - else: - await update.effective_message.reply_text( - "The group admins haven't set any rules for this chat yet. " - "This probably doesn't mean it's lawless though...!", - ) - - -@check_admin(is_user=True) -async def set_rules(update: Update, context: ContextTypes.DEFAULT_TYPE): - chat_id = update.effective_chat.id - msg = update.effective_message # type: Optional[Message] - raw_text = msg.text - args = raw_text.split(None, 1) # use python's maxsplit to separate cmd and args - if len(args) == 2: - txt = args[1] - offset = len(txt) - len(raw_text) # set correct offset relative to command - markdown_rules = markdown_parser( - txt, - entities=msg.parse_entities(), - offset=offset, - ) - - sql.set_rules(chat_id, markdown_rules) - await update.effective_message.reply_text( - "Successfully set rules for this group." - ) - - -@check_admin(is_user=True) -async def clear_rules(update: Update, context: ContextTypes.DEFAULT_TYPE): - chat_id = update.effective_chat.id - sql.set_rules(chat_id, "") - await update.effective_message.reply_text("Successfully cleared rules!") - - -def __stats__(): - return f"• {sql.num_chats()} chats have rules set." - - -async def __import_data__(chat_id, data, message): - # set chat rules - rules = data.get("info", {}).get("rules", "") - sql.set_rules(chat_id, rules) - - -def __migrate__(old_chat_id, new_chat_id): - sql.migrate_chat(old_chat_id, new_chat_id) - - -def __chat_settings__(chat_id, user_id): - return f"This chat has had its rules set: `{bool(sql.get_rules(chat_id))}`" - - -# <=======================================================================================================> - - -# <================================================= HELP ======================================================> -__help__ = """ -➠ /rules: Get the rules for this chat. - -➠ *Admins only*: -» /setrules <your rules here>: Set the rules for this chat. - -» /clearrules: Clear the rules for this chat. -""" - -__mod_name__ = "RULES" - -# <================================================ HANDLER =======================================================> -function( - CommandHandler("rules", get_rules, filters=filters.ChatType.GROUPS, block=False) -) -function( - CommandHandler("setrules", set_rules, filters=filters.ChatType.GROUPS, block=False) -) -function( - CommandHandler( - "clearrules", clear_rules, filters=filters.ChatType.GROUPS, block=False - ) -) -# <================================================== END =====================================================> diff --git a/Mikobot/plugins/search.py b/Mikobot/plugins/search.py deleted file mode 100644 index a81d1c663baf2063b9d2c7e7456ee10b89b4b8b8..0000000000000000000000000000000000000000 --- a/Mikobot/plugins/search.py +++ /dev/null @@ -1,185 +0,0 @@ -# SOURCE https://github.com/Team-ProjectCodeX -# CREATED BY https://t.me/O_okarma -# API BY https://www.github.com/SOME-1HING -# PROVIDED BY https://t.me/ProjectCodeX - -# <============================================== IMPORTS =========================================================> -import json -import random - -from pyrogram import Client, filters -from pyrogram.types import InputMediaPhoto, Message - -from Mikobot import app -from Mikobot.state import state - -# <=======================================================================================================> - -BINGSEARCH_URL = "https://sugoi-api.vercel.app/search" -NEWS_URL = "https://sugoi-api.vercel.app/news?keyword={}" - - -# <================================================ FUNCTION =======================================================> -@app.on_message(filters.command("news")) -async def news(_, message: Message): - keyword = ( - message.text.split(" ", 1)[1].strip() if len(message.text.split()) > 1 else "" - ) - url = NEWS_URL.format(keyword) - - try: - response = await state.get(url) # Assuming state is an asynchronous function - news_data = response.json() - - if "error" in news_data: - error_message = news_data["error"] - await message.reply_text(f"Error: {error_message}") - else: - if len(news_data) > 0: - news_item = random.choice(news_data) - - title = news_item["title"] - excerpt = news_item["excerpt"] - source = news_item["source"] - relative_time = news_item["relative_time"] - news_url = news_item["url"] - - message_text = f"𝗧𝗜𝗧𝗟𝗘: {title}\n𝗦𝗢𝗨𝗥𝗖𝗘: {source}\n𝗧𝗜𝗠𝗘: {relative_time}\n𝗘𝗫𝗖𝗘𝗥𝗣𝗧: {excerpt}\n𝗨𝗥𝗟: {news_url}" - await message.reply_text(message_text) - else: - await message.reply_text("No news found.") - - except Exception as e: # Replace with specific exception type if possible - await message.reply_text(f"Error: {str(e)}") - - -@app.on_message(filters.command("bingsearch")) -async def bing_search(client: Client, message: Message): - try: - if len(message.command) == 1: - await message.reply_text("Please provide a keyword to search.") - return - - keyword = " ".join( - message.command[1:] - ) # Assuming the keyword is passed as arguments - params = {"keyword": keyword} - - response = await state.get( - BINGSEARCH_URL, params=params - ) # Use the state.get method - - if response.status_code == 200: - results = response.json() - if not results: - await message.reply_text("No results found.") - else: - message_text = "" - for result in results[:7]: - title = result.get("title", "") - link = result.get("link", "") - message_text += f"{title}\n{link}\n\n" - await message.reply_text(message_text.strip()) - else: - await message.reply_text("Sorry, something went wrong with the search.") - except Exception as e: - await message.reply_text(f"An error occurred: {str(e)}") - - -# Command handler for the '/bingimg' command -@app.on_message(filters.command("bingimg")) -async def bingimg_search(client: Client, message: Message): - try: - text = message.text.split(None, 1)[ - 1 - ] # Extract the query from command arguments - except IndexError: - return await message.reply_text( - "Provide me a query to search!" - ) # Return error if no query is provided - - search_message = await message.reply_text("🔎") # Display searching message - - # Send request to Bing image search API using state function - bingimg_url = "https://sugoi-api.vercel.app/bingimg?keyword=" + text - resp = await state.get(bingimg_url) - images = json.loads(resp.text) # Parse the response JSON into a list of image URLs - - media = [] - count = 0 - for img in images: - if count == 7: - break - - # Create InputMediaPhoto object for each image URL - media.append(InputMediaPhoto(media=img)) - count += 1 - - # Send the media group as a reply to the user - await message.reply_media_group(media=media) - - # Delete the searching message and the original command message - await search_message.delete() - await message.delete() - - -# Command handler for the '/googleimg' command -@app.on_message(filters.command("googleimg")) -async def googleimg_search(client: Client, message: Message): - try: - text = message.text.split(None, 1)[ - 1 - ] # Extract the query from command arguments - except IndexError: - return await message.reply_text( - "Provide me a query to search!" - ) # Return error if no query is provided - - search_message = await message.reply_text("💭") # Display searching message - - # Send request to Google image search API using state function - googleimg_url = "https://sugoi-api.vercel.app/googleimg?keyword=" + text - resp = await state.get(googleimg_url) - images = json.loads(resp.text) # Parse the response JSON into a list of image URLs - - media = [] - count = 0 - for img in images: - if count == 7: - break - - # Create InputMediaPhoto object for each image URL - media.append(InputMediaPhoto(media=img)) - count += 1 - - # Send the media group as a reply to the user - await message.reply_media_group(media=media) - - # Delete the searching message and the original command message - await search_message.delete() - await message.delete() - - -# <=======================================================================================================> - - -# <=================================================== HELP ====================================================> -__mod_name__ = "SEARCH" - -__help__ = """ -💭 𝗦𝗘𝗔𝗥𝗖𝗛 - -➠ *Available commands:* - -» /googleimg <search query>: It retrieves and displays images obtained through a Google image search. - -» /bingimg <search query>: It retrieves and displays images obtained through a Bing image search. - -» /news <search query> : search news. - -» /bingsearch <search query> : get search result with links. - -➠ *Example:* -➠ `/bingsearch app`: return search results. -""" -# <================================================ END =======================================================> diff --git a/Mikobot/plugins/speedtest.py b/Mikobot/plugins/speedtest.py deleted file mode 100644 index bd4cbfd8e8ab1bf2c0d4f68708f0effb937f352c..0000000000000000000000000000000000000000 --- a/Mikobot/plugins/speedtest.py +++ /dev/null @@ -1,74 +0,0 @@ -# <============================================== IMPORTS =========================================================> -import speedtest -from telegram import InlineKeyboardButton, InlineKeyboardMarkup, Update -from telegram.constants import ParseMode -from telegram.ext import CallbackQueryHandler, ContextTypes - -from Mikobot import DEV_USERS, function -from Mikobot.plugins.disable import DisableAbleCommandHandler -from Mikobot.plugins.helper_funcs.chat_status import check_admin - -# <=======================================================================================================> - - -# <================================================ FUNCTION =======================================================> -def convert(speed): - return round(int(speed) / 1048576, 2) - - -@check_admin(only_dev=True) -async def speedtestxyz(update: Update, context: ContextTypes.DEFAULT_TYPE): - buttons = [ - [ - InlineKeyboardButton("Image", callback_data="speedtest_image"), - InlineKeyboardButton("Text", callback_data="speedtest_text"), - ], - ] - await update.effective_message.reply_text( - "Select SpeedTest Mode", - reply_markup=InlineKeyboardMarkup(buttons), - ) - - -async def speedtestxyz_callback(update: Update, context: ContextTypes.DEFAULT_TYPE): - query = update.callback_query - - if query.from_user.id in DEV_USERS: - msg = await update.effective_message.edit_text("Running a speedtest....") - speed = speedtest.Speedtest() - speed.get_best_server() - speed.download() - speed.upload() - replymsg = "SpeedTest Results:" - - if query.data == "speedtest_image": - speedtest_image = speed.results.share() - await update.effective_message.reply_photo( - photo=speedtest_image, - caption=replymsg, - ) - await msg.delete() - - elif query.data == "speedtest_text": - result = speed.results.dict() - replymsg += f"\nDownload: `{convert(result['download'])}Mb/s`\nUpload: `{convert(result['upload'])}Mb/s`\nPing: `{result['ping']}`" - await update.effective_message.edit_text( - replymsg, parse_mode=ParseMode.MARKDOWN - ) - else: - await query.answer("You are required to join Black Bulls to use this command.") - - -# <================================================ HANDLER =======================================================> -SPEED_TEST_HANDLER = DisableAbleCommandHandler("speedtest", speedtestxyz, block=False) -SPEED_TEST_CALLBACKHANDLER = CallbackQueryHandler( - speedtestxyz_callback, pattern="speedtest_.*", block=False -) - -function(SPEED_TEST_HANDLER) -function(SPEED_TEST_CALLBACKHANDLER) - -__mod_name__ = "SpeedTest" -__command_list__ = ["speedtest"] -__handlers__ = [SPEED_TEST_HANDLER, SPEED_TEST_CALLBACKHANDLER] -# <================================================ END =======================================================> diff --git a/Mikobot/plugins/sports.py b/Mikobot/plugins/sports.py deleted file mode 100644 index a87c260a8c92f5c6cd77893ac7c11a7d0589a9de..0000000000000000000000000000000000000000 --- a/Mikobot/plugins/sports.py +++ /dev/null @@ -1,174 +0,0 @@ -# SOURCE https://github.com/Team-ProjectCodeX -# CREATED BY https://t.me/O_okarma -# API BY https://www.github.com/SOME-1HING -# PROVIDED BY https://t.me/ProjectCodeX - -# <============================================== IMPORTS =========================================================> -from telegram import InlineKeyboardButton, InlineKeyboardMarkup, Update -from telegram.constants import ParseMode -from telegram.ext import CallbackQueryHandler, CommandHandler, ContextTypes - -from Mikobot import function -from Mikobot.state import state - -# <=======================================================================================================> - -# API URLs -CRICKET_API_URL = "https://sugoi-api.vercel.app/cricket" -FOOTBALL_API_URL = "https://sugoi-api.vercel.app/football" - - -# Define the MatchManager class as provided in your code -class MatchManager: - def __init__(self, api_url): - self.api_url = api_url - self.matches = [] - self.match_count = 0 - - async def fetch_matches(self): - response = await state.get(self.api_url) - self.matches = response.json() - - def get_next_matches(self, count): - next_matches = self.matches[self.match_count : self.match_count + count] - self.match_count += count - return next_matches - - def reset_matches(self): - self.matches = [] - self.match_count = 0 - - -# <================================================ FUNCTION =======================================================> -async def get_match_text(match, sport): - match_text = f"{'🏏' if sport == 'cricket' else '⚽️'} **{match['title']}**\n\n" - match_text += f"🗓 *Date:* {match['date']}\n" - match_text += f"🏆 *Team 1:* {match['team1']}\n" - match_text += f"🏆 *Team 2:* {match['team2']}\n" - match_text += f"🏟️ *Venue:* {match['venue']}" - return match_text - - -def create_inline_keyboard(sport): - inline_keyboard = [ - [ - InlineKeyboardButton( - f"Next {sport.capitalize()} Match ➡️", - callback_data=f"next_{sport}_match", - ) - ] - ] - return InlineKeyboardMarkup(inline_keyboard) - - -cricket_manager = MatchManager(CRICKET_API_URL) -football_manager = MatchManager(FOOTBALL_API_URL) - - -# Define a command handler for the /cricket command -async def get_cricket_matches(update: Update, context: ContextTypes.DEFAULT_TYPE): - try: - cricket_manager.reset_matches() - await cricket_manager.fetch_matches() - - if not cricket_manager.matches: - await update.message.reply_text("No cricket matches found.") - return - - next_matches = cricket_manager.get_next_matches(1) - match = next_matches[0] - - match_text = await get_match_text(match, "cricket") - reply_markup = create_inline_keyboard("cricket") - - await update.message.reply_text( - match_text, reply_markup=reply_markup, parse_mode=ParseMode.MARKDOWN - ) - - except Exception as e: - await update.message.reply_text(f"An error occurred: {str(e)}") - - -# Define a command handler for the /football command -async def get_football_matches(update: Update, context: ContextTypes.DEFAULT_TYPE): - try: - football_manager.reset_matches() - await football_manager.fetch_matches() - - if not football_manager.matches: - await update.message.reply_text("No football matches found.") - return - - next_matches = football_manager.get_next_matches(1) - match = next_matches[0] - - match_text = await get_match_text(match, "football") - reply_markup = create_inline_keyboard("football") - - await update.message.reply_text( - match_text, reply_markup=reply_markup, parse_mode=ParseMode.MARKDOWN - ) - - except Exception as e: - await update.message.reply_text(f"An error occurred: {str(e)}") - - -# Define a callback query handler for showing the next match -async def show_next_match(update: Update, context: ContextTypes.DEFAULT_TYPE): - try: - query = update.callback_query - sport = query.data.split("_")[1] - manager = cricket_manager if sport == "cricket" else football_manager - - if not manager.matches: - await query.answer(f"No more {sport} matches available.") - return - - next_matches = manager.get_next_matches(3) - - if not next_matches: - await query.answer(f"No more {sport} matches available.") - return - - match_text = "" - for match in next_matches: - match_text += await get_match_text(match, sport) + "\n\n" - - reply_markup = create_inline_keyboard(sport) - - await query.message.edit_text( - match_text, - reply_markup=reply_markup, - parse_mode=ParseMode.MARKDOWN, - disable_web_page_preview=True, - ) - await query.answer() - - except Exception as e: - await query.message.reply_text(f"An error occurred: {str(e)}") - - -# <=======================================================================================================> - - -# <================================================ HANDLER =======================================================> -# Add command handlers to the dispatcher -function(CommandHandler("cricket", get_cricket_matches)) -function(CommandHandler("football", get_football_matches)) -function( - CallbackQueryHandler(show_next_match, pattern=r"^next_(cricket|football)_match$") -) - -# <================================================= HELP ======================================================> -__help__ = """ -🏅 *Match 𝗦chedule* - -➠ *Commands*: - -» /cricket: use this command to get information about the next cricket match. - -» /football: use this command to get information about the next football match. -""" - -__mod_name__ = "SPORTS" -# <================================================== END =====================================================> diff --git a/Mikobot/plugins/telegraph.py b/Mikobot/plugins/telegraph.py deleted file mode 100644 index 3fccba6e2cce31bed38e08335f831f99e3eece97..0000000000000000000000000000000000000000 --- a/Mikobot/plugins/telegraph.py +++ /dev/null @@ -1,73 +0,0 @@ -# <============================================== IMPORTS =========================================================> -import os -from datetime import datetime - -from PIL import Image -from pyrogram import filters -from telegraph import Telegraph, exceptions, upload_file - -from Mikobot import app -from Mikobot.utils.errors import capture_err - -# <=======================================================================================================> - -TMP_DOWNLOAD_DIRECTORY = "tg-File/" -bname = "YaeMiko_Roxbot" # ᴅᴏɴ'ᴛ ᴇᴅɪᴛ ᴛʜɪᴀ ʟɪɴᴇ -telegraph = Telegraph() -r = telegraph.create_account(short_name=bname) -auth_url = r["auth_url"] - - -# <================================================ FUNCTION =======================================================> -@app.on_message(filters.command(["tgm", "tmg", "telegraph"], prefixes="/")) -@capture_err -async def telegraph_upload(client, message): - if message.reply_to_message: - start = datetime.now() - r_message = message.reply_to_message - input_str = message.command[0] - if input_str in ["tgm", "tmg", "telegraph"]: - downloaded_file_name = await client.download_media( - r_message, file_name=TMP_DOWNLOAD_DIRECTORY - ) - end = datetime.now() - ms = (end - start).seconds - h = await message.reply_text(f"Downloaded to file in {ms} seconds.") - if downloaded_file_name.endswith(".webp"): - resize_image(downloaded_file_name) - try: - start = datetime.now() - media_urls = upload_file(downloaded_file_name) - except exceptions.TelegraphException as exc: - await h.edit_text("Error: " + str(exc)) - os.remove(downloaded_file_name) - else: - end = datetime.now() - ms_two = (end - start).seconds - os.remove(downloaded_file_name) - await h.edit_text( - f""" -➼ **Uploaded to [Telegraph](https://telegra.ph{media_urls[0]}) in {ms + ms_two} seconds.**\n -➼ **Copy Link :** `https://telegra.ph{media_urls[0]}`""", - disable_web_page_preview=False, - ) - else: - await message.reply_text( - "Reply to a message to get a permanent telegra.ph link." - ) - - -def resize_image(image): - im = Image.open(image) - im.save(image, "PNG") - - -# <=================================================== HELP ====================================================> -__help__ = """ -➠ *TELEGRAPH*: - -» /tgm, /tmg, /telegraph*:* `get telegram link of replied media` - """ - -__mod_name__ = "TELEGRAPH" -# <================================================ END =======================================================> diff --git a/Mikobot/plugins/tr.py b/Mikobot/plugins/tr.py deleted file mode 100644 index e5adb7effb785d310dde82760dde26a4787e22b0..0000000000000000000000000000000000000000 --- a/Mikobot/plugins/tr.py +++ /dev/null @@ -1,355 +0,0 @@ -# <============================================== IMPORTS =========================================================> -import json -import random -import re -from urllib.parse import quote - -import requests -import urllib3 -from emoji import EMOJI_DATA -from telegram import Update -from telegram.constants import ParseMode -from telegram.ext import ContextTypes, filters - -from Mikobot import LOGGER, function -from Mikobot.plugins.anime import DEFAULT_SERVICE_URLS, LANGUAGES -from Mikobot.plugins.disable import DisableAbleCommandHandler -from Mikobot.plugins.helper_funcs.chat_status import check_admin - -# <=======================================================================================================> - -urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) - -URLS_SUFFIX = [ - re.search("translate.google.(.*)", url.strip()).group(1) - for url in DEFAULT_SERVICE_URLS -] -URL_SUFFIX_DEFAULT = "com" - - -# <================================================ FUNCTION =======================================================> -class google_translator: - """ - You can use 108 language in target and source,details view LANGUAGES. - Target language: like 'en'、'zh'、'th'... - - :param url_suffix: The source text(s) to be translated. Batch translation is supported via sequence input. - The value should be one of the url_suffix listed in : `DEFAULT_SERVICE_URLS` - :type url_suffix: UTF-8 :class:`str`; :class:`unicode`; string sequence (list, tuple, iterator, generator) - - :param text: The source text(s) to be translated. - :type text: UTF-8 :class:`str`; :class:`unicode`; - - :param lang_tgt: The language to translate the source text into. - The value should be one of the language codes listed in : `LANGUAGES` - :type lang_tgt: :class:`str`; :class:`unicode` - - :param lang_src: The language of the source text. - The value should be one of the language codes listed in :const:`googletrans.LANGUAGES` - If a language is not specified, - the system will attempt to identify the source language automatically. - :type lang_src: :class:`str`; :class:`unicode` - - :param timeout: Timeout Will be used for every request. - :type timeout: number or a double of numbers - - :param proxies: proxies Will be used for every request. - :type proxies: class : dict; like: {'http': 'http:171.112.169.47:19934/', 'https': 'https:171.112.169.47:19934/'} - - """ - - def __init__(self, url_suffix="com", timeout=5, proxies=None): - self.proxies = proxies - if url_suffix not in URLS_SUFFIX: - self.url_suffix = URL_SUFFIX_DEFAULT - else: - self.url_suffix = url_suffix - url_base = "https://translate.google.{}".format(self.url_suffix) - self.url = url_base + "/_/TranslateWebserverUi/data/batchexecute" - self.timeout = timeout - - def _package_rpc(self, text, lang_src="auto", lang_tgt="auto"): - GOOGLE_TTS_RPC = ["MkEWBc"] - parameter = [[text.strip(), lang_src, lang_tgt, True], [1]] - escaped_parameter = json.dumps(parameter, separators=(",", ":")) - rpc = [[[random.choice(GOOGLE_TTS_RPC), escaped_parameter, None, "generic"]]] - espaced_rpc = json.dumps(rpc, separators=(",", ":")) - # text_urldecode = quote(text.strip()) - freq_initial = "f.req={}&".format(quote(espaced_rpc)) - freq = freq_initial - return freq - - def translate(self, text, lang_tgt="auto", lang_src="auto", pronounce=False): - try: - lang = LANGUAGES[lang_src] - except: - lang_src = "auto" - try: - lang = LANGUAGES[lang_tgt] - except: - lang_src = "auto" - text = str(text) - if len(text) >= 5000: - return "Warning: Can only detect less than 5000 characters" - if len(text) == 0: - return "" - headers = { - "Referer": "http://translate.google.{}/".format(self.url_suffix), - "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) " - "AppleWebKit/537.36 (KHTML, like Gecko) " - "Chrome/47.0.2526.106 Safari/537.36", - "Content-Type": "application/x-www-form-urlencoded;charset=utf-8", - } - freq = self._package_rpc(text, lang_src, lang_tgt) - response = requests.Request( - method="POST", - url=self.url, - data=freq, - headers=headers, - ) - try: - if self.proxies == None or type(self.proxies) != dict: - self.proxies = {} - with requests.Session() as s: - s.proxies = self.proxies - r = s.send( - request=response.prepare(), verify=False, timeout=self.timeout - ) - for line in r.iter_lines(chunk_size=1024): - decoded_line = line.decode("utf-8") - if "MkEWBc" in decoded_line: - try: - response = decoded_line - response = json.loads(response) - response = list(response) - response = json.loads(response[0][2]) - response_ = list(response) - response = response_[1][0] - if len(response) == 1: - if len(response[0]) > 5: - sentences = response[0][5] - else: ## only url - sentences = response[0][0] - if pronounce == False: - return sentences - elif pronounce == True: - return [sentences, None, None] - translate_text = "" - for sentence in sentences: - sentence = sentence[0] - translate_text += sentence.strip() + " " - translate_text = translate_text - if pronounce == False: - return translate_text - elif pronounce == True: - pronounce_src = response_[0][0] - pronounce_tgt = response_[1][0][0][1] - return [translate_text, pronounce_src, pronounce_tgt] - elif len(response) == 2: - sentences = [] - for i in response: - sentences.append(i[0]) - if pronounce == False: - return sentences - elif pronounce == True: - pronounce_src = response_[0][0] - pronounce_tgt = response_[1][0][0][1] - return [sentences, pronounce_src, pronounce_tgt] - except Exception as e: - raise e - r.raise_for_status() - except requests.exceptions.ConnectTimeout as e: - raise e - except requests.exceptions.HTTPError as e: - # Request successful, bad response - raise google_new_transError(tts=self, response=r) - except requests.exceptions.RequestException as e: - # Request failed - raise google_new_transError(tts=self) - - def detect(self, text): - text = str(text) - if len(text) >= 5000: - return LOGGER.debug("Warning: Can only detect less than 5000 characters") - if len(text) == 0: - return "" - headers = { - "Referer": "http://translate.google.{}/".format(self.url_suffix), - "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) " - "AppleWebKit/537.36 (KHTML, like Gecko) " - "Chrome/47.0.2526.106 Safari/537.36", - "Content-Type": "application/x-www-form-urlencoded;charset=utf-8", - } - freq = self._package_rpc(text) - response = requests.Request( - method="POST", url=self.url, data=freq, headers=headers - ) - try: - if self.proxies == None or type(self.proxies) != dict: - self.proxies = {} - with requests.Session() as s: - s.proxies = self.proxies - r = s.send( - request=response.prepare(), verify=False, timeout=self.timeout - ) - - for line in r.iter_lines(chunk_size=1024): - decoded_line = line.decode("utf-8") - if "MkEWBc" in decoded_line: - # regex_str = r"\[\[\"wrb.fr\",\"MkEWBc\",\"\[\[(.*).*?,\[\[\[" - try: - # data_got = re.search(regex_str,decoded_line).group(1) - response = decoded_line - response = json.loads(response) - response = list(response) - response = json.loads(response[0][2]) - response = list(response) - detect_lang = response[0][2] - except Exception: - raise Exception - # data_got = data_got.split('\\\"]')[0] - return [detect_lang, LANGUAGES[detect_lang.lower()]] - r.raise_for_status() - except requests.exceptions.HTTPError as e: - # Request successful, bad response - LOGGER.debug(str(e)) - raise google_new_transError(tts=self, response=r) - except requests.exceptions.RequestException as e: - # Request failed - LOGGER.debug(str(e)) - raise google_new_transError(tts=self) - - -@check_admin(is_user=True) -async def echo(update: Update, context: ContextTypes.DEFAULT_TYPE): - args = update.effective_message.text.split(None, 1) - message = update.effective_message - - if message.reply_to_message: - await message.reply_to_message.reply_text( - args[1], - parse_mode="MARKDOWN", - disable_web_page_preview=True, - ) - else: - await message.reply_text( - args[1], - quote=False, - parse_mode="MARKDOWN", - disable_web_page_preview=True, - ) - await message.delete() - - -async def totranslate(update: Update, context: ContextTypes.DEFAULT_TYPE): - message = update.effective_message - problem_lang_code = [] - for key in LANGUAGES: - if "-" in key: - problem_lang_code.append(key) - - try: - if ( - message.reply_to_message - and not message.reply_to_message.forum_topic_created - ): - args = update.effective_message.text.split(None, 1) - if message.reply_to_message.text: - text = message.reply_to_message.text - elif message.reply_to_message.caption: - text = message.reply_to_message.caption - - try: - source_lang = args[1].split(None, 1)[0] - except (IndexError, AttributeError): - source_lang = "en" - - else: - args = update.effective_message.text.split(None, 2) - text = args[2] - source_lang = args[1] - - if source_lang.count("-") == 2: - for lang in problem_lang_code: - if lang in source_lang: - if source_lang.startswith(lang): - dest_lang = source_lang.rsplit("-", 1)[1] - source_lang = source_lang.rsplit("-", 1)[0] - else: - dest_lang = source_lang.split("-", 1)[1] - source_lang = source_lang.split("-", 1)[0] - elif source_lang.count("-") == 1: - for lang in problem_lang_code: - if lang in source_lang: - dest_lang = source_lang - source_lang = None - break - if dest_lang is None: - dest_lang = source_lang.split("-")[1] - source_lang = source_lang.split("-")[0] - else: - dest_lang = source_lang - source_lang = None - - exclude_list = EMOJI_DATA.keys() - for emoji in exclude_list: - if emoji in text: - text = text.replace(emoji, "") - - trl = google_translator() - if source_lang is None: - detection = trl.detect(text) - trans_str = trl.translate(text, lang_tgt=dest_lang) - return await message.reply_text( - f"📒 *Translated from* `{detection[0]}` to `{dest_lang}`:\n`{trans_str}`", - parse_mode=ParseMode.MARKDOWN, - ) - else: - trans_str = trl.translate(text, lang_tgt=dest_lang, lang_src=source_lang) - await message.reply_text( - f"📒 *Translated from* `{source_lang}` to `{dest_lang}`:\n`{trans_str}`", - parse_mode=ParseMode.MARKDOWN, - ) - - except IndexError: - await update.effective_message.reply_text( - "Reply to messages or write messages from other languages ​​for translating into the intended language\n\n" - "Example: `/tr en-ta` to translate from English to Tamil\n" - "Or use: `/tr ta` for automatic detection and translating it into Tamil.\n" - "See [List of Language Codes](https://t.me/Hydra_Updates/80) for a list of language codes.", - parse_mode="markdown", - disable_web_page_preview=True, - ) - except ValueError: - await update.effective_message.reply_text("The intended language is not found!") - else: - return - - -# <=================================================== HELP ====================================================> - - -__help__ = """ -➠ `/tr` or `/tl` (language code) as reply to a long message - -➠ *Example:* - -» `/tr en`*:* translates something to english - -» `/tr hi-en`*:* translates hindi to english - -» /echo < text >: echos the message. -""" - -TRANSLATE_HANDLER = DisableAbleCommandHandler(["tr", "tl"], totranslate, block=False) -ECHO_HANDLER = DisableAbleCommandHandler( - "echo", echo, filters=filters.ChatType.GROUPS, block=False -) - -function(TRANSLATE_HANDLER) -function(ECHO_HANDLER) - -__mod_name__ = "TRANSLATOR" -__command_list__ = ["tr", "tl", "echo"] -__handlers__ = [TRANSLATE_HANDLER] -# <================================================ END =======================================================> diff --git a/Mikobot/plugins/users.py b/Mikobot/plugins/users.py deleted file mode 100644 index 1b831537e68e78ffa36b7c1eca12e4e7b63c3d29..0000000000000000000000000000000000000000 --- a/Mikobot/plugins/users.py +++ /dev/null @@ -1,281 +0,0 @@ -# <============================================== IMPORTS =========================================================> -import asyncio -from io import BytesIO -from typing import Union - -from pyrogram import Client -from pyrogram import filters as fil -from pyrogram.types import Message -from telegram import ChatMemberAdministrator, Update -from telegram.constants import ParseMode -from telegram.error import BadRequest, Forbidden, TelegramError -from telegram.ext import CommandHandler, ContextTypes, MessageHandler, filters -from telegram.helpers import escape_markdown - -import Database.sql.users_sql as sql -from Database.sql.users_sql import get_all_users -from Mikobot import DEV_USERS, LOGGER, OWNER_ID, app, dispatcher, function - -# <=======================================================================================================> - -USERS_GROUP = 4 -CHAT_GROUP = 5 -DEV_AND_MORE = DEV_USERS.append(int(OWNER_ID)) - - -# <================================================ FUNCTION =======================================================> -# get_arg function to retrieve an argument from a message -def get_arg(message): - args = message.text.split(" ") - if len(args) > 1: - return args[1] - else: - return None - - -# Broadcast Function -@app.on_message(fil.command("gcast")) -async def broadcast_cmd(client: Client, message: Message): - user_id = message.from_user.id - texttt = message.text.split(" ") - - if user_id not in [OWNER_ID] + DEV_USERS: - await message.reply_text( - "You are not authorized to use this command. Only the owner and authorized users can use it." - ) - return - - if len(texttt) < 2: - return await message.reply_text( - "<b>GLOBALCASTING COMMANDS</b>\n-user : broadcasting all user's DM\n-group : broadcasting all groups\n-all : broadcasting both\nEx: /gcast -all" - ) - - if message.reply_to_message is None and not get_arg(message): - return await message.reply_text( - "<b>Please provide a message or reply to a message</b>" - ) - - tex = await message.reply_text("<code>Starting global broadcast...</code>") - - usersss = 0 - chatttt = 0 - uerror = 0 - cerror = 0 - chats = sql.get_all_chats() or [] - users = get_all_users() - - if "-all" in texttt: - texttt.append("-user") - texttt.append("-group") - - if "-user" in texttt: - for chat in users: - if message.reply_to_message: - msg = message.reply_to_message - else: - msg = get_arg(message) - try: - if message.reply_to_message: - aa = await msg.copy(chat.user_id) - else: - aa = await client.send_message(chat.user_id, msg) - - usersss += 1 - await asyncio.sleep(0.3) - except Exception: - uerror += 1 - await asyncio.sleep(0.3) - if "-group" in texttt: - for chat in chats: - if message.reply_to_message: - msg = message.reply_to_message - else: - msg = get_arg(message) - try: - if message.reply_to_message: - aa = await msg.copy(chat.chat_id) - else: - aa = await client.send_message(chat.chat_id, msg) - - chatttt += 1 - await asyncio.sleep(0.3) - except Exception: - cerror += 1 - await asyncio.sleep(0.3) - - await tex.edit_text( - f"<b>Message Successfully Sent</b> \nTotal Users: <code>{usersss}</code> \nFailed Users: <code>{uerror}</code> \nTotal GroupChats: <code>{chatttt}</code> \nFailed GroupChats: <code>{cerror}</code>" - ) - - -async def get_user_id(username: str) -> Union[int, None]: - # ensure valid user ID - if len(username) <= 5: - return None - - if username.startswith("@"): - username = username[1:] - - users = sql.get_userid_by_name(username) - - if not users: - return None - - elif len(users) == 1: - return users[0].user_id - - else: - for user_obj in users: - try: - userdat = await dispatcher.bot.get_chat(user_obj.user_id) - if userdat.username == username: - return userdat.id - - except BadRequest as excp: - if excp.message == "Chat not found": - pass - else: - LOGGER.exception("Error extracting user ID") - - return None - - -async def broadcast(update: Update, context: ContextTypes.DEFAULT_TYPE): - to_send = update.effective_message.text.split(None, 1) - - if len(to_send) >= 2: - to_group = False - to_user = False - if to_send[0] == "/broadcastgroups": - to_group = True - if to_send[0] == "/broadcastusers": - to_user = True - else: - to_group = to_user = True - chats = sql.get_all_chats() or [] - users = get_all_users() - failed = 0 - failed_user = 0 - if to_group: - for chat in chats: - try: - await context.bot.sendMessage( - int(chat.chat_id), - escape_markdown(to_send[1], 2), - parse_mode=ParseMode.MARKDOWN_V2, - disable_web_page_preview=True, - ) - await asyncio.sleep(1) - except TelegramError: - failed += 1 - if to_user: - for user in users: - try: - await context.bot.sendMessage( - int(user.user_id), - escape_markdown(to_send[1], 2), - parse_mode=ParseMode.MARKDOWN_V2, - disable_web_page_preview=True, - ) - await asyncio.sleep(1) - except TelegramError: - failed_user += 1 - await update.effective_message.reply_text( - f"Broadcast complete.\nGroups failed: {failed}.\nUsers failed: {failed_user}.", - ) - - -async def log_user(update: Update, context: ContextTypes.DEFAULT_TYPE): - chat = update.effective_chat - msg = update.effective_message - - sql.update_user(msg.from_user.id, msg.from_user.username, chat.id, chat.title) - - if msg.reply_to_message: - sql.update_user( - msg.reply_to_message.from_user.id, - msg.reply_to_message.from_user.username, - chat.id, - chat.title, - ) - - if msg.forward_from: - sql.update_user(msg.forward_from.id, msg.forward_from.username) - - -async def chats(update: Update, context: ContextTypes.DEFAULT_TYPE): - all_chats = sql.get_all_chats() or [] - chatfile = "List of chats.\n0. Chat Name | Chat ID | Members Count\n" - P = 1 - for chat in all_chats: - try: - curr_chat = await context.bot.getChat(chat.chat_id) - await curr_chat.get_member(context.bot.id) - chat_members = await curr_chat.get_member_count(context.bot.id) - chatfile += "{}. {} | {} | {}\n".format( - P, - chat.chat_name, - chat.chat_id, - chat_members, - ) - P = P + 1 - except: - pass - - with BytesIO(str.encode(chatfile)) as output: - output.name = "groups_list.txt" - await update.effective_message.reply_document( - document=output, - filename="groups_list.txt", - caption="Here be the list of groups in my database.", - ) - - -async def chat_checker(update: Update, context: ContextTypes.DEFAULT_TYPE): - bot = context.bot - try: - bot_admin = await update.effective_message.chat.get_member(bot.id) - if isinstance(bot_admin, ChatMemberAdministrator): - if bot_admin.can_post_messages is False: - await bot.leaveChat(update.effective_message.chat.id) - except Forbidden: - pass - - -def __user_info__(user_id): - if user_id in [777000, 1087968824]: - return """Groups Count: ???""" - if user_id == dispatcher.bot.id: - return """Groups Count: ???""" - num_chats = sql.get_user_num_chats(user_id) - return f"""Groups Count: {num_chats}""" - - -def __stats__(): - return f"• {sql.num_users()} users, across {sql.num_chats()} chats" - - -def __migrate__(old_chat_id, new_chat_id): - sql.migrate_chat(old_chat_id, new_chat_id) - - -# <================================================ HANDLER =======================================================> -BROADCAST_HANDLER = CommandHandler( - ["broadcastall", "broadcastusers", "broadcastgroups"], broadcast, block=False -) -USER_HANDLER = MessageHandler( - filters.ALL & filters.ChatType.GROUPS, log_user, block=False -) -CHAT_CHECKER_HANDLER = MessageHandler( - filters.ALL & filters.ChatType.GROUPS, chat_checker, block=False -) -CHATLIST_HANDLER = CommandHandler("groups", chats, block=False) - -function(USER_HANDLER, USERS_GROUP) -function(BROADCAST_HANDLER) -function(CHATLIST_HANDLER) -function(CHAT_CHECKER_HANDLER, CHAT_GROUP) - -__mod_name__ = "USERS" -__handlers__ = [(USER_HANDLER, USERS_GROUP), BROADCAST_HANDLER, CHATLIST_HANDLER] -# <================================================ END =======================================================> diff --git a/Mikobot/plugins/warns.py b/Mikobot/plugins/warns.py deleted file mode 100644 index f5517f4a6b9f999021a518ddfd9178921b1e071a..0000000000000000000000000000000000000000 --- a/Mikobot/plugins/warns.py +++ /dev/null @@ -1,576 +0,0 @@ -# <============================================== IMPORTS =========================================================> -import html -import re -from typing import Optional - -from telegram import ( - CallbackQuery, - Chat, - ChatMemberAdministrator, - ChatMemberOwner, - InlineKeyboardButton, - InlineKeyboardMarkup, - Message, - Update, - User, -) -from telegram.constants import MessageLimit, ParseMode -from telegram.error import BadRequest -from telegram.ext import ( - ApplicationHandlerStop, - CallbackQueryHandler, - CommandHandler, - ContextTypes, - MessageHandler, - filters, -) -from telegram.helpers import mention_html - -from Database.sql import warns_sql as sql -from Database.sql.approve_sql import is_approved -from Mikobot import dispatcher, function -from Mikobot.utils.can_restrict import BAN_STICKER -from Mikobot.plugins.disable import DisableAbleCommandHandler -from Mikobot.plugins.helper_funcs.chat_status import check_admin, is_user_admin -from Mikobot.plugins.helper_funcs.extraction import ( - extract_text, - extract_user, - extract_user_and_text, -) -from Mikobot.plugins.helper_funcs.misc import split_message -from Mikobot.plugins.helper_funcs.string_handling import split_quotes -from Mikobot.plugins.log_channel import loggable - -# <=======================================================================================================> - -WARN_HANDLER_GROUP = 9 -CURRENT_WARNING_FILTER_STRING = "<b>Current warning filters in this chat:</b>\n" - - -# <================================================ FUNCTION =======================================================> -# Not async -async def warn( - user: User, - chat: Chat, - reason: str, - message: Message, - warner: User = None, -) -> str: - if await is_user_admin(chat, user.id): - await message.reply_text("Damn admins, They are too far to be Warned") - return - - if warner: - warner_tag = mention_html(warner.id, warner.first_name) - else: - warner_tag = "Automated warn filter." - - limit, soft_warn = sql.get_warn_setting(chat.id) - num_warns, reasons = sql.warn_user(user.id, chat.id, reason) - if num_warns >= limit: - sql.reset_warns(user.id, chat.id) - if soft_warn: # punch - chat.unban_member(user.id) - reply = ( - f"<code>❕</code><b>Kick Event</b>\n" - f"<code> </code><b>• User:</b> {mention_html(user.id, user.first_name)}\n" - f"<code> </code><b>• Count:</b> {limit}" - ) - - else: # ban - await chat.ban_member(user.id) - reply = ( - f"<code>❕</code><b>Ban Event</b>\n" - f"<code> </code><b>• User:</b> {mention_html(user.id, user.first_name)}\n" - f"<code> </code><b>• Count:</b> {limit}" - ) - - for warn_reason in reasons: - reply += f"\n - {html.escape(warn_reason)}" - - await message.reply_sticker(BAN_STICKER) # Saitama's sticker - keyboard = None - log_reason = ( - f"<b>{html.escape(chat.title)}:</b>\n" - f"#WARN_BAN\n" - f"<b>Admin:</b> {warner_tag}\n" - f"<b>User:</b> {mention_html(user.id, user.first_name)}\n" - f"<b>Reason:</b> {reason}\n" - f"<b>Counts:</b> <code>{num_warns}/{limit}</code>" - ) - - else: - keyboard = InlineKeyboardMarkup( - [ - [ - InlineKeyboardButton( - "🔘 Remove warn", - callback_data="rm_warn({})".format(user.id), - ), - ], - ], - ) - - reply = ( - f"<code>❕</code><b>Warn Event</b>\n" - f"<code> </code><b>• User:</b> {mention_html(user.id, user.first_name)}\n" - f"<code> </code><b>• Count:</b> {num_warns}/{limit}" - ) - if reason: - reply += f"\n<code> </code><b>• Reason:</b> {html.escape(reason)}" - - log_reason = ( - f"<b>{html.escape(chat.title)}:</b>\n" - f"#WARN\n" - f"<b>Admin:</b> {warner_tag}\n" - f"<b>User:</b> {mention_html(user.id, user.first_name)}\n" - f"<b>Reason:</b> {reason}\n" - f"<b>Counts:</b> <code>{num_warns}/{limit}</code>" - ) - - try: - await message.reply_text( - reply, reply_markup=keyboard, parse_mode=ParseMode.HTML - ) - except BadRequest as excp: - if excp.message == "Reply message not found": - # Do not reply - await message.reply_text( - reply, - reply_markup=keyboard, - parse_mode=ParseMode.HTML, - quote=False, - ) - else: - raise - return log_reason - - -@loggable -async def button(update: Update, context: ContextTypes.DEFAULT_TYPE) -> str: - query: Optional[CallbackQuery] = update.callback_query - user: Optional[User] = update.effective_user - match = re.match(r"rm_warn\((.+?)\)", query.data) - if match: - user_id = match.group(1) - chat: Optional[Chat] = update.effective_chat - chat_member = await chat.get_member(user.id) - if isinstance(chat_member, (ChatMemberAdministrator, ChatMemberOwner)): - pass - else: - await query.answer("You need to be admin to do this!") - return - res = sql.remove_warn(user_id, chat.id) - if res: - await update.effective_message.edit_text( - "Warn removed by {}.".format(mention_html(user.id, user.first_name)), - parse_mode=ParseMode.HTML, - ) - user_member = await chat.get_member(user_id) - return ( - f"<b>{html.escape(chat.title)}:</b>\n" - f"#UNWARN\n" - f"<b>Admin:</b> {mention_html(user.id, user.first_name)}\n" - f"<b>User:</b> {mention_html(user_member.user.id, user_member.user.first_name)}" - ) - else: - await update.effective_message.edit_text( - "User already has no warns.", - parse_mode=ParseMode.HTML, - ) - - return "" - - -@loggable -@check_admin(permission="can_restrict_members", is_both=True) -async def warn_user(update: Update, context: ContextTypes.DEFAULT_TYPE) -> str: - args = context.args - message: Optional[Message] = update.effective_message - chat: Optional[Chat] = update.effective_chat - warner: Optional[User] = update.effective_user - - user_id, reason = await extract_user_and_text(message, context, args) - if ( - message.text.startswith("/d") - and message.reply_to_message - and not message.reply_to_message.forum_topic_created - ): - await message.reply_to_message.delete() - if user_id: - if ( - message.reply_to_message - and message.reply_to_message.from_user.id == user_id - ): - return await warn( - message.reply_to_message.from_user, - chat, - reason, - message.reply_to_message, - warner, - ) - else: - member = await chat.get_member(user_id) - return await warn(member.user, chat, reason, message, warner) - else: - await message.reply_text("That looks like an invalid User ID to me.") - return "" - - -@loggable -@check_admin(is_both=True) -async def reset_warns(update: Update, context: ContextTypes.DEFAULT_TYPE) -> str: - args = context.args - message: Optional[Message] = update.effective_message - chat: Optional[Chat] = update.effective_chat - user: Optional[User] = update.effective_user - - user_id = await extract_user(message, context, args) - - if user_id: - sql.reset_warns(user_id, chat.id) - await message.reply_text("Warns have been reset!") - warned = await chat.get_member(user_id).user - return ( - f"<b>{html.escape(chat.title)}:</b>\n" - f"#RESETWARNS\n" - f"<b>Admin:</b> {mention_html(user.id, user.first_name)}\n" - f"<b>User:</b> {mention_html(warned.id, warned.first_name)}" - ) - else: - await message.reply_text("No user has been designated!") - return "" - - -async def warns(update: Update, context: ContextTypes.DEFAULT_TYPE): - args = context.args - message: Optional[Message] = update.effective_message - chat: Optional[Chat] = update.effective_chat - user_id = await extract_user(message, context, args) or update.effective_user.id - result = sql.get_warns(user_id, chat.id) - - if result and result[0] != 0: - num_warns, reasons = result - limit, soft_warn = sql.get_warn_setting(chat.id) - - if reasons: - text = ( - f"This user has {num_warns}/{limit} warns, for the following reasons:" - ) - for reason in reasons: - text += f"\n • {reason}" - - msgs = split_message(text) - for msg in msgs: - await update.effective_message.reply_text(msg) - else: - await update.effective_message.reply_text( - f"User has {num_warns}/{limit} warns, but no reasons for any of them.", - ) - else: - await update.effective_message.reply_text("This user doesn't have any warns!") - - -# Dispatcher handler stop - do not async -@check_admin(is_user=True) -async def add_warn_filter(update: Update, context: ContextTypes.DEFAULT_TYPE): - chat: Optional[Chat] = update.effective_chat - msg: Optional[Message] = update.effective_message - - args = msg.text.split( - None, - 1, - ) # use python's maxsplit to separate Cmd, keyword, and reply_text - - if len(args) < 2: - return - - extracted = split_quotes(args[1]) - - if len(extracted) >= 2: - # set trigger -> lower, so as to avoid adding duplicate filters with different cases - keyword = extracted[0].lower() - content = extracted[1] - - else: - return - - # Note: perhaps handlers can be removed somehow using sql.get_chat_filters - for handler in dispatcher.handlers.get(WARN_HANDLER_GROUP, []): - if handler.filters == (keyword, chat.id): - dispatcher.remove_handler(handler, WARN_HANDLER_GROUP) - - sql.add_warn_filter(chat.id, keyword, content) - - await update.effective_message.reply_text(f"Warn handler added for '{keyword}'!") - raise ApplicationHandlerStop - - -@check_admin(is_user=True) -async def remove_warn_filter(update: Update, context: ContextTypes.DEFAULT_TYPE): - chat: Optional[Chat] = update.effective_chat - msg: Optional[Message] = update.effective_message - - args = msg.text.split( - None, - 1, - ) # use python's maxsplit to separate Cmd, keyword, and reply_text - - if len(args) < 2: - return - - extracted = split_quotes(args[1]) - - if len(extracted) < 1: - return - - to_remove = extracted[0] - - chat_filters = sql.get_chat_warn_triggers(chat.id) - - if not chat_filters: - await msg.reply_text("No warning filters are active here!") - return - - for filt in chat_filters: - if filt == to_remove: - sql.remove_warn_filter(chat.id, to_remove) - await msg.reply_text("Okay, I'll stop warning people for that.") - raise ApplicationHandlerStop - - await msg.reply_text( - "That's not a current warning filter - run /warnlist for all active warning filters.", - ) - - -async def list_warn_filters(update: Update, context: ContextTypes.DEFAULT_TYPE): - chat: Optional[Chat] = update.effective_chat - all_handlers = sql.get_chat_warn_triggers(chat.id) - - if not all_handlers: - await update.effective_message.reply_text("No warning filters are active here!") - return - - filter_list = CURRENT_WARNING_FILTER_STRING - for keyword in all_handlers: - entry = f" - {html.escape(keyword)}\n" - if len(entry) + len(filter_list) > MessageLimit.MAX_TEXT_LENGTH: - await update.effective_message.reply_text( - filter_list, parse_mode=ParseMode.HTML - ) - filter_list = entry - else: - filter_list += entry - - if filter_list != CURRENT_WARNING_FILTER_STRING: - await update.effective_message.reply_text( - filter_list, parse_mode=ParseMode.HTML - ) - - -@loggable -async def reply_filter(update: Update, context: ContextTypes.DEFAULT_TYPE) -> str: - chat: Optional[Chat] = update.effective_chat - message: Optional[Message] = update.effective_message - user: Optional[User] = update.effective_user - - if not user: # Ignore channel - return - - if user.id == 777000: - return - if is_approved(chat.id, user.id): - return - chat_warn_filters = sql.get_chat_warn_triggers(chat.id) - to_match = await extract_text(message) - if not to_match: - return "" - - for keyword in chat_warn_filters: - pattern = r"( |^|[^\w])" + re.escape(keyword) + r"( |$|[^\w])" - if re.search(pattern, to_match, flags=re.IGNORECASE): - user: Optional[User] = update.effective_user - warn_filter = sql.get_warn_filter(chat.id, keyword) - return await warn(user, chat, warn_filter.reply, message) - return "" - - -@check_admin(is_user=True) -@loggable -async def set_warn_limit(update: Update, context: ContextTypes.DEFAULT_TYPE) -> str: - args = context.args - chat: Optional[Chat] = update.effective_chat - user: Optional[User] = update.effective_user - msg: Optional[Message] = update.effective_message - - if args: - if args[0].isdigit(): - if int(args[0]) < 3: - await msg.reply_text("The minimum warn limit is 3!") - else: - sql.set_warn_limit(chat.id, int(args[0])) - await msg.reply_text("Updated the warn limit to {}".format(args[0])) - return ( - f"<b>{html.escape(chat.title)}:</b>\n" - f"#SET_WARN_LIMIT\n" - f"<b>Admin:</b> {mention_html(user.id, user.first_name)}\n" - f"Set the warn limit to <code>{args[0]}</code>" - ) - else: - await msg.reply_text("Give me a number as an arg!") - else: - limit, soft_warn = sql.get_warn_setting(chat.id) - - await msg.reply_text("The current warn limit is {}".format(limit)) - return "" - - -@check_admin(is_user=True) -async def set_warn_strength(update: Update, context: ContextTypes.DEFAULT_TYPE): - args = context.args - chat: Optional[Chat] = update.effective_chat - user: Optional[User] = update.effective_user - msg: Optional[Message] = update.effective_message - - if args: - if args[0].lower() in ("on", "yes"): - sql.set_warn_strength(chat.id, False) - await msg.reply_text("Too many warns will now result in a Ban!") - return ( - f"<b>{html.escape(chat.title)}:</b>\n" - f"<b>Admin:</b> {mention_html(user.id, user.first_name)}\n" - f"Has enabled strong warns. Users will be seriously Kicked.(banned)" - ) - - elif args[0].lower() in ("off", "no"): - sql.set_warn_strength(chat.id, True) - await msg.reply_text( - "Too many warns will now result in a normal Kick! Users will be able to join again after.", - ) - return ( - f"<b>{html.escape(chat.title)}:</b>\n" - f"<b>Admin:</b> {mention_html(user.id, user.first_name)}\n" - f"Has disabled strong Kicks. I will use normal kick on users." - ) - - else: - await msg.reply_text("I only understand on/yes/no/off!") - else: - limit, soft_warn = sql.get_warn_setting(chat.id) - if soft_warn: - await msg.reply_text( - "Warns are currently set to *kick* users when they exceed the limits.", - parse_mode=ParseMode.MARKDOWN, - ) - else: - await msg.reply_text( - "Warns are currently set to *Ban* users when they exceed the limits.", - parse_mode=ParseMode.MARKDOWN, - ) - return "" - - -def __stats__(): - return ( - f"• {sql.num_warns()} overall warns, across {sql.num_warn_chats()} chats.\n" - f"• {sql.num_warn_filters()} warn filters, across {sql.num_warn_filter_chats()} chats." - ) - - -async def __import_data__(chat_id, data, message): - for user_id, count in data.get("warns", {}).items(): - for x in range(int(count)): - sql.warn_user(user_id, chat_id) - - -def __migrate__(old_chat_id, new_chat_id): - sql.migrate_chat(old_chat_id, new_chat_id) - - -def __chat_settings__(chat_id, user_id): - num_warn_filters = sql.num_warn_chat_filters(chat_id) - limit, soft_warn = sql.get_warn_setting(chat_id) - return ( - f"This chat has `{num_warn_filters}` warn filters. " - f"It takes `{limit}` warns before the user gets *{'kicked' if soft_warn else 'banned'}*." - ) - - -# <=================================================== HELP ====================================================> - - -__help__ = """ -» /warns <userhandle>: get a user's number, and reason, of warns. - -» /warnlist: list of all current warning filters - -➠ *Admins only:* - -» /warn <userhandle>: warn a user. After 3 warns, the user will be banned from the group. Can also be used as a reply. - -» /dwarn <userhandle>: warn a user and delete the message. After 3 warns, the user will be banned from the group. Can also be used as a reply. - -» /resetwarn <userhandle>: reset the warns for a user. Can also be used as a reply. - -» /addwarn <keyword> <reply message>: set a warning filter on a certain keyword. If you want your keyword to \ -be a sentence, encompass it with quotes, as such: `/addwarn "very angry" This is an angry user`. - -» /nowarn <keyword>: stop a warning filter - -» /warnlimit <num>: set the warning limit - -» /strongwarn <on/yes/off/no>: If set to on, exceeding the warn limit will result in a ban. Else, will just kick. -""" - -__mod_name__ = "WARN" - -# <================================================ HANDLER =======================================================> -WARN_HANDLER = CommandHandler( - ["warn", "dwarn"], warn_user, filters=filters.ChatType.GROUPS, block=False -) -RESET_WARN_HANDLER = CommandHandler( - ["resetwarn", "resetwarns"], - reset_warns, - filters=filters.ChatType.GROUPS, - block=False, -) -CALLBACK_QUERY_HANDLER = CallbackQueryHandler(button, pattern=r"rm_warn", block=False) -MYWARNS_HANDLER = DisableAbleCommandHandler( - "warns", warns, filters=filters.ChatType.GROUPS, block=False -) -ADD_WARN_HANDLER = CommandHandler( - "addwarn", add_warn_filter, filters=filters.ChatType.GROUPS -) -RM_WARN_HANDLER = CommandHandler( - ["nowarn", "stopwarn"], - remove_warn_filter, - filters=filters.ChatType.GROUPS, -) -LIST_WARN_HANDLER = DisableAbleCommandHandler( - ["warnlist", "warnfilters"], - list_warn_filters, - filters=filters.ChatType.GROUPS, - admin_ok=True, - block=False, -) -WARN_FILTER_HANDLER = MessageHandler( - filters.TEXT & filters.ChatType.GROUPS, reply_filter, block=False -) -WARN_LIMIT_HANDLER = CommandHandler( - "warnlimit", set_warn_limit, filters=filters.ChatType.GROUPS, block=False -) -WARN_STRENGTH_HANDLER = CommandHandler( - "strongwarn", set_warn_strength, filters=filters.ChatType.GROUPS, block=False -) - -function(WARN_HANDLER) -function(CALLBACK_QUERY_HANDLER) -function(RESET_WARN_HANDLER) -function(MYWARNS_HANDLER) -function(ADD_WARN_HANDLER) -function(RM_WARN_HANDLER) -function(LIST_WARN_HANDLER) -function(WARN_LIMIT_HANDLER) -function(WARN_STRENGTH_HANDLER) -function(WARN_FILTER_HANDLER, WARN_HANDLER_GROUP) -# <================================================ END =======================================================> diff --git a/Mikobot/plugins/welcome.py b/Mikobot/plugins/welcome.py deleted file mode 100644 index ca8ad3db291113f0ab23eb089632087729bcda13..0000000000000000000000000000000000000000 --- a/Mikobot/plugins/welcome.py +++ /dev/null @@ -1,1339 +0,0 @@ -# <============================================== IMPORTS =========================================================> -import html -import os -import random -import re -import textwrap -import time -from contextlib import suppress -from datetime import datetime -from functools import partial - -import unidecode -from PIL import Image, ImageChops, ImageDraw, ImageFont -from pyrogram import filters as ft -from pyrogram.types import ChatMemberUpdated, Message -from telegram import ChatPermissions, InlineKeyboardButton, InlineKeyboardMarkup, Update -from telegram.constants import ParseMode -from telegram.error import BadRequest -from telegram.ext import ( - CallbackQueryHandler, - CommandHandler, - ContextTypes, - MessageHandler, - filters, -) -from telegram.helpers import escape_markdown, mention_html, mention_markdown - -import Database.sql.welcome_sql as sql -from Database.mongodb.toggle_mongo import dwelcome_off, dwelcome_on, is_dwelcome_on -from Database.sql.global_bans_sql import is_user_gbanned -from Infamous.temp import temp -from Mikobot import DEV_USERS -from Mikobot import DEV_USERS as SUDO -from Mikobot import DRAGONS, EVENT_LOGS, LOGGER, OWNER_ID, app, dispatcher, function -from Mikobot.plugins.helper_funcs.chat_status import check_admin, is_user_ban_protected -from Mikobot.plugins.helper_funcs.misc import build_keyboard, revert_buttons -from Mikobot.plugins.helper_funcs.msg_types import get_welcome_type -from Mikobot.plugins.helper_funcs.string_handling import escape_invalid_curly_brackets -from Mikobot.plugins.log_channel import loggable -from Mikobot.utils.can_restrict import can_restrict - -# <=======================================================================================================> - -VALID_WELCOME_FORMATTERS = [ - "first", - "last", - "fullname", - "username", - "id", - "count", - "chatname", - "mention", -] - -ENUM_FUNC_MAP = { - sql.Types.TEXT.value: dispatcher.bot.send_message, - sql.Types.BUTTON_TEXT.value: dispatcher.bot.send_message, - sql.Types.STICKER.value: dispatcher.bot.send_sticker, - sql.Types.DOCUMENT.value: dispatcher.bot.send_document, - sql.Types.PHOTO.value: dispatcher.bot.send_photo, - sql.Types.AUDIO.value: dispatcher.bot.send_audio, - sql.Types.VOICE.value: dispatcher.bot.send_voice, - sql.Types.VIDEO.value: dispatcher.bot.send_video, -} - -VERIFIED_USER_WAITLIST = {} - - -# <================================================ TEMPLATE WELCOME FUNCTION =======================================================> -async def circle(pfp, size=(259, 259)): - pfp = pfp.resize(size, Image.ANTIALIAS).convert("RGBA") - bigsize = (pfp.size[0] * 3, pfp.size[1] * 3) - mask = Image.new("L", bigsize, 0) - draw = ImageDraw.Draw(mask) - draw.ellipse((0, 0) + bigsize, fill=255) - mask = mask.resize(pfp.size, Image.ANTIALIAS) - mask = ImageChops.darker(mask, pfp.split()[-1]) - pfp.putalpha(mask) - return pfp - - -async def draw_multiple_line_text(image, text, font, text_start_height): - draw = ImageDraw.Draw(image) - image_width, image_height = image.size - y_text = text_start_height - lines = textwrap.wrap(text, width=50) - for line in lines: - line_width, line_height = font.getsize(line) - draw.text( - ((image_width - line_width) // 2, y_text), line, font=font, fill="black" - ) - y_text += line_height - - -async def welcomepic(pic, user, chat, user_id): - user = unidecode.unidecode(user) - background = Image.open("Extra/bgg.jpg") - background = background.resize( - (background.size[0], background.size[1]), Image.ANTIALIAS - ) - pfp = Image.open(pic).convert("RGBA") - pfp = await circle(pfp, size=(259, 259)) - pfp_x = 55 - pfp_y = (background.size[1] - pfp.size[1]) // 2 + 38 - draw = ImageDraw.Draw(background) - font = ImageFont.truetype("Extra/Calistoga-Regular.ttf", 42) - text_width, text_height = draw.textsize(f"{user} [{user_id}]", font=font) - text_x = 20 - text_y = background.height - text_height - 20 - 25 - draw.text((text_x, text_y), f"{user} [{user_id}]", font=font, fill="white") - background.paste(pfp, (pfp_x, pfp_y), pfp) - welcome_image_path = f"downloads/welcome_{user_id}.png" - background.save(welcome_image_path) - return welcome_image_path - - -@app.on_chat_member_updated(ft.group) -async def member_has_joined(client, member: ChatMemberUpdated): - if ( - not member.new_chat_member - or member.new_chat_member.status in {"banned", "left", "restricted"} - or member.old_chat_member - ): - return - user = member.new_chat_member.user if member.new_chat_member else member.from_user - if user.id in SUDO: - await client.send_message(member.chat.id, "**Global Admins Joined The Chat!**") - return - elif user.is_bot: - return - else: - chat_id = member.chat.id - welcome_enabled = await is_dwelcome_on(chat_id) - if not welcome_enabled: - return - if f"welcome-{chat_id}" in temp.MELCOW: - try: - await temp.MELCOW[f"welcome-{chat_id}"].delete() - except: - pass - mention = f"<a href='tg://user?id={user.id}'>{user.first_name}</a>" - joined_date = datetime.fromtimestamp(time.time()).strftime("%Y.%m. %d %H:%M:%S") - first_name = ( - f"{user.first_name} {user.last_name}" if user.last_name else user.first_name - ) - user_id = user.id - dc = user.dc_id - try: - pic = await client.download_media( - user.photo.big_file_id, file_name=f"pp{user_id}.png" - ) - except AttributeError: - pic = "Extra/profilepic.png" - try: - welcomeimg = await welcomepic( - pic, user.first_name, member.chat.title, user_id - ) - temp.MELCOW[f"welcome-{chat_id}"] = await client.send_photo( - member.chat.id, - photo=welcomeimg, - caption=f"**𝗛𝗲𝘆❗️{mention}, 𝗪𝗲𝗹𝗰𝗼𝗺𝗲 𝗧𝗼 {member.chat.title} 𝗚𝗿𝗼𝘂𝗽.**\n\n**➖➖➖➖➖➖➖➖➖➖➖➖**\n**𝗡𝗔𝗠𝗘 : {first_name}**\n**𝗜𝗗 : {user_id}**\n**𝗗𝗔𝗧𝗘 𝗝𝗢𝗜𝗡𝗘𝗗 : {joined_date}**", - ) - except Exception as e: - print(e) - try: - os.remove(f"downloads/welcome_{user_id}.png") - os.remove(f"downloads/pp{user_id}.png") - except Exception: - pass - - -@app.on_message(ft.command("dwelcome on")) -@can_restrict -async def enable_welcome(_, message: Message): - chat_id = message.chat.id - welcome_enabled = await is_dwelcome_on(chat_id) - if welcome_enabled: - await message.reply_text("Default welcome is already enabled") - return - await dwelcome_on(chat_id) - await message.reply_text("New default welcome message enabled for this chat.") - - -@app.on_message(ft.command("dwelcome off")) -@can_restrict -async def disable_welcome(_, message: Message): - chat_id = message.chat.id - welcome_enabled = await is_dwelcome_on(chat_id) - if not welcome_enabled: - await message.reply_text("Default welcome is already disabled") - return - await dwelcome_off(chat_id) - await message.reply_text("New default welcome disabled for this chat.") - - -# <=======================================================================================================> - - -# <================================================ NORMAL WELCOME FUNCTION =======================================================> -async def send(update: Update, message, keyboard, backup_message): - chat = update.effective_chat - cleanserv = sql.clean_service(chat.id) - reply = update.effective_message.message_id - if cleanserv: - try: - await dispatcher.bot.delete_message(chat.id, update.message.message_id) - except BadRequest: - pass - reply = False - try: - try: - msg = await dispatcher.bot.send_message( - chat.id, - message, - parse_mode=ParseMode.HTML, - reply_markup=keyboard, - ) - except: - msg = await update.effective_message.reply_text( - message, - parse_mode=ParseMode.HTML, - reply_markup=keyboard, - reply_to_message_id=reply, - ) - except BadRequest as excp: - if excp.message == "Reply message not found": - msg = await update.effective_message.reply_text( - message, - parse_mode=ParseMode.MARKDOWN, - reply_markup=keyboard, - quote=False, - ) - elif excp.message == "Button_url_invalid": - try: - msg = await dispatcher.bot.send_message( - chat.id, - backup_message - + "\nNote: The current message has an invalid URL in one of its buttons. Please update.", - parse_mode=ParseMode.MARKDOWN, - ) - except: - msg = await update.effective_message.reply_text( - backup_message - + "\nNote: The current message has an invalid URL in one of its buttons. Please update.", - parse_mode=ParseMode.MARKDOWN, - reply_to_message_id=reply, - ) - elif excp.message == "Unsupported URL protocol": - try: - msg = await dispatcher.bot.send_message( - chat.id, - backup_message - + "\nNote: The current message has buttons which use URL protocols that are unsupported by Telegram. Please update.", - parse_mode=ParseMode.MARKDOWN, - ) - except: - msg = await update.effective_message.reply_text( - backup_message - + "\nNote: The current message has buttons which use URL protocols that are unsupported by Telegram. Please update.", - parse_mode=ParseMode.MARKDOWN, - reply_to_message_id=reply, - ) - elif excp.message == "Wrong URL host": - try: - msg = await dispatcher.bot.send_message( - chat.id, - backup_message - + "\nNote: The current message has some bad URLs. Please update.", - parse_mode=ParseMode.MARKDOWN, - ) - except: - msg = await update.effective_message.reply_text( - backup_message - + "\nNote: The current message has some bad URLs. Please update.", - parse_mode=ParseMode.MARKDOWN, - reply_to_message_id=reply, - ) - LOGGER.warning(message) - LOGGER.warning(keyboard) - LOGGER.exception("Could not parse! Got invalid URL host errors") - elif excp.message == "Have no rights to send a message": - return - else: - try: - msg = await dispatcher.bot.send_message( - chat.id, - backup_message - + "\nNote: An error occurred when sending the custom message. Please update.", - parse_mode=ParseMode.MARKDOWN, - ) - except: - msg = await update.effective_message.reply_text( - backup_message - + "\nNote: An error occurred when sending the custom message. Please update.", - parse_mode=ParseMode.MARKDOWN, - reply_to_message_id=reply, - ) - LOGGER.exception() - return msg - - -@loggable -async def new_member(update: Update, context: ContextTypes.DEFAULT_TYPE): - bot, job_queue = context.bot, context.job_queue - chat = update.effective_chat - user = update.effective_user - msg = update.effective_message - - should_welc, cust_welcome, cust_content, welc_type = sql.get_welc_pref(chat.id) - welc_mutes = sql.welcome_mutes(chat.id) - human_checks = sql.get_human_checks(user.id, chat.id) - - new_members = update.effective_message.new_chat_members - - for new_mem in new_members: - if new_mem.id == bot.id and not Mikobot.ALLOW_CHATS: - with suppress(BadRequest): - await update.effective_message.reply_text( - "Groups are disabled for {}, I'm outta here.".format(bot.first_name) - ) - await bot.leave_chat(update.effective_chat.id) - return - - welcome_log = None - res = None - sent = None - should_mute = True - welcome_bool = True - media_wel = False - - if is_user_gbanned(new_mem.id): - return - - if should_welc: - reply = update.message.message_id - cleanserv = sql.clean_service(chat.id) - if cleanserv: - try: - await dispatcher.bot.delete_message( - chat.id, update.message.message_id - ) - except BadRequest: - pass - reply = False - - if new_mem.id == OWNER_ID: - await update.effective_message.reply_text( - "Oh, darling, I have searched for you everywhere.", - reply_to_message_id=reply, - ) - welcome_log = ( - "{}\n" - "#USER_JOINED\n" - "Bot owner just joined the group".format(html.escape(chat.title)) - ) - continue - - elif new_mem.id in DEV_USERS: - await update.effective_message.reply_text( - "Be cool! A member of the team just joined.", - reply_to_message_id=reply, - ) - welcome_log = ( - "{}\n" - "#USER_JOINED\n" - "Bot dev just joined the group".format(html.escape(chat.title)) - ) - continue - - elif new_mem.id in DRAGONS: - await update.effective_message.reply_text( - "Whoa! A dragon disaster just joined! Stay alert!", - reply_to_message_id=reply, - ) - welcome_log = ( - "{}\n" - "#USER_JOINED\n" - "Bot sudo just joined the group".format(html.escape(chat.title)) - ) - continue - - elif new_mem.id == bot.id: - creator = None - for x in await bot.get_chat_administrators(update.effective_chat.id): - if x.status == "creator": - creator = x.user - break - if creator: - reply = """#NEWGROUP \ - \nID: `{}` \ - """.format( - chat.id - ) - - if chat.title: - reply += "\nGroup name: **{}**".format( - escape_markdown(chat.title) - ) - - if chat.username: - reply += "\nUsername: @{}".format( - escape_markdown(chat.username) - ) - - reply += "\nCreator ID: `{}`".format(creator.id) - - if creator.username: - reply += "\nCreator Username: @{}".format(creator.username) - - await bot.send_message( - EVENT_LOGS, - reply, - parse_mode="markdown", - ) - else: - await bot.send_message( - EVENT_LOGS, - "#NEW_GROUP\n<b>Group name:</b> {}\n<b>ID:</b> <code>{}</code>".format( - html.escape(chat.title), - chat.id, - ), - parse_mode=ParseMode.HTML, - ) - await update.effective_message.reply_text( - "I feel like I'm gonna suffocate in here.", - reply_to_message_id=reply, - ) - continue - - else: - buttons = sql.get_welc_buttons(chat.id) - keyb = build_keyboard(buttons) - - if welc_type not in (sql.Types.TEXT, sql.Types.BUTTON_TEXT): - media_wel = True - - first_name = new_mem.first_name or "PersonWithNoName" - - if cust_welcome: - if cust_welcome == sql.DEFAULT_WELCOME: - cust_welcome = random.choice( - sql.DEFAULT_WELCOME_MESSAGES, - ).format(first=escape_markdown(first_name)) - - if new_mem.last_name: - fullname = escape_markdown( - "{} {}".format(first_name, new_mem.last_name) - ) - else: - fullname = escape_markdown(first_name) - count = await chat.get_member_count() - mention = mention_markdown(new_mem.id, escape_markdown(first_name)) - if new_mem.username: - username = "@{}".format(escape_markdown(new_mem.username)) - else: - username = mention - - valid_format = escape_invalid_curly_brackets( - cust_welcome, - VALID_WELCOME_FORMATTERS, - ) - res = valid_format.format( - first=escape_markdown(first_name), - last=escape_markdown(new_mem.last_name or first_name), - fullname=escape_markdown(fullname), - username=username, - mention=mention, - count=count, - chatname=escape_markdown(chat.title), - id=new_mem.id, - ) - - else: - res = random.choice(sql.DEFAULT_WELCOME_MESSAGES).format( - first=escape_markdown(first_name), - ) - keyb = [] - - backup_message = random.choice(sql.DEFAULT_WELCOME_MESSAGES).format( - first=escape_markdown(first_name), - ) - keyboard = InlineKeyboardMarkup(keyb) - - else: - welcome_bool = False - res = None - keyboard = None - backup_message = None - reply = None - - if ( - await is_user_ban_protected( - chat, new_mem.id, await chat.get_member(new_mem.id) - ) - or human_checks - ): - should_mute = False - if new_mem.is_bot: - should_mute = False - - if user.id == new_mem.id: - if should_mute: - if welc_mutes == "soft": - await bot.restrict_chat_member( - chat.id, - new_mem.id, - permissions=ChatPermissions( - can_send_messages=True, - can_send_media_messages=False, - can_send_other_messages=False, - can_invite_users=False, - can_pin_messages=False, - can_send_polls=False, - can_change_info=False, - can_add_web_page_previews=False, - can_manage_topics=False, - ), - until_date=(int(time.time() + 24 * 60 * 60)), - ) - if welc_mutes == "strong": - welcome_bool = False - if not media_wel: - VERIFIED_USER_WAITLIST.update( - { - new_mem.id: { - "should_welc": should_welc, - "media_wel": False, - "status": False, - "update": update, - "res": res, - "keyboard": keyboard, - "backup_message": backup_message, - }, - }, - ) - else: - VERIFIED_USER_WAITLIST.update( - { - new_mem.id: { - "should_welc": should_welc, - "chat_id": chat.id, - "status": False, - "media_wel": True, - "cust_content": cust_content, - "welc_type": welc_type, - "res": res, - "keyboard": keyboard, - }, - }, - ) - new_join_mem = '<a href="tg://user?id={}">{}</a>'.format( - user.id, html.escape(new_mem.first_name) - ) - message = await msg.reply_text( - "{}\nYou have 120 seconds to prove you're human.".format( - new_join_mem - ), - reply_markup=InlineKeyboardMarkup( - [ - [ - InlineKeyboardButton( - text="YES, I'M HUMAN", - callback_data="user_join_({})".format( - new_mem.id - ), - ), - ], - ], - ), - parse_mode=ParseMode.HTML, - reply_to_message_id=reply, - ) - await bot.restrict_chat_member( - chat.id, - new_mem.id, - permissions=ChatPermissions( - can_send_messages=False, - can_invite_users=False, - can_pin_messages=False, - can_send_polls=False, - can_change_info=False, - can_send_media_messages=False, - can_send_other_messages=False, - can_add_web_page_previews=False, - can_manage_topics=False, - ), - ) - job_queue.run_once( - partial(check_not_bot, new_mem, chat.id, message.message_id), - 120, - name="welcomemute", - ) - - if welcome_bool: - if media_wel: - sent = await ENUM_FUNC_MAP[welc_type]( - chat.id, - cust_content, - caption=res, - reply_markup=keyboard, - reply_to_message_id=reply, - parse_mode="markdown", - ) - else: - sent = await send(update, res, keyboard, backup_message) - prev_welc = sql.get_clean_pref(chat.id) - if prev_welc: - try: - await bot.delete_message(chat.id, prev_welc) - except BadRequest: - pass - - if sent: - sql.set_clean_welcome(chat.id, sent.message_id) - - if welcome_log: - return welcome_log - - if user.id == new_mem.id: - welcome_log = ( - "{}\n" - "#USER_JOINED\n" - "<b>User</b>: {}\n" - "<b>ID</b>: <code>{}</code>".format( - html.escape(chat.title), - mention_html(user.id, user.first_name), - user.id, - ) - ) - elif new_mem.is_bot and user.id != new_mem.id: - welcome_log = ( - "{}\n" - "#BOT_ADDED\n" - "<b>Bot</b>: {}\n" - "<b>ID</b>: <code>{}</code>".format( - html.escape(chat.title), - mention_html(new_mem.id, new_mem.first_name), - new_mem.id, - ) - ) - else: - welcome_log = ( - "{}\n" - "#USER_ADDED\n" - "<b>User</b>: {}\n" - "<b>ID</b>: <code>{}</code>".format( - html.escape(chat.title), - mention_html(new_mem.id, new_mem.first_name), - new_mem.id, - ) - ) - return welcome_log - - return "" - - -async def check_not_bot(member, chat_id, message_id, context): - bot = context.bot - member_dict = VERIFIED_USER_WAITLIST.pop(member.id) - member_status = member_dict.get("status") - if not member_status: - try: - await bot.unban_chat_member(chat_id, member.id) - except: - pass - - try: - await bot.edit_message_text( - "Kicks user\nThey can always rejoin and try.", - chat_id=chat_id, - message_id=message_id, - ) - except: - pass - - -async def left_member(update, context: ContextTypes.DEFAULT_TYPE): - bot = context.bot - chat = update.effective_chat - user = update.effective_user - should_goodbye, cust_goodbye, goodbye_type = sql.get_gdbye_pref(chat.id) - - if user.id == bot.id: - return - - if should_goodbye: - reply = update.message.message_id - cleanserv = sql.clean_service(chat.id) - if cleanserv: - try: - await dispatcher.bot.delete_message(chat.id, update.message.message_id) - except BadRequest: - pass - reply = False - - left_mem = update.effective_message.left_chat_member - if left_mem: - if is_user_gbanned(left_mem.id): - return - - if left_mem.id == bot.id: - return - - if left_mem.id == OWNER_ID: - await update.effective_message.reply_text( - "My master left..", - reply_to_message_id=reply, - ) - return - - elif left_mem.id in DEV_USERS: - await update.effective_message.reply_text( - "see you later pro!", - reply_to_message_id=reply, - ) - return - - if goodbye_type != sql.Types.TEXT and goodbye_type != sql.Types.BUTTON_TEXT: - await ENUM_FUNC_MAP[goodbye_type](chat.id, cust_goodbye) - return - - first_name = left_mem.first_name or "PersonWithNoName" - if cust_goodbye: - if cust_goodbye == sql.DEFAULT_GOODBYE: - cust_goodbye = random.choice(sql.DEFAULT_GOODBYE_MESSAGES).format( - first=first_name, - ) - if left_mem.last_name: - fullname = "{} {}".format(first_name, left_mem.last_name) - else: - fullname = first_name - count = await chat.get_member_count() - mention = mention_markdown(left_mem.id, first_name) - if left_mem.username: - username = "@{}".format(left_mem.username) - else: - username = mention - - valid_format = escape_invalid_curly_brackets( - cust_goodbye, - VALID_WELCOME_FORMATTERS, - ) - res = valid_format.format( - first=first_name, - last=left_mem.last_name or first_name, - fullname=fullname, - username=username, - mention=mention, - count=count, - chatname=chat.title, - id=left_mem.id, - ) - buttons = sql.get_gdbye_buttons(chat.id) - keyb = build_keyboard(buttons) - - else: - res = random.choice(sql.DEFAULT_GOODBYE_MESSAGES).format( - first=first_name, - ) - keyb = [] - - keyboard = InlineKeyboardMarkup(keyb) - - await send( - update, - res, - keyboard, - random.choice(sql.DEFAULT_GOODBYE_MESSAGES).format(first=first_name), - ) - - -@check_admin(is_user=True) -async def welcome(update, context: ContextTypes.DEFAULT_TYPE): - args = context.args - chat = update.effective_chat - if not args or args[0].lower() == "noformat": - noformat = True - pref, welcome_m, cust_content, welcome_type = sql.get_welc_pref(chat.id) - await update.effective_message.reply_text( - f"This chat has its welcome setting set to: `{pref}`.\n" - f"The welcome message (not filling the {{}}) is:", - parse_mode=ParseMode.MARKDOWN, - ) - - if welcome_type == sql.Types.BUTTON_TEXT or welcome_type == sql.Types.TEXT: - buttons = sql.get_welc_buttons(chat.id) - if noformat: - welcome_m += revert_buttons(buttons) - await update.effective_message.reply_text(welcome_m) - else: - keyb = build_keyboard(buttons) - keyboard = InlineKeyboardMarkup(keyb) - await send(update, welcome_m, keyboard, sql.DEFAULT_WELCOME) - else: - buttons = sql.get_welc_buttons(chat.id) - if noformat: - welcome_m += revert_buttons(buttons) - await ENUM_FUNC_MAP[welcome_type]( - chat.id, cust_content, caption=welcome_m - ) - else: - keyb = build_keyboard(buttons) - keyboard = InlineKeyboardMarkup(keyb) - ENUM_FUNC_MAP[welcome_type]( - chat.id, - cust_content, - caption=welcome_m, - reply_markup=keyboard, - parse_mode=ParseMode.MARKDOWN, - disable_web_page_preview=True, - ) - - elif len(args) >= 1: - if args[0].lower() in ("on", "yes"): - sql.set_welc_preference(str(chat.id), True) - await update.effective_message.reply_text( - "Okay! I'll greet members when they join.", - ) - - elif args[0].lower() in ("off", "no"): - sql.set_welc_preference(str(chat.id), False) - await update.effective_message.reply_text( - "I'll go loaf around and not welcome anyone then.", - ) - - else: - await update.effective_message.reply_text( - "I understand 'on/yes' or 'off/no' only!", - ) - - -@check_admin(is_user=True) -async def goodbye(update, context: ContextTypes.DEFAULT_TYPE): - args = context.args - chat = update.effective_chat - - if not args or args[0] == "noformat": - noformat = True - pref, goodbye_m, goodbye_type = sql.get_gdbye_pref(chat.id) - await update.effective_message.reply_text( - f"This chat has its goodbye setting set to: `{pref}`.\n" - f"The goodbye message (not filling the {{}}) is:", - parse_mode=ParseMode.MARKDOWN, - ) - - if goodbye_type == sql.Types.BUTTON_TEXT: - buttons = sql.get_gdbye_buttons(chat.id) - if noformat: - goodbye_m += revert_buttons(buttons) - await update.effective_message.reply_text(goodbye_m) - - else: - keyb = build_keyboard(buttons) - keyboard = InlineKeyboardMarkup(keyb) - await send(update, goodbye_m, keyboard, sql.DEFAULT_GOODBYE) - - else: - if noformat: - await ENUM_FUNC_MAP[goodbye_type](chat.id, goodbye_m) - - else: - await ENUM_FUNC_MAP[goodbye_type]( - chat.id, goodbye_m, parse_mode=ParseMode.MARKDOWN - ) - - elif len(args) >= 1: - if args[0].lower() in ("on", "yes"): - sql.set_gdbye_preference(str(chat.id), True) - await update.effective_message.reply_text("Okay its set to on!") - - elif args[0].lower() in ("off", "no"): - sql.set_gdbye_preference(str(chat.id), False) - await update.effective_message.reply_text("Okay its set to no!") - - else: - await update.effective_message.reply_text( - "I understand 'on/yes' or 'off/no' only!", - ) - - -@check_admin(is_user=True) -@loggable -async def set_welcome(update, context: ContextTypes.DEFAULT_TYPE): - chat = update.effective_chat - user = update.effective_user - msg = update.effective_message - - text, data_type, content, buttons = get_welcome_type(msg) - - if data_type is None: - await msg.reply_text("You didn't specify what to reply with!") - return "" - - sql.set_custom_welcome(chat.id, content, text, data_type, buttons) - await msg.reply_text("Successfully set custom welcome message!") - - return ( - f"<b>{html.escape(chat.title)}:</b>\n" - f"#SET_WELCOME\n" - f"<b>Admin:</b> {mention_html(user.id, user.first_name)}\n" - "Set the welcome message." - ) - - -@check_admin(is_user=True) -@loggable -async def reset_welcome(update, context: ContextTypes.DEFAULT_TYPE): - chat = update.effective_chat - user = update.effective_user - - sql.set_custom_welcome(chat.id, None, sql.DEFAULT_WELCOME, sql.Types.TEXT) - await update.effective_message.reply_text( - "Successfully reset welcome message to default!" - ) - - return ( - f"<b>{html.escape(chat.title)}:</b>\n" - f"#RESET_WELCOME\n" - f"<b>Admin:</b> {mention_html(user.id, user.first_name)}\n" - "Reset the welcome message to default." - ) - - -@check_admin(is_user=True) -@loggable -async def set_goodbye(update, context: ContextTypes.DEFAULT_TYPE): - chat = update.effective_chat - user = update.effective_user - msg = update.effective_message - text, data_type, content, buttons = get_welcome_type(msg) - - if data_type is None: - await msg.reply_text("You didn't specify what to reply with!") - return "" - - sql.set_custom_gdbye(chat.id, content or text, data_type, buttons) - await msg.reply_text("Successfully set custom goodbye message!") - return ( - f"<b>{html.escape(chat.title)}:</b>\n" - f"#SET_GOODBYE\n" - f"<b>Admin:</b> {mention_html(user.id, user.first_name)}\n" - "Set the goodbye message." - ) - - -@check_admin(is_user=True) -@loggable -async def reset_goodbye(update: Update, context: ContextTypes.DEFAULT_TYPE) -> str: - chat = update.effective_chat - user = update.effective_user - - sql.set_custom_gdbye(chat.id, sql.DEFAULT_GOODBYE, sql.Types.TEXT) - await update.effective_message.reply_text( - "Successfully reset goodbye message to default!", - ) - - return ( - f"<b>{html.escape(chat.title)}:</b>\n" - f"#RESET_GOODBYE\n" - f"<b>Admin:</b> {mention_html(user.id, user.first_name)}\n" - f"Reset the goodbye message." - ) - - -@check_admin(is_user=True) -@loggable -async def welcomemute(update: Update, context: ContextTypes.DEFAULT_TYPE) -> str: - args = context.args - chat = update.effective_chat - user = update.effective_user - msg = update.effective_message - - if len(args) >= 1: - if args[0].lower() in ("off", "no"): - sql.set_welcome_mutes(chat.id, False) - await msg.reply_text("I will no longer mute people on joining!") - return ( - f"<b>{html.escape(chat.title)}:</b>\n" - f"#WELCOME_MUTE\n" - f"<b>Admin:</b> {mention_html(user.id, user.first_name)}\n" - f"Has toggled welcome mute to <b>off</b>." - ) - elif args[0].lower() in ["soft"]: - sql.set_welcome_mutes(chat.id, "soft") - await msg.reply_text( - "I will restrict users permission to send media for 24 hours.", - ) - return ( - f"<b>{html.escape(chat.title)}:</b>\n" - f"#𝐖𝐄𝐋𝐂𝐎𝐌𝐄_𝐌𝐔𝐓𝐄\n" - f"<b>Admin:</b> {mention_html(user.id, user.first_name)}\n" - f"Has toggled welcome mute to <b>soft</b>." - ) - elif args[0].lower() in ["strong"]: - sql.set_welcome_mutes(chat.id, "strong") - await msg.reply_text( - "I will now mute people when they join until they prove they're not a bot. They will have 120 seconds before they get kicked.", - ) - return ( - f"<b>{html.escape(chat.title)}:</b>\n" - f"#𝐖𝐄𝐋𝐂𝐎𝐌𝐄_𝐌𝐔𝐓𝐄\n" - f"<b>Admin:</b> {mention_html(user.id, user.first_name)}\n" - f"Has toggled welcome mute to <b>strong</b>." - ) - else: - await msg.reply_text( - "Please enter <code>off</code>/<code>no</code>/<code>soft</code>/<code>strong</code>!", - parse_mode=ParseMode.HTML, - ) - return "" - else: - curr_setting = sql.welcome_mutes(chat.id) - reply = ( - "Give me a setting!\nChoose one out of: <code>off</code>/<code>no</code> or <code>soft</code> or <code>strong</code> only! \n" - f"Current setting: <code>{curr_setting}</code>" - ) - await msg.reply_text(reply, parse_mode=ParseMode.HTML) - return "" - - -@check_admin(is_user=True) -@loggable -async def clean_welcome(update: Update, context: ContextTypes.DEFAULT_TYPE) -> str: - args = context.args - chat = update.effective_chat - user = update.effective_user - - if not args: - clean_pref = sql.get_clean_pref(chat.id) - if clean_pref: - await update.effective_message.reply_text( - "I should be deleting welcome messages up to two days old.", - ) - else: - await update.effective_message.reply_text( - "I'm currently not deleting old welcome messages!", - ) - return "" - - if args[0].lower() in ("on", "yes"): - sql.set_clean_welcome(str(chat.id), True) - await update.effective_message.reply_text( - "I'll try to delete old welcome messages!" - ) - return ( - f"<b>{html.escape(chat.title)}:</b>\n" - f"#𝐂𝐋𝐄𝐀𝐍_𝐖𝐄𝐋𝐂𝐎𝐌𝐄\n" - f"<b>Admin:</b> {mention_html(user.id, user.first_name)}\n" - "Has toggled clean welcomes to <code>on</code>." - ) - elif args[0].lower() in ("off", "no"): - sql.set_clean_welcome(str(chat.id), False) - await update.effective_message.reply_text( - "I won't delete old welcome messages." - ) - return ( - f"<b>{html.escape(chat.title)}:</b>\n" - f"#𝐂𝐋𝐄𝐀𝐍_𝐖𝐄𝐋𝐂𝐎𝐌𝐄\n" - f"<b>Admin:</b> {mention_html(user.id, user.first_name)}\n" - "Has toggled clean welcomes to <code>off</code>." - ) - else: - await update.effective_message.reply_text( - "I understand 'on/yes' or 'off/no' only!", - ) - return "" - - -@check_admin(is_user=True) -async def cleanservice(update: Update, context: ContextTypes.DEFAULT_TYPE) -> str: - args = context.args - chat = update.effective_chat # type: Optional[Chat] - if chat.type != chat.PRIVATE: - if len(args) >= 1: - var = args[0] - if var in ("no", "off"): - sql.set_clean_service(chat.id, False) - await update.effective_message.reply_text( - "Welcome clean service is : off" - ) - elif var in ("yes", "on"): - sql.set_clean_service(chat.id, True) - await update.effective_message.reply_text( - "Welcome clean service is : on" - ) - else: - await update.effective_message.reply_text( - "Invalid option", - parse_mode=ParseMode.HTML, - ) - else: - await update.effective_message.reply_text( - "Usage is <code>on</code>/<code>yes</code> or <code>off</code>/<code>no</code>", - parse_mode=ParseMode.HTML, - ) - else: - curr = sql.clean_service(chat.id) - if curr: - await update.effective_message.reply_text( - "Welcome clean service is : <code>on</code>", - parse_mode=ParseMode.HTML, - ) - else: - await update.effective_message.reply_text( - "Welcome clean service is : <code>off</code>", - parse_mode=ParseMode.HTML, - ) - - -async def user_button(update: Update, context: ContextTypes.DEFAULT_TYPE): - chat = update.effective_chat - user = update.effective_user - query = update.callback_query - bot = context.bot - match = re.match(r"user_join_\((.+?)\)", query.data) - message = update.effective_message - join_user = int(match.group(1)) - - if join_user == user.id: - sql.set_human_checks(user.id, chat.id) - member_dict = VERIFIED_USER_WAITLIST.pop(user.id) - member_dict["status"] = True - VERIFIED_USER_WAITLIST.update({user.id: member_dict}) - await query.answer(text="Yeet! You're a human, unmuted!") - await bot.restrict_chat_member( - chat.id, - user.id, - permissions=ChatPermissions( - can_send_messages=True, - can_invite_users=True, - can_pin_messages=True, - can_send_polls=True, - can_change_info=True, - can_send_media_messages=True, - can_send_other_messages=True, - can_add_web_page_previews=True, - can_manage_topics=False, - ), - ) - try: - await bot.deleteMessage(chat.id, message.message_id) - except: - pass - if member_dict["should_welc"]: - if member_dict["media_wel"]: - # topic_chat = get_action_topic(chat.id) - sent = await ENUM_FUNC_MAP[member_dict["welc_type"]]( - member_dict["chat_id"], - member_dict["cust_content"], - caption=member_dict["res"], - reply_markup=member_dict["keyboard"], - parse_mode="markdown", - ) - else: - sent = await send( - member_dict["update"], - member_dict["res"], - member_dict["keyboard"], - member_dict["backup_message"], - ) - - prev_welc = sql.get_clean_pref(chat.id) - if prev_welc: - try: - await bot.delete_message(chat.id, prev_welc) - except BadRequest: - pass - - if sent: - sql.set_clean_welcome(chat.id, sent.message_id) - - else: - await query.answer(text="You're not allowed to do this!") - - -WELC_MUTE_HELP_TXT = ( - "You can get the bot to mute new people who join your group and hence prevent spambots from flooding your group. " - "The following options are possible:\n" - "• `/welcomemute soft`: Restricts new members from sending media for 24 hours.\n" - "• `/welcomemute strong`: Mutes new members until they tap on a button, thereby verifying they're human.\n" - "• `/welcomemute off`: Turns off welcomemute.\n" - "Note: Strong mode kicks a user from the chat if they don't verify in 120 seconds. They can always rejoin though." -) - - -@check_admin(is_user=True) -async def welcome_help(update: Update, context: ContextTypes.DEFAULT_TYPE): - WELC_HELP_TXT = ( - "Your group's welcome/goodbye messages can be personalized in multiple ways. If you want the messages" - " to be individually generated, like the default welcome message is, you can use these variables:\n" - " • `{first}`: This represents the user's *first* name\n" - " • `{last}`: This represents the user's *last* name. Defaults to *first name* if the user has no last name.\n" - " • `{fullname}`: This represents the user's *full* name. Defaults to *first name* if the user has no last name.\n" - " • `{username}`: This represents the user's *username*. Defaults to a *mention* of the user's" - " first name if they have no username.\n" - " • `{mention}`: This simply *mentions* a user - tagging them with their first name.\n" - " • `{id}`: This represents the user's *ID*\n" - " • `{count}`: This represents the user's *member number*.\n" - " • `{chatname}`: This represents the *current chat name*.\n" - "\nEach variable must be surrounded by `{}` to be replaced.\n" - "Welcome messages also support markdown, so you can make any elements bold/italic/code/links. " - "Buttons are also supported, so you can make your welcomes look awesome with some nice intro buttons." - "\nTo create a button linking to your rules, use this: `[rules](buttonurl://t.me/" - f"{context.bot.username}?start=group_id)`. Simply replace `group_id` with your group's ID," - " which can be obtained via /id, and you're good to go. Note that group IDs are usually preceded by a `-` sign, so please don't remove it." - " You can even set images/gifs/videos/voice messages as the welcome message by replying to the desired media," - " and calling `/setwelcome`." - ) - - await update.effective_message.reply_text( - WELC_HELP_TXT, parse_mode=ParseMode.MARKDOWN - ) - - -@check_admin(is_user=True) -async def welcome_mute_help(update: Update, context: ContextTypes.DEFAULT_TYPE): - await update.effective_message.reply_text( - WELC_MUTE_HELP_TXT, - parse_mode=ParseMode.MARKDOWN, - ) - - -def __migrate__(old_chat_id, new_chat_id): - sql.migrate_chat(old_chat_id, new_chat_id) - - -def __chat_settings__(chat_id, user_id): - welcome_pref = sql.get_welc_pref(chat_id)[0] - goodbye_pref = sql.get_gdbye_pref(chat_id)[0] - return ( - f"This chat has its welcome preference set to `{welcome_pref}`.\n" - f"Its goodbye preference is `{goodbye_pref}`." - ) - - -# <=================================================== HELP ====================================================> - - -__help__ = """ -➠ *Admins Only:* - -➠ *Default Welcome CMDS:* -» /dwelcome on : Enables the default template welcome. -» /dwelcome off : Disables the default template welcome. - -➠ *Normal Welcome CMDS:* -» /welcome <on/off>: Enable/disable welcome messages. -» /welcome: Shows current welcome settings. -» /welcome noformat: Shows current welcome settings, without the formatting - useful to recycle your welcome messages! -» /goodbye: Same usage and args as /welcome -» /setwelcome <sometext>: Set a custom welcome message. If used replying to media, uses that media. -» /setgoodbye <sometext>: Set a custom goodbye message. If used replying to media, uses that media. -» /resetwelcome: Reset to the default welcome message. -» /resetgoodbye: Reset to the default goodbye message. -» /cleanwelcome <on/off>: On new member, try to delete the previous welcome message to avoid spamming the chat. -» /welcomemutehelp: Gives information about welcome mutes. -» /cleanservice <on/off>: Deletes Telegram's welcome/left service messages. - -➠ *Example:* -User joined chat, user left chat. - -➠ *Welcome Markdown:* -» /welcomehelp: View more formatting information for custom welcome/goodbye messages. -""" - -# <================================================ HANDLER =======================================================> -NEW_MEM_HANDLER = MessageHandler( - filters.StatusUpdate.NEW_CHAT_MEMBERS, new_member, block=False -) -LEFT_MEM_HANDLER = MessageHandler( - filters.StatusUpdate.LEFT_CHAT_MEMBER, left_member, block=False -) -WELC_PREF_HANDLER = CommandHandler( - "welcome", welcome, filters=filters.ChatType.GROUPS, block=False -) -GOODBYE_PREF_HANDLER = CommandHandler( - "goodbye", goodbye, filters=filters.ChatType.GROUPS, block=False -) -SET_WELCOME = CommandHandler( - "setwelcome", set_welcome, filters=filters.ChatType.GROUPS, block=False -) -SET_GOODBYE = CommandHandler( - "setgoodbye", set_goodbye, filters=filters.ChatType.GROUPS, block=False -) -RESET_WELCOME = CommandHandler( - "resetwelcome", reset_welcome, filters=filters.ChatType.GROUPS, block=False -) -RESET_GOODBYE = CommandHandler( - "resetgoodbye", reset_goodbye, filters=filters.ChatType.GROUPS, block=False -) -WELCOMEMUTE_HANDLER = CommandHandler( - "welcomemute", welcomemute, filters=filters.ChatType.GROUPS, block=False -) -CLEAN_SERVICE_HANDLER = CommandHandler( - "cleanservice", cleanservice, filters=filters.ChatType.GROUPS, block=False -) -CLEAN_WELCOME = CommandHandler( - "cleanwelcome", clean_welcome, filters=filters.ChatType.GROUPS, block=False -) -WELCOME_HELP = CommandHandler("welcomehelp", welcome_help, block=False) -WELCOME_MUTE_HELP = CommandHandler("welcomemutehelp", welcome_mute_help, block=False) -BUTTON_VERIFY_HANDLER = CallbackQueryHandler( - user_button, pattern=r"user_join_", block=False -) - -function(NEW_MEM_HANDLER) -function(LEFT_MEM_HANDLER) -function(WELC_PREF_HANDLER) -function(GOODBYE_PREF_HANDLER) -function(SET_WELCOME) -function(SET_GOODBYE) -function(RESET_WELCOME) -function(RESET_GOODBYE) -function(CLEAN_WELCOME) -function(WELCOME_HELP) -function(WELCOMEMUTE_HANDLER) -function(CLEAN_SERVICE_HANDLER) -function(BUTTON_VERIFY_HANDLER) -function(WELCOME_MUTE_HELP) - -__mod_name__ = "WELCOME" -__command_list__ = [] -__handlers__ = [ - NEW_MEM_HANDLER, - LEFT_MEM_HANDLER, - WELC_PREF_HANDLER, - GOODBYE_PREF_HANDLER, - SET_WELCOME, - SET_GOODBYE, - RESET_WELCOME, - RESET_GOODBYE, - CLEAN_WELCOME, - WELCOME_HELP, - WELCOMEMUTE_HANDLER, - CLEAN_SERVICE_HANDLER, - BUTTON_VERIFY_HANDLER, - WELCOME_MUTE_HELP, -] -# <================================================ END =======================================================> diff --git a/Mikobot/plugins/whispers.py b/Mikobot/plugins/whispers.py deleted file mode 100644 index cff019d61f943140d0128e94072ab796ccbca57e..0000000000000000000000000000000000000000 --- a/Mikobot/plugins/whispers.py +++ /dev/null @@ -1,182 +0,0 @@ -# SOURCE https://github.com/Team-ProjectCodeX -# CREATED BY https://t.me/O_okarma -# PROVIDED BY https://t.me/ProjectCodeX -# ➥ @MIKO_V2BOT ʏᴏᴜʀ ᴍᴇssᴀɢᴇ @ᴜsᴇʀɴᴀᴍᴇ -# ➥ @MIKO_V2BOT @ᴜsᴇʀɴᴀᴍᴇ ʏᴏᴜʀ ᴍᴇssᴀɢᴇ - -# TURN ON INLINE MODE FOR USE. - -# <============================================== IMPORTS =========================================================> -import shortuuid -from pymongo import MongoClient -from telegram import ( - InlineKeyboardButton, - InlineKeyboardMarkup, - InlineQueryResultArticle, - InputTextMessageContent, - Update, -) -from telegram.ext import CallbackQueryHandler, ContextTypes, InlineQueryHandler - -from Mikobot import DB_NAME, MONGO_DB_URI, function - -# Initialize MongoDB client -client = MongoClient(MONGO_DB_URI) -db = client[DB_NAME] -collection = db["whispers"] - - -# <==================================================== CLASS ===================================================> -# Whispers Class -class Whispers: - @staticmethod - def add_whisper(WhisperId, WhisperData): - whisper = {"WhisperId": WhisperId, "whisperData": WhisperData} - collection.insert_one(whisper) - - @staticmethod - def del_whisper(WhisperId): - collection.delete_one({"WhisperId": WhisperId}) - - @staticmethod - def get_whisper(WhisperId): - whisper = collection.find_one({"WhisperId": WhisperId}) - return whisper["whisperData"] if whisper else None - - -# <==================================================== BOOT FUNCTION ===================================================> -# Inline query handler -async def mainwhisper(update: Update, context: ContextTypes.DEFAULT_TYPE): - query = update.inline_query - if not query.query: - return await query.answer( - [], - switch_pm_text="Give me a username or ID!", - switch_pm_parameter="ghelp_whisper", - ) - - user, message = parse_user_message(query.query) - if len(message) > 200: - return - - usertype = "username" if user.startswith("@") else "id" - - if user.isdigit(): - try: - chat = await context.bot.get_chat(int(user)) - user = f"@{chat.username}" if chat.username else chat.first_name - except Exception: - pass - - whisperData = { - "user": query.from_user.id, - "withuser": user, - "usertype": usertype, - "type": "inline", - "message": message, - } - whisperId = shortuuid.uuid() - - # Add the whisper to the database - Whispers.add_whisper(whisperId, whisperData) - - answers = [ - InlineQueryResultArticle( - id=whisperId, - title=f"👤 Send a whisper message to {user}!", - description="Only they can see it!", - input_message_content=InputTextMessageContent( - f"🔐 A Whisper Message For {user}\nOnly they can see it!" - ), - reply_markup=InlineKeyboardMarkup( - [ - [ - InlineKeyboardButton( - "📩 𝗦𝗵𝗼𝘄 𝗪𝗵𝗶𝘀𝗽𝗲𝗿 📩", - callback_data=f"whisper_{whisperId}", - ) - ] - ] - ), - ) - ] - - await context.bot.answer_inline_query(query.id, answers) - - -# Callback query handler -async def showWhisper(update: Update, context: ContextTypes.DEFAULT_TYPE): - callback_query = update.callback_query - whisperId = callback_query.data.split("_")[-1] - whisper = Whispers.get_whisper(whisperId) - - if not whisper: - await context.bot.answer_callback_query( - callback_query.id, "This whisper is not valid anymore!" - ) - return - - userType = whisper["usertype"] - from_user_id = callback_query.from_user.id - - if from_user_id == whisper["user"]: - await context.bot.answer_callback_query( - callback_query.id, whisper["message"], show_alert=True - ) - elif ( - userType == "username" - and callback_query.from_user.username - and callback_query.from_user.username.lower() - == whisper["withuser"].replace("@", "").lower() - ): - await context.bot.answer_callback_query( - callback_query.id, whisper["message"], show_alert=True - ) - elif userType == "id" and from_user_id == int(whisper["withuser"]): - await context.bot.answer_callback_query( - callback_query.id, whisper["message"], show_alert=True - ) - else: - await context.bot.answer_callback_query( - callback_query.id, "Not your Whisper!", show_alert=True - ) - - -# Function to parse user message -def parse_user_message(query_text): - text = query_text.split(" ") - user = text[0] - first = True - message = "" - - if not user.startswith("@") and not user.isdigit(): - user = text[-1] - first = False - - if first: - message = " ".join(text[1:]) - else: - text.pop() - message = " ".join(text) - - return user, message - - -# <==================================================== FUNCTION ===================================================> -# Add handlers -function(InlineQueryHandler(mainwhisper, block=False)) -function(CallbackQueryHandler(showWhisper, pattern="^whisper_", block=False)) - - -# <==================================================== HELP ===================================================> -__help__ = """ -➠ *Whisper inline function for secret chats.* - -➠ *Commands:* - -» @MIKO_V2BOT your message @username -» @MIKO_V2BOT @username your message -""" - -__mod_name__ = "WHISPER-MSG" -# <==================================================== END ===================================================> diff --git a/Mikobot/plugins/zombies.py b/Mikobot/plugins/zombies.py deleted file mode 100644 index 108cdec2eb994e256e3432b23347fe0708057ae1..0000000000000000000000000000000000000000 --- a/Mikobot/plugins/zombies.py +++ /dev/null @@ -1,108 +0,0 @@ -# <============================================== IMPORTS =========================================================> -from asyncio import sleep - -from telethon import events -from telethon.errors import ChatAdminRequiredError, UserAdminInvalidError -from telethon.tl.functions.channels import EditBannedRequest -from telethon.tl.types import ChannelParticipantsAdmins, ChatBannedRights - -from Mikobot import SUPPORT_STAFF, tbot - -# <=======================================================================================================> - -BANNED_RIGHTS = ChatBannedRights( - until_date=None, - view_messages=True, - send_messages=True, - send_media=True, - send_stickers=True, - send_gifs=True, - send_games=True, - send_inline=True, - embed_links=True, -) - -UNBAN_RIGHTS = ChatBannedRights( - until_date=None, - send_messages=None, - send_media=None, - send_stickers=None, - send_gifs=None, - send_games=None, - send_inline=None, - embed_links=None, -) - - -# <==================================================== FUNCTION ===================================================> -async def is_administrator(user_id: int, message): - admin = False - async for user in tbot.iter_participants( - message.chat_id, filter=ChannelParticipantsAdmins - ): - if user_id == user.id or user_id in SUPPORT_STAFF: - admin = True - break - return admin - - -@tbot.on(events.NewMessage(pattern="^[!/]zombies ?(.*)")) -async def rm_deletedacc(show): - con = show.pattern_match.group(1).lower() - del_u = 0 - del_status = "Group is clean, 0 deleted accounts found." - if con != "clean": - kontol = await show.reply("`Searching for deleted accounts...`") - async for user in show.client.iter_participants(show.chat_id): - if user.deleted: - del_u += 1 - await sleep(1) - if del_u > 0: - del_status = ( - f"Searching... `{del_u}` deleted account(s) found," - "\nclean it with command `/zombies clean`" - ) - return await kontol.edit(del_status) - chat = await show.get_chat() - admin = chat.admin_rights - creator = chat.creator - if not admin and not creator: - return await show.reply("Sorry, you're not an admin!") - ok = await show.reply("`Banning deleted accounts...`") - del_u = 0 - del_a = 0 - async for user in tbot.iter_participants(show.chat_id): - if user.deleted: - try: - await show.client( - EditBannedRequest(show.chat_id, user.id, BANNED_RIGHTS) - ) - except ChatAdminRequiredError: - return await show.edit("I don't have ban rights in this group") - except UserAdminInvalidError: - del_u -= 1 - del_a += 1 - await tbot(EditBannedRequest(show.chat_id, user.id, UNBAN_RIGHTS)) - del_u += 1 - if del_u > 0: - del_status = f"Cleaned `{del_u}` zombies" - if del_a > 0: - del_status = ( - f"Zombies `{del_u}` zombies " f"\n`{del_a}` admin zombies not deleted." - ) - await ok.edit(del_status) - - -# <=======================================================================================================> - - -# <==================================================== HELP ===================================================> -__help__ = """ -➠ *Remove Deleted Accounts*: - -» /zombies: Starts searching for deleted accounts in the group. - -» /zombies clean: Removes the deleted accounts from the group. -""" -__mod_name__ = "ZOMBIES" -# <==================================================== END ===================================================> diff --git a/Mikobot/state.py b/Mikobot/state.py deleted file mode 100644 index 235017bef6e7aeeea074f2eea2a5834141d6450d..0000000000000000000000000000000000000000 --- a/Mikobot/state.py +++ /dev/null @@ -1,30 +0,0 @@ -# https://github.com/Infamous-Hydra/YaeMiko -# https://github.com/Team-ProjectCodeX - -# <============================================== IMPORTS =========================================================> -from aiohttp import ClientSession -from httpx import AsyncClient, Timeout -from Python_ARQ import ARQ - -# <=============================================== SETUP ========================================================> -# Aiohttp Async Client -session = ClientSession() - -# HTTPx Async Client -state = AsyncClient( - http2=True, - verify=False, - headers={ - "Accept-Language": "en-US,en;q=0.9,id-ID;q=0.8,id;q=0.7", - "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36 Edge/107.0.1418.42", - }, - timeout=Timeout(20), -) # <=======================================================================================================> - - -# <=============================================== ARQ SETUP ========================================================> -ARQ_API_KEY = "RLWCED-WZASYO-AWOLTB-ITBWTP-ARQ" # GET API KEY FROM @ARQRobot -ARQ_API_URL = "arq.hamker.dev" - -arq = ARQ(ARQ_API_URL, ARQ_API_KEY, session) -# <===================================================== END ==================================================> diff --git a/Mikobot/utils/caching.py b/Mikobot/utils/caching.py deleted file mode 100644 index 9e9490983fb6efc28585f2f600ce0ce739a70184..0000000000000000000000000000000000000000 --- a/Mikobot/utils/caching.py +++ /dev/null @@ -1,57 +0,0 @@ -# <============================================== IMPORTS =========================================================> -from threading import RLock -from time import perf_counter, time -from typing import List - -from cachetools import TTLCache -from pyrogram.enums import ChatMembersFilter -from pyrogram.types import CallbackQuery -from pyrogram.types.messages_and_media.message import Message - -from Mikobot import LOGGER - -THREAD_LOCK = RLock() - -# admins stay cached for 30 mins -ADMIN_CACHE = TTLCache(maxsize=512, ttl=(60 * 30), timer=perf_counter) -# Block from refreshing admin list for 10 mins -TEMP_ADMIN_CACHE_BLOCK = TTLCache(maxsize=512, ttl=(60 * 10), timer=perf_counter) -# <=======================================================================================================> - - -# <================================================ FUNCTION =======================================================> -async def admin_cache_reload(m: Message or CallbackQuery, status=None) -> List[int]: - start = time() - with THREAD_LOCK: - if isinstance(m, CallbackQuery): - m = m.message - if status is not None: - TEMP_ADMIN_CACHE_BLOCK[m.chat.id] = status - - try: - if TEMP_ADMIN_CACHE_BLOCK[m.chat.id] in ("autoblock", "manualblock"): - return [] - except KeyError: - # Because it might be first time when admn_list is being reloaded - pass - - admin_list = [ - ( - z.user.id, - f"@{z.user.username}" if z.user.username else z.user.first_name, - z.privileges.is_anonymous, - ) - async for z in m.chat.get_members(filter=ChatMembersFilter.ADMINISTRATORS) - if not z.user.is_deleted - ] - - ADMIN_CACHE[m.chat.id] = admin_list - LOGGER.info( - f"Loaded admins for chat {m.chat.id} in {round((time() - start), 3)}s due to '{status}'", - ) - TEMP_ADMIN_CACHE_BLOCK[m.chat.id] = "autoblock" - - return admin_list - - -# <================================================ END =======================================================> diff --git a/Mikobot/utils/can_restrict.py b/Mikobot/utils/can_restrict.py deleted file mode 100644 index d737d1e00303bff911db5e0d88b159be6b29b29e..0000000000000000000000000000000000000000 --- a/Mikobot/utils/can_restrict.py +++ /dev/null @@ -1,38 +0,0 @@ -# <============================================== IMPORTS =========================================================> -from typing import Callable - -from pyrogram.enums import ChatMemberStatus -from pyrogram.types import Message - -from Mikobot import DEV_USERS, app - -# <=======================================================================================================> -BAN_STICKER = "CAACAgUAAxkBAAEGWC5lloYv1tiI3-KPguoH5YX-RveWugACoQ4AAi4b2FQGdUhawbi91DQE" - - -# <================================================ FUNCTION =======================================================> -def can_restrict(func: Callable) -> Callable: - async def non_admin(_, message: Message): - if message.from_user.id in DEV_USERS: - return await func(_, message) - - check = await app.get_chat_member(message.chat.id, message.from_user.id) - if check.status not in [ChatMemberStatus.OWNER, ChatMemberStatus.ADMINISTRATOR]: - return await message.reply_text( - "» You're not an admin, Please stay in your limits." - ) - - admin = ( - await app.get_chat_member(message.chat.id, message.from_user.id) - ).privileges - if admin.can_restrict_members: - return await func(_, message) - else: - return await message.reply_text( - "`You don't have permissions to restrict users in this chat." - ) - - return non_admin - - -# <================================================ END =======================================================> diff --git a/Mikobot/utils/cmdprefix.py b/Mikobot/utils/cmdprefix.py deleted file mode 100644 index a5e841d5e3bacc6dcb1cf3bdcd9ce0b506eb4577..0000000000000000000000000000000000000000 --- a/Mikobot/utils/cmdprefix.py +++ /dev/null @@ -1,14 +0,0 @@ -from Mikobot import ALLOW_EXCL - -if ALLOW_EXCL: - CMD_STARTERS = ( - "/", - "!", - ".", - "-", - "$", - "*", - "+", - ) -else: - CMD_STARTERS = ("/",) diff --git a/Mikobot/utils/custom_filters.py b/Mikobot/utils/custom_filters.py deleted file mode 100644 index 5af7c92d9610dfdd8027d165f897ff5f62f82372..0000000000000000000000000000000000000000 --- a/Mikobot/utils/custom_filters.py +++ /dev/null @@ -1,305 +0,0 @@ -# <============================================== IMPORTS =========================================================> -from re import compile as compile_re -from re import escape -from shlex import split -from typing import List, Union - -from pyrogram.enums import ChatMemberStatus as CMS -from pyrogram.enums import ChatType -from pyrogram.filters import create -from pyrogram.types import CallbackQuery, Message - -from Mikobot import BOT_ID, BOT_USERNAME, DEV_USERS, DRAGONS, OWNER_ID -from Mikobot.utils.caching import ADMIN_CACHE, admin_cache_reload - -# <=======================================================================================================> - -SUDO_LEVEL = set(DRAGONS + DEV_USERS + [int(OWNER_ID)]) -DEV_LEVEL = set(DEV_USERS + [int(OWNER_ID)]) -PREFIX_HANDLER = ["!", "/", "$"] - - -# <================================================ FUNCTION =======================================================> -def command( - commands: Union[str, List[str]], - case_sensitive: bool = False, - owner_cmd: bool = False, - dev_cmd: bool = False, - sudo_cmd: bool = False, -): - async def func(flt, _, m: Message): - if not m: - return - - date = m.edit_date - if date: - return # reaction - - if m.chat and m.chat.type == ChatType.CHANNEL: - return - - if m and not m.from_user: - return False - - if m.from_user.is_bot: - return False - - if any([m.forward_from_chat, m.forward_from]): - return False - - if owner_cmd and (m.from_user.id != OWNER_ID): - # Only owner allowed to use this...! - return False - - if dev_cmd and (m.from_user.id not in DEV_LEVEL): - # Only devs allowed to use this...! - return False - - if sudo_cmd and (m.from_user.id not in SUDO_LEVEL): - # Only sudos and above allowed to use it - return False - - text: str = m.text or m.caption - if not text: - return False - regex = r"^[{prefix}](\w+)(@{bot_name})?(?: |$)(.*)".format( - prefix="|".join(escape(x) for x in PREFIX_HANDLER), - bot_name=BOT_USERNAME, - ) - matches = compile_re(regex).search(text) - if matches: - m.command = [matches.group(1)] - if matches.group(1) not in flt.commands: - return False - if matches.group(3) == "": - return True - try: - for arg in split(matches.group(3)): - m.command.append(arg) - except ValueError: - pass - return True - return False - - commands = commands if isinstance(commands, list) else [commands] - commands = {c if case_sensitive else c.lower() for c in commands} - - return create( - func, - "NormalCommandFilter", - commands=commands, - case_sensitive=case_sensitive, - ) - - -async def bot_admin_check_func(_, __, m: Message or CallbackQuery): - """Check if bot is Admin or not.""" - - if isinstance(m, CallbackQuery): - m = m.message - - if m.chat.type not in [ChatType.SUPERGROUP, ChatType.GROUP]: - return False - - # Telegram and GroupAnonyamousBot - if m.sender_chat: - return True - - try: - admin_group = {i[0] for i in ADMIN_CACHE[m.chat.id]} - except KeyError: - admin_group = { - i[0] for i in await admin_cache_reload(m, "custom_filter_update") - } - except ValueError as ef: - # To make language selection work in private chat of user, i.e. PM - if ("The chat_id" and "belongs to a user") in ef: - return True - - if BOT_ID in admin_group: - return True - - await m.reply_text( - "I am not an admin to receive updates in this group; Mind Promoting?", - ) - - return False - - -async def admin_check_func(_, __, m: Message or CallbackQuery): - """Check if user is Admin or not.""" - if isinstance(m, CallbackQuery): - m = m.message - - if m.chat.type not in [ChatType.SUPERGROUP, ChatType.GROUP]: - return False - - # Telegram and GroupAnonyamousBot - if m.sender_chat: - return True - - if not m.from_user: - return False - - try: - admin_group = {i[0] for i in ADMIN_CACHE[m.chat.id]} - except KeyError: - admin_group = { - i[0] for i in await admin_cache_reload(m, "custom_filter_update") - } - except ValueError as ef: - # To make language selection work in private chat of user, i.e. PM - if ("The chat_id" and "belongs to a user") in ef: - return True - - if m.from_user.id in admin_group: - return True - - await m.reply_text(text="You cannot use an admin command!") - - return False - - -async def owner_check_func(_, __, m: Message or CallbackQuery): - """Check if user is Owner or not.""" - if isinstance(m, CallbackQuery): - m = m.message - - if m.chat.type not in [ChatType.SUPERGROUP, ChatType.GROUP]: - return False - - if not m.from_user: - return False - - user = await m.chat.get_member(m.from_user.id) - - if user.status == CMS.OWNER: - status = True - else: - status = False - if user.status == CMS.ADMINISTRATOR: - msg = "You're an admin only, stay in your limits!" - else: - msg = "Do you think that you can execute owner commands?" - await m.reply_text(msg) - - return status - - -async def restrict_check_func(_, __, m: Message or CallbackQuery): - """Check if user can restrict users or not.""" - if isinstance(m, CallbackQuery): - m = m.message - - if m.chat.type not in [ChatType.SUPERGROUP, ChatType.GROUP]: - return False - - if not m.from_user: - return False - - user = await m.chat.get_member(m.from_user.id) - - if ( - user - and user.status in [CMS.ADMINISTRATOR, CMS.OWNER] - and user.privileges.can_restrict_members - ): - status = True - else: - status = False - await m.reply_text(text="You don't have permissions to restrict members!") - - return status - - -async def promote_check_func(_, __, m: Message or CallbackQuery): - """Check if user can promote users or not.""" - if isinstance(m, CallbackQuery): - m = m.message - - if m.chat.type not in [ChatType.SUPERGROUP, ChatType.GROUP]: - return False - - if not m.from_user: - return False - - user = await m.chat.get_member(m.from_user.id) - - if ( - user.status in [CMS.ADMINISTRATOR, CMS.OWNER] - and user.privileges.can_promote_members - ): - status = True - else: - status = False - await m.reply_text(text="You don't have permission to promote members!") - - return status - - -async def changeinfo_check_func(_, __, m): - """Check if user can change info or not.""" - if isinstance(m, CallbackQuery): - m = m.message - - if m.chat.type not in [ChatType.SUPERGROUP, ChatType.GROUP]: - await m.reply_text("This command is made to be used in groups not in pm!") - return False - - # Telegram and GroupAnonyamousBot - if m.sender_chat: - return True - - user = await m.chat.get_member(m.from_user.id) - - if ( - user.status in [CMS.ADMINISTRATOR, CMS.OWNER] - and user.privileges.can_change_info - ): - status = True - else: - status = False - await m.reply_text("You don't have: can_change_info permission!") - - return status - - -async def can_pin_message_func(_, __, m): - """Check if user can change info or not.""" - if isinstance(m, CallbackQuery): - m = m.message - - if m.chat.type not in [ChatType.SUPERGROUP, ChatType.GROUP]: - await m.reply_text("This command is made to be used in groups not in pm!") - return False - - # Telegram and GroupAnonyamousBot - if m.sender_chat: - return True - - # Bypass the bot devs, sudos and owner - if m.from_user.id in SUDO_LEVEL: - return True - - user = await m.chat.get_member(m.from_user.id) - - if ( - user.status in [CMS.ADMINISTRATOR, CMS.OWNER] - and user.privileges.can_pin_messages - ): - status = True - else: - status = False - await m.reply_text("You don't have: can_pin_messages permission!") - - return status - - -admin_filter = create(admin_check_func) -owner_filter = create(owner_check_func) -restrict_filter = create(restrict_check_func) -promote_filter = create(promote_check_func) -bot_admin_filter = create(bot_admin_check_func) -can_change_filter = create(changeinfo_check_func) -can_pin_filter = create(can_pin_message_func) -# <================================================ END =======================================================> diff --git a/Mikobot/utils/errors.py b/Mikobot/utils/errors.py deleted file mode 100644 index 809788e99f2fa96da49e1a2c055fd81400e9243c..0000000000000000000000000000000000000000 --- a/Mikobot/utils/errors.py +++ /dev/null @@ -1,62 +0,0 @@ -# <============================================== IMPORTS =========================================================> -import sys -import traceback -from functools import wraps - -from pyrogram.errors.exceptions.forbidden_403 import ChatWriteForbidden - -from Mikobot import OWNER_ID, app - -# <=======================================================================================================> - - -# <================================================ FUNCTION =======================================================> -def split_limits(text): - if len(text) < 2048: - return [text] - - lines = text.splitlines(True) - small_msg = "" - result = [] - for line in lines: - if len(small_msg) + len(line) < 2048: - small_msg += line - else: - result.append(small_msg) - small_msg = line - - result.append(small_msg) - - return result - - -def capture_err(func): - @wraps(func) - async def capture(client, message, *args, **kwargs): - try: - return await func(client, message, *args, **kwargs) - except ChatWriteForbidden: - return - except Exception as err: - exc_type, exc_obj, exc_tb = sys.exc_info() - errors = traceback.format_exception( - exc_type, - value=exc_obj, - tb=exc_tb, - ) - error_feedback = split_limits( - "**ERROR** | `{}` | `{}`\n\n```{}```\n\n```{}```\n".format( - 0 if not message.from_user else message.from_user.id, - 0 if not message.chat else message.chat.id, - message.text or message.caption, - "".join(errors), - ), - ) - for x in error_feedback: - await app.send_message(OWNER_ID, x) - raise err - - return capture - - -# <================================================ END =======================================================> diff --git a/Mikobot/utils/extract_user.py b/Mikobot/utils/extract_user.py deleted file mode 100644 index 85bcbe771e3ae99ba93144900e2dad5aa479edcb..0000000000000000000000000000000000000000 --- a/Mikobot/utils/extract_user.py +++ /dev/null @@ -1,117 +0,0 @@ -# <============================================== IMPORTS =========================================================> -from traceback import format_exc -from typing import Tuple - -from pyrogram.enums import MessageEntityType as entity -from pyrogram.types.messages_and_media.message import Message - -from Database.mongodb.users_db import Users -from Mikobot import LOGGER, app - -# <=======================================================================================================> - - -# <================================================ FUNCTION =======================================================> -async def extract_user(c: app, m: Message) -> Tuple[int, str, str]: - """Extract the user from the provided message.""" - user_id = None - user_first_name = None - user_name = None - - if m.reply_to_message and m.reply_to_message.from_user: - user_id = m.reply_to_message.from_user.id - user_first_name = m.reply_to_message.from_user.first_name - user_name = m.reply_to_message.from_user.username - - elif len(m.text.split()) > 1: - if len(m.entities) > 1: - required_entity = m.entities[1] - if required_entity.type == entity.TEXT_MENTION: - user_id = required_entity.user.id - user_first_name = required_entity.user.first_name - user_name = required_entity.user.username - elif required_entity.type in (entity.MENTION, entity.PHONE_NUMBER): - # new long user ids are identified as phone_number - user_found = m.text[ - required_entity.offset : ( - required_entity.offset + required_entity.length - ) - ] - - try: - user_found = int(user_found) - except (ValueError, Exception) as ef: - if "invalid literal for int() with base 10:" in str(ef): - user_found = str(user_found) - else: - LOGGER.error(ef) - LOGGER.error(format_exc()) - - try: - user = Users.get_user_info(user_found) - user_id = user["_id"] - user_first_name = user["name"] - user_name = user["username"] - except KeyError: - # If user not in database - try: - user = await c.get_users(user_found) - except Exception as ef: - try: - user_r = await c.resolve_peer(user_found) - user = await c.get_users(user_r.user_id) - except Exception as ef: - return await m.reply_text(f"User not found ! Error: {ef}") - user_id = user.id - user_first_name = user.first_name - user_name = user.username - except Exception as ef: - user_id = user_found - user_first_name = user_found - user_name = "" - LOGGER.error(ef) - LOGGER.error(format_exc()) - - else: - try: - user_id = int(m.text.split()[1]) - except (ValueError, Exception) as ef: - if "invalid literal for int() with base 10:" in str(ef): - user_id = ( - str(m.text.split()[1]) - if (m.text.split()[1]).startswith("@") - else None - ) - else: - user_id = m.text.split()[1] - LOGGER.error(ef) - LOGGER.error(format_exc()) - - if user_id is not None: - try: - user = Users.get_user_info(user_id) - user_first_name = user["name"] - user_name = user["username"] - except Exception as ef: - try: - user = await c.get_users(user_id) - except Exception as ef: - try: - user_r = await c.resolve_peer(user_found) - user = await c.get_users(user_r.user_id) - except Exception as ef: - return await m.reply_text(f"User not found ! Error: {ef}") - user_first_name = user.first_name - user_name = user.username - LOGGER.error(ef) - LOGGER.error(format_exc()) - - else: - user_id = m.from_user.id - user_first_name = m.from_user.first_name - user_name = m.from_user.username - - return user_id, user_first_name, user_name - - -# <================================================ END =======================================================> diff --git a/Mikobot/utils/fun_strings.py b/Mikobot/utils/fun_strings.py deleted file mode 100644 index 1efe9d1b7ace1f5523f8b1c2815bc53d209e62a5..0000000000000000000000000000000000000000 --- a/Mikobot/utils/fun_strings.py +++ /dev/null @@ -1,250 +0,0 @@ -ITEMS = ( - "cast iron skillet", - "angry meow", - "cricket bat", - "wooden cane", - "shovel", - "toaster", - "book", - "laptop", - "rubber chicken", - "spiked bat", - "heavy rock", - "chunk of dirt", - "ton of bricks", - "rasengan", - "spirit bomb", - "100-Type Guanyin Bodhisattva", - "rasenshuriken", - "Murasame", - "ban", - "chunchunmaru", - "Kubikiribōchō", - "rasengan", - "spherical flying kat", -) - -THROW = ( - "throws", - "flings", - "chucks", - "hurls", -) - -HIT = ( - "hits", - "whacks", - "slaps", - "smacks", - "bashes", - "pats", -) - -EYES = [ - ["⌐■", "■"], - [" ͠°", " °"], - ["⇀", "↼"], - ["´• ", " •`"], - ["´", "`"], - ["`", "´"], - ["ó", "ò"], - ["ò", "ó"], - ["⸌", "⸍"], - [">", "<"], - ["Ƹ̵̡", "Ʒ"], - ["ᗒ", "ᗕ"], - ["⟃", "⟄"], - ["⪧", "⪦"], - ["⪦", "⪧"], - ["⪩", "⪨"], - ["⪨", "⪩"], - ["⪰", "⪯"], - ["⫑", "⫒"], - ["⨴", "⨵"], - ["⩿", "⪀"], - ["⩾", "⩽"], - ["⩺", "⩹"], - ["⩹", "⩺"], - ["◥▶", "◀◤"], - ["◍", "◎"], - ["/͠-", "┐͡-\\"], - ["⌣", "⌣”"], - [" ͡⎚", " ͡⎚"], - ["≋"], - ["૦ઁ"], - [" ͯ"], - [" ͌"], - ["ළ"], - ["◉"], - ["☉"], - ["・"], - ["▰"], - ["ᵔ"], - [" ゚"], - ["□"], - ["☼"], - ["*"], - ["`"], - ["⚆"], - ["⊜"], - [">"], - ["❍"], - [" ̄"], - ["─"], - ["✿"], - ["•"], - ["T"], - ["^"], - ["ⱺ"], - ["@"], - ["ȍ"], - ["  "], - ["  "], - ["x"], - ["-"], - ["$"], - ["Ȍ"], - ["ʘ"], - ["Ꝋ"], - [""], - ["⸟"], - ["๏"], - ["ⴲ"], - ["◕"], - ["◔"], - ["✧"], - ["■"], - ["♥"], - [" ͡°"], - ["¬"], - [" º "], - ["⨶"], - ["⨱"], - ["⏓"], - ["⏒"], - ["⍜"], - ["⍤"], - ["ᚖ"], - ["ᴗ"], - ["ಠ"], - ["σ"], - ["☯"], -] - -MOUTHS = [ - ["v"], - ["ᴥ"], - ["ᗝ"], - ["Ѡ"], - ["ᗜ"], - ["Ꮂ"], - ["ᨓ"], - ["ᨎ"], - ["ヮ"], - ["╭͜ʖ╮"], - [" ͟ل͜"], - [" ͜ʖ"], - [" ͟ʖ"], - [" ʖ̯"], - ["ω"], - [" ³"], - [" ε "], - ["﹏"], - ["□"], - ["ل͜"], - ["‿"], - ["╭╮"], - ["‿‿"], - ["▾"], - ["‸"], - ["Д"], - ["∀"], - ["!"], - ["人"], - ["."], - ["ロ"], - ["_"], - ["෴"], - ["ѽ"], - ["ഌ"], - ["⏠"], - ["⏏"], - ["⍊"], - ["⍘"], - ["ツ"], - ["益"], - ["╭∩╮"], - ["Ĺ̯"], - ["◡"], - [" ͜つ"], -] - -EARS = [ - ["q", "p"], - ["ʢ", "ʡ"], - ["⸮", "?"], - ["ʕ", "ʔ"], - ["ᖗ", "ᖘ"], - ["ᕦ", "ᕥ"], - ["ᕦ(", ")ᕥ"], - ["ᕙ(", ")ᕗ"], - ["ᘳ", "ᘰ"], - ["ᕮ", "ᕭ"], - ["ᕳ", "ᕲ"], - ["(", ")"], - ["[", "]"], - ["¯\\_", "_/¯"], - ["୧", "୨"], - ["୨", "୧"], - ["⤜(", ")⤏"], - ["☞", "☞"], - ["ᑫ", "ᑷ"], - ["ᑴ", "ᑷ"], - ["ヽ(", ")ノ"], - ["\\(", ")/"], - ["乁(", ")ㄏ"], - ["└[", "]┘"], - ["(づ", ")づ"], - ["(ง", ")ง"], - ["⎝", "⎠"], - ["ლ(", "ლ)"], - ["ᕕ(", ")ᕗ"], - ["(∩", ")⊃━☆゚.*"], -] - -TOSS = ( - "Heads", - "Tails", -) - -DECIDE = ("Yes.", "No.", "Maybe.") - - -# This feature's credit goes to @ishikki_akabane -FLIRT = ( - "I hope you know CPR, because you just took my breath away!", - "So, aside from taking my breath away, what do you do for a living?", - "I ought to complain to Spotify for you not being named this week’s hottest single.", - "Your eyes are like the ocean; I could swim in them all day.", - "When I look in your eyes, I see a very kind soul.", - "If you were a vegetable, you’d be a ‘cute-cumber.’", - "Do you happen to have a Band-Aid? ‘Cause I scraped my knees falling for you.", - "I never believed in love at first sight, but that was before I saw you.", - "I didn’t know what I wanted in a woman until I saw you.", - "You’re like a fine wine. The more of you I drink in, the better I feel.", - "You’ve got a lot of beautiful curves, but your smile is absolutely my favorite.", - "If being sexy was a crime, you’d be guilty as charged.", - "I was wondering if you’re an artist because you were so good at drawing me in.", - "It says in the Bible to only think about what’s pure and lovely… So I’ve been thinking about you all day long.", - "Do you have a map? I just got lost in your eyes.", - "You know what you would look really beautiful in? My arms.", - "Are you a magician? It’s the strangest thing, but every time I look at you, everyone else disappears.", - "I would never play hide and seek with you because someone like you is impossible to find.", - "I think there’s something wrong with my phone. Could you try calling it to see if it works?", - "Are you an electrician? Because you’re definitely lighting up my day/night!", - "I always thought happiness started with an ‘h,’ but it turns out mine starts with ‘u.’", - "I believe in following my dreams. Can I have your Instagram?", - "If you were a song, you’d be the best track on the album.", - "Is your name Google? Because you have everything I’m searching for.", - "Do you ever get tired from running through my thoughts all night?", -) diff --git a/Mikobot/utils/parser.py b/Mikobot/utils/parser.py deleted file mode 100644 index 616c8bc42bbfffb8c24ef517472c0a23d9ad23a8..0000000000000000000000000000000000000000 --- a/Mikobot/utils/parser.py +++ /dev/null @@ -1,33 +0,0 @@ -# <============================================== IMPORTS =========================================================> -from html import escape -from re import compile as compilere -from re import sub - -# <=======================================================================================================> - - -# <================================================ FUNCTION =======================================================> -async def cleanhtml(raw_html: str) -> str: - """Clean html data.""" - cleanr = compilere("<.*?>") - return sub(cleanr, "", raw_html) - - -async def escape_markdown(text: str) -> str: - """Escape markdown data.""" - escape_chars = r"\*_`\[" - return sub(r"([%s])" % escape_chars, r"\\\1", text) - - -async def mention_html(name: str, user_id: int) -> str: - """Mention user in html format.""" - name = escape(name) - return f'<a href="tg://user?id={user_id}">{name}</a>' - - -async def mention_markdown(name: str, user_id: int) -> str: - """Mention user in markdown format.""" - return f"[{(await escape_markdown(name))}](tg://user?id={user_id})" - - -# <================================================ END =======================================================> diff --git a/Mikobot/utils/permissions.py b/Mikobot/utils/permissions.py deleted file mode 100644 index 79abf59dd8a65a0cbf38e6b592ea068e59500c27..0000000000000000000000000000000000000000 --- a/Mikobot/utils/permissions.py +++ /dev/null @@ -1,122 +0,0 @@ -# <============================================== IMPORTS =========================================================> -from functools import wraps -from time import time -from traceback import format_exc as err - -from pyrogram.enums import ChatMembersFilter -from pyrogram.errors.exceptions.forbidden_403 import ChatWriteForbidden -from pyrogram.types import Message - -from Mikobot import DRAGONS, app - -# <=======================================================================================================> - - -# <================================================ FUNCTION =======================================================> -async def member_permissions(chat_id: int, user_id: int): - perms = [] - member = (await app.get_chat_member(chat_id, user_id)).privileges - if not member: - return [] - if member.can_post_messages: - perms.append("can_post_messages") - if member.can_edit_messages: - perms.append("can_edit_messages") - if member.can_delete_messages: - perms.append("can_delete_messages") - if member.can_restrict_members: - perms.append("can_restrict_members") - if member.can_promote_members: - perms.append("can_promote_members") - if member.can_change_info: - perms.append("can_change_info") - if member.can_invite_users: - perms.append("can_invite_users") - if member.can_pin_messages: - perms.append("can_pin_messages") - if member.can_manage_video_chats: - perms.append("can_manage_video_chats") - return perms - - -async def authorised(func, subFunc2, client, message, *args, **kwargs): - chatID = message.chat.id - try: - await func(client, message, *args, **kwargs) - except ChatWriteForbidden: - await app.leave_chat(chatID) - except Exception as e: - try: - await message.reply_text(str(e.MESSAGE)) - except AttributeError: - await message.reply_text(str(e)) - e = err() - print(str(e)) - return subFunc2 - - -async def unauthorised(message: Message, permission, subFunc2): - chatID = message.chat.id - text = ( - "You don't have the required permission to perform this action." - + f"\n**Permission:** __{permission}__" - ) - try: - await message.reply_text(text) - except ChatWriteForbidden: - await app.leave_chat(chatID) - return subFunc2 - - -def adminsOnly(permission): - def subFunc(func): - @wraps(func) - async def subFunc2(client, message: Message, *args, **kwargs): - chatID = message.chat.id - if not message.from_user: - # For anonymous admins - if message.sender_chat and message.sender_chat.id == message.chat.id: - return await authorised( - func, - subFunc2, - client, - message, - *args, - **kwargs, - ) - return await unauthorised(message, permission, subFunc2) - # For admins and sudo users - userID = message.from_user.id - permissions = await member_permissions(chatID, userID) - if userID not in DRAGONS and permission not in permissions: - return await unauthorised(message, permission, subFunc2) - return await authorised(func, subFunc2, client, message, *args, **kwargs) - - return subFunc2 - - return subFunc - - -admins_in_chat = {} - - -async def list_admins(chat_id: int): - global admins_in_chat - if chat_id in admins_in_chat: - interval = time() - admins_in_chat[chat_id]["last_updated_at"] - if interval < 3600: - return admins_in_chat[chat_id]["data"] - - admins_in_chat[chat_id] = { - "last_updated_at": time(), - "data": [ - member.user.id - async for member in app.get_chat_members( - chat_id, filter=ChatMembersFilter.ADMINISTRATORS - ) - ], - } - return admins_in_chat[chat_id]["data"] - - -# <================================================ END =======================================================> diff --git a/Mikobot/utils/string.py b/Mikobot/utils/string.py deleted file mode 100644 index 1f1db4c9ef08d2166d5102acdeec5417ba3ae22a..0000000000000000000000000000000000000000 --- a/Mikobot/utils/string.py +++ /dev/null @@ -1,200 +0,0 @@ -# <============================================== IMPORTS =========================================================> -from datetime import datetime, timedelta -from html import escape -from re import compile as compile_re -from typing import List - -from pyrogram.enums import ChatType -from pyrogram.types import Message - -from Mikobot.utils.parser import escape_markdown - -TIME_ZONE = "Asia/Kolkata" -BTN_URL_REGEX = compile_re(r"(\[([^\[]+?)\]\(buttonurl:(?:/{0,2})(.+?)(:same)?\))") -# <=======================================================================================================> - - -# <================================================ FUNCTION =======================================================> -async def extract_time(m: Message, time_val: str): - """Extract time from message.""" - if any(time_val.endswith(unit) for unit in ("m", "h", "d")): - unit = time_val[-1] - time_num = time_val[:-1] # type: str - if not time_num.isdigit(): - await m.reply("Unspecified amount of time.") - return "" - initial_time = datetime.now(TIME_ZONE) - if unit == "m": - bantime = initial_time + timedelta(minutes=int(time_num)) - elif unit == "h": - bantime = initial_time + timedelta(hours=int(time_num)) - elif unit == "d": - bantime = initial_time + timedelta(days=int(time_num)) - else: - # how even...? - return "" - return bantime - await m.reply( - f"Invalid time type specified. Needed m, h, or s. got: {time_val[-1]}", - ) - return "" - - -async def parse_button(text: str): - """Parse button from text.""" - markdown_note = text - prev = 0 - note_data = "" - buttons = [] - for match in BTN_URL_REGEX.finditer(markdown_note): - # Check if btnurl is escaped - n_escapes = 0 - to_check = match.start(1) - 1 - while to_check > 0 and markdown_note[to_check] == "\\": - n_escapes += 1 - to_check -= 1 - - # if even, not escaped -> create button - if n_escapes % 2 == 0: - # create a thruple with button label, url, and newline status - buttons.append((match.group(2), match.group(3), bool(match.group(4)))) - note_data += markdown_note[prev : match.start(1)] - prev = match.end(1) - # if odd, escaped -> move along - else: - note_data += markdown_note[prev:to_check] - prev = match.start(1) - 1 - else: - note_data += markdown_note[prev:] - return note_data, buttons - - -async def build_keyboard(buttons): - """Build keyboards from provided buttons.""" - keyb = [] - for btn in buttons: - if btn[-1] and keyb: - keyb[-1].append((btn[0], btn[1], "url")) - else: - keyb.append([(btn[0], btn[1], "url")]) - - return keyb - - -SMART_OPEN = "“" -SMART_CLOSE = "”" -START_CHAR = ("'", '"', SMART_OPEN) - - -async def escape_invalid_curly_brackets(text: str, valids: List[str]) -> str: - new_text = "" - idx = 0 - while idx < len(text): - if text[idx] == "{": - if idx + 1 < len(text) and text[idx + 1] == "{": - idx += 2 - new_text += "{{{{" - continue - success = False - for v in valids: - if text[idx:].startswith("{" + v + "}"): - success = True - break - if success: - new_text += text[idx : idx + len(v) + 2] - idx += len(v) + 2 - continue - new_text += "{{" - - elif text[idx] == "}": - if idx + 1 < len(text) and text[idx + 1] == "}": - idx += 2 - new_text += "}}}}" - continue - new_text += "}}" - - else: - new_text += text[idx] - idx += 1 - - return new_text - - -async def escape_mentions_using_curly_brackets( - m: Message, - text: str, - parse_words: list, -) -> str: - if m.chat.type in [ChatType.SUPERGROUP, ChatType.GROUP, ChatType.CHANNEL]: - chat_name = escape(m.chat.title) - else: - chat_name = escape(m.from_user.first_name) - teks = await escape_invalid_curly_brackets(text, parse_words) - if teks: - teks = teks.format( - first=escape(m.from_user.first_name), - last=escape(m.from_user.last_name or m.from_user.first_name), - mention=m.from_user.mention, - username=( - "@" + (await escape_markdown(escape(m.from_user.username))) - if m.from_user.username - else m.from_user.mention - ), - fullname=" ".join( - [ - escape(m.from_user.first_name), - escape(m.from_user.last_name), - ] - if m.from_user.last_name - else [escape(m.from_user.first_name)], - ), - chatname=chat_name, - id=m.from_user.id, - ) - else: - teks = "" - - return teks - - -async def split_quotes(text: str): - """Split quotes in text.""" - if not any(text.startswith(char) for char in START_CHAR): - return text.split(None, 1) - counter = 1 # ignore first char -> is some kind of quote - while counter < len(text): - if text[counter] == "\\": - counter += 1 - elif text[counter] == text[0] or ( - text[0] == SMART_OPEN and text[counter] == SMART_CLOSE - ): - break - counter += 1 - else: - return text.split(None, 1) - - # 1 to avoid starting quote, and counter is exclusive so avoids ending - key = await remove_escapes(text[1:counter].strip()) - # index will be in range, or `else` would have been executed and returned - rest = text[counter + 1 :].strip() - if not key: - key = text[0] + text[0] - return list(filter(None, [key, rest])) - - -async def remove_escapes(text: str) -> str: - """Remove the escaped from message.""" - res = "" - is_escaped = False - for counter in range(len(text)): - if is_escaped: - res += text[counter] - is_escaped = False - elif text[counter] == "\\": - is_escaped = True - else: - res += text[counter] - return res - - -# <================================================ END =======================================================>