whalibmob 5.21.1 → 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/lib/Client.js CHANGED
@@ -574,20 +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
- // On Android, ask the way registration does — fetchWaVersion(), which
580
- // reads the version straight off the APK material when there is any. That
581
- // material is the exact build the token was computed from and the only
582
- // Android source that is actually reliable: the Play Store listing no
583
- // longer exposes a version to scrape, so fetchAndroidVersion() on its own
584
- // usually just hands back the pinned fallback and a session can never
585
- // climb past it. With the material preferred, refreshing it (e.g.
586
- // `wa apk-material --download`) is picked up on the next connect and
587
- // written through to <number>.json. fetchWaVersion falls through to the
588
- // Play Store lookup, and then the fallback, only when no material exists.
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.
589
593
  const live = android
590
- ? await fetchWaVersion(device)
594
+ ? await fetchAndroidVersionLive(business)
591
595
  : await fetchIosVersion(business);
592
596
  if (!live) return;
593
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.30.84';
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.1",
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",