refactor: unify telegram interactive button resolution

This commit is contained in:
Peter Steinberger 2026-03-16 05:37:12 +00:00
parent ff558862f0
commit ecaafb6a4f
4 changed files with 93 additions and 9 deletions

View File

@ -0,0 +1,69 @@
import { describe, expect, it } from "vitest";
import { buildTelegramInteractiveButtons, resolveTelegramInlineButtons } from "./button-types.js";
describe("buildTelegramInteractiveButtons", () => {
it("maps shared buttons and selects into Telegram inline rows", () => {
expect(
buildTelegramInteractiveButtons({
blocks: [
{
type: "buttons",
buttons: [
{ label: "Approve", value: "approve", style: "success" },
{ label: "Reject", value: "reject", style: "danger" },
{ label: "Later", value: "later" },
{ label: "Archive", value: "archive" },
],
},
{
type: "select",
options: [{ label: "Alpha", value: "alpha" }],
},
],
}),
).toEqual([
[
{ text: "Approve", callback_data: "approve", style: "success" },
{ text: "Reject", callback_data: "reject", style: "danger" },
{ text: "Later", callback_data: "later", style: undefined },
],
[{ text: "Archive", callback_data: "archive", style: undefined }],
[{ text: "Alpha", callback_data: "alpha", style: undefined }],
]);
});
});
describe("resolveTelegramInlineButtons", () => {
it("prefers explicit buttons over shared interactive blocks", () => {
const explicit = [[{ text: "Keep", callback_data: "keep" }]] as const;
expect(
resolveTelegramInlineButtons({
buttons: explicit,
interactive: {
blocks: [
{
type: "buttons",
buttons: [{ label: "Override", value: "override" }],
},
],
},
}),
).toBe(explicit);
});
it("derives buttons from raw interactive payloads", () => {
expect(
resolveTelegramInlineButtons({
interactive: {
blocks: [
{
type: "buttons",
buttons: [{ label: "Retry", value: "retry", style: "primary" }],
},
],
},
}),
).toEqual([[{ text: "Retry", callback_data: "retry", style: "primary" }]]);
});
});

View File

@ -1,5 +1,9 @@
import { reduceInteractiveReply } from "../../../src/channels/plugins/outbound/interactive.js";
import type { InteractiveReply, InteractiveReplyButton } from "../../../src/interactive/payload.js";
import {
normalizeInteractiveReply,
type InteractiveReply,
type InteractiveReplyButton,
} from "../../../src/interactive/payload.js";
export type TelegramButtonStyle = "danger" | "success" | "primary";
@ -60,3 +64,12 @@ export function buildTelegramInteractiveButtons(
);
return rows.length > 0 ? rows : undefined;
}
export function resolveTelegramInlineButtons(params: {
buttons?: TelegramInlineButtons;
interactive?: unknown;
}): TelegramInlineButtons | undefined {
return (
params.buttons ?? buildTelegramInteractiveButtons(normalizeInteractiveReply(params.interactive))
);
}

View File

@ -15,7 +15,6 @@ import type {
ChannelMessageActionName,
} from "../../../src/channels/plugins/types.js";
import type { TelegramActionConfig } from "../../../src/config/types.telegram.js";
import { normalizeInteractiveReply } from "../../../src/interactive/payload.js";
import { readBooleanParam } from "../../../src/plugin-sdk/boolean-param.js";
import { extractToolSend } from "../../../src/plugin-sdk/tool-send.js";
import { resolveTelegramPollVisibility } from "../../../src/poll-params.js";
@ -24,7 +23,7 @@ import {
listEnabledTelegramAccounts,
resolveTelegramPollActionGateState,
} from "./accounts.js";
import { buildTelegramInteractiveButtons } from "./button-types.js";
import { resolveTelegramInlineButtons } from "./button-types.js";
import { isTelegramInlineButtonsEnabled } from "./inline-buttons.js";
const providerId = "telegram";
@ -32,9 +31,10 @@ const providerId = "telegram";
function readTelegramSendParams(params: Record<string, unknown>) {
const to = readStringParam(params, "to", { required: true });
const mediaUrl = readStringParam(params, "media", { trim: false });
const buttons =
params.buttons ??
buildTelegramInteractiveButtons(normalizeInteractiveReply(params.interactive));
const buttons = resolveTelegramInlineButtons({
buttons: params.buttons as ReturnType<typeof resolveTelegramInlineButtons>,
interactive: params.interactive,
});
const hasButtons = Array.isArray(buttons) && buttons.length > 0;
const message = readStringParam(params, "message", {
required: !mediaUrl && !hasButtons,

View File

@ -10,7 +10,7 @@ import {
} from "../../../src/infra/outbound/send-deps.js";
import { resolveInteractiveTextFallback } from "../../../src/interactive/payload.js";
import type { TelegramInlineButtons } from "./button-types.js";
import { buildTelegramInteractiveButtons } from "./button-types.js";
import { resolveTelegramInlineButtons } from "./button-types.js";
import { markdownToTelegramHtmlChunks } from "./format.js";
import { parseTelegramReplyToMessageId, parseTelegramThreadId } from "./outbound-params.js";
import { sendMessageTelegram } from "./send.js";
@ -67,8 +67,10 @@ export async function sendTelegramPayloadMessages(params: {
interactive: params.payload.interactive,
}) ?? "";
const mediaUrls = resolvePayloadMediaUrls(params.payload);
const interactiveButtons = buildTelegramInteractiveButtons(params.payload.interactive);
const buttons = telegramData?.buttons ?? interactiveButtons;
const buttons = resolveTelegramInlineButtons({
buttons: telegramData?.buttons,
interactive: params.payload.interactive,
});
const payloadOpts = {
...params.baseOpts,
quoteText,