From 27b6499248080a26d0240891dab8c53aeba37958 Mon Sep 17 00:00:00 2001 From: Helian Nuits Date: Fri, 20 Mar 2026 01:13:49 +0800 Subject: [PATCH 1/2] fix: auto-restart telegram polling loop on failure --- .../platform/sources/telegram/tg_adapter.py | 36 +++++++++++++++++-- 1 file changed, 33 insertions(+), 3 deletions(-) diff --git a/astrbot/core/platform/sources/telegram/tg_adapter.py b/astrbot/core/platform/sources/telegram/tg_adapter.py index 87e21391e6..e7b5f89eab 100644 --- a/astrbot/core/platform/sources/telegram/tg_adapter.py +++ b/astrbot/core/platform/sources/telegram/tg_adapter.py @@ -93,6 +93,10 @@ def __init__( logger.debug(f"Telegram base url: {self.client.base_url}") self.scheduler = AsyncIOScheduler() + self._terminating = False + self._polling_restart_delay = float( + self.config.get("telegram_polling_restart_delay", 5.0) + ) # Media group handling # Cache structure: {media_group_id: {"created_at": datetime, "items": [(update, context), ...]}} @@ -145,9 +149,34 @@ async def run(self) -> None: logger.error("Telegram Updater is not initialized. Cannot start polling.") return - queue = self.application.updater.start_polling() - logger.info("Telegram Platform Adapter is running.") - await queue + while not self._terminating: + try: + queue = self.application.updater.start_polling( + error_callback=self._on_polling_error + ) + logger.info("Telegram Platform Adapter is running.") + await queue + if not self._terminating: + logger.warning( + "Telegram polling loop exited unexpectedly, " + f"retrying in {self._polling_restart_delay}s." + ) + except asyncio.CancelledError: + raise + except Exception as e: + logger.error( + "Telegram polling crashed with exception: " + f"{type(e).__name__}: {e!s}. " + f"Retrying in {self._polling_restart_delay}s." + ) + + if not self._terminating: + await asyncio.sleep(self._polling_restart_delay) + + def _on_polling_error(self, error: Exception) -> None: + logger.error( + f"Telegram polling request failed: {type(error).__name__}: {error!s}" + ) async def register_commands(self) -> None: """收集所有注册的指令并注册到 Telegram""" @@ -567,6 +596,7 @@ def get_client(self) -> ExtBot: async def terminate(self) -> None: try: + self._terminating = True if self.scheduler.running: self.scheduler.shutdown() From 77754d5081bdf5be7052a79ee01391b7bd94811a Mon Sep 17 00:00:00 2001 From: Helian Nuits Date: Fri, 20 Mar 2026 01:42:21 +0800 Subject: [PATCH 2/2] fix: harden telegram polling restart lifecycle --- .../platform/sources/telegram/tg_adapter.py | 39 +++++++++++++++---- 1 file changed, 31 insertions(+), 8 deletions(-) diff --git a/astrbot/core/platform/sources/telegram/tg_adapter.py b/astrbot/core/platform/sources/telegram/tg_adapter.py index e7b5f89eab..b22149c33b 100644 --- a/astrbot/core/platform/sources/telegram/tg_adapter.py +++ b/astrbot/core/platform/sources/telegram/tg_adapter.py @@ -94,9 +94,25 @@ def __init__( self.scheduler = AsyncIOScheduler() self._terminating = False - self._polling_restart_delay = float( - self.config.get("telegram_polling_restart_delay", 5.0) - ) + raw_delay = self.config.get("telegram_polling_restart_delay", 5.0) + try: + delay = float(raw_delay) + except (TypeError, ValueError): + logger.warning( + "Invalid 'telegram_polling_restart_delay' value %r in config, " + "falling back to default 5.0s", + raw_delay, + ) + delay = 5.0 + + if delay < 0.1: + logger.warning( + "Configured 'telegram_polling_restart_delay' (%s) is too small; " + "enforcing minimum of 0.1s to avoid tight restart loops", + delay, + ) + delay = 0.1 + self._polling_restart_delay = delay # Media group handling # Cache structure: {media_group_id: {"created_at": datetime, "items": [(update, context), ...]}} @@ -151,11 +167,17 @@ async def run(self) -> None: while not self._terminating: try: - queue = self.application.updater.start_polling( + await self.application.updater.start_polling( error_callback=self._on_polling_error ) logger.info("Telegram Platform Adapter is running.") - await queue + polling_check_event = asyncio.Event() + while self.application.updater.running and not self._terminating: + try: + await asyncio.wait_for(polling_check_event.wait(), timeout=1) + except TimeoutError: + continue + if not self._terminating: logger.warning( "Telegram polling loop exited unexpectedly, " @@ -164,10 +186,10 @@ async def run(self) -> None: except asyncio.CancelledError: raise except Exception as e: - logger.error( + logger.exception( "Telegram polling crashed with exception: " f"{type(e).__name__}: {e!s}. " - f"Retrying in {self._polling_restart_delay}s." + f"Retrying in {self._polling_restart_delay}s.", ) if not self._terminating: @@ -175,7 +197,8 @@ async def run(self) -> None: def _on_polling_error(self, error: Exception) -> None: logger.error( - f"Telegram polling request failed: {type(error).__name__}: {error!s}" + f"Telegram polling request failed: {type(error).__name__}: {error!s}", + exc_info=(type(error), error, error.__traceback__), ) async def register_commands(self) -> None: