main functional bot. BugFixing required
This commit is contained in:
124
app/bots/bot_runner.py
Normal file
124
app/bots/bot_runner.py
Normal file
@@ -0,0 +1,124 @@
|
||||
import logging
|
||||
import asyncio
|
||||
import signal
|
||||
from contextlib import suppress
|
||||
from typing import Optional, Any
|
||||
|
||||
from apscheduler.schedulers.asyncio import AsyncIOScheduler
|
||||
from telegram.ext import Application
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
class BotApplication:
|
||||
"""Класс для управления жизненным циклом бота и планировщика."""
|
||||
|
||||
def __init__(self):
|
||||
self.app: Optional[Application] = None
|
||||
self.scheduler: Optional[AsyncIOScheduler] = None
|
||||
self.loop: Optional[asyncio.AbstractEventLoop] = None
|
||||
self._shutdown: bool = False
|
||||
|
||||
def signal_handler(self, signum: int, frame: Any) -> None:
|
||||
"""Обработчик сигналов для корректного завершения."""
|
||||
logger.info("Received shutdown signal")
|
||||
self._shutdown = True
|
||||
if self.loop and self.loop.is_running():
|
||||
self.loop.create_task(self.shutdown())
|
||||
|
||||
async def shutdown(self) -> None:
|
||||
"""Корректное завершение работы всех компонентов."""
|
||||
logger.info("Starting graceful shutdown")
|
||||
|
||||
# Останавливаем планировщик
|
||||
if self.scheduler and self.scheduler.running:
|
||||
logger.debug("Shutting down scheduler")
|
||||
with suppress(Exception):
|
||||
self.scheduler.shutdown()
|
||||
|
||||
# Останавливаем приложение
|
||||
if self.app:
|
||||
logger.debug("Shutting down application")
|
||||
with suppress(Exception):
|
||||
await self.app.stop()
|
||||
with suppress(Exception):
|
||||
await self.app.shutdown()
|
||||
|
||||
# Ждем завершения всех задач
|
||||
if self.loop:
|
||||
tasks = [t for t in asyncio.all_tasks(self.loop)
|
||||
if t is not asyncio.current_task(self.loop)]
|
||||
if tasks:
|
||||
logger.debug(f"Cancelling {len(tasks)} pending tasks")
|
||||
for task in tasks:
|
||||
task.cancel()
|
||||
await asyncio.gather(*tasks, return_exceptions=True)
|
||||
|
||||
async def run_bot(self, app: Application, cleanup_job) -> None:
|
||||
"""Запуск бота и планировщика."""
|
||||
try:
|
||||
self.app = app
|
||||
if not self.app:
|
||||
logger.critical("Failed to initialize application")
|
||||
return
|
||||
|
||||
# Настройка планировщика
|
||||
self.scheduler = AsyncIOScheduler()
|
||||
self.scheduler.add_job(cleanup_job, 'interval', minutes=30)
|
||||
|
||||
# Инициализация приложения
|
||||
await self.app.initialize()
|
||||
await self.app.start()
|
||||
|
||||
# Запуск планировщика
|
||||
self.scheduler.start()
|
||||
|
||||
# Запуск бота и ожидание завершения
|
||||
stop_event = asyncio.Event()
|
||||
|
||||
def stop_polling():
|
||||
stop_event.set()
|
||||
|
||||
# Устанавливаем обработчик для остановки
|
||||
self.app.stop_signals = None # Отключаем встроенную обработку сигналов
|
||||
|
||||
try:
|
||||
# Запускаем поллинг
|
||||
await self.app.updater.start_polling(drop_pending_updates=True)
|
||||
|
||||
# Ждем сигнала остановки
|
||||
await stop_event.wait()
|
||||
except Exception as e:
|
||||
if not self._shutdown:
|
||||
logger.error(f"Polling error: {e}")
|
||||
await asyncio.sleep(1)
|
||||
|
||||
except Exception as e:
|
||||
logger.critical(f"Critical error: {e}")
|
||||
finally:
|
||||
await self.shutdown()
|
||||
|
||||
def run_bot(app: Application, cleanup_job) -> None:
|
||||
"""Функция запуска бота с правильной обработкой сигналов и циклом событий."""
|
||||
bot_app = BotApplication()
|
||||
|
||||
# Настройка обработчиков сигналов
|
||||
signal.signal(signal.SIGINT, bot_app.signal_handler)
|
||||
signal.signal(signal.SIGTERM, bot_app.signal_handler)
|
||||
|
||||
# Создание и настройка цикла событий
|
||||
loop = asyncio.new_event_loop()
|
||||
asyncio.set_event_loop(loop)
|
||||
bot_app.loop = loop
|
||||
|
||||
try:
|
||||
loop.run_until_complete(bot_app.run_bot(app, cleanup_job))
|
||||
except KeyboardInterrupt:
|
||||
pass
|
||||
finally:
|
||||
try:
|
||||
if not loop.is_closed():
|
||||
# Очистка
|
||||
loop.run_until_complete(loop.shutdown_asyncgens())
|
||||
loop.close()
|
||||
except Exception as e:
|
||||
logger.error(f"Error during loop cleanup: {e}")
|
||||
Reference in New Issue
Block a user