@periskope/baileys 7.0.0-beta-2 → 7.0.0-beta-3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (83) hide show
  1. package/WAProto/GenerateStatics.sh +2 -2
  2. package/WAProto/index.d.ts +45605 -2820
  3. package/WAProto/index.js +152067 -24346
  4. package/lib/Defaults/index.d.ts +5 -0
  5. package/lib/Defaults/index.d.ts.map +1 -1
  6. package/lib/Defaults/index.js +8 -2
  7. package/lib/Defaults/index.js.map +1 -1
  8. package/lib/Signal/Group/queue-job.d.ts +1 -0
  9. package/lib/Signal/Group/queue-job.d.ts.map +1 -1
  10. package/lib/Signal/Group/queue-job.js +3 -0
  11. package/lib/Signal/Group/queue-job.js.map +1 -1
  12. package/lib/Signal/Group/sender-key-distribution-message.d.ts.map +1 -1
  13. package/lib/Signal/Group/sender-key-distribution-message.js +1 -1
  14. package/lib/Signal/Group/sender-key-distribution-message.js.map +1 -1
  15. package/lib/Signal/Group/sender-key-message.d.ts.map +1 -1
  16. package/lib/Signal/Group/sender-key-message.js +1 -1
  17. package/lib/Signal/Group/sender-key-message.js.map +1 -1
  18. package/lib/Signal/Group/sender-key-state.d.ts.map +1 -1
  19. package/lib/Signal/Group/sender-key-state.js +6 -1
  20. package/lib/Signal/Group/sender-key-state.js.map +1 -1
  21. package/lib/Signal/libsignal.d.ts.map +1 -1
  22. package/lib/Signal/libsignal.js +147 -9
  23. package/lib/Signal/libsignal.js.map +1 -1
  24. package/lib/Socket/business.d.ts +18 -7
  25. package/lib/Socket/business.d.ts.map +1 -1
  26. package/lib/Socket/business.js +122 -1
  27. package/lib/Socket/business.js.map +1 -1
  28. package/lib/Socket/chats.d.ts +8 -2
  29. package/lib/Socket/chats.d.ts.map +1 -1
  30. package/lib/Socket/chats.js +38 -1
  31. package/lib/Socket/chats.js.map +1 -1
  32. package/lib/Socket/communities.d.ts +17 -4
  33. package/lib/Socket/communities.d.ts.map +1 -1
  34. package/lib/Socket/communities.js +45 -1
  35. package/lib/Socket/communities.js.map +1 -1
  36. package/lib/Socket/groups.d.ts +7 -2
  37. package/lib/Socket/groups.d.ts.map +1 -1
  38. package/lib/Socket/groups.js +1 -1
  39. package/lib/Socket/groups.js.map +1 -1
  40. package/lib/Socket/index.d.ts +17 -4
  41. package/lib/Socket/index.d.ts.map +1 -1
  42. package/lib/Socket/messages-recv.d.ts +11 -4
  43. package/lib/Socket/messages-recv.d.ts.map +1 -1
  44. package/lib/Socket/messages-recv.js +73 -10
  45. package/lib/Socket/messages-recv.js.map +1 -1
  46. package/lib/Socket/messages-send.d.ts +11 -4
  47. package/lib/Socket/messages-send.d.ts.map +1 -1
  48. package/lib/Socket/messages-send.js +443 -55
  49. package/lib/Socket/messages-send.js.map +1 -1
  50. package/lib/Socket/newsletter.d.ts +7 -2
  51. package/lib/Socket/newsletter.d.ts.map +1 -1
  52. package/lib/Socket/socket.d.ts +2 -2
  53. package/lib/Socket/socket.d.ts.map +1 -1
  54. package/lib/Socket/socket.js +147 -25
  55. package/lib/Socket/socket.js.map +1 -1
  56. package/lib/Socket/usync.d.ts +2 -2
  57. package/lib/Types/Auth.d.ts +1 -0
  58. package/lib/Types/Auth.d.ts.map +1 -1
  59. package/lib/Types/Chat.d.ts +3 -0
  60. package/lib/Types/Chat.d.ts.map +1 -1
  61. package/lib/Types/Chat.js.map +1 -1
  62. package/lib/Types/Message.d.ts +21 -1
  63. package/lib/Types/Message.d.ts.map +1 -1
  64. package/lib/Types/Signal.d.ts +20 -0
  65. package/lib/Types/Signal.d.ts.map +1 -1
  66. package/lib/Utils/chat-utils.d.ts.map +1 -1
  67. package/lib/Utils/chat-utils.js +18 -1
  68. package/lib/Utils/chat-utils.js.map +1 -1
  69. package/lib/Utils/decode-wa-message.d.ts +5 -0
  70. package/lib/Utils/decode-wa-message.d.ts.map +1 -1
  71. package/lib/Utils/decode-wa-message.js +51 -3
  72. package/lib/Utils/decode-wa-message.js.map +1 -1
  73. package/lib/Utils/messages-media.d.ts.map +1 -1
  74. package/lib/Utils/messages-media.js +4 -1
  75. package/lib/Utils/messages-media.js.map +1 -1
  76. package/lib/Utils/messages.d.ts.map +1 -1
  77. package/lib/Utils/messages.js +35 -15
  78. package/lib/Utils/messages.js.map +1 -1
  79. package/lib/Utils/use-multi-file-auth-state.js +1 -1
  80. package/lib/Utils/use-multi-file-auth-state.js.map +1 -1
  81. package/lib/Utils/validate-connection.js +2 -2
  82. package/lib/Utils/validate-connection.js.map +1 -1
  83. package/package.json +4 -2
