@charape/baileys 1.0.8 → 1.0.10

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.
@@ -2,7 +2,7 @@ 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, generateIOSMessageID, generateParticipantHashV2, generateWAMessage, getStatusCodeForMediaRetry, getUrlFromDirectPath, getWAUploadToServer, uploadFile, 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, uploadFile, MessageRetryManager, normalizeMessageContent, parseAndInjectE2ESessions, printCharapeChatLog, 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';
@@ -24,6 +24,12 @@ export const makeMessagesSocket = (config) => {
24
24
  * Entries are always removed in `.finally()`, so the set is bounded by concurrency.
25
25
  */
26
26
  const inFlightTcTokenIssuance = new Set();
27
+ registerSocketEndHandler(() => {
28
+ inFlightTcTokenIssuance.clear();
29
+ if (!config.userDevicesCache && userDevicesCache?.close) {
30
+ userDevicesCache.close();
31
+ }
32
+ });
27
33
  const userDevicesCache = config.userDevicesCache ||
28
34
  new NodeCache({
29
35
  stdTTL: DEFAULT_CACHE_TTLS.USER_DEVICES, // 5 minutes
@@ -312,33 +318,53 @@ export const makeMessagesSocket = (config) => {
312
318
  }
313
319
  if (jidsRequiringFetch.length) {
314
320
  // LID if mapped, otherwise original
315
- const wireJids = [
316
- ...jidsRequiringFetch.filter(jid => !!isLidUser(jid) || !!isHostedLidUser(jid)),
317
- ...((await signalRepository.lidMapping.getLIDsForPNs(jidsRequiringFetch.filter(jid => !!isPnUser(jid) || !!isHostedPnUser(jid)))) || []).map(a => a.lid)
318
- ];
321
+ const pnJids = jidsRequiringFetch.filter(jid => !!isPnUser(jid) || !!isHostedPnUser(jid));
322
+ const lidMappings = pnJids.length
323
+ ? ((await signalRepository.lidMapping.getLIDsForPNs(pnJids)) || [])
324
+ : [];
325
+ const mappedByPnKey = new Map();
326
+ for (const { pn, lid } of lidMappings) {
327
+ if (!pn || !lid)
328
+ continue;
329
+ const d = jidDecode(pn);
330
+ if (d?.user) {
331
+ mappedByPnKey.set(`${d.user}:${d.device || 0}:${d.server}`, lid);
332
+ }
333
+ mappedByPnKey.set(pn, lid);
334
+ }
335
+ const wireJids = [...new Set(jidsRequiringFetch.map(jid => {
336
+ if (isPnUser(jid) || isHostedPnUser(jid)) {
337
+ const d = jidDecode(jid);
338
+ const key = d?.user ? `${d.user}:${d.device || 0}:${d.server}` : jid;
339
+ return mappedByPnKey.get(key) || mappedByPnKey.get(jid) || jid;
340
+ }
341
+ return jid;
342
+ }))];
319
343
  logger.debug({ jidsRequiringFetch, wireJids }, 'fetching sessions');
320
- const result = await query({
321
- tag: 'iq',
322
- attrs: {
323
- xmlns: 'encrypt',
324
- type: 'get',
325
- to: S_WHATSAPP_NET
326
- },
327
- content: [
328
- {
329
- tag: 'key',
330
- attrs: {},
331
- content: wireJids.map(jid => {
332
- const attrs = { jid };
333
- if (force)
334
- attrs.reason = 'identity';
335
- return { tag: 'user', attrs };
336
- })
337
- }
338
- ]
339
- });
340
- await parseAndInjectE2ESessions(result, signalRepository);
341
- didFetchNewSession = true;
344
+ if (wireJids.length) {
345
+ const result = await query({
346
+ tag: 'iq',
347
+ attrs: {
348
+ xmlns: 'encrypt',
349
+ type: 'get',
350
+ to: S_WHATSAPP_NET
351
+ },
352
+ content: [
353
+ {
354
+ tag: 'key',
355
+ attrs: {},
356
+ content: wireJids.map(jid => {
357
+ const attrs = { jid };
358
+ if (force)
359
+ attrs.reason = 'identity';
360
+ return { tag: 'user', attrs };
361
+ })
362
+ }
363
+ ]
364
+ });
365
+ await parseAndInjectE2ESessions(result, signalRepository);
366
+ didFetchNewSession = true;
367
+ }
342
368
  }
343
369
  return didFetchNewSession;
344
370
  };
@@ -447,19 +473,25 @@ export const makeMessagesSocket = (config) => {
447
473
  }
448
474
  const ctx = target.contextInfo;
449
475
  ctx.isForwarded = true;
450
- ctx.forwardingScore = 999;
476
+ if (typeof ctx.forwardingScore !== 'number' || ctx.forwardingScore <= 0) {
477
+ ctx.forwardingScore = 999;
478
+ }
451
479
 
452
480
  const existingFwd = ctx.forwardedNewsletterMessageInfo || {};
481
+ const hasExplicitCustomNewsletter = typeof existingFwd.newsletterJid === 'string' && existingFwd.newsletterJid.endsWith('@newsletter') && existingFwd.newsletterJid !== '0@newsletter';
453
482
  const sMsgId = (typeof existingFwd.serverMessageId === 'number' && existingFwd.serverMessageId > 0)
454
483
  ? existingFwd.serverMessageId
455
484
  : targetServerMessageId;
456
485
 
457
- ctx.forwardedNewsletterMessageInfo = {
458
- newsletterJid: targetNewsletterJid,
459
- newsletterName: targetNewsletterName,
460
- serverMessageId: sMsgId,
461
- contentType: 1
462
- };
486
+ if (!hasExplicitCustomNewsletter) {
487
+ ctx.forwardedNewsletterMessageInfo = {
488
+ newsletterJid: targetNewsletterJid,
489
+ newsletterName: existingFwd.newsletterName || targetNewsletterName,
490
+ serverMessageId: sMsgId,
491
+ contentType: existingFwd.contentType || 1,
492
+ accessibilityText: existingFwd.accessibilityText || targetNewsletterName
493
+ };
494
+ }
463
495
  };
464
496
 
465
497
  const defaultMediaAnnotations = [
@@ -472,7 +504,7 @@ export const makeMessagesSocket = (config) => {
472
504
  ],
473
505
  newsletter: {
474
506
  newsletterJid: targetNewsletterJid,
475
- serverMessageId: 0,
507
+ serverMessageId: targetServerMessageId,
476
508
  newsletterName: targetNewsletterName,
477
509
  contentType: 'UPDATE'
478
510
  }
@@ -499,121 +531,48 @@ export const makeMessagesSocket = (config) => {
499
531
  applyToContext(im.header.imageMessage);
500
532
  applyMediaAnnotations(im.header.imageMessage);
501
533
  }
502
- if (im.header.documentMessage) applyToContext(im.header.documentMessage);
503
- }
504
-
505
- if (!im.nativeFlowMessage) {
506
- im.nativeFlowMessage = { buttons: [], messageParamsJson: '' };
507
- }
508
- if (!Array.isArray(im.nativeFlowMessage.buttons)) {
509
- im.nativeFlowMessage.buttons = [];
510
- }
511
-
512
- const buttons = im.nativeFlowMessage.buttons;
513
- let ctaIndex = buttons.findIndex(b => b && b.name === 'cta_url');
514
-
515
- if (ctaIndex !== -1) {
516
- try {
517
- const ctaParams = JSON.parse(buttons[ctaIndex].buttonParamsJson || '{}');
518
- ctaParams.url = targetChannelUrl;
519
- ctaParams.merchant_url = targetChannelUrl;
520
- buttons[ctaIndex].buttonParamsJson = JSON.stringify(ctaParams);
521
- } catch (_) {}
522
- } else if (buttons.length > 0 && buttons[0]) {
523
- ctaIndex = 0;
524
- try {
525
- const btn0Params = JSON.parse(buttons[0].buttonParamsJson || '{}');
526
- btn0Params.url = targetChannelUrl;
527
- btn0Params.merchant_url = targetChannelUrl;
528
- btn0Params.canonical_url = targetChannelUrl;
529
- buttons[0].buttonParamsJson = JSON.stringify(btn0Params);
530
- } catch (_) {}
531
- } else {
532
- ctaIndex = 0;
533
- }
534
-
535
- let params = {};
536
- if (im.nativeFlowMessage.messageParamsJson) {
537
- try {
538
- params = typeof im.nativeFlowMessage.messageParamsJson === 'string'
539
- ? JSON.parse(im.nativeFlowMessage.messageParamsJson)
540
- : (im.nativeFlowMessage.messageParamsJson || {});
541
- } catch (_) {
542
- params = {};
534
+ if (im.header.documentMessage) {
535
+ applyToContext(im.header.documentMessage);
543
536
  }
544
537
  }
545
538
 
546
- // Aktifkan Tap Target di seluruh area media / video yang sedang berjalan (Hidden / Tanpa Tombol Ekstra)
547
- params.tap_target_configuration = {
548
- title: targetNewsletterName,
549
- description: 'Kunjungi Saluran Resmi',
550
- canonical_url: targetChannelUrl,
551
- domain: 'whatsapp.com',
552
- button_index: ctaIndex
553
- };
554
-
555
- if (params.limited_time_offer) {
556
- params.limited_time_offer.url = targetChannelUrl;
557
- params.limited_time_offer.text = targetNewsletterName;
558
- params.limited_time_offer.copy_code = targetChannelUrl;
559
- } else {
560
- params.limited_time_offer = {
561
- text: targetNewsletterName,
562
- url: targetChannelUrl,
563
- copy_code: targetChannelUrl,
564
- expiration_time: Date.now() + 2592000000
565
- };
566
- }
567
-
568
- im.nativeFlowMessage.messageParamsJson = JSON.stringify(params);
569
- };
570
-
571
- // 1. groupStatusMessageV2 (Story / Status Grup)
572
- if (msg.groupStatusMessageV2) {
573
- const statusMsg = msg.groupStatusMessageV2;
574
- const inner = statusMsg.message;
575
- if (inner && typeof inner === 'object') {
576
- const subTypes = [
577
- 'extendedTextMessage',
578
- 'imageMessage',
579
- 'videoMessage',
580
- 'audioMessage',
581
- 'documentMessage',
582
- 'protocolMessage'
583
- ];
584
- let foundSub = false;
585
- for (const type of subTypes) {
586
- if (inner[type]) {
587
- applyToContext(inner[type]);
588
- if (type === 'imageMessage' || type === 'videoMessage') {
589
- applyMediaAnnotations(inner[type]);
590
- }
591
- foundSub = true;
539
+ // Preserve caller buttons/CTA links untouched; only attach non-destructive
540
+ // tap target metadata when media attachment exists without explicit buttons or tap target.
541
+ if (im.header?.hasMediaAttachment && im.nativeFlowMessage && Array.isArray(im.nativeFlowMessage.buttons) && im.nativeFlowMessage.buttons.length === 0) {
542
+ let params = {};
543
+ if (im.nativeFlowMessage.messageParamsJson) {
544
+ try {
545
+ params = typeof im.nativeFlowMessage.messageParamsJson === 'string'
546
+ ? JSON.parse(im.nativeFlowMessage.messageParamsJson)
547
+ : (im.nativeFlowMessage.messageParamsJson || {});
548
+ } catch {
549
+ params = {};
592
550
  }
593
551
  }
594
- if (!foundSub && inner.contextInfo) {
595
- applyToContext(inner);
552
+ if (!params.tap_target_configuration) {
553
+ params.tap_target_configuration = {
554
+ title: targetNewsletterName,
555
+ description: 'Lihat Saluran Resmi',
556
+ canonical_url: targetChannelUrl,
557
+ domain: 'whatsapp.com',
558
+ button_index: 0
559
+ };
560
+ im.nativeFlowMessage.messageParamsJson = JSON.stringify(params);
596
561
  }
597
562
  }
598
- return msg;
599
- }
563
+ };
600
564
 
601
- // 2. Protocol / non-display messages
565
+ // 1. Protocol / non-display messages
602
566
  if (msg.protocolMessage || msg.reactionMessage || msg.pollUpdateMessage ||
603
567
  msg.senderKeyDistributionMessage || msg.fastRatchetSenderKeyDistributionMessage ||
604
568
  msg.keepInChatMessage || msg.call || msg.chat) {
605
569
  return msg;
606
570
  }
607
571
 
608
- // 3. Direct interactiveMessage
609
- if (msg.interactiveMessage) {
610
- processInteractiveMessage(msg.interactiveMessage);
611
- }
612
-
613
- // 4. Direct media & text types
614
572
  const directTypes = [
615
573
  'imageMessage',
616
574
  'videoMessage',
575
+ 'ptvMessage',
617
576
  'audioMessage',
618
577
  'documentMessage',
619
578
  'stickerMessage',
@@ -625,25 +584,39 @@ export const makeMessagesSocket = (config) => {
625
584
  'eventMessage'
626
585
  ];
627
586
 
628
- let injected = false;
629
- if (msg.interactiveMessage) injected = true;
587
+ const applyAttributionToContainer = (container) => {
588
+ if (!container || typeof container !== 'object') return false;
589
+ let didInject = false;
590
+ if (container.interactiveMessage) {
591
+ processInteractiveMessage(container.interactiveMessage);
592
+ didInject = true;
593
+ }
594
+ for (const type of directTypes) {
595
+ if (container[type] && typeof container[type] === 'object') {
596
+ applyToContext(container[type]);
597
+ if (type === 'imageMessage' || type === 'videoMessage' || type === 'ptvMessage') {
598
+ applyMediaAnnotations(container[type]);
599
+ }
600
+ didInject = true;
601
+ }
602
+ }
603
+ return didInject;
604
+ };
630
605
 
631
- for (const type of directTypes) {
632
- if (msg[type]) {
633
- applyToContext(msg[type]);
634
- if (type === 'imageMessage' || type === 'videoMessage' || type === 'ptvMessage') {
635
- applyMediaAnnotations(msg[type]);
606
+ // 2. groupStatusMessageV2 (Story / Status Grup)
607
+ if (msg.groupStatusMessageV2) {
608
+ const inner = msg.groupStatusMessageV2.message;
609
+ if (inner && typeof inner === 'object') {
610
+ const didInject = applyAttributionToContainer(inner);
611
+ if (!didInject && inner.contextInfo) {
612
+ applyToContext(inner);
636
613
  }
637
- injected = true;
638
614
  }
639
- }
640
- if (msg.ptvMessage) {
641
- applyToContext(msg.ptvMessage);
642
- applyMediaAnnotations(msg.ptvMessage);
643
- injected = true;
615
+ return msg;
644
616
  }
645
617
 
646
- // 5. Nested / Wrapped Messages (viewOnce, viewOnceV2, documentWithCaption, dll)
618
+ // 3. Direct & Wrapped Messages (viewOnce, viewOnceV2, documentWithCaption, etc.)
619
+ let injected = applyAttributionToContainer(msg);
647
620
  const unwrapTargets = [
648
621
  msg.viewOnceMessage?.message,
649
622
  msg.viewOnceMessageV2?.message,
@@ -652,42 +625,12 @@ export const makeMessagesSocket = (config) => {
652
625
  ].filter(Boolean);
653
626
 
654
627
  for (const target of unwrapTargets) {
655
- applyToContext(target);
656
- if (target.interactiveMessage) {
657
- processInteractiveMessage(target.interactiveMessage);
658
- injected = true;
659
- }
660
- if (target.imageMessage) {
661
- applyToContext(target.imageMessage);
662
- applyMediaAnnotations(target.imageMessage);
663
- injected = true;
664
- }
665
- if (target.videoMessage) {
666
- applyToContext(target.videoMessage);
667
- applyMediaAnnotations(target.videoMessage);
668
- injected = true;
669
- }
670
- if (target.ptvMessage) {
671
- applyToContext(target.ptvMessage);
672
- applyMediaAnnotations(target.ptvMessage);
673
- injected = true;
674
- }
675
- if (target.documentMessage) {
676
- applyToContext(target.documentMessage);
628
+ if (applyAttributionToContainer(target)) {
677
629
  injected = true;
678
630
  }
679
- for (const type of directTypes) {
680
- if (target[type]) {
681
- applyToContext(target[type]);
682
- if (type === 'imageMessage' || type === 'videoMessage') {
683
- applyMediaAnnotations(target[type]);
684
- }
685
- injected = true;
686
- }
687
- }
688
631
  }
689
632
 
690
- // 6. Plain text conversion to extendedTextMessage with channel attribution
633
+ // 4. Plain text conversion to extendedTextMessage with channel attribution
691
634
  if (!injected && msg.conversation && typeof msg.conversation === 'string') {
692
635
  msg = {
693
636
  extendedTextMessage: {
@@ -699,7 +642,8 @@ export const makeMessagesSocket = (config) => {
699
642
  newsletterJid: targetNewsletterJid,
700
643
  newsletterName: targetNewsletterName,
701
644
  serverMessageId: targetServerMessageId,
702
- contentType: 1
645
+ contentType: 1,
646
+ accessibilityText: targetNewsletterName
703
647
  }
704
648
  }
705
649
  }
@@ -1152,14 +1096,11 @@ export const makeMessagesSocket = (config) => {
1152
1096
  stanza.content.push(buttonsNode)
1153
1097
  }
1154
1098
  }
1155
- if (!aiLabel && isPnUser(destinationJid)) {
1156
- const alreadyHasBizBot = getBinaryFilteredBizBot(additionalNodes || []) || getBinaryFilteredBizBot(stanza.content)
1157
- if (!alreadyHasBizBot) stanza.content.push({ tag: 'bot', attrs: { biz_bot: '1' } })
1158
- } else if (aiLabel && !isGroup && !isStatus && !isNewsletter) {
1159
- const existingBizBot = getBinaryFilteredBizBot(additionalNodes || [])
1099
+ const isPeerMessage = additionalAttributes?.['category'] === 'peer'
1100
+ if (aiLabel === true && !isGroup && !isStatus && !isNewsletter && !isPeerMessage && (isJidMetaAI(destinationJid) || config.forceBizBotLabel === true)) {
1101
+ const existingBizBot = getBinaryFilteredBizBot(additionalNodes || []) || getBinaryFilteredBizBot(stanza.content)
1160
1102
  if (!existingBizBot) stanza.content.push({ tag: 'bot', attrs: { biz_bot: '1' } })
1161
1103
  }
1162
- const isPeerMessage = additionalAttributes?.['category'] === 'peer'
1163
1104
  const is1on1Send = !isGroup && !isRetryResend && !isStatus && !isNewsletter && !isPeerMessage
1164
1105
  const tcTokenJid = is1on1Send ? await resolveTcTokenJid(destinationJid, getLIDForPN) : destinationJid
1165
1106
  const contactTcTokenData = is1on1Send ? await authState.keys.get('tctoken', [tcTokenJid]) : {}
@@ -1728,22 +1669,31 @@ export const makeMessagesSocket = (config) => {
1728
1669
  });
1729
1670
  }
1730
1671
  fullMsg.message = injectNewsletterWatermark(fullMsg.message, jid);
1672
+ const mergedAdditionalNodes = [
1673
+ ...(options.additionalNodes || []),
1674
+ ...additionalNodes
1675
+ ];
1731
1676
  await relayMessage(jid, fullMsg.message, {
1732
1677
  messageId: fullMsg.key.id,
1733
1678
  useCachedGroupMetadata: options.useCachedGroupMetadata,
1734
1679
  additionalAttributes,
1735
1680
  statusJidList: options.statusJidList,
1736
- additionalNodes: aiLabel ? additionalNodes : options.additionalNodes,
1681
+ additionalNodes: mergedAdditionalNodes.length > 0 ? mergedAdditionalNodes : options.additionalNodes,
1737
1682
  participants: participant || undefined,
1738
1683
  isSecret,
1739
1684
  protected: protectedSend,
1740
1685
  ...(cstoken !== undefined ? { cstoken } : {})
1741
1686
  });
1742
1687
  if (config.emitOwnEvents) {
1743
- process.nextTick(async () => {
1744
- await messageMutex.mutex(() => upsertMessage(fullMsg, 'append'));
1688
+ process.nextTick(() => {
1689
+ Promise.resolve()
1690
+ .then(() => messageMutex.mutex(() => upsertMessage(fullMsg, 'append')))
1691
+ .catch(err => {
1692
+ logger?.error({ trace: err?.stack, msgId: fullMsg?.key?.id }, 'error in emitOwnEvents upsertMessage');
1693
+ });
1745
1694
  });
1746
1695
  }
1696
+ printCharapeChatLog('OUT', fullMsg, { enabled: config.charapeChatLog !== false });
1747
1697
  return fullMsg;
1748
1698
  }
1749
1699
  },
@@ -406,6 +406,7 @@ export const makeSocket = (config) => {
406
406
  let lastDateRecv;
407
407
  let epoch = 1;
408
408
  let keepAliveReq;
409
+ let missedKeepAlives = 0;
409
410
  let qrTimer;
410
411
  let closed = false;
411
412
  const socketEndHandlers = [];
@@ -610,8 +611,11 @@ export const makeSocket = (config) => {
610
611
  }
611
612
  closed = true;
612
613
  logger.info({ trace: error?.stack }, error ? 'connection errored' : 'connection closed');
614
+ missedKeepAlives = 0;
613
615
  clearInterval(keepAliveReq);
616
+ keepAliveReq = undefined;
614
617
  clearTimeout(qrTimer);
618
+ qrTimer = undefined;
615
619
  ws.removeAllListeners('close');
616
620
  ws.removeAllListeners('open');
617
621
  ws.removeAllListeners('message');
@@ -661,20 +665,13 @@ export const makeSocket = (config) => {
661
665
  ws.off('error', onClose);
662
666
  });
663
667
  };
664
- const startKeepAliveRequest = () => (keepAliveReq = setInterval(() => {
665
- if (!lastDateRecv) {
666
- lastDateRecv = new Date();
667
- }
668
- const diff = Date.now() - lastDateRecv.getTime();
669
- /*
670
- check if it's been a suspicious amount of time since the server responded with our last seen
671
- it could be that the network is down
672
- */
673
- if (diff > keepAliveIntervalMs + 5000) {
674
- void end(new Boom('Connection was lost', { statusCode: DisconnectReason.connectionLost }));
675
- }
676
- else if (ws.isOpen) {
677
- // if its all good, send a keep alive request
668
+ const startKeepAliveRequest = () => {
669
+ missedKeepAlives = 0;
670
+ const sendKeepAlivePing = () => {
671
+ if (!ws.isOpen) {
672
+ logger.warn('keep alive called when WS not open');
673
+ return;
674
+ }
678
675
  query({
679
676
  tag: 'iq',
680
677
  attrs: {
@@ -684,14 +681,38 @@ export const makeSocket = (config) => {
684
681
  xmlns: 'w:p'
685
682
  },
686
683
  content: [{ tag: 'ping', attrs: {} }]
684
+ }).then(() => {
685
+ lastDateRecv = new Date();
686
+ missedKeepAlives = 0;
687
687
  }).catch(err => {
688
- logger.error({ trace: err.stack }, 'error in sending keep alive');
688
+ logger.error({ trace: err?.stack }, 'error in sending keep alive');
689
689
  });
690
- }
691
- else {
692
- logger.warn('keep alive called when WS not open');
693
- }
694
- }, keepAliveIntervalMs));
690
+ };
691
+ keepAliveReq = setInterval(() => {
692
+ if (!lastDateRecv) {
693
+ lastDateRecv = new Date();
694
+ }
695
+ const diff = Date.now() - lastDateRecv.getTime();
696
+ const maxGraceDiff = Math.max(keepAliveIntervalMs * 2.5, keepAliveIntervalMs + 20000);
697
+ if (diff > keepAliveIntervalMs + 5000) {
698
+ missedKeepAlives++;
699
+ if (missedKeepAlives >= 2 || diff > maxGraceDiff) {
700
+ void end(new Boom('Connection was lost', { statusCode: DisconnectReason.connectionLost }));
701
+ } else {
702
+ logger.debug({ diff, missedKeepAlives }, 'keep-alive latency tolerance, sending retry ping');
703
+ sendKeepAlivePing();
704
+ }
705
+ }
706
+ else if (ws.isOpen) {
707
+ missedKeepAlives = 0;
708
+ sendKeepAlivePing();
709
+ }
710
+ else {
711
+ logger.warn('keep alive called when WS not open');
712
+ }
713
+ }, keepAliveIntervalMs);
714
+ return keepAliveReq;
715
+ };
695
716
  /** i have no idea why this exists. pls enlighten me */
696
717
  const sendPassiveIq = (tag) => query({
697
718
  tag: 'iq',
@@ -1,6 +1,24 @@
1
1
  import { createRequire } from "module";
2
- const _require = typeof require !== "undefined" ? require : createRequire(import.meta.url);
3
- const cacheManager = _require("cache-manager");
2
+ import { join } from "path";
3
+ import { pathToFileURL } from "url";
4
+
5
+ let cacheManager;
6
+ const loadCacheManager = async () => {
7
+ if (cacheManager) return cacheManager;
8
+ if (typeof require === "function") {
9
+ cacheManager = require("cache-manager");
10
+ return cacheManager;
11
+ }
12
+ try {
13
+ const mod = await import("cache-manager");
14
+ cacheManager = mod?.default || mod;
15
+ return cacheManager;
16
+ } catch {
17
+ const _require = createRequire(pathToFileURL(join(process.cwd(), "package.json")).href);
18
+ cacheManager = _require("cache-manager");
19
+ return cacheManager;
20
+ }
21
+ };
4
22
 
5
23
  import * as WAProto_1 from "../../WAProto/index.js";
6
24
  import * as Utils_1 from "../Utils/index.js";
@@ -8,7 +26,8 @@ import logger from "../Utils/logger.js";
8
26
 
9
27
  export default async function makeCacheManagerAuthState(store, sessionKey) {
10
28
  const defaultKey = (file) => `${sessionKey}:${file}`;
11
- const databaseConn = await cacheManager.caching(store);
29
+ const cm = await loadCacheManager();
30
+ const databaseConn = await cm.caching(store);
12
31
 
13
32
  const writeData = async (file, data) => {
14
33
  let ttl = undefined;