import asyncio import logging from typing import Optional from aiogram import Bot, Dispatcher from aiogram.client.default import DefaultBotProperties from aiogram.fsm.storage.memory import MemoryStorage from aiogram.fsm.strategy import FSMStrategy from helper_bot.handlers.admin import admin_router from helper_bot.handlers.callback import callback_router from helper_bot.handlers.group import group_router from helper_bot.handlers.private import private_router from helper_bot.handlers.voice import VoiceHandlers from helper_bot.middlewares.blacklist_middleware import BlacklistMiddleware from helper_bot.middlewares.dependencies_middleware import DependenciesMiddleware from helper_bot.middlewares.metrics_middleware import ( ErrorMetricsMiddleware, MetricsMiddleware, ) from helper_bot.middlewares.rate_limit_middleware import RateLimitMiddleware from helper_bot.server_prometheus import start_metrics_server, stop_metrics_server async def start_bot_with_retry( bot: Bot, dp: Dispatcher, max_retries: int = 5, base_delay: float = 1.0 ): """Запуск бота с автоматическим перезапуском при сетевых ошибках""" for attempt in range(max_retries): try: logging.info(f"Запуск бота (попытка {attempt + 1}/{max_retries})") await dp.start_polling(bot, skip_updates=True) break except Exception as e: error_msg = str(e).lower() if any( keyword in error_msg for keyword in ["network", "disconnected", "timeout", "connection"] ): if attempt < max_retries - 1: delay = base_delay * (2**attempt) # Exponential backoff logging.warning( f"Сетевая ошибка при запуске бота: {e}. Повтор через {delay:.1f}с (попытка {attempt + 1}/{max_retries})" ) await asyncio.sleep(delay) continue else: logging.error( f"Превышено максимальное количество попыток запуска бота: {e}" ) raise else: logging.error(f"Критическая ошибка при запуске бота: {e}") raise async def start_bot(bdf): token = bdf.settings["Telegram"]["bot_token"] bot = Bot( token=token, default=DefaultBotProperties( parse_mode="HTML", link_preview_is_disabled=bdf.settings["Telegram"]["preview_link"], ), timeout=60.0, ) # Увеличиваем timeout для стабильности dp = Dispatcher(storage=MemoryStorage(), fsm_strategy=FSMStrategy.GLOBAL_USER) # ✅ Оптимизированная регистрация middleware dp.update.outer_middleware(DependenciesMiddleware()) dp.update.outer_middleware(MetricsMiddleware()) dp.update.outer_middleware(BlacklistMiddleware()) dp.update.outer_middleware(RateLimitMiddleware()) # Создаем экземпляр VoiceHandlers voice_handlers = VoiceHandlers(bdf, bdf.settings) voice_router = voice_handlers.router # Middleware уже добавлены на уровне dispatcher dp.include_routers( admin_router, private_router, callback_router, group_router, voice_router ) # Добавляем обработчик завершения для корректного закрытия @dp.shutdown() async def on_shutdown(): logging.info("Bot shutdown initiated, cleaning up resources...") try: await bot.session.close() logging.info("Bot session closed successfully") except Exception as e: logging.error(f"Error closing bot session during shutdown: {e}") await bot.delete_webhook(drop_pending_updates=True) # Запускаем HTTP сервер для метрик параллельно с ботом metrics_host = bdf.settings.get("Metrics", {}).get("host", "0.0.0.0") metrics_port = bdf.settings.get("Metrics", {}).get("port", 8080) try: # Запускаем метрики сервер await start_metrics_server(metrics_host, metrics_port) logging.info(f"✅ Метрики сервер запущен на {metrics_host}:{metrics_port}") logging.info("✅ Метрики будут обновляться в реальном времени через middleware") # Запускаем бота с retry логикой await start_bot_with_retry(bot, dp) logging.info("✅ Бот запущен") except Exception as e: logging.error(f"❌ Ошибка запуска бота: {e}") raise finally: # Останавливаем метрики сервер при завершении try: await stop_metrics_server() except Exception as e: logging.error(f"Error stopping metrics server: {e}") # Закрываем сессию бота try: await bot.session.close() except Exception as e: logging.error(f"Error closing bot session: {e}") return bot