@leonoremods/baileys 0.0.0-stage → 1.2.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (150) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +1204 -2
  3. package/WAProto/GenerateStatics.sh +3 -0
  4. package/WAProto/WAProto.proto +8083 -0
  5. package/WAProto/fix-imports.js +75 -0
  6. package/WAProto/index.d.ts +23020 -0
  7. package/WAProto/index.js +136324 -0
  8. package/engine-requirements.js +17 -0
  9. package/package.json +107 -4
  10. package/scripts/check-syntax.js +28 -0
  11. package/src/Defaults/index.js +186 -0
  12. package/src/Defaults/phonenumber-mcc.json +223 -0
  13. package/src/Signal/Group/ciphertext-message.js +15 -0
  14. package/src/Signal/Group/group-session-builder.js +86 -0
  15. package/src/Signal/Group/group_cipher.js +82 -0
  16. package/src/Signal/Group/index.js +140 -0
  17. package/src/Signal/Group/keyhelper.js +77 -0
  18. package/src/Signal/Group/sender-chain-key.js +29 -0
  19. package/src/Signal/Group/sender-key-distribution-message.js +66 -0
  20. package/src/Signal/Group/sender-key-message.js +69 -0
  21. package/src/Signal/Group/sender-key-name.js +51 -0
  22. package/src/Signal/Group/sender-key-record.js +44 -0
  23. package/src/Signal/Group/sender-key-state.js +87 -0
  24. package/src/Signal/Group/sender-message-key.js +29 -0
  25. package/src/Signal/libsignal.js +455 -0
  26. package/src/Signal/lid-mapping.js +273 -0
  27. package/src/Socket/Client/index.js +31 -0
  28. package/src/Socket/Client/types.js +13 -0
  29. package/src/Socket/Client/websocket.js +61 -0
  30. package/src/Socket/aigroups.js +221 -0
  31. package/src/Socket/business.js +411 -0
  32. package/src/Socket/chats.js +1449 -0
  33. package/src/Socket/communities.js +463 -0
  34. package/src/Socket/graphql.js +523 -0
  35. package/src/Socket/groups.js +516 -0
  36. package/src/Socket/index.js +31 -0
  37. package/src/Socket/interactive-handler.js +527 -0
  38. package/src/Socket/interop.js +339 -0
  39. package/src/Socket/luxu.js +349 -0
  40. package/src/Socket/managed-account.js +98 -0
  41. package/src/Socket/messages-recv.js +2685 -0
  42. package/src/Socket/messages-send.js +2047 -0
  43. package/src/Socket/mex.js +57 -0
  44. package/src/Socket/newsletter.js +455 -0
  45. package/src/Socket/privacy.js +125 -0
  46. package/src/Socket/registration.js +236 -0
  47. package/src/Socket/socket.js +983 -0
  48. package/src/Socket/text-router.js +65 -0
  49. package/src/Socket/username.js +130 -0
  50. package/src/Store/index.js +33 -0
  51. package/src/Store/keyed-db.js +118 -0
  52. package/src/Store/make-cache-manager-store.js +84 -0
  53. package/src/Store/make-in-memory-store.js +551 -0
  54. package/src/Store/make-ordered-dictionary.js +81 -0
  55. package/src/Store/object-repository.js +26 -0
  56. package/src/Types/Auth.js +31 -0
  57. package/src/Types/Bussines.js +2 -0
  58. package/src/Types/Call.js +2 -0
  59. package/src/Types/Chat.js +4 -0
  60. package/src/Types/Contact.js +2 -0
  61. package/src/Types/Events.js +2 -0
  62. package/src/Types/GroupMetadata.js +2 -0
  63. package/src/Types/Label.js +26 -0
  64. package/src/Types/LabelAssociation.js +8 -0
  65. package/src/Types/Message.js +93 -0
  66. package/src/Types/Mex.js +112 -0
  67. package/src/Types/Newsletter.js +109 -0
  68. package/src/Types/Product.js +2 -0
  69. package/src/Types/Signal.js +2 -0
  70. package/src/Types/Socket.js +2 -0
  71. package/src/Types/State.js +62 -0
  72. package/src/Types/USync.js +2 -0
  73. package/src/Types/index.js +56 -0
  74. package/src/Utils/auth-utils.js +254 -0
  75. package/src/Utils/browser-utils.js +112 -0
  76. package/src/Utils/business.js +240 -0
  77. package/src/Utils/chat-utils.js +1230 -0
  78. package/src/Utils/command-loader.d.ts +27 -0
  79. package/src/Utils/command-loader.js +89 -0
  80. package/src/Utils/companion-reg-client-utils.js +40 -0
  81. package/src/Utils/consumer-application.js +105 -0
  82. package/src/Utils/crypto.js +118 -0
  83. package/src/Utils/curve25519-js.js +242 -0
  84. package/src/Utils/decode-wa-message.js +529 -0
  85. package/src/Utils/event-buffer.js +580 -0
  86. package/src/Utils/generics.js +483 -0
  87. package/src/Utils/group-history.js +44 -0
  88. package/src/Utils/history.js +232 -0
  89. package/src/Utils/identity-change-handler.js +52 -0
  90. package/src/Utils/index.js +59 -0
  91. package/src/Utils/jid-display-normalization.js +195 -0
  92. package/src/Utils/link-preview.js +135 -0
  93. package/src/Utils/logger.js +8 -0
  94. package/src/Utils/lt-hash.js +25 -0
  95. package/src/Utils/make-mutex.js +36 -0
  96. package/src/Utils/message-composer.js +471 -0
  97. package/src/Utils/message-retry-manager.js +217 -0
  98. package/src/Utils/messages-media.js +843 -0
  99. package/src/Utils/messages.js +2817 -0
  100. package/src/Utils/meta-ai-msmsg.js +222 -0
  101. package/src/Utils/native-bridge.js +68 -0
  102. package/src/Utils/noise-handler.js +169 -0
  103. package/src/Utils/offline-node-processor.js +34 -0
  104. package/src/Utils/pre-key-manager.js +90 -0
  105. package/src/Utils/process-message.js +950 -0
  106. package/src/Utils/reporting-utils.js +261 -0
  107. package/src/Utils/session-pool.d.ts +14 -0
  108. package/src/Utils/session-pool.js +70 -0
  109. package/src/Utils/signal.js +217 -0
  110. package/src/Utils/socket-tools.js +252 -0
  111. package/src/Utils/stanza-ack.js +30 -0
  112. package/src/Utils/sticker.d.ts +13 -0
  113. package/src/Utils/sticker.js +123 -0
  114. package/src/Utils/sync-action-utils.js +45 -0
  115. package/src/Utils/tc-token-utils.js +158 -0
  116. package/src/Utils/use-multi-file-auth-state.js +111 -0
  117. package/src/Utils/validate-connection.js +209 -0
  118. package/src/Utils/view-once-cache.d.ts +12 -0
  119. package/src/Utils/view-once-cache.js +63 -0
  120. package/src/Utils/voip-rekey.js +22 -0
  121. package/src/WABinary/constants.js +1304 -0
  122. package/src/WABinary/decode.js +342 -0
  123. package/src/WABinary/encode.js +225 -0
  124. package/src/WABinary/generic-utils.js +238 -0
  125. package/src/WABinary/index.js +34 -0
  126. package/src/WABinary/jid-utils.js +351 -0
  127. package/src/WABinary/types.js +2 -0
  128. package/src/WAM/BinaryInfo.js +13 -0
  129. package/src/WAM/constants.js +39484 -0
  130. package/src/WAM/encode.js +149 -0
  131. package/src/WAM/index.js +32 -0
  132. package/src/WAUSync/Protocols/USyncBotProfileProtocol.js +53 -0
  133. package/src/WAUSync/Protocols/USyncBusinessProtocol.js +44 -0
  134. package/src/WAUSync/Protocols/USyncContactProtocol.js +55 -0
  135. package/src/WAUSync/Protocols/USyncDeviceProtocol.js +55 -0
  136. package/src/WAUSync/Protocols/USyncDisappearingModeProtocol.js +31 -0
  137. package/src/WAUSync/Protocols/USyncFeatureProtocol.js +54 -0
  138. package/src/WAUSync/Protocols/USyncLIDProtocol.js +32 -0
  139. package/src/WAUSync/Protocols/USyncNewsletterProtocol.js +481 -0
  140. package/src/WAUSync/Protocols/USyncPictureProtocol.js +34 -0
  141. package/src/WAUSync/Protocols/USyncSidelistProtocol.js +29 -0
  142. package/src/WAUSync/Protocols/USyncStatusProtocol.js +42 -0
  143. package/src/WAUSync/Protocols/USyncTextStatusProtocol.js +38 -0
  144. package/src/WAUSync/Protocols/USyncUsernameProtocol.js +28 -0
  145. package/src/WAUSync/Protocols/index.js +40 -0
  146. package/src/WAUSync/USyncQuery.js +126 -0
  147. package/src/WAUSync/USyncUser.js +50 -0
  148. package/src/WAUSync/index.js +32 -0
  149. package/src/antiban.js +4152 -0
  150. package/src/index.js +78 -0
