129 lines
5.4 KiB
Python
129 lines
5.4 KiB
Python
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
|