@@ -4,6 +4,7 @@ import { proto } from '../../WAProto/index.js';
4
4
  import { DEFAULT_CACHE_TTLS, WA_DEFAULT_EPHEMERAL } from '../Defaults/index.js';
5
5
  import { aggregateMessageKeysNotFromMe, assertMediaContent, bindWaitForEvent, decryptMediaRetryData, encodeNewsletterMessage, encodeSignedDeviceIdentity, encodeWAMessage, encryptMediaRetryRequest, extractDeviceJids, generateMessageIDV2, generateWAMessage, getStatusCodeForMediaRetry, getUrlFromDirectPath, getWAUploadToServer, normalizeMessageContent, parseAndInjectE2ESessions, unixTimestampSeconds } from '../Utils/index.js';
6
6
  import { getUrlInfo } from '../Utils/link-preview.js';
7
+ import { makeKeyedMutex } from '../Utils/make-mutex.js';
7
8
  import { areJidsSameUser, getBinaryNodeChild, getBinaryNodeChildren, isJidGroup, isJidUser, jidDecode, jidEncode, jidNormalizedUser, S_WHATSAPP_NET } from '../WABinary/index.js';
8
9
  import { USyncQuery, USyncUser } from '../WAUSync/index.js';
9
10
  import { makeGroupsSocket } from './groups.js';
@@ -17,6 +18,8 @@ export const makeMessagesSocket = (config) => {
17
18
  stdTTL: DEFAULT_CACHE_TTLS.USER_DEVICES, // 5 minutes
18
19
  useClones: false
19
20
  });
21
+ // Prevent race conditions in Signal session encryption by user
22
+ const encryptionMutex = makeKeyedMutex();
20
23
  let mediaConn;
