beniocord.js 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/Client.js CHANGED
@@ -11,7 +11,7 @@ const Channel = require("./structures/Channel");
11
11
  const Emoji = require("./structures/Emoji");
12
12
  const Sticker = require("./structures/Sticker");
13
13
 
14
- const { MessageEmbed, MessageAttachment } = require("./structures/Util");
14
+ const { MessageEmbed, MessageAttachment, MessageImageAttachment, MessageAudioAttachment, MessageVideoAttachment } = require("./structures/Util");
15
15
  const { formatUrl, stripDomain } = require("./helpers");
16
16
 
17
17
  let global = {
@@ -541,20 +541,16 @@ class Client extends EventEmitter {
541
541
 
542
542
  // Handle MessageAttachment as opts (backward compatibility)
543
543
  if (opts instanceof MessageAttachment) {
544
- const uploadedFile = await this.uploadFile(opts);
545
- if (uploadedFile) {
546
- const mimetype = opts.name.split('.').pop().toLowerCase();
547
- const detectedType = ['png', 'jpg', 'jpeg', 'gif', 'webp'].includes(mimetype)
548
- ? 'image'
549
- : 'file';
550
- opts = {
551
- fileUrl: uploadedFile.url,
552
- fileName: uploadedFile.originalName,
553
- fileSize: uploadedFile.size,
554
- messageType: detectedType
555
- };
556
- messageType = detectedType;
557
- }
544
+ const fileData = await this._handleFileUpload(opts.buffer, opts.name);
545
+ const detectedType = opts.type || fileData.detectedType;
546
+
547
+ opts = {
548
+ fileUrl: fileData.url,
549
+ fileName: fileData.originalName,
550
+ fileSize: fileData.size,
551
+ messageType: detectedType
552
+ };
553
+ messageType = detectedType;
558
554
  }
559
555
 
560
556
  // Handle file upload
@@ -671,6 +667,155 @@ class Client extends EventEmitter {
671
667
  });
672
668
  }
673
669
 
