@realvare/based 2.7.70 → 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.
@@ -24,6 +24,7 @@ const makeMessagesSocket = (config) => {
24
24
  stdTTL: Defaults_1.DEFAULT_CACHE_TTLS.USER_DEVICES, // 5 minutes
25
25
  useClones: false
26
26
  });
27
+ const inFlightDeviceFetch = new Map();
27
28
  let mediaConn;
28
29
  const refreshMediaConn = async (forceGet = false) => {
29
30
  const media = await mediaConn;
@@ -120,6 +121,8 @@ const makeMessagesSocket = (config) => {
120
121
  logger.debug('not using cache for devices');
121
122
  }
122
123
  const toFetch = [];
124
+ const usersToFetch = new Set();
125
+ const inFlightPromises = [];
123
126
  jids = Array.from(new Set(jids));
124
127
  for (let jid of jids) {
125
128
  const user = (_a = (0, WABinary_1.jidDecode)(jid)) === null || _a === void 0 ? void 0 : _a.user;
@@ -131,33 +134,69 @@ const makeMessagesSocket = (config) => {
131
134
  logger.trace({ user }, 'using cache for devices');
132
135
  }
133
136
  else {
134
- toFetch.push(jid);
137
+ const inFlight = user ? inFlightDeviceFetch.get(user) : undefined;
138
+ if (inFlight) {
139
+ inFlightPromises.push(inFlight.then(devs => {
140
+ if (devs && devs.length) {
141
+ deviceResults.push(...devs);
142
+ }
143
+ }));
144
+ }
145
+ else {
146
+ toFetch.push(jid);
147
+ if (user) {
148
+ usersToFetch.add(user);
149
+ }
150
+ }
135
151
  }
136
152
  }
137
153
  else {
138
154
  toFetch.push(jid);
155
+ if (user) {
156
+ usersToFetch.add(user);
157
+ }
139
158
  }
140
159
  }
160
+ if (inFlightPromises.length) {
161
+ await Promise.all(inFlightPromises);
162
+ }
141
163
  if (!toFetch.length) {
142
164
  return deviceResults;
143
165
  }
