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