cuki-bailx 1.2.6 → 2.0.7

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.
@@ -36,7 +36,26 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
36
36
  return (mod && mod.__esModule) ? mod : { "default": mod };
37
37
  };
38
38
  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;
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;
40
59
  exports.getMediaKeys = getMediaKeys;
41
60
  exports.uploadFile = uploadFile;
42
61
  exports.vid2jpg = vid2jpg;
@@ -44,6 +63,7 @@ exports.getAudioDuration = getAudioDuration;
44
63
  exports.getAudioWaveform = getAudioWaveform;
45
64
  exports.generateThumbnail = generateThumbnail;
46
65
  exports.extensionForMediaMessage = extensionForMediaMessage;
66
+ // Kazumari Baileys
47
67
  const boom_1 = require("@hapi/boom");
48
68
  const axios_1 = __importDefault(require("axios"));
49
69
  const form_data_1 = __importDefault(require("form-data"));
@@ -349,58 +369,173 @@ const mediaMessageSHA256B64 = (message) => {
349
369
  };
350
370
  exports.mediaMessageSHA256B64 = mediaMessageSHA256B64;
351
371
  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);
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;
365
408
  }
366
- return metadata.format.duration;
367
409
  }
410
+ exports.getAudioDuration = getAudioDuration;
368
411
  async function getAudioWaveform(buffer, logger) {
369
412
  try {
370
- const { default: decoder } = await eval('import(\'audio-decode\')');
413
+ const { PassThrough } = require('stream');
414
+ const ff = require('fluent-ffmpeg');
415
+
371
416
  let audioData;
372
417
  if (Buffer.isBuffer(buffer)) {
373
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);
374
424
  }
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;
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);
399
462
  }
400
- catch (e) {
401
- logger === null || logger === void 0 ? void 0 : logger.debug('Failed to generate waveform: ' + e);
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;
501
+ }
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;
402
535
  }
403
536
  }
537
+
538
+ exports.convertToMp4Buffer = convertToMp4Buffer;
404
539
  const toReadable = (buffer) => {
405
540
  const readable = new stream_1.Readable({ read: () => { } });
406
541
  readable.push(buffer);
@@ -526,56 +661,90 @@ const prepareStream = async (media, mediaType, { logger, saveOriginalFileIfRequi
526
661
  }
527
662
  };
528
663
  exports.prepareStream = prepareStream;
529
- const encryptedStream = async (media, mediaType, { logger, saveOriginalFileIfRequired, opts } = {}) => {
664
+ const encryptedStream = async (media, mediaType, { logger, saveOriginalFileIfRequired, opts, isPtt, forceOpus, convertVideo } = {}) => {
530
665
  const { stream, type } = await (0, exports.getStream)(media, opts);
531
- logger === null || logger === void 0 ? void 0 : logger.debug('fetched media stream');
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
+
532
696
  const mediaKey = Crypto.randomBytes(32);
533
697
  const { cipherKey, iv, macKey } = await getMediaKeys(mediaKey, mediaType);
534
- const encWriteStream = new stream_1.Readable({ read: () => { } });
698
+ const encWriteStream = new stream_1.Readable({ read: () => {} });
535
699
  let bodyPath;
536
700
  let writeStream;
537
701
  let didSaveToTmpPath = false;
702
+
538
703
  if (type === 'file') {
539
704
  bodyPath = media.url;
540
- }
541
- else if (saveOriginalFileIfRequired) {
705
+ } else if (saveOriginalFileIfRequired) {
542
706
  bodyPath = (0, path_1.join)(getTmpFilesDirectory(), mediaType + (0, generics_1.generateMessageIDV2)());
543
707
  writeStream = (0, fs_1.createWriteStream)(bodyPath);
544
708
  didSaveToTmpPath = true;
545
709
  }
710
+
546
711
  let fileLength = 0;
547
712
  const aes = Crypto.createCipheriv('aes-256-cbc', cipherKey, iv);
548
713
  let hmac = Crypto.createHmac('sha256', macKey).update(iv);
549
714
  let sha256Plain = Crypto.createHash('sha256');
550
715
  let sha256Enc = Crypto.createHash('sha256');
716
+
551
717
  try {
552
- for await (const data of stream) {
718
+ for await (const data of finalStream) {
553
719
  fileLength += data.length;
554
- if (type === 'remote'
555
- && (opts === null || opts === void 0 ? void 0 : opts.maxContentLength)
556
- && fileLength + data.length > opts.maxContentLength) {
557
- throw new boom_1.Boom(`content length exceeded when encrypting "${type}"`, {
558
- data: { media, type }
559
- });
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 } });
560
722
  }
723
+
561
724
  sha256Plain = sha256Plain.update(data);
725
+
562
726
  if (writeStream) {
563
727
  if (!writeStream.write(data)) {
564
728
  await (0, events_1.once)(writeStream, 'drain');
565
729
  }
566
730
  }
731
+
567
732
  onChunk(aes.update(data));
568
733
  }
734
+
569
735
  onChunk(aes.final());
570
736
  const mac = hmac.digest().slice(0, 10);
571
737
  sha256Enc = sha256Enc.update(mac);
572
738
  const fileSha256 = sha256Plain.digest();
573
739
  const fileEncSha256 = sha256Enc.digest();
740
+
574
741
  encWriteStream.push(mac);
575
742
  encWriteStream.push(null);
576
- 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');
743
+ writeStream?.end();
744
+ finalStream.destroy();
745
+
746
+ logger?.debug('encrypted data successfully');
747
+
579
748
  return {
580
749
  mediaKey,
581
750
  encWriteStream,
@@ -586,31 +755,32 @@ const encryptedStream = async (media, mediaType, { logger, saveOriginalFileIfReq
586
755
  fileLength,
587
756
  didSaveToTmpPath
588
757
  };
589
- }
590
- catch (error) {
758
+ } catch (error) {
591
759
  encWriteStream.destroy();
592
- writeStream === null || writeStream === void 0 ? void 0 : writeStream.destroy();
760
+ writeStream?.destroy();
593
761
  aes.destroy();
594
762
  hmac.destroy();
595
763
  sha256Plain.destroy();
596
764
  sha256Enc.destroy();
597
- stream.destroy();
765
+ finalStream.destroy();
766
+
598
767
  if (didSaveToTmpPath) {
599
768
  try {
600
769
  await fs_1.promises.unlink(bodyPath);
601
- }
602
- catch (err) {
603
- logger === null || logger === void 0 ? void 0 : logger.error({ err }, 'failed to save to tmp path');
770
+ } catch (err) {
771
+ logger?.error({ err }, 'failed to save to tmp path');
604
772
  }
605
773
  }
606
774
  throw error;
607
775
  }
776
+
608
777
  function onChunk(buff) {
609
778
  sha256Enc = sha256Enc.update(buff);
610
779
  hmac = hmac.update(buff);
611
780
  encWriteStream.push(buff);
612
781
  }
613
782
  };
783
+
614
784
  exports.encryptedStream = encryptedStream;
615
785
  const DEF_HOST = 'mmg.whatsapp.net';
616
786
  const AES_CHUNK_SIZE = 16;