Files
PrimoGuardBot-/main.py

285 lines
9.5 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.
"""
Точка входа PrimoGuard Bot
"""
from asyncio import run
import asyncio
from typing import List
from configs import settings
from bot import bot, dp, BotInfo, WebhookManager, setup_middlewares, router
from database import get_manager
from middleware.loggers import logger
__all__ = ("main",)
def _start_background_tasks_safe() -> List[asyncio.Task]:
"""
Безопасный запуск фоновых задач с обработкой ошибок.
Returns:
List[asyncio.Task]: Список запущенных задач (пустой список если модуль не найден)
"""
try:
from bot.tasks import start_background_tasks
tasks = start_background_tasks()
logger.info(
f"🚀 Запущено {len(tasks)} фоновых задач",
log_type="STARTUP"
)
return tasks
except ImportError:
logger.warning(
"⚠️ Модуль 'bot.tasks' не найден, фоновые задачи не запущены",
log_type="STARTUP"
)
return []
except Exception as e:
logger.error(
f"❌ Ошибка запуска фоновых задач: {e}",
log_type="STARTUP"
)
return []
async def setup_services(setup_webhook: bool = True) -> str:
"""
Инициализация всех сервисов: БД и бот.
Args:
setup_webhook: Устанавливать ли webhook в BotInfo.setup()
Returns:
str: Username бота
"""
# База данных
manager = get_manager()
await manager.init()
stats = await manager.get_stats()
logger.info(
f"📊 БД инициализирована: {stats.get('total_banwords', 0)} банвордов",
log_type="DATABASE"
)
# Бот: получение информации (БЕЗ webhook)
await BotInfo.setup(bots=bot, setup_webhook=setup_webhook)
# ВАЖНО: Регистрируем middleware и роутеры ДО установки webhook
setup_middlewares(
dp=dp,
bot=bot,
enable_spam_check=settings.ANTI_SPAM,
channel_ids=[],
)
# Подключение маршрутов
dp.include_router(router)
logger.info("Все handlers и middleware зарегистрированы", log_type="STARTUP")
return BotInfo.username
async def on_startup(app) -> None:
"""Выполняется при запуске webhook-сервера."""
# 1. Инициализируем всё БЕЗ webhook
username = await setup_services(setup_webhook=False)
# 2. Запускаем фоновые задачи
background_tasks = _start_background_tasks_safe()
app['background_tasks'] = background_tasks
# 3. ТЕПЕРЬ устанавливаем webhook (когда всё готово)
webhook = WebhookManager(bot, dp)
if settings.WEBHOOK_URL:
success = await webhook.setup(
webhook_url=settings.WEBHOOK_URL,
secret_token=settings.SECRET_TOKEN,
drop_pending_updates=True
)
if success:
logger.success(
f"✅ Бот @{username} запущен в режиме Webhook",
log_type="STARTUP"
)
else:
logger.error(
"Не удалось установить webhook, но сервер запущен",
log_type="STARTUP"
)
else:
logger.warning(
"⚠️ WEBHOOK_URL не указан",
log_type="STARTUP"
)
async def on_shutdown(app) -> None:
"""Очистка ресурсов при остановке сервера."""
logger.info("👋 Остановка бота...", log_type="SHUTDOWN")
try:
# Отменяем фоновые задачи
if 'background_tasks' in app and app['background_tasks']:
tasks = app['background_tasks']
logger.info(
f"⏸️ Остановка {len(tasks)} фоновых задач...",
log_type="SHUTDOWN"
)
for task in tasks:
if not task.done():
task.cancel()
# Ждём завершения с таймаутом 5 секунд
try:
await asyncio.wait_for(
asyncio.gather(*tasks, return_exceptions=True),
timeout=5.0
)
logger.info("✅ Фоновые задачи остановлены", log_type="SHUTDOWN")
except asyncio.TimeoutError:
logger.warning(
"⚠️ Таймаут остановки фоновых задач (5 сек)",
log_type="SHUTDOWN"
)
# Закрываем соединения
logger.info("📊 Закрытие базы данных...", log_type="SHUTDOWN")
await get_manager().close()
logger.info("🤖 Закрытие сессии бота...", log_type="SHUTDOWN")
await bot.session.close()
except Exception as e:
logger.error(f"❌ Ошибка при закрытии: {e}", log_type="SHUTDOWN")
logger.success("✅ Бот остановлен", log_type="SHUTDOWN")
def start_webhook() -> None:
"""Запуск в режиме Webhook (синхронный)."""
logger.setup()
webhook = WebhookManager(bot, dp)
# ВАЖНО: Конфигурируем webhook ПЕРЕД on_startup
webhook.configure()
# Добавляем startup/shutdown handlers
webhook.app.on_startup.append(on_startup)
webhook.app.on_shutdown.append(on_shutdown)
# Запускаем webhook сервер
logger.info("🌐 Запуск Webhook сервера...", log_type="MAIN")
webhook.run()
async def start_polling() -> None:
"""Запуск в режиме Polling (асинхронный)."""
logger.setup()
background_tasks: List[asyncio.Task] = []
try:
# 1. Инициализируем сервисы
username = await setup_services(setup_webhook=False)
# 2. Запускаем фоновые задачи
background_tasks = _start_background_tasks_safe()
# 3. Удаляем webhook для polling режима
webhook = WebhookManager(bot, dp)
await webhook.delete(drop_pending_updates=True)
logger.success(
f"✅ Бот @{username} запущен в режиме Polling",
log_type="STARTUP"
)
# 4. Запускаем polling
await dp.start_polling(bot, drop_pending_updates=True)
except KeyboardInterrupt:
logger.info("⚠️ Получен сигнал остановки (Ctrl+C)", log_type="MAIN")
except Exception as e:
logger.critical(
f"🔥 Критическая ошибка: {e}",
log_type="MAIN"
)
raise
finally:
logger.info("🧹 Очистка ресурсов...", log_type="SHUTDOWN")
try:
# Отменяем фоновые задачи
if background_tasks:
logger.info(
f"⏸️ Остановка {len(background_tasks)} фоновых задач...",
log_type="SHUTDOWN"
)
for task in background_tasks:
if not task.done():
task.cancel()
# Ждём завершения с таймаутом 5 секунд
try:
await asyncio.wait_for(
asyncio.gather(*background_tasks, return_exceptions=True),
timeout=5.0
)
logger.info("✅ Фоновые задачи остановлены", log_type="SHUTDOWN")
except asyncio.TimeoutError:
logger.warning(
"⚠️ Таймаут остановки фоновых задач (5 сек)",
log_type="SHUTDOWN"
)
# Закрываем соединения
logger.info("📊 Закрытие базы данных...", log_type="SHUTDOWN")
await get_manager().close()
logger.info("🤖 Закрытие сессии бота...", log_type="SHUTDOWN")
await bot.session.close()
logger.success("✅ Бот остановлен", log_type="SHUTDOWN")
except Exception as e:
logger.error(
f"❌ Ошибка при очистке ресурсов: {e}",
log_type="SHUTDOWN"
)
def main() -> None:
"""Входная точка проекта."""
try:
if settings.WEBHOOK:
# ========== WEBHOOK РЕЖИМ ==========
logger.info("🔧 Режим: Webhook", log_type="MAIN")
start_webhook()
else:
# ========== POLLING РЕЖИМ ==========
logger.info("🔧 Режим: Polling", log_type="MAIN")
run(start_polling())
except KeyboardInterrupt:
logger.info("⚠️ Остановка по сигналу Ctrl+C", log_type="MAIN")
except Exception as e:
logger.critical(
f"🔥 Критическая ошибка при запуске: {e}",
log_type="MAIN"
)
raise
if __name__ == "__main__":
main()