@badzz88/baileys 8.5.4 → 8.5.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/WAProto/WAProto.proto +75 -1256
- package/WAProto/index.d.ts +19729 -36122
- package/WAProto/index.js +132193 -178538
- package/engine-requirements.js +15 -7
- package/lib/Defaults/index.js +14 -22
- package/lib/Signal/Group/group_cipher.js +5 -5
- package/lib/Signal/Group/keyhelper.js +4 -4
- package/lib/Signal/Group/sender-chain-key.js +2 -2
- package/lib/Signal/Group/sender-key-distribution-message.js +1 -1
- package/lib/Signal/Group/sender-key-message.js +3 -3
- package/lib/Signal/Group/sender-key-record.js +1 -1
- package/lib/Signal/Group/sender-message-key.js +5 -5
- package/lib/Signal/libsignal.js +28 -39
- package/lib/Signal/lid-mapping.js +8 -28
- package/lib/Socket/aigroups.js +5 -5
- package/lib/Socket/business.js +11 -3
- package/lib/Socket/chats.js +319 -668
- package/lib/Socket/communities.js +89 -85
- package/lib/Socket/graphql.js +228 -435
- package/lib/Socket/groups.js +211 -132
- package/lib/Socket/index.js +7 -9
- package/lib/Socket/interactive-handler.js +60 -117
- package/lib/Socket/interop.js +175 -56
- package/lib/Socket/luxu.js +387 -0
- package/lib/Socket/managed-account.js +48 -79
- package/lib/Socket/messages-recv.js +340 -898
- package/lib/Socket/messages-send.js +160 -462
- package/lib/Socket/mex.js +53 -40
- package/lib/Socket/newsletter.js +295 -291
- package/lib/Socket/privacy.js +265 -77
- package/lib/Socket/registration.js +157 -164
- package/lib/Socket/socket.js +199 -181
- package/lib/Socket/text-router.js +83 -0
- package/lib/Socket/username.js +227 -144
- package/lib/Store/keyed-db.js +108 -0
- package/lib/Store/make-cache-manager-store.js +2 -2
- package/lib/Store/make-in-memory-store.js +141 -25
- package/lib/Types/Auth.js +3 -3
- package/lib/Types/Label.js +1 -1
- package/lib/Types/LabelAssociation.js +1 -1
- package/lib/Types/Mex.js +112 -0
- package/lib/Types/Newsletter.js +65 -96
- package/lib/Types/State.js +4 -4
- package/lib/Types/index.js +1 -1
- package/lib/Utils/auth-utils.js +57 -52
- package/lib/Utils/browser-utils.js +1 -1
- package/lib/Utils/business.js +6 -2
- package/lib/Utils/chat-utils.js +38 -102
- 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 +42 -0
- package/lib/Utils/crypto.js +84 -54
- package/lib/Utils/curve25519-js.js +275 -0
- package/lib/Utils/decode-wa-message.js +83 -316
- package/lib/Utils/event-buffer.js +61 -37
- package/lib/Utils/generics.js +103 -157
- package/lib/Utils/group-history.js +12 -3
- package/lib/Utils/history.js +15 -15
- package/lib/Utils/index.js +5 -1
- package/lib/Utils/link-preview.js +8 -3
- package/lib/Utils/lt-hash.js +35 -2
- package/lib/Utils/make-mutex.js +1 -1
- package/lib/Utils/message-retry-manager.js +92 -27
- package/lib/Utils/messages-media.js +130 -159
- package/lib/Utils/messages.js +566 -144
- package/lib/Utils/meta-ai-msmsg.js +202 -62
- package/lib/Utils/native-bridge.js +82 -0
- package/lib/Utils/noise-handler.js +21 -19
- package/lib/Utils/offline-node-processor.js +8 -3
- package/lib/Utils/pre-key-manager.js +23 -13
- package/lib/Utils/process-message.js +106 -197
- package/lib/Utils/reporting-utils.js +2 -2
- package/lib/Utils/session-pool.d.ts +16 -0
- package/lib/Utils/session-pool.js +94 -0
- package/lib/Utils/signal.js +63 -28
- package/lib/Utils/stanza-ack.js +12 -39
- package/lib/Utils/sticker.d.ts +17 -0
- package/lib/Utils/sticker.js +145 -0
- package/lib/Utils/sync-action-utils.js +7 -4
- package/lib/Utils/tc-token-utils.js +25 -20
- package/lib/Utils/use-multi-file-auth-state.js +14 -30
- package/lib/Utils/validate-connection.js +19 -59
- package/lib/Utils/view-once-cache.d.ts +9 -0
- package/lib/Utils/view-once-cache.js +79 -0
- package/lib/WABinary/decode.js +16 -50
- package/lib/WABinary/encode.js +224 -54
- package/lib/WABinary/generic-utils.js +97 -7
- package/lib/WABinary/jid-utils.js +14 -27
- package/lib/WAM/encode.js +7 -7
- package/lib/WAUSync/Protocols/USyncBusinessProtocol.js +1 -58
- package/lib/WAUSync/Protocols/USyncContactProtocol.js +1 -7
- package/lib/WAUSync/Protocols/USyncDeviceProtocol.js +5 -13
- package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.js +4 -1
- package/lib/WAUSync/Protocols/USyncFeatureProtocol.js +7 -20
- package/lib/WAUSync/Protocols/USyncPictureProtocol.js +1 -1
- package/lib/WAUSync/Protocols/USyncSidelistProtocol.js +2 -2
- package/lib/WAUSync/Protocols/USyncStatusProtocol.js +2 -7
- package/lib/WAUSync/Protocols/USyncTextStatusProtocol.js +1 -1
- package/lib/WAUSync/Protocols/USyncUsernameProtocol.js +1 -2
- package/lib/WAUSync/Protocols/index.js +0 -1
- package/lib/WAUSync/USyncQuery.js +6 -77
- package/lib/WAUSync/USyncUser.js +8 -16
- package/lib/WAUSync/index.js +0 -1
- package/lib/antiban.js +854 -608
- package/lib/index.js +1 -1
- package/package.json +20 -16
|
@@ -48,47 +48,26 @@ const makeMessagesSocket = config => {
|
|
|
48
48
|
trustInteropContact
|
|
49
49
|
} = sock
|
|
50
50
|
const getLIDForPN = signalRepository.lidMapping.getLIDForPN.bind(signalRepository.lidMapping)
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
const seen = new Set()
|
|
56
|
-
const allUsers = []
|
|
57
|
-
for (const gjid of groupJids) {
|
|
58
|
-
let meta
|
|
59
|
-
try {
|
|
60
|
-
meta = cachedGroupMetadata ? await cachedGroupMetadata(gjid) : null
|
|
61
|
-
if (!meta) meta = await groupMetadata(gjid)
|
|
62
|
-
} catch {
|
|
63
|
-
continue
|
|
64
|
-
}
|
|
65
|
-
for (const p of meta?.participants || []) {
|
|
66
|
-
const id = p.id
|
|
67
|
-
if (!id || seen.has(id)) continue
|
|
68
|
-
if (!includeMe && id === myJid) continue
|
|
69
|
-
seen.add(id)
|
|
70
|
-
allUsers.push(id)
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
return { allUsers }
|
|
74
|
-
}
|
|
75
|
-
|
|
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
|
+
*/
|
|
76
55
|
const inFlightTcTokenIssuance = new Set()
|
|
77
56
|
const userDevicesCache =
|
|
78
57
|
config.userDevicesCache ||
|
|
79
58
|
new node_cache_1.default({
|
|
80
|
-
stdTTL: Defaults_1.DEFAULT_CACHE_TTLS.USER_DEVICES,
|
|
59
|
+
stdTTL: Defaults_1.DEFAULT_CACHE_TTLS.USER_DEVICES, // 5 minutes
|
|
81
60
|
useClones: false
|
|
82
61
|
})
|
|
83
62
|
const peerSessionsCache = new node_cache_1.default({
|
|
84
63
|
stdTTL: Defaults_1.DEFAULT_CACHE_TTLS.USER_DEVICES,
|
|
85
64
|
useClones: false
|
|
86
65
|
})
|
|
87
|
-
|
|
66
|
+
// Initialize message retry manager if enabled
|
|
88
67
|
const messageRetryManager = enableRecentMessageCache
|
|
89
68
|
? new Utils_1.MessageRetryManager(logger, maxMsgRetryCount)
|
|
90
69
|
: null
|
|
91
|
-
|
|
70
|
+
// Prevent race conditions in Signal session encryption by user
|
|
92
71
|
const encryptionMutex = (0, make_mutex_1.makeKeyedMutex)()
|
|
93
72
|
let mediaConn
|
|
94
73
|
const refreshMediaConn = async (forceGet = false) => {
|
|
@@ -105,7 +84,7 @@ const makeMessagesSocket = config => {
|
|
|
105
84
|
content: [{ tag: 'media_conn', attrs: {} }]
|
|
106
85
|
})
|
|
107
86
|
const mediaConnNode = (0, WABinary_1.getBinaryNodeChild)(result, 'media_conn')
|
|
108
|
-
|
|
87
|
+
// TODO: explore full length of data that whatsapp provides
|
|
109
88
|
const node = {
|
|
110
89
|
hosts: (0, WABinary_1.getBinaryNodeChildren)(mediaConnNode, 'host').map(({ attrs }) => ({
|
|
111
90
|
hostname: attrs.hostname,
|
|
@@ -121,7 +100,10 @@ const makeMessagesSocket = config => {
|
|
|
121
100
|
}
|
|
122
101
|
return mediaConn
|
|
123
102
|
}
|
|
124
|
-
|
|
103
|
+
/**
|
|
104
|
+
* generic send receipt function
|
|
105
|
+
* used for receipts of phone call, read, delivery etc.
|
|
106
|
+
* */
|
|
125
107
|
const sendReceipt = async (jid, participant, messageIds, type, sts) => {
|
|
126
108
|
if (!messageIds || messageIds.length === 0) {
|
|
127
109
|
throw new boom_1.Boom('missing ids in receipt')
|
|
@@ -141,7 +123,7 @@ const makeMessagesSocket = config => {
|
|
|
141
123
|
node.attrs.recipient = jid
|
|
142
124
|
node.attrs.to = participant
|
|
143
125
|
} else if (isInterop && !type) {
|
|
144
|
-
|
|
126
|
+
// Delivery receipt to interop contact: WA sends to device-qualified JID (user:0@interop)
|
|
145
127
|
const { user } = (0, WABinary_1.jidDecode)(jid)
|
|
146
128
|
node.attrs.to = `${user}:0@interop`
|
|
147
129
|
} else {
|
|
@@ -153,7 +135,7 @@ const makeMessagesSocket = config => {
|
|
|
153
135
|
if (type) {
|
|
154
136
|
node.attrs.type = type
|
|
155
137
|
}
|
|
156
|
-
|
|
138
|
+
// Interop bridge requires sts (microsecond stanza timestamp) from the original message
|
|
157
139
|
if (isInterop && sts) {
|
|
158
140
|
node.attrs.sts = sts
|
|
159
141
|
}
|
|
@@ -173,21 +155,21 @@ const makeMessagesSocket = config => {
|
|
|
173
155
|
logger.debug({ attrs: node.attrs, messageIds }, 'sending receipt for messages')
|
|
174
156
|
await sendNode(node)
|
|
175
157
|
}
|
|
176
|
-
|
|
158
|
+
/** Correctly bulk send receipts to multiple chats, participants */
|
|
177
159
|
const sendReceipts = async (keys, type) => {
|
|
178
160
|
const recps = (0, Utils_1.aggregateMessageKeysNotFromMe)(keys)
|
|
179
161
|
for (const { jid, participant, messageIds, sts } of recps) {
|
|
180
162
|
await sendReceipt(jid, participant, messageIds, type, sts)
|
|
181
163
|
}
|
|
182
164
|
}
|
|
183
|
-
|
|
165
|
+
/** Bulk read messages. Keys can be from different chats & participants */
|
|
184
166
|
const readMessages = async keys => {
|
|
185
167
|
const privacySettings = await fetchPrivacySettings()
|
|
186
|
-
|
|
168
|
+
// based on privacy settings, we have to change the read type
|
|
187
169
|
const readType = privacySettings.readreceipts === 'all' ? 'read' : 'read-self'
|
|
188
170
|
await sendReceipts(keys, readType)
|
|
189
171
|
}
|
|
190
|
-
|
|
172
|
+
/** Fetch all the devices we've to send a message to */
|
|
191
173
|
const getUSyncDevices = async (jids, useCache, ignoreZeroDevices) => {
|
|
192
174
|
const deviceResults = []
|
|
193
175
|
if (!useCache) {
|
|
@@ -245,26 +227,17 @@ const makeMessagesSocket = config => {
|
|
|
245
227
|
}
|
|
246
228
|
}
|
|
247
229
|
const query = new WAUSync_1.USyncQuery().withContext('message').withDeviceProtocol().withLIDProtocol()
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
const pnJidsToFetch = toFetch.filter(jid => (0, WABinary_1.isPnUser)(jid) || (0, WABinary_1.isHostedPnUser)(jid))
|
|
251
|
-
const cachedLidPairs = pnJidsToFetch.length
|
|
252
|
-
? (await signalRepository.lidMapping.getLIDsForPNs(pnJidsToFetch)) || []
|
|
253
|
-
: []
|
|
254
|
-
const cachedLidByPn = new Map(cachedLidPairs.map(({ pn, lid }) => [pn, lid]))
|
|
255
230
|
for (const jid of toFetch) {
|
|
256
|
-
|
|
257
|
-
const cachedLid = cachedLidByPn.get(jid)
|
|
258
|
-
if (cachedLid) syncUser.withLid(cachedLid)
|
|
259
|
-
query.withUser(syncUser)
|
|
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
|
|
260
232
|
}
|
|
261
233
|
const result = await sock.executeUSyncQuery(query)
|
|
262
234
|
if (result) {
|
|
235
|
+
// TODO: LID MAP this stuff (lid protocol will now return lid with devices)
|
|
263
236
|
const lidResults = result.list.filter(a => !!a.lid)
|
|
264
237
|
if (lidResults.length > 0) {
|
|
265
238
|
logger.trace('Storing LID maps from device call')
|
|
266
239
|
await signalRepository.lidMapping.storeLIDPNMappings(lidResults.map(a => ({ lid: a.lid, pn: a.id })))
|
|
267
|
-
|
|
240
|
+
// Force-refresh sessions for newly mapped LIDs to align identity addressing
|
|
268
241
|
try {
|
|
269
242
|
const lids = lidResults.map(a => a.lid)
|
|
270
243
|
if (lids.length) {
|
|
@@ -285,10 +258,10 @@ const makeMessagesSocket = config => {
|
|
|
285
258
|
deviceMap[item.user] = deviceMap[item.user] || []
|
|
286
259
|
deviceMap[item.user]?.push(item)
|
|
287
260
|
}
|
|
288
|
-
|
|
261
|
+
// Process each user's devices as a group for bulk LID migration
|
|
289
262
|
for (const [user, userDevices] of Object.entries(deviceMap)) {
|
|
290
263
|
const isLidUser = requestedLidUsers.has(user)
|
|
291
|
-
|
|
264
|
+
// Process all devices for this user
|
|
292
265
|
for (const item of userDevices) {
|
|
293
266
|
const finalJid = isLidUser
|
|
294
267
|
? (0, WABinary_1.jidEncode)(user, item.server, item.device)
|
|
@@ -309,7 +282,7 @@ const makeMessagesSocket = config => {
|
|
|
309
282
|
}
|
|
310
283
|
}
|
|
311
284
|
if (userDevicesCache.mset) {
|
|
312
|
-
|
|
285
|
+
// if the cache supports mset, we can set all devices in one go
|
|
313
286
|
await userDevicesCache.mset(Object.entries(deviceMap).map(([key, value]) => ({ key, value })))
|
|
314
287
|
} else {
|
|
315
288
|
for (const key in deviceMap) {
|
|
@@ -336,7 +309,9 @@ const makeMessagesSocket = config => {
|
|
|
336
309
|
}
|
|
337
310
|
return deviceResults
|
|
338
311
|
}
|
|
339
|
-
|
|
312
|
+
/**
|
|
313
|
+
* Update Member Label
|
|
314
|
+
*/
|
|
340
315
|
const updateMemberLabel = (jid, memberLabel) => {
|
|
341
316
|
return relayMessage(
|
|
342
317
|
jid,
|
|
@@ -365,16 +340,16 @@ const makeMessagesSocket = config => {
|
|
|
365
340
|
}
|
|
366
341
|
const assertSessions = async (jids, force) => {
|
|
367
342
|
let didFetchNewSession = false
|
|
368
|
-
const uniqueJids = [...new Set(jids)]
|
|
343
|
+
const uniqueJids = [...new Set(jids)] // Deduplicate JIDs
|
|
369
344
|
const jidsRequiringFetch = []
|
|
370
345
|
logger.debug({ jids }, 'assertSessions call with jids')
|
|
371
|
-
|
|
346
|
+
// Check peerSessionsCache and validate sessions using libsignal loadSession
|
|
372
347
|
for (const jid of uniqueJids) {
|
|
373
348
|
const signalId = signalRepository.jidToSignalProtocolAddress(jid)
|
|
374
349
|
const cachedSession = peerSessionsCache.get(signalId)
|
|
375
350
|
if (cachedSession !== undefined) {
|
|
376
351
|
if (cachedSession && !force) {
|
|
377
|
-
continue
|
|
352
|
+
continue // Session exists in cache
|
|
378
353
|
}
|
|
379
354
|
} else {
|
|
380
355
|
const sessionValidation = await signalRepository.validateSession(jid)
|
|
@@ -387,7 +362,7 @@ const makeMessagesSocket = config => {
|
|
|
387
362
|
jidsRequiringFetch.push(jid)
|
|
388
363
|
}
|
|
389
364
|
if (jidsRequiringFetch.length) {
|
|
390
|
-
|
|
365
|
+
// LID if mapped, otherwise original; interop JIDs pass through as-is
|
|
391
366
|
const wireJids = [
|
|
392
367
|
...jidsRequiringFetch.filter(jid => !!(0, WABinary_1.isLidUser)(jid) || !!(0, WABinary_1.isHostedLidUser)(jid)),
|
|
393
368
|
...(
|
|
@@ -430,7 +405,7 @@ const makeMessagesSocket = config => {
|
|
|
430
405
|
}
|
|
431
406
|
await (0, Utils_1.parseAndInjectE2ESessions)(result, signalRepository)
|
|
432
407
|
didFetchNewSession = true
|
|
433
|
-
|
|
408
|
+
// Cache fetched sessions using wire JIDs
|
|
434
409
|
for (const wireJid of wireJids) {
|
|
435
410
|
const signalId = signalRepository.jidToSignalProtocolAddress(wireJid)
|
|
436
411
|
peerSessionsCache.set(signalId, true)
|
|
@@ -439,7 +414,7 @@ const makeMessagesSocket = config => {
|
|
|
439
414
|
return didFetchNewSession
|
|
440
415
|
}
|
|
441
416
|
const sendPeerDataOperationMessage = async pdoMessage => {
|
|
442
|
-
|
|
417
|
+
//TODO: for later, abstract the logic to send a Peer Message instead of just PDO - useful for App State Key Resync with phone
|
|
443
418
|
if (!authState.creds.me?.id) {
|
|
444
419
|
throw new boom_1.Boom('Not authenticated')
|
|
445
420
|
}
|
|
@@ -532,9 +507,7 @@ const makeMessagesSocket = config => {
|
|
|
532
507
|
additionalNodes,
|
|
533
508
|
useUserDevicesCache,
|
|
534
509
|
useCachedGroupMetadata,
|
|
535
|
-
statusJidList
|
|
536
|
-
statusPrivacy,
|
|
537
|
-
AI = false
|
|
510
|
+
statusJidList
|
|
538
511
|
}
|
|
539
512
|
) => {
|
|
540
513
|
const meId = authState.creds.me.id
|
|
@@ -550,7 +523,8 @@ const makeMessagesSocket = config => {
|
|
|
550
523
|
const isInterop = (0, WABinary_1.isInteropUser)(jid)
|
|
551
524
|
const isGroupOrStatus = isGroup || isStatus
|
|
552
525
|
const finalJid = jid
|
|
553
|
-
|
|
526
|
+
const iosBros = config.browser[0] === "iOS" || config.browser[1] === "Safari";
|
|
527
|
+
msgId = iosBros ? Utils_1.generateIOSMessageID() : msgId ?? (0, Utils_1.generateMessageIDV2)(meId)
|
|
554
528
|
useUserDevicesCache = useUserDevicesCache !== false
|
|
555
529
|
useCachedGroupMetadata = useCachedGroupMetadata !== false && !isStatus
|
|
556
530
|
const participants = []
|
|
@@ -573,18 +547,6 @@ const makeMessagesSocket = config => {
|
|
|
573
547
|
|
|
574
548
|
const buttonType = getButtonType(messages)
|
|
575
549
|
const pollMessage = messages.pollCreationMessage || messages.pollCreationMessageV2 || messages.pollCreationMessageV3
|
|
576
|
-
|
|
577
|
-
if (participant) {
|
|
578
|
-
if (!isGroup && !isStatus) {
|
|
579
|
-
additionalAttributes = { ...additionalAttributes, device_fanout: 'false' }
|
|
580
|
-
}
|
|
581
|
-
const { user, device } = (0, WABinary_1.jidDecode)(participant.jid)
|
|
582
|
-
devices.push({
|
|
583
|
-
user,
|
|
584
|
-
device,
|
|
585
|
-
jid: participant.jid
|
|
586
|
-
})
|
|
587
|
-
}
|
|
588
550
|
await authState.keys.transaction(async () => {
|
|
589
551
|
const mediaType = getMediaType(message)
|
|
590
552
|
if (mediaType) {
|
|
@@ -616,24 +578,35 @@ const makeMessagesSocket = config => {
|
|
|
616
578
|
(0, Utils_1.normalizeMessageContent)(message)?.pinInChatMessage ||
|
|
617
579
|
(0, Utils_1.normalizeMessageContent)(message)?.reactionMessage
|
|
618
580
|
) {
|
|
619
|
-
extraAttrs['decrypt-fail'] = 'hide'
|
|
581
|
+
extraAttrs['decrypt-fail'] = 'hide' // todo: expand for reactions and other types
|
|
620
582
|
}
|
|
621
583
|
if (isGroupOrStatus && !isRetryResend) {
|
|
622
584
|
const [groupData, senderKeyMap] = await Promise.all([
|
|
623
585
|
(async () => {
|
|
624
|
-
let groupData = useCachedGroupMetadata && cachedGroupMetadata ? await cachedGroupMetadata(jid) : undefined
|
|
625
|
-
|
|
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) {
|
|
626
593
|
logger.trace({ jid, participants: groupData.participants.length }, 'using cached group metadata')
|
|
627
594
|
} else if (!isStatus) {
|
|
628
|
-
|
|
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
|
|
629
602
|
}
|
|
630
603
|
return groupData
|
|
631
604
|
})(),
|
|
632
605
|
(async () => {
|
|
633
606
|
if (!participant && !isStatus) {
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
const result = await authState.keys.get('sender-key-memory', [jid])
|
|
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?
|
|
637
610
|
return result[jid] || {}
|
|
638
611
|
}
|
|
639
612
|
return {}
|
|
@@ -817,8 +790,8 @@ const makeMessagesSocket = config => {
|
|
|
817
790
|
!(0, WABinary_1.isHostedPnUser)(deviceJid) &&
|
|
818
791
|
device.device !== 99
|
|
819
792
|
) {
|
|
820
|
-
|
|
821
|
-
|
|
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
|
|
822
795
|
senderKeyRecipients.push(deviceJid)
|
|
823
796
|
senderKeyMap[deviceJid] = true
|
|
824
797
|
}
|
|
@@ -844,8 +817,8 @@ const makeMessagesSocket = config => {
|
|
|
844
817
|
})
|
|
845
818
|
await authState.keys.set({ 'sender-key-memory': { [jid]: senderKeyMap } })
|
|
846
819
|
} else {
|
|
847
|
-
|
|
848
|
-
|
|
820
|
+
// ADDRESSING CONSISTENCY: Match own identity to conversation context
|
|
821
|
+
// TODO: investigate if this is true
|
|
849
822
|
let ownId = meId
|
|
850
823
|
if (isLid && meLid) {
|
|
851
824
|
ownId = meLid
|
|
@@ -865,9 +838,9 @@ const makeMessagesSocket = config => {
|
|
|
865
838
|
devices.push({
|
|
866
839
|
user,
|
|
867
840
|
device: 0,
|
|
868
|
-
jid: (0, WABinary_1.jidEncode)(user, targetUserServer, 0)
|
|
841
|
+
jid: (0, WABinary_1.jidEncode)(user, targetUserServer, 0) // rajeh, todo: this entire logic is convoluted and weird.
|
|
869
842
|
})
|
|
870
|
-
|
|
843
|
+
// Interop contacts don't receive a copy to own devices — native WA sends only one flat <enc>
|
|
871
844
|
if (user !== ownUser && !isInterop) {
|
|
872
845
|
const ownUserServer = isLid ? 'lid' : 's.whatsapp.net'
|
|
873
846
|
const ownUserForAddressing =
|
|
@@ -879,14 +852,14 @@ const makeMessagesSocket = config => {
|
|
|
879
852
|
})
|
|
880
853
|
}
|
|
881
854
|
if (additionalAttributes?.['category'] !== 'peer' && !isInterop) {
|
|
882
|
-
|
|
855
|
+
// Clear placeholders and enumerate actual devices
|
|
883
856
|
devices.length = 0
|
|
884
|
-
|
|
857
|
+
// Use conversation-appropriate sender identity
|
|
885
858
|
const senderIdentity =
|
|
886
859
|
isLid && meLid
|
|
887
860
|
? (0, WABinary_1.jidEncode)((0, WABinary_1.jidDecode)(meLid)?.user, 'lid', undefined)
|
|
888
861
|
: (0, WABinary_1.jidEncode)((0, WABinary_1.jidDecode)(meId)?.user, 's.whatsapp.net', undefined)
|
|
889
|
-
|
|
862
|
+
// Enumerate devices for sender and target with consistent addressing
|
|
890
863
|
const sessionDevices = await getUSyncDevices([senderIdentity, jid], true, false)
|
|
891
864
|
devices.push(...sessionDevices)
|
|
892
865
|
logger.debug(
|
|
@@ -904,26 +877,25 @@ const makeMessagesSocket = config => {
|
|
|
904
877
|
const { user: mePnUser } = (0, WABinary_1.jidDecode)(meId)
|
|
905
878
|
const { user: meLidUser } = meLid ? (0, WABinary_1.jidDecode)(meLid) : { user: null }
|
|
906
879
|
for (const { user, jid } of devices) {
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
}
|
|
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;
|
|
886
|
+
}
|
|
887
|
+
if (!ptcp) {
|
|
888
|
+
if (isMe) meRecipients.push(jid);
|
|
889
|
+
else otherRecipients.push(jid);
|
|
890
|
+
allRecipients.push(jid);
|
|
891
|
+
}
|
|
892
|
+
}
|
|
921
893
|
await assertSessions(allRecipients)
|
|
922
894
|
const [
|
|
923
895
|
{ nodes: meNodes, shouldIncludeDeviceIdentity: s1 },
|
|
924
896
|
{ nodes: otherNodes, shouldIncludeDeviceIdentity: s2 }
|
|
925
897
|
] = await Promise.all([
|
|
926
|
-
|
|
898
|
+
// For own devices: use DSM if available (1:1 chats only)
|
|
927
899
|
createParticipantNodes(meRecipients, meMsg || message, extraAttrs),
|
|
928
900
|
createParticipantNodes(otherRecipients, message, extraAttrs, meMsg)
|
|
929
901
|
])
|
|
@@ -934,40 +906,15 @@ const makeMessagesSocket = config => {
|
|
|
934
906
|
}
|
|
935
907
|
shouldIncludeDeviceIdentity = shouldIncludeDeviceIdentity || s1 || s2
|
|
936
908
|
}
|
|
937
|
-
if (isRetryResend) {
|
|
938
|
-
const isParticipantLid = (0, WABinary_1.isLidUser)(participant.jid)
|
|
939
|
-
const isMe = (0, WABinary_1.areJidsSameUser)(participant.jid, isParticipantLid ? meLid : meId)
|
|
940
|
-
const encodedMessageToSend = isMe
|
|
941
|
-
? (0, Utils_1.encodeWAMessage)({
|
|
942
|
-
deviceSentMessage: {
|
|
943
|
-
destinationJid,
|
|
944
|
-
message
|
|
945
|
-
}
|
|
946
|
-
})
|
|
947
|
-
: (0, Utils_1.encodeWAMessage)(message)
|
|
948
|
-
const { type, ciphertext: encryptedContent } = await signalRepository.encryptMessage({
|
|
949
|
-
data: encodedMessageToSend,
|
|
950
|
-
jid: participant.jid
|
|
951
|
-
})
|
|
952
|
-
binaryNodeContent.push({
|
|
953
|
-
tag: 'enc',
|
|
954
|
-
attrs: {
|
|
955
|
-
v: '2',
|
|
956
|
-
type,
|
|
957
|
-
count: (participant.count ?? 0).toString()
|
|
958
|
-
},
|
|
959
|
-
content: encryptedContent
|
|
960
|
-
})
|
|
961
|
-
}
|
|
962
909
|
if (participants.length) {
|
|
963
910
|
if (additionalAttributes?.['category'] === 'peer') {
|
|
964
911
|
const peerNode = participants[0]?.content?.[0]
|
|
965
912
|
if (peerNode) {
|
|
966
|
-
binaryNodeContent.push(peerNode)
|
|
913
|
+
binaryNodeContent.push(peerNode) // push only enc
|
|
967
914
|
}
|
|
968
915
|
} else if (isInterop) {
|
|
969
|
-
|
|
970
|
-
|
|
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.
|
|
971
918
|
const recipientNode = participants.find(p => (0, WABinary_1.isInteropUser)(p?.attrs?.jid))
|
|
972
919
|
const encNode = (recipientNode ?? participants[0])?.content?.[0]
|
|
973
920
|
if (encNode) {
|
|
@@ -991,22 +938,6 @@ const makeMessagesSocket = config => {
|
|
|
991
938
|
},
|
|
992
939
|
content: binaryNodeContent
|
|
993
940
|
}
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
if (participant) {
|
|
998
|
-
if ((0, WABinary_1.isJidGroup)(destinationJid)) {
|
|
999
|
-
stanza.attrs.to = destinationJid
|
|
1000
|
-
stanza.attrs.participant = participant.jid
|
|
1001
|
-
} else if ((0, WABinary_1.areJidsSameUser)(participant.jid, meId)) {
|
|
1002
|
-
stanza.attrs.to = participant.jid
|
|
1003
|
-
stanza.attrs.recipient = destinationJid
|
|
1004
|
-
} else {
|
|
1005
|
-
stanza.attrs.to = participant.jid
|
|
1006
|
-
}
|
|
1007
|
-
} else {
|
|
1008
|
-
stanza.attrs.to = destinationJid
|
|
1009
|
-
}
|
|
1010
941
|
if (shouldIncludeDeviceIdentity) {
|
|
1011
942
|
stanza.content.push({
|
|
1012
943
|
tag: 'device-identity',
|
|
@@ -1023,14 +954,6 @@ const makeMessagesSocket = config => {
|
|
|
1023
954
|
})
|
|
1024
955
|
}
|
|
1025
956
|
|
|
1026
|
-
|
|
1027
|
-
if (isStatus && statusPrivacy && !additionalAttributes?.edit) {
|
|
1028
|
-
stanza.content.push({
|
|
1029
|
-
tag: 'meta',
|
|
1030
|
-
attrs: { status_setting: statusPrivacy, session_scope: 'status' }
|
|
1031
|
-
})
|
|
1032
|
-
}
|
|
1033
|
-
|
|
1034
957
|
if (pollMessage || messages.eventMessage) {
|
|
1035
958
|
stanza.content.push({
|
|
1036
959
|
tag: 'meta',
|
|
@@ -1088,30 +1011,30 @@ const makeMessagesSocket = config => {
|
|
|
1088
1011
|
stanza.content.push(buttonsNode)
|
|
1089
1012
|
}
|
|
1090
1013
|
}
|
|
1091
|
-
if (!
|
|
1014
|
+
if (!config.aiLabel && (0, WABinary_1.isPnUser)(destinationJid)) {
|
|
1092
1015
|
const alreadyHasBizBot =
|
|
1093
1016
|
WABinary_1.getBinaryFilteredBizBot(additionalNodes || []) ||
|
|
1094
1017
|
WABinary_1.getBinaryFilteredBizBot(stanza.content)
|
|
1095
1018
|
if (!alreadyHasBizBot) {
|
|
1096
1019
|
stanza.content.push({ tag: 'bot', attrs: { biz_bot: '1' } })
|
|
1097
1020
|
}
|
|
1098
|
-
} else if (
|
|
1021
|
+
} else if (config.aiLabel && !isGroup && !isStatus && !isNewsletter) {
|
|
1099
1022
|
const existingBizBot = WABinary_1.getBinaryFilteredBizBot(additionalNodes || [])
|
|
1100
1023
|
if (!existingBizBot) {
|
|
1101
1024
|
stanza.content.push({ tag: 'bot', attrs: { biz_bot: '1' } })
|
|
1102
1025
|
}
|
|
1103
1026
|
}
|
|
1104
|
-
|
|
1027
|
+
// WA Web never attaches tctoken to peer (AppStateSync) messages — server rejects with 479
|
|
1105
1028
|
const isPeerMessage = additionalAttributes?.['category'] === 'peer'
|
|
1106
1029
|
const is1on1Send = !isGroup && !isRetryResend && !isStatus && !isNewsletter && !isPeerMessage
|
|
1107
|
-
|
|
1030
|
+
// Resolve destination to LID for tctoken storage — matches Signal session key pattern
|
|
1108
1031
|
const tcTokenJid = is1on1Send
|
|
1109
1032
|
? await (0, tc_token_utils_1.resolveTcTokenJid)(destinationJid, getLIDForPN)
|
|
1110
1033
|
: destinationJid
|
|
1111
1034
|
const contactTcTokenData = is1on1Send ? await authState.keys.get('tctoken', [tcTokenJid]) : {}
|
|
1112
1035
|
const existingTokenEntry = contactTcTokenData[tcTokenJid]
|
|
1113
1036
|
let tcTokenBuffer = existingTokenEntry?.token
|
|
1114
|
-
|
|
1037
|
+
// Treat expired tokens the same as missing — clear from cache
|
|
1115
1038
|
if (tcTokenBuffer?.length && (0, tc_token_utils_1.isTcTokenExpired)(existingTokenEntry?.timestamp)) {
|
|
1116
1039
|
logger.debug({ jid: destinationJid, timestamp: existingTokenEntry?.timestamp }, 'tctoken expired, clearing')
|
|
1117
1040
|
tcTokenBuffer = undefined
|
|
@@ -1132,38 +1055,17 @@ const makeMessagesSocket = config => {
|
|
|
1132
1055
|
content: tcTokenBuffer
|
|
1133
1056
|
})
|
|
1134
1057
|
}
|
|
1135
|
-
|
|
1136
|
-
if (is1on1Send && !tcTokenBuffer?.length) {
|
|
1137
|
-
try {
|
|
1138
|
-
const saltStore = await authState.keys.get('nct-salt', ['default'])
|
|
1139
|
-
const nctSalt = saltStore?.['default']
|
|
1140
|
-
if (nctSalt?.length) {
|
|
1141
|
-
const recipientLid = await getLIDForPN(destinationJid)
|
|
1142
|
-
if (recipientLid) {
|
|
1143
|
-
const { createHmac } = require('crypto')
|
|
1144
|
-
const lidStr = (0, WABinary_1.isLidUser)(recipientLid)
|
|
1145
|
-
? recipientLid
|
|
1146
|
-
: `${recipientLid.split('@')[0]}@lid`
|
|
1147
|
-
const cs = createHmac('sha256', nctSalt).update(lidStr, 'utf8').digest()
|
|
1148
|
-
stanza.content.push({ tag: 'cstoken', attrs: {}, content: cs })
|
|
1149
|
-
logger.debug({ jid: destinationJid, lid: lidStr }, 'nct: attached cstoken')
|
|
1150
|
-
}
|
|
1151
|
-
}
|
|
1152
|
-
} catch (err) {
|
|
1153
|
-
logger.debug({ jid: destinationJid, err: err?.message }, 'nct: cstoken attach failed')
|
|
1154
|
-
}
|
|
1155
|
-
}
|
|
1156
1058
|
if (additionalNodes && additionalNodes.length > 0 && !didPushAdditional) {
|
|
1157
1059
|
stanza.content.push(...additionalNodes)
|
|
1158
1060
|
}
|
|
1159
1061
|
logger.debug({ msgId }, `sending message to ${participants.length} devices`)
|
|
1160
1062
|
await sendNode(stanza)
|
|
1161
|
-
|
|
1162
|
-
|
|
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.
|
|
1163
1065
|
if (message.messageContextInfo?.messageSecret) {
|
|
1164
1066
|
setBotMessageSecret(msgId, message.messageContextInfo.messageSecret, destinationJid)
|
|
1165
1067
|
}
|
|
1166
|
-
|
|
1068
|
+
// Fire-and-forget: issue our token to the contact AFTER message send.
|
|
1167
1069
|
const isProtocolMsg = !!(0, Utils_1.normalizeMessageContent)(message)?.protocolMessage
|
|
1168
1070
|
const isBotOrPSA =
|
|
1169
1071
|
destinationJid === WABinary_1.PSA_WID ||
|
|
@@ -1214,7 +1116,7 @@ const makeMessagesSocket = config => {
|
|
|
1214
1116
|
inFlightTcTokenIssuance.delete(tcTokenJid)
|
|
1215
1117
|
})
|
|
1216
1118
|
}
|
|
1217
|
-
|
|
1119
|
+
// Add message to retry cache if enabled
|
|
1218
1120
|
if (messageRetryManager && !participant) {
|
|
1219
1121
|
messageRetryManager.addRecentMessage(destinationJid, msgId, message)
|
|
1220
1122
|
}
|
|
@@ -1336,7 +1238,7 @@ const makeMessagesSocket = config => {
|
|
|
1336
1238
|
}
|
|
1337
1239
|
}
|
|
1338
1240
|
} else if (nativeFlow && nativeFlowSpecials.includes(firstButtonName)) {
|
|
1339
|
-
|
|
1241
|
+
// Only works for WhatsApp Original, not WhatsApp Business
|
|
1340
1242
|
return {
|
|
1341
1243
|
tag: 'biz',
|
|
1342
1244
|
attrs: {
|
|
@@ -1370,7 +1272,7 @@ const makeMessagesSocket = config => {
|
|
|
1370
1272
|
]
|
|
1371
1273
|
}
|
|
1372
1274
|
} else if (nativeFlow || message.buttonsMessage) {
|
|
1373
|
-
|
|
1275
|
+
// It works for whatsapp original and whatsapp business
|
|
1374
1276
|
return {
|
|
1375
1277
|
tag: 'biz',
|
|
1376
1278
|
attrs: {
|
|
@@ -1466,7 +1368,7 @@ const makeMessagesSocket = config => {
|
|
|
1466
1368
|
}
|
|
1467
1369
|
const getPrivacyTokens = issuePrivacyTokens
|
|
1468
1370
|
const waUploadToServer = (0, Utils_1.getWAUploadToServer)(config, refreshMediaConn)
|
|
1469
|
-
const
|
|
1371
|
+
const interactiveHandler = new interactive_handler_1.InteractiveMessageHandler(waUploadToServer, relayMessage, config, sock)
|
|
1470
1372
|
const waitForMsgMediaUpdate = (0, Utils_1.bindWaitForEvent)(ev, 'messages.media-update')
|
|
1471
1373
|
return {
|
|
1472
1374
|
...sock,
|
|
@@ -1619,7 +1521,6 @@ const makeMessagesSocket = config => {
|
|
|
1619
1521
|
mediaHandle = up.handle
|
|
1620
1522
|
return up
|
|
1621
1523
|
},
|
|
1622
|
-
mediaAbProps: authState.creds.mediaAbProps,
|
|
1623
1524
|
...options
|
|
1624
1525
|
}
|
|
1625
1526
|
)
|
|
@@ -1641,7 +1542,6 @@ const makeMessagesSocket = config => {
|
|
|
1641
1542
|
mediaHandle = up.handle
|
|
1642
1543
|
return up
|
|
1643
1544
|
},
|
|
1644
|
-
mediaAbProps: authState.creds.mediaAbProps,
|
|
1645
1545
|
...options
|
|
1646
1546
|
}
|
|
1647
1547
|
)
|
|
@@ -1661,7 +1561,7 @@ const makeMessagesSocket = config => {
|
|
|
1661
1561
|
},
|
|
1662
1562
|
|
|
1663
1563
|
sendStatusMention: async (content, jids = []) => {
|
|
1664
|
-
return await
|
|
1564
|
+
return await interactiveHandler.sendStatusWhatsApp(content, jids)
|
|
1665
1565
|
},
|
|
1666
1566
|
sendTable: async (jid, title, headers, rows, quoted, options = {}) => {
|
|
1667
1567
|
const { message, messageId } = message_composer_1.generateTableContent(title, headers, rows, quoted, options)
|
|
@@ -1716,7 +1616,9 @@ const makeMessagesSocket = config => {
|
|
|
1716
1616
|
},
|
|
1717
1617
|
sendMessage: async (jid, content, options = {}) => {
|
|
1718
1618
|
const userJid = authState.creds.me.id
|
|
1719
|
-
|
|
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 ──────
|
|
1720
1622
|
if (
|
|
1721
1623
|
typeof content === 'object' &&
|
|
1722
1624
|
Array.isArray(content.buttons) &&
|
|
@@ -1741,181 +1643,6 @@ const makeMessagesSocket = config => {
|
|
|
1741
1643
|
const { buttons, headerType, viewOnce, ...rest } = content
|
|
1742
1644
|
content = { ...rest, interactiveButtons }
|
|
1743
1645
|
}
|
|
1744
|
-
if (
|
|
1745
|
-
typeof content === 'object' &&
|
|
1746
|
-
Array.isArray(content.interactiveButtons) &&
|
|
1747
|
-
content.interactiveButtons.length > 0
|
|
1748
|
-
) {
|
|
1749
|
-
const {
|
|
1750
|
-
text = '',
|
|
1751
|
-
caption = '',
|
|
1752
|
-
title = '',
|
|
1753
|
-
footer = '',
|
|
1754
|
-
interactiveButtons,
|
|
1755
|
-
hasMediaAttachment = false,
|
|
1756
|
-
image = null,
|
|
1757
|
-
video = null,
|
|
1758
|
-
document = null,
|
|
1759
|
-
mimetype = null,
|
|
1760
|
-
jpegThumbnail = null,
|
|
1761
|
-
location = null,
|
|
1762
|
-
product = null,
|
|
1763
|
-
businessOwnerJid = null,
|
|
1764
|
-
externalAdReply = null
|
|
1765
|
-
} = content
|
|
1766
|
-
|
|
1767
|
-
const processedButtons = []
|
|
1768
|
-
for (let i = 0; i < interactiveButtons.length; i++) {
|
|
1769
|
-
const btn = interactiveButtons[i]
|
|
1770
|
-
if (!btn || typeof btn !== 'object') throw new Error(`interactiveButtons[${i}] must be an object`)
|
|
1771
|
-
if (btn.name && btn.buttonParamsJson) {
|
|
1772
|
-
processedButtons.push(btn)
|
|
1773
|
-
continue
|
|
1774
|
-
}
|
|
1775
|
-
if (btn.id || btn.text || btn.displayText) {
|
|
1776
|
-
processedButtons.push({
|
|
1777
|
-
name: 'quick_reply',
|
|
1778
|
-
buttonParamsJson: JSON.stringify({
|
|
1779
|
-
display_text: btn.text || btn.displayText || `Button ${i + 1}`,
|
|
1780
|
-
id: btn.id || `quick_${i + 1}`
|
|
1781
|
-
})
|
|
1782
|
-
})
|
|
1783
|
-
continue
|
|
1784
|
-
}
|
|
1785
|
-
if (btn.buttonId && btn.buttonText?.displayText) {
|
|
1786
|
-
processedButtons.push({
|
|
1787
|
-
name: 'quick_reply',
|
|
1788
|
-
buttonParamsJson: JSON.stringify({ display_text: btn.buttonText.displayText, id: btn.buttonId })
|
|
1789
|
-
})
|
|
1790
|
-
continue
|
|
1791
|
-
}
|
|
1792
|
-
throw new Error(`interactiveButtons[${i}] has invalid shape`)
|
|
1793
|
-
}
|
|
1794
|
-
let messageContent = {}
|
|
1795
|
-
if (image) {
|
|
1796
|
-
const mi = Buffer.isBuffer(image)
|
|
1797
|
-
? { image }
|
|
1798
|
-
: { image: { url: typeof image === 'object' ? image.url : image } }
|
|
1799
|
-
const pm = await (0, Utils_1.prepareWAMessageMedia)(mi, { upload: waUploadToServer })
|
|
1800
|
-
messageContent.header = { title: title || '', hasMediaAttachment: true, imageMessage: pm.imageMessage }
|
|
1801
|
-
} else if (video) {
|
|
1802
|
-
const mi = Buffer.isBuffer(video)
|
|
1803
|
-
? { video }
|
|
1804
|
-
: { video: { url: typeof video === 'object' ? video.url : video } }
|
|
1805
|
-
const pm = await (0, Utils_1.prepareWAMessageMedia)(mi, { upload: waUploadToServer })
|
|
1806
|
-
messageContent.header = { title: title || '', hasMediaAttachment: true, videoMessage: pm.videoMessage }
|
|
1807
|
-
} else if (document) {
|
|
1808
|
-
const mi = Buffer.isBuffer(document)
|
|
1809
|
-
? { document }
|
|
1810
|
-
: { document: { url: typeof document === 'object' ? document.url : document } }
|
|
1811
|
-
if (mimetype && typeof mi.document === 'object') mi.document.mimetype = mimetype
|
|
1812
|
-
if (jpegThumbnail) {
|
|
1813
|
-
const thumb = Buffer.isBuffer(jpegThumbnail)
|
|
1814
|
-
? jpegThumbnail
|
|
1815
|
-
: await (async () => {
|
|
1816
|
-
try {
|
|
1817
|
-
const r = await fetch(jpegThumbnail)
|
|
1818
|
-
return Buffer.from(await r.arrayBuffer())
|
|
1819
|
-
} catch {
|
|
1820
|
-
return undefined
|
|
1821
|
-
}
|
|
1822
|
-
})()
|
|
1823
|
-
if (thumb) mi.document.jpegThumbnail = thumb
|
|
1824
|
-
}
|
|
1825
|
-
const pm = await (0, Utils_1.prepareWAMessageMedia)(mi, { upload: waUploadToServer })
|
|
1826
|
-
messageContent.header = { title: title || '', hasMediaAttachment: true, documentMessage: pm.documentMessage }
|
|
1827
|
-
} else if (location && typeof location === 'object') {
|
|
1828
|
-
messageContent.header = {
|
|
1829
|
-
title: title || location.name || 'Location',
|
|
1830
|
-
hasMediaAttachment: false,
|
|
1831
|
-
locationMessage: {
|
|
1832
|
-
degreesLatitude: location.degreesLatitude || location.degressLatitude || 0,
|
|
1833
|
-
degreesLongitude: location.degreesLongitude || location.degressLongitude || 0,
|
|
1834
|
-
name: location.name || '',
|
|
1835
|
-
address: location.address || ''
|
|
1836
|
-
}
|
|
1837
|
-
}
|
|
1838
|
-
} else if (product && typeof product === 'object') {
|
|
1839
|
-
let productImageMessage = null
|
|
1840
|
-
if (product.productImage) {
|
|
1841
|
-
const mi = Buffer.isBuffer(product.productImage)
|
|
1842
|
-
? { image: product.productImage }
|
|
1843
|
-
: {
|
|
1844
|
-
image: {
|
|
1845
|
-
url: typeof product.productImage === 'object' ? product.productImage.url : product.productImage
|
|
1846
|
-
}
|
|
1847
|
-
}
|
|
1848
|
-
const pm = await (0, Utils_1.prepareWAMessageMedia)(mi, { upload: waUploadToServer })
|
|
1849
|
-
productImageMessage = pm.imageMessage
|
|
1850
|
-
}
|
|
1851
|
-
messageContent.header = {
|
|
1852
|
-
title: title || product.title || 'Product',
|
|
1853
|
-
hasMediaAttachment: false,
|
|
1854
|
-
productMessage: {
|
|
1855
|
-
product: {
|
|
1856
|
-
productImage: productImageMessage,
|
|
1857
|
-
productId: product.productId || '',
|
|
1858
|
-
title: product.title || '',
|
|
1859
|
-
description: product.description || '',
|
|
1860
|
-
currencyCode: product.currencyCode || 'USD',
|
|
1861
|
-
priceAmount1000: parseInt(product.priceAmount1000, 10) || 0,
|
|
1862
|
-
retailerId: product.retailerId || '',
|
|
1863
|
-
url: product.url || '',
|
|
1864
|
-
productImageCount: product.productImageCount || 1
|
|
1865
|
-
},
|
|
1866
|
-
businessOwnerJid: businessOwnerJid || product.businessOwnerJid || userJid
|
|
1867
|
-
}
|
|
1868
|
-
}
|
|
1869
|
-
} else if (title) {
|
|
1870
|
-
messageContent.header = { title, hasMediaAttachment: false }
|
|
1871
|
-
}
|
|
1872
|
-
const hasMedia = !!(image || video || document || location || product)
|
|
1873
|
-
const bodyText = hasMedia ? caption : text || caption
|
|
1874
|
-
if (bodyText) messageContent.body = { text: bodyText }
|
|
1875
|
-
if (footer) messageContent.footer = { text: footer }
|
|
1876
|
-
messageContent.nativeFlowMessage = { buttons: processedButtons }
|
|
1877
|
-
if (externalAdReply && typeof externalAdReply === 'object') {
|
|
1878
|
-
messageContent.contextInfo = {
|
|
1879
|
-
externalAdReply: {
|
|
1880
|
-
title: externalAdReply.title || '',
|
|
1881
|
-
body: externalAdReply.body || '',
|
|
1882
|
-
mediaType: externalAdReply.mediaType || 1,
|
|
1883
|
-
sourceUrl: externalAdReply.sourceUrl || externalAdReply.url || '',
|
|
1884
|
-
thumbnailUrl: externalAdReply.thumbnailUrl || externalAdReply.thumbnail || '',
|
|
1885
|
-
renderLargerThumbnail: externalAdReply.renderLargerThumbnail || false,
|
|
1886
|
-
showAdAttribution: externalAdReply.showAdAttribution !== false,
|
|
1887
|
-
containsAutoReply: externalAdReply.containsAutoReply || false,
|
|
1888
|
-
...(externalAdReply.mediaUrl && { mediaUrl: externalAdReply.mediaUrl }),
|
|
1889
|
-
...(Buffer.isBuffer(externalAdReply.thumbnail) && { thumbnail: externalAdReply.thumbnail }),
|
|
1890
|
-
...(externalAdReply.jpegThumbnail && { jpegThumbnail: externalAdReply.jpegThumbnail })
|
|
1891
|
-
},
|
|
1892
|
-
...(options.mentionedJid && { mentionedJid: options.mentionedJid })
|
|
1893
|
-
}
|
|
1894
|
-
} else if (options.mentionedJid) {
|
|
1895
|
-
messageContent.contextInfo = { mentionedJid: options.mentionedJid }
|
|
1896
|
-
}
|
|
1897
|
-
const payload = index_js_1.proto.Message.InteractiveMessage.create(messageContent)
|
|
1898
|
-
const msg = (0, Utils_1.generateWAMessageFromContent)(
|
|
1899
|
-
jid,
|
|
1900
|
-
{ viewOnceMessage: { message: { interactiveMessage: payload } } },
|
|
1901
|
-
{ userJid, quoted: options?.quoted || null }
|
|
1902
|
-
)
|
|
1903
|
-
const additionalNodes = [
|
|
1904
|
-
{
|
|
1905
|
-
tag: 'biz',
|
|
1906
|
-
attrs: {},
|
|
1907
|
-
content: [
|
|
1908
|
-
{
|
|
1909
|
-
tag: 'interactive',
|
|
1910
|
-
attrs: { type: 'native_flow', v: '1' },
|
|
1911
|
-
content: [{ tag: 'native_flow', attrs: { v: '9', name: 'mixed' } }]
|
|
1912
|
-
}
|
|
1913
|
-
]
|
|
1914
|
-
}
|
|
1915
|
-
]
|
|
1916
|
-
await relayMessage(jid, msg.message, { messageId: msg.key.id, additionalNodes })
|
|
1917
|
-
return msg
|
|
1918
|
-
}
|
|
1919
1646
|
if (
|
|
1920
1647
|
typeof content === 'object' &&
|
|
1921
1648
|
'disappearingMessagesInChat' in content &&
|
|
@@ -1930,25 +1657,25 @@ const makeMessagesSocket = config => {
|
|
|
1930
1657
|
: 0
|
|
1931
1658
|
: disappearingMessagesInChat
|
|
1932
1659
|
await groupToggleEphemeral(jid, value)
|
|
1933
|
-
} else if (
|
|
1660
|
+
} else if (interactiveHandler.detectType(content)) {
|
|
1934
1661
|
const { quoted } = options
|
|
1935
|
-
const messageType =
|
|
1662
|
+
const messageType = interactiveHandler.detectType(content)
|
|
1936
1663
|
switch (messageType) {
|
|
1937
1664
|
case 'PAYMENT': {
|
|
1938
|
-
const paymentContent = await
|
|
1665
|
+
const paymentContent = await interactiveHandler.handlePayment(content, quoted)
|
|
1939
1666
|
return await relayMessage(jid, paymentContent, {
|
|
1940
|
-
messageId:
|
|
1667
|
+
messageId: mId
|
|
1941
1668
|
})
|
|
1942
1669
|
}
|
|
1943
1670
|
case 'PRODUCT': {
|
|
1944
|
-
const productContent = await
|
|
1671
|
+
const productContent = await interactiveHandler.handleProduct(content, jid, quoted)
|
|
1945
1672
|
const productMsg = await (0, Utils_1.generateWAMessageFromContent)(jid, productContent, { quoted, userJid })
|
|
1946
1673
|
return await relayMessage(jid, productMsg.message, {
|
|
1947
1674
|
messageId: productMsg.key.id
|
|
1948
1675
|
})
|
|
1949
1676
|
}
|
|
1950
1677
|
case 'INTERACTIVE': {
|
|
1951
|
-
const interactiveContent = await
|
|
1678
|
+
const interactiveContent = await interactiveHandler.handleInteractive(content, jid, quoted)
|
|
1952
1679
|
const interactiveMsg = await (0, Utils_1.generateWAMessageFromContent)(jid, interactiveContent, {
|
|
1953
1680
|
quoted,
|
|
1954
1681
|
userJid
|
|
@@ -1957,25 +1684,21 @@ const makeMessagesSocket = config => {
|
|
|
1957
1684
|
messageId: interactiveMsg.key.id
|
|
1958
1685
|
})
|
|
1959
1686
|
}
|
|
1960
|
-
case 'INTERACTIVE_BUTTONS': {
|
|
1961
|
-
const ibContent = await badzzne2.handleInteractiveButtons(content, jid, quoted)
|
|
1962
|
-
const ibMsg = await (0, Utils_1.generateWAMessageFromContent)(jid, ibContent, { quoted, userJid })
|
|
1963
|
-
return await relayMessage(jid, ibMsg.message, {
|
|
1964
|
-
messageId: ibMsg.key.id
|
|
1965
|
-
})
|
|
1966
|
-
}
|
|
1967
1687
|
case 'ALBUM':
|
|
1968
|
-
return await
|
|
1688
|
+
return await interactiveHandler.handleAlbum(content, jid, quoted)
|
|
1969
1689
|
case 'EVENT':
|
|
1970
|
-
return await
|
|
1690
|
+
return await interactiveHandler.handleEvent(content, jid, quoted)
|
|
1971
1691
|
case 'POLL_RESULT':
|
|
1972
|
-
return await
|
|
1692
|
+
return await interactiveHandler.handlePollResult(content, jid, quoted)
|
|
1973
1693
|
case 'GROUP_STORY':
|
|
1974
|
-
return await
|
|
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)
|
|
1975
1699
|
}
|
|
1976
1700
|
} else {
|
|
1977
1701
|
let mediaHandle
|
|
1978
|
-
const { ai, ...optionsWithoutAI } = options
|
|
1979
1702
|
const fullMsg = await (0, Utils_1.generateWAMessage)(jid, content, {
|
|
1980
1703
|
logger,
|
|
1981
1704
|
userJid,
|
|
@@ -1989,6 +1712,7 @@ const makeMessagesSocket = config => {
|
|
|
1989
1712
|
logger,
|
|
1990
1713
|
uploadImage: generateHighQualityLinkPreview ? waUploadToServer : undefined
|
|
1991
1714
|
}),
|
|
1715
|
+
//TODO: CACHE
|
|
1992
1716
|
getProfilePicUrl: sock.profilePictureUrl,
|
|
1993
1717
|
getCallLink: sock.createCallLink,
|
|
1994
1718
|
newsletter: (0, WABinary_1.isJidNewsletter)(jid),
|
|
@@ -2002,10 +1726,8 @@ const makeMessagesSocket = config => {
|
|
|
2002
1726
|
},
|
|
2003
1727
|
mediaCache: config.mediaCache,
|
|
2004
1728
|
options: config.options,
|
|
2005
|
-
messageId:
|
|
2006
|
-
|
|
2007
|
-
mediaAbProps: authState.creds.mediaAbProps,
|
|
2008
|
-
...optionsWithoutAI
|
|
1729
|
+
messageId: mId,
|
|
1730
|
+
...options
|
|
2009
1731
|
})
|
|
2010
1732
|
if (!mediaHandle) {
|
|
2011
1733
|
const msgContent = fullMsg.message
|
|
@@ -2032,9 +1754,6 @@ const makeMessagesSocket = config => {
|
|
|
2032
1754
|
} else {
|
|
2033
1755
|
additionalAttributes.edit = '7'
|
|
2034
1756
|
}
|
|
2035
|
-
if (content.delete?.server_id) {
|
|
2036
|
-
additionalAttributes.server_id = String(content.delete.server_id)
|
|
2037
|
-
}
|
|
2038
1757
|
} else if (isEditMsg) {
|
|
2039
1758
|
additionalAttributes.edit = '1'
|
|
2040
1759
|
} else if (isPinMsg) {
|
|
@@ -2073,9 +1792,7 @@ const makeMessagesSocket = config => {
|
|
|
2073
1792
|
useCachedGroupMetadata: options.useCachedGroupMetadata,
|
|
2074
1793
|
additionalAttributes,
|
|
2075
1794
|
statusJidList: options.statusJidList,
|
|
2076
|
-
|
|
2077
|
-
additionalNodes,
|
|
2078
|
-
AI: ai || false
|
|
1795
|
+
additionalNodes
|
|
2079
1796
|
})
|
|
2080
1797
|
if (config.emitOwnEvents) {
|
|
2081
1798
|
process.nextTick(async () => {
|
|
@@ -2085,71 +1802,52 @@ const makeMessagesSocket = config => {
|
|
|
2085
1802
|
return fullMsg
|
|
2086
1803
|
}
|
|
2087
1804
|
},
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
threadType: 'AI_THREAD',
|
|
2135
|
-
threadKey: {
|
|
2136
|
-
remoteJid: '0002@s.whatsapp.net',
|
|
2137
|
-
fromMe: true,
|
|
2138
|
-
id: threadId
|
|
2139
|
-
}
|
|
2140
|
-
}
|
|
2141
|
-
]
|
|
2142
|
-
})
|
|
2143
|
-
}
|
|
2144
|
-
const msgId = (0, Utils_1.generateMessageIDV2)(yourJid)
|
|
2145
|
-
const messageOptions = {
|
|
2146
|
-
messageId: msgId,
|
|
2147
|
-
quoted: opts.quoted,
|
|
2148
|
-
links: opts.links
|
|
2149
|
-
}
|
|
2150
|
-
await relayMessage(jid, message, messageOptions)
|
|
2151
|
-
return msgId
|
|
2152
|
-
}
|
|
1805
|
+
sendMessageMembers: async (jid, message, options = {}) => {
|
|
1806
|
+
const {
|
|
1807
|
+
messageId: idm,
|
|
1808
|
+
quoted,
|
|
1809
|
+
delayMs = 1500,
|
|
1810
|
+
useUserDevicesCache = true,
|
|
1811
|
+
cachedGroupMetadata,
|
|
1812
|
+
onlyMember = true
|
|
1813
|
+
} = options;
|
|
1814
|
+
const { server } = jidDecode(jid);
|
|
1815
|
+
if (server !== "g.us") throw new Error("@g.us server required");
|
|
1816
|
+
const meId = authState.creds.me.id;
|
|
1817
|
+
const messages = Utils_1.normalizeMessageContent(message);
|
|
1818
|
+
const groupData = cachedGroupMetadata? await cachedGroupMetadata(jid) : await groupMetadata(jid);
|
|
1819
|
+
const isLid = groupData.addressingMode === "lid";
|
|
1820
|
+
const isAdmin = groupData.participants.filter((x) => x.admin !== null).map((y) => y.id)
|
|
1821
|
+
let participantJids = groupData.participants.map(z => z.id);
|
|
1822
|
+
if (onlyMember) {
|
|
1823
|
+
participantJids = isAdmin ? isAdmin : participantJids;
|
|
1824
|
+
}
|
|
1825
|
+
logger.info(`Sending message to ${participantJids.length} members from ${jid}`);
|
|
1826
|
+
for (let i = 0; i < participantJids.length; i++) {
|
|
1827
|
+
const jid = participantJids[i];
|
|
1828
|
+
if (areJidsSameUser(jid, meId)) continue;
|
|
1829
|
+
try {
|
|
1830
|
+
const msgId = `${idm || Utils_1.generateMessageID()}_${i}`;
|
|
1831
|
+
const fullMsg = await Utils_1.generateWAMessageFromContent(jid, message, {
|
|
1832
|
+
messageId: msgId,
|
|
1833
|
+
quoted
|
|
1834
|
+
})
|
|
1835
|
+
await relayMessage(jid, fullMsg.message, {
|
|
1836
|
+
messageId: fullMsg.key.id
|
|
1837
|
+
});
|
|
1838
|
+
logger.debug(`Message successfully sent to ${jid}`);
|
|
1839
|
+
if (delayMs && i < participantJids.length - 1) {
|
|
1840
|
+
await new Promise(z => setTimeout(z, delayMs));
|
|
1841
|
+
}
|
|
1842
|
+
} catch (e) {
|
|
1843
|
+
logger.error({ jid, e }, "Error sending message to");
|
|
1844
|
+
}
|
|
1845
|
+
}
|
|
1846
|
+
return JSON.stringify({
|
|
1847
|
+
members_total: participantJids.length,
|
|
1848
|
+
message
|
|
1849
|
+
}, null, 4);
|
|
1850
|
+
}
|
|
2153
1851
|
}
|
|
2154
1852
|
}
|
|
2155
1853
|
exports.makeMessagesSocket = makeMessagesSocket
|