zapo-js 1.6.2 → 1.7.1

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 (218) hide show
  1. package/README.md +26 -12
  2. package/dist/abprops-spec.d.ts +10 -0
  3. package/dist/abprops-spec.js +25 -0
  4. package/dist/appstate/crypto/WaAppStateCrypto.js +4 -6
  5. package/dist/auth/WaAuthClient.d.ts +14 -1
  6. package/dist/auth/WaAuthClient.js +22 -1
  7. package/dist/auth/credentials-flow.d.ts +3 -1
  8. package/dist/auth/credentials-flow.js +3 -0
  9. package/dist/auth/pairing/WaPairingFlow.d.ts +5 -0
  10. package/dist/auth/pairing/WaPairingFlow.js +66 -14
  11. package/dist/auth/types.d.ts +13 -0
  12. package/dist/client/WaClient.d.ts +11 -0
  13. package/dist/client/WaClient.js +60 -0
  14. package/dist/client/WaClientFactory.d.ts +2 -2
  15. package/dist/client/WaClientFactory.js +53 -13
  16. package/dist/client/coordinators/WaAbPropsCoordinator.d.ts +2 -2
  17. package/dist/client/coordinators/WaAbPropsCoordinator.js +16 -4
  18. package/dist/client/coordinators/WaIncomingNodeCoordinator.d.ts +2 -1
  19. package/dist/client/coordinators/WaIncomingNodeCoordinator.js +4 -0
  20. package/dist/client/coordinators/WaMessageCoordinator.d.ts +96 -0
  21. package/dist/client/coordinators/WaMessageCoordinator.js +163 -8
  22. package/dist/client/coordinators/WaMessageDispatchCoordinator.d.ts +64 -0
  23. package/dist/client/coordinators/WaMessageDispatchCoordinator.js +252 -57
  24. package/dist/client/coordinators/WaPrivacyCoordinator.d.ts +97 -19
  25. package/dist/client/coordinators/WaPrivacyCoordinator.js +152 -101
  26. package/dist/client/coordinators/WaRetryCoordinator.d.ts +22 -0
  27. package/dist/client/coordinators/WaRetryCoordinator.js +74 -11
  28. package/dist/client/events/dirty.d.ts +3 -0
  29. package/dist/client/events/dirty.js +12 -7
  30. package/dist/client/events/offline.d.ts +9 -0
  31. package/dist/client/events/offline.js +95 -0
  32. package/dist/client/events/privacy.d.ts +45 -0
  33. package/dist/client/events/privacy.js +130 -0
  34. package/dist/client/messaging/group-metadata.d.ts +12 -0
  35. package/dist/client/messaging/group-metadata.js +24 -8
  36. package/dist/client/messaging/link-preview.d.ts +7 -0
  37. package/dist/client/messaging/link-preview.js +27 -0
  38. package/dist/client/messaging/messages.js +16 -12
  39. package/dist/client/newsletter/content.d.ts +4 -1
  40. package/dist/client/newsletter/content.js +43 -18
  41. package/dist/client/newsletter/discovery.js +4 -2
  42. package/dist/client/newsletter/messaging.d.ts +2 -0
  43. package/dist/client/newsletter/messaging.js +2 -1
  44. package/dist/client/newsletter/mex.d.ts +2 -2
  45. package/dist/client/persistence/WriteBehindPersistence.js +2 -1
  46. package/dist/client/persistence/ephemeral-setting.d.ts +42 -0
  47. package/dist/client/persistence/ephemeral-setting.js +120 -0
  48. package/dist/client/persistence/group-history.d.ts +29 -0
  49. package/dist/client/persistence/group-history.js +149 -0
  50. package/dist/client/persistence/history-blob.d.ts +25 -0
  51. package/dist/client/persistence/history-blob.js +38 -0
  52. package/dist/client/persistence/history-sync.d.ts +20 -0
  53. package/dist/client/persistence/history-sync.js +165 -71
  54. package/dist/client/plugins/types.d.ts +10 -2
  55. package/dist/client/types.d.ts +150 -16
  56. package/dist/crypto/core/index.d.ts +1 -1
  57. package/dist/crypto/core/index.js +2 -1
  58. package/dist/crypto/core/primitives.d.ts +19 -0
  59. package/dist/crypto/core/primitives.js +48 -0
  60. package/dist/crypto/core/xeddsa.js +35 -4
  61. package/dist/crypto/curves/X25519.js +29 -2
  62. package/dist/crypto/nativeBackend.d.ts +20 -0
  63. package/dist/crypto/nativeBackend.js +95 -0
  64. package/dist/esm/abprops-spec.js +15 -0
  65. package/dist/esm/appstate/crypto/WaAppStateCrypto.js +4 -6
  66. package/dist/esm/auth/WaAuthClient.js +22 -1
  67. package/dist/esm/auth/credentials-flow.js +3 -0
  68. package/dist/esm/auth/pairing/WaPairingFlow.js +67 -15
  69. package/dist/esm/client/WaClient.js +60 -0
  70. package/dist/esm/client/WaClientFactory.js +54 -14
  71. package/dist/esm/client/coordinators/WaAbPropsCoordinator.js +17 -5
  72. package/dist/esm/client/coordinators/WaIncomingNodeCoordinator.js +4 -0
  73. package/dist/esm/client/coordinators/WaMessageCoordinator.js +166 -11
  74. package/dist/esm/client/coordinators/WaMessageDispatchCoordinator.js +254 -60
  75. package/dist/esm/client/coordinators/WaPrivacyCoordinator.js +145 -94
  76. package/dist/esm/client/coordinators/WaRetryCoordinator.js +74 -11
  77. package/dist/esm/client/events/dirty.js +12 -7
  78. package/dist/esm/client/events/offline.js +92 -0
  79. package/dist/esm/client/events/privacy.js +123 -0
  80. package/dist/esm/client/messaging/group-metadata.js +25 -9
  81. package/dist/esm/client/messaging/link-preview.js +29 -2
  82. package/dist/esm/client/messaging/messages.js +16 -12
  83. package/dist/esm/client/newsletter/content.js +44 -19
  84. package/dist/esm/client/newsletter/discovery.js +4 -2
  85. package/dist/esm/client/newsletter/messaging.js +2 -1
  86. package/dist/esm/client/persistence/WriteBehindPersistence.js +2 -1
  87. package/dist/esm/client/persistence/ephemeral-setting.js +116 -0
  88. package/dist/esm/client/persistence/group-history.js +145 -0
  89. package/dist/esm/client/persistence/history-blob.js +34 -0
  90. package/dist/esm/client/persistence/history-sync.js +157 -65
  91. package/dist/esm/crypto/core/index.js +1 -1
  92. package/dist/esm/crypto/core/primitives.js +47 -0
  93. package/dist/esm/crypto/core/xeddsa.js +36 -5
  94. package/dist/esm/crypto/curves/X25519.js +29 -2
  95. package/dist/esm/crypto/nativeBackend.js +93 -0
  96. package/dist/esm/index.js +3 -1
  97. package/dist/esm/media/crypto/WaMediaCrypto.js +38 -19
  98. package/dist/esm/message/context-info.js +39 -0
  99. package/dist/esm/message/crypto/reporting-token.js +38 -216
  100. package/dist/esm/message/encode/content.js +73 -12
  101. package/dist/esm/message/encode/media-payload.js +3 -1
  102. package/dist/esm/message/kinds/group-history.js +36 -0
  103. package/dist/esm/message/primitives/incoming.js +32 -13
  104. package/dist/esm/protocol/abprops.js +18 -162
  105. package/dist/esm/protocol/constants.js +4 -3
  106. package/dist/esm/protocol/defaults.js +7 -0
  107. package/dist/esm/protocol/device-identity.js +10 -5
  108. package/dist/esm/protocol/jid.js +83 -36
  109. package/dist/esm/protocol/media.js +1 -0
  110. package/dist/esm/protocol/message.js +8 -0
  111. package/dist/esm/protocol/notification.js +1 -0
  112. package/dist/esm/protocol/privacy.js +70 -4
  113. package/dist/esm/signal/group/SenderKeyChain.js +1 -2
  114. package/dist/esm/signal/group/SenderKeyManager.js +1 -7
  115. package/dist/esm/signal/session/SignalProtocol.js +22 -21
  116. package/dist/esm/signal/session/SignalRatchet.js +20 -17
  117. package/dist/esm/signal/session/encoding.js +25 -3
  118. package/dist/esm/signal/session/resolver.js +11 -5
  119. package/dist/esm/store/contracts/chat-metadata.store.js +1 -0
  120. package/dist/esm/store/createStore.js +19 -2
  121. package/dist/esm/store/index.js +1 -0
  122. package/dist/esm/store/memory/chat-metadata.store.js +66 -0
  123. package/dist/esm/store/noop.store.js +8 -0
  124. package/dist/esm/transport/binary/constants.js +19 -0
  125. package/dist/esm/transport/binary/decoder.js +17 -19
  126. package/dist/esm/transport/binary/encoder.js +61 -18
  127. package/dist/esm/transport/node/builders/global.js +20 -0
  128. package/dist/esm/transport/node/builders/message.js +32 -15
  129. package/dist/esm/transport/node/builders/privacy.js +63 -4
  130. package/dist/esm/transport/noise/WaClientPayload.js +43 -22
  131. package/dist/esm/transport/noise/WaFrameCodec.js +14 -4
  132. package/dist/esm/util/coercion.js +12 -4
  133. package/dist/esm/util/index.js +1 -1
  134. package/dist/esm/util/protoscan.js +181 -0
  135. package/dist/esm/util/runtime.js +34 -0
  136. package/dist/index.d.ts +11 -6
  137. package/dist/index.js +15 -2
  138. package/dist/media/crypto/WaMediaCrypto.js +36 -17
  139. package/dist/media/types.d.ts +1 -1
  140. package/dist/message/context-info.d.ts +10 -0
  141. package/dist/message/context-info.js +40 -0
  142. package/dist/message/crypto/reporting-token.d.ts +3 -1
  143. package/dist/message/crypto/reporting-token.js +38 -216
  144. package/dist/message/encode/content.d.ts +24 -4
  145. package/dist/message/encode/content.js +74 -12
  146. package/dist/message/encode/media-payload.d.ts +5 -2
  147. package/dist/message/encode/media-payload.js +3 -1
  148. package/dist/message/kinds/group-history.d.ts +30 -0
  149. package/dist/message/kinds/group-history.js +40 -0
  150. package/dist/message/primitives/incoming.d.ts +5 -0
  151. package/dist/message/primitives/incoming.js +32 -13
  152. package/dist/message/types.d.ts +10 -0
  153. package/dist/protocol/abprops.d.ts +27 -140
  154. package/dist/protocol/abprops.js +18 -163
  155. package/dist/protocol/constants.d.ts +4 -3
  156. package/dist/protocol/constants.js +10 -3
  157. package/dist/protocol/defaults.d.ts +7 -0
  158. package/dist/protocol/defaults.js +7 -0
  159. package/dist/protocol/device-identity.d.ts +14 -4
  160. package/dist/protocol/device-identity.js +9 -4
  161. package/dist/protocol/jid.d.ts +4 -1
  162. package/dist/protocol/jid.js +83 -36
  163. package/dist/protocol/media.d.ts +1 -0
  164. package/dist/protocol/media.js +1 -0
  165. package/dist/protocol/message.d.ts +6 -0
  166. package/dist/protocol/message.js +9 -0
  167. package/dist/protocol/notification.d.ts +1 -0
  168. package/dist/protocol/notification.js +1 -0
  169. package/dist/protocol/privacy.d.ts +52 -0
  170. package/dist/protocol/privacy.js +71 -5
  171. package/dist/signal/group/SenderKeyChain.d.ts +1 -1
  172. package/dist/signal/group/SenderKeyChain.js +1 -2
  173. package/dist/signal/group/SenderKeyManager.js +1 -7
  174. package/dist/signal/session/SignalProtocol.d.ts +13 -0
  175. package/dist/signal/session/SignalProtocol.js +22 -21
  176. package/dist/signal/session/SignalRatchet.d.ts +4 -4
  177. package/dist/signal/session/SignalRatchet.js +19 -16
  178. package/dist/signal/session/SignalSession.d.ts +8 -1
  179. package/dist/signal/session/encoding.d.ts +5 -0
  180. package/dist/signal/session/encoding.js +25 -3
  181. package/dist/signal/session/resolver.js +11 -5
  182. package/dist/store/contracts/chat-metadata.store.d.ts +34 -0
  183. package/dist/store/contracts/chat-metadata.store.js +2 -0
  184. package/dist/store/contracts/group-metadata.store.d.ts +7 -0
  185. package/dist/store/contracts/thread.store.d.ts +6 -0
  186. package/dist/store/createStore.d.ts +10 -2
  187. package/dist/store/createStore.js +18 -1
  188. package/dist/store/index.d.ts +3 -1
  189. package/dist/store/index.js +3 -1
  190. package/dist/store/memory/chat-metadata.store.d.ts +26 -0
  191. package/dist/store/memory/chat-metadata.store.js +70 -0
  192. package/dist/store/noop.store.d.ts +2 -0
  193. package/dist/store/noop.store.js +9 -1
  194. package/dist/store/types.d.ts +190 -39
  195. package/dist/transport/binary/constants.d.ts +8 -0
  196. package/dist/transport/binary/constants.js +20 -1
  197. package/dist/transport/binary/decoder.js +17 -19
  198. package/dist/transport/binary/encoder.js +60 -17
  199. package/dist/transport/node/builders/global.d.ts +8 -0
  200. package/dist/transport/node/builders/global.js +21 -0
  201. package/dist/transport/node/builders/message.js +32 -15
  202. package/dist/transport/node/builders/privacy.d.ts +26 -2
  203. package/dist/transport/node/builders/privacy.js +63 -3
  204. package/dist/transport/noise/WaClientPayload.js +43 -22
  205. package/dist/transport/noise/WaFrameCodec.d.ts +1 -0
  206. package/dist/transport/noise/WaFrameCodec.js +14 -4
  207. package/dist/transport/noise/types.d.ts +17 -0
  208. package/dist/util/coercion.d.ts +6 -0
  209. package/dist/util/coercion.js +13 -4
  210. package/dist/util/index.d.ts +1 -1
  211. package/dist/util/index.js +2 -1
  212. package/dist/util/protoscan.d.ts +33 -0
  213. package/dist/util/protoscan.js +186 -0
  214. package/dist/util/runtime.d.ts +12 -0
  215. package/dist/util/runtime.js +35 -0
  216. package/package.json +3 -1
  217. package/spec/abprops/index.d.ts +4402 -0
  218. package/spec/abprops/index.js +4372 -0
