175 lines
7.2 KiB
Python
175 lines
7.2 KiB
Python
"""
|
||
Инициализация бота, диспетчера и базы данных
|
||
"""
|
||
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 = "🤖 <b>Бот запущен!</b>\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
|