From 879970fda86b382c71e2eab67a4e9145ec04e095 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 6 Feb 2026 21:27:40 +0000 Subject: [PATCH] fix(carousel): remove hasSubtitle proto field + add debug logging - Remove hasSubtitle (field 10) from Header proto definition, index.js and index.d.ts - this field was adding extra bytes to encoded protobuf that working implementations don't send, potentially causing rejection - Remove hasSubtitle from carousel card headers and root header - Add [CAROUSEL DEBUG] logging in relayMessage to dump: - Encoded message bytes as base64 (for binary comparison) - Message structure as JSON - Per-device encoded bytes with DSM flag - This enables byte-level comparison with working implementations https://claude.ai/code/session_01EK9NpViRCtda1WAvFd8ptR --- WAProto/WAProto.proto | 1 - WAProto/index.d.ts | 2 -- WAProto/index.js | 21 ------------------- src/Socket/messages-send.ts | 42 +++++++++++++++++++++++++++++++++++++ src/Utils/messages.ts | 5 ++--- 5 files changed, 44 insertions(+), 27 deletions(-) diff --git a/WAProto/WAProto.proto b/WAProto/WAProto.proto index 08ac034f..d7631748 100644 --- a/WAProto/WAProto.proto +++ b/WAProto/WAProto.proto @@ -2717,7 +2717,6 @@ message Message { optional string title = 1; optional string subtitle = 2; optional bool hasMediaAttachment = 5; - optional bool hasSubtitle = 10; oneof media { Message.DocumentMessage documentMessage = 3; Message.ImageMessage imageMessage = 4; diff --git a/WAProto/index.d.ts b/WAProto/index.d.ts index fe4825c6..86405800 100644 --- a/WAProto/index.d.ts +++ b/WAProto/index.d.ts @@ -6854,7 +6854,6 @@ export namespace proto { title?: (string|null); subtitle?: (string|null); hasMediaAttachment?: (boolean|null); - hasSubtitle?: (boolean|null); documentMessage?: (proto.Message.IDocumentMessage|null); imageMessage?: (proto.Message.IImageMessage|null); jpegThumbnail?: (Uint8Array|null); @@ -6868,7 +6867,6 @@ export namespace proto { public title?: (string|null); public subtitle?: (string|null); public hasMediaAttachment?: (boolean|null); - public hasSubtitle?: (boolean|null); public documentMessage?: (proto.Message.IDocumentMessage|null); public imageMessage?: (proto.Message.IImageMessage|null); public jpegThumbnail?: (Uint8Array|null); diff --git a/WAProto/index.js b/WAProto/index.js index 3f78f208..7ccf7180 100644 --- a/WAProto/index.js +++ b/WAProto/index.js @@ -48195,7 +48195,6 @@ export const proto = $root.proto = (() => { Header.prototype.title = null; Header.prototype.subtitle = null; Header.prototype.hasMediaAttachment = null; - Header.prototype.hasSubtitle = null; Header.prototype.documentMessage = null; Header.prototype.imageMessage = null; Header.prototype.jpegThumbnail = null; @@ -48223,12 +48222,6 @@ export const proto = $root.proto = (() => { set: $util.oneOfSetter($oneOfFields) }); - // Virtual OneOf for proto3 optional field - Object.defineProperty(Header.prototype, "_hasSubtitle", { - get: $util.oneOfGetter($oneOfFields = ["hasSubtitle"]), - set: $util.oneOfSetter($oneOfFields) - }); - Object.defineProperty(Header.prototype, "media", { get: $util.oneOfGetter($oneOfFields = ["documentMessage", "imageMessage", "jpegThumbnail", "videoMessage", "locationMessage", "productMessage"]), set: $util.oneOfSetter($oneOfFields) @@ -48251,8 +48244,6 @@ export const proto = $root.proto = (() => { $root.proto.Message.ImageMessage.encode(m.imageMessage, w.uint32(34).fork()).ldelim(); if (m.hasMediaAttachment != null && Object.hasOwnProperty.call(m, "hasMediaAttachment")) w.uint32(40).bool(m.hasMediaAttachment); - if (m.hasSubtitle != null && Object.hasOwnProperty.call(m, "hasSubtitle")) - w.uint32(80).bool(m.hasSubtitle); if (m.jpegThumbnail != null && Object.hasOwnProperty.call(m, "jpegThumbnail")) w.uint32(50).bytes(m.jpegThumbnail); if (m.videoMessage != null && Object.hasOwnProperty.call(m, "videoMessage")) @@ -48309,10 +48300,6 @@ export const proto = $root.proto = (() => { m.productMessage = $root.proto.Message.ProductMessage.decode(r, r.uint32()); break; } - case 10: { - m.hasSubtitle = r.bool(); - break; - } default: r.skipType(t & 7); break; @@ -48334,9 +48321,6 @@ export const proto = $root.proto = (() => { if (d.hasMediaAttachment != null) { m.hasMediaAttachment = Boolean(d.hasMediaAttachment); } - if (d.hasSubtitle != null) { - m.hasSubtitle = Boolean(d.hasSubtitle); - } if (d.documentMessage != null) { if (typeof d.documentMessage !== "object") throw TypeError(".proto.Message.InteractiveMessage.Header.documentMessage: object expected"); @@ -48400,11 +48384,6 @@ export const proto = $root.proto = (() => { if (o.oneofs) d._hasMediaAttachment = "hasMediaAttachment"; } - if (m.hasSubtitle != null && m.hasOwnProperty("hasSubtitle")) { - d.hasSubtitle = m.hasSubtitle; - if (o.oneofs) - d._hasSubtitle = "hasSubtitle"; - } if (m.jpegThumbnail != null && m.hasOwnProperty("jpegThumbnail")) { d.jpegThumbnail = o.bytes === String ? $util.base64.encode(m.jpegThumbnail, 0, m.jpegThumbnail.length) : o.bytes === Array ? Array.prototype.slice.call(m.jpegThumbnail) : m.jpegThumbnail; if (o.oneofs) diff --git a/src/Socket/messages-send.ts b/src/Socket/messages-send.ts index b3b277f0..adefe441 100644 --- a/src/Socket/messages-send.ts +++ b/src/Socket/messages-send.ts @@ -572,6 +572,26 @@ export const makeMessagesSocket = (config: SocketConfig) => { } } + // Debug: log per-device carousel encoding + const carouselInMsg = msgToEncrypt?.viewOnceMessage?.message?.interactiveMessage?.carouselMessage + || msgToEncrypt?.deviceSentMessage?.message?.viewOnceMessage?.message?.interactiveMessage?.carouselMessage + if (carouselInMsg) { + try { + const debugBytes = proto.Message.encode(proto.Message.fromObject(msgToEncrypt)).finish() + logger.info( + { + recipientJid: jid, + isDSM: !!msgToEncrypt?.deviceSentMessage, + encodedBase64: Buffer.from(debugBytes).toString('base64'), + encodedLength: debugBytes.length, + }, + '[CAROUSEL DEBUG] Per-device encoded message' + ) + } catch (err) { + logger.warn({ jid, error: (err as Error).message }, '[CAROUSEL DEBUG] Per-device encode failed') + } + } + const bytes = encodeWAMessage(msgToEncrypt) const mutexKey = jid @@ -899,6 +919,28 @@ export const makeMessagesSocket = (config: SocketConfig) => { messageContextInfo: message.messageContextInfo } + // Debug logging for carousel messages - dump encoded bytes for binary comparison + if (isCarouselMessage(message)) { + try { + const msgBytes = proto.Message.encode(proto.Message.fromObject(message)).finish() + const meMsgBytes = proto.Message.encode(proto.Message.fromObject(meMsg)).finish() + logger.info( + { + msgId, + messageBase64: Buffer.from(msgBytes).toString('base64'), + meMsgBase64: Buffer.from(meMsgBytes).toString('base64'), + messageBytesLength: msgBytes.length, + meMsgBytesLength: meMsgBytes.length, + messageStructure: JSON.stringify(proto.Message.toObject(proto.Message.fromObject(message), { defaults: false })), + meMsgStructure: JSON.stringify(proto.Message.toObject(proto.Message.fromObject(meMsg), { defaults: false })), + }, + '[CAROUSEL DEBUG] Encoded message bytes for binary comparison' + ) + } catch (err) { + logger.warn({ msgId, error: (err as Error).message }, '[CAROUSEL DEBUG] Failed to encode for debug') + } + } + const extraAttrs: BinaryNodeAttributes = {} if (participant) { diff --git a/src/Utils/messages.ts b/src/Utils/messages.ts index 531c4d52..27507070 100644 --- a/src/Utils/messages.ts +++ b/src/Utils/messages.ts @@ -616,8 +616,7 @@ export const generateCarouselMessage = async ( const header: any = { title: card.title || '', - hasMediaAttachment: hasMedia, - hasSubtitle: !!card.title + hasMediaAttachment: hasMedia } // Process media if present @@ -643,7 +642,7 @@ export const generateCarouselMessage = async ( // Build the interactive message with carousel const interactiveMessage: proto.Message.IInteractiveMessage = { - header: text ? { title: text, hasSubtitle: true } : undefined, + header: text ? { title: text } : undefined, body: { text: text || '' }, footer: footer ? { text: footer } : undefined, carouselMessage: {