savagedb_bot/main.py

191 lines
6.3 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/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())
# Инициализируем DatabaseManager сначала
database_manager = DatabaseManager()
await database_manager.initialize()
# Настройка middleware для работы с базой данных
dp.message.middleware(DbSessionMiddleware(database_manager))
dp.callback_query.middleware(DbSessionMiddleware(database_manager))
# Настройка всех роутеров
setup_routers(dp)
# Регистрация событий завершения (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)