whalibmob 5.1.21 → 5.5.21
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/README.md +76 -83
- package/cli.js +2 -2
- package/index.js +4 -2
- package/lib/DeviceConfig.js +51 -36
- package/lib/MediaService.js +1 -1
- package/lib/Registration.js +102 -97
- package/lib/Store.js +2 -2
- package/lib/constants.js +116 -101
- package/lib/noise.js +5 -8
- package/package.json +1 -1
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/README.md
CHANGED
|
@@ -1,22 +1,16 @@
|
|
|
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.14</div>
|
|
3
3
|
|
|
4
4
|
##
|
|
5
5
|
|
|
6
6
|
> [!CAUTION]
|
|
7
7
|
> Use a dedicated phone number with this library. Connecting with a number that is already active on a real device will cause WhatsApp to log that device out.
|
|
8
8
|
|
|
9
|
-
This product is free for everyone but if you want to support more whalibmob or others you can support me with some USD donations through Crypto at This product is free for everyone but if you want us to support more whalibmob or others you can support me with some USD donations through Crypto at 0x8AD64F47a715eC24DeF193FBb9aC64d4E857f0f3
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
9
|
> [!IMPORTANT]
|
|
16
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.
|
|
17
11
|
|
|
18
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.
|
|
19
|
-
- The library operates as a real **iOS mobile device
|
|
13
|
+
- The library operates as a real **iOS mobile device**, not as WhatsApp Web. It uses the Mobile API endpoint, which behaves differently from the Web API.
|
|
20
14
|
- Signal Protocol encryption is **fully inlined** in pure JavaScript — no native binaries, no node-gyp, runs anywhere Node.js runs.
|
|
21
15
|
|
|
22
16
|
## Install
|
|
@@ -190,8 +184,9 @@ npm install -g whalibmob
|
|
|
190
184
|
- [Media Encryption](#media-encryption)
|
|
191
185
|
- [Device Emulation](#device-emulation)
|
|
192
186
|
- [Quick Start](#device-quick-start)
|
|
193
|
-
- [
|
|
194
|
-
- [
|
|
187
|
+
- [iOS Profiles](#ios-profiles)
|
|
188
|
+
- [Android Profiles](#android-profiles)
|
|
189
|
+
- [Custom Device Fields](#custom-device-fields)
|
|
195
190
|
- [Version & Token Overrides](#version--token-overrides)
|
|
196
191
|
|
|
197
192
|
---
|
|
@@ -2191,20 +2186,18 @@ See the [Receiving Media](#receiving-media) section for a complete working code
|
|
|
2191
2186
|
|
|
2192
2187
|
## Device Emulation
|
|
2193
2188
|
|
|
2194
|
-
whalibmob
|
|
2195
|
-
The device profile controls the User-Agent header, the Noise Protocol `platform` field, and the token computation.
|
|
2196
|
-
|
|
2197
|
-
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.
|
|
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.
|
|
2198
2191
|
|
|
2199
2192
|
Configuration is done entirely through environment variables — no code changes required.
|
|
2200
2193
|
Copy `.env.example` to `.env` in your project root and set the variables you need.
|
|
2201
2194
|
|
|
2202
2195
|
### Device Quick Start
|
|
2203
2196
|
|
|
2204
|
-
Emulate an
|
|
2197
|
+
Emulate an Android Pixel 8 Pro:
|
|
2205
2198
|
|
|
2206
2199
|
```sh
|
|
2207
|
-
WA_DEVICE=
|
|
2200
|
+
WA_OS=android WA_DEVICE=pixel_8_pro node your-app.js
|
|
2208
2201
|
```
|
|
2209
2202
|
|
|
2210
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')`:
|
|
@@ -2215,86 +2208,86 @@ const { WhalibmobClient } = require('whalibmob')
|
|
|
2215
2208
|
```
|
|
2216
2209
|
|
|
2217
2210
|
```dotenv
|
|
2218
|
-
|
|
2211
|
+
WA_OS=android
|
|
2212
|
+
WA_DEVICE=pixel_8_pro
|
|
2219
2213
|
```
|
|
2220
2214
|
|
|
2221
|
-
|
|
2215
|
+
Emulate a custom Samsung device:
|
|
2222
2216
|
|
|
2223
|
-
|
|
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):
|
|
2224
2229
|
|
|
2225
2230
|
| Profile key | Device | iOS version |
|
|
2226
2231
|
|---|---|---|
|
|
2227
|
-
| `
|
|
2228
|
-
| `
|
|
2229
|
-
| `
|
|
2230
|
-
| `
|
|
2231
|
-
| `
|
|
2232
|
-
| `
|
|
2233
|
-
| `
|
|
2234
|
-
| `
|
|
2235
|
-
| `
|
|
2236
|
-
| `
|
|
2237
|
-
| `
|
|
2238
|
-
| `
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
|
2247
|
-
|
|
2248
|
-
User-Agent format: `WhatsApp/<version> iOS/<osVersion> Device/<model>`
|
|
2249
|
-
|
|
2250
|
-
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.
|
|
2251
|
-
|
|
2252
|
-
### Custom iPhone Fields
|
|
2253
|
-
|
|
2254
|
-
These variables override individual fields to emulate a specific iPhone not in the list above.
|
|
2255
|
-
|
|
2256
|
-
| Variable | Sent to WhatsApp as | Notes |
|
|
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 |
|
|
2257
2252
|
|---|---|---|
|
|
2258
|
-
| `
|
|
2259
|
-
| `
|
|
2260
|
-
| `
|
|
2261
|
-
| `
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
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:
|
|
2267
2276
|
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
const store = createNewStore('919634847671')
|
|
2277
|
-
await requestSmsCode(store, 'sms')
|
|
2278
|
-
```
|
|
2279
|
-
|
|
2280
|
-
**Example — custom iPhone via `.env` file:**
|
|
2281
|
-
|
|
2282
|
-
```dotenv
|
|
2283
|
-
WA_DEVICE_MODEL=iPhone 16 Pro Max
|
|
2284
|
-
WA_DEVICE_OS_VERSION=18.3.2
|
|
2285
|
-
WA_DEVICE_BUILD=22D82
|
|
2286
|
-
WA_DEVICE_MODEL_ID=iPhone17,2
|
|
2287
|
-
```
|
|
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`) |
|
|
2288
2284
|
|
|
2289
2285
|
### Version & Token Overrides
|
|
2290
2286
|
|
|
2291
2287
|
| Variable | Description |
|
|
2292
2288
|
|---|---|
|
|
2293
|
-
| `WA_VERSION` | Pin
|
|
2294
|
-
| `WA_STATIC_TOKEN` |
|
|
2295
|
-
|
|
2296
|
-
> [!NOTE]
|
|
2297
|
-
> **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.
|
|
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. |
|
|
2298
2291
|
|
|
2299
2292
|
## License
|
|
2300
2293
|
|
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 WA_DEVICE, WA_VERSION etc. without touching
|
|
5
|
+
// This lets users configure WA_OS, 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.21';
|
|
39
39
|
|
|
40
40
|
// ─── output helpers ───────────────────────────────────────────────────────────
|
|
41
41
|
|
package/index.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
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');
|
|
5
6
|
const { createNewStore, saveStore, loadStore, toSixParts, fromSixParts, storeToJson, storeFromJson } = require('./lib/Store');
|
|
6
7
|
const { checkIfRegistered, requestSmsCode, verifyCode } = require('./lib/Registration');
|
|
7
8
|
const { SignalProtocol } = require('./lib/signal/SignalProtocol');
|
|
@@ -18,11 +19,12 @@ module.exports = {
|
|
|
18
19
|
requestSmsCode,
|
|
19
20
|
verifyCode,
|
|
20
21
|
assertRegistrationKeys,
|
|
21
|
-
// Version fetch — fetchWaVersion
|
|
22
|
+
// Version fetch — use fetchWaVersion for device-aware (iOS or Android) fetching.
|
|
22
23
|
// fetchIosVersion is kept for backward compatibility.
|
|
23
24
|
fetchWaVersion,
|
|
24
25
|
fetchIosVersion,
|
|
25
|
-
|
|
26
|
+
fetchAndroidVersion,
|
|
27
|
+
// Device config — reads WA_OS / WA_DEVICE / WA_DEVICE_* from process.env
|
|
26
28
|
getDeviceConfig,
|
|
27
29
|
// Store helpers
|
|
28
30
|
createNewStore,
|
package/lib/DeviceConfig.js
CHANGED
|
@@ -2,65 +2,80 @@
|
|
|
2
2
|
|
|
3
3
|
const {
|
|
4
4
|
IOS_DEVICE,
|
|
5
|
-
IOS_DEVICE_PROFILES
|
|
5
|
+
IOS_DEVICE_PROFILES,
|
|
6
|
+
ANDROID_DEVICE_PROFILES
|
|
6
7
|
} = require('./constants');
|
|
7
8
|
|
|
8
|
-
// Pre-build normalised lookup
|
|
9
|
+
// Pre-build normalised lookup tables so that user-supplied profile keys in any
|
|
9
10
|
// separator style (underscores, dashes, spaces, or none) always resolve.
|
|
10
11
|
// Normalisation: lowercase, strip underscores / dashes / spaces.
|
|
11
12
|
function _normalise(s) { return String(s).toLowerCase().replace(/[\s_-]/g, ''); }
|
|
12
13
|
|
|
13
|
-
const _IOS_MAP
|
|
14
|
-
const
|
|
15
|
-
for (const k of Object.keys(IOS_DEVICE_PROFILES))
|
|
16
|
-
|
|
17
|
-
_IOS_KEYS.push(k);
|
|
18
|
-
}
|
|
14
|
+
const _IOS_MAP = {};
|
|
15
|
+
const _ANDROID_MAP = {};
|
|
16
|
+
for (const k of Object.keys(IOS_DEVICE_PROFILES)) _IOS_MAP[_normalise(k)] = IOS_DEVICE_PROFILES[k];
|
|
17
|
+
for (const k of Object.keys(ANDROID_DEVICE_PROFILES)) _ANDROID_MAP[_normalise(k)] = ANDROID_DEVICE_PROFILES[k];
|
|
19
18
|
|
|
20
19
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
21
|
-
// DeviceConfig — reads environment variables and returns the active
|
|
20
|
+
// DeviceConfig — reads environment variables and returns the active device
|
|
22
21
|
// profile to be used during registration and connection.
|
|
23
22
|
//
|
|
24
23
|
// Priority order:
|
|
25
|
-
// 1. WA_DEVICE
|
|
26
|
-
// 2. WA_DEVICE_*
|
|
27
|
-
// 3. No env vars
|
|
24
|
+
// 1. WA_OS + WA_DEVICE → pick a named predefined profile
|
|
25
|
+
// 2. WA_OS + individual WA_DEVICE_* vars → build a custom profile
|
|
26
|
+
// 3. No env vars → default iOS (iPhone 15 Pro)
|
|
28
27
|
//
|
|
29
28
|
// Env variables:
|
|
30
|
-
//
|
|
31
|
-
//
|
|
32
|
-
//
|
|
33
|
-
//
|
|
34
|
-
//
|
|
29
|
+
// WA_OS ios | android (default: ios)
|
|
30
|
+
// WA_DEVICE profile key (e.g. samsung-s24-ultra, pixel8, iphone14)
|
|
31
|
+
// WA_DEVICE_MODEL device display name
|
|
32
|
+
// WA_DEVICE_MANUFACTURER Apple | Samsung | Google …
|
|
33
|
+
// WA_DEVICE_OS_VERSION OS version string (e.g. 17.4.1 or 14)
|
|
34
|
+
// WA_DEVICE_BUILD OS build number
|
|
35
|
+
// WA_DEVICE_MODEL_ID internal model identifier (e.g. SM-S928B, iPhone16,1)
|
|
35
36
|
// WA_VERSION override WhatsApp version (skips live fetch if set)
|
|
36
|
-
// WA_STATIC_TOKEN override static registration token
|
|
37
|
+
// WA_STATIC_TOKEN override static registration token
|
|
37
38
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
38
39
|
|
|
39
40
|
function getDeviceConfig() {
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
const
|
|
44
|
-
|
|
45
|
-
|
|
41
|
+
const osType = (process.env.WA_OS || 'ios').toLowerCase().trim();
|
|
42
|
+
|
|
43
|
+
if (osType === 'android') {
|
|
44
|
+
const profileKey = _normalise(process.env.WA_DEVICE || 'samsungs24ultra');
|
|
45
|
+
const found = _ANDROID_MAP[profileKey] || null;
|
|
46
|
+
if (found) {
|
|
47
|
+
return Object.assign({}, found);
|
|
48
|
+
}
|
|
46
49
|
|
|
47
|
-
// 2. Custom profile via individual WA_DEVICE_* vars
|
|
48
|
-
if (process.env.WA_DEVICE_MODEL || process.env.WA_DEVICE_MODEL_ID) {
|
|
49
50
|
return {
|
|
50
|
-
os: '
|
|
51
|
-
platform:
|
|
52
|
-
model: process.env.WA_DEVICE_MODEL
|
|
53
|
-
manufacturer: '
|
|
54
|
-
osVersion: process.env.WA_DEVICE_OS_VERSION
|
|
55
|
-
osBuildNumber: process.env.WA_DEVICE_BUILD
|
|
56
|
-
modelId: process.env.WA_DEVICE_MODEL_ID
|
|
51
|
+
os: 'android',
|
|
52
|
+
platform: 3,
|
|
53
|
+
model: process.env.WA_DEVICE_MODEL || 'Samsung Galaxy S24 Ultra',
|
|
54
|
+
manufacturer: process.env.WA_DEVICE_MANUFACTURER || 'Samsung',
|
|
55
|
+
osVersion: process.env.WA_DEVICE_OS_VERSION || '14',
|
|
56
|
+
osBuildNumber: process.env.WA_DEVICE_BUILD || 'UP1A.231005.007',
|
|
57
|
+
modelId: process.env.WA_DEVICE_MODEL_ID || 'SM-S928B',
|
|
57
58
|
deviceModelType: 2
|
|
58
59
|
};
|
|
59
60
|
}
|
|
60
61
|
|
|
61
|
-
//
|
|
62
|
-
const
|
|
63
|
-
|
|
62
|
+
// iOS (default)
|
|
63
|
+
const profileKey = _normalise(process.env.WA_DEVICE || 'iphone15pro');
|
|
64
|
+
const found = _IOS_MAP[profileKey] || null;
|
|
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
|
+
};
|
|
64
79
|
}
|
|
65
80
|
|
|
66
81
|
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 `WhatsApp/${d.version || '2.26.
|
|
123
|
+
'User-Agent': (() => { const d = getDeviceConfig(); return d.os === 'android' ? `WhatsApp/${d.version || '2.26.7.75'} A` : `WhatsApp/${d.version || '2.26.7.75'} iOS/${d.osVersion} Device/${d.model}`; })()
|
|
124
124
|
}
|
|
125
125
|
};
|
|
126
126
|
|
package/lib/Registration.js
CHANGED
|
@@ -38,7 +38,9 @@ async function httpPostViaSocks(path, body, waVersion, proxyUrl) {
|
|
|
38
38
|
});
|
|
39
39
|
|
|
40
40
|
const _dev = getDeviceConfig();
|
|
41
|
-
const userAgent =
|
|
41
|
+
const userAgent = _dev.os === 'android'
|
|
42
|
+
? `WhatsApp/${waVersion} A`
|
|
43
|
+
: `WhatsApp/${waVersion} iOS/${_dev.osVersion} Device/${_dev.model}`;
|
|
42
44
|
const req = [
|
|
43
45
|
`POST /v2${path} HTTP/1.1`,
|
|
44
46
|
`Host: ${dHost}`,
|
|
@@ -88,7 +90,9 @@ const {
|
|
|
88
90
|
REGISTRATION_ENDPOINT,
|
|
89
91
|
REGISTRATION_PUBLIC_KEY,
|
|
90
92
|
IOS_STATIC_TOKEN,
|
|
93
|
+
ANDROID_STATIC_TOKEN,
|
|
91
94
|
IOS_VERSION_FALLBACK,
|
|
95
|
+
ANDROID_VERSION_FALLBACK,
|
|
92
96
|
IOS_USER_AGENT,
|
|
93
97
|
IOS_DEVICE,
|
|
94
98
|
SIGNAL_KEY_TYPE,
|
|
@@ -227,93 +231,93 @@ function parsePhone(phoneNumber) {
|
|
|
227
231
|
return { cc: cc1, national: String(BigInt(str.slice(1))) };
|
|
228
232
|
}
|
|
229
233
|
|
|
230
|
-
// ---------- WhatsApp version fetch (from
|
|
234
|
+
// ---------- WhatsApp version fetch (iOS from iTunes, Android from Play Store) ----------
|
|
231
235
|
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
const VERSION_CACHE_TTL_MS = 6 * 60 * 60 * 1000; // 6 hours
|
|
235
|
-
let _versionCache = { version: null, fetchedAt: 0 };
|
|
236
|
+
let _cachedIosVersion = null;
|
|
237
|
+
let _cachedAndroidVersion = null;
|
|
236
238
|
|
|
237
|
-
|
|
238
|
-
|
|
239
|
+
async function fetchIosVersion() {
|
|
240
|
+
if (_cachedIosVersion) return _cachedIosVersion;
|
|
239
241
|
return new Promise((resolve) => {
|
|
240
|
-
const req = https.get(
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
242
|
+
const req = https.get(
|
|
243
|
+
'https://itunes.apple.com/lookup?bundleId=net.whatsapp.WhatsApp',
|
|
244
|
+
{ headers: { 'User-Agent': IOS_USER_AGENT } },
|
|
245
|
+
(res) => {
|
|
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
|
+
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
|
+
});
|
|
245
259
|
}
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
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); });
|
|
260
|
+
);
|
|
261
|
+
req.on('error', () => resolve(IOS_VERSION_FALLBACK));
|
|
262
|
+
req.setTimeout(8000, () => { req.destroy(); resolve(IOS_VERSION_FALLBACK); });
|
|
263
263
|
});
|
|
264
264
|
}
|
|
265
265
|
|
|
266
|
-
//
|
|
267
|
-
//
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
return
|
|
266
|
+
// Fetch latest WhatsApp Android version from Google Play Store.
|
|
267
|
+
// Parses the 4-part "2.x.x.x" version string embedded in the page JSON data.
|
|
268
|
+
// Falls back to ANDROID_VERSION_FALLBACK on any error.
|
|
269
|
+
async function fetchAndroidVersion() {
|
|
270
|
+
if (_cachedAndroidVersion) return _cachedAndroidVersion;
|
|
271
|
+
try {
|
|
272
|
+
const axios = require('axios');
|
|
273
|
+
const resp = await axios.get(
|
|
274
|
+
'https://play.google.com/store/apps/details?id=com.whatsapp&hl=en&gl=us',
|
|
275
|
+
{
|
|
276
|
+
headers: {
|
|
277
|
+
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36',
|
|
278
|
+
'Accept-Language': 'en-US,en;q=0.9'
|
|
279
|
+
},
|
|
280
|
+
timeout: 12000
|
|
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
291
|
}
|
|
292
|
-
}
|
|
293
292
|
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
293
|
+
// Secondary: unquoted version adjacent to "WhatsApp" text (catches alternate HTML structures).
|
|
294
|
+
const secondary = html.match(/WhatsApp[^<"]{0,200}?(2\.\d+\.\d+\.\d+)/);
|
|
295
|
+
if (secondary) {
|
|
296
|
+
_cachedAndroidVersion = secondary[1];
|
|
297
|
+
return secondary[1];
|
|
298
|
+
}
|
|
299
|
+
} catch (_) {}
|
|
300
|
+
return ANDROID_VERSION_FALLBACK;
|
|
297
301
|
}
|
|
298
302
|
|
|
299
|
-
// Return the
|
|
300
|
-
// If WA_VERSION is set in the environment, that value is always used
|
|
301
|
-
async function fetchWaVersion() {
|
|
303
|
+
// Return the appropriate WhatsApp version for the active device.
|
|
304
|
+
// If WA_VERSION is set in the environment, that value is always used.
|
|
305
|
+
async function fetchWaVersion(device) {
|
|
302
306
|
if (process.env.WA_VERSION) return process.env.WA_VERSION;
|
|
303
|
-
return fetchIosVersion();
|
|
307
|
+
return (device && device.os === 'android') ? fetchAndroidVersion() : fetchIosVersion();
|
|
304
308
|
}
|
|
305
309
|
|
|
306
310
|
// ---------- Token computation ----------
|
|
307
|
-
//
|
|
308
|
-
//
|
|
309
|
-
//
|
|
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.
|
|
311
|
+
// token = MD5( staticToken + MD5hex(waVersion) + nationalNumber )
|
|
312
|
+
// The static token depends on the platform (iOS vs Android).
|
|
313
|
+
// Override with WA_STATIC_TOKEN in .env if the bundled token becomes stale.
|
|
312
314
|
|
|
313
315
|
function computeToken(waVersion, national) {
|
|
314
|
-
const
|
|
316
|
+
const device = getDeviceConfig();
|
|
317
|
+
const staticToken = process.env.WA_STATIC_TOKEN
|
|
318
|
+
|| (device.os === 'android' ? ANDROID_STATIC_TOKEN : IOS_STATIC_TOKEN);
|
|
315
319
|
const versionHashHex = md5Bytes(waVersion).toString('hex');
|
|
316
|
-
return md5Hex(
|
|
320
|
+
return md5Hex(staticToken + versionHashHex + national);
|
|
317
321
|
}
|
|
318
322
|
|
|
319
323
|
// ---------- Byte helpers ----------
|
|
@@ -368,31 +372,24 @@ function buildPayload(store, waVersion, useToken, extraPairs) {
|
|
|
368
372
|
const { cc, national } = parsePhone(store.phoneNumber);
|
|
369
373
|
const token = useToken ? computeToken(waVersion, national) : null;
|
|
370
374
|
const fdid = store.fdid.toUpperCase();
|
|
371
|
-
const meta = getCountryMeta(cc);
|
|
372
375
|
|
|
373
376
|
return buildForm([
|
|
374
|
-
'cc',
|
|
375
|
-
'in',
|
|
376
|
-
'rc',
|
|
377
|
-
'lg',
|
|
378
|
-
'lc',
|
|
379
|
-
'
|
|
380
|
-
'
|
|
381
|
-
'
|
|
382
|
-
'
|
|
383
|
-
'
|
|
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)),
|
|
377
|
+
'cc', cc,
|
|
378
|
+
'in', national,
|
|
379
|
+
'rc', String(RELEASE_CHANNEL),
|
|
380
|
+
'lg', 'en',
|
|
381
|
+
'lc', 'US',
|
|
382
|
+
'authkey', toBase64Url(stripKeyPrefix(store.noiseKeyPair.public)),
|
|
383
|
+
'e_regid', toBase64Url(intToBytes(store.registrationId, 4)),
|
|
384
|
+
'e_keytype', toBase64Url(Buffer.from([SIGNAL_KEY_TYPE])),
|
|
385
|
+
'e_ident', toBase64Url(stripKeyPrefix(store.identityKeyPair.public)),
|
|
386
|
+
'e_skey_id', toBase64Url(intToBytes(store.signedPreKey.id, 3)),
|
|
390
387
|
'e_skey_val', toBase64Url(stripKeyPrefix(store.signedPreKey.public)),
|
|
391
388
|
'e_skey_sig', toBase64Url(store.signedPreKey.signature),
|
|
392
|
-
'fdid',
|
|
393
|
-
'expid',
|
|
394
|
-
'id',
|
|
395
|
-
'token',
|
|
389
|
+
'fdid', fdid,
|
|
390
|
+
'expid', toBase64Url(store.deviceId),
|
|
391
|
+
'id', toUrlHex(store.identityId),
|
|
392
|
+
'token', token
|
|
396
393
|
], extraPairs);
|
|
397
394
|
}
|
|
398
395
|
|
|
@@ -419,7 +416,9 @@ function httpPost(path, body, waVersion) {
|
|
|
419
416
|
|
|
420
417
|
return new Promise((resolve, reject) => {
|
|
421
418
|
const _httpDev = getDeviceConfig();
|
|
422
|
-
const userAgent =
|
|
419
|
+
const userAgent = _httpDev.os === 'android'
|
|
420
|
+
? `WhatsApp/${waVersion} A`
|
|
421
|
+
: `WhatsApp/${waVersion} iOS/${_httpDev.osVersion} Device/${_httpDev.model}`;
|
|
423
422
|
const bodyBuf = Buffer.from(body, 'utf8');
|
|
424
423
|
const opts = {
|
|
425
424
|
hostname: 'v.whatsapp.net',
|
|
@@ -462,7 +461,7 @@ async function sendRequest(path, store, waVersion, useToken, extraPairs) {
|
|
|
462
461
|
// ---------- Public API ----------
|
|
463
462
|
|
|
464
463
|
async function checkIfRegistered(store) {
|
|
465
|
-
const waVersion = await fetchWaVersion();
|
|
464
|
+
const waVersion = await fetchWaVersion(getDeviceConfig());
|
|
466
465
|
return sendRequest('/exist', store, waVersion, false, null);
|
|
467
466
|
}
|
|
468
467
|
|
|
@@ -491,9 +490,13 @@ async function checkNumberStatus(phoneNumber) {
|
|
|
491
490
|
|
|
492
491
|
phoneNumber = String(phoneNumber).replace(/\D/g, '');
|
|
493
492
|
const store = createNewStore(phoneNumber);
|
|
494
|
-
const waVersion = await fetchWaVersion();
|
|
493
|
+
const waVersion = await fetchWaVersion(getDeviceConfig());
|
|
494
|
+
const { cc } = parsePhone(phoneNumber);
|
|
495
|
+
const meta = getCountryMeta(cc);
|
|
495
496
|
|
|
496
497
|
const baseExtra = [
|
|
498
|
+
'sim_mcc', meta.mcc,
|
|
499
|
+
'sim_mnc', meta.mnc,
|
|
497
500
|
'reason', '',
|
|
498
501
|
'cellular_strength', '1'
|
|
499
502
|
];
|
|
@@ -589,7 +592,7 @@ async function assertRegistrationKeys(store, waVersion) {
|
|
|
589
592
|
async function requestSmsCode(store, method) {
|
|
590
593
|
method = method || 'sms';
|
|
591
594
|
const _device = getDeviceConfig();
|
|
592
|
-
const waVersion = await fetchWaVersion();
|
|
595
|
+
const waVersion = await fetchWaVersion(_device);
|
|
593
596
|
store.version = waVersion;
|
|
594
597
|
store.device = _device;
|
|
595
598
|
|
|
@@ -601,6 +604,8 @@ async function requestSmsCode(store, method) {
|
|
|
601
604
|
async function _tryMethod(m) {
|
|
602
605
|
const extra = [
|
|
603
606
|
'method', m,
|
|
607
|
+
'sim_mcc', '000',
|
|
608
|
+
'sim_mnc', '000',
|
|
604
609
|
'reason', '',
|
|
605
610
|
'cellular_strength', '1'
|
|
606
611
|
];
|
|
@@ -676,7 +681,7 @@ async function requestSmsCode(store, method) {
|
|
|
676
681
|
|
|
677
682
|
async function verifyCode(store, code) {
|
|
678
683
|
const _device = getDeviceConfig();
|
|
679
|
-
const waVersion = await fetchWaVersion();
|
|
684
|
+
const waVersion = await fetchWaVersion(_device);
|
|
680
685
|
store.version = waVersion;
|
|
681
686
|
store.device = _device;
|
|
682
687
|
const normalized = code.replace(/[\s\-]/g, '').replace(/\D/g, '');
|
|
@@ -701,4 +706,4 @@ async function verifyCode(store, code) {
|
|
|
701
706
|
throw new Error(`Verification failed: ${reason || JSON.stringify(result)}`);
|
|
702
707
|
}
|
|
703
708
|
|
|
704
|
-
module.exports = { checkIfRegistered, checkNumberStatus, requestSmsCode, verifyCode, fetchIosVersion, fetchWaVersion, parsePhone,
|
|
709
|
+
module.exports = { checkIfRegistered, checkNumberStatus, requestSmsCode, verifyCode, fetchIosVersion, fetchAndroidVersion, fetchWaVersion, parsePhone, 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 } = require('./constants');
|
|
8
|
+
const { IOS_DEVICE, IOS_VERSION_FALLBACK, ANDROID_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 = IOS_VERSION_FALLBACK;
|
|
63
|
+
const version = device.os === 'android' ? ANDROID_VERSION_FALLBACK : IOS_VERSION_FALLBACK;
|
|
64
64
|
|
|
65
65
|
return {
|
|
66
66
|
phoneNumber: String(phoneNumber).replace(/^\+/, ''),
|
package/lib/constants.js
CHANGED
|
@@ -7,16 +7,18 @@ const REGISTRATION_PUBLIC_KEY = Buffer.from('8e8c0f74c3ebc5d7a6865c6c3c843856b06
|
|
|
7
7
|
|
|
8
8
|
const MOBILE_PROLOGUE = Buffer.from([0x57, 0x41, 0x05, 0x03]);
|
|
9
9
|
|
|
10
|
-
//
|
|
10
|
+
// Static tokens used in WhatsApp registration token computation.
|
|
11
|
+
// token = MD5( staticToken + MD5hex(waVersion) + nationalNumber )
|
|
11
12
|
const IOS_STATIC_TOKEN = '0a1mLfGUIBVrMKF1RdvLI5lkRBvof6vn0fD2QRSM';
|
|
12
13
|
const IOS_BUSINESS_STATIC_TOKEN = 'USUDuDYDeQhY4RF2fCSp5m3F6kJ1M2J8wS7bbNA2';
|
|
14
|
+
const ANDROID_STATIC_TOKEN = 'Y29Cs6AVNR2bj5PBeKSYFd1nAKuvNQ3h';
|
|
13
15
|
|
|
14
|
-
// WhatsApp version
|
|
15
|
-
|
|
16
|
-
const
|
|
16
|
+
// WhatsApp version fallbacks — used when live fetch fails.
|
|
17
|
+
const IOS_VERSION_FALLBACK = '2.26.9.75';
|
|
18
|
+
const ANDROID_VERSION_FALLBACK = '2.24.13.80';
|
|
17
19
|
|
|
18
20
|
// Safari UA for the iTunes lookup only.
|
|
19
|
-
const IOS_USER_AGENT = 'Mozilla/5.0 (iPhone; CPU iPhone OS
|
|
21
|
+
const IOS_USER_AGENT = 'Mozilla/5.0 (iPhone; CPU iPhone OS 17_4_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.4.1 Mobile/15E148 Safari/604.1';
|
|
20
22
|
|
|
21
23
|
// Default iOS device (iPhone 15 Pro, iOS 17.4.1).
|
|
22
24
|
// platform 1 = iOS in WhatsApp's ClientPayload.UserAgent proto enum.
|
|
@@ -32,129 +34,139 @@ const IOS_DEVICE = {
|
|
|
32
34
|
};
|
|
33
35
|
|
|
34
36
|
// ─── iOS device profiles ───────────────────────────────────────────────────
|
|
35
|
-
// Set WA_DEVICE=<key>
|
|
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.
|
|
37
|
+
// Set WA_OS=ios WA_DEVICE=<key> in .env to select one.
|
|
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
|
-
},
|
|
69
39
|
'iphone15pro': {
|
|
70
40
|
os: 'ios', platform: 1,
|
|
71
41
|
model: 'iPhone 15 Pro', manufacturer: 'Apple',
|
|
72
|
-
osVersion: '
|
|
42
|
+
osVersion: '17.4.1', osBuildNumber: '21E236',
|
|
73
43
|
modelId: 'iPhone16,1', deviceModelType: 2
|
|
74
44
|
},
|
|
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
|
-
},
|
|
81
45
|
'iphone15': {
|
|
82
46
|
os: 'ios', platform: 1,
|
|
83
47
|
model: 'iPhone 15', manufacturer: 'Apple',
|
|
84
|
-
osVersion: '
|
|
48
|
+
osVersion: '17.4.1', osBuildNumber: '21E236',
|
|
85
49
|
modelId: 'iPhone15,4', deviceModelType: 2
|
|
86
50
|
},
|
|
87
|
-
'
|
|
51
|
+
'iphone14': {
|
|
88
52
|
os: 'ios', platform: 1,
|
|
89
|
-
model: 'iPhone 14
|
|
90
|
-
osVersion: '
|
|
91
|
-
modelId: '
|
|
53
|
+
model: 'iPhone 14', manufacturer: 'Apple',
|
|
54
|
+
osVersion: '17.4.1', osBuildNumber: '21E236',
|
|
55
|
+
modelId: 'iPhone14,2', deviceModelType: 2
|
|
92
56
|
},
|
|
93
57
|
'iphone14pro': {
|
|
94
58
|
os: 'ios', platform: 1,
|
|
95
59
|
model: 'iPhone 14 Pro', manufacturer: 'Apple',
|
|
96
|
-
osVersion: '
|
|
60
|
+
osVersion: '17.4.1', osBuildNumber: '21E236',
|
|
97
61
|
modelId: 'iPhone15,2', deviceModelType: 2
|
|
98
62
|
},
|
|
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
|
-
},
|
|
117
63
|
'iphone13': {
|
|
118
64
|
os: 'ios', platform: 1,
|
|
119
65
|
model: 'iPhone 13', manufacturer: 'Apple',
|
|
120
|
-
osVersion: '
|
|
66
|
+
osVersion: '16.7.8', osBuildNumber: '20H343',
|
|
121
67
|
modelId: 'iPhone14,5', deviceModelType: 2
|
|
122
68
|
},
|
|
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
|
-
},
|
|
129
69
|
'iphone12': {
|
|
130
70
|
os: 'ios', platform: 1,
|
|
131
71
|
model: 'iPhone 12', manufacturer: 'Apple',
|
|
132
|
-
osVersion: '
|
|
72
|
+
osVersion: '15.8.3', osBuildNumber: '19H384',
|
|
133
73
|
modelId: 'iPhone13,2', deviceModelType: 1
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
'
|
|
142
|
-
os: '
|
|
143
|
-
model: '
|
|
144
|
-
osVersion: '
|
|
145
|
-
modelId: '
|
|
146
|
-
},
|
|
147
|
-
'
|
|
148
|
-
os: '
|
|
149
|
-
model: '
|
|
150
|
-
osVersion: '
|
|
151
|
-
modelId: '
|
|
152
|
-
},
|
|
153
|
-
'
|
|
154
|
-
os: '
|
|
155
|
-
model: '
|
|
156
|
-
osVersion: '
|
|
157
|
-
modelId: '
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
// ─── Android device profiles ───────────────────────────────────────────────
|
|
78
|
+
// Set WA_OS=android WA_DEVICE=<key> in .env to select one.
|
|
79
|
+
// platform 3 = ANDROID in WhatsApp's ClientPayload.UserAgent proto enum.
|
|
80
|
+
const ANDROID_DEVICE_PROFILES = {
|
|
81
|
+
'samsung-s24-ultra': {
|
|
82
|
+
os: 'android', platform: 3,
|
|
83
|
+
model: 'Samsung Galaxy S24 Ultra', manufacturer: 'Samsung',
|
|
84
|
+
osVersion: '14', osBuildNumber: 'UP1A.231005.007',
|
|
85
|
+
modelId: 'SM-S928B', deviceModelType: 2
|
|
86
|
+
},
|
|
87
|
+
'samsung-s24': {
|
|
88
|
+
os: 'android', platform: 3,
|
|
89
|
+
model: 'Samsung Galaxy S24', manufacturer: 'Samsung',
|
|
90
|
+
osVersion: '14', osBuildNumber: 'UP1A.231005.007',
|
|
91
|
+
modelId: 'SM-S921B', deviceModelType: 2
|
|
92
|
+
},
|
|
93
|
+
'samsung-s23': {
|
|
94
|
+
os: 'android', platform: 3,
|
|
95
|
+
model: 'Samsung Galaxy S23', manufacturer: 'Samsung',
|
|
96
|
+
osVersion: '14', osBuildNumber: 'UP1A.231005.007',
|
|
97
|
+
modelId: 'SM-S911B', deviceModelType: 2
|
|
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
|
|
158
170
|
}
|
|
159
171
|
};
|
|
160
172
|
|
|
@@ -187,10 +199,13 @@ module.exports = {
|
|
|
187
199
|
MOBILE_PROLOGUE,
|
|
188
200
|
IOS_STATIC_TOKEN,
|
|
189
201
|
IOS_BUSINESS_STATIC_TOKEN,
|
|
202
|
+
ANDROID_STATIC_TOKEN,
|
|
190
203
|
IOS_VERSION_FALLBACK,
|
|
204
|
+
ANDROID_VERSION_FALLBACK,
|
|
191
205
|
IOS_USER_AGENT,
|
|
192
206
|
IOS_DEVICE,
|
|
193
207
|
IOS_DEVICE_PROFILES,
|
|
208
|
+
ANDROID_DEVICE_PROFILES,
|
|
194
209
|
SIGNAL_KEY_TYPE,
|
|
195
210
|
RELEASE_CHANNEL,
|
|
196
211
|
TAG
|
package/lib/noise.js
CHANGED
|
@@ -9,7 +9,6 @@ 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');
|
|
13
12
|
|
|
14
13
|
// ─── CertChain validator ────────
|
|
15
14
|
//
|
|
@@ -283,8 +282,6 @@ class NoiseSocket extends EventEmitter {
|
|
|
283
282
|
const sharedSS = dhShared(noisePriv, serverHello.ephemeral);
|
|
284
283
|
this.noiseState.mixKey(sharedSS);
|
|
285
284
|
|
|
286
|
-
const { cc: _cc } = parsePhone(this.store.phoneNumber);
|
|
287
|
-
const _meta = getCountryMeta(_cc);
|
|
288
285
|
const payload = encodeClientPayload({
|
|
289
286
|
username: BigInt(this.store.phoneNumber),
|
|
290
287
|
passive: false,
|
|
@@ -296,18 +293,18 @@ class NoiseSocket extends EventEmitter {
|
|
|
296
293
|
device: 0,
|
|
297
294
|
oc: false,
|
|
298
295
|
userAgent: {
|
|
299
|
-
platform: 1,
|
|
296
|
+
platform: (this.store.device && this.store.device.platform) || 1,
|
|
300
297
|
version: this.store.version,
|
|
301
|
-
mcc:
|
|
302
|
-
mnc:
|
|
298
|
+
mcc: '000',
|
|
299
|
+
mnc: '000',
|
|
303
300
|
osVersion: this.store.device.osVersion,
|
|
304
301
|
manufacturer: this.store.device.manufacturer,
|
|
305
302
|
device: this.store.device.model,
|
|
306
303
|
osBuildNumber: this.store.device.osBuildNumber,
|
|
307
304
|
phoneId: this.store.fdid.toUpperCase(),
|
|
308
305
|
releaseChannel: 0,
|
|
309
|
-
localeLanguage:
|
|
310
|
-
localeCountry:
|
|
306
|
+
localeLanguage: 'en',
|
|
307
|
+
localeCountry: 'US',
|
|
311
308
|
deviceType: 0,
|
|
312
309
|
deviceModelType: this.store.device.modelId
|
|
313
310
|
}
|