mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-05 15:20:23 +00:00
refactor: unify approval forwarding and rendering
This commit is contained in:
@@ -240,4 +240,50 @@ describe("discordOutbound", () => {
|
||||
channelId: "ch-1",
|
||||
});
|
||||
});
|
||||
|
||||
it("neutralizes approval mentions only for approval payloads", async () => {
|
||||
await discordOutbound.sendPayload?.({
|
||||
cfg: {},
|
||||
to: "channel:123456",
|
||||
text: "",
|
||||
payload: {
|
||||
text: "Approval @everyone <@123> <#456>",
|
||||
channelData: {
|
||||
execApproval: {
|
||||
approvalId: "req-1",
|
||||
approvalSlug: "req-1",
|
||||
},
|
||||
},
|
||||
},
|
||||
accountId: "default",
|
||||
});
|
||||
|
||||
expect(hoisted.sendMessageDiscordMock).toHaveBeenCalledWith(
|
||||
"channel:123456",
|
||||
"Approval @\u200beveryone <@\u200b123> <#\u200b456>",
|
||||
expect.objectContaining({
|
||||
accountId: "default",
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it("leaves non-approval mentions unchanged", async () => {
|
||||
await discordOutbound.sendPayload?.({
|
||||
cfg: {},
|
||||
to: "channel:123456",
|
||||
text: "",
|
||||
payload: {
|
||||
text: "Hello @everyone",
|
||||
},
|
||||
accountId: "default",
|
||||
});
|
||||
|
||||
expect(hoisted.sendMessageDiscordMock).toHaveBeenCalledWith(
|
||||
"channel:123456",
|
||||
"Hello @everyone",
|
||||
expect.objectContaining({
|
||||
accountId: "default",
|
||||
}),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -26,6 +26,33 @@ import { buildDiscordInteractiveComponents } from "./shared-interactive.js";
|
||||
|
||||
export const DISCORD_TEXT_CHUNK_LIMIT = 2000;
|
||||
|
||||
function hasApprovalChannelData(payload: { channelData?: unknown }): boolean {
|
||||
const channelData = payload.channelData;
|
||||
if (!channelData || typeof channelData !== "object" || Array.isArray(channelData)) {
|
||||
return false;
|
||||
}
|
||||
return Boolean((channelData as { execApproval?: unknown }).execApproval);
|
||||
}
|
||||
|
||||
function neutralizeDiscordApprovalMentions(value: string): string {
|
||||
return value
|
||||
.replace(/@everyone/gi, "@\u200beveryone")
|
||||
.replace(/@here/gi, "@\u200bhere")
|
||||
.replace(/<@/g, "<@\u200b")
|
||||
.replace(/<#/g, "<#\u200b");
|
||||
}
|
||||
|
||||
function normalizeDiscordApprovalPayload<T extends { text?: string; channelData?: unknown }>(
|
||||
payload: T,
|
||||
): T {
|
||||
return hasApprovalChannelData(payload) && payload.text
|
||||
? {
|
||||
...payload,
|
||||
text: neutralizeDiscordApprovalMentions(payload.text),
|
||||
}
|
||||
: payload;
|
||||
}
|
||||
|
||||
function resolveDiscordOutboundTarget(params: {
|
||||
to: string;
|
||||
threadId?: string | number | null;
|
||||
@@ -96,12 +123,13 @@ export const discordOutbound: ChannelOutboundAdapter = {
|
||||
chunker: null,
|
||||
textChunkLimit: DISCORD_TEXT_CHUNK_LIMIT,
|
||||
pollMaxOptions: 10,
|
||||
normalizePayload: ({ payload }) => normalizeDiscordApprovalPayload(payload),
|
||||
resolveTarget: ({ to }) => normalizeDiscordOutboundTarget(to),
|
||||
sendPayload: async (ctx) => {
|
||||
const payload = {
|
||||
const payload = normalizeDiscordApprovalPayload({
|
||||
...ctx.payload,
|
||||
text: ctx.payload.text ?? "",
|
||||
};
|
||||
});
|
||||
const discordData = payload.channelData?.discord as
|
||||
| { components?: DiscordComponentMessageSpec }
|
||||
| undefined;
|
||||
|
||||
Reference in New Issue
Block a user