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.
- package/README.md +26 -12
- package/dist/abprops-spec.d.ts +10 -0
- package/dist/abprops-spec.js +25 -0
- package/dist/appstate/crypto/WaAppStateCrypto.js +4 -6
- package/dist/auth/WaAuthClient.d.ts +14 -1
- package/dist/auth/WaAuthClient.js +22 -1
- package/dist/auth/credentials-flow.d.ts +3 -1
- package/dist/auth/credentials-flow.js +3 -0
- package/dist/auth/pairing/WaPairingFlow.d.ts +5 -0
- package/dist/auth/pairing/WaPairingFlow.js +66 -14
- package/dist/auth/types.d.ts +13 -0
- package/dist/client/WaClient.d.ts +11 -0
- package/dist/client/WaClient.js +60 -0
- package/dist/client/WaClientFactory.d.ts +2 -2
- package/dist/client/WaClientFactory.js +53 -13
- package/dist/client/coordinators/WaAbPropsCoordinator.d.ts +2 -2
- package/dist/client/coordinators/WaAbPropsCoordinator.js +16 -4
- package/dist/client/coordinators/WaIncomingNodeCoordinator.d.ts +2 -1
- package/dist/client/coordinators/WaIncomingNodeCoordinator.js +4 -0
- package/dist/client/coordinators/WaMessageCoordinator.d.ts +96 -0
- package/dist/client/coordinators/WaMessageCoordinator.js +163 -8
- package/dist/client/coordinators/WaMessageDispatchCoordinator.d.ts +64 -0
- package/dist/client/coordinators/WaMessageDispatchCoordinator.js +252 -57
- package/dist/client/coordinators/WaPrivacyCoordinator.d.ts +97 -19
- package/dist/client/coordinators/WaPrivacyCoordinator.js +152 -101
- package/dist/client/coordinators/WaRetryCoordinator.d.ts +22 -0
- package/dist/client/coordinators/WaRetryCoordinator.js +74 -11
- package/dist/client/events/dirty.d.ts +3 -0
- package/dist/client/events/dirty.js +12 -7
- package/dist/client/events/offline.d.ts +9 -0
- package/dist/client/events/offline.js +95 -0
- package/dist/client/events/privacy.d.ts +45 -0
- package/dist/client/events/privacy.js +130 -0
- package/dist/client/messaging/group-metadata.d.ts +12 -0
- package/dist/client/messaging/group-metadata.js +24 -8
- package/dist/client/messaging/link-preview.d.ts +7 -0
- package/dist/client/messaging/link-preview.js +27 -0
- package/dist/client/messaging/messages.js +16 -12
- package/dist/client/newsletter/content.d.ts +4 -1
- package/dist/client/newsletter/content.js +43 -18
- package/dist/client/newsletter/discovery.js +4 -2
- package/dist/client/newsletter/messaging.d.ts +2 -0
- package/dist/client/newsletter/messaging.js +2 -1
- package/dist/client/newsletter/mex.d.ts +2 -2
- package/dist/client/persistence/WriteBehindPersistence.js +2 -1
- package/dist/client/persistence/ephemeral-setting.d.ts +42 -0
- package/dist/client/persistence/ephemeral-setting.js +120 -0
- package/dist/client/persistence/group-history.d.ts +29 -0
- package/dist/client/persistence/group-history.js +149 -0
- package/dist/client/persistence/history-blob.d.ts +25 -0
- package/dist/client/persistence/history-blob.js +38 -0
- package/dist/client/persistence/history-sync.d.ts +20 -0
- package/dist/client/persistence/history-sync.js +165 -71
- package/dist/client/plugins/types.d.ts +10 -2
- package/dist/client/types.d.ts +150 -16
- package/dist/crypto/core/index.d.ts +1 -1
- package/dist/crypto/core/index.js +2 -1
- package/dist/crypto/core/primitives.d.ts +19 -0
- package/dist/crypto/core/primitives.js +48 -0
- package/dist/crypto/core/xeddsa.js +35 -4
- package/dist/crypto/curves/X25519.js +29 -2
- package/dist/crypto/nativeBackend.d.ts +20 -0
- package/dist/crypto/nativeBackend.js +95 -0
- package/dist/esm/abprops-spec.js +15 -0
- package/dist/esm/appstate/crypto/WaAppStateCrypto.js +4 -6
- package/dist/esm/auth/WaAuthClient.js +22 -1
- package/dist/esm/auth/credentials-flow.js +3 -0
- package/dist/esm/auth/pairing/WaPairingFlow.js +67 -15
- package/dist/esm/client/WaClient.js +60 -0
- package/dist/esm/client/WaClientFactory.js +54 -14
- package/dist/esm/client/coordinators/WaAbPropsCoordinator.js +17 -5
- package/dist/esm/client/coordinators/WaIncomingNodeCoordinator.js +4 -0
- package/dist/esm/client/coordinators/WaMessageCoordinator.js +166 -11
- package/dist/esm/client/coordinators/WaMessageDispatchCoordinator.js +254 -60
- package/dist/esm/client/coordinators/WaPrivacyCoordinator.js +145 -94
- package/dist/esm/client/coordinators/WaRetryCoordinator.js +74 -11
- package/dist/esm/client/events/dirty.js +12 -7
- package/dist/esm/client/events/offline.js +92 -0
- package/dist/esm/client/events/privacy.js +123 -0
- package/dist/esm/client/messaging/group-metadata.js +25 -9
- package/dist/esm/client/messaging/link-preview.js +29 -2
- package/dist/esm/client/messaging/messages.js +16 -12
- package/dist/esm/client/newsletter/content.js +44 -19
- package/dist/esm/client/newsletter/discovery.js +4 -2
- package/dist/esm/client/newsletter/messaging.js +2 -1
- package/dist/esm/client/persistence/WriteBehindPersistence.js +2 -1
- package/dist/esm/client/persistence/ephemeral-setting.js +116 -0
- package/dist/esm/client/persistence/group-history.js +145 -0
- package/dist/esm/client/persistence/history-blob.js +34 -0
- package/dist/esm/client/persistence/history-sync.js +157 -65
- package/dist/esm/crypto/core/index.js +1 -1
- package/dist/esm/crypto/core/primitives.js +47 -0
- package/dist/esm/crypto/core/xeddsa.js +36 -5
- package/dist/esm/crypto/curves/X25519.js +29 -2
- package/dist/esm/crypto/nativeBackend.js +93 -0
- package/dist/esm/index.js +3 -1
- package/dist/esm/media/crypto/WaMediaCrypto.js +38 -19
- package/dist/esm/message/context-info.js +39 -0
- package/dist/esm/message/crypto/reporting-token.js +38 -216
- package/dist/esm/message/encode/content.js +73 -12
- package/dist/esm/message/encode/media-payload.js +3 -1
- package/dist/esm/message/kinds/group-history.js +36 -0
- package/dist/esm/message/primitives/incoming.js +32 -13
- package/dist/esm/protocol/abprops.js +18 -162
- package/dist/esm/protocol/constants.js +4 -3
- package/dist/esm/protocol/defaults.js +7 -0
- package/dist/esm/protocol/device-identity.js +10 -5
- package/dist/esm/protocol/jid.js +83 -36
- package/dist/esm/protocol/media.js +1 -0
- package/dist/esm/protocol/message.js +8 -0
- package/dist/esm/protocol/notification.js +1 -0
- package/dist/esm/protocol/privacy.js +70 -4
- package/dist/esm/signal/group/SenderKeyChain.js +1 -2
- package/dist/esm/signal/group/SenderKeyManager.js +1 -7
- package/dist/esm/signal/session/SignalProtocol.js +22 -21
- package/dist/esm/signal/session/SignalRatchet.js +20 -17
- package/dist/esm/signal/session/encoding.js +25 -3
- package/dist/esm/signal/session/resolver.js +11 -5
- package/dist/esm/store/contracts/chat-metadata.store.js +1 -0
- package/dist/esm/store/createStore.js +19 -2
- package/dist/esm/store/index.js +1 -0
- package/dist/esm/store/memory/chat-metadata.store.js +66 -0
- package/dist/esm/store/noop.store.js +8 -0
- package/dist/esm/transport/binary/constants.js +19 -0
- package/dist/esm/transport/binary/decoder.js +17 -19
- package/dist/esm/transport/binary/encoder.js +61 -18
- package/dist/esm/transport/node/builders/global.js +20 -0
- package/dist/esm/transport/node/builders/message.js +32 -15
- package/dist/esm/transport/node/builders/privacy.js +63 -4
- package/dist/esm/transport/noise/WaClientPayload.js +43 -22
- package/dist/esm/transport/noise/WaFrameCodec.js +14 -4
- package/dist/esm/util/coercion.js +12 -4
- package/dist/esm/util/index.js +1 -1
- package/dist/esm/util/protoscan.js +181 -0
- package/dist/esm/util/runtime.js +34 -0
- package/dist/index.d.ts +11 -6
- package/dist/index.js +15 -2
- package/dist/media/crypto/WaMediaCrypto.js +36 -17
- package/dist/media/types.d.ts +1 -1
- package/dist/message/context-info.d.ts +10 -0
- package/dist/message/context-info.js +40 -0
- package/dist/message/crypto/reporting-token.d.ts +3 -1
- package/dist/message/crypto/reporting-token.js +38 -216
- package/dist/message/encode/content.d.ts +24 -4
- package/dist/message/encode/content.js +74 -12
- package/dist/message/encode/media-payload.d.ts +5 -2
- package/dist/message/encode/media-payload.js +3 -1
- package/dist/message/kinds/group-history.d.ts +30 -0
- package/dist/message/kinds/group-history.js +40 -0
- package/dist/message/primitives/incoming.d.ts +5 -0
- package/dist/message/primitives/incoming.js +32 -13
- package/dist/message/types.d.ts +10 -0
- package/dist/protocol/abprops.d.ts +27 -140
- package/dist/protocol/abprops.js +18 -163
- package/dist/protocol/constants.d.ts +4 -3
- package/dist/protocol/constants.js +10 -3
- package/dist/protocol/defaults.d.ts +7 -0
- package/dist/protocol/defaults.js +7 -0
- package/dist/protocol/device-identity.d.ts +14 -4
- package/dist/protocol/device-identity.js +9 -4
- package/dist/protocol/jid.d.ts +4 -1
- package/dist/protocol/jid.js +83 -36
- package/dist/protocol/media.d.ts +1 -0
- package/dist/protocol/media.js +1 -0
- package/dist/protocol/message.d.ts +6 -0
- package/dist/protocol/message.js +9 -0
- package/dist/protocol/notification.d.ts +1 -0
- package/dist/protocol/notification.js +1 -0
- package/dist/protocol/privacy.d.ts +52 -0
- package/dist/protocol/privacy.js +71 -5
- package/dist/signal/group/SenderKeyChain.d.ts +1 -1
- package/dist/signal/group/SenderKeyChain.js +1 -2
- package/dist/signal/group/SenderKeyManager.js +1 -7
- package/dist/signal/session/SignalProtocol.d.ts +13 -0
- package/dist/signal/session/SignalProtocol.js +22 -21
- package/dist/signal/session/SignalRatchet.d.ts +4 -4
- package/dist/signal/session/SignalRatchet.js +19 -16
- package/dist/signal/session/SignalSession.d.ts +8 -1
- package/dist/signal/session/encoding.d.ts +5 -0
- package/dist/signal/session/encoding.js +25 -3
- package/dist/signal/session/resolver.js +11 -5
- package/dist/store/contracts/chat-metadata.store.d.ts +34 -0
- package/dist/store/contracts/chat-metadata.store.js +2 -0
- package/dist/store/contracts/group-metadata.store.d.ts +7 -0
- package/dist/store/contracts/thread.store.d.ts +6 -0
- package/dist/store/createStore.d.ts +10 -2
- package/dist/store/createStore.js +18 -1
- package/dist/store/index.d.ts +3 -1
- package/dist/store/index.js +3 -1
- package/dist/store/memory/chat-metadata.store.d.ts +26 -0
- package/dist/store/memory/chat-metadata.store.js +70 -0
- package/dist/store/noop.store.d.ts +2 -0
- package/dist/store/noop.store.js +9 -1
- package/dist/store/types.d.ts +190 -39
- package/dist/transport/binary/constants.d.ts +8 -0
- package/dist/transport/binary/constants.js +20 -1
- package/dist/transport/binary/decoder.js +17 -19
- package/dist/transport/binary/encoder.js +60 -17
- package/dist/transport/node/builders/global.d.ts +8 -0
- package/dist/transport/node/builders/global.js +21 -0
- package/dist/transport/node/builders/message.js +32 -15
- package/dist/transport/node/builders/privacy.d.ts +26 -2
- package/dist/transport/node/builders/privacy.js +63 -3
- package/dist/transport/noise/WaClientPayload.js +43 -22
- package/dist/transport/noise/WaFrameCodec.d.ts +1 -0
- package/dist/transport/noise/WaFrameCodec.js +14 -4
- package/dist/transport/noise/types.d.ts +17 -0
- package/dist/util/coercion.d.ts +6 -0
- package/dist/util/coercion.js +13 -4
- package/dist/util/index.d.ts +1 -1
- package/dist/util/index.js +2 -1
- package/dist/util/protoscan.d.ts +33 -0
- package/dist/util/protoscan.js +186 -0
- package/dist/util/runtime.d.ts +12 -0
- package/dist/util/runtime.js +35 -0
- package/package.json +3 -1
- package/spec/abprops/index.d.ts +4402 -0
- 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: (
|
|
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
|
|
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
|
|
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,
|
|
112
|
+
const name = (0, _abprops_spec_1.resolveAbPropNameByCode)(entry.configCode);
|
|
108
113
|
if (!name) {
|
|
109
114
|
continue;
|
|
110
115
|
}
|
|
111
|
-
const config =
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
...(
|
|
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.
|