670
+ // ============================================================================
671
+ // PUBLIC API METHODS - File/Media Sending
672
+ // ============================================================================
673
+
674
+ /**
675
+ * Sends a file to a channel
676
+ * @param {string} channelId - Channel ID
677
+ * @param {Buffer|string} file - Buffer, base64 string, or file path
678
+ * @param {Object} options - Send options
679
+ * @param {string} options.fileName - File name (required for Buffer/base64)
680
+ * @param {string} options.content - Optional text content to send with the file
681
+ * @param {string} options.replyTo - Optional message ID to reply to
682
+ * @returns {Promise<Message>} Sent message object
683
+ * @example
684
+ * // Send from file path
685
+ * client.sendFile(channelId, './document.pdf');
686
+ *
687
+ * // Send from buffer
688
+ * client.sendFile(channelId, buffer, { fileName: 'document.pdf' });
689
+ *
690
+ * // Send with message
691
+ * client.sendFile(channelId, './photo.png', { content: 'Check this out!' });
692
+ */
693
+ async sendFile(channelId, file, options = {}) {
694
+ const { fileName, content = '', replyTo } = options;
695
+
696
+ try {
697
+ const fileData = await this._handleFileUpload(file, fileName);
698
+
699
+ return this.sendMessage(channelId, content, {
700
+ file: null, // Already uploaded
701
+ fileUrl: fileData.url,
702
+ fileName: fileData.originalName,
703
+ fileSize: fileData.size,
704
+ messageType: fileData.detectedType,
705
+ replyTo,
706
+ });
707
+ } catch (error) {
708
+ throw error instanceof ClientError ? error : new ClientError(error.message, "SEND_FILE_ERROR");
709
+ }
710
+ }
711
+
712
+ /**
713
+ * Sends an image to a channel
714
+ * @param {string} channelId - Channel ID
715
+ * @param {Buffer|string} image - Buffer, base64 string, or file path
716
+ * @param {Object} options - Send options
717
+ * @param {string} options.fileName - File name (required for Buffer/base64)
718
+ * @param {string} options.content - Optional text content to send with the image
719
+ * @param {string} options.replyTo - Optional message ID to reply to
720
+ * @returns {Promise<Message>} Sent message object
721
+ * @example
722
+ * // Send from file path
723
+ * client.sendImage(channelId, './photo.png');
724
+ *
725
+ * // Send from buffer
726
+ * client.sendImage(channelId, imageBuffer, { fileName: 'screenshot.png' });
727
+ *
728
+ * // Send from base64
729
+ * client.sendImage(channelId, 'data:image/png;base64,...');
730
+ */
731
+ async sendImage(channelId, image, options = {}) {
732
+ const { fileName, content = '', replyTo } = options;
733
+
734
+ try {
735
+ const fileData = await this._handleFileUpload(image, fileName);
736
+
737
+ return this.sendMessage(channelId, content, {
738
+ fileUrl: fileData.url,
739
+ fileName: fileData.originalName,
740
+ fileSize: fileData.size,
741
+ messageType: 'image',
742
+ replyTo,
743
+ });
744
+ } catch (error) {
745
+ throw error instanceof ClientError ? error : new ClientError(error.message, "SEND_IMAGE_ERROR");
746
+ }
747
+ }
748
+
749
+ /**
750
+ * Sends an audio file to a channel
751
+ * @param {string} channelId - Channel ID
752
+ * @param {Buffer|string} audio - Buffer, base64 string, or file path
753
+ * @param {Object} options - Send options
754
+ * @param {string} options.fileName - File name (required for Buffer/base64)
755
+ * @param {string} options.content - Optional text content to send with the audio
756
+ * @param {string} options.replyTo - Optional message ID to reply to
757
+ * @returns {Promise<Message>} Sent message object
758
+ * @example
759
+ * // Send from file path
760
+ * client.sendAudio(channelId, './song.mp3');
761
+ *
762
+ * // Send from buffer (voice recording for example)
763
+ * client.sendAudio(channelId, audioBuffer, { fileName: 'voice_message.ogg' });
764
+ *
765
+ * // Send with content
766
+ * client.sendAudio(channelId, './podcast.mp3', { content: 'New episode!' });
767
+ */
768
+ async sendAudio(channelId, audio, options = {}) {
769
+ const { fileName, content = '', replyTo } = options;
770
+
771
+ try {
772
+ const fileData = await this._handleFileUpload(audio, fileName);
773
+
774
+ return this.sendMessage(channelId, content, {
775
+ fileUrl: fileData.url,
776
+ fileName: fileData.originalName,
777
+ fileSize: fileData.size,
778
+ messageType: 'audio',
779
+ replyTo,
780
+ });
781
+ } catch (error) {
782
+ throw error instanceof ClientError ? error : new ClientError(error.message, "SEND_AUDIO_ERROR");
783
+ }
784
+ }
785
+
786
+ /**
787
+ * Sends a video to a channel
788
+ * @param {string} channelId - Channel ID
789
+ * @param {Buffer|string} video - Buffer, base64 string, or file path
790
+ * @param {Object} options - Send options
791
+ * @param {string} options.fileName - File name (required for Buffer/base64)
792
+ * @param {string} options.content - Optional text content to send with the video
793
+ * @param {string} options.replyTo - Optional message ID to reply to
794
+ * @returns {Promise<Message>} Sent message object
795
+ * @example
796
+ * // Send from file path
797
+ * client.sendVideo(channelId, './video.mp4');
798
+ *
799
+ * // Send from buffer
800
+ * client.sendVideo(channelId, videoBuffer, { fileName: 'clip.mp4' });
801
+ */
802
+ async sendVideo(channelId, video, options = {}) {
803
+ const { fileName, content = '', replyTo } = options;
804
+
805
+ try {
806
+ const fileData = await this._handleFileUpload(video, fileName);
807
+
808
+ return this.sendMessage(channelId, content, {
809
+ fileUrl: fileData.url,
810
+ fileName: fileData.originalName,
811
+ fileSize: fileData.size,
812
+ messageType: 'video',
813
+ replyTo,
814
+ });
815
+ } catch (error) {
816
+ throw error instanceof ClientError ? error : new ClientError(error.message, "SEND_VIDEO_ERROR");
817
+ }
818
+ }
674
819
 
