@ubedsockets/baileys 1.6.6 → 1.6.8

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 (87) hide show
  1. package/WAProto/GenerateStatics.sh +4 -3
  2. package/WAProto/WAProto.proto +511 -1215
  3. package/WAProto/index.js +131318 -59331
  4. package/lib/Defaults/index.js +92 -76
  5. package/lib/Signal/Group/ciphertext-message.js +5 -2
  6. package/lib/Signal/Group/group-session-builder.js +41 -7
  7. package/lib/Signal/Group/group_cipher.js +51 -37
  8. package/lib/Signal/Group/index.js +57 -12
  9. package/lib/Signal/Group/keyhelper.js +44 -7
  10. package/lib/Signal/Group/sender-chain-key.js +15 -7
  11. package/lib/Signal/Group/sender-key-distribution-message.js +11 -8
  12. package/lib/Signal/Group/sender-key-message.js +12 -9
  13. package/lib/Signal/Group/sender-key-name.js +5 -2
  14. package/lib/Signal/Group/sender-key-record.js +21 -9
  15. package/lib/Signal/Group/sender-key-state.js +42 -27
  16. package/lib/Signal/Group/sender-message-key.js +7 -4
  17. package/lib/Signal/libsignal.js +90 -347
  18. package/lib/Socket/Client/index.js +18 -3
  19. package/lib/Socket/Client/types.js +6 -4
  20. package/lib/Socket/Client/websocket.js +78 -21
  21. package/lib/Socket/business.js +43 -162
  22. package/lib/Socket/chats.js +249 -571
  23. package/lib/Socket/groups.js +99 -114
  24. package/lib/Socket/index.js +9 -11
  25. package/lib/Socket/messages-recv.js +559 -1277
  26. package/lib/Socket/messages-send.js +669 -674
  27. package/lib/Socket/newsletter.js +286 -149
  28. package/lib/Socket/socket.js +235 -548
  29. package/lib/Types/Auth.js +2 -2
  30. package/lib/Types/Call.js +2 -2
  31. package/lib/Types/Chat.js +4 -8
  32. package/lib/Types/Contact.js +2 -2
  33. package/lib/Types/Events.js +2 -2
  34. package/lib/Types/GroupMetadata.js +2 -2
  35. package/lib/Types/Label.js +5 -3
  36. package/lib/Types/LabelAssociation.js +5 -3
  37. package/lib/Types/Message.js +7 -11
  38. package/lib/Types/Product.js +2 -2
  39. package/lib/Types/Signal.js +2 -2
  40. package/lib/Types/Socket.js +2 -3
  41. package/lib/Types/State.js +2 -56
  42. package/lib/Types/USync.js +2 -2
  43. package/lib/Types/index.js +31 -15
  44. package/lib/Utils/auth-utils.js +136 -239
  45. package/lib/Utils/business.js +69 -66
  46. package/lib/Utils/chat-utils.js +238 -380
  47. package/lib/Utils/crypto.js +127 -56
  48. package/lib/Utils/decode-wa-message.js +63 -173
  49. package/lib/Utils/event-buffer.js +70 -174
  50. package/lib/Utils/generics.js +207 -159
  51. package/lib/Utils/history.js +53 -93
  52. package/lib/Utils/index.js +33 -22
  53. package/lib/Utils/link-preview.js +58 -17
  54. package/lib/Utils/logger.js +7 -3
  55. package/lib/Utils/lt-hash.js +46 -3
  56. package/lib/Utils/make-mutex.js +34 -24
  57. package/lib/Utils/messages-media.js +744 -483
  58. package/lib/Utils/messages.js +621 -411
  59. package/lib/Utils/noise-handler.js +99 -149
  60. package/lib/Utils/process-message.js +150 -459
  61. package/lib/Utils/signal.js +73 -112
  62. package/lib/Utils/use-multi-file-auth-state.js +27 -23
  63. package/lib/Utils/validate-connection.js +104 -74
  64. package/lib/WABinary/constants.js +11 -9
  65. package/lib/WABinary/decode.js +52 -49
  66. package/lib/WABinary/encode.js +49 -19
  67. package/lib/WABinary/generic-utils.js +43 -46
  68. package/lib/WABinary/index.js +21 -6
  69. package/lib/WABinary/jid-utils.js +45 -72
  70. package/lib/WABinary/types.js +2 -2
  71. package/lib/WAM/BinaryInfo.js +5 -2
  72. package/lib/WAM/constants.js +11958 -19461
  73. package/lib/WAM/encode.js +22 -17
  74. package/lib/WAM/index.js +19 -4
  75. package/lib/WAUSync/Protocols/USyncContactProtocol.js +13 -33
  76. package/lib/WAUSync/Protocols/USyncDeviceProtocol.js +14 -11
  77. package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.js +11 -8
  78. package/lib/WAUSync/Protocols/USyncStatusProtocol.js +13 -9
  79. package/lib/WAUSync/Protocols/UsyncBotProfileProtocol.js +22 -20
  80. package/lib/WAUSync/Protocols/UsyncLIDProtocol.js +8 -13
  81. package/lib/WAUSync/Protocols/index.js +20 -6
  82. package/lib/WAUSync/USyncQuery.js +35 -44
  83. package/lib/WAUSync/USyncUser.js +5 -10
  84. package/lib/WAUSync/index.js +19 -4
  85. package/lib/index.js +70 -11
  86. package/package.json +60 -100
  87. package/engine-requirements.js +0 -10
