@xbibzlibrary/whatsbibz 1.3.1 → 1.4.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.
- package/CHANGELOG.md +143 -99
- package/README.cn.md +604 -0
- package/README.id.md +604 -0
- package/README.md +505 -181
- package/SECURITY.md +91 -0
- package/assets/logo/README.md +11 -13
- package/assets/logo/preview.png +0 -0
- package/lib/BibzWhats/banner.d.ts +31 -0
- package/lib/BibzWhats/banner.js +120 -0
- package/lib/BibzWhats/client.d.ts +7 -1
- package/lib/BibzWhats/client.js +12 -0
- package/lib/BibzWhats/index.d.ts +1 -0
- package/lib/BibzWhats/index.js +1 -0
- package/lib/BibzWhats/version.js +2 -2
- package/lib/Defaults/index.js +1 -1
- package/lib/Modded/message_builder.js +25 -8
- package/lib/Utils/browser-utils.js +2 -2
- package/lib/Utils/generics.js +1 -1
- package/lib/Utils/rich-messages.js +10 -0
- package/package.json +20 -6
package/README.md
CHANGED
|
@@ -1,280 +1,604 @@
|
|
|
1
1
|
<p align="center">
|
|
2
|
-
<
|
|
3
|
-
onerror="this.src='assets/logo/preview.png'">
|
|
2
|
+
<a href="https://www.npmjs.com/package/@xbibzlibrary/whatsbibz"><img src="https://cdn.jsdelivr.net/gh/XbibzOfficial777/whatsbibz@main/assets/logo/preview.webp" alt="WhatsBibz" width="640"></a>
|
|
4
3
|
</p>
|
|
5
4
|
|
|
6
|
-
|
|
5
|
+
<h1 align="center">WhatsBibz</h1>
|
|
7
6
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
7
|
+
<p align="center">
|
|
8
|
+
WhatsApp Web multi-device library for Node.js — a maintained fork of Baileys with a high-level client,
|
|
9
|
+
custom pairing codes, self-healing sessions, and a linked-device identity that is configurable or chosen automatically.
|
|
10
|
+
</p>
|
|
11
|
+
|
|
12
|
+
<p align="center">
|
|
13
|
+
<a href="https://www.npmjs.com/package/@xbibzlibrary/whatsbibz"><img src="https://img.shields.io/npm/v/%40xbibzlibrary%2Fwhatsbibz?label=npm&color=0a6958" alt="npm version"></a>
|
|
14
|
+
<a href="https://www.npmjs.com/package/@xbibzlibrary/whatsbibz"><img src="https://img.shields.io/npm/dm/%40xbibzlibrary%2Fwhatsbibz?color=0a6958" alt="npm downloads"></a>
|
|
15
|
+
<a href="https://github.com/XbibzOfficial777/whatsbibz/actions/workflows/ci.yml"><img src="https://github.com/XbibzOfficial777/whatsbibz/actions/workflows/ci.yml/badge.svg" alt="CI"></a>
|
|
16
|
+
<a href="#requirements"><img src="https://img.shields.io/badge/node-%E2%89%A5%2020-0a6958" alt="Node.js >= 20"></a>
|
|
17
|
+
<a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-0a6958" alt="MIT"></a>
|
|
18
|
+
</p>
|
|
19
|
+
|
|
20
|
+
<p align="center">
|
|
21
|
+
<b>English</b> · <a href="README.id.md">Bahasa Indonesia</a> · <a href="README.cn.md">简体中文</a>
|
|
22
|
+
</p>
|
|
11
23
|
|
|
12
|
-
|
|
13
|
-
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## Table of contents
|
|
27
|
+
|
|
28
|
+
- [Why WhatsBibz](#why-whatsbibz)
|
|
29
|
+
- [Requirements](#requirements)
|
|
30
|
+
- [Installation](#installation)
|
|
31
|
+
- [Quick start](#quick-start)
|
|
32
|
+
- [The high-level client: `createBibzWhats()`](#the-high-level-client-createbibzwhats)
|
|
33
|
+
- [Options](#options)
|
|
34
|
+
- [Events](#events)
|
|
35
|
+
- [Properties and methods](#properties-and-methods)
|
|
36
|
+
- [Connection lifecycle](#connection-lifecycle)
|
|
37
|
+
- [Linked-device identity](#linked-device-identity)
|
|
38
|
+
- [Automatic mode (default)](#automatic-mode-default)
|
|
39
|
+
- [Custom mode](#custom-mode)
|
|
40
|
+
- [What WhatsApp accepts](#what-whatsapp-accepts)
|
|
41
|
+
- [Sending and reading messages](#sending-and-reading-messages)
|
|
42
|
+
- [Text formatting and chat markup](#text-formatting-and-chat-markup)
|
|
43
|
+
- [Interactive buttons](#interactive-buttons)
|
|
44
|
+
- [Rich and AI-styled messages](#rich-and-ai-styled-messages)
|
|
45
|
+
- [Low-level API (Baileys-compatible)](#low-level-api-baileys-compatible)
|
|
46
|
+
- [TypeScript](#typescript)
|
|
47
|
+
- [Migrating from Baileys / ourin-baileys](#migrating-from-baileys--ourin-baileys)
|
|
48
|
+
- [Troubleshooting](#troubleshooting)
|
|
49
|
+
- [Testing](#testing)
|
|
50
|
+
- [Project structure](#project-structure)
|
|
51
|
+
- [Versioning and compatibility](#versioning-and-compatibility)
|
|
52
|
+
- [Contributing](#contributing)
|
|
53
|
+
- [Security](#security)
|
|
54
|
+
- [License](#license)
|
|
55
|
+
- [Support & community](#support--community)
|
|
56
|
+
|
|
57
|
+
## Why WhatsBibz
|
|
58
|
+
|
|
59
|
+
WhatsBibz is a full fork of [Baileys](https://github.com/WhiskeySockets/Baileys) v7 (via `ourin-baileys@9.0.21`), patched up to the current upstream `master`, with a built-in layer that handles the connection, identity and session plumbing:
|
|
60
|
+
|
|
61
|
+
| Problem | What WhatsBibz does |
|
|
62
|
+
|---|---|
|
|
63
|
+
| Pairing "succeeds" but the phone never shows a notification | `requestPairingCode()` waits for the server's acknowledgement. Rejections (`400`, `429`) and timeouts surface as errors instead of a fake success. |
|
|
64
|
+
| Linked-device name gets rejected by the server (`428` before QR, `400` at pairing) | Identity is **configurable** (`identity: 'archLinux:Chrome'`) or **automatic**: the most stable profile is used first and the client rotates to the next one only when the server rejects it. The working profile is persisted so the device name never changes between restarts. |
|
|
65
|
+
| Corrupt or logged-out sessions loop forever | `401 / 500 / 411 / corrupt` → session folder wiped, new credentials, re-pair — bounded by `maxSessionWipes`. |
|
|
66
|
+
| `companion_reg_refresh` notifications kill unpaired sessions | Handled inside the socket (no `postinstall` patches). |
|
|
67
|
+
| Leaked timers after `close()`, ghost reconnects | `close()` cancels every scheduled reconnect; verified over 40 create→close cycles. |
|
|
68
|
+
| TypeScript users get an empty `WAProto/index.d.ts` from upstream | Complete declarations for all 217 `proto` namespaces; `tsc --strict` passes with `skipLibCheck: false`. |
|
|
69
|
+
|
|
70
|
+
Everything from Baileys is still exported — existing code keeps working with only the import specifier changed.
|
|
14
71
|
|
|
15
|
-
|
|
16
|
-
Baileys v7 (basis `ourin-baileys@9.0.21`) dengan lapisan API tingkat tinggi,
|
|
17
|
-
pairing code custom, self-healing session, dan handler protokol terbaru
|
|
18
|
-
(`companion_reg_refresh`) yang **sudah tertanam di source** (tidak perlu
|
|
19
|
-
patch `postinstall`).
|
|
72
|
+
## Requirements
|
|
20
73
|
|
|
21
|
-
-
|
|
22
|
-
-
|
|
23
|
-
- `
|
|
74
|
+
- **Node.js ≥ 20** (tested on 20, 22, 24 and 26)
|
|
75
|
+
- ESM only (`"type": "module"` or dynamic `import()`)
|
|
76
|
+
- Optional peers: `sharp` (media thumbnails), `qrcode-terminal` (ASCII QR fallback), `link-preview-js`, `jimp`, `audio-decode`, `@roamhq/wrtc` (VoIP)
|
|
24
77
|
|
|
25
|
-
##
|
|
78
|
+
## Installation
|
|
26
79
|
|
|
27
80
|
```bash
|
|
28
81
|
npm install @xbibzlibrary/whatsbibz
|
|
29
|
-
# atau lokal (vendored di proyek):
|
|
30
|
-
npm install ./BibzWhats
|
|
31
82
|
```
|
|
32
83
|
|
|
33
|
-
|
|
34
|
-
sebagai vendored `bibzwhats` (`"bibzwhats": "file:./BibzWhats"`), cukup ganti specifier-nya —
|
|
35
|
-
API-nya sama persis.
|
|
36
|
-
|
|
37
|
-
Dependensi opsional (peer): `sharp` (thumbnail media), `qrcode-terminal`
|
|
38
|
-
(QR ASCII saat fallback), `link-preview-js`, `jimp`, `audio-decode`,
|
|
39
|
-
`@roamhq/wrtc` (VoIP).
|
|
40
|
-
|
|
41
|
-
## Mulai cepat — `createBibzWhats()`
|
|
84
|
+
## Quick start
|
|
42
85
|
|
|
43
86
|
```js
|
|
44
87
|
import { createBibzWhats, extractMessage, sendText } from '@xbibzlibrary/whatsbibz';
|
|
45
88
|
|
|
46
89
|
const client = await createBibzWhats({
|
|
47
|
-
phone: '6281234567890',
|
|
48
|
-
pairingCode: 'XBIBZPRO',
|
|
49
|
-
authDir: '
|
|
50
|
-
printQR: true, // QR ASCII bila pairing code tak kunjung diterima
|
|
90
|
+
phone: '6281234567890', // bot number, digits with country code
|
|
91
|
+
pairingCode: 'XBIBZPRO', // optional: custom 8-character code (A–Z, 0–9)
|
|
92
|
+
authDir: 'whatsbibz-session',
|
|
51
93
|
});
|
|
52
94
|
|
|
53
|
-
client.on('pairing-code', (code
|
|
54
|
-
console.log(`PAIRING CODE: ${code} ${custom ? '(custom)' : '(acak)'}`));
|
|
95
|
+
client.on('pairing-code', (code) => console.log('Enter this code on your phone:', code));
|
|
55
96
|
|
|
56
97
|
client.on('ready', (sock) => {
|
|
57
|
-
console.log('terhubung sebagai', sock.user?.id);
|
|
58
98
|
sock.ev.on('messages.upsert', async ({ messages, type }) => {
|
|
59
99
|
if (type !== 'notify') return;
|
|
60
100
|
for (const m of messages) {
|
|
61
101
|
if (m.key.fromMe) continue;
|
|
62
102
|
const item = extractMessage(m);
|
|
63
103
|
if (item?.type === 'text' && /^ping$/i.test(item.text)) {
|
|
64
|
-
await sendText(sock, m.key.remoteJid, '
|
|
104
|
+
await sendText(sock, m.key.remoteJid, 'pong', { quoted: m });
|
|
65
105
|
}
|
|
66
106
|
}
|
|
67
107
|
});
|
|
68
108
|
});
|
|
69
109
|
```
|
|
70
110
|
|
|
71
|
-
`client`
|
|
111
|
+
On the phone: **Settings → Linked devices → Link a device → Link with phone number instead**, then type the code. Leave `phone` empty to get a QR code instead (`client.on('qr', ...)`, or `printQR: true` to print it in the terminal).
|
|
112
|
+
|
|
113
|
+
A runnable example lives in [`example/ping-bot.js`](example/ping-bot.js).
|
|
114
|
+
|
|
115
|
+
## The high-level client: `createBibzWhats()`
|
|
116
|
+
|
|
117
|
+
`createBibzWhats(options)` returns a Promise of an `EventEmitter`-based client that owns the socket for you: it fetches the current WhatsApp Web version, resolves the device identity, requests the pairing code, reconnects with back-off, wipes broken sessions and re-pairs.
|
|
118
|
+
|
|
119
|
+
### Options
|
|
72
120
|
|
|
73
|
-
|
|
|
121
|
+
| Option | Type | Default | Description |
|
|
122
|
+
|---|---|---|---|
|
|
123
|
+
| `phone` | `string` | — | Bot number in digits with country code. Required for pairing codes; omit for QR. |
|
|
124
|
+
| `pairingCode` | `string` | random | Custom code, exactly 8 characters `A–Z0–9`. If the server refuses a custom code the client falls back to a random one once. |
|
|
125
|
+
| `authDir` | `string` | `'bibzwhats-session'` | Folder for credentials, signal keys and `identity.json`. |
|
|
126
|
+
| `identity` | `'auto' \| string \| [os, browser, version]` | `'auto'` | Linked-device identity. See [Linked-device identity](#linked-device-identity). |
|
|
127
|
+
| `browser` | `[os, browser, version]` | `null` | Legacy alias of `identity` (explicit tuple). |
|
|
128
|
+
| `maxIdentityRotations` | `number` | `4` | Automatic mode: how many alternative profiles to try before giving up. |
|
|
129
|
+
| `companionPlatformDisplay` | `string \| null` | `null` | Override the `companion_platform_display` sent at pairing. Only set this if you know the value is on WhatsApp's allow-list. |
|
|
130
|
+
| `logger` | `{ info, warn, error, debug, ok }` | console | Any object with those methods (all optional). |
|
|
131
|
+
| `printQR` | `boolean` | `false` | Print the QR as ASCII when a QR is shown (requires `qrcode-terminal`). |
|
|
132
|
+
| `banner` | `boolean` | `auto` | Print the rainbow *"WhatsBibz."* ASCII banner (Small Slant) to the terminal when the client starts. Default prints only in an interactive terminal; `true` forces it, `false` disables it. |
|
|
133
|
+
| `socketConfig` | `Partial<SocketConfig>` | `{}` | Raw `makeWASocket` options merged last — full escape hatch. |
|
|
134
|
+
| `fetchLatestVersion` | `boolean` | `true` | Fetch the current WhatsApp Web version on every connect. The built-in version is never downgraded. |
|
|
135
|
+
| `forceIPv4` | `boolean` | `true` | Force IPv4 for media uploads. |
|
|
136
|
+
| `readyOnEveryConnect` | `boolean` | `true` | Emit `ready` for every new socket (first connect and each reconnect). |
|
|
137
|
+
| `maxReconnectAttempts` | `number` | `10` | Consecutive failed reconnects before `give-up`. |
|
|
138
|
+
| `reconnectStepMs` / `reconnectMaxMs` | `number` | `10000` / `60000` | Linear back-off: `step × attempt`, capped at `max`. |
|
|
139
|
+
| `restartDelayMs` | `number` | `2000` | Delay after `515 restartRequired` (normal after pairing) and after an identity rotation. |
|
|
140
|
+
| `maxSessionWipes` | `number` | `3` | Session wipes (401/500/411/corrupt) before `give-up`. |
|
|
141
|
+
| `wipeReconnectDelayMs` | `number` | `10000` | Delay before reconnecting with fresh credentials. |
|
|
142
|
+
| `qrFallbackAfterMs` | `number` | `90000` | With `phone` set, show a QR if no pairing code was accepted within this time. |
|
|
143
|
+
| `pairingRequestDelayMs` | `number` | `20000` | If the server sends no QR at all, request a pairing code directly after this delay. |
|
|
144
|
+
| `groupMetadataTtlMs` | `number` | `300000` | Cache TTL for group metadata used when sending to groups. |
|
|
145
|
+
|
|
146
|
+
Defaults are exported as `BIBZWHATS_DEFAULTS`.
|
|
147
|
+
|
|
148
|
+
### Events
|
|
149
|
+
|
|
150
|
+
| Event | Payload | When |
|
|
74
151
|
|---|---|---|
|
|
75
|
-
| `pairing-code` | `(code, {custom, fallback})` |
|
|
76
|
-
| `qr` | `(qr)` |
|
|
77
|
-
| `ready` | `(sock)` |
|
|
78
|
-
| `first-ready` | `(sock)` |
|
|
79
|
-
| `open`
|
|
80
|
-
| `user` | `(digits)` |
|
|
81
|
-
| `
|
|
82
|
-
| `
|
|
83
|
-
| `
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
`
|
|
87
|
-
|
|
88
|
-
###
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
152
|
+
| `pairing-code` | `(code, { custom, fallback })` | The server accepted a pairing code request. `custom` = your code was used; `fallback` = server refused the custom code and a random one was issued. |
|
|
153
|
+
| `qr` | `(qr)` | A QR string is available (immediately without `phone`, or after `qrFallbackAfterMs`). |
|
|
154
|
+
| `ready` | `(sock)` | A **new socket** is ready. Attach your message handlers here — sockets are replaced on reconnect and old listeners die with them. |
|
|
155
|
+
| `first-ready` | `(sock)` | Like `ready`, but only once per client. |
|
|
156
|
+
| `open` | `(sock)` | Connection opened (every time). |
|
|
157
|
+
| `user` | `(digits)` | The bot's own number became known. |
|
|
158
|
+
| `close` | `({ status, error })` | Connection closed. `status` is the `DisconnectReason` code. |
|
|
159
|
+
| `reconnecting` | `({ delay, attempt, fresh, identity?, pairingPending? })` | A reconnect is scheduled. `fresh` = with new credentials; `identity` = the profile after a rotation; `pairingPending` = the previous pairing was still pending on the server (see lifecycle). |
|
|
160
|
+
| `identity-changed` | `({ browser, linkedDeviceName, pairingDisplay, pairingDisplayAccepted, profileId, reason })` | Automatic mode only: the server rejected the previous profile and the next one is in use. |
|
|
161
|
+
| `session-wiped` | `(reason)` | The session folder was deleted (`'401 loggedOut'`, `'500 badSession'`, `'411'`, `'sesi korup'`, `'401 pairing tertunda'`). |
|
|
162
|
+
| `give-up` | `(message)` | The client stopped trying. Human intervention is needed (wrong number, blocked IP, …). |
|
|
163
|
+
| `connection.update` | `(update)` | Raw Baileys connection update, forwarded. |
|
|
164
|
+
|
|
165
|
+
### Properties and methods
|
|
166
|
+
|
|
167
|
+
| Member | Description |
|
|
168
|
+
|---|---|
|
|
169
|
+
| `client.sock` | The current socket (`null` before the first connect; changes on reconnect). |
|
|
170
|
+
| `client.identity` | `{ mode: 'auto' \| 'custom', source, profileId, browser, linkedDeviceName, pairingDisplay, pairingDisplayAccepted, tried: [{ id, reason }] }` |
|
|
171
|
+
| `client.options` | Effective options after defaults. |
|
|
172
|
+
| `client.isConnected()` | `true` while the socket is open. |
|
|
173
|
+
| `client.close()` | Close the socket and cancel scheduled reconnects. Leaves no timers behind. |
|
|
174
|
+
| `client.logout()` | Log the device out on the server and delete the session folder. |
|
|
175
|
+
|
|
176
|
+
### Connection lifecycle
|
|
177
|
+
|
|
178
|
+
```
|
|
179
|
+
connect ──► QR / pairing code ──► 515 restartRequired ──► reconnect (2 s) ──► open ──► ready
|
|
180
|
+
│ │
|
|
181
|
+
│ 428 before QR / 405 (identity rejected) auto → next profile, 2 s │ network drop → 1 s, then back-off
|
|
182
|
+
│ 400 at pairing (identity rejected) auto → next profile, 2 s │ 401 registered → wipe, re-pair
|
|
183
|
+
│ 401 with pairing pending new credentials, 2 s │
|
|
184
|
+
└─ other close codes ── back-off 10 s × attempt (max 60 s, 10 attempts) ── give-up
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
Details worth knowing:
|
|
188
|
+
|
|
189
|
+
- **515 after pairing is normal.** The server asks the client to restart; the client reconnects after `restartDelayMs`.
|
|
190
|
+
- **401 with a pending pairing.** If the connection drops right after a pairing code was issued, the server answers the next connect with `401`. This is not a wrong number: the client replaces the credentials and requests a new code within 2 s. This path does not count towards `maxSessionWipes`.
|
|
191
|
+
- **Identity rotation happens only before the device is registered.** Once linked, the identity is locked — changing it would look like a different device to the phone.
|
|
192
|
+
- **Session wipes keep `identity.json`** when the identity was already accepted (`qr-received`, `pairing-accepted`, `open`), so the device name stays the same after re-pairing.
|
|
193
|
+
|
|
194
|
+
## Linked-device identity
|
|
195
|
+
|
|
196
|
+
The tuple `browser = [os, browser, version]` decides what the phone shows under **Linked devices**, and part of it is validated by WhatsApp at pairing time.
|
|
197
|
+
|
|
198
|
+
### Automatic mode (default)
|
|
199
|
+
|
|
200
|
+
```js
|
|
201
|
+
const client = await createBibzWhats({ phone }); // identity: 'auto'
|
|
202
|
+
client.on('identity-changed', (i) => console.log('switched to', i.linkedDeviceName, 'because', i.reason));
|
|
203
|
+
console.log(client.identity);
|
|
204
|
+
// { mode: 'auto', profileId: 'macos-chrome', linkedDeviceName: 'Chrome (Mac OS)',
|
|
205
|
+
// pairingDisplay: 'Chrome (Mac OS)', pairingDisplayAccepted: true, tried: [] }
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
The client starts with the profile that has the best track record and switches only on a rejection signal (`428` before QR, `400 bad-request` at pairing, `405`):
|
|
209
|
+
|
|
210
|
+
| Order | Profile | Shown on the phone | Sent at pairing |
|
|
211
|
+
|---|---|---|---|
|
|
212
|
+
| 1 | `macos-chrome` | Chrome (Mac OS) | Chrome (Mac OS) |
|
|
213
|
+
| 2 | `macos-safari` | Safari (Mac OS) | Safari (Mac OS) |
|
|
214
|
+
| 3 | `windows-chrome` | Chrome (Windows) | Chrome (Windows) |
|
|
215
|
+
| 4 | `linux-chrome` | Chrome (Linux) | Chrome (Linux) |
|
|
216
|
+
| 5 | `ubuntu-chrome` | Chrome (Ubuntu) | Chrome (Ubuntu) |
|
|
217
|
+
| 6 | `macos-firefox` | Firefox (Mac OS) | Firefox (Mac OS) |
|
|
218
|
+
| 7 | `windows-edge` | Edge (Windows) | Edge (Windows) |
|
|
219
|
+
| 8 | `archlinux-chrome` | Chrome (Arch Linux) | Chrome (Linux) |
|
|
220
|
+
|
|
221
|
+
The profile that works is written to `<authDir>/identity.json` and reused on the next start. Delete that file (or the whole `authDir`) to start over.
|
|
222
|
+
|
|
223
|
+
### Custom mode
|
|
224
|
+
|
|
225
|
+
A custom identity is used **as is** and never replaced silently. If the server rejects it you get a clear error in the log, not a different device name.
|
|
106
226
|
|
|
107
227
|
```js
|
|
108
|
-
|
|
228
|
+
createBibzWhats({ phone, identity: Browsers.archLinux('Chrome') }); // preset tuple
|
|
229
|
+
createBibzWhats({ phone, identity: ['Arch Linux', 'Firefox', '6.16.4'] }); // free tuple
|
|
230
|
+
createBibzWhats({ phone, identity: 'archLinux:Firefox' }); // "preset:Browser"
|
|
231
|
+
createBibzWhats({ phone, identity: 'Mac OS/Safari/15.6.1' }); // "OS/Browser/Version"
|
|
232
|
+
createBibzWhats({ phone, identity: 'linux-chrome' }); // profile id
|
|
233
|
+
createBibzWhats({ phone, browser: ['Mac OS', 'Chrome', '14.4.1'] }); // legacy alias
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
Environment variables work without code changes — priority is option → env → auto:
|
|
237
|
+
|
|
238
|
+
```bash
|
|
239
|
+
BIBZ_BROWSER="archLinux:Chrome" # or "Arch Linux/Firefox/6.16", or "auto"
|
|
240
|
+
# or the trio
|
|
241
|
+
BIBZ_DEVICE_OS="Arch Linux" BIBZ_DEVICE_BROWSER="Chrome" BIBZ_DEVICE_VERSION="6.16"
|
|
109
242
|
```
|
|
110
243
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
`Browsers`, `
|
|
114
|
-
|
|
244
|
+
An empty version is filled per OS (Mac OS `15.6.1`, Windows `10.0.22631`, Linux `6.12.44`, other `1.0.0`).
|
|
245
|
+
|
|
246
|
+
Presets on `Browsers`: `macOS`, `windows`, `ubuntu`, `linux`, `archLinux`, `android` (experimental), `appropriate` (follows the host OS, distro-aware via `/etc/os-release`), `whatsbibz`.
|
|
247
|
+
|
|
248
|
+
### What WhatsApp accepts
|
|
115
249
|
|
|
116
|
-
|
|
117
|
-
|
|
250
|
+
The identity is used in three places with different rules. Measured against production servers on 2026-09-03:
|
|
251
|
+
|
|
252
|
+
| Where | Used for | Server rule | WhatsBibz behaviour |
|
|
253
|
+
|---|---|---|---|
|
|
254
|
+
| `DeviceProps.os` (`browser[0]`) | Text under *Linked devices* | Free text | Sent unchanged — "Arch Linux" shows as "Arch Linux" |
|
|
255
|
+
| `companion_platform_display` (pairing code) | Validation of the pairing request | **Allow-list**; unknown values → `400`, the phone never gets a notification | Derived automatically to a valid value (`derivePairingDisplay`): Arch Linux → `Chrome (Linux)` |
|
|
256
|
+
| `WebInfo.webSubPlatform` (`Desktop` + `syncFullHistory`) | Desktop client type | `DARWIN` / `APP_STORE` are closed (`428` before QR); `WIN_HYBRID` / `WEB_BROWSER` work | Mac OS Desktop → `WEB_BROWSER`, full history still requested |
|
|
257
|
+
|
|
258
|
+
| Accepted | Rejected (`400`) |
|
|
259
|
+
|---|---|
|
|
260
|
+
| OS: Mac OS, macOS, Windows, Linux, Ubuntu, Debian, Fedora, CentOS, Gentoo, Manjaro, Chromium OS, Android, iOS | OS: Arch Linux, Arch, GNU/Linux, Mac OS X, Linux Mint, openSUSE, Kali, NixOS, Pop!_OS, FreeBSD, Chrome OS, any product name |
|
|
261
|
+
| Browser: Chrome, Chromium, Firefox, Safari, Edge, Opera, Brave, Vivaldi, Arc | Browser: Desktop, any product name |
|
|
118
262
|
|
|
119
|
-
|
|
263
|
+
Helpers: `derivePairingDisplay(browser)`, `isPairingDisplayAccepted(str)`, `lintIdentity({ browser, companionPlatformDisplay })`, `resolveDeviceIdentity(opts)`, `parseBrowserSpec(spec)`, `identityFromEnv(env)`, `describeIdentity(browser)`, `IDENTITY_PROFILES`.
|
|
264
|
+
|
|
265
|
+
## Sending and reading messages
|
|
120
266
|
|
|
121
267
|
```js
|
|
122
268
|
import {
|
|
123
269
|
sendText, sendMedia, react, presence, sendWithRetry, splitText, whatsappify,
|
|
124
270
|
extractMessage, unwrapMessage, messageTimestampMs,
|
|
125
271
|
LidMap, digitsOf, pnJid, lidJid, isGroupJid, isLidJid, sameUser, normalizeJid,
|
|
126
|
-
createPairingController, normalizePairingCode, wipeAuthDir,
|
|
127
272
|
} from '@xbibzlibrary/whatsbibz';
|
|
128
273
|
```
|
|
129
274
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
275
|
+
| Helper | What it does |
|
|
276
|
+
|---|---|
|
|
277
|
+
| `sendText(sock, jid, text, { quoted, format, maxLen })` | Splits long text (≤ 4000 chars per message), retries 3×, converts Markdown to WhatsApp formatting when `format: true`. Returns `{ ok, ids, error? }`. |
|
|
278
|
+
| `sendMedia(sock, jid, content, { quoted, fallbackToDocument })` | Sends image/video/audio/document; if the media is refused it is re-sent as a document. |
|
|
279
|
+
| `react(sock, jid, key, emoji)` | Reacts to a message. |
|
|
280
|
+
| `presence(sock, jid, state)` | `composing`, `recording`, `paused`, `available`, `unavailable`. |
|
|
281
|
+
| `sendWithRetry(sock, jid, content, options, { attempts })` | Generic `sendMessage` with retry. |
|
|
282
|
+
| `extractMessage(m)` | Normalises any incoming message to `{ type, text, participant, mentions, quoted, imageMsg \| videoMsg \| … , buttonId, pollName, … }`. Unwraps ephemeral, view-once and edited wrappers. |
|
|
283
|
+
| `LidMap` | Maps LID ↔ phone JIDs: `learnFromMessage(m)`, `canonical(jid)`, `variants(jid)`, `toJSON()` / `fromJSON()`. |
|
|
134
284
|
|
|
135
|
-
|
|
285
|
+
The raw `sock.sendMessage(jid, content)` supports everything Baileys does: text, image, video, audio, document, sticker, sticker packs, location, contacts, polls, reactions, edits, deletes, pins, forwards, view-once, interactive buttons, product and event messages, newsletters. Builders `Button`, `Carousel` and `AIRich` are exported.
|
|
136
286
|
|
|
137
|
-
|
|
138
|
-
`pair-device-rotate-qr` yang menyita material registrasi perangkat yang belum
|
|
139
|
-
paired. BibzWhats menanganinya di `lib/Socket/socket.js`: rotate adv secret
|
|
140
|
-
(32 byte CSPRNG) → `creds.update` → render ulang QR **ref yang sama** (pool ref
|
|
141
|
-
tidak habis). Bila `creds.me` sudah ada (pairing code sudah diminta / pair
|
|
142
|
-
sukses) adv secret dipertahankan agar verifikasi tidak rusak.
|
|
287
|
+
## Text formatting and chat markup
|
|
143
288
|
|
|
144
|
-
|
|
145
|
-
(`import ... from '@xbibzlibrary/whatsbibz'`).
|
|
289
|
+
### 1) Native WhatsApp formatting (raw, no conversion)
|
|
146
290
|
|
|
147
|
-
|
|
291
|
+
`sendText(..., { format: false })` and `sock.sendMessage(jid, { text })` send text **as-is** — WhatsApp renders the markup itself on the recipient's phone:
|
|
148
292
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
293
|
+
| You type | Shows in WhatsApp |
|
|
294
|
+
|---|---|
|
|
295
|
+
| `*text*` | **bold** |
|
|
296
|
+
| `_text_` | _italic_ |
|
|
297
|
+
| `~text~` | ~~strikethrough~~ |
|
|
298
|
+
| `` `text` `` | `monospace` |
|
|
299
|
+
| `*_combined_*` / `_*combined*_` | **_bold italic_** |
|
|
300
|
+
| `` ```line\ntext``` `` | monospace code block |
|
|
301
|
+
|
|
302
|
+
```js
|
|
303
|
+
await sock.sendMessage(jid, { text: '*Hello* _world_ ~everyone~ `code`' });
|
|
304
|
+
// sendText with format:false also passes it through untouched
|
|
305
|
+
await sendText(sock, jid, '*bold* _italic_ ~strike~ `mono`', { format: false });
|
|
156
306
|
```
|
|
157
307
|
|
|
158
|
-
|
|
308
|
+
### 2) Automatic Markdown → WhatsApp conversion
|
|
159
309
|
|
|
160
|
-
|
|
161
|
-
Sejak 1.3.0 ada dua mode:
|
|
310
|
+
`sendText(..., { format: true })` (default) runs `whatsappify()` so Markdown coming from an LLM looks clean as WhatsApp formatting:
|
|
162
311
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
312
|
+
| Markdown source | WhatsApp result |
|
|
313
|
+
|---|---|
|
|
314
|
+
| `**bold**` or `__bold__` | `*bold*` |
|
|
315
|
+
| `***bold-italic***` | `*bold-italic*` |
|
|
316
|
+
| `~~strike~~` | `~strike~` |
|
|
317
|
+
| `_italic_` | `_italic_` (kept — already WhatsApp syntax) |
|
|
318
|
+
| `# / ## / ### …` at line start | removed |
|
|
319
|
+
| `[label](https://url)` | `label (https://url)` |
|
|
320
|
+
| stray `***`/`___` | collapsed to `*`/`_` |
|
|
168
321
|
|
|
169
322
|
```js
|
|
170
|
-
const
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
323
|
+
const llmAnswer = '## Summary\n\n**Weather** today is *clear*, ~~rain~~ has passed.\nSee [forecast](https://bmkg.go.id).';
|
|
324
|
+
await sendText(sock, jid, llmAnswer); // format: true by default
|
|
325
|
+
// renders as: "Summary" (no #) + *Weather* + _clear_ + ~rain~ + "forecast (https://bmkg.go.id)"
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
`splitText(text, maxLen)` splits text over 4000 chars (breaks at newlines/spaces, 250 ms pause between parts) — `sendText` does this automatically.
|
|
329
|
+
|
|
330
|
+
### 3) Mentions & mention-all
|
|
331
|
+
|
|
332
|
+
```js
|
|
333
|
+
import { pnJid } from '@xbibzlibrary/whatsbibz';
|
|
334
|
+
|
|
335
|
+
await sock.sendMessage(jid, {
|
|
336
|
+
text: 'Hi @6281234567890, check this!',
|
|
337
|
+
mentions: [pnJid('6281234567890')], // array of JIDs
|
|
338
|
+
});
|
|
339
|
+
await sock.sendMessage(groupJid, {
|
|
340
|
+
text: 'Attention all members',
|
|
341
|
+
mentions: [], // or [jidA, jidB, ...]
|
|
342
|
+
mentionAll: true, // highlights the whole chat (broadcast mention)
|
|
343
|
+
});
|
|
175
344
|
```
|
|
176
345
|
|
|
177
|
-
|
|
178
|
-
|
|
346
|
+
## Interactive buttons
|
|
347
|
+
|
|
348
|
+
All modern WhatsApp buttons use the **native flow** format: a button is the markup `{ name, buttonParamsJson }`. WhatsBibz forwards that through `sock.sendMessage(jid, { interactiveMessage: {...} })` or the `ButtonV2` builder.
|
|
349
|
+
|
|
350
|
+
### Way 1 — Native flow (recommended) via `sendMessage`
|
|
179
351
|
|
|
180
352
|
```js
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
353
|
+
await sock.sendMessage(jid, {
|
|
354
|
+
interactiveMessage: {
|
|
355
|
+
title: 'Please pick a menu option below:', // → message body text
|
|
356
|
+
footer: 'WhatsBibz Bot', // small footer text (optional)
|
|
357
|
+
header: 'FOOD MENU', // big header title (optional)
|
|
358
|
+
buttons: [
|
|
359
|
+
{ name: 'quick_reply', buttonParamsJson: JSON.stringify({ display_text: 'Food', id: 'food' }) },
|
|
360
|
+
{ name: 'quick_reply', buttonParamsJson: JSON.stringify({ display_text: 'Drinks', id: 'drink' }) },
|
|
361
|
+
{ name: 'quick_reply', buttonParamsJson: JSON.stringify({ display_text: 'Cancel', id: 'cancel' }) },
|
|
362
|
+
],
|
|
363
|
+
},
|
|
364
|
+
});
|
|
187
365
|
```
|
|
188
366
|
|
|
189
|
-
|
|
190
|
-
atau trio `BIBZ_DEVICE_OS="Arch Linux" BIBZ_DEVICE_BROWSER="Chrome" BIBZ_DEVICE_VERSION="6.16"`. `BIBZ_BROWSER=auto`
|
|
191
|
-
memaksa mode otomatis. Prioritas: opsi di kode → env → otomatis.
|
|
367
|
+
**Native-flow button markup** (`name` + contents of `buttonParamsJson`):
|
|
192
368
|
|
|
193
|
-
|
|
|
369
|
+
| `name` | Shows as | `buttonParamsJson` example |
|
|
194
370
|
|---|---|---|
|
|
195
|
-
| `
|
|
196
|
-
| `
|
|
197
|
-
| `
|
|
198
|
-
| `
|
|
371
|
+
| `quick_reply` | plain reply button | `{"display_text":"Yes","id":"yes"}` |
|
|
372
|
+
| `single_select` | list dropdown | `{"title":"Pick City","sections":[{...}]}` (see Way 3) |
|
|
373
|
+
| `cta_url` | open-link button | `{"display_text":"Visit","url":"https://…","webview_interaction":false}` |
|
|
374
|
+
| `cta_call` | call button | `{"display_text":"Call","id":"+628…"}` |
|
|
375
|
+
| `cta_copy` | copy button | `{"display_text":"Copy code","copy_code":"ABC123"}` |
|
|
376
|
+
| `cta_reminder` / `cta_cancel_reminder` | set / cancel reminder | `{"display_text":"Remind me","id":"…"}` |
|
|
377
|
+
| `address_message` | request address | `{"display_text":"Send address","id":"…"}` |
|
|
378
|
+
| `send_location` | request location | `{"display_text":"…"}` |
|
|
379
|
+
| `limited_time_offer` | limited-time offer | `{"text":"…","url":"…","copy_code":"…","expiration_time":0}` |
|
|
199
380
|
|
|
200
|
-
|
|
201
|
-
`parseBrowserSpec`, `identityFromEnv`, `IDENTITY_PROFILES`, `describeIdentity`, `createIdentityRotator`.
|
|
381
|
+
Known keys: `display_text`, `id`, `url`, `copy_code`, `webview_interaction`, `expiration_time`, `sections`, `title`, `rows`. Comfortable limits: up to 3 `quick_reply`, or 1 `single_select` per message.
|
|
202
382
|
|
|
203
|
-
|
|
383
|
+
### Way 2 — `ButtonV2` builder (legacy `buttonsMessage`)
|
|
204
384
|
|
|
205
|
-
|
|
206
|
-
|
|
385
|
+
```js
|
|
386
|
+
import { ButtonV2 } from '@xbibzlibrary/whatsbibz';
|
|
207
387
|
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
388
|
+
const btn = new ButtonV2(sock)
|
|
389
|
+
.text('Pick your answer') // .title()/.text()/.footer()/.image(url)
|
|
390
|
+
.footer('WhatsBibz Quiz')
|
|
391
|
+
.addButton('Very satisfied', 'a')
|
|
392
|
+
.addButton('Okay', 'b')
|
|
393
|
+
.addButton('Not great', 'c');
|
|
394
|
+
|
|
395
|
+
const msg = await btn.send(jid); // automatically injects the required <biz> node
|
|
396
|
+
// msg.key.id → id of the sent message
|
|
397
|
+
```
|
|
398
|
+
|
|
399
|
+
### Way 3 — Single-select dropdown list
|
|
213
400
|
|
|
214
401
|
```js
|
|
215
|
-
|
|
402
|
+
await sock.sendMessage(jid, {
|
|
403
|
+
interactiveMessage: {
|
|
404
|
+
title: 'Pick a destination city:',
|
|
405
|
+
buttons: [{
|
|
406
|
+
name: 'single_select',
|
|
407
|
+
buttonParamsJson: JSON.stringify({
|
|
408
|
+
title: 'Cities',
|
|
409
|
+
sections: [{
|
|
410
|
+
title: 'Java Island',
|
|
411
|
+
rows: [
|
|
412
|
+
{ title: 'Jakarta', description: 'DKI Jakarta', id: 'jkt' },
|
|
413
|
+
{ title: 'Bandung', description: 'West Java', id: 'bdg' },
|
|
414
|
+
{ title: 'Surabaya', description: 'East Java', id: 'sby' },
|
|
415
|
+
],
|
|
416
|
+
}],
|
|
417
|
+
}),
|
|
418
|
+
}],
|
|
419
|
+
},
|
|
420
|
+
});
|
|
421
|
+
```
|
|
216
422
|
|
|
217
|
-
|
|
218
|
-
await createBibzWhats({ phone, browser: Browsers.macOS('Chrome') }); // default library
|
|
423
|
+
### Reading button taps (inbound)
|
|
219
424
|
|
|
220
|
-
|
|
221
|
-
await createBibzWhats({ phone, browser: Browsers.archLinux('Chrome') });
|
|
425
|
+
When a user taps a button (native flow or legacy), the bot gets a `messages.upsert`; `extractMessage` normalises it to `type: 'button'`:
|
|
222
426
|
|
|
223
|
-
|
|
224
|
-
|
|
427
|
+
```js
|
|
428
|
+
sock.ev.on('messages.upsert', async ({ messages, type }) => {
|
|
429
|
+
if (type !== 'notify') return;
|
|
430
|
+
for (const m of messages) {
|
|
431
|
+
if (m.key.fromMe || !m.message) continue;
|
|
432
|
+
const item = extractMessage(m); // auto-unwrapped
|
|
433
|
+
if (item?.type !== 'button') continue;
|
|
434
|
+
|
|
435
|
+
console.log('Button pressed:', item.buttonId, '|', item.buttonText);
|
|
436
|
+
switch (item.buttonId) {
|
|
437
|
+
case 'food': return sendText(sock, m.key.remoteJid, 'You picked *Food*', { quoted: m });
|
|
438
|
+
case 'drink': return sendText(sock, m.key.remoteJid, 'You picked *Drinks*', { quoted: m });
|
|
439
|
+
case 'jkt': return sendText(sock, m.key.remoteJid, 'Destination: *Jakarta*', { quoted: m });
|
|
440
|
+
case 'cancel': return sendText(sock, m.key.remoteJid, 'Cancelled');
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
});
|
|
444
|
+
```
|
|
445
|
+
|
|
446
|
+
Raw **quick_reply** taps can also be read directly: `m.message.interactiveResponseMessage.nativeFlowResponseMessage` carries `name` (e.g. `quick_reply`) and `paramsJson` — a JSON string like `{"id":"food","text":"Food"}`. Replies from `listResponseMessage`, `buttonsResponseMessage` and `templateButtonReplyMessage` (legacy) are covered by `extractMessage` too.
|
|
447
|
+
|
|
448
|
+
## Rich and AI-styled messages
|
|
449
|
+
|
|
450
|
+
WhatsBibz ships a **rich message** builder (the same format as Meta AI replies on WhatsApp: `botForwardedMessage` / `richResponseMessage`) so a bot can show tables, syntax-highlighted code blocks, formatted link lists and follow-up suggestions in one formatted message.
|
|
225
451
|
|
|
226
|
-
|
|
452
|
+
### Built-in socket methods
|
|
453
|
+
|
|
454
|
+
```js
|
|
455
|
+
// Table
|
|
456
|
+
await sock.sendTable(jid, 'Price List', ['Item', 'Price'], [['Fried rice', 'Rp15k'], ['Iced tea', 'Rp5k']], undefined, { footer: 'valid today' });
|
|
457
|
+
|
|
458
|
+
// Syntax-highlighted code block
|
|
459
|
+
await sock.sendCodeBlock(jid, 'const x = 1;\nconsole.log(x);', undefined, { language: 'javascript', title: 'JS sample' });
|
|
460
|
+
|
|
461
|
+
// Formatted link list — links: a URL string or { url, displayName }
|
|
462
|
+
await sock.sendLink(jid, 'Trusted sources:', [
|
|
463
|
+
{ displayName: 'BMKG — Weather & quakes', url: 'https://www.bmkg.go.id' },
|
|
464
|
+
'https://www.bmkg.go.id/cuaca',
|
|
465
|
+
], undefined);
|
|
466
|
+
|
|
467
|
+
// Full rich message from raw submessages
|
|
468
|
+
await sock.sendRichMessage(jid, [
|
|
469
|
+
{ messageType: 2, messageText: 'Hello from a rich message' },
|
|
470
|
+
{ messageType: 4, tableMetadata: { title: 'Score', rows: [{ items: ['A', '1'], isHeading: true }] } },
|
|
471
|
+
], undefined);
|
|
227
472
|
```
|
|
228
473
|
|
|
229
|
-
|
|
474
|
+
Other methods: `sendTableV2`, `sendList`, `sendCodeBlockV2`, `sendLinkV2`, `sendLatex`, `sendLatexImage`, `sendUnifiedResponse`, `captureUnifiedResponse` (see `Socket/messages-send.js`).
|
|
230
475
|
|
|
231
|
-
|
|
232
|
-
|---|---|
|
|
233
|
-
| OS: `Mac OS`, `macOS`, `Windows`, `Linux`, `Ubuntu`, `Debian`, `Fedora`, `CentOS`, `Gentoo`, `Manjaro`, `Chromium OS`, `Android`, `iOS` | OS: `Arch Linux`, `Arch`, `ArchLinux`, `GNU/Linux`, `Mac OS X`, `Linux Mint`, `openSUSE`, `Kali`, `NixOS`, `Pop!_OS`, `FreeBSD`, `Chrome OS`, nama produk apa pun |
|
|
234
|
-
| Browser: `Chrome`, `Chromium`, `Firefox`, `Safari`, `Edge`, `Opera`, `Brave`, `Vivaldi`, `Arc` | Browser: `Desktop`, nama produk |
|
|
476
|
+
### `AIRich` fluent builder
|
|
235
477
|
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
478
|
+
```js
|
|
479
|
+
import { AIRich } from '@xbibzlibrary/whatsbibz';
|
|
480
|
+
|
|
481
|
+
const rich = new AIRich(sock)
|
|
482
|
+
.addText('Here is today\'s weather summary for **Jakarta**:')
|
|
483
|
+
.addTable([
|
|
484
|
+
['Time', 'Weather', 'Temp'],
|
|
485
|
+
['Morning','Clear', '26°C'],
|
|
486
|
+
['Noon', 'Cloudy', '32°C'],
|
|
487
|
+
['Evening','Rain', '28°C'],
|
|
488
|
+
])
|
|
489
|
+
.addCode('javascript', `async function weather(city) {\n return await fetch('https://api/weather?q=' + city);\n}`)
|
|
490
|
+
.addSuggest(['What is the weather tomorrow?', 'Weekly forecast']);
|
|
491
|
+
|
|
492
|
+
await rich.send(jid, { forwarded: true, includesUnifiedResponse: true });
|
|
493
|
+
```
|
|
239
494
|
|
|
240
|
-
|
|
495
|
+
Chainable `AIRich` methods: `addText(text)`, `addTable(array2dOfStrings)`, `addCode(lang, code)`, `addSource([[icon, url, text], ...])`, `addImage(url/buffer | array)`, `addVideo(...)`, `addProduct({...}|[...])`, `addPost(...)`, `addReels(...)`, `addTip(text)`, `addSuggest(string | string[])`, `addSubmessage(...)`, `addSection(...)`. `send(jid, { forwarded, notification, includesUnifiedResponse, includesSubmessages })` — options default to `true` except `notification` (`false`). `ORich` is an alias of `AIRich`; statics: `AIRich.tokenizer(code, lang)`, `AIRich.toTableMetadata(arr)`, `AIRich.newLayout(...)`.
|
|
241
496
|
|
|
242
|
-
|
|
497
|
+
### Ethical usage note
|
|
243
498
|
|
|
244
|
-
|
|
499
|
+
These rich/AI messages render on the recipient's phone **as if they came from Meta's official AI assistant** (they reuse `forwardedAiBotMessageInfo`, `GenAI*` view models, etc.). Use them only for bots that transparently mimic an AI assistant — never for impersonation, deception or tricking users. Likewise, this is an **unofficial** WhatsApp client; run it on a spare number and accept the risk of WhatsApp restricting the account.
|
|
245
500
|
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
Cek sendiri kapan saja: `node -e "import('@xbibzlibrary/whatsbibz').then(m=>m.fetchLatestWaWebVersion().then(console.log))"`.
|
|
501
|
+
## Low-level API (Baileys-compatible)
|
|
502
|
+
|
|
503
|
+
```js
|
|
504
|
+
import makeWASocket, {
|
|
505
|
+
useMultiFileAuthState, makeCacheableSignalKeyStore, fetchLatestWaWebVersion,
|
|
506
|
+
DisconnectReason, Browsers, downloadMediaMessage, proto, jidNormalizedUser,
|
|
507
|
+
} from '@xbibzlibrary/whatsbibz';
|
|
508
|
+
|
|
509
|
+
const { state, saveCreds } = await useMultiFileAuthState('session');
|
|
510
|
+
const { version } = await fetchLatestWaWebVersion();
|
|
511
|
+
const sock = makeWASocket({ version, auth: state, browser: Browsers.macOS('Chrome') });
|
|
512
|
+
sock.ev.on('creds.update', saveCreds);
|
|
513
|
+
```
|
|
514
|
+
|
|
515
|
+
Everything from Baileys v7 / ourin-baileys is exported under the same names. Renames: `Dugong` → `BibzWhatsEngine`, `sock.ourin` → `sock.bibz` (`sock.ourin` kept as an alias). Extra socket options: `companionPlatformDisplay`, `companionPlatformId`, `webSubPlatform`.
|
|
516
|
+
|
|
517
|
+
Additions on top of upstream `master` (see [CHANGELOG.md](CHANGELOG.md) for sources): `WIN32 → WIN_HYBRID` for Windows Desktop, `requestPairingCode` via `query()` with `creds.me` set only after the ACK, tctoken handling, self-only `protocolMessage` guards, `groupOnlineCount`, no `<presence/>` on partial `creds.update`, `directPath`-first media downloads, Lottie sticker unwrapping, `ev.destroy()` / `signalRepository.close()` on socket end, `whatsapp-rust-bridge` 0.5.5.
|
|
264
518
|
|
|
265
519
|
## TypeScript
|
|
266
520
|
|
|
267
|
-
|
|
268
|
-
`tsc --strict`, `moduleResolution: NodeNext`, dan `skipLibCheck: false` — tidak perlu `@types` tambahan selain `@types/node`.
|
|
521
|
+
Type declarations ship with the package, including a complete `WAProto/index.d.ts`. Verified with `tsc --strict`, `moduleResolution: NodeNext` and `skipLibCheck: false`; the only extra you need is `@types/node`.
|
|
269
522
|
|
|
270
523
|
```ts
|
|
271
524
|
import { createBibzWhats, type BibzWhatsClient, type ResolvedIdentity } from '@xbibzlibrary/whatsbibz';
|
|
525
|
+
|
|
272
526
|
const client: BibzWhatsClient = await createBibzWhats({ phone: '628…', identity: 'archLinux:Chrome' });
|
|
273
527
|
client.on('identity-changed', (i) => console.log(i.linkedDeviceName));
|
|
274
528
|
```
|
|
275
529
|
|
|
276
|
-
##
|
|
530
|
+
## Migrating from Baileys / ourin-baileys
|
|
531
|
+
|
|
532
|
+
1. `npm uninstall baileys ourin-baileys && npm install @xbibzlibrary/whatsbibz`
|
|
533
|
+
2. Replace the import specifier. Every export keeps its name.
|
|
534
|
+
3. Remove any `postinstall` patch scripts for `companion_reg_refresh` — the handler is built in.
|
|
535
|
+
4. Optional: replace your own connect/reconnect/pairing loop with `createBibzWhats()`.
|
|
536
|
+
|
|
537
|
+
## Troubleshooting
|
|
538
|
+
|
|
539
|
+
| Symptom | Cause | Fix |
|
|
540
|
+
|---|---|---|
|
|
541
|
+
| Pairing code printed, phone shows nothing | Old libraries print the code before the server confirms it. WhatsBibz only emits `pairing-code` after the ACK — if you still see nothing, the number is wrong or the IP is rate-limited. | Check the digits (country code, no `+`, no leading `0`). Wait 15 minutes if you retried many times (`429`). |
|
|
542
|
+
| `428` immediately, no QR | The advertised identity is refused (e.g. `DARWIN` sub-platform). | Leave `identity` unset (auto rotates), or pick a profile from the table above. |
|
|
543
|
+
| `400 bad-request` at pairing | `companion_platform_display` not on the allow-list. | Don't override `companionPlatformDisplay`; the derived value is always valid. |
|
|
544
|
+
| `401` right after a reconnect while pairing | Previous pairing still pending on the server. | Handled automatically (`reconnecting` with `pairingPending: true`). |
|
|
545
|
+
| `408` loop | WhatsApp Web version too old. | Keep `fetchLatestVersion: true` (default) or update the package. |
|
|
546
|
+
| `give-up` after 3 session wipes | Not a session problem: unregistered number, wrong digits or blocked IP. | Verify the number is active on WhatsApp; try another network. |
|
|
547
|
+
| Handlers stop working after a reconnect | Handlers were attached to the old socket. | Attach them inside `client.on('ready', sock => …)`. |
|
|
548
|
+
| Process won't exit | Something else keeps the loop alive — the client leaves no timers after `close()`. | Check your own intervals; `client.close()` before `process.exit`. |
|
|
549
|
+
|
|
550
|
+
## Testing
|
|
551
|
+
|
|
552
|
+
```bash
|
|
553
|
+
npm test # 55 offline tests (protocol, identity, pairing controller, lifecycle) — no network
|
|
554
|
+
npm run check # export surface sanity check
|
|
555
|
+
npm run test:live # 11 tests against real WhatsApp servers (no phone needed; uses fictional Ofcom numbers)
|
|
556
|
+
```
|
|
557
|
+
|
|
558
|
+
The live suite covers QR, pairing acknowledgement, automatic rotation on `428` and `400`, custom identities, negative controls and a forced network drop. Full results: [VERIFIKASI-IDENTITAS-2026-09-03.md](VERIFIKASI-IDENTITAS-2026-09-03.md) (Indonesian).
|
|
559
|
+
|
|
560
|
+
## Project structure
|
|
561
|
+
|
|
562
|
+
```
|
|
563
|
+
lib/
|
|
564
|
+
BibzWhats/ client.js (createBibzWhats), device-identity.js, pairing.js, send.js, extract.js, jid.js
|
|
565
|
+
Socket/ socket.js, engine.js, messages-send.js, messages-recv.js, groups.js, newsletter.js, ...
|
|
566
|
+
Utils/ platform-identity.js, browser-utils.js, messages-media.js, use-multi-file-auth-state.js, ...
|
|
567
|
+
Defaults/ Signal/ Types/ WABinary/ WAM/ WAUSync/ VoIP/ Modded/
|
|
568
|
+
WAProto/ protobuf definitions (index.js + complete index.d.ts)
|
|
569
|
+
assets/logo/ logo, icons, favicon
|
|
570
|
+
example/ ping-bot.js
|
|
571
|
+
test/ node:test suites (offline + live)
|
|
572
|
+
```
|
|
573
|
+
|
|
574
|
+
## Versioning and compatibility
|
|
575
|
+
|
|
576
|
+
- Semantic versioning. Breaking changes only in a major release.
|
|
577
|
+
- Built-in WhatsApp Web version is refreshed with every release and fetched live on every connect.
|
|
578
|
+
- Supported Node.js: current LTS lines and current release (20 / 22 / 24 / 26 at the time of writing).
|
|
579
|
+
- Changes are listed in [CHANGELOG.md](CHANGELOG.md).
|
|
580
|
+
|
|
581
|
+
## Contributing
|
|
582
|
+
|
|
583
|
+
Bug reports, measurements against the WhatsApp servers and pull requests are welcome. Please read [CONTRIBUTING.md](CONTRIBUTING.md) first — it explains the test requirements and how to run the live suite safely. Everyone participating is expected to follow the [Code of Conduct](CODE_OF_CONDUCT.md).
|
|
584
|
+
|
|
585
|
+
## Security
|
|
586
|
+
|
|
587
|
+
Do not open public issues for vulnerabilities. See [SECURITY.md](SECURITY.md).
|
|
588
|
+
|
|
589
|
+
## License
|
|
590
|
+
|
|
591
|
+
MIT — © 2026 Xbibz Developer. Based on Baileys, MIT © Rajeh Taher / WhiskeySockets — see [LICENSE](LICENSE) and [LICENSE.upstream](LICENSE.upstream).
|
|
592
|
+
|
|
593
|
+
WhatsBibz is not affiliated with, endorsed by, or supported by WhatsApp or Meta. Use a dedicated number, respect WhatsApp's Terms of Service and do not send unsolicited messages.
|
|
594
|
+
|
|
595
|
+
## Support & community
|
|
596
|
+
|
|
597
|
+
<p align="center">
|
|
598
|
+
<a href="https://ko-fi.com/xbibzofficial"><img src="https://img.shields.io/badge/Ko--Fi-Donate-FF5E5B?style=for-the-badge&logo=ko-fi&logoColor=white" alt="Ko-Fi"></a>
|
|
599
|
+
<a href="https://saweria.co/XbibzOfficial"><img src="https://img.shields.io/badge/Saweria-Support-FFAA00?style=for-the-badge" alt="Saweria"></a>
|
|
600
|
+
<a href="https://tiktok.com/@xbibzofficial"><img src="https://img.shields.io/badge/TikTok-@xbibzofficial-000000?style=for-the-badge&logo=tiktok&logoColor=white" alt="TikTok"></a>
|
|
601
|
+
<a href="https://t.me/xbibzofc"><img src="https://img.shields.io/badge/Telegram-@xbibzofc-26A5E4?style=for-the-badge&logo=telegram&logoColor=white" alt="Telegram"></a>
|
|
602
|
+
</p>
|
|
277
603
|
|
|
278
|
-
-
|
|
279
|
-
- WhatsApp Web API tidak resmi — gunakan nomor khusus, hindari spam (risiko banned).
|
|
280
|
-
- Lisensi MIT (WhatsBibz/BibzWhats) di atas MIT Baileys — lihat `LICENSE` dan `LICENSE.upstream`.
|
|
604
|
+
[Ko-Fi](https://ko-fi.com/xbibzofficial) · [Saweria](https://saweria.co/XbibzOfficial) · [TikTok](https://tiktok.com/@xbibzofficial) · [Telegram](https://t.me/xbibzofc)
|