@ellxz24/baileys 2.0.28 → 2.0.30
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 +65 -55
- package/lib/Signal/lid-mapping.js.bak +166 -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,94 +1,91 @@
|
|
|
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
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
}
|
|
8
|
+
const isHostedPnUser = (jid) => {
|
|
9
|
+
const decoded = (0, WABinary_1.jidDecode)(jid);
|
|
10
|
+
return decoded ? decoded.server === 'hosted' : false;
|
|
11
|
+
};
|
|
12
|
+
const makeLIDMappingStore = (keys, logger, pnToLIDFunc) => {
|
|
13
|
+
const mappingCache = new lru_cache_1.LRUCache({
|
|
14
|
+
ttl: 7 * 24 * 60 * 60 * 1000,
|
|
15
|
+
ttlAutopurge: true,
|
|
16
|
+
updateAgeOnGet: true
|
|
17
|
+
});
|
|
19
18
|
|
|
20
|
-
async
|
|
19
|
+
const storeLIDPNMappings = async (pairs) => {
|
|
21
20
|
const pairMap = {};
|
|
22
21
|
for (const { lid, pn } of pairs) {
|
|
23
22
|
if (!(((0, WABinary_1.isLidUser)(lid) && (0, WABinary_1.isPnUser)(pn)) || ((0, WABinary_1.isPnUser)(lid) && (0, WABinary_1.isLidUser)(pn)))) {
|
|
24
|
-
|
|
23
|
+
logger && logger.warn(`Invalid LID-PN mapping: ${lid}, ${pn}`);
|
|
25
24
|
continue;
|
|
26
25
|
}
|
|
27
26
|
const lidDecoded = (0, WABinary_1.jidDecode)(lid);
|
|
28
27
|
const pnDecoded = (0, WABinary_1.jidDecode)(pn);
|
|
29
|
-
if (!lidDecoded || !pnDecoded)
|
|
28
|
+
if (!lidDecoded || !pnDecoded) continue;
|
|
30
29
|
const pnUser = pnDecoded.user;
|
|
31
30
|
const lidUser = lidDecoded.user;
|
|
32
31
|
|
|
33
|
-
let existingLidUser =
|
|
32
|
+
let existingLidUser = mappingCache.get(`pn:${pnUser}`);
|
|
34
33
|
if (!existingLidUser) {
|
|
35
|
-
|
|
36
|
-
const stored = await
|
|
34
|
+
logger && logger.trace(`Cache miss for PN user ${pnUser}; checking database`);
|
|
35
|
+
const stored = await keys.get("lid-mapping", [pnUser]);
|
|
37
36
|
existingLidUser = stored[pnUser];
|
|
38
37
|
if (existingLidUser) {
|
|
39
|
-
|
|
40
|
-
|
|
38
|
+
mappingCache.set(`pn:${pnUser}`, existingLidUser);
|
|
39
|
+
mappingCache.set(`lid:${existingLidUser}`, pnUser);
|
|
41
40
|
}
|
|
42
41
|
}
|
|
43
42
|
if (existingLidUser === lidUser) {
|
|
44
|
-
|
|
43
|
+
logger && logger.debug({ pnUser, lidUser }, "LID mapping already exists, skipping");
|
|
45
44
|
continue;
|
|
46
45
|
}
|
|
47
46
|
pairMap[pnUser] = lidUser;
|
|
48
47
|
}
|
|
49
48
|
|
|
50
|
-
|
|
51
|
-
await
|
|
49
|
+
logger && logger.trace({ pairMap }, `Storing ${Object.keys(pairMap).length} pn mappings`);
|
|
50
|
+
await keys.transaction(async () => {
|
|
52
51
|
for (const [pnUser, lidUser] of Object.entries(pairMap)) {
|
|
53
|
-
await
|
|
52
|
+
await keys.set({
|
|
54
53
|
"lid-mapping": {
|
|
55
54
|
[pnUser]: lidUser,
|
|
56
55
|
[`${lidUser}_reverse`]: pnUser
|
|
57
56
|
}
|
|
58
57
|
});
|
|
59
|
-
|
|
60
|
-
|
|
58
|
+
mappingCache.set(`pn:${pnUser}`, lidUser);
|
|
59
|
+
mappingCache.set(`lid:${lidUser}`, pnUser);
|
|
61
60
|
}
|
|
62
61
|
}, "lid-mapping");
|
|
63
|
-
}
|
|
62
|
+
};
|
|
64
63
|
|
|
65
|
-
async
|
|
66
|
-
const result = await this.getLIDsForPNs([pn]);
|
|
67
|
-
return result?.[0]?.lid || null;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
async getLIDsForPNs(pns) {
|
|
64
|
+
const getLIDsForPNs = async (pns) => {
|
|
71
65
|
const usyncFetch = {};
|
|
72
66
|
const successfulPairs = {};
|
|
73
67
|
|
|
74
68
|
for (const pn of pns) {
|
|
75
|
-
|
|
69
|
+
// MENGGUNAKAN FALLBACK isHostedPnUser
|
|
70
|
+
if (!(0, WABinary_1.isPnUser)(pn) && !isHostedPnUser(pn)) continue;
|
|
76
71
|
const decoded = (0, WABinary_1.jidDecode)(pn);
|
|
77
72
|
if (!decoded) continue;
|
|
78
73
|
const pnUser = decoded.user;
|
|
79
74
|
|
|
80
|
-
let lidUser =
|
|
75
|
+
let lidUser = mappingCache.get(`pn:${pnUser}`);
|
|
81
76
|
if (!lidUser) {
|
|
82
|
-
const stored = await
|
|
77
|
+
const stored = await keys.get("lid-mapping", [pnUser]);
|
|
83
78
|
lidUser = stored[pnUser];
|
|
84
79
|
if (lidUser) {
|
|
85
|
-
|
|
86
|
-
|
|
80
|
+
mappingCache.set(`pn:${pnUser}`, lidUser);
|
|
81
|
+
mappingCache.set(`lid:${lidUser}`, pnUser);
|
|
87
82
|
} else {
|
|
88
|
-
|
|
83
|
+
logger && logger.trace(`No LID mapping found for PN user ${pnUser}; batch getting from USync`);
|
|
89
84
|
const device = decoded.device || 0;
|
|
90
85
|
let normalizedPn = (0, WABinary_1.jidNormalizedUser)(pn);
|
|
91
|
-
|
|
86
|
+
|
|
87
|
+
// MENGGUNAKAN FALLBACK isHostedPnUser
|
|
88
|
+
if (isHostedPnUser(normalizedPn)) {
|
|
92
89
|
normalizedPn = `${pnUser}@s.whatsapp.net`;
|
|
93
90
|
}
|
|
94
91
|
if (!usyncFetch[normalizedPn]) {
|
|
@@ -102,19 +99,19 @@ class LIDMappingStore {
|
|
|
102
99
|
|
|
103
100
|
lidUser = lidUser.toString();
|
|
104
101
|
if (!lidUser) {
|
|
105
|
-
|
|
102
|
+
logger && logger.warn(`Invalid or empty LID user for PN ${pn}: lidUser = "${lidUser}"`);
|
|
106
103
|
return null;
|
|
107
104
|
}
|
|
108
105
|
const pnDevice = decoded.device !== undefined ? decoded.device : 0;
|
|
109
106
|
const deviceSpecificLid = `${lidUser}${!!pnDevice ? `:${pnDevice}` : ``}@${decoded.server === "hosted" ? "hosted.lid" : "lid"}`;
|
|
110
|
-
|
|
107
|
+
logger && logger.trace(`getLIDForPN: ${pn} → ${deviceSpecificLid} (user mapping with device ${pnDevice})`);
|
|
111
108
|
successfulPairs[pn] = { lid: deviceSpecificLid, pn };
|
|
112
109
|
}
|
|
113
110
|
|
|
114
111
|
if (Object.keys(usyncFetch).length > 0) {
|
|
115
|
-
const result = await
|
|
112
|
+
const result = await pnToLIDFunc?.(Object.keys(usyncFetch));
|
|
116
113
|
if (result && result.length > 0) {
|
|
117
|
-
|
|
114
|
+
await storeLIDPNMappings(result);
|
|
118
115
|
for (const pair of result) {
|
|
119
116
|
const pnDecoded = (0, WABinary_1.jidDecode)(pair.pn);
|
|
120
117
|
const pnUser = pnDecoded?.user;
|
|
@@ -124,7 +121,7 @@ class LIDMappingStore {
|
|
|
124
121
|
|
|
125
122
|
for (const device of usyncFetch[pair.pn]) {
|
|
126
123
|
const deviceSpecificLid = `${lidUser}${!!device ? `:${device}` : ``}@${device === 99 ? "hosted.lid" : "lid"}`;
|
|
127
|
-
|
|
124
|
+
logger && logger.trace(`getLIDForPN: USYNC success for ${pair.pn} → ${deviceSpecificLid} (user mapping with device ${device})`);
|
|
128
125
|
const deviceSpecificPn = `${pnUser}${!!device ? `:${device}` : ``}@${device === 99 ? "hosted" : "s.whatsapp.net"}`;
|
|
129
126
|
successfulPairs[deviceSpecificPn] = { lid: deviceSpecificLid, pn: deviceSpecificPn };
|
|
130
127
|
}
|
|
@@ -134,30 +131,43 @@ class LIDMappingStore {
|
|
|
134
131
|
}
|
|
135
132
|
}
|
|
136
133
|
return Object.values(successfulPairs);
|
|
137
|
-
}
|
|
134
|
+
};
|
|
138
135
|
|
|
139
|
-
async
|
|
136
|
+
const getLIDForPN = async (pn) => {
|
|
137
|
+
const result = await getLIDsForPNs([pn]);
|
|
138
|
+
return result?.[0]?.lid || null;
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
const getPNForLID = async (lid) => {
|
|
140
142
|
if (!(0, WABinary_1.isLidUser)(lid)) return null;
|
|
141
143
|
const decoded = (0, WABinary_1.jidDecode)(lid);
|
|
142
144
|
if (!decoded) return null;
|
|
143
145
|
|
|
144
146
|
const lidUser = decoded.user;
|
|
145
|
-
let pnUser =
|
|
147
|
+
let pnUser = mappingCache.get(`lid:${lidUser}`);
|
|
146
148
|
|
|
147
149
|
if (!pnUser || typeof pnUser !== "string") {
|
|
148
|
-
const stored = await
|
|
150
|
+
const stored = await keys.get("lid-mapping", [`${lidUser}_reverse`]);
|
|
149
151
|
pnUser = stored[`${lidUser}_reverse`];
|
|
150
152
|
if (!pnUser || typeof pnUser !== "string") {
|
|
151
|
-
|
|
153
|
+
logger && logger.trace(`No reverse mapping found for LID user: ${lidUser}`);
|
|
152
154
|
return null;
|
|
153
155
|
}
|
|
154
|
-
|
|
156
|
+
mappingCache.set(`lid:${lidUser}`, pnUser);
|
|
155
157
|
}
|
|
156
158
|
|
|
157
159
|
const lidDevice = decoded.device !== undefined ? decoded.device : 0;
|
|
158
160
|
const pnJid = `${pnUser}:${lidDevice}@${decoded.domainType === WABinary_1.WAJIDDomains.HOSTED_LID ? "hosted" : "s.whatsapp.net"}`;
|
|
159
|
-
|
|
161
|
+
logger && logger.trace(`Found reverse mapping: ${lid} → ${pnJid}`);
|
|
160
162
|
return pnJid;
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
return {
|
|
166
|
+
mappingCache,
|
|
167
|
+
storeLIDPNMappings,
|
|
168
|
+
getLIDForPN,
|
|
169
|
+
getLIDsForPNs,
|
|
170
|
+
getPNForLID
|
|
171
|
+
};
|
|
172
|
+
};
|
|
173
|
+
exports.makeLIDMappingStore = makeLIDMappingStore;
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.makeLIDMappingStore = void 0;
|
|
4
|
+
|
|
5
|
+
const WABinary_1 = require("../WABinary");
|
|
6
|
+
const lru_cache_1 = require("lru-cache");
|
|
7
|
+
|
|
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
|
+
});
|
|
14
|
+
|
|
15
|
+
const storeLIDPNMappings = async (pairs) => {
|
|
16
|
+
const pairMap = {};
|
|
17
|
+
for (const { lid, pn } of pairs) {
|
|
18
|
+
if (!(((0, WABinary_1.isLidUser)(lid) && (0, WABinary_1.isPnUser)(pn)) || ((0, WABinary_1.isPnUser)(lid) && (0, WABinary_1.isLidUser)(pn)))) {
|
|
19
|
+
logger && logger.warn(`Invalid LID-PN mapping: ${lid}, ${pn}`);
|
|
20
|
+
continue;
|
|
21
|
+
}
|
|
22
|
+
const lidDecoded = (0, WABinary_1.jidDecode)(lid);
|
|
23
|
+
const pnDecoded = (0, WABinary_1.jidDecode)(pn);
|
|
24
|
+
if (!lidDecoded || !pnDecoded) continue;
|
|
25
|
+
const pnUser = pnDecoded.user;
|
|
26
|
+
const lidUser = lidDecoded.user;
|
|
27
|
+
|
|
28
|
+
let existingLidUser = mappingCache.get(`pn:${pnUser}`);
|
|
29
|
+
if (!existingLidUser) {
|
|
30
|
+
logger && logger.trace(`Cache miss for PN user ${pnUser}; checking database`);
|
|
31
|
+
const stored = await keys.get("lid-mapping", [pnUser]);
|
|
32
|
+
existingLidUser = stored[pnUser];
|
|
33
|
+
if (existingLidUser) {
|
|
34
|
+
mappingCache.set(`pn:${pnUser}`, existingLidUser);
|
|
35
|
+
mappingCache.set(`lid:${existingLidUser}`, pnUser);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
if (existingLidUser === lidUser) {
|
|
39
|
+
logger && logger.debug({ pnUser, lidUser }, "LID mapping already exists, skipping");
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
42
|
+
pairMap[pnUser] = lidUser;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
logger && logger.trace({ pairMap }, `Storing ${Object.keys(pairMap).length} pn mappings`);
|
|
46
|
+
await keys.transaction(async () => {
|
|
47
|
+
for (const [pnUser, lidUser] of Object.entries(pairMap)) {
|
|
48
|
+
await keys.set({
|
|
49
|
+
"lid-mapping": {
|
|
50
|
+
[pnUser]: lidUser,
|
|
51
|
+
[`${lidUser}_reverse`]: pnUser
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
mappingCache.set(`pn:${pnUser}`, lidUser);
|
|
55
|
+
mappingCache.set(`lid:${lidUser}`, pnUser);
|
|
56
|
+
}
|
|
57
|
+
}, "lid-mapping");
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
const getLIDsForPNs = async (pns) => {
|
|
61
|
+
const usyncFetch = {};
|
|
62
|
+
const successfulPairs = {};
|
|
63
|
+
|
|
64
|
+
for (const pn of pns) {
|
|
65
|
+
if (!(0, WABinary_1.isPnUser)(pn) && !(0, WABinary_1.isHostedPnUser)(pn)) continue;
|
|
66
|
+
const decoded = (0, WABinary_1.jidDecode)(pn);
|
|
67
|
+
if (!decoded) continue;
|
|
68
|
+
const pnUser = decoded.user;
|
|
69
|
+
|
|
70
|
+
let lidUser = mappingCache.get(`pn:${pnUser}`);
|
|
71
|
+
if (!lidUser) {
|
|
72
|
+
const stored = await keys.get("lid-mapping", [pnUser]);
|
|
73
|
+
lidUser = stored[pnUser];
|
|
74
|
+
if (lidUser) {
|
|
75
|
+
mappingCache.set(`pn:${pnUser}`, lidUser);
|
|
76
|
+
mappingCache.set(`lid:${lidUser}`, pnUser);
|
|
77
|
+
} else {
|
|
78
|
+
logger && logger.trace(`No LID mapping found for PN user ${pnUser}; batch getting from USync`);
|
|
79
|
+
const device = decoded.device || 0;
|
|
80
|
+
let normalizedPn = (0, WABinary_1.jidNormalizedUser)(pn);
|
|
81
|
+
if ((0, WABinary_1.isHostedPnUser)(normalizedPn)) {
|
|
82
|
+
normalizedPn = `${pnUser}@s.whatsapp.net`;
|
|
83
|
+
}
|
|
84
|
+
if (!usyncFetch[normalizedPn]) {
|
|
85
|
+
usyncFetch[normalizedPn] = [device];
|
|
86
|
+
} else {
|
|
87
|
+
usyncFetch[normalizedPn]?.push(device);
|
|
88
|
+
}
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
lidUser = lidUser.toString();
|
|
94
|
+
if (!lidUser) {
|
|
95
|
+
logger && logger.warn(`Invalid or empty LID user for PN ${pn}: lidUser = "${lidUser}"`);
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
const pnDevice = decoded.device !== undefined ? decoded.device : 0;
|
|
99
|
+
const deviceSpecificLid = `${lidUser}${!!pnDevice ? `:${pnDevice}` : ``}@${decoded.server === "hosted" ? "hosted.lid" : "lid"}`;
|
|
100
|
+
logger && logger.trace(`getLIDForPN: ${pn} → ${deviceSpecificLid} (user mapping with device ${pnDevice})`);
|
|
101
|
+
successfulPairs[pn] = { lid: deviceSpecificLid, pn };
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (Object.keys(usyncFetch).length > 0) {
|
|
105
|
+
const result = await pnToLIDFunc?.(Object.keys(usyncFetch));
|
|
106
|
+
if (result && result.length > 0) {
|
|
107
|
+
await storeLIDPNMappings(result);
|
|
108
|
+
for (const pair of result) {
|
|
109
|
+
const pnDecoded = (0, WABinary_1.jidDecode)(pair.pn);
|
|
110
|
+
const pnUser = pnDecoded?.user;
|
|
111
|
+
if (!pnUser) continue;
|
|
112
|
+
const lidUser = (0, WABinary_1.jidDecode)(pair.lid)?.user;
|
|
113
|
+
if (!lidUser) continue;
|
|
114
|
+
|
|
115
|
+
for (const device of usyncFetch[pair.pn]) {
|
|
116
|
+
const deviceSpecificLid = `${lidUser}${!!device ? `:${device}` : ``}@${device === 99 ? "hosted.lid" : "lid"}`;
|
|
117
|
+
logger && logger.trace(`getLIDForPN: USYNC success for ${pair.pn} → ${deviceSpecificLid} (user mapping with device ${device})`);
|
|
118
|
+
const deviceSpecificPn = `${pnUser}${!!device ? `:${device}` : ``}@${device === 99 ? "hosted" : "s.whatsapp.net"}`;
|
|
119
|
+
successfulPairs[deviceSpecificPn] = { lid: deviceSpecificLid, pn: deviceSpecificPn };
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
} else {
|
|
123
|
+
return null;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
return Object.values(successfulPairs);
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
const getLIDForPN = async (pn) => {
|
|
130
|
+
const result = await getLIDsForPNs([pn]);
|
|
131
|
+
return result?.[0]?.lid || null;
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
const getPNForLID = async (lid) => {
|
|
135
|
+
if (!(0, WABinary_1.isLidUser)(lid)) return null;
|
|
136
|
+
const decoded = (0, WABinary_1.jidDecode)(lid);
|
|
137
|
+
if (!decoded) return null;
|
|
138
|
+
|
|
139
|
+
const lidUser = decoded.user;
|
|
140
|
+
let pnUser = mappingCache.get(`lid:${lidUser}`);
|
|
141
|
+
|
|
142
|
+
if (!pnUser || typeof pnUser !== "string") {
|
|
143
|
+
const stored = await keys.get("lid-mapping", [`${lidUser}_reverse`]);
|
|
144
|
+
pnUser = stored[`${lidUser}_reverse`];
|
|
145
|
+
if (!pnUser || typeof pnUser !== "string") {
|
|
146
|
+
logger && logger.trace(`No reverse mapping found for LID user: ${lidUser}`);
|
|
147
|
+
return null;
|
|
148
|
+
}
|
|
149
|
+
mappingCache.set(`lid:${lidUser}`, pnUser);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const lidDevice = decoded.device !== undefined ? decoded.device : 0;
|
|
153
|
+
const pnJid = `${pnUser}:${lidDevice}@${decoded.domainType === WABinary_1.WAJIDDomains.HOSTED_LID ? "hosted" : "s.whatsapp.net"}`;
|
|
154
|
+
logger && logger.trace(`Found reverse mapping: ${lid} → ${pnJid}`);
|
|
155
|
+
return pnJid;
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
return {
|
|
159
|
+
mappingCache,
|
|
160
|
+
storeLIDPNMappings,
|
|
161
|
+
getLIDForPN,
|
|
162
|
+
getLIDsForPNs,
|
|
163
|
+
getPNForLID
|
|
164
|
+
};
|
|
165
|
+
};
|
|
166
|
+
exports.makeLIDMappingStore = makeLIDMappingStore;
|