187 lines
6.0 KiB
Python
187 lines
6.0 KiB
Python
#!/usr/bin/env python3
|
||
"""
|
||
SalvageDB Bot - Главный файл запуска
|
||
Модульная архитектура с разделением хэндлеров
|
||
"""
|
||
|
||
import asyncio
|
||
import signal
|
||
import sys
|
||
import logging
|
||
|
||
from aiogram import Bot, Dispatcher
|
||
from aiogram.fsm.storage.memory import MemoryStorage
|
||
|
||
from config.settings import BOT_TOKEN
|
||
from database import DatabaseManager
|
||
from middlewares.db import DbSessionMiddleware
|
||
from utils.logging_config import setup_logging
|
||
from utils.system_utils import get_operating_system, log_system_info
|
||
|
||
# Импорт всех роутеров
|
||
from handlers.main_handlers import router as main_router
|
||
from handlers.vin_handlers import router as vin_router
|
||
from handlers.payment_handlers import router as payment_router
|
||
from handlers.admin.main_admin import router as admin_main_router
|
||
|
||
# Глобальные переменные
|
||
bot = None
|
||
dp = None
|
||
database_manager = None
|
||
|
||
|
||
async def on_startup():
|
||
"""Инициализация при запуске"""
|
||
global database_manager
|
||
|
||
logging.info("=== BOT STARTUP ===")
|
||
log_system_info()
|
||
|
||
# Инициализируем базу данных
|
||
try:
|
||
database_manager = DatabaseManager()
|
||
await database_manager.initialize()
|
||
logging.info("Database manager initialized successfully")
|
||
except Exception as e:
|
||
logging.error(f"Failed to initialize database: {e}")
|
||
sys.exit(1)
|
||
|
||
logging.info("Bot startup completed successfully")
|
||
|
||
|
||
async def on_shutdown():
|
||
"""Очистка при завершении"""
|
||
global database_manager
|
||
|
||
logging.info("=== BOT SHUTDOWN ===")
|
||
|
||
if database_manager:
|
||
await database_manager.close()
|
||
logging.info("Database connections closed")
|
||
|
||
logging.info("Bot shutdown completed")
|
||
|
||
|
||
def setup_signal_handlers():
|
||
"""Настройка обработчиков сигналов для корректного завершения"""
|
||
|
||
def signal_handler(signum, frame):
|
||
logging.info(f"Received signal {signum}, initiating graceful shutdown...")
|
||
|
||
# Получаем текущий event loop
|
||
try:
|
||
loop = asyncio.get_running_loop()
|
||
# Создаем задачу для завершения
|
||
loop.create_task(shutdown_bot())
|
||
except RuntimeError:
|
||
# Если loop не найден, завершаем принудительно
|
||
logging.warning("No running event loop found, forcing exit...")
|
||
sys.exit(0)
|
||
|
||
# Регистрируем обработчики для разных сигналов
|
||
signal.signal(signal.SIGINT, signal_handler) # Ctrl+C
|
||
signal.signal(signal.SIGTERM, signal_handler) # Команда завершения
|
||
|
||
# Для Windows добавляем обработку SIGBREAK
|
||
if get_operating_system() == 'Windows':
|
||
try:
|
||
signal.signal(signal.SIGBREAK, signal_handler)
|
||
except AttributeError:
|
||
pass # SIGBREAK может быть недоступен в некоторых версиях
|
||
|
||
|
||
async def shutdown_bot():
|
||
"""Корректное завершение работы бота"""
|
||
logging.info("Shutting down bot...")
|
||
|
||
# Останавливаем polling
|
||
if dp:
|
||
await dp.stop_polling()
|
||
|
||
# Закрываем сессию бота
|
||
if bot:
|
||
await bot.session.close()
|
||
|
||
# Вызываем on_shutdown
|
||
await on_shutdown()
|
||
|
||
logging.info("Bot shutdown complete, exiting...")
|
||
sys.exit(0)
|
||
|
||
|
||
def setup_routers(dispatcher: Dispatcher):
|
||
"""Настройка всех роутеров"""
|
||
# Порядок важен - более специфичные роутеры должны быть первыми
|
||
|
||
# Админ роутеры (самый высокий приоритет)
|
||
dispatcher.include_router(admin_main_router)
|
||
|
||
# Платежные роутеры
|
||
dispatcher.include_router(payment_router)
|
||
|
||
# VIN роутеры
|
||
dispatcher.include_router(vin_router)
|
||
|
||
# Основные роутеры (самый низкий приоритет)
|
||
dispatcher.include_router(main_router)
|
||
|
||
logging.info("All routers configured successfully")
|
||
|
||
|
||
async def main():
|
||
"""Главная функция запуска бота"""
|
||
global bot, dp
|
||
|
||
# Настройка логирования
|
||
setup_logging()
|
||
|
||
# Настройка обработчиков сигналов
|
||
setup_signal_handlers()
|
||
|
||
logging.info("Starting SalvageDB Bot...")
|
||
logging.info(f"Python version: {sys.version}")
|
||
logging.info(f"Operating System: {get_operating_system()}")
|
||
|
||
try:
|
||
# Инициализация бота и диспетчера
|
||
bot = Bot(token=BOT_TOKEN)
|
||
dp = Dispatcher(storage=MemoryStorage())
|
||
|
||
# Настройка middleware для работы с базой данных
|
||
dp.middleware.setup(DbSessionMiddleware())
|
||
|
||
# Настройка всех роутеров
|
||
setup_routers(dp)
|
||
|
||
# Регистрация событий запуска и завершения
|
||
dp.startup.register(on_startup)
|
||
dp.shutdown.register(on_shutdown)
|
||
|
||
# Запуск polling
|
||
logging.info("Starting bot polling...")
|
||
await dp.start_polling(
|
||
bot,
|
||
allowed_updates=['message', 'callback_query', 'pre_checkout_query'],
|
||
drop_pending_updates=True
|
||
)
|
||
|
||
except KeyboardInterrupt:
|
||
logging.info("Received KeyboardInterrupt, shutting down...")
|
||
except Exception as e:
|
||
logging.error(f"Fatal error in main: {e}")
|
||
raise
|
||
finally:
|
||
# Финальная очистка
|
||
if bot:
|
||
await bot.session.close()
|
||
logging.info("Bot stopped")
|
||
|
||
|
||
if __name__ == "__main__":
|
||
try:
|
||
asyncio.run(main())
|
||
except KeyboardInterrupt:
|
||
logging.info("Bot interrupted by user")
|
||
except Exception as e:
|
||
logging.error(f"Fatal error: {e}")
|
||
sys.exit(1) |