@xbibzlibrary/whatsbibz 1.3.1 → 1.3.2

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,374 @@
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
+ <img src="https://cdn.jsdelivr.net/gh/XbibzOfficial777/whatsbibz@main/assets/logo/preview.png" alt="WhatsBibz" width="360">
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>
22
+ </p>
23
+
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
+ - [Low-level API (Baileys-compatible)](#low-level-api-baileys-compatible)
43
+ - [TypeScript](#typescript)
44
+ - [Migrating from Baileys / ourin-baileys](#migrating-from-baileys--ourin-baileys)
45
+ - [Troubleshooting](#troubleshooting)
46
+ - [Testing](#testing)
47
+ - [Project structure](#project-structure)
48
+ - [Versioning and compatibility](#versioning-and-compatibility)
49
+ - [Contributing](#contributing)
50
+ - [Security](#security)
51
+ - [License](#license)
52
+
53
+ ## Why WhatsBibz
54
+
55
+ 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`, plus a layer that solves the things every bot author ends up rewriting:
56
+
57
+ | Problem | What WhatsBibz does |
58
+ |---|---|
59
+ | 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. |
60
+ | 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. |
61
+ | Corrupt or logged-out sessions loop forever | `401 / 500 / 411 / corrupt` → session folder wiped, new credentials, re-pair — bounded by `maxSessionWipes`. |
62
+ | `companion_reg_refresh` notifications kill unpaired sessions | Handled inside the socket (no `postinstall` patches). |
63
+ | Leaked timers after `close()`, ghost reconnects | `close()` cancels every scheduled reconnect; verified over 40 create→close cycles. |
64
+ | TypeScript users get an empty `WAProto/index.d.ts` from upstream | Complete declarations for all 217 `proto` namespaces; `tsc --strict` passes with `skipLibCheck: false`. |
11
65
 
12
- > Paket npm: **`@xbibzlibrary/whatsbibz`** — nama API internal tetap `BibzWhats`
13
- > (`createBibzWhats`, `BIBZWHATS_*`); alias `createWhatsBibz` juga tersedia.
66
+ Everything from Baileys is still exported — existing code keeps working with only the import specifier changed.
14
67
 
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`).
68
+ ## Requirements
20
69
 
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
70
+ - **Node.js ≥ 20** (tested on 20, 22, 24 and 26)
71
+ - ESM only (`"type": "module"` or dynamic `import()`)
72
+ - Optional peers: `sharp` (media thumbnails), `qrcode-terminal` (ASCII QR fallback), `link-preview-js`, `jimp`, `audio-decode`, `@roamhq/wrtc` (VoIP)
24
73
 
25
- ## Instalasi
74
+ ## Installation
26
75
 
27
76
  ```bash
28
77
  npm install @xbibzlibrary/whatsbibz
29
- # atau lokal (vendored di proyek):
30
- npm install ./BibzWhats
31
78
  ```
32
79
 
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()`
80
+ ## Quick start
42
81
 
43
82
  ```js
44
83
  import { createBibzWhats, extractMessage, sendText } from '@xbibzlibrary/whatsbibz';
45
84
 
46
85
  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
86
+ phone: '6281234567890', // bot number, digits with country code
87
+ pairingCode: 'XBIBZPRO', // optional: custom 8-character code (A–Z, 0–9)
88
+ authDir: 'whatsbibz-session',
51
89
  });
52
90
 
53
- client.on('pairing-code', (code, { custom }) =>
54
- console.log(`PAIRING CODE: ${code} ${custom ? '(custom)' : '(acak)'}`));
91
+ client.on('pairing-code', (code) => console.log('Enter this code on your phone:', code));
55
92
 
56
93
  client.on('ready', (sock) => {
57
- console.log('terhubung sebagai', sock.user?.id);
58
94
  sock.ev.on('messages.upsert', async ({ messages, type }) => {
59
95
  if (type !== 'notify') return;
60
96
  for (const m of messages) {
61
97
  if (m.key.fromMe) continue;
62
98
  const item = extractMessage(m);
63
99
  if (item?.type === 'text' && /^ping$/i.test(item.text)) {
64
- await sendText(sock, m.key.remoteJid, '*pong* dari BibzWhats', { quoted: m });
100
+ await sendText(sock, m.key.remoteJid, 'pong', { quoted: m });
65
101
  }
66
102
  }
67
103
  });
68
104
  });
69
105
  ```
70
106
 
71
- `client` adalah `EventEmitter`:
107
+ 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).
72
108
 
73
- | Event | Payload | Keterangan |
74
- |---|---|---|
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)
109
+ A runnable example lives in [`example/ping-bot.js`](example/ping-bot.js).
106
110
 
107
- ```js
108
- import makeWASocket, { useMultiFileAuthState, DisconnectReason, fetchLatestWaWebVersion, downloadMediaMessage } from '@xbibzlibrary/whatsbibz';
109
- ```
111
+ ## The high-level client: `createBibzWhats()`
110
112
 
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.
113
+ `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.
115
114
 
116
- Penyesuaian nama: `Dugong` → `BibzWhatsEngine`, `sock.ourin` → `sock.bibz`
117
- (alias `sock.ourin` dipertahankan), `Browsers.whatsbibz('Chrome')` tersedia (alias `Browsers.bibzwhats`).
115
+ ### Options
118
116
 
119
- ## Helper BibzWhats
117
+ | Option | Type | Default | Description |
118
+ |---|---|---|---|
119
+ | `phone` | `string` | — | Bot number in digits with country code. Required for pairing codes; omit for QR. |
120
+ | `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. |
121
+ | `authDir` | `string` | `'bibzwhats-session'` | Folder for credentials, signal keys and `identity.json`. |
122
+ | `identity` | `'auto' \| string \| [os, browser, version]` | `'auto'` | Linked-device identity. See [Linked-device identity](#linked-device-identity). |
123
+ | `browser` | `[os, browser, version]` | `null` | Legacy alias of `identity` (explicit tuple). |
124
+ | `maxIdentityRotations` | `number` | `4` | Automatic mode: how many alternative profiles to try before giving up. |
125
+ | `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. |
126
+ | `logger` | `{ info, warn, error, debug, ok }` | console | Any object with those methods (all optional). |
127
+ | `printQR` | `boolean` | `false` | Print the QR as ASCII when a QR is shown (requires `qrcode-terminal`). |
128
+ | `socketConfig` | `Partial<SocketConfig>` | `{}` | Raw `makeWASocket` options merged last — full escape hatch. |
129
+ | `fetchLatestVersion` | `boolean` | `true` | Fetch the current WhatsApp Web version on every connect. The built-in version is never downgraded. |
130
+ | `forceIPv4` | `boolean` | `true` | Force IPv4 for media uploads. |
131
+ | `readyOnEveryConnect` | `boolean` | `true` | Emit `ready` for every new socket (first connect and each reconnect). |
132
+ | `maxReconnectAttempts` | `number` | `10` | Consecutive failed reconnects before `give-up`. |
133
+ | `reconnectStepMs` / `reconnectMaxMs` | `number` | `10000` / `60000` | Linear back-off: `step × attempt`, capped at `max`. |
134
+ | `restartDelayMs` | `number` | `2000` | Delay after `515 restartRequired` (normal after pairing) and after an identity rotation. |
135
+ | `maxSessionWipes` | `number` | `3` | Session wipes (401/500/411/corrupt) before `give-up`. |
136
+ | `wipeReconnectDelayMs` | `number` | `10000` | Delay before reconnecting with fresh credentials. |
137
+ | `qrFallbackAfterMs` | `number` | `90000` | With `phone` set, show a QR if no pairing code was accepted within this time. |
138
+ | `pairingRequestDelayMs` | `number` | `20000` | If the server sends no QR at all, request a pairing code directly after this delay. |
139
+ | `groupMetadataTtlMs` | `number` | `300000` | Cache TTL for group metadata used when sending to groups. |
140
+
141
+ Defaults are exported as `BIBZWHATS_DEFAULTS`.
142
+
143
+ ### Events
144
+
145
+ | Event | Payload | When |
146
+ |---|---|---|
147
+ | `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. |
148
+ | `qr` | `(qr)` | A QR string is available (immediately without `phone`, or after `qrFallbackAfterMs`). |
149
+ | `ready` | `(sock)` | A **new socket** is ready. Attach your message handlers here — sockets are replaced on reconnect and old listeners die with them. |
150
+ | `first-ready` | `(sock)` | Like `ready`, but only once per client. |
151
+ | `open` | `(sock)` | Connection opened (every time). |
152
+ | `user` | `(digits)` | The bot's own number became known. |
153
+ | `close` | `({ status, error })` | Connection closed. `status` is the `DisconnectReason` code. |
154
+ | `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). |
155
+ | `identity-changed` | `({ browser, linkedDeviceName, pairingDisplay, pairingDisplayAccepted, profileId, reason })` | Automatic mode only: the server rejected the previous profile and the next one is in use. |
156
+ | `session-wiped` | `(reason)` | The session folder was deleted (`'401 loggedOut'`, `'500 badSession'`, `'411'`, `'sesi korup'`, `'401 pairing tertunda'`). |
157
+ | `give-up` | `(message)` | The client stopped trying. Human intervention is needed (wrong number, blocked IP, …). |
158
+ | `connection.update` | `(update)` | Raw Baileys connection update, forwarded. |
159
+
160
+ ### Properties and methods
161
+
162
+ | Member | Description |
163
+ |---|---|
164
+ | `client.sock` | The current socket (`null` before the first connect; changes on reconnect). |
165
+ | `client.identity` | `{ mode: 'auto' \| 'custom', source, profileId, browser, linkedDeviceName, pairingDisplay, pairingDisplayAccepted, tried: [{ id, reason }] }` |
166
+ | `client.options` | Effective options after defaults. |
167
+ | `client.isConnected()` | `true` while the socket is open. |
168
+ | `client.close()` | Close the socket and cancel scheduled reconnects. Leaves no timers behind. |
169
+ | `client.logout()` | Log the device out on the server and delete the session folder. |
170
+
171
+ ### Connection lifecycle
120
172
 
121
- ```js
122
- import {
123
- sendText, sendMedia, react, presence, sendWithRetry, splitText, whatsappify,
124
- extractMessage, unwrapMessage, messageTimestampMs,
125
- LidMap, digitsOf, pnJid, lidJid, isGroupJid, isLidJid, sameUser, normalizeJid,
126
- createPairingController, normalizePairingCode, wipeAuthDir,
127
- } from '@xbibzlibrary/whatsbibz';
173
+ ```
174
+ connect ──► QR / pairing code ──► 515 restartRequired ──► reconnect (2 s) ──► open ──► ready
175
+ │ │
176
+ │ 428 before QR / 405 (identity rejected) auto → next profile, 2 s │ network drop → 1 s, then back-off
177
+ │ 400 at pairing (identity rejected) auto → next profile, 2 s │ 401 registered → wipe, re-pair
178
+ │ 401 with pairing pending new credentials, 2 s │
179
+ └─ other close codes ── back-off 10 s × attempt (max 60 s, 10 attempts) ── give-up
128
180
  ```
129
181
 
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()`).
182
+ Details worth knowing:
134
183
 
135
- ## Handler `companion_reg_refresh` (bawaan)
184
+ - **515 after pairing is normal.** The server asks the client to restart; the client reconnects after `restartDelayMs`.
185
+ - **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`.
186
+ - **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.
187
+ - **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.
136
188
 
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.
189
+ ## Linked-device identity
143
190
 
144
- Fungsi murni untuk pengujian: `makePairingQRRenderer`, `handleCompanionRegRefresh`
145
- (`import ... from '@xbibzlibrary/whatsbibz'`).
191
+ 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.
146
192
 
147
- ## Struktur
193
+ ### Automatic mode (default)
148
194
 
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
195
+ ```js
196
+ const client = await createBibzWhats({ phone }); // identity: 'auto'
197
+ client.on('identity-changed', (i) => console.log('switched to', i.linkedDeviceName, 'because', i.reason));
198
+ console.log(client.identity);
199
+ // { mode: 'auto', profileId: 'macos-chrome', linkedDeviceName: 'Chrome (Mac OS)',
200
+ // pairingDisplay: 'Chrome (Mac OS)', pairingDisplayAccepted: true, tried: [] }
156
201
  ```
157
202
 
158
- ## Identitas perangkat tertaut: kustom atau otomatis (paling stabil)
203
+ 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`):
159
204
 
160
- Yang tampil di HP sebagai *Perangkat tertaut* ditentukan oleh `browser = [os, browser, version]`.
161
- Sejak 1.3.0 ada dua mode:
205
+ | Order | Profile | Shown on the phone | Sent at pairing |
206
+ |---|---|---|---|
207
+ | 1 | `macos-chrome` | Chrome (Mac OS) | Chrome (Mac OS) |
208
+ | 2 | `macos-safari` | Safari (Mac OS) | Safari (Mac OS) |
209
+ | 3 | `windows-chrome` | Chrome (Windows) | Chrome (Windows) |
210
+ | 4 | `linux-chrome` | Chrome (Linux) | Chrome (Linux) |
211
+ | 5 | `ubuntu-chrome` | Chrome (Ubuntu) | Chrome (Ubuntu) |
212
+ | 6 | `macos-firefox` | Firefox (Mac OS) | Firefox (Mac OS) |
213
+ | 7 | `windows-edge` | Edge (Windows) | Edge (Windows) |
214
+ | 8 | `archlinux-chrome` | Chrome (Arch Linux) | Chrome (Linux) |
162
215
 
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).
216
+ 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.
168
217
 
169
- ```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:[] }
175
- ```
218
+ ### Custom mode
176
219
 
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:
220
+ 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.
179
221
 
180
222
  ```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
223
+ createBibzWhats({ phone, identity: Browsers.archLinux('Chrome') }); // preset tuple
224
+ createBibzWhats({ phone, identity: ['Arch Linux', 'Firefox', '6.16.4'] }); // free tuple
225
+ createBibzWhats({ phone, identity: 'archLinux:Firefox' }); // "preset:Browser"
226
+ createBibzWhats({ phone, identity: 'Mac OS/Safari/15.6.1' }); // "OS/Browser/Version"
227
+ createBibzWhats({ phone, identity: 'linux-chrome' }); // profile id
228
+ createBibzWhats({ phone, browser: ['Mac OS', 'Chrome', '14.4.1'] }); // legacy alias
187
229
  ```
188
230
 
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.
231
+ Environment variables work without code changes — priority is option → env → auto:
192
232
 
193
- | Profil auto (urutan) | Nama di HP | Display pairing |
194
- |---|---|---|
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 (…) |
233
+ ```bash
234
+ BIBZ_BROWSER="archLinux:Chrome" # or "Arch Linux/Firefox/6.16", or "auto"
235
+ # or the trio
236
+ BIBZ_DEVICE_OS="Arch Linux" BIBZ_DEVICE_BROWSER="Chrome" BIBZ_DEVICE_VERSION="6.16"
237
+ ```
199
238
 
200
- Opsi terkait: `maxIdentityRotations` (default 4). Fungsi yang diekspor: `resolveDeviceIdentity`,
201
- `parseBrowserSpec`, `identityFromEnv`, `IDENTITY_PROFILES`, `describeIdentity`, `createIdentityRotator`.
239
+ An empty version is filled per OS (Mac OS `15.6.1`, Windows `10.0.22631`, Linux `6.12.44`, other `1.0.0`).
202
240
 
203
- ## Identitas platform — Mac OS & Arch Linux (diukur ke server 2026-09-03)
241
+ Presets on `Browsers`: `macOS`, `windows`, `ubuntu`, `linux`, `archLinux`, `android` (experimental), `appropriate` (follows the host OS, distro-aware via `/etc/os-release`), `whatsbibz`.
204
242
 
205
- Identitas companion adalah tuple `browser = [os, browser, version]`. Ia dipakai di **tiga**
206
- tempat dengan aturan berbeda, dan BibzWhats menangani ketiganya:
243
+ ### What WhatsApp accepts
207
244
 
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`) |
245
+ The identity is used in three places with different rules. Measured against production servers on 2026-09-03:
213
246
 
214
- ```js
215
- import { createBibzWhats, Browsers } from '@xbibzlibrary/whatsbibz';
247
+ | Where | Used for | Server rule | WhatsBibz behaviour |
248
+ |---|---|---|---|
249
+ | `DeviceProps.os` (`browser[0]`) | Text under *Linked devices* | Free text | Sent unchanged — "Arch Linux" shows as "Arch Linux" |
250
+ | `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)` |
251
+ | `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 |
216
252
 
217
- // macOS — tampil "Chrome (Mac OS)" / "Safari (Mac OS)" di HP
218
- await createBibzWhats({ phone, browser: Browsers.macOS('Chrome') }); // default library
253
+ | Accepted | Rejected (`400`) |
254
+ |---|---|
255
+ | 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 |
256
+ | Browser: Chrome, Chromium, Firefox, Safari, Edge, Opera, Brave, Vivaldi, Arc | Browser: Desktop, any product name |
219
257
 
220
- // Arch Linux — tampil "Chrome (Arch Linux)" di HP; pairing kode memakai "Chrome (Linux)"
221
- await createBibzWhats({ phone, browser: Browsers.archLinux('Chrome') });
258
+ Helpers: `derivePairingDisplay(browser)`, `isPairingDisplayAccepted(str)`, `lintIdentity({ browser, companionPlatformDisplay })`, `resolveDeviceIdentity(opts)`, `parseBrowserSpec(spec)`, `identityFromEnv(env)`, `describeIdentity(browser)`, `IDENTITY_PROFILES`.
222
259
 
223
- // mengikuti host: macOS → "Mac OS", Arch → "Arch Linux" (dibaca dari /etc/os-release), Windows → "Windows"
224
- await createBibzWhats({ phone, browser: Browsers.appropriate('Chrome') });
260
+ ## Sending and reading messages
225
261
 
226
- client.identity // → { browser: ['Arch Linux','Chrome','6.12.44'], pairingDisplay: 'Chrome (Linux)' }
262
+ ```js
263
+ import {
264
+ sendText, sendMedia, react, presence, sendWithRetry, splitText, whatsappify,
265
+ extractMessage, unwrapMessage, messageTimestampMs,
266
+ LidMap, digitsOf, pnJid, lidJid, isGroupJid, isLidJid, sameUser, normalizeJid,
267
+ } from '@xbibzlibrary/whatsbibz';
227
268
  ```
228
269
 
229
- Hasil pengukuran `companion_platform_display` terhadap server produksi (WA Web 2.3000.1046672143):
230
-
231
- | Diterima | Ditolak (400) |
270
+ | Helper | What it does |
232
271
  |---|---|
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 |
272
+ | `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? }`. |
273
+ | `sendMedia(sock, jid, content, { quoted, fallbackToDocument })` | Sends image/video/audio/document; if the media is refused it is re-sent as a document. |
274
+ | `react(sock, jid, key, emoji)` | Reacts to a message. |
275
+ | `presence(sock, jid, state)` | `composing`, `recording`, `paused`, `available`, `unavailable`. |
276
+ | `sendWithRetry(sock, jid, content, options, { attempts })` | Generic `sendMessage` with retry. |
277
+ | `extractMessage(m)` | Normalises any incoming message to `{ type, text, participant, mentions, quoted, imageMsg \| videoMsg \| … , buttonId, pollName, … }`. Unwraps ephemeral, view-once and edited wrappers. |
278
+ | `LidMap` | Maps LID ↔ phone JIDs: `learnFromMessage(m)`, `canonical(jid)`, `variants(jid)`, `toJSON()` / `fromJSON()`. |
235
279
 
236
- Helper yang diekspor: `derivePairingDisplay(browser)`, `isPairingDisplayAccepted(str)`,
237
- `resolvePairingOs(os)`, `lintIdentity({browser, companionPlatformDisplay})`, `hostOsLabel()`,
238
- `linuxDistroName()`. Opsi socket baru: `companionPlatformId`, `webSubPlatform` (override eksplisit).
280
+ 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.
239
281
 
240
- Uji hidup ke server (tanpa HP, nomor fiktif Ofcom +44 7700 900xxx): `npm run test:live`.
282
+ ## Low-level API (Baileys-compatible)
241
283
 
242
- ## Kompatibilitas WhatsApp Web (sinkron 2026-09-02)
284
+ ```js
285
+ import makeWASocket, {
286
+ useMultiFileAuthState, makeCacheableSignalKeyStore, fetchLatestWaWebVersion,
287
+ DisconnectReason, Browsers, downloadMediaMessage, proto, jidNormalizedUser,
288
+ } from '@xbibzlibrary/whatsbibz';
243
289
 
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:
290
+ const { state, saveCreds } = await useMultiFileAuthState('session');
291
+ const { version } = await fetchLatestWaWebVersion();
292
+ const sock = makeWASocket({ version, auth: state, browser: Browsers.macOS('Chrome') });
293
+ sock.ev.on('creds.update', saveCreds);
294
+ ```
245
295
 
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))"`.
296
+ 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`.
297
+
298
+ 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
299
 
265
300
  ## TypeScript
266
301
 
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`.
302
+ 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
303
 
270
304
  ```ts
271
305
  import { createBibzWhats, type BibzWhatsClient, type ResolvedIdentity } from '@xbibzlibrary/whatsbibz';
306
+
272
307
  const client: BibzWhatsClient = await createBibzWhats({ phone: '628…', identity: 'archLinux:Chrome' });
273
308
  client.on('identity-changed', (i) => console.log(i.linkedDeviceName));
274
309
  ```
275
310
 
276
- ## Catatan
311
+ ## Migrating from Baileys / ourin-baileys
312
+
313
+ 1. `npm uninstall baileys ourin-baileys && npm install @xbibzlibrary/whatsbibz`
314
+ 2. Replace the import specifier. Every export keeps its name.
315
+ 3. Remove any `postinstall` patch scripts for `companion_reg_refresh` — the handler is built in.
316
+ 4. Optional: replace your own connect/reconnect/pairing loop with `createBibzWhats()`.
317
+
318
+ ## Troubleshooting
319
+
320
+ | Symptom | Cause | Fix |
321
+ |---|---|---|
322
+ | 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`). |
323
+ | `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. |
324
+ | `400 bad-request` at pairing | `companion_platform_display` not on the allow-list. | Don't override `companionPlatformDisplay`; the derived value is always valid. |
325
+ | `401` right after a reconnect while pairing | Previous pairing still pending on the server. | Handled automatically (`reconnecting` with `pairingPending: true`). |
326
+ | `408` loop | WhatsApp Web version too old. | Keep `fetchLatestVersion: true` (default) or update the package. |
327
+ | `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. |
328
+ | Handlers stop working after a reconnect | Handlers were attached to the old socket. | Attach them inside `client.on('ready', sock => …)`. |
329
+ | 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`. |
330
+
331
+ ## Testing
332
+
333
+ ```bash
334
+ npm test # 55 offline tests (protocol, identity, pairing controller, lifecycle) — no network
335
+ npm run check # export surface sanity check
336
+ npm run test:live # 11 tests against real WhatsApp servers (no phone needed; uses fictional Ofcom numbers)
337
+ ```
338
+
339
+ 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).
340
+
341
+ ## Project structure
342
+
343
+ ```
344
+ lib/
345
+ BibzWhats/ client.js (createBibzWhats), device-identity.js, pairing.js, send.js, extract.js, jid.js
346
+ Socket/ socket.js, engine.js, messages-send.js, messages-recv.js, groups.js, newsletter.js, ...
347
+ Utils/ platform-identity.js, browser-utils.js, messages-media.js, use-multi-file-auth-state.js, ...
348
+ Defaults/ Signal/ Types/ WABinary/ WAM/ WAUSync/ VoIP/ Modded/
349
+ WAProto/ protobuf definitions (index.js + complete index.d.ts)
350
+ assets/logo/ logo, icons, favicon
351
+ example/ ping-bot.js
352
+ test/ node:test suites (offline + live)
353
+ ```
354
+
355
+ ## Versioning and compatibility
356
+
357
+ - Semantic versioning. Breaking changes only in a major release.
358
+ - Built-in WhatsApp Web version is refreshed with every release and fetched live on every connect.
359
+ - Supported Node.js: current LTS lines and current release (20 / 22 / 24 / 26 at the time of writing).
360
+ - Changes are listed in [CHANGELOG.md](CHANGELOG.md).
361
+
362
+ ## Contributing
363
+
364
+ 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).
365
+
366
+ ## Security
367
+
368
+ Do not open public issues for vulnerabilities. See [SECURITY.md](SECURITY.md).
369
+
370
+ ## License
371
+
372
+ MIT — © 2026 Xbibz Developer. Based on Baileys, MIT © Rajeh Taher / WhiskeySockets — see [LICENSE](LICENSE) and [LICENSE.upstream](LICENSE.upstream).
277
373
 
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`.
374
+ 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.