| """ |
| Master Bot (Telethon) - Eval & Plugin Userbot Edition |
| - Upload a .session file or send a Bot Token to register an assistant. |
| - Assistants respond to .val, .bash, /install, /promote, /demote, /help |
| - Commands are restricted to the owner, the userbot account itself, and promoted users. |
| - Dynamic plugin loading via /install. |
| """ |
|
|
| import os |
| import asyncio |
| import logging |
| import sqlite3 |
| import time |
| import inspect |
| import sys |
| import traceback |
| import json |
| import requests |
| import importlib.util |
| from io import StringIO, BytesIO |
| from datetime import datetime |
| from typing import Dict, Any, Optional |
|
|
| import telethon |
| from telethon import TelegramClient, events, Button |
| from telethon.utils import get_display_name |
|
|
| try: |
| import black |
| except ImportError: |
| black = None |
|
|
| |
| API_ID = 22138159 |
| API_HASH = "3fe4592e4cad72f366b6c564505f2d57" |
|
|
| MASTER_BOT_SESSION = "master.session" |
| MASTER_BOT_TOKEN = "8445494601:AAGTLtC-yifUHAPXY9DdJ-wEfqCyXCqguSU" |
|
|
| DB_PATH = "masterbot.db" |
| SESSIONS_DIR = "sessions" |
| PLUGINS_DIR = "plugins" |
|
|
| SUPER_ADMIN_ID = 948247711 |
| LOG_CHANNEL_ID = -1001744991128 |
|
|
| CLIENT_IDLE_TIMEOUT = 60 * 60 |
| CLEANUP_INTERVAL = 15 * 60 |
| MAX_ACTIVE_CLIENTS = 60 |
| |
|
|
| os.makedirs(SESSIONS_DIR, exist_ok=True) |
| os.makedirs(PLUGINS_DIR, exist_ok=True) |
| logging.basicConfig(level=logging.INFO) |
| logger = logging.getLogger("masterbot") |
|
|
|
|
| |
| def generate_session_via_api(token: str, filepath: str) -> bool: |
| |
| url = "https://string-zfw3.onrender.com/generate" |
| payload = { |
| "api_id": API_ID, |
| "api_hash": API_HASH, |
| "bot_token": token, |
| "library": "telethon" |
| } |
| try: |
| response = requests.post(url, json=payload) |
| if response.status_code == 200: |
| with open(filepath, "wb") as f: |
| f.write(response.content) |
| return True |
| else: |
| logger.error(f"API Generation Failed. Error: {response.text}") |
| return False |
| except Exception as e: |
| logger.error(f"Request to Session API failed: {e}") |
| return False |
|
|
|
|
| |
| def time_formatter(milliseconds): |
| seconds, milliseconds = divmod(int(milliseconds), 1000) |
| minutes, seconds = divmod(seconds, 60) |
| hours, minutes = divmod(minutes, 60) |
| days, hours = divmod(hours, 24) |
| tmp = ( |
| (f"{days}d, " if days else "") |
| + (f"{hours}h, " if hours else "") |
| + (f"{minutes}m, " if minutes else "") |
| + (f"{seconds}s, " if seconds else "") |
| + (f"{milliseconds}ms, " if milliseconds else "") |
| ) |
| return tmp[:-2] |
|
|
| class u: |
| _ = "" |
| r = None |
| lr = None |
|
|
| async def aexec(code, event): |
| exec( |
| ( |
| "async def __aexec(e, client): " |
| + "\n print = p = _stringify" |
| + "\n message = event = e" |
| + "\n u.r = reply = await event.get_reply_message()" |
| + "\n chat = event.chat_id" |
| + "\n u.lr = locals()" |
| ) |
| + "".join(f"\n {l}" for l in code.split("\n")) |
| ) |
| return await locals()["__aexec"](event, event.client) |
|
|
| def _parse_eval(value=None): |
| if not value: |
| return value |
| if hasattr(value, "stringify"): |
| try: |
| return value.stringify() |
| except TypeError: |
| pass |
| elif isinstance(value, dict): |
| try: |
| return json.dumps(value, indent=1, default=str) |
| except BaseException: |
| pass |
| elif isinstance(value, list): |
| newlist = "[" |
| for index, child in enumerate(value): |
| newlist += "\n " + str(_parse_eval(child)) |
| if index < len(value) - 1: |
| newlist += "," |
| newlist += "\n]" |
| return newlist |
| return str(value) |
|
|
| def _stringify(text=None, *args, **kwargs): |
| if text: |
| u._ = text |
| text = _parse_eval(text) |
| return print(text, *args, **kwargs) |
|
|
| async def eor(event, text, **kwargs): |
| """Polyfill for userbot eor (edit or reply)""" |
| try: |
| return await event.edit(text, **kwargs) |
| except Exception: |
| return await event.reply(text, **kwargs) |
|
|
| |
| def init_db(): |
| conn = sqlite3.connect(DB_PATH, check_same_thread=False) |
| cur = conn.cursor() |
| cur.execute(""" |
| CREATE TABLE IF NOT EXISTS bots ( |
| id INTEGER PRIMARY KEY AUTOINCREMENT, |
| owner_id INTEGER NOT NULL, |
| bot_username TEXT NOT NULL UNIQUE, |
| token_encrypted BLOB NOT NULL, |
| active INTEGER NOT NULL DEFAULT 1, |
| reply_text TEXT, |
| created_at TEXT NOT NULL |
| ); |
| """) |
| cur.execute(""" |
| CREATE TABLE IF NOT EXISTS bans ( |
| user_id INTEGER PRIMARY KEY, |
| reason TEXT, |
| banned_at TEXT |
| ); |
| """) |
| |
| cur.execute(""" |
| CREATE TABLE IF NOT EXISTS sudoers ( |
| bot_username TEXT NOT NULL, |
| user_id INTEGER NOT NULL, |
| UNIQUE(bot_username, user_id) |
| ); |
| """) |
| conn.commit() |
| return conn |
|
|
| db = init_db() |
|
|
| |
| assistant_clients: Dict[str, Dict[str, Any]] = {} |
| assistant_lock = asyncio.Lock() |
| user_states: Dict[int, Dict[str, Any]] = {} |
|
|
| |
| master = TelegramClient(MASTER_BOT_SESSION, API_ID, API_HASH) |
|
|
| |
| def db_add_bot(owner_id: int, bot_username: str): |
| cur = db.cursor() |
| cur.execute("INSERT OR REPLACE INTO bots (owner_id, bot_username, token_encrypted, created_at) VALUES (?, ?, ?, ?)", |
| (owner_id, bot_username, b"SESSION_FILE", datetime.utcnow().isoformat())) |
| db.commit() |
|
|
| def db_set_active(bot_username: str, active: bool): |
| cur = db.cursor() |
| cur.execute("UPDATE bots SET active = ? WHERE bot_username = ?", (1 if active else 0, bot_username)) |
| db.commit() |
|
|
| def db_get_bot_record(bot_username: str): |
| cur = db.cursor() |
| cur.execute("SELECT owner_id, active FROM bots WHERE bot_username = ?", (bot_username,)) |
| r = cur.fetchone() |
| if not r: |
| return None |
| return {"owner_id": r[0], "active": bool(r[1])} |
|
|
| def db_get_bots_for_owner(owner_id: int): |
| cur = db.cursor() |
| cur.execute("SELECT bot_username FROM bots WHERE owner_id = ?", (owner_id,)) |
| return [row[0] for row in cur.fetchall()] |
|
|
| def db_remove_bot(bot_username: str): |
| cur = db.cursor() |
| cur.execute("DELETE FROM bots WHERE bot_username = ?", (bot_username,)) |
| db.commit() |
| try: |
| path = session_path_for_username(bot_username) |
| if os.path.exists(path): |
| os.remove(path) |
| except Exception as e: |
| logger.error(f"Failed to remove session file for {bot_username}: {e}") |
|
|
| def db_is_banned(user_id: int) -> bool: |
| cur = db.cursor() |
| cur.execute("SELECT 1 FROM bans WHERE user_id = ?", (user_id,)) |
| return cur.fetchone() is not None |
|
|
| |
| def db_add_sudo(bot_username: str, user_id: int): |
| cur = db.cursor() |
| cur.execute("INSERT OR IGNORE INTO sudoers (bot_username, user_id) VALUES (?, ?)", (bot_username, user_id)) |
| db.commit() |
|
|
| def db_remove_sudo(bot_username: str, user_id: int): |
| cur = db.cursor() |
| cur.execute("DELETE FROM sudoers WHERE bot_username = ? AND user_id = ?", (bot_username, user_id)) |
| db.commit() |
|
|
| def db_get_sudos(bot_username: str): |
| cur = db.cursor() |
| cur.execute("SELECT user_id FROM sudoers WHERE bot_username = ?", (bot_username,)) |
| return [row[0] for row in cur.fetchall()] |
|
|
| def session_path_for_username(username: str) -> str: |
| safe = username.replace("@", "") |
| return os.path.join(SESSIONS_DIR, f"{safe}.session") |
|
|
| async def send_to_log_channel(text: str, file=None, parse_mode=None): |
| try: |
| if file: |
| await master.send_file(LOG_CHANNEL_ID, file, caption=text, parse_mode=parse_mode) |
| else: |
| await master.send_message(LOG_CHANNEL_ID, text, parse_mode=parse_mode) |
| except Exception: |
| logger.exception("Failed to send log to channel") |
|
|
| |
| async def create_and_start_assistant(bot_username: str, owner_id: int) -> bool: |
| sess_path = session_path_for_username(bot_username) |
| if not os.path.exists(sess_path): |
| return False |
|
|
| client = TelegramClient(sess_path, API_ID, API_HASH) |
| |
| try: |
| await client.connect() |
| if not await client.is_user_authorized(): |
| await client.disconnect() |
| return False |
| me = await client.get_me() |
| bot_id = me.id |
| except Exception as e: |
| logger.exception("Failed to start assistant client for %s: %s", bot_username, e) |
| return False |
|
|
| |
| async def is_auth(event): |
| sender_id = event.sender_id |
| if sender_id == owner_id: return True |
| if sender_id == bot_id: return True |
| if sender_id in db_get_sudos(bot_username): return True |
| return False |
|
|
| |
| @client.on(events.NewMessage(pattern=r"^[.\/!]val(?:\s|$)")) |
| async def eval_handler(event): |
| if not await is_auth(event): return |
| |
| try: |
| cmd = event.text.split(maxsplit=1)[1] |
| except IndexError: |
| return await eor(event, "Please provide code to evaluate.") |
| |
| async with assistant_lock: |
| if bot_username in assistant_clients: |
| assistant_clients[bot_username]["last_used"] = time.time() |
|
|
| xx = None |
| mode = "" |
| spli = cmd.split() |
|
|
| async def get_(): |
| try: |
| return cmd.split(maxsplit=1)[1] |
| except IndexError: |
| await eor(event, "->> Wrong Format <<-") |
| return None |
|
|
| if spli[0] in ["-s", "--silent"]: |
| await event.delete() |
| mode = "silent" |
| elif spli[0] in ["-n", "-noedit"]: |
| mode = "no-edit" |
| xx = await event.reply("Running...") |
| elif spli[0] in ["-gs", "--source"]: |
| mode = "gsource" |
| elif spli[0] in ["-ga", "--args"]: |
| mode = "g-args" |
| |
| if mode: cmd = await get_() |
| if not cmd: return |
| |
| if not mode == "silent" and not xx: |
| xx = await eor(event, "Running...") |
| |
| if black: |
| try: |
| cmd = black.format_str(cmd, mode=black.Mode()) |
| except BaseException: pass |
| |
| reply_to_id = event.reply_to_msg_id or event |
| old_stderr, old_stdout = sys.stderr, sys.stdout |
| redirected_output = sys.stdout = StringIO() |
| redirected_error = sys.stderr = StringIO() |
| stdout, stderr, exc, timeg = None, None, None, None |
| tima = time.time() |
| |
| try: |
| value = await aexec(cmd, event) |
| except Exception: |
| value = None |
| exc = traceback.format_exc() |
| |
| tima = time.time() - tima |
| stdout = redirected_output.getvalue() |
| stderr = redirected_error.getvalue() |
| sys.stdout, sys.stderr = old_stdout, old_stderr |
| |
| if value: |
| try: |
| if mode == "gsource": |
| exc = inspect.getsource(value) |
| elif mode == "g-args": |
| args = inspect.signature(value).parameters.values() |
| name = getattr(value, "__name__", "") |
| exc = f"**{name}**\n\n" + "\n ".join([str(arg) for arg in args]) |
| except Exception: |
| exc = traceback.format_exc() |
| |
| evaluation = exc or stderr or stdout or _parse_eval(value) or "Evaluation finished." |
| |
| |
| tc = chr(96) * 3 |
|
|
| if mode == "silent": |
| if exc: |
| msg = f"β’ <b>EVAL ERROR\n\nβ’ CHAT:</b> <code>{get_display_name(event.chat)}</code> [<code>{event.chat_id}</code>]" |
| msg += f"\n\nβ <b>CODE:</b>\n<code>{cmd}</code>\n\nβ <b>ERROR:</b>\n<code>{exc}</code>" |
| if len(msg) > 4000: |
| with BytesIO(msg.encode()) as out_file: |
| out_file.name = "Eval-Error.txt" |
| return await send_to_log_channel(f"`{cmd}`", file=out_file) |
| await send_to_log_channel(msg, parse_mode="html") |
| return |
| |
| tmt = tima * 1000 |
| timef = time_formatter(tmt) |
| timeform = timef if not timef == "0s" else f"{tmt:.3f}ms" |
|
|
| |
| final_output = f"__βΊ__ **EVAL** (__in {timeform}__)\n{tc}python\n{cmd}\n{tc}\n\n __βΊ__ **OUTPUT**: \n{tc}\n{evaluation}\n{tc}\n" |
| |
| if len(final_output) > 4096: |
| final_output = evaluation |
| with BytesIO(str.encode(final_output)) as out_file: |
| out_file.name = "eval.txt" |
| capt = f"{tc}{cmd}{tc}" if len(cmd) < 998 else None |
| await client.send_file(event.chat_id, out_file, force_document=True, allow_cache=False, caption=capt, reply_to=reply_to_id) |
| if xx: return await xx.delete() |
| return |
| |
| if xx: await eor(xx, final_output) |
|
|
| |
| @client.on(events.NewMessage(pattern=r"^[.\/!]bash(?:\s|$)")) |
| async def bash_handler(event): |
| if not await is_auth(event): return |
| |
| try: |
| cmd = event.text.split(maxsplit=1)[1] |
| except IndexError: |
| return await eor(event, "Please provide a shell command to execute.") |
| |
| xx = await eor(event, "`Executing bash command...`") |
| |
| try: |
| process = await asyncio.create_subprocess_shell( |
| cmd, |
| stdout=asyncio.subprocess.PIPE, |
| stderr=asyncio.subprocess.PIPE |
| ) |
| stdout, stderr = await process.communicate() |
| |
| output = stdout.decode().strip() or stderr.decode().strip() or "Command executed successfully with no output." |
| |
| tc = chr(96) * 3 |
| final_output = f"**βΊ BASH**\n{tc}bash\n{cmd}\n{tc}\n\n**βΊ OUTPUT**\n{tc}\n{output}\n{tc}" |
| |
| if len(final_output) > 4000: |
| with BytesIO(output.encode()) as out_file: |
| out_file.name = "bash_output.txt" |
| await client.send_file(event.chat_id, out_file, caption=f"`{cmd}`") |
| await xx.delete() |
| else: |
| await eor(xx, final_output) |
| |
| except Exception as e: |
| err = traceback.format_exc() |
| tc = chr(96) * 3 |
| await eor(xx, f"**βΊ BASH ERROR**\n{tc}bash\n{cmd}\n{tc}\n\n**βΊ ERROR**\n{tc}\n{err}\n{tc}") |
| await send_to_log_channel(f"Bash Error on {bot_username}: {str(e)}") |
|
|
| |
| @client.on(events.NewMessage(pattern=r"^[.\/!]install$")) |
| async def install_handler(event): |
| if not await is_auth(event): return |
| |
| tc = chr(96) * 3 |
| if not event.is_reply: |
| example = ( |
| "**How to use /install:**\n\n" |
| "1. Reply to a `.py` file containing Telethon code.\n" |
| "2. The file will be downloaded and its code loaded dynamically.\n\n" |
| "**Example Plugin:**\n" |
| f"{tc}python\n" |
| "@client.on(events.NewMessage(pattern='/ping'))\n" |
| "async def ping(event):\n" |
| " await event.reply('Pong!')\n" |
| f"{tc}" |
| ) |
| return await eor(event, example) |
| |
| reply = await event.get_reply_message() |
| if not reply.file or not reply.file.name.endswith('.py'): |
| return await eor(event, "Please reply to a valid `.py` Python script.") |
| |
| xx = await eor(event, "`Installing plugin...`") |
| |
| plugin_name = reply.file.name |
| bot_plugin_dir = os.path.join(PLUGINS_DIR, bot_username.replace("@", "")) |
| os.makedirs(bot_plugin_dir, exist_ok=True) |
| |
| file_path = os.path.join(bot_plugin_dir, plugin_name) |
| await reply.download_media(file=file_path) |
| |
| try: |
| |
| |
| with open(file_path, "r", encoding="utf-8") as f: |
| code_str = f.read() |
| |
| exec_globals = { |
| "client": client, |
| "events": events, |
| "telethon": telethon, |
| "asyncio": asyncio, |
| "os": os, |
| "sys": sys |
| } |
| exec(code_str, exec_globals) |
| await eor(xx, f"β
**Successfully installed module:** `{plugin_name}`") |
| except Exception as e: |
| err = traceback.format_exc() |
| await eor(xx, f"β **Failed to install {plugin_name}**\n\n{tc}python\n{err}\n{tc}") |
| if os.path.exists(file_path): os.remove(file_path) |
|
|
| |
| @client.on(events.NewMessage(pattern=r"^[.\/!]promote(?:\s|$)")) |
| async def promote_handler(event): |
| if event.sender_id != owner_id and event.sender_id != bot_id: |
| return await eor(event, "Only the owner can promote users.") |
| |
| try: |
| target = event.text.split(maxsplit=1)[1] |
| entity = await client.get_entity(target) |
| db_add_sudo(bot_username, entity.id) |
| await eor(event, f"β
Successfully promoted `{entity.first_name}` to sudo.") |
| except IndexError: |
| await eor(event, "Provide a username or user ID.") |
| except Exception as e: |
| await eor(event, f"Error: {str(e)}") |
|
|
| @client.on(events.NewMessage(pattern=r"^[.\/!]demote(?:\s|$)")) |
| async def demote_handler(event): |
| if event.sender_id != owner_id and event.sender_id != bot_id: |
| return await eor(event, "Only the owner can demote users.") |
| |
| try: |
| target = event.text.split(maxsplit=1)[1] |
| entity = await client.get_entity(target) |
| db_remove_sudo(bot_username, entity.id) |
| await eor(event, f"β Successfully demoted `{entity.first_name}`.") |
| except IndexError: |
| await eor(event, "Provide a username or user ID.") |
| except Exception as e: |
| await eor(event, f"Error: {str(e)}") |
|
|
| |
| @client.on(events.NewMessage(pattern=r"^[.\/!]help$")) |
| async def help_handler(event): |
| if not await is_auth(event): return |
| |
| help_text = ( |
| f"π€ **{bot_username} Assistant Help Menu**\n\n" |
| "**Core Commands:**\n" |
| "πΉ `.val <code>` - Evaluate Python code.\n" |
| "πΉ `.bash <code>` - Execute shell/terminal commands.\n" |
| "πΉ `/install` - Reply to a `.py` file to load custom code.\n\n" |
| "**Admin Commands (Owner Only):**\n" |
| "πΉ `/promote <id/username>` - Allow a user to use this bot's commands.\n" |
| "πΉ `/demote <id/username>` - Revoke user's access.\n\n" |
| "*(Note: Telegram user accounts cannot send inline buttons, which is why this is a text menu!)*" |
| ) |
| await eor(event, help_text) |
|
|
| |
| async with assistant_lock: |
| if len(assistant_clients) >= MAX_ACTIVE_CLIENTS: |
| await client.disconnect() |
| return False |
| assistant_clients[bot_username] = {"client": client, "owner_id": owner_id, "last_used": time.time()} |
|
|
| logger.info("Assistant started: %s", bot_username) |
| return True |
|
|
| async def disconnect_assistant(bot_username: str): |
| async with assistant_lock: |
| info = assistant_clients.pop(bot_username, None) |
| if info: |
| try: |
| await info["client"].disconnect() |
| except Exception: |
| pass |
| db_set_active(bot_username, False) |
|
|
| async def cleanup_idle_clients(): |
| while True: |
| await asyncio.sleep(CLEANUP_INTERVAL) |
| now = time.time() |
| to_disconnect = [] |
| async with assistant_lock: |
| for uname, info in list(assistant_clients.items()): |
| if now - info.get("last_used", 0) > CLIENT_IDLE_TIMEOUT: |
| to_disconnect.append(uname) |
| for uname in to_disconnect: |
| await disconnect_assistant(uname) |
| logger.info("Idle assistant disconnected: %s", uname) |
|
|
| |
| def main_menu_buttons(): |
| return [ |
| [Button.inline("β Register (Send Token or Session File)", b"register")], |
| [Button.inline("π Connect / Disconnect", b"connect_menu")], |
| [Button.inline("π Manage bots", b"manage_menu")], |
| ] |
|
|
| def bot_action_buttons(bot_username: str): |
| return [ |
| [Button.inline("Connect", f"connect:{bot_username}"), Button.inline("Disconnect", f"disconnect:{bot_username}")], |
| [Button.inline("Remove Session", f"remove:{bot_username}")], |
| [Button.inline("Back", b"back_main")] |
| ] |
|
|
| |
| @master.on(events.NewMessage(pattern=r"^/start$|^/menu$")) |
| async def start_cmd(event): |
| if db_is_banned(event.sender_id): return |
| await event.reply("Welcome β Manage your evaluation userbots here.", buttons=main_menu_buttons()) |
|
|
| @master.on(events.NewMessage(pattern=r"^/status$")) |
| async def status_cmd(event): |
| if db_is_banned(event.sender_id): return |
| async with assistant_lock: |
| active = len(assistant_clients) |
| cur = db.cursor() |
| cur.execute("SELECT COUNT(*) FROM bots") |
| total = cur.fetchone()[0] |
| await event.reply(f"Master status\nActive Eval Clients: {active}\nTotal Registered: {total}") |
|
|
| |
| @master.on(events.NewMessage(incoming=True)) |
| async def generic_handler(event): |
| uid = event.sender_id |
| if db_is_banned(uid): return |
|
|
| state_info = user_states.get(uid, {}) |
| if state_info.get("state") == "await_session": |
| temp_path = os.path.join(SESSIONS_DIR, f"temp_{uid}_{int(time.time())}.session") |
| |
| if event.text and ":" in event.text: |
| |
| msg = await event.reply("βοΈ Generating session via API...") |
| success = generate_session_via_api(event.text.strip(), temp_path) |
| if not success: |
| return await msg.edit("β Failed to generate session via API. Please check your Bot Token.") |
| await msg.edit("β
Session generated. Validating...") |
| |
| elif event.file: |
| |
| if not getattr(event.file, "name", "").endswith(".session"): |
| return await event.reply("Please upload a `.session` file or send a Bot Token.") |
| msg = await event.reply("π₯ Downloading and validating session file...") |
| await event.message.download_media(file=temp_path) |
| |
| else: |
| return await event.reply("Please upload a `.session` file or send a valid Bot Token (e.g., `1234:ABC...`).") |
| |
| try: |
| temp_client = TelegramClient(temp_path, API_ID, API_HASH) |
| await temp_client.connect() |
| if not await temp_client.is_user_authorized(): |
| raise Exception("Session is invalid or revoked.") |
| |
| me = await temp_client.get_me() |
| bot_username = f"@{me.username or me.id}" |
| await temp_client.disconnect() |
| |
| final_path = session_path_for_username(bot_username) |
| if os.path.exists(final_path): |
| os.remove(final_path) |
| os.rename(temp_path, final_path) |
|
|
| db_add_bot(uid, bot_username) |
| success = await create_and_start_assistant(bot_username, uid) |
| |
| if success: |
| db_set_active(bot_username, True) |
| await msg.edit(f"β
Registered {bot_username} successfully!\nYou can now use `.val`, `.bash` and `/help` from that account.") |
| await send_to_log_channel(f"Master: User {uid} registered {bot_username}.") |
| else: |
| await msg.edit("Session valid, but failed to start client.") |
| |
| except Exception as e: |
| if os.path.exists(temp_path): os.remove(temp_path) |
| await msg.edit(f"Error: {str(e)}") |
| |
| finally: |
| user_states.pop(uid, None) |
|
|
| |
| @master.on(events.CallbackQuery) |
| async def callback_handler(event): |
| data = event.data.decode() |
| uid = event.sender_id |
| if db_is_banned(uid): return |
|
|
| if data == "register": |
| user_states[uid] = {"state": "await_session", "tmp": {}} |
| return await event.edit("π€ **Please send your Bot Token** or upload your `.session` file now.") |
|
|
| if data == "connect_menu": |
| bots = db_get_bots_for_owner(uid) |
| if not bots: |
| return await event.answer("No registered bots", alert=True) |
| rows = [[Button.inline(b, f"showbot:{b}")] for b in bots] |
| rows.append([Button.inline("Back", b"back_main")]) |
| return await event.edit("Select your session account:", buttons=rows) |
|
|
| if data.startswith("showbot:"): |
| bot_username = data.split(":", 1)[1] |
| rec = db_get_bot_record(bot_username) |
| if rec and rec["owner_id"] == uid: |
| status = "π’ Active" if rec["active"] else "π΄ Inactive" |
| await event.edit(f"**Session:** {bot_username}\n**Status:** {status}", buttons=bot_action_buttons(bot_username)) |
| return |
|
|
| if data == "back_main": |
| return await event.edit("Main menu:", buttons=main_menu_buttons()) |
|
|
| if data == "manage_menu": |
| return await event.edit("Manage:", buttons=[[Button.inline("List my sessions", b"connect_menu")], [Button.inline("Back", b"back_main")]]) |
|
|
| if data.startswith("connect:"): |
| bot_username = data.split(":", 1)[1] |
| if await create_and_start_assistant(bot_username, uid): |
| db_set_active(bot_username, True) |
| await event.answer("Connected!", alert=False) |
| await event.edit(f"π’ {bot_username} is now Online.", buttons=main_menu_buttons()) |
| else: |
| await event.answer("Failed to connect", alert=True) |
| return |
|
|
| if data.startswith("disconnect:"): |
| bot_username = data.split(":", 1)[1] |
| await disconnect_assistant(bot_username) |
| await event.answer("Disconnected", alert=False) |
| return await event.edit(f"π΄ {bot_username} is now Offline.", buttons=main_menu_buttons()) |
|
|
| if data.startswith("remove:"): |
| bot_username = data.split(":", 1)[1] |
| await disconnect_assistant(bot_username) |
| db_remove_bot(bot_username) |
| await event.answer("Removed", alert=False) |
| return await event.edit(f"ποΈ {bot_username} removed.", buttons=main_menu_buttons()) |
|
|
| |
| async def load_registered_bots(): |
| cur = db.cursor() |
| cur.execute("SELECT bot_username, owner_id FROM bots WHERE active=1") |
| for bot_username, owner_id in cur.fetchall(): |
| await create_and_start_assistant(bot_username, owner_id) |
| await asyncio.sleep(0.1) |
|
|
| async def main(): |
| |
| sess_file = MASTER_BOT_SESSION |
| if not sess_file.endswith(".session"): |
| sess_file += ".session" |
| |
| if not os.path.exists(sess_file): |
| logger.info(f"Master session not found. Generating via Render API with token: {MASTER_BOT_TOKEN[:10]}...") |
| success = generate_session_via_api(MASTER_BOT_TOKEN, sess_file) |
| if success: |
| logger.info("Successfully generated master session.") |
| else: |
| logger.error("Failed to generate master session. Ensure the MASTER_BOT_TOKEN is correct.") |
|
|
| await master.start(bot_token=MASTER_BOT_TOKEN) |
| logger.info("Master started.") |
| asyncio.create_task(load_registered_bots()) |
| asyncio.create_task(cleanup_idle_clients()) |
| await master.run_until_disconnected() |
|
|
| if __name__ == "__main__": |
| asyncio.run(main()) |