@jkt48connect-corp/baileys 7.4.5 → 7.4.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/WAProto/CompanionReg/CompanionReg.d.ts +6 -0
  2. package/WAProto/CompanionReg/CompanionReg.js +36 -0
  3. package/WAProto/CompanionReg/CompanionReg.proto +1 -0
  4. package/WAProto/E2E/E2E.d.ts +434 -6
  5. package/WAProto/E2E/E2E.js +1427 -2
  6. package/WAProto/E2E/E2E.proto +33 -0
  7. package/WAProto/HistorySync/HistorySync.d.ts +434 -6
  8. package/WAProto/HistorySync/HistorySync.js +1427 -2
  9. package/WAProto/MdStorageMsgRowOpaqueData/MdStorageMsgRowOpaqueData.d.ts +434 -6
  10. package/WAProto/MdStorageMsgRowOpaqueData/MdStorageMsgRowOpaqueData.js +1427 -2
  11. package/WAProto/StatusAttributions/StatusAttributions.d.ts +95 -3
  12. package/WAProto/StatusAttributions/StatusAttributions.js +270 -2
  13. package/WAProto/StatusAttributions/StatusAttributions.proto +8 -0
  14. package/WAProto/Web/Web.d.ts +434 -6
  15. package/WAProto/Web/Web.js +1427 -2
  16. package/lib/Defaults/baileys-version.json +1 -1
  17. package/lib/Socket/business.js +3 -3
  18. package/lib/Socket/chats.js +7 -7
  19. package/lib/Socket/groups.js +11 -9
  20. package/lib/Socket/messages-recv.js +12 -11
  21. package/lib/Socket/messages-send.js +981 -983
  22. package/lib/Socket/newsletter.js +3 -3
  23. package/lib/Socket/socket.js +13 -47
  24. package/lib/Socket/usync.js +3 -3
  25. package/lib/Store/make-cache-manager-store.js +9 -17
  26. package/lib/Store/make-in-memory-store.d.ts +2 -2
  27. package/lib/Store/make-in-memory-store.js +5 -5
  28. package/lib/Types/GroupMetadata.d.ts +2 -1
  29. package/lib/Utils/business.js +4 -0
  30. package/lib/Utils/decode-wa-message.js +4 -0
  31. package/lib/Utils/generics.js +10 -9
  32. package/lib/Utils/messages.js +1329 -1326
  33. package/lib/Utils/process-message.js +14 -1
  34. package/lib/index.js +1 -1
  35. package/package.json +5 -6
  36. package/WAProto/Adv/JKT48Connect - Valzyy +0 -0
  37. package/WAProto/Cert/JKT48Connect - Valzyy +0 -0
  38. package/WAProto/ChatLockSettings/JKT48Connect - Valzyy +0 -0
  39. package/WAProto/CompanionReg/JKT48Connect - Valzyy +0 -0
  40. package/WAProto/DeviceCapabilities/JKT48Connect - Valzyy +0 -0
  41. package/WAProto/E2E/JKT48Connect - Valzyy +0 -0
  42. package/WAProto/Ephemeral/JKT48Connect - Valzyy +0 -0
  43. package/WAProto/HistorySync/JKT48Connect - Valzyy +0 -0
  44. package/WAProto/JKT48Connect - Valzyy +0 -0
  45. package/WAProto/MdStorageChatRowOpaqueData/JKT48Connect - Valzyy +0 -0
  46. package/WAProto/MdStorageMsgRowOpaqueData/JKT48Connect - Valzyy +0 -0
  47. package/WAProto/MmsRetry/JKT48Connect - Valzyy +0 -0
  48. package/WAProto/Protocol/JKT48Connect - Valzyy +0 -0
  49. package/WAProto/Reporting/JKT48Connect - Valzyy +0 -0
  50. package/WAProto/ServerSync/JKT48Connect - Valzyy +0 -0
  51. package/WAProto/SignalLocalStorageProtocol/JKT48Connect - Valzyy +0 -0
  52. package/WAProto/SignalWhisperTextProtocol/JKT48Connect - Valzyy +0 -0
  53. package/WAProto/StatusAttributions/JKT48Connect - Valzyy +0 -0
  54. package/WAProto/SyncAction/JKT48Connect - Valzyy +0 -0
  55. package/WAProto/UserPassword/JKT48Connect - Valzyy +0 -0
  56. package/WAProto/VnameCert/JKT48Connect - Valzyy +0 -0
  57. package/WAProto/Wa6/JKT48Connect - Valzyy +0 -0
  58. package/WAProto/Web/JKT48Connect - Valzyy +0 -0
  59. package/lib/JKT48Connect - Valzyy +0 -0
