whalibmob 5.1.13 → 5.1.15
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/.env.example +49 -0
- package/LICENSE +21 -0
- package/README.md +238 -5
- package/cli.js +38 -22
- package/index.js +20 -6
- package/lib/Client.js +159 -114
- package/lib/DeviceConfig.js +81 -0
- package/lib/DeviceManager.js +3 -3
- package/lib/MediaService.js +2 -1
- package/lib/Registration.js +84 -23
- package/lib/Store.js +9 -5
- package/lib/constants.js +172 -28
- package/lib/messages/MessageSender.js +37 -20
- package/lib/noise.js +7 -7
- package/lib/proto/MessageProto.js +5 -2
- package/lib/signal/SenderKey.js +119 -70
- package/lib/signal/SignalProtocol.js +12 -7
- package/lib/signal/SignalStore.js +1 -1
- package/package.json +6 -3
package/.env.example
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# ─────────────────────────────────────────────────────────────────────────────
|
|
2
|
+
# whalibmob — Device Emulation Configuration
|
|
3
|
+
# Copy this file to .env in your project root and set the values you need.
|
|
4
|
+
# All variables are optional; defaults emulate an iPhone 15 Pro running iOS 17.
|
|
5
|
+
# ─────────────────────────────────────────────────────────────────────────────
|
|
6
|
+
|
|
7
|
+
# Operating system to emulate. Accepted values: ios | android
|
|
8
|
+
# Default: ios
|
|
9
|
+
# WA_OS=android
|
|
10
|
+
|
|
11
|
+
# Named device profile.
|
|
12
|
+
#
|
|
13
|
+
# iOS profiles (WA_OS=ios):
|
|
14
|
+
# iphone_15_pro, iphone_15, iphone_14_pro, iphone_14, iphone_13_pro,
|
|
15
|
+
# iphone_13, iphone_12_pro, iphone_12, iphone_11_pro, iphone_11,
|
|
16
|
+
# iphone_se3, iphone_xs
|
|
17
|
+
#
|
|
18
|
+
# Android profiles (WA_OS=android):
|
|
19
|
+
# pixel_8_pro, pixel_8, pixel_7, pixel_7a,
|
|
20
|
+
# samsung_s24_ultra, samsung_s24, samsung_s23_ultra, samsung_s23, samsung_a55,
|
|
21
|
+
# oneplus_12, oneplus_11, xiaomi_14, xiaomi_13, oppo_find_x7, realme_gt5
|
|
22
|
+
#
|
|
23
|
+
# Default: iphone_15_pro (or pixel_8_pro when WA_OS=android)
|
|
24
|
+
# WA_DEVICE=pixel_8_pro
|
|
25
|
+
|
|
26
|
+
# ── Custom device overrides (applied on top of the selected profile) ─────────
|
|
27
|
+
# Use these to fine-tune any field without creating a new profile.
|
|
28
|
+
|
|
29
|
+
# WA_DEVICE_MODEL=SM-S928B
|
|
30
|
+
# WA_DEVICE_MANUFACTURER=samsung
|
|
31
|
+
# WA_DEVICE_OS_VERSION=14
|
|
32
|
+
# WA_DEVICE_BUILD=UP1A.231005.007
|
|
33
|
+
# WA_DEVICE_MODEL_ID=samsung-sm-s928b
|
|
34
|
+
|
|
35
|
+
# ── Version & token overrides ─────────────────────────────────────────────────
|
|
36
|
+
|
|
37
|
+
# Pin the WhatsApp version string instead of fetching the latest from the store.
|
|
38
|
+
# Format: 2.x.x.x (four-part)
|
|
39
|
+
# WA_VERSION=2.24.13.80
|
|
40
|
+
|
|
41
|
+
# Override the static token used in registration token computation.
|
|
42
|
+
# Only needed if WhatsApp rotates the bundled token.
|
|
43
|
+
# WA_STATIC_TOKEN=Y29Cs6AVNR2bj5PBeKSYFd1nAKuvNQ3h
|
|
44
|
+
|
|
45
|
+
# ── Proxy / Tor ───────────────────────────────────────────────────────────────
|
|
46
|
+
|
|
47
|
+
# Route registration HTTP traffic through a SOCKS5 proxy or Tor.
|
|
48
|
+
# TOR_PROXY=socks5://127.0.0.1:9050
|
|
49
|
+
# SOCKS_PROXY=socks5://user:pass@proxy.example.com:1080
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 Kunboruto20
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<div align='center'>whalibmob is a pure JavaScript Node.js library for interacting with the WhatsApp Mobile API.</div>
|
|
2
|
-
<div align='center'>v5.
|
|
2
|
+
<div align='center'>v5.1.14</div>
|
|
3
3
|
|
|
4
4
|
##
|
|
5
5
|
|
|
@@ -41,6 +41,7 @@ npm install -g whalibmob
|
|
|
41
41
|
- [Send Voice Note](#send-voice-note)
|
|
42
42
|
- [Send Document](#send-document)
|
|
43
43
|
- [Send Sticker](#send-sticker)
|
|
44
|
+
- [Send Poll](#send-poll-cli)
|
|
44
45
|
- [React to a Message](#react-to-a-message)
|
|
45
46
|
- [Edit a Message](#edit-a-message)
|
|
46
47
|
- [Delete a Message](#delete-a-message)
|
|
@@ -90,6 +91,9 @@ npm install -g whalibmob
|
|
|
90
91
|
- [Pending Join Requests](#pending-join-requests)
|
|
91
92
|
- [Approve / Reject Join Requests](#approve--reject-join-requests)
|
|
92
93
|
- [Group Settings](#group-settings)
|
|
94
|
+
- [Community Commands](#community-commands-cli)
|
|
95
|
+
- [Newsletter / Channel Commands](#newsletter-channel-commands)
|
|
96
|
+
- [Business Profile Command](#business-profile-command-cli)
|
|
93
97
|
- [Registration Commands (in-shell)](#registration-commands-in-shell)
|
|
94
98
|
- [Connection Commands (in-shell)](#connection-commands-in-shell)
|
|
95
99
|
- [Full Command Reference Table](#full-command-reference-table)
|
|
@@ -124,6 +128,7 @@ npm install -g whalibmob
|
|
|
124
128
|
- [Status / Stories](#status--stories)
|
|
125
129
|
- [Send States in Chat](#send-states-in-chat)
|
|
126
130
|
- [Reading Messages](#reading-messages)
|
|
131
|
+
- [Mark Voice Message Played](#mark-voice-message-played)
|
|
127
132
|
- [Update Presence](#update-presence)
|
|
128
133
|
- [Modifying Chats](#modifying-chats)
|
|
129
134
|
- [Archive / Unarchive a Chat](#archive--unarchive-a-chat)
|
|
@@ -177,6 +182,12 @@ npm install -g whalibmob
|
|
|
177
182
|
- [WhatsApp IDs](#whatsapp-ids)
|
|
178
183
|
- [Transport](#transport)
|
|
179
184
|
- [Media Encryption](#media-encryption)
|
|
185
|
+
- [Device Emulation](#device-emulation)
|
|
186
|
+
- [Quick Start](#device-quick-start)
|
|
187
|
+
- [iOS Profiles](#ios-profiles)
|
|
188
|
+
- [Android Profiles](#android-profiles)
|
|
189
|
+
- [Custom Device Fields](#custom-device-fields)
|
|
190
|
+
- [Version & Token Overrides](#version--token-overrides)
|
|
180
191
|
|
|
181
192
|
---
|
|
182
193
|
|
|
@@ -308,10 +319,19 @@ connect()
|
|
|
308
319
|
| `auth_failure` | `{ reason }` | Session revoked or banned |
|
|
309
320
|
| `message` | message object | Incoming message received |
|
|
310
321
|
| `receipt` | `{ type, id, from }` | Delivery / read / played receipt |
|
|
311
|
-
| `presence` | `{ from, available
|
|
322
|
+
| `presence` | `{ from, available }` | Contact came online or went offline |
|
|
312
323
|
| `group_update` | `{ type, groupJid, actor, participants, subject, timestamp }` | Member added / removed / promoted / demoted, subject or settings changed |
|
|
313
324
|
| `notification` | node object | Group or contact update notification |
|
|
314
325
|
| `call` | `{ from }` | Incoming call event |
|
|
326
|
+
| `chat_read` | `{ jid, read }` | Chat marked read (`read: true`) or unread (`read: false`) |
|
|
327
|
+
| `chat_muted` | `{ jid, muted, until }` | Chat muted or unmuted; `until` is epoch ms (−1 = indefinite) |
|
|
328
|
+
| `chat_pinned` | `{ jid, pinned }` | Chat pinned or unpinned |
|
|
329
|
+
| `chat_archived` | `{ jid, archived }` | Chat archived or unarchived |
|
|
330
|
+
| `message_starred` | `{ msgId, chatJid, starred }` | Message starred or unstarred |
|
|
331
|
+
| `stream_error` | `{ reason }` | Server sent a fatal stream error |
|
|
332
|
+
| `decrypt_error` | `{ id, from, participant, err }` | Failed to decrypt an incoming message |
|
|
333
|
+
| `session_refresh` | `{ node }` | Late re-authentication success; Signal session refreshed |
|
|
334
|
+
| `close` | — | Underlying TCP socket closed |
|
|
315
335
|
| `error` | Error | Unhandled transport error |
|
|
316
336
|
|
|
317
337
|
The message object contains:
|
|
@@ -429,7 +449,7 @@ function decryptMedia(encrypted, mediaKey, mediaType) {
|
|
|
429
449
|
function downloadBuffer(url) {
|
|
430
450
|
return new Promise((resolve, reject) => {
|
|
431
451
|
const lib = url.startsWith('https') ? https : http
|
|
432
|
-
const req = lib.get(url, { headers: { 'User-Agent': 'WhatsApp/2.
|
|
452
|
+
const req = lib.get(url, { headers: { 'User-Agent': 'WhatsApp/2.26.7.75 A' } }, res => {
|
|
433
453
|
if (res.statusCode !== 200) { res.resume(); return reject(new Error('HTTP ' + res.statusCode)) }
|
|
434
454
|
const chunks = []
|
|
435
455
|
res.on('data', c => chunks.push(c))
|
|
@@ -711,6 +731,15 @@ await client.sendStatus('Good morning!')
|
|
|
711
731
|
await client.markChatRead('919634847671@s.whatsapp.net')
|
|
712
732
|
```
|
|
713
733
|
|
|
734
|
+
### Mark Voice Message Played
|
|
735
|
+
|
|
736
|
+
Send a `played` receipt for a received voice note (push-to-talk audio). This tells the sender that you have listened to the message.
|
|
737
|
+
|
|
738
|
+
```js
|
|
739
|
+
// msgId: ID of the audio message, from: JID of the sender
|
|
740
|
+
client.markMessagePlayed('3EB0ABCDEF123456', '919634847671@s.whatsapp.net')
|
|
741
|
+
```
|
|
742
|
+
|
|
714
743
|
### Update Presence
|
|
715
744
|
|
|
716
745
|
```js
|
|
@@ -820,7 +849,7 @@ const groupUrl = await client.queryPicture('120363000000000000@g.us')
|
|
|
820
849
|
// triggers 'presence' events when the contact comes online or goes offline
|
|
821
850
|
client.subscribeToPresence('919634847671@s.whatsapp.net')
|
|
822
851
|
|
|
823
|
-
client.on('presence', ({ from, available
|
|
852
|
+
client.on('presence', ({ from, available }) => {
|
|
824
853
|
console.log(from, available ? 'online' : 'offline')
|
|
825
854
|
})
|
|
826
855
|
```
|
|
@@ -1330,6 +1359,18 @@ The file must be in WebP format:
|
|
|
1330
1359
|
wa> /sticker 919634847671@s.whatsapp.net ./sticker.webp
|
|
1331
1360
|
```
|
|
1332
1361
|
|
|
1362
|
+
#### Send Poll (CLI)
|
|
1363
|
+
|
|
1364
|
+
Separate the question from the options using `|`. At least two options are required. Optionally append `selectable=N` to limit how many options a voter may choose (0 = any):
|
|
1365
|
+
|
|
1366
|
+
```sh
|
|
1367
|
+
# single-choice poll (selectable=1)
|
|
1368
|
+
wa> /poll 919634847671@s.whatsapp.net Best language? | JavaScript | Python | Rust | selectable=1
|
|
1369
|
+
|
|
1370
|
+
# unlimited-choice poll (default)
|
|
1371
|
+
wa> /poll 120363000000000000@g.us Pick your favourites | Red | Green | Blue
|
|
1372
|
+
```
|
|
1373
|
+
|
|
1333
1374
|
#### React to a Message
|
|
1334
1375
|
|
|
1335
1376
|
```sh
|
|
@@ -1849,6 +1890,78 @@ wa> /group settings 120363000000000000@g.us approve_participants admins
|
|
|
1849
1890
|
|
|
1850
1891
|
---
|
|
1851
1892
|
|
|
1893
|
+
### Community Commands (CLI)
|
|
1894
|
+
|
|
1895
|
+
Communities group multiple linked groups under one umbrella. Only the community creator can link / unlink groups or deactivate the community.
|
|
1896
|
+
|
|
1897
|
+
```sh
|
|
1898
|
+
# create a community (description is optional)
|
|
1899
|
+
wa> /community create "Dev Squad" "Our developer community"
|
|
1900
|
+
|
|
1901
|
+
# link an existing group into the community
|
|
1902
|
+
wa> /community link 120363000000000001@g.us 120363000000000002@g.us
|
|
1903
|
+
|
|
1904
|
+
# unlink a group from the community
|
|
1905
|
+
wa> /community unlink 120363000000000001@g.us 120363000000000002@g.us
|
|
1906
|
+
|
|
1907
|
+
# permanently deactivate (delete) a community
|
|
1908
|
+
wa> /community deactivate 120363000000000001@g.us
|
|
1909
|
+
```
|
|
1910
|
+
|
|
1911
|
+
---
|
|
1912
|
+
|
|
1913
|
+
### Newsletter / Channel Commands
|
|
1914
|
+
|
|
1915
|
+
Newsletters are one-to-many broadcast channels. Only the owner can post; anyone can subscribe.
|
|
1916
|
+
|
|
1917
|
+
```sh
|
|
1918
|
+
# create a new channel
|
|
1919
|
+
wa> /newsletter create Tech News Daily tips about technology
|
|
1920
|
+
|
|
1921
|
+
# subscribe to a channel
|
|
1922
|
+
wa> /newsletter join 120363000000000004@newsletter
|
|
1923
|
+
|
|
1924
|
+
# unsubscribe from a channel
|
|
1925
|
+
wa> /newsletter leave 120363000000000004@newsletter
|
|
1926
|
+
|
|
1927
|
+
# query channel metadata (name, description, subscriber count)
|
|
1928
|
+
wa> /newsletter info 120363000000000004@newsletter
|
|
1929
|
+
────────────────────────────────────────────────────────
|
|
1930
|
+
jid 120363000000000004@newsletter
|
|
1931
|
+
name Tech News
|
|
1932
|
+
description Daily tips about technology
|
|
1933
|
+
subscribers 1234
|
|
1934
|
+
────────────────────────────────────────────────────────
|
|
1935
|
+
|
|
1936
|
+
# update the channel description (you must be the owner)
|
|
1937
|
+
wa> /newsletter desc 120363000000000004@newsletter New description here
|
|
1938
|
+
|
|
1939
|
+
# post a text update to your channel (you must be the owner)
|
|
1940
|
+
wa> /newsletter post 120363000000000004@newsletter Breaking: WhatsApp adds polls!
|
|
1941
|
+
```
|
|
1942
|
+
|
|
1943
|
+
---
|
|
1944
|
+
|
|
1945
|
+
### Business Profile Command (CLI)
|
|
1946
|
+
|
|
1947
|
+
Query the public business profile of any WhatsApp Business account:
|
|
1948
|
+
|
|
1949
|
+
```sh
|
|
1950
|
+
wa> /biz 919634847671@s.whatsapp.net
|
|
1951
|
+
────────────────────────────────────────────────────────
|
|
1952
|
+
jid 919634847671@s.whatsapp.net
|
|
1953
|
+
category Software & IT Services
|
|
1954
|
+
email contact@example.com
|
|
1955
|
+
website https://example.com
|
|
1956
|
+
address 123 Main St
|
|
1957
|
+
description We build software
|
|
1958
|
+
────────────────────────────────────────────────────────
|
|
1959
|
+
```
|
|
1960
|
+
|
|
1961
|
+
Returns a message if the number is not a WhatsApp Business account.
|
|
1962
|
+
|
|
1963
|
+
---
|
|
1964
|
+
|
|
1852
1965
|
### Registration Commands (in-shell)
|
|
1853
1966
|
|
|
1854
1967
|
These commands work from within the shell — useful when you need to register a second number without closing the current session:
|
|
@@ -1909,6 +2022,7 @@ wa> /quit
|
|
|
1909
2022
|
| `/ptt <jid> <file>` | Send a voice note (push-to-talk) |
|
|
1910
2023
|
| `/doc <jid> <file> [name]` | Send a document |
|
|
1911
2024
|
| `/sticker <jid> <file>` | Send a sticker (.webp) |
|
|
2025
|
+
| `/poll <jid> <question> \| <opt1> \| <opt2> [selectable=N]` | Send a poll |
|
|
1912
2026
|
| `/react <jid> <msgId> <emoji>` | React to a message |
|
|
1913
2027
|
| `/edit <jid> <msgId> <text>` | Edit a sent message |
|
|
1914
2028
|
| `/delete <jid> <msgId> [all]` | Delete a message (add `all` for everyone) |
|
|
@@ -1970,6 +2084,20 @@ wa> /quit
|
|
|
1970
2084
|
| `/group approve <jid> <member...>` | Approve pending join requests |
|
|
1971
2085
|
| `/group reject <jid> <member...>` | Reject pending join requests |
|
|
1972
2086
|
| `/group settings <jid> <setting> <policy>` | Change group setting |
|
|
2087
|
+
| **Community** | |
|
|
2088
|
+
| `/community create <subject> [description]` | Create a community |
|
|
2089
|
+
| `/community deactivate <communityJid>` | Permanently delete a community |
|
|
2090
|
+
| `/community link <communityJid> <groupJid>` | Link a group into a community |
|
|
2091
|
+
| `/community unlink <communityJid> <groupJid>` | Unlink a group from a community |
|
|
2092
|
+
| **Newsletter / Channel** | |
|
|
2093
|
+
| `/newsletter create <name> [description]` | Create a newsletter channel |
|
|
2094
|
+
| `/newsletter join <jid>` | Subscribe to a channel |
|
|
2095
|
+
| `/newsletter leave <jid>` | Unsubscribe from a channel |
|
|
2096
|
+
| `/newsletter info <jid>` | Query channel metadata |
|
|
2097
|
+
| `/newsletter desc <jid> <text>` | Update channel description |
|
|
2098
|
+
| `/newsletter post <jid> <text>` | Post a text update to your channel |
|
|
2099
|
+
| **Business** | |
|
|
2100
|
+
| `/biz <phone\|jid>` | Query business profile of a WhatsApp Business account |
|
|
1973
2101
|
| **Registration** | |
|
|
1974
2102
|
| `/reg check <phone>` | Check if number has WhatsApp |
|
|
1975
2103
|
| `/reg code <phone> [method]` | Request verification code |
|
|
@@ -1980,7 +2108,7 @@ wa> /quit
|
|
|
1980
2108
|
| `/reconnect` | Force reconnection |
|
|
1981
2109
|
| `/session` | Show session info |
|
|
1982
2110
|
| `/help` | Show all commands |
|
|
1983
|
-
| `/quit` | Disconnect and exit |
|
|
2111
|
+
| `/quit` / `/exit` | Disconnect and exit |
|
|
1984
2112
|
|
|
1985
2113
|
---
|
|
1986
2114
|
|
|
@@ -2056,6 +2184,111 @@ HKDF info strings:
|
|
|
2056
2184
|
|
|
2057
2185
|
See the [Receiving Media](#receiving-media) section for a complete working code example.
|
|
2058
2186
|
|
|
2187
|
+
## Device Emulation
|
|
2188
|
+
|
|
2189
|
+
whalibmob can emulate any iOS or Android device when communicating with WhatsApp servers.
|
|
2190
|
+
The device profile controls the User-Agent header, the Noise Protocol `platform` field, and the static token used in registration token computation.
|
|
2191
|
+
|
|
2192
|
+
Configuration is done entirely through environment variables — no code changes required.
|
|
2193
|
+
Copy `.env.example` to `.env` in your project root and set the variables you need.
|
|
2194
|
+
|
|
2195
|
+
### Device Quick Start
|
|
2196
|
+
|
|
2197
|
+
Emulate an Android Pixel 8 Pro:
|
|
2198
|
+
|
|
2199
|
+
```sh
|
|
2200
|
+
WA_OS=android WA_DEVICE=pixel_8_pro node your-app.js
|
|
2201
|
+
```
|
|
2202
|
+
|
|
2203
|
+
Or put the variables in a `.env` file. When using the **CLI** (`wa` command) the file is loaded automatically. When using the **library directly**, load it before `require('whalibmob')`:
|
|
2204
|
+
|
|
2205
|
+
```js
|
|
2206
|
+
require('dotenv').config() // must be first
|
|
2207
|
+
const { WhalibmobClient } = require('whalibmob')
|
|
2208
|
+
```
|
|
2209
|
+
|
|
2210
|
+
```dotenv
|
|
2211
|
+
WA_OS=android
|
|
2212
|
+
WA_DEVICE=pixel_8_pro
|
|
2213
|
+
```
|
|
2214
|
+
|
|
2215
|
+
Emulate a custom Samsung device:
|
|
2216
|
+
|
|
2217
|
+
```dotenv
|
|
2218
|
+
WA_OS=android
|
|
2219
|
+
WA_DEVICE_MODEL=SM-S928B
|
|
2220
|
+
WA_DEVICE_MANUFACTURER=samsung
|
|
2221
|
+
WA_DEVICE_OS_VERSION=14
|
|
2222
|
+
WA_DEVICE_BUILD=UP1A.231005.007
|
|
2223
|
+
WA_DEVICE_MODEL_ID=samsung-sm-s928b
|
|
2224
|
+
```
|
|
2225
|
+
|
|
2226
|
+
### iOS Profiles
|
|
2227
|
+
|
|
2228
|
+
Available values for `WA_DEVICE` when `WA_OS=ios` (default):
|
|
2229
|
+
|
|
2230
|
+
| Profile key | Device | iOS version |
|
|
2231
|
+
|---|---|---|
|
|
2232
|
+
| `iphone_15_pro` | iPhone 15 Pro | 17.4.1 |
|
|
2233
|
+
| `iphone_15` | iPhone 15 | 17.4.1 |
|
|
2234
|
+
| `iphone_14_pro` | iPhone 14 Pro | 16.7.5 |
|
|
2235
|
+
| `iphone_14` | iPhone 14 | 16.7.5 |
|
|
2236
|
+
| `iphone_13_pro` | iPhone 13 Pro | 16.7.5 |
|
|
2237
|
+
| `iphone_13` | iPhone 13 | 16.7.5 |
|
|
2238
|
+
| `iphone_12_pro` | iPhone 12 Pro | 15.8.2 |
|
|
2239
|
+
| `iphone_12` | iPhone 12 | 15.8.2 |
|
|
2240
|
+
| `iphone_11_pro` | iPhone 11 Pro | 15.8.2 |
|
|
2241
|
+
| `iphone_11` | iPhone 11 | 15.8.2 |
|
|
2242
|
+
| `iphone_se3` | iPhone SE (3rd gen) | 16.7.5 |
|
|
2243
|
+
| `iphone_xs` | iPhone Xs | 15.8.2 |
|
|
2244
|
+
|
|
2245
|
+
iOS User-Agent format: `WhatsApp/<version> iOS/<osVersion> Device/<model>`
|
|
2246
|
+
|
|
2247
|
+
### Android Profiles
|
|
2248
|
+
|
|
2249
|
+
Available values for `WA_DEVICE` when `WA_OS=android`:
|
|
2250
|
+
|
|
2251
|
+
| Profile key | Device | Android version |
|
|
2252
|
+
|---|---|---|
|
|
2253
|
+
| `pixel_8_pro` | Pixel 8 Pro | 14 |
|
|
2254
|
+
| `pixel_8` | Pixel 8 | 14 |
|
|
2255
|
+
| `pixel_7` | Pixel 7 | 14 |
|
|
2256
|
+
| `pixel_7a` | Pixel 7a | 14 |
|
|
2257
|
+
| `samsung_s24_ultra` | Samsung Galaxy S24 Ultra | 14 |
|
|
2258
|
+
| `samsung_s24` | Samsung Galaxy S24 | 14 |
|
|
2259
|
+
| `samsung_s23_ultra` | Samsung Galaxy S23 Ultra | 14 |
|
|
2260
|
+
| `samsung_s23` | Samsung Galaxy S23 | 14 |
|
|
2261
|
+
| `samsung_a55` | Samsung Galaxy A55 | 14 |
|
|
2262
|
+
| `oneplus_12` | OnePlus 12 | 14 |
|
|
2263
|
+
| `oneplus_11` | OnePlus 11 | 13 |
|
|
2264
|
+
| `xiaomi_14` | Xiaomi 14 | 14 |
|
|
2265
|
+
| `xiaomi_13` | Xiaomi 13 | 13 |
|
|
2266
|
+
| `oppo_find_x7` | OPPO Find X7 | 14 |
|
|
2267
|
+
| `realme_gt5` | realme GT 5 Pro | 14 |
|
|
2268
|
+
|
|
2269
|
+
Android User-Agent format: `WhatsApp/<version> A`
|
|
2270
|
+
|
|
2271
|
+
The Android version is fetched automatically from the Google Play Store on first use and cached in memory. If the fetch fails, `ANDROID_VERSION_FALLBACK` is used.
|
|
2272
|
+
|
|
2273
|
+
### Custom Device Fields
|
|
2274
|
+
|
|
2275
|
+
These variables override individual fields on top of the selected profile:
|
|
2276
|
+
|
|
2277
|
+
| Variable | Description |
|
|
2278
|
+
|---|---|
|
|
2279
|
+
| `WA_DEVICE_MODEL` | Device model string (e.g. `SM-S928B`) |
|
|
2280
|
+
| `WA_DEVICE_MANUFACTURER` | Manufacturer name (e.g. `samsung`) |
|
|
2281
|
+
| `WA_DEVICE_OS_VERSION` | OS version string (e.g. `14`) |
|
|
2282
|
+
| `WA_DEVICE_BUILD` | Build fingerprint (e.g. `UP1A.231005.007`) |
|
|
2283
|
+
| `WA_DEVICE_MODEL_ID` | Model ID slug (e.g. `samsung-sm-s928b`) |
|
|
2284
|
+
|
|
2285
|
+
### Version & Token Overrides
|
|
2286
|
+
|
|
2287
|
+
| Variable | Description |
|
|
2288
|
+
|---|---|
|
|
2289
|
+
| `WA_VERSION` | Pin the WhatsApp version (e.g. `2.24.13.80`). Skips the live store fetch. |
|
|
2290
|
+
| `WA_STATIC_TOKEN` | Override the static token used in registration token computation. |
|
|
2291
|
+
|
|
2059
2292
|
## License
|
|
2060
2293
|
|
|
2061
2294
|
MIT
|
package/cli.js
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
|
+
// Load .env from the current working directory (silently — no error if missing).
|
|
5
|
+
// This lets users configure WA_OS, WA_DEVICE, WA_VERSION etc. without touching
|
|
6
|
+
// their shell environment. Must happen before any other require() so that
|
|
7
|
+
// process.env is fully populated when modules read it at load time.
|
|
8
|
+
try { require('dotenv').config(); } catch (_) {}
|
|
9
|
+
|
|
4
10
|
// Suppress internal [DBG] lines — keep console clean for users
|
|
5
11
|
const _origStderrWrite = process.stderr.write.bind(process.stderr);
|
|
6
12
|
process.stderr.write = function(chunk, enc, cb) {
|
|
@@ -22,13 +28,14 @@ const {
|
|
|
22
28
|
requestSmsCode,
|
|
23
29
|
verifyCode,
|
|
24
30
|
assertRegistrationKeys,
|
|
25
|
-
|
|
31
|
+
fetchWaVersion,
|
|
32
|
+
getDeviceConfig,
|
|
26
33
|
createNewStore,
|
|
27
34
|
saveStore,
|
|
28
35
|
loadStore
|
|
29
36
|
} = require('./lib/Client');
|
|
30
37
|
|
|
31
|
-
const VERSION = '5.1.
|
|
38
|
+
const VERSION = '5.1.15';
|
|
32
39
|
|
|
33
40
|
// ─── output helpers ───────────────────────────────────────────────────────────
|
|
34
41
|
|
|
@@ -481,8 +488,9 @@ async function handleLine(line) {
|
|
|
481
488
|
const jid = normalizeJid(jR);
|
|
482
489
|
if (!jid || !file) { fail('usage: /image <jid> <file> [caption]'); break; }
|
|
483
490
|
out('uploading...');
|
|
484
|
-
|
|
485
|
-
|
|
491
|
+
_client.sendImage(jid, file, { caption: cap.join(' ') || undefined })
|
|
492
|
+
.then(r => out('sent ' + (r && r.id ? r.id : r)))
|
|
493
|
+
.catch(e => fail('image error: ' + e.message));
|
|
486
494
|
break;
|
|
487
495
|
}
|
|
488
496
|
|
|
@@ -492,8 +500,9 @@ async function handleLine(line) {
|
|
|
492
500
|
const jid = normalizeJid(jR);
|
|
493
501
|
if (!jid || !file) { fail('usage: /video <jid> <file> [caption]'); break; }
|
|
494
502
|
out('uploading...');
|
|
495
|
-
|
|
496
|
-
|
|
503
|
+
_client.sendVideo(jid, file, { caption: cap.join(' ') || undefined })
|
|
504
|
+
.then(r => out('sent ' + (r && r.id ? r.id : r)))
|
|
505
|
+
.catch(e => fail('video error: ' + e.message));
|
|
497
506
|
break;
|
|
498
507
|
}
|
|
499
508
|
|
|
@@ -503,8 +512,9 @@ async function handleLine(line) {
|
|
|
503
512
|
const jid = normalizeJid(jR);
|
|
504
513
|
if (!jid || !file) { fail('usage: /audio <jid> <file>'); break; }
|
|
505
514
|
out('uploading...');
|
|
506
|
-
|
|
507
|
-
|
|
515
|
+
_client.sendAudio(jid, file, {})
|
|
516
|
+
.then(r => out('sent ' + (r && r.id ? r.id : r)))
|
|
517
|
+
.catch(e => fail('audio error: ' + e.message));
|
|
508
518
|
break;
|
|
509
519
|
}
|
|
510
520
|
|
|
@@ -514,8 +524,9 @@ async function handleLine(line) {
|
|
|
514
524
|
const jid = normalizeJid(jR);
|
|
515
525
|
if (!jid || !file) { fail('usage: /ptt <jid> <file>'); break; }
|
|
516
526
|
out('uploading...');
|
|
517
|
-
|
|
518
|
-
|
|
527
|
+
_client.sendAudio(jid, file, { ptt: true })
|
|
528
|
+
.then(r => out('sent ' + (r && r.id ? r.id : r)))
|
|
529
|
+
.catch(e => fail('ptt error: ' + e.message));
|
|
519
530
|
break;
|
|
520
531
|
}
|
|
521
532
|
|
|
@@ -525,8 +536,9 @@ async function handleLine(line) {
|
|
|
525
536
|
const jid = normalizeJid(jR);
|
|
526
537
|
if (!jid || !file) { fail('usage: /doc <jid> <file> [name]'); break; }
|
|
527
538
|
out('uploading...');
|
|
528
|
-
|
|
529
|
-
|
|
539
|
+
_client.sendDocument(jid, file, { fileName: fname || path.basename(file) })
|
|
540
|
+
.then(r => out('sent ' + (r && r.id ? r.id : r)))
|
|
541
|
+
.catch(e => fail('document error: ' + e.message));
|
|
530
542
|
break;
|
|
531
543
|
}
|
|
532
544
|
|
|
@@ -536,8 +548,9 @@ async function handleLine(line) {
|
|
|
536
548
|
const jid = normalizeJid(jR);
|
|
537
549
|
if (!jid || !file) { fail('usage: /sticker <jid> <file>'); break; }
|
|
538
550
|
out('uploading...');
|
|
539
|
-
|
|
540
|
-
|
|
551
|
+
_client.sendSticker(jid, file, {})
|
|
552
|
+
.then(r => out('sent ' + (r && r.id ? r.id : r)))
|
|
553
|
+
.catch(e => fail('sticker error: ' + e.message));
|
|
541
554
|
break;
|
|
542
555
|
}
|
|
543
556
|
|
|
@@ -546,8 +559,9 @@ async function handleLine(line) {
|
|
|
546
559
|
const [, jR, msgId, emoji] = p;
|
|
547
560
|
const jid = normalizeJid(jR);
|
|
548
561
|
if (!jid || !msgId || !emoji) { fail('usage: /react <jid> <msgId> <emoji>'); break; }
|
|
549
|
-
|
|
550
|
-
|
|
562
|
+
_client.sendReaction(jid, msgId, emoji)
|
|
563
|
+
.then(r => out('sent ' + (r && r.id ? r.id : r)))
|
|
564
|
+
.catch(e => fail('react error: ' + e.message));
|
|
551
565
|
break;
|
|
552
566
|
}
|
|
553
567
|
|
|
@@ -556,8 +570,9 @@ async function handleLine(line) {
|
|
|
556
570
|
const [, jR, msgId, ...rest] = p;
|
|
557
571
|
const jid = normalizeJid(jR);
|
|
558
572
|
if (!jid || !msgId || !rest.length) { fail('usage: /edit <jid> <msgId> <text>'); break; }
|
|
559
|
-
|
|
560
|
-
|
|
573
|
+
_client.editMessage(msgId, jid, rest.join(' '))
|
|
574
|
+
.then(r => out('edited ' + (r && r.id ? r.id : r)))
|
|
575
|
+
.catch(e => fail('edit error: ' + e.message));
|
|
561
576
|
break;
|
|
562
577
|
}
|
|
563
578
|
|
|
@@ -566,8 +581,9 @@ async function handleLine(line) {
|
|
|
566
581
|
const [, jR, msgId, scope] = p;
|
|
567
582
|
const jid = normalizeJid(jR);
|
|
568
583
|
if (!jid || !msgId) { fail('usage: /delete <jid> <msgId> [all]'); break; }
|
|
569
|
-
|
|
570
|
-
|
|
584
|
+
_client.deleteMessage(msgId, jid, true, scope === 'all')
|
|
585
|
+
.then(() => out('deleted ' + (scope === 'all' ? 'for everyone' : 'for me')))
|
|
586
|
+
.catch(e => fail('delete error: ' + e.message));
|
|
571
587
|
break;
|
|
572
588
|
}
|
|
573
589
|
|
|
@@ -1142,7 +1158,7 @@ async function handleLine(line) {
|
|
|
1142
1158
|
} else if (!store.codePending && !store.registered) {
|
|
1143
1159
|
// Only check /exist when keys were never used to request a code.
|
|
1144
1160
|
out('checking device keys...');
|
|
1145
|
-
const waVersion = await
|
|
1161
|
+
const waVersion = await fetchWaVersion(getDeviceConfig());
|
|
1146
1162
|
const fresh = await assertRegistrationKeys(store, waVersion);
|
|
1147
1163
|
if (!fresh) {
|
|
1148
1164
|
out(' device keys already registered — generating new keys...');
|
|
@@ -1468,7 +1484,7 @@ async function main() {
|
|
|
1468
1484
|
// keys are already taken (e.g. leftover from a previous failed attempt).
|
|
1469
1485
|
// FIX: only 1 /exist call now (was 2), and skipped entirely when codePending.
|
|
1470
1486
|
out('checking device keys...');
|
|
1471
|
-
const waVersion = await
|
|
1487
|
+
const waVersion = await fetchWaVersion(getDeviceConfig());
|
|
1472
1488
|
const fresh = await assertRegistrationKeys(store, waVersion);
|
|
1473
1489
|
if (!fresh) {
|
|
1474
1490
|
out(' device keys already registered — generating new keys...');
|
package/index.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const { WhalibmobClient, checkNumberStatus, fetchIosVersion, assertRegistrationKeys } = require('./lib/Client');
|
|
4
|
-
const {
|
|
3
|
+
const { WhalibmobClient, checkNumberStatus, fetchIosVersion, fetchWaVersion, assertRegistrationKeys } = require('./lib/Client');
|
|
4
|
+
const { getDeviceConfig } = require('./lib/DeviceConfig');
|
|
5
|
+
const { fetchAndroidVersion } = require('./lib/Registration');
|
|
6
|
+
const { createNewStore, saveStore, loadStore, toSixParts, fromSixParts, storeToJson, storeFromJson } = require('./lib/Store');
|
|
5
7
|
const { checkIfRegistered, requestSmsCode, verifyCode } = require('./lib/Registration');
|
|
6
8
|
const { SignalProtocol } = require('./lib/signal/SignalProtocol');
|
|
7
9
|
const { SignalStore } = require('./lib/signal/SignalStore');
|
|
@@ -13,25 +15,37 @@ const { MessageSender, makeJid, generateMessageId } = require('./lib/messages/Me
|
|
|
13
15
|
module.exports = {
|
|
14
16
|
WhalibmobClient,
|
|
15
17
|
checkNumberStatus,
|
|
16
|
-
|
|
18
|
+
checkIfRegistered,
|
|
19
|
+
requestSmsCode,
|
|
20
|
+
verifyCode,
|
|
17
21
|
assertRegistrationKeys,
|
|
22
|
+
// Version fetch — use fetchWaVersion for device-aware (iOS or Android) fetching.
|
|
23
|
+
// fetchIosVersion is kept for backward compatibility.
|
|
24
|
+
fetchWaVersion,
|
|
25
|
+
fetchIosVersion,
|
|
26
|
+
fetchAndroidVersion,
|
|
27
|
+
// Device config — reads WA_OS / WA_DEVICE / WA_DEVICE_* from process.env
|
|
28
|
+
getDeviceConfig,
|
|
29
|
+
// Store helpers
|
|
18
30
|
createNewStore,
|
|
19
31
|
saveStore,
|
|
20
32
|
loadStore,
|
|
33
|
+
storeToJson,
|
|
34
|
+
storeFromJson,
|
|
21
35
|
toSixParts,
|
|
22
36
|
fromSixParts,
|
|
23
|
-
|
|
24
|
-
requestSmsCode,
|
|
25
|
-
verifyCode,
|
|
37
|
+
// Signal / encryption internals
|
|
26
38
|
SignalProtocol,
|
|
27
39
|
SignalStore,
|
|
28
40
|
SenderKeyStore,
|
|
29
41
|
SenderKeyCrypto,
|
|
30
42
|
DeviceManager,
|
|
43
|
+
// Media
|
|
31
44
|
encryptMedia,
|
|
32
45
|
decryptMedia,
|
|
33
46
|
uploadMedia,
|
|
34
47
|
downloadMedia,
|
|
48
|
+
// Message helpers
|
|
35
49
|
MessageSender,
|
|
36
50
|
makeJid,
|
|
37
51
|
generateMessageId
|