""" Инициализация бота, диспетчера и базы данных """ import asyncio from aiogram import Bot, Dispatcher from aiogram.enums import ParseMode from aiogram.fsm.storage.memory import MemoryStorage from config import config from config.constants import ALLOWED_UPDATES from dependencies import DependencyMiddleware, get_dependencies from handlers import admin, answers, errors, questions, start from middlewares.rate_limit_middleware import RateLimitMiddleware from middlewares.validation_middleware import ValidationMiddleware from services.infrastructure.database import DatabaseService from services.infrastructure.logger import get_logger # Настройка логирования logger = get_logger(__name__) class BotLoader: """Класс для инициализации и запуска бота""" def __init__(self): self.bot: Bot = None self.dp: Dispatcher = None self.db: DatabaseService = None async def init_bot(self) -> Bot: """Инициализация бота""" logger.info("🤖 Инициализация Telegram бота") self.bot = Bot(token=config.BOT_TOKEN) # Устанавливаем parse_mode по умолчанию для aiogram 3.3.0 self.bot.parse_mode = ParseMode.HTML logger.info("✅ Бот успешно инициализирован") return self.bot async def init_dispatcher(self) -> Dispatcher: """Инициализация диспетчера""" logger.info("📡 Инициализация диспетчера") # Используем MemoryStorage для FSM storage = MemoryStorage() self.dp = Dispatcher(storage=storage) # Инициализируем зависимости logger.info("🏗️ Инициализация системы инъекции зависимостей") deps = get_dependencies() await deps.init() # Добавляем middleware для инъекции зависимостей self.dp.update.middleware(DependencyMiddleware(deps)) logger.info("✅ DependencyMiddleware зарегистрирован") # Добавляем middleware для rate limiting self.dp.update.middleware(RateLimitMiddleware()) logger.info("✅ RateLimitMiddleware зарегистрирован") # Добавляем middleware для валидации validation_middleware = ValidationMiddleware(deps.validator) self.dp.update.middleware(validation_middleware) logger.info("✅ ValidationMiddleware зарегистрирован") # Регистрируем обработчики self._register_handlers() logger.info("✅ Диспетчер успешно инициализирован") return self.dp async def init_database(self) -> DatabaseService: """Инициализация базы данных""" logger.info(f"💾 Инициализация базы данных: {config.DATABASE_PATH}") self.db = DatabaseService(config.DATABASE_PATH) await self.db.init() logger.info("✅ База данных успешно инициализирована") return self.db def _register_handlers(self): """Регистрация всех обработчиков""" logger.info("🔧 Регистрация обработчиков") # Обработчики команд self.dp.include_router(start.router) self.dp.include_router(questions.router) self.dp.include_router(answers.router) self.dp.include_router(admin.router) # Обработчик ошибок (должен быть последним) self.dp.include_router(errors.router) logger.info("✅ Все обработчики зарегистрированы") async def start_polling(self): """Запуск бота в режиме polling""" try: logger.info("🚀 Запуск бота в режиме polling") # Инициализируем компоненты await self.init_bot() await self.init_dispatcher() await self.init_database() # Уведомляем администраторов о запуске await self._notify_admins_startup() # Запускаем polling logger.info("🔄 Начинаем polling...") await self.dp.start_polling( self.bot, allowed_updates=ALLOWED_UPDATES ) except Exception as e: logger.error(f"💥 Ошибка при запуске бота: {e}") raise finally: await self.cleanup() async def _notify_admins_startup(self): """Уведомление администраторов о запуске бота""" if not config.ADMINS: logger.warning("⚠️ Список администраторов пуст") return logger.info(f"📢 Уведомление {len(config.ADMINS)} администраторов о запуске") message = "🤖 Бот запущен!\n\n" \ "Анонимный бот для вопросов готов к работе." for admin_id in config.ADMINS: try: await self.bot.send_message(admin_id, message) logger.info(f"✅ Уведомление отправлено админу {admin_id}") except Exception as e: logger.warning(f"⚠️ Не удалось отправить уведомление админу {admin_id}: {e}") async def cleanup(self): """Очистка ресурсов при завершении""" logger.info("🧹 Очистка ресурсов") # Закрываем зависимости try: deps = get_dependencies() await deps.close() logger.info("✅ Зависимости закрыты") except Exception as e: logger.warning(f"⚠️ Ошибка при закрытии зависимостей: {e}") if self.bot: await self.bot.session.close() logger.info("✅ Сессия бота закрыта") if self.db: await self.db.close() logger.info("✅ Соединение с БД закрыто") logger.info("🛑 Бот остановлен") # Создаем глобальный экземпляр загрузчика loader = BotLoader() async def main(): """Главная функция для запуска бота""" await loader.start_polling() if __name__ == "__main__": try: asyncio.run(main()) except KeyboardInterrupt: logger.info("Получен сигнал остановки") except Exception as e: logger.error(f"Критическая ошибка: {e}") raise