For list messages, use type='list' instead of 'native_flow' in the
biz node structure. This allows the list button to open properly.
- native_flow messages: biz > interactive(type=native_flow) > native_flow
- list messages: biz > interactive(type=list) > list
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Changes the biz node from flat structure (biz > native_flow) to nested
structure (biz > interactive > native_flow) as used by Pastorini/Astra-Api.
Structure:
- biz: {}
- interactive: { type: 'native_flow', v: '1' }
- native_flow: { v: '9', name: 'mixed' }
This fixes error 479 that was breaking all interactive messages.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Fix carousel detection in getButtonType to return native_flow
- Add isCarouselMessage helper function
- Skip bot node injection for carousel messages (fixes error 479)
- Restore ProductCarouselCard and ProductCarouselMessageOptions types
- Restore generateProductCarouselMessage function
- Add productCarousel handler in generateWAMessageContent
The bot node with biz_bot='1' was causing error 479 for media carousels
because carousels are regular interactive messages, not bot messages.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Fix ProductCarouselMessageOptions import indentation
- Fix productList comment indentation to align with else-if chain
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Replace catalogId with businessOwnerJid (required for catalog reference)
- Fix cards structure to use proper IInteractiveMessage[] format
- Each card now uses collectionMessage with bizJid and id
- Fix body reading from productCarousel.body (was reading from message.body)
- Remove 'as any' type casting by using correct proto types
- Update examples in types and function documentation
Addresses:
- Schema mismatch in carousel cards
- Body text being silently ignored when nested in productCarousel
- Improper type casting masking validation errors
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The previous condition `!isJidGroup()` was too broad and would incorrectly
inject bot nodes for status broadcasts, newsletters, and Meta AI bots.
Now uses explicit checks for private 1:1 user conversations:
- isPnUser() for @s.whatsapp.net JIDs
- isLidUser() for @lid JIDs
- @c.us suffix for legacy format
- Excludes bot JIDs with isJidBot()
This prevents potential issues with message delivery to non-user destinations.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Update getButtonType() to return 'native_flow' for modern nativeFlowMessage format
- Add product list detection (PRODUCT_LIST type returns native_flow)
- Implement getButtonArgs() with proper v:4 attributes for native_flow
- Add special attribute handling for payment flows (review_and_pay, mpm, review_order)
- Add bot node injection for private chats (required for interactive messages to render)
- Based on Itsukichan/Baileys and baileys_helpers implementation
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Fixes based on code review:
1. Fix sections vs productSections mismatch
- Changed `productMsg.productList.sections` to `productMsg.productList.productSections`
- Ensures consistency with ProductListMessageOptions type
2. Add section title validation
- Each section must have a non-empty title string
3. Add productId validation for each product
- Each product in a section must have a non-empty productId string
4. Add headerImage.productId validation
- When headerImage is provided, productId must be a non-empty string
5. Remove fallback values (|| '')
- Removed fallbacks for title and description
- Let generateProductListMessage handle validation consistently
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Address PR review comment: the viewOnceMessage detection now checks
all 7 message types for consistency with direct message detection:
- buttonsMessage
- templateMessage
- listMessage
- buttonsResponseMessage
- listResponseMessage
- templateButtonReplyMessage
- interactiveMessage
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The getButtonType function was not detecting interactive messages
when wrapped inside viewOnceMessage (the modern nativeFlowMessage format).
This fix adds detection for:
- viewOnceMessage > interactiveMessage
- viewOnceMessage > listMessage
- viewOnceMessage > buttonsMessage
When enableInteractiveMessages is true, the 'biz' node will now be
correctly injected for messages using the viewOnceMessage wrapper.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add generateProductListMessage function for sending multiple products
from the WhatsApp Business catalog in a single message.
Features:
- ProductListMessageOptions type with full validation
- Support for product sections (categories)
- Optional header image from catalog
- Integration with sendMessage via 'productList' property
- Maximum 30 products limit per WhatsApp specs
Usage:
```typescript
const msg = generateProductListMessage({
title: 'Our Best Sellers',
description: 'Check out our products!',
buttonText: 'View Products',
businessOwnerJid: '5511999999999@s.whatsapp.net',
productSections: [
{ title: 'Electronics', products: [{ productId: 'prod_001' }] }
]
})
await sock.sendMessage(jid, msg)
```
Note: Requires WhatsApp Business account with catalog configured.
Does NOT require Meta Business Manager integration.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Fixes all 9 issues identified in PR #49 review:
## 1. Input Validation (formatNativeFlowButton)
- Added validateNonEmptyString helper function
- Validates required fields: text, url, copyText, id, phoneNumber
- Throws Boom error with descriptive message for empty/whitespace values
## 2-3. Async Media Processing (generateButtonMessage)
- Function is now async, returns Promise<WAMessageContent>
- Accepts optional MessageContentGenerationOptions parameter
- Calls prepareWAMessageMedia() for headerImage/headerVideo
- Throws error if media provided without mediaOptions
## 4-7. Async Media Processing (generateCarouselMessage)
- Function is now async, returns Promise<WAMessageContent>
- Uses Promise.all to process all card media in parallel
- Properly converts WAMediaUpload to IImageMessage/IVideoMessage
## 5. Mutual Exclusivity Validation
- generateButtonMessage: Throws if both headerImage AND headerVideo provided
- generateCarouselMessage: Throws if card has both image AND video
## 6. Empty Button Array Validation
- Each carousel card must have at least one button
- Throws descriptive error with card index
## 8. Updated Async Calls
- generateWAMessageContent now awaits generateButtonMessage
- generateWAMessageContent now awaits generateCarouselMessage
- Both pass MessageContentGenerationOptions for media processing
## 9. Type Support
- Functions accept MessageContentGenerationOptions as optional param
- Enables access to upload, mediaCache, logger options
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Extends the Native Flow implementation with additional features:
## New Button Type
- `CallButton` - `cta_call` type for initiating phone calls
```typescript
{ type: 'call', text: 'Call Us', phoneNumber: '+5511999999999' }
```
## New List Message Support
- `generateListMessage()` - Creates interactive list with single_select
- `nativeList` type for sendMessage integration
```typescript
await sock.sendMessage(jid, {
text: 'Choose:',
nativeList: {
buttonText: 'View Options',
sections: [{ title: 'Section', rows: [...] }]
}
})
```
## Legacy Functions (for backward compatibility)
- `generateButtonMessageLegacy()` - Old buttonsMessage format
- `generateListMessageLegacy()` - Old listMessage format
⚠️ These are deprecated and may not work on all devices
## Other Improvements
- Added `merchantUrl` support for URL buttons
- Added `messageVersion` parameter (default: 2)
- Added `messageParamsJson` to nativeFlowMessage
- Created `NativeListSection` type to avoid conflict with legacy `ListSection`
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The import of TimeMs from Defaults/index.ts caused a circular
dependency error in ESM:
"ReferenceError: Cannot access 'TimeMs' before initialization"
Solution: Define TimeMs constants locally in unified-session.ts
instead of importing from Defaults. This avoids the ESM module
initialization order issue.
Adds backward compatibility alias:
- isJidUser -> isPersonJid
This allows zpro.io and other consumers that depend on the old
isJidUser function name to continue working without changes.
1. Add logger to writeCacheFile (version-cache.ts)
- Now logs warnings when file write fails
- Helps debugging in production environments
2. Sanitize logging in parseGroupResult (communities.ts)
- Changed from info to debug level
- Removed full node/groupNode dumps (sensitive data)
- Now only logs nodeTag and groupId
3. Add input validation in parseNewsletterCreateResponse (newsletter.ts)
- Validates response structure before destructuring
- Adds fallback values for parseInt (prevents NaN)
- Adds null checks for optional fields
4. Add health-status.ts module
- getHealthStatus(): Full health check with circuit breakers, cache, memory
- isHealthy(): Simple boolean for liveness probes
- getSimpleHealthStatus(): Returns 'ok', 'degraded', or 'error'
- Useful for k8s probes and monitoring dashboards
Fixes 3 issues identified in code review:
1. ENV VAR PRECEDENCE: Changed DEFAULT_CONNECTION_CONFIG to use
undefined for enableUnifiedSession, allowing env var
BAILEYS_UNIFIED_SESSION_ENABLED to take precedence.
Priority is now: explicit config > env var > default (true)
2. SINGLE INITIALIZATION: UnifiedSessionManager is now created
only once, after sendNode is defined. This avoids:
- Duplicate circuit breaker instances
- State reinitialization
- Unnecessary overhead and logs
3. CONTINUOUS TIME SYNC: serverTimeOffset is now updated on
every received frame that contains a 't' attribute, not just
on CB:success. This keeps the offset accurate even during
long-running connections.
Implements WhatsApp's unified_session telemetry feature to reduce
detection of unofficial clients. This is an enterprise-grade implementation
inspired by whatsmeow PR #1057 and Baileys PR #2294.
Features:
- UnifiedSessionManager class with circuit breaker protection
- Server time synchronization for accurate session IDs
- Rate limiting to prevent spam (1 minute between sends)
- Prometheus metrics integration (unified_session_sent, errors)
- Structured logging for debugging
- Configurable via SocketConfig.enableUnifiedSession
- Environment variable support (BAILEYS_UNIFIED_SESSION_ENABLED)
Trigger points (matching official WhatsApp Web):
- After successful login (CB:success)
- After successful pairing (CB:iq,,pair-success)
- When sending 'available' presence
Implementation details:
- Session ID algorithm: (now + serverOffset + 3days) % 7days
- Time constants exported from Defaults/index.ts
- Full test coverage (31 tests)
References:
- https://github.com/tulir/whatsmeow/pull/1057
- https://github.com/WhiskeySockets/Baileys/pull/2294
- https://github.com/tulir/whatsmeow/issues/810
Fix TS2345 errors where array element access (medias[i]) was inferred as
`AlbumMediaItem | undefined` instead of `AlbumMediaItem`.
Changes:
- Add non-null assertion (!) after array access in for loops
- Add explicit cast to AnyMessageContent for hasNonNullishProperty calls
The non-null assertion is safe here because we iterate with `i < medias.length`,
guaranteeing the index is valid.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Addresses additional PR review suggestions:
1. **Consistent media type validation**
- Changed from `'image' in m` to `hasNonNullishProperty(m, 'image')`
- Aligns with validation in generateWAMessageContent
- Prevents counting items with undefined image/video properties
2. **Explicit interrupted send indication**
- Added `attemptedItems: number` - how many items were actually tried
- Added `stoppedEarly: boolean` - true if interrupted by continueOnFailure=false
- Updated `success` to be false if stoppedEarly (even if no failures in attempted items)
- Helps automated integrations understand partial sends
Example result when interrupted:
```json
{
"totalItems": 5,
"attemptedItems": 3,
"successCount": 2,
"failedCount": 1,
"stoppedEarly": true,
"success": false
}
```
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Fixes all issues identified in PR #44 review:
1. **Album root message now relayed to server** (Critical)
- Before: Only generated root message, never sent it
- After: Calls relayMessage() on albumRootMsg before sending media items
- Also emits own event if emitOwnEvents is enabled
2. **Fixed messageId collision**
- Before: Spread ...options could pass same messageId to all items
- After: Explicitly pass only safe options (timestamp, quoted, etc.)
- Each media item now gets a fresh ID from generateWAMessage
3. **Fixed proto structure for album association**
- Before: Used non-existent messageContextInfo.messageAddOnType
- After: Uses correct messageContextInfo.messageAssociation with:
- associationType: proto.MessageAssociation.AssociationType.MEDIA_ALBUM
- parentMessageKey: albumKey
4. **Added runtime error for sendMessage misuse**
- sendMessage() now throws clear error if called with { album: ... }
- Forces users to use sendAlbumMessage() for proper behavior
5. **Fixed documentation**
- delay: Changed "based on media size" to "based on media type"
- retryAttempts: Clarified it's "total attempts" not just retries
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Implements WhatsApp album messages (grouped media) with the following features:
- Send 2-10 images/videos grouped as a single album message
- Adaptive delay between sends based on media type (videos get 2x delay)
- Intelligent retry with exponential backoff for failed items
- parentMessageKey reference to album root (as suggested by maintainer)
- Complete result structure with success/failure tracking per item
- Validation for min (2) and max (10) media items
Types added:
- AlbumMediaItem: Single image/video with caption, mentions, dimensions
- AlbumMessageOptions: Configuration for delay, retry, continueOnFailure
- AlbumMediaResult: Per-item result with latency and retry attempts
- AlbumSendResult: Complete result with albumKey and statistics
Based on PR #2058 from WhiskeySockets/Baileys with improvements.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Update outdated file reference in comment (process-message.ts:399-421
-> PDO response handler in src/Utils/process-message.ts)
- Add missing else branch in fallback path for duplicate request logging
to maintain observability parity with scheduled path
- Clarify log message in fallback path to reflect direct request flow
('Message received before direct resend request completed')
https://claude.ai/code/session_011WM4Nb6tE1S5nLjHen8Xsi
Aligns CTWA recovery with upstream philosophy by using messageRetryManager
instead of direct requestPlaceholderResend calls.
Benefits:
- Uses 3-second delay from manager (avoids request spam)
- Enables automatic cancellation if message arrives before request
- Centralizes phone request logic in messageRetryManager
- Adds fallback for when manager is not available
Changes:
- Wrap requestPlaceholderResend in messageRetryManager.schedulePhoneRequest()
- Add new metric status 'scheduled' for tracking scheduled requests
- Add metric status 'sent' when request is actually sent after delay
- Keep direct call as fallback when messageRetryManager is null
https://claude.ai/code/session_011WM4Nb6tE1S5nLjHen8Xsi
Critical bug fix: Interactive message blocks were never being executed because
they were placed AFTER the normal text message processing block. Messages with
text+buttons were processed as plain text, ignoring the interactive features.
Changes:
- Move all interactive message processing (buttons, lists, templates, carousel)
to BEFORE the normal text processing block
- Remove duplicate interactive message blocks that were unreachable
- Ensure correct execution order: check for interactive features first, then
fallback to normal text processing
This fixes the runtime error where interactive message functions were not
being called properly when sending messages with buttons/lists.
Structure now:
1. Check text + buttons → buttonsMessage
2. Check text + templateButtons → templateMessage
3. Check sections → listMessage
4. Check carousel → interactiveMessage
5. Check text (normal) → extendedTextMessage
6. Other message types...
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Fixes type incompatibility error when processing buttons with media:
- Extract only media properties before passing to prepareWAMessageMedia
- Remove problematic interactiveMessage type from AnyRegularMessageContent
- Move Carouselable to text message type as Partial
This resolves the compilation error:
error TS2345: Argument of type '...' is not assignable to parameter
of type 'AnyMediaMessageContent'
Changes:
- src/Utils/messages.ts: Extract media content explicitly for type safety
- src/Types/Message.ts: Simplify type definitions, add Carouselable to text messages
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
⚠️ EXPERIMENTAL FEATURE - Use only for testing with disposable accounts
Implements full support for WhatsApp interactive messages including:
- Simple text buttons (up to 3 buttons)
- Buttons with images/videos
- List messages (up to 10 items in sections)
- Template buttons (quick reply, URL, call actions)
- Carousel messages (up to 10 scrollable cards)
Features:
- Feature flag 'enableInteractiveMessages' (default: true for dev/testing)
- Prometheus metrics for tracking sends, successes, failures, and latency
- Comprehensive TypeScript types for all interactive message formats
- Extensive logging with warnings about potential account bans
- Automatic 'biz' node injection when feature is enabled
CRITICAL WARNINGS:
- These features may NOT work on non-business WhatsApp accounts
- Can cause temporary or permanent account BANS
- WhatsApp actively blocks this functionality since April 2022
- Messages may be rejected or fail silently
- Use ONLY in dev environment with test accounts
Architecture:
- Added ButtonInfo, Templatable, Listable, Carouselable types to Message.ts
- Extended AnyMediaMessageContent and AnyRegularMessageContent
- Implemented message generation in messages.ts
- Added getButtonType() and getButtonArgs() helpers in messages-send.ts
- Injected 'biz' node in stanza construction with metrics tracking
- Added 4 new Prometheus metrics: interactiveMessagesSent, Success, Failures, Latency
Documentation:
- Complete usage guide in INTERACTIVE_MESSAGES.md
- Examples for all interactive message types
- Metrics monitoring queries
- Troubleshooting guide
- Migration path to WhatsApp Business API
Related issues:
- https://github.com/WhiskeySockets/Baileys/issues/56
- https://github.com/WhiskeySockets/Baileys/issues/25
- https://github.com/WhiskeySockets/Baileys/pull/2291
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
The property was declared twice in SocketConfig type:
- Line 134 as required boolean
- Line 211 as optional boolean
This caused TypeScript compilation errors:
- TS2300: Duplicate identifier
- TS2687: Mismatched modifiers
- TS2717: Type mismatch between required and optional
Removed the duplicate declaration, keeping the first one which is
already properly documented and has the correct type.
Prevents unnecessary placeholder resend requests for messages older than 7 days,
improving resource efficiency and aligning with upstream best practices. Adds
metrics tracking for rejected old messages and includes message age in logs.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Add explicit Buffer type annotations to salt, encKey, decKey variables
- Add Promise<[Buffer, Buffer]> return type to localHKDF function
- Add Buffer type casts for subarray return values
- Remove unnecessary non-null assertion operators (!)
Fixes TS2322 type mismatch errors when building with TypeScript 5.9.3+
and Node.js v24+. No runtime behavior changes.
Based on upstream PR #2284.
Merge upstream/master using 'ours' strategy to mark the following
commits as integrated (already cherry-picked with customizations):
- chat-utils,sync-action-utils: provide alternatives for the contact name
- example: revamp logging for example
- defaults, index: change shouldSyncHistoryMessage behavior
- history: fortify contact data
- history: add proper logging support in history
- example: cleanup
- socket: no sync warning!!!!!
- Fix connection showing "Online" but disconnected (#2132)
- fix: skip retry for expired status messages over 24 hours old
- fix: optimize getLIDsForPNs and add getPNsForLIDs (#2274)
- fix: extract LID-PN mappings from conversation objects in history sync
Our customizations are preserved. This zeros the "behind" counter.