mirror of
https://github.com/openclaw/openclaw.git
synced 2026-04-19 11:58:38 +00:00
refactor: share whatsapp outbound adapter base
This commit is contained in:
@@ -8,6 +8,7 @@ import {
|
|||||||
buildChannelConfigSchema,
|
buildChannelConfigSchema,
|
||||||
collectWhatsAppStatusIssues,
|
collectWhatsAppStatusIssues,
|
||||||
createActionGate,
|
createActionGate,
|
||||||
|
createWhatsAppOutboundBase,
|
||||||
DEFAULT_ACCOUNT_ID,
|
DEFAULT_ACCOUNT_ID,
|
||||||
getChatChannelMeta,
|
getChatChannelMeta,
|
||||||
listWhatsAppAccountIds,
|
listWhatsAppAccountIds,
|
||||||
@@ -283,52 +284,16 @@ export const whatsappPlugin: ChannelPlugin<ResolvedWhatsAppAccount> = {
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
outbound: {
|
outbound: createWhatsAppOutboundBase({
|
||||||
deliveryMode: "gateway",
|
|
||||||
chunker: (text, limit) => getWhatsAppRuntime().channel.text.chunkText(text, limit),
|
chunker: (text, limit) => getWhatsAppRuntime().channel.text.chunkText(text, limit),
|
||||||
chunkerMode: "text",
|
sendMessageWhatsApp: async (...args) =>
|
||||||
textChunkLimit: 4000,
|
await getWhatsAppRuntime().channel.whatsapp.sendMessageWhatsApp(...args),
|
||||||
pollMaxOptions: 12,
|
sendPollWhatsApp: async (...args) =>
|
||||||
|
await getWhatsAppRuntime().channel.whatsapp.sendPollWhatsApp(...args),
|
||||||
|
shouldLogVerbose: () => getWhatsAppRuntime().logging.shouldLogVerbose(),
|
||||||
resolveTarget: ({ to, allowFrom, mode }) =>
|
resolveTarget: ({ to, allowFrom, mode }) =>
|
||||||
resolveWhatsAppOutboundTarget({ to, allowFrom, mode }),
|
resolveWhatsAppOutboundTarget({ to, allowFrom, mode }),
|
||||||
sendText: async ({ cfg, to, text, accountId, deps, gifPlayback }) => {
|
}),
|
||||||
const send = deps?.sendWhatsApp ?? getWhatsAppRuntime().channel.whatsapp.sendMessageWhatsApp;
|
|
||||||
const result = await send(to, text, {
|
|
||||||
verbose: false,
|
|
||||||
cfg,
|
|
||||||
accountId: accountId ?? undefined,
|
|
||||||
gifPlayback,
|
|
||||||
});
|
|
||||||
return { channel: "whatsapp", ...result };
|
|
||||||
},
|
|
||||||
sendMedia: async ({
|
|
||||||
cfg,
|
|
||||||
to,
|
|
||||||
text,
|
|
||||||
mediaUrl,
|
|
||||||
mediaLocalRoots,
|
|
||||||
accountId,
|
|
||||||
deps,
|
|
||||||
gifPlayback,
|
|
||||||
}) => {
|
|
||||||
const send = deps?.sendWhatsApp ?? getWhatsAppRuntime().channel.whatsapp.sendMessageWhatsApp;
|
|
||||||
const result = await send(to, text, {
|
|
||||||
verbose: false,
|
|
||||||
cfg,
|
|
||||||
mediaUrl,
|
|
||||||
mediaLocalRoots,
|
|
||||||
accountId: accountId ?? undefined,
|
|
||||||
gifPlayback,
|
|
||||||
});
|
|
||||||
return { channel: "whatsapp", ...result };
|
|
||||||
},
|
|
||||||
sendPoll: async ({ cfg, to, poll, accountId }) =>
|
|
||||||
await getWhatsAppRuntime().channel.whatsapp.sendPollWhatsApp(to, poll, {
|
|
||||||
verbose: getWhatsAppRuntime().logging.shouldLogVerbose(),
|
|
||||||
accountId: accountId ?? undefined,
|
|
||||||
cfg,
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
auth: {
|
auth: {
|
||||||
login: async ({ cfg, accountId, runtime, verbose }) => {
|
login: async ({ cfg, accountId, runtime, verbose }) => {
|
||||||
const resolvedAccountId = accountId?.trim() || resolveDefaultWhatsAppAccountId(cfg);
|
const resolvedAccountId = accountId?.trim() || resolveDefaultWhatsAppAccountId(cfg);
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { chunkText } from "../../../auto-reply/chunk.js";
|
import { chunkText } from "../../../auto-reply/chunk.js";
|
||||||
import { shouldLogVerbose } from "../../../globals.js";
|
import { shouldLogVerbose } from "../../../globals.js";
|
||||||
import { sendPollWhatsApp } from "../../../web/outbound.js";
|
import { sendPollWhatsApp } from "../../../web/outbound.js";
|
||||||
import { resolveWhatsAppOutboundTarget } from "../../../whatsapp/resolve-outbound-target.js";
|
|
||||||
import type { ChannelOutboundAdapter } from "../types.js";
|
import type { ChannelOutboundAdapter } from "../types.js";
|
||||||
|
import { createWhatsAppOutboundBase } from "../whatsapp-shared.js";
|
||||||
import { sendTextMediaPayload } from "./direct-text-media.js";
|
import { sendTextMediaPayload } from "./direct-text-media.js";
|
||||||
|
|
||||||
function trimLeadingWhitespace(text: string | undefined): string {
|
function trimLeadingWhitespace(text: string | undefined): string {
|
||||||
@@ -10,13 +10,15 @@ function trimLeadingWhitespace(text: string | undefined): string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const whatsappOutbound: ChannelOutboundAdapter = {
|
export const whatsappOutbound: ChannelOutboundAdapter = {
|
||||||
deliveryMode: "gateway",
|
...createWhatsAppOutboundBase({
|
||||||
chunker: chunkText,
|
chunker: chunkText,
|
||||||
chunkerMode: "text",
|
sendMessageWhatsApp: async (...args) =>
|
||||||
textChunkLimit: 4000,
|
(await import("../../../web/outbound.js")).sendMessageWhatsApp(...args),
|
||||||
pollMaxOptions: 12,
|
sendPollWhatsApp,
|
||||||
resolveTarget: ({ to, allowFrom, mode }) =>
|
shouldLogVerbose,
|
||||||
resolveWhatsAppOutboundTarget({ to, allowFrom, mode }),
|
normalizeText: trimLeadingWhitespace,
|
||||||
|
skipEmptyText: true,
|
||||||
|
}),
|
||||||
sendPayload: async (ctx) => {
|
sendPayload: async (ctx) => {
|
||||||
const text = trimLeadingWhitespace(ctx.payload.text);
|
const text = trimLeadingWhitespace(ctx.payload.text);
|
||||||
const hasMedia = Boolean(ctx.payload.mediaUrl) || (ctx.payload.mediaUrls?.length ?? 0) > 0;
|
const hasMedia = Boolean(ctx.payload.mediaUrl) || (ctx.payload.mediaUrls?.length ?? 0) > 0;
|
||||||
@@ -35,39 +37,4 @@ export const whatsappOutbound: ChannelOutboundAdapter = {
|
|||||||
adapter: whatsappOutbound,
|
adapter: whatsappOutbound,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
sendText: async ({ cfg, to, text, accountId, deps, gifPlayback }) => {
|
|
||||||
const normalizedText = trimLeadingWhitespace(text);
|
|
||||||
if (!normalizedText) {
|
|
||||||
return { channel: "whatsapp", messageId: "" };
|
|
||||||
}
|
|
||||||
const send =
|
|
||||||
deps?.sendWhatsApp ?? (await import("../../../web/outbound.js")).sendMessageWhatsApp;
|
|
||||||
const result = await send(to, normalizedText, {
|
|
||||||
verbose: false,
|
|
||||||
cfg,
|
|
||||||
accountId: accountId ?? undefined,
|
|
||||||
gifPlayback,
|
|
||||||
});
|
|
||||||
return { channel: "whatsapp", ...result };
|
|
||||||
},
|
|
||||||
sendMedia: async ({ cfg, to, text, mediaUrl, mediaLocalRoots, accountId, deps, gifPlayback }) => {
|
|
||||||
const normalizedText = trimLeadingWhitespace(text);
|
|
||||||
const send =
|
|
||||||
deps?.sendWhatsApp ?? (await import("../../../web/outbound.js")).sendMessageWhatsApp;
|
|
||||||
const result = await send(to, normalizedText, {
|
|
||||||
verbose: false,
|
|
||||||
cfg,
|
|
||||||
mediaUrl,
|
|
||||||
mediaLocalRoots,
|
|
||||||
accountId: accountId ?? undefined,
|
|
||||||
gifPlayback,
|
|
||||||
});
|
|
||||||
return { channel: "whatsapp", ...result };
|
|
||||||
},
|
|
||||||
sendPoll: async ({ cfg, to, poll, accountId }) =>
|
|
||||||
await sendPollWhatsApp(to, poll, {
|
|
||||||
verbose: shouldLogVerbose(),
|
|
||||||
accountId: accountId ?? undefined,
|
|
||||||
cfg,
|
|
||||||
}),
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
|
import type { PluginRuntimeChannel } from "../../plugins/runtime/types-channel.js";
|
||||||
import { escapeRegExp } from "../../utils.js";
|
import { escapeRegExp } from "../../utils.js";
|
||||||
|
import { resolveWhatsAppOutboundTarget } from "../../whatsapp/resolve-outbound-target.js";
|
||||||
|
import type { ChannelOutboundAdapter } from "./types.js";
|
||||||
|
|
||||||
export const WHATSAPP_GROUP_INTRO_HINT =
|
export const WHATSAPP_GROUP_INTRO_HINT =
|
||||||
"WhatsApp IDs: SenderId is the participant JID (group participant id).";
|
"WhatsApp IDs: SenderId is the participant JID (group participant id).";
|
||||||
@@ -15,3 +18,89 @@ export function resolveWhatsAppMentionStripPatterns(ctx: { To?: string | null })
|
|||||||
const escaped = escapeRegExp(selfE164);
|
const escaped = escapeRegExp(selfE164);
|
||||||
return [escaped, `@${escaped}`];
|
return [escaped, `@${escaped}`];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type WhatsAppChunker = NonNullable<ChannelOutboundAdapter["chunker"]>;
|
||||||
|
type WhatsAppSendMessage = PluginRuntimeChannel["whatsapp"]["sendMessageWhatsApp"];
|
||||||
|
type WhatsAppSendPoll = PluginRuntimeChannel["whatsapp"]["sendPollWhatsApp"];
|
||||||
|
|
||||||
|
type CreateWhatsAppOutboundBaseParams = {
|
||||||
|
chunker: WhatsAppChunker;
|
||||||
|
sendMessageWhatsApp: WhatsAppSendMessage;
|
||||||
|
sendPollWhatsApp: WhatsAppSendPoll;
|
||||||
|
shouldLogVerbose: () => boolean;
|
||||||
|
resolveTarget?: ChannelOutboundAdapter["resolveTarget"];
|
||||||
|
normalizeText?: (text: string | undefined) => string;
|
||||||
|
skipEmptyText?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function createWhatsAppOutboundBase({
|
||||||
|
chunker,
|
||||||
|
sendMessageWhatsApp,
|
||||||
|
sendPollWhatsApp,
|
||||||
|
shouldLogVerbose,
|
||||||
|
resolveTarget = ({ to, allowFrom, mode }) =>
|
||||||
|
resolveWhatsAppOutboundTarget({ to, allowFrom, mode }),
|
||||||
|
normalizeText = (text) => text ?? "",
|
||||||
|
skipEmptyText = false,
|
||||||
|
}: CreateWhatsAppOutboundBaseParams): Pick<
|
||||||
|
ChannelOutboundAdapter,
|
||||||
|
| "deliveryMode"
|
||||||
|
| "chunker"
|
||||||
|
| "chunkerMode"
|
||||||
|
| "textChunkLimit"
|
||||||
|
| "pollMaxOptions"
|
||||||
|
| "resolveTarget"
|
||||||
|
| "sendText"
|
||||||
|
| "sendMedia"
|
||||||
|
| "sendPoll"
|
||||||
|
> {
|
||||||
|
return {
|
||||||
|
deliveryMode: "gateway",
|
||||||
|
chunker,
|
||||||
|
chunkerMode: "text",
|
||||||
|
textChunkLimit: 4000,
|
||||||
|
pollMaxOptions: 12,
|
||||||
|
resolveTarget,
|
||||||
|
sendText: async ({ cfg, to, text, accountId, deps, gifPlayback }) => {
|
||||||
|
const normalizedText = normalizeText(text);
|
||||||
|
if (skipEmptyText && !normalizedText) {
|
||||||
|
return { channel: "whatsapp", messageId: "" };
|
||||||
|
}
|
||||||
|
const send = deps?.sendWhatsApp ?? sendMessageWhatsApp;
|
||||||
|
const result = await send(to, normalizedText, {
|
||||||
|
verbose: false,
|
||||||
|
cfg,
|
||||||
|
accountId: accountId ?? undefined,
|
||||||
|
gifPlayback,
|
||||||
|
});
|
||||||
|
return { channel: "whatsapp", ...result };
|
||||||
|
},
|
||||||
|
sendMedia: async ({
|
||||||
|
cfg,
|
||||||
|
to,
|
||||||
|
text,
|
||||||
|
mediaUrl,
|
||||||
|
mediaLocalRoots,
|
||||||
|
accountId,
|
||||||
|
deps,
|
||||||
|
gifPlayback,
|
||||||
|
}) => {
|
||||||
|
const send = deps?.sendWhatsApp ?? sendMessageWhatsApp;
|
||||||
|
const result = await send(to, normalizeText(text), {
|
||||||
|
verbose: false,
|
||||||
|
cfg,
|
||||||
|
mediaUrl,
|
||||||
|
mediaLocalRoots,
|
||||||
|
accountId: accountId ?? undefined,
|
||||||
|
gifPlayback,
|
||||||
|
});
|
||||||
|
return { channel: "whatsapp", ...result };
|
||||||
|
},
|
||||||
|
sendPoll: async ({ cfg, to, poll, accountId }) =>
|
||||||
|
await sendPollWhatsApp(to, poll, {
|
||||||
|
verbose: shouldLogVerbose(),
|
||||||
|
accountId: accountId ?? undefined,
|
||||||
|
cfg,
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ export {
|
|||||||
resolveWhatsAppGroupToolPolicy,
|
resolveWhatsAppGroupToolPolicy,
|
||||||
} from "../channels/plugins/group-mentions.js";
|
} from "../channels/plugins/group-mentions.js";
|
||||||
export {
|
export {
|
||||||
|
createWhatsAppOutboundBase,
|
||||||
resolveWhatsAppGroupIntroHint,
|
resolveWhatsAppGroupIntroHint,
|
||||||
resolveWhatsAppMentionStripPatterns,
|
resolveWhatsAppMentionStripPatterns,
|
||||||
} from "../channels/plugins/whatsapp-shared.js";
|
} from "../channels/plugins/whatsapp-shared.js";
|
||||||
|
|||||||
Reference in New Issue
Block a user