whalibmob 5.5.31 → 5.5.33

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
@@ -122,6 +122,7 @@ class WhalibmobClient extends EventEmitter {
122
122
  this._connected = false;
123
123
  this._reconnecting = false;
124
124
  this._reconnectTry = 0;
125
+ this._hasConnectedOnce = false; // true after first successful open; used to detect reconnects
125
126
  this._phoneNumber = null;
126
127
  this._mediaConn = null;
127
128
  this._pendingIqs = new Map(); // id → resolve fn
@@ -277,6 +278,32 @@ class WhalibmobClient extends EventEmitter {
277
278
 
278
279
  this._requestMediaConnection();
279
280
  this._uploadPreKeys();
281
+
282
+ // ── Reconnect: flush stale device cache + background re-usync ─────────────
283
+ // On first connect _hasConnectedOnce is false — nothing extra to do.
284
+ // On every reconnect (network drop / NAT reset / server restart) we flush the
285
+ // in-memory device cache because contacts may have linked or unlinked devices
286
+ // while we were offline. Disk files are kept for next restart warm-read;
287
+ // in-memory entries are cleared so the next send triggers a fresh usync IQ.
288
+ if (this._hasConnectedOnce && this._devMgr) {
289
+ const phonesInCache = Object.keys(this._devMgr._cacheSnapshot || {});
290
+ _whaDbg('[DBG] RECONNECT: flushing device cache (' + phonesInCache.length + ' entries) for fresh usync');
291
+ this._devMgr._dcFlush();
292
+
293
+ // Immediately re-usync own devices in the background (tablets / linked devices
294
+ // could have been added or unlinked while we were offline).
295
+ if (this._store && this._store.phoneNumber && this._signal) {
296
+ const ownPhone = this._store.phoneNumber;
297
+ setImmediate(() => {
298
+ if (!this._connected || !this._devMgr) return;
299
+ this._devMgr.ensureOwnDeviceSessions(ownPhone, this._signal, false)
300
+ .then(() => _whaDbg('[DBG] RECONNECT own-device usync done'))
301
+ .catch(e => _whaDbg('[DBG] RECONNECT own-device usync err: ' + e.message));
302
+ });
303
+ }
304
+ }
305
+ this._hasConnectedOnce = true;
306
+
280
307
  this.emit('connected');
281
308
  }
282
309
 
@@ -1009,6 +1036,50 @@ class WhalibmobClient extends EventEmitter {
1009
1036
  this._handlePrivacyTokenNotification(node);
1010
1037
  }
1011
1038
 
1039
+ if (type === 'devices') {
1040
+ // Server push: a contact's linked device list changed (they linked or
1041
+ // unlinked a tablet, desktop, etc.). Invalidate that phone's cache entry
1042
+ // so the next send triggers a fresh usync IQ and reaches all their devices.
1043
+ const fromJid = attrs.from || '';
1044
+ const fromPhone = fromJid.split('@')[0].split(':')[0];
1045
+ if (fromPhone && this._devMgr) {
1046
+ this._devMgr._dcDel([fromPhone]);
1047
+ _whaDbg('[DBG] NOTIF_DEVICES invalidated cache for ' + fromPhone);
1048
+ // Also process any inline <devices> list the server included
1049
+ const devicesNode = findChildDeep(node, 'devices');
1050
+ if (devicesNode) this._processDeviceUpdate(devicesNode);
1051
+ // Background re-usync so the cache is warm before the next send
1052
+ if (this._signal) {
1053
+ setImmediate(() => {
1054
+ if (!this._connected || !this._devMgr) return;
1055
+ this._devMgr.bulkEnsureSessions([fromPhone], this._signal, false)
1056
+ .then(() => _whaDbg('[DBG] NOTIF_DEVICES bg-usync done for ' + fromPhone))
1057
+ .catch(e => _whaDbg('[DBG] NOTIF_DEVICES bg-usync err: ' + e.message));
1058
+ });
1059
+ }
1060
+ }
1061
+ }
1062
+
1063
+ if (type === 'identity') {
1064
+ // Server push: a contact re-registered WhatsApp (new identity key / new phone).
1065
+ // Their old Signal sessions are no longer valid — clear them and the device
1066
+ // cache so the next send builds a fresh pkmsg session from scratch.
1067
+ const fromJid = attrs.from || '';
1068
+ const fromPhone = fromJid.split('@')[0].split(':')[0];
1069
+ if (fromPhone && this._devMgr) {
1070
+ this._devMgr._dcDel([fromPhone]);
1071
+ _whaDbg('[DBG] NOTIF_IDENTITY flushed device cache for re-registered ' + fromPhone);
1072
+ }
1073
+ if (fromPhone && this._signal && this._signal.store && this._signal.store._sessions) {
1074
+ const sessions = this._signal.store._sessions;
1075
+ let cleared = 0;
1076
+ for (const addr of Object.keys(sessions)) {
1077
+ if (addr.startsWith(fromPhone + '.')) { delete sessions[addr]; cleared++; }
1078
+ }
1079
+ _whaDbg('[DBG] NOTIF_IDENTITY cleared ' + cleared + ' Signal sessions for ' + fromPhone);
1080
+ }
1081
+ }
1082
+
1012
1083
  // Ack the notification
1013
1084
  if (this._socket && this._connected) {
1014
1085
  this._socket.sendNode(new BinaryNode('ack', {
@@ -1021,16 +1092,29 @@ class WhalibmobClient extends EventEmitter {
1021
1092
  }
1022
1093
 
1023
1094
  _processDeviceUpdate(devicesNode) {
1024
- if (!Array.isArray(devicesNode.content)) return;
1095
+ // Called from _handleNotification(type='account_sync') and type='devices'.
1096
+ // Rebuilds the device cache for the affected phones from the server-provided list.
1097
+ // Previous bug: called Set.add() on cache entries that are number[] arrays → silently failed.
1098
+ if (!Array.isArray(devicesNode.content) || !this._devMgr) return;
1099
+
1100
+ // Group all device IDs per phone from the notification
1101
+ const phoneDevices = new Map(); // phone → number[]
1025
1102
  for (const deviceNode of devicesNode.content) {
1026
1103
  if (!deviceNode || deviceNode.description !== 'device') continue;
1027
1104
  const jid = deviceNode.attrs && deviceNode.attrs.jid;
1028
- if (jid && this._devMgr) {
1029
- const phone = jid.split('@')[0].split(':')[0];
1030
- const device = parseInt((jid.split(':')[1] || '0').split('@')[0], 10);
1031
- if (!this._devMgr._deviceCache.has(phone)) this._devMgr._deviceCache.set(phone, new Set());
1032
- this._devMgr._deviceCache.get(phone).add(device);
1033
- }
1105
+ if (!jid) continue;
1106
+ const phone = String(jid).split('@')[0].split(':')[0];
1107
+ const device = parseInt((String(jid).split(':')[1] || '0').split('@')[0], 10);
1108
+ if (!phoneDevices.has(phone)) phoneDevices.set(phone, []);
1109
+ const ids = phoneDevices.get(phone);
1110
+ if (!ids.includes(device)) ids.push(device);
1111
+ }
1112
+
1113
+ // Replace cache entries atomically per phone (server list is authoritative)
1114
+ for (const [phone, ids] of phoneDevices) {
1115
+ this._devMgr._dcDel([phone]); // evict stale entry
1116
+ for (const id of ids) this._devMgr._dcAdd(phone, id);
1117
+ _whaDbg('[DBG] DEV_UPDATE phone=' + phone + ' ids=[' + ids.join(',') + ']');
1034
1118
  }
1035
1119
  }
1036
1120
 
@@ -1,7 +1,5 @@
1
1
  'use strict';
2
2
 
3
- const { dbg: _whaDbg } = require('./logger');
4
-
5
3
  const crypto = require('crypto');
6
4
  const https = require('https');
7
5
  const tls = require('tls');
@@ -49,7 +47,7 @@ async function httpPostViaSocks(path, body, waVersion, proxyUrl) {
49
47
  `User-Agent: ${userAgent}`,
50
48
  `Content-Type: application/x-www-form-urlencoded`,
51
49
  `Content-Length: ${Buffer.byteLength(body)}`,
52
- `Connection: close`,
50
+ `Connection: keep-alive`,
53
51
  '',
54
52
  body
55
53
  ].join('\r\n');
@@ -693,7 +691,7 @@ async function requestSmsCode(store, method, opts) {
693
691
  // Auto-fallback on no_routes
694
692
  if (result && result._noRoutes && !autoFallbackDone) {
695
693
  autoFallbackDone = true;
696
- _whaDbg(`[REG] ${method} returned no_routes — auto-trying ${fallbackMethod}`);
694
+ process.stderr.write(`[REG] ${method} returned no_routes — auto-trying ${fallbackMethod}\n`);
697
695
  result = await _tryMethod(fallbackMethod);
698
696
  }
699
697
 
package/lib/noise.js CHANGED
@@ -310,8 +310,8 @@ class NoiseSocket extends EventEmitter {
310
310
  username: BigInt(this.store.phoneNumber),
311
311
  passive: false,
312
312
  pushName: this.store.registered ? (this.store.name || null) : null,
313
- shortConnect: false,
314
- connectType: 1,
313
+ shortConnect: (this.store.connectAttemptCount || 0) > 0,
314
+ connectType: (this.store.connectAttemptCount || 0) > 0 ? 3 : 1,
315
315
  connectReason: 1,
316
316
  connectAttemptCount: (this.store.connectAttemptCount || 0),
317
317
  device: 0,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "whalibmob",
3
- "version": "5.5.31",
3
+ "version": "5.5.33",
4
4
  "description": "WhatsApp library for interaction with WhatsApp Mobile API no web",
5
5
  "author": "Kunboruto20",
6
6
  "main": "index.js",