diff --git a/src/Signal/libsignal.ts b/src/Signal/libsignal.ts index 9fd76b1c..744cc147 100644 --- a/src/Signal/libsignal.ts +++ b/src/Signal/libsignal.ts @@ -22,6 +22,17 @@ import { SenderKeyRecord } from './Group/sender-key-record' import { GroupCipher, GroupSessionBuilder, SenderKeyDistributionMessage } from './Group' import { LIDMappingStore } from './lid-mapping' +// Suppress verbose "Closing session: SessionEntry {...}" logs from libsignal +// These are raw console.log calls from the native Signal library that dump +// cryptographic session details (keys, ratchets, etc.) on every encrypt/decrypt +const _origConsoleLog = console.log +console.log = function(...args: unknown[]) { + if (args.length > 0 && typeof args[0] === 'string' && args[0].startsWith('Closing session')) { + return // suppress libsignal session dump + } + _origConsoleLog.apply(console, args) +} + // ============================================ // Identity Key Detection Constants // ============================================ diff --git a/src/Utils/messages.ts b/src/Utils/messages.ts index daa1c718..b78c5a17 100644 --- a/src/Utils/messages.ts +++ b/src/Utils/messages.ts @@ -610,6 +610,16 @@ export const generateCarouselMessage = async ( throw new Boom('mediaOptions required for processing card media', { statusCode: 400 }) } + // WhatsApp requires ALL carousel cards to have media attachments (image or video) + // Cards without media will not render on WhatsApp Web/Desktop + const cardsWithoutMedia = cards.filter(card => !card.image && !card.video) + if (cardsWithoutMedia.length > 0) { + mediaOptions?.logger?.warn( + { cardsWithoutMedia: cardsWithoutMedia.length, totalCards: cards.length }, + '[CAROUSEL] WARNING: Cards without media will NOT render on WhatsApp Web. Every card MUST have an image or video.' + ) + } + // Map cards to the carousel format (processing media) const carouselCards = await Promise.all(cards.map(async (card) => { const hasMedia = !!(card.image || card.video) @@ -653,6 +663,24 @@ export const generateCarouselMessage = async ( } } + // Log carousel structure summary for debugging + mediaOptions?.logger?.info( + { + totalCards: carouselCards.length, + cards: carouselCards.map((c: any, i: number) => ({ + index: i, + hasMedia: c.header?.hasMediaAttachment, + mediaType: c.header?.imageMessage ? 'image' : c.header?.videoMessage ? 'video' : 'none', + hasBody: !!c.body?.text, + hasFooter: !!c.footer?.text, + buttonsCount: c.nativeFlowMessage?.buttons?.length || 0 + })), + rootHeader: !!interactiveMessage.header?.title, + rootBody: !!interactiveMessage.body?.text + }, + '[CAROUSEL] Structure summary' + ) + // Wrap in viewOnceMessageV2 (NOT V1!) for WhatsApp Web/Desktop rendering // V2 (field 55) is the stable wrapper for interactive messages from non-Cloud API accounts // V1 (field 37) caused error 479 - V2 is the correct approach (confirmed by Z-API)