@fer2809fl/baileys 7.0.1 → 7.0.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.
Files changed (273) hide show
  1. package/README.md +259 -595
  2. package/WAProto/WAProto.proto +916 -360
  3. package/WAProto/fix-imports.js +18 -22
  4. package/WAProto/index.d.ts +15471 -8638
  5. package/WAProto/index.js +80576 -45941
  6. package/lib/Defaults/index.d.ts +14 -18
  7. package/lib/Defaults/index.d.ts.map +1 -1
  8. package/lib/Defaults/index.js +110 -115
  9. package/lib/Defaults/index.js.map +1 -1
  10. package/lib/Signal/Group/group_cipher.js.map +1 -1
  11. package/lib/Signal/Group/keyhelper.d.ts.map +1 -1
  12. package/lib/Signal/Group/keyhelper.js.map +1 -1
  13. package/lib/Signal/Group/sender-chain-key.js.map +1 -1
  14. package/lib/Signal/Group/sender-key-message.js.map +1 -1
  15. package/lib/Signal/Group/sender-message-key.js.map +1 -1
  16. package/lib/Signal/libsignal.d.ts.map +1 -1
  17. package/lib/Signal/libsignal.js +17 -45
  18. package/lib/Signal/libsignal.js.map +1 -1
  19. package/lib/Signal/lid-mapping.d.ts +0 -4
  20. package/lib/Signal/lid-mapping.d.ts.map +1 -1
  21. package/lib/Signal/lid-mapping.js +0 -6
  22. package/lib/Signal/lid-mapping.js.map +1 -1
  23. package/lib/Socket/business.d.ts +532 -280
  24. package/lib/Socket/business.d.ts.map +1 -1
  25. package/lib/Socket/business.js +401 -358
  26. package/lib/Socket/chats.d.ts +2 -22
  27. package/lib/Socket/chats.d.ts.map +1 -1
  28. package/lib/Socket/chats.js +43 -314
  29. package/lib/Socket/chats.js.map +1 -1
  30. package/lib/Socket/communities.d.ts +641 -336
  31. package/lib/Socket/communities.d.ts.map +1 -1
  32. package/lib/Socket/communities.js +3 -6
  33. package/lib/Socket/communities.js.map +1 -1
  34. package/lib/Socket/dugong.d.ts +130 -0
  35. package/lib/Socket/dugong.d.ts.map +1 -0
  36. package/lib/Socket/dugong.js +772 -0
  37. package/lib/Socket/dugong.js.map +1 -0
  38. package/lib/Socket/groups.d.ts +2 -23
  39. package/lib/Socket/groups.d.ts.map +1 -1
  40. package/lib/Socket/groups.js +8 -171
  41. package/lib/Socket/groups.js.map +1 -1
  42. package/lib/Socket/index.d.ts +631 -322
  43. package/lib/Socket/index.d.ts.map +1 -1
  44. package/lib/Socket/index.js +12 -9
  45. package/lib/Socket/index.js.map +1 -1
  46. package/lib/Socket/messages-recv.d.ts +52 -84
  47. package/lib/Socket/messages-recv.d.ts.map +1 -1
  48. package/lib/Socket/messages-recv.js +185 -686
  49. package/lib/Socket/messages-recv.js.map +1 -1
  50. package/lib/Socket/messages-send.d.ts +643 -262
  51. package/lib/Socket/messages-send.d.ts.map +1 -1
  52. package/lib/Socket/messages-send.js +1559 -1359
  53. package/lib/Socket/messages-send.js.map +1 -1
  54. package/lib/Socket/mex.d.ts +0 -1
  55. package/lib/Socket/mex.d.ts.map +1 -1
  56. package/lib/Socket/mex.js +8 -22
  57. package/lib/Socket/mex.js.map +1 -1
  58. package/lib/Socket/native-buttons.js +231 -0
  59. package/lib/Socket/newsletter.d.ts +27 -42
  60. package/lib/Socket/newsletter.d.ts.map +1 -1
  61. package/lib/Socket/newsletter.js +439 -402
  62. package/lib/Socket/newsletter.js.map +1 -1
  63. package/lib/Socket/socket.d.ts +2 -6
  64. package/lib/Socket/socket.d.ts.map +1 -1
  65. package/lib/Socket/socket.js +26 -62
  66. package/lib/Socket/socket.js.map +1 -1
  67. package/lib/Types/Auth.d.ts +0 -1
  68. package/lib/Types/Auth.d.ts.map +1 -1
  69. package/lib/Types/Bussines.d.ts +34 -22
  70. package/lib/Types/Call.d.ts +1 -1
  71. package/lib/Types/Call.d.ts.map +1 -1
  72. package/lib/Types/Chat.d.ts +0 -1
  73. package/lib/Types/Chat.d.ts.map +1 -1
  74. package/lib/Types/Contact.d.ts +0 -2
  75. package/lib/Types/Contact.d.ts.map +1 -1
  76. package/lib/Types/Events.d.ts +1 -20
  77. package/lib/Types/Events.d.ts.map +1 -1
  78. package/lib/Types/GroupMetadata.d.ts +1 -4
  79. package/lib/Types/GroupMetadata.d.ts.map +1 -1
  80. package/lib/Types/Message.d.ts +348 -354
  81. package/lib/Types/Message.d.ts.map +1 -1
  82. package/lib/Types/Message.js +0 -4
  83. package/lib/Types/Message.js.map +1 -1
  84. package/lib/Types/{Mex.d.ts → Newsletter.d.ts} +7 -26
  85. package/lib/Types/Newsletter.d.ts.map +1 -0
  86. package/lib/Types/{Mex.js → Newsletter.js} +3 -11
  87. package/lib/Types/Newsletter.js.map +1 -0
  88. package/lib/Types/Signal.d.ts +1 -12
  89. package/lib/Types/Signal.d.ts.map +1 -1
  90. package/lib/Types/Socket.d.ts +148 -135
  91. package/lib/Types/Socket.d.ts.map +1 -1
  92. package/lib/Types/Socket.js.map +1 -1
  93. package/lib/Types/State.d.ts +0 -65
  94. package/lib/Types/State.d.ts.map +1 -1
  95. package/lib/Types/State.js +0 -43
  96. package/lib/Types/State.js.map +1 -1
  97. package/lib/Types/index.d.ts +52 -52
  98. package/lib/Types/index.d.ts.map +1 -1
  99. package/lib/Types/index.js +30 -23
  100. package/lib/Types/index.js.map +1 -1
  101. package/lib/Utils/auth-utils.d.ts +0 -5
  102. package/lib/Utils/auth-utils.d.ts.map +1 -1
  103. package/lib/Utils/auth-utils.js +0 -12
  104. package/lib/Utils/auth-utils.js.map +1 -1
  105. package/lib/Utils/browser-utils.d.ts.map +1 -1
  106. package/lib/Utils/browser-utils.js +0 -1
  107. package/lib/Utils/browser-utils.js.map +1 -1
  108. package/lib/Utils/chat-utils.d.ts +4 -34
  109. package/lib/Utils/chat-utils.d.ts.map +1 -1
  110. package/lib/Utils/chat-utils.js +20 -80
  111. package/lib/Utils/chat-utils.js.map +1 -1
  112. package/lib/Utils/crypto.d.ts +12 -12
  113. package/lib/Utils/crypto.d.ts.map +1 -1
  114. package/lib/Utils/crypto.js.map +1 -1
  115. package/lib/Utils/decode-wa-message.d.ts +0 -18
  116. package/lib/Utils/decode-wa-message.d.ts.map +1 -1
  117. package/lib/Utils/decode-wa-message.js +0 -34
  118. package/lib/Utils/decode-wa-message.js.map +1 -1
  119. package/lib/Utils/event-buffer.d.ts +0 -2
  120. package/lib/Utils/event-buffer.d.ts.map +1 -1
  121. package/lib/Utils/event-buffer.js +1 -49
  122. package/lib/Utils/event-buffer.js.map +1 -1
  123. package/lib/Utils/generics.d.ts +2 -3
  124. package/lib/Utils/generics.d.ts.map +1 -1
  125. package/lib/Utils/generics.js +1 -10
  126. package/lib/Utils/generics.js.map +1 -1
  127. package/lib/Utils/history.d.ts +0 -2
  128. package/lib/Utils/history.d.ts.map +1 -1
  129. package/lib/Utils/history.js +9 -12
  130. package/lib/Utils/history.js.map +1 -1
  131. package/lib/Utils/identity-change-handler.d.ts +0 -7
  132. package/lib/Utils/identity-change-handler.d.ts.map +1 -1
  133. package/lib/Utils/identity-change-handler.js +0 -1
  134. package/lib/Utils/identity-change-handler.js.map +1 -1
  135. package/lib/Utils/index.d.ts +23 -30
  136. package/lib/Utils/index.d.ts.map +1 -1
  137. package/lib/Utils/index.js +2 -9
  138. package/lib/Utils/index.js.map +1 -1
  139. package/lib/Utils/link-preview.js +2 -2
  140. package/lib/Utils/link-preview.js.map +1 -1
  141. package/lib/Utils/message-retry-manager.d.ts +0 -5
  142. package/lib/Utils/message-retry-manager.d.ts.map +1 -1
  143. package/lib/Utils/message-retry-manager.js +0 -40
  144. package/lib/Utils/message-retry-manager.js.map +1 -1
  145. package/lib/Utils/messages-media.d.ts +13 -45
  146. package/lib/Utils/messages-media.d.ts.map +1 -1
  147. package/lib/Utils/messages-media.js +98 -293
  148. package/lib/Utils/messages-media.js.map +1 -1
  149. package/lib/Utils/messages.d.ts +3 -16
  150. package/lib/Utils/messages.d.ts.map +1 -1
  151. package/lib/Utils/messages.js +1123 -1219
  152. package/lib/Utils/messages.js.map +1 -1
  153. package/lib/Utils/noise-handler.d.ts +1 -1
  154. package/lib/Utils/process-message.d.ts.map +1 -1
  155. package/lib/Utils/process-message.js +1 -103
  156. package/lib/Utils/process-message.js.map +1 -1
  157. package/lib/Utils/rich-messages.d.ts +277 -0
  158. package/lib/Utils/rich-messages.d.ts.map +1 -0
  159. package/lib/Utils/rich-messages.js +1144 -0
  160. package/lib/Utils/rich-messages.js.map +1 -0
  161. package/lib/Utils/signal.d.ts +0 -13
  162. package/lib/Utils/signal.d.ts.map +1 -1
  163. package/lib/Utils/signal.js +0 -42
  164. package/lib/Utils/signal.js.map +1 -1
  165. package/lib/Utils/sticker-pack.d.ts +10 -0
  166. package/lib/Utils/sticker-pack.js +111 -0
  167. package/lib/Utils/sync-action-utils.d.ts.map +1 -1
  168. package/lib/Utils/sync-action-utils.js +0 -1
  169. package/lib/Utils/sync-action-utils.js.map +1 -1
  170. package/lib/Utils/tc-token-utils.d.ts +1 -26
  171. package/lib/Utils/tc-token-utils.d.ts.map +1 -1
  172. package/lib/Utils/tc-token-utils.js +5 -151
  173. package/lib/Utils/tc-token-utils.js.map +1 -1
  174. package/lib/Utils/validate-connection.d.ts +0 -1
  175. package/lib/Utils/validate-connection.d.ts.map +1 -1
  176. package/lib/Utils/validate-connection.js +3 -13
  177. package/lib/Utils/validate-connection.js.map +1 -1
  178. package/lib/WABinary/decode.d.ts +1 -1
  179. package/lib/WABinary/decode.d.ts.map +1 -1
  180. package/lib/WABinary/generic-utils.d.ts +3 -1
  181. package/lib/WABinary/generic-utils.d.ts.map +1 -1
  182. package/lib/WABinary/generic-utils.js +78 -0
  183. package/lib/WABinary/generic-utils.js.map +1 -1
  184. package/lib/WABinary/jid-utils.d.ts +0 -11
  185. package/lib/WABinary/jid-utils.d.ts.map +1 -1
  186. package/lib/WABinary/jid-utils.js +0 -33
  187. package/lib/WABinary/jid-utils.js.map +1 -1
  188. package/lib/WAM/encode.d.ts +1 -1
  189. package/lib/WAM/encode.d.ts.map +1 -1
  190. package/lib/WAUSync/Protocols/USyncContactProtocol.d.ts.map +1 -1
  191. package/lib/WAUSync/Protocols/USyncContactProtocol.js +3 -26
  192. package/lib/WAUSync/Protocols/USyncContactProtocol.js.map +1 -1
  193. package/lib/WAUSync/Protocols/index.d.ts +0 -1
  194. package/lib/WAUSync/Protocols/index.d.ts.map +1 -1
  195. package/lib/WAUSync/Protocols/index.js +0 -1
  196. package/lib/WAUSync/Protocols/index.js.map +1 -1
  197. package/lib/WAUSync/USyncQuery.d.ts +0 -1
  198. package/lib/WAUSync/USyncQuery.d.ts.map +1 -1
  199. package/lib/WAUSync/USyncQuery.js +2 -6
  200. package/lib/WAUSync/USyncQuery.js.map +1 -1
  201. package/lib/WAUSync/USyncUser.d.ts +0 -4
  202. package/lib/WAUSync/USyncUser.d.ts.map +1 -1
  203. package/lib/WAUSync/USyncUser.js +0 -8
  204. package/lib/WAUSync/USyncUser.js.map +1 -1
  205. package/lib/index.d.ts +1 -1
  206. package/lib/index.d.ts.map +1 -1
  207. package/lib/index.js +1 -4
  208. package/lib/index.js.map +1 -1
  209. package/package.json +30 -34
  210. package/LICENSE +0 -21
  211. package/engine-requirements.js +0 -10
  212. package/lib/Socket/smgss.d.ts +0 -119
  213. package/lib/Socket/smgss.d.ts.map +0 -1
  214. package/lib/Socket/smgss.js +0 -453
  215. package/lib/Socket/smgss.js.map +0 -1
  216. package/lib/Store/index.d.ts +0 -4
  217. package/lib/Store/index.d.ts.map +0 -1
  218. package/lib/Store/index.js +0 -4
  219. package/lib/Store/index.js.map +0 -1
  220. package/lib/Store/make-in-memory-store.d.ts +0 -132
  221. package/lib/Store/make-in-memory-store.d.ts.map +0 -1
  222. package/lib/Store/make-in-memory-store.js +0 -450
  223. package/lib/Store/make-in-memory-store.js.map +0 -1
  224. package/lib/Store/make-ordered-dictionary.d.ts +0 -14
  225. package/lib/Store/make-ordered-dictionary.d.ts.map +0 -1
  226. package/lib/Store/make-ordered-dictionary.js +0 -80
  227. package/lib/Store/make-ordered-dictionary.js.map +0 -1
  228. package/lib/Store/object-repository.d.ts +0 -13
  229. package/lib/Store/object-repository.d.ts.map +0 -1
  230. package/lib/Store/object-repository.js +0 -24
  231. package/lib/Store/object-repository.js.map +0 -1
  232. package/lib/Types/Mex.d.ts.map +0 -1
  233. package/lib/Types/Mex.js.map +0 -1
  234. package/lib/Utils/button-handler.d.ts +0 -28
  235. package/lib/Utils/button-handler.d.ts.map +0 -1
  236. package/lib/Utils/button-handler.js +0 -140
  237. package/lib/Utils/button-handler.js.map +0 -1
  238. package/lib/Utils/companion-reg-client-utils.d.ts +0 -17
  239. package/lib/Utils/companion-reg-client-utils.d.ts.map +0 -1
  240. package/lib/Utils/companion-reg-client-utils.js +0 -35
  241. package/lib/Utils/companion-reg-client-utils.js.map +0 -1
  242. package/lib/Utils/interactive.d.ts +0 -60
  243. package/lib/Utils/interactive.d.ts.map +0 -1
  244. package/lib/Utils/interactive.js +0 -224
  245. package/lib/Utils/interactive.js.map +0 -1
  246. package/lib/Utils/message-composer.d.ts +0 -93
  247. package/lib/Utils/message-composer.d.ts.map +0 -1
  248. package/lib/Utils/message-composer.js +0 -311
  249. package/lib/Utils/message-composer.js.map +0 -1
  250. package/lib/Utils/messages-newsletter.d.ts +0 -74
  251. package/lib/Utils/messages-newsletter.d.ts.map +0 -1
  252. package/lib/Utils/messages-newsletter.js +0 -128
  253. package/lib/Utils/messages-newsletter.js.map +0 -1
  254. package/lib/Utils/shortcake-crypto.d.ts +0 -36
  255. package/lib/Utils/shortcake-crypto.d.ts.map +0 -1
  256. package/lib/Utils/shortcake-crypto.js +0 -71
  257. package/lib/Utils/shortcake-crypto.js.map +0 -1
  258. package/lib/Utils/shortcake.d.ts +0 -36
  259. package/lib/Utils/shortcake.d.ts.map +0 -1
  260. package/lib/Utils/shortcake.js +0 -162
  261. package/lib/Utils/shortcake.js.map +0 -1
  262. package/lib/Utils/use-single-file-auth-state.d.ts +0 -6
  263. package/lib/Utils/use-single-file-auth-state.d.ts.map +0 -1
  264. package/lib/Utils/use-single-file-auth-state.js +0 -111
  265. package/lib/Utils/use-single-file-auth-state.js.map +0 -1
  266. package/lib/Utils/use-sqlite-auth-state.d.ts +0 -14
  267. package/lib/Utils/use-sqlite-auth-state.d.ts.map +0 -1
  268. package/lib/Utils/use-sqlite-auth-state.js +0 -107
  269. package/lib/Utils/use-sqlite-auth-state.js.map +0 -1
  270. package/lib/WAUSync/Protocols/USyncUsernameProtocol.d.ts +0 -10
  271. package/lib/WAUSync/Protocols/USyncUsernameProtocol.d.ts.map +0 -1
  272. package/lib/WAUSync/Protocols/USyncUsernameProtocol.js +0 -25
  273. package/lib/WAUSync/Protocols/USyncUsernameProtocol.js.map +0 -1
