From 6f61184d1e8a783c60ad767afeef420ca8d882ce Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Tue, 25 Aug 2026 17:36:11 -0700 Subject: [PATCH] fix(telegram): support polls lasting up to seven days (#129461) --- docs/channels/telegram.md | 2 +- docs/cli/message.md | 2 +- extensions/telegram/src/send-special.ts | 6 +-- extensions/telegram/src/send.test.ts | 67 +++++++++++++++--------- src/cli/program/message/register.poll.ts | 2 +- 5 files changed, 48 insertions(+), 31 deletions(-) diff --git a/docs/channels/telegram.md b/docs/channels/telegram.md index a792e8161d76..7bcb42600284 100644 --- a/docs/channels/telegram.md +++ b/docs/channels/telegram.md @@ -849,7 +849,7 @@ openclaw message poll --channel telegram --target -1001234567890:topic:42 \ --poll-duration-seconds 300 --poll-public ``` - Telegram-only poll flags: `--poll-duration-seconds` (5-600), `--poll-anonymous`, `--poll-public`, `--thread-id` (or a `:topic:` target). `--poll-option` repeats 2-12 times (Telegram's option cap). + Telegram-only poll flags: `--poll-duration-seconds` (5-604800; up to seven days), `--poll-anonymous`, `--poll-public`, `--thread-id` (or a `:topic:` target). `--poll-option` repeats 2-12 times (Telegram's option cap). Telegram send also supports `--presentation` with `buttons` blocks for inline keyboards (when `channels.telegram.capabilities.inlineButtons` allows it), `--pin` or `--delivery '{"pin":true}'` to request pinned delivery when the bot can pin in that chat, and `--force-document` to send outbound images, GIFs, and videos as documents instead of compressed/animated/video uploads. diff --git a/docs/cli/message.md b/docs/cli/message.md index e4ec6978357a..23a0e2ffb623 100644 --- a/docs/cli/message.md +++ b/docs/cli/message.md @@ -168,7 +168,7 @@ openclaw message poll --channel discord \ - `--poll-option `: repeat 2-12 times. - `--poll-multi`: allow multiple selections. - Discord: `--poll-duration-hours`, `--silent`, `--message`. -- Telegram: `--poll-duration-seconds ` (5-600), `--silent`, +- Telegram: `--poll-duration-seconds ` (5-604800; up to seven days), `--silent`, `--poll-anonymous` / `--poll-public`, `--thread-id`. ```bash diff --git a/extensions/telegram/src/send-special.ts b/extensions/telegram/src/send-special.ts index 9e1924222e43..f022fb769020 100644 --- a/extensions/telegram/src/send-special.ts +++ b/extensions/telegram/src/send-special.ts @@ -148,11 +148,11 @@ async function sendPollTelegramWithContext( const durationSeconds = normalizedPoll.durationSeconds; if (durationSeconds === undefined && normalizedPoll.durationHours !== undefined) { throw new Error( - "Telegram poll durationHours is not supported. Use durationSeconds (5-600) instead.", + "Telegram poll durationHours is not supported. Use durationSeconds (5-604800) instead.", ); } - if (durationSeconds !== undefined && (durationSeconds < 5 || durationSeconds > 600)) { - throw new Error("Telegram poll durationSeconds must be between 5 and 600"); + if (durationSeconds !== undefined && (durationSeconds < 5 || durationSeconds > 604_800)) { + throw new Error("Telegram poll durationSeconds must be between 5 and 604800"); } const pollParams: TelegramSendPollParams = { diff --git a/extensions/telegram/src/send.test.ts b/extensions/telegram/src/send.test.ts index 431ccfd69970..98ddd08e60ac 100644 --- a/extensions/telegram/src/send.test.ts +++ b/extensions/telegram/src/send.test.ts @@ -5779,33 +5779,50 @@ describe("sendPollTelegram", () => { }); }); - it("maps durationSeconds to open_period", async () => { - const api = makeTelegramApiTestMock({ - sendPoll: vi.fn(async () => - makeTelegramPollMessage({ poll: { id: "p1" } }), + it.each([5, 600, 601, 3_600, 86_400, 604_800])( + "maps supported poll duration %i seconds to open_period", + async (durationSeconds) => { + const api = makeTelegramApiTestMock({ + sendPoll: vi.fn(async () => + makeTelegramPollMessage({ poll: { id: "p1" } }), + ), + }); + + const res = await sendPollTelegram( + "123", + { question: " Q ", options: [" A ", "B "], durationSeconds }, + { cfg: TELEGRAM_TEST_CFG, token: "t", api }, + ); + + expect(res).toMatchObject({ + messageId: "123", + chatId: "555", + pollId: "p1", + pollAnswerRouting: "unavailable", + }); + expect(api.sendPoll).toHaveBeenCalledTimes(1); + const sendPollMock = api.sendPoll as ReturnType; + const sendPollCall = firstMockCall(sendPollMock, "send poll call"); + expect(sendPollCall[0]).toBe("123"); + expect(sendPollCall[1]).toBe("Q"); + expect(sendPollCall[2]).toEqual(["A", "B"]); + expect(requireRecord(sendPollCall[3], "send poll params").open_period).toBe(durationSeconds); + expect(wasSentByBot("123", 123)).toBe(true); + }, + ); + + it.each([4, 604_801])("rejects unsupported poll duration %i seconds", async (durationSeconds) => { + const api = makeTelegramApiTestMock({ sendPoll: vi.fn() }); + + await expect( + sendPollTelegram( + "123", + { question: "Q", options: ["A", "B"], durationSeconds }, + { cfg: TELEGRAM_TEST_CFG, token: "t", api }, ), - }); + ).rejects.toThrow("Telegram poll durationSeconds must be between 5 and 604800"); - const res = await sendPollTelegram( - "123", - { question: " Q ", options: [" A ", "B "], durationSeconds: 60 }, - { cfg: TELEGRAM_TEST_CFG, token: "t", api }, - ); - - expect(res).toMatchObject({ - messageId: "123", - chatId: "555", - pollId: "p1", - pollAnswerRouting: "unavailable", - }); - expect(api.sendPoll).toHaveBeenCalledTimes(1); - const sendPollMock = api.sendPoll as ReturnType; - const sendPollCall = firstMockCall(sendPollMock, "send poll call"); - expect(sendPollCall[0]).toBe("123"); - expect(sendPollCall[1]).toBe("Q"); - expect(sendPollCall[2]).toEqual(["A", "B"]); - expect(requireRecord(sendPollCall[3], "send poll params").open_period).toBe(60); - expect(wasSentByBot("123", 123)).toBe(true); + expect(api.sendPoll).not.toHaveBeenCalled(); }); it("records a public poll origin with its resolved topic", async () => { diff --git a/src/cli/program/message/register.poll.ts b/src/cli/program/message/register.poll.ts index 756bf576bd5c..0aabdc4c1378 100644 --- a/src/cli/program/message/register.poll.ts +++ b/src/cli/program/message/register.poll.ts @@ -18,7 +18,7 @@ export function registerMessagePollCommand(message: Command, helpers: MessageCli ) .option("--poll-multi", "Allow multiple selections", false) .option("--poll-duration-hours ", "Poll duration in hours (Discord)") - .option("--poll-duration-seconds ", "Poll duration in seconds (Telegram; 5-600)") + .option("--poll-duration-seconds ", "Poll duration in seconds (Telegram; 5-604800)") .option("--poll-anonymous", "Send an anonymous poll (Telegram)", false) .option("--poll-public", "Send a non-anonymous poll (Telegram)", false) .option("-m, --message ", "Optional message body")