@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/README.md CHANGED
@@ -1,280 +1,604 @@
1
1
  <p align="center">
2
- <img src="https://raw.githubusercontent.com/xbibzlibrary/whatsbibz/main/assets/logo/preview.png" alt="WhatsBibz" width="420"
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
- # WhatsBibz
5
+ <h1 align="center">WhatsBibz</h1>
7
6
 
8
- [![npm](https://img.shields.io/npm/v/%40xbibzlibrary%2Fwhatsbibz?label=%40xbibzlibrary%2Fwhatsbibz&color=0a6958)](https://www.npmjs.com/package/@xbibzlibrary/whatsbibz)
9
- [![node](https://img.shields.io/badge/node-%E2%89%A520-0a6958)](#instalasi)
10
- [![license](https://img.shields.io/badge/license-MIT-0a6958)](LICENSE)
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
- > Paket npm: **`@xbibzlibrary/whatsbibz`** — nama API internal tetap `BibzWhats`
13
- > (`createBibzWhats`, `BIBZWHATS_*`); alias `createWhatsBibz` juga tersedia.
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
- Library WhatsApp Web multi-device untuk ekosistem **Xbibz** — fork dari
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
- - ESM only, Node.js ≥ 20
22
- - Seluruh API Baileys/ourin tetap diekspor → kode lama jalan tanpa perubahan
23
- - `createBibzWhats()` = koneksi + pairing + reconnect + wipe sesi otomatis dalam satu panggilan
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
- ## Instalasi
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
- Semua contoh di bawah memakai specifier `@xbibzlibrary/whatsbibz`. Bila kamu memasangnya
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', // nomor bot (digit + kode negara)
48
- pairingCode: 'XBIBZPRO', // opsional: kode custom 8 karakter A-Z0-9
49
- authDir: 'bibzwhats-session',
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, { custom }) =>
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, '*pong* dari BibzWhats', { quoted: m });
104
+ await sendText(sock, m.key.remoteJid, 'pong', { quoted: m });
65
105
  }
66
106
  }
67
107
  });
68
108
  });
