@roidev/kachina-md 2.1.3 → 2.1.4
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/lib/client/Client.js +180 -19
- package/lib/index.d.ts +7 -1
- package/package.json +1 -1
package/lib/client/Client.js
CHANGED
|
@@ -467,11 +467,92 @@ export class Client extends EventEmitter {
|
|
|
467
467
|
});
|
|
468
468
|
}
|
|
469
469
|
|
|
470
|
+
/**
|
|
471
|
+
* Helper function to unwrap view once message from various structures
|
|
472
|
+
* @private
|
|
473
|
+
* @param {Object} quotedMessage - The quoted message object
|
|
474
|
+
* @returns {Object|null} Unwrapped message or null if not view once
|
|
475
|
+
*/
|
|
476
|
+
_unwrapViewOnceMessage(quotedMessage) {
|
|
477
|
+
const quotedMsg = quotedMessage?.message;
|
|
478
|
+
const quotedRaw = quotedMessage?.raw?.message?.extendedTextMessage?.contextInfo?.quotedMessage;
|
|
479
|
+
|
|
480
|
+
let innerMsg = null;
|
|
481
|
+
let isViewOnce = false;
|
|
482
|
+
|
|
483
|
+
// Helper to extract from wrapper
|
|
484
|
+
const extractFromWrapper = (wrapper) => {
|
|
485
|
+
if (wrapper?.viewOnceMessageV2?.message) {
|
|
486
|
+
return { msg: wrapper.viewOnceMessageV2.message, isVO: true };
|
|
487
|
+
}
|
|
488
|
+
if (wrapper?.viewOnceMessageV2Extension?.message) {
|
|
489
|
+
return { msg: wrapper.viewOnceMessageV2Extension.message, isVO: true };
|
|
490
|
+
}
|
|
491
|
+
if (wrapper?.viewOnceMessage?.message) {
|
|
492
|
+
return { msg: wrapper.viewOnceMessage.message, isVO: true };
|
|
493
|
+
}
|
|
494
|
+
return null;
|
|
495
|
+
};
|
|
496
|
+
|
|
497
|
+
// Try multiple paths to find view once message
|
|
498
|
+
// Path 1: Direct from quotedRaw
|
|
499
|
+
if (quotedRaw) {
|
|
500
|
+
const extracted = extractFromWrapper(quotedRaw);
|
|
501
|
+
if (extracted) {
|
|
502
|
+
innerMsg = extracted.msg;
|
|
503
|
+
isViewOnce = extracted.isVO;
|
|
504
|
+
} else if (quotedRaw.ephemeralMessage?.message) {
|
|
505
|
+
const ephExtracted = extractFromWrapper(quotedRaw.ephemeralMessage.message);
|
|
506
|
+
if (ephExtracted) {
|
|
507
|
+
innerMsg = ephExtracted.msg;
|
|
508
|
+
isViewOnce = ephExtracted.isVO;
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
// Path 2: From quotedMsg
|
|
514
|
+
if (!innerMsg && quotedMsg) {
|
|
515
|
+
const extracted = extractFromWrapper(quotedMsg);
|
|
516
|
+
if (extracted) {
|
|
517
|
+
innerMsg = extracted.msg;
|
|
518
|
+
isViewOnce = extracted.isVO;
|
|
519
|
+
} else if (quotedMsg.ephemeralMessage?.message) {
|
|
520
|
+
const ephExtracted = extractFromWrapper(quotedMsg.ephemeralMessage.message);
|
|
521
|
+
if (ephExtracted) {
|
|
522
|
+
innerMsg = ephExtracted.msg;
|
|
523
|
+
isViewOnce = ephExtracted.isVO;
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
// Path 3: Already unwrapped (has viewOnce flag)
|
|
529
|
+
if (!innerMsg && quotedMsg) {
|
|
530
|
+
if (quotedMsg.imageMessage?.viewOnce ||
|
|
531
|
+
quotedMsg.videoMessage?.viewOnce ||
|
|
532
|
+
quotedMsg.audioMessage?.viewOnce) {
|
|
533
|
+
innerMsg = quotedMsg;
|
|
534
|
+
isViewOnce = true;
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
// Path 4: Check raw message directly
|
|
539
|
+
if (!innerMsg && quotedRaw) {
|
|
540
|
+
if (quotedRaw.imageMessage?.viewOnce ||
|
|
541
|
+
quotedRaw.videoMessage?.viewOnce ||
|
|
542
|
+
quotedRaw.audioMessage?.viewOnce) {
|
|
543
|
+
innerMsg = quotedRaw;
|
|
544
|
+
isViewOnce = true;
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
return isViewOnce ? innerMsg : null;
|
|
549
|
+
}
|
|
550
|
+
|
|
470
551
|
/**
|
|
471
552
|
* Read and download view once message
|
|
472
553
|
* @async
|
|
473
554
|
* @param {Object} quotedMessage - The quoted message object (m.quoted)
|
|
474
|
-
* @returns {Promise<{buffer: Buffer, type: 'image'|'video', caption: string}>} Object containing media buffer, type, and caption
|
|
555
|
+
* @returns {Promise<{buffer: Buffer, type: 'image'|'video'|'audio', caption: string, mimetype?: string, ptt?: boolean}>} Object containing media buffer, type, and caption
|
|
475
556
|
* @throws {Error} If quoted message is not provided or not a view once message
|
|
476
557
|
* @example
|
|
477
558
|
* const { buffer, type, caption } = await client.readViewOnce(m.quoted);
|
|
@@ -484,29 +565,100 @@ export class Client extends EventEmitter {
|
|
|
484
565
|
throw new Error('Quoted message is required');
|
|
485
566
|
}
|
|
486
567
|
|
|
487
|
-
//
|
|
488
|
-
const
|
|
489
|
-
|
|
568
|
+
// Try to unwrap view once message
|
|
569
|
+
const innerMsg = this._unwrapViewOnceMessage(quotedMessage);
|
|
570
|
+
|
|
571
|
+
if (!innerMsg) {
|
|
572
|
+
throw new Error('Message is not a view once message or has already been opened');
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
// Extract media messages
|
|
576
|
+
const ViewOnceImg = innerMsg.imageMessage;
|
|
577
|
+
const ViewOnceVid = innerMsg.videoMessage;
|
|
578
|
+
const ViewOnceAud = innerMsg.audioMessage;
|
|
579
|
+
|
|
580
|
+
// Get the media message
|
|
581
|
+
const mediaMsg = ViewOnceImg || ViewOnceVid || ViewOnceAud;
|
|
582
|
+
|
|
583
|
+
if (!mediaMsg) {
|
|
584
|
+
throw new Error('No media found in view once message');
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
// Check if media key exists (if not, it's expired/opened)
|
|
588
|
+
if (!mediaMsg.mediaKey || mediaMsg.mediaKey.length === 0) {
|
|
589
|
+
throw new Error('View once message has been opened or expired. Reply to view once BEFORE it is opened.');
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
// Download the media with multiple fallback methods
|
|
593
|
+
let buffer = null;
|
|
594
|
+
const errors = [];
|
|
595
|
+
|
|
596
|
+
// Method 1: Try using quotedMessage.download() if available
|
|
597
|
+
if (typeof quotedMessage.download === 'function') {
|
|
598
|
+
try {
|
|
599
|
+
buffer = await quotedMessage.download();
|
|
600
|
+
this.config.logger?.debug('Downloaded view once using m.quoted.download()');
|
|
601
|
+
} catch (err) {
|
|
602
|
+
errors.push(`Method 1 (m.quoted.download): ${err.message}`);
|
|
603
|
+
this.config.logger?.debug('Download via m.quoted.download failed:', err.message);
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
// Method 2: Try downloadMediaMessage with innerMsg
|
|
608
|
+
if (!buffer) {
|
|
609
|
+
try {
|
|
610
|
+
buffer = await downloadMediaMessage(
|
|
611
|
+
{ message: innerMsg, key: quotedMessage.key },
|
|
612
|
+
'buffer',
|
|
613
|
+
{},
|
|
614
|
+
{ logger: this.config.logger }
|
|
615
|
+
);
|
|
616
|
+
this.config.logger?.debug('Downloaded view once using downloadMediaMessage with innerMsg');
|
|
617
|
+
} catch (err) {
|
|
618
|
+
errors.push(`Method 2 (downloadMediaMessage with innerMsg): ${err.message}`);
|
|
619
|
+
this.config.logger?.debug('Download via downloadMediaMessage (innerMsg) failed:', err.message);
|
|
620
|
+
}
|
|
621
|
+
}
|
|
490
622
|
|
|
491
|
-
//
|
|
492
|
-
if (!
|
|
493
|
-
|
|
623
|
+
// Method 3: Try downloadMediaMessage with original quotedMessage
|
|
624
|
+
if (!buffer) {
|
|
625
|
+
try {
|
|
626
|
+
buffer = await downloadMediaMessage(
|
|
627
|
+
quotedMessage,
|
|
628
|
+
'buffer',
|
|
629
|
+
{},
|
|
630
|
+
{ logger: this.config.logger }
|
|
631
|
+
);
|
|
632
|
+
this.config.logger?.debug('Downloaded view once using downloadMediaMessage with quotedMessage');
|
|
633
|
+
} catch (err) {
|
|
634
|
+
errors.push(`Method 3 (downloadMediaMessage with quotedMessage): ${err.message}`);
|
|
635
|
+
this.config.logger?.debug('Download via downloadMediaMessage (quotedMessage) failed:', err.message);
|
|
636
|
+
}
|
|
494
637
|
}
|
|
495
638
|
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
{ logger: this.config.logger }
|
|
502
|
-
);
|
|
639
|
+
if (!buffer) {
|
|
640
|
+
const errorMsg = `Failed to download view once media. Tried ${errors.length} methods:\n${errors.join('\n')}`;
|
|
641
|
+
this.config.logger?.error(errorMsg);
|
|
642
|
+
throw new Error(errorMsg);
|
|
643
|
+
}
|
|
503
644
|
|
|
504
645
|
// Return buffer with type and caption
|
|
505
|
-
|
|
646
|
+
const result = {
|
|
506
647
|
buffer,
|
|
507
|
-
|
|
508
|
-
caption: ViewOnceImg?.caption || ViewOnceVid?.caption || ''
|
|
648
|
+
caption: ViewOnceImg?.caption || ViewOnceVid?.caption || ViewOnceAud?.caption || ''
|
|
509
649
|
};
|
|
650
|
+
|
|
651
|
+
if (ViewOnceImg) {
|
|
652
|
+
result.type = 'image';
|
|
653
|
+
} else if (ViewOnceVid) {
|
|
654
|
+
result.type = 'video';
|
|
655
|
+
} else if (ViewOnceAud) {
|
|
656
|
+
result.type = 'audio';
|
|
657
|
+
result.mimetype = ViewOnceAud.mimetype || 'audio/mpeg';
|
|
658
|
+
result.ptt = !!ViewOnceAud.ptt;
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
return result;
|
|
510
662
|
}
|
|
511
663
|
|
|
512
664
|
/**
|
|
@@ -524,7 +676,8 @@ export class Client extends EventEmitter {
|
|
|
524
676
|
async sendViewOnce(jid, quotedMessage, options = {}) {
|
|
525
677
|
try {
|
|
526
678
|
// Read the view once message
|
|
527
|
-
const
|
|
679
|
+
const result = await this.readViewOnce(quotedMessage);
|
|
680
|
+
const { buffer, type, caption, mimetype, ptt } = result;
|
|
528
681
|
|
|
529
682
|
// Send based on type
|
|
530
683
|
if (type === 'image') {
|
|
@@ -532,11 +685,19 @@ export class Client extends EventEmitter {
|
|
|
532
685
|
jpegThumbnail: null,
|
|
533
686
|
...options
|
|
534
687
|
});
|
|
535
|
-
} else {
|
|
688
|
+
} else if (type === 'video') {
|
|
536
689
|
return await this.sendVideo(jid, buffer, caption, {
|
|
537
690
|
jpegThumbnail: null,
|
|
538
691
|
...options
|
|
539
692
|
});
|
|
693
|
+
} else if (type === 'audio') {
|
|
694
|
+
return await this.sendAudio(jid, buffer, {
|
|
695
|
+
mimetype: mimetype || 'audio/mpeg',
|
|
696
|
+
ptt: ptt || false,
|
|
697
|
+
...options
|
|
698
|
+
});
|
|
699
|
+
} else {
|
|
700
|
+
throw new Error(`Unsupported view once media type: ${type}`);
|
|
540
701
|
}
|
|
541
702
|
} catch (error) {
|
|
542
703
|
throw new Error(`Failed to send view once: ${error.message}`);
|
package/lib/index.d.ts
CHANGED
|
@@ -51,7 +51,13 @@ export class Client extends EventEmitter {
|
|
|
51
51
|
sendReact(jid: string, messageKey: any, emoji: string): Promise<any>;
|
|
52
52
|
|
|
53
53
|
// View once methods
|
|
54
|
-
readViewOnce(quotedMessage: any): Promise<{
|
|
54
|
+
readViewOnce(quotedMessage: any): Promise<{
|
|
55
|
+
buffer: Buffer;
|
|
56
|
+
type: 'image' | 'video' | 'audio';
|
|
57
|
+
caption: string;
|
|
58
|
+
mimetype?: string;
|
|
59
|
+
ptt?: boolean;
|
|
60
|
+
}>;
|
|
55
61
|
sendViewOnce(jid: string, quotedMessage: any, options?: any): Promise<any>;
|
|
56
62
|
|
|
57
63
|
// Group methods
|