@periskope/baileys 6.7.18-17-3 → 6.7.18-17-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.
@@ -8,6 +8,7 @@ export declare class GroupCipher {
8
8
  private readonly senderKeyStore;
9
9
  private readonly senderKeyName;
10
10
  constructor(senderKeyStore: SenderKeyStore, senderKeyName: SenderKeyName);
11
+ private queueJob;
11
12
  encrypt(paddedPlaintext: Uint8Array | string): Promise<Uint8Array>;
12
13
  decrypt(senderKeyMessageBytes: Uint8Array): Promise<Uint8Array>;
13
14
  private getSenderKey;
@@ -1,45 +1,55 @@
1
1
  "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
2
5
  Object.defineProperty(exports, "__esModule", { value: true });
3
6
  exports.GroupCipher = void 0;
4
- /* @ts-ignore */
5
7
  const crypto_1 = require("libsignal/src/crypto");
8
+ const queue_job_1 = __importDefault(require("./queue-job"));
6
9
  const sender_key_message_1 = require("./sender-key-message");
7
10
  class GroupCipher {
8
11
  constructor(senderKeyStore, senderKeyName) {
9
12
  this.senderKeyStore = senderKeyStore;
10
13
  this.senderKeyName = senderKeyName;
11
14
  }
15
+ queueJob(awaitable) {
16
+ return (0, queue_job_1.default)(this.senderKeyName.toString(), awaitable);
17
+ }
12
18
  async encrypt(paddedPlaintext) {
13
- const record = await this.senderKeyStore.loadSenderKey(this.senderKeyName);
14
- if (!record) {
15
- throw new Error('No SenderKeyRecord found for encryption');
16
- }
17
- const senderKeyState = record.getSenderKeyState();
18
- if (!senderKeyState) {
19
- throw new Error('No session to encrypt message');
20
- }
21
- const iteration = senderKeyState.getSenderChainKey().getIteration();
22
- const senderKey = this.getSenderKey(senderKeyState, iteration === 0 ? 0 : iteration + 1);
23
- const ciphertext = await this.getCipherText(senderKey.getIv(), senderKey.getCipherKey(), paddedPlaintext);
24
- const senderKeyMessage = new sender_key_message_1.SenderKeyMessage(senderKeyState.getKeyId(), senderKey.getIteration(), ciphertext, senderKeyState.getSigningKeyPrivate());
25
- await this.senderKeyStore.storeSenderKey(this.senderKeyName, record);
26
- return senderKeyMessage.serialize();
19
+ return await this.queueJob(async () => {
20
+ const record = await this.senderKeyStore.loadSenderKey(this.senderKeyName);
21
+ if (!record) {
22
+ throw new Error('No SenderKeyRecord found for encryption');
23
+ }
24
+ const senderKeyState = record.getSenderKeyState();
25
+ if (!senderKeyState) {
26
+ throw new Error('No session to encrypt message');
27
+ }
28
+ const iteration = senderKeyState.getSenderChainKey().getIteration();
29
+ const senderKey = this.getSenderKey(senderKeyState, iteration === 0 ? 0 : iteration + 1);
30
+ const ciphertext = await this.getCipherText(senderKey.getIv(), senderKey.getCipherKey(), paddedPlaintext);
31
+ const senderKeyMessage = new sender_key_message_1.SenderKeyMessage(senderKeyState.getKeyId(), senderKey.getIteration(), ciphertext, senderKeyState.getSigningKeyPrivate());
32
+ await this.senderKeyStore.storeSenderKey(this.senderKeyName, record);
33
+ return senderKeyMessage.serialize();
34
+ });
27
35
  }
28
36
  async decrypt(senderKeyMessageBytes) {
29
- const record = await this.senderKeyStore.loadSenderKey(this.senderKeyName);
30
- if (!record) {
31
- throw new Error('No SenderKeyRecord found for decryption');
32
- }
33
- const senderKeyMessage = new sender_key_message_1.SenderKeyMessage(null, null, null, null, senderKeyMessageBytes);
34
- const senderKeyState = record.getSenderKeyState(senderKeyMessage.getKeyId());
35
- if (!senderKeyState) {
36
- throw new Error('No session found to decrypt message');
37
- }
38
- senderKeyMessage.verifySignature(senderKeyState.getSigningKeyPublic());
39
- const senderKey = this.getSenderKey(senderKeyState, senderKeyMessage.getIteration());
40
- const plaintext = await this.getPlainText(senderKey.getIv(), senderKey.getCipherKey(), senderKeyMessage.getCipherText());
41
- await this.senderKeyStore.storeSenderKey(this.senderKeyName, record);
42
- return plaintext;
37
+ return await this.queueJob(async () => {
38
+ const record = await this.senderKeyStore.loadSenderKey(this.senderKeyName);
39
+ if (!record) {
40
+ throw new Error('No SenderKeyRecord found for decryption');
41
+ }
42
+ const senderKeyMessage = new sender_key_message_1.SenderKeyMessage(null, null, null, null, senderKeyMessageBytes);
43
+ const senderKeyState = record.getSenderKeyState(senderKeyMessage.getKeyId());
44
+ if (!senderKeyState) {
45
+ throw new Error('No session found to decrypt message');
46
+ }
47
+ senderKeyMessage.verifySignature(senderKeyState.getSigningKeyPublic());
48
+ const senderKey = this.getSenderKey(senderKeyState, senderKeyMessage.getIteration());
49
+ const plaintext = await this.getPlainText(senderKey.getIv(), senderKey.getCipherKey(), senderKeyMessage.getCipherText());
50
+ await this.senderKeyStore.storeSenderKey(this.senderKeyName, record);
51
+ return plaintext;
52
+ });
43
53
  }
44
54
  getSenderKey(senderKeyState, iteration) {
45
55
  let senderChainKey = senderKeyState.getSenderChainKey();
@@ -1,2 +1 @@
1
1
  export default function queueJob<T>(bucket: string | number, awaitable: () => Promise<T>): Promise<T>;
2
- //# sourceMappingURL=queue-job.d.ts.map
@@ -1,7 +1,11 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.default = queueJob;
1
4
  const _queueAsyncBuckets = new Map();
2
5
  const _gcLimit = 10000;
3
6
  async function _asyncQueueExecutor(queue, cleanup) {
4
7
  let offt = 0;
8
+ // eslint-disable-next-line no-constant-condition
5
9
  while (true) {
6
10
  const limit = Math.min(queue.length, _gcLimit);
7
11
  for (let i = offt; i < limit; i++) {
@@ -28,7 +32,7 @@ async function _asyncQueueExecutor(queue, cleanup) {
28
32
  }
29
33
  cleanup();
30
34
  }
31
- export default function queueJob(bucket, awaitable) {
35
+ function queueJob(bucket, awaitable) {
32
36
  // Skip name assignment since it's readonly in strict mode
33
37
  if (typeof bucket !== 'string') {
34
38
  console.warn('Unhandled bucket type (for naming):', typeof bucket, bucket);
@@ -51,4 +55,3 @@ export default function queueJob(bucket, awaitable) {
51
55
  }
52
56
  return job;
53
57
  }
54
- //# sourceMappingURL=queue-job.js.map
@@ -35,22 +35,27 @@ var __importStar = (this && this.__importStar) || (function () {
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.makeLibSignalRepository = makeLibSignalRepository;
37
37
  const libsignal = __importStar(require("libsignal"));
38
+ /* @ts-ignore */
39
+ const lru_cache_1 = require("lru-cache");
38
40
  const Utils_1 = require("../Utils");
39
41
  const WABinary_1 = require("../WABinary");
40
42
  const sender_key_name_1 = require("./Group/sender-key-name");
41
43
  const sender_key_record_1 = require("./Group/sender-key-record");
42
44
  const Group_1 = require("./Group");
45
+ const lid_mapping_1 = require("./lid-mapping");
43
46
  function makeLibSignalRepository(auth) {
44
- const storage = signalStorage(auth);
45
- const parsedKeys = auth.keys;
46
- return {
47
+ const lidMapping = new lid_mapping_1.LIDMappingStore(auth.keys);
48
+ const storage = signalStorage(auth, lidMapping);
49
+ // Simple operation-level deduplication (5 minutes)
50
+ const recentMigrations = new lru_cache_1.LRUCache({
51
+ max: 500,
52
+ ttl: 5 * 60 * 1000
53
+ });
54
+ const repository = {
47
55
  decryptGroupMessage({ group, authorJid, msg }) {
48
56
  const senderName = jidToSignalSenderKeyName(group, authorJid);
49
57
  const cipher = new Group_1.GroupCipher(storage, senderName);
50
- // Use transaction to ensure atomicity
51
- return parsedKeys.transaction(async () => {
52
- return cipher.decrypt(msg);
53
- });
58
+ return cipher.decrypt(msg);
54
59
  },
55
60
  async processSenderKeyDistributionMessage({ item, authorJid }) {
56
61
  const builder = new Group_1.GroupSessionBuilder(storage);
@@ -64,88 +69,200 @@ function makeLibSignalRepository(auth) {
64
69
  if (!senderKey) {
65
70
  await storage.storeSenderKey(senderName, new sender_key_record_1.SenderKeyRecord());
66
71
  }
67
- return parsedKeys.transaction(async () => {
68
- const { [senderNameStr]: senderKey } = await auth.keys.get('sender-key', [senderNameStr]);
69
- if (!senderKey) {
70
- await storage.storeSenderKey(senderName, new sender_key_record_1.SenderKeyRecord());
71
- }
72
- await builder.process(senderName, senderMsg);
73
- });
72
+ await builder.process(senderName, senderMsg);
74
73
  },
75
74
  async decryptMessage({ jid, type, ciphertext }) {
76
75
  const addr = jidToSignalProtocolAddress(jid);
77
76
  const session = new libsignal.SessionCipher(storage, addr);
78
- // Use transaction to ensure atomicity
79
- return parsedKeys.transaction(async () => {
80
- let result;
81
- switch (type) {
82
- case 'pkmsg':
83
- result = await session.decryptPreKeyWhisperMessage(ciphertext);
84
- break;
85
- case 'msg':
86
- result = await session.decryptWhisperMessage(ciphertext);
87
- break;
88
- default:
89
- throw new Error(`Unknown message type: ${type}`);
90
- }
91
- return result;
92
- });
77
+ let result;
78
+ switch (type) {
79
+ case 'pkmsg':
80
+ result = await session.decryptPreKeyWhisperMessage(ciphertext);
81
+ break;
82
+ case 'msg':
83
+ result = await session.decryptWhisperMessage(ciphertext);
84
+ break;
85
+ default:
86
+ throw new Error(`Unknown message type: ${type}`);
87
+ }
88
+ return result;
93
89
  },
94
90
  async encryptMessage({ jid, data }) {
95
- const addr = jidToSignalProtocolAddress(jid);
91
+ // LID SINGLE SOURCE OF TRUTH: Always prefer LID when available
92
+ let encryptionJid = jid;
93
+ // Check for LID mapping and use it if session exists
94
+ if (jid.includes('@s.whatsapp.net')) {
95
+ const lidForPN = await lidMapping.getLIDForPN(jid);
96
+ if (lidForPN === null || lidForPN === void 0 ? void 0 : lidForPN.includes('@lid')) {
97
+ const lidAddr = jidToSignalProtocolAddress(lidForPN);
98
+ const { [lidAddr.toString()]: lidSession } = await auth.keys.get('session', [lidAddr.toString()]);
99
+ if (lidSession) {
100
+ // LID session exists, use it
101
+ encryptionJid = lidForPN;
102
+ }
103
+ else {
104
+ // Try to migrate if PN session exists
105
+ const pnAddr = jidToSignalProtocolAddress(jid);
106
+ const { [pnAddr.toString()]: pnSession } = await auth.keys.get('session', [pnAddr.toString()]);
107
+ if (pnSession) {
108
+ // Migrate PN to LID
109
+ await repository.migrateSession(jid, lidForPN);
110
+ encryptionJid = lidForPN;
111
+ }
112
+ }
113
+ }
114
+ }
115
+ const addr = jidToSignalProtocolAddress(encryptionJid);
96
116
  const cipher = new libsignal.SessionCipher(storage, addr);
97
- // Use transaction to ensure atomicity
98
- return parsedKeys.transaction(async () => {
99
- const { type: sigType, body } = await cipher.encrypt(data);
100
- const type = sigType === 3 ? 'pkmsg' : 'msg';
101
- return { type, ciphertext: Buffer.from(body, 'binary') };
102
- });
117
+ const { type: sigType, body } = await cipher.encrypt(data);
118
+ const type = sigType === 3 ? 'pkmsg' : 'msg';
119
+ return { type, ciphertext: Buffer.from(body, 'binary') };
103
120
  },
104
121
  async encryptGroupMessage({ group, meId, data }) {
105
122
  const senderName = jidToSignalSenderKeyName(group, meId);
106
123
  const builder = new Group_1.GroupSessionBuilder(storage);
107
124
  const senderNameStr = senderName.toString();
108
- return parsedKeys.transaction(async () => {
109
- const { [senderNameStr]: senderKey } = await auth.keys.get('sender-key', [senderNameStr]);
110
- if (!senderKey) {
111
- await storage.storeSenderKey(senderName, new sender_key_record_1.SenderKeyRecord());
112
- }
113
- const senderKeyDistributionMessage = await builder.create(senderName);
114
- const session = new Group_1.GroupCipher(storage, senderName);
115
- const ciphertext = await session.encrypt(data);
116
- return {
117
- ciphertext,
118
- senderKeyDistributionMessage: senderKeyDistributionMessage.serialize()
119
- };
120
- });
125
+ const { [senderNameStr]: senderKey } = await auth.keys.get('sender-key', [senderNameStr]);
126
+ if (!senderKey) {
127
+ await storage.storeSenderKey(senderName, new sender_key_record_1.SenderKeyRecord());
128
+ }
129
+ const senderKeyDistributionMessage = await builder.create(senderName);
130
+ const session = new Group_1.GroupCipher(storage, senderName);
131
+ const ciphertext = await session.encrypt(data);
132
+ return {
133
+ ciphertext,
134
+ senderKeyDistributionMessage: senderKeyDistributionMessage.serialize()
135
+ };
121
136
  },
122
137
  async injectE2ESession({ jid, session }) {
123
138
  const cipher = new libsignal.SessionBuilder(storage, jidToSignalProtocolAddress(jid));
124
- return parsedKeys.transaction(async () => {
125
- await cipher.initOutgoing(session);
126
- });
139
+ await cipher.initOutgoing(session);
127
140
  },
128
141
  jidToSignalProtocolAddress(jid) {
129
142
  return jidToSignalProtocolAddress(jid).toString();
143
+ },
144
+ async storeLIDPNMapping(lid, pn) {
145
+ await lidMapping.storeLIDPNMapping(lid, pn);
146
+ },
147
+ getLIDMappingStore() {
148
+ return lidMapping;
149
+ },
150
+ async validateSession(jid) {
151
+ try {
152
+ const addr = jidToSignalProtocolAddress(jid);
153
+ const session = await storage.loadSession(addr.toString());
154
+ if (!session) {
155
+ return { exists: false, reason: 'no session' };
156
+ }
157
+ if (!session.haveOpenSession()) {
158
+ return { exists: false, reason: 'no open session' };
159
+ }
160
+ return { exists: true };
161
+ }
162
+ catch (error) {
163
+ return { exists: false, reason: 'validation error' };
164
+ }
165
+ },
166
+ async deleteSession(jid) {
167
+ const addr = jidToSignalProtocolAddress(jid);
168
+ return auth.keys.transaction(async () => {
169
+ await auth.keys.set({ session: { [addr.toString()]: null } });
170
+ });
171
+ },
172
+ async migrateSession(fromJid, toJid) {
173
+ // Only migrate PN → LID
174
+ if (!fromJid.includes('@s.whatsapp.net') || !toJid.includes('@lid')) {
175
+ return;
176
+ }
177
+ const fromDecoded = (0, WABinary_1.jidDecode)(fromJid);
178
+ const toDecoded = (0, WABinary_1.jidDecode)(toJid);
179
+ if (!fromDecoded || !toDecoded)
180
+ return;
181
+ const deviceId = fromDecoded.device || 0;
182
+ const migrationKey = `${fromDecoded.user}.${deviceId}→${toDecoded.user}.${deviceId}`;
183
+ // Check if recently migrated (5 min window)
184
+ if (recentMigrations.has(migrationKey)) {
185
+ return;
186
+ }
187
+ // Check if LID session already exists
188
+ const lidAddr = jidToSignalProtocolAddress(toJid);
189
+ const { [lidAddr.toString()]: lidExists } = await auth.keys.get('session', [lidAddr.toString()]);
190
+ if (lidExists) {
191
+ recentMigrations.set(migrationKey, true);
192
+ return;
193
+ }
194
+ return auth.keys.transaction(async () => {
195
+ // Store mapping
196
+ await lidMapping.storeLIDPNMapping(toJid, fromJid);
197
+ // Load and copy session
198
+ const fromAddr = jidToSignalProtocolAddress(fromJid);
199
+ const fromSession = await storage.loadSession(fromAddr.toString());
200
+ if (fromSession === null || fromSession === void 0 ? void 0 : fromSession.haveOpenSession()) {
201
+ // Deep copy session to prevent reference issues
202
+ const sessionBytes = fromSession.serialize();
203
+ const copiedSession = libsignal.SessionRecord.deserialize(sessionBytes);
204
+ // Store at LID address
205
+ await storage.storeSession(lidAddr.toString(), copiedSession);
206
+ // Delete PN session - maintain single encryption layer
207
+ await auth.keys.set({ session: { [fromAddr.toString()]: null } });
208
+ }
209
+ recentMigrations.set(migrationKey, true);
210
+ });
211
+ },
212
+ async encryptMessageWithWire({ encryptionJid, wireJid, data }) {
213
+ const result = await repository.encryptMessage({ jid: encryptionJid, data });
214
+ return { ...result, wireJid };
215
+ },
216
+ destroy() {
217
+ recentMigrations.clear();
130
218
  }
131
219
  };
220
+ return repository;
132
221
  }
133
222
  const jidToSignalProtocolAddress = (jid) => {
134
- const { user, device } = (0, WABinary_1.jidDecode)(jid);
135
- return new libsignal.ProtocolAddress(user, device || 0);
223
+ const decoded = (0, WABinary_1.jidDecode)(jid);
224
+ const { user, device, server } = decoded;
225
+ // LID addresses get _1 suffix for Signal protocol
226
+ const signalUser = server === 'lid' ? `${user}_1` : user;
227
+ const finalDevice = device || 0;
228
+ return new libsignal.ProtocolAddress(signalUser, finalDevice);
136
229
  };
137
230
  const jidToSignalSenderKeyName = (group, user) => {
138
231
  return new sender_key_name_1.SenderKeyName(group, jidToSignalProtocolAddress(user));
139
232
  };
140
- function signalStorage({ creds, keys }) {
233
+ function signalStorage({ creds, keys }, lidMapping) {
141
234
  return {
142
235
  loadSession: async (id) => {
143
- const { [id]: sess } = await keys.get('session', [id]);
144
- if (sess) {
145
- return libsignal.SessionRecord.deserialize(sess);
236
+ try {
237
+ // LID SINGLE SOURCE OF TRUTH: Auto-redirect PN to LID if mapping exists
238
+ let actualId = id;
239
+ if (id.includes('.') && !id.includes('_1')) {
240
+ // This is a PN signal address format (e.g., "1234567890.0")
241
+ // Convert back to JID to check for LID mapping
242
+ const parts = id.split('.');
243
+ const device = parts[1] || '0';
244
+ const pnJid = device === '0' ? `${parts[0]}@s.whatsapp.net` : `${parts[0]}:${device}@s.whatsapp.net`;
245
+ const lidForPN = await lidMapping.getLIDForPN(pnJid);
246
+ if (lidForPN === null || lidForPN === void 0 ? void 0 : lidForPN.includes('@lid')) {
247
+ const lidAddr = jidToSignalProtocolAddress(lidForPN);
248
+ const lidId = lidAddr.toString();
249
+ // Check if LID session exists
250
+ const { [lidId]: lidSession } = await keys.get('session', [lidId]);
251
+ if (lidSession) {
252
+ actualId = lidId;
253
+ }
254
+ }
255
+ }
256
+ const { [actualId]: sess } = await keys.get('session', [actualId]);
257
+ if (sess) {
258
+ return libsignal.SessionRecord.deserialize(sess);
259
+ }
260
+ }
261
+ catch (e) {
262
+ return null;
146
263
  }
264
+ return null;
147
265
  },
148
- // TODO: Replace with libsignal.SessionRecord when type exports are added to libsignal
149
266
  storeSession: async (id, session) => {
150
267
  await keys.set({ session: { [id]: session.serialize() } });
151
268
  },
@@ -0,0 +1,19 @@
1
+ import type { SignalKeyStoreWithTransaction } from '../Types';
2
+ export declare class LIDMappingStore {
3
+ private readonly keys;
4
+ private getLIDFromPN?;
5
+ private getPNFromLID?;
6
+ constructor(keys: SignalKeyStoreWithTransaction, getLIDFromPN?: (pn: string) => Promise<string | null>, getPNFromLID?: (lid: string) => Promise<string | null>);
7
+ /**
8
+ * Store LID-PN mapping - USER LEVEL
9
+ */
10
+ storeLIDPNMapping(lid: string, pn: string): Promise<void>;
11
+ /**
12
+ * Get LID for PN - Returns device-specific LID based on user mapping
13
+ */
14
+ getLIDForPN(pn: string): Promise<string | null>;
15
+ /**
16
+ * Get PN for LID - USER LEVEL with device construction
17
+ */
18
+ getPNForLID(lid: string): Promise<string | null>;
19
+ }
@@ -0,0 +1,115 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.LIDMappingStore = void 0;
7
+ const logger_1 = __importDefault(require("../Utils/logger"));
8
+ const WABinary_1 = require("../WABinary");
9
+ class LIDMappingStore {
10
+ constructor(keys, getLIDFromPN, getPNFromLID) {
11
+ this.keys = keys;
12
+ this.getLIDFromPN = getLIDFromPN;
13
+ this.getPNFromLID = getPNFromLID;
14
+ }
15
+ /**
16
+ * Store LID-PN mapping - USER LEVEL
17
+ */
18
+ async storeLIDPNMapping(lid, pn) {
19
+ // Validate inputs
20
+ if (!(((0, WABinary_1.isLidUser)(lid) && (0, WABinary_1.isJidUser)(pn)) || ((0, WABinary_1.isJidUser)(lid) && (0, WABinary_1.isLidUser)(pn)))) {
21
+ logger_1.default.warn(`Invalid LID-PN mapping: ${lid}, ${pn}`);
22
+ return;
23
+ }
24
+ const [lidJid, pnJid] = (0, WABinary_1.isLidUser)(lid) ? [lid, pn] : [pn, lid];
25
+ const lidDecoded = (0, WABinary_1.jidDecode)(lidJid);
26
+ const pnDecoded = (0, WABinary_1.jidDecode)(pnJid);
27
+ if (!lidDecoded || !pnDecoded)
28
+ return;
29
+ const pnUser = pnDecoded.user;
30
+ const lidUser = lidDecoded.user;
31
+ logger_1.default.trace(`Storing USER LID mapping: PN ${pnUser} → LID ${lidUser}`);
32
+ await this.keys.transaction(async () => {
33
+ await this.keys.set({
34
+ 'lid-mapping': {
35
+ [pnUser]: lidUser, // "554396160286" -> "102765716062358"
36
+ [`${lidUser}_reverse`]: pnUser // "102765716062358_reverse" -> "554396160286"
37
+ }
38
+ });
39
+ });
40
+ logger_1.default.trace(`USER LID mapping stored: PN ${pnUser} → LID ${lidUser}`);
41
+ }
42
+ /**
43
+ * Get LID for PN - Returns device-specific LID based on user mapping
44
+ */
45
+ async getLIDForPN(pn) {
46
+ if (!(0, WABinary_1.isJidUser)(pn))
47
+ return null;
48
+ const decoded = (0, WABinary_1.jidDecode)(pn);
49
+ if (!decoded)
50
+ return null;
51
+ // Look up user-level mapping (whatsmeow approach)
52
+ const pnUser = decoded.user;
53
+ const stored = await this.keys.get('lid-mapping', [pnUser]);
54
+ let lidUser = stored[pnUser];
55
+ if (!lidUser) {
56
+ if (this.getLIDFromPN) {
57
+ const lid = await this.getLIDFromPN(`${pnUser}@s.whatsapp.net`);
58
+ if (lid) {
59
+ const decodedLid = (0, WABinary_1.jidDecode)(lid);
60
+ if (decodedLid) {
61
+ lidUser = decodedLid.user;
62
+ await this.storeLIDPNMapping(`${lidUser}@lid`, `${pnUser}@s.whatsapp.net`);
63
+ }
64
+ }
65
+ }
66
+ }
67
+ if (!lidUser) {
68
+ logger_1.default.trace(`No LID mapping found for PN user ${pnUser}`);
69
+ return null;
70
+ }
71
+ if (typeof lidUser !== 'string')
72
+ return null;
73
+ // Push the PN device ID to the LID to maintain device separation
74
+ const pnDevice = decoded.device !== undefined ? decoded.device : 0;
75
+ const deviceSpecificLid = `${lidUser}:${pnDevice}@lid`;
76
+ logger_1.default.trace(`getLIDForPN: ${pn} → ${deviceSpecificLid} (user mapping with device ${pnDevice})`);
77
+ return deviceSpecificLid;
78
+ }
79
+ /**
80
+ * Get PN for LID - USER LEVEL with device construction
81
+ */
82
+ async getPNForLID(lid) {
83
+ if (!(0, WABinary_1.isLidUser)(lid))
84
+ return null;
85
+ const decoded = (0, WABinary_1.jidDecode)(lid);
86
+ if (!decoded)
87
+ return null;
88
+ // Look up reverse user mapping
89
+ const lidUser = decoded.user;
90
+ const stored = await this.keys.get('lid-mapping', [`${lidUser}_reverse`]);
91
+ let pnUser = stored[`${lidUser}_reverse`];
92
+ if (!pnUser) {
93
+ if (this.getPNFromLID) {
94
+ const pn = await this.getPNFromLID(`${lidUser}@lid`);
95
+ if (pn) {
96
+ const decodedJid = (0, WABinary_1.jidDecode)(pn);
97
+ if (decodedJid) {
98
+ pnUser = decodedJid.user;
99
+ await this.storeLIDPNMapping(`${lidUser}@lid`, `${pnUser}@s.whatsapp.net`);
100
+ }
101
+ }
102
+ }
103
+ }
104
+ if (!pnUser || typeof pnUser !== 'string') {
105
+ logger_1.default.trace(`No reverse mapping found for LID user: ${lidUser}`);
106
+ return null;
107
+ }
108
+ // Construct device-specific PN JID
109
+ const lidDevice = decoded.device !== undefined ? decoded.device : 0;
110
+ const pnJid = `${pnUser}:${lidDevice}@s.whatsapp.net`;
111
+ logger_1.default.trace(`Found reverse mapping: ${lid} → ${pnJid}`);
112
+ return pnJid;
113
+ }
114
+ }
115
+ exports.LIDMappingStore = LIDMappingStore;
@@ -32,11 +32,13 @@ export declare const makeBusinessSocket: (config: SocketConfig) => {
32
32
  [_: string]: string;
33
33
  }>;
34
34
  sendPeerDataOperationMessage: (pdoMessage: import("../Types").WAProto.Message.IPeerDataOperationRequestMessage) => Promise<string>;
35
- createParticipantNodes: (jids: string[], message: import("../Types").WAProto.IMessage, extraAttrs?: BinaryNode["attrs"]) => Promise<{
35
+ createParticipantNodes: (jids: string[], message: import("../Types").WAProto.IMessage, extraAttrs?: BinaryNode["attrs"], dsmMessage?: import("../Types").WAProto.IMessage) => Promise<{
36
36
  nodes: BinaryNode[];
37
37
  shouldIncludeDeviceIdentity: boolean;
38
38
  }>;
39
- getUSyncDevices: (jids: string[], useCache: boolean, ignoreZeroDevices: boolean) => Promise<import("../WABinary").JidWithDevice[]>;
39
+ getUSyncDevices: (jids: string[], useCache: boolean, ignoreZeroDevices: boolean) => Promise<(import("../WABinary").JidWithDevice & {
40
+ wireJid: string;
41
+ })[]>;
40
42
  updateMediaMessage: (message: import("../Types").WAProto.IWebMessageInfo) => Promise<import("../Types").WAProto.IWebMessageInfo>;
41
43
  sendMessage: (jid: string, content: import("../Types").AnyMessageContent, options?: import("../Types").MiscMessageGenerationOptions) => Promise<import("../Types").WAProto.WebMessageInfo | undefined>;
42
44
  newsletterCreate: (name: string, description?: string) => Promise<import("../Types").NewsletterMetadata>;
@@ -31,11 +31,13 @@ declare const makeWASocket: (config: UserFacingSocketConfig) => {
31
31
  [_: string]: string;
32
32
  }>;
33
33
  sendPeerDataOperationMessage: (pdoMessage: import("../Types").WAProto.Message.IPeerDataOperationRequestMessage) => Promise<string>;
34
- createParticipantNodes: (jids: string[], message: import("../Types").WAProto.IMessage, extraAttrs?: import("..").BinaryNode["attrs"]) => Promise<{
34
+ createParticipantNodes: (jids: string[], message: import("../Types").WAProto.IMessage, extraAttrs?: import("..").BinaryNode["attrs"], dsmMessage?: import("../Types").WAProto.IMessage) => Promise<{
35
35
  nodes: import("..").BinaryNode[];
36
36
  shouldIncludeDeviceIdentity: boolean;
37
37
  }>;
38
- getUSyncDevices: (jids: string[], useCache: boolean, ignoreZeroDevices: boolean) => Promise<import("..").JidWithDevice[]>;
38
+ getUSyncDevices: (jids: string[], useCache: boolean, ignoreZeroDevices: boolean) => Promise<(import("..").JidWithDevice & {
39
+ wireJid: string;
40
+ })[]>;
39
41
  updateMediaMessage: (message: import("../Types").WAProto.IWebMessageInfo) => Promise<import("../Types").WAProto.IWebMessageInfo>;
40
42
  sendMessage: (jid: string, content: import("../Types").AnyMessageContent, options?: import("../Types").MiscMessageGenerationOptions) => Promise<import("../Types").WAProto.WebMessageInfo | undefined>;
41
43
  newsletterCreate: (name: string, description?: string) => Promise<import("../Types").NewsletterMetadata>;
@@ -21,11 +21,13 @@ export declare const makeMessagesRecvSocket: (config: SocketConfig) => {
21
21
  [_: string]: string;
22
22
  }>;
23
23
  sendPeerDataOperationMessage: (pdoMessage: proto.Message.IPeerDataOperationRequestMessage) => Promise<string>;
24
- createParticipantNodes: (jids: string[], message: proto.IMessage, extraAttrs?: BinaryNode["attrs"]) => Promise<{
24
+ createParticipantNodes: (jids: string[], message: proto.IMessage, extraAttrs?: BinaryNode["attrs"], dsmMessage?: proto.IMessage) => Promise<{
25
25
  nodes: BinaryNode[];
26
26
  shouldIncludeDeviceIdentity: boolean;
27
27
  }>;
28
- getUSyncDevices: (jids: string[], useCache: boolean, ignoreZeroDevices: boolean) => Promise<import("../WABinary").JidWithDevice[]>;
28
+ getUSyncDevices: (jids: string[], useCache: boolean, ignoreZeroDevices: boolean) => Promise<(import("../WABinary").JidWithDevice & {
29
+ wireJid: string;
30
+ })[]>;
29
31
  updateMediaMessage: (message: proto.IWebMessageInfo) => Promise<proto.IWebMessageInfo>;
30
32
  sendMessage: (jid: string, content: import("../Types").AnyMessageContent, options?: import("../Types").MiscMessageGenerationOptions) => Promise<proto.WebMessageInfo | undefined>;
31
33
  newsletterCreate: (name: string, description?: string) => Promise<import("../Types").NewsletterMetadata>;
@@ -661,7 +661,7 @@ const makeMessagesRecvSocket = (config) => {
661
661
  }
662
662
  };
663
663
  const handleMessage = async (node) => {
664
- var _a, _b, _c;
664
+ var _a, _b, _c, _d, _e, _f, _g;
665
665
  if (shouldIgnoreJid(node.attrs.from) && node.attrs.from !== '@s.whatsapp.net') {
666
666
  logger.debug({ key: node.attrs.key }, 'ignored message');
667
667
  await sendMessageAck(node);
@@ -697,6 +697,36 @@ const makeMessagesRecvSocket = (config) => {
697
697
  node.attrs.sender_pn) {
698
698
  ev.emit('chats.phoneNumberShare', { lid: node.attrs.from, jid: node.attrs.sender_pn });
699
699
  }
700
+ if ((_f = (_e = (_d = msg.message) === null || _d === void 0 ? void 0 : _d.protocolMessage) === null || _e === void 0 ? void 0 : _e.lidMigrationMappingSyncMessage) === null || _f === void 0 ? void 0 : _f.encodedMappingPayload) {
701
+ try {
702
+ const payload = msg.message.protocolMessage.lidMigrationMappingSyncMessage.encodedMappingPayload;
703
+ const decoded = WAProto_1.proto.LIDMigrationMappingSyncPayload.decode(payload);
704
+ logger.debug({
705
+ mappingCount: ((_g = decoded.pnToLidMappings) === null || _g === void 0 ? void 0 : _g.length) || 0,
706
+ timestamp: decoded.chatDbMigrationTimestamp
707
+ }, 'Received LID migration sync message from server');
708
+ const lidMapping = signalRepository.getLIDMappingStore();
709
+ if (decoded.pnToLidMappings && decoded.pnToLidMappings.length > 0) {
710
+ for (const mapping of decoded.pnToLidMappings) {
711
+ const pn = `${mapping.pn}@s.whatsapp.net`;
712
+ // Use latestLid if available, otherwise assignedLid (proper LID refresh)
713
+ const lidValue = mapping.latestLid || mapping.assignedLid;
714
+ const lid = `${lidValue}@lid`;
715
+ await lidMapping.storeLIDPNMapping(lid, pn);
716
+ logger.debug({
717
+ pn,
718
+ lid,
719
+ assignedLid: mapping.assignedLid,
720
+ latestLid: mapping.latestLid,
721
+ usedLatest: !!mapping.latestLid
722
+ }, 'Stored server-provided PN-LID mapping');
723
+ }
724
+ }
725
+ }
726
+ catch (error) {
727
+ logger.error({ error }, 'Failed to process LID migration sync message');
728
+ }
729
+ }
700
730
  try {
701
731
  await Promise.all([
702
732
  processingMutex.mutex(async () => {