mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-08 22:58:26 +00:00
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:
@@ -6,22 +6,24 @@ export const discordOutbound: ChannelOutboundAdapter = {
|
|||||||
chunker: null,
|
chunker: null,
|
||||||
textChunkLimit: 2000,
|
textChunkLimit: 2000,
|
||||||
pollMaxOptions: 10,
|
pollMaxOptions: 10,
|
||||||
sendText: async ({ to, text, accountId, deps, replyToId }) => {
|
sendText: async ({ to, text, accountId, deps, replyToId, silent }) => {
|
||||||
const send = deps?.sendDiscord ?? sendMessageDiscord;
|
const send = deps?.sendDiscord ?? sendMessageDiscord;
|
||||||
const result = await send(to, text, {
|
const result = await send(to, text, {
|
||||||
verbose: false,
|
verbose: false,
|
||||||
replyTo: replyToId ?? undefined,
|
replyTo: replyToId ?? undefined,
|
||||||
accountId: accountId ?? undefined,
|
accountId: accountId ?? undefined,
|
||||||
|
silent: silent ?? undefined,
|
||||||
});
|
});
|
||||||
return { channel: "discord", ...result };
|
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 send = deps?.sendDiscord ?? sendMessageDiscord;
|
||||||
const result = await send(to, text, {
|
const result = await send(to, text, {
|
||||||
verbose: false,
|
verbose: false,
|
||||||
mediaUrl,
|
mediaUrl,
|
||||||
replyTo: replyToId ?? undefined,
|
replyTo: replyToId ?? undefined,
|
||||||
accountId: accountId ?? undefined,
|
accountId: accountId ?? undefined,
|
||||||
|
silent: silent ?? undefined,
|
||||||
});
|
});
|
||||||
return { channel: "discord", ...result };
|
return { channel: "discord", ...result };
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -80,6 +80,7 @@ export type ChannelOutboundContext = {
|
|||||||
threadId?: string | number | null;
|
threadId?: string | number | null;
|
||||||
accountId?: string | null;
|
accountId?: string | null;
|
||||||
deps?: OutboundSendDeps;
|
deps?: OutboundSendDeps;
|
||||||
|
silent?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type ChannelOutboundPayloadContext = ChannelOutboundContext & {
|
export type ChannelOutboundPayloadContext = ChannelOutboundContext & {
|
||||||
|
|||||||
@@ -278,6 +278,9 @@ async function resolveChannelId(
|
|||||||
return { channelId: dmChannel.id, dm: true };
|
return { channelId: dmChannel.id, dm: true };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Discord message flag for silent/suppress notifications
|
||||||
|
const SUPPRESS_NOTIFICATIONS_FLAG = 1 << 12;
|
||||||
|
|
||||||
export function buildDiscordTextChunks(
|
export function buildDiscordTextChunks(
|
||||||
text: string,
|
text: string,
|
||||||
opts: { maxLinesPerMessage?: number; chunkMode?: ChunkMode; maxChars?: number } = {},
|
opts: { maxLinesPerMessage?: number; chunkMode?: ChunkMode; maxChars?: number } = {},
|
||||||
@@ -305,11 +308,13 @@ async function sendDiscordText(
|
|||||||
maxLinesPerMessage?: number,
|
maxLinesPerMessage?: number,
|
||||||
embeds?: unknown[],
|
embeds?: unknown[],
|
||||||
chunkMode?: ChunkMode,
|
chunkMode?: ChunkMode,
|
||||||
|
silent?: boolean,
|
||||||
) {
|
) {
|
||||||
if (!text.trim()) {
|
if (!text.trim()) {
|
||||||
throw new Error("Message must be non-empty for Discord sends");
|
throw new Error("Message must be non-empty for Discord sends");
|
||||||
}
|
}
|
||||||
const messageReference = replyTo ? { message_id: replyTo, fail_if_not_exists: false } : undefined;
|
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 });
|
const chunks = buildDiscordTextChunks(text, { maxLinesPerMessage, chunkMode });
|
||||||
if (chunks.length === 1) {
|
if (chunks.length === 1) {
|
||||||
const res = (await request(
|
const res = (await request(
|
||||||
@@ -319,6 +324,7 @@ async function sendDiscordText(
|
|||||||
content: chunks[0],
|
content: chunks[0],
|
||||||
message_reference: messageReference,
|
message_reference: messageReference,
|
||||||
...(embeds?.length ? { embeds } : {}),
|
...(embeds?.length ? { embeds } : {}),
|
||||||
|
...(flags ? { flags } : {}),
|
||||||
},
|
},
|
||||||
}) as Promise<{ id: string; channel_id: string }>,
|
}) as Promise<{ id: string; channel_id: string }>,
|
||||||
"text",
|
"text",
|
||||||
@@ -335,6 +341,7 @@ async function sendDiscordText(
|
|||||||
content: chunk,
|
content: chunk,
|
||||||
message_reference: isFirst ? messageReference : undefined,
|
message_reference: isFirst ? messageReference : undefined,
|
||||||
...(isFirst && embeds?.length ? { embeds } : {}),
|
...(isFirst && embeds?.length ? { embeds } : {}),
|
||||||
|
...(flags ? { flags } : {}),
|
||||||
},
|
},
|
||||||
}) as Promise<{ id: string; channel_id: string }>,
|
}) as Promise<{ id: string; channel_id: string }>,
|
||||||
"text",
|
"text",
|
||||||
@@ -357,12 +364,14 @@ async function sendDiscordMedia(
|
|||||||
maxLinesPerMessage?: number,
|
maxLinesPerMessage?: number,
|
||||||
embeds?: unknown[],
|
embeds?: unknown[],
|
||||||
chunkMode?: ChunkMode,
|
chunkMode?: ChunkMode,
|
||||||
|
silent?: boolean,
|
||||||
) {
|
) {
|
||||||
const media = await loadWebMedia(mediaUrl);
|
const media = await loadWebMedia(mediaUrl);
|
||||||
const chunks = text ? buildDiscordTextChunks(text, { maxLinesPerMessage, chunkMode }) : [];
|
const chunks = text ? buildDiscordTextChunks(text, { maxLinesPerMessage, chunkMode }) : [];
|
||||||
const caption = chunks[0] ?? "";
|
const caption = chunks[0] ?? "";
|
||||||
const hasCaption = caption.trim().length > 0;
|
const hasCaption = caption.trim().length > 0;
|
||||||
const messageReference = replyTo ? { message_id: replyTo, fail_if_not_exists: false } : undefined;
|
const messageReference = replyTo ? { message_id: replyTo, fail_if_not_exists: false } : undefined;
|
||||||
|
const flags = silent ? SUPPRESS_NOTIFICATIONS_FLAG : undefined;
|
||||||
const res = (await request(
|
const res = (await request(
|
||||||
() =>
|
() =>
|
||||||
rest.post(Routes.channelMessages(channelId), {
|
rest.post(Routes.channelMessages(channelId), {
|
||||||
@@ -373,6 +382,7 @@ async function sendDiscordMedia(
|
|||||||
...(hasCaption ? { content: caption } : {}),
|
...(hasCaption ? { content: caption } : {}),
|
||||||
...(messageReference ? { message_reference: messageReference } : {}),
|
...(messageReference ? { message_reference: messageReference } : {}),
|
||||||
...(embeds?.length ? { embeds } : {}),
|
...(embeds?.length ? { embeds } : {}),
|
||||||
|
...(flags ? { flags } : {}),
|
||||||
files: [
|
files: [
|
||||||
{
|
{
|
||||||
data: media.buffer,
|
data: media.buffer,
|
||||||
@@ -396,6 +406,7 @@ async function sendDiscordMedia(
|
|||||||
maxLinesPerMessage,
|
maxLinesPerMessage,
|
||||||
undefined,
|
undefined,
|
||||||
chunkMode,
|
chunkMode,
|
||||||
|
silent,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
|
|||||||
@@ -86,6 +86,7 @@ async function createChannelHandler(params: {
|
|||||||
threadId?: string | number | null;
|
threadId?: string | number | null;
|
||||||
deps?: OutboundSendDeps;
|
deps?: OutboundSendDeps;
|
||||||
gifPlayback?: boolean;
|
gifPlayback?: boolean;
|
||||||
|
silent?: boolean;
|
||||||
}): Promise<ChannelHandler> {
|
}): Promise<ChannelHandler> {
|
||||||
const outbound = await loadChannelOutboundAdapter(params.channel);
|
const outbound = await loadChannelOutboundAdapter(params.channel);
|
||||||
if (!outbound?.sendText || !outbound?.sendMedia) {
|
if (!outbound?.sendText || !outbound?.sendMedia) {
|
||||||
@@ -101,6 +102,7 @@ async function createChannelHandler(params: {
|
|||||||
threadId: params.threadId,
|
threadId: params.threadId,
|
||||||
deps: params.deps,
|
deps: params.deps,
|
||||||
gifPlayback: params.gifPlayback,
|
gifPlayback: params.gifPlayback,
|
||||||
|
silent: params.silent,
|
||||||
});
|
});
|
||||||
if (!handler) {
|
if (!handler) {
|
||||||
throw new Error(`Outbound not configured for channel: ${params.channel}`);
|
throw new Error(`Outbound not configured for channel: ${params.channel}`);
|
||||||
@@ -118,6 +120,7 @@ function createPluginHandler(params: {
|
|||||||
threadId?: string | number | null;
|
threadId?: string | number | null;
|
||||||
deps?: OutboundSendDeps;
|
deps?: OutboundSendDeps;
|
||||||
gifPlayback?: boolean;
|
gifPlayback?: boolean;
|
||||||
|
silent?: boolean;
|
||||||
}): ChannelHandler | null {
|
}): ChannelHandler | null {
|
||||||
const outbound = params.outbound;
|
const outbound = params.outbound;
|
||||||
if (!outbound?.sendText || !outbound?.sendMedia) {
|
if (!outbound?.sendText || !outbound?.sendMedia) {
|
||||||
@@ -143,6 +146,7 @@ function createPluginHandler(params: {
|
|||||||
threadId: params.threadId,
|
threadId: params.threadId,
|
||||||
gifPlayback: params.gifPlayback,
|
gifPlayback: params.gifPlayback,
|
||||||
deps: params.deps,
|
deps: params.deps,
|
||||||
|
silent: params.silent,
|
||||||
payload,
|
payload,
|
||||||
})
|
})
|
||||||
: undefined,
|
: undefined,
|
||||||
@@ -156,6 +160,7 @@ function createPluginHandler(params: {
|
|||||||
threadId: params.threadId,
|
threadId: params.threadId,
|
||||||
gifPlayback: params.gifPlayback,
|
gifPlayback: params.gifPlayback,
|
||||||
deps: params.deps,
|
deps: params.deps,
|
||||||
|
silent: params.silent,
|
||||||
}),
|
}),
|
||||||
sendMedia: async (caption, mediaUrl) =>
|
sendMedia: async (caption, mediaUrl) =>
|
||||||
sendMedia({
|
sendMedia({
|
||||||
@@ -168,6 +173,7 @@ function createPluginHandler(params: {
|
|||||||
threadId: params.threadId,
|
threadId: params.threadId,
|
||||||
gifPlayback: params.gifPlayback,
|
gifPlayback: params.gifPlayback,
|
||||||
deps: params.deps,
|
deps: params.deps,
|
||||||
|
silent: params.silent,
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -192,6 +198,7 @@ export async function deliverOutboundPayloads(params: {
|
|||||||
text?: string;
|
text?: string;
|
||||||
mediaUrls?: string[];
|
mediaUrls?: string[];
|
||||||
};
|
};
|
||||||
|
silent?: boolean;
|
||||||
}): Promise<OutboundDeliveryResult[]> {
|
}): Promise<OutboundDeliveryResult[]> {
|
||||||
const { cfg, channel, to, payloads } = params;
|
const { cfg, channel, to, payloads } = params;
|
||||||
const accountId = params.accountId;
|
const accountId = params.accountId;
|
||||||
@@ -208,6 +215,7 @@ export async function deliverOutboundPayloads(params: {
|
|||||||
replyToId: params.replyToId,
|
replyToId: params.replyToId,
|
||||||
threadId: params.threadId,
|
threadId: params.threadId,
|
||||||
gifPlayback: params.gifPlayback,
|
gifPlayback: params.gifPlayback,
|
||||||
|
silent: params.silent,
|
||||||
});
|
});
|
||||||
const textLimit = handler.chunker
|
const textLimit = handler.chunker
|
||||||
? resolveTextChunkLimit(cfg, channel, accountId, {
|
? resolveTextChunkLimit(cfg, channel, accountId, {
|
||||||
|
|||||||
@@ -820,6 +820,7 @@ async function handleSendAction(ctx: ResolvedActionContext): Promise<MessageActi
|
|||||||
params.message = message;
|
params.message = message;
|
||||||
const gifPlayback = readBooleanParam(params, "gifPlayback") ?? false;
|
const gifPlayback = readBooleanParam(params, "gifPlayback") ?? false;
|
||||||
const bestEffort = readBooleanParam(params, "bestEffort");
|
const bestEffort = readBooleanParam(params, "bestEffort");
|
||||||
|
const silent = readBooleanParam(params, "silent");
|
||||||
|
|
||||||
const replyToId = readStringParam(params, "replyTo");
|
const replyToId = readStringParam(params, "replyTo");
|
||||||
const threadId = readStringParam(params, "threadId");
|
const threadId = readStringParam(params, "threadId");
|
||||||
@@ -884,6 +885,7 @@ async function handleSendAction(ctx: ResolvedActionContext): Promise<MessageActi
|
|||||||
}
|
}
|
||||||
: undefined,
|
: undefined,
|
||||||
abortSignal,
|
abortSignal,
|
||||||
|
silent: silent ?? undefined,
|
||||||
},
|
},
|
||||||
to,
|
to,
|
||||||
message,
|
message,
|
||||||
|
|||||||
@@ -51,6 +51,7 @@ type MessageSendParams = {
|
|||||||
mediaUrls?: string[];
|
mediaUrls?: string[];
|
||||||
};
|
};
|
||||||
abortSignal?: AbortSignal;
|
abortSignal?: AbortSignal;
|
||||||
|
silent?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type MessageSendResult = {
|
export type MessageSendResult = {
|
||||||
@@ -173,6 +174,7 @@ export async function sendMessage(params: MessageSendParams): Promise<MessageSen
|
|||||||
deps: params.deps,
|
deps: params.deps,
|
||||||
bestEffort: params.bestEffort,
|
bestEffort: params.bestEffort,
|
||||||
abortSignal: params.abortSignal,
|
abortSignal: params.abortSignal,
|
||||||
|
silent: params.silent,
|
||||||
mirror: params.mirror
|
mirror: params.mirror
|
||||||
? {
|
? {
|
||||||
...params.mirror,
|
...params.mirror,
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ export type OutboundSendContext = {
|
|||||||
mediaUrls?: string[];
|
mediaUrls?: string[];
|
||||||
};
|
};
|
||||||
abortSignal?: AbortSignal;
|
abortSignal?: AbortSignal;
|
||||||
|
silent?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
function extractToolPayload(result: AgentToolResult<unknown>): unknown {
|
function extractToolPayload(result: AgentToolResult<unknown>): unknown {
|
||||||
@@ -128,6 +129,7 @@ export async function executeSendAction(params: {
|
|||||||
gateway: params.ctx.gateway,
|
gateway: params.ctx.gateway,
|
||||||
mirror: params.ctx.mirror,
|
mirror: params.ctx.mirror,
|
||||||
abortSignal: params.ctx.abortSignal,
|
abortSignal: params.ctx.abortSignal,
|
||||||
|
silent: params.ctx.silent,
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
Reference in New Issue
Block a user