@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.
Files changed (185) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +839 -267
  3. package/WAProto/GenerateStatics.sh +4 -3
  4. package/WAProto/WAProto.proto +511 -1215
  5. package/WAProto/index.d.ts +55 -14017
  6. package/WAProto/index.js +29 -97689
  7. package/lib/Defaults/index.d.ts +74 -72
  8. package/lib/Defaults/index.js +108 -94
  9. package/lib/Signal/Group/ciphertext-message.d.ts +7 -8
  10. package/lib/Signal/Group/ciphertext-message.js +16 -9
  11. package/lib/Signal/Group/group-session-builder.d.ts +13 -11
  12. package/lib/Signal/Group/group-session-builder.js +61 -19
  13. package/lib/Signal/Group/group_cipher.d.ts +16 -14
  14. package/lib/Signal/Group/group_cipher.js +70 -41
  15. package/lib/Signal/Group/index.d.ts +11 -12
  16. package/lib/Signal/Group/index.js +61 -12
  17. package/lib/Signal/Group/keyhelper.d.ts +14 -9
  18. package/lib/Signal/Group/keyhelper.js +58 -10
  19. package/lib/Signal/Group/sender-chain-key.d.ts +13 -13
  20. package/lib/Signal/Group/sender-chain-key.js +34 -13
  21. package/lib/Signal/Group/sender-key-distribution-message.d.ts +16 -16
  22. package/lib/Signal/Group/sender-key-distribution-message.js +36 -28
  23. package/lib/Signal/Group/sender-key-message.d.ts +18 -18
  24. package/lib/Signal/Group/sender-key-message.js +45 -38
  25. package/lib/Signal/Group/sender-key-name.d.ts +15 -14
  26. package/lib/Signal/Group/sender-key-name.js +31 -20
  27. package/lib/Signal/Group/sender-key-record.d.ts +25 -24
  28. package/lib/Signal/Group/sender-key-record.js +35 -18
  29. package/lib/Signal/Group/sender-key-state.d.ts +34 -29
  30. package/lib/Signal/Group/sender-key-state.js +100 -37
  31. package/lib/Signal/Group/sender-message-key.d.ts +10 -11
  32. package/lib/Signal/Group/sender-message-key.js +22 -15
  33. package/lib/Signal/libsignal.d.ts +8 -5
  34. package/lib/Signal/libsignal.js +142 -288
  35. package/lib/Signal/lid-mapping.d.ts +28 -23
  36. package/lib/Signal/lid-mapping.js +184 -277
  37. package/lib/Socket/Client/index.d.ts +2 -3
  38. package/lib/Socket/Client/index.js +22 -3
  39. package/lib/Socket/Client/types.d.ts +15 -15
  40. package/lib/Socket/Client/types.js +15 -8
  41. package/lib/Socket/Client/websocket.d.ts +12 -12
  42. package/lib/Socket/Client/websocket.js +37 -29
  43. package/lib/Socket/business.d.ts +178 -208
  44. package/lib/Socket/business.js +127 -91
  45. package/lib/Socket/chats.d.ts +93 -120
  46. package/lib/Socket/chats.js +652 -727
  47. package/lib/Socket/community.js +10 -2
  48. package/lib/Socket/groups.d.ts +111 -143
  49. package/lib/Socket/groups.js +186 -165
  50. package/lib/Socket/index.d.ts +183 -252
  51. package/lib/Socket/index.js +18 -38
  52. package/lib/Socket/messages-recv.d.ts +167 -196
  53. package/lib/Socket/messages-recv.js +1567 -1443
  54. package/lib/Socket/messages-send.d.ts +158 -192
  55. package/lib/Socket/messages-send.js +1208 -1102
  56. package/lib/Socket/newsletter.d.ts +138 -163
  57. package/lib/Socket/newsletter.js +249 -153
  58. package/lib/Socket/socket.d.ts +36 -50
  59. package/lib/Socket/socket.js +820 -749
  60. package/lib/Store/index.d.ts +4 -4
  61. package/lib/Store/index.js +24 -4
  62. package/lib/Store/make-in-memory-store.d.ts +104 -44
  63. package/lib/Store/make-in-memory-store.js +213 -199
  64. package/lib/Store/make-ordered-dictionary.d.ts +12 -13
  65. package/lib/Store/make-ordered-dictionary.js +45 -38
  66. package/lib/Store/object-repository.d.ts +10 -11
  67. package/lib/Store/object-repository.js +16 -9
  68. package/lib/Types/Auth.d.ts +97 -93
  69. package/lib/Types/Auth.js +3 -2
  70. package/lib/Types/Bussines.js +3 -2
  71. package/lib/Types/Call.d.ts +13 -14
  72. package/lib/Types/Call.js +3 -2
  73. package/lib/Types/Chat.d.ts +97 -78
  74. package/lib/Types/Chat.js +9 -8
  75. package/lib/Types/Contact.d.ts +9 -12
  76. package/lib/Types/Contact.js +3 -2
  77. package/lib/Types/Events.d.ts +176 -206
  78. package/lib/Types/Events.js +3 -2
  79. package/lib/Types/GroupMetadata.d.ts +48 -53
  80. package/lib/Types/GroupMetadata.js +3 -2
  81. package/lib/Types/Label.d.ts +14 -13
  82. package/lib/Types/Label.js +30 -24
  83. package/lib/Types/LabelAssociation.d.ts +20 -15
  84. package/lib/Types/LabelAssociation.js +12 -6
  85. package/lib/Types/Message.d.ts +412 -248
  86. package/lib/Types/Message.js +19 -17
  87. package/lib/Types/Product.d.ts +71 -58
  88. package/lib/Types/Product.js +3 -2
  89. package/lib/Types/Signal.d.ts +82 -71
  90. package/lib/Types/Signal.js +3 -2
  91. package/lib/Types/Socket.d.ts +81 -76
  92. package/lib/Types/Socket.js +3 -3
  93. package/lib/Types/State.d.ts +19 -75
  94. package/lib/Types/State.js +14 -56
  95. package/lib/Types/USync.d.ts +8 -8
  96. package/lib/Types/USync.js +3 -2
  97. package/lib/Types/index.d.ts +62 -47
  98. package/lib/Types/index.js +50 -26
  99. package/lib/Utils/auth-utils.d.ts +10 -13
  100. package/lib/Utils/auth-utils.js +472 -213
  101. package/lib/Utils/business.d.ts +20 -14
  102. package/lib/Utils/business.js +134 -110
  103. package/lib/Utils/chat-utils.d.ts +70 -88
  104. package/lib/Utils/chat-utils.js +403 -466
  105. package/lib/Utils/crypto.d.ts +46 -27
  106. package/lib/Utils/crypto.js +188 -117
  107. package/lib/Utils/decode-wa-message.d.ts +45 -58
  108. package/lib/Utils/decode-wa-message.js +311 -212
  109. package/lib/Utils/event-buffer.d.ts +17 -14
  110. package/lib/Utils/event-buffer.js +296 -321
  111. package/lib/Utils/generics.d.ts +92 -66
  112. package/lib/Utils/generics.js +436 -270
  113. package/lib/Utils/history.d.ts +23 -24
  114. package/lib/Utils/history.js +76 -106
  115. package/lib/Utils/index.d.ts +21 -24
  116. package/lib/Utils/index.js +41 -24
  117. package/lib/Utils/link-preview.d.ts +14 -12
  118. package/lib/Utils/link-preview.js +75 -40
  119. package/lib/Utils/logger.d.ts +11 -10
  120. package/lib/Utils/logger.js +7 -3
  121. package/lib/Utils/lt-hash.d.ts +14 -8
  122. package/lib/Utils/lt-hash.js +53 -3
  123. package/lib/Utils/make-mutex.d.ts +7 -7
  124. package/lib/Utils/make-mutex.js +44 -28
  125. package/lib/Utils/message-retry-manager.d.ts +88 -115
  126. package/lib/Utils/message-retry-manager.js +160 -265
  127. package/lib/Utils/messages-media.d.ts +107 -105
  128. package/lib/Utils/messages-media.js +619 -548
  129. package/lib/Utils/messages.d.ts +66 -52
  130. package/lib/Utils/messages.js +1558 -1730
  131. package/lib/Utils/noise-handler.d.ts +18 -18
  132. package/lib/Utils/noise-handler.js +130 -176
  133. package/lib/Utils/process-message.d.ts +32 -43
  134. package/lib/Utils/process-message.js +282 -484
  135. package/lib/Utils/rich-message-utils.js +724 -1220
  136. package/lib/Utils/signal.d.ts +32 -37
  137. package/lib/Utils/signal.js +105 -140
  138. package/lib/Utils/use-multi-file-auth-state.d.ts +5 -5
  139. package/lib/Utils/use-multi-file-auth-state.js +120 -70
  140. package/lib/Utils/use-single-file-auth-state.d.ts +13 -11
  141. package/lib/Utils/use-single-file-auth-state.js +66 -92
  142. package/lib/Utils/validate-connection.d.ts +13 -11
  143. package/lib/Utils/validate-connection.js +113 -128
  144. package/lib/WABinary/constants.d.ts +27 -25
  145. package/lib/WABinary/constants.js +1292 -1451
  146. package/lib/WABinary/decode.d.ts +9 -7
  147. package/lib/WABinary/decode.js +189 -163
  148. package/lib/WABinary/encode.d.ts +3 -3
  149. package/lib/WABinary/encode.js +155 -110
  150. package/lib/WABinary/generic-utils.d.ts +27 -14
  151. package/lib/WABinary/generic-utils.js +105 -177
  152. package/lib/WABinary/index.d.ts +5 -6
  153. package/lib/WABinary/index.js +25 -6
  154. package/lib/WABinary/jid-utils.d.ts +54 -41
  155. package/lib/WABinary/jid-utils.js +152 -83
  156. package/lib/WABinary/types.d.ts +13 -10
  157. package/lib/WABinary/types.js +3 -2
  158. package/lib/WAM/BinaryInfo.d.ts +15 -8
  159. package/lib/WAM/BinaryInfo.js +14 -7
  160. package/lib/WAM/constants.d.ts +37 -30
  161. package/lib/WAM/constants.js +11983 -19465
  162. package/lib/WAM/encode.d.ts +3 -3
  163. package/lib/WAM/encode.js +109 -95
  164. package/lib/WAM/index.d.ts +3 -4
  165. package/lib/WAM/index.js +23 -4
  166. package/lib/WAUSync/Protocols/USyncContactProtocol.d.ts +9 -9
  167. package/lib/WAUSync/Protocols/USyncContactProtocol.js +21 -37
  168. package/lib/WAUSync/Protocols/USyncDeviceProtocol.d.ts +22 -19
  169. package/lib/WAUSync/Protocols/USyncDeviceProtocol.js +26 -18
  170. package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.d.ts +12 -11
  171. package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.js +20 -12
  172. package/lib/WAUSync/Protocols/USyncStatusProtocol.d.ts +12 -11
  173. package/lib/WAUSync/Protocols/USyncStatusProtocol.js +24 -16
  174. package/lib/WAUSync/Protocols/index.d.ts +6 -6
  175. package/lib/WAUSync/Protocols/index.js +26 -6
  176. package/lib/WAUSync/USyncQuery.d.ts +28 -27
  177. package/lib/WAUSync/USyncQuery.js +62 -68
  178. package/lib/WAUSync/USyncUser.d.ts +10 -15
  179. package/lib/WAUSync/USyncUser.js +19 -20
  180. package/lib/WAUSync/index.d.ts +3 -4
  181. package/lib/WAUSync/index.js +23 -4
  182. package/lib/index.d.ts +12 -11
  183. package/lib/index.js +39 -12
  184. package/package.json +43 -87
  185. package/engine-requirements.js +0 -10
