fix(carousel): bypass proto fromObject for carousel messages to fix

fix(carousel): bypass proto fromObject for carousel messages to fix
This commit is contained in:
Renato Alcara
2026-02-06 21:05:28 -03:00
committed by GitHub
2 changed files with 29 additions and 18 deletions
+19 -11
View File
@@ -2020,12 +2020,16 @@ export const makeMessagesSocket = (config: SocketConfig) => {
const userJid = authState.creds.me!.id
// Special path for carousel: call relayMessage directly
// This bypasses generateWAMessageFromContent (which adds contextInfo.expiration
// and calls WAProto.Message.create that may corrupt nested carousel structures)
// Matches the working implementation's approach: generateWAMessageContent → relayMessage
// Special path for carousel: call relayMessage directly with plain JS object
// This matches Pastorini's working approach:
// 1. Build message as plain JS object (not proto instance)
// 2. Pass directly to relayMessage (no WAProto.Message.fromObject/create)
// 3. protobuf encode() handles plain objects correctly during serialization
// Going through fromObject() can corrupt nested carousel InteractiveMessage oneOf fields
if (typeof content === 'object' && 'nativeCarousel' in content && (content as any).nativeCarousel) {
const messageId = generateMessageIDV2(sock.user?.id)
// generateWAMessageContent returns a plain JS object for carousel
// (not a proto instance) to preserve nested structure integrity
const msgContent = await generateWAMessageContent(content, {
logger,
upload: waUploadToServer,
@@ -2034,6 +2038,17 @@ export const makeMessagesSocket = (config: SocketConfig) => {
jid,
})
// Pass the plain JS object directly to relayMessage
// This avoids proto.WebMessageInfo.fromObject() which would call
// Message.fromObject() on the nested message, potentially corrupting it
await relayMessage(jid, msgContent, {
messageId,
useCachedGroupMetadata: options.useCachedGroupMetadata,
additionalAttributes: {},
statusJidList: options.statusJidList,
})
// Build WebMessageInfo only for event emission and return value
const timestamp = unixTimestampSeconds()
const fullMsg = proto.WebMessageInfo.fromObject({
key: {
@@ -2048,13 +2063,6 @@ export const makeMessagesSocket = (config: SocketConfig) => {
status: proto.WebMessageInfo.Status.PENDING
}) as WAMessage
await relayMessage(jid, fullMsg.message!, {
messageId: fullMsg.key.id!,
useCachedGroupMetadata: options.useCachedGroupMetadata,
additionalAttributes: {},
statusJidList: options.statusJidList,
})
if (config.emitOwnEvents) {
process.nextTick(async () => {
const mutexKey = fullMsg.key.remoteJid || fullMsg.key.id || 'unknown'
+10 -7
View File
@@ -641,8 +641,10 @@ export const generateCarouselMessage = async (
}))
// Build the interactive message with carousel
// Root header MUST always be present for carousel messages (WhatsApp MD requirement)
// Missing root header causes error 479 on some devices
const interactiveMessage: proto.Message.IInteractiveMessage = {
header: text ? { title: text } : undefined,
header: { title: text || '', hasMediaAttachment: false },
body: { text: text || '' },
footer: footer ? { text: footer } : undefined,
carouselMessage: {
@@ -1156,12 +1158,13 @@ export const generateWAMessageContent = async (
// Pass options for media processing if cards have images/videos
const generated = await generateCarouselMessage(carouselOptions, options)
m.viewOnceMessage = generated.viewOnceMessage
options.logger?.info('Sending carousel with viewOnceMessage wrapper + fromObject encoding')
// Return early with fromObject for proper deep conversion of nested carousel structure
// fromObject recursively converts nested plain objects to protobuf message instances
// whereas create() does shallow property copy which may not properly encode deep structures
// This matches the working implementation's encoding approach
return WAProto.Message.fromObject(m)
options.logger?.info('Sending carousel with viewOnceMessage 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
// by incorrectly handling oneOf fields in deeply nested InteractiveMessage cards.
// The protobuf encode() method handles plain objects correctly when serializing.
return m as WAMessageContent
}
// Check for nativeList
else if (hasNonNullishProperty(message, 'nativeList')) {