amiudmodz 5.0.0 → 5.0.2
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.
- package/engine-requirements.js +1 -1
- package/lib/Defaults/index.js +1 -1
- package/lib/Utils/messages-media.js +81 -39
- package/lib/Utils/messages.js +7 -4
- package/package.json +1 -1
package/engine-requirements.js
CHANGED
package/lib/Defaults/index.js
CHANGED
|
@@ -141,7 +141,7 @@ exports.MEDIA_HKDF_KEY_MAPPING = {
|
|
|
141
141
|
exports.MEDIA_KEYS = Object.keys(exports.MEDIA_PATH_MAP);
|
|
142
142
|
exports.MIN_PREKEY_COUNT = 5;
|
|
143
143
|
exports.INITIAL_PREKEY_COUNT = 30;
|
|
144
|
-
exports.UPLOAD_TIMEOUT =
|
|
144
|
+
exports.UPLOAD_TIMEOUT = 7200000; // 2 hours in ms
|
|
145
145
|
exports.MIN_UPLOAD_INTERVAL = 5000;
|
|
146
146
|
|
|
147
147
|
exports.TimeMs = {
|
|
@@ -400,33 +400,60 @@ const prepareStream = async (media, mediaType, { logger, saveOriginalFileIfRequi
|
|
|
400
400
|
logger === null || logger === void 0 ? void 0 : logger.debug('fetched media stream');
|
|
401
401
|
let bodyPath;
|
|
402
402
|
let didSaveToTmpPath = false;
|
|
403
|
+
let fileLength = 0;
|
|
404
|
+
const sha256 = Crypto.createHash('sha256');
|
|
405
|
+
|
|
406
|
+
// Always save prepared data to a temp file for large files support and retries
|
|
407
|
+
const preparedPath = (0, path_1.join)(getTmpFilesDirectory(), `prepared-${mediaType}-${(0, generics_1.generateMessageID)()}`);
|
|
408
|
+
const preparedWriteStream = (0, fs_1.createWriteStream)(preparedPath);
|
|
409
|
+
|
|
410
|
+
if (type === 'file') {
|
|
411
|
+
bodyPath = media.url;
|
|
412
|
+
} else if (saveOriginalFileIfRequired) {
|
|
413
|
+
bodyPath = (0, path_1.join)(getTmpFilesDirectory(), mediaType + (0, generics_1.generateMessageID)());
|
|
414
|
+
didSaveToTmpPath = true;
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
const writeStream = bodyPath && !media.url ? (0, fs_1.createWriteStream)(bodyPath) : undefined;
|
|
418
|
+
|
|
403
419
|
try {
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
420
|
+
for await (const chunk of stream) {
|
|
421
|
+
fileLength += chunk.length;
|
|
422
|
+
sha256.update(chunk);
|
|
423
|
+
if (writeStream) {
|
|
424
|
+
if (!writeStream.write(chunk)) {
|
|
425
|
+
await (0, events_1.once)(writeStream, 'drain');
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
if (!preparedWriteStream.write(chunk)) {
|
|
429
|
+
await (0, events_1.once)(preparedWriteStream, 'drain');
|
|
430
|
+
}
|
|
412
431
|
}
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
432
|
+
|
|
433
|
+
preparedWriteStream.end();
|
|
434
|
+
writeStream?.end();
|
|
435
|
+
|
|
436
|
+
const fileSha256 = sha256.digest();
|
|
416
437
|
logger === null || logger === void 0 ? void 0 : logger.debug('prepare stream data successfully');
|
|
438
|
+
|
|
417
439
|
return {
|
|
418
440
|
mediaKey: undefined,
|
|
419
|
-
encWriteStream:
|
|
441
|
+
encWriteStream: (0, fs_1.createReadStream)(preparedPath),
|
|
420
442
|
fileLength,
|
|
421
443
|
fileSha256,
|
|
422
444
|
fileEncSha256: undefined,
|
|
423
445
|
bodyPath,
|
|
446
|
+
streamPath: preparedPath,
|
|
424
447
|
didSaveToTmpPath
|
|
425
448
|
};
|
|
426
449
|
}
|
|
427
450
|
catch (error) {
|
|
428
|
-
|
|
429
451
|
stream.destroy();
|
|
452
|
+
preparedWriteStream.destroy();
|
|
453
|
+
writeStream?.destroy();
|
|
454
|
+
try {
|
|
455
|
+
await fs_1.promises.unlink(preparedPath);
|
|
456
|
+
} catch {}
|
|
430
457
|
if (didSaveToTmpPath) {
|
|
431
458
|
try {
|
|
432
459
|
await fs_1.promises.unlink(bodyPath);
|
|
@@ -441,7 +468,7 @@ const prepareStream = async (media, mediaType, { logger, saveOriginalFileIfRequi
|
|
|
441
468
|
exports.prepareStream = prepareStream;
|
|
442
469
|
const encryptedStream = async (media, mediaType, { logger, saveOriginalFileIfRequired, opts, isPtt, forceOpus } = {}) => {
|
|
443
470
|
const { stream, type } = await (0, exports.getStream)(media, opts);
|
|
444
|
-
|
|
471
|
+
let finalStream = stream;
|
|
445
472
|
let opusConverted = false;
|
|
446
473
|
if (mediaType === 'audio' && (isPtt === true || forceOpus === true)) {
|
|
447
474
|
try {
|
|
@@ -449,19 +476,22 @@ const encryptedStream = async (media, mediaType, { logger, saveOriginalFileIfReq
|
|
|
449
476
|
const opusBuffer = await exports.convertToOpusBuffer(buffer, logger);
|
|
450
477
|
finalStream = (0, exports.toReadable)(opusBuffer);
|
|
451
478
|
opusConverted = true;
|
|
452
|
-
}
|
|
479
|
+
}
|
|
480
|
+
catch (error) {
|
|
453
481
|
const { stream: newStream } = await (0, exports.getStream)(media, opts);
|
|
454
482
|
finalStream = newStream;
|
|
455
483
|
}
|
|
456
484
|
}
|
|
457
|
-
|
|
458
485
|
const mediaKey = Crypto.randomBytes(32);
|
|
459
486
|
const { cipherKey, iv, macKey } = getMediaKeys(mediaKey, mediaType);
|
|
460
|
-
|
|
487
|
+
|
|
488
|
+
// Always save encrypted data to a temp file for 2GB support and retries
|
|
489
|
+
const encPath = (0, path_1.join)(getTmpFilesDirectory(), `enc-${mediaType}-${(0, generics_1.generateMessageID)()}`);
|
|
490
|
+
const encTempWriteStream = (0, fs_1.createWriteStream)(encPath);
|
|
491
|
+
|
|
461
492
|
let bodyPath;
|
|
462
493
|
let writeStream;
|
|
463
494
|
let didSaveToTmpPath = false;
|
|
464
|
-
|
|
465
495
|
if (type === 'file') {
|
|
466
496
|
bodyPath = media.url;
|
|
467
497
|
}
|
|
@@ -470,13 +500,11 @@ const encryptedStream = async (media, mediaType, { logger, saveOriginalFileIfReq
|
|
|
470
500
|
writeStream = (0, fs_1.createWriteStream)(bodyPath);
|
|
471
501
|
didSaveToTmpPath = true;
|
|
472
502
|
}
|
|
473
|
-
|
|
474
503
|
let fileLength = 0;
|
|
475
504
|
const aes = Crypto.createCipheriv('aes-256-cbc', cipherKey, iv);
|
|
476
505
|
let hmac = Crypto.createHmac('sha256', macKey).update(iv);
|
|
477
506
|
let sha256Plain = Crypto.createHash('sha256');
|
|
478
507
|
let sha256Enc = Crypto.createHash('sha256');
|
|
479
|
-
|
|
480
508
|
try {
|
|
481
509
|
for await (const data of finalStream) {
|
|
482
510
|
fileLength += data.length;
|
|
@@ -487,31 +515,44 @@ const encryptedStream = async (media, mediaType, { logger, saveOriginalFileIfReq
|
|
|
487
515
|
data: { media, type }
|
|
488
516
|
});
|
|
489
517
|
}
|
|
490
|
-
|
|
491
518
|
sha256Plain = sha256Plain.update(data);
|
|
492
519
|
if (writeStream) {
|
|
493
520
|
if (!writeStream.write(data)) {
|
|
494
521
|
await (0, events_1.once)(writeStream, 'drain');
|
|
495
522
|
}
|
|
496
523
|
}
|
|
497
|
-
|
|
524
|
+
const encrypted = aes.update(data);
|
|
525
|
+
sha256Enc = sha256Enc.update(encrypted);
|
|
526
|
+
hmac = hmac.update(encrypted);
|
|
527
|
+
if (!encTempWriteStream.write(encrypted)) {
|
|
528
|
+
await (0, events_1.once)(encTempWriteStream, 'drain');
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
const finalEnc = aes.final();
|
|
532
|
+
sha256Enc = sha256Enc.update(finalEnc);
|
|
533
|
+
hmac = hmac.update(finalEnc);
|
|
534
|
+
if (!encTempWriteStream.write(finalEnc)) {
|
|
535
|
+
await (0, events_1.once)(encTempWriteStream, 'drain');
|
|
498
536
|
}
|
|
499
537
|
|
|
500
|
-
onChunk(aes.final());
|
|
501
538
|
const mac = hmac.digest().slice(0, 10);
|
|
502
539
|
sha256Enc = sha256Enc.update(mac);
|
|
540
|
+
if (!encTempWriteStream.write(mac)) {
|
|
541
|
+
await (0, events_1.once)(encTempWriteStream, 'drain');
|
|
542
|
+
}
|
|
543
|
+
|
|
503
544
|
const fileSha256 = sha256Plain.digest();
|
|
504
545
|
const fileEncSha256 = sha256Enc.digest();
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
encWriteStream.push(null);
|
|
546
|
+
|
|
547
|
+
encTempWriteStream.end();
|
|
508
548
|
writeStream === null || writeStream === void 0 ? void 0 : writeStream.end();
|
|
509
549
|
finalStream.destroy();
|
|
510
550
|
|
|
511
551
|
return {
|
|
512
552
|
mediaKey,
|
|
513
|
-
encWriteStream,
|
|
553
|
+
encWriteStream: (0, fs_1.createReadStream)(encPath),
|
|
514
554
|
bodyPath,
|
|
555
|
+
streamPath: encPath,
|
|
515
556
|
mac,
|
|
516
557
|
fileEncSha256,
|
|
517
558
|
fileSha256,
|
|
@@ -521,14 +562,16 @@ const encryptedStream = async (media, mediaType, { logger, saveOriginalFileIfReq
|
|
|
521
562
|
};
|
|
522
563
|
}
|
|
523
564
|
catch (error) {
|
|
524
|
-
|
|
565
|
+
encTempWriteStream.destroy();
|
|
525
566
|
writeStream === null || writeStream === void 0 ? void 0 : writeStream.destroy();
|
|
526
567
|
aes.destroy();
|
|
527
568
|
hmac.destroy();
|
|
528
569
|
sha256Plain.destroy();
|
|
529
570
|
sha256Enc.destroy();
|
|
530
571
|
finalStream.destroy();
|
|
531
|
-
|
|
572
|
+
try {
|
|
573
|
+
await fs_1.promises.unlink(encPath);
|
|
574
|
+
} catch {}
|
|
532
575
|
if (didSaveToTmpPath) {
|
|
533
576
|
try {
|
|
534
577
|
await fs_1.promises.unlink(bodyPath);
|
|
@@ -538,12 +581,6 @@ const encryptedStream = async (media, mediaType, { logger, saveOriginalFileIfReq
|
|
|
538
581
|
}
|
|
539
582
|
throw error;
|
|
540
583
|
}
|
|
541
|
-
|
|
542
|
-
function onChunk(buff) {
|
|
543
|
-
sha256Enc = sha256Enc.update(buff);
|
|
544
|
-
hmac = hmac.update(buff);
|
|
545
|
-
encWriteStream.push(buff);
|
|
546
|
-
}
|
|
547
584
|
};
|
|
548
585
|
exports.encryptedStream = encryptedStream;
|
|
549
586
|
const DEF_HOST = 'mmg.whatsapp.net';
|
|
@@ -664,14 +701,14 @@ function extensionForMediaMessage(message) {
|
|
|
664
701
|
}
|
|
665
702
|
exports.extensionForMediaMessage = extensionForMediaMessage;
|
|
666
703
|
const getWAUploadToServer = ({ customUploadHosts, fetchAgent, logger, options }, refreshMediaConn) => {
|
|
667
|
-
return async (stream, { mediaType, fileEncSha256B64, newsletter, timeoutMs }) => {
|
|
704
|
+
return async (stream, { mediaType, fileEncSha256B64, newsletter, timeoutMs, fileLength, streamPath }) => {
|
|
668
705
|
var _a, _b;
|
|
669
706
|
const { default: axios } = await import('axios');
|
|
707
|
+
const { createReadStream } = await import('fs');
|
|
670
708
|
|
|
671
709
|
let uploadInfo = await refreshMediaConn(false);
|
|
672
710
|
let urls;
|
|
673
711
|
const hosts = [...customUploadHosts, ...uploadInfo.hosts];
|
|
674
|
-
const reqBody = stream;
|
|
675
712
|
fileEncSha256B64 = (0, exports.encodeBase64EncodedStringForUpload)(fileEncSha256B64);
|
|
676
713
|
let media = Defaults_1.MEDIA_PATH_MAP[mediaType];
|
|
677
714
|
if (newsletter) {
|
|
@@ -681,11 +718,16 @@ const getWAUploadToServer = ({ customUploadHosts, fetchAgent, logger, options },
|
|
|
681
718
|
logger.debug(`uploading to "${hostname}"`);
|
|
682
719
|
const auth = encodeURIComponent(uploadInfo.auth);
|
|
683
720
|
const url = `https://${hostname}${media}/${fileEncSha256B64}?auth=${auth}&token=${fileEncSha256B64}`;
|
|
721
|
+
|
|
722
|
+
// Re-create stream if retrying and we have a path
|
|
723
|
+
// This is essential for large files (2GB) as the first stream will be consumed
|
|
724
|
+
const reqBody = streamPath ? createReadStream(streamPath) : stream;
|
|
725
|
+
|
|
684
726
|
let result;
|
|
685
727
|
try {
|
|
686
|
-
const bodyLength = Buffer.isBuffer(reqBody) ? reqBody.length : 0;
|
|
728
|
+
const bodyLength = fileLength || (Buffer.isBuffer(reqBody) ? reqBody.length : 0);
|
|
687
729
|
if (maxContentLengthBytes && bodyLength > maxContentLengthBytes) {
|
|
688
|
-
throw new boom_1.Boom(`Body too large for "${hostname}"`, { statusCode: 413 });
|
|
730
|
+
throw new boom_1.Boom(`Body too large for "${hostname}" (${bodyLength} > ${maxContentLengthBytes})`, { statusCode: 413 });
|
|
689
731
|
}
|
|
690
732
|
const body = await axios.post(url, reqBody, {
|
|
691
733
|
...options,
|
package/lib/Utils/messages.js
CHANGED
|
@@ -149,7 +149,7 @@ const prepareWAMessageMedia = async (message, options) => {
|
|
|
149
149
|
const requiresAudioBackground = options.backgroundColor && mediaType === 'audio' && uploadData.ptt === true;
|
|
150
150
|
const requiresOriginalForSomeProcessing = requiresDurationComputation || requiresThumbnailComputation;
|
|
151
151
|
|
|
152
|
-
const { mediaKey, encWriteStream, bodyPath, fileEncSha256, fileSha256, fileLength, didSaveToTmpPath, opusConverted } = await (options.newsletter ? messages_media_1.prepareStream : messages_media_1.encryptedStream)(uploadData.media, options.mediaTypeOverride || mediaType, {
|
|
152
|
+
const { mediaKey, encWriteStream, bodyPath, streamPath, fileEncSha256, fileSha256, fileLength, didSaveToTmpPath, opusConverted } = await (options.newsletter ? messages_media_1.prepareStream : messages_media_1.encryptedStream)(uploadData.media, options.mediaTypeOverride || mediaType, {
|
|
153
153
|
logger,
|
|
154
154
|
saveOriginalFileIfRequired: requiresOriginalForSomeProcessing,
|
|
155
155
|
opts: options.options,
|
|
@@ -165,7 +165,7 @@ const prepareWAMessageMedia = async (message, options) => {
|
|
|
165
165
|
|
|
166
166
|
const [{ mediaUrl, directPath, handle }] = await Promise.all([
|
|
167
167
|
(async () => {
|
|
168
|
-
const result = await options.upload(encWriteStream, { fileEncSha256B64, mediaType, timeoutMs: options.mediaUploadTimeoutMs });
|
|
168
|
+
const result = await options.upload(encWriteStream, { fileEncSha256B64, mediaType, timeoutMs: options.mediaUploadTimeoutMs, fileLength, streamPath });
|
|
169
169
|
logger === null || logger === void 0 ? void 0 : logger.debug({ mediaType, cacheableKey }, 'uploaded media');
|
|
170
170
|
return result;
|
|
171
171
|
})(),
|
|
@@ -205,9 +205,12 @@ const prepareWAMessageMedia = async (message, options) => {
|
|
|
205
205
|
}
|
|
206
206
|
|
|
207
207
|
if (didSaveToTmpPath && bodyPath) {
|
|
208
|
-
await fs_1.promises.unlink(bodyPath);
|
|
209
|
-
logger === null || logger === void 0 ? void 0 : logger.debug('removed tmp files');
|
|
208
|
+
await fs_1.promises.unlink(bodyPath).catch(() => { });
|
|
210
209
|
}
|
|
210
|
+
if (streamPath) {
|
|
211
|
+
await fs_1.promises.unlink(streamPath).catch(() => { });
|
|
212
|
+
}
|
|
213
|
+
logger === null || logger === void 0 ? void 0 : logger.debug('removed tmp files');
|
|
211
214
|
});
|
|
212
215
|
|
|
213
216
|
const obj = Types_1.WAProto.Message.fromObject({
|