whalibmob 5.1.16 → 5.1.17
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 +28 -33
- package/README.md +77 -76
- package/cli.js +2 -2
- package/index.js +2 -4
- package/lib/DeviceConfig.js +36 -51
- package/lib/MediaService.js +1 -1
- package/lib/Registration.js +97 -104
- package/lib/Store.js +2 -2
- package/lib/constants.js +101 -116
- package/lib/noise.js +8 -5
- package/package.json +1 -1
package/.env.example
CHANGED
|
@@ -1,49 +1,44 @@
|
|
|
1
1
|
# ─────────────────────────────────────────────────────────────────────────────
|
|
2
|
-
# whalibmob — Device Emulation Configuration
|
|
2
|
+
# whalibmob — iPhone Device Emulation Configuration
|
|
3
3
|
# Copy this file to .env in your project root and set the values you need.
|
|
4
|
-
# All variables are optional;
|
|
4
|
+
# All variables are optional; the library picks a random iPhone by default.
|
|
5
5
|
# ─────────────────────────────────────────────────────────────────────────────
|
|
6
6
|
|
|
7
|
-
#
|
|
8
|
-
#
|
|
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
|
|
7
|
+
# Named iPhone profile to emulate.
|
|
8
|
+
# If not set, the library picks a random iPhone from the list below.
|
|
17
9
|
#
|
|
18
|
-
#
|
|
19
|
-
#
|
|
20
|
-
#
|
|
21
|
-
#
|
|
10
|
+
# Available profiles:
|
|
11
|
+
# iphone16promax, iphone16pro, iphone16plus, iphone16,
|
|
12
|
+
# iphone15promax, iphone15pro, iphone15plus, iphone15,
|
|
13
|
+
# iphone14promax, iphone14pro, iphone14plus, iphone14,
|
|
14
|
+
# iphone13pro, iphone13, iphone12pro, iphone12,
|
|
15
|
+
# iphone11pro, iphone11, iphonese3, iphonexs
|
|
22
16
|
#
|
|
23
|
-
#
|
|
24
|
-
# WA_DEVICE=pixel_8_pro
|
|
17
|
+
# WA_DEVICE=iphone16pro
|
|
25
18
|
|
|
26
|
-
# ── Custom
|
|
27
|
-
# Use these to
|
|
19
|
+
# ── Custom iPhone overrides ──────────────────────────────────────────────────
|
|
20
|
+
# Use these to emulate a specific iPhone model not in the list above.
|
|
21
|
+
# All values are sent as-is in the WhatsApp registration and connection.
|
|
28
22
|
|
|
29
|
-
# WA_DEVICE_MODEL=
|
|
30
|
-
#
|
|
31
|
-
#
|
|
32
|
-
#
|
|
33
|
-
# WA_DEVICE_MODEL_ID=samsung-sm-s928b
|
|
23
|
+
# WA_DEVICE_MODEL=iPhone 16 Pro Max
|
|
24
|
+
# WA_DEVICE_OS_VERSION=18.3.2
|
|
25
|
+
# WA_DEVICE_BUILD=22D82
|
|
26
|
+
# WA_DEVICE_MODEL_ID=iPhone17,2
|
|
34
27
|
|
|
35
|
-
# ── Version & token overrides
|
|
28
|
+
# ── Version & token overrides ────────────────────────────────────────────────
|
|
36
29
|
|
|
37
|
-
# Pin the WhatsApp version string instead of fetching the latest from the
|
|
38
|
-
# Format: 2.x.x.x
|
|
39
|
-
# WA_VERSION=2.
|
|
30
|
+
# Pin the WhatsApp version string instead of fetching the latest from the
|
|
31
|
+
# App Store (iTunes lookup). Format: 2.x.x.x (four-part).
|
|
32
|
+
# WA_VERSION=2.26.10.74
|
|
40
33
|
|
|
41
|
-
# Override the static
|
|
42
|
-
#
|
|
43
|
-
#
|
|
34
|
+
# Override the built-in iOS static secret used in the token MD5 formula:
|
|
35
|
+
# token = MD5( WA_STATIC_TOKEN + MD5hex(version) + nationalNumber )
|
|
36
|
+
# Only needed if WhatsApp rotates the built-in secret.
|
|
37
|
+
# WA_STATIC_TOKEN=0a1mLfGUIBVrMKF1RdvLI5lkRBvof6vn0fD2QRSM
|
|
44
38
|
|
|
45
|
-
# ── Proxy / Tor
|
|
39
|
+
# ── Proxy / Tor ──────────────────────────────────────────────────────────────
|
|
46
40
|
|
|
47
41
|
# Route registration HTTP traffic through a SOCKS5 proxy or Tor.
|
|
42
|
+
# Residential proxies recommended to avoid WhatsApp security blocks.
|
|
48
43
|
# TOR_PROXY=socks5://127.0.0.1:9050
|
|
49
44
|
# SOCKS_PROXY=socks5://user:pass@proxy.example.com:1080
|
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.1.
|
|
2
|
+
<div align='center'>v5.1.16</div>
|
|
3
3
|
|
|
4
4
|
##
|
|
5
5
|
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
> This project is not affiliated, associated, authorized, endorsed by, or in any way officially connected with WhatsApp or any of its subsidiaries or affiliates. "WhatsApp" and related names are registered trademarks of their respective owners. Use at your own discretion.
|
|
11
11
|
|
|
12
12
|
- whalibmob does not require a browser, Selenium, or any other external runtime — it communicates directly with WhatsApp using a **TCP socket** and the **Noise Protocol** handshake.
|
|
13
|
-
- The library operates as a real **iOS mobile device
|
|
13
|
+
- The library operates as a real **iOS mobile device** (iPhone), not as WhatsApp Web. It uses the Mobile API endpoint, which behaves differently from the Web API.
|
|
14
14
|
- Signal Protocol encryption is **fully inlined** in pure JavaScript — no native binaries, no node-gyp, runs anywhere Node.js runs.
|
|
15
15
|
|
|
16
16
|
## Install
|
|
@@ -184,9 +184,8 @@ npm install -g whalibmob
|
|
|
184
184
|
- [Media Encryption](#media-encryption)
|
|
185
185
|
- [Device Emulation](#device-emulation)
|
|
186
186
|
- [Quick Start](#device-quick-start)
|
|
187
|
-
- [
|
|
188
|
-
- [
|
|
189
|
-
- [Custom Device Fields](#custom-device-fields)
|
|
187
|
+
- [iPhone Profiles](#iphone-profiles)
|
|
188
|
+
- [Custom iPhone Fields](#custom-iphone-fields)
|
|
190
189
|
- [Version & Token Overrides](#version--token-overrides)
|
|
191
190
|
|
|
192
191
|
---
|
|
@@ -2186,18 +2185,20 @@ See the [Receiving Media](#receiving-media) section for a complete working code
|
|
|
2186
2185
|
|
|
2187
2186
|
## Device Emulation
|
|
2188
2187
|
|
|
2189
|
-
whalibmob
|
|
2190
|
-
The device profile controls the User-Agent header, the Noise Protocol `platform` field, and the
|
|
2188
|
+
whalibmob emulates an iPhone when communicating with WhatsApp servers.
|
|
2189
|
+
The device profile controls the User-Agent header, the Noise Protocol `platform` field, and the token computation.
|
|
2190
|
+
|
|
2191
|
+
When no `WA_DEVICE` is set, the library **picks a random iPhone model** from the built-in profile list for each new session. This provides natural device diversity.
|
|
2191
2192
|
|
|
2192
2193
|
Configuration is done entirely through environment variables — no code changes required.
|
|
2193
2194
|
Copy `.env.example` to `.env` in your project root and set the variables you need.
|
|
2194
2195
|
|
|
2195
2196
|
### Device Quick Start
|
|
2196
2197
|
|
|
2197
|
-
Emulate an
|
|
2198
|
+
Emulate an iPhone 16 Pro:
|
|
2198
2199
|
|
|
2199
2200
|
```sh
|
|
2200
|
-
|
|
2201
|
+
WA_DEVICE=iphone16pro node your-app.js
|
|
2201
2202
|
```
|
|
2202
2203
|
|
|
2203
2204
|
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')`:
|
|
@@ -2208,86 +2209,86 @@ const { WhalibmobClient } = require('whalibmob')
|
|
|
2208
2209
|
```
|
|
2209
2210
|
|
|
2210
2211
|
```dotenv
|
|
2211
|
-
|
|
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
|
|
2212
|
+
WA_DEVICE=iphone16pro
|
|
2224
2213
|
```
|
|
2225
2214
|
|
|
2226
|
-
###
|
|
2215
|
+
### iPhone Profiles
|
|
2227
2216
|
|
|
2228
|
-
Available values for `WA_DEVICE
|
|
2217
|
+
Available values for `WA_DEVICE`:
|
|
2229
2218
|
|
|
2230
2219
|
| Profile key | Device | iOS version |
|
|
2231
2220
|
|---|---|---|
|
|
2232
|
-
| `
|
|
2233
|
-
| `
|
|
2234
|
-
| `
|
|
2235
|
-
| `
|
|
2236
|
-
| `
|
|
2237
|
-
| `
|
|
2238
|
-
| `
|
|
2239
|
-
| `
|
|
2240
|
-
| `
|
|
2241
|
-
| `
|
|
2242
|
-
| `
|
|
2243
|
-
| `
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
|
2221
|
+
| `iphone16promax` | iPhone 16 Pro Max | 18.3.2 |
|
|
2222
|
+
| `iphone16pro` | iPhone 16 Pro | 18.3.2 |
|
|
2223
|
+
| `iphone16plus` | iPhone 16 Plus | 18.3.2 |
|
|
2224
|
+
| `iphone16` | iPhone 16 | 18.3.2 |
|
|
2225
|
+
| `iphone15promax` | iPhone 15 Pro Max | 18.3.2 |
|
|
2226
|
+
| `iphone15pro` | iPhone 15 Pro | 18.3.2 |
|
|
2227
|
+
| `iphone15plus` | iPhone 15 Plus | 18.3.2 |
|
|
2228
|
+
| `iphone15` | iPhone 15 | 18.3.2 |
|
|
2229
|
+
| `iphone14promax` | iPhone 14 Pro Max | 18.3.2 |
|
|
2230
|
+
| `iphone14pro` | iPhone 14 Pro | 18.3.2 |
|
|
2231
|
+
| `iphone14plus` | iPhone 14 Plus | 17.7.5 |
|
|
2232
|
+
| `iphone14` | iPhone 14 | 17.7.5 |
|
|
2233
|
+
| `iphone13pro` | iPhone 13 Pro | 17.7.5 |
|
|
2234
|
+
| `iphone13` | iPhone 13 | 17.7.5 |
|
|
2235
|
+
| `iphone12pro` | iPhone 12 Pro | 17.7.5 |
|
|
2236
|
+
| `iphone12` | iPhone 12 | 17.7.5 |
|
|
2237
|
+
| `iphonese3` | iPhone SE (3rd gen) | 17.7.5 |
|
|
2238
|
+
| `iphone11pro` | iPhone 11 Pro | 17.7.5 |
|
|
2239
|
+
| `iphone11` | iPhone 11 | 17.7.5 |
|
|
2240
|
+
| `iphonexs` | iPhone Xs | 16.7.11 |
|
|
2241
|
+
|
|
2242
|
+
User-Agent format: `WhatsApp/<version> iOS/<osVersion> Device/<model>`
|
|
2243
|
+
|
|
2244
|
+
The WhatsApp version is fetched automatically from the Apple App Store (iTunes API) every time you register, always using the latest published version. The result is cached for **6 hours** so repeated calls within a session are fast. If all App Store sources fail (no internet, rate limit, etc.), the built-in `IOS_VERSION_FALLBACK` constant is used as a last resort. Three iTunes API endpoints are tried in sequence for maximum reliability.
|
|
2245
|
+
|
|
2246
|
+
### Custom iPhone Fields
|
|
2247
|
+
|
|
2248
|
+
These variables override individual fields to emulate a specific iPhone not in the list above.
|
|
2249
|
+
|
|
2250
|
+
| Variable | Sent to WhatsApp as | Notes |
|
|
2252
2251
|
|---|---|---|
|
|
2253
|
-
| `
|
|
2254
|
-
| `
|
|
2255
|
-
| `
|
|
2256
|
-
| `
|
|
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:
|
|
2252
|
+
| `WA_DEVICE_MODEL` | proto field 7 | e.g. `iPhone 16 Pro Max` |
|
|
2253
|
+
| `WA_DEVICE_OS_VERSION` | proto field 5 | e.g. `18.3.2` or `17.4.1` |
|
|
2254
|
+
| `WA_DEVICE_BUILD` | proto field 8 | iOS build number, e.g. `22D82` |
|
|
2255
|
+
| `WA_DEVICE_MODEL_ID` | proto field 16 | Apple model number, e.g. `iPhone17,2` |
|
|
2276
2256
|
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
2257
|
+
> [!NOTE]
|
|
2258
|
+
> These fields are sent in the **Noise Protocol handshake** (TCP connection to WhatsApp), not in the HTTP registration body. The HTTP registration body sends identity keys, token, `mcc`/`mnc`/`sim_mcc`/`sim_mnc` (derived from the country code), and locale (`lg`/`lc`) — it does **not** send the human-readable device strings.
|
|
2259
|
+
|
|
2260
|
+
**Example — custom iPhone via Node.js API:**
|
|
2261
|
+
|
|
2262
|
+
```js
|
|
2263
|
+
process.env.WA_DEVICE_MODEL = 'iPhone 16 Pro Max'
|
|
2264
|
+
process.env.WA_DEVICE_OS_VERSION = '18.3.2'
|
|
2265
|
+
process.env.WA_DEVICE_BUILD = '22D82'
|
|
2266
|
+
process.env.WA_DEVICE_MODEL_ID = 'iPhone17,2'
|
|
2267
|
+
|
|
2268
|
+
const { createNewStore, requestSmsCode, verifyCode } = require('whalibmob')
|
|
2269
|
+
|
|
2270
|
+
const store = createNewStore('919634847671')
|
|
2271
|
+
await requestSmsCode(store, 'sms')
|
|
2272
|
+
```
|
|
2273
|
+
|
|
2274
|
+
**Example — custom iPhone via `.env` file:**
|
|
2275
|
+
|
|
2276
|
+
```dotenv
|
|
2277
|
+
WA_DEVICE_MODEL=iPhone 16 Pro Max
|
|
2278
|
+
WA_DEVICE_OS_VERSION=18.3.2
|
|
2279
|
+
WA_DEVICE_BUILD=22D82
|
|
2280
|
+
WA_DEVICE_MODEL_ID=iPhone17,2
|
|
2281
|
+
```
|
|
2284
2282
|
|
|
2285
2283
|
### Version & Token Overrides
|
|
2286
2284
|
|
|
2287
2285
|
| Variable | Description |
|
|
2288
2286
|
|---|---|
|
|
2289
|
-
| `WA_VERSION` | Pin
|
|
2290
|
-
| `WA_STATIC_TOKEN` |
|
|
2287
|
+
| `WA_VERSION` | Pin a specific WhatsApp version (e.g. `2.26.10.74`). Skips the live App Store fetch entirely. |
|
|
2288
|
+
| `WA_STATIC_TOKEN` | Replaces the built-in iOS static secret in the MD5 formula (`token = MD5(secret + MD5hex(version) + national)`). Only needed if WhatsApp rotates the built-in secret. |
|
|
2289
|
+
|
|
2290
|
+
> [!NOTE]
|
|
2291
|
+
> **No token configuration is needed.** The built-in iOS static secret (`IOS_STATIC_TOKEN`) is valid for current WhatsApp servers. `WA_STATIC_TOKEN` is only needed if that secret is rotated in a future WhatsApp update.
|
|
2291
2292
|
|
|
2292
2293
|
## License
|
|
2293
2294
|
|
package/cli.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
4
|
// Load .env from the current working directory (silently — no error if missing).
|
|
5
|
-
// This lets users configure
|
|
5
|
+
// This lets users configure WA_DEVICE, WA_VERSION etc. without touching
|
|
6
6
|
// their shell environment. Must happen before any other require() so that
|
|
7
7
|
// process.env is fully populated when modules read it at load time.
|
|
8
8
|
try { require('dotenv').config(); } catch (_) {}
|
|
@@ -35,7 +35,7 @@ const {
|
|
|
35
35
|
loadStore
|
|
36
36
|
} = require('./lib/Client');
|
|
37
37
|
|
|
38
|
-
const VERSION = '5.1.
|
|
38
|
+
const VERSION = '5.1.16';
|
|
39
39
|
|
|
40
40
|
// ─── output helpers ───────────────────────────────────────────────────────────
|
|
41
41
|
|
package/index.js
CHANGED
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
const { WhalibmobClient, checkNumberStatus, fetchIosVersion, fetchWaVersion, assertRegistrationKeys } = require('./lib/Client');
|
|
4
4
|
const { getDeviceConfig } = require('./lib/DeviceConfig');
|
|
5
|
-
const { fetchAndroidVersion } = require('./lib/Registration');
|
|
6
5
|
const { createNewStore, saveStore, loadStore, toSixParts, fromSixParts, storeToJson, storeFromJson } = require('./lib/Store');
|
|
7
6
|
const { checkIfRegistered, requestSmsCode, verifyCode } = require('./lib/Registration');
|
|
8
7
|
const { SignalProtocol } = require('./lib/signal/SignalProtocol');
|
|
@@ -19,12 +18,11 @@ module.exports = {
|
|
|
19
18
|
requestSmsCode,
|
|
20
19
|
verifyCode,
|
|
21
20
|
assertRegistrationKeys,
|
|
22
|
-
// Version fetch —
|
|
21
|
+
// Version fetch — fetchWaVersion fetches from the Apple App Store.
|
|
23
22
|
// fetchIosVersion is kept for backward compatibility.
|
|
24
23
|
fetchWaVersion,
|
|
25
24
|
fetchIosVersion,
|
|
26
|
-
|
|
27
|
-
// Device config — reads WA_OS / WA_DEVICE / WA_DEVICE_* from process.env
|
|
25
|
+
// Device config — reads WA_DEVICE / WA_DEVICE_* from process.env
|
|
28
26
|
getDeviceConfig,
|
|
29
27
|
// Store helpers
|
|
30
28
|
createNewStore,
|
package/lib/DeviceConfig.js
CHANGED
|
@@ -2,80 +2,65 @@
|
|
|
2
2
|
|
|
3
3
|
const {
|
|
4
4
|
IOS_DEVICE,
|
|
5
|
-
IOS_DEVICE_PROFILES
|
|
6
|
-
ANDROID_DEVICE_PROFILES
|
|
5
|
+
IOS_DEVICE_PROFILES
|
|
7
6
|
} = require('./constants');
|
|
8
7
|
|
|
9
|
-
// Pre-build normalised lookup
|
|
8
|
+
// Pre-build normalised lookup table so that user-supplied profile keys in any
|
|
10
9
|
// separator style (underscores, dashes, spaces, or none) always resolve.
|
|
11
10
|
// Normalisation: lowercase, strip underscores / dashes / spaces.
|
|
12
11
|
function _normalise(s) { return String(s).toLowerCase().replace(/[\s_-]/g, ''); }
|
|
13
12
|
|
|
14
|
-
const _IOS_MAP
|
|
15
|
-
const
|
|
16
|
-
for (const k of Object.keys(IOS_DEVICE_PROFILES))
|
|
17
|
-
|
|
13
|
+
const _IOS_MAP = {};
|
|
14
|
+
const _IOS_KEYS = [];
|
|
15
|
+
for (const k of Object.keys(IOS_DEVICE_PROFILES)) {
|
|
16
|
+
_IOS_MAP[_normalise(k)] = IOS_DEVICE_PROFILES[k];
|
|
17
|
+
_IOS_KEYS.push(k);
|
|
18
|
+
}
|
|
18
19
|
|
|
19
20
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
20
|
-
// DeviceConfig — reads environment variables and returns the active device
|
|
21
|
+
// DeviceConfig — reads environment variables and returns the active iOS device
|
|
21
22
|
// profile to be used during registration and connection.
|
|
22
23
|
//
|
|
23
24
|
// Priority order:
|
|
24
|
-
// 1.
|
|
25
|
-
// 2.
|
|
26
|
-
// 3. No env vars
|
|
25
|
+
// 1. WA_DEVICE → pick a named predefined iPhone profile
|
|
26
|
+
// 2. WA_DEVICE_* variables → build a custom iOS profile
|
|
27
|
+
// 3. No env vars → random iPhone from the profile list
|
|
27
28
|
//
|
|
28
29
|
// Env variables:
|
|
29
|
-
//
|
|
30
|
-
//
|
|
31
|
-
//
|
|
32
|
-
//
|
|
33
|
-
//
|
|
34
|
-
// WA_DEVICE_BUILD OS build number
|
|
35
|
-
// WA_DEVICE_MODEL_ID internal model identifier (e.g. SM-S928B, iPhone16,1)
|
|
30
|
+
// WA_DEVICE profile key (e.g. iphone16pro, iphone15, iphonese3)
|
|
31
|
+
// WA_DEVICE_MODEL device display name (e.g. "iPhone 16 Pro Max")
|
|
32
|
+
// WA_DEVICE_OS_VERSION iOS version string (e.g. "18.3.2")
|
|
33
|
+
// WA_DEVICE_BUILD iOS build number (e.g. "22D82")
|
|
34
|
+
// WA_DEVICE_MODEL_ID internal model id (e.g. "iPhone17,2")
|
|
36
35
|
// WA_VERSION override WhatsApp version (skips live fetch if set)
|
|
37
|
-
// WA_STATIC_TOKEN override static registration token
|
|
36
|
+
// WA_STATIC_TOKEN override static registration token secret
|
|
38
37
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
39
38
|
|
|
40
39
|
function getDeviceConfig() {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
const
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
return Object.assign({}, found);
|
|
48
|
-
}
|
|
40
|
+
// 1. Named profile via WA_DEVICE
|
|
41
|
+
if (process.env.WA_DEVICE) {
|
|
42
|
+
const profileKey = _normalise(process.env.WA_DEVICE);
|
|
43
|
+
const found = _IOS_MAP[profileKey] || null;
|
|
44
|
+
if (found) return Object.assign({}, found);
|
|
45
|
+
}
|
|
49
46
|
|
|
47
|
+
// 2. Custom profile via individual WA_DEVICE_* vars
|
|
48
|
+
if (process.env.WA_DEVICE_MODEL || process.env.WA_DEVICE_MODEL_ID) {
|
|
50
49
|
return {
|
|
51
|
-
os: '
|
|
52
|
-
platform:
|
|
53
|
-
model: process.env.WA_DEVICE_MODEL
|
|
54
|
-
manufacturer:
|
|
55
|
-
osVersion: process.env.WA_DEVICE_OS_VERSION
|
|
56
|
-
osBuildNumber: process.env.WA_DEVICE_BUILD
|
|
57
|
-
modelId: process.env.WA_DEVICE_MODEL_ID
|
|
50
|
+
os: 'ios',
|
|
51
|
+
platform: 1,
|
|
52
|
+
model: process.env.WA_DEVICE_MODEL || IOS_DEVICE.model,
|
|
53
|
+
manufacturer: 'Apple',
|
|
54
|
+
osVersion: process.env.WA_DEVICE_OS_VERSION || IOS_DEVICE.osVersion,
|
|
55
|
+
osBuildNumber: process.env.WA_DEVICE_BUILD || IOS_DEVICE.osBuildNumber,
|
|
56
|
+
modelId: process.env.WA_DEVICE_MODEL_ID || IOS_DEVICE.modelId,
|
|
58
57
|
deviceModelType: 2
|
|
59
58
|
};
|
|
60
59
|
}
|
|
61
60
|
|
|
62
|
-
//
|
|
63
|
-
const
|
|
64
|
-
|
|
65
|
-
if (found) {
|
|
66
|
-
return Object.assign({}, found);
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
return {
|
|
70
|
-
os: 'ios',
|
|
71
|
-
platform: 1,
|
|
72
|
-
model: process.env.WA_DEVICE_MODEL || IOS_DEVICE.model,
|
|
73
|
-
manufacturer: process.env.WA_DEVICE_MANUFACTURER || IOS_DEVICE.manufacturer,
|
|
74
|
-
osVersion: process.env.WA_DEVICE_OS_VERSION || IOS_DEVICE.osVersion,
|
|
75
|
-
osBuildNumber: process.env.WA_DEVICE_BUILD || IOS_DEVICE.osBuildNumber,
|
|
76
|
-
modelId: process.env.WA_DEVICE_MODEL_ID || IOS_DEVICE.modelId,
|
|
77
|
-
deviceModelType: 2
|
|
78
|
-
};
|
|
61
|
+
// 3. No env vars → pick a random iPhone profile for device diversity
|
|
62
|
+
const idx = Math.floor(Math.random() * _IOS_KEYS.length);
|
|
63
|
+
return Object.assign({}, IOS_DEVICE_PROFILES[_IOS_KEYS[idx]]);
|
|
79
64
|
}
|
|
80
65
|
|
|
81
66
|
module.exports = { getDeviceConfig };
|
package/lib/MediaService.js
CHANGED
|
@@ -120,7 +120,7 @@ function _tryUpload(host, mediaPath, token, auth, encrypted, encSha256, sha256)
|
|
|
120
120
|
'Content-Type': 'application/octet-stream',
|
|
121
121
|
'Content-Length': encrypted.length,
|
|
122
122
|
'Accept': 'application/json',
|
|
123
|
-
'User-Agent': (() => { const d = getDeviceConfig(); return
|
|
123
|
+
'User-Agent': (() => { const d = getDeviceConfig(); return `WhatsApp/${d.version || '2.26.10.74'} iOS/${d.osVersion} Device/${d.model}`; })()
|
|
124
124
|
}
|
|
125
125
|
};
|
|
126
126
|
|
package/lib/Registration.js
CHANGED
|
@@ -38,9 +38,7 @@ async function httpPostViaSocks(path, body, waVersion, proxyUrl) {
|
|
|
38
38
|
});
|
|
39
39
|
|
|
40
40
|
const _dev = getDeviceConfig();
|
|
41
|
-
const userAgent = _dev.
|
|
42
|
-
? `WhatsApp/${waVersion} A`
|
|
43
|
-
: `WhatsApp/${waVersion} iOS/${_dev.osVersion} Device/${_dev.model}`;
|
|
41
|
+
const userAgent = `WhatsApp/${waVersion} iOS/${_dev.osVersion} Device/${_dev.model}`;
|
|
44
42
|
const req = [
|
|
45
43
|
`POST /v2${path} HTTP/1.1`,
|
|
46
44
|
`Host: ${dHost}`,
|
|
@@ -90,9 +88,7 @@ const {
|
|
|
90
88
|
REGISTRATION_ENDPOINT,
|
|
91
89
|
REGISTRATION_PUBLIC_KEY,
|
|
92
90
|
IOS_STATIC_TOKEN,
|
|
93
|
-
ANDROID_STATIC_TOKEN,
|
|
94
91
|
IOS_VERSION_FALLBACK,
|
|
95
|
-
ANDROID_VERSION_FALLBACK,
|
|
96
92
|
IOS_USER_AGENT,
|
|
97
93
|
IOS_DEVICE,
|
|
98
94
|
SIGNAL_KEY_TYPE,
|
|
@@ -231,93 +227,93 @@ function parsePhone(phoneNumber) {
|
|
|
231
227
|
return { cc: cc1, national: String(BigInt(str.slice(1))) };
|
|
232
228
|
}
|
|
233
229
|
|
|
234
|
-
// ---------- WhatsApp version fetch (
|
|
230
|
+
// ---------- WhatsApp version fetch (from Apple App Store / iTunes) ----------
|
|
235
231
|
|
|
236
|
-
|
|
237
|
-
|
|
232
|
+
// Version cache with TTL — re-fetches every 6 hours so long-running processes
|
|
233
|
+
// always use the current App Store version, not a stale one from startup.
|
|
234
|
+
const VERSION_CACHE_TTL_MS = 6 * 60 * 60 * 1000; // 6 hours
|
|
235
|
+
let _versionCache = { version: null, fetchedAt: 0 };
|
|
238
236
|
|
|
239
|
-
|
|
240
|
-
|
|
237
|
+
// Fetch JSON from a single iTunes API URL. Resolves to a version string or null.
|
|
238
|
+
function _fetchFromUrl(url) {
|
|
241
239
|
return new Promise((resolve) => {
|
|
242
|
-
const req = https.get(
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
res.on('data', d => chunks.push(d));
|
|
248
|
-
res.on('end', () => {
|
|
249
|
-
try {
|
|
250
|
-
const json = JSON.parse(Buffer.concat(chunks).toString('utf8'));
|
|
251
|
-
let ver = (json.results && json.results[0] && json.results[0].version) || IOS_VERSION_FALLBACK;
|
|
252
|
-
if (!ver.startsWith('2.')) ver = '2.' + ver;
|
|
253
|
-
_cachedIosVersion = ver;
|
|
254
|
-
resolve(ver);
|
|
255
|
-
} catch (_) {
|
|
256
|
-
resolve(IOS_VERSION_FALLBACK);
|
|
257
|
-
}
|
|
258
|
-
});
|
|
240
|
+
const req = https.get(url, { headers: { 'User-Agent': IOS_USER_AGENT } }, (res) => {
|
|
241
|
+
// Follow one redirect (iTunes sometimes 301 → 200)
|
|
242
|
+
if ((res.statusCode === 301 || res.statusCode === 302) && res.headers.location) {
|
|
243
|
+
resolve(_fetchFromUrl(res.headers.location));
|
|
244
|
+
return;
|
|
259
245
|
}
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
246
|
+
const chunks = [];
|
|
247
|
+
res.on('data', d => chunks.push(d));
|
|
248
|
+
res.on('end', () => {
|
|
249
|
+
try {
|
|
250
|
+
const json = JSON.parse(Buffer.concat(chunks).toString('utf8'));
|
|
251
|
+
const raw = json.results && json.results[0] && json.results[0].version;
|
|
252
|
+
if (!raw) return resolve(null);
|
|
253
|
+
let ver = String(raw).trim();
|
|
254
|
+
if (!ver.startsWith('2.')) ver = '2.' + ver;
|
|
255
|
+
resolve(ver);
|
|
256
|
+
} catch (_) {
|
|
257
|
+
resolve(null);
|
|
258
|
+
}
|
|
259
|
+
});
|
|
260
|
+
});
|
|
261
|
+
req.on('error', () => resolve(null));
|
|
262
|
+
req.setTimeout(8000, () => { req.destroy(); resolve(null); });
|
|
263
263
|
});
|
|
264
264
|
}
|
|
265
265
|
|
|
266
|
-
//
|
|
267
|
-
//
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
const html = String(resp.data);
|
|
284
|
-
|
|
285
|
-
// Primary: first quoted 4-part version string matching WhatsApp's "2.x.x.x" scheme.
|
|
286
|
-
// In Play Store JSON the current stable version appears first, before beta/history entries.
|
|
287
|
-
const primary = html.match(/"(2\.\d+\.\d+\.\d+)"/);
|
|
288
|
-
if (primary) {
|
|
289
|
-
_cachedAndroidVersion = primary[1];
|
|
290
|
-
return primary[1];
|
|
291
|
-
}
|
|
266
|
+
// Live App Store sources, tried in order until one succeeds.
|
|
267
|
+
// Multiple endpoints provide resilience against CDN hiccups and region blocks.
|
|
268
|
+
const IOS_VERSION_SOURCES = [
|
|
269
|
+
'https://itunes.apple.com/lookup?bundleId=net.whatsapp.WhatsApp&country=us',
|
|
270
|
+
'https://itunes.apple.com/lookup?bundleId=net.whatsapp.WhatsApp',
|
|
271
|
+
'https://itunes.apple.com/lookup?bundleId=net.whatsapp.WhatsApp&country=gb',
|
|
272
|
+
];
|
|
273
|
+
|
|
274
|
+
// Fetch and return the latest WhatsApp iOS version from the Apple App Store.
|
|
275
|
+
// Results are cached for VERSION_CACHE_TTL_MS (6 hours) so long-running
|
|
276
|
+
// servers automatically pick up new releases without a restart.
|
|
277
|
+
// Set WA_VERSION env var to pin a specific version and skip the live fetch.
|
|
278
|
+
async function fetchIosVersion() {
|
|
279
|
+
const now = Date.now();
|
|
280
|
+
if (_versionCache.version && (now - _versionCache.fetchedAt) < VERSION_CACHE_TTL_MS) {
|
|
281
|
+
return _versionCache.version;
|
|
282
|
+
}
|
|
292
283
|
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
284
|
+
// Try each source in order — return the first successful result
|
|
285
|
+
for (const url of IOS_VERSION_SOURCES) {
|
|
286
|
+
const ver = await _fetchFromUrl(url);
|
|
287
|
+
if (ver) {
|
|
288
|
+
_versionCache = { version: ver, fetchedAt: now };
|
|
289
|
+
console.error(`[DBG] fetchIosVersion: ${ver} (source: ${url})`);
|
|
290
|
+
return ver;
|
|
298
291
|
}
|
|
299
|
-
}
|
|
300
|
-
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
// All sources failed — use fallback constant (and don't cache it so next call retries)
|
|
295
|
+
console.error(`[DBG] fetchIosVersion: all sources failed, using fallback ${IOS_VERSION_FALLBACK}`);
|
|
296
|
+
return IOS_VERSION_FALLBACK;
|
|
301
297
|
}
|
|
302
298
|
|
|
303
|
-
// Return the
|
|
304
|
-
// If WA_VERSION is set in the environment, that value is always used.
|
|
305
|
-
async function fetchWaVersion(
|
|
299
|
+
// Return the latest WhatsApp iOS version.
|
|
300
|
+
// If WA_VERSION is set in the environment, that value is always used (no fetch).
|
|
301
|
+
async function fetchWaVersion() {
|
|
306
302
|
if (process.env.WA_VERSION) return process.env.WA_VERSION;
|
|
307
|
-
return
|
|
303
|
+
return fetchIosVersion();
|
|
308
304
|
}
|
|
309
305
|
|
|
310
306
|
// ---------- Token computation ----------
|
|
311
|
-
//
|
|
312
|
-
//
|
|
313
|
-
//
|
|
307
|
+
//
|
|
308
|
+
// iOS token formula:
|
|
309
|
+
// token = MD5( staticSecret + MD5hex(waVersion) + nationalNumber )
|
|
310
|
+
// The staticSecret is IOS_STATIC_TOKEN unless WA_STATIC_TOKEN is set.
|
|
311
|
+
// This algorithm is accepted by WhatsApp servers as of 2.26.x.
|
|
314
312
|
|
|
315
313
|
function computeToken(waVersion, national) {
|
|
316
|
-
const
|
|
317
|
-
const staticToken = process.env.WA_STATIC_TOKEN
|
|
318
|
-
|| (device.os === 'android' ? ANDROID_STATIC_TOKEN : IOS_STATIC_TOKEN);
|
|
314
|
+
const secret = process.env.WA_STATIC_TOKEN || IOS_STATIC_TOKEN;
|
|
319
315
|
const versionHashHex = md5Bytes(waVersion).toString('hex');
|
|
320
|
-
return md5Hex(
|
|
316
|
+
return md5Hex(secret + versionHashHex + national);
|
|
321
317
|
}
|
|
322
318
|
|
|
323
319
|
// ---------- Byte helpers ----------
|
|
@@ -372,26 +368,31 @@ function buildPayload(store, waVersion, useToken, extraPairs) {
|
|
|
372
368
|
const { cc, national } = parsePhone(store.phoneNumber);
|
|
373
369
|
const token = useToken ? computeToken(waVersion, national) : null;
|
|
374
370
|
const fdid = store.fdid.toUpperCase();
|
|
375
|
-
const
|
|
371
|
+
const meta = getCountryMeta(cc);
|
|
376
372
|
|
|
377
373
|
return buildForm([
|
|
378
|
-
'cc',
|
|
379
|
-
'in',
|
|
380
|
-
'rc',
|
|
381
|
-
'lg',
|
|
382
|
-
'lc',
|
|
383
|
-
'
|
|
384
|
-
'
|
|
385
|
-
'
|
|
386
|
-
'
|
|
387
|
-
'
|
|
388
|
-
'
|
|
374
|
+
'cc', cc,
|
|
375
|
+
'in', national,
|
|
376
|
+
'rc', String(RELEASE_CHANNEL),
|
|
377
|
+
'lg', meta.lg || 'en',
|
|
378
|
+
'lc', meta.lc || 'US',
|
|
379
|
+
'mcc', meta.mcc,
|
|
380
|
+
'mnc', meta.mnc,
|
|
381
|
+
'sim_mcc', meta.mcc,
|
|
382
|
+
'sim_mnc', meta.mnc,
|
|
383
|
+
'platform', '1',
|
|
384
|
+
'hasinrc', '0',
|
|
385
|
+
'authkey', toBase64Url(stripKeyPrefix(store.noiseKeyPair.public)),
|
|
386
|
+
'e_regid', toBase64Url(intToBytes(store.registrationId, 4)),
|
|
387
|
+
'e_keytype', toBase64Url(Buffer.from([SIGNAL_KEY_TYPE])),
|
|
388
|
+
'e_ident', toBase64Url(stripKeyPrefix(store.identityKeyPair.public)),
|
|
389
|
+
'e_skey_id', toBase64Url(intToBytes(store.signedPreKey.id, 3)),
|
|
389
390
|
'e_skey_val', toBase64Url(stripKeyPrefix(store.signedPreKey.public)),
|
|
390
391
|
'e_skey_sig', toBase64Url(store.signedPreKey.signature),
|
|
391
|
-
'fdid',
|
|
392
|
-
'expid',
|
|
393
|
-
'id',
|
|
394
|
-
'token',
|
|
392
|
+
'fdid', fdid,
|
|
393
|
+
'expid', toBase64Url(store.deviceId),
|
|
394
|
+
'id', toUrlHex(store.identityId),
|
|
395
|
+
'token', token
|
|
395
396
|
], extraPairs);
|
|
396
397
|
}
|
|
397
398
|
|
|
@@ -418,9 +419,7 @@ function httpPost(path, body, waVersion) {
|
|
|
418
419
|
|
|
419
420
|
return new Promise((resolve, reject) => {
|
|
420
421
|
const _httpDev = getDeviceConfig();
|
|
421
|
-
const userAgent = _httpDev.
|
|
422
|
-
? `WhatsApp/${waVersion} A`
|
|
423
|
-
: `WhatsApp/${waVersion} iOS/${_httpDev.osVersion} Device/${_httpDev.model}`;
|
|
422
|
+
const userAgent = `WhatsApp/${waVersion} iOS/${_httpDev.osVersion} Device/${_httpDev.model}`;
|
|
424
423
|
const bodyBuf = Buffer.from(body, 'utf8');
|
|
425
424
|
const opts = {
|
|
426
425
|
hostname: 'v.whatsapp.net',
|
|
@@ -463,7 +462,7 @@ async function sendRequest(path, store, waVersion, useToken, extraPairs) {
|
|
|
463
462
|
// ---------- Public API ----------
|
|
464
463
|
|
|
465
464
|
async function checkIfRegistered(store) {
|
|
466
|
-
const waVersion = await fetchWaVersion(
|
|
465
|
+
const waVersion = await fetchWaVersion();
|
|
467
466
|
return sendRequest('/exist', store, waVersion, false, null);
|
|
468
467
|
}
|
|
469
468
|
|
|
@@ -492,13 +491,9 @@ async function checkNumberStatus(phoneNumber) {
|
|
|
492
491
|
|
|
493
492
|
phoneNumber = String(phoneNumber).replace(/\D/g, '');
|
|
494
493
|
const store = createNewStore(phoneNumber);
|
|
495
|
-
const waVersion = await fetchWaVersion(
|
|
496
|
-
const { cc } = parsePhone(phoneNumber);
|
|
497
|
-
const meta = getCountryMeta(cc);
|
|
494
|
+
const waVersion = await fetchWaVersion();
|
|
498
495
|
|
|
499
496
|
const baseExtra = [
|
|
500
|
-
'sim_mcc', meta.mcc,
|
|
501
|
-
'sim_mnc', meta.mnc,
|
|
502
497
|
'reason', '',
|
|
503
498
|
'cellular_strength', '1'
|
|
504
499
|
];
|
|
@@ -594,7 +589,7 @@ async function assertRegistrationKeys(store, waVersion) {
|
|
|
594
589
|
async function requestSmsCode(store, method) {
|
|
595
590
|
method = method || 'sms';
|
|
596
591
|
const _device = getDeviceConfig();
|
|
597
|
-
const waVersion = await fetchWaVersion(
|
|
592
|
+
const waVersion = await fetchWaVersion();
|
|
598
593
|
store.version = waVersion;
|
|
599
594
|
store.device = _device;
|
|
600
595
|
|
|
@@ -606,8 +601,6 @@ async function requestSmsCode(store, method) {
|
|
|
606
601
|
async function _tryMethod(m) {
|
|
607
602
|
const extra = [
|
|
608
603
|
'method', m,
|
|
609
|
-
'sim_mcc', '000',
|
|
610
|
-
'sim_mnc', '000',
|
|
611
604
|
'reason', '',
|
|
612
605
|
'cellular_strength', '1'
|
|
613
606
|
];
|
|
@@ -683,7 +676,7 @@ async function requestSmsCode(store, method) {
|
|
|
683
676
|
|
|
684
677
|
async function verifyCode(store, code) {
|
|
685
678
|
const _device = getDeviceConfig();
|
|
686
|
-
const waVersion = await fetchWaVersion(
|
|
679
|
+
const waVersion = await fetchWaVersion();
|
|
687
680
|
store.version = waVersion;
|
|
688
681
|
store.device = _device;
|
|
689
682
|
const normalized = code.replace(/[\s\-]/g, '').replace(/\D/g, '');
|
|
@@ -708,4 +701,4 @@ async function verifyCode(store, code) {
|
|
|
708
701
|
throw new Error(`Verification failed: ${reason || JSON.stringify(result)}`);
|
|
709
702
|
}
|
|
710
703
|
|
|
711
|
-
module.exports = { checkIfRegistered, checkNumberStatus, requestSmsCode, verifyCode, fetchIosVersion,
|
|
704
|
+
module.exports = { checkIfRegistered, checkNumberStatus, requestSmsCode, verifyCode, fetchIosVersion, fetchWaVersion, parsePhone, getCountryMeta, assertRegistrationKeys };
|
package/lib/Store.js
CHANGED
|
@@ -5,7 +5,7 @@ const path = require('path');
|
|
|
5
5
|
const crypto = require('crypto');
|
|
6
6
|
const curveJs = require('curve25519-js');
|
|
7
7
|
const { v4: uuidv4 } = require('uuid');
|
|
8
|
-
const { IOS_DEVICE, IOS_VERSION_FALLBACK
|
|
8
|
+
const { IOS_DEVICE, IOS_VERSION_FALLBACK } = require('./constants');
|
|
9
9
|
const { getDeviceConfig } = require('./DeviceConfig');
|
|
10
10
|
|
|
11
11
|
// ─────────────────────────────────────────────────────────
|
|
@@ -60,7 +60,7 @@ function createNewStore(phoneNumber) {
|
|
|
60
60
|
const identityId = crypto.randomBytes(16);
|
|
61
61
|
|
|
62
62
|
const device = getDeviceConfig();
|
|
63
|
-
const version =
|
|
63
|
+
const version = IOS_VERSION_FALLBACK;
|
|
64
64
|
|
|
65
65
|
return {
|
|
66
66
|
phoneNumber: String(phoneNumber).replace(/^\+/, ''),
|
package/lib/constants.js
CHANGED
|
@@ -7,18 +7,16 @@ const REGISTRATION_PUBLIC_KEY = Buffer.from('8e8c0f74c3ebc5d7a6865c6c3c843856b06
|
|
|
7
7
|
|
|
8
8
|
const MOBILE_PROLOGUE = Buffer.from([0x57, 0x41, 0x05, 0x03]);
|
|
9
9
|
|
|
10
|
-
//
|
|
11
|
-
// token = MD5( staticToken + MD5hex(waVersion) + nationalNumber )
|
|
10
|
+
// iOS static tokens (MD5 approach): token = MD5( staticToken + MD5hex(waVersion) + nationalNumber )
|
|
12
11
|
const IOS_STATIC_TOKEN = '0a1mLfGUIBVrMKF1RdvLI5lkRBvof6vn0fD2QRSM';
|
|
13
12
|
const IOS_BUSINESS_STATIC_TOKEN = 'USUDuDYDeQhY4RF2fCSp5m3F6kJ1M2J8wS7bbNA2';
|
|
14
|
-
const ANDROID_STATIC_TOKEN = 'Y29Cs6AVNR2bj5PBeKSYFd1nAKuvNQ3h';
|
|
15
13
|
|
|
16
|
-
// WhatsApp version
|
|
17
|
-
|
|
18
|
-
const
|
|
14
|
+
// WhatsApp version fallback — used when ALL live iTunes sources fail.
|
|
15
|
+
// Updated: 2025-03. The library always tries the App Store first.
|
|
16
|
+
const IOS_VERSION_FALLBACK = '2.26.10.74';
|
|
19
17
|
|
|
20
18
|
// Safari UA for the iTunes lookup only.
|
|
21
|
-
const IOS_USER_AGENT = 'Mozilla/5.0 (iPhone; CPU iPhone OS
|
|
19
|
+
const IOS_USER_AGENT = 'Mozilla/5.0 (iPhone; CPU iPhone OS 18_3_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.3 Mobile/15E148 Safari/604.1';
|
|
22
20
|
|
|
23
21
|
// Default iOS device (iPhone 15 Pro, iOS 17.4.1).
|
|
24
22
|
// platform 1 = iOS in WhatsApp's ClientPayload.UserAgent proto enum.
|
|
@@ -34,139 +32,129 @@ const IOS_DEVICE = {
|
|
|
34
32
|
};
|
|
35
33
|
|
|
36
34
|
// ─── iOS device profiles ───────────────────────────────────────────────────
|
|
37
|
-
// Set
|
|
35
|
+
// Set WA_DEVICE=<key> in .env to select one.
|
|
36
|
+
// When no profile is set, the library picks one at random for each new session.
|
|
37
|
+
// platform 1 = IOS in WhatsApp's ClientPayload.UserAgent proto enum.
|
|
38
38
|
const IOS_DEVICE_PROFILES = {
|
|
39
|
+
'iphone16promax': {
|
|
40
|
+
os: 'ios', platform: 1,
|
|
41
|
+
model: 'iPhone 16 Pro Max', manufacturer: 'Apple',
|
|
42
|
+
osVersion: '18.3.2', osBuildNumber: '22D82',
|
|
43
|
+
modelId: 'iPhone17,2', deviceModelType: 2
|
|
44
|
+
},
|
|
45
|
+
'iphone16pro': {
|
|
46
|
+
os: 'ios', platform: 1,
|
|
47
|
+
model: 'iPhone 16 Pro', manufacturer: 'Apple',
|
|
48
|
+
osVersion: '18.3.2', osBuildNumber: '22D82',
|
|
49
|
+
modelId: 'iPhone17,1', deviceModelType: 2
|
|
50
|
+
},
|
|
51
|
+
'iphone16plus': {
|
|
52
|
+
os: 'ios', platform: 1,
|
|
53
|
+
model: 'iPhone 16 Plus', manufacturer: 'Apple',
|
|
54
|
+
osVersion: '18.3.2', osBuildNumber: '22D82',
|
|
55
|
+
modelId: 'iPhone17,4', deviceModelType: 2
|
|
56
|
+
},
|
|
57
|
+
'iphone16': {
|
|
58
|
+
os: 'ios', platform: 1,
|
|
59
|
+
model: 'iPhone 16', manufacturer: 'Apple',
|
|
60
|
+
osVersion: '18.3.2', osBuildNumber: '22D82',
|
|
61
|
+
modelId: 'iPhone17,3', deviceModelType: 2
|
|
62
|
+
},
|
|
63
|
+
'iphone15promax': {
|
|
64
|
+
os: 'ios', platform: 1,
|
|
65
|
+
model: 'iPhone 15 Pro Max', manufacturer: 'Apple',
|
|
66
|
+
osVersion: '18.3.2', osBuildNumber: '22D82',
|
|
67
|
+
modelId: 'iPhone16,2', deviceModelType: 2
|
|
68
|
+
},
|
|
39
69
|
'iphone15pro': {
|
|
40
70
|
os: 'ios', platform: 1,
|
|
41
71
|
model: 'iPhone 15 Pro', manufacturer: 'Apple',
|
|
42
|
-
osVersion: '
|
|
72
|
+
osVersion: '18.3.2', osBuildNumber: '22D82',
|
|
43
73
|
modelId: 'iPhone16,1', deviceModelType: 2
|
|
44
74
|
},
|
|
75
|
+
'iphone15plus': {
|
|
76
|
+
os: 'ios', platform: 1,
|
|
77
|
+
model: 'iPhone 15 Plus', manufacturer: 'Apple',
|
|
78
|
+
osVersion: '18.3.2', osBuildNumber: '22D82',
|
|
79
|
+
modelId: 'iPhone15,5', deviceModelType: 2
|
|
80
|
+
},
|
|
45
81
|
'iphone15': {
|
|
46
82
|
os: 'ios', platform: 1,
|
|
47
83
|
model: 'iPhone 15', manufacturer: 'Apple',
|
|
48
|
-
osVersion: '
|
|
84
|
+
osVersion: '18.3.2', osBuildNumber: '22D82',
|
|
49
85
|
modelId: 'iPhone15,4', deviceModelType: 2
|
|
50
86
|
},
|
|
51
|
-
'
|
|
87
|
+
'iphone14promax': {
|
|
52
88
|
os: 'ios', platform: 1,
|
|
53
|
-
model: 'iPhone 14', manufacturer: 'Apple',
|
|
54
|
-
osVersion: '
|
|
55
|
-
modelId: '
|
|
89
|
+
model: 'iPhone 14 Pro Max', manufacturer: 'Apple',
|
|
90
|
+
osVersion: '18.3.2', osBuildNumber: '22D82',
|
|
91
|
+
modelId: 'iPhone15,3', deviceModelType: 2
|
|
56
92
|
},
|
|
57
93
|
'iphone14pro': {
|
|
58
94
|
os: 'ios', platform: 1,
|
|
59
95
|
model: 'iPhone 14 Pro', manufacturer: 'Apple',
|
|
60
|
-
osVersion: '
|
|
96
|
+
osVersion: '18.3.2', osBuildNumber: '22D82',
|
|
61
97
|
modelId: 'iPhone15,2', deviceModelType: 2
|
|
62
98
|
},
|
|
99
|
+
'iphone14plus': {
|
|
100
|
+
os: 'ios', platform: 1,
|
|
101
|
+
model: 'iPhone 14 Plus', manufacturer: 'Apple',
|
|
102
|
+
osVersion: '17.7.5', osBuildNumber: '21H516',
|
|
103
|
+
modelId: 'iPhone14,8', deviceModelType: 2
|
|
104
|
+
},
|
|
105
|
+
'iphone14': {
|
|
106
|
+
os: 'ios', platform: 1,
|
|
107
|
+
model: 'iPhone 14', manufacturer: 'Apple',
|
|
108
|
+
osVersion: '17.7.5', osBuildNumber: '21H516',
|
|
109
|
+
modelId: 'iPhone14,7', deviceModelType: 2
|
|
110
|
+
},
|
|
111
|
+
'iphone13pro': {
|
|
112
|
+
os: 'ios', platform: 1,
|
|
113
|
+
model: 'iPhone 13 Pro', manufacturer: 'Apple',
|
|
114
|
+
osVersion: '17.7.5', osBuildNumber: '21H516',
|
|
115
|
+
modelId: 'iPhone14,2', deviceModelType: 2
|
|
116
|
+
},
|
|
63
117
|
'iphone13': {
|
|
64
118
|
os: 'ios', platform: 1,
|
|
65
119
|
model: 'iPhone 13', manufacturer: 'Apple',
|
|
66
|
-
osVersion: '
|
|
120
|
+
osVersion: '17.7.5', osBuildNumber: '21H516',
|
|
67
121
|
modelId: 'iPhone14,5', deviceModelType: 2
|
|
68
122
|
},
|
|
123
|
+
'iphone12pro': {
|
|
124
|
+
os: 'ios', platform: 1,
|
|
125
|
+
model: 'iPhone 12 Pro', manufacturer: 'Apple',
|
|
126
|
+
osVersion: '17.7.5', osBuildNumber: '21H516',
|
|
127
|
+
modelId: 'iPhone13,3', deviceModelType: 1
|
|
128
|
+
},
|
|
69
129
|
'iphone12': {
|
|
70
130
|
os: 'ios', platform: 1,
|
|
71
131
|
model: 'iPhone 12', manufacturer: 'Apple',
|
|
72
|
-
osVersion: '
|
|
132
|
+
osVersion: '17.7.5', osBuildNumber: '21H516',
|
|
73
133
|
modelId: 'iPhone13,2', deviceModelType: 1
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
'
|
|
82
|
-
os: '
|
|
83
|
-
model: '
|
|
84
|
-
osVersion: '
|
|
85
|
-
modelId: '
|
|
86
|
-
},
|
|
87
|
-
'
|
|
88
|
-
os: '
|
|
89
|
-
model: '
|
|
90
|
-
osVersion: '
|
|
91
|
-
modelId: '
|
|
92
|
-
},
|
|
93
|
-
'
|
|
94
|
-
os: '
|
|
95
|
-
model: '
|
|
96
|
-
osVersion: '
|
|
97
|
-
modelId: '
|
|
98
|
-
},
|
|
99
|
-
'samsung-s23-ultra': {
|
|
100
|
-
os: 'android', platform: 3,
|
|
101
|
-
model: 'Samsung Galaxy S23 Ultra', manufacturer: 'Samsung',
|
|
102
|
-
osVersion: '14', osBuildNumber: 'UP1A.231005.007',
|
|
103
|
-
modelId: 'SM-S918B', deviceModelType: 2
|
|
104
|
-
},
|
|
105
|
-
'samsung-a55': {
|
|
106
|
-
os: 'android', platform: 3,
|
|
107
|
-
model: 'Samsung Galaxy A55', manufacturer: 'Samsung',
|
|
108
|
-
osVersion: '14', osBuildNumber: 'UP1A.231005.007',
|
|
109
|
-
modelId: 'SM-A556B', deviceModelType: 1
|
|
110
|
-
},
|
|
111
|
-
'pixel8pro': {
|
|
112
|
-
os: 'android', platform: 3,
|
|
113
|
-
model: 'Pixel 8 Pro', manufacturer: 'Google',
|
|
114
|
-
osVersion: '14', osBuildNumber: 'UP1A.231005.007',
|
|
115
|
-
modelId: 'Pixel 8 Pro', deviceModelType: 2
|
|
116
|
-
},
|
|
117
|
-
'pixel8': {
|
|
118
|
-
os: 'android', platform: 3,
|
|
119
|
-
model: 'Pixel 8', manufacturer: 'Google',
|
|
120
|
-
osVersion: '14', osBuildNumber: 'UP1A.231005.007',
|
|
121
|
-
modelId: 'Pixel 8', deviceModelType: 2
|
|
122
|
-
},
|
|
123
|
-
'pixel7': {
|
|
124
|
-
os: 'android', platform: 3,
|
|
125
|
-
model: 'Pixel 7', manufacturer: 'Google',
|
|
126
|
-
osVersion: '14', osBuildNumber: 'TP1A.221005.002',
|
|
127
|
-
modelId: 'Pixel 7', deviceModelType: 2
|
|
128
|
-
},
|
|
129
|
-
'pixel7a': {
|
|
130
|
-
os: 'android', platform: 3,
|
|
131
|
-
model: 'Pixel 7a', manufacturer: 'Google',
|
|
132
|
-
osVersion: '14', osBuildNumber: 'UP1A.231005.007',
|
|
133
|
-
modelId: 'Pixel 7a', deviceModelType: 1
|
|
134
|
-
},
|
|
135
|
-
'xiaomi14': {
|
|
136
|
-
os: 'android', platform: 3,
|
|
137
|
-
model: 'Xiaomi 14', manufacturer: 'Xiaomi',
|
|
138
|
-
osVersion: '14', osBuildNumber: 'UP1A.231005.007',
|
|
139
|
-
modelId: '23127PN0CG', deviceModelType: 2
|
|
140
|
-
},
|
|
141
|
-
'xiaomi13': {
|
|
142
|
-
os: 'android', platform: 3,
|
|
143
|
-
model: 'Xiaomi 13', manufacturer: 'Xiaomi',
|
|
144
|
-
osVersion: '13', osBuildNumber: 'TQ3A.230901.001',
|
|
145
|
-
modelId: '2211133G', deviceModelType: 2
|
|
146
|
-
},
|
|
147
|
-
'oneplus12': {
|
|
148
|
-
os: 'android', platform: 3,
|
|
149
|
-
model: 'OnePlus 12', manufacturer: 'OnePlus',
|
|
150
|
-
osVersion: '14', osBuildNumber: 'UP1A.231005.007',
|
|
151
|
-
modelId: 'CPH2573', deviceModelType: 2
|
|
152
|
-
},
|
|
153
|
-
'oneplus11': {
|
|
154
|
-
os: 'android', platform: 3,
|
|
155
|
-
model: 'OnePlus 11', manufacturer: 'OnePlus',
|
|
156
|
-
osVersion: '13', osBuildNumber: 'TQ3A.230901.001',
|
|
157
|
-
modelId: 'CPH2449', deviceModelType: 2
|
|
158
|
-
},
|
|
159
|
-
'oppo-find-x7': {
|
|
160
|
-
os: 'android', platform: 3,
|
|
161
|
-
model: 'OPPO Find X7', manufacturer: 'OPPO',
|
|
162
|
-
osVersion: '14', osBuildNumber: 'UP1A.231005.007',
|
|
163
|
-
modelId: 'CPH2599', deviceModelType: 2
|
|
164
|
-
},
|
|
165
|
-
'realme-gt5': {
|
|
166
|
-
os: 'android', platform: 3,
|
|
167
|
-
model: 'realme GT 5 Pro', manufacturer: 'realme',
|
|
168
|
-
osVersion: '14', osBuildNumber: 'UP1A.231005.007',
|
|
169
|
-
modelId: 'RMX3888', deviceModelType: 2
|
|
134
|
+
},
|
|
135
|
+
'iphonese3': {
|
|
136
|
+
os: 'ios', platform: 1,
|
|
137
|
+
model: 'iPhone SE (3rd generation)', manufacturer: 'Apple',
|
|
138
|
+
osVersion: '17.7.5', osBuildNumber: '21H516',
|
|
139
|
+
modelId: 'iPhone14,6', deviceModelType: 1
|
|
140
|
+
},
|
|
141
|
+
'iphone11pro': {
|
|
142
|
+
os: 'ios', platform: 1,
|
|
143
|
+
model: 'iPhone 11 Pro', manufacturer: 'Apple',
|
|
144
|
+
osVersion: '17.7.5', osBuildNumber: '21H516',
|
|
145
|
+
modelId: 'iPhone12,3', deviceModelType: 1
|
|
146
|
+
},
|
|
147
|
+
'iphone11': {
|
|
148
|
+
os: 'ios', platform: 1,
|
|
149
|
+
model: 'iPhone 11', manufacturer: 'Apple',
|
|
150
|
+
osVersion: '17.7.5', osBuildNumber: '21H516',
|
|
151
|
+
modelId: 'iPhone12,1', deviceModelType: 1
|
|
152
|
+
},
|
|
153
|
+
'iphonexs': {
|
|
154
|
+
os: 'ios', platform: 1,
|
|
155
|
+
model: 'iPhone Xs', manufacturer: 'Apple',
|
|
156
|
+
osVersion: '16.7.11', osBuildNumber: '20H330',
|
|
157
|
+
modelId: 'iPhone11,2', deviceModelType: 1
|
|
170
158
|
}
|
|
171
159
|
};
|
|
172
160
|
|
|
@@ -199,13 +187,10 @@ module.exports = {
|
|
|
199
187
|
MOBILE_PROLOGUE,
|
|
200
188
|
IOS_STATIC_TOKEN,
|
|
201
189
|
IOS_BUSINESS_STATIC_TOKEN,
|
|
202
|
-
ANDROID_STATIC_TOKEN,
|
|
203
190
|
IOS_VERSION_FALLBACK,
|
|
204
|
-
ANDROID_VERSION_FALLBACK,
|
|
205
191
|
IOS_USER_AGENT,
|
|
206
192
|
IOS_DEVICE,
|
|
207
193
|
IOS_DEVICE_PROFILES,
|
|
208
|
-
ANDROID_DEVICE_PROFILES,
|
|
209
194
|
SIGNAL_KEY_TYPE,
|
|
210
195
|
RELEASE_CHANNEL,
|
|
211
196
|
TAG
|
package/lib/noise.js
CHANGED
|
@@ -9,6 +9,7 @@ const { hkdf } = require('@noble/hashes/hkdf');
|
|
|
9
9
|
const { MOBILE_PROLOGUE, WHATSAPP_HOST, WHATSAPP_PORT } = require('./constants');
|
|
10
10
|
const { encodeHandshakeClientHello, encodeHandshakeClientFinish,
|
|
11
11
|
decodeServerHello, encodeClientPayload } = require('./proto');
|
|
12
|
+
const { parsePhone, getCountryMeta } = require('./Registration');
|
|
12
13
|
|
|
13
14
|
// ─── CertChain validator ────────
|
|
14
15
|
//
|
|
@@ -282,6 +283,8 @@ class NoiseSocket extends EventEmitter {
|
|
|
282
283
|
const sharedSS = dhShared(noisePriv, serverHello.ephemeral);
|
|
283
284
|
this.noiseState.mixKey(sharedSS);
|
|
284
285
|
|
|
286
|
+
const { cc: _cc } = parsePhone(this.store.phoneNumber);
|
|
287
|
+
const _meta = getCountryMeta(_cc);
|
|
285
288
|
const payload = encodeClientPayload({
|
|
286
289
|
username: BigInt(this.store.phoneNumber),
|
|
287
290
|
passive: false,
|
|
@@ -293,18 +296,18 @@ class NoiseSocket extends EventEmitter {
|
|
|
293
296
|
device: 0,
|
|
294
297
|
oc: false,
|
|
295
298
|
userAgent: {
|
|
296
|
-
platform:
|
|
299
|
+
platform: 1,
|
|
297
300
|
version: this.store.version,
|
|
298
|
-
mcc:
|
|
299
|
-
mnc:
|
|
301
|
+
mcc: _meta.mcc,
|
|
302
|
+
mnc: _meta.mnc,
|
|
300
303
|
osVersion: this.store.device.osVersion,
|
|
301
304
|
manufacturer: this.store.device.manufacturer,
|
|
302
305
|
device: this.store.device.model,
|
|
303
306
|
osBuildNumber: this.store.device.osBuildNumber,
|
|
304
307
|
phoneId: this.store.fdid.toUpperCase(),
|
|
305
308
|
releaseChannel: 0,
|
|
306
|
-
localeLanguage: 'en',
|
|
307
|
-
localeCountry: 'US',
|
|
309
|
+
localeLanguage: _meta.lg || 'en',
|
|
310
|
+
localeCountry: _meta.lc || 'US',
|
|
308
311
|
deviceType: 0,
|
|
309
312
|
deviceModelType: this.store.device.modelId
|
|
310
313
|
}
|