@realvare/based 2.7.62 → 2.7.71

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 (57) hide show
  1. package/README.MD +1062 -282
  2. package/WAProto/WAProto.proto +1073 -244
  3. package/WAProto/index.d.ts +16282 -8183
  4. package/WAProto/index.js +76605 -50628
  5. package/engine-requirements.js +10 -10
  6. package/lib/Defaults/baileys-version.json +1 -1
  7. package/lib/Defaults/index.d.ts +4 -2
  8. package/lib/Defaults/index.js +8 -6
  9. package/lib/Signal/Group/ciphertext-message.d.ts +1 -1
  10. package/lib/Signal/Group/ciphertext-message.js +1 -1
  11. package/lib/Signal/Group/sender-message-key.d.ts +1 -1
  12. package/lib/Signal/Group/sender-message-key.js +1 -1
  13. package/lib/Signal/libsignal.d.ts +1 -1
  14. package/lib/Socket/business.d.ts +1 -1
  15. package/lib/Socket/business.js +1 -1
  16. package/lib/Socket/chats.d.ts +4 -1
  17. package/lib/Socket/chats.js +213 -36
  18. package/lib/Socket/groups.js +87 -15
  19. package/lib/Socket/index.js +9 -0
  20. package/lib/Socket/messages-interactive.js +259 -0
  21. package/lib/Socket/messages-recv.js +1473 -1228
  22. package/lib/Socket/messages-send.js +437 -469
  23. package/lib/Socket/socket.js +143 -26
  24. package/lib/Socket/usync.js +57 -4
  25. package/lib/Store/make-in-memory-store.js +28 -15
  26. package/lib/Types/Auth.d.ts +4 -0
  27. package/lib/Types/Message.d.ts +316 -6
  28. package/lib/Types/Message.js +1 -1
  29. package/lib/Types/Socket.d.ts +2 -0
  30. package/lib/Utils/cache-manager.d.ts +16 -0
  31. package/lib/Utils/cache-manager.js +22 -5
  32. package/lib/Utils/chat-utils.js +17 -13
  33. package/lib/Utils/decode-wa-message.js +1 -11
  34. package/lib/Utils/event-buffer.js +103 -2
  35. package/lib/Utils/generics.js +5 -6
  36. package/lib/Utils/index.d.ts +5 -0
  37. package/lib/Utils/index.js +3 -0
  38. package/lib/Utils/jid-validation.d.ts +2 -0
  39. package/lib/Utils/jid-validation.js +43 -10
  40. package/lib/Utils/link-preview.js +38 -28
  41. package/lib/Utils/messages-media.d.ts +1 -1
  42. package/lib/Utils/messages-media.js +22 -53
  43. package/lib/Utils/messages.js +653 -65
  44. package/lib/Utils/performance-config.d.ts +2 -0
  45. package/lib/Utils/performance-config.js +16 -7
  46. package/lib/Utils/process-message.js +124 -12
  47. package/lib/Utils/rate-limiter.js +15 -20
  48. package/lib/WABinary/generic-utils.js +5 -1
  49. package/lib/WABinary/jid-utils.d.ts +1 -0
  50. package/lib/WABinary/jid-utils.js +265 -5
  51. package/lib/WAUSync/Protocols/USyncContactProtocol.js +75 -5
  52. package/lib/WAUSync/Protocols/USyncDeviceProtocol.js +59 -6
  53. package/lib/WAUSync/USyncQuery.js +64 -6
  54. package/lib/index.d.ts +1 -0
  55. package/lib/index.js +5 -4
  56. package/package.json +10 -15
  57. package/WAProto/index.ts.ts +0 -53473
@@ -1,10 +1,10 @@
1
- const major = parseInt(process.versions.node.split('.')[0], 10);
2
-
3
- if (major < 20) {
4
- console.error(
5
- `\n❌ This package requires Node.js 20+ to run reliably.\n` +
6
- ` You are using Node.js ${process.versions.node}.\n` +
7
- ` Please upgrade to Node.js 20+ to proceed.\n`
8
- );
9
- process.exit(1);
10
- }
1
+ const major = parseInt(process.versions.node.split('.')[0], 10);
2
+
3
+ if (major < 20) {
4
+ console.error(
5
+ `\n❌ This package requires Node.js 20+ to run reliably.\n` +
6
+ ` You are using Node.js ${process.versions.node}.\n` +
7
+ ` Please upgrade to Node.js 20+ to proceed.\n`
8
+ );
9
+ process.exit(1);
10
+ }
@@ -1,3 +1,3 @@
1
1
  {
2
- "version": [2, 3000, 1030470917]
2
+ "version": [2, 3000, 1032094433]
3
3
  }
