import httpx from bs4 import BeautifulSoup import re from datetime import datetime, timedelta, timezone import time from flask import Flask, Response, request, redirect from threading import Thread from collections import deque import json from queue import Queue import os import uuid import sys from pymongo import MongoClient from pymongo.errors import ConnectionFailure MONGODB_URI = os.environ.get("MONGODB_URI") DB_NAME = "otp_bot" COLLECTION_NAME = "accounts" if MONGODB_URI: try: mongo_client = MongoClient(MONGODB_URI) db = mongo_client[DB_NAME] accounts_collection = db[COLLECTION_NAME] mongo_client.admin.command('ping') print("โœ… MongoDB Connected!") # Cek isi database count = accounts_collection.count_documents({}) print(f"๐Ÿ“Š Total dokumen di MongoDB: {count}") except ConnectionFailure as e: print(f"โŒ MongoDB Connection Failed: {e}") mongo_client = None else: print("โš ๏ธ MONGODB_URI tidak di set, menggunakan penyimpanan lokal") mongo_client = None print = lambda *args, **kwargs: __builtins__.print(*args, **kwargs, flush=True) BASE = "http://159.69.3.189" LOGIN_URL = f"{BASE}/login" GET_RANGE_URL = f"{BASE}/portal/sms/received/getsms" GET_NUMBER_URL = f"{BASE}/portal/sms/received/getsms/number" GET_SMS_URL = f"{BASE}/portal/sms/received/getsms/number/sms" TELEGRAM_PROXY_URL = "https://danihitambangetjir.termai.cc/api/proxy" CUSTOM_DOMAIN = "https://fourstore-otp.hf.space" ACCOUNTS_FILE = "accounts.json" def mask_email(email): if not email or '@' not in email: return email parts = email.split('@') username = parts[0] domain = parts[1] if len(username) <= 3: masked_username = username[0] + '*' * (len(username) - 1) else: masked_username = username[:2] + '*' * (len(username) - 3) + username[-1] return f"{masked_username}@{domain}" def load_accounts_from_mongodb(): accounts_dict = {} try: if mongo_client: print("๐Ÿ“ฅ Loading accounts from MongoDB...") cursor = accounts_collection.find({}) count = 0 for doc in cursor: count += 1 acc_id = doc.pop("_id") doc["session"] = None doc["csrf"] = None doc["status"] = False doc["otp_logs"] = doc.get("otp_logs", []) doc["sent_cache"] = [] doc["sms_cache"] = {} doc["sms_counter"] = {} doc["range_counter"] = {} doc["last_cleanup"] = time.time() accounts_dict[acc_id] = doc print(f"๐Ÿ“Š Loaded {len(accounts_dict)} accounts from MongoDB") if count > 0: print(f"โœ… Data MongoDB berhasil dimuat: {count} akun") # Tampilkan sample for acc_id, acc in list(accounts_dict.items())[:2]: email_masked = mask_email(acc.get('username', 'Unknown')) log_count = len(acc.get('otp_logs', [])) print(f" - Akun: {email_masked} | OTP Logs: {log_count}") else: print("โš ๏ธ Tidak ada data di MongoDB") except Exception as e: print(f"โŒ Error load from MongoDB: {e}") return accounts_dict def save_accounts_to_mongodb(accounts_dict): try: if mongo_client: print("๐Ÿ’พ Saving accounts to MongoDB...") saved_count = 0 for acc_id, acc in accounts_dict.items(): acc_copy = acc.copy() acc_copy.pop("session", None) acc_copy.pop("csrf", None) acc_copy.pop("sms_cache", None) acc_copy.pop("sms_counter", None) acc_copy.pop("range_counter", None) acc_copy.pop("last_cleanup", None) # Batasi logs if "otp_logs" in acc_copy and len(acc_copy["otp_logs"]) > 100: acc_copy["otp_logs"] = acc_copy["otp_logs"][:100] result = accounts_collection.update_one( {"_id": acc_id}, {"$set": acc_copy}, upsert=True ) if result.upserted_id or result.modified_count > 0: saved_count += 1 print(f"๐Ÿ’พ Saved {saved_count} accounts to MongoDB") except Exception as e: print(f"โŒ Error save to MongoDB: {e}") def load_accounts_from_file(): if os.path.exists(ACCOUNTS_FILE): try: with open(ACCOUNTS_FILE, 'r') as f: data = json.load(f) for acc_id in data: data[acc_id]["session"] = None data[acc_id]["csrf"] = None data[acc_id]["status"] = False if "otp_logs" not in data[acc_id]: data[acc_id]["otp_logs"] = [] data[acc_id]["sent_cache"] = [] data[acc_id]["sms_cache"] = {} data[acc_id]["sms_counter"] = {} data[acc_id]["range_counter"] = {} data[acc_id]["last_cleanup"] = time.time() print(f"๐Ÿ“Š Loaded {len(data)} accounts from file") return data except: return {} return {} def save_accounts_to_file(accounts_dict): try: accounts_to_save = {} for acc_id, acc in accounts_dict.items(): acc_copy = acc.copy() acc_copy.pop("session", None) acc_copy.pop("csrf", None) if "otp_logs" in acc_copy and len(acc_copy["otp_logs"]) > 100: acc_copy["otp_logs"] = acc_copy["otp_logs"][:100] acc_copy.pop("sms_cache", None) acc_copy.pop("sms_counter", None) acc_copy.pop("range_counter", None) acc_copy.pop("last_cleanup", None) accounts_to_save[acc_id] = acc_copy with open(ACCOUNTS_FILE, 'w') as f: json.dump(accounts_to_save, f, indent=2) print(f"๐Ÿ’พ Accounts saved to file") except Exception as e: print(f"โŒ Error save to file: {e}") # Load accounts if mongo_client: accounts = load_accounts_from_mongodb() else: accounts = load_accounts_from_file() app = Flask('') app.secret_key = "fourstore-multi-account-secret" sse_clients = [] global_otp_logs = [] def get_utc_time(): return datetime.now(timezone.utc) def get_wib_time(): return datetime.now(timezone.utc) + timedelta(hours=7) def get_wib_time_str(): return get_wib_time().strftime("%H:%M:%S") def get_search_date(): now_utc = datetime.now(timezone.utc) return now_utc.strftime("%Y-%m-%d") def login_account(account_id, username, password, bot_token, chat_id): try: masked = mask_email(username) print(f"\n{'='*60}") print(f"๐Ÿ” PROSES LOGIN UNTUK: {masked} (ID: {account_id})") print(f"{'='*60}") session = httpx.Client(follow_redirects=True, timeout=30.0) r = session.get(LOGIN_URL, timeout=30) if r.status_code != 200: return False, f"HTTP {r.status_code}" soup = BeautifulSoup(r.text, "html.parser") token = soup.find("input", {"name": "_token"}) if not token: return False, "Token tidak ditemukan" csrf_token = token.get("value") r = session.post(LOGIN_URL, data={ "_token": csrf_token, "email": username, "password": password }, timeout=30) if "dashboard" in r.text.lower() or "logout" in r.text.lower(): accounts[account_id]["session"] = session accounts[account_id]["csrf"] = csrf_token accounts[account_id]["status"] = True accounts[account_id]["username"] = username accounts[account_id]["password"] = password accounts[account_id]["bot_token"] = bot_token accounts[account_id]["chat_id"] = chat_id accounts[account_id]["last_login"] = time.time() if mongo_client: save_accounts_to_mongodb(accounts) else: save_accounts_to_file(accounts) return True, "Login berhasil" else: return False, "Login gagal" except Exception as e: return False, str(e) def tg_send(account_id, msg): try: account = accounts.get(account_id) if not account or not account.get("bot_token") or not account.get("chat_id"): return False url = f"{TELEGRAM_PROXY_URL}?url=https://api.telegram.org/bot{account['bot_token']}/sendMessage" payload = { "chat_id": account['chat_id'], "text": msg, "parse_mode": "Markdown" } httpx.post(url, json=payload, timeout=30) print(f"โœ… OTP terkirim ke chat {account['chat_id']}") return True except Exception as e: print(f"โŒ Gagal kirim Telegram: {e}") return False def clean_country(rng): return re.sub(r"\s*\d+$", "", rng).strip() if rng else "UNKNOWN" def mask_number(number): if not number: return "UNKNOWN" clean = re.sub(r"[^\d+]", "", number) if len(clean) <= 6: return clean return f"{clean[:4]}****{clean[-3:]}" def map_service(raw): if not raw: return "UNKNOWN" s = raw.lower().strip() if 'whatsapp' in s: return "WHATSAPP" if 'telegram' in s: return "TELEGRAM" if 'google' in s or 'gmail' in s: return "GOOGLE" if 'facebook' in s or 'fb' in s: return "FACEBOOK" if 'instagram' in s or 'ig' in s: return "INSTAGRAM" if 'tiktok' in s: return "TIKTOK" if 'temu' in s: return "TEMU" if 'shopee' in s: return "SHOPEE" if 'tokopedia' in s: return "TOKOPEDIA" if 'grab' in s: return "GRAB" if 'gojek' in s or 'go-jek' in s: return "GOJEK" return raw.upper() def extract_otp(text): if not text: return None m = re.search(r"\b(\d{6})\b", text) if m: return m.group(0) m = re.search(r"\b(\d{4,5})\b", text) if m: return m.group(0) m = re.search(r"\b(\d{3}[- ]?\d{3})\b", text) if m: return m.group(0).replace("-", "").replace(" ", "") digits = re.findall(r'\d+', text) for d in digits: if 4 <= len(d) <= 6: return d return None def generate_otp_id(account_id, rng, number, otp, service): return f"{account_id}-{rng}-{number}-{otp}-{service}" def get_ranges_with_count(account_id): account = accounts.get(account_id) if not account or not account.get("session") or not account.get("csrf"): return [] try: date = get_search_date() r = account["session"].post(GET_RANGE_URL, data={ "_token": account["csrf"], "from": date, "to": date }, timeout=15) soup = BeautifulSoup(r.text, "html.parser") ranges_data = [] for item in soup.select(".item"): name_div = item.select_one(".col-sm-4") if not name_div: continue rng = name_div.get_text(strip=True) count_p = item.select_one(".col-3 .mb-0.pb-0") count = int(count_p.get_text(strip=True)) if count_p else 0 ranges_data.append({ "name": rng, "count": count }) return ranges_data except Exception as e: print(f"Error get_ranges: {e}") return [] def get_numbers_with_count(account_id, rng): account = accounts.get(account_id) if not account or not account.get("session") or not account.get("csrf"): return [] try: date = get_search_date() r = account["session"].post(GET_NUMBER_URL, data={ "_token": account["csrf"], "start": date, "end": date, "range": rng }, timeout=15) soup = BeautifulSoup(r.text, "html.parser") numbers_data = [] for div in soup.find_all("div", onclick=True): onclick = div.get("onclick", "") match = re.search(r"getDetialsNumber\w*\('?(\d+)'?", onclick) if not match: match = re.search(r"open_(\d+)", onclick) if not match: match = re.search(r"'(\d+)'", onclick) if match: num = match.group(1) if num and len(num) > 5: parent = div.find_parent("div", class_="card") count = 0 if parent: p_tag = parent.find("p", class_="mb-0 pb-0") if p_tag: try: count = int(p_tag.get_text(strip=True)) except: count = 0 numbers_data.append({ "number": num, "count": count }) if len(numbers_data) == 0: for div in soup.find_all("div", class_="col-sm-4"): text = div.get_text(strip=True) match = re.search(r'\b(\d{10,15})\b', text) if match: num = match.group(1) parent = div.find_parent("div", class_="card") count = 0 if parent: p_tag = parent.find("p", class_="mb-0 pb-0") if p_tag: try: count = int(p_tag.get_text(strip=True)) except: count = 0 numbers_data.append({ "number": num, "count": count }) return numbers_data except Exception as e: print(f"Error get_numbers: {e}") return [] def get_sms_fast(account_id, rng, number): account = accounts.get(account_id) if not account or not account.get("session") or not account.get("csrf"): return [] try: date = get_search_date() cache_key = f"{rng}-{number}" if cache_key in account["sms_cache"]: timestamp, results = account["sms_cache"][cache_key] if time.time() - timestamp < 5: return results r = account["session"].post(GET_SMS_URL, data={ "_token": account["csrf"], "start": date, "end": date, "Number": number, "Range": rng }, timeout=20) soup = BeautifulSoup(r.text, "html.parser") results = [] for card in soup.select("div.card.card-body"): try: service = "UNKNOWN" service_div = card.select_one("div.col-sm-4") if service_div: raw = service_div.get_text(strip=True) service = map_service(raw) msg_p = card.find("p", class_="mb-0 pb-0") if msg_p: sms = msg_p.get_text(strip=True) otp = extract_otp(sms) if otp: results.append((service, sms, otp)) except: continue account["sms_cache"][cache_key] = (time.time(), results) return results except Exception as e: print(f"Error get_sms: {e}") return [] def is_otp_sent(account_id, otp_id): account = accounts.get(account_id) if not account: return True return otp_id in account.get("sent_cache", []) def mark_otp_sent(account_id, otp_id): account = accounts.get(account_id) if not account: return if "sent_cache" not in account: account["sent_cache"] = [] account["sent_cache"].append(otp_id) if len(account["sent_cache"]) > 1000: account["sent_cache"] = account["sent_cache"][-1000:] def add_otp_log(account_id, country, number, service, otp, sms, otp_id): account = accounts.get(account_id) if not account: return wib = get_wib_time() wib_str = wib.strftime("%Y-%m-%d %H:%M:%S WIB") time_only = wib.strftime("%H:%M:%S") log_entry = { "time": time_only, "time_full": wib_str, "timestamp": time.time(), "country": country, "number": number, "service": service, "otp": otp, "sms": sms[:150] + "..." if len(sms) > 150 else sms, "account_id": account_id, "account_username": mask_email(account.get("username", "Unknown")), "otp_id": otp_id } if "otp_logs" not in account: account["otp_logs"] = [] account["otp_logs"].insert(0, log_entry) if len(account["otp_logs"]) > 100: account["otp_logs"] = account["otp_logs"][:100] global_otp_logs.insert(0, log_entry) if len(global_otp_logs) > 500: global_otp_logs[:] = global_otp_logs[:500] broadcast_sse(log_entry) if mongo_client: save_accounts_to_mongodb(accounts) else: save_accounts_to_file(accounts) print(f"โœ… Log ditambahkan: {service} - {otp} - {time_only}") return log_entry def broadcast_sse(data): msg = f"data: {json.dumps(data)}\n\n" dead = [] for q in sse_clients: try: q.put(msg) except: dead.append(q) for q in dead: sse_clients.remove(q) @app.route('/') def home(): all_logs = global_otp_logs.copy() search_query = request.args.get('q', '').lower() filter_service = request.args.get('service', '') if search_query: all_logs = [log for log in all_logs if search_query in log.get('country', '').lower() or search_query in log.get('number', '').lower() or search_query in log.get('otp', '').lower() or search_query in log.get('sms', '').lower() or search_query in log.get('account_username', '').lower()] if filter_service: all_logs = [log for log in all_logs if log.get('service') == filter_service] all_services = list(set([log.get('service') for log in all_logs if log.get('service')])) total_otp = len(global_otp_logs) today_otp = len([l for l in global_otp_logs if l.get('time_full', '').startswith(get_wib_time().strftime("%Y-%m-%d"))]) service_stats = {} for log in global_otp_logs[:50]: service = log.get('service', 'UNKNOWN') service_stats[service] = service_stats.get(service, 0) + 1 html = f""" OTP MULTI ACCOUNT ยท FOURSTORE

