@pontalabs/baileys 1.0.11 → 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/README.md +96 -85
- package/lib/Defaults/connection.js +39 -0
- package/lib/Defaults/constants.js +50 -0
- package/lib/Defaults/history.js +13 -0
- package/lib/Defaults/index.js +36 -151
- package/lib/Defaults/media.js +43 -0
- package/lib/Defaults/phonenumber-mcc.json +221 -221
- package/lib/Defaults/prefix.js +12 -0
- package/lib/Defaults/vialeys-version.json +7 -0
- package/lib/Signal/Group/ciphertext-message.js +10 -15
- package/lib/Signal/Group/group-session-builder.js +34 -60
- package/lib/Signal/Group/group_cipher.js +69 -76
- package/lib/Signal/Group/index.js +41 -60
- package/lib/Signal/Group/keyhelper.js +12 -57
- package/lib/Signal/Group/sender-chain-key.js +20 -33
- package/lib/Signal/Group/sender-key-distribution-message.js +43 -48
- package/lib/Signal/Group/sender-key-message.js +58 -53
- package/lib/Signal/Group/sender-key-name.js +21 -31
- package/lib/Signal/Group/sender-key-record.js +21 -34
- package/lib/Signal/Group/sender-key-state.js +59 -106
- package/lib/Signal/Group/sender-message-key.js +16 -21
- package/lib/Signal/libsignal.js +282 -203
- package/lib/Signal/lid-mapping.js +164 -184
- package/lib/Socket/Client/index.js +31 -22
- package/lib/Socket/Client/types.js +9 -15
- package/lib/Socket/Client/websocket.js +42 -37
- package/lib/Socket/business.js +212 -336
- package/lib/Socket/chats.js +696 -940
- package/lib/Socket/community.js +454 -369
- package/lib/Socket/groups.js +278 -280
- package/lib/Socket/index.js +9 -21
- package/lib/Socket/messages-recv.js +1027 -1570
- package/lib/Socket/messages-send.js +1180 -1136
- package/lib/Socket/mex.js +59 -0
- package/lib/Socket/newsletter.js +193 -251
- package/lib/Socket/socket.js +758 -898
- package/lib/Store/index.js +35 -24
- package/lib/Store/make-cache-manager-store.js +53 -66
- package/lib/Store/make-in-memory-store.js +319 -325
- package/lib/Store/make-ordered-dictionary.js +49 -57
- package/lib/Store/object-repository.js +10 -15
- package/lib/Types/Auth.js +2 -3
- package/lib/Types/Bussines.js +2 -3
- package/lib/Types/Call.js +2 -3
- package/lib/Types/Chat.js +10 -9
- package/lib/Types/Contact.js +2 -3
- package/lib/Types/Events.js +2 -3
- package/lib/Types/GroupMetadata.js +2 -3
- package/lib/Types/Label.js +6 -12
- package/lib/Types/LabelAssociation.js +4 -13
- package/lib/Types/Message.js +10 -18
- package/lib/Types/MexUpdates.js +15 -18
- package/lib/Types/Newsletter.js +26 -34
- package/lib/Types/Product.js +2 -3
- package/lib/Types/Signal.js +2 -3
- package/lib/Types/Socket.js +2 -3
- package/lib/Types/State.js +8 -13
- package/lib/Types/USync.js +2 -3
- package/lib/Types/index.js +49 -40
- package/lib/Utils/auth-utils.js +190 -495
- package/lib/Utils/browser-utils.js +29 -0
- package/lib/Utils/business.js +177 -199
- package/lib/Utils/chat-utils.js +711 -624
- package/lib/Utils/crypto.js +116 -161
- package/lib/Utils/decode-wa-message.js +225 -321
- package/lib/Utils/event-buffer.js +363 -405
- package/lib/Utils/generics.js +318 -472
- package/lib/Utils/history.js +72 -76
- package/lib/Utils/index.js +49 -41
- package/lib/Utils/link-preview.js +59 -103
- package/lib/Utils/logger.js +4 -7
- package/lib/Utils/lt-hash.js +29 -42
- package/lib/Utils/make-mutex.js +26 -42
- package/lib/Utils/message-retry-manager.js +134 -160
- package/lib/Utils/messages-media.js +665 -731
- package/lib/Utils/messages.js +518 -1
- package/lib/Utils/noise-handler.js +127 -125
- package/lib/Utils/pre-key-manager.js +86 -0
- package/lib/Utils/process-message.js +739 -351
- package/lib/Utils/signal.js +146 -129
- package/lib/Utils/use-multi-file-auth-state.js +68 -135
- package/lib/Utils/validate-connection.js +183 -142
- package/lib/Utils/vialeys-event-stream.js +41 -0
- package/lib/WABinary/constants.js +1281 -1289
- package/lib/WABinary/decode.js +145 -200
- package/lib/WABinary/encode.js +149 -202
- package/lib/WABinary/generic-utils.js +79 -97
- package/lib/WABinary/index.js +36 -25
- package/lib/WABinary/jid-utils.js +91 -123
- package/lib/WABinary/types.js +2 -3
- package/lib/WAM/BinaryInfo.js +8 -13
- package/lib/WAM/constants.js +16584 -11464
- package/lib/WAM/encode.js +117 -133
- package/lib/WAM/index.js +34 -23
- package/lib/WAUSync/Protocols/USyncBotProfileProtocol.js +62 -69
- package/lib/WAUSync/Protocols/USyncContactProtocol.js +11 -25
- package/lib/WAUSync/Protocols/USyncDeviceProtocol.js +32 -44
- package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.js +13 -23
- package/lib/WAUSync/Protocols/USyncLIDProtocol.js +24 -38
- package/lib/WAUSync/Protocols/USyncStatusProtocol.js +17 -31
- package/lib/WAUSync/Protocols/index.js +37 -26
- package/lib/WAUSync/USyncQuery.js +66 -66
- package/lib/WAUSync/USyncUser.js +13 -18
- package/lib/WAUSync/index.js +34 -23
- package/lib/index.js +95 -28
- package/package.json +8 -6
- package/lib/Defaults/baileys-version.json +0 -3
- package/lib/Signal/Group/queue-job.d.ts +0 -1
- package/lib/Signal/Group/queue-job.js +0 -57
- package/lib/Socket/usync.d.ts +0 -37
- package/lib/Socket/usync.js +0 -83
- package/lib/Utils/baileys-event-stream.d.ts +0 -18
- package/lib/Utils/baileys-event-stream.js +0 -70
- package/lib/Utils/signal-housekeeping.d.ts +0 -19
- package/lib/Utils/signal-housekeeping.js +0 -73
- package/lib/Utils/use-mongo-file-auth-state.d.ts +0 -6
- package/lib/Utils/use-mongo-file-auth-state.js +0 -84
- package/lib/Utils/use-single-file-auth-state.d.ts +0 -13
- package/lib/Utils/use-single-file-auth-state.js +0 -80
package/lib/Socket/socket.js
CHANGED
|
@@ -1,1038 +1,898 @@
|
|
|
1
|
-
"use strict"
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
})
|
|
6
|
-
|
|
7
|
-
const
|
|
8
|
-
const
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
const
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const { Boom: Boom } = require("@hapi/boom");
|
|
4
|
+
const { randomBytes: randomBytes } = require("crypto");
|
|
5
|
+
const { URL: URL } = require("url");
|
|
6
|
+
const { promisify: promisify } = require("util");
|
|
7
|
+
const { proto: proto } = require("../../WAProto");
|
|
8
|
+
const {
|
|
9
|
+
DEF_TAG_PREFIX: DEF_TAG_PREFIX,
|
|
10
|
+
DEF_CALLBACK_PREFIX: DEF_CALLBACK_PREFIX,
|
|
11
|
+
} = require("../Defaults/prefix");
|
|
12
|
+
const {
|
|
13
|
+
NOISE_WA_HEADER: NOISE_WA_HEADER,
|
|
14
|
+
INITIAL_PREKEY_COUNT: INITIAL_PREKEY_COUNT,
|
|
15
|
+
MIN_UPLOAD_INTERVAL: MIN_UPLOAD_INTERVAL,
|
|
16
|
+
MIN_PREKEY_COUNT: MIN_PREKEY_COUNT,
|
|
17
|
+
UPLOAD_TIMEOUT: UPLOAD_TIMEOUT,
|
|
18
|
+
} = require("../Defaults/constants");
|
|
19
|
+
const { DisconnectReason: DisconnectReason } = require("../Types");
|
|
20
|
+
const {
|
|
21
|
+
addTransactionCapability: addTransactionCapability,
|
|
22
|
+
aesEncryptCTR: aesEncryptCTR,
|
|
23
|
+
printQRIfNecessaryListener: printQRIfNecessaryListener,
|
|
24
|
+
bindWaitForConnectionUpdate: bindWaitForConnectionUpdate,
|
|
25
|
+
bytesToCrockford: bytesToCrockford,
|
|
26
|
+
configureSuccessfulPairing: configureSuccessfulPairing,
|
|
27
|
+
Curve: Curve,
|
|
28
|
+
derivePairingCodeKey: derivePairingCodeKey,
|
|
29
|
+
generateLoginNode: generateLoginNode,
|
|
30
|
+
generateMdTagPrefix: generateMdTagPrefix,
|
|
31
|
+
generateRegistrationNode: generateRegistrationNode,
|
|
32
|
+
getCodeFromWSError: getCodeFromWSError,
|
|
33
|
+
getErrorCodeFromStreamError: getErrorCodeFromStreamError,
|
|
34
|
+
getNextPreKeysNode: getNextPreKeysNode,
|
|
35
|
+
getPlatformId: getPlatformId,
|
|
36
|
+
makeEventBuffer: makeEventBuffer,
|
|
37
|
+
makeNoiseHandler: makeNoiseHandler,
|
|
38
|
+
promiseTimeout: promiseTimeout,
|
|
39
|
+
asciiDecode: asciiDecode,
|
|
40
|
+
} = require("../Utils");
|
|
41
|
+
const {
|
|
42
|
+
assertNodeErrorFree: assertNodeErrorFree,
|
|
43
|
+
binaryNodeToString: binaryNodeToString,
|
|
44
|
+
encodeBinaryNode: encodeBinaryNode,
|
|
45
|
+
getBinaryNodeChild: getBinaryNodeChild,
|
|
46
|
+
getBinaryNodeChildren: getBinaryNodeChildren,
|
|
47
|
+
getAllBinaryNodeChildren: getAllBinaryNodeChildren,
|
|
48
|
+
isLidUser: isLidUser,
|
|
49
|
+
jidDecode: jidDecode,
|
|
50
|
+
jidEncode: jidEncode,
|
|
51
|
+
S_WHATSAPP_NET: S_WHATSAPP_NET,
|
|
52
|
+
} = require("../WABinary");
|
|
53
|
+
const { BinaryInfo: BinaryInfo } = require("../WAM");
|
|
54
|
+
const { USyncUser: USyncUser, USyncQuery: USyncQuery } = require("../WAUSync");
|
|
55
|
+
const { WebSocketClient: WebSocketClient } = require("./Client");
|
|
19
56
|
const makeSocket = (config) => {
|
|
20
57
|
const {
|
|
21
|
-
waWebSocketUrl,
|
|
22
|
-
connectTimeoutMs,
|
|
23
|
-
logger,
|
|
24
|
-
keepAliveIntervalMs,
|
|
25
|
-
browser,
|
|
58
|
+
waWebSocketUrl: waWebSocketUrl,
|
|
59
|
+
connectTimeoutMs: connectTimeoutMs,
|
|
60
|
+
logger: logger,
|
|
61
|
+
keepAliveIntervalMs: keepAliveIntervalMs,
|
|
62
|
+
browser: browser,
|
|
26
63
|
auth: authState,
|
|
27
|
-
printQRInTerminal,
|
|
28
|
-
defaultQueryTimeoutMs,
|
|
29
|
-
transactionOpts,
|
|
30
|
-
qrTimeout,
|
|
31
|
-
makeSignalRepository
|
|
32
|
-
} = config
|
|
33
|
-
|
|
34
|
-
const
|
|
35
|
-
const
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
statusCode: Types_1.DisconnectReason.loggedOut
|
|
42
|
-
})
|
|
64
|
+
printQRInTerminal: printQRInTerminal,
|
|
65
|
+
defaultQueryTimeoutMs: defaultQueryTimeoutMs,
|
|
66
|
+
transactionOpts: transactionOpts,
|
|
67
|
+
qrTimeout: qrTimeout,
|
|
68
|
+
makeSignalRepository: makeSignalRepository,
|
|
69
|
+
} = config;
|
|
70
|
+
const uqTagId = generateMdTagPrefix();
|
|
71
|
+
const generateMessageTag = () => `${uqTagId}${epoch++}`;
|
|
72
|
+
const publicWAMBuffer = new BinaryInfo();
|
|
73
|
+
const url = typeof waWebSocketUrl === "string" ? new URL(waWebSocketUrl) : waWebSocketUrl;
|
|
74
|
+
if (config.mobile || url.protocol === "tcp:") {
|
|
75
|
+
throw new Boom("Mobile API is not supported anymore", {
|
|
76
|
+
statusCode: DisconnectReason.loggedOut,
|
|
77
|
+
});
|
|
43
78
|
}
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
url.searchParams.append('ED', authState.creds.routingInfo.toString('base64url'))
|
|
79
|
+
if (url.protocol === "wss" && authState?.creds?.routingInfo) {
|
|
80
|
+
url.searchParams.append("ED", authState.creds.routingInfo.toString("base64url"));
|
|
47
81
|
}
|
|
48
|
-
|
|
82
|
+
const ephemeralKeyPair = Curve.generateKeyPair();
|
|
83
|
+
const noise = makeNoiseHandler({
|
|
84
|
+
keyPair: ephemeralKeyPair,
|
|
85
|
+
NOISE_HEADER: NOISE_WA_HEADER,
|
|
86
|
+
logger: logger,
|
|
87
|
+
routingInfo: authState?.creds?.routingInfo,
|
|
88
|
+
});
|
|
89
|
+
const ws = new WebSocketClient(url, config);
|
|
90
|
+
ws.connect();
|
|
91
|
+
const sendPromise = promisify(ws.send);
|
|
92
|
+
const sendRawMessage = async (data) => {
|
|
93
|
+
if (!ws.isOpen) {
|
|
94
|
+
throw new Boom("Connection Closed", {
|
|
95
|
+
statusCode: DisconnectReason.connectionClosed,
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
const bytes = noise.encodeFrame(data);
|
|
99
|
+
await promiseTimeout(connectTimeoutMs, async (resolve, reject) => {
|
|
100
|
+
try {
|
|
101
|
+
await sendPromise.call(ws, bytes);
|
|
102
|
+
resolve();
|
|
103
|
+
} catch (error) {
|
|
104
|
+
reject(error);
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
};
|
|
108
|
+
const sendNode = (frame) => {
|
|
109
|
+
if (logger.level === "trace") {
|
|
110
|
+
logger.trace({ xml: binaryNodeToString(frame), msg: "xml send" });
|
|
111
|
+
}
|
|
112
|
+
const buff = encodeBinaryNode(frame);
|
|
113
|
+
return sendRawMessage(buff);
|
|
114
|
+
};
|
|
49
115
|
const waitForMessage = async (msgId, timeoutMs = defaultQueryTimeoutMs) => {
|
|
50
|
-
let onRecv
|
|
51
|
-
let onErr
|
|
116
|
+
let onRecv;
|
|
117
|
+
let onErr;
|
|
52
118
|
try {
|
|
53
|
-
|
|
54
|
-
onRecv =
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
119
|
+
const result = await promiseTimeout(timeoutMs, (resolve, reject) => {
|
|
120
|
+
onRecv = (data) => {
|
|
121
|
+
resolve(data);
|
|
122
|
+
};
|
|
123
|
+
onErr = (err) => {
|
|
124
|
+
reject(
|
|
125
|
+
err ||
|
|
126
|
+
new Boom("Connection Closed", {
|
|
127
|
+
statusCode: DisconnectReason.connectionClosed,
|
|
128
|
+
})
|
|
129
|
+
);
|
|
130
|
+
};
|
|
131
|
+
ws.on(`TAG:${msgId}`, onRecv);
|
|
132
|
+
ws.on("close", onErr);
|
|
133
|
+
ws.on("error", onErr);
|
|
134
|
+
return () => reject(new Boom("Query Cancelled"));
|
|
135
|
+
});
|
|
136
|
+
return result;
|
|
137
|
+
} catch (error) {
|
|
138
|
+
if (error instanceof Boom && error.output?.statusCode === DisconnectReason.timedOut) {
|
|
139
|
+
logger?.warn?.({ msgId: msgId }, "timed out waiting for message");
|
|
140
|
+
return undefined;
|
|
141
|
+
}
|
|
142
|
+
throw error;
|
|
64
143
|
} finally {
|
|
65
|
-
ws.off(`TAG:${msgId}`, onRecv)
|
|
66
|
-
|
|
67
|
-
|
|
144
|
+
if (onRecv) ws.off(`TAG:${msgId}`, onRecv);
|
|
145
|
+
if (onErr) {
|
|
146
|
+
ws.off("close", onErr);
|
|
147
|
+
ws.off("error", onErr);
|
|
148
|
+
}
|
|
68
149
|
}
|
|
69
|
-
}
|
|
70
|
-
|
|
150
|
+
};
|
|
71
151
|
const query = async (node, timeoutMs) => {
|
|
72
152
|
if (!node.attrs.id) {
|
|
73
|
-
node.attrs.id = generateMessageTag()
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
const
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
153
|
+
node.attrs.id = generateMessageTag();
|
|
154
|
+
}
|
|
155
|
+
const msgId = node.attrs.id;
|
|
156
|
+
const result = await promiseTimeout(timeoutMs, async (resolve, reject) => {
|
|
157
|
+
const result = waitForMessage(msgId, timeoutMs).catch(reject);
|
|
158
|
+
sendNode(node)
|
|
159
|
+
.then(async () => resolve(await result))
|
|
160
|
+
.catch(reject);
|
|
161
|
+
});
|
|
162
|
+
if (result && "tag" in result) {
|
|
163
|
+
assertNodeErrorFree(result);
|
|
164
|
+
}
|
|
165
|
+
return result;
|
|
166
|
+
};
|
|
167
|
+
const digestKeyBundle = async () => {
|
|
168
|
+
const res = await query({
|
|
169
|
+
tag: "iq",
|
|
170
|
+
attrs: { to: S_WHATSAPP_NET, type: "get", xmlns: "encrypt" },
|
|
171
|
+
content: [{ tag: "digest", attrs: {} }],
|
|
172
|
+
});
|
|
173
|
+
const digestNode = getBinaryNodeChild(res, "digest");
|
|
174
|
+
if (!digestNode) {
|
|
175
|
+
await uploadPreKeys();
|
|
176
|
+
throw new Error("encrypt/get digest returned no digest node");
|
|
177
|
+
}
|
|
178
|
+
};
|
|
179
|
+
const rotateSignedPreKey = async () => {
|
|
180
|
+
const newId = (creds.signedPreKey.keyId || 0) + 1;
|
|
181
|
+
const skey = await signedKeyPair(creds.signedIdentityKey, newId);
|
|
182
|
+
await query({
|
|
183
|
+
tag: "iq",
|
|
184
|
+
attrs: { to: S_WHATSAPP_NET, type: "set", xmlns: "encrypt" },
|
|
185
|
+
content: [{ tag: "rotate", attrs: {}, content: [xmppSignedPreKey(skey)] }],
|
|
186
|
+
});
|
|
187
|
+
ev.emit("creds.update", { signedPreKey: skey });
|
|
188
|
+
};
|
|
90
189
|
const executeUSyncQuery = async (usyncQuery) => {
|
|
91
190
|
if (usyncQuery.protocols.length === 0) {
|
|
92
|
-
throw new
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
const
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
}
|
|
104
|
-
})
|
|
105
|
-
|
|
106
|
-
const listNode = {
|
|
107
|
-
tag: 'list',
|
|
108
|
-
attrs: {},
|
|
109
|
-
content: userNodes
|
|
110
|
-
}
|
|
111
|
-
|
|
191
|
+
throw new Boom("USyncQuery must have at least one protocol");
|
|
192
|
+
}
|
|
193
|
+
const validUsers = usyncQuery.users;
|
|
194
|
+
const userNodes = validUsers.map((user) => ({
|
|
195
|
+
tag: "user",
|
|
196
|
+
attrs: { jid: !user.phone ? user.id : undefined },
|
|
197
|
+
content: usyncQuery.protocols
|
|
198
|
+
.map((a) => a.getUserElement(user))
|
|
199
|
+
.filter((a) => a !== null),
|
|
200
|
+
}));
|
|
201
|
+
const listNode = { tag: "list", attrs: {}, content: userNodes };
|
|
112
202
|
const queryNode = {
|
|
113
|
-
tag:
|
|
203
|
+
tag: "query",
|
|
114
204
|
attrs: {},
|
|
115
|
-
content: usyncQuery.protocols.map(a => a.getQueryElement())
|
|
116
|
-
}
|
|
117
|
-
|
|
205
|
+
content: usyncQuery.protocols.map((a) => a.getQueryElement()),
|
|
206
|
+
};
|
|
118
207
|
const iq = {
|
|
119
|
-
tag:
|
|
120
|
-
attrs: {
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
index: '0'
|
|
208
|
+
tag: "iq",
|
|
209
|
+
attrs: { to: S_WHATSAPP_NET, type: "get", xmlns: "usync" },
|
|
210
|
+
content: [
|
|
211
|
+
{
|
|
212
|
+
tag: "usync",
|
|
213
|
+
attrs: {
|
|
214
|
+
context: usyncQuery.context,
|
|
215
|
+
mode: usyncQuery.mode,
|
|
216
|
+
sid: generateMessageTag(),
|
|
217
|
+
last: "true",
|
|
218
|
+
index: "0",
|
|
219
|
+
},
|
|
220
|
+
content: [queryNode, listNode],
|
|
133
221
|
},
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
usyncQuery.withUser(new WAUSync_1.USyncUser().withId(jid)) // intentional
|
|
222
|
+
],
|
|
223
|
+
};
|
|
224
|
+
const result = await query(iq);
|
|
225
|
+
return usyncQuery.parseUSyncQueryResult(result);
|
|
226
|
+
};
|
|
227
|
+
const onWhatsApp = async (...phoneNumber) => {
|
|
228
|
+
let usyncQuery = new USyncQuery();
|
|
229
|
+
let contactEnabled = false;
|
|
230
|
+
for (const jid of phoneNumber) {
|
|
231
|
+
if (isLidUser(jid)) {
|
|
232
|
+
logger?.warn("LIDs are not supported with onWhatsApp");
|
|
233
|
+
continue;
|
|
147
234
|
} else {
|
|
148
|
-
|
|
149
|
-
|
|
235
|
+
if (!contactEnabled) {
|
|
236
|
+
contactEnabled = true;
|
|
237
|
+
usyncQuery = usyncQuery.withContactProtocol();
|
|
238
|
+
}
|
|
239
|
+
const phone = `+${jid.replace("+", "").split("@")[0]?.split(":")[0]}`;
|
|
240
|
+
usyncQuery.withUser(new USyncUser().withPhone(phone));
|
|
150
241
|
}
|
|
151
242
|
}
|
|
152
|
-
|
|
243
|
+
if (usyncQuery.users.length === 0) {
|
|
244
|
+
return [];
|
|
245
|
+
}
|
|
246
|
+
const results = await executeUSyncQuery(usyncQuery);
|
|
153
247
|
if (results) {
|
|
154
|
-
if (results.list.filter(a => !!a.lid).length > 0) {
|
|
155
|
-
const lidOnly = results.list.filter(a => !!a.lid)
|
|
156
|
-
await signalRepository.lidMapping.storeLIDPNMappings(lidOnly.map(a => ({
|
|
157
|
-
pn: a.id,
|
|
158
|
-
lid: a.lid
|
|
159
|
-
})))
|
|
160
|
-
}
|
|
161
248
|
return results.list
|
|
162
|
-
.filter(a => !!a.contact)
|
|
163
|
-
.map(({
|
|
164
|
-
contact,
|
|
165
|
-
id,
|
|
166
|
-
lid
|
|
167
|
-
}) => ({
|
|
168
|
-
jid: id,
|
|
169
|
-
exists: contact,
|
|
170
|
-
lid: lid
|
|
171
|
-
}))
|
|
249
|
+
.filter((a) => !!a.contact)
|
|
250
|
+
.map(({ contact: contact, id: id }) => ({ jid: id, exists: contact }));
|
|
172
251
|
}
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
const noise = Utils_1.makeNoiseHandler({
|
|
183
|
-
keyPair: ephemeralKeyPair,
|
|
184
|
-
NOISE_HEADER: Defaults_1.NOISE_WA_HEADER,
|
|
185
|
-
logger,
|
|
186
|
-
routingInfo: authState?.creds?.routingInfo
|
|
187
|
-
})
|
|
188
|
-
|
|
189
|
-
const { creds } = authState
|
|
190
|
-
|
|
191
|
-
const keys = Utils_1.addTransactionCapability(authState.keys, logger, transactionOpts)
|
|
192
|
-
const signalRepository = makeSignalRepository({
|
|
193
|
-
creds,
|
|
194
|
-
keys
|
|
195
|
-
}, onWhatsApp, logger)
|
|
196
|
-
|
|
197
|
-
let lastDateRecv
|
|
198
|
-
let epoch = 1
|
|
199
|
-
let keepAliveReq
|
|
200
|
-
// FIX: sebelumnya uploadPreKeysToServerIfRequired() cuma dipanggil SEKALI pas
|
|
201
|
-
// 'CB:success' (baru connect/reconnect). Kalau proses bot jalan lama (berhari-hari)
|
|
202
|
-
// tanpa reconnect, prekey yang kepakai server gak pernah dicek ulang sampai jumlahnya
|
|
203
|
-
// abis - baru ketahuan pas sudah kejadian error decrypt di penerima. Interval ini
|
|
204
|
-
// bikin prekey selalu "terupdate", dicek ulang berkala tanpa perlu nunggu reconnect.
|
|
205
|
-
let preKeyCheckInterval
|
|
206
|
-
let qrTimer
|
|
207
|
-
let closed = false
|
|
208
|
-
|
|
209
|
-
const sendPromise = util_1.promisify(ws.send)
|
|
210
|
-
|
|
211
|
-
const sendRawMessage = async (data) => {
|
|
212
|
-
if (!ws.isOpen) {
|
|
213
|
-
throw new boom_1.Boom('Connection Closed', {
|
|
214
|
-
statusCode: Types_1.DisconnectReason.connectionClosed
|
|
215
|
-
})
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
const bytes = noise.encodeFrame(data)
|
|
219
|
-
await Utils_1.promiseTimeout(connectTimeoutMs, async (resolve, reject) => {
|
|
220
|
-
try {
|
|
221
|
-
await sendPromise.call(ws, bytes)
|
|
222
|
-
resolve()
|
|
223
|
-
} catch (error) {
|
|
224
|
-
reject(error)
|
|
252
|
+
};
|
|
253
|
+
const pnFromLIDUSync = async (jids) => {
|
|
254
|
+
const usyncQuery = new USyncQuery().withLIDProtocol().withContext("background");
|
|
255
|
+
for (const jid of jids) {
|
|
256
|
+
if (isLidUser(jid)) {
|
|
257
|
+
logger?.warn("LID user found in LID fetch call");
|
|
258
|
+
continue;
|
|
259
|
+
} else {
|
|
260
|
+
usyncQuery.withUser(new USyncUser().withId(jid));
|
|
225
261
|
}
|
|
226
|
-
})
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
const sendNode = (frame) => {
|
|
230
|
-
if (logger.level === 'trace') {
|
|
231
|
-
logger.trace({
|
|
232
|
-
xml: WABinary_1.binaryNodeToString(frame),
|
|
233
|
-
msg: 'xml send'
|
|
234
|
-
})
|
|
235
262
|
}
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
263
|
+
if (usyncQuery.users.length === 0) {
|
|
264
|
+
return [];
|
|
265
|
+
}
|
|
266
|
+
const results = await executeUSyncQuery(usyncQuery);
|
|
267
|
+
if (results) {
|
|
268
|
+
return results.list
|
|
269
|
+
.filter((a) => !!a.lid)
|
|
270
|
+
.map(({ lid: lid, id: id }) => ({ pn: id, lid: lid }));
|
|
271
|
+
}
|
|
272
|
+
return [];
|
|
273
|
+
};
|
|
274
|
+
const ev = makeEventBuffer(logger);
|
|
275
|
+
const { creds: creds } = authState;
|
|
276
|
+
const keys = addTransactionCapability(authState.keys, logger, transactionOpts);
|
|
277
|
+
const signalRepository = makeSignalRepository(
|
|
278
|
+
{ creds: creds, keys: keys },
|
|
279
|
+
logger,
|
|
280
|
+
pnFromLIDUSync
|
|
281
|
+
);
|
|
282
|
+
let lastDateRecv;
|
|
283
|
+
let epoch = 1;
|
|
284
|
+
let keepAliveReq;
|
|
285
|
+
let qrTimer;
|
|
286
|
+
let closed = false;
|
|
242
287
|
const onUnexpectedError = (err, msg) => {
|
|
243
|
-
logger.error({
|
|
244
|
-
|
|
245
|
-
}, `unexpected error in '${msg}'`)
|
|
246
|
-
}
|
|
247
|
-
|
|
288
|
+
logger.error({ err: err }, `unexpected error in '${msg}'`);
|
|
289
|
+
};
|
|
248
290
|
const awaitNextMessage = async (sendMsg) => {
|
|
249
291
|
if (!ws.isOpen) {
|
|
250
|
-
throw new
|
|
251
|
-
statusCode:
|
|
252
|
-
})
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
let
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
onOpen
|
|
260
|
-
onClose
|
|
261
|
-
ws.on(
|
|
262
|
-
ws.on('close', onClose)
|
|
263
|
-
ws.on('error', onClose)
|
|
292
|
+
throw new Boom("Connection Closed", {
|
|
293
|
+
statusCode: DisconnectReason.connectionClosed,
|
|
294
|
+
});
|
|
295
|
+
}
|
|
296
|
+
let onOpen;
|
|
297
|
+
let onClose;
|
|
298
|
+
const result = promiseTimeout(connectTimeoutMs, (resolve, reject) => {
|
|
299
|
+
onOpen = resolve;
|
|
300
|
+
onClose = mapWebSocketError(reject);
|
|
301
|
+
ws.on("frame", onOpen);
|
|
302
|
+
ws.on("close", onClose);
|
|
303
|
+
ws.on("error", onClose);
|
|
264
304
|
}).finally(() => {
|
|
265
|
-
ws.off(
|
|
266
|
-
ws.off(
|
|
267
|
-
ws.off(
|
|
268
|
-
})
|
|
269
|
-
|
|
305
|
+
ws.off("frame", onOpen);
|
|
306
|
+
ws.off("close", onClose);
|
|
307
|
+
ws.off("error", onClose);
|
|
308
|
+
});
|
|
270
309
|
if (sendMsg) {
|
|
271
|
-
sendRawMessage(sendMsg).catch(onClose)
|
|
310
|
+
sendRawMessage(sendMsg).catch(onClose);
|
|
272
311
|
}
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
}
|
|
276
|
-
|
|
312
|
+
return result;
|
|
313
|
+
};
|
|
277
314
|
const validateConnection = async () => {
|
|
278
|
-
let helloMsg = {
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
helloMsg
|
|
288
|
-
}, 'connected to WA')
|
|
289
|
-
|
|
290
|
-
const init = WAProto_1.proto.HandshakeMessage.encode(helloMsg).finish()
|
|
291
|
-
const result = await awaitNextMessage(init)
|
|
292
|
-
const handshake = WAProto_1.proto.HandshakeMessage.decode(result)
|
|
293
|
-
|
|
294
|
-
logger.trace({
|
|
295
|
-
handshake
|
|
296
|
-
}, 'handshake recv from WA')
|
|
297
|
-
|
|
298
|
-
const keyEnc = await noise.processHandshake(handshake, creds.noiseKey)
|
|
299
|
-
let node
|
|
300
|
-
|
|
315
|
+
let helloMsg = { clientHello: { ephemeral: ephemeralKeyPair.public } };
|
|
316
|
+
helloMsg = proto.HandshakeMessage.fromObject(helloMsg);
|
|
317
|
+
logger.info({ browser: browser, helloMsg: helloMsg }, "connected to WA");
|
|
318
|
+
const init = proto.HandshakeMessage.encode(helloMsg).finish();
|
|
319
|
+
const result = await awaitNextMessage(init);
|
|
320
|
+
const handshake = proto.HandshakeMessage.decode(result);
|
|
321
|
+
logger.trace({ handshake: handshake }, "handshake recv from WA");
|
|
322
|
+
const keyEnc = await noise.processHandshake(handshake, creds.noiseKey);
|
|
323
|
+
let node;
|
|
301
324
|
if (!creds.me) {
|
|
302
|
-
node =
|
|
303
|
-
logger.info({
|
|
304
|
-
node
|
|
305
|
-
}, 'not logged in, attempting registration...')
|
|
325
|
+
node = generateRegistrationNode(creds, config);
|
|
326
|
+
logger.info({ node: node }, "not logged in, attempting registration...");
|
|
306
327
|
} else {
|
|
307
|
-
node =
|
|
308
|
-
logger.info({
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
}).finish())
|
|
320
|
-
noise.finishInit()
|
|
321
|
-
startKeepAliveRequest()
|
|
322
|
-
}
|
|
323
|
-
|
|
328
|
+
node = generateLoginNode(creds.me.id, config);
|
|
329
|
+
logger.info({ node: node }, "logging in...");
|
|
330
|
+
}
|
|
331
|
+
const payloadEnc = noise.encrypt(proto.ClientPayload.encode(node).finish());
|
|
332
|
+
await sendRawMessage(
|
|
333
|
+
proto.HandshakeMessage.encode({
|
|
334
|
+
clientFinish: { static: keyEnc, payload: payloadEnc },
|
|
335
|
+
}).finish()
|
|
336
|
+
);
|
|
337
|
+
await noise.finishInit();
|
|
338
|
+
startKeepAliveRequest();
|
|
339
|
+
};
|
|
324
340
|
const getAvailablePreKeysOnServer = async () => {
|
|
325
341
|
const result = await query({
|
|
326
|
-
tag:
|
|
342
|
+
tag: "iq",
|
|
327
343
|
attrs: {
|
|
328
344
|
id: generateMessageTag(),
|
|
329
|
-
xmlns:
|
|
330
|
-
type:
|
|
331
|
-
to:
|
|
345
|
+
xmlns: "encrypt",
|
|
346
|
+
type: "get",
|
|
347
|
+
to: S_WHATSAPP_NET,
|
|
332
348
|
},
|
|
333
|
-
content: [{
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
return +countChild.attrs.value
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
let uploadPreKeysPromise = null
|
|
345
|
-
let lastUploadTime = 0
|
|
346
|
-
|
|
347
|
-
const uploadPreKeys = async (count = Defaults_1.INITIAL_PREKEY_COUNT, retryCount = 0) => {
|
|
349
|
+
content: [{ tag: "count", attrs: {} }],
|
|
350
|
+
});
|
|
351
|
+
const countChild = getBinaryNodeChild(result, "count");
|
|
352
|
+
return +countChild.attrs.value;
|
|
353
|
+
};
|
|
354
|
+
let uploadPreKeysPromise = null;
|
|
355
|
+
let lastUploadTime = 0;
|
|
356
|
+
const uploadPreKeys = async (count = MIN_PREKEY_COUNT, retryCount = 0) => {
|
|
348
357
|
if (retryCount === 0) {
|
|
349
|
-
const timeSinceLastUpload = Date.now() - lastUploadTime
|
|
350
|
-
if (timeSinceLastUpload <
|
|
351
|
-
logger.debug(`Skipping upload, only ${timeSinceLastUpload}ms since last upload`)
|
|
352
|
-
return
|
|
358
|
+
const timeSinceLastUpload = Date.now() - lastUploadTime;
|
|
359
|
+
if (timeSinceLastUpload < MIN_UPLOAD_INTERVAL) {
|
|
360
|
+
logger.debug(`Skipping upload, only ${timeSinceLastUpload}ms since last upload`);
|
|
361
|
+
return;
|
|
353
362
|
}
|
|
354
363
|
}
|
|
355
|
-
|
|
356
364
|
if (uploadPreKeysPromise) {
|
|
357
|
-
logger.debug(
|
|
358
|
-
|
|
365
|
+
logger.debug("Pre-key upload already in progress, waiting for completion");
|
|
366
|
+
await uploadPreKeysPromise;
|
|
359
367
|
}
|
|
360
|
-
|
|
361
368
|
const uploadLogic = async () => {
|
|
362
|
-
logger.info({
|
|
363
|
-
count,
|
|
364
|
-
retryCount
|
|
365
|
-
}, 'uploading pre-keys')
|
|
366
|
-
|
|
369
|
+
logger.info({ count: count, retryCount: retryCount }, "uploading pre-keys");
|
|
367
370
|
const node = await keys.transaction(async () => {
|
|
368
|
-
logger.debug({
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
const {
|
|
372
|
-
update,
|
|
373
|
-
node
|
|
374
|
-
} = await Utils_1.getNextPreKeysNode({
|
|
375
|
-
creds,
|
|
376
|
-
keys
|
|
377
|
-
}, count)
|
|
378
|
-
|
|
379
|
-
ev.emit('creds.update', update)
|
|
380
|
-
|
|
381
|
-
return node
|
|
382
|
-
}, creds?.me?.id || 'upload-pre-keys')
|
|
383
|
-
|
|
384
|
-
try {
|
|
385
|
-
await query(node)
|
|
386
|
-
logger.info({
|
|
371
|
+
logger.debug({ requestedCount: count }, "generating pre-keys with requested count");
|
|
372
|
+
const { update: update, node: node } = await getNextPreKeysNode(
|
|
373
|
+
{ creds: creds, keys: keys },
|
|
387
374
|
count
|
|
388
|
-
|
|
389
|
-
|
|
375
|
+
);
|
|
376
|
+
ev.emit("creds.update", update);
|
|
377
|
+
return node;
|
|
378
|
+
}, creds?.me?.id || "upload-pre-keys");
|
|
379
|
+
try {
|
|
380
|
+
await query(node);
|
|
381
|
+
logger.info({ count: count }, "uploaded pre-keys successfully");
|
|
382
|
+
lastUploadTime = Date.now();
|
|
390
383
|
} catch (uploadError) {
|
|
391
|
-
logger.error(
|
|
392
|
-
uploadError,
|
|
393
|
-
|
|
394
|
-
|
|
384
|
+
logger.error(
|
|
385
|
+
{ uploadError: uploadError.toString(), count: count },
|
|
386
|
+
"Failed to upload pre-keys to server"
|
|
387
|
+
);
|
|
395
388
|
if (retryCount < 3) {
|
|
396
|
-
const backoffDelay = Math.min(
|
|
397
|
-
logger.info(`Retrying pre-key upload in ${backoffDelay}ms`)
|
|
398
|
-
await new Promise(resolve => setTimeout(resolve, backoffDelay))
|
|
399
|
-
return uploadPreKeys(count, retryCount + 1)
|
|
389
|
+
const backoffDelay = Math.min(1e3 * Math.pow(2, retryCount), 1e4);
|
|
390
|
+
logger.info(`Retrying pre-key upload in ${backoffDelay}ms`);
|
|
391
|
+
await new Promise((resolve) => setTimeout(resolve, backoffDelay));
|
|
392
|
+
return uploadPreKeys(count, retryCount + 1);
|
|
400
393
|
}
|
|
401
|
-
throw uploadError
|
|
394
|
+
throw uploadError;
|
|
402
395
|
}
|
|
403
|
-
}
|
|
404
|
-
|
|
396
|
+
};
|
|
405
397
|
uploadPreKeysPromise = Promise.race([
|
|
406
398
|
uploadLogic(),
|
|
407
|
-
new Promise((_, reject) =>
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
399
|
+
new Promise((_, reject) =>
|
|
400
|
+
setTimeout(
|
|
401
|
+
() => reject(new Boom("Pre-key upload timeout", { statusCode: 408 })),
|
|
402
|
+
UPLOAD_TIMEOUT
|
|
403
|
+
)
|
|
404
|
+
),
|
|
405
|
+
]);
|
|
411
406
|
try {
|
|
412
|
-
await uploadPreKeysPromise
|
|
407
|
+
await uploadPreKeysPromise;
|
|
413
408
|
} finally {
|
|
414
|
-
uploadPreKeysPromise = null
|
|
409
|
+
uploadPreKeysPromise = null;
|
|
415
410
|
}
|
|
416
|
-
}
|
|
417
|
-
|
|
411
|
+
};
|
|
418
412
|
const verifyCurrentPreKeyExists = async () => {
|
|
419
|
-
const currentPreKeyId = creds.nextPreKeyId - 1
|
|
413
|
+
const currentPreKeyId = creds.nextPreKeyId - 1;
|
|
420
414
|
if (currentPreKeyId <= 0) {
|
|
421
|
-
return {
|
|
422
|
-
exists: false,
|
|
423
|
-
currentPreKeyId: 0
|
|
424
|
-
}
|
|
415
|
+
return { exists: false, currentPreKeyId: 0 };
|
|
425
416
|
}
|
|
426
|
-
const preKeys = await keys.get(
|
|
427
|
-
const exists = !!preKeys[currentPreKeyId.toString()]
|
|
428
|
-
return {
|
|
429
|
-
|
|
430
|
-
currentPreKeyId
|
|
431
|
-
}
|
|
432
|
-
}
|
|
433
|
-
|
|
417
|
+
const preKeys = await keys.get("pre-key", [currentPreKeyId.toString()]);
|
|
418
|
+
const exists = !!preKeys[currentPreKeyId.toString()];
|
|
419
|
+
return { exists: exists, currentPreKeyId: currentPreKeyId };
|
|
420
|
+
};
|
|
434
421
|
const uploadPreKeysToServerIfRequired = async () => {
|
|
435
422
|
try {
|
|
436
|
-
|
|
437
|
-
const
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
} =
|
|
441
|
-
|
|
442
|
-
logger.info(`${preKeyCount} pre-keys found on server`)
|
|
443
|
-
logger.info(
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
const
|
|
447
|
-
const
|
|
448
|
-
|
|
423
|
+
let count = 0;
|
|
424
|
+
const preKeyCount = await getAvailablePreKeysOnServer();
|
|
425
|
+
if (preKeyCount === 0) count = INITIAL_PREKEY_COUNT;
|
|
426
|
+
else count = MIN_PREKEY_COUNT;
|
|
427
|
+
const { exists: currentPreKeyExists, currentPreKeyId: currentPreKeyId } =
|
|
428
|
+
await verifyCurrentPreKeyExists();
|
|
429
|
+
logger.info(`${preKeyCount} pre-keys found on server`);
|
|
430
|
+
logger.info(
|
|
431
|
+
`Current prekey ID: ${currentPreKeyId}, exists in storage: ${currentPreKeyExists}`
|
|
432
|
+
);
|
|
433
|
+
const lowServerCount = preKeyCount <= count;
|
|
434
|
+
const missingCurrentPreKey = !currentPreKeyExists && currentPreKeyId > 0;
|
|
435
|
+
const shouldUpload = lowServerCount || missingCurrentPreKey;
|
|
449
436
|
if (shouldUpload) {
|
|
450
|
-
const reasons = []
|
|
451
|
-
if (lowServerCount)
|
|
452
|
-
reasons.push(`server count low (${preKeyCount})`)
|
|
437
|
+
const reasons = [];
|
|
438
|
+
if (lowServerCount) reasons.push(`server count low (${preKeyCount})`);
|
|
453
439
|
if (missingCurrentPreKey)
|
|
454
|
-
reasons.push(`current prekey ${currentPreKeyId} missing from storage`)
|
|
455
|
-
logger.info(`Uploading PreKeys due to: ${reasons.join(
|
|
456
|
-
await uploadPreKeys()
|
|
440
|
+
reasons.push(`current prekey ${currentPreKeyId} missing from storage`);
|
|
441
|
+
logger.info(`Uploading PreKeys due to: ${reasons.join(", ")}`);
|
|
442
|
+
await uploadPreKeys(count);
|
|
457
443
|
} else {
|
|
458
|
-
logger.info(
|
|
444
|
+
logger.info(
|
|
445
|
+
`PreKey validation passed - Server: ${preKeyCount}, Current prekey ${currentPreKeyId} exists`
|
|
446
|
+
);
|
|
459
447
|
}
|
|
460
448
|
} catch (error) {
|
|
461
|
-
logger.error({
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
lastDateRecv = new Date()
|
|
471
|
-
let anyTriggered = false
|
|
472
|
-
anyTriggered = ws.emit('frame', frame)
|
|
473
|
-
|
|
449
|
+
logger.error({ error: error }, "Failed to check/upload pre-keys during initialization");
|
|
450
|
+
}
|
|
451
|
+
};
|
|
452
|
+
const onMessageReceived = async (data) => {
|
|
453
|
+
await noise.decodeFrame(data, (frame) => {
|
|
454
|
+
lastDateRecv = new Date();
|
|
455
|
+
let anyTriggered = false;
|
|
456
|
+
anyTriggered = ws.emit("frame", frame);
|
|
474
457
|
if (!(frame instanceof Uint8Array)) {
|
|
475
|
-
const msgId = frame.attrs.id
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
logger.trace({
|
|
479
|
-
xml: WABinary_1.binaryNodeToString(frame),
|
|
480
|
-
msg: 'recv xml'
|
|
481
|
-
})
|
|
458
|
+
const msgId = frame.attrs.id;
|
|
459
|
+
if (logger.level === "trace") {
|
|
460
|
+
logger.trace({ xml: binaryNodeToString(frame), msg: "recv xml" });
|
|
482
461
|
}
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
const
|
|
487
|
-
const l1 = frame.attrs || {}
|
|
488
|
-
const l2 = Array.isArray(frame.content) ? frame.content[0]?.tag : ''
|
|
489
|
-
|
|
462
|
+
anyTriggered = ws.emit(`${DEF_TAG_PREFIX}${msgId}`, frame) || anyTriggered;
|
|
463
|
+
const l0 = frame.tag;
|
|
464
|
+
const l1 = frame.attrs || {};
|
|
465
|
+
const l2 = Array.isArray(frame.content) ? frame.content[0]?.tag : "";
|
|
490
466
|
for (const key of Object.keys(l1)) {
|
|
491
|
-
anyTriggered =
|
|
492
|
-
|
|
493
|
-
|
|
467
|
+
anyTriggered =
|
|
468
|
+
ws.emit(`${DEF_CALLBACK_PREFIX}${l0},${key}:${l1[key]},${l2}`, frame) ||
|
|
469
|
+
anyTriggered;
|
|
470
|
+
anyTriggered =
|
|
471
|
+
ws.emit(`${DEF_CALLBACK_PREFIX}${l0},${key}:${l1[key]}`, frame) ||
|
|
472
|
+
anyTriggered;
|
|
473
|
+
anyTriggered =
|
|
474
|
+
ws.emit(`${DEF_CALLBACK_PREFIX}${l0},${key}`, frame) || anyTriggered;
|
|
494
475
|
}
|
|
495
|
-
|
|
496
|
-
anyTriggered = ws.emit(`${
|
|
497
|
-
anyTriggered
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
msgId,
|
|
503
|
-
fromMe: false,
|
|
504
|
-
frame
|
|
505
|
-
}, 'communication recv')
|
|
476
|
+
anyTriggered = ws.emit(`${DEF_CALLBACK_PREFIX}${l0},,${l2}`, frame) || anyTriggered;
|
|
477
|
+
anyTriggered = ws.emit(`${DEF_CALLBACK_PREFIX}${l0}`, frame) || anyTriggered;
|
|
478
|
+
if (!anyTriggered && logger.level === "debug") {
|
|
479
|
+
logger.debug(
|
|
480
|
+
{ unhandled: true, msgId: msgId, fromMe: false, frame: frame },
|
|
481
|
+
"communication recv"
|
|
482
|
+
);
|
|
506
483
|
}
|
|
507
484
|
}
|
|
508
|
-
})
|
|
509
|
-
}
|
|
510
|
-
|
|
485
|
+
});
|
|
486
|
+
};
|
|
511
487
|
const end = (error) => {
|
|
512
488
|
if (closed) {
|
|
513
|
-
logger.trace({
|
|
514
|
-
|
|
515
|
-
}, 'connection already closed')
|
|
516
|
-
return
|
|
489
|
+
logger.trace({ trace: error?.stack }, "connection already closed");
|
|
490
|
+
return;
|
|
517
491
|
}
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
clearInterval(preKeyCheckInterval)
|
|
526
|
-
clearTimeout(qrTimer)
|
|
527
|
-
ws.removeAllListeners('close')
|
|
528
|
-
ws.removeAllListeners('open')
|
|
529
|
-
ws.removeAllListeners('message')
|
|
530
|
-
|
|
492
|
+
closed = true;
|
|
493
|
+
logger.info({ trace: error?.stack }, error ? "connection errored" : "connection closed");
|
|
494
|
+
clearInterval(keepAliveReq);
|
|
495
|
+
clearTimeout(qrTimer);
|
|
496
|
+
ws.removeAllListeners("close");
|
|
497
|
+
ws.removeAllListeners("open");
|
|
498
|
+
ws.removeAllListeners("message");
|
|
531
499
|
if (!ws.isClosed && !ws.isClosing) {
|
|
532
500
|
try {
|
|
533
|
-
ws.close()
|
|
534
|
-
} catch
|
|
535
|
-
}
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
}
|
|
543
|
-
})
|
|
544
|
-
|
|
545
|
-
ev.removeAllListeners('connection.update')
|
|
546
|
-
}
|
|
547
|
-
|
|
501
|
+
ws.close();
|
|
502
|
+
} catch {}
|
|
503
|
+
}
|
|
504
|
+
ev.emit("connection.update", {
|
|
505
|
+
connection: "close",
|
|
506
|
+
lastDisconnect: { error: error, date: new Date() },
|
|
507
|
+
});
|
|
508
|
+
ev.removeAllListeners("connection.update");
|
|
509
|
+
};
|
|
548
510
|
const waitForSocketOpen = async () => {
|
|
549
511
|
if (ws.isOpen) {
|
|
550
|
-
return
|
|
512
|
+
return;
|
|
551
513
|
}
|
|
552
|
-
|
|
553
514
|
if (ws.isClosed || ws.isClosing) {
|
|
554
|
-
throw new
|
|
555
|
-
statusCode:
|
|
556
|
-
})
|
|
515
|
+
throw new Boom("Connection Closed", {
|
|
516
|
+
statusCode: DisconnectReason.connectionClosed,
|
|
517
|
+
});
|
|
557
518
|
}
|
|
558
|
-
|
|
559
|
-
let
|
|
560
|
-
let onClose
|
|
561
|
-
|
|
519
|
+
let onOpen;
|
|
520
|
+
let onClose;
|
|
562
521
|
await new Promise((resolve, reject) => {
|
|
563
|
-
onOpen = () => resolve(undefined)
|
|
564
|
-
onClose = mapWebSocketError(reject)
|
|
565
|
-
ws.on(
|
|
566
|
-
ws.on(
|
|
567
|
-
ws.on(
|
|
522
|
+
onOpen = () => resolve(undefined);
|
|
523
|
+
onClose = mapWebSocketError(reject);
|
|
524
|
+
ws.on("open", onOpen);
|
|
525
|
+
ws.on("close", onClose);
|
|
526
|
+
ws.on("error", onClose);
|
|
568
527
|
}).finally(() => {
|
|
569
|
-
ws.off(
|
|
570
|
-
ws.off(
|
|
571
|
-
ws.off(
|
|
572
|
-
})
|
|
573
|
-
}
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
}
|
|
599
|
-
}
|
|
600
|
-
logger.
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
const sendPassiveIq = (tag) => (query({
|
|
610
|
-
tag: 'iq',
|
|
611
|
-
attrs: {
|
|
612
|
-
to: WABinary_1.S_WHATSAPP_NET,
|
|
613
|
-
xmlns: 'passive',
|
|
614
|
-
type: 'set',
|
|
615
|
-
},
|
|
616
|
-
content: [{
|
|
617
|
-
tag,
|
|
618
|
-
attrs: {}
|
|
619
|
-
}]
|
|
620
|
-
}))
|
|
621
|
-
|
|
528
|
+
ws.off("open", onOpen);
|
|
529
|
+
ws.off("close", onClose);
|
|
530
|
+
ws.off("error", onClose);
|
|
531
|
+
});
|
|
532
|
+
};
|
|
533
|
+
const startKeepAliveRequest = () =>
|
|
534
|
+
(keepAliveReq = setInterval(() => {
|
|
535
|
+
if (!lastDateRecv) {
|
|
536
|
+
lastDateRecv = new Date();
|
|
537
|
+
}
|
|
538
|
+
const diff = Date.now() - lastDateRecv.getTime();
|
|
539
|
+
if (diff > keepAliveIntervalMs + 5e3) {
|
|
540
|
+
end(
|
|
541
|
+
new Boom("Connection was lost", {
|
|
542
|
+
statusCode: DisconnectReason.connectionLost,
|
|
543
|
+
})
|
|
544
|
+
);
|
|
545
|
+
} else if (ws.isOpen) {
|
|
546
|
+
query({
|
|
547
|
+
tag: "iq",
|
|
548
|
+
attrs: {
|
|
549
|
+
id: generateMessageTag(),
|
|
550
|
+
to: S_WHATSAPP_NET,
|
|
551
|
+
type: "get",
|
|
552
|
+
xmlns: "w:p",
|
|
553
|
+
},
|
|
554
|
+
content: [{ tag: "ping", attrs: {} }],
|
|
555
|
+
}).catch((err) => {
|
|
556
|
+
logger.error({ trace: err.stack }, "error in sending keep alive");
|
|
557
|
+
});
|
|
558
|
+
} else {
|
|
559
|
+
logger.warn("keep alive called when WS not open");
|
|
560
|
+
}
|
|
561
|
+
}, keepAliveIntervalMs));
|
|
562
|
+
const sendPassiveIq = (tag) =>
|
|
563
|
+
query({
|
|
564
|
+
tag: "iq",
|
|
565
|
+
attrs: { to: S_WHATSAPP_NET, xmlns: "passive", type: "set" },
|
|
566
|
+
content: [{ tag: tag, attrs: {} }],
|
|
567
|
+
});
|
|
622
568
|
const logout = async (msg) => {
|
|
623
|
-
const jid = authState.creds.me?.id
|
|
624
|
-
|
|
569
|
+
const jid = authState.creds.me?.id;
|
|
625
570
|
if (jid) {
|
|
626
571
|
await sendNode({
|
|
627
|
-
tag:
|
|
572
|
+
tag: "iq",
|
|
628
573
|
attrs: {
|
|
629
|
-
to:
|
|
630
|
-
type:
|
|
574
|
+
to: S_WHATSAPP_NET,
|
|
575
|
+
type: "set",
|
|
631
576
|
id: generateMessageTag(),
|
|
632
|
-
xmlns:
|
|
577
|
+
xmlns: "md",
|
|
633
578
|
},
|
|
634
|
-
content: [
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
jid,
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
})
|
|
579
|
+
content: [
|
|
580
|
+
{
|
|
581
|
+
tag: "remove-companion-device",
|
|
582
|
+
attrs: { jid: jid, reason: "user_initiated" },
|
|
583
|
+
},
|
|
584
|
+
],
|
|
585
|
+
});
|
|
642
586
|
}
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
587
|
+
end(
|
|
588
|
+
new Boom(msg || "Intentional Logout", {
|
|
589
|
+
statusCode: DisconnectReason.loggedOut,
|
|
590
|
+
})
|
|
591
|
+
);
|
|
592
|
+
};
|
|
649
593
|
const requestPairingCode = async (phoneNumber, code) => {
|
|
650
|
-
authState.creds.pairingCode =
|
|
651
|
-
|
|
652
|
-
|
|
594
|
+
authState.creds.pairingCode =
|
|
595
|
+
code?.toUpperCase() || asciiDecode([81, 87, 69, 82, 84, 89, 85, 73]);
|
|
653
596
|
authState.creds.me = {
|
|
654
|
-
id:
|
|
655
|
-
name:
|
|
656
|
-
}
|
|
657
|
-
|
|
658
|
-
ev.emit('creds.update', authState.creds)
|
|
659
|
-
|
|
597
|
+
id: jidEncode(phoneNumber, "s.whatsapp.net"),
|
|
598
|
+
name: "~",
|
|
599
|
+
};
|
|
600
|
+
ev.emit("creds.update", authState.creds);
|
|
660
601
|
await sendNode({
|
|
661
|
-
tag:
|
|
602
|
+
tag: "iq",
|
|
662
603
|
attrs: {
|
|
663
|
-
to:
|
|
664
|
-
type:
|
|
604
|
+
to: S_WHATSAPP_NET,
|
|
605
|
+
type: "set",
|
|
665
606
|
id: generateMessageTag(),
|
|
666
|
-
xmlns:
|
|
607
|
+
xmlns: "md",
|
|
667
608
|
},
|
|
668
|
-
content: [
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
content: [{
|
|
676
|
-
tag: 'link_code_pairing_wrapped_companion_ephemeral_pub',
|
|
677
|
-
attrs: {},
|
|
678
|
-
content: await generatePairingKey()
|
|
679
|
-
},
|
|
680
|
-
{
|
|
681
|
-
tag: 'companion_server_auth_key_pub',
|
|
682
|
-
attrs: {},
|
|
683
|
-
content: authState.creds.noiseKey.public
|
|
684
|
-
},
|
|
685
|
-
{
|
|
686
|
-
tag: 'companion_platform_id',
|
|
687
|
-
attrs: {},
|
|
688
|
-
content: Utils_1.getPlatformId(browser[1])
|
|
689
|
-
},
|
|
690
|
-
{
|
|
691
|
-
tag: 'companion_platform_display',
|
|
692
|
-
attrs: {},
|
|
693
|
-
content: `${browser[1]} (${browser[0]})`
|
|
609
|
+
content: [
|
|
610
|
+
{
|
|
611
|
+
tag: "link_code_companion_reg",
|
|
612
|
+
attrs: {
|
|
613
|
+
jid: authState.creds.me.id,
|
|
614
|
+
stage: "companion_hello",
|
|
615
|
+
should_show_push_notification: "true",
|
|
694
616
|
},
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
617
|
+
content: [
|
|
618
|
+
{
|
|
619
|
+
tag: "link_code_pairing_wrapped_companion_ephemeral_pub",
|
|
620
|
+
attrs: {},
|
|
621
|
+
content: await generatePairingKey(),
|
|
622
|
+
},
|
|
623
|
+
{
|
|
624
|
+
tag: "companion_server_auth_key_pub",
|
|
625
|
+
attrs: {},
|
|
626
|
+
content: authState.creds.noiseKey.public,
|
|
627
|
+
},
|
|
628
|
+
{
|
|
629
|
+
tag: "companion_platform_id",
|
|
630
|
+
attrs: {},
|
|
631
|
+
content: getPlatformId(browser[1]),
|
|
632
|
+
},
|
|
633
|
+
{
|
|
634
|
+
tag: "companion_platform_display",
|
|
635
|
+
attrs: {},
|
|
636
|
+
content: `${browser[1]} (${browser[0]})`,
|
|
637
|
+
},
|
|
638
|
+
{ tag: "link_code_pairing_nonce", attrs: {}, content: "0" },
|
|
639
|
+
],
|
|
640
|
+
},
|
|
641
|
+
],
|
|
642
|
+
});
|
|
643
|
+
return authState.creds.pairingCode;
|
|
644
|
+
};
|
|
707
645
|
async function generatePairingKey() {
|
|
708
|
-
const salt =
|
|
709
|
-
const randomIv =
|
|
710
|
-
const key = await
|
|
711
|
-
const ciphered =
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
return query({
|
|
718
|
-
tag: 'iq',
|
|
719
|
-
attrs: {
|
|
720
|
-
to: WABinary_1.S_WHATSAPP_NET,
|
|
721
|
-
id: generateMessageTag(),
|
|
722
|
-
xmlns: 'w:stats'
|
|
723
|
-
},
|
|
724
|
-
content: [{
|
|
725
|
-
tag: 'add',
|
|
726
|
-
attrs: {},
|
|
727
|
-
content: wamBuffer
|
|
728
|
-
}]
|
|
729
|
-
})
|
|
646
|
+
const salt = randomBytes(32);
|
|
647
|
+
const randomIv = randomBytes(16);
|
|
648
|
+
const key = await derivePairingCodeKey(authState.creds.pairingCode, salt);
|
|
649
|
+
const ciphered = aesEncryptCTR(
|
|
650
|
+
authState.creds.pairingEphemeralKeyPair.public,
|
|
651
|
+
key,
|
|
652
|
+
randomIv
|
|
653
|
+
);
|
|
654
|
+
return Buffer.concat([salt, randomIv, ciphered]);
|
|
730
655
|
}
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
656
|
+
const sendWAMBuffer = (wamBuffer) =>
|
|
657
|
+
query({
|
|
658
|
+
tag: "iq",
|
|
659
|
+
attrs: { to: S_WHATSAPP_NET, id: generateMessageTag(), xmlns: "w:stats" },
|
|
660
|
+
content: [
|
|
661
|
+
{
|
|
662
|
+
tag: "add",
|
|
663
|
+
attrs: { t: Math.round(Date.now() / 1e3) + "" },
|
|
664
|
+
content: wamBuffer,
|
|
665
|
+
},
|
|
666
|
+
],
|
|
667
|
+
});
|
|
668
|
+
ws.on("message", onMessageReceived);
|
|
669
|
+
ws.on("open", async () => {
|
|
735
670
|
try {
|
|
736
|
-
await validateConnection()
|
|
671
|
+
await validateConnection();
|
|
737
672
|
} catch (err) {
|
|
738
|
-
logger.error({
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
673
|
+
logger.error({ err: err }, "error in validating connection");
|
|
674
|
+
end(err);
|
|
675
|
+
}
|
|
676
|
+
});
|
|
677
|
+
ws.on("error", mapWebSocketError(end));
|
|
678
|
+
ws.on("close", () =>
|
|
679
|
+
end(
|
|
680
|
+
new Boom("Connection Terminated", {
|
|
681
|
+
statusCode: DisconnectReason.connectionClosed,
|
|
682
|
+
})
|
|
683
|
+
)
|
|
684
|
+
);
|
|
685
|
+
ws.on("CB:xmlstreamend", () =>
|
|
686
|
+
end(
|
|
687
|
+
new Boom("Connection Terminated by Server", {
|
|
688
|
+
statusCode: DisconnectReason.connectionClosed,
|
|
689
|
+
})
|
|
690
|
+
)
|
|
691
|
+
);
|
|
692
|
+
ws.on("CB:iq,type:set,pair-device", async (stanza) => {
|
|
756
693
|
const iq = {
|
|
757
|
-
tag:
|
|
758
|
-
attrs: {
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
const pairDeviceNode = WABinary_1.getBinaryNodeChild(stanza, 'pair-device')
|
|
768
|
-
const refNodes = WABinary_1.getBinaryNodeChildren(pairDeviceNode, 'ref')
|
|
769
|
-
const noiseKeyB64 = Buffer.from(creds.noiseKey.public).toString('base64')
|
|
770
|
-
const identityKeyB64 = Buffer.from(creds.signedIdentityKey.public).toString('base64')
|
|
771
|
-
const advB64 = creds.advSecretKey
|
|
772
|
-
|
|
773
|
-
let qrMs = qrTimeout || 60000 // time to let a QR live
|
|
774
|
-
|
|
694
|
+
tag: "iq",
|
|
695
|
+
attrs: { to: S_WHATSAPP_NET, type: "result", id: stanza.attrs.id },
|
|
696
|
+
};
|
|
697
|
+
await sendNode(iq);
|
|
698
|
+
const pairDeviceNode = getBinaryNodeChild(stanza, "pair-device");
|
|
699
|
+
const refNodes = getBinaryNodeChildren(pairDeviceNode, "ref");
|
|
700
|
+
const noiseKeyB64 = Buffer.from(creds.noiseKey.public).toString("base64");
|
|
701
|
+
const identityKeyB64 = Buffer.from(creds.signedIdentityKey.public).toString("base64");
|
|
702
|
+
const advB64 = creds.advSecretKey;
|
|
703
|
+
let qrMs = qrTimeout || 6e4;
|
|
775
704
|
const genPairQR = () => {
|
|
776
705
|
if (!ws.isOpen) {
|
|
777
|
-
return
|
|
706
|
+
return;
|
|
778
707
|
}
|
|
779
|
-
|
|
780
|
-
const refNode = refNodes.shift()
|
|
781
|
-
|
|
708
|
+
const refNode = refNodes.shift();
|
|
782
709
|
if (!refNode) {
|
|
783
|
-
end(
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
710
|
+
end(
|
|
711
|
+
new Boom("QR refs attempts ended", {
|
|
712
|
+
statusCode: DisconnectReason.timedOut,
|
|
713
|
+
})
|
|
714
|
+
);
|
|
715
|
+
return;
|
|
787
716
|
}
|
|
788
|
-
|
|
789
|
-
const
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
genPairQR()
|
|
800
|
-
})
|
|
801
|
-
|
|
802
|
-
ws.on('CB:iq,,pair-success', async (stanza) => {
|
|
803
|
-
logger.debug('pair success recv')
|
|
717
|
+
const ref = refNode.content.toString("utf-8");
|
|
718
|
+
const qr = [ref, noiseKeyB64, identityKeyB64, advB64].join(",");
|
|
719
|
+
ev.emit("connection.update", { qr: qr });
|
|
720
|
+
qrTimer = setTimeout(genPairQR, qrMs);
|
|
721
|
+
qrMs = qrTimeout || 2e4;
|
|
722
|
+
};
|
|
723
|
+
genPairQR();
|
|
724
|
+
});
|
|
725
|
+
ws.on("CB:iq,,pair-success", async (stanza) => {
|
|
726
|
+
logger.debug("pair success recv");
|
|
804
727
|
try {
|
|
805
|
-
const {
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
ev.emit('creds.update', updatedCreds)
|
|
815
|
-
ev.emit('connection.update', {
|
|
816
|
-
isNewLogin: true,
|
|
817
|
-
qr: undefined
|
|
818
|
-
})
|
|
819
|
-
|
|
820
|
-
await sendNode(reply)
|
|
728
|
+
const { reply: reply, creds: updatedCreds } = configureSuccessfulPairing(stanza, creds);
|
|
729
|
+
logger.info(
|
|
730
|
+
{ me: updatedCreds.me, platform: updatedCreds.platform },
|
|
731
|
+
"pairing configured successfully, expect to restart the connection..."
|
|
732
|
+
);
|
|
733
|
+
ev.emit("creds.update", updatedCreds);
|
|
734
|
+
ev.emit("connection.update", { isNewLogin: true, qr: undefined });
|
|
735
|
+
await sendNode(reply);
|
|
821
736
|
} catch (error) {
|
|
822
|
-
logger.info({
|
|
823
|
-
|
|
824
|
-
}, 'error in pairing')
|
|
825
|
-
end(error)
|
|
737
|
+
logger.info({ trace: error.stack }, "error in pairing");
|
|
738
|
+
end(error);
|
|
826
739
|
}
|
|
827
|
-
})
|
|
828
|
-
|
|
829
|
-
ws.on('CB:success', async (node) => {
|
|
740
|
+
});
|
|
741
|
+
ws.on("CB:success", async (node) => {
|
|
830
742
|
try {
|
|
831
|
-
await uploadPreKeysToServerIfRequired()
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
uploadPreKeysToServerIfRequired().catch((err) => {
|
|
839
|
-
logger.warn({ err }, 'periodic pre-key check failed')
|
|
840
|
-
})
|
|
841
|
-
}, 6 * 60 * 60 * 1000)
|
|
842
|
-
await sendPassiveIq('active')
|
|
743
|
+
await uploadPreKeysToServerIfRequired();
|
|
744
|
+
await sendPassiveIq("active");
|
|
745
|
+
try {
|
|
746
|
+
await digestKeyBundle();
|
|
747
|
+
} catch (e) {
|
|
748
|
+
logger.warn({ e: e }, "failed to run digest after login");
|
|
749
|
+
}
|
|
843
750
|
} catch (err) {
|
|
844
|
-
logger.warn({
|
|
845
|
-
err
|
|
846
|
-
}, 'failed to send initial passive iq');
|
|
751
|
+
logger.warn({ err: err }, "failed to send initial passive iq");
|
|
847
752
|
}
|
|
848
|
-
logger.info(
|
|
753
|
+
logger.info("opened connection to WA");
|
|
849
754
|
clearTimeout(qrTimer);
|
|
850
|
-
ev.emit(
|
|
851
|
-
me: {
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
}
|
|
855
|
-
})
|
|
856
|
-
ev.emit('connection.update', {
|
|
857
|
-
connection: 'open'
|
|
858
|
-
})
|
|
755
|
+
ev.emit("creds.update", {
|
|
756
|
+
me: { ...authState.creds.me, lid: node.attrs.lid },
|
|
757
|
+
});
|
|
758
|
+
ev.emit("connection.update", { connection: "open" });
|
|
859
759
|
if (node.attrs.lid && authState.creds.me?.id) {
|
|
860
|
-
const myLID = node.attrs.lid
|
|
760
|
+
const myLID = node.attrs.lid;
|
|
861
761
|
process.nextTick(async () => {
|
|
862
762
|
try {
|
|
863
|
-
const myPN = authState.creds.me.id
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
763
|
+
const myPN = authState.creds.me.id;
|
|
764
|
+
await signalRepository.lidMapping.storeLIDPNMappings([
|
|
765
|
+
{ lid: myLID, pn: myPN },
|
|
766
|
+
]);
|
|
767
|
+
const { user: user, device: device } = jidDecode(myPN);
|
|
768
|
+
await authState.keys.set({
|
|
769
|
+
"device-list": { [user]: [device?.toString() || "0"] },
|
|
770
|
+
});
|
|
771
|
+
await signalRepository.migrateSession(myPN, myLID);
|
|
772
|
+
logger.info(
|
|
773
|
+
{ myPN: myPN, myLID: myLID },
|
|
774
|
+
"Own LID session created successfully"
|
|
775
|
+
);
|
|
874
776
|
} catch (error) {
|
|
875
|
-
logger.error({
|
|
876
|
-
error,
|
|
877
|
-
lid: myLID
|
|
878
|
-
}, 'Failed to create own LID session')
|
|
777
|
+
logger.error({ error: error, lid: myLID }, "Failed to create own LID session");
|
|
879
778
|
}
|
|
779
|
+
});
|
|
780
|
+
}
|
|
781
|
+
});
|
|
782
|
+
ws.on("CB:stream:error", (node) => {
|
|
783
|
+
const [reasonNode] = getAllBinaryNodeChildren(node);
|
|
784
|
+
logger.error({ reasonNode: reasonNode, fullErrorNode: node }, "stream errored out");
|
|
785
|
+
const { reason: reason, statusCode: statusCode } = getErrorCodeFromStreamError(node);
|
|
786
|
+
end(
|
|
787
|
+
new Boom(`Stream Errored (${reason})`, {
|
|
788
|
+
statusCode: statusCode,
|
|
789
|
+
data: reasonNode || node,
|
|
880
790
|
})
|
|
881
|
-
|
|
882
|
-
})
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
}))
|
|
897
|
-
})
|
|
898
|
-
|
|
899
|
-
ws.on('CB:failure', (node) => {
|
|
900
|
-
const reason = +(node.attrs.reason || 500)
|
|
901
|
-
|
|
902
|
-
end(new boom_1.Boom('Connection Failure', {
|
|
903
|
-
statusCode: reason,
|
|
904
|
-
data: node.attrs
|
|
905
|
-
}))
|
|
906
|
-
})
|
|
907
|
-
|
|
908
|
-
ws.on('CB:ib,,downgrade_webclient', () => {
|
|
909
|
-
end(new boom_1.Boom('Multi-device beta not joined', {
|
|
910
|
-
statusCode: Types_1.DisconnectReason.multideviceMismatch
|
|
911
|
-
}))
|
|
912
|
-
})
|
|
913
|
-
|
|
914
|
-
ws.on('CB:ib,,offline_preview', (node) => {
|
|
915
|
-
logger.info('offline preview received', JSON.stringify(node))
|
|
916
|
-
|
|
791
|
+
);
|
|
792
|
+
});
|
|
793
|
+
ws.on("CB:failure", (node) => {
|
|
794
|
+
const reason = +(node.attrs.reason || 500);
|
|
795
|
+
end(new Boom("Connection Failure", { statusCode: reason, data: node.attrs }));
|
|
796
|
+
});
|
|
797
|
+
ws.on("CB:ib,,downgrade_webclient", () => {
|
|
798
|
+
end(
|
|
799
|
+
new Boom("Multi-device beta not joined", {
|
|
800
|
+
statusCode: DisconnectReason.multideviceMismatch,
|
|
801
|
+
})
|
|
802
|
+
);
|
|
803
|
+
});
|
|
804
|
+
ws.on("CB:ib,,offline_preview", (node) => {
|
|
805
|
+
logger.info("offline preview received", JSON.stringify(node));
|
|
917
806
|
sendNode({
|
|
918
|
-
tag:
|
|
807
|
+
tag: "ib",
|
|
919
808
|
attrs: {},
|
|
920
|
-
content: [{
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
})
|
|
927
|
-
})
|
|
928
|
-
|
|
929
|
-
ws.on('CB:ib,,edge_routing', (node) => {
|
|
930
|
-
const edgeRoutingNode = WABinary_1.getBinaryNodeChild(node, 'edge_routing')
|
|
931
|
-
const routingInfo = WABinary_1.getBinaryNodeChild(edgeRoutingNode, 'routing_info')
|
|
932
|
-
|
|
809
|
+
content: [{ tag: "offline_batch", attrs: { count: "100" } }],
|
|
810
|
+
});
|
|
811
|
+
});
|
|
812
|
+
ws.on("CB:ib,,edge_routing", (node) => {
|
|
813
|
+
const edgeRoutingNode = getBinaryNodeChild(node, "edge_routing");
|
|
814
|
+
const routingInfo = getBinaryNodeChild(edgeRoutingNode, "routing_info");
|
|
933
815
|
if (routingInfo?.content) {
|
|
934
|
-
authState.creds.routingInfo = Buffer.from(routingInfo?.content)
|
|
935
|
-
ev.emit(
|
|
816
|
+
authState.creds.routingInfo = Buffer.from(routingInfo?.content);
|
|
817
|
+
ev.emit("creds.update", authState.creds);
|
|
936
818
|
}
|
|
937
|
-
})
|
|
938
|
-
|
|
939
|
-
let didStartBuffer = false
|
|
940
|
-
|
|
819
|
+
});
|
|
820
|
+
let didStartBuffer = false;
|
|
941
821
|
process.nextTick(() => {
|
|
942
822
|
if (creds.me?.id) {
|
|
943
|
-
ev.buffer()
|
|
944
|
-
didStartBuffer = true
|
|
823
|
+
ev.buffer();
|
|
824
|
+
didStartBuffer = true;
|
|
945
825
|
}
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
connection: 'connecting',
|
|
826
|
+
ev.emit("connection.update", {
|
|
827
|
+
connection: "connecting",
|
|
949
828
|
receivedPendingNotifications: false,
|
|
950
|
-
qr: undefined
|
|
951
|
-
})
|
|
952
|
-
})
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
const
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
logger.info(`handled ${offlineNotifs} offline messages/notifications`)
|
|
959
|
-
|
|
829
|
+
qr: undefined,
|
|
830
|
+
});
|
|
831
|
+
});
|
|
832
|
+
ws.on("CB:ib,,offline", (node) => {
|
|
833
|
+
const child = getBinaryNodeChild(node, "offline");
|
|
834
|
+
const offlineNotifs = +(child?.attrs.count || 0);
|
|
835
|
+
logger.info(`handled ${offlineNotifs} offline messages/notifications`);
|
|
960
836
|
if (didStartBuffer) {
|
|
961
|
-
ev.flush()
|
|
962
|
-
logger.trace(
|
|
837
|
+
ev.flush();
|
|
838
|
+
logger.trace("flushed events for initial buffer");
|
|
963
839
|
}
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
})
|
|
969
|
-
|
|
970
|
-
ev.on('creds.update', update => {
|
|
971
|
-
const name = update.me?.name
|
|
840
|
+
ev.emit("connection.update", { receivedPendingNotifications: true });
|
|
841
|
+
});
|
|
842
|
+
ev.on("creds.update", (update) => {
|
|
843
|
+
const name = update.me?.name;
|
|
972
844
|
if (creds.me?.name !== name) {
|
|
973
|
-
logger.debug({
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
logger.warn({
|
|
984
|
-
trace: err.stack
|
|
985
|
-
}, 'error in sending presence update on name change')
|
|
986
|
-
})
|
|
987
|
-
}
|
|
988
|
-
Object.assign(creds, update)
|
|
989
|
-
})
|
|
990
|
-
|
|
845
|
+
logger.debug({ name: name }, "updated pushName");
|
|
846
|
+
sendNode({ tag: "presence", attrs: { name: name } }).catch((err) => {
|
|
847
|
+
logger.warn(
|
|
848
|
+
{ trace: err.stack },
|
|
849
|
+
"error in sending presence update on name change"
|
|
850
|
+
);
|
|
851
|
+
});
|
|
852
|
+
}
|
|
853
|
+
Object.assign(creds, update);
|
|
854
|
+
});
|
|
991
855
|
if (printQRInTerminal) {
|
|
992
|
-
|
|
856
|
+
printQRIfNecessaryListener(ev, logger);
|
|
993
857
|
}
|
|
994
|
-
|
|
995
858
|
return {
|
|
996
|
-
type:
|
|
997
|
-
ws,
|
|
998
|
-
ev,
|
|
999
|
-
authState: {
|
|
1000
|
-
|
|
1001
|
-
keys
|
|
1002
|
-
},
|
|
1003
|
-
signalRepository,
|
|
859
|
+
type: "md",
|
|
860
|
+
ws: ws,
|
|
861
|
+
ev: ev,
|
|
862
|
+
authState: { creds: creds, keys: keys },
|
|
863
|
+
signalRepository: signalRepository,
|
|
1004
864
|
get user() {
|
|
1005
|
-
return authState.creds.me
|
|
865
|
+
return authState.creds.me;
|
|
1006
866
|
},
|
|
1007
|
-
generateMessageTag,
|
|
1008
|
-
query,
|
|
1009
|
-
waitForMessage,
|
|
1010
|
-
waitForSocketOpen,
|
|
1011
|
-
sendRawMessage,
|
|
1012
|
-
sendNode,
|
|
1013
|
-
logout,
|
|
1014
|
-
end,
|
|
1015
|
-
onUnexpectedError,
|
|
1016
|
-
uploadPreKeys,
|
|
1017
|
-
uploadPreKeysToServerIfRequired,
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
867
|
+
generateMessageTag: generateMessageTag,
|
|
868
|
+
query: query,
|
|
869
|
+
waitForMessage: waitForMessage,
|
|
870
|
+
waitForSocketOpen: waitForSocketOpen,
|
|
871
|
+
sendRawMessage: sendRawMessage,
|
|
872
|
+
sendNode: sendNode,
|
|
873
|
+
logout: logout,
|
|
874
|
+
end: end,
|
|
875
|
+
onUnexpectedError: onUnexpectedError,
|
|
876
|
+
uploadPreKeys: uploadPreKeys,
|
|
877
|
+
uploadPreKeysToServerIfRequired: uploadPreKeysToServerIfRequired,
|
|
878
|
+
digestKeyBundle: digestKeyBundle,
|
|
879
|
+
rotateSignedPreKey: rotateSignedPreKey,
|
|
880
|
+
requestPairingCode: requestPairingCode,
|
|
881
|
+
wamBuffer: publicWAMBuffer,
|
|
882
|
+
waitForConnectionUpdate: bindWaitForConnectionUpdate(ev),
|
|
883
|
+
sendWAMBuffer: sendWAMBuffer,
|
|
884
|
+
executeUSyncQuery: executeUSyncQuery,
|
|
885
|
+
onWhatsApp: onWhatsApp,
|
|
886
|
+
};
|
|
887
|
+
};
|
|
1027
888
|
function mapWebSocketError(handler) {
|
|
1028
889
|
return (error) => {
|
|
1029
|
-
handler(
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
890
|
+
handler(
|
|
891
|
+
new Boom(`WebSocket Error (${error?.message})`, {
|
|
892
|
+
statusCode: getCodeFromWSError(error),
|
|
893
|
+
data: error,
|
|
894
|
+
})
|
|
895
|
+
);
|
|
896
|
+
};
|
|
1034
897
|
}
|
|
1035
|
-
|
|
1036
|
-
module.exports = {
|
|
1037
|
-
makeSocket
|
|
1038
|
-
}
|
|
898
|
+
module.exports = { makeSocket: makeSocket };
|