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:
parent
b9da2c4679
commit
77df8b1104
@ -6,22 +6,24 @@ export const discordOutbound: ChannelOutboundAdapter = {
|
||||
chunker: null,
|
||||
textChunkLimit: 2000,
|
||||
pollMaxOptions: 10,
|
||||
sendText: async ({ to, text, accountId, deps, replyToId }) => {
|
||||
sendText: async ({ to, text, accountId, deps, replyToId, silent }) => {
|
||||
const send = deps?.sendDiscord ?? sendMessageDiscord;
|
||||
const result = await send(to, text, {
|
||||
verbose: false,
|
||||
replyTo: replyToId ?? undefined,
|
||||
accountId: accountId ?? undefined,
|
||||
silent: silent ?? undefined,
|
||||
});
|
||||
return { channel: "discord", ...result };
|
||||
},
|
||||
sendMedia: async ({ to, text, mediaUrl, accountId, deps, replyToId }) => {
|
||||
sendMedia: async ({ to, text, mediaUrl, accountId, deps, replyToId, silent }) => {
|
||||
const send = deps?.sendDiscord ?? sendMessageDiscord;
|
||||
const result = await send(to, text, {
|
||||
verbose: false,
|
||||
mediaUrl,
|
||||
replyTo: replyToId ?? undefined,
|
||||
accountId: accountId ?? undefined,
|
||||
silent: silent ?? undefined,
|
||||
});
|
||||
return { channel: "discord", ...result };
|
||||
},
|
||||
|
||||
@ -80,6 +80,7 @@ export type ChannelOutboundContext = {
|
||||
threadId?: string | number | null;
|
||||
accountId?: string | null;
|
||||
deps?: OutboundSendDeps;
|
||||
silent?: boolean;
|
||||
};
|
||||
|
||||
export type ChannelOutboundPayloadContext = ChannelOutboundContext & {
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -86,6 +86,7 @@ async function createChannelHandler(params: {
|
||||
threadId?: string | number | null;
|
||||
deps?: OutboundSendDeps;
|
||||
gifPlayback?: boolean;
|
||||
silent?: boolean;
|
||||
}): Promise<ChannelHandler> {
|
||||
const outbound = await loadChannelOutboundAdapter(params.channel);
|
||||
if (!outbound?.sendText || !outbound?.sendMedia) {
|
||||
@ -101,6 +102,7 @@ async function createChannelHandler(params: {
|
||||
threadId: params.threadId,
|
||||
deps: params.deps,
|
||||
gifPlayback: params.gifPlayback,
|
||||
silent: params.silent,
|
||||
});
|
||||
if (!handler) {
|
||||
throw new Error(`Outbound not configured for channel: ${params.channel}`);
|
||||
@ -118,6 +120,7 @@ function createPluginHandler(params: {
|
||||
threadId?: string | number | null;
|
||||
deps?: OutboundSendDeps;
|
||||
gifPlayback?: boolean;
|
||||
silent?: boolean;
|
||||
}): ChannelHandler | null {
|
||||
const outbound = params.outbound;
|
||||
if (!outbound?.sendText || !outbound?.sendMedia) {
|
||||
@ -143,6 +146,7 @@ function createPluginHandler(params: {
|
||||
threadId: params.threadId,
|
||||
gifPlayback: params.gifPlayback,
|
||||
deps: params.deps,
|
||||
silent: params.silent,
|
||||
payload,
|
||||
})
|
||||
: undefined,
|
||||
@ -156,6 +160,7 @@ function createPluginHandler(params: {
|
||||
threadId: params.threadId,
|
||||
gifPlayback: params.gifPlayback,
|
||||
deps: params.deps,
|
||||
silent: params.silent,
|
||||
}),
|
||||
sendMedia: async (caption, mediaUrl) =>
|
||||
sendMedia({
|
||||
@ -168,6 +173,7 @@ function createPluginHandler(params: {
|
||||
threadId: params.threadId,
|
||||
gifPlayback: params.gifPlayback,
|
||||
deps: params.deps,
|
||||
silent: params.silent,
|
||||
}),
|
||||
};
|
||||
}
|
||||
@ -192,6 +198,7 @@ export async function deliverOutboundPayloads(params: {
|
||||
text?: string;
|
||||
mediaUrls?: string[];
|
||||
};
|
||||
silent?: boolean;
|
||||
}): Promise<OutboundDeliveryResult[]> {
|
||||
const { cfg, channel, to, payloads } = params;
|
||||
const accountId = params.accountId;
|
||||
@ -208,6 +215,7 @@ export async function deliverOutboundPayloads(params: {
|
||||
replyToId: params.replyToId,
|
||||
threadId: params.threadId,
|
||||
gifPlayback: params.gifPlayback,
|
||||
silent: params.silent,
|
||||
});
|
||||
const textLimit = handler.chunker
|
||||
? resolveTextChunkLimit(cfg, channel, accountId, {
|
||||
|
||||
@ -820,6 +820,7 @@ async function handleSendAction(ctx: ResolvedActionContext): Promise<MessageActi
|
||||
params.message = message;
|
||||
const gifPlayback = readBooleanParam(params, "gifPlayback") ?? false;
|
||||
const bestEffort = readBooleanParam(params, "bestEffort");
|
||||
const silent = readBooleanParam(params, "silent");
|
||||
|
||||
const replyToId = readStringParam(params, "replyTo");
|
||||
const threadId = readStringParam(params, "threadId");
|
||||
@ -884,6 +885,7 @@ async function handleSendAction(ctx: ResolvedActionContext): Promise<MessageActi
|
||||
}
|
||||
: undefined,
|
||||
abortSignal,
|
||||
silent: silent ?? undefined,
|
||||
},
|
||||
to,
|
||||
message,
|
||||
|
||||
@ -51,6 +51,7 @@ type MessageSendParams = {
|
||||
mediaUrls?: string[];
|
||||
};
|
||||
abortSignal?: AbortSignal;
|
||||
silent?: boolean;
|
||||
};
|
||||
|
||||
export type MessageSendResult = {
|
||||
@ -173,6 +174,7 @@ export async function sendMessage(params: MessageSendParams): Promise<MessageSen
|
||||
deps: params.deps,
|
||||
bestEffort: params.bestEffort,
|
||||
abortSignal: params.abortSignal,
|
||||
silent: params.silent,
|
||||
mirror: params.mirror
|
||||
? {
|
||||
...params.mirror,
|
||||
|
||||
@ -34,6 +34,7 @@ export type OutboundSendContext = {
|
||||
mediaUrls?: string[];
|
||||
};
|
||||
abortSignal?: AbortSignal;
|
||||
silent?: boolean;
|
||||
};
|
||||
|
||||
function extractToolPayload(result: AgentToolResult<unknown>): unknown {
|
||||
@ -128,6 +129,7 @@ export async function executeSendAction(params: {
|
||||
gateway: params.ctx.gateway,
|
||||
mirror: params.ctx.mirror,
|
||||
abortSignal: params.ctx.abortSignal,
|
||||
silent: params.ctx.silent,
|
||||
});
|
||||
|
||||
return {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user