import type { AgentToolResult } from "@mariozechner/pi-agent-core"; import type { ClawdbotConfig } from "../../config/config.js"; import { resolveProviderCapabilities } from "../../config/provider-capabilities.js"; import { reactMessageTelegram, sendMessageTelegram, } from "../../telegram/send.js"; import { resolveTelegramToken } from "../../telegram/token.js"; import { createActionGate, jsonResult, readNumberParam, readReactionParams, readStringOrNumberParam, readStringParam, } from "./common.js"; type TelegramButton = { text: string; callback_data: string; }; function hasInlineButtonsCapability(params: { cfg: ClawdbotConfig; accountId?: string | undefined; }): boolean { const caps = resolveProviderCapabilities({ cfg: params.cfg, provider: "telegram", accountId: params.accountId, }) ?? []; return caps.some((cap) => cap.toLowerCase() === "inlinebuttons"); } export function readTelegramButtons( params: Record, ): TelegramButton[][] | undefined { const raw = params.buttons; if (raw == null) return undefined; if (!Array.isArray(raw)) { throw new Error("buttons must be an array of button rows"); } const rows = raw.map((row, rowIndex) => { if (!Array.isArray(row)) { throw new Error(`buttons[${rowIndex}] must be an array`); } return row.map((button, buttonIndex) => { if (!button || typeof button !== "object") { throw new Error( `buttons[${rowIndex}][${buttonIndex}] must be an object`, ); } const text = typeof (button as { text?: unknown }).text === "string" ? (button as { text: string }).text.trim() : ""; const callbackData = typeof (button as { callback_data?: unknown }).callback_data === "string" ? (button as { callback_data: string }).callback_data.trim() : ""; if (!text || !callbackData) { throw new Error( `buttons[${rowIndex}][${buttonIndex}] requires text and callback_data`, ); } if (callbackData.length > 64) { throw new Error( `buttons[${rowIndex}][${buttonIndex}] callback_data too long (max 64 chars)`, ); } return { text, callback_data: callbackData }; }); }); const filtered = rows.filter((row) => row.length > 0); return filtered.length > 0 ? filtered : undefined; } export async function handleTelegramAction( params: Record, cfg: ClawdbotConfig, ): Promise> { const action = readStringParam(params, "action", { required: true }); const accountId = readStringParam(params, "accountId"); const isActionEnabled = createActionGate(cfg.telegram?.actions); if (action === "react") { if (!isActionEnabled("reactions")) { throw new Error("Telegram reactions are disabled."); } const chatId = readStringOrNumberParam(params, "chatId", { required: true, }); const messageId = readNumberParam(params, "messageId", { required: true, integer: true, }); const { emoji, remove, isEmpty } = readReactionParams(params, { removeErrorMessage: "Emoji is required to remove a Telegram reaction.", }); const token = resolveTelegramToken(cfg, { accountId }).token; if (!token) { throw new Error( "Telegram bot token missing. Set TELEGRAM_BOT_TOKEN or telegram.botToken.", ); } await reactMessageTelegram(chatId ?? "", messageId ?? 0, emoji ?? "", { token, remove, accountId: accountId ?? undefined, }); if (!remove && !isEmpty) { return jsonResult({ ok: true, added: emoji }); } return jsonResult({ ok: true, removed: true }); } if (action === "sendMessage") { if (!isActionEnabled("sendMessage")) { throw new Error("Telegram sendMessage is disabled."); } const to = readStringParam(params, "to", { required: true }); const content = readStringParam(params, "content", { required: true }); const mediaUrl = readStringParam(params, "mediaUrl"); const buttons = readTelegramButtons(params); if ( buttons && !hasInlineButtonsCapability({ cfg, accountId: accountId ?? undefined }) ) { throw new Error( 'Telegram inline buttons requested but not enabled. Add "inlineButtons" to telegram.capabilities (or telegram.accounts..capabilities).', ); } // Optional threading parameters for forum topics and reply chains const replyToMessageId = readNumberParam(params, "replyToMessageId", { integer: true, }); const messageThreadId = readNumberParam(params, "messageThreadId", { integer: true, }); const token = resolveTelegramToken(cfg, { accountId }).token; if (!token) { throw new Error( "Telegram bot token missing. Set TELEGRAM_BOT_TOKEN or telegram.botToken.", ); } const result = await sendMessageTelegram(to, content, { token, accountId: accountId ?? undefined, mediaUrl: mediaUrl || undefined, buttons, replyToMessageId: replyToMessageId ?? undefined, messageThreadId: messageThreadId ?? undefined, }); return jsonResult({ ok: true, messageId: result.messageId, chatId: result.chatId, }); } throw new Error(`Unsupported Telegram action: ${action}`); }