# -*- coding: utf-8 -*- # CPM bot — "light" verzija bez aiogram/aiohttp (samo requests + stdlib) import json, re, time, base64, zlib, struct, hashlib, sqlite3, logging, traceback import random as _rnd_cars from datetime import datetime, timedelta from pathlib import Path from copy import deepcopy from typing import Any, Dict, List import requests requests.packages.urllib3.disable_warnings() try: import brotli HAS_BROTLI = True except ImportError: HAS_BROTLI = False try: from Crypto.Cipher import AES from Crypto.Util.Padding import unpad HAS_CRYPTO = True except ImportError: HAS_CRYPTO = False # ═══ CONFIG ═══ BOT_TOKEN = "8640157879:AAEJjsKhwNSfIYenkUb-NjZoUAu0ZtB171Y" OWNER_ID = 1482882642 RATE_LIMIT_ACTIONS = 10 RATE_LIMIT_SECONDS = 60 BULKADD_TIMEOUT_SECONDS = 180 FK = "AIzaSyAe_aOVT1gSfmHKBrorFvX4fRwN5nODXVA" LOAD_URL = "https://europe-west1-cp-multiplayer.cloudfunctions.net/GetPlayerRecords3" SAVE_URL = "https://europe-west1-cp-multiplayer.cloudfunctions.net/SavePlayerRecordsPartially8" GET_ALL_CARS_URL = "https://europe-west1-cp-multiplayer.cloudfunctions.net/TestGetAllCars" SET_USER_RATING_URL = "https://us-central1-cp-multiplayer.cloudfunctions.net/SetUserRating6" RANK_URL = SET_USER_RATING_URL CAR_IDS = { 0: "Car 0", 1: "Car 1", 2: "Car 2", # ... 273: "Car 273" } # ═══ FUNGSI PROSES MESEJ TELEGRAM ═══ def process_telegram_update(update: dict): """ Fungsi ini dipanggil secara automatik setiap kali Telegram hantar mesej baharu. Masukkan / kekalkan fungsi handler mesej bot anda di sini. """ try: if "message" in update: message = update["message"] chat_id = message.get("chat", {}).get("id") text = message.get("text", "") # Contoh tindak balas asas if text == "/start": payload = { "chat_id": chat_id, "text": "Selamat datang ke CPM Bot! Bot sedia digunakan." } requests.post(f"https://api.telegram.org/bot{BOT_TOKEN}/sendMessage", json=payload) # --- TAMPAL / KEKALKAN LOGIK UTAMA BOT ANDA DI SINI --- except Exception as e: logging.error(f"Error processing update: {e}") traceback.print_exc() # ═══ PELAYAN WEB HOOK (HTTP SERVER) ═══ class TelegramWebhookHandler(BaseHTTPRequestHandler): def do_POST(self): # Menerima data JSON yang dihantar oleh Telegram content_length = int(self.headers.get('Content-Length', 0)) post_data = self.rfile.read(content_length) try: if post_data: update = json.loads(post_data.decode('utf-8')) process_telegram_update(update) except Exception as e: logging.error(f"Error decoding webhook data: {e}") # Sentiasa hantar respons HTTP 200 OK ke Telegram self.send_response(200) self.send_header("Content-type", "text/plain") self.end_headers() self.wfile.write(b"OK") def do_GET(self): # Respons ringkas jika pautan dibuka melalui browser biasa self.send_response(200) self.end_headers() self.wfile.write(b"CPM Bot Webhook Server Active!") def run_webhook_server(port=5000): server_address = ('0.0.0.0', port) httpd = HTTPServer(server_address, TelegramWebhookHandler) print(f"[*] Server Webhook Bot sedang berjalan di port {port}...") httpd.serve_forever() if __name__ == "__main__": # Jalankan pelayan Webhook run_webhook_server(port=5000) MAX_MONEY = 50_000_000 MAX_COIN = 500_000 logging.basicConfig(level=logging.INFO, format="%(asctime)s | %(levelname)s | %(message)s", datefmt="%H:%M:%S") log = logging.getLogger("CPM") # ═══════════════════════════════════════════ # 🗄️ STORE # ═══════════════════════════════════════════ STORE_PATH = Path("cpm_store.json") DEFAULT_STORE: Dict[str, Any] = { "allowed_users": [], "vip_users": [], "admins": {}, "pending": {}, "banned": [], "expiry": {}, "stats": {"total_logins": 0, "total_actions": 0, "total_unlocks": 0}, "admin_log": [], "users": {}, "daily_stats": {}, "notes": {}, "warnings": {}, "maintenance": False, "broadcast_history": [], "bot_photo": "", } def load_store() -> Dict[str, Any]: try: if STORE_PATH.exists(): with STORE_PATH.open("r", encoding="utf-8") as f: data = json.load(f) for k, v in DEFAULT_STORE.items(): if k not in data: data[k] = deepcopy(v) data["admins"] = {str(k): v for k, v in data.get("admins", {}).items()} data["allowed_users"] = list({int(x) for x in data.get("allowed_users", [])}) data["vip_users"] = list({int(x) for x in data.get("vip_users", [])}) data["banned"] = list({int(x) for x in data.get("banned", [])}) return data save_store(DEFAULT_STORE) return deepcopy(DEFAULT_STORE) except Exception: save_store(DEFAULT_STORE) return deepcopy(DEFAULT_STORE) def save_store(data: Dict[str, Any]) -> bool: try: tmp = STORE_PATH.with_suffix(".json.tmp") with tmp.open("w", encoding="utf-8") as f: json.dump(data, f, indent=2, ensure_ascii=False) tmp.replace(STORE_PATH) return True except Exception as e: log.error(f"Save error: {e}") return False STORE = load_store() if OWNER_ID not in STORE["allowed_users"]: STORE["allowed_users"].append(OWNER_ID) if str(OWNER_ID) not in STORE["admins"]: STORE["admins"][str(OWNER_ID)] = "owner" save_store(STORE) ALLOWED_USERS: List[int] = list(STORE.get("allowed_users", [])) VIP_USERS: List[int] = list(STORE.get("vip_users", [])) ADMINS: Dict[int, str] = {int(k): v for k, v in STORE.get("admins", {}).items()} BANNED: List[int] = list(STORE.get("banned", [])) PENDING: Dict[str, Any] = STORE.get("pending", {}) EXPIRY: Dict[str, Any] = STORE.get("expiry", {}) RATE_DATA: Dict[str, Any] = {} ADMIN_LEVELS = {"owner": 100, "superadmin": 50, "admin": 10, "moderator": 5} def is_allowed(uid): return uid in ALLOWED_USERS def is_banned(uid): return uid in BANNED def is_pending(uid): return str(uid) in PENDING def is_vip(uid): return uid in VIP_USERS def is_maintenance(): return STORE.get("maintenance", False) def admin_level(uid): return ADMIN_LEVELS.get(ADMINS.get(uid, ""), 0) def admin_role(uid): return ADMINS.get(uid, "") def has_admin(uid, required="admin"): return admin_level(uid) >= ADMIN_LEVELS.get(required, 10) def get_bot_photo(): return STORE.get("bot_photo", "") def set_bot_photo(file_id: str): STORE["bot_photo"] = file_id save_store(STORE) def is_expired(uid): exp = EXPIRY.get(str(uid)) if not exp: return False try: return datetime.now() > datetime.fromisoformat(exp) except: return False def check_rate_limit(uid): now = time.time() key = str(uid) data = RATE_DATA.get(key, {"count": 0, "reset": now + RATE_LIMIT_SECONDS}) if now > data["reset"]: data = {"count": 0, "reset": now + RATE_LIMIT_SECONDS} if data["count"] >= RATE_LIMIT_ACTIONS: return False, int(data["reset"] - now) data["count"] += 1 RATE_DATA[key] = data return True, 0 def store_allow(uid, name="", save=True): global ALLOWED_USERS, STORE uid = int(uid) if uid in ALLOWED_USERS: return False ALLOWED_USERS.append(uid) STORE["allowed_users"] = list(ALLOWED_USERS) if save: save_store(STORE) return True def store_ban(uid): global BANNED, ALLOWED_USERS, STORE uid = int(uid) if uid in BANNED: return False BANNED.append(uid) if uid in ALLOWED_USERS: ALLOWED_USERS.remove(uid) STORE["banned"] = list(BANNED) STORE["allowed_users"] = list(ALLOWED_USERS) save_store(STORE); return True def store_unban(uid): global BANNED, STORE uid = int(uid) if uid not in BANNED: return False BANNED.remove(uid) STORE["banned"] = list(BANNED) save_store(STORE); return True def store_remove_user(uid): global ALLOWED_USERS, STORE uid = int(uid) if uid not in ALLOWED_USERS: return False ALLOWED_USERS = [x for x in ALLOWED_USERS if x != uid] STORE["allowed_users"] = list(ALLOWED_USERS) save_store(STORE); return True def store_add_admin(uid, role="admin"): global ADMINS, STORE uid = int(uid) role = role if role in ADMIN_LEVELS else "admin" store_allow(uid) ADMINS[uid] = role STORE["admins"] = {str(k): v for k, v in ADMINS.items()} save_store(STORE); return True def store_remove_admin(uid): global ADMINS, STORE uid = int(uid) if uid not in ADMINS: return False ADMINS.pop(uid, None) STORE["admins"] = {str(k): v for k, v in ADMINS.items()} save_store(STORE); return True def store_add_pending(uid, name, username=""): global PENDING, STORE if str(uid) in PENDING: return False PENDING[str(uid)] = {"name": name, "username": username, "time": datetime.now().isoformat()} STORE["pending"] = PENDING save_store(STORE); return True def store_remove_pending(uid): global PENDING, STORE PENDING.pop(str(uid), None) STORE["pending"] = PENDING save_store(STORE) def store_add_vip(uid): global VIP_USERS, STORE uid = int(uid) if uid in VIP_USERS: return False VIP_USERS.append(uid); store_allow(uid) STORE["vip_users"] = list(VIP_USERS) save_store(STORE); return True def store_remove_vip(uid): global VIP_USERS, STORE uid = int(uid) if uid not in VIP_USERS: return False VIP_USERS.remove(uid) STORE["vip_users"] = list(VIP_USERS) save_store(STORE); return True def store_set_expiry(uid, days): global EXPIRY, STORE EXPIRY[str(uid)] = (datetime.now() + timedelta(days=days)).isoformat() STORE["expiry"] = EXPIRY; save_store(STORE) def store_remove_expiry(uid): global EXPIRY, STORE EXPIRY.pop(str(uid), None) STORE["expiry"] = EXPIRY; save_store(STORE) def store_get_warnings(uid): return STORE.get("warnings", {}).get(str(uid), []) def store_set_note(uid, note): STORE.setdefault("notes", {})[str(uid)] = note save_store(STORE) def store_get_note(uid): return STORE.get("notes", {}).get(str(uid), "") def admin_log(actor_id, action, target=""): STORE.setdefault("admin_log", []).insert(0, { "time": datetime.now().isoformat(), "actor": actor_id, "action": action, "target": target, }) STORE["admin_log"] = STORE["admin_log"][:200] save_store(STORE) def add_broadcast_history(actor, msg_type, text, sent, failed): bh = STORE.setdefault("broadcast_history", []) bh.insert(0, {"time": datetime.now().isoformat(), "actor": actor, "type": msg_type, "text": text[:50], "sent": sent, "failed": failed}) STORE["broadcast_history"] = bh[:20]; save_store(STORE) def update_daily_stats(key="actions"): today = datetime.now().strftime("%Y-%m-%d") ds = STORE.setdefault("daily_stats", {}) td = ds.setdefault(today, {"actions": 0, "logins": 0, "unlocks": 0}) td[key] = td.get(key, 0) + 1; save_store(STORE) # ═══ CRYPTO ═══ def make_xor_key(uid: str) -> bytes: chars = list(uid) if len(chars) >= 9: chars[1], chars[8] = chars[8], chars[1] if len(chars) >= 3: chars.pop(2) if len(chars) >= 5: chars.append(chars[4]) return "".join(chars).encode("utf-8") def xor_bytes(data: bytes, key: bytes) -> bytes: return bytes(data[i] ^ key[i % len(key)] for i in range(len(data))) def decompress(data: bytes): if HAS_BROTLI: try: return brotli.decompress(data) except Exception: pass try: return zlib.decompress(data, zlib.MAX_WBITS | 16) except Exception: pass try: return zlib.decompress(data) except Exception: pass return None def decrypt_aes(data: bytes, key: bytes): if not HAS_CRYPTO: return None try: cipher = AES.new(key[:16], AES.MODE_CBC, b"\x00" * 16) return unpad(cipher.decrypt(data), 16) except Exception: return None def _md5(t): return hashlib.md5(t.encode()).digest() def _sha1(t): return hashlib.sha1(t.encode()).digest()[:16] def build_aes_keys(uid, password=None, email=None): keys = [_md5("olzhas_carparking")] if password: keys += [_md5(password), _sha1(password)] if uid: keys += [_md5(uid), _sha1(uid)] if email: keys.append(_md5(email)) return keys class Reader: def __init__(self, data): self.buf = data; self.pos = 0 def has_bytes(self, n): return self.pos + n <= len(self.buf) def read_byte(self): if not self.has_bytes(1): return 0 v = self.buf[self.pos]; self.pos += 1; return v def read_int(self): if not self.has_bytes(4): self.pos = len(self.buf); return 0 v = struct.unpack_from(" 1_000_000: length = 1_000_000 if not self.has_bytes(length): return "" text = self.buf[self.pos:self.pos + length].decode("utf-8", errors="replace") self.pos += length return text.replace("\x00", "").strip() def read_list(self, item_fn): count = self.read_int() if count <= 0 or count > 1_000_000: return [] result = [] for _ in range(count): if self.pos >= len(self.buf): break v = item_fn() if v is not None: result.append(v) return result def read_dict(self): count = self.read_int() if count <= 0 or count > 1_000_000: return {} d = {} for _ in range(count): if self.pos >= len(self.buf): break d[self.read_int()] = self.read_int() return d def read_equipment(self): if self.read_byte() == 0: return None return { "hair": self.read_list(self.read_int), "face": self.read_list(self.read_int), "beard": self.read_list(self.read_int), "cap": self.read_list(self.read_int), "mask": self.read_list(self.read_int), "top": self.read_list(self.read_int), "gloves": self.read_list(self.read_int), "bag": self.read_list(self.read_int), "pants": self.read_list(self.read_int), "shoes": self.read_list(self.read_int), "glasses": self.read_list(self.read_int), "SelectedEquipments": self.read_list(self.read_int), "Gender": self.read_int(), } def parse_player(buf): r = Reader(buf) if r.read_byte() == 0: return None p = {} p["Name"] = r.read_string(); p["money"] = r.read_int() p["coin"] = r.read_int(); p["localID"] = r.read_string() p["boughtFsos"] = r.read_list(r.read_int) def read_friend(): r.read_byte() return {"id": r.read_string(), "Name": r.read_string(), "accountID": r.read_string()} p["FriendsID"] = r.read_list(read_friend) p["LevelsDoneTime"] = r.read_list(r.read_float) p["floats"] = r.read_list(r.read_float) p["integers"] = r.read_list(r.read_int) p["fcar"] = r.read_list(r.read_int) p["favouriteWheels"] = r.read_list(r.read_int) p["favouriteVinyls"] = r.read_list(r.read_int) p["favouriteEmojis"] = r.read_list(r.read_int) p["personEquipmentsMale"] = r.read_equipment() p["personEquipmentsFemale"] = r.read_equipment() if r.read_byte() == 0: p["platesData"] = None else: def read_vinyl(): r.read_byte() def rv(): return {"x": r.read_float(), "y": r.read_float(), "z": r.read_float()} return {"vectors": r.read_list(rv), "v": r.read_list(r.read_string), "floats": r.read_list(r.read_float), "text": r.read_string()} def read_plate(): r.read_byte() return {"plateId": r.read_int(), "frontCarId": r.read_int(), "rearCarId": r.read_int(), "vinyls": r.read_list(read_vinyl)} p["platesData"] = {"allPlates": r.read_list(read_plate)} if r.read_byte() == 0: p["carIDnStatus"] = None else: p["carIDnStatus"] = {"carGeneratedIDs": r.read_list(r.read_string), "carStatus": r.read_list(r.read_int)} p["allData"] = r.read_string() p["flags"] = r.read_dict() p["animations"] = r.read_list(r.read_int) p["emojiPacks"] = r.read_list(r.read_int) p["wheels"] = r.read_list(r.read_int) p["boughtPoliceLights"] = r.read_list(r.read_int) p["boughtPoliceSirens"] = r.read_list(r.read_int) return p def try_parse(buf): candidates = [buf] d1 = decompress(buf) if d1: candidates.append(d1) d2 = decompress(d1) if d2: candidates.append(d2) for c in candidates: if not c: continue if len(c) > 0 and c[0] in (17, 23, 24): try: p = parse_player(c) if p and p.get("Name") is not None: return p except Exception: pass try: clean = c[3:] if (len(c) >= 3 and c[0] == 0xef and c[1] == 0xbb) else c if clean[0] == 123: return json.loads(clean.decode("utf-8")) except Exception: pass return None def decrypt_player_record(base64_text, uid, password=None, email=None): try: buf = base64.b64decode(base64_text) except Exception: return {"success": False, "message": "Bad base64"} if len(buf) < 10: return {"success": False, "message": "Too small"} direct = try_parse(buf) if direct: return {"success": True, "record": direct} if uid: try: xp = xor_bytes(buf, make_xor_key(uid)) d = decompress(xp) if d: p = try_parse(d) if p: return {"success": True, "record": p} except Exception: pass for key in build_aes_keys(uid or "", password, email): plain = decrypt_aes(buf, key) if not plain: continue p = try_parse(plain) if p: return {"success": True, "record": p} return {"success": False, "message": "Could not decrypt"} class Writer: def __init__(self): self._p = [] def write_byte(self, v): self._p.append(bytes([v & 0xFF])) def write_int(self, v): self._p.append(struct.pack(" 0 elif original is not None: should = _field_modified(value, original.get(key)) else: should = True if not should: continue raw = serialize_field(fid, value) if raw is not None: fields.append((fid, raw)) parts = [struct.pack(" 0 else 0) save_store(STORE) update_daily_stats("unlocks") return { "ok": success > 0, "message": f"{success}/270 cars unlocked (fail {failed})" } def unlock_paid_cars(self, uid): return self.unlock_all_cars(uid) def clone_account(self, uid, t_email, t_password): self.load(uid) td = self.get_token_data(uid) s_email = td.get("email") if td else None src = deepcopy(self.get_record(uid, s_email)) if not src or not src.get("Name"): return {"ok":False,"message":"Could not load source account. Try Refresh first."} r = self.login(t_email, t_password) if not r.get("ok"): return {"ok":False,"message":"Target login failed: " + str(r.get("message",""))} t_auth = r["auth"]; t_fuid = r.get("firebase_uid","") resp = self._post(LOAD_URL, {"data":None}, {**GAME_HEADERS, "Authorization":f"Bearer {t_auth}"}) if not resp or not resp.get("result"): return {"ok":False,"message":"Could not load target account data."} dec = decrypt_player_record(resp["result"], t_fuid, t_password, t_email) orig = dec.get("record") if dec.get("success") else None new = deepcopy(src) if orig and orig.get("localID"): new["localID"] = orig["localID"] ok, msg = self._send(t_auth, new, t_fuid, orig) if ok: return {"ok":True} return {"ok":False,"message":msg} def complete_all_levels(self, uid): lvl = [0] + [120 if i==43 else 1 for i in range(1,110)] return self._modify(uid, {"LevelsDoneTime": lvl}) def set_rank(self, uid): self.load(uid) ok, msg, auth = self.get_auth(uid) if not ok: return {"ok":False,"message":msg} rd = {"RatingData":{"time":1e22,"cars":1e16,"car_fix":1e13,"car_collided":1e12, "car_exchange":1e13,"car_trade":1e13,"car_wash":1e13,"slicer_cut":1e13, "drift_max":1e14,"drift":1e14,"cargo":1e5,"delivery":1e5,"race_win":3e20, "taxi":1e10,"levels":10000990000,"gifts":1e9,"fuel":1e10,"offroad":1e10, "speed_banner":1e9,"reactions":1e17,"run":1e9,"real_estate":1e9, "t_distance":1e10,"treasure":1e10,"block_post":1e10,"push_ups":1e12, "burnt_tire":1e10,"passanger_distance":1e8}} r = self._post(RANK_URL, {"data":json.dumps(rd)}, {**GAME_HEADERS, "Authorization":f"Bearer {auth}"}) if r and self._ok(r): STORE["stats"]["total_unlocks"] = STORE["stats"].get("total_unlocks",0)+1 save_store(STORE); return {"ok":True} return {"ok":False,"message":"RANK_FAILED"} def fix_account(self, uid): self.load(uid) td = self.get_token_data(uid) email = td.get("email") if td else None d = deepcopy(self.get_record(uid, email)) if not d or not d.get("Name"): return {"ok":False,"message":"Could not load account data."} bugs = 0 fl = (d.get("floats",[]))[:54] while len(fl) < 54: fl.append(0.0) fixed_fl = [] for v in fl: if v in (1, 1.0): fixed_fl.append(1.0) elif isinstance(v, (int, float)) and v > 1: bugs += 1; fixed_fl.append(0.0) else: fixed_fl.append(float(v) if v else 0.0) it = (d.get("integers",[]))[:120] while len(it) < 120: it.append(0) fixed_it = [] for v in it: if v == 1: fixed_it.append(1) elif isinstance(v, (int, float)) and v > 1: bugs += 1; fixed_it.append(0) else: fixed_it.append(int(v) if v else 0) d["floats"] = fixed_fl; d["integers"] = fixed_it result = self._save(uid, d) return {"ok":True,"bugs_fixed":bugs} if result.get("ok") else {"ok":False,"message":"FIX_FAILED"} nuker = CPMNuker() # ═══ TELEGRAM LAYER (raw Bot API, bez aiogram) ═══ API = f"https://api.telegram.org/bot{BOT_TOKEN}" def tg_call(method, **params): try: r = requests.post(f"{API}/{method}", json=params, timeout=40, verify=False) return r.json() except Exception as e: log.error(f"TG {method}: {e}") return {} def send(chat_id, text, kb=None): p = {"chat_id": chat_id, "text": text} if kb: p["reply_markup"] = json.dumps(kb) return tg_call("sendMessage", **p) def edit(chat_id, mid, text, kb=None): p = {"chat_id": chat_id, "message_id": mid, "text": text} if kb: p["reply_markup"] = json.dumps(kb) return tg_call("editMessageText", **p) def answer_cb(cb_id, text=None, alert=False): p = {"callback_query_id": cb_id} if text: p["text"] = text; p["show_alert"] = alert tg_call("answerCallbackQuery", **p) def ikb(rows): return {"inline_keyboard": [[{"text": t, "callback_data": d} for t, d in row] for row in rows]} def kb_url(text, url): return {"inline_keyboard": [[{"text": text, "url": url}]]} B = "┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅" def hdr(icon, title): return f"{B}\n {icon} {title}\n{B}" def fmt(n): return f"{int(n):,}" class T: @staticmethod def welcome(name, username, uid): now = datetime.now() return (f"{B}\n🔥 𝗣𝗥𝗜𝗠𝗢𝗖𝗣𝗠𝗧𝗢𝗢𝗟 🔥\n{B}\n\n" f" ╭──── 𝗪𝗘𝗟𝗖𝗢𝗠𝗘 ────╮\n" f" │ 👤 {name}\n │ 📱 @{username or 'N/A'}\n │ 🆔 {uid}\n" f" │ 📅 {now.strftime('%d %b %Y • %I:%M %p')}\n" f" ╰─────────────────╯\n\n ▸ Sign in with your CPM credentials") @staticmethod def no_access(): return f"{B}\n 🔒 𝗔𝗖𝗖𝗘𝗦𝗦 𝗥𝗘𝗤𝗨𝗜𝗥𝗘𝗗\n{B}\n\n You don't have access yet.\n Tap below to request access." @staticmethod def banned(): return f"{B}\n 🚫 𝗕𝗔𝗡𝗡𝗘𝗗\n{B}\n\n Your access has been revoked." @staticmethod def maintenance(): return f"{B}\n 🔧 𝗠𝗔𝗜𝗡𝗧𝗘𝗡𝗔𝗡𝗖𝗘\n{B}\n\n Bot under maintenance. Try later." @staticmethod def login_fail(reason): err_map = {"EMAIL_NOT_FOUND":"Email not found","INVALID_PASSWORD":"Wrong password", "INVALID_LOGIN_CREDENTIALS":"Invalid credentials","TOO_MANY_ATTEMPTS":"Too many attempts, wait", "USER_DISABLED":"Account disabled","INVALID_EMAIL":"Invalid email format","NETWORK_ERROR":"Network error, try again"} clean = reason.replace("LOGIN_FAILED: ","") if "LOGIN_FAILED:" in reason else reason return f"{B}\n ❌ 𝗟𝗢𝗚𝗜𝗡 𝗙𝗔𝗜𝗟𝗘𝗗\n{B}\n\n ✗ {err_map.get(reason, clean)}\n\n Tap 🔐 Sign In to retry." @staticmethod def dashboard(record, email, uid): name=record.get("Name","Unknown"); pid=record.get("localID","—"); money=record.get("money",0); coin=record.get("coin",0) floats=record.get("floats",[]); wheels=record.get("wheels",[]); anims=record.get("animations",[]) wins=int(floats[8]) if len(floats)>8 else 0; loses=int(floats[9]) if len(floats)>9 else 0 levels=record.get("LevelsDoneTime",[]); done=sum(1 for x in levels if x and x>0) if levels else 0 friends=len(record.get("FriendsID",[])); badge=" 💎" if is_vip(uid) else "" exp_txt="" if str(uid) in EXPIRY: try: exp_txt=f"\n ⏰ Expires in {(datetime.fromisoformat(EXPIRY[str(uid)])-datetime.now()).days} days" except Exception: pass return (f"{B}\n 🏠 𝗗𝗔𝗦𝗛𝗕𝗢𝗔𝗥𝗗{badge}\n{B}\n\n" f" ╭──── 𝗔𝗖𝗖𝗢𝗨𝗡𝗧 ────╮\n │ 📧 {email}\n │ 👤 {name}\n │ 🆔 {pid}\n ╰─────────────────╯\n\n" f" ╭──── 𝗦𝗧𝗔𝗧𝗦 ──────╮\n │ 💰 ${money:,}\n │ 🪙 {coin:,} coins\n │ 🏆 {wins:,}W / {loses:,}L\n" f" │ 🎮 {done} levels done\n │ 🛞 {len(wheels)} wheels\n │ 🎭 {len(anims)} animations\n │ 👥 {friends} friends\n ╰─────────────────╯{exp_txt}\n\n ▸ Select an option below:") @staticmethod def admin_panel(uid): role=admin_role(uid); badge={"owner":"👑 Owner","superadmin":"⭐ Super Admin","admin":"🛡 Admin","moderator":"👮 Moderator"}.get(role,"❓") maint="🔴 ON" if is_maintenance() else "🟢 OFF" return (f"{B}\n 👑 𝗔𝗗𝗠𝗜𝗡 𝗣𝗔𝗡𝗘𝗟\n{B}\n\n ◆ Role: {badge}\n ◆ Maintenance: {maint}\n\n" f" 👥 Users {len(ALLOWED_USERS)}\n ⏳ Pending {len(PENDING)}\n 🚫 Banned {len(BANNED)}\n 💎 VIP {len(VIP_USERS)}\n 🛡 Admins {len(ADMINS)}") @staticmethod def stats(): s=STORE.get("stats",{}); ds=STORE.get("daily_stats",{}); td=ds.get(datetime.now().strftime("%Y-%m-%d"),{}) return (f"{B}\n 📊 𝗦𝗧𝗔𝗧𝗜𝗦𝗧𝗜𝗖𝗦\n{B}\n\n 👥 Users {len(ALLOWED_USERS)}\n 💎 VIP {len(VIP_USERS)}\n" f" ⏳ Pending {len(PENDING)}\n 🚫 Banned {len(BANNED)}\n\n ⚡ Actions {s.get('total_actions',0)}\n" f" 🔓 Unlocks {s.get('total_unlocks',0)}\n 🔐 Logins {s.get('total_logins',0)}\n\n 📅 Today {td.get('actions',0)} actions") @staticmethod def request_to_admin(name, username, uid): return (f"{B}\n 🔔 𝗡𝗘𝗪 𝗥𝗘𝗤𝗨𝗘𝗦𝗧\n{B}\n\n 👤 Name: {name}\n 📱 Username: @{username or 'N/A'}\n" f" 🆔 ID: {uid}\n 📅 {datetime.now().strftime('%d %b %Y • %I:%M %p')}\n\n ▸ Select action:") class K: @staticmethod def no_access(): return ikb([[("🔑 Request Access","send_request")],[("💬 Contact Admin","msg_admin")]]) @staticmethod def after_request(): return ikb([[("🔄 Check Status","check_status")],[("💬 Contact Admin","msg_admin")]]) @staticmethod def login(): return ikb([[("🔐 Sign In","login")]]) @staticmethod def cancel(): return ikb([[("✗ Cancel","cancel")]]) @staticmethod def home(uid=0): rows=[[("💰 Money","menu_money"),("🪙 Coins","menu_coins")],[("⚡ Features","menu_feat"),("🔧 Settings","menu_set")],[("🔄 Refresh Account","refresh")]] if has_admin(uid,"moderator"): rows.append([('👑 Admin Panel','admin_menu')]) rows.append([('🚪 Sign Out','logout')]); return ikb(rows) @staticmethod def money(): return ikb([[("$1M","m_1000000"),("$5M","m_5000000"),("$10M","m_10000000")],[("$25M","m_25000000"),("$50M ★","m_50000000")],[("✏ Custom Amount","m_custom")],[("◂ Back","back_home")]]) @staticmethod def coins(): return ikb([[("100K","c_100000"),("250K","c_250000"),("500K ★","c_500000")],[("✏ Custom Amount","c_custom")],[("◂ Back","back_home")]]) @staticmethod def feat(): return ikb([[("🚗 W16","f_w16"),("🔊 Horns","f_horns")],[("🛡 No Dmg","f_damage"),("⛽ Fuel","f_fuel")],[("💨 Smoke","f_smoke"),("🎭 Anims","f_anims")],[("🛞 Wheels","f_wheels"),("🏠 Houses","f_houses")],[("🎮 Levels","f_levels"),("🏅 Rank","f_rank")],[("🚘 All Cars","f_cars"),("💎 Paid Cars","f_paid")],[("🚀 ★ UNLOCK ALL ★","f_all")],[("◂ Back","back_home")]]) @staticmethod def sett(): return ikb([[("✏ Name","s_name"),("🆔 Player ID","s_pid")],[("🏆 Wins","s_wins"),("😞 Loses","s_loses")],[("🔧 Fix Account Bugs","s_fix")],[("📋 Clone Account","s_clone")],[("◂ Back","back_home")]]) @staticmethod def admin(uid): lvl=admin_level(uid); b=[] if lvl>=5: b += [[('📊 Stats','a_stats'),('👥 Users','a_users')],[('📋 Activity Log','a_log')]] if lvl>=10: b += [[('⏳ Pending Requests','a_pend')],[('➕ Add','a_adduser'),('📥 Bulk Add','a_bulkadd'),('🚫 Ban','a_ban'),('🔓 Unban','a_unban')],[('👢 Kick','a_kick'),('⏰ Expiry','a_expiry'),('ℹ Profile','a_profile')]] if lvl>=50: b += [[('💎 +VIP','a_addvip'),('💎 -VIP','a_rmvip')],[('📢 Broadcast','a_bcast_menu')]] if lvl>=100: b += [[('➕ Add Admin','a_addadm'),('➖ Rem Admin','a_rmadm')],[('🖼 Update Photo','a_photo'),('🔧 Maintenance','a_maint')],[('🔄 Reset Stats','a_reset')]] b.append([('◂ Home','back_home')]); return ikb(b) @staticmethod def request_actions(uid): return ikb([[('✅ Accept',f'rq_accept_{uid}'),('❌ Reject',f'rq_reject_{uid}'),('🚫 Ban',f'rq_ban_{uid}')]]) @staticmethod def broadcast_menu(): return ikb([[('📝 Text Message','bcast_text')],[('🖼 Photo + Caption','bcast_photo')],[('💎 VIP Only','bcast_vip')],[('◂ Back','admin_menu')]]) @staticmethod def back_admin(): return ikb([[('◂ Admin Panel','admin_menu')]]) @staticmethod def back_home(): return ikb([[('◂ Home','back_home')]]) @staticmethod def confirm_logout(): return ikb([[('✔ Yes','do_logout'),('✗ No','back_home')]]) @staticmethod def pending_list(): b=[] for uid_str,info in list(PENDING.items())[:15]: uid_int=int(uid_str); name=info.get('name',f'User {uid_int}')[:16] b.append([(f'✅ {name}',f'rq_accept_{uid_int}'),('❌',f'rq_reject_{uid_int}'),('🚫',f'rq_ban_{uid_int}')]) b.append([('◂ Back','admin_menu')]); return ikb(b) @staticmethod def roles(uid): return ikb([[('👮 Moderator','sl_moderator')],[('🛡 Admin','sl_admin')],[('⭐ Super Admin','sl_superadmin')],[('◂ Cancel','admin_menu')]]) STATE = {} SDATA = {} def notify_admins(text, kb=None): for uid, role in ADMINS.items(): if ADMIN_LEVELS.get(role, 0) >= 10: try: send(uid, text, kb) except Exception: pass def show_home(chat_id, mid, uid): td = nuker.get_token_data(uid) if not td: edit(chat_id, mid, T.welcome(STORE.get("users",{}).get(str(uid),{}).get("name","User"), STORE.get("users",{}).get(str(uid),{}).get("username",""), uid), K.login()) return email = td.get("email","") record = nuker.get_record(uid, email) if record and record.get("Name"): edit(chat_id, mid, T.dashboard(record, email, uid), K.home(uid)) else: edit(chat_id, mid, f"{B}\n DASHBOARD\n{B}\n\n Email: {email}\n\n > Tap Refresh to load data", K.home(uid)) def show_home_new(chat_id, uid): td = nuker.get_token_data(uid) if not td: send(chat_id, T.welcome(STORE.get("users",{}).get(str(uid),{}).get("name","User"), STORE.get("users",{}).get(str(uid),{}).get("username",""), uid), K.login()) return email = td.get("email","") record = nuker.get_record(uid, email) if record and record.get("Name"): send(chat_id, T.dashboard(record, email, uid), K.home(uid)) else: send(chat_id, f"{B}\n DASHBOARD\n{B}\n\n Email: {email}\n\n > Tap Refresh to load data", K.home(uid)) FEAT_MAP = { "f_w16": ("W16 Engine", nuker.unlock_w16), "f_horns": ("Horns", nuker.unlock_horns), "f_damage": ("No Damage", nuker.disable_damage), "f_fuel": ("Unlimited Fuel",nuker.unlimited_fuel), "f_smoke": ("Smoke", nuker.unlock_smoke), "f_anims": ("Animations", nuker.unlock_animations), "f_wheels": ("Wheels", nuker.unlock_wheels), "f_houses": ("Houses", nuker.unlock_houses), "f_levels": ("All Levels", nuker.complete_all_levels), "f_rank": ("Max Rank", nuker.set_rank), "f_cars": ("All Cars", nuker.unlock_all_cars), "f_paid": ("Paid Cars", nuker.unlock_paid_cars), } def result_edit(chat_id, mid, ok, title, detail="", kb=None): icon = "OK" if ok else "X" text = f"{B}\n {icon} {title}\n{B}" if detail: text += f"\n\n {detail}" edit(chat_id, mid, text, kb) # ═══ MESSAGE HANDLER ═══ def on_message(m): uid = m["from"]["id"] chat_id = m["chat"]["id"] user = m["from"] text = (m.get("text") or "").strip() # 📷 Photo-based handlers from main.py if m.get("photo"): photos = m.get("photo") or [] file_id = photos[-1].get("file_id") if photos else None if STATE.get(uid) == "a_photo_upload": if not has_admin(uid, "owner"): send(chat_id, " ❌ Owner only!"); STATE.pop(uid, None); return if file_id: set_bot_photo(file_id); admin_log(uid, "UPDATE_PHOTO"); STATE.pop(uid, None) send(chat_id, f"{B}\n ✅ 𝗣𝗛𝗢𝗧𝗢 𝗨𝗣𝗗𝗔𝗧𝗘𝗗\n{B}\n\n ✔ Bot welcome photo updated!", K.back_admin()) return if STATE.get(uid) == "a_bcast_photo": if not has_admin(uid, "superadmin"): send(chat_id, " ❌ No permission!"); STATE.pop(uid, None); return SDATA.setdefault(uid,{})["bcast_photo_id"] = file_id STATE[uid] = "a_bcast_photo_cap" send(chat_id, " 🖼 Photo received!\n ✏ Type caption:", K.back_admin()) return STORE.setdefault("users", {})[str(uid)] = { "name": user.get("first_name","") + (" " + user.get("last_name","") if user.get("last_name") else ""), "username": user.get("username","") or "", "last_seen": datetime.now().isoformat(), } save_store(STORE) if is_banned(uid): send(chat_id, T.banned()); return state = STATE.get(uid) if state: handle_state(uid, chat_id, state, text); return if text.startswith("/start"): if is_maintenance() and not has_admin(uid, "moderator"): send(chat_id, T.maintenance()); return if is_expired(uid): store_remove_user(uid) send(chat_id, f"{B}\n EXPIRED\n{B}\n\n Access expired.", K.no_access()); return if not is_allowed(uid): send(chat_id, T.no_access(), K.no_access()); return STATE.pop(uid, None); SDATA.pop(uid, None) show_home_new(chat_id, uid) elif text.startswith("/admin"): if not has_admin(uid, "moderator"): send(chat_id, " X No admin access."); return STATE.pop(uid, None) send(chat_id, T.admin_panel(uid), K.admin(uid)) elif text.startswith("/bulkadd"): if not has_admin(uid, "admin"): send(chat_id, " X No admin access."); return STATE[uid] = "a_bulk"; SDATA[uid] = {} send(chat_id, hdr("Bulk Add", "BULK ADD") + "\n\n Send the user IDs (one per line).\n Send /cancel to cancel.", K.back_admin()) elif text.startswith("/help"): send(chat_id, f"{B}\n ❓ 𝗛𝗘𝗟𝗣\n{B}\n\n /start — 🎮 Start bot\n /admin — 👑 Admin panel\n /bulkadd — 📥 Bulk add users\n /help — ❓ Help\n /status — 📊 Status\n /ping — 🏓 Ping") elif text.startswith("/status"): td = nuker.get_token_data(uid) up = time.strftime("%H:%M:%S", time.gmtime(time.time() - START_TIME)) txt = f"{B}\n 🤖 𝗦𝗧𝗔𝗧𝗨𝗦\n{B}\n\n 🔐 Logged: {'yes' if td else 'no'}\n" if td: txt += f" Email: {td.get('email','-')}\n" txt += f" 👥 Users: {len(ALLOWED_USERS)}\n 🔧 Maint: {'🔴 ON' if is_maintenance() else '🟢 OFF'}\n ⏱ Uptime: {up}" send(chat_id, txt) elif text.startswith("/ping"): t = time.time() send(chat_id, " ...") send(chat_id, f" Pong! {(time.time()-t)*1000:.0f}ms") def handle_state(uid, chat_id, state, text): if text.lower() in ("/cancel", "cancel"): STATE.pop(uid, None); SDATA.pop(uid, None) td = nuker.get_token_data(uid) if td: show_home_new(chat_id, uid) else: send(chat_id, " X Cancelled.", K.login()) return if state == "email": em = text.strip() if "@" not in em or "." not in em: send(chat_id, " X Invalid email.", K.cancel()); return SDATA.setdefault(uid, {})["email"] = em STATE[uid] = "password" send(chat_id, hdr("Password", "PASSWORD") + "\n\n Type your password:", K.cancel()) elif state == "password": pw = text.strip() em = SDATA.get(uid, {}).get("email", "") STATE.pop(uid, None) mid = send(chat_id, " ... Signing in...").get("result", {}).get("message_id") try: r = nuker.login(em, pw) if r.get("ok"): nuker.save_token(uid, r["auth"], em, pw, r.get("refresh_token",""), r.get("firebase_uid","")) if mid: edit(chat_id, mid, " ... Loading account data...") loaded = nuker.load(uid, force=True) STORE["stats"]["total_logins"] = STORE["stats"].get("total_logins",0)+1 save_store(STORE); update_daily_stats("logins") if loaded: record = nuker.get_record(uid, em) txt = T.dashboard(record, em, uid) else: txt = f"{B}\n LOGIN SUCCESS\n{B}\n\n Email: {em}\n\n ! Tap Refresh to load data." if mid: edit(chat_id, mid, txt, K.home(uid)) else: send(chat_id, txt, K.home(uid)) else: txt = T.login_fail(r.get("message","LOGIN_FAILED")) if mid: edit(chat_id, mid, txt, K.login()) else: send(chat_id, txt, K.login()) except Exception as e: log.error(f"Login handler: {e}") send(chat_id, T.login_fail("NETWORK_ERROR"), K.login()) elif state == "money": try: a = int(text.replace(",","").replace(" ","")); assert 1 <= a <= MAX_MONEY except Exception: send(chat_id, f" X Enter 1 - {fmt(MAX_MONEY)}", K.cancel()); return STATE.pop(uid, None) mid = send(chat_id, f" ... Setting ${fmt(a)}...").get("result", {}).get("message_id") r = nuker.set_money(uid, a) if mid: result_edit(chat_id, mid, r.get("ok"), "MONEY SET" if r.get("ok") else "FAILED", f"$ {fmt(a)}" if r.get("ok") else r.get("message",""), K.back_home()) elif state == "coins": try: a = int(text.replace(",","").replace(" ","")); assert 1 <= a <= MAX_COIN except Exception: send(chat_id, f" X Enter 1 - {fmt(MAX_COIN)}", K.cancel()); return STATE.pop(uid, None) mid = send(chat_id, f" ... Setting {fmt(a)} coins...").get("result", {}).get("message_id") r = nuker.set_coin(uid, a) if mid: result_edit(chat_id, mid, r.get("ok"), "COINS SET" if r.get("ok") else "FAILED", f"{fmt(a)} coins" if r.get("ok") else "", K.back_home()) elif state == "name": name = text.strip() if not name or len(name) > 100: send(chat_id, " X 1-100 characters.", K.cancel()); return STATE.pop(uid, None) mid = send(chat_id, " ... Setting name...").get("result", {}).get("message_id") r = nuker.set_player_name(uid, name) if mid: result_edit(chat_id, mid, r.get("ok"), "NAME UPDATED" if r.get("ok") else "FAILED", f"OK {name}" if r.get("ok") else r.get("message",""), K.back_home()) elif state == "pid": pid = text.strip() clean = re.sub(r'\[\w+\]', '', pid) if not clean or len(clean) < 4 or len(clean) > 100: send(chat_id, " X 4-100 characters.", K.cancel()); return STATE.pop(uid, None) mid = send(chat_id, " ... Setting ID...").get("result", {}).get("message_id") r = nuker.set_player_id(uid, pid) if mid: result_edit(chat_id, mid, r.get("ok"), "ID UPDATED" if r.get("ok") else "FAILED", f"OK {pid.upper()}" if r.get("ok") else r.get("message",""), K.back_home()) elif state == "wins": try: v = int(text); assert v >= 0 except Exception: send(chat_id, " X Invalid number.", K.cancel()); return STATE.pop(uid, None) mid = send(chat_id, " ... Setting wins...").get("result", {}).get("message_id") r = nuker.set_race_wins(uid, v) if mid: result_edit(chat_id, mid, r.get("ok"), "WINS UPDATED" if r.get("ok") else "FAILED", f"{fmt(v)} wins" if r.get("ok") else r.get("message",""), K.back_home()) elif state == "loses": try: v = int(text); assert v >= 0 except Exception: send(chat_id, " X Invalid number.", K.cancel()); return STATE.pop(uid, None) mid = send(chat_id, " ... Setting loses...").get("result", {}).get("message_id") r = nuker.set_race_loses(uid, v) if mid: result_edit(chat_id, mid, r.get("ok"), "LOSES UPDATED" if r.get("ok") else "FAILED", f"{fmt(v)} loses" if r.get("ok") else r.get("message",""), K.back_home()) elif state == "clone_email": em = text.strip() if "@" not in em or "." not in em: send(chat_id, " X Invalid email.", K.cancel()); return SDATA.setdefault(uid, {})["t_email"] = em STATE[uid] = "clone_password" send(chat_id, hdr("Clone", "CLONE ACCOUNT") + f"\n\n Target: {em}\n\n Enter target account password:", K.cancel()) elif state == "clone_password": pw = text.strip() t_email = SDATA.get(uid, {}).get("t_email", "") STATE.pop(uid, None) mid = send(chat_id, " ... Cloning account (this can take a minute)...").get("result", {}).get("message_id") r = nuker.clone_account(uid, t_email, pw) if mid: result_edit(chat_id, mid, r.get("ok"), "ACCOUNT CLONED" if r.get("ok") else "FAILED", f"OK copied to {t_email}" if r.get("ok") else r.get("message",""), K.back_home()) # ── admin states ── elif state == "a_ban": try: tuid = int(text) except Exception: send(chat_id, " X Invalid ID."); return if tuid == OWNER_ID: send(chat_id, " X Cannot ban owner!", K.back_admin()); STATE.pop(uid,None); return STATE.pop(uid, None) store_ban(tuid); nuker.delete_token(tuid); admin_log(uid, "BANNED", str(tuid)) send(chat_id, f" Banned: {tuid}", K.back_admin()) elif state == "a_unban": try: tuid = int(text) except Exception: send(chat_id, " X Invalid ID."); return STATE.pop(uid, None) store_unban(tuid); admin_log(uid, "UNBANNED", str(tuid)) send(chat_id, f" Unbanned: {tuid}", K.back_admin()) elif state == "a_adduser": try: tuid = int(text) except Exception: send(chat_id, " X Invalid ID."); return STATE.pop(uid, None) store_allow(tuid); store_remove_pending(tuid); admin_log(uid, "ADDED", str(tuid)) info = STORE.get("users",{}).get(str(tuid),{}) try: send(tuid, T.welcome(info.get("name","User"), info.get("username",""), tuid), K.login()) except Exception: pass send(chat_id, f" OK {tuid} added!", K.back_admin()) elif state == "a_bulk": lines = [l.strip() for l in text.splitlines() if l.strip()] if not lines: send(chat_id, " X Empty. Send one user ID per line, or /cancel.", K.back_admin()); return seen, added, already, invalid = set(), [], [], [] for raw in lines: if raw in seen: continue seen.add(raw) if not raw.isdigit(): invalid.append(raw); continue tuid = int(raw) if tuid in ALLOWED_USERS: already.append(tuid); continue if store_allow(tuid, save=False): added.append(tuid); PENDING.pop(str(tuid), None) else: already.append(tuid) STORE["pending"] = PENDING; save_store(STORE) if added: admin_log(uid, "BULK_ADDED", f"{len(added)} users") STATE.pop(uid, None) detail = (f"Added: {len(added)}\nAlready existed: {len(already)}\nInvalid: {len(invalid)}\n" f"Total: {len(seen)}") if invalid: detail += "\n\nInvalid:\n" + "\n".join(x[:60] for x in invalid[:20]) send(chat_id, f"{B}\n Bulk Add Complete\n{B}\n\n {detail}", K.back_admin()) elif state == "a_kick": try: tuid = int(text) except Exception: send(chat_id, " X Invalid ID."); return if tuid == OWNER_ID: send(chat_id, " X Cannot kick owner!", K.back_admin()); STATE.pop(uid,None); return STATE.pop(uid, None) store_remove_user(tuid); nuker.delete_token(tuid); admin_log(uid, "KICKED", str(tuid)) try: send(tuid, hdr("Kicked", "KICKED") + "\n\n Access removed.") except Exception: pass send(chat_id, f" Kicked: {tuid}", K.back_admin()) elif state == "a_expiry1": try: tuid = int(text) except Exception: send(chat_id, " X Invalid ID."); return SDATA.setdefault(uid, {})["target"] = tuid STATE[uid] = "a_expiry2" send(chat_id, f" Enter days for {tuid} (0 = remove):", K.back_admin()) elif state == "a_expiry2": try: days = int(text); assert days >= 0 except Exception: send(chat_id, " X Invalid."); return tuid = SDATA.get(uid, {}).get("target") STATE.pop(uid, None) if days == 0: store_remove_expiry(tuid) send(chat_id, f" OK Expiry removed for {tuid}", K.back_admin()) else: store_set_expiry(tuid, days); admin_log(uid, f"EXPIRY_{days}d", str(tuid)) try: send(tuid, f" Expires in {days} days.") except Exception: pass send(chat_id, f" OK {tuid} expires in {days} days", K.back_admin()) elif state == "a_profile": try: tuid = int(text) except Exception: send(chat_id, " X Invalid ID."); return STATE.pop(uid, None) info = STORE.get("users",{}).get(str(tuid),{}) role = ADMINS.get(tuid, "user") vip = "yes" if tuid in VIP_USERS else "no" st = "Banned" if tuid in BANNED else ("Allowed" if tuid in ALLOWED_USERS else "Pending") exp = EXPIRY.get(str(tuid), "None") if exp != "None": try: exp = datetime.fromisoformat(exp).strftime("%d %b %Y") except Exception: pass send(chat_id, (f"{B}\n PROFILE\n{B}\n\n Name: {info.get('name','Unknown')}\n" f" User: @{info.get('username','N/A')}\n ID: {tuid}\n" f" Status: {st}\n Role: {role}\n VIP: {vip}\n" f" Expiry: {exp}\n Warns: {len(store_get_warnings(tuid))}/3"), K.back_admin()) elif state == "a_addvip": try: tuid = int(text) except Exception: send(chat_id, " X Invalid ID."); return STATE.pop(uid, None) store_add_vip(tuid); admin_log(uid, "ADD_VIP", str(tuid)) try: send(tuid, " You are now VIP!") except Exception: pass send(chat_id, f" OK {tuid} is now VIP!", K.back_admin()) elif state == "a_rmvip": try: tuid = int(text) except Exception: send(chat_id, " X Invalid ID."); return STATE.pop(uid, None) store_remove_vip(tuid) send(chat_id, f" OK VIP removed for {tuid}", K.back_admin()) elif state == "a_bcast": txt = text.strip() target = SDATA.get(uid, {}).get("bcast_target", "all") targets = VIP_USERS if target == "vip" else ALLOWED_USERS STATE.pop(uid, None); SDATA.pop(uid, None) bc = f"{B}\n 📢 𝗕𝗥𝗢𝗔𝗗𝗖𝗔𝗦𝗧\n{B}\n\n {txt}" s = f = 0 for t in list(targets): try: send(t, bc); s += 1 except Exception: f += 1 time.sleep(0.05) admin_log(uid, f"BCAST_TEXT s={s} f={f}") add_broadcast_history(uid, "text", txt, s, f) send(chat_id, f" Done!\n Sent: {s} Failed: {f}", K.back_admin()) elif state == "a_bcast_photo_cap": caption = text.strip() d = SDATA.get(uid, {}) photo = d.get("bcast_photo_id", "") target = d.get("bcast_target", "all") targets = VIP_USERS if target == "vip" else ALLOWED_USERS STATE.pop(uid, None); SDATA.pop(uid, None) bc_cap = f"{B}\n 📢 𝗕𝗥𝗢𝗔𝗗𝗖𝗔𝗦𝗧\n{B}\n\n {caption}" sent = failed = 0 for t in list(targets): try: tg_call("sendPhoto", chat_id=t, photo=photo, caption=bc_cap); sent += 1 except Exception: try: send(t, bc_cap); sent += 1 except Exception: failed += 1 time.sleep(0.05) admin_log(uid, f"BCAST_PHOTO s={sent} f={failed}") add_broadcast_history(uid, "photo", caption, sent, failed) send(chat_id, f" 📢 Done!\n ✔ {sent} sent ✗ {failed} failed", K.back_admin()) elif state == "a_addadm1": try: tuid = int(text) except Exception: send(chat_id, " X Invalid ID."); return SDATA.setdefault(uid, {})["target"] = tuid STATE[uid] = "a_addadm2" send(chat_id, f" Select role for {tuid}:", K.roles(tuid)) elif state == "a_rmadm": try: tuid = int(text) except Exception: send(chat_id, " X Invalid ID."); return if tuid == OWNER_ID: send(chat_id, " X Cannot remove owner!", K.back_admin()); STATE.pop(uid,None); return STATE.pop(uid, None) store_remove_admin(tuid); admin_log(uid, "REM_ADMIN", str(tuid)) send(chat_id, f" OK {tuid} demoted!", K.back_admin()) # ═══ CALLBACK HANDLER ═══ def on_callback(cq): uid = cq["from"]["id"] data = cq["data"] msg = cq.get("message") or {} chat_id = msg.get("chat", {}).get("id", uid) mid = msg.get("message_id") def answer(text=None, alert=False): answer_cb(cq["id"], text, alert) def refresh_home(): if mid: show_home(chat_id, mid, uid) # ── access request flow ── if data == "send_request": name = (cq["from"].get("first_name","") + (" " + cq["from"].get("last_name","") if cq["from"].get("last_name") else "")).strip() un = cq["from"].get("username") or "" if is_banned(uid): if mid: edit(chat_id, mid, T.banned()) answer(); return if is_allowed(uid): answer("Already approved!", True); refresh_home(); return if is_pending(uid): if mid: edit(chat_id, mid, f"{B}\n PENDING\n{B}\n\n Request pending. Wait for admin.", K.after_request()) answer(); return store_add_pending(uid, name, un) notify_admins(T.request_to_admin(name, un, uid), K.request_actions(uid)) if mid: edit(chat_id, mid, f"{B}\n SENT\n{B}\n\n OK Request sent!\n You'll be notified.", K.after_request()) answer("Sent!") elif data.startswith("rq_accept_"): if not has_admin(uid, "admin"): answer("X No permission", True); return tuid = int(data.split("_")[2]) info = PENDING.get(str(tuid), {}) name = info.get("name", f"User {tuid}"); un = info.get("username", "") store_allow(tuid, name); store_remove_pending(tuid); admin_log(uid, "APPROVED", str(tuid)) try: send(tuid, T.welcome(name, un, tuid), K.login()) except Exception: pass if mid: edit(chat_id, mid, f" OK ACCEPTED\n\n {name}\n ID: {tuid}\n By {cq['from'].get('first_name','')}") answer("Approved!") elif data.startswith("rq_reject_"): if not has_admin(uid, "admin"): answer("X No permission", True); return tuid = int(data.split("_")[2]) name = PENDING.get(str(tuid), {}).get("name", f"User {tuid}") store_remove_pending(tuid); admin_log(uid, "REJECTED", str(tuid)) try: send(tuid, f"{B}\n REJECTED\n{B}\n\n Request declined.") except Exception: pass if mid: edit(chat_id, mid, f" X REJECTED\n\n {name}\n ID: {tuid}") answer("Rejected") elif data.startswith("rq_ban_"): if not has_admin(uid, "admin"): answer("X No permission", True); return tuid = int(data.split("_")[2]) name = PENDING.get(str(tuid), {}).get("name", f"User {tuid}") store_ban(tuid); store_remove_pending(tuid); admin_log(uid, "BANNED_REQUEST", str(tuid)) try: send(tuid, T.banned()) except Exception: pass if mid: edit(chat_id, mid, f" BANNED\n\n {name}\n ID: {tuid}") answer("Banned") elif data == "msg_admin": if mid: edit(chat_id, mid, f"{B}\n CONTACT\n{B}\n\n Admin ID: {OWNER_ID}\n Your ID: {uid}", K.after_request()) answer() elif data == "check_status": if is_allowed(uid): refresh_home(); answer("Approved!") elif is_banned(uid): if mid: edit(chat_id, mid, T.banned()) answer() else: if mid: edit(chat_id, mid, f"{B}\n PENDING\n{B}\n\n Still pending.", K.after_request()) answer() # ── login / nav ── elif data == "login": if is_banned(uid): if mid: edit(chat_id, mid, T.banned()) answer(); return if not is_allowed(uid): if mid: edit(chat_id, mid, T.no_access(), K.no_access()) answer(); return if is_maintenance() and not has_admin(uid, "moderator"): if mid: edit(chat_id, mid, T.maintenance()) answer(); return STATE[uid] = "email"; SDATA[uid] = {} if mid: edit(chat_id, mid, hdr("Email", "ENTER EMAIL") + "\n\n Type your CPM email:", K.cancel()) else: send(chat_id, hdr("Email", "ENTER EMAIL") + "\n\n Type your CPM email:", K.cancel()) answer() elif data == "cancel": STATE.pop(uid, None); SDATA.pop(uid, None) refresh_home(); answer("Cancelled") elif data == "back_home": STATE.pop(uid, None); SDATA.pop(uid, None) refresh_home(); answer() elif data == "refresh": td = nuker.get_token_data(uid) if not td: refresh_home(); answer(); return if mid: edit(chat_id, mid, " ... Refreshing...") nuker.load(uid, force=True) email = td.get("email","") record = nuker.get_record(uid, email) if record and record.get("Name"): if mid: edit(chat_id, mid, T.dashboard(record, email, uid), K.home(uid)) else: if mid: edit(chat_id, mid, f"{B}\n COULD NOT LOAD\n{B}\n\n Try again.", K.home(uid)) answer("Refreshed!") elif data == "logout": if mid: edit(chat_id, mid, hdr("Sign Out", "SIGN OUT") + "\n\n Are you sure?", K.confirm_logout()) answer() elif data == "do_logout": STATE.pop(uid, None); SDATA.pop(uid, None) nuker.delete_token(uid) if mid: edit(chat_id, mid, hdr("OK", "SIGNED OUT") + "\n\n Successfully signed out.", K.login()) answer("OK") # ── money / coins ── elif data == "menu_money": if not nuker.get_token(uid): answer("X Sign in first!", True); return ok, w = check_rate_limit(uid) if not ok: answer(f"Wait {w}s", True); return if mid: edit(chat_id, mid, f"{B}\n MONEY\n{B}\n\n Max: $50,000,000", K.money()) answer() elif data.startswith("m_"): v = data[2:] if v == "custom": STATE[uid] = "money" if mid: edit(chat_id, mid, hdr("Money", "CUSTOM") + f"\n\n Enter amount (1 - {fmt(MAX_MONEY)}):", K.cancel()) answer(); return amount = int(v) if mid: edit(chat_id, mid, f" ... Setting ${fmt(amount)}...") answer() r = nuker.set_money(uid, amount) result_edit(chat_id, mid, r.get("ok"), "MONEY SET" if r.get("ok") else "FAILED", f"$ {fmt(amount)}" if r.get("ok") else r.get("message",""), K.back_home()) elif data == "menu_coins": if not nuker.get_token(uid): answer("X Sign in first!", True); return if mid: edit(chat_id, mid, f"{B}\n COINS\n{B}\n\n Max: 500,000", K.coins()) answer() elif data.startswith("c_"): v = data[2:] if v == "custom": STATE[uid] = "coins" if mid: edit(chat_id, mid, hdr("Coins", "CUSTOM") + f"\n\n Enter amount (1 - {fmt(MAX_COIN)}):", K.cancel()) answer(); return amount = int(v) if mid: edit(chat_id, mid, f" ... Setting {fmt(amount)} coins...") answer() r = nuker.set_coin(uid, amount) result_edit(chat_id, mid, r.get("ok"), "COINS SET" if r.get("ok") else "FAILED", f"{fmt(amount)} coins" if r.get("ok") else "", K.back_home()) # ── features ── elif data == "menu_feat": if not nuker.get_token(uid): answer("X Sign in first!", True); return if mid: edit(chat_id, mid, f"{B}\n FEATURES\n{B}\n\n Select a feature or UNLOCK ALL:", K.feat()) answer() elif data in FEAT_MAP: fname, fn = FEAT_MAP[data] if mid: edit(chat_id, mid, f" ... Loading account & applying {fname}...") answer() r = fn(uid) if r.get("ok"): STORE["stats"]["total_unlocks"] = STORE["stats"].get("total_unlocks",0)+1 save_store(STORE); update_daily_stats("unlocks") result_edit(chat_id, mid, r.get("ok"), f"{fname} OK" if r.get("ok") else f"{fname} X", "" if r.get("ok") else r.get("message",""), K.feat()) elif data == "f_all": if not nuker.get_token(uid): answer("X Sign in first!", True); return answer() if mid: edit(chat_id, mid, f"{B}\n UNLOCKING ALL\n{B}\n\n ... Loading account...") nuker.load(uid, force=True) ALL = list(FEAT_MAP.values()); total = len(ALL); done = failed = 0; results = [] for i, (name, fn) in enumerate(ALL): try: if mid: edit(chat_id, mid, f"{B}\n UNLOCKING ALL\n{B}\n\n {i+1}/{total} > {name}\n\n OK {done} X {failed}") except Exception: pass r = fn(uid) if r.get("ok"): done += 1; results.append(f" OK {name}") else: failed += 1; results.append(f" X {name}") time.sleep(0.3) STORE["stats"]["total_unlocks"] = STORE["stats"].get("total_unlocks",0)+done save_store(STORE) if mid: edit(chat_id, mid, f"{B}\n COMPLETE\n{B}\n\n OK {done}/{total} X {failed}/{total}\n\n" + "\n".join(results), K.back_home()) # ── settings ── elif data == "menu_set": if not nuker.get_token(uid): answer("X Sign in first!", True); return if mid: edit(chat_id, mid, f"{B}\n SETTINGS\n{B}\n\n Modify your account:", K.sett()) answer() elif data == "s_name": STATE[uid] = "name" if mid: edit(chat_id, mid, hdr("Name", "CHANGE NAME") + "\n\n Enter new name:", K.cancel()) answer() elif data == "s_pid": STATE[uid] = "pid" if mid: edit(chat_id, mid, hdr("ID", "PLAYER ID") + "\n\n Enter new Player ID:", K.cancel()) answer() elif data == "s_wins": STATE[uid] = "wins" if mid: edit(chat_id, mid, hdr("Wins", "SET WINS") + "\n\n Enter win count:", K.cancel()) answer() elif data == "s_loses": STATE[uid] = "loses" if mid: edit(chat_id, mid, hdr("Loses", "SET LOSES") + "\n\n Enter loss count:", K.cancel()) answer() elif data == "s_clone": if not nuker.get_token(uid): answer("X Sign in first!", True); return STATE[uid] = "clone_email"; SDATA[uid] = {} if mid: edit(chat_id, mid, hdr("Clone", "CLONE ACCOUNT") + "\n\n Enter the TARGET account email (the account you want to copy everything TO):", K.cancel()) answer() elif data == "s_fix": if mid: edit(chat_id, mid, " ... Loading & fixing account...") answer() r = nuker.fix_account(uid) result_edit(chat_id, mid, r.get("ok"), "ACCOUNT FIXED" if r.get("ok") else "FAILED", f"OK {r.get('bugs_fixed',0)} bugs fixed" if r.get("ok") else r.get("message",""), K.back_home()) # ── admin ── elif data == "admin_menu": if not has_admin(uid, "moderator"): answer("X No access!", True); return STATE.pop(uid, None) if mid: edit(chat_id, mid, T.admin_panel(uid), K.admin(uid)) answer() elif data == "a_stats": if not has_admin(uid, "moderator"): answer("X", True); return if mid: edit(chat_id, mid, T.stats(), K.back_admin()) answer() elif data == "a_log": if not has_admin(uid, "moderator"): answer("X", True); return logs = STORE.get("admin_log", [])[:10] txt = f"{B}\n ACTIVITY LOG\n{B}\n\n" if not logs: txt += " No activity yet." else: for e in logs: t = e.get("time","")[:16].replace("T", " ") txt += f" * {t} {e.get('action','')} -> {e.get('target','')}\n" if mid: edit(chat_id, mid, txt, K.back_admin()) answer() elif data == "a_users": if not has_admin(uid, "moderator"): answer("X", True); return users = STORE.get("users", {}) lb = {"owner":"[O]","superadmin":"[S]","admin":"[A]","moderator":"[M]"} txt = f"{B}\n USERS ({len(ALLOWED_USERS)})\n{B}\n\n" for t in sorted(ALLOWED_USERS)[:25]: info = users.get(str(t), {}) txt += f" {lb.get(ADMINS.get(t,''),'')}{'[VIP]' if t in VIP_USERS else ''} {info.get('name', f'User {t}')[:16]} {t}\n" if len(ALLOWED_USERS) > 25: txt += f"\n ... +{len(ALLOWED_USERS)-25} more" if mid: edit(chat_id, mid, txt, K.back_admin()) answer() elif data == "a_pend": if not has_admin(uid, "admin"): answer("X", True); return if not PENDING: if mid: edit(chat_id, mid, hdr("OK", "NO PENDING") + "\n\n No pending requests.", K.back_admin()) else: if mid: edit(chat_id, mid, hdr("Pending", "PENDING") + f"\n\n {len(PENDING)} pending:", K.pending_list()) answer() elif data == "a_adduser": if not has_admin(uid, "admin"): answer("X", True); return STATE[uid] = "a_adduser" if mid: edit(chat_id, mid, hdr("Add", "ADD USER") + "\n\n Enter user ID:", K.back_admin()) answer() elif data == "a_bulkadd": if not has_admin(uid, "admin"): answer("X", True); return STATE[uid] = "a_bulk"; SDATA[uid] = {} if mid: edit(chat_id, mid, hdr("Bulk Add", "BULK ADD") + "\n\n Send the user IDs (one per line).\n Send /cancel to cancel.", K.back_admin()) answer() elif data == "a_ban": if not has_admin(uid, "admin"): answer("X", True); return STATE[uid] = "a_ban" if mid: edit(chat_id, mid, hdr("Ban", "BAN") + "\n\n Enter user ID:", K.back_admin()) answer() elif data == "a_unban": if not has_admin(uid, "admin"): answer("X", True); return STATE[uid] = "a_unban" if mid: edit(chat_id, mid, hdr("Unban", "UNBAN") + "\n\n Enter user ID:", K.back_admin()) answer() elif data == "a_kick": if not has_admin(uid, "admin"): answer("X", True); return STATE[uid] = "a_kick" if mid: edit(chat_id, mid, hdr("Kick", "KICK") + "\n\n Enter user ID:", K.back_admin()) answer() elif data == "a_expiry": if not has_admin(uid, "admin"): answer("X", True); return STATE[uid] = "a_expiry1" if mid: edit(chat_id, mid, hdr("Expiry", "SET EXPIRY") + "\n\n Enter user ID:", K.back_admin()) answer() elif data == "a_profile": if not has_admin(uid, "admin"): answer("X", True); return STATE[uid] = "a_profile" if mid: edit(chat_id, mid, hdr("Profile", "USER PROFILE") + "\n\n Enter user ID:", K.back_admin()) answer() elif data == "a_addvip": if not has_admin(uid, "superadmin"): answer("X", True); return STATE[uid] = "a_addvip" if mid: edit(chat_id, mid, hdr("VIP", "ADD VIP") + "\n\n Enter user ID:", K.back_admin()) answer() elif data == "a_rmvip": if not has_admin(uid, "superadmin"): answer("X", True); return STATE[uid] = "a_rmvip" if mid: edit(chat_id, mid, hdr("VIP", "REM VIP") + "\n\n Enter user ID:", K.back_admin()) answer() elif data == "a_bcast": if not has_admin(uid, "superadmin"): answer("X", True); return STATE[uid] = "a_bcast" if mid: edit(chat_id, mid, hdr("Broadcast", "BROADCAST") + f"\n\n Message to all {len(ALLOWED_USERS)} users:", K.back_admin()) answer() elif data == "a_addadm": if not has_admin(uid, "owner"): answer("X Owner only!", True); return STATE[uid] = "a_addadm1" if mid: edit(chat_id, mid, hdr("Add Admin", "ADD ADMIN") + "\n\n Enter user ID:", K.back_admin()) answer() elif data == "a_rmadm": if not has_admin(uid, "owner"): answer("X Owner only!", True); return STATE[uid] = "a_rmadm" if mid: edit(chat_id, mid, hdr("Rem Admin", "REM ADMIN") + "\n\n Enter user ID:", K.back_admin()) answer() elif data.startswith("sl_"): role = data[3:] tuid = SDATA.get(uid, {}).get("target") if not tuid: answer("X"); STATE.pop(uid, None); return STATE.pop(uid, None) store_add_admin(tuid, role); admin_log(uid, f"ADD_ADMIN_{role}", str(tuid)) try: send(tuid, f" You are now {role}!\n Use /admin") except Exception: pass if mid: edit(chat_id, mid, f" OK {tuid} -> {role}", K.back_admin()) answer() elif data == "a_photo": if not has_admin(uid, "owner"): answer("✗ Owner only!", True); return STATE[uid] = "a_photo_upload" current = get_bot_photo() txt = f"{B}\n 🖼 𝗨𝗣𝗗𝗔𝗧𝗘 𝗕𝗢𝗧 𝗣𝗛𝗢𝗧𝗢\n{B}\n\n" if current: txt += " ◆ Current photo is set.\n\n" txt += " Send a new photo to update it." if mid: edit(chat_id, mid, txt, K.back_admin()) answer("🖼 Send photo") elif data == "a_bcast_menu": if not has_admin(uid, "superadmin"): answer("✗ No permission", True); return if mid: edit(chat_id, mid, f"{B}\n 📢 𝗕𝗥𝗢𝗔𝗗𝗖𝗔𝗦𝗧\n{B}\n\n 👥 All: {len(ALLOWED_USERS)} 💎 VIP: {len(VIP_USERS)}", K.broadcast_menu()) answer() elif data == "bcast_text": if not has_admin(uid, "superadmin"): answer("✗ No permission", True); return STATE[uid] = "a_bcast"; SDATA[uid] = {"bcast_target":"all"} if mid: edit(chat_id, mid, hdr("📢","𝗧𝗘𝗫𝗧 𝗕𝗥𝗢𝗔𝗗𝗖𝗔𝗦𝗧") + f"\n\n Message to all {len(ALLOWED_USERS)} users:", K.back_admin()) answer() elif data == "bcast_vip": if not has_admin(uid, "superadmin"): answer("✗ No permission", True); return STATE[uid] = "a_bcast"; SDATA[uid] = {"bcast_target":"vip"} if mid: edit(chat_id, mid, hdr("💎","𝗩𝗜𝗣 𝗕𝗥𝗢𝗔𝗗𝗖𝗔𝗦𝗧") + f"\n\n Message to {len(VIP_USERS)} VIPs:", K.back_admin()) answer() elif data == "bcast_photo": if not has_admin(uid, "superadmin"): answer("✗ No permission", True); return STATE[uid] = "a_bcast_photo"; SDATA[uid] = {"bcast_target":"all"} if mid: edit(chat_id, mid, hdr("🖼","𝗣𝗛𝗢𝗧𝗢 𝗕𝗥𝗢𝗔𝗗𝗖𝗔𝗦𝗧") + "\n\n Send a photo:", K.back_admin()) answer() elif data == "a_maint": if not has_admin(uid, "owner"): answer("X Owner only!", True); return STORE["maintenance"] = not STORE.get("maintenance", False) save_store(STORE) admin_log(uid, f"MAINTENANCE_{'ON' if STORE['maintenance'] else 'OFF'}") if mid: edit(chat_id, mid, f" Maintenance: {'ON' if STORE['maintenance'] else 'OFF'}", K.back_admin()) answer() elif data == "a_reset": if not has_admin(uid, "owner"): answer("X Owner only!", True); return STORE["stats"] = {"total_logins":0,"total_actions":0,"total_unlocks":0} STORE["daily_stats"] = {} save_store(STORE); admin_log(uid, "RESET_STATS") if mid: edit(chat_id, mid, " OK Stats reset!", K.back_admin()) answer() else: answer() # ═══ MAIN LOOP ═══ START_TIME = time.time() def main(): log.info("=" * 40) log.info(" CPM bot light (no aiogram)") log.info(f" Owner: {OWNER_ID}") log.info(f" Users: {len(ALLOWED_USERS)}") log.info(f" Brotli: {'yes' if HAS_BROTLI else 'NO (pip install brotli)'}") log.info(f" Crypto: {'yes' if HAS_CRYPTO else 'no (optional)'}") log.info("=" * 40) tg_call("deleteWebhook", drop_pending_updates=True) offset = 0 while True: try: r = tg_call("getUpdates", offset=offset, timeout=30, allowed_updates=json.dumps(["message", "callback_query"])) for u in r.get("result", []): offset = u["update_id"] + 1 try: if "callback_query" in u: on_callback(u["callback_query"]) elif "message" in u: on_message(u["message"]) except Exception as e: log.error(f"Handler: {e}\n{traceback.format_exc()}") except Exception as e: log.error(f"Poll: {e}") time.sleep(3) if __name__ == "__main__": try: main() except KeyboardInterrupt: log.info("Stopped.") except Exception as e: log.error(f"Fatal: {e}\n{traceback.format_exc()}")