@periskope/baileys 6.7.18-17-3 → 6.7.18-17-4
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 +147 -9
- package/lib/Signal/lid-mapping.d.ts +17 -0
- package/lib/Signal/lid-mapping.js +89 -0
- package/lib/Socket/business.d.ts +4 -2
- package/lib/Socket/index.d.ts +4 -2
- package/lib/Socket/messages-recv.d.ts +4 -2
- package/lib/Socket/messages-recv.js +31 -1
- package/lib/Socket/messages-send.d.ts +4 -2
- package/lib/Socket/messages-send.js +438 -59
- package/lib/Socket/socket.js +17 -0
- package/lib/Types/Auth.d.ts +1 -0
- package/lib/Types/Signal.d.ts +20 -0
- package/lib/Utils/decode-wa-message.d.ts +5 -0
- package/lib/Utils/decode-wa-message.js +51 -2
- package/package.json +2 -1
package/lib/Signal/libsignal.js
CHANGED
|
@@ -35,15 +35,24 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
36
|
exports.makeLibSignalRepository = makeLibSignalRepository;
|
|
37
37
|
const libsignal = __importStar(require("libsignal"));
|
|
38
|
+
/* @ts-ignore */
|
|
39
|
+
const lru_cache_1 = require("lru-cache");
|
|
38
40
|
const Utils_1 = require("../Utils");
|
|
39
41
|
const WABinary_1 = require("../WABinary");
|
|
40
42
|
const sender_key_name_1 = require("./Group/sender-key-name");
|
|
41
43
|
const sender_key_record_1 = require("./Group/sender-key-record");
|
|
42
44
|
const Group_1 = require("./Group");
|
|
45
|
+
const lid_mapping_1 = require("./lid-mapping");
|
|
43
46
|
function makeLibSignalRepository(auth) {
|
|
44
|
-
const
|
|
47
|
+
const lidMapping = new lid_mapping_1.LIDMappingStore(auth.keys);
|
|
48
|
+
const storage = signalStorage(auth, lidMapping);
|
|
49
|
+
// Simple operation-level deduplication (5 minutes)
|
|
50
|
+
const recentMigrations = new lru_cache_1.LRUCache({
|
|
51
|
+
max: 500,
|
|
52
|
+
ttl: 5 * 60 * 1000
|
|
53
|
+
});
|
|
45
54
|
const parsedKeys = auth.keys;
|
|
46
|
-
|
|
55
|
+
const repository = {
|
|
47
56
|
decryptGroupMessage({ group, authorJid, msg }) {
|
|
48
57
|
const senderName = jidToSignalSenderKeyName(group, authorJid);
|
|
49
58
|
const cipher = new Group_1.GroupCipher(storage, senderName);
|
|
@@ -92,7 +101,31 @@ function makeLibSignalRepository(auth) {
|
|
|
92
101
|
});
|
|
93
102
|
},
|
|
94
103
|
async encryptMessage({ jid, data }) {
|
|
95
|
-
|
|
104
|
+
// LID SINGLE SOURCE OF TRUTH: Always prefer LID when available
|
|
105
|
+
let encryptionJid = jid;
|
|
106
|
+
// Check for LID mapping and use it if session exists
|
|
107
|
+
if (jid.includes('@s.whatsapp.net')) {
|
|
108
|
+
const lidForPN = await lidMapping.getLIDForPN(jid);
|
|
109
|
+
if (lidForPN === null || lidForPN === void 0 ? void 0 : lidForPN.includes('@lid')) {
|
|
110
|
+
const lidAddr = jidToSignalProtocolAddress(lidForPN);
|
|
111
|
+
const { [lidAddr.toString()]: lidSession } = await auth.keys.get('session', [lidAddr.toString()]);
|
|
112
|
+
if (lidSession) {
|
|
113
|
+
// LID session exists, use it
|
|
114
|
+
encryptionJid = lidForPN;
|
|
115
|
+
}
|
|
116
|
+
else {
|
|
117
|
+
// Try to migrate if PN session exists
|
|
118
|
+
const pnAddr = jidToSignalProtocolAddress(jid);
|
|
119
|
+
const { [pnAddr.toString()]: pnSession } = await auth.keys.get('session', [pnAddr.toString()]);
|
|
120
|
+
if (pnSession) {
|
|
121
|
+
// Migrate PN to LID
|
|
122
|
+
await repository.migrateSession(jid, lidForPN);
|
|
123
|
+
encryptionJid = lidForPN;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
const addr = jidToSignalProtocolAddress(encryptionJid);
|
|
96
129
|
const cipher = new libsignal.SessionCipher(storage, addr);
|
|
97
130
|
// Use transaction to ensure atomicity
|
|
98
131
|
return parsedKeys.transaction(async () => {
|
|
@@ -127,23 +160,128 @@ function makeLibSignalRepository(auth) {
|
|
|
127
160
|
},
|
|
128
161
|
jidToSignalProtocolAddress(jid) {
|
|
129
162
|
return jidToSignalProtocolAddress(jid).toString();
|
|
163
|
+
},
|
|
164
|
+
async storeLIDPNMapping(lid, pn) {
|
|
165
|
+
await lidMapping.storeLIDPNMapping(lid, pn);
|
|
166
|
+
},
|
|
167
|
+
getLIDMappingStore() {
|
|
168
|
+
return lidMapping;
|
|
169
|
+
},
|
|
170
|
+
async validateSession(jid) {
|
|
171
|
+
try {
|
|
172
|
+
const addr = jidToSignalProtocolAddress(jid);
|
|
173
|
+
const session = await storage.loadSession(addr.toString());
|
|
174
|
+
if (!session) {
|
|
175
|
+
return { exists: false, reason: 'no session' };
|
|
176
|
+
}
|
|
177
|
+
if (!session.haveOpenSession()) {
|
|
178
|
+
return { exists: false, reason: 'no open session' };
|
|
179
|
+
}
|
|
180
|
+
return { exists: true };
|
|
181
|
+
}
|
|
182
|
+
catch (error) {
|
|
183
|
+
return { exists: false, reason: 'validation error' };
|
|
184
|
+
}
|
|
185
|
+
},
|
|
186
|
+
async deleteSession(jid) {
|
|
187
|
+
const addr = jidToSignalProtocolAddress(jid);
|
|
188
|
+
return auth.keys.transaction(async () => {
|
|
189
|
+
await auth.keys.set({ session: { [addr.toString()]: null } });
|
|
190
|
+
});
|
|
191
|
+
},
|
|
192
|
+
async migrateSession(fromJid, toJid) {
|
|
193
|
+
// Only migrate PN → LID
|
|
194
|
+
if (!fromJid.includes('@s.whatsapp.net') || !toJid.includes('@lid')) {
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
const fromDecoded = (0, WABinary_1.jidDecode)(fromJid);
|
|
198
|
+
const toDecoded = (0, WABinary_1.jidDecode)(toJid);
|
|
199
|
+
if (!fromDecoded || !toDecoded)
|
|
200
|
+
return;
|
|
201
|
+
const deviceId = fromDecoded.device || 0;
|
|
202
|
+
const migrationKey = `${fromDecoded.user}.${deviceId}→${toDecoded.user}.${deviceId}`;
|
|
203
|
+
// Check if recently migrated (5 min window)
|
|
204
|
+
if (recentMigrations.has(migrationKey)) {
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
// Check if LID session already exists
|
|
208
|
+
const lidAddr = jidToSignalProtocolAddress(toJid);
|
|
209
|
+
const { [lidAddr.toString()]: lidExists } = await auth.keys.get('session', [lidAddr.toString()]);
|
|
210
|
+
if (lidExists) {
|
|
211
|
+
recentMigrations.set(migrationKey, true);
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
return auth.keys.transaction(async () => {
|
|
215
|
+
// Store mapping
|
|
216
|
+
await lidMapping.storeLIDPNMapping(toJid, fromJid);
|
|
217
|
+
// Load and copy session
|
|
218
|
+
const fromAddr = jidToSignalProtocolAddress(fromJid);
|
|
219
|
+
const fromSession = await storage.loadSession(fromAddr.toString());
|
|
220
|
+
if (fromSession === null || fromSession === void 0 ? void 0 : fromSession.haveOpenSession()) {
|
|
221
|
+
// Deep copy session to prevent reference issues
|
|
222
|
+
const sessionBytes = fromSession.serialize();
|
|
223
|
+
const copiedSession = libsignal.SessionRecord.deserialize(sessionBytes);
|
|
224
|
+
// Store at LID address
|
|
225
|
+
await storage.storeSession(lidAddr.toString(), copiedSession);
|
|
226
|
+
// Delete PN session - maintain single encryption layer
|
|
227
|
+
await auth.keys.set({ session: { [fromAddr.toString()]: null } });
|
|
228
|
+
}
|
|
229
|
+
recentMigrations.set(migrationKey, true);
|
|
230
|
+
});
|
|
231
|
+
},
|
|
232
|
+
async encryptMessageWithWire({ encryptionJid, wireJid, data }) {
|
|
233
|
+
const result = await repository.encryptMessage({ jid: encryptionJid, data });
|
|
234
|
+
return { ...result, wireJid };
|
|
235
|
+
},
|
|
236
|
+
destroy() {
|
|
237
|
+
recentMigrations.clear();
|
|
130
238
|
}
|
|
131
239
|
};
|
|
240
|
+
return repository;
|
|
132
241
|
}
|
|
133
242
|
const jidToSignalProtocolAddress = (jid) => {
|
|
134
|
-
const
|
|
135
|
-
|
|
243
|
+
const decoded = (0, WABinary_1.jidDecode)(jid);
|
|
244
|
+
const { user, device, server } = decoded;
|
|
245
|
+
// LID addresses get _1 suffix for Signal protocol
|
|
246
|
+
const signalUser = server === 'lid' ? `${user}_1` : user;
|
|
247
|
+
const finalDevice = device || 0;
|
|
248
|
+
return new libsignal.ProtocolAddress(signalUser, finalDevice);
|
|
136
249
|
};
|
|
137
250
|
const jidToSignalSenderKeyName = (group, user) => {
|
|
138
251
|
return new sender_key_name_1.SenderKeyName(group, jidToSignalProtocolAddress(user));
|
|
139
252
|
};
|
|
140
|
-
function signalStorage({ creds, keys }) {
|
|
253
|
+
function signalStorage({ creds, keys }, lidMapping) {
|
|
141
254
|
return {
|
|
142
255
|
loadSession: async (id) => {
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
256
|
+
try {
|
|
257
|
+
// LID SINGLE SOURCE OF TRUTH: Auto-redirect PN to LID if mapping exists
|
|
258
|
+
let actualId = id;
|
|
259
|
+
if (id.includes('.') && !id.includes('_1')) {
|
|
260
|
+
// This is a PN signal address format (e.g., "1234567890.0")
|
|
261
|
+
// Convert back to JID to check for LID mapping
|
|
262
|
+
const parts = id.split('.');
|
|
263
|
+
const device = parts[1] || '0';
|
|
264
|
+
const pnJid = device === '0' ? `${parts[0]}@s.whatsapp.net` : `${parts[0]}:${device}@s.whatsapp.net`;
|
|
265
|
+
const lidForPN = await lidMapping.getLIDForPN(pnJid);
|
|
266
|
+
if (lidForPN === null || lidForPN === void 0 ? void 0 : lidForPN.includes('@lid')) {
|
|
267
|
+
const lidAddr = jidToSignalProtocolAddress(lidForPN);
|
|
268
|
+
const lidId = lidAddr.toString();
|
|
269
|
+
// Check if LID session exists
|
|
270
|
+
const { [lidId]: lidSession } = await keys.get('session', [lidId]);
|
|
271
|
+
if (lidSession) {
|
|
272
|
+
actualId = lidId;
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
const { [actualId]: sess } = await keys.get('session', [actualId]);
|
|
277
|
+
if (sess) {
|
|
278
|
+
return libsignal.SessionRecord.deserialize(sess);
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
catch (e) {
|
|
282
|
+
return null;
|
|
146
283
|
}
|
|
284
|
+
return null;
|
|
147
285
|
},
|
|
148
286
|
// TODO: Replace with libsignal.SessionRecord when type exports are added to libsignal
|
|
149
287
|
storeSession: async (id, session) => {
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { SignalKeyStoreWithTransaction } from '../Types';
|
|
2
|
+
export declare class LIDMappingStore {
|
|
3
|
+
private readonly keys;
|
|
4
|
+
constructor(keys: SignalKeyStoreWithTransaction);
|
|
5
|
+
/**
|
|
6
|
+
* Store LID-PN mapping - USER LEVEL
|
|
7
|
+
*/
|
|
8
|
+
storeLIDPNMapping(lid: string, pn: string): Promise<void>;
|
|
9
|
+
/**
|
|
10
|
+
* Get LID for PN - Returns device-specific LID based on user mapping
|
|
11
|
+
*/
|
|
12
|
+
getLIDForPN(pn: string): Promise<string | null>;
|
|
13
|
+
/**
|
|
14
|
+
* Get PN for LID - USER LEVEL with device construction
|
|
15
|
+
*/
|
|
16
|
+
getPNForLID(lid: string): Promise<string | null>;
|
|
17
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.LIDMappingStore = void 0;
|
|
7
|
+
const logger_1 = __importDefault(require("../Utils/logger"));
|
|
8
|
+
const WABinary_1 = require("../WABinary");
|
|
9
|
+
class LIDMappingStore {
|
|
10
|
+
constructor(keys) {
|
|
11
|
+
this.keys = keys;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Store LID-PN mapping - USER LEVEL
|
|
15
|
+
*/
|
|
16
|
+
async storeLIDPNMapping(lid, pn) {
|
|
17
|
+
// Validate inputs
|
|
18
|
+
if (!(((0, WABinary_1.isLidUser)(lid) && (0, WABinary_1.isJidUser)(pn)) || ((0, WABinary_1.isJidUser)(lid) && (0, WABinary_1.isLidUser)(pn)))) {
|
|
19
|
+
logger_1.default.warn(`Invalid LID-PN mapping: ${lid}, ${pn}`);
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
const [lidJid, pnJid] = (0, WABinary_1.isLidUser)(lid) ? [lid, pn] : [pn, lid];
|
|
23
|
+
const lidDecoded = (0, WABinary_1.jidDecode)(lidJid);
|
|
24
|
+
const pnDecoded = (0, WABinary_1.jidDecode)(pnJid);
|
|
25
|
+
if (!lidDecoded || !pnDecoded)
|
|
26
|
+
return;
|
|
27
|
+
const pnUser = pnDecoded.user;
|
|
28
|
+
const lidUser = lidDecoded.user;
|
|
29
|
+
logger_1.default.trace(`Storing USER LID mapping: PN ${pnUser} → LID ${lidUser}`);
|
|
30
|
+
await this.keys.transaction(async () => {
|
|
31
|
+
await this.keys.set({
|
|
32
|
+
'lid-mapping': {
|
|
33
|
+
[pnUser]: lidUser, // "554396160286" -> "102765716062358"
|
|
34
|
+
[`${lidUser}_reverse`]: pnUser // "102765716062358_reverse" -> "554396160286"
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
logger_1.default.trace(`USER LID mapping stored: PN ${pnUser} → LID ${lidUser}`);
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Get LID for PN - Returns device-specific LID based on user mapping
|
|
42
|
+
*/
|
|
43
|
+
async getLIDForPN(pn) {
|
|
44
|
+
if (!(0, WABinary_1.isJidUser)(pn))
|
|
45
|
+
return null;
|
|
46
|
+
const decoded = (0, WABinary_1.jidDecode)(pn);
|
|
47
|
+
if (!decoded)
|
|
48
|
+
return null;
|
|
49
|
+
// Look up user-level mapping (whatsmeow approach)
|
|
50
|
+
const pnUser = decoded.user;
|
|
51
|
+
const stored = await this.keys.get('lid-mapping', [pnUser]);
|
|
52
|
+
const lidUser = stored[pnUser];
|
|
53
|
+
if (!lidUser) {
|
|
54
|
+
logger_1.default.trace(`No LID mapping found for PN user ${pnUser}`);
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
if (typeof lidUser !== 'string')
|
|
58
|
+
return null;
|
|
59
|
+
// Push the PN device ID to the LID to maintain device separation
|
|
60
|
+
const pnDevice = decoded.device !== undefined ? decoded.device : 0;
|
|
61
|
+
const deviceSpecificLid = `${lidUser}:${pnDevice}@lid`;
|
|
62
|
+
logger_1.default.trace(`getLIDForPN: ${pn} → ${deviceSpecificLid} (user mapping with device ${pnDevice})`);
|
|
63
|
+
return deviceSpecificLid;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Get PN for LID - USER LEVEL with device construction
|
|
67
|
+
*/
|
|
68
|
+
async getPNForLID(lid) {
|
|
69
|
+
if (!(0, WABinary_1.isLidUser)(lid))
|
|
70
|
+
return null;
|
|
71
|
+
const decoded = (0, WABinary_1.jidDecode)(lid);
|
|
72
|
+
if (!decoded)
|
|
73
|
+
return null;
|
|
74
|
+
// Look up reverse user mapping
|
|
75
|
+
const lidUser = decoded.user;
|
|
76
|
+
const stored = await this.keys.get('lid-mapping', [`${lidUser}_reverse`]);
|
|
77
|
+
const pnUser = stored[`${lidUser}_reverse`];
|
|
78
|
+
if (!pnUser || typeof pnUser !== 'string') {
|
|
79
|
+
logger_1.default.trace(`No reverse mapping found for LID user: ${lidUser}`);
|
|
80
|
+
return null;
|
|
81
|
+
}
|
|
82
|
+
// Construct device-specific PN JID
|
|
83
|
+
const lidDevice = decoded.device !== undefined ? decoded.device : 0;
|
|
84
|
+
const pnJid = `${pnUser}:${lidDevice}@s.whatsapp.net`;
|
|
85
|
+
logger_1.default.trace(`Found reverse mapping: ${lid} → ${pnJid}`);
|
|
86
|
+
return pnJid;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
exports.LIDMappingStore = LIDMappingStore;
|
package/lib/Socket/business.d.ts
CHANGED
|
@@ -32,11 +32,13 @@ export declare const makeBusinessSocket: (config: SocketConfig) => {
|
|
|
32
32
|
[_: string]: string;
|
|
33
33
|
}>;
|
|
34
34
|
sendPeerDataOperationMessage: (pdoMessage: import("../Types").WAProto.Message.IPeerDataOperationRequestMessage) => Promise<string>;
|
|
35
|
-
createParticipantNodes: (jids: string[], message: import("../Types").WAProto.IMessage, extraAttrs?: BinaryNode["attrs"]) => Promise<{
|
|
35
|
+
createParticipantNodes: (jids: string[], message: import("../Types").WAProto.IMessage, extraAttrs?: BinaryNode["attrs"], dsmMessage?: import("../Types").WAProto.IMessage) => Promise<{
|
|
36
36
|
nodes: BinaryNode[];
|
|
37
37
|
shouldIncludeDeviceIdentity: boolean;
|
|
38
38
|
}>;
|
|
39
|
-
getUSyncDevices: (jids: string[], useCache: boolean, ignoreZeroDevices: boolean) => Promise<import("../WABinary").JidWithDevice
|
|
39
|
+
getUSyncDevices: (jids: string[], useCache: boolean, ignoreZeroDevices: boolean) => Promise<(import("../WABinary").JidWithDevice & {
|
|
40
|
+
wireJid: string;
|
|
41
|
+
})[]>;
|
|
40
42
|
updateMediaMessage: (message: import("../Types").WAProto.IWebMessageInfo) => Promise<import("../Types").WAProto.IWebMessageInfo>;
|
|
41
43
|
sendMessage: (jid: string, content: import("../Types").AnyMessageContent, options?: import("../Types").MiscMessageGenerationOptions) => Promise<import("../Types").WAProto.WebMessageInfo | undefined>;
|
|
42
44
|
newsletterCreate: (name: string, description?: string) => Promise<import("../Types").NewsletterMetadata>;
|
package/lib/Socket/index.d.ts
CHANGED
|
@@ -31,11 +31,13 @@ declare const makeWASocket: (config: UserFacingSocketConfig) => {
|
|
|
31
31
|
[_: string]: string;
|
|
32
32
|
}>;
|
|
33
33
|
sendPeerDataOperationMessage: (pdoMessage: import("../Types").WAProto.Message.IPeerDataOperationRequestMessage) => Promise<string>;
|
|
34
|
-
createParticipantNodes: (jids: string[], message: import("../Types").WAProto.IMessage, extraAttrs?: import("..").BinaryNode["attrs"]) => Promise<{
|
|
34
|
+
createParticipantNodes: (jids: string[], message: import("../Types").WAProto.IMessage, extraAttrs?: import("..").BinaryNode["attrs"], dsmMessage?: import("../Types").WAProto.IMessage) => Promise<{
|
|
35
35
|
nodes: import("..").BinaryNode[];
|
|
36
36
|
shouldIncludeDeviceIdentity: boolean;
|
|
37
37
|
}>;
|
|
38
|
-
getUSyncDevices: (jids: string[], useCache: boolean, ignoreZeroDevices: boolean) => Promise<import("..").JidWithDevice
|
|
38
|
+
getUSyncDevices: (jids: string[], useCache: boolean, ignoreZeroDevices: boolean) => Promise<(import("..").JidWithDevice & {
|
|
39
|
+
wireJid: string;
|
|
40
|
+
})[]>;
|
|
39
41
|
updateMediaMessage: (message: import("../Types").WAProto.IWebMessageInfo) => Promise<import("../Types").WAProto.IWebMessageInfo>;
|
|
40
42
|
sendMessage: (jid: string, content: import("../Types").AnyMessageContent, options?: import("../Types").MiscMessageGenerationOptions) => Promise<import("../Types").WAProto.WebMessageInfo | undefined>;
|
|
41
43
|
newsletterCreate: (name: string, description?: string) => Promise<import("../Types").NewsletterMetadata>;
|
|
@@ -21,11 +21,13 @@ export declare const makeMessagesRecvSocket: (config: SocketConfig) => {
|
|
|
21
21
|
[_: string]: string;
|
|
22
22
|
}>;
|
|
23
23
|
sendPeerDataOperationMessage: (pdoMessage: proto.Message.IPeerDataOperationRequestMessage) => Promise<string>;
|
|
24
|
-
createParticipantNodes: (jids: string[], message: proto.IMessage, extraAttrs?: BinaryNode["attrs"]) => Promise<{
|
|
24
|
+
createParticipantNodes: (jids: string[], message: proto.IMessage, extraAttrs?: BinaryNode["attrs"], dsmMessage?: proto.IMessage) => Promise<{
|
|
25
25
|
nodes: BinaryNode[];
|
|
26
26
|
shouldIncludeDeviceIdentity: boolean;
|
|
27
27
|
}>;
|
|
28
|
-
getUSyncDevices: (jids: string[], useCache: boolean, ignoreZeroDevices: boolean) => Promise<import("../WABinary").JidWithDevice
|
|
28
|
+
getUSyncDevices: (jids: string[], useCache: boolean, ignoreZeroDevices: boolean) => Promise<(import("../WABinary").JidWithDevice & {
|
|
29
|
+
wireJid: string;
|
|
30
|
+
})[]>;
|
|
29
31
|
updateMediaMessage: (message: proto.IWebMessageInfo) => Promise<proto.IWebMessageInfo>;
|
|
30
32
|
sendMessage: (jid: string, content: import("../Types").AnyMessageContent, options?: import("../Types").MiscMessageGenerationOptions) => Promise<proto.WebMessageInfo | undefined>;
|
|
31
33
|
newsletterCreate: (name: string, description?: string) => Promise<import("../Types").NewsletterMetadata>;
|
|
@@ -661,7 +661,7 @@ const makeMessagesRecvSocket = (config) => {
|
|
|
661
661
|
}
|
|
662
662
|
};
|
|
663
663
|
const handleMessage = async (node) => {
|
|
664
|
-
var _a, _b, _c;
|
|
664
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
665
665
|
if (shouldIgnoreJid(node.attrs.from) && node.attrs.from !== '@s.whatsapp.net') {
|
|
666
666
|
logger.debug({ key: node.attrs.key }, 'ignored message');
|
|
667
667
|
await sendMessageAck(node);
|
|
@@ -697,6 +697,36 @@ const makeMessagesRecvSocket = (config) => {
|
|
|
697
697
|
node.attrs.sender_pn) {
|
|
698
698
|
ev.emit('chats.phoneNumberShare', { lid: node.attrs.from, jid: node.attrs.sender_pn });
|
|
699
699
|
}
|
|
700
|
+
if ((_f = (_e = (_d = msg.message) === null || _d === void 0 ? void 0 : _d.protocolMessage) === null || _e === void 0 ? void 0 : _e.lidMigrationMappingSyncMessage) === null || _f === void 0 ? void 0 : _f.encodedMappingPayload) {
|
|
701
|
+
try {
|
|
702
|
+
const payload = msg.message.protocolMessage.lidMigrationMappingSyncMessage.encodedMappingPayload;
|
|
703
|
+
const decoded = WAProto_1.proto.LIDMigrationMappingSyncPayload.decode(payload);
|
|
704
|
+
logger.debug({
|
|
705
|
+
mappingCount: ((_g = decoded.pnToLidMappings) === null || _g === void 0 ? void 0 : _g.length) || 0,
|
|
706
|
+
timestamp: decoded.chatDbMigrationTimestamp
|
|
707
|
+
}, 'Received LID migration sync message from server');
|
|
708
|
+
const lidMapping = signalRepository.getLIDMappingStore();
|
|
709
|
+
if (decoded.pnToLidMappings && decoded.pnToLidMappings.length > 0) {
|
|
710
|
+
for (const mapping of decoded.pnToLidMappings) {
|
|
711
|
+
const pn = `${mapping.pn}@s.whatsapp.net`;
|
|
712
|
+
// Use latestLid if available, otherwise assignedLid (proper LID refresh)
|
|
713
|
+
const lidValue = mapping.latestLid || mapping.assignedLid;
|
|
714
|
+
const lid = `${lidValue}@lid`;
|
|
715
|
+
await lidMapping.storeLIDPNMapping(lid, pn);
|
|
716
|
+
logger.debug({
|
|
717
|
+
pn,
|
|
718
|
+
lid,
|
|
719
|
+
assignedLid: mapping.assignedLid,
|
|
720
|
+
latestLid: mapping.latestLid,
|
|
721
|
+
usedLatest: !!mapping.latestLid
|
|
722
|
+
}, 'Stored server-provided PN-LID mapping');
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
catch (error) {
|
|
727
|
+
logger.error({ error }, 'Failed to process LID migration sync message');
|
|
728
|
+
}
|
|
729
|
+
}
|
|
700
730
|
try {
|
|
701
731
|
await Promise.all([
|
|
702
732
|
processingMutex.mutex(async () => {
|
|
@@ -16,11 +16,13 @@ export declare const makeMessagesSocket: (config: SocketConfig) => {
|
|
|
16
16
|
[_: string]: string;
|
|
17
17
|
}>;
|
|
18
18
|
sendPeerDataOperationMessage: (pdoMessage: proto.Message.IPeerDataOperationRequestMessage) => Promise<string>;
|
|
19
|
-
createParticipantNodes: (jids: string[], message: proto.IMessage, extraAttrs?: BinaryNode["attrs"]) => Promise<{
|
|
19
|
+
createParticipantNodes: (jids: string[], message: proto.IMessage, extraAttrs?: BinaryNode["attrs"], dsmMessage?: proto.IMessage) => Promise<{
|
|
20
20
|
nodes: BinaryNode[];
|
|
21
21
|
shouldIncludeDeviceIdentity: boolean;
|
|
22
22
|
}>;
|
|
23
|
-
getUSyncDevices: (jids: string[], useCache: boolean, ignoreZeroDevices: boolean) => Promise<JidWithDevice
|
|
23
|
+
getUSyncDevices: (jids: string[], useCache: boolean, ignoreZeroDevices: boolean) => Promise<(JidWithDevice & {
|
|
24
|
+
wireJid: string;
|
|
25
|
+
})[]>;
|
|
24
26
|
updateMediaMessage: (message: proto.IWebMessageInfo) => Promise<proto.IWebMessageInfo>;
|
|
25
27
|
sendMessage: (jid: string, content: AnyMessageContent, options?: MiscMessageGenerationOptions) => Promise<proto.WebMessageInfo | undefined>;
|
|
26
28
|
newsletterCreate: (name: string, description?: string) => Promise<import("../Types").NewsletterMetadata>;
|
|
@@ -10,6 +10,7 @@ const WAProto_1 = require("../../WAProto");
|
|
|
10
10
|
const Defaults_1 = require("../Defaults");
|
|
11
11
|
const Utils_1 = require("../Utils");
|
|
12
12
|
const link_preview_1 = require("../Utils/link-preview");
|
|
13
|
+
const make_mutex_1 = require("../Utils/make-mutex");
|
|
13
14
|
const WABinary_1 = require("../WABinary");
|
|
14
15
|
const WAUSync_1 = require("../WAUSync");
|
|
15
16
|
const groups_1 = require("./groups");
|
|
@@ -23,6 +24,8 @@ const makeMessagesSocket = (config) => {
|
|
|
23
24
|
stdTTL: Defaults_1.DEFAULT_CACHE_TTLS.USER_DEVICES, // 5 minutes
|
|
24
25
|
useClones: false
|
|
25
26
|
});
|
|
27
|
+
// Prevent race conditions in Signal session encryption by user
|
|
28
|
+
const encryptionMutex = (0, make_mutex_1.makeKeyedMutex)();
|
|
26
29
|
let mediaConn;
|
|
27
30
|
const refreshMediaConn = async (forceGet = false) => {
|
|
28
31
|
const media = await mediaConn;
|
|
@@ -111,22 +114,70 @@ const makeMessagesSocket = (config) => {
|
|
|
111
114
|
const readType = privacySettings.readreceipts === 'all' ? 'read' : 'read-self';
|
|
112
115
|
await sendReceipts(keys, readType);
|
|
113
116
|
};
|
|
117
|
+
/**
|
|
118
|
+
* Deduplicate JIDs when both LID and PN versions exist for same user
|
|
119
|
+
* Prefers LID over PN to maintain single encryption layer
|
|
120
|
+
*/
|
|
121
|
+
const deduplicateLidPnJids = (jids) => {
|
|
122
|
+
var _a, _b;
|
|
123
|
+
const lidUsers = new Set();
|
|
124
|
+
const filteredJids = [];
|
|
125
|
+
// Collect all LID users
|
|
126
|
+
for (const jid of jids) {
|
|
127
|
+
if (jid.includes('@lid')) {
|
|
128
|
+
const user = (_a = (0, WABinary_1.jidDecode)(jid)) === null || _a === void 0 ? void 0 : _a.user;
|
|
129
|
+
if (user)
|
|
130
|
+
lidUsers.add(user);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
// Filter out PN versions when LID exists
|
|
134
|
+
for (const jid of jids) {
|
|
135
|
+
if (jid.includes('@s.whatsapp.net')) {
|
|
136
|
+
const user = (_b = (0, WABinary_1.jidDecode)(jid)) === null || _b === void 0 ? void 0 : _b.user;
|
|
137
|
+
if (user && lidUsers.has(user)) {
|
|
138
|
+
logger.debug({ jid }, 'Skipping PN - LID version exists');
|
|
139
|
+
continue;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
filteredJids.push(jid);
|
|
143
|
+
}
|
|
144
|
+
return filteredJids;
|
|
145
|
+
};
|
|
114
146
|
/** Fetch all the devices we've to send a message to */
|
|
115
147
|
const getUSyncDevices = async (jids, useCache, ignoreZeroDevices) => {
|
|
116
|
-
var _a;
|
|
148
|
+
var _a, _b;
|
|
117
149
|
const deviceResults = [];
|
|
118
150
|
if (!useCache) {
|
|
119
151
|
logger.debug('not using cache for devices');
|
|
120
152
|
}
|
|
121
153
|
const toFetch = [];
|
|
122
|
-
|
|
154
|
+
// Deduplicate and normalize JIDs
|
|
155
|
+
jids = deduplicateLidPnJids(Array.from(new Set(jids)));
|
|
123
156
|
for (let jid of jids) {
|
|
124
|
-
const
|
|
157
|
+
const decoded = (0, WABinary_1.jidDecode)(jid);
|
|
158
|
+
const user = decoded === null || decoded === void 0 ? void 0 : decoded.user;
|
|
159
|
+
const device = decoded === null || decoded === void 0 ? void 0 : decoded.device;
|
|
160
|
+
const isExplicitDevice = typeof device === 'number' && device >= 0;
|
|
161
|
+
// Handle explicit device JIDs directly
|
|
162
|
+
if (isExplicitDevice && user) {
|
|
163
|
+
deviceResults.push({
|
|
164
|
+
user,
|
|
165
|
+
device,
|
|
166
|
+
wireJid: jid // Preserve exact JID format for wire protocol
|
|
167
|
+
});
|
|
168
|
+
continue;
|
|
169
|
+
}
|
|
170
|
+
// For user JIDs, normalize and prepare for device enumeration
|
|
125
171
|
jid = (0, WABinary_1.jidNormalizedUser)(jid);
|
|
126
172
|
if (useCache) {
|
|
127
173
|
const devices = userDevicesCache.get(user);
|
|
128
174
|
if (devices) {
|
|
129
|
-
|
|
175
|
+
const isLidJid = jid.includes('@lid');
|
|
176
|
+
const devicesWithWire = devices.map(d => ({
|
|
177
|
+
...d,
|
|
178
|
+
wireJid: isLidJid ? (0, WABinary_1.jidEncode)(d.user, 'lid', d.device) : (0, WABinary_1.jidEncode)(d.user, 's.whatsapp.net', d.device)
|
|
179
|
+
}));
|
|
180
|
+
deviceResults.push(...devicesWithWire);
|
|
130
181
|
logger.trace({ user }, 'using cache for devices');
|
|
131
182
|
}
|
|
132
183
|
else {
|
|
@@ -140,6 +191,14 @@ const makeMessagesSocket = (config) => {
|
|
|
140
191
|
if (!toFetch.length) {
|
|
141
192
|
return deviceResults;
|
|
142
193
|
}
|
|
194
|
+
const requestedLidUsers = new Set();
|
|
195
|
+
for (const jid of toFetch) {
|
|
196
|
+
if (jid.includes('@lid')) {
|
|
197
|
+
const user = (_a = (0, WABinary_1.jidDecode)(jid)) === null || _a === void 0 ? void 0 : _a.user;
|
|
198
|
+
if (user)
|
|
199
|
+
requestedLidUsers.add(user);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
143
202
|
const query = new WAUSync_1.USyncQuery().withContext('message').withDeviceProtocol();
|
|
144
203
|
for (const jid of toFetch) {
|
|
145
204
|
query.withUser(new WAUSync_1.USyncUser().withId(jid));
|
|
@@ -150,8 +209,27 @@ const makeMessagesSocket = (config) => {
|
|
|
150
209
|
const deviceMap = {};
|
|
151
210
|
for (const item of extracted) {
|
|
152
211
|
deviceMap[item.user] = deviceMap[item.user] || [];
|
|
153
|
-
deviceMap[item.user].push(item);
|
|
154
|
-
|
|
212
|
+
(_b = deviceMap[item.user]) === null || _b === void 0 ? void 0 : _b.push(item);
|
|
213
|
+
}
|
|
214
|
+
// Process each user's devices as a group for bulk LID migration
|
|
215
|
+
for (const [user, userDevices] of Object.entries(deviceMap)) {
|
|
216
|
+
const isLidUser = requestedLidUsers.has(user);
|
|
217
|
+
// Process all devices for this user
|
|
218
|
+
for (const item of userDevices) {
|
|
219
|
+
const finalWireJid = isLidUser
|
|
220
|
+
? (0, WABinary_1.jidEncode)(user, 'lid', item.device)
|
|
221
|
+
: (0, WABinary_1.jidEncode)(item.user, 's.whatsapp.net', item.device);
|
|
222
|
+
deviceResults.push({
|
|
223
|
+
...item,
|
|
224
|
+
wireJid: finalWireJid
|
|
225
|
+
});
|
|
226
|
+
logger.debug({
|
|
227
|
+
user: item.user,
|
|
228
|
+
device: item.device,
|
|
229
|
+
finalWireJid,
|
|
230
|
+
usedLid: isLidUser
|
|
231
|
+
}, 'Processed device with LID priority');
|
|
232
|
+
}
|
|
155
233
|
}
|
|
156
234
|
for (const key in deviceMap) {
|
|
157
235
|
userDevicesCache.set(key, deviceMap[key]);
|
|
@@ -159,24 +237,180 @@ const makeMessagesSocket = (config) => {
|
|
|
159
237
|
}
|
|
160
238
|
return deviceResults;
|
|
161
239
|
};
|
|
240
|
+
// Helper to check if JID has migrated LID session
|
|
241
|
+
const checkForMigratedLidSession = async (jid) => {
|
|
242
|
+
if (!jid.includes('@s.whatsapp.net'))
|
|
243
|
+
return false;
|
|
244
|
+
const lidMapping = signalRepository.getLIDMappingStore();
|
|
245
|
+
const lidForPN = await lidMapping.getLIDForPN(jid);
|
|
246
|
+
if (!(lidForPN === null || lidForPN === void 0 ? void 0 : lidForPN.includes('@lid')))
|
|
247
|
+
return false;
|
|
248
|
+
const lidSignalId = signalRepository.jidToSignalProtocolAddress(lidForPN);
|
|
249
|
+
const lidSessions = await authState.keys.get('session', [lidSignalId]);
|
|
250
|
+
return !!lidSessions[lidSignalId];
|
|
251
|
+
};
|
|
162
252
|
const assertSessions = async (jids, force) => {
|
|
253
|
+
var _a;
|
|
163
254
|
let didFetchNewSession = false;
|
|
164
|
-
|
|
255
|
+
const jidsRequiringFetch = [];
|
|
256
|
+
// Apply same deduplication as in getUSyncDevices
|
|
257
|
+
jids = deduplicateLidPnJids(jids);
|
|
165
258
|
if (force) {
|
|
166
|
-
|
|
259
|
+
// Check which sessions are missing (with LID migration check)
|
|
260
|
+
const addrs = jids.map(jid => signalRepository.jidToSignalProtocolAddress(jid));
|
|
261
|
+
const sessions = await authState.keys.get('session', addrs);
|
|
262
|
+
// Helper to check session for a JID
|
|
263
|
+
const checkJidSession = async (jid) => {
|
|
264
|
+
const signalId = signalRepository.jidToSignalProtocolAddress(jid);
|
|
265
|
+
let hasSession = !!sessions[signalId];
|
|
266
|
+
// Check for migrated LID session if PN session missing
|
|
267
|
+
if (!hasSession) {
|
|
268
|
+
hasSession = await checkForMigratedLidSession(jid);
|
|
269
|
+
if (hasSession) {
|
|
270
|
+
logger.debug({ jid }, 'Found migrated LID session during force assert, skipping PN fetch');
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
// Add to fetch list if no session exists
|
|
274
|
+
if (!hasSession) {
|
|
275
|
+
if (jid.includes('@lid')) {
|
|
276
|
+
logger.debug({ jid }, 'No LID session found, will create new LID session');
|
|
277
|
+
}
|
|
278
|
+
jidsRequiringFetch.push(jid);
|
|
279
|
+
}
|
|
280
|
+
};
|
|
281
|
+
// Process all JIDs
|
|
282
|
+
for (const jid of jids) {
|
|
283
|
+
await checkJidSession(jid);
|
|
284
|
+
}
|
|
167
285
|
}
|
|
168
286
|
else {
|
|
287
|
+
const lidMapping = signalRepository.getLIDMappingStore();
|
|
169
288
|
const addrs = jids.map(jid => signalRepository.jidToSignalProtocolAddress(jid));
|
|
170
289
|
const sessions = await authState.keys.get('session', addrs);
|
|
290
|
+
// Group JIDs by user for bulk migration
|
|
291
|
+
const userGroups = new Map();
|
|
171
292
|
for (const jid of jids) {
|
|
172
|
-
const
|
|
173
|
-
if (!
|
|
174
|
-
|
|
293
|
+
const user = (0, WABinary_1.jidNormalizedUser)(jid);
|
|
294
|
+
if (!userGroups.has(user)) {
|
|
295
|
+
userGroups.set(user, []);
|
|
296
|
+
}
|
|
297
|
+
userGroups.get(user).push(jid);
|
|
298
|
+
}
|
|
299
|
+
// Helper to check LID mapping for a user
|
|
300
|
+
const checkUserLidMapping = async (user, userJids) => {
|
|
301
|
+
if (!userJids.some(jid => jid.includes('@s.whatsapp.net'))) {
|
|
302
|
+
return { shouldMigrate: false, lidForPN: undefined };
|
|
303
|
+
}
|
|
304
|
+
try {
|
|
305
|
+
const mapping = await lidMapping.getLIDForPN(user);
|
|
306
|
+
if (mapping === null || mapping === void 0 ? void 0 : mapping.includes('@lid')) {
|
|
307
|
+
logger.debug({ user, lidForPN: mapping, deviceCount: userJids.length }, 'User has LID mapping - preparing bulk migration');
|
|
308
|
+
return { shouldMigrate: true, lidForPN: mapping };
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
catch (error) {
|
|
312
|
+
logger.debug({ user, error }, 'Failed to check LID mapping for user');
|
|
313
|
+
}
|
|
314
|
+
return { shouldMigrate: false, lidForPN: undefined };
|
|
315
|
+
};
|
|
316
|
+
// Helper to migrate a single device
|
|
317
|
+
const migrateDeviceToLid = async (jid, lidForPN) => {
|
|
318
|
+
if (!jid.includes('@s.whatsapp.net'))
|
|
319
|
+
return;
|
|
320
|
+
try {
|
|
321
|
+
const jidDecoded = (0, WABinary_1.jidDecode)(jid);
|
|
322
|
+
const deviceId = (jidDecoded === null || jidDecoded === void 0 ? void 0 : jidDecoded.device) || 0;
|
|
323
|
+
const lidDecoded = (0, WABinary_1.jidDecode)(lidForPN);
|
|
324
|
+
const lidWithDevice = (0, WABinary_1.jidEncode)(lidDecoded === null || lidDecoded === void 0 ? void 0 : lidDecoded.user, 'lid', deviceId);
|
|
325
|
+
await signalRepository.migrateSession(jid, lidWithDevice);
|
|
326
|
+
logger.debug({ fromJid: jid, toJid: lidWithDevice }, 'Migrated device session to LID');
|
|
327
|
+
// Delete PN session after successful migration
|
|
328
|
+
try {
|
|
329
|
+
await signalRepository.deleteSession(jid);
|
|
330
|
+
logger.debug({ deletedPNSession: jid }, 'Deleted PN session after migration');
|
|
331
|
+
}
|
|
332
|
+
catch (deleteError) {
|
|
333
|
+
logger.warn({ jid, error: deleteError }, 'Failed to delete PN session');
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
catch (migrationError) {
|
|
337
|
+
logger.warn({ jid, error: migrationError }, 'Failed to migrate device session');
|
|
338
|
+
}
|
|
339
|
+
};
|
|
340
|
+
// Process each user group for potential bulk LID migration
|
|
341
|
+
for (const [user, userJids] of userGroups) {
|
|
342
|
+
const mappingResult = await checkUserLidMapping(user, userJids);
|
|
343
|
+
const shouldMigrateUser = mappingResult.shouldMigrate;
|
|
344
|
+
const lidForPN = mappingResult.lidForPN;
|
|
345
|
+
// Migrate all devices for this user if LID mapping exists
|
|
346
|
+
if (shouldMigrateUser && lidForPN) {
|
|
347
|
+
// Migrate each device individually
|
|
348
|
+
for (const jid of userJids) {
|
|
349
|
+
await migrateDeviceToLid(jid, lidForPN);
|
|
350
|
+
}
|
|
351
|
+
logger.info({
|
|
352
|
+
user,
|
|
353
|
+
lidMapping: lidForPN,
|
|
354
|
+
deviceCount: userJids.length
|
|
355
|
+
}, 'Completed migration attempt for user devices');
|
|
356
|
+
}
|
|
357
|
+
// Helper to check session for migrated user
|
|
358
|
+
const checkMigratedSession = async (jid) => {
|
|
359
|
+
const signalId = signalRepository.jidToSignalProtocolAddress(jid);
|
|
360
|
+
let hasSession = !!sessions[signalId];
|
|
361
|
+
let jidToFetch = jid;
|
|
362
|
+
// Check if we should use migrated LID session instead
|
|
363
|
+
if (shouldMigrateUser && lidForPN && jid.includes('@s.whatsapp.net')) {
|
|
364
|
+
const originalDecoded = (0, WABinary_1.jidDecode)(jid);
|
|
365
|
+
const deviceId = (originalDecoded === null || originalDecoded === void 0 ? void 0 : originalDecoded.device) || 0;
|
|
366
|
+
const lidDecoded = (0, WABinary_1.jidDecode)(lidForPN);
|
|
367
|
+
const lidWithDevice = (0, WABinary_1.jidEncode)(lidDecoded === null || lidDecoded === void 0 ? void 0 : lidDecoded.user, 'lid', deviceId);
|
|
368
|
+
// Check if LID session exists
|
|
369
|
+
const lidSignalId = signalRepository.jidToSignalProtocolAddress(lidWithDevice);
|
|
370
|
+
const lidSessions = await authState.keys.get('session', [lidSignalId]);
|
|
371
|
+
hasSession = !!lidSessions[lidSignalId];
|
|
372
|
+
jidToFetch = lidWithDevice;
|
|
373
|
+
if (hasSession) {
|
|
374
|
+
logger.debug({ originalJid: jid, lidJid: lidWithDevice }, '✅ Found bulk-migrated LID session');
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
// Add to fetch list if no session exists
|
|
378
|
+
if (!hasSession) {
|
|
379
|
+
jidsRequiringFetch.push(jidToFetch);
|
|
380
|
+
logger.debug({ jid: jidToFetch, originalJid: jid !== jidToFetch ? jid : undefined }, 'Adding to session fetch list');
|
|
381
|
+
}
|
|
382
|
+
};
|
|
383
|
+
// Now check which sessions need to be fetched for this user
|
|
384
|
+
for (const jid of userJids) {
|
|
385
|
+
await checkMigratedSession(jid);
|
|
175
386
|
}
|
|
176
387
|
}
|
|
177
388
|
}
|
|
178
389
|
if (jidsRequiringFetch.length) {
|
|
179
390
|
logger.debug({ jidsRequiringFetch }, 'fetching sessions');
|
|
391
|
+
// DEBUG: Check if there are PN versions of LID users being fetched
|
|
392
|
+
const lidUsersBeingFetched = new Set();
|
|
393
|
+
const pnUsersBeingFetched = new Set();
|
|
394
|
+
for (const jid of jidsRequiringFetch) {
|
|
395
|
+
const user = (_a = (0, WABinary_1.jidDecode)(jid)) === null || _a === void 0 ? void 0 : _a.user;
|
|
396
|
+
if (user) {
|
|
397
|
+
if (jid.includes('@lid')) {
|
|
398
|
+
lidUsersBeingFetched.add(user);
|
|
399
|
+
}
|
|
400
|
+
else if (jid.includes('@s.whatsapp.net')) {
|
|
401
|
+
pnUsersBeingFetched.add(user);
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
// Find overlaps
|
|
406
|
+
const overlapping = Array.from(pnUsersBeingFetched).filter(user => lidUsersBeingFetched.has(user));
|
|
407
|
+
if (overlapping.length > 0) {
|
|
408
|
+
logger.warn({
|
|
409
|
+
overlapping,
|
|
410
|
+
lidUsersBeingFetched: Array.from(lidUsersBeingFetched),
|
|
411
|
+
pnUsersBeingFetched: Array.from(pnUsersBeingFetched)
|
|
412
|
+
}, 'Fetching both LID and PN sessions for same users');
|
|
413
|
+
}
|
|
180
414
|
const result = await query({
|
|
181
415
|
tag: 'iq',
|
|
182
416
|
attrs: {
|
|
@@ -222,44 +456,135 @@ const makeMessagesSocket = (config) => {
|
|
|
222
456
|
});
|
|
223
457
|
return msgId;
|
|
224
458
|
};
|
|
225
|
-
const createParticipantNodes = async (jids, message, extraAttrs) => {
|
|
459
|
+
const createParticipantNodes = async (jids, message, extraAttrs, dsmMessage) => {
|
|
460
|
+
var _a, _b;
|
|
226
461
|
let patched = await patchMessageBeforeSending(message, jids);
|
|
227
462
|
if (!Array.isArray(patched)) {
|
|
228
463
|
patched = jids ? jids.map(jid => ({ recipientJid: jid, ...patched })) : [patched];
|
|
229
464
|
}
|
|
230
465
|
let shouldIncludeDeviceIdentity = false;
|
|
231
|
-
const
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
const
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
const
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
466
|
+
const meId = authState.creds.me.id;
|
|
467
|
+
const meLid = (_a = authState.creds.me) === null || _a === void 0 ? void 0 : _a.lid;
|
|
468
|
+
const meLidUser = meLid ? (_b = (0, WABinary_1.jidDecode)(meLid)) === null || _b === void 0 ? void 0 : _b.user : null;
|
|
469
|
+
const devicesByUser = new Map();
|
|
470
|
+
for (const patchedMessageWithJid of patched) {
|
|
471
|
+
const { recipientJid: wireJid, ...patchedMessage } = patchedMessageWithJid;
|
|
472
|
+
if (!wireJid)
|
|
473
|
+
continue;
|
|
474
|
+
// Extract user from JID for grouping
|
|
475
|
+
const decoded = (0, WABinary_1.jidDecode)(wireJid);
|
|
476
|
+
const user = decoded === null || decoded === void 0 ? void 0 : decoded.user;
|
|
477
|
+
if (!user)
|
|
478
|
+
continue;
|
|
479
|
+
if (!devicesByUser.has(user)) {
|
|
480
|
+
devicesByUser.set(user, []);
|
|
481
|
+
}
|
|
482
|
+
devicesByUser.get(user).push({ recipientJid: wireJid, patchedMessage });
|
|
483
|
+
}
|
|
484
|
+
// Process each user's devices sequentially, but different users in parallel
|
|
485
|
+
const userEncryptionPromises = Array.from(devicesByUser.entries()).map(([user, userDevices]) => encryptionMutex.mutex(user, async () => {
|
|
486
|
+
var _a;
|
|
487
|
+
logger.debug({ user, deviceCount: userDevices.length }, 'Acquiring encryption lock for user devices');
|
|
488
|
+
const userNodes = [];
|
|
489
|
+
// Helper to get encryption JID with LID migration
|
|
490
|
+
const getEncryptionJid = async (wireJid) => {
|
|
491
|
+
if (!wireJid.includes('@s.whatsapp.net'))
|
|
492
|
+
return wireJid;
|
|
493
|
+
try {
|
|
494
|
+
const lidMapping = signalRepository.getLIDMappingStore();
|
|
495
|
+
const lidForPN = await lidMapping.getLIDForPN(wireJid);
|
|
496
|
+
if (!(lidForPN === null || lidForPN === void 0 ? void 0 : lidForPN.includes('@lid')))
|
|
497
|
+
return wireJid;
|
|
498
|
+
// Preserve device ID from original wire JID
|
|
499
|
+
const wireDecoded = (0, WABinary_1.jidDecode)(wireJid);
|
|
500
|
+
const deviceId = (wireDecoded === null || wireDecoded === void 0 ? void 0 : wireDecoded.device) || 0;
|
|
501
|
+
const lidDecoded = (0, WABinary_1.jidDecode)(lidForPN);
|
|
502
|
+
const lidWithDevice = (0, WABinary_1.jidEncode)(lidDecoded === null || lidDecoded === void 0 ? void 0 : lidDecoded.user, 'lid', deviceId);
|
|
503
|
+
// Migrate session to LID for unified encryption layer
|
|
504
|
+
try {
|
|
505
|
+
await signalRepository.migrateSession(wireJid, lidWithDevice);
|
|
506
|
+
const recipientUser = (0, WABinary_1.jidNormalizedUser)(wireJid);
|
|
507
|
+
const ownPnUser = (0, WABinary_1.jidNormalizedUser)(meId);
|
|
508
|
+
const isOwnDevice = recipientUser === ownPnUser;
|
|
509
|
+
logger.info({ wireJid, lidWithDevice, isOwnDevice }, 'Migrated to LID encryption');
|
|
510
|
+
// Delete PN session after successful migration
|
|
511
|
+
try {
|
|
512
|
+
await signalRepository.deleteSession(wireJid);
|
|
513
|
+
logger.debug({ deletedPNSession: wireJid }, 'Deleted PN session');
|
|
514
|
+
}
|
|
515
|
+
catch (deleteError) {
|
|
516
|
+
logger.warn({ wireJid, error: deleteError }, 'Failed to delete PN session');
|
|
517
|
+
}
|
|
518
|
+
return lidWithDevice;
|
|
253
519
|
}
|
|
254
|
-
|
|
520
|
+
catch (migrationError) {
|
|
521
|
+
logger.warn({ wireJid, error: migrationError }, 'Failed to migrate session');
|
|
522
|
+
return wireJid;
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
catch (error) {
|
|
526
|
+
logger.debug({ wireJid, error }, 'Failed to check LID mapping');
|
|
527
|
+
return wireJid;
|
|
528
|
+
}
|
|
255
529
|
};
|
|
256
|
-
|
|
530
|
+
// Encrypt to this user's devices sequentially to prevent session corruption
|
|
531
|
+
for (const { recipientJid: wireJid, patchedMessage } of userDevices) {
|
|
532
|
+
// DSM logic: Use DSM for own other devices (following whatsmeow implementation)
|
|
533
|
+
let messageToEncrypt = patchedMessage;
|
|
534
|
+
if (dsmMessage) {
|
|
535
|
+
const { user: targetUser } = (0, WABinary_1.jidDecode)(wireJid);
|
|
536
|
+
const { user: ownPnUser } = (0, WABinary_1.jidDecode)(meId);
|
|
537
|
+
const ownLidUser = meLidUser;
|
|
538
|
+
// Check if this is our device (same user, different device)
|
|
539
|
+
const isOwnUser = targetUser === ownPnUser || (ownLidUser && targetUser === ownLidUser);
|
|
540
|
+
// Exclude exact sender device (whatsmeow: if jid == ownJID || jid == ownLID { continue })
|
|
541
|
+
const isExactSenderDevice = wireJid === meId || (((_a = authState.creds.me) === null || _a === void 0 ? void 0 : _a.lid) && wireJid === authState.creds.me.lid);
|
|
542
|
+
if (isOwnUser && !isExactSenderDevice) {
|
|
543
|
+
messageToEncrypt = dsmMessage;
|
|
544
|
+
logger.debug({ wireJid, targetUser }, 'Using DSM for own device');
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
const bytes = (0, Utils_1.encodeWAMessage)(messageToEncrypt);
|
|
548
|
+
// Get encryption JID with LID migration
|
|
549
|
+
const encryptionJid = await getEncryptionJid(wireJid);
|
|
550
|
+
// ENCRYPT: Use the determined encryption identity (prefers migrated LID)
|
|
551
|
+
const { type, ciphertext } = await signalRepository.encryptMessage({
|
|
552
|
+
jid: encryptionJid, // Unified encryption layer (LID when available)
|
|
553
|
+
data: bytes
|
|
554
|
+
});
|
|
555
|
+
if (type === 'pkmsg') {
|
|
556
|
+
shouldIncludeDeviceIdentity = true;
|
|
557
|
+
}
|
|
558
|
+
const node = {
|
|
559
|
+
tag: 'to',
|
|
560
|
+
attrs: { jid: wireJid }, // Always use original wire identity in envelope
|
|
561
|
+
content: [
|
|
562
|
+
{
|
|
563
|
+
tag: 'enc',
|
|
564
|
+
attrs: {
|
|
565
|
+
v: '2',
|
|
566
|
+
type,
|
|
567
|
+
...(extraAttrs || {})
|
|
568
|
+
},
|
|
569
|
+
content: ciphertext
|
|
570
|
+
}
|
|
571
|
+
]
|
|
572
|
+
};
|
|
573
|
+
userNodes.push(node);
|
|
574
|
+
}
|
|
575
|
+
logger.debug({ user, nodesCreated: userNodes.length }, 'Releasing encryption lock for user devices');
|
|
576
|
+
return userNodes;
|
|
257
577
|
}));
|
|
578
|
+
// Wait for all users to complete (users are processed in parallel)
|
|
579
|
+
const userNodesArrays = await Promise.all(userEncryptionPromises);
|
|
580
|
+
const nodes = userNodesArrays.flat();
|
|
258
581
|
return { nodes, shouldIncludeDeviceIdentity };
|
|
259
582
|
};
|
|
260
583
|
const relayMessage = async (jid, message, { messageId: msgId, participant, additionalAttributes, additionalNodes, useUserDevicesCache, useCachedGroupMetadata, statusJidList }) => {
|
|
261
|
-
var _a;
|
|
584
|
+
var _a, _b;
|
|
262
585
|
const meId = authState.creds.me.id;
|
|
586
|
+
const meLid = (_a = authState.creds.me) === null || _a === void 0 ? void 0 : _a.lid;
|
|
587
|
+
// ADDRESSING CONSISTENCY: Keep envelope addressing as user provided, handle LID migration in encryption
|
|
263
588
|
let shouldIncludeDeviceIdentity = false;
|
|
264
589
|
const { user, server } = (0, WABinary_1.jidDecode)(jid);
|
|
265
590
|
const statusJid = 'status@broadcast';
|
|
@@ -267,11 +592,22 @@ const makeMessagesSocket = (config) => {
|
|
|
267
592
|
const isStatus = jid === statusJid;
|
|
268
593
|
const isLid = server === 'lid';
|
|
269
594
|
const isNewsletter = server === 'newsletter';
|
|
270
|
-
|
|
595
|
+
// Keep user's original JID choice for envelope addressing
|
|
596
|
+
const finalJid = jid;
|
|
597
|
+
// ADDRESSING CONSISTENCY: Match own identity to conversation context
|
|
598
|
+
let ownId = meId;
|
|
599
|
+
if (isLid && meLid) {
|
|
600
|
+
ownId = meLid;
|
|
601
|
+
logger.debug({ to: jid, ownId }, 'Using LID identity for @lid conversation');
|
|
602
|
+
}
|
|
603
|
+
else {
|
|
604
|
+
logger.debug({ to: jid, ownId }, 'Using PN identity for @s.whatsapp.net conversation');
|
|
605
|
+
}
|
|
606
|
+
msgId = msgId || (0, Utils_1.generateMessageIDV2)((_b = sock.user) === null || _b === void 0 ? void 0 : _b.id);
|
|
271
607
|
useUserDevicesCache = useUserDevicesCache !== false;
|
|
272
608
|
useCachedGroupMetadata = useCachedGroupMetadata !== false && !isStatus;
|
|
273
609
|
const participants = [];
|
|
274
|
-
const destinationJid = !isStatus ?
|
|
610
|
+
const destinationJid = !isStatus ? finalJid : statusJid;
|
|
275
611
|
const binaryNodeContent = [];
|
|
276
612
|
const devices = [];
|
|
277
613
|
const meMsg = {
|
|
@@ -290,7 +626,11 @@ const makeMessagesSocket = (config) => {
|
|
|
290
626
|
additionalAttributes = { ...additionalAttributes, device_fanout: 'false' };
|
|
291
627
|
}
|
|
292
628
|
const { user, device } = (0, WABinary_1.jidDecode)(participant.jid);
|
|
293
|
-
devices.push({
|
|
629
|
+
devices.push({
|
|
630
|
+
user,
|
|
631
|
+
device,
|
|
632
|
+
wireJid: participant.jid // Use the participant JID as wire JID
|
|
633
|
+
});
|
|
294
634
|
}
|
|
295
635
|
await authState.keys.transaction(async () => {
|
|
296
636
|
var _a, _b, _c, _d, _e;
|
|
@@ -350,9 +690,10 @@ const makeMessagesSocket = (config) => {
|
|
|
350
690
|
participantsList.push(...statusJidList);
|
|
351
691
|
}
|
|
352
692
|
if (!isStatus) {
|
|
693
|
+
const groupAddressingMode = (groupData === null || groupData === void 0 ? void 0 : groupData.addressingMode) || (isLid ? 'lid' : 'pn');
|
|
353
694
|
additionalAttributes = {
|
|
354
695
|
...additionalAttributes,
|
|
355
|
-
addressing_mode:
|
|
696
|
+
addressing_mode: groupAddressingMode
|
|
356
697
|
};
|
|
357
698
|
}
|
|
358
699
|
const additionalDevices = await getUSyncDevices(participantsList, !!useUserDevicesCache, false);
|
|
@@ -363,19 +704,24 @@ const makeMessagesSocket = (config) => {
|
|
|
363
704
|
throw new boom_1.Boom('Per-jid patching is not supported in groups');
|
|
364
705
|
}
|
|
365
706
|
const bytes = (0, Utils_1.encodeWAMessage)(patched);
|
|
707
|
+
// This should match the group's addressing mode and conversation context
|
|
708
|
+
const groupAddressingMode = (groupData === null || groupData === void 0 ? void 0 : groupData.addressingMode) || (isLid ? 'lid' : 'pn');
|
|
709
|
+
const groupSenderIdentity = groupAddressingMode === 'lid' && meLid ? meLid : meId;
|
|
366
710
|
const { ciphertext, senderKeyDistributionMessage } = await signalRepository.encryptGroupMessage({
|
|
367
711
|
group: destinationJid,
|
|
368
712
|
data: bytes,
|
|
369
|
-
meId
|
|
713
|
+
meId: groupSenderIdentity
|
|
370
714
|
});
|
|
371
715
|
const senderKeyJids = [];
|
|
372
716
|
// ensure a connection is established with every device
|
|
373
|
-
for (const
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
717
|
+
for (const device of devices) {
|
|
718
|
+
// This preserves the LID migration results from getUSyncDevices
|
|
719
|
+
const deviceJid = device.wireJid;
|
|
720
|
+
const hasKey = !!senderKeyMap[deviceJid];
|
|
721
|
+
if (!hasKey || !!participant) {
|
|
722
|
+
senderKeyJids.push(deviceJid);
|
|
377
723
|
// store that this person has had the sender keys sent to them
|
|
378
|
-
senderKeyMap[
|
|
724
|
+
senderKeyMap[deviceJid] = true;
|
|
379
725
|
}
|
|
380
726
|
}
|
|
381
727
|
// if there are some participants with whom the session has not been established
|
|
@@ -401,23 +747,54 @@ const makeMessagesSocket = (config) => {
|
|
|
401
747
|
await authState.keys.set({ 'sender-key-memory': { [jid]: senderKeyMap } });
|
|
402
748
|
}
|
|
403
749
|
else {
|
|
404
|
-
const { user:
|
|
750
|
+
const { user: ownUser } = (0, WABinary_1.jidDecode)(ownId);
|
|
405
751
|
if (!participant) {
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
752
|
+
const targetUserServer = isLid ? 'lid' : 's.whatsapp.net';
|
|
753
|
+
devices.push({
|
|
754
|
+
user,
|
|
755
|
+
device: 0,
|
|
756
|
+
wireJid: (0, WABinary_1.jidEncode)(user, targetUserServer, 0)
|
|
757
|
+
});
|
|
758
|
+
// Own user matches conversation addressing mode
|
|
759
|
+
if (user !== ownUser) {
|
|
760
|
+
const ownUserServer = isLid ? 'lid' : 's.whatsapp.net';
|
|
761
|
+
const ownUserForAddressing = isLid && meLid ? (0, WABinary_1.jidDecode)(meLid).user : (0, WABinary_1.jidDecode)(meId).user;
|
|
762
|
+
devices.push({
|
|
763
|
+
user: ownUserForAddressing,
|
|
764
|
+
device: 0,
|
|
765
|
+
wireJid: (0, WABinary_1.jidEncode)(ownUserForAddressing, ownUserServer, 0)
|
|
766
|
+
});
|
|
409
767
|
}
|
|
410
768
|
if ((additionalAttributes === null || additionalAttributes === void 0 ? void 0 : additionalAttributes['category']) !== 'peer') {
|
|
411
|
-
|
|
412
|
-
devices.
|
|
769
|
+
// Clear placeholders and enumerate actual devices
|
|
770
|
+
devices.length = 0;
|
|
771
|
+
// Use conversation-appropriate sender identity
|
|
772
|
+
const senderIdentity = isLid && meLid
|
|
773
|
+
? (0, WABinary_1.jidEncode)((_b = (0, WABinary_1.jidDecode)(meLid)) === null || _b === void 0 ? void 0 : _b.user, 'lid', undefined)
|
|
774
|
+
: (0, WABinary_1.jidEncode)((_c = (0, WABinary_1.jidDecode)(meId)) === null || _c === void 0 ? void 0 : _c.user, 's.whatsapp.net', undefined);
|
|
775
|
+
// Enumerate devices for sender and target with consistent addressing
|
|
776
|
+
const sessionDevices = await getUSyncDevices([senderIdentity, jid], false, false);
|
|
777
|
+
devices.push(...sessionDevices);
|
|
778
|
+
logger.debug({
|
|
779
|
+
deviceCount: devices.length,
|
|
780
|
+
devices: devices.map(d => { var _a; return `${d.user}:${d.device}@${(_a = (0, WABinary_1.jidDecode)(d.wireJid)) === null || _a === void 0 ? void 0 : _a.server}`; })
|
|
781
|
+
}, 'Device enumeration complete with unified addressing');
|
|
413
782
|
}
|
|
414
783
|
}
|
|
415
784
|
const allJids = [];
|
|
416
785
|
const meJids = [];
|
|
417
786
|
const otherJids = [];
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
787
|
+
const { user: mePnUser } = (0, WABinary_1.jidDecode)(meId);
|
|
788
|
+
const { user: meLidUser } = meLid ? (0, WABinary_1.jidDecode)(meLid) : { user: null };
|
|
789
|
+
for (const { user, wireJid } of devices) {
|
|
790
|
+
const isExactSenderDevice = wireJid === meId || (meLid && wireJid === meLid);
|
|
791
|
+
if (isExactSenderDevice) {
|
|
792
|
+
logger.debug({ wireJid, meId, meLid }, 'Skipping exact sender device (whatsmeow pattern)');
|
|
793
|
+
continue;
|
|
794
|
+
}
|
|
795
|
+
// Check if this is our device (could match either PN or LID user)
|
|
796
|
+
const isMe = user === mePnUser || (meLidUser && user === meLidUser);
|
|
797
|
+
const jid = wireJid;
|
|
421
798
|
if (isMe) {
|
|
422
799
|
meJids.push(jid);
|
|
423
800
|
}
|
|
@@ -426,10 +803,11 @@ const makeMessagesSocket = (config) => {
|
|
|
426
803
|
}
|
|
427
804
|
allJids.push(jid);
|
|
428
805
|
}
|
|
429
|
-
await assertSessions(
|
|
806
|
+
await assertSessions([...otherJids, ...meJids], false);
|
|
430
807
|
const [{ nodes: meNodes, shouldIncludeDeviceIdentity: s1 }, { nodes: otherNodes, shouldIncludeDeviceIdentity: s2 }] = await Promise.all([
|
|
431
|
-
|
|
432
|
-
createParticipantNodes(
|
|
808
|
+
// For own devices: use DSM if available (1:1 chats only)
|
|
809
|
+
createParticipantNodes(meJids, meMsg || message, extraAttrs),
|
|
810
|
+
createParticipantNodes(otherJids, message, extraAttrs, meMsg)
|
|
433
811
|
]);
|
|
434
812
|
participants.push(...meNodes);
|
|
435
813
|
participants.push(...otherNodes);
|
|
@@ -454,6 +832,7 @@ const makeMessagesSocket = (config) => {
|
|
|
454
832
|
tag: 'message',
|
|
455
833
|
attrs: {
|
|
456
834
|
id: msgId,
|
|
835
|
+
to: destinationJid,
|
|
457
836
|
type: getMessageType(message),
|
|
458
837
|
...(additionalAttributes || {})
|
|
459
838
|
},
|
package/lib/Socket/socket.js
CHANGED
|
@@ -504,12 +504,29 @@ const makeSocket = (config) => {
|
|
|
504
504
|
});
|
|
505
505
|
// login complete
|
|
506
506
|
ws.on('CB:success', async (node) => {
|
|
507
|
+
var _a;
|
|
507
508
|
await uploadPreKeysToServerIfRequired();
|
|
508
509
|
await sendPassiveIq('active');
|
|
509
510
|
logger.info('opened connection to WA');
|
|
510
511
|
clearTimeout(qrTimer); // will never happen in all likelyhood -- but just in case WA sends success on first try
|
|
511
512
|
ev.emit('creds.update', { me: { ...authState.creds.me, lid: node.attrs.lid } });
|
|
512
513
|
ev.emit('connection.update', { connection: 'open' });
|
|
514
|
+
if (node.attrs.lid && ((_a = authState.creds.me) === null || _a === void 0 ? void 0 : _a.id)) {
|
|
515
|
+
const myLID = node.attrs.lid;
|
|
516
|
+
process.nextTick(async () => {
|
|
517
|
+
try {
|
|
518
|
+
const myPN = authState.creds.me.id;
|
|
519
|
+
// Store our own LID-PN mapping
|
|
520
|
+
await signalRepository.storeLIDPNMapping(myLID, myPN);
|
|
521
|
+
// Create LID session for ourselves (whatsmeow pattern)
|
|
522
|
+
await signalRepository.migrateSession(myPN, myLID);
|
|
523
|
+
logger.info({ myPN, myLID }, 'Own LID session created successfully');
|
|
524
|
+
}
|
|
525
|
+
catch (error) {
|
|
526
|
+
logger.error({ error, lid: myLID }, 'Failed to create own LID session');
|
|
527
|
+
}
|
|
528
|
+
});
|
|
529
|
+
}
|
|
513
530
|
});
|
|
514
531
|
ws.on('CB:stream:error', (node) => {
|
|
515
532
|
logger.error({ node }, 'stream errored out');
|
package/lib/Types/Auth.d.ts
CHANGED
package/lib/Types/Signal.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { proto } from '../../WAProto';
|
|
2
|
+
import type { LIDMappingStore } from '../Signal/lid-mapping';
|
|
2
3
|
type DecryptGroupSignalOpts = {
|
|
3
4
|
group: string;
|
|
4
5
|
authorJid: string;
|
|
@@ -17,6 +18,11 @@ type EncryptMessageOpts = {
|
|
|
17
18
|
jid: string;
|
|
18
19
|
data: Uint8Array;
|
|
19
20
|
};
|
|
21
|
+
type EncryptMessageWithWireOpts = {
|
|
22
|
+
encryptionJid: string;
|
|
23
|
+
wireJid: string;
|
|
24
|
+
data: Uint8Array;
|
|
25
|
+
};
|
|
20
26
|
type EncryptGroupMessageOpts = {
|
|
21
27
|
group: string;
|
|
22
28
|
data: Uint8Array;
|
|
@@ -47,11 +53,25 @@ export type SignalRepository = {
|
|
|
47
53
|
type: 'pkmsg' | 'msg';
|
|
48
54
|
ciphertext: Uint8Array;
|
|
49
55
|
}>;
|
|
56
|
+
encryptMessageWithWire(opts: EncryptMessageWithWireOpts): Promise<{
|
|
57
|
+
type: 'pkmsg' | 'msg';
|
|
58
|
+
ciphertext: Uint8Array;
|
|
59
|
+
wireJid: string;
|
|
60
|
+
}>;
|
|
50
61
|
encryptGroupMessage(opts: EncryptGroupMessageOpts): Promise<{
|
|
51
62
|
senderKeyDistributionMessage: Uint8Array;
|
|
52
63
|
ciphertext: Uint8Array;
|
|
53
64
|
}>;
|
|
54
65
|
injectE2ESession(opts: E2ESessionOpts): Promise<void>;
|
|
55
66
|
jidToSignalProtocolAddress(jid: string): string;
|
|
67
|
+
storeLIDPNMapping(lid: string, pn: string): Promise<void>;
|
|
68
|
+
getLIDMappingStore(): LIDMappingStore;
|
|
69
|
+
migrateSession(fromJid: string, toJid: string): Promise<void>;
|
|
70
|
+
validateSession(jid: string): Promise<{
|
|
71
|
+
exists: boolean;
|
|
72
|
+
reason?: string;
|
|
73
|
+
}>;
|
|
74
|
+
deleteSession(jid: string): Promise<void>;
|
|
75
|
+
destroy(): void;
|
|
56
76
|
};
|
|
57
77
|
export {};
|
|
@@ -18,6 +18,11 @@ export declare const NACK_REASONS: {
|
|
|
18
18
|
UnsupportedLIDGroup: number;
|
|
19
19
|
DBOperationFailed: number;
|
|
20
20
|
};
|
|
21
|
+
export declare const extractAddressingContext: (stanza: BinaryNode) => {
|
|
22
|
+
addressingMode: string;
|
|
23
|
+
senderAlt: string;
|
|
24
|
+
recipientAlt: string;
|
|
25
|
+
};
|
|
21
26
|
/**
|
|
22
27
|
* Decode the received node as a message.
|
|
23
28
|
* @note this will only parse the message, not decrypt it
|
|
@@ -1,11 +1,37 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.decryptMessageNode = exports.NACK_REASONS = exports.MISSING_KEYS_ERROR_TEXT = exports.NO_MESSAGE_FOUND_ERROR_TEXT = void 0;
|
|
3
|
+
exports.decryptMessageNode = exports.extractAddressingContext = exports.NACK_REASONS = exports.MISSING_KEYS_ERROR_TEXT = exports.NO_MESSAGE_FOUND_ERROR_TEXT = void 0;
|
|
4
4
|
exports.decodeMessageNode = decodeMessageNode;
|
|
5
5
|
const boom_1 = require("@hapi/boom");
|
|
6
6
|
const WAProto_1 = require("../../WAProto");
|
|
7
7
|
const WABinary_1 = require("../WABinary");
|
|
8
8
|
const generics_1 = require("./generics");
|
|
9
|
+
const getDecryptionJid = async (sender, repository) => {
|
|
10
|
+
if (!sender.includes('@s.whatsapp.net')) {
|
|
11
|
+
return sender;
|
|
12
|
+
}
|
|
13
|
+
const lidMapping = repository.getLIDMappingStore();
|
|
14
|
+
const normalizedSender = (0, WABinary_1.jidNormalizedUser)(sender);
|
|
15
|
+
const lidForPN = await lidMapping.getLIDForPN(normalizedSender);
|
|
16
|
+
if (lidForPN === null || lidForPN === void 0 ? void 0 : lidForPN.includes('@lid')) {
|
|
17
|
+
const senderDecoded = (0, WABinary_1.jidDecode)(sender);
|
|
18
|
+
const deviceId = (senderDecoded === null || senderDecoded === void 0 ? void 0 : senderDecoded.device) || 0;
|
|
19
|
+
return (0, WABinary_1.jidEncode)((0, WABinary_1.jidDecode)(lidForPN).user, 'lid', deviceId);
|
|
20
|
+
}
|
|
21
|
+
return sender;
|
|
22
|
+
};
|
|
23
|
+
const storeMappingFromEnvelope = async (stanza, sender, decryptionJid, repository, logger) => {
|
|
24
|
+
const { senderAlt } = (0, exports.extractAddressingContext)(stanza);
|
|
25
|
+
if (senderAlt && (0, WABinary_1.isLidUser)(senderAlt) && (0, WABinary_1.isJidUser)(sender) && decryptionJid === sender) {
|
|
26
|
+
try {
|
|
27
|
+
await repository.storeLIDPNMapping(senderAlt, sender);
|
|
28
|
+
logger.debug({ sender, senderAlt }, 'Stored LID mapping from envelope');
|
|
29
|
+
}
|
|
30
|
+
catch (error) {
|
|
31
|
+
logger.warn({ sender, senderAlt, error }, 'Failed to store LID mapping');
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
};
|
|
9
35
|
exports.NO_MESSAGE_FOUND_ERROR_TEXT = 'Message absent from node';
|
|
10
36
|
exports.MISSING_KEYS_ERROR_TEXT = 'Key used already or never filled';
|
|
11
37
|
exports.NACK_REASONS = {
|
|
@@ -23,6 +49,27 @@ exports.NACK_REASONS = {
|
|
|
23
49
|
UnsupportedLIDGroup: 551,
|
|
24
50
|
DBOperationFailed: 552
|
|
25
51
|
};
|
|
52
|
+
const extractAddressingContext = (stanza) => {
|
|
53
|
+
const addressingMode = stanza.attrs.addressing_mode || 'pn';
|
|
54
|
+
let senderAlt;
|
|
55
|
+
let recipientAlt;
|
|
56
|
+
if (addressingMode === 'lid') {
|
|
57
|
+
// Message is LID-addressed: sender is LID, extract corresponding PN
|
|
58
|
+
senderAlt = stanza.attrs.participant_pn || stanza.attrs.sender_pn;
|
|
59
|
+
recipientAlt = stanza.attrs.recipient_pn;
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
// Message is PN-addressed: sender is PN, extract corresponding LID
|
|
63
|
+
senderAlt = stanza.attrs.participant_lid || stanza.attrs.sender_lid;
|
|
64
|
+
recipientAlt = stanza.attrs.recipient_lid;
|
|
65
|
+
}
|
|
66
|
+
return {
|
|
67
|
+
addressingMode,
|
|
68
|
+
senderAlt,
|
|
69
|
+
recipientAlt
|
|
70
|
+
};
|
|
71
|
+
};
|
|
72
|
+
exports.extractAddressingContext = extractAddressingContext;
|
|
26
73
|
/**
|
|
27
74
|
* Decode the received node as a message.
|
|
28
75
|
* @note this will only parse the message, not decrypt it
|
|
@@ -150,11 +197,13 @@ const decryptMessageNode = (stanza, meId, meLid, repository, logger) => {
|
|
|
150
197
|
case 'pkmsg':
|
|
151
198
|
case 'msg':
|
|
152
199
|
const user = (0, WABinary_1.isJidUser)(sender) ? sender : author;
|
|
200
|
+
const decryptionJid = await getDecryptionJid(user, repository);
|
|
153
201
|
msgBuffer = await repository.decryptMessage({
|
|
154
|
-
jid:
|
|
202
|
+
jid: decryptionJid,
|
|
155
203
|
type: e2eType,
|
|
156
204
|
ciphertext: content
|
|
157
205
|
});
|
|
206
|
+
await storeMappingFromEnvelope(stanza, user, decryptionJid, repository, logger);
|
|
158
207
|
break;
|
|
159
208
|
case 'plaintext':
|
|
160
209
|
msgBuffer = content;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@periskope/baileys",
|
|
3
|
-
"version": "6.7.18-17-
|
|
3
|
+
"version": "6.7.18-17-4",
|
|
4
4
|
"description": "WhatsApp API",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"periskope",
|
|
@@ -66,6 +66,7 @@
|
|
|
66
66
|
"jimp": "^1.6.0",
|
|
67
67
|
"json": "^11.0.0",
|
|
68
68
|
"link-preview-js": "^3.0.0",
|
|
69
|
+
"lru-cache": "^10.4.3",
|
|
69
70
|
"open": "^8.4.2",
|
|
70
71
|
"prettier": "^3.5.3",
|
|
71
72
|
"protobufjs-cli": "^1.1.3",
|