@@ -15,7 +15,7 @@ export declare const URL_REGEX: RegExp;
15
15
  export declare const WA_CERT_DETAILS: {
16
16
  SERIAL: number;
17
17
  };
18
- export declare const PROCESSABLE_HISTORY_TYPES: proto.Message.HistorySyncNotification.HistorySyncType[];
18
+ export declare const PROCESSABLE_HISTORY_TYPES: proto.Message.HistorySyncType[];
19
19
  export declare const DEFAULT_CONNECTION_CONFIG: SocketConfig;
20
20
  export declare const MEDIA_PATH_MAP: {
21
21
  [T in MediaType]?: string;
@@ -48,4 +48,6 @@ export declare const DEFAULT_CACHE_TTLS: {
48
48
  MSG_RETRY: number;
49
49
  CALL_OFFER: number;
50
50
  USER_DEVICES: number;
51
- };
51
+ LID_JID: number;
52
+ PROFILE_PIC: number;
53
+ };
@@ -18,18 +18,18 @@ exports.WA_DEFAULT_EPHEMERAL = 7 * 24 * 60 * 60;
18
18
  exports.NOISE_MODE = 'Noise_XX_25519_AESGCM_SHA256\0\0\0\0';
19
19
  exports.DICT_VERSION = 3;
20
20
  exports.KEY_BUNDLE_TYPE = Buffer.from([5]);
21
- exports.NOISE_WA_HEADER = Buffer.from([87, 65, 6, exports.DICT_VERSION]); // last isambasedd "DICT_VERSION"
21
+ exports.NOISE_WA_HEADER = Buffer.from([87, 65, 6, exports.DICT_VERSION]); // last is "DICT_VERSION"
22
22
  /** from: https://stackoverflow.com/questions/3809401/what-is-a-good-regular-expression-to-match-a-url */
23
23
  exports.URL_REGEX = /https:\/\/(?![^:@\/\s]+:[^:@\/\s]+@)[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}(:\d+)?(\/[^\s]*)?/g;
24
24
  exports.WA_CERT_DETAILS = {
25
25
  SERIAL: 0,
26
26
  };
27
27
  exports.PROCESSABLE_HISTORY_TYPES = [
28
- WAProto_1.proto.Message.HistorySyncNotification.HistorySyncType.INITIAL_BOOTSTRAP,
29
- WAProto_1.proto.Message.HistorySyncNotification.HistorySyncType.PUSH_NAME,
30
- WAProto_1.proto.Message.HistorySyncNotification.HistorySyncType.RECENT,
31
- WAProto_1.proto.Message.HistorySyncNotification.HistorySyncType.FULL,
32
- WAProto_1.proto.Message.HistorySyncNotification.HistorySyncType.ON_DEMAND,
28
+ WAProto_1.proto.Message.HistorySyncType.INITIAL_BOOTSTRAP,
29
+ WAProto_1.proto.Message.HistorySyncType.PUSH_NAME,
30
+ WAProto_1.proto.Message.HistorySyncType.RECENT,
31
+ WAProto_1.proto.Message.HistorySyncType.FULL,
32
+ WAProto_1.proto.Message.HistorySyncType.ON_DEMAND,
33
33
  ];
34
34
  exports.DEFAULT_CONNECTION_CONFIG = {
35
35
  version: baileys_version_json_1.version,
@@ -103,4 +103,6 @@ exports.DEFAULT_CACHE_TTLS = {
103
103
  MSG_RETRY: 60 * 60, // 1 hour
104
104
  CALL_OFFER: 5 * 60, // 5 minutes
105
105
  USER_DEVICES: 5 * 60, // 5 minutes
106
+ LID_JID: 5 * 60, // 5 minutes
107
+ PROFILE_PIC: 60 * 60, // 1 hour
106
108
  };
@@ -6,4 +6,4 @@ export declare class CiphertextMessage {
6
6
  readonly SENDERKEY_TYPE: number;
7
7
  readonly SENDERKEY_DISTRIBUTION_TYPE: number;
8
8
  readonly ENCRYPTED_MESSAGE_OVERHEAD: number;
9
- }
9
+ }
@@ -12,4 +12,4 @@ class CiphertextMessage {
12
12
  this.ENCRYPTED_MESSAGE_OVERHEAD = 53;
13
13
  }
14
14
  }
