fix(telegram): retry failed pagination preflight

This commit is contained in:
Vincent Koc
2026-04-13 17:37:53 +01:00
parent 139a3f49fe
commit 1f7f8b02d0
2 changed files with 71 additions and 9 deletions

View File

@@ -1417,15 +1417,20 @@ export const registerTelegramHandlers = ({
}
const agentId = paginationMatch[2]?.trim() || resolveDefaultAgentId(runtimeCfg);
const skillCommands = telegramDeps.listSkillCommandsForAgents({
cfg: runtimeCfg,
agentIds: [agentId],
});
const result = buildCommandsMessagePaginated(runtimeCfg, skillCommands, {
page,
forcePaginatedList: true,
surface: "telegram",
});
let result: ReturnType<typeof buildCommandsMessagePaginated>;
try {
const skillCommands = telegramDeps.listSkillCommandsForAgents({
cfg: runtimeCfg,
agentIds: [agentId],
});
result = buildCommandsMessagePaginated(runtimeCfg, skillCommands, {
page,
forcePaginatedList: true,
surface: "telegram",
});
} catch (err) {
throw new TelegramRetryableCallbackError(err);
}
const keyboard =
result.totalPages > 1

View File

@@ -20,6 +20,7 @@ const {
getOnHandler,
getReadChannelAllowFromStoreMock,
getUpsertChannelPairingRequestMock,
listSkillCommandsForAgents,
makeForumGroupMessageCtx,
middlewareUseSpy,
onSpy,
@@ -2987,6 +2988,62 @@ describe("createTelegramBot", () => {
expect(editMessageTextSpy.mock.calls.at(-1)?.[2]).toContain("Commands (2/");
});
it("retries command pagination callbacks after a bubbled preflight failure", async () => {
const listSkillCommandsMock = listSkillCommandsForAgents as unknown as ReturnType<typeof vi.fn>;
listSkillCommandsMock.mockClear();
createTelegramBot({ token: "tok" });
const callbackHandler = getOnHandler("callback_query");
const middlewares = middlewareUseSpy.mock.calls
.map((call) => call[0])
.filter(
(fn): fn is (ctx: Record<string, unknown>, next: () => Promise<void>) => Promise<void> =>
typeof fn === "function",
);
const runMiddlewareChain = async (ctx: Record<string, unknown>) => {
let idx = -1;
const dispatch = async (i: number): Promise<void> => {
if (i <= idx) {
throw new Error("middleware dispatch called multiple times");
}
idx = i;
const fn = middlewares[i];
if (!fn) {
await callbackHandler(ctx);
return;
}
await fn(ctx, async () => dispatch(i + 1));
};
await dispatch(0);
};
const ctx = {
update: { update_id: 778 },
callbackQuery: {
id: "cbq-commands-retry-2",
data: "commands_page_2:main",
from: { id: 9, first_name: "Ada", username: "ada_bot" },
message: {
chat: { id: 1234, type: "private" },
date: 1736380800,
message_id: 21,
},
},
me: { username: "openclaw_bot" },
getFile: async () => ({ download: async () => new Uint8Array() }),
};
listSkillCommandsMock.mockImplementationOnce(() => {
throw new Error("commands boom");
});
await expect(runMiddlewareChain(ctx)).rejects.toThrow("commands boom");
await runMiddlewareChain(ctx);
expect(listSkillCommandsMock).toHaveBeenCalledTimes(2);
expect(editMessageTextSpy).toHaveBeenCalledTimes(1);
expect(editMessageTextSpy.mock.calls.at(-1)?.[2]).toContain("Commands (2/");
});
it("retries plugin binding approval callbacks after a bubbled resolution failure", async () => {
createTelegramBot({ token: "tok" });
const callbackHandler = getOnHandler("callback_query");