From 83a25d26fc76d32f2b378247657a16173152b4ba Mon Sep 17 00:00:00 2001 From: sleontenko Date: Wed, 14 Jan 2026 22:20:17 +0200 Subject: [PATCH] feat(telegram): add deleteMessage action Add ability to delete messages in Telegram chats via the message tool. Changes: - Add deleteMessageTelegram function in send.ts - Add deleteMessage action handler in telegram-actions.ts - Add delete action support in telegram message plugin adapter - Add deleteMessage to TelegramActionConfig type - Update message tool description to mention delete action Usage: - Via message tool: action="delete", chatId, messageId - Can be disabled via channels.telegram.actions.deleteMessage=false Limitations (Telegram API): - Bot can delete its own messages in any chat - Bot can delete others' messages only if admin with "Delete Messages" - Messages older than 48h in groups may fail to delete --- src/agents/tools/message-tool.ts | 2 +- src/agents/tools/telegram-actions.ts | 30 ++++++++++++++++++- src/channels/plugins/actions/telegram.ts | 17 +++++++++++ src/config/types.telegram.ts | 1 + src/telegram/send.ts | 38 ++++++++++++++++++++++++ 5 files changed, 86 insertions(+), 2 deletions(-) diff --git a/src/agents/tools/message-tool.ts b/src/agents/tools/message-tool.ts index 4b784eb9e..95ad0311f 100644 --- a/src/agents/tools/message-tool.ts +++ b/src/agents/tools/message-tool.ts @@ -148,7 +148,7 @@ export function createMessageTool(options?: MessageToolOptions): AnyAgentTool { label: "Message", name: "message", description: - "Send messages and channel actions (polls, reactions, pins, threads, etc.) via configured channel plugins.", + "Send, delete, and manage messages via channel plugins. Supports actions: send, delete, react, poll, pin, threads, and more.", parameters: schema, execute: async (_toolCallId, args) => { const params = args as Record; diff --git a/src/agents/tools/telegram-actions.ts b/src/agents/tools/telegram-actions.ts index 02c0df803..c340954aa 100644 --- a/src/agents/tools/telegram-actions.ts +++ b/src/agents/tools/telegram-actions.ts @@ -1,7 +1,11 @@ import type { AgentToolResult } from "@mariozechner/pi-agent-core"; import { resolveChannelCapabilities } from "../../config/channel-capabilities.js"; import type { ClawdbotConfig } from "../../config/config.js"; -import { reactMessageTelegram, sendMessageTelegram } from "../../telegram/send.js"; +import { + deleteMessageTelegram, + reactMessageTelegram, + sendMessageTelegram, +} from "../../telegram/send.js"; import { resolveTelegramToken } from "../../telegram/token.js"; import { createActionGate, @@ -149,5 +153,29 @@ export async function handleTelegramAction( }); } + if (action === "deleteMessage") { + if (!isActionEnabled("deleteMessage")) { + throw new Error("Telegram deleteMessage is disabled."); + } + const chatId = readStringOrNumberParam(params, "chatId", { + required: true, + }); + const messageId = readNumberParam(params, "messageId", { + required: true, + integer: true, + }); + const token = resolveTelegramToken(cfg, { accountId }).token; + if (!token) { + throw new Error( + "Telegram bot token missing. Set TELEGRAM_BOT_TOKEN or channels.telegram.botToken.", + ); + } + await deleteMessageTelegram(chatId ?? "", messageId ?? 0, { + token, + accountId: accountId ?? undefined, + }); + return jsonResult({ ok: true, deleted: true }); + } + throw new Error(`Unsupported Telegram action: ${action}`); } diff --git a/src/channels/plugins/actions/telegram.ts b/src/channels/plugins/actions/telegram.ts index c6e02142c..0b5917d07 100644 --- a/src/channels/plugins/actions/telegram.ts +++ b/src/channels/plugins/actions/telegram.ts @@ -35,6 +35,7 @@ export const telegramMessageActions: ChannelMessageActionAdapter = { const gate = createActionGate(cfg.channels?.telegram?.actions); const actions = new Set(["send"]); if (gate("reactions")) actions.add("react"); + if (gate("deleteMessage")) actions.add("delete"); return Array.from(actions); }, supportsButtons: ({ cfg }) => hasTelegramInlineButtons(cfg), @@ -92,6 +93,22 @@ export const telegramMessageActions: ChannelMessageActionAdapter = { ); } + if (action === "delete") { + const chatId = readStringParam(params, "chatId", { required: true }); + const messageId = readStringParam(params, "messageId", { + required: true, + }); + return await handleTelegramAction( + { + action: "deleteMessage", + chatId, + messageId: Number(messageId), + accountId: accountId ?? undefined, + }, + cfg, + ); + } + throw new Error(`Action ${action} is not supported for provider ${providerId}.`); }, }; diff --git a/src/config/types.telegram.ts b/src/config/types.telegram.ts index 366c48e65..b52c4e937 100644 --- a/src/config/types.telegram.ts +++ b/src/config/types.telegram.ts @@ -11,6 +11,7 @@ import type { DmConfig, ProviderCommandsConfig } from "./types.messages.js"; export type TelegramActionConfig = { reactions?: boolean; sendMessage?: boolean; + deleteMessage?: boolean; }; export type TelegramAccountConfig = { diff --git a/src/telegram/send.ts b/src/telegram/send.ts index 291c73017..96588647c 100644 --- a/src/telegram/send.ts +++ b/src/telegram/send.ts @@ -396,6 +396,44 @@ export async function reactMessageTelegram( return { ok: true }; } +type TelegramDeleteOpts = { + token?: string; + accountId?: string; + verbose?: boolean; + api?: Bot["api"]; + retry?: RetryConfig; +}; + +export async function deleteMessageTelegram( + chatIdInput: string | number, + messageIdInput: string | number, + opts: TelegramDeleteOpts = {}, +): Promise<{ ok: true }> { + const cfg = loadConfig(); + const account = resolveTelegramAccount({ + cfg, + accountId: opts.accountId, + }); + const token = resolveToken(opts.token, account); + const chatId = normalizeChatId(String(chatIdInput)); + const messageId = normalizeMessageId(messageIdInput); + const fetchImpl = resolveTelegramFetch(); + const client: ApiClientOptions | undefined = fetchImpl + ? { fetch: fetchImpl as unknown as ApiClientOptions["fetch"] } + : undefined; + const api = opts.api ?? new Bot(token, client ? { client } : undefined).api; + const request = createTelegramRetryRunner({ + retry: opts.retry, + configRetry: account.config.retry, + verbose: opts.verbose, + }); + await request(() => api.deleteMessage(chatId, messageId), "deleteMessage"); + logVerbose( + `[telegram] Deleted message ${messageId} from chat ${chatId}`, + ); + return { ok: true }; +} + function inferFilename(kind: ReturnType) { switch (kind) { case "image":