675
820
  /**
676
821
  * Deletes a message
@@ -715,7 +860,28 @@ class Client extends EventEmitter {
715
860
  }
716
861
 
717
862
  const res = await this._axios.get(`/api/channels/${channelId}/messages`, { params });
718
- const messages = res.data.map(m => new Message(m, this));
863
+
864
+ const messages = res.data.map(raw => {
865
+ let userData = raw.user;
866
+ if (!userData && raw.user_id) {
867
+ userData = {
868
+ id: raw.user_id,
869
+ username: raw.username,
870
+ display_name: raw.display_name,
871
+ avatar_url: raw.avatar_url,
872
+ status: raw.status || 'online',
873
+ emblems: raw.emblems || [],
874
+ is_bot: raw.is_bot ?? false,
875
+ last_seen: raw.last_seen ?? raw.created_at,
876
+ created_at: raw.created_at,
877
+ };
878
+ }
879
+
880
+ const messageData = { ...raw, user: userData };
881
+ const msg = new Message(messageData, this);
882
+ if (msg.author) this.cache.users.set(msg.author.id, msg.author);
883
+ return msg;
884
+ });
719
885
 
720
886
  if (!this.cache.messages.has(channelId)) {
721
887
  this.cache.messages.set(channelId, []);
@@ -728,6 +894,24 @@ class Client extends EventEmitter {
728
894
  }
729
895
  });
730
896
 
897
+ let channel = this.cache.channels.get(channelId);
898
+ if (!channel) {
899
+ const id = String(channelId);
900
+ for (const [key, val] of this.cache.channels) {
901
+ if (String(key) === id) {
902
+ channel = val;
903
+ break;
904
+ }
905
+ }
906
+ }
907
+
908
+ if (channel) {
909
+ messages.forEach(msg => {
910
+ if (!msg.channel) msg.channel = channel;
911
+ channel.messages.set(msg.id, msg);
912
+ });
913
+ }
914
+
731
915
  return messages;
732
916
  } catch (error) {
733
917
  throw error instanceof ClientError ? error : new ClientError(error.message, "FETCH_MESSAGES_ERROR");
@@ -933,30 +1117,6 @@ class Client extends EventEmitter {
933
1117
  }
934
1118
  }
935
1119
 
936
- // ============================================================================
937
- // PUBLIC API METHODS - File Upload
938
- // ============================================================================
939
-
940
- /**
941
- * Uploads a file to the server
942
- * @param {MessageAttachment} file - File attachment to upload
943
- * @returns {Promise<Object>} Upload response with file URL
944
- */
945
- async uploadFile(file) {
946
- try {
947
- const formData = new FormData();
948
- formData.append('file', file.buffer, { filename: file.name });
949
- const res = await this._axios.post('/api/upload', formData, {
950
- headers: formData.getHeaders(),
951
- timeout: 30000
952
- });
953
-
954
- return res.data;
955
- } catch (error) {
956
- throw error instanceof ClientError ? error : new ClientError(error.message, "UPLOAD_ERROR");
957
- }
958
- }
959
-
960
1120
  // ============================================================================
961
1121
  // PUBLIC API METHODS - Cache Management
962
1122
  // ============================================================================
@@ -1531,6 +1691,16 @@ class Client extends EventEmitter {
1531
1691
  'image/webp': '.webp',
1532
1692
  'video/mp4': '.mp4',
1533
1693
  'video/webm': '.webm',
1694
+ 'audio/mpeg': '.mp3',
1695
+ 'audio/mp3': '.mp3',
1696
+ 'audio/wav': '.wav',
1697
+ 'audio/wave': '.wav',
1698
+ 'audio/ogg': '.ogg',
1699
+ 'audio/webm': '.weba',
1700
+ 'audio/aac': '.aac',
1701
+ 'audio/flac': '.flac',
1702
+ 'audio/m4a': '.m4a',
1703
+ 'audio/x-m4a': '.m4a',
1534
1704
  };
1535
1705
 
1536
1706
  const ext = mimeToExt[mimeType] || '.bin';
@@ -1557,11 +1727,14 @@ class Client extends EventEmitter {
1557
1727
  const ext = path.extname(finalFileName).toLowerCase();
1558
1728
  const imageExts = ['.jpg', '.jpeg', '.png', '.gif', '.webp', '.bmp', '.svg'];
1559
1729
  const videoExts = ['.mp4', '.avi', '.mov', '.wmv', '.flv', '.mkv', '.webm'];
1730
+ const audioExts = ['.mp3', '.wav', '.ogg', '.flac', '.aac', '.m4a', '.weba', '.opus', '.wma'];
1560
1731
 
1561
1732
  if (imageExts.includes(ext)) {
1562
1733
  detectedType = 'image';
1563
1734
  } else if (videoExts.includes(ext)) {
1564
1735
  detectedType = 'video';
1736
+ } else if (audioExts.includes(ext)) {
1737
+ detectedType = 'audio';
1565
1738
  } else {
1566
1739
  detectedType = 'file';
1567
1740
  }
@@ -1634,5 +1807,9 @@ class ClientError extends Error {
1634
1807
  }
1635
1808
 
1636
1809
  Client.MessageEmbed = MessageEmbed;
1810
+ Client.MessageAttachment = MessageAttachment;
1811
+ Client.MessageImageAttachment = MessageImageAttachment;
1812
+ Client.MessageAudioAttachment = MessageAudioAttachment;
1813
+ Client.MessageVideoAttachment = MessageVideoAttachment;
1637
1814
  // Client.ClientError = ClientError;
1638
1815
  module.exports = Client;
package/package.json CHANGED
@@ -5,7 +5,7 @@
5
5
  "socket.io-client": "^4.8.1"
6
6
  },
