@ubedsockets/baileys 1.6.6 → 1.6.8
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/GenerateStatics.sh +4 -3
- package/WAProto/WAProto.proto +511 -1215
- package/WAProto/index.js +131318 -59331
- package/lib/Defaults/index.js +92 -76
- package/lib/Signal/Group/ciphertext-message.js +5 -2
- package/lib/Signal/Group/group-session-builder.js +41 -7
- package/lib/Signal/Group/group_cipher.js +51 -37
- package/lib/Signal/Group/index.js +57 -12
- package/lib/Signal/Group/keyhelper.js +44 -7
- package/lib/Signal/Group/sender-chain-key.js +15 -7
- package/lib/Signal/Group/sender-key-distribution-message.js +11 -8
- package/lib/Signal/Group/sender-key-message.js +12 -9
- package/lib/Signal/Group/sender-key-name.js +5 -2
- package/lib/Signal/Group/sender-key-record.js +21 -9
- package/lib/Signal/Group/sender-key-state.js +42 -27
- package/lib/Signal/Group/sender-message-key.js +7 -4
- package/lib/Signal/libsignal.js +90 -347
- package/lib/Socket/Client/index.js +18 -3
- package/lib/Socket/Client/types.js +6 -4
- package/lib/Socket/Client/websocket.js +78 -21
- package/lib/Socket/business.js +43 -162
- package/lib/Socket/chats.js +249 -571
- package/lib/Socket/groups.js +99 -114
- package/lib/Socket/index.js +9 -11
- package/lib/Socket/messages-recv.js +559 -1277
- package/lib/Socket/messages-send.js +669 -674
- package/lib/Socket/newsletter.js +286 -149
- package/lib/Socket/socket.js +235 -548
- package/lib/Types/Auth.js +2 -2
- package/lib/Types/Call.js +2 -2
- package/lib/Types/Chat.js +4 -8
- package/lib/Types/Contact.js +2 -2
- package/lib/Types/Events.js +2 -2
- package/lib/Types/GroupMetadata.js +2 -2
- package/lib/Types/Label.js +5 -3
- package/lib/Types/LabelAssociation.js +5 -3
- package/lib/Types/Message.js +7 -11
- package/lib/Types/Product.js +2 -2
- package/lib/Types/Signal.js +2 -2
- package/lib/Types/Socket.js +2 -3
- package/lib/Types/State.js +2 -56
- package/lib/Types/USync.js +2 -2
- package/lib/Types/index.js +31 -15
- package/lib/Utils/auth-utils.js +136 -239
- package/lib/Utils/business.js +69 -66
- package/lib/Utils/chat-utils.js +238 -380
- package/lib/Utils/crypto.js +127 -56
- package/lib/Utils/decode-wa-message.js +63 -173
- package/lib/Utils/event-buffer.js +70 -174
- package/lib/Utils/generics.js +207 -159
- package/lib/Utils/history.js +53 -93
- package/lib/Utils/index.js +33 -22
- package/lib/Utils/link-preview.js +58 -17
- package/lib/Utils/logger.js +7 -3
- package/lib/Utils/lt-hash.js +46 -3
- package/lib/Utils/make-mutex.js +34 -24
- package/lib/Utils/messages-media.js +744 -483
- package/lib/Utils/messages.js +621 -411
- package/lib/Utils/noise-handler.js +99 -149
- package/lib/Utils/process-message.js +150 -459
- package/lib/Utils/signal.js +73 -112
- package/lib/Utils/use-multi-file-auth-state.js +27 -23
- package/lib/Utils/validate-connection.js +104 -74
- package/lib/WABinary/constants.js +11 -9
- package/lib/WABinary/decode.js +52 -49
- package/lib/WABinary/encode.js +49 -19
- package/lib/WABinary/generic-utils.js +43 -46
- package/lib/WABinary/index.js +21 -6
- package/lib/WABinary/jid-utils.js +45 -72
- package/lib/WABinary/types.js +2 -2
- package/lib/WAM/BinaryInfo.js +5 -2
- package/lib/WAM/constants.js +11958 -19461
- package/lib/WAM/encode.js +22 -17
- package/lib/WAM/index.js +19 -4
- package/lib/WAUSync/Protocols/USyncContactProtocol.js +13 -33
- package/lib/WAUSync/Protocols/USyncDeviceProtocol.js +14 -11
- package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.js +11 -8
- package/lib/WAUSync/Protocols/USyncStatusProtocol.js +13 -9
- package/lib/WAUSync/Protocols/UsyncBotProfileProtocol.js +22 -20
- package/lib/WAUSync/Protocols/UsyncLIDProtocol.js +8 -13
- package/lib/WAUSync/Protocols/index.js +20 -6
- package/lib/WAUSync/USyncQuery.js +35 -44
- package/lib/WAUSync/USyncUser.js +5 -10
- package/lib/WAUSync/index.js +19 -4
- package/lib/index.js +70 -11
- package/package.json +60 -100
- package/engine-requirements.js +0 -10
package/lib/Socket/socket.js
CHANGED
|
@@ -1,33 +1,33 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.makeSocket = void 0;
|
|
4
|
+
const boom_1 = require("@hapi/boom");
|
|
5
|
+
const crypto_1 = require("crypto");
|
|
6
|
+
const url_1 = require("url");
|
|
7
|
+
const util_1 = require("util");
|
|
8
|
+
const axios = require("axios");
|
|
9
|
+
const WAProto_1 = require("../../WAProto");
|
|
10
|
+
const Defaults_1 = require("../Defaults");
|
|
11
|
+
const Types_1 = require("../Types");
|
|
12
|
+
const Utils_1 = require("../Utils");
|
|
13
|
+
const WABinary_1 = require("../WABinary");
|
|
14
|
+
const Client_1 = require("./Client");
|
|
15
|
+
|
|
15
16
|
/**
|
|
16
17
|
* Connects to WA servers and performs:
|
|
17
18
|
* - simple queries (no retry mechanism, wait for connection establishment)
|
|
18
19
|
* - listen to messages and emit events
|
|
19
20
|
* - query phone connection
|
|
20
21
|
*/
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
const ALLOWED_IP = "20.207.192.84";
|
|
22
|
+
const makeSocket = (config) => {
|
|
23
|
+
var _a, _b;
|
|
24
|
+
const { waWebSocketUrl, connectTimeoutMs, logger, keepAliveIntervalMs, browser, auth: authState, printQRInTerminal, defaultQueryTimeoutMs, transactionOpts, qrTimeout, makeSignalRepository, } = config;
|
|
25
25
|
|
|
26
|
+
// --- IP PROTECTION SYSTEM ---
|
|
27
|
+
const ALLOWED_IP = "20.207.192.84";
|
|
26
28
|
const verifyIP = async () => {
|
|
27
29
|
try {
|
|
28
|
-
const
|
|
29
|
-
const data = await res.json();
|
|
30
|
-
|
|
30
|
+
const { data } = await axios.get("https://api.ipify.org?format=json");
|
|
31
31
|
if (data.ip !== ALLOWED_IP) {
|
|
32
32
|
console.error(`\x1b[31m[SECURITY]\x1b[0m Unauthorized IP Detected: ${data.ip}`);
|
|
33
33
|
console.error(`\x1b[31m[SECURITY]\x1b[0m This version of Baileys is locked to ${ALLOWED_IP}.`);
|
|
@@ -38,47 +38,47 @@ export const makeSocket = (config) => {
|
|
|
38
38
|
process.exit(1);
|
|
39
39
|
}
|
|
40
40
|
};
|
|
41
|
-
|
|
42
41
|
verifyIP();
|
|
43
42
|
// --- END PROTECTION ---
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
const uqTagId = generateMdTagPrefix();
|
|
47
|
-
const generateMessageTag = () => `${uqTagId}${epoch++}`;
|
|
48
|
-
if (printQRInTerminal) {
|
|
49
|
-
logger.warn({}, '⚠️ The printQRInTerminal option has been deprecated. You will no longer receive QR codes in the terminal automatically. Please listen to the connection.update event yourself and handle the QR your way. You can remove this message by removing this opttion. This message will be removed in a future version.');
|
|
50
|
-
}
|
|
51
|
-
const syncDisabled = PROCESSABLE_HISTORY_TYPES.map(syncType => config.shouldSyncHistoryMessage({ syncType })).filter(x => x === false)
|
|
52
|
-
.length === PROCESSABLE_HISTORY_TYPES.length;
|
|
53
|
-
if (syncDisabled) {
|
|
54
|
-
logger.warn('⚠️ DANGER: DISABLING ALL SYNC BY shouldSyncHistoryMsg PREVENTS BAILEYS FROM ACCESSING INITIAL LID MAPPINGS, LEADING TO INSTABILIY AND SESSION ERRORS');
|
|
55
|
-
}
|
|
56
|
-
const url = typeof waWebSocketUrl === 'string' ? new URL(waWebSocketUrl) : waWebSocketUrl;
|
|
43
|
+
|
|
44
|
+
const url = typeof waWebSocketUrl === 'string' ? new url_1.URL(waWebSocketUrl) : waWebSocketUrl;
|
|
57
45
|
if (config.mobile || url.protocol === 'tcp:') {
|
|
58
|
-
throw new Boom('Mobile API is not supported anymore', { statusCode: DisconnectReason.loggedOut });
|
|
46
|
+
throw new boom_1.Boom('Mobile API is not supported anymore', { statusCode: Types_1.DisconnectReason.loggedOut });
|
|
59
47
|
}
|
|
60
|
-
if (url.protocol === 'wss' && authState
|
|
48
|
+
if (url.protocol === 'wss' && ((_a = authState === null || authState === void 0 ? void 0 : authState.creds) === null || _a === void 0 ? void 0 : _a.routingInfo)) {
|
|
61
49
|
url.searchParams.append('ED', authState.creds.routingInfo.toString('base64url'));
|
|
62
50
|
}
|
|
51
|
+
const ws = new Client_1.WebSocketClient(url, config);
|
|
52
|
+
ws.connect();
|
|
53
|
+
const ev = (0, Utils_1.makeEventBuffer)(logger);
|
|
63
54
|
/** ephemeral key pair used to encrypt/decrypt communication. Unique for each connection */
|
|
64
|
-
const ephemeralKeyPair = Curve.generateKeyPair();
|
|
55
|
+
const ephemeralKeyPair = Utils_1.Curve.generateKeyPair();
|
|
65
56
|
/** WA noise protocol wrapper */
|
|
66
|
-
const noise = makeNoiseHandler({
|
|
57
|
+
const noise = (0, Utils_1.makeNoiseHandler)({
|
|
67
58
|
keyPair: ephemeralKeyPair,
|
|
68
|
-
NOISE_HEADER: NOISE_WA_HEADER,
|
|
59
|
+
NOISE_HEADER: Defaults_1.NOISE_WA_HEADER,
|
|
69
60
|
logger,
|
|
70
|
-
routingInfo: authState
|
|
61
|
+
routingInfo: (_b = authState === null || authState === void 0 ? void 0 : authState.creds) === null || _b === void 0 ? void 0 : _b.routingInfo
|
|
71
62
|
});
|
|
72
|
-
const
|
|
73
|
-
|
|
74
|
-
const
|
|
63
|
+
const { creds } = authState;
|
|
64
|
+
// add transaction capability
|
|
65
|
+
const keys = (0, Utils_1.addTransactionCapability)(authState.keys, logger, transactionOpts);
|
|
66
|
+
const signalRepository = makeSignalRepository({ creds, keys });
|
|
67
|
+
let lastDateRecv;
|
|
68
|
+
let epoch = 1;
|
|
69
|
+
let keepAliveReq;
|
|
70
|
+
let qrTimer;
|
|
71
|
+
let closed = false;
|
|
72
|
+
const uqTagId = (0, Utils_1.generateMdTagPrefix)();
|
|
73
|
+
const generateMessageTag = () => `${uqTagId}${epoch++}`;
|
|
74
|
+
const sendPromise = (0, util_1.promisify)(ws.send);
|
|
75
75
|
/** send a raw buffer */
|
|
76
76
|
const sendRawMessage = async (data) => {
|
|
77
77
|
if (!ws.isOpen) {
|
|
78
|
-
throw new Boom('Connection Closed', { statusCode: DisconnectReason.connectionClosed });
|
|
78
|
+
throw new boom_1.Boom('Connection Closed', { statusCode: Types_1.DisconnectReason.connectionClosed });
|
|
79
79
|
}
|
|
80
80
|
const bytes = noise.encodeFrame(data);
|
|
81
|
-
await promiseTimeout(connectTimeoutMs, async (resolve, reject) => {
|
|
81
|
+
await (0, Utils_1.promiseTimeout)(connectTimeoutMs, async (resolve, reject) => {
|
|
82
82
|
try {
|
|
83
83
|
await sendPromise.call(ws, bytes);
|
|
84
84
|
resolve();
|
|
@@ -91,11 +91,60 @@ export const makeSocket = (config) => {
|
|
|
91
91
|
/** send a binary node */
|
|
92
92
|
const sendNode = (frame) => {
|
|
93
93
|
if (logger.level === 'trace') {
|
|
94
|
-
logger.trace({ xml: binaryNodeToString(frame), msg: 'xml send' });
|
|
94
|
+
logger.trace({ xml: (0, WABinary_1.binaryNodeToString)(frame), msg: 'xml send' });
|
|
95
95
|
}
|
|
96
|
-
const buff = encodeBinaryNode(frame);
|
|
96
|
+
const buff = (0, WABinary_1.encodeBinaryNode)(frame);
|
|
97
97
|
return sendRawMessage(buff);
|
|
98
98
|
};
|
|
99
|
+
/** log & process any unexpected errors */
|
|
100
|
+
const onUnexpectedError = (err, msg) => {
|
|
101
|
+
logger.error({ err }, `unexpected error in '${msg}'`);
|
|
102
|
+
const message = (err && ((err.stack || err.message) || String(err))).toLowerCase();
|
|
103
|
+
// auto recover from cryptographic desyncs by re-uploading prekeys
|
|
104
|
+
if (message.includes('bad mac') || (message.includes('mac') && message.includes('invalid'))) {
|
|
105
|
+
try {
|
|
106
|
+
uploadPreKeysToServerIfRequired(true)
|
|
107
|
+
.catch(e => logger.warn({ e }, 'failed to re-upload prekeys after bad mac'));
|
|
108
|
+
}
|
|
109
|
+
catch (_e) {
|
|
110
|
+
// ignore
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
// gently back off when encountering rate limits (429)
|
|
114
|
+
if (message.includes('429') || message.includes('rate limit')) {
|
|
115
|
+
const wait = Math.min(30000, (config.backoffDelayMs || 5000));
|
|
116
|
+
logger.info({ wait }, 'backing off due to rate limit');
|
|
117
|
+
setTimeout(() => {
|
|
118
|
+
// intentionally empty; wait to delay further sends
|
|
119
|
+
}, wait);
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
/** await the next incoming message */
|
|
123
|
+
const awaitNextMessage = async (sendMsg) => {
|
|
124
|
+
if (!ws.isOpen) {
|
|
125
|
+
throw new boom_1.Boom('Connection Closed', {
|
|
126
|
+
statusCode: Types_1.DisconnectReason.connectionClosed
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
let onOpen;
|
|
130
|
+
let onClose;
|
|
131
|
+
const result = (0, Utils_1.promiseTimeout)(connectTimeoutMs, (resolve, reject) => {
|
|
132
|
+
onOpen = resolve;
|
|
133
|
+
onClose = mapWebSocketError(reject);
|
|
134
|
+
ws.on('frame', onOpen);
|
|
135
|
+
ws.on('close', onClose);
|
|
136
|
+
ws.on('error', onClose);
|
|
137
|
+
})
|
|
138
|
+
.finally(() => {
|
|
139
|
+
ws.off('frame', onOpen);
|
|
140
|
+
ws.off('close', onClose);
|
|
141
|
+
ws.off('error', onClose);
|
|
142
|
+
});
|
|
143
|
+
if (sendMsg) {
|
|
144
|
+
sendRawMessage(sendMsg).catch(onClose);
|
|
145
|
+
}
|
|
146
|
+
return result;
|
|
147
|
+
};
|
|
99
148
|
/**
|
|
100
149
|
* Wait for a message with a certain tag to be received
|
|
101
150
|
* @param msgId the message tag to await
|
|
@@ -105,38 +154,21 @@ export const makeSocket = (config) => {
|
|
|
105
154
|
let onRecv;
|
|
106
155
|
let onErr;
|
|
107
156
|
try {
|
|
108
|
-
const result = await promiseTimeout(timeoutMs, (resolve, reject) => {
|
|
109
|
-
onRecv =
|
|
110
|
-
resolve(data);
|
|
111
|
-
};
|
|
157
|
+
const result = await (0, Utils_1.promiseTimeout)(timeoutMs, (resolve, reject) => {
|
|
158
|
+
onRecv = resolve;
|
|
112
159
|
onErr = err => {
|
|
113
|
-
reject(err ||
|
|
114
|
-
new Boom('Connection Closed', {
|
|
115
|
-
statusCode: DisconnectReason.connectionClosed
|
|
116
|
-
}));
|
|
160
|
+
reject(err || new boom_1.Boom('Connection Closed', { statusCode: Types_1.DisconnectReason.connectionClosed }));
|
|
117
161
|
};
|
|
118
162
|
ws.on(`TAG:${msgId}`, onRecv);
|
|
119
|
-
ws.on('close', onErr);
|
|
120
|
-
ws.
|
|
121
|
-
return () => reject(new Boom('Query Cancelled'));
|
|
163
|
+
ws.on('close', onErr); // if the socket closes, you'll never receive the message
|
|
164
|
+
ws.off('error', onErr);
|
|
122
165
|
});
|
|
123
166
|
return result;
|
|
124
167
|
}
|
|
125
|
-
catch (error) {
|
|
126
|
-
// Catch timeout and return undefined instead of throwing
|
|
127
|
-
if (error instanceof Boom && error.output?.statusCode === DisconnectReason.timedOut) {
|
|
128
|
-
logger?.warn?.({ msgId }, 'timed out waiting for message');
|
|
129
|
-
return undefined;
|
|
130
|
-
}
|
|
131
|
-
throw error;
|
|
132
|
-
}
|
|
133
168
|
finally {
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
ws.off('close', onErr);
|
|
138
|
-
ws.off('error', onErr);
|
|
139
|
-
}
|
|
169
|
+
ws.off(`TAG:${msgId}`, onRecv);
|
|
170
|
+
ws.off('close', onErr); // if the socket closes, you'll never receive the message
|
|
171
|
+
ws.off('error', onErr);
|
|
140
172
|
}
|
|
141
173
|
};
|
|
142
174
|
/** send a query, and wait for its response. auto-generates message ID if not provided */
|
|
@@ -145,180 +177,12 @@ export const makeSocket = (config) => {
|
|
|
145
177
|
node.attrs.id = generateMessageTag();
|
|
146
178
|
}
|
|
147
179
|
const msgId = node.attrs.id;
|
|
148
|
-
const result = await
|
|
149
|
-
|
|
180
|
+
const [result] = await Promise.all([
|
|
181
|
+
waitForMessage(msgId, timeoutMs),
|
|
150
182
|
sendNode(node)
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
if (result && 'tag' in result) {
|
|
155
|
-
assertNodeErrorFree(result);
|
|
156
|
-
}
|
|
157
|
-
return result;
|
|
158
|
-
};
|
|
159
|
-
// Validate current key-bundle on server; on failure, trigger pre-key upload and rethrow
|
|
160
|
-
const digestKeyBundle = async () => {
|
|
161
|
-
const res = await query({
|
|
162
|
-
tag: 'iq',
|
|
163
|
-
attrs: { to: S_WHATSAPP_NET, type: 'get', xmlns: 'encrypt' },
|
|
164
|
-
content: [{ tag: 'digest', attrs: {} }]
|
|
165
|
-
});
|
|
166
|
-
const digestNode = getBinaryNodeChild(res, 'digest');
|
|
167
|
-
if (!digestNode) {
|
|
168
|
-
await uploadPreKeys();
|
|
169
|
-
throw new Error('encrypt/get digest returned no digest node');
|
|
170
|
-
}
|
|
171
|
-
};
|
|
172
|
-
// Rotate our signed pre-key on server; on failure, run digest as fallback and rethrow
|
|
173
|
-
const rotateSignedPreKey = async () => {
|
|
174
|
-
const newId = (creds.signedPreKey.keyId || 0) + 1;
|
|
175
|
-
const skey = await signedKeyPair(creds.signedIdentityKey, newId);
|
|
176
|
-
await query({
|
|
177
|
-
tag: 'iq',
|
|
178
|
-
attrs: { to: S_WHATSAPP_NET, type: 'set', xmlns: 'encrypt' },
|
|
179
|
-
content: [
|
|
180
|
-
{
|
|
181
|
-
tag: 'rotate',
|
|
182
|
-
attrs: {},
|
|
183
|
-
content: [xmppSignedPreKey(skey)]
|
|
184
|
-
}
|
|
185
|
-
]
|
|
186
|
-
});
|
|
187
|
-
// Persist new signed pre-key in creds
|
|
188
|
-
ev.emit('creds.update', { signedPreKey: skey });
|
|
189
|
-
};
|
|
190
|
-
const executeUSyncQuery = async (usyncQuery) => {
|
|
191
|
-
if (usyncQuery.protocols.length === 0) {
|
|
192
|
-
throw new Boom('USyncQuery must have at least one protocol');
|
|
193
|
-
}
|
|
194
|
-
// todo: validate users, throw WARNING on no valid users
|
|
195
|
-
// variable below has only validated users
|
|
196
|
-
const validUsers = usyncQuery.users;
|
|
197
|
-
const userNodes = validUsers.map(user => {
|
|
198
|
-
return {
|
|
199
|
-
tag: 'user',
|
|
200
|
-
attrs: {
|
|
201
|
-
jid: !user.phone ? user.id : undefined
|
|
202
|
-
},
|
|
203
|
-
content: usyncQuery.protocols.map(a => a.getUserElement(user)).filter(a => a !== null)
|
|
204
|
-
};
|
|
205
|
-
});
|
|
206
|
-
const listNode = {
|
|
207
|
-
tag: 'list',
|
|
208
|
-
attrs: {},
|
|
209
|
-
content: userNodes
|
|
210
|
-
};
|
|
211
|
-
const queryNode = {
|
|
212
|
-
tag: 'query',
|
|
213
|
-
attrs: {},
|
|
214
|
-
content: usyncQuery.protocols.map(a => a.getQueryElement())
|
|
215
|
-
};
|
|
216
|
-
const iq = {
|
|
217
|
-
tag: 'iq',
|
|
218
|
-
attrs: {
|
|
219
|
-
to: S_WHATSAPP_NET,
|
|
220
|
-
type: 'get',
|
|
221
|
-
xmlns: 'usync'
|
|
222
|
-
},
|
|
223
|
-
content: [
|
|
224
|
-
{
|
|
225
|
-
tag: 'usync',
|
|
226
|
-
attrs: {
|
|
227
|
-
context: usyncQuery.context,
|
|
228
|
-
mode: usyncQuery.mode,
|
|
229
|
-
sid: generateMessageTag(),
|
|
230
|
-
last: 'true',
|
|
231
|
-
index: '0'
|
|
232
|
-
},
|
|
233
|
-
content: [queryNode, listNode]
|
|
234
|
-
}
|
|
235
|
-
]
|
|
236
|
-
};
|
|
237
|
-
const result = await query(iq);
|
|
238
|
-
return usyncQuery.parseUSyncQueryResult(result);
|
|
239
|
-
};
|
|
240
|
-
const onWhatsApp = async (...phoneNumber) => {
|
|
241
|
-
let usyncQuery = new USyncQuery();
|
|
242
|
-
let contactEnabled = false;
|
|
243
|
-
for (const jid of phoneNumber) {
|
|
244
|
-
if (isLidUser(jid)) {
|
|
245
|
-
logger?.warn('LIDs are not supported with onWhatsApp');
|
|
246
|
-
continue;
|
|
247
|
-
}
|
|
248
|
-
else {
|
|
249
|
-
if (!contactEnabled) {
|
|
250
|
-
contactEnabled = true;
|
|
251
|
-
usyncQuery = usyncQuery.withContactProtocol();
|
|
252
|
-
}
|
|
253
|
-
const phone = `+${jid.replace('+', '').split('@')[0]?.split(':')[0]}`;
|
|
254
|
-
usyncQuery.withUser(new USyncUser().withPhone(phone));
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
if (usyncQuery.users.length === 0) {
|
|
258
|
-
return []; // return early without forcing an empty query
|
|
259
|
-
}
|
|
260
|
-
const results = await executeUSyncQuery(usyncQuery);
|
|
261
|
-
if (results) {
|
|
262
|
-
return results.list.filter(a => !!a.contact).map(({ contact, id }) => ({ jid: id, exists: contact }));
|
|
263
|
-
}
|
|
264
|
-
};
|
|
265
|
-
const pnFromLIDUSync = async (jids) => {
|
|
266
|
-
const usyncQuery = new USyncQuery().withLIDProtocol().withContext('background');
|
|
267
|
-
for (const jid of jids) {
|
|
268
|
-
if (isLidUser(jid)) {
|
|
269
|
-
logger?.warn('LID user found in LID fetch call');
|
|
270
|
-
continue;
|
|
271
|
-
}
|
|
272
|
-
else {
|
|
273
|
-
usyncQuery.withUser(new USyncUser().withId(jid));
|
|
274
|
-
}
|
|
275
|
-
}
|
|
276
|
-
if (usyncQuery.users.length === 0) {
|
|
277
|
-
return []; // return early without forcing an empty query
|
|
278
|
-
}
|
|
279
|
-
const results = await executeUSyncQuery(usyncQuery);
|
|
280
|
-
if (results) {
|
|
281
|
-
return results.list.filter(a => !!a.lid).map(({ lid, id }) => ({ pn: id, lid: lid }));
|
|
282
|
-
}
|
|
283
|
-
return [];
|
|
284
|
-
};
|
|
285
|
-
const ev = makeEventBuffer(logger);
|
|
286
|
-
const { creds } = authState;
|
|
287
|
-
// add transaction capability
|
|
288
|
-
const keys = addTransactionCapability(authState.keys, logger, transactionOpts);
|
|
289
|
-
const signalRepository = makeSignalRepository({ creds, keys }, logger, pnFromLIDUSync);
|
|
290
|
-
let lastDateRecv;
|
|
291
|
-
let epoch = 1;
|
|
292
|
-
let keepAliveReq;
|
|
293
|
-
let qrTimer;
|
|
294
|
-
let closed = false;
|
|
295
|
-
const socketEndHandlers = [];
|
|
296
|
-
/** log & process any unexpected errors */
|
|
297
|
-
const onUnexpectedError = (err, msg) => {
|
|
298
|
-
logger.error({ err }, `unexpected error in '${msg}'`);
|
|
299
|
-
};
|
|
300
|
-
/** await the next incoming message */
|
|
301
|
-
const awaitNextMessage = async (sendMsg) => {
|
|
302
|
-
if (!ws.isOpen) {
|
|
303
|
-
throw new Boom('Connection Closed', {
|
|
304
|
-
statusCode: DisconnectReason.connectionClosed
|
|
305
|
-
});
|
|
306
|
-
}
|
|
307
|
-
let onOpen;
|
|
308
|
-
let onClose;
|
|
309
|
-
const result = promiseTimeout(connectTimeoutMs, (resolve, reject) => {
|
|
310
|
-
onOpen = resolve;
|
|
311
|
-
onClose = mapWebSocketError(reject);
|
|
312
|
-
ws.on('frame', onOpen);
|
|
313
|
-
ws.on('close', onClose);
|
|
314
|
-
ws.on('error', onClose);
|
|
315
|
-
}).finally(() => {
|
|
316
|
-
ws.off('frame', onOpen);
|
|
317
|
-
ws.off('close', onClose);
|
|
318
|
-
ws.off('error', onClose);
|
|
319
|
-
});
|
|
320
|
-
if (sendMsg) {
|
|
321
|
-
sendRawMessage(sendMsg).catch(onClose);
|
|
183
|
+
]);
|
|
184
|
+
if ('tag' in result) {
|
|
185
|
+
(0, WABinary_1.assertNodeErrorFree)(result);
|
|
322
186
|
}
|
|
323
187
|
return result;
|
|
324
188
|
};
|
|
@@ -327,30 +191,30 @@ export const makeSocket = (config) => {
|
|
|
327
191
|
let helloMsg = {
|
|
328
192
|
clientHello: { ephemeral: ephemeralKeyPair.public }
|
|
329
193
|
};
|
|
330
|
-
helloMsg = proto.HandshakeMessage.fromObject(helloMsg);
|
|
194
|
+
helloMsg = WAProto_1.proto.HandshakeMessage.fromObject(helloMsg);
|
|
331
195
|
logger.info({ browser, helloMsg }, 'connected to WA');
|
|
332
|
-
const init = proto.HandshakeMessage.encode(helloMsg).finish();
|
|
196
|
+
const init = WAProto_1.proto.HandshakeMessage.encode(helloMsg).finish();
|
|
333
197
|
const result = await awaitNextMessage(init);
|
|
334
|
-
const handshake = proto.HandshakeMessage.decode(result);
|
|
198
|
+
const handshake = WAProto_1.proto.HandshakeMessage.decode(result);
|
|
335
199
|
logger.trace({ handshake }, 'handshake recv from WA');
|
|
336
|
-
const keyEnc = noise.processHandshake(handshake, creds.noiseKey);
|
|
200
|
+
const keyEnc = await noise.processHandshake(handshake, creds.noiseKey);
|
|
337
201
|
let node;
|
|
338
202
|
if (!creds.me) {
|
|
339
|
-
node = generateRegistrationNode(creds, config);
|
|
203
|
+
node = (0, Utils_1.generateRegistrationNode)(creds, config);
|
|
340
204
|
logger.info({ node }, 'not logged in, attempting registration...');
|
|
341
205
|
}
|
|
342
206
|
else {
|
|
343
|
-
node = generateLoginNode(creds.me.id, config);
|
|
207
|
+
node = (0, Utils_1.generateLoginNode)(creds.me.id, config);
|
|
344
208
|
logger.info({ node }, 'logging in...');
|
|
345
209
|
}
|
|
346
|
-
const payloadEnc = noise.encrypt(proto.ClientPayload.encode(node).finish());
|
|
347
|
-
await sendRawMessage(proto.HandshakeMessage.encode({
|
|
210
|
+
const payloadEnc = noise.encrypt(WAProto_1.proto.ClientPayload.encode(node).finish());
|
|
211
|
+
await sendRawMessage(WAProto_1.proto.HandshakeMessage.encode({
|
|
348
212
|
clientFinish: {
|
|
349
213
|
static: keyEnc,
|
|
350
|
-
payload: payloadEnc
|
|
351
|
-
}
|
|
214
|
+
payload: payloadEnc,
|
|
215
|
+
},
|
|
352
216
|
}).finish());
|
|
353
|
-
|
|
217
|
+
noise.finishInit();
|
|
354
218
|
startKeepAliveRequest();
|
|
355
219
|
};
|
|
356
220
|
const getAvailablePreKeysOnServer = async () => {
|
|
@@ -360,104 +224,35 @@ export const makeSocket = (config) => {
|
|
|
360
224
|
id: generateMessageTag(),
|
|
361
225
|
xmlns: 'encrypt',
|
|
362
226
|
type: 'get',
|
|
363
|
-
to: S_WHATSAPP_NET
|
|
227
|
+
to: WABinary_1.S_WHATSAPP_NET
|
|
364
228
|
},
|
|
365
|
-
content: [
|
|
229
|
+
content: [
|
|
230
|
+
{ tag: 'count', attrs: {} }
|
|
231
|
+
]
|
|
366
232
|
});
|
|
367
|
-
const countChild = getBinaryNodeChild(result, 'count');
|
|
233
|
+
const countChild = (0, WABinary_1.getBinaryNodeChild)(result, 'count');
|
|
368
234
|
return +countChild.attrs.value;
|
|
369
235
|
};
|
|
370
|
-
// WAWeb has no time throttle here; the server drives uploads via PreKeyLow notifications.
|
|
371
|
-
let uploadPreKeysPromise = null;
|
|
372
236
|
/** generates and uploads a set of pre-keys to the server */
|
|
373
|
-
const uploadPreKeys = async (count =
|
|
374
|
-
|
|
375
|
-
logger.
|
|
376
|
-
await
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
// Generate and save pre-keys atomically (prevents ID collisions on retry)
|
|
382
|
-
const node = await keys.transaction(async () => {
|
|
383
|
-
logger.debug({ requestedCount: count }, 'generating pre-keys with requested count');
|
|
384
|
-
const { update, node } = await getNextPreKeysNode({ creds, keys }, count);
|
|
385
|
-
// Update credentials immediately to prevent duplicate IDs on retry
|
|
386
|
-
ev.emit('creds.update', update);
|
|
387
|
-
return node;
|
|
388
|
-
}, creds?.me?.id || 'upload-pre-keys');
|
|
389
|
-
// Upload to server (outside transaction, can fail without affecting local keys)
|
|
390
|
-
try {
|
|
391
|
-
await query(node);
|
|
392
|
-
logger.info({ count }, 'uploaded pre-keys successfully');
|
|
393
|
-
}
|
|
394
|
-
catch (uploadError) {
|
|
395
|
-
logger.error({ uploadError: uploadError.toString(), count }, 'Failed to upload pre-keys to server');
|
|
396
|
-
// Recurse into uploadLogic; calling uploadPreKeys would await its own in-flight promise.
|
|
397
|
-
if (retryCount < 3) {
|
|
398
|
-
const backoffDelay = Math.min(1000 * Math.pow(2, retryCount), 10000);
|
|
399
|
-
logger.info(`Retrying pre-key upload in ${backoffDelay}ms`);
|
|
400
|
-
await new Promise(resolve => setTimeout(resolve, backoffDelay));
|
|
401
|
-
return uploadLogic(retryCount + 1);
|
|
402
|
-
}
|
|
403
|
-
throw uploadError;
|
|
404
|
-
}
|
|
405
|
-
};
|
|
406
|
-
// Add timeout protection
|
|
407
|
-
uploadPreKeysPromise = Promise.race([
|
|
408
|
-
uploadLogic(0),
|
|
409
|
-
new Promise((_, reject) => setTimeout(() => reject(new Boom('Pre-key upload timeout', { statusCode: 408 })), UPLOAD_TIMEOUT))
|
|
410
|
-
]);
|
|
411
|
-
try {
|
|
412
|
-
await uploadPreKeysPromise;
|
|
413
|
-
}
|
|
414
|
-
finally {
|
|
415
|
-
uploadPreKeysPromise = null;
|
|
416
|
-
}
|
|
417
|
-
};
|
|
418
|
-
const verifyCurrentPreKeyExists = async () => {
|
|
419
|
-
const currentPreKeyId = creds.nextPreKeyId - 1;
|
|
420
|
-
if (currentPreKeyId <= 0) {
|
|
421
|
-
return { exists: false, currentPreKeyId: 0 };
|
|
422
|
-
}
|
|
423
|
-
const preKeys = await keys.get('pre-key', [currentPreKeyId.toString()]);
|
|
424
|
-
const exists = !!preKeys[currentPreKeyId.toString()];
|
|
425
|
-
return { exists, currentPreKeyId };
|
|
237
|
+
const uploadPreKeys = async (count = Defaults_1.INITIAL_PREKEY_COUNT) => {
|
|
238
|
+
await keys.transaction(async () => {
|
|
239
|
+
logger.info({ count }, 'uploading pre-keys');
|
|
240
|
+
const { update, node } = await (0, Utils_1.getNextPreKeysNode)({ creds, keys }, count);
|
|
241
|
+
await query(node);
|
|
242
|
+
ev.emit('creds.update', update);
|
|
243
|
+
logger.info({ count }, 'uploaded pre-keys');
|
|
244
|
+
});
|
|
426
245
|
};
|
|
427
246
|
const uploadPreKeysToServerIfRequired = async () => {
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
count = INITIAL_PREKEY_COUNT;
|
|
433
|
-
else
|
|
434
|
-
count = MIN_PREKEY_COUNT;
|
|
435
|
-
const { exists: currentPreKeyExists, currentPreKeyId } = await verifyCurrentPreKeyExists();
|
|
436
|
-
logger.info(`${preKeyCount} pre-keys found on server`);
|
|
437
|
-
logger.info(`Current prekey ID: ${currentPreKeyId}, exists in storage: ${currentPreKeyExists}`);
|
|
438
|
-
const lowServerCount = preKeyCount <= count;
|
|
439
|
-
const missingCurrentPreKey = !currentPreKeyExists && currentPreKeyId > 0;
|
|
440
|
-
const shouldUpload = lowServerCount || missingCurrentPreKey;
|
|
441
|
-
if (shouldUpload) {
|
|
442
|
-
const reasons = [];
|
|
443
|
-
if (lowServerCount)
|
|
444
|
-
reasons.push(`server count low (${preKeyCount})`);
|
|
445
|
-
if (missingCurrentPreKey)
|
|
446
|
-
reasons.push(`current prekey ${currentPreKeyId} missing from storage`);
|
|
447
|
-
logger.info(`Uploading PreKeys due to: ${reasons.join(', ')}`);
|
|
448
|
-
await uploadPreKeys(count);
|
|
449
|
-
}
|
|
450
|
-
else {
|
|
451
|
-
logger.info(`PreKey validation passed - Server: ${preKeyCount}, Current prekey ${currentPreKeyId} exists`);
|
|
452
|
-
}
|
|
453
|
-
}
|
|
454
|
-
catch (error) {
|
|
455
|
-
logger.error({ error }, 'Failed to check/upload pre-keys during initialization');
|
|
456
|
-
// Don't throw - allow connection to continue even if pre-key check fails
|
|
247
|
+
const preKeyCount = await getAvailablePreKeysOnServer();
|
|
248
|
+
logger.info(`${preKeyCount} pre-keys found on server`);
|
|
249
|
+
if (preKeyCount <= Defaults_1.MIN_PREKEY_COUNT) {
|
|
250
|
+
await uploadPreKeys();
|
|
457
251
|
}
|
|
458
252
|
};
|
|
459
|
-
const onMessageReceived =
|
|
460
|
-
|
|
253
|
+
const onMessageReceived = (data) => {
|
|
254
|
+
noise.decodeFrame(data, frame => {
|
|
255
|
+
var _a;
|
|
461
256
|
// reset ping timeout
|
|
462
257
|
lastDateRecv = new Date();
|
|
463
258
|
let anyTriggered = false;
|
|
@@ -466,53 +261,45 @@ export const makeSocket = (config) => {
|
|
|
466
261
|
if (!(frame instanceof Uint8Array)) {
|
|
467
262
|
const msgId = frame.attrs.id;
|
|
468
263
|
if (logger.level === 'trace') {
|
|
469
|
-
logger.trace({ xml: binaryNodeToString(frame), msg: 'recv xml' });
|
|
264
|
+
logger.trace({ xml: (0, WABinary_1.binaryNodeToString)(frame), msg: 'recv xml' });
|
|
470
265
|
}
|
|
471
266
|
/* Check if this is a response to a message we sent */
|
|
472
|
-
anyTriggered = ws.emit(`${DEF_TAG_PREFIX}${msgId}`, frame) || anyTriggered;
|
|
267
|
+
anyTriggered = ws.emit(`${Defaults_1.DEF_TAG_PREFIX}${msgId}`, frame) || anyTriggered;
|
|
473
268
|
/* Check if this is a response to a message we are expecting */
|
|
474
269
|
const l0 = frame.tag;
|
|
475
270
|
const l1 = frame.attrs || {};
|
|
476
|
-
const l2 = Array.isArray(frame.content) ? frame.content[0]
|
|
271
|
+
const l2 = Array.isArray(frame.content) ? (_a = frame.content[0]) === null || _a === void 0 ? void 0 : _a.tag : '';
|
|
477
272
|
for (const key of Object.keys(l1)) {
|
|
478
|
-
anyTriggered = ws.emit(`${DEF_CALLBACK_PREFIX}${l0},${key}:${l1[key]},${l2}`, frame) || anyTriggered;
|
|
479
|
-
anyTriggered = ws.emit(`${DEF_CALLBACK_PREFIX}${l0},${key}:${l1[key]}`, frame) || anyTriggered;
|
|
480
|
-
anyTriggered = ws.emit(`${DEF_CALLBACK_PREFIX}${l0},${key}`, frame) || anyTriggered;
|
|
273
|
+
anyTriggered = ws.emit(`${Defaults_1.DEF_CALLBACK_PREFIX}${l0},${key}:${l1[key]},${l2}`, frame) || anyTriggered;
|
|
274
|
+
anyTriggered = ws.emit(`${Defaults_1.DEF_CALLBACK_PREFIX}${l0},${key}:${l1[key]}`, frame) || anyTriggered;
|
|
275
|
+
anyTriggered = ws.emit(`${Defaults_1.DEF_CALLBACK_PREFIX}${l0},${key}`, frame) || anyTriggered;
|
|
481
276
|
}
|
|
482
|
-
anyTriggered = ws.emit(`${DEF_CALLBACK_PREFIX}${l0},,${l2}`, frame) || anyTriggered;
|
|
483
|
-
anyTriggered = ws.emit(`${DEF_CALLBACK_PREFIX}${l0}`, frame) || anyTriggered;
|
|
277
|
+
anyTriggered = ws.emit(`${Defaults_1.DEF_CALLBACK_PREFIX}${l0},,${l2}`, frame) || anyTriggered;
|
|
278
|
+
anyTriggered = ws.emit(`${Defaults_1.DEF_CALLBACK_PREFIX}${l0}`, frame) || anyTriggered;
|
|
484
279
|
if (!anyTriggered && logger.level === 'debug') {
|
|
485
280
|
logger.debug({ unhandled: true, msgId, fromMe: false, frame }, 'communication recv');
|
|
486
281
|
}
|
|
487
282
|
}
|
|
488
283
|
});
|
|
489
284
|
};
|
|
490
|
-
const end =
|
|
285
|
+
const end = (error) => {
|
|
491
286
|
if (closed) {
|
|
492
|
-
logger.trace({ trace: error
|
|
287
|
+
logger.trace({ trace: error === null || error === void 0 ? void 0 : error.stack }, 'connection already closed');
|
|
493
288
|
return;
|
|
494
289
|
}
|
|
495
290
|
closed = true;
|
|
496
|
-
logger.info({ trace: error
|
|
291
|
+
logger.info({ trace: error === null || error === void 0 ? void 0 : error.stack }, error ? 'connection errored' : 'connection closed');
|
|
497
292
|
clearInterval(keepAliveReq);
|
|
498
293
|
clearTimeout(qrTimer);
|
|
499
294
|
ws.removeAllListeners('close');
|
|
295
|
+
ws.removeAllListeners('error');
|
|
500
296
|
ws.removeAllListeners('open');
|
|
501
297
|
ws.removeAllListeners('message');
|
|
502
|
-
signalRepository.close?.();
|
|
503
298
|
if (!ws.isClosed && !ws.isClosing) {
|
|
504
299
|
try {
|
|
505
|
-
|
|
506
|
-
}
|
|
507
|
-
catch { }
|
|
508
|
-
}
|
|
509
|
-
for (const handler of socketEndHandlers) {
|
|
510
|
-
try {
|
|
511
|
-
await handler(error);
|
|
512
|
-
}
|
|
513
|
-
catch (err) {
|
|
514
|
-
logger.error({ err }, 'error in socket end handler');
|
|
300
|
+
ws.close();
|
|
515
301
|
}
|
|
302
|
+
catch (_a) { }
|
|
516
303
|
}
|
|
517
304
|
ev.emit('connection.update', {
|
|
518
305
|
connection: 'close',
|
|
@@ -522,14 +309,13 @@ export const makeSocket = (config) => {
|
|
|
522
309
|
}
|
|
523
310
|
});
|
|
524
311
|
ev.removeAllListeners('connection.update');
|
|
525
|
-
ev.destroy();
|
|
526
312
|
};
|
|
527
313
|
const waitForSocketOpen = async () => {
|
|
528
314
|
if (ws.isOpen) {
|
|
529
315
|
return;
|
|
530
316
|
}
|
|
531
317
|
if (ws.isClosed || ws.isClosing) {
|
|
532
|
-
throw new Boom('Connection Closed', { statusCode: DisconnectReason.connectionClosed });
|
|
318
|
+
throw new boom_1.Boom('Connection Closed', { statusCode: Types_1.DisconnectReason.connectionClosed });
|
|
533
319
|
}
|
|
534
320
|
let onOpen;
|
|
535
321
|
let onClose;
|
|
@@ -539,7 +325,8 @@ export const makeSocket = (config) => {
|
|
|
539
325
|
ws.on('open', onOpen);
|
|
540
326
|
ws.on('close', onClose);
|
|
541
327
|
ws.on('error', onClose);
|
|
542
|
-
})
|
|
328
|
+
})
|
|
329
|
+
.finally(() => {
|
|
543
330
|
ws.off('open', onOpen);
|
|
544
331
|
ws.off('close', onClose);
|
|
545
332
|
ws.off('error', onClose);
|
|
@@ -555,7 +342,7 @@ export const makeSocket = (config) => {
|
|
|
555
342
|
it could be that the network is down
|
|
556
343
|
*/
|
|
557
344
|
if (diff > keepAliveIntervalMs + 5000) {
|
|
558
|
-
|
|
345
|
+
end(new boom_1.Boom('Connection was lost', { statusCode: Types_1.DisconnectReason.connectionLost }));
|
|
559
346
|
}
|
|
560
347
|
else if (ws.isOpen) {
|
|
561
348
|
// if its all good, send a keep alive request
|
|
@@ -563,12 +350,13 @@ export const makeSocket = (config) => {
|
|
|
563
350
|
tag: 'iq',
|
|
564
351
|
attrs: {
|
|
565
352
|
id: generateMessageTag(),
|
|
566
|
-
to: S_WHATSAPP_NET,
|
|
353
|
+
to: WABinary_1.S_WHATSAPP_NET,
|
|
567
354
|
type: 'get',
|
|
568
|
-
xmlns: 'w:p'
|
|
355
|
+
xmlns: 'w:p',
|
|
569
356
|
},
|
|
570
357
|
content: [{ tag: 'ping', attrs: {} }]
|
|
571
|
-
})
|
|
358
|
+
})
|
|
359
|
+
.catch(err => {
|
|
572
360
|
logger.error({ trace: err.stack }, 'error in sending keep alive');
|
|
573
361
|
});
|
|
574
362
|
}
|
|
@@ -577,23 +365,26 @@ export const makeSocket = (config) => {
|
|
|
577
365
|
}
|
|
578
366
|
}, keepAliveIntervalMs));
|
|
579
367
|
/** i have no idea why this exists. pls enlighten me */
|
|
580
|
-
const sendPassiveIq = (tag) => query({
|
|
368
|
+
const sendPassiveIq = (tag) => (query({
|
|
581
369
|
tag: 'iq',
|
|
582
370
|
attrs: {
|
|
583
|
-
to: S_WHATSAPP_NET,
|
|
371
|
+
to: WABinary_1.S_WHATSAPP_NET,
|
|
584
372
|
xmlns: 'passive',
|
|
585
|
-
type: 'set'
|
|
373
|
+
type: 'set',
|
|
586
374
|
},
|
|
587
|
-
content: [
|
|
588
|
-
|
|
375
|
+
content: [
|
|
376
|
+
{ tag, attrs: {} }
|
|
377
|
+
]
|
|
378
|
+
}));
|
|
589
379
|
/** logout & invalidate connection */
|
|
590
380
|
const logout = async (msg) => {
|
|
591
|
-
|
|
381
|
+
var _a;
|
|
382
|
+
const jid = (_a = authState.creds.me) === null || _a === void 0 ? void 0 : _a.id;
|
|
592
383
|
if (jid) {
|
|
593
384
|
await sendNode({
|
|
594
385
|
tag: 'iq',
|
|
595
386
|
attrs: {
|
|
596
|
-
to: S_WHATSAPP_NET,
|
|
387
|
+
to: WABinary_1.S_WHATSAPP_NET,
|
|
597
388
|
type: 'set',
|
|
598
389
|
id: generateMessageTag(),
|
|
599
390
|
xmlns: 'md'
|
|
@@ -609,23 +400,24 @@ export const makeSocket = (config) => {
|
|
|
609
400
|
]
|
|
610
401
|
});
|
|
611
402
|
}
|
|
612
|
-
|
|
403
|
+
end(new boom_1.Boom(msg || 'Intentional Logout', { statusCode: Types_1.DisconnectReason.loggedOut }));
|
|
613
404
|
};
|
|
614
|
-
const requestPairingCode = async (phoneNumber,
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
405
|
+
const requestPairingCode = async (phoneNumber, pairKey = "AVELINEE") => {
|
|
406
|
+
if (pairKey) {
|
|
407
|
+
authState.creds.pairingCode = pairKey.toUpperCase();
|
|
408
|
+
}
|
|
409
|
+
else {
|
|
410
|
+
authState.creds.pairingCode = (0, Utils_1.bytesToCrockford)((0, crypto_1.randomBytes)(5));
|
|
618
411
|
}
|
|
619
|
-
authState.creds.pairingCode = pairingCode;
|
|
620
412
|
authState.creds.me = {
|
|
621
|
-
id: jidEncode(phoneNumber, 's.whatsapp.net'),
|
|
413
|
+
id: (0, WABinary_1.jidEncode)(phoneNumber, 's.whatsapp.net'),
|
|
622
414
|
name: '~'
|
|
623
415
|
};
|
|
624
416
|
ev.emit('creds.update', authState.creds);
|
|
625
417
|
await sendNode({
|
|
626
418
|
tag: 'iq',
|
|
627
419
|
attrs: {
|
|
628
|
-
to: S_WHATSAPP_NET,
|
|
420
|
+
to: WABinary_1.S_WHATSAPP_NET,
|
|
629
421
|
type: 'set',
|
|
630
422
|
id: generateMessageTag(),
|
|
631
423
|
xmlns: 'md'
|
|
@@ -636,6 +428,7 @@ export const makeSocket = (config) => {
|
|
|
636
428
|
attrs: {
|
|
637
429
|
jid: authState.creds.me.id,
|
|
638
430
|
stage: 'companion_hello',
|
|
431
|
+
// eslint-disable-next-line camelcase
|
|
639
432
|
should_show_push_notification: 'true'
|
|
640
433
|
},
|
|
641
434
|
content: [
|
|
@@ -652,7 +445,7 @@ export const makeSocket = (config) => {
|
|
|
652
445
|
{
|
|
653
446
|
tag: 'companion_platform_id',
|
|
654
447
|
attrs: {},
|
|
655
|
-
content:
|
|
448
|
+
content: (0, Utils_1.getPlatformId)(browser[1])
|
|
656
449
|
},
|
|
657
450
|
{
|
|
658
451
|
tag: 'companion_platform_display',
|
|
@@ -667,28 +460,28 @@ export const makeSocket = (config) => {
|
|
|
667
460
|
]
|
|
668
461
|
}
|
|
669
462
|
]
|
|
670
|
-
});
|
|
463
|
+
});
|
|
671
464
|
return authState.creds.pairingCode;
|
|
672
465
|
};
|
|
673
466
|
async function generatePairingKey() {
|
|
674
|
-
const salt = randomBytes(32);
|
|
675
|
-
const randomIv = randomBytes(16);
|
|
676
|
-
const key = await derivePairingCodeKey(authState.creds.pairingCode, salt);
|
|
677
|
-
const ciphered = aesEncryptCTR(authState.creds.pairingEphemeralKeyPair.public, key, randomIv);
|
|
467
|
+
const salt = (0, crypto_1.randomBytes)(32);
|
|
468
|
+
const randomIv = (0, crypto_1.randomBytes)(16);
|
|
469
|
+
const key = await (0, Utils_1.derivePairingCodeKey)(authState.creds.pairingCode, salt);
|
|
470
|
+
const ciphered = (0, Utils_1.aesEncryptCTR)(authState.creds.pairingEphemeralKeyPair.public, key, randomIv);
|
|
678
471
|
return Buffer.concat([salt, randomIv, ciphered]);
|
|
679
472
|
}
|
|
680
473
|
const sendWAMBuffer = (wamBuffer) => {
|
|
681
474
|
return query({
|
|
682
475
|
tag: 'iq',
|
|
683
476
|
attrs: {
|
|
684
|
-
to: S_WHATSAPP_NET,
|
|
477
|
+
to: WABinary_1.S_WHATSAPP_NET,
|
|
685
478
|
id: generateMessageTag(),
|
|
686
479
|
xmlns: 'w:stats'
|
|
687
480
|
},
|
|
688
481
|
content: [
|
|
689
482
|
{
|
|
690
483
|
tag: 'add',
|
|
691
|
-
attrs: {
|
|
484
|
+
attrs: {},
|
|
692
485
|
content: wamBuffer
|
|
693
486
|
}
|
|
694
487
|
]
|
|
@@ -701,26 +494,26 @@ export const makeSocket = (config) => {
|
|
|
701
494
|
}
|
|
702
495
|
catch (err) {
|
|
703
496
|
logger.error({ err }, 'error in validating connection');
|
|
704
|
-
|
|
497
|
+
end(err);
|
|
705
498
|
}
|
|
706
499
|
});
|
|
707
500
|
ws.on('error', mapWebSocketError(end));
|
|
708
|
-
ws.on('close', () =>
|
|
501
|
+
ws.on('close', () => end(new boom_1.Boom('Connection Terminated', { statusCode: Types_1.DisconnectReason.connectionClosed })));
|
|
709
502
|
// the server terminated the connection
|
|
710
|
-
ws.on('CB:xmlstreamend', () =>
|
|
503
|
+
ws.on('CB:xmlstreamend', () => end(new boom_1.Boom('Connection Terminated by Server', { statusCode: Types_1.DisconnectReason.connectionClosed })));
|
|
711
504
|
// QR gen
|
|
712
505
|
ws.on('CB:iq,type:set,pair-device', async (stanza) => {
|
|
713
506
|
const iq = {
|
|
714
507
|
tag: 'iq',
|
|
715
508
|
attrs: {
|
|
716
|
-
to: S_WHATSAPP_NET,
|
|
509
|
+
to: WABinary_1.S_WHATSAPP_NET,
|
|
717
510
|
type: 'result',
|
|
718
|
-
id: stanza.attrs.id
|
|
511
|
+
id: stanza.attrs.id,
|
|
719
512
|
}
|
|
720
513
|
};
|
|
721
514
|
await sendNode(iq);
|
|
722
|
-
const pairDeviceNode = getBinaryNodeChild(stanza, 'pair-device');
|
|
723
|
-
const refNodes = getBinaryNodeChildren(pairDeviceNode, 'ref');
|
|
515
|
+
const pairDeviceNode = (0, WABinary_1.getBinaryNodeChild)(stanza, 'pair-device');
|
|
516
|
+
const refNodes = (0, WABinary_1.getBinaryNodeChildren)(pairDeviceNode, 'ref');
|
|
724
517
|
const noiseKeyB64 = Buffer.from(creds.noiseKey.public).toString('base64');
|
|
725
518
|
const identityKeyB64 = Buffer.from(creds.signedIdentityKey.public).toString('base64');
|
|
726
519
|
const advB64 = creds.advSecretKey;
|
|
@@ -731,11 +524,11 @@ export const makeSocket = (config) => {
|
|
|
731
524
|
}
|
|
732
525
|
const refNode = refNodes.shift();
|
|
733
526
|
if (!refNode) {
|
|
734
|
-
|
|
527
|
+
end(new boom_1.Boom('QR refs attempts ended', { statusCode: Types_1.DisconnectReason.timedOut }));
|
|
735
528
|
return;
|
|
736
529
|
}
|
|
737
530
|
const ref = refNode.content.toString('utf-8');
|
|
738
|
-
const qr =
|
|
531
|
+
const qr = [ref, noiseKeyB64, identityKeyB64, advB64].join(',');
|
|
739
532
|
ev.emit('connection.update', { qr });
|
|
740
533
|
qrTimer = setTimeout(genPairQR, qrMs);
|
|
741
534
|
qrMs = qrTimeout || 20000; // shorter subsequent qrs
|
|
@@ -747,98 +540,65 @@ export const makeSocket = (config) => {
|
|
|
747
540
|
ws.on('CB:iq,,pair-success', async (stanza) => {
|
|
748
541
|
logger.debug('pair success recv');
|
|
749
542
|
try {
|
|
750
|
-
|
|
751
|
-
const { reply, creds: updatedCreds } = configureSuccessfulPairing(stanza, creds);
|
|
543
|
+
const { reply, creds: updatedCreds } = (0, Utils_1.configureSuccessfulPairing)(stanza, creds);
|
|
752
544
|
logger.info({ me: updatedCreds.me, platform: updatedCreds.platform }, 'pairing configured successfully, expect to restart the connection...');
|
|
753
545
|
ev.emit('creds.update', updatedCreds);
|
|
754
546
|
ev.emit('connection.update', { isNewLogin: true, qr: undefined });
|
|
755
547
|
await sendNode(reply);
|
|
756
|
-
void sendUnifiedSession();
|
|
757
548
|
}
|
|
758
549
|
catch (error) {
|
|
759
550
|
logger.info({ trace: error.stack }, 'error in pairing');
|
|
760
|
-
|
|
551
|
+
end(error);
|
|
761
552
|
}
|
|
762
553
|
});
|
|
763
554
|
// login complete
|
|
764
555
|
ws.on('CB:success', async (node) => {
|
|
765
556
|
try {
|
|
766
|
-
updateServerTimeOffset(node);
|
|
767
557
|
await uploadPreKeysToServerIfRequired();
|
|
768
558
|
await sendPassiveIq('active');
|
|
769
|
-
|
|
770
|
-
try
|
|
771
|
-
|
|
772
|
-
}
|
|
773
|
-
catch (e) {
|
|
774
|
-
logger.warn({ e }, 'failed to run digest after login');
|
|
775
|
-
}
|
|
559
|
+
logger.info('opened connection to WA');
|
|
560
|
+
clearTimeout(qrTimer); // will never happen in all likelyhood -- but just in case WA sends success on first try
|
|
561
|
+
ev.emit('creds.update', { me: { ...authState.creds.me, lid: node.attrs.lid } });
|
|
562
|
+
ev.emit('connection.update', { connection: 'open' });
|
|
776
563
|
}
|
|
777
564
|
catch (err) {
|
|
778
|
-
logger.
|
|
779
|
-
|
|
780
|
-
logger.info('opened connection to WA');
|
|
781
|
-
clearTimeout(qrTimer); // will never happen in all likelyhood -- but just in case WA sends success on first try
|
|
782
|
-
ev.emit('creds.update', { me: { ...authState.creds.me, lid: node.attrs.lid } });
|
|
783
|
-
ev.emit('connection.update', { connection: 'open' });
|
|
784
|
-
void sendUnifiedSession();
|
|
785
|
-
if (node.attrs.lid && authState.creds.me?.id) {
|
|
786
|
-
const myLID = node.attrs.lid;
|
|
787
|
-
process.nextTick(async () => {
|
|
788
|
-
try {
|
|
789
|
-
const myPN = authState.creds.me.id;
|
|
790
|
-
// Store our own LID-PN mapping
|
|
791
|
-
await signalRepository.lidMapping.storeLIDPNMappings([{ lid: myLID, pn: myPN }]);
|
|
792
|
-
// Create device list for our own user (needed for bulk migration)
|
|
793
|
-
const { user, device } = jidDecode(myPN);
|
|
794
|
-
await authState.keys.set({
|
|
795
|
-
'device-list': {
|
|
796
|
-
[user]: [device?.toString() || '0']
|
|
797
|
-
}
|
|
798
|
-
});
|
|
799
|
-
// migrate our own session
|
|
800
|
-
await signalRepository.migrateSession(myPN, myLID);
|
|
801
|
-
logger.info({ myPN, myLID }, 'Own LID session created successfully');
|
|
802
|
-
}
|
|
803
|
-
catch (error) {
|
|
804
|
-
logger.error({ error, lid: myLID }, 'Failed to create own LID session');
|
|
805
|
-
}
|
|
806
|
-
});
|
|
565
|
+
logger.error({ err }, 'error opening connection');
|
|
566
|
+
end(err);
|
|
807
567
|
}
|
|
808
568
|
});
|
|
809
569
|
ws.on('CB:stream:error', (node) => {
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
void end(new Boom(`Stream Errored (${reason})`, { statusCode, data: reasonNode || node }));
|
|
570
|
+
logger.error({ node }, 'stream errored out');
|
|
571
|
+
const { reason, statusCode } = (0, Utils_1.getErrorCodeFromStreamError)(node);
|
|
572
|
+
end(new boom_1.Boom(`Stream Errored (${reason})`, { statusCode, data: node }));
|
|
814
573
|
});
|
|
815
574
|
// stream fail, possible logout
|
|
816
575
|
ws.on('CB:failure', (node) => {
|
|
817
576
|
const reason = +(node.attrs.reason || 500);
|
|
818
|
-
|
|
577
|
+
end(new boom_1.Boom('Connection Failure', { statusCode: reason, data: node.attrs }));
|
|
819
578
|
});
|
|
820
579
|
ws.on('CB:ib,,downgrade_webclient', () => {
|
|
821
|
-
|
|
580
|
+
end(new boom_1.Boom('Multi-device beta not joined', { statusCode: Types_1.DisconnectReason.multideviceMismatch }));
|
|
822
581
|
});
|
|
823
|
-
ws.on('CB:ib,,offline_preview',
|
|
582
|
+
ws.on('CB:ib,,offline_preview', (node) => {
|
|
824
583
|
logger.info('offline preview received', JSON.stringify(node));
|
|
825
|
-
|
|
584
|
+
sendNode({
|
|
826
585
|
tag: 'ib',
|
|
827
586
|
attrs: {},
|
|
828
587
|
content: [{ tag: 'offline_batch', attrs: { count: '100' } }]
|
|
829
588
|
});
|
|
830
589
|
});
|
|
831
590
|
ws.on('CB:ib,,edge_routing', (node) => {
|
|
832
|
-
const edgeRoutingNode = getBinaryNodeChild(node, 'edge_routing');
|
|
833
|
-
const routingInfo = getBinaryNodeChild(edgeRoutingNode, 'routing_info');
|
|
834
|
-
if (routingInfo
|
|
835
|
-
authState.creds.routingInfo = Buffer.from(routingInfo
|
|
591
|
+
const edgeRoutingNode = (0, WABinary_1.getBinaryNodeChild)(node, 'edge_routing');
|
|
592
|
+
const routingInfo = (0, WABinary_1.getBinaryNodeChild)(edgeRoutingNode, 'routing_info');
|
|
593
|
+
if (routingInfo === null || routingInfo === void 0 ? void 0 : routingInfo.content) {
|
|
594
|
+
authState.creds.routingInfo = Buffer.from(routingInfo === null || routingInfo === void 0 ? void 0 : routingInfo.content);
|
|
836
595
|
ev.emit('creds.update', authState.creds);
|
|
837
596
|
}
|
|
838
597
|
});
|
|
839
598
|
let didStartBuffer = false;
|
|
840
599
|
process.nextTick(() => {
|
|
841
|
-
|
|
600
|
+
var _a;
|
|
601
|
+
if ((_a = creds.me) === null || _a === void 0 ? void 0 : _a.id) {
|
|
842
602
|
// start buffering important events
|
|
843
603
|
// if we're logged in
|
|
844
604
|
ev.buffer();
|
|
@@ -848,8 +608,8 @@ export const makeSocket = (config) => {
|
|
|
848
608
|
});
|
|
849
609
|
// called when all offline notifs are handled
|
|
850
610
|
ws.on('CB:ib,,offline', (node) => {
|
|
851
|
-
const child = getBinaryNodeChild(node, 'offline');
|
|
852
|
-
const offlineNotifs = +(child
|
|
611
|
+
const child = (0, WABinary_1.getBinaryNodeChild)(node, 'offline');
|
|
612
|
+
const offlineNotifs = +((child === null || child === void 0 ? void 0 : child.attrs.count) || 0);
|
|
853
613
|
logger.info(`handled ${offlineNotifs} offline messages/notifications`);
|
|
854
614
|
if (didStartBuffer) {
|
|
855
615
|
ev.flush();
|
|
@@ -859,87 +619,24 @@ export const makeSocket = (config) => {
|
|
|
859
619
|
});
|
|
860
620
|
// update credentials when required
|
|
861
621
|
ev.on('creds.update', update => {
|
|
862
|
-
|
|
622
|
+
var _a, _b;
|
|
623
|
+
const name = (_a = update.me) === null || _a === void 0 ? void 0 : _a.name;
|
|
863
624
|
// if name has just been received
|
|
864
|
-
if (creds.me
|
|
625
|
+
if (((_b = creds.me) === null || _b === void 0 ? void 0 : _b.name) !== name) {
|
|
865
626
|
logger.debug({ name }, 'updated pushName');
|
|
866
627
|
sendNode({
|
|
867
628
|
tag: 'presence',
|
|
868
629
|
attrs: { name: name }
|
|
869
|
-
})
|
|
630
|
+
})
|
|
631
|
+
.catch(err => {
|
|
870
632
|
logger.warn({ trace: err.stack }, 'error in sending presence update on name change');
|
|
871
633
|
});
|
|
872
634
|
}
|
|
873
635
|
Object.assign(creds, update);
|
|
874
636
|
});
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
return;
|
|
879
|
-
}
|
|
880
|
-
const parsed = Number(tValue);
|
|
881
|
-
if (Number.isNaN(parsed) || parsed <= 0) {
|
|
882
|
-
return;
|
|
883
|
-
}
|
|
884
|
-
const localMs = Date.now();
|
|
885
|
-
serverTimeOffsetMs = parsed * 1000 - localMs;
|
|
886
|
-
logger.debug({ offset: serverTimeOffsetMs }, 'calculated server time offset');
|
|
887
|
-
};
|
|
888
|
-
const getUnifiedSessionId = () => {
|
|
889
|
-
const offsetMs = 3 * TimeMs.Day;
|
|
890
|
-
const now = Date.now() + serverTimeOffsetMs;
|
|
891
|
-
const id = (now + offsetMs) % TimeMs.Week;
|
|
892
|
-
return id.toString();
|
|
893
|
-
};
|
|
894
|
-
const sendUnifiedSession = async () => {
|
|
895
|
-
if (!ws.isOpen) {
|
|
896
|
-
return;
|
|
897
|
-
}
|
|
898
|
-
const node = {
|
|
899
|
-
tag: 'ib',
|
|
900
|
-
attrs: {},
|
|
901
|
-
content: [
|
|
902
|
-
{
|
|
903
|
-
tag: 'unified_session',
|
|
904
|
-
attrs: {
|
|
905
|
-
id: getUnifiedSessionId()
|
|
906
|
-
}
|
|
907
|
-
}
|
|
908
|
-
]
|
|
909
|
-
};
|
|
910
|
-
try {
|
|
911
|
-
await sendNode(node);
|
|
912
|
-
}
|
|
913
|
-
catch (error) {
|
|
914
|
-
logger.debug({ error }, 'failed to send unified_session telemetry');
|
|
915
|
-
}
|
|
916
|
-
};
|
|
917
|
-
const registerSocketEndHandler = (handler) => {
|
|
918
|
-
socketEndHandlers.push(handler);
|
|
919
|
-
};
|
|
920
|
-
/**
|
|
921
|
-
* Fetches your account's standing when it comes to restrictions.
|
|
922
|
-
* @returns Returns the state of the restrictions.
|
|
923
|
-
*/
|
|
924
|
-
const fetchAccountReachoutTimelock = async () => {
|
|
925
|
-
const queryResult = await executeWMexQuery({}, QueryIds.REACHOUT_TIMELOCK, XWAPaths.xwa2_fetch_account_reachout_timelock, query, generateMessageTag);
|
|
926
|
-
const result = {
|
|
927
|
-
isActive: !!queryResult?.is_active,
|
|
928
|
-
timeEnforcementEnds: queryResult?.time_enforcement_ends && queryResult?.time_enforcement_ends !== '0'
|
|
929
|
-
? new Date(parseInt(queryResult.time_enforcement_ends, 10) * 1000)
|
|
930
|
-
: undefined,
|
|
931
|
-
enforcementType: queryResult?.enforcement_type ?? ReachoutTimelockEnforcementType.DEFAULT
|
|
932
|
-
};
|
|
933
|
-
ev.emit('connection.update', { reachoutTimeLock: result });
|
|
934
|
-
return result;
|
|
935
|
-
};
|
|
936
|
-
/**
|
|
937
|
-
* Fetches your account's new chat limits.
|
|
938
|
-
* @returns Returns the quota and the usage.
|
|
939
|
-
*/
|
|
940
|
-
const fetchNewChatMessageCap = async () => {
|
|
941
|
-
return executeWMexQuery({ input: { type: 'INDIVIDUAL_NEW_CHAT_MSG' } }, QueryIds.MESSAGE_CAPPING_INFO, XWAPaths.xwa2_message_capping_info, query, generateMessageTag);
|
|
942
|
-
};
|
|
637
|
+
if (printQRInTerminal) {
|
|
638
|
+
(0, Utils_1.printQRIfNecessaryListener)(ev, logger);
|
|
639
|
+
}
|
|
943
640
|
return {
|
|
944
641
|
type: 'md',
|
|
945
642
|
ws,
|
|
@@ -957,32 +654,22 @@ export const makeSocket = (config) => {
|
|
|
957
654
|
sendNode,
|
|
958
655
|
logout,
|
|
959
656
|
end,
|
|
960
|
-
registerSocketEndHandler,
|
|
961
657
|
onUnexpectedError,
|
|
962
658
|
uploadPreKeys,
|
|
963
659
|
uploadPreKeysToServerIfRequired,
|
|
964
|
-
digestKeyBundle,
|
|
965
|
-
rotateSignedPreKey,
|
|
966
660
|
requestPairingCode,
|
|
967
|
-
updateServerTimeOffset,
|
|
968
|
-
sendUnifiedSession,
|
|
969
|
-
wamBuffer: publicWAMBuffer,
|
|
970
661
|
/** Waits for the connection to WA to reach a state */
|
|
971
|
-
waitForConnectionUpdate: bindWaitForConnectionUpdate(ev),
|
|
662
|
+
waitForConnectionUpdate: (0, Utils_1.bindWaitForConnectionUpdate)(ev),
|
|
972
663
|
sendWAMBuffer,
|
|
973
|
-
executeUSyncQuery,
|
|
974
|
-
onWhatsApp,
|
|
975
|
-
fetchAccountReachoutTimelock,
|
|
976
|
-
fetchNewChatMessageCap
|
|
977
664
|
};
|
|
978
665
|
};
|
|
666
|
+
exports.makeSocket = makeSocket;
|
|
979
667
|
/**
|
|
980
668
|
* map the websocket error to the right type
|
|
981
669
|
* so it can be retried by the caller
|
|
982
670
|
* */
|
|
983
671
|
function mapWebSocketError(handler) {
|
|
984
672
|
return (error) => {
|
|
985
|
-
handler(new Boom(`WebSocket Error (${error
|
|
673
|
+
handler(new boom_1.Boom(`WebSocket Error (${error === null || error === void 0 ? void 0 : error.message})`, { statusCode: (0, Utils_1.getCodeFromWSError)(error), data: error }));
|
|
986
674
|
};
|
|
987
|
-
}
|
|
988
|
-
//# sourceMappingURL=socket.js.map
|
|
675
|
+
}
|