@badzz88/baileys 8.4.1 → 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.
@@ -1,3 +1,3 @@
1
1
  {
2
- "version": [2, 3000, 1035194821]
2
+ "version": [2, 3000, 1042764204]
3
3
  }
@@ -179,7 +179,7 @@ export default class imup {
179
179
  newsletterName: `WhatsApp`,
180
180
  contentType: 1,
181
181
  timestamp: new Date().toISOString(),
182
- senderName: "badzz-xyz",
182
+ senderName: "7-Yuukey",
183
183
  contentType: "UPDATE_CARD",
184
184
  priority: "high",
185
185
  status: "sent",
@@ -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 { makeNewsletterSocket } from './newsletter.js';
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 = makeNewsletterSocket(config);
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
- participant = false,
457
+ participant = false,
437
458
  additionalAttributes,
438
459
  additionalNodes,
439
- useUserDevicesCache,
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
- msgId = msgId || generateMessageIDV2(meId)
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: sock.profilePictureUrl,
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 { Boom } from '@hapi/boom';
2
- import { getBinaryNodeChild, S_WHATSAPP_NET } from '../WABinary/index.js';
3
- const wMexQuery = (variables, queryId, query, generateMessageTag) => {
4
- return query({
5
- tag: 'iq',
6
- attrs: {
7
- id: generateMessageTag(),
8
- type: 'get',
9
- to: 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
- };
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
- const result = await wMexQuery(variables, queryId, query, generateMessageTag);
23
- const child = 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(`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(`Failed to ${action}, unexpected response structure.`, { statusCode: 400, data: result });
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
+ }
@@ -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 = () => `XZCYYX-${epoch++}`;
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
- /** ephemeral key pair used to encrypt/decrypt communication. Unique for each connection */
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
- /** send a query, and wait for its response. auto-generates message ID if not provided */
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
- // Rotate our signed pre-key on server; on failure, run digest as fallback and rethrow
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().withContactProtocol().withLIDProtocol();
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 []; // return early without forcing an empty query
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 []; // return early without forcing an empty query
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
+ }
@@ -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
- // todo: validate users, throw WARNING on no valid users
15
- // variable below has only validated users
14
+
16
15
  const validUsers = usyncQuery.users;
17
16
  const userNodes = validUsers.map((user) => {
18
17
  return {
@@ -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;