update
This commit is contained in:
		@@ -113,14 +113,13 @@ class ChannelManager extends CachedManager {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const data = await this.client.api.channels(id).get();
 | 
			
		||||
    // Delete in cache
 | 
			
		||||
    this._remove(id);
 | 
			
		||||
    return this._add(data, null, { cache, allowUnknownGuild });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Create Group DM
 | 
			
		||||
   * @param {UserResolvable[]} recipients Array of recipients
 | 
			
		||||
   * @returns {Promise<PartialGroupDMChannel>} Channel
 | 
			
		||||
   * @returns {Promise<GroupDMChannel>} Channel
 | 
			
		||||
   */
 | 
			
		||||
  async createGroupDM(recipients) {
 | 
			
		||||
    // Check
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,7 @@
 | 
			
		||||
const ThreadManager = require('./ThreadManager');
 | 
			
		||||
const { TypeError } = require('../errors');
 | 
			
		||||
const MessagePayload = require('../structures/MessagePayload');
 | 
			
		||||
const { resolveAutoArchiveMaxLimit, getAttachments, uploadFile } = require('../util/Util');
 | 
			
		||||
const { resolveAutoArchiveMaxLimit, getUploadURL, uploadFile } = require('../util/Util');
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Manages API methods for threads in forum channels and stores their cache.
 | 
			
		||||
@@ -20,7 +20,7 @@ class GuildForumThreadManager extends ThreadManager {
 | 
			
		||||
   * @typedef {BaseMessageOptions} GuildForumThreadMessageCreateOptions
 | 
			
		||||
   * @property {StickerResolvable} [stickers] The stickers to send with the message
 | 
			
		||||
   * @property {BitFieldResolvable} [flags] The flags to send with the message.
 | 
			
		||||
   * Only `SUPPRESS_EMBEDS`, `SUPPRESS_NOTIFICATIONS` and `IS_VOICE_MESSAGE` can be set.
 | 
			
		||||
   * Only `SUPPRESS_EMBEDS` and `SUPPRESS_NOTIFICATIONS` can be set.
 | 
			
		||||
   */
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
@@ -63,35 +63,29 @@ class GuildForumThreadManager extends ThreadManager {
 | 
			
		||||
    let messagePayload;
 | 
			
		||||
 | 
			
		||||
    if (message instanceof MessagePayload) {
 | 
			
		||||
      messagePayload = await message.resolveData();
 | 
			
		||||
      messagePayload = message.resolveData();
 | 
			
		||||
    } else {
 | 
			
		||||
      messagePayload = await MessagePayload.create(this, message).resolveData();
 | 
			
		||||
      messagePayload = MessagePayload.create(this, message).resolveData();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    let { data: body, files } = await messagePayload.resolveFiles();
 | 
			
		||||
    const { data: body, files } = await messagePayload.resolveFiles();
 | 
			
		||||
 | 
			
		||||
    if (typeof message == 'object' && typeof message.usingNewAttachmentAPI !== 'boolean') {
 | 
			
		||||
      message.usingNewAttachmentAPI = this.client.options.usingNewAttachmentAPI;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (message?.usingNewAttachmentAPI === true) {
 | 
			
		||||
      const attachments = await getAttachments(this.client, this.channel.id, ...files);
 | 
			
		||||
      const requestPromises = attachments.map(async attachment => {
 | 
			
		||||
        await uploadFile(files[attachment.id].file, attachment.upload_url);
 | 
			
		||||
        return {
 | 
			
		||||
          id: attachment.id,
 | 
			
		||||
          filename: files[attachment.id].name,
 | 
			
		||||
          uploaded_filename: attachment.upload_filename,
 | 
			
		||||
          description: files[attachment.id].description,
 | 
			
		||||
          duration_secs: files[attachment.id].duration_secs,
 | 
			
		||||
          waveform: files[attachment.id].waveform,
 | 
			
		||||
        };
 | 
			
		||||
      });
 | 
			
		||||
      const attachmentsData = await Promise.all(requestPromises);
 | 
			
		||||
      attachmentsData.sort((a, b) => parseInt(a.id) - parseInt(b.id));
 | 
			
		||||
      body.attachments = attachmentsData;
 | 
			
		||||
      files = [];
 | 
			
		||||
    }
 | 
			
		||||
    // New API
 | 
			
		||||
    const attachments = await getUploadURL(this.client, this.channel.id, files);
 | 
			
		||||
    const requestPromises = attachments.map(async attachment => {
 | 
			
		||||
      await uploadFile(files[attachment.id].file, attachment.upload_url);
 | 
			
		||||
      return {
 | 
			
		||||
        id: attachment.id,
 | 
			
		||||
        filename: files[attachment.id].name,
 | 
			
		||||
        uploaded_filename: attachment.upload_filename,
 | 
			
		||||
        description: files[attachment.id].description,
 | 
			
		||||
        duration_secs: files[attachment.id].duration_secs,
 | 
			
		||||
        waveform: files[attachment.id].waveform,
 | 
			
		||||
      };
 | 
			
		||||
    });
 | 
			
		||||
    const attachmentsData = await Promise.all(requestPromises);
 | 
			
		||||
    attachmentsData.sort((a, b) => parseInt(a.id) - parseInt(b.id));
 | 
			
		||||
    data.attachments = attachmentsData;
 | 
			
		||||
 | 
			
		||||
    if (autoArchiveDuration === 'MAX') autoArchiveDuration = resolveAutoArchiveMaxLimit(this.channel.guild);
 | 
			
		||||
 | 
			
		||||
@@ -103,7 +97,7 @@ class GuildForumThreadManager extends ThreadManager {
 | 
			
		||||
        applied_tags: appliedTags,
 | 
			
		||||
        message: body,
 | 
			
		||||
      },
 | 
			
		||||
      files,
 | 
			
		||||
      files: [],
 | 
			
		||||
      reason,
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -2,7 +2,7 @@
 | 
			
		||||
 | 
			
		||||
const { Collection } = require('@discordjs/collection');
 | 
			
		||||
const CachedManager = require('./CachedManager');
 | 
			
		||||
const { TypeError, Error } = require('../errors');
 | 
			
		||||
const { TypeError } = require('../errors');
 | 
			
		||||
const { Message } = require('../structures/Message');
 | 
			
		||||
const MessagePayload = require('../structures/MessagePayload');
 | 
			
		||||
const Util = require('../util/Util');
 | 
			
		||||
@@ -123,38 +123,32 @@ class MessageManager extends CachedManager {
 | 
			
		||||
    const messageId = this.resolveId(message);
 | 
			
		||||
    if (!messageId) throw new TypeError('INVALID_TYPE', 'message', 'MessageResolvable');
 | 
			
		||||
 | 
			
		||||
    let messagePayload;
 | 
			
		||||
    if (options instanceof MessagePayload) {
 | 
			
		||||
      messagePayload = await options.resolveData();
 | 
			
		||||
    } else {
 | 
			
		||||
      messagePayload = await MessagePayload.create(message instanceof Message ? message : this, options).resolveData();
 | 
			
		||||
    }
 | 
			
		||||
    let { data, files } = await messagePayload.resolveFiles();
 | 
			
		||||
    const { data, files } = await (options instanceof MessagePayload
 | 
			
		||||
      ? options
 | 
			
		||||
      : MessagePayload.create(message instanceof Message ? message : this, options)
 | 
			
		||||
    )
 | 
			
		||||
      .resolveData()
 | 
			
		||||
      .resolveFiles();
 | 
			
		||||
 | 
			
		||||
    if (typeof options == 'object' && typeof options.usingNewAttachmentAPI !== 'boolean') {
 | 
			
		||||
      options.usingNewAttachmentAPI = this.client.options.usingNewAttachmentAPI;
 | 
			
		||||
    }
 | 
			
		||||
    // New API
 | 
			
		||||
    const attachments = await Util.getUploadURL(this.client, this.channel.id, files);
 | 
			
		||||
    const requestPromises = attachments.map(async attachment => {
 | 
			
		||||
      await Util.uploadFile(files[attachment.id].file, attachment.upload_url);
 | 
			
		||||
      return {
 | 
			
		||||
        id: attachment.id,
 | 
			
		||||
        filename: files[attachment.id].name,
 | 
			
		||||
        uploaded_filename: attachment.upload_filename,
 | 
			
		||||
        description: files[attachment.id].description,
 | 
			
		||||
        duration_secs: files[attachment.id].duration_secs,
 | 
			
		||||
        waveform: files[attachment.id].waveform,
 | 
			
		||||
      };
 | 
			
		||||
    });
 | 
			
		||||
    const attachmentsData = await Promise.all(requestPromises);
 | 
			
		||||
    attachmentsData.sort((a, b) => parseInt(a.id) - parseInt(b.id));
 | 
			
		||||
    data.attachments = attachmentsData;
 | 
			
		||||
    // Empty Files
 | 
			
		||||
 | 
			
		||||
    if (options?.usingNewAttachmentAPI === true) {
 | 
			
		||||
      const attachments = await Util.getAttachments(this.client, this.channel.id, ...files);
 | 
			
		||||
      const requestPromises = attachments.map(async attachment => {
 | 
			
		||||
        await Util.uploadFile(files[attachment.id].file, attachment.upload_url);
 | 
			
		||||
        return {
 | 
			
		||||
          id: attachment.id,
 | 
			
		||||
          filename: files[attachment.id].name,
 | 
			
		||||
          uploaded_filename: attachment.upload_filename,
 | 
			
		||||
          description: files[attachment.id].description,
 | 
			
		||||
          duration_secs: files[attachment.id].duration_secs,
 | 
			
		||||
          waveform: files[attachment.id].waveform,
 | 
			
		||||
        };
 | 
			
		||||
      });
 | 
			
		||||
      const attachmentsData = await Promise.all(requestPromises);
 | 
			
		||||
      attachmentsData.sort((a, b) => parseInt(a.id) - parseInt(b.id));
 | 
			
		||||
      data.attachments = attachmentsData;
 | 
			
		||||
      files = [];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const d = await this.client.api.channels[this.channel.id].messages[messageId].patch({ data, files });
 | 
			
		||||
    const d = await this.client.api.channels[this.channel.id].messages[messageId].patch({ data });
 | 
			
		||||
 | 
			
		||||
    const existing = this.cache.get(messageId);
 | 
			
		||||
    if (existing) {
 | 
			
		||||
@@ -251,12 +245,16 @@ class MessageManager extends CachedManager {
 | 
			
		||||
      const existing = this.cache.get(messageId);
 | 
			
		||||
      if (existing && !existing.partial) return existing;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // https://discord.com/api/v9/channels/:id/messages?limit=50&around=:msgid
 | 
			
		||||
    return new Promise((resolve, reject) => {
 | 
			
		||||
      this._fetchMany({
 | 
			
		||||
        around: messageId,
 | 
			
		||||
        limit: 50,
 | 
			
		||||
      })
 | 
			
		||||
      this._fetchMany(
 | 
			
		||||
        {
 | 
			
		||||
          around: messageId,
 | 
			
		||||
          limit: 50,
 | 
			
		||||
        },
 | 
			
		||||
        cache,
 | 
			
		||||
      )
 | 
			
		||||
        .then(data_ =>
 | 
			
		||||
          data_.has(messageId) ? resolve(data_.get(messageId)) : reject(new Error('MESSAGE_ID_NOT_FOUND')),
 | 
			
		||||
        )
 | 
			
		||||
@@ -264,13 +262,6 @@ class MessageManager extends CachedManager {
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async _fetchMany(options = {}, cache) {
 | 
			
		||||
    const data = await this.client.api.channels[this.channel.id].messages.get({ query: options });
 | 
			
		||||
    const messages = new Collection();
 | 
			
		||||
    for (const message of data) messages.set(message.id, this._add(message, cache));
 | 
			
		||||
    return messages;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * @typedef {object} MessageSearchOptions
 | 
			
		||||
   * @property {Array<UserResolvable>} [authors] An array of author to filter by
 | 
			
		||||
@@ -388,6 +379,13 @@ class MessageManager extends CachedManager {
 | 
			
		||||
      total: data.total_results,
 | 
			
		||||
    };
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async _fetchMany(options = {}, cache) {
 | 
			
		||||
    const data = await this.client.api.channels[this.channel.id].messages.get({ query: options });
 | 
			
		||||
    const messages = new Collection();
 | 
			
		||||
    for (const message of data) messages.set(message.id, this._add(message, cache));
 | 
			
		||||
    return messages;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
module.exports = MessageManager;
 | 
			
		||||
 
 | 
			
		||||
@@ -1,22 +1,26 @@
 | 
			
		||||
'use strict';
 | 
			
		||||
 | 
			
		||||
const process = require('node:process');
 | 
			
		||||
const { setTimeout } = require('node:timers');
 | 
			
		||||
const { Collection } = require('@discordjs/collection');
 | 
			
		||||
const Base = require('./Base');
 | 
			
		||||
const BaseMessageComponent = require('./BaseMessageComponent');
 | 
			
		||||
const ClientApplication = require('./ClientApplication');
 | 
			
		||||
const InteractionCollector = require('./InteractionCollector');
 | 
			
		||||
const MessageAttachment = require('./MessageAttachment');
 | 
			
		||||
const MessageButton = require('./MessageButton');
 | 
			
		||||
const Embed = require('./MessageEmbed');
 | 
			
		||||
const Mentions = require('./MessageMentions');
 | 
			
		||||
const MessagePayload = require('./MessagePayload');
 | 
			
		||||
const MessageSelectMenu = require('./MessageSelectMenu');
 | 
			
		||||
const ReactionCollector = require('./ReactionCollector');
 | 
			
		||||
const { Sticker } = require('./Sticker');
 | 
			
		||||
const Application = require('./interfaces/Application');
 | 
			
		||||
const { Error } = require('../errors');
 | 
			
		||||
const ReactionManager = require('../managers/ReactionManager');
 | 
			
		||||
const { InteractionTypes, MessageTypes, SystemMessageTypes, MaxBulkDeletableMessageAge } = require('../util/Constants');
 | 
			
		||||
const {
 | 
			
		||||
  InteractionTypes,
 | 
			
		||||
  MessageTypes,
 | 
			
		||||
  SystemMessageTypes,
 | 
			
		||||
  MessageComponentTypes,
 | 
			
		||||
  Events,
 | 
			
		||||
} = require('../util/Constants');
 | 
			
		||||
const MessageFlags = require('../util/MessageFlags');
 | 
			
		||||
const Permissions = require('../util/Permissions');
 | 
			
		||||
const SnowflakeUtil = require('../util/SnowflakeUtil');
 | 
			
		||||
@@ -256,9 +260,9 @@ class Message extends Base {
 | 
			
		||||
    if ('application' in data) {
 | 
			
		||||
      /**
 | 
			
		||||
       * Supplemental application information for group activities
 | 
			
		||||
       * @type {?ClientApplication}
 | 
			
		||||
       * @type {?Application}
 | 
			
		||||
       */
 | 
			
		||||
      this.groupActivityApplication = new ClientApplication(this.client, data.application);
 | 
			
		||||
      this.groupActivityApplication = new Application(this.client, data.application);
 | 
			
		||||
    } else {
 | 
			
		||||
      this.groupActivityApplication ??= null;
 | 
			
		||||
    }
 | 
			
		||||
@@ -533,65 +537,6 @@ class Message extends Base {
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * @typedef {CollectorOptions} MessageComponentCollectorOptions
 | 
			
		||||
   * @property {MessageComponentType} [componentType] The type of component to listen for
 | 
			
		||||
   * @property {number} [max] The maximum total amount of interactions to collect
 | 
			
		||||
   * @property {number} [maxComponents] The maximum number of components to collect
 | 
			
		||||
   * @property {number} [maxUsers] The maximum number of users to interact
 | 
			
		||||
   */
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Creates a message component interaction collector.
 | 
			
		||||
   * @param {MessageComponentCollectorOptions} [options={}] Options to send to the collector
 | 
			
		||||
   * @returns {InteractionCollector}
 | 
			
		||||
   * @example
 | 
			
		||||
   * // Create a message component interaction collector
 | 
			
		||||
   * const filter = (interaction) => interaction.customId === 'button' && interaction.user.id === 'someId';
 | 
			
		||||
   * const collector = message.createMessageComponentCollector({ filter, time: 15_000 });
 | 
			
		||||
   * collector.on('collect', i => console.log(`Collected ${i.customId}`));
 | 
			
		||||
   * collector.on('end', collected => console.log(`Collected ${collected.size} items`));
 | 
			
		||||
   */
 | 
			
		||||
  createMessageComponentCollector(options = {}) {
 | 
			
		||||
    return new InteractionCollector(this.client, {
 | 
			
		||||
      ...options,
 | 
			
		||||
      interactionType: InteractionTypes.MESSAGE_COMPONENT,
 | 
			
		||||
      message: this,
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * An object containing the same properties as CollectorOptions, but a few more:
 | 
			
		||||
   * @typedef {Object} AwaitMessageComponentOptions
 | 
			
		||||
   * @property {CollectorFilter} [filter] The filter applied to this collector
 | 
			
		||||
   * @property {number} [time] Time to wait for an interaction before rejecting
 | 
			
		||||
   * @property {MessageComponentType} [componentType] The type of component interaction to collect
 | 
			
		||||
   */
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Collects a single component interaction that passes the filter.
 | 
			
		||||
   * The Promise will reject if the time expires.
 | 
			
		||||
   * @param {AwaitMessageComponentOptions} [options={}] Options to pass to the internal collector
 | 
			
		||||
   * @returns {Promise<MessageComponentInteraction>}
 | 
			
		||||
   * @example
 | 
			
		||||
   * // Collect a message component interaction
 | 
			
		||||
   * const filter = (interaction) => interaction.customId === 'button' && interaction.user.id === 'someId';
 | 
			
		||||
   * message.awaitMessageComponent({ filter, time: 15_000 })
 | 
			
		||||
   *   .then(interaction => console.log(`${interaction.customId} was clicked!`))
 | 
			
		||||
   *   .catch(console.error);
 | 
			
		||||
   */
 | 
			
		||||
  awaitMessageComponent(options = {}) {
 | 
			
		||||
    const _options = { ...options, max: 1 };
 | 
			
		||||
    return new Promise((resolve, reject) => {
 | 
			
		||||
      const collector = this.createMessageComponentCollector(_options);
 | 
			
		||||
      collector.once('end', (interactions, reason) => {
 | 
			
		||||
        const interaction = interactions.first();
 | 
			
		||||
        if (interaction) resolve(interaction);
 | 
			
		||||
        else reject(new Error('INTERACTION_COLLECTOR_ERROR', reason));
 | 
			
		||||
      });
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Whether the message is editable by the client user
 | 
			
		||||
   * @type {boolean}
 | 
			
		||||
@@ -653,14 +598,7 @@ class Message extends Base {
 | 
			
		||||
   * channel.bulkDelete(messages.filter(message => message.bulkDeletable));
 | 
			
		||||
   */
 | 
			
		||||
  get bulkDeletable() {
 | 
			
		||||
    return (
 | 
			
		||||
      (this.inGuild() &&
 | 
			
		||||
        this.client.user.bot &&
 | 
			
		||||
        Date.now() - this.createdTimestamp < MaxBulkDeletableMessageAge &&
 | 
			
		||||
        this.deletable &&
 | 
			
		||||
        this.channel?.permissionsFor(this.client.user).has(Permissions.FLAGS.MANAGE_MESSAGES, false)) ??
 | 
			
		||||
      false
 | 
			
		||||
    );
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
@@ -722,7 +660,7 @@ class Message extends Base {
 | 
			
		||||
   * @property {MessageAttachment[]} [attachments] An array of attachments to keep,
 | 
			
		||||
   * all attachments will be kept if omitted
 | 
			
		||||
   * @property {FileOptions[]|BufferResolvable[]|MessageAttachment[]} [files] Files to add to the message
 | 
			
		||||
   * @property {Array<(MessageActionRow|MessageActionRowOptions)>} [components]
 | 
			
		||||
   * @property {MessageActionRow[]|MessageActionRowOptions[]} [components]
 | 
			
		||||
   * Action rows containing interactive components for the message (buttons, select menus)
 | 
			
		||||
   */
 | 
			
		||||
 | 
			
		||||
@@ -792,7 +730,7 @@ class Message extends Base {
 | 
			
		||||
  /**
 | 
			
		||||
   * Adds a reaction to the message.
 | 
			
		||||
   * @param {EmojiIdentifierResolvable} emoji The emoji to react with
 | 
			
		||||
   * @param {boolean} [burst=false] Super Reactions (Discord Nitro only)
 | 
			
		||||
   * @param {boolean} [burst=false] Super Reactions
 | 
			
		||||
   * @returns {Promise<MessageReaction>}
 | 
			
		||||
   * @example
 | 
			
		||||
   * // React to a message with a unicode emoji
 | 
			
		||||
@@ -1024,13 +962,221 @@ class Message extends Base {
 | 
			
		||||
      reactions: false,
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
  // Added
 | 
			
		||||
 | 
			
		||||
  // TypeScript
 | 
			
		||||
  /**
 | 
			
		||||
   * Check data
 | 
			
		||||
   * @type {boolean}
 | 
			
		||||
   * @readonly
 | 
			
		||||
   */
 | 
			
		||||
  get isMessage() {
 | 
			
		||||
    return true;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Click specific button with X and Y
 | 
			
		||||
   * @typedef {Object} MessageButtonLocation
 | 
			
		||||
   * @property {number} X Index of the row
 | 
			
		||||
   * @property {number} Y Index of the column
 | 
			
		||||
   */
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Click specific button or automatically click first button if no button is specified.
 | 
			
		||||
   * @param {MessageButtonLocation|string|undefined} button button
 | 
			
		||||
   * @returns {Promise<Message|Modal>}
 | 
			
		||||
   * @example
 | 
			
		||||
   * // Demo msg
 | 
			
		||||
   * Some content
 | 
			
		||||
   *  ――――――――――――――――――――――――――――――――> X from 0
 | 
			
		||||
   *  │ [button1] [button2] [button3]
 | 
			
		||||
   *  │ [button4] [button5] [button6]
 | 
			
		||||
   *  ↓
 | 
			
		||||
   *  Y from 0
 | 
			
		||||
   * // Click button6 with X and Y
 | 
			
		||||
   * [0,0] [1,0] [2,0]
 | 
			
		||||
   * [0,1] [1,1] [2,1]
 | 
			
		||||
   * // Code
 | 
			
		||||
   * message.clickButton({
 | 
			
		||||
   *  X: 2, Y: 1,
 | 
			
		||||
   * });
 | 
			
		||||
   * // Click button with customId (Ex button 5)
 | 
			
		||||
   * message.clickButton('button5');
 | 
			
		||||
   * // Click button 1
 | 
			
		||||
   * message.clickButton();
 | 
			
		||||
   */
 | 
			
		||||
  clickButton(button) {
 | 
			
		||||
    if (typeof button == 'undefined') {
 | 
			
		||||
      button = this.components
 | 
			
		||||
        .flatMap(row => row.components)
 | 
			
		||||
        .find(b => b.type === 'BUTTON' && b.customId && !b.disabled);
 | 
			
		||||
    } else if (typeof button == 'string') {
 | 
			
		||||
      button = this.components.flatMap(row => row.components).find(b => b.type === 'BUTTON' && b.customId == button);
 | 
			
		||||
    } else {
 | 
			
		||||
      button = this.components[button.Y]?.components[button.X];
 | 
			
		||||
    }
 | 
			
		||||
    button = button.toJSON();
 | 
			
		||||
    if (!button) throw new TypeError('BUTTON_NOT_FOUND');
 | 
			
		||||
    if (!button.custom_id || button.disabled) throw new TypeError('BUTTON_CANNOT_CLICK');
 | 
			
		||||
    const nonce = SnowflakeUtil.generate();
 | 
			
		||||
    const data = {
 | 
			
		||||
      type: InteractionTypes.MESSAGE_COMPONENT,
 | 
			
		||||
      nonce,
 | 
			
		||||
      guild_id: this.guildId,
 | 
			
		||||
      channel_id: this.channelId,
 | 
			
		||||
      message_id: this.id,
 | 
			
		||||
      application_id: this.applicationId ?? this.author.id,
 | 
			
		||||
      session_id: this.client.ws.shards.first()?.sessionId,
 | 
			
		||||
      message_flags: this.flags.bitfield,
 | 
			
		||||
      data: {
 | 
			
		||||
        component_type: MessageComponentTypes.BUTTON,
 | 
			
		||||
        custom_id: button.custom_id,
 | 
			
		||||
      },
 | 
			
		||||
    };
 | 
			
		||||
    this.client.api.interactions.post({
 | 
			
		||||
      data,
 | 
			
		||||
    });
 | 
			
		||||
    return new Promise((resolve, reject) => {
 | 
			
		||||
      const timeoutMs = 5_000;
 | 
			
		||||
      // Waiting for MsgCreate / ModalCreate
 | 
			
		||||
      const handler = data => {
 | 
			
		||||
        // UnhandledPacket
 | 
			
		||||
        if (data.d?.nonce == nonce && data.t == 'INTERACTION_SUCCESS') {
 | 
			
		||||
          // Interaction#deferUpdate
 | 
			
		||||
          this.client.removeListener(Events.MESSAGE_CREATE, handler);
 | 
			
		||||
          this.client.removeListener(Events.UNHANDLED_PACKET, handler);
 | 
			
		||||
          this.client.removeListener(Events.INTERACTION_MODAL_CREATE, handler);
 | 
			
		||||
          resolve(this);
 | 
			
		||||
        }
 | 
			
		||||
        if (data.nonce !== nonce) return;
 | 
			
		||||
        clearTimeout(timeout);
 | 
			
		||||
        this.client.removeListener(Events.MESSAGE_CREATE, handler);
 | 
			
		||||
        this.client.removeListener(Events.UNHANDLED_PACKET, handler);
 | 
			
		||||
        this.client.removeListener(Events.INTERACTION_MODAL_CREATE, handler);
 | 
			
		||||
        this.client.decrementMaxListeners();
 | 
			
		||||
        resolve(data);
 | 
			
		||||
      };
 | 
			
		||||
      const timeout = setTimeout(() => {
 | 
			
		||||
        this.client.removeListener(Events.MESSAGE_CREATE, handler);
 | 
			
		||||
        this.client.removeListener(Events.UNHANDLED_PACKET, handler);
 | 
			
		||||
        this.client.removeListener(Events.INTERACTION_MODAL_CREATE, handler);
 | 
			
		||||
        this.client.decrementMaxListeners();
 | 
			
		||||
        reject(new Error('INTERACTION_FAILED'));
 | 
			
		||||
      }, timeoutMs).unref();
 | 
			
		||||
      this.client.incrementMaxListeners();
 | 
			
		||||
      this.client.on(Events.MESSAGE_CREATE, handler);
 | 
			
		||||
      this.client.on(Events.UNHANDLED_PACKET, handler);
 | 
			
		||||
      this.client.on(Events.INTERACTION_MODAL_CREATE, handler);
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Select specific menu
 | 
			
		||||
   * @param {number|string} menu Target
 | 
			
		||||
   * @param {Array<UserResolvable | RoleResolvable | ChannelResolvable | string>} values Any value
 | 
			
		||||
   * @returns {Promise<Message|Modal>}
 | 
			
		||||
   */
 | 
			
		||||
  selectMenu(menu, values = []) {
 | 
			
		||||
    let selectMenu;
 | 
			
		||||
    if (/[0-4]/.test(menu)) {
 | 
			
		||||
      selectMenu = this.components[menu]?.components[0];
 | 
			
		||||
    } else {
 | 
			
		||||
      selectMenu = this.components
 | 
			
		||||
        .flatMap(row => row.components)
 | 
			
		||||
        .find(
 | 
			
		||||
          b =>
 | 
			
		||||
            ['STRING_SELECT', 'USER_SELECT', 'ROLE_SELECT', 'MENTIONABLE_SELECT', 'CHANNEL_SELECT'].includes(b.type) &&
 | 
			
		||||
            b.customId == menu &&
 | 
			
		||||
            !b.disabled,
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
    if (values.length < selectMenu.minValues) {
 | 
			
		||||
      throw new RangeError(`[SELECT_MENU_MIN_VALUES] The minimum number of values is ${selectMenu.minValues}`);
 | 
			
		||||
    }
 | 
			
		||||
    if (values.length > selectMenu.maxValues) {
 | 
			
		||||
      throw new RangeError(`[SELECT_MENU_MAX_VALUES] The maximum number of values is ${selectMenu.maxValues}`);
 | 
			
		||||
    }
 | 
			
		||||
    values = values.map(value => {
 | 
			
		||||
      switch (selectMenu.type) {
 | 
			
		||||
        case 'STRING_SELECT': {
 | 
			
		||||
          return selectMenu.options.find(obj => obj.value === value || obj.label === value).value;
 | 
			
		||||
        }
 | 
			
		||||
        case 'USER_SELECT': {
 | 
			
		||||
          return this.client.users.resolveId(value);
 | 
			
		||||
        }
 | 
			
		||||
        case 'ROLE_SELECT': {
 | 
			
		||||
          return this.guild.roles.resolveId(value);
 | 
			
		||||
        }
 | 
			
		||||
        case 'MENTIONABLE_SELECT': {
 | 
			
		||||
          return this.client.users.resolveId(value) || this.guild.roles.resolveId(value);
 | 
			
		||||
        }
 | 
			
		||||
        case 'CHANNEL_SELECT': {
 | 
			
		||||
          return this.client.channels.resolveId(value);
 | 
			
		||||
        }
 | 
			
		||||
        default: {
 | 
			
		||||
          return value;
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
    const nonce = SnowflakeUtil.generate();
 | 
			
		||||
    const data = {
 | 
			
		||||
      type: InteractionTypes.MESSAGE_COMPONENT,
 | 
			
		||||
      guild_id: this.guildId,
 | 
			
		||||
      channel_id: this.channelId,
 | 
			
		||||
      message_id: this.id,
 | 
			
		||||
      application_id: this.applicationId ?? this.author.id,
 | 
			
		||||
      session_id: this.client.ws.shards.first()?.sessionId,
 | 
			
		||||
      message_flags: this.flags.bitfield,
 | 
			
		||||
      data: {
 | 
			
		||||
        component_type: MessageComponentTypes[selectMenu.type],
 | 
			
		||||
        custom_id: selectMenu.customId,
 | 
			
		||||
        type: MessageComponentTypes[selectMenu.type],
 | 
			
		||||
        values,
 | 
			
		||||
      },
 | 
			
		||||
      nonce,
 | 
			
		||||
    };
 | 
			
		||||
    this.client.api.interactions.post({
 | 
			
		||||
      data,
 | 
			
		||||
    });
 | 
			
		||||
    return new Promise((resolve, reject) => {
 | 
			
		||||
      const timeoutMs = 5_000;
 | 
			
		||||
      // Waiting for MsgCreate / ModalCreate
 | 
			
		||||
      const handler = data => {
 | 
			
		||||
        // UnhandledPacket
 | 
			
		||||
        if (data.d?.nonce == nonce && data.t == 'INTERACTION_SUCCESS') {
 | 
			
		||||
          // Interaction#deferUpdate
 | 
			
		||||
          this.client.removeListener(Events.MESSAGE_CREATE, handler);
 | 
			
		||||
          this.client.removeListener(Events.UNHANDLED_PACKET, handler);
 | 
			
		||||
          this.client.removeListener(Events.INTERACTION_MODAL_CREATE, handler);
 | 
			
		||||
          resolve(this);
 | 
			
		||||
        }
 | 
			
		||||
        if (data.nonce !== nonce) return;
 | 
			
		||||
        clearTimeout(timeout);
 | 
			
		||||
        this.client.removeListener(Events.MESSAGE_CREATE, handler);
 | 
			
		||||
        this.client.removeListener(Events.UNHANDLED_PACKET, handler);
 | 
			
		||||
        this.client.removeListener(Events.INTERACTION_MODAL_CREATE, handler);
 | 
			
		||||
        this.client.decrementMaxListeners();
 | 
			
		||||
        resolve(data);
 | 
			
		||||
      };
 | 
			
		||||
      const timeout = setTimeout(() => {
 | 
			
		||||
        this.client.removeListener(Events.MESSAGE_CREATE, handler);
 | 
			
		||||
        this.client.removeListener(Events.UNHANDLED_PACKET, handler);
 | 
			
		||||
        this.client.removeListener(Events.INTERACTION_MODAL_CREATE, handler);
 | 
			
		||||
        this.client.decrementMaxListeners();
 | 
			
		||||
        reject(new Error('INTERACTION_FAILED'));
 | 
			
		||||
      }, timeoutMs).unref();
 | 
			
		||||
      this.client.incrementMaxListeners();
 | 
			
		||||
      this.client.on(Events.MESSAGE_CREATE, handler);
 | 
			
		||||
      this.client.on(Events.UNHANDLED_PACKET, handler);
 | 
			
		||||
      this.client.on(Events.INTERACTION_MODAL_CREATE, handler);
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Marks the message as unread.
 | 
			
		||||
   * @returns {Promise<boolean>}
 | 
			
		||||
   * @returns {Promise<void>}
 | 
			
		||||
   */
 | 
			
		||||
  async markUnread() {
 | 
			
		||||
    await this.client.api.channels[this.channelId].messages[this.id].ack.post({
 | 
			
		||||
  markUnread() {
 | 
			
		||||
    return this.client.api.channels[this.channelId].messages[this.id].ack.post({
 | 
			
		||||
      data: {
 | 
			
		||||
        manual: true,
 | 
			
		||||
        mention_count:
 | 
			
		||||
@@ -1042,145 +1188,18 @@ class Message extends Base {
 | 
			
		||||
            : 0,
 | 
			
		||||
      },
 | 
			
		||||
    });
 | 
			
		||||
    return true;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Marks the message as read.
 | 
			
		||||
   * @returns {Promise<boolean>}
 | 
			
		||||
   * @returns {Promise<void>}
 | 
			
		||||
   */
 | 
			
		||||
  async markRead() {
 | 
			
		||||
    await this.client.api.channels[this.channelId].messages[this.id].ack.post({
 | 
			
		||||
  markRead() {
 | 
			
		||||
    return this.client.api.channels[this.channelId].messages[this.id].ack.post({
 | 
			
		||||
      data: {
 | 
			
		||||
        token: null,
 | 
			
		||||
      },
 | 
			
		||||
    });
 | 
			
		||||
    return true;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * @typedef {Object} MessageButtonLocation
 | 
			
		||||
   * @property {number} row Index of the row
 | 
			
		||||
   * @property {number} col Index of the column
 | 
			
		||||
   */
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Click specific button or automatically click first button if no button is specified.
 | 
			
		||||
   * @param {MessageButton|MessageButtonLocation|string} button Button ID
 | 
			
		||||
   * @returns {Promise<InteractionResponse>}
 | 
			
		||||
   * @example
 | 
			
		||||
   * client.on('messageCreate', async message => {
 | 
			
		||||
   *  if (message.components.length) {
 | 
			
		||||
   *    // Find first button and click it
 | 
			
		||||
   *    await message.clickButton();
 | 
			
		||||
   *    // Click with button ID
 | 
			
		||||
   *    await message.clickButton('button-id');
 | 
			
		||||
   *    // Click with button location
 | 
			
		||||
   *    await message.clickButton({ row: 0, col: 0 });
 | 
			
		||||
   *    // Click with class MessageButton
 | 
			
		||||
   *    const button = message.components[0].components[0];
 | 
			
		||||
   *    await message.clickButton(button);
 | 
			
		||||
   *    // Click with class MessageButton (2)
 | 
			
		||||
   *    button.click(message);
 | 
			
		||||
   *  }
 | 
			
		||||
   * });
 | 
			
		||||
   */
 | 
			
		||||
  clickButton(button) {
 | 
			
		||||
    if (!button) {
 | 
			
		||||
      button = this.components.flatMap(row => row.components).find(b => b.type === 'BUTTON')?.customId;
 | 
			
		||||
    } else if (button instanceof MessageButton) {
 | 
			
		||||
      button = button.customId;
 | 
			
		||||
    } else if (typeof button === 'object') {
 | 
			
		||||
      if (!('row' in button) || !('col' in button)) throw new TypeError('INVALID_BUTTON_LOCATION');
 | 
			
		||||
      button = this.components[button.row]?.components[button.col]?.customId;
 | 
			
		||||
    }
 | 
			
		||||
    if (!button) throw new TypeError('BUTTON_NOT_FOUND');
 | 
			
		||||
    button = this.components.flatMap(row => row.components).find(b => b.customId === button && b.type === 'BUTTON');
 | 
			
		||||
    return button ? button.click(this) : Promise.reject(new TypeError('BUTTON_NOT_FOUND'));
 | 
			
		||||
  }
 | 
			
		||||
  /**
 | 
			
		||||
   * Select specific menu or First Menu
 | 
			
		||||
   * @param {MessageSelectMenu|string|number|Array<any>} menuID MenuId / MessageSelectMenu / Row of Menu / Array of Values (first menu)
 | 
			
		||||
   * @param {Array<any>} options Array of Values
 | 
			
		||||
   * @returns {Promise<InteractionResponse>}
 | 
			
		||||
   * @example
 | 
			
		||||
   * client.on('messageCreate', async message => {
 | 
			
		||||
   *  if (message.components.length) {
 | 
			
		||||
   *    // Row
 | 
			
		||||
   *    await message.selectMenu(1, [message.channel]); // row 1, type: Channel, multi: false
 | 
			
		||||
   *    // Id
 | 
			
		||||
   *    await message.selectMenu('menu-id', ['uid1', client.user, message.member]); // MenuId, type: User, multi: true
 | 
			
		||||
   *    // First Menu
 | 
			
		||||
   *    await message.selectMenu(['role-id']); // First Menu, type: role, multi: false
 | 
			
		||||
   *    // class MessageSelectMenu
 | 
			
		||||
   *    const menu = message.components[0].components[0];
 | 
			
		||||
   *    await message.selectMenu(menu, ['option1', 'option2']);
 | 
			
		||||
   *    // MessageSelectMenu (2)
 | 
			
		||||
   *    menu.select(message, ['option1', 'option2']);
 | 
			
		||||
   *  }
 | 
			
		||||
   * });
 | 
			
		||||
   */
 | 
			
		||||
  selectMenu(menuID, options = []) {
 | 
			
		||||
    if (!this.components[0]) throw new TypeError('MESSAGE_NO_COMPONENTS');
 | 
			
		||||
    if (menuID instanceof MessageSelectMenu) {
 | 
			
		||||
      //
 | 
			
		||||
    } else if (/[0-4]/.test(menuID)) {
 | 
			
		||||
      menuID = this.components[menuID]?.components[0];
 | 
			
		||||
    } else {
 | 
			
		||||
      const menuAll = this.components
 | 
			
		||||
        .flatMap(row => row.components)
 | 
			
		||||
        .filter(b =>
 | 
			
		||||
          [
 | 
			
		||||
            'STRING_SELECT',
 | 
			
		||||
            'USER_SELECT',
 | 
			
		||||
            'ROLE_SELECT',
 | 
			
		||||
            'MENTIONABLE_SELECT',
 | 
			
		||||
            'CHANNEL_SELECT',
 | 
			
		||||
            'SELECT_MENU',
 | 
			
		||||
          ].includes(b.type),
 | 
			
		||||
        );
 | 
			
		||||
      if (menuAll.length == 0) throw new TypeError('MENU_NOT_FOUND');
 | 
			
		||||
      if (menuID) {
 | 
			
		||||
        menuID = menuAll.find(b => b.customId === menuID);
 | 
			
		||||
      } else {
 | 
			
		||||
        menuID = menuAll[0];
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    if (!menuID.type.includes('SELECT')) throw new TypeError('MENU_NOT_FOUND');
 | 
			
		||||
    return menuID.select(this, Array.isArray(menuID) ? menuID : options);
 | 
			
		||||
  }
 | 
			
		||||
  //
 | 
			
		||||
  /**
 | 
			
		||||
   * Send context Menu v2
 | 
			
		||||
   * @param {Snowflake} botId Bot id
 | 
			
		||||
   * @param {string} commandName Command name in Context Menu
 | 
			
		||||
   * @returns {Promise<InteractionResponse>}
 | 
			
		||||
   */
 | 
			
		||||
  async contextMenu(botId, commandName) {
 | 
			
		||||
    if (!botId) throw new Error('Bot ID is required');
 | 
			
		||||
    const user = await this.client.users.fetch(botId).catch(() => {});
 | 
			
		||||
    if (!user || !user.bot || !user.application) {
 | 
			
		||||
      throw new Error('BotID is not a bot or does not have an application slash command');
 | 
			
		||||
    }
 | 
			
		||||
    if (!commandName || typeof commandName !== 'string') {
 | 
			
		||||
      throw new Error('Command name is required');
 | 
			
		||||
    }
 | 
			
		||||
    let contextCMD;
 | 
			
		||||
    const data = await this.channel.searchInteraction(botId, 'MESSAGE');
 | 
			
		||||
    for (const command of data.application_commands) {
 | 
			
		||||
      user.application?.commands?._add(command, true);
 | 
			
		||||
    }
 | 
			
		||||
    contextCMD = user.application?.commands?.cache.find(c => c.name == commandName && c.type === 'MESSAGE');
 | 
			
		||||
    if (!contextCMD) {
 | 
			
		||||
      throw new Error(
 | 
			
		||||
        'INTERACTION_SEND_FAILURE',
 | 
			
		||||
        `Command ${commandName} is not found (with search)\nList command avalible: ${user.application?.commands?.cache
 | 
			
		||||
          .filter(a => a.type == 'MESSAGE')
 | 
			
		||||
          .map(a => a.name)
 | 
			
		||||
          .join(', ')}`,
 | 
			
		||||
      );
 | 
			
		||||
    }
 | 
			
		||||
    return contextCMD.sendContextMenu(this, true);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -31,7 +31,7 @@ class MessageReaction {
 | 
			
		||||
    this.me = data.me || data.me_burst;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Super reaction
 | 
			
		||||
     * Is super reaction
 | 
			
		||||
     * @type {boolean}
 | 
			
		||||
     */
 | 
			
		||||
    this.isBurst = Boolean(data.me_burst || data.burst);
 | 
			
		||||
 
 | 
			
		||||
@@ -86,11 +86,15 @@ class VoiceState extends Base {
 | 
			
		||||
 | 
			
		||||
    // The self_stream is property is omitted if false, check for another property
 | 
			
		||||
    // here to avoid incorrectly clearing this when partial data is specified
 | 
			
		||||
    /**
 | 
			
		||||
     * Whether this member is streaming using "Screen Share"
 | 
			
		||||
     * @type {boolean}
 | 
			
		||||
     */
 | 
			
		||||
    this.streaming = data.self_stream ?? false;
 | 
			
		||||
    if ('self_stream' in data) {
 | 
			
		||||
      /**
 | 
			
		||||
       * Whether this member is streaming using "Screen Share"
 | 
			
		||||
       * @type {boolean}
 | 
			
		||||
       */
 | 
			
		||||
      this.streaming = data.self_stream ?? false;
 | 
			
		||||
    } else {
 | 
			
		||||
      this.streaming ??= null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if ('channel_id' in data) {
 | 
			
		||||
      /**
 | 
			
		||||
@@ -144,12 +148,11 @@ class VoiceState extends Base {
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * The channel that the member is connected to
 | 
			
		||||
   * @type {?(VoiceChannel|StageChannel)}
 | 
			
		||||
   * @type {?(VoiceChannel|StageChannel|DMChannel|GroupDMChannel)}
 | 
			
		||||
   * @readonly
 | 
			
		||||
   */
 | 
			
		||||
  get channel() {
 | 
			
		||||
    if (!this.guild?.id) return this.guild.client.channels.cache.get(this.channelId) ?? null;
 | 
			
		||||
    return this.guild.channels.cache.get(this.channelId) ?? null;
 | 
			
		||||
    return (this.guild || this.client).channels.cache.get(this.channelId) ?? null;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
@@ -177,7 +180,6 @@ class VoiceState extends Base {
 | 
			
		||||
   * @returns {Promise<GuildMember>}
 | 
			
		||||
   */
 | 
			
		||||
  setMute(mute = true, reason) {
 | 
			
		||||
    if (!this.guild?.id) return null;
 | 
			
		||||
    return this.guild.members.edit(this.id, { mute }, reason);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@@ -188,7 +190,6 @@ class VoiceState extends Base {
 | 
			
		||||
   * @returns {Promise<GuildMember>}
 | 
			
		||||
   */
 | 
			
		||||
  setDeaf(deaf = true, reason) {
 | 
			
		||||
    if (!this.guild?.id) return null;
 | 
			
		||||
    return this.guild.members.edit(this.id, { deaf }, reason);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@@ -198,7 +199,6 @@ class VoiceState extends Base {
 | 
			
		||||
   * @returns {Promise<GuildMember>}
 | 
			
		||||
   */
 | 
			
		||||
  disconnect(reason) {
 | 
			
		||||
    if (!this.guild?.id) return this.callVoice?.disconnect();
 | 
			
		||||
    return this.setChannel(null, reason);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@@ -210,36 +210,9 @@ class VoiceState extends Base {
 | 
			
		||||
   * @returns {Promise<GuildMember>}
 | 
			
		||||
   */
 | 
			
		||||
  setChannel(channel, reason) {
 | 
			
		||||
    if (!this.guild?.id) return null;
 | 
			
		||||
    return this.guild.members.edit(this.id, { channel }, reason);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Sets the status of the voice channel
 | 
			
		||||
   * @param {string} status The message to set the channel status to
 | 
			
		||||
   * @example
 | 
			
		||||
   * // Setting the status to something
 | 
			
		||||
   * guild.members.me.voice.setStatus("something")
 | 
			
		||||
   * @example
 | 
			
		||||
   * // Removing the status
 | 
			
		||||
   * guild.members.me.voice.setStatus("")
 | 
			
		||||
   * @returns {Promise<void>}
 | 
			
		||||
   */
 | 
			
		||||
  async setStatus(status) {
 | 
			
		||||
    // PUT https://discord.com/api/v9/channels/channelID/voice-status
 | 
			
		||||
    if (this.channel?.id) {
 | 
			
		||||
      // You can set the staus in normal voice channels and in stages so any type starting with GUILD should work
 | 
			
		||||
      if (!this.channel?.type.startsWith('GUILD')) throw new Error('VOICE_NOT_IN_GUILD');
 | 
			
		||||
 | 
			
		||||
      await this.client.api.channels(this.channel.id, 'voice-status').put({
 | 
			
		||||
        data: {
 | 
			
		||||
          status,
 | 
			
		||||
        },
 | 
			
		||||
        versioned: true,
 | 
			
		||||
      });
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Toggles the request to speak in the channel.
 | 
			
		||||
   * Only applicable for stage channels and for the client's own voice state.
 | 
			
		||||
@@ -253,18 +226,16 @@ class VoiceState extends Base {
 | 
			
		||||
   * @returns {Promise<void>}
 | 
			
		||||
   */
 | 
			
		||||
  async setRequestToSpeak(request = true) {
 | 
			
		||||
    if (this.guild?.id) {
 | 
			
		||||
      if (this.channel?.type !== 'GUILD_STAGE_VOICE') throw new Error('VOICE_NOT_STAGE_CHANNEL');
 | 
			
		||||
    if (this.channel?.type !== 'GUILD_STAGE_VOICE') throw new Error('VOICE_NOT_STAGE_CHANNEL');
 | 
			
		||||
 | 
			
		||||
      if (this.client.user.id !== this.id) throw new Error('VOICE_STATE_NOT_OWN');
 | 
			
		||||
    if (this.client.user.id !== this.id) throw new Error('VOICE_STATE_NOT_OWN');
 | 
			
		||||
 | 
			
		||||
      await this.client.api.guilds(this.guild.id, 'voice-states', '@me').patch({
 | 
			
		||||
        data: {
 | 
			
		||||
          channel_id: this.channelId,
 | 
			
		||||
          request_to_speak_timestamp: request ? new Date().toISOString() : null,
 | 
			
		||||
        },
 | 
			
		||||
      });
 | 
			
		||||
    }
 | 
			
		||||
    await this.client.api.guilds(this.guild.id, 'voice-states', '@me').patch({
 | 
			
		||||
      data: {
 | 
			
		||||
        channel_id: this.channelId,
 | 
			
		||||
        request_to_speak_timestamp: request ? new Date().toISOString() : null,
 | 
			
		||||
      },
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
@@ -285,21 +256,39 @@ class VoiceState extends Base {
 | 
			
		||||
   * @returns {Promise<void>}
 | 
			
		||||
   */
 | 
			
		||||
  async setSuppressed(suppressed = true) {
 | 
			
		||||
    if (this.guild?.id) {
 | 
			
		||||
      if (typeof suppressed !== 'boolean') throw new TypeError('VOICE_STATE_INVALID_TYPE', 'suppressed');
 | 
			
		||||
    if (typeof suppressed !== 'boolean') throw new TypeError('VOICE_STATE_INVALID_TYPE', 'suppressed');
 | 
			
		||||
 | 
			
		||||
      if (this.channel?.type !== 'GUILD_STAGE_VOICE') throw new Error('VOICE_NOT_STAGE_CHANNEL');
 | 
			
		||||
    if (this.channel?.type !== 'GUILD_STAGE_VOICE') throw new Error('VOICE_NOT_STAGE_CHANNEL');
 | 
			
		||||
 | 
			
		||||
      const target = this.client.user.id === this.id ? '@me' : this.id;
 | 
			
		||||
    const target = this.client.user.id === this.id ? '@me' : this.id;
 | 
			
		||||
 | 
			
		||||
      await this.client.api.guilds(this.guild.id, 'voice-states', target).patch({
 | 
			
		||||
        data: {
 | 
			
		||||
          channel_id: this.channelId,
 | 
			
		||||
          suppress: suppressed,
 | 
			
		||||
          request_to_speak_timestamp: null,
 | 
			
		||||
        },
 | 
			
		||||
      });
 | 
			
		||||
    }
 | 
			
		||||
    await this.client.api.guilds(this.guild.id, 'voice-states', target).patch({
 | 
			
		||||
      data: {
 | 
			
		||||
        channel_id: this.channelId,
 | 
			
		||||
        suppress: suppressed,
 | 
			
		||||
        request_to_speak_timestamp: null,
 | 
			
		||||
      },
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Sets the status of the voice channel
 | 
			
		||||
   * @param {string} [status=""] The message to set the channel status to
 | 
			
		||||
   * @example
 | 
			
		||||
   * // Setting the status to something
 | 
			
		||||
   * guild.members.me.voice.setStatus("something")
 | 
			
		||||
   * @example
 | 
			
		||||
   * // Removing the status
 | 
			
		||||
   * guild.members.me.voice.setStatus()
 | 
			
		||||
   * @returns {Promise<void>}
 | 
			
		||||
   */
 | 
			
		||||
  setStatus(status = '') {
 | 
			
		||||
    // PUT https://discord.com/api/v9/channels/:id/voice-status
 | 
			
		||||
    return this.client.api.channels(this.channel.id, 'voice-status').put({
 | 
			
		||||
      data: {
 | 
			
		||||
        status,
 | 
			
		||||
      },
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
@@ -322,19 +311,18 @@ class VoiceState extends Base {
 | 
			
		||||
   * @param {string} base64Image Base64 URI (data:image/jpeg;base64,data)
 | 
			
		||||
   * @returns {Promise<void>}
 | 
			
		||||
   */
 | 
			
		||||
  async postPreview(base64Image) {
 | 
			
		||||
  postPreview(base64Image) {
 | 
			
		||||
    if (!this.client.user.id === this.id || !this.streaming) throw new Error('USER_NOT_STREAMING');
 | 
			
		||||
    // URL: https://discord.com/api/v9/streams/guild:guildid:voicechannelid:userid/preview
 | 
			
		||||
    // URL: https://discord.com/api/v9/streams/call:channelId:userId/preview
 | 
			
		||||
    const streamKey = this.guild.id
 | 
			
		||||
      ? `guild:${this.guild.id}:${this.channelId}:${this.id}`
 | 
			
		||||
      : `call:${this.channelId}:${this.id}`;
 | 
			
		||||
    await this.client.api.streams[encodeURIComponent(streamKey)].preview.post({
 | 
			
		||||
    return this.client.api.streams[encodeURIComponent(streamKey)].preview.post({
 | 
			
		||||
      data: {
 | 
			
		||||
        thumbnail: base64Image,
 | 
			
		||||
      },
 | 
			
		||||
    });
 | 
			
		||||
    return true;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  toJSON() {
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user