From e50545d7674467baee955e0babb8c603a8a778e2 Mon Sep 17 00:00:00 2001 From: Vincent Koc Date: Sun, 15 Mar 2026 18:44:09 -0700 Subject: [PATCH] Telegram: add shared interactive renderer --- extensions/telegram/src/shared-interactive.ts | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 extensions/telegram/src/shared-interactive.ts diff --git a/extensions/telegram/src/shared-interactive.ts b/extensions/telegram/src/shared-interactive.ts new file mode 100644 index 00000000000..bb346e34539 --- /dev/null +++ b/extensions/telegram/src/shared-interactive.ts @@ -0,0 +1,48 @@ +import type { InteractiveReply, InteractiveReplyButton } from "../../../src/interactive/payload.js"; +import type { TelegramInlineButton, TelegramInlineButtons } from "./button-types.js"; + +const TELEGRAM_INTERACTIVE_ROW_SIZE = 3; + +function toTelegramButtonStyle( + style?: InteractiveReplyButton["style"], +): TelegramInlineButton["style"] { + return style === "danger" || style === "success" || style === "primary" ? style : undefined; +} + +function chunkInteractiveButtons( + buttons: readonly InteractiveReplyButton[], + rows: TelegramInlineButton[][], +) { + for (let i = 0; i < buttons.length; i += TELEGRAM_INTERACTIVE_ROW_SIZE) { + const row = buttons.slice(i, i + TELEGRAM_INTERACTIVE_ROW_SIZE).map((button) => ({ + text: button.label, + callback_data: button.value, + style: toTelegramButtonStyle(button.style), + })); + if (row.length > 0) { + rows.push(row); + } + } +} + +export function buildTelegramInteractiveButtons( + interactive?: InteractiveReply, +): TelegramInlineButtons | undefined { + const rows: TelegramInlineButton[][] = []; + for (const block of interactive?.blocks ?? []) { + if (block.type === "buttons") { + chunkInteractiveButtons(block.buttons, rows); + continue; + } + if (block.type === "select") { + chunkInteractiveButtons( + block.options.map((option) => ({ + label: option.label, + value: option.value, + })), + rows, + ); + } + } + return rows.length > 0 ? rows : undefined; +}