keithbaileys 1.1.0 → 1.1.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.
- package/lib/Signal/libsignal.js +68 -6
- package/lib/Signal/lid-mapping.js +260 -0
- package/lib/Socket/socket.js +81 -1
- package/lib/WABinary/jid-utils.js +13 -0
- package/package.json +2 -2
package/lib/Signal/libsignal.js
CHANGED
|
@@ -40,8 +40,43 @@ const WABinary_1 = require("../WABinary");
|
|
|
40
40
|
const sender_key_name_1 = require("./Group/sender-key-name");
|
|
41
41
|
const sender_key_record_1 = require("./Group/sender-key-record");
|
|
42
42
|
const Group_1 = require("./Group");
|
|
43
|
-
|
|
44
|
-
|
|
43
|
+
const lid_mapping_1 = require("./lid-mapping");
|
|
44
|
+
/**
|
|
45
|
+
* Fix: previously every call built the signal protocol address straight from
|
|
46
|
+
* whatever JID form (PN or LID) happened to be on the message/stanza. WhatsApp
|
|
47
|
+
* sends the same contact as a PN JID sometimes and a LID JID other times, so
|
|
48
|
+
* the two forms silently ended up as two *different* Signal sessions for the
|
|
49
|
+
* same device -- once they drifted out of sync, decryption kept failing and
|
|
50
|
+
* WhatsApp shows the message as permanently "Waiting for this message".
|
|
51
|
+
*
|
|
52
|
+
* `resolveConsistentAddress` makes sure that whenever we already know a
|
|
53
|
+
* PN<->LID mapping for a contact, we always operate on ONE canonical address
|
|
54
|
+
* (preferring LID, matching current WhatsApp behavior) instead of flip-flopping.
|
|
55
|
+
*/
|
|
56
|
+
function makeLibSignalRepository(auth, logger, pnToLIDFunc) {
|
|
57
|
+
const log = logger || console;
|
|
58
|
+
const lidMapping = new lid_mapping_1.LIDMappingStore(auth.keys, log, pnToLIDFunc);
|
|
59
|
+
const storage = signalStorage(auth, lidMapping, log);
|
|
60
|
+
const resolveConsistentAddress = async (jid) => {
|
|
61
|
+
try {
|
|
62
|
+
if ((0, WABinary_1.isLidUser)(jid)) {
|
|
63
|
+
return jidToSignalProtocolAddress(jid);
|
|
64
|
+
}
|
|
65
|
+
if ((0, WABinary_1.isPnUser)(jid) || (0, WABinary_1.isHostedPnUser)(jid)) {
|
|
66
|
+
const lid = await lidMapping.getLIDForPN(jid);
|
|
67
|
+
if (lid) {
|
|
68
|
+
// if we already have session state under the old PN address,
|
|
69
|
+
// carry it over to the LID address so we don't lose the session
|
|
70
|
+
await migrateSessionIfNeeded(auth.keys, jid, lid, log);
|
|
71
|
+
return jidToSignalProtocolAddress(lid);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
catch (err) {
|
|
76
|
+
log.warn?.({ err, jid }, 'LID resolution failed, falling back to original jid');
|
|
77
|
+
}
|
|
78
|
+
return jidToSignalProtocolAddress(jid);
|
|
79
|
+
};
|
|
45
80
|
return {
|
|
46
81
|
decryptGroupMessage({ group, authorJid, msg }) {
|
|
47
82
|
const senderName = jidToSignalSenderKeyName(group, authorJid);
|
|
@@ -63,7 +98,7 @@ function makeLibSignalRepository(auth) {
|
|
|
63
98
|
await builder.process(senderName, senderMsg);
|
|
64
99
|
},
|
|
65
100
|
async decryptMessage({ jid, type, ciphertext }) {
|
|
66
|
-
const addr =
|
|
101
|
+
const addr = await resolveConsistentAddress(jid);
|
|
67
102
|
const session = new libsignal.SessionCipher(storage, addr);
|
|
68
103
|
let result;
|
|
69
104
|
switch (type) {
|
|
@@ -79,7 +114,7 @@ function makeLibSignalRepository(auth) {
|
|
|
79
114
|
return result;
|
|
80
115
|
},
|
|
81
116
|
async encryptMessage({ jid, data }) {
|
|
82
|
-
const addr =
|
|
117
|
+
const addr = await resolveConsistentAddress(jid);
|
|
83
118
|
const cipher = new libsignal.SessionCipher(storage, addr);
|
|
84
119
|
const { type: sigType, body } = await cipher.encrypt(data);
|
|
85
120
|
const type = sigType === 3 ? 'pkmsg' : 'msg';
|
|
@@ -102,14 +137,41 @@ function makeLibSignalRepository(auth) {
|
|
|
102
137
|
};
|
|
103
138
|
},
|
|
104
139
|
async injectE2ESession({ jid, session }) {
|
|
105
|
-
const
|
|
140
|
+
const addr = await resolveConsistentAddress(jid);
|
|
141
|
+
const cipher = new libsignal.SessionBuilder(storage, addr);
|
|
106
142
|
await cipher.initOutgoing(session);
|
|
107
143
|
},
|
|
108
144
|
jidToSignalProtocolAddress(jid) {
|
|
109
145
|
return jidToSignalProtocolAddress(jid).toString();
|
|
110
|
-
}
|
|
146
|
+
},
|
|
147
|
+
lidMapping
|
|
111
148
|
};
|
|
112
149
|
}
|
|
150
|
+
/**
|
|
151
|
+
* If a session already exists under the PN address but not yet under the
|
|
152
|
+
* newly-resolved LID address, copy it over so we don't force a brand new
|
|
153
|
+
* (and momentarily out-of-sync) session just because we learned the LID
|
|
154
|
+
* mapping after the fact.
|
|
155
|
+
*/
|
|
156
|
+
async function migrateSessionIfNeeded(keys, pnJid, lidJid, logger) {
|
|
157
|
+
try {
|
|
158
|
+
const pnAddr = jidToSignalProtocolAddress(pnJid).toString();
|
|
159
|
+
const lidAddr = jidToSignalProtocolAddress(lidJid).toString();
|
|
160
|
+
if (pnAddr === lidAddr)
|
|
161
|
+
return;
|
|
162
|
+
const { [lidAddr]: existingLidSession } = await keys.get('session', [lidAddr]);
|
|
163
|
+
if (existingLidSession)
|
|
164
|
+
return; // already migrated / already has its own session
|
|
165
|
+
const { [pnAddr]: pnSession } = await keys.get('session', [pnAddr]);
|
|
166
|
+
if (pnSession) {
|
|
167
|
+
await keys.set({ session: { [lidAddr]: pnSession } });
|
|
168
|
+
logger.debug?.({ pnAddr, lidAddr }, 'migrated signal session from PN address to LID address');
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
catch (err) {
|
|
172
|
+
logger.warn?.({ err }, 'session migration to LID address failed, continuing without it');
|
|
173
|
+
}
|
|
174
|
+
}
|
|
113
175
|
const jidToSignalProtocolAddress = (jid) => {
|
|
114
176
|
const { user, device } = (0, WABinary_1.jidDecode)(jid);
|
|
115
177
|
return new libsignal.ProtocolAddress(user, device || 0);
|
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.LIDMappingStore = void 0;
|
|
4
|
+
const WABinary_1 = require("../WABinary");
|
|
5
|
+
const MAPPING_TTL_MS = 3 * 24 * 60 * 60 * 1000; // 3 days
|
|
6
|
+
/**
|
|
7
|
+
* A minimal TTL cache used instead of the `lru-cache` package so this file
|
|
8
|
+
* doesn't require adding a new dependency to keithbaileys.
|
|
9
|
+
*/
|
|
10
|
+
class SimpleTTLCache {
|
|
11
|
+
constructor(ttlMs) {
|
|
12
|
+
this.ttlMs = ttlMs;
|
|
13
|
+
this.map = new Map();
|
|
14
|
+
}
|
|
15
|
+
get(key) {
|
|
16
|
+
const entry = this.map.get(key);
|
|
17
|
+
if (!entry)
|
|
18
|
+
return undefined;
|
|
19
|
+
if (Date.now() - entry.t > this.ttlMs) {
|
|
20
|
+
this.map.delete(key);
|
|
21
|
+
return undefined;
|
|
22
|
+
}
|
|
23
|
+
entry.t = Date.now(); // updateAgeOnGet
|
|
24
|
+
return entry.v;
|
|
25
|
+
}
|
|
26
|
+
set(key, value) {
|
|
27
|
+
this.map.set(key, { v: value, t: Date.now() });
|
|
28
|
+
}
|
|
29
|
+
delete(key) {
|
|
30
|
+
this.map.delete(key);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
class LIDMappingStore {
|
|
34
|
+
constructor(keys, logger, pnToLIDFunc) {
|
|
35
|
+
this.mappingCache = new SimpleTTLCache(MAPPING_TTL_MS);
|
|
36
|
+
this.inflightLIDLookups = new Map();
|
|
37
|
+
this.inflightPNLookups = new Map();
|
|
38
|
+
this.keys = keys;
|
|
39
|
+
this.pnToLIDFunc = pnToLIDFunc;
|
|
40
|
+
this.logger = logger || console;
|
|
41
|
+
}
|
|
42
|
+
async storeLIDPNMappings(pairs) {
|
|
43
|
+
if (!pairs || pairs.length === 0)
|
|
44
|
+
return;
|
|
45
|
+
const validatedPairs = [];
|
|
46
|
+
for (const { lid, pn } of pairs) {
|
|
47
|
+
if (!((0, WABinary_1.isLidUser)(lid) && (0, WABinary_1.isPnUser)(pn)) &&
|
|
48
|
+
!((0, WABinary_1.isPnUser)(lid) && (0, WABinary_1.isLidUser)(pn))) {
|
|
49
|
+
this.logger.warn?.(`Invalid LID-PN mapping: ${lid}, ${pn}`);
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
const lidDecoded = (0, WABinary_1.jidDecode)(lid);
|
|
53
|
+
const pnDecoded = (0, WABinary_1.jidDecode)(pn);
|
|
54
|
+
if (!lidDecoded || !pnDecoded)
|
|
55
|
+
continue;
|
|
56
|
+
validatedPairs.push({ pnUser: pnDecoded.user, lidUser: lidDecoded.user });
|
|
57
|
+
}
|
|
58
|
+
if (validatedPairs.length === 0)
|
|
59
|
+
return;
|
|
60
|
+
const cacheMissSet = new Set();
|
|
61
|
+
const existingMappings = new Map();
|
|
62
|
+
for (const { pnUser } of validatedPairs) {
|
|
63
|
+
const cached = this.mappingCache.get(`pn:${pnUser}`);
|
|
64
|
+
if (cached) {
|
|
65
|
+
existingMappings.set(pnUser, cached);
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
cacheMissSet.add(pnUser);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
if (cacheMissSet.size > 0) {
|
|
72
|
+
const cacheMisses = [...cacheMissSet];
|
|
73
|
+
const stored = await this.keys.get('lid-mapping', cacheMisses);
|
|
74
|
+
for (const pnUser of cacheMisses) {
|
|
75
|
+
const existingLidUser = stored[pnUser];
|
|
76
|
+
if (existingLidUser) {
|
|
77
|
+
existingMappings.set(pnUser, existingLidUser);
|
|
78
|
+
this.mappingCache.set(`pn:${pnUser}`, existingLidUser);
|
|
79
|
+
this.mappingCache.set(`lid:${existingLidUser}`, pnUser);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
const pairMap = {};
|
|
84
|
+
for (const { pnUser, lidUser } of validatedPairs) {
|
|
85
|
+
const existingLidUser = existingMappings.get(pnUser);
|
|
86
|
+
if (existingLidUser === lidUser) {
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
pairMap[pnUser] = lidUser;
|
|
90
|
+
}
|
|
91
|
+
if (Object.keys(pairMap).length === 0)
|
|
92
|
+
return;
|
|
93
|
+
const batchData = {};
|
|
94
|
+
for (const [pnUser, lidUser] of Object.entries(pairMap)) {
|
|
95
|
+
batchData[pnUser] = lidUser;
|
|
96
|
+
batchData[`${lidUser}_reverse`] = pnUser;
|
|
97
|
+
const oldLidUser = existingMappings.get(pnUser);
|
|
98
|
+
if (oldLidUser && oldLidUser !== lidUser) {
|
|
99
|
+
batchData[`${oldLidUser}_reverse`] = null;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
if (this.keys.transaction) {
|
|
103
|
+
await this.keys.transaction(async () => {
|
|
104
|
+
await this.keys.set({ 'lid-mapping': batchData });
|
|
105
|
+
}, 'lid-mapping');
|
|
106
|
+
}
|
|
107
|
+
else {
|
|
108
|
+
await this.keys.set({ 'lid-mapping': batchData });
|
|
109
|
+
}
|
|
110
|
+
for (const [pnUser, lidUser] of Object.entries(pairMap)) {
|
|
111
|
+
this.mappingCache.set(`pn:${pnUser}`, lidUser);
|
|
112
|
+
this.mappingCache.set(`lid:${lidUser}`, pnUser);
|
|
113
|
+
const oldLidUser = existingMappings.get(pnUser);
|
|
114
|
+
if (oldLidUser && oldLidUser !== lidUser) {
|
|
115
|
+
this.mappingCache.delete(`lid:${oldLidUser}`);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
async getLIDForPN(pn) {
|
|
120
|
+
var _a, _b;
|
|
121
|
+
return ((_b = (_a = (await this.getLIDsForPNs([pn]))) === null || _a === void 0 ? void 0 : _a[0]) === null || _b === void 0 ? void 0 : _b.lid) || null;
|
|
122
|
+
}
|
|
123
|
+
async getLIDsForPNs(pns) {
|
|
124
|
+
if (!pns || pns.length === 0)
|
|
125
|
+
return null;
|
|
126
|
+
const sortedPns = [...new Set(pns)].sort();
|
|
127
|
+
const cacheKey = sortedPns.join(',');
|
|
128
|
+
const inflight = this.inflightLIDLookups.get(cacheKey);
|
|
129
|
+
if (inflight) {
|
|
130
|
+
return inflight;
|
|
131
|
+
}
|
|
132
|
+
const promise = this._getLIDsForPNsImpl(pns);
|
|
133
|
+
this.inflightLIDLookups.set(cacheKey, promise);
|
|
134
|
+
try {
|
|
135
|
+
return await promise;
|
|
136
|
+
}
|
|
137
|
+
finally {
|
|
138
|
+
this.inflightLIDLookups.delete(cacheKey);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
async _getLIDsForPNsImpl(pns) {
|
|
142
|
+
const usyncFetch = {};
|
|
143
|
+
const successfulPairs = {};
|
|
144
|
+
for (const pn of pns) {
|
|
145
|
+
if (!(0, WABinary_1.isPnUser)(pn) && !(0, WABinary_1.isHostedPnUser)(pn))
|
|
146
|
+
continue;
|
|
147
|
+
const decoded = (0, WABinary_1.jidDecode)(pn);
|
|
148
|
+
if (!decoded)
|
|
149
|
+
continue;
|
|
150
|
+
const pnUser = decoded.user;
|
|
151
|
+
let lidUser = this.mappingCache.get(`pn:${pnUser}`);
|
|
152
|
+
if (!lidUser) {
|
|
153
|
+
const stored = await this.keys.get('lid-mapping', [pnUser]);
|
|
154
|
+
lidUser = stored[pnUser];
|
|
155
|
+
if (lidUser) {
|
|
156
|
+
this.mappingCache.set(`pn:${pnUser}`, lidUser);
|
|
157
|
+
this.mappingCache.set(`lid:${lidUser}`, pnUser);
|
|
158
|
+
}
|
|
159
|
+
else {
|
|
160
|
+
const device = decoded.device || 0;
|
|
161
|
+
let normalizedPn = (0, WABinary_1.jidNormalizedUser)(pn);
|
|
162
|
+
if ((0, WABinary_1.isHostedPnUser)(normalizedPn)) {
|
|
163
|
+
normalizedPn = `${pnUser}@s.whatsapp.net`;
|
|
164
|
+
}
|
|
165
|
+
if (!usyncFetch[normalizedPn]) {
|
|
166
|
+
usyncFetch[normalizedPn] = [device];
|
|
167
|
+
}
|
|
168
|
+
else {
|
|
169
|
+
usyncFetch[normalizedPn].push(device);
|
|
170
|
+
}
|
|
171
|
+
continue;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
lidUser = lidUser.toString();
|
|
175
|
+
if (!lidUser) {
|
|
176
|
+
this.logger.warn?.(`Invalid or empty LID user for PN ${pn}`);
|
|
177
|
+
return null;
|
|
178
|
+
}
|
|
179
|
+
const pnDevice = decoded.device !== undefined ? decoded.device : 0;
|
|
180
|
+
const deviceSpecificLid = `${lidUser}${pnDevice ? `:${pnDevice}` : ``}@${decoded.server === 'hosted' ? 'hosted.lid' : 'lid'}`;
|
|
181
|
+
successfulPairs[pn] = { lid: deviceSpecificLid, pn };
|
|
182
|
+
}
|
|
183
|
+
if (Object.keys(usyncFetch).length > 0) {
|
|
184
|
+
const result = this.pnToLIDFunc ? await this.pnToLIDFunc(Object.keys(usyncFetch)) : null;
|
|
185
|
+
if (result && result.length > 0) {
|
|
186
|
+
await this.storeLIDPNMappings(result);
|
|
187
|
+
for (const pair of result) {
|
|
188
|
+
const pnDecoded = (0, WABinary_1.jidDecode)(pair.pn);
|
|
189
|
+
const pnUser = pnDecoded === null || pnDecoded === void 0 ? void 0 : pnDecoded.user;
|
|
190
|
+
if (!pnUser)
|
|
191
|
+
continue;
|
|
192
|
+
const lidDecoded = (0, WABinary_1.jidDecode)(pair.lid);
|
|
193
|
+
const lidUser = lidDecoded === null || lidDecoded === void 0 ? void 0 : lidDecoded.user;
|
|
194
|
+
if (!lidUser)
|
|
195
|
+
continue;
|
|
196
|
+
for (const device of (usyncFetch[pair.pn] || [])) {
|
|
197
|
+
const deviceSpecificLid = `${lidUser}${device ? `:${device}` : ``}@${device === 99 ? 'hosted.lid' : 'lid'}`;
|
|
198
|
+
const deviceSpecificPn = `${pnUser}${device ? `:${device}` : ``}@${device === 99 ? 'hosted' : 's.whatsapp.net'}`;
|
|
199
|
+
successfulPairs[deviceSpecificPn] = { lid: deviceSpecificLid, pn: deviceSpecificPn };
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
else {
|
|
204
|
+
return null;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
return Object.values(successfulPairs);
|
|
208
|
+
}
|
|
209
|
+
/** Get PNs for LIDs - user level, with device numbers reattached */
|
|
210
|
+
async getPNsForLIDs(lids) {
|
|
211
|
+
const result = [];
|
|
212
|
+
const missingLids = [];
|
|
213
|
+
for (const lid of lids) {
|
|
214
|
+
if (!(0, WABinary_1.isLidUser)(lid)) {
|
|
215
|
+
continue;
|
|
216
|
+
}
|
|
217
|
+
const decoded = (0, WABinary_1.jidDecode)(lid);
|
|
218
|
+
if (!decoded) {
|
|
219
|
+
continue;
|
|
220
|
+
}
|
|
221
|
+
const lidUser = decoded.user;
|
|
222
|
+
const pnUser = this.mappingCache.get(`lid:${lidUser}`);
|
|
223
|
+
if (!pnUser || typeof pnUser !== 'string') {
|
|
224
|
+
missingLids.push(lidUser);
|
|
225
|
+
}
|
|
226
|
+
else {
|
|
227
|
+
const lidDevice = decoded.device !== undefined ? decoded.device : 0;
|
|
228
|
+
const domain = decoded.domainType === WABinary_1.WAJIDDomains.HOSTED_LID ? 'hosted' : 's.whatsapp.net';
|
|
229
|
+
const pnJid = `${pnUser}${lidDevice ? `:${lidDevice}` : ``}@${domain}`;
|
|
230
|
+
result.push({ lid, pn: pnJid });
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
if (missingLids.length > 0) {
|
|
234
|
+
const lookupKeys = missingLids.map(l => `${l}_reverse`);
|
|
235
|
+
const stored = await this.keys.get('lid-mapping', lookupKeys);
|
|
236
|
+
for (const lidUser of missingLids) {
|
|
237
|
+
const pnUser = stored[`${lidUser}_reverse`];
|
|
238
|
+
if (pnUser && typeof pnUser === 'string') {
|
|
239
|
+
this.mappingCache.set(`lid:${lidUser}`, pnUser);
|
|
240
|
+
for (const lid of lids) {
|
|
241
|
+
const decoded = (0, WABinary_1.jidDecode)(lid);
|
|
242
|
+
if (decoded && decoded.user === lidUser) {
|
|
243
|
+
const lidDevice = decoded.device !== undefined ? decoded.device : 0;
|
|
244
|
+
const domain = decoded.domainType === WABinary_1.WAJIDDomains.HOSTED_LID ? 'hosted' : 's.whatsapp.net';
|
|
245
|
+
const pnJid = `${pnUser}${lidDevice ? `:${lidDevice}` : ``}@${domain}`;
|
|
246
|
+
result.push({ lid, pn: pnJid });
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
return result;
|
|
253
|
+
}
|
|
254
|
+
async getPNForLID(lid) {
|
|
255
|
+
var _a, _b;
|
|
256
|
+
const results = await this.getPNsForLIDs([lid]);
|
|
257
|
+
return ((_b = (_a = results) === null || _a === void 0 ? void 0 : _a[0]) === null || _b === void 0 ? void 0 : _b.pn) || null;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
exports.LIDMappingStore = LIDMappingStore;
|
package/lib/Socket/socket.js
CHANGED
|
@@ -12,6 +12,8 @@ const Utils_1 = require("../Utils");
|
|
|
12
12
|
const WABinary_1 = require("../WABinary");
|
|
13
13
|
const Client_1 = require("./Client");
|
|
14
14
|
const mex_1 = require("./mex");
|
|
15
|
+
const USyncQuery_1 = require("../WAUSync/USyncQuery");
|
|
16
|
+
const USyncUser_1 = require("../WAUSync/USyncUser");
|
|
15
17
|
/**
|
|
16
18
|
* Connects to WA servers and performs:
|
|
17
19
|
* - simple queries (no retry mechanism, wait for connection establishment)
|
|
@@ -46,7 +48,37 @@ const makeSocket = (config) => {
|
|
|
46
48
|
const { creds } = authState;
|
|
47
49
|
// add transaction capability
|
|
48
50
|
const keys = (0, Utils_1.addTransactionCapability)(authState.keys, logger, transactionOpts);
|
|
49
|
-
|
|
51
|
+
/**
|
|
52
|
+
* Resolves PN -> LID mappings via a USync query. This is a closure so it
|
|
53
|
+
* can safely reference `query`/`generateMessageTag` even though they are
|
|
54
|
+
* declared further down in this function: by the time this is actually
|
|
55
|
+
* *called* (during message send/receive), those are already initialized.
|
|
56
|
+
* Passed into the LID mapping store so signal sessions stop flip-flopping
|
|
57
|
+
* between PN and LID addresses for the same contact.
|
|
58
|
+
*/
|
|
59
|
+
const pnFromLIDUSync = async (jids) => {
|
|
60
|
+
const usyncQuery = new USyncQuery_1.USyncQuery().withLIDProtocol().withContext('background');
|
|
61
|
+
for (const jid of jids) {
|
|
62
|
+
if ((0, WABinary_1.isLidUser)(jid)) {
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
usyncQuery.withUser(new USyncUser_1.USyncUser().withId(jid));
|
|
66
|
+
}
|
|
67
|
+
if (usyncQuery.users.length === 0) {
|
|
68
|
+
return [];
|
|
69
|
+
}
|
|
70
|
+
try {
|
|
71
|
+
const result = await executeUSyncQuery(usyncQuery);
|
|
72
|
+
if (result) {
|
|
73
|
+
return result.list.filter(a => !!a.lid).map(({ lid, id }) => ({ pn: id, lid }));
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
catch (err) {
|
|
77
|
+
logger?.debug?.({ err }, 'pnFromLIDUSync query failed');
|
|
78
|
+
}
|
|
79
|
+
return [];
|
|
80
|
+
};
|
|
81
|
+
const signalRepository = makeSignalRepository({ creds, keys }, logger, pnFromLIDUSync);
|
|
50
82
|
let lastDateRecv;
|
|
51
83
|
let epoch = 1;
|
|
52
84
|
let keepAliveReq;
|
|
@@ -146,6 +178,53 @@ const makeSocket = (config) => {
|
|
|
146
178
|
}
|
|
147
179
|
return result;
|
|
148
180
|
};
|
|
181
|
+
/** run a USync query (used internally for PN<->LID resolution, also exposed on the socket) */
|
|
182
|
+
const executeUSyncQuery = async (usyncQuery) => {
|
|
183
|
+
if (usyncQuery.protocols.length === 0) {
|
|
184
|
+
throw new boom_1.Boom('USyncQuery must have at least one protocol');
|
|
185
|
+
}
|
|
186
|
+
const validUsers = usyncQuery.users;
|
|
187
|
+
const userNodes = validUsers.map(user => ({
|
|
188
|
+
tag: 'user',
|
|
189
|
+
attrs: {
|
|
190
|
+
jid: !user.phone ? user.id : undefined
|
|
191
|
+
},
|
|
192
|
+
content: usyncQuery.protocols.map(a => a.getUserElement(user)).filter(a => a !== null)
|
|
193
|
+
}));
|
|
194
|
+
const listNode = {
|
|
195
|
+
tag: 'list',
|
|
196
|
+
attrs: {},
|
|
197
|
+
content: userNodes
|
|
198
|
+
};
|
|
199
|
+
const queryNode = {
|
|
200
|
+
tag: 'query',
|
|
201
|
+
attrs: {},
|
|
202
|
+
content: usyncQuery.protocols.map(a => a.getQueryElement())
|
|
203
|
+
};
|
|
204
|
+
const iq = {
|
|
205
|
+
tag: 'iq',
|
|
206
|
+
attrs: {
|
|
207
|
+
to: WABinary_1.S_WHATSAPP_NET,
|
|
208
|
+
type: 'get',
|
|
209
|
+
xmlns: 'usync'
|
|
210
|
+
},
|
|
211
|
+
content: [
|
|
212
|
+
{
|
|
213
|
+
tag: 'usync',
|
|
214
|
+
attrs: {
|
|
215
|
+
context: usyncQuery.context,
|
|
216
|
+
mode: usyncQuery.mode,
|
|
217
|
+
sid: generateMessageTag(),
|
|
218
|
+
last: 'true',
|
|
219
|
+
index: '0'
|
|
220
|
+
},
|
|
221
|
+
content: [queryNode, listNode]
|
|
222
|
+
}
|
|
223
|
+
]
|
|
224
|
+
};
|
|
225
|
+
const result = await query(iq);
|
|
226
|
+
return usyncQuery.parseUSyncQueryResult(result);
|
|
227
|
+
};
|
|
149
228
|
/** connection handshake */
|
|
150
229
|
const validateConnection = async () => {
|
|
151
230
|
let helloMsg = {
|
|
@@ -615,6 +694,7 @@ const makeSocket = (config) => {
|
|
|
615
694
|
},
|
|
616
695
|
generateMessageTag,
|
|
617
696
|
query,
|
|
697
|
+
executeUSyncQuery,
|
|
618
698
|
waitForMessage,
|
|
619
699
|
waitForSocketOpen,
|
|
620
700
|
sendRawMessage,
|
|
@@ -64,3 +64,16 @@ const jidNormalizedUser = (jid) => {
|
|
|
64
64
|
return (0, exports.jidEncode)(user, server === 'c.us' ? 's.whatsapp.net' : server);
|
|
65
65
|
};
|
|
66
66
|
exports.jidNormalizedUser = jidNormalizedUser;
|
|
67
|
+
/** is the jid a normal phone-number user (used by LID mapping) */
|
|
68
|
+
const isPnUser = (jid) => jid === null || jid === void 0 ? void 0 : jid.endsWith('@s.whatsapp.net');
|
|
69
|
+
exports.isPnUser = isPnUser;
|
|
70
|
+
/** is the jid a "hosted" phone-number user (business hosted accounts) */
|
|
71
|
+
const isHostedPnUser = (jid) => jid === null || jid === void 0 ? void 0 : jid.endsWith('@hosted');
|
|
72
|
+
exports.isHostedPnUser = isHostedPnUser;
|
|
73
|
+
/** domain type constants used by the LID mapping store */
|
|
74
|
+
exports.WAJIDDomains = {
|
|
75
|
+
PN: 0,
|
|
76
|
+
LID: 1,
|
|
77
|
+
HOSTED: 2,
|
|
78
|
+
HOSTED_LID: 3
|
|
79
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "keithbaileys",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.1",
|
|
4
4
|
"description": "A lightweight, full-featured WhatsApp Web API library for Node.js",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"types": "lib/index.d.ts",
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
"async-mutex": "^0.5.0",
|
|
23
23
|
"axios": "^1.6.0",
|
|
24
24
|
"fflate": "^0.8.2",
|
|
25
|
-
"libsignal": "
|
|
25
|
+
"libsignal": "^6.0.0",
|
|
26
26
|
"lodash": "^4.17.21",
|
|
27
27
|
"music-metadata": "^7.12.3",
|
|
28
28
|
"node-fetch": "^2.6.1",
|