@@ -1,984 +1,982 @@
1
- "use strict"
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod }
4
- }
5
- Object.defineProperty(exports, "__esModule", { value: true })
6
- const node_cache_1 = __importDefault(require("@cacheable/node-cache"))
7
- const boom_1 = require("@hapi/boom")
8
- const crypto_1 = require("crypto")
9
- const WAProto_1 = require("../../WAProto")
10
- const Defaults_1 = require("../Defaults")
11
- const Utils_1 = require("../Utils")
12
- const Types_1 = require("../Types")
13
- const WABinary_1 = require("../WABinary")
14
- const WAUSync_1 = require("../WAUSync")
15
- const newsletter_1 = require("./newsletter")
16
- const link_preview_1 = require("../Utils/link-preview")
17
- const makeMessagesSocket = (config) => {
18
- const { logger, linkPreviewImageThumbnailWidth, generateHighQualityLinkPreview, options: axiosOptions, patchMessageBeforeSending, cachedGroupMetadata, } = config
19
- const suki = newsletter_1.makeNewsletterSocket(config)
20
- const { ev, authState, processingMutex, signalRepository, upsertMessage, query, fetchPrivacySettings, sendNode, groupMetadata, groupToggleEphemeral, newsletterWMexQuery, executeUSyncQuery } = suki
21
- const userDevicesCache = config.userDevicesCache || new node_cache_1.default({
22
- stdTTL: Defaults_1.DEFAULT_CACHE_TTLS.USER_DEVICES,
23
- useClones: false
24
- })
25
- let mediaConn
26
- const refreshMediaConn = async (forceGet = false) => {
27
- const media = await mediaConn
28
- if (!media || forceGet || (new Date().getTime() - media.fetchDate.getTime()) > media.ttl * 1000) {
29
- mediaConn = (async () => {
30
- const result = await query({
31
- tag: 'iq',
32
- attrs: {
33
- type: 'set',
34
- xmlns: 'w:m',
35
- to: WABinary_1.S_WHATSAPP_NET,
36
- },
37
- content: [{ tag: 'media_conn', attrs: {} }]
38
- })
39
- const mediaConnNode = WABinary_1.getBinaryNodeChild(result, 'media_conn')
40
- const node = {
41
- hosts: WABinary_1.getBinaryNodeChildren(mediaConnNode, 'host').map(({ attrs }) => ({
42
- hostname: attrs.hostname,
43
- maxContentLengthBytes: +attrs.maxContentLengthBytes,
44
- })),
45
- auth: mediaConnNode.attrs.auth,
46
- ttl: +mediaConnNode.attrs.ttl,
47
- fetchDate: new Date()
48
- }
49
- logger.debug('fetched media conn')
50
- return node
51
- })()
52
- }
53
- return mediaConn
54
- }
55
- /**
56
- * generic send receipt function
57
- * used for receipts of phone call, read, delivery etc.
58
- * */
59
- const sendReceipt = async (jid, participant, messageIds, type) => {
60
- const node = {
61
- tag: 'receipt',
62
- attrs: {
63
- id: messageIds[0],
64
- },
65
- }
66
- const isReadReceipt = type === 'read' || type === 'read-self'
67
- if (isReadReceipt) {
68
- node.attrs.t = Utils_1.unixTimestampSeconds().toString()
69
- }
70
- if (type === 'sender' && WABinary_1.isJidUser(jid)) {
71
- node.attrs.recipient = jid
72
- node.attrs.to = participant
73
- }
74
- else {
75
- node.attrs.to = jid
76
- if (participant) {
77
- node.attrs.participant = participant
78
- }
79
- }
80
- if (type) {
81
- node.attrs.type = WABinary_1.isJidNewsletter(jid) ? 'read-self' : type
82
- }
83
- const remainingMessageIds = messageIds.slice(1)
84
- if (remainingMessageIds.length) {
85
- node.content = [
86
- {
87
- tag: 'list',
88
- attrs: {},
89
- content: remainingMessageIds.map(id => ({
90
- tag: 'item',
91
- attrs: { id }
92
- }))
93
- }
94
- ]
95
- }
96
- logger.debug({ attrs: node.attrs, messageIds }, 'sending receipt for messages')
97
- await sendNode(node)
98
- }
99
- /** Correctly bulk send receipts to multiple chats, participants */
100
- const sendReceipts = async (keys, type) => {
101
- const recps = Utils_1.aggregateMessageKeysNotFromMe(keys)
102
- for (const { jid, participant, messageIds } of recps) {
103
- await sendReceipt(jid, participant, messageIds, type)
104
- }
105
- }
106
- /** Bulk read messages. Keys can be from different chats & participants */
107
- const readMessages = async (keys) => {
108
- const privacySettings = await fetchPrivacySettings()
109
- // based on privacy settings, we have to change the read type
110
- const readType = privacySettings.readreceipts === 'all' ? 'read' : 'read-self'
111
- await sendReceipts(keys, readType)
112
- }
113
- /** Fetch image for groups, user, and newsletter **/
114
- const profilePictureUrl = async (jid) => {
115
- if (WABinary_1.isJidNewsletter(jid)) {
116
- let node = await newsletterWMexQuery(undefined, Types_1.QueryIds.METADATA, {
117
- input: {
118
- key: jid,
119
- type: 'JID',
120
- view_role: 'GUEST'
121
- },
122
- fetch_viewer_metadata: true,
123
- fetch_full_image: true,
124
- fetch_creation_time: true
125
- })
126
- let result = WABinary_1.getBinaryNodeChild(node, 'result')?.content?.toString()
127
- let metadata = JSON.parse(result).data[Types_1.XWAPaths.NEWSLETTER]
128
- return Utils_1.getUrlFromDirectPath(metadata.thread_metadata.picture?.direct_path || '')
129
- }
130
- else {
131
- const result = await query({
132
- tag: 'iq',
133
- attrs: {
134
- target: WABinary_1.jidNormalizedUser(jid),
135
- to: WABinary_1.S_WHATSAPP_NET,
136
- type: 'get',
137
- xmlns: 'w:profile:picture'
138
- },
139
- content: [{
140
- tag: 'picture',
141
- attrs: {
142
- type: 'image',
143
- query: 'url'
144
- }
145
- }]
146
- })
147
- const child = WABinary_1.getBinaryNodeChild(result, 'picture')
148
- return child?.attrs?.url || null
149
- }
150
- }
151
- /** Fetch all the devices we've to send a message to */
152
- const getUSyncDevices = async (jids, useCache, ignoreZeroDevices) => {
153
- const deviceResults = []
154
- if (!useCache) {
155
- logger.debug('not using cache for devices')
156
- }
157
- const toFetch = []
158
- jids = Array.from(new Set(jids))
159
- for (let jid of jids) {
160
- const user = WABinary_1.jidDecode(jid)?.user
161
- jid = WABinary_1.jidNormalizedUser(jid)
162
- if (useCache) {
163
- const devices = userDevicesCache.get(user)
164
- if (devices) {
165
- deviceResults.push(...devices)
166
- logger.trace({ user }, 'using cache for devices')
167
- }
168
- else {
169
- toFetch.push(jid)
170
- }
171
- }
172
- else {
173
- toFetch.push(jid)
174
- }
175
- }
176
- if (!toFetch.length) {
177
- return deviceResults
178
- }
179
- const query = new WAUSync_1.USyncQuery()
180
- .withContext('message')
181
- .withDeviceProtocol()
182
- for (const jid of toFetch) {
183
- query.withUser(new WAUSync_1.USyncUser().withId(jid))
184
- }
185
- const result = await executeUSyncQuery(query)
186
- if (result) {
187
- const extracted = Utils_1.extractDeviceJids(result?.list, authState.creds.me.id, ignoreZeroDevices)
188
- const deviceMap = {}
189
- for (const item of extracted) {
190
- deviceMap[item.user] = deviceMap[item.user] || []
191
- deviceMap[item.user].push(item)
192
- deviceResults.push(item)
193
- }
194
- for (const key in deviceMap) {
195
- userDevicesCache.set(key, deviceMap[key])
196
- }
197
- }
198
- return deviceResults
199
- }
200
- /** Assert Sessions */
201
- const assertSessions = async (jids, force) => {
202
- let didFetchNewSession = false
203
- let jidsRequiringFetch = []
204
- if (force) {
205
- jidsRequiringFetch = jids
206
- }
207
- else {
208
- const addrs = jids.map(jid => (signalRepository.jidToSignalProtocolAddress(jid)))
209
- const sessions = await authState.keys.get('session', addrs)
210
- for (const jid of jids) {
211
- const signalId = signalRepository
212
- .jidToSignalProtocolAddress(jid)
213
- if (!sessions[signalId]) {
214
- jidsRequiringFetch.push(jid)
215
- }
216
- }
217
- }
218
- if (jidsRequiringFetch.length) {
219
- logger.debug({ jidsRequiringFetch }, 'fetching sessions')
220
- const result = await query({
221
- tag: 'iq',
222
- attrs: {
223
- xmlns: 'encrypt',
224
- type: 'get',
225
- to: WABinary_1.S_WHATSAPP_NET,
226
- },
227
- content: [
228
- {
229
- tag: 'key',
230
- attrs: {},
231
- content: jidsRequiringFetch.map(jid => ({
232
- tag: 'user',
233
- attrs: { jid },
234
- }))
235
- }
236
- ]
237
- })
238
- await Utils_1.parseAndInjectE2ESessions(result, signalRepository)
239
- didFetchNewSession = true
240
- }
241
- return didFetchNewSession
242
- }
243
- /** Send Peer Operation */
244
- const sendPeerDataOperationMessage = async (pdoMessage) => {
245
- //TODO: for later, abstract the logic to send a Peer Message instead of just PDO - useful for App State Key Resync with phone
246
- if (!authState.creds.me?.id) {
247
- throw new boom_1.Boom('Not authenticated')
248
- }
249
- const protocolMessage = {
250
- protocolMessage: {
251
- peerDataOperationRequestMessage: pdoMessage,
252
- type: WAProto_1.proto.Message.ProtocolMessage.Type.PEER_DATA_OPERATION_REQUEST_MESSAGE
253
- }
254
- }
255
- const meJid = WABinary_1.jidNormalizedUser(authState.creds.me.id)
256
- const msgId = await relayMessage(meJid, protocolMessage, {
257
- additionalAttributes: {
258
- category: 'peer',
259
- // eslint-disable-next-line camelcase
260
- push_priority: 'high_force',
261
- },
262
- })
263
- return msgId
264
- }
265
- const createParticipantNodes = async (jids, message, extraAttrs) => {
266
- const patched = await patchMessageBeforeSending(message, jids)
267
- const bytes = Utils_1.encodeWAMessage(patched)
268
- let shouldIncludeDeviceIdentity = false
269
- const nodes = await Promise.all(jids.map(async (jid) => {
270
- const { type, ciphertext } = await signalRepository.encryptMessage({ jid, data: bytes })
271
- if (type === 'pkmsg') {
272
- shouldIncludeDeviceIdentity = true
273
- }
274
- const node = {
275
- tag: 'to',
276
- attrs: { jid },
277
- content: [{
278
- tag: 'enc',
279
- attrs: {
280
- v: '2',
281
- type,
282
- ...extraAttrs || {}
283
- },
284
- content: ciphertext
285
- }]
286
- }
287
- return node
288
- }))
289
- return {
290
- nodes,
291
- shouldIncludeDeviceIdentity
292
- }
293
- }
294
- const relayMessage = async (jid, message, { messageId: msgId, participant, additionalAttributes, useUserDevicesCache, useCachedGroupMetadata, statusJidList, additionalNodes }) => {
295
- const meId = authState.creds.me.id
296
- let didPushAdditional = false
297
- let shouldIncludeDeviceIdentity = false
298
- const { user, server } = WABinary_1.jidDecode(jid)
299
- const statusJid = 'status@broadcast'
300
- const isGroup = server === 'g.us'
301
- const isPrivate = server === 's.whatsapp.net'
302
- const isNewsletter = server == 'newsletter'
303
- const isStatus = jid === statusJid
304
- const isLid = server === 'lid'
305
- msgId = msgId || Utils_1.generateMessageID(authState.creds.me.id)
306
- useUserDevicesCache = useUserDevicesCache !== false
307
- useCachedGroupMetadata = useCachedGroupMetadata !== false && !isStatus
308
- const participants = []
309
- const destinationJid = (!isStatus) ? WABinary_1.jidEncode(user, isLid ? 'lid' : isGroup ? 'g.us' : isNewsletter ? 'newsletter' : 's.whatsapp.net') : statusJid
310
- const binaryNodeContent = []
311
- const devices = []
312
- const meMsg = {
313
- deviceSentMessage: {
314
- destinationJid,
315
- message
316
- }
317
- }
318
- const extraAttrs = {}
319
- if (participant) {
320
- // when the retry request is not for a group
321
- // only send to the specific device that asked for a retry
322
- // otherwise the message is sent out to every device that should be a recipient
323
- if (!isGroup && !isStatus) {
324
- additionalAttributes = { ...additionalAttributes, 'device_fanout': 'false' }
325
- }
326
- const { user, device } = WABinary_1.jidDecode(participant.jid)
327
- devices.push({ user, device })
328
- }
329
- await authState.keys.transaction(async () => {
330
- const mediaType = getMediaType(message)
331
- if (mediaType) {
332
- extraAttrs['mediatype'] = mediaType
333
- }
334
- if (Utils_1.normalizeMessageContent(message)?.pinInChatMessage) {
335
- extraAttrs['decrypt-fail'] = 'hide'
336
- }
337
- if (isGroup || isStatus) {
338
- const [groupData, senderKeyMap] = await Promise.all([
339
- (async () => {
340
- let groupData = useCachedGroupMetadata && cachedGroupMetadata ? await cachedGroupMetadata(jid) : undefined
341
- if (groupData && Array.isArray(groupData?.participants)) {
342
- logger.trace({ jid, participants: groupData.participants.length }, 'using cached group metadata')
343
- }
344
- else if (!isStatus) {
345
- groupData = await groupMetadata(jid)
346
- }
347
- return groupData
348
- })(),
349
- (async () => {
350
- if (!participant && !isStatus) {
351
- const result = await authState.keys.get('sender-key-memory', [jid])
352
- return result[jid] || {}
353
- }
354
- return {}
355
- })()
356
- ])
357
- if (!participant) {
358
- const participantsList = (groupData && !isStatus) ? groupData.participants.map(p => p.id) : []
359
- if (isStatus && statusJidList) {
360
- participantsList.push(...statusJidList)
361
- }
362
- if (!isStatus) {
363
- additionalAttributes = {
364
- ...additionalAttributes,
365
- addressing_mode: groupData?.addressingMode || 'pn'
366
- }
367
- }
368
- const additionalDevices = await getUSyncDevices(participantsList, !!useUserDevicesCache, false)
369
- devices.push(...additionalDevices)
370
- }
371
- const patched = await patchMessageBeforeSending(message, devices.map(d => WABinary_1.jidEncode(d.user, isLid ? 'lid' : 's.whatsapp.net', d.device)))
372
- const bytes = Utils_1.encodeWAMessage(patched)
373
- const { ciphertext, senderKeyDistributionMessage } = await signalRepository.encryptGroupMessage({
374
- group: destinationJid,
375
- data: bytes,
376
- meId,
377
- })
378
- const senderKeyJids = []
379
- // ensure a connection is established with every device
380
- for (const { user, device } of devices) {
381
- const jid = WABinary_1.jidEncode(user, groupData?.addressingMode === 'lid' ? 'lid' : 's.whatsapp.net', device)
382
- if (!senderKeyMap[jid] || !!participant) {
383
- senderKeyJids.push(jid)
384
- // store that this person has had the sender keys sent to them
385
- senderKeyMap[jid] = true
386
- }
387
- }
388
- // if there are some participants with whom the session has not been established
389
- // if there are, we re-send the senderkey
390
- if (senderKeyJids.length) {
391
- logger.debug({ senderKeyJids }, 'sending new sender key')
392
- const senderKeyMsg = {
393
- senderKeyDistributionMessage: {
394
- axolotlSenderKeyDistributionMessage: senderKeyDistributionMessage,
395
- groupId: destinationJid
396
- }
397
- }
398
- await assertSessions(senderKeyJids, false)
399
- const result = await createParticipantNodes(senderKeyJids, senderKeyMsg, extraAttrs)
400
- shouldIncludeDeviceIdentity = shouldIncludeDeviceIdentity || result.shouldIncludeDeviceIdentity
401
- participants.push(...result.nodes)
402
- }
403
- binaryNodeContent.push({
404
- tag: 'enc',
405
- attrs: { v: '2', type: 'skmsg' },
406
- content: ciphertext
407
- })
408
- await authState.keys.set({ 'sender-key-memory': { [jid]: senderKeyMap } })
409
- }
410
- else if (isNewsletter) {
411
- // Message edit
412
- if (message.protocolMessage?.editedMessage) {
413
- msgId = message.protocolMessage.key?.id
414
- message = message.protocolMessage.editedMessage
415
- }
416
- // Message delete
417
- if (message.protocolMessage?.type === WAProto_1.proto.Message.ProtocolMessage.Type.REVOKE) {
418
- msgId = message.protocolMessage.key?.id
419
- message = {}
420
- }
421
- const patched = await patchMessageBeforeSending(message, [])
422
- const bytes = Utils_1.encodeNewsletterMessage(patched)
423
- binaryNodeContent.push({
424
- tag: 'plaintext',
425
- attrs: mediaType ? { mediatype: mediaType } : {},
426
- content: bytes
427
- })
428
- }
429
- else {
430
- const { user: meUser } = WABinary_1.jidDecode(meId)
431
- if (!participant) {
432
- devices.push({ user })
433
- if (user !== meUser) {
434
- devices.push({ user: meUser })
435
- }
436
- if (additionalAttributes?.['category'] !== 'peer') {
437
- const additionalDevices = await getUSyncDevices([meId, jid], !!useUserDevicesCache, true)
438
- devices.push(...additionalDevices)
439
- }
440
- }
441
- const allJids = []
442
- const meJids = []
443
- const otherJids = []
444
- for (const { user, device } of devices) {
445
- const isMe = user === meUser
446
- const jid = WABinary_1.jidEncode(isMe && isLid ? authState.creds?.me?.lid?.split(':')[0] || user : user, isLid ? 'lid' : 's.whatsapp.net', device)
447
- if (isMe) {
448
- meJids.push(jid)
449
- }
450
- else {
451
- otherJids.push(jid)
452
- }
453
- allJids.push(jid)
454
- }
455
- await assertSessions(allJids, false)
456
- const [{ nodes: meNodes, shouldIncludeDeviceIdentity: s1 }, { nodes: otherNodes, shouldIncludeDeviceIdentity: s2 }] = await Promise.all([
457
- createParticipantNodes(meJids, meMsg, extraAttrs),
458
- createParticipantNodes(otherJids, message, extraAttrs)
459
- ])
460
- participants.push(...meNodes)
461
- participants.push(...otherNodes)
462
- shouldIncludeDeviceIdentity = shouldIncludeDeviceIdentity || s1 || s2
463
- }
464
- if (participants.length) {
465
- if (additionalAttributes?.['category'] === 'peer') {
466
- const peerNode = participants[0]?.content?.[0]
467
- if (peerNode) {
468
- binaryNodeContent.push(peerNode) // push only enc
469
- }
470
- }
471
- else {
472
- binaryNodeContent.push({
473
- tag: 'participants',
474
- attrs: {},
475
- content: participants
476
- })
477
- }
478
- }
479
- const stanza = {
480
- tag: 'message',
481
- attrs: {
482
- id: msgId,
483
- type: isNewsletter ? getTypeMessage(message) : 'text',
484
- ...(additionalAttributes || {})
485
- },
486
- content: binaryNodeContent
487
- }
488
- // if the participant to send to is explicitly specified (generally retry recp)
489
- // ensure the message is only sent to that person
490
- // if a retry receipt is sent to everyone -- it'll fail decryption for everyone else who received the msg
491
- if (participant) {
492
- if (WABinary_1.isJidGroup(destinationJid)) {
493
- stanza.attrs.to = destinationJid
494
- stanza.attrs.participant = participant.jid
495
- }
496
- else if (WABinary_1.areJidsSameUser(participant.jid, meId)) {
497
- stanza.attrs.to = participant.jid
498
- stanza.attrs.recipient = destinationJid
499
- }
500
- else {
501
- stanza.attrs.to = participant.jid
502
- }
503
- }
504
- else {
505
- stanza.attrs.to = destinationJid
506
- }
507
- if (shouldIncludeDeviceIdentity) {
508
- stanza.content.push({
509
- tag: 'device-identity',
510
- attrs: {},
511
- content: Utils_1.encodeSignedDeviceIdentity(authState.creds.account, true)
512
- })
513
- logger.debug({ jid }, 'adding device identity')
514
- }
515
- const messages = Utils_1.normalizeMessageContent(message)
516
- const buttonType = getButtonType(messages)
517
- if (!isNewsletter && buttonType) {
518
- if (!stanza.content || !Array.isArray(stanza.content)) {
519
- stanza.content = []
520
- }
521
- const buttonsNode = getButtonArgs(messages)
522
- const filteredButtons = WABinary_1.getBinaryFilteredButtons(additionalNodes ? additionalNodes : [])
523
- if (filteredButtons) {
524
- stanza.content.push(...additionalNodes)
525
- didPushAdditional = true
526
- }
527
- else {
528
- stanza.content.push(buttonsNode)
529
- }
530
- }
531
- if (isPrivate) {
532
- if (!stanza.content || !Array.isArray(stanza.content)) {
533
- stanza.content = []
534
- }
535
- const botNode = {
536
- tag: 'bot',
537
- attrs: {
538
- biz_bot: '1'
539
- }
540
- }
541
- const filteredBizBot = WABinary_1.getBinaryFilteredBizBot(additionalNodes ? additionalNodes : [])
542
- if (filteredBizBot) {
543
- stanza.content.push(...additionalNodes)
544
- didPushAdditional = true
545
- }
546
- else {
547
- stanza.content.push(botNode)
548
- }
549
- }
550
- if (!didPushAdditional && additionalNodes && additionalNodes.length > 0) {
551
- if (!stanza.content || !Array.isArray(stanza.content)) {
552
- stanza.content = []
553
- }
554
- stanza.content.push(...additionalNodes)
555
- }
556
- logger.debug({ msgId }, `sending message to ${participants.length} devices`)
557
- await sendNode(stanza)
558
- })
559
- return msgId
560
- }
561
- const getTypeMessage = (msg) => {
562
- const message = Utils_1.normalizeMessageContent(msg)
563
- if (message.reactionMessage) {
564
- return 'reaction'
565
- }
566
- else if (getMediaType(message)) {
567
- return 'media'
568
- }
569
- else {
570
- return 'text'
571
- }
572
- }
573
- const getMediaType = (message) => {
574
- if (message.imageMessage) {
575
- return 'image'
576
- }
577
- else if (message.videoMessage) {
578
- return message.videoMessage.gifPlayback ? 'gif' : 'video'
579
- }
580
- else if (message.audioMessage) {
581
- return message.audioMessage.ptt ? 'ptt' : 'audio'
582
- }
583
- else if (message.contactMessage) {
584
- return 'vcard'
585
- }
586
- else if (message.documentMessage) {
587
- return 'document'
588
- }
589
- else if (message.contactsArrayMessage) {
590
- return 'contact_array'
591
- }
592
- else if (message.liveLocationMessage) {
593
- return 'livelocation'
594
- }
595
- else if (message.stickerMessage) {
596
- return 'sticker'
597
- }
598
- else if (message.listMessage) {
599
- return 'list'
600
- }
601
- else if (message.listResponseMessage) {
602
- return 'list_response'
603
- }
604
- else if (message.buttonsResponseMessage) {
605
- return 'buttons_response'
606
- }
607
- else if (message.orderMessage) {
608
- return 'order'
609
- }
610
- else if (message.productMessage) {
611
- return 'product'
612
- }
613
- else if (message.interactiveResponseMessage) {
614
- return 'native_flow_response'
615
- }
616
- else if (message.groupInviteMessage) {
617
- return 'url'
618
- }
619
- else if (/https:\/\/wa\.me\/p\/\d+\/\d+/.test(message.extendedTextMessage?.text)) {
620
- return 'productlink'
621
- }
622
- }
623
- const getButtonType = (message) => {
624
- if (message.listMessage) {
625
- return 'list'
626
- }
627
- else if (message.buttonsMessage) {
628
- return 'buttons'
629
- }
630
- else if(message.interactiveMessage?.nativeFlowMessage) {
631
- return 'native_flow'
632
- }
633
- }
634
- const getButtonArgs = (message) => {
635
- if (message.interactiveMessage?.nativeFlowMessage && message.interactiveMessage.nativeFlowMessage?.buttons?.length > 0 && message.interactiveMessage.nativeFlowMessage.buttons[0].name === 'review_and_pay') {
636
- return {
637
- tag: 'biz',
638
- attrs: {
639
- native_flow_name: 'order_details'
640
- }
641
- }
642
- } else if (message.interactiveMessage?.nativeFlowMessage && message.interactiveMessage.nativeFlowMessage?.buttons?.length > 0 && message.interactiveMessage.nativeFlowMessage.buttons[0].name === 'payment_info') {
643
- return {
644
- tag: 'biz',
645
- attrs: {
646
- native_flow_name: 'payment_info'
647
- }
648
- }
649
- } else if (message.interactiveMessage?.nativeFlowMessage &&message.interactiveMessage.nativeFlowMessage?.buttons?.length > 0 &&
650
- ['mpm', 'cta_catalog', 'send_location', 'call_permission_request', 'wa_payment_transaction_details', 'automated_greeting_message_view_catalog']
651
- .includes(message.interactiveMessage.nativeFlowMessage.buttons[0].name)) {
652
- // Only works for WhatsApp, not WhatsApp Business
653
- return {
654
- tag: 'biz',
655
- attrs: {},
656
- content: [{
657
- tag: 'interactive',
658
- attrs: {
659
- type: 'native_flow',
660
- v: '1'
661
- },
662
- content: [{
663
- tag: 'native_flow',
664
- attrs: {
665
- v: '2',
666
- name: message.interactiveMessage.nativeFlowMessage.buttons[0].name
667
- }
668
- }]
669
- }]
670
- }
671
- } else if (message.interactiveMessage?.nativeFlowMessage || message.buttonsMessage) {
672
- // It works for whatsapp and whatsapp business
673
- return {
674
- tag: 'biz',
675
- attrs: {},
676
- content: [{
677
- tag: 'interactive',
678
- attrs: {
679
- type: 'native_flow',
680
- v: '1'
681
- },
682
- content: [{
683
- tag: 'native_flow',
684
- attrs: {
685
- v: '9',
686
- name: 'mixed'
687
- }
688
- }]
689
- }]
690
- }
691
- } else if (message.listMessage) {
692
- return {
693
- tag: 'biz',
694
- attrs: {},
695
- content: [{
696
- tag: 'list',
697
- attrs: {
698
- v: '2',
699
- type: 'product_list'
700
- }
701
- }]
702
- }
703
- } else {
704
- return {
705
- tag: 'biz',
706
- attrs: {}
707
- }
708
- }
709
- }
710
- const getPrivacyTokens = async (jids) => {
711
- const t = Utils_1.unixTimestampSeconds().toString()
712
- const result = await query({
713
- tag: 'iq',
714
- attrs: {
715
- to: WABinary_1.S_WHATSAPP_NET,
716
- type: 'set',
717
- xmlns: 'privacy'
718
- },
719
- content: [
720
- {
721
- tag: 'tokens',
722
- attrs: {},
723
- content: jids.map(jid => ({
724
- tag: 'token',
725
- attrs: {
726
- jid: WABinary_1.jidNormalizedUser(jid),
727
- t,
728
- type: 'trusted_contact'
729
- }
730
- }))
731
- }
732
- ]
733
- })
734
- return result
735
- }
736
- const waUploadToServer = Utils_1.getWAUploadToServer(config, refreshMediaConn)
737
- const waitForMsgMediaUpdate = Utils_1.bindWaitForEvent(ev, 'messages.media-update')
738
- return {
739
- ...suki,
740
- getPrivacyTokens,
741
- assertSessions,
742
- relayMessage,
743
- sendReceipt,
744
- sendReceipts,
745
- readMessages,
746
- profilePictureUrl,
747
- getUSyncDevices,
748
- refreshMediaConn,
749
- waUploadToServer,
750
- fetchPrivacySettings,
751
- createParticipantNodes,
752
- sendPeerDataOperationMessage,
753
- updateMediaMessage: async (message) => {
754
- const content = Utils_1.assertMediaContent(message.message)
755
- const mediaKey = content.mediaKey
756
- const meId = authState.creds.me.id
757
- const node = await Utils_1.encryptMediaRetryRequest(message.key, mediaKey, meId)
758
- let error = undefined
759
- await Promise.all([
760
- sendNode(node),
761
- waitForMsgMediaUpdate(async (update) => {
762
- const result = update.find(c => c.key.id === message.key.id)
763
- if (result) {
764
- if (result.error) {
765
- error = result.error
766
- }
767
- else {
768
- try {
769
- const media = await Utils_1.decryptMediaRetryData(result.media, mediaKey, result.key.id)
770
- if (media.result !== WAProto_1.proto.MediaRetryNotification.ResultType.SUCCESS) {
771
- const resultStr = WAProto_1.proto.MediaRetryNotification.ResultType[media.result]
772
- throw new boom_1.Boom(`Media re-upload failed by device (${resultStr})`, { data: media, statusCode: Utils_1.getStatusCodeForMediaRetry(media.result) || 404 })
773
- }
774
- content.directPath = media.directPath
775
- content.url = Utils_1.getUrlFromDirectPath(content.directPath)
776
- logger.debug({ directPath: media.directPath, key: result.key }, 'media update successful')
777
- }
778
- catch (err) {
779
- error = err
780
- }
781
- }
782
- return true
783
- }
784
- })
785
- ])
786
- if (error) {
787
- throw error
788
- }
789
- ev.emit('messages.update', [
790
- { key: message.key, update: { message: message.message } }
791
- ])
792
- return message
793
- },
794
- sendStatusMentions: async (jid, content) => {
795
- const media = await Utils_1.generateWAMessage(WABinary_1.STORIES_JID, content, {
796
- upload: await waUploadToServer,
797
- backgroundColor: "#" + Math.floor(Math.random() * 16777215).toString(16).padStart(6, "0"),
798
- font: content.text ? Math.floor(Math.random() * 9) : null
799
- })
800
- const additionalNodes = [{
801
- tag: 'meta',
802
- attrs: {},
803
- content: [{
804
- tag: 'mentioned_users',
805
- attrs: {},
806
- content: [{
807
- tag: 'to',
808
- attrs: { jid },
809
- content: undefined,
810
- }],
811
- }],
812
- }]
813
- let Private = WABinary_1.isJidUser(jid)
814
- let statusJid = Private ? [jid] : (await groupMetadata(jid)).participants.map((num) => num.id)
815
- await relayMessage(WABinary_1.STORIES_JID, media.message, {
816
- messageId: media.key.id,
817
- statusJidList: statusJid,
818
- additionalNodes,
819
- })
820
- let type = Private ? 'statusMentionMessage' : 'groupStatusMentionMessage'
821
- let msg = await Utils_1.generateWAMessageFromContent(jid, {
822
- [type]: {
823
- message: {
824
- protocolMessage: {
825
- key: media.key,
826
- type: 25,
827
- }
828
- }
829
- },
830
- messageContextInfo: {
831
- messageSecret: crypto_1.randomBytes(32)
832
- }
833
- }, {})
834
- await relayMessage(jid, msg.message, {
835
- additionalNodes: Private ? [{
836
- tag: 'meta',
837
- attrs: { is_status_mention: 'true' },
838
- content: undefined,
839
- }] : undefined
840
- }, {})
841
- return media
842
- },
843
- sendAlbumMessage: async (jid, medias, options = {}) => {
844
- const userJid = authState.creds.me.id
845
- for (const media of medias) {
846
- if (!media.image && !media.video)
847
- throw new TypeError(`medias[i] must have image or video property`)
848
- }
849
- const time = options.delay || 500
850
- delete options.delay
851
- const album = await Utils_1.generateWAMessageFromContent(jid, {
852
- albumMessage: {
853
- expectedImageCount: medias.filter(media => media.image).length,
854
- expectedVideoCount: medias.filter(media => media.video).length,
855
- ...options
856
- }
857
- }, { userJid, ...options })
858
- await relayMessage(jid, album.message, { messageId: album.key.id })
859
- let mediaHandle
860
- let msg
861
- for (const i in medias) {
862
- const media = medias[i]
863
- if (media.image) {
864
- msg = await Utils_1.generateWAMessage(jid, {
865
- image: media.image,
866
- ...media,
867
- ...options
868
- }, {
869
- userJid,
870
- upload: async (readStream, opts) => {
871
- const up = await waUploadToServer(readStream, { ...opts, newsletter: WABinary_1.isJidNewsletter(jid) })
872
- mediaHandle = up.handle
873
- return up
874
- },
875
- ...options
876
- })
877
- }
878
- else if (media.video) {
879
- msg = await Utils_1.generateWAMessage(jid, {
880
- video: media.video,
881
- ...media,
882
- ...options
883
- }, {
884
- userJid,
885
- upload: async (readStream, opts) => {
886
- const up = await waUploadToServer(readStream, { ...opts, newsletter: WABinary_1.isJidNewsletter(jid) })
887
- mediaHandle = up.handle
888
- return up
889
- },
890
- ...options,
891
- })
892
- }
893
- if (msg) {
894
- msg.message.messageContextInfo = {
895
- messageSecret: crypto_1.randomBytes(32),
896
- messageAssociation: {
897
- associationType: 1,
898
- parentMessageKey: album.key
899
- }
900
- }
901
- }
902
- await relayMessage(jid, msg.message, { messageId: msg.key.id })
903
- await Utils_1.delay(time)
904
- }
905
- return album
906
- },
907
- sendMessage: async (jid, content, options = {}) => {
908
- const userJid = authState.creds.me.id
909
- if (typeof content === 'object' &&
910
- 'disappearingMessagesInChat' in content &&
911
- typeof content['disappearingMessagesInChat'] !== 'undefined' &&
912
- WABinary_1.isJidGroup(jid)) {
913
- const { disappearingMessagesInChat } = content
914
- const value = typeof disappearingMessagesInChat === 'boolean' ?
915
- (disappearingMessagesInChat ? Defaults_1.WA_DEFAULT_EPHEMERAL : 0) :
916
- disappearingMessagesInChat
917
- await groupToggleEphemeral(jid, value)
918
- }
919
- else {
920
- let mediaHandle
921
- const fullMsg = await Utils_1.generateWAMessage(jid, content, {
922
- logger,
923
- userJid,
924
- getUrlInfo: text => link_preview_1.getUrlInfo(text, {
925
- thumbnailWidth: linkPreviewImageThumbnailWidth,
926
- fetchOpts: {
927
- timeout: 3000,
928
- ...axiosOptions || {}
929
- },
930
- logger,
931
- uploadImage: generateHighQualityLinkPreview
932
- ? waUploadToServer
933
- : undefined
934
- }),
935
- getProfilePicUrl: profilePictureUrl,
936
- upload: async (readStream, opts) => {
937
- const up = await waUploadToServer(readStream, { ...opts, newsletter: WABinary_1.isJidNewsletter(jid) })
938
- mediaHandle = up.handle
939
- return up
940
- },
941
- mediaCache: config.mediaCache,
942
- options: config.options,
943
- messageId: Utils_1.generateMessageID(userJid),
944
- ...options,
945
- })
946
- const isPin = 'pin' in content && !!content.pin
947
- const isEdit = 'edit' in content && !!content.edit
948
- const isDelete = 'delete' in content && !!content.delete
949
- const additionalAttributes = {}
950
- if (isDelete) {
951
- // if the chat is a group, and I am not the author, then delete the message as an admin
952
- if (WABinary_1.isJidGroup(content.delete?.remoteJid) && !content.delete?.fromMe || WABinary_1.isJidNewsletter(jid)) {
953
- additionalAttributes.edit = '8'
954
- }
955
- else {
956
- additionalAttributes.edit = '7'
957
- }
958
- }
959
- else if (isEdit) {
960
- additionalAttributes.edit = WABinary_1.isJidNewsletter(jid) ? '3' : '1'
961
- }
962
- else if (isPin) {
963
- additionalAttributes.edit = '2'
964
- }
965
- if (mediaHandle) {
966
- additionalAttributes['media_id'] = mediaHandle
967
- }
968
- if ('cachedGroupMetadata' in options) {
969
- console.warn('cachedGroupMetadata in sendMessage are deprecated, now cachedGroupMetadata is part of the socket config.')
970
- }
971
- await relayMessage(jid, fullMsg.message, { messageId: fullMsg.key.id, useCachedGroupMetadata: options.useCachedGroupMetadata, additionalAttributes, statusJidList: options.statusJidList, additionalNodes: options.additionalNodes })
972
- if (config.emitOwnEvents) {
973
- process.nextTick(() => {
974
- processingMutex.mutex(() => (upsertMessage(fullMsg, 'append')))
975
- })
976
- }
977
- return fullMsg
978
- }
979
- }
980
- }
981
- }
982
- module.exports = {
983
- makeMessagesSocket
1
+ "use strict"
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod }
4
+ }
5
+ Object.defineProperty(exports, "__esModule", { value: true })
6
+ const node_cache_1 = __importDefault(require("@cacheable/node-cache"))
7
+ const boom_1 = require("@hapi/boom")
8
+ const crypto_1 = require("crypto")
9
+ const WAProto_1 = require("../../WAProto")
10
+ const Defaults_1 = require("../Defaults")
11
+ const Utils_1 = require("../Utils")
12
+ const Types_1 = require("../Types")
13
+ const WABinary_1 = require("../WABinary")
14
+ const WAUSync_1 = require("../WAUSync")
15
+ const newsletter_1 = require("./newsletter")
16
+ const link_preview_1 = require("../Utils/link-preview")
17
+ const makeMessagesSocket = (config) => {
18
+ const { logger, linkPreviewImageThumbnailWidth, generateHighQualityLinkPreview, options: axiosOptions, patchMessageBeforeSending, cachedGroupMetadata, } = config
19
+ const sock = newsletter_1.makeNewsletterSocket(config)
20
+ const { ev, authState, processingMutex, signalRepository, upsertMessage, query, fetchPrivacySettings, sendNode, groupMetadata, groupToggleEphemeral, newsletterWMexQuery, executeUSyncQuery } = sock
21
+ const userDevicesCache = config.userDevicesCache || new node_cache_1.default({
22
+ stdTTL: Defaults_1.DEFAULT_CACHE_TTLS.USER_DEVICES,
23
+ useClones: false
24
+ })
25
+ let mediaConn
26
+ const refreshMediaConn = async (forceGet = false) => {
27
+ const media = await mediaConn
28
+ if (!media || forceGet || (new Date().getTime() - media.fetchDate.getTime()) > media.ttl * 1000) {
29
+ mediaConn = (async () => {
30
+ const result = await query({
31
+ tag: 'iq',
32
+ attrs: {
33
+ type: 'set',
34
+ xmlns: 'w:m',
35
+ to: WABinary_1.S_WHATSAPP_NET,
36
+ },
37
+ content: [{ tag: 'media_conn', attrs: {} }]
38
+ })
39
+ const mediaConnNode = WABinary_1.getBinaryNodeChild(result, 'media_conn')
40
+ const node = {
41
+ hosts: WABinary_1.getBinaryNodeChildren(mediaConnNode, 'host').map(({ attrs }) => ({
42
+ hostname: attrs.hostname,
43
+ maxContentLengthBytes: +attrs.maxContentLengthBytes,
44
+ })),
45
+ auth: mediaConnNode.attrs.auth,
46
+ ttl: +mediaConnNode.attrs.ttl,
47
+ fetchDate: new Date()
48
+ }
49
+ logger.debug('fetched media conn')
50
+ return node
51
+ })()
52
+ }
53
+ return mediaConn
54
+ }
55
+ /**
56
+ * generic send receipt function
57
+ * used for receipts of phone call, read, delivery etc.
58
+ * */
59
+ const sendReceipt = async (jid, participant, messageIds, type) => {
60
+ const node = {
61
+ tag: 'receipt',
62
+ attrs: {
63
+ id: messageIds[0],
64
+ },
65
+ }
66
+ const isReadReceipt = type === 'read' || type === 'read-self'
67
+ if (isReadReceipt) {
68
+ node.attrs.t = Utils_1.unixTimestampSeconds().toString()
69
+ }
70
+ if (type === 'sender' && WABinary_1.isJidUser(jid)) {
71
+ node.attrs.recipient = jid
72
+ node.attrs.to = participant
73
+ }
74
+ else {
75
+ node.attrs.to = jid
76
+ if (participant) {
77
+ node.attrs.participant = participant
78
+ }
79
+ }
80
+ if (type) {
81
+ node.attrs.type = WABinary_1.isJidNewsletter(jid) ? 'read-self' : type
82
+ }
83
+ const remainingMessageIds = messageIds.slice(1)
84
+ if (remainingMessageIds.length) {
85
+ node.content = [
86
+ {
87
+ tag: 'list',
88
+ attrs: {},
89
+ content: remainingMessageIds.map(id => ({
90
+ tag: 'item',
91
+ attrs: { id }
92
+ }))
93
+ }
94
+ ]
95
+ }
96
+ logger.debug({ attrs: node.attrs, messageIds }, 'sending receipt for messages')
97
+ await sendNode(node)
98
+ }
99
+ /** Correctly bulk send receipts to multiple chats, participants */
100
+ const sendReceipts = async (keys, type) => {
101
+ const recps = Utils_1.aggregateMessageKeysNotFromMe(keys)
102
+ for (const { jid, participant, messageIds } of recps) {
103
+ await sendReceipt(jid, participant, messageIds, type)
104
+ }
105
+ }
106
+ /** Bulk read messages. Keys can be from different chats & participants */
107
+ const readMessages = async (keys) => {
108
+ const privacySettings = await fetchPrivacySettings()
109
+ // based on privacy settings, we have to change the read type
110
+ const readType = privacySettings.readreceipts === 'all' ? 'read' : 'read-self'
111
+ await sendReceipts(keys, readType)
112
+ }
113
+ /** Fetch image for groups, user, and newsletter **/
114
+ const profilePictureUrl = async (jid) => {
115
+ if (WABinary_1.isJidNewsletter(jid)) {
116
+ let node = await newsletterWMexQuery(undefined, Types_1.QueryIds.METADATA, {
117
+ input: {
118
+ key: jid,
119
+ type: 'JID',
120
+ view_role: 'GUEST'
121
+ },
122
+ fetch_viewer_metadata: true,
123
+ fetch_full_image: true,
124
+ fetch_creation_time: true
125
+ })
126
+ let result = WABinary_1.getBinaryNodeChild(node, 'result')?.content?.toString()
127
+ let metadata = JSON.parse(result).data[Types_1.XWAPaths.NEWSLETTER]
128
+ return Utils_1.getUrlFromDirectPath(metadata.thread_metadata.picture?.direct_path || '')
129
+ }
130
+ else {
131
+ const result = await query({
132
+ tag: 'iq',
133
+ attrs: {
134
+ target: WABinary_1.jidNormalizedUser(jid),
135
+ to: WABinary_1.S_WHATSAPP_NET,
136
+ type: 'get',
137
+ xmlns: 'w:profile:picture'
138
+ },
139
+ content: [{
140
+ tag: 'picture',
141
+ attrs: {
142
+ type: 'image',
143
+ query: 'url'
144
+ }
145
+ }]
146
+ })
147
+ const child = WABinary_1.getBinaryNodeChild(result, 'picture')
148
+ return child?.attrs?.url || null
149
+ }
150
+ }
151
+ /** Fetch all the devices we've to send a message to */
152
+ const getUSyncDevices = async (jids, useCache, ignoreZeroDevices) => {
153
+ const deviceResults = []
154
+ if (!useCache) {
155
+ logger.debug('not using cache for devices')
156
+ }
157
+ const toFetch = []
158
+ jids = Array.from(new Set(jids))
159
+ for (let jid of jids) {
160
+ const user = WABinary_1.jidDecode(jid)?.user
161
+ jid = WABinary_1.jidNormalizedUser(jid)
162
+ if (useCache) {
163
+ const devices = userDevicesCache.get(user)
164
+ if (devices) {
165
+ deviceResults.push(...devices)
166
+ logger.trace({ user }, 'using cache for devices')
167
+ }
168
+ else {
169
+ toFetch.push(jid)
170
+ }
171
+ }
172
+ else {
173
+ toFetch.push(jid)
174
+ }
175
+ }
176
+ if (!toFetch.length) {
177
+ return deviceResults
178
+ }
179
+ const query = new WAUSync_1.USyncQuery()
180
+ .withContext('message')
181
+ .withDeviceProtocol()
182
+ for (const jid of toFetch) {
183
+ query.withUser(new WAUSync_1.USyncUser().withId(jid))
184
+ }
185
+ const result = await executeUSyncQuery(query)
186
+ if (result) {
187
+ const extracted = Utils_1.extractDeviceJids(result?.list, authState.creds.me.id, ignoreZeroDevices)
188
+ const deviceMap = {}
189
+ for (const item of extracted) {
190
+ deviceMap[item.user] = deviceMap[item.user] || []
191
+ deviceMap[item.user].push(item)
192
+ deviceResults.push(item)
193
+ }
194
+ for (const key in deviceMap) {
195
+ userDevicesCache.set(key, deviceMap[key])
196
+ }
197
+ }
198
+ return deviceResults
199
+ }
200
+ /** Assert Sessions */
201
+ const assertSessions = async (jids, force) => {
202
+ let didFetchNewSession = false
203
+ let jidsRequiringFetch = []
204
+ if (force) {
205
+ jidsRequiringFetch = jids
206
+ }
207
+ else {
208
+ const addrs = jids.map(jid => (signalRepository.jidToSignalProtocolAddress(jid)))
209
+ const sessions = await authState.keys.get('session', addrs)
210
+ for (const jid of jids) {
211
+ const signalId = signalRepository
212
+ .jidToSignalProtocolAddress(jid)
213
+ if (!sessions[signalId]) {
214
+ jidsRequiringFetch.push(jid)
215
+ }
216
+ }
217
+ }
218
+ if (jidsRequiringFetch.length) {
219
+ logger.debug({ jidsRequiringFetch }, 'fetching sessions')
220
+ const result = await query({
221
+ tag: 'iq',
222
+ attrs: {
223
+ xmlns: 'encrypt',
224
+ type: 'get',
225
+ to: WABinary_1.S_WHATSAPP_NET,
226
+ },
227
+ content: [
228
+ {
229
+ tag: 'key',
230
+ attrs: {},
231
+ content: jidsRequiringFetch.map(jid => ({
232
+ tag: 'user',
233
+ attrs: { jid },
234
+ }))
235
+ }
236
+ ]
237
+ })
238
+ await Utils_1.parseAndInjectE2ESessions(result, signalRepository)
239
+ didFetchNewSession = true
240
+ }
241
+ return didFetchNewSession
242
+ }
243
+ /** Send Peer Operation */
244
+ const sendPeerDataOperationMessage = async (pdoMessage) => {
245
+ //TODO: for later, abstract the logic to send a Peer Message instead of just PDO - useful for App State Key Resync with phone
246
+ if (!authState.creds.me?.id) {
247
+ throw new boom_1.Boom('Not authenticated')
248
+ }
249
+ const protocolMessage = {
250
+ protocolMessage: {
251
+ peerDataOperationRequestMessage: pdoMessage,
252
+ type: WAProto_1.proto.Message.ProtocolMessage.Type.PEER_DATA_OPERATION_REQUEST_MESSAGE
253
+ }
254
+ }
255
+ const meJid = WABinary_1.jidNormalizedUser(authState.creds.me.id)
256
+ const msgId = await relayMessage(meJid, protocolMessage, {
257
+ additionalAttributes: {
258
+ category: 'peer',
259
+ // eslint-disable-next-line camelcase
260
+ push_priority: 'high_force',
261
+ },
262
+ })
263
+ return msgId
264
+ }
265
+ const createParticipantNodes = async (jids, message, extraAttrs) => {
266
+ const patched = await patchMessageBeforeSending(message, jids)
267
+ const bytes = Utils_1.encodeWAMessage(patched)
268
+ let shouldIncludeDeviceIdentity = false
269
+ const nodes = await Promise.all(jids.map(async (jid) => {
270
+ const { type, ciphertext } = await signalRepository.encryptMessage({ jid, data: bytes })
271
+ if (type === 'pkmsg') {
272
+ shouldIncludeDeviceIdentity = true
273
+ }
274
+ const node = {
275
+ tag: 'to',
276
+ attrs: { jid },
277
+ content: [{
278
+ tag: 'enc',
279
+ attrs: {
280
+ v: '2',
281
+ type,
282
+ ...extraAttrs || {}
283
+ },
284
+ content: ciphertext
285
+ }]
286
+ }
287
+ return node
288
+ }))
289
+ return {
290
+ nodes,
291
+ shouldIncludeDeviceIdentity
292
+ }
293
+ }
294
+ const relayMessage = async (jid, message, { messageId: msgId, participant, additionalAttributes, useUserDevicesCache, useCachedGroupMetadata, statusJidList, additionalNodes }) => {
295
+ const meId = authState.creds.me.id
296
+ let didPushAdditional = false
297
+ let shouldIncludeDeviceIdentity = false
298
+ const { user, server } = WABinary_1.jidDecode(jid)
299
+ const statusJid = 'status@broadcast'
300
+ const isGroup = server === 'g.us'
301
+ const isPrivate = server === 's.whatsapp.net'
302
+ const isNewsletter = server == 'newsletter'
303
+ const isStatus = jid === statusJid
304
+ const isLid = server === 'lid'
305
+ msgId = msgId || Utils_1.generateMessageID(authState.creds.me.id)
306
+ useUserDevicesCache = useUserDevicesCache !== false
307
+ useCachedGroupMetadata = useCachedGroupMetadata !== false && !isStatus
308
+ const participants = []
309
+ const destinationJid = (!isStatus) ? WABinary_1.jidEncode(user, isLid ? 'lid' : isGroup ? 'g.us' : isNewsletter ? 'newsletter' : 's.whatsapp.net') : statusJid
310
+ const binaryNodeContent = []
311
+ const devices = []
312
+ const meMsg = {
313
+ deviceSentMessage: {
314
+ destinationJid,
315
+ message
316
+ }
317
+ }
318
+ const extraAttrs = {}
319
+ if (participant) {
320
+ // when the retry request is not for a group
321
+ // only send to the specific device that asked for a retry
322
+ // otherwise the message is sent out to every device that should be a recipient
323
+ if (!isGroup && !isStatus) {
324
+ additionalAttributes = { ...additionalAttributes, 'device_fanout': 'false' }
325
+ }
326
+ const { user, device } = WABinary_1.jidDecode(participant.jid)
327
+ devices.push({ user, device })
328
+ }
329
+ await authState.keys.transaction(async () => {
330
+ const mediaType = getMediaType(message)
331
+ if (mediaType) {
332
+ extraAttrs['mediatype'] = mediaType
333
+ }
334
+ if (Utils_1.normalizeMessageContent(message)?.pinInChatMessage || Utils_1.normalizeMessageContent(message)?.keepInChatMessage) {
335
+ extraAttrs['decrypt-fail'] = 'hide'
336
+ }
337
+ if (isGroup || isStatus) {
338
+ const [groupData, senderKeyMap] = await Promise.all([
339
+ (async () => {
340
+ let groupData = useCachedGroupMetadata && cachedGroupMetadata ? await cachedGroupMetadata(jid) : undefined
341
+ if (groupData && Array.isArray(groupData?.participants)) {
342
+ logger.trace({ jid, participants: groupData.participants.length }, 'using cached group metadata')
343
+ }
344
+ else if (!isStatus) {
345
+ groupData = await groupMetadata(jid)
346
+ }
347
+ return groupData
348
+ })(),
349
+ (async () => {
350
+ if (!participant && !isStatus) {
351
+ const result = await authState.keys.get('sender-key-memory', [jid])
352
+ return result[jid] || {}
353
+ }
354
+ return {}
355
+ })()
356
+ ])
357
+ if (!participant) {
358
+ const participantsList = (groupData && !isStatus) ? groupData.participants.map(p => p.id) : []
359
+ if (isStatus && statusJidList) {
360
+ participantsList.push(...statusJidList)
361
+ }
362
+ if (!isStatus) {
363
+ additionalAttributes = {
364
+ ...additionalAttributes,
365
+ addressing_mode: groupData?.addressingMode || 'pn'
366
+ }
367
+ }
368
+ const additionalDevices = await getUSyncDevices(participantsList, !!useUserDevicesCache, false)
369
+ devices.push(...additionalDevices)
370
+ }
371
+ const patched = await patchMessageBeforeSending(message, devices.map(d => WABinary_1.jidEncode(d.user, isLid ? 'lid' : 's.whatsapp.net', d.device)))
372
+ const bytes = Utils_1.encodeWAMessage(patched)
373
+ const { ciphertext, senderKeyDistributionMessage } = await signalRepository.encryptGroupMessage({
374
+ group: destinationJid,
375
+ data: bytes,
376
+ meId,
377
+ })
378
+ const senderKeyJids = []
379
+ // ensure a connection is established with every device
380
+ for (const { user, device } of devices) {
381
+ const jid = WABinary_1.jidEncode(user, groupData?.addressingMode === 'lid' ? 'lid' : 's.whatsapp.net', device)
382
+ if (!senderKeyMap[jid] || !!participant) {
383
+ senderKeyJids.push(jid)
384
+ // store that this person has had the sender keys sent to them
385
+ senderKeyMap[jid] = true
386
+ }
387
+ }
388
+ // if there are some participants with whom the session has not been established
389
+ // if there are, we re-send the senderkey
390
+ if (senderKeyJids.length) {
391
+ logger.debug({ senderKeyJids }, 'sending new sender key')
392
+ const senderKeyMsg = {
393
+ senderKeyDistributionMessage: {
394
+ axolotlSenderKeyDistributionMessage: senderKeyDistributionMessage,
395
+ groupId: destinationJid
396
+ }
397
+ }
398
+ await assertSessions(senderKeyJids, false)
399
+ const result = await createParticipantNodes(senderKeyJids, senderKeyMsg, extraAttrs)
400
+ shouldIncludeDeviceIdentity = shouldIncludeDeviceIdentity || result.shouldIncludeDeviceIdentity
401
+ participants.push(...result.nodes)
402
+ }
403
+ binaryNodeContent.push({
404
+ tag: 'enc',
405
+ attrs: { v: '2', type: 'skmsg' },
406
+ content: ciphertext
407
+ })
408
+ await authState.keys.set({ 'sender-key-memory': { [jid]: senderKeyMap } })
409
+ }
410
+ else if (isNewsletter) {
411
+ // Message edit
412
+ if (message.protocolMessage?.editedMessage) {
413
+ msgId = message.protocolMessage.key?.id
414
+ message = message.protocolMessage.editedMessage
415
+ }
416
+ // Message delete
417
+ if (message.protocolMessage?.type === WAProto_1.proto.Message.ProtocolMessage.Type.REVOKE) {
418
+ msgId = message.protocolMessage.key?.id
419
+ message = {}
420
+ }
421
+ const patched = await patchMessageBeforeSending(message, [])
422
+ const bytes = Utils_1.encodeNewsletterMessage(patched)
423
+ binaryNodeContent.push({
424
+ tag: 'plaintext',
425
+ attrs: mediaType ? { mediatype: mediaType } : {},
426
+ content: bytes
427
+ })
428
+ }
429
+ else {
430
+ const { user: meUser } = WABinary_1.jidDecode(meId)
431
+ if (!participant) {
432
+ devices.push({ user })
433
+ if (user !== meUser) {
434
+ devices.push({ user: meUser })
435
+ }
436
+ if (additionalAttributes?.['category'] !== 'peer') {
437
+ const additionalDevices = await getUSyncDevices([meId, jid], !!useUserDevicesCache, true)
438
+ devices.push(...additionalDevices)
439
+ }
440
+ }
441
+ const allJids = []
442
+ const meJids = []
443
+ const otherJids = []
444
+ for (const { user, device } of devices) {
445
+ const isMe = user === meUser
446
+ const jid = WABinary_1.jidEncode(isMe && isLid ? authState.creds?.me?.lid?.split(':')[0] || user : user, isLid ? 'lid' : 's.whatsapp.net', device)
447
+ if (isMe) {
448
+ meJids.push(jid)
449
+ }
450
+ else {
451
+ otherJids.push(jid)
452
+ }
453
+ allJids.push(jid)
454
+ }
455
+ await assertSessions(allJids, false)
456
+ const [{ nodes: meNodes, shouldIncludeDeviceIdentity: s1 }, { nodes: otherNodes, shouldIncludeDeviceIdentity: s2 }] = await Promise.all([
457
+ createParticipantNodes(meJids, meMsg, extraAttrs),
458
+ createParticipantNodes(otherJids, message, extraAttrs)
459
+ ])
460
+ participants.push(...meNodes)
461
+ participants.push(...otherNodes)
462
+ shouldIncludeDeviceIdentity = shouldIncludeDeviceIdentity || s1 || s2
463
+ }
464
+ if (participants.length) {
465
+ if (additionalAttributes?.['category'] === 'peer') {
466
+ const peerNode = participants[0]?.content?.[0]
467
+ if (peerNode) {
468
+ binaryNodeContent.push(peerNode) // push only enc
469
+ }
470
+ }
471
+ else {
472
+ binaryNodeContent.push({
473
+ tag: 'participants',
474
+ attrs: {},
475
+ content: participants
476
+ })
477
+ }
478
+ }
479
+ const stanza = {
480
+ tag: 'message',
481
+ attrs: {
482
+ id: msgId,
483
+ type: isNewsletter ? getTypeMessage(message) : 'text',
484
+ ...(additionalAttributes || {})
485
+ },
486
+ content: binaryNodeContent
487
+ }
488
+ // if the participant to send to is explicitly specified (generally retry recp)
489
+ // ensure the message is only sent to that person
490
+ // if a retry receipt is sent to everyone -- it'll fail decryption for everyone else who received the msg
491
+ if (participant) {
492
+ if (WABinary_1.isJidGroup(destinationJid)) {
493
+ stanza.attrs.to = destinationJid
494
+ stanza.attrs.participant = participant.jid
495
+ }
496
+ else if (WABinary_1.areJidsSameUser(participant.jid, meId)) {
497
+ stanza.attrs.to = participant.jid
498
+ stanza.attrs.recipient = destinationJid
499
+ }
500
+ else {
501
+ stanza.attrs.to = participant.jid
502
+ }
503
+ }
504
+ else {
505
+ stanza.attrs.to = destinationJid
506
+ }
507
+ if (shouldIncludeDeviceIdentity) {
508
+ stanza.content.push({
509
+ tag: 'device-identity',
510
+ attrs: {},
511
+ content: Utils_1.encodeSignedDeviceIdentity(authState.creds.account, true)
512
+ })
513
+ logger.debug({ jid }, 'adding device identity')
514
+ }
515
+ const messages = Utils_1.normalizeMessageContent(message)
516
+ const buttonType = getButtonType(messages)
517
+ if (!isNewsletter && buttonType) {
518
+ if (!stanza.content || !Array.isArray(stanza.content)) {
519
+ stanza.content = []
520
+ }
521
+ const buttonsNode = getButtonArgs(messages)
522
+ const filteredButtons = WABinary_1.getBinaryFilteredButtons(additionalNodes ? additionalNodes : [])
523
+ if (filteredButtons) {
524
+ stanza.content.push(...additionalNodes)
525
+ didPushAdditional = true
526
+ }
527
+ else {
528
+ stanza.content.push(buttonsNode)
529
+ }
530
+ }
531
+ if (isPrivate) {
532
+ if (!stanza.content || !Array.isArray(stanza.content)) {
533
+ stanza.content = []
534
+ }
535
+ const botNode = {
536
+ tag: 'bot',
537
+ attrs: {
538
+ biz_bot: '1'
539
+ }
540
+ }
541
+ const filteredBizBot = WABinary_1.getBinaryFilteredBizBot(additionalNodes ? additionalNodes : [])
542
+ if (filteredBizBot) {
543
+ stanza.content.push(...additionalNodes)
544
+ didPushAdditional = true
545
+ }
546
+ else {
547
+ stanza.content.push(botNode)
548
+ }
549
+ }
550
+ if (!didPushAdditional && additionalNodes && additionalNodes.length > 0) {
551
+ if (!stanza.content || !Array.isArray(stanza.content)) {
552
+ stanza.content = []
553
+ }
554
+ stanza.content.push(...additionalNodes)
555
+ }
556
+ logger.debug({ msgId }, `sending message to ${participants.length} devices`)
557
+ await sendNode(stanza)
558
+ })
559
+ return msgId
560
+ }
561
+ const getTypeMessage = (msg) => {
562
+ const message = Utils_1.normalizeMessageContent(msg)
563
+ if (message.reactionMessage) {
564
+ return 'reaction'
565
+ }
566
+ else if (getMediaType(message)) {
567
+ return 'media'
568
+ }
569
+ else {
570
+ return 'text'
571
+ }
572
+ }
573
+ const getMediaType = (message) => {
574
+ if (message.imageMessage) {
575
+ return 'image'
576
+ }
577
+ else if (message.stickerMessage) {
578
+ return message.stickerMessage.isLottie ? '1p_sticker' : 'sticker'
579
+ }
580
+ else if (message.videoMessage) {
581
+ return message.videoMessage.gifPlayback ? 'gif' : 'video'
582
+ }
583
+ else if (message.audioMessage) {
584
+ return message.audioMessage.ptt ? 'ptt' : 'audio'
585
+ }
586
+ else if (message.ptvMessage) {
587
+ return 'ptv'
588
+ }
589
+ else if (message.contactMessage) {
590
+ return 'vcard'
591
+ }
592
+ else if (message.documentMessage) {
593
+ return 'document'
594
+ }
595
+ else if (message.contactsArrayMessage) {
596
+ return 'contact_array'
597
+ }
598
+ else if (message.liveLocationMessage) {
599
+ return 'livelocation'
600
+ }
601
+ else if (message.stickerMessage) {
602
+ return 'sticker'
603
+ }
604
+ else if (message.listMessage) {
605
+ return 'list'
606
+ }
607
+ else if (message.listResponseMessage) {
608
+ return 'list_response'
609
+ }
610
+ else if (message.buttonsResponseMessage) {
611
+ return 'buttons_response'
612
+ }
613
+ else if (message.orderMessage) {
614
+ return 'order'
615
+ }
616
+ else if (message.productMessage) {
617
+ return 'product'
618
+ }
619
+ else if (message.interactiveResponseMessage) {
620
+ return 'native_flow_response'
621
+ }
622
+ else if (message.groupInviteMessage) {
623
+ return 'url'
624
+ }
625
+ else if (/https:\/\/wa\.me\/c\/\d+/.test(message.extendedTextMessage?.text)) {
626
+ return 'cataloglink'
627
+ }
628
+ else if (/https:\/\/wa\.me\/p\/\d+\/\d+/.test(message.extendedTextMessage?.text)) {
629
+ return 'productlink'
630
+ }
631
+ }
632
+ const getButtonType = (message) => {
633
+ if (message.listMessage) {
634
+ return 'list'
635
+ }
636
+ else if (message.buttonsMessage) {
637
+ return 'buttons'
638
+ }
639
+ else if(message.interactiveMessage?.nativeFlowMessage) {
640
+ return 'native_flow'
641
+ }
642
+ }
643
+ const getButtonArgs = (message) => {
644
+ const nativeFlow = message.interactiveMessage?.nativeFlowMessage
645
+ const firstButtonName = nativeFlow?.buttons?.[0]?.name
646
+ const nativeFlowSpecials = [
647
+ 'mpm', 'cta_catalog', 'send_location',
648
+ 'call_permission_request', 'wa_payment_transaction_details',
649
+ 'automated_greeting_message_view_catalog'
650
+ ]
651
+ if (nativeFlow && (firstButtonName === 'review_and_pay' || firstButtonName === 'payment_info')) {
652
+ return {
653
+ tag: 'biz',
654
+ attrs: {
655
+ native_flow_name: firstButtonName
656
+ }
657
+ }
658
+ } else if (nativeFlow && nativeFlowSpecials.includes(firstButtonName)) {
659
+ // Only works for WhatsApp Original, not WhatsApp Business
660
+ return {
661
+ tag: 'biz',
662
+ attrs: {},
663
+ content: [{
664
+ tag: 'interactive',
665
+ attrs: {
666
+ type: 'native_flow',
667
+ v: '1'
668
+ },
669
+ content: [{
670
+ tag: 'native_flow',
671
+ attrs: {
672
+ v: '2',
673
+ name: firstButtonName
674
+ }
675
+ }]
676
+ }]
677
+ }
678
+ } else if (nativeFlow || message.buttonsMessage) {
679
+ // It works for whatsapp original and whatsapp business
680
+ return {
681
+ tag: 'biz',
682
+ attrs: {},
683
+ content: [{
684
+ tag: 'interactive',
685
+ attrs: {
686
+ type: 'native_flow',
687
+ v: '1'
688
+ },
689
+ content: [{
690
+ tag: 'native_flow',
691
+ attrs: {
692
+ v: '9',
693
+ name: 'mixed'
694
+ }
695
+ }]
696
+ }]
697
+ }
698
+ } else if (message.listMessage) {
699
+ return {
700
+ tag: 'biz',
701
+ attrs: {},
702
+ content: [{
703
+ tag: 'list',
704
+ attrs: {
705
+ v: '2',
706
+ type: 'product_list'
707
+ }
708
+ }]
709
+ }
710
+ } else {
711
+ return {
712
+ tag: 'biz',
713
+ attrs: {}
714
+ }
715
+ }
716
+ }
717
+ const getPrivacyTokens = async (jids) => {
718
+ const t = Utils_1.unixTimestampSeconds().toString()
719
+ const result = await query({
720
+ tag: 'iq',
721
+ attrs: {
722
+ to: WABinary_1.S_WHATSAPP_NET,
723
+ type: 'set',
724
+ xmlns: 'privacy'
725
+ },
726
+ content: [
727
+ {
728
+ tag: 'tokens',
729
+ attrs: {},
730
+ content: jids.map(jid => ({
731
+ tag: 'token',
732
+ attrs: {
733
+ jid: WABinary_1.jidNormalizedUser(jid),
734
+ t,
735
+ type: 'trusted_contact'
736
+ }
737
+ }))
738
+ }
739
+ ]
740
+ })
741
+ return result
742
+ }
743
+ const waUploadToServer = Utils_1.getWAUploadToServer(config, refreshMediaConn)
744
+ const waitForMsgMediaUpdate = Utils_1.bindWaitForEvent(ev, 'messages.media-update')
745
+ return {
746
+ ...sock,
747
+ getPrivacyTokens,
748
+ assertSessions,
749
+ relayMessage,
750
+ sendReceipt,
751
+ sendReceipts,
752
+ readMessages,
753
+ profilePictureUrl,
754
+ getUSyncDevices,
755
+ refreshMediaConn,
756
+ waUploadToServer,
757
+ fetchPrivacySettings,
758
+ createParticipantNodes,
759
+ sendPeerDataOperationMessage,
760
+ updateMediaMessage: async (message) => {
761
+ const content = Utils_1.assertMediaContent(message.message)
762
+ const mediaKey = content.mediaKey
763
+ const meId = authState.creds.me.id
764
+ const node = await Utils_1.encryptMediaRetryRequest(message.key, mediaKey, meId)
765
+ let error = undefined
766
+ await Promise.all([
767
+ sendNode(node),
768
+ waitForMsgMediaUpdate(async (update) => {
769
+ const result = update.find(c => c.key.id === message.key.id)
770
+ if (result) {
771
+ if (result.error) {
772
+ error = result.error
773
+ }
774
+ else {
775
+ try {
776
+ const media = await Utils_1.decryptMediaRetryData(result.media, mediaKey, result.key.id)
777
+ if (media.result !== WAProto_1.proto.MediaRetryNotification.ResultType.SUCCESS) {
778
+ const resultStr = WAProto_1.proto.MediaRetryNotification.ResultType[media.result]
779
+ throw new boom_1.Boom(`Media re-upload failed by device (${resultStr})`, { data: media, statusCode: Utils_1.getStatusCodeForMediaRetry(media.result) || 404 })
780
+ }
781
+ content.directPath = media.directPath
782
+ content.url = Utils_1.getUrlFromDirectPath(content.directPath)
783
+ logger.debug({ directPath: media.directPath, key: result.key }, 'media update successful')
784
+ }
785
+ catch (err) {
786
+ error = err
787
+ }
788
+ }
789
+ return true
790
+ }
791
+ })
792
+ ])
793
+ if (error) {
794
+ throw error
795
+ }
796
+ ev.emit('messages.update', [
797
+ { key: message.key, update: { message: message.message } }
798
+ ])
799
+ return message
800
+ },
801
+ sendStatusMentions: async (jid, content) => {
802
+ const media = await Utils_1.generateWAMessage(WABinary_1.STORIES_JID, content, {
803
+ upload: await waUploadToServer,
804
+ backgroundColor: "#" + Math.floor(Math.random() * 16777215).toString(16).padStart(6, "0"),
805
+ font: content.text ? Math.floor(Math.random() * 9) : null
806
+ })
807
+ const additionalNodes = [{
808
+ tag: 'meta',
809
+ attrs: {},
810
+ content: [{
811
+ tag: 'mentioned_users',
812
+ attrs: {},
813
+ content: [{
814
+ tag: 'to',
815
+ attrs: { jid },
816
+ content: undefined
817
+ }]
818
+ }]
819
+ }]
820
+ let Private = WABinary_1.isJidUser(jid)
821
+ let statusJid = Private ? [jid] : (await groupMetadata(jid)).participants.map((num) => num.id)
822
+ await relayMessage(WABinary_1.STORIES_JID, media.message, {
823
+ messageId: media.key.id,
824
+ statusJidList: statusJid,
825
+ additionalNodes
826
+ })
827
+ let type = Private ? 'statusMentionMessage' : 'groupStatusMentionMessage'
828
+ let msg = await Utils_1.generateWAMessageFromContent(jid, {
829
+ [type]: {
830
+ message: {
831
+ protocolMessage: {
832
+ key: media.key,
833
+ type: 25
834
+ }
835
+ }
836
+ }
837
+ }, {})
838
+ await relayMessage(jid, msg.message, {})
839
+ return media
840
+ },
841
+ sendAlbumMessage: async (jid, medias, options = {}) => {
842
+ const userJid = authState.creds.me.id
843
+ for (const media of medias) {
844
+ if (!media.image && !media.video)
845
+ throw new TypeError(`medias[i] must have image or video property`)
846
+ }
847
+ const time = options.delay || 500
848
+ delete options.delay
849
+ const album = await Utils_1.generateWAMessageFromContent(jid, {
850
+ albumMessage: {
851
+ expectedImageCount: medias.filter(media => media.image).length,
852
+ expectedVideoCount: medias.filter(media => media.video).length,
853
+ ...options
854
+ }
855
+ }, { userJid, ...options })
856
+ await relayMessage(jid, album.message, { messageId: album.key.id })
857
+ let mediaHandle
858
+ let msg
859
+ for (const i in medias) {
860
+ const media = medias[i]
861
+ if (media.image) {
862
+ msg = await Utils_1.generateWAMessage(jid, {
863
+ image: media.image,
864
+ ...media,
865
+ ...options
866
+ }, {
867
+ userJid,
868
+ upload: async (encFilePath, opts) => {
869
+ const up = await waUploadToServer(encFilePath, { ...opts, newsletter: WABinary_1.isJidNewsletter(jid) })
870
+ mediaHandle = up.handle
871
+ return up
872
+ },
873
+ ...options
874
+ })
875
+ }
876
+ else if (media.video) {
877
+ msg = await Utils_1.generateWAMessage(jid, {
878
+ video: media.video,
879
+ ...media,
880
+ ...options
881
+ }, {
882
+ userJid,
883
+ upload: async (encFilePath, opts) => {
884
+ const up = await waUploadToServer(encFilePath, { ...opts, newsletter: WABinary_1.isJidNewsletter(jid) })
885
+ mediaHandle = up.handle
886
+ return up
887
+ },
888
+ ...options,
889
+ })
890
+ }
891
+ if (msg) {
892
+ msg.message.messageContextInfo = {
893
+ messageSecret: crypto_1.randomBytes(32),
894
+ messageAssociation: {
895
+ associationType: 1,
896
+ parentMessageKey: album.key
897
+ }
898
+ }
899
+ }
900
+ await relayMessage(jid, msg.message, { messageId: msg.key.id })
901
+ await Utils_1.delay(time)
902
+ }
903
+ return album
904
+ },
905
+ sendMessage: async (jid, content, options = {}) => {
906
+ const userJid = authState.creds.me.id
907
+ if (typeof content === 'object' &&
908
+ 'disappearingMessagesInChat' in content &&
909
+ typeof content['disappearingMessagesInChat'] !== 'undefined' &&
910
+ WABinary_1.isJidGroup(jid)) {
911
+ const { disappearingMessagesInChat } = content
912
+ const value = typeof disappearingMessagesInChat === 'boolean' ?
913
+ (disappearingMessagesInChat ? Defaults_1.WA_DEFAULT_EPHEMERAL : 0) :
914
+ disappearingMessagesInChat
915
+ await groupToggleEphemeral(jid, value)
916
+ }
917
+ else {
918
+ let mediaHandle
919
+ const fullMsg = await Utils_1.generateWAMessage(jid, content, {
920
+ logger,
921
+ userJid,
922
+ getUrlInfo: text => link_preview_1.getUrlInfo(text, {
923
+ thumbnailWidth: linkPreviewImageThumbnailWidth,
924
+ fetchOpts: {
925
+ timeout: 3000,
926
+ ...axiosOptions || {}
927
+ },
928
+ logger,
929
+ uploadImage: generateHighQualityLinkPreview
930
+ ? waUploadToServer
931
+ : undefined
932
+ }),
933
+ getProfilePicUrl: profilePictureUrl,
934
+ upload: async (encFilePath, opts) => {
935
+ const up = await waUploadToServer(encFilePath, { ...opts, newsletter: WABinary_1.isJidNewsletter(jid) })
936
+ mediaHandle = up.handle
937
+ return up
938
+ },
939
+ mediaCache: config.mediaCache,
940
+ options: config.options,
941
+ messageId: Utils_1.generateMessageID(userJid),
942
+ ...options,
943
+ })
944
+ const isPin = 'pin' in content && !!content.pin
945
+ const isEdit = 'edit' in content && !!content.edit
946
+ const isDelete = 'delete' in content && !!content.delete
947
+ const additionalAttributes = {}
948
+ if (isDelete) {
949
+ // if the chat is a group, and I am not the author, then delete the message as an admin
950
+ if (WABinary_1.isJidGroup(content.delete?.remoteJid) && !content.delete?.fromMe || WABinary_1.isJidNewsletter(jid)) {
951
+ additionalAttributes.edit = '8'
952
+ }
953
+ else {
954
+ additionalAttributes.edit = '7'
955
+ }
956
+ }
957
+ else if (isEdit) {
958
+ additionalAttributes.edit = WABinary_1.isJidNewsletter(jid) ? '3' : '1'
959
+ }
960
+ else if (isPin) {
961
+ additionalAttributes.edit = '2'
962
+ }
963
+ if (mediaHandle) {
964
+ additionalAttributes['media_id'] = mediaHandle
965
+ }
966
+ if ('cachedGroupMetadata' in options) {
967
+ console.warn('cachedGroupMetadata in sendMessage are deprecated, now cachedGroupMetadata is part of the socket config.')
968
+ }
969
+ await relayMessage(jid, fullMsg.message, { messageId: fullMsg.key.id, useCachedGroupMetadata: options.useCachedGroupMetadata, additionalAttributes, statusJidList: options.statusJidList, additionalNodes: options.additionalNodes })
970
+ if (config.emitOwnEvents) {
971
+ process.nextTick(() => {
972
+ processingMutex.mutex(() => (upsertMessage(fullMsg, 'append')))
973
+ })
974
+ }
975
+ return fullMsg
976
+ }
977
+ }
978
+ }
979
+ }
980
+ module.exports = {
981
+ makeMessagesSocket
984
982
  }