alipclutch-baileys 6.7.0 → 7.1.0

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 (121) hide show
  1. package/LICENSE +1 -1
  2. package/WAProto/index.js +77698 -117050
  3. package/engine-requirements.js +3 -3
  4. package/lib/Defaults/baileys-version.json +3 -0
  5. package/lib/Defaults/index.d.ts +6 -4
  6. package/lib/Defaults/index.js +118 -78
  7. package/lib/Defaults/phonenumber-mcc.json +223 -0
  8. package/lib/Socket/Client/abstract-socket-client.d.ts +17 -0
  9. package/lib/Socket/Client/abstract-socket-client.js +13 -0
  10. package/lib/Socket/Client/index.d.ts +3 -2
  11. package/lib/Socket/Client/index.js +3 -2
  12. package/lib/Socket/Client/mobile-socket-client.d.ts +13 -0
  13. package/lib/Socket/Client/mobile-socket-client.js +65 -0
  14. package/lib/Socket/Client/types.d.ts +1 -0
  15. package/lib/Socket/Client/types.js +1 -0
  16. package/lib/Socket/Client/web-socket-client.d.ts +12 -0
  17. package/lib/Socket/Client/web-socket-client.js +62 -0
  18. package/lib/Socket/Client/websocket.d.ts +1 -0
  19. package/lib/Socket/Client/websocket.js +1 -0
  20. package/lib/Socket/business.d.ts +58 -59
  21. package/lib/Socket/chats.d.ts +230 -45
  22. package/lib/Socket/chats.js +238 -139
  23. package/lib/Socket/dugong.d.ts +254 -0
  24. package/lib/Socket/dugong.js +483 -0
  25. package/lib/Socket/groups.d.ts +32 -41
  26. package/lib/Socket/groups.js +23 -38
  27. package/lib/Socket/index.d.ts +64 -63
  28. package/lib/Socket/index.js +3 -2
  29. package/lib/Socket/messages-recv.js +65 -9
  30. package/lib/Socket/messages-send.d.ts +47 -49
  31. package/lib/Socket/messages-send.js +420 -382
  32. package/lib/Socket/newsletter.d.ts +37 -39
  33. package/lib/Socket/newsletter.js +124 -71
  34. package/lib/Socket/registration.d.ts +267 -0
  35. package/lib/Socket/registration.js +166 -0
  36. package/lib/Socket/socket.d.ts +10 -10
  37. package/lib/Socket/socket.js +38 -62
  38. package/lib/Socket/usync.d.ts +4 -3
  39. package/lib/Socket/usync.js +1 -0
  40. package/lib/Store/index.d.ts +2 -1
  41. package/lib/Store/index.js +3 -1
  42. package/lib/Store/make-cache-manager-store.d.ts +13 -0
  43. package/lib/Store/make-cache-manager-store.js +83 -0
  44. package/lib/Store/make-in-memory-store.d.ts +24 -24
  45. package/lib/Store/make-in-memory-store.js +14 -26
  46. package/lib/Store/make-ordered-dictionary.d.ts +1 -1
  47. package/lib/Store/make-ordered-dictionary.js +2 -2
  48. package/lib/Types/Auth.d.ts +7 -0
  49. package/lib/Types/Call.d.ts +1 -1
  50. package/lib/Types/Chat.d.ts +7 -14
  51. package/lib/Types/Contact.d.ts +1 -5
  52. package/lib/Types/Events.d.ts +2 -44
  53. package/lib/Types/GroupMetadata.d.ts +2 -11
  54. package/lib/Types/Label.js +1 -1
  55. package/lib/Types/LabelAssociation.js +1 -1
  56. package/lib/Types/Message.d.ts +21 -148
  57. package/lib/Types/Message.js +2 -0
  58. package/lib/Types/Newsletter.d.ts +97 -73
  59. package/lib/Types/Newsletter.js +38 -18
  60. package/lib/Types/Socket.d.ts +9 -17
  61. package/lib/Types/index.d.ts +1 -8
  62. package/lib/Types/index.js +2 -2
  63. package/lib/Utils/auth-utils.d.ts +3 -3
  64. package/lib/Utils/auth-utils.js +13 -6
  65. package/lib/Utils/business.js +2 -2
  66. package/lib/Utils/chat-utils.d.ts +16 -15
  67. package/lib/Utils/chat-utils.js +35 -36
  68. package/lib/Utils/crypto.d.ts +16 -15
  69. package/lib/Utils/crypto.js +29 -71
  70. package/lib/Utils/decode-wa-message.d.ts +6 -22
  71. package/lib/Utils/decode-wa-message.js +56 -65
  72. package/lib/Utils/event-buffer.d.ts +2 -2
  73. package/lib/Utils/event-buffer.js +7 -11
  74. package/lib/Utils/generics.d.ts +20 -17
  75. package/lib/Utils/generics.js +76 -96
  76. package/lib/Utils/history.d.ts +0 -4
  77. package/lib/Utils/history.js +6 -4
  78. package/lib/Utils/link-preview.d.ts +2 -2
  79. package/lib/Utils/link-preview.js +1 -34
  80. package/lib/Utils/logger.d.ts +3 -10
  81. package/lib/Utils/lt-hash.d.ts +2 -2
  82. package/lib/Utils/lt-hash.js +6 -6
  83. package/lib/Utils/make-mutex.d.ts +2 -2
  84. package/lib/Utils/messages-media.d.ts +24 -28
  85. package/lib/Utils/messages-media.js +236 -296
  86. package/lib/Utils/messages.d.ts +10 -13
  87. package/lib/Utils/messages.js +92 -325
  88. package/lib/Utils/noise-handler.d.ts +12 -10
  89. package/lib/Utils/noise-handler.js +23 -18
  90. package/lib/Utils/process-message.d.ts +4 -5
  91. package/lib/Utils/process-message.js +25 -108
  92. package/lib/Utils/signal.d.ts +1 -2
  93. package/lib/Utils/signal.js +26 -26
  94. package/lib/Utils/use-multi-file-auth-state.d.ts +1 -0
  95. package/lib/Utils/use-multi-file-auth-state.js +6 -51
  96. package/lib/Utils/validate-connection.d.ts +4 -3
  97. package/lib/Utils/validate-connection.js +52 -20
  98. package/lib/WABinary/constants.d.ts +27 -24
  99. package/lib/WABinary/constants.js +13 -1276
  100. package/lib/WABinary/decode.d.ts +4 -3
  101. package/lib/WABinary/decode.js +13 -26
  102. package/lib/WABinary/encode.d.ts +2 -1
  103. package/lib/WABinary/encode.js +152 -137
  104. package/lib/WABinary/generic-utils.d.ts +4 -1
  105. package/lib/WABinary/generic-utils.js +125 -37
  106. package/lib/WABinary/jid-utils.d.ts +5 -11
  107. package/lib/WABinary/jid-utils.js +5 -28
  108. package/lib/WAM/BinaryInfo.d.ts +11 -2
  109. package/lib/WAM/encode.d.ts +2 -1
  110. package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.js +1 -1
  111. package/lib/index.d.ts +1 -0
  112. package/lib/index.js +36 -43
  113. package/package.json +1 -1
  114. package/WAProto/GenerateStatics.sh +0 -4
  115. package/WAProto/WAProto.proto +0 -4775
  116. package/WAProto/index.d.ts +0 -55057
  117. package/WAProto/index.ts.ts +0 -53473
  118. package/WAProto/p.html +0 -1
  119. package/lib/Defaults/wileys-version.json +0 -3
  120. package/lib/WABinary/jid-utils.js.bak +0 -83
  121. /package/{README.MD → README.md} +0 -0
