* refactor: move WhatsApp channel from src/web/ to extensions/whatsapp/ Move all WhatsApp implementation code (77 source/test files + 9 channel plugin files) from src/web/ and src/channels/plugins/*/whatsapp* to extensions/whatsapp/src/. - Leave thin re-export shims at all original locations so cross-cutting imports continue to resolve - Update plugin-sdk/whatsapp.ts to only re-export generic framework utilities; channel-specific functions imported locally by the extension - Update vi.mock paths in 15 cross-cutting test files - Rename outbound.ts -> send.ts to match extension naming conventions and avoid false positive in cfg-threading guard test - Widen tsconfig.plugin-sdk.dts.json rootDir to support shim->extension cross-directory references Part of the core-channels-to-extensions migration (PR 6/10). * style: format WhatsApp extension files * fix: correct stale import paths in WhatsApp extension tests Fix vi.importActual, test mock, and hardcoded source paths that weren't updated during the file move: - media.test.ts: vi.importActual path - onboarding.test.ts: vi.importActual path - test-helpers.ts: test/mocks/baileys.js path - monitor-inbox.test-harness.ts: incomplete media/store mock - login.test.ts: hardcoded source file path - message-action-runner.media.test.ts: vi.mock/importActual path
114 lines
3.6 KiB
TypeScript
114 lines
3.6 KiB
TypeScript
import type { AnyMessageContent, WAPresence } from "@whiskeysockets/baileys";
|
|
import { recordChannelActivity } from "../../../../src/infra/channel-activity.js";
|
|
import { toWhatsappJid } from "../../../../src/utils.js";
|
|
import type { ActiveWebSendOptions } from "../active-listener.js";
|
|
|
|
function recordWhatsAppOutbound(accountId: string) {
|
|
recordChannelActivity({
|
|
channel: "whatsapp",
|
|
accountId,
|
|
direction: "outbound",
|
|
});
|
|
}
|
|
|
|
function resolveOutboundMessageId(result: unknown): string {
|
|
return typeof result === "object" && result && "key" in result
|
|
? String((result as { key?: { id?: string } }).key?.id ?? "unknown")
|
|
: "unknown";
|
|
}
|
|
|
|
export function createWebSendApi(params: {
|
|
sock: {
|
|
sendMessage: (jid: string, content: AnyMessageContent) => Promise<unknown>;
|
|
sendPresenceUpdate: (presence: WAPresence, jid?: string) => Promise<unknown>;
|
|
};
|
|
defaultAccountId: string;
|
|
}) {
|
|
return {
|
|
sendMessage: async (
|
|
to: string,
|
|
text: string,
|
|
mediaBuffer?: Buffer,
|
|
mediaType?: string,
|
|
sendOptions?: ActiveWebSendOptions,
|
|
): Promise<{ messageId: string }> => {
|
|
const jid = toWhatsappJid(to);
|
|
let payload: AnyMessageContent;
|
|
if (mediaBuffer && mediaType) {
|
|
if (mediaType.startsWith("image/")) {
|
|
payload = {
|
|
image: mediaBuffer,
|
|
caption: text || undefined,
|
|
mimetype: mediaType,
|
|
};
|
|
} else if (mediaType.startsWith("audio/")) {
|
|
payload = { audio: mediaBuffer, ptt: true, mimetype: mediaType };
|
|
} else if (mediaType.startsWith("video/")) {
|
|
const gifPlayback = sendOptions?.gifPlayback;
|
|
payload = {
|
|
video: mediaBuffer,
|
|
caption: text || undefined,
|
|
mimetype: mediaType,
|
|
...(gifPlayback ? { gifPlayback: true } : {}),
|
|
};
|
|
} else {
|
|
const fileName = sendOptions?.fileName?.trim() || "file";
|
|
payload = {
|
|
document: mediaBuffer,
|
|
fileName,
|
|
caption: text || undefined,
|
|
mimetype: mediaType,
|
|
};
|
|
}
|
|
} else {
|
|
payload = { text };
|
|
}
|
|
const result = await params.sock.sendMessage(jid, payload);
|
|
const accountId = sendOptions?.accountId ?? params.defaultAccountId;
|
|
recordWhatsAppOutbound(accountId);
|
|
const messageId = resolveOutboundMessageId(result);
|
|
return { messageId };
|
|
},
|
|
sendPoll: async (
|
|
to: string,
|
|
poll: { question: string; options: string[]; maxSelections?: number },
|
|
): Promise<{ messageId: string }> => {
|
|
const jid = toWhatsappJid(to);
|
|
const result = await params.sock.sendMessage(jid, {
|
|
poll: {
|
|
name: poll.question,
|
|
values: poll.options,
|
|
selectableCount: poll.maxSelections ?? 1,
|
|
},
|
|
} as AnyMessageContent);
|
|
recordWhatsAppOutbound(params.defaultAccountId);
|
|
const messageId = resolveOutboundMessageId(result);
|
|
return { messageId };
|
|
},
|
|
sendReaction: async (
|
|
chatJid: string,
|
|
messageId: string,
|
|
emoji: string,
|
|
fromMe: boolean,
|
|
participant?: string,
|
|
): Promise<void> => {
|
|
const jid = toWhatsappJid(chatJid);
|
|
await params.sock.sendMessage(jid, {
|
|
react: {
|
|
text: emoji,
|
|
key: {
|
|
remoteJid: jid,
|
|
id: messageId,
|
|
fromMe,
|
|
participant: participant ? toWhatsappJid(participant) : undefined,
|
|
},
|
|
},
|
|
} as AnyMessageContent);
|
|
},
|
|
sendComposingTo: async (to: string): Promise<void> => {
|
|
const jid = toWhatsappJid(to);
|
|
await params.sock.sendPresenceUpdate("composing", jid);
|
|
},
|
|
} as const;
|
|
}
|