From f2a0e8e5bb553a0e4ee0122abf01b855471c7178 Mon Sep 17 00:00:00 2001 From: Muhammed Mukhthar CM Date: Sat, 17 Jan 2026 11:32:16 +0000 Subject: [PATCH 1/2] feat(telegram): support sending audio as native voice notes via asVoice param in message tool --- src/agents/tools/message-tool.ts | 1 + src/agents/tools/telegram-actions.ts | 1 + src/channels/plugins/actions/telegram.ts | 2 ++ 3 files changed, 4 insertions(+) diff --git a/src/agents/tools/message-tool.ts b/src/agents/tools/message-tool.ts index 55eb2698b..9c228b770 100644 --- a/src/agents/tools/message-tool.ts +++ b/src/agents/tools/message-tool.ts @@ -39,6 +39,7 @@ function buildSendSchema(options: { includeButtons: boolean }) { media: Type.Optional(Type.String()), replyTo: Type.Optional(Type.String()), threadId: Type.Optional(Type.String()), + asVoice: Type.Optional(Type.Boolean()), bestEffort: Type.Optional(Type.Boolean()), gifPlayback: Type.Optional(Type.Boolean()), buttons: Type.Optional( diff --git a/src/agents/tools/telegram-actions.ts b/src/agents/tools/telegram-actions.ts index 7cec1b1a1..5385dd10f 100644 --- a/src/agents/tools/telegram-actions.ts +++ b/src/agents/tools/telegram-actions.ts @@ -175,6 +175,7 @@ export async function handleTelegramAction( buttons, replyToMessageId: replyToMessageId ?? undefined, messageThreadId: messageThreadId ?? undefined, + asVoice: typeof params.asVoice === "boolean" ? params.asVoice : undefined, }); return jsonResult({ ok: true, diff --git a/src/channels/plugins/actions/telegram.ts b/src/channels/plugins/actions/telegram.ts index bb65baf0f..80cd1dd07 100644 --- a/src/channels/plugins/actions/telegram.ts +++ b/src/channels/plugins/actions/telegram.ts @@ -50,6 +50,7 @@ export const telegramMessageActions: ChannelMessageActionAdapter = { const replyTo = readStringParam(params, "replyTo"); const threadId = readStringParam(params, "threadId"); const buttons = params.buttons; + const asVoice = typeof params.asVoice === "boolean" ? params.asVoice : undefined; return await handleTelegramAction( { action: "sendMessage", @@ -60,6 +61,7 @@ export const telegramMessageActions: ChannelMessageActionAdapter = { messageThreadId: threadId ?? undefined, accountId: accountId ?? undefined, buttons, + asVoice, }, cfg, ); From af29c6a9802443cec008cdce2721f4bda41efad1 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Sat, 17 Jan 2026 17:32:13 +0000 Subject: [PATCH 2/2] fix: allow media-only telegram voice sends (#1099) (thanks @mukhtharcm) --- CHANGELOG.md | 2 +- docs/channels/telegram.md | 13 +++++++ src/channels/plugins/actions/telegram.test.ts | 39 +++++++++++++++++++ src/channels/plugins/actions/telegram.ts | 9 +++-- 4 files changed, 58 insertions(+), 5 deletions(-) create mode 100644 src/channels/plugins/actions/telegram.test.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 59ddcd117..74f51300d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,7 +9,7 @@ Docs: https://docs.clawd.bot ### Fixes - Matrix: send voice/image-specific media payloads and keep legacy poll parsing. (#1088) — thanks @sibbl. - +- Telegram: allow media-only message tool sends to request voice notes via `asVoice`. (#1099) — thanks @mukhtharcm. ## 2026.1.16-2 ### Changes diff --git a/docs/channels/telegram.md b/docs/channels/telegram.md index 3a38d8931..334d6fccb 100644 --- a/docs/channels/telegram.md +++ b/docs/channels/telegram.md @@ -360,6 +360,19 @@ To force a voice note bubble in agent replies, include this tag anywhere in the The tag is stripped from the delivered text. Other channels ignore this tag. +For message tool sends, set `asVoice: true` with a voice-compatible audio `media` URL +(`message` is optional when media is present): + +```json5 +{ + "action": "send", + "channel": "telegram", + "to": "123456789", + "media": "https://example.com/voice.ogg", + "asVoice": true +} +``` + ## Streaming (drafts) Telegram can stream **draft bubbles** while the agent is generating a response. Clawdbot uses Bot API `sendMessageDraft` (not real messages) and then sends the diff --git a/src/channels/plugins/actions/telegram.test.ts b/src/channels/plugins/actions/telegram.test.ts new file mode 100644 index 000000000..aac316858 --- /dev/null +++ b/src/channels/plugins/actions/telegram.test.ts @@ -0,0 +1,39 @@ +import { describe, expect, it, vi } from "vitest"; + +import type { ClawdbotConfig } from "../../../config/config.js"; +import { telegramMessageActions } from "./telegram.js"; + +const handleTelegramAction = vi.fn(async () => ({ ok: true })); + +vi.mock("../../../agents/tools/telegram-actions.js", () => ({ + handleTelegramAction: (...args: unknown[]) => handleTelegramAction(...args), +})); + +describe("telegramMessageActions", () => { + it("allows media-only sends and passes asVoice", async () => { + handleTelegramAction.mockClear(); + const cfg = { channels: { telegram: { botToken: "tok" } } } as ClawdbotConfig; + + await telegramMessageActions.handleAction({ + action: "send", + params: { + to: "123", + media: "https://example.com/voice.ogg", + asVoice: true, + }, + cfg, + accountId: undefined, + }); + + expect(handleTelegramAction).toHaveBeenCalledWith( + expect.objectContaining({ + action: "sendMessage", + to: "123", + content: "", + mediaUrl: "https://example.com/voice.ogg", + asVoice: true, + }), + cfg, + ); + }); +}); diff --git a/src/channels/plugins/actions/telegram.ts b/src/channels/plugins/actions/telegram.ts index 80cd1dd07..fdd67aee1 100644 --- a/src/channels/plugins/actions/telegram.ts +++ b/src/channels/plugins/actions/telegram.ts @@ -42,11 +42,12 @@ export const telegramMessageActions: ChannelMessageActionAdapter = { handleAction: async ({ action, params, cfg, accountId }) => { if (action === "send") { const to = readStringParam(params, "to", { required: true }); - const content = readStringParam(params, "message", { - required: true, - allowEmpty: true, - }); const mediaUrl = readStringParam(params, "media", { trim: false }); + const content = + readStringParam(params, "message", { + required: !mediaUrl, + allowEmpty: true, + }) ?? ""; const replyTo = readStringParam(params, "replyTo"); const threadId = readStringParam(params, "threadId"); const buttons = params.buttons;