diff --git a/TelegramBot/plugins/sudo/broadcast.py b/TelegramBot/plugins/sudo/broadcast.py new file mode 100644 index 0000000..c232564 --- /dev/null +++ b/TelegramBot/plugins/sudo/broadcast.py @@ -0,0 +1,69 @@ +from asyncio import sleep + +from pyrogram import Client, filters +from pyrogram.types import Message + +from TelegramBot.database import MongoDb +from TelegramBot.helpers.decorators import ratelimiter +from TelegramBot.helpers.filters import dev_cmd +from TelegramBot.logging import LOGGER + + +@Client.on_message(filters.command(["broadcast"]) & dev_cmd) +@ratelimiter +async def broadcast(_: Client, message: Message): + """ + Broadcast the message via bot to bot users and groups.. + """ + + if not (broadcast_msg := message.reply_to_message): + broadcast_usage = f"Reply with command /broadcast to the message you want to broadcast.\n\n/broadcast users - To broadcast message to users only.\n\n/broadcast chats - To broadcast message to chats only.\n\n/broadcast all - To broadcast message everywhere." + return await message.reply_text(broadcast_usage, quote=True) + + proses_msg = await message.reply_text( + "**Broadcasting started. Please wait for few minutes for it to get completed.", quote=True) + + to_chats = False + to_users = False + disable_notification = True + commands = message.command + + if len(commands) > 3: + return await proses_msg.edit("Invalid Command") + + for command in message.command: + if command.lower() == "all": + to_chats = True + to_users = True + elif command.lower() == "users": + to_users = True + to_chats = False + elif command.lower() == "chats": + to_users = False + to_chats = True + elif command.lower() == "loud": + disable_notification = False + + total_list = [] + if to_chats: + total_list += await MongoDb.chats.get_all_id() + + if to_users: + total_list += await MongoDb.users.get_all_id() + + failed = 0 + success = 0 + + for __id in total_list: + try: + await broadcast_msg.copy( + __id, broadcast_msg.caption, disable_notification=disable_notification) + success += 1 + # preventing flood wait + await sleep(0.3) + except Exception as error: + LOGGER(__name__).error(str(error)) + failed += 1 + + return await proses_msg.edit( + f"**The message has been successfully broadcasted.**\n\nTotal success = {success}\nTotal Failure = {failed}") diff --git a/TelegramBot/plugins/sudo/dbstats.py b/TelegramBot/plugins/sudo/dbstats.py new file mode 100644 index 0000000..f64e010 --- /dev/null +++ b/TelegramBot/plugins/sudo/dbstats.py @@ -0,0 +1,21 @@ +from pyrogram.types import Message +from pyrogram import Client, filters + +from TelegramBot.database import MongoDb +from TelegramBot.helpers.filters import sudo_cmd +from TelegramBot.helpers.decorators import ratelimiter + + +@Client.on_message(filters.command("dbstats") & sudo_cmd) +@ratelimiter +async def dbstats(_, message: Message): + """ + Returns database stats of MongoDB, which includes Total number + of bot user and total number of bot chats. + """ + + TotalUsers = await MongoDb.users.total_documents() + TotalChats = await MongoDb.chats.total_documents() + + stats_string = f"**Bot Database Statics.\n\n**Total Number of users = {TotalUsers}\nTotal number of chats = {TotalChats}" + return await message.reply_text(stats_string) diff --git a/TelegramBot/plugins/sudo/log.py b/TelegramBot/plugins/sudo/log.py new file mode 100644 index 0000000..124f0dc --- /dev/null +++ b/TelegramBot/plugins/sudo/log.py @@ -0,0 +1,18 @@ +from pyrogram.types import Message +from pyrogram import Client, filters + +from TelegramBot.helpers.filters import sudo_cmd +from TelegramBot.helpers.decorators import ratelimiter + + +@Client.on_message(filters.command(["log", "logs"]) & sudo_cmd) +@ratelimiter +async def log(_, message: Message): + """ + upload the logs file of the bot. + """ + + try: + return await message.reply_document("logs.txt", caption="logs.txt", quote=True) + except Exception as error: + return await message.reply_text(f"{error}", quote=True) diff --git a/TelegramBot/plugins/sudo/terminal.py b/TelegramBot/plugins/sudo/terminal.py new file mode 100644 index 0000000..5bbf6cc --- /dev/null +++ b/TelegramBot/plugins/sudo/terminal.py @@ -0,0 +1,139 @@ +import asyncio +import shlex +import sys +import traceback +from io import BytesIO, StringIO +from os import remove + +import aiofiles +from pyrogram import Client, filters +from pyrogram.types import ( + CallbackQuery, + InlineKeyboardButton, + InlineKeyboardMarkup, + Message) + +from TelegramBot.helpers.decorators import ratelimiter +from TelegramBot.helpers.filters import dev_cmd +from TelegramBot.logging import LOGGER + + +@Client.on_message(filters.command(["shell", "sh"]) & dev_cmd) +@ratelimiter +async def shell(_, message: Message): + """ + Executes command in terminal via bot. + """ + + if len(message.command) < 2: + shell_usage = f"**USAGE:** Executes terminal commands directly via bot.\n\n**Example: **
/shell pip install requests
" + return await message.reply_text(shell_usage, quote=True) + + user_input = message.text.split(maxsplit=1)[1] + args = shlex.split(user_input) + + try: + shell_replymsg = await message.reply_text("running...", quote=True) + shell = await asyncio.create_subprocess_exec(*args, stdout=-1, stderr=-1) + stdout, stderr = await shell.communicate() + result = str(stdout.decode().strip()) + str(stderr.decode().strip()) + except Exception as error: + LOGGER(__name__).warning(f"{error}") + return await shell_replymsg.edit(f"Error :-\n\n{error}") + + if len(result) > 4000: + await shell_replymsg.edit("output too large. sending it as a file..") + file = BytesIO(result.encode()) + file.name = "output.txt" + await shell_replymsg.reply_document(file, caption=file.name, quote=True) + else: + await shell_replymsg.edit(f"Output :-\n\n{result}") + + +async def aexec(code, client, message): + exec( + "async def __aexec(client, message): " + + "".join(f"\n {a}" for a in code.split("\n")) + ) + return await locals()["__aexec"](client, message) + + +async def py_runexec(client: Client, message: Message, replymsg: Message): + old_stderr = sys.stderr + old_stdout = sys.stdout + redirected_output = sys.stdout = StringIO() + redirected_error = sys.stderr = StringIO() + stdout, stderr, exc = None, None, None + + try: + await replymsg.edit("executing...") + code = message.text.split(maxsplit=1)[1] + except IndexError: + return await replymsg.edit("No codes found to execute.", reply_markup=InlineKeyboardMarkup( + [[InlineKeyboardButton("Refresh 🔄", callback_data="refresh")]])) + + if "config.env" in code: + return await replymsg.edit("That's a dangerous operation! Not Permitted!", reply_markup=InlineKeyboardMarkup( + [[InlineKeyboardButton("Refresh 🔄", callback_data="refresh")]])) + + try: + await aexec(code, client, message) + except Exception: + exc = traceback.format_exc() + + stdout = redirected_output.getvalue() + stderr = redirected_error.getvalue() + sys.stdout = old_stdout + sys.stderr = old_stderr + + evaluation = "" + if exc: + evaluation = exc + elif stderr: + evaluation = stderr + elif stdout: + evaluation = stdout + else: + evaluation = "success" + final_output = f"{evaluation.strip()}" + + if len(final_output) <= 4000: + return await replymsg.edit(final_output, reply_markup=InlineKeyboardMarkup( + [[InlineKeyboardButton("refresh 🔄", callback_data="refresh")]])) + async with aiofiles.open("output.txt", "w+", encoding="utf8") as file: + await file.write(str(evaluation.strip())) + + await replymsg.edit("output too large. sending it as a file...", reply_markup=InlineKeyboardMarkup( + [[InlineKeyboardButton("refresh 🔄", callback_data="refresh")]])) + await client.send_document(message.chat.id, "output.txt", caption="output.txt") + remove("output.txt") + + +@Client.on_callback_query(filters.regex("refresh")) +@ratelimiter +async def pyCallbacks(client, CallbackQuery: CallbackQuery): + cliker_user_id = CallbackQuery.from_user.id + message_user_id = CallbackQuery.message.reply_to_message.from_user.id + + if cliker_user_id != message_user_id: + return await CallbackQuery.answer("That command is not initiated by you.", show_alert=True) + + message = await client.get_messages( + CallbackQuery.message.chat.id, CallbackQuery.message.reply_to_message.id) + replymsg = await client.get_messages( + CallbackQuery.message.chat.id, CallbackQuery.message.id) + + if CallbackQuery.data == "refresh": + await py_runexec(client, message, replymsg) + + +@Client.on_message(filters.command(["exec", "py"]) & dev_cmd) +@ratelimiter +async def py_exec(client, message): + """Executes python command via bot with refresh button.""" + if len(message.command) < 2: + await message.reply_text( + f"**Usage:** Executes python commands directly via bot.\n\n**Example: **
/exec print('hello world')
") + else: + replymsg = await message.reply_text("executing..", quote=True) + await py_runexec(client, message, replymsg) diff --git a/TelegramBot/plugins/sudo/updater.py b/TelegramBot/plugins/sudo/updater.py new file mode 100644 index 0000000..6da8966 --- /dev/null +++ b/TelegramBot/plugins/sudo/updater.py @@ -0,0 +1,36 @@ +import os +import sys + +from pyrogram import Client, filters +from pyrogram.types import Message + +from TelegramBot.helpers.decorators import ratelimiter +from TelegramBot.helpers.filters import dev_cmd +from TelegramBot.logging import LOGGER + + +@Client.on_message(filters.command("update") & dev_cmd) +@ratelimiter +async def update(_, message: Message): + """ + Update the bot with the latest commit changes from GitHub. + """ + + msg = await message.reply_text("Pulling changes with latest commits...", quote=True) + os.system("git pull") + LOGGER(__name__).info("Bot Updated with latest commits. Restarting now..") + await msg.edit("Changes pulled with latest commits. Restarting bot now... ") + os.execl(sys.executable, sys.executable, "-m", "TelegramBot") + + +@Client.on_message(filters.command("restart") & dev_cmd) +@ratelimiter +async def restart(_, message: Message): + """ + Just Restart the bot and update the bot with local changes. + """ + + LOGGER(__name__).info("Restarting the bot. shutting down this instance") + await message.reply_text( + "Starting a new instance and shutting down this one...", quote=True) + os.execl(sys.executable, sys.executable, "-m", "TelegramBot")