alipclutch-baileys 8.5.4 → 8.5.5
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/WAProto/index.js +56887 -17535
- package/lib/Socket/socket.js +66 -31
- package/lib/Utils/use-multi-file-auth-state.js +45 -6
- package/package.json +1 -1
package/lib/Socket/socket.js
CHANGED
|
@@ -20,15 +20,16 @@ const Client_1 = require("./Client");
|
|
|
20
20
|
const makeSocket = (config) => {
|
|
21
21
|
var _a, _b;
|
|
22
22
|
const { waWebSocketUrl, connectTimeoutMs, logger, keepAliveIntervalMs, browser, auth: authState, printQRInTerminal, defaultQueryTimeoutMs, transactionOpts, qrTimeout, makeSignalRepository, } = config;
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
23
|
+
const url = typeof waWebSocketUrl === 'string' ? new url_1.URL(waWebSocketUrl) : waWebSocketUrl;
|
|
24
|
+
if (config.mobile || url.protocol === 'tcp:') {
|
|
25
|
+
throw new boom_1.Boom('Mobile API is not supported anymore', {
|
|
26
|
+
statusCode: Types_1.DisconnectReason.loggedOut
|
|
27
|
+
});
|
|
27
28
|
}
|
|
28
|
-
if (
|
|
29
|
+
if (url.protocol === 'wss' && ((_a = authState === null || authState === void 0 ? void 0 : authState.creds) === null || _a === void 0 ? void 0 : _a.routingInfo)) {
|
|
29
30
|
url.searchParams.append('ED', authState.creds.routingInfo.toString('base64url'));
|
|
30
31
|
}
|
|
31
|
-
const ws =
|
|
32
|
+
const ws = new Client_1.WebSocketClient(url, config);
|
|
32
33
|
ws.connect();
|
|
33
34
|
const ev = (0, Utils_1.makeEventBuffer)(logger);
|
|
34
35
|
/** ephemeral key pair used to encrypt/decrypt communication. Unique for each connection */
|
|
@@ -36,8 +37,7 @@ const makeSocket = (config) => {
|
|
|
36
37
|
/** WA noise protocol wrapper */
|
|
37
38
|
const noise = (0, Utils_1.makeNoiseHandler)({
|
|
38
39
|
keyPair: ephemeralKeyPair,
|
|
39
|
-
NOISE_HEADER:
|
|
40
|
-
mobile: config.mobile,
|
|
40
|
+
NOISE_HEADER: Defaults_1.NOISE_WA_HEADER,
|
|
41
41
|
logger,
|
|
42
42
|
routingInfo: (_b = authState === null || authState === void 0 ? void 0 : authState.creds) === null || _b === void 0 ? void 0 : _b.routingInfo
|
|
43
43
|
});
|
|
@@ -80,6 +80,25 @@ const makeSocket = (config) => {
|
|
|
80
80
|
/** log & process any unexpected errors */
|
|
81
81
|
const onUnexpectedError = (err, msg) => {
|
|
82
82
|
logger.error({ err }, `unexpected error in '${msg}'`);
|
|
83
|
+
const message = (err && ((err.stack || err.message) || String(err))).toLowerCase();
|
|
84
|
+
// auto recover from cryptographic desyncs by re-uploading prekeys
|
|
85
|
+
if (message.includes('bad mac') || (message.includes('mac') && message.includes('invalid'))) {
|
|
86
|
+
try {
|
|
87
|
+
uploadPreKeysToServerIfRequired(true)
|
|
88
|
+
.catch(e => logger.warn({ e }, 'failed to re-upload prekeys after bad mac'));
|
|
89
|
+
}
|
|
90
|
+
catch (_e) {
|
|
91
|
+
// ignore
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
// gently back off when encountering rate limits (429)
|
|
95
|
+
if (message.includes('429') || message.includes('rate limit')) {
|
|
96
|
+
const wait = Math.min(30000, (config.backoffDelayMs || 5000));
|
|
97
|
+
logger.info({ wait }, 'backing off due to rate limit');
|
|
98
|
+
setTimeout(() => {
|
|
99
|
+
// intentionally empty; wait to delay further sends
|
|
100
|
+
}, wait);
|
|
101
|
+
}
|
|
83
102
|
};
|
|
84
103
|
/** await the next incoming message */
|
|
85
104
|
const awaitNextMessage = async (sendMsg) => {
|
|
@@ -116,7 +135,7 @@ const makeSocket = (config) => {
|
|
|
116
135
|
let onRecv;
|
|
117
136
|
let onErr;
|
|
118
137
|
try {
|
|
119
|
-
|
|
138
|
+
const result = await (0, Utils_1.promiseTimeout)(timeoutMs, (resolve, reject) => {
|
|
120
139
|
onRecv = resolve;
|
|
121
140
|
onErr = err => {
|
|
122
141
|
reject(err || new boom_1.Boom('Connection Closed', { statusCode: Types_1.DisconnectReason.connectionClosed }));
|
|
@@ -125,6 +144,7 @@ const makeSocket = (config) => {
|
|
|
125
144
|
ws.on('close', onErr); // if the socket closes, you'll never receive the message
|
|
126
145
|
ws.off('error', onErr);
|
|
127
146
|
});
|
|
147
|
+
return result;
|
|
128
148
|
}
|
|
129
149
|
finally {
|
|
130
150
|
ws.off(`TAG:${msgId}`, onRecv);
|
|
@@ -138,9 +158,10 @@ const makeSocket = (config) => {
|
|
|
138
158
|
node.attrs.id = generateMessageTag();
|
|
139
159
|
}
|
|
140
160
|
const msgId = node.attrs.id;
|
|
141
|
-
const
|
|
142
|
-
|
|
143
|
-
|
|
161
|
+
const [result] = await Promise.all([
|
|
162
|
+
waitForMessage(msgId, timeoutMs),
|
|
163
|
+
sendNode(node)
|
|
164
|
+
]);
|
|
144
165
|
if ('tag' in result) {
|
|
145
166
|
(0, WABinary_1.assertNodeErrorFree)(result);
|
|
146
167
|
}
|
|
@@ -157,12 +178,9 @@ const makeSocket = (config) => {
|
|
|
157
178
|
const result = await awaitNextMessage(init);
|
|
158
179
|
const handshake = WAProto_1.proto.HandshakeMessage.decode(result);
|
|
159
180
|
logger.trace({ handshake }, 'handshake recv from WA');
|
|
160
|
-
const keyEnc = noise.processHandshake(handshake, creds.noiseKey);
|
|
181
|
+
const keyEnc = await noise.processHandshake(handshake, creds.noiseKey);
|
|
161
182
|
let node;
|
|
162
|
-
if (
|
|
163
|
-
node = (0, Utils_1.generateMobileNode)(config);
|
|
164
|
-
}
|
|
165
|
-
else if (!creds.me) {
|
|
183
|
+
if (!creds.me) {
|
|
166
184
|
node = (0, Utils_1.generateRegistrationNode)(creds, config);
|
|
167
185
|
logger.info({ node }, 'not logged in, attempting registration...');
|
|
168
186
|
}
|
|
@@ -232,11 +250,11 @@ const makeSocket = (config) => {
|
|
|
232
250
|
const l0 = frame.tag;
|
|
233
251
|
const l1 = frame.attrs || {};
|
|
234
252
|
const l2 = Array.isArray(frame.content) ? (_a = frame.content[0]) === null || _a === void 0 ? void 0 : _a.tag : '';
|
|
235
|
-
Object.keys(l1)
|
|
253
|
+
for (const key of Object.keys(l1)) {
|
|
236
254
|
anyTriggered = ws.emit(`${Defaults_1.DEF_CALLBACK_PREFIX}${l0},${key}:${l1[key]},${l2}`, frame) || anyTriggered;
|
|
237
255
|
anyTriggered = ws.emit(`${Defaults_1.DEF_CALLBACK_PREFIX}${l0},${key}:${l1[key]}`, frame) || anyTriggered;
|
|
238
256
|
anyTriggered = ws.emit(`${Defaults_1.DEF_CALLBACK_PREFIX}${l0},${key}`, frame) || anyTriggered;
|
|
239
|
-
}
|
|
257
|
+
}
|
|
240
258
|
anyTriggered = ws.emit(`${Defaults_1.DEF_CALLBACK_PREFIX}${l0},,${l2}`, frame) || anyTriggered;
|
|
241
259
|
anyTriggered = ws.emit(`${Defaults_1.DEF_CALLBACK_PREFIX}${l0}`, frame) || anyTriggered;
|
|
242
260
|
if (!anyTriggered && logger.level === 'debug') {
|
|
@@ -365,11 +383,11 @@ const makeSocket = (config) => {
|
|
|
365
383
|
}
|
|
366
384
|
end(new boom_1.Boom(msg || 'Intentional Logout', { statusCode: Types_1.DisconnectReason.loggedOut }));
|
|
367
385
|
};
|
|
368
|
-
/** This method was created by snowi, and implemented by KyuuRzy */
|
|
369
386
|
|
|
387
|
+
/** This method was created by snowi, and implemented by KyuuRzy */
|
|
370
388
|
/** hey bro, if you delete this text */
|
|
371
389
|
/** you are the most cursed human being who likes to claim other people's property 😹🙌🏻 */
|
|
372
|
-
const requestPairingCode = async (phoneNumber, pairKey
|
|
390
|
+
const requestPairingCode = async (phoneNumber, pairKey) => {
|
|
373
391
|
if (pairKey) {
|
|
374
392
|
authState.creds.pairingCode = pairKey.toUpperCase();
|
|
375
393
|
} else {
|
|
@@ -431,12 +449,11 @@ const makeSocket = (config) => {
|
|
|
431
449
|
});
|
|
432
450
|
|
|
433
451
|
return authState.creds.pairingCode;
|
|
434
|
-
}
|
|
435
|
-
|
|
452
|
+
}
|
|
436
453
|
async function generatePairingKey() {
|
|
437
454
|
const salt = (0, crypto_1.randomBytes)(32);
|
|
438
455
|
const randomIv = (0, crypto_1.randomBytes)(16);
|
|
439
|
-
const key = (0, Utils_1.derivePairingCodeKey)(authState.creds.pairingCode, salt);
|
|
456
|
+
const key = await (0, Utils_1.derivePairingCodeKey)(authState.creds.pairingCode, salt);
|
|
440
457
|
const ciphered = (0, Utils_1.aesEncryptCTR)(authState.creds.pairingEphemeralKeyPair.public, key, randomIv);
|
|
441
458
|
return Buffer.concat([salt, randomIv, ciphered]);
|
|
442
459
|
}
|
|
@@ -523,12 +540,18 @@ const makeSocket = (config) => {
|
|
|
523
540
|
});
|
|
524
541
|
// login complete
|
|
525
542
|
ws.on('CB:success', async (node) => {
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
543
|
+
try {
|
|
544
|
+
await uploadPreKeysToServerIfRequired();
|
|
545
|
+
await sendPassiveIq('active');
|
|
546
|
+
logger.info('opened connection to WA');
|
|
547
|
+
clearTimeout(qrTimer); // will never happen in all likelyhood -- but just in case WA sends success on first try
|
|
548
|
+
ev.emit('creds.update', { me: { ...authState.creds.me, lid: node.attrs.lid } });
|
|
549
|
+
ev.emit('connection.update', { connection: 'open' });
|
|
550
|
+
}
|
|
551
|
+
catch (err) {
|
|
552
|
+
logger.error({ err }, 'error opening connection');
|
|
553
|
+
end(err);
|
|
554
|
+
}
|
|
532
555
|
});
|
|
533
556
|
ws.on('CB:stream:error', (node) => {
|
|
534
557
|
logger.error({ node }, 'stream errored out');
|
|
@@ -543,11 +566,20 @@ const makeSocket = (config) => {
|
|
|
543
566
|
ws.on('CB:ib,,downgrade_webclient', () => {
|
|
544
567
|
end(new boom_1.Boom('Multi-device beta not joined', { statusCode: Types_1.DisconnectReason.multideviceMismatch }));
|
|
545
568
|
});
|
|
569
|
+
ws.on('CB:ib,,offline_preview', (node) => {
|
|
570
|
+
logger.info('offline preview received', JSON.stringify(node));
|
|
571
|
+
sendNode({
|
|
572
|
+
tag: 'ib',
|
|
573
|
+
attrs: {},
|
|
574
|
+
content: [{ tag: 'offline_batch', attrs: { count: '100' } }]
|
|
575
|
+
});
|
|
576
|
+
});
|
|
546
577
|
ws.on('CB:ib,,edge_routing', (node) => {
|
|
547
578
|
const edgeRoutingNode = (0, WABinary_1.getBinaryNodeChild)(node, 'edge_routing');
|
|
548
579
|
const routingInfo = (0, WABinary_1.getBinaryNodeChild)(edgeRoutingNode, 'routing_info');
|
|
549
580
|
if (routingInfo === null || routingInfo === void 0 ? void 0 : routingInfo.content) {
|
|
550
581
|
authState.creds.routingInfo = Buffer.from(routingInfo === null || routingInfo === void 0 ? void 0 : routingInfo.content);
|
|
582
|
+
ev.emit('creds.update', authState.creds);
|
|
551
583
|
}
|
|
552
584
|
});
|
|
553
585
|
let didStartBuffer = false;
|
|
@@ -596,7 +628,10 @@ const makeSocket = (config) => {
|
|
|
596
628
|
type: 'md',
|
|
597
629
|
ws,
|
|
598
630
|
ev,
|
|
599
|
-
authState: {
|
|
631
|
+
authState: {
|
|
632
|
+
creds,
|
|
633
|
+
keys
|
|
634
|
+
},
|
|
600
635
|
signalRepository,
|
|
601
636
|
get user() {
|
|
602
637
|
return authState.creds.me;
|
|
@@ -1,11 +1,21 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.useMultiFileAuthState = void 0;
|
|
4
|
+
const async_mutex_1 = require("async-mutex");
|
|
4
5
|
const promises_1 = require("fs/promises");
|
|
5
6
|
const path_1 = require("path");
|
|
6
7
|
const WAProto_1 = require("../../WAProto");
|
|
7
8
|
const auth_utils_1 = require("./auth-utils");
|
|
8
9
|
const generics_1 = require("./generics");
|
|
10
|
+
const fileLocks = new Map();
|
|
11
|
+
const getFileLock = (path) => {
|
|
12
|
+
let mutex = fileLocks.get(path);
|
|
13
|
+
if (!mutex) {
|
|
14
|
+
mutex = new async_mutex_1.Mutex();
|
|
15
|
+
fileLocks.set(path, mutex);
|
|
16
|
+
}
|
|
17
|
+
return mutex;
|
|
18
|
+
};
|
|
9
19
|
/**
|
|
10
20
|
* stores the full authentication state in a single folder.
|
|
11
21
|
* Far more efficient than singlefileauthstate
|
|
@@ -14,13 +24,31 @@ const generics_1 = require("./generics");
|
|
|
14
24
|
* Would recommend writing an auth state for use with a proper SQL or No-SQL DB
|
|
15
25
|
* */
|
|
16
26
|
const useMultiFileAuthState = async (folder) => {
|
|
17
|
-
const writeData = (data, file) => {
|
|
18
|
-
|
|
27
|
+
const writeData = async (data, file) => {
|
|
28
|
+
const filePath = (0, path_1.join)(folder, fixFileName(file));
|
|
29
|
+
const mutex = getFileLock(filePath);
|
|
30
|
+
return mutex.acquire().then(async (release) => {
|
|
31
|
+
try {
|
|
32
|
+
await (0, promises_1.writeFile)(filePath, JSON.stringify(data, generics_1.BufferJSON.replacer));
|
|
33
|
+
}
|
|
34
|
+
finally {
|
|
35
|
+
release();
|
|
36
|
+
}
|
|
37
|
+
});
|
|
19
38
|
};
|
|
20
39
|
const readData = async (file) => {
|
|
21
40
|
try {
|
|
22
|
-
const
|
|
23
|
-
|
|
41
|
+
const filePath = (0, path_1.join)(folder, fixFileName(file));
|
|
42
|
+
const mutex = getFileLock(filePath);
|
|
43
|
+
return await mutex.acquire().then(async (release) => {
|
|
44
|
+
try {
|
|
45
|
+
const data = await (0, promises_1.readFile)(filePath, { encoding: 'utf-8' });
|
|
46
|
+
return JSON.parse(data, generics_1.BufferJSON.reviver);
|
|
47
|
+
}
|
|
48
|
+
finally {
|
|
49
|
+
release();
|
|
50
|
+
}
|
|
51
|
+
});
|
|
24
52
|
}
|
|
25
53
|
catch (error) {
|
|
26
54
|
return null;
|
|
@@ -28,7 +56,18 @@ const useMultiFileAuthState = async (folder) => {
|
|
|
28
56
|
};
|
|
29
57
|
const removeData = async (file) => {
|
|
30
58
|
try {
|
|
31
|
-
|
|
59
|
+
const filePath = (0, path_1.join)(folder, fixFileName(file));
|
|
60
|
+
const mutex = getFileLock(filePath);
|
|
61
|
+
return mutex.acquire().then(async (release) => {
|
|
62
|
+
try {
|
|
63
|
+
await (0, promises_1.unlink)(filePath);
|
|
64
|
+
}
|
|
65
|
+
catch (_a) {
|
|
66
|
+
}
|
|
67
|
+
finally {
|
|
68
|
+
release();
|
|
69
|
+
}
|
|
70
|
+
});
|
|
32
71
|
}
|
|
33
72
|
catch (_a) {
|
|
34
73
|
}
|
|
@@ -72,7 +111,7 @@ const useMultiFileAuthState = async (folder) => {
|
|
|
72
111
|
}
|
|
73
112
|
}
|
|
74
113
|
},
|
|
75
|
-
saveCreds: () => {
|
|
114
|
+
saveCreds: async () => {
|
|
76
115
|
return writeData(creds, 'creds.json');
|
|
77
116
|
}
|
|
78
117
|
};
|