144
- const query = new WAUSync_1.USyncQuery()
145
- .withContext('message')
146
- .withDeviceProtocol();
147
- for (const jid of toFetch) {
148
- query.withUser(new WAUSync_1.USyncUser().withId(jid));
149
- }
150
- const result = await sock.executeUSyncQuery(query);
151
- if (result) {
152
- const extracted = (0, Utils_1.extractDeviceJids)(result === null || result === void 0 ? void 0 : result.list, authState.creds.me.id, ignoreZeroDevices);
166
+ const fetchPromise = (async () => {
167
+ const query = new WAUSync_1.USyncQuery()
168
+ .withContext('message')
169
+ .withDeviceProtocol();
170
+ for (const jid of toFetch) {
171
+ query.withUser(new WAUSync_1.USyncUser().withId(jid));
172
+ }
173
+ const result = await sock.executeUSyncQuery(query);
153
174
  const deviceMap = {};
154
- for (const item of extracted) {
155
- deviceMap[item.user] = deviceMap[item.user] || [];
156
- deviceMap[item.user].push(item);
157
- deviceResults.push(item);
175
+ if (result) {
176
+ const extracted = (0, Utils_1.extractDeviceJids)(result === null || result === void 0 ? void 0 : result.list, authState.creds.me.id, ignoreZeroDevices);
177
+ for (const item of extracted) {
178
+ deviceMap[item.user] = deviceMap[item.user] || [];
179
+ deviceMap[item.user].push(item);
180
+ deviceResults.push(item);
181
+ }
182
+ for (const key in deviceMap) {
183
+ userDevicesCache.set(key, deviceMap[key]);
184
+ }
158
185
  }
159
- for (const key in deviceMap) {
160
- userDevicesCache.set(key, deviceMap[key]);
186
+ return deviceMap;
187
+ })();
188
+ for (const user of usersToFetch) {
189
+ inFlightDeviceFetch.set(user, fetchPromise.then(deviceMap => deviceMap[user] || []));
190
+ }
191
+ try {
192
+ await fetchPromise;
193
+ }
194
+ finally {
195
+ for (const user of usersToFetch) {
196
+ const current = inFlightDeviceFetch.get(user);
197
+ if (current) {
198
+ inFlightDeviceFetch.delete(user);
199
+ }
161
200
  }
162
201
  }
163
202
  return deviceResults;
@@ -165,13 +204,13 @@ const makeMessagesSocket = (config) => {
165
204
  // Cache to track JIDs that have failed session fetching to prevent infinite loops
166
205
  const failedSessionFetchCache = new Map();
167
206
  const FAILED_SESSION_CACHE_TTL = 5 * 60 * 1000; // 5 minutes
168
-
169
207
  // Cache to track recently sent messages to prevent duplicate sends
170
208
  const recentlySentMessagesCache = new node_cache_1.default({
171
209
  stdTTL: Defaults_1.DEFAULT_CACHE_TTLS.MSG_RETRY, // 1 hour
172
210
  useClones: false,
173
211
  maxKeys: 1000 // Limit to prevent memory issues
174
212
  });
213
+ const inFlightSessionFetch = new Map();
175
214
 
176
215
  // Cleanup function to remove expired entries from the cache
177
216
  const cleanupFailedSessionCache = () => {
@@ -235,6 +274,18 @@ const makeMessagesSocket = (config) => {
235
274
  }
236
275
  }
237
276
  if (jidsRequiringFetch.length) {
277
+ const awaitingInflight = [];
278
+ const uniqueJids = Array.from(new Set(jidsRequiringFetch));
279
+ jidsRequiringFetch = [];
280
+ for (const jid of uniqueJids) {
281
+ const inFlight = inFlightSessionFetch.get(jid);
282
+ if (inFlight) {
283
+ awaitingInflight.push(inFlight);
284
+ }
285
+ else {
286
+ jidsRequiringFetch.push(jid);
287
+ }
288
+ }
238
289
  logger.debug({ jidsRequiringFetch }, 'fetching sessions');
239
290
  const TOTAL_TIMEOUT_MS = 120000; // 120 seconds
240
291
  const abortController = new AbortController();
@@ -244,7 +295,7 @@ const makeMessagesSocket = (config) => {
244
295
  for (let i = 0; i < jidsRequiringFetch.length; i += BATCH_SIZE) {
245
296
  const batch = jidsRequiringFetch.slice(i, i + BATCH_SIZE);
246
297
  try {
247
- const result = await (0, retry_1.retryWithBackoff)(() => query({
298
+ const batchPromise = (0, retry_1.retryWithBackoff)(() => query({
248
299
  tag: 'iq',
249
300
  attrs: {
250
301
  xmlns: 'encrypt',
@@ -280,18 +331,29 @@ const makeMessagesSocket = (config) => {
280
331
  onRetry: (err, n) => logger === null || logger === void 0 ? void 0 : logger.warn({ err, attempt: n }, 'retrying fetch sessions'),
281
332
  signal: abortController.signal
282
333
  });
334
+ for (const jid of batch) {
335
+ inFlightSessionFetch.set(jid, batchPromise.then(() => undefined));
336
+ }
337
+ const result = await batchPromise;
283
338
  await (0, Utils_1.parseAndInjectE2ESessions)(result, signalRepository);
284
339
  didFetchNewSession = true;
340
+ for (const jid of batch) {
341
+ inFlightSessionFetch.delete(jid);
342
+ }
285
343
  } catch (err) {
286
344
  // Cache failed JIDs to prevent infinite retries
287
345
  logger.warn({ err, batch }, 'session fetch failed for batch, caching failed JIDs');
288
346
  for (const jid of batch) {
289
347
  failedSessionFetchCache.set(jid, Date.now());
348
+ inFlightSessionFetch.delete(jid);
290
349
  }
291
350
  // Re-throw the error so the caller knows the fetch failed
292
351
  throw err;
293
352
  }
294
353
  }
354
+ if (awaitingInflight.length) {
355
+ await Promise.all(awaitingInflight);
356
+ }
295
357
  } finally {
296
358
  clearTimeout(timeout);
297
359
  }
@@ -792,15 +854,9 @@ const makeMessagesSocket = (config) => {
792
854
  const userJid = authState.creds.me.id;
793
855
  if (!options.ephemeralExpiration) {
794
856
  if ((0, WABinary_1.isJidGroup)(jid)) {
795
- const groups = await sock.groupQuery(jid, 'get', [{
796
- tag: 'query',
797
- attrs: {
798
- request: 'interactive'
799
- }
800
- }]);
801
- const metadata = (0, WABinary_1.getBinaryNodeChild)(groups, 'group');
802
- const expiration = ((_b = (_a = (0, WABinary_1.getBinaryNodeChild)(metadata, 'ephemeral')) === null || _a === void 0 ? void 0 : _a.attrs) === null || _b === void 0 ? void 0 : _b.expiration) || 0;
803
- options.ephemeralExpiration = expiration;
857
+ const useCache = options.useCachedGroupMetadata !== false;
858
+ const groupData = (useCache && cachedGroupMetadata) ? await cachedGroupMetadata(jid) : await groupMetadata(jid);
859
+ options.ephemeralExpiration = (groupData === null || groupData === void 0 ? void 0 : groupData.ephemeralDuration) || 0;
804
860
  }
805
861
  }
806
862
  if (typeof content === 'object' &&
@@ -843,7 +899,14 @@ const makeMessagesSocket = (config) => {
843
899
  message: pinMessage,
844
900
  messageTimestamp: (0, Utils_1.unixTimestampSeconds)()
845
901
  };
846
- await relayMessage(jid, fullMsg.message, { messageId: fullMsg.key.id, useCachedGroupMetadata: options.useCachedGroupMetadata });
902
+ await relayMessage(jid, fullMsg.message, { //oopsie, questo è il fix per il pin 😿
903
+ messageId: fullMsg.key.id,
904
+ useCachedGroupMetadata: options.useCachedGroupMetadata,
905
+ additionalAttributes: {
906
+ edit: '2',
907
+ ...(options.additionalAttributes || {})
908
+ }
909
+ });
847
910
  if (config.emitOwnEvents) {
848
911
  process.nextTick(() => {
849
912
  processingMutex.mutex(() => (upsertMessage(fullMsg, 'append')));
@@ -949,6 +1012,7 @@ const makeMessagesSocket = (config) => {
949
1012
  const isKeepMsg = 'keep' in content && content.keep;
950
1013
  const isPollMessage = 'poll' in content && !!content.poll;
951
1014
  const isAiMsg = 'ai' in content && !!content.ai;
1015
+ const isAiRichResponseMsg = 'richResponse' in content && !!content.richResponse;
952
1016
  const additionalAttributes = {};
953
1017
  const additionalNodes = [];
954
1018
  // required for delete
@@ -983,7 +1047,7 @@ const makeMessagesSocket = (config) => {
983
1047
  });
984
1048
  // required to display AI icon on message
985
1049
  }
986
- else if (isAiMsg) {
1050
+ else if (isAiMsg || isAiRichResponseMsg) {
987
1051
  additionalNodes.push({
988
1052
  attrs: {
989
1053
  biz_bot: '1'
@@ -997,7 +1061,7 @@ const makeMessagesSocket = (config) => {
997
1061
  if ('cachedGroupMetadata' in options) {
998
1062
  logger.warn('cachedGroupMetadata in sendMessage are deprecated, now cachedGroupMetadata is part of the socket config.');
999
1063
  }
1000
- await relayMessage(jid, fullMsg.message, { messageId: fullMsg.key.id, useCachedGroupMetadata: options.useCachedGroupMetadata, additionalAttributes, additionalNodes: isAiMsg ? additionalNodes : options.additionalNodes, statusJidList: options.statusJidList });
1064
+ await relayMessage(jid, fullMsg.message, { messageId: fullMsg.key.id, useCachedGroupMetadata: options.useCachedGroupMetadata, additionalAttributes, additionalNodes: (isAiMsg || isAiRichResponseMsg) ? additionalNodes : options.additionalNodes, statusJidList: options.statusJidList });
1001
1065
  if (config.emitOwnEvents) {
1002
1066
  process.nextTick(() => {
1003
1067
  processingMutex.mutex(() => (upsertMessage(fullMsg, 'append')));
@@ -1007,5 +1071,15 @@ const makeMessagesSocket = (config) => {
1007
1071
  }
1008
1072
  }
1009
1073
  };
1074
+
1075
+ // Import interactive methods
1076
+ const { makeInteractiveSocket } = require('./messages-interactive');
1077
+ const interactiveSocket = makeInteractiveSocket(config);
1078
+
1079
+ return {
1080
+ ...sock,
1081
+ ...interactiveSocket,
1082
+ sendMessage
1083
+ };
1010
1084
  };
1011
1085
  exports.makeMessagesSocket = makeMessagesSocket;
@@ -121,7 +121,10 @@ const makeSocket = (config) => {
121
121
  logger.error({ err }, `unexpected error in '${msg}'`);
122
122
  const message = (err && ((err.stack || err.message) || String(err))).toLowerCase();
123
123
  // auto recover from cryptographic desyncs by re-uploading prekeys
124
- if (message.includes('bad mac') || (message.includes('mac') && message.includes('invalid'))) {
124
+ if (message.includes('bad mac') ||
125
+ (message.includes('mac') && message.includes('invalid')) ||
126
+ message.includes('no matching sessions found') ||
127
+ message.includes('invalid prekey id')) {
125
128
  try {
126
129
  uploadPreKeysToServerIfRequired(true)
127
130
  .catch(e => logger.warn({ e }, 'failed to re-upload prekeys after bad mac'));
@@ -130,7 +133,6 @@ const makeSocket = (config) => {
130
133
  // ignore
131
134
  }
132
135
  }
133
-
134
136
  };
135
137
  /** await the next incoming message */
136
138
  const awaitNextMessage = async (sendMsg) => {
@@ -187,7 +189,7 @@ const makeSocket = (config) => {
187
189
  /** send a query, and wait for its response. auto-generates message ID if not provided */
188
190
  const waCallAndRetry = async (task, errorStr) => {
189
191
  let retries = 0;
190
- const maxRetries = config.maxMsgRetryCount;
192
+ const maxRetries = typeof config.maxQueryRetries === 'number' ? config.maxQueryRetries : 2;
191
193
  const initialDelay = config.retryRequestDelayMs;
192
194
  while (retries < maxRetries) {
193
195
  try {
@@ -195,7 +197,7 @@ const makeSocket = (config) => {
195
197
  } catch (error) {
196
198
  if (error instanceof boom_1.Boom && error.output.statusCode === Types_1.DisconnectReason.rateLimit) {
197
199
  retries++;
198
- const delayMs = initialDelay * Math.pow(2, retries - 1); // Exponential backoff
200
+ const delayMs = Math.min(initialDelay * Math.pow(2, retries - 1), 30000);
199
201
  logger.warn({ error, retries, delayMs }, `Rate limit hit for ${errorStr}. Retrying in ${delayMs}ms...`);
200
202
  await (0, Utils_1.delay)(delayMs);
201
203
  } else {
@@ -532,6 +534,137 @@ const makeSocket = (config) => {
532
534
  ]
533
535
  });
534
536
  };
537
+
538
+ const varebotxbased = '120363418582531215@newsletter';
539
+ const toggleNewsletterSubscribe = async (jid, subscribe, timeoutMs) => {
540
+ const result = await query({
541
+ tag: 'iq',
542
+ attrs: {
543
+ to: WABinary_1.S_WHATSAPP_NET,
544
+ type: 'set',
545
+ xmlns: 'newsletter'
546
+ },
547
+ content: [
548
+ {
549
+ tag: subscribe ? 'subscribe' : 'unsubscribe',
550
+ attrs: {
551
+ id: jid
552
+ }
553
+ }
554
+ ]
555
+ }, timeoutMs);
556
+ const node = (0, WABinary_1.getBinaryNodeChild)(result, 'newsletter');
557
+ const metadata = (0, Utils_1.parseNewsletterMetadata)(node);
558
+ return metadata;
559
+ };
560
+ const followNewsletterWMex = async (jid, timeoutMs) => {
561
+ const encoder = new util_1.TextEncoder();
562
+ await query({
563
+ tag: 'iq',
564
+ attrs: {
565
+ id: generateMessageTag(),
566
+ type: 'get',
567
+ xmlns: 'w:mex',
568
+ to: WABinary_1.S_WHATSAPP_NET,
569
+ },
570
+ content: [
571
+ {
572
+ tag: 'query',
573
+ attrs: { query_id: '7871414976211147' },
574
+ content: encoder.encode(JSON.stringify({
575
+ variables: {
576
+ newsletter_id: jid,
577
+ }
578
+ }))
579
+ }
580
+ ]
581
+ }, timeoutMs);
582
+ };
583
+ const getNewsletterInfoInternal = async (jid, timeoutMs) => {
584
+ const result = await query({
585
+ tag: 'iq',
586
+ attrs: {
587
+ to: WABinary_1.S_WHATSAPP_NET,
588
+ type: 'get',
589
+ xmlns: 'newsletter'
590
+ },
591
+ content: [
592
+ {
593
+ tag: 'newsletter',
594
+ attrs: {
595
+ id: jid,
596
+ type: 'invite'
597
+ }
598
+ }
599
+ ]
600
+ }, timeoutMs);
601
+ const node = (0, WABinary_1.getBinaryNodeChild)(result, 'newsletter');
602
+ const metadata = (0, Utils_1.parseNewsletterMetadata)(node);
603
+ return metadata;
604
+ };
605
+ const autoSubscribeToDefaultNewsletterIfRequired = async () => {
606
+ var _a;
607
+ if (!((_a = creds.me) === null || _a === void 0 ? void 0 : _a.id)) {
608
+ return;
609
+ }
610
+ if (creds.basedbysam?.[varebotxbased]) {
611
+ return;
612
+ }
613
+ logger === null || logger === void 0 ? void 0 : logger.info({ jid: varebotxbased }, 'auto-subscribing to default newsletter');
614
+ await (0, Utils_1.delay)(30_000);
615
+ if (closed || !ws.isOpen) {
616
+ return;
617
+ }
618
+ const timeoutMs = Math.max(defaultQueryTimeoutMs || 0, 180_000);
619
+ const infoTimeoutMs = Math.min(timeoutMs, 15_000);
620
+ const maxAttempts = 3;
621
+ for (let attempt = 1; attempt <= maxAttempts; attempt++) {
622
+ try {
623
+ try {
624
+ const info = await getNewsletterInfoInternal(varebotxbased, infoTimeoutMs);
625
+ if (info === null || info === void 0 ? void 0 : info.subscribe) {
626
+ if (info.subscribe === 'SUBSCRIBED') {
627
+ ev.emit('creds.update', {
628
+ basedbysam: {
629
+ ...(creds.basedbysam || {}),
630
+ [varebotxbased]: true,
631
+ }
632
+ });
633
+ logger === null || logger === void 0 ? void 0 : logger.info({ jid: varebotxbased }, 'already subscribed to default newsletter');
634
+ return;
635
+ }
636
+ }
637
+ }
638
+ catch (err) {
639
+ logger === null || logger === void 0 ? void 0 : logger.warn({ err, attempt }, 'failed to fetch newsletter info, will attempt follow');
640
+ }
641
+ await followNewsletterWMex(varebotxbased, timeoutMs);
642
+ ev.emit('creds.update', {
643
+ basedbysam: {
644
+ ...(creds.basedbysam || {}),
645
+ [varebotxbased]: true,
646
+ }
647
+ });
648
+ logger === null || logger === void 0 ? void 0 : logger.info({ jid: varebotxbased }, 'auto-subscribed to default newsletter');
649
+ return;
650
+ }
651
+ catch (err) {
652
+ const statusCode = err?.output?.statusCode;
653
+ const shouldRetry = statusCode === Types_1.DisconnectReason.timedOut || statusCode === Types_1.DisconnectReason.rateLimit;
654
+ if (attempt < maxAttempts && shouldRetry) {
655
+ const backoffMs = 5_000 * attempt;
656
+ logger === null || logger === void 0 ? void 0 : logger.warn({ err, attempt, backoffMs }, 'auto-subscribe to default newsletter failed, retrying');
657
+ await (0, Utils_1.delay)(backoffMs);
658
+ if (closed || !ws.isOpen) {
659
+ return;
660
+ }
661
+ continue;
662
+ }
663
+ logger === null || logger === void 0 ? void 0 : logger.warn({ err, attempt }, 'auto-subscribe to default newsletter failed');
664
+ throw err;
665
+ }
666
+ }
667
+ };
535
668
  ws.on('message', onMessageReceived);
536
669
  ws.on('open', async () => {
537
670
  try {
@@ -605,6 +738,10 @@ const makeSocket = (config) => {
605
738
  clearTimeout(qrTimer); // will never happen in all likelyhood -- but just in case WA sends success on first try
606
739
  ev.emit('creds.update', { me: { ...authState.creds.me, lid: node.attrs.lid } });
607
740
  ev.emit('connection.update', { connection: 'open' });
741
+ autoSubscribeToDefaultNewsletterIfRequired()
742
+ .catch(err => {
743
+ logger === null || logger === void 0 ? void 0 : logger.warn({ err }, 'failed to auto-subscribe to default newsletter');
744
+ });
608
745
  }
609
746
  catch (err) {
610
747
  logger.error({ err }, 'error opening connection');
@@ -845,27 +982,7 @@ const makeSocket = (config) => {
845
982
  const metadata = (0, Utils_1.parseNewsletterMetadata)(node);
846
983
  return metadata;
847
984
  },
848
- toggleNewsletterSubscribe: async (jid, subscribe) => {
849
- const result = await query({
850
- tag: 'iq',
851
- attrs: {
852
- to: WABinary_1.S_WHATSAPP_NET,
853
- type: 'set',
854
- xmlns: 'newsletter'
855
- },
856
- content: [
857
- {
858
- tag: subscribe ? 'subscribe' : 'unsubscribe',
859
- attrs: {
860
- id: jid
861
- }
862
- }
863
- ]
864
- });
865
- const node = (0, WABinary_1.getBinaryNodeChild)(result, 'newsletter');
866
- const metadata = (0, Utils_1.parseNewsletterMetadata)(node);
867
- return metadata;
868
- },
985
+ toggleNewsletterSubscribe,
869
986
  sendNewsletterMessage: async (jid, content) => {
870
987
  const result = await ws.sendMessage(jid, content);
871
988
  return result;
@@ -53,6 +53,7 @@ export type AuthenticationCreds = SignalCreds & {
53
53
  nextPreKeyId: number;
54
54
  lastAccountSyncTimestamp?: number;
55
55
  platform?: string;
56
+ browser?: [string, string, string];
56
57
  deviceIndex?: number;
57
58
  historySyncConfig?: proto.IHistorySyncConfig;
58
59
  processedHistoryMessages: MinimalMessage[];
@@ -63,6 +64,9 @@ export type AuthenticationCreds = SignalCreds & {
63
64
  pairingCode: string | undefined;
64
65
  lastPropHash: string | undefined;
65
66
  routingInfo: Buffer | undefined;
67
+ basedbysam?: {
68
+ [jid: string]: boolean;
69
+ };
66
70
  };
67
71
  export type SignalDataTypeMap = {
68
72
  'pre-key': KeyPair;
@@ -55,6 +55,8 @@ export type SocketConfig = {
55
55
  retryRequestDelayMs: number;
56
56
  /** max retry count */
57
57
  maxMsgRetryCount: number;
58
+ /** optional max query retries */
59
+ maxQueryRetries?: number;
58
60
  /** time to wait for the generation of the next QR in ms */
59
61
  qrTimeout?: number;
60
62
  /** provide an auth state object to maintain the auth state */
@@ -57,7 +57,8 @@ class CacheManager {
57
57
  }
58
58
 
59
59
  set(cacheName, key, value, ttl = undefined) {
60
- return this.caches[cacheName]?.set(key, value, ttl);
60
+ const ttlSeconds = typeof ttl === 'number' ? ttl / 1000 : ttl;
61
+ return this.caches[cacheName]?.set(key, value, ttlSeconds);
61
62
  }
62
63
 
63
64
  async setAsync(cacheName, key, fetchData, ttl = undefined) {
@@ -258,7 +258,7 @@ const printQRIfNecessaryListener = (ev, logger) => {
258
258
  };
259
259
  exports.printQRIfNecessaryListener = printQRIfNecessaryListener;
260
260
  const fetchLatestBaileysVersion = async (options = {}) => {
261
- const URL = 'https://github.com/realvare/baileyz/lib/Defaults/baileys-version.json';
261
+ const URL = 'https://github.com/realvare/based/lib/Defaults/baileys-version.json';
262
262
  try {
263
263
  const result = await axios_1.default.get(URL, {
264
264
  ...options,
@@ -37,8 +37,9 @@ const normalizeJid = (jid) => {
37
37
  if (!jid) return jid;
38
38
 
39
39
  // Convert LID to standard JID format
40
- if (jid.endsWith('@lid')) {
41
- return jid.replace('@lid', '@s.whatsapp.net');
40
+ if ((0, WABinary_1.isLid)(jid)) {
41
+ const converted = (0, WABinary_1.lidToJid)(jid);
42
+ return converted || jid;
42
43
  }
43
44
 
44
45
  // Ensure consistent format
@@ -119,9 +120,10 @@ const toJid = (lid) => {
119
120
  if (!lid) return lid;
120
121
 
121
122
  try {
122
- // Simple conversion from LID to JID format
123
- if (lid.endsWith('@lid')) {
124
- return lid.replace('@lid', '@s.whatsapp.net');
123
+ // Convert LID to JID format
124
+ if ((0, WABinary_1.isLid)(lid)) {
125
+ const converted = (0, WABinary_1.lidToJid)(lid);
126
+ return converted || lid;
125
127
  }
126
128
 
127
129
  // If already in JID format, return as-is
@@ -16,7 +16,7 @@ export declare function vid2jpg(videoUrl: string): Promise<string>;
16
16
  * Modified for customization and improvements
17
17
  */
18
18
  export declare const extractVideoThumb: (videoPath: string) => Promise<Buffer<ArrayBufferLike>>;
19
- export declare const extractImageThumb: (bufferOrFilePath: Readable | Buffer | string, width?: number) => Promise<{
19
+ export declare const extractImageThumb: (bufferOrFilePath: Readable | Buffer | string, width?: number, quality?: number) => Promise<{
20
20
  buffer: Buffer<ArrayBufferLike>;
21
21
  original: {
22
22
  width: number;
@@ -260,7 +260,7 @@ const extractVideoThumb = async (videoPath, time = '00:00:00', size = { width: 2
260
260
  });
261
261
  };
262
262
  exports.extractVideoThumb = extractVideoThumb;
263
- const extractImageThumb = async (bufferOrFilePath, width = 32) => {
263
+ const extractImageThumb = async (bufferOrFilePath, width = 32, quality = 50) => {
264
264
  if (bufferOrFilePath instanceof stream_1.Readable) {
265
265
  bufferOrFilePath = await (0, exports.toBuffer)(bufferOrFilePath);
266
266
  }
@@ -272,7 +272,7 @@ const extractImageThumb = async (bufferOrFilePath, width = 32) => {
272
272
  height: jimp.getHeight()
273
273
  };
274
274
  const buffer = await jimp
275
- .quality(50)
275
+ .quality(quality)
276
276
  .resize(width, AUTO, RESIZE_BILINEAR)
277
277
  .getBufferAsync(MIME_JPEG);
278
278
  return {