diff --git a/src/web/inbound.media.test.ts b/src/web/inbound.media.test.ts index b8d60f0af..c975cb86e 100644 --- a/src/web/inbound.media.test.ts +++ b/src/web/inbound.media.test.ts @@ -117,4 +117,52 @@ describe("web inbound media saves with extension", () => { await listener.close(); }); + + it("extracts mentions from media captions", async () => { + const onMessage = vi.fn(); + const listener = await monitorWebInbox({ verbose: false, onMessage }); + const { createWaSocket } = await import("./session.js"); + const realSock = await ( + createWaSocket as unknown as () => Promise<{ + ev: import("node:events").EventEmitter; + }> + )(); + + const upsert = { + type: "notify", + messages: [ + { + key: { + id: "img2", + fromMe: false, + remoteJid: "123@g.us", + participant: "999@s.whatsapp.net", + }, + message: { + messageContextInfo: {}, + imageMessage: { + caption: "@bot", + contextInfo: { mentionedJid: ["999@s.whatsapp.net"] }, + mimetype: "image/jpeg", + }, + }, + messageTimestamp: 1_700_000_002, + }, + ], + }; + + realSock.ev.emit("messages.upsert", upsert); + + for (let i = 0; i < 10; i++) { + if (onMessage.mock.calls.length > 0) break; + await new Promise((resolve) => setTimeout(resolve, 5)); + } + + expect(onMessage).toHaveBeenCalledTimes(1); + const msg = onMessage.mock.calls[0][0]; + expect(msg.chatType).toBe("group"); + expect(msg.mentionedJids).toEqual(["999@s.whatsapp.net"]); + + await listener.close(); + }); }); diff --git a/src/web/inbound.ts b/src/web/inbound.ts index cd822fb89..c09dd1b1e 100644 --- a/src/web/inbound.ts +++ b/src/web/inbound.ts @@ -202,11 +202,9 @@ export async function monitorWebInbox(options: { const timestamp = msg.messageTimestamp ? Number(msg.messageTimestamp) * 1000 : undefined; - const unwrapped = unwrapMessage(msg.message as proto.IMessage | undefined); - const mentionedJids = - unwrapped?.extendedTextMessage?.contextInfo?.mentionedJid ?? - unwrapped?.extendedTextMessage?.contextInfo?.quotedMessage - ?.extendedTextMessage?.contextInfo?.mentionedJid; + const mentionedJids = extractMentionedJids( + msg.message as proto.IMessage | undefined, + ); const senderName = msg.pushName ?? undefined; inboundLogger.info( { @@ -355,6 +353,31 @@ function unwrapMessage(message: proto.IMessage | undefined): proto.IMessage | un return message; } +function extractMentionedJids( + rawMessage: proto.IMessage | undefined, +): string[] | undefined { + const message = unwrapMessage(rawMessage); + if (!message) return undefined; + + const candidates: (string[] | undefined)[] = [ + message.extendedTextMessage?.contextInfo?.mentionedJid, + message.extendedTextMessage?.contextInfo?.quotedMessage?.extendedTextMessage + ?.contextInfo?.mentionedJid, + message.imageMessage?.contextInfo?.mentionedJid, + message.videoMessage?.contextInfo?.mentionedJid, + message.documentMessage?.contextInfo?.mentionedJid, + message.audioMessage?.contextInfo?.mentionedJid, + message.stickerMessage?.contextInfo?.mentionedJid, + message.buttonsResponseMessage?.contextInfo?.mentionedJid, + message.listResponseMessage?.contextInfo?.mentionedJid, + ]; + + const flattened = candidates.flat().filter((j): j is string => !!j); + if (flattened.length === 0) return undefined; + // De-dupe + return Array.from(new Set(flattened)); +} + export function extractText( rawMessage: proto.IMessage | undefined, ): string | undefined {