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