whalibmob 5.5.34 → 5.5.36
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 +24 -11
- package/lib/DeviceManager.js +42 -21
- package/package.json +1 -1
package/lib/Client.js
CHANGED
|
@@ -117,8 +117,11 @@ class WhalibmobClient extends EventEmitter {
|
|
|
117
117
|
this._signal = null;
|
|
118
118
|
this._devMgr = null;
|
|
119
119
|
this._sessionDir = opts.sessionDir || process.env.HOME + '/.waSession';
|
|
120
|
-
this._pingTimer
|
|
121
|
-
this._keepTimer
|
|
120
|
+
this._pingTimer = null;
|
|
121
|
+
this._keepTimer = null;
|
|
122
|
+
this._watchdogTimer = null;
|
|
123
|
+
this._watchdogLastPingSentAt = 0; // timestamp when last keepalive ping was sent
|
|
124
|
+
this._watchdogPongReceived = true; // true = server responded after last ping (or no ping sent yet)
|
|
122
125
|
this._connected = false;
|
|
123
126
|
this._reconnecting = false;
|
|
124
127
|
this._reconnectTry = 0;
|
|
@@ -259,6 +262,8 @@ class WhalibmobClient extends EventEmitter {
|
|
|
259
262
|
|
|
260
263
|
_onOpen(successNode) {
|
|
261
264
|
this._connected = true;
|
|
265
|
+
this._watchdogLastPingSentAt = 0;
|
|
266
|
+
this._watchdogPongReceived = true;
|
|
262
267
|
this._startTimers();
|
|
263
268
|
|
|
264
269
|
// ── Feature 1: Parse <success> node ──────────────────────────────────────
|
|
@@ -495,6 +500,9 @@ class WhalibmobClient extends EventEmitter {
|
|
|
495
500
|
|
|
496
501
|
this._keepTimer = setInterval(() => {
|
|
497
502
|
if (this._socket && this._connected) {
|
|
503
|
+
// Record the time we sent this ping so the watchdog can check for missing pongs.
|
|
504
|
+
this._watchdogLastPingSentAt = Date.now();
|
|
505
|
+
this._watchdogPongReceived = false;
|
|
498
506
|
this._socket.sendNode(new BinaryNode('iq', {
|
|
499
507
|
id: this._genMsgId(),
|
|
500
508
|
to: 's.whatsapp.net',
|
|
@@ -504,18 +512,21 @@ class WhalibmobClient extends EventEmitter {
|
|
|
504
512
|
}
|
|
505
513
|
}, KEEPALIVE_INTERVAL);
|
|
506
514
|
|
|
507
|
-
// Watchdog:
|
|
508
|
-
//
|
|
509
|
-
//
|
|
515
|
+
// Watchdog: detects a truly dead TCP connection where the OS never emits 'close'
|
|
516
|
+
// (common with NAT/firewall drops). Logic: if we sent a keepalive ping and the
|
|
517
|
+
// server did NOT reply within 2× keepalive window, the connection is dead.
|
|
518
|
+
// NOTE: idleness alone (bot sleeping between sends) is NOT treated as dead —
|
|
519
|
+
// only an unanswered ping triggers the close, so any delay is safe.
|
|
510
520
|
const WATCHDOG_INTERVAL = KEEPALIVE_INTERVAL * 2 + 5000;
|
|
511
521
|
this._watchdogTimer = setInterval(() => {
|
|
512
522
|
if (!this._socket || !this._connected) return;
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
523
|
+
if (!this._watchdogPongReceived &&
|
|
524
|
+
this._watchdogLastPingSentAt > 0 &&
|
|
525
|
+
Date.now() - this._watchdogLastPingSentAt > WATCHDOG_INTERVAL) {
|
|
526
|
+
_whaDbg('[DBG] WATCHDOG: keepalive ping sent ' +
|
|
527
|
+
Math.round((Date.now() - this._watchdogLastPingSentAt) / 1000) +
|
|
528
|
+
's ago with no server response — force-closing dead connection');
|
|
529
|
+
try { this._socket.close(); } catch (_) {}
|
|
519
530
|
}
|
|
520
531
|
}, KEEPALIVE_INTERVAL);
|
|
521
532
|
}
|
|
@@ -530,6 +541,8 @@ class WhalibmobClient extends EventEmitter {
|
|
|
530
541
|
|
|
531
542
|
_onNode(node) {
|
|
532
543
|
if (!node || !node.description) return;
|
|
544
|
+
// Any node from the server means the connection is alive — reset watchdog state.
|
|
545
|
+
this._watchdogPongReceived = true;
|
|
533
546
|
const tag = node.description;
|
|
534
547
|
// Debug: log every node received
|
|
535
548
|
_whaDbg('[DBG] _onNode tag=' + tag + ' attrs=' + JSON.stringify(node.attrs || {}));
|
package/lib/DeviceManager.js
CHANGED
|
@@ -412,34 +412,45 @@ class DeviceManager {
|
|
|
412
412
|
return jids.length > 0 ? jids : phones.map(p => makeDeviceJid(p, 0));
|
|
413
413
|
}
|
|
414
414
|
|
|
415
|
-
// ───
|
|
415
|
+
// ─── usync IQ — builds the device-list query exactly as WhatsApp APK does ────
|
|
416
416
|
//
|
|
417
|
-
//
|
|
418
|
-
// Was: <user><contact>+phone</contact></user>
|
|
419
|
-
// Fix: <user jid="phone@s.whatsapp.net"/> (jid ATTRIBUTE, not contact child)
|
|
417
|
+
// APK-confirmed format (extracted from WhatsApp DEX string tables):
|
|
420
418
|
//
|
|
421
|
-
//
|
|
422
|
-
//
|
|
423
|
-
//
|
|
419
|
+
// <iq to="s.whatsapp.net" type="get" xmlns="usync">
|
|
420
|
+
// <usync sid="..." mode="query" last="true" index="0" context="message">
|
|
421
|
+
// <query>
|
|
422
|
+
// <devices version="2" device_orientation="0" fetch_options="5"/>
|
|
423
|
+
// <lid/>
|
|
424
|
+
// </query>
|
|
425
|
+
// <list>
|
|
426
|
+
// <user><contact>+phone</contact></user> ← Buffer, not jid attr string
|
|
427
|
+
// </list>
|
|
428
|
+
// <side_list/>
|
|
429
|
+
// </usync>
|
|
430
|
+
// </iq>
|
|
424
431
|
//
|
|
425
|
-
//
|
|
426
|
-
// Was: (absent)
|
|
427
|
-
// Fix: <side_list/> (required for LID device discovery)
|
|
432
|
+
// Critical fixes vs original whalibmob implementation:
|
|
428
433
|
//
|
|
429
|
-
//
|
|
430
|
-
//
|
|
431
|
-
//
|
|
432
|
-
//
|
|
433
|
-
//
|
|
434
|
+
// Fix A — fetch_options="5" MISSING from <devices> node:
|
|
435
|
+
// Without fetch_options="5", the WA server returns ONLY device 0 (primary phone).
|
|
436
|
+
// fetch_options="5" instructs the server to return ALL linked devices.
|
|
437
|
+
// device_orientation="0" is also required by the server.
|
|
438
|
+
//
|
|
439
|
+
// Fix B — User list format: use <contact>+phone</contact> as Buffer content.
|
|
440
|
+
// The BinaryNode encoder encodes string attributes as raw UTF-8 (BINARY_8 tag),
|
|
441
|
+
// NOT as JID_PAIR binary format. The APK sends phone as Buffer content of a
|
|
442
|
+
// <contact> child node — this is what the server actually expects in the list.
|
|
434
443
|
//
|
|
435
444
|
async _doUsyncIq(phones) {
|
|
436
445
|
const iqId = this._client._genMsgId();
|
|
437
446
|
const sid = this._client._genMsgId();
|
|
438
447
|
|
|
439
|
-
//
|
|
440
|
-
//
|
|
448
|
+
// Fix B: <user><contact>+phone</contact></user> — matches APK exactly.
|
|
449
|
+
// Buffer.from ensures binary encoding (not raw UTF-8 string attribute).
|
|
441
450
|
const listChildren = phones.map(p =>
|
|
442
|
-
new BinaryNode('user', {
|
|
451
|
+
new BinaryNode('user', {}, [
|
|
452
|
+
new BinaryNode('contact', {}, Buffer.from('+' + p, 'utf8'))
|
|
453
|
+
])
|
|
443
454
|
);
|
|
444
455
|
|
|
445
456
|
const iqNode = new BinaryNode('iq',
|
|
@@ -449,7 +460,9 @@ class DeviceManager {
|
|
|
449
460
|
[
|
|
450
461
|
new BinaryNode('query', {},
|
|
451
462
|
[
|
|
452
|
-
|
|
463
|
+
// Fix A: fetch_options="5" + device_orientation="0" — required for
|
|
464
|
+
// the server to return ALL linked devices, not just device 0.
|
|
465
|
+
new BinaryNode('devices', { version: '2', device_orientation: '0', fetch_options: '5' }, null),
|
|
453
466
|
new BinaryNode('lid', {}, null)
|
|
454
467
|
]
|
|
455
468
|
),
|
|
@@ -771,6 +784,13 @@ class DeviceManager {
|
|
|
771
784
|
const iqId = this._client._genMsgId();
|
|
772
785
|
const sid = this._client._genMsgId();
|
|
773
786
|
|
|
787
|
+
// Build proper JID object for binary encoding (JID_PAIR format, not raw string)
|
|
788
|
+
const jidStr = typeof jid === 'string' ? jid : String(jid);
|
|
789
|
+
const atIdx = jidStr.indexOf('@');
|
|
790
|
+
const jidUser = atIdx >= 0 ? jidStr.slice(0, atIdx) : jidStr;
|
|
791
|
+
const jidSrv = atIdx >= 0 ? jidStr.slice(atIdx + 1) : 'lid';
|
|
792
|
+
const jidObj = { user: jidUser, server: jidSrv, toString() { return jidStr; } };
|
|
793
|
+
|
|
774
794
|
const iqNode = new BinaryNode('iq',
|
|
775
795
|
{ id: iqId, to: 's.whatsapp.net', type: 'get', xmlns: 'usync' },
|
|
776
796
|
[new BinaryNode('usync',
|
|
@@ -778,12 +798,13 @@ class DeviceManager {
|
|
|
778
798
|
[
|
|
779
799
|
new BinaryNode('query', {},
|
|
780
800
|
[
|
|
781
|
-
|
|
801
|
+
// Fix A applied here too: fetch_options="5" + device_orientation="0"
|
|
802
|
+
new BinaryNode('devices', { version: '2', device_orientation: '0', fetch_options: '5' }, null),
|
|
782
803
|
new BinaryNode('lid', {}, null)
|
|
783
804
|
]
|
|
784
805
|
),
|
|
785
806
|
new BinaryNode('list', {},
|
|
786
|
-
[new BinaryNode('user', { jid }, null)]
|
|
807
|
+
[new BinaryNode('user', { jid: jidObj }, null)]
|
|
787
808
|
),
|
|
788
809
|
new BinaryNode('side_list', {}, null)
|
|
789
810
|
]
|