From 79b02a4618b5796e04911d4fb7a78b9fc381de67 Mon Sep 17 00:00:00 2001 From: Helian Nuits Date: Fri, 20 Mar 2026 01:51:15 +0800 Subject: [PATCH 1/3] fix: auto-restart telegram polling loop on failure (#373) * fix: auto-restart telegram polling loop on failure * fix: harden telegram polling restart lifecycle --- .../platform/sources/telegram/tg_adapter.py | 59 ++++++++++++++++++- 1 file changed, 56 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..b22149c33b 100644 --- a/astrbot/core/platform/sources/telegram/tg_adapter.py +++ b/astrbot/core/platform/sources/telegram/tg_adapter.py @@ -93,6 +93,26 @@ def __init__( logger.debug(f"Telegram base url: {self.client.base_url}") self.scheduler = AsyncIOScheduler() + self._terminating = False + 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), ...]}} @@ -145,9 +165,41 @@ 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: + await self.application.updater.start_polling( + error_callback=self._on_polling_error + ) + logger.info("Telegram Platform Adapter is running.") + 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, " + f"retrying in {self._polling_restart_delay}s." + ) + except asyncio.CancelledError: + raise + except Exception as e: + logger.exception( + "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}", + exc_info=(type(error), error, error.__traceback__), + ) async def register_commands(self) -> None: """收集所有注册的指令并注册到 Telegram""" @@ -567,6 +619,7 @@ def get_client(self) -> ExtBot: async def terminate(self) -> None: try: + self._terminating = True if self.scheduler.running: self.scheduler.shutdown() From 9c758a75e972e1ad4cfd97e358634c267f3b4861 Mon Sep 17 00:00:00 2001 From: SXP-Simon Date: Fri, 20 Mar 2026 14:34:36 +0800 Subject: [PATCH 2/3] =?UTF-8?q?fix(telegram):=20=E6=A0=B9=E6=8D=AE?= =?UTF-8?q?=E5=BB=BA=E8=AE=AE=E4=BC=98=E5=8C=96=E8=BD=AE=E8=AF=A2=E9=B2=81?= =?UTF-8?q?=E6=A3=92=E6=80=A7=E5=B9=B6=E5=A4=84=E7=90=86=20Token=20?= =?UTF-8?q?=E5=A4=B1=E6=95=88=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../platform/sources/telegram/tg_adapter.py | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/astrbot/core/platform/sources/telegram/tg_adapter.py b/astrbot/core/platform/sources/telegram/tg_adapter.py index b22149c33b..2829759102 100644 --- a/astrbot/core/platform/sources/telegram/tg_adapter.py +++ b/astrbot/core/platform/sources/telegram/tg_adapter.py @@ -8,6 +8,7 @@ from apscheduler.schedulers.asyncio import AsyncIOScheduler from telegram import BotCommand, Update from telegram.constants import ChatType +from telegram.error import InvalidToken, Unauthorized from telegram.ext import ApplicationBuilder, ContextTypes, ExtBot, filters from telegram.ext import MessageHandler as TelegramMessageHandler @@ -167,16 +168,13 @@ async def run(self) -> None: while not self._terminating: try: + logger.info("Starting Telegram polling...") await self.application.updater.start_polling( error_callback=self._on_polling_error ) logger.info("Telegram Platform Adapter is running.") - 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 + while self.application.updater.running and not self._terminating: # noqa: ASYNC110 + await asyncio.sleep(1) if not self._terminating: logger.warning( @@ -185,6 +183,11 @@ async def run(self) -> None: ) except asyncio.CancelledError: raise + except (Unauthorized, InvalidToken) as e: + logger.error( + f"Telegram token is invalid or unauthorized: {e}. Polling stopped." + ) + break except Exception as e: logger.exception( "Telegram polling crashed with exception: " @@ -198,7 +201,7 @@ 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}", - exc_info=(type(error), error, error.__traceback__), + exc_info=error, ) async def register_commands(self) -> None: From 5ef935c90c575b24d0fc4ce4817a1968b9c30d43 Mon Sep 17 00:00:00 2001 From: SXP-Simon Date: Fri, 20 Mar 2026 14:39:42 +0800 Subject: [PATCH 3/3] =?UTF-8?q?fix:=20=E8=A1=A5=E5=85=A8=E9=85=8D=E7=BD=AE?= =?UTF-8?q?=E5=85=83=E6=95=B0=E6=8D=AE=E5=8F=8A=20i18n?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- astrbot/core/config/default.py | 6 ++++++ .../src/i18n/locales/en-US/features/config-metadata.json | 4 ++++ .../src/i18n/locales/zh-CN/features/config-metadata.json | 4 ++++ 3 files changed, 14 insertions(+) diff --git a/astrbot/core/config/default.py b/astrbot/core/config/default.py index 58c1726814..a31a7df339 100644 --- a/astrbot/core/config/default.py +++ b/astrbot/core/config/default.py @@ -396,6 +396,7 @@ class ChatProviderTemplate(TypedDict): "telegram_command_register": True, "telegram_command_auto_refresh": True, "telegram_command_register_interval": 300, + "telegram_polling_restart_delay": 5.0, }, "Discord": { "id": "discord", @@ -686,6 +687,11 @@ class ChatProviderTemplate(TypedDict): "type": "int", "hint": "Telegram 命令自动刷新间隔,单位为秒。", }, + "telegram_polling_restart_delay": { + "description": "Telegram 轮询重启延迟", + "type": "float", + "hint": "当轮询意外结束尝试自动重启时的延迟时间,理论上越短恢复越快,但过短(<0.1s)可能导致死循环针对 API 服务器的请求阻断。单位为秒。默认为 5s。", + }, "id": { "description": "机器人名称", "type": "string", diff --git a/dashboard/src/i18n/locales/en-US/features/config-metadata.json b/dashboard/src/i18n/locales/en-US/features/config-metadata.json index a51dfefbe4..596fb7ecb2 100644 --- a/dashboard/src/i18n/locales/en-US/features/config-metadata.json +++ b/dashboard/src/i18n/locales/en-US/features/config-metadata.json @@ -532,6 +532,10 @@ "description": "Telegram Command Auto Refresh Interval", "hint": "Telegram command auto-refresh interval in seconds." }, + "telegram_polling_restart_delay": { + "description": "Telegram Polling Restart Delay", + "hint": "Waiting time in seconds when the polling loop needs to restart after unexpected exits. Defaults to 5s." + }, "telegram_token": { "description": "Bot Token", "hint": "If you are in mainland China, set a proxy or change api_base in Other Settings." diff --git a/dashboard/src/i18n/locales/zh-CN/features/config-metadata.json b/dashboard/src/i18n/locales/zh-CN/features/config-metadata.json index 69c1746c6e..54b0693de0 100644 --- a/dashboard/src/i18n/locales/zh-CN/features/config-metadata.json +++ b/dashboard/src/i18n/locales/zh-CN/features/config-metadata.json @@ -534,6 +534,10 @@ "description": "Telegram 命令自动刷新间隔", "hint": "Telegram 命令自动刷新间隔,单位为秒。" }, + "telegram_polling_restart_delay": { + "description": "Telegram 轮询重启延迟", + "hint": "当轮询意外结束尝试自动重启时的延迟时间,单位为秒。默认为 5s。" + }, "telegram_token": { "description": "Bot Token", "hint": "如果你的网络环境为中国大陆,请在 `其他配置` 处设置代理或更改 api_base。"