Major update bash install etc
Browse files
main.py
CHANGED
|
@@ -1,9 +1,9 @@
|
|
| 1 |
"""
|
| 2 |
-
Master Bot (Telethon) - Eval Userbot Edition
|
| 3 |
-
-
|
| 4 |
-
-
|
| 5 |
-
-
|
| 6 |
-
-
|
| 7 |
"""
|
| 8 |
|
| 9 |
import os
|
|
@@ -15,36 +15,39 @@ import inspect
|
|
| 15 |
import sys
|
| 16 |
import traceback
|
| 17 |
import json
|
|
|
|
| 18 |
from io import StringIO, BytesIO
|
| 19 |
from datetime import datetime
|
| 20 |
from typing import Dict, Any, Optional
|
| 21 |
|
|
|
|
| 22 |
from telethon import TelegramClient, events, Button
|
| 23 |
from telethon.utils import get_display_name
|
| 24 |
|
| 25 |
-
# Used for Formatting Eval Code, if installed
|
| 26 |
try:
|
| 27 |
import black
|
| 28 |
except ImportError:
|
| 29 |
black = None
|
| 30 |
|
| 31 |
# ---------------- CONFIG - EDIT THESE ----------------
|
| 32 |
-
API_ID = 22138159
|
| 33 |
-
API_HASH = "3fe4592e4cad72f366b6c564505f2d57"
|
| 34 |
|
| 35 |
MASTER_BOT_SESSION = "master.session"
|
| 36 |
DB_PATH = "masterbot.db"
|
| 37 |
SESSIONS_DIR = "sessions"
|
|
|
|
| 38 |
|
| 39 |
SUPER_ADMIN_ID = 7205748624
|
| 40 |
LOG_CHANNEL_ID = -1002524453831
|
| 41 |
|
| 42 |
-
CLIENT_IDLE_TIMEOUT = 60 * 60
|
| 43 |
CLEANUP_INTERVAL = 15 * 60
|
| 44 |
MAX_ACTIVE_CLIENTS = 60
|
| 45 |
# ---------------- END CONFIG ----------------
|
| 46 |
|
| 47 |
os.makedirs(SESSIONS_DIR, exist_ok=True)
|
|
|
|
| 48 |
logging.basicConfig(level=logging.INFO)
|
| 49 |
logger = logging.getLogger("masterbot")
|
| 50 |
|
|
@@ -129,7 +132,7 @@ def init_db():
|
|
| 129 |
bot_username TEXT NOT NULL UNIQUE,
|
| 130 |
token_encrypted BLOB NOT NULL,
|
| 131 |
active INTEGER NOT NULL DEFAULT 1,
|
| 132 |
-
reply_text TEXT,
|
| 133 |
created_at TEXT NOT NULL
|
| 134 |
);
|
| 135 |
""")
|
|
@@ -140,6 +143,14 @@ def init_db():
|
|
| 140 |
banned_at TEXT
|
| 141 |
);
|
| 142 |
""")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 143 |
conn.commit()
|
| 144 |
return conn
|
| 145 |
|
|
@@ -194,6 +205,22 @@ def db_is_banned(user_id: int) -> bool:
|
|
| 194 |
cur.execute("SELECT 1 FROM bans WHERE user_id = ?", (user_id,))
|
| 195 |
return cur.fetchone() is not None
|
| 196 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 197 |
def session_path_for_username(username: str) -> str:
|
| 198 |
safe = username.replace("@", "")
|
| 199 |
return os.path.join(SESSIONS_DIR, f"{safe}.session")
|
|
@@ -207,7 +234,7 @@ async def send_to_log_channel(text: str, file=None, parse_mode=None):
|
|
| 207 |
except Exception:
|
| 208 |
logger.exception("Failed to send log to channel")
|
| 209 |
|
| 210 |
-
# ---------------- Assistant lifecycle &
|
| 211 |
async def create_and_start_assistant(bot_username: str, owner_id: int) -> bool:
|
| 212 |
sess_path = session_path_for_username(bot_username)
|
| 213 |
if not os.path.exists(sess_path):
|
|
@@ -220,19 +247,30 @@ async def create_and_start_assistant(bot_username: str, owner_id: int) -> bool:
|
|
| 220 |
if not await client.is_user_authorized():
|
| 221 |
await client.disconnect()
|
| 222 |
return False
|
|
|
|
|
|
|
| 223 |
except Exception as e:
|
| 224 |
logger.exception("Failed to start assistant client for %s: %s", bot_username, e)
|
| 225 |
return False
|
| 226 |
|
| 227 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 228 |
@client.on(events.NewMessage(pattern=r"^[.\/!]val(?:\s|$)"))
|
| 229 |
async def eval_handler(event):
|
|
|
|
|
|
|
| 230 |
try:
|
| 231 |
cmd = event.text.split(maxsplit=1)[1]
|
| 232 |
except IndexError:
|
| 233 |
return await eor(event, "Please provide code to evaluate.")
|
| 234 |
-
|
| 235 |
-
# Update last used state
|
| 236 |
async with assistant_lock:
|
| 237 |
if bot_username in assistant_clients:
|
| 238 |
assistant_clients[bot_username]["last_used"] = time.time()
|
|
@@ -243,11 +281,10 @@ async def create_and_start_assistant(bot_username: str, owner_id: int) -> bool:
|
|
| 243 |
|
| 244 |
async def get_():
|
| 245 |
try:
|
| 246 |
-
|
| 247 |
except IndexError:
|
| 248 |
await eor(event, "->> Wrong Format <<-")
|
| 249 |
-
|
| 250 |
-
return cm
|
| 251 |
|
| 252 |
if spli[0] in ["-s", "--silent"]:
|
| 253 |
await event.delete()
|
|
@@ -260,10 +297,8 @@ async def create_and_start_assistant(bot_username: str, owner_id: int) -> bool:
|
|
| 260 |
elif spli[0] in ["-ga", "--args"]:
|
| 261 |
mode = "g-args"
|
| 262 |
|
| 263 |
-
if mode:
|
| 264 |
-
|
| 265 |
-
if not cmd:
|
| 266 |
-
return
|
| 267 |
|
| 268 |
if not mode == "silent" and not xx:
|
| 269 |
xx = await eor(event, "Running...")
|
|
@@ -271,12 +306,10 @@ async def create_and_start_assistant(bot_username: str, owner_id: int) -> bool:
|
|
| 271 |
if black:
|
| 272 |
try:
|
| 273 |
cmd = black.format_str(cmd, mode=black.Mode())
|
| 274 |
-
except BaseException:
|
| 275 |
-
pass
|
| 276 |
|
| 277 |
reply_to_id = event.reply_to_msg_id or event
|
| 278 |
-
old_stderr = sys.stderr
|
| 279 |
-
old_stdout = sys.stdout
|
| 280 |
redirected_output = sys.stdout = StringIO()
|
| 281 |
redirected_error = sys.stderr = StringIO()
|
| 282 |
stdout, stderr, exc, timeg = None, None, None, None
|
|
@@ -291,8 +324,7 @@ async def create_and_start_assistant(bot_username: str, owner_id: int) -> bool:
|
|
| 291 |
tima = time.time() - tima
|
| 292 |
stdout = redirected_output.getvalue()
|
| 293 |
stderr = redirected_error.getvalue()
|
| 294 |
-
sys.stdout = old_stdout
|
| 295 |
-
sys.stderr = old_stderr
|
| 296 |
|
| 297 |
if value:
|
| 298 |
try:
|
|
@@ -321,31 +353,158 @@ async def create_and_start_assistant(bot_username: str, owner_id: int) -> bool:
|
|
| 321 |
tmt = tima * 1000
|
| 322 |
timef = time_formatter(tmt)
|
| 323 |
timeform = timef if not timef == "0s" else f"{tmt:.3f}ms"
|
| 324 |
-
final_output = "__►__ **EVAL** (__in {}__)\n```python\n{}``` \n\n __►__ **OUTPUT**: \n```\n{}``` \n".format(
|
| 325 |
-
timeform,
|
| 326 |
-
cmd,
|
| 327 |
-
evaluation,
|
| 328 |
-
)
|
| 329 |
|
| 330 |
if len(final_output) > 4096:
|
| 331 |
final_output = evaluation
|
| 332 |
with BytesIO(str.encode(final_output)) as out_file:
|
| 333 |
out_file.name = "eval.txt"
|
| 334 |
-
await client.send_file(
|
| 335 |
-
|
| 336 |
-
out_file,
|
| 337 |
-
force_document=True,
|
| 338 |
-
allow_cache=False,
|
| 339 |
-
caption=f"```{cmd}```" if len(cmd) < 998 else None,
|
| 340 |
-
reply_to=reply_to_id,
|
| 341 |
-
)
|
| 342 |
-
if xx:
|
| 343 |
-
return await xx.delete()
|
| 344 |
return
|
| 345 |
|
| 346 |
-
if xx:
|
| 347 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 348 |
|
|
|
|
| 349 |
async with assistant_lock:
|
| 350 |
if len(assistant_clients) >= MAX_ACTIVE_CLIENTS:
|
| 351 |
await client.disconnect()
|
|
@@ -444,7 +603,7 @@ async def generic_handler(event):
|
|
| 444 |
|
| 445 |
if success:
|
| 446 |
db_set_active(bot_username, True)
|
| 447 |
-
await msg.edit(f"✅ Registered {bot_username} successfully!\nYou can now use `.val`
|
| 448 |
await send_to_log_channel(f"Master: User {uid} registered {bot_username}.")
|
| 449 |
else:
|
| 450 |
await msg.edit("Session valid, but failed to start client.")
|
|
@@ -529,4 +688,3 @@ async def main():
|
|
| 529 |
|
| 530 |
if __name__ == "__main__":
|
| 531 |
asyncio.run(main())
|
| 532 |
-
|
|
|
|
| 1 |
"""
|
| 2 |
+
Master Bot (Telethon) - Eval & Plugin Userbot Edition
|
| 3 |
+
- Upload a .session file to register an assistant.
|
| 4 |
+
- Assistants respond to .val, .bash, /install, /promote, /demote, /help
|
| 5 |
+
- Commands are restricted to the owner, the userbot account itself, and promoted users.
|
| 6 |
+
- Dynamic plugin loading via /install.
|
| 7 |
"""
|
| 8 |
|
| 9 |
import os
|
|
|
|
| 15 |
import sys
|
| 16 |
import traceback
|
| 17 |
import json
|
| 18 |
+
import importlib.util
|
| 19 |
from io import StringIO, BytesIO
|
| 20 |
from datetime import datetime
|
| 21 |
from typing import Dict, Any, Optional
|
| 22 |
|
| 23 |
+
import telethon
|
| 24 |
from telethon import TelegramClient, events, Button
|
| 25 |
from telethon.utils import get_display_name
|
| 26 |
|
|
|
|
| 27 |
try:
|
| 28 |
import black
|
| 29 |
except ImportError:
|
| 30 |
black = None
|
| 31 |
|
| 32 |
# ---------------- CONFIG - EDIT THESE ----------------
|
| 33 |
+
API_ID = 22138159
|
| 34 |
+
API_HASH = "3fe4592e4cad72f366b6c564505f2d57"
|
| 35 |
|
| 36 |
MASTER_BOT_SESSION = "master.session"
|
| 37 |
DB_PATH = "masterbot.db"
|
| 38 |
SESSIONS_DIR = "sessions"
|
| 39 |
+
PLUGINS_DIR = "plugins"
|
| 40 |
|
| 41 |
SUPER_ADMIN_ID = 7205748624
|
| 42 |
LOG_CHANNEL_ID = -1002524453831
|
| 43 |
|
| 44 |
+
CLIENT_IDLE_TIMEOUT = 60 * 60
|
| 45 |
CLEANUP_INTERVAL = 15 * 60
|
| 46 |
MAX_ACTIVE_CLIENTS = 60
|
| 47 |
# ---------------- END CONFIG ----------------
|
| 48 |
|
| 49 |
os.makedirs(SESSIONS_DIR, exist_ok=True)
|
| 50 |
+
os.makedirs(PLUGINS_DIR, exist_ok=True)
|
| 51 |
logging.basicConfig(level=logging.INFO)
|
| 52 |
logger = logging.getLogger("masterbot")
|
| 53 |
|
|
|
|
| 132 |
bot_username TEXT NOT NULL UNIQUE,
|
| 133 |
token_encrypted BLOB NOT NULL,
|
| 134 |
active INTEGER NOT NULL DEFAULT 1,
|
| 135 |
+
reply_text TEXT,
|
| 136 |
created_at TEXT NOT NULL
|
| 137 |
);
|
| 138 |
""")
|
|
|
|
| 143 |
banned_at TEXT
|
| 144 |
);
|
| 145 |
""")
|
| 146 |
+
# New table for promoted users
|
| 147 |
+
cur.execute("""
|
| 148 |
+
CREATE TABLE IF NOT EXISTS sudoers (
|
| 149 |
+
bot_username TEXT NOT NULL,
|
| 150 |
+
user_id INTEGER NOT NULL,
|
| 151 |
+
UNIQUE(bot_username, user_id)
|
| 152 |
+
);
|
| 153 |
+
""")
|
| 154 |
conn.commit()
|
| 155 |
return conn
|
| 156 |
|
|
|
|
| 205 |
cur.execute("SELECT 1 FROM bans WHERE user_id = ?", (user_id,))
|
| 206 |
return cur.fetchone() is not None
|
| 207 |
|
| 208 |
+
# Sudo Utilities
|
| 209 |
+
def db_add_sudo(bot_username: str, user_id: int):
|
| 210 |
+
cur = db.cursor()
|
| 211 |
+
cur.execute("INSERT OR IGNORE INTO sudoers (bot_username, user_id) VALUES (?, ?)", (bot_username, user_id))
|
| 212 |
+
db.commit()
|
| 213 |
+
|
| 214 |
+
def db_remove_sudo(bot_username: str, user_id: int):
|
| 215 |
+
cur = db.cursor()
|
| 216 |
+
cur.execute("DELETE FROM sudoers WHERE bot_username = ? AND user_id = ?", (bot_username, user_id))
|
| 217 |
+
db.commit()
|
| 218 |
+
|
| 219 |
+
def db_get_sudos(bot_username: str):
|
| 220 |
+
cur = db.cursor()
|
| 221 |
+
cur.execute("SELECT user_id FROM sudoers WHERE bot_username = ?", (bot_username,))
|
| 222 |
+
return [row[0] for row in cur.fetchall()]
|
| 223 |
+
|
| 224 |
def session_path_for_username(username: str) -> str:
|
| 225 |
safe = username.replace("@", "")
|
| 226 |
return os.path.join(SESSIONS_DIR, f"{safe}.session")
|
|
|
|
| 234 |
except Exception:
|
| 235 |
logger.exception("Failed to send log to channel")
|
| 236 |
|
| 237 |
+
# ---------------- Assistant lifecycle & COMMAND HANDLERS ----------------
|
| 238 |
async def create_and_start_assistant(bot_username: str, owner_id: int) -> bool:
|
| 239 |
sess_path = session_path_for_username(bot_username)
|
| 240 |
if not os.path.exists(sess_path):
|
|
|
|
| 247 |
if not await client.is_user_authorized():
|
| 248 |
await client.disconnect()
|
| 249 |
return False
|
| 250 |
+
me = await client.get_me()
|
| 251 |
+
bot_id = me.id
|
| 252 |
except Exception as e:
|
| 253 |
logger.exception("Failed to start assistant client for %s: %s", bot_username, e)
|
| 254 |
return False
|
| 255 |
|
| 256 |
+
# Authorization check function
|
| 257 |
+
async def is_auth(event):
|
| 258 |
+
sender_id = event.sender_id
|
| 259 |
+
if sender_id == owner_id: return True # Master owner
|
| 260 |
+
if sender_id == bot_id: return True # The userbot itself
|
| 261 |
+
if sender_id in db_get_sudos(bot_username): return True # Promoted users
|
| 262 |
+
return False
|
| 263 |
+
|
| 264 |
+
# 1. VAL SCRIPT (Eval)
|
| 265 |
@client.on(events.NewMessage(pattern=r"^[.\/!]val(?:\s|$)"))
|
| 266 |
async def eval_handler(event):
|
| 267 |
+
if not await is_auth(event): return
|
| 268 |
+
|
| 269 |
try:
|
| 270 |
cmd = event.text.split(maxsplit=1)[1]
|
| 271 |
except IndexError:
|
| 272 |
return await eor(event, "Please provide code to evaluate.")
|
| 273 |
+
|
|
|
|
| 274 |
async with assistant_lock:
|
| 275 |
if bot_username in assistant_clients:
|
| 276 |
assistant_clients[bot_username]["last_used"] = time.time()
|
|
|
|
| 281 |
|
| 282 |
async def get_():
|
| 283 |
try:
|
| 284 |
+
return cmd.split(maxsplit=1)[1]
|
| 285 |
except IndexError:
|
| 286 |
await eor(event, "->> Wrong Format <<-")
|
| 287 |
+
return None
|
|
|
|
| 288 |
|
| 289 |
if spli[0] in ["-s", "--silent"]:
|
| 290 |
await event.delete()
|
|
|
|
| 297 |
elif spli[0] in ["-ga", "--args"]:
|
| 298 |
mode = "g-args"
|
| 299 |
|
| 300 |
+
if mode: cmd = await get_()
|
| 301 |
+
if not cmd: return
|
|
|
|
|
|
|
| 302 |
|
| 303 |
if not mode == "silent" and not xx:
|
| 304 |
xx = await eor(event, "Running...")
|
|
|
|
| 306 |
if black:
|
| 307 |
try:
|
| 308 |
cmd = black.format_str(cmd, mode=black.Mode())
|
| 309 |
+
except BaseException: pass
|
|
|
|
| 310 |
|
| 311 |
reply_to_id = event.reply_to_msg_id or event
|
| 312 |
+
old_stderr, old_stdout = sys.stderr, sys.stdout
|
|
|
|
| 313 |
redirected_output = sys.stdout = StringIO()
|
| 314 |
redirected_error = sys.stderr = StringIO()
|
| 315 |
stdout, stderr, exc, timeg = None, None, None, None
|
|
|
|
| 324 |
tima = time.time() - tima
|
| 325 |
stdout = redirected_output.getvalue()
|
| 326 |
stderr = redirected_error.getvalue()
|
| 327 |
+
sys.stdout, sys.stderr = old_stdout, old_stderr
|
|
|
|
| 328 |
|
| 329 |
if value:
|
| 330 |
try:
|
|
|
|
| 353 |
tmt = tima * 1000
|
| 354 |
timef = time_formatter(tmt)
|
| 355 |
timeform = timef if not timef == "0s" else f"{tmt:.3f}ms"
|
| 356 |
+
final_output = "__►__ **EVAL** (__in {}__)\n```python\n{}``` \n\n __►__ **OUTPUT**: \n```\n{}``` \n".format(timeform, cmd, evaluation)
|
|
|
|
|
|
|
|
|
|
|
|
|
| 357 |
|
| 358 |
if len(final_output) > 4096:
|
| 359 |
final_output = evaluation
|
| 360 |
with BytesIO(str.encode(final_output)) as out_file:
|
| 361 |
out_file.name = "eval.txt"
|
| 362 |
+
await client.send_file(event.chat_id, out_file, force_document=True, allow_cache=False, caption=f"```{cmd}```" if len(cmd) < 998 else None, reply_to=reply_to_id)
|
| 363 |
+
if xx: return await xx.delete()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 364 |
return
|
| 365 |
|
| 366 |
+
if xx: await eor(xx, final_output)
|
| 367 |
+
|
| 368 |
+
# 2. BASH SCRIPT (Shell Execution)
|
| 369 |
+
@client.on(events.NewMessage(pattern=r"^[.\/!]bash(?:\s|$)"))
|
| 370 |
+
async def bash_handler(event):
|
| 371 |
+
if not await is_auth(event): return
|
| 372 |
+
|
| 373 |
+
try:
|
| 374 |
+
cmd = event.text.split(maxsplit=1)[1]
|
| 375 |
+
except IndexError:
|
| 376 |
+
return await eor(event, "Please provide a shell command to execute.")
|
| 377 |
+
|
| 378 |
+
xx = await eor(event, "`Executing bash command...`")
|
| 379 |
+
|
| 380 |
+
try:
|
| 381 |
+
process = await asyncio.create_subprocess_shell(
|
| 382 |
+
cmd,
|
| 383 |
+
stdout=asyncio.subprocess.PIPE,
|
| 384 |
+
stderr=asyncio.subprocess.PIPE
|
| 385 |
+
)
|
| 386 |
+
stdout, stderr = await process.communicate()
|
| 387 |
+
|
| 388 |
+
output = stdout.decode().strip() or stderr.decode().strip() or "Command executed successfully with no output."
|
| 389 |
+
|
| 390 |
+
final_output = f"**► BASH**\n```bash\n{cmd}```\n\n**► OUTPUT**\n```\n{output}```"
|
| 391 |
+
|
| 392 |
+
if len(final_output) > 4000:
|
| 393 |
+
with BytesIO(output.encode()) as out_file:
|
| 394 |
+
out_file.name = "bash_output.txt"
|
| 395 |
+
await client.send_file(event.chat_id, out_file, caption=f"`{cmd}`")
|
| 396 |
+
await xx.delete()
|
| 397 |
+
else:
|
| 398 |
+
await eor(xx, final_output)
|
| 399 |
+
|
| 400 |
+
except Exception as e:
|
| 401 |
+
err = traceback.format_exc()
|
| 402 |
+
await eor(xx, f"**► BASH ERROR**\n```bash\n{cmd}```\n\n**► ERROR**\n```\n{err}```")
|
| 403 |
+
await send_to_log_channel(f"Bash Error on {bot_username}: {str(e)}")
|
| 404 |
+
|
| 405 |
+
# 3. INSTALL PLUGIN
|
| 406 |
+
@client.on(events.NewMessage(pattern=r"^[.\/!]install$"))
|
| 407 |
+
async def install_handler(event):
|
| 408 |
+
if not await is_auth(event): return
|
| 409 |
+
|
| 410 |
+
if not event.is_reply:
|
| 411 |
+
example = (
|
| 412 |
+
"**How to use /install:**\n\n"
|
| 413 |
+
"1. Reply to a `.py` file containing Telethon code.\n"
|
| 414 |
+
"2. The file will be downloaded and its code loaded dynamically.\n\n"
|
| 415 |
+
"**Example Plugin:**\n"
|
| 416 |
+
"```python\n"
|
| 417 |
+
"@client.on(events.NewMessage(pattern='/ping'))\n"
|
| 418 |
+
"async def ping(event):\n"
|
| 419 |
+
" await event.reply('Pong!')\n"
|
| 420 |
+
"```"
|
| 421 |
+
)
|
| 422 |
+
return await eor(event, example)
|
| 423 |
+
|
| 424 |
+
reply = await event.get_reply_message()
|
| 425 |
+
if not reply.file or not reply.file.name.endswith('.py'):
|
| 426 |
+
return await eor(event, "Please reply to a valid `.py` Python script.")
|
| 427 |
+
|
| 428 |
+
xx = await eor(event, "`Installing plugin...`")
|
| 429 |
+
|
| 430 |
+
plugin_name = reply.file.name
|
| 431 |
+
bot_plugin_dir = os.path.join(PLUGINS_DIR, bot_username.replace("@", ""))
|
| 432 |
+
os.makedirs(bot_plugin_dir, exist_ok=True)
|
| 433 |
+
|
| 434 |
+
file_path = os.path.join(bot_plugin_dir, plugin_name)
|
| 435 |
+
await reply.download_media(file=file_path)
|
| 436 |
+
|
| 437 |
+
try:
|
| 438 |
+
# We inject 'client', 'events', and 'telethon' into the script's global namespace
|
| 439 |
+
# so the decorators in the raw script will attach directly to this specific assistant.
|
| 440 |
+
with open(file_path, "r", encoding="utf-8") as f:
|
| 441 |
+
code_str = f.read()
|
| 442 |
+
|
| 443 |
+
exec_globals = {
|
| 444 |
+
"client": client,
|
| 445 |
+
"events": events,
|
| 446 |
+
"telethon": telethon,
|
| 447 |
+
"asyncio": asyncio,
|
| 448 |
+
"os": os,
|
| 449 |
+
"sys": sys
|
| 450 |
+
}
|
| 451 |
+
exec(code_str, exec_globals)
|
| 452 |
+
await eor(xx, f"✅ **Successfully installed module:** `{plugin_name}`")
|
| 453 |
+
except Exception as e:
|
| 454 |
+
err = traceback.format_exc()
|
| 455 |
+
await eor(xx, f"❌ **Failed to install {plugin_name}**\n\n```python\n{err}```")
|
| 456 |
+
if os.path.exists(file_path): os.remove(file_path)
|
| 457 |
+
|
| 458 |
+
# 4. PROMOTE / DEMOTE
|
| 459 |
+
@client.on(events.NewMessage(pattern=r"^[.\/!]promote(?:\s|$)"))
|
| 460 |
+
async def promote_handler(event):
|
| 461 |
+
if event.sender_id != owner_id and event.sender_id != bot_id:
|
| 462 |
+
return await eor(event, "Only the owner can promote users.")
|
| 463 |
+
|
| 464 |
+
try:
|
| 465 |
+
target = event.text.split(maxsplit=1)[1]
|
| 466 |
+
entity = await client.get_entity(target)
|
| 467 |
+
db_add_sudo(bot_username, entity.id)
|
| 468 |
+
await eor(event, f"✅ Successfully promoted `{entity.first_name}` to sudo.")
|
| 469 |
+
except IndexError:
|
| 470 |
+
await eor(event, "Provide a username or user ID.")
|
| 471 |
+
except Exception as e:
|
| 472 |
+
await eor(event, f"Error: {str(e)}")
|
| 473 |
+
|
| 474 |
+
@client.on(events.NewMessage(pattern=r"^[.\/!]demote(?:\s|$)"))
|
| 475 |
+
async def demote_handler(event):
|
| 476 |
+
if event.sender_id != owner_id and event.sender_id != bot_id:
|
| 477 |
+
return await eor(event, "Only the owner can demote users.")
|
| 478 |
+
|
| 479 |
+
try:
|
| 480 |
+
target = event.text.split(maxsplit=1)[1]
|
| 481 |
+
entity = await client.get_entity(target)
|
| 482 |
+
db_remove_sudo(bot_username, entity.id)
|
| 483 |
+
await eor(event, f"❌ Successfully demoted `{entity.first_name}`.")
|
| 484 |
+
except IndexError:
|
| 485 |
+
await eor(event, "Provide a username or user ID.")
|
| 486 |
+
except Exception as e:
|
| 487 |
+
await eor(event, f"Error: {str(e)}")
|
| 488 |
+
|
| 489 |
+
# 5. HELP COMMAND
|
| 490 |
+
@client.on(events.NewMessage(pattern=r"^[.\/!]help$"))
|
| 491 |
+
async def help_handler(event):
|
| 492 |
+
if not await is_auth(event): return
|
| 493 |
+
|
| 494 |
+
help_text = (
|
| 495 |
+
f"🤖 **{bot_username} Assistant Help Menu**\n\n"
|
| 496 |
+
"**Core Commands:**\n"
|
| 497 |
+
"🔹 `.val <code>` - Evaluate Python code.\n"
|
| 498 |
+
"🔹 `.bash <code>` - Execute shell/terminal commands.\n"
|
| 499 |
+
"🔹 `/install` - Reply to a `.py` file to load custom code.\n\n"
|
| 500 |
+
"**Admin Commands (Owner Only):**\n"
|
| 501 |
+
"🔹 `/promote <id/username>` - Allow a user to use this bot's commands.\n"
|
| 502 |
+
"🔹 `/demote <id/username>` - Revoke user's access.\n\n"
|
| 503 |
+
"*(Note: Telegram user accounts cannot send inline buttons, which is why this is a text menu!)*"
|
| 504 |
+
)
|
| 505 |
+
await eor(event, help_text)
|
| 506 |
|
| 507 |
+
# --- Start Assistant Registration ---
|
| 508 |
async with assistant_lock:
|
| 509 |
if len(assistant_clients) >= MAX_ACTIVE_CLIENTS:
|
| 510 |
await client.disconnect()
|
|
|
|
| 603 |
|
| 604 |
if success:
|
| 605 |
db_set_active(bot_username, True)
|
| 606 |
+
await msg.edit(f"✅ Registered {bot_username} successfully!\nYou can now use `.val`, `.bash` and `/help` from that account.")
|
| 607 |
await send_to_log_channel(f"Master: User {uid} registered {bot_username}.")
|
| 608 |
else:
|
| 609 |
await msg.edit("Session valid, but failed to start client.")
|
|
|
|
| 688 |
|
| 689 |
if __name__ == "__main__":
|
| 690 |
asyncio.run(main())
|
|
|