From 200414c47e32227cc91813cd286dd55beaaceca5 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 7 Feb 2026 02:50:20 +0000 Subject: [PATCH] fix(carousel): switch to viewOnceMessage V1 + stop skipping own devices 1. Switch wrapper from viewOnceMessageV2 (field 55) to viewOnceMessage V1 (field 37). V2 renders on mobile but NOT on WhatsApp Web/Desktop. V1 is what ckptw, Vkazee, and most working Baileys forks use. Previous error 479 with V1 was caused by missing root header and fromObject() corruption - both now fixed. 2. Stop skipping own linked devices for carousel messages. This was preventing the sender's WhatsApp Web from receiving the carousel. 3. Allow DSM (deviceSentMessage) wrapper for carousel - no longer skip it for own devices or retry paths. https://claude.ai/code/session_018DkDxsjWzM131jy3ivWjZp --- src/Socket/messages-send.ts | 17 +++-------------- src/Utils/messages.ts | 17 +++++++++-------- 2 files changed, 12 insertions(+), 22 deletions(-) diff --git a/src/Socket/messages-send.ts b/src/Socket/messages-send.ts index 3a9b1248..543770b0 100644 --- a/src/Socket/messages-send.ts +++ b/src/Socket/messages-send.ts @@ -1125,9 +1125,6 @@ export const makeMessagesSocket = (config: SocketConfig) => { const { user: mePnUser } = jidDecode(meId)! const { user: meLidUser } = meLid ? jidDecode(meLid)! : { user: null } - // Detect carousel to skip own devices (they can't handle carousel in DSM wrapper) - const isCarouselForDSM = isCarouselMessage(message) - for (const { user, jid } of devices) { const isExactSenderDevice = jid === meId || (meLid && jid === meLid) if (isExactSenderDevice) { @@ -1139,11 +1136,6 @@ export const makeMessagesSocket = (config: SocketConfig) => { const isMe = user === mePnUser || user === meLidUser if (isMe) { - // Skip own linked devices for carousel - they reject DSM-wrapped carousel with error 479 - if (isCarouselForDSM) { - logger.debug({ jid }, '[CAROUSEL] Skipping own linked device - carousel DSM not supported') - continue - } meRecipients.push(jid) } else { otherRecipients.push(jid) @@ -1158,10 +1150,9 @@ export const makeMessagesSocket = (config: SocketConfig) => { { nodes: meNodes, shouldIncludeDeviceIdentity: s1 }, { nodes: otherNodes, shouldIncludeDeviceIdentity: s2 } ] = await Promise.all([ - // For own devices: use DSM if available (1:1 chats only) - // Skip for carousel - linked devices can't handle carousel in DSM wrapper (error 479) + // For own devices: use DSM (deviceSentMessage) wrapper createParticipantNodes(meRecipients, meMsg || message, extraAttrs), - createParticipantNodes(otherRecipients, message, extraAttrs, isCarouselForDSM ? undefined : meMsg) + createParticipantNodes(otherRecipients, message, extraAttrs) ]) participants.push(...meNodes) participants.push(...otherNodes) @@ -1179,9 +1170,7 @@ export const makeMessagesSocket = (config: SocketConfig) => { const isParticipantLid = isLidUser(participant!.jid) const isMe = areJidsSameUser(participant!.jid, isParticipantLid ? meLid : meId) - // Skip DSM for carousel retries to own devices - they can't handle it - const isCarouselRetry = isCarouselMessage(message) - const usesDSM = isMe && !isCarouselRetry + const usesDSM = isMe const encodedMessageToSend = usesDSM ? encodeWAMessage({ deviceSentMessage: { diff --git a/src/Utils/messages.ts b/src/Utils/messages.ts index b78c5a17..b8779126 100644 --- a/src/Utils/messages.ts +++ b/src/Utils/messages.ts @@ -681,12 +681,13 @@ export const generateCarouselMessage = async ( '[CAROUSEL] Structure summary' ) - // Wrap in viewOnceMessageV2 (NOT V1!) for WhatsApp Web/Desktop rendering - // V2 (field 55) is the stable wrapper for interactive messages from non-Cloud API accounts - // V1 (field 37) caused error 479 - V2 is the correct approach (confirmed by Z-API) - // messageContextInfo with deviceListMetadata is required for multi-device rendering + // Wrap in viewOnceMessage V1 (field 37) - used by ckptw, Vkazee, and most Baileys forks + // V2 (field 55) renders on mobile but NOT on WhatsApp Web/Desktop + // V1 is what WhatsApp Web expects for interactive messages + // Previous error 479 with V1 was caused by missing root header + fromObject() corruption + // Now fixed: root header always present, plain JS object, messageContextInfo included return { - viewOnceMessageV2: { + viewOnceMessage: { message: { messageContextInfo: { deviceListMetadata: {}, @@ -1191,11 +1192,11 @@ export const generateWAMessageContent = async ( } // Pass options for media processing if cards have images/videos const generated = await generateCarouselMessage(carouselOptions, options) - m.viewOnceMessageV2 = generated.viewOnceMessageV2 - // messageContextInfo at the OUTER Message level too (not just inside the V2 wrapper) + m.viewOnceMessage = generated.viewOnceMessage + // messageContextInfo at the OUTER Message level too (not just inside the wrapper) // This ensures meMsg/DSM and multi-device routing get proper device list metadata m.messageContextInfo = { deviceListMetadata: {}, deviceListMetadataVersion: 2 } - options.logger?.info('Sending carousel with viewOnceMessageV2 wrapper (plain object, no proto conversion)') + options.logger?.info('Sending carousel with viewOnceMessage V1 wrapper (plain object, no proto conversion)') // Return the plain JS object directly WITHOUT calling WAProto.Message.fromObject() // This matches Pastorini's working approach where plain objects are passed directly // to relayMessage. The fromObject() conversion can corrupt nested carousel structures