@lordmega/baileys 0.3.23 → 0.3.25

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 (59) hide show
  1. package/README.md +35 -1434
  2. package/WAProto/index.d.ts +26 -0
  3. package/WAProto/index.js +203 -0
  4. package/lib/Defaults/index.d.ts +1 -0
  5. package/lib/Defaults/index.d.ts.map +1 -1
  6. package/lib/Defaults/index.js +1 -0
  7. package/lib/Defaults/index.js.map +1 -1
  8. package/lib/Signal/libsignal.js +4 -4
  9. package/lib/Socket/Client/websocket.js +10 -5
  10. package/lib/Socket/business.d.ts +1 -1
  11. package/lib/Socket/chats.d.ts +1 -1
  12. package/lib/Socket/chats.js +8 -6
  13. package/lib/Socket/chats.js.map +1 -1
  14. package/lib/Socket/communities.d.ts +1 -1
  15. package/lib/Socket/communities.js +3 -2
  16. package/lib/Socket/groups.d.ts +1 -1
  17. package/lib/Socket/groups.js +24 -8
  18. package/lib/Socket/index.d.ts +1 -1
  19. package/lib/Socket/messages-recv.d.ts +1 -1
  20. package/lib/Socket/messages-recv.js +92 -24
  21. package/lib/Socket/messages-send.d.ts +2 -1
  22. package/lib/Socket/messages-send.js +68 -8
  23. package/lib/Socket/messages-send.js.map +1 -1
  24. package/lib/Socket/newsletter.d.ts +1 -1
  25. package/lib/Socket/newsletter.js +28 -1
  26. package/lib/Socket/socket.js +41 -13
  27. package/lib/Store/make-in-memory-store.js +7 -2
  28. package/lib/Utils/browser-utils.d.ts +1 -0
  29. package/lib/Utils/browser-utils.js +1 -0
  30. package/lib/Utils/crypto.js +34 -1
  31. package/lib/Utils/decode-wa-message.d.ts.map +1 -1
  32. package/lib/Utils/decode-wa-message.js.map +1 -1
  33. package/lib/Utils/event-buffer.js +19 -2
  34. package/lib/Utils/generics.js +3 -6
  35. package/lib/Utils/index.js.map +1 -1
  36. package/lib/Utils/message-retry-manager.d.ts +1 -0
  37. package/lib/Utils/message-retry-manager.js +7 -0
  38. package/lib/Utils/messages-media.js +108 -41
  39. package/lib/Utils/messages.d.ts.map +1 -1
  40. package/lib/Utils/messages.js +49 -140
  41. package/lib/Utils/messages.js.map +1 -1
  42. package/lib/Utils/process-message.d.ts +1 -0
  43. package/lib/Utils/process-message.d.ts.map +1 -1
  44. package/lib/Utils/process-message.js +154 -78
  45. package/lib/Utils/process-message.js.map +1 -1
  46. package/lib/Utils/reporting-utils.d.ts +1 -0
  47. package/lib/Utils/reporting-utils.js +1 -1
  48. package/lib/Utils/rich-message-utils.d.ts.map +1 -1
  49. package/lib/Utils/rich-message-utils.js.map +1 -1
  50. package/lib/Utils/tc-token-utils.js +9 -11
  51. package/lib/Utils/use-multi-file-auth-state.js +10 -11
  52. package/lib/Utils/use-single-file-auth-state.js +8 -2
  53. package/lib/Utils/validate-connection.js +10 -4
  54. package/lib/WABinary/constants.d.ts.map +1 -1
  55. package/lib/WABinary/constants.js.map +1 -1
  56. package/lib/WAUSync/USyncQuery.js +24 -2
  57. package/package.json +10 -7
  58. package/lib/Utils/use-sqlite-auth-state.d.ts.map +0 -1
  59. package/lib/Utils/use-sqlite-auth-state.js.map +0 -1
@@ -11,7 +11,7 @@ import { proto } from '../../WAProto/index.js';
11
11
  import { DEFAULT_ORIGIN, MEDIA_HKDF_KEY_MAPPING, MEDIA_PATH_MAP, NEWSLETTER_MEDIA_PATH_MAP } from '../Defaults/index.js';
12
12
  import { getBinaryNodeChild, getBinaryNodeChildBuffer, jidNormalizedUser } from '../WABinary/index.js';