@@ -4,56 +4,18 @@ import { randomBytes } from 'crypto';
4
4
  import Long from 'long';
5
5
  import { proto } from '../../WAProto/index.js';
6
6
  import { DEFAULT_CACHE_TTLS, KEY_BUNDLE_TYPE, MIN_PREKEY_COUNT, PLACEHOLDER_MAX_AGE_SECONDS, STATUS_EXPIRY_SECONDS } from '../Defaults/index.js';
7
- import { ReachoutTimelockEnforcementType, WAMessageStatus, WAMessageStubType } from '../Types/index.js';
8
- import { ACCOUNT_RESTRICTED_TEXT, aesDecryptCTR, aesEncryptGCM, cleanMessage, Curve, decodeMediaRetryNode, decodeMessageNode, decryptMessageNode, delay, derivePairingCodeKey, encodeBigEndian, encodeSignedDeviceIdentity, extractAddressingContext, extractE2ESessionFromRetryReceipt, getCallStatusFromNode, getHistoryMsg, getNextPreKeys, getPlatformType, getStatusFromReceiptType, handleIdentityChange, hkdf, makeShortcakeFlow, MISSING_KEYS_ERROR_TEXT, NACK_REASONS, NO_MESSAGE_FOUND_ERROR_TEXT, SERVER_ERROR_CODES, toNumber, unixTimestampSeconds, xmppPreKey, xmppSignedPreKey } from '../Utils/index.js';
7
+ import { WAMessageStatus, WAMessageStubType } from '../Types/index.js';
8
+ import { aesDecryptCTR, aesEncryptGCM, cleanMessage, Curve, decodeMediaRetryNode, decodeMessageNode, decryptMessageNode, delay, derivePairingCodeKey, encodeBigEndian, encodeSignedDeviceIdentity, extractAddressingContext, getCallStatusFromNode, getHistoryMsg, getNextPreKeys, getStatusFromReceiptType, handleIdentityChange, hkdf, MISSING_KEYS_ERROR_TEXT, NACK_REASONS, NO_MESSAGE_FOUND_ERROR_TEXT, toNumber, unixTimestampSeconds, xmppPreKey, xmppSignedPreKey } from '../Utils/index.js';
9
9
  import { makeMutex } from '../Utils/make-mutex.js';
