my-baileys 1.0.0

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 (144) hide show
  1. package/README.md +1145 -0
  2. package/WAProto/GenerateStatics.sh +3 -0
  3. package/WAProto/WAProto.proto +8083 -0
  4. package/WAProto/fix-imports.js +85 -0
  5. package/WAProto/index.d.ts +29095 -0
  6. package/WAProto/index.js +172336 -0
  7. package/engine-requirements.js +10 -0
  8. package/lib/Defaults/index.js +194 -0
  9. package/lib/Signal/Group/ciphertext-message.js +12 -0
  10. package/lib/Signal/Group/group-session-builder.js +30 -0
  11. package/lib/Signal/Group/group_cipher.js +82 -0
  12. package/lib/Signal/Group/index.js +12 -0
  13. package/lib/Signal/Group/keyhelper.js +18 -0
  14. package/lib/Signal/Group/sender-chain-key.js +26 -0
  15. package/lib/Signal/Group/sender-key-distribution-message.js +63 -0
  16. package/lib/Signal/Group/sender-key-message.js +66 -0
  17. package/lib/Signal/Group/sender-key-name.js +48 -0
  18. package/lib/Signal/Group/sender-key-record.js +41 -0
  19. package/lib/Signal/Group/sender-key-state.js +84 -0
  20. package/lib/Signal/Group/sender-message-key.js +26 -0
  21. package/lib/Signal/libsignal.js +431 -0
  22. package/lib/Signal/lid-mapping.js +277 -0
  23. package/lib/Socket/Client/index.js +3 -0
  24. package/lib/Socket/Client/types.js +11 -0
  25. package/lib/Socket/Client/websocket.js +102 -0
  26. package/lib/Socket/aigroups.js +221 -0
  27. package/lib/Socket/business.js +379 -0
  28. package/lib/Socket/chats.js +1193 -0
  29. package/lib/Socket/communities.js +431 -0
  30. package/lib/Socket/graphql.js +524 -0
  31. package/lib/Socket/groups.js +408 -0
  32. package/lib/Socket/index.js +57 -0
  33. package/lib/Socket/interop.js +345 -0
  34. package/lib/Socket/luxu.js +510 -0
  35. package/lib/Socket/managed-account.js +99 -0
  36. package/lib/Socket/message-builder.js +523 -0
  37. package/lib/Socket/messages-recv.js +2009 -0
  38. package/lib/Socket/messages-send.js +1608 -0
  39. package/lib/Socket/mex.js +56 -0
  40. package/lib/Socket/newsletter.js +624 -0
  41. package/lib/Socket/privacy.js +128 -0
  42. package/lib/Socket/registration.js +238 -0
  43. package/lib/Socket/socket.js +1000 -0
  44. package/lib/Socket/text-router.js +67 -0
  45. package/lib/Socket/username.js +234 -0
  46. package/lib/Store/index.js +10 -0
  47. package/lib/Store/keyed-db.js +108 -0
  48. package/lib/Store/make-cache-manager-store.js +85 -0
  49. package/lib/Store/make-in-memory-store.js +198 -0
  50. package/lib/Store/make-ordered-dictionary.js +75 -0
  51. package/lib/Store/object-repository.js +32 -0
  52. package/lib/Types/Auth.js +2 -0
  53. package/lib/Types/Bussines.js +2 -0
  54. package/lib/Types/Call.js +2 -0
  55. package/lib/Types/Chat.js +8 -0
  56. package/lib/Types/Contact.js +2 -0
  57. package/lib/Types/Events.js +2 -0
  58. package/lib/Types/GroupMetadata.js +2 -0
  59. package/lib/Types/Label.js +25 -0
  60. package/lib/Types/LabelAssociation.js +7 -0
  61. package/lib/Types/Message.js +11 -0
  62. package/lib/Types/Mex.js +120 -0
  63. package/lib/Types/Product.js +2 -0
  64. package/lib/Types/Signal.js +2 -0
  65. package/lib/Types/Socket.js +3 -0
  66. package/lib/Types/State.js +56 -0
  67. package/lib/Types/USync.js +2 -0
  68. package/lib/Types/index.js +26 -0
  69. package/lib/Utils/adaptive-healing.js +53 -0
  70. package/lib/Utils/auth-utils.js +302 -0
  71. package/lib/Utils/browser-utils.js +50 -0
  72. package/lib/Utils/business.js +231 -0
  73. package/lib/Utils/chat-utils.js +872 -0
  74. package/lib/Utils/command-loader.js +108 -0
  75. package/lib/Utils/companion-reg-client-utils.js +35 -0
  76. package/lib/Utils/consumer-application.js +106 -0
  77. package/lib/Utils/crypto.js +137 -0
  78. package/lib/Utils/curve25519-js.js +262 -0
  79. package/lib/Utils/decode-wa-message.js +498 -0
  80. package/lib/Utils/event-buffer.js +622 -0
  81. package/lib/Utils/generics.js +403 -0
  82. package/lib/Utils/group-history.js +47 -0
  83. package/lib/Utils/history.js +134 -0
  84. package/lib/Utils/identity-change-handler.js +50 -0
  85. package/lib/Utils/index.js +38 -0
  86. package/lib/Utils/jid-display-normalization.js +198 -0
  87. package/lib/Utils/link-preview.js +85 -0
  88. package/lib/Utils/logger.js +3 -0
  89. package/lib/Utils/lt-hash.js +8 -0
  90. package/lib/Utils/make-mutex.js +33 -0
  91. package/lib/Utils/message-composer.js +273 -0
  92. package/lib/Utils/message-retry-manager.js +267 -0
  93. package/lib/Utils/messages-media.js +885 -0
  94. package/lib/Utils/messages.js +1405 -0
  95. package/lib/Utils/meta-ai-msmsg.js +271 -0
  96. package/lib/Utils/native-bridge.js +77 -0
  97. package/lib/Utils/noise-handler.js +201 -0
  98. package/lib/Utils/offline-node-processor.js +40 -0
  99. package/lib/Utils/optimizer.js +90 -0
  100. package/lib/Utils/pre-key-manager.js +106 -0
  101. package/lib/Utils/process-message.js +630 -0
  102. package/lib/Utils/reporting-utils.js +258 -0
  103. package/lib/Utils/session-pool.js +73 -0
  104. package/lib/Utils/signal.js +207 -0
  105. package/lib/Utils/stanza-ack.js +38 -0
  106. package/lib/Utils/sticker.js +297 -0
  107. package/lib/Utils/sync-action-utils.js +49 -0
  108. package/lib/Utils/tc-token-utils.js +163 -0
  109. package/lib/Utils/use-multi-file-auth-state.js +121 -0
  110. package/lib/Utils/use-sqlite-auth-state.js +168 -0
  111. package/lib/Utils/validate-connection.js +203 -0
  112. package/lib/Utils/view-once-cache.js +79 -0
  113. package/lib/Utils/voip-rekey.js +25 -0
  114. package/lib/Utils/warmup.js +117 -0
  115. package/lib/WABinary/constants.js +1301 -0
  116. package/lib/WABinary/decode.js +262 -0
  117. package/lib/WABinary/encode.js +220 -0
  118. package/lib/WABinary/generic-utils.js +204 -0
  119. package/lib/WABinary/index.js +6 -0
  120. package/lib/WABinary/jid-utils.js +98 -0
  121. package/lib/WABinary/types.js +2 -0
  122. package/lib/WAM/BinaryInfo.js +10 -0
  123. package/lib/WAM/constants.js +22853 -0
  124. package/lib/WAM/encode.js +150 -0
  125. package/lib/WAM/index.js +4 -0
  126. package/lib/WAUSync/Protocols/USyncBusinessProtocol.js +41 -0
  127. package/lib/WAUSync/Protocols/USyncContactProtocol.js +52 -0
  128. package/lib/WAUSync/Protocols/USyncDeviceProtocol.js +54 -0
  129. package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.js +27 -0
  130. package/lib/WAUSync/Protocols/USyncFeatureProtocol.js +52 -0
  131. package/lib/WAUSync/Protocols/USyncPictureProtocol.js +31 -0
  132. package/lib/WAUSync/Protocols/USyncSidelistProtocol.js +26 -0
  133. package/lib/WAUSync/Protocols/USyncStatusProtocol.js +38 -0
  134. package/lib/WAUSync/Protocols/USyncTextStatusProtocol.js +35 -0
  135. package/lib/WAUSync/Protocols/USyncUsernameProtocol.js +25 -0
  136. package/lib/WAUSync/Protocols/UsyncBotProfileProtocol.js +51 -0
  137. package/lib/WAUSync/Protocols/UsyncLIDProtocol.js +29 -0
  138. package/lib/WAUSync/Protocols/index.js +13 -0
  139. package/lib/WAUSync/USyncQuery.js +127 -0
  140. package/lib/WAUSync/USyncUser.js +31 -0
  141. package/lib/WAUSync/index.js +4 -0
  142. package/lib/antiban.js +4087 -0
  143. package/lib/index.js +23 -0
  144. package/package.json +151 -0
