whalibmob 5.9.0 → 5.9.2
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/README.md +1 -0
- package/cli.js +7 -0
- package/lib/Client.js +105 -104
- package/lib/DeviceManager.js +25 -26
- package/lib/MediaService.js +2 -2
- package/lib/MediaThumbnail.js +5 -7
- package/lib/auth-utils.js +11 -13
- package/lib/messages/MessageSender.js +60 -72
- package/lib/messages/ReportingToken.js +4 -6
- package/lib/messages/TcTokenStore.js +9 -15
- package/lib/noise.js +3 -4
- package/lib/proto/MessageProto.js +24 -14
- package/lib/signal/SignalProtocol.js +4 -4
- package/lib/signal/SignalStore.js +2 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1112,6 +1112,7 @@ connect()
|
|
|
1112
1112
|
| `app_state_sync` | `{ collections, applied }` | An app-state sync finished; see [Reading Changes Made Elsewhere](#app-state-sync) |
|
|
1113
1113
|
| `app_state_mutation` | `{ collection, index, action, removed }` | An app-state change this library does not model |
|
|
1114
1114
|
| `app_state_key_missing` | `{ collection, keyId }` | App state cannot be read until your phone shares this key |
|
|
1115
|
+
| `app_state_keys` | `{ keys }` | Your phone shared app-state sync keys; a sync starts automatically |
|
|
1115
1116
|
|
|
1116
1117
|
`remote: true` on a chat event means the change was made on your phone or another
|
|
1117
1118
|
linked device rather than by this session. Your own calls carry `synced` instead,
|
package/cli.js
CHANGED
|
@@ -600,6 +600,13 @@ function attachEvents(client) {
|
|
|
600
600
|
_rl && (_rl.resume(), _rl.prompt(true));
|
|
601
601
|
});
|
|
602
602
|
|
|
603
|
+
client.on('app_state_keys', (e) => {
|
|
604
|
+
_rl && _rl.pause();
|
|
605
|
+
out(' app state: received ' + e.keys.length + ' sync key(s) from your phone —' +
|
|
606
|
+
' pins, archives, mutes and stars will sync from now on');
|
|
607
|
+
_rl && (_rl.resume(), _rl.prompt(true));
|
|
608
|
+
});
|
|
609
|
+
|
|
603
610
|
client.on('decrypt_error', (e) => {
|
|
604
611
|
_rl && _rl.pause();
|
|
605
612
|
out(' DECRYPT_ERROR from ' + e.from + ' id ' + e.id + ' : ' + (e.err && e.err.message));
|
package/lib/Client.js
CHANGED
|
@@ -51,8 +51,8 @@ const CALL_VIDEO_PREFIX = 'https://call.whatsapp.com/video/';
|
|
|
51
51
|
const INVITE_LINK_PREFIX = 'https://chat.whatsapp.com/';
|
|
52
52
|
|
|
53
53
|
// The names privacy settings actually go by on the wire, and the friendly
|
|
54
|
-
// aliases this library has always accepted for them.
|
|
55
|
-
//
|
|
54
|
+
// aliases this library has always accepted for them. The wire names are the
|
|
55
|
+
// right-hand column.
|
|
56
56
|
const PRIVACY_NAME_ALIASES = {
|
|
57
57
|
last_seen: 'last', last: 'last',
|
|
58
58
|
profile_picture: 'profile', profile_pic: 'profile', profile: 'profile',
|
|
@@ -80,8 +80,8 @@ const PRIVACY_WIRE_TO_KEY = {
|
|
|
80
80
|
stickers: 'stickers'
|
|
81
81
|
};
|
|
82
82
|
|
|
83
|
-
// Reduce a JID to its bare user
|
|
84
|
-
//
|
|
83
|
+
// Reduce a JID to its bare user. Blocking, and anything else that names an
|
|
84
|
+
// account rather than one of its devices, wants this form.
|
|
85
85
|
function toNonAdJid(input) {
|
|
86
86
|
const s = String(input || '');
|
|
87
87
|
const at = s.indexOf('@');
|
|
@@ -246,14 +246,14 @@ class WhalibmobClient extends EventEmitter {
|
|
|
246
246
|
this._tcTokenStore = null; // TcTokenStore — loaded in init()
|
|
247
247
|
this._inFlightTcTokenIssuance = new Set(); // dedupe concurrent proactive issuePrivacyTokens per JID
|
|
248
248
|
this._inFlight463Recoveries = new Set(); // dedupe concurrent 463-triggered token issuances per JID (separate from proactive)
|
|
249
|
-
// Blue-tick every message as it arrives. Set to false
|
|
250
|
-
//
|
|
249
|
+
// Blue-tick every message as it arrives. Set to false to make read receipts
|
|
250
|
+
// explicit, sent only by a markRead() call.
|
|
251
251
|
this.autoRead = opts.autoRead !== false;
|
|
252
252
|
// Flipped by setOnline(). Delivery receipts sent while this is false carry
|
|
253
253
|
// type="inactive", which the sender's app records but does not render.
|
|
254
254
|
this._sendActiveReceipts = false;
|
|
255
|
-
// Cached privacy settings
|
|
256
|
-
//
|
|
255
|
+
// Cached privacy settings. Warmed after connect and refreshed whenever the
|
|
256
|
+
// server reports a change; markRead reads it.
|
|
257
257
|
this._privacySettings = null;
|
|
258
258
|
if (opts.pino !== undefined) _whaConfigLogger(opts.pino);
|
|
259
259
|
}
|
|
@@ -731,9 +731,9 @@ class WhalibmobClient extends EventEmitter {
|
|
|
731
731
|
this._saveWebStore();
|
|
732
732
|
_whaDbg('[DBG] COMPANION_FINISH adv_secret derived');
|
|
733
733
|
|
|
734
|
-
//
|
|
735
|
-
//
|
|
736
|
-
//
|
|
734
|
+
// Awaited rather than fired and forgotten, so a server rejection surfaces
|
|
735
|
+
// as an error here instead of as a silent stall waiting for a
|
|
736
|
+
// pair-success that is never coming.
|
|
737
737
|
const finishResult = await this._sendIq(new BinaryNode('iq', {
|
|
738
738
|
to: 's.whatsapp.net',
|
|
739
739
|
type: 'set',
|
|
@@ -1810,10 +1810,9 @@ class WhalibmobClient extends EventEmitter {
|
|
|
1810
1810
|
// Companion pairing — these arrive unsolicited, so they are checked before
|
|
1811
1811
|
// the pending-IQ table rather than after it.
|
|
1812
1812
|
//
|
|
1813
|
-
// pair-device is matched on type=set; pair-success is matched on any type
|
|
1814
|
-
//
|
|
1815
|
-
//
|
|
1816
|
-
// drop the stanza that completes the link.
|
|
1813
|
+
// pair-device is matched on type=set; pair-success is matched on any type.
|
|
1814
|
+
// Requiring type=set on pair-success would drop the stanza that completes
|
|
1815
|
+
// the link.
|
|
1817
1816
|
if (this._mode === 'web') {
|
|
1818
1817
|
if (type === 'set' && findChild(node, 'pair-device')) { this._handlePairDevice(node); return; }
|
|
1819
1818
|
if (findChild(node, 'pair-success')) { this._handlePairSuccess(node); return; }
|
|
@@ -1984,13 +1983,13 @@ class WhalibmobClient extends EventEmitter {
|
|
|
1984
1983
|
if (decoded.type === 'protocol') {
|
|
1985
1984
|
_whaDbg('[DBG] PROTO subtype=' + decoded.subtype + ' id=' + id);
|
|
1986
1985
|
|
|
1987
|
-
//
|
|
1988
|
-
|
|
1986
|
+
// Keyed off what the message actually carried rather than off the
|
|
1987
|
+
// type that announced it. A payload that is present is the payload,
|
|
1988
|
+
// whatever the type number turns out to mean.
|
|
1989
|
+
if (decoded.historySyncNotification) {
|
|
1989
1990
|
this._handleHistorySync(decoded.historySyncNotification, id);
|
|
1990
1991
|
}
|
|
1991
|
-
|
|
1992
|
-
// ── APP_STATE_SYNC_KEY_SHARE ──────────────────────────────────────
|
|
1993
|
-
if (decoded.subtype === 'app_state_sync_key_share' && decoded.appStateSyncKeyShare) {
|
|
1992
|
+
if (decoded.appStateSyncKeyShare) {
|
|
1994
1993
|
this._handleAppStateSyncKeyShare(decoded.appStateSyncKeyShare);
|
|
1995
1994
|
}
|
|
1996
1995
|
|
|
@@ -2001,8 +2000,8 @@ class WhalibmobClient extends EventEmitter {
|
|
|
2001
2000
|
this._sendReadReceipt(id, fromRaw, partRaw);
|
|
2002
2001
|
|
|
2003
2002
|
// ── pkmsg = peer opened a new Signal session (identity/device change) ──
|
|
2004
|
-
// Re-issue our tcToken to them so the fresh session carries a valid
|
|
2005
|
-
//
|
|
2003
|
+
// Re-issue our tcToken to them so the fresh session carries a valid
|
|
2004
|
+
// token.
|
|
2006
2005
|
if (encType === 'pkmsg' && this._tcTokenStore) {
|
|
2007
2006
|
this._reissueTcTokenAfterIdentityChange(sigJid);
|
|
2008
2007
|
}
|
|
@@ -2025,10 +2024,10 @@ class WhalibmobClient extends EventEmitter {
|
|
|
2025
2024
|
const decoded = this._decodeMsg(plaintext);
|
|
2026
2025
|
if (decoded.type === 'senderKeyDistribution') return;
|
|
2027
2026
|
if (decoded.type === 'protocol') {
|
|
2028
|
-
if (decoded.
|
|
2027
|
+
if (decoded.historySyncNotification) {
|
|
2029
2028
|
this._handleHistorySync(decoded.historySyncNotification, id);
|
|
2030
2029
|
}
|
|
2031
|
-
if (decoded.
|
|
2030
|
+
if (decoded.appStateSyncKeyShare) {
|
|
2032
2031
|
this._handleAppStateSyncKeyShare(decoded.appStateSyncKeyShare);
|
|
2033
2032
|
}
|
|
2034
2033
|
return;
|
|
@@ -2250,10 +2249,10 @@ class WhalibmobClient extends EventEmitter {
|
|
|
2250
2249
|
}
|
|
2251
2250
|
|
|
2252
2251
|
// A read receipt only tells the sender we read it when read receipts are on.
|
|
2253
|
-
//
|
|
2254
|
-
//
|
|
2255
|
-
//
|
|
2256
|
-
//
|
|
2252
|
+
// With them off, "read" is downgraded to "read-self", which marks the message
|
|
2253
|
+
// read across our own devices without reporting it to the other side. Nothing
|
|
2254
|
+
// here looked at the setting, so a user who had turned read receipts off
|
|
2255
|
+
// still sent blue ticks.
|
|
2257
2256
|
_readReceiptType(base) {
|
|
2258
2257
|
const settings = this._privacySettings;
|
|
2259
2258
|
if (settings && settings.readReceipts === 'none' && base === 'read') {
|
|
@@ -2264,9 +2263,8 @@ class WhalibmobClient extends EventEmitter {
|
|
|
2264
2263
|
|
|
2265
2264
|
// The key a privacy token is stored and looked up under.
|
|
2266
2265
|
//
|
|
2267
|
-
//
|
|
2268
|
-
//
|
|
2269
|
-
// Baileys' resolveTcTokenJid are the same three lines:
|
|
2266
|
+
// It resolves to the contact's LID, falling back to the plain JID only when
|
|
2267
|
+
// no LID is known:
|
|
2270
2268
|
//
|
|
2271
2269
|
// if (isLidUser(jid)) return jid
|
|
2272
2270
|
// const lid = await getLIDForPN(jid)
|
|
@@ -2289,9 +2287,9 @@ class WhalibmobClient extends EventEmitter {
|
|
|
2289
2287
|
// The in-memory map starts empty and is filled by usync and by incoming
|
|
2290
2288
|
// messages, so a send early in a process — or one that hits a warm device
|
|
2291
2289
|
// cache and never runs usync — could find nothing and fall back to the
|
|
2292
|
-
// phone JID. The mapping is on disk in the Signal store either way,
|
|
2293
|
-
//
|
|
2294
|
-
//
|
|
2290
|
+
// phone JID. The mapping is on disk in the Signal store either way, so
|
|
2291
|
+
// consult that before giving up. The consequence of not doing so is not
|
|
2292
|
+
// cosmetic: the lookup lands on
|
|
2295
2293
|
// a key with no token while a live one sits under the LID, and the message
|
|
2296
2294
|
// goes out without one.
|
|
2297
2295
|
if (!lidUser) {
|
|
@@ -2325,9 +2323,8 @@ class WhalibmobClient extends EventEmitter {
|
|
|
2325
2323
|
//
|
|
2326
2324
|
// Storage is always the LID. Issuance follows AB prop 14303
|
|
2327
2325
|
// (lid_trusted_token_issue_to_lid), whose WA Web default is off — so the IQ
|
|
2328
|
-
// names the phone JID.
|
|
2329
|
-
//
|
|
2330
|
-
// follow WA Web, and more importantly we now follow it in one place: the
|
|
2326
|
+
// names the phone JID. We follow WA Web, and more importantly we now follow
|
|
2327
|
+
// it in one place: the
|
|
2331
2328
|
// send path, the 463 recovery and the post-identity-change re-issue used to
|
|
2332
2329
|
// each pick a different address family for the same contact.
|
|
2333
2330
|
_resolveTcTokenIssuanceJid(jid) {
|
|
@@ -2340,9 +2337,9 @@ class WhalibmobClient extends EventEmitter {
|
|
|
2340
2337
|
|
|
2341
2338
|
// The one way to read a token that is safe to put on the wire.
|
|
2342
2339
|
//
|
|
2343
|
-
//
|
|
2344
|
-
//
|
|
2345
|
-
//
|
|
2340
|
+
// Every attach site goes through here, and an expired token returns nothing.
|
|
2341
|
+
// The expiry check used to be on the message path only; presence subscribe,
|
|
2342
|
+
// group create, participant add and the
|
|
2346
2343
|
// profile-picture query each tested `token.length` and nothing else, so once
|
|
2347
2344
|
// a token aged past the 28-day window those four kept sending it. Same rule
|
|
2348
2345
|
// everywhere now, and the dead token gets cleared on the way out.
|
|
@@ -2373,10 +2370,10 @@ class WhalibmobClient extends EventEmitter {
|
|
|
2373
2370
|
// Delivery receipt — the sender's two grey ticks.
|
|
2374
2371
|
//
|
|
2375
2372
|
// Distinct from the <ack> above: the ack tells the server we took the stanza,
|
|
2376
|
-
// the receipt tells the sender it reached a device.
|
|
2377
|
-
//
|
|
2378
|
-
//
|
|
2379
|
-
//
|
|
2373
|
+
// the receipt tells the sender it reached a device. Both go out for every
|
|
2374
|
+
// incoming message, with the blue ticks left to markRead. Only the ack used
|
|
2375
|
+
// to be sent, jumping straight to type="read", so a sender never saw the grey
|
|
2376
|
+
// stage.
|
|
2380
2377
|
//
|
|
2381
2378
|
// A client that has not marked itself available sends this with
|
|
2382
2379
|
// type="inactive": the sender's app records the delivery but does not render
|
|
@@ -2399,7 +2396,7 @@ class WhalibmobClient extends EventEmitter {
|
|
|
2399
2396
|
//
|
|
2400
2397
|
// Sent automatically after a message decodes unless autoRead is turned off, in
|
|
2401
2398
|
// which case marking a chat read is up to the caller via markRead(). Turning
|
|
2402
|
-
// it off
|
|
2399
|
+
// it off makes read receipts always explicit.
|
|
2403
2400
|
// fromJid / partJid are raw JID objects (AD_JID encoding for correct routing).
|
|
2404
2401
|
_sendReadReceipt(msgId, fromJid, partJid) {
|
|
2405
2402
|
if (!this._socket || !this._connected) return;
|
|
@@ -2543,8 +2540,8 @@ class WhalibmobClient extends EventEmitter {
|
|
|
2543
2540
|
}
|
|
2544
2541
|
|
|
2545
2542
|
// <blocklist action="..." dhash="..." prev_dhash="..."><item jid=... action=.../></blocklist>
|
|
2546
|
-
//
|
|
2547
|
-
//
|
|
2543
|
+
// The action on the node describes the whole change; each child names one JID
|
|
2544
|
+
// and what happened to it.
|
|
2548
2545
|
_handleBlocklistNotification(node) {
|
|
2549
2546
|
const attrs = node.attrs || {};
|
|
2550
2547
|
const changes = [];
|
|
@@ -2587,9 +2584,9 @@ class WhalibmobClient extends EventEmitter {
|
|
|
2587
2584
|
// blocked only ever changed when it did the blocking itself.
|
|
2588
2585
|
const blocklistNode = findChildDeep(node, 'blocklist');
|
|
2589
2586
|
if (blocklistNode) this._handleBlocklistNotification(blocklistNode);
|
|
2590
|
-
// ...and privacy setting changes, which arrive the same way.
|
|
2591
|
-
// re-
|
|
2592
|
-
//
|
|
2587
|
+
// ...and privacy setting changes, which arrive the same way. The settings
|
|
2588
|
+
// are re-read on this and an event raised; nothing was watching the node,
|
|
2589
|
+
// so a change made on the phone never reached the cache.
|
|
2593
2590
|
const privacyNode = findChildDeep(node, 'privacy');
|
|
2594
2591
|
if (privacyNode) this._handlePrivacySettingsNotification(privacyNode);
|
|
2595
2592
|
}
|
|
@@ -2847,9 +2844,9 @@ class WhalibmobClient extends EventEmitter {
|
|
|
2847
2844
|
return;
|
|
2848
2845
|
}
|
|
2849
2846
|
|
|
2850
|
-
// Not every failure kills the session.
|
|
2851
|
-
//
|
|
2852
|
-
//
|
|
2847
|
+
// Not every failure kills the session. These five do — 401, 403, 405, 406,
|
|
2848
|
+
// 409 — and the rest are worth another attempt. A 500 or a 503 is the
|
|
2849
|
+
// server having a bad minute; giving
|
|
2853
2850
|
// up on the session for that would be throwing away a working registration.
|
|
2854
2851
|
const LOGGED_OUT = ['401', '403', '405', '406', '409'];
|
|
2855
2852
|
const spent = LOGGED_OUT.includes(reason);
|
|
@@ -3289,8 +3286,6 @@ class WhalibmobClient extends EventEmitter {
|
|
|
3289
3286
|
* Send a `<iq type='set' xmlns='privacy'>` to issue a trusted-contact token
|
|
3290
3287
|
* for `jid`. Returns the raw result node (or null on timeout).
|
|
3291
3288
|
*
|
|
3292
|
-
* Mirrors Baileys' `issuePrivacyTokens` in messages-send.js.
|
|
3293
|
-
*
|
|
3294
3289
|
* @param {string} jid - Bare or full JID of the conversation partner
|
|
3295
3290
|
* @param {number} timestamp - Unix seconds to use as the token timestamp
|
|
3296
3291
|
*/
|
|
@@ -3378,13 +3373,12 @@ class WhalibmobClient extends EventEmitter {
|
|
|
3378
3373
|
: (typeof tokenNode.content === 'string' && tokenNode.content.length > 0)
|
|
3379
3374
|
? Buffer.from(tokenNode.content, 'base64')
|
|
3380
3375
|
: null;
|
|
3381
|
-
//
|
|
3382
|
-
//
|
|
3383
|
-
// → always prefer fallbackJid; only fall back to a.jid when fallbackJid is absent
|
|
3376
|
+
// In a notification, tokenNode.attrs.jid is our own device JID rather
|
|
3377
|
+
// than the sender's, so fallbackJid wins whenever it is present.
|
|
3384
3378
|
const jid = fallbackJid || String(a.jid);
|
|
3385
3379
|
const t = a.t ? parseInt(String(a.t), 10) : issueTs;
|
|
3386
|
-
//
|
|
3387
|
-
//
|
|
3380
|
+
// Anything that is not a regular user is dropped here, guarding
|
|
3381
|
+
// against a malformed or spoofed notification filing a token under
|
|
3388
3382
|
// the PSA account or a bot.
|
|
3389
3383
|
if (!isRegularTcTokenUser(jid)) {
|
|
3390
3384
|
_whaDbg('[DBG] STORE_TCTOKEN skipped non-user jid=' + jid);
|
|
@@ -3407,8 +3401,8 @@ class WhalibmobClient extends EventEmitter {
|
|
|
3407
3401
|
}
|
|
3408
3402
|
}
|
|
3409
3403
|
|
|
3410
|
-
// ── CRITICAL:
|
|
3411
|
-
// ── regardless of whether server returned token bytes.
|
|
3404
|
+
// ── CRITICAL: senderTimestamp is ALWAYS persisted after issuing, ─────
|
|
3405
|
+
// ── regardless of whether the server returned token bytes. ─────
|
|
3412
3406
|
// ── Without this, shouldSendNewTcToken() returns true every send and ─────
|
|
3413
3407
|
// ── we flood the server with privacy IQs. ─────
|
|
3414
3408
|
// ── For incoming privacy_token notifications saveSenderTs=false so ─────
|
|
@@ -3427,14 +3421,14 @@ class WhalibmobClient extends EventEmitter {
|
|
|
3427
3421
|
/**
|
|
3428
3422
|
* Handle a server-pushed `<notification type='privacy_token'>`.
|
|
3429
3423
|
*
|
|
3430
|
-
* Wire format
|
|
3424
|
+
* Wire format:
|
|
3431
3425
|
* <notification type='privacy_token' from='...' [sender_lid='...@lid']>
|
|
3432
3426
|
* <tokens>
|
|
3433
3427
|
* <token type='trusted_contact' jid='...' t='...'>…bytes…</token>
|
|
3434
3428
|
* </tokens>
|
|
3435
3429
|
* </notification>
|
|
3436
3430
|
*
|
|
3437
|
-
* Key rules
|
|
3431
|
+
* Key rules:
|
|
3438
3432
|
* 1. Must find <tokens> wrapper inside notification; abort if absent.
|
|
3439
3433
|
* 2. storage key = sender_lid (if present and @lid) else PN→LID lookup else from-JID.
|
|
3440
3434
|
* 3. senderTimestamp is NOT updated here — only the peer-token bytes + timestamp.
|
|
@@ -3449,7 +3443,7 @@ class WhalibmobClient extends EventEmitter {
|
|
|
3449
3443
|
_whaDbg('[DBG] PRIVACY_TOKEN_NOTIF_ENTER from=' + String(attrs.from || '') +
|
|
3450
3444
|
' sender_lid=' + String(attrs.sender_lid || ''));
|
|
3451
3445
|
|
|
3452
|
-
// ── 1. Require <tokens> wrapper
|
|
3446
|
+
// ── 1. Require the <tokens> wrapper ────────────────────────────────────────
|
|
3453
3447
|
const content = Array.isArray(node.content) ? node.content : [];
|
|
3454
3448
|
const tokensNode = content.find(n => n && n.description === 'tokens');
|
|
3455
3449
|
if (!tokensNode) {
|
|
@@ -3458,7 +3452,7 @@ class WhalibmobClient extends EventEmitter {
|
|
|
3458
3452
|
}
|
|
3459
3453
|
|
|
3460
3454
|
// ── 2. Resolve storage JID ─────────────────────────────────────────────────
|
|
3461
|
-
// Priority
|
|
3455
|
+
// Priority:
|
|
3462
3456
|
// 1. sender_lid attr if it is a @lid JID
|
|
3463
3457
|
// 2. from user is a known LID user (LID comes as @s.whatsapp.net in notification)
|
|
3464
3458
|
// 3. from is a PN → look up LID in _pnToLid
|
|
@@ -3494,7 +3488,7 @@ class WhalibmobClient extends EventEmitter {
|
|
|
3494
3488
|
* Called when we successfully decrypt a pkmsg — the peer opened a fresh Signal
|
|
3495
3489
|
* session (reset or new device), so we re-issue a privacy token so they can
|
|
3496
3490
|
* reach us. Only fires if we've previously issued a token (senderTimestamp
|
|
3497
|
-
* present and not expired)
|
|
3491
|
+
* present and not expired).
|
|
3498
3492
|
*
|
|
3499
3493
|
* @param {string} from - Signal-session JID (PN or LID JID of the sender)
|
|
3500
3494
|
*/
|
|
@@ -3516,8 +3510,8 @@ class WhalibmobClient extends EventEmitter {
|
|
|
3516
3510
|
_whaDbg('[DBG] REISSUE_TC_TOKEN_AFTER_IDENTITY_CHANGE jid=' + tcJid +
|
|
3517
3511
|
' prevSenderTs=' + senderTs);
|
|
3518
3512
|
|
|
3519
|
-
//
|
|
3520
|
-
//
|
|
3513
|
+
// The re-issue carries the *previous* senderTimestamp, not a fresh one,
|
|
3514
|
+
// and the result is stored without touching the dedupe state. That
|
|
3521
3515
|
// matters: this is a repair of an existing grant after the peer's
|
|
3522
3516
|
// device changed, not a new grant, so it must not move the 7-day
|
|
3523
3517
|
// bucket that governs regular issuance. We were stamping it with
|
|
@@ -3614,7 +3608,7 @@ class WhalibmobClient extends EventEmitter {
|
|
|
3614
3608
|
* - Skips expired tokens (no point loading a token we'd immediately discard).
|
|
3615
3609
|
* - Storage key follows TcTokenStore convention: normalized bare JID.
|
|
3616
3610
|
* For LID chats (jid ends in @lid) we use the lid JID directly.
|
|
3617
|
-
* For PN chats we try to look up the LID first
|
|
3611
|
+
* For PN chats we try to look up the LID first, since storage is by LID.
|
|
3618
3612
|
*
|
|
3619
3613
|
* @param {object|null} merged - Return value of mergeHistoryToStore()
|
|
3620
3614
|
*/
|
|
@@ -3642,7 +3636,7 @@ class WhalibmobClient extends EventEmitter {
|
|
|
3642
3636
|
continue;
|
|
3643
3637
|
}
|
|
3644
3638
|
|
|
3645
|
-
// Resolve storage JID
|
|
3639
|
+
// Resolve storage JID — storage is always by LID.
|
|
3646
3640
|
// If this chat is a @lid jid, use it directly.
|
|
3647
3641
|
// If it's a PN jid, try to find its LID counterpart.
|
|
3648
3642
|
let storageJid;
|
|
@@ -3700,6 +3694,15 @@ class WhalibmobClient extends EventEmitter {
|
|
|
3700
3694
|
}
|
|
3701
3695
|
|
|
3702
3696
|
this.emit('app_state_keys', { keys: keyShare.keys });
|
|
3697
|
+
|
|
3698
|
+
// The key is what app state was waiting on. Anything the server has been
|
|
3699
|
+
// holding for us can be read now, so go and get it rather than sitting on
|
|
3700
|
+
// it until the next dirty bit happens along.
|
|
3701
|
+
if (this._connected && this._appState) {
|
|
3702
|
+
this.syncAppState().catch(err => {
|
|
3703
|
+
_whaDbg('[DBG] APP_STATE sync after key share failed: ' + (err && err.message));
|
|
3704
|
+
});
|
|
3705
|
+
}
|
|
3703
3706
|
}
|
|
3704
3707
|
|
|
3705
3708
|
/**
|
|
@@ -3792,9 +3795,9 @@ class WhalibmobClient extends EventEmitter {
|
|
|
3792
3795
|
// required in a group; type may be 'read' (default) or 'played'
|
|
3793
3796
|
// to mark a voice note as listened to.
|
|
3794
3797
|
//
|
|
3795
|
-
//
|
|
3796
|
-
//
|
|
3797
|
-
//
|
|
3798
|
+
// The FIRST id goes on the receipt and the rest as <item> children of a
|
|
3799
|
+
// <list> node. The last id used to be used instead, with the items straight
|
|
3800
|
+
// inside <receipt>, which is not a shape the server
|
|
3798
3801
|
// reads back — the extra ids were silently ignored, so only one message per
|
|
3799
3802
|
// call was ever marked. The participant was missing entirely, so a group read
|
|
3800
3803
|
// never told the server whose message had been read.
|
|
@@ -3881,8 +3884,8 @@ class WhalibmobClient extends EventEmitter {
|
|
|
3881
3884
|
|
|
3882
3885
|
// Set global online/offline status.
|
|
3883
3886
|
//
|
|
3884
|
-
//
|
|
3885
|
-
//
|
|
3887
|
+
// This is refused without a push name: the server takes the display name from
|
|
3888
|
+
// this node, and sending it empty makes
|
|
3886
3889
|
// every other user see "-" instead of a name. Going available also flips the
|
|
3887
3890
|
// client into sending active delivery receipts — see _sendDeliveryReceipt.
|
|
3888
3891
|
//
|
|
@@ -3905,9 +3908,9 @@ class WhalibmobClient extends EventEmitter {
|
|
|
3905
3908
|
// Set typing/recording presence in a specific chat.
|
|
3906
3909
|
// presence: 'composing' | 'paused' | 'recording'
|
|
3907
3910
|
//
|
|
3908
|
-
//
|
|
3909
|
-
//
|
|
3910
|
-
//
|
|
3911
|
+
// Both `from` (our own JID) and `to` go on the chatstate node, and recording
|
|
3912
|
+
// is expressed as composing with media="audio" — there is no separate
|
|
3913
|
+
// recording state in the protocol. The `from` was missing
|
|
3911
3914
|
// here, and both JIDs were raw strings, which the server ignores for @lid
|
|
3912
3915
|
// chats, so the indicator never showed for a LID-addressed contact.
|
|
3913
3916
|
setChatPresence(chatJid, presence) {
|
|
@@ -3931,18 +3934,17 @@ class WhalibmobClient extends EventEmitter {
|
|
|
3931
3934
|
|
|
3932
3935
|
// Subscribe to presence updates for contacts.
|
|
3933
3936
|
//
|
|
3934
|
-
//
|
|
3935
|
-
//
|
|
3936
|
-
//
|
|
3937
|
-
//
|
|
3937
|
+
// The contact's privacy token is attached when there is one; without it the
|
|
3938
|
+
// server may decline to send their presence. The server also only forwards
|
|
3939
|
+
// presence to clients that have marked themselves available, so call
|
|
3940
|
+
// setOnline(true) first.
|
|
3938
3941
|
subscribeToPresence(jids) {
|
|
3939
3942
|
if (!this._socket || !this._connected) return;
|
|
3940
3943
|
const arr = Array.isArray(jids) ? jids : [jids];
|
|
3941
3944
|
const { isRegularTcTokenUser } = require('./messages/TcTokenStore');
|
|
3942
3945
|
for (const raw of arr) {
|
|
3943
3946
|
const jid = makeJid(raw);
|
|
3944
|
-
// Groups and newsletters have no privacy token
|
|
3945
|
-
// way before building the node.
|
|
3947
|
+
// Groups and newsletters have no privacy token.
|
|
3946
3948
|
const token = isRegularTcTokenUser(jid) ? this._tcTokenFor(jid) : null;
|
|
3947
3949
|
const children = token ? [new BinaryNode('tctoken', {}, token)] : null;
|
|
3948
3950
|
this._socket.sendNode(new BinaryNode('presence', {
|
|
@@ -4049,8 +4051,8 @@ class WhalibmobClient extends EventEmitter {
|
|
|
4049
4051
|
return (info && info.url) || null;
|
|
4050
4052
|
}
|
|
4051
4053
|
|
|
4052
|
-
// Full profile picture info,
|
|
4053
|
-
//
|
|
4054
|
+
// Full profile picture info: { id, url, type, directPath, hash }, or null
|
|
4055
|
+
// when the user has no
|
|
4054
4056
|
// picture or it has not changed since opts.existingId.
|
|
4055
4057
|
//
|
|
4056
4058
|
// opts: { preview, existingId, isCommunity }
|
|
@@ -4176,8 +4178,8 @@ class WhalibmobClient extends EventEmitter {
|
|
|
4176
4178
|
|
|
4177
4179
|
// Parse the <list> a blocklist query or update comes back with.
|
|
4178
4180
|
//
|
|
4179
|
-
//
|
|
4180
|
-
//
|
|
4181
|
+
// Every child is walked and its jid taken, whatever the tag is called, with
|
|
4182
|
+
// the dhash kept off the list itself.
|
|
4181
4183
|
// The JIDs are stringified here: the binary decoder hands them back as JID
|
|
4182
4184
|
// objects, and while those print correctly they are not strings, so the array
|
|
4183
4185
|
// this used to return failed any includes() or === against a JID string —
|
|
@@ -4310,9 +4312,9 @@ class WhalibmobClient extends EventEmitter {
|
|
|
4310
4312
|
// 'none' | 'match_last_seen' | 'known' | 'on_standard' | 'off'
|
|
4311
4313
|
// excluded: JIDs to exclude — see the note below
|
|
4312
4314
|
//
|
|
4313
|
-
// The names above are friendly aliases. What goes on the wire is
|
|
4314
|
-
//
|
|
4315
|
-
//
|
|
4315
|
+
// The names above are friendly aliases. What goes on the wire is: last,
|
|
4316
|
+
// profile, status, online, readreceipts, groupadd, calladd, messages,
|
|
4317
|
+
// defense, stickers. Four of the six names this used to
|
|
4316
4318
|
// put on the wire — last_seen, profile_picture, read_receipts, groups_add —
|
|
4317
4319
|
// are not names the server knows, so those settings never changed and the
|
|
4318
4320
|
// discarded reply meant it never said so either.
|
|
@@ -4323,8 +4325,8 @@ class WhalibmobClient extends EventEmitter {
|
|
|
4323
4325
|
Object.keys(PRIVACY_NAME_ALIASES).join(', '));
|
|
4324
4326
|
}
|
|
4325
4327
|
|
|
4326
|
-
//
|
|
4327
|
-
//
|
|
4328
|
+
// WhatsApp Web does not put <user> children on this IQ; the exclusion list
|
|
4329
|
+
// is maintained separately. Kept for callers that pass it,
|
|
4328
4330
|
// and only emitted when they do.
|
|
4329
4331
|
const children = [];
|
|
4330
4332
|
if (excluded && excluded.length > 0) {
|
|
@@ -4355,8 +4357,8 @@ class WhalibmobClient extends EventEmitter {
|
|
|
4355
4357
|
(code ? ' (' + code + (text ? ' ' + text : '') + ')' : ''));
|
|
4356
4358
|
}
|
|
4357
4359
|
|
|
4358
|
-
//
|
|
4359
|
-
//
|
|
4360
|
+
// The change is folded into the cached copy and the whole thing handed back
|
|
4361
|
+
// rather than making the caller re-query.
|
|
4360
4362
|
const key = PRIVACY_WIRE_TO_KEY[name];
|
|
4361
4363
|
this._privacySettings = Object.assign({}, this._privacySettings || {},
|
|
4362
4364
|
{ [key]: String(value) });
|
|
@@ -4385,8 +4387,8 @@ class WhalibmobClient extends EventEmitter {
|
|
|
4385
4387
|
|
|
4386
4388
|
// Who your Status posts go to.
|
|
4387
4389
|
//
|
|
4388
|
-
// Returns [{ type, isDefault, list }] with the default first
|
|
4389
|
-
//
|
|
4390
|
+
// Returns [{ type, isDefault, list }] with the default first. type is
|
|
4391
|
+
// 'contacts', 'blacklist' or 'whitelist'; list
|
|
4390
4392
|
// holds the JIDs the last two are built from. A server that has never been
|
|
4391
4393
|
// told otherwise answers nothing, which means the default: all contacts.
|
|
4392
4394
|
async queryStatusPrivacy() {
|
|
@@ -4427,8 +4429,8 @@ class WhalibmobClient extends EventEmitter {
|
|
|
4427
4429
|
}
|
|
4428
4430
|
|
|
4429
4431
|
// Resolve the status privacy settings into the JIDs a post actually goes to,
|
|
4430
|
-
//
|
|
4431
|
-
//
|
|
4432
|
+
// a whitelist is the list itself, anything else is every known contact minus
|
|
4433
|
+
// the blacklist, and
|
|
4432
4434
|
// our own JID is added so the post lands on our own devices too.
|
|
4433
4435
|
//
|
|
4434
4436
|
// Contacts come from the synced history store — this client has no separate
|
|
@@ -4989,10 +4991,9 @@ class WhalibmobClient extends EventEmitter {
|
|
|
4989
4991
|
// </iq>
|
|
4990
4992
|
//
|
|
4991
4993
|
// The item is addressed by LID, and a block carries the phone number beside
|
|
4992
|
-
// it; an unblock carries only the LID. That is the shape
|
|
4993
|
-
//
|
|
4994
|
-
//
|
|
4995
|
-
// used to do and what the server answered with 400 bad-request.
|
|
4994
|
+
// it; an unblock carries only the LID. That is the shape this server accepts
|
|
4995
|
+
// — sending the plain JID it was handed, which is what this used to do, got a
|
|
4996
|
+
// 400 bad-request back.
|
|
4996
4997
|
//
|
|
4997
4998
|
// Three other things were wrong with the pair this replaces. The jid went out
|
|
4998
4999
|
// as a raw string rather than a binary JID. It was not reduced to its bare
|