๐Ÿ“ฑ OTP MULTI ACCOUNT ยท FOURSTORE

{CUSTOM_DOMAIN} ยท {get_wib_time().strftime('%d %B %Y')}

โ— ONLINE ยท 24 JAM
Total Akun
{len(accounts)}
Akun Online
{sum(1 for a in accounts.values() if a.get('status'))}
Total OTP
{total_otp}
WIB
{get_wib_time().strftime('%H:%M:%S')}

โž• Tambah Akun Baru

โœ• Reset ๐Ÿ“Š {len(all_logs)} hasil
{''.join([f'{service}: {count}' for service, count in sorted(service_stats.items())][:8])}

๐Ÿ“จ OTP TERBARU LIVE ยท 24 JAM

{generate_otp_rows(all_logs, search_query)}
WIB Akun Country Number Service OTP Message
""" return html def generate_otp_rows(logs, search_query): if not logs: return '๐Ÿ“ญ Belum ada OTP ยท Menunggu OTP masuk...' rows = "" for log in logs[:50]: country = log.get('country', '') number = log.get('number', '') otp = log.get('otp', '') sms = log.get('sms', '') service = log.get('service', 'UNKNOWN') account = log.get('account_username', 'Unknown') time_full = log.get('time_full', '') if search_query: country = re.sub(f'({re.escape(search_query)})', r'\1', country, flags=re.I) number = re.sub(f'({re.escape(search_query)})', r'\1', number, flags=re.I) otp = re.sub(f'({re.escape(search_query)})', r'\1', otp, flags=re.I) service_class = service.lower().replace(' ', '') time_tooltip = f' title="{time_full}"' rows += f''' {log.get('time', '')} {account} {country} {number} {service} {otp}
{log.get('sms', '')}
''' return rows @app.route('/add_account', methods=['POST']) def add_account_route(): account_id = str(uuid.uuid4())[:8] username = request.form['username'] password = request.form['password'] bot_token = request.form.get('bot_token', '') chat_id = request.form.get('chat_id', '') masked = mask_email(username) print(f"\nโž• TAMBAH AKUN BARU: {masked} (ID: {account_id})") accounts[account_id] = { "id": account_id, "username": username, "password": password, "bot_token": bot_token, "chat_id": chat_id, "session": None, "csrf": None, "status": False, "otp_logs": [], "sent_cache": [], "sms_cache": {}, "sms_counter": {}, "range_counter": {}, "last_cleanup": time.time(), "created_at": time.time() } if mongo_client: save_accounts_to_mongodb(accounts) else: save_accounts_to_file(accounts) print(f"โœ… Akun ditambahkan: {masked}") print(f"๐Ÿ”„ Mencoba login otomatis untuk {masked}...") success, msg = login_account( account_id, username, password, bot_token, chat_id ) if success: print(f"โœ…โœ…โœ… LOGIN OTOMATIS BERHASIL! Memulai thread scraper...") thread = Thread(target=run_account_scraper, args=(account_id,), daemon=True) thread.start() print(f"โœ… Thread scraper dimulai untuk {masked}") else: print(f"โŒโŒโŒ LOGIN OTOMATIS GAGAL: {msg}") return redirect('/') @app.route('/stream') def stream(): def generate(): q = Queue() sse_clients.append(q) try: while True: yield q.get() except: if q in sse_clients: sse_clients.remove(q) return Response(generate(), mimetype="text/event-stream") @app.route('/api/logs') def api_logs(): return json.dumps(global_otp_logs[:100]) @app.route('/api/stats') def api_stats(): return json.dumps({ "total_accounts": len(accounts), "online_accounts": sum(1 for a in accounts.values() if a.get('status')), "total_otp": len(global_otp_logs), "mongo_connected": mongo_client is not None, "mongo_docs": accounts_collection.count_documents({}) if mongo_client else 0 }) def run_account_scraper(account_id): account = accounts.get(account_id) if not account: return username = account['username'] masked = mask_email(username) print(f"\n๐Ÿš€๐Ÿš€๐Ÿš€ STARTING SCRAPER FOR: {masked} ๐Ÿš€๐Ÿš€๐Ÿš€") loop_count = 0 while account.get("status"): loop_count += 1 try: print(f"\n{'='*60}") print(f"๐Ÿ”„ [{masked}] LOOP #{loop_count} - {get_wib_time_str()}") print(f"{'='*60}") if time.time() - account.get("last_cleanup", 0) > 300: account["sms_cache"] = {} account["sms_counter"] = {} account["range_counter"] = {} account["last_cleanup"] = time.time() print(f"๐Ÿงน [{masked}] Cache cleared") ranges_data = get_ranges_with_count(account_id) print(f"๐Ÿ“Š [{masked}] Total ranges: {len(ranges_data)}") for range_item in ranges_data: if not account.get("status"): break rng = range_item["name"] current_count = range_item["count"] prev_count = account["range_counter"].get(rng, 0) if current_count > prev_count: country = clean_country(rng) print(f"\n๐Ÿ”ฅ RANGE BERUBAH: {country} ({masked})") print(f" ๐Ÿ“Š {prev_count} โ†’ {current_count} SMS") account["range_counter"][rng] = current_count numbers_data = get_numbers_with_count(account_id, rng) print(f" ๐Ÿ“ž Total nomor: {len(numbers_data)}") for number_item in numbers_data: if not account.get("status"): break num = number_item["number"] num_count = number_item["count"] key = f"{rng}-{num}" prev_num_count = account["sms_counter"].get(key, 0) if num_count > prev_num_count: print(f" ๐Ÿ“ฑ Nomor: {mask_number(num)}") print(f" ๐Ÿ“จ {prev_num_count} โ†’ {num_count} SMS") all_sms = get_sms_fast(account_id, rng, num) print(f" ๐Ÿ“จ Total SMS ditemukan: {len(all_sms)}") for i in range(prev_num_count, len(all_sms)): service, sms, otp = all_sms[i] if otp: otp_id = generate_otp_id(account_id, rng, num, otp, service) if not is_otp_sent(account_id, otp_id): masked_num = mask_number(num) msg = f"๐Ÿ”” *NEW OTP*\n๐ŸŒ {country}\n๐Ÿ“ž `{masked_num}`\n๐Ÿ’ฌ {service}\n๐Ÿ” `{otp}`\n\n{sms[:300]}" print(f" ๐Ÿ“ค Mengirim OTP {otp} ke Telegram...") if tg_send(account_id, msg): mark_otp_sent(account_id, otp_id) add_otp_log(account_id, country, masked_num, service, otp, sms, otp_id) print(f" โœ… OTP: {otp} - {service} TERKIRIM! (ID: {otp_id})") else: print(f" โŒ Gagal mengirim OTP {otp}") else: print(f" โญ๏ธ OTP {otp} sudah pernah dikirim sebelumnya (ID: {otp_id})") account["sms_counter"][key] = num_count time.sleep(0.5) else: print(f" โญ๏ธ Range {clean_country(rng)} tidak berubah (count: {current_count})") print(f"\nโณ [{masked}] Tidur 2 detik...") time.sleep(2) except Exception as e: print(f"โŒ ERROR in scraper for {masked}: {str(e)}") time.sleep(5) print(f"\n๐Ÿ›‘๐Ÿ›‘๐Ÿ›‘ SCRAPER STOPPED FOR: {masked} ๐Ÿ›‘๐Ÿ›‘๐Ÿ›‘") def run_server(): app.run(host='0.0.0.0', port=7860, debug=False, threaded=True) Thread(target=run_server, daemon=True).start() def main(): print("\n" + "="*70) print(" ๐Ÿ”ฅ OTP MULTI ACCOUNT - FOURSTORE ๐Ÿ”ฅ") print("="*70) # CEK MONGODB if mongo_client: print(" โœ… MongoDB: TERHUBUNG") try: count = accounts_collection.count_documents({}) print(f" ๐Ÿ“Š Data di MongoDB: {count} akun") # Coba baca sample sample = accounts_collection.find_one() if sample: print(f" ๐Ÿ“ Sample akun: {mask_email(sample.get('username', 'Unknown'))}") log_count = len(sample.get('otp_logs', [])) print(f" ๐Ÿ“ OTP logs: {log_count}") except Exception as e: print(f" โŒ Error cek MongoDB: {e}") else: print(" โš ๏ธ MongoDB: TIDAK TERHUBUNG (pakai file)") print(f" โšก PORT: 7860") print(f" ๐ŸŒ DOMAIN: {CUSTOM_DOMAIN}") print(" ๐Ÿ“‹ LOGGING: FULL DETAIL") print(" ๐Ÿ”’ EMAIL SENSOR: AKTIF") print(" ๐Ÿค– AUTO LOGIN: AKTIF") print(" ๐Ÿ“ฑ TELEGRAM: HANYA KIRIM OTP") print(" ๐Ÿ›ก๏ธ ANTI DUPLIKAT: AKTIF") print(" ๐ŸŒ SERVER UTC: AKTIF") print(" โฐ 24 JAM MONITORING: AKTIF") print("="*70 + "\n") # Muat ulang log dari akun yang ada print("๐Ÿ“ฅ Memuat ulang logs dari akun tersimpan...") for acc_id, acc in accounts.items(): if "otp_logs" in acc and acc["otp_logs"]: for log in acc["otp_logs"]: global_otp_logs.append(log) global_otp_logs.sort(key=lambda x: x.get('timestamp', 0), reverse=True) print(f"๐Ÿ“Š Total logs dimuat: {len(global_otp_logs)}") # Auto login untuk akun yang sudah login sebelumnya for acc_id, acc in accounts.items(): if acc.get("status"): masked = mask_email(acc['username']) print(f"๐Ÿ”„ Auto-login untuk {masked}...") success, msg = login_account( acc_id, acc['username'], acc['password'], acc.get('bot_token', ''), acc.get('chat_id', '') ) if success: thread = Thread(target=run_account_scraper, args=(acc_id,), daemon=True) thread.start() print(f"โœ… {masked} online") else: print(f"โŒ {masked} offline: {msg}") acc["status"] = False if mongo_client: save_accounts_to_mongodb(accounts) else: save_accounts_to_file(accounts) print("\n" + "="*70) print("โœ… BOT SIAP! Dashboard: https://fourstore-otp.hf.space") print("๐ŸŒ SERVER MENGGUNAKAN UTC - SEARCH DATE: UTC HARI INI") print("โฐ 24 JAM AKTIF - SEMUA OTP AKAN MUNCUL DI WEB") print("="*70 + "\n") while True: time.sleep(300) if mongo_client: save_accounts_to_mongodb(accounts) # Cek status MongoDB try: count = accounts_collection.count_documents({}) print(f"๐Ÿ’พ Auto-save: {count} akun tersimpan - {get_wib_time_str()}") except: print(f"๐Ÿ’พ Auto-save - {get_wib_time_str()}") else: save_accounts_to_file(accounts) print(f"๐Ÿ’พ Auto-save data - {get_wib_time_str()}") if __name__ == "__main__": try: main() except KeyboardInterrupt: print("\n๐Ÿ›‘ BOT STOPPED") if mongo_client: save_accounts_to_mongodb(accounts) else: save_accounts_to_file(accounts)