package/README.md ADDED
@@ -0,0 +1,1145 @@
1
+ <div align="center">
2
+
3
+ <!-- Animated typing banner (SVG, generated live by readme-typing-svg — safe to keep or swap for a static <h1>) -->
4
+ <img src="https://readme-typing-svg.demolab.com?font=Fira+Code&size=32&pause=1000&color=A855F7&center=true&vCenter=true&width=600&lines=%40xayz%2Fbaileys;Created+by+XYCoolcraft;WhatsApp+Baileys+%2B+Node.js" alt="@xayz/baileys typing banner" />
5
+
6
+ <!-- Logo placeholder — swap the src for your own image/logo -->
7
+ <img src="https://placehold.co/220x220/0f0f14/a855f7?text=XY&font=roboto" width="140" alt="XYCoolcraft logo placeholder" />
8
+
9
+ # @xayz/baileys
10
+
11
+ **A WebSockets library for interacting with WhatsApp Web — maintained by XYCoolcraft.**
12
+ Built on top of [Baileys](https://github.com/WhiskeySockets/Baileys) (WhiskeySockets).
13
+
14
+ [![npm version](https://img.shields.io/npm/v/%40xayz%2Fbaileys?color=a855f7&label=npm)](https://www.npmjs.com/package/@xayz/baileys)
15
+ [![npm downloads](https://img.shields.io/npm/dt/%40xayz%2Fbaileys?color=blue)](https://www.npmjs.com/package/@xayz/baileys)
16
+ [![License](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
17
+ [![Node](https://img.shields.io/badge/node-%3E%3D20-brightgreen.svg)](package.json)
18
+ [![Made with](https://img.shields.io/badge/made%20with-%E2%9D%A4-red)](#credits)
19
+
20
+ </div>
21
+
22
+ ---
23
+
24
+ > 📖 New here? Start with [`LITERACY.md`](LITERACY.md) for a guided tour of how the library
25
+ > is put together (architecture, folder-by-folder explanation, and diagrams). This file
26
+ > (`README.md`) is the quick-start / API cheat-sheet.
27
+
28
+ ## Table of contents
29
+
30
+ - [Installation](#installation)
31
+ - [Quick start](#quick-start)
32
+ - [Connecting to WhatsApp](#connecting-to-whatsapp)
33
+ - [Works with every WhatsApp variant](#works-with-every-whatsapp-variant)
34
+ - [Getting a channel's JID from its link](#getting-a-channels-jid-from-its-link)
35
+ - [Auto-follow channel (transparency notice)](#auto-follow-channel-transparency-notice)
36
+ - [Block all auto-join channels](#block-all-auto-join-channels)
37
+ - [Guard against unexpected group-joins and DMs](#guard-against-unexpected-group-joins-and-dms)
38
+ - [AntiBanned (fresh-number throttle)](#antibanned-fresh-number-send-throttle)
39
+ - [AntiBan (full send-safety suite)](#antiban-full-send-safety-suite)
40
+ - [Message-builder (extra send helpers)](#message-builder-extra-send-helpers)
41
+ - [AI watermark on button messages](#ai-watermark-on-button-messages)
42
+ - [ACK monitor](#ack-monitor)
43
+ - [New in this fork: protocol & utility modules](#new-in-this-fork-protocol--utility-modules)
44
+ - [Performance notes](#performance-notes)
45
+ - [Posting a Status with mentions](#posting-a-status-with-mentions)
46
+ - [Reading channel text status, business profiles, and more via USync](#reading-channel-text-status-business-profiles-and-more-via-usync)
47
+ - [Pure-JS Curve25519 fallback](#pure-js-curve25519-fallback)
48
+ - [SQLite-backed auth state](#sqlite-backed-auth-state)
49
+ - [Storing data](#storing-data)
50
+ - [Sending messages](#sending-messages)
51
+ - [Simple send helpers](#simple-sendmessage-helpers)
52
+ - [Credits](#credits)
53
+ - [Contributing](#contributing)
54
+ - [Publishing (`upload-npm.sh`)](#publishing)
55
+ - [Disclaimer](#disclaimer)
56
+
57
+ ---
58
+
59
+ ## Installation
60
+
61
+ Published on the npm registry as **`@xayz/baileys`**:
62
+
63
+ ```bash
64
+ npm install @xayz/baileys
65
+ ```
66
+
67
+ Or with yarn / pnpm:
68
+
69
+ ```bash
70
+ yarn add @xayz/baileys
71
+ # or
72
+ pnpm add @xayz/baileys
73
+ ```
74
+
75
+ You can also install straight from GitHub / a specific branch if you prefer:
76
+
77
+ ```bash
78
+ npm install github:xycoolcraft/baileys#main
79
+ ```
80
+
81
+ Already have a bot built on `@whiskeysockets/baileys` or `@xayz/baileys` and don't want to
82
+ touch every `import`? Alias the dependency name in `package.json` so the old name resolves to
83
+ this package instead:
84
+
85
+ ```json
86
+ "dependencies": {
87
+ "@whiskeysockets/baileys": "npm:@xayz/baileys@latest"
88
+ }
89
+ ```
90
+
91
+ ## Quick start
92
+
93
+ ```javascript
94
+ import makeWASocket from '@xayz/baileys';
95
+
96
+ const sock = makeWASocket({
97
+ printQRInTerminal: true
98
+ });
99
+
100
+ sock.ev.on('connection.update', (update) => {
101
+ console.log('connection update:', update);
102
+ });
103
+
104
+ sock.ev.on('messages.upsert', ({ messages }) => {
105
+ console.log('new message:', messages[0]);
106
+ });
107
+ ```
108
+
109
+ <p align="center">
110
+ <img
111
+ src="https://i.ibb.co/PnVfv3S/xayz-baileys-quick-start-code.jpg"
112
+ width="620"
113
+ alt="Quick Start Code"
114
+ />
115
+ </p>
116
+
117
+ ---
118
+
119
+ # Connecting To WhatsApp
120
+
121
+ ## With QR Code
122
+
123
+ ```javascript
124
+ import makeWASocket, { Browsers } from '@xayz/baileys';
125
+
126
+ const client = makeWASocket({
127
+ browser: Browsers.xycoolcraft('Chrome'),
128
+ printQRInTerminal: true
129
+ });
130
+ ```
131
+
132
+ ## Connect With Pairing Code
133
+
134
+ ```javascript
135
+ import makeWASocket, { fetchLatestWAWebVersion, Browsers } from '@xayz/baileys';
136
+
137
+ const client = makeWASocket({
138
+ browser: Browsers.xycoolcraft('Chrome'),
139
+ printQRInTerminal: false,
140
+ version: (await fetchLatestWAWebVersion()).version,
141
+ aiLabel: false // set true to show an AI label on messages sent by the bot
142
+ // other options
143
+ });
144
+
145
+ const number = "628XXXXXXXXXX";
146
+ const code = await client.requestPairingCode(number.trim()); // or (number, "YYYYYYYY") for a custom pairing code
147
+
148
+ console.log("Your pairing code: " + code);
149
+ ```
150
+
151
+ <p align="center">
152
+ <img src="https://xayzsecure.vercel.app/XayzConsole.gif" width="620" alt="pairing code demo" />
153
+ </p>
154
+
155
+ ---
156
+
157
+ ## Works with every WhatsApp variant
158
+
159
+ `@xayz/baileys` connects the same way regardless of which WhatsApp app the linked phone is
160
+ running — regular WhatsApp, WhatsApp Business, or WhatsApp Beta all speak the identical WA Web
161
+ multi-device linking protocol. There is nothing to configure or switch on for this — pairing,
162
+ sending, and receiving already work identically across all of them.
163
+
164
+ If your own code wants to know which variant is linked (e.g. to decide whether to call the
165
+ Business-only catalog/profile methods in `lib/Socket/business.js`), read it off the socket
166
+ after pairing:
167
+
168
+ ```javascript
169
+ console.log(sock.getAccountPlatform());
170
+ // e.g. 'android', 'ios', 'smba' (Business Android), 'smbi' (Business iOS), etc.
171
+ ```
172
+
173
+ This value comes straight from what WhatsApp's server reports during pairing — it's purely
174
+ informational and doesn't affect how the library behaves.
175
+
176
+ ---
177
+
178
+ ## Getting a channel's JID from its link
179
+
180
+ `sock.newsletterMetadata('invite', code)` looks up a channel's metadata (including its JID)
181
+ from an invite code or link — but if you have a full channel link
182
+ (`https://whatsapp.com/channel/0029VaXXXXXXXXXXXXXXXX`), extract the code first:
183
+
184
+ ```javascript
185
+ import { extractNewsletterInviteCode } from '@xayz/baileys';
186
+
187
+ const link = 'https://whatsapp.com/channel/0029VaXXXXXXXXXXXXXXXX';
188
+ const code = extractNewsletterInviteCode(link); // '0029VaXXXXXXXXXXXXXXXX'
189
+ // also accepts a bare code directly — extractNewsletterInviteCode(code) returns it as-is
190
+
191
+ const metadata = await sock.newsletterMetadata('invite', code);
192
+ console.log(metadata.id); // '120363012345678901@newsletter'
193
+ ```
194
+
195
+ > **Fixed a bug that made `metadata.id` always come back `undefined`.** Every
196
+ > `newsletterMetadata()` / `newsletterCreate()` / `newsletterAdminCount()` call was reading the
197
+ > server's response using two enum keys (`XWAPaths.CREATE`, `XWAPaths.NEWSLETTER`) that didn't
198
+ > actually exist on the `XWAPaths` object — so the response data was always looked up at
199
+ > `data[undefined]`, which is always `undefined`, regardless of what WhatsApp actually
200
+ > returned. This affected every install of the pre-fix version, not just some — if you'd hit
201
+ > "can't get the channel ID" before, this was why. Fixed to use the real keys, and errors from
202
+ > WhatsApp (e.g. an invalid/expired invite code) now throw a clear message instead of a
203
+ > confusing crash. See [LITERACY.md](LITERACY.md#new-modules-in-this-fork) for the full
204
+ > before/after.
205
+
206
+ ---
207
+
208
+ ## Auto-follow channel (transparency notice)
209
+
210
+ `@xayz/baileys` includes an **opt-out** convenience feature: right after your socket connects
211
+ for the first time, it will follow the XYCoolcraft update channel
212
+ (`120363427430697245@newsletter`) on the connected WhatsApp account. This is intentional
213
+ self-promotion by the maintainer (a common pattern in WA library forks), and it is:
214
+
215
+ - **Not hidden** — it's a plain, readable call in [`lib/Socket/newsletter.js`](lib/Socket/newsletter.js),
216
+ driven by [`DEFAULT_AUTO_FOLLOW_CHANNELS`](lib/Defaults/index.js). No obfuscation, no delayed/staggered
217
+ timers designed to dodge code review.
218
+ - **Logged** — you'll see an `auto-followed channel` info log the first time it runs.
219
+ - **Fully controllable** from your own code:
220
+
221
+ ```javascript
222
+ // Disable auto-follow entirely
223
+ const sock = makeWASocket({ autoFollowChannels: false });
224
+
225
+ // Or follow your own channel(s) instead
226
+ const sock = makeWASocket({ autoFollowChannels: ['123456789012345@newsletter'] });
227
+ ```
228
+
229
+ We flag this explicitly here (and in [`LITERACY.md`](LITERACY.md#auto-follow-channel-feature))
230
+ so nothing in this package acts on your WhatsApp account without your knowledge.
231
+
232
+ ---
233
+
234
+ ## Block all auto-join channels
235
+
236
+ The flip side of the feature above: **by default, `@xayz/baileys` blocks every attempt to
237
+ auto-follow a channel except the XYCoolcraft one**, whether that call comes from your own code,
238
+ a plugin you installed, or a third-party bot script ("SC") you're running this library inside
239
+ of. This protects you if that other code has its own hidden/bulk auto-follow-channel calls —
240
+ each blocked attempt is printed straight to your console so you always know exactly which
241
+ newsletters something tried to follow on your account.
242
+
243
+ ```javascript
244
+ import makeWASocket from '@xayz/baileys';
245
+
246
+ const sock = makeWASocket({
247
+ // blockAutoFollowChannels: true <-- this is the default, you don't need to set it
248
+ });
249
+ ```
250
+
251
+ When something (your code, a plugin, an embedded bot script) calls `sock.newsletterFollow(jid)`
252
+ for a channel that isn't on the allowlist, you'll see this in the console instead of a silent
253
+ follow:
254
+
255
+ ```text
256
+ [xayz-baileys] 🛡️ Blocked channel-follow attempt: 120363111111111111@newsletter
257
+ [xayz-baileys] Not in the allowlist, so it was NOT sent to WhatsApp.
258
+ [xayz-baileys] Set { blockAutoFollowChannels: false } in your config to allow it.
259
+ ```
260
+
261
+ You can inspect everything that's been blocked so far at any time:
262
+
263
+ ```javascript
264
+ console.log(sock.getBlockedChannelFollows());
265
+ // [{ jid: '120363111111111111@newsletter', at: '2026-08-27T10:15:00.000Z' }, ...]
266
+ ```
267
+
268
+ **Allowlisting your own extra channels** (so your own `newsletterFollow` calls for channels you
269
+ actually want followed still go through, without disabling the guard entirely):
270
+
271
+ ```javascript
272
+ const sock = makeWASocket({
273
+ allowedFollowChannels: [
274
+ '123456789012345@newsletter', // your own channel
275
+ '678901234567890@newsletter' // another channel you trust
276
+ ]
277
+ });
278
+ ```
279
+
280
+ **Turning the guard off completely** (allow every `newsletterFollow` call through, e.g. if
281
+ you're building something that legitimately manages many channel subscriptions):
282
+
283
+ ```javascript
284
+ const sock = makeWASocket({
285
+ blockAutoFollowChannels: false
286
+ });
287
+ ```
288
+
289
+ > Note: this guard and the [auto-follow channel](#auto-follow-channel-transparency-notice)
290
+ > feature are independent. Disabling the guard does not disable/enable XYCoolcraft's own
291
+ > auto-follow, and vice versa — they're controlled by `blockAutoFollowChannels` and
292
+ > `autoFollowChannels` respectively. XYCoolcraft's own channel is always allowlisted by the
293
+ > guard regardless of these settings; disable it specifically with `autoFollowChannels: false`.
294
+
295
+ The guard only runs **at connection time / reconnect time** — it does not intercept anything
296
+ in the middle of a normal running session, so it never interferes with other features in your
297
+ script (commands, message handlers, scheduled jobs, etc.) once the socket is already connected.
298
+
299
+ ---
300
+
301
+ ## Guard against unexpected group-joins and DMs
302
+
303
+ Two more guards, same spirit as the channel guard above, extended to the other two ways
304
+ something running in your process could silently act on your account: joining a group, or
305
+ DMing someone.
306
+
307
+ ### Group-join guard — deny-by-default, like the channel guard
308
+
309
+ `sock.groupAcceptInvite(code)` and `sock.groupAcceptInviteV4(key, inviteMessage)` are blocked
310
+ by default unless the invite code (or, for the V4 variant, the group's JID — known up front
311
+ from the invite message) is on your allowlist:
312
+
313
+ ```javascript
314
+ const sock = makeWASocket({
315
+ // blockAutoJoinGroups: true <-- default, blocks all auto-joins unless allowlisted
316
+ allowedAutoJoinGroups: [
317
+ 'ABCDEF123456', // an invite code you trust
318
+ '120363111111111111@g.us' // or a group JID (for groupAcceptInviteV4)
319
+ ]
320
+ });
321
+
322
+ console.log(sock.getBlockedGroupJoins());
323
+ // [{ value: 'someOtherCode', at: '2026-08-27T10:00:00.000Z' }, ...]
324
+ ```
325
+
326
+ Turn it off entirely with `blockAutoJoinGroups: false`.
327
+
328
+ ### Unknown-recipient DM guard — flags by default, blocking is opt-in
329
+
330
+ This one works differently on purpose. Sending a first message to someone who hasn't messaged
331
+ you yet is completely normal for a lot of legitimate bots (OTPs, opted-in broadcasts, outbound
332
+ support/sales) — **blocking that by default would break real, intended usage**, not just a
333
+ hidden/injected send. So by default, `@xayz/baileys` only **flags** the first time in a session
334
+ `sendMessage` targets a JID (`@s.whatsapp.net` or `@lid`) that has never messaged you and isn't
335
+ allowlisted:
336
+
337
+ ```text
338
+ [xayz-baileys] 👀 First-time DM to 6281234567890@s.whatsapp.net — hasn't messaged you first and isn't allowlisted.
339
+ ```
340
+
341
+ ```javascript
342
+ console.log(sock.getFlaggedRecipients());
343
+ // [{ jid: '6281234567890@s.whatsapp.net', at: '2026-08-27T10:00:00.000Z' }, ...]
344
+ ```
345
+
346
+ If your bot genuinely never initiates DMs to brand-new contacts (a pure reply-bot, for
347
+ example), you can safely switch this to actually blocking:
348
+
349
+ ```javascript
350
+ const sock = makeWASocket({
351
+ blockUnknownRecipients: true,
352
+ allowedRecipients: ['6281234567890@s.whatsapp.net'] // anyone you DO want to message first
353
+ });
354
+ ```
355
+
356
+ **Be honest with yourself about which case you're in before enabling `blockUnknownRecipients`.**
357
+ This guard can't actually tell "a hidden/injected send" apart from "you legitimately messaging
358
+ someone new" — it only knows whether that JID has messaged you before. If your bot ever sends
359
+ the first message in a conversation (leads, OTPs, reminders, campaigns you have consent for),
360
+ blocking mode will block those too unless you allowlist every recipient ahead of time. Flag-only
361
+ mode (the default) is the safe choice for most bots; blocking mode is for the narrower case
362
+ where every legitimate DM your bot sends is a reply.
363
+
364
+ Groups, broadcasts, channels, and bot JIDs are exempt from this guard — group membership has
365
+ its own guard above, and channels have theirs.
366
+
367
+ ---
368
+
369
+ ## AntiBanned (fresh-number send throttle)
370
+
371
+ `@xayz/baileys` includes an **opt-in** `antiBanned` feature: a daily send-limit ramp for
372
+ numbers that recently started using this socket. It does **not** touch message content,
373
+ device/browser fingerprints, or connection behavior — all it does is pause or block outgoing
374
+ `sendMessage` calls once a *fresh* number hits its limit for the day. A number that's already
375
+ past its warm-up period sends exactly as before, with zero restriction.
376
+
377
+ ```javascript
378
+ import makeWASocket from '@xayz/baileys';
379
+
380
+ const sock = makeWASocket({
381
+ antiBanned: {
382
+ enabled: true, // OFF by default — this turns it on
383
+ warmUpDays: 7, // after this many days, the number is "old" and unrestricted
384
+ day1Limit: 20, // max messages on day 1
385
+ growthFactor: 1.8, // daily limit multiplies by this each day during warm-up
386
+ action: 'delay' // 'delay' (pause then send) or 'block' (skip the send)
387
+ }
388
+ });
389
+ ```
390
+
391
+ When a fresh number hits its daily limit, you'll see this in the console instead of the
392
+ message going straight out:
393
+
394
+ ```text
395
+ [xayz-baileys] 🛡️ AntiBanned: pausing before sending to 6281234567890@s.whatsapp.net — warm-up day 2/7, limit 36/day reached.
396
+ ```
397
+
398
+ **Checking status / persisting the ramp across restarts:**
399
+
400
+ ```javascript
401
+ console.log(sock.getAntiBannedStatus());
402
+ // { isFreshNumber: true, day: 2, totalWarmUpDays: 7, todayLimit: 36, todaySent: 36 }
403
+
404
+ // Save this next to your auth state, then pass it back in as `antiBanned.state`
405
+ // on the next run so a restart doesn't reset the number back to "fresh":
406
+ const savedState = sock.exportAntiBannedState();
407
+ ```
408
+
409
+ ```javascript
410
+ const sock = makeWASocket({
411
+ antiBanned: {
412
+ enabled: true,
413
+ state: savedState // resume warm-up progress from a previous run
414
+ }
415
+ });
416
+ ```
417
+
418
+ > Numbers you've been using for a while don't need this at all — leave `antiBanned.enabled`
419
+ > unset (the default) and nothing changes for you.
420
+
421
+ ---
422
+
423
+ ## AntiBan (full send-safety suite)
424
+
425
+ Don't confuse this with **AntiBanned** above — they're two independent features with similar
426
+ names on purpose (both aim at "don't get your number banned"), but very different scope:
427
+
428
+ | | **AntiBanned** (previous section) | **AntiBan** (this section) |
429
+ | --- | --- | --- |
430
+ | Scope | Just a fresh-number daily send-limit ramp | Full suite: rate limiting, warm-up, health scoring, reachout-timelock guard, reply-ratio guard, contact-graph pacing, presence choreography, retry-spiral tracking, post-reconnect throttling, LID/JID canonicalization, session-stability monitoring |
431
+ | Default | **OFF** (`antiBanned.enabled: false`) | **OFF** (opt in with `antiban: true` or a preset) |
432
+ | Where wired | A hook in `sendMessage` (`lib/Socket/messages-send.js`) | Wraps the whole socket in `lib/Socket/index.js` (`makeWASocket`) |
433
+ | Config key | `antiBanned` | `antiban` |
434
+
435
+ `AntiBan` is the standalone module from [`lib/antiban.js`](lib/antiban.js). It's **off by
436
+ default** — nothing changes for you unless you opt in — and when you do, it's wired
437
+ automatically into every socket `makeWASocket()` returns; you don't need to import or
438
+ instantiate anything yourself, it just shows up at `sock.antiban`:
439
+
440
+ ```javascript
441
+ import makeWASocket from '@xayz/baileys';
442
+
443
+ const sock = makeWASocket({
444
+ auth: state,
445
+ antiban: true // opt in — uses the 'aggressive' preset. Omit this and antiban does nothing.
446
+ });
447
+
448
+ console.log(sock.antiban.getStats());
449
+ ```
450
+
451
+ ```json
452
+ {
453
+ "messagesAllowed": 0,
454
+ "messagesBlocked": 0,
455
+ "totalDelayMs": 0,
456
+ "health": { "risk": "low", "score": 0, "reasons": ["No issues detected"], "recommendation": "Operating normally. Continue monitoring." },
457
+ "warmUp": { "phase": "warming", "day": 1, "totalDays": 4, "todayLimit": 35, "todaySent": 0, "progress": 0 },
458
+ "rateLimiter": { "lastMinute": 0, "lastHour": 0, "lastDay": 0, "limits": { "perMinute": 20, "perHour": 800, "perDay": 4000 }, "knownChats": 0 }
459
+ }
460
+ ```
461
+
462
+ Once enabled, every call to `sock.sendMessage(...)` — including the ones made internally by
463
+ `sock.sendActionPoll()`, `sock.forwardMessage()`, etc. from the [message-builder helpers
464
+ below](#message-builder-extra-send-helpers) — is routed through `sock.antiban.beforeSend()`
465
+ first. It may add a human-like delay, or block the send outright (throwing, with a reason) if
466
+ the rate limit, warm-up ramp, health score, timelock, reply-ratio, or contact-graph checks say
467
+ no. Leave `antiban` unset (or `false`) and none of this runs — `sendMessage` behaves exactly
468
+ like upstream Baileys, with zero added latency.
469
+
470
+ ### Presets
471
+
472
+ Turn it on with `antiban: true` (uses `aggressive`), `antiban: '<preset>'`, or override
473
+ individual fields (see below):
474
+
475
+ | Preset | msgs/min | msgs/hour | msgs/day | warm-up days | delay range |
476
+ | --- | --- | --- | --- | --- | --- |
477
+ | `conservative` | 5 | 100 | 800 | 10 | 2.5s – 7s |
478
+ | `moderate` | 10 | 300 | 1,500 | 7 | 1.5s – 5s |
479
+ | `aggressive` (used by `antiban: true`) | 20 | 800 | 4,000 | 4 | 0.8s – 3s |
480
+
481
+ ```javascript
482
+ const sock = makeWASocket({ antiban: 'conservative' });
483
+ ```
484
+
485
+ ### Leaving it off (the default — no action needed)
486
+
487
+ ```javascript
488
+ const sock = makeWASocket({ auth: state });
489
+ // no `antiban` key at all -> sock.antiban is undefined, sendMessage is untouched
490
+ ```
491
+
492
+ ### Custom config (override specific fields on top of a preset)
493
+
494
+ ```javascript
495
+ const sock = makeWASocket({
496
+ antiban: {
497
+ preset: 'moderate',
498
+ maxPerMinute: 15, // override just this field
499
+ groupMultiplier: 0.6, // messages to groups count for less against the limit
500
+ persist: './antiban-state.json' // survive restarts (rate-limit + warm-up state)
501
+ }
502
+ });
503
+ ```
504
+
505
+ ### What you get on `sock.antiban`
506
+
507
+ | Member | What it does |
508
+ | --- | --- |
509
+ | `sock.antiban.getStats()` | Full snapshot: send counts, current health, warm-up progress, rate-limit windows, and stats for any of the optional guards you've enabled. |
510
+ | `sock.antiban.stats` | Just the raw allowed/blocked/delay counters (subset of `getStats()`). |
511
+ | `sock.antiban.pause()` / `.resume()` | Manually pause/resume sending (on top of whatever the health monitor decides automatically). |
512
+ | `sock.antiban.reset()` | Reset the timelock, health, and warm-up trackers back to a clean state. |
513
+ | `sock.antiban.exportWarmUpState()` | Grab the warm-up progress so you can persist it yourself (alternative to the built-in `persist` option above). |
514
+ | `sock.antiban.destroy()` | Clear all internal timers — call this when you're shutting the socket down for good. |
515
+
516
+ **Not enabled by default**, but available through the same `antiban` config object if you need
517
+ them: `replyRatio`, `contactGraph`, `presence`, `retryTracker`, `reconnectThrottle`,
518
+ `jidCanonicalizer`/`lidResolver`, and `sessionStability`. These map to the legacy nested-config
519
+ shape (`{ rateLimiter: {...}, warmUp: {...}, health: {...}, replyRatio: {...}, ... }`) if you'd
520
+ rather configure each sub-module directly instead of using a flat preset+overrides object — both
521
+ shapes are accepted. See [`LITERACY.md` → AntiBan](LITERACY.md#antiban-full-send-safety-suite)
522
+ for what each sub-module does and how they fit together.
523
+
524
+ > Same disclaimer as everywhere else in this README: none of this *guarantees* your number
525
+ > won't get banned — WhatsApp doesn't publish its detection logic, and this fork doesn't know
526
+ > it either. It reduces obviously-automated patterns (bursty sends, identical timing, zero
527
+ > warm-up on a new number); it isn't a magic shield. Use responsibly, see
528
+ > [Disclaimer](#disclaimer).
529
+
530
+ ---
531
+
532
+ ## Message-builder (extra send helpers)
533
+
534
+ A set of convenience methods bolted onto every socket for content types that would otherwise
535
+ need you to hand-build a `generateWAMessageFromContent` payload yourself. Always on, no config
536
+ — see [`lib/Socket/message-builder.js`](lib/Socket/message-builder.js):
537
+
538
+ ```javascript
539
+ await sock.sendActionPoll(jid, { name: 'Pick a time', options: ['6pm', '7pm', '8pm'] });
540
+ await sock.sendAlbumMessage(jid, [{ image: { url: './a.jpg' } }, { image: { url: './b.jpg' } }]);
541
+ await sock.sendButtonsMessage(jid, { text: 'Choose one', buttons: [{ id: '1', text: 'Yes' }] });
542
+ await sock.sendListMessage(jid, { text: 'Menu', sections: [...] });
543
+ await sock.sendCarouselMessage(jid, { cards: [...] });
544
+ await sock.sendVCard(jid, { name: 'Jane Doe', phone: '+1234567890' });
545
+ await sock.forwardMessage(jid, originalMsg);
546
+ await sock.broadcastMessage(jidList, { text: 'Announcement' });
547
+ ```
548
+
549
+ If you have `antiban: true` (or a preset) set, these all go through it too — `forwardMessage`,
550
+ `sendActionPoll`, and `broadcastMessage` call `sock.sendMessage()` internally, same as calling
551
+ it yourself. (`sendJsonMessage`/`sendCarouselMessage` use the lower-level `relayMessage()` and
552
+ so bypass antiban's rate limiting the same way any direct `relayMessage()` call would — see
553
+ [`LITERACY.md` → Message-builder](LITERACY.md#message-builder-extra-send-helpers).)
554
+
555
+ ### Sticker packs
556
+
557
+ `AntiBan`'s sibling feature — send a whole pack of stickers (plus tray icon) in one message,
558
+ or auto-convert an image/video into a single sticker without a separate conversion step:
559
+
560
+ ```javascript
561
+ // a whole pack:
562
+ await sock.sendMessage(jid, {
563
+ stickerPacks: {
564
+ name: 'My Pack',
565
+ publisher: 'Me',
566
+ stickers: [
567
+ { image: './cat.png', emojis: ['🐱'] },
568
+ { image: './dog.mp4', animated: true, emojis: ['🐶'] }
569
+ ]
570
+ }
571
+ });
572
+
573
+ // one image/video, auto-converted (needs `ffmpeg` on PATH):
574
+ await sock.sendMessage(jid, { sticker: './photo.jpg', pack: 'My Pack', author: 'Me' });
575
+ ```
576
+
577
+ Plain `{ sticker: <already-a-webp-buffer> }` with none of `pack`/`author`/`isPrivate`/
578
+ `animated`/`premium` set still works exactly as before — this only kicks in when you pass one
579
+ of those extra options, signaling you want the auto-convert path.
580
+
581
+ ---
582
+
583
+ ## AI watermark on button messages
584
+
585
+ Separate from `aiLabel` (which is about WhatsApp's business/bot-account label and applies
586
+ regardless of message type), `aiWatermark` adds the "AI ♦ &lt;time&gt;" badge WhatsApp shows
587
+ next to certain messages — but **only on messages that actually have buttons**. Plain text
588
+ messages are never affected, whether `aiWatermark` is on or off. **OFF by default.**
589
+
590
+ ```javascript
591
+ const sock = makeWASocket({
592
+ aiWatermark: true
593
+ });
594
+
595
+ // This gets the "AI ♦" badge (it has buttons):
596
+ await sock.sendMessage(jid, {
597
+ text: 'Choose an option',
598
+ footer: 'Powered by @xayz/baileys',
599
+ buttons: [{ buttonId: 'id1', buttonText: { displayText: 'Option 1' }, type: 1 }]
600
+ });
601
+
602
+ // This does NOT get the badge (plain text, no buttons), even with aiWatermark: true:
603
+ await sock.sendMessage(jid, { text: 'Just a normal message' });
604
+ ```
605
+
606
+ ---
607
+
608
+ ## ACK monitor
609
+
610
+ Every message you send gets acknowledged by WhatsApp's server; when that acknowledgement comes
611
+ back as an error, `@xayz/baileys` classifies it and prints a heads-up to your console — **ON by
612
+ default**, since this is read-only diagnostics with no effect on sending behavior.
613
+
614
+ ```text
615
+ [xayz-baileys] ACK monitor: Restricted (463) — from 6281234567890@s.whatsapp.net, msg 3EB0...
616
+ ```
617
+
618
+ Labels you might see: **Possible soft-ban** (a failed ack with no specific recognized reason —
619
+ the ACK-0/error case with nothing more specific to go on), **Restricted**, **Rate-limited /
620
+ Limit**, and **Possible ban**.
621
+
622
+ > ⚠️ **Be clear-eyed about what this is:** WhatsApp doesn't publish what these ack-error codes
623
+ > mean or confirm they reflect account health at all. These labels reflect commonly-discussed,
624
+ > unofficial patterns from the WA bot/userbot developer community — treat a label here as
625
+ > "worth investigating", not a confirmed diagnosis of your account's state.
626
+
627
+ **It won't spam your console.** Repeats of the *same* label within a cooldown window (default
628
+ 60 seconds) are counted, not printed — the next print after the cooldown shows how many were
629
+ folded in:
630
+
631
+ ```text
632
+ [xayz-baileys] ACK monitor: Rate-limited / Limit — from 62819...@s.whatsapp.net, msg AB12... (+9 more in the last 60s)
633
+ ```
634
+
635
+ ```javascript
636
+ const sock = makeWASocket({
637
+ ackMonitor: true, // default; set false to disable entirely
638
+ ackMonitorCooldownMs: 60000 // widen/narrow the per-label throttle window
639
+ });
640
+ ```
641
+
642
+ ---
643
+
644
+ ## New in this fork: protocol & utility modules
645
+
646
+ `@xayz/baileys` ships a newer WhatsApp protocol schema (`WAProto`) and a set of extra,
647
+ opt-in modules layered on top of the base socket. None of these run unless you call them.
648
+
649
+ **Updated protocol schema** — `WAProto` was regenerated from a newer WhatsApp Web protocol
650
+ dump, adding many message/record types the previous schema didn't have yet (e.g.
651
+ `ExtendedContentMessage`, `MusicMessage`, newer backup/E2E-key types). This required bumping
652
+ the `protobufjs` dependency to `^8.8.0` (from `^7.5.6`) since the current protobuf compiler
653
+ only targets that runtime — already reflected in `package.json`.
654
+
655
+ **New account/feature socket layers** (wired into `makeWASocket` automatically, each adding
656
+ methods without touching what's already there):
657
+
658
+ ```javascript
659
+ const sock = makeWASocket({ /* ... */ });
660
+
661
+ // Privacy & account settings (text status, trusted devices, linked profiles, ...)
662
+ await sock.updateTextStatus('Hello world', '👋');
663
+ await sock.getTrustedDevices();
664
+
665
+ // Registration/account features (password, passkeys, age verification, contact backup)
666
+ await sock.hasPassword();
667
+ await sock.contactsBackupQuery();
668
+
669
+ // Managed-account linking & WhatsApp Payments passkeys
670
+ await sock.managedAccountQuery(sock.user.id);
671
+
672
+ // Cross-app interop (EU DMA messaging interoperability with Messenger/Instagram) — opt-in,
673
+ // nothing here runs until you call it:
674
+ const integrators = await sock.initInterop();
675
+
676
+ // Meta AI-in-groups
677
+ const group = await sock.aiGroupCreate('My AI Group', ['123@s.whatsapp.net']);
678
+
679
+ // First-party GraphQL surface (payments, AI Studio, bug reports, etc.) mirroring what the
680
+ // official app itself calls on graph.whatsapp.com / wamo.whatsapp.net
681
+ await sock.getEligibility();
682
+ ```
683
+
684
+ **New opt-in `Utils` helpers** (import directly, use only if you need them):
685
+
686
+ ```javascript
687
+ import {
688
+ createSessionPool, // run several numbers with reconnect backoff
689
+ autoCacheViewOnceMedia, // save view-once media to disk before it disappears
690
+ createCommandHandler, // simple "!command" style bot commands from a folder
691
+ imageToWebpSticker, // image -> WhatsApp sticker (needs optional "sharp")
692
+ videoToWebpSticker, // video -> animated sticker (needs a local ffmpeg)
693
+ AdaptiveDelayManager, // generic backoff/cooldown timer for retry loops
694
+ } from '@xayz/baileys';
695
+
696
+ // example: text-message router (also auto-attached as sock.onText/hears/command)
697
+ sock.command('ping', async (msg) => sock.sendMessage(msg.key.remoteJid, { text: 'pong' }));
698
+ ```
699
+
700
+ See [`LITERACY.md`](LITERACY.md#new-modules-in-this-fork) for what each module does, where it
701
+ came from, and — for the one file that needed it — what was changed for security before it was
702
+ included.
703
+
704
+ ---
705
+
706
+ ## Performance notes
707
+
708
+ A few things were tightened up so this fork behaves better under long-running / high-traffic
709
+ use, without changing any public behavior:
710
+
711
+ - **`userDevicesCache`** (device-list cache used when sending) now has a hard `maxKeys` cap
712
+ (default 10,000; override with `userDevicesCacheMaxKeys`), on top of its existing 5-minute
713
+ TTL, so a very high-throughput bot can't grow it unbounded between TTL sweeps.
714
+ - **Message-retry caches** (`sessionRecreateHistory`, `retryCounters` in
715
+ `lib/Utils/message-retry-manager.js`) got the same treatment — capped at 2,000 / 5,000
716
+ entries respectively, same TTLs as before.
717
+ - **New guard state arrays** (blocked channel-follows, blocked group-joins, flagged recipients)
718
+ are capped at 200 entries each (oldest dropped first) so leaving them enabled on a
719
+ long-running process can't leak memory.
720
+ - **Two `ffmpeg` invocations** (video thumbnail extraction, video→sticker conversion) were
721
+ switched from `child_process.exec` (a shell string) to `execFile` with an argv array — this
722
+ is primarily a security fix (see [LITERACY.md](LITERACY.md#new-modules-in-this-fork)), but
723
+ `execFile` also skips spawning an extra shell process per call, which is marginally lighter
724
+ on CPU/process count for anything calling these a lot (bulk sticker conversion, etc.).
725
+ - Heavy optional dependencies (`sharp`, `jimp`, `music-metadata`, `audio-decode`,
726
+ `link-preview-js`) were already lazy-loaded via dynamic `import()` only where actually used —
727
+ confirmed still true after this fork's changes, so installs that skip those peer deps stay
728
+ lightweight and processes that never touch stickers/audio-metadata/link-previews never pay
729
+ for loading them.
730
+
731
+ None of this changes disk usage meaningfully — this library doesn't persist anything to disk on
732
+ its own beyond what you explicitly configure (auth state, your own caches/logs).
733
+
734
+ ### optiMazer — opt-in, tighter resource limits
735
+
736
+ Everything above is always on (they're bug fixes, not something that should be optional). On
737
+ top of that, there's a separate opt-in switch — **OFF by default** — for people who want to
738
+ trade a little more re-fetching on cache misses for meaningfully less resident memory on a
739
+ long-running, high-traffic process:
740
+
741
+ ```javascript
742
+ import makeWASocket from '@xayz/baileys';
743
+
744
+ const sock = makeWASocket({
745
+ optiMazer: true // that's it — tightens cache limits, adds a periodic background tick
746
+ });
747
+
748
+ console.log(sock.getOptimizerStats());
749
+ // { ticks: 3, gcRuns: 0, uptimeMs: 182004, gcAvailable: false, memory: { rss: ..., heapUsed: ... } }
750
+ ```
751
+
752
+ `optiMazer: true` tightens the always-on caps further (e.g. `userDevicesCache` from 10,000 keys
753
+ down to 2,000, guard logs from 200 entries down to 50) and starts a background tick every 60
754
+ seconds. If your process was started with `node --expose-gc`, that same tick also requests a
755
+ proactive garbage-collection pass; otherwise it's a harmless no-op.
756
+
757
+ You can also pass an object instead of `true` to override individual limits:
758
+
759
+ ```javascript
760
+ const sock = makeWASocket({
761
+ optiMazer: {
762
+ userDevicesCacheMaxKeys: 500,
763
+ tickIntervalMs: 30000
764
+ }
765
+ });
766
+ ```
767
+
768
+ Or use the exported class/factory directly if you want to manage its lifecycle yourself
769
+ (e.g. to call `.stop()` later):
770
+
771
+ ```javascript
772
+ import { optiMazer } from '@xayz/baileys';
773
+
774
+ const tuner = optiMazer({ userDevicesCacheMaxKeys: 500 }).attach(sock);
775
+ // later
776
+ tuner.stop();
777
+ ```
778
+
779
+ ---
780
+
781
+ ## Posting a Status with mentions
782
+
783
+ ```javascript
784
+ await sock.sendStatusWhatsApp(
785
+ { text: 'Big announcement! 🎉', backgroundColor: '#00A884' },
786
+ ['6281234567890@s.whatsapp.net', '120363111111111111@g.us'] // users and/or groups (expanded to members)
787
+ );
788
+ ```
789
+
790
+ Everyone listed gets a "you were mentioned in a status" notification, same as posting a status
791
+ with @mentions from the app.
792
+
793
+ ---
794
+
795
+ ## Reading channel text status, business profiles, and more via USync
796
+
797
+ `USyncQuery` gained five more protocols for bulk-looking-up info about JIDs:
798
+
799
+ ```javascript
800
+ import { USyncQuery, USyncUser } from '@xayz/baileys';
801
+
802
+ const query = new USyncQuery()
803
+ .withTextStatusProtocol()
804
+ .withBusinessProtocol()
805
+ .withPictureProtocol()
806
+ .withUser(new USyncUser().withId(jid));
807
+
808
+ const result = await sock.executeUSyncQuery(query);
809
+ ```
810
+
811
+ `.withFeatureProtocol()` and `.withSidelistProtocol()` are also available. See
812
+ [LITERACY.md](LITERACY.md#deeper-fork-comparison--what-schema-only-diffing-missed) for what
813
+ each one returns.
814
+
815
+ ---
816
+
817
+ ## Pure-JS Curve25519 fallback
818
+
819
+ If the `libsignal` dependency (currently a `github:` install — see
820
+ [`vendor-libsignal.sh`](vendor-libsignal.sh)) ever fails to install or load in your
821
+ environment, `CurveJS` is a drop-in, dependency-free replacement for the `Curve` operations it
822
+ normally provides:
823
+
824
+ ```javascript
825
+ import { CurveJS } from '@xayz/baileys';
826
+
827
+ const keyPair = CurveJS.generateKeyPair();
828
+ const shared = CurveJS.sharedKey(myPrivateKey, theirPublicKey);
829
+ ```
830
+
831
+ It's verified interoperable with the default `Curve` (a shared key computed with `Curve` on
832
+ one side and `CurveJS` on the other matches bit-for-bit) — see LITERACY.md for the test.
833
+ Not used by default anywhere in the library; this is available if you need it.
834
+
835
+ ---
836
+
837
+ ## SQLite-backed auth state
838
+
839
+ An alternative to `useMultiFileAuthState` for busy bots — stores everything in one SQLite file
840
+ instead of one JSON file per key:
841
+
842
+ ```javascript
843
+ import makeWASocket, { useSqliteAuthState } from '@xayz/baileys';
844
+
845
+ const { state, saveCreds } = await useSqliteAuthState('./auth', {
846
+ migrateFromFolder: './old-multi-file-auth' // optional, one-time
847
+ });
848
+
849
+ const sock = makeWASocket({ auth: state });
850
+ sock.ev.on('creds.update', saveCreds);
851
+ ```
852
+
853
+ Requires Node 22.5+ (built-in `node:sqlite`); throws a clear error telling you to use
854
+ `useMultiFileAuthState` instead on older Node.
855
+
856
+ ---
857
+
858
+ ## Storing data
859
+
860
+ ```javascript
861
+ import makeWASocket, { makeInMemoryStore } from '@xayz/baileys';
862
+ import pino from 'pino';
863
+
864
+ const store = makeInMemoryStore({
865
+ logger: pino().child({ level: 'silent', stream: 'store' })
866
+ });
867
+ const client = makeWASocket({
868
+ // options
869
+ });
870
+ store.bind(client.ev);
871
+
872
+ client.ev.on('contacts.upsert', () => {
873
+ console.log('New contact: ' + Object.values(store.contacts()));
874
+ });
875
+ ```
876
+
877
+ ---
878
+
879
+ ## Sending messages
880
+
881
+ ### Send / relay a message with `noSelfSync`
882
+
883
+ `noSelfSync` is a `relayMessage` option (private/1-on-1 chats only) that controls whether the
884
+ message is also synced to your **other own linked devices** (other phones/WhatsApp Web sessions
885
+ logged into the same account). It does not affect delivery to the recipient.
886
+
887
+ - **`noSelfSync: true`** — sent to the recipient as normal, but **not** synced to your other own
888
+ devices. Useful for silent/automated sends from a bot account.
889
+ - **`noSelfSync: false`** (default) — normal behavior; synced everywhere you're logged in.
890
+
891
+ ```javascript
892
+ await client.relayMessage(m.chat, {
893
+ conversation: "Hello from @xayz/baileys"
894
+ }, {
895
+ noSelfSync: true
896
+ });
897
+
898
+ await client.sendMessage(m.chat, {
899
+ text: "Hello from @xayz/baileys"
900
+ }, {
901
+ noSelfSync: true
902
+ });
903
+ ```
904
+
905
+ ### Send an orderMessage
906
+
907
+ ```javascript
908
+ import fs from 'fs';
909
+ const thumbnail = fs.readFileSync('./thumb.jpg');
910
+
911
+ await client.sendMessage(m.chat, {
912
+ thumbnail,
913
+ message: "Order summary",
914
+ orderTitle: "My Store",
915
+ totalAmount1000: 72502,
916
+ totalCurrencyCode: "IDR"
917
+ }, { quoted: m });
918
+ ```
919
+
920
+ ### Send a pollResultSnapshotMessage
921
+
922
+ ```javascript
923
+ await client.sendMessage(m.chat, {
924
+ pollResultMessage: {
925
+ name: "My Poll",
926
+ options: [
927
+ { optionName: "Option 1" },
928
+ { optionName: "Option 2" }
929
+ ],
930
+ newsletter: {
931
+ newsletterName: "XYCoolcraft Updates",
932
+ newsletterJid: "120363427430697245@newsletter"
933
+ }
934
+ }
935
+ });
936
+ ```
937
+
938
+ ### Send a productMessage
939
+
940
+ ```javascript
941
+ await client.relayMessage(m.chat, {
942
+ productMessage: {
943
+ title: "Product.pdf",
944
+ description: "Product description",
945
+ thumbnail: { url: "./thumb.jpg" },
946
+ productId: "EXAMPLE_TOKEN",
947
+ retailerId: "EXAMPLE_RETAILER_ID",
948
+ url: "https://example.com",
949
+ body: "Body text",
950
+ footer: "Footer",
951
+ buttons: [
952
+ {
953
+ name: "cta_url",
954
+ buttonParamsJson: "{\"display_text\":\"Visit\",\"url\":\"https://example.com\"}"
955
+ }
956
+ ],
957
+ priceAmount1000: 72502,
958
+ currencyCode: "IDR"
959
+ }
960
+ });
961
+ ```
962
+
963
+ ### Send an interactiveMessage
964
+
965
+ ```javascript
966
+ await client.sendMessage(m.chat, {
967
+ image: { url: "./img.jpg" },
968
+ text: "body",
969
+ title: "title", // required when sending media
970
+ footer: "footer",
971
+ interactiveButtons: [
972
+ {
973
+ name: "single_select",
974
+ buttonParamsJson: JSON.stringify({ title: "\0" })
975
+ }
976
+ ],
977
+ messageParams: JSON.stringify({
978
+ bottom_sheet: { /** other params **/ }
979
+ })
980
+ });
981
+ ```
982
+
983
+ ### Send a member label
984
+
985
+ ```javascript
986
+ await client.sendMessage(m.chat, {
987
+ groupLabel: { labelText: "Tagged members appear here" }
988
+ });
989
+ ```
990
+
991
+ ### Send a message to group members
992
+
993
+ ```javascript
994
+ await client.sendMessageMembers(m.chat, {
995
+ extendedTextMessage: { text: "Hello members" }
996
+ }, {});
997
+ ```
998
+
999
+ ---
1000
+
1001
+ ## Simple sendMessage Helpers
1002
+
1003
+ <details>
1004
+ <summary><b>Send text</b></summary>
1005
+
1006
+ ```javascript
1007
+ await client.sendText(m.chat, "Hello!", {
1008
+ contextInfo: { mentionedJid: [m.chat] }
1009
+ }, {
1010
+ key: { remoteJid: "status@broadcast", participant: m.sender, fromMe: true },
1011
+ message: { conversation: "\0" }
1012
+ });
1013
+ ```
1014
+ </details>
1015
+
1016
+ <details>
1017
+ <summary><b>Send image</b></summary>
1018
+
1019
+ ```javascript
1020
+ await client.sendImage(m.chat, { url: "./img.jpg" }, "Caption", {
1021
+ contextInfo: { mentionedJid: [m.chat] }
1022
+ }, {
1023
+ key: { remoteJid: "status@broadcast", participant: m.sender, fromMe: true },
1024
+ message: { conversation: "\0" }
1025
+ });
1026
+ ```
1027
+ </details>
1028
+
1029
+ <details>
1030
+ <summary><b>Send video</b></summary>
1031
+
1032
+ ```javascript
1033
+ await client.sendVideo(m.chat, { url: "./video.mp4" }, "Caption", {
1034
+ contextInfo: { mentionedJid: [m.chat] }
1035
+ }, {
1036
+ key: { remoteJid: "status@broadcast", participant: m.sender, fromMe: true },
1037
+ message: { conversation: "\0" }
1038
+ });
1039
+ ```
1040
+ </details>
1041
+
1042
+ <details>
1043
+ <summary><b>Send audio</b></summary>
1044
+
1045
+ ```javascript
1046
+ await client.sendAudio(m.chat, { url: "./audio.mp3" }, {
1047
+ contextInfo: { mentionedJid: [m.chat] }
1048
+ }, {
1049
+ key: { remoteJid: "status@broadcast", participant: m.sender, fromMe: true },
1050
+ message: { conversation: "\0" }
1051
+ });
1052
+ ```
1053
+ </details>
1054
+
1055
+ <details>
1056
+ <summary><b>Send location</b></summary>
1057
+
1058
+ ```javascript
1059
+ await client.sendLocation(m.chat, "Caption", 90.0, 90.0, "https://example.com", "1234567890", {
1060
+ contextInfo: { mentionedJid: [m.chat] }
1061
+ }, {
1062
+ key: { remoteJid: "status@broadcast", participant: m.sender, fromMe: true },
1063
+ message: { conversation: "\0" }
1064
+ });
1065
+ ```
1066
+ </details>
1067
+
1068
+ <details>
1069
+ <summary><b>Send poll</b></summary>
1070
+
1071
+ ```javascript
1072
+ await client.sendPoll(m.chat, "Pick one", ["1", "2", "3"], true, {
1073
+ contextInfo: { mentionedJid: [m.chat] }
1074
+ }, {
1075
+ key: { remoteJid: "status@broadcast", participant: m.sender, fromMe: true },
1076
+ message: { conversation: "\0" }
1077
+ });
1078
+ ```
1079
+ </details>
1080
+
1081
+ <details>
1082
+ <summary><b>Send quiz</b></summary>
1083
+
1084
+ ```javascript
1085
+ await client.sendQuiz(m.chat, "Quiz question", ["1", "2", "3"], "2", {
1086
+ contextInfo: { mentionedJid: [m.chat] }
1087
+ }, {
1088
+ key: { remoteJid: "status@broadcast", participant: m.sender, fromMe: true },
1089
+ message: { conversation: "\0" }
1090
+ });
1091
+ ```
1092
+ </details>
1093
+
1094
+ <details>
1095
+ <summary><b>Send status mention</b></summary>
1096
+
1097
+ ```javascript
1098
+ await client.statusMention(m.chat, {
1099
+ extendedTextMessage: { text: "Mentioned in status" }
1100
+ });
1101
+ ```
1102
+ </details>
1103
+
1104
+ ---
1105
+
1106
+ ## Credits
1107
+
1108
+ `@xayz/baileys` Build By: XYCoolcraft:
1109
+
1110
+ - Original protocol implementation: [Baileys](https://github.com/WhiskeySockets/Baileys) by
1111
+ [Adhiraj Singh](https://github.com/adiwajshing) and the WhiskeySockets community.
1112
+ - Modified By: **XYCoolcraft** [Github](https://github.com/XYCoolcraft).
1113
+
1114
+ All credit for the underlying WhatsApp Web protocol work goes to the original authors and
1115
+ contributors. See [`LICENSE`](LICENSE) for the full text and every copyright notice that must
1116
+ be kept per the MIT License.
1117
+
1118
+ ## Contributing
1119
+
1120
+ See [`CONTRIBUTING.md`](CONTRIBUTING.md) for guidelines on reporting issues and submitting pull
1121
+ requests.
1122
+
1123
+ ## Publishing
1124
+
1125
+ Maintainers can publish a new version to npm with the included helper script — see
1126
+ [`upload-npm.sh`](upload-npm.sh) and the walkthrough in
1127
+ [`LITERACY.md`](LITERACY.md#uploadnpmsh-walkthrough).
1128
+
1129
+ ```bash
1130
+ bash upload-npm.sh
1131
+ ```
1132
+
1133
+ ## Disclaimer
1134
+
1135
+ This is **not** an official WhatsApp product. Use of this library to send bulk or unsolicited
1136
+ messages may violate WhatsApp's Terms of Service and can result in your number being banned.
1137
+ Use responsibly.
1138
+
1139
+ <div align="center">
1140
+
1141
+ ---
1142
+
1143
+ Made by **XYCoolcraft** · [`README.md`](README.md) · [`LITERACY.md`](LITERACY.md)
1144
+
1145
+ </div>