@@ -15,47 +15,23 @@ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (
15
15
  }) : function(o, v) {
16
16
  o["default"] = v;
17
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 };
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;
37
24
  };
38
25
  Object.defineProperty(exports, "__esModule", { value: true });
39
- exports.getStatusCodeForMediaRetry = exports.decryptMediaRetryData = exports.decodeMediaRetryNode = exports.encryptMediaRetryRequest = exports.getWAUploadToServer = exports.downloadEncryptedContent = exports.downloadContentFromMessage = exports.getUrlFromDirectPath = exports.encryptedStream = exports.prepareStream = exports.getHttpStream = exports.getStream = exports.toBuffer = exports.toReadable = exports.mediaMessageSHA256B64 = exports.generateProfilePicture = exports.encodeBase64EncodedStringForUpload = exports.extractImageThumb = exports.extractVideoThumb = exports.hkdfInfoKey = void 0;
40
- exports.getMediaKeys = getMediaKeys;
41
- exports.uploadFile = uploadFile;
42
- exports.vid2jpg = vid2jpg;
43
- exports.getAudioDuration = getAudioDuration;
44
- exports.getAudioWaveform = getAudioWaveform;
45
- exports.generateThumbnail = generateThumbnail;
46
- exports.extensionForMediaMessage = extensionForMediaMessage;
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;
47
27
  const boom_1 = require("@hapi/boom");
48
- const axios_1 = __importDefault(require("axios"));
49
- const form_data_1 = __importDefault(require("form-data"));
50
- const cheerio = __importStar(require("cheerio"));
28
+ const child_process_1 = require("child_process");
51
29
  const Crypto = __importStar(require("crypto"));
52
30
  const events_1 = require("events");
53
31
  const fs_1 = require("fs");
54
32
  const os_1 = require("os");
55
33
  const path_1 = require("path");
56
- const jimp_1 = __importDefault(require("jimp"));
57
34
  const stream_1 = require("stream");
58
- const child_process_1 = require("child_process");
59
35
  const WAProto_1 = require("../../WAProto");
60
36
  const Defaults_1 = require("../Defaults");
61
37
  const WABinary_1 = require("../WABinary");
