@ellxz24/baileys 2.0.28 → 2.0.29
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/lib/Signal/libsignal.js +1 -1
- package/lib/Signal/libsignal.js.bak +231 -49
- package/lib/Signal/lid-mapping.js +56 -53
- package/lib/Signal/lid-mapping.js.bak +163 -0
- package/package.json +1 -1
package/lib/Signal/libsignal.js
CHANGED
|
@@ -46,7 +46,7 @@ const lid_mapping_1 = require("./lid-mapping");
|
|
|
46
46
|
const lru_cache_1 = require("lru-cache");
|
|
47
47
|
|
|
48
48
|
function makeLibSignalRepository(auth, logger, pnToLIDFunc) {
|
|
49
|
-
const lidMapping =
|
|
49
|
+
const lidMapping = lid_mapping_1.makeLIDMappingStore(auth.keys, logger, pnToLIDFunc);
|
|
50
50
|
const storage = signalStorage(auth, lidMapping);
|
|
51
51
|
const parsedKeys = auth.keys;
|
|
52
52
|
const migratedSessionCache = new lru_cache_1.LRUCache({
|
|
@@ -32,108 +32,290 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
32
32
|
return result;
|
|
33
33
|
};
|
|
34
34
|
})();
|
|
35
|
+
|
|
35
36
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
37
|
exports.makeLibSignalRepository = makeLibSignalRepository;
|
|
38
|
+
|
|
37
39
|
const libsignal = __importStar(require("libsignal"));
|
|
38
40
|
const Utils_1 = require("../Utils");
|
|
39
41
|
const WABinary_1 = require("../WABinary");
|
|
42
|
+
const Group_1 = require("./Group");
|
|
40
43
|
const sender_key_name_1 = require("./Group/sender-key-name");
|
|
41
44
|
const sender_key_record_1 = require("./Group/sender-key-record");
|
|
42
|
-
const
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
45
|
+
const lid_mapping_1 = require("./lid-mapping");
|
|
46
|
+
const lru_cache_1 = require("lru-cache");
|
|
47
|
+
|
|
48
|
+
function makeLibSignalRepository(auth, logger, pnToLIDFunc) {
|
|
49
|
+
const lidMapping = new lid_mapping_1.LIDMappingStore(auth.keys, logger, pnToLIDFunc);
|
|
50
|
+
const storage = signalStorage(auth, lidMapping);
|
|
51
|
+
const parsedKeys = auth.keys;
|
|
52
|
+
const migratedSessionCache = new lru_cache_1.LRUCache({
|
|
53
|
+
ttl: 7 * 24 * 60 * 60 * 1000,
|
|
54
|
+
ttlAutopurge: true,
|
|
55
|
+
updateAgeOnGet: true
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
const repository = {
|
|
46
59
|
decryptGroupMessage({ group, authorJid, msg }) {
|
|
47
60
|
const senderName = jidToSignalSenderKeyName(group, authorJid);
|
|
48
61
|
const cipher = new Group_1.GroupCipher(storage, senderName);
|
|
49
|
-
return
|
|
62
|
+
return parsedKeys.transaction(async () => {
|
|
63
|
+
return cipher.decrypt(msg);
|
|
64
|
+
}, group);
|
|
50
65
|
},
|
|
51
66
|
async processSenderKeyDistributionMessage({ item, authorJid }) {
|
|
52
67
|
const builder = new Group_1.GroupSessionBuilder(storage);
|
|
53
68
|
if (!item.groupId) {
|
|
54
|
-
throw new Error(
|
|
69
|
+
throw new Error("Group ID is required for sender key distribution message");
|
|
55
70
|
}
|
|
56
71
|
const senderName = jidToSignalSenderKeyName(item.groupId, authorJid);
|
|
57
72
|
const senderMsg = new Group_1.SenderKeyDistributionMessage(null, null, null, null, item.axolotlSenderKeyDistributionMessage);
|
|
58
73
|
const senderNameStr = senderName.toString();
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
74
|
+
return parsedKeys.transaction(async () => {
|
|
75
|
+
const { [senderNameStr]: senderKey } = await auth.keys.get("sender-key", [senderNameStr]);
|
|
76
|
+
if (!senderKey) {
|
|
77
|
+
await storage.storeSenderKey(senderName, new sender_key_record_1.SenderKeyRecord());
|
|
78
|
+
}
|
|
79
|
+
await builder.process(senderName, senderMsg);
|
|
80
|
+
}, item.groupId);
|
|
64
81
|
},
|
|
65
82
|
async decryptMessage({ jid, type, ciphertext }) {
|
|
66
83
|
const addr = jidToSignalProtocolAddress(jid);
|
|
67
84
|
const session = new libsignal.SessionCipher(storage, addr);
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
85
|
+
async function doDecrypt() {
|
|
86
|
+
let result;
|
|
87
|
+
switch (type) {
|
|
88
|
+
case "pkmsg":
|
|
89
|
+
result = await session.decryptPreKeyWhisperMessage(ciphertext);
|
|
90
|
+
break;
|
|
91
|
+
case "msg":
|
|
92
|
+
result = await session.decryptWhisperMessage(ciphertext);
|
|
93
|
+
break;
|
|
94
|
+
}
|
|
95
|
+
return result;
|
|
78
96
|
}
|
|
79
|
-
return
|
|
97
|
+
return parsedKeys.transaction(async () => {
|
|
98
|
+
return await doDecrypt();
|
|
99
|
+
}, jid);
|
|
80
100
|
},
|
|
81
101
|
async encryptMessage({ jid, data }) {
|
|
82
102
|
const addr = jidToSignalProtocolAddress(jid);
|
|
83
103
|
const cipher = new libsignal.SessionCipher(storage, addr);
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
104
|
+
return parsedKeys.transaction(async () => {
|
|
105
|
+
const { type: sigType, body } = await cipher.encrypt(data);
|
|
106
|
+
const type = sigType === 3 ? "pkmsg" : "msg";
|
|
107
|
+
return { type, ciphertext: Buffer.from(body, "binary") };
|
|
108
|
+
}, jid);
|
|
87
109
|
},
|
|
88
110
|
async encryptGroupMessage({ group, meId, data }) {
|
|
89
111
|
const senderName = jidToSignalSenderKeyName(group, meId);
|
|
90
112
|
const builder = new Group_1.GroupSessionBuilder(storage);
|
|
91
113
|
const senderNameStr = senderName.toString();
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
114
|
+
return parsedKeys.transaction(async () => {
|
|
115
|
+
const { [senderNameStr]: senderKey } = await auth.keys.get("sender-key", [senderNameStr]);
|
|
116
|
+
if (!senderKey) {
|
|
117
|
+
await storage.storeSenderKey(senderName, new sender_key_record_1.SenderKeyRecord());
|
|
118
|
+
}
|
|
119
|
+
const senderKeyDistributionMessage = await builder.create(senderName);
|
|
120
|
+
const session = new Group_1.GroupCipher(storage, senderName);
|
|
121
|
+
const ciphertext = await session.encrypt(data);
|
|
122
|
+
return {
|
|
123
|
+
ciphertext,
|
|
124
|
+
senderKeyDistributionMessage: senderKeyDistributionMessage.serialize()
|
|
125
|
+
};
|
|
126
|
+
}, group);
|
|
103
127
|
},
|
|
104
128
|
async injectE2ESession({ jid, session }) {
|
|
129
|
+
logger.trace({ jid }, "injecting E2EE session");
|
|
105
130
|
const cipher = new libsignal.SessionBuilder(storage, jidToSignalProtocolAddress(jid));
|
|
106
|
-
|
|
131
|
+
return parsedKeys.transaction(async () => {
|
|
132
|
+
await cipher.initOutgoing(session);
|
|
133
|
+
}, jid);
|
|
107
134
|
},
|
|
108
135
|
jidToSignalProtocolAddress(jid) {
|
|
109
136
|
return jidToSignalProtocolAddress(jid).toString();
|
|
137
|
+
},
|
|
138
|
+
lidMapping,
|
|
139
|
+
async validateSession(jid) {
|
|
140
|
+
try {
|
|
141
|
+
const addr = jidToSignalProtocolAddress(jid);
|
|
142
|
+
const session = await storage.loadSession(addr.toString());
|
|
143
|
+
if (!session) {
|
|
144
|
+
return { exists: false, reason: "no session" };
|
|
145
|
+
}
|
|
146
|
+
if (!session.haveOpenSession()) {
|
|
147
|
+
return { exists: false, reason: "no open session" };
|
|
148
|
+
}
|
|
149
|
+
return { exists: true };
|
|
150
|
+
}
|
|
151
|
+
catch (error) {
|
|
152
|
+
return { exists: false, reason: "validation error" };
|
|
153
|
+
}
|
|
154
|
+
},
|
|
155
|
+
async deleteSession(jids) {
|
|
156
|
+
if (!jids.length) return;
|
|
157
|
+
const sessionUpdates = {};
|
|
158
|
+
jids.forEach(jid => {
|
|
159
|
+
const addr = jidToSignalProtocolAddress(jid);
|
|
160
|
+
sessionUpdates[addr.toString()] = null;
|
|
161
|
+
});
|
|
162
|
+
return parsedKeys.transaction(async () => {
|
|
163
|
+
await auth.keys.set({ session: sessionUpdates });
|
|
164
|
+
}, `delete-${jids.length}-sessions`);
|
|
165
|
+
},
|
|
166
|
+
async migrateSession(fromJid, toJid) {
|
|
167
|
+
if (!fromJid || (!(0, WABinary_1.isLidUser)(toJid) && !(0, WABinary_1.isHostedLidUser)(toJid)))
|
|
168
|
+
return { migrated: 0, skipped: 0, total: 0 };
|
|
169
|
+
if (!(0, WABinary_1.isPnUser)(fromJid) && !(0, WABinary_1.isHostedPnUser)(fromJid)) {
|
|
170
|
+
return { migrated: 0, skipped: 0, total: 1 };
|
|
171
|
+
}
|
|
172
|
+
const { user } = (0, WABinary_1.jidDecode)(fromJid);
|
|
173
|
+
logger.debug({ fromJid }, "bulk device migration - loading all user devices");
|
|
174
|
+
const { [user]: userDevices } = await parsedKeys.get("device-list", [user]);
|
|
175
|
+
if (!userDevices) {
|
|
176
|
+
return { migrated: 0, skipped: 0, total: 0 };
|
|
177
|
+
}
|
|
178
|
+
const { device: fromDevice } = (0, WABinary_1.jidDecode)(fromJid);
|
|
179
|
+
const fromDeviceStr = fromDevice?.toString() || "0";
|
|
180
|
+
if (!userDevices.includes(fromDeviceStr)) {
|
|
181
|
+
userDevices.push(fromDeviceStr);
|
|
182
|
+
}
|
|
183
|
+
const uncachedDevices = userDevices.filter(device => {
|
|
184
|
+
const deviceKey = `${user}.${device}`;
|
|
185
|
+
return !migratedSessionCache.has(deviceKey);
|
|
186
|
+
});
|
|
187
|
+
const deviceSessionKeys = uncachedDevices.map(device => `${user}.${device}`);
|
|
188
|
+
const existingSessions = await parsedKeys.get("session", deviceSessionKeys);
|
|
189
|
+
const deviceJids = [];
|
|
190
|
+
for (const [sessionKey, sessionData] of Object.entries(existingSessions)) {
|
|
191
|
+
if (sessionData) {
|
|
192
|
+
const deviceStr = sessionKey.split(".")[1];
|
|
193
|
+
if (!deviceStr) continue;
|
|
194
|
+
const deviceNum = parseInt(deviceStr);
|
|
195
|
+
let jid = deviceNum === 0 ? `${user}@s.whatsapp.net` : `${user}:${deviceNum}@s.whatsapp.net`;
|
|
196
|
+
if (deviceNum === 99) {
|
|
197
|
+
jid = `${user}:99@hosted`;
|
|
198
|
+
}
|
|
199
|
+
deviceJids.push(jid);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
logger.debug({
|
|
203
|
+
fromJid,
|
|
204
|
+
totalDevices: userDevices.length,
|
|
205
|
+
devicesWithSessions: deviceJids.length,
|
|
206
|
+
devices: deviceJids
|
|
207
|
+
}, "bulk device migration complete");
|
|
208
|
+
|
|
209
|
+
return parsedKeys.transaction(async () => {
|
|
210
|
+
const migrationOps = deviceJids.map(jid => {
|
|
211
|
+
const lidWithDevice = (0, WABinary_1.transferDevice)(jid, toJid);
|
|
212
|
+
const fromDecoded = (0, WABinary_1.jidDecode)(jid);
|
|
213
|
+
const toDecoded = (0, WABinary_1.jidDecode)(lidWithDevice);
|
|
214
|
+
return {
|
|
215
|
+
fromJid: jid,
|
|
216
|
+
toJid: lidWithDevice,
|
|
217
|
+
pnUser: fromDecoded.user,
|
|
218
|
+
lidUser: toDecoded.user,
|
|
219
|
+
deviceId: fromDecoded.device || 0,
|
|
220
|
+
fromAddr: jidToSignalProtocolAddress(jid),
|
|
221
|
+
toAddr: jidToSignalProtocolAddress(lidWithDevice)
|
|
222
|
+
};
|
|
223
|
+
});
|
|
224
|
+
const totalOps = migrationOps.length;
|
|
225
|
+
let migratedCount = 0;
|
|
226
|
+
const pnAddrStrings = Array.from(new Set(migrationOps.map(op => op.fromAddr.toString())));
|
|
227
|
+
const pnSessions = await parsedKeys.get("session", pnAddrStrings);
|
|
228
|
+
const sessionUpdates = {};
|
|
229
|
+
for (const op of migrationOps) {
|
|
230
|
+
const pnAddrStr = op.fromAddr.toString();
|
|
231
|
+
const lidAddrStr = op.toAddr.toString();
|
|
232
|
+
const pnSession = pnSessions[pnAddrStr];
|
|
233
|
+
if (pnSession) {
|
|
234
|
+
const fromSession = libsignal.SessionRecord.deserialize(pnSession);
|
|
235
|
+
if (fromSession.haveOpenSession()) {
|
|
236
|
+
sessionUpdates[lidAddrStr] = fromSession.serialize();
|
|
237
|
+
sessionUpdates[pnAddrStr] = null;
|
|
238
|
+
migratedCount++;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
if (Object.keys(sessionUpdates).length > 0) {
|
|
243
|
+
await parsedKeys.set({ session: sessionUpdates });
|
|
244
|
+
logger.debug({ migratedSessions: migratedCount }, "bulk session migration complete");
|
|
245
|
+
for (const op of migrationOps) {
|
|
246
|
+
if (sessionUpdates[op.toAddr.toString()]) {
|
|
247
|
+
const deviceKey = `${op.pnUser}.${op.deviceId}`;
|
|
248
|
+
migratedSessionCache.set(deviceKey, true);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
const skippedCount = totalOps - migratedCount;
|
|
253
|
+
return { migrated: migratedCount, skipped: skippedCount, total: totalOps };
|
|
254
|
+
}, `migrate-${deviceJids.length}-sessions-${(0, WABinary_1.jidDecode)(toJid)?.user}`);
|
|
110
255
|
}
|
|
111
256
|
};
|
|
257
|
+
return repository;
|
|
112
258
|
}
|
|
259
|
+
|
|
113
260
|
const jidToSignalProtocolAddress = (jid) => {
|
|
114
|
-
const
|
|
115
|
-
|
|
261
|
+
const decoded = (0, WABinary_1.jidDecode)(jid);
|
|
262
|
+
const { user, device, server, domainType } = decoded;
|
|
263
|
+
if (!user) {
|
|
264
|
+
throw new Error(`JID decoded but user is empty: "${jid}" -> user: "${user}", server: "${server}", device: ${device}`);
|
|
265
|
+
}
|
|
266
|
+
const signalUser = domainType !== WABinary_1.WAJIDDomains.WHATSAPP ? `${user}_${domainType}` : user;
|
|
267
|
+
const finalDevice = device || 0;
|
|
268
|
+
if (device === 99 && decoded.server !== "hosted" && decoded.server !== "hosted.lid") {
|
|
269
|
+
throw new Error("Unexpected non-hosted device JID with device 99. This ID seems invalid. ID:" + jid);
|
|
270
|
+
}
|
|
271
|
+
return new libsignal.ProtocolAddress(signalUser, finalDevice);
|
|
116
272
|
};
|
|
273
|
+
|
|
117
274
|
const jidToSignalSenderKeyName = (group, user) => {
|
|
118
275
|
return new sender_key_name_1.SenderKeyName(group, jidToSignalProtocolAddress(user));
|
|
119
276
|
};
|
|
120
|
-
|
|
277
|
+
|
|
278
|
+
function signalStorage({ creds, keys }, lidMapping) {
|
|
279
|
+
const resolveLIDSignalAddress = async (id) => {
|
|
280
|
+
if (id.includes(".")) {
|
|
281
|
+
const [deviceId, device] = id.split(".");
|
|
282
|
+
const [user, domainType_] = deviceId.split("_");
|
|
283
|
+
const domainType = parseInt(domainType_ || "0");
|
|
284
|
+
if (domainType === WABinary_1.WAJIDDomains.LID || domainType === WABinary_1.WAJIDDomains.HOSTED_LID)
|
|
285
|
+
return id;
|
|
286
|
+
const pnJid = `${user}${device !== "0" ? `:${device}` : ""}@${domainType === WABinary_1.WAJIDDomains.HOSTED ? "hosted" : "s.whatsapp.net"}`;
|
|
287
|
+
const lidForPN = await lidMapping.getLIDForPN(pnJid);
|
|
288
|
+
if (lidForPN) {
|
|
289
|
+
const lidAddr = jidToSignalProtocolAddress(lidForPN);
|
|
290
|
+
return lidAddr.toString();
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
return id;
|
|
294
|
+
};
|
|
295
|
+
|
|
121
296
|
return {
|
|
122
297
|
loadSession: async (id) => {
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
298
|
+
try {
|
|
299
|
+
const wireJid = await resolveLIDSignalAddress(id);
|
|
300
|
+
const { [wireJid]: sess } = await keys.get("session", [wireJid]);
|
|
301
|
+
if (sess) {
|
|
302
|
+
return libsignal.SessionRecord.deserialize(sess);
|
|
303
|
+
}
|
|
304
|
+
} catch (e) {
|
|
305
|
+
return null;
|
|
126
306
|
}
|
|
307
|
+
return null;
|
|
127
308
|
},
|
|
128
309
|
storeSession: async (id, session) => {
|
|
129
|
-
|
|
310
|
+
const wireJid = await resolveLIDSignalAddress(id);
|
|
311
|
+
await keys.set({ session: { [wireJid]: session.serialize() } });
|
|
130
312
|
},
|
|
131
313
|
isTrustedIdentity: () => {
|
|
132
314
|
return true;
|
|
133
315
|
},
|
|
134
316
|
loadPreKey: async (id) => {
|
|
135
317
|
const keyId = id.toString();
|
|
136
|
-
const { [keyId]: key } = await keys.get(
|
|
318
|
+
const { [keyId]: key } = await keys.get("pre-key", [keyId]);
|
|
137
319
|
if (key) {
|
|
138
320
|
return {
|
|
139
321
|
privKey: Buffer.from(key.private),
|
|
@@ -141,7 +323,7 @@ function signalStorage({ creds, keys }) {
|
|
|
141
323
|
};
|
|
142
324
|
}
|
|
143
325
|
},
|
|
144
|
-
removePreKey: (id) => keys.set({
|
|
326
|
+
removePreKey: (id) => keys.set({ "pre-key": { [id]: null } }),
|
|
145
327
|
loadSignedPreKey: () => {
|
|
146
328
|
const key = creds.signedPreKey;
|
|
147
329
|
return {
|
|
@@ -151,7 +333,7 @@ function signalStorage({ creds, keys }) {
|
|
|
151
333
|
},
|
|
152
334
|
loadSenderKey: async (senderKeyName) => {
|
|
153
335
|
const keyId = senderKeyName.toString();
|
|
154
|
-
const { [keyId]: key } = await keys.get(
|
|
336
|
+
const { [keyId]: key } = await keys.get("sender-key", [keyId]);
|
|
155
337
|
if (key) {
|
|
156
338
|
return sender_key_record_1.SenderKeyRecord.deserialize(key);
|
|
157
339
|
}
|
|
@@ -160,14 +342,14 @@ function signalStorage({ creds, keys }) {
|
|
|
160
342
|
storeSenderKey: async (senderKeyName, key) => {
|
|
161
343
|
const keyId = senderKeyName.toString();
|
|
162
344
|
const serialized = JSON.stringify(key.serialize());
|
|
163
|
-
await keys.set({
|
|
345
|
+
await keys.set({ "sender-key": { [keyId]: Buffer.from(serialized, "utf-8") } });
|
|
164
346
|
},
|
|
165
347
|
getOurRegistrationId: () => creds.registrationId,
|
|
166
348
|
getOurIdentity: () => {
|
|
167
349
|
const { signedIdentityKey } = creds;
|
|
168
350
|
return {
|
|
169
351
|
privKey: Buffer.from(signedIdentityKey.private),
|
|
170
|
-
pubKey: (0, Utils_1.generateSignalPubKey)(signedIdentityKey.public)
|
|
352
|
+
pubKey: Buffer.from((0, Utils_1.generateSignalPubKey)(signedIdentityKey.public))
|
|
171
353
|
};
|
|
172
354
|
}
|
|
173
355
|
};
|
|
@@ -1,73 +1,63 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.makeLIDMappingStore = void 0;
|
|
4
4
|
|
|
5
5
|
const WABinary_1 = require("../WABinary");
|
|
6
6
|
const lru_cache_1 = require("lru-cache");
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
});
|
|
15
|
-
this.keys = keys;
|
|
16
|
-
this.pnToLIDFunc = pnToLIDFunc;
|
|
17
|
-
this.logger = logger;
|
|
18
|
-
}
|
|
8
|
+
const makeLIDMappingStore = (keys, logger, pnToLIDFunc) => {
|
|
9
|
+
const mappingCache = new lru_cache_1.LRUCache({
|
|
10
|
+
ttl: 7 * 24 * 60 * 60 * 1000,
|
|
11
|
+
ttlAutopurge: true,
|
|
12
|
+
updateAgeOnGet: true
|
|
13
|
+
});
|
|
19
14
|
|
|
20
|
-
async
|
|
15
|
+
const storeLIDPNMappings = async (pairs) => {
|
|
21
16
|
const pairMap = {};
|
|
22
17
|
for (const { lid, pn } of pairs) {
|
|
23
18
|
if (!(((0, WABinary_1.isLidUser)(lid) && (0, WABinary_1.isPnUser)(pn)) || ((0, WABinary_1.isPnUser)(lid) && (0, WABinary_1.isLidUser)(pn)))) {
|
|
24
|
-
|
|
19
|
+
logger && logger.warn(`Invalid LID-PN mapping: ${lid}, ${pn}`);
|
|
25
20
|
continue;
|
|
26
21
|
}
|
|
27
22
|
const lidDecoded = (0, WABinary_1.jidDecode)(lid);
|
|
28
23
|
const pnDecoded = (0, WABinary_1.jidDecode)(pn);
|
|
29
|
-
if (!lidDecoded || !pnDecoded)
|
|
24
|
+
if (!lidDecoded || !pnDecoded) continue;
|
|
30
25
|
const pnUser = pnDecoded.user;
|
|
31
26
|
const lidUser = lidDecoded.user;
|
|
32
27
|
|
|
33
|
-
let existingLidUser =
|
|
28
|
+
let existingLidUser = mappingCache.get(`pn:${pnUser}`);
|
|
34
29
|
if (!existingLidUser) {
|
|
35
|
-
|
|
36
|
-
const stored = await
|
|
30
|
+
logger && logger.trace(`Cache miss for PN user ${pnUser}; checking database`);
|
|
31
|
+
const stored = await keys.get("lid-mapping", [pnUser]);
|
|
37
32
|
existingLidUser = stored[pnUser];
|
|
38
33
|
if (existingLidUser) {
|
|
39
|
-
|
|
40
|
-
|
|
34
|
+
mappingCache.set(`pn:${pnUser}`, existingLidUser);
|
|
35
|
+
mappingCache.set(`lid:${existingLidUser}`, pnUser);
|
|
41
36
|
}
|
|
42
37
|
}
|
|
43
38
|
if (existingLidUser === lidUser) {
|
|
44
|
-
|
|
39
|
+
logger && logger.debug({ pnUser, lidUser }, "LID mapping already exists, skipping");
|
|
45
40
|
continue;
|
|
46
41
|
}
|
|
47
42
|
pairMap[pnUser] = lidUser;
|
|
48
43
|
}
|
|
49
44
|
|
|
50
|
-
|
|
51
|
-
await
|
|
45
|
+
logger && logger.trace({ pairMap }, `Storing ${Object.keys(pairMap).length} pn mappings`);
|
|
46
|
+
await keys.transaction(async () => {
|
|
52
47
|
for (const [pnUser, lidUser] of Object.entries(pairMap)) {
|
|
53
|
-
await
|
|
48
|
+
await keys.set({
|
|
54
49
|
"lid-mapping": {
|
|
55
50
|
[pnUser]: lidUser,
|
|
56
51
|
[`${lidUser}_reverse`]: pnUser
|
|
57
52
|
}
|
|
58
53
|
});
|
|
59
|
-
|
|
60
|
-
|
|
54
|
+
mappingCache.set(`pn:${pnUser}`, lidUser);
|
|
55
|
+
mappingCache.set(`lid:${lidUser}`, pnUser);
|
|
61
56
|
}
|
|
62
57
|
}, "lid-mapping");
|
|
63
|
-
}
|
|
58
|
+
};
|
|
64
59
|
|
|
65
|
-
async
|
|
66
|
-
const result = await this.getLIDsForPNs([pn]);
|
|
67
|
-
return result?.[0]?.lid || null;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
async getLIDsForPNs(pns) {
|
|
60
|
+
const getLIDsForPNs = async (pns) => {
|
|
71
61
|
const usyncFetch = {};
|
|
72
62
|
const successfulPairs = {};
|
|
73
63
|
|
|
@@ -77,15 +67,15 @@ class LIDMappingStore {
|
|
|
77
67
|
if (!decoded) continue;
|
|
78
68
|
const pnUser = decoded.user;
|
|
79
69
|
|
|
80
|
-
let lidUser =
|
|
70
|
+
let lidUser = mappingCache.get(`pn:${pnUser}`);
|
|
81
71
|
if (!lidUser) {
|
|
82
|
-
const stored = await
|
|
72
|
+
const stored = await keys.get("lid-mapping", [pnUser]);
|
|
83
73
|
lidUser = stored[pnUser];
|
|
84
74
|
if (lidUser) {
|
|
85
|
-
|
|
86
|
-
|
|
75
|
+
mappingCache.set(`pn:${pnUser}`, lidUser);
|
|
76
|
+
mappingCache.set(`lid:${lidUser}`, pnUser);
|
|
87
77
|
} else {
|
|
88
|
-
|
|
78
|
+
logger && logger.trace(`No LID mapping found for PN user ${pnUser}; batch getting from USync`);
|
|
89
79
|
const device = decoded.device || 0;
|
|
90
80
|
let normalizedPn = (0, WABinary_1.jidNormalizedUser)(pn);
|
|
91
81
|
if ((0, WABinary_1.isHostedPnUser)(normalizedPn)) {
|
|
@@ -102,19 +92,19 @@ class LIDMappingStore {
|
|
|
102
92
|
|
|
103
93
|
lidUser = lidUser.toString();
|
|
104
94
|
if (!lidUser) {
|
|
105
|
-
|
|
95
|
+
logger && logger.warn(`Invalid or empty LID user for PN ${pn}: lidUser = "${lidUser}"`);
|
|
106
96
|
return null;
|
|
107
97
|
}
|
|
108
98
|
const pnDevice = decoded.device !== undefined ? decoded.device : 0;
|
|
109
99
|
const deviceSpecificLid = `${lidUser}${!!pnDevice ? `:${pnDevice}` : ``}@${decoded.server === "hosted" ? "hosted.lid" : "lid"}`;
|
|
110
|
-
|
|
100
|
+
logger && logger.trace(`getLIDForPN: ${pn} → ${deviceSpecificLid} (user mapping with device ${pnDevice})`);
|
|
111
101
|
successfulPairs[pn] = { lid: deviceSpecificLid, pn };
|
|
112
102
|
}
|
|
113
103
|
|
|
114
104
|
if (Object.keys(usyncFetch).length > 0) {
|
|
115
|
-
const result = await
|
|
105
|
+
const result = await pnToLIDFunc?.(Object.keys(usyncFetch));
|
|
116
106
|
if (result && result.length > 0) {
|
|
117
|
-
|
|
107
|
+
await storeLIDPNMappings(result);
|
|
118
108
|
for (const pair of result) {
|
|
119
109
|
const pnDecoded = (0, WABinary_1.jidDecode)(pair.pn);
|
|
120
110
|
const pnUser = pnDecoded?.user;
|
|
@@ -124,7 +114,7 @@ class LIDMappingStore {
|
|
|
124
114
|
|
|
125
115
|
for (const device of usyncFetch[pair.pn]) {
|
|
126
116
|
const deviceSpecificLid = `${lidUser}${!!device ? `:${device}` : ``}@${device === 99 ? "hosted.lid" : "lid"}`;
|
|
127
|
-
|
|
117
|
+
logger && logger.trace(`getLIDForPN: USYNC success for ${pair.pn} → ${deviceSpecificLid} (user mapping with device ${device})`);
|
|
128
118
|
const deviceSpecificPn = `${pnUser}${!!device ? `:${device}` : ``}@${device === 99 ? "hosted" : "s.whatsapp.net"}`;
|
|
129
119
|
successfulPairs[deviceSpecificPn] = { lid: deviceSpecificLid, pn: deviceSpecificPn };
|
|
130
120
|
}
|
|
@@ -134,30 +124,43 @@ class LIDMappingStore {
|
|
|
134
124
|
}
|
|
135
125
|
}
|
|
136
126
|
return Object.values(successfulPairs);
|
|
137
|
-
}
|
|
127
|
+
};
|
|
138
128
|
|
|
139
|
-
async
|
|
129
|
+
const getLIDForPN = async (pn) => {
|
|
130
|
+
const result = await getLIDsForPNs([pn]);
|
|
131
|
+
return result?.[0]?.lid || null;
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
const getPNForLID = async (lid) => {
|
|
140
135
|
if (!(0, WABinary_1.isLidUser)(lid)) return null;
|
|
141
136
|
const decoded = (0, WABinary_1.jidDecode)(lid);
|
|
142
137
|
if (!decoded) return null;
|
|
143
138
|
|
|
144
139
|
const lidUser = decoded.user;
|
|
145
|
-
let pnUser =
|
|
140
|
+
let pnUser = mappingCache.get(`lid:${lidUser}`);
|
|
146
141
|
|
|
147
142
|
if (!pnUser || typeof pnUser !== "string") {
|
|
148
|
-
const stored = await
|
|
143
|
+
const stored = await keys.get("lid-mapping", [`${lidUser}_reverse`]);
|
|
149
144
|
pnUser = stored[`${lidUser}_reverse`];
|
|
150
145
|
if (!pnUser || typeof pnUser !== "string") {
|
|
151
|
-
|
|
146
|
+
logger && logger.trace(`No reverse mapping found for LID user: ${lidUser}`);
|
|
152
147
|
return null;
|
|
153
148
|
}
|
|
154
|
-
|
|
149
|
+
mappingCache.set(`lid:${lidUser}`, pnUser);
|
|
155
150
|
}
|
|
156
151
|
|
|
157
152
|
const lidDevice = decoded.device !== undefined ? decoded.device : 0;
|
|
158
153
|
const pnJid = `${pnUser}:${lidDevice}@${decoded.domainType === WABinary_1.WAJIDDomains.HOSTED_LID ? "hosted" : "s.whatsapp.net"}`;
|
|
159
|
-
|
|
154
|
+
logger && logger.trace(`Found reverse mapping: ${lid} → ${pnJid}`);
|
|
160
155
|
return pnJid;
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
return {
|
|
159
|
+
mappingCache,
|
|
160
|
+
storeLIDPNMappings,
|
|
161
|
+
getLIDForPN,
|
|
162
|
+
getLIDsForPNs,
|
|
163
|
+
getPNForLID
|
|
164
|
+
};
|
|
165
|
+
};
|
|
166
|
+
exports.makeLIDMappingStore = makeLIDMappingStore;
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.LIDMappingStore = void 0;
|
|
4
|
+
|
|
5
|
+
const WABinary_1 = require("../WABinary");
|
|
6
|
+
const lru_cache_1 = require("lru-cache");
|
|
7
|
+
|
|
8
|
+
class LIDMappingStore {
|
|
9
|
+
constructor(keys, logger, pnToLIDFunc) {
|
|
10
|
+
this.mappingCache = new lru_cache_1.LRUCache({
|
|
11
|
+
ttl: 7 * 24 * 60 * 60 * 1000,
|
|
12
|
+
ttlAutopurge: true,
|
|
13
|
+
updateAgeOnGet: true
|
|
14
|
+
});
|
|
15
|
+
this.keys = keys;
|
|
16
|
+
this.pnToLIDFunc = pnToLIDFunc;
|
|
17
|
+
this.logger = logger;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
async storeLIDPNMappings(pairs) {
|
|
21
|
+
const pairMap = {};
|
|
22
|
+
for (const { lid, pn } of pairs) {
|
|
23
|
+
if (!(((0, WABinary_1.isLidUser)(lid) && (0, WABinary_1.isPnUser)(pn)) || ((0, WABinary_1.isPnUser)(lid) && (0, WABinary_1.isLidUser)(pn)))) {
|
|
24
|
+
this.logger.warn(`Invalid LID-PN mapping: ${lid}, ${pn}`);
|
|
25
|
+
continue;
|
|
26
|
+
}
|
|
27
|
+
const lidDecoded = (0, WABinary_1.jidDecode)(lid);
|
|
28
|
+
const pnDecoded = (0, WABinary_1.jidDecode)(pn);
|
|
29
|
+
if (!lidDecoded || !pnDecoded) return;
|
|
30
|
+
const pnUser = pnDecoded.user;
|
|
31
|
+
const lidUser = lidDecoded.user;
|
|
32
|
+
|
|
33
|
+
let existingLidUser = this.mappingCache.get(`pn:${pnUser}`);
|
|
34
|
+
if (!existingLidUser) {
|
|
35
|
+
this.logger.trace(`Cache miss for PN user ${pnUser}; checking database`);
|
|
36
|
+
const stored = await this.keys.get("lid-mapping", [pnUser]);
|
|
37
|
+
existingLidUser = stored[pnUser];
|
|
38
|
+
if (existingLidUser) {
|
|
39
|
+
this.mappingCache.set(`pn:${pnUser}`, existingLidUser);
|
|
40
|
+
this.mappingCache.set(`lid:${existingLidUser}`, pnUser);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
if (existingLidUser === lidUser) {
|
|
44
|
+
this.logger.debug({ pnUser, lidUser }, "LID mapping already exists, skipping");
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
pairMap[pnUser] = lidUser;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
this.logger.trace({ pairMap }, `Storing ${Object.keys(pairMap).length} pn mappings`);
|
|
51
|
+
await this.keys.transaction(async () => {
|
|
52
|
+
for (const [pnUser, lidUser] of Object.entries(pairMap)) {
|
|
53
|
+
await this.keys.set({
|
|
54
|
+
"lid-mapping": {
|
|
55
|
+
[pnUser]: lidUser,
|
|
56
|
+
[`${lidUser}_reverse`]: pnUser
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
this.mappingCache.set(`pn:${pnUser}`, lidUser);
|
|
60
|
+
this.mappingCache.set(`lid:${lidUser}`, pnUser);
|
|
61
|
+
}
|
|
62
|
+
}, "lid-mapping");
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
async getLIDForPN(pn) {
|
|
66
|
+
const result = await this.getLIDsForPNs([pn]);
|
|
67
|
+
return result?.[0]?.lid || null;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
async getLIDsForPNs(pns) {
|
|
71
|
+
const usyncFetch = {};
|
|
72
|
+
const successfulPairs = {};
|
|
73
|
+
|
|
74
|
+
for (const pn of pns) {
|
|
75
|
+
if (!(0, WABinary_1.isPnUser)(pn) && !(0, WABinary_1.isHostedPnUser)(pn)) continue;
|
|
76
|
+
const decoded = (0, WABinary_1.jidDecode)(pn);
|
|
77
|
+
if (!decoded) continue;
|
|
78
|
+
const pnUser = decoded.user;
|
|
79
|
+
|
|
80
|
+
let lidUser = this.mappingCache.get(`pn:${pnUser}`);
|
|
81
|
+
if (!lidUser) {
|
|
82
|
+
const stored = await this.keys.get("lid-mapping", [pnUser]);
|
|
83
|
+
lidUser = stored[pnUser];
|
|
84
|
+
if (lidUser) {
|
|
85
|
+
this.mappingCache.set(`pn:${pnUser}`, lidUser);
|
|
86
|
+
this.mappingCache.set(`lid:${lidUser}`, pnUser);
|
|
87
|
+
} else {
|
|
88
|
+
this.logger.trace(`No LID mapping found for PN user ${pnUser}; batch getting from USync`);
|
|
89
|
+
const device = decoded.device || 0;
|
|
90
|
+
let normalizedPn = (0, WABinary_1.jidNormalizedUser)(pn);
|
|
91
|
+
if ((0, WABinary_1.isHostedPnUser)(normalizedPn)) {
|
|
92
|
+
normalizedPn = `${pnUser}@s.whatsapp.net`;
|
|
93
|
+
}
|
|
94
|
+
if (!usyncFetch[normalizedPn]) {
|
|
95
|
+
usyncFetch[normalizedPn] = [device];
|
|
96
|
+
} else {
|
|
97
|
+
usyncFetch[normalizedPn]?.push(device);
|
|
98
|
+
}
|
|
99
|
+
continue;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
lidUser = lidUser.toString();
|
|
104
|
+
if (!lidUser) {
|
|
105
|
+
this.logger.warn(`Invalid or empty LID user for PN ${pn}: lidUser = "${lidUser}"`);
|
|
106
|
+
return null;
|
|
107
|
+
}
|
|
108
|
+
const pnDevice = decoded.device !== undefined ? decoded.device : 0;
|
|
109
|
+
const deviceSpecificLid = `${lidUser}${!!pnDevice ? `:${pnDevice}` : ``}@${decoded.server === "hosted" ? "hosted.lid" : "lid"}`;
|
|
110
|
+
this.logger.trace(`getLIDForPN: ${pn} → ${deviceSpecificLid} (user mapping with device ${pnDevice})`);
|
|
111
|
+
successfulPairs[pn] = { lid: deviceSpecificLid, pn };
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
if (Object.keys(usyncFetch).length > 0) {
|
|
115
|
+
const result = await this.pnToLIDFunc?.(Object.keys(usyncFetch));
|
|
116
|
+
if (result && result.length > 0) {
|
|
117
|
+
this.storeLIDPNMappings(result);
|
|
118
|
+
for (const pair of result) {
|
|
119
|
+
const pnDecoded = (0, WABinary_1.jidDecode)(pair.pn);
|
|
120
|
+
const pnUser = pnDecoded?.user;
|
|
121
|
+
if (!pnUser) continue;
|
|
122
|
+
const lidUser = (0, WABinary_1.jidDecode)(pair.lid)?.user;
|
|
123
|
+
if (!lidUser) continue;
|
|
124
|
+
|
|
125
|
+
for (const device of usyncFetch[pair.pn]) {
|
|
126
|
+
const deviceSpecificLid = `${lidUser}${!!device ? `:${device}` : ``}@${device === 99 ? "hosted.lid" : "lid"}`;
|
|
127
|
+
this.logger.trace(`getLIDForPN: USYNC success for ${pair.pn} → ${deviceSpecificLid} (user mapping with device ${device})`);
|
|
128
|
+
const deviceSpecificPn = `${pnUser}${!!device ? `:${device}` : ``}@${device === 99 ? "hosted" : "s.whatsapp.net"}`;
|
|
129
|
+
successfulPairs[deviceSpecificPn] = { lid: deviceSpecificLid, pn: deviceSpecificPn };
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
} else {
|
|
133
|
+
return null;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
return Object.values(successfulPairs);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
async getPNForLID(lid) {
|
|
140
|
+
if (!(0, WABinary_1.isLidUser)(lid)) return null;
|
|
141
|
+
const decoded = (0, WABinary_1.jidDecode)(lid);
|
|
142
|
+
if (!decoded) return null;
|
|
143
|
+
|
|
144
|
+
const lidUser = decoded.user;
|
|
145
|
+
let pnUser = this.mappingCache.get(`lid:${lidUser}`);
|
|
146
|
+
|
|
147
|
+
if (!pnUser || typeof pnUser !== "string") {
|
|
148
|
+
const stored = await this.keys.get("lid-mapping", [`${lidUser}_reverse`]);
|
|
149
|
+
pnUser = stored[`${lidUser}_reverse`];
|
|
150
|
+
if (!pnUser || typeof pnUser !== "string") {
|
|
151
|
+
this.logger.trace(`No reverse mapping found for LID user: ${lidUser}`);
|
|
152
|
+
return null;
|
|
153
|
+
}
|
|
154
|
+
this.mappingCache.set(`lid:${lidUser}`, pnUser);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const lidDevice = decoded.device !== undefined ? decoded.device : 0;
|
|
158
|
+
const pnJid = `${pnUser}:${lidDevice}@${decoded.domainType === WABinary_1.WAJIDDomains.HOSTED_LID ? "hosted" : "s.whatsapp.net"}`;
|
|
159
|
+
this.logger.trace(`Found reverse mapping: ${lid} → ${pnJid}`);
|
|
160
|
+
return pnJid;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
exports.LIDMappingStore = LIDMappingStore;
|