@@ -100,6 +100,7 @@ function resolveWaClientBase(options, logger) {
100
100
  sessionId,
101
101
  deviceBrowser: device.browser,
102
102
  deviceOsDisplayName: device.osDisplayName,
103
+ deviceOsVersion: device.osVersion ?? undefined,
103
104
  devicePlatform: device.platform,
104
105
  urls: options.urls ?? options.chatSocketUrls ?? constants_1.WA_DEFAULTS.CHAT_SOCKET_URLS,
105
106
  iqTimeoutMs: options.iqTimeoutMs ?? constants_1.WA_DEFAULTS.IQ_TIMEOUT_MS,
@@ -124,7 +125,7 @@ function createIncomingNodeRuntime(input) {
124
125
  const { logger, emitEvent, authClient, connectionManager, nodeOrchestrator, streamControl, mediaMessageBuildOptions, retryCoordinator, messageDispatch, sendNode, syncAppState, disconnect, clearStoredCredentials, getCurrentCredentials, handleClientDirtyBits, incomingMessageAckOptions } = input;
125
126
  return {
126
127
  handleStreamControlResult: streamControl.handleStreamControlResult,
127
- persistSuccessAttributes: (attributes) => authClient.persistSuccessAttributes(attributes),
128
+ persistSuccessAttributes: (attributes) => authClient.persistSuccessAttributes(attributes, { countsAsLogin: true }),
128
129
  emitSuccessNode: (node) => emitEvent('debug_connection_success', { node }),
129
130
  updateClockSkewFromSuccess: (serverUnixSeconds) => connectionManager.updateClockSkewFromSuccess(serverUnixSeconds),
130
131
  shouldWarmupMediaConn: () => !!(getCurrentCredentials()?.meJid && connectionManager.isConnected()),
@@ -149,6 +150,7 @@ function createIncomingNodeRuntime(input) {
149
150
  emitIncomingFailure: (event) => emitEvent('stream_failure', event),
150
151
  emitIncomingErrorStanza: (event) => emitEvent('stanza_error', event),
151
152
  emitIncomingNotification: (event) => emitEvent('debug_notification', event),
153
+ emitOfflineThreadMetadata: (event) => emitEvent('offline_thread_metadata', event),
152
154
  emitMexNotification: (event) => emitEvent('mex_notification', event),
153
155
  emitRegistrationCode: (event) => emitEvent('mobile_registration_code', event),
154
156
  emitAccountTakeoverNotice: (event) => emitEvent('mobile_account_takeover_notice', event),
@@ -244,6 +246,15 @@ function buildWaClientDependencies(input) {
244
246
  allowPrivateHosts: linkPreviewOptions.allowPrivateHosts,
245
247
  proxy: linkPreviewOptions.proxy ?? options.proxy?.linkPreview
246
248
  });
249
+ const createLinkPreviewResolver = (surface) => (content) => (0, link_preview_1.resolveLinkPreview)(content.text, content.linkPreview, {
250
+ logger,
251
+ mediaTransfer,
252
+ getMediaConn: () => (0, messages_1.getMediaConn)(mediaMessageBuildOptions),
253
+ fetcher: linkPreviewFetcher,
254
+ options: linkPreviewOptions,
255
+ serverClock,
256
+ surface
257
+ });
247
258
  const mediaMessageBuildOptions = {
248
259
  logger,
249
260
  mediaTransfer,
@@ -261,14 +272,7 @@ function buildWaClientDependencies(input) {
261
272
  },
262
273
  serverClock,
263
274
  media: options.media,
264
- linkPreviewResolver: (content) => (0, link_preview_1.resolveLinkPreview)(content.text, content.linkPreview, {
265
- logger,
266
- mediaTransfer,
267
- getMediaConn: () => (0, messages_1.getMediaConn)(mediaMessageBuildOptions),
268
- fetcher: linkPreviewFetcher,
269
- options: linkPreviewOptions,
270
- serverClock
271
- })
275
+ linkPreviewResolver: createLinkPreviewResolver()
272
276
  };
273
277
  const messageClient = new WaMessageClient_1.WaMessageClient({
274
278
  logger,
@@ -329,6 +333,7 @@ function buildWaClientDependencies(input) {
329
333
  const authClient = new WaAuthClient_1.WaAuthClient({
330
334
  deviceBrowser: options.deviceBrowser,
331
335
  deviceOsDisplayName: options.deviceOsDisplayName,
336
+ deviceOsVersion: options.deviceOsVersion,
332
337
  devicePlatform: options.devicePlatform,
333
338
  requireFullSync: options.requireFullSync,
334
339
  version: options.version,
@@ -371,12 +376,16 @@ function buildWaClientDependencies(input) {
371
376
  generateStanzaId: () => messageDispatch.generateOutgoingMessageId(),
372
377
  mediaTransfer,
373
378
  getMediaConn: () => (0, messages_1.getMediaConn)(mediaMessageBuildOptions),
379
+ linkPreviewResolver: createLinkPreviewResolver('newsletter'),
374
380
  getAbPropString: (name) => abPropsCoordinator.getConfigValue(name),
375
381
  logger
376
382
  });
377
383
  const privacyCoordinator = (0, WaPrivacyCoordinator_1.createPrivacyCoordinator)({
384
+ logger,
378
385
  queryWithContext: runtime.queryWithContext,
379
- resolveUserJidPair: (userJid) => signalDeviceSync.resolveUserJidPair(userJid)
386
+ resolveUserJidPair: (userJid) => signalDeviceSync.resolveUserJidPair(userJid),
387
+ getSelfLid: () => getCurrentCredentials()?.meLid ?? null,
388
+ emitPrivacy: (event) => runtime.emitEvent('privacy', event)
380
389
  });
381
390
  const businessCoordinator = (0, WaBusinessCoordinator_1.createBusinessCoordinator)({
382
391
  queryWithContext: runtime.queryWithContext,
@@ -415,7 +424,8 @@ function buildWaClientDependencies(input) {
415
424
  }
416
425
  return {
417
426
  participants: participantJids,
418
- ephemeral: metadata.ephemeral
427
+ ephemeral: metadata.ephemeral,
428
+ ephemeralTrigger: metadata.ephemeralTrigger
419
429
  };
420
430
  },
421
431
  logger
@@ -470,6 +480,8 @@ function buildWaClientDependencies(input) {
470
480
  sessionStore: sessionStore.session,
471
481
  identityStore: sessionStore.identity,
472
482
  deviceListStore: sessionStore.deviceList,
483
+ threadStore: sessionStore.threads,
484
+ chatMetadataStore: sessionStore.chatMetadata,
473
485
  signalDeviceSync,
474
486
  messageSecretStore: sessionStore.messageSecret,
475
487
  persistAllMessageSecrets: options.addons?.persistAllSecrets === true,
@@ -512,7 +524,9 @@ function buildWaClientDependencies(input) {
512
524
  trustedContactToken,
513
525
  emitAddon: (event) => runtime.emitEvent('message_addon', event),
514
526
  mexSocket: { query: runtime.query },
515
- peerDataOperation
527
+ peerDataOperation,
528
+ isGroupHistorySendEnabled: () => abPropsCoordinator.getConfigValue('group_history_send'),
529
+ getAbPropNumber: (name) => abPropsCoordinator.getConfigValue(name)
516
530
  });
517
531
  const retryCoordinator = new WaRetryCoordinator_1.WaRetryCoordinator({
518
532
  logger,
@@ -537,7 +551,8 @@ function buildWaClientDependencies(input) {
537
551
  .handleIncomingMessageEvent(event)
538
552
  .catch((err) => runtime.handleError((0, primitives_1.toError)(err)));
539
553
  },
540
- isMobilePrimary
554
+ isMobilePrimary,
555
+ getAbPropNumber: (name) => abPropsCoordinator.getConfigValue(name)
541
556
  });
542
557
  const botCoordinator = (0, WaBotCoordinator_1.createBotCoordinator)({
543
558
  logger,
@@ -649,6 +664,7 @@ function buildWaClientDependencies(input) {
649
664
  const disconnectWithClientSideEffects = async (reason, isLogout, code) => {
650
665
  abPropsCoordinator.reset();
651
666
  offlineResume.reset();
667
+ privacyCoordinator.stopAccountSyncRefresh();
652
668
  await connectionManager?.disconnect();
653
669
  runtime.emitEvent('connection', {
654
670
  status: 'close',
@@ -695,6 +711,7 @@ function buildWaClientDependencies(input) {
695
711
  signalProtocol,
696
712
  senderKeyManager,
697
713
  onDecryptFailure: (context, error) => retryCoordinator.onDecryptFailure(context, error),
714
+ requestPlaceholderResend: (context) => retryCoordinator.onUnavailableMessage(context),
698
715
  emitIncomingMessage: (event) => {
699
716
  void runtime
700
717
  .handleIncomingMessageEvent(event)
@@ -710,6 +727,10 @@ function buildWaClientDependencies(input) {
710
727
  getCurrentCredentials,
711
728
  syncAppState: runtime.syncAppState,
712
729
  generateUsyncSid,
730
+ syncAccountPrivacy: async () => {
731
+ await privacyCoordinator.refreshFromAccountSync();
732
+ },
733
+ emitBlocklist: (blocklist) => runtime.emitEvent('blocklist', blocklist),
713
734
  newsletterListSubscribed: () => newsletterCoordinator.listSubscribed()
714
735
  }, dirtyBits);
715
736
  const offlineResume = new WaOfflineResumeCoordinator_1.WaOfflineResumeCoordinator({
@@ -891,6 +912,25 @@ function buildWaClientDependencies(input) {
891
912
  return false;
892
913
  }
893
914
  });
915
+ incomingNode.registerIncomingHandler({
916
+ tag: constants_1.WA_NODE_TAGS.NOTIFICATION,
917
+ subtype: constants_1.WA_NOTIFICATION_TYPES.ACCOUNT_SYNC,
918
+ prepend: true,
919
+ /**
920
+ * The stanza carries the changed `<category>` inline, but it is a
921
+ * trigger only: the payload is a partial view, so the whole set is
922
+ * refetched instead. Observer only - returning false keeps the
923
+ * generic notification handler responsible for the ack and the
924
+ * `debug_notification` emit, leaving wire behavior unchanged.
925
+ */
926
+ // eslint-disable-next-line @typescript-eslint/require-await
927
+ handler: async (node) => {
928
+ if ((0, helpers_1.findNodeChild)(node, constants_1.WA_NODE_TAGS.PRIVACY)) {
929
+ privacyCoordinator.scheduleAccountSyncRefresh();
930
+ }
931
+ return false;
932
+ }
933
+ });
894
934
  incomingNode.registerIncomingHandler({
895
935
  tag: constants_1.WA_NODE_TAGS.NOTIFICATION,
896
936
  subtype: constants_1.WA_NOTIFICATION_TYPES.DEVICES,
@@ -1,5 +1,5 @@
1
+ import { type WaAbPropName, type WaAbPropValue } from '../../abprops-spec';
1
2
  import type { Logger } from '../../infra/log/types';
2
- import { type AbPropName, type AbPropValue } from '../../protocol/abprops';
3
3
  import type { BinaryNode } from '../../transport/types';
4
4
  type WaAbPropsRuntime = {
5
5
  readonly queryWithContext: (context: string, node: BinaryNode, timeoutMs?: number, contextData?: Readonly<Record<string, unknown>>) => Promise<BinaryNode>;
@@ -16,7 +16,7 @@ export declare class WaAbPropsCoordinator {
16
16
  readonly logger: Logger;
17
17
  readonly runtime: WaAbPropsRuntime;
18
18
  });
19
- getConfigValue<T extends AbPropValue>(name: AbPropName): T;
19
+ getConfigValue<T extends WaAbPropValue>(name: WaAbPropName): T;
20
20
  sync(): void;
21
21
  reset(): void;
22
22
  private startSync;
@@ -1,11 +1,16 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.WaAbPropsCoordinator = void 0;
4
+ const _abprops_spec_1 = require("../../abprops-spec");
4
5
  const abprops_1 = require("../events/abprops");
5
6
  const abprops_2 = require("../../protocol/abprops");
6
7
  const constants_1 = require("../../protocol/constants");
7
8
  const abprops_3 = require("../../transport/node/builders/abprops");
8
9
  const primitives_1 = require("../../util/primitives");
10
+ // The generated table types every entry with literal `code` / `defaultValue`
11
+ // types. Reading through a widened view keeps a lookup by a non-literal name
12
+ // from expanding into a 2000-member union at every call site.
13
+ const AB_PROPS = _abprops_spec_1.WA_ABPROPS;
9
14
  class WaAbPropsCoordinator {
10
15
  constructor(options) {
11
16
  this.logger = options.logger;
@@ -21,7 +26,7 @@ class WaAbPropsCoordinator {
21
26
  if (cached !== undefined) {
22
27
  return cached;
23
28
  }
24
- return abprops_2.AB_PROP_CONFIGS[name].defaultValue;
29
+ return AB_PROPS[name].defaultValue;
25
30
  }
26
31
  sync() {
27
32
  if (this.syncPromise) {
@@ -104,11 +109,11 @@ class WaAbPropsCoordinator {
104
109
  let applied = 0;
105
110
  for (let i = 0; i < result.props.length; i += 1) {
106
111
  const entry = result.props[i];
107
- const name = (0, abprops_2.resolveAbPropNameByCode)(entry.configCode);
112
+ const name = (0, _abprops_spec_1.resolveAbPropNameByCode)(entry.configCode);
108
113
  if (!name) {
109
114
  continue;
110
115
  }
111
- const config = abprops_2.AB_PROP_CONFIGS[name];
116
+ const config = AB_PROPS[name];
112
117
  const parsed = parseConfigValue(entry.configValue, config.type, config.defaultValue);
113
118
  this.cache.set(name, parsed);
114
119
  applied += 1;
@@ -123,6 +128,8 @@ class WaAbPropsCoordinator {
123
128
  }
124
129
  }
125
130
  exports.WaAbPropsCoordinator = WaAbPropsCoordinator;
131
+ // parseInt, not a digits-only parse: negative values are in band, several props
132
+ // ship -1 as their default. Divergence: WA Web caches NaN, we keep the default.
126
133
  function parseConfigValue(value, type, defaultValue) {
127
134
  if (value === null) {
128
135
  return defaultValue;
@@ -131,7 +138,12 @@ function parseConfigValue(value, type, defaultValue) {
131
138
  return value === '1' || value === 'true' || value === 'True';
132
139
  }
133
140
  if (type === 'int') {
134
- return (0, primitives_1.parseOptionalInt)(value) ?? defaultValue;
141
+ const parsed = Number.parseInt(value, 10);
142
+ return Number.isNaN(parsed) ? defaultValue : parsed;
143
+ }
144
+ if (type === 'float') {
145
+ const parsed = Number.parseFloat(value);
146
+ return Number.isNaN(parsed) ? defaultValue : parsed;
135
147
  }
136
148
  return value;
137
149
  }
@@ -1,7 +1,7 @@
1
1
  import type { WaAuthCredentials, WaSuccessPersistAttributes } from '../../auth/types';
2
2
  import type { WaOfflineResumeCoordinator } from '../coordinators/WaOfflineResumeCoordinator';
3
3
  import type { WaDirtyBit } from '../events/dirty';
4
- import type { WaAccountTakeoverNoticeEvent, WaBusinessEvent, WaGroupEvent, WaIncomingCallEvent, WaIncomingChatstateEvent, WaIncomingErrorStanzaEvent, WaIncomingFailureEvent, WaIncomingNodeHandlerRegistration, WaIncomingNotificationEvent, WaIncomingPresenceEvent, WaIncomingReceiptEvent, WaIncomingStanzaFilter, WaIncomingUnhandledStanzaEvent, WaMexNotificationEvent, WaPictureEvent, WaRegistrationCodeEvent } from '../types';
4
+ import type { WaAccountTakeoverNoticeEvent, WaBusinessEvent, WaGroupEvent, WaIncomingCallEvent, WaIncomingChatstateEvent, WaIncomingErrorStanzaEvent, WaIncomingFailureEvent, WaIncomingNodeHandlerRegistration, WaIncomingNotificationEvent, WaIncomingPresenceEvent, WaIncomingReceiptEvent, WaIncomingStanzaFilter, WaIncomingUnhandledStanzaEvent, WaMexNotificationEvent, WaOfflineThreadMetadataEvent, WaPictureEvent, WaRegistrationCodeEvent } from '../types';
5
5
  import type { Logger } from '../../infra/log/types';
6
6
  import type { WaConnectionCode, WaDisconnectReason } from '../../protocol/stream';
7
7
  import { type WaStreamControlNodeResult } from '../../transport/stream/parse';
@@ -31,6 +31,7 @@ interface WaIncomingNodeRuntime {
31
31
  readonly emitIncomingFailure: (event: WaIncomingFailureEvent) => void;
32
32
  readonly emitIncomingErrorStanza: (event: WaIncomingErrorStanzaEvent) => void;
33
33
  readonly emitIncomingNotification: (event: WaIncomingNotificationEvent) => void;
34
+ readonly emitOfflineThreadMetadata: (event: WaOfflineThreadMetadataEvent) => void;
34
35
  readonly emitMexNotification: (event: WaMexNotificationEvent) => void;
35
36
  readonly emitRegistrationCode: (event: WaRegistrationCodeEvent) => void;
36
37
  readonly emitAccountTakeoverNotice: (event: WaAccountTakeoverNoticeEvent) => void;
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.WaIncomingNodeCoordinator = void 0;
4
4
  const chatstate_1 = require("../events/chatstate");
5
5
  const incoming_1 = require("../events/incoming");
6
+ const offline_1 = require("../events/offline");
6
7
  const presence_1 = require("../events/presence");
7
8
  const constants_1 = require("../../protocol/constants");
8
9
  const helpers_1 = require("../../transport/node/helpers");
@@ -381,6 +382,9 @@ class WaIncomingNodeCoordinator {
381
382
  if (child.tag === 'offline') {
382
383
  this.offlineResume.handleOfflineComplete((0, primitives_1.parseOptionalInt)(child.attrs.count) ?? 0);
383
384
  }
385
+ if (child.tag === 'thread_metadata') {
386
+ this.runtime.emitOfflineThreadMetadata((0, offline_1.parseOfflineThreadMetadata)(child));
387
+ }
384
388
  handled = true;
385
389
  }
386
390
  }
@@ -1,4 +1,5 @@
1
1
  import type { Readable } from 'node:stream';
2
+ import type { WaAbPropName } from '../../abprops-spec';
2
3
  import type { WaMessageDispatchCoordinator } from '../coordinators/WaMessageDispatchCoordinator';
3
4
  import type { WaTrustedContactTokenCoordinator } from '../coordinators/WaTrustedContactTokenCoordinator';
4
5
  import { type WaUploadMediaSource } from '../media';
@@ -25,6 +26,65 @@ export interface WaMessageCoordinatorDeps {
25
26
  readonly emitAddon: (event: WaIncomingAddonEvent) => void;
26
27
  readonly mexSocket: WaMexQuerySocket;
27
28
  readonly peerDataOperation: PeerDataOperationRequester;
29
+ /**
30
+ * Server-synced `group_history_send` AB-prop. WhatsApp gates the sender
31
+ * side per account, and rejects the stanza with SMAX_INVALID when it is
32
+ * off, so {@link WaMessageCoordinator.shareGroupHistory} checks it before
33
+ * spending an upload.
34
+ */
35
+ readonly isGroupHistorySendEnabled: () => boolean;
36
+ /**
37
+ * Reads a server-synced numeric AB-prop, falling back to its shipped
38
+ * default. Sole source for the group-history message-count limit, so a
39
+ * server-side change takes effect without a release.
40
+ */
41
+ readonly getAbPropNumber: (name: WaAbPropName) => number;
42
+ }
43
+ export interface WaShareGroupHistoryInput {
44
+ /**
45
+ * Members to share the history with. Must be current members of the group,
46
+ * written in the group's own addressing mode - a LID-addressed group only
47
+ * matches `@lid` entries, and a PN one only matches `@s.whatsapp.net`.
48
+ * Anything else throws, naming the mode the group uses. Read the mode off
49
+ * `client.group.queryGroupMetadata()`, whose participants carry both forms.
50
+ */
51
+ readonly toJids: readonly string[];
52
+ /**
53
+ * How many of the most recent messages to read from the mailbox store.
54
+ * Defaults to WhatsApp's `group_history_message_count_limit` (100).
55
+ * Ignored when {@link messages} is supplied - that list is bundled as-is.
56
+ */
57
+ readonly count?: number;
58
+ /**
59
+ * Only read messages at or after this timestamp (ms) from the mailbox
60
+ * store. Ignored when {@link messages} is supplied.
61
+ */
62
+ readonly sinceMs?: number;
63
+ /**
64
+ * Messages to bundle, bypassing the mailbox store entirely - {@link count}
65
+ * and {@link sinceMs} do not apply, so the caller owns the windowing.
66
+ * Required when the `messages` store domain is `'none'` (the default),
67
+ * since there is nothing to read back in that case.
68
+ */
69
+ readonly messages?: readonly Proto.IWebMessageInfo[];
70
+ /**
71
+ * Pinned messages older than the shared window. The receiver injects these
72
+ * regardless of the age cutoff it applies to `messages`.
73
+ */
74
+ readonly outOfWindowPinnedMessages?: readonly Proto.IWebMessageInfo[];
75
+ }
76
+ export interface WaShareGroupHistoryResult {
77
+ /** Stanza id of the bundle message, fanned out only to `historyReceivers`. */
78
+ readonly bundleMessageId: string;
79
+ /**
80
+ * Stanza id of the notice message, sent to the whole group. Absent when the
81
+ * notice failed to send: the bundle still reached its receivers, so do not
82
+ * retry the share - it would upload and deliver the history a second time.
83
+ */
84
+ readonly noticeMessageId?: string;
85
+ readonly messagesCount: number;
86
+ readonly historyReceivers: readonly string[];
87
+ readonly nonHistoryReceivers: readonly string[];
28
88
  }
29
89
  export interface WaRequestHistorySyncInput {
30
90
  /** Chat the older messages should be fetched from. */
@@ -111,6 +171,8 @@ export declare class WaMessageCoordinator {
111
171
  private readonly emitAddon;
112
172
  private readonly mexSocket;
113
173
  private readonly peerDataOperation;
174
+ private readonly isGroupHistorySendEnabled;
175
+ private readonly getAbPropNumber;
114
176
  constructor(deps: WaMessageCoordinatorDeps);
115
177
  /**
116
178
  * Asks the server to backfill older messages for `chatJid` beyond what
@@ -128,6 +190,40 @@ export declare class WaMessageCoordinator {
128
190
  requestHistorySync(input: WaRequestHistorySyncInput): Promise<{
129
191
  readonly messageId: string;
130
192
  }>;
193
+ /**
194
+ * Shares recent group history with members who joined after the fact - the
195
+ * counterpart of the `group_history_bundle` event on the receiving side.
196
+ *
197
+ * Sends two messages: the bundle itself, encrypted per device and fanned
198
+ * out **only** to `toJids` (never to the whole group), followed by a hidden
199
+ * notice addressed to everyone so other clients can render the "history was
200
+ * shared" marker.
201
+ *
202
+ * WhatsApp gates the sender side per account through the `group_history_send`
203
+ * AB-prop and rejects the stanza with SMAX_INVALID when it is off, so this
204
+ * throws up front rather than spending an upload. Admin-only groups
205
+ * (`memberShareGroupHistoryMode: 'admin_share'`) reject a share from a
206
+ * regular member server-side - read `client.group.queryGroupMetadata()`
207
+ * first if you want to check that too.
208
+ *
209
+ * @throws when the account is not allowed to share group history, when
210
+ * `groupJid` is not a group, when any of `toJids` is not a current member
211
+ * (or is this account), or when there is nothing to bundle.
212
+ * @example
213
+ * ```ts
214
+ * await client.message.shareGroupHistory('12036@g.us', {
215
+ * toJids: ['5511999999999@s.whatsapp.net'],
216
+ * count: 50
217
+ * })
218
+ * ```
219
+ */
220
+ shareGroupHistory(groupJid: string, input: WaShareGroupHistoryInput): Promise<WaShareGroupHistoryResult>;
221
+ /**
222
+ * Reads the most recent messages of a group back out of the mailbox store
223
+ * and rebuilds the `WebMessageInfo` shape a bundle carries. Yields nothing
224
+ * when the `messages` store domain is `'none'`.
225
+ */
226
+ private loadGroupHistoryMessages;
131
227
  /**
132
228
  * Fetches the server-side "reachout" timelock that throttles cold outreach
133
229
  * to non-contacts, returning the active window when enforcement is on.
@@ -9,12 +9,26 @@ const messages_1 = require("../messaging/messages");
9
9
  const constants_1 = require("../../media/constants");
10
10
  const addon_crypto_1 = require("../../message/crypto/addon-crypto");
11
11
  const content_1 = require("../../message/encode/content");
12
+ const group_history_1 = require("../../message/kinds/group-history");
12
13
  const _proto_1 = require("../../proto");
13
14
  const jid_1 = require("../../protocol/jid");
14
15
  const client_1 = require("../../transport/node/mex/client");
15
16
  const bytes_1 = require("../../util/bytes");
16
17
  const coercion_1 = require("../../util/coercion");
17
18
  const primitives_1 = require("../../util/primitives");
19
+ /** MIME type the group-history bundle is uploaded and advertised under. */
20
+ const GROUP_HISTORY_BUNDLE_MIMETYPE = 'application/protobuf';
21
+ /** Oldest `messageTimestamp` across `messages`, in seconds; `undefined` when none carries one. */
22
+ function oldestTimestampSeconds(messages) {
23
+ let oldest;
24
+ for (let index = 0; index < messages.length; index += 1) {
25
+ const timestamp = (0, primitives_1.longToNumber)(messages[index].messageTimestamp);
26
+ if (timestamp > 0 && (oldest === undefined || timestamp < oldest)) {
27
+ oldest = timestamp;
28
+ }
29
+ }
30
+ return oldest;
31
+ }
18
32
  function parseReachoutTimelockMexResponse(data) {
19
33
  const root = data?.xwa2_fetch_account_reachout_timelock;
20
34
  return {
@@ -73,6 +87,8 @@ class WaMessageCoordinator {
73
87
  this.emitAddon = deps.emitAddon;
74
88
  this.mexSocket = deps.mexSocket;
75
89
  this.peerDataOperation = deps.peerDataOperation;
90
+ this.isGroupHistorySendEnabled = deps.isGroupHistorySendEnabled;
91
+ this.getAbPropNumber = deps.getAbPropNumber;
76
92
  }
77
93
  /**
78
94
  * Asks the server to backfill older messages for `chatJid` beyond what
@@ -89,13 +105,7 @@ class WaMessageCoordinator {
89
105
  */
90
106
  async requestHistorySync(input) {
91
107
  const chatJid = (0, jid_1.normalizeRecipientJid)(input.chatJid);
92
- if (input.count !== undefined) {
93
- if (!Number.isFinite(input.count) ||
94
- !Number.isSafeInteger(input.count) ||
95
- input.count <= 0) {
96
- throw new Error(`invalid count: ${input.count}`);
97
- }
98
- }
108
+ const onDemandMsgCount = (0, coercion_1.resolveOptionalPositive)(input.count, 'count');
99
109
  if (input.oldestMsgTimestampMs !== undefined) {
100
110
  if (!Number.isFinite(input.oldestMsgTimestampMs) ||
101
111
  !Number.isSafeInteger(input.oldestMsgTimestampMs) ||
@@ -113,10 +123,155 @@ class WaMessageCoordinator {
113
123
  ...(input.oldestMsgTimestampMs === undefined
114
124
  ? {}
115
125
  : { oldestMsgTimestampMs: input.oldestMsgTimestampMs }),
116
- ...(input.count === undefined ? {} : { onDemandMsgCount: input.count })
126
+ ...(onDemandMsgCount === undefined ? {} : { onDemandMsgCount })
117
127
  };
118
128
  return this.peerDataOperation.send(_proto_1.proto.Message.PeerDataOperationRequestType.HISTORY_SYNC_ON_DEMAND, { historySyncOnDemandRequest });
119
129
  }
130
+ /**
131
+ * Shares recent group history with members who joined after the fact - the
132
+ * counterpart of the `group_history_bundle` event on the receiving side.
133
+ *
134
+ * Sends two messages: the bundle itself, encrypted per device and fanned
135
+ * out **only** to `toJids` (never to the whole group), followed by a hidden
136
+ * notice addressed to everyone so other clients can render the "history was
137
+ * shared" marker.
138
+ *
139
+ * WhatsApp gates the sender side per account through the `group_history_send`
140
+ * AB-prop and rejects the stanza with SMAX_INVALID when it is off, so this
141
+ * throws up front rather than spending an upload. Admin-only groups
142
+ * (`memberShareGroupHistoryMode: 'admin_share'`) reject a share from a
143
+ * regular member server-side - read `client.group.queryGroupMetadata()`
144
+ * first if you want to check that too.
145
+ *
146
+ * @throws when the account is not allowed to share group history, when
147
+ * `groupJid` is not a group, when any of `toJids` is not a current member
148
+ * (or is this account), or when there is nothing to bundle.
149
+ * @example
150
+ * ```ts
151
+ * await client.message.shareGroupHistory('12036@g.us', {
152
+ * toJids: ['5511999999999@s.whatsapp.net'],
153
+ * count: 50
154
+ * })
155
+ * ```
156
+ */
157
+ async shareGroupHistory(groupJid, input) {
158
+ const normalizedGroupJid = (0, jid_1.normalizeRecipientJid)(groupJid);
159
+ if (!(0, jid_1.isGroupJid)(normalizedGroupJid)) {
160
+ throw new Error(`shareGroupHistory requires a group jid: ${normalizedGroupJid}`);
161
+ }
162
+ if (input.toJids.length === 0) {
163
+ throw new Error('shareGroupHistory requires at least one recipient');
164
+ }
165
+ const messageLimit = (0, coercion_1.resolvePositive)(input.count, this.getAbPropNumber('group_history_message_count_limit'), 'count');
166
+ if (!this.isGroupHistorySendEnabled()) {
167
+ throw new Error('shareGroupHistory is disabled for this account (group_history_send is off)');
168
+ }
169
+ const audience = await this.messageDispatch.resolveGroupHistoryAudience(normalizedGroupJid, input.toJids);
170
+ if (audience.requestedSelf) {
171
+ throw new Error('shareGroupHistory cannot share history with this account itself');
172
+ }
173
+ if (audience.unknownJids.length > 0) {
174
+ throw new Error(`shareGroupHistory recipients are not members of the group (addressing mode: ${audience.addressingMode}): ${audience.unknownJids.join(', ')}`);
175
+ }
176
+ if (audience.historyReceivers.length === 0) {
177
+ throw new Error('shareGroupHistory resolved no recipients');
178
+ }
179
+ const messages = input.messages ??
180
+ (await this.loadGroupHistoryMessages(normalizedGroupJid, messageLimit, input.sinceMs));
181
+ if (messages.length === 0) {
182
+ throw new Error('shareGroupHistory found no messages to share');
183
+ }
184
+ const { compressed } = await (0, group_history_1.encodeGroupHistoryBundle)(messages, input.outOfWindowPinnedMessages);
185
+ const upload = await (0, messages_1.uploadMedia)(this.mediaUploadOptions, {
186
+ source: compressed,
187
+ cryptoType: 'group-history',
188
+ uploadPath: constants_1.MEDIA_UPLOAD_PATHS['group-history'],
189
+ contentType: GROUP_HISTORY_BUNDLE_MIMETYPE,
190
+ sidecar: false,
191
+ logLabel: 'group history bundle upload'
192
+ });
193
+ const oldestInWindow = oldestTimestampSeconds(messages);
194
+ const oldestPin = oldestTimestampSeconds(input.outOfWindowPinnedMessages ?? []);
195
+ const oldestInBundle = oldestPin === undefined
196
+ ? oldestInWindow
197
+ : oldestInWindow === undefined
198
+ ? oldestPin
199
+ : Math.min(oldestInWindow, oldestPin);
200
+ const metadata = {
201
+ historyReceivers: audience.historyReceivers,
202
+ nonHistoryReceivers: audience.nonHistoryReceivers,
203
+ messageCount: messages.length,
204
+ oldestMessageTimestampInWindow: oldestInWindow,
205
+ oldestMessageTimestampInBundle: oldestInBundle
206
+ };
207
+ const bundleResult = await this.send(normalizedGroupJid, {
208
+ messageHistoryBundle: {
209
+ mimetype: GROUP_HISTORY_BUNDLE_MIMETYPE,
210
+ fileSha256: upload.fileSha256,
211
+ fileEncSha256: upload.fileEncSha256,
212
+ mediaKey: upload.mediaKey,
213
+ directPath: upload.directPath,
214
+ mediaKeyTimestamp: this.mediaUploadOptions.serverClock.nowSeconds(),
215
+ messageHistoryMetadata: metadata
216
+ }
217
+ });
218
+ let noticeMessageId;
219
+ try {
220
+ noticeMessageId = (await this.send(normalizedGroupJid, {
221
+ messageHistoryNotice: { messageHistoryMetadata: metadata }
222
+ })).id;
223
+ }
224
+ catch (error) {
225
+ this.logger.warn('group history notice failed after the bundle was delivered', {
226
+ groupJid: normalizedGroupJid,
227
+ id: bundleResult.id,
228
+ receiverCount: audience.historyReceivers.length,
229
+ message: (0, primitives_1.toError)(error).message
230
+ });
231
+ }
232
+ this.logger.debug('shared group history', {
233
+ groupJid: normalizedGroupJid,
234
+ id: bundleResult.id,
235
+ messagesCount: messages.length,
236
+ receiverCount: audience.historyReceivers.length
237
+ });
238
+ return {
239
+ bundleMessageId: bundleResult.id,
240
+ noticeMessageId,
241
+ messagesCount: messages.length,
242
+ historyReceivers: audience.historyReceivers,
243
+ nonHistoryReceivers: audience.nonHistoryReceivers
244
+ };
245
+ }
246
+ /**
247
+ * Reads the most recent messages of a group back out of the mailbox store
248
+ * and rebuilds the `WebMessageInfo` shape a bundle carries. Yields nothing
249
+ * when the `messages` store domain is `'none'`.
250
+ */
251
+ async loadGroupHistoryMessages(groupJid, limit, sinceMs) {
252
+ const records = await this.messageStore.listByThread(groupJid, limit);
253
+ const messages = [];
254
+ for (let index = 0; index < records.length; index += 1) {
255
+ const record = records[index];
256
+ if (!record.messageBytes) {
257
+ continue;
258
+ }
259
+ if (sinceMs !== undefined && (record.timestampMs ?? 0) < sinceMs) {
260
+ continue;
261
+ }
262
+ messages[messages.length] = {
263
+ key: {
264
+ id: record.id,
265
+ remoteJid: groupJid,
266
+ fromMe: record.fromMe,
267
+ participant: record.participantJid ?? record.senderJid
268
+ },
269
+ message: _proto_1.proto.Message.decode(record.messageBytes),
270
+ messageTimestamp: Math.floor((record.timestampMs ?? 0) / 1000)
271
+ };
272
+ }
273
+ return messages;
274
+ }
120
275
  /**
121
276
  * Fetches the server-side "reachout" timelock that throttles cold outreach
122
277
  * to non-contacts, returning the active window when enforcement is on.