whalibmob 5.21.0 → 5.22.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 CHANGED
@@ -132,6 +132,8 @@ npm install -g whalibmob
132
132
  - [Client Options](#client-options)
133
133
  - [Linking to an Existing Account (Pairing Code or QR)](#linking-to-an-existing-account-pairing-code-or-qr)
134
134
  - [Requesting a Pairing Code](#requesting-a-pairing-code)
135
+ - [Linking by QR Code](#linking-by-qr-code)
136
+ - [After the Link — Same for Both Paths](#after-the-link--same-for-both-paths)
135
137
  - [Reconnecting a Linked Session](#reconnecting-a-linked-session)
136
138
  - [Choosing Your Own Code](#choosing-your-own-code)
137
139
  - [Options](#options)
@@ -153,13 +155,20 @@ npm install -g whalibmob
153
155
  - [The Number WhatsApp Files Your Account Under](#the-number-whatsapp-files-your-account-under)
154
156
  - [When Registration Is Refused for Consent](#when-registration-is-refused-for-consent)
155
157
  - [The Push Token](#the-push-token)
158
+ - [Receiving the Code over Push](#receiving-the-code-over-push-without-typing-it)
156
159
  - [Routing Traffic Through a Proxy](#routing-traffic-through-a-proxy)
160
+ - [What Goes Through It](#what-goes-through-it)
157
161
  - [Saving & Restoring Sessions](#saving--restoring-sessions)
162
+ - [Keeping the Announced Version Current](#keeping-the-announced-version-current)
163
+ - [One-time Pre-keys](#one-time-pre-keys)
164
+ - [Where the Folder Comes From](#where-the-folder-comes-from)
165
+ - [Working Out the Paths Yourself](#working-out-the-paths-yourself)
158
166
  - [Signal Store Utilities](#signal-store-utilities)
159
167
  - [makeCacheableSignalKeyStore](#makecacheablesignalkeystore)
160
168
  - [addTransactionCapability](#addtransactioncapability)
161
169
  - [assertMeId](#assertmeid)
162
170
  - [initAuthCreds](#initauthcreds)
171
+ - [Recommended Stacking Pattern](#recommended-stacking-pattern)
163
172
  - [Handling Events](#handling-events)
164
173
  - [Example to Start](#example-to-start)
165
174
  - [All Events](#all-events)
@@ -248,11 +257,13 @@ npm install -g whalibmob
248
257
  - [Query Metadata](#query-metadata)
249
258
  - [Get Request Join List](#get-request-join-list)
250
259
  - [Approve / Reject Request Join](#approve--reject-request-join)
251
- - [Personal Invitations](#personal-invitations)
260
+ - [Personal Invitations](#personal-invitations-1)
252
261
  - [Toggle Ephemeral in Group](#toggle-ephemeral-in-group)
253
262
  - [WhatsApp IDs](#whatsapp-ids)
254
263
  - [Transport](#transport)
255
264
  - [Media Encryption](#media-encryption)
265
+ - [Sending (Upload Flow)](#sending-upload-flow)
266
+ - [Receiving (Download + Decrypt Flow)](#receiving-download--decrypt-flow)
256
267
  - [Device Emulation](#device-emulation)
257
268
  - [Quick Start](#device-quick-start)
258
269
  - [iOS Profiles](#ios-profiles)
@@ -261,6 +272,7 @@ npm install -g whalibmob
261
272
  - [Version & Token Overrides](#version--token-overrides)
262
273
  - [When the server answers 405 on connect](#when-the-server-answers-405-on-connect)
263
274
  - [Finding out what a 405 objects to](#finding-out-what-a-405-objects-to)
275
+ - [License](#license)
264
276
 
265
277
  ---
266
278
 
@@ -1616,6 +1628,10 @@ wa> /quit
1616
1628
 
1617
1629
  ## Library API
1618
1630
 
1631
+ Everything the CLI does is available as a Node.js library. The sections below
1632
+ cover connecting an account, sending and receiving every message type, groups,
1633
+ communities, channels, presence, privacy, history sync, and device emulation.
1634
+
1619
1635
  ## Connecting Account
1620
1636
 
1621
1637
  ### Register a New Number
package/lib/Client.js CHANGED
@@ -574,10 +574,24 @@ class WhalibmobClient extends EventEmitter {
574
574
  const source = android ? 'Play Store listing' : 'App Store listing';
575
575
 
576
576
  try {
577
- const { compareVersions } = require('./Registration');
577
+ const { compareVersions, fetchAndroidVersionLive } = require('./Registration');
578
578
  const before = this._store.version;
579
+ // Connecting and registering ask different questions, and they must not
580
+ // share an answer.
581
+ //
582
+ // Registration announces the build its token was computed from, which is
583
+ // the APK material and nothing else — see fetchWaVersion().
584
+ //
585
+ // Connecting announces the build the store is serving *now*, because a
586
+ // version the server no longer recognises is what a 405 is. The material
587
+ // is the wrong source for that: it ages the moment Play moves on, and a
588
+ // session pinned to it would keep announcing an older build forever
589
+ // without anything looking broken.
590
+ //
591
+ // fetchAndroidVersionLive asks Play's own API first and only falls back
592
+ // to the store page, which no longer carries a version worth reading.
579
593
  const live = android
580
- ? await fetchAndroidVersion(business)
594
+ ? await fetchAndroidVersionLive(business)
581
595
  : await fetchIosVersion(business);
582
596
  if (!live) return;
583
597
 
@@ -553,6 +553,7 @@ const VERSION_CACHE_TTL_MS = 6 * 60 * 60 * 1000;
553
553
 
554
554
  const _cachedIosVersion = {};
555
555
  const _cachedAndroidVersion = {};
556
+ const _cachedPlayVersion = {};
556
557
 
557
558
  function _cachedVersion(cache, key) {
558
559
  const entry = cache[key];
@@ -578,6 +579,84 @@ function _cacheVersion(cache, key, value) {
578
579
  function clearVersionCache() {
579
580
  for (const k of Object.keys(_cachedIosVersion)) delete _cachedIosVersion[k];
580
581
  for (const k of Object.keys(_cachedAndroidVersion)) delete _cachedAndroidVersion[k];
582
+ for (const k of Object.keys(_cachedPlayVersion)) delete _cachedPlayVersion[k];
583
+ }
584
+
585
+ // How long to let the Play lookup run before giving up on it. This is on the
586
+ // connect path, so a slow answer has to become "no answer" rather than a
587
+ // connection that hangs; the caller keeps whatever version it already had.
588
+ const PLAY_VERSION_TIMEOUT_MS = 20000;
589
+
590
+ /**
591
+ * The version Google Play is actually serving, read through Play's own API.
592
+ *
593
+ * This is the Android counterpart of the iTunes lookup, and the only Android
594
+ * source that can be trusted. The store *page* stopped carrying a version to
595
+ * scrape: fetchAndroidVersion() below now usually returns the pinned fallback,
596
+ * and when it does find a number on the page it can be the wrong one — a live
597
+ * run had it read 2.26.27.85 off a listing that was serving 2.26.31.77, which
598
+ * is older than the fallback it was supposed to improve on.
599
+ *
600
+ * Play's device-facing API answers with the real thing. It needs a Google
601
+ * account, which the Aurora dispenser hands out anonymously — a free
602
+ * third-party service that rate-limits, hence the six-hour memoisation shared
603
+ * with the other lookups and the timeout above. A failure here is not an
604
+ * error: the caller falls back to the scrape and then to the pinned version.
605
+ *
606
+ * @param {boolean} business the w4b package rather than the consumer one
607
+ * @returns {Promise<string|null>} e.g. "2.26.31.77", or null if Play did not answer
608
+ */
609
+ async function fetchPlayVersion(business) {
610
+ const key = business ? 'business' : 'personal';
611
+ const hit = _cachedVersion(_cachedPlayVersion, key);
612
+ if (hit) return hit;
613
+
614
+ const packageName = business ? WHATSAPP_BUSINESS_PACKAGE : WHATSAPP_PACKAGE;
615
+ const PlayStore = require('./PlayStore');
616
+
617
+ const lookup = (async () => {
618
+ const auth = await PlayStore.fetchAnonymousAuth();
619
+ const headers = PlayStore.buildFdfeHeaders(auth);
620
+ const info = await PlayStore.fetchVersion(packageName, headers);
621
+ return info && info.versionName ? info.versionName : null;
622
+ })();
623
+
624
+ let timer;
625
+ const deadline = new Promise((resolve) => {
626
+ timer = setTimeout(() => resolve(null), PLAY_VERSION_TIMEOUT_MS);
627
+ });
628
+
629
+ let version = null;
630
+ try {
631
+ version = await Promise.race([lookup, deadline]);
632
+ } catch (err) {
633
+ _whaDbg('[DBG] PLAY_VERSION lookup failed: ' + (err && err.message));
634
+ version = null;
635
+ } finally {
636
+ if (timer) clearTimeout(timer);
637
+ }
638
+ // The race leaves the lookup running when the deadline wins; it must not take
639
+ // the process down if it rejects after nobody is waiting on it any more.
640
+ lookup.catch(() => {});
641
+
642
+ if (!version) return null;
643
+ _whaDbg('[DBG] PLAY_VERSION ' + packageName + ' -> ' + version);
644
+ return _cacheVersion(_cachedPlayVersion, key, version);
645
+ }
646
+
647
+ /**
648
+ * The current Android version for a client that is connecting, best source
649
+ * first: Play's API, then the store page, then the pinned fallback.
650
+ *
651
+ * Registration does not come through here — it reads the version off the APK
652
+ * material it computed its token from, and the two must not be confused. See
653
+ * fetchWaVersion().
654
+ */
655
+ async function fetchAndroidVersionLive(business) {
656
+ const fromPlay = await fetchPlayVersion(business);
657
+ if (fromPlay) return fromPlay;
658
+ _whaDbg('[DBG] PLAY_VERSION unavailable — falling back to the store page');
659
+ return fetchAndroidVersion(business);
581
660
  }
582
661
 
583
662
  async function fetchIosVersion(business) {
@@ -2098,7 +2177,7 @@ async function verifyCode(store, code, opts) {
2098
2177
  throw new Error(`Verification failed: ${reason || JSON.stringify(result)}`);
2099
2178
  }
2100
2179
 
2101
- module.exports = { checkIfRegistered, checkNumberStatus, requestSmsCode, verifyCode, fetchIosVersion, fetchAndroidVersion, fetchWaVersion, currentVersionFor, refreshSessionVersion, clearVersionCache, tryLoadAndroidMaterial, compareVersions, parsePhone, getCountryMeta, assertRegistrationKeys, parseSocksProxy, socksProxyUrl };
2180
+ module.exports = { checkIfRegistered, checkNumberStatus, requestSmsCode, verifyCode, fetchIosVersion, fetchAndroidVersion, fetchPlayVersion, fetchAndroidVersionLive, fetchWaVersion, currentVersionFor, refreshSessionVersion, clearVersionCache, tryLoadAndroidMaterial, compareVersions, parsePhone, getCountryMeta, assertRegistrationKeys, parseSocksProxy, socksProxyUrl };
2102
2181
 
2103
2182
  // The HTTP reader, exposed for tests. Not part of the public API.
2104
2183
  module.exports._http = { readHttpResponse, parseHttpResponse, decodeChunkedBody };
package/lib/constants.js CHANGED
@@ -48,7 +48,7 @@ const IOS_BUSINESS_STATIC_TOKEN = 'USUDuDYDeQhY4RF2fCSp5m3F6kJ1M2J8wS7bbNA2';
48
48
  // reason nothing in the error names. Keep them within a few releases of what
49
49
  // the stores are actually serving; the live lookup is what normally decides.
50
50
  const IOS_VERSION_FALLBACK = '2.26.9.75';
51
- const ANDROID_VERSION_FALLBACK = '2.26.29.73';
51
+ const ANDROID_VERSION_FALLBACK = '2.26.31.77';
52
52
 
53
53
  // Safari UA for the iTunes lookup only.
54
54
  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';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "whalibmob",
3
- "version": "5.21.0",
3
+ "version": "5.22.0",
4
4
  "description": "WhatsApp library for interaction with WhatsApp Mobile API and web ",
5
5
  "author": "Kunboruto20",
6
6
  "main": "index.js",