fix(telegram): wire delete action for message tool (#903) - thanks @sleontenko

Co-authored-by: Stan <sleontenko@users.noreply.github.com>
This commit is contained in:
Peter Steinberger
2026-01-15 00:29:28 +00:00
parent 83a25d26fc
commit 0d0b77ded6
6 changed files with 56 additions and 6 deletions

View File

@@ -6,6 +6,7 @@
- Usage: add MiniMax coding plan usage tracking. - Usage: add MiniMax coding plan usage tracking.
- Auth: label Claude Code CLI auth options. (#915) — thanks @SeanZoR. - Auth: label Claude Code CLI auth options. (#915) — thanks @SeanZoR.
- Docs: standardize Claude Code CLI naming across docs and prompts. (follow-up to #915) - Docs: standardize Claude Code CLI naming across docs and prompts. (follow-up to #915)
- Telegram: add message delete action in the message tool. (#903) — thanks @sleontenko.
### Fixes ### Fixes
- Mac: pass auth token/password to dashboard URL for authenticated access. (#918) — thanks @rahthakor. - Mac: pass auth token/password to dashboard URL for authenticated access. (#918) — thanks @rahthakor.

View File

@@ -262,8 +262,9 @@ Outbound Telegram API calls retry on transient network/429 errors with exponenti
## Agent tool (messages + reactions) ## Agent tool (messages + reactions)
- Tool: `telegram` with `sendMessage` action (`to`, `content`, optional `mediaUrl`, `replyToMessageId`, `messageThreadId`). - Tool: `telegram` with `sendMessage` action (`to`, `content`, optional `mediaUrl`, `replyToMessageId`, `messageThreadId`).
- Tool: `telegram` with `react` action (`chatId`, `messageId`, `emoji`). - Tool: `telegram` with `react` action (`chatId`, `messageId`, `emoji`).
- Tool: `telegram` with `deleteMessage` action (`chatId`, `messageId`).
- Reaction removal semantics: see [/tools/reactions](/tools/reactions). - Reaction removal semantics: see [/tools/reactions](/tools/reactions).
- Tool gating: `channels.telegram.actions.reactions` and `channels.telegram.actions.sendMessage` (default: enabled). - Tool gating: `channels.telegram.actions.reactions`, `channels.telegram.actions.sendMessage`, `channels.telegram.actions.deleteMessage` (default: enabled).
## Delivery targets (CLI/cron) ## Delivery targets (CLI/cron)
- Use a chat id (`123456789`) or a username (`@name`) as the target. - Use a chat id (`123456789`) or a username (`@name`) as the target.
@@ -322,6 +323,7 @@ Provider options:
- `channels.telegram.webhookPath`: local webhook path (default `/telegram-webhook`). - `channels.telegram.webhookPath`: local webhook path (default `/telegram-webhook`).
- `channels.telegram.actions.reactions`: gate Telegram tool reactions. - `channels.telegram.actions.reactions`: gate Telegram tool reactions.
- `channels.telegram.actions.sendMessage`: gate Telegram tool message sends. - `channels.telegram.actions.sendMessage`: gate Telegram tool message sends.
- `channels.telegram.actions.deleteMessage`: gate Telegram tool message deletes.
Related global options: Related global options:
- `agents.list[].groupChat.mentionPatterns` (mention gating patterns). - `agents.list[].groupChat.mentionPatterns` (mention gating patterns).

View File

@@ -81,7 +81,7 @@ Target formats (`--to`):
- Optional: `--channel-id` - Optional: `--channel-id`
- `delete` - `delete`
- Channels: Discord/Slack - Channels: Discord/Slack/Telegram
- Required: `--message-id`, `--to` or `--channel-id` - Required: `--message-id`, `--to` or `--channel-id`
- Optional: `--channel-id` - Optional: `--channel-id`

View File

@@ -8,17 +8,20 @@ const sendMessageTelegram = vi.fn(async () => ({
messageId: "789", messageId: "789",
chatId: "123", chatId: "123",
})); }));
const deleteMessageTelegram = vi.fn(async () => ({ ok: true }));
const originalToken = process.env.TELEGRAM_BOT_TOKEN; const originalToken = process.env.TELEGRAM_BOT_TOKEN;
vi.mock("../../telegram/send.js", () => ({ vi.mock("../../telegram/send.js", () => ({
reactMessageTelegram: (...args: unknown[]) => reactMessageTelegram(...args), reactMessageTelegram: (...args: unknown[]) => reactMessageTelegram(...args),
sendMessageTelegram: (...args: unknown[]) => sendMessageTelegram(...args), sendMessageTelegram: (...args: unknown[]) => sendMessageTelegram(...args),
deleteMessageTelegram: (...args: unknown[]) => deleteMessageTelegram(...args),
})); }));
describe("handleTelegramAction", () => { describe("handleTelegramAction", () => {
beforeEach(() => { beforeEach(() => {
reactMessageTelegram.mockClear(); reactMessageTelegram.mockClear();
sendMessageTelegram.mockClear(); sendMessageTelegram.mockClear();
deleteMessageTelegram.mockClear();
process.env.TELEGRAM_BOT_TOKEN = "tok"; process.env.TELEGRAM_BOT_TOKEN = "tok";
}); });
@@ -177,6 +180,43 @@ describe("handleTelegramAction", () => {
).rejects.toThrow(/Telegram sendMessage is disabled/); ).rejects.toThrow(/Telegram sendMessage is disabled/);
}); });
it("deletes a message", async () => {
const cfg = {
channels: { telegram: { botToken: "tok" } },
} as ClawdbotConfig;
await handleTelegramAction(
{
action: "deleteMessage",
chatId: "123",
messageId: 456,
},
cfg,
);
expect(deleteMessageTelegram).toHaveBeenCalledWith(
"123",
456,
expect.objectContaining({ token: "tok" }),
);
});
it("respects deleteMessage gating", async () => {
const cfg = {
channels: {
telegram: { botToken: "tok", actions: { deleteMessage: false } },
},
} as ClawdbotConfig;
await expect(
handleTelegramAction(
{
action: "deleteMessage",
chatId: "123",
messageId: 456,
},
cfg,
),
).rejects.toThrow(/Telegram deleteMessage is disabled/);
});
it("throws on missing bot token for sendMessage", async () => { it("throws on missing bot token for sendMessage", async () => {
delete process.env.TELEGRAM_BOT_TOKEN; delete process.env.TELEGRAM_BOT_TOKEN;
const cfg = {} as ClawdbotConfig; const cfg = {} as ClawdbotConfig;

View File

@@ -1,4 +1,8 @@
import { createActionGate, readStringParam } from "../../../agents/tools/common.js"; import {
createActionGate,
readStringOrNumberParam,
readStringParam,
} from "../../../agents/tools/common.js";
import { handleTelegramAction } from "../../../agents/tools/telegram-actions.js"; import { handleTelegramAction } from "../../../agents/tools/telegram-actions.js";
import type { ClawdbotConfig } from "../../../config/config.js"; import type { ClawdbotConfig } from "../../../config/config.js";
import { listEnabledTelegramAccounts } from "../../../telegram/accounts.js"; import { listEnabledTelegramAccounts } from "../../../telegram/accounts.js";
@@ -94,7 +98,10 @@ export const telegramMessageActions: ChannelMessageActionAdapter = {
} }
if (action === "delete") { if (action === "delete") {
const chatId = readStringParam(params, "chatId", { required: true }); const chatId =
readStringOrNumberParam(params, "chatId") ??
readStringOrNumberParam(params, "channelId") ??
readStringParam(params, "to", { required: true });
const messageId = readStringParam(params, "messageId", { const messageId = readStringParam(params, "messageId", {
required: true, required: true,
}); });

View File

@@ -101,12 +101,12 @@ function normalizeMessageId(raw: string | number): number {
if (typeof raw === "string") { if (typeof raw === "string") {
const value = raw.trim(); const value = raw.trim();
if (!value) { if (!value) {
throw new Error("Message id is required for Telegram reactions"); throw new Error("Message id is required for Telegram actions");
} }
const parsed = Number.parseInt(value, 10); const parsed = Number.parseInt(value, 10);
if (Number.isFinite(parsed)) return parsed; if (Number.isFinite(parsed)) return parsed;
} }
throw new Error("Message id is required for Telegram reactions"); throw new Error("Message id is required for Telegram actions");
} }
export function buildInlineKeyboard( export function buildInlineKeyboard(