update
This commit is contained in:
		@@ -1,115 +0,0 @@
 | 
				
			|||||||
'use strict';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const process = require('node:process');
 | 
					 | 
				
			||||||
const Action = require('./Action');
 | 
					 | 
				
			||||||
const AutocompleteInteraction = require('../../structures/AutocompleteInteraction');
 | 
					 | 
				
			||||||
const ButtonInteraction = require('../../structures/ButtonInteraction');
 | 
					 | 
				
			||||||
const CommandInteraction = require('../../structures/CommandInteraction');
 | 
					 | 
				
			||||||
const MessageContextMenuInteraction = require('../../structures/MessageContextMenuInteraction');
 | 
					 | 
				
			||||||
const ModalSubmitInteraction = require('../../structures/ModalSubmitInteraction');
 | 
					 | 
				
			||||||
const {
 | 
					 | 
				
			||||||
  ChannelSelectInteraction,
 | 
					 | 
				
			||||||
  MentionableSelectInteraction,
 | 
					 | 
				
			||||||
  RoleSelectInteraction,
 | 
					 | 
				
			||||||
  SelectMenuInteraction,
 | 
					 | 
				
			||||||
  UserSelectInteraction,
 | 
					 | 
				
			||||||
} = require('../../structures/SelectMenuInteraction');
 | 
					 | 
				
			||||||
const UserContextMenuInteraction = require('../../structures/UserContextMenuInteraction');
 | 
					 | 
				
			||||||
