From ab143a754cd7e25f5ea7fde62a39c3f6c183a033 Mon Sep 17 00:00:00 2001 From: Vincent Koc Date: Mon, 13 Apr 2026 17:19:37 +0100 Subject: [PATCH] fix(telegram): retry failed reaction updates --- .../telegram/src/bot-handlers.runtime.ts | 1 + .../src/bot.create-telegram-bot.test.ts | 55 +++++++++++++++++++ 2 files changed, 56 insertions(+) diff --git a/extensions/telegram/src/bot-handlers.runtime.ts b/extensions/telegram/src/bot-handlers.runtime.ts index a79bba721e9..132da683c00 100644 --- a/extensions/telegram/src/bot-handlers.runtime.ts +++ b/extensions/telegram/src/bot-handlers.runtime.ts @@ -899,6 +899,7 @@ export const registerTelegramHandlers = ({ } } catch (err) { runtime.error?.(danger(`telegram reaction handler failed: ${String(err)}`)); + throw err; } }); const processInboundMessage = async (params: { diff --git a/extensions/telegram/src/bot.create-telegram-bot.test.ts b/extensions/telegram/src/bot.create-telegram-bot.test.ts index d36305fdd13..8c2efddbda7 100644 --- a/extensions/telegram/src/bot.create-telegram-bot.test.ts +++ b/extensions/telegram/src/bot.create-telegram-bot.test.ts @@ -9,6 +9,7 @@ const { botCtorSpy, commandSpy, dispatchReplyWithBufferedBlockDispatcher, + enqueueSystemEventSpy, getLoadWebMediaMock, getChatSpy, getLoadConfigMock, @@ -2804,4 +2805,58 @@ describe("createTelegramBot", () => { expect(loadConfigCallsAfterFailure).toBe(loadConfigCallsBeforeRetry + 1); expect(loadConfig.mock.calls.length).toBeGreaterThan(loadConfigCallsAfterFailure); }); + + it("retries reaction updates after a bubbled enqueue failure", async () => { + loadConfig.mockReturnValue({ + channels: { + telegram: { dmPolicy: "open", reactionNotifications: "all" }, + }, + }); + + createTelegramBot({ token: "tok" }); + const reactionHandler = getOnHandler("message_reaction"); + const middlewares = middlewareUseSpy.mock.calls + .map((call) => call[0]) + .filter( + (fn): fn is (ctx: Record, next: () => Promise) => Promise => + typeof fn === "function", + ); + const runMiddlewareChain = async (ctx: Record) => { + let idx = -1; + const dispatch = async (i: number): Promise => { + if (i <= idx) { + throw new Error("middleware dispatch called multiple times"); + } + idx = i; + const fn = middlewares[i]; + if (!fn) { + await reactionHandler(ctx); + return; + } + await fn(ctx, async () => dispatch(i + 1)); + }; + await dispatch(0); + }; + + const ctx = { + update: { update_id: 555 }, + messageReaction: { + chat: { id: 1234, type: "private" }, + message_id: 42, + user: { id: 9, first_name: "Ada", username: "ada_bot" }, + date: 1736380800, + old_reaction: [], + new_reaction: [{ type: "emoji", emoji: "\u{1F44D}" }], + }, + }; + + enqueueSystemEventSpy.mockImplementationOnce(() => { + throw new Error("queue boom"); + }); + await expect(runMiddlewareChain(ctx)).rejects.toThrow("queue boom"); + await runMiddlewareChain(ctx); + + expect(enqueueSystemEventSpy).toHaveBeenCalledTimes(2); + expect(enqueueSystemEventSpy.mock.calls.at(-1)?.[0]).toContain("Telegram reaction added:"); + }); });