10
10
  import { makeOfflineNodeProcessor } from '../Utils/offline-node-processor.js';
11
11
  import { buildAckStanza } from '../Utils/stanza-ack.js';
12
- import { buildMergedTcTokenIndexWrite, isTcTokenExpired, readTcTokenIndex, resolveIssuanceJid, resolveTcTokenJid, storeTcTokensFromIqResult, TC_TOKEN_INDEX_KEY } from '../Utils/tc-token-utils.js';
13
- import { areJidsSameUser, binaryNodeToString, getAllBinaryNodeChildren, getBinaryNodeChild, getBinaryNodeChildBuffer, getBinaryNodeChildren, getBinaryNodeChildString, getBinaryNodeChildUInt, isJidGroup, isJidNewsletter, isJidStatusBroadcast, isLidUser, isPnUser, jidDecode, jidNormalizedUser, S_WHATSAPP_NET } from '../WABinary/index.js';
12
+ import { areJidsSameUser, binaryNodeToString, getAllBinaryNodeChildren, getBinaryNodeChild, getBinaryNodeChildBuffer, getBinaryNodeChildren, getBinaryNodeChildString, isJidGroup, isJidNewsletter, isJidStatusBroadcast, isLidUser, isPnUser, jidDecode, jidNormalizedUser, S_WHATSAPP_NET } from '../WABinary/index.js';
14
13
  import { extractGroupMetadata } from './groups.js';
15
14
  import { makeMessagesSocket } from './messages-send.js';
16
- const ENFORCEMENT_TYPE_VALUES = new Set(Object.values(ReachoutTimelockEnforcementType));
17
- function isValidEnforcementType(value) {
18
- return typeof value === 'string' && ENFORCEMENT_TYPE_VALUES.has(value);
19
- }
20
15
  export const makeMessagesRecvSocket = (config) => {
21
16
  const { logger, retryRequestDelayMs, maxMsgRetryCount, getMessage, shouldIgnoreJid, enableAutoSessionRecreation } = config;
22
17
  const sock = makeMessagesSocket(config);
23
- const { userDevicesCache, devicesMutex, ev, authState, ws, messageMutex, notificationMutex, receiptMutex, signalRepository, query, upsertMessage, resyncAppState, onUnexpectedError, assertSessions, sendNode, relayMessage, sendReceipt, uploadPreKeys, sendPeerDataOperationMessage, messageRetryManager, registerSocketEndHandler, issuePrivacyTokens, fetchAccountReachoutTimelock, placeholderResendCache } = sock;
24
- const getLIDForPN = signalRepository.lidMapping.getLIDForPN.bind(signalRepository.lidMapping);
25
- /** Shortcake passkey-linking flow, only when a `signPasskeyAssertion` is configured. */
26
- const shortcakeFlow = config.signPasskeyAssertion
27
- ? makeShortcakeFlow({
28
- logger,
29
- query,
30
- signAssertion: config.signPasskeyAssertion,
31
- getCreds: () => authState.creds,
32
- updateCreds: patch => ev.emit('creds.update', patch),
33
- deviceType: getPlatformType(config.browser[1]),
34
- emitVerificationCode: code => logger.debug({ code }, 'shortcake verification code')
35
- })
36
- : null;
37
- registerSocketEndHandler(() => shortcakeFlow?.clearSession());
38
- /** Serializes prologue -> continuation off the notification mutex, so the ack isn't blocked by the handshake. */
39
- const shortcakeMutex = makeMutex();
40
- /** Runs the forced-passkey handshake (detached); with no signer, surfaces `passkeyRequired` and warns. */
41
- const handleShortcakeNotification = (node) => {
42
- if (node.attrs.type === 'passkey_prologue_request') {
43
- ev.emit('connection.update', { passkeyRequired: { hasSigner: !!shortcakeFlow } });
44
- }
45
- if (!shortcakeFlow) {
46
- if (node.attrs.type === 'passkey_prologue_request') {
47
- logger.warn({ id: node.attrs.id }, 'server requested passkey prologue but no signPasskeyAssertion configured');
48
- }
49
- return;
50
- }
51
- // run detached + serialized: the notification is acked immediately, and the
52
- // handshake (external signer + follow-up IQs) never blocks other notifications
53
- shortcakeMutex
54
- .mutex(() => shortcakeFlow.handleIncomingNotification(node))
55
- .catch(err => logger.error({ err, id: node.attrs.id }, 'shortcake handshake failed'));
56
- };
18
+ const { ev, authState, ws, messageMutex, notificationMutex, receiptMutex, signalRepository, query, upsertMessage, resyncAppState, onUnexpectedError, assertSessions, sendNode, relayMessage, sendReceipt, uploadPreKeys, sendPeerDataOperationMessage, messageRetryManager } = sock;
57
19
  /** this mutex ensures that each retryRequest will wait for the previous one to finish */
58
20
  const retryMutex = makeMutex();
59
21
  const msgRetryCache = config.msgRetryCounterCache ||
@@ -66,6 +28,11 @@ export const makeMessagesRecvSocket = (config) => {
66
28
  stdTTL: DEFAULT_CACHE_TTLS.CALL_OFFER, // 5 mins
67
29
  useClones: false
68
30
  });
31
+ const placeholderResendCache = config.placeholderResendCache ||
32
+ new NodeCache({
33
+ stdTTL: DEFAULT_CACHE_TTLS.MSG_RETRY, // 1 hour
34
+ useClones: false
35
+ });
69
36
  // Debounce identity-change session refreshes per JID to avoid bursts
70
37
  const identityAssertDebounce = new NodeCache({ stdTTL: 5, useClones: false });
71
38
  let sendActiveReceipts = false;
@@ -119,140 +86,25 @@ export const makeMessagesRecvSocket = (config) => {
119
86
  }, 8000);
120
87
  return sendPeerDataOperationMessage(pdoMessage);
121
88
  };
