@alannxd/baileys 6.0.5 → 6.0.6

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 (234) hide show
  1. package/WAProto/fix-import.js +29 -0
  2. package/WAProto/index.js +160 -201
  3. package/engine-requirements.js +1 -1
  4. package/lib/Defaults/baileys-version.json +3 -0
  5. package/lib/Defaults/index.d.ts +15 -37
  6. package/lib/Defaults/index.js +136 -119
  7. package/lib/Defaults/phonenumber-mcc.json +223 -0
  8. package/lib/Signal/Group/ciphertext-message.d.ts +0 -1
  9. package/lib/Signal/Group/ciphertext-message.js +5 -2
  10. package/lib/Signal/Group/group-session-builder.d.ts +3 -4
  11. package/lib/Signal/Group/group-session-builder.js +41 -7
  12. package/lib/Signal/Group/group_cipher.d.ts +4 -4
  13. package/lib/Signal/Group/group_cipher.js +51 -37
  14. package/lib/Signal/Group/index.d.ts +11 -12
  15. package/lib/Signal/Group/index.js +57 -12
  16. package/lib/Signal/Group/keyhelper.d.ts +1 -2
  17. package/lib/Signal/Group/keyhelper.js +44 -7
  18. package/lib/Signal/Group/queue-job.d.ts +1 -0
  19. package/lib/Signal/Group/queue-job.js +57 -0
  20. package/lib/Signal/Group/sender-chain-key.d.ts +2 -3
  21. package/lib/Signal/Group/sender-chain-key.js +15 -7
  22. package/lib/Signal/Group/sender-key-distribution-message.d.ts +1 -2
  23. package/lib/Signal/Group/sender-key-distribution-message.js +11 -8
  24. package/lib/Signal/Group/sender-key-message.d.ts +1 -2
  25. package/lib/Signal/Group/sender-key-message.js +12 -9
  26. package/lib/Signal/Group/sender-key-name.d.ts +0 -1
  27. package/lib/Signal/Group/sender-key-name.js +5 -2
  28. package/lib/Signal/Group/sender-key-record.d.ts +2 -3
  29. package/lib/Signal/Group/sender-key-record.js +21 -9
  30. package/lib/Signal/Group/sender-key-state.d.ts +6 -7
  31. package/lib/Signal/Group/sender-key-state.js +42 -27
  32. package/lib/Signal/Group/sender-message-key.d.ts +0 -1
  33. package/lib/Signal/Group/sender-message-key.js +7 -4
  34. package/lib/Signal/libsignal.d.ts +3 -5
  35. package/lib/Signal/libsignal.js +90 -347
  36. package/lib/Socket/Client/{types.d.ts → abstract-socket-client.d.ts} +5 -4
  37. package/lib/Socket/Client/abstract-socket-client.js +13 -0
  38. package/lib/Socket/Client/index.d.ts +3 -3
  39. package/lib/Socket/Client/index.js +19 -3
  40. package/lib/Socket/Client/mobile-socket-client.d.ts +13 -0
  41. package/lib/Socket/Client/mobile-socket-client.js +65 -0
  42. package/lib/Socket/Client/{websocket.d.ts → web-socket-client.d.ts} +2 -3
  43. package/lib/Socket/Client/web-socket-client.js +62 -0
  44. package/lib/Socket/business.d.ts +108 -154
  45. package/lib/Socket/business.js +43 -162
  46. package/lib/Socket/chats.d.ts +239 -96
  47. package/lib/Socket/chats.js +427 -627
  48. package/lib/Socket/communities.d.ts +146 -239
  49. package/lib/Socket/communities.js +80 -90
  50. package/lib/Socket/groups.d.ts +57 -104
  51. package/lib/Socket/groups.js +161 -154
  52. package/lib/Socket/index.d.ts +115 -202
  53. package/lib/Socket/index.js +10 -11
  54. package/lib/Socket/luxu.d.ts +266 -22
  55. package/lib/Socket/luxu.js +465 -422
  56. package/lib/Socket/messages-recv.d.ts +84 -136
  57. package/lib/Socket/messages-recv.js +615 -1421
  58. package/lib/Socket/messages-send.d.ts +126 -142
  59. package/lib/Socket/messages-send.js +671 -878
  60. package/lib/Socket/newsletter.d.ts +85 -121
  61. package/lib/Socket/newsletter.js +272 -147
  62. package/lib/Socket/registration.d.ts +267 -0
  63. package/lib/Socket/registration.js +166 -0
  64. package/lib/Socket/socket.d.ts +19 -34
  65. package/lib/Socket/socket.js +313 -544
  66. package/lib/Socket/usync.d.ts +36 -0
  67. package/lib/Socket/usync.js +70 -0
  68. package/lib/Store/index.d.ts +3 -10
  69. package/lib/Store/index.js +10 -10
  70. package/lib/Store/make-cache-manager-store.d.ts +11 -17
  71. package/lib/Store/make-cache-manager-store.js +41 -43
  72. package/lib/Store/make-in-memory-store.d.ts +118 -39
  73. package/lib/Store/make-in-memory-store.js +341 -112
  74. package/lib/Store/make-ordered-dictionary.d.ts +10 -11
  75. package/lib/Store/make-ordered-dictionary.js +20 -14
  76. package/lib/Store/object-repository.d.ts +9 -10
  77. package/lib/Store/object-repository.js +6 -11
  78. package/lib/Types/Auth.d.ts +12 -19
  79. package/lib/Types/Auth.js +2 -2
  80. package/lib/Types/Call.d.ts +1 -3
  81. package/lib/Types/Call.js +2 -2
  82. package/lib/Types/Chat.d.ts +13 -35
  83. package/lib/Types/Chat.js +4 -8
  84. package/lib/Types/Contact.d.ts +1 -8
  85. package/lib/Types/Contact.js +2 -2
  86. package/lib/Types/Events.d.ts +17 -116
  87. package/lib/Types/Events.js +2 -2
  88. package/lib/Types/GroupMetadata.d.ts +5 -21
  89. package/lib/Types/GroupMetadata.js +2 -2
  90. package/lib/Types/Label.d.ts +0 -12
  91. package/lib/Types/Label.js +5 -3
  92. package/lib/Types/LabelAssociation.d.ts +0 -1
  93. package/lib/Types/LabelAssociation.js +5 -3
  94. package/lib/Types/Message.d.ts +58 -105
  95. package/lib/Types/Message.js +9 -11
  96. package/lib/Types/Newsletter.d.ts +103 -0
  97. package/lib/Types/Newsletter.js +38 -0
  98. package/lib/Types/Product.d.ts +1 -2
  99. package/lib/Types/Product.js +2 -2
  100. package/lib/Types/Signal.d.ts +2 -32
  101. package/lib/Types/Signal.js +2 -2
  102. package/lib/Types/Socket.d.ts +25 -50
  103. package/lib/Types/Socket.js +2 -3
  104. package/lib/Types/State.d.ts +2 -72
  105. package/lib/Types/State.js +2 -56
  106. package/lib/Types/USync.d.ts +2 -3
  107. package/lib/Types/USync.js +2 -2
  108. package/lib/Types/index.d.ts +14 -22
  109. package/lib/Types/index.js +31 -15
  110. package/lib/Utils/auth-utils.d.ts +6 -12
  111. package/lib/Utils/auth-utils.js +143 -239
  112. package/lib/Utils/baileys-event-stream.d.ts +16 -0
  113. package/lib/Utils/baileys-event-stream.js +63 -0
  114. package/lib/Utils/business.d.ts +2 -3
  115. package/lib/Utils/business.js +69 -66
  116. package/lib/Utils/chat-utils.d.ts +23 -52
  117. package/lib/Utils/chat-utils.js +253 -396
  118. package/lib/Utils/crypto.d.ts +22 -18
  119. package/lib/Utils/crypto.js +90 -57
  120. package/lib/Utils/decode-wa-message.d.ts +8 -55
  121. package/lib/Utils/decode-wa-message.js +84 -203
  122. package/lib/Utils/event-buffer.d.ts +8 -9
  123. package/lib/Utils/event-buffer.js +77 -185
  124. package/lib/Utils/generics.d.ts +29 -28
  125. package/lib/Utils/generics.js +210 -180
  126. package/lib/Utils/history.d.ts +9 -18
  127. package/lib/Utils/history.js +55 -93
  128. package/lib/Utils/index.d.ts +17 -22
  129. package/lib/Utils/index.js +33 -22
  130. package/lib/Utils/link-preview.d.ts +5 -5
  131. package/lib/Utils/link-preview.js +24 -16
  132. package/lib/Utils/logger.d.ts +3 -11
  133. package/lib/Utils/logger.js +7 -3
  134. package/lib/Utils/lt-hash.d.ts +12 -8
  135. package/lib/Utils/lt-hash.js +46 -3
  136. package/lib/Utils/make-mutex.d.ts +2 -4
  137. package/lib/Utils/make-mutex.js +34 -24
  138. package/lib/Utils/messages-media.d.ts +44 -61
  139. package/lib/Utils/messages-media.js +482 -451
  140. package/lib/Utils/messages.d.ts +18 -32
  141. package/lib/Utils/messages.js +369 -458
  142. package/lib/Utils/noise-handler.d.ts +14 -13
  143. package/lib/Utils/noise-handler.js +99 -145
  144. package/lib/Utils/process-message.d.ts +12 -31
  145. package/lib/Utils/process-message.js +150 -459
  146. package/lib/Utils/signal.d.ts +5 -20
  147. package/lib/Utils/signal.js +72 -120
  148. package/lib/Utils/use-multi-file-auth-state.d.ts +2 -2
  149. package/lib/Utils/use-multi-file-auth-state.js +27 -29
  150. package/lib/Utils/validate-connection.d.ts +7 -7
  151. package/lib/Utils/validate-connection.js +99 -73
  152. package/lib/WABinary/constants.d.ts +27 -25
  153. package/lib/WABinary/constants.js +20 -1281
  154. package/lib/WABinary/decode.d.ts +5 -5
  155. package/lib/WABinary/decode.js +42 -52
  156. package/lib/WABinary/encode.d.ts +3 -3
  157. package/lib/WABinary/encode.js +155 -110
  158. package/lib/WABinary/generic-utils.d.ts +7 -8
  159. package/lib/WABinary/generic-utils.js +49 -48
  160. package/lib/WABinary/index.d.ts +5 -6
  161. package/lib/WABinary/index.js +21 -6
  162. package/lib/WABinary/jid-utils.d.ts +8 -25
  163. package/lib/WABinary/jid-utils.js +40 -74
  164. package/lib/WABinary/types.d.ts +1 -2
  165. package/lib/WABinary/types.js +2 -2
  166. package/lib/WAM/BinaryInfo.d.ts +11 -3
  167. package/lib/WAM/BinaryInfo.js +5 -2
  168. package/lib/WAM/constants.d.ts +3 -5
  169. package/lib/WAM/constants.js +11958 -19461
  170. package/lib/WAM/encode.d.ts +3 -3
  171. package/lib/WAM/encode.js +22 -17
  172. package/lib/WAM/index.d.ts +3 -4
  173. package/lib/WAM/index.js +19 -4
  174. package/lib/WAUSync/Protocols/USyncContactProtocol.d.ts +3 -4
  175. package/lib/WAUSync/Protocols/USyncContactProtocol.js +13 -33
  176. package/lib/WAUSync/Protocols/USyncDeviceProtocol.d.ts +2 -3
  177. package/lib/WAUSync/Protocols/USyncDeviceProtocol.js +14 -11
  178. package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.d.ts +2 -3
  179. package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.js +12 -9
  180. package/lib/WAUSync/Protocols/USyncStatusProtocol.d.ts +2 -3
  181. package/lib/WAUSync/Protocols/USyncStatusProtocol.js +13 -9
  182. package/lib/WAUSync/Protocols/UsyncBotProfileProtocol.d.ts +3 -4
  183. package/lib/WAUSync/Protocols/UsyncBotProfileProtocol.js +22 -20
  184. package/lib/WAUSync/Protocols/UsyncLIDProtocol.d.ts +3 -5
  185. package/lib/WAUSync/Protocols/UsyncLIDProtocol.js +8 -13
  186. package/lib/WAUSync/Protocols/index.d.ts +4 -6
  187. package/lib/WAUSync/Protocols/index.js +20 -6
  188. package/lib/WAUSync/USyncQuery.d.ts +4 -6
  189. package/lib/WAUSync/USyncQuery.js +35 -44
  190. package/lib/WAUSync/USyncUser.d.ts +5 -10
  191. package/lib/WAUSync/USyncUser.js +5 -10
  192. package/lib/WAUSync/index.js +19 -4
  193. package/lib/index.d.ts +9 -10
  194. package/lib/index.js +34 -12
  195. package/package.json +50 -83
  196. package/WAProto/GenerateStatics.sh +0 -3
  197. package/WAProto/WAProto.proto +0 -5479
  198. package/WAProto/fix-imports.js +0 -85
  199. package/WAProto/index.d.ts +0 -14017
  200. package/lib/Signal/lid-mapping.d.ts +0 -23
  201. package/lib/Signal/lid-mapping.js +0 -277
  202. package/lib/Socket/Client/types.js +0 -11
  203. package/lib/Socket/Client/websocket.js +0 -54
  204. package/lib/Socket/mex.d.ts +0 -3
  205. package/lib/Socket/mex.js +0 -42
  206. package/lib/Store/keyed-db.d.ts +0 -22
  207. package/lib/Store/keyed-db.js +0 -108
  208. package/lib/Types/Bussines.d.ts +0 -25
  209. package/lib/Types/Bussines.js +0 -2
  210. package/lib/Types/Mex.d.ts +0 -141
  211. package/lib/Types/Mex.js +0 -37
  212. package/lib/Utils/browser-utils.d.ts +0 -4
  213. package/lib/Utils/browser-utils.js +0 -28
  214. package/lib/Utils/companion-reg-client-utils.d.ts +0 -17
  215. package/lib/Utils/companion-reg-client-utils.js +0 -35
  216. package/lib/Utils/identity-change-handler.d.ts +0 -44
  217. package/lib/Utils/identity-change-handler.js +0 -50
  218. package/lib/Utils/message-retry-manager.d.ts +0 -115
  219. package/lib/Utils/message-retry-manager.js +0 -265
  220. package/lib/Utils/offline-node-processor.d.ts +0 -17
  221. package/lib/Utils/offline-node-processor.js +0 -40
  222. package/lib/Utils/pre-key-manager.d.ts +0 -28
  223. package/lib/Utils/pre-key-manager.js +0 -106
  224. package/lib/Utils/reporting-utils.d.ts +0 -11
  225. package/lib/Utils/reporting-utils.js +0 -258
  226. package/lib/Utils/stanza-ack.d.ts +0 -11
  227. package/lib/Utils/stanza-ack.js +0 -38
  228. package/lib/Utils/sync-action-utils.d.ts +0 -19
  229. package/lib/Utils/sync-action-utils.js +0 -49
  230. package/lib/Utils/tc-token-utils.d.ts +0 -37
  231. package/lib/Utils/tc-token-utils.js +0 -163
  232. package/lib/WAUSync/Protocols/USyncUsernameProtocol.d.ts +0 -10
  233. package/lib/WAUSync/Protocols/USyncUsernameProtocol.js +0 -25
  234. package/lib/WAUSync/index.d.ts +0 -4
