""" Unified entry point for bot and web interface """ import asyncio import logging from pyrogram import Client from shared.config import settings from shared.database.session import init_db from bot.modules.message_handler.commands import register_commands from bot.modules.message_handler.callbacks import register_callbacks from bot.modules.access_control.middleware import setup_middleware from bot.modules.task_scheduler.executor import task_executor, set_app_client from bot.modules.task_scheduler.queue import task_queue from bot.utils.logger import setup_logger from web.app import app as web_app import uvicorn # Setup logging setup_logger() logger = logging.getLogger(__name__) async def run_web_server(): """Run web server in the same event loop""" config = uvicorn.Config( app=web_app, host=settings.WEB_HOST, port=settings.WEB_PORT, log_level="info", loop="asyncio" ) server = uvicorn.Server(config) logger.info(f"Starting web server on {settings.WEB_HOST}:{settings.WEB_PORT}") await server.serve() async def run_bot(): """Run Telegram bot""" logger.info("Starting Telegram bot...") # Check cookies file if configured from shared.config import settings if settings.COOKIES_FILE: from pathlib import Path import os cookies_paths = [ Path(settings.COOKIES_FILE), Path(__file__).parent / settings.COOKIES_FILE, Path(os.getcwd()) / settings.COOKIES_FILE, ] cookies_found = False for path in cookies_paths: if path.exists() and path.is_file(): logger.info(f"✅ Cookies file found: {path} (configured as: {settings.COOKIES_FILE})") cookies_found = True break if not cookies_found: logger.warning( f"⚠️ Cookies file not found: {settings.COOKIES_FILE}. " f"Searched in: {[str(p) for p in cookies_paths]}. " f"Instagram downloads may fail without cookies." ) else: logger.info("ℹ️ No cookies file configured (COOKIES_FILE not set)") # Initialize database await init_db() logger.info("Database initialized") # Initialize task queue await task_queue.initialize() logger.info("Task queue initialized") # Create Pyrogram client app = Client( "tgloader_bot", api_id=settings.TELEGRAM_API_ID, api_hash=settings.TELEGRAM_API_HASH, bot_token=settings.BOT_TOKEN, ) # Setup middleware for access control setup_middleware(app) # Set client for task executor set_app_client(app) # Register handlers register_commands(app) register_callbacks(app) logger.info("Bot ready") # Start bot await app.start() logger.info(f"Bot started. Owner ID: {settings.OWNER_ID}") # Start task executor AFTER bot is started await task_executor.start() logger.info("Task executor started") # Start background task for cleaning old files from bot.utils.file_cleanup import cleanup_files_periodically cleanup_task = asyncio.create_task(cleanup_files_periodically()) logger.info("File cleanup task started") # Start background task for updating user information from bot.utils.user_info_updater import update_users_without_info_periodically user_info_task = asyncio.create_task(update_users_without_info_periodically()) logger.info("User info update task started") return app async def main(): """Main startup function""" logger.info("=" * 50) logger.info("Starting TGLoader (Bot + Web Interface)") logger.info("=" * 50) # Start bot bot_app = await run_bot() # Start web server in the same event loop web_task = asyncio.create_task(run_web_server()) logger.info("Web server started in the same event loop") # Wait for completion (running in parallel) try: # Wait for web server to complete (runs indefinitely) await web_task except KeyboardInterrupt: logger.info("Received stop signal (Ctrl+C)") except asyncio.CancelledError: logger.info("Received cancellation signal") finally: logger.info("Starting graceful shutdown...") # Stop web server web_task.cancel() try: await web_task except asyncio.CancelledError: pass # Stop task executor try: await task_executor.stop() logger.info("Task executor stopped") except Exception as e: logger.error(f"Error stopping executor: {e}") # Stop bot try: await bot_app.stop() logger.info("Bot stopped") except Exception as e: logger.error(f"Error stopping bot: {e}") logger.info("Application terminated") if __name__ == "__main__": try: asyncio.run(main()) except KeyboardInterrupt: logger.info("Получен сигнал остановки (KeyboardInterrupt)") except Exception as e: logger.error(f"Критическая ошибка: {e}", exc_info=True)