@periskope/baileys 7.0.0-alpha-8 → 7.0.0-alpha-10
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 +3 -0
- package/WAProto/GenerateStatics.sh +2 -2
- package/WAProto/index.d.ts +2793 -45578
- package/WAProto/index.js +19344 -147065
- package/lib/Defaults/index.js +1 -1
- package/lib/Defaults/index.js.map +1 -1
- package/lib/Signal/Group/group_cipher.d.ts +1 -1
- package/lib/Signal/Group/group_cipher.d.ts.map +1 -1
- package/lib/Signal/Group/group_cipher.js +1 -4
- package/lib/Signal/Group/group_cipher.js.map +1 -1
- package/lib/Signal/Group/sender-chain-key.d.ts.map +1 -1
- package/lib/Signal/Group/sender-chain-key.js +1 -13
- package/lib/Signal/Group/sender-chain-key.js.map +1 -1
- package/lib/Signal/Group/sender-key-distribution-message.d.ts.map +1 -1
- package/lib/Signal/Group/sender-key-distribution-message.js +6 -11
- package/lib/Signal/Group/sender-key-distribution-message.js.map +1 -1
- package/lib/Signal/Group/sender-key-message.d.ts.map +1 -1
- package/lib/Signal/Group/sender-key-message.js +3 -5
- package/lib/Signal/Group/sender-key-message.js.map +1 -1
- package/lib/Signal/Group/sender-key-record.d.ts +1 -1
- package/lib/Signal/Group/sender-key-record.d.ts.map +1 -1
- package/lib/Signal/Group/sender-key-record.js +2 -11
- package/lib/Signal/Group/sender-key-record.js.map +1 -1
- package/lib/Signal/Group/sender-key-state.d.ts.map +1 -1
- package/lib/Signal/Group/sender-key-state.js +9 -52
- package/lib/Signal/Group/sender-key-state.js.map +1 -1
- package/lib/Signal/libsignal.d.ts +2 -5
- package/lib/Signal/libsignal.d.ts.map +1 -1
- package/lib/Signal/libsignal.js +109 -88
- package/lib/Signal/libsignal.js.map +1 -1
- package/lib/Signal/lid-mapping.d.ts +3 -6
- package/lib/Signal/lid-mapping.d.ts.map +1 -1
- package/lib/Signal/lid-mapping.js +19 -33
- package/lib/Signal/lid-mapping.js.map +1 -1
- package/lib/Socket/business.d.ts +2 -2
- package/lib/Socket/communities.d.ts +2 -2
- package/lib/Socket/communities.d.ts.map +1 -1
- package/lib/Socket/communities.js +1 -1
- package/lib/Socket/communities.js.map +1 -1
- package/lib/Socket/groups.d.ts.map +1 -1
- package/lib/Socket/groups.js +1 -1
- package/lib/Socket/groups.js.map +1 -1
- package/lib/Socket/index.d.ts +2 -2
- package/lib/Socket/messages-recv.d.ts +2 -2
- package/lib/Socket/messages-recv.d.ts.map +1 -1
- package/lib/Socket/messages-recv.js +12 -33
- package/lib/Socket/messages-recv.js.map +1 -1
- package/lib/Socket/messages-send.d.ts +2 -2
- package/lib/Socket/messages-send.d.ts.map +1 -1
- package/lib/Socket/messages-send.js +157 -288
- package/lib/Socket/messages-send.js.map +1 -1
- package/lib/Socket/socket.d.ts.map +1 -1
- package/lib/Socket/socket.js +34 -12
- package/lib/Socket/socket.js.map +1 -1
- package/lib/Types/Auth.d.ts +1 -0
- package/lib/Types/Auth.d.ts.map +1 -1
- package/lib/Types/Message.d.ts +1 -0
- package/lib/Types/Message.d.ts.map +1 -1
- package/lib/Types/Message.js.map +1 -1
- package/lib/Types/Signal.d.ts +1 -11
- package/lib/Types/Signal.d.ts.map +1 -1
- package/lib/Types/Socket.d.ts +1 -1
- package/lib/Types/Socket.d.ts.map +1 -1
- package/lib/Utils/auth-utils.js +1 -1
- package/lib/Utils/auth-utils.js.map +1 -1
- package/lib/Utils/browser-utils.d.ts +4 -0
- package/lib/Utils/browser-utils.d.ts.map +1 -0
- package/lib/Utils/browser-utils.js +28 -0
- package/lib/Utils/browser-utils.js.map +1 -0
- package/lib/Utils/chat-utils.d.ts.map +1 -1
- package/lib/Utils/chat-utils.js +7 -6
- package/lib/Utils/chat-utils.js.map +1 -1
- package/lib/Utils/decode-wa-message.d.ts.map +1 -1
- package/lib/Utils/decode-wa-message.js +20 -6
- package/lib/Utils/decode-wa-message.js.map +1 -1
- package/lib/Utils/generics.d.ts +1 -3
- package/lib/Utils/generics.d.ts.map +1 -1
- package/lib/Utils/generics.js +11 -29
- package/lib/Utils/generics.js.map +1 -1
- package/lib/Utils/history.d.ts.map +1 -1
- package/lib/Utils/history.js +2 -1
- package/lib/Utils/history.js.map +1 -1
- package/lib/Utils/index.d.ts +2 -0
- package/lib/Utils/index.d.ts.map +1 -1
- package/lib/Utils/index.js +2 -0
- package/lib/Utils/index.js.map +1 -1
- package/lib/Utils/messages-media.d.ts.map +1 -1
- package/lib/Utils/messages-media.js +2 -1
- package/lib/Utils/messages-media.js.map +1 -1
- package/lib/Utils/messages.d.ts.map +1 -1
- package/lib/Utils/messages.js +20 -16
- package/lib/Utils/messages.js.map +1 -1
- package/lib/Utils/noise-handler.d.ts.map +1 -1
- package/lib/Utils/noise-handler.js +2 -1
- package/lib/Utils/noise-handler.js.map +1 -1
- package/lib/Utils/process-message.d.ts.map +1 -1
- package/lib/Utils/process-message.js +8 -2
- package/lib/Utils/process-message.js.map +1 -1
- package/lib/Utils/proto-utils.d.ts +7 -0
- package/lib/Utils/proto-utils.d.ts.map +1 -0
- package/lib/Utils/proto-utils.js +7 -0
- package/lib/Utils/proto-utils.js.map +1 -0
- package/lib/Utils/signal.d.ts.map +1 -1
- package/lib/Utils/signal.js.map +1 -1
- package/lib/Utils/use-multi-file-auth-state.js +1 -1
- package/lib/Utils/use-multi-file-auth-state.js.map +1 -1
- package/lib/Utils/validate-connection.d.ts.map +1 -1
- package/lib/Utils/validate-connection.js +7 -6
- package/lib/Utils/validate-connection.js.map +1 -1
- package/lib/WABinary/generic-utils.d.ts.map +1 -1
- package/lib/WABinary/generic-utils.js +2 -1
- package/lib/WABinary/generic-utils.js.map +1 -1
- package/package.json +1 -1
|
@@ -18,6 +18,10 @@ export const makeMessagesSocket = (config) => {
|
|
|
18
18
|
stdTTL: DEFAULT_CACHE_TTLS.USER_DEVICES, // 5 minutes
|
|
19
19
|
useClones: false
|
|
20
20
|
});
|
|
21
|
+
const peerSessionsCache = new NodeCache({
|
|
22
|
+
stdTTL: DEFAULT_CACHE_TTLS.USER_DEVICES,
|
|
23
|
+
useClones: false
|
|
24
|
+
});
|
|
21
25
|
// Initialize message retry manager if enabled
|
|
22
26
|
const messageRetryManager = enableRecentMessageCache ? new MessageRetryManager(logger, maxMsgRetryCount) : null;
|
|
23
27
|
// Prevent race conditions in Signal session encryption by user
|
|
@@ -113,33 +117,28 @@ export const makeMessagesSocket = (config) => {
|
|
|
113
117
|
const readType = privacySettings.readreceipts === 'all' ? 'read' : 'read-self';
|
|
114
118
|
await sendReceipts(keys, readType);
|
|
115
119
|
};
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
const deduplicateLidPnJids = (jids) => {
|
|
121
|
-
const lidUsers = new Set();
|
|
122
|
-
const filteredJids = [];
|
|
123
|
-
// Collect all LID users
|
|
124
|
-
for (const jid of jids) {
|
|
125
|
-
if (jid.includes('@lid')) {
|
|
126
|
-
const user = jidDecode(jid)?.user;
|
|
127
|
-
if (user)
|
|
128
|
-
lidUsers.add(user);
|
|
129
|
-
}
|
|
120
|
+
const resolveSessionJids = async (jids) => {
|
|
121
|
+
const uniquePnJids = Array.from(new Set(jids.filter(isPnUser)));
|
|
122
|
+
if (!uniquePnJids.length) {
|
|
123
|
+
return new Map();
|
|
130
124
|
}
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
125
|
+
const lookups = await Promise.all(uniquePnJids.map(async (pnJid) => {
|
|
126
|
+
try {
|
|
127
|
+
const resolved = await signalRepository.lidMapping.getLIDForPN(pnJid);
|
|
128
|
+
return resolved ? [pnJid, resolved] : [pnJid, pnJid];
|
|
129
|
+
}
|
|
130
|
+
catch (error) {
|
|
131
|
+
logger.warn({ pnJid, error }, 'Failed to resolve LID mapping for PN JID');
|
|
132
|
+
return [pnJid, pnJid];
|
|
133
|
+
}
|
|
134
|
+
}));
|
|
135
|
+
const sessionMap = new Map();
|
|
136
|
+
for (const entry of lookups) {
|
|
137
|
+
if (entry) {
|
|
138
|
+
sessionMap.set(entry[0], entry[1]);
|
|
139
139
|
}
|
|
140
|
-
filteredJids.push(jid);
|
|
141
140
|
}
|
|
142
|
-
return
|
|
141
|
+
return sessionMap;
|
|
143
142
|
};
|
|
144
143
|
/** Fetch all the devices we've to send a message to */
|
|
145
144
|
const getUSyncDevices = async (jids, useCache, ignoreZeroDevices) => {
|
|
@@ -148,7 +147,6 @@ export const makeMessagesSocket = (config) => {
|
|
|
148
147
|
logger.debug('not using cache for devices');
|
|
149
148
|
}
|
|
150
149
|
const toFetch = [];
|
|
151
|
-
jids = deduplicateLidPnJids(Array.from(new Set(jids)));
|
|
152
150
|
const jidsWithUser = jids
|
|
153
151
|
.map(jid => {
|
|
154
152
|
const decoded = jidDecode(jid);
|
|
@@ -246,139 +244,49 @@ export const makeMessagesSocket = (config) => {
|
|
|
246
244
|
await userDevicesCache.set(key, deviceMap[key]);
|
|
247
245
|
}
|
|
248
246
|
}
|
|
247
|
+
const userDeviceUpdates = {};
|
|
248
|
+
for (const [userId, devices] of Object.entries(deviceMap)) {
|
|
249
|
+
if (devices && devices.length > 0) {
|
|
250
|
+
userDeviceUpdates[userId] = devices.map(d => d.device?.toString() || '0');
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
if (Object.keys(userDeviceUpdates).length > 0) {
|
|
254
|
+
try {
|
|
255
|
+
await authState.keys.set({ 'device-list': userDeviceUpdates });
|
|
256
|
+
logger.debug({ userCount: Object.keys(userDeviceUpdates).length }, 'stored user device lists for bulk migration');
|
|
257
|
+
}
|
|
258
|
+
catch (error) {
|
|
259
|
+
logger.warn({ error }, 'failed to store user device lists');
|
|
260
|
+
}
|
|
261
|
+
}
|
|
249
262
|
}
|
|
250
263
|
return deviceResults;
|
|
251
264
|
};
|
|
252
|
-
const assertSessions = async (jids
|
|
265
|
+
const assertSessions = async (jids) => {
|
|
253
266
|
let didFetchNewSession = false;
|
|
267
|
+
const uniqueJids = [...new Set(jids)]; // Deduplicate JIDs
|
|
254
268
|
const jidsRequiringFetch = [];
|
|
255
|
-
//
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
const checkJidSession = (jid) => {
|
|
263
|
-
const signalId = signalRepository.jidToSignalProtocolAddress(jid);
|
|
264
|
-
const hasSession = !!sessions[signalId];
|
|
265
|
-
// Add to fetch list if no session exists
|
|
266
|
-
// Session type selection (LID vs PN) is handled in encryptMessage
|
|
267
|
-
if (!hasSession) {
|
|
268
|
-
if (jid.includes('@lid')) {
|
|
269
|
-
logger.debug({ jid }, 'No LID session found, will create new LID session');
|
|
270
|
-
}
|
|
271
|
-
jidsRequiringFetch.push(jid);
|
|
272
|
-
}
|
|
273
|
-
};
|
|
274
|
-
// Process all JIDs
|
|
275
|
-
for (const jid of jids) {
|
|
276
|
-
checkJidSession(jid);
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
|
-
else {
|
|
280
|
-
const addrs = jids.map(jid => signalRepository.jidToSignalProtocolAddress(jid));
|
|
281
|
-
const sessions = await authState.keys.get('session', addrs);
|
|
282
|
-
// Group JIDs by user for bulk migration
|
|
283
|
-
const userGroups = new Map();
|
|
284
|
-
for (const jid of jids) {
|
|
285
|
-
const user = jidNormalizedUser(jid);
|
|
286
|
-
if (!userGroups.has(user)) {
|
|
287
|
-
userGroups.set(user, []);
|
|
269
|
+
// Check peerSessionsCache and authState.keys
|
|
270
|
+
for (const jid of uniqueJids) {
|
|
271
|
+
const signalId = signalRepository.jidToSignalProtocolAddress(jid);
|
|
272
|
+
const cachedSession = peerSessionsCache.get(signalId);
|
|
273
|
+
if (cachedSession !== undefined) {
|
|
274
|
+
if (cachedSession) {
|
|
275
|
+
continue; // Session exists in cache
|
|
288
276
|
}
|
|
289
|
-
userGroups.get(user).push(jid);
|
|
290
277
|
}
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
// Convert user to proper PN JID format for getLIDForPN
|
|
298
|
-
const pnJid = `${user}@s.whatsapp.net`;
|
|
299
|
-
const mapping = await signalRepository.lidMapping.getLIDForPN(pnJid);
|
|
300
|
-
if (mapping?.includes('@lid')) {
|
|
301
|
-
logger.debug({ user, lidForPN: mapping, deviceCount: userJids.length }, 'User has LID mapping - preparing bulk migration');
|
|
302
|
-
return { shouldMigrate: true, lidForPN: mapping };
|
|
303
|
-
}
|
|
304
|
-
}
|
|
305
|
-
catch (error) {
|
|
306
|
-
logger.debug({ user, error }, 'Failed to check LID mapping for user');
|
|
307
|
-
}
|
|
308
|
-
return { shouldMigrate: false, lidForPN: undefined };
|
|
309
|
-
};
|
|
310
|
-
// Process each user group for potential bulk LID migration
|
|
311
|
-
for (const [user, userJids] of userGroups) {
|
|
312
|
-
const mappingResult = await checkUserLidMapping(user, userJids);
|
|
313
|
-
const shouldMigrateUser = mappingResult.shouldMigrate;
|
|
314
|
-
const lidForPN = mappingResult.lidForPN;
|
|
315
|
-
// Migrate all devices for this user if LID mapping exists
|
|
316
|
-
if (shouldMigrateUser && lidForPN) {
|
|
317
|
-
// Bulk migrate all user devices in single transaction
|
|
318
|
-
const migrationResult = await signalRepository.migrateSession(userJids, lidForPN);
|
|
319
|
-
if (migrationResult.migrated > 0) {
|
|
320
|
-
logger.info({
|
|
321
|
-
user,
|
|
322
|
-
lidMapping: lidForPN,
|
|
323
|
-
migrated: migrationResult.migrated,
|
|
324
|
-
skipped: migrationResult.skipped,
|
|
325
|
-
total: migrationResult.total
|
|
326
|
-
}, 'Completed bulk migration for user devices');
|
|
327
|
-
}
|
|
328
|
-
else {
|
|
329
|
-
logger.debug({
|
|
330
|
-
user,
|
|
331
|
-
lidMapping: lidForPN,
|
|
332
|
-
skipped: migrationResult.skipped,
|
|
333
|
-
total: migrationResult.total
|
|
334
|
-
}, 'All user device sessions already migrated');
|
|
335
|
-
}
|
|
278
|
+
else {
|
|
279
|
+
const sessions = await authState.keys.get('session', [signalId]);
|
|
280
|
+
const hasSession = !!sessions[signalId];
|
|
281
|
+
peerSessionsCache.set(signalId, hasSession);
|
|
282
|
+
if (hasSession) {
|
|
283
|
+
continue;
|
|
336
284
|
}
|
|
337
|
-
// Direct bulk session check with LID single source of truth
|
|
338
|
-
const addMissingSessionsToFetchList = (jid) => {
|
|
339
|
-
const signalId = signalRepository.jidToSignalProtocolAddress(jid);
|
|
340
|
-
if (sessions[signalId])
|
|
341
|
-
return;
|
|
342
|
-
// Determine correct JID to fetch (LID if mapping exists, otherwise original)
|
|
343
|
-
if (jid.includes('@s.whatsapp.net') && shouldMigrateUser && lidForPN) {
|
|
344
|
-
const decoded = jidDecode(jid);
|
|
345
|
-
const lidDeviceJid = decoded.device !== undefined ? `${jidDecode(lidForPN).user}:${decoded.device}@lid` : lidForPN;
|
|
346
|
-
jidsRequiringFetch.push(lidDeviceJid);
|
|
347
|
-
logger.debug({ pnJid: jid, lidJid: lidDeviceJid }, 'Adding LID JID to fetch list (conversion)');
|
|
348
|
-
}
|
|
349
|
-
else {
|
|
350
|
-
jidsRequiringFetch.push(jid);
|
|
351
|
-
logger.debug({ jid }, 'Adding JID to fetch list');
|
|
352
|
-
}
|
|
353
|
-
};
|
|
354
|
-
userJids.forEach(addMissingSessionsToFetchList);
|
|
355
285
|
}
|
|
286
|
+
jidsRequiringFetch.push(jid);
|
|
356
287
|
}
|
|
357
288
|
if (jidsRequiringFetch.length) {
|
|
358
289
|
logger.debug({ jidsRequiringFetch }, 'fetching sessions');
|
|
359
|
-
// DEBUG: Check if there are PN versions of LID users being fetched
|
|
360
|
-
const lidUsersBeingFetched = new Set();
|
|
361
|
-
const pnUsersBeingFetched = new Set();
|
|
362
|
-
for (const jid of jidsRequiringFetch) {
|
|
363
|
-
const user = jidDecode(jid)?.user;
|
|
364
|
-
if (user) {
|
|
365
|
-
if (jid.includes('@lid')) {
|
|
366
|
-
lidUsersBeingFetched.add(user);
|
|
367
|
-
}
|
|
368
|
-
else if (jid.includes('@s.whatsapp.net')) {
|
|
369
|
-
pnUsersBeingFetched.add(user);
|
|
370
|
-
}
|
|
371
|
-
}
|
|
372
|
-
}
|
|
373
|
-
// Find overlaps
|
|
374
|
-
const overlapping = Array.from(pnUsersBeingFetched).filter(user => lidUsersBeingFetched.has(user));
|
|
375
|
-
if (overlapping.length > 0) {
|
|
376
|
-
logger.warn({
|
|
377
|
-
overlapping,
|
|
378
|
-
lidUsersBeingFetched: Array.from(lidUsersBeingFetched),
|
|
379
|
-
pnUsersBeingFetched: Array.from(pnUsersBeingFetched)
|
|
380
|
-
}, 'Fetching both LID and PN sessions for same users');
|
|
381
|
-
}
|
|
382
290
|
const result = await query({
|
|
383
291
|
tag: 'iq',
|
|
384
292
|
attrs: {
|
|
@@ -399,6 +307,11 @@ export const makeMessagesSocket = (config) => {
|
|
|
399
307
|
});
|
|
400
308
|
await parseAndInjectE2ESessions(result, signalRepository);
|
|
401
309
|
didFetchNewSession = true;
|
|
310
|
+
// Cache fetched sessions
|
|
311
|
+
for (const jid of jidsRequiringFetch) {
|
|
312
|
+
const signalId = signalRepository.jidToSignalProtocolAddress(jid);
|
|
313
|
+
peerSessionsCache.set(signalId, true);
|
|
314
|
+
}
|
|
402
315
|
}
|
|
403
316
|
return didFetchNewSession;
|
|
404
317
|
};
|
|
@@ -422,107 +335,47 @@ export const makeMessagesSocket = (config) => {
|
|
|
422
335
|
});
|
|
423
336
|
return msgId;
|
|
424
337
|
};
|
|
425
|
-
const
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
338
|
+
const createParticipantNodesWithSessionMap = async (recipientWireJids, sessionMap, message, extraAttrs, dsmMessage) => {
|
|
339
|
+
if (!recipientWireJids.length) {
|
|
340
|
+
return { nodes: [], shouldIncludeDeviceIdentity: false };
|
|
341
|
+
}
|
|
342
|
+
const patched = await patchMessageBeforeSending(message, recipientWireJids);
|
|
343
|
+
const patchedMessages = Array.isArray(patched)
|
|
344
|
+
? patched
|
|
345
|
+
: recipientWireJids.map(jid => ({ recipientJid: jid, message: patched }));
|
|
430
346
|
let shouldIncludeDeviceIdentity = false;
|
|
431
347
|
const meId = authState.creds.me.id;
|
|
432
348
|
const meLid = authState.creds.me?.lid;
|
|
433
349
|
const meLidUser = meLid ? jidDecode(meLid)?.user : null;
|
|
434
|
-
const
|
|
435
|
-
for (const patchedMessageWithJid of patched) {
|
|
436
|
-
const { recipientJid: wireJid, ...patchedMessage } = patchedMessageWithJid;
|
|
350
|
+
const encryptionPromises = patchedMessages.map(async ({ recipientJid: wireJid, message: patchedMessage }) => {
|
|
437
351
|
if (!wireJid)
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
const
|
|
453
|
-
|
|
454
|
-
const
|
|
455
|
-
if (!wireJid.includes('@s.whatsapp.net'))
|
|
456
|
-
return wireJid;
|
|
457
|
-
try {
|
|
458
|
-
const lidForPN = await signalRepository.lidMapping.getLIDForPN(wireJid);
|
|
459
|
-
if (!lidForPN?.includes('@lid'))
|
|
460
|
-
return wireJid;
|
|
461
|
-
// Preserve device ID from original wire JID
|
|
462
|
-
const wireDecoded = jidDecode(wireJid);
|
|
463
|
-
const deviceId = wireDecoded?.device || 0;
|
|
464
|
-
const lidDecoded = jidDecode(lidForPN);
|
|
465
|
-
const lidWithDevice = jidEncode(lidDecoded?.user, 'lid', deviceId);
|
|
466
|
-
// Migrate session to LID for unified encryption layer
|
|
467
|
-
try {
|
|
468
|
-
const migrationResult = await signalRepository.migrateSession([wireJid], lidWithDevice);
|
|
469
|
-
const recipientUser = jidNormalizedUser(wireJid);
|
|
470
|
-
const ownPnUser = jidNormalizedUser(meId);
|
|
471
|
-
const isOwnDevice = recipientUser === ownPnUser;
|
|
472
|
-
logger.info({ wireJid, lidWithDevice, isOwnDevice }, 'Migrated to LID encryption');
|
|
473
|
-
// Delete PN session after successful migration
|
|
474
|
-
try {
|
|
475
|
-
if (migrationResult.migrated) {
|
|
476
|
-
await signalRepository.deleteSession([wireJid]);
|
|
477
|
-
logger.debug({ deletedPNSession: wireJid }, 'Deleted PN session');
|
|
478
|
-
}
|
|
479
|
-
}
|
|
480
|
-
catch (deleteError) {
|
|
481
|
-
logger.warn({ wireJid, error: deleteError }, 'Failed to delete PN session');
|
|
482
|
-
}
|
|
483
|
-
return lidWithDevice;
|
|
484
|
-
}
|
|
485
|
-
catch (migrationError) {
|
|
486
|
-
logger.warn({ wireJid, error: migrationError }, 'Failed to migrate session');
|
|
487
|
-
return wireJid;
|
|
488
|
-
}
|
|
489
|
-
}
|
|
490
|
-
catch (error) {
|
|
491
|
-
logger.debug({ wireJid, error }, 'Failed to check LID mapping');
|
|
492
|
-
return wireJid;
|
|
493
|
-
}
|
|
494
|
-
};
|
|
495
|
-
// Encrypt to this user's devices sequentially to prevent session corruption
|
|
496
|
-
for (const { recipientJid: wireJid, patchedMessage } of userDevices) {
|
|
497
|
-
// DSM logic: Use DSM for own other devices (following whatsmeow implementation)
|
|
498
|
-
let messageToEncrypt = patchedMessage;
|
|
499
|
-
if (dsmMessage) {
|
|
500
|
-
const { user: targetUser } = jidDecode(wireJid);
|
|
501
|
-
const { user: ownPnUser } = jidDecode(meId);
|
|
502
|
-
const ownLidUser = meLidUser;
|
|
503
|
-
// Check if this is our device (same user, different device)
|
|
504
|
-
const isOwnUser = targetUser === ownPnUser || (ownLidUser && targetUser === ownLidUser);
|
|
505
|
-
// Exclude exact sender device (whatsmeow: if jid == ownJID || jid == ownLID { continue })
|
|
506
|
-
const isExactSenderDevice = wireJid === meId || (authState.creds.me?.lid && wireJid === authState.creds.me.lid);
|
|
507
|
-
if (isOwnUser && !isExactSenderDevice) {
|
|
508
|
-
messageToEncrypt = dsmMessage;
|
|
509
|
-
logger.debug({ wireJid, targetUser }, 'Using DSM for own device');
|
|
510
|
-
}
|
|
511
|
-
}
|
|
512
|
-
const bytes = encodeWAMessage(messageToEncrypt);
|
|
513
|
-
// Get encryption JID with LID migration
|
|
514
|
-
const encryptionJid = await getEncryptionJid(wireJid);
|
|
515
|
-
// ENCRYPT: Use the determined encryption identity (prefers migrated LID)
|
|
352
|
+
return null;
|
|
353
|
+
wireJid = sessionMap.get(wireJid) ?? wireJid;
|
|
354
|
+
let msgToEncrypt = patchedMessage;
|
|
355
|
+
if (dsmMessage) {
|
|
356
|
+
const { user: targetUser } = jidDecode(wireJid);
|
|
357
|
+
const { user: ownPnUser } = jidDecode(meId);
|
|
358
|
+
const ownLidUser = meLidUser;
|
|
359
|
+
const isOwnUser = targetUser === ownPnUser || (ownLidUser && targetUser === ownLidUser);
|
|
360
|
+
const isExactSenderDevice = wireJid === meId || (meLid && wireJid === meLid);
|
|
361
|
+
if (isOwnUser && !isExactSenderDevice) {
|
|
362
|
+
msgToEncrypt = dsmMessage;
|
|
363
|
+
logger.debug({ wireJid, targetUser }, 'Using DSM for own device');
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
const bytes = encodeWAMessage(msgToEncrypt);
|
|
367
|
+
const mutexKey = wireJid;
|
|
368
|
+
const node = await encryptionMutex.mutex(mutexKey, async () => {
|
|
516
369
|
const { type, ciphertext } = await signalRepository.encryptMessage({
|
|
517
|
-
jid:
|
|
370
|
+
jid: wireJid,
|
|
518
371
|
data: bytes
|
|
519
372
|
});
|
|
520
373
|
if (type === 'pkmsg') {
|
|
521
374
|
shouldIncludeDeviceIdentity = true;
|
|
522
375
|
}
|
|
523
|
-
|
|
376
|
+
return {
|
|
524
377
|
tag: 'to',
|
|
525
|
-
attrs: { jid: wireJid },
|
|
378
|
+
attrs: { jid: wireJid },
|
|
526
379
|
content: [
|
|
527
380
|
{
|
|
528
381
|
tag: 'enc',
|
|
@@ -535,28 +388,27 @@ export const makeMessagesSocket = (config) => {
|
|
|
535
388
|
}
|
|
536
389
|
]
|
|
537
390
|
};
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
}));
|
|
543
|
-
// Wait for all users to complete (users are processed in parallel)
|
|
544
|
-
const userNodesArrays = await Promise.all(userEncryptionPromises);
|
|
545
|
-
const nodes = userNodesArrays.flat();
|
|
391
|
+
});
|
|
392
|
+
return node;
|
|
393
|
+
});
|
|
394
|
+
const nodes = (await Promise.all(encryptionPromises)).filter(node => node !== null);
|
|
546
395
|
return { nodes, shouldIncludeDeviceIdentity };
|
|
547
396
|
};
|
|
397
|
+
const createParticipantNodes = async (recipientWireJids, message, extraAttrs, dsmMessage) => {
|
|
398
|
+
const sessionMap = await resolveSessionJids(recipientWireJids);
|
|
399
|
+
return createParticipantNodesWithSessionMap(recipientWireJids, sessionMap, message, extraAttrs, dsmMessage);
|
|
400
|
+
};
|
|
548
401
|
const relayMessage = async (jid, message, { messageId: msgId, participant, additionalAttributes, additionalNodes, useUserDevicesCache, useCachedGroupMetadata, statusJidList }) => {
|
|
549
402
|
const meId = authState.creds.me.id;
|
|
550
403
|
const meLid = authState.creds.me?.lid;
|
|
551
|
-
|
|
552
|
-
let shouldIncludeDeviceIdentity =
|
|
553
|
-
const { user, server } = jidDecode(jid);
|
|
404
|
+
const isRetryResend = Boolean(participant?.jid);
|
|
405
|
+
let shouldIncludeDeviceIdentity = isRetryResend;
|
|
554
406
|
const statusJid = 'status@broadcast';
|
|
407
|
+
const { user, server } = jidDecode(jid);
|
|
555
408
|
const isGroup = server === 'g.us';
|
|
556
409
|
const isStatus = jid === statusJid;
|
|
557
410
|
const isLid = server === 'lid';
|
|
558
411
|
const isNewsletter = server === 'newsletter';
|
|
559
|
-
// Keep user's original JID choice for envelope addressing
|
|
560
412
|
const finalJid = jid;
|
|
561
413
|
// ADDRESSING CONSISTENCY: Match own identity to conversation context
|
|
562
414
|
// TODO: investigate if this is true
|
|
@@ -584,17 +436,14 @@ export const makeMessagesSocket = (config) => {
|
|
|
584
436
|
};
|
|
585
437
|
const extraAttrs = {};
|
|
586
438
|
if (participant) {
|
|
587
|
-
// when the retry request is not for a group
|
|
588
|
-
// only send to the specific device that asked for a retry
|
|
589
|
-
// otherwise the message is sent out to every device that should be a recipient
|
|
590
439
|
if (!isGroup && !isStatus) {
|
|
591
440
|
additionalAttributes = { ...additionalAttributes, device_fanout: 'false' };
|
|
592
441
|
}
|
|
593
|
-
const { user, device } = jidDecode(participant.jid);
|
|
442
|
+
const { user, device } = jidDecode(participant.jid);
|
|
594
443
|
devices.push({
|
|
595
444
|
user,
|
|
596
445
|
device,
|
|
597
|
-
wireJid: participant.jid
|
|
446
|
+
wireJid: participant.jid
|
|
598
447
|
});
|
|
599
448
|
}
|
|
600
449
|
await authState.keys.transaction(async () => {
|
|
@@ -603,7 +452,6 @@ export const makeMessagesSocket = (config) => {
|
|
|
603
452
|
extraAttrs['mediatype'] = mediaType;
|
|
604
453
|
}
|
|
605
454
|
if (isNewsletter) {
|
|
606
|
-
// Patch message if needed, then encode as plaintext
|
|
607
455
|
const patched = patchMessageBeforeSending ? await patchMessageBeforeSending(message, []) : message;
|
|
608
456
|
const bytes = encodeNewsletterMessage(patched);
|
|
609
457
|
binaryNodeContent.push({
|
|
@@ -663,12 +511,17 @@ export const makeMessagesSocket = (config) => {
|
|
|
663
511
|
const additionalDevices = await getUSyncDevices(participantsList, !!useUserDevicesCache, false);
|
|
664
512
|
devices.push(...additionalDevices);
|
|
665
513
|
}
|
|
514
|
+
if (groupData?.ephemeralDuration && groupData.ephemeralDuration > 0) {
|
|
515
|
+
additionalAttributes = {
|
|
516
|
+
...additionalAttributes,
|
|
517
|
+
expiration: groupData.ephemeralDuration.toString()
|
|
518
|
+
};
|
|
519
|
+
}
|
|
666
520
|
const patched = await patchMessageBeforeSending(message);
|
|
667
521
|
if (Array.isArray(patched)) {
|
|
668
522
|
throw new Boom('Per-jid patching is not supported in groups');
|
|
669
523
|
}
|
|
670
524
|
const bytes = encodeWAMessage(patched);
|
|
671
|
-
// This should match the group's addressing mode and conversation context
|
|
672
525
|
const groupAddressingMode = groupData?.addressingMode || (isLid ? 'lid' : 'pn');
|
|
673
526
|
const groupSenderIdentity = groupAddressingMode === 'lid' && meLid ? meLid : meId;
|
|
674
527
|
const { ciphertext, senderKeyDistributionMessage } = await signalRepository.encryptGroupMessage({
|
|
@@ -676,39 +529,53 @@ export const makeMessagesSocket = (config) => {
|
|
|
676
529
|
data: bytes,
|
|
677
530
|
meId: groupSenderIdentity
|
|
678
531
|
});
|
|
679
|
-
const
|
|
680
|
-
|
|
532
|
+
const deviceSessionMap = await resolveSessionJids(devices.map(d => d.wireJid));
|
|
533
|
+
const senderKeyRecipients = [];
|
|
681
534
|
for (const device of devices) {
|
|
682
|
-
// This preserves the LID migration results from getUSyncDevices
|
|
683
535
|
const deviceJid = device.wireJid;
|
|
684
536
|
const hasKey = !!senderKeyMap[deviceJid];
|
|
685
|
-
if (!hasKey
|
|
686
|
-
|
|
687
|
-
// store that this person has had the sender keys sent to them
|
|
537
|
+
if (!hasKey && !isRetryResend) {
|
|
538
|
+
senderKeyRecipients.push(deviceJid);
|
|
688
539
|
senderKeyMap[deviceJid] = true;
|
|
689
540
|
}
|
|
690
541
|
}
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
if (senderKeyJids.length) {
|
|
694
|
-
logger.debug({ senderKeyJids }, 'sending new sender key');
|
|
542
|
+
if (senderKeyRecipients.length) {
|
|
543
|
+
logger.debug({ senderKeyJids: senderKeyRecipients }, 'sending new sender key');
|
|
695
544
|
const senderKeyMsg = {
|
|
696
545
|
senderKeyDistributionMessage: {
|
|
697
546
|
axolotlSenderKeyDistributionMessage: senderKeyDistributionMessage,
|
|
698
547
|
groupId: destinationJid
|
|
699
548
|
}
|
|
700
549
|
};
|
|
701
|
-
|
|
702
|
-
|
|
550
|
+
const senderKeySessionTargets = senderKeyRecipients.map(jid => deviceSessionMap.get(jid) ?? jid);
|
|
551
|
+
await assertSessions(senderKeySessionTargets);
|
|
552
|
+
const result = await createParticipantNodesWithSessionMap(senderKeyRecipients, deviceSessionMap, senderKeyMsg, extraAttrs);
|
|
703
553
|
shouldIncludeDeviceIdentity = shouldIncludeDeviceIdentity || result.shouldIncludeDeviceIdentity;
|
|
704
554
|
participants.push(...result.nodes);
|
|
705
555
|
}
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
556
|
+
if (isRetryResend) {
|
|
557
|
+
const { type, ciphertext: encryptedContent } = await signalRepository.encryptMessage({
|
|
558
|
+
data: bytes,
|
|
559
|
+
jid: participant?.jid
|
|
560
|
+
});
|
|
561
|
+
binaryNodeContent.push({
|
|
562
|
+
tag: 'enc',
|
|
563
|
+
attrs: {
|
|
564
|
+
v: '2',
|
|
565
|
+
type,
|
|
566
|
+
count: participant.count.toString()
|
|
567
|
+
},
|
|
568
|
+
content: encryptedContent
|
|
569
|
+
});
|
|
570
|
+
}
|
|
571
|
+
else {
|
|
572
|
+
binaryNodeContent.push({
|
|
573
|
+
tag: 'enc',
|
|
574
|
+
attrs: { v: '2', type: 'skmsg', ...extraAttrs },
|
|
575
|
+
content: ciphertext
|
|
576
|
+
});
|
|
577
|
+
await authState.keys.set({ 'sender-key-memory': { [jid]: senderKeyMap } });
|
|
578
|
+
}
|
|
712
579
|
}
|
|
713
580
|
else {
|
|
714
581
|
const { user: ownUser } = jidDecode(ownId);
|
|
@@ -737,7 +604,7 @@ export const makeMessagesSocket = (config) => {
|
|
|
737
604
|
? jidEncode(jidDecode(meLid)?.user, 'lid', undefined)
|
|
738
605
|
: jidEncode(jidDecode(meId)?.user, 's.whatsapp.net', undefined);
|
|
739
606
|
// Enumerate devices for sender and target with consistent addressing
|
|
740
|
-
const sessionDevices = await getUSyncDevices([senderIdentity, jid],
|
|
607
|
+
const sessionDevices = await getUSyncDevices([senderIdentity, jid], true, false);
|
|
741
608
|
devices.push(...sessionDevices);
|
|
742
609
|
logger.debug({
|
|
743
610
|
deviceCount: devices.length,
|
|
@@ -745,9 +612,9 @@ export const makeMessagesSocket = (config) => {
|
|
|
745
612
|
}, 'Device enumeration complete with unified addressing');
|
|
746
613
|
}
|
|
747
614
|
}
|
|
748
|
-
const
|
|
749
|
-
const
|
|
750
|
-
const
|
|
615
|
+
const allRecipients = [];
|
|
616
|
+
const meRecipients = [];
|
|
617
|
+
const otherRecipients = [];
|
|
751
618
|
const { user: mePnUser } = jidDecode(meId);
|
|
752
619
|
const { user: meLidUser } = meLid ? jidDecode(meLid) : { user: null };
|
|
753
620
|
for (const { user, wireJid } of devices) {
|
|
@@ -758,25 +625,26 @@ export const makeMessagesSocket = (config) => {
|
|
|
758
625
|
}
|
|
759
626
|
// Check if this is our device (could match either PN or LID user)
|
|
760
627
|
const isMe = user === mePnUser || (meLidUser && user === meLidUser);
|
|
761
|
-
const jid = wireJid;
|
|
762
628
|
if (isMe) {
|
|
763
|
-
|
|
629
|
+
meRecipients.push(wireJid);
|
|
764
630
|
}
|
|
765
631
|
else {
|
|
766
|
-
|
|
632
|
+
otherRecipients.push(wireJid);
|
|
767
633
|
}
|
|
768
|
-
|
|
634
|
+
allRecipients.push(wireJid);
|
|
769
635
|
}
|
|
770
|
-
await
|
|
636
|
+
const deviceSessionMap = await resolveSessionJids(devices.map(d => d.wireJid));
|
|
637
|
+
const sessionTargets = allRecipients.map(jid => deviceSessionMap.get(jid) ?? jid);
|
|
638
|
+
await assertSessions(sessionTargets);
|
|
771
639
|
const [{ nodes: meNodes, shouldIncludeDeviceIdentity: s1 }, { nodes: otherNodes, shouldIncludeDeviceIdentity: s2 }] = await Promise.all([
|
|
772
640
|
// For own devices: use DSM if available (1:1 chats only)
|
|
773
|
-
|
|
774
|
-
|
|
641
|
+
createParticipantNodesWithSessionMap(meRecipients, deviceSessionMap, meMsg || message, extraAttrs),
|
|
642
|
+
createParticipantNodesWithSessionMap(otherRecipients, deviceSessionMap, message, extraAttrs, meMsg)
|
|
775
643
|
]);
|
|
776
644
|
participants.push(...meNodes);
|
|
777
645
|
participants.push(...otherNodes);
|
|
778
|
-
if (
|
|
779
|
-
extraAttrs['phash'] = generateParticipantHashV2([...
|
|
646
|
+
if (meRecipients.length > 0 || otherRecipients.length > 0) {
|
|
647
|
+
extraAttrs['phash'] = generateParticipantHashV2([...meRecipients, ...otherRecipients]);
|
|
780
648
|
}
|
|
781
649
|
shouldIncludeDeviceIdentity = shouldIncludeDeviceIdentity || s1 || s2;
|
|
782
650
|
}
|
|
@@ -853,6 +721,9 @@ export const makeMessagesSocket = (config) => {
|
|
|
853
721
|
if (message.eventMessage) {
|
|
854
722
|
return 'event';
|
|
855
723
|
}
|
|
724
|
+
if (getMediaType(message) !== '') {
|
|
725
|
+
return 'media';
|
|
726
|
+
}
|
|
856
727
|
return 'text';
|
|
857
728
|
};
|
|
858
729
|
const getMediaType = (message) => {
|
|
@@ -901,6 +772,7 @@ export const makeMessagesSocket = (config) => {
|
|
|
901
772
|
else if (message.groupInviteMessage) {
|
|
902
773
|
return 'url';
|
|
903
774
|
}
|
|
775
|
+
return '';
|
|
904
776
|
};
|
|
905
777
|
const getPrivacyTokens = async (jids) => {
|
|
906
778
|
const t = unixTimestampSeconds().toString();
|
|
@@ -1062,9 +934,6 @@ export const makeMessagesSocket = (config) => {
|
|
|
1062
934
|
}
|
|
1063
935
|
});
|
|
1064
936
|
}
|
|
1065
|
-
if ('cachedGroupMetadata' in options) {
|
|
1066
|
-
console.warn('cachedGroupMetadata in sendMessage are deprecated, now cachedGroupMetadata is part of the socket config.');
|
|
1067
|
-
}
|
|
1068
937
|
await relayMessage(jid, fullMsg.message, {
|
|
1069
938
|
messageId: fullMsg.key.id,
|
|
1070
939
|
useCachedGroupMetadata: options.useCachedGroupMetadata,
|