@@ -1,91 +1,91 @@
1
- import { Boom } from '@hapi/boom';
2
- import { exec } from 'child_process';
3
- import * as Crypto from 'crypto';
4
- import { once } from 'events';
5
- import { createReadStream, createWriteStream, promises as fs, WriteStream } from 'fs';
6
- import { tmpdir } from 'os';
7
- import { join } from 'path';
8
- import { Readable, Transform } from 'stream';
9
- import { URL } from 'url';
10
- import { proto } from '../../WAProto/index.js';
11
- import { DEFAULT_ORIGIN, MEDIA_HKDF_KEY_MAPPING, MEDIA_PATH_MAP } from '../Defaults/index.js';
12
- import { getBinaryNodeChild, getBinaryNodeChildBuffer, jidNormalizedUser } from '../WABinary/index.js';
13
- import { aesDecryptGCM, aesEncryptGCM, hkdf } from './crypto.js';
14
- import { generateMessageIDV2 } from './generics.js';
15
- const getTmpFilesDirectory = () => tmpdir();
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ Object.defineProperty(exports, "__esModule", { value: true });
26
+ exports.getStatusCodeForMediaRetry = exports.decryptMediaRetryData = exports.decodeMediaRetryNode = exports.encryptMediaRetryRequest = exports.getWAUploadToServer = exports.extensionForMediaMessage = exports.downloadEncryptedContent = exports.downloadContentFromMessage = exports.getUrlFromDirectPath = exports.encryptedStream = exports.prepareStream = exports.getHttpStream = exports.generateThumbnail = exports.getStream = exports.toBuffer = exports.toReadable = exports.getAudioWaveform = exports.getAudioDuration = exports.mediaMessageSHA256B64 = exports.generateProfilePicture = exports.encodeBase64EncodedStringForUpload = exports.extractImageThumb = exports.getMediaKeys = exports.hkdfInfoKey = void 0;
27
+ const boom_1 = require("@hapi/boom");
28
+ const child_process_1 = require("child_process");
29
+ const Crypto = __importStar(require("crypto"));
30
+ const events_1 = require("events");
31
+ const fs_1 = require("fs");
32
+ const os_1 = require("os");
33
+ const path_1 = require("path");
34
+ const stream_1 = require("stream");
35
+ const WAProto_1 = require("../../WAProto");
36
+ const Defaults_1 = require("../Defaults");
37
+ const WABinary_1 = require("../WABinary");
38
+ const crypto_1 = require("./crypto");
39
+ const generics_1 = require("./generics");
40
+ const getTmpFilesDirectory = () => (0, os_1.tmpdir)();
16
41
  const getImageProcessingLibrary = async () => {
17
- //@ts-ignore
18
- const [jimp, sharp] = await Promise.all([import('jimp').catch(() => { }), import('sharp').catch(() => { })]);
42
+ const [_jimp, sharp] = await Promise.all([
43
+ (async () => {
44
+ const jimp = await (import('jimp')
45
+ .catch(() => { }));
46
+ return jimp;
47
+ })(),
48
+ (async () => {
49
+ const sharp = await (import('sharp')
50
+ .catch(() => { }));
51
+ return sharp;
52
+ })()
53
+ ]);
19
54
  if (sharp) {
20
55
  return { sharp };
21
56
  }
57
+ const jimp = (_jimp === null || _jimp === void 0 ? void 0 : _jimp.default) || _jimp;
22
58
  if (jimp) {
23
59
  return { jimp };
24
60
  }
25
- throw new Boom('No image processing library available');
61
+ throw new boom_1.Boom('No image processing library available');
26
62
  };