13
13
  import { aesDecryptGCM, aesEncryptGCM, hkdf } from './crypto.js';
14
- import { generateMessageIDV2 } from './generics.js';
14
+ import { delay, generateMessageIDV2 } from './generics.js';
15
15
  const getTmpFilesDirectory = () => tmpdir();
16
16
  let imageProcessingLibrary;
17
17
  export const getImageProcessingLibrary = async () => {
@@ -621,10 +621,14 @@ export const uploadWithNodeHttp = async ({ url, filePath, headers, timeoutMs, ag
621
621
  res.on('data', chunk => (body += chunk));
622
622
  res.on('end', () => {
623
623
  try {
624
- resolve(JSON.parse(body));
624
+ const parsed = JSON.parse(body);
625
+ // attach status so caller can log/branch on it
626
+ parsed._status = res.statusCode;
627
+ resolve(parsed);
625
628
  }
626
629
  catch {
627
- resolve(undefined);
630
+ // non-JSON body — resolve with status-only object
631
+ resolve({ _status: res.statusCode, _body: body.substring(0, 200) });
628
632
  }
629
633
  });
630
634
  });
@@ -656,10 +660,12 @@ const uploadWithFetch = async ({ url, filePath, headers, timeoutMs, agent }) =>
656
660
  signal: timeoutMs ? AbortSignal.timeout(timeoutMs) : undefined
657
661
  });
658
662
  try {
659
- return (await response.json());
663
+ const parsed = await response.json();
664
+ parsed._status = response.status;
665
+ return parsed;
660
666
  }
661
667
  catch {
662
- return undefined;
668
+ return { _status: response.status };
663
669
  }
664
670
  };
