diff --git a/extensions/telegram/src/polling-lease.test.ts b/extensions/telegram/src/polling-lease.test.ts index d56b2f4b1fa..fc95743c3cf 100644 --- a/extensions/telegram/src/polling-lease.test.ts +++ b/extensions/telegram/src/polling-lease.test.ts @@ -1,3 +1,4 @@ +import { MAX_TIMER_TIMEOUT_MS } from "openclaw/plugin-sdk/number-runtime"; import { beforeEach, describe, expect, it, vi } from "vitest"; import { acquireTelegramPollingLease, @@ -101,6 +102,33 @@ describe("Telegram polling lease", () => { } }); + it("caps oversized duplicate-poller wait timers before scheduling", async () => { + vi.useFakeTimers(); + try { + const oldAbort = new AbortController(); + const first = await acquireTelegramPollingLease({ + token: "123:abc", + accountId: "old", + abortSignal: oldAbort.signal, + }); + oldAbort.abort(); + const timeoutSpy = vi.spyOn(globalThis, "setTimeout"); + + void acquireTelegramPollingLease({ + token: "123:abc", + accountId: "new", + waitMs: Number.MAX_SAFE_INTEGER, + }).catch(() => undefined); + await Promise.resolve(); + + expect(timeoutSpy).toHaveBeenCalledWith(expect.any(Function), MAX_TIMER_TIMEOUT_MS); + first.release(); + } finally { + vi.useRealTimers(); + vi.restoreAllMocks(); + } + }); + it("does not release a no-signal active lease", async () => { const first = await acquireTelegramPollingLease({ token: "123:abc", diff --git a/extensions/telegram/src/polling-lease.ts b/extensions/telegram/src/polling-lease.ts index 9e853b576ee..4669cea0bc3 100644 --- a/extensions/telegram/src/polling-lease.ts +++ b/extensions/telegram/src/polling-lease.ts @@ -1,3 +1,4 @@ +import { resolveTimerTimeoutMs } from "openclaw/plugin-sdk/number-runtime"; import { fingerprintTelegramBotToken } from "./token-fingerprint.js"; const TELEGRAM_POLLING_LEASES_KEY = Symbol.for("openclaw.telegram.pollingLeases"); @@ -71,8 +72,9 @@ async function waitForPreviousRelease(params: { let timer: ReturnType | undefined; let abortListener: (() => void) | undefined; try { + const waitMs = resolveTimerTimeoutMs(params.waitMs, DEFAULT_TELEGRAM_POLLING_LEASE_WAIT_MS, 0); const timeout = new Promise<"timeout">((resolve) => { - timer = setTimeout(() => resolve("timeout"), Math.max(0, params.waitMs)); + timer = setTimeout(() => resolve("timeout"), waitMs); timer.unref?.(); }); const aborted = new Promise<"aborted">((resolve) => {