openclaw/src/channels/plugins/whatsapp-shared.ts
2026-03-15 08:44:12 -07:00

114 lines
3.5 KiB
TypeScript

import { resolveOutboundSendDep } from "../../infra/outbound/send-deps.js";
import type { PluginRuntimeChannel } from "../../plugins/runtime/types-channel.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 =
"WhatsApp IDs: SenderId is the participant JID (group participant id).";
export function resolveWhatsAppGroupIntroHint(): string {
return WHATSAPP_GROUP_INTRO_HINT;
}
export function resolveWhatsAppMentionStripPatterns(ctx: { To?: string | null }): string[] {
const selfE164 = (ctx.To ?? "").replace(/^whatsapp:/, "");
if (!selfE164) {
return [];
}
const escaped = escapeRegExp(selfE164);
return [escaped, `@${escaped}`];
}
export function resolveWhatsAppMentionStripRegexes(ctx: { To?: string | null }): RegExp[] {
return resolveWhatsAppMentionStripPatterns(ctx).map((pattern) => new RegExp(pattern, "g"));
}
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 =
resolveOutboundSendDep<WhatsAppSendMessage>(deps, "whatsapp") ?? 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 =
resolveOutboundSendDep<WhatsAppSendMessage>(deps, "whatsapp") ?? 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,
}),
};
}