whalibmob 5.1.21 → 5.5.22

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.
@@ -4,7 +4,7 @@ const crypto = require('crypto');
4
4
  const path = require('path');
5
5
  const fs = require('fs');
6
6
  const { BinaryNode } = require('../BinaryNode');
7
- const { DeviceManager } = require('../DeviceManager');
7
+ const { DeviceManager, jidStrToObj } = require('../DeviceManager');
8
8
  const {
9
9
  encodeMessage,
10
10
  encodeText, encodeImageMessage, encodeVideoMessage,
@@ -553,12 +553,42 @@ class MessageSender {
553
553
  // Primary devices can always use pkmsg to establish new Signal sessions —
554
554
  // they simply omit the device-identity node (server accepts this for primaries).
555
555
  const allowPkmsg = true;
556
- const [recipientDevices, ownDevices] = await Promise.all([
556
+
557
+ // First, run the phone-based usync (and own devices) in parallel.
558
+ // IMPORTANT: _pnToLid may be empty RIGHT NOW but gets populated during this
559
+ // await — incoming messages from the recipient (which arrive while we wait
560
+ // for the usync timeout) tell us their LID JID via from=LID + sender_pn attrs.
561
+ // We therefore check _pnToLid AFTER this await, not before.
562
+ const [_recipientDevicesByPhone, ownDevices] = await Promise.all([
557
563
  this._devMgr.bulkEnsureSessions([recipientPhone], this._signal, allowPkmsg),
558
564
  this._devMgr.ensureOwnDeviceSessions(ownPhone, this._signal, allowPkmsg)
559
565
  ]);
560
566
 
561
- const otherJids = recipientDevices.length > 0 ? recipientDevices : [toJid];
567
+ // ── LID routing fix ───────────────────────────────────────────────────────
568
+ // Check _pnToLid NOW — after the await above, incoming messages from the
569
+ // recipient during the usync wait will have populated this map.
570
+ // For LID-migrated accounts, the message `to` field AND Signal participants
571
+ // must use the LID JID. Phone-JID messages are silently accepted by the
572
+ // server but never delivered to the recipient's LID-registered device.
573
+ const lidUser = this._client._pnToLid && this._client._pnToLid.get(recipientPhone);
574
+ const routingToJid = lidUser ? `${lidUser}@lid` : toJid;
575
+
576
+ process.stderr.write('[DBG] DM_ROUTE phone=' + recipientPhone +
577
+ ' routing=' + routingToJid + (lidUser ? ' (LID)' : ' (PN)') + '\n');
578
+
579
+ let otherJids;
580
+ if (lidUser) {
581
+ // Fetch bundle + build Signal session for the LID JID.
582
+ // skipUsync=true: we already know the LID from _pnToLid — skip a second
583
+ // 15 s usync round-trip and go straight to bundle fetch (device 0 primary).
584
+ const lidDevices = await this._devMgr.bulkEnsureSessionsForLid(
585
+ lidUser, this._signal, allowPkmsg, /* skipUsync */ true);
586
+ otherJids = lidDevices.length > 0 ? lidDevices : [routingToJid];
587
+ } else {
588
+ otherJids = _recipientDevicesByPhone.length > 0 ? _recipientDevicesByPhone : [toJid];
589
+ }
590
+ // ─────────────────────────────────────────────────────────────────────────
591
+
562
592
  const ownLinkedJids = ownDevices.filter(j => j !== ownMainJid);
563
593
 
564
594
  const allParticipants = [...otherJids, ...ownLinkedJids];
@@ -577,7 +607,10 @@ class MessageSender {
577
607
 
578
608
  const encryptedList = [...otherEncrypted, ...ownEncrypted];
579
609
 
580
- const stanzaAttrs = { to: toJid, id: msgId, type: mediaType, t: String(msNow()) };
610
+ // Encode the routing JID as a binary JID object (JID_PAIR for @lid / @s.whatsapp.net).
611
+ // Raw UTF-8 strings are not recognised by the server for LID recipients, causing
612
+ // the message to be accepted (server ACK) but never delivered to the device.
613
+ const stanzaAttrs = { to: jidStrToObj(routingToJid), id: msgId, type: mediaType, t: String(msNow()) };
581
614
  if (phash) stanzaAttrs.phash = phash;
582
615
  if (options.edit) stanzaAttrs.edit = String(options.edit);
583
616
 
@@ -599,6 +632,19 @@ class MessageSender {
599
632
 
600
633
  const msgNode = new BinaryNode('message', stanzaAttrs, msgContent);
601
634
 
635
+ // Debug: log outgoing stanza details
636
+ process.stderr.write('[DBG] DM_SEND to=' + toJid +
637
+ ' otherJids=[' + otherJids.join(',') + ']' +
638
+ ' ownLinked=[' + ownLinkedJids.join(',') + ']' +
639
+ ' encrypted=' + encryptedList.length +
640
+ ' hasPkmsg=' + hasPkmsg +
641
+ ' hasAdv=' + !!(advBytes) +
642
+ '\n');
643
+ if (encryptedList.length > 0) {
644
+ process.stderr.write('[DBG] DM_PARTICIPANTS ' +
645
+ encryptedList.map(e => e.jid + '(' + e.type + ')').join(', ') + '\n');
646
+ }
647
+
602
648
  // Cache plaintext so Client can re-send with fresh session on recipient retry
603
649
  if (this._client._sentMsgCache) {
604
650
  this._client._sentMsgCache.set(msgId, {
@@ -699,7 +745,7 @@ class MessageSender {
699
745
  msgContent.push(new BinaryNode('enc', skmsgAttrs, Buffer.isBuffer(skmsgCiphertext) ? skmsgCiphertext : Buffer.from(skmsgCiphertext)));
700
746
 
701
747
  const stanzaAttrs = {
702
- to: groupJid,
748
+ to: jidStrToObj(groupJid),
703
749
  id: msgId,
704
750
  type: mediaType,
705
751
  addressing_mode: groupAddressingMode,
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: _meta.mcc,
302
- mnc: _meta.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: _meta.lg || 'en',
310
- localeCountry: _meta.lc || 'US',
306
+ localeLanguage: 'en',
307
+ localeCountry: 'US',
311
308
  deviceType: 0,
312
309
  deviceModelType: this.store.device.modelId
313
310
  }
@@ -26,6 +26,7 @@ class SignalStore {
26
26
  this._signedPreKeys = {};
27
27
  this._identities = {};
28
28
  this._filePath = null;
29
+ this._lidMappings = {}; // phone → lid — persisted alongside sessions
29
30
 
30
31
  // Debounce state
31
32
  this._dirty = false;
@@ -56,9 +57,24 @@ class SignalStore {
56
57
  this._preKeys = raw.preKeys || {};
57
58
  this._signedPreKeys = raw.signedPreKeys || {};
58
59
  this._identities = raw.identities || {};
60
+ this._lidMappings = raw.lidMappings || {}; // phone → lid, persisted
59
61
  } catch (_) {}
60
62
  }
61
63
 
64
+ // Persist a phone ↔ LID mapping. Called any time we learn a new mapping from
65
+ // incoming messages or from a contact usync fallback query.
66
+ setLidMapping(phone, lid) {
67
+ if (!phone || !lid) return;
68
+ if (this._lidMappings[phone] === lid) return; // already stored, skip disk write
69
+ this._lidMappings[phone] = lid;
70
+ this._save();
71
+ }
72
+
73
+ // Return all stored phone → lid mappings (used to populate Client._pnToLid on startup)
74
+ getLidMappings() {
75
+ return this._lidMappings;
76
+ }
77
+
62
78
  // Immediate synchronous flush (used on process exit only)
63
79
  _flushSync() {
64
80
  if (!this._dirty || !this._filePath) return;
@@ -68,7 +84,8 @@ class SignalStore {
68
84
  sessions: this._sessions,
69
85
  preKeys: this._preKeys,
70
86
  signedPreKeys: this._signedPreKeys,
71
- identities: this._identities
87
+ identities: this._identities,
88
+ lidMappings: this._lidMappings
72
89
  });
73
90
  const dir = path.dirname(this._filePath);
74
91
  if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
@@ -89,7 +106,8 @@ class SignalStore {
89
106
  sessions: this._sessions,
90
107
  preKeys: this._preKeys,
91
108
  signedPreKeys: this._signedPreKeys,
92
- identities: this._identities
109
+ identities: this._identities,
110
+ lidMappings: this._lidMappings
93
111
  });
94
112
  const dir = path.dirname(this._filePath);
95
113
  if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "whalibmob",
3
- "version": "5.1.21",
3
+ "version": "5.5.22",
4
4
  "description": "WhatsApp library for interaction with WhatsApp Mobile API no web",
5
5
  "author": "Kunboruto20",
6
6
  "main": "index.js",