import asyncio import logging import pydantic from aiogram import Bot, Dispatcher, Router class TelegramConfig(pydantic.BaseModel): TOKEN: str DROP_PENDING_UPDATES: bool = True class TelegramBase: def __init__(self, config: TelegramConfig, *routers: Router) -> None: self.bot: Bot = Bot(token=config.TOKEN) self.dispatcher: Dispatcher = Dispatcher() self._polling_task: asyncio.Task[None] | None = None self._drop_pending_updates: bool = config.DROP_PENDING_UPDATES for router in routers: self.dispatcher.include_router(router) logging.info('Telegram bot initialized') async def start_polling(self) -> None: if self._polling_task is not None: logging.warning('Bot polling already started') return self._polling_task = asyncio.create_task( self.dispatcher.start_polling( self.bot, drop_pending_updates=self._drop_pending_updates, handle_signals=False, # Don't handle SIGINT/SIGTERM in dispatcher ) ) logging.info('Telegram bot polling started') async def stop_polling(self) -> None: if self._polling_task is None: logging.warning('Bot polling not started') return # Stop dispatcher polling gracefully await self.dispatcher.stop_polling() # Wait for polling task to finish try: await asyncio.wait_for(self._polling_task, timeout=5.0) except TimeoutError: logging.warning('Polling task did not stop in time, cancelling') self._polling_task.cancel() try: await self._polling_task except asyncio.CancelledError: pass except asyncio.CancelledError: logging.info('Polling task cancelled') # Close bot session await self.bot.session.close() self._polling_task = None logging.info('Telegram bot stopped')