122
- const handleMexNotification = async (node) => {
123
- const updateNode = getBinaryNodeChild(node, 'update');
124
- if (updateNode) {
125
- const opName = updateNode.attrs?.op_name;
126
- if (!opName) {
127
- logger.warn({ node: binaryNodeToString(node) }, 'mex notification missing op_name, fallback to legacy');
128
- await handleLegacyMexNewsletterNotification(node);
129
- return;
130
- }
131
- let mexResponse;
132
- try {
133
- mexResponse = JSON.parse(updateNode.content.toString());
134
- }
135
- catch (error) {
136
- logger.error({ err: error, opName }, 'failed to parse mex notification JSON');
137
- return;
138
- }
139
- if (mexResponse.errors?.length) {
140
- logger.warn({ errors: mexResponse.errors, opName }, 'mex notification has GQL errors');
141
- return;
142
- }
143
- const data = mexResponse.data;
144
- if (!data) {
145
- logger.warn({ opName }, 'mex notification has null data');
146
- return;
147
- }
148
- logger.debug({ opName }, 'processing mex notification');
149
- switch (opName) {
150
- case 'NotificationUserReachoutTimelockUpdate':
151
- handleReachoutTimelockNotification(data);
152
- break;
153
- case 'MessageCappingInfoNotification':
154
- handleMessageCappingNotification(data);
155
- break;
156
- // newsletter ops still use the legacy <mex> child structure
157
- case 'NotificationNewsletterUpdate':
158
- case 'NotificationLinkedProfilesUpdates':
159
- case 'NotificationNewsletterAdminPromote':
160
- case 'NotificationNewsletterAdminDemote':
161
- case 'NotificationNewsletterUserSettingChange':
162
- case 'NotificationNewsletterJoin':
163
- case 'NotificationNewsletterLeave':
164
- case 'NotificationNewsletterStateChange':
165
- case 'NotificationNewsletterAdminMetadataUpdate':
166
- case 'NotificationNewsletterOwnerUpdate':
167
- case 'NotificationNewsletterAdminInviteRevoke':
168
- case 'NotificationNewsletterWamoSubStatusChange':
169
- case 'NotificationNewsletterBlockUser':
170
- case 'NotificationNewsletterPaidPartnership':
171
- case 'NotificationNewsletterMilestone':
172
- case 'NewsletterResponseStateUpdate':
173
- await handleLegacyMexNewsletterNotification(node);
174
- break;
175
- default:
176
- logger.debug({ opName }, 'unhandled mex notification');
177
- break;
178
- }
179
- return;
180
- }
181
- await handleLegacyMexNewsletterNotification(node);
182
- };
183
- const handleReachoutTimelockNotification = (data) => {
184
- const payload = data.xwa2_notify_account_reachout_timelock;
185
- if (!payload) {
186
- logger.warn('reachout timelock notification missing payload');
187
- return;
188
- }
189
- if (!payload.is_active) {
190
- logger.info('reachout timelock restriction lifted');
191
- ev.emit('connection.update', {
192
- reachoutTimeLock: {
193
- isActive: false,
194
- enforcementType: ReachoutTimelockEnforcementType.DEFAULT
195
- }
196
- });
197
- return;
198
- }
199
- // WA Web defaults to now+60s when the server omits the expiry
200
- const timeEnforcementEnds = payload.time_enforcement_ends
201
- ? new Date(parseInt(payload.time_enforcement_ends, 10) * 1000)
202
- : new Date(Date.now() + 60000);
203
- const enforcementType = isValidEnforcementType(payload.enforcement_type)
204
- ? payload.enforcement_type
205
- : ReachoutTimelockEnforcementType.DEFAULT;
206
- logger.info({ enforcementType, timeEnforcementEnds }, 'reachout timelock restriction set');
207
- ev.emit('connection.update', {
208
- reachoutTimeLock: {
209
- isActive: true,
210
- timeEnforcementEnds,
211
- enforcementType
212
- }
213
- });
214
- };
215
- const handleMessageCappingNotification = (data) => {
216
- const payload = data.xwa2_notify_new_chat_messages_capping_info_update;
217
- if (!payload) {
218
- logger.warn('message capping notification missing payload');
219
- return;
220
- }
221
- logger.info({ payload }, 'received message capping update');
222
- ev.emit('message-capping.update', payload);
223
- };
224
- const handleLegacyMexNewsletterNotification = async (node) => {
89
+ // Handles mex newsletter notifications
90
+ const handleMexNewsletterNotification = async (node) => {
225
91
  const mexNode = getBinaryNodeChild(node, 'mex');
226
- const updateNode = mexNode?.content ? null : getBinaryNodeChild(node, 'update') || getAllBinaryNodeChildren(node)[0];
227
- const payloadNode = mexNode?.content ? mexNode : updateNode;
228
- if (!payloadNode?.content) {
229
- logger.warn({ node: binaryNodeToString(node) }, 'invalid mex newsletter notification');
92
+ if (!mexNode?.content) {
93
+ logger.warn({ node }, 'Invalid mex newsletter notification');
230
94
  return;
231
95
  }
232
96
  let data;
233
97
  try {
234
- const payloadContent = payloadNode.content;
235
- if (Array.isArray(payloadContent)) {
236
- logger.warn({ payloadNode }, 'invalid mex newsletter notification payload format');
237
- return;
238
- }
239
- const contentBuf = typeof payloadContent === 'string' ? Buffer.from(payloadContent, 'binary') : Buffer.from(payloadContent);
240
- data = JSON.parse(contentBuf.toString());
98
+ data = JSON.parse(mexNode.content.toString());
241
99
  }
242
100
  catch (error) {
243
- logger.error({ err: error, node: binaryNodeToString(node) }, 'failed to parse mex newsletter notification');
101
+ logger.error({ err: error, node }, 'Failed to parse mex newsletter notification');
244
102
  return;
245
103
  }
246
- const operation = data?.operation ?? payloadNode?.attrs?.op_name;
247
- let updates = data?.updates;
248
- if (!updates) {
249
- const linkedProfiles = data?.data?.xwa2_notify_linked_profiles;
250
- if (linkedProfiles) {
251
- updates = [linkedProfiles];
252
- }
253
- }
104
+ const operation = data?.operation;
105
+ const updates = data?.updates;
254
106
  if (!updates || !operation) {
255
- logger.warn({ data }, 'invalid mex newsletter notification content');
107
+ logger.warn({ data }, 'Invalid mex newsletter notification content');
256
108
  return;
257
109
  }
258
110
  logger.info({ operation, updates }, 'got mex newsletter notification');
@@ -280,114 +132,91 @@ export const makeMessagesRecvSocket = (config) => {
280
132
  }
281
133
  }
282
134
  break;
283
- case 'NotificationLinkedProfilesUpdates':
284
- for (const update of updates) {
285
- const lid = update?.jid;
286
- const addedProfiles = Array.isArray(update?.added_profiles) ? update.added_profiles : [];
287
- const mappings = [];
288
- for (const profile of addedProfiles) {
289
- const pn = typeof profile === 'string' ? profile : (profile?.pn ?? profile?.jid ?? null);
290
- if (lid && pn) {
291
- const mapping = { lid, pn };
292
- ev.emit('lid-mapping.update', mapping);
293
- mappings.push(mapping);
294
- }
295
- }
296
- await signalRepository.lidMapping.storeLIDPNMappings(mappings);
297
- }
298
- break;
299
135
  default:
300
- logger.info({ operation, data }, 'unhandled mex newsletter notification');
136
+ logger.info({ operation, data }, 'Unhandled mex newsletter notification');
301
137
  break;
302
138
  }
303
139
  };
304
140
  // Handles newsletter notifications