@@ -65,11 +41,13 @@ const getTmpFilesDirectory = () => (0, os_1.tmpdir)();
65
41
  const getImageProcessingLibrary = async () => {
66
42
  const [_jimp, sharp] = await Promise.all([
67
43
  (async () => {
68
- const jimp = await (Promise.resolve().then(() => __importStar(require('jimp'))).catch(() => { }));
44
+ const jimp = await (import('jimp')
45
+ .catch(() => { }));
69
46
  return jimp;
70
47
  })(),
71
48
  (async () => {
72
- const sharp = await (Promise.resolve().then(() => __importStar(require('sharp'))).catch(() => { }));
49
+ const sharp = await (import('sharp')
50
+ .catch(() => { }));
73
51
  return sharp;
74
52
  })()
75
53
  ]);
@@ -88,7 +66,7 @@ const hkdfInfoKey = (type) => {
88
66
  };
89
67
  exports.hkdfInfoKey = hkdfInfoKey;
90
68
  /** generates all the keys required to encrypt/decrypt & sign a media message */
91
- async function getMediaKeys(buffer, mediaType) {
69
+ function getMediaKeys(buffer, mediaType) {
92
70
  if (!buffer) {
93
71
  throw new boom_1.Boom('Cannot derive from empty media key');
94
72
  }
@@ -96,183 +74,26 @@ async function getMediaKeys(buffer, mediaType) {
96
74
  buffer = Buffer.from(buffer.replace('data:;base64,', ''), 'base64');
97
75
  }
98
76
  // expand using HKDF to 112 bytes, also pass in the relevant app info
99
- const expandedMediaKey = await (0, crypto_1.hkdf)(buffer, 112, { info: (0, exports.hkdfInfoKey)(mediaType) });
77
+ const expandedMediaKey = (0, crypto_1.hkdf)(buffer, 112, { info: (0, exports.hkdfInfoKey)(mediaType) });
100
78
  return {
101
79
  iv: expandedMediaKey.slice(0, 16),
102
80
  cipherKey: expandedMediaKey.slice(16, 48),
103
81
  macKey: expandedMediaKey.slice(48, 80),
104
82
  };
105
83
  }
106
- async function uploadFile(buffer, logger) {
107
- const { fromBuffer } = await Promise.resolve().then(() => __importStar(require('file-type')));
108
- const fileType = await fromBuffer(buffer);
109
- if (!fileType)
110
- throw new Error("Failed to detect file type.");
111
- const { ext, mime } = fileType;
112
- const services = [
113
- {
114
- name: "catbox",
115
- url: "https://catbox.moe/user/api.php",
116
- buildForm: () => {
117
- const form = new form_data_1.default();
118
- form.append("fileToUpload", buffer, {
119
- filename: `file.${ext}`,
120
- contentType: mime || "application/octet-stream"
121
- });
122
- form.append("reqtype", "fileupload");
123
- return form;
124
- },
125
- parseResponse: res => res.data
126
- },
127
- {
128
- name: "pdi.moe",
129
- url: "https://scdn.pdi.moe/upload",
130
- buildForm: () => {
131
- const form = new form_data_1.default();
132
- form.append("file", buffer, {
133
- filename: `file.${ext}`,
134
- contentType: mime
135
- });
136
- return form;
137
- },
138
- parseResponse: res => res.data.result.url
139
- },
140
- {
141
- name: "qu.ax",
142
- url: "https://qu.ax/upload.php",
143
- buildForm: () => {
144
- const form = new form_data_1.default();
145
- form.append("files[]", buffer, {
146
- filename: `file.${ext}`,
147
- contentType: mime || "application/octet-stream"
148
- });
149
- return form;
150
- },
151
- parseResponse: res => {
152
- var _a, _b, _c;
153
- 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))
154
- throw new Error("Failed to get URL from qu.ax");
155
- return res.data.files[0].url;
156
- }
157
- },
158
- {
159
- name: "uguu.se",
160
- url: "https://uguu.se/upload.php",
161
- buildForm: () => {
162
- const form = new form_data_1.default();
163
- form.append("files[]", buffer, {
164
- filename: `file.${ext}`,
165
- contentType: mime || "application/octet-stream"
166
- });
167
- return form;
168
- },
169
- parseResponse: res => {
170
- var _a, _b, _c;
171
- 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))
172
- throw new Error("Failed to get URL from uguu.se");
173
- return res.data.files[0].url;
174
- }
175
- },
176
- {
177
- name: "tmpfiles",
178
- url: "https://tmpfiles.org/api/v1/upload",
179
- buildForm: () => {
180
- const form = new form_data_1.default();
181
- form.append("file", buffer, {
182
- filename: `file.${ext}`,
183
- contentType: mime
184
- });
185
- return form;
186
- },
187
- parseResponse: res => {
188
- const match = res.data.data.url.match(/https:\/\/tmpfiles\.org\/(.*)/);
189
- if (!match)
190
- throw new Error("Failed to parse tmpfiles URL.");
191
- return `https://tmpfiles.org/dl/${match[1]}`;
192
- }
193
- }
194
- ];
195
- for (const service of services) {
196
- try {
197
- const form = service.buildForm();
198
- const res = await axios_1.default.post(service.url, form, {
199
- headers: form.getHeaders()
200
- });
201
- const url = service.parseResponse(res);
202
- return url;
203
- }
204
- catch (error) {
205
- logger === null || logger === void 0 ? void 0 : logger.debug(`[${service.name}] eror:`, (error === null || error === void 0 ? void 0 : error.message) || error);
206
- }
207
- }
208
- throw new Error("All upload services failed.");
209
- }
210
- async function vid2jpg(videoUrl) {
211
- try {
212
- const { data } = await axios_1.default.get(`https://ezgif.com/video-to-jpg?url=${encodeURIComponent(videoUrl)}`);
213
- const $ = cheerio.load(data);
214
- const fileToken = $('input[name="file"]').attr("value");
215
- if (!fileToken) {
216
- throw new Error("Failed to retrieve file token. The video URL may be invalid or inaccessible.");
217
- }
218
- const formData = new URLSearchParams();
219
- formData.append("file", fileToken);
220
- formData.append("end", "1");
221
- formData.append("video-to-jpg", "Convert to JPG!");
222
- const convert = await axios_1.default.post(`https://ezgif.com/video-to-jpg/${fileToken}`, formData);
223
- const $2 = cheerio.load(convert.data);
224
- let imageUrl = $2("#output img").first().attr("src");
225
- if (!imageUrl) {
226
- throw new Error("Could not locate the converted image output.");
227
- }
228
- if (imageUrl.startsWith("//")) {
229
- imageUrl = "https:" + imageUrl;
84
+ exports.getMediaKeys = getMediaKeys;
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
+ (0, child_process_1.exec)(cmd, (err) => {
89
+ if (err) {
90
+ reject(err);
230
91
  }
231
- else if (imageUrl.startsWith("/")) {
232
- const cdnMatch = imageUrl.match(/\/(s\d+\..+?)\/.*/);
233
- if (cdnMatch) {
234
- imageUrl = "https://" + imageUrl.slice(2);
235
- }
236
- else {
237
- imageUrl = "https://ezgif.com" + imageUrl;
238
- }
92
+ else {
93
+ resolve();
239
94
  }
240
- return imageUrl;
241
- }
242
- catch (error) {
243
- throw new Error("Failed to convert video to JPG: " + error.message);
244
- }
245
- }
246
- /**
247
- * Extracts video thumbnail using FFmpeg
248
- */
249
- const extractVideoThumb = async (videoPath, time = '00:00:00', size = { width: 256 }) => {
250
- return new Promise((resolve, reject) => {
251
- const args = [
252
- '-ss', time,
253
- '-i', videoPath,
254
- '-y',
255
- '-vf', `scale=${size.width}:-1`,
256
- '-vframes', '1',
257
- '-f', 'image2',
258
- '-vcodec', 'mjpeg',
259
- 'pipe:1'
260
- ];
261
- const ffmpeg = (0, child_process_1.spawn)('ffmpeg', args);
262
- const chunks = [];
263
- let errorOutput = '';
264
- ffmpeg.stdout.on('data', chunk => chunks.push(chunk));
265
- ffmpeg.stderr.on('data', data => {
266
- errorOutput += data.toString();
267
- });
268
- ffmpeg.on('error', reject);
269
- ffmpeg.on('close', code => {
270
- if (code === 0) return resolve(Buffer.concat(chunks));
271
- reject(new Error(`ffmpeg exited with code ${code}\n${errorOutput}`));
272
- });
273
95
  });
274
- };
275
- exports.extractVideoThumb = extractVideoThumb;
96
+ });
276
97
  const extractImageThumb = async (bufferOrFilePath, width = 32) => {
277
98
  var _a, _b;
278
99
  if (bufferOrFilePath instanceof stream_1.Readable) {
@@ -321,8 +142,8 @@ const encodeBase64EncodedStringForUpload = (b64) => (encodeURIComponent(b64
321
142
  .replace(/\=+$/, '')));
322
143
  exports.encodeBase64EncodedStringForUpload = encodeBase64EncodedStringForUpload;
323
144
  const generateProfilePicture = async (mediaUpload) => {
145
+ var _a, _b;
324
146
  let bufferOrFilePath;
325
- let img;
326
147
  if (Buffer.isBuffer(mediaUpload)) {
327
148
  bufferOrFilePath = mediaUpload;
328
149
  }
@@ -332,11 +153,29 @@ const generateProfilePicture = async (mediaUpload) => {
332
153
  else {
333
154
  bufferOrFilePath = await (0, exports.toBuffer)(mediaUpload.stream);
334
155
  }
335
- const jimp = await jimp_1.default.read(bufferOrFilePath);
336
- const cropped = jimp.getWidth() > jimp.getHeight() ? jimp.resize(550, -1) : jimp.resize(-1, 650);
337
- img = cropped
338
- .quality(100)
339
- .getBufferAsync(jimp_1.default.MIME_JPEG);
156
+ const lib = await getImageProcessingLibrary();
157
+ let img;
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)
161
+ .jpeg({
162
+ quality: 50,
163
+ })
164
+ .toBuffer();
165
+ }
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);
175
+ }
176
+ else {
177
+ throw new boom_1.Boom('No image processing library available');
178
+ }
340
179
  return {
341
180
  img: await img,
342
181
  };
@@ -349,58 +188,138 @@ const mediaMessageSHA256B64 = (message) => {
349
188
  };
350
189
  exports.mediaMessageSHA256B64 = mediaMessageSHA256B64;
351
190
  async function getAudioDuration(buffer) {
352
- const musicMetadata = await Promise.resolve().then(() => __importStar(require('music-metadata')));
353
- let metadata;
354
- const options = {
355
- duration: true
356
- };
357
- if (Buffer.isBuffer(buffer)) {
358
- metadata = await musicMetadata.parseBuffer(buffer, undefined, options);
359
- }
360
- else if (typeof buffer === 'string') {
361
- metadata = await musicMetadata.parseFile(buffer, options);
362
- }
363
- else {
364
- metadata = await musicMetadata.parseStream(buffer, undefined, options);
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;
365
227
  }
366
- return metadata.format.duration;
367
228
  }
229
+ exports.getAudioDuration = getAudioDuration;
368
230
  async function getAudioWaveform(buffer, logger) {
369
231
  try {
370
- const { default: decoder } = await eval('import(\'audio-decode\')');
232
+ const { PassThrough } = require('stream');
233
+ const ff = require('fluent-ffmpeg');
234
+
371
235
  let audioData;
372
236
  if (Buffer.isBuffer(buffer)) {
373
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);
374
243
  }
375
- else if (typeof buffer === 'string') {
376
- const rStream = (0, fs_1.createReadStream)(buffer);
377
- audioData = await (0, exports.toBuffer)(rStream);
378
- }
379
- else {
380
- audioData = await (0, exports.toBuffer)(buffer);
381
- }
382
- const audioBuffer = await decoder(audioData);
383
- const rawData = audioBuffer.getChannelData(0);
384
- const samples = 64;
385
- const blockSize = Math.floor(rawData.length / samples);
386
- const filteredData = [];
387
- for (let i = 0; i < samples; i++) {
388
- const blockStart = blockSize * i;
389
- let sum = 0;
390
- for (let j = 0; j < blockSize; j++) {
391
- sum = sum + Math.abs(rawData[blockStart + j]);
392
- }
393
- filteredData.push(sum / blockSize);
394
- }
395
- const multiplier = Math.pow(Math.max(...filteredData), -1);
396
- const normalizedData = filteredData.map((n) => n * multiplier);
397
- const waveform = new Uint8Array(normalizedData.map((n) => Math.floor(100 * n)));
398
- 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);
399
281
  }
400
- catch (e) {
401
- logger === null || logger === void 0 ? void 0 : 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;
402
320
  }
403
321
  }
