feat(discord): add silent message support (SUPPRESS_NOTIFICATIONS flag)

- Add silent option to message tool for Discord
- Passes SUPPRESS_NOTIFICATIONS flag (4096) to Discord API
- Threads silent param through entire outbound chain:
  - message-action-runner.ts
  - outbound-send-service.ts
  - message.ts
  - deliver.ts
  - discord outbound adapter
  - send.outbound.ts
  - send.shared.ts

Usage: message tool with silent=true suppresses push/desktop notifications
This commit is contained in:
nyanjou
2026-02-03 14:19:24 +01:00
committed by Shadow
parent b9da2c4679
commit 77df8b1104
7 changed files with 30 additions and 2 deletions

View File

@@ -278,6 +278,9 @@ async function resolveChannelId(
return { channelId: dmChannel.id, dm: true };
}
// Discord message flag for silent/suppress notifications
const SUPPRESS_NOTIFICATIONS_FLAG = 1 << 12;
export function buildDiscordTextChunks(
text: string,
opts: { maxLinesPerMessage?: number; chunkMode?: ChunkMode; maxChars?: number } = {},
@@ -305,11 +308,13 @@ async function sendDiscordText(
maxLinesPerMessage?: number,
embeds?: unknown[],
chunkMode?: ChunkMode,
silent?: boolean,
) {
if (!text.trim()) {
throw new Error("Message must be non-empty for Discord sends");
}
const messageReference = replyTo ? { message_id: replyTo, fail_if_not_exists: false } : undefined;
const flags = silent ? SUPPRESS_NOTIFICATIONS_FLAG : undefined;
const chunks = buildDiscordTextChunks(text, { maxLinesPerMessage, chunkMode });
if (chunks.length === 1) {
const res = (await request(
@@ -319,6 +324,7 @@ async function sendDiscordText(
content: chunks[0],
message_reference: messageReference,
...(embeds?.length ? { embeds } : {}),
...(flags ? { flags } : {}),
},
}) as Promise<{ id: string; channel_id: string }>,
"text",
@@ -335,6 +341,7 @@ async function sendDiscordText(
content: chunk,
message_reference: isFirst ? messageReference : undefined,
...(isFirst && embeds?.length ? { embeds } : {}),
...(flags ? { flags } : {}),
},
}) as Promise<{ id: string; channel_id: string }>,
"text",
@@ -357,12 +364,14 @@ async function sendDiscordMedia(
maxLinesPerMessage?: number,
embeds?: unknown[],
chunkMode?: ChunkMode,
silent?: boolean,
) {
const media = await loadWebMedia(mediaUrl);
const chunks = text ? buildDiscordTextChunks(text, { maxLinesPerMessage, chunkMode }) : [];
const caption = chunks[0] ?? "";
const hasCaption = caption.trim().length > 0;
const messageReference = replyTo ? { message_id: replyTo, fail_if_not_exists: false } : undefined;
const flags = silent ? SUPPRESS_NOTIFICATIONS_FLAG : undefined;
const res = (await request(
() =>
rest.post(Routes.channelMessages(channelId), {
@@ -373,6 +382,7 @@ async function sendDiscordMedia(
...(hasCaption ? { content: caption } : {}),
...(messageReference ? { message_reference: messageReference } : {}),
...(embeds?.length ? { embeds } : {}),
...(flags ? { flags } : {}),
files: [
{
data: media.buffer,
@@ -396,6 +406,7 @@ async function sendDiscordMedia(
maxLinesPerMessage,
undefined,
chunkMode,
silent,
);
}
return res;