@badzz88/baileys 8.4.7 → 8.5.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/LICENSE +21 -0
- package/README.md +1055 -337
- package/WAProto/index.js +5 -5
- package/lib/Defaults/index.js +187 -127
- package/lib/Defaults/phonenumber-mcc.json +223 -0
- package/lib/Signal/Group/ciphertext-message.js +14 -11
- package/lib/Signal/Group/group-session-builder.js +91 -29
- package/lib/Signal/Group/group_cipher.js +88 -81
- package/lib/Signal/Group/index.js +136 -12
- package/lib/Signal/Group/keyhelper.js +70 -15
- package/lib/Signal/Group/sender-chain-key.js +31 -25
- package/lib/Signal/Group/sender-key-distribution-message.js +65 -62
- package/lib/Signal/Group/sender-key-message.js +68 -65
- package/lib/Signal/Group/sender-key-name.js +43 -41
- package/lib/Signal/Group/sender-key-record.js +43 -40
- package/lib/Signal/Group/sender-key-state.js +96 -83
- package/lib/Signal/Group/sender-message-key.js +29 -25
- package/lib/Signal/libsignal.js +483 -424
- package/lib/Signal/lid-mapping.js +261 -276
- package/lib/Socket/Client/index.js +30 -3
- package/lib/Socket/Client/types.js +12 -10
- package/lib/Socket/Client/websocket.js +61 -53
- package/lib/Socket/aigroups.js +240 -0
- package/lib/Socket/business.js +422 -379
- package/lib/Socket/chats.js +1816 -1193
- package/lib/Socket/communities.js +549 -431
- package/lib/Socket/graphql.js +656 -0
- package/lib/Socket/groups.js +764 -374
- package/lib/Socket/index.js +38 -11
- package/lib/Socket/interactive-handler.js +522 -0
- package/lib/Socket/interop.js +549 -0
- package/lib/Socket/luxu.js +6 -5
- package/lib/Socket/managed-account.js +183 -0
- package/lib/Socket/messages-recv.js +2439 -1915
- package/lib/Socket/messages-send.js +1642 -1242
- package/lib/Socket/mex.js +59 -40
- package/lib/Socket/newsletter.js +812 -249
- package/lib/Socket/privacy.js +449 -0
- package/lib/Socket/registration.js +427 -0
- package/lib/Socket/socket.js +1077 -965
- package/lib/Socket/text-router.js +83 -0
- package/lib/Socket/username.js +237 -140
- package/lib/Store/index.js +36 -10
- package/lib/Store/make-cache-manager-store.js +85 -80
- package/lib/Store/make-in-memory-store.js +591 -185
- package/lib/Store/make-ordered-dictionary.js +78 -72
- package/lib/Store/object-repository.js +25 -28
- package/lib/Types/Auth.js +38 -2
- package/lib/Types/Bussines.js +2 -2
- package/lib/Types/Call.js +2 -2
- package/lib/Types/Chat.js +4 -8
- package/lib/Types/Contact.js +2 -2
- package/lib/Types/Events.js +2 -2
- package/lib/Types/GroupMetadata.js +2 -2
- package/lib/Types/Label.js +26 -24
- package/lib/Types/LabelAssociation.js +8 -6
- package/lib/Types/Message.js +95 -11
- package/lib/Types/Mex.js +112 -37
- package/lib/Types/Newsletter.js +121 -0
- package/lib/Types/Product.js +2 -2
- package/lib/Types/Signal.js +2 -2
- package/lib/Types/Socket.js +2 -3
- package/lib/Types/State.js +70 -56
- package/lib/Types/USync.js +2 -2
- package/lib/Types/index.js +55 -26
- package/lib/Utils/auth-utils.js +292 -288
- package/lib/Utils/browser-utils.js +113 -48
- package/lib/Utils/business.js +240 -224
- package/lib/Utils/chat-utils.js +1205 -869
- package/lib/Utils/command-loader.d.ts +31 -0
- package/lib/Utils/command-loader.js +100 -0
- package/lib/Utils/companion-reg-client-utils.js +41 -34
- package/lib/Utils/consumer-application.js +107 -0
- package/lib/Utils/crypto.js +144 -107
- package/lib/Utils/curve25519-js.js +275 -0
- package/lib/Utils/decode-wa-message.js +518 -308
- package/lib/Utils/event-buffer.js +596 -611
- package/lib/Utils/generics.js +514 -354
- package/lib/Utils/group-history.js +60 -0
- package/lib/Utils/history.js +244 -134
- package/lib/Utils/identity-change-handler.js +51 -49
- package/lib/Utils/index.js +57 -23
- package/lib/Utils/jid-display-normalization.js +218 -0
- package/lib/Utils/link-preview.js +135 -77
- package/lib/Utils/logger.js +9 -3
- package/lib/Utils/lt-hash.js +37 -6
- package/lib/Utils/make-mutex.js +36 -33
- package/lib/Utils/message-composer.js +439 -233
- package/lib/Utils/message-retry-manager.js +265 -260
- package/lib/Utils/messages-media.js +855 -747
- package/lib/Utils/messages.js +2528 -884
- package/lib/Utils/meta-ai-msmsg.js +262 -0
- package/lib/Utils/native-bridge.js +82 -0
- package/lib/Utils/noise-handler.js +195 -200
- package/lib/Utils/offline-node-processor.js +35 -33
- package/lib/Utils/pre-key-manager.js +103 -102
- package/lib/Utils/process-message.js +978 -560
- package/lib/Utils/reporting-utils.js +257 -253
- package/lib/Utils/session-pool.d.ts +16 -0
- package/lib/Utils/session-pool.js +94 -0
- package/lib/Utils/signal.js +218 -195
- package/lib/Utils/stanza-ack.js +31 -29
- package/lib/Utils/sticker.d.ts +17 -0
- package/lib/Utils/sticker.js +145 -0
- package/lib/Utils/sync-action-utils.js +50 -45
- package/lib/Utils/tc-token-utils.js +137 -139
- package/lib/Utils/use-multi-file-auth-state.js +109 -109
- package/lib/Utils/validate-connection.js +218 -202
- package/lib/Utils/view-once-cache.d.ts +9 -0
- package/lib/Utils/view-once-cache.js +79 -0
- package/lib/Utils/voip-rekey.js +22 -0
- package/lib/WABinary/constants.js +1302 -1299
- package/lib/WABinary/decode.js +343 -262
- package/lib/WABinary/encode.js +12 -4
- package/lib/WABinary/generic-utils.js +223 -187
- package/lib/WABinary/index.js +33 -6
- package/lib/WABinary/jid-utils.js +351 -88
- package/lib/WABinary/types.js +2 -2
- package/lib/WAM/BinaryInfo.js +12 -9
- package/lib/WAM/constants.js +39486 -22853
- package/lib/WAM/encode.js +132 -140
- package/lib/WAM/index.js +31 -4
- package/lib/WAUSync/Protocols/USyncBotProfileProtocol.js +55 -0
- package/lib/WAUSync/Protocols/USyncBusinessProtocol.js +43 -0
- package/lib/WAUSync/Protocols/USyncContactProtocol.js +53 -51
- package/lib/WAUSync/Protocols/USyncDeviceProtocol.js +56 -53
- package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.js +29 -26
- package/lib/WAUSync/Protocols/USyncFeatureProtocol.js +59 -0
- package/lib/WAUSync/Protocols/USyncLIDProtocol.js +31 -0
- package/lib/WAUSync/Protocols/USyncPictureProtocol.js +32 -0
- package/lib/WAUSync/Protocols/USyncSidelistProtocol.js +29 -0
- package/lib/WAUSync/Protocols/USyncStatusProtocol.js +38 -37
- package/lib/WAUSync/Protocols/USyncTextStatusProtocol.js +38 -0
- package/lib/WAUSync/Protocols/USyncUsernameProtocol.js +26 -24
- package/lib/WAUSync/Protocols/index.js +39 -6
- package/lib/WAUSync/USyncQuery.js +132 -97
- package/lib/WAUSync/USyncUser.js +49 -30
- package/lib/WAUSync/index.js +31 -4
- package/lib/antiban.js +4637 -0
- package/lib/index.js +48 -15
- package/package.json +55 -92
- package/lib/WAUSync/Protocols/USyncNewsletterProtocol.js +0 -263
- package/lib/WAUSync/Protocols/UsyncBotProfileProtocol.js +0 -51
- package/lib/WAUSync/Protocols/UsyncLIDProtocol.js +0 -29
|
@@ -1,1006 +1,1224 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
1
|
+
'use strict'
|
|
2
|
+
var __importDefault =
|
|
3
|
+
(this && this.__importDefault) ||
|
|
4
|
+
function (mod) {
|
|
5
|
+
return mod && mod.__esModule ? mod : { default: mod }
|
|
6
|
+
}
|
|
7
|
+
Object.defineProperty(exports, '__esModule', { value: true })
|
|
8
|
+
exports.makeMessagesSocket = void 0
|
|
9
|
+
const node_cache_1 = __importDefault(require('@cacheable/node-cache'))
|
|
10
|
+
const boom_1 = require('@hapi/boom')
|
|
11
|
+
const index_js_1 = require('../../WAProto/index.js')
|
|
12
|
+
const Defaults_1 = require('../Defaults')
|
|
13
|
+
const Utils_1 = require('../Utils')
|
|
14
|
+
const link_preview_1 = require('../Utils/link-preview')
|
|
15
|
+
const make_mutex_1 = require('../Utils/make-mutex')
|
|
16
|
+
const reporting_utils_1 = require('../Utils/reporting-utils')
|
|
17
|
+
const tc_token_utils_1 = require('../Utils/tc-token-utils')
|
|
18
|
+
const jid_display_normalization_1 = require('../Utils/jid-display-normalization')
|
|
19
|
+
const WABinary_1 = require('../WABinary')
|
|
20
|
+
const WAUSync_1 = require('../WAUSync')
|
|
21
|
+
const message_composer_1 = require('../Utils/message-composer.js')
|
|
22
|
+
const interactive_handler_1 = require('./interactive-handler.js')
|
|
23
|
+
const username_1 = require('./username')
|
|
24
|
+
const { setBotMessageSecret } = require('../Utils/decode-wa-message')
|
|
25
|
+
const makeMessagesSocket = config => {
|
|
26
|
+
const {
|
|
27
|
+
logger,
|
|
28
|
+
linkPreviewImageThumbnailWidth,
|
|
29
|
+
generateHighQualityLinkPreview,
|
|
30
|
+
options: httpRequestOptions,
|
|
31
|
+
patchMessageBeforeSending,
|
|
32
|
+
cachedGroupMetadata,
|
|
33
|
+
enableRecentMessageCache,
|
|
34
|
+
maxMsgRetryCount
|
|
35
|
+
} = config
|
|
36
|
+
const sock = (0, username_1.makeUsernameSocket)(config)
|
|
37
|
+
const {
|
|
38
|
+
ev,
|
|
39
|
+
authState,
|
|
40
|
+
messageMutex,
|
|
41
|
+
signalRepository,
|
|
42
|
+
upsertMessage,
|
|
43
|
+
query,
|
|
44
|
+
fetchPrivacySettings,
|
|
45
|
+
sendNode,
|
|
46
|
+
groupMetadata,
|
|
47
|
+
groupToggleEphemeral,
|
|
48
|
+
trustInteropContact
|
|
49
|
+
} = sock
|
|
50
|
+
const getLIDForPN = signalRepository.lidMapping.getLIDForPN.bind(signalRepository.lidMapping)
|
|
51
|
+
/**
|
|
52
|
+
* Set of tctoken storage JIDs with a fire-and-forget issuePrivacyTokens IQ in flight.
|
|
53
|
+
* Prevents duplicate IQs from rapid back-to-back sends before senderTimestamp persists.
|
|
54
|
+
*/
|
|
55
|
+
const inFlightTcTokenIssuance = new Set()
|
|
56
|
+
const userDevicesCache =
|
|
57
|
+
config.userDevicesCache ||
|
|
58
|
+
new node_cache_1.default({
|
|
59
|
+
stdTTL: Defaults_1.DEFAULT_CACHE_TTLS.USER_DEVICES, // 5 minutes
|
|
60
|
+
useClones: false
|
|
61
|
+
})
|
|
62
|
+
const peerSessionsCache = new node_cache_1.default({
|
|
63
|
+
stdTTL: Defaults_1.DEFAULT_CACHE_TTLS.USER_DEVICES,
|
|
64
|
+
useClones: false
|
|
65
|
+
})
|
|
66
|
+
// Initialize message retry manager if enabled
|
|
67
|
+
const messageRetryManager = enableRecentMessageCache
|
|
68
|
+
? new Utils_1.MessageRetryManager(logger, maxMsgRetryCount)
|
|
69
|
+
: null
|
|
70
|
+
// Prevent race conditions in Signal session encryption by user
|
|
71
|
+
const encryptionMutex = (0, make_mutex_1.makeKeyedMutex)()
|
|
72
|
+
let mediaConn
|
|
73
|
+
const refreshMediaConn = async (forceGet = false) => {
|
|
74
|
+
const media = await mediaConn
|
|
75
|
+
if (!media || forceGet || new Date().getTime() - media.fetchDate.getTime() > media.ttl * 1000) {
|
|
76
|
+
mediaConn = (async () => {
|
|
77
|
+
const result = await query({
|
|
78
|
+
tag: 'iq',
|
|
79
|
+
attrs: {
|
|
80
|
+
type: 'set',
|
|
81
|
+
xmlns: 'w:m',
|
|
82
|
+
to: WABinary_1.S_WHATSAPP_NET
|
|
83
|
+
},
|
|
84
|
+
content: [{ tag: 'media_conn', attrs: {} }]
|
|
85
|
+
})
|
|
86
|
+
const mediaConnNode = (0, WABinary_1.getBinaryNodeChild)(result, 'media_conn')
|
|
87
|
+
// TODO: explore full length of data that whatsapp provides
|
|
88
|
+
const node = {
|
|
89
|
+
hosts: (0, WABinary_1.getBinaryNodeChildren)(mediaConnNode, 'host').map(({ attrs }) => ({
|
|
90
|
+
hostname: attrs.hostname,
|
|
91
|
+
maxContentLengthBytes: +attrs.maxContentLengthBytes
|
|
92
|
+
})),
|
|
93
|
+
auth: mediaConnNode.attrs.auth,
|
|
94
|
+
ttl: +mediaConnNode.attrs.ttl,
|
|
95
|
+
fetchDate: new Date()
|
|
96
|
+
}
|
|
97
|
+
logger.debug('fetched media conn')
|
|
98
|
+
return node
|
|
99
|
+
})()
|
|
100
|
+
}
|
|
101
|
+
return mediaConn
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* generic send receipt function
|
|
105
|
+
* used for receipts of phone call, read, delivery etc.
|
|
106
|
+
* */
|
|
107
|
+
const sendReceipt = async (jid, participant, messageIds, type, sts) => {
|
|
108
|
+
if (!messageIds || messageIds.length === 0) {
|
|
109
|
+
throw new boom_1.Boom('missing ids in receipt')
|
|
110
|
+
}
|
|
111
|
+
const isInterop = (0, WABinary_1.isInteropUser)(jid)
|
|
112
|
+
const node = {
|
|
113
|
+
tag: 'receipt',
|
|
114
|
+
attrs: {
|
|
115
|
+
id: messageIds[0]
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
const isReadReceipt = type === 'read' || type === 'read-self'
|
|
119
|
+
if (isReadReceipt) {
|
|
120
|
+
node.attrs.t = (0, Utils_1.unixTimestampSeconds)().toString()
|
|
121
|
+
}
|
|
122
|
+
if (type === 'sender' && ((0, WABinary_1.isPnUser)(jid) || (0, WABinary_1.isLidUser)(jid))) {
|
|
123
|
+
node.attrs.recipient = jid
|
|
124
|
+
node.attrs.to = participant
|
|
125
|
+
} else if (isInterop && !type) {
|
|
126
|
+
// Delivery receipt to interop contact: WA sends to device-qualified JID (user:0@interop)
|
|
127
|
+
const { user } = (0, WABinary_1.jidDecode)(jid)
|
|
128
|
+
node.attrs.to = `${user}:0@interop`
|
|
129
|
+
} else {
|
|
130
|
+
node.attrs.to = jid
|
|
131
|
+
if (participant) {
|
|
132
|
+
node.attrs.participant = participant
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
if (type) {
|
|
136
|
+
node.attrs.type = type
|
|
137
|
+
}
|
|
138
|
+
// Interop bridge requires sts (microsecond stanza timestamp) from the original message
|
|
139
|
+
if (isInterop && sts) {
|
|
140
|
+
node.attrs.sts = sts
|
|
141
|
+
}
|
|
142
|
+
const remainingMessageIds = messageIds.slice(1)
|
|
143
|
+
if (remainingMessageIds.length) {
|
|
144
|
+
node.content = [
|
|
145
|
+
{
|
|
146
|
+
tag: 'list',
|
|
147
|
+
attrs: {},
|
|
148
|
+
content: remainingMessageIds.map(id => ({
|
|
149
|
+
tag: 'item',
|
|
150
|
+
attrs: { id }
|
|
151
|
+
}))
|
|
152
|
+
}
|
|
153
|
+
]
|
|
154
|
+
}
|
|
155
|
+
logger.debug({ attrs: node.attrs, messageIds }, 'sending receipt for messages')
|
|
156
|
+
await sendNode(node)
|
|
157
|
+
}
|
|
158
|
+
/** Correctly bulk send receipts to multiple chats, participants */
|
|
159
|
+
const sendReceipts = async (keys, type) => {
|
|
160
|
+
const recps = (0, Utils_1.aggregateMessageKeysNotFromMe)(keys)
|
|
161
|
+
for (const { jid, participant, messageIds, sts } of recps) {
|
|
162
|
+
await sendReceipt(jid, participant, messageIds, type, sts)
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
/** Bulk read messages. Keys can be from different chats & participants */
|
|
166
|
+
const readMessages = async keys => {
|
|
167
|
+
const privacySettings = await fetchPrivacySettings()
|
|
168
|
+
// based on privacy settings, we have to change the read type
|
|
169
|
+
const readType = privacySettings.readreceipts === 'all' ? 'read' : 'read-self'
|
|
170
|
+
await sendReceipts(keys, readType)
|
|
171
|
+
}
|
|
172
|
+
/** Fetch all the devices we've to send a message to */
|
|
173
|
+
const getUSyncDevices = async (jids, useCache, ignoreZeroDevices) => {
|
|
174
|
+
const deviceResults = []
|
|
175
|
+
if (!useCache) {
|
|
176
|
+
logger.debug('not using cache for devices')
|
|
177
|
+
}
|
|
178
|
+
const toFetch = []
|
|
179
|
+
const jidsWithUser = jids
|
|
180
|
+
.map(jid => {
|
|
181
|
+
const decoded = (0, WABinary_1.jidDecode)(jid)
|
|
182
|
+
const user = decoded?.user
|
|
183
|
+
const device = decoded?.device
|
|
184
|
+
const isExplicitDevice = typeof device === 'number' && device >= 0
|
|
185
|
+
if (isExplicitDevice && user) {
|
|
186
|
+
deviceResults.push({
|
|
187
|
+
user,
|
|
188
|
+
device,
|
|
189
|
+
jid
|
|
190
|
+
})
|
|
191
|
+
return null
|
|
192
|
+
}
|
|
193
|
+
jid = (0, WABinary_1.jidNormalizedUser)(jid)
|
|
194
|
+
return { jid, user }
|
|
195
|
+
})
|
|
196
|
+
.filter(jid => jid !== null)
|
|
197
|
+
let mgetDevices
|
|
198
|
+
if (useCache && userDevicesCache.mget) {
|
|
199
|
+
const usersToFetch = jidsWithUser.map(j => j?.user).filter(Boolean)
|
|
200
|
+
mgetDevices = await userDevicesCache.mget(usersToFetch)
|
|
201
|
+
}
|
|
202
|
+
for (const { jid, user } of jidsWithUser) {
|
|
203
|
+
if (useCache) {
|
|
204
|
+
const devices = mgetDevices?.[user] || (userDevicesCache.mget ? undefined : await userDevicesCache.get(user))
|
|
205
|
+
if (devices) {
|
|
206
|
+
const devicesWithJid = devices.map(d => ({
|
|
207
|
+
...d,
|
|
208
|
+
jid: (0, WABinary_1.jidEncode)(d.user, d.server, d.device)
|
|
209
|
+
}))
|
|
210
|
+
deviceResults.push(...devicesWithJid)
|
|
211
|
+
logger.trace({ user }, 'using cache for devices')
|
|
212
|
+
} else {
|
|
213
|
+
toFetch.push(jid)
|
|
214
|
+
}
|
|
215
|
+
} else {
|
|
216
|
+
toFetch.push(jid)
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
if (!toFetch.length) {
|
|
220
|
+
return deviceResults
|
|
221
|
+
}
|
|
222
|
+
const requestedLidUsers = new Set()
|
|
223
|
+
for (const jid of toFetch) {
|
|
224
|
+
if ((0, WABinary_1.isLidUser)(jid) || (0, WABinary_1.isHostedLidUser)(jid)) {
|
|
225
|
+
const user = (0, WABinary_1.jidDecode)(jid)?.user
|
|
226
|
+
if (user) requestedLidUsers.add(user)
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
const query = new WAUSync_1.USyncQuery().withContext('message').withDeviceProtocol().withLIDProtocol()
|
|
230
|
+
for (const jid of toFetch) {
|
|
231
|
+
query.withUser(new WAUSync_1.USyncUser().withId(jid)) // todo: investigate - the idea here is that <user> should have an inline lid field with the lid being the pn equivalent
|
|
232
|
+
}
|
|
233
|
+
const result = await sock.executeUSyncQuery(query)
|
|
234
|
+
if (result) {
|
|
235
|
+
// TODO: LID MAP this stuff (lid protocol will now return lid with devices)
|
|
236
|
+
const lidResults = result.list.filter(a => !!a.lid)
|
|
237
|
+
if (lidResults.length > 0) {
|
|
238
|
+
logger.trace('Storing LID maps from device call')
|
|
239
|
+
await signalRepository.lidMapping.storeLIDPNMappings(lidResults.map(a => ({ lid: a.lid, pn: a.id })))
|
|
240
|
+
// Force-refresh sessions for newly mapped LIDs to align identity addressing
|
|
241
|
+
try {
|
|
242
|
+
const lids = lidResults.map(a => a.lid)
|
|
243
|
+
if (lids.length) {
|
|
244
|
+
await assertSessions(lids, true)
|
|
245
|
+
}
|
|
246
|
+
} catch (e) {
|
|
247
|
+
logger.warn({ e, count: lidResults.length }, 'failed to assert sessions for newly mapped LIDs')
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
const extracted = (0, Utils_1.extractDeviceJids)(
|
|
251
|
+
result?.list,
|
|
252
|
+
authState.creds.me.id,
|
|
253
|
+
authState.creds.me.lid,
|
|
254
|
+
ignoreZeroDevices
|
|
255
|
+
)
|
|
256
|
+
const deviceMap = {}
|
|
257
|
+
for (const item of extracted) {
|
|
258
|
+
deviceMap[item.user] = deviceMap[item.user] || []
|
|
259
|
+
deviceMap[item.user]?.push(item)
|
|
260
|
+
}
|
|
261
|
+
// Process each user's devices as a group for bulk LID migration
|
|
262
|
+
for (const [user, userDevices] of Object.entries(deviceMap)) {
|
|
263
|
+
const isLidUser = requestedLidUsers.has(user)
|
|
264
|
+
// Process all devices for this user
|
|
265
|
+
for (const item of userDevices) {
|
|
266
|
+
const finalJid = isLidUser
|
|
267
|
+
? (0, WABinary_1.jidEncode)(user, item.server, item.device)
|
|
268
|
+
: (0, WABinary_1.jidEncode)(item.user, item.server, item.device)
|
|
269
|
+
deviceResults.push({
|
|
270
|
+
...item,
|
|
271
|
+
jid: finalJid
|
|
272
|
+
})
|
|
273
|
+
logger.debug(
|
|
274
|
+
{
|
|
275
|
+
user: item.user,
|
|
276
|
+
device: item.device,
|
|
277
|
+
finalJid,
|
|
278
|
+
usedLid: isLidUser
|
|
279
|
+
},
|
|
280
|
+
'Processed device with LID priority'
|
|
281
|
+
)
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
if (userDevicesCache.mset) {
|
|
285
|
+
// if the cache supports mset, we can set all devices in one go
|
|
286
|
+
await userDevicesCache.mset(Object.entries(deviceMap).map(([key, value]) => ({ key, value })))
|
|
287
|
+
} else {
|
|
288
|
+
for (const key in deviceMap) {
|
|
289
|
+
if (deviceMap[key]) await userDevicesCache.set(key, deviceMap[key])
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
const userDeviceUpdates = {}
|
|
293
|
+
for (const [userId, devices] of Object.entries(deviceMap)) {
|
|
294
|
+
if (devices && devices.length > 0) {
|
|
295
|
+
userDeviceUpdates[userId] = devices.map(d => d.device?.toString() || '0')
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
if (Object.keys(userDeviceUpdates).length > 0) {
|
|
299
|
+
try {
|
|
300
|
+
await authState.keys.set({ 'device-list': userDeviceUpdates })
|
|
301
|
+
logger.debug(
|
|
302
|
+
{ userCount: Object.keys(userDeviceUpdates).length },
|
|
303
|
+
'stored user device lists for bulk migration'
|
|
304
|
+
)
|
|
305
|
+
} catch (error) {
|
|
306
|
+
logger.warn({ error }, 'failed to store user device lists')
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
return deviceResults
|
|
311
|
+
}
|
|
312
|
+
/**
|
|
313
|
+
* Update Member Label
|
|
314
|
+
*/
|
|
315
|
+
const updateMemberLabel = (jid, memberLabel) => {
|
|
316
|
+
return relayMessage(
|
|
317
|
+
jid,
|
|
318
|
+
{
|
|
319
|
+
protocolMessage: {
|
|
320
|
+
type: index_js_1.proto.Message.ProtocolMessage.Type.GROUP_MEMBER_LABEL_CHANGE,
|
|
321
|
+
memberLabel: {
|
|
322
|
+
label: memberLabel?.slice(0, 30),
|
|
323
|
+
labelTimestamp: (0, Utils_1.unixTimestampSeconds)()
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
},
|
|
327
|
+
{
|
|
328
|
+
additionalNodes: [
|
|
329
|
+
{
|
|
330
|
+
tag: 'meta',
|
|
331
|
+
attrs: {
|
|
332
|
+
tag_reason: 'user_update',
|
|
333
|
+
appdata: 'member_tag'
|
|
334
|
+
},
|
|
335
|
+
content: undefined
|
|
336
|
+
}
|
|
337
|
+
]
|
|
338
|
+
}
|
|
339
|
+
)
|
|
340
|
+
}
|
|
341
|
+
const assertSessions = async (jids, force) => {
|
|
342
|
+
let didFetchNewSession = false
|
|
343
|
+
const uniqueJids = [...new Set(jids)] // Deduplicate JIDs
|
|
344
|
+
const jidsRequiringFetch = []
|
|
345
|
+
logger.debug({ jids }, 'assertSessions call with jids')
|
|
346
|
+
// Check peerSessionsCache and validate sessions using libsignal loadSession
|
|
347
|
+
for (const jid of uniqueJids) {
|
|
348
|
+
const signalId = signalRepository.jidToSignalProtocolAddress(jid)
|
|
349
|
+
const cachedSession = peerSessionsCache.get(signalId)
|
|
350
|
+
if (cachedSession !== undefined) {
|
|
351
|
+
if (cachedSession && !force) {
|
|
352
|
+
continue // Session exists in cache
|
|
353
|
+
}
|
|
354
|
+
} else {
|
|
355
|
+
const sessionValidation = await signalRepository.validateSession(jid)
|
|
356
|
+
const hasSession = sessionValidation.exists
|
|
357
|
+
peerSessionsCache.set(signalId, hasSession)
|
|
358
|
+
if (hasSession && !force) {
|
|
359
|
+
continue
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
jidsRequiringFetch.push(jid)
|
|
363
|
+
}
|
|
364
|
+
if (jidsRequiringFetch.length) {
|
|
365
|
+
// LID if mapped, otherwise original; interop JIDs pass through as-is
|
|
366
|
+
const wireJids = [
|
|
367
|
+
...jidsRequiringFetch.filter(jid => !!(0, WABinary_1.isLidUser)(jid) || !!(0, WABinary_1.isHostedLidUser)(jid)),
|
|
368
|
+
...(
|
|
369
|
+
(await signalRepository.lidMapping.getLIDsForPNs(
|
|
370
|
+
jidsRequiringFetch.filter(jid => !!(0, WABinary_1.isPnUser)(jid) || !!(0, WABinary_1.isHostedPnUser)(jid))
|
|
371
|
+
)) || []
|
|
372
|
+
).map(a => a.lid),
|
|
373
|
+
...jidsRequiringFetch.filter(jid => (0, WABinary_1.isInteropUser)(jid))
|
|
374
|
+
]
|
|
375
|
+
const interopFetches = wireJids.filter(j => (0, WABinary_1.isInteropUser)(j))
|
|
376
|
+
if (interopFetches.length) {
|
|
377
|
+
logger.info({ interopFetches }, '[interop] fetching pre-key bundle for interop device(s)')
|
|
378
|
+
}
|
|
379
|
+
logger.debug({ jidsRequiringFetch, wireJids }, 'fetching sessions')
|
|
380
|
+
if (!wireJids.length) {
|
|
381
|
+
logger.debug({ jidsRequiringFetch }, 'assertSessions: no wire JIDs to fetch (all unsupported domain)')
|
|
382
|
+
return didFetchNewSession
|
|
383
|
+
}
|
|
384
|
+
const result = await query({
|
|
385
|
+
tag: 'iq',
|
|
386
|
+
attrs: {
|
|
387
|
+
xmlns: 'encrypt',
|
|
388
|
+
type: 'get',
|
|
389
|
+
to: WABinary_1.S_WHATSAPP_NET
|
|
390
|
+
},
|
|
391
|
+
content: [
|
|
392
|
+
{
|
|
393
|
+
tag: 'key',
|
|
394
|
+
attrs: {},
|
|
395
|
+
content: wireJids.map(jid => {
|
|
396
|
+
const attrs = { jid }
|
|
397
|
+
if (force) attrs.reason = 'identity'
|
|
398
|
+
return { tag: 'user', attrs }
|
|
399
|
+
})
|
|
400
|
+
}
|
|
401
|
+
]
|
|
402
|
+
})
|
|
403
|
+
if (interopFetches.length) {
|
|
404
|
+
logger.debug({ interopFetches }, '[interop] pre-key IQ response received')
|
|
405
|
+
}
|
|
406
|
+
await (0, Utils_1.parseAndInjectE2ESessions)(result, signalRepository)
|
|
407
|
+
didFetchNewSession = true
|
|
408
|
+
// Cache fetched sessions using wire JIDs
|
|
409
|
+
for (const wireJid of wireJids) {
|
|
410
|
+
const signalId = signalRepository.jidToSignalProtocolAddress(wireJid)
|
|
411
|
+
peerSessionsCache.set(signalId, true)
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
return didFetchNewSession
|
|
415
|
+
}
|
|
416
|
+
const sendPeerDataOperationMessage = async pdoMessage => {
|
|
417
|
+
//TODO: for later, abstract the logic to send a Peer Message instead of just PDO - useful for App State Key Resync with phone
|
|
418
|
+
if (!authState.creds.me?.id) {
|
|
419
|
+
throw new boom_1.Boom('Not authenticated')
|
|
420
|
+
}
|
|
421
|
+
const protocolMessage = {
|
|
422
|
+
protocolMessage: {
|
|
423
|
+
peerDataOperationRequestMessage: pdoMessage,
|
|
424
|
+
type: index_js_1.proto.Message.ProtocolMessage.Type.PEER_DATA_OPERATION_REQUEST_MESSAGE
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
const meJid = (0, WABinary_1.jidNormalizedUser)(authState.creds.me.id)
|
|
428
|
+
const msgId = await relayMessage(meJid, protocolMessage, {
|
|
429
|
+
additionalAttributes: {
|
|
430
|
+
category: 'peer',
|
|
431
|
+
push_priority: 'high_force'
|
|
432
|
+
},
|
|
433
|
+
additionalNodes: [
|
|
434
|
+
{
|
|
435
|
+
tag: 'meta',
|
|
436
|
+
attrs: { appdata: 'default' }
|
|
437
|
+
}
|
|
438
|
+
]
|
|
439
|
+
})
|
|
440
|
+
return msgId
|
|
441
|
+
}
|
|
442
|
+
const createParticipantNodes = async (recipientJids, message, extraAttrs, dsmMessage) => {
|
|
443
|
+
if (!recipientJids.length) {
|
|
444
|
+
return { nodes: [], shouldIncludeDeviceIdentity: false }
|
|
445
|
+
}
|
|
446
|
+
const patched = await patchMessageBeforeSending(message, recipientJids)
|
|
447
|
+
const patchedMessages = Array.isArray(patched)
|
|
448
|
+
? patched
|
|
449
|
+
: recipientJids.map(jid => ({ recipientJid: jid, message: patched }))
|
|
450
|
+
let shouldIncludeDeviceIdentity = false
|
|
451
|
+
const meId = authState.creds.me.id
|
|
452
|
+
const meLid = authState.creds.me?.lid
|
|
453
|
+
const meLidUser = meLid ? (0, WABinary_1.jidDecode)(meLid)?.user : null
|
|
454
|
+
const encryptionPromises = patchedMessages.map(async ({ recipientJid: jid, message: patchedMessage }) => {
|
|
455
|
+
try {
|
|
456
|
+
if (!jid) return null
|
|
457
|
+
let msgToEncrypt = patchedMessage
|
|
458
|
+
if (dsmMessage) {
|
|
459
|
+
const { user: targetUser } = (0, WABinary_1.jidDecode)(jid)
|
|
460
|
+
const { user: ownPnUser } = (0, WABinary_1.jidDecode)(meId)
|
|
461
|
+
const ownLidUser = meLidUser
|
|
462
|
+
const isOwnUser = targetUser === ownPnUser || (ownLidUser && targetUser === ownLidUser)
|
|
463
|
+
const isExactSenderDevice = jid === meId || (meLid && jid === meLid)
|
|
464
|
+
if (isOwnUser && !isExactSenderDevice) {
|
|
465
|
+
msgToEncrypt = dsmMessage
|
|
466
|
+
logger.debug({ jid, targetUser }, 'Using DSM for own device')
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
const bytes = (0, Utils_1.encodeWAMessage)(msgToEncrypt)
|
|
470
|
+
const mutexKey = jid
|
|
471
|
+
const node = await encryptionMutex.mutex(mutexKey, async () => {
|
|
472
|
+
const { type, ciphertext } = await signalRepository.encryptMessage({ jid, data: bytes })
|
|
473
|
+
if (type === 'pkmsg') {
|
|
474
|
+
shouldIncludeDeviceIdentity = true
|
|
475
|
+
}
|
|
476
|
+
return {
|
|
477
|
+
tag: 'to',
|
|
478
|
+
attrs: { jid },
|
|
479
|
+
content: [
|
|
480
|
+
{
|
|
481
|
+
tag: 'enc',
|
|
482
|
+
attrs: { v: '2', type, ...(extraAttrs || {}) },
|
|
483
|
+
content: ciphertext
|
|
484
|
+
}
|
|
485
|
+
]
|
|
486
|
+
}
|
|
487
|
+
})
|
|
488
|
+
return node
|
|
489
|
+
} catch (err) {
|
|
490
|
+
logger.error({ jid, err }, 'Failed to encrypt for recipient')
|
|
491
|
+
return null
|
|
492
|
+
}
|
|
493
|
+
})
|
|
494
|
+
const nodes = (await Promise.all(encryptionPromises)).filter(node => node !== null)
|
|
495
|
+
if (recipientJids.length > 0 && nodes.length === 0) {
|
|
496
|
+
throw new boom_1.Boom('All encryptions failed', { statusCode: 500 })
|
|
497
|
+
}
|
|
498
|
+
return { nodes, shouldIncludeDeviceIdentity }
|
|
499
|
+
}
|
|
500
|
+
const relayMessage = async (
|
|
501
|
+
jid,
|
|
502
|
+
message,
|
|
503
|
+
{
|
|
504
|
+
messageId: msgId,
|
|
505
|
+
participant,
|
|
506
|
+
additionalAttributes,
|
|
507
|
+
additionalNodes,
|
|
508
|
+
useUserDevicesCache,
|
|
509
|
+
useCachedGroupMetadata,
|
|
510
|
+
statusJidList
|
|
511
|
+
}
|
|
444
512
|
) => {
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
msgId = iosBros ? generateIOSMessageID() : msgId ?? generateMessageIDV2(meId)
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
const pollMessage =
|
|
476
|
-
messages.pollCreationMessage || messages.pollCreationMessageV2 || messages.pollCreationMessageV3
|
|
477
|
-
await authState.keys.transaction(async () => {
|
|
478
|
-
const mediaType = getMediaType(message)
|
|
479
|
-
if (mediaType) extraAttrs.mediatype = mediaType
|
|
480
|
-
if (isNewsletter) {
|
|
481
|
-
const patched = patchMessageBeforeSending? await patchMessageBeforeSending(message, []) : message
|
|
482
|
-
const bytes = encodeNewsletterMessage(patched)
|
|
483
|
-
binaryNodeContent.push({ tag: 'plaintext', attrs: {}, content: bytes })
|
|
484
|
-
const stanza = {
|
|
485
|
-
tag: 'message',
|
|
486
|
-
attrs: {
|
|
487
|
-
to: jid,
|
|
488
|
-
id: msgId,
|
|
489
|
-
type: getMessageType(message),
|
|
490
|
-
...(additionalAttributes || {})
|
|
491
|
-
},
|
|
492
|
-
content: binaryNodeContent
|
|
493
|
-
}
|
|
494
|
-
logger.debug({ msgId }, `sending newsletter message to ${jid}`)
|
|
495
|
-
await sendNode(stanza)
|
|
496
|
-
return
|
|
497
|
-
}
|
|
498
|
-
if (normalizeMessageContent(message)?.pinInChatMessage || normalizeMessageContent(message)?.reactionMessage) {
|
|
499
|
-
extraAttrs['decrypt-fail'] = 'hide'
|
|
500
|
-
}
|
|
501
|
-
if (isGroupOrStatus &&!isRetryResend) {
|
|
502
|
-
const [groupData, senderKeyMap] = await Promise.all([
|
|
503
|
-
(async () => {
|
|
504
|
-
let groupData = useCachedGroupMetadata && cachedGroupMetadata? await cachedGroupMetadata(jid) : undefined
|
|
505
|
-
if (groupData && Array.isArray(groupData?.participants)) {
|
|
506
|
-
logger.trace({ jid, participants: groupData.participants.length }, 'using cached group metadata')
|
|
507
|
-
} else if (!isStatus) {
|
|
508
|
-
groupData = await groupMetadata(jid)
|
|
509
|
-
}
|
|
510
|
-
return groupData
|
|
511
|
-
})(),
|
|
512
|
-
(async () => {
|
|
513
|
-
if (!participant &&!isStatus) {
|
|
514
|
-
const result = await authState.keys.get('sender-key-memory', [jid])
|
|
515
|
-
return result[jid] || {}
|
|
516
|
-
}
|
|
517
|
-
return {}
|
|
518
|
-
})()
|
|
519
|
-
])
|
|
520
|
-
const participantsList = groupData? groupData.participants.map(p => p.id) : []
|
|
521
|
-
if (groupData?.ephemeralDuration && groupData.ephemeralDuration > 0) {
|
|
522
|
-
additionalAttributes = {...additionalAttributes, expiration: groupData.ephemeralDuration.toString() }
|
|
523
|
-
}
|
|
524
|
-
if (isStatus && statusJidList) participantsList.push(...statusJidList)
|
|
525
|
-
const additionalDevices = await getUSyncDevices(participantsList, false, false)
|
|
526
|
-
devices.push(...additionalDevices)
|
|
527
|
-
if (isGroup) {
|
|
528
|
-
additionalAttributes = {
|
|
529
|
-
...additionalAttributes,
|
|
530
|
-
addressing_mode: groupData?.addressingMode || 'lid'
|
|
531
|
-
}
|
|
532
|
-
}
|
|
533
|
-
if (message?.groupStatusMessageV2 &&!message?.messageContextInfo?.messageSecret) {
|
|
534
|
-
message = {
|
|
535
|
-
...message,
|
|
536
|
-
messageContextInfo: {
|
|
537
|
-
...(message.messageContextInfo || {}),
|
|
538
|
-
messageSecret: randomBytes(32)
|
|
539
|
-
},
|
|
540
|
-
groupStatusMessageV2: {
|
|
541
|
-
...message.groupStatusMessageV2,
|
|
542
|
-
message: {
|
|
543
|
-
...(message.groupStatusMessageV2.message || {}),
|
|
544
|
-
messageContextInfo: {
|
|
545
|
-
...(message.groupStatusMessageV2.message?.messageContextInfo || {}),
|
|
546
|
-
messageSecret: message.messageContextInfo?.messageSecret || randomBytes(32)
|
|
547
|
-
}
|
|
548
|
-
}
|
|
549
|
-
}
|
|
550
|
-
}
|
|
551
|
-
}
|
|
552
|
-
// list/buttons/template -> interactiveMessage
|
|
553
|
-
if (message.listMessage) {
|
|
554
|
-
const list = message.listMessage
|
|
555
|
-
message = {
|
|
556
|
-
interactiveMessage: {
|
|
557
|
-
nativeFlowMessage: {
|
|
558
|
-
buttons: [
|
|
559
|
-
{
|
|
560
|
-
name: 'single_select',
|
|
561
|
-
buttonParamsJson: JSON.stringify({
|
|
562
|
-
title: list.buttonText || 'Select',
|
|
563
|
-
sections: (list.sections || []).map(section => ({
|
|
564
|
-
title: section.title || '',
|
|
565
|
-
highlight_label: '',
|
|
566
|
-
rows: (section.rows || []).map(row => ({
|
|
567
|
-
header: '',
|
|
568
|
-
title: row.title || '',
|
|
569
|
-
description: row.description || '',
|
|
570
|
-
id: row.rowId || row.id || ''
|
|
571
|
-
}))
|
|
572
|
-
}))
|
|
573
|
-
})
|
|
574
|
-
}
|
|
575
|
-
],
|
|
576
|
-
messageParamsJson: '',
|
|
577
|
-
messageVersion: 1
|
|
578
|
-
},
|
|
579
|
-
body: { text: list.description || '' },
|
|
580
|
-
footer: list.footerText? { text: list.footerText } : undefined,
|
|
581
|
-
header: list.title? { title: list.title, hasMediaAttachment: false, subtitle: '' } : undefined,
|
|
582
|
-
contextInfo: list.contextInfo
|
|
583
|
-
}
|
|
584
|
-
}
|
|
585
|
-
} else if (message.buttonsMessage) {
|
|
586
|
-
const bMsg = message.buttonsMessage
|
|
587
|
-
const buttons = (bMsg.buttons || []).map(btn => ({
|
|
588
|
-
name: 'quick_reply',
|
|
589
|
-
buttonParamsJson: JSON.stringify({
|
|
590
|
-
display_text: btn.buttonText?.displayText || btn.buttonText || '',
|
|
591
|
-
id: btn.buttonId || btn.buttonText?.displayText || ''
|
|
592
|
-
})
|
|
593
|
-
}))
|
|
594
|
-
message = {
|
|
595
|
-
interactiveMessage: {
|
|
596
|
-
nativeFlowMessage: { buttons, messageParamsJson: '', messageVersion: 1 },
|
|
597
|
-
body: { text: bMsg.contentText || bMsg.text || '' },
|
|
598
|
-
footer: bMsg.footerText? { text: bMsg.footerText } : undefined,
|
|
599
|
-
header: bMsg.text
|
|
600
|
-
? { title: bMsg.text, hasMediaAttachment: false, subtitle: '' }
|
|
601
|
-
: bMsg.imageMessage || bMsg.videoMessage || bMsg.documentMessage
|
|
602
|
-
? { hasMediaAttachment: true,...(bMsg.imageMessage? { imageMessage: bMsg.imageMessage } : {}),...(bMsg.videoMessage? { videoMessage: bMsg.videoMessage } : {}) }
|
|
603
|
-
: undefined,
|
|
604
|
-
contextInfo: bMsg.contextInfo
|
|
605
|
-
}
|
|
606
|
-
}
|
|
607
|
-
} else if (message.templateMessage) {
|
|
608
|
-
const tmpl = message.templateMessage.hydratedTemplate || message.templateMessage.fourRowTemplate
|
|
609
|
-
if (tmpl) {
|
|
610
|
-
const buttons = (tmpl.hydratedButtons || [])
|
|
611
|
-
.map(hBtn => {
|
|
612
|
-
if (hBtn.quickReplyButton) {
|
|
613
|
-
return { name: 'quick_reply', buttonParamsJson: JSON.stringify({ display_text: hBtn.quickReplyButton.displayText || '', id: hBtn.quickReplyButton.id || hBtn.quickReplyButton.displayText || '' }) }
|
|
614
|
-
} else if (hBtn.urlButton) {
|
|
615
|
-
return { name: 'cta_url', buttonParamsJson: JSON.stringify({ display_text: hBtn.urlButton.displayText || '', url: hBtn.urlButton.url || '', merchant_url: hBtn.urlButton.url || '' }) }
|
|
616
|
-
} else if (hBtn.callButton) {
|
|
617
|
-
return { name: 'cta_call', buttonParamsJson: JSON.stringify({ display_text: hBtn.callButton.displayText || '', phone_number: hBtn.callButton.phoneNumber || '' }) }
|
|
618
|
-
}
|
|
619
|
-
return null
|
|
620
|
-
})
|
|
621
|
-
.filter(Boolean)
|
|
622
|
-
message = {
|
|
623
|
-
interactiveMessage: {
|
|
624
|
-
nativeFlowMessage: { buttons, messageParamsJson: '', messageVersion: 1 },
|
|
625
|
-
body: { text: tmpl.hydratedContentText || tmpl.contentText || '' },
|
|
626
|
-
footer: tmpl.hydratedFooterText? { text: tmpl.hydratedFooterText } : undefined,
|
|
627
|
-
header: tmpl.hydratedTitleText
|
|
628
|
-
? { title: tmpl.hydratedTitleText, hasMediaAttachment: false, subtitle: '' }
|
|
629
|
-
: tmpl.imageMessage || tmpl.videoMessage || tmpl.documentMessage
|
|
630
|
-
? { hasMediaAttachment: true,...(tmpl.imageMessage? { imageMessage: tmpl.imageMessage } : {}),...(tmpl.videoMessage? { videoMessage: tmpl.videoMessage } : {}) }
|
|
631
|
-
: undefined,
|
|
632
|
-
contextInfo: tmpl.contextInfo
|
|
633
|
-
}
|
|
634
|
-
}
|
|
635
|
-
}
|
|
636
|
-
}
|
|
513
|
+
const meId = authState.creds.me.id
|
|
514
|
+
const meLid = authState.creds.me?.lid
|
|
515
|
+
const isRetryResend = Boolean(participant?.jid)
|
|
516
|
+
let shouldIncludeDeviceIdentity = isRetryResend
|
|
517
|
+
const statusJid = 'status@broadcast'
|
|
518
|
+
const { user, server } = (0, WABinary_1.jidDecode)(jid)
|
|
519
|
+
const isGroup = server === 'g.us'
|
|
520
|
+
const isStatus = jid === statusJid
|
|
521
|
+
const isLid = server === 'lid'
|
|
522
|
+
const isNewsletter = server === 'newsletter'
|
|
523
|
+
const isInterop = (0, WABinary_1.isInteropUser)(jid)
|
|
524
|
+
const isGroupOrStatus = isGroup || isStatus
|
|
525
|
+
const finalJid = jid
|
|
526
|
+
const iosBros = config.browser[0] === "iOS" || config.browser[1] === "Safari";
|
|
527
|
+
msgId = iosBros ? Utils_1.generateIOSMessageID() : msgId ?? (0, Utils_1.generateMessageIDV2)(meId)
|
|
528
|
+
useUserDevicesCache = useUserDevicesCache !== false
|
|
529
|
+
useCachedGroupMetadata = useCachedGroupMetadata !== false && !isStatus
|
|
530
|
+
const participants = []
|
|
531
|
+
const destinationJid = !isStatus ? finalJid : statusJid
|
|
532
|
+
const binaryNodeContent = []
|
|
533
|
+
const devices = []
|
|
534
|
+
let reportingMessage
|
|
535
|
+
const meMsg = {
|
|
536
|
+
deviceSentMessage: {
|
|
537
|
+
destinationJid,
|
|
538
|
+
message
|
|
539
|
+
},
|
|
540
|
+
messageContextInfo: message.messageContextInfo
|
|
541
|
+
}
|
|
542
|
+
const extraAttrs = {}
|
|
637
543
|
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
544
|
+
const regexGroupOld = /^(\d{1,15})-(\d+)@g\.us$/
|
|
545
|
+
|
|
546
|
+
const messages = Utils_1.normalizeMessageContent(message)
|
|
547
|
+
|
|
548
|
+
const buttonType = getButtonType(messages)
|
|
549
|
+
const pollMessage = messages.pollCreationMessage || messages.pollCreationMessageV2 || messages.pollCreationMessageV3
|
|
550
|
+
await authState.keys.transaction(async () => {
|
|
551
|
+
const mediaType = getMediaType(message)
|
|
552
|
+
if (mediaType) {
|
|
553
|
+
extraAttrs['mediatype'] = mediaType
|
|
554
|
+
}
|
|
555
|
+
if (isNewsletter) {
|
|
556
|
+
const patched = patchMessageBeforeSending ? await patchMessageBeforeSending(message, []) : message
|
|
557
|
+
const bytes = (0, Utils_1.encodeNewsletterMessage)(patched)
|
|
558
|
+
binaryNodeContent.push({
|
|
559
|
+
tag: 'plaintext',
|
|
560
|
+
attrs: {},
|
|
561
|
+
content: bytes
|
|
562
|
+
})
|
|
563
|
+
const stanza = {
|
|
564
|
+
tag: 'message',
|
|
565
|
+
attrs: {
|
|
566
|
+
to: jid,
|
|
567
|
+
id: msgId,
|
|
568
|
+
type: getTypeMessage(message),
|
|
569
|
+
...(additionalAttributes || {})
|
|
570
|
+
},
|
|
571
|
+
content: binaryNodeContent
|
|
572
|
+
}
|
|
573
|
+
logger.debug({ msgId }, `sending newsletter message to ${jid}`)
|
|
574
|
+
await sendNode(stanza)
|
|
575
|
+
return
|
|
576
|
+
}
|
|
577
|
+
if (
|
|
578
|
+
(0, Utils_1.normalizeMessageContent)(message)?.pinInChatMessage ||
|
|
579
|
+
(0, Utils_1.normalizeMessageContent)(message)?.reactionMessage
|
|
580
|
+
) {
|
|
581
|
+
extraAttrs['decrypt-fail'] = 'hide' // todo: expand for reactions and other types
|
|
582
|
+
}
|
|
583
|
+
if (isGroupOrStatus && !isRetryResend) {
|
|
584
|
+
const [groupData, senderKeyMap] = await Promise.all([
|
|
585
|
+
(async () => {
|
|
586
|
+
let groupData = useCachedGroupMetadata && cachedGroupMetadata ? await cachedGroupMetadata(jid) : undefined
|
|
587
|
+
const looksStale =
|
|
588
|
+
groupData &&
|
|
589
|
+
typeof groupData.size === 'number' &&
|
|
590
|
+
Array.isArray(groupData.participants) &&
|
|
591
|
+
groupData.participants.length < groupData.size
|
|
592
|
+
if (groupData && Array.isArray(groupData?.participants) && !looksStale) {
|
|
593
|
+
logger.trace({ jid, participants: groupData.participants.length }, 'using cached group metadata')
|
|
594
|
+
} else if (!isStatus) {
|
|
595
|
+
if (looksStale) {
|
|
596
|
+
logger.debug(
|
|
597
|
+
{ jid, cached: groupData.participants.length, expected: groupData.size },
|
|
598
|
+
'cached group metadata looks stale, re-fetching'
|
|
599
|
+
)
|
|
600
|
+
}
|
|
601
|
+
groupData = await groupMetadata(jid) // TODO: start storing group participant list + addr mode in Signal & stop relying on this
|
|
602
|
+
}
|
|
603
|
+
return groupData
|
|
604
|
+
})(),
|
|
605
|
+
(async () => {
|
|
606
|
+
if (!participant && !isStatus) {
|
|
607
|
+
// what if sender memory is less accurate than the cached metadata
|
|
608
|
+
// on participant change in group, we should do sender memory manipulation
|
|
609
|
+
const result = await authState.keys.get('sender-key-memory', [jid]) // TODO: check out what if the sender key memory doesn't include the LID stuff now?
|
|
610
|
+
return result[jid] || {}
|
|
611
|
+
}
|
|
612
|
+
return {}
|
|
613
|
+
})()
|
|
614
|
+
])
|
|
615
|
+
const participantsList = groupData ? groupData.participants.map(p => p.id) : []
|
|
616
|
+
if (groupData?.ephemeralDuration && groupData.ephemeralDuration > 0) {
|
|
617
|
+
additionalAttributes = {
|
|
618
|
+
...additionalAttributes,
|
|
619
|
+
expiration: groupData.ephemeralDuration.toString()
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
if (isStatus && statusJidList) {
|
|
623
|
+
participantsList.push(...statusJidList)
|
|
624
|
+
}
|
|
625
|
+
const additionalDevices = await getUSyncDevices(participantsList, !!useUserDevicesCache, false)
|
|
626
|
+
devices.push(...additionalDevices)
|
|
627
|
+
if (isGroup) {
|
|
628
|
+
additionalAttributes = {
|
|
629
|
+
...additionalAttributes,
|
|
630
|
+
addressing_mode: groupData?.addressingMode || 'lid'
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
if (message?.groupStatusMessageV2 && !message?.messageContextInfo?.messageSecret) {
|
|
634
|
+
const { randomBytes } = require('crypto')
|
|
635
|
+
message = {
|
|
636
|
+
...message,
|
|
637
|
+
messageContextInfo: {
|
|
638
|
+
...(message.messageContextInfo || {}),
|
|
639
|
+
messageSecret: randomBytes(32)
|
|
640
|
+
},
|
|
641
|
+
groupStatusMessageV2: {
|
|
642
|
+
...message.groupStatusMessageV2,
|
|
643
|
+
message: {
|
|
644
|
+
...(message.groupStatusMessageV2.message || {}),
|
|
645
|
+
messageContextInfo: {
|
|
646
|
+
...(message.groupStatusMessageV2.message?.messageContextInfo || {}),
|
|
647
|
+
messageSecret: message.messageContextInfo?.messageSecret || randomBytes(32)
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
if (message.listMessage) {
|
|
654
|
+
const list = message.listMessage
|
|
655
|
+
const interactiveMessage = {
|
|
656
|
+
nativeFlowMessage: {
|
|
657
|
+
buttons: [
|
|
658
|
+
{
|
|
659
|
+
name: 'single_select',
|
|
660
|
+
buttonParamsJson: JSON.stringify({
|
|
661
|
+
title: list.buttonText || 'Select',
|
|
662
|
+
sections: (list.sections || []).map(section => ({
|
|
663
|
+
title: section.title || '',
|
|
664
|
+
highlight_label: '',
|
|
665
|
+
rows: (section.rows || []).map(row => ({
|
|
666
|
+
header: '',
|
|
667
|
+
title: row.title || '',
|
|
668
|
+
description: row.description || '',
|
|
669
|
+
id: row.rowId || row.id || ''
|
|
670
|
+
}))
|
|
671
|
+
}))
|
|
672
|
+
})
|
|
673
|
+
}
|
|
674
|
+
],
|
|
675
|
+
messageParamsJson: '',
|
|
676
|
+
messageVersion: 1
|
|
677
|
+
},
|
|
678
|
+
body: { text: list.description || '' },
|
|
679
|
+
footer: list.footerText ? { text: list.footerText } : undefined,
|
|
680
|
+
header: list.title ? { title: list.title, hasMediaAttachment: false, subtitle: '' } : undefined,
|
|
681
|
+
contextInfo: list.contextInfo
|
|
682
|
+
}
|
|
683
|
+
message = { interactiveMessage }
|
|
684
|
+
} else if (message.buttonsMessage) {
|
|
685
|
+
const bMsg = message.buttonsMessage
|
|
686
|
+
const buttons = (bMsg.buttons || []).map(btn => ({
|
|
687
|
+
name: 'quick_reply',
|
|
688
|
+
buttonParamsJson: JSON.stringify({
|
|
689
|
+
display_text: btn.buttonText?.displayText || btn.buttonText || '',
|
|
690
|
+
id: btn.buttonId || btn.buttonText?.displayText || ''
|
|
691
|
+
})
|
|
692
|
+
}))
|
|
693
|
+
const interactiveMessage = {
|
|
694
|
+
nativeFlowMessage: {
|
|
695
|
+
buttons,
|
|
696
|
+
messageParamsJson: '',
|
|
697
|
+
messageVersion: 1
|
|
698
|
+
},
|
|
699
|
+
body: { text: bMsg.contentText || bMsg.text || '' },
|
|
700
|
+
footer: bMsg.footerText ? { text: bMsg.footerText } : undefined,
|
|
701
|
+
header: bMsg.text
|
|
702
|
+
? { title: bMsg.text, hasMediaAttachment: false, subtitle: '' }
|
|
703
|
+
: bMsg.imageMessage || bMsg.videoMessage || bMsg.documentMessage
|
|
704
|
+
? {
|
|
705
|
+
hasMediaAttachment: true,
|
|
706
|
+
...(bMsg.imageMessage ? { imageMessage: bMsg.imageMessage } : {}),
|
|
707
|
+
...(bMsg.videoMessage ? { videoMessage: bMsg.videoMessage } : {})
|
|
708
|
+
}
|
|
709
|
+
: undefined,
|
|
710
|
+
contextInfo: bMsg.contextInfo
|
|
711
|
+
}
|
|
712
|
+
message = { interactiveMessage }
|
|
713
|
+
} else if (message.templateMessage) {
|
|
714
|
+
const tmpl = message.templateMessage.hydratedTemplate || message.templateMessage.fourRowTemplate
|
|
715
|
+
if (tmpl) {
|
|
716
|
+
const hydratedButtons = tmpl.hydratedButtons || []
|
|
717
|
+
const buttons = hydratedButtons
|
|
718
|
+
.map(hBtn => {
|
|
719
|
+
if (hBtn.quickReplyButton) {
|
|
720
|
+
return {
|
|
721
|
+
name: 'quick_reply',
|
|
722
|
+
buttonParamsJson: JSON.stringify({
|
|
723
|
+
display_text: hBtn.quickReplyButton.displayText || '',
|
|
724
|
+
id: hBtn.quickReplyButton.id || hBtn.quickReplyButton.displayText || ''
|
|
725
|
+
})
|
|
726
|
+
}
|
|
727
|
+
} else if (hBtn.urlButton) {
|
|
728
|
+
return {
|
|
729
|
+
name: 'cta_url',
|
|
730
|
+
buttonParamsJson: JSON.stringify({
|
|
731
|
+
display_text: hBtn.urlButton.displayText || '',
|
|
732
|
+
url: hBtn.urlButton.url || '',
|
|
733
|
+
merchant_url: hBtn.urlButton.url || ''
|
|
734
|
+
})
|
|
735
|
+
}
|
|
736
|
+
} else if (hBtn.callButton) {
|
|
737
|
+
return {
|
|
738
|
+
name: 'cta_call',
|
|
739
|
+
buttonParamsJson: JSON.stringify({
|
|
740
|
+
display_text: hBtn.callButton.displayText || '',
|
|
741
|
+
phone_number: hBtn.callButton.phoneNumber || ''
|
|
742
|
+
})
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
return null
|
|
746
|
+
})
|
|
747
|
+
.filter(Boolean)
|
|
748
|
+
const interactiveMessage = {
|
|
749
|
+
nativeFlowMessage: {
|
|
750
|
+
buttons,
|
|
751
|
+
messageParamsJson: '',
|
|
752
|
+
messageVersion: 1
|
|
753
|
+
},
|
|
754
|
+
body: { text: tmpl.hydratedContentText || tmpl.contentText || '' },
|
|
755
|
+
footer: tmpl.hydratedFooterText ? { text: tmpl.hydratedFooterText } : undefined,
|
|
756
|
+
header: tmpl.hydratedTitleText
|
|
757
|
+
? { title: tmpl.hydratedTitleText, hasMediaAttachment: false, subtitle: '' }
|
|
758
|
+
: tmpl.imageMessage || tmpl.videoMessage || tmpl.documentMessage
|
|
759
|
+
? {
|
|
760
|
+
hasMediaAttachment: true,
|
|
761
|
+
...(tmpl.imageMessage ? { imageMessage: tmpl.imageMessage } : {}),
|
|
762
|
+
...(tmpl.videoMessage ? { videoMessage: tmpl.videoMessage } : {})
|
|
763
|
+
}
|
|
764
|
+
: undefined,
|
|
765
|
+
contextInfo: tmpl.contextInfo
|
|
766
|
+
}
|
|
767
|
+
message = { interactiveMessage }
|
|
768
|
+
}
|
|
769
|
+
}
|
|
770
|
+
const patched = await patchMessageBeforeSending(message)
|
|
771
|
+
if (Array.isArray(patched)) {
|
|
772
|
+
throw new boom_1.Boom('Per-jid patching is not supported in groups')
|
|
773
|
+
}
|
|
774
|
+
const bytes = (0, Utils_1.encodeWAMessage)(patched)
|
|
775
|
+
reportingMessage = patched
|
|
776
|
+
const groupAddressingMode = additionalAttributes?.['addressing_mode'] || groupData?.addressingMode || 'lid'
|
|
777
|
+
const groupSenderIdentity = groupAddressingMode === 'lid' && meLid ? meLid : meId
|
|
778
|
+
const { ciphertext, senderKeyDistributionMessage } = await signalRepository.encryptGroupMessage({
|
|
779
|
+
group: destinationJid,
|
|
780
|
+
data: bytes,
|
|
781
|
+
meId: groupSenderIdentity
|
|
782
|
+
})
|
|
783
|
+
const senderKeyRecipients = []
|
|
784
|
+
for (const device of devices) {
|
|
785
|
+
const deviceJid = device.jid
|
|
786
|
+
const hasKey = !!senderKeyMap[deviceJid]
|
|
787
|
+
if (
|
|
788
|
+
(!hasKey || !!participant) &&
|
|
789
|
+
!(0, WABinary_1.isHostedLidUser)(deviceJid) &&
|
|
790
|
+
!(0, WABinary_1.isHostedPnUser)(deviceJid) &&
|
|
791
|
+
device.device !== 99
|
|
792
|
+
) {
|
|
793
|
+
//todo: revamp all this logic
|
|
794
|
+
// the goal is to follow with what I said above for each group, and instead of a true false map of ids, we can set an array full of those the app has already sent pkmsgs
|
|
795
|
+
senderKeyRecipients.push(deviceJid)
|
|
796
|
+
senderKeyMap[deviceJid] = true
|
|
797
|
+
}
|
|
798
|
+
}
|
|
799
|
+
if (senderKeyRecipients.length) {
|
|
800
|
+
logger.debug({ senderKeyJids: senderKeyRecipients }, 'sending new sender key')
|
|
801
|
+
const senderKeyMsg = {
|
|
802
|
+
senderKeyDistributionMessage: {
|
|
803
|
+
axolotlSenderKeyDistributionMessage: senderKeyDistributionMessage,
|
|
804
|
+
groupId: destinationJid
|
|
805
|
+
}
|
|
806
|
+
}
|
|
807
|
+
const senderKeySessionTargets = senderKeyRecipients
|
|
808
|
+
await assertSessions(senderKeySessionTargets)
|
|
809
|
+
const result = await createParticipantNodes(senderKeyRecipients, senderKeyMsg, extraAttrs)
|
|
810
|
+
shouldIncludeDeviceIdentity = shouldIncludeDeviceIdentity || result.shouldIncludeDeviceIdentity
|
|
811
|
+
participants.push(...result.nodes)
|
|
812
|
+
}
|
|
813
|
+
binaryNodeContent.push({
|
|
814
|
+
tag: 'enc',
|
|
815
|
+
attrs: { v: '2', type: 'skmsg', ...extraAttrs },
|
|
816
|
+
content: ciphertext
|
|
817
|
+
})
|
|
818
|
+
await authState.keys.set({ 'sender-key-memory': { [jid]: senderKeyMap } })
|
|
819
|
+
} else {
|
|
820
|
+
// ADDRESSING CONSISTENCY: Match own identity to conversation context
|
|
821
|
+
// TODO: investigate if this is true
|
|
822
|
+
let ownId = meId
|
|
823
|
+
if (isLid && meLid) {
|
|
824
|
+
ownId = meLid
|
|
825
|
+
logger.debug({ to: jid, ownId }, 'Using LID identity for @lid conversation')
|
|
826
|
+
} else {
|
|
827
|
+
logger.debug({ to: jid, ownId }, 'Using PN identity for @s.whatsapp.net conversation')
|
|
828
|
+
}
|
|
829
|
+
const { user: ownUser } = (0, WABinary_1.jidDecode)(ownId)
|
|
830
|
+
if (!participant) {
|
|
831
|
+
const patchedForReporting = await patchMessageBeforeSending(message, [jid])
|
|
832
|
+
reportingMessage = Array.isArray(patchedForReporting)
|
|
833
|
+
? patchedForReporting.find(item => item.recipientJid === jid) || patchedForReporting[0]
|
|
834
|
+
: patchedForReporting
|
|
835
|
+
}
|
|
836
|
+
if (!isRetryResend) {
|
|
837
|
+
const targetUserServer = isLid ? 'lid' : isInterop ? 'interop' : 's.whatsapp.net'
|
|
838
|
+
devices.push({
|
|
839
|
+
user,
|
|
840
|
+
device: 0,
|
|
841
|
+
jid: (0, WABinary_1.jidEncode)(user, targetUserServer, 0) // rajeh, todo: this entire logic is convoluted and weird.
|
|
842
|
+
})
|
|
843
|
+
// Interop contacts don't receive a copy to own devices — native WA sends only one flat <enc>
|
|
844
|
+
if (user !== ownUser && !isInterop) {
|
|
845
|
+
const ownUserServer = isLid ? 'lid' : 's.whatsapp.net'
|
|
846
|
+
const ownUserForAddressing =
|
|
847
|
+
isLid && meLid ? (0, WABinary_1.jidDecode)(meLid).user : (0, WABinary_1.jidDecode)(meId).user
|
|
848
|
+
devices.push({
|
|
849
|
+
user: ownUserForAddressing,
|
|
850
|
+
device: 0,
|
|
851
|
+
jid: (0, WABinary_1.jidEncode)(ownUserForAddressing, ownUserServer, 0)
|
|
852
|
+
})
|
|
853
|
+
}
|
|
854
|
+
if (additionalAttributes?.['category'] !== 'peer' && !isInterop) {
|
|
855
|
+
// Clear placeholders and enumerate actual devices
|
|
856
|
+
devices.length = 0
|
|
857
|
+
// Use conversation-appropriate sender identity
|
|
858
|
+
const senderIdentity =
|
|
859
|
+
isLid && meLid
|
|
860
|
+
? (0, WABinary_1.jidEncode)((0, WABinary_1.jidDecode)(meLid)?.user, 'lid', undefined)
|
|
861
|
+
: (0, WABinary_1.jidEncode)((0, WABinary_1.jidDecode)(meId)?.user, 's.whatsapp.net', undefined)
|
|
862
|
+
// Enumerate devices for sender and target with consistent addressing
|
|
863
|
+
const sessionDevices = await getUSyncDevices([senderIdentity, jid], true, false)
|
|
864
|
+
devices.push(...sessionDevices)
|
|
865
|
+
logger.debug(
|
|
866
|
+
{
|
|
867
|
+
deviceCount: devices.length,
|
|
868
|
+
devices: devices.map(d => `${d.user}:${d.device}@${(0, WABinary_1.jidDecode)(d.jid)?.server}`)
|
|
869
|
+
},
|
|
870
|
+
'Device enumeration complete with unified addressing'
|
|
871
|
+
)
|
|
872
|
+
}
|
|
873
|
+
}
|
|
874
|
+
const allRecipients = []
|
|
875
|
+
const meRecipients = []
|
|
876
|
+
const otherRecipients = []
|
|
877
|
+
const { user: mePnUser } = (0, WABinary_1.jidDecode)(meId)
|
|
878
|
+
const { user: meLidUser } = meLid ? (0, WABinary_1.jidDecode)(meLid) : { user: null }
|
|
879
|
+
for (const { user, jid } of devices) {
|
|
880
|
+
const isExactSenderDevice = jid === meId || (meLid && jid === meLid);
|
|
881
|
+
if (isExactSenderDevice) continue;
|
|
882
|
+
const isMe = user === mePnUser || user === meLidUser;
|
|
883
|
+
let ptcp = false;
|
|
884
|
+
if (participant) {
|
|
885
|
+
if (!(!isMe)) ptcp = true;
|
|
726
886
|
}
|
|
727
887
|
if (!ptcp) {
|
|
728
|
-
if (isMe)
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
otherRecipients.push(jid)
|
|
732
|
-
}
|
|
733
|
-
allRecipients.push(jid)
|
|
888
|
+
if (isMe) meRecipients.push(jid);
|
|
889
|
+
else otherRecipients.push(jid);
|
|
890
|
+
allRecipients.push(jid);
|
|
734
891
|
}
|
|
735
892
|
}
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
893
|
+
await assertSessions(allRecipients)
|
|
894
|
+
const [
|
|
895
|
+
{ nodes: meNodes, shouldIncludeDeviceIdentity: s1 },
|
|
896
|
+
{ nodes: otherNodes, shouldIncludeDeviceIdentity: s2 }
|
|
897
|
+
] = await Promise.all([
|
|
898
|
+
// For own devices: use DSM if available (1:1 chats only)
|
|
899
|
+
createParticipantNodes(meRecipients, meMsg || message, extraAttrs),
|
|
900
|
+
createParticipantNodes(otherRecipients, message, extraAttrs, meMsg)
|
|
901
|
+
])
|
|
902
|
+
participants.push(...meNodes)
|
|
903
|
+
participants.push(...otherNodes)
|
|
904
|
+
if (meRecipients.length > 0 || otherRecipients.length > 0) {
|
|
905
|
+
extraAttrs['phash'] = (0, Utils_1.generateParticipantHashV2)([...meRecipients, ...otherRecipients])
|
|
906
|
+
}
|
|
907
|
+
shouldIncludeDeviceIdentity = shouldIncludeDeviceIdentity || s1 || s2
|
|
908
|
+
}
|
|
909
|
+
if (participants.length) {
|
|
910
|
+
if (additionalAttributes?.['category'] === 'peer') {
|
|
911
|
+
const peerNode = participants[0]?.content?.[0]
|
|
912
|
+
if (peerNode) {
|
|
913
|
+
binaryNodeContent.push(peerNode) // push only enc
|
|
914
|
+
}
|
|
915
|
+
} else if (isInterop) {
|
|
916
|
+
// Flat <enc> directly on <message> — no <participants> wrapper (native WA behavior).
|
|
917
|
+
// Find the enc for the actual interop recipient, not own-device DSM copy.
|
|
918
|
+
const recipientNode = participants.find(p => (0, WABinary_1.isInteropUser)(p?.attrs?.jid))
|
|
919
|
+
const encNode = (recipientNode ?? participants[0])?.content?.[0]
|
|
920
|
+
if (encNode) {
|
|
921
|
+
binaryNodeContent.push(encNode)
|
|
922
|
+
}
|
|
923
|
+
} else {
|
|
924
|
+
binaryNodeContent.push({
|
|
925
|
+
tag: 'participants',
|
|
926
|
+
attrs: {},
|
|
927
|
+
content: participants
|
|
928
|
+
})
|
|
929
|
+
}
|
|
930
|
+
}
|
|
931
|
+
const stanza = {
|
|
932
|
+
tag: 'message',
|
|
933
|
+
attrs: {
|
|
934
|
+
id: msgId,
|
|
935
|
+
to: destinationJid,
|
|
936
|
+
type: getTypeMessage(message),
|
|
937
|
+
...(additionalAttributes || {})
|
|
938
|
+
},
|
|
939
|
+
content: binaryNodeContent
|
|
940
|
+
}
|
|
941
|
+
if (shouldIncludeDeviceIdentity) {
|
|
942
|
+
stanza.content.push({
|
|
943
|
+
tag: 'device-identity',
|
|
944
|
+
attrs: {},
|
|
945
|
+
content: (0, Utils_1.encodeSignedDeviceIdentity)(authState.creds.account, true)
|
|
946
|
+
})
|
|
947
|
+
logger.debug({ jid }, 'adding device identity')
|
|
784
948
|
}
|
|
785
|
-
}
|
|
786
|
-
const stanza = {
|
|
787
|
-
tag: 'message',
|
|
788
|
-
attrs: { id: msgId, to: destinationJid, type: getMessageType(message),...(additionalAttributes || {}) },
|
|
789
|
-
content: binaryNodeContent
|
|
790
|
-
}
|
|
791
|
-
if (shouldIncludeDeviceIdentity) {
|
|
792
|
-
stanza.content.push({ tag: 'device-identity', attrs: {}, content: encodeSignedDeviceIdentity(authState.creds.account, true) })
|
|
793
|
-
logger.debug({ jid }, 'adding device identity')
|
|
794
|
-
}
|
|
795
949
|
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
950
|
+
if (isGroup && regexGroupOld.test(jid) && !message.reactionMessage) {
|
|
951
|
+
stanza.content.push({
|
|
952
|
+
tag: 'multicast',
|
|
953
|
+
attrs: {}
|
|
954
|
+
})
|
|
955
|
+
}
|
|
956
|
+
|
|
957
|
+
if (pollMessage || messages.eventMessage) {
|
|
958
|
+
stanza.content.push({
|
|
959
|
+
tag: 'meta',
|
|
960
|
+
attrs: messages.eventMessage
|
|
961
|
+
? {
|
|
962
|
+
event_type: 'creation'
|
|
963
|
+
}
|
|
964
|
+
: isNewsletter
|
|
965
|
+
? {
|
|
966
|
+
polltype: 'creation',
|
|
967
|
+
contenttype: pollMessage?.pollContentType === 2 ? 'image' : 'text'
|
|
968
|
+
}
|
|
969
|
+
: {
|
|
970
|
+
polltype: 'creation'
|
|
971
|
+
}
|
|
972
|
+
})
|
|
973
|
+
}
|
|
974
|
+
if (
|
|
975
|
+
!isNewsletter &&
|
|
976
|
+
!isRetryResend &&
|
|
977
|
+
reportingMessage?.messageContextInfo?.messageSecret &&
|
|
978
|
+
(0, reporting_utils_1.shouldIncludeReportingToken)(reportingMessage)
|
|
979
|
+
) {
|
|
980
|
+
try {
|
|
981
|
+
const encoded = (0, Utils_1.encodeWAMessage)(reportingMessage)
|
|
982
|
+
const reportingKey = {
|
|
983
|
+
id: msgId,
|
|
984
|
+
fromMe: true,
|
|
985
|
+
remoteJid: destinationJid,
|
|
986
|
+
participant: participant?.jid
|
|
987
|
+
}
|
|
988
|
+
const reportingNode = await (0, reporting_utils_1.getMessageReportingToken)(
|
|
989
|
+
encoded,
|
|
990
|
+
reportingMessage,
|
|
991
|
+
reportingKey
|
|
992
|
+
)
|
|
993
|
+
if (reportingNode) {
|
|
994
|
+
stanza.content.push(reportingNode)
|
|
995
|
+
logger.trace({ jid }, 'added reporting token to message')
|
|
996
|
+
}
|
|
997
|
+
} catch (error) {
|
|
998
|
+
logger.warn({ jid, trace: error?.stack }, 'failed to attach reporting token')
|
|
999
|
+
}
|
|
1000
|
+
}
|
|
1001
|
+
|
|
1002
|
+
let didPushAdditional = false
|
|
1003
|
+
if (!isNewsletter && buttonType) {
|
|
1004
|
+
const buttonsNode = getButtonArgs(messages)
|
|
1005
|
+
const filteredButtons = WABinary_1.getBinaryFilteredButtons(additionalNodes ? additionalNodes : [])
|
|
1006
|
+
|
|
1007
|
+
if (filteredButtons) {
|
|
1008
|
+
stanza.content.push(...additionalNodes)
|
|
1009
|
+
didPushAdditional = true
|
|
1010
|
+
} else {
|
|
1011
|
+
stanza.content.push(buttonsNode)
|
|
817
1012
|
}
|
|
818
|
-
} catch (error) {
|
|
819
|
-
logger.warn({ jid, trace: error?.stack }, 'failed to attach reporting token')
|
|
820
1013
|
}
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
1014
|
+
if (!config.aiLabel && (0, WABinary_1.isPnUser)(destinationJid)) {
|
|
1015
|
+
const alreadyHasBizBot =
|
|
1016
|
+
WABinary_1.getBinaryFilteredBizBot(additionalNodes || []) ||
|
|
1017
|
+
WABinary_1.getBinaryFilteredBizBot(stanza.content)
|
|
1018
|
+
if (!alreadyHasBizBot) {
|
|
1019
|
+
stanza.content.push({ tag: 'bot', attrs: { biz_bot: '1' } })
|
|
1020
|
+
}
|
|
1021
|
+
} else if (config.aiLabel && !isGroup && !isStatus && !isNewsletter) {
|
|
1022
|
+
const existingBizBot = WABinary_1.getBinaryFilteredBizBot(additionalNodes || [])
|
|
1023
|
+
if (!existingBizBot) {
|
|
1024
|
+
stanza.content.push({ tag: 'bot', attrs: { biz_bot: '1' } })
|
|
1025
|
+
}
|
|
1026
|
+
}
|
|
1027
|
+
// WA Web never attaches tctoken to peer (AppStateSync) messages — server rejects with 479
|
|
1028
|
+
const isPeerMessage = additionalAttributes?.['category'] === 'peer'
|
|
1029
|
+
const is1on1Send = !isGroup && !isRetryResend && !isStatus && !isNewsletter && !isPeerMessage
|
|
1030
|
+
// Resolve destination to LID for tctoken storage — matches Signal session key pattern
|
|
1031
|
+
const tcTokenJid = is1on1Send
|
|
1032
|
+
? await (0, tc_token_utils_1.resolveTcTokenJid)(destinationJid, getLIDForPN)
|
|
1033
|
+
: destinationJid
|
|
1034
|
+
const contactTcTokenData = is1on1Send ? await authState.keys.get('tctoken', [tcTokenJid]) : {}
|
|
1035
|
+
const existingTokenEntry = contactTcTokenData[tcTokenJid]
|
|
1036
|
+
let tcTokenBuffer = existingTokenEntry?.token
|
|
1037
|
+
// Treat expired tokens the same as missing — clear from cache
|
|
1038
|
+
if (tcTokenBuffer?.length && (0, tc_token_utils_1.isTcTokenExpired)(existingTokenEntry?.timestamp)) {
|
|
1039
|
+
logger.debug({ jid: destinationJid, timestamp: existingTokenEntry?.timestamp }, 'tctoken expired, clearing')
|
|
1040
|
+
tcTokenBuffer = undefined
|
|
1041
|
+
const cleared =
|
|
1042
|
+
existingTokenEntry?.senderTimestamp !== undefined
|
|
1043
|
+
? { token: Buffer.alloc(0), senderTimestamp: existingTokenEntry.senderTimestamp }
|
|
1044
|
+
: null
|
|
1045
|
+
try {
|
|
1046
|
+
await authState.keys.set({ tctoken: { [tcTokenJid]: cleared } })
|
|
1047
|
+
} catch (err) {
|
|
1048
|
+
logger.debug({ jid: destinationJid, err: err?.message }, 'failed to persist tctoken expiry cleanup')
|
|
1049
|
+
}
|
|
1050
|
+
}
|
|
1051
|
+
if (tcTokenBuffer?.length && sock.serverProps.privacyTokenOn1to1) {
|
|
1052
|
+
stanza.content.push({
|
|
1053
|
+
tag: 'tctoken',
|
|
1054
|
+
attrs: {},
|
|
1055
|
+
content: tcTokenBuffer
|
|
1056
|
+
})
|
|
1057
|
+
}
|
|
1058
|
+
if (additionalNodes && additionalNodes.length > 0 && !didPushAdditional) {
|
|
827
1059
|
stanza.content.push(...additionalNodes)
|
|
828
|
-
didPushAdditional = true
|
|
829
|
-
} else {
|
|
830
|
-
stanza.content.push(buttonsNode)
|
|
831
1060
|
}
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
if
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
if (!existingBizBot) stanza.content.push({ tag: 'bot', attrs: { biz_bot: '1' } })
|
|
839
|
-
}
|
|
840
|
-
const isPeerMessage = additionalAttributes?.['category'] === 'peer'
|
|
841
|
-
const is1on1Send =!isGroup &&!isRetryResend &&!isStatus &&!isNewsletter &&!isPeerMessage
|
|
842
|
-
const tcTokenJid = is1on1Send? await resolveTcTokenJid(destinationJid, getLIDForPN) : destinationJid
|
|
843
|
-
const contactTcTokenData = is1on1Send? await authState.keys.get('tctoken', [tcTokenJid]) : {}
|
|
844
|
-
const existingTokenEntry = contactTcTokenData[tcTokenJid]
|
|
845
|
-
let tcTokenBuffer = existingTokenEntry?.token
|
|
846
|
-
if (tcTokenBuffer?.length && isTcTokenExpired(existingTokenEntry?.timestamp)) {
|
|
847
|
-
logger.debug({ jid: destinationJid, timestamp: existingTokenEntry?.timestamp }, 'tctoken expired, clearing')
|
|
848
|
-
tcTokenBuffer = undefined
|
|
849
|
-
const cleared = existingTokenEntry?.senderTimestamp!== undefined? { token: Buffer.alloc(0), senderTimestamp: existingTokenEntry.senderTimestamp } : null
|
|
850
|
-
try {
|
|
851
|
-
await authState.keys.set({ tctoken: { [tcTokenJid]: cleared } })
|
|
852
|
-
} catch (err) {
|
|
853
|
-
logger.debug({ jid: destinationJid, err: err?.message }, 'failed to persist tctoken expiry cleanup')
|
|
1061
|
+
logger.debug({ msgId }, `sending message to ${participants.length} devices`)
|
|
1062
|
+
await sendNode(stanza)
|
|
1063
|
+
// Register messageSecret immediately at send time so msmsg replies can be
|
|
1064
|
+
// decrypted even if they arrive before our own message echo comes back.
|
|
1065
|
+
if (message.messageContextInfo?.messageSecret) {
|
|
1066
|
+
setBotMessageSecret(msgId, message.messageContextInfo.messageSecret, destinationJid)
|
|
854
1067
|
}
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
}
|
|
1068
|
+
// Fire-and-forget: issue our token to the contact AFTER message send.
|
|
1069
|
+
const isProtocolMsg = !!(0, Utils_1.normalizeMessageContent)(message)?.protocolMessage
|
|
1070
|
+
const isBotOrPSA =
|
|
1071
|
+
destinationJid === WABinary_1.PSA_WID ||
|
|
1072
|
+
(0, WABinary_1.isJidBot)(destinationJid) ||
|
|
1073
|
+
(0, WABinary_1.isJidMetaAI)(destinationJid)
|
|
1074
|
+
if (
|
|
1075
|
+
is1on1Send &&
|
|
1076
|
+
!isProtocolMsg &&
|
|
1077
|
+
!isBotOrPSA &&
|
|
1078
|
+
(0, tc_token_utils_1.shouldSendNewTcToken)(existingTokenEntry?.senderTimestamp) &&
|
|
1079
|
+
!inFlightTcTokenIssuance.has(tcTokenJid)
|
|
1080
|
+
) {
|
|
1081
|
+
inFlightTcTokenIssuance.add(tcTokenJid)
|
|
1082
|
+
const issueTimestamp = (0, Utils_1.unixTimestampSeconds)()
|
|
1083
|
+
const getPNForLID = signalRepository.lidMapping.getPNForLID.bind(signalRepository.lidMapping)
|
|
1084
|
+
;(0, tc_token_utils_1.resolveIssuanceJid)(
|
|
1085
|
+
destinationJid,
|
|
1086
|
+
sock.serverProps.lidTrustedTokenIssueToLid,
|
|
1087
|
+
getLIDForPN,
|
|
1088
|
+
getPNForLID
|
|
1089
|
+
)
|
|
1090
|
+
.then(issueJid => issuePrivacyTokens([issueJid], issueTimestamp))
|
|
1091
|
+
.then(async result => {
|
|
1092
|
+
await (0, tc_token_utils_1.storeTcTokensFromIqResult)({
|
|
1093
|
+
result,
|
|
1094
|
+
fallbackJid: tcTokenJid,
|
|
1095
|
+
keys: authState.keys,
|
|
1096
|
+
getLIDForPN
|
|
1097
|
+
})
|
|
1098
|
+
const currentData = await authState.keys.get('tctoken', [tcTokenJid])
|
|
1099
|
+
const currentEntry = currentData[tcTokenJid]
|
|
1100
|
+
const indexWrite = await (0, tc_token_utils_1.buildMergedTcTokenIndexWrite)(authState.keys, [tcTokenJid])
|
|
1101
|
+
await authState.keys.set({
|
|
1102
|
+
tctoken: {
|
|
1103
|
+
[tcTokenJid]: {
|
|
1104
|
+
token: Buffer.alloc(0),
|
|
1105
|
+
...currentEntry,
|
|
1106
|
+
senderTimestamp: issueTimestamp
|
|
1107
|
+
},
|
|
1108
|
+
...indexWrite
|
|
1109
|
+
}
|
|
1110
|
+
})
|
|
1111
|
+
})
|
|
1112
|
+
.catch(err => {
|
|
1113
|
+
logger.debug({ jid: destinationJid, err: err?.message }, 'fire-and-forget tctoken issuance failed')
|
|
885
1114
|
})
|
|
1115
|
+
.finally(() => {
|
|
1116
|
+
inFlightTcTokenIssuance.delete(tcTokenJid)
|
|
1117
|
+
})
|
|
1118
|
+
}
|
|
1119
|
+
// Add message to retry cache if enabled
|
|
1120
|
+
if (messageRetryManager && !participant) {
|
|
1121
|
+
messageRetryManager.addRecentMessage(destinationJid, msgId, message)
|
|
1122
|
+
}
|
|
1123
|
+
if (isInterop && !isRetryResend) {
|
|
1124
|
+
await trustInteropContact(destinationJid).catch(err => {
|
|
1125
|
+
logger.debug({ err, jid: destinationJid }, 'failed to trust interop contact')
|
|
886
1126
|
})
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
if (
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
}
|
|
983
|
-
else if (message.interactiveMessage?.nativeFlowMessage?.buttons?.[0]?.name === 'payment_info') {
|
|
984
|
-
return 'payment_info'
|
|
985
|
-
} else if (message.interactiveMessage?.nativeFlowMessage?.buttons?.[0]?.name === 'payment_key_info') {
|
|
986
|
-
return 'payment_key_info'
|
|
987
|
-
} else if (message.interactiveMessage?.nativeFlowMessage?.buttons?.[0]?.name === 'payment_status') {
|
|
988
|
-
return 'payment_status'
|
|
989
|
-
}
|
|
990
|
-
else if (message.interactiveMessage?.nativeFlowMessage?.buttons?.[0]?.name === 'payment_method') {
|
|
991
|
-
return 'payment_method'
|
|
992
|
-
}
|
|
993
|
-
else if (message.interactiveMessage?.nativeFlowMessage?.buttons?.[0]?.name === 'catalog_message') {
|
|
994
|
-
return 'catalog_message'
|
|
995
|
-
}
|
|
996
|
-
else if (message.interactiveMessage && message.interactiveMessage?.nativeFlowMessage) {
|
|
997
|
-
return 'interactive'
|
|
998
|
-
}
|
|
999
|
-
else if (message.interactiveMessage?.nativeFlowMessage) {
|
|
1000
|
-
return 'native_flow'
|
|
1001
|
-
}
|
|
1002
|
-
};
|
|
1003
|
-
const getButtonArgs = (message) => {
|
|
1127
|
+
}
|
|
1128
|
+
}, meId)
|
|
1129
|
+
return msgId
|
|
1130
|
+
}
|
|
1131
|
+
const getTypeMessage = msg => {
|
|
1132
|
+
const message = Utils_1.normalizeMessageContent(msg)
|
|
1133
|
+
if (message.pollCreationMessage || message.pollCreationMessageV2 || message.pollCreationMessageV3) {
|
|
1134
|
+
return 'poll'
|
|
1135
|
+
} else if (message.reactionMessage) {
|
|
1136
|
+
return 'reaction'
|
|
1137
|
+
} else if (message.eventMessage) {
|
|
1138
|
+
return 'event'
|
|
1139
|
+
} else if (getMediaType(message)) {
|
|
1140
|
+
return 'media'
|
|
1141
|
+
} else {
|
|
1142
|
+
return 'text'
|
|
1143
|
+
}
|
|
1144
|
+
}
|
|
1145
|
+
|
|
1146
|
+
const getMediaType = message => {
|
|
1147
|
+
if (message.imageMessage) {
|
|
1148
|
+
return 'image'
|
|
1149
|
+
} else if (message.stickerMessage) {
|
|
1150
|
+
return message.stickerMessage.isLottie
|
|
1151
|
+
? '1p_sticker'
|
|
1152
|
+
: message.stickerMessage.isAvatar
|
|
1153
|
+
? 'avatar_sticker'
|
|
1154
|
+
: 'sticker'
|
|
1155
|
+
} else if (message.videoMessage) {
|
|
1156
|
+
return message.videoMessage.gifPlayback ? 'gif' : 'video'
|
|
1157
|
+
} else if (message.audioMessage) {
|
|
1158
|
+
return message.audioMessage.ptt ? 'ptt' : 'audio'
|
|
1159
|
+
} else if (message.ptvMessage) {
|
|
1160
|
+
return 'ptv'
|
|
1161
|
+
} else if (message.albumMessage) {
|
|
1162
|
+
return 'collection'
|
|
1163
|
+
} else if (message.contactMessage) {
|
|
1164
|
+
return 'vcard'
|
|
1165
|
+
} else if (message.documentMessage) {
|
|
1166
|
+
return 'document'
|
|
1167
|
+
} else if (message.stickerPackMessage) {
|
|
1168
|
+
return 'sticker_pack'
|
|
1169
|
+
} else if (message.contactsArrayMessage) {
|
|
1170
|
+
return 'contact_array'
|
|
1171
|
+
} else if (message.locationMessage) {
|
|
1172
|
+
return 'location'
|
|
1173
|
+
} else if (message.liveLocationMessage) {
|
|
1174
|
+
return 'livelocation'
|
|
1175
|
+
} else if (message.listMessage) {
|
|
1176
|
+
return 'list'
|
|
1177
|
+
} else if (message.listResponseMessage) {
|
|
1178
|
+
return 'list_response'
|
|
1179
|
+
} else if (message.buttonsResponseMessage) {
|
|
1180
|
+
return 'buttons_response'
|
|
1181
|
+
} else if (message.orderMessage) {
|
|
1182
|
+
return 'order'
|
|
1183
|
+
} else if (message.productMessage) {
|
|
1184
|
+
return 'product'
|
|
1185
|
+
} else if (message.interactiveResponseMessage) {
|
|
1186
|
+
return 'native_flow_response'
|
|
1187
|
+
} else if (/https:\/\/wa\.me\/c\/\d+/.test(message.extendedTextMessage?.text)) {
|
|
1188
|
+
return 'cataloglink'
|
|
1189
|
+
} else if (/https:\/\/wa\.me\/p\/\d+\/\d+/.test(message.extendedTextMessage?.text)) {
|
|
1190
|
+
return 'productlink'
|
|
1191
|
+
} else if (message.extendedTextMessage?.matchedText || message.groupInviteMessage) {
|
|
1192
|
+
return 'url'
|
|
1193
|
+
}
|
|
1194
|
+
}
|
|
1195
|
+
const getButtonType = message => {
|
|
1196
|
+
if (message.listMessage) {
|
|
1197
|
+
return 'list'
|
|
1198
|
+
} else if (message.buttonsMessage) {
|
|
1199
|
+
return 'buttons'
|
|
1200
|
+
} else if (message.interactiveMessage?.nativeFlowMessage) {
|
|
1201
|
+
const firstButtonName = message.interactiveMessage.nativeFlowMessage.buttons?.[0]?.name
|
|
1202
|
+
if (firstButtonName === 'review_and_pay') {
|
|
1203
|
+
return 'review_and_pay'
|
|
1204
|
+
} else if (firstButtonName === 'review_order') {
|
|
1205
|
+
return 'review_order'
|
|
1206
|
+
} else if (firstButtonName === 'payment_info') {
|
|
1207
|
+
return 'payment_info'
|
|
1208
|
+
} else if (firstButtonName === 'payment_status') {
|
|
1209
|
+
return 'payment_status'
|
|
1210
|
+
} else if (firstButtonName === 'payment_method') {
|
|
1211
|
+
return 'payment_method'
|
|
1212
|
+
} else if (firstButtonName === 'pix') {
|
|
1213
|
+
return 'pix'
|
|
1214
|
+
} else if (firstButtonName === 'pay') {
|
|
1215
|
+
return 'pay'
|
|
1216
|
+
}
|
|
1217
|
+
return 'native_flow'
|
|
1218
|
+
}
|
|
1219
|
+
}
|
|
1220
|
+
|
|
1221
|
+
const getButtonArgs = message => {
|
|
1004
1222
|
const nativeFlow = message.interactiveMessage?.nativeFlowMessage
|
|
1005
1223
|
const firstButtonName = nativeFlow?.buttons?.[0]?.name
|
|
1006
1224
|
const nativeFlowSpecials = [
|
|
@@ -1122,282 +1340,469 @@ export const makeMessagesSocket = (config) => {
|
|
|
1122
1340
|
}
|
|
1123
1341
|
}
|
|
1124
1342
|
}
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1343
|
+
const issuePrivacyTokens = async (jids, timestamp) => {
|
|
1344
|
+
const t = (timestamp ?? (0, Utils_1.unixTimestampSeconds)()).toString()
|
|
1345
|
+
const result = await query({
|
|
1346
|
+
tag: 'iq',
|
|
1347
|
+
attrs: {
|
|
1348
|
+
to: WABinary_1.S_WHATSAPP_NET,
|
|
1349
|
+
type: 'set',
|
|
1350
|
+
xmlns: 'privacy'
|
|
1351
|
+
},
|
|
1352
|
+
content: [
|
|
1353
|
+
{
|
|
1354
|
+
tag: 'tokens',
|
|
1355
|
+
attrs: {},
|
|
1356
|
+
content: jids.map(jid => ({
|
|
1357
|
+
tag: 'token',
|
|
1358
|
+
attrs: {
|
|
1359
|
+
jid: (0, WABinary_1.jidNormalizedUser)(jid),
|
|
1360
|
+
t,
|
|
1361
|
+
type: 'trusted_contact'
|
|
1362
|
+
}
|
|
1363
|
+
}))
|
|
1364
|
+
}
|
|
1365
|
+
]
|
|
1366
|
+
})
|
|
1367
|
+
return result
|
|
1368
|
+
}
|
|
1369
|
+
const getPrivacyTokens = issuePrivacyTokens
|
|
1370
|
+
const waUploadToServer = (0, Utils_1.getWAUploadToServer)(config, refreshMediaConn)
|
|
1371
|
+
const interactiveHandler = new interactive_handler_1.InteractiveMessageHandler(waUploadToServer, relayMessage, config, sock)
|
|
1372
|
+
const waitForMsgMediaUpdate = (0, Utils_1.bindWaitForEvent)(ev, 'messages.media-update')
|
|
1373
|
+
return {
|
|
1374
|
+
...sock,
|
|
1375
|
+
issuePrivacyTokens,
|
|
1376
|
+
getPrivacyTokens,
|
|
1377
|
+
assertSessions,
|
|
1378
|
+
relayMessage,
|
|
1379
|
+
sendReceipt,
|
|
1380
|
+
sendReceipts,
|
|
1381
|
+
readMessages,
|
|
1382
|
+
refreshMediaConn,
|
|
1383
|
+
waUploadToServer,
|
|
1384
|
+
fetchPrivacySettings,
|
|
1385
|
+
sendPeerDataOperationMessage,
|
|
1386
|
+
createParticipantNodes,
|
|
1387
|
+
getUSyncDevices,
|
|
1388
|
+
messageRetryManager,
|
|
1389
|
+
updateMemberLabel,
|
|
1390
|
+
updateMediaMessage: async message => {
|
|
1391
|
+
const content = (0, Utils_1.assertMediaContent)(message.message)
|
|
1392
|
+
const mediaKey = content.mediaKey
|
|
1393
|
+
const meId = authState.creds.me.id
|
|
1394
|
+
const node = (0, Utils_1.encryptMediaRetryRequest)(message.key, mediaKey, meId)
|
|
1395
|
+
let error = undefined
|
|
1396
|
+
await Promise.all([
|
|
1397
|
+
sendNode(node),
|
|
1398
|
+
waitForMsgMediaUpdate(async update => {
|
|
1399
|
+
const result = update.find(c => c.key.id === message.key.id)
|
|
1400
|
+
if (result) {
|
|
1401
|
+
if (result.error) {
|
|
1402
|
+
error = result.error
|
|
1403
|
+
} else {
|
|
1404
|
+
try {
|
|
1405
|
+
const media = (0, Utils_1.decryptMediaRetryData)(result.media, mediaKey, result.key.id)
|
|
1406
|
+
if (media.result !== index_js_1.proto.MediaRetryNotification.ResultType.SUCCESS) {
|
|
1407
|
+
const resultStr = index_js_1.proto.MediaRetryNotification.ResultType[media.result]
|
|
1408
|
+
throw new boom_1.Boom(`Media re-upload failed by device (${resultStr})`, {
|
|
1409
|
+
data: media,
|
|
1410
|
+
statusCode: (0, Utils_1.getStatusCodeForMediaRetry)(media.result) || 404
|
|
1411
|
+
})
|
|
1412
|
+
}
|
|
1413
|
+
content.directPath = media.directPath
|
|
1414
|
+
content.url = (0, Utils_1.getUrlFromDirectPath)(content.directPath)
|
|
1415
|
+
logger.debug({ directPath: media.directPath, key: result.key }, 'media update successful')
|
|
1416
|
+
} catch (err) {
|
|
1417
|
+
error = err
|
|
1418
|
+
}
|
|
1419
|
+
}
|
|
1420
|
+
return true
|
|
1421
|
+
}
|
|
1422
|
+
})
|
|
1423
|
+
])
|
|
1424
|
+
if (error) {
|
|
1425
|
+
throw error
|
|
1426
|
+
}
|
|
1427
|
+
ev.emit('messages.update', [{ key: message.key, update: { message: message.message } }])
|
|
1428
|
+
return message
|
|
1429
|
+
},
|
|
1430
|
+
|
|
1431
|
+
sendGroupStatus: async (groupIdsOrContent = [], contentOrGroups = {}, opts = {}) => {
|
|
1432
|
+
const toGroupIdArray = input => {
|
|
1433
|
+
if (Array.isArray(input)) {
|
|
1434
|
+
return input
|
|
1435
|
+
}
|
|
1436
|
+
if (typeof input === 'string') {
|
|
1437
|
+
return [input]
|
|
1438
|
+
}
|
|
1439
|
+
return []
|
|
1440
|
+
}
|
|
1441
|
+
const normalizeGroupJid = value => {
|
|
1442
|
+
if (typeof value !== 'string') {
|
|
1443
|
+
return ''
|
|
1444
|
+
}
|
|
1445
|
+
const trimmed = value.trim()
|
|
1446
|
+
if (!trimmed) {
|
|
1447
|
+
return ''
|
|
1448
|
+
}
|
|
1449
|
+
if (/^\d{8,}$/.test(trimmed)) {
|
|
1450
|
+
return `${trimmed}@g.us`
|
|
1451
|
+
}
|
|
1452
|
+
return trimmed
|
|
1453
|
+
}
|
|
1454
|
+
const useNewSignature = Array.isArray(groupIdsOrContent) || typeof groupIdsOrContent === 'string'
|
|
1455
|
+
const groupIds = useNewSignature ? toGroupIdArray(groupIdsOrContent) : toGroupIdArray(contentOrGroups)
|
|
1456
|
+
const content = useNewSignature ? contentOrGroups || {} : groupIdsOrContent || {}
|
|
1457
|
+
const sendOpts = opts || {}
|
|
1458
|
+
const groupJids = groupIds.map(normalizeGroupJid).filter(jid => (0, WABinary_1.isJidGroup)(jid))
|
|
1459
|
+
const nonGroupJids = groupIds.filter(jid => !(0, WABinary_1.isJidGroup)(normalizeGroupJid(jid)))
|
|
1460
|
+
if (nonGroupJids.length) {
|
|
1461
|
+
logger.warn({ nonGroupJids }, 'ignoring non-group JIDs in sendGroupStatus')
|
|
1462
|
+
}
|
|
1463
|
+
if (!groupJids.length) {
|
|
1464
|
+
return []
|
|
1465
|
+
}
|
|
1466
|
+
const { allUsers } = await resolveStatusAudience(groupJids, false)
|
|
1467
|
+
if (!allUsers.length) {
|
|
1468
|
+
return []
|
|
1469
|
+
}
|
|
1470
|
+
const msg = await generateStatusMessage({
|
|
1471
|
+
...content,
|
|
1472
|
+
backgroundColor: content.backgroundColor || getRandomHexColor(),
|
|
1473
|
+
font: normalizeStatusFont(content.font, logger)
|
|
1474
|
+
})
|
|
1475
|
+
await relayMessage(WABinary_1.STORIES_JID, msg.message, {
|
|
1476
|
+
messageId: msg.key.id,
|
|
1477
|
+
statusJidList: allUsers,
|
|
1478
|
+
...(sendOpts.relay || {})
|
|
1479
|
+
})
|
|
1480
|
+
return groupJids
|
|
1481
|
+
},
|
|
1482
|
+
sendAlbumMessage: async (jid, medias, options = {}) => {
|
|
1483
|
+
const userJid = authState.creds.me.id
|
|
1484
|
+
for (const media of medias) {
|
|
1485
|
+
if (!media.image && !media.video) throw new TypeError(`medias[i] must have image or video property`)
|
|
1486
|
+
}
|
|
1487
|
+
if (medias.length < 2) throw new RangeError('Minimum 2 media')
|
|
1488
|
+
const time = options.delay || 500
|
|
1489
|
+
delete options.delay
|
|
1490
|
+
const album = await (0, Utils_1.generateWAMessageFromContent)(
|
|
1491
|
+
jid,
|
|
1492
|
+
{
|
|
1493
|
+
albumMessage: {
|
|
1494
|
+
expectedImageCount: medias.filter(media => media.image).length,
|
|
1495
|
+
expectedVideoCount: medias.filter(media => media.video).length,
|
|
1496
|
+
...options
|
|
1497
|
+
}
|
|
1498
|
+
},
|
|
1499
|
+
{ userJid, ...options }
|
|
1500
|
+
)
|
|
1501
|
+
await relayMessage(jid, album.message, { messageId: album.key.id })
|
|
1502
|
+
let mediaHandle
|
|
1503
|
+
let msg
|
|
1504
|
+
for (const i in medias) {
|
|
1505
|
+
const media = medias[i]
|
|
1506
|
+
if (media.image) {
|
|
1507
|
+
msg = await (0, Utils_1.generateWAMessage)(
|
|
1508
|
+
jid,
|
|
1509
|
+
{
|
|
1510
|
+
image: media.image,
|
|
1511
|
+
...media,
|
|
1512
|
+
...options
|
|
1513
|
+
},
|
|
1514
|
+
{
|
|
1515
|
+
userJid,
|
|
1516
|
+
upload: async (readStream, opts) => {
|
|
1517
|
+
const up = await waUploadToServer(readStream, {
|
|
1518
|
+
...opts,
|
|
1519
|
+
newsletter: (0, WABinary_1.isJidNewsletter)(jid)
|
|
1520
|
+
})
|
|
1521
|
+
mediaHandle = up.handle
|
|
1522
|
+
return up
|
|
1523
|
+
},
|
|
1524
|
+
...options
|
|
1525
|
+
}
|
|
1526
|
+
)
|
|
1527
|
+
} else if (media.video) {
|
|
1528
|
+
msg = await (0, Utils_1.generateWAMessage)(
|
|
1529
|
+
jid,
|
|
1530
|
+
{
|
|
1531
|
+
video: media.video,
|
|
1532
|
+
...media,
|
|
1533
|
+
...options
|
|
1534
|
+
},
|
|
1535
|
+
{
|
|
1536
|
+
userJid,
|
|
1537
|
+
upload: async (readStream, opts) => {
|
|
1538
|
+
const up = await waUploadToServer(readStream, {
|
|
1539
|
+
...opts,
|
|
1540
|
+
newsletter: (0, WABinary_1.isJidNewsletter)(jid)
|
|
1541
|
+
})
|
|
1542
|
+
mediaHandle = up.handle
|
|
1543
|
+
return up
|
|
1544
|
+
},
|
|
1545
|
+
...options
|
|
1546
|
+
}
|
|
1547
|
+
)
|
|
1548
|
+
}
|
|
1549
|
+
if (msg) {
|
|
1550
|
+
msg.message.messageContextInfo = {
|
|
1551
|
+
messageAssociation: {
|
|
1552
|
+
associationType: 1,
|
|
1553
|
+
parentMessageKey: album.key
|
|
1554
|
+
}
|
|
1555
|
+
}
|
|
1556
|
+
}
|
|
1557
|
+
await relayMessage(jid, msg.message, { messageId: msg.key.id })
|
|
1558
|
+
await (0, Utils_1.delay)(time)
|
|
1559
|
+
}
|
|
1560
|
+
return album
|
|
1561
|
+
},
|
|
1562
|
+
|
|
1563
|
+
sendStatusMention: async (content, jids = []) => {
|
|
1564
|
+
return await interactiveHandler.sendStatusWhatsApp(content, jids)
|
|
1565
|
+
},
|
|
1566
|
+
sendTable: async (jid, title, headers, rows, quoted, options = {}) => {
|
|
1567
|
+
const { message, messageId } = message_composer_1.generateTableContent(title, headers, rows, quoted, options)
|
|
1568
|
+
await relayMessage(jid, message, { messageId })
|
|
1227
1569
|
return { message, messageId }
|
|
1228
1570
|
},
|
|
1229
1571
|
sendList: async (jid, title, items, quoted, options = {}) => {
|
|
1230
|
-
const { message, messageId } =
|
|
1231
|
-
await relayMessage(jid, message, { messageId
|
|
1572
|
+
const { message, messageId } = message_composer_1.generateListContent(title, items, quoted, options)
|
|
1573
|
+
await relayMessage(jid, message, { messageId })
|
|
1232
1574
|
return { message, messageId }
|
|
1233
1575
|
},
|
|
1234
1576
|
sendCodeBlock: async (jid, code, quoted, options = {}) => {
|
|
1235
|
-
const { message, messageId } =
|
|
1236
|
-
await relayMessage(jid, message, { messageId
|
|
1577
|
+
const { message, messageId } = message_composer_1.generateCodeBlockContent(code, quoted, options)
|
|
1578
|
+
await relayMessage(jid, message, { messageId })
|
|
1237
1579
|
return { message, messageId }
|
|
1238
1580
|
},
|
|
1239
1581
|
sendLatex: async (jid, quoted, options) => {
|
|
1240
|
-
const { message, messageId } =
|
|
1241
|
-
await relayMessage(jid, message, { messageId
|
|
1582
|
+
const { message, messageId } = message_composer_1.generateLatexContent(quoted, options)
|
|
1583
|
+
await relayMessage(jid, message, { messageId })
|
|
1242
1584
|
return { message, messageId }
|
|
1243
1585
|
},
|
|
1244
1586
|
sendLatexImage: async (jid, quoted, options, renderLatexToPng, uploadFn) => {
|
|
1245
|
-
const { message, messageId } = await
|
|
1587
|
+
const { message, messageId } = await message_composer_1.generateLatexImageContent(
|
|
1246
1588
|
quoted,
|
|
1247
1589
|
options,
|
|
1248
1590
|
uploadFn,
|
|
1249
1591
|
renderLatexToPng
|
|
1250
1592
|
)
|
|
1251
|
-
await relayMessage(jid, message, { messageId
|
|
1593
|
+
await relayMessage(jid, message, { messageId })
|
|
1252
1594
|
return { message, messageId }
|
|
1253
1595
|
},
|
|
1254
1596
|
sendLatexInlineImage: async (jid, quoted, options, renderLatexToPng, uploadFn) => {
|
|
1255
|
-
const { message, messageId } = await
|
|
1597
|
+
const { message, messageId } = await message_composer_1.generateLatexInlineImageContent(
|
|
1256
1598
|
quoted,
|
|
1257
1599
|
options,
|
|
1258
1600
|
uploadFn,
|
|
1259
1601
|
renderLatexToPng
|
|
1260
1602
|
)
|
|
1261
|
-
await relayMessage(jid, message, { messageId
|
|
1603
|
+
await relayMessage(jid, message, { messageId })
|
|
1262
1604
|
return { message, messageId }
|
|
1263
1605
|
},
|
|
1264
|
-
captureUnifiedResponse:
|
|
1606
|
+
captureUnifiedResponse: message_composer_1.captureUnifiedResponse,
|
|
1265
1607
|
sendUnifiedResponse: async (jid, quoted, captured) => {
|
|
1266
|
-
const { message, messageId } =
|
|
1267
|
-
await relayMessage(jid, message, { messageId
|
|
1608
|
+
const { message, messageId } = message_composer_1.generateUnifiedResponseContent(quoted, captured)
|
|
1609
|
+
await relayMessage(jid, message, { messageId })
|
|
1268
1610
|
return { message, messageId }
|
|
1269
1611
|
},
|
|
1270
1612
|
sendRichMessage: async (jid, submessages, quoted, options = {}) => {
|
|
1271
|
-
const { message, messageId } =
|
|
1272
|
-
await relayMessage(jid, message, { messageId
|
|
1613
|
+
const { message, messageId } = message_composer_1.generateRichMessageContent(submessages, quoted, options)
|
|
1614
|
+
await relayMessage(jid, message, { messageId })
|
|
1273
1615
|
return { message, messageId }
|
|
1274
1616
|
},
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1617
|
+
sendMessage: async (jid, content, options = {}) => {
|
|
1618
|
+
const userJid = authState.creds.me.id
|
|
1619
|
+
const iosBros = config.browser[0] === "iOS" || config.browser[1] === "Safari";
|
|
1620
|
+
const mId = iosBros ? Utils_1.generateIOSMessageID() : msgId ?? (0, Utils_1.generateMessageIDV2)(userJid)
|
|
1621
|
+
// ── Normalize: buttons[].nativeFlowInfo -> interactiveButtons ──────
|
|
1622
|
+
if (
|
|
1623
|
+
typeof content === 'object' &&
|
|
1624
|
+
Array.isArray(content.buttons) &&
|
|
1625
|
+
content.buttons.length > 0 &&
|
|
1626
|
+
content.buttons.some(b => b.nativeFlowInfo)
|
|
1627
|
+
) {
|
|
1628
|
+
const interactiveButtons = content.buttons.map(b => {
|
|
1629
|
+
if (b.nativeFlowInfo) {
|
|
1630
|
+
return {
|
|
1631
|
+
name: b.nativeFlowInfo.name,
|
|
1632
|
+
buttonParamsJson: b.nativeFlowInfo.paramsJson || '{}'
|
|
1633
|
+
}
|
|
1634
|
+
}
|
|
1635
|
+
return {
|
|
1636
|
+
name: 'quick_reply',
|
|
1637
|
+
buttonParamsJson: JSON.stringify({
|
|
1638
|
+
display_text: b.buttonText?.displayText || b.buttonId || 'Button',
|
|
1639
|
+
id: b.buttonId || b.buttonText?.displayText || 'btn'
|
|
1640
|
+
})
|
|
1641
|
+
}
|
|
1642
|
+
})
|
|
1643
|
+
const { buttons, headerType, viewOnce, ...rest } = content
|
|
1644
|
+
content = { ...rest, interactiveButtons }
|
|
1645
|
+
}
|
|
1646
|
+
if (
|
|
1647
|
+
typeof content === 'object' &&
|
|
1648
|
+
'disappearingMessagesInChat' in content &&
|
|
1649
|
+
typeof content['disappearingMessagesInChat'] !== 'undefined' &&
|
|
1650
|
+
(0, WABinary_1.isJidGroup)(jid)
|
|
1651
|
+
) {
|
|
1652
|
+
const { disappearingMessagesInChat } = content
|
|
1653
|
+
const value =
|
|
1654
|
+
typeof disappearingMessagesInChat === 'boolean'
|
|
1655
|
+
? disappearingMessagesInChat
|
|
1656
|
+
? WA_DEFAULT_EPHEMERAL
|
|
1657
|
+
: 0
|
|
1658
|
+
: disappearingMessagesInChat
|
|
1659
|
+
await groupToggleEphemeral(jid, value)
|
|
1660
|
+
} else if (interactiveHandler.detectType(content)) {
|
|
1661
|
+
const { quoted } = options
|
|
1662
|
+
const messageType = interactiveHandler.detectType(content)
|
|
1663
|
+
switch (messageType) {
|
|
1664
|
+
case 'PAYMENT': {
|
|
1665
|
+
const paymentContent = await interactiveHandler.handlePayment(content, quoted)
|
|
1666
|
+
return await relayMessage(jid, paymentContent, {
|
|
1667
|
+
messageId: mId
|
|
1668
|
+
})
|
|
1669
|
+
}
|
|
1670
|
+
case 'PRODUCT': {
|
|
1671
|
+
const productContent = await interactiveHandler.handleProduct(content, jid, quoted)
|
|
1672
|
+
const productMsg = await (0, Utils_1.generateWAMessageFromContent)(jid, productContent, { quoted, userJid })
|
|
1673
|
+
return await relayMessage(jid, productMsg.message, {
|
|
1674
|
+
messageId: productMsg.key.id
|
|
1675
|
+
})
|
|
1676
|
+
}
|
|
1677
|
+
case 'INTERACTIVE': {
|
|
1678
|
+
const interactiveContent = await interactiveHandler.handleInteractive(content, jid, quoted)
|
|
1679
|
+
const interactiveMsg = await (0, Utils_1.generateWAMessageFromContent)(jid, interactiveContent, {
|
|
1680
|
+
quoted,
|
|
1681
|
+
userJid
|
|
1682
|
+
})
|
|
1683
|
+
return await relayMessage(jid, interactiveMsg.message, {
|
|
1684
|
+
messageId: interactiveMsg.key.id
|
|
1685
|
+
})
|
|
1686
|
+
}
|
|
1687
|
+
case 'ALBUM':
|
|
1688
|
+
return await interactiveHandler.handleAlbum(content, jid, quoted)
|
|
1689
|
+
case 'EVENT':
|
|
1690
|
+
return await interactiveHandler.handleEvent(content, jid, quoted)
|
|
1691
|
+
case 'POLL_RESULT':
|
|
1692
|
+
return await interactiveHandler.handlePollResult(content, jid, quoted)
|
|
1693
|
+
case 'GROUP_STORY':
|
|
1694
|
+
return await interactiveHandler.handleGroupStory(content, jid, quoted)
|
|
1695
|
+
case 'ORDER':
|
|
1696
|
+
return await interactiveHandler.handleOrderMessage(content, jid, quoted)
|
|
1697
|
+
case 'GROUP_LABEL':
|
|
1698
|
+
return await interactiveHandler.handleGbLabel(content, jid)
|
|
1699
|
+
}
|
|
1700
|
+
} else {
|
|
1701
|
+
let mediaHandle
|
|
1702
|
+
const fullMsg = await (0, Utils_1.generateWAMessage)(jid, content, {
|
|
1703
|
+
logger,
|
|
1704
|
+
userJid,
|
|
1705
|
+
getUrlInfo: text =>
|
|
1706
|
+
(0, link_preview_1.getUrlInfo)(text, {
|
|
1707
|
+
thumbnailWidth: linkPreviewImageThumbnailWidth,
|
|
1708
|
+
fetchOpts: {
|
|
1709
|
+
timeout: 3000,
|
|
1710
|
+
...(httpRequestOptions || {})
|
|
1711
|
+
},
|
|
1712
|
+
logger,
|
|
1713
|
+
uploadImage: generateHighQualityLinkPreview ? waUploadToServer : undefined
|
|
1714
|
+
}),
|
|
1715
|
+
//TODO: CACHE
|
|
1716
|
+
getProfilePicUrl: sock.profilePictureUrl,
|
|
1717
|
+
getCallLink: sock.createCallLink,
|
|
1718
|
+
newsletter: (0, WABinary_1.isJidNewsletter)(jid),
|
|
1719
|
+
upload: async (encFilePath, opts) => {
|
|
1720
|
+
const up = await waUploadToServer(encFilePath, {
|
|
1721
|
+
...opts,
|
|
1722
|
+
newsletter: (0, WABinary_1.isJidNewsletter)(jid)
|
|
1723
|
+
})
|
|
1724
|
+
mediaHandle = up.handle
|
|
1725
|
+
return up
|
|
1726
|
+
},
|
|
1727
|
+
mediaCache: config.mediaCache,
|
|
1728
|
+
options: config.options,
|
|
1729
|
+
messageId: mId,
|
|
1730
|
+
...options
|
|
1731
|
+
})
|
|
1732
|
+
if (!mediaHandle) {
|
|
1733
|
+
const msgContent = fullMsg.message
|
|
1734
|
+
const msgTypes = ['audioMessage', 'imageMessage', 'videoMessage', 'documentMessage', 'stickerMessage']
|
|
1735
|
+
for (const t of msgTypes) {
|
|
1736
|
+
if (msgContent?.[t]?._uploadHandle) {
|
|
1737
|
+
mediaHandle = msgContent[t]._uploadHandle
|
|
1738
|
+
|
|
1739
|
+
delete msgContent[t]._uploadHandle
|
|
1740
|
+
break
|
|
1741
|
+
}
|
|
1742
|
+
}
|
|
1743
|
+
}
|
|
1744
|
+
const isEventMsg = 'event' in content && !!content.event
|
|
1745
|
+
const isDeleteMsg = 'delete' in content && !!content.delete
|
|
1746
|
+
const isEditMsg = 'edit' in content && !!content.edit
|
|
1747
|
+
const isPinMsg = 'pin' in content && !!content.pin
|
|
1748
|
+
const isPollMessage = 'poll' in content && !!content.poll
|
|
1749
|
+
const additionalAttributes = {}
|
|
1750
|
+
const additionalNodes = []
|
|
1751
|
+
if (isDeleteMsg) {
|
|
1752
|
+
if ((0, WABinary_1.isJidGroup)(content.delete?.remoteJid) && !content.delete?.fromMe) {
|
|
1753
|
+
additionalAttributes.edit = '8'
|
|
1754
|
+
} else {
|
|
1755
|
+
additionalAttributes.edit = '7'
|
|
1756
|
+
}
|
|
1757
|
+
} else if (isEditMsg) {
|
|
1758
|
+
additionalAttributes.edit = '1'
|
|
1759
|
+
} else if (isPinMsg) {
|
|
1760
|
+
additionalAttributes.edit = '2'
|
|
1761
|
+
} else if (isPollMessage) {
|
|
1762
|
+
additionalNodes.push({
|
|
1763
|
+
tag: 'meta',
|
|
1764
|
+
attrs: {
|
|
1765
|
+
polltype: 'creation'
|
|
1766
|
+
}
|
|
1767
|
+
})
|
|
1768
|
+
} else if (isEventMsg) {
|
|
1769
|
+
additionalNodes.push({
|
|
1770
|
+
tag: 'meta',
|
|
1771
|
+
attrs: {
|
|
1772
|
+
event_type: 'creation'
|
|
1773
|
+
}
|
|
1774
|
+
})
|
|
1775
|
+
}
|
|
1776
|
+
const buttonType = getButtonType(fullMsg.message)
|
|
1777
|
+
if (content?.audio && options?.contextInfo) {
|
|
1778
|
+
const msgContent = fullMsg.message
|
|
1779
|
+
if (msgContent?.audioMessage) {
|
|
1780
|
+
msgContent.audioMessage.contextInfo = options.contextInfo
|
|
1781
|
+
}
|
|
1782
|
+
}
|
|
1783
|
+
if (buttonType) {
|
|
1784
|
+
const btnNode = getButtonArgs(fullMsg.message)
|
|
1785
|
+
if (btnNode) additionalNodes.push(btnNode)
|
|
1786
|
+
}
|
|
1787
|
+
if (mediaHandle) {
|
|
1788
|
+
additionalAttributes['media_id'] = mediaHandle
|
|
1789
|
+
}
|
|
1790
|
+
await relayMessage(jid, fullMsg.message, {
|
|
1791
|
+
messageId: fullMsg.key.id,
|
|
1792
|
+
useCachedGroupMetadata: options.useCachedGroupMetadata,
|
|
1793
|
+
additionalAttributes,
|
|
1794
|
+
statusJidList: options.statusJidList,
|
|
1795
|
+
additionalNodes
|
|
1796
|
+
})
|
|
1797
|
+
if (config.emitOwnEvents) {
|
|
1798
|
+
process.nextTick(async () => {
|
|
1799
|
+
await messageMutex.mutex(() => upsertMessage(fullMsg, 'append'))
|
|
1800
|
+
})
|
|
1801
|
+
}
|
|
1802
|
+
return fullMsg
|
|
1803
|
+
}
|
|
1804
|
+
},
|
|
1805
|
+
sendMessageMembers: async (jid, message, options = {}) => {
|
|
1401
1806
|
const {
|
|
1402
1807
|
messageId: idm,
|
|
1403
1808
|
quoted,
|
|
@@ -1412,13 +1817,10 @@ export const makeMessagesSocket = (config) => {
|
|
|
1412
1817
|
const messages = Utils_1.normalizeMessageContent(message);
|
|
1413
1818
|
const groupData = cachedGroupMetadata? await cachedGroupMetadata(jid) : await groupMetadata(jid);
|
|
1414
1819
|
const isLid = groupData.addressingMode === "lid";
|
|
1415
|
-
const
|
|
1820
|
+
const isAdmin = groupData.participants.filter((x) => x.admin !== null).map((y) => y.id)
|
|
1416
1821
|
let participantJids = groupData.participants.map(z => z.id);
|
|
1417
1822
|
if (onlyMember) {
|
|
1418
|
-
|
|
1419
|
-
// Cek .length supaya fallback ke semua member beneran jalan
|
|
1420
|
-
// kalau data admin kosong, bukan diam-diam kirim ke 0 orang.
|
|
1421
|
-
participantJids = adminJids.length ? adminJids : participantJids;
|
|
1823
|
+
participantJids = isAdmin ? isAdmin : participantJids;
|
|
1422
1824
|
}
|
|
1423
1825
|
logger.info(`Sending message to ${participantJids.length} members from ${jid}`);
|
|
1424
1826
|
for (let i = 0; i < participantJids.length; i++) {
|
|
@@ -1431,9 +1833,7 @@ export const makeMessagesSocket = (config) => {
|
|
|
1431
1833
|
quoted
|
|
1432
1834
|
})
|
|
1433
1835
|
await relayMessage(jid, fullMsg.message, {
|
|
1434
|
-
messageId: fullMsg.key.id
|
|
1435
|
-
useUserDevicesCache,
|
|
1436
|
-
noSelfSync: options.noSelfSync
|
|
1836
|
+
messageId: fullMsg.key.id
|
|
1437
1837
|
});
|
|
1438
1838
|
logger.debug(`Message successfully sent to ${jid}`);
|
|
1439
1839
|
if (delayMs && i < participantJids.length - 1) {
|
|
@@ -1448,6 +1848,6 @@ export const makeMessagesSocket = (config) => {
|
|
|
1448
1848
|
message
|
|
1449
1849
|
}, null, 4);
|
|
1450
1850
|
}
|
|
1451
|
-
|
|
1452
|
-
}
|
|
1453
|
-
|
|
1851
|
+
}
|
|
1852
|
+
}
|
|
1853
|
+
exports.makeMessagesSocket = makeMessagesSocket
|