@@ -1,244 +1,549 @@
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 () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ var __importDefault = (this && this.__importDefault) || function (mod) {
36
+ return (mod && mod.__esModule) ? mod : { "default": mod };
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.getStatusCodeForMediaRetry =
40
+ exports.decryptMediaRetryData =
41
+ exports.decodeMediaRetryNode =
42
+ exports.encryptMediaRetryRequest =
43
+ exports.getWAUploadToServer =
44
+ exports.downloadEncryptedContent =
45
+ exports.downloadContentFromMessage =
46
+ exports.getUrlFromDirectPath =
47
+ exports.encryptedStream =
48
+ exports.prepareStream =
49
+ exports.getHttpStream =
50
+ exports.getStream =
51
+ exports.toBuffer =
52
+ exports.toReadable =
53
+ exports.mediaMessageSHA256B64 =
54
+ exports.generateProfilePicture =
55
+ exports.encodeBase64EncodedStringForUpload =
56
+ exports.extractImageThumb =
57
+ exports.extractVideoThumb =
58
+ exports.hkdfInfoKey = void 0;
59
+ exports.getMediaKeys = getMediaKeys;
60
+ exports.uploadFile = uploadFile;
61
+ exports.vid2jpg = vid2jpg;
62
+ exports.getAudioDuration = getAudioDuration;
63
+ exports.getAudioWaveform = getAudioWaveform;
64
+ exports.generateThumbnail = generateThumbnail;
65
+ exports.extensionForMediaMessage = extensionForMediaMessage;
66
+ // 🆅🆈🆉🅴🅽 🅱🅰🅸🅻
67
+ const boom_1 = require("@hapi/boom");
68
+ const axios_1 = __importDefault(require("axios"));
69
+ const form_data_1 = __importDefault(require("form-data"));
70
+ const cheerio = __importStar(require("cheerio"));
71
+ const Crypto = __importStar(require("crypto"));
72
+ const events_1 = require("events");
73
+ const fs_1 = require("fs");
74
+ const os_1 = require("os");
75
+ const path_1 = require("path");
76
+ const jimp_1 = __importDefault(require("jimp"));
77
+ const stream_1 = require("stream");
78
+ const child_process_1 = require("child_process");
79
+ const WAProto_1 = require("../../WAProto");
80
+ const Defaults_1 = require("../Defaults");
81
+ const WABinary_1 = require("../WABinary");
82
+ const crypto_1 = require("./crypto");
83
+ const generics_1 = require("./generics");
84
+ const getTmpFilesDirectory = () => (0, os_1.tmpdir)();
16
85
  const getImageProcessingLibrary = async () => {
17
- //@ts-ignore
18
- const [jimp, sharp] = await Promise.all([import('jimp').catch(() => { }), import('sharp').catch(() => { })]);
86
+ const [_jimp, sharp] = await Promise.all([
87
+ (async () => {
88
+ const jimp = await (Promise.resolve().then(() => __importStar(require('jimp'))).catch(() => { }));
89
+ return jimp;
90
+ })(),
91
+ (async () => {
92
+ const sharp = await (Promise.resolve().then(() => __importStar(require('sharp'))).catch(() => { }));
93
+ return sharp;
94
+ })()
95
+ ]);
19
96
  if (sharp) {
20
97
  return { sharp };
21
98
  }
99
+ const jimp = (_jimp === null || _jimp === void 0 ? void 0 : _jimp.default) || _jimp;
22
100
  if (jimp) {
23
101
  return { jimp };
24
102
  }
25
- throw new Boom('No image processing library available');
103
+ throw new boom_1.Boom('No image processing library available');
26
104
  };
27
- export const hkdfInfoKey = (type) => {
28
- const hkdfInfo = MEDIA_HKDF_KEY_MAPPING[type];
105
+ const hkdfInfoKey = (type) => {
106
+ const hkdfInfo = Defaults_1.MEDIA_HKDF_KEY_MAPPING[type];
29
107
  return `WhatsApp ${hkdfInfo} Keys`;
30
108
  };
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
- };
109
+ exports.hkdfInfoKey = hkdfInfoKey;
69
110
  /** generates all the keys required to encrypt/decrypt & sign a media message */
70
- export async function getMediaKeys(buffer, mediaType) {
111
+ async function getMediaKeys(buffer, mediaType) {
71
112
  if (!buffer) {
72
- throw new Boom('Cannot derive from empty media key');
113
+ throw new boom_1.Boom('Cannot derive from empty media key');
73
114
  }
74
115
  if (typeof buffer === 'string') {
75
116
  buffer = Buffer.from(buffer.replace('data:;base64,', ''), 'base64');
76
117
  }
77
118
  // expand using HKDF to 112 bytes, also pass in the relevant app info
78
- const expandedMediaKey = hkdf(buffer, 112, { info: hkdfInfoKey(mediaType) });
119
+ const expandedMediaKey = await (0, crypto_1.hkdf)(buffer, 112, { info: (0, exports.hkdfInfoKey)(mediaType) });
79
120
  return {
80
121
  iv: expandedMediaKey.slice(0, 16),
81
122
  cipherKey: expandedMediaKey.slice(16, 48),
82
- macKey: expandedMediaKey.slice(48, 80)
123
+ macKey: expandedMediaKey.slice(48, 80),
83
124
  };
84
125
  }
85
- /** Extracts video thumb using FFMPEG */
86
- const extractVideoThumb = async (path, destPath, time, size) => new Promise((resolve, reject) => {
87
- const cmd = `ffmpeg -ss ${time} -i ${path} -y -vf scale=${size.width}:-1 -vframes 1 -f image2 ${destPath}`;
88
- exec(cmd, err => {
89
- if (err) {
90
- reject(err);
126
+ async function uploadFile(buffer, logger) {
127
+ const { fromBuffer } = await Promise.resolve().then(() => __importStar(require('file-type')));
128
+ const fileType = await fromBuffer(buffer);
129
+ if (!fileType)
130
+ throw new Error("Failed to detect file type.");
131
+ const { ext, mime } = fileType;
132
+ const services = [
133
+ {
134
+ name: "catbox",
135
+ url: "https://catbox.moe/user/api.php",
136
+ buildForm: () => {
137
+ const form = new form_data_1.default();
138
+ form.append("fileToUpload", buffer, {
139
+ filename: `file.${ext}`,
140
+ contentType: mime || "application/octet-stream"
141
+ });
142
+ form.append("reqtype", "fileupload");
143
+ return form;
144
+ },
145
+ parseResponse: res => res.data
146
+ },
147
+ {
148
+ name: "pdi.moe",
149
+ url: "https://scdn.pdi.moe/upload",
150
+ buildForm: () => {
151
+ const form = new form_data_1.default();
152
+ form.append("file", buffer, {
153
+ filename: `file.${ext}`,
154
+ contentType: mime
155
+ });
156
+ return form;
157
+ },
158
+ parseResponse: res => res.data.result.url
159
+ },
160
+ {
161
+ name: "qu.ax",
162
+ url: "https://qu.ax/upload.php",
163
+ buildForm: () => {
164
+ const form = new form_data_1.default();
165
+ form.append("files[]", buffer, {
166
+ filename: `file.${ext}`,
167
+ contentType: mime || "application/octet-stream"
168
+ });
169
+ return form;
170
+ },
171
+ parseResponse: res => {
172
+ var _a, _b, _c;
173
+ if (!((_c = (_b = (_a = res.data) === null || _a === void 0 ? void 0 : _a.files) === null || _b === void 0 ? void 0 : _b[0]) === null || _c === void 0 ? void 0 : _c.url))
174
+ throw new Error("Failed to get URL from qu.ax");
175
+ return res.data.files[0].url;
176
+ }
177
+ },
178
+ {
179
+ name: "uguu.se",
180
+ url: "https://uguu.se/upload.php",
181
+ buildForm: () => {
182
+ const form = new form_data_1.default();
183
+ form.append("files[]", buffer, {
184
+ filename: `file.${ext}`,
185
+ contentType: mime || "application/octet-stream"
186
+ });
187
+ return form;
188
+ },
189
+ parseResponse: res => {
190
+ var _a, _b, _c;
191
+ if (!((_c = (_b = (_a = res.data) === null || _a === void 0 ? void 0 : _a.files) === null || _b === void 0 ? void 0 : _b[0]) === null || _c === void 0 ? void 0 : _c.url))
192
+ throw new Error("Failed to get URL from uguu.se");
193
+ return res.data.files[0].url;
194
+ }
195
+ },
196
+ {
197
+ name: "tmpfiles",
198
+ url: "https://tmpfiles.org/api/v1/upload",
199
+ buildForm: () => {
200
+ const form = new form_data_1.default();
201
+ form.append("file", buffer, {
202
+ filename: `file.${ext}`,
203
+ contentType: mime
204
+ });
205
+ return form;
206
+ },
207
+ parseResponse: res => {
208
+ const match = res.data.data.url.match(/https:\/\/tmpfiles\.org\/(.*)/);
209
+ if (!match)
210
+ throw new Error("Failed to parse tmpfiles URL.");
211
+ return `https://tmpfiles.org/dl/${match[1]}`;
212
+ }
91
213
  }
92
- else {
93
- resolve();
214
+ ];
215
+ for (const service of services) {
216
+ try {
217
+ const form = service.buildForm();
218
+ const res = await axios_1.default.post(service.url, form, {
219
+ headers: form.getHeaders()
220
+ });
221
+ const url = service.parseResponse(res);
222
+ return url;
223
+ }
224
+ catch (error) {
225
+ logger === null || logger === void 0 ? void 0 : logger.debug(`[${service.name}] eror:`, (error === null || error === void 0 ? void 0 : error.message) || error);
226
+ }
227
+ }
228
+ throw new Error("All upload services failed.");
229
+ }
230
+ async function vid2jpg(videoUrl) {
231
+ try {
232
+ const { data } = await axios_1.default.get(`https://ezgif.com/video-to-jpg?url=${encodeURIComponent(videoUrl)}`);
233
+ const $ = cheerio.load(data);
234
+ const fileToken = $('input[name="file"]').attr("value");
235
+ if (!fileToken) {
236
+ throw new Error("Failed to retrieve file token. The video URL may be invalid or inaccessible.");
237
+ }
238
+ const formData = new URLSearchParams();
239
+ formData.append("file", fileToken);
240
+ formData.append("end", "1");
241
+ formData.append("video-to-jpg", "Convert to JPG!");
242
+ const convert = await axios_1.default.post(`https://ezgif.com/video-to-jpg/${fileToken}`, formData);
243
+ const $2 = cheerio.load(convert.data);
244
+ let imageUrl = $2("#output img").first().attr("src");
245
+ if (!imageUrl) {
246
+ throw new Error("Could not locate the converted image output.");
247
+ }
248
+ if (imageUrl.startsWith("//")) {
249
+ imageUrl = "https:" + imageUrl;
250
+ }
251
+ else if (imageUrl.startsWith("/")) {
252
+ const cdnMatch = imageUrl.match(/\/(s\d+\..+?)\/.*/);
253
+ if (cdnMatch) {
254
+ imageUrl = "https://" + imageUrl.slice(2);
255
+ }
256
+ else {
257
+ imageUrl = "https://ezgif.com" + imageUrl;
258
+ }
94
259
  }
260
+ return imageUrl;
261
+ }
262
+ catch (error) {
263
+ throw new Error("Failed to convert video to JPG: " + error.message);
264
+ }
265
+ }
266
+ /**
267
+ * Extracts video thumbnail using FFmpeg
268
+ */
269
+ const extractVideoThumb = async (videoPath, time = '00:00:00', size = { width: 256 }) => {
270
+ return new Promise((resolve, reject) => {
271
+ const args = [
272
+ '-ss', time,
273
+ '-i', videoPath,
274
+ '-y',
275
+ '-vf', `scale=${size.width}:-1`,
276
+ '-vframes', '1',
277
+ '-f', 'image2',
278
+ '-vcodec', 'mjpeg',
279
+ 'pipe:1'
280
+ ];
281
+ const ffmpeg = (0, child_process_1.spawn)('ffmpeg', args);
282
+ const chunks = [];
283
+ let errorOutput = '';
284
+ ffmpeg.stdout.on('data', chunk => chunks.push(chunk));
285
+ ffmpeg.stderr.on('data', data => {
286
+ errorOutput += data.toString();
287
+ });
288
+ ffmpeg.on('error', reject);
289
+ ffmpeg.on('close', code => {
290
+ if (code === 0) return resolve(Buffer.concat(chunks));
291
+ reject(new Error(`ffmpeg exited with code ${code}\n${errorOutput}`));
292
+ });
95
293
  });
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);
294
+ };
295
+ exports.extractVideoThumb = extractVideoThumb;
296
+ const extractImageThumb = async (bufferOrFilePath, width = 32) => {
297
+ var _a, _b;
298
+ if (bufferOrFilePath instanceof stream_1.Readable) {
299
+ bufferOrFilePath = await (0, exports.toBuffer)(bufferOrFilePath);
102
300
  }
103
301
  const lib = await getImageProcessingLibrary();
104
- if ('sharp' in lib && typeof lib.sharp?.default === 'function') {
302
+ if ('sharp' in lib && typeof ((_a = lib.sharp) === null || _a === void 0 ? void 0 : _a.default) === 'function') {
105
303
  const img = lib.sharp.default(bufferOrFilePath);
106
304
  const dimensions = await img.metadata();
107
- const buffer = await img.resize(width).jpeg({ quality: 50 }).toBuffer();
305
+ const buffer = await img
306
+ .resize(width)
307
+ .jpeg({ quality: 50 })
308
+ .toBuffer();
108
309
  return {
109
310
  buffer,
110
311
  original: {
111
312
  width: dimensions.width,
112
- height: dimensions.height
113
- }
313
+ height: dimensions.height,
314
+ },
114
315
  };
115
316
  }
116
- else if ('jimp' in lib && typeof lib.jimp?.Jimp === 'object') {
117
- const jimp = await lib.jimp.Jimp.read(bufferOrFilePath);
317
+ else if ('jimp' in lib && typeof ((_b = lib.jimp) === null || _b === void 0 ? void 0 : _b.read) === 'function') {
318
+ const { read, MIME_JPEG, RESIZE_BILINEAR, AUTO } = lib.jimp;
319
+ const jimp = await read(bufferOrFilePath);
118
320
  const dimensions = {
119
- width: jimp.width,
120
- height: jimp.height
321
+ width: jimp.getWidth(),
322
+ height: jimp.getHeight()
121
323
  };
122
324
  const buffer = await jimp
123
- .resize({ w: width, mode: lib.jimp.ResizeStrategy.BILINEAR })
124
- .getBuffer('image/jpeg', { quality: 50 });
325
+ .quality(50)
326
+ .resize(width, AUTO, RESIZE_BILINEAR)
327
+ .getBufferAsync(MIME_JPEG);
125
328
  return {
126
329
  buffer,
127
330
  original: dimensions
128
331
  };
129
332
  }
130
333
  else {
131
- throw new Boom('No image processing library available');
334
+ throw new boom_1.Boom('No image processing library available');
132
335
  }
133
336
  };
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
- if (Buffer.isBuffer(mediaUpload)) {
139
- buffer = mediaUpload;
140
- }
141
- 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);
146
- }
147
- const lib = await getImageProcessingLibrary();
337
+ exports.extractImageThumb = extractImageThumb;
338
+ const encodeBase64EncodedStringForUpload = (b64) => (encodeURIComponent(b64
339
+ .replace(/\+/g, '-')
340
+ .replace(/\//g, '_')
341
+ .replace(/\=+$/, '')));
342
+ exports.encodeBase64EncodedStringForUpload = encodeBase64EncodedStringForUpload;
343
+ const generateProfilePicture = async (mediaUpload) => {
344
+ let bufferOrFilePath;
148
345
  let img;
149
- if ('sharp' in lib && typeof lib.sharp?.default === 'function') {
150
- img = lib.sharp
151
- .default(buffer)
152
- .resize(w, h)
153
- .jpeg({
154
- quality: 50
155
- })
156
- .toBuffer();
346
+ if (Buffer.isBuffer(mediaUpload)) {
347
+ bufferOrFilePath = mediaUpload;
157
348
  }
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 });
349
+ else if ('url' in mediaUpload) {
350
+ bufferOrFilePath = mediaUpload.url.toString();
163
351
  }
164
352
  else {
165
- throw new Boom('No image processing library available');
353
+ bufferOrFilePath = await (0, exports.toBuffer)(mediaUpload.stream);
166
354
  }
355
+ const jimp = await jimp_1.default.read(bufferOrFilePath);
356
+ const cropped = jimp.getWidth() > jimp.getHeight() ? jimp.resize(550, -1) : jimp.resize(-1, 650);
357
+ img = cropped
358
+ .quality(100)
359
+ .getBufferAsync(jimp_1.default.MIME_JPEG);
167
360
  return {
168
- img: await img
361
+ img: await img,
169
362
  };
170
363
  };
364
+ exports.generateProfilePicture = generateProfilePicture;
171
365
  /** gets the SHA256 of the given media message */
172
- export const mediaMessageSHA256B64 = (message) => {
366
+ const mediaMessageSHA256B64 = (message) => {
173
367
  const media = Object.values(message)[0];
174
- return media?.fileSha256 && Buffer.from(media.fileSha256).toString('base64');
368
+ return (media === null || media === void 0 ? void 0 : media.fileSha256) && Buffer.from(media.fileSha256).toString('base64');
175
369
  };
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);
370
+ exports.mediaMessageSHA256B64 = mediaMessageSHA256B64;
371
+ async function getAudioDuration(buffer) {
372
+ try {
373
+ const { PassThrough } = require('stream');
374
+ const ff = require('fluent-ffmpeg');
375
+
376
+ return await new Promise((resolve, reject) => {
377
+ const inputStream = new PassThrough();
378
+ inputStream.end(buffer);
379
+
380
+ ff(inputStream)
381
+ .ffprobe((err, data) => {
382
+ if (err) reject(err);
383
+ else resolve(data.format.duration);
384
+ });
385
+ });
386
+ } catch (error) {
387
+ const musicMetadata = await import('music-metadata');
388
+ let metadata;
389
+ if (Buffer.isBuffer(buffer)) {
390
+ metadata = await musicMetadata.parseBuffer(buffer, undefined, {
391
+ duration: true
392
+ });
393
+ } else if (typeof buffer === 'string') {
394
+ const rStream = (0, fs_1.createReadStream)(buffer);
395
+ try {
396
+ metadata = await musicMetadata.parseStream(rStream, undefined, {
397
+ duration: true
398
+ });
399
+ } finally {
400
+ rStream.destroy();
401
+ }
402
+ } else {
403
+ metadata = await musicMetadata.parseStream(buffer, undefined, {
404
+ duration: true
405
+ });
406
+ }
407
+ return metadata.format.duration;
190
408
  }
191
- return metadata.format.duration;
192
409
  }
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) {
410
+ exports.getAudioDuration = getAudioDuration;
411
+ async function getAudioWaveform(buffer, logger) {
197
412
  try {
198
- // @ts-ignore
199
- const { default: decoder } = await import('audio-decode');
413
+ const { PassThrough } = require('stream');
414
+ const ff = require('fluent-ffmpeg');
415
+
200
416
  let audioData;
201
417
  if (Buffer.isBuffer(buffer)) {
202
418
  audioData = buffer;
419
+ } else if (typeof buffer === 'string') {
420
+ const rStream = require('fs').createReadStream(buffer);
421
+ audioData = await exports.toBuffer(rStream);
422
+ } else {
423
+ audioData = await exports.toBuffer(buffer);
203
424
  }
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;
425
+
426
+ return await new Promise((resolve, reject) => {
427
+ const inputStream = new PassThrough();
428
+ inputStream.end(audioData);
429
+ const chunks = [];
430
+ const bars = 64;
431
+
432
+ ff(inputStream)
433
+ .audioChannels(1)
434
+ .audioFrequency(16000)
435
+ .format('s16le')
436
+ .on('error', reject)
437
+ .on('end', () => {
438
+ const rawData = Buffer.concat(chunks);
439
+ const samples = rawData.length / 2;
440
+ const amplitudes = [];
441
+
442
+ for (let i = 0; i < samples; i++) {
443
+ amplitudes.push(Math.abs(rawData.readInt16LE(i * 2)) / 32768);
444
+ }
445
+
446
+ const blockSize = Math.floor(amplitudes.length / bars);
447
+ const avg = [];
448
+ for (let i = 0; i < bars; i++) {
449
+ const block = amplitudes.slice(i * blockSize, (i + 1) * blockSize);
450
+ avg.push(block.reduce((a, b) => a + b, 0) / block.length);
451
+ }
452
+
453
+ const max = Math.max(...avg);
454
+ const normalized = avg.map(v => Math.floor((v / max) * 100));
455
+ resolve(new Uint8Array(normalized));
456
+ })
457
+ .pipe()
458
+ .on('data', chunk => chunks.push(chunk));
459
+ });
460
+ } catch (e) {
461
+ logger?.debug(e);
462
+ }
463
+ }
464
+ exports.getAudioWaveform = getAudioWaveform;
465
+ async function convertToOpusBuffer(buffer, logger) {
466
+ try {
467
+ const { PassThrough } = require('stream');
468
+ const ff = require('fluent-ffmpeg');
469
+
470
+ return await new Promise((resolve, reject) => {
471
+ const inStream = new PassThrough();
472
+ const outStream = new PassThrough();
473
+ const chunks = [];
474
+ inStream.end(buffer);
475
+
476
+ ff(inStream)
477
+ .noVideo()
478
+ .audioCodec('libopus')
479
+ .format('ogg')
480
+ .audioBitrate('48k')
481
+ .audioChannels(1)
482
+ .audioFrequency(48000)
483
+ .outputOptions([
484
+ '-vn',
485
+ '-b:a 64k',
486
+ '-ac 2',
487
+ '-ar 48000',
488
+ '-map_metadata', '-1',
489
+ '-application', 'voip'
490
+ ])
491
+ .on('error', reject)
492
+ .on('end', () => resolve(Buffer.concat(chunks)))
493
+ .pipe(outStream, {
494
+ end: true
495
+ });
496
+ outStream.on('data', c => chunks.push(c));
497
+ });
498
+ } catch (e) {
499
+ logger?.debug(e);
500
+ throw e;
230
501
  }
231
- catch (e) {
232
- logger?.debug('Failed to generate waveform: ' + e);
502
+ }
503
+ exports.convertToOpusBuffer = convertToOpusBuffer;
504
+ async function convertToMp4Buffer(buffer, logger) {
505
+ try {
506
+ const { PassThrough } = require('stream');
507
+ const ff = require('fluent-ffmpeg');
508
+
509
+ return await new Promise((resolve, reject) => {
510
+ const inStream = new PassThrough();
511
+ const outStream = new PassThrough();
512
+ const chunks = [];
513
+
514
+ inStream.end(buffer);
515
+
516
+ ff(inStream)
517
+ .videoCodec('libx264')
518
+ .audioCodec('aac')
519
+ .format('mp4')
520
+ .outputOptions([
521
+ '-preset', 'veryfast',
522
+ '-crf', '23',
523
+ '-movflags', 'faststart',
524
+ '-map_metadata', '-1'
525
+ ])
526
+ .on('error', reject)
527
+ .on('end', () => resolve(Buffer.concat(chunks)))
528
+ .pipe(outStream, { end: true });
529
+
530
+ outStream.on('data', c => chunks.push(c));
531
+ });
532
+ } catch (e) {
533
+ logger?.debug(e);
534
+ throw e;
233
535
  }
234
536
  }
235
- export const toReadable = (buffer) => {
236
- const readable = new Readable({ read: () => { } });
537
+
538
+ exports.convertToMp4Buffer = convertToMp4Buffer;
539
+ const toReadable = (buffer) => {
540
+ const readable = new stream_1.Readable({ read: () => { } });
237
541
  readable.push(buffer);
238
542
  readable.push(null);
239
543
  return readable;
240
544
  };
241
- export const toBuffer = async (stream) => {
545
+ exports.toReadable = toReadable;
546
+ const toBuffer = async (stream) => {
242
547
  const chunks = [];
243
548
  for await (const chunk of stream) {
244
549
  chunks.push(chunk);
@@ -246,47 +551,61 @@ export const toBuffer = async (stream) => {
246
551
  stream.destroy();
247
552
  return Buffer.concat(chunks);
248
553
  };
249
- export const getStream = async (item, opts) => {
554
+ exports.toBuffer = toBuffer;
555
+ const getStream = async (item, opts) => {
250
556
  if (Buffer.isBuffer(item)) {
251
- return { stream: toReadable(item), type: 'buffer' };
557
+ return { stream: (0, exports.toReadable)(item), type: 'buffer' };
252
558
  }
253
559
  if ('stream' in item) {
254
560
  return { stream: item.stream, type: 'readable' };
255
561
  }
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' };
562
+ if (item.url.toString().startsWith('http://') || item.url.toString().startsWith('https://')) {
563
+ return { stream: await (0, exports.getHttpStream)(item.url, opts), type: 'remote' };
260
564
  }
261
- if (urlStr.startsWith('http://') || urlStr.startsWith('https://')) {
262
- return { stream: await getHttpStream(item.url, opts), type: 'remote' };
263
- }
264
- return { stream: createReadStream(item.url), type: 'file' };
565
+ return { stream: (0, fs_1.createReadStream)(item.url), type: 'file' };
265
566
  };
567
+ exports.getStream = getStream;
266
568
  /** generates a thumbnail for a given media, if required */
267
- export async function generateThumbnail(file, mediaType, options) {
569
+ async function generateThumbnail(file, mediaType, options) {
570
+ var _a;
268
571
  let thumbnail;
269
572
  let originalImageDimensions;
270
573
  if (mediaType === 'image') {
271
- const { buffer, original } = await extractImageThumb(file);
574
+ const { buffer, original } = await (0, exports.extractImageThumb)(file);
272
575
  thumbnail = buffer.toString('base64');
273
576
  if (original.width && original.height) {
274
577
  originalImageDimensions = {
275
578
  width: original.width,
276
- height: original.height
579
+ height: original.height,
277
580
  };
278
581
  }
279
582
  }
280
583
  else if (mediaType === 'video') {
281
- const imgFilename = join(getTmpFilesDirectory(), generateMessageIDV2() + '.jpg');
282
584
  try {
283
- await extractVideoThumb(file, imgFilename, '00:00:00', { width: 32, height: 32 });
284
- const buff = await fs.readFile(imgFilename);
285
- thumbnail = buff.toString('base64');
286
- await fs.unlink(imgFilename);
585
+ let videoPath = file;
586
+ if (Buffer.isBuffer(file) || file instanceof stream_1.Readable) {
587
+ videoPath = (0, path_1.join)(getTmpFilesDirectory(), (0, generics_1.generateMessageIDV2)() + '.mp4');
588
+ const buffer = Buffer.isBuffer(file) ? file : await (0, exports.toBuffer)(file);
589
+ await fs_1.promises.writeFile(videoPath, buffer);
590
+ }
591
+ const thumbnailBuffer = await (0, exports.extractVideoThumb)(videoPath);
592
+ const imgFilename = (0, path_1.join)(getTmpFilesDirectory(), (0, generics_1.generateMessageIDV2)() + '.jpg');
593
+ await fs_1.promises.writeFile(imgFilename, thumbnailBuffer);
594
+ const { buffer: processedThumbnailBuffer, original } = await (0, exports.extractImageThumb)(imgFilename);
595
+ thumbnail = processedThumbnailBuffer.toString('base64');
596
+ if (original.width && original.height) {
597
+ originalImageDimensions = {
598
+ width: original.width,
599
+ height: original.height,
600
+ };
601
+ }
602
+ await fs_1.promises.unlink(imgFilename);
603
+ if (videoPath !== file) {
604
+ await fs_1.promises.unlink(videoPath);
605
+ }
287
606
  }
288
607
  catch (err) {
289
- options.logger?.debug('could not generate video thumb: ' + err);
608
+ (_a = options.logger) === null || _a === void 0 ? void 0 : _a.debug('could not generate video thumb: ' + err);
290
609
  }
291
610
  }
292
611
  return {
@@ -294,145 +613,196 @@ export async function generateThumbnail(file, mediaType, options) {
294
613
  originalImageDimensions
295
614
  };
296
615
  }
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 } });
616
+ const getHttpStream = async (url, options = {}) => {
617
+ const fetched = await axios_1.default.get(url.toString(), { ...options, responseType: 'stream' });
618
+ return fetched.data;
619
+ };
620
+ exports.getHttpStream = getHttpStream;
621
+ const prepareStream = async (media, mediaType, { logger, saveOriginalFileIfRequired, opts } = {}) => {
622
+ const { stream, type } = await (0, exports.getStream)(media, opts);
623
+ logger === null || logger === void 0 ? void 0 : logger.debug('fetched media stream');
624
+ let bodyPath;
625
+ let didSaveToTmpPath = false;
626
+ try {
627
+ const buffer = await (0, exports.toBuffer)(stream);
628
+ if (type === 'file') {
629
+ bodyPath = media.url;
630
+ }
631
+ else if (saveOriginalFileIfRequired) {
632
+ bodyPath = (0, path_1.join)(getTmpFilesDirectory(), mediaType + (0, generics_1.generateMessageIDV2)());
633
+ (0, fs_1.writeFileSync)(bodyPath, buffer);
634
+ didSaveToTmpPath = true;
635
+ }
636
+ const fileLength = buffer.length;
637
+ const fileSha256 = Crypto.createHash('sha256').update(buffer).digest();
638
+ stream === null || stream === void 0 ? void 0 : stream.destroy();
639
+ logger === null || logger === void 0 ? void 0 : logger.debug('prepare stream data successfully');
640
+ return {
641
+ mediaKey: undefined,
642
+ encWriteStream: buffer,
643
+ fileLength,
644
+ fileSha256,
645
+ fileEncSha256: undefined,
646
+ bodyPath,
647
+ didSaveToTmpPath
648
+ };
649
+ }
650
+ catch (error) {
651
+ stream.destroy();
652
+ if (didSaveToTmpPath) {
653
+ try {
654
+ await fs_1.promises.unlink(bodyPath);
655
+ }
656
+ catch (err) {
657
+ logger === null || logger === void 0 ? void 0 : logger.error({ err }, 'failed to save to tmp path');
658
+ }
659
+ }
660
+ throw error;
305
661
  }
306
- // @ts-ignore Node18+ Readable.fromWeb exists
307
- return response.body instanceof Readable ? response.body : Readable.fromWeb(response.body);
308
662
  };
309
- export const encryptedStream = async (media, mediaType, { logger, saveOriginalFileIfRequired, opts } = {}) => {
310
- const { stream, type } = await getStream(media, opts);
663
+ exports.prepareStream = prepareStream;
664
+ const encryptedStream = async (media, mediaType, { logger, saveOriginalFileIfRequired, opts, isPtt, forceOpus, convertVideo } = {}) => {
665
+ const { stream, type } = await (0, exports.getStream)(media, opts);
311
666
  logger?.debug('fetched media stream');
667
+
668
+ let finalStream = stream;
669
+
670
+ if (mediaType === 'audio' && (isPtt === true || forceOpus === true)) {
671
+ try {
672
+ const buffer = await (0, exports.toBuffer)(stream);
673
+ const opusBuffer = await exports.convertToOpusBuffer(buffer, logger);
674
+ finalStream = (0, exports.toReadable)(opusBuffer);
675
+ logger?.debug('converted audio to Opus');
676
+ } catch (error) {
677
+ logger?.error('failed to convert audio to Opus, fallback to original stream');
678
+ const { stream: newStream } = await (0, exports.getStream)(media, opts);
679
+ finalStream = newStream;
680
+ }
681
+ }
682
+
683
+ if (mediaType === 'video' && convertVideo === true) {
684
+ try {
685
+ const buffer = await (0, exports.toBuffer)(stream);
686
+ const mp4Buffer = await exports.convertToMp4Buffer(buffer, logger);
687
+ finalStream = (0, exports.toReadable)(mp4Buffer);
688
+ logger?.debug('converted video to mp4');
689
+ } catch (error) {
690
+ logger?.error('failed to convert video to mp4, fallback to original stream');
691
+ const { stream: newStream } = await (0, exports.getStream)(media, opts);
692
+ finalStream = newStream;
693
+ }
694
+ }
695
+
312
696
  const mediaKey = Crypto.randomBytes(32);
313
697
  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);
698
+ const encWriteStream = new stream_1.Readable({ read: () => {} });
699
+ let bodyPath;
700
+ let writeStream;
701
+ let didSaveToTmpPath = false;
702
+
703
+ if (type === 'file') {
704
+ bodyPath = media.url;
705
+ } else if (saveOriginalFileIfRequired) {
706
+ bodyPath = (0, path_1.join)(getTmpFilesDirectory(), mediaType + (0, generics_1.generateMessageIDV2)());
707
+ writeStream = (0, fs_1.createWriteStream)(bodyPath);
708
+ didSaveToTmpPath = true;
321
709
  }
710
+
322
711
  let fileLength = 0;
323
712
  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
- };
713
+ let hmac = Crypto.createHmac('sha256', macKey).update(iv);
714
+ let sha256Plain = Crypto.createHash('sha256');
715
+ let sha256Enc = Crypto.createHash('sha256');
716
+
335
717
  try {
336
- for await (const data of stream) {
718
+ for await (const data of finalStream) {
337
719
  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}"`, {
342
- data: { media, type }
343
- });
720
+ if (type === 'remote' && (opts?.maxContentLength) && fileLength + data.length > opts.maxContentLength) {
721
+ throw new boom_1.Boom(`content length exceeded when encrypting "${type}"`, { data: { media, type } });
344
722
  }
345
- if (originalFileStream) {
346
- if (!originalFileStream.write(data)) {
347
- await once(originalFileStream, 'drain');
723
+
724
+ sha256Plain = sha256Plain.update(data);
725
+
726
+ if (writeStream) {
727
+ if (!writeStream.write(data)) {
728
+ await (0, events_1.once)(writeStream, 'drain');
348
729
  }
349
730
  }
350
- sha256Plain.update(data);
351
- await onChunk(aes.update(data));
731
+
732
+ onChunk(aes.update(data));
352
733
  }
353
- await onChunk(aes.final());
734
+
735
+ onChunk(aes.final());
354
736
  const mac = hmac.digest().slice(0, 10);
355
- sha256Enc.update(mac);
737
+ sha256Enc = sha256Enc.update(mac);
356
738
  const fileSha256 = sha256Plain.digest();
357
739
  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;
740
+
741
+ encWriteStream.push(mac);
742
+ encWriteStream.push(null);
743
+ writeStream?.end();
744
+ finalStream.destroy();
745
+
368
746
  logger?.debug('encrypted data successfully');
747
+
369
748
  return {
370
749
  mediaKey,
371
- originalFilePath,
372
- encFilePath,
750
+ encWriteStream,
751
+ bodyPath,
373
752
  mac,
374
753
  fileEncSha256,
375
754
  fileSha256,
376
- fileLength
755
+ fileLength,
756
+ didSaveToTmpPath
377
757
  };
378
- }
379
- catch (error) {
380
- // destroy all streams with error
381
- encFileWriteStream.destroy();
382
- originalFileStream?.destroy?.();
758
+ } catch (error) {
759
+ encWriteStream.destroy();
760
+ writeStream?.destroy();
383
761
  aes.destroy();
384
762
  hmac.destroy();
385
763
  sha256Plain.destroy();
386
764
  sha256Enc.destroy();
387
- stream.destroy();
388
- try {
389
- await fs.unlink(encFilePath);
390
- if (originalFilePath) {
391
- await fs.unlink(originalFilePath);
765
+ finalStream.destroy();
766
+
767
+ if (didSaveToTmpPath) {
768
+ try {
769
+ await fs_1.promises.unlink(bodyPath);
770
+ } catch (err) {
771
+ logger?.error({ err }, 'failed to save to tmp path');
392
772
  }
393
773
  }
394
- catch (err) {
395
- logger?.error({ err }, 'failed deleting tmp files');
396
- }
397
774
  throw error;
398
775
  }
776
+
777
+ function onChunk(buff) {
778
+ sha256Enc = sha256Enc.update(buff);
779
+ hmac = hmac.update(buff);
780
+ encWriteStream.push(buff);
781
+ }
399
782
  };
400
- export const DEF_MEDIA_HOST = 'mmg.whatsapp.net';
783
+
784
+ exports.encryptedStream = encryptedStream;
785
+ const DEF_HOST = 'mmg.whatsapp.net';
401
786
  const AES_CHUNK_SIZE = 16;
402
787
  const toSmallestChunkSize = (num) => {
403
788
  return Math.floor(num / AES_CHUNK_SIZE) * AES_CHUNK_SIZE;
404
789
  };
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;
790
+ const getUrlFromDirectPath = (directPath) => `https://${DEF_HOST}${directPath}`;
791
+ exports.getUrlFromDirectPath = getUrlFromDirectPath;
792
+ const downloadContentFromMessage = async ({ mediaKey, directPath, url }, type, opts = {}) => {
793
+ const isValidMediaUrl = url === null || url === void 0 ? void 0 : url.startsWith('https://mmg.whatsapp.net/');
794
+ const downloadUrl = isValidMediaUrl ? url : (0, exports.getUrlFromDirectPath)(directPath);
421
795
  if (!downloadUrl) {
422
- throw new Boom('No valid media URL or directPath present in message', { statusCode: 400 });
796
+ throw new boom_1.Boom('No valid media URL or directPath present in message', { statusCode: 400 });
423
797
  }
424
798
  const keys = await getMediaKeys(mediaKey, type);
425
- return downloadEncryptedContent(downloadUrl, keys, opts);
799
+ return (0, exports.downloadEncryptedContent)(downloadUrl, keys, opts);
426
800
  };
427
- /**
428
- * Decrypts and downloads an AES256-CBC encrypted file given the keys.
429
- * Assumes the SHA256 of the plaintext is appended to the end of the ciphertext
430
- * */
431
- export const downloadEncryptedContent = async (downloadUrl, { cipherKey, iv }, { startByte, endByte, options } = {}) => {
801
+ exports.downloadContentFromMessage = downloadContentFromMessage;
802
+ const downloadEncryptedContent = async (downloadUrl, { cipherKey, iv }, { startByte, endByte, options } = {}) => {
432
803
  let bytesFetched = 0;
433
804
  let startChunk = 0;
434
805
  let firstBlockIsIV = false;
435
- // if a start byte is specified -- then we need to fetch the previous chunk as that will form the IV
436
806
  if (startByte) {
437
807
  const chunk = toSmallestChunkSize(startByte || 0);
438
808
  if (chunk) {
@@ -442,14 +812,9 @@ export const downloadEncryptedContent = async (downloadUrl, { cipherKey, iv }, {
442
812
  }
443
813
  }
444
814
  const endChunk = endByte ? toSmallestChunkSize(endByte || 0) + AES_CHUNK_SIZE : undefined;
445
- const headersInit = options?.headers ? options.headers : undefined;
446
815
  const headers = {
447
- ...(headersInit
448
- ? Array.isArray(headersInit)
449
- ? Object.fromEntries(headersInit)
450
- : headersInit
451
- : {}),
452
- Origin: DEFAULT_ORIGIN
816
+ ...(options === null || options === void 0 ? void 0 : options.headers) || {},
817
+ Origin: Defaults_1.DEFAULT_ORIGIN,
453
818
  };
454
819
  if (startChunk || endChunk) {
455
820
  headers.Range = `bytes=${startChunk}-`;
@@ -457,10 +822,11 @@ export const downloadEncryptedContent = async (downloadUrl, { cipherKey, iv }, {
457
822
  headers.Range += endChunk;
458
823
  }
459
824
  }
460
- // download the message
461
- const fetched = await getHttpStream(downloadUrl, {
462
- ...(options || {}),
463
- headers
825
+ const fetched = await (0, exports.getHttpStream)(downloadUrl, {
826
+ ...options || {},
827
+ headers,
828
+ maxBodyLength: Infinity,
829
+ maxContentLength: Infinity,
464
830
  });
465
831
  let remainingBytes = Buffer.from([]);
466
832
  let aes;
@@ -475,9 +841,9 @@ export const downloadEncryptedContent = async (downloadUrl, { cipherKey, iv }, {
475
841
  push(bytes);
476
842
  }
477
843
  };
478
- const output = new Transform({
844
+ const output = new stream_1.Transform({
479
845
  transform(chunk, _, callback) {
480
- let data = remainingBytes.length ? Buffer.concat([remainingBytes, chunk]) : chunk;
846
+ let data = Buffer.concat([remainingBytes, chunk]);
481
847
  const decryptLength = toSmallestChunkSize(data.length);
482
848
  remainingBytes = data.slice(decryptLength);
483
849
  data = data.slice(0, decryptLength);
@@ -488,8 +854,6 @@ export const downloadEncryptedContent = async (downloadUrl, { cipherKey, iv }, {
488
854
  data = data.slice(AES_CHUNK_SIZE);
489
855
  }
490
856
  aes = Crypto.createDecipheriv('aes-256-cbc', cipherKey, ivValue);
491
- // if an end byte that is not EOF is specified
492
- // stop auto padding (PKCS7) -- otherwise throws an error for decryption
493
857
  if (endByte) {
494
858
  aes.setAutoPadding(false);
495
859
  }
@@ -510,15 +874,18 @@ export const downloadEncryptedContent = async (downloadUrl, { cipherKey, iv }, {
510
874
  catch (error) {
511
875
  callback(error);
512
876
  }
513
- }
877
+ },
514
878
  });
515
879
  return fetched.pipe(output, { end: true });
516
880
  };
517
- export function extensionForMediaMessage(message) {
518
- const getExtension = (mimetype) => mimetype.split(';')[0]?.split('/')[1];
881
+ exports.downloadEncryptedContent = downloadEncryptedContent;
882
+ function extensionForMediaMessage(message) {
883
+ const getExtension = (mimetype) => mimetype.split(';')[0].split('/')[1];
519
884
  const type = Object.keys(message)[0];
520
885
  let extension;
521
- if (type === 'locationMessage' || type === 'liveLocationMessage' || type === 'productMessage') {
886
+ if (type === 'locationMessage' ||
887
+ type === 'liveLocationMessage' ||
888
+ type === 'productMessage') {
522
889
  extension = '.jpeg';
523
890
  }
524
891
  else {
@@ -527,158 +894,52 @@ export function extensionForMediaMessage(message) {
527
894
  }
528
895
  return extension;
529
896
  }
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 }) => {
645
- // send a query JSON to obtain the url & auth token to upload our media
897
+ const getWAUploadToServer = ({ customUploadHosts, fetchAgent, logger, options }, refreshMediaConn) => {
898
+ return async (stream, { mediaType, fileEncSha256B64, newsletter, timeoutMs }) => {
899
+ var _a, _b;
646
900
  let uploadInfo = await refreshMediaConn(false);
647
901
  let urls;
648
902
  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) {
903
+ const chunks = [];
904
+ if (!Buffer.isBuffer(stream)) {
905
+ for await (const chunk of stream) {
906
+ chunks.push(chunk);
907
+ }
908
+ }
909
+ const reqBody = Buffer.isBuffer(stream) ? stream : Buffer.concat(chunks);
910
+ fileEncSha256B64 = (0, exports.encodeBase64EncodedStringForUpload)(fileEncSha256B64);
911
+ let media = Defaults_1.MEDIA_PATH_MAP[mediaType];
912
+ if (newsletter) {
913
+ media = media === null || media === void 0 ? void 0 : media.replace('/mms/', '/newsletter/newsletter-');
914
+ }
915
+ for (const { hostname, maxContentLengthBytes } of hosts) {
663
916
  logger.debug(`uploading to "${hostname}"`);
664
917
  const auth = encodeURIComponent(uploadInfo.auth);
665
- const url = `https://${hostname}${MEDIA_PATH_MAP[mediaType]}/${fileEncSha256B64}?auth=${auth}&token=${fileEncSha256B64}`;
918
+ const url = `https://${hostname}${media}/${fileEncSha256B64}?auth=${auth}&token=${fileEncSha256B64}`;
666
919
  let result;
667
920
  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) {
921
+ if (maxContentLengthBytes && reqBody.length > maxContentLengthBytes) {
922
+ throw new boom_1.Boom(`Body too large for "${hostname}"`, { statusCode: 413 });
923
+ }
924
+ const body = await axios_1.default.post(url, reqBody, {
925
+ ...options,
926
+ headers: {
927
+ ...options.headers || {},
928
+ 'Content-Type': 'application/octet-stream',
929
+ 'Origin': Defaults_1.DEFAULT_ORIGIN
930
+ },
931
+ httpsAgent: fetchAgent,
932
+ timeout: timeoutMs,
933
+ responseType: 'json',
934
+ maxBodyLength: Infinity,
935
+ maxContentLength: Infinity,
936
+ });
937
+ result = body.data;
938
+ if ((result === null || result === void 0 ? void 0 : result.url) || (result === null || result === void 0 ? void 0 : result.directPath)) {
676
939
  urls = {
677
940
  mediaUrl: result.url,
678
941
  directPath: result.direct_path,
679
- meta_hmac: result.meta_hmac,
680
- fbid: result.fbid,
681
- ts: result.ts
942
+ handle: result.handle
682
943
  };
683
944
  break;
684
945
  }
@@ -688,39 +949,37 @@ export const getWAUploadToServer = ({ customUploadHosts, fetchAgent, logger, opt
688
949
  }
689
950
  }
690
951
  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...'}`);
952
+ if (axios_1.default.isAxiosError(error)) {
953
+ result = (_a = error.response) === null || _a === void 0 ? void 0 : _a.data;
954
+ }
955
+ const isLast = hostname === ((_b = hosts[uploadInfo.hosts.length - 1]) === null || _b === void 0 ? void 0 : _b.hostname);
956
+ logger.warn({ trace: error.stack, uploadResult: result }, `Error in uploading to ${hostname} ${isLast ? '' : ', retrying...'}`);
693
957
  }
694
958
  }
695
959
  if (!urls) {
696
- throw new Boom('Media upload failed on all hosts', { statusCode: 500 });
960
+ throw new boom_1.Boom('Media upload failed on all hosts', { statusCode: 500 });
697
961
  }
698
962
  return urls;
699
963
  };
700
964
  };
965
+ exports.getWAUploadToServer = getWAUploadToServer;
701
966
  const getMediaRetryKey = (mediaKey) => {
702
- return hkdf(mediaKey, 32, { info: 'WhatsApp Media Retry Notification' });
967
+ return (0, crypto_1.hkdf)(mediaKey, 32, { info: 'WhatsApp Media Retry Notification' });
703
968
  };
704
- /**
705
- * Generate a binary node that will request the phone to re-upload the media & return the newly uploaded URL
706
- */
707
- export const encryptMediaRetryRequest = (key, mediaKey, meId) => {
969
+ const encryptMediaRetryRequest = async (key, mediaKey, meId) => {
708
970
  const recp = { stanzaId: key.id };
709
- const recpBuffer = proto.ServerErrorReceipt.encode(recp).finish();
971
+ const recpBuffer = WAProto_1.proto.ServerErrorReceipt.encode(recp).finish();
710
972
  const iv = Crypto.randomBytes(12);
711
- const retryKey = getMediaRetryKey(mediaKey);
712
- const ciphertext = aesEncryptGCM(recpBuffer, retryKey, iv, Buffer.from(key.id));
973
+ const retryKey = await getMediaRetryKey(mediaKey);
974
+ const ciphertext = (0, crypto_1.aesEncryptGCM)(recpBuffer, retryKey, iv, Buffer.from(key.id));
713
975
  const req = {
714
976
  tag: 'receipt',
715
977
  attrs: {
716
978
  id: key.id,
717
- to: jidNormalizedUser(meId),
979
+ to: (0, WABinary_1.jidNormalizedUser)(meId),
718
980
  type: 'server-error'
719
981
  },
720
982
  content: [
721
- // this encrypt node is actually pretty useless
722
- // the media is returned even without this node
723
- // keeping it here to maintain parity with WA Web
724
983
  {
725
984
  tag: 'encrypt',
726
985
  attrs: {},
@@ -733,8 +992,7 @@ export const encryptMediaRetryRequest = (key, mediaKey, meId) => {
733
992
  tag: 'rmr',
734
993
  attrs: {
735
994
  jid: key.remoteJid,
736
- from_me: (!!key.fromMe).toString(),
737
- // @ts-ignore
995
+ 'from_me': (!!key.fromMe).toString(),
738
996
  participant: key.participant || undefined
739
997
  }
740
998
  }
@@ -742,8 +1000,9 @@ export const encryptMediaRetryRequest = (key, mediaKey, meId) => {
742
1000
  };
743
1001
  return req;
744
1002
  };
745
- export const decodeMediaRetryNode = (node) => {
746
- const rmrNode = getBinaryNodeChild(node, 'rmr');
1003
+ exports.encryptMediaRetryRequest = encryptMediaRetryRequest;
1004
+ const decodeMediaRetryNode = (node) => {
1005
+ const rmrNode = (0, WABinary_1.getBinaryNodeChild)(node, 'rmr');
747
1006
  const event = {
748
1007
  key: {
749
1008
  id: node.attrs.id,
@@ -752,37 +1011,39 @@ export const decodeMediaRetryNode = (node) => {
752
1011
  participant: rmrNode.attrs.participant
753
1012
  }
754
1013
  };
755
- const errorNode = getBinaryNodeChild(node, 'error');
1014
+ const errorNode = (0, WABinary_1.getBinaryNodeChild)(node, 'error');
756
1015
  if (errorNode) {
757
1016
  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
- });
1017
+ event.error = new boom_1.Boom(`Failed to re-upload media (${errorCode})`, { data: errorNode.attrs, statusCode: (0, exports.getStatusCodeForMediaRetry)(errorCode) });
762
1018
  }
763
1019
  else {
764
- const encryptedInfoNode = getBinaryNodeChild(node, 'encrypt');
765
- const ciphertext = getBinaryNodeChildBuffer(encryptedInfoNode, 'enc_p');
766
- const iv = getBinaryNodeChildBuffer(encryptedInfoNode, 'enc_iv');
1020
+ const encryptedInfoNode = (0, WABinary_1.getBinaryNodeChild)(node, 'encrypt');
1021
+ const ciphertext = (0, WABinary_1.getBinaryNodeChildBuffer)(encryptedInfoNode, 'enc_p');
1022
+ const iv = (0, WABinary_1.getBinaryNodeChildBuffer)(encryptedInfoNode, 'enc_iv');
767
1023
  if (ciphertext && iv) {
768
1024
  event.media = { ciphertext, iv };
769
1025
  }
770
1026
  else {
771
- event.error = new Boom('Failed to re-upload media (missing ciphertext)', { statusCode: 404 });
1027
+ event.error = new boom_1.Boom('Failed to re-upload media (missing ciphertext)', { statusCode: 404 });
772
1028
  }
773
1029
  }
774
1030
  return event;
775
1031
  };
776
- export const decryptMediaRetryData = ({ ciphertext, iv }, mediaKey, msgId) => {
777
- const retryKey = getMediaRetryKey(mediaKey);
778
- const plaintext = aesDecryptGCM(ciphertext, retryKey, iv, Buffer.from(msgId));
779
- return proto.MediaRetryNotification.decode(plaintext);
1032
+ exports.decodeMediaRetryNode = decodeMediaRetryNode;
1033
+ const decryptMediaRetryData = async ({ ciphertext, iv }, mediaKey, msgId) => {
1034
+ const retryKey = await getMediaRetryKey(mediaKey);
1035
+ const plaintext = (0, crypto_1.aesDecryptGCM)(ciphertext, retryKey, iv, Buffer.from(msgId));
1036
+ return WAProto_1.proto.MediaRetryNotification.decode(plaintext);
780
1037
  };
781
- export const getStatusCodeForMediaRetry = (code) => MEDIA_RETRY_STATUS_MAP[code];
1038
+ exports.decryptMediaRetryData = decryptMediaRetryData;
1039
+ const getStatusCodeForMediaRetry = (code) => MEDIA_RETRY_STATUS_MAP[code];
1040
+ exports.getStatusCodeForMediaRetry = getStatusCodeForMediaRetry;
782
1041
  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
1042
+ [WAProto_1.proto.MediaRetryNotification.ResultType.SUCCESS]: 200,
1043
+ [WAProto_1.proto.MediaRetryNotification.ResultType.DECRYPTION_ERROR]: 412,
1044
+ [WAProto_1.proto.MediaRetryNotification.ResultType.NOT_FOUND]: 404,
1045
+ [WAProto_1.proto.MediaRetryNotification.ResultType.GENERAL_ERROR]: 418,
787
1046
  };
788
- //# sourceMappingURL=messages-media.js.map
1047
+ function __importStar(arg0) {
1048
+ throw new Error('Function not implemented.');
1049
+ }