From be782126baea95340c502a2c761c07f3e5225c4e Mon Sep 17 00:00:00 2001 From: Trainingdlu Date: Wed, 5 Aug 2026 01:15:58 +0900 Subject: [PATCH 1/2] feat(telegram): add dedicated proxy configuration --- astrbot/core/config/default.py | 8 +++++- .../platform/sources/telegram/tg_adapter.py | 9 +++++-- .../en-US/features/config-metadata.json | 6 ++++- .../ru-RU/features/config-metadata.json | 6 ++++- .../zh-CN/features/config-metadata.json | 6 ++++- tests/test_telegram_adapter.py | 27 +++++++++++++++++++ 6 files changed, 56 insertions(+), 6 deletions(-) diff --git a/astrbot/core/config/default.py b/astrbot/core/config/default.py index 8836a51f1a..a0988afac3 100644 --- a/astrbot/core/config/default.py +++ b/astrbot/core/config/default.py @@ -450,6 +450,7 @@ "type": "telegram", "enable": True, "telegram_token": "your_bot_token", + "telegram_proxy": "", "start_message": "Hello, I'm AstrBot!", "telegram_api_base_url": "https://api.telegram.org/bot", "telegram_file_base_url": "https://api.telegram.org/file/bot", @@ -673,7 +674,12 @@ "telegram_token": { "description": "Bot Token", "type": "string", - "hint": "如果你的网络环境为中国大陆,请在 `其他配置` 处设置代理或更改 api_base。", + "hint": "在此处填入你的 Telegram Bot Token", + }, + "telegram_proxy": { + "description": "Telegram 代理地址", + "type": "string", + "hint": "可选的代理地址,如 http://ip:port 或 socks5://ip:port。仅 Telegram 适配器使用,留空则回退到全局代理设置。", }, "mattermost_url": { "description": "Mattermost URL", diff --git a/astrbot/core/platform/sources/telegram/tg_adapter.py b/astrbot/core/platform/sources/telegram/tg_adapter.py index d8efd7f5d8..8d4bf14462 100644 --- a/astrbot/core/platform/sources/telegram/tg_adapter.py +++ b/astrbot/core/platform/sources/telegram/tg_adapter.py @@ -130,13 +130,18 @@ def __init__( ) # max seconds - hard cap to prevent indefinite delay def _build_application(self) -> None: - self.application = ( + builder = ( ApplicationBuilder() .token(self.config["telegram_token"]) .base_url(self.base_url) .base_file_url(self.file_base_url) - .build() ) + proxy = self.config.get("telegram_proxy") + if proxy: + # getUpdates long-polling uses a separate request pool, so .proxy() + # alone would leave polling on a direct connection. + builder = builder.proxy(proxy).get_updates_proxy(proxy) + self.application = builder.build() message_handler = TelegramMessageHandler( filters=filters.ALL, callback=self.message_handler, 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 fdd430c63e..b9ac177a7a 100644 --- a/dashboard/src/i18n/locales/en-US/features/config-metadata.json +++ b/dashboard/src/i18n/locales/en-US/features/config-metadata.json @@ -600,9 +600,13 @@ "description": "Telegram Polling Restart Delay", "hint": "Waiting time in seconds when the polling loop needs to restart after unexpected exits. Defaults to 5s." }, + "telegram_proxy": { + "description": "Telegram Proxy URL", + "hint": "Optional proxy URL, e.g. http://ip:port or socks5://ip:port. Used only by the Telegram adapter; falls back to the global proxy settings when empty." + }, "telegram_token": { "description": "Bot Token", - "hint": "If you are in mainland China, set a proxy or change api_base in Other Settings." + "hint": "Enter your Telegram Bot Token here." }, "mattermost_url": { "description": "Mattermost URL", diff --git a/dashboard/src/i18n/locales/ru-RU/features/config-metadata.json b/dashboard/src/i18n/locales/ru-RU/features/config-metadata.json index 3cd0104ca1..47e6b17fa5 100644 --- a/dashboard/src/i18n/locales/ru-RU/features/config-metadata.json +++ b/dashboard/src/i18n/locales/ru-RU/features/config-metadata.json @@ -596,9 +596,13 @@ "description": "Интервал автообновления команд Telegram", "hint": "Интервал автоматического обновления команд Telegram в секундах." }, + "telegram_proxy": { + "description": "Proxy URL для Telegram", + "hint": "Формат: http://ip:port или socks5://ip:port. Используется только адаптером Telegram. Если поле пустое, применяются глобальные настройки прокси." + }, "telegram_token": { "description": "Токен бота", - "hint": "Если вы находитесь в материковом Китае, установите прокси или измените api_base в разделе «Другие настройки»." + "hint": "Введите сюда токен вашего Telegram-бота." }, "mattermost_url": { "description": "URL Mattermost", 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 e25cb8e0fb..11e6910e87 100644 --- a/dashboard/src/i18n/locales/zh-CN/features/config-metadata.json +++ b/dashboard/src/i18n/locales/zh-CN/features/config-metadata.json @@ -602,9 +602,13 @@ "description": "Telegram 轮询重启延迟", "hint": "当轮询意外结束尝试自动重启时的延迟时间,单位为秒。默认为 5s。" }, + "telegram_proxy": { + "description": "Telegram 代理地址", + "hint": "可选的代理地址,如 http://ip:port 或 socks5://ip:port。仅 Telegram 适配器使用,留空则回退到全局代理设置。" + }, "telegram_token": { "description": "Bot Token", - "hint": "如果你的网络环境为中国大陆,请在 `其他配置` 处设置代理或更改 api_base。" + "hint": "在此处填入你的 Telegram Bot Token" }, "mattermost_url": { "description": "Mattermost URL", diff --git a/tests/test_telegram_adapter.py b/tests/test_telegram_adapter.py index 17fe60f111..c4ff5e05db 100644 --- a/tests/test_telegram_adapter.py +++ b/tests/test_telegram_adapter.py @@ -465,3 +465,30 @@ async def final_start_polling(*args, **kwargs): app_two.shutdown.assert_awaited() app_three.initialize.assert_awaited() app_three.start.assert_awaited() + + +@pytest.mark.asyncio +async def test_telegram_proxy_is_applied_to_bot_api_and_polling_requests(): + TelegramPlatformAdapter = _load_telegram_adapter() + module_globals = TelegramPlatformAdapter.__init__.__globals__ + proxy_url = "http://127.0.0.1:7890" + + builder = MagicMock() + builder.token.return_value = builder + builder.base_url.return_value = builder + builder.base_file_url.return_value = builder + builder.proxy.return_value = builder + builder.get_updates_proxy.return_value = builder + + with patch.dict( + module_globals, + {"ApplicationBuilder": MagicMock(return_value=builder)}, + ): + TelegramPlatformAdapter( + make_platform_config("telegram", telegram_proxy=proxy_url), + {}, + asyncio.Queue(), + ) + + builder.proxy.assert_called_once_with(proxy_url) + builder.get_updates_proxy.assert_called_once_with(proxy_url) From a6285df21ba26a7d4a4f8429c5ee2965eda3c849 Mon Sep 17 00:00:00 2001 From: Trainingdlu Date: Thu, 6 Aug 2026 00:51:39 +0900 Subject: [PATCH 2/2] test: cover the unset Telegram proxy path --- tests/test_telegram_adapter.py | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/tests/test_telegram_adapter.py b/tests/test_telegram_adapter.py index c4ff5e05db..f01f60bb03 100644 --- a/tests/test_telegram_adapter.py +++ b/tests/test_telegram_adapter.py @@ -492,3 +492,28 @@ async def test_telegram_proxy_is_applied_to_bot_api_and_polling_requests(): builder.proxy.assert_called_once_with(proxy_url) builder.get_updates_proxy.assert_called_once_with(proxy_url) + + +@pytest.mark.asyncio +@pytest.mark.parametrize("telegram_proxy", [None, ""]) +async def test_telegram_proxy_is_skipped_when_not_configured(telegram_proxy): + TelegramPlatformAdapter = _load_telegram_adapter() + module_globals = TelegramPlatformAdapter.__init__.__globals__ + + builder = MagicMock() + builder.token.return_value = builder + builder.base_url.return_value = builder + builder.base_file_url.return_value = builder + + with patch.dict( + module_globals, + {"ApplicationBuilder": MagicMock(return_value=builder)}, + ): + TelegramPlatformAdapter( + make_platform_config("telegram", telegram_proxy=telegram_proxy), + {}, + asyncio.Queue(), + ) + + builder.proxy.assert_not_called() + builder.get_updates_proxy.assert_not_called()