⚠️ 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.
Refactor example.ts to use pino logger with structured data:
- Replace all console.log with logger.debug/fatal
- Use objects for data instead of string concatenation
- Add contacts.upsert event handler
- Improves log filtering and searchability
Remove the override that was forcing full history sync when
syncFullHistory was true. Now uses the default from Defaults/index.ts
which skips FULL sync type for better performance and stability.
Benefits:
- Faster connection time (2-10s vs 30s-5min)
- Lower bandwidth usage (1-10MB vs 50-500MB+)
- More stable connections (no timeouts)
- INITIAL_BOOTSTRAP + RECENT provide sufficient data
Users can still customize via shouldSyncHistoryMessage if needed.
- Add fallback chain for contact name: displayName || name || username
- Add fallback for LID: lidJid || accountLid
- Add TODO marker for WAJIDHASH support in picture updates
These fallbacks improve robustness when WhatsApp returns contact
data in different fields depending on account type (business, personal).
- Add optional ILogger parameter to processHistoryMessage and
downloadAndProcessHistorySyncNotification functions
- Add trace-level logging with syncType and progress for debugging
- Preserve all existing imports and LID-PN extraction functionality
- Enhanced JSDoc documentation with detailed parameter descriptions
This enables trace-level visibility into history sync processing,
helping debug issues with message synchronization and LID mappings.
Fixes 4 issues identified in PR #35 code review:
## 1. Self-Primary Identity Check (Copilot #1)
- BEFORE: `ctx.meId && (areJidsSameUser(...) || (ctx.meLid && ...))`
- AFTER: Check meId and meLid independently with OR
- FIX: Now correctly detects when only meLid exists
## 2. Debounce Cache Placement (Copilot #3, #4)
- BEFORE: Cache set immediately after debounce check
- AFTER: Cache set only before actual assertSessions call
- FIX: Prevents incorrect debouncing when exiting early (offline, etc.)
## 3. Session Regression (ChatGPT Codex P2)
- BEFORE: Returned 'skipped_no_session' when no session exists
- AFTER: Always call assertSessions - identity change IS the signal to rebuild
- FIX: Critical for key reset and device restore scenarios
## 4. Result Type Enhancement
- Added `hadExistingSession: boolean` to 'session_refreshed' result
- Removed 'skipped_no_session' action (no longer applicable)
- Enables better monitoring of session creation vs refresh
## Test Updates
- Added test for meLid-only self-primary detection
- Updated session creation test (no longer skips)
- Added test verifying debounce not set on offline skip
- Added Result Types test suite for type safety
Cherry-pick from upstream PR #2280 (commit 92d4198)
- Add STATUS_EXPIRY_SECONDS constant (24h = 86400s)
- Skip retry attempts for status broadcast messages older than 24h
- Saves resources by not retrying messages that are already expired
Co-authored-by: João Lucas de Oliveira Lopes <55464917+jlucaso1@users.noreply.github.com>
Add extractPnFromMessages() function to extract phone numbers from
userReceipt fields when pnJid is missing in LID conversations.
This is a cherry-pick of the functionality from upstream PR #2282
(commit f829b6d7a) integrated with our existing LID-PN extraction logic.
Closes: WhiskeySockets/Baileys#2282
Metrics with labels only appear in Prometheus output after being
incremented at least once. This fix pre-initializes all labeled metrics
with zero values so they appear in Grafana dashboards immediately,
including buffer_destroyed_total which was showing "No data".
Added metrics tracking for:
- message_retries_total: Incremented when message retry is attempted
- message_failures_total: Incremented when max retries reached or encryption fails
Note: messages_queued metric is not applicable as this implementation
sends messages directly without an explicit queue system.
Added metrics tracking for:
- circuit_breaker_trips_total: Incremented when circuit opens
- adaptive_health_status: 1 if healthy (not in aggressive mode), 0 otherwise
- adaptive_event_rate: Current events per second
Added methods to AdaptiveTimeoutCalculator:
- getEventRate(): Returns current event rate in events/second
- isHealthy(): Returns true if not in aggressive mode
Metrics are updated on each buffer flush when adaptive timeout is enabled.
Added recordBufferDestroyed() and recordBufferFinalFlush() functions
and integrated into destroy() function in event-buffer.ts.
Tracks:
- Buffer destruction with reason and whether there was pending flush
- Final flushes that occur during buffer destruction
Added recordConnectionError() function and integrated it into socket.ts
to track connection errors by type:
- connection_closed
- connection_lost
- connection_replaced
- timed_out
- logged_out
- bad_session
- restart_required
- multidevice_mismatch
- error_{code} for other status codes
The metric was defined but never incremented when buffer overflow occurred.
Added recordBufferOverflow() function and call it from checkBufferOverflow()
when buffer exceeds maxBufferSize (default 5000 events).
The metric was defined but never incremented when LRU cleanup happened.
Added recordCacheCleanup() function and call it from cleanupHistoryCache()
when cache entries are removed due to exceeding maxHistoryCacheSize.
The buffer_cache_size metric was defined but never updated, always showing 0.
Fix:
- Add historyCacheSize parameter to recordBufferFlush function
- Pass historyCache.size from event-buffer.ts when recording flush metrics
- Update both bufferCacheSize and bufferHistoryCacheSize metrics
The buffer_flushes_total metric was registered twice with different labels:
- In main metrics object: ['type', 'reason']
- In getEventBufferMetrics(): ['forced']
This caused recordBufferFlush() to silently fail because it tried to
increment with { forced } labels but the registered metric expected
{ type, reason } labels.
Fix:
- Remove duplicate bufferFlushes from getEventBufferMetrics()
- Update recordBufferFlush() to use metrics.bufferFlushes with correct labels
- Update recordEventBuffered() to use main metrics.eventsBuffered
- Rename local variable to avoid shadowing global metrics object
Previously, buffer metrics required a separate BAILEYS_BUFFER_METRICS=true
env variable even when BAILEYS_PROMETHEUS_ENABLED=true was set. This caused
the buffer_flushes_total metric to have no data even though flushes were
occurring.
Now buffer metrics are enabled automatically when either BAILEYS_BUFFER_METRICS
or BAILEYS_PROMETHEUS_ENABLED is set to true.