@badzz88/baileys 8.4.2 → 8.4.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/WAProto/WAProto.proto +168 -671
- package/lib/Defaults/baileys-version.json +1 -1
- package/lib/Socket/luxu.js +1 -1
- package/lib/Socket/messages-send.js +32 -8
- package/lib/Socket/mex.js +40 -41
- package/lib/Socket/socket.js +22 -40
- package/lib/Socket/username.js +147 -0
- package/lib/Socket/usync.js +1 -2
- package/lib/Utils/messages.js +7 -0
- package/lib/index.js +22 -25
- package/package.json +6 -5
package/lib/Socket/luxu.js
CHANGED
|
@@ -2,20 +2,20 @@ import NodeCache from '@cacheable/node-cache';
|
|
|
2
2
|
import { Boom } from '@hapi/boom';
|
|
3
3
|
import { proto } from '../../WAProto/index.js';
|
|
4
4
|
import { DEFAULT_CACHE_TTLS, WA_DEFAULT_EPHEMERAL } from '../Defaults/index.js';
|
|
5
|
-
import { aggregateMessageKeysNotFromMe, assertMediaContent, assertMeId, bindWaitForEvent, decryptMediaRetryData, DEF_MEDIA_HOST, encodeNewsletterMessage, encodeSignedDeviceIdentity, encodeWAMessage, encryptMediaRetryRequest, extractDeviceJids, generateMessageIDV2, generateParticipantHashV2, generateWAMessage, getStatusCodeForMediaRetry, getUrlFromDirectPath, getWAUploadToServer, MessageRetryManager, normalizeMessageContent, parseAndInjectE2ESessions, unixTimestampSeconds, setBotMessageSecret } from '../Utils/index.js';
|
|
5
|
+
import { aggregateMessageKeysNotFromMe, assertMediaContent, assertMeId, bindWaitForEvent, decryptMediaRetryData, DEF_MEDIA_HOST, encodeNewsletterMessage, encodeSignedDeviceIdentity, encodeWAMessage, encryptMediaRetryRequest, extractDeviceJids, generateMessageIDV2, generateIOSMessageID, generateParticipantHashV2, generateWAMessage, getStatusCodeForMediaRetry, getUrlFromDirectPath, getWAUploadToServer, MessageRetryManager, normalizeMessageContent, parseAndInjectE2ESessions, unixTimestampSeconds, setBotMessageSecret } from '../Utils/index.js';
|
|
6
6
|
import { getUrlInfo } from '../Utils/link-preview.js';
|
|
7
7
|
import { makeKeyedMutex, makeMutex } from '../Utils/make-mutex.js';
|
|
8
8
|
import { getMessageReportingToken, shouldIncludeReportingToken } from '../Utils/reporting-utils.js';
|
|
9
9
|
import { buildMergedTcTokenIndexWrite, isTcTokenExpired, resolveIssuanceJid, resolveTcTokenJid, shouldSendNewTcToken, storeTcTokensFromIqResult } from '../Utils/tc-token-utils.js';
|
|
10
|
-
import { areJidsSameUser, getBinaryNodeChild, getBinaryNodeChildren, isHostedLidUser, isHostedPnUser, isJidBot, isJidGroup, isJidMetaAI, isLidUser, isPnUser, jidDecode, jidEncode, jidNormalizedUser, PSA_WID, S_WHATSAPP_NET, getAdditionalNode, getBinaryNodeFilter, getBinaryFilteredBizBot, isInteropUser } from '../WABinary/index.js';
|
|
10
|
+
import { areJidsSameUser, getBinaryNodeChild, getBinaryNodeChildren, isJidNewsletter, isHostedLidUser, isHostedPnUser, isJidBot, isJidGroup, isJidMetaAI, isLidUser, isPnUser, jidDecode, jidEncode, jidNormalizedUser, PSA_WID, S_WHATSAPP_NET, getAdditionalNode, getBinaryNodeFilter, getBinaryFilteredBizBot, isInteropUser } from '../WABinary/index.js';
|
|
11
11
|
import { USyncQuery, USyncUser } from '../WAUSync/index.js';
|
|
12
|
-
import {
|
|
12
|
+
import { makeUsernameSocket } from './username.js';
|
|
13
13
|
import imup from './luxu.js';
|
|
14
14
|
import * as Utils_1 from '../Utils/index.js';
|
|
15
15
|
import { randomBytes } from 'crypto';
|
|
16
16
|
export const makeMessagesSocket = (config) => {
|
|
17
17
|
const { logger, linkPreviewImageThumbnailWidth, generateHighQualityLinkPreview, options: httpRequestOptions, patchMessageBeforeSending, cachedGroupMetadata, enableRecentMessageCache, maxMsgRetryCount, aiLabel } = config;
|
|
18
|
-
const sock =
|
|
18
|
+
const sock = makeUsernameSocket(config);
|
|
19
19
|
const { ev, authState, messageMutex, signalRepository, upsertMessage, query, fetchPrivacySettings, sendNode, groupMetadata, groupToggleEphemeral, registerSocketEndHandler } = sock;
|
|
20
20
|
const getLIDForPN = signalRepository.lidMapping.getLIDForPN.bind(signalRepository.lidMapping);
|
|
21
21
|
/**
|
|
@@ -428,15 +428,36 @@ export const makeMessagesSocket = (config) => {
|
|
|
428
428
|
}
|
|
429
429
|
return { nodes, shouldIncludeDeviceIdentity };
|
|
430
430
|
};
|
|
431
|
+
|
|
432
|
+
const profilePictureUrl = async (jid) => {
|
|
433
|
+
if (isJidNewsletter(jid)) {
|
|
434
|
+
const metadata = await sock.newsletterMetadata("JID", jid);
|
|
435
|
+
return getUrlFromDirectPath(metadata.thread_metadata.picture?.direct_path || "");
|
|
436
|
+
} else {
|
|
437
|
+
const result = await query({
|
|
438
|
+
tag: "iq",
|
|
439
|
+
attrs: {
|
|
440
|
+
target: jidNormalizedUser(jid),
|
|
441
|
+
to: S_WHATSAPP_NET,
|
|
442
|
+
type: "get",
|
|
443
|
+
xmlns: "w:profile:picture",
|
|
444
|
+
},
|
|
445
|
+
content: [{ tag: "picture", attrs: { type: "image", query: "url" } }],
|
|
446
|
+
});
|
|
447
|
+
const child = getBinaryNodeChild(result, "picture");
|
|
448
|
+
return child?.attrs?.url || null;
|
|
449
|
+
}
|
|
450
|
+
};
|
|
451
|
+
|
|
431
452
|
const relayMessage = async (
|
|
432
453
|
jid,
|
|
433
454
|
message,
|
|
434
455
|
{
|
|
435
456
|
messageId: msgId,
|
|
436
|
-
|
|
457
|
+
participant = false,
|
|
437
458
|
additionalAttributes,
|
|
438
459
|
additionalNodes,
|
|
439
|
-
|
|
460
|
+
useUserDevicesCache,
|
|
440
461
|
useCachedGroupMetadata,
|
|
441
462
|
statusJidList
|
|
442
463
|
}
|
|
@@ -454,7 +475,8 @@ export const makeMessagesSocket = (config) => {
|
|
|
454
475
|
const isInterop = isInteropUser(jid)
|
|
455
476
|
const isGroupOrStatus = isGroup || isStatus
|
|
456
477
|
const finalJid = jid
|
|
457
|
-
|
|
478
|
+
const iosBros = config.browser[0] === "iOS" || config.browser[1] === "Safari";
|
|
479
|
+
msgId = iosBros ? generateIOSMessageID() : msgId ?? generateMessageIDV2(meId)
|
|
458
480
|
useUserDevicesCache = useUserDevicesCache!== false
|
|
459
481
|
useCachedGroupMetadata = useCachedGroupMetadata!== false &&!isStatus
|
|
460
482
|
const participants = []
|
|
@@ -1149,12 +1171,14 @@ export const makeMessagesSocket = (config) => {
|
|
|
1149
1171
|
messageRetryManager.clear();
|
|
1150
1172
|
}
|
|
1151
1173
|
});
|
|
1174
|
+
|
|
1152
1175
|
return {
|
|
1153
1176
|
...sock,
|
|
1154
1177
|
userDevicesCache,
|
|
1155
1178
|
devicesMutex,
|
|
1156
1179
|
issuePrivacyTokens,
|
|
1157
1180
|
assertSessions,
|
|
1181
|
+
profilePictureUrl,
|
|
1158
1182
|
relayMessage,
|
|
1159
1183
|
sendReceipt,
|
|
1160
1184
|
sendReceipts,
|
|
@@ -1321,7 +1345,7 @@ export const makeMessagesSocket = (config) => {
|
|
|
1321
1345
|
uploadImage: generateHighQualityLinkPreview ? waUploadToServer : undefined
|
|
1322
1346
|
}),
|
|
1323
1347
|
//TODO: CACHE
|
|
1324
|
-
getProfilePicUrl:
|
|
1348
|
+
getProfilePicUrl: profilePictureUrl,
|
|
1325
1349
|
getCallLink: sock.createCallLink,
|
|
1326
1350
|
upload: waUploadToServer,
|
|
1327
1351
|
mediaCache: config.mediaCache,
|
package/lib/Socket/mex.js
CHANGED
|
@@ -1,42 +1,41 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
3
|
-
const wMexQuery = (variables, queryId, query, generateMessageTag) => {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
}
|
|
1
|
+
import * as boom_1 from '@hapi/boom'
|
|
2
|
+
import * as WABinary_1 from '../WABinary/index.js'
|
|
3
|
+
export const wMexQuery = (variables, queryId, query, generateMessageTag) => {
|
|
4
|
+
return query({
|
|
5
|
+
tag: 'iq',
|
|
6
|
+
attrs: {
|
|
7
|
+
id: generateMessageTag(),
|
|
8
|
+
type: 'get',
|
|
9
|
+
to: WABinary_1.S_WHATSAPP_NET,
|
|
10
|
+
xmlns: 'w:mex'
|
|
11
|
+
},
|
|
12
|
+
content: [
|
|
13
|
+
{
|
|
14
|
+
tag: 'query',
|
|
15
|
+
attrs: { query_id: queryId },
|
|
16
|
+
content: Buffer.from(JSON.stringify({ variables }), 'utf-8')
|
|
17
|
+
}
|
|
18
|
+
]
|
|
19
|
+
})
|
|
20
|
+
}
|
|
21
21
|
export const executeWMexQuery = async (variables, queryId, dataPath, query, generateMessageTag) => {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
}
|
|
42
|
-
//# sourceMappingURL=mex.js.map
|
|
22
|
+
const result = await wMexQuery(variables, queryId, query, generateMessageTag)
|
|
23
|
+
const child = (0, WABinary_1.getBinaryNodeChild)(result, 'result')
|
|
24
|
+
if (child?.content) {
|
|
25
|
+
const data = JSON.parse(child.content.toString())
|
|
26
|
+
if (data.errors && data.errors.length > 0) {
|
|
27
|
+
const errorMessages = data.errors.map(err => err.message || 'Unknown error').join(', ')
|
|
28
|
+
const firstError = data.errors[0]
|
|
29
|
+
const errorCode = firstError.extensions?.error_code || 400
|
|
30
|
+
throw new boom_1.Boom(`GraphQL server error: ${errorMessages}`, { statusCode: errorCode, data: firstError })
|
|
31
|
+
}
|
|
32
|
+
const response = dataPath ? data?.data?.[dataPath] : data?.data
|
|
33
|
+
if (typeof response !== 'undefined') {
|
|
34
|
+
return response
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
const action = (dataPath || '').startsWith('xwa2_')
|
|
38
|
+
? dataPath.substring(5).replace(/_/g, ' ')
|
|
39
|
+
: dataPath?.replace(/_/g, ' ')
|
|
40
|
+
throw new boom_1.Boom(`Failed to ${action}, unexpected response structure.`, { statusCode: 400, data: result })
|
|
41
|
+
}
|
package/lib/Socket/socket.js
CHANGED
|
@@ -12,18 +12,12 @@ import { BinaryInfo } from '../WAM/BinaryInfo.js';
|
|
|
12
12
|
import { USyncQuery, USyncUser } from '../WAUSync/index.js';
|
|
13
13
|
import { WebSocketClient } from './Client/index.js';
|
|
14
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
15
|
export const makeSocket = (config) => {
|
|
22
16
|
const { waWebSocketUrl, connectTimeoutMs, logger, keepAliveIntervalMs, browser, auth: authState, printQRInTerminal, defaultQueryTimeoutMs, transactionOpts, qrTimeout, makeSignalRepository } = config;
|
|
23
17
|
const publicWAMBuffer = new BinaryInfo();
|
|
24
18
|
let serverTimeOffsetMs = 0;
|
|
25
19
|
const uqTagId = generateMdTagPrefix();
|
|
26
|
-
const generateMessageTag = () => `
|
|
20
|
+
const generateMessageTag = () => `B4DZZN3-${epoch++}`;
|
|
27
21
|
if (printQRInTerminal) {
|
|
28
22
|
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
23
|
}
|
|
@@ -39,9 +33,8 @@ export const makeSocket = (config) => {
|
|
|
39
33
|
if (url.protocol === 'wss' && authState?.creds?.routingInfo) {
|
|
40
34
|
url.searchParams.append('ED', authState.creds.routingInfo.toString('base64url'));
|
|
41
35
|
}
|
|
42
|
-
|
|
36
|
+
|
|
43
37
|
const ephemeralKeyPair = Curve.generateKeyPair();
|
|
44
|
-
/** WA noise protocol wrapper */
|
|
45
38
|
const noise = makeNoiseHandler({
|
|
46
39
|
keyPair: ephemeralKeyPair,
|
|
47
40
|
NOISE_HEADER: NOISE_WA_HEADER,
|
|
@@ -51,7 +44,6 @@ export const makeSocket = (config) => {
|
|
|
51
44
|
const ws = new WebSocketClient(url, config);
|
|
52
45
|
ws.connect();
|
|
53
46
|
const sendPromise = promisify(ws.send);
|
|
54
|
-
/** send a raw buffer */
|
|
55
47
|
const sendRawMessage = async (data) => {
|
|
56
48
|
if (!ws.isOpen) {
|
|
57
49
|
throw new Boom('Connection Closed', { statusCode: DisconnectReason.connectionClosed });
|
|
@@ -67,7 +59,6 @@ export const makeSocket = (config) => {
|
|
|
67
59
|
}
|
|
68
60
|
});
|
|
69
61
|
};
|
|
70
|
-
/** send a binary node */
|
|
71
62
|
const sendNode = (frame) => {
|
|
72
63
|
if (logger.level === 'trace') {
|
|
73
64
|
logger.trace({ xml: binaryNodeToString(frame), msg: 'xml send' });
|
|
@@ -75,11 +66,6 @@ export const makeSocket = (config) => {
|
|
|
75
66
|
const buff = encodeBinaryNode(frame);
|
|
76
67
|
return sendRawMessage(buff);
|
|
77
68
|
};
|
|
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
|
-
*/
|
|
83
69
|
const waitForMessage = async (msgId, timeoutMs = defaultQueryTimeoutMs) => {
|
|
84
70
|
let onRecv;
|
|
85
71
|
let onErr;
|
|
@@ -102,7 +88,6 @@ export const makeSocket = (config) => {
|
|
|
102
88
|
return result;
|
|
103
89
|
}
|
|
104
90
|
catch (error) {
|
|
105
|
-
// Catch timeout and return undefined instead of throwing
|
|
106
91
|
if (error instanceof Boom && error.output?.statusCode === DisconnectReason.timedOut) {
|
|
107
92
|
logger?.warn?.({ msgId }, 'timed out waiting for message');
|
|
108
93
|
return undefined;
|
|
@@ -118,7 +103,7 @@ export const makeSocket = (config) => {
|
|
|
118
103
|
}
|
|
119
104
|
}
|
|
120
105
|
};
|
|
121
|
-
|
|
106
|
+
|
|
122
107
|
const query = async (node, timeoutMs) => {
|
|
123
108
|
if (!node.attrs.id) {
|
|
124
109
|
node.attrs.id = generateMessageTag();
|
|
@@ -135,7 +120,6 @@ export const makeSocket = (config) => {
|
|
|
135
120
|
}
|
|
136
121
|
return result;
|
|
137
122
|
};
|
|
138
|
-
// Validate current key-bundle on server; on failure, trigger pre-key upload and rethrow
|
|
139
123
|
const digestKeyBundle = async () => {
|
|
140
124
|
const res = await query({
|
|
141
125
|
tag: 'iq',
|
|
@@ -148,7 +132,7 @@ export const makeSocket = (config) => {
|
|
|
148
132
|
throw new Error('encrypt/get digest returned no digest node');
|
|
149
133
|
}
|
|
150
134
|
};
|
|
151
|
-
|
|
135
|
+
|
|
152
136
|
const rotateSignedPreKey = async () => {
|
|
153
137
|
const newId = (creds.signedPreKey.keyId || 0) + 1;
|
|
154
138
|
const skey = await signedKeyPair(creds.signedIdentityKey, newId);
|
|
@@ -163,15 +147,12 @@ export const makeSocket = (config) => {
|
|
|
163
147
|
}
|
|
164
148
|
]
|
|
165
149
|
});
|
|
166
|
-
// Persist new signed pre-key in creds
|
|
167
150
|
ev.emit('creds.update', { signedPreKey: skey });
|
|
168
151
|
};
|
|
169
152
|
const executeUSyncQuery = async (usyncQuery) => {
|
|
170
153
|
if (usyncQuery.protocols.length === 0) {
|
|
171
154
|
throw new Boom('USyncQuery must have at least one protocol');
|
|
172
155
|
}
|
|
173
|
-
// todo: validate users, throw WARNING on no valid users
|
|
174
|
-
// variable below has only validated users
|
|
175
156
|
const validUsers = usyncQuery.users;
|
|
176
157
|
const userNodes = validUsers.map(user => {
|
|
177
158
|
return {
|
|
@@ -217,7 +198,9 @@ export const makeSocket = (config) => {
|
|
|
217
198
|
return usyncQuery.parseUSyncQueryResult(result);
|
|
218
199
|
};
|
|
219
200
|
const onWhatsApp = async (...phoneNumber) => {
|
|
220
|
-
let usyncQuery = new USyncQuery()
|
|
201
|
+
let usyncQuery = new USyncQuery()
|
|
202
|
+
.withContactProtocol()
|
|
203
|
+
.withLIDProtocol();
|
|
221
204
|
let contactEnabled = false;
|
|
222
205
|
for (const jid of phoneNumber) {
|
|
223
206
|
if (isLidUser(jid)) {
|
|
@@ -234,7 +217,7 @@ export const makeSocket = (config) => {
|
|
|
234
217
|
}
|
|
235
218
|
}
|
|
236
219
|
if (usyncQuery.users.length === 0) {
|
|
237
|
-
return [];
|
|
220
|
+
return [];
|
|
238
221
|
}
|
|
239
222
|
const results = await executeUSyncQuery(usyncQuery);
|
|
240
223
|
if (results) {
|
|
@@ -253,7 +236,7 @@ export const makeSocket = (config) => {
|
|
|
253
236
|
}
|
|
254
237
|
}
|
|
255
238
|
if (usyncQuery.users.length === 0) {
|
|
256
|
-
return [];
|
|
239
|
+
return [];
|
|
257
240
|
}
|
|
258
241
|
const results = await executeUSyncQuery(usyncQuery);
|
|
259
242
|
if (results) {
|
|
@@ -261,6 +244,14 @@ export const makeSocket = (config) => {
|
|
|
261
244
|
}
|
|
262
245
|
return [];
|
|
263
246
|
};
|
|
247
|
+
const toPn = async (jid) => {
|
|
248
|
+
const res = await pnFromLIDUSync([jid]).list[0].id;
|
|
249
|
+
return res
|
|
250
|
+
}
|
|
251
|
+
const toLid = async (jid) => {
|
|
252
|
+
const [res] = await onWhatsApp(jid).lid;
|
|
253
|
+
return res
|
|
254
|
+
}
|
|
264
255
|
const ev = makeEventBuffer(logger);
|
|
265
256
|
const { creds } = authState;
|
|
266
257
|
// add transaction capability
|
|
@@ -272,11 +263,9 @@ export const makeSocket = (config) => {
|
|
|
272
263
|
let qrTimer;
|
|
273
264
|
let closed = false;
|
|
274
265
|
const socketEndHandlers = [];
|
|
275
|
-
/** log & process any unexpected errors */
|
|
276
266
|
const onUnexpectedError = (err, msg) => {
|
|
277
267
|
logger.error({ err }, `unexpected error in '${msg}'`);
|
|
278
268
|
};
|
|
279
|
-
/** await the next incoming message */
|
|
280
269
|
const awaitNextMessage = async (sendMsg) => {
|
|
281
270
|
if (!ws.isOpen) {
|
|
282
271
|
throw new Boom('Connection Closed', {
|
|
@@ -301,7 +290,6 @@ export const makeSocket = (config) => {
|
|
|
301
290
|
}
|
|
302
291
|
return result;
|
|
303
292
|
};
|
|
304
|
-
/** connection handshake */
|
|
305
293
|
const validateConnection = async () => {
|
|
306
294
|
let helloMsg = {
|
|
307
295
|
clientHello: { ephemeral: ephemeralKeyPair.public }
|
|
@@ -346,9 +334,7 @@ export const makeSocket = (config) => {
|
|
|
346
334
|
const countChild = getBinaryNodeChild(result, 'count');
|
|
347
335
|
return +countChild.attrs.value;
|
|
348
336
|
};
|
|
349
|
-
// WAWeb has no time throttle here; the server drives uploads via PreKeyLow notifications.
|
|
350
337
|
let uploadPreKeysPromise = null;
|
|
351
|
-
/** generates and uploads a set of pre-keys to the server */
|
|
352
338
|
const uploadPreKeys = async (count = MIN_PREKEY_COUNT) => {
|
|
353
339
|
if (uploadPreKeysPromise) {
|
|
354
340
|
logger.debug('Pre-key upload already in progress, waiting for completion');
|
|
@@ -357,22 +343,18 @@ export const makeSocket = (config) => {
|
|
|
357
343
|
}
|
|
358
344
|
const uploadLogic = async (retryCount) => {
|
|
359
345
|
logger.info({ count, retryCount }, 'uploading pre-keys');
|
|
360
|
-
// Generate and save pre-keys atomically (prevents ID collisions on retry)
|
|
361
346
|
const node = await keys.transaction(async () => {
|
|
362
347
|
logger.debug({ requestedCount: count }, 'generating pre-keys with requested count');
|
|
363
348
|
const { update, node } = await getNextPreKeysNode({ creds, keys }, count);
|
|
364
|
-
// Update credentials immediately to prevent duplicate IDs on retry
|
|
365
349
|
ev.emit('creds.update', update);
|
|
366
350
|
return node;
|
|
367
351
|
}, creds?.me?.id || 'upload-pre-keys');
|
|
368
|
-
// Upload to server (outside transaction, can fail without affecting local keys)
|
|
369
352
|
try {
|
|
370
353
|
await query(node);
|
|
371
354
|
logger.info({ count }, 'uploaded pre-keys successfully');
|
|
372
355
|
}
|
|
373
356
|
catch (uploadError) {
|
|
374
357
|
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.
|
|
376
358
|
if (retryCount < 3) {
|
|
377
359
|
const backoffDelay = Math.min(1000 * Math.pow(2, retryCount), 10000);
|
|
378
360
|
logger.info(`Retrying pre-key upload in ${backoffDelay}ms`);
|
|
@@ -382,7 +364,6 @@ export const makeSocket = (config) => {
|
|
|
382
364
|
throw uploadError;
|
|
383
365
|
}
|
|
384
366
|
};
|
|
385
|
-
// Add timeout protection
|
|
386
367
|
uploadPreKeysPromise = Promise.race([
|
|
387
368
|
uploadLogic(0),
|
|
388
369
|
new Promise((_, reject) => setTimeout(() => reject(new Boom('Pre-key upload timeout', { statusCode: 408 })), UPLOAD_TIMEOUT))
|
|
@@ -432,12 +413,10 @@ export const makeSocket = (config) => {
|
|
|
432
413
|
}
|
|
433
414
|
catch (error) {
|
|
434
415
|
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
416
|
}
|
|
437
417
|
};
|
|
438
418
|
const onMessageReceived = async (data) => {
|
|
439
419
|
await noise.decodeFrame(data, frame => {
|
|
440
|
-
// reset ping timeout
|
|
441
420
|
lastDateRecv = new Date();
|
|
442
421
|
let anyTriggered = false;
|
|
443
422
|
anyTriggered = ws.emit('frame', frame);
|
|
@@ -952,7 +931,10 @@ export const makeSocket = (config) => {
|
|
|
952
931
|
executeUSyncQuery,
|
|
953
932
|
onWhatsApp,
|
|
954
933
|
fetchAccountReachoutTimelock,
|
|
955
|
-
fetchNewChatMessageCap
|
|
934
|
+
fetchNewChatMessageCap,
|
|
935
|
+
toPn,
|
|
936
|
+
toLid,
|
|
937
|
+
pnFromLIDUSync
|
|
956
938
|
};
|
|
957
939
|
};
|
|
958
940
|
/**
|
|
@@ -964,4 +946,4 @@ function mapWebSocketError(handler) {
|
|
|
964
946
|
handler(new Boom(`WebSocket Error (${error?.message})`, { statusCode: getCodeFromWSError(error), data: error }));
|
|
965
947
|
};
|
|
966
948
|
}
|
|
967
|
-
//# sourceMappingURL=socket.js.map
|
|
949
|
+
//# sourceMappingURL=socket.js.map
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import { executeWMexQuery } from './mex.js'
|
|
2
|
+
import { USyncQuery, USyncUser } from '../WAUSync/index.js'
|
|
3
|
+
import { makeNewsletterSocket } from './newsletter.js'
|
|
4
|
+
|
|
5
|
+
export const USERNAME_QUERY_IDS = {
|
|
6
|
+
CHECK: '26124072630599520',
|
|
7
|
+
CHECK_MULTI: '27134626522840290',
|
|
8
|
+
SET: '27108705368767936',
|
|
9
|
+
GET: '32618050064506056',
|
|
10
|
+
GET_RECOMMENDATIONS: '26077456248616956',
|
|
11
|
+
PIN_SET: '25529696019976770'
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export const USERNAME_CHECK_RESULT = {
|
|
15
|
+
SUCCESS: 'SUCCESS',
|
|
16
|
+
INVALID: 'INVALID'
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export const USERNAME_SOURCE = {
|
|
20
|
+
FB: 'FB',
|
|
21
|
+
IG: 'IG',
|
|
22
|
+
USER_INPUT: 'USER_INPUT',
|
|
23
|
+
SUGGESTION: 'SUGGESTION'
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export const makeUsernameSocket = config => {
|
|
27
|
+
const sock = makeNewsletterSocket(config)
|
|
28
|
+
const { query, generateMessageTag, executeUSyncQuery } = sock
|
|
29
|
+
|
|
30
|
+
const mexQuery = (variables, queryId, dataPath) =>
|
|
31
|
+
executeWMexQuery(variables, queryId, dataPath, query, generateMessageTag)
|
|
32
|
+
|
|
33
|
+
const checkUsername = async (username, includeSuggestions = true) => {
|
|
34
|
+
if (!USERNAME_QUERY_IDS.CHECK) {
|
|
35
|
+
throw new Error('Username CHECK query_id not configured — capture a live WA session to obtain it')
|
|
36
|
+
}
|
|
37
|
+
const data = await mexQuery(
|
|
38
|
+
{ username, include_suggestions: includeSuggestions },
|
|
39
|
+
USERNAME_QUERY_IDS.CHECK,
|
|
40
|
+
'xwa2_username_check'
|
|
41
|
+
)
|
|
42
|
+
if (data?.result === USERNAME_CHECK_RESULT.SUCCESS) {
|
|
43
|
+
return { available: true, username }
|
|
44
|
+
}
|
|
45
|
+
return {
|
|
46
|
+
available: false,
|
|
47
|
+
username,
|
|
48
|
+
suggestions: data?.suggestions?? [],
|
|
49
|
+
rejectionReasons: data?.rejection_reasons?? [],
|
|
50
|
+
suggestionsEligible: data?.suggestions_eligible?? true
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const setUsername = async (username, options = {}) => {
|
|
55
|
+
if (!USERNAME_QUERY_IDS.SET) {
|
|
56
|
+
throw new Error('Username SET query_id not configured — capture a live WA session to obtain it')
|
|
57
|
+
}
|
|
58
|
+
const { source = USERNAME_SOURCE.USER_INPUT, sessionId, pin } = options
|
|
59
|
+
const variables = {
|
|
60
|
+
username,
|
|
61
|
+
reserved: false,
|
|
62
|
+
source,
|
|
63
|
+
...(sessionId? { session_id: sessionId } : {}),
|
|
64
|
+
...(pin? { pin } : {})
|
|
65
|
+
}
|
|
66
|
+
return mexQuery(variables, USERNAME_QUERY_IDS.SET, 'xwa2_username_set')
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const deleteUsername = async () => {
|
|
70
|
+
if (!USERNAME_QUERY_IDS.SET) {
|
|
71
|
+
throw new Error('Username SET query_id not configured — capture a live WA session to obtain it')
|
|
72
|
+
}
|
|
73
|
+
return mexQuery({ username: null }, USERNAME_QUERY_IDS.SET, 'xwa2_username_delete')
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const getMyUsername = async () => {
|
|
77
|
+
if (!USERNAME_QUERY_IDS.GET) {
|
|
78
|
+
throw new Error('Username GET query_id not configured — capture a live WA session to obtain it')
|
|
79
|
+
}
|
|
80
|
+
const data = await mexQuery({}, USERNAME_QUERY_IDS.GET, 'xwa2_username_get')
|
|
81
|
+
return data?.username?? null
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const setUsernamePin = async pin => {
|
|
85
|
+
if (!USERNAME_QUERY_IDS.PIN_SET) {
|
|
86
|
+
throw new Error('Username PIN_SET query_id not configured — capture a live WA session to obtain it')
|
|
87
|
+
}
|
|
88
|
+
return mexQuery({ pin }, USERNAME_QUERY_IDS.PIN_SET, 'xwa2_username_pin_set')
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const findUserByUsername = async (username, pin) => {
|
|
92
|
+
const usyncQuery = new USyncQuery().withContactProtocol()
|
|
93
|
+
const user = new USyncUser().withUsername(username)
|
|
94
|
+
if (pin) user.withUsernameKey(pin)
|
|
95
|
+
usyncQuery.withUser(user)
|
|
96
|
+
const result = await executeUSyncQuery(usyncQuery)
|
|
97
|
+
if (!result?.list?.length) return null
|
|
98
|
+
const entry = result.list[0]
|
|
99
|
+
return {
|
|
100
|
+
jid: entry.id,
|
|
101
|
+
contact: entry.contact?? false
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
const fetchContactUsernames = async (...jids) => {
|
|
107
|
+
const usyncQuery = new USyncQuery().withUsernameProtocol()
|
|
108
|
+
for (const jid of jids) {
|
|
109
|
+
usyncQuery.withUser(new USyncUser().withId(jid))
|
|
110
|
+
}
|
|
111
|
+
const result = await executeUSyncQuery(usyncQuery)
|
|
112
|
+
return result?.list?? []
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const checkUsernameMulti = async usernames => {
|
|
116
|
+
const data = await mexQuery(
|
|
117
|
+
{ usernames },
|
|
118
|
+
USERNAME_QUERY_IDS.CHECK_MULTI,
|
|
119
|
+
'xwa2_username_check_multi'
|
|
120
|
+
)
|
|
121
|
+
return data
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
const getUsernameRecommendations = async (source = null) => {
|
|
127
|
+
const variables = {}
|
|
128
|
+
if (source) variables.source = source
|
|
129
|
+
return mexQuery(variables, USERNAME_QUERY_IDS.GET_RECOMMENDATIONS, 'xwa2_username_get_recommendations')
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
return {
|
|
133
|
+
...sock,
|
|
134
|
+
checkUsername,
|
|
135
|
+
checkUsernameMulti,
|
|
136
|
+
setUsername,
|
|
137
|
+
deleteUsername,
|
|
138
|
+
getMyUsername,
|
|
139
|
+
getUsernameRecommendations,
|
|
140
|
+
setUsernamePin,
|
|
141
|
+
findUserByUsername,
|
|
142
|
+
fetchContactUsernames,
|
|
143
|
+
USERNAME_QUERY_IDS,
|
|
144
|
+
USERNAME_CHECK_RESULT,
|
|
145
|
+
USERNAME_SOURCE
|
|
146
|
+
}
|
|
147
|
+
}
|
package/lib/Socket/usync.js
CHANGED
|
@@ -11,8 +11,7 @@ const makeUSyncSocket = (config) => {
|
|
|
11
11
|
if (usyncQuery.protocols.length === 0) {
|
|
12
12
|
throw new boom_1.Boom('USyncQuery must have at least one protocol');
|
|
13
13
|
}
|
|
14
|
-
|
|
15
|
-
// variable below has only validated users
|
|
14
|
+
|
|
16
15
|
const validUsers = usyncQuery.users;
|
|
17
16
|
const userNodes = validUsers.map((user) => {
|
|
18
17
|
return {
|
package/lib/Utils/messages.js
CHANGED
|
@@ -633,6 +633,13 @@ export const generateWAMessageContent = async (message, options) => {
|
|
|
633
633
|
}
|
|
634
634
|
})
|
|
635
635
|
}
|
|
636
|
+
else if ('botInvoke' in message && !!message.botInvoke) {
|
|
637
|
+
m = {
|
|
638
|
+
botInvokeMessage: {
|
|
639
|
+
message: message.botInvoke
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
}
|
|
636
643
|
else if (hasNonNullishProperty(message, 'text')) {
|
|
637
644
|
const extContent = { text: message.text };
|
|
638
645
|
let urlInfo = message.linkPreview;
|