665
671
  /**
@@ -692,7 +698,13 @@ const uploadMedia = async (params, logger) => {
692
698
  export const getWAUploadToServer = ({ customUploadHosts, fetchAgent, logger, options }, refreshMediaConn) => {
693
699
  return async (filePath, { mediaType, fileEncSha256B64, timeoutMs, newsletter }) => {
694
700
  // send a query JSON to obtain the url & auth token to upload our media
695
- let uploadInfo = await refreshMediaConn(false);
701
+ let uploadInfo;
702
+ try {
703
+ uploadInfo = await refreshMediaConn(false);
704
+ }
705
+ catch (err) {
706
+ throw new Boom(`Media upload failed: cannot fetch media connection (${err?.message})`, { statusCode: 503, data: err });
707
+ }
696
708
  let urls;
697
709
  const hosts = [...customUploadHosts, ...uploadInfo.hosts];
698
710
  fileEncSha256B64 = encodeBase64EncodedStringForUpload(fileEncSha256B64);
@@ -708,47 +720,102 @@ export const getWAUploadToServer = ({ customUploadHosts, fetchAgent, logger, opt
708
720
  'Content-Type': 'application/octet-stream',
709
721
  Origin: DEFAULT_ORIGIN
710
722
  };
711
- for (const { hostname } of hosts) {
712
- logger.debug(`uploading to "${hostname}"`);
713
- const auth = encodeURIComponent(uploadInfo.auth);
714
- // Lia@Changes 06-02-26 --- Switch media path map for newsletter uploads
715
- const mediaPathMap = newsletter ? NEWSLETTER_MEDIA_PATH_MAP : MEDIA_PATH_MAP;
716
- // Lia@Changes 20-03-26 --- Add server thumb for newsletter media
717
- const serverThumb = newsletter ? '&server_thumb_gen=1' : '';
718
- const url = `https://${hostname}${mediaPathMap[mediaType]}/${fileEncSha256B64}?auth=${auth}&token=${fileEncSha256B64}${serverThumb}`;
719
- let result;
720
- try {
721
- result = await uploadMedia({
722
- url,
723
- filePath,
724
- headers,
725
- timeoutMs,
726
- agent: fetchAgent
727
- }, logger);
728
- if (result?.url || result?.direct_path) {
729
- urls = {
730
- mediaUrl: result.url,
731
- directPath: result.direct_path,
732
- meta_hmac: result.meta_hmac,
733
- fbid: result.fbid,
734
- ts: result.ts,
735
- thumbnailDirectPath: result.thumbnail_info?.thumbnail_direct_path,
736
- thumbnailSha256: result.thumbnail_info?.thumbnail_sha256
737
- };
738
- break;
739
- }
740
- else {
723
+ // deduplicate hosts WA server sometimes returns same host multiple times
724
+ const seenHosts = new Set();
725
+ const uniqueHosts = hosts.filter(h => {
726
+ if (seenHosts.has(h.hostname)) return false;
727
+ seenHosts.add(h.hostname);
728
+ return true;
729
+ });
730
+ // Lia@Changes 12-06-26 --- mediaConn auth can go stale on long-lived sessions; WA then
731
+ // rejects EVERY host with HTTP 500 (not 401), so plain host-rotation never recovers.
732
+ // Force-refresh once on the first auth-suspect failure and retry the SAME host with fresh auth.
733
+ const AUTH_SUSPECT_STATUS = new Set([401, 403, 408, 500, 502, 503]);
734
+ let refreshedOnce = false;
735
+ // Lia@Changes 06-02-26 --- Switch media path map for newsletter uploads
736
+ const mediaPathMap = newsletter ? NEWSLETTER_MEDIA_PATH_MAP : MEDIA_PATH_MAP;
737
+ const mediaPath = mediaPathMap[mediaType];
738
+ if (!mediaPath && mediaPath !== '') {
739
+ throw new Boom(`Unknown mediaType "${mediaType}" — no upload path defined`, { statusCode: 400 });
740
+ }
741
+ // Lia@Changes 20-03-26 --- Add server thumb for newsletter media.
742
+ // Lia@Changes 12-06-26 --- Gated behind options.newsletterServerThumbGen (default on) so
743
+ // it can be A/B disabled — a non-standard query param is a suspect when newsletter uploads 500.
744
+ const useServerThumb = newsletter && (options?.newsletterServerThumbGen ?? true);
745
+ const serverThumb = useServerThumb ? '&server_thumb_gen=1' : '';
746
+ // Lia@Changes 12-06-26 --- Datacenter/VPS IPs get throttled by the WA media CDN, which
747
+ // surfaces as ALL hosts failing in one pass. Wrap the host sweep in bounded retry ROUNDS
748
+ // with exponential backoff (modeled on whatsmeow's download retry path) so a transient
749
+ // CDN/IP throttle recovers instead of bubbling up a hard "failed on all hosts".
750
+ const maxRounds = Math.max(1, options?.mediaUploadMaxRetries ?? 3);
751
+ for (let round = 0; round < maxRounds && !urls; round++) {
752
+ if (round > 0) {
753
+ // exponential backoff: 1s, 2s, 4s ... capped at 15s
754
+ const backoffMs = Math.min(1000 * Math.pow(2, round - 1), 15000);
755
+ logger.warn({ round, backoffMs }, `all hosts failed, retrying upload round (${round + 1}/${maxRounds}) after backoff`);
756
+ await delay(backoffMs);
757
+ // fresh mediaConn between rounds — auth/host list may have rotated server-side
758
+ try {
741
759
  uploadInfo = await refreshMediaConn(true);
742
- throw new Error(`upload failed, reason: ${JSON.stringify(result)}`);
760
+ }
761
+ catch (refreshErr) {
762
+ logger.warn({ trace: refreshErr?.stack }, 'mediaConn refresh between rounds failed');
743
763
  }
744
764
  }
745
- catch (error) {
746
- const isLast = hostname === hosts[uploadInfo.hosts.length - 1]?.hostname;
747
- logger.warn({ trace: error?.stack, uploadResult: result }, `Error in uploading to ${hostname} ${isLast ? '' : ', retrying...'}`);
765
+ for (let i = 0; i < uniqueHosts.length && !urls; i++) {
766
+ const { hostname } = uniqueHosts[i];
767
+ const isLast = i === uniqueHosts.length - 1;
768
+ // attempt 0 = first try; attempt 1 = retry same host after a forced mediaConn refresh
769
+ for (let attempt = 0; attempt < 2; attempt++) {
770
+ const auth = encodeURIComponent(uploadInfo.auth);
771
+ const url = `https://${hostname}${mediaPath}/${fileEncSha256B64}?auth=${auth}&token=${fileEncSha256B64}${serverThumb}`;
772
+ logger.debug({ hostname, mediaType, round, attempt, url: url.split('?')[0] }, `uploading to host (${i + 1}/${uniqueHosts.length})`);
773
+ let result;
774
+ try {
775
+ result = await uploadMedia({
776
+ url,
777
+ filePath,
778
+ headers,
779
+ timeoutMs,
780
+ agent: fetchAgent
781
+ }, logger);
782
+ if (result?.url || result?.direct_path) {
783
+ urls = {
784
+ mediaUrl: result.url,
785
+ directPath: result.direct_path,
786
+ meta_hmac: result.meta_hmac,
787
+ fbid: result.fbid,
788
+ ts: result.ts,
789
+ thumbnailDirectPath: result.thumbnail_info?.thumbnail_direct_path,
790
+ thumbnailSha256: result.thumbnail_info?.thumbnail_sha256
791
+ };
792
+ break;
793
+ }
794
+ throw new Error(`upload failed, status=${result?._status ?? 'unknown'}, reason: ${JSON.stringify(result)}`);
795
+ }
796
+ catch (error) {
797
+ const status = result?._status;
798
+ // Stale auth often surfaces as a 5xx/408, not just 401/403. Refresh once globally,
799
+ // then immediately retry THIS host with the new token before giving up on it.
800
+ if (!refreshedOnce && AUTH_SUSPECT_STATUS.has(status) && attempt === 0) {
801
+ refreshedOnce = true;
802
+ try {
803
+ uploadInfo = await refreshMediaConn(true);
804
+ logger.warn({ hostname, status }, 'mediaConn refreshed after auth-suspect failure, retrying same host');
805
+ continue; // retry same host, attempt 1
806
+ }
807
+ catch (refreshErr) {
808
+ logger.warn({ trace: refreshErr?.stack }, 'mediaConn refresh failed');
809
+ }
810
+ }
811
+ logger.warn({ trace: error?.stack, uploadResult: result, status }, `Error uploading to ${hostname}${isLast ? ' (last host this round)' : ', retrying next host...'}`);
812
+ break; // move to next host
813
+ }
814
+ }
748
815
  }
749
816
  }
750
817
  if (!urls) {
751
- throw new Boom('Media upload failed on all hosts', { statusCode: 500 });
818
+ throw new Boom(`Media upload failed on all hosts after ${maxRounds} round(s) (${uniqueHosts.map(h => h.hostname).join(', ')})`, { statusCode: 500 });
752
819
  }
753
820
  return urls;
754
821
  };
@@ -1 +1 @@
1
- {"version":3,"file":"messages.d.ts","sourceRoot":"","sources":["../../src/Utils/messages.js"],"names":[],"mappings":"AA6mDA;;;;;GAKG;AACH;;;qBAgCC;AACD;;;;;GAKG;AACH;;qBAgBC;AAnnDM,mDAA+D;AAC/D,qGAYN;AAeM,0FAmKN;AAmTM,kGAaN;AAMM,oFAwBN;AACM,uEAKN;AACM,iEAKN;AAEM,0DAGN;AACM,iEAMN;AAEM,8DAIN;AACM,mFAmxBN;AACM,yGA4EN;AACM,uGAQN;AAEM,iEAMN;AAOM,2DA2CN;AAKM,yDAkCN;AAIM,sFAQoB;AAEpB,uEASN;AAEM,yEAMN;AAEM,yEAON;AAEM,4EAKN;AAgEM,gEAgBN;AAKM,oGA8CN;AAEM,sDAWN;AAkEM,kEAOF;sBAh0DiB,wBAAwB"}
1
+ {"version":3,"file":"messages.d.ts","sourceRoot":"","sources":["../../src/Utils/messages.js"],"names":[],"mappings":"AAgnDA;;;;;GAKG;AACH;;;qBAgCC;AACD;;;;;GAKG;AACH;;qBAgBC;AApnDM,mDAA+D;AAC/D,qGAYN;AAeM,0FAmKN;AAuTM,kGAaN;AAMM,oFAwBN;AACM,uEAKN;AACM,iEAKN;AAEM,sDAGN;AACM,6DAMN;AAEM,0DAIN;AACM,mFAixBN;AACM,yGA4EN;AACM,uGAQN;AAEM,iEAMN;AAOM,2DA0CN;AAKM,yDAkCN;AAIM,sFAQoB;AAEpB,uEASN;AAEM,yEAMN;AAEM,yEAON;AAEM,4EAKN;AAgEM,gEAgBN;AAKM,oGA8CN;AAEM,sDAWN;AAkEM,8DAUN;sBAt0DqB,wBAAwB"}