whalibmob 5.23.1 → 5.23.3

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 CHANGED
@@ -1,5 +1,28 @@
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.14</div>
1
+ <div align="center">
2
+
3
+ # whalibmob
4
+
5
+ **A Node.js library for WhatsApp that can register a phone number of its own.**
6
+
7
+ Every other JavaScript library links to an account that already exists on someone's phone.
8
+ whalibmob does that too — but it can also take a bare phone number, request the SMS or voice
9
+ code, and bring the account into being. Both transports, one API.
10
+
11
+ [![npm](https://img.shields.io/npm/v/whalibmob?style=for-the-badge&color=25D366&label=npm)](https://www.npmjs.com/package/whalibmob)
12
+ [![node](https://img.shields.io/node/v/whalibmob?style=for-the-badge&color=339933&label=node)](https://nodejs.org)
13
+ [![license](https://img.shields.io/npm/l/whalibmob?style=for-the-badge&color=555555)](LICENSE)
14
+
15
+ ### Start here
16
+
17
+ [![Register a number](https://img.shields.io/badge/Register_a_number-SMS_or_voice-25D366?style=for-the-badge)](#register-a-new-number)
18
+ [![Link an account](https://img.shields.io/badge/Link_an_account-QR_or_pairing_code-128C7E?style=for-the-badge)](#linking-to-an-existing-account-pairing-code-or-qr)
19
+ [![Use the CLI](https://img.shields.io/badge/Use_the_CLI-no_code_needed-075E54?style=for-the-badge)](#cli--getting-started)
20
+
21
+ [![Library API](https://img.shields.io/badge/Library_API-Node.js-34B7F1?style=for-the-badge)](#library-api)
22
+ [![Send messages](https://img.shields.io/badge/Send_messages-text_media_polls-34B7F1?style=for-the-badge)](#sending-messages)
23
+ [![Handle events](https://img.shields.io/badge/Handle_events-incoming_%26_receipts-34B7F1?style=for-the-badge)](#handling-events)
24
+
25
+ </div>
3
26
 
4
27
  ##
5
28
 
@@ -7,9 +30,9 @@
7
30
 
8
31
 
9
32
 
10
- CONTACT ME ON TELEGRAM IF YOU WANT TO WORK WITH ME AND IF YOU HAVE PROBLEM WITH WHALIBMOB : @brtyu545
11
33
 
12
- **Need test numbers to try whalibmob with?** Message me on Telegram at **@brtyu545** — I can provide phone numbers for receiving SMS verification codes, so you can register and test whalibmob without using your own number.
34
+
35
+
13
36
 
14
37
  If you want News about whalibmob enter this whalibmob channel: https://t.me/+sHN4MDCyB7U5OWY0
15
38
 
@@ -26,9 +49,10 @@ If you want News about whalibmob enter this whalibmob channel: https://t.me/+sHN
26
49
  > [!IMPORTANT]
27
50
  > 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.
28
51
 
29
- - 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.
30
- - The library operates as a real **iOS mobile device**, using the Mobile API endpoint, which behaves differently from the Web API.
31
- - It **also speaks WhatsApp Web over a WebSocket**. When a number cannot receive an SMS, or is already in use on a phone, whalibmob can link itself to that existing account with an **8-character pairing code** and run as one of its linked devices — with full message history and the account's address book. See [Linking to an Existing Account](#linking-to-an-existing-account-pairing-code-or-qr). The API is identical in both modes.
52
+ - **It registers numbers.** Give it a phone number that has never been on WhatsApp, request the code over SMS, voice call, flash call or an old WhatsApp account, confirm it, and the account exists — as an **Android or iOS device**, on the Mobile API. No phone, no scanning, no existing account to borrow. See [Register a New Number](#register-a-new-number).
53
+ - **It also speaks WhatsApp Web**, over a WebSocket. When a number cannot receive an SMS, or is already live on a phone, whalibmob links itself to that account by **QR code** or an **8-character pairing code** and runs as one of its linked devices — with the full message history and the account's address book. See [Linking to an Existing Account](#linking-to-an-existing-account-pairing-code-or-qr).
54
+ - **The API is identical in both modes.** Everything below — sending, media, groups, events — reads the same whichever way the session was created.
55
+ - No browser, no Selenium, no external runtime. It talks to WhatsApp directly over a **TCP socket** with the **Noise Protocol** handshake.
32
56
  - Signal Protocol encryption is **fully inlined** in pure JavaScript — no native binaries, no node-gyp, runs anywhere Node.js runs.
33
57
 
34
58
  ## Install
@@ -1689,7 +1713,7 @@ if (result.status === 'ok') {
1689
1713
  }
1690
1714
  ```
1691
1715
 
1692
- **Optional — let the code arrive by itself.** The two steps above are the whole flow, and nothing about them changes if you do nothing else. But because registration now sends a Firebase push token (see [The Push Token](#the-push-token)), WhatsApp *may* also deliver the six-digit code as a silent push. Open a listener for it before requesting the code, and the code can come back with nothing typed:
1716
+ **Optional — let the code arrive by itself.** The two steps above are the whole flow, and nothing about them changes if you do nothing else. But because an Android registration sends a Firebase push token (see [The Push Token](#the-push-token)), WhatsApp *may* also deliver the six-digit code as a silent push. Open a listener for it before requesting the code, and the code can come back with nothing typed — on an `WA_OS=android` profile; on iOS it resolves `null` straight away, since only Firebase is implemented:
1693
1717
 
1694
1718
  ```js
1695
1719
  const { receivePushCode } = require('whalibmob')
@@ -2579,7 +2603,9 @@ If that is refused too, the number has to go through the real app once, on a pho
2579
2603
 
2580
2604
  ## The Push Token
2581
2605
 
2582
- Every WhatsApp on a real phone holds a Firebase push token. It is the address Google uses to wake the app, and no install exists without one — so a registration that ships no `push_token` describes a WhatsApp that cannot be notified, which is a device that does not exist.
2606
+ Every WhatsApp on a real phone holds a push token. It is the address the push network uses to wake the app, and no install exists without one — so a registration that ships no `push_token` describes a WhatsApp that cannot be notified, which is a device that does not exist.
2607
+
2608
+ **This is an Android-profile feature.** Which push network an install uses follows from its platform: Android holds a *Firebase* token and keeps a stream open to Google, iOS holds an *APNs* token and keeps one open to Apple. They are not interchangeable, and the registration server sees both the token and the User-Agent naming the platform that sent it — an iPhone presenting a Firebase token describes a device nobody ships. Only the Firebase side is implemented here, so everything in this section applies when `WA_OS=android`. An iOS registration sends no `push_token` at all, which is the correct thing for a client with no push line, and every other part of the flow is unaffected.
2583
2609
 
2584
2610
  The token does two distinct jobs, and it is easy to conflate them:
2585
2611
 
@@ -2613,6 +2639,8 @@ Turn it off with `WA_FCM_PUSH=0`.
2613
2639
 
2614
2640
  This is the receiving end of job 2 above. When WhatsApp sends the code as a silent push, something has to be listening on the Firebase line to catch it — the same long-lived connection every Android phone keeps open to Google. `receivePushCode(store, device)` opens it: a TLS stream to `mtalk.google.com:5228` speaking the MCS protocol, logged in with the Firebase identity, resolving with the code the moment a push carrying it arrives.
2615
2641
 
2642
+ Like the token itself, this is Android-only. On an iOS profile `receivePushCode` resolves `null` immediately rather than holding a listener open on a line no push can reach, and `/reg push` says so and stops instead of waiting out its timeout. Check with `supportsPush(store.device)` if you want to branch on it.
2643
+
2616
2644
  The order matters. Open the listener **first**, so the line is live before the code is requested; then request the code by whatever method; then await it.
2617
2645
 
2618
2646
  ```js
package/cli.js CHANGED
@@ -2431,7 +2431,7 @@ async function handleLine(line) {
2431
2431
  break;
2432
2432
  }
2433
2433
  const method = (p[3] && !p[3].startsWith('--')) ? p[3] : 'sms';
2434
- const { receivePushCode } = require('./lib/fcm');
2434
+ const { pushClientFor } = require('./lib/PushClient');
2435
2435
 
2436
2436
  sessionDirFor(_sessDir, ph, { create: true });
2437
2437
  const sessFile = storeFileFor(_sessDir, ph);
@@ -2439,6 +2439,23 @@ async function handleLine(line) {
2439
2439
  if (!store) { store = initAuthCreds(ph, { name: regName }); saveStore(store, sessFile); }
2440
2440
  if (!store.device) store.device = getDeviceConfig();
2441
2441
 
2442
+ // Push verification needs a push line, and only Android has one here.
2443
+ // An iOS session holds no Firebase identity, so the listener would sit
2444
+ // for three minutes on a push that can never be routed to it. Say so
2445
+ // now instead, and point at the two things that do work.
2446
+ const pushClient = pushClientFor(store.device);
2447
+ if (!pushClient.supportsPush) {
2448
+ fail('push verification is not available for this device profile (' +
2449
+ (store.device.os || 'unknown') + ')');
2450
+ out(' the code arrives over the push line of the platform being announced,');
2451
+ out(' and only Android has one implemented (Firebase). iOS needs APNs.');
2452
+ out(' either register this number on an Android profile:');
2453
+ out(' WA_OS=android (see /device) and re-run /reg push ' + ph);
2454
+ out(' or use the ordinary path: /reg code ' + ph + ' then /reg confirm ' + ph + ' <code>');
2455
+ break;
2456
+ }
2457
+ const receivePushCode = (s, d, o) => pushClient.receivePushCode(s, d, o);
2458
+
2442
2459
  out('opening Firebase push listener (this can take a moment)...');
2443
2460
  // Open the listener first so the push has somewhere to land. onReady
2444
2461
  // fires once MCS is logged in — only then is it safe to ask for the
package/index.js CHANGED
@@ -61,10 +61,21 @@ module.exports = {
61
61
  checkIfRegistered,
62
62
  requestSmsCode,
63
63
  verifyCode,
64
- // Receive the verification code as a silent Firebase push, without an SMS —
65
- // opens the MCS listener the native client keeps to Google. See "Receiving
66
- // the code over push" in the README.
67
- receivePushCode: (store, device, opts) => require('./lib/fcm').receivePushCode(store, device, opts),
64
+ // Receive the verification code as a silent push, without an SMS — opens the
65
+ // listener the native client of that platform keeps open. See "Receiving the
66
+ // code over push" in the README.
67
+ //
68
+ // Routed through the push client for the device's platform: Android opens the
69
+ // Firebase MCS stream, iOS resolves null because APNs is not implemented and
70
+ // an iOS session holds no Firebase identity to listen with.
71
+ receivePushCode: (store, device, opts) => {
72
+ const dev = device || (store && store.device);
73
+ return require('./lib/PushClient')
74
+ .pushClientFor(dev)
75
+ .receivePushCode(store, dev, opts);
76
+ },
77
+ // Whether the device profile can do push verification at all.
78
+ supportsPush: (device) => require('./lib/PushClient').supportsPush(device),
68
79
  assertRegistrationKeys,
69
80
  // Version fetch — use fetchWaVersion for device-aware (iOS or Android) fetching.
70
81
  // fetchIosVersion is kept for backward compatibility.
@@ -0,0 +1,106 @@
1
+ 'use strict';
2
+
3
+ // ─── Push clients ─────────────────────────────────────────────────────────────
4
+ //
5
+ // Every real WhatsApp install can be woken by the server, and which line it is
6
+ // woken on follows from the platform it runs on. An Android install holds a
7
+ // Firebase token and keeps an MCS stream open to Google. An iOS install holds
8
+ // an APNs token and keeps a courier stream open to Apple. The two are not
9
+ // interchangeable, and the registration server sees both halves: the push token
10
+ // in the body, and the User-Agent that says which platform sent it. An iPhone
11
+ // presenting a Firebase token describes a device that does not exist.
12
+ //
13
+ // This module is the seam. `pushClientFor(device)` returns the client that
14
+ // matches the device profile, so no caller has to reach for a transport by
15
+ // name. Two implementations exist today:
16
+ //
17
+ // android → ./fcm (checkin → FIS install → register3, then MCS for the code)
18
+ // ios → NONE (APNs is not implemented; every push field is omitted)
19
+ //
20
+ // The NONE client is not a failure path — it is the correct answer for a
21
+ // platform we cannot speak push for. It supplies nothing, `buildForm` drops the
22
+ // null, and the registration body goes out without a push token rather than
23
+ // with somebody else's.
24
+
25
+ const { dbg: _whaDbg } = require('./logger');
26
+
27
+ /**
28
+ * The push client for a platform we have no transport for.
29
+ *
30
+ * Supplies nothing and never performs I/O. Registration proceeds without any
31
+ * push field, which is what an install with no push line should send.
32
+ */
33
+ const NONE_PUSH_CLIENT = {
34
+ platform: 'none',
35
+ supportsPush: false,
36
+
37
+ async getPushToken() {
38
+ return null;
39
+ },
40
+
41
+ async receivePushCode() {
42
+ return null;
43
+ }
44
+ };
45
+
46
+ /**
47
+ * The push client backed by Firebase Cloud Messaging, for Android profiles.
48
+ *
49
+ * Delegates to ./fcm, which owns the Google handshake and the MCS stream. The
50
+ * requires are lazy so that loading this module does not pull in the whole FCM
51
+ * stack for a session that never registers.
52
+ */
53
+ const FCM_PUSH_CLIENT = {
54
+ platform: 'android',
55
+ supportsPush: true,
56
+
57
+ async getPushToken(store, device) {
58
+ return require('./fcm').getPushToken(store, device);
59
+ },
60
+
61
+ async receivePushCode(store, device, opts) {
62
+ return require('./fcm').receivePushCode(store, device, opts);
63
+ }
64
+ };
65
+
66
+ /**
67
+ * The push client matching a device profile.
68
+ *
69
+ * Android profiles get the Firebase client; everything else gets NONE. The
70
+ * choice is made on the device rather than on a global setting, because a
71
+ * process can register an Android session and an iOS session in turn and each
72
+ * has to present its own platform's push line — or none at all.
73
+ *
74
+ * @param {object} device a device config; only `device.os` is read
75
+ * @returns {{platform: string, supportsPush: boolean,
76
+ * getPushToken: function, receivePushCode: function}}
77
+ */
78
+ function pushClientFor(device) {
79
+ if (device && device.os === 'android') return FCM_PUSH_CLIENT;
80
+
81
+ if (device && device.os) {
82
+ _whaDbg('[DBG] no push transport for ' + device.os + ' — push fields omitted');
83
+ }
84
+ return NONE_PUSH_CLIENT;
85
+ }
86
+
87
+ /**
88
+ * Whether this device profile can send and receive push at all.
89
+ *
90
+ * Callers that want to offer push verification check this first, so they can
91
+ * say why it is unavailable instead of opening a listener that nothing will
92
+ * ever reach.
93
+ *
94
+ * @param {object} device a device config; only `device.os` is read
95
+ * @returns {boolean}
96
+ */
97
+ function supportsPush(device) {
98
+ return pushClientFor(device).supportsPush;
99
+ }
100
+
101
+ module.exports = {
102
+ pushClientFor,
103
+ supportsPush,
104
+ NONE_PUSH_CLIENT,
105
+ FCM_PUSH_CLIENT
106
+ };
@@ -11,6 +11,7 @@ const { v4: uuidv4 } = require('uuid');
11
11
  const { getDeviceConfig } = require('./DeviceConfig');
12
12
  const AndroidApk = require('./AndroidApk');
13
13
  const attestation = require('./Attestation');
14
+ const { pushClientFor } = require('./PushClient');
14
15
  const { dbg: _whaDbg, warn: _whaWarn } = require('./logger');
15
16
 
16
17
  // ---------- Request envelope ----------
@@ -1207,6 +1208,20 @@ function buildClientMetrics(attempt) {
1207
1208
  return encodeURIComponent(json);
1208
1209
  }
1209
1210
 
1211
+ // A note on `push_code`, because it looks like an omission and is not.
1212
+ //
1213
+ // It is not the field that makes silent push verification work. The code
1214
+ // arrives over the push line *after* /code has been sent — that is the whole
1215
+ // point of it — so a code cannot be in the request that asks for it. Filling
1216
+ // this field by waiting for a push would hang /code forever on the push it is
1217
+ // itself supposed to trigger. The working flow is the other shape entirely:
1218
+ // open the MCS listener first, send /code, then await the push and hand what
1219
+ // arrives to /register. That is what `receivePushCode` and `/reg push` do.
1220
+ //
1221
+ // So the field stays absent. An empty value is worse than no value: the server
1222
+ // validates the shape of what it receives and answers a blank push_code with
1223
+ // bad_param/bad_format naming that field, while a field that is not there is
1224
+ // not validated at all. buildForm drops nulls, which is exactly what we want.
1210
1225
  function getRequestVerificationCodeParameters(store, method, meta, device, attempt) {
1211
1226
  if (device && device.os === 'android') {
1212
1227
  // Flash call is the one method whose delivery the server only routes when
@@ -1245,8 +1260,7 @@ function getRequestVerificationCodeParameters(store, method, meta, device, attem
1245
1260
  'manage_call_permission', wantsFlash ? 'true' : 'false',
1246
1261
  'clicked_education_link', 'false',
1247
1262
  'aid', '',
1248
- // Omitted unless a push client supplies a real code — an empty value is
1249
- // rejected as bad_format, an absent field is not validated at all.
1263
+ // Always absent — see the note above the function.
1250
1264
  'push_code', null
1251
1265
  ];
1252
1266
  }
@@ -1257,7 +1271,7 @@ function getRequestVerificationCodeParameters(store, method, meta, device, attem
1257
1271
  'sim_mcc', meta.mcc,
1258
1272
  'sim_mnc', meta.mnc,
1259
1273
  'jailbroken', '0',
1260
- // Omitted unless a push client supplies a real code (see above).
1274
+ // Always absent — see the note above the function.
1261
1275
  'push_code', null,
1262
1276
  'cellular_strength', '1'
1263
1277
  ];
@@ -1357,11 +1371,17 @@ async function buildPayload(store, waVersion, useToken, extraPairs) {
1357
1371
  .update(Buffer.concat([store.identityId, stripKeyPrefix(store.noiseKeyPair.public)]))
1358
1372
  .digest()
1359
1373
  );
1360
- // The push token a real install always has. Acquired from Google once and
1361
- // then cached on the store, so the two registration steps share one. Returns
1362
- // null on any failure, which drops the field and leaves the body exactly as
1363
- // it was before push tokens were wired up.
1364
- const pushToken = await require('./fcm').getPushToken(store, device);
1374
+ // The push token a real install always has, taken from the transport that
1375
+ // matches this device's platform: Firebase for Android, nothing for iOS until
1376
+ // APNs exists. Asking ./fcm directly would have put a Google token in an
1377
+ // iPhone's registration body — a device that keeps an MCS stream to Google
1378
+ // and announces itself as iOS is not a device anyone ships.
1379
+ //
1380
+ // Acquired once and cached on the store, so the two registration steps share
1381
+ // one. Returns null on any failure or on a platform with no transport, which
1382
+ // drops the field and leaves the body exactly as it was before push tokens
1383
+ // were wired up.
1384
+ const pushToken = await pushClientFor(device).getPushToken(store, device);
1365
1385
 
1366
1386
  const attestFields = await attestation.attestationFields(device, nonceB64, { pushToken });
1367
1387
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "whalibmob",
3
- "version": "5.23.1",
3
+ "version": "5.23.3",
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",