7
7
  "name": "beniocord.js",
8
- "version": "2.1.3",
8
+ "version": "2.1.4",
9
9
  "description": "Uma biblioteca leve e intuitiva para integração com APIs de bots em plataformas de mensagens, como Discord. Facilita o envio de mensagens, gerenciamento de canais e interação com usuários, proporcionando uma experiência de desenvolvimento ágil e eficiente.",
10
10
  "main": "Client.js",
11
11
  "scripts": {
@@ -586,7 +586,89 @@ class MessageAttachment {
586
586
  }
587
587
  this.buffer = buffer;
588
588
  this.name = name;
589
+ this.type = this._detectType();
590
+ }
591
+
592
+ /**
593
+ * Detects the file type based on extension
594
+ * @private
595
+ * @returns {string} The detected type
596
+ */
597
+ _detectType() {
598
+ const ext = this.name.split('.').pop().toLowerCase();
599
+
600
+ const imageExts = ['jpg', 'jpeg', 'png', 'gif', 'webp', 'bmp', 'svg'];
601
+ const videoExts = ['mp4', 'avi', 'mov', 'wmv', 'flv', 'mkv', 'webm'];
602
+ const audioExts = ['mp3', 'wav', 'ogg', 'flac', 'aac', 'm4a', 'weba', 'opus', 'wma'];
603
+
604
+ if (imageExts.includes(ext)) return 'image';
605
+ if (videoExts.includes(ext)) return 'video';
606
+ if (audioExts.includes(ext)) return 'audio';
607
+ return 'file';
608
+ }
609
+ }
610
+
611
+ /**
612
+ * @class
613
+ */
614
+ class MessageImageAttachment extends MessageAttachment {
615
+ /**
616
+ * Creates a new MessageImageAttachment instance.
617
+ *
618
+ * @param {Buffer|Uint8Array|string} buffer - The image data.
619
+ * @param {string} name - The name of the image file (e.g., "screenshot.png").
620
+ *
621
+ * @example
622
+ * const image = new MessageImageAttachment(imageBuffer, "screenshot.png");
623
+ */
624
+ constructor(buffer, name) {
625
+ super(buffer, name);
626
+ this.type = 'image';
627
+ }
628
+ }
629
+
630
+ /**
631
+ * @class
632
+ */
633
+ class MessageAudioAttachment extends MessageAttachment {
634
+ /**
635
+ * Creates a new MessageAudioAttachment instance.
636
+ *
637
+ * @param {Buffer|Uint8Array|string} buffer - The audio data.
638
+ * @param {string} name - The name of the audio file (e.g., "voice_message.mp3").
639
+ *
640
+ * @example
641
+ * const audio = new MessageAudioAttachment(audioBuffer, "song.mp3");
642
+ */
643
+ constructor(buffer, name) {
644
+ super(buffer, name);
645
+ this.type = 'audio';
646
+ }
647
+ }
648
+
649
+ /**
650
+ * @class
651
+ */
652
+ class MessageVideoAttachment extends MessageAttachment {
653
+ /**
654
+ * Creates a new MessageVideoAttachment instance.
655
+ *
656
+ * @param {Buffer|Uint8Array|string} buffer - The video data.
657
+ * @param {string} name - The name of the video file (e.g., "video.mp4").
658
+ *
659
+ * @example
660
+ * const video = new MessageVideoAttachment(videoBuffer, "clip.mp4");
661
+ */
662
+ constructor(buffer, name) {
663
+ super(buffer, name);
664
+ this.type = 'video';
589
665
  }
590
666
  }
591
667
 
592
- module.exports = { MessageEmbed, MessageAttachment };
668
+ module.exports = {
669
+ MessageEmbed,
670
+ MessageAttachment,
671
+ MessageImageAttachment,
672
+ MessageAudioAttachment,
673
+ MessageVideoAttachment
674
+ };