import os import asyncio import random from collections import deque from aiohttp import web # 1. PRE-IMPORT CONFIGURATION: Inject environment variables first from dotenv import load_dotenv load_dotenv() # ⚡ Import 'idle' correctly to keep the bot listening from pyrogram import idle # Strict Environment Validation Framework REQUIRED_ENV_VARS = ["SESSION_GIST_URL", "BOT_TOKEN", "ADMIN_IDS", "CHECK_FILE_URL", "SNIPE_FILE_URL", "MONGO_URI", "API_ID", "API_HASH"] for var in REQUIRED_ENV_VARS: if not os.environ.get(var): raise ValueError(f"🚨 CRITICAL BOOT FAILURE: Missing Environment Variable: {var}") # 2. MODULE IMPORTS from utils.logger import log from utils.loader import fetch_txt_file, fetch_json_file from bot.database import db from bot.state import state from bot.client import bot, init_clients from bot.scheduler import scheduler, shutdown_campers from bot.scraper import scraper async def handle_ping(request): """OPSEC Decoy server to satisfy PaaS port requirements.""" return web.json_response({"error": "Forbidden", "status": 403}, status=403) async def keep_alive_server(): app = web.Application() app.router.add_get('/', handle_ping) runner = web.AppRunner(app) await runner.setup() site = web.TCPSite(runner, '0.0.0.0', 7860) await site.start() log("🛡️ Enterprise Keep-alive server started on port 7860.") async def safe_boot_sequence(): """⚡ ENTERPRISE STABILIZATION: Fault-Tolerant Boot Sequence""" log("🔄 Initiating Enterprise Boot Sequence...") try: # Initialize DB and Settings await db.init_db() await db.load_settings(state) # ⚡ Load Admins from Environment and Database if "admin_ids" not in state: state["admin_ids"] = set() env_admins = os.environ.get("ADMIN_IDS", "") if env_admins: for x in env_admins.split(","): if x.strip().lstrip('-').isdigit(): state["admin_ids"].add(int(x.strip())) db_admins = await db.get_admins() for adm in db_admins: state["admin_ids"].add(adm) log(f"🔑 Authorization loaded for {len(state['admin_ids'])} admins.") # Boot up the Scraper's HTTP pool await scraper.init_session() # Load Target Queues temp_c = list(set(await fetch_txt_file(os.environ.get("CHECK_FILE_URL")))) temp_s = list(set(await fetch_txt_file(os.environ.get("SNIPE_FILE_URL")))) state["unavailable_set"] = await db.get_unavailable_set() custom_snipes = await db.get_custom_snipes() for u in custom_snipes: if u not in temp_s: temp_s.append(u) # ⚡ Restore Watchdog and Fragment Lists from MongoDB if "watch_submitters" not in state: state["watch_submitters"] = {} if "watchdog_queue" not in state: state["watchdog_queue"] = set() if "fragcheck_queue" not in state: state["fragcheck_queue"] = set() watchdog_data = await db.get_watchdog() for u, user_id in watchdog_data.items(): state["watchdog_queue"].add(u) state["watch_submitters"][u] = user_id frag_data = await db.get_fragcheck() for u in frag_data: state["fragcheck_queue"].add(u) log(f"📥 Restored {len(state['watchdog_queue'])} Watchdog targets & {len(state['fragcheck_queue'])} Fragment targets from Database.") if state["db_filter_on"]: before = len(temp_c) temp_c = [u for u in temp_c if u not in state["unavailable_set"]] state["skipped_by_filter"] = before - len(temp_c) else: state["skipped_by_filter"] = 0 random.shuffle(temp_c) random.shuffle(temp_s) accounts_data = await fetch_json_file(os.environ.get("SESSION_GIST_URL")) if not accounts_data or "accounts" not in accounts_data: raise ValueError("Failed to fetch Session Gist. Boot aborted.") state["check_queue"] = deque(temp_c) state["snipe_queue"] = deque(temp_s) # Initialize UI Bot await bot.start() # Alias handlers to link your commands and buttons to the active bot import bot.handlers as ui_handlers # Initialize Snipe Fleet await init_clients(accounts_data["accounts"]) log(f"✅ Boot Successful: Loaded {len(state['account_pool'])} accounts. Check: {len(temp_c)} | Snipe: {len(temp_s)}") return True except Exception as e: log(f"🚨 BOOT FAILURE: {e}") return False async def main(): try: boot_success = await safe_boot_sequence() if not boot_success: return from bot.scheduler import account_status_job, schedule_next, watchdog_cycle, reset_hourly_counters, flush_stats_to_db # Start Background Engines scheduler.add_job(account_status_job, "interval", minutes=1) scheduler.add_job(watchdog_cycle, "interval", seconds=3) scheduler.add_job(reset_hourly_counters, "cron", minute=0) scheduler.add_job(flush_stats_to_db, "interval", minutes=1) schedule_next("check", state["check_interval"]) schedule_next("snipe", state["snipe_interval"]) scheduler.start() from bot.handlers import sync_fleet await sync_fleet() from bot.alerts import alert_admin await alert_admin("🟢 Enterprise System Online\nAll modules restored and listening.") # ⚡ ENTERPRISE FIX: Run both concurrently so the bot actively listens to buttons await asyncio.gather( keep_alive_server(), idle() ) except KeyboardInterrupt: pass finally: log("🛑 Initiating Graceful Shutdown...") shutdown_campers() for acc in state["account_pool"]: if getattr(acc.client, "is_connected", False): await acc.client.stop() if getattr(bot, "is_connected", False): await bot.stop() await scraper.close_session() if __name__ == "__main__": asyncio.run(main())