69
109
  ```
70
110
 
71
- `client` adalah `EventEmitter`:
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
- | Event | Payload | Keterangan |
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})` | kode siap dimasukkan di HP |
76
- | `qr` | `(qr)` | fallback QR (setelah `qrFallbackAfterMs`, atau langsung bila `phone` kosong) |
77
- | `ready` | `(sock)` | socket **baru** siap dipakai — pasang handler di sini. Dipancarkan saat start **dan tiap reconnect** (socket lama mati, listener ikut hilang). Set `readyOnEveryConnect:false` untuk perilaku lama (sekali) |
78
- | `first-ready` | `(sock)` | hanya sekali seumur client |
79
- | `open` / `close` | `(sock)` / `({status, error})` | tiap kali koneksi buka/tutup |
80
- | `user` | `(digits)` | nomor bot terdeteksi (persist bila perlu) |
81
- | `reconnecting` | `({delay, attempt, fresh})` | reconnect dijadwalkan |
82
- | `session-wiped` | `(reason)` | sesi dihapus (401/500/411/korup) |
83
- | `give-up` | `(message)` | berhenti mencoba — perlu campur tangan manusia |
84
-
85
- Properti/metode: `client.sock` (socket aktif; berubah setelah reconnect),
86
- `client.isConnected()`, `client.close()`, `client.logout()`.
87
-
88
- ### Perilaku bawaan
89
-
90
- - **Versi WA Web terbaru** diambil di setiap connect (`fetchLatestWaWebVersion`) — server menolak pairing dari klien yang versinya tertinggal. Bila fetch gagal, versi **tidak diturunkan** (dipakai versi bawaan library `BIBZWHATS_WA_WEB_VERSION`, diperbarui 2026-09-03 → `2.3000.1046721733`; upstream issue #2777: versi lama = 408 loop).
91
- - **Pairing code diverifikasi server**: `requestPairingCode()` menunggu jawaban `companion_hello` (upstream PR #2769). Penolakan (`400 bad-request`, `429 rate-overlimit`) atau timeout kini **terlihat sebagai error** dan `creds.me` tidak tersimpan untuk perangkat yang tak pernah diakui — dulu kode "sukses" palsu muncul tapi notifikasi tautkan perangkat tidak pernah datang di HP.
92
- - `companion_platform_display` **divalidasi WhatsApp dengan allow-list**. Sejak 1.2.0 library menurunkannya otomatis ke nilai yang pasti valid (`Browsers.macOS` → `"Chrome (Mac OS)"`, `Browsers.archLinux` → `"Chrome (Linux)"`, nama produk → OS host). Override manual tetap dihormati apa adanya — lihat § Identitas platform.
93
- - **515 restartRequired** setelah pairing = normal → reconnect otomatis 2 dtk.
94
- - **401 loggedOut / 500 badSession / 411 / sesi korup** → folder sesi dihapus, kredensial baru, pairing ulang (maks. `maxSessionWipes`, default 3). Identitas perangkat yang sudah terbukti diterima (`identity.json`) **tidak** ikut dihapus.
95
- - **401 saat pairing masih tertunda** (koneksi putus setelah kode terbit, lalu sambung ulang) dikenali terpisah: kredensial diganti dan kode diminta ulang dalam 2 s, tanpa mengurangi kuota `maxSessionWipes`. Event `reconnecting` membawa `pairingPending: true`.
96
- - **Reconnect** biasa dengan backoff 10 s × percobaan (maks. 60 s, `maxReconnectAttempts` = 10); koneksi yang sudah sehat lalu putus jaringan mencoba lagi dalam 1 s dulu.
97
- - **`client.close()` bersih**: reconnect terjadwal dibatalkan, tidak ada timer tersisa — proses bisa keluar sendiri (diuji 40 siklus create→close).
98
- - **Pairing controller**: satu request in-flight, refresh 150 dtk (kode yang sedang diketik tidak hangus), custom code ditolak → fallback acak sekali, 428/429 → backoff eksponensial sampai 10 menit.
99
- - **IPv4 dipaksa** untuk upload media (`forceIPv4`, bisa dimatikan).
100
- - Cache metadata grup 5 menit (`cachedGroupMetadata`) — hemat query saat kirim ke grup.
101
- - **Tidak ada auto-follow channel/newsletter diam-diam** (`autoFollowNewsletterOnConnect` default `false`).
102
-
103
- Semua opsi bisa dioverride; opsi mentah `makeWASocket` lewat `socketConfig`.
104
-
105
- ## API tingkat rendah (kompatibel Baileys)
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
- import makeWASocket, { useMultiFileAuthState, DisconnectReason, fetchLatestWaWebVersion, downloadMediaMessage } from '@xbibzlibrary/whatsbibz';
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
- Semua yang ada di Baileys v7 / ourin-baileys tersedia: `makeWASocket`,
112
- `useMultiFileAuthState`, `DisconnectReason`, `downloadMediaMessage`, `proto`,
113
- `Browsers`, `jidNormalizedUser`, interactive message (`sock.sendMessage(jid, { interactiveButtons: [...] })`),
114
- album, newsletter, `Button`/`Carousel`/`AIRich` builder, `VoipClient`, dll.
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
- Penyesuaian nama: `Dugong` → `BibzWhatsEngine`, `sock.ourin` → `sock.bibz`
117
- (alias `sock.ourin` dipertahankan), `Browsers.whatsbibz('Chrome')` tersedia (alias `Browsers.bibzwhats`).
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
- ## Helper BibzWhats
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
- - `sendText(sock, jid, text, {quoted, format})` — pecah otomatis ≤ 4000 karakter, retry 3×, markdown ChatGPT → format WA.
131
- - `sendMedia(sock, jid, content, {quoted, fallbackToDocument})` — bila media ditolak, dikirim ulang sebagai dokumen.
132
- - `extractMessage(m)` — `{type, text, participant, mentions, quoted, imageMsg|audioMsg|...}`; unwrap ephemeral/view-once/edited otomatis; tombol/list/poll/interactive response → `type: 'button' | 'poll'`.
133
- - `LidMap` — peta LID ↔ nomor (`learnFromMessage(m)`, `canonical(jid)`, `variants(jid)`, `toJSON()/fromJSON()`).
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
- ## Handler `companion_reg_refresh` (bawaan)
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
- Sejak 2026-07-28 server WA mengirim notification `companion_reg_refresh` /
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
- Fungsi murni untuk pengujian: `makePairingQRRenderer`, `handleCompanionRegRefresh`
145
- (`import ... from '@xbibzlibrary/whatsbibz'`).
289
+ ### 1) Native WhatsApp formatting (raw, no conversion)
146
290
 
147
- ## Struktur
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
- lib/
151
- BibzWhats/ client.js (createBibzWhats), pairing.js, send.js, extract.js, jid.js
152
- Socket/ socket.js (+ handler refresh), engine.js (BibzWhatsEngine), messages-*.js, ...
153
- Utils/ companion-reg-client-utils.js (baru), generics.js, messages-media.js, ...
154
- Defaults/ Signal/ Types/ WABinary/ WAM/ WAUSync/ VoIP/ Modded/
155
- WAProto/ protobuf WhatsApp
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
- ## Identitas perangkat tertaut: kustom atau otomatis (paling stabil)
308
+ ### 2) Automatic Markdown → WhatsApp conversion
159
309
 
160
- Yang tampil di HP sebagai *Perangkat tertaut* ditentukan oleh `browser = [os, browser, version]`.
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
- **Otomatis (default — tidak perlu setel apa pun).** Library memilih profil dengan skor stabilitas
164
- tertinggi (`Mac OS / Chrome`), lalu **berpindah sendiri** bila server menolaknya — `428` sebelum QR,
165
- `400 bad-request` saat pairing kode, atau `405` — ke profil berikutnya (`Mac OS/Safari` → `Windows/Chrome`
166
- → `Linux/Chrome` → `Ubuntu/Chrome` → …). Profil yang terbukti bekerja disimpan di `<authDir>/identity.json`
167
- sehingga setelah restart identitasnya tetap sama (nama perangkat di HP tidak berubah-ubah).
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 client = await createBibzWhats({ phone }); // identity: 'auto'
171
- client.on('identity-changed', (i) => console.log('ganti identitas →', i.linkedDeviceName, i.reason));
172
- console.log(client.identity);
173
- // { mode:'auto', profileId:'macos-chrome', linkedDeviceName:'Chrome (Mac OS)',
174
- // pairingDisplay:'Chrome (Mac OS)', pairingDisplayAccepted:true, tried:[] }
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
- **Kustom.** Identitas yang kamu tentukan dipakai **apa adanya** — tidak pernah diganti diam-diam. Bila
178
- server menolaknya kamu mendapat log error yang jelas, bukan identitas lain. Semua bentuk ini setara:
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
- createBibzWhats({ phone, identity: Browsers.archLinux('Chrome') }); // tuple preset
182
- createBibzWhats({ phone, identity: ['Arch Linux', 'Firefox', '6.16.4'] }); // tuple bebas
183
- createBibzWhats({ phone, identity: 'archLinux:Firefox' }); // "preset:Browser"
184
- createBibzWhats({ phone, identity: 'Mac OS/Safari/15.6.1' }); // "OS/Browser/Versi"
185
- createBibzWhats({ phone, identity: 'linux-chrome' }); // id profil
186
- createBibzWhats({ phone, browser: ['Mac OS', 'Chrome', '14.4.1'] }); // alias lama, tetap jalan
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
- Lewat environment (tanpa ubah kode): `BIBZ_BROWSER="archLinux:Chrome"` atau `BIBZ_BROWSER="Arch Linux/Firefox/6.16"`,
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
- | Profil auto (urutan) | Nama di HP | Display pairing |
369
+ | `name` | Shows as | `buttonParamsJson` example |
194
370
  |---|---|---|
195
- | `macos-chrome` (skor 100) | Chrome (Mac OS) | Chrome (Mac OS) |
196
- | `macos-safari` 95 · `windows-chrome` 92 · `linux-chrome` 90 · `ubuntu-chrome` 88 | … | sama dengan nama |
197
- | `macos-firefox` 85 · `windows-edge` 84 · `archlinux-chrome` 80 | Chrome (Arch Linux) | Chrome (Linux) |
198
- | `macos-desktop` / `windows-desktop` (hanya bila diminta eksplisit; butuh `syncFullHistory`) | Desktop (…) | Chrome (…) |
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
- Opsi terkait: `maxIdentityRotations` (default 4). Fungsi yang diekspor: `resolveDeviceIdentity`,
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
- ## Identitas platform — Mac OS & Arch Linux (diukur ke server 2026-09-03)
383
+ ### Way 2 — `ButtonV2` builder (legacy `buttonsMessage`)
204
384
 
205
- Identitas companion adalah tuple `browser = [os, browser, version]`. Ia dipakai di **tiga**
206
- tempat dengan aturan berbeda, dan BibzWhats menangani ketiganya:
385
+ ```js
386
+ import { ButtonV2 } from '@xbibzlibrary/whatsbibz';
207
387
 
208
- | Tempat | Dipakai untuk | Aturan server | Yang dilakukan BibzWhats |
209
- |---|---|---|---|
210
- | `DeviceProps.os` (= `browser[0]`) | teks di *Perangkat tertaut* di HP | bebas | dikirim apa adanya: **"Mac OS"**, **"Arch Linux"** |
211
- | `companion_platform_display` (pairing kode) | validasi `link_code_companion_reg` | **allow-list** — tak dikenal = `400 bad-request`, HP tak dapat notifikasi | diturunkan otomatis ke padanan valid (`derivePairingDisplay`) |
212
- | `WebInfo.webSubPlatform` (`Desktop` + `syncFullHistory`) | jenis klien desktop | `DARWIN`/`APP_STORE` **ditutup** (428 sebelum QR), `WIN_HYBRID`/`WEB_BROWSER` OK | Mac OS Desktop → `WEB_BROWSER` (riwayat penuh tetap via `requireFullSync`) |
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
- import { createBibzWhats, Browsers } from '@xbibzlibrary/whatsbibz';
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
- // macOS — tampil "Chrome (Mac OS)" / "Safari (Mac OS)" di HP
218
- await createBibzWhats({ phone, browser: Browsers.macOS('Chrome') }); // default library
423
+ ### Reading button taps (inbound)
219
424
 
220
- // Arch Linux — tampil "Chrome (Arch Linux)" di HP; pairing kode memakai "Chrome (Linux)"
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
- // mengikuti host: macOS → "Mac OS", Arch → "Arch Linux" (dibaca dari /etc/os-release), Windows → "Windows"
224
- await createBibzWhats({ phone, browser: Browsers.appropriate('Chrome') });
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
- client.identity // → { browser: ['Arch Linux','Chrome','6.12.44'], pairingDisplay: 'Chrome (Linux)' }
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
- Hasil pengukuran `companion_platform_display` terhadap server produksi (WA Web 2.3000.1046672143):
474
+ Other methods: `sendTableV2`, `sendList`, `sendCodeBlockV2`, `sendLinkV2`, `sendLatex`, `sendLatexImage`, `sendUnifiedResponse`, `captureUnifiedResponse` (see `Socket/messages-send.js`).
230
475
 
231
- | Diterima | Ditolak (400) |
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
- Helper yang diekspor: `derivePairingDisplay(browser)`, `isPairingDisplayAccepted(str)`,
237
- `resolvePairingOs(os)`, `lintIdentity({browser, companionPlatformDisplay})`, `hostOsLabel()`,
238
- `linuxDistroName()`. Opsi socket baru: `companionPlatformId`, `webSubPlatform` (override eksplisit).
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
- Uji hidup ke server (tanpa HP, nomor fiktif Ofcom +44 7700 900xxx): `npm run test:live`.
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
- ## Kompatibilitas WhatsApp Web (sinkron 2026-09-02)
497
+ ### Ethical usage note
243
498
 
244
- Basis `ourin-baileys@9.0.21` setara Baileys **7.0.0-rc10** (Mei 2026). Patch upstream rc11 → rc14 → master + PR terbuka yang relevan sudah di-port:
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
- | Patch | Sumber | Dampak |
247
- |---|---|---|
248
- | `WIN32` → `WIN_HYBRID` untuk `Browsers.windows('Desktop')` | master #2741 (2026-08-04) | server menutup 428 sebelum QR bila WIN32 diiklankan |
249
- | versi WA Web bawaan `2.3000.1046721733` | web.whatsapp.com (2026-09-03) | fallback tidak lagi tertinggal |
250
- | `requestPairingCode` pakai `query()`, `creds.me` setelah ACK, `companionPlatformDisplay` | PR #2769 | pairing kode gagal kini terdeteksi, sesi tidak teracuni |
251
- | tctoken: atribut `t`, token kedaluwarsa/tanpa timestamp dibuang, bersarang di `<picture>`, tidak untuk JID sendiri | rc11+, #2607 | `profilePictureUrl` & kirim 1:1 sesuai WA Web |
252
- | guard `protocolMessage` self-only (history sync / app-state key / LID migration / PDO) | rc12 | pengirim asing tidak bisa memalsukan state lokal |
253
- | `Browsers.android()` + platform ANDROID (eksperimental) | #2201 | companion Android (bisa terima view-once) |
254
- | `DARWIN` → `WEB_BROWSER` untuk `Browsers.macOS('Desktop')`; `companion_platform_display` allow-list; `Browsers.archLinux/linux`; `appropriate()` sadar distro | pengukuran BibzWhats 2026-09-03 (belum ada di upstream) | Mac OS Desktop tidak lagi 428; Arch Linux bisa pairing kode |
255
- | `groupOnlineCount` di presence grup | #2545 | data tambahan `presence.update` |
256
- | `<presence/>` tidak dikirim pada `creds.update` parsial | PR #2789 | `markOnlineOnConnect:false` dihormati, notifikasi HP tidak tersedot |
257
- | `downloadContentFromMessage` mengutamakan `directPath` | PR #2778 | hindari `ENOTFOUND a.whatsapp.net` |
258
- | `lottieStickerMessage` di-unwrap | PR #2776 | stiker Lottie terdeteksi sebagai sticker |
259
- | guard `dispatcher` fetch (hanya Undici) | #2557 | `https.Agent` tidak bikin fetch error |
260
- | `ev.destroy()`, `signalRepository.close()`, `registerSocketEndHandler` | #2191 | tidak bocor memori setelah reconnect berulang |
261
- | `whatsapp-rust-bridge` 0.5.5 | upstream rc14 | dukungan CPU tanpa SIMD |
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
- Deklarasi tipe ikut di paket (`lib/index.d.ts`, `WAProto/index.d.ts` lengkap 217 namespace `proto`). Diverifikasi dengan
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
- ## Catatan
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&amp;logo=ko-fi&amp;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&amp;logo=tiktok&amp;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&amp;logo=telegram&amp;logoColor=white" alt="Telegram"></a>
602
+ </p>
277
603
 
278
- - Logo & ikon (PNG/ICO) ada di `assets/logo/` — lihat `assets/logo/README.md`.
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)