15
- exports.CiphertextMessage = CiphertextMessage;
15
+ exports.CiphertextMessage = CiphertextMessage;
@@ -8,4 +8,4 @@ export declare class SenderMessageKey {
8
8
  getIv(): Uint8Array;
9
9
  getCipherKey(): Uint8Array;
10
10
  getSeed(): Uint8Array;
11
- }
11
+ }
@@ -26,4 +26,4 @@ class SenderMessageKey {
26
26
  return this.seed;
27
27
  }
28
28
  }
29
- exports.SenderMessageKey = SenderMessageKey;
29
+ exports.SenderMessageKey = SenderMessageKey;
@@ -1,3 +1,3 @@
1
1
  import { SignalAuthState } from '../Types';
2
2
  import { SignalRepository } from '../Types/Signal';
3
- export declare function makeLibSignalRepository(auth: SignalAuthState): SignalRepository;
3
+ export declare function makeLibSignalRepository(auth: SignalAuthState): SignalRepository;
@@ -169,4 +169,4 @@ export declare const makeBusinessSocket: (config: SocketConfig) => {
169
169
  requestPairingCode: (phoneNumber: any, pairKey?: string) => Promise<string>;
170
170
  waitForConnectionUpdate: (check: (u: Partial<import("../Types").ConnectionState>) => Promise<boolean | undefined>, timeoutMs?: number) => Promise<void>;
171
171
  sendWAMBuffer: (wamBuffer: Buffer) => Promise<any>;
172
- };
172
+ };
@@ -257,4 +257,4 @@ const makeBusinessSocket = (config) => {
257
257
  productUpdate
258
258
  };
259
259
  };
260
- exports.makeBusinessSocket = makeBusinessSocket;
260
+ exports.makeBusinessSocket = makeBusinessSocket;
@@ -28,6 +28,9 @@ export declare const makeChatsSocket: (config: SocketConfig) => {
28
28
  updateProfileStatus: (status: string) => Promise<void>;
29
29
  updateProfileName: (name: string) => Promise<void>;
30
30
  updateBlockStatus: (jid: string, action: "block" | "unblock") => Promise<void>;
31
+ reportUser: (jid: string, reason?: "spam" | "harassment" | "impersonation" | "scam" | "other", waitForAckMs?: number) => Promise<boolean>;
32
+ reportMessage: (jid: string, messageId: string, reason?: "spam" | "harassment" | "impersonation" | "scam" | "other", participantJid?: string, waitForAckMs?: number) => Promise<boolean>;
33
+ reportAndBlockUser: (jid: string, reason?: "spam" | "harassment" | "impersonation" | "scam" | "other", waitForAckMs?: number) => Promise<boolean>;
31
34
  updateCallPrivacy: (value: WAPrivacyCallValue) => Promise<void>;
32
35
  updateMessagesPrivacy: (value: WAPrivacyMessagesValue) => Promise<void>;
33
36
  updateLastSeenPrivacy: (value: WAPrivacyValue) => Promise<void>;
@@ -79,4 +82,4 @@ export declare const makeChatsSocket: (config: SocketConfig) => {
79
82
  requestPairingCode: (phoneNumber: any, pairKey?: string) => Promise<string>;
80
83
  waitForConnectionUpdate: (check: (u: Partial<import("../Types").ConnectionState>) => Promise<boolean | undefined>, timeoutMs?: number) => Promise<void>;
81
84
  sendWAMBuffer: (wamBuffer: Buffer) => Promise<any>;
82
- };
85
+ };
@@ -15,7 +15,9 @@ const process_message_1 = __importDefault(require("../Utils/process-message"));
15
15
  const WABinary_1 = require("../WABinary");
16
16
  const WAUSync_1 = require("../WAUSync");
17
17
  const usync_1 = require("./usync");
18
+ const retry_1 = require("../Utils/retry");
18
19
  const MAX_SYNC_ATTEMPTS = 2;
