fix(carousel): use viewOnceMessageV2 wrapper with messageContextInfo
fix(carousel): use viewOnceMessageV2 wrapper with messageContextInfo
This commit is contained in:
@@ -574,7 +574,11 @@ export const makeMessagesSocket = (config: SocketConfig) => {
|
|||||||
|
|
||||||
// Debug: log per-device carousel encoding
|
// Debug: log per-device carousel encoding
|
||||||
const carouselInMsg = msgToEncrypt?.viewOnceMessage?.message?.interactiveMessage?.carouselMessage
|
const carouselInMsg = msgToEncrypt?.viewOnceMessage?.message?.interactiveMessage?.carouselMessage
|
||||||
|
|| msgToEncrypt?.viewOnceMessageV2?.message?.interactiveMessage?.carouselMessage
|
||||||
|
|| msgToEncrypt?.interactiveMessage?.carouselMessage
|
||||||
|| msgToEncrypt?.deviceSentMessage?.message?.viewOnceMessage?.message?.interactiveMessage?.carouselMessage
|
|| msgToEncrypt?.deviceSentMessage?.message?.viewOnceMessage?.message?.interactiveMessage?.carouselMessage
|
||||||
|
|| msgToEncrypt?.deviceSentMessage?.message?.viewOnceMessageV2?.message?.interactiveMessage?.carouselMessage
|
||||||
|
|| msgToEncrypt?.deviceSentMessage?.message?.interactiveMessage?.carouselMessage
|
||||||
if (carouselInMsg) {
|
if (carouselInMsg) {
|
||||||
try {
|
try {
|
||||||
const debugBytes = proto.Message.encode(proto.Message.fromObject(msgToEncrypt)).finish()
|
const debugBytes = proto.Message.encode(proto.Message.fromObject(msgToEncrypt)).finish()
|
||||||
@@ -683,9 +687,9 @@ export const makeMessagesSocket = (config: SocketConfig) => {
|
|||||||
return 'interactive'
|
return 'interactive'
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check inside viewOnceMessage wrapper (modern nativeFlowMessage format)
|
// Check inside viewOnceMessage/viewOnceMessageV2 wrapper (modern nativeFlowMessage format)
|
||||||
// This is the recommended format for interactive messages
|
// V2 is the recommended format for interactive messages (carousel, buttons)
|
||||||
const innerMessage = message.viewOnceMessage?.message
|
const innerMessage = message.viewOnceMessage?.message || message.viewOnceMessageV2?.message
|
||||||
if (innerMessage) {
|
if (innerMessage) {
|
||||||
if (innerMessage.buttonsMessage) {
|
if (innerMessage.buttonsMessage) {
|
||||||
return 'buttons'
|
return 'buttons'
|
||||||
@@ -740,7 +744,8 @@ export const makeMessagesSocket = (config: SocketConfig) => {
|
|||||||
// For native_flow messages, check for special button types that need specific attributes
|
// For native_flow messages, check for special button types that need specific attributes
|
||||||
if (buttonType === 'native_flow') {
|
if (buttonType === 'native_flow') {
|
||||||
const interactiveMsg = message.interactiveMessage ||
|
const interactiveMsg = message.interactiveMessage ||
|
||||||
message.viewOnceMessage?.message?.interactiveMessage
|
message.viewOnceMessage?.message?.interactiveMessage ||
|
||||||
|
message.viewOnceMessageV2?.message?.interactiveMessage
|
||||||
|
|
||||||
if (interactiveMsg?.nativeFlowMessage?.buttons?.[0]) {
|
if (interactiveMsg?.nativeFlowMessage?.buttons?.[0]) {
|
||||||
const firstButtonName = interactiveMsg.nativeFlowMessage.buttons[0].name
|
const firstButtonName = interactiveMsg.nativeFlowMessage.buttons[0].name
|
||||||
@@ -770,7 +775,8 @@ export const makeMessagesSocket = (config: SocketConfig) => {
|
|||||||
*/
|
*/
|
||||||
const isCarouselMessage = (message: proto.IMessage): boolean => {
|
const isCarouselMessage = (message: proto.IMessage): boolean => {
|
||||||
const interactiveMsg = message.interactiveMessage ||
|
const interactiveMsg = message.interactiveMessage ||
|
||||||
message.viewOnceMessage?.message?.interactiveMessage
|
message.viewOnceMessage?.message?.interactiveMessage ||
|
||||||
|
message.viewOnceMessageV2?.message?.interactiveMessage
|
||||||
|
|
||||||
if (interactiveMsg?.carouselMessage?.cards?.length) {
|
if (interactiveMsg?.carouselMessage?.cards?.length) {
|
||||||
return true
|
return true
|
||||||
@@ -785,7 +791,8 @@ export const makeMessagesSocket = (config: SocketConfig) => {
|
|||||||
*/
|
*/
|
||||||
const isCatalogMessage = (message: proto.IMessage): boolean => {
|
const isCatalogMessage = (message: proto.IMessage): boolean => {
|
||||||
const interactiveMsg = message.interactiveMessage ||
|
const interactiveMsg = message.interactiveMessage ||
|
||||||
message.viewOnceMessage?.message?.interactiveMessage
|
message.viewOnceMessage?.message?.interactiveMessage ||
|
||||||
|
message.viewOnceMessageV2?.message?.interactiveMessage
|
||||||
|
|
||||||
const nativeFlow = interactiveMsg?.nativeFlowMessage
|
const nativeFlow = interactiveMsg?.nativeFlowMessage
|
||||||
if (nativeFlow?.buttons?.length) {
|
if (nativeFlow?.buttons?.length) {
|
||||||
@@ -806,7 +813,8 @@ export const makeMessagesSocket = (config: SocketConfig) => {
|
|||||||
*/
|
*/
|
||||||
const isListNativeFlow = (message: proto.IMessage): boolean => {
|
const isListNativeFlow = (message: proto.IMessage): boolean => {
|
||||||
const interactiveMsg = message.interactiveMessage ||
|
const interactiveMsg = message.interactiveMessage ||
|
||||||
message.viewOnceMessage?.message?.interactiveMessage
|
message.viewOnceMessage?.message?.interactiveMessage ||
|
||||||
|
message.viewOnceMessageV2?.message?.interactiveMessage
|
||||||
|
|
||||||
const nativeFlow = interactiveMsg?.nativeFlowMessage
|
const nativeFlow = interactiveMsg?.nativeFlowMessage
|
||||||
if (nativeFlow?.buttons?.length) {
|
if (nativeFlow?.buttons?.length) {
|
||||||
@@ -1284,8 +1292,8 @@ export const makeMessagesSocket = (config: SocketConfig) => {
|
|||||||
const startTime = Date.now()
|
const startTime = Date.now()
|
||||||
|
|
||||||
// Debug: Log message structure to diagnose list detection
|
// Debug: Log message structure to diagnose list detection
|
||||||
const interactiveMsg = message.interactiveMessage || message.viewOnceMessage?.message?.interactiveMessage
|
const interactiveMsg = message.interactiveMessage || message.viewOnceMessage?.message?.interactiveMessage || message.viewOnceMessageV2?.message?.interactiveMessage
|
||||||
const listMsg = message.listMessage || message.viewOnceMessage?.message?.listMessage
|
const listMsg = message.listMessage || message.viewOnceMessage?.message?.listMessage || message.viewOnceMessageV2?.message?.listMessage
|
||||||
const nativeFlowButtons = interactiveMsg?.nativeFlowMessage?.buttons || []
|
const nativeFlowButtons = interactiveMsg?.nativeFlowMessage?.buttons || []
|
||||||
const isListDetected = isListNativeFlow(message)
|
const isListDetected = isListNativeFlow(message)
|
||||||
|
|
||||||
|
|||||||
+15
-6
@@ -653,11 +653,20 @@ export const generateCarouselMessage = async (
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return interactiveMessage directly (NOT wrapped in viewOnceMessage)
|
// Wrap in viewOnceMessageV2 (NOT V1!) for WhatsApp Web/Desktop rendering
|
||||||
// Pastorini's working implementation confirms carousels must be sent
|
// V2 (field 55) is the stable wrapper for interactive messages from non-Cloud API accounts
|
||||||
// as direct interactiveMessage, not wrapped in viewOnceMessage
|
// V1 (field 37) caused error 479 - V2 is the correct approach (confirmed by Z-API)
|
||||||
|
// messageContextInfo with deviceListMetadata is required for multi-device rendering
|
||||||
return {
|
return {
|
||||||
interactiveMessage
|
viewOnceMessageV2: {
|
||||||
|
message: {
|
||||||
|
messageContextInfo: {
|
||||||
|
deviceListMetadata: {},
|
||||||
|
deviceListMetadataVersion: 2
|
||||||
|
},
|
||||||
|
interactiveMessage
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1154,8 +1163,8 @@ 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.interactiveMessage = generated.interactiveMessage
|
m.viewOnceMessageV2 = generated.viewOnceMessageV2
|
||||||
options.logger?.info('Sending carousel as direct interactiveMessage (plain object, no proto conversion)')
|
options.logger?.info('Sending carousel with viewOnceMessageV2 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