From d7c3a77b938d0fcc74a6bbd8aa43cb05596604a5 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Mon, 27 Apr 2026 20:49:48 +0100 Subject: [PATCH] fix(telegram): skip polling webhook probe --- CHANGELOG.md | 1 + extensions/telegram/src/channel.ts | 2 ++ extensions/telegram/src/probe.test.ts | 13 +++++++- extensions/telegram/src/probe.ts | 44 +++++++++++++++------------ 4 files changed, 39 insertions(+), 21 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b246b456f33..a18b4fe8d74 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ Docs: https://docs.openclaw.ai ### Fixes +- Channels/Telegram: skip the optional webhook-info API call during polling-mode status checks and startup bot-label probes so long-polling setups avoid an unnecessary Telegram round trip. Carries forward #72990. Thanks @danielgruneberg. - Sessions: ignore future-dated session activity timestamps during reset freshness checks and cap future `updatedAt` values at the merge boundary so clock-skewed messages cannot keep stale sessions alive forever. Fixes #72989. Thanks @martingarramon. - Plugins/CLI: allow managed plugin installs when the active extensions root is a symlink to a real state directory, while keeping nested target symlinks blocked and suppressing misleading hook-pack fallback errors for install-boundary failures. Fixes #72946. Thanks @mayank6136. - Gateway/startup: keep hot Gateway boot paths on leaf config imports and add max-RSS reporting to the gateway startup bench so low-memory startup regressions are visible before release. Thanks @vincentkoc. diff --git a/extensions/telegram/src/channel.ts b/extensions/telegram/src/channel.ts index c314889c07c..171211dad0f 100644 --- a/extensions/telegram/src/channel.ts +++ b/extensions/telegram/src/channel.ts @@ -769,6 +769,7 @@ export const telegramPlugin = createChatChannelPlugin({ proxyUrl: account.config.proxy, network: account.config.network, apiRoot: account.config.apiRoot, + includeWebhookInfo: Boolean(account.config.webhookUrl), }), formatCapabilitiesProbe: ({ probe }) => { const lines = []; @@ -885,6 +886,7 @@ export const telegramPlugin = createChatChannelPlugin({ proxyUrl: account.config.proxy, network: account.config.network, apiRoot: account.config.apiRoot, + includeWebhookInfo: false, }); const username = probe.ok ? probe.bot?.username?.trim() : null; if (username) { diff --git a/extensions/telegram/src/probe.test.ts b/extensions/telegram/src/probe.test.ts index d6e23140f51..95956a699db 100644 --- a/extensions/telegram/src/probe.test.ts +++ b/extensions/telegram/src/probe.test.ts @@ -173,6 +173,18 @@ describe("probeTelegram retry logic", () => { expect(fetchMock).toHaveBeenCalledTimes(1); // Should not retry }); + it("can skip webhook info when caller only needs bot identity", async () => { + const fetchMock = installFetchMock(); + mockGetMeSuccess(fetchMock); + + const result = await probeTelegram(token, timeoutMs, { includeWebhookInfo: false }); + + expect(result.ok).toBe(true); + expect(result.webhook).toBeUndefined(); + expect(fetchMock).toHaveBeenCalledTimes(1); + expect(fetchMock.mock.calls[0]?.[0]).toBe("https://api.telegram.org/bottest-token/getMe"); + }); + it("uses resolver-scoped Telegram fetch with probe network options", async () => { const fetchMock = installFetchMock(); mockGetMeSuccess(fetchMock); @@ -192,7 +204,6 @@ describe("probeTelegram retry logic", () => { autoSelectFamily: false, dnsResultOrder: "ipv4first", }, - apiRoot: undefined, }); }); diff --git a/extensions/telegram/src/probe.ts b/extensions/telegram/src/probe.ts index 32382b77a89..444a2f67026 100644 --- a/extensions/telegram/src/probe.ts +++ b/extensions/telegram/src/probe.ts @@ -23,6 +23,7 @@ export type TelegramProbeOptions = { network?: TelegramNetworkConfig; accountId?: string; apiRoot?: string; + includeWebhookInfo?: boolean; }; const probeFetcherCache = new Map(); @@ -102,6 +103,7 @@ export async function probeTelegram( const timeoutBudgetMs = Math.max(1, Math.floor(timeoutMs)); const deadlineMs = started + timeoutBudgetMs; const options = resolveProbeOptions(proxyOrOptions); + const includeWebhookInfo = options?.includeWebhookInfo !== false; const fetcher = resolveProbeFetcher(token, options); const apiBase = resolveTelegramApiBase(options?.apiRoot); const base = `${apiBase}/bot${token}`; @@ -184,29 +186,31 @@ export async function probeTelegram( : null, }; - // Try to fetch webhook info, but don't fail health if it errors. - try { - const webhookRemainingBudgetMs = resolveRemainingBudgetMs(); - if (webhookRemainingBudgetMs > 0) { - const webhookRes = await fetchWithTimeout( - `${base}/getWebhookInfo`, - {}, - Math.max(1, Math.min(timeoutBudgetMs, webhookRemainingBudgetMs)), - fetcher, - ); - const webhookJson = (await webhookRes.json()) as { - ok?: boolean; - result?: { url?: string; has_custom_certificate?: boolean }; - }; - if (webhookRes.ok && webhookJson?.ok) { - result.webhook = { - url: webhookJson.result?.url ?? null, - hasCustomCert: webhookJson.result?.has_custom_certificate ?? null, + if (includeWebhookInfo) { + // Try to fetch webhook info, but don't fail health if it errors. + try { + const webhookRemainingBudgetMs = resolveRemainingBudgetMs(); + if (webhookRemainingBudgetMs > 0) { + const webhookRes = await fetchWithTimeout( + `${base}/getWebhookInfo`, + {}, + Math.max(1, Math.min(timeoutBudgetMs, webhookRemainingBudgetMs)), + fetcher, + ); + const webhookJson = (await webhookRes.json()) as { + ok?: boolean; + result?: { url?: string; has_custom_certificate?: boolean }; }; + if (webhookRes.ok && webhookJson?.ok) { + result.webhook = { + url: webhookJson.result?.url ?? null, + hasCustomCert: webhookJson.result?.has_custom_certificate ?? null, + }; + } } + } catch { + // ignore webhook errors for probe } - } catch { - // ignore webhook errors for probe } result.ok = true;