From f68714ec8eed6ee06924963c3cc91313eb550925 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Wed, 3 Dec 2025 13:15:46 +0000 Subject: [PATCH] fix(web): unwrap ephemeral/view-once and keep mentions --- src/web/inbound.ts | 27 +++++++++++++++++---- src/web/monitor-inbox.test.ts | 45 +++++++++++++++++++++++++++++++++++ 2 files changed, 67 insertions(+), 5 deletions(-) diff --git a/src/web/inbound.ts b/src/web/inbound.ts index 0127d4653..b64831218 100644 --- a/src/web/inbound.ts +++ b/src/web/inbound.ts @@ -166,9 +166,10 @@ 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 = - msg.message?.extendedTextMessage?.contextInfo?.mentionedJid ?? - msg.message?.extendedTextMessage?.contextInfo?.quotedMessage + unwrapped?.extendedTextMessage?.contextInfo?.mentionedJid ?? + unwrapped?.extendedTextMessage?.contextInfo?.quotedMessage ?.extendedTextMessage?.contextInfo?.mentionedJid; const senderName = msg.pushName ?? undefined; inboundLogger.info( @@ -302,9 +303,24 @@ export async function monitorWebInbox(options: { } as const; } +function unwrapMessage(message: proto.IMessage | undefined): proto.IMessage | undefined { + if (!message) return undefined; + if (message.ephemeralMessage?.message) { + return unwrapMessage(message.ephemeralMessage.message as proto.IMessage); + } + if (message.viewOnceMessage?.message) { + return unwrapMessage(message.viewOnceMessage.message as proto.IMessage); + } + if (message.viewOnceMessageV2?.message) { + return unwrapMessage(message.viewOnceMessageV2.message as proto.IMessage); + } + return message; +} + export function extractText( - message: proto.IMessage | undefined, + rawMessage: proto.IMessage | undefined, ): string | undefined { + const message = unwrapMessage(rawMessage); if (!message) return undefined; if (typeof message.conversation === "string" && message.conversation.trim()) { return message.conversation.trim(); @@ -318,8 +334,9 @@ export function extractText( } export function extractMediaPlaceholder( - message: proto.IMessage | undefined, + rawMessage: proto.IMessage | undefined, ): string | undefined { + const message = unwrapMessage(rawMessage); if (!message) return undefined; if (message.imageMessage) return ""; if (message.videoMessage) return ""; @@ -333,7 +350,7 @@ async function downloadInboundMedia( msg: proto.IWebMessageInfo, sock: Awaited>, ): Promise<{ buffer: Buffer; mimetype?: string } | undefined> { - const message = msg.message; + const message = unwrapMessage(msg.message as proto.IMessage | undefined); if (!message) return undefined; const mimetype = message.imageMessage?.mimetype ?? diff --git a/src/web/monitor-inbox.test.ts b/src/web/monitor-inbox.test.ts index e1a89d54d..1c3b0af29 100644 --- a/src/web/monitor-inbox.test.ts +++ b/src/web/monitor-inbox.test.ts @@ -270,6 +270,51 @@ describe("web monitor inbox", () => { await listener.close(); }); + it("unwraps ephemeral messages, preserves mentions, and still delivers group pings", async () => { + const onMessage = vi.fn(); + const listener = await monitorWebInbox({ verbose: false, onMessage }); + const sock = await createWaSocket(); + const upsert = { + type: "notify", + messages: [ + { + key: { + id: "grp-ephem", + fromMe: false, + remoteJid: "424242@g.us", + participant: "888@s.whatsapp.net", + }, + message: { + ephemeralMessage: { + message: { + extendedTextMessage: { + text: "oh hey @Clawd UK !", + contextInfo: { mentionedJid: ["123@s.whatsapp.net"] }, + }, + }, + }, + }, + }, + ], + }; + + sock.ev.emit("messages.upsert", upsert); + await new Promise((resolve) => setImmediate(resolve)); + + expect(onMessage).toHaveBeenCalledTimes(1); + expect(onMessage).toHaveBeenCalledWith( + expect.objectContaining({ + chatType: "group", + conversationId: "424242@g.us", + body: "oh hey @Clawd UK !", + mentionedJids: ["123@s.whatsapp.net"], + senderE164: "+888", + }), + ); + + await listener.close(); + }); + it("still forwards group messages (with sender info) even when allowFrom is restrictive", async () => { mockLoadConfig.mockReturnValue({ inbound: {