whalibmob 5.9.1 → 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/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. whatsmeow's
55
- // PrivacySettingType constants are the right-hand column.
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 — whatsmeow's ToNonAD. Blocking, and anything
84
- // else that names an account rather than one of its devices, wants this form.
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 for whatsmeow's model,
250
- // where read is only ever sent by an explicit markRead() call.
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, as whatsmeow caches them. Warmed after connect
256
- // and refreshed whenever the server reports a change; markRead reads it.
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
- // Baileys awaits this one rather than firing and forgetting, so a server
735
- // rejection surfaces as an error here instead of as a silent stall
736
- // waiting for a pair-success that is never coming.
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
- // the way Baileys distinguishes them ('CB:iq,type:set,pair-device' against
1815
- // a bare 'CB:iq,,pair-success'). Requiring type=set on pair-success would
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; }
@@ -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 token.
2005
- // Mirrors Baileys' reissueTcTokenAfterIdentityChange (messages-recv.js).
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
  }
@@ -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
- // whatsmeow's MarkRead checks the same setting and downgrades "read" to
2254
- // "read-self", which marks the message read across our own devices without
2255
- // reporting it to the other side. Nothing here looked at the setting, so a
2256
- // user who had turned read receipts off still sent blue ticks.
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
- // Both references resolve it to the contact's LID and fall back to the plain
2268
- // JID only when no LID is known — whatsmeow's resolveTCTokenStorageLID and
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, which
2293
- // is the same place whatsmeow reads it from, so consult that before giving
2294
- // up. The consequence of not doing so is not cosmetic: the lookup lands on
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. Baileys implements exactly this in resolveIssuanceJid
2329
- // and defaults the prop to false; whatsmeow issues to the LID instead. We
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
- // whatsmeow funnels every attach site through ensureTCToken, which returns
2344
- // nothing for an expired token. whalibmob had the expiry check on the message
2345
- // path only; presence subscribe, group create, participant add and the
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. whatsmeow sends both for
2377
- // every incoming message (sendAck + sendMessageReceipt) and leaves the blue
2378
- // ticks to an explicit MarkRead. whalibmob only ever sent the ack and then
2379
- // jumped straight to type="read", so a sender never saw the grey stage.
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 gives whatsmeow's model, where read is always explicit.
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
- // Mirrors whatsmeow's handleBlocklist: the action on the node describes the
2547
- // whole change, each child names one JID and what happened to it.
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. whatsmeow
2591
- // re-reads the settings on this and raises an event; nothing was watching
2592
- // the node, so a change made on the phone never reached the cache.
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. whatsmeow's IsLoggedOut() marks the
2851
- // ones that do — 401, 403, 405, 406, 409 — and treats the rest as worth
2852
- // another attempt. A 500 or a 503 is the server having a bad minute; giving
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
- // Baileys tc-token-utils.js line 137-138:
3382
- // "In notifications tokenNode.attrs.jid is your own device JID, not the sender's"
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
- // Baileys drops anything that is not a regular user here — the guard
3387
- // is against a malformed or spoofed notification filing a token under
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: Baileys ALWAYS persists senderTimestamp after issuing, ─────
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 (mirrors Baileys' handlePrivacyTokenNotification):
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 (from Baileys messages-recv.js lines 996-1015):
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 (matches Baileys' getBinaryNodeChild check) ──
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 (mirrors Baileys lines 1001-1006):
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), mirroring Baileys' reissueTcTokenAfterIdentityChange.
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
- // Baileys re-issues with the *previous* senderTimestamp, not a fresh
3520
- // one, and stores the result without touching the dedupe state. That
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 (Baileys always stores under LID).
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: Baileys always stores under LID.
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;
@@ -3801,9 +3795,9 @@ class WhalibmobClient extends EventEmitter {
3801
3795
  // required in a group; type may be 'read' (default) or 'played'
3802
3796
  // to mark a voice note as listened to.
3803
3797
  //
3804
- // Shaped the way whatsmeow's MarkRead builds it: the FIRST id on the receipt
3805
- // and the rest as <item> children of a <list> node. whalibmob used the last id
3806
- // and put the items straight into <receipt>, which is not a shape the server
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
3807
3801
  // reads back — the extra ids were silently ignored, so only one message per
3808
3802
  // call was ever marked. The participant was missing entirely, so a group read
3809
3803
  // never told the server whose message had been read.
@@ -3890,8 +3884,8 @@ class WhalibmobClient extends EventEmitter {
3890
3884
 
3891
3885
  // Set global online/offline status.
3892
3886
  //
3893
- // whatsmeow refuses to send this without a push name (ErrNoPushName): the
3894
- // server takes the display name from this node, and sending it empty makes
3887
+ // This is refused without a push name: the server takes the display name from
3888
+ // this node, and sending it empty makes
3895
3889
  // every other user see "-" instead of a name. Going available also flips the
3896
3890
  // client into sending active delivery receipts — see _sendDeliveryReceipt.
3897
3891
  //
@@ -3914,9 +3908,9 @@ class WhalibmobClient extends EventEmitter {
3914
3908
  // Set typing/recording presence in a specific chat.
3915
3909
  // presence: 'composing' | 'paused' | 'recording'
3916
3910
  //
3917
- // whatsmeow's SendChatPresence puts both `from` (our own JID) and `to` on the
3918
- // chatstate node and expresses recording as composing with media="audio" —
3919
- // there is no separate recording state in the protocol. The `from` was missing
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
3920
3914
  // here, and both JIDs were raw strings, which the server ignores for @lid
3921
3915
  // chats, so the indicator never showed for a LID-addressed contact.
3922
3916
  setChatPresence(chatJid, presence) {
@@ -3940,18 +3934,17 @@ class WhalibmobClient extends EventEmitter {
3940
3934
 
3941
3935
  // Subscribe to presence updates for contacts.
3942
3936
  //
3943
- // whatsmeow attaches the contact's privacy token when it has one; without it
3944
- // the server may decline to send their presence. It also notes that the server
3945
- // only forwards presence to clients that have marked themselves available, so
3946
- // call setOnline(true) first.
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.
3947
3941
  subscribeToPresence(jids) {
3948
3942
  if (!this._socket || !this._connected) return;
3949
3943
  const arr = Array.isArray(jids) ? jids : [jids];
3950
3944
  const { isRegularTcTokenUser } = require('./messages/TcTokenStore');
3951
3945
  for (const raw of arr) {
3952
3946
  const jid = makeJid(raw);
3953
- // Groups and newsletters have no privacy token; Baileys guards the same
3954
- // way before building the node.
3947
+ // Groups and newsletters have no privacy token.
3955
3948
  const token = isRegularTcTokenUser(jid) ? this._tcTokenFor(jid) : null;
3956
3949
  const children = token ? [new BinaryNode('tctoken', {}, token)] : null;
3957
3950
  this._socket.sendNode(new BinaryNode('presence', {
@@ -4058,8 +4051,8 @@ class WhalibmobClient extends EventEmitter {
4058
4051
  return (info && info.url) || null;
4059
4052
  }
4060
4053
 
4061
- // Full profile picture info, the way whatsmeow's GetProfilePictureInfo returns
4062
- // it: { id, url, type, directPath, hash }, or null when the user has no
4054
+ // Full profile picture info: { id, url, type, directPath, hash }, or null
4055
+ // when the user has no
4063
4056
  // picture or it has not changed since opts.existingId.
4064
4057
  //
4065
4058
  // opts: { preview, existingId, isCommunity }
@@ -4185,8 +4178,8 @@ class WhalibmobClient extends EventEmitter {
4185
4178
 
4186
4179
  // Parse the <list> a blocklist query or update comes back with.
4187
4180
  //
4188
- // whatsmeow's parseBlocklist walks every child and takes its jid, without
4189
- // caring what the tag is called, and keeps the dhash off the list itself.
4181
+ // Every child is walked and its jid taken, whatever the tag is called, with
4182
+ // the dhash kept off the list itself.
4190
4183
  // The JIDs are stringified here: the binary decoder hands them back as JID
4191
4184
  // objects, and while those print correctly they are not strings, so the array
4192
4185
  // this used to return failed any includes() or === against a JID string —
@@ -4319,9 +4312,9 @@ class WhalibmobClient extends EventEmitter {
4319
4312
  // 'none' | 'match_last_seen' | 'known' | 'on_standard' | 'off'
4320
4313
  // excluded: JIDs to exclude — see the note below
4321
4314
  //
4322
- // The names above are friendly aliases. What goes on the wire is what
4323
- // whatsmeow sends: last, profile, status, online, readreceipts, groupadd,
4324
- // calladd, messages, defense, stickers. Four of the six names this used to
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
4325
4318
  // put on the wire — last_seen, profile_picture, read_receipts, groups_add —
4326
4319
  // are not names the server knows, so those settings never changed and the
4327
4320
  // discarded reply meant it never said so either.
@@ -4332,8 +4325,8 @@ class WhalibmobClient extends EventEmitter {
4332
4325
  Object.keys(PRIVACY_NAME_ALIASES).join(', '));
4333
4326
  }
4334
4327
 
4335
- // Neither whatsmeow nor WhatsApp Web puts <user> children on this IQ; the
4336
- // exclusion list is maintained separately. Kept for callers that pass it,
4328
+ // WhatsApp Web does not put <user> children on this IQ; the exclusion list
4329
+ // is maintained separately. Kept for callers that pass it,
4337
4330
  // and only emitted when they do.
4338
4331
  const children = [];
4339
4332
  if (excluded && excluded.length > 0) {
@@ -4364,8 +4357,8 @@ class WhalibmobClient extends EventEmitter {
4364
4357
  (code ? ' (' + code + (text ? ' ' + text : '') + ')' : ''));
4365
4358
  }
4366
4359
 
4367
- // whatsmeow folds the change into its cached copy and hands the whole thing
4368
- // back rather than making the caller re-query.
4360
+ // The change is folded into the cached copy and the whole thing handed back
4361
+ // rather than making the caller re-query.
4369
4362
  const key = PRIVACY_WIRE_TO_KEY[name];
4370
4363
  this._privacySettings = Object.assign({}, this._privacySettings || {},
4371
4364
  { [key]: String(value) });
@@ -4394,8 +4387,8 @@ class WhalibmobClient extends EventEmitter {
4394
4387
 
4395
4388
  // Who your Status posts go to.
4396
4389
  //
4397
- // Returns [{ type, isDefault, list }] with the default first, as whatsmeow's
4398
- // GetStatusPrivacy does. type is 'contacts', 'blacklist' or 'whitelist'; list
4390
+ // Returns [{ type, isDefault, list }] with the default first. type is
4391
+ // 'contacts', 'blacklist' or 'whitelist'; list
4399
4392
  // holds the JIDs the last two are built from. A server that has never been
4400
4393
  // told otherwise answers nothing, which means the default: all contacts.
4401
4394
  async queryStatusPrivacy() {
@@ -4436,8 +4429,8 @@ class WhalibmobClient extends EventEmitter {
4436
4429
  }
4437
4430
 
4438
4431
  // Resolve the status privacy settings into the JIDs a post actually goes to,
4439
- // the way whatsmeow's getStatusBroadcastRecipients does: a whitelist is the
4440
- // list itself, anything else is every known contact minus the blacklist, and
4432
+ // a whitelist is the list itself, anything else is every known contact minus
4433
+ // the blacklist, and
4441
4434
  // our own JID is added so the post lands on our own devices too.
4442
4435
  //
4443
4436
  // Contacts come from the synced history store — this client has no separate
@@ -4998,10 +4991,9 @@ class WhalibmobClient extends EventEmitter {
4998
4991
  // </iq>
4999
4992
  //
5000
4993
  // The item is addressed by LID, and a block carries the phone number beside
5001
- // it; an unblock carries only the LID. That is the shape Baileys'
5002
- // updateBlockStatus builds, and the one this server accepts — whatsmeow's
5003
- // UpdateBlocklist still sends the plain JID it was handed, which is what this
5004
- // 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.
5005
4997
  //
5006
4998
  // Three other things were wrong with the pair this replaces. The jid went out
5007
4999
  // as a raw string rather than a binary JID. It was not reduced to its bare
@@ -45,8 +45,8 @@ function jidStrToObj(jidStr) {
45
45
  return { user, agent: 0, device, server, toString() { return self; } };
46
46
  }
47
47
  // @lid multi-device (e.g. "112713111982325:2@lid"): AD_JID with the domain in
48
- // the agent byte — 1 for lid, matching Baileys' writeJid, which emits AD_JID
49
- // with domainType whenever a device is present.
48
+ // the agent byte — 1 for lid. AD_JID with a domain type is the shape whenever
49
+ // a device is present.
50
50
  //
51
51
  // This previously packed the device into the user string as a JID_PAIR
52
52
  // ("112713111982325:2"), a workaround from when the encoder could not express
@@ -204,7 +204,7 @@ class DeviceManager {
204
204
  // It loads the on-disk device cache so the very first send after a process
205
205
  // restart is instant — no cold-start usync round-trip needed.
206
206
  //
207
- // Files saved in session dir (baileys-compatible naming):
207
+ // Files saved in session dir:
208
208
  // <phone>.device-cache.json ← combined cache (internal)
209
209
  // device-list-447911234567@s.whatsapp.net.json ← per-phone device IDs
210
210
  // device-list-139471160877194@lid.json ← per-LID device IDs
@@ -268,7 +268,7 @@ class DeviceManager {
268
268
  'utf8'
269
269
  );
270
270
 
271
- // ── Individual device-list files (baileys-compatible) ──────────────────
271
+ // ── Individual device-list files ───────────────────────────────────────
272
272
  for (const [key, ids] of Object.entries(entries)) {
273
273
  let fileName;
274
274
  if (key.startsWith('lid:')) {
@@ -411,13 +411,12 @@ class DeviceManager {
411
411
  return jids.length > 0 ? jids : phones.map(p => makeDeviceJid(p, 0));
412
412
  }
413
413
 
414
- // ─── USync executor (Baileys WAUSync, mobile-API transport) ─────────────────
414
+ // ─── USync executor (mobile-API transport) ──────────────────────────────────
415
415
  //
416
- // Mirrors Baileys' socket.js `executeUSyncQuery`: it builds the <query> and
417
- // <list> nodes from the USyncQuery protocols/users, sends the IQ, and parses
418
- // the response through `usyncQuery.parseUSyncQueryResult`. The difference is
419
- // purely the transport — whalibmob talks the WhatsApp *mobile* protocol, not
420
- // the web protocol — so three adaptations are applied:
416
+ // Builds the <query> and <list> nodes from the USyncQuery protocols/users,
417
+ // sends the IQ, and parses the response through
418
+ // `usyncQuery.parseUSyncQueryResult`. Talking the WhatsApp *mobile* protocol
419
+ // rather than the web one calls for three adaptations:
421
420
  //
422
421
  // 1. Nodes use whalibmob's BinaryNode (`description` field) instead of the
423
422
  // web `{ tag }` object literals.
@@ -431,7 +430,7 @@ class DeviceManager {
431
430
  // protocol → <user><contact>+phone</contact></user>. The mobile server
432
431
  // ignores the <user jid="..."> form for phone-based device discovery.
433
432
  // • Discovery for an already-known routing JID (a LID) uses
434
- // USyncUser().withId(jid) → <user jid="..."> exactly like Baileys.
433
+ // USyncUser().withId(jid) → <user jid="...">.
435
434
  async executeUSyncQuery(usyncQuery) {
436
435
  if (!usyncQuery || usyncQuery.protocols.length === 0) {
437
436
  throw new Error('USyncQuery must have at least one protocol');
@@ -464,9 +463,9 @@ class DeviceManager {
464
463
  return { user: (server === 'lid' && device > 0) ? raw : user, server, toString() { return str; } };
465
464
  };
466
465
 
467
- // <user> nodes — jid attr is set only when the user has no phone (Baileys:
468
- // `jid: !user.phone ? user.id : undefined`); phone users carry a <contact>
469
- // child instead, produced by the contact protocol's getUserElement.
466
+ // <user> nodes — the jid attr is set only when the user has no phone; phone
467
+ // users carry a <contact> child instead, produced by the contact protocol's
468
+ // getUserElement.
470
469
  const userNodes = usyncQuery.users.map(user => {
471
470
  const children = usyncQuery.protocols
472
471
  .map(p => p.getUserElement(user))
@@ -535,9 +534,9 @@ class DeviceManager {
535
534
  // Fix C — context and side_list must match _doContactUsync (the working format):
536
535
  // context="interactive" + <side_list/> is REQUIRED. context="message" → server ignores IQ.
537
536
  async _doUsyncIq(phones) {
538
- // Build the device-discovery query exactly as Baileys does — via USyncQuery
539
- // and the device / LID / contact protocols — then run it through the
540
- // mobile-API executor and parse it with the shared parseUSyncQueryResult.
537
+ // Build the device-discovery query via USyncQuery and the device / LID /
538
+ // contact protocols, then run it through the mobile-API executor and parse
539
+ // it with the shared parseUSyncQueryResult.
541
540
  //
542
541
  // Users are addressed by PHONE (USyncUser().withPhone → <contact>+phone</>)
543
542
  // because the WA *mobile* server silently drops usync IQs that address
@@ -626,9 +625,9 @@ class DeviceManager {
626
625
  for (const dev of deviceList) {
627
626
  const devId = Number.isFinite(dev.id) ? dev.id : 0;
628
627
  const keyIdx = Number.isFinite(dev.keyIndex) ? dev.keyIndex : -1;
629
- // Mirror Baileys extractDeviceJids: keep device 0 or non-zero
630
- // devices that carry a valid key-index (otherwise the encrypt IQ
631
- // returns a bad-request / empty bundle for that device).
628
+ // Keep device 0, and non-zero devices that carry a valid
629
+ // key-index — otherwise the encrypt IQ returns a bad-request or an
630
+ // empty bundle for that device.
632
631
  if (devId === 0 || keyIdx > 0) {
633
632
  this._dcAdd(cachePhone, devId);
634
633
  }
@@ -883,8 +882,8 @@ class DeviceManager {
883
882
  // expressed as <ourLid>:<device>@lid.
884
883
  //
885
884
  // Device 0 is excluded exactly as it is on the phone path: it is the device
886
- // doing the sending, and both Baileys and whatsmeow skip the exact sender
887
- // device when building participants.
885
+ // doing the sending, and the sender device is never one of its own
886
+ // participants.
888
887
  async ensureOwnLidDeviceSessions(lidUser, ownPhone, signalProto, allowPkmsg = true) {
889
888
  if (!lidUser) return [];
890
889
  const cacheKey = 'lid:' + lidUser;
@@ -977,7 +976,7 @@ class DeviceManager {
977
976
  async _doContactUsync(phones) {
978
977
  // Contact-only fallback (device/lid protocols omitted): the plain contact
979
978
  // query is the form the mobile server reliably answers when the richer
980
- // device query times out. Built via the shared Baileys USyncQuery builder.
979
+ // device query times out. Built via the shared USyncQuery builder.
981
980
  const query = new USyncQuery()
982
981
  .withContext('interactive')
983
982
  .withContactProtocol();
@@ -1031,9 +1030,9 @@ class DeviceManager {
1031
1030
  const jidStr = typeof jid === 'string' ? jid : String(jid);
1032
1031
 
1033
1032
  // Discovery for an already-known routing JID (a LID) — addressed via
1034
- // USyncUser().withId(jid) → <user jid="...">, exactly as Baileys'
1035
- // getUSyncDevices does. executeUSyncQuery encodes the jid attr as a
1036
- // JID object so the mobile binary encoder emits proper JID_PAIR bytes.
1033
+ // USyncUser().withId(jid) → <user jid="...">. executeUSyncQuery encodes
1034
+ // the jid attr as a JID object so the mobile binary encoder emits proper
1035
+ // JID_PAIR bytes.
1037
1036
  const query = new USyncQuery()
1038
1037
  .withContext('interactive')
1039
1038
  .withDeviceProtocol()
@@ -119,8 +119,8 @@ function decryptMedia(encryptedWithMac, mediaKey, keyName) {
119
119
 
120
120
  // Upload encrypted media, trying every CDN host in turn.
121
121
  //
122
- // refreshAuth (optional) mirrors Baileys' refreshMediaConn(true): when a host
123
- // answers but the upload yields no usable URL — the signature of an expired or
122
+ // refreshAuth (optional): when a host answers but the upload yields no usable
123
+ // URL — the signature of an expired or
124
124
  // rejected auth token — the auth is force-refreshed once and the hosts are
125
125
  // retried with the fresh credentials. Without this a token that expires between
126
126
  // the TTL check and the upload burns through every host and fails permanently
@@ -8,9 +8,8 @@
8
8
  // drawn, so the recipient gets the grey placeholder with a download arrow and
9
9
  // has to tap it before seeing anything.
10
10
  //
11
- // Baileys builds that preview by resizing the source to 32 pixels wide at JPEG
12
- // quality 50, and takes the original dimensions from the same decode. This does
13
- // the same, with the difference that none of it is mandatory:
11
+ // The preview is the source resized to 32 pixels wide at JPEG quality 50, with
12
+ // the original dimensions read from the same decode. None of it is mandatory:
14
13
  //
15
14
  // • Dimensions are read straight out of the file header — JPEG, PNG, WebP,
16
15
  // GIF and BMP — with no library at all.
@@ -31,8 +30,8 @@ const path = require('path');
31
30
  const crypto = require('crypto');
32
31
  const { execFile } = require('child_process');
33
32
 
34
- // Baileys' numbers — the preview is deliberately tiny, it is only ever shown
35
- // blurred behind the real image while that downloads.
33
+ // The preview is deliberately tiny; it is only ever shown blurred behind the
34
+ // real image while that downloads.
36
35
  const THUMB_WIDTH = 32;
37
36
  const THUMB_QUALITY = 50;
38
37
 
@@ -358,8 +357,7 @@ async function videoThumbnail(input, opts) {
358
357
  } catch (_) {}
359
358
  }
360
359
 
361
- // Same frame grab Baileys runs: first frame, scaled to the preview width
362
- // with the aspect ratio kept.
360
+ // First frame, scaled to the preview width with the aspect ratio kept.
363
361
  const dest = tmpFile('.jpg');
364
362
  try {
365
363
  const ok = await run('ffmpeg', [
package/lib/auth-utils.js CHANGED
@@ -2,8 +2,8 @@
2
2
 
3
3
  // ─── auth-utils.js ────────────────────────────────────────────────────────────
4
4
  //
5
- // Provides four utilities that mirror Baileys' auth-utils, adapted to
6
- // whalibmob's architecture (method-based SignalStore, Buffer key pairs, etc.):
5
+ // Four utilities built around whalibmob's architecture (method-based
6
+ // SignalStore, Buffer key pairs, etc.):
7
7
  //
8
8
  // 1. makeCacheableSignalKeyStore — wraps SignalStore with NodeCache (5-min TTL)
9
9
  // and a Mutex for all read/write operations
@@ -591,7 +591,6 @@ function addTransactionCapability(state, logger, opts) {
591
591
  * Returns the authenticated user's JID (phone@s.whatsapp.net) or throws
592
592
  * a descriptive error if the store is not yet fully authenticated.
593
593
  *
594
- * Equivalent to Baileys' assertMeId(creds) but for whalibmob's store format.
595
594
  * Use this anywhere we'd otherwise reach for `store.phoneNumber` directly,
596
595
  * to fail fast with a clear error instead of a silent null-reference crash.
597
596
  *
@@ -623,12 +622,12 @@ function assertMeId(store) {
623
622
  * Creates a fresh set of whalibmob auth credentials (key pairs, registration ID,
624
623
  * device identifiers) for a given phone number.
625
624
  *
626
- * Equivalent to Baileys' initAuthCreds() but returns whalibmob's store format
627
- * (Buffer key pairs, signedPreKey as { id, public, private, signature }).
625
+ * Returns whalibmob's store format (Buffer key pairs, signedPreKey as
626
+ * { id, public, private, signature }).
628
627
  *
629
- * Additional Baileys-compatible fields (nextPreKeyId, firstUnuploadedPreKeyId,
630
- * accountSyncCounter, accountSettings, advSecretKey, etc.) are appended so
631
- * code that was written against Baileys' credential shape works unchanged.
628
+ * A few extra fields (nextPreKeyId, firstUnuploadedPreKeyId, accountSyncCounter,
629
+ * accountSettings, advSecretKey, etc.) are appended for account sync and for
630
+ * application code that expects them.
632
631
  *
633
632
  * @param {string|number} phoneNumber — full international number (e.g. "40712345678")
634
633
  * @param {object} options — optional overrides:
@@ -643,9 +642,8 @@ function initAuthCreds(phoneNumber, options) {
643
642
  // Use whalibmob's own key-generation machinery (curve25519-js, proper prefixes)
644
643
  const store = createNewStore(phoneNumber);
645
644
 
646
- // ── Extra Baileys-compatible fields ──────────────────────────────────────────
647
- // These are not required by whalibmob internally but are commonly expected by
648
- // application code that was originally written against Baileys.
645
+ // ── Extra credential fields ──────────────────────────────────────────────────
646
+ // Not required internally, but commonly expected by application code.
649
647
 
650
648
  /** Next pre-key ID to generate (monotonically increasing). */
651
649
  store.nextPreKeyId = 1;
@@ -656,7 +654,7 @@ function initAuthCreds(phoneNumber, options) {
656
654
  /** Sync counter used in account-sync IQ stanzas. */
657
655
  store.accountSyncCounter = 0;
658
656
 
659
- /** Account-level settings mirroring Baileys' IAccountSettings shape. */
657
+ /** Account-level settings. */
660
658
  store.accountSettings = { unarchiveChats: false };
661
659
 
662
660
  /** Processed history messages — used to avoid duplicate handling on reconnect. */
@@ -664,7 +662,7 @@ function initAuthCreds(phoneNumber, options) {
664
662
 
665
663
  /**
666
664
  * 32-byte random secret used for ADV (multi-device) poll encryption.
667
- * Stored as a base64 string, consistent with Baileys' advSecretKey.
665
+ * Stored as a base64 string.
668
666
  */
669
667
  store.advSecretKey = crypto.randomBytes(32).toString('base64');
670
668
 
@@ -43,8 +43,7 @@ function isGroupJid(jid) {
43
43
 
44
44
  // A Status/Story is posted as a message to status@broadcast, and it travels the
45
45
  // SenderKey path a group message does — one skmsg plus a distribution to every
46
- // recipient — not the 1:1 path. Baileys calls the pair isGroupOrStatus;
47
- // whatsmeow routes BroadcastServer into sendGroup for the same reason.
46
+ // recipient — not the 1:1 path. A Status and a group are the same case here.
48
47
  const STATUS_BROADCAST_JID = 'status@broadcast';
49
48
 
50
49
  function isStatusBroadcast(jid) {
@@ -196,18 +195,14 @@ function adString(jid) {
196
195
  return `${user}.0:${device}@${server}`;
197
196
  }
198
197
 
199
- // Participant-list hash v2, as whatsmeow's participantListHashV2 computes it:
200
- // sort the AD strings, sha256 the concatenation, then base64 the FIRST SIX
201
- // BYTES of the digest — eight characters, prefixed "2:".
198
+ // Participant-list hash v2: sort the AD strings, sha256 the concatenation, then
199
+ // base64 the FIRST SIX BYTES of the digest — eight characters, prefixed "2:".
202
200
  //
203
- // This previously mirrored Baileys' generateParticipantHashV2, which hashes
204
- // plain JID strings and then takes the first six CHARACTERS of the base64 of
205
- // the whole digest. That produces a different, six-character value from
206
- // different input. Baileys never puts the result on the wire — it assigns it to
207
- // extraAttrs after the participant nodes are built, so nothing ever reads it —
208
- // which is why the discrepancy has gone unnoticed there. whalibmob did put it
209
- // on the wire, so every multi-device send carried a hash the server could not
210
- // match against its own view of the participant list.
201
+ // This previously hashed plain JID strings and took the first six CHARACTERS of
202
+ // the base64 of the whole digest, which is a different, six-character value over
203
+ // different input. It goes on the wire, so every multi-device send carried a
204
+ // hash the server could not match against its own view of the participant
205
+ // list.
211
206
  function computePhash(jids) {
212
207
  const sorted = [...new Set(jids.map(adString))].sort();
213
208
  const hash = crypto.createHash('sha256').update(sorted.join('')).digest();
@@ -341,7 +336,7 @@ class MessageSender {
341
336
  gifPlayback: options.gifPlayback || false,
342
337
  contextInfo: options.contextInfo
343
338
  }));
344
- // whatsmeow labels a video with gifPlayback as mediatype "gif", not "video".
339
+ // A video with gifPlayback is labelled mediatype "gif", not "video".
345
340
  return this._sendMessage(toJid, msgId, vidBuf, 'media',
346
341
  { ...options, _mediaSubtype: options.gifPlayback ? 'gif' : 'video' });
347
342
  }
@@ -459,8 +454,8 @@ class MessageSender {
459
454
  selectableOptionsCount: selectableCount != null ? selectableCount : 0,
460
455
  messageSecret: opts.messageSecret || opts.encKey || null
461
456
  });
462
- // whatsmeow's BuildPollCreation pairs the poll with a messageContextInfo
463
- // holding the secret. It is a sibling field of the poll inside Message, not
457
+ // The poll has to be paired with a messageContextInfo holding the secret.
458
+ // It is a sibling field of the poll inside Message, not
464
459
  // part of the poll — a voter derives their vote key from this copy, so a
465
460
  // poll sent without it cannot be voted on.
466
461
  const pollBuf = Buffer.concat([
@@ -497,9 +492,9 @@ class MessageSender {
497
492
  contextInfo: opts.contextInfo
498
493
  }));
499
494
  // A location is a plain message as far as the envelope is concerned.
500
- // whatsmeow's getTypeFromMessage has only text, media, reaction and poll —
501
- // LocationMessage is in none of the media cases, so it falls through to
502
- // text. Sending type="location" hands the server a type it has no case for.
495
+ // The stanza types are text, media, reaction and poll — LocationMessage is
496
+ // in none of the media cases, so it is text. Sending type="location" hands
497
+ // the server a type it has no case for.
503
498
  return this._sendMessage(toJid, msgId, locBuf, 'text', opts);
504
499
  }
505
500
 
@@ -922,9 +917,8 @@ class MessageSender {
922
917
  const allowPkmsg = true;
923
918
 
924
919
  // ── Addressing family ─────────────────────────────────────────────────────
925
- // Baileys 7.0.0-rc13 — the release that fixed ack 479 on LID addressing —
926
- // derives the whole address family from the JID the caller passed, and
927
- // never converts between the two:
920
+ // The whole address family comes from the JID the caller passed, and the
921
+ // two are never converted between:
928
922
  //
929
923
  // const targetUserServer = isLid ? 'lid' : 's.whatsapp.net'
930
924
  // const senderIdentity = isLid && meLid ? <ourLid> : <ourPn>
@@ -939,9 +933,9 @@ class MessageSender {
939
933
  // The recipient's devices followed the caller's JID but ours were always
940
934
  // enumerated by phone, so a DM to an @lid went out with @lid recipient
941
935
  // entries and @s.whatsapp.net entries for our own linked devices in the same
942
- // <participants> list. whatsmeow builds the pair as [to, ownID] with ownID
943
- // switched to our LID for @lid destinations precisely to avoid that, and a
944
- // participant list the server cannot reconcile is what comes back as 479.
936
+ // <participants> list. The pair has to be [to, ownID] with ownID switched to
937
+ // our LID for @lid destinations; a participant list the server cannot
938
+ // reconcile is what comes back as 479.
945
939
  const isLidTarget = toJid.endsWith('@lid');
946
940
  const routingToJid = toJid;
947
941
  const ownLidUser = this._client._myLid ? phoneFromJid(this._client._myLid) : null;
@@ -955,8 +949,7 @@ class MessageSender {
955
949
  }
956
950
 
957
951
  // Enumerate the recipient's devices and our own in parallel, both in the
958
- // address family the destination JID dictates — Baileys does the same in one
959
- // getUSyncDevices([senderIdentity, jid]) call.
952
+ // address family the destination JID dictates.
960
953
  let otherJids, ownLinkedJids, ownPrimaryJid;
961
954
  if (isLidTarget) {
962
955
  const lidUser = phoneFromJid(toJid);
@@ -981,8 +974,7 @@ class MessageSender {
981
974
  ownPrimaryJid = this._client._ownDeviceJid();
982
975
  }
983
976
 
984
- // The device doing the sending is never one of its own recipients. Both
985
- // Baileys ("Skipping exact sender device") and whatsmeow drop it explicitly.
977
+ // The device doing the sending is never one of its own recipients.
986
978
  const ownPrimaryAddr = adString(ownPrimaryJid);
987
979
  otherJids = otherJids.filter(j => adString(j) !== ownPrimaryAddr);
988
980
  ownLinkedJids = ownLinkedJids.filter(j => adString(j) !== ownPrimaryAddr);
@@ -999,12 +991,12 @@ class MessageSender {
999
991
  // ─────────────────────────────────────────────────────────────────────────
1000
992
 
1001
993
  // The hash covers every device the server enumerates for this pair of users,
1002
- // our own primary included — whatsmeow hashes GetUserDevices([to, ownID]) in
1003
- // full and only skips the sending device when it encrypts. Excluding it here
1004
- // produced a hash over a different set than the server's.
994
+ // our own primary included — the sending device is skipped only when it
995
+ // comes to encrypting. Excluding it here produced a hash over a different
996
+ // set than the server's.
1005
997
  //
1006
- // It is not put on the stanza: neither whatsmeow nor Baileys sends phash on a
1007
- // 1:1 message, only on a group one. It is carried inside the (encrypted)
998
+ // It is not put on the stanza: phash belongs on a group message, not a 1:1
999
+ // one. It is carried inside the (encrypted)
1008
1000
  // DeviceSentMessage for our own devices, and compared against the phash the
1009
1001
  // server echoes back on the ack so a drifted device cache gets flushed.
1010
1002
  const phash = computePhash([...otherJids, ownPrimaryJid, ...ownLinkedJids]);
@@ -1115,8 +1107,7 @@ class MessageSender {
1115
1107
 
1116
1108
  // The server echoes its own participant hash on the ack. A difference means
1117
1109
  // its device list for this chat is not the one we just fanned out to, so the
1118
- // cached list is stale — drop it and let the next send re-run usync, exactly
1119
- // as whatsmeow does when the hashes disagree.
1110
+ // cached list is stale — drop it and let the next send re-run usync.
1120
1111
  if (dispatchResult && dispatchResult.phash && dispatchResult.phash !== phash) {
1121
1112
  _whaDbg('[DBG] PHASH_MISMATCH ours=' + phash + ' server=' + dispatchResult.phash +
1122
1113
  ' to=' + toJid + ' — flushing device cache');
@@ -1136,10 +1127,9 @@ class MessageSender {
1136
1127
  // Two targets never get one. WhatsApp's own announcement account
1137
1128
  // (0@s.whatsapp.net) and the bot numbers, including Meta AI, are not
1138
1129
  // contacts you can be "timelocked" against, and no real client issues to
1139
- // them — whatsmeow gates on shouldSendTCTokenInChatAction, Baileys on
1140
- // isBotOrPSA. Protocol messages (a revoke, an ephemeral-timer change) are
1141
- // out too: Baileys skips issuance for them because they are bookkeeping,
1142
- // not a reach-out. Attaching an existing token to them is still correct
1130
+ // them. Protocol messages (a revoke, an ephemeral-timer change) are out
1131
+ // too — they are bookkeeping, not a reach-out. Attaching an existing token
1132
+ // to them is still correct
1143
1133
  // and still happens above — this only governs asking for a new one.
1144
1134
  const isProtocolMsg = !!(options && options._protocol);
1145
1135
  if (tcStore && isRegularTcTokenUser(tcJid) && !isProtocolMsg &&
@@ -1219,9 +1209,9 @@ class MessageSender {
1219
1209
  }
1220
1210
 
1221
1211
  // Determine addressing mode for this group (lid = modern groups, pn = legacy).
1222
- // A Status carries none: whatsmeow sets addressing_mode only for the group
1223
- // server, and its recipients are ordinary contacts, so everything below
1224
- // stays in phone space.
1212
+ // A Status carries none: addressing_mode is set only for the group server,
1213
+ // and a Status goes to ordinary contacts, so everything below stays in
1214
+ // phone space.
1225
1215
  const groupAddressingMode = isStatus
1226
1216
  ? null
1227
1217
  : (this._client._groupAddressingMode.get(groupJid) || 'lid');
@@ -1258,9 +1248,8 @@ class MessageSender {
1258
1248
  // out of the member walk and put back through the own-device path below.
1259
1249
  // Left in, the walk enumerates our devices starting at device 0 — the very
1260
1250
  // device doing the sending — and it lands in <participants> as a recipient
1261
- // of our own SenderKey. Baileys skips the exact sender device explicitly
1262
- // ("Skipping exact sender device"), whatsmeow does the same with
1263
- // `jid == ownJID || jid == ownLID`.
1251
+ // of our own SenderKey. The exact sender device has to be skipped, matched
1252
+ // against both our phone JID and our LID.
1264
1253
  //
1265
1254
  // The phone branch already dropped us via `p !== ownPhone`; the LID branch
1266
1255
  // never did, so this only ever misfired on LID-addressed groups.
@@ -1313,10 +1302,10 @@ class MessageSender {
1313
1302
  ]);
1314
1303
 
1315
1304
  // Every device this group resolves to, our own primary included, deduped so
1316
- // a device reached through more than one path is counted once. whatsmeow
1317
- // hashes GetUserDevices(groupParticipants) in exactly this shape — the whole
1318
- // enumeration, sending device and all — and drops hosted endpoints first,
1319
- // since those are bot-side and never real participants.
1305
+ // a device reached through more than one path is counted once. The hash
1306
+ // covers the whole enumeration, sending device and all, with hosted
1307
+ // endpoints dropped first since those are bot-side and never real
1308
+ // participants.
1320
1309
  const isHostedJid = (jid) => {
1321
1310
  const s = String(jid);
1322
1311
  return s.endsWith('@hosted') || s.endsWith('@hosted.lid');
@@ -1334,14 +1323,14 @@ class MessageSender {
1334
1323
  [...memberDevices, ...lidDevices, ...ownTargets, ownSelfJid]
1335
1324
  )].filter(jid => !isHostedJid(jid));
1336
1325
 
1337
- // The participant hash the server checks the fan-out against. whatsmeow puts
1338
- // it on group stanzas (sendGroup sets node.Attrs["phash"]) and nowhere else;
1339
- // it is computed over the enumeration above, not over the encryption targets,
1326
+ // The participant hash the server checks the fan-out against. It goes on
1327
+ // group stanzas and nowhere else, and is computed over the enumeration
1328
+ // above rather than over the encryption targets,
1340
1329
  // so the sending device counts here even though it is not a recipient.
1341
1330
  const phash = computePhash(enumeratedDevices);
1342
1331
 
1343
- // Targets to actually encrypt for: everything but the sending device. Both
1344
- // Baileys and whatsmeow skip it explicitly at this point and not before.
1332
+ // Targets to actually encrypt for: everything but the sending device, and
1333
+ // it is skipped at this point rather than earlier.
1345
1334
  // Only the device actually sending. On a companion link that is not the
1346
1335
  // account's phone — the phone is a peer that has to receive the SenderKey
1347
1336
  // like everyone else, so excluding the bare number here would leave the
@@ -1364,8 +1353,8 @@ class MessageSender {
1364
1353
 
1365
1354
  const skStore = this._signal.senderKeyStore;
1366
1355
  const existingSkdmMap = skStore.getSKDMMap(groupJid);
1367
- // Same exclusions Baileys applies when choosing SenderKey recipients:
1368
- // hosted (bot-side) users and device 99, which is a server placeholder
1356
+ // Exclusions when choosing SenderKey recipients: hosted (bot-side) users
1357
+ // and device 99, which is a server placeholder
1369
1358
  // rather than a real endpoint, must never receive a distribution.
1370
1359
  const isSkdmEligible = (jid) => {
1371
1360
  const s = String(jid);
@@ -1408,10 +1397,10 @@ class MessageSender {
1408
1397
  msgContent.push(new BinaryNode('device-identity', {}, advBytes));
1409
1398
  }
1410
1399
  if (skdmEncrypted.length > 0) {
1411
- // Baileys applies the same extra attrs — mediatype included — to the
1412
- // SenderKey participant nodes as to the skmsg, so an image sent to a
1413
- // group carries mediatype on both. Passing null here left the SKDM
1414
- // <enc> nodes unlabelled.
1400
+ // The same extra attrs — mediatype included — belong on the SenderKey
1401
+ // participant nodes as on the skmsg, so an image sent to a group carries
1402
+ // mediatype on both. Passing null here left the SKDM <enc> nodes
1403
+ // unlabelled.
1415
1404
  msgContent.push(DeviceManager.buildParticipantsNode(skdmEncrypted, mediaSubtype));
1416
1405
  }
1417
1406
  const skmsgAttrs = { type: 'skmsg', v: '2' };
@@ -1429,10 +1418,9 @@ class MessageSender {
1429
1418
  _whaDbg('[DBG] REPORTING_TOKEN attached to=' + groupJid + ' id=' + msgId);
1430
1419
  }
1431
1420
 
1432
- // phash belongs on a group stanza and only on a group stanza — whatsmeow's
1433
- // sendGroup sets it, sendDM does not. It tells the server which device set we
1434
- // fanned out to; the server compares it against its own and echoes its hash
1435
- // back on the ack when they differ.
1421
+ // phash belongs on a group stanza and only on a group stanza. It tells the
1422
+ // server which device set we fanned out to; the server compares it against
1423
+ // its own and echoes its hash back on the ack when they differ.
1436
1424
  const stanzaAttrs = {
1437
1425
  to: jidStrToObj(groupJid),
1438
1426
  id: msgId,
@@ -1464,9 +1452,9 @@ class MessageSender {
1464
1452
 
1465
1453
  // A hash on the ack that differs from ours means the server's participant
1466
1454
  // list for this group is not the one we just fanned out to, so some members
1467
- // did not get the message. whatsmeow drops its group cache on exactly this
1468
- // signal; the device lists behind the members are dropped too, so the next
1469
- // send re-runs group metadata and usync rather than repeating the miss.
1455
+ // did not get the message. The group cache is dropped on exactly this
1456
+ // signal, and the device lists behind the members with it, so the next send
1457
+ // re-runs group metadata and usync rather than repeating the miss.
1470
1458
  if (dispatchResult && dispatchResult.phash && dispatchResult.phash !== phash) {
1471
1459
  _whaDbg('[DBG] GROUP_PHASH_MISMATCH ours=' + phash +
1472
1460
  ' server=' + dispatchResult.phash + ' group=' + groupJid +
@@ -1485,7 +1473,7 @@ class MessageSender {
1485
1473
 
1486
1474
  // ─── Dispatch and wait for ack ────────────────────────────────────────────
1487
1475
  //
1488
- // Error 463 recovery (mirrors Baileys messages-recv.js inFlight463Recoveries):
1476
+ // Error 463 recovery:
1489
1477
  // 1. 463 ack arrives → issue privacy token via _issuePrivacyTokens
1490
1478
  // 2. Wait for token to be stored in _tcTokenStore
1491
1479
  // 3. Re-send the SAME message node with a new msgId — the tctoken node
@@ -1496,7 +1484,7 @@ class MessageSender {
1496
1484
  //
1497
1485
  // Uses a SEPARATE _inFlight463Recoveries Set (not shared with the proactive
1498
1486
  // _inFlightTcTokenIssuance) so 463 recovery and proactive issuance never
1499
- // block each other. Matches Baileys pattern exactly.
1487
+ // block each other.
1500
1488
 
1501
1489
  _dispatchAndAck(node, msgId, _isRetry) {
1502
1490
  const self = this;
@@ -1659,8 +1647,8 @@ class MessageSender {
1659
1647
  key: { remoteJid: toJid, fromMe: !!fromMe, id: origMsgId }
1660
1648
  });
1661
1649
  const msgBuf = encodeMessage('protocol', revokePayload);
1662
- // ProtocolMessage is one of the explicit "text" cases in whatsmeow's
1663
- // getTypeFromMessage; there is no "protocol" stanza type.
1650
+ // A ProtocolMessage travels as stanza type "text"; there is no "protocol"
1651
+ // stanza type.
1664
1652
  // _protocol marks it for the tcToken issuance gate — see _sendDMMessage.
1665
1653
  return this._sendMessage(toJid, msgId, msgBuf, 'text',
1666
1654
  { ...opts, edit: editBit, _protocol: true });
@@ -16,10 +16,9 @@
16
16
  // not change the token. REPORTING_FIELDS below is that subset, and it is the
17
17
  // part that has to match byte for byte or the token is worthless.
18
18
  //
19
- // Ported field-for-field from Baileys' reporting-utils. whatsmeow derives the
20
- // key from (ownID, to) where Baileys uses the destination JID for both halves;
21
- // the reference followed here is Baileys, and since the server only checks the
22
- // token when a report is actually filed, neither choice is observable on send.
19
+ // The key is derived using the destination JID for both halves. Since the
20
+ // server only checks the token when a report is actually filed, the choice is
21
+ // not observable on send.
23
22
 
24
23
  const crypto = require('crypto');
25
24
  const { hkdf } = require('@noble/hashes/hkdf');
@@ -121,8 +120,7 @@ function encodeVarint(value) {
121
120
  // The result is sorted by field number, which is what makes the token
122
121
  // independent of the order the encoder happened to write the fields in.
123
122
  //
124
- // Returns null on malformed input — the caller then attaches no token, which is
125
- // the same thing Baileys does.
123
+ // Returns null on malformed input — the caller then attaches no token.
126
124
  function extractReportingTokenContent(data, cfg) {
127
125
  const out = [];
128
126
  let i = 0;
@@ -3,16 +3,14 @@
3
3
  const fs = require('fs');
4
4
  const path = require('path');
5
5
 
6
- // 7-day buckets, 4 buckets = ~28-day rolling window (matches Baileys tc-token-utils)
6
+ // 7-day buckets, 4 buckets = ~28-day rolling window
7
7
  const TC_BUCKET_DURATION = 604800;
8
8
  const TC_NUM_BUCKETS = 4;
9
- // whatsmeow prunes expired rows out of the token table at most once a day
10
- // (tcTokenDBPruneInterval). Ours is a JSON file, so the same cadence applies.
9
+ // Expired rows are pruned out of the token store at most once a day; it is a
10
+ // JSON file, so there is nothing to gain from doing it more often.
11
11
  const TC_PRUNE_INTERVAL_MS = 24 * 60 * 60 * 1000;
12
12
 
13
13
  // The two phone-number ranges WhatsApp uses for bots, including Meta AI.
14
- // Byte-for-byte the same expression in whatsmeow (botUserRegex) and in
15
- // Baileys (BOT_PHONE_REGEX).
16
14
  const BOT_USER_RE = /^1313555\d{4}$|^131655500\d{2}$/;
17
15
 
18
16
  function tcTokenExpired(timestamp) {
@@ -35,9 +33,8 @@ function shouldSendNewTcToken(senderTimestamp) {
35
33
 
36
34
  // Whether a JID is a real person we may hold a trusted-contact token for.
37
35
  //
38
- // whatsmeow gates issuance with shouldSendTCTokenInChatAction and Baileys gates
39
- // both issuance and storage with isRegularUser; the two agree on the rule:
40
- // a user address (phone or LID), not the PSA pseudo-contact 0@…, not a bot.
36
+ // The rule is: a user address (phone or LID), not the PSA pseudo-contact 0@…,
37
+ // and not a bot.
41
38
  // Without it we issue a privacy IQ to WhatsApp's own announcement account and
42
39
  // to Meta AI, which no real client ever does.
43
40
  function isRegularTcTokenUser(jid) {
@@ -78,10 +75,8 @@ class TcTokenStore {
78
75
 
79
76
  // Drop entries that are past the 28-day window on both counts.
80
77
  //
81
- // whatsmeow runs DeleteExpiredPrivacyTokens against the same cutoff and lets
82
- // its in-memory sender-timestamp map age out on the same boundary. An entry
83
- // whose token has expired but whose senderTimestamp is still inside the
84
- // window has to stay: that timestamp is what keeps us from re-issuing on
78
+ // An entry whose token has expired but whose senderTimestamp is still inside
79
+ // the window has to stay: that timestamp is what keeps us from re-issuing on
85
80
  // every single send.
86
81
  pruneExpired() {
87
82
  let removed = 0;
@@ -134,15 +129,14 @@ class TcTokenStore {
134
129
  }
135
130
 
136
131
  get(jid) {
137
- // whatsmeow prunes from ensureTCToken, i.e. on the read path, rate-limited
138
- // to once a day. Same here.
132
+ // Pruning happens on the read path, rate-limited to once a day.
139
133
  this._maybePrune();
140
134
  return this._map.get(normalizeJidForTcToken(jid)) || null;
141
135
  }
142
136
 
143
137
  // Returns true when the token was accepted.
144
138
  //
145
- // Two rejections, both taken from Baileys' storeTcTokensFromIqResult:
139
+ // Two rejections:
146
140
  // - a token with no timestamp is already expired by definition, so storing
147
141
  // it only wastes a slot and guarantees a miss on the next send;
148
142
  // - a token older than the one on file must never overwrite it. Token
package/lib/noise.js CHANGED
@@ -449,10 +449,9 @@ class NoiseSocket extends EventEmitter {
449
449
  // for a bug in the client; the credentials were rejected by the
450
450
  // server, which is a different thing and has different remedies.
451
451
  //
452
- // The names and numbers are whatsmeow's ConnectFailureReason, which
453
- // is the only place they are written down — WhatsApp publishes none
454
- // of this. Codes marked "wipe" are the ones its IsLoggedOut() covers:
455
- // the session is finished and retrying with it is pointless.
452
+ // WhatsApp publishes none of this, so the meanings below come from
453
+ // observing the server. Codes marked "wipe" mean the session is
454
+ // finished and retrying with it is pointless.
456
455
  const FAILURE = {
457
456
  '400': { wipe: false, text: 'generic failure.' },
458
457
  '401': { wipe: true, text: 'logged out. The session was unpaired ' +
@@ -226,10 +226,10 @@ function encodePollOption(name) {
226
226
  // PollCreationMessage { encKey=1, name=2, options=3, selectableOptionsCount=4 }
227
227
  //
228
228
  // The 32-byte secret was being written to encKey, inside the poll itself.
229
- // whatsmeow's BuildPollCreation leaves encKey unset and puts the secret in
230
- // Message.messageContextInfo.messageSecret instead — which is the copy a voter
231
- // derives their vote key from. In the wrong place it reached nobody, so the
232
- // poll could not be voted on.
229
+ // encKey has to stay unset; the secret belongs in
230
+ // Message.messageContextInfo.messageSecret, which is the copy a voter derives
231
+ // their vote key from. In the wrong place it reached nobody, so the poll could
232
+ // not be voted on.
233
233
  function encodePollCreationMessage(opts) {
234
234
  const messageSecret = opts.messageSecret || opts.encKey ||
235
235
  require('crypto').randomBytes(32);
@@ -278,15 +278,15 @@ class SignalProtocol {
278
278
 
279
279
  // ─── SenderKey group encrypt / decrypt ────────────────────────────────────
280
280
  //
281
- // Uses WaSignalGroup GroupCipher/GroupSessionBuilder (Baileys-compatible).
281
+ // Uses WaSignalGroup GroupCipher/GroupSessionBuilder.
282
282
  // SenderKeyName format: groupId::senderId::0 (device 0 for group identity).
283
283
 
284
284
  _senderKeyName(groupId, senderId) {
285
285
  // A SenderKey belongs to a device, not to an account. Every device that
286
286
  // posts to a group distributes its own key, so the name has to carry the
287
- // device index the way Baileys and whatsmeow do — otherwise two devices of
288
- // the same person share one slot and the second one to speak overwrites
289
- // the first, leaving its messages undecryptable.
287
+ // device index — otherwise two devices of the same person share one slot
288
+ // and the second one to speak overwrites the first, leaving its messages
289
+ // undecryptable.
290
290
  //
291
291
  // Bare JIDs still resolve to device 0, so everything already on disk keeps
292
292
  // the exact same key string.
@@ -209,8 +209,8 @@ class SignalStore {
209
209
  // SessionCipher.encrypt — which throws "No open session". bulkEncryptForDevices
210
210
  // drops whatever it cannot encrypt, so the stanza went out with a <to> entry
211
211
  // missing for that device and the server nacked the whole send with ack 479.
212
- // Baileys hit the same class of bug with its peerSessionsCache and fixed it by
213
- // validating through haveOpenSession() on every check.
212
+ // The fix is to validate the record itself on every check rather than
213
+ // trusting that a cached entry still holds an open session.
214
214
  hasSession(encodedAddress) {
215
215
  if (!this._openSessionMemo) this._openSessionMemo = new Map();
216
216
  const data = this._sessions[encodedAddress];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "whalibmob",
3
- "version": "5.9.1",
3
+ "version": "5.9.2",
4
4
  "description": "WhatsApp library for interaction with WhatsApp Mobile API no web",
5
5
  "author": "Kunboruto50",
6
6
  "main": "index.js",