zapo-js 1.6.2 → 1.7.0
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/dist/abprops-spec.d.ts +10 -0
- package/dist/abprops-spec.js +25 -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 +50 -12
- 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 +217 -30
- 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/newsletter/content.d.ts +4 -1
- package/dist/client/newsletter/content.js +43 -18
- 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 +2 -0
- package/dist/client/persistence/history-sync.js +31 -29
- package/dist/client/types.d.ts +150 -16
- package/dist/crypto/core/xeddsa.js +16 -0
- package/dist/crypto/curves/X25519.js +13 -0
- 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/client/WaClient.js +60 -0
- package/dist/esm/client/WaClientFactory.js +51 -13
- 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 +218 -32
- 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/newsletter/content.js +44 -19
- 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 +24 -22
- package/dist/esm/crypto/core/xeddsa.js +17 -1
- package/dist/esm/crypto/curves/X25519.js +13 -0
- package/dist/esm/crypto/nativeBackend.js +93 -0
- package/dist/esm/index.js +3 -1
- package/dist/esm/message/context-info.js +39 -0
- package/dist/esm/message/encode/content.js +20 -4
- 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 +18 -6
- package/dist/esm/protocol/abprops.js +18 -162
- package/dist/esm/protocol/constants.js +4 -3
- package/dist/esm/protocol/defaults.js +6 -0
- 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/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/node/builders/privacy.js +63 -4
- package/dist/esm/util/coercion.js +12 -4
- package/dist/esm/util/index.js +1 -1
- package/dist/index.d.ts +11 -6
- package/dist/index.js +15 -2
- 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/encode/content.d.ts +7 -3
- package/dist/message/encode/content.js +20 -4
- 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 +18 -6
- 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 +6 -0
- package/dist/protocol/defaults.js +6 -0
- 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/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/node/builders/privacy.d.ts +26 -2
- package/dist/transport/node/builders/privacy.js +63 -3
- 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/package.json +3 -1
- package/spec/abprops/index.d.ts +4402 -0
- package/spec/abprops/index.js +4372 -0
|
@@ -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.
|
|
@@ -17,11 +17,13 @@ import type { SenderKeyManager } from '../../signal/group/SenderKeyManager';
|
|
|
17
17
|
import type { SignalSessionResolver } from '../../signal/session/resolver';
|
|
18
18
|
import type { SignalProtocol } from '../../signal/session/SignalProtocol';
|
|
19
19
|
import type { SignalAddress } from '../../signal/types';
|
|
20
|
+
import type { WaChatMetadataStore } from '../../store/contracts/chat-metadata.store';
|
|
20
21
|
import type { WaDeviceListStore } from '../../store/contracts/device-list.store';
|
|
21
22
|
import type { WaIdentityStore } from '../../store/contracts/identity.store';
|
|
22
23
|
import type { WaMessageSecretStore } from '../../store/contracts/message-secret.store';
|
|
23
24
|
import type { WaSessionStore } from '../../store/contracts/session.store';
|
|
24
25
|
import type { WaSignalStore } from '../../store/contracts/signal.store';
|
|
26
|
+
import type { WaThreadStore } from '../../store/contracts/thread.store';
|
|
25
27
|
import type { BinaryNode } from '../../transport/types';
|
|
26
28
|
import type { ServerClock } from '../../util/clock';
|
|
27
29
|
interface WaMessageDispatchCoordinatorOptions {
|
|
@@ -45,6 +47,8 @@ interface WaMessageDispatchCoordinatorOptions {
|
|
|
45
47
|
readonly sessionStore: WaSessionStore;
|
|
46
48
|
readonly identityStore: WaIdentityStore;
|
|
47
49
|
readonly deviceListStore: WaDeviceListStore;
|
|
50
|
+
readonly threadStore: WaThreadStore;
|
|
51
|
+
readonly chatMetadataStore: WaChatMetadataStore;
|
|
48
52
|
readonly signalDeviceSync: SignalDeviceSyncApi;
|
|
49
53
|
readonly messageSecretStore: WaMessageSecretStore;
|
|
50
54
|
/**
|
|
@@ -66,6 +70,32 @@ interface WaMessageDispatchCoordinatorOptions {
|
|
|
66
70
|
readonly mobileMessageIdFormat?: () => boolean;
|
|
67
71
|
readonly serverClock: ServerClock;
|
|
68
72
|
}
|
|
73
|
+
/** Membership split behind a group-history share, in the group's addressing mode. */
|
|
74
|
+
export interface WaGroupHistoryAudience {
|
|
75
|
+
/** Members the bundle is addressed to. */
|
|
76
|
+
readonly historyReceivers: readonly string[];
|
|
77
|
+
/** Members in the group who are not receiving it. */
|
|
78
|
+
readonly nonHistoryReceivers: readonly string[];
|
|
79
|
+
/** Requested JIDs that are not current members of the group. */
|
|
80
|
+
readonly unknownJids: readonly string[];
|
|
81
|
+
/** Whether the caller asked to share with this account itself. */
|
|
82
|
+
readonly requestedSelf: boolean;
|
|
83
|
+
readonly addressingMode: 'pn' | 'lid';
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Narrows a group's participant list to the members a group-history bundle is
|
|
87
|
+
* addressed to, plus the sender so its own devices keep a copy. Receivers are
|
|
88
|
+
* intersected with the live participant list, so a stale receiver entry cannot
|
|
89
|
+
* pull a non-member into the fanout. Any other message keeps the full list.
|
|
90
|
+
*
|
|
91
|
+
* Receivers must be written in the group's addressing mode - a PN entry for a
|
|
92
|
+
* LID-addressed group does not match and is reported through `onDropped`.
|
|
93
|
+
*
|
|
94
|
+
* @throws when no receiver matches a current member. Sending to the sender's
|
|
95
|
+
* own devices alone counts as no match: it would look like a delivered share
|
|
96
|
+
* while nobody received anything.
|
|
97
|
+
*/
|
|
98
|
+
export declare function restrictGroupHistoryTargets(message: Proto.IMessage, participantUserJids: readonly string[], senderJid: string, onDropped?: (droppedJids: readonly string[], totalExpected: number) => void): readonly string[];
|
|
69
99
|
export declare class WaMessageDispatchCoordinator {
|
|
70
100
|
private readonly deps;
|
|
71
101
|
private readonly mobileMessageIdFormat;
|
|
@@ -74,6 +104,24 @@ export declare class WaMessageDispatchCoordinator {
|
|
|
74
104
|
private readonly privacyTokenDedup;
|
|
75
105
|
private readonly distributionDedup;
|
|
76
106
|
constructor(options: WaMessageDispatchCoordinatorOptions);
|
|
107
|
+
/**
|
|
108
|
+
* Per-chat disappearing settings from the cache, falling back to the
|
|
109
|
+
* durable thread record on a cold miss and warming the cache with it. A
|
|
110
|
+
* chat with no disappearing mode is cached as expiration `0` rather than
|
|
111
|
+
* left absent, so the archive is not re-read on every send to it; the
|
|
112
|
+
* incoming writers overwrite that entry the moment the chat turns it on.
|
|
113
|
+
* A failing archive degrades the send to "no ephemeral fields" instead of
|
|
114
|
+
* rejecting it - the store is opt-in persistence, not a send dependency.
|
|
115
|
+
*/
|
|
116
|
+
private resolveChatMetadata;
|
|
117
|
+
/**
|
|
118
|
+
* Disappearing-message fields for a send into a chat with the mode on, or
|
|
119
|
+
* `{}` when nothing resolves. Both chat kinds report initiator
|
|
120
|
+
* `CHANGED_IN_CHAT` when nothing more specific is stored; the trigger comes
|
|
121
|
+
* from the group metadata for a group and defaults to `CHAT_SETTING` for
|
|
122
|
+
* 1:1, and is omitted when a group reports none.
|
|
123
|
+
*/
|
|
124
|
+
private resolveChatEphemeral;
|
|
77
125
|
publishMessageNode(node: BinaryNode, options?: WaMessagePublishOptions): Promise<WaMessagePublishResult>;
|
|
78
126
|
publishEncryptedMessage(input: WaEncryptedMessageInput, options?: WaMessagePublishOptions): Promise<WaMessagePublishResult>;
|
|
79
127
|
publishSignalMessage(input: WaSignalMessagePublishInput, options?: WaMessagePublishOptions): Promise<WaMessagePublishResult>;
|
|
@@ -119,6 +167,22 @@ export declare class WaMessageDispatchCoordinator {
|
|
|
119
167
|
requestAppStateSyncKeys(keyIds: readonly Uint8Array[]): Promise<readonly string[]>;
|
|
120
168
|
sendAppStateSyncKeyShare(toDeviceJid: string, keys: readonly WaAppStateSyncKey[], missingKeyIds?: readonly Uint8Array[]): Promise<void>;
|
|
121
169
|
mutateGroupMetadataCacheFromGroupEvent(event: WaGroupEvent): Promise<void>;
|
|
170
|
+
/**
|
|
171
|
+
* Splits a group's members into the ones a history bundle is being shared
|
|
172
|
+
* with and the rest, normalizing everything to the group's addressing mode
|
|
173
|
+
* so the receiver list written into the proto matches what the fanout can
|
|
174
|
+
* actually resolve. This account is excluded from both lists.
|
|
175
|
+
*
|
|
176
|
+
* Requested JIDs that are not current members come back in `unknownJids`
|
|
177
|
+
* instead of being silently dropped - sharing history with a non-member is
|
|
178
|
+
* a caller mistake worth surfacing.
|
|
179
|
+
*/
|
|
180
|
+
resolveGroupHistoryAudience(groupJid: string, requestedJids: readonly string[]): Promise<WaGroupHistoryAudience>;
|
|
181
|
+
/**
|
|
182
|
+
* A group-history bundle goes to the members named in `historyReceivers`
|
|
183
|
+
* rather than to the whole group, so it takes the per-device direct fanout
|
|
184
|
+
* instead of the group sender key.
|
|
185
|
+
*/
|
|
122
186
|
private shouldUseGroupDirectPath;
|
|
123
187
|
private publishGroupDirectMessage;
|
|
124
188
|
private buildBotSidecarParticipant;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.WaMessageDispatchCoordinator = void 0;
|
|
4
|
+
exports.restrictGroupHistoryTargets = restrictGroupHistoryTargets;
|
|
4
5
|
const _crypto_1 = require("../../crypto");
|
|
5
6
|
const primitives_1 = require("../../crypto/core/primitives");
|
|
6
7
|
const PromiseDedup_1 = require("../../infra/perf/PromiseDedup");
|
|
@@ -20,6 +21,58 @@ const binary_1 = require("../../transport/binary");
|
|
|
20
21
|
const message_1 = require("../../transport/node/builders/message");
|
|
21
22
|
const bytes_1 = require("../../util/bytes");
|
|
22
23
|
const primitives_2 = require("../../util/primitives");
|
|
24
|
+
/**
|
|
25
|
+
* Members a group-history bundle is addressed to. Empty for every other
|
|
26
|
+
* message, which is what keeps the restricted fanout scoped to bundles.
|
|
27
|
+
*/
|
|
28
|
+
function resolveGroupHistoryReceivers(message) {
|
|
29
|
+
const metadata = (0, content_1.unwrapMessage)(message).messageHistoryBundle?.messageHistoryMetadata;
|
|
30
|
+
return metadata?.historyReceivers ?? [];
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Narrows a group's participant list to the members a group-history bundle is
|
|
34
|
+
* addressed to, plus the sender so its own devices keep a copy. Receivers are
|
|
35
|
+
* intersected with the live participant list, so a stale receiver entry cannot
|
|
36
|
+
* pull a non-member into the fanout. Any other message keeps the full list.
|
|
37
|
+
*
|
|
38
|
+
* Receivers must be written in the group's addressing mode - a PN entry for a
|
|
39
|
+
* LID-addressed group does not match and is reported through `onDropped`.
|
|
40
|
+
*
|
|
41
|
+
* @throws when no receiver matches a current member. Sending to the sender's
|
|
42
|
+
* own devices alone counts as no match: it would look like a delivered share
|
|
43
|
+
* while nobody received anything.
|
|
44
|
+
*/
|
|
45
|
+
function restrictGroupHistoryTargets(message, participantUserJids, senderJid, onDropped) {
|
|
46
|
+
const receivers = resolveGroupHistoryReceivers(message);
|
|
47
|
+
if (receivers.length === 0) {
|
|
48
|
+
return participantUserJids;
|
|
49
|
+
}
|
|
50
|
+
const pending = new Set();
|
|
51
|
+
for (let index = 0; index < receivers.length; index += 1) {
|
|
52
|
+
pending.add((0, jid_1.toUserJid)(receivers[index]));
|
|
53
|
+
}
|
|
54
|
+
const senderUserJid = (0, jid_1.toUserJid)(senderJid);
|
|
55
|
+
const targets = [];
|
|
56
|
+
let matchedReceivers = 0;
|
|
57
|
+
for (let index = 0; index < participantUserJids.length; index += 1) {
|
|
58
|
+
const participant = participantUserJids[index];
|
|
59
|
+
const participantUserJid = (0, jid_1.toUserJid)(participant);
|
|
60
|
+
if (pending.delete(participantUserJid)) {
|
|
61
|
+
matchedReceivers += 1;
|
|
62
|
+
targets[targets.length] = participant;
|
|
63
|
+
}
|
|
64
|
+
else if (participantUserJid === senderUserJid) {
|
|
65
|
+
targets[targets.length] = participant;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
if (pending.size > 0 && onDropped) {
|
|
69
|
+
onDropped([...pending], receivers.length);
|
|
70
|
+
}
|
|
71
|
+
if (matchedReceivers === 0) {
|
|
72
|
+
throw new Error('group history bundle resolved no receivers in the group');
|
|
73
|
+
}
|
|
74
|
+
return targets;
|
|
75
|
+
}
|
|
23
76
|
class WaMessageDispatchCoordinator {
|
|
24
77
|
constructor(options) {
|
|
25
78
|
this.icdcDedup = new PromiseDedup_1.PromiseDedup();
|
|
@@ -29,6 +82,82 @@ class WaMessageDispatchCoordinator {
|
|
|
29
82
|
this.mobileMessageIdFormat = options.mobileMessageIdFormat ?? (() => false);
|
|
30
83
|
this.serverClock = options.serverClock;
|
|
31
84
|
}
|
|
85
|
+
/**
|
|
86
|
+
* Per-chat disappearing settings from the cache, falling back to the
|
|
87
|
+
* durable thread record on a cold miss and warming the cache with it. A
|
|
88
|
+
* chat with no disappearing mode is cached as expiration `0` rather than
|
|
89
|
+
* left absent, so the archive is not re-read on every send to it; the
|
|
90
|
+
* incoming writers overwrite that entry the moment the chat turns it on.
|
|
91
|
+
* A failing archive degrades the send to "no ephemeral fields" instead of
|
|
92
|
+
* rejecting it - the store is opt-in persistence, not a send dependency.
|
|
93
|
+
*/
|
|
94
|
+
async resolveChatMetadata(chatJid) {
|
|
95
|
+
try {
|
|
96
|
+
const cached = await this.deps.chatMetadataStore.getChatMetadata(chatJid);
|
|
97
|
+
if (cached) {
|
|
98
|
+
return cached;
|
|
99
|
+
}
|
|
100
|
+
const thread = await this.deps.threadStore.getByJid(chatJid);
|
|
101
|
+
const expiration = thread?.ephemeralExpiration ?? 0;
|
|
102
|
+
const settingTimestamp = expiration > 0 ? thread?.ephemeralSettingTimestamp : undefined;
|
|
103
|
+
const snapshot = {
|
|
104
|
+
chatJid,
|
|
105
|
+
ephemeralExpiration: expiration,
|
|
106
|
+
...(settingTimestamp !== undefined
|
|
107
|
+
? { ephemeralSettingTimestamp: settingTimestamp }
|
|
108
|
+
: {}),
|
|
109
|
+
updatedAtMs: Date.now()
|
|
110
|
+
};
|
|
111
|
+
await this.deps.chatMetadataStore.upsertChatMetadata(snapshot);
|
|
112
|
+
return snapshot;
|
|
113
|
+
}
|
|
114
|
+
catch (error) {
|
|
115
|
+
this.deps.logger.debug('chat metadata lookup failed, sending without ephemeral fields', {
|
|
116
|
+
jid: chatJid,
|
|
117
|
+
message: (0, primitives_2.toError)(error).message
|
|
118
|
+
});
|
|
119
|
+
return null;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Disappearing-message fields for a send into a chat with the mode on, or
|
|
124
|
+
* `{}` when nothing resolves. Both chat kinds report initiator
|
|
125
|
+
* `CHANGED_IN_CHAT` when nothing more specific is stored; the trigger comes
|
|
126
|
+
* from the group metadata for a group and defaults to `CHAT_SETTING` for
|
|
127
|
+
* 1:1, and is omitted when a group reports none.
|
|
128
|
+
*/
|
|
129
|
+
async resolveChatEphemeral(recipientJid) {
|
|
130
|
+
if ((0, jid_1.isGroupJid)(recipientJid)) {
|
|
131
|
+
const settings = await this.deps.groupMetadataCache.resolveEphemeralSettings(recipientJid);
|
|
132
|
+
if (!settings) {
|
|
133
|
+
return {};
|
|
134
|
+
}
|
|
135
|
+
return {
|
|
136
|
+
expirationSeconds: settings.expirationSeconds,
|
|
137
|
+
disappearingModeInitiator: _proto_1.proto.DisappearingMode.Initiator.CHANGED_IN_CHAT,
|
|
138
|
+
...(settings.trigger !== undefined
|
|
139
|
+
? {
|
|
140
|
+
disappearingModeTrigger: settings.trigger
|
|
141
|
+
}
|
|
142
|
+
: {})
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
const chat = await this.resolveChatMetadata(recipientJid);
|
|
146
|
+
const expiration = chat?.ephemeralExpiration;
|
|
147
|
+
if (expiration === undefined || expiration <= 0) {
|
|
148
|
+
return {};
|
|
149
|
+
}
|
|
150
|
+
const stored = chat?.ephemeralSettingTimestamp;
|
|
151
|
+
const settingTimestamp = stored !== undefined ? (0, constants_1.normalizeEphemeralSettingSeconds)(stored) : undefined;
|
|
152
|
+
return {
|
|
153
|
+
expirationSeconds: expiration,
|
|
154
|
+
disappearingModeInitiator: _proto_1.proto.DisappearingMode.Initiator.CHANGED_IN_CHAT,
|
|
155
|
+
disappearingModeTrigger: _proto_1.proto.DisappearingMode.Trigger.CHAT_SETTING,
|
|
156
|
+
...(settingTimestamp !== undefined
|
|
157
|
+
? { ephemeralSettingTimestamp: settingTimestamp }
|
|
158
|
+
: {})
|
|
159
|
+
};
|
|
160
|
+
}
|
|
32
161
|
async publishMessageNode(node, options = {}) {
|
|
33
162
|
this.deps.logger.trace('wa client publish message node', {
|
|
34
163
|
tag: node.tag,
|
|
@@ -149,17 +278,34 @@ class WaMessageDispatchCoordinator {
|
|
|
149
278
|
this.withResolvedMessageId(options)
|
|
150
279
|
]);
|
|
151
280
|
}
|
|
281
|
+
const isGroupRecipient = (0, jid_1.isGroupJid)(recipientJid);
|
|
282
|
+
const directRecipientJid = isGroupRecipient
|
|
283
|
+
? recipientJid
|
|
284
|
+
: await this.resolveDirectRecipientLid((0, jid_1.toUserJid)(recipientJid));
|
|
152
285
|
let optionsCtx = options.contextInfo;
|
|
153
286
|
if (options.expirationSeconds !== undefined) {
|
|
154
287
|
optionsCtx = { ...optionsCtx, expirationSeconds: options.expirationSeconds };
|
|
155
288
|
}
|
|
156
|
-
if (
|
|
157
|
-
optionsCtx
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
289
|
+
if (options.ephemeralSettingTimestamp !== undefined) {
|
|
290
|
+
optionsCtx = {
|
|
291
|
+
...optionsCtx,
|
|
292
|
+
ephemeralSettingTimestamp: options.ephemeralSettingTimestamp
|
|
293
|
+
};
|
|
294
|
+
}
|
|
295
|
+
if (options.disappearingModeTrigger !== undefined) {
|
|
296
|
+
optionsCtx = {
|
|
297
|
+
...optionsCtx,
|
|
298
|
+
disappearingModeTrigger: options.disappearingModeTrigger
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
const autoInjectDisabled = isGroupRecipient
|
|
302
|
+
? options.disableGroupEphemeralAutoInject
|
|
303
|
+
: options.disableDirectEphemeralAutoInject;
|
|
304
|
+
if (optionsCtx?.expirationSeconds === undefined && !autoInjectDisabled) {
|
|
305
|
+
optionsCtx = {
|
|
306
|
+
...(await this.resolveChatEphemeral(directRecipientJid)),
|
|
307
|
+
...optionsCtx
|
|
308
|
+
};
|
|
163
309
|
}
|
|
164
310
|
const ctx = (0, context_info_1.resolveSendContextInfo)({
|
|
165
311
|
contentLevel: pickContentContextInfo(content),
|
|
@@ -285,9 +431,6 @@ class WaMessageDispatchCoordinator {
|
|
|
285
431
|
customNodes: customNodes.length > 0 ? customNodes : undefined,
|
|
286
432
|
sendOptions
|
|
287
433
|
};
|
|
288
|
-
const directRecipientJid = isGroup
|
|
289
|
-
? recipientJid
|
|
290
|
-
: await this.resolveDirectRecipientLid((0, jid_1.toUserJid)(recipientJid));
|
|
291
434
|
const peerRecipientPn = isGroup
|
|
292
435
|
? undefined
|
|
293
436
|
: await this.resolvePeerRecipientPn((0, jid_1.toUserJid)(recipientJid), directRecipientJid);
|
|
@@ -550,9 +693,59 @@ class WaMessageDispatchCoordinator {
|
|
|
550
693
|
async mutateGroupMetadataCacheFromGroupEvent(event) {
|
|
551
694
|
await this.deps.groupMetadataCache.mutateFromGroupEvent(event);
|
|
552
695
|
}
|
|
553
|
-
|
|
696
|
+
/**
|
|
697
|
+
* Splits a group's members into the ones a history bundle is being shared
|
|
698
|
+
* with and the rest, normalizing everything to the group's addressing mode
|
|
699
|
+
* so the receiver list written into the proto matches what the fanout can
|
|
700
|
+
* actually resolve. This account is excluded from both lists.
|
|
701
|
+
*
|
|
702
|
+
* Requested JIDs that are not current members come back in `unknownJids`
|
|
703
|
+
* instead of being silently dropped - sharing history with a non-member is
|
|
704
|
+
* a caller mistake worth surfacing.
|
|
705
|
+
*/
|
|
706
|
+
async resolveGroupHistoryAudience(groupJid, requestedJids) {
|
|
707
|
+
const participants = await this.deps.groupMetadataCache.resolveParticipantUsers(groupJid);
|
|
708
|
+
const meJid = this.requireCurrentMeJid('shareGroupHistory');
|
|
709
|
+
const addressingMode = this.resolveGroupAddressingMode(participants, groupJid);
|
|
710
|
+
const meUserJid = (0, jid_1.toUserJid)(this.resolveSenderForAddressingMode(addressingMode, meJid));
|
|
711
|
+
const pending = new Set();
|
|
712
|
+
for (let index = 0; index < requestedJids.length; index += 1) {
|
|
713
|
+
pending.add((0, jid_1.toUserJid)((0, jid_1.normalizeRecipientJid)(requestedJids[index])));
|
|
714
|
+
}
|
|
715
|
+
const historyReceivers = [];
|
|
716
|
+
const nonHistoryReceivers = [];
|
|
717
|
+
for (let index = 0; index < participants.length; index += 1) {
|
|
718
|
+
const participantUserJid = (0, jid_1.toUserJid)(participants[index]);
|
|
719
|
+
if (participantUserJid === meUserJid) {
|
|
720
|
+
continue;
|
|
721
|
+
}
|
|
722
|
+
if (pending.delete(participantUserJid)) {
|
|
723
|
+
historyReceivers[historyReceivers.length] = participantUserJid;
|
|
724
|
+
}
|
|
725
|
+
else {
|
|
726
|
+
nonHistoryReceivers[nonHistoryReceivers.length] = participantUserJid;
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
const requestedSelf = pending.delete(meUserJid);
|
|
730
|
+
const unknownJids = [];
|
|
731
|
+
for (const jid of pending) {
|
|
732
|
+
unknownJids[unknownJids.length] = jid;
|
|
733
|
+
}
|
|
734
|
+
return {
|
|
735
|
+
historyReceivers,
|
|
736
|
+
nonHistoryReceivers,
|
|
737
|
+
unknownJids,
|
|
738
|
+
requestedSelf,
|
|
739
|
+
addressingMode
|
|
740
|
+
};
|
|
741
|
+
}
|
|
742
|
+
/**
|
|
743
|
+
* A group-history bundle goes to the members named in `historyReceivers`
|
|
744
|
+
* rather than to the whole group, so it takes the per-device direct fanout
|
|
745
|
+
* instead of the group sender key.
|
|
746
|
+
*/
|
|
554
747
|
shouldUseGroupDirectPath(message) {
|
|
555
|
-
return
|
|
748
|
+
return resolveGroupHistoryReceivers(message).length > 0;
|
|
556
749
|
}
|
|
557
750
|
async publishGroupDirectMessage(groupJid, envelope, retryContext = {}) {
|
|
558
751
|
const { message, plaintext, type, edit, mediatype, sendOptions } = envelope;
|
|
@@ -562,8 +755,16 @@ class WaMessageDispatchCoordinator {
|
|
|
562
755
|
: await this.deps.groupMetadataCache.resolveParticipantUsers(groupJid);
|
|
563
756
|
const addressingMode = retryContext.forceAddressingMode ??
|
|
564
757
|
this.resolveGroupAddressingMode(participantUserJids, groupJid);
|
|
565
|
-
const
|
|
566
|
-
const
|
|
758
|
+
const senderJid = this.resolveSenderForAddressingMode(addressingMode, meJid);
|
|
759
|
+
const targetUserJids = restrictGroupHistoryTargets(message, participantUserJids, senderJid, (droppedJids, totalExpected) => {
|
|
760
|
+
this.deps.logger.warn('group history receivers missing from the participant list', {
|
|
761
|
+
groupJid,
|
|
762
|
+
droppedCount: droppedJids.length,
|
|
763
|
+
totalExpected,
|
|
764
|
+
sample: droppedJids.slice(0, 3)
|
|
765
|
+
});
|
|
766
|
+
});
|
|
767
|
+
const fanoutDeviceJids = await this.deps.fanoutResolver.resolveGroupParticipantDeviceJids(targetUserJids);
|
|
567
768
|
if (fanoutDeviceJids.length === 0) {
|
|
568
769
|
throw new Error('group direct send resolved no target devices');
|
|
569
770
|
}
|
|
@@ -618,22 +819,10 @@ class WaMessageDispatchCoordinator {
|
|
|
618
819
|
break;
|
|
619
820
|
}
|
|
620
821
|
}
|
|
621
|
-
const phashTargets = new Array(resolvedFanoutTargets.length + 1);
|
|
622
|
-
let phashTargetCount = 0;
|
|
623
|
-
for (let index = 0; index < resolvedFanoutTargets.length; index += 1) {
|
|
624
|
-
const candidate = resolvedFanoutTargets[index].jid;
|
|
625
|
-
if ((0, jid_1.isHostedDeviceJid)(candidate))
|
|
626
|
-
continue;
|
|
627
|
-
phashTargets[phashTargetCount] = candidate;
|
|
628
|
-
phashTargetCount += 1;
|
|
629
|
-
}
|
|
630
|
-
phashTargets[phashTargetCount] = senderForPhash;
|
|
631
|
-
phashTargets.length = phashTargetCount + 1;
|
|
632
|
-
const localPhash = (0, phash_1.computePhashV2)(phashTargets);
|
|
633
822
|
const reportingArtifacts = await this.tryBuildReportingTokenArtifacts({
|
|
634
823
|
message,
|
|
635
824
|
stanzaId: sendOptions.id,
|
|
636
|
-
senderUserJid: (0, jid_1.toUserJid)(
|
|
825
|
+
senderUserJid: (0, jid_1.toUserJid)(senderJid),
|
|
637
826
|
remoteJid: groupJid,
|
|
638
827
|
context: 'group_direct'
|
|
639
828
|
});
|
|
@@ -645,7 +834,6 @@ class WaMessageDispatchCoordinator {
|
|
|
645
834
|
type,
|
|
646
835
|
id: sendOptions.id,
|
|
647
836
|
edit,
|
|
648
|
-
phash: localPhash,
|
|
649
837
|
addressingMode,
|
|
650
838
|
participants,
|
|
651
839
|
deviceIdentity: shouldAttachDeviceIdentity
|
|
@@ -671,7 +859,7 @@ class WaMessageDispatchCoordinator {
|
|
|
671
859
|
const ackError = result.ack.error;
|
|
672
860
|
const serverPhash = result.ack.phash;
|
|
673
861
|
const serverAddressingMode = result.ack.addressingMode;
|
|
674
|
-
const hasPhashMismatch = !!serverPhash
|
|
862
|
+
const hasPhashMismatch = !!serverPhash;
|
|
675
863
|
const hasAddressingMismatch = !!serverAddressingMode && serverAddressingMode !== addressingMode;
|
|
676
864
|
const hasAddressingError = ackError === constants_1.WA_NACK_REASONS.STALE_GROUP_ADDRESSING_MODE;
|
|
677
865
|
if (!retryContext.retried &&
|
|
@@ -679,7 +867,6 @@ class WaMessageDispatchCoordinator {
|
|
|
679
867
|
this.deps.logger.warn('group direct publish acknowledged with mismatch metadata', {
|
|
680
868
|
id: result.id,
|
|
681
869
|
groupJid,
|
|
682
|
-
localPhash,
|
|
683
870
|
serverPhash,
|
|
684
871
|
localAddressingMode: addressingMode,
|
|
685
872
|
serverAddressingMode,
|