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/astrbot/core/platform/sources/telegram/tg_adapter.py b/astrbot/core/platform/sources/telegram/tg_adapter.py index 87e21391e6..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 @@ -93,6 +94,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 +166,43 @@ 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: + logger.info("Starting Telegram polling...") + await self.application.updater.start_polling( + error_callback=self._on_polling_error + ) + logger.info("Telegram Platform Adapter is running.") + while self.application.updater.running and not self._terminating: # noqa: ASYNC110 + await asyncio.sleep(1) + + if not self._terminating: + logger.warning( + "Telegram polling loop exited unexpectedly, " + f"retrying in {self._polling_restart_delay}s." + ) + 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: " + 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=error, + ) async def register_commands(self) -> None: """收集所有注册的指令并注册到 Telegram""" @@ -567,6 +622,7 @@ def get_client(self) -> ExtBot: async def terminate(self) -> None: try: + self._terminating = True if self.scheduler.running: self.scheduler.shutdown() 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。"