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
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
import { EventEmitter } from 'node:events';
|
|
2
2
|
import { createIgnoreKeyFilter, validateIgnoreKey } from './messaging/ignore-key.js';
|
|
3
|
+
import { createEphemeralObserver, persistIncomingEphemeralSetting } from './persistence/ephemeral-setting.js';
|
|
4
|
+
import { runGroupHistoryBundle } from './persistence/group-history.js';
|
|
3
5
|
import { runHistorySyncNotification } from './persistence/history-sync.js';
|
|
4
6
|
import { persistIncomingMailboxEntities } from './persistence/mailbox.js';
|
|
5
7
|
import { WriteBehindPersistence } from './persistence/WriteBehindPersistence.js';
|
|
6
8
|
import { installWaClientPlugins } from './plugins/install.js';
|
|
7
9
|
import { buildWaClientDependencies, resolveWaClientBase } from './WaClientFactory.js';
|
|
8
10
|
import { ConsoleLogger } from '../infra/log/ConsoleLogger.js';
|
|
11
|
+
import { unwrapMessage } from '../message/encode/content.js';
|
|
9
12
|
import { proto } from '../proto.js';
|
|
10
13
|
import { WA_DEFAULTS, WA_MESSAGE_TYPES } from '../protocol/constants.js';
|
|
11
14
|
import { normalizeDeviceJid } from '../protocol/jid.js';
|
|
@@ -42,6 +45,10 @@ class WaClientImpl extends EventEmitter {
|
|
|
42
45
|
threadStore: this.stores.threads,
|
|
43
46
|
contactStore: this.stores.contacts
|
|
44
47
|
}, this.logger, this.options.writeBehind);
|
|
48
|
+
this.observeEphemeralSetting = createEphemeralObserver({
|
|
49
|
+
logger: this.logger,
|
|
50
|
+
chatMetadataStore: this.stores.chatMetadata
|
|
51
|
+
});
|
|
45
52
|
const dependencies = buildWaClientDependencies({
|
|
46
53
|
base,
|
|
47
54
|
runtime: {
|
|
@@ -182,6 +189,7 @@ class WaClientImpl extends EventEmitter {
|
|
|
182
189
|
}
|
|
183
190
|
try {
|
|
184
191
|
this.emit('message', event);
|
|
192
|
+
this.observeEphemeralSetting(event);
|
|
185
193
|
void persistIncomingMailboxEntities({
|
|
186
194
|
logger: this.logger,
|
|
187
195
|
writeBehind: this.writeBehind,
|
|
@@ -207,6 +215,9 @@ class WaClientImpl extends EventEmitter {
|
|
|
207
215
|
});
|
|
208
216
|
});
|
|
209
217
|
}
|
|
218
|
+
if (this.options.history?.groupBundles === true && event.message && !event.key.fromMe) {
|
|
219
|
+
this.tryProcessGroupHistoryBundle(event);
|
|
220
|
+
}
|
|
210
221
|
const protocolMessage = event.message?.protocolMessage;
|
|
211
222
|
if (!protocolMessage) {
|
|
212
223
|
return;
|
|
@@ -258,6 +269,7 @@ class WaClientImpl extends EventEmitter {
|
|
|
258
269
|
await runHistorySyncNotification({
|
|
259
270
|
logger: this.logger,
|
|
260
271
|
mediaTransfer: this.mediaTransfer,
|
|
272
|
+
chatMetadataStore: this.stores.chatMetadata,
|
|
261
273
|
writeBehind: this.writeBehind,
|
|
262
274
|
emitEvent: this.emit.bind(this),
|
|
263
275
|
onPrivacyTokens: (conversations) => this.deps.trustedContactToken.hydrateFromHistorySync(conversations),
|
|
@@ -270,6 +282,16 @@ class WaClientImpl extends EventEmitter {
|
|
|
270
282
|
}
|
|
271
283
|
return;
|
|
272
284
|
}
|
|
285
|
+
if (protocolType === proto.Message.ProtocolMessage.Type.EPHEMERAL_SETTING) {
|
|
286
|
+
persistIncomingEphemeralSetting({
|
|
287
|
+
logger: this.logger,
|
|
288
|
+
writeBehind: this.writeBehind,
|
|
289
|
+
chatMetadataStore: this.stores.chatMetadata,
|
|
290
|
+
event,
|
|
291
|
+
protocolMessage
|
|
292
|
+
});
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
273
295
|
if (SYNC_RELATED_PROTOCOL_TYPES.has(protocolType)) {
|
|
274
296
|
this.logger.info('incoming sync-related protocol message', {
|
|
275
297
|
id: event.key.id,
|
|
@@ -288,6 +310,43 @@ class WaClientImpl extends EventEmitter {
|
|
|
288
310
|
this.leaveIncomingHandler();
|
|
289
311
|
}
|
|
290
312
|
}
|
|
313
|
+
/**
|
|
314
|
+
* Kicks off the group-history bundle download when the incoming message
|
|
315
|
+
* carries one. Fire-and-forget: the blob can be large, and the incoming
|
|
316
|
+
* handler must not stall behind a CDN fetch.
|
|
317
|
+
*
|
|
318
|
+
* The download outlives the handler that started it, so it takes a slot of
|
|
319
|
+
* its own in the drain accounting - `disconnect()` and `clearStoredState()`
|
|
320
|
+
* must not flush or wipe the stores while a bundle is still writing.
|
|
321
|
+
*/
|
|
322
|
+
tryProcessGroupHistoryBundle(event) {
|
|
323
|
+
const bundle = unwrapMessage(event.message ?? {}).messageHistoryBundle;
|
|
324
|
+
const groupJid = event.key.remoteJid;
|
|
325
|
+
if (!bundle || !groupJid) {
|
|
326
|
+
return;
|
|
327
|
+
}
|
|
328
|
+
if (!this.tryEnterIncomingHandler()) {
|
|
329
|
+
return;
|
|
330
|
+
}
|
|
331
|
+
const credentials = this.deps.authClient.getCurrentCredentials();
|
|
332
|
+
void runGroupHistoryBundle({
|
|
333
|
+
logger: this.logger,
|
|
334
|
+
mediaTransfer: this.mediaTransfer,
|
|
335
|
+
writeBehind: this.writeBehind,
|
|
336
|
+
emitEvent: this.emit.bind(this),
|
|
337
|
+
meJid: credentials?.meJid,
|
|
338
|
+
meLid: credentials?.meLid,
|
|
339
|
+
getAbPropNumber: (name) => this.deps.abPropsCoordinator.getConfigValue(name)
|
|
340
|
+
}, {
|
|
341
|
+
bundle,
|
|
342
|
+
groupJid,
|
|
343
|
+
senderJid: event.key.participant ?? undefined,
|
|
344
|
+
bundleMessageId: event.key.id,
|
|
345
|
+
sentAtSeconds: event.timestampSeconds
|
|
346
|
+
}).finally(() => {
|
|
347
|
+
this.leaveIncomingHandler();
|
|
348
|
+
});
|
|
349
|
+
}
|
|
291
350
|
async queryWithContext(context, node, timeoutMs = this.options.iqTimeoutMs ?? WA_DEFAULTS.IQ_TIMEOUT_MS, contextData = {}, options = {}) {
|
|
292
351
|
return queryNodeWithContext(async (queryNode, queryTimeoutMs) => this.deps.lowLevelCoordinator.query(queryNode, queryTimeoutMs, options), this.logger, context, node, timeoutMs, contextData);
|
|
293
352
|
}
|
|
@@ -362,6 +421,7 @@ class WaClientImpl extends EventEmitter {
|
|
|
362
421
|
* your own backoff.
|
|
363
422
|
*/
|
|
364
423
|
async disconnect() {
|
|
424
|
+
this.deps.privacyCoordinator.stopAccountSyncRefresh();
|
|
365
425
|
await this.pauseIncomingEventsAndWaitDrain();
|
|
366
426
|
const writeBehindFlush = await this.writeBehind.flush(this.options.writeBehind?.flushTimeoutMs);
|
|
367
427
|
if (writeBehindFlush.remaining > 0) {
|
|
@@ -55,7 +55,7 @@ import { SignalProtocol } from '../signal/session/SignalProtocol.js';
|
|
|
55
55
|
import { WaKeepAlive } from '../transport/keepalive/WaKeepAlive.js';
|
|
56
56
|
import { buildAckNode } from '../transport/node/builders/global.js';
|
|
57
57
|
import { buildPresenceNode } from '../transport/node/builders/presence.js';
|
|
58
|
-
import { getFirstNodeChild } from '../transport/node/helpers.js';
|
|
58
|
+
import { findNodeChild, getFirstNodeChild } from '../transport/node/helpers.js';
|
|
59
59
|
import { createUsyncSidGenerator } from '../transport/node/usync.js';
|
|
60
60
|
import { WaNodeOrchestrator } from '../transport/node/WaNodeOrchestrator.js';
|
|
61
61
|
import { WaNodeTransport } from '../transport/node/WaNodeTransport.js';
|
|
@@ -96,6 +96,7 @@ export function resolveWaClientBase(options, logger) {
|
|
|
96
96
|
sessionId,
|
|
97
97
|
deviceBrowser: device.browser,
|
|
98
98
|
deviceOsDisplayName: device.osDisplayName,
|
|
99
|
+
deviceOsVersion: device.osVersion ?? undefined,
|
|
99
100
|
devicePlatform: device.platform,
|
|
100
101
|
urls: options.urls ?? options.chatSocketUrls ?? WA_DEFAULTS.CHAT_SOCKET_URLS,
|
|
101
102
|
iqTimeoutMs: options.iqTimeoutMs ?? WA_DEFAULTS.IQ_TIMEOUT_MS,
|
|
@@ -120,7 +121,7 @@ function createIncomingNodeRuntime(input) {
|
|
|
120
121
|
const { logger, emitEvent, authClient, connectionManager, nodeOrchestrator, streamControl, mediaMessageBuildOptions, retryCoordinator, messageDispatch, sendNode, syncAppState, disconnect, clearStoredCredentials, getCurrentCredentials, handleClientDirtyBits, incomingMessageAckOptions } = input;
|
|
121
122
|
return {
|
|
122
123
|
handleStreamControlResult: streamControl.handleStreamControlResult,
|
|
123
|
-
persistSuccessAttributes: (attributes) => authClient.persistSuccessAttributes(attributes),
|
|
124
|
+
persistSuccessAttributes: (attributes) => authClient.persistSuccessAttributes(attributes, { countsAsLogin: true }),
|
|
124
125
|
emitSuccessNode: (node) => emitEvent('debug_connection_success', { node }),
|
|
125
126
|
updateClockSkewFromSuccess: (serverUnixSeconds) => connectionManager.updateClockSkewFromSuccess(serverUnixSeconds),
|
|
126
127
|
shouldWarmupMediaConn: () => !!(getCurrentCredentials()?.meJid && connectionManager.isConnected()),
|
|
@@ -145,6 +146,7 @@ function createIncomingNodeRuntime(input) {
|
|
|
145
146
|
emitIncomingFailure: (event) => emitEvent('stream_failure', event),
|
|
146
147
|
emitIncomingErrorStanza: (event) => emitEvent('stanza_error', event),
|
|
147
148
|
emitIncomingNotification: (event) => emitEvent('debug_notification', event),
|
|
149
|
+
emitOfflineThreadMetadata: (event) => emitEvent('offline_thread_metadata', event),
|
|
148
150
|
emitMexNotification: (event) => emitEvent('mex_notification', event),
|
|
149
151
|
emitRegistrationCode: (event) => emitEvent('mobile_registration_code', event),
|
|
150
152
|
emitAccountTakeoverNotice: (event) => emitEvent('mobile_account_takeover_notice', event),
|
|
@@ -240,6 +242,15 @@ export function buildWaClientDependencies(input) {
|
|
|
240
242
|
allowPrivateHosts: linkPreviewOptions.allowPrivateHosts,
|
|
241
243
|
proxy: linkPreviewOptions.proxy ?? options.proxy?.linkPreview
|
|
242
244
|
});
|
|
245
|
+
const createLinkPreviewResolver = (surface) => (content) => resolveLinkPreview(content.text, content.linkPreview, {
|
|
246
|
+
logger,
|
|
247
|
+
mediaTransfer,
|
|
248
|
+
getMediaConn: () => getClientMediaConn(mediaMessageBuildOptions),
|
|
249
|
+
fetcher: linkPreviewFetcher,
|
|
250
|
+
options: linkPreviewOptions,
|
|
251
|
+
serverClock,
|
|
252
|
+
surface
|
|
253
|
+
});
|
|
243
254
|
const mediaMessageBuildOptions = {
|
|
244
255
|
logger,
|
|
245
256
|
mediaTransfer,
|
|
@@ -257,14 +268,7 @@ export function buildWaClientDependencies(input) {
|
|
|
257
268
|
},
|
|
258
269
|
serverClock,
|
|
259
270
|
media: options.media,
|
|
260
|
-
linkPreviewResolver: (
|
|
261
|
-
logger,
|
|
262
|
-
mediaTransfer,
|
|
263
|
-
getMediaConn: () => getClientMediaConn(mediaMessageBuildOptions),
|
|
264
|
-
fetcher: linkPreviewFetcher,
|
|
265
|
-
options: linkPreviewOptions,
|
|
266
|
-
serverClock
|
|
267
|
-
})
|
|
271
|
+
linkPreviewResolver: createLinkPreviewResolver()
|
|
268
272
|
};
|
|
269
273
|
const messageClient = new WaMessageClient({
|
|
270
274
|
logger,
|
|
@@ -325,6 +329,7 @@ export function buildWaClientDependencies(input) {
|
|
|
325
329
|
const authClient = new WaAuthClient({
|
|
326
330
|
deviceBrowser: options.deviceBrowser,
|
|
327
331
|
deviceOsDisplayName: options.deviceOsDisplayName,
|
|
332
|
+
deviceOsVersion: options.deviceOsVersion,
|
|
328
333
|
devicePlatform: options.devicePlatform,
|
|
329
334
|
requireFullSync: options.requireFullSync,
|
|
330
335
|
version: options.version,
|
|
@@ -367,12 +372,16 @@ export function buildWaClientDependencies(input) {
|
|
|
367
372
|
generateStanzaId: () => messageDispatch.generateOutgoingMessageId(),
|
|
368
373
|
mediaTransfer,
|
|
369
374
|
getMediaConn: () => getClientMediaConn(mediaMessageBuildOptions),
|
|
375
|
+
linkPreviewResolver: createLinkPreviewResolver('newsletter'),
|
|
370
376
|
getAbPropString: (name) => abPropsCoordinator.getConfigValue(name),
|
|
371
377
|
logger
|
|
372
378
|
});
|
|
373
379
|
const privacyCoordinator = createPrivacyCoordinator({
|
|
380
|
+
logger,
|
|
374
381
|
queryWithContext: runtime.queryWithContext,
|
|
375
|
-
resolveUserJidPair: (userJid) => signalDeviceSync.resolveUserJidPair(userJid)
|
|
382
|
+
resolveUserJidPair: (userJid) => signalDeviceSync.resolveUserJidPair(userJid),
|
|
383
|
+
getSelfLid: () => getCurrentCredentials()?.meLid ?? null,
|
|
384
|
+
emitPrivacy: (event) => runtime.emitEvent('privacy', event)
|
|
376
385
|
});
|
|
377
386
|
const businessCoordinator = createBusinessCoordinator({
|
|
378
387
|
queryWithContext: runtime.queryWithContext,
|
|
@@ -411,7 +420,8 @@ export function buildWaClientDependencies(input) {
|
|
|
411
420
|
}
|
|
412
421
|
return {
|
|
413
422
|
participants: participantJids,
|
|
414
|
-
ephemeral: metadata.ephemeral
|
|
423
|
+
ephemeral: metadata.ephemeral,
|
|
424
|
+
ephemeralTrigger: metadata.ephemeralTrigger
|
|
415
425
|
};
|
|
416
426
|
},
|
|
417
427
|
logger
|
|
@@ -466,6 +476,8 @@ export function buildWaClientDependencies(input) {
|
|
|
466
476
|
sessionStore: sessionStore.session,
|
|
467
477
|
identityStore: sessionStore.identity,
|
|
468
478
|
deviceListStore: sessionStore.deviceList,
|
|
479
|
+
threadStore: sessionStore.threads,
|
|
480
|
+
chatMetadataStore: sessionStore.chatMetadata,
|
|
469
481
|
signalDeviceSync,
|
|
470
482
|
messageSecretStore: sessionStore.messageSecret,
|
|
471
483
|
persistAllMessageSecrets: options.addons?.persistAllSecrets === true,
|
|
@@ -508,7 +520,9 @@ export function buildWaClientDependencies(input) {
|
|
|
508
520
|
trustedContactToken,
|
|
509
521
|
emitAddon: (event) => runtime.emitEvent('message_addon', event),
|
|
510
522
|
mexSocket: { query: runtime.query },
|
|
511
|
-
peerDataOperation
|
|
523
|
+
peerDataOperation,
|
|
524
|
+
isGroupHistorySendEnabled: () => abPropsCoordinator.getConfigValue('group_history_send'),
|
|
525
|
+
getAbPropNumber: (name) => abPropsCoordinator.getConfigValue(name)
|
|
512
526
|
});
|
|
513
527
|
const retryCoordinator = new WaRetryCoordinator({
|
|
514
528
|
logger,
|
|
@@ -533,7 +547,8 @@ export function buildWaClientDependencies(input) {
|
|
|
533
547
|
.handleIncomingMessageEvent(event)
|
|
534
548
|
.catch((err) => runtime.handleError(toError(err)));
|
|
535
549
|
},
|
|
536
|
-
isMobilePrimary
|
|
550
|
+
isMobilePrimary,
|
|
551
|
+
getAbPropNumber: (name) => abPropsCoordinator.getConfigValue(name)
|
|
537
552
|
});
|
|
538
553
|
const botCoordinator = createBotCoordinator({
|
|
539
554
|
logger,
|
|
@@ -645,6 +660,7 @@ export function buildWaClientDependencies(input) {
|
|
|
645
660
|
const disconnectWithClientSideEffects = async (reason, isLogout, code) => {
|
|
646
661
|
abPropsCoordinator.reset();
|
|
647
662
|
offlineResume.reset();
|
|
663
|
+
privacyCoordinator.stopAccountSyncRefresh();
|
|
648
664
|
await connectionManager?.disconnect();
|
|
649
665
|
runtime.emitEvent('connection', {
|
|
650
666
|
status: 'close',
|
|
@@ -691,6 +707,7 @@ export function buildWaClientDependencies(input) {
|
|
|
691
707
|
signalProtocol,
|
|
692
708
|
senderKeyManager,
|
|
693
709
|
onDecryptFailure: (context, error) => retryCoordinator.onDecryptFailure(context, error),
|
|
710
|
+
requestPlaceholderResend: (context) => retryCoordinator.onUnavailableMessage(context),
|
|
694
711
|
emitIncomingMessage: (event) => {
|
|
695
712
|
void runtime
|
|
696
713
|
.handleIncomingMessageEvent(event)
|
|
@@ -706,6 +723,10 @@ export function buildWaClientDependencies(input) {
|
|
|
706
723
|
getCurrentCredentials,
|
|
707
724
|
syncAppState: runtime.syncAppState,
|
|
708
725
|
generateUsyncSid,
|
|
726
|
+
syncAccountPrivacy: async () => {
|
|
727
|
+
await privacyCoordinator.refreshFromAccountSync();
|
|
728
|
+
},
|
|
729
|
+
emitBlocklist: (blocklist) => runtime.emitEvent('blocklist', blocklist),
|
|
709
730
|
newsletterListSubscribed: () => newsletterCoordinator.listSubscribed()
|
|
710
731
|
}, dirtyBits);
|
|
711
732
|
const offlineResume = new WaOfflineResumeCoordinator({
|
|
@@ -887,6 +908,25 @@ export function buildWaClientDependencies(input) {
|
|
|
887
908
|
return false;
|
|
888
909
|
}
|
|
889
910
|
});
|
|
911
|
+
incomingNode.registerIncomingHandler({
|
|
912
|
+
tag: WA_NODE_TAGS.NOTIFICATION,
|
|
913
|
+
subtype: WA_NOTIFICATION_TYPES.ACCOUNT_SYNC,
|
|
914
|
+
prepend: true,
|
|
915
|
+
/**
|
|
916
|
+
* The stanza carries the changed `<category>` inline, but it is a
|
|
917
|
+
* trigger only: the payload is a partial view, so the whole set is
|
|
918
|
+
* refetched instead. Observer only - returning false keeps the
|
|
919
|
+
* generic notification handler responsible for the ack and the
|
|
920
|
+
* `debug_notification` emit, leaving wire behavior unchanged.
|
|
921
|
+
*/
|
|
922
|
+
// eslint-disable-next-line @typescript-eslint/require-await
|
|
923
|
+
handler: async (node) => {
|
|
924
|
+
if (findNodeChild(node, WA_NODE_TAGS.PRIVACY)) {
|
|
925
|
+
privacyCoordinator.scheduleAccountSyncRefresh();
|
|
926
|
+
}
|
|
927
|
+
return false;
|
|
928
|
+
}
|
|
929
|
+
});
|
|
890
930
|
incomingNode.registerIncomingHandler({
|
|
891
931
|
tag: WA_NODE_TAGS.NOTIFICATION,
|
|
892
932
|
subtype: WA_NOTIFICATION_TYPES.DEVICES,
|
|
@@ -1,8 +1,13 @@
|
|
|
1
|
+
import { resolveAbPropNameByCode, WA_ABPROPS } from '../../abprops-spec.js';
|
|
1
2
|
import { parseAbPropsIqResult } from '../events/abprops.js';
|
|
2
|
-
import {
|
|
3
|
+
import { WA_ABPROPS_REFRESH_BOUNDS } from '../../protocol/abprops.js';
|
|
3
4
|
import { WA_DEFAULTS } from '../../protocol/constants.js';
|
|
4
5
|
import { buildGetAbPropsIq } from '../../transport/node/builders/abprops.js';
|
|
5
|
-
import {
|
|
6
|
+
import { toError } from '../../util/primitives.js';
|
|
7
|
+
// The generated table types every entry with literal `code` / `defaultValue`
|
|
8
|
+
// types. Reading through a widened view keeps a lookup by a non-literal name
|
|
9
|
+
// from expanding into a 2000-member union at every call site.
|
|
10
|
+
const AB_PROPS = WA_ABPROPS;
|
|
6
11
|
export class WaAbPropsCoordinator {
|
|
7
12
|
constructor(options) {
|
|
8
13
|
this.logger = options.logger;
|
|
@@ -18,7 +23,7 @@ export class WaAbPropsCoordinator {
|
|
|
18
23
|
if (cached !== undefined) {
|
|
19
24
|
return cached;
|
|
20
25
|
}
|
|
21
|
-
return
|
|
26
|
+
return AB_PROPS[name].defaultValue;
|
|
22
27
|
}
|
|
23
28
|
sync() {
|
|
24
29
|
if (this.syncPromise) {
|
|
@@ -105,7 +110,7 @@ export class WaAbPropsCoordinator {
|
|
|
105
110
|
if (!name) {
|
|
106
111
|
continue;
|
|
107
112
|
}
|
|
108
|
-
const config =
|
|
113
|
+
const config = AB_PROPS[name];
|
|
109
114
|
const parsed = parseConfigValue(entry.configValue, config.type, config.defaultValue);
|
|
110
115
|
this.cache.set(name, parsed);
|
|
111
116
|
applied += 1;
|
|
@@ -119,6 +124,8 @@ export class WaAbPropsCoordinator {
|
|
|
119
124
|
});
|
|
120
125
|
}
|
|
121
126
|
}
|
|
127
|
+
// parseInt, not a digits-only parse: negative values are in band, several props
|
|
128
|
+
// ship -1 as their default. Divergence: WA Web caches NaN, we keep the default.
|
|
122
129
|
function parseConfigValue(value, type, defaultValue) {
|
|
123
130
|
if (value === null) {
|
|
124
131
|
return defaultValue;
|
|
@@ -127,7 +134,12 @@ function parseConfigValue(value, type, defaultValue) {
|
|
|
127
134
|
return value === '1' || value === 'true' || value === 'True';
|
|
128
135
|
}
|
|
129
136
|
if (type === 'int') {
|
|
130
|
-
|
|
137
|
+
const parsed = Number.parseInt(value, 10);
|
|
138
|
+
return Number.isNaN(parsed) ? defaultValue : parsed;
|
|
139
|
+
}
|
|
140
|
+
if (type === 'float') {
|
|
141
|
+
const parsed = Number.parseFloat(value);
|
|
142
|
+
return Number.isNaN(parsed) ? defaultValue : parsed;
|
|
131
143
|
}
|
|
132
144
|
return value;
|
|
133
145
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { parseChatstateNode } from '../events/chatstate.js';
|
|
2
2
|
import { buildInboundAck, createIncomingBaseEvent, createIncomingBusinessNotificationHandler, createIncomingCallHandler, createIncomingFailureHandler, createIncomingGroupNotificationHandler, createIncomingNotificationHandler, createIncomingPictureNotificationHandler, createIncomingReceiptHandler, createIncomingRegistrationNotificationHandler, createInfoBulletinNotificationEvent, createUnhandledIncomingNodeEvent, sendSafeAck } from '../events/incoming.js';
|
|
3
|
+
import { parseOfflineThreadMetadata } from '../events/offline.js';
|
|
3
4
|
import { parsePresenceNode } from '../events/presence.js';
|
|
4
5
|
import { WA_IQ_TYPES, WA_MESSAGE_TAGS, WA_MESSAGE_TYPES, WA_NODE_TAGS, WA_NOTIFICATION_TYPES, WA_SIGNALING } from '../../protocol/constants.js';
|
|
5
6
|
import { decodeNodeContentBase64OrBytes, findNodeChild, getNodeChildren, getNodeChildrenByTag } from '../../transport/node/helpers.js';
|
|
@@ -378,6 +379,9 @@ export class WaIncomingNodeCoordinator {
|
|
|
378
379
|
if (child.tag === 'offline') {
|
|
379
380
|
this.offlineResume.handleOfflineComplete(parseOptionalInt(child.attrs.count) ?? 0);
|
|
380
381
|
}
|
|
382
|
+
if (child.tag === 'thread_metadata') {
|
|
383
|
+
this.runtime.emitOfflineThreadMetadata(parseOfflineThreadMetadata(child));
|
|
384
|
+
}
|
|
381
385
|
handled = true;
|
|
382
386
|
}
|
|
383
387
|
}
|
|
@@ -6,12 +6,26 @@ import { uploadMedia } from '../messaging/messages.js';
|
|
|
6
6
|
import { MEDIA_UPLOAD_PATHS } from '../../media/constants.js';
|
|
7
7
|
import { buildAddonAdditionalData, decodeAddonPlaintext, decryptAddonPayload, identifyEncryptedAddon, resolveParentMessageSecret, resolvePollOptionNames, shouldUseAddonAdditionalData } from '../../message/crypto/addon-crypto.js';
|
|
8
8
|
import { unwrapMessage } from '../../message/encode/content.js';
|
|
9
|
+
import { encodeGroupHistoryBundle } from '../../message/kinds/group-history.js';
|
|
9
10
|
import { proto } from '../../proto.js';
|
|
10
|
-
import { applyDeviceToJid, normalizeRecipientJid } from '../../protocol/jid.js';
|
|
11
|
+
import { applyDeviceToJid, isGroupJid, normalizeRecipientJid } from '../../protocol/jid.js';
|
|
11
12
|
import { runMexQuery } from '../../transport/node/mex/client.js';
|
|
12
13
|
import { readAllBytes } from '../../util/bytes.js';
|
|
13
|
-
import { tryAsNumber, tryAsString } from '../../util/coercion.js';
|
|
14
|
-
import { toError } from '../../util/primitives.js';
|
|
14
|
+
import { resolveOptionalPositive, resolvePositive, tryAsNumber, tryAsString } from '../../util/coercion.js';
|
|
15
|
+
import { longToNumber, toError } from '../../util/primitives.js';
|
|
16
|
+
/** MIME type the group-history bundle is uploaded and advertised under. */
|
|
17
|
+
const GROUP_HISTORY_BUNDLE_MIMETYPE = 'application/protobuf';
|
|
18
|
+
/** Oldest `messageTimestamp` across `messages`, in seconds; `undefined` when none carries one. */
|
|
19
|
+
function oldestTimestampSeconds(messages) {
|
|
20
|
+
let oldest;
|
|
21
|
+
for (let index = 0; index < messages.length; index += 1) {
|
|
22
|
+
const timestamp = longToNumber(messages[index].messageTimestamp);
|
|
23
|
+
if (timestamp > 0 && (oldest === undefined || timestamp < oldest)) {
|
|
24
|
+
oldest = timestamp;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return oldest;
|
|
28
|
+
}
|
|
15
29
|
function parseReachoutTimelockMexResponse(data) {
|
|
16
30
|
const root = data?.xwa2_fetch_account_reachout_timelock;
|
|
17
31
|
return {
|
|
@@ -70,6 +84,8 @@ export class WaMessageCoordinator {
|
|
|
70
84
|
this.emitAddon = deps.emitAddon;
|
|
71
85
|
this.mexSocket = deps.mexSocket;
|
|
72
86
|
this.peerDataOperation = deps.peerDataOperation;
|
|
87
|
+
this.isGroupHistorySendEnabled = deps.isGroupHistorySendEnabled;
|
|
88
|
+
this.getAbPropNumber = deps.getAbPropNumber;
|
|
73
89
|
}
|
|
74
90
|
/**
|
|
75
91
|
* Asks the server to backfill older messages for `chatJid` beyond what
|
|
@@ -86,13 +102,7 @@ export class WaMessageCoordinator {
|
|
|
86
102
|
*/
|
|
87
103
|
async requestHistorySync(input) {
|
|
88
104
|
const chatJid = normalizeRecipientJid(input.chatJid);
|
|
89
|
-
|
|
90
|
-
if (!Number.isFinite(input.count) ||
|
|
91
|
-
!Number.isSafeInteger(input.count) ||
|
|
92
|
-
input.count <= 0) {
|
|
93
|
-
throw new Error(`invalid count: ${input.count}`);
|
|
94
|
-
}
|
|
95
|
-
}
|
|
105
|
+
const onDemandMsgCount = resolveOptionalPositive(input.count, 'count');
|
|
96
106
|
if (input.oldestMsgTimestampMs !== undefined) {
|
|
97
107
|
if (!Number.isFinite(input.oldestMsgTimestampMs) ||
|
|
98
108
|
!Number.isSafeInteger(input.oldestMsgTimestampMs) ||
|
|
@@ -110,10 +120,155 @@ export class WaMessageCoordinator {
|
|
|
110
120
|
...(input.oldestMsgTimestampMs === undefined
|
|
111
121
|
? {}
|
|
112
122
|
: { oldestMsgTimestampMs: input.oldestMsgTimestampMs }),
|
|
113
|
-
...(
|
|
123
|
+
...(onDemandMsgCount === undefined ? {} : { onDemandMsgCount })
|
|
114
124
|
};
|
|
115
125
|
return this.peerDataOperation.send(proto.Message.PeerDataOperationRequestType.HISTORY_SYNC_ON_DEMAND, { historySyncOnDemandRequest });
|
|
116
126
|
}
|
|
127
|
+
/**
|
|
128
|
+
* Shares recent group history with members who joined after the fact - the
|
|
129
|
+
* counterpart of the `group_history_bundle` event on the receiving side.
|
|
130
|
+
*
|
|
131
|
+
* Sends two messages: the bundle itself, encrypted per device and fanned
|
|
132
|
+
* out **only** to `toJids` (never to the whole group), followed by a hidden
|
|
133
|
+
* notice addressed to everyone so other clients can render the "history was
|
|
134
|
+
* shared" marker.
|
|
135
|
+
*
|
|
136
|
+
* WhatsApp gates the sender side per account through the `group_history_send`
|
|
137
|
+
* AB-prop and rejects the stanza with SMAX_INVALID when it is off, so this
|
|
138
|
+
* throws up front rather than spending an upload. Admin-only groups
|
|
139
|
+
* (`memberShareGroupHistoryMode: 'admin_share'`) reject a share from a
|
|
140
|
+
* regular member server-side - read `client.group.queryGroupMetadata()`
|
|
141
|
+
* first if you want to check that too.
|
|
142
|
+
*
|
|
143
|
+
* @throws when the account is not allowed to share group history, when
|
|
144
|
+
* `groupJid` is not a group, when any of `toJids` is not a current member
|
|
145
|
+
* (or is this account), or when there is nothing to bundle.
|
|
146
|
+
* @example
|
|
147
|
+
* ```ts
|
|
148
|
+
* await client.message.shareGroupHistory('12036@g.us', {
|
|
149
|
+
* toJids: ['5511999999999@s.whatsapp.net'],
|
|
150
|
+
* count: 50
|
|
151
|
+
* })
|
|
152
|
+
* ```
|
|
153
|
+
*/
|
|
154
|
+
async shareGroupHistory(groupJid, input) {
|
|
155
|
+
const normalizedGroupJid = normalizeRecipientJid(groupJid);
|
|
156
|
+
if (!isGroupJid(normalizedGroupJid)) {
|
|
157
|
+
throw new Error(`shareGroupHistory requires a group jid: ${normalizedGroupJid}`);
|
|
158
|
+
}
|
|
159
|
+
if (input.toJids.length === 0) {
|
|
160
|
+
throw new Error('shareGroupHistory requires at least one recipient');
|
|
161
|
+
}
|
|
162
|
+
const messageLimit = resolvePositive(input.count, this.getAbPropNumber('group_history_message_count_limit'), 'count');
|
|
163
|
+
if (!this.isGroupHistorySendEnabled()) {
|
|
164
|
+
throw new Error('shareGroupHistory is disabled for this account (group_history_send is off)');
|
|
165
|
+
}
|
|
166
|
+
const audience = await this.messageDispatch.resolveGroupHistoryAudience(normalizedGroupJid, input.toJids);
|
|
167
|
+
if (audience.requestedSelf) {
|
|
168
|
+
throw new Error('shareGroupHistory cannot share history with this account itself');
|
|
169
|
+
}
|
|
170
|
+
if (audience.unknownJids.length > 0) {
|
|
171
|
+
throw new Error(`shareGroupHistory recipients are not members of the group (addressing mode: ${audience.addressingMode}): ${audience.unknownJids.join(', ')}`);
|
|
172
|
+
}
|
|
173
|
+
if (audience.historyReceivers.length === 0) {
|
|
174
|
+
throw new Error('shareGroupHistory resolved no recipients');
|
|
175
|
+
}
|
|
176
|
+
const messages = input.messages ??
|
|
177
|
+
(await this.loadGroupHistoryMessages(normalizedGroupJid, messageLimit, input.sinceMs));
|
|
178
|
+
if (messages.length === 0) {
|
|
179
|
+
throw new Error('shareGroupHistory found no messages to share');
|
|
180
|
+
}
|
|
181
|
+
const { compressed } = await encodeGroupHistoryBundle(messages, input.outOfWindowPinnedMessages);
|
|
182
|
+
const upload = await uploadMedia(this.mediaUploadOptions, {
|
|
183
|
+
source: compressed,
|
|
184
|
+
cryptoType: 'group-history',
|
|
185
|
+
uploadPath: MEDIA_UPLOAD_PATHS['group-history'],
|
|
186
|
+
contentType: GROUP_HISTORY_BUNDLE_MIMETYPE,
|
|
187
|
+
sidecar: false,
|
|
188
|
+
logLabel: 'group history bundle upload'
|
|
189
|
+
});
|
|
190
|
+
const oldestInWindow = oldestTimestampSeconds(messages);
|
|
191
|
+
const oldestPin = oldestTimestampSeconds(input.outOfWindowPinnedMessages ?? []);
|
|
192
|
+
const oldestInBundle = oldestPin === undefined
|
|
193
|
+
? oldestInWindow
|
|
194
|
+
: oldestInWindow === undefined
|
|
195
|
+
? oldestPin
|
|
196
|
+
: Math.min(oldestInWindow, oldestPin);
|
|
197
|
+
const metadata = {
|
|
198
|
+
historyReceivers: audience.historyReceivers,
|
|
199
|
+
nonHistoryReceivers: audience.nonHistoryReceivers,
|
|
200
|
+
messageCount: messages.length,
|
|
201
|
+
oldestMessageTimestampInWindow: oldestInWindow,
|
|
202
|
+
oldestMessageTimestampInBundle: oldestInBundle
|
|
203
|
+
};
|
|
204
|
+
const bundleResult = await this.send(normalizedGroupJid, {
|
|
205
|
+
messageHistoryBundle: {
|
|
206
|
+
mimetype: GROUP_HISTORY_BUNDLE_MIMETYPE,
|
|
207
|
+
fileSha256: upload.fileSha256,
|
|
208
|
+
fileEncSha256: upload.fileEncSha256,
|
|
209
|
+
mediaKey: upload.mediaKey,
|
|
210
|
+
directPath: upload.directPath,
|
|
211
|
+
mediaKeyTimestamp: this.mediaUploadOptions.serverClock.nowSeconds(),
|
|
212
|
+
messageHistoryMetadata: metadata
|
|
213
|
+
}
|
|
214
|
+
});
|
|
215
|
+
let noticeMessageId;
|
|
216
|
+
try {
|
|
217
|
+
noticeMessageId = (await this.send(normalizedGroupJid, {
|
|
218
|
+
messageHistoryNotice: { messageHistoryMetadata: metadata }
|
|
219
|
+
})).id;
|
|
220
|
+
}
|
|
221
|
+
catch (error) {
|
|
222
|
+
this.logger.warn('group history notice failed after the bundle was delivered', {
|
|
223
|
+
groupJid: normalizedGroupJid,
|
|
224
|
+
id: bundleResult.id,
|
|
225
|
+
receiverCount: audience.historyReceivers.length,
|
|
226
|
+
message: toError(error).message
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
this.logger.debug('shared group history', {
|
|
230
|
+
groupJid: normalizedGroupJid,
|
|
231
|
+
id: bundleResult.id,
|
|
232
|
+
messagesCount: messages.length,
|
|
233
|
+
receiverCount: audience.historyReceivers.length
|
|
234
|
+
});
|
|
235
|
+
return {
|
|
236
|
+
bundleMessageId: bundleResult.id,
|
|
237
|
+
noticeMessageId,
|
|
238
|
+
messagesCount: messages.length,
|
|
239
|
+
historyReceivers: audience.historyReceivers,
|
|
240
|
+
nonHistoryReceivers: audience.nonHistoryReceivers
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* Reads the most recent messages of a group back out of the mailbox store
|
|
245
|
+
* and rebuilds the `WebMessageInfo` shape a bundle carries. Yields nothing
|
|
246
|
+
* when the `messages` store domain is `'none'`.
|
|
247
|
+
*/
|
|
248
|
+
async loadGroupHistoryMessages(groupJid, limit, sinceMs) {
|
|
249
|
+
const records = await this.messageStore.listByThread(groupJid, limit);
|
|
250
|
+
const messages = [];
|
|
251
|
+
for (let index = 0; index < records.length; index += 1) {
|
|
252
|
+
const record = records[index];
|
|
253
|
+
if (!record.messageBytes) {
|
|
254
|
+
continue;
|
|
255
|
+
}
|
|
256
|
+
if (sinceMs !== undefined && (record.timestampMs ?? 0) < sinceMs) {
|
|
257
|
+
continue;
|
|
258
|
+
}
|
|
259
|
+
messages[messages.length] = {
|
|
260
|
+
key: {
|
|
261
|
+
id: record.id,
|
|
262
|
+
remoteJid: groupJid,
|
|
263
|
+
fromMe: record.fromMe,
|
|
264
|
+
participant: record.participantJid ?? record.senderJid
|
|
265
|
+
},
|
|
266
|
+
message: proto.Message.decode(record.messageBytes),
|
|
267
|
+
messageTimestamp: Math.floor((record.timestampMs ?? 0) / 1000)
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
return messages;
|
|
271
|
+
}
|
|
117
272
|
/**
|
|
118
273
|
* Fetches the server-side "reachout" timelock that throttles cold outreach
|
|
119
274
|
* to non-contacts, returning the active window when enforcement is on.
|