@nexustechpro/baileys 1.0.1 → 1.0.3-rc.1

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.
@@ -1,342 +1,404 @@
1
1
  /* @ts-ignore */
2
- import * as libsignal from 'libsignal';
3
- import { LRUCache } from 'lru-cache';
4
- import { generateSignalPubKey } from '../Utils/index.js';
5
- import { isHostedLidUser, isHostedPnUser, isLidUser, isPnUser, jidDecode, transferDevice, WAJIDDomains } from '../WABinary/index.js';
6
- import { SenderKeyName } from './Group/sender-key-name.js';
7
- import { SenderKeyRecord } from './Group/sender-key-record.js';
8
- import { GroupCipher, GroupSessionBuilder, SenderKeyDistributionMessage } from './Group/index.js';
9
- import { LIDMappingStore } from './lid-mapping.js';
2
+ import * as libsignal from "libsignal"
3
+ import { LRUCache } from "lru-cache"
4
+ import { generateSignalPubKey } from "../Utils/index.js"
5
+ import {
6
+ isHostedLidUser,
7
+ isHostedPnUser,
8
+ isLidUser,
9
+ isPnUser,
10
+ jidDecode,
11
+ transferDevice,
12
+ WAJIDDomains,
13
+ } from "../WABinary/index.js"
14
+ import { SenderKeyName } from "./Group/sender-key-name.js"
15
+ import { SenderKeyRecord } from "./Group/sender-key-record.js"
16
+ import { GroupCipher, GroupSessionBuilder, SenderKeyDistributionMessage } from "./Group/index.js"
17
+ import { LIDMappingStore } from "./lid-mapping.js"
18
+
10
19
  export function makeLibSignalRepository(auth, logger, pnToLIDFunc) {
11
- const lidMapping = new LIDMappingStore(auth.keys, logger, pnToLIDFunc);
12
- const storage = signalStorage(auth, lidMapping);
13
- const parsedKeys = auth.keys;
14
- const migratedSessionCache = new LRUCache({
15
- ttl: 7 * 24 * 60 * 60 * 1000, // 7 days
16
- ttlAutopurge: true,
17
- updateAgeOnGet: true
18
- });
19
- const repository = {
20
- decryptGroupMessage({ group, authorJid, msg }) {
21
- const senderName = jidToSignalSenderKeyName(group, authorJid);
22
- const cipher = new GroupCipher(storage, senderName);
23
- // Use transaction to ensure atomicity
24
- return parsedKeys.transaction(async () => {
25
- return cipher.decrypt(msg);
26
- }, group);
27
- },
28
- async processSenderKeyDistributionMessage({ item, authorJid }) {
29
- const builder = new GroupSessionBuilder(storage);
30
- if (!item.groupId) {
31
- throw new Error('Group ID is required for sender key distribution message');
32
- }
33
- const senderName = jidToSignalSenderKeyName(item.groupId, authorJid);
34
- const senderMsg = new SenderKeyDistributionMessage(null, null, null, null, item.axolotlSenderKeyDistributionMessage);
35
- const senderNameStr = senderName.toString();
36
- const { [senderNameStr]: senderKey } = await auth.keys.get('sender-key', [senderNameStr]);
37
- if (!senderKey) {
38
- await storage.storeSenderKey(senderName, new SenderKeyRecord());
39
- }
40
- return parsedKeys.transaction(async () => {
41
- const { [senderNameStr]: senderKey } = await auth.keys.get('sender-key', [senderNameStr]);
42
- if (!senderKey) {
43
- await storage.storeSenderKey(senderName, new SenderKeyRecord());
44
- }
45
- await builder.process(senderName, senderMsg);
46
- }, item.groupId);
20
+ const lidMapping = new LIDMappingStore(auth.keys, logger, pnToLIDFunc)
21
+ const storage = signalStorage(auth, lidMapping)
22
+ const parsedKeys = auth.keys
23
+ const migratedSessionCache = new LRUCache({
24
+ ttl: 7 * 24 * 60 * 60 * 1000, // 7 days
25
+ ttlAutopurge: true,
26
+ updateAgeOnGet: true,
27
+ })
28
+
29
+ const repository = {
30
+ decryptGroupMessage({ group, authorJid, msg }) {
31
+ const senderName = jidToSignalSenderKeyName(group, authorJid)
32
+ const cipher = new GroupCipher(storage, senderName)
33
+ // Use transaction to ensure atomicity
34
+ return parsedKeys.transaction(async () => {
35
+ return cipher.decrypt(msg)
36
+ }, group)
37
+ },
38
+
39
+ async processSenderKeyDistributionMessage({ item, authorJid }) {
40
+ const builder = new GroupSessionBuilder(storage)
41
+ if (!item.groupId) {
42
+ throw new Error("Group ID is required for sender key distribution message")
43
+ }
44
+ const senderName = jidToSignalSenderKeyName(item.groupId, authorJid)
45
+ const senderMsg = new SenderKeyDistributionMessage(
46
+ null,
47
+ null,
48
+ null,
49
+ null,
50
+ item.axolotlSenderKeyDistributionMessage,
51
+ )
52
+ const senderNameStr = senderName.toString()
53
+ const { [senderNameStr]: senderKey } = await auth.keys.get("sender-key", [senderNameStr])
54
+ if (!senderKey) {
55
+ await storage.storeSenderKey(senderName, new SenderKeyRecord())
56
+ }
57
+ return parsedKeys.transaction(async () => {
58
+ const { [senderNameStr]: senderKey } = await auth.keys.get("sender-key", [senderNameStr])
59
+ if (!senderKey) {
60
+ await storage.storeSenderKey(senderName, new SenderKeyRecord())
61
+ }
62
+ await builder.process(senderName, senderMsg)
63
+ }, item.groupId)
64
+ },
65
+
66
+ async decryptMessage({ jid, type, ciphertext }) {
67
+ const addr = jidToSignalProtocolAddress(jid)
68
+ const session = new libsignal.SessionCipher(storage, addr)
69
+
70
+ async function doDecrypt() {
71
+ let result
72
+ switch (type) {
73
+ case "pkmsg":
74
+ result = await session.decryptPreKeyWhisperMessage(ciphertext)
75
+ break
76
+ case "msg":
77
+ result = await session.decryptWhisperMessage(ciphertext)
78
+ break
79
+ }
80
+ return result
81
+ }
82
+
83
+ // For regular messages, use a transaction to ensure atomicity
84
+ try {
85
+ return parsedKeys.transaction(async () => {
86
+ return await doDecrypt()
87
+ }, jid)
88
+ } catch (error) {
89
+ const errorMsg = error?.message || error?.toString() || ""
90
+ const isBadMac = errorMsg.includes("Bad MAC")
91
+ const isMessageCounter = errorMsg.includes("Key used already or never filled")
92
+
93
+ if (isBadMac || isMessageCounter) {
94
+ logger.warn(
95
+ { jid, error: errorMsg, isBadMac, isMessageCounter },
96
+ "Signal session error detected - session corrupted"
97
+ )
98
+
99
+ // Re-throw with original error message preserved for handleMessage to detect
100
+ const originalError = new Error(errorMsg)
101
+ originalError.name = error.name
102
+ originalError.stack = error.stack
103
+ throw originalError
104
+ }
105
+
106
+ throw error
107
+ }
108
+ },
109
+
110
+ async encryptMessage({ jid, data }) {
111
+ const addr = jidToSignalProtocolAddress(jid)
112
+ const cipher = new libsignal.SessionCipher(storage, addr)
113
+ // Use transaction to ensure atomicity
114
+ return parsedKeys.transaction(async () => {
115
+ const { type: sigType, body } = await cipher.encrypt(data)
116
+ const type = sigType === 3 ? "pkmsg" : "msg"
117
+ return { type, ciphertext: Buffer.from(body, "binary") }
118
+ }, jid)
119
+ },
120
+
121
+ async encryptGroupMessage({ group, meId, data }) {
122
+ const senderName = jidToSignalSenderKeyName(group, meId)
123
+ const builder = new GroupSessionBuilder(storage)
124
+ const senderNameStr = senderName.toString()
125
+ return parsedKeys.transaction(async () => {
126
+ const { [senderNameStr]: senderKey } = await auth.keys.get("sender-key", [senderNameStr])
127
+ if (!senderKey) {
128
+ await storage.storeSenderKey(senderName, new SenderKeyRecord())
129
+ }
130
+ const senderKeyDistributionMessage = await builder.create(senderName)
131
+ const session = new GroupCipher(storage, senderName)
132
+ const ciphertext = await session.encrypt(data)
133
+ return {
134
+ ciphertext,
135
+ senderKeyDistributionMessage: senderKeyDistributionMessage.serialize(),
136
+ }
137
+ }, group)
138
+ },
139
+
140
+ async injectE2ESession({ jid, session }) {
141
+ logger.trace({ jid }, "injecting E2EE session")
142
+ const cipher = new libsignal.SessionBuilder(storage, jidToSignalProtocolAddress(jid))
143
+ return parsedKeys.transaction(async () => {
144
+ await cipher.initOutgoing(session)
145
+ }, jid)
146
+ },
147
+
148
+ jidToSignalProtocolAddress(jid) {
149
+ return jidToSignalProtocolAddress(jid).toString()
150
+ },
151
+
152
+ // Optimized direct access to LID mapping store
153
+ lidMapping,
154
+
155
+ async validateSession(jid) {
156
+ try {
157
+ const addr = jidToSignalProtocolAddress(jid)
158
+ const session = await storage.loadSession(addr.toString())
159
+ if (!session) {
160
+ return { exists: false, reason: "no session" }
161
+ }
162
+ if (!session.haveOpenSession()) {
163
+ return { exists: false, reason: "no open session" }
164
+ }
165
+ return { exists: true }
166
+ } catch (error) {
167
+ return { exists: false, reason: "validation error" }
168
+ }
169
+ },
170
+
171
+ async deleteSession(jids) {
172
+ if (!jids.length) return
173
+ // Convert JIDs to signal addresses and prepare for bulk deletion
174
+ const sessionUpdates = {}
175
+ jids.forEach((jid) => {
176
+ const addr = jidToSignalProtocolAddress(jid)
177
+ sessionUpdates[addr.toString()] = null
178
+ })
179
+ // Single transaction for all deletions
180
+ return parsedKeys.transaction(async () => {
181
+ await auth.keys.set({ session: sessionUpdates })
182
+ }, `delete-${jids.length}-sessions`)
183
+ },
184
+
185
+ async migrateSession(fromJid, toJid) {
186
+ // TODO: use usync to handle this entire mess
187
+ if (!fromJid || (!isLidUser(toJid) && !isHostedLidUser(toJid))) return { migrated: 0, skipped: 0, total: 0 }
188
+ // Only support PN to LID migration
189
+ if (!isPnUser(fromJid) && !isHostedPnUser(fromJid)) {
190
+ return { migrated: 0, skipped: 0, total: 1 }
191
+ }
192
+ const { user } = jidDecode(fromJid)
193
+ logger.debug({ fromJid }, "bulk device migration - loading all user devices")
194
+ // Get user's device list from storage
195
+ const { [user]: userDevices } = await parsedKeys.get("device-list", [user])
196
+ if (!userDevices) {
197
+ return { migrated: 0, skipped: 0, total: 0 }
198
+ }
199
+ const { device: fromDevice } = jidDecode(fromJid)
200
+ const fromDeviceStr = fromDevice?.toString() || "0"
201
+ if (!userDevices.includes(fromDeviceStr)) {
202
+ userDevices.push(fromDeviceStr)
203
+ }
204
+ // Filter out cached devices before database fetch
205
+ const uncachedDevices = userDevices.filter((device) => {
206
+ const deviceKey = `${user}.${device}`
207
+ return !migratedSessionCache.has(deviceKey)
208
+ })
209
+ // Bulk check session existence only for uncached devices
210
+ const deviceSessionKeys = uncachedDevices.map((device) => `${user}.${device}`)
211
+ const existingSessions = await parsedKeys.get("session", deviceSessionKeys)
212
+ // Step 3: Convert existing sessions to JIDs (only migrate sessions that exist)
213
+ const deviceJids = []
214
+ for (const [sessionKey, sessionData] of Object.entries(existingSessions)) {
215
+ if (sessionData) {
216
+ // Session exists in storage
217
+ const deviceStr = sessionKey.split(".")[1]
218
+ if (!deviceStr) continue
219
+ const deviceNum = Number.parseInt(deviceStr)
220
+ let jid = deviceNum === 0 ? `${user}@s.whatsapp.net` : `${user}:${deviceNum}@s.whatsapp.net`
221
+ if (deviceNum === 99) {
222
+ jid = `${user}:99@hosted`
223
+ }
224
+ deviceJids.push(jid)
225
+ }
226
+ }
227
+ logger.debug(
228
+ {
229
+ fromJid,
230
+ totalDevices: userDevices.length,
231
+ devicesWithSessions: deviceJids.length,
232
+ devices: deviceJids,
47
233
  },
48
- async decryptMessage({ jid, type, ciphertext }) {
49
- const addr = jidToSignalProtocolAddress(jid);
50
- const session = new libsignal.SessionCipher(storage, addr);
51
- async function doDecrypt() {
52
- let result;
53
- switch (type) {
54
- case 'pkmsg':
55
- result = await session.decryptPreKeyWhisperMessage(ciphertext);
56
- break;
57
- case 'msg':
58
- result = await session.decryptWhisperMessage(ciphertext);
59
- break;
60
- }
61
- return result;
234
+ "bulk device migration complete - all user devices processed",
235
+ )
236
+ // Single transaction for all migrations
237
+ return parsedKeys.transaction(
238
+ async () => {
239
+ const migrationOps = deviceJids.map((jid) => {
240
+ const lidWithDevice = transferDevice(jid, toJid)
241
+ const fromDecoded = jidDecode(jid)
242
+ const toDecoded = jidDecode(lidWithDevice)
243
+ return {
244
+ fromJid: jid,
245
+ toJid: lidWithDevice,
246
+ pnUser: fromDecoded.user,
247
+ lidUser: toDecoded.user,
248
+ deviceId: fromDecoded.device || 0,
249
+ fromAddr: jidToSignalProtocolAddress(jid),
250
+ toAddr: jidToSignalProtocolAddress(lidWithDevice),
62
251
  }
63
- // If it's not a sync message, we need to ensure atomicity
64
- // For regular messages, we use a transaction to ensure atomicity
65
- return parsedKeys.transaction(async () => {
66
- return await doDecrypt();
67
- }, jid);
68
- },
69
- async encryptMessage({ jid, data }) {
70
- const addr = jidToSignalProtocolAddress(jid);
71
- const cipher = new libsignal.SessionCipher(storage, addr);
72
- // Use transaction to ensure atomicity
73
- return parsedKeys.transaction(async () => {
74
- const { type: sigType, body } = await cipher.encrypt(data);
75
- const type = sigType === 3 ? 'pkmsg' : 'msg';
76
- return { type, ciphertext: Buffer.from(body, 'binary') };
77
- }, jid);
78
- },
79
- async encryptGroupMessage({ group, meId, data }) {
80
- const senderName = jidToSignalSenderKeyName(group, meId);
81
- const builder = new GroupSessionBuilder(storage);
82
- const senderNameStr = senderName.toString();
83
- return parsedKeys.transaction(async () => {
84
- const { [senderNameStr]: senderKey } = await auth.keys.get('sender-key', [senderNameStr]);
85
- if (!senderKey) {
86
- await storage.storeSenderKey(senderName, new SenderKeyRecord());
87
- }
88
- const senderKeyDistributionMessage = await builder.create(senderName);
89
- const session = new GroupCipher(storage, senderName);
90
- const ciphertext = await session.encrypt(data);
91
- return {
92
- ciphertext,
93
- senderKeyDistributionMessage: senderKeyDistributionMessage.serialize()
94
- };
95
- }, group);
96
- },
97
- async injectE2ESession({ jid, session }) {
98
- logger.trace({ jid }, 'injecting E2EE session');
99
- const cipher = new libsignal.SessionBuilder(storage, jidToSignalProtocolAddress(jid));
100
- return parsedKeys.transaction(async () => {
101
- await cipher.initOutgoing(session);
102
- }, jid);
103
- },
104
- jidToSignalProtocolAddress(jid) {
105
- return jidToSignalProtocolAddress(jid).toString();
106
- },
107
- // Optimized direct access to LID mapping store
108
- lidMapping,
109
- async validateSession(jid) {
110
- try {
111
- const addr = jidToSignalProtocolAddress(jid);
112
- const session = await storage.loadSession(addr.toString());
113
- if (!session) {
114
- return { exists: false, reason: 'no session' };
115
- }
116
- if (!session.haveOpenSession()) {
117
- return { exists: false, reason: 'no open session' };
118
- }
119
- return { exists: true };
252
+ })
253
+ const totalOps = migrationOps.length
254
+ let migratedCount = 0
255
+ // Bulk fetch PN sessions - already exist (verified during device discovery)
256
+ const pnAddrStrings = Array.from(new Set(migrationOps.map((op) => op.fromAddr.toString())))
257
+ const pnSessions = await parsedKeys.get("session", pnAddrStrings)
258
+ // Prepare bulk session updates (PN LID migration + deletion)
259
+ const sessionUpdates = {}
260
+ for (const op of migrationOps) {
261
+ const pnAddrStr = op.fromAddr.toString()
262
+ const lidAddrStr = op.toAddr.toString()
263
+ const pnSession = pnSessions[pnAddrStr]
264
+ if (pnSession) {
265
+ // Session exists (guaranteed from device discovery)
266
+ const fromSession = libsignal.SessionRecord.deserialize(pnSession)
267
+ if (fromSession.haveOpenSession()) {
268
+ // Queue for bulk update: copy to LID, delete from PN
269
+ sessionUpdates[lidAddrStr] = fromSession.serialize()
270
+ sessionUpdates[pnAddrStr] = null
271
+ migratedCount++
272
+ }
120
273
  }
121
- catch (error) {
122
- return { exists: false, reason: 'validation error' };
274
+ }
275
+ // Single bulk session update for all migrations
276
+ if (Object.keys(sessionUpdates).length > 0) {
277
+ await parsedKeys.set({ session: sessionUpdates })
278
+ logger.debug({ migratedSessions: migratedCount }, "bulk session migration complete")
279
+ // Cache device-level migrations
280
+ for (const op of migrationOps) {
281
+ if (sessionUpdates[op.toAddr.toString()]) {
282
+ const deviceKey = `${op.pnUser}.${op.deviceId}`
283
+ migratedSessionCache.set(deviceKey, true)
284
+ }
123
285
  }
286
+ }
287
+ const skippedCount = totalOps - migratedCount
288
+ return { migrated: migratedCount, skipped: skippedCount, total: totalOps }
124
289
  },
125
- async deleteSession(jids) {
126
- if (!jids.length)
127
- return;
128
- // Convert JIDs to signal addresses and prepare for bulk deletion
129
- const sessionUpdates = {};
130
- jids.forEach(jid => {
131
- const addr = jidToSignalProtocolAddress(jid);
132
- sessionUpdates[addr.toString()] = null;
133
- });
134
- // Single transaction for all deletions
135
- return parsedKeys.transaction(async () => {
136
- await auth.keys.set({ session: sessionUpdates });
137
- }, `delete-${jids.length}-sessions`);
138
- },
139
- async migrateSession(fromJid, toJid) {
140
- // TODO: use usync to handle this entire mess
141
- if (!fromJid || (!isLidUser(toJid) && !isHostedLidUser(toJid)))
142
- return { migrated: 0, skipped: 0, total: 0 };
143
- // Only support PN to LID migration
144
- if (!isPnUser(fromJid) && !isHostedPnUser(fromJid)) {
145
- return { migrated: 0, skipped: 0, total: 1 };
146
- }
147
- const { user } = jidDecode(fromJid);
148
- logger.debug({ fromJid }, 'bulk device migration - loading all user devices');
149
- // Get user's device list from storage
150
- const { [user]: userDevices } = await parsedKeys.get('device-list', [user]);
151
- if (!userDevices) {
152
- return { migrated: 0, skipped: 0, total: 0 };
153
- }
154
- const { device: fromDevice } = jidDecode(fromJid);
155
- const fromDeviceStr = fromDevice?.toString() || '0';
156
- if (!userDevices.includes(fromDeviceStr)) {
157
- userDevices.push(fromDeviceStr);
158
- }
159
- // Filter out cached devices before database fetch
160
- const uncachedDevices = userDevices.filter(device => {
161
- const deviceKey = `${user}.${device}`;
162
- return !migratedSessionCache.has(deviceKey);
163
- });
164
- // Bulk check session existence only for uncached devices
165
- const deviceSessionKeys = uncachedDevices.map(device => `${user}.${device}`);
166
- const existingSessions = await parsedKeys.get('session', deviceSessionKeys);
167
- // Step 3: Convert existing sessions to JIDs (only migrate sessions that exist)
168
- const deviceJids = [];
169
- for (const [sessionKey, sessionData] of Object.entries(existingSessions)) {
170
- if (sessionData) {
171
- // Session exists in storage
172
- const deviceStr = sessionKey.split('.')[1];
173
- if (!deviceStr)
174
- continue;
175
- const deviceNum = parseInt(deviceStr);
176
- let jid = deviceNum === 0 ? `${user}@s.whatsapp.net` : `${user}:${deviceNum}@s.whatsapp.net`;
177
- if (deviceNum === 99) {
178
- jid = `${user}:99@hosted`;
179
- }
180
- deviceJids.push(jid);
181
- }
182
- }
183
- logger.debug({
184
- fromJid,
185
- totalDevices: userDevices.length,
186
- devicesWithSessions: deviceJids.length,
187
- devices: deviceJids
188
- }, 'bulk device migration complete - all user devices processed');
189
- // Single transaction for all migrations
190
- return parsedKeys.transaction(async () => {
191
- const migrationOps = deviceJids.map(jid => {
192
- const lidWithDevice = transferDevice(jid, toJid);
193
- const fromDecoded = jidDecode(jid);
194
- const toDecoded = jidDecode(lidWithDevice);
195
- return {
196
- fromJid: jid,
197
- toJid: lidWithDevice,
198
- pnUser: fromDecoded.user,
199
- lidUser: toDecoded.user,
200
- deviceId: fromDecoded.device || 0,
201
- fromAddr: jidToSignalProtocolAddress(jid),
202
- toAddr: jidToSignalProtocolAddress(lidWithDevice)
203
- };
204
- });
205
- const totalOps = migrationOps.length;
206
- let migratedCount = 0;
207
- // Bulk fetch PN sessions - already exist (verified during device discovery)
208
- const pnAddrStrings = Array.from(new Set(migrationOps.map(op => op.fromAddr.toString())));
209
- const pnSessions = await parsedKeys.get('session', pnAddrStrings);
210
- // Prepare bulk session updates (PN → LID migration + deletion)
211
- const sessionUpdates = {};
212
- for (const op of migrationOps) {
213
- const pnAddrStr = op.fromAddr.toString();
214
- const lidAddrStr = op.toAddr.toString();
215
- const pnSession = pnSessions[pnAddrStr];
216
- if (pnSession) {
217
- // Session exists (guaranteed from device discovery)
218
- const fromSession = libsignal.SessionRecord.deserialize(pnSession);
219
- if (fromSession.haveOpenSession()) {
220
- // Queue for bulk update: copy to LID, delete from PN
221
- sessionUpdates[lidAddrStr] = fromSession.serialize();
222
- sessionUpdates[pnAddrStr] = null;
223
- migratedCount++;
224
- }
225
- }
226
- }
227
- // Single bulk session update for all migrations
228
- if (Object.keys(sessionUpdates).length > 0) {
229
- await parsedKeys.set({ session: sessionUpdates });
230
- logger.debug({ migratedSessions: migratedCount }, 'bulk session migration complete');
231
- // Cache device-level migrations
232
- for (const op of migrationOps) {
233
- if (sessionUpdates[op.toAddr.toString()]) {
234
- const deviceKey = `${op.pnUser}.${op.deviceId}`;
235
- migratedSessionCache.set(deviceKey, true);
236
- }
237
- }
238
- }
239
- const skippedCount = totalOps - migratedCount;
240
- return { migrated: migratedCount, skipped: skippedCount, total: totalOps };
241
- }, `migrate-${deviceJids.length}-sessions-${jidDecode(toJid)?.user}`);
242
- }
243
- };
244
- return repository;
290
+ `migrate-${deviceJids.length}-sessions-${jidDecode(toJid)?.user}`,
291
+ )
292
+ },
293
+ }
294
+ return repository
245
295
  }
296
+
246
297
  const jidToSignalProtocolAddress = (jid) => {
247
- const decoded = jidDecode(jid);
248
- const { user, device, server, domainType } = decoded;
249
- if (!user) {
250
- throw new Error(`JID decoded but user is empty: "${jid}" -> user: "${user}", server: "${server}", device: ${device}`);
251
- }
252
- const signalUser = domainType !== WAJIDDomains.WHATSAPP ? `${user}_${domainType}` : user;
253
- const finalDevice = device || 0;
254
- if (device === 99 && decoded.server !== 'hosted' && decoded.server !== 'hosted.lid') {
255
- throw new Error('Unexpected non-hosted device JID with device 99. This ID seems invalid. ID:' + jid);
256
- }
257
- return new libsignal.ProtocolAddress(signalUser, finalDevice);
258
- };
298
+ const decoded = jidDecode(jid)
299
+ const { user, device, server, domainType } = decoded
300
+ if (!user) {
301
+ throw new Error(
302
+ `JID decoded but user is empty: "${jid}" -> user: "${user}", server: "${server}", device: ${device}`,
303
+ )
304
+ }
305
+ const signalUser = domainType !== WAJIDDomains.WHATSAPP ? `${user}_${domainType}` : user
306
+ const finalDevice = device || 0
307
+ if (device === 99 && decoded.server !== "hosted" && decoded.server !== "hosted.lid") {
308
+ throw new Error("Unexpected non-hosted device JID with device 99. This ID seems invalid. ID:" + jid)
309
+ }
310
+ return new libsignal.ProtocolAddress(signalUser, finalDevice)
311
+ }
312
+
259
313
  const jidToSignalSenderKeyName = (group, user) => {
260
- return new SenderKeyName(group, jidToSignalProtocolAddress(user));
261
- };
314
+ return new SenderKeyName(group, jidToSignalProtocolAddress(user))
315
+ }
316
+
262
317
  function signalStorage({ creds, keys }, lidMapping) {
263
- // Shared function to resolve PN signal address to LID if mapping exists
264
- const resolveLIDSignalAddress = async (id) => {
265
- if (id.includes('.')) {
266
- const [deviceId, device] = id.split('.');
267
- const [user, domainType_] = deviceId.split('_');
268
- const domainType = parseInt(domainType_ || '0');
269
- if (domainType === WAJIDDomains.LID || domainType === WAJIDDomains.HOSTED_LID)
270
- return id;
271
- const pnJid = `${user}${device !== '0' ? `:${device}` : ''}@${domainType === WAJIDDomains.HOSTED ? 'hosted' : 's.whatsapp.net'}`;
272
- const lidForPN = await lidMapping.getLIDForPN(pnJid);
273
- if (lidForPN) {
274
- const lidAddr = jidToSignalProtocolAddress(lidForPN);
275
- return lidAddr.toString();
276
- }
318
+ // Shared function to resolve PN signal address to LID if mapping exists
319
+ const resolveLIDSignalAddress = async (id) => {
320
+ if (id.includes(".")) {
321
+ const [deviceId, device] = id.split(".")
322
+ const [user, domainType_] = deviceId.split("_")
323
+ const domainType = Number.parseInt(domainType_ || "0")
324
+ if (domainType === WAJIDDomains.LID || domainType === WAJIDDomains.HOSTED_LID) return id
325
+ const pnJid = `${user}${device !== "0" ? `:${device}` : ""}@${domainType === WAJIDDomains.HOSTED ? "hosted" : "s.whatsapp.net"}`
326
+ const lidForPN = await lidMapping.getLIDForPN(pnJid)
327
+ if (lidForPN) {
328
+ const lidAddr = jidToSignalProtocolAddress(lidForPN)
329
+ return lidAddr.toString()
330
+ }
331
+ }
332
+ return id
333
+ }
334
+
335
+ return {
336
+ loadSession: async (id) => {
337
+ try {
338
+ const wireJid = await resolveLIDSignalAddress(id)
339
+ const { [wireJid]: sess } = await keys.get("session", [wireJid])
340
+ if (sess) {
341
+ return libsignal.SessionRecord.deserialize(sess)
277
342
  }
278
- return id;
279
- };
280
- return {
281
- loadSession: async (id) => {
282
- try {
283
- const wireJid = await resolveLIDSignalAddress(id);
284
- const { [wireJid]: sess } = await keys.get('session', [wireJid]);
285
- if (sess) {
286
- return libsignal.SessionRecord.deserialize(sess);
287
- }
288
- }
289
- catch (e) {
290
- return null;
291
- }
292
- return null;
293
- },
294
- storeSession: async (id, session) => {
295
- const wireJid = await resolveLIDSignalAddress(id);
296
- await keys.set({ session: { [wireJid]: session.serialize() } });
297
- },
298
- isTrustedIdentity: () => {
299
- return true; // todo: implement
300
- },
301
- loadPreKey: async (id) => {
302
- const keyId = id.toString();
303
- const { [keyId]: key } = await keys.get('pre-key', [keyId]);
304
- if (key) {
305
- return {
306
- privKey: Buffer.from(key.private),
307
- pubKey: Buffer.from(key.public)
308
- };
309
- }
310
- },
311
- removePreKey: (id) => keys.set({ 'pre-key': { [id]: null } }),
312
- loadSignedPreKey: () => {
313
- const key = creds.signedPreKey;
314
- return {
315
- privKey: Buffer.from(key.keyPair.private),
316
- pubKey: Buffer.from(key.keyPair.public)
317
- };
318
- },
319
- loadSenderKey: async (senderKeyName) => {
320
- const keyId = senderKeyName.toString();
321
- const { [keyId]: key } = await keys.get('sender-key', [keyId]);
322
- if (key) {
323
- return SenderKeyRecord.deserialize(key);
324
- }
325
- return new SenderKeyRecord();
326
- },
327
- storeSenderKey: async (senderKeyName, key) => {
328
- const keyId = senderKeyName.toString();
329
- const serialized = JSON.stringify(key.serialize());
330
- await keys.set({ 'sender-key': { [keyId]: Buffer.from(serialized, 'utf-8') } });
331
- },
332
- getOurRegistrationId: () => creds.registrationId,
333
- getOurIdentity: () => {
334
- const { signedIdentityKey } = creds;
335
- return {
336
- privKey: Buffer.from(signedIdentityKey.private),
337
- pubKey: Buffer.from(generateSignalPubKey(signedIdentityKey.public))
338
- };
343
+ } catch (e) {
344
+ return null
345
+ }
346
+ return null
347
+ },
348
+
349
+ storeSession: async (id, session) => {
350
+ const wireJid = await resolveLIDSignalAddress(id)
351
+ await keys.set({ session: { [wireJid]: session.serialize() } })
352
+ },
353
+
354
+ isTrustedIdentity: () => {
355
+ return true // todo: implement
356
+ },
357
+
358
+ loadPreKey: async (id) => {
359
+ const keyId = id.toString()
360
+ const { [keyId]: key } = await keys.get("pre-key", [keyId])
361
+ if (key) {
362
+ return {
363
+ privKey: Buffer.from(key.private),
364
+ pubKey: Buffer.from(key.public),
339
365
  }
340
- };
341
- }
342
- //# sourceMappingURL=libsignal.js.map
366
+ }
367
+ },
368
+
369
+ removePreKey: (id) => keys.set({ "pre-key": { [id]: null } }),
370
+
371
+ loadSignedPreKey: () => {
372
+ const key = creds.signedPreKey
373
+ return {
374
+ privKey: Buffer.from(key.keyPair.private),
375
+ pubKey: Buffer.from(key.keyPair.public),
376
+ }
377
+ },
378
+
379
+ loadSenderKey: async (senderKeyName) => {
380
+ const keyId = senderKeyName.toString()
381
+ const { [keyId]: key } = await keys.get("sender-key", [keyId])
382
+ if (key) {
383
+ return SenderKeyRecord.deserialize(key)
384
+ }
385
+ return new SenderKeyRecord()
386
+ },
387
+
388
+ storeSenderKey: async (senderKeyName, key) => {
389
+ const keyId = senderKeyName.toString()
390
+ const serialized = JSON.stringify(key.serialize())
391
+ await keys.set({ "sender-key": { [keyId]: Buffer.from(serialized, "utf-8") } })
392
+ },
393
+
394
+ getOurRegistrationId: () => creds.registrationId,
395
+
396
+ getOurIdentity: () => {
397
+ const { signedIdentityKey } = creds
398
+ return {
399
+ privKey: Buffer.from(signedIdentityKey.private),
400
+ pubKey: Buffer.from(generateSignalPubKey(signedIdentityKey.public)),
401
+ }
402
+ },
403
+ }
404
+ }