Files
AnonBot/loader.py

175 lines
7.2 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.
"""
Инициализация бота, диспетчера и базы данных
"""
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