package/README.md CHANGED
@@ -1,3 +1,1205 @@
1
- # Temporary Holding Version
1
+ # @leonoremods/baileys
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ A high-performance WhatsApp Web library built on [Baileys](https://github.com/WhiskeySockets/Baileys), with critical paths accelerated via a [Rust WASM bridge](https://github.com/7ucg/whatsapp-rust-bridge).
4
+
5
+ <p align="center">
6
+ <img alt="package" src="https://img.shields.io/badge/package-%40leonoremods%2Fbaileys-25D366?style=for-the-badge&logo=whatsapp&logoColor=white">
7
+ <img alt="version" src="https://img.shields.io/badge/version-1.2.9-blue?style=for-the-badge">
8
+ </p>
9
+ <p align="center">
10
+ <a href="https://t.me/FoxsSql"><img alt="Telegram" src="https://img.shields.io/badge/Telegram-FoxsSql-26A5E4?style=for-the-badge&logo=telegram&logoColor=white"></a>
11
+ <a href="https://github.com/LeonoreMods"><img alt="GitHub" src="https://img.shields.io/badge/GitHub-LeonoreMods-181717?style=for-the-badge&logo=github&logoColor=white"></a>
12
+ </p>
13
+
14
+ | | |
15
+ |---|---|
16
+ | 📦 **Package** | `@leonoremods/baileys` |
17
+ | 🏷️ **Version** | `1.2.9` |
18
+ | 💬 **Telegram** | [t.me/FoxsSql](https://t.me/FoxsSql) |
19
+ | 🐙 **GitHub** | [github.com/LeonoreMods](https://github.com/LeonoreMods) |
20
+
21
+ ---
22
+
23
+ ## Index
24
+
25
+ - [What's Different](#whats-different)
26
+ - [Installation](#installation)
27
+ - [Running on Termux / Android](#running-on-termux--android)
28
+ - [Running on Pterodactyl](#running-on-pterodactyl)
29
+ - [Connecting Account](#connecting-account)
30
+ - [QR Code](#qr-code)
31
+ - [Pairing Code](#pairing-code)
32
+ - [Receive Full History](#receive-full-history)
33
+ - [Socket Config Notes](#socket-config-notes)
34
+ - [Saving & Restoring Sessions](#saving--restoring-sessions)
35
+ - [Handling Events](#handling-events)
36
+ - [Text Routing (onText / hears / command)](#text-routing-onText--hears--command)
37
+ - [Anti-Ban System](#anti-ban-system)
38
+ - [RateLimiter](#ratelimiter--throttle-outbound-messages)
39
+ - [WarmUp](#warmup--gradual-daily-limit-increase-for-new-numbers)
40
+ - [HealthMonitor](#healthmonitor--detect-ban-risk)
41
+ - [TimelockGuard](#timelockguard--handle-wa-463-reachout-blocks)
42
+ - [PresenceChoreographer](#presencechoreographer--human-like-typing-simulation)
43
+ - [wrapSocket](#wrapsocket--apply-all-anti-ban-layers-at-once)
44
+ - [Sending Messages](#sending-messages)
45
+ - [Text & Basic](#text--basic)
46
+ - [Buttons & Interactive](#buttons--interactive)
47
+ - [Media](#media)
48
+ - [Meta AI / Rich Responses](#meta-ai--rich-responses)
49
+ - [Status / Stories](#status--stories)
50
+ - [Modifying Messages](#modifying-messages)
51
+ - [Manipulating Media](#manipulating-media)
52
+ - [Groups](#groups)
53
+ - [Privacy](#privacy)
54
+ - [User Queries](#user-queries)
55
+ - [Change Profile](#change-profile)
56
+ - [Chat Modifiers](#chat-modifiers)
57
+ - [Writing Custom Functionality](#writing-custom-functionality)
58
+ - [Extra Utilities](#extra-utilities)
59
+ - [Sticker Maker](#sticker-maker)
60
+ - [Auto-Cache View-Once Media](#auto-cache-view-once-media)
61
+ - [Folder-Based Command Loader](#folder-based-command-loader)
62
+ - [Multi-Account Session Pool](#multi-account-session-pool)
63
+ - [Store Auto-Save, Message Limits & Encryption](#store-auto-save-message-limits--encryption)
64
+ - [Rust WASM Bridge](#rust-wasm-bridge)
65
+
66
+ ---
67
+
68
+ ## What's Different
69
+
70
+ **Performance — Rust WASM**
71
+
72
+ | Area | Upstream Baileys | This fork |
73
+ |---|---|---|
74
+ | Binary decode | JS | Rust WASM |
75
+ | Noise handshake | JS | Rust WASM |
76
+ | AES / HMAC / HKDF | JS (`crypto`) | Rust WASM |
77
+ | Signal protocol | `libsignal-node` | Rust WASM |
78
+
79
+ **Extra Features**
80
+
81
+ | Feature | Notes |
82
+ |---|---|
83
+ | Meta AI / msmsg decrypt | Full `messageSecret`-encrypted AI message decryption |
84
+ | Meta AI message handling | Receive and process Meta AI bot responses |
85
+ | Rich AI composer | Send tables, lists, code blocks, LaTeX via Meta AI format |
86
+ | Interactive buttons | List, reply, template, cards, product list, PIX/PAY |
87
+ | Interop (FB/IG) | Near-parity with mobile & web for cross-platform JIDs |
88
+ | Anti-ban measures | Connection fingerprinting aligned with official clients |
89
+ | Album messages | Send multiple media as an album |
90
+ | Sticker packs | Sticker pack message support |
91
+ | Newsletter messages | Follower invite messages |
92
+ | Top-level call signalling | Emits `call` for both `<call>`-wrapped and top-level `<offer>`/`<terminate>` stanzas (+ acks them) |
93
+
94
+ ---
95
+
96
+ ## Installation
97
+
98
+ ```bash
99
+ npm install npm:@leonoremods/baileys
100
+ # or
101
+ yarn add npm:@leonoremods/baileys
102
+ ```
103
+
104
+ **Requirements:** Node.js ≥ 20
105
+
106
+ **Optional peer dependencies:**
107
+
108
+ | Package | Purpose |
109
+ |---|---|
110
+ | `sharp` | Image processing / thumbnails |
111
+ | `jimp` | Fallback image processing |
112
+ | `audio-decode` | Voice message metadata |
113
+ | `link-preview-js` | Link preview generation |
114
+
115
+ ### Running on Termux / Android
116
+
117
+ ```bash
118
+ pkg install nodejs-lts
119
+ npm install npm:@leonoremods/baileys
120
+ ```
121
+
122
+ `whatsapp-rust-bridge` compiles to **WASM**, not a platform-specific native binary, and
123
+ ships a prebuilt `.wasm` artifact — so a normal `npm install` typically works on Termux
124
+ without installing a Rust toolchain at all (WASM bytecode runs the same on ARM, x86,
125
+ etc., unlike a native N-API `.node` addon). It's still marked as an **optional**
126
+ dependency as a safety net: on the rare setup where the prebuilt doesn't load (very old
127
+ CPUs lacking WASM SIMD, an unusual libc, etc.), `npm install` will **not** fail, and the
128
+ package still loads and works:
129
+
130
+ - MD5, SHA-256, HMAC-SHA256, AES-GCM/CTR/CBC, and HKDF transparently fall back to
131
+ Node's built-in `crypto` module — no functionality lost, no native module needed.
132
+ - WABinary node encoding/decoding fall back to a pure-JS implementation.
133
+ - **X25519 key exchange and XEdDSA signing** (used for identity/pre-key generation
134
+ and signing) also fall back to pure JS — X25519 itself via Node's own built-in
135
+ `crypto` (native, audited), and XEdDSA signing via a small hand-written
136
+ Edwards-curve implementation validated against the official RFC 8032 Ed25519 test
137
+ vectors and hundreds of randomized Montgomery↔Edwards interop checks (see
138
+ `Utils/curve25519-js.js`).
139
+ - **The Noise handshake and the Signal Double Ratchet session** genuinely need the
140
+ module (hand-rolling a full protocol session state machine from scratch is a much
141
+ larger correctness/security risk than a single curve operation, so there's no JS
142
+ fallback for these). Without the native module, connecting a WhatsApp session will
143
+ throw a clear error rather than silently failing — everything else (stickers, the
144
+ in-memory store, USync helpers, the command loader, session pool, key generation,
145
+ etc.) keeps working.
146
+
147
+ If you ever do need to build it from source (e.g. `WHATSAPP_RUST_BRIDGE_SKIP_PREBUILT=1`):
148
+
149
+ ```bash
150
+ pkg install rust binutils
151
+ npm install npm:@leonoremods/baileys
152
+ ```
153
+
154
+ > **Note:** some published versions of the bridge ship a `package.json` whose
155
+ > `"exports"` map is missing a `.` entry, which makes a plain `require()` throw
156
+ > `ERR_PACKAGE_PATH_NOT_EXPORTED` even though the module itself works fine. This
157
+ > package works around that automatically (it locates the installed module's real
158
+ > entry file and requires it directly) — no action needed on your end.
159
+
160
+ Other Termux notes:
161
+ - `sharp` needs `pkg install libvips` to build; if that's not available, install
162
+ `jimp` instead (`npm install jimp`) — it's a pure-JS fallback used automatically.
163
+ - Animated stickers (`videoToWebpSticker`) need `ffmpeg` on PATH: `pkg install ffmpeg`.
164
+
165
+ ### Running on Pterodactyl
166
+
167
+ Works fine on a standard Node.js egg — Pterodactyl containers are normal x86_64 Linux
168
+ (Debian/Alpine-based) Docker images, which is the best-supported target for both the
169
+ prebuilt WASM bridge and `sharp`'s prebuilt binaries. Checklist:
170
+
171
+ - Pick a **Node.js ≥ 20** egg/variable (the package's `engines` field enforces this).
172
+ - If you want animated stickers, make sure `ffmpeg` is available in the container —
173
+ either an egg/image that already bundles it, or add an install script step
174
+ (`apt-get install -y ffmpeg` on Debian-based yolks). Static stickers only need
175
+ `sharp`, which installs normally.
176
+ - Persistent storage: point `useMultiFileAuthState`/the store's `writeToFile` at a path
177
+ under the server's data volume so sessions survive container restarts/reinstalls.
178
+ - If the egg's image is Alpine/musl-based rather than Debian/glibc-based, the
179
+ prebuilt WASM/`sharp` binaries are less likely to match — everything still installs
180
+ (thanks to the fallbacks above), but a live connection needs the native bridge to
181
+ work, so prefer a glibc-based Node image if you have the choice.
182
+
183
+ ---
184
+
185
+ ## Connecting Account
186
+
187
+ ### QR Code
188
+
189
+ ```js
190
+ const { makeWASocket, useMultiFileAuthState, DisconnectReason } = require('@leonoremods/baileys')
191
+ const { Boom } = require('@hapi/boom')
192
+
193
+ const { state, saveCreds } = await useMultiFileAuthState('./auth')
194
+
195
+ const sock = makeWASocket({ auth: state, printQRInTerminal: true })
196
+
197
+ sock.ev.on('creds.update', saveCreds)
198
+ sock.ev.on('connection.update', ({ connection, lastDisconnect }) => {
199
+ if (connection === 'close') {
200
+ const shouldReconnect = new Boom(lastDisconnect?.error)?.output?.statusCode !== DisconnectReason.loggedOut
201
+ if (shouldReconnect) connect()
202
+ }
203
+ })
204
+ ```
205
+
206
+ ### Pairing Code
207
+
208
+ ```js
209
+ const sock = makeWASocket({ auth: state, printQRInTerminal: false })
210
+
211
+ if (!state.creds.registered) {
212
+ const code = await sock.requestPairingCode('49123456789') // phone number without +
213
+ console.log('Pairing code:', code)
214
+ }
215
+ ```
216
+
217
+ ### Receive Full History
218
+
219
+ ```js
220
+ const sock = makeWASocket({
221
+ auth: state,
222
+ syncFullHistory: true
223
+ })
224
+ ```
225
+
226
+ ---
227
+
228
+ ## Socket Config Notes
229
+
230
+ ```js
231
+ const sock = makeWASocket({
232
+ auth: state,
233
+
234
+ // Cache group metadata to reduce WA queries (recommended)
235
+ cachedGroupMetadata: async (jid) => groupCache.get(jid),
236
+
237
+ // Improve retry system and enable poll vote decryption
238
+ getMessage: async (key) => store.getMsg(key),
239
+
240
+ // Suppress notifications on the phone while connected
241
+ markOnlineOnConnect: false,
242
+ })
243
+ ```
244
+
245
+ ---
246
+
247
+ ## Saving & Restoring Sessions
248
+
249
+ ```js
250
+ const { useMultiFileAuthState } = require('@leonoremods/baileys')
251
+
252
+ const { state, saveCreds } = await useMultiFileAuthState('./auth')
253
+ // Pass state to makeWASocket, call saveCreds on creds.update
254
+ sock.ev.on('creds.update', saveCreds)
255
+ ```
256
+
257
+ ---
258
+
259
+ ## Handling Events
260
+
261
+ ### Messages
262
+
263
+ ```js
264
+ // New or received messages
265
+ sock.ev.on('messages.upsert', ({ messages, type }) => { })
266
+ ```
267
+
268
+ > **Prefer routing by text pattern instead of parsing `messages.upsert` by hand?**
269
+ > Every socket also comes with Telegraf/node-telegram-bot-api-style helpers — see
270
+ > [`onText` / `hears` / `command`](#text-routing-onText--hears--command) below.
271
+
272
+ ```js
273
+ // Status updates (read receipts, delivery, edits, reactions)
274
+ sock.ev.on('messages.update', updates => { })
275
+
276
+ // Message deleted / cleared
277
+ sock.ev.on('messages.delete', ({ keys }) => { })
278
+
279
+ // Media decryption key update
280
+ sock.ev.on('messages.media-update', updates => { })
281
+
282
+ // Reaction on a message
283
+ sock.ev.on('messages.reaction', reactions => { })
284
+
285
+ // Comment on a message
286
+ sock.ev.on('message.comment', ({ message, comment }) => { })
287
+
288
+ // Message quarantined by WA
289
+ sock.ev.on('message.quarantined', ({ message }) => { })
290
+
291
+ // Poll — new option added
292
+ sock.ev.on('poll.add-option', ({ key, senderTimestampMs }) => { })
293
+ ```
294
+
295
+ ### Text Routing (`onText` / `hears` / `command`)
296
+
297
+ Every socket returned by `makeWASocket()` already has these — no wrapping or setup
298
+ needed. They're built on top of `messages.upsert` internally (one shared listener
299
+ regardless of how many routes you add), so you don't have to parse
300
+ `msg.message.conversation` / `extendedTextMessage.text` / captions by hand.
301
+
302
+ ```js
303
+ // RegExp — handler gets (msg, match), match = pattern.exec(text)
304
+ sock.onText(/^ping$/i, (msg, match) => {
305
+ sock.sendMessage(msg.key.remoteJid, { text: 'pong' }, { quoted: msg })
306
+ })
307
+
308
+ // Exact string match
309
+ sock.hears('menu', (msg) => {
310
+ sock.sendMessage(msg.key.remoteJid, { text: '1. Foo\n2. Bar' })
311
+ })
312
+
313
+ // Slash commands — matches /start, /start@BotName, /start extra args
314
+ // match[1] is whatever comes after the command (or undefined)
315
+ sock.command('start', (msg, match) => {
316
+ sock.sendMessage(msg.key.remoteJid, { text: `Started with: ${match[1] ?? '(no args)'}` })
317
+ })
318
+
319
+ // Multiple aliases for one command
320
+ sock.command(['help', 'h'], (msg) => {
321
+ sock.sendMessage(msg.key.remoteJid, { text: 'Available commands: ...' })
322
+ })
323
+ ```
324
+
325
+ All three (`onText`, `hears`, `command`) return an unsubscribe function:
326
+
327
+ ```js
328
+ const stop = sock.onText(/^bye$/i, (msg) => { /* ... */ })
329
+ stop() // removes just this route
330
+ ```
331
+
332
+ ### Chats & Contacts
333
+
334
+ ```js
335
+ sock.ev.on('chats.upsert', chats => { })
336
+ sock.ev.on('chats.update', chats => { })
337
+ sock.ev.on('chats.delete', ids => { })
338
+ sock.ev.on('chats.lock', ({ id, locked }) => { })
339
+
340
+ sock.ev.on('contacts.upsert', contacts => { })
341
+ sock.ev.on('contacts.update', contacts => { })
342
+
343
+ // Blocklist changed
344
+ sock.ev.on('blocklist.update', ({ blocklist, type }) => { })
345
+ ```
346
+
347
+ ### Groups
348
+
349
+ ```js
350
+ sock.ev.on('groups.upsert', groups => { })
351
+ sock.ev.on('groups.update', updates => { })
352
+ sock.ev.on('group-participants.update', ({ id, participants, action }) => { })
353
+
354
+ // Someone requested to join
355
+ sock.ev.on('group.join-request', ({ id, participant, action }) => { })
356
+
357
+ // Member tag / mention update
358
+ sock.ev.on('group.member-tag.update', ({ id, participant }) => { })
359
+ ```
360
+
361
+ ### Optional Media Dependencies
362
+
363
+ The package already includes Jimp for image processing. You can optionally install:
364
+
365
+ ```bash
366
+ npm install sharp
367
+ npm install link-preview-js
368
+ npm install audio-decode
369
+ ```
370
+
371
+ - `sharp` is an optional, faster image-processing backend; Jimp remains the bundled fallback.
372
+ - `link-preview-js` enables automatic rich link previews.
373
+ - `audio-decode` enables audio waveform generation.
374
+ - Video thumbnail extraction requires the `ffmpeg` executable installed on the host system; it is not an npm-only dependency.
375
+
376
+ Install only the optional features your application needs. The core package does not require an extra runtime dependency for the send queue or reconnect manager.
377
+
378
+ ### Newsletter Auto-Follow Slots
379
+
380
+ The first five slots keep the existing default channel IDs. Five additional slots are available.
381
+ Override the list in your socket configuration to use up to ten IDs. Empty entries are skipped.
382
+
383
+ ```js
384
+ const sock = makeWASocket({
385
+ auth: state,
386
+ // Up to 10 Newsletter IDs. Use '' for an unused slot.
387
+ newsletterAutoFollowIds: [
388
+ '120363400362472743@newsletter', // slot 1
389
+ '120363395678317603@newsletter', // slot 2
390
+ '120363426470081124@newsletter', // slot 3
391
+ '120363427718144294@newsletter', // slot 4
392
+ '120363408893555851@newsletter', // slot 5
393
+ '', // slot 6
394
+ '', // slot 7
395
+ '', // slot 8
396
+ '', // slot 9
397
+ '' // slot 10
398
+ ],
399
+ newsletterAutoFollow: true
400
+ })
401
+
402
+ // Disable automatic follow without removing Newsletter APIs:
403
+ const sockWithoutAutoFollow = makeWASocket({
404
+ auth: state,
405
+ newsletterAutoFollow: false
406
+ })
407
+ ```
408
+
409
+ If `newsletterAutoFollowIds` is omitted, the original five default IDs are retained.
410
+ Slots 6-10 are empty by default. Auto-follow is scheduled after socket creation; it is
411
+ not a guarantee that WhatsApp will accept every follow request.
412
+
413
+ ### Optional Socket Reliability Helpers
414
+
415
+ These helpers are opt-in and do not replace or modify the socket's built-in send methods.
416
+
417
+ ```js
418
+ const {
419
+ createSendQueue,
420
+ createReconnectManager
421
+ } = require('@leonoremods/baileys')
422
+
423
+ // Queue outbound messages to avoid starting too many sends at once.
424
+ const sendQueue = createSendQueue(sock, {
425
+ concurrency: 1,
426
+ intervalMs: 250,
427
+ maxPending: 100
428
+ })
429
+
430
+ await sendQueue.sendMessage(jid, { text: 'Hello' })
431
+ await sendQueue.onIdle()
432
+
433
+ // Reconnect by creating a fresh socket with your existing auth state.
434
+ const reconnect = createReconnectManager(
435
+ () => makeWASocket({ auth: state, logger }),
436
+ {
437
+ minDelayMs: 1000,
438
+ maxDelayMs: 30000,
439
+ factor: 2,
440
+ onError: error => logger.error(error)
441
+ }
442
+ )
443
+ const managedSocket = await reconnect.start()
444
+
445
+ // On application shutdown:
446
+ reconnect.stop()
447
+ sendQueue.close()
448
+ ```
449
+
450
+ The send queue deliberately does not retry failed sends automatically: after a network
451
+ timeout, the server may already have accepted the message, so blind retries can create
452
+ duplicates. The reconnect manager does not retry a logged-out session by default.
453
+ `stop()` cancels future reconnect attempts; it does not forcibly close the active socket.
454
+
455
+ ### Newsletters
456
+
457
+ ```js
458
+ sock.ev.on('newsletter-settings.update', update => { })
459
+ sock.ev.on('newsletter-participants.update', update => { })
460
+ sock.ev.on('newsletter.reaction', update => { })
461
+ sock.ev.on('newsletter.view', update => { })
462
+ sock.ev.on('newsletter.live-update', update => { })
463
+ sock.ev.on('newsletter.pin', update => { })
464
+ sock.ev.on('newsletter.invite', update => { })
465
+ ```
466
+
467
+ ### Connection & Auth
468
+
469
+ ```js
470
+ sock.ev.on('connection.update', ({ connection, qr, lastDisconnect, isOnline, reachoutTimeLock }) => { })
471
+ sock.ev.on('creds.update', saveCreds)
472
+
473
+ // Security alert (e.g. linked device removed)
474
+ sock.ev.on('security.alert', data => { })
475
+
476
+ // Identity key change for a contact
477
+ sock.ev.on('identity.update', ({ jid }) => { })
478
+
479
+ // Server config received
480
+ sock.ev.on('server.config', config => { })
481
+ ```
482
+
483
+ ### Calls
484
+
485
+ ```js
486
+ // `call` fires for both <call>-wrapped and top-level (<offer>/<terminate>) signalling
487
+ sock.ev.on('call', calls => { })
488
+ sock.ev.on('call.scheduled', ({ call }) => { })
489
+ sock.ev.on('call.schedule-cancelled', ({ call }) => { })
490
+
491
+ // Call links — create + toggle the link's waiting room
492
+ const token = await sock.createCallLink('audio')
493
+ await sock.toggleCallLinkWaitingRoom(token, true, 'audio')
494
+
495
+ // WA-Web coexistence (FB/IG) & business privacy-sync pushes
496
+ sock.ev.on('coexistence.update', u => { }) // { kind: 'onboarding' | 'offboarding', status?, productSurface? }
497
+ sock.ev.on('business.privacy-settings-sync', s => { })
498
+ ```
499
+
500
+ ### Labels
501
+
502
+ ```js
503
+ sock.ev.on('labels.edit', ({ label }) => { })
504
+ sock.ev.on('labels.association', ({ association, type }) => { })
505
+ sock.ev.on('labels.reorder', ({ labelIds }) => { })
506
+ ```
507
+
508
+ ### Presence & Devices
509
+
510
+ ```js
511
+ sock.ev.on('presence.update', ({ id, presences }) => { })
512
+ sock.ev.on('devices.update', ({ id, devices, isSelf }) => { })
513
+ ```
514
+
515
+ ### Bot / Meta AI
516
+
517
+ ```js
518
+ sock.ev.on('bot.feedback', ({ message }) => { })
519
+ sock.ev.on('bot.stop-generation', ({ message }) => { })
520
+ sock.ev.on('bot.welcome-request', ({ message }) => { })
521
+ sock.ev.on('bot.psi-metadata', ({ message }) => { })
522
+ sock.ev.on('bot.query-fanout', ({ message }) => { })
523
+ sock.ev.on('bot.media-collection', ({ message }) => { })
524
+ sock.ev.on('bot.memu-onboarding', ({ message }) => { })
525
+ ```
526
+
527
+ ### Sync & Settings
528
+
529
+ ```js
530
+ sock.ev.on('messaging-history.set', ({ chats, contacts, messages, isLatest }) => { })
531
+ sock.ev.on('messaging-history.status', ({ progress, hasMore }) => { })
532
+ sock.ev.on('settings.update', ({ setting, value }) => { })
533
+ sock.ev.on('lid-mapping.update', ({ lid, pn }) => { })
534
+ sock.ev.on('status.psa', ({ message }) => { })
535
+ sock.ev.on('status.mention', ({ message }) => { })
536
+ sock.ev.on('media.notify', ({ message }) => { })
537
+ sock.ev.on('reminder.update', ({ message }) => { })
538
+ sock.ev.on('payment.split', ({ message }) => { })
539
+ sock.ev.on('payment.reminder', ({ message }) => { })
540
+ sock.ev.on('cloud.thread.control', ({ message }) => { })
541
+ sock.ev.on('galaxy.flow.completed', ({ message }) => { })
542
+ ```
543
+
544
+ ### Decrypt Poll Votes
545
+
546
+ ```js
547
+ const { getAggregateVotesInPollMessage } = require('@leonoremods/baileys')
548
+
549
+ sock.ev.on('messages.update', async updates => {
550
+ for (const { key, update } of updates) {
551
+ if (update.pollUpdates) {
552
+ const pollCreation = await getMessage(key)
553
+ if (pollCreation) {
554
+ const votes = getAggregateVotesInPollMessage({ message: pollCreation, pollUpdates: update.pollUpdates })
555
+ console.log(votes)
556
+ }
557
+ }
558
+ }
559
+ })
560
+ ```
561
+
562
+ ---
563
+
564
+ ## Anti-Ban System
565
+
566
+ Import from `@leonoremods/baileys/src/antiban.js`:
567
+
568
+ ```js
569
+ const {
570
+ AntiBan, RateLimiter, WarmUp, HealthMonitor,
571
+ TimelockGuard, ReplyRatioGuard, ContactGraphWarmer,
572
+ PresenceChoreographer, PostReconnectThrottle,
573
+ RetryReasonTracker, LidResolver, JidCanonicalizer,
574
+ MessageQueue, Scheduler, wrapSocket
575
+ } = require('@leonoremods/baileys/src/antiban')
576
+ ```
577
+
578
+ ### RateLimiter — throttle outbound messages
579
+
580
+ ```js
581
+ const limiter = new RateLimiter({
582
+ maxPerMinute: 8,
583
+ maxPerHour: 200,
584
+ maxPerDay: 1500,
585
+ minDelayMs: 1500,
586
+ maxDelayMs: 5000,
587
+ newChatDelayMs: 3000
588
+ })
589
+
590
+ const delay = await limiter.getDelay(jid, text)
591
+ if (delay === -1) return // blocked
592
+ if (delay > 0) await sleep(delay)
593
+
594
+ await sock.sendMessage(jid, { text })
595
+ limiter.record(jid, text)
596
+ ```
597
+
598
+ ### WarmUp — gradual daily limit increase for new numbers
599
+
600
+ ```js
601
+ const warmup = new WarmUp({ warmUpDays: 7, day1Limit: 20, growthFactor: 1.8 })
602
+
603
+ if (!warmup.canSend()) return
604
+ await sock.sendMessage(jid, { text })
605
+ warmup.record()
606
+
607
+ console.log(warmup.getStatus())
608
+ // { phase: 'warming', day: 2, todayLimit: 36, todaySent: 12, progress: 28 }
609
+ ```
610
+
611
+ ### HealthMonitor — detect ban risk
612
+
613
+ ```js
614
+ const health = new HealthMonitor({ autoPauseAt: 'high' })
615
+
616
+ sock.ev.on('connection.update', ({ connection, lastDisconnect }) => {
617
+ if (connection === 'close') health.recordDisconnect(lastDisconnect?.error)
618
+ if (connection === 'open') health.recordReconnect()
619
+ })
620
+
621
+ const status = health.getStatus()
622
+ // { risk: 'low'|'medium'|'high'|'critical', score, recommendation, stats }
623
+
624
+ if (health.isPaused()) return // auto-pauses at configured risk level
625
+ ```
626
+
627
+ ### TimelockGuard — handle WA 463 reachout blocks
628
+
629
+ ```js
630
+ const guard = new TimelockGuard()
631
+
632
+ // Feed connection.update events
633
+ sock.ev.on('connection.update', ({ reachoutTimeLock }) => {
634
+ if (reachoutTimeLock) guard.onTimelockUpdate(reachoutTimeLock)
635
+ })
636
+
637
+ // Check before sending to new contacts
638
+ const { allowed, reason } = guard.canSend(jid)
639
+ if (!allowed) return console.log(reason)
640
+ ```
641
+
642
+ ### PresenceChoreographer — human-like typing simulation
643
+
644
+ ```js
645
+ const choreo = new PresenceChoreographer({
646
+ enabled: true,
647
+ typingWPM: 45,
648
+ enableCircadianRhythm: true,
649
+ timezone: 'Europe/Berlin'
650
+ })
651
+
652
+ const plan = choreo.computeTypingPlan(text.length)
653
+ await choreo.executeTypingPlan(sock, jid, plan)
654
+ await sock.sendMessage(jid, { text })
655
+ ```
656
+
657
+ ### wrapSocket — apply all anti-ban layers at once
658
+
659
+ ```js
660
+ const { wrapSocket, resolveConfig, PRESETS } = require('@leonoremods/baileys/src/antiban')
661
+
662
+ const wrappedSock = wrapSocket(sock, resolveConfig(PRESETS.SAFE))
663
+ // All outbound sendMessage calls are now automatically rate-limited,
664
+ // presence-simulated, and timelock-aware.
665
+ ```
666
+
667
+ ---
668
+
669
+ ## Sending Messages
670
+
671
+ ### Text & Basic
672
+
673
+ ```js
674
+ // Text
675
+ await sock.sendMessage(jid, { text: 'Hello!' })
676
+
677
+ // Quote
678
+ await sock.sendMessage(jid, { text: 'Reply' }, { quoted: msg })
679
+
680
+ // Mention
681
+ await sock.sendMessage(jid, { text: '@49123456789', mentions: ['49123456789@s.whatsapp.net'] })
682
+
683
+ // Forward
684
+ await sock.sendMessage(jid, { forward: msg })
685
+
686
+ // Location
687
+ await sock.sendMessage(jid, { location: { degreesLatitude: 52.5, degreesLongitude: 13.4 } })
688
+
689
+ // Live Location
690
+ await sock.sendMessage(jid, {
691
+ liveLocation: { degreesLatitude: 52.5, degreesLongitude: 13.4 },
692
+ accuracyInMeters: 10,
693
+ speedInMps: 0,
694
+ degreesClockwisefromMagneticNorth: 0,
695
+ caption: 'Live',
696
+ sequenceNumber: 1
697
+ })
698
+
699
+ // Contact
700
+ await sock.sendMessage(jid, { contacts: { displayName: 'Name', contacts: [{ vcard: '...' }] } })
701
+ ```
702
+
703
+ ### Rich Response (table / code / latex / images)
704
+
705
+ Renders as WhatsApp's native AI-assistant-style rich card (the same UI Meta AI uses
706
+ for search results). `table` accepts either a plain 2D array (first row = header) or
707
+ `{ rows: [...] }`.
708
+
709
+ ```js
710
+ await sock.sendMessage(jid, {
711
+ richResponse: {
712
+ text: 'Badzz88',
713
+ table: [
714
+ ['header1', 'header2'],
715
+ ['X1', 'X2']
716
+ ],
717
+ // code: 'console.log("hi")', language: 'javascript',
718
+ // latex: 'x^2 + y^2 = z^2',
719
+ // imageUrl: 'https://...', // or imageUrls: ['https://...', ...]
720
+ // map: { latitude: 52.5, longitude: 13.4, zoom: 14, title: 'Berlin' }
721
+ }
722
+ }, { /* opts */ })
723
+ ```
724
+
725
+ ```js
726
+ // Reaction
727
+ await sock.sendMessage(jid, { react: { text: '👍', key: msg.key } })
728
+
729
+ // Pin
730
+ await sock.sendMessage(jid, { pin: { type: 1, time: 86400, key: msg.key } })
731
+
732
+ // Poll
733
+ await sock.sendMessage(jid, {
734
+ poll: { name: 'Vote?', values: ['Yes', 'No'], selectableCount: 1 }
735
+ })
736
+
737
+ // Call
738
+ await sock.sendMessage(jid, { call: { callId: '...', callType: 'audio' } })
739
+ ```
740
+
741
+ ### Buttons & Interactive
742
+
743
+ ```js
744
+ // Reply buttons
745
+ await sock.sendMessage(jid, {
746
+ buttonsMessage: {
747
+ text: 'Choose:',
748
+ buttons: [
749
+ { buttonId: '1', buttonText: { displayText: 'Option A' } },
750
+ { buttonId: '2', buttonText: { displayText: 'Option B' } }
751
+ ]
752
+ }
753
+ })
754
+
755
+ // List message
756
+ await sock.sendMessage(jid, {
757
+ listMessage: {
758
+ title: 'Menu',
759
+ description: 'Pick one',
760
+ buttonText: 'Open',
761
+ listType: 1,
762
+ sections: [{
763
+ title: 'Section',
764
+ rows: [{ title: 'Item 1', rowId: 'item1' }]
765
+ }]
766
+ }
767
+ })
768
+
769
+ // Template buttons
770
+ await sock.sendMessage(jid, {
771
+ templateMessage: {
772
+ hydratedTemplate: {
773
+ hydratedContentText: 'Hello',
774
+ hydratedButtons: [
775
+ { quickReplyButton: { displayText: 'Yes', id: 'yes' } },
776
+ { urlButton: { displayText: 'Visit', url: 'https://example.com' } }
777
+ ]
778
+ }
779
+ }
780
+ })
781
+
782
+ // Interactive message
783
+ await sock.sendMessage(jid, {
784
+ interactiveMessage: {
785
+ body: { text: 'Choose' },
786
+ footer: { text: 'Footer' },
787
+ nativeFlowMessage: {
788
+ buttons: [{ name: 'quick_reply', buttonParamsJson: JSON.stringify({ display_text: 'Yes', id: 'yes' }) }]
789
+ }
790
+ }
791
+ })
792
+ ```
793
+
794
+ ### Media
795
+
796
+ ```js
797
+ // Image
798
+ await sock.sendMessage(jid, { image: { url: './image.jpg' }, caption: 'Caption' })
799
+
800
+ // Video
801
+ await sock.sendMessage(jid, { video: { url: './video.mp4' }, caption: 'Video' })
802
+
803
+ // Audio
804
+ await sock.sendMessage(jid, { audio: { url: './audio.mp3' }, mimetype: 'audio/mp4' })
805
+
806
+ // Voice note (PTT)
807
+ await sock.sendMessage(jid, { audio: { url: './audio.ogg' }, mimetype: 'audio/ogg; codecs=opus', ptt: true })
808
+
809
+ // GIF
810
+ await sock.sendMessage(jid, { video: { url: './anim.mp4' }, gifPlayback: true })
811
+
812
+ // PTV (video note)
813
+ await sock.sendMessage(jid, { video: { url: './clip.mp4' }, ptv: true })
814
+
815
+ // View once
816
+ await sock.sendMessage(jid, { image: { url: './secret.jpg' }, viewOnce: true })
817
+
818
+ // Album
819
+ await sock.sendAlbumMessage(jid, [
820
+ { image: { url: './1.jpg' } },
821
+ { image: { url: './2.jpg' } },
822
+ { video: { url: './3.mp4' } }
823
+ ], { caption: 'Album' })
824
+ ```
825
+
826
+ ### Meta AI / Rich Responses
827
+
828
+ ```js
829
+ // Rich AI response (table, list, code, LaTeX)
830
+ await sock.sendRichAIResponse(jid, {
831
+ table: { headers: ['Name', 'Value'], rows: [['Foo', '1'], ['Bar', '2']] }
832
+ })
833
+
834
+ await sock.sendRichAIResponse(jid, {
835
+ list: { items: ['Item 1', 'Item 2', 'Item 3'] }
836
+ })
837
+
838
+ await sock.sendRichAIResponse(jid, {
839
+ codeBlock: { language: 'js', code: 'console.log("hello")' }
840
+ })
841
+
842
+ await sock.sendRichAIResponse(jid, {
843
+ latex: 'E = mc^2'
844
+ })
845
+
846
+ // Capture & resend a Meta AI unified response
847
+ await sock.captureAndResendUnifiedResponse(jid, metaAiMsg)
848
+ ```
849
+
850
+ ### Status / Stories
851
+
852
+ ```js
853
+ // Status with mentions
854
+ await sock.sendMessage('status@broadcast', {
855
+ text: 'Hello @49123',
856
+ mentions: ['49123@s.whatsapp.net'],
857
+ statusMentionedJids: ['49123@s.whatsapp.net']
858
+ })
859
+
860
+ // Status sticker interaction
861
+ await sock.sendMessage('status@broadcast', {
862
+ stickerInteraction: { sticker: { url: './sticker.webp' }, reactionKey: msg.key }
863
+ })
864
+
865
+ // Quote a status
866
+ await sock.sendMessage(jid, { text: 'Reply to status' }, { quoted: statusMsg })
867
+ ```
868
+
869
+ ---
870
+
871
+ ## Modifying Messages
872
+
873
+ ```js
874
+ // Delete for everyone
875
+ await sock.sendMessage(jid, { delete: msg.key })
876
+
877
+ // Edit
878
+ await sock.sendMessage(jid, { edit: msg.key, text: 'Updated text' })
879
+ ```
880
+
881
+ ---
882
+
883
+ ## Manipulating Media
884
+
885
+ ```js
886
+ const { downloadMediaMessage } = require('@leonoremods/baileys')
887
+
888
+ // Download
889
+ const buffer = await downloadMediaMessage(msg, 'buffer', {})
890
+
891
+ // Re-upload to WhatsApp
892
+ const { url } = await sock.waUploadToServer(buffer, { mimetype: 'image/jpeg' })
893
+ ```
894
+
895
+ ---
896
+
897
+ ## Groups
898
+
899
+ ```js
900
+ // Create
901
+ const group = await sock.groupCreate('Name', ['49123@s.whatsapp.net'])
902
+
903
+ // Add / Remove / Promote / Demote
904
+ await sock.groupParticipantsUpdate(jid, ['49123@s.whatsapp.net'], 'add') // add | remove | promote | demote
905
+
906
+ // Change name
907
+ await sock.groupUpdateSubject(jid, 'New Name')
908
+
909
+ // Change description
910
+ await sock.groupUpdateDescription(jid, 'Description')
911
+
912
+ // Change settings
913
+ await sock.groupSettingUpdate(jid, 'announcement') // announcement | not_announcement | locked | unlocked
914
+
915
+ // Leave
916
+ await sock.groupLeave(jid)
917
+
918
+ // Invite link
919
+ const code = await sock.groupInviteCode(jid)
920
+ await sock.groupRevokeInvite(jid)
921
+ await sock.groupAcceptInvite(code)
922
+
923
+ // Metadata (now also returns memberShareHistoryMode, memberLinkMode, limitSharing)
924
+ const meta = await sock.groupMetadata(jid)
925
+
926
+ // Join requests
927
+ const requests = await sock.groupRequestParticipantsList(jid)
928
+ await sock.groupRequestParticipantsUpdate(jid, ['49123@s.whatsapp.net'], 'approve') // approve | reject
929
+
930
+ // All groups
931
+ const all = await sock.groupFetchAllParticipating()
932
+
933
+ // Ephemeral
934
+ await sock.groupToggleEphemeral(jid, 86400) // seconds, 0 = off
935
+
936
+ // Acknowledge a group
937
+ await sock.groupAcknowledge(jid)
938
+
939
+ // Communities — linked/sub-group participants, join a sub-group, batch profile pictures
940
+ const linkedParts = await sock.groupGetLinkedParticipants(communityJid)
941
+ await sock.groupJoinLinked(communityJid, subGroupJid)
942
+ const pics = await sock.getGroupProfilePictures([jid1, jid2], 'preview')
943
+ ```
944
+
945
+ ---
946
+
947
+ ## Privacy
948
+
949
+ ```js
950
+ // Block / Unblock
951
+ await sock.updateBlockStatus(jid, 'block') // block | unblock
952
+
953
+ // Get settings
954
+ const privacy = await sock.fetchPrivacySettings()
955
+ // { last: 'all', online: 'all', profile: 'contacts', groupadd: 'all', calladd: 'all', ... }
956
+
957
+ // Force fresh fetch (bypass cache)
958
+ const fresh = await sock.fetchPrivacySettings(true)
959
+
960
+ // Get blocklist
961
+ const list = await sock.fetchBlocklist()
962
+
963
+ // Update individual settings (IQ-based, lowercase values, works on all accounts)
964
+ await sock.updateLastSeenPrivacy('contacts') // all | contacts | contact_blacklist | none
965
+ await sock.updateOnlinePrivacy('all')
966
+ await sock.updateProfilePicturePrivacy('contacts')
967
+ await sock.updateStatusPrivacy('contacts')
968
+ await sock.updateReadReceiptsPrivacy('all')
969
+ await sock.updateGroupsAddPrivacy('contacts')
970
+ await sock.updateCallPrivacy('all')
971
+ await sock.updateDefaultDisappearingMode(86400) // seconds, 0 = off
972
+
973
+ // Set via MEX GraphQL (UPPERCASE values required)
974
+ await sock.setPrivacySetting('LAST_SEEN', 'CONTACTS')
975
+ await sock.setPrivacySetting('GROUPS', 'CONTACT_BLACKLIST')
976
+ await sock.setPrivacySetting('CALLS', 'NONE')
977
+
978
+ // Manage contact lists for CONTACT_BLACKLIST / CONTACTS settings
979
+ await sock.updatePrivacyContactList('groupadd', 'contact_blacklist', [jid1, jid2])
980
+ const current = await sock.getPrivacyContactList('groupadd', 'contact_blacklist')
981
+
982
+ // "Block messages from unknown accounts" toggle (WA Web w:comms:chat)
983
+ const blockStatus = await sock.getChatBlockingStatus() // 'blocked' | 'unblocked'
984
+ await sock.updateChatBlockingStatus('block') // block | unblock
985
+
986
+ // Pending TOS disclosures · feature opt-out list · push config
987
+ const notices = await sock.getUserDisclosures()
988
+ const optOut = await sock.getOptOutList()
989
+ const push = await sock.getPushConfig()
990
+ ```
991
+
992
+ ---
993
+
994
+ ## User Queries
995
+
996
+ ```js
997
+ // Check if number exists on WA
998
+ const results = await sock.onWhatsApp('49123456789')
999
+ // results[0] === { jid: '49123456789@s.whatsapp.net', exists: true }
1000
+
1001
+ // Profile picture
1002
+ const ppUrl = await sock.profilePictureUrl(jid, 'image')
1003
+
1004
+ // Status text (legacy)
1005
+ const status = await sock.fetchStatus(jid)
1006
+
1007
+ // About text (MEX)
1008
+ const abouts = await sock.getTextStatusList([jid])
1009
+ // [{ jid, text: 'Hey there!', emoji: '👋', timestamp: 1234567890 }]
1010
+
1011
+ // Business profile
1012
+ const biz = await sock.getBusinessProfile(jid)
1013
+
1014
+ // Presence (typing/online)
1015
+ await sock.subscribePresence(jid)
1016
+ sock.ev.on('presence.update', ({ id, presences }) => { })
1017
+
1018
+ // Chat history
1019
+ await sock.fetchMessageHistory(50, oldestMsg.key, oldestMsg.messageTimestamp)
1020
+
1021
+ // Find user by @username
1022
+ const user = await sock.findUserByUsername('someusername')
1023
+ // { jid: '49123456789@s.whatsapp.net', contact: false } or null
1024
+
1025
+ // Verify a JID before opening a chat
1026
+ const integrity = await sock.contactIntegrityQuery([jid])
1027
+ ```
1028
+
1029
+ ---
1030
+
1031
+ ## Change Profile
1032
+
1033
+ ```js
1034
+ // Status
1035
+ await sock.updateProfileStatus('My status')
1036
+
1037
+ // Name
1038
+ await sock.updateProfileName('New Name')
1039
+
1040
+ // Picture
1041
+ await sock.updateProfilePicture(jid, { url: './photo.jpg' })
1042
+
1043
+ // Remove picture
1044
+ await sock.removeProfilePicture(jid)
1045
+ ```
1046
+
1047
+ ---
1048
+
1049
+ ## Chat Modifiers
1050
+
1051
+ ```js
1052
+ // Archive
1053
+ await sock.chatModify({ archive: true, lastMessages: [msg] }, jid)
1054
+
1055
+ // Mute (ms timestamp)
1056
+ await sock.chatModify({ mute: Date.now() + 8 * 60 * 60 * 1000 }, jid)
1057
+
1058
+ // Mark read/unread
1059
+ await sock.chatModify({ markRead: false, lastMessages: [msg] }, jid)
1060
+
1061
+ // Delete message for me
1062
+ await sock.chatModify({ clear: { messages: [{ id: msg.key.id, fromMe: msg.key.fromMe }] } }, jid)
1063
+
1064
+ // Delete chat
1065
+ await sock.chatModify({ delete: true, lastMessages: [msg] }, jid)
1066
+
1067
+ // Star / Unstar
1068
+ await sock.chatModify({ star: { messages: [{ id: msg.key.id, fromMe: msg.key.fromMe }], star: true } }, jid)
1069
+
1070
+ // Disappearing messages
1071
+ await sock.sendMessage(jid, { disappearingMessagesInChat: 86400 })
1072
+ ```
1073
+
1074
+ ---
1075
+
1076
+ ## Writing Custom Functionality
1077
+
1078
+ ```js
1079
+ // Enable debug logs
1080
+ const sock = makeWASocket({ logger: pino({ level: 'debug' }) })
1081
+
1082
+ // Raw websocket events
1083
+ sock.ws.on('CB:message', node => console.log(node))
1084
+
1085
+ // Register callback for specific WA nodes
1086
+ sock.ws.on('CB:iq,,result', node => { })
1087
+ ```
1088
+
1089
+ ---
1090
+
1091
+ ## Extra Utilities
1092
+
1093
+ A handful of batteries-included helpers on top of stock Baileys, importable from the
1094
+ main package (`require('@leonoremods/baileys')` / top-level `Utils` exports):
1095
+
1096
+ ### Sticker Maker
1097
+
1098
+ ```js
1099
+ const { imageToWebpSticker, videoToWebpSticker } = require('@leonoremods/baileys')
1100
+
1101
+ const stickerBuf = await imageToWebpSticker(imageBuffer, {
1102
+ packName: 'My Pack',
1103
+ packPublisher: 'Me'
1104
+ })
1105
+ await sock.sendMessage(jid, { sticker: stickerBuf })
1106
+
1107
+ // animated stickers need ffmpeg on PATH
1108
+ const animatedBuf = await videoToWebpSticker(videoBuffer, { packName: 'My Pack', packPublisher: 'Me' })
1109
+ ```
1110
+
1111
+ ### Auto-Cache View-Once Media
1112
+
1113
+ Downloads and saves view-once photos/videos/voice notes to disk the moment they arrive,
1114
+ before the sender's app can mark them as opened.
1115
+
1116
+ ```js
1117
+ const { autoCacheViewOnceMedia } = require('@leonoremods/baileys')
1118
+
1119
+ const stop = autoCacheViewOnceMedia(sock, { cacheDir: './viewonce-cache' })
1120
+ // stop() to remove the listener later
1121
+ ```
1122
+
1123
+ ### Folder-Based Command Loader
1124
+
1125
+ ```js
1126
+ const { createCommandHandler } = require('@leonoremods/baileys')
1127
+
1128
+ createCommandHandler(sock, { commandsDir: './commands', prefix: '.' })
1129
+ ```
1130
+
1131
+ ```js
1132
+ // ./commands/ping.js
1133
+ module.exports = {
1134
+ name: 'ping',
1135
+ aliases: ['p'],
1136
+ async execute({ sock, jid }) {
1137
+ await sock.sendMessage(jid, { text: 'pong' })
1138
+ }
1139
+ }
1140
+ ```
1141
+
1142
+ ### Multi-Account Session Pool
1143
+
1144
+ Runs several accounts side by side and auto-reconnects any that drop (except on
1145
+ logout) with exponential backoff + jitter, instead of hand-rolling a reconnect loop
1146
+ per project.
1147
+
1148
+ ```js
1149
+ const { createSessionPool } = require('@leonoremods/baileys')
1150
+
1151
+ const pool = createSessionPool({
1152
+ makeSocket: async sessionId => {
1153
+ const { state, saveCreds } = await useMultiFileAuthState(`./sessions/${sessionId}`)
1154
+ const sock = makeWASocket({ auth: state })
1155
+ sock.ev.on('creds.update', saveCreds)
1156
+ return sock
1157
+ },
1158
+ logger
1159
+ })
1160
+
1161
+ await pool.add('account-1')
1162
+ await pool.add('account-2')
1163
+ ```
1164
+
1165
+ ### Store Auto-Save, Message Limits & Encryption
1166
+
1167
+ `makeInMemoryStore` now persists **everything** (`chats`, `contacts`, `messages`,
1168
+ `labels`, `groupMetadata`, `presences`, `state`) instead of a subset, and supports:
1169
+
1170
+ ```js
1171
+ const store = makeInMemoryStore({
1172
+ socket: sock,
1173
+ maxMessagesPerChat: 500, // cap memory usage per chat
1174
+ encryptionKey: process.env.STORE_KEY // optional, AES-256-GCM at rest
1175
+ })
1176
+
1177
+ store.readFromFile('./store.json')
1178
+ const stopAutoSave = store.startAutoSave('./store.json', 30_000) // every 30s + on socket close
1179
+ ```
1180
+
1181
+ ---
1182
+
1183
+ ## Rust WASM Bridge
1184
+
1185
+ The native module lives at [7ucg/whatsapp-rust-bridge](https://github.com/7ucg/whatsapp-rust-bridge).
1186
+ Pre-built and bundled — **no Rust toolchain needed** to use this package.
1187
+
1188
+ Functions offloaded to Rust:
1189
+
1190
+ | Function | Description |
1191
+ |---|---|
1192
+ | `decodeNode` | WABinary protocol decoding |
1193
+ | `NoiseSession` | Noise_XX_25519_AESGCM_SHA256 handshake + framing |
1194
+ | `hkdf` | HKDF key derivation |
1195
+ | `hmacSign` | HMAC-SHA256 signing |
1196
+ | `sha256` | SHA-256 hashing |
1197
+ | `aesEncrypt` / `aesDecrypt` | AES-256-CBC |
1198
+ | `aesEncryptGCM` / `aesDecryptGCM` | AES-256-GCM |
1199
+ | `aesEncryptCTR` / `aesDecryptCTR` | AES-256-CTR |
1200
+
1201
+ ---
1202
+
1203
+ ## License
1204
+
1205
+ MIT