21
24
  const refreshMediaConn = async (forceGet = false) => {
22
25
  const media = await mediaConn;
@@ -108,6 +111,34 @@ export const makeMessagesSocket = (config) => {
108
111
  const readType = privacySettings.readreceipts === 'all' ? 'read' : 'read-self';
109
112
  await sendReceipts(keys, readType);
110
113
  };
114
+ /**
115
+ * Deduplicate JIDs when both LID and PN versions exist for same user
116
+ * Prefers LID over PN to maintain single encryption layer
117
+ */
118
+ const deduplicateLidPnJids = (jids) => {
119
+ const lidUsers = new Set();
120
+ const filteredJids = [];
121
+ // Collect all LID users
122
+ for (const jid of jids) {
123
+ if (jid.includes('@lid')) {
124
+ const user = jidDecode(jid)?.user;
125
+ if (user)
126
+ lidUsers.add(user);
127
+ }
128
+ }
129
+ // Filter out PN versions when LID exists
130
+ for (const jid of jids) {
131
+ if (jid.includes('@s.whatsapp.net')) {
132
+ const user = jidDecode(jid)?.user;
133
+ if (user && lidUsers.has(user)) {
134
+ logger.debug({ jid }, 'Skipping PN - LID version exists');
135
+ continue;
136
+ }
137
+ }
138
+ filteredJids.push(jid);
139
+ }
140
+ return filteredJids;
141
+ };
111
142
  /** Fetch all the devices we've to send a message to */
112
143
  const getUSyncDevices = async (jids, useCache, ignoreZeroDevices) => {
113
144
  const deviceResults = [];
@@ -115,14 +146,33 @@ export const makeMessagesSocket = (config) => {
115
146
  logger.debug('not using cache for devices');
116
147
  }
117
148
  const toFetch = [];
118
- jids = Array.from(new Set(jids));
149
+ // Deduplicate and normalize JIDs
150
+ jids = deduplicateLidPnJids(Array.from(new Set(jids)));
119
151
  for (let jid of jids) {
120
- const user = jidDecode(jid)?.user;
152
+ const decoded = jidDecode(jid);
153
+ const user = decoded?.user;
154
+ const device = decoded?.device;
155
+ const isExplicitDevice = typeof device === 'number' && device >= 0;
156
+ // Handle explicit device JIDs directly
157
+ if (isExplicitDevice && user) {
158
+ deviceResults.push({
159
+ user,
160
+ device,
161
+ wireJid: jid // Preserve exact JID format for wire protocol
162
+ });
163
+ continue;
164
+ }
165
+ // For user JIDs, normalize and prepare for device enumeration
121
166
  jid = jidNormalizedUser(jid);
122
167
  if (useCache) {
123
168
  const devices = userDevicesCache.get(user);
124
169
  if (devices) {
125
- deviceResults.push(...devices);
170
+ const isLidJid = jid.includes('@lid');
171
+ const devicesWithWire = devices.map(d => ({
172
+ ...d,
173
+ wireJid: isLidJid ? jidEncode(d.user, 'lid', d.device) : jidEncode(d.user, 's.whatsapp.net', d.device)
174
+ }));
175
+ deviceResults.push(...devicesWithWire);
126
176
  logger.trace({ user }, 'using cache for devices');
127
177
  }
128
178
  else {
@@ -136,6 +186,14 @@ export const makeMessagesSocket = (config) => {
136
186
  if (!toFetch.length) {
137
187
  return deviceResults;
138
188
  }
189
+ const requestedLidUsers = new Set();
190
+ for (const jid of toFetch) {
191
+ if (jid.includes('@lid')) {
192
+ const user = jidDecode(jid)?.user;
193
+ if (user)
194
+ requestedLidUsers.add(user);
195
+ }
196
+ }
139
197
  const query = new USyncQuery().withContext('message').withDeviceProtocol();
140
198
  for (const jid of toFetch) {
141
199
  query.withUser(new USyncUser().withId(jid));
@@ -147,7 +205,26 @@ export const makeMessagesSocket = (config) => {
147
205
  for (const item of extracted) {
148
206
  deviceMap[item.user] = deviceMap[item.user] || [];
149
207
  deviceMap[item.user]?.push(item);
150
- deviceResults.push(item);
208
+ }
209
+ // Process each user's devices as a group for bulk LID migration
210
+ for (const [user, userDevices] of Object.entries(deviceMap)) {
211
+ const isLidUser = requestedLidUsers.has(user);
212
+ // Process all devices for this user
213
+ for (const item of userDevices) {
214
+ const finalWireJid = isLidUser
215
+ ? jidEncode(user, 'lid', item.device)
216
+ : jidEncode(item.user, 's.whatsapp.net', item.device);
217
+ deviceResults.push({
218
+ ...item,
219
+ wireJid: finalWireJid
220
+ });
221
+ logger.debug({
222
+ user: item.user,
223
+ device: item.device,
224
+ finalWireJid,
225
+ usedLid: isLidUser
226
+ }, 'Processed device with LID priority');
227
+ }
151
228
  }
152
229
  for (const key in deviceMap) {
153
230
  userDevicesCache.set(key, deviceMap[key]);
@@ -155,24 +232,179 @@ export const makeMessagesSocket = (config) => {
155
232
  }
156
233
  return deviceResults;
157
234
  };
235
+ // Helper to check if JID has migrated LID session
236
+ const checkForMigratedLidSession = async (jid) => {
237
+ if (!jid.includes('@s.whatsapp.net'))
238
+ return false;
239
+ const lidMapping = signalRepository.getLIDMappingStore();
240
+ const lidForPN = await lidMapping.getLIDForPN(jid);
241
+ if (!lidForPN?.includes('@lid'))
242
+ return false;
243
+ const lidSignalId = signalRepository.jidToSignalProtocolAddress(lidForPN);
244
+ const lidSessions = await authState.keys.get('session', [lidSignalId]);
245
+ return !!lidSessions[lidSignalId];
246
+ };
158
247
  const assertSessions = async (jids, force) => {
159
248
  let didFetchNewSession = false;
160
- let jidsRequiringFetch = [];
249
+ const jidsRequiringFetch = [];
250
+ // Apply same deduplication as in getUSyncDevices
251
+ jids = deduplicateLidPnJids(jids);
161
252
  if (force) {
162
- jidsRequiringFetch = jids;
253
+ // Check which sessions are missing (with LID migration check)
254
+ const addrs = jids.map(jid => signalRepository.jidToSignalProtocolAddress(jid));
255
+ const sessions = await authState.keys.get('session', addrs);
256
+ // Helper to check session for a JID
257
+ const checkJidSession = async (jid) => {
258
+ const signalId = signalRepository.jidToSignalProtocolAddress(jid);
259
+ let hasSession = !!sessions[signalId];
260
+ // Check for migrated LID session if PN session missing
261
+ if (!hasSession) {
262
+ hasSession = await checkForMigratedLidSession(jid);
263
+ if (hasSession) {
264
+ logger.debug({ jid }, 'Found migrated LID session during force assert, skipping PN fetch');
265
+ }
266
+ }
267
+ // Add to fetch list if no session exists
268
+ if (!hasSession) {
269
+ if (jid.includes('@lid')) {
270
+ logger.debug({ jid }, 'No LID session found, will create new LID session');
271
+ }
272
+ jidsRequiringFetch.push(jid);
273
+ }
274
+ };
275
+ // Process all JIDs
276
+ for (const jid of jids) {
277
+ await checkJidSession(jid);
278
+ }
163
279
  }
164
280
  else {
281
+ const lidMapping = signalRepository.getLIDMappingStore();
165
282
  const addrs = jids.map(jid => signalRepository.jidToSignalProtocolAddress(jid));
166
283
  const sessions = await authState.keys.get('session', addrs);
284
+ // Group JIDs by user for bulk migration
285
+ const userGroups = new Map();
167
286
  for (const jid of jids) {
168
- const signalId = signalRepository.jidToSignalProtocolAddress(jid);
169
- if (!sessions[signalId]) {
170
- jidsRequiringFetch.push(jid);
287
+ const user = jidNormalizedUser(jid);
288
+ if (!userGroups.has(user)) {
289
+ userGroups.set(user, []);
290
+ }
291
+ userGroups.get(user).push(jid);
292
+ }
293
+ // Helper to check LID mapping for a user
294
+ const checkUserLidMapping = async (user, userJids) => {
295
+ if (!userJids.some(jid => jid.includes('@s.whatsapp.net'))) {
296
+ return { shouldMigrate: false, lidForPN: undefined };
297
+ }
298
+ try {
299
+ const mapping = await lidMapping.getLIDForPN(user);
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
+ // Helper to migrate a single device
311
+ const migrateDeviceToLid = async (jid, lidForPN) => {
312
+ if (!jid.includes('@s.whatsapp.net'))
313
+ return;
314
+ try {
315
+ const jidDecoded = jidDecode(jid);
316
+ const deviceId = jidDecoded?.device || 0;
317
+ const lidDecoded = jidDecode(lidForPN);
318
+ const lidWithDevice = jidEncode(lidDecoded?.user, 'lid', deviceId);
319
+ await signalRepository.migrateSession(jid, lidWithDevice);
320
+ logger.debug({ fromJid: jid, toJid: lidWithDevice }, 'Migrated device session to LID');
321
+ // Delete PN session after successful migration
322
+ try {
323
+ await signalRepository.deleteSession(jid);
324
+ logger.debug({ deletedPNSession: jid }, 'Deleted PN session after migration');
325
+ }
326
+ catch (deleteError) {
327
+ logger.warn({ jid, error: deleteError }, 'Failed to delete PN session');
328
+ }
329
+ }
330
+ catch (migrationError) {
331
+ logger.warn({ jid, error: migrationError }, 'Failed to migrate device session');
332
+ }
333
+ };
334
+ // Process each user group for potential bulk LID migration
335
+ for (const [user, userJids] of userGroups) {
336
+ const mappingResult = await checkUserLidMapping(user, userJids);
337
+ const shouldMigrateUser = mappingResult.shouldMigrate;
338
+ const lidForPN = mappingResult.lidForPN;
339
+ // Migrate all devices for this user if LID mapping exists
340
+ if (shouldMigrateUser && lidForPN) {
341
+ // Migrate each device individually
342
+ for (const jid of userJids) {
343
+ await migrateDeviceToLid(jid, lidForPN);
344
+ }
345
+ logger.info({
346
+ user,
347
+ lidMapping: lidForPN,
348
+ deviceCount: userJids.length
349
+ }, 'Completed migration attempt for user devices');
350
+ }
351
+ // Helper to check session for migrated user
352
+ const checkMigratedSession = async (jid) => {
353
+ const signalId = signalRepository.jidToSignalProtocolAddress(jid);
354
+ let hasSession = !!sessions[signalId];
355
+ let jidToFetch = jid;
356
+ // Check if we should use migrated LID session instead
357
+ if (shouldMigrateUser && lidForPN && jid.includes('@s.whatsapp.net')) {
358
+ const originalDecoded = jidDecode(jid);
359
+ const deviceId = originalDecoded?.device || 0;
360
+ const lidDecoded = jidDecode(lidForPN);
361
+ const lidWithDevice = jidEncode(lidDecoded?.user, 'lid', deviceId);
362
+ // Check if LID session exists
363
+ const lidSignalId = signalRepository.jidToSignalProtocolAddress(lidWithDevice);
364
+ const lidSessions = await authState.keys.get('session', [lidSignalId]);
365
+ hasSession = !!lidSessions[lidSignalId];
366
+ jidToFetch = lidWithDevice;
367
+ if (hasSession) {
368
+ logger.debug({ originalJid: jid, lidJid: lidWithDevice }, '✅ Found bulk-migrated LID session');
369
+ }
370
+ }
371
+ // Add to fetch list if no session exists
372
+ if (!hasSession) {
373
+ jidsRequiringFetch.push(jidToFetch);
374
+ logger.debug({ jid: jidToFetch, originalJid: jid !== jidToFetch ? jid : undefined }, 'Adding to session fetch list');
375
+ }
376
+ };
377
+ // Now check which sessions need to be fetched for this user
378
+ for (const jid of userJids) {
379
+ await checkMigratedSession(jid);
171
380
  }
172
381
  }
173
382
  }
174
383
  if (jidsRequiringFetch.length) {
175
384
  logger.debug({ jidsRequiringFetch }, 'fetching sessions');
385
+ // DEBUG: Check if there are PN versions of LID users being fetched
386
+ const lidUsersBeingFetched = new Set();
387
+ const pnUsersBeingFetched = new Set();
388
+ for (const jid of jidsRequiringFetch) {
389
+ const user = jidDecode(jid)?.user;
390
+ if (user) {
391
+ if (jid.includes('@lid')) {
392
+ lidUsersBeingFetched.add(user);
393
+ }
394
+ else if (jid.includes('@s.whatsapp.net')) {
395
+ pnUsersBeingFetched.add(user);
396
+ }
397
+ }
398
+ }
399
+ // Find overlaps
400
+ const overlapping = Array.from(pnUsersBeingFetched).filter(user => lidUsersBeingFetched.has(user));
401
+ if (overlapping.length > 0) {
402
+ logger.warn({
403
+ overlapping,
404
+ lidUsersBeingFetched: Array.from(lidUsersBeingFetched),
405
+ pnUsersBeingFetched: Array.from(pnUsersBeingFetched)
406
+ }, 'Fetching both LID and PN sessions for same users');
407
+ }
176
408
  const result = await query({
177
409
  tag: 'iq',
178
410
  attrs: {
@@ -216,43 +448,132 @@ export const makeMessagesSocket = (config) => {
216
448
  });
217
449
  return msgId;
218
450
  };
219
- const createParticipantNodes = async (jids, message, extraAttrs) => {
451
+ const createParticipantNodes = async (jids, message, extraAttrs, dsmMessage) => {
220
452
  let patched = await patchMessageBeforeSending(message, jids);
221
453
  if (!Array.isArray(patched)) {
222
454
  patched = jids ? jids.map(jid => ({ recipientJid: jid, ...patched })) : [patched];
223
455
  }
224
456
  let shouldIncludeDeviceIdentity = false;
225
- const nodes = await Promise.all(patched.map(async (patchedMessageWithJid) => {
226
- const { recipientJid: jid, ...patchedMessage } = patchedMessageWithJid;
227
- if (!jid) {
228
- return {};
229
- }
230
- const bytes = encodeWAMessage(patchedMessage);
231
- const { type, ciphertext } = await signalRepository.encryptMessage({ jid, data: bytes });
232
- if (type === 'pkmsg') {
233
- shouldIncludeDeviceIdentity = true;
234
- }
235
- const node = {
236
- tag: 'to',
237
- attrs: { jid },
238
- content: [
239
- {
240
- tag: 'enc',
241
- attrs: {
242
- v: '2',
243
- type,
244
- ...(extraAttrs || {})
245
- },
246
- content: ciphertext
457
+ const meId = authState.creds.me.id;
458
+ const meLid = authState.creds.me?.lid;
459
+ const meLidUser = meLid ? jidDecode(meLid)?.user : null;
460
+ const devicesByUser = new Map();
461
+ for (const patchedMessageWithJid of patched) {
462
+ const { recipientJid: wireJid, ...patchedMessage } = patchedMessageWithJid;
463
+ if (!wireJid)
464
+ continue;
465
+ // Extract user from JID for grouping
466
+ const decoded = jidDecode(wireJid);
467
+ const user = decoded?.user;
468
+ if (!user)
469
+ continue;
470
+ if (!devicesByUser.has(user)) {
471
+ devicesByUser.set(user, []);
472
+ }
473
+ devicesByUser.get(user).push({ recipientJid: wireJid, patchedMessage });
474
+ }
475
+ // Process each user's devices sequentially, but different users in parallel
476
+ const userEncryptionPromises = Array.from(devicesByUser.entries()).map(([user, userDevices]) => encryptionMutex.mutex(user, async () => {
477
+ logger.debug({ user, deviceCount: userDevices.length }, 'Acquiring encryption lock for user devices');
478
+ const userNodes = [];
479
+ // Helper to get encryption JID with LID migration
480
+ const getEncryptionJid = async (wireJid) => {
481
+ if (!wireJid.includes('@s.whatsapp.net'))
482
+ return wireJid;
483
+ try {
484
+ const lidMapping = signalRepository.getLIDMappingStore();
485
+ const lidForPN = await lidMapping.getLIDForPN(wireJid);
486
+ if (!lidForPN?.includes('@lid'))
487
+ return wireJid;
488
+ // Preserve device ID from original wire JID
489
+ const wireDecoded = jidDecode(wireJid);
490
+ const deviceId = wireDecoded?.device || 0;
491
+ const lidDecoded = jidDecode(lidForPN);
492
+ const lidWithDevice = jidEncode(lidDecoded?.user, 'lid', deviceId);
493
+ // Migrate session to LID for unified encryption layer
494
+ try {
495
+ await signalRepository.migrateSession(wireJid, lidWithDevice);
496
+ const recipientUser = jidNormalizedUser(wireJid);
497
+ const ownPnUser = jidNormalizedUser(meId);
498
+ const isOwnDevice = recipientUser === ownPnUser;
499
+ logger.info({ wireJid, lidWithDevice, isOwnDevice }, 'Migrated to LID encryption');
500
+ // Delete PN session after successful migration
501
+ try {
502
+ await signalRepository.deleteSession(wireJid);
503
+ logger.debug({ deletedPNSession: wireJid }, 'Deleted PN session');
504
+ }
505
+ catch (deleteError) {
506
+ logger.warn({ wireJid, error: deleteError }, 'Failed to delete PN session');
507
+ }
508
+ return lidWithDevice;
247
509
  }
248
- ]
510
+ catch (migrationError) {
511
+ logger.warn({ wireJid, error: migrationError }, 'Failed to migrate session');
512
+ return wireJid;
513
+ }
514
+ }
515
+ catch (error) {
516
+ logger.debug({ wireJid, error }, 'Failed to check LID mapping');
517
+ return wireJid;
518
+ }
249
519
  };
250
- return node;
520
+ // Encrypt to this user's devices sequentially to prevent session corruption
521
+ for (const { recipientJid: wireJid, patchedMessage } of userDevices) {
522
+ // DSM logic: Use DSM for own other devices (following whatsmeow implementation)
523
+ let messageToEncrypt = patchedMessage;
524
+ if (dsmMessage) {
525
+ const { user: targetUser } = jidDecode(wireJid);
526
+ const { user: ownPnUser } = jidDecode(meId);
527
+ const ownLidUser = meLidUser;
528
+ // Check if this is our device (same user, different device)
529
+ const isOwnUser = targetUser === ownPnUser || (ownLidUser && targetUser === ownLidUser);
530
+ // Exclude exact sender device (whatsmeow: if jid == ownJID || jid == ownLID { continue })
531
+ const isExactSenderDevice = wireJid === meId || (authState.creds.me?.lid && wireJid === authState.creds.me.lid);
532
+ if (isOwnUser && !isExactSenderDevice) {
533
+ messageToEncrypt = dsmMessage;
534
+ logger.debug({ wireJid, targetUser }, 'Using DSM for own device');
535
+ }
536
+ }
537
+ const bytes = encodeWAMessage(messageToEncrypt);
538
+ // Get encryption JID with LID migration
539
+ const encryptionJid = await getEncryptionJid(wireJid);
540
+ // ENCRYPT: Use the determined encryption identity (prefers migrated LID)
541
+ const { type, ciphertext } = await signalRepository.encryptMessage({
542
+ jid: encryptionJid, // Unified encryption layer (LID when available)
543
+ data: bytes
544
+ });
545
+ if (type === 'pkmsg') {
546
+ shouldIncludeDeviceIdentity = true;
547
+ }
548
+ const node = {
549
+ tag: 'to',
550
+ attrs: { jid: wireJid }, // Always use original wire identity in envelope
551
+ content: [
552
+ {
553
+ tag: 'enc',
554
+ attrs: {
555
+ v: '2',
556
+ type,
557
+ ...(extraAttrs || {})
558
+ },
559
+ content: ciphertext
560
+ }
561
+ ]
562
+ };
563
+ userNodes.push(node);
564
+ }
565
+ logger.debug({ user, nodesCreated: userNodes.length }, 'Releasing encryption lock for user devices');
566
+ return userNodes;
251
567
  }));
568
+ // Wait for all users to complete (users are processed in parallel)
569
+ const userNodesArrays = await Promise.all(userEncryptionPromises);
570
+ const nodes = userNodesArrays.flat();
252
571
  return { nodes, shouldIncludeDeviceIdentity };
253
572
  };
254
573
  const relayMessage = async (jid, message, { messageId: msgId, participant, additionalAttributes, additionalNodes, useUserDevicesCache, useCachedGroupMetadata, statusJidList }) => {
255
574
  const meId = authState.creds.me.id;
575
+ const meLid = authState.creds.me?.lid;
576
+ // ADDRESSING CONSISTENCY: Keep envelope addressing as user provided, handle LID migration in encryption
256
577
  let shouldIncludeDeviceIdentity = false;
257
578
  const { user, server } = jidDecode(jid);
258
579
  const statusJid = 'status@broadcast';
@@ -260,11 +581,22 @@ export const makeMessagesSocket = (config) => {
260
581
  const isStatus = jid === statusJid;
261
582
  const isLid = server === 'lid';
262
583
  const isNewsletter = server === 'newsletter';
584
+ // Keep user's original JID choice for envelope addressing
585
+ const finalJid = jid;
586
+ // ADDRESSING CONSISTENCY: Match own identity to conversation context
587
+ let ownId = meId;
588
+ if (isLid && meLid) {
589
+ ownId = meLid;
590
+ logger.debug({ to: jid, ownId }, 'Using LID identity for @lid conversation');
591
+ }
592
+ else {
593
+ logger.debug({ to: jid, ownId }, 'Using PN identity for @s.whatsapp.net conversation');
594
+ }
263
595
  msgId = msgId || generateMessageIDV2(sock.user?.id);
264
596
  useUserDevicesCache = useUserDevicesCache !== false;
265
597
  useCachedGroupMetadata = useCachedGroupMetadata !== false && !isStatus;
266
598
  const participants = [];
267
- const destinationJid = !isStatus ? jidEncode(user, isLid ? 'lid' : isGroup ? 'g.us' : 's.whatsapp.net') : statusJid;
599
+ const destinationJid = !isStatus ? finalJid : statusJid;
268
600
  const binaryNodeContent = [];
269
601
  const devices = [];
270
602
  const meMsg = {
@@ -283,7 +615,11 @@ export const makeMessagesSocket = (config) => {
283
615
  additionalAttributes = { ...additionalAttributes, device_fanout: 'false' };
284
616
  }
285
617
  const { user, device } = jidDecode(participant.jid);
286
- devices.push({ user, device });
618
+ devices.push({
619
+ user,
620
+ device,
621
+ wireJid: participant.jid // Use the participant JID as wire JID
622
+ });
287
623
  }
288
624
  await authState.keys.transaction(async () => {
289
625
  const mediaType = getMediaType(message);
@@ -342,9 +678,10 @@ export const makeMessagesSocket = (config) => {
342
678
  participantsList.push(...statusJidList);
343
679
  }
344
680
  if (!isStatus) {
681
+ const groupAddressingMode = groupData?.addressingMode || (isLid ? 'lid' : 'pn');
345
682
  additionalAttributes = {
346
683
  ...additionalAttributes,
347
- addressing_mode: groupData?.addressingMode || 'pn'
684
+ addressing_mode: groupAddressingMode
348
685
  };
349
686
  }
350
687
  const additionalDevices = await getUSyncDevices(participantsList, !!useUserDevicesCache, false);
@@ -355,19 +692,24 @@ export const makeMessagesSocket = (config) => {
355
692
  throw new Boom('Per-jid patching is not supported in groups');
356
693
  }
357
694
  const bytes = encodeWAMessage(patched);
695
+ // This should match the group's addressing mode and conversation context
696
+ const groupAddressingMode = groupData?.addressingMode || (isLid ? 'lid' : 'pn');
697
+ const groupSenderIdentity = groupAddressingMode === 'lid' && meLid ? meLid : meId;
358
698
  const { ciphertext, senderKeyDistributionMessage } = await signalRepository.encryptGroupMessage({
359
699
  group: destinationJid,
360
700
  data: bytes,
361
- meId
701
+ meId: groupSenderIdentity
362
702
  });
363
703
  const senderKeyJids = [];
364
704
  // ensure a connection is established with every device
365
- for (const { user, device } of devices) {
366
- const jid = jidEncode(user, groupData?.addressingMode === 'lid' ? 'lid' : 's.whatsapp.net', device);
367
- if (!senderKeyMap[jid] || !!participant) {
368
- senderKeyJids.push(jid);
705
+ for (const device of devices) {
706
+ // This preserves the LID migration results from getUSyncDevices
707
+ const deviceJid = device.wireJid;
708
+ const hasKey = !!senderKeyMap[deviceJid];
709
+ if (!hasKey || !!participant) {
710
+ senderKeyJids.push(deviceJid);
369
711
  // store that this person has had the sender keys sent to them
370
- senderKeyMap[jid] = true;
712
+ senderKeyMap[deviceJid] = true;
371
713
  }
372
714
  }
373
715
  // if there are some participants with whom the session has not been established
@@ -393,23 +735,54 @@ export const makeMessagesSocket = (config) => {
393
735
  await authState.keys.set({ 'sender-key-memory': { [jid]: senderKeyMap } });
394
736
  }
395
737
  else {
396
- const { user: meUser } = jidDecode(meId);
738
+ const { user: ownUser } = jidDecode(ownId);
397
739
  if (!participant) {
398
- devices.push({ user });
399
- if (user !== meUser) {
400
- devices.push({ user: meUser });
740
+ const targetUserServer = isLid ? 'lid' : 's.whatsapp.net';
741
+ devices.push({
742
+ user,
743
+ device: 0,
744
+ wireJid: jidEncode(user, targetUserServer, 0)
745
+ });
746
+ // Own user matches conversation addressing mode
747
+ if (user !== ownUser) {
748
+ const ownUserServer = isLid ? 'lid' : 's.whatsapp.net';
749
+ const ownUserForAddressing = isLid && meLid ? jidDecode(meLid).user : jidDecode(meId).user;
750
+ devices.push({
751
+ user: ownUserForAddressing,
752
+ device: 0,
753
+ wireJid: jidEncode(ownUserForAddressing, ownUserServer, 0)
754
+ });
401
755
  }
402
756
  if (additionalAttributes?.['category'] !== 'peer') {
403
- const additionalDevices = await getUSyncDevices([meId, jid], !!useUserDevicesCache, true);
404
- devices.push(...additionalDevices);
757
+ // Clear placeholders and enumerate actual devices
758
+ devices.length = 0;
759
+ // Use conversation-appropriate sender identity
760
+ const senderIdentity = isLid && meLid
761
+ ? jidEncode(jidDecode(meLid)?.user, 'lid', undefined)
762
+ : jidEncode(jidDecode(meId)?.user, 's.whatsapp.net', undefined);
763
+ // Enumerate devices for sender and target with consistent addressing
764
+ const sessionDevices = await getUSyncDevices([senderIdentity, jid], false, false);
765
+ devices.push(...sessionDevices);
766
+ logger.debug({
767
+ deviceCount: devices.length,
768
+ devices: devices.map(d => `${d.user}:${d.device}@${jidDecode(d.wireJid)?.server}`)
769
+ }, 'Device enumeration complete with unified addressing');
405
770
  }
406
771
  }
407
772
  const allJids = [];
408
773
  const meJids = [];
409
774
  const otherJids = [];
410
- for (const { user, device } of devices) {
411
- const isMe = user === meUser;
412
- const jid = jidEncode(isMe && isLid ? authState.creds?.me?.lid.split(':')[0] || user : user, isLid ? 'lid' : 's.whatsapp.net', device);
775
+ const { user: mePnUser } = jidDecode(meId);
776
+ const { user: meLidUser } = meLid ? jidDecode(meLid) : { user: null };
777
+ for (const { user, wireJid } of devices) {
778
+ const isExactSenderDevice = wireJid === meId || (meLid && wireJid === meLid);
779
+ if (isExactSenderDevice) {
780
+ logger.debug({ wireJid, meId, meLid }, 'Skipping exact sender device (whatsmeow pattern)');
781
+ continue;
782
+ }
783
+ // Check if this is our device (could match either PN or LID user)
784
+ const isMe = user === mePnUser || (meLidUser && user === meLidUser);
785
+ const jid = wireJid;
413
786
  if (isMe) {
414
787
  meJids.push(jid);
415
788
  }
@@ -418,10 +791,11 @@ export const makeMessagesSocket = (config) => {
418
791
  }
419
792
  allJids.push(jid);
420
793
  }
421
- await assertSessions(allJids, false);
794
+ await assertSessions([...otherJids, ...meJids], false);
422
795
  const [{ nodes: meNodes, shouldIncludeDeviceIdentity: s1 }, { nodes: otherNodes, shouldIncludeDeviceIdentity: s2 }] = await Promise.all([
423
- createParticipantNodes(meJids, meMsg, extraAttrs),
424
- createParticipantNodes(otherJids, message, extraAttrs)
796
+ // For own devices: use DSM if available (1:1 chats only)
797
+ createParticipantNodes(meJids, meMsg || message, extraAttrs),
798
+ createParticipantNodes(otherJids, message, extraAttrs, meMsg)
425
799
  ]);
426
800
  participants.push(...meNodes);
427
801
  participants.push(...otherNodes);
@@ -446,6 +820,7 @@ export const makeMessagesSocket = (config) => {
446
820
  tag: 'message',
447
821
  attrs: {
448
822
  id: msgId,
823
+ to: destinationJid,
449
824
  type: getMessageType(message),
450
825
  ...(additionalAttributes || {})
451
826
  },
@@ -492,6 +867,9 @@ export const makeMessagesSocket = (config) => {
492
867
  if (message.pollCreationMessage || message.pollCreationMessageV2 || message.pollCreationMessageV3) {
493
868
  return 'poll';
494
869
  }
870
+ if (message.eventMessage) {
871
+ return 'event';
872
+ }
495
873
  return 'text';
496
874
  };
497
875
  const getMediaType = (message) => {
@@ -654,12 +1032,14 @@ export const makeMessagesSocket = (config) => {
654
1032
  }),
655
1033
  //TODO: CACHE
656
1034
  getProfilePicUrl: sock.profilePictureUrl,
1035
+ getCallLink: sock.createCallLink,
657
1036
  upload: waUploadToServer,
658
1037
  mediaCache: config.mediaCache,
659
1038
  options: config.options,
660
1039
  messageId: generateMessageIDV2(sock.user?.id),
661
1040
  ...options
662
1041
  });
1042
+ const isEventMsg = 'event' in content && !!content.event;
663
1043
  const isDeleteMsg = 'delete' in content && !!content.delete;
664
1044
  const isEditMsg = 'edit' in content && !!content.edit;
665
1045
  const isPinMsg = 'pin' in content && !!content.pin;
@@ -690,6 +1070,14 @@ export const makeMessagesSocket = (config) => {
690
1070
  }
691
1071
  });
692
1072
  }
1073
+ else if (isEventMsg) {
1074
+ additionalNodes.push({
1075
+ tag: 'meta',
1076
+ attrs: {
1077
+ event_type: 'creation'
1078
+ }
1079
+ });
1080
+ }
693
1081
  if ('cachedGroupMetadata' in options) {
694
1082
  console.warn('cachedGroupMetadata in sendMessage are deprecated, now cachedGroupMetadata is part of the socket config.');
695
1083
  }