whalibmob 5.29.6 → 5.30.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -0
- package/lib/Registration.js +62 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1845,6 +1845,9 @@ Both are optional and both keep working when omitted — you get an error naming
|
|
|
1845
1845
|
> [!NOTE]
|
|
1846
1846
|
> Registration reports the screens it passes through to WhatsApp's `/client_log`, the way the phone clients do — a client that registers in total silence does something no real installation does. It is fire-and-forget and every failure is swallowed, so it can never take a registration down. Set `WA_FUNNEL_LOG=0` to send none of it.
|
|
1847
1847
|
|
|
1848
|
+
> [!NOTE]
|
|
1849
|
+
> Those events carry timestamps, and registration waits between them the way a person would: a few seconds to type the number in, a moment on the confirmation sheet, longer before asking again after a refusal. Without the waits the whole funnel leaves inside one millisecond, which no handset does. It adds a handful of seconds to a registration. Set `WA_REG_PACING=0` to remove them.
|
|
1850
|
+
|
|
1848
1851
|
### Registering as Android
|
|
1849
1852
|
|
|
1850
1853
|
**There is nothing to do first.** Name the platform and register:
|
package/lib/Registration.js
CHANGED
|
@@ -1339,10 +1339,17 @@ function getAppPid(store) {
|
|
|
1339
1339
|
return store._appPid;
|
|
1340
1340
|
}
|
|
1341
1341
|
|
|
1342
|
-
|
|
1342
|
+
// is_sim_absent used to be the constant false while the same request could send
|
|
1343
|
+
// sim_mcc/sim_mnc as 000/000 — which is precisely what a handset reports when
|
|
1344
|
+
// there is no SIM in it. One request then said both "a SIM is present" and "no
|
|
1345
|
+
// operator", which no real handset ever says. Reading it off the operator the
|
|
1346
|
+
// request actually declares keeps the two halves telling one story.
|
|
1347
|
+
function buildClientMetrics(attempt, meta) {
|
|
1348
|
+
const mcc = meta && meta.mcc != null ? String(meta.mcc).trim() : '';
|
|
1349
|
+
const simAbsent = !/^\d+$/.test(mcc) || /^0+$/.test(mcc);
|
|
1343
1350
|
const json = '{"attempts":' + (attempt || 1)
|
|
1344
1351
|
+ ',"app_campaign_download_source":"google-play|unknown"'
|
|
1345
|
-
+ ',"is_sim_absent":false}';
|
|
1352
|
+
+ ',"is_sim_absent":' + (simAbsent ? 'true' : 'false') + '}';
|
|
1346
1353
|
return encodeURIComponent(json);
|
|
1347
1354
|
}
|
|
1348
1355
|
|
|
@@ -1383,7 +1390,7 @@ function getRequestVerificationCodeParameters(store, method, meta, device, attem
|
|
|
1383
1390
|
'prefer_sms_over_flash', wantsFlash ? 'false' : 'true',
|
|
1384
1391
|
'simnum', '0',
|
|
1385
1392
|
'airplane_mode_type', '0',
|
|
1386
|
-
'client_metrics', buildClientMetrics(attempt),
|
|
1393
|
+
'client_metrics', buildClientMetrics(attempt, meta),
|
|
1387
1394
|
'mistyped', '7',
|
|
1388
1395
|
'advertising_id', store.advertisingId || '',
|
|
1389
1396
|
'hasinrc', '1',
|
|
@@ -1641,6 +1648,45 @@ async function sendEncrypted(path, plaintext, store, waVersion) {
|
|
|
1641
1648
|
return result;
|
|
1642
1649
|
}
|
|
1643
1650
|
|
|
1651
|
+
// ---------- Pacing ----------
|
|
1652
|
+
//
|
|
1653
|
+
// The funnel events say a person walked through the screens. The timestamps on
|
|
1654
|
+
// them said otherwise: session_start, the number lookup and the code request
|
|
1655
|
+
// left within a few milliseconds of each other, because nothing in between was
|
|
1656
|
+
// waiting on a person. Nobody opens WhatsApp and has a phone number typed,
|
|
1657
|
+
// checked and submitted inside one millisecond, and that gap is visible to the
|
|
1658
|
+
// server on every event it receives.
|
|
1659
|
+
//
|
|
1660
|
+
// So the waits a person actually causes are put back. Each one is a range
|
|
1661
|
+
// rather than a number — a fixed delay is its own signature — and each is
|
|
1662
|
+
// named after the thing being waited for:
|
|
1663
|
+
//
|
|
1664
|
+
// enter_number typing the number in before the lookup fires
|
|
1665
|
+
// confirm_number the "is this your number?" sheet, and tapping through it
|
|
1666
|
+
// retry_code after a refused request, before asking again
|
|
1667
|
+
// switch_method picking a different delivery method out of the list
|
|
1668
|
+
//
|
|
1669
|
+
// Set WA_REG_PACING=0 to drop all of it, for anything that wants the request
|
|
1670
|
+
// shapes without the waiting.
|
|
1671
|
+
const PACING_RANGES_MS = {
|
|
1672
|
+
enter_number: [1800, 5200],
|
|
1673
|
+
confirm_number: [1200, 3600],
|
|
1674
|
+
retry_code: [2800, 7500],
|
|
1675
|
+
switch_method: [2000, 5000]
|
|
1676
|
+
};
|
|
1677
|
+
|
|
1678
|
+
function pacingEnabled() {
|
|
1679
|
+
return process.env.WA_REG_PACING !== '0';
|
|
1680
|
+
}
|
|
1681
|
+
|
|
1682
|
+
function humanPause(kind) {
|
|
1683
|
+
const range = PACING_RANGES_MS[kind];
|
|
1684
|
+
if (!range || !pacingEnabled()) return Promise.resolve();
|
|
1685
|
+
const ms = range[0] + Math.floor(Math.random() * (range[1] - range[0] + 1));
|
|
1686
|
+
_whaDbg('[DBG] REG pacing ' + kind + ' ' + ms + 'ms');
|
|
1687
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
1688
|
+
}
|
|
1689
|
+
|
|
1644
1690
|
// ---------- Funnel telemetry ----------
|
|
1645
1691
|
//
|
|
1646
1692
|
// The native client reports every screen it moves through — the number entry,
|
|
@@ -2042,10 +2088,13 @@ async function checkNumberStatus(phoneNumber) {
|
|
|
2042
2088
|
async function assertRegistrationKeys(store, waVersion) {
|
|
2043
2089
|
// The session-start event the native client fires before it knows the number.
|
|
2044
2090
|
await sendPrePnFunnelLog(store, waVersion, 'session_start', 'registration_session_start');
|
|
2091
|
+
// The number gets typed between those two events.
|
|
2092
|
+
await humanPause('enter_number');
|
|
2045
2093
|
await sendFunnelLog(store, waVersion, 'enter_number', 'exist_check', 'exist_attempt');
|
|
2046
2094
|
|
|
2047
2095
|
for (let attempt = 0; attempt < 2; attempt++) {
|
|
2048
2096
|
try {
|
|
2097
|
+
if (attempt > 0) await humanPause('retry_code');
|
|
2049
2098
|
logDeviceIdentity('before /exist', store, waVersion);
|
|
2050
2099
|
const result = await sendRequest('/exist', store, waVersion, false, null);
|
|
2051
2100
|
// reason === 'incorrect' → keys not found → fresh
|
|
@@ -2140,6 +2189,8 @@ async function requestSmsCode(store, method, opts) {
|
|
|
2140
2189
|
// 7. Unknown error first time → retry once
|
|
2141
2190
|
let lastReason = null;
|
|
2142
2191
|
let attemptNum = 1;
|
|
2192
|
+
// The confirmation sheet the app puts up before it will ask for a code.
|
|
2193
|
+
await humanPause('confirm_number');
|
|
2143
2194
|
while (true) {
|
|
2144
2195
|
// Rebuilt per attempt so client_metrics carries the current attempt count.
|
|
2145
2196
|
const extra = getRequestVerificationCodeParameters(store, m, _regMeta, _device, attemptNum);
|
|
@@ -2204,6 +2255,9 @@ async function requestSmsCode(store, method, opts) {
|
|
|
2204
2255
|
if (attemptNum > MAX_CODE_REQUEST_ATTEMPTS) {
|
|
2205
2256
|
throw new Error(`Registration error (${m}): giving up after ${MAX_CODE_REQUEST_ATTEMPTS} attempts — raw: ${JSON.stringify(result)}`);
|
|
2206
2257
|
}
|
|
2258
|
+
|
|
2259
|
+
// Nobody re-taps the button the instant the error lands.
|
|
2260
|
+
await humanPause('retry_code');
|
|
2207
2261
|
}
|
|
2208
2262
|
}
|
|
2209
2263
|
|
|
@@ -2214,6 +2268,8 @@ async function requestSmsCode(store, method, opts) {
|
|
|
2214
2268
|
if (result && result._noRoutes && !autoFallbackDone && method !== 'email') {
|
|
2215
2269
|
autoFallbackDone = true;
|
|
2216
2270
|
process.stderr.write(`[REG] ${method} returned no_routes — auto-trying ${fallbackMethod}\n`);
|
|
2271
|
+
// Reading the refusal and picking another method off the list.
|
|
2272
|
+
await humanPause('switch_method');
|
|
2217
2273
|
result = await _tryMethod(fallbackMethod);
|
|
2218
2274
|
}
|
|
2219
2275
|
|
|
@@ -2373,3 +2429,6 @@ module.exports._verify = {
|
|
|
2373
2429
|
flashCodeFromCallerId, flashCodeLength, codeForSubmission,
|
|
2374
2430
|
getRequestVerificationCodeParameters
|
|
2375
2431
|
};
|
|
2432
|
+
|
|
2433
|
+
// Pacing internals, exposed for tests. Not part of the public API.
|
|
2434
|
+
module.exports._pacing = { pacingEnabled, humanPause, buildClientMetrics, PACING_RANGES_MS };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "whalibmob",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.30.0",
|
|
4
4
|
"description": "Node.js library for WhatsApp — register a number over SMS, or link as a companion by QR. Signal E2E encryption, media, groups, channels.",
|
|
5
5
|
"author": "Kunboruto20",
|
|
6
6
|
"main": "index.js",
|