fix(carousel): switch to viewOnceMessage V1
fix(carousel): switch to viewOnceMessage V1
This commit is contained in:
@@ -1125,9 +1125,6 @@ export const makeMessagesSocket = (config: SocketConfig) => {
|
|||||||
const { user: mePnUser } = jidDecode(meId)!
|
const { user: mePnUser } = jidDecode(meId)!
|
||||||
const { user: meLidUser } = meLid ? jidDecode(meLid)! : { user: null }
|
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) {
|
for (const { user, jid } of devices) {
|
||||||
const isExactSenderDevice = jid === meId || (meLid && jid === meLid)
|
const isExactSenderDevice = jid === meId || (meLid && jid === meLid)
|
||||||
if (isExactSenderDevice) {
|
if (isExactSenderDevice) {
|
||||||
@@ -1139,11 +1136,6 @@ export const makeMessagesSocket = (config: SocketConfig) => {
|
|||||||
const isMe = user === mePnUser || user === meLidUser
|
const isMe = user === mePnUser || user === meLidUser
|
||||||
|
|
||||||
if (isMe) {
|
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)
|
meRecipients.push(jid)
|
||||||
} else {
|
} else {
|
||||||
otherRecipients.push(jid)
|
otherRecipients.push(jid)
|
||||||
@@ -1158,10 +1150,9 @@ export const makeMessagesSocket = (config: SocketConfig) => {
|
|||||||
{ nodes: meNodes, shouldIncludeDeviceIdentity: s1 },
|
{ nodes: meNodes, shouldIncludeDeviceIdentity: s1 },
|
||||||
{ nodes: otherNodes, shouldIncludeDeviceIdentity: s2 }
|
{ nodes: otherNodes, shouldIncludeDeviceIdentity: s2 }
|
||||||
] = await Promise.all([
|
] = await Promise.all([
|
||||||
// For own devices: use DSM if available (1:1 chats only)
|
// For own devices: use DSM (deviceSentMessage) wrapper
|
||||||
// Skip for carousel - linked devices can't handle carousel in DSM wrapper (error 479)
|
|
||||||
createParticipantNodes(meRecipients, meMsg || message, extraAttrs),
|
createParticipantNodes(meRecipients, meMsg || message, extraAttrs),
|
||||||
createParticipantNodes(otherRecipients, message, extraAttrs, isCarouselForDSM ? undefined : meMsg)
|
createParticipantNodes(otherRecipients, message, extraAttrs)
|
||||||
])
|
])
|
||||||
participants.push(...meNodes)
|
participants.push(...meNodes)
|
||||||
participants.push(...otherNodes)
|
participants.push(...otherNodes)
|
||||||
@@ -1179,9 +1170,7 @@ export const makeMessagesSocket = (config: SocketConfig) => {
|
|||||||
const isParticipantLid = isLidUser(participant!.jid)
|
const isParticipantLid = isLidUser(participant!.jid)
|
||||||
const isMe = areJidsSameUser(participant!.jid, isParticipantLid ? meLid : meId)
|
const isMe = areJidsSameUser(participant!.jid, isParticipantLid ? meLid : meId)
|
||||||
|
|
||||||
// Skip DSM for carousel retries to own devices - they can't handle it
|
const usesDSM = isMe
|
||||||
const isCarouselRetry = isCarouselMessage(message)
|
|
||||||
const usesDSM = isMe && !isCarouselRetry
|
|
||||||
const encodedMessageToSend = usesDSM
|
const encodedMessageToSend = usesDSM
|
||||||
? encodeWAMessage({
|
? encodeWAMessage({
|
||||||
deviceSentMessage: {
|
deviceSentMessage: {
|
||||||
|
|||||||
+14
-14
@@ -651,15 +651,15 @@ export const generateCarouselMessage = async (
|
|||||||
}))
|
}))
|
||||||
|
|
||||||
// Build the interactive message with carousel
|
// Build the interactive message with carousel
|
||||||
// Root header MUST always be present for carousel messages (WhatsApp MD requirement)
|
// Match ckptw's working structure: NO root header, NO messageVersion
|
||||||
// Missing root header causes error 479 on some devices
|
// Root header is NOT used for carousel (only individual cards have headers)
|
||||||
|
// Previous assumption that root header was required was incorrect -
|
||||||
|
// the error 479 was caused by fromObject() corruption, not missing header
|
||||||
const interactiveMessage: proto.Message.IInteractiveMessage = {
|
const interactiveMessage: proto.Message.IInteractiveMessage = {
|
||||||
header: { title: text || '', hasMediaAttachment: false },
|
|
||||||
body: { text: text || '' },
|
body: { text: text || '' },
|
||||||
footer: footer ? { text: footer } : undefined,
|
footer: footer ? { text: footer } : undefined,
|
||||||
carouselMessage: {
|
carouselMessage: {
|
||||||
cards: carouselCards,
|
cards: carouselCards
|
||||||
messageVersion: 1
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -675,18 +675,18 @@ export const generateCarouselMessage = async (
|
|||||||
hasFooter: !!c.footer?.text,
|
hasFooter: !!c.footer?.text,
|
||||||
buttonsCount: c.nativeFlowMessage?.buttons?.length || 0
|
buttonsCount: c.nativeFlowMessage?.buttons?.length || 0
|
||||||
})),
|
})),
|
||||||
rootHeader: !!interactiveMessage.header?.title,
|
|
||||||
rootBody: !!interactiveMessage.body?.text
|
rootBody: !!interactiveMessage.body?.text
|
||||||
},
|
},
|
||||||
'[CAROUSEL] Structure summary'
|
'[CAROUSEL] Structure summary'
|
||||||
)
|
)
|
||||||
|
|
||||||
// Wrap in viewOnceMessageV2 (NOT V1!) for WhatsApp Web/Desktop rendering
|
// Wrap in viewOnceMessage V1 (field 37) - used by ckptw, Vkazee, and most Baileys forks
|
||||||
// V2 (field 55) is the stable wrapper for interactive messages from non-Cloud API accounts
|
// V2 (field 55) renders on mobile but NOT on WhatsApp Web/Desktop
|
||||||
// V1 (field 37) caused error 479 - V2 is the correct approach (confirmed by Z-API)
|
// V1 is what WhatsApp Web expects for interactive messages
|
||||||
// messageContextInfo with deviceListMetadata is required for multi-device rendering
|
// Previous error 479 with V1 was caused by fromObject() corruption, not missing header
|
||||||
|
// Now fixed: plain JS object (no fromObject), no root header, messageContextInfo included
|
||||||
return {
|
return {
|
||||||
viewOnceMessageV2: {
|
viewOnceMessage: {
|
||||||
message: {
|
message: {
|
||||||
messageContextInfo: {
|
messageContextInfo: {
|
||||||
deviceListMetadata: {},
|
deviceListMetadata: {},
|
||||||
@@ -1191,11 +1191,11 @@ export const generateWAMessageContent = async (
|
|||||||
}
|
}
|
||||||
// Pass options for media processing if cards have images/videos
|
// Pass options for media processing if cards have images/videos
|
||||||
const generated = await generateCarouselMessage(carouselOptions, options)
|
const generated = await generateCarouselMessage(carouselOptions, options)
|
||||||
m.viewOnceMessageV2 = generated.viewOnceMessageV2
|
m.viewOnceMessage = generated.viewOnceMessage
|
||||||
// messageContextInfo at the OUTER Message level too (not just inside the V2 wrapper)
|
// 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
|
// This ensures meMsg/DSM and multi-device routing get proper device list metadata
|
||||||
m.messageContextInfo = { deviceListMetadata: {}, deviceListMetadataVersion: 2 }
|
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()
|
// Return the plain JS object directly WITHOUT calling WAProto.Message.fromObject()
|
||||||
// This matches Pastorini's working approach where plain objects are passed directly
|
// This matches Pastorini's working approach where plain objects are passed directly
|
||||||
// to relayMessage. The fromObject() conversion can corrupt nested carousel structures
|
// to relayMessage. The fromObject() conversion can corrupt nested carousel structures
|
||||||
|
|||||||
Reference in New Issue
Block a user