import asyncio import io import re import json import html import os import httpx import pyotp import random import string from datetime import datetime, timedelta from telegram import Update, ReplyKeyboardMarkup, KeyboardButton, InlineKeyboardButton, InlineKeyboardMarkup from telegram.ext import ApplicationBuilder, CommandHandler, MessageHandler, ContextTypes, filters, CallbackQueryHandler # ==================== CONFIG SECTION ==================== BOT_TOKEN = "8926378970:AAHIZMtQWbBVOolpf_JMPFkXSON1bfOVv0c" API_KEY = "MURAD_C80D9E90F3C39ED1D4364B80" BASE_URL = "https://fastxotps.com" # আপনার প্যানেল ডোমেন (trailing slash ছাড়া) USER_DATA_FILE = "users.json" PAID_SMS_FILE = "paid_sms.json" STATS_FILE = "user_stats.json" REFERRAL_DATA_FILE = "referral_data.json" BANNED_USERS_FILE = "banned_users.json" WITHDRAW_DATA_FILE = "withdraw_requests.json" ACTIVITY_LOGS_FILE = "activity_logs.json" DATA_RANGE_FILE = "datarange.json" # ==================== MULTIPLE ADMINS CONFIGURATION ==================== ADMINS = [6263959292] OTP_GROUP_ID = -1002826437332 # ==================== WELCOME MESSAGE CONFIGURATION ==================== WELCOME_MESSAGE = """✨ 𝗪𝗘𝗟𝗖𝗢𝗠𝗘 𝗧𝗢 PRO OTP GENARET ✨ ━━━━━━━━━━━━━━━━━━━━━━ 🚀 Enjoy Premium Quality Service 🚀""" # ==================== OTP RATE CONFIGURATION ==================== OTP_RATE = 0.0010 # ==================== REFERRAL / WITHDRAW CONFIGURATION ==================== REFERRAL_PRICE = 0 MIN_WITHDRAW = 50 MAX_WITHDRAW = 10000 # ==================== SUPPORT & DEVELOPER LINKS ==================== SUPPORT_LINK = "https://t.me/onlineearninzone99" # আপনার সাপোর্ট লিংক দিন DEVELOPER_LINK = "http://t.me/Suppor_admin9" # আপনার ডেভেলপার লিংক দিন request_queue = asyncio.Queue() MAX_WORKERS = 5000 client_async = httpx.AsyncClient( timeout=httpx.Timeout(connect=3.0, read=8.0, write=5.0, pool=3.0), headers={"X-API-Key": API_KEY}, limits=httpx.Limits(max_connections=1000, max_keepalive_connections=200) ) active_numbers = {} last_range = {} CHECK_INTERVAL = 1.5 # ==================== LIVEACCESS CACHE ==================== _liveaccess_cache = {"services": []} LIVEACCESS_REFRESH_INTERVAL = 25 async def _do_liveaccess_fetch(): global _liveaccess_cache try: r = await client_async.get(f"{BASE_URL}/api/liveaccess") data = r.json() if data.get("status") == "ok": svcs = data.get("services", []) if svcs: _liveaccess_cache["services"] = svcs print(f"[liveaccess] cache updated — {len(svcs)} service(s)") except Exception as e: print(f"[liveaccess] fetch error: {e}") async def liveaccess_refresh_loop(): while True: await _do_liveaccess_fetch() await asyncio.sleep(LIVEACCESS_REFRESH_INTERVAL) def get_cached_services(): return _liveaccess_cache["services"] # ==================== CHECK IF USER IS ADMIN ==================== def is_admin(user_id): return user_id in ADMINS # ==================== WITHDRAW DATA FUNCTIONS ==================== def load_withdraw_requests(): if not os.path.exists(WITHDRAW_DATA_FILE): with open(WITHDRAW_DATA_FILE, "w") as f: json.dump({}, f) return {} try: with open(WITHDRAW_DATA_FILE, "r") as f: return json.load(f) except: return {} def save_withdraw_requests(data): with open(WITHDRAW_DATA_FILE, "w") as f: json.dump(data, f, indent=4) def generate_payment_id(): return ''.join(random.choices(string.ascii_lowercase + string.digits, k=20)) # ==================== BANNED USERS FUNCTIONS ==================== def load_banned_users(): if not os.path.exists(BANNED_USERS_FILE): with open(BANNED_USERS_FILE, "w") as f: json.dump([], f) return [] try: with open(BANNED_USERS_FILE, "r") as f: return json.load(f) except: return [] def save_banned_users(banned_list): with open(BANNED_USERS_FILE, "w") as f: json.dump(banned_list, f, indent=4) def is_user_banned(uid): banned_list = load_banned_users() return str(uid) in banned_list def ban_user(uid): banned_list = load_banned_users() uid_str = str(uid) if uid_str not in banned_list: banned_list.append(uid_str) save_banned_users(banned_list) return True return False def unban_user(uid): banned_list = load_banned_users() uid_str = str(uid) if uid_str in banned_list: banned_list.remove(uid_str) save_banned_users(banned_list) return True return False # ==================== REFERRAL DATA FUNCTIONS ==================== def load_referral_data(): if not os.path.exists(REFERRAL_DATA_FILE): with open(REFERRAL_DATA_FILE, "w") as f: json.dump({}, f) return {} try: with open(REFERRAL_DATA_FILE, "r") as f: return json.load(f) except: return {} def save_referral_data(data): with open(REFERRAL_DATA_FILE, "w") as f: json.dump(data, f, indent=4) def update_referral_count(uid, count): referral_data = load_referral_data() uid_str = str(uid) if uid_str not in referral_data: referral_data[uid_str] = {"referral_count": 0} referral_data[uid_str]["referral_count"] = count save_referral_data(referral_data) def get_referral_count(uid): referral_data = load_referral_data() uid_str = str(uid) return referral_data.get(uid_str, {}).get("referral_count", 0) # ==================== DATA RANGE FILE ==================== def load_range_db(): if not os.path.exists(DATA_RANGE_FILE): return {} try: with open(DATA_RANGE_FILE, "r") as f: return json.load(f) except: return {} def save_range_db(data): with open(DATA_RANGE_FILE, "w") as f: json.dump(data, f, indent=4) def save_number_range_info(uid, number, range_text): db = load_range_db() flag, name = get_country_info(number) db[normalize_number(number)] = { "user_id": str(uid), "number": f"+{normalize_number(number)}", "range": range_text, "country": f"{flag} {name}" } save_range_db(db) # ==================== COUNTRY MAPPING SECTION ==================== def get_country_info(number): number = str(number).strip() country_map = { "2376": ("🇨🇲", "Cameroon"), "2250": ("🇨🇮", "Ivory Coast"), "2613": ("🇲🇬", "Madagascar"), "4077": ("🇷🇴", "Romania"), "237": ("🇨🇲", "Cameroon"), "225": ("🇨🇮", "Ivory Coast"), "261": ("🇲🇬", "Madagascar"), "20": ("🇪🇬", "Egypt"), "27": ("🇿🇦", "South Africa"), "234": ("🇳🇬", "Nigeria"), "254": ("🇰🇪", "Kenya"), "233": ("🇬🇭", "Ghana"), "212": ("🇲🇦", "Morocco"), "213": ("🇩🇿", "Algeria"), "216": ("🇹🇳", "Tunisia"), "218": ("🇱🇾", "Libya"), "249": ("🇸🇩", "Sudan"), "251": ("🇪🇹", "Ethiopia"), "252": ("🇸🇴", "Somalia"), "253": ("🇩🇯", "Djibouti"), "255": ("🇹🇿", "Tanzania"), "256": ("🇺🇬", "Uganda"), "257": ("🇧🇮", "Burundi"), "258": ("🇲🇿", "Mozambique"), "260": ("🇿🇲", "Zambia"), "263": ("🇿🇼", "Zimbabwe"), "264": ("🇳🇦", "Namibia"), "265": ("🇲🇼", "Malawi"), "266": ("🇱🇸", "Lesotho"), "267": ("🇧🇼", "Botswana"), "268": ("🇸🇿", "Swaziland"), "269": ("🇰🇲", "Comoros"), "220": ("🇬🇲", "Gambia"), "221": ("🇸🇳", "Senegal"), "222": ("🇲🇷", "Mauritania"), "223": ("🇲🇱", "Mali"), "224": ("🇬🇳", "Guinea"), "226": ("🇧🇫", "Burkina Faso"), "227": ("🇳🇪", "Niger"), "228": ("🇹🇬", "Togo"), "229": ("🇧🇯", "Benin"), "230": ("🇲🇺", "Mauritius"), "231": ("🇱🇷", "Liberia"), "232": ("🇸🇱", "Sierra Leone"), "235": ("🇹🇩", "Chad"), "236": ("🇨🇫", "Central African Republic"), "238": ("🇨🇻", "Cape Verde"), "239": ("🇸🇹", "Sao Tome and Principe"), "240": ("🇬🇶", "Equatorial Guinea"), "241": ("🇬🇦", "Gabon"), "242": ("🇨🇬", "Congo"), "243": ("🇨🇩", "DR Congo"), "244": ("🇦🇴", "Angola"), "245": ("🇬🇼", "Guinea-Bissau"), "247": ("🇸🇭", "Saint Helena"), "248": ("🇸🇨", "Seychelles"), "250": ("🇷🇼", "Rwanda"), "290": ("🇸🇭", "Saint Helena"), "291": ("🇪🇷", "Eritrea"), "40": ("🇷🇴", "Romania"), "44": ("🇬🇧", "United Kingdom"), "33": ("🇫🇷", "France"), "49": ("🇩🇪", "Germany"), "39": ("🇮🇹", "Italy"), "34": ("🇪🇸", "Spain"), "31": ("🇳🇱", "Netherlands"), "32": ("🇧🇪", "Belgium"), "41": ("🇨🇭", "Switzerland"), "43": ("🇦🇹", "Austria"), "46": ("🇸🇪", "Sweden"), "47": ("🇳🇴", "Norway"), "45": ("🇩🇰", "Denmark"), "358": ("🇫🇮", "Finland"), "351": ("🇵🇹", "Portugal"), "353": ("🇮🇪", "Ireland"), "36": ("🇭🇺", "Hungary"), "48": ("🇵🇱", "Poland"), "380": ("🇺🇦", "Ukraine"), "370": ("🇱🇹", "Lithuania"), "371": ("🇱🇻", "Latvia"), "372": ("🇪🇪", "Estonia"), "373": ("🇲🇩", "Moldova"), "374": ("🇦🇲", "Armenia"), "375": ("🇧🇾", "Belarus"), "376": ("🇦🇩", "Andorra"), "377": ("🇲🇨", "Monaco"), "381": ("🇷🇸", "Serbia"), "382": ("🇲🇪", "Montenegro"), "385": ("🇭🇷", "Croatia"), "386": ("🇸🇮", "Slovenia"), "387": ("🇧🇦", "Bosnia and Herzegovina"), "389": ("🇲🇰", "North Macedonia"), "350": ("🇬🇮", "Gibraltar"), "352": ("🇱🇺", "Luxembourg"), "354": ("🇮🇸", "Iceland"), "355": ("🇦🇱", "Albania"), "356": ("🇲🇹", "Malta"), "357": ("🇨🇾", "Cyprus"), "359": ("🇧🇬", "Bulgaria"), "421": ("🇸🇰", "Slovakia"), "420": ("🇨🇿", "Czech Republic"), "298": ("🇫🇴", "Faroe Islands"), "299": ("🇬🇱", "Greenland"), "1": ("🇺🇸", "United States"), "7": ("🇷🇺", "Russia"), "91": ("🇮🇳", "India"), "92": ("🇵🇰", "Pakistan"), "880": ("🇧🇩", "Bangladesh"), "86": ("🇨🇳", "China"), "81": ("🇯🇵", "Japan"), "82": ("🇰🇷", "South Korea"), "84": ("🇻🇳", "Vietnam"), "66": ("🇹🇭", "Thailand"), "62": ("🇮🇩", "Indonesia"), "60": ("🇲🇾", "Malaysia"), "65": ("🇸🇬", "Singapore"), "63": ("🇵🇭", "Philippines"), "95": ("🇲🇲", "Myanmar"), "94": ("🇱🇰", "Sri Lanka"), "977": ("🇳🇵", "Nepal"), "93": ("🇦🇫", "Afghanistan"), "98": ("🇮🇷", "Iran"), "90": ("🇹🇷", "Turkey"), "964": ("🇮🇶", "Iraq"), "963": ("🇸🇾", "Syria"), "961": ("🇱🇧", "Lebanon"), "962": ("🇯🇴", "Jordan"), "965": ("🇰🇼", "Kuwait"), "966": ("🇸🇦", "Saudi Arabia"), "967": ("🇾🇲", "Yemen"), "968": ("🇴🇲", "Oman"), "971": ("🇦🇪", "United Arab Emirates"), "972": ("🇮🇱", "Israel"), "973": ("🇧🇭", "Bahrain"), "974": ("🇶🇦", "Qatar"), "994": ("🇦🇿", "Azerbaijan"), "995": ("🇬🇪", "Georgia"), "996": ("🇰🇬", "Kyrgyzstan"), "992": ("🇹🇯", "Tajikistan"), "993": ("🇹🇲", "Turkmenistan"), "998": ("🇺🇿", "Uzbekistan"), "855": ("🇰🇭", "Cambodia"), "856": ("🇱🇦", "Laos"), "976": ("🇲🇳", "Mongolia"), "850": ("🇰🇵", "North Korea"), "55": ("🇧🇷", "Brazil"), "52": ("🇲🇽", "Mexico"), "54": ("🇦🇷", "Argentina"), "57": ("🇨🇴", "Colombia"), "51": ("🇵🇪", "Peru"), "58": ("🇻🇪", "Venezuela"), "56": ("🇨🇱", "Chile"), "593": ("🇪🇨", "Ecuador"), "591": ("🇧🇴", "Bolivia"), "595": ("🇵🇾", "Paraguay"), "598": ("🇺🇾", "Uruguay"), "502": ("🇬🇹", "Guatemala"), "503": ("🇸🇻", "El Salvador"), "504": ("🇭🇳", "Honduras"), "506": ("🇨🇷", "Costa Rica"), "507": ("🇵🇦", "Panama"), "509": ("🇭🇹", "Haiti"), "501": ("🇧🇿", "Belize"), "61": ("🇦🇺", "Australia"), "64": ("🇳🇿", "New Zealand"), "675": ("🇵🇬", "Papua New Guinea"), "679": ("🇫🇯", "Fiji"), "1246": ("🇧🇧", "Barbados"), "1876": ("🇯🇲", "Jamaica"), "53": ("🇨🇺", "Cuba"), "592": ("🇬🇾", "Guyana"), } clean_num = str(number).replace('+', '').replace(' ', '').replace('-', '').strip() sorted_prefixes = sorted(country_map.keys(), key=len, reverse=True) for prefix in sorted_prefixes: if clean_num.startswith(prefix): return country_map[prefix] return ("🌍", "Unknown") # ==================== SERVICE DETECTION SECTION ==================== def detect_service(full_sms): if not full_sms: return "SMS SERVICE" sms_lower = full_sms.lower() service_keywords = { "facebook": "FACEBOOK", "fb": "FACEBOOK", "instagram": "INSTAGRAM", "insta": "INSTAGRAM", "tiktok": "TIKTOK", "twitter": "TWITTER", "x.com": "TWITTER", "snapchat": "SNAPCHAT", "snap": "SNAPCHAT", "whatsapp": "WHATSAPP", "telegram": "TELEGRAM", "outlook": "MICROSOFT", "yahoo": "YAHOO", "paypal": "PAYPAL", "binance": "BINANCE", "coinbase": "COINBASE", "spotify": "SPOTIFY", "netflix": "NETFLIX", "uber": "UBER", "apple": "APPLE", "icloud": "APPLE", "bkash": "BKASH", "nagad": "NAGAD", "stripe": "STRIPE", "line": "LINE", "wechat": "WECHAT", "viber": "VIBER", "signal": "SIGNAL", "pubg": "PUBG", "free fire": "FREE FIRE", } for keyword, service_name in sorted(service_keywords.items(), key=lambda x: len(x[0]), reverse=True): if keyword in sms_lower: return service_name return "SMS SERVICE" # ==================== KEYBOARDS SECTION ==================== # GET 2FA এবং SEARCH OTP বাটনের স্থান পরিবর্তন করা হয়েছে (swap) from telegram import KeyboardButton, ReplyKeyboardMarkup def main_keyboard(user_id): keyboard = [ [ KeyboardButton( text="📞 GET NUMBER", style="success" ) ], [ KeyboardButton( text="🔍 SEARCH OTP", style="primary" ) ], [ KeyboardButton( text="⚡ GET 2FA", style="danger" ), KeyboardButton( text="💰 BALANCE", style="success" ) ], [ KeyboardButton( text="REFER AND EARN", style="primary" ), KeyboardButton( text="👤 PROFILE", style="primary" ) ], [ KeyboardButton( text="🏆 LEADERBOARD", style="danger" ) ], [ KeyboardButton( text="💬 SUPPORT", style="primary" ) ] ] if is_admin(user_id): keyboard.append([ KeyboardButton( text="⚙️ ADMIN PANEL ⚙️", style="danger" ) ]) return ReplyKeyboardMarkup( keyboard, resize_keyboard=True ) def cancel_keyboard(): keyboard = [[KeyboardButton("❌ CANCEL", style="danger")]] return ReplyKeyboardMarkup(keyboard, resize_keyboard=True) def admin_main_keyboard(): keyboard = [ [KeyboardButton("👥 USER MANAGEMENT", style="success")], [KeyboardButton("⚙️ SYSTEM CONFIGURATION", style="success")], [KeyboardButton("🔙 BACK TO MAIN", style="danger")] ] return ReplyKeyboardMarkup(keyboard, resize_keyboard=True) def user_management_keyboard(): keyboard = [ [KeyboardButton("📢 SEND MESSAGE TO ALL USERS", style="success")], [KeyboardButton("🆔 ALL USER ID", style="primary")], [KeyboardButton("📜 BAN USER LIST", style="primary")], [KeyboardButton("💰 ALL USER BALANCE", style="primary")], [KeyboardButton("🔙 BACK TO ADMIN", style="danger")] ] return ReplyKeyboardMarkup(keyboard, resize_keyboard=True) def system_config_keyboard(): keyboard = [ [KeyboardButton("📈 TODAY ALL STATUS", style="success"), KeyboardButton("👤 USER STATUS CHECK", style="success")], [KeyboardButton("⛔ BAN USER", style="danger"), KeyboardButton("🔓 UNBAN USER", style="primary")], [KeyboardButton("📜 BAN USER LIST", style="primary")], [KeyboardButton("➖ REMOVE BALANCE", style="danger"), KeyboardButton("➕ ADD BALANCE", style="success")], [KeyboardButton("🔙 BACK TO ADMIN", style="danger")] ] return ReplyKeyboardMarkup(keyboard, resize_keyboard=True) def withdraw_method_keyboard(): keyboard = ReplyKeyboardMarkup([ [KeyboardButton("📱 BKASH", style="success"), KeyboardButton("💵 NAGAD", style="success")], [KeyboardButton("🚀 ROCKET", style="primary"), KeyboardButton("🏦 BINANCE", style="primary")], [KeyboardButton("❌ CANCEL", style="danger")] ], resize_keyboard=True) return keyboard # ==================== HELPER FUNCTIONS SECTION ==================== def format_balance(balance): return f"{balance:.2f}" def extract_otp(text): if not text or text == "No Content": return "N/A" spaced_otp = re.search(r'\b(\d{3}\s\d{3})\b', text) if spaced_otp: return spaced_otp.group(1).replace(" ", "") match = re.search(r'\b(\d{4,8})\b', text) return match.group(1) if match else "N/A" def normalize_number(num): return re.sub(r'\D', '', str(num)) def mask_number(num): if len(num) > 6: return f"{num[:4]}****{num[-6:]}" return num def get_date_reset_time(): now = datetime.now() today_midnight = datetime(now.year, now.month, now.day, 0, 0, 0) return today_midnight def is_valid_bangladesh_number(number): number = re.sub(r'\D', '', str(number)) return len(number) == 11 and number.startswith('01') def is_range_request(param): return 'X' in param.upper() def is_referral_request(param): return param.isdigit() # ==================== DATABASE FUNCTIONS SECTION ==================== def load_data(filename=USER_DATA_FILE): if not os.path.exists(filename): with open(filename, "w") as f: json.dump({}, f) return {} try: with open(filename, "r") as f: return json.load(f) except: return {} def save_data(data, filename=USER_DATA_FILE): with open(filename, "w") as f: json.dump(data, f, indent=4) def get_user(uid): uid = str(uid) data = load_data() if uid not in data: data[uid] = {"user_id": uid, "balance": 0.0, "total_numbers": 0, "referral_count": 0} save_data(data) return data[uid] async def update_db_balance(uid, amount): uid = str(uid) data = load_data() if uid in data: data[uid]["balance"] = round(data[uid].get("balance", 0.0) + amount, 2) save_data(data) return data[uid]["balance"] return 0.0 def get_all_users(): data = load_data(USER_DATA_FILE) return list(data.keys()) if data else [] def user_exists(uid): data = load_data(USER_DATA_FILE) return str(uid) in data # ==================== STATS FUNCTIONS SECTION ==================== def load_stats(): if not os.path.exists(STATS_FILE): with open(STATS_FILE, "w") as f: json.dump({}, f) return {} try: with open(STATS_FILE, "r") as f: return json.load(f) except: return {} def save_stats(stats): with open(STATS_FILE, "w") as f: json.dump(stats, f, indent=4) def add_number_taken(uid, count=1): uid = str(uid) stats = load_stats() if uid not in stats: stats[uid] = {"numbers_taken": [], "otps_received": []} now = datetime.now().isoformat() for _ in range(count): stats[uid]["numbers_taken"].append(now) log_global_activity(uid, "NUMBER_TAKEN", {"count": count}) save_stats(stats) def add_otp_received(uid): uid = str(uid) stats = load_stats() if uid not in stats: stats[uid] = {"numbers_taken": [], "otps_received": []} stats[uid]["otps_received"].append(datetime.now().isoformat()) save_stats(stats) def get_user_stats(uid): uid = str(uid) stats = load_stats() user_stats = stats.get(uid, {"numbers_taken": [], "otps_received": []}) now = datetime.now() today_midnight = get_date_reset_time() last_24h = now - timedelta(hours=24) last_7d = now - timedelta(days=7) numbers_taken = user_stats.get("numbers_taken", []) otps_received = user_stats.get("otps_received", []) today_numbers = sum(1 for t in numbers_taken if datetime.fromisoformat(t) >= today_midnight) today_otps = sum(1 for t in otps_received if datetime.fromisoformat(t) >= today_midnight) last24h_numbers = sum(1 for t in numbers_taken if datetime.fromisoformat(t) > last_24h) last24h_otps = sum(1 for t in otps_received if datetime.fromisoformat(t) > last_24h) last7d_numbers = sum(1 for t in numbers_taken if datetime.fromisoformat(t) > last_7d) last7d_otps = sum(1 for t in otps_received if datetime.fromisoformat(t) > last_7d) total_numbers = len(numbers_taken) total_otps = len(otps_received) return { "total_numbers": total_numbers, "total_otps": total_otps, "today_numbers": today_numbers, "today_otps": today_otps, "last24h_numbers": last24h_numbers, "last24h_otps": last24h_otps, "last7d_numbers": last7d_numbers, "last7d_otps": last7d_otps } def log_global_activity(uid, action, details): if not os.path.exists(ACTIVITY_LOGS_FILE): with open(ACTIVITY_LOGS_FILE, "w") as f: json.dump([], f) try: with open(ACTIVITY_LOGS_FILE, "r") as f: logs = json.load(f) except: logs = [] now = datetime.now() logs.append({ "uid": str(uid), "action": action, "details": details, "timestamp": now.isoformat(), "date": now.strftime("%d/%m/%Y"), "time": now.strftime("%H:%M:%S") }) with open(ACTIVITY_LOGS_FILE, "w") as f: json.dump(logs, f, indent=4) def get_global_system_stats(): stats = load_stats() now = datetime.now() today_midnight = datetime(now.year, now.month, now.day) last_7d = now - timedelta(days=7) total_n = total_o = today_n = today_o = seven_n = seven_o = 0 for uid in stats: u = stats[uid] n_list = u.get("numbers_taken", []) o_list = u.get("otps_received", []) total_n += len(n_list) total_o += len(o_list) for t in n_list: dt = datetime.fromisoformat(t) if dt >= today_midnight: today_n += 1 if dt >= last_7d: seven_n += 1 for t in o_list: dt = datetime.fromisoformat(t) if dt >= today_midnight: today_o += 1 if dt >= last_7d: seven_o += 1 return today_n, today_o, seven_n, seven_o, total_n, total_o # ==================== LEADERBOARD SECTION ==================== async def leaderboard_command(update: Update, context: ContextTypes.DEFAULT_TYPE): uid = update.effective_user.id if is_user_banned(uid): await update.message.reply_text("🚫 YOU ARE BANNED 🚫", reply_markup=main_keyboard(uid)) return stats_data = load_stats() today_midnight = get_date_reset_time() user_data_all = load_data(USER_DATA_FILE) user_today_counts = [] for uid_str, user_stats in stats_data.items(): otps_received = user_stats.get("otps_received", []) today_count = 0 for ts in otps_received: try: dt = datetime.fromisoformat(ts) if dt >= today_midnight: today_count += 1 except: continue if today_count > 0: name = user_data_all.get(uid_str, {}).get("full_name") if not name: name = user_data_all.get(uid_str, {}).get("username") if not name: name = f"User {uid_str}" user_today_counts.append((uid_str, today_count, html.escape(name))) user_today_counts.sort(key=lambda x: x[1], reverse=True) top10 = user_today_counts[:10] if not top10: msg = ( "🏆 TOP 10 OTP LEADERBOARD 🏆\n" "━━━━━━━━━━━━━━━━━━━━\n\n" "❌ আজ পর্যন্ত কেউ OTP পায়নি।\n" ) else: msg = ( "🏆 TOP 10 OTP RECEIVERS (TODAY) 🏆\n" "━━━━━━━━━━━━━━━━━━━━\n\n" ) for idx, (uid_str, count, name) in enumerate(top10, 1): if idx == 1: medal = "🥇" elif idx == 2: medal = "🥈" elif idx == 3: medal = "🥉" else: medal = f"{idx}️⃣" msg += f"{medal} {name}\n 🔑 {count} OTPs\n\n" msg += ( "━━━━━━━━━━━━━━━━━━━━\n" "📊 প্রতিদিন রাত ১২টায় রিসেট হয়" ) await update.message.reply_text(msg, parse_mode="HTML", reply_markup=main_keyboard(uid)) # ==================== 2FA CODE GENERATOR SECTION ==================== def generate_2fa_code(secret_key): try: clean_secret = secret_key.replace(" ", "").strip() totp = pyotp.TOTP(clean_secret) otp = totp.now() return otp, clean_secret except: return None, None async def get_2fa_code(update: Update, context: ContextTypes.DEFAULT_TYPE): uid = update.effective_user.id if is_user_banned(uid): await update.message.reply_text("🚫 YOU ARE BANNED 🚫", reply_markup=main_keyboard(uid)) return context.user_data["mode"] = "get_2fa" await update.message.reply_text( "⚡ GET 2FA CODE ⚡\n\n" "
🔑 ENTER YOUR 2FA SECRET KEY:
", parse_mode="HTML" ) async def process_2fa_key(update: Update, context: ContextTypes.DEFAULT_TYPE): uid = update.effective_user.id secret_key = update.message.text.strip() context.user_data["mode"] = None otp_code, clean_key = generate_2fa_code(secret_key) if otp_code is None: await update.message.reply_text( "❌ INVALID 2FA SECRET KEY\n\n⚠️ Please send a valid base32 key.", parse_mode="HTML", reply_markup=main_keyboard(uid) ) return now = datetime.now() final_msg = ( "✅ 2FA CODE GENERATED!\n\n" f"
🔑 KEY: {clean_key}
\n" f"
🔢 CODE: {otp_code}
\n" f"
⏳ EXPIRES IN: 30 SECONDS
\n" f"📅 {now.strftime('%d %B, %Y')} | {now.strftime('%I:%M %p')}" ) await update.message.reply_text(final_msg, parse_mode="HTML") # ==================== GET NUMBER — LIVEACCESS SERVICE SELECTION (FIXED: REMOVED HTML TAGS) ==================== # ── রঙের চক্র: লাল→হলুদ→সবুজ→নীল→গোলাপি... ── _SVC_STYLES = ["danger", "primary", "success", "danger", "primary", "success", "danger", "primary", "success", "danger", "primary", "success"] def _build_services_keyboard(services): buttons = [] for i, svc in enumerate(services): sid = svc.get("sid", f"Service {i+1}") ranges = svc.get("ranges", []) label = f"🚀 {sid} ({len(ranges)})" color = _SVC_STYLES[i % len(_SVC_STYLES)] buttons.append([InlineKeyboardButton(label, callback_data=f"svc_{i}", style=color)]) # CUSTOM RANGE — আলাদা রঙে (danger = লাল/বাদামি) buttons.append([InlineKeyboardButton("⚙️ CUSTOM RANGE", callback_data="custom_range", style="danger")]) return InlineKeyboardMarkup(buttons) def _build_countries_keyboard(ranges, service_idx): """Range গুলো থেকে দেশের Flag + নাম দেখায়।""" btns = [] seen = {} # country button colors rotate clrs = ["primary", "success", "danger", "primary", "success", "danger"] ci = 0 for i, r in enumerate(ranges[:24]): prefix = re.sub(r'[xX]+$', '', str(r)).strip() prefix_clean = re.sub(r'\D', '', prefix) flag, cname = get_country_info(prefix_clean) label = f"{flag} {cname}" if label not in seen: seen[label] = i color = clrs[ci % len(clrs)] ci += 1 btns.append(InlineKeyboardButton(label, callback_data=f"rng_{i}", style=color)) rows = [btns[j:j+2] for j in range(0, len(btns), 2)] rows.append([InlineKeyboardButton("◀️ BACK", callback_data="back_services", style="danger")]) return InlineKeyboardMarkup(rows) async def show_app_selection(update, context): uid = update.effective_user.id if is_user_banned(uid): await update.message.reply_text("🚫 YOU ARE BANNED 🚫", reply_markup=main_keyboard(uid)) return services = get_cached_services() if not services: await _do_liveaccess_fetch() services = get_cached_services() if not services: await update.message.reply_text( "⚠️ কোনো সার্ভিস উপলব্ধ নেই\n⏳ কিছুক্ষণ পর আবার চেষ্টা করুন।", parse_mode="HTML", reply_markup=main_keyboard(uid) ) return context.user_data["la_services"] = services keyboard = _build_services_keyboard(services) await update.message.reply_text( "📞 GET NUMBER\n\n" "
✨ নিচ থেকে আপনার পছন্দের Service নির্বাচন করুন:
", parse_mode="HTML", reply_markup=keyboard ) # ==================== AUTO OTP MONITOR SECTION ==================== async def monitor_loop(app): while True: try: r = await client_async.get(f"{BASE_URL}/api/otps") res = r.json() if "data" in res and "otps" in res["data"]: otps = res["data"]["otps"] paid_data = load_data(PAID_SMS_FILE) range_db = load_data(DATA_RANGE_FILE) paid_keys_set = set(paid_data.keys()) processed_in_session = set() for otp in otps: num = normalize_number(otp.get("number", "")) full_sms = otp.get('message') or otp.get('otp') or otp.get('sms') or "No SMS Content" otp_code = extract_otp(full_sms) otp_id = str(otp.get("otp_id", "")) sms_key = otp_id if otp_id else f"{num}_{full_sms}" if (num in active_numbers and sms_key not in paid_keys_set and sms_key not in processed_in_session): details = active_numbers[num] paid_keys_set.add(sms_key) processed_in_session.add(sms_key) paid_data[sms_key] = {"uid": details["uid"], "otp": otp_code} await update_db_balance(details["uid"], OTP_RATE) add_otp_received(details["uid"]) log_global_activity(details["uid"], "OTP_RECEIVED", {"number": num, "otp": otp_code, "sms": full_sms}) num_range_info = range_db.get(num, {}).get("range", "") if not num_range_info: num_range_info = active_numbers.get(num, {}).get("range", "") if not num_range_info and num: _d = re.sub(r'\D', '', str(num)) num_range_info = (_d[:-3] + 'XXX') if len(_d) > 3 else (_d + 'XXX') country_flag, country_name = get_country_info(num) service_name = detect_service(full_sms) clean_num = num.replace('+', '').strip() full_number = f"+{clean_num}" masked_number = f"+{mask_number(clean_num)}" safe_full_sms = html.escape(str(full_sms)) safe_otp_code = html.escape(str(otp_code)) user_msg = ( f"✅ OTP RECEIVE SUCCESSFUL ✅\n\n" f"
📶 RANGE: {num_range_info}
\n" f"
🌍 COUNTRY: {country_flag} {country_name}
\n" f"
📱 SERVICE: {service_name}
\n" f"
📞 NUMBER: {full_number}
\n" f"
🔑 OTP: {safe_otp_code}
\n\n" f"
📩 FULL SMS:\n{safe_full_sms}
\n\n" f"💵 ADD BALANCE FOR {OTP_RATE:.2f} BDT" ) group_msg = ( f"✅ OTP RECEIVE SUCCESSFUL ✅\n\n" f"
📶 RANGE: {num_range_info}
\n" f"
🌍 COUNTRY: {country_flag} {country_name}
\n" f"
📱 SERVICE: {service_name}
\n" f"
📞 NUMBER: {masked_number}
\n" f"
🔑 OTP: {safe_otp_code}
\n\n" f"
📩 FULL SMS:\n{safe_full_sms}
" ) # ★★★ GROUP BUTTONS COLOR UPDATED ★★★ group_buttons = InlineKeyboardMarkup([ [ InlineKeyboardButton("‼️ PANEL", url="https://t.me/VOLT_X_LITE_BOT", style="danger"), InlineKeyboardButton("📢 CHANNEL", url="https://t.me/OTP_MASTER_MURAD_100", style="success") ] ]) try: await app.bot.send_message(details["uid"], user_msg, parse_mode="HTML") except Exception as e: print(f"❌ User Message Send Fail: {e}") try: await app.bot.send_message(OTP_GROUP_ID, group_msg, parse_mode="HTML", reply_markup=group_buttons) except Exception as e: print(f"❌ Group Send Fail: {e}") save_data(paid_data, PAID_SMS_FILE) current_time = datetime.now() for num_key in list(active_numbers.keys()): entry = active_numbers[num_key] if 'timestamp' not in entry: entry['timestamp'] = current_time elif (current_time - entry['timestamp']).total_seconds() > 3600: del active_numbers[num_key] except Exception as e: print(f"Monitor Error: {e}") await asyncio.sleep(CHECK_INTERVAL) # ==================== WORKER & API SECTION ==================== async def fetch_number_async(range_str): try: r = await client_async.post( f"{BASE_URL}/api/getnum", json={"range": range_str, "is_national": False} ) data = r.json() d = data.get("data", {}) if "full_number" in d: return { "number": d["full_number"], "otp_now": bool(d.get("otp_now", False)), "otp": d.get("otp"), "sms": d.get("sms"), } except Exception as e: print(f"Fetch number error: {e}") return None async def fast_allocate_number(query, context, range_text, sid): uid = query.from_user.id if is_user_banned(uid): await query.message.edit_text("🚫 YOU ARE BANNED 🚫") return try: res = await fetch_number_async(range_text) except Exception as e: await query.message.edit_text(f"❌ Server error: {str(e)[:100]}") return if not res or not res.get("number"): await query.message.edit_text( "❌ Number পাওয়া যায়নি।\n\n" "
⚠️ এই range-এ এখন number নেই বা server busy।\n" "আরেকটি range চেষ্টা করুন।
", parse_mode="HTML", reply_markup=InlineKeyboardMarkup([[ InlineKeyboardButton("🔙 BACK", callback_data="back_services", style="danger") ]]) ) return clean_num = normalize_number(res["number"]) add_number_taken(uid, 1) last_range[uid] = range_text active_numbers[clean_num] = {"uid": uid, "range": range_text, "timestamp": datetime.now()} save_number_range_info(uid, clean_num, range_text) country_flag, country_name = get_country_info(clean_num) if res.get("otp_now") and res.get("otp"): otp_safe = html.escape(str(res["otp"])) sms_safe = html.escape(str(res.get("sms") or "")) add_otp_received(uid) text = ( f"✅ YOUR NUMBER ✅\n\n" f"
🌍 COUNTRY: {country_flag} {html.escape(country_name)}
\n" f"
📶 RANGE: {range_text}
\n" f"
📞 NUMBER: +{clean_num}
\n" f"
🔑 OTP: {otp_safe}
" + (f"\n
📩 SMS: {sms_safe}
" if sms_safe else "") + "\n\n✅ OTP RECEIVED INSTANTLY!" ) else: text = ( f"✅ YOUR NUMBER ✅\n\n" f"
🌍 COUNTRY: {country_flag} {html.escape(country_name)}
\n" f"
📶 RANGE: {range_text}
\n" f"
📞 NUMBER: +{clean_num}
\n\n" f"📩 SMS STATUS: ⏳ WAITING..." ) # ★★★ USER BUTTONS COLOR UPDATED ★★★ keyboard = InlineKeyboardMarkup([ [InlineKeyboardButton("🔄 SAME RANGE", callback_data="same_range", style="success")], [InlineKeyboardButton("📢 OTP GROUP", url="https://t.me/volt_x_lite_otp", style="primary")] ]) try: await query.message.edit_text(text, parse_mode="HTML", reply_markup=keyboard) except Exception as e: print(f"fast_allocate edit error: {e}") async def worker(): while True: task = await request_queue.get() try: if task['type'] == 'process_numbers': await process_numbers(task['update'], task['context'], task['range_text'], task['count']) elif task['type'] == 'search_otp': await perform_otp_search(task['update'], task['context'], task['target_num']) elif task['type'] == 'auto_number': await process_auto_number(task['update'], task['context'], task['range_text']) except Exception as e: print(f"Worker Error: {e}") finally: request_queue.task_done() # ==================== AUTO NUMBER FROM LINK / DEEP LINK ==================== async def process_auto_number(update, context, range_text): uid = update.effective_user.id chat_id = update.effective_chat.id if is_user_banned(uid): await context.bot.send_message(chat_id=chat_id, text="🚫 YOU ARE BANNED 🚫", reply_markup=main_keyboard(uid)) return status_msg = await context.bot.send_message(chat_id=chat_id, text="🔍 SEARCHING...") try: res = await fetch_number_async(range_text) if not res: await status_msg.edit_text("❌ NO NUMBERS FOUND. TRY A VALID RANGE.") return generated_num = normalize_number(res["number"]) if res else None if not generated_num: await status_msg.edit_text("❌ NO NUMBERS FOUND. TRY A VALID RANGE.") return add_number_taken(uid, 1) last_range[uid] = range_text active_numbers[generated_num] = {"uid": uid, "range": range_text, "timestamp": datetime.now()} save_number_range_info(uid, generated_num, range_text) country_flag, country_name = get_country_info(generated_num) if res.get("otp_now") and res.get("otp"): instant_otp = html.escape(str(res["otp"])) instant_sms = html.escape(str(res.get("sms") or "")) add_otp_received(uid) final_text = ( f"✅ YOUR NUMBER DETAILS ✅\n\n" f"
🌍 COUNTRY: {country_flag} {country_name}
\n" f"
📶 RANGE: {range_text}
\n\n" f"
📞 NUMBER: +{generated_num}
\n\n" f"
🔑 OTP: {instant_otp}
\n" + (f"
📩 SMS: {instant_sms}
\n" if instant_sms else "") + f"\n✅ OTP RECEIVED INSTANTLY!" ) else: final_text = ( f"✅ YOUR NUMBER DETAILS ✅\n\n" f"
🌍 COUNTRY: {country_flag} {country_name}
\n" f"
📶 RANGE: {range_text}
\n\n" f"
📞 NUMBER: +{generated_num}
\n\n" f"📩 SMS STATUS: ⏳ WAITING..." ) # ★★★ SAME RANGE BUTTON COLOR IN AUTO NUMBER (FOR CONSISTENCY) ★★★ keyboard = InlineKeyboardMarkup([ [InlineKeyboardButton("🔄 SAME RANGE", callback_data="same_range", style="success")], [InlineKeyboardButton("📢 OTP GROUP", url="https://t.me/volt_x_lite_otp", style="primary")] ]) await status_msg.edit_text(final_text, parse_mode="HTML", reply_markup=keyboard) except Exception as e: print(f"Auto Number Error: {e}") await status_msg.edit_text(f"❌ Error: {str(e)}") # ==================== USER PANEL — PROCESS NUMBERS ==================== async def process_numbers(update_or_query, context, range_text, count): if isinstance(update_or_query, Update) and update_or_query.callback_query: uid = update_or_query.callback_query.from_user.id chat_id = update_or_query.callback_query.message.chat_id else: uid = update_or_query.effective_user.id chat_id = update_or_query.effective_chat.id if is_user_banned(uid): await context.bot.send_message(chat_id=chat_id, text="🚫 YOU ARE BANNED 🚫", reply_markup=main_keyboard(uid)) return status_msg = await context.bot.send_message(chat_id=chat_id, text="🔍 SEARCHING . . .") try: add_number_taken(uid, count) last_range[uid] = range_text tasks = [fetch_number_async(range_text) for _ in range(count)] results = await asyncio.gather(*tasks) valid_results = [r for r in results if r and r.get("number")] if not valid_results: await status_msg.edit_text("❌ NO NUMBERS FOUND. TRY A VALID RANGE.") return num_entries = [] for r in valid_results: clean_num = normalize_number(r["number"]) if clean_num: active_numbers[clean_num] = {"uid": uid, "range": range_text, "timestamp": datetime.now()} save_number_range_info(uid, clean_num, range_text) num_entries.append({ "num": clean_num, "otp_now": r.get("otp_now", False), "otp": r.get("otp"), "sms": r.get("sms"), }) if not num_entries: await status_msg.edit_text("❌ NO NUMBERS FOUND. TRY A VALID RANGE.") return country_flag, country_name = get_country_info(num_entries[0]["num"]) num_lines = [] for entry in num_entries: if entry["otp_now"] and entry["otp"]: otp_safe = html.escape(str(entry["otp"])) sms_safe = html.escape(str(entry.get("sms") or "")) add_otp_received(uid) line = ( f"
📞 NUMBER: +{entry['num']}\n" f"🔑 OTP: {otp_safe}" + (f"\n📩 SMS: {sms_safe}" if sms_safe else "") + "
" ) else: line = f"
📞 NUMBER: +{entry['num']}
" num_lines.append(line) num_list_text = "\n".join(num_lines) any_instant = any(e["otp_now"] and e["otp"] for e in num_entries) sms_status = "✅ OTP RECEIVED INSTANTLY!" if any_instant else "📩 SMS STATUS: ⏳ WAITING..." final_text = ( f"✅ YOUR NUMBER DETAILS ✅\n\n" f"
🌍 COUNTRY: {country_flag} {country_name}
\n" f"
📶 RANGE: {range_text}
\n\n" f"{num_list_text}\n\n" f"{sms_status}" ) # ★★★ PROCESS NUMBERS BUTTONS COLOR UPDATE (FOR SINGLE NUMBER) ★★★ keyboard = InlineKeyboardMarkup([ [InlineKeyboardButton("🔄 SAME RANGE", callback_data="same_range", style="success")], [InlineKeyboardButton("📢 OTP GROUP", url="https://t.me/volt_x_lite_otp", style="primary")] ]) await status_msg.edit_text(final_text, parse_mode="HTML", reply_markup=keyboard) except Exception as e: print(f"Process Number Error: {e}") await status_msg.edit_text(f"❌ System Error: {str(e)}") async def perform_otp_search(update, context, target_num): uid = str(update.effective_user.id) if is_user_banned(int(uid)): await update.message.reply_text("🚫 YOU ARE BANNED 🚫", reply_markup=main_keyboard(int(uid))) return status_msg = await update.message.reply_text("🔍 SEARCHING IN SERVER...") try: r = await client_async.get(f"{BASE_URL}/api/otps") res = r.json() if "data" in res and "otps" in res["data"]: all_otps = res["data"]["otps"] found_otps = [o for o in all_otps if normalize_number(o.get("number", "")) == target_num] if not found_otps: error_msg = ( "━━━━━━━━━━━━━━━━━━\n❌ NO OTP FOUND\n━━━━━━━━━━━━━━━━━━\n\n" f"📞 NUMBER:\n`+{target_num}`\n\n⏳ PLEASE TRY AGAIN LATER\n━━━━━━━━━━━━━━━━━━" ) await status_msg.edit_text(error_msg, parse_mode="Markdown") await update.message.reply_text("🔙 RETURNING TO MAIN MENU...", reply_markup=main_keyboard(int(uid))) else: await status_msg.delete() paid_data = load_data(PAID_SMS_FILE) for o in found_otps: full_sms = o.get('message') or o.get('otp') or o.get('sms') or "No Content Found" otp_code = extract_otp(full_sms) otp_id = str(o.get("otp_id", "")) sms_key = otp_id if otp_id else f"{target_num}_{full_sms}" if sms_key in paid_data: payment_status = "❌ ALREADY PAID" else: await update_db_balance(uid, OTP_RATE) add_otp_received(uid) paid_data[sms_key] = {"uid": uid, "otp": otp_code} payment_status = f"💵 ADD BALANCE FOR {OTP_RATE:.2f} BDT" save_data(paid_data, PAID_SMS_FILE) country_flag, country_name = get_country_info(target_num) service_name = detect_service(full_sms) msg = ( f"✅ OTP FOUND!\n\n" f"
🌍 COUNTRY: {country_flag} {country_name}
\n" f"
📱 SERVICE: {service_name}
\n" f"
📞 NUMBER: +{target_num}
\n" f"
🔑 OTP: {html.escape(otp_code)}
\n\n" f"
📩 FULL SMS:\n{html.escape(str(full_sms))}
\n\n" f"{payment_status}" ) await update.message.reply_text(msg, parse_mode="HTML", reply_markup=main_keyboard(int(uid))) else: await status_msg.edit_text("❌ SERVER RETURNED AN ERROR.") await update.message.reply_text("🔙 Returning to Main Menu...", reply_markup=main_keyboard(int(uid))) except Exception as e: try: await status_msg.edit_text(f"❌ Error: {str(e)}") except: await update.message.reply_text(f"❌ Error: {str(e)}") await update.message.reply_text("🔙 Returning to Main Menu...", reply_markup=main_keyboard(int(uid))) # ==================== REFER AND EARN SECTION ==================== async def refer_command(update: Update, context: ContextTypes.DEFAULT_TYPE): uid = update.effective_user.id if is_user_banned(uid): await update.message.reply_text("🚫 YOU ARE BANNED 🚫", reply_markup=main_keyboard(uid)) return user_data = get_user(uid) bot_info = await context.bot.get_me() referral_link = f"https://t.me/{bot_info.username}?start={uid}" successful_refers = get_referral_count(uid) total_reward = float(successful_refers) * REFERRAL_PRICE refer_msg = ( f"🎁 REFER AND EARN SYSTEM 🎁\n\n" f"
🚀 INVITE FRIENDS & EARN {int(REFERRAL_PRICE)} BDT EACH! 💸
\n\n" f"🔗 YOUR REFERRAL LINK:\n" f"
{referral_link}
\n\n" f"📊 YOUR STATS:\n" f"
👥 TOTAL REFERS: {successful_refers}\n" f"💰 TOTAL EARNED: {format_balance(total_reward)} BDT
\n\n" f"✨ SHARE LINK & EARN MONEY! ✨" ) await update.message.reply_text( refer_msg, parse_mode="HTML", disable_web_page_preview=True, reply_markup=InlineKeyboardMarkup([[ InlineKeyboardButton("👥 YOUR REFERRAL", callback_data=f"my_ref_{uid}", style="primary") ]]) ) # ==================== WITHDRAW FUNCTIONS ==================== async def withdraw_method_selected(update: Update, context: ContextTypes.DEFAULT_TYPE): text = update.message.text.strip() uid = update.effective_user.id if text == "❌ CANCEL": context.user_data["withdraw_mode"] = None await update.message.reply_text("❌ WITHDRAW CANCELLED", reply_markup=main_keyboard(uid)) return method_map = {"📱 BKASH": "BKASH", "💵 NAGAD": "NAGAD", "🚀 ROCKET": "ROCKET", "🏦 BINANCE": "BINANCE"} if text in method_map: balance = get_user(uid)['balance'] context.user_data["withdraw_method"] = method_map[text] context.user_data["withdraw_mode"] = "amount" msg = ( f"
💸 SEND YOUR AMOUNT!\n" f"💵 TOTAL BALANCE: {format_balance(balance)} BDT
\n\n" f"
📉 MINIMUM WITHDRAW {MIN_WITHDRAW} BDT
" ) await update.message.reply_text(msg, parse_mode="HTML", reply_markup=cancel_keyboard()) else: await update.message.reply_text("⚠️ PLEASE SELECT A VALID PAYMENT METHOD!", reply_markup=withdraw_method_keyboard()) async def withdraw_amount_received(update: Update, context: ContextTypes.DEFAULT_TYPE): text = update.message.text.strip() uid = update.effective_user.id if text == "❌ CANCEL": context.user_data["withdraw_mode"] = None await update.message.reply_text("❌ WITHDRAW CANCELLED", reply_markup=main_keyboard(uid)) return try: amount = float(text) except: await update.message.reply_text("⚠️ PLEASE SEND A VALID AMOUNT!", reply_markup=cancel_keyboard()) return balance = get_user(uid)['balance'] if amount < MIN_WITHDRAW or amount > MAX_WITHDRAW: await update.message.reply_text(f"📉 MIN: {MIN_WITHDRAW} BDT | MAX: {MAX_WITHDRAW} BDT", reply_markup=cancel_keyboard()) return if amount > balance: await update.message.reply_text("🚫 INSUFFICIENT BALANCE!", reply_markup=cancel_keyboard()) return context.user_data["withdraw_amount"] = amount context.user_data["withdraw_mode"] = "number" await update.message.reply_text( "📞 PLEASE SEND YOUR PAYMENT NUMBER!\n\n
🔢 EXAMPLE: 017XXXXXXXX
", parse_mode="HTML", reply_markup=cancel_keyboard() ) async def withdraw_number_received(update: Update, context: ContextTypes.DEFAULT_TYPE): text = update.message.text.strip() uid = update.effective_user.id if text == "❌ CANCEL": context.user_data["withdraw_mode"] = None await update.message.reply_text("❌ WITHDRAW CANCELLED", reply_markup=main_keyboard(uid)) return if not is_valid_bangladesh_number(text): await update.message.reply_text("⚠️ PLEASE SEND VALID NUMBER! 017XXXXXXXX", reply_markup=cancel_keyboard()) return method = context.user_data.get("withdraw_method") amount = context.user_data.get("withdraw_amount") payment_number = text payment_id = generate_payment_id() context.user_data["temp_withdraw"] = { "method": method, "amount": amount, "number": payment_number, "payment_id": payment_id } msg = ( "✨ YOUR PAYMENT DETAILS! ✨\n\n" f"
📝 METHOD: {method}\n" f"📞 NUMBER: {payment_number}\n\n" f"✅ CORRECT → CONFIRM\n❌ WRONG → CANCEL
" ) await update.message.reply_text( msg, parse_mode="HTML", reply_markup=InlineKeyboardMarkup([[ InlineKeyboardButton("❌ CANCEL", callback_data="withdraw_cancel", style="danger"), InlineKeyboardButton("✅ CONFIRM", callback_data="withdraw_confirm", style="success") ]]) ) async def process_withdraw_confirm(update: Update, context: ContextTypes.DEFAULT_TYPE): query = update.callback_query uid = query.from_user.id await query.answer() temp_data = context.user_data.get("temp_withdraw") if not temp_data: await query.message.reply_text("⚠️ SESSION EXPIRED.", reply_markup=main_keyboard(uid)) return method = temp_data["method"] amount = temp_data["amount"] payment_number = temp_data["number"] payment_id = temp_data["payment_id"] new_balance = await update_db_balance(uid, -amount) wr = load_withdraw_requests() wr[str(payment_id)] = { "user_id": uid, "method": method, "amount": amount, "number": payment_number, "payment_id": payment_id, "status": "pending", "timestamp": datetime.now().isoformat() } save_withdraw_requests(wr) await query.message.edit_text( f"✅ WITHDRAWAL REQUEST SUBMITTED ✅\n\n" f"
📝 METHOD: {method}\n" f"📞 NUMBER: {payment_number}\n" f"💰 AMOUNT: {format_balance(amount)} BDT\n" f"🆔 ID: {payment_id}
", parse_mode="HTML" ) await context.bot.send_message(uid, "🎉 WITHDRAW REQUEST SUBMITTED!", parse_mode="HTML", reply_markup=main_keyboard(uid)) admin_msg = ( f"✅ NEW WITHDRAWAL REQUEST\n\n" f"
🆔 USER: {uid}\n" f"📝 METHOD: {method}\n" f"📞 NUMBER: {payment_number}\n" f"💰 AMOUNT: {format_balance(amount)} BDT\n" f"🆔 ID: {payment_id}
" ) admin_kb = InlineKeyboardMarkup([[ InlineKeyboardButton("❌ REJECT", callback_data=f"admin_reject_{payment_id}", style="danger"), InlineKeyboardButton("✅ APPROVE", callback_data=f"admin_approve_{payment_id}", style="success") ]]) for admin_id in ADMINS: try: await context.bot.send_message(admin_id, admin_msg, parse_mode="HTML", reply_markup=admin_kb) except Exception as e: print(f"Admin notify fail {admin_id}: {e}") context.user_data["temp_withdraw"] = None context.user_data["withdraw_mode"] = None async def process_withdraw_cancel(update: Update, context: ContextTypes.DEFAULT_TYPE): query = update.callback_query uid = query.from_user.id await query.answer() context.user_data["temp_withdraw"] = None context.user_data["withdraw_mode"] = None await query.message.edit_text("❌ WITHDRAW CANCELLED") await context.bot.send_message(uid, "🔹 PLEASE USE THE BUTTONS BELOW:", reply_markup=main_keyboard(uid)) # ==================== ADMIN PANEL - WITHDRAW APPROVAL ==================== async def admin_approve_withdraw(update, context, payment_id): query = update.callback_query await query.answer() wr = load_withdraw_requests() if payment_id not in wr: await query.message.reply_text("⚠️ REQUEST NOT FOUND!") return rd = wr[payment_id] uid = rd["user_id"] method = rd["method"] amount = rd["amount"] payment_number = rd["number"] wr[payment_id]["status"] = "approved" save_withdraw_requests(wr) try: await context.bot.send_message( uid, f"🎉 WITHDRAWAL APPROVED!\n\n" f"
📝 METHOD: {method}\n" f"📞 NUMBER: {payment_number}\n" f"💰 AMOUNT: {format_balance(amount)} BDT
", parse_mode="HTML" ) except: pass await query.message.edit_text(f"✅ APPROVED | User: {uid} | Amount: {format_balance(amount)} BDT") async def admin_reject_withdraw(update, context, payment_id): query = update.callback_query await query.answer() wr = load_withdraw_requests() if payment_id not in wr: await query.message.reply_text("⚠️ REQUEST NOT FOUND!") return rd = wr[payment_id] uid = rd["user_id"] amount = rd["amount"] wr[payment_id]["status"] = "rejected" save_withdraw_requests(wr) try: await context.bot.send_message(uid, "❌ **WITHDRAWAL REQUEST REJECTED**\n\nContact admin for more info.", parse_mode="Markdown") except: pass await query.message.edit_text(f"❌ REJECTED | User: {uid} | Amount: {format_balance(amount)} BDT") # ==================== ADMIN PANEL - BALANCE MANAGEMENT ==================== async def admin_add_balance_start(update, context): context.user_data["add_balance_mode"] = True context.user_data["remove_balance_mode"] = False await update.message.reply_text("💰 SEND USER ID TO ADD BALANCE:") async def admin_remove_balance_start(update, context): context.user_data["remove_balance_mode"] = True context.user_data["add_balance_mode"] = False await update.message.reply_text("💸 SEND USER ID TO REMOVE BALANCE:") async def process_add_balance_user(update, context): uid_to_add = update.message.text.strip() if not uid_to_add.isdigit(): await update.message.reply_text("❌ INVALID USER ID!") return uid_to_add_int = int(uid_to_add) if not user_exists(uid_to_add_int): await update.message.reply_text("❌ USER NOT FOUND!") context.user_data["add_balance_mode"] = False return context.user_data["pending_add_user"] = uid_to_add_int await update.message.reply_text("💵 SEND AMOUNT TO ADD:") async def process_remove_balance_user(update, context): uid_to_remove = update.message.text.strip() if not uid_to_remove.isdigit(): await update.message.reply_text("❌ INVALID USER ID!") return uid_to_remove_int = int(uid_to_remove) if not user_exists(uid_to_remove_int): await update.message.reply_text("❌ USER NOT FOUND!") context.user_data["remove_balance_mode"] = False return context.user_data["pending_remove_user"] = uid_to_remove_int await update.message.reply_text("💸 SEND AMOUNT TO REMOVE:") async def process_add_balance_amount(update, context): try: amount = float(update.message.text.strip()) if amount <= 0: raise ValueError except: await update.message.reply_text("❌ INVALID AMOUNT!") return uid = context.user_data.get("pending_add_user") if not uid: context.user_data["add_balance_mode"] = False await update.message.reply_text("⚠️ SESSION EXPIRED.") return old_balance = get_user(uid).get("balance", 0) new_balance = await update_db_balance(uid, amount) await update.message.reply_text( f"✅ **ADD BALANCE SUCCESSFUL**\n🆔 USER: `{uid}`\n" f"💰 ADDED: `{format_balance(amount)} BDT`\n" f"📈 NEW BALANCE: `{format_balance(new_balance)} BDT`", parse_mode="Markdown" ) try: await context.bot.send_message(uid, f"🎉 ADMIN ADDED `{format_balance(amount)} BDT` TO YOUR ACCOUNT!\n💵 NEW BALANCE: `{format_balance(new_balance)} BDT`", parse_mode="Markdown") except: pass context.user_data["add_balance_mode"] = False context.user_data["pending_add_user"] = None async def process_remove_balance_amount(update, context): try: amount = float(update.message.text.strip()) if amount <= 0: raise ValueError except: await update.message.reply_text("❌ INVALID AMOUNT!") return uid = context.user_data.get("pending_remove_user") if not uid: context.user_data["remove_balance_mode"] = False await update.message.reply_text("⚠️ SESSION EXPIRED.") return old_balance = get_user(uid).get("balance", 0) if amount > old_balance: await update.message.reply_text(f"❌ INSUFFICIENT BALANCE! Current: {format_balance(old_balance)} BDT") context.user_data["remove_balance_mode"] = False context.user_data["pending_remove_user"] = None return new_balance = await update_db_balance(uid, -amount) await update.message.reply_text( f"✅ **REMOVE BALANCE SUCCESSFUL**\n🆔 USER: `{uid}`\n" f"💸 REMOVED: `{format_balance(amount)} BDT`\n" f"📉 NEW BALANCE: `{format_balance(new_balance)} BDT`", parse_mode="Markdown" ) try: await context.bot.send_message(uid, f"⚠️ ADMIN REMOVED `{format_balance(amount)} BDT` FROM YOUR ACCOUNT!\n💵 NEW BALANCE: `{format_balance(new_balance)} BDT`", parse_mode="Markdown") except: pass context.user_data["remove_balance_mode"] = False context.user_data["pending_remove_user"] = None # ==================== ADMIN PANEL - BAN/UNBAN ==================== async def admin_ban_user_start(update, context): context.user_data["admin_ban_mode"] = True context.user_data["admin_unban_mode"] = False await update.message.reply_text("🚫 SEND TELEGRAM ID TO BAN USER:") async def admin_unban_user_start(update, context): context.user_data["admin_unban_mode"] = True context.user_data["admin_ban_mode"] = False await update.message.reply_text("🔓 SEND TELEGRAM ID TO UNBAN USER:") async def process_ban_user(update, context): uid_to_ban = update.message.text.strip() if not uid_to_ban.isdigit(): await update.message.reply_text("❌ INVALID USER ID!") return uid_to_ban_int = int(uid_to_ban) if not user_exists(uid_to_ban_int): await update.message.reply_text("❌ USER NOT FOUND!") context.user_data["admin_ban_mode"] = False return if is_user_banned(uid_to_ban_int): await update.message.reply_text("⚠️ USER IS ALREADY BANNED!") context.user_data["admin_ban_mode"] = False return ban_user(uid_to_ban_int) try: await context.bot.send_message(uid_to_ban_int, "🚫 **YOU HAVE BEEN BANNED**\n📞 Contact support.", parse_mode="Markdown") except: pass await update.message.reply_text(f"✅ USER `{uid_to_ban}` BANNED!", parse_mode="Markdown", reply_markup=system_config_keyboard()) context.user_data["admin_ban_mode"] = False async def process_unban_user(update, context): uid_to_unban = update.message.text.strip() if not uid_to_unban.isdigit(): await update.message.reply_text("❌ INVALID USER ID!") return uid_to_unban_int = int(uid_to_unban) if not is_user_banned(uid_to_unban_int): await update.message.reply_text("⚠️ THIS USER IS NOT BANNED!") context.user_data["admin_unban_mode"] = False return unban_user(uid_to_unban_int) try: await context.bot.send_message(uid_to_unban_int, "✅ **YOU HAVE BEEN UNBANNED!** Use /start", parse_mode="Markdown") except: pass await update.message.reply_text(f"✅ USER `{uid_to_unban}` UNBANNED!", parse_mode="Markdown", reply_markup=system_config_keyboard()) context.user_data["admin_unban_mode"] = False async def show_banned_users_list(update, context): banned_list = load_banned_users() if not banned_list: await update.message.reply_text("📜 NO BANNED USERS.", reply_markup=system_config_keyboard()) return text = "📜 **BANNED USER LIST**\n━━━━━━━━━━━━━━━━━━━━\n\n" for i, uid in enumerate(banned_list, 1): text += f"{i}. `{uid}`\n" text += f"\n📊 Total: {len(banned_list)}" await update.message.reply_text(text, parse_mode="Markdown", reply_markup=system_config_keyboard()) # ==================== MESSAGE HANDLER SECTION ==================== async def handle_message(update: Update, context: ContextTypes.DEFAULT_TYPE): if not update.message: return uid = update.effective_user.id text = update.message.text.strip() # Withdraw flow if context.user_data.get("withdraw_mode") == "select_method": await withdraw_method_selected(update, context) return if context.user_data.get("withdraw_mode") == "amount": await withdraw_amount_received(update, context) return if context.user_data.get("withdraw_mode") == "number": await withdraw_number_received(update, context) return # Admin balance if context.user_data.get("add_balance_mode") and is_admin(uid): if context.user_data.get("pending_add_user"): await process_add_balance_amount(update, context) else: await process_add_balance_user(update, context) return if context.user_data.get("remove_balance_mode") and is_admin(uid): if context.user_data.get("pending_remove_user"): await process_remove_balance_amount(update, context) else: await process_remove_balance_user(update, context) return # Admin ban/unban if context.user_data.get("admin_ban_mode") and is_admin(uid): await process_ban_user(update, context) return if context.user_data.get("admin_unban_mode") and is_admin(uid): await process_unban_user(update, context) return # CUSTOM RANGE — user sent a range text if context.user_data.get("mode") == "custom_range": context.user_data["mode"] = None range_text = text.strip().upper() if not re.search(r'\d', range_text): await update.message.reply_text( "❌ INVALID RANGE!\n\n" "
সঠিক উদাহরণ: 234XXX
", parse_mode="HTML", reply_markup=main_keyboard(uid) ) return await request_queue.put({ 'type': 'process_numbers', 'update': update, 'context': context, 'range_text': range_text, 'count': 1 }) return # Ban check if not is_admin(uid) and is_user_banned(uid): await update.message.reply_text("🚫 YOU ARE BANNED 🚫", reply_markup=main_keyboard(uid)) return # Cancel if text == "❌ CANCEL": context.user_data.clear() await update.message.reply_text("❌ CANCELLED", reply_markup=main_keyboard(uid)) return # Main menu buttons if text == "👤 PROFILE": user_data = get_user(uid) stats = get_user_stats(uid) user = update.effective_user full_name = html.escape(user.full_name) username = html.escape(user.username or "No username") profile_text = ( f"👤 YOUR PROFILE\n\n" f"
🏷️ NAME: {full_name}
\n" f"
🆔 USERNAME: @{username}
\n" f"
🗝️ TELEGRAM ID: {uid}
\n\n" f"
💵 BALANCE: {format_balance(user_data.get('balance', 0))} BDT
\n\n" f"✨ TODAY\n" f"
📱 NUMBERS: {stats['today_numbers']}\n🔑 OTPS: {stats['today_otps']}
\n\n" f"🔥 LAST 7 DAYS\n" f"
📱 NUMBERS: {stats['last7d_numbers']}\n🔑 OTPS: {stats['last7d_otps']}
\n\n" f"🌐 ALL TIME\n" f"
📱 NUMBERS: {stats['total_numbers']}\n🔑 OTPS: {stats['total_otps']}
" ) await update.message.reply_text(profile_text, parse_mode="HTML") return if text == "💰 BALANCE": balance = get_user(uid)['balance'] await update.message.reply_text( f"💰 YOUR CURRENT BALANCE\n\n" f"
💵 TOTAL: {format_balance(balance)} BDT
", parse_mode="HTML", reply_markup=InlineKeyboardMarkup([[ InlineKeyboardButton("💸 WITHDRAW", callback_data="withdraw_start", style="primary") ]]) ) return if text == "REFER AND EARN": await refer_command(update, context) return # SEARCH OTP if text == "🔍 SEARCH OTP": context.user_data["mode"] = "search_otp" await update.message.reply_text("🔍 **ENTER THE NUMBER TO SEARCH OTP:**", parse_mode="Markdown") return if context.user_data.get("mode") == "search_otp": context.user_data["mode"] = None await request_queue.put({'type': 'search_otp', 'update': update, 'context': context, 'target_num': normalize_number(text)}) return # GET 2FA if text == "⚡ GET 2FA": await get_2fa_code(update, context) return # GET NUMBER if text == "📞 GET NUMBER": await show_app_selection(update, context) return if context.user_data.get("mode") == "get_2fa": await process_2fa_key(update, context) return # LEADERBOARD if text == "🏆 LEADERBOARD": await leaderboard_command(update, context) return # SUPPORT BUTTON HANDLER if text == "💬 SUPPORT": support_text = "💬 SUPPORT 🎧\n\nCLICK THE BUTTON BELOW TO CONTACT SUPPORT 📩" keyboard = InlineKeyboardMarkup([ [InlineKeyboardButton("💬 SUPPORT", url=SUPPORT_LINK, style="primary")], [InlineKeyboardButton("👨‍💻 DEVELOPER BY", url=DEVELOPER_LINK, style="danger")] ]) await update.message.reply_text(support_text, reply_markup=keyboard, parse_mode="Markdown") return # Admin panel if text == "⚙️ ADMIN PANEL ⚙️" and is_admin(uid): context.user_data["admin_mode"] = "main" await update.message.reply_text( "⌬━━━━━━━━━━━━━━━━━━━━⌬\n WELCOME ADMIN PANEL\n⌬━━━━━━━━━━━━━━━━━━━━⌬", reply_markup=admin_main_keyboard() ) return if text == "🔙 BACK TO MAIN" and context.user_data.get("admin_mode"): context.user_data["admin_mode"] = None await update.message.reply_text("🔙 Back to main menu.", reply_markup=main_keyboard(uid)) return if text == "🔙 BACK TO ADMIN": context.user_data["user_management_mode"] = None context.user_data["system_config_mode"] = None context.user_data["admin_mode"] = "main" await update.message.reply_text("🔙 Back to admin panel.", reply_markup=admin_main_keyboard()) return if text == "👥 USER MANAGEMENT" and context.user_data.get("admin_mode") == "main" and is_admin(uid): context.user_data["user_management_mode"] = "main" await update.message.reply_text("👥 User Management:", reply_markup=user_management_keyboard()) return if text == "⚙️ SYSTEM CONFIGURATION" and context.user_data.get("admin_mode") == "main" and is_admin(uid): context.user_data["system_config_mode"] = "main" await update.message.reply_text("⚙️ System Configuration:", reply_markup=system_config_keyboard()) return if text == "📈 TODAY ALL STATUS" and context.user_data.get("system_config_mode") == "main" and is_admin(uid): t_n, t_o, s_n, s_o, tot_n, tot_o = get_global_system_stats() msg = ( f"📊 SYSTEM STATUS\n━━━━━━━━━━━━━━━━━━━━━━\n\n" f"✨ TODAY\n📱 NUMBERS: {t_n}\n🔑 OTPS: {t_o}\n\n" f"🔥 LAST 7 DAYS\n📱 NUMBERS: {s_n}\n🔑 OTPS: {s_o}\n\n" f"🌐 ALL TIME\n📱 NUMBERS: {tot_n}\n🔑 OTPS: {tot_o}" ) await update.message.reply_text(msg, parse_mode="HTML") return if text == "👤 USER STATUS CHECK" and is_admin(uid): context.user_data["mode"] = "input_user_id" await update.message.reply_text("🔍 ENTER TELEGRAM ID:", reply_markup=cancel_keyboard()) return if context.user_data.get("mode") == "input_user_id" and is_admin(uid): target_uid = text.strip() if not target_uid.isdigit(): await update.message.reply_text("❌ INVALID ID!") return context.user_data["mode"] = None stats = get_user_stats(target_uid) msg = ( f"👤 USER STATUS{target_uid}\n━━━━━━━━━━━━━━━━━━━━━━\n\n" f"✨ TODAY: 📱 {stats['today_numbers']} | 🔑 {stats['today_otps']}\n" f"🔥 7 DAYS: 📱 {stats['last7d_numbers']} | 🔑 {stats['last7d_otps']}\n" f"🌐 ALL TIME: 📱 {stats['total_numbers']} | 🔑 {stats['total_otps']}" ) await update.message.reply_text( msg, parse_mode="HTML", reply_markup=InlineKeyboardMarkup([[ InlineKeyboardButton("📂 CHECK ALL DATA", callback_data=f"full_logs_{target_uid}", style="primary") ]]) ) return if text == "🆔 ALL USER ID" and context.user_data.get("user_management_mode") == "main" and is_admin(uid): users = get_all_users() if users: content = "\n".join(f"{i}. {u}" for i, u in enumerate(users, 1)) f = io.BytesIO(content.encode()); f.name = f"ALL_USERS_{len(users)}.txt" await update.message.reply_document(document=f, caption=f"👥 Total Users: {len(users)}", reply_markup=user_management_keyboard()) else: await update.message.reply_text("No users found.", reply_markup=user_management_keyboard()) return if text == "💰 ALL USER BALANCE" and context.user_data.get("user_management_mode") == "main" and is_admin(uid): user_db = load_data(USER_DATA_FILE) if user_db: total_bal = sum(v.get("balance", 0) for v in user_db.values()) lines = [f"{i}. {uid_}: {v.get('balance', 0):.2f} BDT" for i, (uid_, v) in enumerate(user_db.items(), 1)] content = f"💰 TOTAL BALANCE: {total_bal:.2f} BDT\n\n" + "\n".join(lines) f = io.BytesIO(content.encode()); f.name = f"BALANCES_{total_bal:.0f}.txt" await update.message.reply_document(document=f, caption=f"💵 Total Balance: {total_bal:.2f} BDT", reply_markup=user_management_keyboard()) else: await update.message.reply_text("No data.", reply_markup=user_management_keyboard()) return if text == "📜 BAN USER LIST" and is_admin(uid): await show_banned_users_list(update, context) return if text == "⛔ BAN USER" and context.user_data.get("system_config_mode") == "main" and is_admin(uid): await admin_ban_user_start(update, context) return if text == "🔓 UNBAN USER" and context.user_data.get("system_config_mode") == "main" and is_admin(uid): await admin_unban_user_start(update, context) return if text == "➕ ADD BALANCE" and context.user_data.get("system_config_mode") == "main" and is_admin(uid): await admin_add_balance_start(update, context) return if text == "➖ REMOVE BALANCE" and context.user_data.get("system_config_mode") == "main" and is_admin(uid): await admin_remove_balance_start(update, context) return # ==================== FIXED BROADCAST (TEXT, PHOTO, VIDEO, FILE, ETC.) ==================== if text == "📢 SEND MESSAGE TO ALL USERS" and is_admin(uid): context.user_data["broadcast_mode"] = True await update.message.reply_text( "📢 ADMIN BROADCAST SYSTEM (PRO)\n\n" "💬 আপনি এখন যা পাঠাবেন (Text, Photo, Video, Document, Voice, Audio, Animation, Sticker) – সকল ইউজারের কাছে প্রফেশনাল হেডারসহ চলে যাবে।\n\n" "✨ রেঞ্জ (যেমন: 237XXX) থাকলে তা অটোমেটিক ক্লিক-টু-কপি হয়ে যাবে।", parse_mode="HTML", reply_markup=cancel_keyboard() ) return if context.user_data.get("broadcast_mode") and is_admin(uid): context.user_data["broadcast_mode"] = False user_db = load_data(USER_DATA_FILE) all_uids = list(user_db.keys()) if not all_uids: await update.message.reply_text("❌ পাঠানোর জন্য কোনো ইউজার পাওয়া যায়নি!") return success_ids, fail_ids = [], [] status_msg = await update.message.reply_text(f"🚀 ব্রডকাস্ট শুরু হয়েছে...\n🎯 টার্গেট: {len(all_uids)} জন ইউজার।", parse_mode="HTML") def format_broadcast_caption(caption_text): if not caption_text: return "
📢 ADMIN NOTICE :
" formatted = re.sub(r'(\d{3,}[xX]{3,})', r'\1', str(caption_text)) return f"
📢 ADMIN NOTICE :
\n\n{formatted}" for user_id_str in all_uids: try: target_id = int(user_id_str) # টেক্সট মেসেজ if update.message.text: await context.bot.send_message( chat_id=target_id, text=format_broadcast_caption(update.message.text), parse_mode="HTML" ) # ফটো elif update.message.photo: caption = format_broadcast_caption(update.message.caption) if update.message.caption else None await context.bot.send_photo( chat_id=target_id, photo=update.message.photo[-1].file_id, caption=caption, parse_mode="HTML" if caption else None ) # ভিডিও elif update.message.video: caption = format_broadcast_caption(update.message.caption) if update.message.caption else None await context.bot.send_video( chat_id=target_id, video=update.message.video.file_id, caption=caption, parse_mode="HTML" if caption else None ) # ডকুমেন্ট (যেকোনো ফাইল) elif update.message.document: caption = format_broadcast_caption(update.message.caption) if update.message.caption else None await context.bot.send_document( chat_id=target_id, document=update.message.document.file_id, caption=caption, parse_mode="HTML" if caption else None ) # অডিও elif update.message.audio: caption = format_broadcast_caption(update.message.caption) if update.message.caption else None await context.bot.send_audio( chat_id=target_id, audio=update.message.audio.file_id, caption=caption, parse_mode="HTML" if caption else None ) # ভয়েস elif update.message.voice: caption = format_broadcast_caption(update.message.caption) if update.message.caption else None await context.bot.send_voice( chat_id=target_id, voice=update.message.voice.file_id, caption=caption, parse_mode="HTML" if caption else None ) # অ্যানিমেশন (GIF) elif update.message.animation: caption = format_broadcast_caption(update.message.caption) if update.message.caption else None await context.bot.send_animation( chat_id=target_id, animation=update.message.animation.file_id, caption=caption, parse_mode="HTML" if caption else None ) # স্টিকার elif update.message.sticker: await context.bot.send_sticker( chat_id=target_id, sticker=update.message.sticker.file_id ) # অন্য সব কিছু (ফরোয়ার্ড/কপি) else: try: await context.bot.copy_message( chat_id=target_id, from_chat_id=update.message.chat_id, message_id=update.message.message_id ) except: await context.bot.send_message( chat_id=target_id, text="📢 ADMIN NOTICE :\n\nআপনার জন্য একটি নতুন বার্তা আছে, কিন্তু এটি প্রদর্শন করা সম্ভব হয়নি।", parse_mode="HTML" ) success_ids.append(user_id_str) except Exception as e: print(f"Broadcast fail to {user_id_str}: {e}") fail_ids.append(user_id_str) await asyncio.sleep(0.05) report_text = ( f"✅ ADMIN NOTICE COMPLETE !\n\n" f"📊 BROADCAST REPORT:\n\n" f"
✅ SUCCESSFULLY SENT: {len(success_ids)} USERS !
\n" f"
❌ FAILED TO SEND: {len(fail_ids)} USERS !
" ) await status_msg.delete() await context.bot.send_message(chat_id=uid, text=report_text, parse_mode="HTML", reply_markup=main_keyboard(uid)) random_suffix = ''.join(random.choices(string.ascii_uppercase + string.digits, k=5)) if success_ids: s_file = io.BytesIO(("\n".join(success_ids)).encode()); s_file.name = f"SUCCESS_{random_suffix}.txt" await context.bot.send_document(chat_id=uid, document=s_file, caption="✅ Success User List") if fail_ids: f_file = io.BytesIO(("\n".join(fail_ids)).encode()); f_file.name = f"FAILED_{random_suffix}.txt" await context.bot.send_document(chat_id=uid, document=f_file, caption="❌ Failed User List") return await update.message.reply_text("🔹 PLEASE USE THE BUTTONS BELOW:", reply_markup=main_keyboard(uid)) # ==================== COMMAND HANDLERS SECTION ==================== async def get1number_command(update: Update, context: ContextTypes.DEFAULT_TYPE): uid = update.effective_user.id if is_user_banned(uid): await update.message.reply_text("🚫 YOU ARE BANNED 🚫", reply_markup=main_keyboard(uid)) return await show_app_selection(update, context) async def searchotp_command(update: Update, context: ContextTypes.DEFAULT_TYPE): uid = update.effective_user.id if is_user_banned(uid): await update.message.reply_text("🚫 YOU ARE BANNED 🚫", reply_markup=main_keyboard(uid)) return context.user_data["mode"] = "search_otp" await update.message.reply_text("🔍 **ENTER THE NUMBER TO SEARCH OTP:**", parse_mode="Markdown") async def balance_command(update: Update, context: ContextTypes.DEFAULT_TYPE): uid = update.effective_user.id if is_user_banned(uid): await update.message.reply_text("🚫 YOU ARE BANNED 🚫", reply_markup=main_keyboard(uid)) return balance = get_user(uid)['balance'] await update.message.reply_text(f"💰 BALANCE: `{format_balance(balance)} BDT`", parse_mode="Markdown", reply_markup=main_keyboard(uid)) async def profile_command(update: Update, context: ContextTypes.DEFAULT_TYPE): uid = update.effective_user.id if is_user_banned(uid): await update.message.reply_text("🚫 YOU ARE BANNED 🚫", reply_markup=main_keyboard(uid)) return user_data = get_user(uid) stats = get_user_stats(uid) user = update.effective_user profile_text = ( f"👤 **YOUR PROFILE**\n━━━━━━━━━━━━━━━━━━━━━━\n\n" f"🏷️ NAME: `{user.full_name}`\n" f"🆔 USERNAME: @{user.username or 'No username'}\n" f"🗝️ ID: `{uid}`\n\n" f"💵 BALANCE: {format_balance(user_data.get('balance', 0))} BDT\n\n" f"✨ TODAY: 📱 {stats['today_numbers']} | 🔑 {stats['today_otps']}\n" f"🔥 7 DAYS: 📱 {stats['last7d_numbers']} | 🔑 {stats['last7d_otps']}\n" f"🌐 ALL TIME: 📱 {stats['total_numbers']} | 🔑 {stats['total_otps']}" ) await update.message.reply_text(profile_text, parse_mode="Markdown") async def refer_command_slash(update: Update, context: ContextTypes.DEFAULT_TYPE): uid = update.effective_user.id if is_user_banned(uid): await update.message.reply_text("🚫 YOU ARE BANNED 🚫", reply_markup=main_keyboard(uid)) return await refer_command(update, context) async def leaderboard_command_slash(update: Update, context: ContextTypes.DEFAULT_TYPE): uid = update.effective_user.id if is_user_banned(uid): await update.message.reply_text("🚫 YOU ARE BANNED 🚫", reply_markup=main_keyboard(uid)) return await leaderboard_command(update, context) # ==================== START & CALLBACK SECTION ==================== async def start(update: Update, context: ContextTypes.DEFAULT_TYPE): uid = update.effective_user.id uid_str = str(uid) existing_data = load_data(USER_DATA_FILE) is_new_user = uid_str not in existing_data if is_new_user: get_user(uid) args = context.args if args: param = args[0] if is_range_request(param): await request_queue.put({'type': 'auto_number', 'update': update, 'context': context, 'range_text': param}) return elif is_referral_request(param) and is_new_user: try: referrer_id = int(param) if referrer_id != uid and str(referrer_id) in existing_data: current_count = get_referral_count(referrer_id) new_count = current_count + 1 update_referral_count(referrer_id, new_count) await update_db_balance(referrer_id, REFERRAL_PRICE) log_global_activity(referrer_id, "REFERRAL_JOINED", {"referred_user": uid}) try: await context.bot.send_message( referrer_id, f"🎉 NEW REFERRAL!\n\n
🗝️ ID: {uid}\n💰 REWARD: {format_balance(REFERRAL_PRICE)} BDT\n👥 TOTAL REFERS: {new_count}
", parse_mode="HTML" ) except: pass except Exception as e: print(f"Referral error: {e}") context.user_data.clear() await update.message.reply_text(WELCOME_MESSAGE, parse_mode="Markdown") await update.message.reply_text("🔹 PLEASE USE THE BUTTONS BELOW:", reply_markup=main_keyboard(uid)) async def button_callback(update: Update, context: ContextTypes.DEFAULT_TYPE): query = update.callback_query uid = query.from_user.id data = query.data await query.answer() if not is_admin(uid) and is_user_banned(uid): await query.edit_message_text("🚫 YOU ARE BANNED 🚫") return # LIVEACCESS — SERVICE SELECTION if data.startswith("svc_"): idx = int(data.replace("svc_", "")) services = context.user_data.get("la_services", []) if not services: services = get_cached_services() context.user_data["la_services"] = services if idx >= len(services): await query.answer("Service not found. Please try again.", show_alert=True) return svc = services[idx] sid = svc.get("sid", "Service") ranges = svc.get("ranges", []) if not ranges: await query.answer("No ranges available for this service right now.", show_alert=True) return context.user_data["la_svc_idx"] = idx context.user_data["la_sid"] = sid context.user_data["la_ranges"] = ranges keyboard = _build_countries_keyboard(ranges, idx) await query.message.edit_text( f"📞 GET NUMBER\n\n" f"
📱 Service: {html.escape(sid)}
\n" f"
🌍 আপনার পছন্দের Country সিলেক্ট করুন:
", parse_mode="HTML", reply_markup=keyboard ) return # LIVEACCESS — RANGE SELECTION if data.startswith("rng_"): idx = int(data.replace("rng_", "")) ranges = context.user_data.get("la_ranges", []) if idx >= len(ranges): await query.answer("Range not found. Please try again.", show_alert=True) return range_text = ranges[idx] sid = context.user_data.get("la_sid", "") asyncio.create_task(fast_allocate_number(query, context, range_text, sid)) return # CUSTOM RANGE — user types range manually if data == "custom_range": context.user_data["mode"] = "custom_range" await query.message.edit_text( "⚙️ CUSTOM RANGE\n\n" "
📶 আপনার কাস্টম range টাইপ করুন।\n" "উদাহরণ: 234XXX বা 225XXX
\n\n" "
⌨️ নিচে range লিখে Send করুন:
", parse_mode="HTML", reply_markup=InlineKeyboardMarkup([[ InlineKeyboardButton("◀️ BACK", callback_data="back_services", style="danger") ]]) ) return # LIVEACCESS — BACK TO SERVICES if data == "back_services": services = get_cached_services() or context.user_data.get("la_services", []) if not services: await query.message.edit_text("❌ Services লোড করা যায়নি। পরে চেষ্টা করুন।") return context.user_data["la_services"] = services keyboard = _build_services_keyboard(services) await query.message.edit_text( "📞 GET NUMBER\n\n" "
📱 নিচ থেকে একটি Service সিলেক্ট করুন:
", parse_mode="HTML", reply_markup=keyboard ) return # SAME RANGE if data == "same_range": r_text = last_range.get(uid) if r_text: try: await query.message.edit_reply_markup(reply_markup=InlineKeyboardMarkup([[ InlineKeyboardButton("📢 OTP GROUP", url="https://t.me/volt_x_lite_otp", style="primary") ]])) except: pass await process_numbers(update, context, r_text, 1) return # WITHDRAW if data == "withdraw_start": balance = get_user(uid)['balance'] if balance < MIN_WITHDRAW: await query.message.reply_text( f"
💵 BALANCE: {format_balance(balance)} BDT\n📉 MIN WITHDRAW: {MIN_WITHDRAW} BDT
", parse_mode="HTML" ) return context.user_data["withdraw_mode"] = "select_method" await query.message.reply_text("💳 SELECT YOUR PAYMENT METHOD!", reply_markup=withdraw_method_keyboard()) return if data == "withdraw_confirm": await process_withdraw_confirm(update, context) return if data == "withdraw_cancel": await process_withdraw_cancel(update, context) return if data.startswith("admin_approve_"): await admin_approve_withdraw(update, context, data.replace("admin_approve_", "")) return if data.startswith("admin_reject_"): await admin_reject_withdraw(update, context, data.replace("admin_reject_", "")) return # COPY / MISC CALLBACKS if data.startswith("copy_id_"): await query.answer(f"✅ Copied ID: {data.replace('copy_id_', '')}", show_alert=True) return if data.startswith("copy_text_"): await query.answer(f"✅ Copied: {data.replace('copy_text_', '')}", show_alert=True) return if data.startswith("my_ref_"): target_uid = data.replace("my_ref_", "") all_logs = load_data(ACTIVITY_LOGS_FILE) my_referrals = [log for log in all_logs if str(log.get('uid')) == str(target_uid) and log.get('action') == "REFERRAL_JOINED"] content = f"👥 REFERRAL REPORT — {target_uid}\n━━━━━━━━━━━━\nTOTAL: {len(my_referrals)}\n\n" for i, log in enumerate(my_referrals, 1): try: dt_obj = datetime.fromisoformat(log['timestamp']) ref_id = log.get('details', {}).get('referred_user', 'N/A') content += f"{i}. ID: {ref_id} | {dt_obj.strftime('%d/%m/%Y %I:%M %p')}\n" except: continue f = io.BytesIO(content.encode()) f.name = f"REF_{target_uid}.txt" await context.bot.send_document(chat_id=uid, document=f, caption="✅ **REFERRAL DATA**", parse_mode="Markdown") return if data.startswith("full_logs_"): target_uid = data.replace("full_logs_", "") stats = get_user_stats(target_uid) all_logs = load_data(ACTIVITY_LOGS_FILE) user_db = load_data(USER_DATA_FILE) user_info = user_db.get(str(target_uid), {}) user_otps = [log for log in all_logs if str(log.get('uid')) == str(target_uid) and log.get('action') == "OTP_RECEIVED"] content = ( f"📊 USER DATA REPORT — {target_uid}\n" f"💰 BALANCE: {user_info.get('balance', 0):.2f} BDT\n" f"━━━━━━━━━━━━━━━━━━\n" f"TODAY NUMBERS: {stats['today_numbers']}\n" f"TODAY OTPS: {stats['today_otps']}\n" f"7D NUMBERS: {stats['last7d_numbers']}\n" f"7D OTPS: {stats['last7d_otps']}\n" f"TOTAL NUMBERS: {stats['total_numbers']}\n" f"TOTAL OTPS: {stats['total_otps']}\n" f"━━━━━━━━━━━━━━━━━━\n\nOTP LOGS:\n" ) for i, log in enumerate(user_otps, 1): try: dt_obj = datetime.fromisoformat(log['timestamp']) d = log.get('details', {}) content += f"{i}. {dt_obj.strftime('%d/%m/%Y %I:%M %p')}\n 📞 {d.get('number', 'N/A')}\n 🔑 {d.get('otp', 'N/A')}\n\n" except: continue f = io.BytesIO(content.encode()) f.name = f"USER_{target_uid}.txt" await context.bot.send_document( chat_id=uid, document=f, caption=f"✅ DATA FOR USER: {target_uid}", parse_mode="HTML" ) return # ==================== MAIN & POST INIT SECTION ==================== async def post_init(application): for _ in range(20): asyncio.create_task(worker()) asyncio.create_task(monitor_loop(application)) asyncio.create_task(liveaccess_refresh_loop()) def main(): app = ApplicationBuilder().token(BOT_TOKEN).concurrent_updates(True).post_init(post_init).build() app.add_handler(CommandHandler("start", start)) app.add_handler(CommandHandler("get1number", get1number_command)) app.add_handler(CommandHandler("searchotp", searchotp_command)) app.add_handler(CommandHandler("balance", balance_command)) app.add_handler(CommandHandler("profile", profile_command)) app.add_handler(CommandHandler("refer", refer_command_slash)) app.add_handler(CommandHandler("leaderboard", leaderboard_command_slash)) app.add_handler(CallbackQueryHandler(button_callback)) app.add_handler(MessageHandler(filters.TEXT & (~filters.COMMAND), handle_message)) print("🚀 BOT RUNNING...") app.run_polling(allowed_updates=Update.ALL_TYPES, drop_pending_updates=True) if __name__ == "__main__": main()