mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 05:30:42 +00:00
fix: keep telegram polling timeout above long poll
This commit is contained in:
@@ -27,6 +27,7 @@ Docs: https://docs.openclaw.ai
|
||||
- Plugins/runtime-deps: always write a dependency map in generated runtime-deps install manifests, so npm does not crash or prune staged bundled-plugin packages when the plan is empty. Fixes #74949. Thanks @hclsys.
|
||||
- Telegram: use durable message edits for streaming previews instead of native draft state, so generated replies no longer flicker through draft-to-message transitions that look like duplicates. (#75073) Thanks @obviyus.
|
||||
- Telegram: echo preflighted DM voice-note transcripts back to the originating chat, including Telegram DM topic thread metadata, instead of only echoing later media-understanding transcripts. Fixes #75084. Thanks @M-Lietz.
|
||||
- Telegram: clamp low long-polling client timeouts so configured `timeoutSeconds` values below the `getUpdates` poll window no longer force a fresh HTTPS connection every few seconds. Fixes #75114. Thanks @hpinho77.
|
||||
- Web search: describe `web_search` as using the configured provider instead of hard-coding Brave when DuckDuckGo or another provider is active. Fixes #75088. Thanks @sun-rongyang.
|
||||
- Infra/tmp: tolerate concurrent temp-dir permission repairs by rechecking directories that another process already tightened, so parallel ACP subprocess startup no longer throws `Unsafe fallback OpenClaw temp dir`. Fixes #66867. Thanks @Kane808-AI and @jarvisz8.
|
||||
- Agents/compaction: add an opt-in `agents.defaults.compaction.midTurnPrecheck` mid-turn precheck that detects tool-loop context pressure and triggers compaction before the next tool call instead of waiting for end-of-turn. (#73499) Thanks @marchpure and @haoxingjun.
|
||||
|
||||
@@ -724,7 +724,7 @@ curl "https://api.telegram.org/bot<bot_token>/getUpdates"
|
||||
- `channels.telegram.textChunkLimit` default is 4000.
|
||||
- `channels.telegram.chunkMode="newline"` prefers paragraph boundaries (blank lines) before length splitting.
|
||||
- `channels.telegram.mediaMaxMb` (default 100) caps inbound and outbound Telegram media size.
|
||||
- `channels.telegram.timeoutSeconds` overrides Telegram API client timeout (if unset, grammY default applies).
|
||||
- `channels.telegram.timeoutSeconds` overrides Telegram API client timeout (if unset, grammY default applies). Long-polling bot clients clamp configured values below the 45-second `getUpdates` request guard so idle polls are not aborted before the 30-second poll window completes.
|
||||
- `channels.telegram.pollingStallThresholdMs` defaults to `120000`; tune between `30000` and `600000` only for false-positive polling-stall restarts.
|
||||
- group context history uses `channels.telegram.historyLimit` or `messages.groupChat.historyLimit` (default 50); `0` disables.
|
||||
- reply/quote/forward supplemental context is currently passed as received.
|
||||
@@ -864,6 +864,7 @@ Per-account, per-group, and per-topic overrides are supported (same inheritance
|
||||
- Node 22+ + custom fetch/proxy can trigger immediate abort behavior if AbortSignal types mismatch.
|
||||
- Some hosts resolve `api.telegram.org` to IPv6 first; broken IPv6 egress can cause intermittent Telegram API failures.
|
||||
- If logs include `TypeError: fetch failed` or `Network request for 'getUpdates' failed!`, OpenClaw now retries these as recoverable network errors.
|
||||
- If Telegram sockets recycle on a short fixed cadence, check for a low `channels.telegram.timeoutSeconds`; long-polling bot clients clamp configured values below the `getUpdates` request guard, but older releases could abort every poll when this was set below the long-poll timeout.
|
||||
- If logs include `Polling stall detected`, OpenClaw restarts polling and rebuilds the Telegram transport after 120 seconds without completed long-poll liveness by default.
|
||||
- `openclaw channels status --probe` and `openclaw doctor` warn when a running polling account has not completed `getUpdates` after startup grace, when a running webhook account has not completed `setWebhook` after startup grace, or when the last successful polling transport activity is stale.
|
||||
- Increase `channels.telegram.pollingStallThresholdMs` only when long-running `getUpdates` calls are healthy but your host still reports false polling-stall restarts. Persistent stalls usually point to proxy, DNS, IPv6, or TLS egress issues between the host and `api.telegram.org`.
|
||||
|
||||
@@ -135,11 +135,25 @@ const TELEGRAM_TIMEOUT_FALLBACK_METHODS = new Set([
|
||||
"setmycommands",
|
||||
"setwebhook",
|
||||
]);
|
||||
|
||||
function shouldRetryTimedOutTelegramControlRequest(method: string | null): boolean {
|
||||
return method !== null && TELEGRAM_TIMEOUT_FALLBACK_METHODS.has(method);
|
||||
}
|
||||
|
||||
function resolveTelegramClientTimeoutSeconds(params: {
|
||||
value: unknown;
|
||||
minimum?: number;
|
||||
}): number | undefined {
|
||||
const { value, minimum } = params;
|
||||
if (typeof value !== "number" || !Number.isFinite(value)) {
|
||||
return undefined;
|
||||
}
|
||||
const configured = Math.max(1, Math.floor(value));
|
||||
if (typeof minimum !== "number" || !Number.isFinite(minimum)) {
|
||||
return configured;
|
||||
}
|
||||
return Math.max(configured, Math.max(1, Math.floor(minimum)));
|
||||
}
|
||||
|
||||
export function createTelegramBotCore(
|
||||
opts: TelegramBotOptions & { telegramDeps: TelegramBotDeps },
|
||||
): TelegramBotInstance {
|
||||
@@ -298,10 +312,10 @@ export function createTelegramBotCore(
|
||||
};
|
||||
}
|
||||
|
||||
const timeoutSeconds =
|
||||
typeof telegramCfg?.timeoutSeconds === "number" && Number.isFinite(telegramCfg.timeoutSeconds)
|
||||
? Math.max(1, Math.floor(telegramCfg.timeoutSeconds))
|
||||
: undefined;
|
||||
const timeoutSeconds = resolveTelegramClientTimeoutSeconds({
|
||||
value: telegramCfg?.timeoutSeconds,
|
||||
minimum: opts.minimumClientTimeoutSeconds,
|
||||
});
|
||||
const apiRoot = normalizeOptionalString(telegramCfg.apiRoot);
|
||||
const normalizedApiRoot = apiRoot ? normalizeTelegramApiRoot(apiRoot) : undefined;
|
||||
const client: ApiClientOptions | undefined =
|
||||
|
||||
@@ -248,6 +248,36 @@ describe("createTelegramBot", () => {
|
||||
);
|
||||
});
|
||||
|
||||
it("honors low timeoutSeconds when no polling floor is requested", () => {
|
||||
loadConfig.mockReturnValue({
|
||||
channels: {
|
||||
telegram: { dmPolicy: "open", allowFrom: ["*"], timeoutSeconds: 10 },
|
||||
},
|
||||
});
|
||||
createTelegramBot({ token: "tok" });
|
||||
expect(botCtorSpy).toHaveBeenCalledWith(
|
||||
"tok",
|
||||
expect.objectContaining({
|
||||
client: expect.objectContaining({ timeoutSeconds: 10 }),
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it("keeps polling client timeout above the getUpdates request guard", () => {
|
||||
loadConfig.mockReturnValue({
|
||||
channels: {
|
||||
telegram: { dmPolicy: "open", allowFrom: ["*"], timeoutSeconds: 10 },
|
||||
},
|
||||
});
|
||||
createTelegramBot({ token: "tok", minimumClientTimeoutSeconds: 45 });
|
||||
expect(botCtorSpy).toHaveBeenCalledWith(
|
||||
"tok",
|
||||
expect.objectContaining({
|
||||
client: expect.objectContaining({ timeoutSeconds: 45 }),
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it("normalizes full Telegram bot endpoint apiRoot before passing it to grammY", () => {
|
||||
loadConfig.mockReturnValue({
|
||||
channels: {
|
||||
|
||||
@@ -16,6 +16,8 @@ export type TelegramBotOptions = {
|
||||
config?: OpenClawConfig;
|
||||
/** Signal to abort in-flight Telegram API fetch requests (e.g. getUpdates) on shutdown. */
|
||||
fetchAbortSignal?: AbortSignal;
|
||||
/** Minimum grammY client timeout when timeoutSeconds is configured on long-polling bots. */
|
||||
minimumClientTimeoutSeconds?: number;
|
||||
updateOffset?: {
|
||||
lastUpdateId?: number | null;
|
||||
onUpdateId?: (updateId: number) => void | Promise<void>;
|
||||
|
||||
@@ -276,6 +276,9 @@ describe("TelegramPollingSession", () => {
|
||||
await session.runUntilAbort();
|
||||
|
||||
expect(runMock).toHaveBeenCalledTimes(2);
|
||||
expect(createTelegramBotMock).toHaveBeenCalledWith(
|
||||
expect.objectContaining({ minimumClientTimeoutSeconds: 45 }),
|
||||
);
|
||||
expect(computeBackoffMock).toHaveBeenCalledTimes(1);
|
||||
expect(sleepWithAbortMock).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
@@ -14,6 +14,7 @@ import { isRecoverableTelegramNetworkError } from "./network-errors.js";
|
||||
import { TelegramPollingLivenessTracker } from "./polling-liveness.js";
|
||||
import { createTelegramPollingStatusPublisher } from "./polling-status.js";
|
||||
import { TelegramPollingTransportState } from "./polling-transport-state.js";
|
||||
import { TELEGRAM_GET_UPDATES_REQUEST_TIMEOUT_MS } from "./request-timeouts.js";
|
||||
|
||||
const TELEGRAM_POLL_RESTART_POLICY = {
|
||||
initialMs: 2000,
|
||||
@@ -27,6 +28,9 @@ const MIN_POLL_STALL_THRESHOLD_MS = 30_000;
|
||||
const MAX_POLL_STALL_THRESHOLD_MS = 600_000;
|
||||
const POLL_WATCHDOG_INTERVAL_MS = 30_000;
|
||||
const POLL_STOP_GRACE_MS = 15_000;
|
||||
const TELEGRAM_POLLING_CLIENT_TIMEOUT_FLOOR_SECONDS = Math.ceil(
|
||||
TELEGRAM_GET_UPDATES_REQUEST_TIMEOUT_MS / 1000,
|
||||
);
|
||||
|
||||
type TelegramBot = ReturnType<typeof createTelegramBot>;
|
||||
|
||||
@@ -184,6 +188,7 @@ export class TelegramPollingSession {
|
||||
config: this.opts.config,
|
||||
accountId: this.opts.accountId,
|
||||
fetchAbortSignal: fetchAbortController.signal,
|
||||
minimumClientTimeoutSeconds: TELEGRAM_POLLING_CLIENT_TIMEOUT_FLOOR_SECONDS,
|
||||
updateOffset: {
|
||||
lastUpdateId: this.opts.getLastUpdateId(),
|
||||
onUpdateId: this.opts.persistUpdateId,
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
export const TELEGRAM_GET_UPDATES_REQUEST_TIMEOUT_MS = 45_000;
|
||||
|
||||
const TELEGRAM_REQUEST_TIMEOUTS_MS = {
|
||||
// Bound startup/control-plane calls so the gateway cannot report Telegram as
|
||||
// healthy while provider startup is still hung on Bot API setup.
|
||||
@@ -9,7 +11,7 @@ const TELEGRAM_REQUEST_TIMEOUTS_MS = {
|
||||
getchat: 15_000,
|
||||
getfile: 30_000,
|
||||
getme: 15_000,
|
||||
getupdates: 45_000,
|
||||
getupdates: TELEGRAM_GET_UPDATES_REQUEST_TIMEOUT_MS,
|
||||
pinchatmessage: 15_000,
|
||||
sendanimation: 30_000,
|
||||
sendaudio: 30_000,
|
||||
|
||||
Reference in New Issue
Block a user