20
+ const APP_STATE_RESYNC_COOLDOWN_MS = 60000;
19
21
  const makeChatsSocket = (config) => {
20
22
  const { logger, markOnlineOnConnect, fireInitQueries, appStateMacVerification, shouldIgnoreJid, shouldSyncHistoryMessage, } = config;
21
23
  const sock = (0, usync_1.makeUSyncSocket)(config);
@@ -23,6 +25,8 @@ const makeChatsSocket = (config) => {
23
25
  let privacySettings;
24
26
  let needToFlushWithAppStateSync = false;
25
27
  let pendingAppStateSync = false;
28
+ let lastSelfPresenceType;
29
+ let lastSelfPresenceTs = 0;
26
30
  /** this mutex ensures that the notifications (receipts, messages etc.) are processed in order */
27
31
  const processingMutex = (0, make_mutex_1.makeMutex)();
28
32
  const placeholderResendCache = config.placeholderResendCache || new node_cache_1.default({
@@ -32,12 +36,15 @@ const makeChatsSocket = (config) => {
32
36
  if (!config.placeholderResendCache) {
33
37
  config.placeholderResendCache = placeholderResendCache;
34
38
  }
35
- const resolveJid = (jid) => {
36
- if (typeof jid === 'string' && jid.endsWith('@lid')) {
37
- return (0, WABinary_1.lidToJid)(jid);
38
- }
39
- return jid;
40
- };
39
+ const profilePictureUrlCache = config.profilePictureUrlCache || new node_cache_1.default({
40
+ stdTTL: Defaults_1.DEFAULT_CACHE_TTLS.PROFILE_PIC,
41
+ useClones: false
42
+ });
43
+ if (!config.profilePictureUrlCache) {
44
+ config.profilePictureUrlCache = profilePictureUrlCache;
45
+ }
46
+ const inFlightProfilePictureUrl = new Map();
47
+ const resolveJid = WABinary_1.resolveJid;
41
48
  /** helper function to fetch the given app state sync key */
42
49
  const getAppStateSyncKey = async (keyId) => {
43
50
  const { [keyId]: key } = await authState.keys.get('app-state-sync-key', [keyId]);
@@ -293,6 +300,110 @@ const makeChatsSocket = (config) => {
293
300
  ]
294
301
  });
295
302
  };
303
+ const reportUser = async (jid, reason = 'spam', waitForAckMs = 5000) => {
304
+ jid = resolveJid(jid);
305
+ const reasonCodes = {
306
+ 'spam': 1,
307
+ 'harassment': 2,
308
+ 'impersonation': 3,
309
+ 'scam': 4,
310
+ 'other': 5
311
+ };
312
+ const reasonCode = reasonCodes[reason] || 1;
313
+ const reportTo = WABinary_1.S_WHATSAPP_NET.replace(/^@/, '');
314
+ const node = {
315
+ tag: 'iq',
316
+ attrs: {
317
+ id: generateMessageTag(),
318
+ xmlns: 'w:report',
319
+ to: reportTo,
320
+ type: 'set'
321
+ },
322
+ content: [
323
+ {
324
+ tag: 'user',
325
+ attrs: {
326
+ jid,
327
+ reason: reasonCode.toString()
328
+ }
329
+ }
330
+ ]
331
+ };
332
+ logger.debug({ node }, 'sending report user node');
333
+ try {
334
+ await query(node, waitForAckMs);
335
+ logger.info({ jid, reason }, 'reported user (ack received)');
336
+ return true;
337
+ }
338
+ catch (err) {
339
+ const statusCode = err?.output?.statusCode || err?.statusCode;
340
+ const isTimeout = statusCode === 408 || (err?.message && err.message.toLowerCase().includes('timed out'));
341
+ if (isTimeout) {
342
+ logger.warn({ jid, reason }, 'reported user (no ack; request likely sent)');
343
+ return false;
344
+ }
345
+ throw err;
346
+ }
347
+ };
348
+ const reportMessage = async (jid, messageId, reason = 'spam', participantJid, waitForAckMs = 5000) => {
349
+ jid = resolveJid(jid);
350
+ if (participantJid) {
351
+ participantJid = resolveJid(participantJid);
352
+ }
353
+ const reasonCodes = {
354
+ 'spam': 1,
355
+ 'harassment': 2,
356
+ 'impersonation': 3,
357
+ 'scam': 4,
358
+ 'other': 5
359
+ };
360
+ const reasonCode = reasonCodes[reason] || 1;
361
+ const reportTo = WABinary_1.S_WHATSAPP_NET.replace(/^@/, '');
362
+ const msgAttrs = {
363
+ id: messageId,
364
+ reason: reasonCode.toString()
365
+ };
366
+ if (participantJid) {
367
+ msgAttrs.participant = participantJid;
368
+ }
369
+ const node = {
370
+ tag: 'iq',
371
+ attrs: {
372
+ id: generateMessageTag(),
373
+ xmlns: 'w:report',
374
+ to: reportTo,
375
+ type: 'set'
376
+ },
377
+ content: [
378
+ {
379
+ tag: 'message',
380
+ attrs: msgAttrs
381
+ }
382
+ ]
383
+ };
384
+ logger.debug({ node }, 'sending report message node');
385
+ try {
386
+ await query(node, waitForAckMs);
387
+ logger.info({ jid, messageId, reason, participantJid }, 'reported message (ack received)');
388
+ return true;
389
+ }
390
+ catch (err) {
391
+ const statusCode = err?.output?.statusCode || err?.statusCode;
392
+ const isTimeout = statusCode === 408 || (err?.message && err.message.toLowerCase().includes('timed out'));
393
+ if (isTimeout) {
394
+ logger.warn({ jid, messageId, reason, participantJid }, 'reported message (no ack; request likely sent)');
395
+ return false;
396
+ }
397
+ throw err;
398
+ }
399
+ };
400
+ const reportAndBlockUser = async (jid, reason = 'spam', waitForAckMs = 5000) => {
401
+ jid = resolveJid(jid);
402
+ const ack = await reportUser(jid, reason, waitForAckMs);
403
+ await updateBlockStatus(jid, 'block');
404
+ logger.info({ jid, reason, ack }, 'reported and blocked user');
405
+ return ack;
406
+ };
296
407
  const getBusinessProfile = async (jid) => {
297
408
  jid = resolveJid(jid);
298
409
  var _a, _b, _c, _d, _e, _f, _g;
@@ -367,14 +478,23 @@ const makeChatsSocket = (config) => {
367
478
  }
368
479
  };
369
480
  };
481
+ const appStateResyncCooldown = new Map();
370
482
  const resyncAppState = ev.createBufferedFunction(async (collections, isInitialSync) => {
483
+ const now = Date.now();
484
+ const collectionsToSync = collections.filter(name => {
485
+ const until = appStateResyncCooldown.get(name) || 0;
486
+ return until <= now;
487
+ });
488
+ if (!collectionsToSync.length) {
489
+ return;
490
+ }
371
491
  // we use this to determine which events to fire
372
492
  // otherwise when we resync from scratch -- all notifications will fire
373
493
  const initialVersionMap = {};
374
494
  const globalMutationMap = {};
375
495
  await authState.keys.transaction(async () => {
376
496
  var _a;
377
- const collectionsToHandle = new Set(collections);
497
+ const collectionsToHandle = new Set(collectionsToSync);
378
498
  // in case something goes wrong -- ensure we don't enter a loop that cannot be exited from
379
499
  const attemptsMap = {};
380
500
  // keep executing till all collections are done
@@ -457,6 +577,9 @@ const makeChatsSocket = (config) => {
457
577
  || error.name === 'TypeError';
458
578
  logger.info({ name, error: error.stack }, `failed to sync state from version${isIrrecoverableError ? '' : ', removing and trying from scratch'}`);
459
579
  await authState.keys.set({ 'app-state-sync-version': { [name]: null } });
580
+ if (((_a = error.output) === null || _a === void 0 ? void 0 : _a.statusCode) === 404) {
581
+ appStateResyncCooldown.set(name, Date.now() + APP_STATE_RESYNC_COOLDOWN_MS);
582
+ }
460
583
  // increment number of retries
461
584
  attemptsMap[name] = (attemptsMap[name] || 0) + 1;
462
585
  if (isIrrecoverableError) {
@@ -481,42 +604,86 @@ const makeChatsSocket = (config) => {
481
604
  jid = resolveJid(jid);
482
605
  var _a;
483
606
  jid = (0, WABinary_1.jidNormalizedUser)(jid);
484
- const result = await query({
485
- tag: 'iq',
486
- attrs: {
487
- target: jid,
488
- to: WABinary_1.S_WHATSAPP_NET,
489
- type: 'get',
490
- xmlns: 'w:profile:picture'
491
- },
492
- content: [
493
- { tag: 'picture', attrs: { type, query: 'url' } }
494
- ]
495
- }, timeoutMs);
496
- const child = (0, WABinary_1.getBinaryNodeChild)(result, 'picture');
497
- return (_a = child === null || child === void 0 ? void 0 : child.attrs) === null || _a === void 0 ? void 0 : _a.url;
607
+ const cacheKey = `${jid}:${type}`;
608
+ const cached = profilePictureUrlCache.get(cacheKey);
609
+ if (typeof cached !== 'undefined') {
610
+ return cached || undefined;
611
+ }
612
+ const inFlight = inFlightProfilePictureUrl.get(cacheKey);
613
+ if (inFlight) {
614
+ return inFlight;
615
+ }
616
+ const fetchPromise = (async () => {
617
+ const result = await query({
618
+ tag: 'iq',
619
+ attrs: {
620
+ target: jid,
621
+ to: WABinary_1.S_WHATSAPP_NET,
622
+ type: 'get',
623
+ xmlns: 'w:profile:picture'
624
+ },
625
+ content: [
626
+ { tag: 'picture', attrs: { type, query: 'url' } }
627
+ ]
628
+ }, timeoutMs);
629
+ const child = (0, WABinary_1.getBinaryNodeChild)(result, 'picture');
630
+ const url = (_a = child === null || child === void 0 ? void 0 : child.attrs) === null || _a === void 0 ? void 0 : _a.url;
631
+ profilePictureUrlCache.set(cacheKey, url || null);
632
+ return url;
633
+ })();
634
+ inFlightProfilePictureUrl.set(cacheKey, fetchPromise);
635
+ try {
636
+ return await fetchPromise;
637
+ }
638
+ finally {
639
+ inFlightProfilePictureUrl.delete(cacheKey);
640
+ }
498
641
  };
499
642
  const sendPresenceUpdate = async (type, toJid) => {
500
643
  toJid = resolveJid(toJid);
501
644
  const me = authState.creds.me;
645
+ const retryOptions = {
646
+ retries: 2,
647
+ baseMs: 1000,
648
+ maxMs: 5000,
649
+ jitter: true,
650
+ timeoutPerAttemptMs: 10000,
651
+ shouldRetry: (err) => {
652
+ if (err.message.includes('WebSocket is not open')) {
653
+ return false;
654
+ }
655
+ var _a;
656
+ const status = ((_a = err.output) === null || _a === void 0 ? void 0 : _a.statusCode) || (err === null || err === void 0 ? void 0 : err.statusCode);
657
+ return !status || (status >= 500 || status === 408 || status === 429);
658
+ },
659
+ onRetry: (err, n) => logger === null || logger === void 0 ? void 0 : logger.warn({ err, attempt: n }, 'retrying presence update')
660
+ };
502
661
  if (type === 'available' || type === 'unavailable') {
503
662
  if (!me.name) {
504
663
  logger.warn('no name present, ignoring presence update request...');
505
664
  return;
506
665
  }
666
+ if (!toJid) {
667
+ const now = Date.now();
668
+ if (lastSelfPresenceType === type && (now - lastSelfPresenceTs) < 3000) {
669
+ return;
670
+ }
671
+ lastSelfPresenceType = type;
672
+ lastSelfPresenceTs = now;
673
+ }
507
674
  ev.emit('connection.update', { isOnline: type === 'available' });
508
- await sendNode({
675
+ await (0, retry_1.retryWithBackoff)(() => sendNode({
509
676
  tag: 'presence',
510
677
  attrs: {
511
678
  name: me.name.replace(/@/g, ''),
512
679
  type
513
680
  }
514
- });
681
+ }), retryOptions);
515
682
  }
516
683
  else {
517
684
  const { server } = (0, WABinary_1.jidDecode)(toJid);
518
685
  const isLid = server === 'lid';
519
- await sendNode({
686
+ await (0, retry_1.retryWithBackoff)(() => sendNode({
520
687
  tag: 'chatstate',
521
688
  attrs: {
522
689
  from: isLid ? me.lid : me.id,
@@ -528,7 +695,7 @@ const makeChatsSocket = (config) => {
528
695
  attrs: type === 'recording' ? { media: 'audio' } : {}
529
696
  }
530
697
  ]
531
- });
698
+ }), retryOptions);
532
699
  }
533
700
  };
534
701
  /**
@@ -754,22 +921,29 @@ const makeChatsSocket = (config) => {
754
921
  const upsertMessage = ev.createBufferedFunction(async (msg, type) => {
755
922
  var _a, _b, _c;
756
923
  try {
757
- const r = msg?.key?.remoteJid;
758
- const p = msg?.key?.participant;
759
- if (r) {
760
- const rd = (0, WABinary_1.jidDecode)(r);
924
+ const r0 = msg?.key?.remoteJid;
925
+ const p0 = msg?.key?.participant;
926
+
927
+ if (r0) {
928
+ const rd = (0, WABinary_1.jidDecode)(r0);
761
929
  if (rd?.user) {
762
- const lid = (0, WABinary_1.jidEncode)(rd.user, 'lid');
763
- msg.key.remoteLid = lid;
764
- msg.key.remoteJidNormalized = (0, WABinary_1.jidNormalizedUser)(r);
930
+ // preserve LID form (original if provided, otherwise derived)
931
+ msg.key.remoteLid = (0, WABinary_1.isLid)(r0) ? r0 : (0, WABinary_1.jidEncode)(rd.user, 'lid');
765
932
  }
933
+ // always expose a standard jid to consumers
934
+ msg.key.remoteJid = (0, WABinary_1.jidNormalizedUser)((0, WABinary_1.lidToJid)(r0));
935
+ msg.key.remoteJidNormalized = msg.key.remoteJid;
766
936
  }
767
- if (p) {
768
- const pd = (0, WABinary_1.jidDecode)(p);
937
+
938
+ if (p0) {
939
+ const pd = (0, WABinary_1.jidDecode)(p0);
769
940
  if (pd?.user) {
770
- const lidP = (0, WABinary_1.jidEncode)(pd.user, 'lid');
771
- msg.key.participantLid = lidP;
941
+ msg.key.participantLid = (0, WABinary_1.isLid)(p0) ? p0 : (0, WABinary_1.jidEncode)(pd.user, 'lid');
772
942
  }
943
+ msg.key.participant = (0, WABinary_1.jidNormalizedUser)((0, WABinary_1.lidToJid)(p0));
944
+ }
945
+ if (msg === null || msg === void 0 ? void 0 : msg.participant) {
946
+ msg.participant = (0, WABinary_1.jidNormalizedUser)((0, WABinary_1.lidToJid)(msg.participant));
773
947
  }
774
948
  } catch {}
775
949
  ev.emit('messages.upsert', { messages: [msg], type });
@@ -894,6 +1068,9 @@ const makeChatsSocket = (config) => {
894
1068
  updateProfileStatus,
895
1069
  updateProfileName,
896
1070
  updateBlockStatus,
1071
+ reportUser,
1072
+ reportMessage,
1073
+ reportAndBlockUser,
897
1074
  updateCallPrivacy,
898
1075
  updateMessagesPrivacy,
899
1076
  updateLastSeenPrivacy,
@@ -6,15 +6,14 @@ const Types_1 = require("../Types");
6
6
  const Utils_1 = require("../Utils");
7
7
  const WABinary_1 = require("../WABinary");
8
8
  const chats_1 = require("./chats");
9
+ const { getPerformanceConfig } = require('../Utils/performance-config');
10
+ const { CacheManager } = require('../Utils/cache-manager');
9
11
  const makeGroupsSocket = (config) => {
10
12
  const sock = (0, chats_1.makeChatsSocket)(config);
11
13
  const { authState, ev, query, upsertMessage } = sock;
12
- const resolveJid = (jid) => {
13
- if (typeof jid === 'string' && jid.endsWith('@lid')) {
14
- return (0, WABinary_1.lidToJid)(jid);
15
- }
16
- return jid;
17
- };
14
+ const inFlightGroupMetadata = new Map();
15
+ const forbiddenGroupMetadataUntil = new Map();
16
+ const resolveJid = WABinary_1.resolveJid;
18
17
  const groupQuery = async (jid, type, content) => (query({
19
18
  tag: 'iq',
20
19
  attrs: {
@@ -25,16 +24,72 @@ const makeGroupsSocket = (config) => {
25
24
  content
26
25
  }));
27
26
  const groupMetadata = async (jid) => {
28
- const result = await groupQuery(jid, 'get', [{ tag: 'query', attrs: { request: 'interactive' } }]);
29
- const metadata = (0, exports.extractGroupMetadata)(result);
30
- if (config.lidCache) {
31
- for (const p of metadata.participants) {
32
- if ((0, WABinary_1.isLid)(p.id) && !(0, WABinary_1.isLid)(p.jid)) {
33
- config.lidCache.set(p.id, p.jid);
27
+ const config = getPerformanceConfig();
28
+
29
+ const forbiddenUntil = forbiddenGroupMetadataUntil.get(jid);
30
+ if (forbiddenUntil && forbiddenUntil > Date.now()) {
31
+ const err = new Error('forbidden');
32
+ err.data = 403;
33
+ throw err;
34
+ }
35
+ else if (forbiddenUntil) {
36
+ forbiddenGroupMetadataUntil.delete(jid);
37
+ }
38
+
39
+ // Check cache first
40
+ const cached = CacheManager.get('groupMetadataCache', jid);
41
+ if (cached) {
42
+ config.logger?.trace({ jid }, 'using cached group metadata');
43
+ return cached;
44
+ }
45
+
46
+ // If a fetch is already in progress for this group, reuse it
47
+ const inFlight = inFlightGroupMetadata.get(jid);
48
+ if (inFlight) {
49
+ return inFlight;
50
+ }
51
+
52
+ const fetchPromise = (async () => {
53
+ // Fetch from server with rate limiting and retry logic
54
+ let retryCount = 0;
55
+ const maxRetries = 3;
56
+ const baseDelay = 1000; // 1 second
57
+
58
+ while (retryCount <= maxRetries) {
59
+ try {
60
+ const result = await groupQuery(jid, 'get', [{ tag: 'query', attrs: { request: 'interactive' } }]);
61
+ const metadata = (0, exports.extractGroupMetadata)(result);
62
+
63
+ // Cache the result
64
+ CacheManager.set('groupMetadataCache', jid, metadata, config.cache.groupMetadataCache.ttl);
65
+
66
+ return metadata;
67
+ } catch (error) {
68
+ const statusCode = error?.output?.statusCode || error?.data;
69
+ if (statusCode === 403 || error?.message === 'forbidden') {
70
+ const cooldownMs = 5 * 60 * 1000;
71
+ forbiddenGroupMetadataUntil.set(jid, Date.now() + cooldownMs);
72
+ throw error;
73
+ }
74
+ if (error.message === 'rate-overlimit' && retryCount < maxRetries) {
75
+ const delay = baseDelay * Math.pow(2, retryCount); // Exponential backoff
76
+ config.logger?.warn({ jid, retryCount, delay }, 'Rate limit hit, retrying group metadata fetch');
77
+ await new Promise(resolve => setTimeout(resolve, delay));
78
+ retryCount++;
79
+ } else {
80
+ // Re-throw non-rate-limit errors or if max retries exceeded
81
+ throw error;
82
+ }
34
83
  }
35
84
  }
85
+ })();
86
+
87
+ inFlightGroupMetadata.set(jid, fetchPromise);
88
+ try {
89
+ return await fetchPromise;
90
+ } finally {
91
+ inFlightGroupMetadata.delete(jid);
36
92
  }
37
- return metadata;
38
93
  };
39
94
  const groupFetchAllParticipating = async () => {
40
95
  const result = await query({
@@ -79,6 +134,23 @@ const makeGroupsSocket = (config) => {
79
134
  await groupFetchAllParticipating();
80
135
  await sock.cleanDirtyBits('groups');
81
136
  });
137
+
138
+ // Cache invalidation on group events
139
+ ev.on('groups.update', async (groups) => {
140
+ for (const group of groups) {
141
+ CacheManager.del('groupMetadataCache', group.id);
142
+ }
143
+ });
144
+
145
+ ev.on('group-participants.update', async (update) => {
146
+ CacheManager.del('groupMetadataCache', update.id);
147
+ });
148
+
149
+ ev.on('groups.upsert', async (groups) => {
150
+ for (const group of groups) {
151
+ CacheManager.del('groupMetadataCache', group.id);
152
+ }
153
+ });
82
154
  return {
83
155
  ...sock,
84
156
  groupQuery,
@@ -339,7 +411,7 @@ const extractGroupMetadata = (result) => {
339
411
  participants: (0, WABinary_1.getBinaryNodeChildren)(group, 'participant').map(({ attrs }) => {
340
412
  return {
341
413
  id: attrs.jid,
342
- jid: attrs.phone_number || attrs.jid,
414
+ jid: attrs.phone_number || (0, WABinary_1.lidToJid)(attrs.jid),
343
415
  lid: attrs.lid || ((0, WABinary_1.isLid)(attrs.jid) ? attrs.jid : undefined),
344
416
  admin: (attrs.type || null),
345
417
  };
@@ -348,4 +420,4 @@ const extractGroupMetadata = (result) => {
348
420
  };
349
421
  return metadata;
350
422
  };
351
- exports.extractGroupMetadata = extractGroupMetadata;
423
+ exports.extractGroupMetadata = extractGroupMetadata;
@@ -8,10 +8,19 @@ const Defaults_1 = require("../Defaults");
8
8
  const business_1 = require("./business");
9
9
  // export the last socket layer
10
10
  const makeWASocket = (config) => {
11
+ const inputConfig = config;
12
+ const hasBrowserOverride = Object.prototype.hasOwnProperty.call(inputConfig, 'browser');
11
13
  config = {
12
14
  ...Defaults_1.DEFAULT_CONNECTION_CONFIG,
13
15
  ...config,
14
16
  };
17
+ const creds = config.auth?.creds;
18
+ if (!hasBrowserOverride && creds?.browser) {
19
+ config.browser = creds.browser;
20
+ }
21
+ else if (creds && !creds.browser) {
22
+ creds.browser = config.browser;
23
+ }
15
24
  if (!config.lidCache) {
16
25
  config.lidCache = new node_cache_1.default({
17
26
  stdTTL: Defaults_1.DEFAULT_CACHE_TTLS.LID_JID,