@hansaka02/baileys 7.3.4 → 7.3.6
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 +203 -247
- package/lib/Defaults/baileys-version.json +2 -2
- package/lib/Defaults/connection.js +1 -1
- package/lib/Defaults/constants.js +13 -1
- package/lib/Defaults/history.js +3 -1
- package/lib/Signal/Group/sender-chain-key.js +1 -14
- package/lib/Signal/Group/sender-key-distribution-message.js +2 -2
- package/lib/Signal/Group/sender-key-record.js +2 -11
- package/lib/Signal/Group/sender-key-state.js +11 -57
- package/lib/Signal/libsignal.js +200 -116
- package/lib/Signal/lid-mapping.js +121 -68
- package/lib/Socket/Client/websocket.js +9 -2
- package/lib/Socket/business.js +5 -1
- package/lib/Socket/chats.js +180 -89
- package/lib/Socket/community.js +169 -41
- package/lib/Socket/groups.js +25 -21
- package/lib/Socket/messages-recv.js +458 -333
- package/lib/Socket/messages-send.js +517 -572
- package/lib/Socket/mex.js +61 -0
- package/lib/Socket/newsletter.js +159 -252
- package/lib/Socket/socket.js +283 -100
- package/lib/Types/Newsletter.js +32 -25
- package/lib/Utils/auth-utils.js +189 -354
- package/lib/Utils/browser-utils.js +43 -0
- package/lib/Utils/chat-utils.js +166 -41
- package/lib/Utils/decode-wa-message.js +77 -35
- package/lib/Utils/event-buffer.js +80 -24
- package/lib/Utils/generics.js +28 -128
- package/lib/Utils/history.js +10 -8
- package/lib/Utils/index.js +1 -1
- package/lib/Utils/link-preview.js +17 -32
- package/lib/Utils/lt-hash.js +28 -22
- package/lib/Utils/make-mutex.js +26 -28
- package/lib/Utils/message-retry-manager.js +51 -3
- package/lib/Utils/messages-media.js +343 -151
- package/lib/Utils/messages.js +806 -792
- package/lib/Utils/noise-handler.js +33 -2
- package/lib/Utils/pre-key-manager.js +126 -0
- package/lib/Utils/process-message.js +115 -55
- package/lib/Utils/signal.js +45 -18
- package/lib/Utils/validate-connection.js +52 -29
- package/lib/WABinary/constants.js +1268 -1268
- package/lib/WABinary/decode.js +58 -4
- package/lib/WABinary/encode.js +54 -7
- package/lib/WABinary/jid-utils.js +58 -11
- package/lib/WAM/constants.js +19064 -11563
- package/lib/WAM/encode.js +57 -8
- package/lib/WAUSync/USyncQuery.js +35 -19
- package/package.json +9 -8
- package/lib/Socket/usync.js +0 -83
|
@@ -34,21 +34,19 @@ const {
|
|
|
34
34
|
prepareAlbumMessageContent,
|
|
35
35
|
aggregateMessageKeysNotFromMe
|
|
36
36
|
} = require("../Utils")
|
|
37
|
-
const {
|
|
38
|
-
QueryIds,
|
|
39
|
-
XWAPaths,
|
|
40
|
-
WAMessageAddressingMode
|
|
41
|
-
} = require("../Types")
|
|
37
|
+
const { WAMessageAddressingMode } = require("../Types")
|
|
42
38
|
const {
|
|
43
39
|
areJidsSameUser,
|
|
44
40
|
getBinaryNodeChild,
|
|
45
41
|
getBinaryNodeChildren,
|
|
46
42
|
getBinaryFilteredBizBot,
|
|
47
43
|
getBinaryFilteredButtons,
|
|
44
|
+
isHostedLidUser,
|
|
45
|
+
isHostedPnUser,
|
|
48
46
|
isJidNewsletter,
|
|
49
47
|
isJidGroup,
|
|
50
48
|
isLidUser,
|
|
51
|
-
|
|
49
|
+
isPnUser,
|
|
52
50
|
jidDecode,
|
|
53
51
|
jidEncode,
|
|
54
52
|
jidNormalizedUser,
|
|
@@ -64,15 +62,46 @@ const { getUrlInfo } = require("../Utils/link-preview")
|
|
|
64
62
|
const { makeKeyedMutex } = require("../Utils/make-mutex")
|
|
65
63
|
|
|
66
64
|
const makeMessagesSocket = (config) => {
|
|
67
|
-
const {
|
|
65
|
+
const {
|
|
66
|
+
logger,
|
|
67
|
+
linkPreviewImageThumbnailWidth,
|
|
68
|
+
generateHighQualityLinkPreview,
|
|
69
|
+
options: httpRequestOptions,
|
|
70
|
+
patchMessageBeforeSending,
|
|
71
|
+
cachedGroupMetadata,
|
|
72
|
+
enableRecentMessageCache,
|
|
73
|
+
maxMsgRetryCount
|
|
74
|
+
} = config
|
|
75
|
+
|
|
68
76
|
const suki = makeNewsletterSocket(config)
|
|
69
|
-
|
|
77
|
+
|
|
78
|
+
const {
|
|
79
|
+
ev,
|
|
80
|
+
authState,
|
|
81
|
+
messageMutex,
|
|
82
|
+
signalRepository,
|
|
83
|
+
upsertMessage,
|
|
84
|
+
createCallLink,
|
|
85
|
+
query,
|
|
86
|
+
fetchPrivacySettings,
|
|
87
|
+
sendNode,
|
|
88
|
+
groupQuery,
|
|
89
|
+
groupMetadata,
|
|
90
|
+
groupToggleEphemeral,
|
|
91
|
+
executeUSyncQuery,
|
|
92
|
+
newsletterMetadata
|
|
93
|
+
} = suki
|
|
70
94
|
|
|
71
95
|
const userDevicesCache = config.userDevicesCache || new NodeCache({
|
|
72
96
|
stdTTL: DEFAULT_CACHE_TTLS.USER_DEVICES,
|
|
73
97
|
useClones: false
|
|
74
98
|
})
|
|
75
99
|
|
|
100
|
+
const peerSessionsCache = new NodeCache({
|
|
101
|
+
stdTTL: DEFAULT_CACHE_TTLS.USER_DEVICES,
|
|
102
|
+
useClones: false
|
|
103
|
+
})
|
|
104
|
+
|
|
76
105
|
// Initialize message retry manager if enabled
|
|
77
106
|
const messageRetryManager = enableRecentMessageCache ? new MessageRetryManager(logger, maxMsgRetryCount) : null
|
|
78
107
|
|
|
@@ -83,38 +112,38 @@ const makeMessagesSocket = (config) => {
|
|
|
83
112
|
|
|
84
113
|
const refreshMediaConn = async (forceGet = false) => {
|
|
85
114
|
const media = await mediaConn
|
|
86
|
-
|
|
87
|
-
if (!media || forceGet ||
|
|
115
|
+
|
|
116
|
+
if (!media || forceGet || new Date().getTime() - media.fetchDate.getTime() > media.ttl * 1000) {
|
|
88
117
|
mediaConn = (async () => {
|
|
89
|
-
|
|
90
118
|
const result = await query({
|
|
91
119
|
tag: 'iq',
|
|
92
120
|
attrs: {
|
|
93
121
|
type: 'set',
|
|
94
122
|
xmlns: 'w:m',
|
|
95
|
-
to: S_WHATSAPP_NET
|
|
123
|
+
to: S_WHATSAPP_NET
|
|
96
124
|
},
|
|
97
125
|
content: [{ tag: 'media_conn', attrs: {} }]
|
|
98
126
|
})
|
|
99
|
-
|
|
127
|
+
|
|
100
128
|
const mediaConnNode = getBinaryNodeChild(result, 'media_conn')
|
|
101
|
-
|
|
129
|
+
|
|
130
|
+
// TODO: explore full length of data that whatsapp provides
|
|
102
131
|
const node = {
|
|
103
132
|
hosts: getBinaryNodeChildren(mediaConnNode, 'host').map(({ attrs }) => ({
|
|
104
133
|
hostname: attrs.hostname,
|
|
105
|
-
maxContentLengthBytes: +attrs.maxContentLengthBytes
|
|
134
|
+
maxContentLengthBytes: +attrs.maxContentLengthBytes
|
|
106
135
|
})),
|
|
107
136
|
auth: mediaConnNode.attrs.auth,
|
|
108
137
|
ttl: +mediaConnNode.attrs.ttl,
|
|
109
138
|
fetchDate: new Date()
|
|
110
139
|
}
|
|
111
|
-
|
|
140
|
+
|
|
112
141
|
logger.debug('fetched media conn')
|
|
113
|
-
|
|
142
|
+
|
|
114
143
|
return node
|
|
115
144
|
})()
|
|
116
145
|
}
|
|
117
|
-
|
|
146
|
+
|
|
118
147
|
return mediaConn
|
|
119
148
|
}
|
|
120
149
|
|
|
@@ -123,37 +152,42 @@ const makeMessagesSocket = (config) => {
|
|
|
123
152
|
* used for receipts of phone call, read, delivery etc.
|
|
124
153
|
* */
|
|
125
154
|
const sendReceipt = async (jid, participant, messageIds, type) => {
|
|
155
|
+
if (!messageIds || messageIds.length === 0) {
|
|
156
|
+
throw new Boom('missing ids in receipt')
|
|
157
|
+
}
|
|
158
|
+
|
|
126
159
|
const node = {
|
|
127
160
|
tag: 'receipt',
|
|
128
161
|
attrs: {
|
|
129
|
-
id: messageIds[0]
|
|
130
|
-
}
|
|
162
|
+
id: messageIds[0]
|
|
163
|
+
}
|
|
131
164
|
}
|
|
132
|
-
|
|
165
|
+
|
|
133
166
|
const isReadReceipt = type === 'read' || type === 'read-self'
|
|
134
|
-
|
|
167
|
+
|
|
135
168
|
if (isReadReceipt) {
|
|
136
169
|
node.attrs.t = unixTimestampSeconds().toString()
|
|
137
170
|
}
|
|
138
|
-
|
|
139
|
-
if (type === 'sender' &&
|
|
171
|
+
|
|
172
|
+
if (type === 'sender' && (isPnUser(jid) || isLidUser(jid))) {
|
|
140
173
|
node.attrs.recipient = jid
|
|
141
174
|
node.attrs.to = participant
|
|
142
175
|
}
|
|
143
|
-
|
|
176
|
+
|
|
144
177
|
else {
|
|
145
178
|
node.attrs.to = jid
|
|
179
|
+
|
|
146
180
|
if (participant) {
|
|
147
181
|
node.attrs.participant = participant
|
|
148
182
|
}
|
|
149
183
|
}
|
|
150
|
-
|
|
184
|
+
|
|
151
185
|
if (type) {
|
|
152
|
-
node.attrs.type =
|
|
186
|
+
node.attrs.type = type
|
|
153
187
|
}
|
|
154
|
-
|
|
188
|
+
|
|
155
189
|
const remainingMessageIds = messageIds.slice(1)
|
|
156
|
-
|
|
190
|
+
|
|
157
191
|
if (remainingMessageIds.length) {
|
|
158
192
|
node.content = [
|
|
159
193
|
{
|
|
@@ -166,9 +200,9 @@ const makeMessagesSocket = (config) => {
|
|
|
166
200
|
}
|
|
167
201
|
]
|
|
168
202
|
}
|
|
169
|
-
|
|
203
|
+
|
|
170
204
|
logger.debug({ attrs: node.attrs, messageIds }, 'sending receipt for messages')
|
|
171
|
-
|
|
205
|
+
|
|
172
206
|
await sendNode(node)
|
|
173
207
|
}
|
|
174
208
|
|
|
@@ -191,84 +225,6 @@ const makeMessagesSocket = (config) => {
|
|
|
191
225
|
await sendReceipts(keys, readType)
|
|
192
226
|
}
|
|
193
227
|
|
|
194
|
-
/**
|
|
195
|
-
* Deduplicate JIDs when both LID and PN versions exist for same user
|
|
196
|
-
* Prefers LID over PN to maintain single encryption layer
|
|
197
|
-
*/
|
|
198
|
-
const deduplicateLidPnJids = (jids) => {
|
|
199
|
-
const lidUsers = new Set()
|
|
200
|
-
const filteredJids = []
|
|
201
|
-
|
|
202
|
-
// Collect all LID users
|
|
203
|
-
for (const jid of jids) {
|
|
204
|
-
if (isLidUser(jid)) {
|
|
205
|
-
const user = jidDecode(jid)?.user
|
|
206
|
-
if (user)
|
|
207
|
-
lidUsers.add(user)
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
// Filter out PN versions when LID exists
|
|
212
|
-
for (const jid of jids) {
|
|
213
|
-
if (isJidUser(jid)) {
|
|
214
|
-
const user = jidDecode(jid)?.user
|
|
215
|
-
if (user && lidUsers.has(user)) {
|
|
216
|
-
logger.debug({ jid }, 'Skipping PN - LID version exists')
|
|
217
|
-
continue
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
filteredJids.push(jid)
|
|
221
|
-
}
|
|
222
|
-
return filteredJids
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
/** Fetch image for groups, user, and newsletter **/
|
|
226
|
-
const profilePictureUrl = async (jid) => {
|
|
227
|
-
if (isJidNewsletter(jid)) {
|
|
228
|
-
|
|
229
|
-
let node = await newsletterWMexQuery(undefined, QueryIds.METADATA, {
|
|
230
|
-
input: {
|
|
231
|
-
key: jid,
|
|
232
|
-
type: 'JID',
|
|
233
|
-
view_role: 'GUEST'
|
|
234
|
-
},
|
|
235
|
-
fetch_viewer_metadata: true,
|
|
236
|
-
fetch_full_image: true,
|
|
237
|
-
fetch_creation_time: true
|
|
238
|
-
})
|
|
239
|
-
|
|
240
|
-
let result = getBinaryNodeChild(node, 'result')?.content?.toString()
|
|
241
|
-
|
|
242
|
-
let metadata = JSON.parse(result).data[XWAPaths.NEWSLETTER]
|
|
243
|
-
|
|
244
|
-
return getUrlFromDirectPath(metadata.thread_metadata.picture?.direct_path || '')
|
|
245
|
-
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
else {
|
|
249
|
-
const result = await query({
|
|
250
|
-
tag: 'iq',
|
|
251
|
-
attrs: {
|
|
252
|
-
target: jidNormalizedUser(jid),
|
|
253
|
-
to: S_WHATSAPP_NET,
|
|
254
|
-
type: 'get',
|
|
255
|
-
xmlns: 'w:profile:picture'
|
|
256
|
-
},
|
|
257
|
-
content: [{
|
|
258
|
-
tag: 'picture',
|
|
259
|
-
attrs: {
|
|
260
|
-
type: 'image',
|
|
261
|
-
query: 'url'
|
|
262
|
-
}
|
|
263
|
-
}]
|
|
264
|
-
})
|
|
265
|
-
|
|
266
|
-
const child = getBinaryNodeChild(result, 'picture')
|
|
267
|
-
|
|
268
|
-
return child?.attrs?.url || null
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
|
|
272
228
|
/** Fetch all the devices we've to send a message to */
|
|
273
229
|
const getUSyncDevices = async (jids, useCache, ignoreZeroDevices) => {
|
|
274
230
|
const deviceResults = []
|
|
@@ -279,7 +235,6 @@ const makeMessagesSocket = (config) => {
|
|
|
279
235
|
|
|
280
236
|
const toFetch = []
|
|
281
237
|
|
|
282
|
-
jids = deduplicateLidPnJids(Array.from(new Set(jids)))
|
|
283
238
|
const jidsWithUser = jids
|
|
284
239
|
.map(jid => {
|
|
285
240
|
const decoded = jidDecode(jid)
|
|
@@ -291,20 +246,22 @@ const makeMessagesSocket = (config) => {
|
|
|
291
246
|
deviceResults.push({
|
|
292
247
|
user,
|
|
293
248
|
device,
|
|
294
|
-
|
|
295
|
-
})
|
|
249
|
+
jid
|
|
250
|
+
})
|
|
251
|
+
|
|
296
252
|
return null
|
|
297
253
|
}
|
|
298
254
|
|
|
299
255
|
jid = jidNormalizedUser(jid)
|
|
256
|
+
|
|
300
257
|
return { jid, user }
|
|
301
|
-
})
|
|
302
|
-
|
|
303
|
-
|
|
258
|
+
}).filter(jid => jid !== null)
|
|
259
|
+
|
|
304
260
|
let mgetDevices
|
|
305
261
|
|
|
306
262
|
if (useCache && userDevicesCache.mget) {
|
|
307
263
|
const usersToFetch = jidsWithUser.map(j => j?.user).filter(Boolean)
|
|
264
|
+
|
|
308
265
|
mgetDevices = await userDevicesCache.mget(usersToFetch)
|
|
309
266
|
}
|
|
310
267
|
|
|
@@ -314,19 +271,21 @@ const makeMessagesSocket = (config) => {
|
|
|
314
271
|
(userDevicesCache.mget ? undefined : (await userDevicesCache.get(user)))
|
|
315
272
|
|
|
316
273
|
if (devices) {
|
|
317
|
-
const
|
|
318
|
-
const devicesWithWire = devices.map(d => ({
|
|
274
|
+
const devicesWithJid = devices.map(d => ({
|
|
319
275
|
...d,
|
|
320
|
-
|
|
276
|
+
jid: jidEncode(d.user, d.server, d.device)
|
|
321
277
|
}))
|
|
322
278
|
|
|
323
|
-
deviceResults.push(...
|
|
279
|
+
deviceResults.push(...devicesWithJid)
|
|
280
|
+
|
|
324
281
|
logger.trace({ user }, 'using cache for devices')
|
|
325
282
|
}
|
|
283
|
+
|
|
326
284
|
else {
|
|
327
285
|
toFetch.push(jid)
|
|
328
286
|
}
|
|
329
287
|
}
|
|
288
|
+
|
|
330
289
|
else {
|
|
331
290
|
toFetch.push(jid)
|
|
332
291
|
}
|
|
@@ -337,23 +296,49 @@ const makeMessagesSocket = (config) => {
|
|
|
337
296
|
}
|
|
338
297
|
|
|
339
298
|
const requestedLidUsers = new Set()
|
|
299
|
+
|
|
340
300
|
for (const jid of toFetch) {
|
|
341
|
-
if (isLidUser(jid)) {
|
|
301
|
+
if (isLidUser(jid) || isHostedLidUser(jid)) {
|
|
342
302
|
const user = jidDecode(jid)?.user
|
|
303
|
+
|
|
343
304
|
if (user)
|
|
344
305
|
requestedLidUsers.add(user)
|
|
345
306
|
}
|
|
346
307
|
}
|
|
347
308
|
|
|
348
|
-
const query = new USyncQuery().withContext('message').withDeviceProtocol()
|
|
309
|
+
const query = new USyncQuery().withContext('message').withDeviceProtocol().withLIDProtocol()
|
|
310
|
+
|
|
349
311
|
for (const jid of toFetch) {
|
|
350
|
-
query.withUser(new USyncUser().withId(jid))
|
|
312
|
+
query.withUser(new USyncUser().withId(jid)) // todo: investigate - the idea here is that <user> should have an inline lid field with the lid being the pn equivalent
|
|
351
313
|
}
|
|
352
314
|
|
|
353
315
|
const result = await executeUSyncQuery(query)
|
|
354
316
|
|
|
355
317
|
if (result) {
|
|
356
|
-
|
|
318
|
+
// TODO: LID MAP this stuff (lid protocol will now return lid with devices)
|
|
319
|
+
const lidResults = result.list.filter(a => !!a.lid)
|
|
320
|
+
|
|
321
|
+
if (lidResults.length > 0) {
|
|
322
|
+
logger.trace('Storing LID maps from device call')
|
|
323
|
+
|
|
324
|
+
await signalRepository.lidMapping.storeLIDPNMappings(lidResults.map(a => ({ lid: a.lid, pn: a.id })))
|
|
325
|
+
|
|
326
|
+
// Force-refresh sessions for newly mapped LIDs to align identity addressing
|
|
327
|
+
try {
|
|
328
|
+
const lids = lidResults.map(a => a.lid)
|
|
329
|
+
|
|
330
|
+
if (lids.length) {
|
|
331
|
+
await assertSessions(lids, true)
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
catch (e) {
|
|
336
|
+
logger.warn({ e, count: lidResults.length }, 'failed to assert sessions for newly mapped LIDs')
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
const extracted = extractDeviceJids(result?.list, authState.creds.me.id, authState.creds.me.lid, ignoreZeroDevices)
|
|
341
|
+
|
|
357
342
|
const deviceMap = {}
|
|
358
343
|
|
|
359
344
|
for (const item of extracted) {
|
|
@@ -363,22 +348,21 @@ const makeMessagesSocket = (config) => {
|
|
|
363
348
|
|
|
364
349
|
// Process each user's devices as a group for bulk LID migration
|
|
365
350
|
for (const [user, userDevices] of Object.entries(deviceMap)) {
|
|
366
|
-
const isLidUser = requestedLidUsers.has(user)
|
|
367
|
-
|
|
351
|
+
const isLidUser = requestedLidUsers.has(user);
|
|
368
352
|
// Process all devices for this user
|
|
369
353
|
for (const item of userDevices) {
|
|
370
|
-
const
|
|
371
|
-
? jidEncode(user,
|
|
372
|
-
: jidEncode(item.user,
|
|
354
|
+
const finalJid = isLidUser
|
|
355
|
+
? jidEncode(user, item.server, item.device)
|
|
356
|
+
: jidEncode(item.user, item.server, item.device)
|
|
373
357
|
deviceResults.push({
|
|
374
358
|
...item,
|
|
375
|
-
|
|
376
|
-
})
|
|
359
|
+
jid: finalJid
|
|
360
|
+
})
|
|
377
361
|
|
|
378
362
|
logger.debug({
|
|
379
363
|
user: item.user,
|
|
380
364
|
device: item.device,
|
|
381
|
-
|
|
365
|
+
finalJid,
|
|
382
366
|
usedLid: isLidUser
|
|
383
367
|
}, 'Processed device with LID priority')
|
|
384
368
|
}
|
|
@@ -396,162 +380,99 @@ const makeMessagesSocket = (config) => {
|
|
|
396
380
|
}
|
|
397
381
|
}
|
|
398
382
|
|
|
399
|
-
|
|
400
|
-
return deviceResults
|
|
401
|
-
}
|
|
402
|
-
|
|
403
|
-
const assertSessions = async (jids, force) => {
|
|
404
|
-
let didFetchNewSession = false
|
|
405
|
-
const jidsRequiringFetch = []
|
|
406
|
-
|
|
407
|
-
// Apply same deduplication as in getUSyncDevices
|
|
408
|
-
jids = deduplicateLidPnJids(jids)
|
|
409
|
-
|
|
410
|
-
if (force) {
|
|
411
|
-
// Check which sessions are missing (with LID migration check)
|
|
412
|
-
const addrs = jids.map(jid => signalRepository.jidToSignalProtocolAddress(jid))
|
|
413
|
-
const sessions = await authState.keys.get('session', addrs)
|
|
383
|
+
const userDeviceUpdates = {}
|
|
414
384
|
|
|
415
|
-
const
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
// Add to fetch list if no session exists
|
|
420
|
-
// Session type selection (LID vs PN) is handled in encryptMessage
|
|
421
|
-
if (!hasSession) {
|
|
422
|
-
if (jid.includes('@lid')) {
|
|
423
|
-
logger.debug({ jid }, 'No LID session found, will create new LID session')
|
|
424
|
-
}
|
|
425
|
-
jidsRequiringFetch.push(jid)
|
|
385
|
+
for (const [userId, devices] of Object.entries(deviceMap)) {
|
|
386
|
+
if (devices && devices.length > 0) {
|
|
387
|
+
userDeviceUpdates[userId] = devices.map(d => d.device?.toString() || '0')
|
|
426
388
|
}
|
|
427
389
|
}
|
|
428
390
|
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
391
|
+
if (Object.keys(userDeviceUpdates).length > 0) {
|
|
392
|
+
try {
|
|
393
|
+
await authState.keys.set({ 'device-list': userDeviceUpdates })
|
|
394
|
+
|
|
395
|
+
logger.debug({ userCount: Object.keys(userDeviceUpdates).length }, 'stored user device lists for bulk migration')
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
catch (error) {
|
|
399
|
+
logger.warn({ error }, 'failed to store user device lists')
|
|
400
|
+
}
|
|
432
401
|
}
|
|
433
402
|
}
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
403
|
+
|
|
404
|
+
return deviceResults
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
const updateMemberLabel = (jid, memberLabel) => {
|
|
408
|
+
if (!isJidGroup(jid)) {
|
|
409
|
+
throw new Error('Jid must a group!')
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
const protocolMessage = {
|
|
413
|
+
protocolMessage: {
|
|
414
|
+
type: proto.Message.ProtocolMessage.Type.GROUP_MEMBER_LABEL_CHANGE,
|
|
415
|
+
memberLabel: {
|
|
416
|
+
label: memberLabel?.slice(0, 30),
|
|
417
|
+
labelTimestamp: unixTimestampSeconds()
|
|
444
418
|
}
|
|
445
|
-
userGroups.get(user).push(jid)
|
|
446
419
|
}
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
const pnJid = `${user}@s.whatsapp.net`
|
|
457
|
-
const mapping = await signalRepository.lidMapping.getLIDForPN(pnJid)
|
|
458
|
-
|
|
459
|
-
if (mapping?.includes('@lid')) {
|
|
460
|
-
logger.debug({ user, lidForPN: mapping, deviceCount: userJids.length }, 'User has LID mapping - preparing bulk migration')
|
|
461
|
-
return { shouldMigrate: true, lidForPN: mapping }
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
return relayMessage(jid, protocolMessage, {
|
|
423
|
+
additionalNodes: [
|
|
424
|
+
{
|
|
425
|
+
tag: 'meta',
|
|
426
|
+
attrs: {
|
|
427
|
+
tag_reason: 'user_update',
|
|
428
|
+
appdata: 'member_tag'
|
|
462
429
|
}
|
|
463
430
|
}
|
|
464
|
-
|
|
465
|
-
|
|
431
|
+
]
|
|
432
|
+
})
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
const assertSessions = async (jids, force) => {
|
|
436
|
+
let didFetchNewSession = false
|
|
437
|
+
|
|
438
|
+
const uniqueJids = [...new Set(jids)] // Deduplicate JIDs
|
|
439
|
+
const jidsRequiringFetch = []
|
|
440
|
+
|
|
441
|
+
logger.debug({ jids }, 'assertSessions call with jids')
|
|
442
|
+
|
|
443
|
+
// Check peerSessionsCache and validate sessions using libsignal loadSession
|
|
444
|
+
for (const jid of uniqueJids) {
|
|
445
|
+
const signalId = signalRepository.jidToSignalProtocolAddress(jid)
|
|
446
|
+
const cachedSession = peerSessionsCache.get(signalId)
|
|
447
|
+
|
|
448
|
+
if (cachedSession !== undefined) {
|
|
449
|
+
if (cachedSession && !force) {
|
|
450
|
+
continue // Session exists in cache
|
|
466
451
|
}
|
|
467
|
-
|
|
468
|
-
return { shouldMigrate: false, lidForPN: undefined }
|
|
469
452
|
}
|
|
470
453
|
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
const
|
|
474
|
-
const shouldMigrateUser = mappingResult.shouldMigrate
|
|
475
|
-
const lidForPN = mappingResult.lidForPN
|
|
454
|
+
else {
|
|
455
|
+
const sessionValidation = await signalRepository.validateSession(jid)
|
|
456
|
+
const hasSession = sessionValidation.exists
|
|
476
457
|
|
|
477
|
-
|
|
478
|
-
if (shouldMigrateUser && lidForPN) {
|
|
479
|
-
// Bulk migrate all user devices in single transaction
|
|
480
|
-
const migrationResult = await signalRepository.migrateSession(userJids, lidForPN)
|
|
481
|
-
|
|
482
|
-
if (migrationResult.migrated > 0) {
|
|
483
|
-
logger.info({
|
|
484
|
-
user,
|
|
485
|
-
lidMapping: lidForPN,
|
|
486
|
-
migrated: migrationResult.migrated,
|
|
487
|
-
skipped: migrationResult.skipped,
|
|
488
|
-
total: migrationResult.total
|
|
489
|
-
}, 'Completed bulk migration for user devices');
|
|
490
|
-
}
|
|
491
|
-
else {
|
|
492
|
-
logger.debug({
|
|
493
|
-
user,
|
|
494
|
-
lidMapping: lidForPN,
|
|
495
|
-
skipped: migrationResult.skipped,
|
|
496
|
-
total: migrationResult.total
|
|
497
|
-
}, 'All user device sessions already migrated');
|
|
498
|
-
}
|
|
499
|
-
}
|
|
458
|
+
peerSessionsCache.set(signalId, hasSession)
|
|
500
459
|
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
const signalId = signalRepository.jidToSignalProtocolAddress(jid)
|
|
504
|
-
|
|
505
|
-
if (sessions[signalId]) return
|
|
506
|
-
|
|
507
|
-
// Determine correct JID to fetch (LID if mapping exists, otherwise original)
|
|
508
|
-
if (jid.includes('@s.whatsapp.net') && shouldMigrateUser && lidForPN) {
|
|
509
|
-
const decoded = jidDecode(jid)
|
|
510
|
-
const lidDeviceJid = decoded.device !== undefined ? `${jidDecode(lidForPN).user}:${decoded.device}@lid` : lidForPN
|
|
511
|
-
|
|
512
|
-
jidsRequiringFetch.push(lidDeviceJid)
|
|
513
|
-
logger.debug({ pnJid: jid, lidJid: lidDeviceJid }, 'Adding LID JID to fetch list (conversion)')
|
|
514
|
-
}
|
|
515
|
-
|
|
516
|
-
else {
|
|
517
|
-
jidsRequiringFetch.push(jid)
|
|
518
|
-
logger.debug({ jid }, 'Adding JID to fetch list')
|
|
519
|
-
}
|
|
460
|
+
if (hasSession && !force) {
|
|
461
|
+
continue
|
|
520
462
|
}
|
|
521
|
-
|
|
522
|
-
userJids.forEach(addMissingSessionsToFetchList)
|
|
523
463
|
}
|
|
464
|
+
|
|
465
|
+
jidsRequiringFetch.push(jid)
|
|
524
466
|
}
|
|
525
467
|
|
|
526
468
|
if (jidsRequiringFetch.length) {
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
for (const jid of jidsRequiringFetch) {
|
|
534
|
-
const user = jidDecode(jid)?.user
|
|
535
|
-
|
|
536
|
-
if (user) {
|
|
537
|
-
if (isLidUser(jid)) {
|
|
538
|
-
lidUsersBeingFetched.add(user)
|
|
539
|
-
}
|
|
540
|
-
else if (isJidUser(jid)) {
|
|
541
|
-
pnUsersBeingFetched.add(user)
|
|
542
|
-
}
|
|
543
|
-
}
|
|
544
|
-
}
|
|
469
|
+
// LID if mapped, otherwise original
|
|
470
|
+
const wireJids = [
|
|
471
|
+
...jidsRequiringFetch.filter(jid => !!isLidUser(jid) || !!isHostedLidUser(jid)),
|
|
472
|
+
...((await signalRepository.lidMapping.getLIDsForPNs(jidsRequiringFetch.filter(jid => !!isPnUser(jid) || !!isHostedPnUser(jid)))) || []).map(a => a.lid)
|
|
473
|
+
]
|
|
545
474
|
|
|
546
|
-
|
|
547
|
-
const overlapping = Array.from(pnUsersBeingFetched).filter(user => lidUsersBeingFetched.has(user))
|
|
548
|
-
if (overlapping.length > 0) {
|
|
549
|
-
logger.warn({
|
|
550
|
-
overlapping,
|
|
551
|
-
lidUsersBeingFetched: Array.from(lidUsersBeingFetched),
|
|
552
|
-
pnUsersBeingFetched: Array.from(pnUsersBeingFetched)
|
|
553
|
-
}, 'Fetching both LID and PN sessions for same users')
|
|
554
|
-
}
|
|
475
|
+
logger.debug({ jidsRequiringFetch, wireJids }, 'fetching sessions')
|
|
555
476
|
|
|
556
477
|
const result = await query({
|
|
557
478
|
tag: 'iq',
|
|
@@ -564,163 +485,99 @@ const makeMessagesSocket = (config) => {
|
|
|
564
485
|
{
|
|
565
486
|
tag: 'key',
|
|
566
487
|
attrs: {},
|
|
567
|
-
content:
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
488
|
+
content: wireJids.map(jid => {
|
|
489
|
+
const attrs = { jid }
|
|
490
|
+
|
|
491
|
+
if (force) attrs.reason = 'identity'
|
|
492
|
+
|
|
493
|
+
return { tag: 'user', attrs }
|
|
494
|
+
})
|
|
571
495
|
}
|
|
572
496
|
]
|
|
573
497
|
})
|
|
574
498
|
|
|
575
499
|
await parseAndInjectE2ESessions(result, signalRepository)
|
|
500
|
+
|
|
576
501
|
didFetchNewSession = true
|
|
502
|
+
|
|
503
|
+
// Cache fetched sessions using wire JIDs
|
|
504
|
+
for (const wireJid of wireJids) {
|
|
505
|
+
const signalId = signalRepository.jidToSignalProtocolAddress(wireJid)
|
|
506
|
+
peerSessionsCache.set(signalId, true)
|
|
507
|
+
}
|
|
577
508
|
}
|
|
509
|
+
|
|
578
510
|
return didFetchNewSession
|
|
579
511
|
}
|
|
580
|
-
|
|
581
|
-
/** Send Peer Operation */
|
|
512
|
+
|
|
582
513
|
const sendPeerDataOperationMessage = async (pdoMessage) => {
|
|
583
514
|
//TODO: for later, abstract the logic to send a Peer Message instead of just PDO - useful for App State Key Resync with phone
|
|
584
515
|
if (!authState.creds.me?.id) {
|
|
585
516
|
throw new Boom('Not authenticated')
|
|
586
517
|
}
|
|
587
|
-
|
|
518
|
+
|
|
588
519
|
const protocolMessage = {
|
|
589
520
|
protocolMessage: {
|
|
590
521
|
peerDataOperationRequestMessage: pdoMessage,
|
|
591
522
|
type: proto.Message.ProtocolMessage.Type.PEER_DATA_OPERATION_REQUEST_MESSAGE
|
|
592
523
|
}
|
|
593
524
|
}
|
|
594
|
-
|
|
525
|
+
|
|
595
526
|
const meJid = jidNormalizedUser(authState.creds.me.id)
|
|
596
|
-
|
|
597
527
|
const msgId = await relayMessage(meJid, protocolMessage, {
|
|
598
528
|
additionalAttributes: {
|
|
599
529
|
category: 'peer',
|
|
600
|
-
|
|
601
|
-
push_priority: 'high_force',
|
|
530
|
+
push_priority: 'high_force'
|
|
602
531
|
},
|
|
532
|
+
additionalNodes: [
|
|
533
|
+
{
|
|
534
|
+
tag: 'meta',
|
|
535
|
+
attrs: { appdata: 'default' }
|
|
536
|
+
}
|
|
537
|
+
]
|
|
603
538
|
})
|
|
604
|
-
|
|
539
|
+
|
|
605
540
|
return msgId
|
|
606
541
|
}
|
|
607
542
|
|
|
608
|
-
const createParticipantNodes = async (
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
if (!Array.isArray(patched)) {
|
|
612
|
-
patched = jids ? jids.map(jid => ({ recipientJid: jid, ...patched })) : [patched]
|
|
543
|
+
const createParticipantNodes = async (recipientJids, message, extraAttrs, dsmMessage) => {
|
|
544
|
+
if (!recipientJids.length) {
|
|
545
|
+
return { nodes: [], shouldIncludeDeviceIdentity: false }
|
|
613
546
|
}
|
|
614
547
|
|
|
548
|
+
const patched = await patchMessageBeforeSending(message, recipientJids)
|
|
549
|
+
const patchedMessages = Array.isArray(patched)
|
|
550
|
+
? patched
|
|
551
|
+
: recipientJids.map(jid => ({ recipientJid: jid, message: patched }))
|
|
552
|
+
|
|
615
553
|
let shouldIncludeDeviceIdentity = false
|
|
616
554
|
|
|
617
555
|
const meId = authState.creds.me.id
|
|
618
556
|
const meLid = authState.creds.me?.lid
|
|
619
557
|
const meLidUser = meLid ? jidDecode(meLid)?.user : null
|
|
620
|
-
const
|
|
621
|
-
|
|
622
|
-
for (const patchedMessageWithJid of patched) {
|
|
623
|
-
const { recipientJid: wireJid, ...patchedMessage } = patchedMessageWithJid
|
|
624
|
-
if (!wireJid)
|
|
625
|
-
continue
|
|
558
|
+
const encryptionPromises = patchedMessages.map(async ({ recipientJid: jid, message: patchedMessage }) => {
|
|
559
|
+
if (!jid) return null
|
|
626
560
|
|
|
627
|
-
|
|
628
|
-
const decoded = jidDecode(wireJid)
|
|
629
|
-
const user = decoded?.user
|
|
561
|
+
let msgToEncrypt = patchedMessage
|
|
630
562
|
|
|
631
|
-
if (
|
|
632
|
-
|
|
563
|
+
if (dsmMessage) {
|
|
564
|
+
const { user: targetUser } = jidDecode(jid)
|
|
565
|
+
const { user: ownPnUser } = jidDecode(meId)
|
|
566
|
+
const ownLidUser = meLidUser
|
|
567
|
+
const isOwnUser = targetUser === ownPnUser || (ownLidUser && targetUser === ownLidUser)
|
|
568
|
+
const isExactSenderDevice = jid === meId || (meLid && jid === meLid)
|
|
633
569
|
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
devicesByUser.get(user).push({ recipientJid: wireJid, patchedMessage })
|
|
639
|
-
}
|
|
640
|
-
|
|
641
|
-
// Process each user's devices sequentially, but different users in parallel
|
|
642
|
-
const userEncryptionPromises = Array.from(devicesByUser.entries()).map(([user, userDevices]) => encryptionMutex.mutex(user, async () => {
|
|
643
|
-
logger.debug({ user, deviceCount: userDevices.length }, 'Acquiring encryption lock for user devices');
|
|
644
|
-
const userNodes = []
|
|
645
|
-
|
|
646
|
-
// Helper to get encryption JID with LID migration
|
|
647
|
-
const getEncryptionJid = async (wireJid) => {
|
|
648
|
-
if (!isJidUser(wireJid))
|
|
649
|
-
return wireJid
|
|
650
|
-
|
|
651
|
-
try {
|
|
652
|
-
const lidForPN = await signalRepository.lidMapping.getLIDForPN(wireJid)
|
|
653
|
-
|
|
654
|
-
if (!lidForPN?.includes('@lid'))
|
|
655
|
-
return wireJid
|
|
656
|
-
|
|
657
|
-
// Preserve device ID from original wire JID
|
|
658
|
-
const wireDecoded = jidDecode(wireJid)
|
|
659
|
-
const deviceId = wireDecoded?.device || 0
|
|
660
|
-
const lidDecoded = jidDecode(lidForPN)
|
|
661
|
-
const lidWithDevice = jidEncode(lidDecoded?.user, 'lid', deviceId)
|
|
662
|
-
|
|
663
|
-
// Migrate session to LID for unified encryption layer
|
|
664
|
-
try {
|
|
665
|
-
const migrationResult = await signalRepository.migrateSession([wireJid], lidWithDevice)
|
|
666
|
-
const recipientUser = jidNormalizedUser(wireJid)
|
|
667
|
-
const ownPnUser = jidNormalizedUser(meId)
|
|
668
|
-
const isOwnDevice = recipientUser === ownPnUser
|
|
669
|
-
logger.info({ wireJid, lidWithDevice, isOwnDevice }, 'Migrated to LID encryption')
|
|
670
|
-
|
|
671
|
-
// Delete PN session after successful migration
|
|
672
|
-
try {
|
|
673
|
-
if (migrationResult.migrated) {
|
|
674
|
-
await signalRepository.deleteSession([wireJid])
|
|
675
|
-
logger.debug({ deletedPNSession: wireJid }, 'Deleted PN session')
|
|
676
|
-
}
|
|
677
|
-
}
|
|
678
|
-
catch (deleteError) {
|
|
679
|
-
logger.warn({ wireJid, error: deleteError }, 'Failed to delete PN session')
|
|
680
|
-
}
|
|
681
|
-
return lidWithDevice
|
|
682
|
-
}
|
|
683
|
-
catch (migrationError) {
|
|
684
|
-
logger.warn({ wireJid, error: migrationError }, 'Failed to migrate session')
|
|
685
|
-
return wireJid
|
|
686
|
-
}
|
|
687
|
-
}
|
|
688
|
-
catch (error) {
|
|
689
|
-
logger.debug({ wireJid, error }, 'Failed to check LID mapping')
|
|
690
|
-
return wireJid
|
|
570
|
+
if (isOwnUser && !isExactSenderDevice) {
|
|
571
|
+
msgToEncrypt = dsmMessage
|
|
572
|
+
logger.debug({ jid, targetUser }, 'Using DSM for own device')
|
|
691
573
|
}
|
|
692
574
|
}
|
|
693
575
|
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
let messageToEncrypt = patchedMessage
|
|
698
|
-
|
|
699
|
-
if (dsmMessage) {
|
|
700
|
-
const { user: targetUser } = jidDecode(wireJid)
|
|
701
|
-
const { user: ownPnUser } = jidDecode(meId)
|
|
702
|
-
const ownLidUser = meLidUser
|
|
703
|
-
|
|
704
|
-
// Check if this is our device (same user, different device)
|
|
705
|
-
const isOwnUser = targetUser === ownPnUser || (ownLidUser && targetUser === ownLidUser)
|
|
706
|
-
|
|
707
|
-
// Exclude exact sender device (whatsmeow: if jid == ownJID || jid == ownLID { continue })
|
|
708
|
-
const isExactSenderDevice = wireJid === meId || (authState.creds.me?.lid && wireJid === authState.creds.me.lid)
|
|
709
|
-
|
|
710
|
-
if (isOwnUser && !isExactSenderDevice) {
|
|
711
|
-
messageToEncrypt = dsmMessage
|
|
712
|
-
logger.debug({ wireJid, targetUser }, 'Using DSM for own device')
|
|
713
|
-
}
|
|
714
|
-
}
|
|
715
|
-
|
|
716
|
-
const bytes = encodeWAMessage(messageToEncrypt)
|
|
717
|
-
|
|
718
|
-
// Get encryption JID with LID migration
|
|
719
|
-
const encryptionJid = await getEncryptionJid(wireJid)
|
|
720
|
-
|
|
721
|
-
// ENCRYPT: Use the determined encryption identity (prefers migrated LID)
|
|
576
|
+
const bytes = encodeWAMessage(msgToEncrypt)
|
|
577
|
+
const mutexKey = jid
|
|
578
|
+
const node = await encryptionMutex.mutex(mutexKey, async () => {
|
|
722
579
|
const { type, ciphertext } = await signalRepository.encryptMessage({
|
|
723
|
-
jid
|
|
580
|
+
jid,
|
|
724
581
|
data: bytes
|
|
725
582
|
})
|
|
726
583
|
|
|
@@ -728,9 +585,9 @@ const makeMessagesSocket = (config) => {
|
|
|
728
585
|
shouldIncludeDeviceIdentity = true
|
|
729
586
|
}
|
|
730
587
|
|
|
731
|
-
|
|
588
|
+
return {
|
|
732
589
|
tag: 'to',
|
|
733
|
-
attrs: { jid
|
|
590
|
+
attrs: { jid },
|
|
734
591
|
content: [
|
|
735
592
|
{
|
|
736
593
|
tag: 'enc',
|
|
@@ -742,53 +599,71 @@ const makeMessagesSocket = (config) => {
|
|
|
742
599
|
content: ciphertext
|
|
743
600
|
}
|
|
744
601
|
]
|
|
745
|
-
}
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
602
|
+
};
|
|
603
|
+
})
|
|
604
|
+
|
|
605
|
+
return node
|
|
606
|
+
})
|
|
607
|
+
|
|
608
|
+
const nodes = (await Promise.all(encryptionPromises)).filter(node => node !== null)
|
|
751
609
|
|
|
752
|
-
// Wait for all users to complete (users are processed in parallel)
|
|
753
|
-
const userNodesArrays = await Promise.all(userEncryptionPromises)
|
|
754
|
-
const nodes = userNodesArrays.flat()
|
|
755
610
|
return { nodes, shouldIncludeDeviceIdentity }
|
|
756
611
|
}
|
|
612
|
+
|
|
613
|
+
/** Fetch image for groups, user, and newsletter **/
|
|
614
|
+
const profilePictureUrl = async (jid) => {
|
|
615
|
+
if (isJidNewsletter(jid)) {
|
|
616
|
+
const metadata = await suki.newsletterMetadata('JID', jid)
|
|
617
|
+
|
|
618
|
+
return getUrlFromDirectPath(metadata.thread_metadata.picture?.direct_path || '')
|
|
619
|
+
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
else {
|
|
623
|
+
const result = await query({
|
|
624
|
+
tag: 'iq',
|
|
625
|
+
attrs: {
|
|
626
|
+
target: jidNormalizedUser(jid),
|
|
627
|
+
to: S_WHATSAPP_NET,
|
|
628
|
+
type: 'get',
|
|
629
|
+
xmlns: 'w:profile:picture'
|
|
630
|
+
},
|
|
631
|
+
content: [{
|
|
632
|
+
tag: 'picture',
|
|
633
|
+
attrs: {
|
|
634
|
+
type: 'image',
|
|
635
|
+
query: 'url'
|
|
636
|
+
}
|
|
637
|
+
}]
|
|
638
|
+
})
|
|
639
|
+
|
|
640
|
+
const child = getBinaryNodeChild(result, 'picture')
|
|
641
|
+
|
|
642
|
+
return child?.attrs?.url || null
|
|
643
|
+
}
|
|
644
|
+
}
|
|
757
645
|
|
|
758
646
|
const relayMessage = async (jid, message, { messageId: msgId, participant, additionalAttributes, useUserDevicesCache, useCachedGroupMetadata, statusJidList, additionalNodes, AI = false }) => {
|
|
759
647
|
const meId = authState.creds.me.id
|
|
760
648
|
const meLid = authState.creds.me?.lid
|
|
761
|
-
|
|
649
|
+
const isRetryResend = Boolean(participant?.jid)
|
|
650
|
+
|
|
651
|
+
let shouldIncludeDeviceIdentity = isRetryResend
|
|
762
652
|
let didPushAdditional = false
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
const { user, server } = jidDecode(jid)
|
|
766
|
-
|
|
653
|
+
|
|
767
654
|
const statusJid = 'status@broadcast'
|
|
655
|
+
const { user, server } = jidDecode(jid)
|
|
768
656
|
const isGroup = server === 'g.us'
|
|
769
|
-
const isPrivate = server === 's.whatsapp.net'
|
|
770
|
-
const isNewsletter = server == 'newsletter'
|
|
771
657
|
const isStatus = jid === statusJid
|
|
772
658
|
const isLid = server === 'lid'
|
|
773
|
-
|
|
774
|
-
|
|
659
|
+
const isNewsletter = server === 'newsletter'
|
|
660
|
+
const isGroupOrStatus = isGroup || isStatus
|
|
775
661
|
const finalJid = jid
|
|
776
662
|
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
if (isLid && meLid) {
|
|
781
|
-
ownId = meLid
|
|
782
|
-
logger.debug({ to: jid, ownId }, 'Using LID identity for @lid conversation')
|
|
783
|
-
}
|
|
784
|
-
else {
|
|
785
|
-
logger.debug({ to: jid, ownId }, 'Using PN identity for @s.whatsapp.net conversation')
|
|
786
|
-
}
|
|
787
|
-
|
|
788
|
-
msgId = msgId || generateMessageID(authState.creds.me.id)
|
|
789
|
-
useUserDevicesCache = useUserDevicesCache !== false
|
|
663
|
+
msgId = msgId || generateMessageID(meId)
|
|
664
|
+
useUserDevicesCache = useUserDevicesCache !== false;
|
|
790
665
|
useCachedGroupMetadata = useCachedGroupMetadata !== false && !isStatus
|
|
791
|
-
|
|
666
|
+
|
|
792
667
|
const participants = []
|
|
793
668
|
const destinationJid = !isStatus ? finalJid : statusJid
|
|
794
669
|
const binaryNodeContent = []
|
|
@@ -799,7 +674,7 @@ const makeMessagesSocket = (config) => {
|
|
|
799
674
|
destinationJid,
|
|
800
675
|
message
|
|
801
676
|
},
|
|
802
|
-
messageContextInfo: message.messageContextInfo
|
|
677
|
+
messageContextInfo: message.messageContextInfo
|
|
803
678
|
}
|
|
804
679
|
|
|
805
680
|
const extraAttrs = {}
|
|
@@ -813,19 +688,16 @@ const makeMessagesSocket = (config) => {
|
|
|
813
688
|
|
|
814
689
|
|
|
815
690
|
if (participant) {
|
|
816
|
-
// when the retry request is not for a group
|
|
817
|
-
// only send to the specific device that asked for a retry
|
|
818
|
-
// otherwise the message is sent out to every device that should be a recipient
|
|
819
691
|
if (!isGroup && !isStatus) {
|
|
820
|
-
additionalAttributes = { ...additionalAttributes,
|
|
692
|
+
additionalAttributes = { ...additionalAttributes, device_fanout: 'false' }
|
|
821
693
|
}
|
|
822
|
-
|
|
694
|
+
|
|
823
695
|
const { user, device } = jidDecode(participant.jid)
|
|
824
|
-
|
|
696
|
+
|
|
825
697
|
devices.push({
|
|
826
698
|
user,
|
|
827
699
|
device,
|
|
828
|
-
|
|
700
|
+
jid: participant.jid
|
|
829
701
|
})
|
|
830
702
|
}
|
|
831
703
|
|
|
@@ -835,6 +707,34 @@ const makeMessagesSocket = (config) => {
|
|
|
835
707
|
if (mediaType) {
|
|
836
708
|
extraAttrs['mediatype'] = mediaType
|
|
837
709
|
}
|
|
710
|
+
|
|
711
|
+
if (isNewsletter) {
|
|
712
|
+
const patched = patchMessageBeforeSending ? await patchMessageBeforeSending(message, []) : message
|
|
713
|
+
const bytes = encodeNewsletterMessage(patched)
|
|
714
|
+
|
|
715
|
+
binaryNodeContent.push({
|
|
716
|
+
tag: 'plaintext',
|
|
717
|
+
attrs: {},
|
|
718
|
+
content: bytes
|
|
719
|
+
})
|
|
720
|
+
|
|
721
|
+
const stanza = {
|
|
722
|
+
tag: 'message',
|
|
723
|
+
attrs: {
|
|
724
|
+
to: jid,
|
|
725
|
+
id: msgId,
|
|
726
|
+
type: getTypeMessage(message),
|
|
727
|
+
...(additionalAttributes || {})
|
|
728
|
+
},
|
|
729
|
+
content: binaryNodeContent
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
logger.debug({ msgId }, `sending newsletter message to ${jid}`)
|
|
733
|
+
|
|
734
|
+
await sendNode(stanza)
|
|
735
|
+
|
|
736
|
+
return
|
|
737
|
+
}
|
|
838
738
|
|
|
839
739
|
if (messages.pinInChatMessage || messages.keepInChatMessage || message.reactionMessage || message.protocolMessage?.editedMessage) {
|
|
840
740
|
extraAttrs['decrypt-fail'] = 'hide'
|
|
@@ -844,153 +744,155 @@ const makeMessagesSocket = (config) => {
|
|
|
844
744
|
extraAttrs['native_flow_name'] = messages.interactiveResponseMessage.nativeFlowResponseMessage?.name || 'menu_options'
|
|
845
745
|
}
|
|
846
746
|
|
|
847
|
-
if (
|
|
747
|
+
if (isGroupOrStatus && !isRetryResend) {
|
|
848
748
|
const [groupData, senderKeyMap] = await Promise.all([
|
|
849
749
|
(async () => {
|
|
850
|
-
let groupData = useCachedGroupMetadata && cachedGroupMetadata ? await cachedGroupMetadata(jid) : undefined
|
|
851
|
-
|
|
750
|
+
let groupData = useCachedGroupMetadata && cachedGroupMetadata ? await cachedGroupMetadata(jid) : undefined // todo: should we rely on the cache specially if the cache is outdated and the metadata has new fields?
|
|
751
|
+
|
|
852
752
|
if (groupData && Array.isArray(groupData?.participants)) {
|
|
853
753
|
logger.trace({ jid, participants: groupData.participants.length }, 'using cached group metadata')
|
|
854
754
|
}
|
|
855
|
-
|
|
755
|
+
|
|
856
756
|
else if (!isStatus) {
|
|
857
|
-
groupData = await groupMetadata(jid)
|
|
757
|
+
groupData = await groupMetadata(jid) // TODO: start storing group participant list + addr mode in Signal & stop relying on this
|
|
858
758
|
}
|
|
859
|
-
|
|
759
|
+
|
|
860
760
|
return groupData
|
|
861
761
|
})(),
|
|
862
|
-
|
|
863
762
|
(async () => {
|
|
864
763
|
if (!participant && !isStatus) {
|
|
865
|
-
|
|
764
|
+
// what if sender memory is less accurate than the cached metadata
|
|
765
|
+
// on participant change in group, we should do sender memory manipulation
|
|
766
|
+
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?
|
|
767
|
+
|
|
866
768
|
return result[jid] || {}
|
|
867
769
|
}
|
|
868
|
-
|
|
770
|
+
|
|
869
771
|
return {}
|
|
870
|
-
|
|
871
772
|
})()
|
|
872
773
|
])
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
774
|
+
|
|
775
|
+
const participantsList = groupData ? groupData.participants.map(p => p.id) : []
|
|
776
|
+
|
|
777
|
+
if (groupData?.ephemeralDuration && groupData.ephemeralDuration > 0) {
|
|
778
|
+
additionalAttributes = {
|
|
779
|
+
...additionalAttributes,
|
|
780
|
+
expiration: groupData.ephemeralDuration.toString()
|
|
879
781
|
}
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
if (isStatus && statusJidList) {
|
|
785
|
+
participantsList.push(...statusJidList)
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
const additionalDevices = await getUSyncDevices(participantsList, !!useUserDevicesCache, false)
|
|
789
|
+
|
|
790
|
+
devices.push(...additionalDevices)
|
|
791
|
+
|
|
792
|
+
if (isGroup) {
|
|
793
|
+
additionalAttributes = {
|
|
794
|
+
...additionalAttributes,
|
|
795
|
+
addressing_mode: groupData?.addressingMode || 'lid'
|
|
887
796
|
}
|
|
888
|
-
|
|
889
|
-
const additionalDevices = await getUSyncDevices(participantsList, !!useUserDevicesCache, false)
|
|
890
|
-
devices.push(...additionalDevices)
|
|
891
797
|
}
|
|
892
|
-
|
|
893
|
-
const patched = await patchMessageBeforeSending(message, devices.map(d => jidEncode(d.user, isLid ? 'lid' : 's.whatsapp.net', d.device)))
|
|
894
|
-
const bytes = encodeWAMessage(patched)
|
|
895
798
|
|
|
896
|
-
|
|
897
|
-
|
|
799
|
+
const patched = await patchMessageBeforeSending(message)
|
|
800
|
+
|
|
801
|
+
if (Array.isArray(patched)) {
|
|
802
|
+
throw new Boom('Per-jid patching is not supported in groups')
|
|
803
|
+
}
|
|
804
|
+
|
|
805
|
+
const bytes = encodeWAMessage(patched)
|
|
806
|
+
const groupAddressingMode = additionalAttributes?.['addressing_mode'] || groupData?.addressingMode || 'lid'
|
|
898
807
|
const groupSenderIdentity = groupAddressingMode === 'lid' && meLid ? meLid : meId
|
|
899
|
-
|
|
900
808
|
const { ciphertext, senderKeyDistributionMessage } = await signalRepository.encryptGroupMessage({
|
|
901
809
|
group: destinationJid,
|
|
902
810
|
data: bytes,
|
|
903
811
|
meId: groupSenderIdentity
|
|
904
812
|
})
|
|
905
|
-
|
|
906
|
-
const
|
|
907
|
-
|
|
908
|
-
// ensure a connection is established with every device
|
|
813
|
+
|
|
814
|
+
const senderKeyRecipients = []
|
|
815
|
+
|
|
909
816
|
for (const device of devices) {
|
|
910
|
-
|
|
911
|
-
const deviceJid = device.wireJid
|
|
817
|
+
const deviceJid = device.jid
|
|
912
818
|
const hasKey = !!senderKeyMap[deviceJid]
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
819
|
+
|
|
820
|
+
if ((!hasKey || !!participant) &&
|
|
821
|
+
!isHostedLidUser(deviceJid) &&
|
|
822
|
+
!isHostedPnUser(deviceJid) &&
|
|
823
|
+
device.device !== 99) {
|
|
824
|
+
//todo: revamp all this logic
|
|
825
|
+
// 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
|
|
826
|
+
senderKeyRecipients.push(deviceJid)
|
|
916
827
|
senderKeyMap[deviceJid] = true
|
|
917
828
|
}
|
|
918
829
|
}
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
logger.debug({ senderKeyJids }, 'sending new sender key')
|
|
830
|
+
|
|
831
|
+
if (senderKeyRecipients.length) {
|
|
832
|
+
logger.debug({ senderKeyJids: senderKeyRecipients }, 'sending new sender key')
|
|
833
|
+
|
|
924
834
|
const senderKeyMsg = {
|
|
925
835
|
senderKeyDistributionMessage: {
|
|
926
836
|
axolotlSenderKeyDistributionMessage: senderKeyDistributionMessage,
|
|
927
837
|
groupId: destinationJid
|
|
928
838
|
}
|
|
929
839
|
}
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
840
|
+
|
|
841
|
+
const senderKeySessionTargets = senderKeyRecipients
|
|
842
|
+
|
|
843
|
+
await assertSessions(senderKeySessionTargets)
|
|
844
|
+
|
|
845
|
+
const result = await createParticipantNodes(senderKeyRecipients, senderKeyMsg, extraAttrs)
|
|
846
|
+
|
|
847
|
+
shouldIncludeDeviceIdentity = shouldIncludeDeviceIdentity || result.shouldIncludeDeviceIdentity;
|
|
937
848
|
participants.push(...result.nodes)
|
|
938
849
|
}
|
|
939
|
-
|
|
850
|
+
|
|
940
851
|
binaryNodeContent.push({
|
|
941
852
|
tag: 'enc',
|
|
942
|
-
attrs: { v: '2', type: 'skmsg', ...extraAttrs },
|
|
853
|
+
attrs: { v: '2', type: 'skmsg', ...extraAttrs },
|
|
943
854
|
content: ciphertext
|
|
944
855
|
})
|
|
945
|
-
|
|
856
|
+
|
|
946
857
|
await authState.keys.set({ 'sender-key-memory': { [jid]: senderKeyMap } })
|
|
947
858
|
}
|
|
948
859
|
|
|
949
|
-
else
|
|
950
|
-
//
|
|
951
|
-
if
|
|
952
|
-
|
|
953
|
-
|
|
860
|
+
else {
|
|
861
|
+
// ADDRESSING CONSISTENCY: Match own identity to conversation context
|
|
862
|
+
// TODO: investigate if this is true
|
|
863
|
+
let ownId = meId
|
|
864
|
+
|
|
865
|
+
if (isLid && meLid) {
|
|
866
|
+
ownId = meLid;
|
|
867
|
+
logger.debug({ to: jid, ownId }, 'Using LID identity for @lid conversation')
|
|
954
868
|
}
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
msgId = message.protocolMessage.key?.id
|
|
959
|
-
message = {}
|
|
869
|
+
|
|
870
|
+
else {
|
|
871
|
+
logger.debug({ to: jid, ownId }, 'Using PN identity for @s.whatsapp.net conversation')
|
|
960
872
|
}
|
|
961
|
-
|
|
962
|
-
const patched = await patchMessageBeforeSending(message, [])
|
|
963
|
-
const bytes = encodeNewsletterMessage(patched)
|
|
964
|
-
|
|
965
|
-
binaryNodeContent.push({
|
|
966
|
-
tag: 'plaintext',
|
|
967
|
-
attrs: extraAttrs,
|
|
968
|
-
content: bytes
|
|
969
|
-
})
|
|
970
|
-
}
|
|
971
|
-
|
|
972
|
-
else {
|
|
873
|
+
|
|
973
874
|
const { user: ownUser } = jidDecode(ownId)
|
|
974
|
-
|
|
975
|
-
if (!
|
|
875
|
+
|
|
876
|
+
if (!isRetryResend) {
|
|
976
877
|
const targetUserServer = isLid ? 'lid' : 's.whatsapp.net'
|
|
878
|
+
|
|
977
879
|
devices.push({
|
|
978
880
|
user,
|
|
979
881
|
device: 0,
|
|
980
|
-
|
|
882
|
+
jid: jidEncode(user, targetUserServer, 0) // rajeh, todo: this entire logic is convoluted and weird.
|
|
981
883
|
})
|
|
982
884
|
|
|
983
|
-
// Own user matches conversation addressing mode
|
|
984
885
|
if (user !== ownUser) {
|
|
985
|
-
const ownUserServer = isLid ? 'lid' : 's.whatsapp.net'
|
|
886
|
+
const ownUserServer = isLid ? 'lid' : 's.whatsapp.net'
|
|
986
887
|
const ownUserForAddressing = isLid && meLid ? jidDecode(meLid).user : jidDecode(meId).user
|
|
888
|
+
|
|
987
889
|
devices.push({
|
|
988
890
|
user: ownUserForAddressing,
|
|
989
891
|
device: 0,
|
|
990
|
-
|
|
892
|
+
jid: jidEncode(ownUserForAddressing, ownUserServer, 0)
|
|
991
893
|
})
|
|
992
894
|
}
|
|
993
|
-
|
|
895
|
+
|
|
994
896
|
if (additionalAttributes?.['category'] !== 'peer') {
|
|
995
897
|
// Clear placeholders and enumerate actual devices
|
|
996
898
|
devices.length = 0
|
|
@@ -999,89 +901,116 @@ const makeMessagesSocket = (config) => {
|
|
|
999
901
|
const senderIdentity = isLid && meLid
|
|
1000
902
|
? jidEncode(jidDecode(meLid)?.user, 'lid', undefined)
|
|
1001
903
|
: jidEncode(jidDecode(meId)?.user, 's.whatsapp.net', undefined)
|
|
1002
|
-
|
|
1003
904
|
// Enumerate devices for sender and target with consistent addressing
|
|
1004
|
-
const sessionDevices = await getUSyncDevices([senderIdentity, jid],
|
|
905
|
+
const sessionDevices = await getUSyncDevices([senderIdentity, jid], true, false)
|
|
906
|
+
|
|
1005
907
|
devices.push(...sessionDevices)
|
|
908
|
+
|
|
1006
909
|
logger.debug({
|
|
1007
910
|
deviceCount: devices.length,
|
|
1008
|
-
devices: devices.map(d => `${d.user}:${d.device}@${jidDecode(d.
|
|
911
|
+
devices: devices.map(d => `${d.user}:${d.device}@${jidDecode(d.jid)?.server}`)
|
|
1009
912
|
}, 'Device enumeration complete with unified addressing')
|
|
1010
913
|
}
|
|
1011
914
|
}
|
|
1012
|
-
|
|
1013
|
-
const
|
|
1014
|
-
const
|
|
1015
|
-
const
|
|
915
|
+
|
|
916
|
+
const allRecipients = []
|
|
917
|
+
const meRecipients = []
|
|
918
|
+
const otherRecipients = []
|
|
1016
919
|
|
|
1017
920
|
const { user: mePnUser } = jidDecode(meId)
|
|
1018
921
|
const { user: meLidUser } = meLid ? jidDecode(meLid) : { user: null }
|
|
1019
|
-
|
|
1020
|
-
for (const { user,
|
|
1021
|
-
const isExactSenderDevice =
|
|
922
|
+
|
|
923
|
+
for (const { user, jid } of devices) {
|
|
924
|
+
const isExactSenderDevice = jid === meId || (meLid && jid === meLid)
|
|
925
|
+
|
|
1022
926
|
if (isExactSenderDevice) {
|
|
1023
|
-
logger.debug({
|
|
927
|
+
logger.debug({ jid, meId, meLid }, 'Skipping exact sender device (whatsmeow pattern)')
|
|
1024
928
|
continue
|
|
1025
929
|
}
|
|
1026
930
|
|
|
1027
931
|
// Check if this is our device (could match either PN or LID user)
|
|
1028
|
-
const isMe = user === mePnUser ||
|
|
1029
|
-
|
|
1030
|
-
|
|
932
|
+
const isMe = user === mePnUser || user === meLidUser
|
|
933
|
+
|
|
1031
934
|
if (isMe) {
|
|
1032
|
-
|
|
935
|
+
meRecipients.push(jid);
|
|
1033
936
|
}
|
|
1034
|
-
|
|
937
|
+
|
|
1035
938
|
else {
|
|
1036
|
-
|
|
939
|
+
otherRecipients.push(jid)
|
|
1037
940
|
}
|
|
1038
|
-
|
|
1039
|
-
|
|
941
|
+
|
|
942
|
+
allRecipients.push(jid)
|
|
1040
943
|
}
|
|
1041
|
-
|
|
1042
|
-
await assertSessions(
|
|
1043
|
-
|
|
944
|
+
|
|
945
|
+
await assertSessions(allRecipients)
|
|
946
|
+
|
|
1044
947
|
const [{ nodes: meNodes, shouldIncludeDeviceIdentity: s1 }, { nodes: otherNodes, shouldIncludeDeviceIdentity: s2 }] = await Promise.all([
|
|
1045
948
|
// For own devices: use DSM if available (1:1 chats only)
|
|
1046
|
-
createParticipantNodes(
|
|
1047
|
-
createParticipantNodes(
|
|
949
|
+
createParticipantNodes(meRecipients, meMsg || message, extraAttrs),
|
|
950
|
+
createParticipantNodes(otherRecipients, message, extraAttrs, meMsg)
|
|
1048
951
|
])
|
|
1049
|
-
|
|
952
|
+
|
|
1050
953
|
participants.push(...meNodes)
|
|
1051
|
-
|
|
1052
954
|
participants.push(...otherNodes)
|
|
1053
955
|
|
|
1054
|
-
if (
|
|
1055
|
-
extraAttrs['phash'] = generateParticipantHashV2([...
|
|
956
|
+
if (meRecipients.length > 0 || otherRecipients.length > 0) {
|
|
957
|
+
extraAttrs['phash'] = generateParticipantHashV2([...meRecipients, ...otherRecipients])
|
|
1056
958
|
}
|
|
1057
|
-
|
|
959
|
+
|
|
1058
960
|
shouldIncludeDeviceIdentity = shouldIncludeDeviceIdentity || s1 || s2
|
|
1059
961
|
}
|
|
1060
|
-
|
|
962
|
+
|
|
963
|
+
if (isRetryResend) {
|
|
964
|
+
const isParticipantLid = isLidUser(participant.jid)
|
|
965
|
+
const isMe = areJidsSameUser(participant.jid, isParticipantLid ? meLid : meId)
|
|
966
|
+
const encodedMessageToSend = isMe
|
|
967
|
+
? encodeWAMessage({
|
|
968
|
+
deviceSentMessage: {
|
|
969
|
+
destinationJid,
|
|
970
|
+
message
|
|
971
|
+
}
|
|
972
|
+
})
|
|
973
|
+
: encodeWAMessage(message)
|
|
974
|
+
const { type, ciphertext: encryptedContent } = await signalRepository.encryptMessage({
|
|
975
|
+
data: encodedMessageToSend,
|
|
976
|
+
jid: participant.jid
|
|
977
|
+
})
|
|
978
|
+
|
|
979
|
+
binaryNodeContent.push({
|
|
980
|
+
tag: 'enc',
|
|
981
|
+
attrs: {
|
|
982
|
+
v: '2',
|
|
983
|
+
type,
|
|
984
|
+
count: participant.count.toString()
|
|
985
|
+
},
|
|
986
|
+
content: encryptedContent
|
|
987
|
+
})
|
|
988
|
+
}
|
|
989
|
+
|
|
1061
990
|
if (participants.length) {
|
|
1062
991
|
if (additionalAttributes?.['category'] === 'peer') {
|
|
1063
992
|
const peerNode = participants[0]?.content?.[0]
|
|
1064
|
-
|
|
993
|
+
|
|
1065
994
|
if (peerNode) {
|
|
1066
995
|
binaryNodeContent.push(peerNode) // push only enc
|
|
1067
996
|
}
|
|
1068
997
|
}
|
|
1069
|
-
|
|
998
|
+
|
|
1070
999
|
else {
|
|
1071
1000
|
binaryNodeContent.push({
|
|
1072
1001
|
tag: 'participants',
|
|
1073
1002
|
attrs: {},
|
|
1074
1003
|
content: participants
|
|
1075
|
-
})
|
|
1004
|
+
});
|
|
1076
1005
|
}
|
|
1077
1006
|
}
|
|
1078
|
-
|
|
1007
|
+
|
|
1079
1008
|
const stanza = {
|
|
1080
1009
|
tag: 'message',
|
|
1081
1010
|
attrs: {
|
|
1011
|
+
id: msgId,
|
|
1082
1012
|
to: destinationJid,
|
|
1083
|
-
|
|
1084
|
-
type: getTypeMessage(message),
|
|
1013
|
+
type: getTypeMessage(message),
|
|
1085
1014
|
...(additionalAttributes || {})
|
|
1086
1015
|
},
|
|
1087
1016
|
content: binaryNodeContent
|
|
@@ -1095,30 +1024,41 @@ const makeMessagesSocket = (config) => {
|
|
|
1095
1024
|
stanza.attrs.to = destinationJid
|
|
1096
1025
|
stanza.attrs.participant = participant.jid
|
|
1097
1026
|
}
|
|
1098
|
-
|
|
1027
|
+
|
|
1099
1028
|
else if (areJidsSameUser(participant.jid, meId)) {
|
|
1100
1029
|
stanza.attrs.to = participant.jid
|
|
1101
1030
|
stanza.attrs.recipient = destinationJid
|
|
1102
1031
|
}
|
|
1103
|
-
|
|
1032
|
+
|
|
1104
1033
|
else {
|
|
1105
1034
|
stanza.attrs.to = participant.jid
|
|
1106
1035
|
}
|
|
1107
1036
|
}
|
|
1108
|
-
|
|
1037
|
+
|
|
1109
1038
|
else {
|
|
1110
1039
|
stanza.attrs.to = destinationJid
|
|
1111
1040
|
}
|
|
1112
|
-
|
|
1041
|
+
|
|
1113
1042
|
if (shouldIncludeDeviceIdentity) {
|
|
1114
1043
|
stanza.content.push({
|
|
1115
1044
|
tag: 'device-identity',
|
|
1116
1045
|
attrs: {},
|
|
1117
1046
|
content: encodeSignedDeviceIdentity(authState.creds.account, true)
|
|
1118
1047
|
})
|
|
1119
|
-
|
|
1048
|
+
|
|
1120
1049
|
logger.debug({ jid }, 'adding device identity')
|
|
1121
1050
|
}
|
|
1051
|
+
|
|
1052
|
+
const contactTcTokenData = !isGroup && !isRetryResend && !isStatus ? await authState.keys.get('tctoken', [destinationJid]) : {}
|
|
1053
|
+
const tcTokenBuffer = contactTcTokenData[destinationJid]?.token
|
|
1054
|
+
|
|
1055
|
+
if (tcTokenBuffer) {
|
|
1056
|
+
stanza.content.push({
|
|
1057
|
+
tag: 'tctoken',
|
|
1058
|
+
attrs: {},
|
|
1059
|
+
content: tcTokenBuffer
|
|
1060
|
+
})
|
|
1061
|
+
}
|
|
1122
1062
|
|
|
1123
1063
|
if (isGroup && regexGroupOld.test(jid) && !message.reactionMessage) {
|
|
1124
1064
|
stanza.content.push({
|
|
@@ -1449,11 +1389,11 @@ const makeMessagesSocket = (config) => {
|
|
|
1449
1389
|
...suki,
|
|
1450
1390
|
getPrivacyTokens,
|
|
1451
1391
|
assertSessions,
|
|
1392
|
+
profilePictureUrl,
|
|
1452
1393
|
relayMessage,
|
|
1453
1394
|
sendReceipt,
|
|
1454
1395
|
sendReceipts,
|
|
1455
1396
|
readMessages,
|
|
1456
|
-
profilePictureUrl,
|
|
1457
1397
|
getUSyncDevices,
|
|
1458
1398
|
refreshMediaConn,
|
|
1459
1399
|
waUploadToServer,
|
|
@@ -1462,57 +1402,59 @@ const makeMessagesSocket = (config) => {
|
|
|
1462
1402
|
messageRetryManager,
|
|
1463
1403
|
createParticipantNodes,
|
|
1464
1404
|
sendPeerDataOperationMessage,
|
|
1405
|
+
updateMemberLabel,
|
|
1465
1406
|
updateMediaMessage: async (message) => {
|
|
1466
1407
|
const content = assertMediaContent(message.message)
|
|
1467
1408
|
const mediaKey = content.mediaKey
|
|
1468
1409
|
const meId = authState.creds.me.id
|
|
1469
1410
|
const node = await encryptMediaRetryRequest(message.key, mediaKey, meId)
|
|
1411
|
+
|
|
1470
1412
|
let error = undefined
|
|
1471
|
-
|
|
1413
|
+
|
|
1472
1414
|
await Promise.all([
|
|
1473
1415
|
sendNode(node),
|
|
1474
1416
|
waitForMsgMediaUpdate(async (update) => {
|
|
1475
1417
|
const result = update.find(c => c.key.id === message.key.id)
|
|
1418
|
+
|
|
1476
1419
|
if (result) {
|
|
1477
1420
|
if (result.error) {
|
|
1478
1421
|
error = result.error
|
|
1479
1422
|
}
|
|
1480
|
-
|
|
1423
|
+
|
|
1481
1424
|
else {
|
|
1482
1425
|
try {
|
|
1483
1426
|
const media = await decryptMediaRetryData(result.media, mediaKey, result.key.id)
|
|
1484
|
-
|
|
1427
|
+
|
|
1485
1428
|
if (media.result !== proto.MediaRetryNotification.ResultType.SUCCESS) {
|
|
1486
1429
|
const resultStr = proto.MediaRetryNotification.ResultType[media.result]
|
|
1487
|
-
|
|
1488
|
-
throw new Boom(`Media re-upload failed by device (${resultStr})`, {
|
|
1430
|
+
|
|
1431
|
+
throw new Boom(`Media re-upload failed by device (${resultStr})`, {
|
|
1432
|
+
data: media,
|
|
1433
|
+
statusCode: getStatusCodeForMediaRetry(media.result) || 404
|
|
1434
|
+
})
|
|
1489
1435
|
}
|
|
1490
|
-
|
|
1436
|
+
|
|
1491
1437
|
content.directPath = media.directPath
|
|
1492
|
-
|
|
1493
1438
|
content.url = getUrlFromDirectPath(content.directPath)
|
|
1494
|
-
|
|
1495
1439
|
logger.debug({ directPath: media.directPath, key: result.key }, 'media update successful')
|
|
1496
1440
|
}
|
|
1497
|
-
|
|
1441
|
+
|
|
1498
1442
|
catch (err) {
|
|
1499
1443
|
error = err
|
|
1500
1444
|
}
|
|
1501
1445
|
}
|
|
1502
|
-
|
|
1446
|
+
|
|
1503
1447
|
return true
|
|
1504
1448
|
}
|
|
1505
1449
|
})
|
|
1506
1450
|
])
|
|
1507
|
-
|
|
1451
|
+
|
|
1508
1452
|
if (error) {
|
|
1509
1453
|
throw error
|
|
1510
1454
|
}
|
|
1511
|
-
|
|
1512
|
-
ev.emit('messages.update', [
|
|
1513
|
-
|
|
1514
|
-
])
|
|
1515
|
-
|
|
1455
|
+
|
|
1456
|
+
ev.emit('messages.update', [{ key: message.key, update: { message: message.message } }])
|
|
1457
|
+
|
|
1516
1458
|
return message
|
|
1517
1459
|
},
|
|
1518
1460
|
sendStatusMentions: async (content, jids = []) => {
|
|
@@ -1522,7 +1464,7 @@ const makeMessagesSocket = (config) => {
|
|
|
1522
1464
|
|
|
1523
1465
|
for (const id of jids) {
|
|
1524
1466
|
const isGroup = isJidGroup(id)
|
|
1525
|
-
const isPrivate =
|
|
1467
|
+
const isPrivate = isPnUser(id)
|
|
1526
1468
|
|
|
1527
1469
|
if (isGroup) {
|
|
1528
1470
|
try {
|
|
@@ -1578,7 +1520,10 @@ const makeMessagesSocket = (config) => {
|
|
|
1578
1520
|
userJid,
|
|
1579
1521
|
getUrlInfo: text => getUrlInfo(text, {
|
|
1580
1522
|
thumbnailWidth: linkPreviewImageThumbnailWidth,
|
|
1581
|
-
fetchOpts: {
|
|
1523
|
+
fetchOpts: {
|
|
1524
|
+
timeout: 3000,
|
|
1525
|
+
...(httpRequestOptions || {})
|
|
1526
|
+
},
|
|
1582
1527
|
logger,
|
|
1583
1528
|
uploadImage: generateHighQualityLinkPreview ? waUploadToServer : undefined
|
|
1584
1529
|
}),
|
|
@@ -1621,7 +1566,7 @@ const makeMessagesSocket = (config) => {
|
|
|
1621
1566
|
for (const id of jids) {
|
|
1622
1567
|
try {
|
|
1623
1568
|
const normalizedId = jidNormalizedUser(id)
|
|
1624
|
-
const isPrivate =
|
|
1569
|
+
const isPrivate = isPnUser(normalizedId)
|
|
1625
1570
|
const type = isPrivate ? 'statusMentionMessage' : 'groupStatusMentionMessage'
|
|
1626
1571
|
|
|
1627
1572
|
const protocolMessage = {
|
|
@@ -1716,8 +1661,8 @@ const makeMessagesSocket = (config) => {
|
|
|
1716
1661
|
getUrlInfo: text => getUrlInfo(text, {
|
|
1717
1662
|
thumbnailWidth: linkPreviewImageThumbnailWidth,
|
|
1718
1663
|
fetchOpts: {
|
|
1719
|
-
|
|
1720
|
-
...
|
|
1664
|
+
timeout: 3000,
|
|
1665
|
+
...(httpRequestOptions || {})
|
|
1721
1666
|
},
|
|
1722
1667
|
logger,
|
|
1723
1668
|
uploadImage: generateHighQualityLinkPreview
|
|
@@ -1772,8 +1717,8 @@ const makeMessagesSocket = (config) => {
|
|
|
1772
1717
|
await relayMessage(jid, fullMsg.message, { messageId: fullMsg.key.id, useCachedGroupMetadata: options.useCachedGroupMetadata, additionalAttributes, statusJidList: options.statusJidList, additionalNodes: options.additionalNodes, AI: options.ai })
|
|
1773
1718
|
|
|
1774
1719
|
if (config.emitOwnEvents) {
|
|
1775
|
-
process.nextTick(() => {
|
|
1776
|
-
|
|
1720
|
+
process.nextTick(async () => {
|
|
1721
|
+
await messageMutex.mutex(() => upsertMessage(fullMsg, 'append'))
|
|
1777
1722
|
})
|
|
1778
1723
|
}
|
|
1779
1724
|
|