"""SPA Telegram Bot — main entry point.""" import asyncio import signal import sys from aiogram import Bot, Dispatcher from app.config import BOT_TOKEN from app.logger import logger # Import routers from app.handlers.start import router as start_router from app.handlers.booking import router as booking_router from app.handlers.profile import router as profile_router from app.handlers.mybookings import router as mybookings_router from app.handlers.services import router as services_router from app.handlers.about import router as about_router from app.handlers.contacts import router as contacts_router from app.handlers.admin import router as admin_router bot = Bot(token=BOT_TOKEN) dp = Dispatcher() # Register routers dp.include_routers( start_router, booking_router, profile_router, mybookings_router, services_router, about_router, contacts_router, admin_router, ) async def on_startup(): """Called when bot starts.""" me = await bot.get_me() logger.info( "Bot started | @{username} (id={id}) | Token: {token_preview}...", username=me.username, id=me.id, token_preview=BOT_TOKEN[:10], ) async def on_shutdown(): """Called when bot shuts down.""" logger.info("Bot shutting down...") # Give running tasks time to finish pending = asyncio.all_tasks() if pending: logger.debug("Cancelling {count} pending tasks", count=len(pending)) logger.info("Bot shutdown complete") async def main(): logger.info("Initializing bot...") dp.startup.register(on_startup) dp.shutdown.register(on_shutdown) # Handle SIGTERM/SIGINT gracefully stop_event = asyncio.Event() def _signal_handler(sig, frame): sig_name = signal.Signals(sig).name logger.warning("Received {sig} signal, shutting down...", sig=sig_name) stop_event.set() loop = asyncio.get_event_loop() for sig in (signal.SIGTERM, signal.SIGINT): try: loop.add_signal_handler(sig, lambda s=sig: _signal_handler(s, None)) except NotImplementedError: # Windows fallback signal.signal(sig, _signal_handler) try: await dp.start_polling(bot, stop_signal=stop_event) except asyncio.CancelledError: logger.info("Polling cancelled") except Exception as e: logger.exception("Fatal error in polling: {error}", error=e) finally: await bot.session.close() logger.info("Bot session closed") if __name__ == "__main__": try: asyncio.run(main()) except KeyboardInterrupt: logger.info("Bot stopped by user (Ctrl+C)") except Exception as e: logger.exception("Unhandled exception: {error}", error=e)