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 +91 -7
- package/lib/Registration.js +2 -4
- package/lib/noise.js +2 -2
- package/package.json +1 -1
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
|
-
|
|
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
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
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
|
|
package/lib/Registration.js
CHANGED
|
@@ -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:
|
|
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
|
-
|
|
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:
|
|
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,
|