diff --git a/WAProto/WAProto.proto b/WAProto/WAProto.proto index d7631748..08ac034f 100644 --- a/WAProto/WAProto.proto +++ b/WAProto/WAProto.proto @@ -2717,6 +2717,7 @@ 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 86405800..fe4825c6 100644 --- a/WAProto/index.d.ts +++ b/WAProto/index.d.ts @@ -6854,6 +6854,7 @@ 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); @@ -6867,6 +6868,7 @@ 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 7ccf7180..3f78f208 100644 --- a/WAProto/index.js +++ b/WAProto/index.js @@ -48195,6 +48195,7 @@ 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; @@ -48222,6 +48223,12 @@ 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) @@ -48244,6 +48251,8 @@ 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")) @@ -48300,6 +48309,10 @@ 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; @@ -48321,6 +48334,9 @@ 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"); @@ -48384,6 +48400,11 @@ 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 e8cbdfa7..b3b277f0 100644 --- a/src/Socket/messages-send.ts +++ b/src/Socket/messages-send.ts @@ -30,6 +30,7 @@ import { generateMessageIDV2, generateParticipantHashV2, generateWAMessage, + generateWAMessageContent, getStatusCodeForMediaRetry, getUrlFromDirectPath, getWAUploadToServer, @@ -1964,6 +1965,52 @@ 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 + if (typeof content === 'object' && 'nativeCarousel' in content && (content as any).nativeCarousel) { + const messageId = generateMessageIDV2(sock.user?.id) + const msgContent = await generateWAMessageContent(content, { + logger, + upload: waUploadToServer, + mediaCache: config.mediaCache, + options: config.options, + jid, + }) + + const timestamp = unixTimestampSeconds() + const fullMsg = proto.WebMessageInfo.fromObject({ + key: { + remoteJid: jid, + fromMe: true, + id: messageId + }, + message: msgContent, + messageTimestamp: timestamp, + messageStubParameters: [], + participant: isJidGroup(jid) ? userJid : undefined, + 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' + await messageMutex.mutex(mutexKey, () => upsertMessage(fullMsg, 'append')) + }) + } + + return fullMsg + } + if ( typeof content === 'object' && 'disappearingMessagesInChat' in content && diff --git a/src/Utils/messages.ts b/src/Utils/messages.ts index 27507070..531c4d52 100644 --- a/src/Utils/messages.ts +++ b/src/Utils/messages.ts @@ -616,7 +616,8 @@ export const generateCarouselMessage = async ( const header: any = { title: card.title || '', - hasMediaAttachment: hasMedia + hasMediaAttachment: hasMedia, + hasSubtitle: !!card.title } // Process media if present @@ -642,7 +643,7 @@ export const generateCarouselMessage = async ( // Build the interactive message with carousel const interactiveMessage: proto.Message.IInteractiveMessage = { - header: text ? { title: text } : undefined, + header: text ? { title: text, hasSubtitle: true } : undefined, body: { text: text || '' }, footer: footer ? { text: footer } : undefined, carouselMessage: {