# main.py import os import logging import asyncio from concurrent.futures import ThreadPoolExecutor from hydrogram import Client, idle from hydrogram.errors import AuthKeyUnregistered from config import Config import handlers # To access register_handlers # --- Global Executor --- # Defined here so it can be imported by handlers if structured that way, # or simply passed/used within main_async_logic. # max_workers can be adjusted based on CPU cores or expected load. executor = ThreadPoolExecutor(max_workers=(os.cpu_count() or 1) + 4) # --- Logging Setup --- def setup_logging(): logging.basicConfig( level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - [%(module)s.%(funcName)s:%(lineno)d] - %(message)s', ) logging.getLogger("hydrogram").setLevel(logging.WARNING) # Reduce hydrogram verbosity return logging.getLogger(__name__) logger = setup_logging() # --- Directory and Font Checks --- def run_startup_checks(): logger.info("Running startup checks...") # Check directories (expected to be created by Dockerfile or COPY . .) if not os.path.isdir(Config.PREDEFINED_TEMPLATES_DIR): logger.error(f"FATAL: Templates directory '{Config.PREDEFINED_TEMPLATES_DIR}' not found or is not a directory in the repository root.") return False if not os.path.isdir(Config.OUTPUT_DIR): # This one is created by Dockerfile if not present logger.info(f"Output directory '{Config.OUTPUT_DIR}' not found, will be created by Dockerfile.") # It's okay if this one is missing initially, Dockerfile handles it. # But templates dir *must* come from repo. # Check if templates directory has content (optional but good) try: if os.path.isdir(Config.PREDEFINED_TEMPLATES_DIR) and not os.listdir(Config.PREDEFINED_TEMPLATES_DIR): logger.warning(f"Templates directory '{Config.PREDEFINED_TEMPLATES_DIR}' is empty. Predefined templates will not work.") except Exception as e: logger.warning(f"Could not check contents of templates directory: {e}") logger.info("Required directories check complete.") logger.info(f"Attempting to use font: '{Config.FONT_PATH}' (requires system font access).") return True # --- Main Application Logic --- async def main_async_logic(): logger.info("Initializing Hydrogram Bot Client...") app = Client( name=Config.SESSION_NAME, api_id=Config.API_ID, api_hash=Config.API_HASH, bot_token=Config.BOT_TOKEN, workdir="/app" # Ensures .session file is stored in /app (writable) ) handlers.register_handlers(app) logger.info("Message handlers registered.") try: logger.info("Starting Hydrogram client (as BOT)...") await app.start() me = await app.get_me() logger.info(f"Successfully started. Bot: {me.first_name} (Username: @{me.username}, ID: {me.id})") logger.info("Bot is up and listening for messages from admins!") await idle() except AuthKeyUnregistered: logger.critical("BOT TOKEN INVALID or REVOKED. Please check your BOT_TOKEN secret in Hugging Face.") except Exception as e: logger.critical(f"Error starting or running Hydrogram client: {e}", exc_info=True) finally: logger.info("Shutting down executor...") executor.shutdown(wait=True) if app.is_initialized and app.is_connected: # Check if client was started logger.info("Stopping Hydrogram client...") await app.stop() logger.info("Shutdown complete.") if __name__ == "__main__": if not run_startup_checks(): logger.critical("Startup checks failed. Exiting.") exit(1) try: asyncio.run(main_async_logic()) except KeyboardInterrupt: logger.info("Bot stopped by KeyboardInterrupt.") except Exception as e: logger.critical(f"Unhandled exception in main: {e}", exc_info=True) finally: logger.info("Application terminated.")