322
+ exports.convertToOpusBuffer = convertToOpusBuffer;
404
323
  const toReadable = (buffer) => {
405
324
  const readable = new stream_1.Readable({ read: () => { } });
406
325
  readable.push(buffer);
@@ -446,28 +365,12 @@ async function generateThumbnail(file, mediaType, options) {
446
365
  }
447
366
  }
448
367
  else if (mediaType === 'video') {
368
+ const imgFilename = (0, path_1.join)(getTmpFilesDirectory(), (0, generics_1.generateMessageID)() + '.jpg');
449
369
  try {
450
- let videoPath = file;
451
- if (Buffer.isBuffer(file) || file instanceof stream_1.Readable) {
452
- videoPath = (0, path_1.join)(getTmpFilesDirectory(), (0, generics_1.generateMessageIDV2)() + '.mp4');
453
- const buffer = Buffer.isBuffer(file) ? file : await (0, exports.toBuffer)(file);
454
- await fs_1.promises.writeFile(videoPath, buffer);
455
- }
456
- const thumbnailBuffer = await (0, exports.extractVideoThumb)(videoPath);
457
- const imgFilename = (0, path_1.join)(getTmpFilesDirectory(), (0, generics_1.generateMessageIDV2)() + '.jpg');
458
- await fs_1.promises.writeFile(imgFilename, thumbnailBuffer);
459
- const { buffer: processedThumbnailBuffer, original } = await (0, exports.extractImageThumb)(imgFilename);
460
- thumbnail = processedThumbnailBuffer.toString('base64');
461
- if (original.width && original.height) {
462
- originalImageDimensions = {
463
- width: original.width,
464
- height: original.height,
465
- };
466
- }
370
+ await extractVideoThumb(file, imgFilename, '00:00:00', { width: 32, height: 32 });
371
+ const buff = await fs_1.promises.readFile(imgFilename);
372
+ thumbnail = buff.toString('base64');
467
373
  await fs_1.promises.unlink(imgFilename);
468
- if (videoPath !== file) {
469
- await fs_1.promises.unlink(videoPath);
470
- }
471
374
  }
472
375
  catch (err) {
473
376
  (_a = options.logger) === null || _a === void 0 ? void 0 : _a.debug('could not generate video thumb: ' + err);
@@ -478,8 +381,10 @@ async function generateThumbnail(file, mediaType, options) {
478
381
  originalImageDimensions
479
382
  };
480
383
  }
384
+ exports.generateThumbnail = generateThumbnail;
481
385
  const getHttpStream = async (url, options = {}) => {
482
- const fetched = await axios_1.default.get(url.toString(), { ...options, responseType: 'stream' });
386
+ const { default: axios } = await import('axios');
387
+ const fetched = await axios.get(url.toString(), { ...options, responseType: 'stream' });
483
388
  return fetched.data;
484
389
  };
485
390
  exports.getHttpStream = getHttpStream;
@@ -494,7 +399,7 @@ const prepareStream = async (media, mediaType, { logger, saveOriginalFileIfRequi
494
399
  bodyPath = media.url;
495
400
  }
496
401
  else if (saveOriginalFileIfRequired) {
497
- bodyPath = (0, path_1.join)(getTmpFilesDirectory(), mediaType + (0, generics_1.generateMessageIDV2)());
402
+ bodyPath = (0, path_1.join)(getTmpFilesDirectory(), mediaType + (0, generics_1.generateMessageID)());
498
403
  (0, fs_1.writeFileSync)(bodyPath, buffer);
499
404
  didSaveToTmpPath = true;
500
405
  }
@@ -513,6 +418,7 @@ const prepareStream = async (media, mediaType, { logger, saveOriginalFileIfRequi
513
418
  };
514
419
  }
515
420
  catch (error) {
421
+ // destroy all streams with error
516
422
  stream.destroy();
517
423
  if (didSaveToTmpPath) {
518
424
  try {
@@ -526,30 +432,45 @@ const prepareStream = async (media, mediaType, { logger, saveOriginalFileIfRequi
526
432
  }
527
433
  };
528
434
  exports.prepareStream = prepareStream;
529
- const encryptedStream = async (media, mediaType, { logger, saveOriginalFileIfRequired, opts } = {}) => {
435
+ const encryptedStream = async (media, mediaType, { logger, saveOriginalFileIfRequired, opts, isPtt, forceOpus } = {}) => {
530
436
  const { stream, type } = await (0, exports.getStream)(media, opts);
531
- logger === null || logger === void 0 ? void 0 : logger.debug('fetched media stream');
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
+ }
448
+ }
449
+
532
450
  const mediaKey = Crypto.randomBytes(32);
533
- const { cipherKey, iv, macKey } = await getMediaKeys(mediaKey, mediaType);
451
+ const { cipherKey, iv, macKey } = getMediaKeys(mediaKey, mediaType);
534
452
  const encWriteStream = new stream_1.Readable({ read: () => { } });
535
453
  let bodyPath;
536
454
  let writeStream;
537
455
  let didSaveToTmpPath = false;
456
+
538
457
  if (type === 'file') {
539
458
  bodyPath = media.url;
540
459
  }
541
460
  else if (saveOriginalFileIfRequired) {
542
- bodyPath = (0, path_1.join)(getTmpFilesDirectory(), mediaType + (0, generics_1.generateMessageIDV2)());
461
+ bodyPath = (0, path_1.join)(getTmpFilesDirectory(), mediaType + (0, generics_1.generateMessageID)());
543
462
  writeStream = (0, fs_1.createWriteStream)(bodyPath);
544
463
  didSaveToTmpPath = true;
545
464
  }
465
+
546
466
  let fileLength = 0;
547
467
  const aes = Crypto.createCipheriv('aes-256-cbc', cipherKey, iv);
548
468
  let hmac = Crypto.createHmac('sha256', macKey).update(iv);
549
469
  let sha256Plain = Crypto.createHash('sha256');
550
470
  let sha256Enc = Crypto.createHash('sha256');
471
+
551
472
  try {
552
- for await (const data of stream) {
473
+ for await (const data of finalStream) {
553
474
  fileLength += data.length;
554
475
  if (type === 'remote'
555
476
  && (opts === null || opts === void 0 ? void 0 : opts.maxContentLength)
@@ -558,6 +479,7 @@ const encryptedStream = async (media, mediaType, { logger, saveOriginalFileIfReq
558
479
  data: { media, type }
559
480
  });
560
481
  }
482
+
561
483
  sha256Plain = sha256Plain.update(data);
562
484
  if (writeStream) {
563
485
  if (!writeStream.write(data)) {
@@ -566,16 +488,18 @@ const encryptedStream = async (media, mediaType, { logger, saveOriginalFileIfReq
566
488
  }
567
489
  onChunk(aes.update(data));
568
490
  }
491
+
569
492
  onChunk(aes.final());
570
493
  const mac = hmac.digest().slice(0, 10);
571
494
  sha256Enc = sha256Enc.update(mac);
572
495
  const fileSha256 = sha256Plain.digest();
573
496
  const fileEncSha256 = sha256Enc.digest();
497
+
574
498
  encWriteStream.push(mac);
575
499
  encWriteStream.push(null);
576
500
  writeStream === null || writeStream === void 0 ? void 0 : writeStream.end();
577
- stream.destroy();
578
- logger === null || logger === void 0 ? void 0 : logger.debug('encrypted data successfully');
501
+ finalStream.destroy();
502
+
579
503
  return {
580
504
  mediaKey,
581
505
  encWriteStream,
@@ -594,17 +518,18 @@ const encryptedStream = async (media, mediaType, { logger, saveOriginalFileIfReq
594
518
  hmac.destroy();
595
519
  sha256Plain.destroy();
596
520
  sha256Enc.destroy();
597
- stream.destroy();
521
+ finalStream.destroy();
522
+
598
523
  if (didSaveToTmpPath) {
599
524
  try {
600
525
  await fs_1.promises.unlink(bodyPath);
601
526
  }
602
527
  catch (err) {
603
- logger === null || logger === void 0 ? void 0 : logger.error({ err }, 'failed to save to tmp path');
604
528
  }
605
529
  }
606
530
  throw error;
607
531
  }
532
+
608
533
  function onChunk(buff) {
609
534
  sha256Enc = sha256Enc.update(buff);
610
535
  hmac = hmac.update(buff);
@@ -619,20 +544,21 @@ const toSmallestChunkSize = (num) => {
619
544
  };
620
545
  const getUrlFromDirectPath = (directPath) => `https://${DEF_HOST}${directPath}`;
621
546
  exports.getUrlFromDirectPath = getUrlFromDirectPath;
622
- const downloadContentFromMessage = async ({ mediaKey, directPath, url }, type, opts = {}) => {
623
- const isValidMediaUrl = url === null || url === void 0 ? void 0 : url.startsWith('https://mmg.whatsapp.net/');
624
- const downloadUrl = isValidMediaUrl ? url : (0, exports.getUrlFromDirectPath)(directPath);
625
- if (!downloadUrl) {
626
- throw new boom_1.Boom('No valid media URL or directPath present in message', { statusCode: 400 });
627
- }
628
- const keys = await getMediaKeys(mediaKey, type);
547
+ const downloadContentFromMessage = ({ mediaKey, directPath, url }, type, opts = {}) => {
548
+ const downloadUrl = url || (0, exports.getUrlFromDirectPath)(directPath);
549
+ const keys = getMediaKeys(mediaKey, type);
629
550
  return (0, exports.downloadEncryptedContent)(downloadUrl, keys, opts);
630
551
  };
631
552
  exports.downloadContentFromMessage = downloadContentFromMessage;
553
+ /**
554
+ * Decrypts and downloads an AES256-CBC encrypted file given the keys.
555
+ * Assumes the SHA256 of the plaintext is appended to the end of the ciphertext
556
+ * */
632
557
  const downloadEncryptedContent = async (downloadUrl, { cipherKey, iv }, { startByte, endByte, options } = {}) => {
633
558
  let bytesFetched = 0;
634
559
  let startChunk = 0;
635
560
  let firstBlockIsIV = false;
561
+ // if a start byte is specified -- then we need to fetch the previous chunk as that will form the IV
636
562
  if (startByte) {
637
563
  const chunk = toSmallestChunkSize(startByte || 0);
638
564
  if (chunk) {
@@ -652,6 +578,7 @@ const downloadEncryptedContent = async (downloadUrl, { cipherKey, iv }, { startB
652
578
  headers.Range += endChunk;
653
579
  }
654
580
  }
581
+ // download the message
655
582
  const fetched = await (0, exports.getHttpStream)(downloadUrl, {
656
583
  ...options || {},
657
584
  headers,
@@ -684,6 +611,8 @@ const downloadEncryptedContent = async (downloadUrl, { cipherKey, iv }, { startB
684
611
  data = data.slice(AES_CHUNK_SIZE);
685
612
  }
686
613
  aes = Crypto.createDecipheriv('aes-256-cbc', cipherKey, ivValue);
614
+ // if an end byte that is not EOF is specified
615
+ // stop auto padding (PKCS7) -- otherwise throws an error for decryption
687
616
  if (endByte) {
688
617
  aes.setAutoPadding(false);
689
618
  }
@@ -724,9 +653,12 @@ function extensionForMediaMessage(message) {
724
653
  }
725
654
  return extension;
726
655
  }
656
+ exports.extensionForMediaMessage = extensionForMediaMessage;
727
657
  const getWAUploadToServer = ({ customUploadHosts, fetchAgent, logger, options }, refreshMediaConn) => {
728
658
  return async (stream, { mediaType, fileEncSha256B64, newsletter, timeoutMs }) => {
729
659
  var _a, _b;
660
+ const { default: axios } = await import('axios');
661
+ // send a query JSON to obtain the url & auth token to upload our media
730
662
  let uploadInfo = await refreshMediaConn(false);
731
663
  let urls;
732
664
  const hosts = [...customUploadHosts, ...uploadInfo.hosts];
@@ -744,14 +676,14 @@ const getWAUploadToServer = ({ customUploadHosts, fetchAgent, logger, options },
744
676
  }
745
677
  for (const { hostname, maxContentLengthBytes } of hosts) {
746
678
  logger.debug(`uploading to "${hostname}"`);
747
- const auth = encodeURIComponent(uploadInfo.auth);
679
+ const auth = encodeURIComponent(uploadInfo.auth); // the auth token
748
680
  const url = `https://${hostname}${media}/${fileEncSha256B64}?auth=${auth}&token=${fileEncSha256B64}`;
749
681
  let result;
750
682
  try {
751
683
  if (maxContentLengthBytes && reqBody.length > maxContentLengthBytes) {
752
684
  throw new boom_1.Boom(`Body too large for "${hostname}"`, { statusCode: 413 });
753
685
  }
754
- const body = await axios_1.default.post(url, reqBody, {
686
+ const body = await axios.post(url, reqBody, {
755
687
  ...options,
756
688
  headers: {
757
689
  ...options.headers || {},
@@ -779,7 +711,7 @@ const getWAUploadToServer = ({ customUploadHosts, fetchAgent, logger, options },
779
711
  }
780
712
  }
781
713
  catch (error) {
782
- if (axios_1.default.isAxiosError(error)) {
714
+ if (axios.isAxiosError(error)) {
783
715
  result = (_a = error.response) === null || _a === void 0 ? void 0 : _a.data;
784
716
  }
785
717
  const isLast = hostname === ((_b = hosts[uploadInfo.hosts.length - 1]) === null || _b === void 0 ? void 0 : _b.hostname);
@@ -796,11 +728,14 @@ exports.getWAUploadToServer = getWAUploadToServer;
796
728
  const getMediaRetryKey = (mediaKey) => {
797
729
  return (0, crypto_1.hkdf)(mediaKey, 32, { info: 'WhatsApp Media Retry Notification' });
798
730
  };
799
- const encryptMediaRetryRequest = async (key, mediaKey, meId) => {
731
+ /**
732
+ * Generate a binary node that will request the phone to re-upload the media & return the newly uploaded URL
733
+ */
734
+ const encryptMediaRetryRequest = (key, mediaKey, meId) => {
800
735
  const recp = { stanzaId: key.id };
801
736
  const recpBuffer = WAProto_1.proto.ServerErrorReceipt.encode(recp).finish();
802
737
  const iv = Crypto.randomBytes(12);
803
- const retryKey = await getMediaRetryKey(mediaKey);
738
+ const retryKey = getMediaRetryKey(mediaKey);
804
739
  const ciphertext = (0, crypto_1.aesEncryptGCM)(recpBuffer, retryKey, iv, Buffer.from(key.id));
805
740
  const req = {
806
741
  tag: 'receipt',
@@ -810,6 +745,9 @@ const encryptMediaRetryRequest = async (key, mediaKey, meId) => {
810
745
  type: 'server-error'
811
746
  },
812
747
  content: [
748
+ // this encrypt node is actually pretty useless
749
+ // the media is returned even without this node
750
+ // keeping it here to maintain parity with WA Web
813
751
  {
814
752
  tag: 'encrypt',
815
753
  attrs: {},
@@ -823,6 +761,7 @@ const encryptMediaRetryRequest = async (key, mediaKey, meId) => {
823
761
  attrs: {
824
762
  jid: key.remoteJid,
825
763
  'from_me': (!!key.fromMe).toString(),
764
+ // @ts-ignore
826
765
  participant: key.participant || undefined
827
766
  }
828
767
  }
@@ -860,8 +799,8 @@ const decodeMediaRetryNode = (node) => {
860
799
  return event;
861
800
  };
862
801
  exports.decodeMediaRetryNode = decodeMediaRetryNode;
863
- const decryptMediaRetryData = async ({ ciphertext, iv }, mediaKey, msgId) => {
864
- const retryKey = await getMediaRetryKey(mediaKey);
802
+ const decryptMediaRetryData = ({ ciphertext, iv }, mediaKey, msgId) => {
803
+ const retryKey = getMediaRetryKey(mediaKey);
865
804
  const plaintext = (0, crypto_1.aesDecryptGCM)(ciphertext, retryKey, iv, Buffer.from(msgId));
866
805
  return WAProto_1.proto.MediaRetryNotification.decode(plaintext);
867
806
  };
@@ -874,6 +813,7 @@ const MEDIA_RETRY_STATUS_MAP = {
874
813
  [WAProto_1.proto.MediaRetryNotification.ResultType.NOT_FOUND]: 404,
875
814
  [WAProto_1.proto.MediaRetryNotification.ResultType.GENERAL_ERROR]: 418,
876
815
  };
816
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
877
817
  function __importStar(arg0) {
878
818
  throw new Error('Function not implemented.');
879
- }
819
+ }