feat(telegram): add silent message option (disable_notification)

Add support for sending Telegram messages silently without notification
sound via the `silent` parameter on the message tool.

Changes:
- Add `silent` boolean to message tool schema
- Extract and pass `silent` through telegram plugin
- Add `disable_notification: true` to Telegram API calls
- Add `--silent` flag to CLI `message send` command
- Add unit test for silent flag

Closes #2249

AI-assisted (Claude) - fully tested with unit tests + manual Telegram testing
This commit is contained in:
{Suksham-sharma} 2026-01-27 02:27:33 +05:30 committed by Pocket Clawd
parent d34ae86114
commit 0fca32ea8f
6 changed files with 36 additions and 1 deletions

View File

@ -59,6 +59,7 @@ function buildSendSchema(options: { includeButtons: boolean; includeCards: boole
replyTo: Type.Optional(Type.String()), replyTo: Type.Optional(Type.String()),
threadId: Type.Optional(Type.String()), threadId: Type.Optional(Type.String()),
asVoice: Type.Optional(Type.Boolean()), asVoice: Type.Optional(Type.Boolean()),
silent: Type.Optional(Type.Boolean()),
bestEffort: Type.Optional(Type.Boolean()), bestEffort: Type.Optional(Type.Boolean()),
gifPlayback: Type.Optional(Type.Boolean()), gifPlayback: Type.Optional(Type.Boolean()),
buttons: Type.Optional( buttons: Type.Optional(

View File

@ -176,6 +176,7 @@ export async function handleTelegramAction(
replyToMessageId: replyToMessageId ?? undefined, replyToMessageId: replyToMessageId ?? undefined,
messageThreadId: messageThreadId ?? undefined, messageThreadId: messageThreadId ?? undefined,
asVoice: typeof params.asVoice === "boolean" ? params.asVoice : undefined, asVoice: typeof params.asVoice === "boolean" ? params.asVoice : undefined,
silent: typeof params.silent === "boolean" ? params.silent : undefined,
}); });
return jsonResult({ return jsonResult({
ok: true, ok: true,

View File

@ -36,4 +36,30 @@ describe("telegramMessageActions", () => {
cfg, cfg,
); );
}); });
it("passes silent flag for silent sends", async () => {
handleTelegramAction.mockClear();
const cfg = { channels: { telegram: { botToken: "tok" } } } as ClawdbotConfig;
await telegramMessageActions.handleAction({
action: "send",
params: {
to: "456",
message: "Silent notification test",
silent: true,
},
cfg,
accountId: undefined,
});
expect(handleTelegramAction).toHaveBeenCalledWith(
expect.objectContaining({
action: "sendMessage",
to: "456",
content: "Silent notification test",
silent: true,
}),
cfg,
);
});
}); });

View File

@ -20,6 +20,7 @@ function readTelegramSendParams(params: Record<string, unknown>) {
const threadId = readStringParam(params, "threadId"); const threadId = readStringParam(params, "threadId");
const buttons = params.buttons; const buttons = params.buttons;
const asVoice = typeof params.asVoice === "boolean" ? params.asVoice : undefined; const asVoice = typeof params.asVoice === "boolean" ? params.asVoice : undefined;
const silent = typeof params.silent === "boolean" ? params.silent : undefined;
return { return {
to, to,
content, content,
@ -28,6 +29,7 @@ function readTelegramSendParams(params: Record<string, unknown>) {
messageThreadId: threadId ?? undefined, messageThreadId: threadId ?? undefined,
buttons, buttons,
asVoice, asVoice,
silent,
}; };
} }

View File

@ -22,7 +22,8 @@ export function registerMessageSendCommand(message: Command, helpers: MessageCli
.option("--card <json>", "Adaptive Card JSON object (when supported by the channel)") .option("--card <json>", "Adaptive Card JSON object (when supported by the channel)")
.option("--reply-to <id>", "Reply-to message id") .option("--reply-to <id>", "Reply-to message id")
.option("--thread-id <id>", "Thread id (Telegram forum thread)") .option("--thread-id <id>", "Thread id (Telegram forum thread)")
.option("--gif-playback", "Treat video media as GIF playback (WhatsApp only).", false), .option("--gif-playback", "Treat video media as GIF playback (WhatsApp only).", false)
.option("--silent", "Send message silently without notification (Telegram only)", false),
) )
.action(async (opts) => { .action(async (opts) => {
await helpers.runMessageAction("send", opts); await helpers.runMessageAction("send", opts);

View File

@ -40,6 +40,8 @@ type TelegramSendOpts = {
plainText?: string; plainText?: string;
/** Send audio as voice message (voice bubble) instead of audio file. Defaults to false. */ /** Send audio as voice message (voice bubble) instead of audio file. Defaults to false. */
asVoice?: boolean; asVoice?: boolean;
/** Send message silently (no notification). Defaults to false. */
silent?: boolean;
/** Message ID to reply to (for threading) */ /** Message ID to reply to (for threading) */
replyToMessageId?: number; replyToMessageId?: number;
/** Forum topic thread ID (for forum supergroups) */ /** Forum topic thread ID (for forum supergroups) */
@ -245,6 +247,7 @@ export async function sendMessageTelegram(
const sendParams = { const sendParams = {
parse_mode: "HTML" as const, parse_mode: "HTML" as const,
...baseParams, ...baseParams,
...(opts.silent === true ? { disable_notification: true } : {}),
}; };
const res = await requestWithDiag( const res = await requestWithDiag(
() => api.sendMessage(chatId, htmlText, sendParams), () => api.sendMessage(chatId, htmlText, sendParams),
@ -298,6 +301,7 @@ export async function sendMessageTelegram(
caption: htmlCaption, caption: htmlCaption,
...(htmlCaption ? { parse_mode: "HTML" as const } : {}), ...(htmlCaption ? { parse_mode: "HTML" as const } : {}),
...baseMediaParams, ...baseMediaParams,
...(opts.silent === true ? { disable_notification: true } : {}),
}; };
let result: let result:
| Awaited<ReturnType<typeof api.sendPhoto>> | Awaited<ReturnType<typeof api.sendPhoto>>