305
141
  const handleNewsletterNotification = async (node) => {
306
142
  const from = node.attrs.from;
307
- const children = getAllBinaryNodeChildren(node);
143
+ const child = getAllBinaryNodeChildren(node)[0];
308
144
  const author = node.attrs.participant;
309
- for (const child of children) {
310
- logger.debug({ from, child }, 'got newsletter notification');
311
- switch (child.tag) {
312
- case 'reaction': {
313
- const reactionUpdate = {
314
- id: from,
315
- server_id: child.attrs.message_id,
316
- reaction: {
317
- code: getBinaryNodeChildString(child, 'reaction'),
318
- count: 1
319
- }
320
- };
321
- ev.emit('newsletter.reaction', reactionUpdate);
322
- break;
323
- }
324
- case 'view': {
325
- const viewUpdate = {
326
- id: from,
327
- server_id: child.attrs.message_id,
328
- count: parseInt(child.content?.toString() || '0', 10)
329
- };
330
- ev.emit('newsletter.view', viewUpdate);
331
- break;
332
- }
333
- case 'participant': {
334
- const participantUpdate = {
145
+ logger.info({ from, child }, 'got newsletter notification');
146
+ switch (child.tag) {
147
+ case 'reaction':
148
+ const reactionUpdate = {
149
+ id: from,
150
+ server_id: child.attrs.message_id,
151
+ reaction: {
152
+ code: getBinaryNodeChildString(child, 'reaction'),
153
+ count: 1
154
+ }
155
+ };
156
+ ev.emit('newsletter.reaction', reactionUpdate);
157
+ break;
158
+ case 'view':
159
+ const viewUpdate = {
160
+ id: from,
161
+ server_id: child.attrs.message_id,
162
+ count: parseInt(child.content?.toString() || '0', 10)
163
+ };
164
+ ev.emit('newsletter.view', viewUpdate);
165
+ break;
166
+ case 'participant':
167
+ const participantUpdate = {
168
+ id: from,
169
+ author,
170
+ user: child.attrs.jid,
171
+ action: child.attrs.action,
172
+ new_role: child.attrs.role
173
+ };
174
+ ev.emit('newsletter-participants.update', participantUpdate);
175
+ break;
176
+ case 'update':
177
+ const settingsNode = getBinaryNodeChild(child, 'settings');
178
+ if (settingsNode) {
179
+ const update = {};
180
+ const nameNode = getBinaryNodeChild(settingsNode, 'name');
181
+ if (nameNode?.content)
182
+ update.name = nameNode.content.toString();
183
+ const descriptionNode = getBinaryNodeChild(settingsNode, 'description');
184
+ if (descriptionNode?.content)
185
+ update.description = descriptionNode.content.toString();
186
+ ev.emit('newsletter-settings.update', {
335
187
  id: from,
336
- author,
337
- user: child.attrs.jid,
338
- action: child.attrs.action,
339
- new_role: child.attrs.role
340
- };
341
- ev.emit('newsletter-participants.update', participantUpdate);
342
- break;
188
+ update
189
+ });
343
190
  }
344
- case 'update': {
345
- const settingsNode = getBinaryNodeChild(child, 'settings');
346
- if (settingsNode) {
347
- const update = {};
348
- const nameNode = getBinaryNodeChild(settingsNode, 'name');
349
- if (nameNode?.content)
350
- update.name = nameNode.content.toString();
351
- const descriptionNode = getBinaryNodeChild(settingsNode, 'description');
352
- if (descriptionNode?.content)
353
- update.description = descriptionNode.content.toString();
354
- ev.emit('newsletter-settings.update', {
355
- id: from,
356
- update
357
- });
191
+ break;
192
+ case 'message':
193
+ const plaintextNode = getBinaryNodeChild(child, 'plaintext');
194
+ if (plaintextNode?.content) {
195
+ try {
196
+ const contentBuf = typeof plaintextNode.content === 'string'
197
+ ? Buffer.from(plaintextNode.content, 'binary')
198
+ : Buffer.from(plaintextNode.content);
199
+ const messageProto = proto.Message.decode(contentBuf).toJSON();
200
+ const fullMessage = proto.WebMessageInfo.fromObject({
201
+ key: {
202
+ remoteJid: from,
203
+ id: child.attrs.message_id || child.attrs.server_id,
204
+ fromMe: false // TODO: is this really true though
205
+ },
206
+ message: messageProto,
207
+ messageTimestamp: +child.attrs.t
208
+ }).toJSON();
209
+ await upsertMessage(fullMessage, 'append');
210
+ logger.info('Processed plaintext newsletter message');
358
211
  }
359
- break;
360
- }
361
- case 'message': {
362
- const plaintextNode = getBinaryNodeChild(child, 'plaintext');
363
- if (plaintextNode?.content) {
364
- try {
365
- const contentBuf = typeof plaintextNode.content === 'string'
366
- ? Buffer.from(plaintextNode.content, 'binary')
367
- : Buffer.from(plaintextNode.content);
368
- const messageProto = proto.Message.decode(contentBuf).toJSON();
369
- const fullMessage = proto.WebMessageInfo.fromObject({
370
- key: {
371
- remoteJid: from,
372
- id: child.attrs.message_id || child.attrs.server_id,
373
- fromMe: false // TODO: is this really true though
374
- },
375
- message: messageProto,
376
- messageTimestamp: +child.attrs.t
377
- }).toJSON();
378
- await upsertMessage(fullMessage, 'append');
379
- logger.debug('Processed plaintext newsletter message');
380
- }
381
- catch (error) {
382
- logger.error({ error }, 'Failed to decode plaintext newsletter message');
383
- }
212
+ catch (error) {
213
+ logger.error({ error }, 'Failed to decode plaintext newsletter message');
384
214
  }
385
- break;
386
215
  }
387
- default:
388
- logger.warn({ node, child }, 'Unknown newsletter notification child');
389
- break;
390
- }
216
+ break;
217
+ default:
218
+ logger.warn({ node }, 'Unknown newsletter notification');
219
+ break;
391
220
  }
392
221
  };
393
222
  const sendMessageAck = async (node, errorCode) => {
@@ -547,58 +376,15 @@ export const makeMessagesRecvSocket = (config) => {
547
376
  logger.info({ msgAttrs: node.attrs, retryCount }, 'sent retry receipt');
548
377
  }, authState?.creds?.me?.id || 'sendRetryRequest');
549
378
  };
550
- // Mirrors WAWeb/Handle/PreKeyLow.js: skip a re-issued notification with the same stanza id.
551
- const inFlightPreKeyLow = new Set();
552
- /**
553
- * Fire-and-forget tctoken re-issuance after a peer's device identity changed.
554
- * Mirrors WAWebSendTcTokenWhenDeviceIdentityChange — runs in parallel with
555
- * the session refresh (not after it).
556
- */
557
- const reissueTcTokenAfterIdentityChange = (from) => {
558
- void (async () => {
559
- const normalizedJid = jidNormalizedUser(from);
560
- const tcJid = await resolveTcTokenJid(normalizedJid, getLIDForPN);
561
- const tcTokenData = await authState.keys.get('tctoken', [tcJid]);
562
- const senderTs = tcTokenData?.[tcJid]?.senderTimestamp;
563
- if (senderTs === null || senderTs === undefined || isTcTokenExpired(senderTs)) {
564
- return;
565
- }
566
- logger.debug({ jid: normalizedJid, senderTimestamp: senderTs }, 'identity changed, re-issuing tctoken');
567
- const getPNForLID = signalRepository.lidMapping.getPNForLID.bind(signalRepository.lidMapping);
568
- const issueJid = await resolveIssuanceJid(normalizedJid, sock.serverProps.lidTrustedTokenIssueToLid, getLIDForPN, getPNForLID);
569
- const result = await issuePrivacyTokens([issueJid], senderTs);
570
- await storeTcTokensFromIqResult({
571
- result,
572
- fallbackJid: tcJid,
573
- keys: authState.keys,
574
- getLIDForPN,
575
- onNewJidStored: trackTcTokenJid
576
- });
577
- })().catch(err => {
578
- logger.debug({ jid: from, err: err?.message }, 'failed to re-issue tctoken after identity change');
579
- });
580
- };
581
379
  const handleEncryptNotification = async (node) => {
582
380
  const from = node.attrs.from;
583
381
  if (from === S_WHATSAPP_NET) {
584
- const stanzaId = node.attrs.id;
585
- if (stanzaId && inFlightPreKeyLow.has(stanzaId)) {
586
- return;
587
- }
588
382
  const countChild = getBinaryNodeChild(node, 'count');
589
383
  const count = +countChild.attrs.value;
590
384
  const shouldUploadMorePreKeys = count < MIN_PREKEY_COUNT;
591
385
  logger.debug({ count, shouldUploadMorePreKeys }, 'recv pre-key count');
592
386
  if (shouldUploadMorePreKeys) {
593
- if (stanzaId)
594
- inFlightPreKeyLow.add(stanzaId);
595
- try {
596
- await uploadPreKeys();
597
- }
598
- finally {
599
- if (stanzaId)
600
- inFlightPreKeyLow.delete(stanzaId);
601
- }
387
+ await uploadPreKeys();
602
388
  }
603
389
  }
604
390
  else {
@@ -608,8 +394,7 @@ export const makeMessagesRecvSocket = (config) => {
608
394
  validateSession: signalRepository.validateSession,
609
395
  assertSessions,
610
396
  debounceCache: identityAssertDebounce,
611
- logger,
612
- onBeforeSessionRefresh: reissueTcTokenAfterIdentityChange
397
+ logger
613
398
  });
614
399
  if (result.action === 'no_identity_node') {
615
400
  logger.info({ node }, 'unknown encrypt notification');
@@ -620,7 +405,6 @@ export const makeMessagesRecvSocket = (config) => {
620
405
  // TODO: Support PN/LID (Here is only LID now)
621
406
  const actingParticipantLid = fullNode.attrs.participant;
622
407
  const actingParticipantPn = fullNode.attrs.participant_pn;
623
- const actingParticipantUsername = fullNode.attrs.participant_username;
624
408
  const affectedParticipantLid = getBinaryNodeChild(child, 'participant')?.attrs?.jid || actingParticipantLid;
625
409
  const affectedParticipantPn = getBinaryNodeChild(child, 'participant')?.attrs?.phone_number || actingParticipantPn;
626
410
  switch (child?.tag) {
@@ -640,8 +424,7 @@ export const makeMessagesRecvSocket = (config) => {
640
424
  {
641
425
  ...metadata,
642
426
  author: actingParticipantLid,
643
- authorPn: actingParticipantPn,
644
- authorUsername: actingParticipantUsername
427
+ authorPn: actingParticipantPn
645
428
  }
646
429
  ]);
647
430
  break;
@@ -667,13 +450,11 @@ export const makeMessagesRecvSocket = (config) => {
667
450
  const stubType = `GROUP_PARTICIPANT_${child.tag.toUpperCase()}`;
668
451
  msg.messageStubType = WAMessageStubType[stubType];
669
452
  const participants = getBinaryNodeChildren(child, 'participant').map(({ attrs }) => {
670
- const isLid = isLidUser(attrs.jid);
671
- const hasPn = isPnUser(attrs.phone_number);
453
+ // TODO: Store LID MAPPINGS
672
454
  return {
673
- id: isLid && hasPn ? attrs.phone_number : attrs.jid,
674
- phoneNumber: isLid && hasPn ? attrs.phone_number : undefined,
675
- lid: isLid ? attrs.jid : isPnUser(attrs.jid) && isLidUser(attrs.lid) ? attrs.lid : undefined,
676
- username: attrs.participant_username || attrs.username || undefined,
455
+ id: attrs.jid,
456
+ phoneNumber: isLidUser(attrs.jid) && isPnUser(attrs.phone_number) ? attrs.phone_number : undefined,
457
+ lid: isPnUser(attrs.jid) && isLidUser(attrs.lid) ? attrs.lid : undefined,
677
458
  admin: (attrs.type || null)
678
459
  };
679
460
  });
@@ -743,89 +524,6 @@ export const makeMessagesRecvSocket = (config) => {
743
524
  break;
744
525
  }
745
526
  };
746
- const handleDevicesNotification = async (node) => {
747
- const [child] = getAllBinaryNodeChildren(node);
748
- const from = jidNormalizedUser(node.attrs.from);
749
- if (!child) {
750
- logger.debug({ from }, 'devices notification missing child, skipping');
751
- return;
752
- }
753
- const tag = child.tag;
754
- const deviceHash = child.attrs.device_hash;
755
- const devices = getBinaryNodeChildren(child, 'device');
756
- if (areJidsSameUser(from, authState.creds.me.id) || areJidsSameUser(from, authState.creds.me.lid)) {
757
- const deviceJids = devices.map(d => d.attrs.jid);
758
- logger.info({ deviceJids }, 'got my own devices');
759
- }
760
- if (!devices.length) {
761
- logger.debug({ from, tag }, 'no devices in notification, skipping');
762
- return;
763
- }
764
- const decoded = [];
765
- for (const d of devices) {
766
- const jid = d.attrs.jid;
767
- if (!jid)
768
- continue;
769
- const parts = jidDecode(jid);
770
- if (!parts) {
771
- logger.debug({ jid }, 'failed to decode device jid, skipping');
772
- continue;
773
- }
774
- decoded.push({ jid, user: parts.user, server: parts.server, device: parts.device });
775
- }
776
- if (!decoded.length)
777
- return;
778
- await devicesMutex.mutex(async () => {
779
- const byUser = new Map();
780
- for (const d of decoded) {
781
- const list = byUser.get(d.user) || [];
782
- list.push(d);
783
- byUser.set(d.user, list);
784
- }
785
- for (const [user, entries] of byUser) {
786
- if (tag === 'update') {
787
- logger.debug({ user }, `${user}'s device list updated, dropping cached devices`);
788
- await userDevicesCache?.del(user);
789
- continue;
790
- }
791
- if (tag === 'remove') {
792
- await signalRepository.deleteSession(entries.map(e => e.jid));
793
- }
794
- const existingCache = (await userDevicesCache?.get(user)) || [];
795
- if (!existingCache.length) {
796
- // No baseline yet; skip applying the delta so getUSyncDevices can
797
- // later fetch the full device list. Caching just the notification
798
- // entries would make a partial list look authoritative.
799
- logger.debug({ user, tag }, 'device list not cached, deferring to USync refresh');
800
- continue;
801
- }
802
- const affected = new Set(entries.map(e => e.device));
803
- let updatedDevices;
804
- switch (tag) {
805
- case 'add':
806
- logger.info({ deviceHash, count: entries.length }, 'devices added');
807
- updatedDevices = [
808
- ...existingCache.filter(d => !affected.has(d.device)),
809
- ...entries.map(e => ({ user: e.user, server: e.server, device: e.device }))
810
- ];
811
- break;
812
- case 'remove':
813
- logger.info({ deviceHash, count: entries.length }, 'devices removed');
814
- updatedDevices = existingCache.filter(d => !affected.has(d.device));
815
- break;
816
- default:
817
- logger.debug({ tag }, 'Unknown device list change tag');
818
- continue;
819
- }
820
- if (updatedDevices.length === 0) {
821
- await userDevicesCache?.del(user);
822
- }
823
- else {
824
- await userDevicesCache?.set(user, updatedDevices);
825
- }
826
- }
827
- });
828
- };
829
527
  const processNotification = async (node) => {
830
528
  const result = {};
831
529
  const [child] = getAllBinaryNodeChildren(node);
@@ -836,7 +534,7 @@ export const makeMessagesRecvSocket = (config) => {
836
534
  await handleNewsletterNotification(node);
837
535
  break;
838
536
  case 'mex':
839
- await handleMexNotification(node);
537
+ await handleMexNewsletterNotification(node);
840
538
  break;
841
539
  case 'w:gp2':
842
540
  // TODO: HANDLE PARTICIPANT_PN
@@ -850,12 +548,13 @@ export const makeMessagesRecvSocket = (config) => {
850
548
  await handleEncryptNotification(node);
851
549
  break;
852
550
  case 'devices':
853
- try {
854
- await handleDevicesNotification(node);
855
- }
856
- catch (error) {
857
- logger.error({ error, node }, 'failed to handle devices notification');
551
+ const devices = getBinaryNodeChildren(child, 'device');
552
+ if (areJidsSameUser(child.attrs.jid, authState.creds.me.id) ||
553
+ areJidsSameUser(child.attrs.lid, authState.creds.me.lid)) {
554
+ const deviceData = devices.map(d => ({ id: d.attrs.jid, lid: d.attrs.lid }));
555
+ logger.info({ deviceData }, 'my own devices changed');
858
556
  }
557
+ //TODO: drop a new event, add hashes
859
558
  break;
860
559
  case 'server_sync':
861
560
  const update = getBinaryNodeChild(node, 'collection');
@@ -973,10 +672,6 @@ export const makeMessagesRecvSocket = (config) => {
973
672
  authState.creds.registered = true;
974
673
  ev.emit('creds.update', authState.creds);
975
674
  break;
976
- case 'passkey_prologue_request':
977
- case 'crsc_continuation':
978
- handleShortcakeNotification(node);
979
- break;
980
675
  case 'privacy_token':
981
676
  await handlePrivacyTokenNotification(node);
982
677
  break;
@@ -985,70 +680,27 @@ export const makeMessagesRecvSocket = (config) => {
985
680
  return result;
986
681
  }
987
682
  };
988
- /**
989
- * In-memory cache of storage JIDs with stored tctokens, seeded from the persisted index.
990
- * Used to coalesce writes during a session; pruning always re-reads the persisted index
991
- * to cover writes made by other layers (e.g. history sync).
992
- */
993
- const tcTokenKnownJids = new Set();
994
- const tcTokenIndexLoaded = (async () => {
995
- try {
996
- const jids = await readTcTokenIndex(authState.keys);
997
- for (const jid of jids)
998
- tcTokenKnownJids.add(jid);
999
- logger.debug({ count: tcTokenKnownJids.size }, 'loaded tctoken index');
1000
- }
1001
- catch (err) {
1002
- logger.warn({ err: err?.message }, 'failed to load tctoken index');
1003
- }
1004
- })();
1005
- let tcTokenIndexTimer;
1006
- async function flushTcTokenIndex() {
1007
- if (tcTokenIndexTimer) {
1008
- clearTimeout(tcTokenIndexTimer);
1009
- tcTokenIndexTimer = undefined;
1010
- }
1011
- // Merge with whatever is already persisted so we don't clobber writes from other
1012
- // paths (history sync, concurrent sessions on the same store).
1013
- const write = await buildMergedTcTokenIndexWrite(authState.keys, tcTokenKnownJids);
1014
- return authState.keys.set({ tctoken: write });
1015
- }
1016
- function scheduleTcTokenIndexSave() {
1017
- if (tcTokenIndexTimer) {
1018
- clearTimeout(tcTokenIndexTimer);
1019
- }
1020
- tcTokenIndexTimer = setTimeout(() => {
1021
- tcTokenIndexTimer = undefined;
1022
- flushTcTokenIndex().catch(err => {
1023
- logger.warn({ err: err?.message }, 'failed to save tctoken index');
1024
- });
1025
- }, 5000);
1026
- }
1027
- function trackTcTokenJid(jid) {
1028
- if (jid && jid !== TC_TOKEN_INDEX_KEY && !tcTokenKnownJids.has(jid)) {
1029
- tcTokenKnownJids.add(jid);
1030
- scheduleTcTokenIndexSave();
1031
- }
1032
- }
1033
683
  const handlePrivacyTokenNotification = async (node) => {
1034
684
  const tokensNode = getBinaryNodeChild(node, 'tokens');
685
+ const from = jidNormalizedUser(node.attrs.from);
1035
686
  if (!tokensNode)
1036
687
  return;
1037
- const from = jidNormalizedUser(node.attrs.from);
1038
- // WA Web uses: senderLid ?? toLid(from) for the storage key
1039
- // The sender_lid attribute provides the LID directly when available
1040
- const senderLid = node.attrs.sender_lid && isLidUser(jidNormalizedUser(node.attrs.sender_lid))
1041
- ? jidNormalizedUser(node.attrs.sender_lid)
1042
- : undefined;
1043
- const fallbackJid = senderLid ?? (await resolveTcTokenJid(from, getLIDForPN));
1044
- logger.debug({ from, storageJid: fallbackJid }, 'processing privacy token notification');
1045
- await storeTcTokensFromIqResult({
1046
- result: node,
1047
- fallbackJid,
1048
- keys: authState.keys,
1049
- getLIDForPN,
1050
- onNewJidStored: trackTcTokenJid
1051
- });
688
+ const tokenNodes = getBinaryNodeChildren(tokensNode, 'token');
689
+ for (const tokenNode of tokenNodes) {
690
+ const { attrs, content } = tokenNode;
691
+ const type = attrs.type;
692
+ const timestamp = attrs.t;
693
+ if (type === 'trusted_contact' && content instanceof Buffer) {
694
+ logger.debug({
695
+ from,
696
+ timestamp,
697
+ tcToken: content
698
+ }, 'received trusted contact token');
699
+ await authState.keys.set({
700
+ tctoken: { [from]: { token: content, timestamp } }
701
+ });
702
+ }
703
+ }
1052
704
  };
1053
705
  async function decipherLinkPublicKey(data) {
1054
706
  const buffer = toRequiredBuffer(data);
@@ -1074,11 +726,10 @@ export const makeMessagesRecvSocket = (config) => {
1074
726
  const newValue = ((await msgRetryCache.get(key)) || 0) + 1;
1075
727
  await msgRetryCache.set(key, newValue);
1076
728
  };
1077
- const sendMessagesAgain = async (key, ids, retryNode, receiptNode) => {
729
+ const sendMessagesAgain = async (key, ids, retryNode) => {
1078
730
  const remoteJid = key.remoteJid;
1079
731
  const participant = key.participant || remoteJid;
1080
732
  const retryCount = +retryNode.attrs.count || 1;
1081
- const msgId = ids[0];
1082
733
  // Try to get messages from cache first, then fallback to getMessage
1083
734
  const msgs = [];
1084
735
  for (const id of ids) {
@@ -1110,49 +761,12 @@ export const makeMessagesRecvSocket = (config) => {
1110
761
  // just re-send the message to everyone
1111
762
  // prevents the first message decryption failure
1112
763
  const sendToAll = !jidDecode(participant)?.device;
1113
- const sessionId = signalRepository.jidToSignalProtocolAddress(participant);
1114
- let injectedFromBundle = false;
1115
- const bundle = extractE2ESessionFromRetryReceipt(receiptNode);
1116
- if (bundle) {
1117
- try {
1118
- await signalRepository.injectE2ESession({ jid: participant, session: bundle });
1119
- injectedFromBundle = true;
1120
- logger.debug({ participant, retryCount }, 'injected session from retry receipt key bundle');
1121
- }
1122
- catch (error) {
1123
- logger.warn({ error, participant }, 'failed to inject session from retry receipt');
1124
- }
1125
- }
1126
- if (!injectedFromBundle) {
1127
- const receivedRegId = getBinaryNodeChildUInt(receiptNode, 'registration', 4);
1128
- if (typeof receivedRegId === 'number' && Number.isInteger(receivedRegId)) {
1129
- const info = await signalRepository.getSessionInfo(participant);
1130
- if (info && info.registrationId !== 0 && info.registrationId !== receivedRegId) {
1131
- logger.info({ participant, stored: info.registrationId, received: receivedRegId }, 'reg id mismatch on retry without bundle, deleting session');
1132
- await authState.keys.set({ session: { [sessionId]: null } });
1133
- }
1134
- }
1135
- }
1136
- const BASE_KEY_CHECK_RETRY = 2;
1137
- if (msgId && messageRetryManager) {
1138
- const info = await signalRepository.getSessionInfo(participant);
1139
- if (info) {
1140
- if (retryCount === BASE_KEY_CHECK_RETRY) {
1141
- messageRetryManager.saveBaseKey(sessionId, msgId, info.baseKey);
1142
- }
1143
- else if (retryCount > BASE_KEY_CHECK_RETRY) {
1144
- if (messageRetryManager.hasSameBaseKey(sessionId, msgId, info.baseKey)) {
1145
- logger.warn({ participant, retryCount }, 'base key collision on retry, forcing fresh session');
1146
- await authState.keys.set({ session: { [sessionId]: null } });
1147
- }
1148
- messageRetryManager.deleteBaseKey(sessionId, msgId);
1149
- }
1150
- }
1151
- }
764
+ // Check if we should recreate session for this retry
1152
765
  let shouldRecreateSession = false;
1153
766
  let recreateReason = '';
1154
- if (enableAutoSessionRecreation && messageRetryManager && retryCount > 1 && !injectedFromBundle) {
767
+ if (enableAutoSessionRecreation && messageRetryManager && retryCount > 1) {
1155
768
  try {
769
+ const sessionId = signalRepository.jidToSignalProtocolAddress(participant);
1156
770
  const hasSession = await signalRepository.validateSession(participant);
1157
771
  const result = messageRetryManager.shouldRecreateSession(participant, hasSession.exists);
1158
772
  shouldRecreateSession = result.recreate;
@@ -1166,13 +780,11 @@ export const makeMessagesRecvSocket = (config) => {
1166
780
  logger.warn({ error, participant }, 'failed to check session recreation for outgoing retry');
1167
781
  }
1168
782
  }
1169
- if (!injectedFromBundle) {
1170
- await assertSessions([participant], true);
1171
- }
783
+ await assertSessions([participant], true);
1172
784
  if (isJidGroup(remoteJid)) {
1173
785
  await authState.keys.set({ 'sender-key-memory': { [remoteJid]: null } });
1174
786
  }
1175
- logger.debug({ participant, sendToAll, shouldRecreateSession, recreateReason, injectedFromBundle }, 'prepared session for retry resend');
787
+ logger.debug({ participant, sendToAll, shouldRecreateSession, recreateReason }, 'forced new session for retry recp');
1176
788
  for (const [i, msg] of msgs.entries()) {
1177
789
  if (!ids[i])
1178
790
  continue;
@@ -1207,6 +819,11 @@ export const makeMessagesRecvSocket = (config) => {
1207
819
  fromMe,
1208
820
  participant: attrs.participant
1209
821
  };
822
+ if (shouldIgnoreJid(remoteJid) && remoteJid !== S_WHATSAPP_NET) {
823
+ logger.debug({ remoteJid }, 'ignoring receipt from jid');
824
+ await sendMessageAck(node);
825
+ return;
826
+ }
1210
827
  const ids = [attrs.id];
1211
828
  if (Array.isArray(content)) {
1212
829
  const items = getBinaryNodeChildren(content[0], 'item');
@@ -1248,7 +865,7 @@ export const makeMessagesRecvSocket = (config) => {
1248
865
  try {
1249
866
  await updateSendMessageAgainCount(ids[0], key.participant);
1250
867
  logger.debug({ attrs, key }, 'recv retry request');
1251
- await sendMessagesAgain(key, ids, retryNode, node);
868
+ await sendMessagesAgain(key, ids, retryNode);
1252
869
  }
1253
870
  catch (error) {
1254
871
  logger.error({ key, ids, trace: error instanceof Error ? error.stack : 'Unknown error' }, 'error in sending message again');
@@ -1271,6 +888,11 @@ export const makeMessagesRecvSocket = (config) => {
1271
888
  };
1272
889
  const handleNotification = async (node) => {
1273
890
  const remoteJid = node.attrs.from;
891
+ if (shouldIgnoreJid(remoteJid) && remoteJid !== S_WHATSAPP_NET) {
892
+ logger.debug({ remoteJid, id: node.attrs.id }, 'ignored notification');
893
+ await sendMessageAck(node);
894
+ return;
895
+ }
1274
896
  try {
1275
897
  await Promise.all([
1276
898
  notificationMutex.mutex(async () => {
@@ -1283,7 +905,6 @@ export const makeMessagesRecvSocket = (config) => {
1283
905
  fromMe,
1284
906
  participant: node.attrs.participant,
1285
907
  participantAlt,
1286
- participantUsername: node.attrs.participant_username,
1287
908
  addressingMode,
1288
909
  id: node.attrs.id,
1289
910
  ...(msg.key || {})
@@ -1301,6 +922,11 @@ export const makeMessagesRecvSocket = (config) => {
1301
922
  }
1302
923
  };
1303
924
  const handleMessage = async (node) => {
925
+ if (shouldIgnoreJid(node.attrs.from) && node.attrs.from !== S_WHATSAPP_NET) {
926
+ logger.debug({ key: node.attrs.key }, 'ignored message');
927
+ await sendMessageAck(node, NACK_REASONS.UnhandledError);
928
+ return;
929
+ }
1304
930
  const encNode = getBinaryNodeChild(node, 'enc');
1305
931
  // TODO: temporary fix for crashes and issues resulting of failed msmsg decryption
1306
932
  if (encNode?.attrs.type === 'msmsg') {
@@ -1404,14 +1030,29 @@ export const makeMessagesRecvSocket = (config) => {
1404
1030
  return sendMessageAck(node);
1405
1031
  }
1406
1032
  }
1407
- logger.debug('[handleMessage] Attempting retry request for failed decryption');
1408
- // WAWeb only retry-receipts here; server emits PreKeyLow if prekeys run low.
1033
+ const errorMessage = msg?.messageStubParameters?.[0] || '';
1034
+ const isPreKeyError = errorMessage.includes('PreKey');
1035
+ logger.debug(`[handleMessage] Attempting retry request for failed decryption`);
1036
+ // Handle both pre-key and normal retries in single mutex
1409
1037
  await retryMutex.mutex(async () => {
1410
1038
  try {
1411
1039
  if (!ws.isOpen) {
1412
1040
  logger.debug({ node }, 'Connection closed, skipping retry');
1413
1041
  return;
1414
1042
  }
1043
+ // Handle pre-key errors with upload and delay
1044
+ if (isPreKeyError) {
1045
+ logger.info({ error: errorMessage }, 'PreKey error detected, uploading and retrying');
1046
+ try {
1047
+ logger.debug('Uploading pre-keys for error recovery');
1048
+ await uploadPreKeys(5);
1049
+ logger.debug('Waiting for server to process new pre-keys');
1050
+ await delay(1000);
1051
+ }
1052
+ catch (uploadErr) {
1053
+ logger.error({ uploadErr }, 'Pre-key upload failed, proceeding with retry anyway');
1054
+ }
1055
+ }
1415
1056
  const encNode = getBinaryNodeChild(node, 'enc');
1416
1057
  await sendRetryRequest(node, !encNode);
1417
1058
  if (retryRequestDelayMs) {
@@ -1419,7 +1060,15 @@ export const makeMessagesRecvSocket = (config) => {
1419
1060
  }
1420
1061
  }
1421
1062
  catch (err) {
1422
- logger.error({ err }, 'Failed to send retry');
1063
+ logger.error({ err, isPreKeyError }, 'Failed to handle retry, attempting basic retry');
1064
+ // Still attempt retry even if pre-key upload failed
1065
+ try {
1066
+ const encNode = getBinaryNodeChild(node, 'enc');
1067
+ await sendRetryRequest(node, !encNode);
1068
+ }
1069
+ catch (retryErr) {
1070
+ logger.error({ retryErr }, 'Failed to send retry after error handling');
1071
+ }
1423
1072
  }
1424
1073
  acked = true;
1425
1074
  await sendMessageAck(node, NACK_REASONS.UnhandledError);
@@ -1495,13 +1144,6 @@ export const makeMessagesRecvSocket = (config) => {
1495
1144
  offline: !!attrs.offline,
1496
1145
  status
1497
1146
  };
1498
- if (status === 'relaylatency') {
1499
- const latencyValue = infoChild.attrs.latency || infoChild.attrs['latency_ms'] || infoChild.attrs['latency-ms'];
1500
- const latencyMs = latencyValue ? Number(latencyValue) : undefined;
1501
- if (Number.isFinite(latencyMs)) {
1502
- call.latencyMs = latencyMs;
1503
- }
1504
- }
1505
1147
  if (status === 'offer') {
1506
1148
  call.isVideo = !!getBinaryNodeChild(infoChild, 'video');
1507
1149
  call.isGroup = infoChild.attrs.type === 'group' || !!infoChild.attrs['group-jid'];
@@ -1546,61 +1188,29 @@ export const makeMessagesRecvSocket = (config) => {
1546
1188
  // error in acknowledgement,
1547
1189
  // device could not display the message
1548
1190
  if (attrs.error) {
1549
- const isReachoutTimelocked = attrs.error === String(NACK_REASONS.SenderReachoutTimelocked);
1550
- if (attrs.error === SERVER_ERROR_CODES.MessageAccountRestriction) {
1551
- // 463 = 1:1 message missing privacy token (tctoken). Usually means the
1552
- // account is restricted: WhatsApp blocks starting new chats but preserves
1553
- // existing ones, since established chats already carry a tctoken.
1554
- // WA Web prevents this client-side (disables the compose bar).
1555
- // No retry — retrying counts as another "reach out" and worsens the restriction.
1556
- logger.warn({ msgId: attrs.id, from: attrs.from }, 'error 463: account restricted or missing tctoken for contact');
1557
- const ackFrom = attrs.from;
1558
- if (ackFrom && !inFlight463Recoveries.has(ackFrom)) {
1559
- inFlight463Recoveries.add(ackFrom);
1560
- void (async () => {
1561
- try {
1562
- const getPNForLID = signalRepository.lidMapping.getPNForLID.bind(signalRepository.lidMapping);
1563
- const tcStorageJid = await resolveTcTokenJid(ackFrom, getLIDForPN);
1564
- const issueJid = await resolveIssuanceJid(ackFrom, sock.serverProps.lidTrustedTokenIssueToLid, getLIDForPN, getPNForLID);
1565
- const result = await issuePrivacyTokens([issueJid], unixTimestampSeconds());
1566
- await storeTcTokensFromIqResult({
1567
- result,
1568
- fallbackJid: tcStorageJid,
1569
- keys: authState.keys,
1570
- getLIDForPN,
1571
- onNewJidStored: trackTcTokenJid
1572
- });
1573
- logger.debug({ from: ackFrom }, 'completed 463 token recovery issuance');
1574
- }
1575
- catch (err) {
1576
- logger.debug({ from: ackFrom, err: err?.message }, 'failed 463 token recovery issuance');
1577
- }
1578
- finally {
1579
- inFlight463Recoveries.delete(ackFrom);
1580
- }
1581
- })();
1582
- }
1583
- }
1584
- else if (attrs.error === SERVER_ERROR_CODES.SmaxInvalid) {
1585
- logger.warn({ msgId: attrs.id, from: attrs.from }, 'smax-invalid (479): stanza rejected by server — likely stale device session or malformed addressing');
1586
- }
1587
- else if (isReachoutTimelocked) {
1588
- // user is temporarily restricted, fetch current restriction details
1589
- await fetchAccountReachoutTimelock().catch(err => logger.warn({ err }, 'failed to fetch reachout timelock'));
1590
- logger.warn({ attrs }, 'received error in ack');
1591
- }
1592
- else {
1593
- logger.warn({ attrs }, 'received error in ack');
1594
- }
1191
+ logger.warn({ attrs }, 'received error in ack');
1595
1192
  ev.emit('messages.update', [
1596
1193
  {
1597
1194
  key,
1598
1195
  update: {
1599
1196
  status: WAMessageStatus.ERROR,
1600
- messageStubParameters: isReachoutTimelocked ? [attrs.error, ACCOUNT_RESTRICTED_TEXT] : [attrs.error]
1197
+ messageStubParameters: [attrs.error]
1601
1198
  }
1602
1199
  }
1603
1200
  ]);
1201
+ // resend the message with device_fanout=false, use at your own risk
1202
+ // if (attrs.error === '475') {
1203
+ // const msg = await getMessage(key)
1204
+ // if (msg) {
1205
+ // await relayMessage(key.remoteJid!, msg, {
1206
+ // messageId: key.id!,
1207
+ // useUserDevicesCache: false,
1208
+ // additionalAttributes: {
1209
+ // device_fanout: 'false'
1210
+ // }
1211
+ // })
1212
+ // }
1213
+ // }
1604
1214
  }
1605
1215
  };
1606
1216
  /// processes a node with the given function
@@ -1624,19 +1234,6 @@ export const makeMessagesRecvSocket = (config) => {
1624
1234
  yieldToEventLoop: () => new Promise(resolve => setImmediate(resolve))
1625
1235
  });
1626
1236
  const processNode = async (type, node, identifier, exec) => {
1627
- // Fast path: ack and drop ignored JIDs before entering the buffer/queue
1628
- const from = node.attrs.from;
1629
- let ignoreJid = from;
1630
- if (type === 'receipt' && from) {
1631
- const attrs = node.attrs;
1632
- const isLid = attrs.from.includes('lid');
1633
- const isNodeFromMe = areJidsSameUser(attrs.participant || attrs.from, isLid ? authState.creds.me?.lid : authState.creds.me?.id);
1634
- ignoreJid = !isNodeFromMe || isJidGroup(attrs.from) ? attrs.from : attrs.recipient;
1635
- }
1636
- if (ignoreJid && ignoreJid !== S_WHATSAPP_NET && shouldIgnoreJid(ignoreJid)) {
1637
- await sendMessageAck(node, type === 'message' ? NACK_REASONS.UnhandledError : undefined);
1638
- return;
1639
- }
1640
1237
  const isOffline = !!node.attrs.offline;
1641
1238
  if (isOffline) {
1642
1239
  offlineNodeProcessor.enqueue(type, node);
@@ -1692,110 +1289,12 @@ export const makeMessagesRecvSocket = (config) => {
1692
1289
  await upsertMessage(protoMsg, call.offline ? 'append' : 'notify');
1693
1290
  }
1694
1291
  });
1695
- /** timestamp of last tctoken prune run — throttles to once per 24h */
1696
- let lastTcTokenPruneTs = 0;
1697
- /** dedupe in-flight 463 recovery token issuance by target JID */
1698
- const inFlight463Recoveries = new Set();
1699
- ev.on('connection.update', ({ isOnline, connection }) => {
1292
+ ev.on('connection.update', ({ isOnline }) => {
1700
1293
  if (typeof isOnline !== 'undefined') {
1701
1294
  sendActiveReceipts = isOnline;
1702
1295
  logger.trace(`sendActiveReceipts set to "${sendActiveReceipts}"`);
1703
1296
  }
1704
- // Flush pending tctoken index save on disconnect to avoid writing after close
1705
- if (connection === 'close' && tcTokenIndexTimer) {
1706
- clearTimeout(tcTokenIndexTimer);
1707
- tcTokenIndexTimer = undefined;
1708
- // Best-effort flush — may fail if store is already closed
1709
- try {
1710
- void Promise.resolve(flushTcTokenIndex()).catch(() => { });
1711
- }
1712
- catch {
1713
- /* ignore sync errors */
1714
- }
1715
- }
1716
- // Prune expired tctokens when coming online, at most once per 24 hours
1717
- // Matches WA Web's CLEAN_TC_TOKENS task
1718
- // Note: don't gate on tcTokenKnownJids.size — the index may still be loading
1719
- if (isOnline) {
1720
- const now = Date.now();
1721
- const DAY_MS = 24 * 60 * 60 * 1000;
1722
- if (now - lastTcTokenPruneTs >= DAY_MS) {
1723
- lastTcTokenPruneTs = now;
1724
- void pruneExpiredTcTokens();
1725
- }
1726
- }
1727
1297
  });
1728
- registerSocketEndHandler(() => {
1729
- if (!config.msgRetryCounterCache && msgRetryCache.close) {
1730
- msgRetryCache.close();
1731
- }
1732
- if (!config.callOfferCache && callOfferCache.close) {
1733
- callOfferCache.close();
1734
- }
1735
- identityAssertDebounce.close();
1736
- sendActiveReceipts = false;
1737
- });
1738
- async function pruneExpiredTcTokens() {
1739
- try {
1740
- await tcTokenIndexLoaded;
1741
- // Union with the persisted index picks up JIDs added by other layers
1742
- // (history sync) without needing inter-module wiring.
1743
- const persisted = await readTcTokenIndex(authState.keys);
1744
- const allJids = new Set(tcTokenKnownJids);
1745
- for (const jid of persisted)
1746
- allJids.add(jid);
1747
- if (!allJids.size)
1748
- return;
1749
- const jids = [...allJids];
1750
- const allTokens = await authState.keys.get('tctoken', jids);
1751
- const writes = {};
1752
- const survivors = new Set();
1753
- let mutated = 0;
1754
- for (const jid of jids) {
1755
- const entry = allTokens[jid];
1756
- if (!entry) {
1757
- // Tracked but nothing in store — drop from index.
1758
- mutated++;
1759
- continue;
1760
- }
1761
- const hasPeerToken = !!entry.token?.length;
1762
- const peerTokenExpired = hasPeerToken && isTcTokenExpired(entry.timestamp);
1763
- const hasSenderTs = entry.senderTimestamp !== undefined;
1764
- const senderTsExpired = hasSenderTs && isTcTokenExpired(entry.senderTimestamp);
1765
- const keepPeerToken = hasPeerToken && !peerTokenExpired;
1766
- const keepSenderTs = hasSenderTs && !senderTsExpired;
1767
- if (!keepPeerToken && !keepSenderTs) {
1768
- writes[jid] = null;
1769
- mutated++;
1770
- }
1771
- else if (peerTokenExpired && keepSenderTs) {
1772
- writes[jid] = { token: Buffer.alloc(0), senderTimestamp: entry.senderTimestamp };
1773
- survivors.add(jid);
1774
- mutated++;
1775
- }
1776
- else {
1777
- survivors.add(jid);
1778
- }
1779
- }
1780
- if (mutated === 0)
1781
- return;
1782
- await authState.keys.set({
1783
- tctoken: {
1784
- ...writes,
1785
- [TC_TOKEN_INDEX_KEY]: {
1786
- token: Buffer.from(JSON.stringify([...survivors]))
1787
- }
1788
- }
1789
- });
1790
- tcTokenKnownJids.clear();
1791
- for (const jid of survivors)
1792
- tcTokenKnownJids.add(jid);
1793
- logger.debug({ mutated, remaining: survivors.size }, 'pruned expired tctokens');
1794
- }
1795
- catch (err) {
1796
- logger.warn({ err: err?.message }, 'failed to prune expired tctokens');
1797
- }
1798
- }
1799
1298
  return {
1800
1299
  ...sock,
1801
1300
  sendMessageAck,