@@ -1,178 +1,98 @@
1
- import { Boom } from '@hapi/boom';
2
- import { randomBytes } from 'crypto';
3
- import { URL } from 'url';
4
- import { promisify } from 'util';
5
- import { proto } from '../../WAProto/index.js';
6
- import { DEF_CALLBACK_PREFIX, DEF_TAG_PREFIX, INITIAL_PREKEY_COUNT, MIN_PREKEY_COUNT, NOISE_WA_HEADER, PROCESSABLE_HISTORY_TYPES, TimeMs, UPLOAD_TIMEOUT } from '../Defaults/index.js';
7
- import { QueryIds, ReachoutTimelockEnforcementType } from '../Types/index.js';
8
- import { DisconnectReason, XWAPaths } from '../Types/index.js';
9
- import { addTransactionCapability, aesEncryptCTR, bindWaitForConnectionUpdate, buildPairingQRData, bytesToCrockford, configureSuccessfulPairing, Curve, derivePairingCodeKey, generateLoginNode, generateMdTagPrefix, generateRegistrationNode, getCodeFromWSError, getCompanionPlatformId, getErrorCodeFromStreamError, getNextPreKeysNode, makeEventBuffer, makeNoiseHandler, promiseTimeout, signedKeyPair, xmppSignedPreKey } from '../Utils/index.js';
10
- import { assertNodeErrorFree, binaryNodeToString, encodeBinaryNode, getAllBinaryNodeChildren, getBinaryNodeChild, getBinaryNodeChildren, isLidUser, jidDecode, jidEncode, S_WHATSAPP_NET } from '../WABinary/index.js';
11
- import { BinaryInfo } from '../WAM/BinaryInfo.js';
12
- import { USyncQuery, USyncUser } from '../WAUSync/index.js';
13
- import { WebSocketClient } from './Client/index.js';
14
- import { executeWMexQuery } from './mex.js';
15
- /**
16
- * Connects to WA servers and performs:
17
- * - simple queries (no retry mechanism, wait for connection establishment)
18
- * - listen to messages and emit events
19
- * - query phone connection
20
- */
21
- export const makeSocket = (config) => {
22
- const { waWebSocketUrl, connectTimeoutMs, logger, keepAliveIntervalMs, browser, auth: authState, printQRInTerminal, defaultQueryTimeoutMs, transactionOpts, qrTimeout, makeSignalRepository } = config;
23
- const publicWAMBuffer = new BinaryInfo();
24
- let serverTimeOffsetMs = 0;
25
- const uqTagId = generateMdTagPrefix();
26
- const generateMessageTag = () => `${uqTagId}${epoch++}`;
27
- if (printQRInTerminal) {
28
- logger.warn({}, '⚠️ The printQRInTerminal option has been deprecated. You will no longer receive QR codes in the terminal automatically. Please listen to the connection.update event yourself and handle the QR your way. You can remove this message by removing this opttion. This message will be removed in a future version.');
29
- }
30
- const syncDisabled = PROCESSABLE_HISTORY_TYPES.map(syncType => config.shouldSyncHistoryMessage({ syncType })).filter(x => x === false)
31
- .length === PROCESSABLE_HISTORY_TYPES.length;
32
- if (syncDisabled) {
33
- logger.warn('⚠️ DANGER: DISABLING ALL SYNC BY shouldSyncHistoryMsg PREVENTS BAILEYS FROM ACCESSING INITIAL LID MAPPINGS, LEADING TO INSTABILIY AND SESSION ERRORS');
34
- }
35
- const url = typeof waWebSocketUrl === 'string' ? new URL(waWebSocketUrl) : waWebSocketUrl;
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', { statusCode: DisconnectReason.loggedOut });
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
- /** ephemeral key pair used to encrypt/decrypt communication. Unique for each connection */
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
- const result = await promiseTimeout(timeoutMs, (resolve, reject) => {
88
- onRecv = data => {
89
- resolve(data);
90
- };
53
+ return await Utils_1.promiseTimeout(timeoutMs, (resolve, reject) => {
54
+ onRecv = resolve
91
55
  onErr = err => {
92
- reject(err ||
93
- new Boom('Connection Closed', {
94
- statusCode: DisconnectReason.connectionClosed
95
- }));
96
- };
97
- ws.on(`TAG:${msgId}`, onRecv);
98
- ws.on('close', onErr);
99
- ws.on('error', onErr);
100
- return () => reject(new Boom('Query Cancelled'));
101
- });
102
- return result;
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
- /** send a query, and wait for its response. auto-generates message ID if not provided */
69
+ }
70
+
122
71
  const query = async (node, timeoutMs) => {
123
72
  if (!node.attrs.id) {
124
- node.attrs.id = generateMessageTag();
125
- }
126
- const msgId = node.attrs.id;
127
- const result = await promiseTimeout(timeoutMs, async (resolve, reject) => {
128
- const result = waitForMessage(msgId, timeoutMs).catch(reject);
129
- sendNode(node)
130
- .then(async () => resolve(await result))
131
- .catch(reject);
132
- });
133
- if (result && 'tag' in result) {
134
- assertNodeErrorFree(result);
135
- }
136
- return result;
137
- };
138
- // Validate current key-bundle on server; on failure, trigger pre-key upload and rethrow
139
- const digestKeyBundle = async () => {
140
- const res = await query({
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
- // todo: validate users, throw WARNING on no valid users
174
- // variable below has only validated users
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: S_WHATSAPP_NET,
121
+ to: WABinary_1._WHATSAPP_NET,
199
122
  type: 'get',
200
123
  xmlns: 'usync'
201
124
  },
202
- content: [
203
- {
204
- tag: 'usync',
205
- attrs: {
206
- context: usyncQuery.context,
207
- mode: usyncQuery.mode,
208
- sid: generateMessageTag(),
209
- last: 'true',
210
- index: '0'
211
- },
212
- content: [queryNode, listNode]
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
- const pnFromLIDUSync = async (jids) => {
245
- const usyncQuery = new USyncQuery().withLIDProtocol().withContext('background');
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
- logger?.warn('LID user found in LID fetch call');
249
- continue;
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
- else {
252
- usyncQuery.withUser(new USyncUser().withId(jid));
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
- if (usyncQuery.users.length === 0) {
256
- return []; // return early without forcing an empty query
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
- const results = await executeUSyncQuery(usyncQuery);
259
- if (results) {
260
- return results.list.filter(a => !!a.lid).map(({ lid, id }) => ({ pn: id, lid: lid }));
261
- }
262
- return [];
263
- };
264
- const ev = makeEventBuffer(logger);
265
- const { creds } = authState;
266
- // add transaction capability
267
- const keys = addTransactionCapability(authState.keys, logger, transactionOpts);
268
- const signalRepository = makeSignalRepository({ creds, keys }, logger, pnFromLIDUSync);
269
- let lastDateRecv;
270
- let epoch = 1;
271
- let keepAliveReq;
272
- let qrTimer;
273
- let closed = false;
274
- const socketEndHandlers = [];
275
- /** log & process any unexpected errors */
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({ err }, `unexpected error in '${msg}'`);
278
- };
279
- /** await the next incoming message */
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
- let onOpen;
287
- let onClose;
288
- const result = promiseTimeout(connectTimeoutMs, (resolve, reject) => {
289
- onOpen = resolve;
290
- onClose = mapWebSocketError(reject);
291
- ws.on('frame', onOpen);
292
- ws.on('close', onClose);
293
- ws.on('error', onClose);
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
- return result;
303
- };
304
- /** connection handshake */
273
+
274
+ return result
275
+ }
276
+
305
277
  const validateConnection = async () => {
306
278
  let helloMsg = {
307
- clientHello: { ephemeral: ephemeralKeyPair.public }
308
- };
309
- helloMsg = proto.HandshakeMessage.fromObject(helloMsg);
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
- else {
322
- node = generateLoginNode(creds.me.id, config);
323
- logger.info({ node }, 'logging in...');
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
- await sendRawMessage(proto.HandshakeMessage.encode({
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
- await noise.finishInit();
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: [{ tag: 'count', attrs: {} }]
345
- });
346
- const countChild = getBinaryNodeChild(result, 'count');
347
- return +countChild.attrs.value;
348
- };
349
- // WAWeb has no time throttle here; the server drives uploads via PreKeyLow notifications.
350
- let uploadPreKeysPromise = null;
351
- /** generates and uploads a set of pre-keys to the server */
352
- const uploadPreKeys = async (count = MIN_PREKEY_COUNT) => {
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
- await uploadPreKeysPromise;
356
- return;
357
+ logger.debug('Pre-key upload already in progress, waiting for completion')
358
+ return uploadPreKeysPromise
357
359
  }
358
- const uploadLogic = async (retryCount) => {
359
- logger.info({ count, retryCount }, 'uploading pre-keys');
360
- // Generate and save pre-keys atomically (prevents ID collisions on retry)
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({ requestedCount: count }, 'generating pre-keys with requested count');
363
- const { update, node } = await getNextPreKeysNode({ creds, keys }, count);
364
- // Update credentials immediately to prevent duplicate IDs on retry
365
- ev.emit('creds.update', update);
366
- return node;
367
- }, creds?.me?.id || 'upload-pre-keys');
368
- // Upload to server (outside transaction, can fail without affecting local keys)
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({ count }, 'uploaded pre-keys successfully');
372
- }
373
- catch (uploadError) {
374
- logger.error({ uploadError: uploadError.toString(), count }, 'Failed to upload pre-keys to server');
375
- // Recurse into uploadLogic; calling uploadPreKeys would await its own in-flight promise.
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 uploadLogic(retryCount + 1);
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
- // Add timeout protection
403
+ }
404
+
386
405
  uploadPreKeysPromise = Promise.race([
387
- uploadLogic(0),
388
- new Promise((_, reject) => setTimeout(() => reject(new Boom('Pre-key upload timeout', { statusCode: 408 })), 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
- finally {
394
- uploadPreKeysPromise = null;
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 { exists: false, currentPreKeyId: 0 };
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
- const preKeys = await keys.get('pre-key', [currentPreKeyId.toString()]);
403
- const exists = !!preKeys[currentPreKeyId.toString()];
404
- return { exists, currentPreKeyId };
405
- };
432
+ }
433
+
406
434
  const uploadPreKeysToServerIfRequired = async () => {
407
435
  try {
408
- let count = 0;
409
- const preKeyCount = await getAvailablePreKeysOnServer();
410
- if (preKeyCount === 0)
411
- count = INITIAL_PREKEY_COUNT;
412
- else
413
- count = MIN_PREKEY_COUNT;
414
- const { exists: currentPreKeyExists, currentPreKeyId } = await verifyCurrentPreKeyExists();
415
- logger.info(`${preKeyCount} pre-keys found on server`);
416
- logger.info(`Current prekey ID: ${currentPreKeyId}, exists in storage: ${currentPreKeyExists}`);
417
- const lowServerCount = preKeyCount <= count;
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(count);
428
- }
429
- else {
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
- catch (error) {
434
- logger.error({ error }, 'Failed to check/upload pre-keys during initialization');
435
- // Don't throw - allow connection to continue even if pre-key check fails
436
- }
437
- };
438
- const onMessageReceived = async (data) => {
439
- await noise.decodeFrame(data, frame => {
440
- // reset ping timeout
441
- lastDateRecv = new Date();
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({ xml: binaryNodeToString(frame), msg: 'recv xml' });
478
+ logger.trace({
479
+ xml: WABinary_1.binaryNodeToString(frame),
480
+ msg: 'recv xml'
481
+ })
449
482
  }
450
- /* Check if this is a response to a message we sent */
451
- anyTriggered = ws.emit(`${DEF_TAG_PREFIX}${msgId}`, frame) || anyTriggered;
452
- /* Check if this is a response to a message we are expecting */
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
- anyTriggered = ws.emit(`${DEF_CALLBACK_PREFIX}${l0},,${l2}`, frame) || anyTriggered;
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({ unhandled: true, msgId, fromMe: false, frame }, 'communication recv');
500
+ logger.debug({
501
+ unhandled: true,
502
+ msgId,
503
+ fromMe: false,
504
+ frame
505
+ }, 'communication recv')
465
506
  }
466
507
  }
467
- });
468
- };
469
- const end = async (error) => {
508
+ })
509
+ }
510
+
511
+ const end = (error) => {
470
512
  if (closed) {
471
- logger.trace({ trace: error?.stack }, 'connection already closed');
472
- return;
513
+ logger.trace({
514
+ trace: error?.stack
515
+ }, 'connection already closed')
516
+ return
473
517
  }
474
- closed = true;
475
- logger.info({ trace: error?.stack }, error ? 'connection errored' : 'connection closed');
476
- clearInterval(keepAliveReq);
477
- clearTimeout(qrTimer);
478
- ws.removeAllListeners('close');
479
- ws.removeAllListeners('open');
480
- ws.removeAllListeners('message');
481
- signalRepository.close?.();
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
- await ws.close();
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
- ev.removeAllListeners('connection.update');
504
- ev.destroy();
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', { statusCode: DisconnectReason.connectionClosed });
554
+ throw new boom_1.Boom('Connection Closed', {
555
+ statusCode: Types_1.DisconnectReason.connectionClosed
556
+ })
512
557
  }
513
- let onOpen;
514
- let onClose;
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
- const diff = Date.now() - lastDateRecv.getTime();
532
- /*
533
- check if it's been a suspicious amount of time since the server responded with our last seen
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
- void end(new Boom('Connection was lost', { statusCode: DisconnectReason.connectionLost }));
538
- }
539
- else if (ws.isOpen) {
540
- // if its all good, send a keep alive request
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: [{ tag: 'ping', attrs: {} }]
595
+ content: [{
596
+ tag: 'ping',
597
+ attrs: {}
598
+ }]
550
599
  }).catch(err => {
551
- logger.error({ trace: err.stack }, 'error in sending keep alive');
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
- else {
555
- logger.warn('keep alive called when WS not open');
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: [{ tag, attrs: {} }]
567
- });
568
- /** logout & invalidate connection */
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
- tag: 'remove-companion-device',
583
- attrs: {
584
- jid,
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
- authState.creds.pairingCode = pairingCode;
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
- ev.emit('creds.update', authState.creds);
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
- tag: 'link_code_companion_reg',
615
- attrs: {
616
- jid: authState.creds.me.id,
617
- stage: 'companion_hello',
618
- should_show_push_notification: 'true'
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
- content: [
621
- {
622
- tag: 'link_code_pairing_wrapped_companion_ephemeral_pub',
623
- attrs: {},
624
- content: await generatePairingKey()
625
- },
626
- {
627
- tag: 'companion_server_auth_key_pub',
628
- attrs: {},
629
- content: authState.creds.noiseKey.public
630
- },
631
- {
632
- tag: 'companion_platform_id',
633
- attrs: {},
634
- content: getCompanionPlatformId(browser)
635
- },
636
- {
637
- tag: 'companion_platform_display',
638
- attrs: {},
639
- content: `${browser[1]} (${browser[0]})`
640
- },
641
- {
642
- tag: 'link_code_pairing_nonce',
643
- attrs: {},
644
- content: '0'
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
- return Buffer.concat([salt, randomIv, ciphered]);
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
- tag: 'add',
670
- attrs: { t: Math.round(Date.now() / 1000) + '' },
671
- content: wamBuffer
672
- }
673
- ]
674
- });
675
- };
676
- ws.on('message', onMessageReceived);
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
- catch (err) {
682
- logger.error({ err }, 'error in validating connection');
683
- void end(err);
684
- }
685
- });
686
- ws.on('error', mapWebSocketError(end));
687
- ws.on('close', () => void end(new Boom('Connection Terminated', { statusCode: DisconnectReason.connectionClosed })));
688
- // the server terminated the connection
689
- ws.on('CB:xmlstreamend', () => void end(new Boom('Connection Terminated by Server', { statusCode: DisconnectReason.connectionClosed })));
690
- // QR gen
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
- await sendNode(iq);
701
- const pairDeviceNode = getBinaryNodeChild(stanza, 'pair-device');
702
- const refNodes = getBinaryNodeChildren(pairDeviceNode, 'ref');
703
- const noiseKeyB64 = Buffer.from(creds.noiseKey.public).toString('base64');
704
- const identityKeyB64 = Buffer.from(creds.signedIdentityKey.public).toString('base64');
705
- const advB64 = creds.advSecretKey;
706
- let qrMs = qrTimeout || 60000; // time to let a QR live
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
- const refNode = refNodes.shift();
779
+
780
+ const refNode = refNodes.shift()
781
+
712
782
  if (!refNode) {
713
- void end(new Boom('QR refs attempts ended', { statusCode: DisconnectReason.timedOut }));
714
- return;
783
+ end(new boom_1.Boom('QR refs attempts ended', {
784
+ statusCode: Types_1.DisconnectReason.timedOut
785
+ }))
786
+ return
715
787
  }
716
- const ref = refNode.content.toString('utf-8');
717
- const qr = buildPairingQRData(ref, noiseKeyB64, identityKeyB64, advB64, browser);
718
- ev.emit('connection.update', { qr });
719
- qrTimer = setTimeout(genPairQR, qrMs);
720
- qrMs = qrTimeout || 20000; // shorter subsequent qrs
721
- };
722
- genPairQR();
723
- });
724
- // device paired for the first time
725
- // if device pairs successfully, the server asks to restart the connection
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
- updateServerTimeOffset(stanza);
730
- const { reply, creds: updatedCreds } = configureSuccessfulPairing(stanza, creds);
731
- logger.info({ me: updatedCreds.me, platform: updatedCreds.platform }, 'pairing configured successfully, expect to restart the connection...');
732
- ev.emit('creds.update', updatedCreds);
733
- ev.emit('connection.update', { isNewLogin: true, qr: undefined });
734
- await sendNode(reply);
735
- void sendUnifiedSession();
736
- }
737
- catch (error) {
738
- logger.info({ trace: error.stack }, 'error in pairing');
739
- void end(error);
740
- }
741
- });
742
- // login complete
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
- updateServerTimeOffset(node);
746
- await uploadPreKeysToServerIfRequired();
747
- await sendPassiveIq('active');
748
- // After successful login, validate our key-bundle against server
749
- try {
750
- await digestKeyBundle();
751
- }
752
- catch (e) {
753
- logger.warn({ e }, 'failed to run digest after login');
754
- }
755
- }
756
- catch (err) {
757
- logger.warn({ err }, 'failed to send initial passive iq');
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); // will never happen in all likelyhood -- but just in case WA sends success on first try
761
- ev.emit('creds.update', { me: { ...authState.creds.me, lid: node.attrs.lid } });
762
- ev.emit('connection.update', { connection: 'open' });
763
- void sendUnifiedSession();
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
- // Store our own LID-PN mapping
770
- await signalRepository.lidMapping.storeLIDPNMappings([{ lid: myLID, pn: myPN }]);
771
- // Create device list for our own user (needed for bulk migration)
772
- const { user, device } = jidDecode(myPN);
773
- await authState.keys.set({
774
- 'device-list': {
775
- [user]: [device?.toString() || '0']
776
- }
777
- });
778
- // migrate our own session
779
- await signalRepository.migrateSession(myPN, myLID);
780
- logger.info({ myPN, myLID }, 'Own LID session created successfully');
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
- catch (error) {
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
- const [reasonNode] = getAllBinaryNodeChildren(node);
790
- logger.error({ reasonNode, fullErrorNode: node }, 'stream errored out');
791
- const { reason, statusCode } = getErrorCodeFromStreamError(node);
792
- void end(new Boom(`Stream Errored (${reason})`, { statusCode, data: reasonNode || node }));
793
- });
794
- // stream fail, possible logout
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
- void end(new Boom('Connection Failure', { statusCode: reason, data: node.attrs }));
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
- void end(new Boom('Multi-device beta not joined', { statusCode: DisconnectReason.multideviceMismatch }));
801
- });
802
- ws.on('CB:ib,,offline_preview', async (node) => {
803
- logger.info('offline preview received', JSON.stringify(node));
804
- await sendNode({
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: [{ tag: 'offline_batch', attrs: { count: '100' } }]
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
- let didStartBuffer = false;
937
+ })
938
+
939
+ let didStartBuffer = false
940
+
819
941
  process.nextTick(() => {
820
942
  if (creds.me?.id) {
821
- // start buffering important events
822
- // if we're logged in
823
- ev.buffer();
824
- didStartBuffer = true;
825
- }
826
- ev.emit('connection.update', { connection: 'connecting', receivedPendingNotifications: false, qr: undefined });
827
- });
828
- // called when all offline notifs are handled
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
- logger.info(`handled ${offlineNotifs} offline messages/notifications`);
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
- ev.emit('connection.update', { receivedPendingNotifications: true });
838
- });
839
- // update credentials when required
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({ name }, 'updated pushName');
973
+ logger.debug({
974
+ name
975
+ }, 'updated pushName')
976
+
845
977
  sendNode({
846
978
  tag: 'presence',
847
- attrs: { name: name }
979
+ attrs: {
980
+ name
981
+ }
848
982
  }).catch(err => {
849
- logger.warn({ trace: err.stack }, 'error in sending presence update on name change');
850
- });
851
- }
852
- Object.assign(creds, update);
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
- const node = {
878
- tag: 'ib',
879
- attrs: {},
880
- content: [
881
- {
882
- tag: 'unified_session',
883
- attrs: {
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: 'md',
996
+ type: 'meta',
924
997
  ws,
925
998
  ev,
926
- authState: { creds, keys },
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
- updateServerTimeOffset,
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
- fetchAccountReachoutTimelock,
955
- fetchNewChatMessageCap
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})`, { statusCode: getCodeFromWSError(error), data: error }));
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
- //# sourceMappingURL=socket.js.map
1035
+
1036
+ module.exports = {
1037
+ makeSocket
1038
+ }