whalibmob 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +379 -0
- package/cli.js +509 -0
- package/index.js +35 -0
- package/lib/BinaryNode.js +278 -0
- package/lib/Client.js +957 -0
- package/lib/DeviceManager.js +238 -0
- package/lib/MediaService.js +181 -0
- package/lib/Registration.js +475 -0
- package/lib/Store.js +226 -0
- package/lib/constants.js +68 -0
- package/lib/messages/MessageSender.js +548 -0
- package/lib/noise.js +371 -0
- package/lib/proto/MessageProto.js +229 -0
- package/lib/proto.js +152 -0
- package/lib/signal/SenderKey.js +329 -0
- package/lib/signal/SignalProtocol.js +269 -0
- package/lib/signal/SignalStore.js +161 -0
- package/lib/tokens.js +216 -0
- package/package.json +18 -0
package/README.md
ADDED
|
@@ -0,0 +1,379 @@
|
|
|
1
|
+
# whalibmob
|
|
2
|
+
|
|
3
|
+
A pure JavaScript Node.js library and CLI for WhatsApp. Operates as a
|
|
4
|
+
mobile iOS client with full Signal Protocol end-to-end encryption, SenderKey
|
|
5
|
+
group messaging, multi-device fanout, persistent sessions, and automatic
|
|
6
|
+
reconnection.
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
> **⚠️ IMPORTANT — You need a brand-new phone number and a brand-new SIM card
|
|
11
|
+
> dedicated to WhatsApp for this library to work for you.** Using an existing
|
|
12
|
+
> WhatsApp account that is already active on a real phone will cause
|
|
13
|
+
> WhatsApp to ban or revoke the session. Register a fresh number that has
|
|
14
|
+
> never been used with WhatsApp before.
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## Features
|
|
19
|
+
|
|
20
|
+
- **Mobile iOS fingerprint** — authentic iOS device headers; behaves like
|
|
21
|
+
WhatsApp on an iPhone
|
|
22
|
+
- **Signal Protocol E2E encryption** — X3DH session setup, Double Ratchet
|
|
23
|
+
for 1-to-1 messages
|
|
24
|
+
- **SenderKey group encryption** — efficient group messaging with chain-key
|
|
25
|
+
advance and SKDM distribution
|
|
26
|
+
- **Multi-device fanout** — messages delivered to all linked devices of each
|
|
27
|
+
recipient via usync device query
|
|
28
|
+
- **All media types** — images, video, audio, voice notes (PTT), documents,
|
|
29
|
+
stickers; AES-256-CBC + HKDF-SHA256 encryption, uploaded to WhatsApp CDN
|
|
30
|
+
- **Text, reactions, extended text** — full message type coverage
|
|
31
|
+
- **Participant hash (phash)** — SHA-256 participant-list validation sent
|
|
32
|
+
with every multi-device and group message
|
|
33
|
+
- **deviceSentMessage** — own linked devices (tablet, secondary phone) receive
|
|
34
|
+
a proper copy-sent wrapper so they show the message as sent
|
|
35
|
+
- **senderKeyMap** — SKDM is sent to each device only once; subsequent group
|
|
36
|
+
messages skip SKDM entirely for devices that already have the key
|
|
37
|
+
- **Automatic reconnection** — exponential backoff (1 s → 2 → 4 → 8 → 15 → 30 s)
|
|
38
|
+
- **Pre-key replenishment** — automatic upload when supply drops below 20
|
|
39
|
+
- **Persistent sessions** — JSON files on disk; no re-registration after restart
|
|
40
|
+
- **Auth confirmation** — waits for server `<success>` before declaring the
|
|
41
|
+
connection open; handles `<failure>` (session revoked / account banned)
|
|
42
|
+
- **Professional `wa` CLI** — yowsup-style interface for registration, sending,
|
|
43
|
+
listening, and interactive chat
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## Installation
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
cd tools/whalibmob
|
|
51
|
+
npm install
|
|
52
|
+
npm link # makes `wa` available globally
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Requires **Node.js 18** or higher.
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
## CLI Usage
|
|
60
|
+
|
|
61
|
+
### Check if a number is registered
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
wa registration --check 919634847671
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### Request a verification code
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
# via SMS (default)
|
|
71
|
+
wa registration --request-code 919634847671
|
|
72
|
+
|
|
73
|
+
# via voice call
|
|
74
|
+
wa registration --request-code 919634847671 --method voice
|
|
75
|
+
|
|
76
|
+
# via backup WhatsApp account (wa_old)
|
|
77
|
+
wa registration --request-code 919634847671 --method wa_old
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### Register with the received code
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
wa registration --register 919634847671 --code 123456
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### Connect and enter an interactive shell
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
wa connect 919634847671
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Interactive commands at the `wa>` prompt:
|
|
93
|
+
|
|
94
|
+
```
|
|
95
|
+
/send <jid> <text> Send a text message
|
|
96
|
+
/presence Set presence to available
|
|
97
|
+
/presence away Set presence to unavailable
|
|
98
|
+
/quit Disconnect and exit
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### Send messages
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
# Text
|
|
105
|
+
wa send 919634847671 --to 911234567890@s.whatsapp.net --text "Hello!"
|
|
106
|
+
|
|
107
|
+
# Image with caption
|
|
108
|
+
wa send 919634847671 --to 911234567890@s.whatsapp.net --image ./photo.jpg --caption "Look at this"
|
|
109
|
+
|
|
110
|
+
# Video
|
|
111
|
+
wa send 919634847671 --to 911234567890@s.whatsapp.net --video ./clip.mp4
|
|
112
|
+
|
|
113
|
+
# Voice note (PTT)
|
|
114
|
+
wa send 919634847671 --to 911234567890@s.whatsapp.net --ptt ./voice.ogg
|
|
115
|
+
|
|
116
|
+
# Audio file
|
|
117
|
+
wa send 919634847671 --to 911234567890@s.whatsapp.net --audio ./song.mp3
|
|
118
|
+
|
|
119
|
+
# Document
|
|
120
|
+
wa send 919634847671 --to 911234567890@s.whatsapp.net --document ./report.pdf
|
|
121
|
+
|
|
122
|
+
# Sticker
|
|
123
|
+
wa send 919634847671 --to 911234567890@s.whatsapp.net --sticker ./sticker.webp
|
|
124
|
+
|
|
125
|
+
# Emoji reaction
|
|
126
|
+
wa send 919634847671 --to 911234567890@s.whatsapp.net --reaction 👍 --msg-id 3EB0ABCDEF123456
|
|
127
|
+
|
|
128
|
+
# Group message
|
|
129
|
+
wa send 919634847671 --to 120363000000000000@g.us --text "Hi everyone!"
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
### Listen for incoming messages
|
|
133
|
+
|
|
134
|
+
```bash
|
|
135
|
+
wa listen 919634847671
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### Print version
|
|
139
|
+
|
|
140
|
+
```bash
|
|
141
|
+
wa version
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### Full options reference
|
|
145
|
+
|
|
146
|
+
| Flag | Description |
|
|
147
|
+
|---|---|
|
|
148
|
+
| `--session <dir>` | Session directory (default: `~/.waSession`) |
|
|
149
|
+
| `--method sms\|voice\|wa_old` | Verification code delivery method |
|
|
150
|
+
| `--code <code>` | Verification code |
|
|
151
|
+
| `--to <jid>` | Recipient JID (`phone@s.whatsapp.net` or `groupid@g.us`) |
|
|
152
|
+
| `--text <msg>` | Text message body |
|
|
153
|
+
| `--caption <text>` | Caption for media messages |
|
|
154
|
+
| `--reaction <emoji>` | Emoji to send as a reaction |
|
|
155
|
+
| `--msg-id <id>` | Message ID to react to (required with `--reaction`) |
|
|
156
|
+
|
|
157
|
+
---
|
|
158
|
+
|
|
159
|
+
## Library Usage
|
|
160
|
+
|
|
161
|
+
### Connect and send messages
|
|
162
|
+
|
|
163
|
+
```javascript
|
|
164
|
+
const { WhalibmobClient } = require('whalibmob');
|
|
165
|
+
|
|
166
|
+
const client = new WhalibmobClient({ sessionDir: '/path/to/sessions' });
|
|
167
|
+
|
|
168
|
+
client.on('connected', async () => {
|
|
169
|
+
// Text
|
|
170
|
+
await client.sendText('919876543210@s.whatsapp.net', 'Hello!');
|
|
171
|
+
|
|
172
|
+
// Image
|
|
173
|
+
await client.sendImage('919876543210@s.whatsapp.net', '/path/to/photo.jpg', {
|
|
174
|
+
caption: 'My photo'
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
// Image from Buffer
|
|
178
|
+
const buf = require('fs').readFileSync('/path/to/photo.jpg');
|
|
179
|
+
await client.sendImage('919876543210@s.whatsapp.net', buf, { caption: 'From buffer' });
|
|
180
|
+
|
|
181
|
+
// Voice note
|
|
182
|
+
await client.sendAudio('919876543210@s.whatsapp.net', '/path/to/voice.ogg', {
|
|
183
|
+
ptt: true,
|
|
184
|
+
seconds: 12
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
// Document
|
|
188
|
+
await client.sendDocument('919876543210@s.whatsapp.net', '/path/to/file.pdf', {
|
|
189
|
+
fileName: 'report.pdf',
|
|
190
|
+
mimetype: 'application/pdf'
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
// Reaction
|
|
194
|
+
await client.sendReaction('919876543210@s.whatsapp.net', '3EB0ABC123', '👍');
|
|
195
|
+
|
|
196
|
+
// Group message
|
|
197
|
+
await client.sendText('120363000000000000@g.us', 'Hi group!');
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
client.on('message', (msg) => {
|
|
201
|
+
console.log('From:', msg.from, '|', msg.text);
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
client.on('auth_failure', ({ reason }) => {
|
|
205
|
+
console.error('Session revoked by WhatsApp:', reason);
|
|
206
|
+
// reason values: '401', '403', 'not_authorized', 'device_removed', etc.
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
client.on('close', () => console.log('Disconnected'));
|
|
210
|
+
client.on('error', (err) => console.error(err));
|
|
211
|
+
|
|
212
|
+
await client.init('919634847671');
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
### Registration (programmatic)
|
|
216
|
+
|
|
217
|
+
```javascript
|
|
218
|
+
const {
|
|
219
|
+
createNewStore,
|
|
220
|
+
saveStore,
|
|
221
|
+
loadStore,
|
|
222
|
+
checkIfRegistered,
|
|
223
|
+
requestSmsCode,
|
|
224
|
+
verifyCode
|
|
225
|
+
} = require('whalibmob');
|
|
226
|
+
|
|
227
|
+
const storePath = '/path/to/sessions/919634847671.json';
|
|
228
|
+
|
|
229
|
+
// Create or load a session store
|
|
230
|
+
let store = loadStore(storePath) || createNewStore('919634847671');
|
|
231
|
+
|
|
232
|
+
// Check if already registered
|
|
233
|
+
const status = await checkIfRegistered(store);
|
|
234
|
+
console.log(status.status); // "ok" if registered
|
|
235
|
+
|
|
236
|
+
// Request verification code — methods: "sms", "voice", "wa_old"
|
|
237
|
+
const res = await requestSmsCode(store, 'sms');
|
|
238
|
+
console.log(res.status); // "sent" on success
|
|
239
|
+
|
|
240
|
+
// Verify the received code and save session
|
|
241
|
+
const reg = await verifyCode(store, '123456');
|
|
242
|
+
if (reg.status === 'ok') {
|
|
243
|
+
saveStore(store, storePath);
|
|
244
|
+
console.log('Registered!');
|
|
245
|
+
}
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
---
|
|
249
|
+
|
|
250
|
+
## Events
|
|
251
|
+
|
|
252
|
+
| Event | Payload | Description |
|
|
253
|
+
|---|---|---|
|
|
254
|
+
| `connected` | — | Server confirmed `<success>`, session is live |
|
|
255
|
+
| `close` | — | Connection closed |
|
|
256
|
+
| `disconnected` | — | TCP connection dropped (reconnect scheduled) |
|
|
257
|
+
| `reconnecting` | `{ delay, attempt }` | About to reconnect after backoff delay |
|
|
258
|
+
| `reconnected` | — | Reconnection succeeded |
|
|
259
|
+
| `auth_failure` | `{ reason, node }` | Server sent `<failure>` — session revoked or account banned |
|
|
260
|
+
| `error` | `Error` | Non-fatal error occurred |
|
|
261
|
+
| `message` | `{ id, from, participant, ts, text?, plaintext?, isGroup? }` | Incoming message (plaintext is a Buffer) |
|
|
262
|
+
| `receipt` | `{ id, from, type }` | Delivery or read receipt |
|
|
263
|
+
| `presence` | `{ from, available }` | Contact came online / went offline |
|
|
264
|
+
| `call` | `{ from, node }` | Incoming call (auto-rejected by the library) |
|
|
265
|
+
| `notification` | `{ type, attrs, node }` | Raw server notification |
|
|
266
|
+
| `decrypt_error` | `{ id, from, err }` | Decryption failed; retry request sent automatically |
|
|
267
|
+
| `media_conn` | `{ hosts, auth }` | Media upload server connection ready |
|
|
268
|
+
| `session_refresh` | `{ node }` | Server sent a late `<success>` (re-auth flow) |
|
|
269
|
+
| `stream_error` | `{ reason }` | Server-side stream error |
|
|
270
|
+
|
|
271
|
+
---
|
|
272
|
+
|
|
273
|
+
## Session Files
|
|
274
|
+
|
|
275
|
+
Sessions are stored as JSON in the session directory (default `~/.waSession`):
|
|
276
|
+
|
|
277
|
+
```
|
|
278
|
+
~/.waSession/919634847671.json # WhatsApp credentials and keys
|
|
279
|
+
~/.waSession/919634847671.signal.json # Signal Protocol session state
|
|
280
|
+
~/.waSession/919634847671.sk.json # SenderKey group state + SKDM map
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
Once registered, sessions persist across restarts. Re-registration is only
|
|
284
|
+
required if WhatsApp revokes the session (you will receive an `auth_failure`
|
|
285
|
+
event with reason `401` or `not_authorized`).
|
|
286
|
+
|
|
287
|
+
---
|
|
288
|
+
|
|
289
|
+
## Signal Protocol
|
|
290
|
+
|
|
291
|
+
End-to-end encryption follows the Signal Protocol spec:
|
|
292
|
+
|
|
293
|
+
1. On first connection, pre-keys are generated and uploaded to WhatsApp.
|
|
294
|
+
2. When contacting a new recipient, the library fetches their pre-key bundle
|
|
295
|
+
and runs X3DH to establish a session.
|
|
296
|
+
3. All subsequent messages use the Double Ratchet algorithm.
|
|
297
|
+
4. Pre-keys are automatically replenished when supply drops below 20.
|
|
298
|
+
5. Session state is persisted to disk between restarts.
|
|
299
|
+
|
|
300
|
+
---
|
|
301
|
+
|
|
302
|
+
## Group Messaging (SenderKey)
|
|
303
|
+
|
|
304
|
+
Group messages use the SenderKey protocol for efficient delivery:
|
|
305
|
+
|
|
306
|
+
1. Each sender generates a SenderKey (signing key pair + chain key).
|
|
307
|
+
2. On first group message, a SenderKey Distribution Message (SKDM) is
|
|
308
|
+
encrypted individually for every device of every group member and sent.
|
|
309
|
+
3. Subsequent messages are encrypted once with the SenderKey and broadcast
|
|
310
|
+
— no per-member re-encryption needed.
|
|
311
|
+
4. The library tracks which devices have already received the SKDM (persisted
|
|
312
|
+
in `.sk.json`) and skips them on subsequent messages.
|
|
313
|
+
5. The chain key advances with each message (HMAC-SHA256).
|
|
314
|
+
6. Out-of-order message decryption is supported via cached message keys.
|
|
315
|
+
|
|
316
|
+
---
|
|
317
|
+
|
|
318
|
+
## Multi-Device
|
|
319
|
+
|
|
320
|
+
The library fully supports WhatsApp's multi-device architecture:
|
|
321
|
+
|
|
322
|
+
- Device lists are fetched via usync queries.
|
|
323
|
+
- Signal sessions are established for each linked device.
|
|
324
|
+
- All outgoing DMs are fanned out to every device of every recipient as well
|
|
325
|
+
as the sender's own linked devices (tablet, secondary phone, etc.).
|
|
326
|
+
- Own linked devices receive a `deviceSentMessage` wrapper so they display
|
|
327
|
+
the message as sent (not received).
|
|
328
|
+
- All multi-device stanzas include a `phash` (SHA-256 participant hash) so
|
|
329
|
+
the server can validate the participant list.
|
|
330
|
+
- SKDM distribution in groups covers all member devices.
|
|
331
|
+
|
|
332
|
+
---
|
|
333
|
+
|
|
334
|
+
## Noise Handshake & Auth
|
|
335
|
+
|
|
336
|
+
The transport uses **Noise_XX_25519_AESGCM_SHA256**:
|
|
337
|
+
|
|
338
|
+
1. TCP connection to `g.whatsapp.net:443`.
|
|
339
|
+
2. Client sends `ClientHello` with ephemeral X25519 public key.
|
|
340
|
+
3. Server replies with `ServerHello` (ephemeral + encrypted static + payload).
|
|
341
|
+
4. Three Diffie-Hellman steps (EE, SE, SS) derive the session keys.
|
|
342
|
+
5. Client sends `ClientFinish` (encrypted static + encrypted payload).
|
|
343
|
+
6. **Library waits for the server's `<success>` or `<failure>` node.**
|
|
344
|
+
7. Only after `<success>` is the channel opened and the `connected` event
|
|
345
|
+
emitted. A `<failure>` rejects with an `auth_failure` event.
|
|
346
|
+
|
|
347
|
+
---
|
|
348
|
+
|
|
349
|
+
## Automatic Reconnection
|
|
350
|
+
|
|
351
|
+
The client reconnects on disconnect with exponential backoff:
|
|
352
|
+
|
|
353
|
+
| Attempt | Delay |
|
|
354
|
+
|---|---|
|
|
355
|
+
| 1 | 1 s |
|
|
356
|
+
| 2 | 2 s |
|
|
357
|
+
| 3 | 4 s |
|
|
358
|
+
| 4 | 8 s |
|
|
359
|
+
| 5 | 15 s |
|
|
360
|
+
| 6+ | 30 s |
|
|
361
|
+
|
|
362
|
+
---
|
|
363
|
+
|
|
364
|
+
## Media Encryption
|
|
365
|
+
|
|
366
|
+
Media files use WhatsApp's standard encryption scheme:
|
|
367
|
+
|
|
368
|
+
1. A random 32-byte media key is generated.
|
|
369
|
+
2. HKDF-SHA256 expands it to produce the IV, cipher key, and MAC key.
|
|
370
|
+
3. The file is encrypted with AES-256-CBC.
|
|
371
|
+
4. A 10-byte HMAC-SHA256 MAC is appended.
|
|
372
|
+
5. The encrypted ciphertext is uploaded to WhatsApp's CDN.
|
|
373
|
+
6. The media key and URL are sent in the message envelope.
|
|
374
|
+
|
|
375
|
+
---
|
|
376
|
+
|
|
377
|
+
## License
|
|
378
|
+
|
|
379
|
+
MIT
|