27
- export const hkdfInfoKey = (type) => {
28
- const hkdfInfo = MEDIA_HKDF_KEY_MAPPING[type];
63
+ const hkdfInfoKey = (type) => {
64
+ const hkdfInfo = Defaults_1.MEDIA_HKDF_KEY_MAPPING[type];
29
65
  return `WhatsApp ${hkdfInfo} Keys`;
30
66
  };
31
- export const getRawMediaUploadData = async (media, mediaType, logger) => {
32
- const { stream } = await getStream(media);
33
- logger?.debug('got stream for raw upload');
34
- const hasher = Crypto.createHash('sha256');
35
- const filePath = join(tmpdir(), mediaType + generateMessageIDV2());
36
- const fileWriteStream = createWriteStream(filePath);
37
- let fileLength = 0;
38
- try {
39
- for await (const data of stream) {
40
- fileLength += data.length;
41
- hasher.update(data);
42
- if (!fileWriteStream.write(data)) {
43
- await once(fileWriteStream, 'drain');
44
- }
45
- }
46
- fileWriteStream.end();
47
- await once(fileWriteStream, 'finish');
48
- stream.destroy();
49
- const fileSha256 = hasher.digest();
50
- logger?.debug('hashed data for raw upload');
51
- return {
52
- filePath: filePath,
53
- fileSha256,
54
- fileLength
55
- };
56
- }
57
- catch (error) {
58
- fileWriteStream.destroy();
59
- stream.destroy();
60
- try {
61
- await fs.unlink(filePath);
62
- }
63
- catch {
64
- //
65
- }
66
- throw error;
67
- }
68
- };
67
+ exports.hkdfInfoKey = hkdfInfoKey;
69
68
  /** generates all the keys required to encrypt/decrypt & sign a media message */
70
- export async function getMediaKeys(buffer, mediaType) {
69
+ function getMediaKeys(buffer, mediaType) {
71
70
  if (!buffer) {
72
- throw new Boom('Cannot derive from empty media key');
71
+ throw new boom_1.Boom('Cannot derive from empty media key');
73
72
  }
74
73
  if (typeof buffer === 'string') {
75
74
  buffer = Buffer.from(buffer.replace('data:;base64,', ''), 'base64');
76
75
  }
77
76
  // expand using HKDF to 112 bytes, also pass in the relevant app info
78
- const expandedMediaKey = hkdf(buffer, 112, { info: hkdfInfoKey(mediaType) });
77
+ const expandedMediaKey = (0, crypto_1.hkdf)(buffer, 112, { info: (0, exports.hkdfInfoKey)(mediaType) });
79
78
  return {
80
79
  iv: expandedMediaKey.slice(0, 16),
81
80
  cipherKey: expandedMediaKey.slice(16, 48),
82
- macKey: expandedMediaKey.slice(48, 80)
81
+ macKey: expandedMediaKey.slice(48, 80),
83
82
  };
84
83
  }
84
+ exports.getMediaKeys = getMediaKeys;
85
85
  /** Extracts video thumb using FFMPEG */
86
86
  const extractVideoThumb = async (path, destPath, time, size) => new Promise((resolve, reject) => {
87
87
  const cmd = `ffmpeg -ss ${time} -i ${path} -y -vf scale=${size.width}:-1 -vframes 1 -f image2 ${destPath}`;
88
- exec(cmd, err => {
88
+ (0, child_process_1.exec)(cmd, (err) => {
89
89
  if (err) {
90
90
  reject(err);
91
91
  }
@@ -94,151 +94,240 @@ const extractVideoThumb = async (path, destPath, time, size) => new Promise((res
94
94
  }
95
95
  });
96
96
  });
97
- export const extractImageThumb = async (bufferOrFilePath, width = 32) => {
98
- // TODO: Move entirely to sharp, removing jimp as it supports readable streams
99
- // This will have positive speed and performance impacts as well as minimizing RAM usage.
100
- if (bufferOrFilePath instanceof Readable) {
101
- bufferOrFilePath = await toBuffer(bufferOrFilePath);
97
+ const extractImageThumb = async (bufferOrFilePath, width = 32) => {
98
+ var _a, _b;
99
+ if (bufferOrFilePath instanceof stream_1.Readable) {
100
+ bufferOrFilePath = await (0, exports.toBuffer)(bufferOrFilePath);
102
101
  }
103
102
  const lib = await getImageProcessingLibrary();
104
- if ('sharp' in lib && typeof lib.sharp?.default === 'function') {
103
+ if ('sharp' in lib && typeof ((_a = lib.sharp) === null || _a === void 0 ? void 0 : _a.default) === 'function') {
105
104
  const img = lib.sharp.default(bufferOrFilePath);
106
105
  const dimensions = await img.metadata();
107
- const buffer = await img.resize(width).jpeg({ quality: 50 }).toBuffer();
106
+ const buffer = await img
107
+ .resize(width)
108
+ .jpeg({ quality: 50 })
109
+ .toBuffer();
108
110
  return {
109
111
  buffer,
110
112
  original: {
111
113
  width: dimensions.width,
112
- height: dimensions.height
113
- }
114
+ height: dimensions.height,
115
+ },
114
116
  };
115
117
  }
116
- else if ('jimp' in lib && typeof lib.jimp?.Jimp === 'object') {
117
- const jimp = await lib.jimp.Jimp.read(bufferOrFilePath);
118
+ else if ('jimp' in lib && typeof ((_b = lib.jimp) === null || _b === void 0 ? void 0 : _b.read) === 'function') {
119
+ const { read, MIME_JPEG, RESIZE_BILINEAR, AUTO } = lib.jimp;
120
+ const jimp = await read(bufferOrFilePath);
118
121
  const dimensions = {
119
- width: jimp.width,
120
- height: jimp.height
122
+ width: jimp.getWidth(),
123
+ height: jimp.getHeight()
121
124
  };
122
125
  const buffer = await jimp
123
- .resize({ w: width, mode: lib.jimp.ResizeStrategy.BILINEAR })
124
- .getBuffer('image/jpeg', { quality: 50 });
126
+ .quality(50)
127
+ .resize(width, AUTO, RESIZE_BILINEAR)
128
+ .getBufferAsync(MIME_JPEG);
125
129
  return {
126
130
  buffer,
127
131
  original: dimensions
128
132
  };
129
133
  }
130
134
  else {
131
- throw new Boom('No image processing library available');
135
+ throw new boom_1.Boom('No image processing library available');
132
136
  }
133
137
  };
134
- export const encodeBase64EncodedStringForUpload = (b64) => encodeURIComponent(b64.replace(/\+/g, '-').replace(/\//g, '_').replace(/\=+$/, ''));
135
- export const generateProfilePicture = async (mediaUpload, dimensions) => {
136
- let buffer;
137
- const { width: w = 640, height: h = 640 } = dimensions || {};
138
+ exports.extractImageThumb = extractImageThumb;
139
+ const encodeBase64EncodedStringForUpload = (b64) => (encodeURIComponent(b64
140
+ .replace(/\+/g, '-')
141
+ .replace(/\//g, '_')
142
+ .replace(/\=+$/, '')));
143
+ exports.encodeBase64EncodedStringForUpload = encodeBase64EncodedStringForUpload;
144
+ const generateProfilePicture = async (mediaUpload) => {
145
+ var _a, _b;
146
+ let bufferOrFilePath;
138
147
  if (Buffer.isBuffer(mediaUpload)) {
139
- buffer = mediaUpload;
148
+ bufferOrFilePath = mediaUpload;
149
+ }
150
+ else if ('url' in mediaUpload) {
151
+ bufferOrFilePath = mediaUpload.url.toString();
140
152
  }
141
153
  else {
142
- // Use getStream to handle all WAMediaUpload types (Buffer, Stream, URL)
143
- const { stream } = await getStream(mediaUpload);
144
- // Convert the resulting stream to a buffer
145
- buffer = await toBuffer(stream);
154
+ bufferOrFilePath = await (0, exports.toBuffer)(mediaUpload.stream);
146
155
  }
147
156
  const lib = await getImageProcessingLibrary();
148
157
  let img;
149
- if ('sharp' in lib && typeof lib.sharp?.default === 'function') {
150
- img = lib.sharp
151
- .default(buffer)
152
- .resize(w, h)
158
+ if ('sharp' in lib && typeof ((_a = lib.sharp) === null || _a === void 0 ? void 0 : _a.default) === 'function') {
159
+ img = lib.sharp.default(bufferOrFilePath)
160
+ .resize(640, 640)
153
161
  .jpeg({
154
- quality: 50
162
+ quality: 50,
155
163
  })
156
164
  .toBuffer();
157
165
  }
158
- else if ('jimp' in lib && typeof lib.jimp?.Jimp === 'function') {
159
- const jimp = await lib.jimp.Jimp.read(buffer);
160
- const min = Math.min(jimp.width, jimp.height);
161
- const cropped = jimp.crop({ x: 0, y: 0, w: min, h: min });
162
- img = cropped.resize({ w, h, mode: lib.jimp.ResizeStrategy.BILINEAR }).getBuffer('image/jpeg', { quality: 50 });
166
+ else if ('jimp' in lib && typeof ((_b = lib.jimp) === null || _b === void 0 ? void 0 : _b.read) === 'function') {
167
+ const { read, MIME_JPEG, RESIZE_BILINEAR } = lib.jimp;
168
+ const jimp = await read(bufferOrFilePath);
169
+ const min = Math.min(jimp.getWidth(), jimp.getHeight());
170
+ const cropped = jimp.crop(0, 0, min, min);
171
+ img = cropped
172
+ .quality(50)
173
+ .resize(640, 640, RESIZE_BILINEAR)
174
+ .getBufferAsync(MIME_JPEG);
163
175
  }
164
176
  else {
165
- throw new Boom('No image processing library available');
177
+ throw new boom_1.Boom('No image processing library available');
166
178
  }
167
179
  return {
168
- img: await img
180
+ img: await img,
169
181
  };
170
182
  };
183
+ exports.generateProfilePicture = generateProfilePicture;
171
184
  /** gets the SHA256 of the given media message */
172
- export const mediaMessageSHA256B64 = (message) => {
185
+ const mediaMessageSHA256B64 = (message) => {
173
186
  const media = Object.values(message)[0];
174
- return media?.fileSha256 && Buffer.from(media.fileSha256).toString('base64');
187
+ return (media === null || media === void 0 ? void 0 : media.fileSha256) && Buffer.from(media.fileSha256).toString('base64');
175
188
  };
176
- export async function getAudioDuration(buffer) {
177
- const musicMetadata = await import('music-metadata');
178
- let metadata;
179
- const options = {
180
- duration: true
181
- };
182
- if (Buffer.isBuffer(buffer)) {
183
- metadata = await musicMetadata.parseBuffer(buffer, undefined, options);
184
- }
185
- else if (typeof buffer === 'string') {
186
- metadata = await musicMetadata.parseFile(buffer, options);
187
- }
188
- else {
189
- metadata = await musicMetadata.parseStream(buffer, undefined, options);
189
+ exports.mediaMessageSHA256B64 = mediaMessageSHA256B64;
190
+ async function getAudioDuration(buffer) {
191
+ try {
192
+ const { PassThrough } = require('stream');
193
+ const ff = require('fluent-ffmpeg');
194
+
195
+ return await new Promise((resolve, reject) => {
196
+ const inputStream = new PassThrough();
197
+ inputStream.end(buffer);
198
+
199
+ ff(inputStream)
200
+ .ffprobe((err, data) => {
201
+ if (err) reject(err);
202
+ else resolve(data.format.duration);
203
+ });
204
+ });
205
+ } catch (error) {
206
+ const musicMetadata = await import('music-metadata');
207
+ let metadata;
208
+ if (Buffer.isBuffer(buffer)) {
209
+ metadata = await musicMetadata.parseBuffer(buffer, undefined, {
210
+ duration: true
211
+ });
212
+ } else if (typeof buffer === 'string') {
213
+ const rStream = (0, fs_1.createReadStream)(buffer);
214
+ try {
215
+ metadata = await musicMetadata.parseStream(rStream, undefined, {
216
+ duration: true
217
+ });
218
+ } finally {
219
+ rStream.destroy();
220
+ }
221
+ } else {
222
+ metadata = await musicMetadata.parseStream(buffer, undefined, {
223
+ duration: true
224
+ });
225
+ }
226
+ return metadata.format.duration;
190
227
  }
191
- return metadata.format.duration;
192
228
  }
193
- /**
194
- referenced from and modifying https://github.com/wppconnect-team/wa-js/blob/main/src/chat/functions/prepareAudioWaveform.ts
195
- */
196
- export async function getAudioWaveform(buffer, logger) {
229
+ exports.getAudioDuration = getAudioDuration;
230
+ async function getAudioWaveform(buffer, logger) {
197
231
  try {
198
- // @ts-ignore
199
- const { default: decoder } = await import('audio-decode');
232
+ const { PassThrough } = require('stream');
233
+ const ff = require('fluent-ffmpeg');
234
+
200
235
  let audioData;
201
236
  if (Buffer.isBuffer(buffer)) {
202
237
  audioData = buffer;
238
+ } else if (typeof buffer === 'string') {
239
+ const rStream = require('fs').createReadStream(buffer);
240
+ audioData = await exports.toBuffer(rStream);
241
+ } else {
242
+ audioData = await exports.toBuffer(buffer);
203
243
  }
204
- else if (typeof buffer === 'string') {
205
- const rStream = createReadStream(buffer);
206
- audioData = await toBuffer(rStream);
207
- }
208
- else {
209
- audioData = await toBuffer(buffer);
210
- }
211
- const audioBuffer = await decoder(audioData);
212
- const rawData = audioBuffer.getChannelData(0); // We only need to work with one channel of data
213
- const samples = 64; // Number of samples we want to have in our final data set
214
- const blockSize = Math.floor(rawData.length / samples); // the number of samples in each subdivision
215
- const filteredData = [];
216
- for (let i = 0; i < samples; i++) {
217
- const blockStart = blockSize * i; // the location of the first sample in the block
218
- let sum = 0;
219
- for (let j = 0; j < blockSize; j++) {
220
- sum = sum + Math.abs(rawData[blockStart + j]); // find the sum of all the samples in the block
221
- }
222
- filteredData.push(sum / blockSize); // divide the sum by the block size to get the average
223
- }
224
- // This guarantees that the largest data point will be set to 1, and the rest of the data will scale proportionally.
225
- const multiplier = Math.pow(Math.max(...filteredData), -1);
226
- const normalizedData = filteredData.map(n => n * multiplier);
227
- // Generate waveform like WhatsApp
228
- const waveform = new Uint8Array(normalizedData.map(n => Math.floor(100 * n)));
229
- return waveform;
244
+
245
+ return await new Promise((resolve, reject) => {
246
+ const inputStream = new PassThrough();
247
+ inputStream.end(audioData);
248
+ const chunks = [];
249
+ const bars = 64;
250
+
251
+ ff(inputStream)
252
+ .audioChannels(1)
253
+ .audioFrequency(16000)
254
+ .format('s16le')
255
+ .on('error', reject)
256
+ .on('end', () => {
257
+ const rawData = Buffer.concat(chunks);
258
+ const samples = rawData.length / 2;
259
+ const amplitudes = [];
260
+
261
+ for (let i = 0; i < samples; i++) {
262
+ amplitudes.push(Math.abs(rawData.readInt16LE(i * 2)) / 32768);
263
+ }
264
+
265
+ const blockSize = Math.floor(amplitudes.length / bars);
266
+ const avg = [];
267
+ for (let i = 0; i < bars; i++) {
268
+ const block = amplitudes.slice(i * blockSize, (i + 1) * blockSize);
269
+ avg.push(block.reduce((a, b) => a + b, 0) / block.length);
270
+ }
271
+
272
+ const max = Math.max(...avg);
273
+ const normalized = avg.map(v => Math.floor((v / max) * 100));
274
+ resolve(new Uint8Array(normalized));
275
+ })
276
+ .pipe()
277
+ .on('data', chunk => chunks.push(chunk));
278
+ });
279
+ } catch (e) {
280
+ logger?.debug(e);
230
281
  }
231
- catch (e) {
232
- logger?.debug('Failed to generate waveform: ' + e);
282
+ }
283
+ exports.getAudioWaveform = getAudioWaveform;
284
+ async function convertToOpusBuffer(buffer, logger) {
285
+ try {
286
+ const { PassThrough } = require('stream');
287
+ const ff = require('fluent-ffmpeg');
288
+
289
+ return await new Promise((resolve, reject) => {
290
+ const inStream = new PassThrough();
291
+ const outStream = new PassThrough();
292
+ const chunks = [];
293
+ inStream.end(buffer);
294
+
295
+ ff(inStream)
296
+ .noVideo()
297
+ .audioCodec('libopus')
298
+ .format('ogg')
299
+ .audioBitrate('48k')
300
+ .audioChannels(1)
301
+ .audioFrequency(48000)
302
+ .outputOptions([
303
+ '-vn',
304
+ '-b:a 64k',
305
+ '-ac 2',
306
+ '-ar 48000',
307
+ '-map_metadata', '-1',
308
+ '-application', 'voip'
309
+ ])
310
+ .on('error', reject)
311
+ .on('end', () => resolve(Buffer.concat(chunks)))
312
+ .pipe(outStream, {
313
+ end: true
314
+ });
315
+ outStream.on('data', c => chunks.push(c));
316
+ });
317
+ } catch (e) {
318
+ logger?.debug(e);
319
+ throw e;
233
320
  }
234
321
  }
235
- export const toReadable = (buffer) => {
236
- const readable = new Readable({ read: () => { } });
322
+ exports.convertToOpusBuffer = convertToOpusBuffer;
323
+ const toReadable = (buffer) => {
324
+ const readable = new stream_1.Readable({ read: () => { } });
237
325
  readable.push(buffer);
238
326
  readable.push(null);
239
327
  return readable;
240
328
  };
241
- export const toBuffer = async (stream) => {
329
+ exports.toReadable = toReadable;
330
+ const toBuffer = async (stream) => {
242
331
  const chunks = [];
243
332
  for await (const chunk of stream) {
244
333
  chunks.push(chunk);
@@ -246,47 +335,45 @@ export const toBuffer = async (stream) => {
246
335
  stream.destroy();
247
336
  return Buffer.concat(chunks);
248
337
  };
249
- export const getStream = async (item, opts) => {
338
+ exports.toBuffer = toBuffer;
339
+ const getStream = async (item, opts) => {
250
340
  if (Buffer.isBuffer(item)) {
251
- return { stream: toReadable(item), type: 'buffer' };
341
+ return { stream: (0, exports.toReadable)(item), type: 'buffer' };
252
342
  }
253
343
  if ('stream' in item) {
254
344
  return { stream: item.stream, type: 'readable' };
255
345
  }
256
- const urlStr = item.url.toString();
257
- if (urlStr.startsWith('data:')) {
258
- const buffer = Buffer.from(urlStr.split(',')[1], 'base64');
259
- return { stream: toReadable(buffer), type: 'buffer' };
260
- }
261
- if (urlStr.startsWith('http://') || urlStr.startsWith('https://')) {
262
- return { stream: await getHttpStream(item.url, opts), type: 'remote' };
346
+ if (item.url.toString().startsWith('http://') || item.url.toString().startsWith('https://')) {
347
+ return { stream: await (0, exports.getHttpStream)(item.url, opts), type: 'remote' };
263
348
  }
264
- return { stream: createReadStream(item.url), type: 'file' };
349
+ return { stream: (0, fs_1.createReadStream)(item.url), type: 'file' };
265
350
  };
351
+ exports.getStream = getStream;
266
352
  /** generates a thumbnail for a given media, if required */
267
- export async function generateThumbnail(file, mediaType, options) {
353
+ async function generateThumbnail(file, mediaType, options) {
354
+ var _a;
268
355
  let thumbnail;
269
356
  let originalImageDimensions;
270
357
  if (mediaType === 'image') {
271
- const { buffer, original } = await extractImageThumb(file);
358
+ const { buffer, original } = await (0, exports.extractImageThumb)(file);
272
359
  thumbnail = buffer.toString('base64');
273
360
  if (original.width && original.height) {
274
361
  originalImageDimensions = {
275
362
  width: original.width,
276
- height: original.height
363
+ height: original.height,
277
364
  };
278
365
  }
279
366
  }
280
367
  else if (mediaType === 'video') {
281
- const imgFilename = join(getTmpFilesDirectory(), generateMessageIDV2() + '.jpg');
368
+ const imgFilename = (0, path_1.join)(getTmpFilesDirectory(), (0, generics_1.generateMessageID)() + '.jpg');
282
369
  try {
283
370
  await extractVideoThumb(file, imgFilename, '00:00:00', { width: 32, height: 32 });
284
- const buff = await fs.readFile(imgFilename);
371
+ const buff = await fs_1.promises.readFile(imgFilename);
285
372
  thumbnail = buff.toString('base64');
286
- await fs.unlink(imgFilename);
373
+ await fs_1.promises.unlink(imgFilename);
287
374
  }
288
375
  catch (err) {
289
- options.logger?.debug('could not generate video thumb: ' + err);
376
+ (_a = options.logger) === null || _a === void 0 ? void 0 : _a.debug('could not generate video thumb: ' + err);
290
377
  }
291
378
  }
292
379
  return {
@@ -294,141 +381,180 @@ export async function generateThumbnail(file, mediaType, options) {
294
381
  originalImageDimensions
295
382
  };
296
383
  }
297
- export const getHttpStream = async (url, options = {}) => {
298
- const response = await fetch(url.toString(), {
299
- dispatcher: options.dispatcher,
300
- method: 'GET',
301
- headers: options.headers
302
- });
303
- if (!response.ok) {
304
- throw new Boom(`Failed to fetch stream from ${url}`, { statusCode: response.status, data: { url } });
384
+ exports.generateThumbnail = generateThumbnail;
385
+ const getHttpStream = async (url, options = {}) => {
386
+ const { default: axios } = await import('axios');
387
+ const fetched = await axios.get(url.toString(), { ...options, responseType: 'stream' });
388
+ return fetched.data;
389
+ };
390
+ exports.getHttpStream = getHttpStream;
391
+ const prepareStream = async (media, mediaType, { logger, saveOriginalFileIfRequired, opts } = {}) => {
392
+ const { stream, type } = await (0, exports.getStream)(media, opts);
393
+ logger === null || logger === void 0 ? void 0 : logger.debug('fetched media stream');
394
+ let bodyPath;
395
+ let didSaveToTmpPath = false;
396
+ try {
397
+ const buffer = await (0, exports.toBuffer)(stream);
398
+ if (type === 'file') {
399
+ bodyPath = media.url;
400
+ }
401
+ else if (saveOriginalFileIfRequired) {
402
+ bodyPath = (0, path_1.join)(getTmpFilesDirectory(), mediaType + (0, generics_1.generateMessageID)());
403
+ (0, fs_1.writeFileSync)(bodyPath, buffer);
404
+ didSaveToTmpPath = true;
405
+ }
406
+ const fileLength = buffer.length;
407
+ const fileSha256 = Crypto.createHash('sha256').update(buffer).digest();
408
+ stream === null || stream === void 0 ? void 0 : stream.destroy();
409
+ logger === null || logger === void 0 ? void 0 : logger.debug('prepare stream data successfully');
410
+ return {
411
+ mediaKey: undefined,
412
+ encWriteStream: buffer,
413
+ fileLength,
414
+ fileSha256,
415
+ fileEncSha256: undefined,
416
+ bodyPath,
417
+ didSaveToTmpPath
418
+ };
419
+ }
420
+ catch (error) {
421
+ // destroy all streams with error
422
+ stream.destroy();
423
+ if (didSaveToTmpPath) {
424
+ try {
425
+ await fs_1.promises.unlink(bodyPath);
426
+ }
427
+ catch (err) {
428
+ logger === null || logger === void 0 ? void 0 : logger.error({ err }, 'failed to save to tmp path');
429
+ }
430
+ }
431
+ throw error;
305
432
  }
306
- // @ts-ignore Node18+ Readable.fromWeb exists
307
- return response.body instanceof Readable ? response.body : Readable.fromWeb(response.body);
308
433
  };
309
- export const encryptedStream = async (media, mediaType, { logger, saveOriginalFileIfRequired, opts } = {}) => {
310
- const { stream, type } = await getStream(media, opts);
311
- logger?.debug('fetched media stream');
312
- const mediaKey = Crypto.randomBytes(32);
313
- const { cipherKey, iv, macKey } = await getMediaKeys(mediaKey, mediaType);
314
- const encFilePath = join(getTmpFilesDirectory(), mediaType + generateMessageIDV2() + '-enc');
315
- const encFileWriteStream = createWriteStream(encFilePath);
316
- let originalFileStream;
317
- let originalFilePath;
318
- if (saveOriginalFileIfRequired) {
319
- originalFilePath = join(getTmpFilesDirectory(), mediaType + generateMessageIDV2() + '-original');
320
- originalFileStream = createWriteStream(originalFilePath);
434
+ exports.prepareStream = prepareStream;
435
+ const encryptedStream = async (media, mediaType, { logger, saveOriginalFileIfRequired, opts, isPtt, forceOpus } = {}) => {
436
+ const { stream, type } = await (0, exports.getStream)(media, opts);
437
+
438
+ let finalStream = stream;
439
+ if (mediaType === 'audio' && (isPtt === true || forceOpus === true)) {
440
+ try {
441
+ const buffer = await (0, exports.toBuffer)(stream);
442
+ const opusBuffer = await exports.convertToOpusBuffer(buffer, logger);
443
+ finalStream = (0, exports.toReadable)(opusBuffer);
444
+ } catch (error) {
445
+ const { stream: newStream } = await (0, exports.getStream)(media, opts);
446
+ finalStream = newStream;
447
+ }
321
448
  }
449
+
450
+ const mediaKey = Crypto.randomBytes(32);
451
+ const { cipherKey, iv, macKey } = getMediaKeys(mediaKey, mediaType);
452
+ const encWriteStream = new stream_1.Readable({ read: () => { } });
453
+ let bodyPath;
454
+ let writeStream;
455
+ let didSaveToTmpPath = false;
456
+
457
+ if (type === 'file') {
458
+ bodyPath = media.url;
459
+ }
460
+ else if (saveOriginalFileIfRequired) {
461
+ bodyPath = (0, path_1.join)(getTmpFilesDirectory(), mediaType + (0, generics_1.generateMessageID)());
462
+ writeStream = (0, fs_1.createWriteStream)(bodyPath);
463
+ didSaveToTmpPath = true;
464
+ }
465
+
322
466
  let fileLength = 0;
323
467
  const aes = Crypto.createCipheriv('aes-256-cbc', cipherKey, iv);
324
- const hmac = Crypto.createHmac('sha256', macKey).update(iv);
325
- const sha256Plain = Crypto.createHash('sha256');
326
- const sha256Enc = Crypto.createHash('sha256');
327
- const onChunk = async (buff) => {
328
- sha256Enc.update(buff);
329
- hmac.update(buff);
330
- // Handle backpressure: if write returns false, wait for drain
331
- if (!encFileWriteStream.write(buff)) {
332
- await once(encFileWriteStream, 'drain');
333
- }
334
- };
468
+ let hmac = Crypto.createHmac('sha256', macKey).update(iv);
469
+ let sha256Plain = Crypto.createHash('sha256');
470
+ let sha256Enc = Crypto.createHash('sha256');
471
+
335
472
  try {
336
- for await (const data of stream) {
473
+ for await (const data of finalStream) {
337
474
  fileLength += data.length;
338
- if (type === 'remote' &&
339
- opts?.maxContentLength &&
340
- fileLength + data.length > opts.maxContentLength) {
341
- throw new Boom(`content length exceeded when encrypting "${type}"`, {
475
+ if (type === 'remote'
476
+ && (opts === null || opts === void 0 ? void 0 : opts.maxContentLength)
477
+ && fileLength + data.length > opts.maxContentLength) {
478
+ throw new boom_1.Boom(`content length exceeded when encrypting "${type}"`, {
342
479
  data: { media, type }
343
480
  });
344
481
  }
345
- if (originalFileStream) {
346
- if (!originalFileStream.write(data)) {
347
- await once(originalFileStream, 'drain');
482
+
483
+ sha256Plain = sha256Plain.update(data);
484
+ if (writeStream) {
485
+ if (!writeStream.write(data)) {
486
+ await (0, events_1.once)(writeStream, 'drain');
348
487
  }
349
488
  }
350
- sha256Plain.update(data);
351
- await onChunk(aes.update(data));
489
+ onChunk(aes.update(data));
352
490
  }
353
- await onChunk(aes.final());
491
+
492
+ onChunk(aes.final());
354
493
  const mac = hmac.digest().slice(0, 10);
355
- sha256Enc.update(mac);
494
+ sha256Enc = sha256Enc.update(mac);
356
495
  const fileSha256 = sha256Plain.digest();
357
496
  const fileEncSha256 = sha256Enc.digest();
358
- encFileWriteStream.write(mac);
359
- const encFinishPromise = once(encFileWriteStream, 'finish');
360
- const originalFinishPromise = originalFileStream ? once(originalFileStream, 'finish') : Promise.resolve();
361
- encFileWriteStream.end();
362
- originalFileStream?.end?.();
363
- stream.destroy();
364
- // Wait for write streams to fully flush to disk
365
- // This helps reduce memory pressure by allowing OS to release buffers
366
- await encFinishPromise;
367
- await originalFinishPromise;
368
- logger?.debug('encrypted data successfully');
497
+
498
+ encWriteStream.push(mac);
499
+ encWriteStream.push(null);
500
+ writeStream === null || writeStream === void 0 ? void 0 : writeStream.end();
501
+ finalStream.destroy();
502
+
369
503
  return {
370
504
  mediaKey,
371
- originalFilePath,
372
- encFilePath,
505
+ encWriteStream,
506
+ bodyPath,
373
507
  mac,
374
508
  fileEncSha256,
375
509
  fileSha256,
376
- fileLength
510
+ fileLength,
511
+ didSaveToTmpPath
377
512
  };
378
513
  }
379
514
  catch (error) {
380
- // destroy all streams with error
381
- encFileWriteStream.destroy();
382
- originalFileStream?.destroy?.();
515
+ encWriteStream.destroy();
516
+ writeStream === null || writeStream === void 0 ? void 0 : writeStream.destroy();
383
517
  aes.destroy();
384
518
  hmac.destroy();
385
519
  sha256Plain.destroy();
386
520
  sha256Enc.destroy();
387
- stream.destroy();
388
- try {
389
- await fs.unlink(encFilePath);
390
- if (originalFilePath) {
391
- await fs.unlink(originalFilePath);
521
+ finalStream.destroy();
522
+
523
+ if (didSaveToTmpPath) {
524
+ try {
525
+ await fs_1.promises.unlink(bodyPath);
526
+ }
527
+ catch (err) {
392
528
  }
393
- }
394
- catch (err) {
395
- logger?.error({ err }, 'failed deleting tmp files');
396
529
  }
397
530
  throw error;
398
531
  }
532
+
533
+ function onChunk(buff) {
534
+ sha256Enc = sha256Enc.update(buff);
535
+ hmac = hmac.update(buff);
536
+ encWriteStream.push(buff);
537
+ }
399
538
  };
400
- export const DEF_MEDIA_HOST = 'mmg.whatsapp.net';
539
+ exports.encryptedStream = encryptedStream;
540
+ const DEF_HOST = 'mmg.whatsapp.net';
401
541
  const AES_CHUNK_SIZE = 16;
402
542
  const toSmallestChunkSize = (num) => {
403
543
  return Math.floor(num / AES_CHUNK_SIZE) * AES_CHUNK_SIZE;
404
544
  };
405
- export const getUrlFromDirectPath = (directPath, host = DEF_MEDIA_HOST) => `https://${host}${directPath}`;
406
- const extractHost = (url) => {
407
- if (!url)
408
- return undefined;
409
- try {
410
- return new URL(url).host;
411
- }
412
- catch {
413
- return undefined;
414
- }
415
- };
416
- export const downloadContentFromMessage = async ({ mediaKey, directPath, url }, type, opts = {}) => {
417
- // Fallback host: explicit opt > host parsed from `url` > DEF_MEDIA_HOST.
418
- // Lets us honor a non-default host carried by the proto without forcing callers to thread it through.
419
- const fallbackHost = opts.host ?? extractHost(url);
420
- const downloadUrl = directPath ? getUrlFromDirectPath(directPath, fallbackHost) : url;
421
- if (!downloadUrl) {
422
- throw new Boom('No valid media URL or directPath present in message', { statusCode: 400 });
423
- }
424
- const keys = await getMediaKeys(mediaKey, type);
425
- return downloadEncryptedContent(downloadUrl, keys, opts);
545
+ const getUrlFromDirectPath = (directPath) => `https://${DEF_HOST}${directPath}`;
546
+ exports.getUrlFromDirectPath = getUrlFromDirectPath;
547
+ const downloadContentFromMessage = ({ mediaKey, directPath, url }, type, opts = {}) => {
548
+ const downloadUrl = url || (0, exports.getUrlFromDirectPath)(directPath);
549
+ const keys = getMediaKeys(mediaKey, type);
550
+ return (0, exports.downloadEncryptedContent)(downloadUrl, keys, opts);
426
551
  };
552
+ exports.downloadContentFromMessage = downloadContentFromMessage;
427
553
  /**
428
554
  * Decrypts and downloads an AES256-CBC encrypted file given the keys.
429
555
  * Assumes the SHA256 of the plaintext is appended to the end of the ciphertext
430
556
  * */
431
- export const downloadEncryptedContent = async (downloadUrl, { cipherKey, iv }, { startByte, endByte, options } = {}) => {
557
+ const downloadEncryptedContent = async (downloadUrl, { cipherKey, iv }, { startByte, endByte, options } = {}) => {
432
558
  let bytesFetched = 0;
433
559
  let startChunk = 0;
434
560
  let firstBlockIsIV = false;
@@ -442,14 +568,9 @@ export const downloadEncryptedContent = async (downloadUrl, { cipherKey, iv }, {
442
568
  }
443
569
  }
444
570
  const endChunk = endByte ? toSmallestChunkSize(endByte || 0) + AES_CHUNK_SIZE : undefined;
445
- const headersInit = options?.headers ? options.headers : undefined;
446
571
  const headers = {
447
- ...(headersInit
448
- ? Array.isArray(headersInit)
449
- ? Object.fromEntries(headersInit)
450
- : headersInit
451
- : {}),
452
- Origin: DEFAULT_ORIGIN
572
+ ...(options === null || options === void 0 ? void 0 : options.headers) || {},
573
+ Origin: Defaults_1.DEFAULT_ORIGIN,
453
574
  };
454
575
  if (startChunk || endChunk) {
455
576
  headers.Range = `bytes=${startChunk}-`;
@@ -458,9 +579,11 @@ export const downloadEncryptedContent = async (downloadUrl, { cipherKey, iv }, {
458
579
  }
459
580
  }
460
581
  // download the message
461
- const fetched = await getHttpStream(downloadUrl, {
462
- ...(options || {}),
463
- headers
582
+ const fetched = await (0, exports.getHttpStream)(downloadUrl, {
583
+ ...options || {},
584
+ headers,
585
+ maxBodyLength: Infinity,
586
+ maxContentLength: Infinity,
464
587
  });
465
588
  let remainingBytes = Buffer.from([]);
466
589
  let aes;
@@ -475,9 +598,9 @@ export const downloadEncryptedContent = async (downloadUrl, { cipherKey, iv }, {
475
598
  push(bytes);
476
599
  }
477
600
  };
478
- const output = new Transform({
601
+ const output = new stream_1.Transform({
479
602
  transform(chunk, _, callback) {
480
- let data = remainingBytes.length ? Buffer.concat([remainingBytes, chunk]) : chunk;
603
+ let data = Buffer.concat([remainingBytes, chunk]);
481
604
  const decryptLength = toSmallestChunkSize(data.length);
482
605
  remainingBytes = data.slice(decryptLength);
483
606
  data = data.slice(0, decryptLength);
@@ -510,15 +633,18 @@ export const downloadEncryptedContent = async (downloadUrl, { cipherKey, iv }, {
510
633
  catch (error) {
511
634
  callback(error);
512
635
  }
513
- }
636
+ },
514
637
  });
515
638
  return fetched.pipe(output, { end: true });
516
639
  };
517
- export function extensionForMediaMessage(message) {
518
- const getExtension = (mimetype) => mimetype.split(';')[0]?.split('/')[1];
640
+ exports.downloadEncryptedContent = downloadEncryptedContent;
641
+ function extensionForMediaMessage(message) {
642
+ const getExtension = (mimetype) => mimetype.split(';')[0].split('/')[1];
519
643
  const type = Object.keys(message)[0];
520
644
  let extension;
521
- if (type === 'locationMessage' || type === 'liveLocationMessage' || type === 'productMessage') {
645
+ if (type === 'locationMessage' ||
646
+ type === 'liveLocationMessage' ||
647
+ type === 'productMessage') {
522
648
  extension = '.jpeg';
523
649
  }
524
650
  else {
@@ -527,158 +653,55 @@ export function extensionForMediaMessage(message) {
527
653
  }
528
654
  return extension;
529
655
  }
530
- const isNodeRuntime = () => {
531
- return (typeof process !== 'undefined' &&
532
- process.versions?.node !== null &&
533
- typeof process.versions.bun === 'undefined' &&
534
- typeof globalThis.Deno === 'undefined');
535
- };
536
- export const uploadWithNodeHttp = async ({ url, filePath, headers, timeoutMs, agent }, redirectCount = 0) => {
537
- if (redirectCount > 5) {
538
- throw new Error('Too many redirects');
539
- }
540
- const parsedUrl = new URL(url);
541
- const httpModule = parsedUrl.protocol === 'https:' ? await import('https') : await import('http');
542
- // Get file size for Content-Length header (required for Node.js streaming)
543
- const fileStats = await fs.stat(filePath);
544
- const fileSize = fileStats.size;
545
- return new Promise((resolve, reject) => {
546
- const req = httpModule.request({
547
- hostname: parsedUrl.hostname,
548
- port: parsedUrl.port || (parsedUrl.protocol === 'https:' ? 443 : 80),
549
- path: parsedUrl.pathname + parsedUrl.search,
550
- method: 'POST',
551
- headers: {
552
- ...headers,
553
- 'Content-Length': fileSize
554
- },
555
- agent,
556
- timeout: timeoutMs
557
- }, res => {
558
- // Handle redirects (3xx)
559
- if (res.statusCode && res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
560
- res.resume(); // Consume response to free resources
561
- const newUrl = new URL(res.headers.location, url).toString();
562
- resolve(uploadWithNodeHttp({
563
- url: newUrl,
564
- filePath,
565
- headers,
566
- timeoutMs,
567
- agent
568
- }, redirectCount + 1));
569
- return;
570
- }
571
- let body = '';
572
- res.on('data', chunk => (body += chunk));
573
- res.on('end', () => {
574
- try {
575
- resolve(JSON.parse(body));
576
- }
577
- catch {
578
- resolve(undefined);
579
- }
580
- });
581
- });
582
- req.on('error', reject);
583
- req.on('timeout', () => {
584
- req.destroy();
585
- reject(new Error('Upload timeout'));
586
- });
587
- const stream = createReadStream(filePath);
588
- stream.pipe(req);
589
- stream.on('error', err => {
590
- req.destroy();
591
- reject(err);
592
- });
593
- });
594
- };
595
- const uploadWithFetch = async ({ url, filePath, headers, timeoutMs, agent }) => {
596
- // Convert Node.js Readable to Web ReadableStream
597
- const nodeStream = createReadStream(filePath);
598
- const webStream = Readable.toWeb(nodeStream);
599
- // Native fetch only accepts Undici-style dispatchers, not generic https Agents.
600
- const dispatcher = typeof agent?.dispatch === 'function' ? agent : undefined;
601
- const response = await fetch(url, {
602
- ...(dispatcher ? { dispatcher } : {}),
603
- method: 'POST',
604
- body: webStream,
605
- headers,
606
- duplex: 'half',
607
- signal: timeoutMs ? AbortSignal.timeout(timeoutMs) : undefined
608
- });
609
- try {
610
- return (await response.json());
611
- }
612
- catch {
613
- return undefined;
614
- }
615
- };
616
- /**
617
- * Uploads media to WhatsApp servers.
618
- *
619
- * ## Why we have two upload implementations:
620
- *
621
- * Node.js's native `fetch` (powered by undici) has a known bug where it buffers
622
- * the entire request body in memory before sending, even when using streams.
623
- * This causes memory issues with large files (e.g., 1GB file = 1GB+ memory usage).
624
- * See: https://github.com/nodejs/undici/issues/4058
625
- *
626
- * Other runtimes (Bun, Deno, browsers) correctly stream the request body without
627
- * buffering, so we can use the web-standard Fetch API there.
628
- *
629
- * ## Future considerations:
630
- * Once the undici bug is fixed, we can simplify this to use only the Fetch API
631
- * across all runtimes. Monitor the GitHub issue for updates.
632
- */
633
- const uploadMedia = async (params, logger) => {
634
- if (isNodeRuntime()) {
635
- logger?.debug('Using Node.js https module for upload (avoids undici buffering bug)');
636
- return uploadWithNodeHttp(params);
637
- }
638
- else {
639
- logger?.debug('Using web-standard Fetch API for upload');
640
- return uploadWithFetch(params);
641
- }
642
- };
643
- export const getWAUploadToServer = ({ customUploadHosts, fetchAgent, logger, options }, refreshMediaConn) => {
644
- return async (filePath, { mediaType, fileEncSha256B64, timeoutMs }) => {
656
+ exports.extensionForMediaMessage = extensionForMediaMessage;
657
+ const getWAUploadToServer = ({ customUploadHosts, fetchAgent, logger, options }, refreshMediaConn) => {
658
+ return async (stream, { mediaType, fileEncSha256B64, newsletter, timeoutMs }) => {
659
+ var _a, _b;
660
+ const { default: axios } = await import('axios');
645
661
  // send a query JSON to obtain the url & auth token to upload our media
646
662
  let uploadInfo = await refreshMediaConn(false);
647
663
  let urls;
648
664
  const hosts = [...customUploadHosts, ...uploadInfo.hosts];
649
- fileEncSha256B64 = encodeBase64EncodedStringForUpload(fileEncSha256B64);
650
- // Prepare common headers
651
- const customHeaders = (() => {
652
- const hdrs = options?.headers;
653
- if (!hdrs)
654
- return {};
655
- return Array.isArray(hdrs) ? Object.fromEntries(hdrs) : hdrs;
656
- })();
657
- const headers = {
658
- ...customHeaders,
659
- 'Content-Type': 'application/octet-stream',
660
- Origin: DEFAULT_ORIGIN
661
- };
662
- for (const { hostname } of hosts) {
665
+ const chunks = [];
666
+ if (!Buffer.isBuffer(stream)) {
667
+ for await (const chunk of stream) {
668
+ chunks.push(chunk);
669
+ }
670
+ }
671
+ const reqBody = Buffer.isBuffer(stream) ? stream : Buffer.concat(chunks);
672
+ fileEncSha256B64 = (0, exports.encodeBase64EncodedStringForUpload)(fileEncSha256B64);
673
+ let media = Defaults_1.MEDIA_PATH_MAP[mediaType];
674
+ if (newsletter) {
675
+ media = media === null || media === void 0 ? void 0 : media.replace('/mms/', '/newsletter/newsletter-');
676
+ }
677
+ for (const { hostname, maxContentLengthBytes } of hosts) {
663
678
  logger.debug(`uploading to "${hostname}"`);
664
- const auth = encodeURIComponent(uploadInfo.auth);
665
- const url = `https://${hostname}${MEDIA_PATH_MAP[mediaType]}/${fileEncSha256B64}?auth=${auth}&token=${fileEncSha256B64}`;
679
+ const auth = encodeURIComponent(uploadInfo.auth); // the auth token
680
+ const url = `https://${hostname}${media}/${fileEncSha256B64}?auth=${auth}&token=${fileEncSha256B64}`;
666
681
  let result;
667
682
  try {
668
- result = await uploadMedia({
669
- url,
670
- filePath,
671
- headers,
672
- timeoutMs,
673
- agent: fetchAgent
674
- }, logger);
675
- if (result?.url || result?.direct_path) {
683
+ if (maxContentLengthBytes && reqBody.length > maxContentLengthBytes) {
684
+ throw new boom_1.Boom(`Body too large for "${hostname}"`, { statusCode: 413 });
685
+ }
686
+ const body = await axios.post(url, reqBody, {
687
+ ...options,
688
+ headers: {
689
+ ...options.headers || {},
690
+ 'Content-Type': 'application/octet-stream',
691
+ 'Origin': Defaults_1.DEFAULT_ORIGIN
692
+ },
693
+ httpsAgent: fetchAgent,
694
+ timeout: timeoutMs,
695
+ responseType: 'json',
696
+ maxBodyLength: Infinity,
697
+ maxContentLength: Infinity,
698
+ });
699
+ result = body.data;
700
+ if ((result === null || result === void 0 ? void 0 : result.url) || (result === null || result === void 0 ? void 0 : result.directPath)) {
676
701
  urls = {
677
702
  mediaUrl: result.url,
678
703
  directPath: result.direct_path,
679
- meta_hmac: result.meta_hmac,
680
- fbid: result.fbid,
681
- ts: result.ts
704
+ handle: result.handle
682
705
  };
683
706
  break;
684
707
  }
@@ -688,33 +711,37 @@ export const getWAUploadToServer = ({ customUploadHosts, fetchAgent, logger, opt
688
711
  }
689
712
  }
690
713
  catch (error) {
691
- const isLast = hostname === hosts[uploadInfo.hosts.length - 1]?.hostname;
692
- logger.warn({ trace: error?.stack, uploadResult: result }, `Error in uploading to ${hostname} ${isLast ? '' : ', retrying...'}`);
714
+ if (axios.isAxiosError(error)) {
715
+ result = (_a = error.response) === null || _a === void 0 ? void 0 : _a.data;
716
+ }
717
+ const isLast = hostname === ((_b = hosts[uploadInfo.hosts.length - 1]) === null || _b === void 0 ? void 0 : _b.hostname);
718
+ logger.warn({ trace: error.stack, uploadResult: result }, `Error in uploading to ${hostname} ${isLast ? '' : ', retrying...'}`);
693
719
  }
694
720
  }
695
721
  if (!urls) {
696
- throw new Boom('Media upload failed on all hosts', { statusCode: 500 });
722
+ throw new boom_1.Boom('Media upload failed on all hosts', { statusCode: 500 });
697
723
  }
698
724
  return urls;
699
725
  };
700
726
  };
727
+ exports.getWAUploadToServer = getWAUploadToServer;
701
728
  const getMediaRetryKey = (mediaKey) => {
702
- return hkdf(mediaKey, 32, { info: 'WhatsApp Media Retry Notification' });
729
+ return (0, crypto_1.hkdf)(mediaKey, 32, { info: 'WhatsApp Media Retry Notification' });
703
730
  };
704
731
  /**
705
732
  * Generate a binary node that will request the phone to re-upload the media & return the newly uploaded URL
706
733
  */
707
- export const encryptMediaRetryRequest = (key, mediaKey, meId) => {
734
+ const encryptMediaRetryRequest = (key, mediaKey, meId) => {
708
735
  const recp = { stanzaId: key.id };
709
- const recpBuffer = proto.ServerErrorReceipt.encode(recp).finish();
736
+ const recpBuffer = WAProto_1.proto.ServerErrorReceipt.encode(recp).finish();
710
737
  const iv = Crypto.randomBytes(12);
711
738
  const retryKey = getMediaRetryKey(mediaKey);
712
- const ciphertext = aesEncryptGCM(recpBuffer, retryKey, iv, Buffer.from(key.id));
739
+ const ciphertext = (0, crypto_1.aesEncryptGCM)(recpBuffer, retryKey, iv, Buffer.from(key.id));
713
740
  const req = {
714
741
  tag: 'receipt',
715
742
  attrs: {
716
743
  id: key.id,
717
- to: jidNormalizedUser(meId),
744
+ to: (0, WABinary_1.jidNormalizedUser)(meId),
718
745
  type: 'server-error'
719
746
  },
720
747
  content: [
@@ -733,7 +760,7 @@ export const encryptMediaRetryRequest = (key, mediaKey, meId) => {
733
760
  tag: 'rmr',
734
761
  attrs: {
735
762
  jid: key.remoteJid,
736
- from_me: (!!key.fromMe).toString(),
763
+ 'from_me': (!!key.fromMe).toString(),
737
764
  // @ts-ignore
738
765
  participant: key.participant || undefined
739
766
  }
@@ -742,8 +769,9 @@ export const encryptMediaRetryRequest = (key, mediaKey, meId) => {
742
769
  };
743
770
  return req;
744
771
  };
745
- export const decodeMediaRetryNode = (node) => {
746
- const rmrNode = getBinaryNodeChild(node, 'rmr');
772
+ exports.encryptMediaRetryRequest = encryptMediaRetryRequest;
773
+ const decodeMediaRetryNode = (node) => {
774
+ const rmrNode = (0, WABinary_1.getBinaryNodeChild)(node, 'rmr');
747
775
  const event = {
748
776
  key: {
749
777
  id: node.attrs.id,
@@ -752,37 +780,40 @@ export const decodeMediaRetryNode = (node) => {
752
780
  participant: rmrNode.attrs.participant
753
781
  }
754
782
  };
755
- const errorNode = getBinaryNodeChild(node, 'error');
783
+ const errorNode = (0, WABinary_1.getBinaryNodeChild)(node, 'error');
756
784
  if (errorNode) {
757
785
  const errorCode = +errorNode.attrs.code;
758
- event.error = new Boom(`Failed to re-upload media (${errorCode})`, {
759
- data: errorNode.attrs,
760
- statusCode: getStatusCodeForMediaRetry(errorCode)
761
- });
786
+ event.error = new boom_1.Boom(`Failed to re-upload media (${errorCode})`, { data: errorNode.attrs, statusCode: (0, exports.getStatusCodeForMediaRetry)(errorCode) });
762
787
  }
763
788
  else {
764
- const encryptedInfoNode = getBinaryNodeChild(node, 'encrypt');
765
- const ciphertext = getBinaryNodeChildBuffer(encryptedInfoNode, 'enc_p');
766
- const iv = getBinaryNodeChildBuffer(encryptedInfoNode, 'enc_iv');
789
+ const encryptedInfoNode = (0, WABinary_1.getBinaryNodeChild)(node, 'encrypt');
790
+ const ciphertext = (0, WABinary_1.getBinaryNodeChildBuffer)(encryptedInfoNode, 'enc_p');
791
+ const iv = (0, WABinary_1.getBinaryNodeChildBuffer)(encryptedInfoNode, 'enc_iv');
767
792
  if (ciphertext && iv) {
768
793
  event.media = { ciphertext, iv };
769
794
  }
770
795
  else {
771
- event.error = new Boom('Failed to re-upload media (missing ciphertext)', { statusCode: 404 });
796
+ event.error = new boom_1.Boom('Failed to re-upload media (missing ciphertext)', { statusCode: 404 });
772
797
  }
773
798
  }
774
799
  return event;
775
800
  };
776
- export const decryptMediaRetryData = ({ ciphertext, iv }, mediaKey, msgId) => {
801
+ exports.decodeMediaRetryNode = decodeMediaRetryNode;
802
+ const decryptMediaRetryData = ({ ciphertext, iv }, mediaKey, msgId) => {
777
803
  const retryKey = getMediaRetryKey(mediaKey);
778
- const plaintext = aesDecryptGCM(ciphertext, retryKey, iv, Buffer.from(msgId));
779
- return proto.MediaRetryNotification.decode(plaintext);
804
+ const plaintext = (0, crypto_1.aesDecryptGCM)(ciphertext, retryKey, iv, Buffer.from(msgId));
805
+ return WAProto_1.proto.MediaRetryNotification.decode(plaintext);
780
806
  };
781
- export const getStatusCodeForMediaRetry = (code) => MEDIA_RETRY_STATUS_MAP[code];
807
+ exports.decryptMediaRetryData = decryptMediaRetryData;
808
+ const getStatusCodeForMediaRetry = (code) => MEDIA_RETRY_STATUS_MAP[code];
809
+ exports.getStatusCodeForMediaRetry = getStatusCodeForMediaRetry;
782
810
  const MEDIA_RETRY_STATUS_MAP = {
783
- [proto.MediaRetryNotification.ResultType.SUCCESS]: 200,
784
- [proto.MediaRetryNotification.ResultType.DECRYPTION_ERROR]: 412,
785
- [proto.MediaRetryNotification.ResultType.NOT_FOUND]: 404,
786
- [proto.MediaRetryNotification.ResultType.GENERAL_ERROR]: 418
811
+ [WAProto_1.proto.MediaRetryNotification.ResultType.SUCCESS]: 200,
812
+ [WAProto_1.proto.MediaRetryNotification.ResultType.DECRYPTION_ERROR]: 412,
813
+ [WAProto_1.proto.MediaRetryNotification.ResultType.NOT_FOUND]: 404,
814
+ [WAProto_1.proto.MediaRetryNotification.ResultType.GENERAL_ERROR]: 418,
787
815
  };
788
- //# sourceMappingURL=messages-media.js.map
816
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
817
+ function __importStar(arg0) {
818
+ throw new Error('Function not implemented.');
819
+ }