const { Events, InteractionTypes, MessageComponentTypes, ApplicationCommandTypes } = require('../../util/Constants');
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
let deprecationEmitted = false;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class InteractionCreateAction extends Action {
 | 
					 | 
				
			||||||
  handle(data) {
 | 
					 | 
				
			||||||
    const client = this.client;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // Resolve and cache partial channels for Interaction#channel getter
 | 
					 | 
				
			||||||
    const channel = this.getChannel(data);
 | 
					 | 
				
			||||||
    // Do not emit this for interactions that cache messages that are non-text-based.
 | 
					 | 
				
			||||||
    let InteractionType;
 | 
					 | 
				
			||||||
    switch (data.type) {
 | 
					 | 
				
			||||||
      case InteractionTypes.APPLICATION_COMMAND:
 | 
					 | 
				
			||||||
        switch (data.data.type) {
 | 
					 | 
				
			||||||
          case ApplicationCommandTypes.CHAT_INPUT:
 | 
					 | 
				
			||||||
            InteractionType = CommandInteraction;
 | 
					 | 
				
			||||||
            break;
 | 
					 | 
				
			||||||
          case ApplicationCommandTypes.USER:
 | 
					 | 
				
			||||||
            InteractionType = UserContextMenuInteraction;
 | 
					 | 
				
			||||||
            break;
 | 
					 | 
				
			||||||
          case ApplicationCommandTypes.MESSAGE:
 | 
					 | 
				
			||||||
            InteractionType = MessageContextMenuInteraction;
 | 
					 | 
				
			||||||
            break;
 | 
					 | 
				
			||||||
          default:
 | 
					 | 
				
			||||||
            client.emit(
 | 
					 | 
				
			||||||
              Events.DEBUG,
 | 
					 | 
				
			||||||
              `[INTERACTION] Received application command interaction with unknown type: ${data.data.type}`,
 | 
					 | 
				
			||||||
            );
 | 
					 | 
				
			||||||
            return;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        break;
 | 
					 | 
				
			||||||
      case InteractionTypes.MESSAGE_COMPONENT:
 | 
					 | 
				
			||||||
        if (channel && !channel.isText()) return;
 | 
					 | 
				
			||||||
        switch (data.data.component_type) {
 | 
					 | 
				
			||||||
          case MessageComponentTypes.BUTTON:
 | 
					 | 
				
			||||||
            InteractionType = ButtonInteraction;
 | 
					 | 
				
			||||||
            break;
 | 
					 | 
				
			||||||
          case MessageComponentTypes.STRING_SELECT:
 | 
					 | 
				
			||||||
            InteractionType = SelectMenuInteraction;
 | 
					 | 
				
			||||||
            break;
 | 
					 | 
				
			||||||
          case MessageComponentTypes.CHANNEL_SELECT:
 | 
					 | 
				
			||||||
            InteractionType = ChannelSelectInteraction;
 | 
					 | 
				
			||||||
            break;
 | 
					 | 
				
			||||||
          case MessageComponentTypes.MENTIONABLE_SELECT:
 | 
					 | 
				
			||||||
            InteractionType = MentionableSelectInteraction;
 | 
					 | 
				
			||||||
            break;
 | 
					 | 
				
			||||||
          case MessageComponentTypes.ROLE_SELECT:
 | 
					 | 
				
			||||||
            InteractionType = RoleSelectInteraction;
 | 
					 | 
				
			||||||
            break;
 | 
					 | 
				
			||||||
          case MessageComponentTypes.USER_SELECT:
 | 
					 | 
				
			||||||
            InteractionType = UserSelectInteraction;
 | 
					 | 
				
			||||||
            break;
 | 
					 | 
				
			||||||
          default:
 | 
					 | 
				
			||||||
            client.emit(
 | 
					 | 
				
			||||||
              Events.DEBUG,
 | 
					 | 
				
			||||||
              `[INTERACTION] Received component interaction with unknown type: ${data.data.component_type}`,
 | 
					 | 
				
			||||||
            );
 | 
					 | 
				
			||||||
            return;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        break;
 | 
					 | 
				
			||||||
      case InteractionTypes.APPLICATION_COMMAND_AUTOCOMPLETE:
 | 
					 | 
				
			||||||
        InteractionType = AutocompleteInteraction;
 | 
					 | 
				
			||||||
        break;
 | 
					 | 
				
			||||||
      case InteractionTypes.MODAL_SUBMIT:
 | 
					 | 
				
			||||||
        InteractionType = ModalSubmitInteraction;
 | 
					 | 
				
			||||||
        break;
 | 
					 | 
				
			||||||
      default:
 | 
					 | 
				
			||||||
        client.emit(
 | 
					 | 
				
			||||||
          Events.DEBUG,
 | 
					 | 
				
			||||||
          `[INTERACTION] Received [BOT] / Send (Selfbot) interactionID ${data.id} with unknown type: ${data.type}`,
 | 
					 | 
				
			||||||
        );
 | 
					 | 
				
			||||||
        return;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    const interaction = new InteractionType(client, data);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * Emitted when an interaction is created.
 | 
					 | 
				
			||||||
     * @event Client#interactionCreate
 | 
					 | 
				
			||||||
     * @param {InteractionResponseBody | Interaction} interaction The interaction which was created.
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    client.emit(Events.INTERACTION_CREATE, interaction);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * Emitted when an interaction is created.
 | 
					 | 
				
			||||||
     * @event Client#interaction
 | 
					 | 
				
			||||||
     * @param {Interaction} interaction The interaction which was created
 | 
					 | 
				
			||||||
     * @deprecated Use {@link Client#event:interactionCreate} instead
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    if (client.emit('interaction', interaction) && !deprecationEmitted) {
 | 
					 | 
				
			||||||
      deprecationEmitted = true;
 | 
					 | 
				
			||||||
      process.emitWarning('The interaction event is deprecated. Use interactionCreate instead', 'DeprecationWarning');
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
module.exports = InteractionCreateAction;
 | 
					 | 
				
			||||||
@@ -1,16 +0,0 @@
 | 
				
			|||||||
'use strict';
 | 
					 | 
				
			||||||
const { Events } = require('../../../util/Constants');
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
 * @typedef {Object} InteractionResponseBody
 | 
					 | 
				
			||||||
 * @property {Snowflake} id Interaction ID
 | 
					 | 
				
			||||||
 * @property {Snowflake} nonce nonce in POST /interactions
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
module.exports = (client, { d: data }) => {
 | 
					 | 
				
			||||||
  if (client.user.bot) {
 | 
					 | 
				
			||||||
    client.actions.InteractionCreate.handle(data);
 | 
					 | 
				
			||||||
  } else {
 | 
					 | 
				
			||||||
    client.emit(Events.INTERACTION_CREATE, data);
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
@@ -1,18 +0,0 @@
 | 
				
			|||||||
'use strict';
 | 
					 | 
				
			||||||
const { Events } = require('../../../util/Constants');
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
module.exports = (client, { d: data }) => {
 | 
					 | 
				
			||||||
  /**
 | 
					 | 
				
			||||||
   * Emitted whenever client user send interaction and error
 | 
					 | 
				
			||||||
   * @event Client#interactionFailure
 | 
					 | 
				
			||||||
   * @param {InteractionResponseBody} data data
 | 
					 | 
				
			||||||
   */
 | 
					 | 
				
			||||||
  client.emit(Events.INTERACTION_FAILURE, data);
 | 
					 | 
				
			||||||
  client.emit('interactionResponse', {
 | 
					 | 
				
			||||||
    status: false,
 | 
					 | 
				
			||||||
    metadata: client._interactionCache.get(data.nonce),
 | 
					 | 
				
			||||||
    error: 'No response from bot',
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
  // Delete cache
 | 
					 | 
				
			||||||
  client._interactionCache.delete(data.nonce);
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
@@ -1,6 +1,7 @@
 | 
				
			|||||||
'use strict';
 | 
					'use strict';
 | 
				
			||||||
const Modal = require('../../../structures/Modal');
 | 
					const Modal = require('../../../structures/Modal');
 | 
				
			||||||
const { Events } = require('../../../util/Constants');
 | 
					const { Events } = require('../../../util/Constants');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
module.exports = (client, { d: data }) => {
 | 
					module.exports = (client, { d: data }) => {
 | 
				
			||||||
  /**
 | 
					  /**
 | 
				
			||||||
   * Emitted whenever client user receive interaction.showModal()
 | 
					   * Emitted whenever client user receive interaction.showModal()
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,30 +0,0 @@
 | 
				
			|||||||
'use strict';
 | 
					 | 
				
			||||||
const { Events } = require('../../../util/Constants');
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
module.exports = (client, { d: data }) => {
 | 
					 | 
				
			||||||
  /**
 | 
					 | 
				
			||||||
   * Emitted whenever client user send interaction and success
 | 
					 | 
				
			||||||
   * @event Client#interactionSuccess
 | 
					 | 
				
			||||||
   * @param {InteractionResponseBody} data data
 | 
					 | 
				
			||||||
   */
 | 
					 | 
				
			||||||
  client.emit(Events.INTERACTION_SUCCESS, data);
 | 
					 | 
				
			||||||
  // Get channel data
 | 
					 | 
				
			||||||
  const cache = client._interactionCache.get(data.nonce);
 | 
					 | 
				
			||||||
  if (!cache) return;
 | 
					 | 
				
			||||||
  const channel = cache.guildId
 | 
					 | 
				
			||||||
    ? client.guilds.cache.get(cache.guildId)?.channels.cache.get(cache.channelId)
 | 
					 | 
				
			||||||
    : client.channels.cache.get(cache.channelId);
 | 
					 | 
				
			||||||
  // Set data
 | 
					 | 
				
			||||||
  const interaction = {
 | 
					 | 
				
			||||||
    ...cache,
 | 
					 | 
				
			||||||
    ...data,
 | 
					 | 
				
			||||||
  };
 | 
					 | 
				
			||||||
  const data_ = channel.interactions._add(interaction);
 | 
					 | 
				
			||||||
  client.emit('interactionResponse', {
 | 
					 | 
				
			||||||
    status: true,
 | 
					 | 
				
			||||||
    metadata: data_,
 | 
					 | 
				
			||||||
    error: 'Success',
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
  // Delete cache
 | 
					 | 
				
			||||||
  // client._interactionCache.delete(data.nonce);
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
@@ -1,17 +1,19 @@
 | 
				
			|||||||
'use strict';
 | 
					'use strict';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const { Events, RelationshipTypes } = require('../../../util/Constants');
 | 
					const { Events } = require('../../../util/Constants');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
module.exports = (client, { d: data }) => {
 | 
					module.exports = (client, { d: data }) => {
 | 
				
			||||||
  if (data.user) {
 | 
					  if (data.user) {
 | 
				
			||||||
    client.users._add(data.user);
 | 
					    client.users._add(data.user);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  client.relationships.cache.set(data.id, data.type);
 | 
					  client.relationships.cache.set(data.id, data.type);
 | 
				
			||||||
 | 
					  client.relationships.friendNicknames.set(data.id, data.nickname);
 | 
				
			||||||
 | 
					  client.relationships.sinceCache.set(data.id, new Date(data.since || 0));
 | 
				
			||||||
  /**
 | 
					  /**
 | 
				
			||||||
   * Emitted whenever a relationship is updated.
 | 
					   * Emitted when a relationship is created, relevant to the current user.
 | 
				
			||||||
   * @event Client#relationshipAdd
 | 
					   * @event Client#relationshipAdd
 | 
				
			||||||
   * @param {Snowflake} user The userID that was updated
 | 
					   * @param {Snowflake} user Target userId
 | 
				
			||||||
   * @param {RelationshipTypes} type The new relationship type
 | 
					   * @param {boolean} shouldNotify Whether the client should notify the user of this relationship's creation
 | 
				
			||||||
   */
 | 
					   */
 | 
				
			||||||
  client.emit(Events.RELATIONSHIP_ADD, data.id, RelationshipTypes[data.type]);
 | 
					  client.emit(Events.RELATIONSHIP_ADD, data.id, Boolean(data.should_notify));
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,15 +1,17 @@
 | 
				
			|||||||
'use strict';
 | 
					'use strict';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const { Events, RelationshipTypes } = require('../../../util/Constants');
 | 
					const { Events } = require('../../../util/Constants');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
module.exports = (client, { d: data }) => {
 | 
					module.exports = (client, { d: data }) => {
 | 
				
			||||||
  client.relationships.cache.delete(data.id);
 | 
					  client.relationships.cache.delete(data.id);
 | 
				
			||||||
  client.user.friendNicknames.delete(data.id);
 | 
					  client.relationships.friendNicknames.delete(data.id);
 | 
				
			||||||
 | 
					  client.relationships.sinceCache.delete(data.id);
 | 
				
			||||||
  /**
 | 
					  /**
 | 
				
			||||||
   * Emitted whenever a relationship is delete.
 | 
					   * Emitted when a relationship is removed, relevant to the current user.
 | 
				
			||||||
   * @event Client#relationshipRemove
 | 
					   * @event Client#relationshipRemove
 | 
				
			||||||
   * @param {Snowflake} user The userID that was updated
 | 
					   * @param {Snowflake} user The userID that was updated
 | 
				
			||||||
   * @param {RelationshipTypes} type The type of the old relationship
 | 
					   * @param {RelationshipTypes} type The type of the old relationship
 | 
				
			||||||
 | 
					   * @param {string | null} nickname The nickname of the user in this relationship (1-32 characters)
 | 
				
			||||||
   */
 | 
					   */
 | 
				
			||||||
  client.emit(Events.RELATIONSHIP_REMOVE, data.id, RelationshipTypes[data.type]);
 | 
					  client.emit(Events.RELATIONSHIP_REMOVE, data.id, data.type, data.nickname);
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,18 +1,41 @@
 | 
				
			|||||||
'use strict';
 | 
					'use strict';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const { Events, RelationshipTypes } = require('../../../util/Constants');
 | 
					const { Events } = require('../../../util/Constants');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
module.exports = (client, { d: data }) => {
 | 
					module.exports = (client, { d: data }) => {
 | 
				
			||||||
  client.relationships.cache.set(data.id, data.type);
 | 
					 | 
				
			||||||
  /**
 | 
					  /**
 | 
				
			||||||
   * Emitted whenever a relationship is updated.
 | 
					   * @typedef {Object} RelationshipUpdateObject
 | 
				
			||||||
 | 
					   * @property {RelationshipTypes} type The type of relationship
 | 
				
			||||||
 | 
					   * @property {Date} since When the user requested a relationship
 | 
				
			||||||
 | 
					   * @property {string | null} nickname The nickname of the user in this relationship (1-32 characters)
 | 
				
			||||||
 | 
					   */
 | 
				
			||||||
 | 
					  /**
 | 
				
			||||||
 | 
					   * Emitted when a relationship is updated, relevant to the current user (e.g. friend nickname changed).
 | 
				
			||||||
 | 
					   * <info>This is not sent when the type of a relationship changes; see {@link Client#relationshipAdd} and {@link Client#relationshipRemove} for that.</info>
 | 
				
			||||||
   * @event Client#relationshipUpdate
 | 
					   * @event Client#relationshipUpdate
 | 
				
			||||||
   * @param {Snowflake} user The userID that was updated
 | 
					   * @param {Snowflake} user The userID that was updated
 | 
				
			||||||
   * @param {RelationshipTypes} type The new relationship type
 | 
					   * @param {RelationshipUpdateObject} oldData Old data
 | 
				
			||||||
   * @param {Object} data The raw data
 | 
					   * @param {RelationshipUpdateObject} newData New data
 | 
				
			||||||
   */
 | 
					   */
 | 
				
			||||||
  if ('nickname' in data) {
 | 
					  const oldType = client.relationships.cache.get(data.id);
 | 
				
			||||||
    client.user.friendNicknames.set(data.id, data.nickname);
 | 
					  const oldSince = client.relationships.sinceCache.get(data.id);
 | 
				
			||||||
  }
 | 
					  const oldNickname = client.relationships.friendNicknames.get(data.id);
 | 
				
			||||||
  client.emit(Events.RELATIONSHIP_UPDATE, data.id, RelationshipTypes[data.type], data);
 | 
					  // Update
 | 
				
			||||||
 | 
					  if (data.type) client.relationships.cache.set(data.id, data.type);
 | 
				
			||||||
 | 
					  if (data.nickname) client.relationships.friendNicknames.set(data.id, data.nickname);
 | 
				
			||||||
 | 
					  if (data.since) client.relationships.sinceCache.set(data.id, new Date(data.since || 0));
 | 
				
			||||||
 | 
					  client.emit(
 | 
				
			||||||
 | 
					    Events.RELATIONSHIP_UPDATE,
 | 
				
			||||||
 | 
					    data.id,
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      type: oldType,
 | 
				
			||||||
 | 
					      nickname: oldNickname,
 | 
				
			||||||
 | 
					      since: oldSince,
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      type: data.type,
 | 
				
			||||||
 | 
					      nickname: data.nickname,
 | 
				
			||||||
 | 
					      since: new Date(data.since || 0),
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,7 +1,7 @@
 | 
				
			|||||||
'use strict';
 | 
					'use strict';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const Buffer = require('node:buffer').Buffer;
 | 
					 | 
				
			||||||
const { Collection } = require('@discordjs/collection');
 | 
					const { Collection } = require('@discordjs/collection');
 | 
				
			||||||
 | 
					const BaseManager = require('./BaseManager');
 | 
				
			||||||
const { GuildMember } = require('../structures/GuildMember');
 | 
					const { GuildMember } = require('../structures/GuildMember');
 | 
				
			||||||
const { Message } = require('../structures/Message');
 | 
					const { Message } = require('../structures/Message');
 | 
				
			||||||
const ThreadMember = require('../structures/ThreadMember');
 | 
					const ThreadMember = require('../structures/ThreadMember');
 | 
				
			||||||
@@ -11,19 +11,22 @@ const { RelationshipTypes } = require('../util/Constants');
 | 
				
			|||||||
/**
 | 
					/**
 | 
				
			||||||
 * Manages API methods for Relationships and stores their cache.
 | 
					 * Manages API methods for Relationships and stores their cache.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
class RelationshipManager {
 | 
					class RelationshipManager extends BaseManager {
 | 
				
			||||||
  constructor(client, users) {
 | 
					  constructor(client, users) {
 | 
				
			||||||
    /**
 | 
					    super(client);
 | 
				
			||||||
     * The client that instantiated this manager.
 | 
					 | 
				
			||||||
     * @type {Client}
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    this.client = client;
 | 
					 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * A collection of users this manager is caching. (Type: Number)
 | 
					     * A collection of users this manager is caching. (Type: Number)
 | 
				
			||||||
     * @type {Collection<Snowflake, RelationshipTypes>}
 | 
					     * @type {Collection<Snowflake, RelationshipTypes>}
 | 
				
			||||||
     * @readonly
 | 
					 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    this.cache = new Collection();
 | 
					    this.cache = new Collection();
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * @type {Collection<Snowflake, string>}
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    this.friendNicknames = new Collection();
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * @type {Collection<Snowflake, Date>}
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    this.sinceCache = new Collection();
 | 
				
			||||||
    this._setup(users);
 | 
					    this._setup(users);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -35,7 +38,7 @@ class RelationshipManager {
 | 
				
			|||||||
  get friendCache() {
 | 
					  get friendCache() {
 | 
				
			||||||
    const users = this.cache
 | 
					    const users = this.cache
 | 
				
			||||||
      .filter(value => value === RelationshipTypes.FRIEND)
 | 
					      .filter(value => value === RelationshipTypes.FRIEND)
 | 
				
			||||||
      .map((value, key) => [key, this.client.users.cache.get(key)]);
 | 
					      .map((_, key) => [key, this.client.users.cache.get(key)]);
 | 
				
			||||||
    return new Collection(users);
 | 
					    return new Collection(users);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -47,7 +50,7 @@ class RelationshipManager {
 | 
				
			|||||||
  get blockedCache() {
 | 
					  get blockedCache() {
 | 
				
			||||||
    const users = this.cache
 | 
					    const users = this.cache
 | 
				
			||||||
      .filter(value => value === RelationshipTypes.BLOCKED)
 | 
					      .filter(value => value === RelationshipTypes.BLOCKED)
 | 
				
			||||||
      .map((value, key) => [key, this.client.users.cache.get(key)]);
 | 
					      .map((_, key) => [key, this.client.users.cache.get(key)]);
 | 
				
			||||||
    return new Collection(users);
 | 
					    return new Collection(users);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -59,7 +62,7 @@ class RelationshipManager {
 | 
				
			|||||||
  get incomingCache() {
 | 
					  get incomingCache() {
 | 
				
			||||||
    const users = this.cache
 | 
					    const users = this.cache
 | 
				
			||||||
      .filter(value => value === RelationshipTypes.PENDING_INCOMING)
 | 
					      .filter(value => value === RelationshipTypes.PENDING_INCOMING)
 | 
				
			||||||
      .map((value, key) => [key, this.client.users.cache.get(key)]);
 | 
					      .map((_, key) => [key, this.client.users.cache.get(key)]);
 | 
				
			||||||
    return new Collection(users);
 | 
					    return new Collection(users);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -71,16 +74,29 @@ class RelationshipManager {
 | 
				
			|||||||
  get outgoingCache() {
 | 
					  get outgoingCache() {
 | 
				
			||||||
    const users = this.cache
 | 
					    const users = this.cache
 | 
				
			||||||
      .filter(value => value === RelationshipTypes.PENDING_OUTGOING)
 | 
					      .filter(value => value === RelationshipTypes.PENDING_OUTGOING)
 | 
				
			||||||
      .map((value, key) => [key, this.client.users.cache.get(key)]);
 | 
					      .map((_, key) => [key, this.client.users.cache.get(key)]);
 | 
				
			||||||
    return new Collection(users);
 | 
					    return new Collection(users);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /**
 | 
					  /**
 | 
				
			||||||
   * Return array of cache
 | 
					   * @typedef {Object} RelationshipJSONData
 | 
				
			||||||
   * @returns {Array<{id: Snowflake, type: RelationshipTypes}>}
 | 
					   * @property {Snowflake} id The ID of the target user
 | 
				
			||||||
 | 
					   * @property {RelationshipTypes} type The type of relationship
 | 
				
			||||||
 | 
					   * @property {string | null} nickname The nickname of the user in this relationship (1-32 characters)
 | 
				
			||||||
 | 
					   * @property {string} since When the user requested a relationship (ISO8601 timestamp)
 | 
				
			||||||
   */
 | 
					   */
 | 
				
			||||||
  toArray() {
 | 
					
 | 
				
			||||||
    return this.cache.map((value, key) => ({ id: key, type: RelationshipTypes[value] }));
 | 
					  /**
 | 
				
			||||||
 | 
					   * Return array of cache
 | 
				
			||||||
 | 
					   * @returns {RelationshipJSONData[]}
 | 
				
			||||||
 | 
					   */
 | 
				
			||||||
 | 
					  toJSON() {
 | 
				
			||||||
 | 
					    return this.cache.map((value, key) => ({
 | 
				
			||||||
 | 
					      id: key,
 | 
				
			||||||
 | 
					      type: RelationshipTypes[value],
 | 
				
			||||||
 | 
					      nickname: this.friendNicknames.get(key),
 | 
				
			||||||
 | 
					      since: this.sinceCache.get(key).toISOString(),
 | 
				
			||||||
 | 
					    }));
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /**
 | 
					  /**
 | 
				
			||||||
@@ -91,8 +107,9 @@ class RelationshipManager {
 | 
				
			|||||||
  _setup(users) {
 | 
					  _setup(users) {
 | 
				
			||||||
    if (!Array.isArray(users)) return;
 | 
					    if (!Array.isArray(users)) return;
 | 
				
			||||||
    for (const relationShip of users) {
 | 
					    for (const relationShip of users) {
 | 
				
			||||||
      this.client.user.friendNicknames.set(relationShip.id, relationShip.nickname);
 | 
					      this.friendNicknames.set(relationShip.id, relationShip.nickname);
 | 
				
			||||||
      this.cache.set(relationShip.id, relationShip.type);
 | 
					      this.cache.set(relationShip.id, relationShip.type);
 | 
				
			||||||
 | 
					      this.sinceCache.set(relationShip.id, new Date(relationShip.since || 0));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -133,66 +150,55 @@ class RelationshipManager {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /**
 | 
					  /**
 | 
				
			||||||
   * Deletes a friend relationship with a client user.
 | 
					   * Deletes a friend / blocked relationship with a client user or cancels a friend request.
 | 
				
			||||||
   * @param {UserResolvable} user Target
 | 
					   * @param {UserResolvable} user Target
 | 
				
			||||||
   * @returns {Promise<boolean>}
 | 
					   * @returns {Promise<boolean>}
 | 
				
			||||||
   */
 | 
					   */
 | 
				
			||||||
  deleteFriend(user) {
 | 
					  async deleteRelationship(user) {
 | 
				
			||||||
    const id = this.resolveId(user);
 | 
					    const id = this.resolveId(user);
 | 
				
			||||||
    // Check if already friends
 | 
					    if (
 | 
				
			||||||
    if (this.cache.get(id) !== RelationshipTypes.FRIEND) return false;
 | 
					      ![RelationshipTypes.FRIEND, RelationshipTypes.BLOCKED, RelationshipTypes.PENDING_OUTGOING].includes(
 | 
				
			||||||
    return this.__cancel(id);
 | 
					        this.cache.get(id),
 | 
				
			||||||
 | 
					      )
 | 
				
			||||||
 | 
					    ) {
 | 
				
			||||||
 | 
					      return Promise.resolve(false);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    await this.client.api.users['@me'].relationships[id].delete({
 | 
				
			||||||
 | 
					      DiscordContext: { location: 'Friends' },
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					    return true;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /**
 | 
					  /**
 | 
				
			||||||
   * Deletes a blocked relationship with a client user.
 | 
					   * @typedef {Object} FriendRequestOptions
 | 
				
			||||||
   * @param {UserResolvable} user Target
 | 
					   * @property {UserResolvable} [user] Target
 | 
				
			||||||
   * @returns {Promise<boolean>}
 | 
					   * @property {string} [username] Discord username
 | 
				
			||||||
 | 
					   * @property {number | null} [discriminator] Discord discriminator
 | 
				
			||||||
   */
 | 
					   */
 | 
				
			||||||
  deleteBlocked(user) {
 | 
					 | 
				
			||||||
    const id = this.resolveId(user);
 | 
					 | 
				
			||||||
    // Check if already blocked
 | 
					 | 
				
			||||||
    if (this.cache.get(id) !== RelationshipTypes.BLOCKED) return false;
 | 
					 | 
				
			||||||
    return this.__cancel(id);
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /**
 | 
					  /**
 | 
				
			||||||
   * Sends a friend request.
 | 
					   * Sends a friend request.
 | 
				
			||||||
   * @param {string} username Username of the user to send the request to
 | 
					   * @param {FriendRequestOptions} options Target
 | 
				
			||||||
   * @param {?number} discriminator Discriminator of the user to send the request to
 | 
					 | 
				
			||||||
   * @returns {Promise<boolean>}
 | 
					   * @returns {Promise<boolean>}
 | 
				
			||||||
   */
 | 
					   */
 | 
				
			||||||
  async sendFriendRequest(username, discriminator) {
 | 
					  async sendFriendRequest(options) {
 | 
				
			||||||
    await this.client.api.users('@me').relationships.post({
 | 
					    if (options?.user) {
 | 
				
			||||||
      data: {
 | 
					      const id = this.resolveId(options.user);
 | 
				
			||||||
        username,
 | 
					      await this.client.api.users['@me'].relationships[id].put({
 | 
				
			||||||
        discriminator: discriminator == 0 ? null : parseInt(discriminator),
 | 
					        data: {},
 | 
				
			||||||
      },
 | 
					        DiscordContext: { location: 'ContextMenu' },
 | 
				
			||||||
      headers: {
 | 
					      });
 | 
				
			||||||
        'X-Context-Properties': Buffer.from(JSON.stringify({ location: 'Add Friend' }), 'utf8').toString('base64'),
 | 
					      return true;
 | 
				
			||||||
      },
 | 
					    } else {
 | 
				
			||||||
    });
 | 
					      await this.client.api.users['@me'].relationships.post({
 | 
				
			||||||
    return true;
 | 
					        data: {
 | 
				
			||||||
  }
 | 
					          username: options.username,
 | 
				
			||||||
 | 
					          discriminator: options.discriminator,
 | 
				
			||||||
  /**
 | 
					        },
 | 
				
			||||||
   * Cancels a friend request.
 | 
					        DiscordContext: { location: 'Add Friend' },
 | 
				
			||||||
   * @param {UserResolvable} user  the user you want to delete
 | 
					      });
 | 
				
			||||||
   * @returns {Promise<boolean>}
 | 
					      return true;
 | 
				
			||||||
   */
 | 
					    }
 | 
				
			||||||
  cancelFriendRequest(user) {
 | 
					 | 
				
			||||||
    const id = this.resolveId(user);
 | 
					 | 
				
			||||||
    if (this.cache.get(id) !== RelationshipTypes.PENDING_OUTGOING) return false;
 | 
					 | 
				
			||||||
    return this.__cancel(id);
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  async __cancel(id) {
 | 
					 | 
				
			||||||
    await this.client.api.users['@me'].relationships[id].delete({
 | 
					 | 
				
			||||||
      headers: {
 | 
					 | 
				
			||||||
        'X-Context-Properties': Buffer.from(JSON.stringify({ location: 'Friends' }), 'utf8').toString('base64'),
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
    return true;
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /**
 | 
					  /**
 | 
				
			||||||
@@ -203,16 +209,14 @@ class RelationshipManager {
 | 
				
			|||||||
  async addFriend(user) {
 | 
					  async addFriend(user) {
 | 
				
			||||||
    const id = this.resolveId(user);
 | 
					    const id = this.resolveId(user);
 | 
				
			||||||
    // Check if already friends
 | 
					    // Check if already friends
 | 
				
			||||||
    if (this.cache.get(id) === RelationshipTypes.FRIEND) return false;
 | 
					    if (this.cache.get(id) === RelationshipTypes.FRIEND) return Promise.resolve(false);
 | 
				
			||||||
    // Check if outgoing request
 | 
					    // Check if outgoing request
 | 
				
			||||||
    if (this.cache.get(id) === RelationshipTypes.PENDING_OUTGOING) return false;
 | 
					    if (this.cache.get(id) === RelationshipTypes.PENDING_OUTGOING) return Promise.resolve(false);
 | 
				
			||||||
    await this.client.api.users['@me'].relationships[id].put({
 | 
					    await this.client.api.users['@me'].relationships[id].put({
 | 
				
			||||||
      data: {
 | 
					      data: {
 | 
				
			||||||
        type: RelationshipTypes.FRIEND,
 | 
					        type: RelationshipTypes.FRIEND,
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
      headers: {
 | 
					      DiscordContext: { location: 'Friends' },
 | 
				
			||||||
        'X-Context-Properties': Buffer.from(JSON.stringify({ location: 'Friends' }), 'utf8').toString('base64'),
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
    return true;
 | 
					    return true;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
@@ -223,14 +227,19 @@ class RelationshipManager {
 | 
				
			|||||||
   * @param {?string} nickname New nickname
 | 
					   * @param {?string} nickname New nickname
 | 
				
			||||||
   * @returns {Promise<boolean>}
 | 
					   * @returns {Promise<boolean>}
 | 
				
			||||||
   */
 | 
					   */
 | 
				
			||||||
  async setNickname(user, nickname) {
 | 
					  async setNickname(user, nickname = null) {
 | 
				
			||||||
    const id = this.resolveId(user);
 | 
					    const id = this.resolveId(user);
 | 
				
			||||||
    if (this.cache.get(id) !== RelationshipTypes.FRIEND) return false;
 | 
					    if (this.cache.get(id) !== RelationshipTypes.FRIEND) return Promise.resolve(false);
 | 
				
			||||||
    await this.client.api.users['@me'].relationships[id].patch({
 | 
					    await this.client.api.users['@me'].relationships[id].patch({
 | 
				
			||||||
      data: {
 | 
					      data: {
 | 
				
			||||||
        nickname: typeof nickname === 'string' ? nickname : null,
 | 
					        nickname: typeof nickname === 'string' ? nickname : null,
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					    if (nickname) {
 | 
				
			||||||
 | 
					      this.friendNicknames.set(id, nickname);
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      this.friendNicknames.delete(id);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    return true;
 | 
					    return true;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -242,14 +251,12 @@ class RelationshipManager {
 | 
				
			|||||||
  async addBlocked(user) {
 | 
					  async addBlocked(user) {
 | 
				
			||||||
    const id = this.resolveId(user);
 | 
					    const id = this.resolveId(user);
 | 
				
			||||||
    // Check
 | 
					    // Check
 | 
				
			||||||
    if (this.cache.get(id) === RelationshipTypes.BLOCKED) return false;
 | 
					    if (this.cache.get(id) === RelationshipTypes.BLOCKED) return Promise.resolve(false);
 | 
				
			||||||
    await this.client.api.users['@me'].relationships[id].put({
 | 
					    await this.client.api.users['@me'].relationships[id].put({
 | 
				
			||||||
      data: {
 | 
					      data: {
 | 
				
			||||||
        type: RelationshipTypes.BLOCKED,
 | 
					        type: RelationshipTypes.BLOCKED,
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
      headers: {
 | 
					      DiscordContext: { location: 'ContextMenu' },
 | 
				
			||||||
        'X-Context-Properties': Buffer.from(JSON.stringify({ location: 'ContextMenu' }), 'utf8').toString('base64'),
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
    return true;
 | 
					    return true;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,114 +0,0 @@
 | 
				
			|||||||
'use strict';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const { setTimeout } = require('node:timers');
 | 
					 | 
				
			||||||
const Base = require('./Base');
 | 
					 | 
				
			||||||
const { Events } = require('../util/Constants');
 | 
					 | 
				
			||||||
const SnowflakeUtil = require('../util/SnowflakeUtil');
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
 * Represents a interaction on Discord.
 | 
					 | 
				
			||||||
 * @extends {Base}
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
class InteractionResponse extends Base {
 | 
					 | 
				
			||||||
  constructor(client, data) {
 | 
					 | 
				
			||||||
    super(client);
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * The id of the channel the interaction was sent in
 | 
					 | 
				
			||||||
     * @type {Snowflake}
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    this.channelId = data.channelId;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * The id of the guild the interaction was sent in, if any
 | 
					 | 
				
			||||||
     * @type {?Snowflake}
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    this.guildId = data.guildId ?? this.channel?.guild?.id ?? null;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * The interaction data was sent in
 | 
					 | 
				
			||||||
     * @type {Object}
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    this.sendData = data.metadata;
 | 
					 | 
				
			||||||
    this._patch(data);
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  _patch(data) {
 | 
					 | 
				
			||||||
    if ('id' in data) {
 | 
					 | 
				
			||||||
      /**
 | 
					 | 
				
			||||||
       * The interaction response's ID
 | 
					 | 
				
			||||||
       * @type {Snowflake}
 | 
					 | 
				
			||||||
       */
 | 
					 | 
				
			||||||
      this.id = data.id;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    if ('nonce' in data) {
 | 
					 | 
				
			||||||
      /**
 | 
					 | 
				
			||||||
       * The interaction response's nonce
 | 
					 | 
				
			||||||
       * @type {Snowflake}
 | 
					 | 
				
			||||||
       */
 | 
					 | 
				
			||||||
      this.nonce = data.nonce;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
  /**
 | 
					 | 
				
			||||||
   * The timestamp the interaction response was created at
 | 
					 | 
				
			||||||
   * @type {number}
 | 
					 | 
				
			||||||
   * @readonly
 | 
					 | 
				
			||||||
   */
 | 
					 | 
				
			||||||
  get createdTimestamp() {
 | 
					 | 
				
			||||||
    return SnowflakeUtil.timestampFrom(this.id);
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  /**
 | 
					 | 
				
			||||||
   * The time the interaction response was created at
 | 
					 | 
				
			||||||
   * @type {Date}
 | 
					 | 
				
			||||||
   * @readonly
 | 
					 | 
				
			||||||
   */
 | 
					 | 
				
			||||||
  get createdAt() {
 | 
					 | 
				
			||||||
    return new Date(this.createdTimestamp);
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  /**
 | 
					 | 
				
			||||||
   * The channel that the interaction was sent in
 | 
					 | 
				
			||||||
   * @type {TextBasedChannels}
 | 
					 | 
				
			||||||
   * @readonly
 | 
					 | 
				
			||||||
   */
 | 
					 | 
				
			||||||
  get channel() {
 | 
					 | 
				
			||||||
    return this.client.channels.resolve(this.channelId);
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  /**
 | 
					 | 
				
			||||||
   * The guild the inteaaction was sent in (if in a guild channel)
 | 
					 | 
				
			||||||
   * @type {?Guild}
 | 
					 | 
				
			||||||
   * @readonly
 | 
					 | 
				
			||||||
   */
 | 
					 | 
				
			||||||
  get guild() {
 | 
					 | 
				
			||||||
    return this.client.guilds.resolve(this.guildId) ?? this.channel?.guild ?? null;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  /**
 | 
					 | 
				
			||||||
   * Get Modal send from interaction
 | 
					 | 
				
			||||||
   * @param {number} time Time to wait for modal
 | 
					 | 
				
			||||||
   * @returns {Modal}
 | 
					 | 
				
			||||||
   */
 | 
					 | 
				
			||||||
  awaitModal(time) {
 | 
					 | 
				
			||||||
    if (!time || typeof time !== 'number' || time < 0) throw new Error('INVALID_TIME');
 | 
					 | 
				
			||||||
    return new Promise((resolve, reject) => {
 | 
					 | 
				
			||||||
      const handler = modal => {
 | 
					 | 
				
			||||||
        timeout.refresh();
 | 
					 | 
				
			||||||
        if (modal.nonce != this.nonce || modal.id != this.id) return;
 | 
					 | 
				
			||||||
        clearTimeout(timeout);
 | 
					 | 
				
			||||||
        this.client.removeListener(Events.INTERACTION_MODAL_CREATE, handler);
 | 
					 | 
				
			||||||
        this.client.decrementMaxListeners();
 | 
					 | 
				
			||||||
        resolve(modal);
 | 
					 | 
				
			||||||
      };
 | 
					 | 
				
			||||||
      const timeout = setTimeout(() => {
 | 
					 | 
				
			||||||
        this.client.removeListener(Events.INTERACTION_MODAL_CREATE, handler);
 | 
					 | 
				
			||||||
        this.client.decrementMaxListeners();
 | 
					 | 
				
			||||||
        reject(new Error('MODAL_TIMEOUT'));
 | 
					 | 
				
			||||||
      }, time).unref();
 | 
					 | 
				
			||||||
      this.client.incrementMaxListeners();
 | 
					 | 
				
			||||||
      this.client.on(Events.INTERACTION_MODAL_CREATE, handler);
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
module.exports = InteractionResponse;
 | 
					 | 
				
			||||||
@@ -2,24 +2,15 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
const { setTimeout } = require('node:timers');
 | 
					const { setTimeout } = require('node:timers');
 | 
				
			||||||
const BaseMessageComponent = require('./BaseMessageComponent');
 | 
					const BaseMessageComponent = require('./BaseMessageComponent');
 | 
				
			||||||
const User = require('./User');
 | 
					const { InteractionTypes, Events } = require('../util/Constants');
 | 
				
			||||||
const SnowflakeUtil = require('../util/SnowflakeUtil');
 | 
					const SnowflakeUtil = require('../util/SnowflakeUtil');
 | 
				
			||||||
const Util = require('../util/Util');
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Represents a modal (form) to be shown in response to an interaction
 | 
					 * Represents a modal (form) to be shown in response to an interaction
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
class Modal {
 | 
					class Modal {
 | 
				
			||||||
  /**
 | 
					  /**
 | 
				
			||||||
   * @typedef {Object} ModalOptions
 | 
					   * @param {Object} data Modal to clone or raw data
 | 
				
			||||||
   * @property {string} [customId] A unique string to be sent in the interaction when clicked
 | 
					 | 
				
			||||||
   * @property {string} [title] The title to be displayed on this modal
 | 
					 | 
				
			||||||
   * @property {Array<(MessageActionRow|MessageActionRowOptions)>} [components]
 | 
					 | 
				
			||||||
   * Action rows containing interactive components for the modal (text input components)
 | 
					 | 
				
			||||||
   */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  /**
 | 
					 | 
				
			||||||
   * @param {Modal|ModalOptions} data Modal to clone or raw data
 | 
					 | 
				
			||||||
   * @param {Client} client The client constructing this Modal, if provided
 | 
					   * @param {Client} client The client constructing this Modal, if provided
 | 
				
			||||||
   */
 | 
					   */
 | 
				
			||||||
  constructor(data = {}, client = null) {
 | 
					  constructor(data = {}, client = null) {
 | 
				
			||||||
@@ -33,106 +24,69 @@ class Modal {
 | 
				
			|||||||
     * A unique string to be sent in the interaction when submitted
 | 
					     * A unique string to be sent in the interaction when submitted
 | 
				
			||||||
     * @type {?string}
 | 
					     * @type {?string}
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    this.customId = data.custom_id ?? data.customId ?? null;
 | 
					    this.customId = data.custom_id;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * The title to be displayed on this modal
 | 
					     * The title to be displayed on this modal
 | 
				
			||||||
     * @type {?string}
 | 
					     * @type {?string}
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    this.title = data.title ?? null;
 | 
					    this.title = data.title;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Timestamp (Discord epoch) of when this modal was created
 | 
					     * Timestamp (Discord epoch) of when this modal was created
 | 
				
			||||||
     * @type {?Snowflake}
 | 
					     * @type {Snowflake}
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    this.nonce = data.nonce ?? null;
 | 
					    this.nonce = data.nonce;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * ID slash / button / menu when modal is displayed
 | 
					     * ID slash / button / menu when modal is displayed
 | 
				
			||||||
     * @type {?Snowflake}
 | 
					     * @type {Snowflake}
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    this.id = data.id ?? null;
 | 
					    this.id = data.id;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Application sending the modal
 | 
					     * Application sending the modal
 | 
				
			||||||
     * @type {?Object}
 | 
					     * @type {Snowflake}
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    this.application = data.application
 | 
					    this.applicationId = data.application.id;
 | 
				
			||||||
      ? {
 | 
					 | 
				
			||||||
          ...data.application,
 | 
					 | 
				
			||||||
          bot: data.application.bot ? new User(client, data.application.bot, data.application) : null,
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
      : null;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    this.client = client;
 | 
					    /**
 | 
				
			||||||
 | 
					     * The id of the channel the message was sent in
 | 
				
			||||||
 | 
					     * @type {Snowflake}
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    this.channelId = data.channel_id;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Object.defineProperty(this, 'client', {
 | 
				
			||||||
 | 
					      value: client,
 | 
				
			||||||
 | 
					      writable: false,
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /**
 | 
					  /**
 | 
				
			||||||
   * Get Interaction Response
 | 
					   * The id of the guild the message was sent in, if any
 | 
				
			||||||
   * @type {?InteractionResponse}
 | 
					   * @type {?Snowflake}
 | 
				
			||||||
   * @readonly
 | 
					   * @readonly
 | 
				
			||||||
   */
 | 
					   */
 | 
				
			||||||
  get sendFromInteraction() {
 | 
					  get guildId() {
 | 
				
			||||||
    if (this.id && this.nonce && this.client) {
 | 
					    return this.client.channels.cache.get(this.channelId)?.guildId || null;
 | 
				
			||||||
      const cache = this.client._interactionCache.get(this.nonce);
 | 
					 | 
				
			||||||
      const channel = cache.guildId
 | 
					 | 
				
			||||||
        ? this.client.guilds.cache.get(cache.guildId)?.channels.cache.get(cache.channelId)
 | 
					 | 
				
			||||||
        : this.client.channels.cache.get(cache.channelId);
 | 
					 | 
				
			||||||
      return channel.interactions.cache.get(this.id);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    return null;
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /**
 | 
					  /**
 | 
				
			||||||
   * Adds components to the modal.
 | 
					   * The channel that the message was sent in
 | 
				
			||||||
   * @param {...MessageActionRowResolvable[]} components The components to add
 | 
					   * @type {TextBasedChannels}
 | 
				
			||||||
   * @returns {Modal}
 | 
					   * @readonly
 | 
				
			||||||
   */
 | 
					   */
 | 
				
			||||||
  addComponents(...components) {
 | 
					  get channel() {
 | 
				
			||||||
    this.components.push(...components.flat(Infinity).map(c => BaseMessageComponent.create(c)));
 | 
					    return this.client.channels.resolve(this.channelId);
 | 
				
			||||||
    return this;
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /**
 | 
					  /**
 | 
				
			||||||
   * Sets the components of the modal.
 | 
					   * The guild the message was sent in (if in a guild channel)
 | 
				
			||||||
   * @param {...MessageActionRowResolvable[]} components The components to set
 | 
					   * @type {?Guild}
 | 
				
			||||||
   * @returns {Modal}
 | 
					   * @readonly
 | 
				
			||||||
   */
 | 
					   */
 | 
				
			||||||
  setComponents(...components) {
 | 
					  get guild() {
 | 
				
			||||||
    this.spliceComponents(0, this.components.length, components);
 | 
					    return this.client.guilds.resolve(this.guildId) ?? this.channel?.guild ?? null;
 | 
				
			||||||
    return this;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  /**
 | 
					 | 
				
			||||||
   * Sets the custom id for this modal
 | 
					 | 
				
			||||||
   * @param {string} customId A unique string to be sent in the interaction when submitted
 | 
					 | 
				
			||||||
   * @returns {Modal}
 | 
					 | 
				
			||||||
   */
 | 
					 | 
				
			||||||
  setCustomId(customId) {
 | 
					 | 
				
			||||||
    this.customId = Util.verifyString(customId, RangeError, 'MODAL_CUSTOM_ID');
 | 
					 | 
				
			||||||
    return this;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  /**
 | 
					 | 
				
			||||||
   * Removes, replaces, and inserts components in the modal.
 | 
					 | 
				
			||||||
   * @param {number} index The index to start at
 | 
					 | 
				
			||||||
   * @param {number} deleteCount The number of components to remove
 | 
					 | 
				
			||||||
   * @param {...MessageActionRowResolvable[]} [components] The replacing components
 | 
					 | 
				
			||||||
   * @returns {Modal}
 | 
					 | 
				
			||||||
   */
 | 
					 | 
				
			||||||
  spliceComponents(index, deleteCount, ...components) {
 | 
					 | 
				
			||||||
    this.components.splice(index, deleteCount, ...components.flat(Infinity).map(c => BaseMessageComponent.create(c)));
 | 
					 | 
				
			||||||
    return this;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  /**
 | 
					 | 
				
			||||||
   * Sets the title of this modal
 | 
					 | 
				
			||||||
   * @param {string} title The title to be displayed on this modal
 | 
					 | 
				
			||||||
   * @returns {Modal}
 | 
					 | 
				
			||||||
   */
 | 
					 | 
				
			||||||
  setTitle(title) {
 | 
					 | 
				
			||||||
    this.title = Util.verifyString(title, RangeError, 'MODAL_TITLE');
 | 
					 | 
				
			||||||
    return this;
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  toJSON() {
 | 
					  toJSON() {
 | 
				
			||||||
@@ -144,136 +98,77 @@ class Modal {
 | 
				
			|||||||
    };
 | 
					    };
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /**
 | 
					 | 
				
			||||||
   * @typedef {Object} TextInputComponentReplyData
 | 
					 | 
				
			||||||
   * @property {string} [customId] TextInputComponent custom id
 | 
					 | 
				
			||||||
   * @property {string} [value] TextInputComponent value
 | 
					 | 
				
			||||||
   */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  /**
 | 
					 | 
				
			||||||
   * @typedef {Object} ModalReplyData
 | 
					 | 
				
			||||||
   * @property {?GuildResolvable} [guild] Guild to send the modal to
 | 
					 | 
				
			||||||
   * @property {?TextChannelResolvable} [channel] User to send the modal to
 | 
					 | 
				
			||||||
   * @property {?TextInputComponentReplyData[]} [data] Reply data
 | 
					 | 
				
			||||||
   */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  /**
 | 
					  /**
 | 
				
			||||||
   * Reply to this modal with data. (Event only)
 | 
					   * Reply to this modal with data. (Event only)
 | 
				
			||||||
   * @param  {ModalReplyData} data Data to send with the modal
 | 
					   * @returns {Promise<Message|Modal>}
 | 
				
			||||||
   * @returns {Promise<InteractionResponse>}
 | 
					 | 
				
			||||||
   * @example
 | 
					   * @example
 | 
				
			||||||
   * client.on('interactionModalCreate', modal => {
 | 
					   * client.on('interactionModalCreate', modal => {
 | 
				
			||||||
   * // 1.
 | 
					   *    // Modal > ActionRow > TextInput
 | 
				
			||||||
   *  modal.reply({
 | 
					   *    modal.components[0].components[0].setValue('1+1');
 | 
				
			||||||
   *     data: [
 | 
					   *    modal.components[1].components[0].setValue('hello');
 | 
				
			||||||
   *         {
 | 
					   *    modal.reply();
 | 
				
			||||||
   *             customId: 'code',
 | 
					 | 
				
			||||||
   *             value: '1+1'
 | 
					 | 
				
			||||||
   *         }, {
 | 
					 | 
				
			||||||
   *             customId: 'message',
 | 
					 | 
				
			||||||
   *             value: 'hello'
 | 
					 | 
				
			||||||
   *         }
 | 
					 | 
				
			||||||
   *     ],
 | 
					 | 
				
			||||||
   *    channel: 'id', // optional
 | 
					 | 
				
			||||||
   *    guild: 'id', // optional
 | 
					 | 
				
			||||||
   *  })
 | 
					 | 
				
			||||||
   * // or 2.
 | 
					 | 
				
			||||||
   * modal.components[0].components[0].setValue('1+1');
 | 
					 | 
				
			||||||
   * modal.components[1].components[0].setValue('hello');
 | 
					 | 
				
			||||||
   * modal.reply();
 | 
					 | 
				
			||||||
   * })
 | 
					   * })
 | 
				
			||||||
   */
 | 
					   */
 | 
				
			||||||
  async reply(data = {}) {
 | 
					  reply() {
 | 
				
			||||||
    if (!this.application) throw new Error('Modal cannot reply (Missing Application)');
 | 
					    if (!this.applicationId || !this.client || !this.channelId) throw new Error('Modal cannot reply');
 | 
				
			||||||
    const data_cache = this.sendFromInteraction;
 | 
					 | 
				
			||||||
    const guild = this.client.guilds.resolveId(data?.guild) || data_cache.guildId || null;
 | 
					 | 
				
			||||||
    const channel = this.client.channels.resolveId(data?.channel) || data_cache.channelId;
 | 
					 | 
				
			||||||
    if (!channel) throw new Error('Modal cannot reply (Missing data)');
 | 
					 | 
				
			||||||
    // Add data to components
 | 
					 | 
				
			||||||
    // this.components = [ MessageActionRow.components = [ TextInputComponent ] ]
 | 
					 | 
				
			||||||
    // 5 MessageActionRow / Modal, 1 TextInputComponent / 1 MessageActionRow
 | 
					 | 
				
			||||||
    if (Array.isArray(data?.data) && data?.data?.length > 0) {
 | 
					 | 
				
			||||||
      for (let i = 0; i < this.components.length; i++) {
 | 
					 | 
				
			||||||
        const value = data.data.find(d => d.customId == this.components[i].components[0].customId);
 | 
					 | 
				
			||||||
        if (this.components[i].components[0].required == true && !value) {
 | 
					 | 
				
			||||||
          throw new Error(
 | 
					 | 
				
			||||||
            'MODAL_REQUIRED_FIELD_MISSING\n' +
 | 
					 | 
				
			||||||
              `Required fieldId ${this.components[i].components[0].customId} missing value`,
 | 
					 | 
				
			||||||
          );
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        if (value) {
 | 
					 | 
				
			||||||
          if (value?.value?.includes('\n') && this.components[i].components[0].style == 'SHORT') {
 | 
					 | 
				
			||||||
            throw new Error(
 | 
					 | 
				
			||||||
              'MODAL_REPLY_DATA_INVALID\n' +
 | 
					 | 
				
			||||||
                `value must be a single line, got multiple lines [Custom ID: ${value.customId}]`,
 | 
					 | 
				
			||||||
            );
 | 
					 | 
				
			||||||
          }
 | 
					 | 
				
			||||||
          this.components[i].components[0].setValue(value.value);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    // Get Object
 | 
					    // Get Object
 | 
				
			||||||
    const dataFinal = this.toJSON();
 | 
					    const dataFinal = this.toJSON();
 | 
				
			||||||
    dataFinal.components = dataFinal.components
 | 
					    dataFinal.components = dataFinal.components
 | 
				
			||||||
      .map(c => {
 | 
					      .map(c => {
 | 
				
			||||||
        delete c.components[0].max_length;
 | 
					        c.components[0] = {
 | 
				
			||||||
        delete c.components[0].min_length;
 | 
					          type: c.components[0].type,
 | 
				
			||||||
        delete c.components[0].required;
 | 
					          value: c.components[0].value,
 | 
				
			||||||
        delete c.components[0].placeholder;
 | 
					          custom_id: c.components[0].custom_id,
 | 
				
			||||||
        delete c.components[0].label;
 | 
					        };
 | 
				
			||||||
        delete c.components[0].style;
 | 
					 | 
				
			||||||
        return c;
 | 
					        return c;
 | 
				
			||||||
      })
 | 
					      })
 | 
				
			||||||
      .filter(c => c.components[0].value && c.components[0].value !== '');
 | 
					      .filter(c => c.components[0].value && c.components[0].value !== '');
 | 
				
			||||||
    delete dataFinal.title;
 | 
					    delete dataFinal.title;
 | 
				
			||||||
    const nonce = SnowflakeUtil.generate();
 | 
					    const nonce = SnowflakeUtil.generate();
 | 
				
			||||||
    const postData = {
 | 
					    const postData = {
 | 
				
			||||||
      type: 5, // Modal
 | 
					      type: InteractionTypes.MODAL_SUBMIT, // Modal
 | 
				
			||||||
      application_id: this.application.id,
 | 
					      application_id: this.applicationId,
 | 
				
			||||||
      guild_id: guild || null,
 | 
					      guild_id: this.guildId,
 | 
				
			||||||
      channel_id: channel,
 | 
					      channel_id: this.channelId,
 | 
				
			||||||
      data: dataFinal,
 | 
					      data: dataFinal,
 | 
				
			||||||
      nonce,
 | 
					      nonce,
 | 
				
			||||||
      session_id: this.client.sessionId,
 | 
					      session_id: this.client.ws.shards.first()?.sessionId,
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
    await this.client.api.interactions.post({
 | 
					    this.client.api.interactions.post({
 | 
				
			||||||
      data: postData,
 | 
					      data: postData,
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
    this.client._interactionCache.set(nonce, {
 | 
					 | 
				
			||||||
      channelId: channel,
 | 
					 | 
				
			||||||
      guildId: guild,
 | 
					 | 
				
			||||||
      metadata: postData,
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
    return new Promise((resolve, reject) => {
 | 
					    return new Promise((resolve, reject) => {
 | 
				
			||||||
 | 
					      const timeoutMs = 15_000;
 | 
				
			||||||
 | 
					      // Waiting for MsgCreate / ModalCreate
 | 
				
			||||||
      const handler = data => {
 | 
					      const handler = data => {
 | 
				
			||||||
        timeout.refresh();
 | 
					        if (data.nonce !== nonce) return;
 | 
				
			||||||
        if (data.metadata?.nonce !== nonce) return;
 | 
					 | 
				
			||||||
        clearTimeout(timeout);
 | 
					        clearTimeout(timeout);
 | 
				
			||||||
        this.client.removeListener('interactionResponse', handler);
 | 
					        this.client.removeListener(Events.MESSAGE_CREATE, handler);
 | 
				
			||||||
 | 
					        this.client.removeListener(Events.INTERACTION_MODAL_CREATE, handler);
 | 
				
			||||||
        this.client.decrementMaxListeners();
 | 
					        this.client.decrementMaxListeners();
 | 
				
			||||||
        if (data.status) {
 | 
					        resolve(data);
 | 
				
			||||||
          resolve(data.metadata);
 | 
					 | 
				
			||||||
        } else {
 | 
					 | 
				
			||||||
          reject(
 | 
					 | 
				
			||||||
            new Error('INTERACTION_ERROR', {
 | 
					 | 
				
			||||||
              cause: data,
 | 
					 | 
				
			||||||
            }),
 | 
					 | 
				
			||||||
          );
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
      };
 | 
					      };
 | 
				
			||||||
      const timeout = setTimeout(() => {
 | 
					      const timeout = setTimeout(() => {
 | 
				
			||||||
        this.client.removeListener('interactionResponse', handler);
 | 
					        this.client.removeListener(Events.MESSAGE_CREATE, handler);
 | 
				
			||||||
 | 
					        this.client.removeListener(Events.INTERACTION_MODAL_CREATE, handler);
 | 
				
			||||||
        this.client.decrementMaxListeners();
 | 
					        this.client.decrementMaxListeners();
 | 
				
			||||||
        reject(
 | 
					        reject(new Error('INTERACTION_FAILED'));
 | 
				
			||||||
          new Error('INTERACTION_TIMEOUT', {
 | 
					      }, timeoutMs).unref();
 | 
				
			||||||
            cause: postData,
 | 
					 | 
				
			||||||
          }),
 | 
					 | 
				
			||||||
        );
 | 
					 | 
				
			||||||
      }, this.client.options.interactionTimeout).unref();
 | 
					 | 
				
			||||||
      this.client.incrementMaxListeners();
 | 
					      this.client.incrementMaxListeners();
 | 
				
			||||||
      this.client.on('interactionResponse', handler);
 | 
					      this.client.on(Events.MESSAGE_CREATE, handler);
 | 
				
			||||||
 | 
					      this.client.on(Events.INTERACTION_MODAL_CREATE, handler);
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // TypeScript
 | 
				
			||||||
 | 
					  /**
 | 
				
			||||||
 | 
					   * Check data
 | 
				
			||||||
 | 
					   * @type {boolean}
 | 
				
			||||||
 | 
					   * @readonly
 | 
				
			||||||
 | 
					   */
 | 
				
			||||||
 | 
					  get isMessage() {
 | 
				
			||||||
 | 
					    return false;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
module.exports = Modal;
 | 
					module.exports = Modal;
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user