Skip to content
Draft
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/discord.js/.eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@
"key-spacing": "error",
"keyword-spacing": "error",
"max-depth": "error",
"max-len": ["error", 120, 2],
"max-len": ["error", 120, 2, { "ignorePattern": "^exports\\.\\w+\\s=\\srequire" }],
"max-nested-callbacks": ["error", { "max": 4 }],
"max-statements-per-line": ["error", { "max": 2 }],
"new-cap": "off",
Expand Down
5 changes: 4 additions & 1 deletion packages/discord.js/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ exports.AnonymousGuild = require('./structures/AnonymousGuild');
exports.Application = require('./structures/interfaces/Application');
exports.ApplicationCommand = require('./structures/ApplicationCommand');
exports.AutocompleteInteraction = require('./structures/AutocompleteInteraction');
exports.AutocompleteInteractionOptionResolver = require('./structures/AutocompleteInteractionOptionResolver');
exports.Base = require('./structures/Base');
exports.BaseGuild = require('./structures/BaseGuild');
exports.BaseGuildEmoji = require('./structures/BaseGuildEmoji');
Expand All @@ -97,14 +98,15 @@ exports.ButtonInteraction = require('./structures/ButtonInteraction');
exports.CategoryChannel = require('./structures/CategoryChannel');
exports.BaseChannel = require('./structures/BaseChannel').BaseChannel;
exports.ChatInputCommandInteraction = require('./structures/ChatInputCommandInteraction');
exports.ChatInputCommandInteractionOptionResolver = require('./structures/ChatInputCommandInteractionOptionResolver');
exports.ClientApplication = require('./structures/ClientApplication');
exports.ClientPresence = require('./structures/ClientPresence');
exports.ClientUser = require('./structures/ClientUser');
exports.CommandInteraction = require('./structures/CommandInteraction');
exports.Collector = require('./structures/interfaces/Collector');
exports.CommandInteractionOptionResolver = require('./structures/CommandInteractionOptionResolver');
exports.Component = require('./structures/Component');
exports.ContextMenuCommandInteraction = require('./structures/ContextMenuCommandInteraction');
exports.ContextMenuCommandInteractionOptionResolver = require('./structures/ContextMenuCommandInteractionOptionResolver');
exports.DMChannel = require('./structures/DMChannel');
exports.Embed = require('./structures/Embed');
exports.EmbedBuilder = require('./structures/EmbedBuilder');
Expand All @@ -124,6 +126,7 @@ exports.Integration = require('./structures/Integration');
exports.IntegrationApplication = require('./structures/IntegrationApplication');
exports.BaseInteraction = require('./structures/BaseInteraction');
exports.InteractionCollector = require('./structures/InteractionCollector');
exports.InteractionOptionResolver = require('./structures/InteractionOptionResolver');
exports.InteractionResponse = require('./structures/InteractionResponse');
exports.InteractionWebhook = require('./structures/InteractionWebhook');
exports.Invite = require('./structures/Invite');
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
'use strict';

const { InteractionResponseType, Routes } = require('discord-api-types/v10');
const AutocompleteInteractionOptionResolver = require('./AutocompleteInteractionOptionResolver');
const BaseInteraction = require('./BaseInteraction');
const CommandInteractionOptionResolver = require('./CommandInteractionOptionResolver');
const { ErrorCodes } = require('../errors');

/**
Expand Down Expand Up @@ -53,7 +53,7 @@ class AutocompleteInteraction extends BaseInteraction {
* The options passed to the command
* @type {CommandInteractionOptionResolver}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* @type {CommandInteractionOptionResolver}
* @type {AutocompleteInteractionOptionResolver}

*/
this.options = new CommandInteractionOptionResolver(this.client, data.data.options ?? []);
this.options = new AutocompleteInteractionOptionResolver(this.client, data.data.options ?? []);
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
'use strict';

const InteractionOptionResolver = require('./InteractionOptionResolver');
const { ErrorCodes } = require('../errors');

/**
* A resolver for autocomplete interaction options.
* @extends {InteractionOptionResolver}
*/
class AutocompleteInteractionOptionResolver extends InteractionOptionResolver {
/**
* The full autocomplete option object.
* @typedef {Object} AutocompleteFocusedOption
* @property {string} name The name of the option
* @property {ApplicationCommandOptionType} type The type of the application command option
* @property {string} value The value of the option
* @property {boolean} focused Whether this option is currently in focus for autocomplete
*/

/**
* Gets the focused option.
* @param {boolean} [getFull=false] Whether to get the full option object
* @returns {string|AutocompleteFocusedOption}
* The value of the option, or the whole option if getFull is true
*/
getFocused(getFull = false) {
const focusedOption = this._hoistedOptions.find(option => option.focused);
if (!focusedOption) throw new TypeError(ErrorCodes.AutocompleteInteractionOptionNoFocusedOption);
return getFull ? focusedOption : focusedOption.value;
}
}

module.exports = AutocompleteInteractionOptionResolver;
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
'use strict';

const ChatInputCommandInteractionOptionResolver = require('./ChatInputCommandInteractionOptionResolver');
const CommandInteraction = require('./CommandInteraction');
const CommandInteractionOptionResolver = require('./CommandInteractionOptionResolver');

/**
* Represents a command interaction.
Expand All @@ -15,7 +15,7 @@ class ChatInputCommandInteraction extends CommandInteraction {
* The options passed to the command.
* @type {CommandInteractionOptionResolver}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* @type {CommandInteractionOptionResolver}
* @type {ChatInputCommandInteractionOptionResolver}

*/
this.options = new CommandInteractionOptionResolver(
this.options = new ChatInputCommandInteractionOptionResolver(
this.client,
data.data.options?.map(option => this.transformOption(option, data.data.resolved)) ?? [],
this.transformResolved(data.data.resolved ?? {}),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,21 +1,16 @@
'use strict';

const { ApplicationCommandOptionType } = require('discord-api-types/v10');
const { TypeError, ErrorCodes } = require('../errors');
const InteractionOptionResolver = require('./InteractionOptionResolver');
const { ErrorCodes } = require('../errors');

/**
* A resolver for command interaction options.
* A resolver for chat input interaction options.
* @extends {InteractionOptionResolver}
*/
class CommandInteractionOptionResolver {
class ChatInputCommandInteractionOptionResolver extends InteractionOptionResolver {
constructor(client, options, resolved) {
/**
* The client that instantiated this.
* @name CommandInteractionOptionResolver#client
* @type {Client}
* @readonly
*/
Object.defineProperty(this, 'client', { value: client });

super(client, options, resolved);
/**
* The name of the subcommand group.
* @type {?string}
Expand All @@ -30,14 +25,6 @@ class CommandInteractionOptionResolver {
*/
this._subcommand = null;

/**
* The bottom-level options for the interaction.
* If there is a subcommand (or subcommand and group), this is the options for the subcommand.
* @type {CommandInteractionOption[]}
* @private
*/
this._hoistedOptions = options;

// Hoist subcommand group if present
if (this._hoistedOptions[0]?.type === ApplicationCommandOptionType.SubcommandGroup) {
this._group = this._hoistedOptions[0].name;
Expand All @@ -48,59 +35,6 @@ class CommandInteractionOptionResolver {
this._subcommand = this._hoistedOptions[0].name;
this._hoistedOptions = this._hoistedOptions[0].options ?? [];
}

/**
* The interaction options array.
* @name CommandInteractionOptionResolver#data
* @type {ReadonlyArray<CommandInteractionOption>}
* @readonly
*/
Object.defineProperty(this, 'data', { value: Object.freeze([...options]) });

/**
* The interaction resolved data
* @name CommandInteractionOptionResolver#resolved
* @type {?Readonly<CommandInteractionResolvedData>}
*/
Object.defineProperty(this, 'resolved', { value: resolved ? Object.freeze(resolved) : null });
}

/**
* Gets an option by its name.
* @param {string} name The name of the option.
* @param {boolean} [required=false] Whether to throw an error if the option is not found.
* @returns {?CommandInteractionOption} The option, if found.
*/
get(name, required = false) {
const option = this._hoistedOptions.find(opt => opt.name === name);
if (!option) {
if (required) {
throw new TypeError(ErrorCodes.CommandInteractionOptionNotFound, name);
}
return null;
}
return option;
}

/**
* Gets an option by name and property and checks its type.
* @param {string} name The name of the option.
* @param {ApplicationCommandOptionType} type The type of the option.
* @param {string[]} properties The properties to check for for `required`.
* @param {boolean} required Whether to throw an error if the option is not found.
* @returns {?CommandInteractionOption} The option, if found.
* @private
*/
_getTypedOption(name, type, properties, required) {
const option = this.get(name, required);
if (!option) {
return null;
} else if (option.type !== type) {
throw new TypeError(ErrorCodes.CommandInteractionOptionType, name, option.type, type);
} else if (required && properties.every(prop => option[prop] === null || typeof option[prop] === 'undefined')) {
throw new TypeError(ErrorCodes.CommandInteractionOptionEmpty, name, option.type);
}
return option;
}

/**
Expand Down Expand Up @@ -243,39 +177,6 @@ class CommandInteractionOptionResolver {
);
return option?.member ?? option?.user ?? option?.role ?? null;
}

/**
* Gets a message option.
* @param {string} name The name of the option.
* @param {boolean} [required=false] Whether to throw an error if the option is not found.
* @returns {?Message}
* The value of the option, or null if not set and not required.
*/
getMessage(name, required = false) {
const option = this._getTypedOption(name, '_MESSAGE', ['message'], required);
return option?.message ?? null;
}

/**
* The full autocomplete option object.
* @typedef {Object} AutocompleteFocusedOption
* @property {string} name The name of the option
* @property {ApplicationCommandOptionType} type The type of the application command option
* @property {string} value The value of the option
* @property {boolean} focused Whether this option is currently in focus for autocomplete
*/

/**
* Gets the focused option.
* @param {boolean} [getFull=false] Whether to get the full option object
* @returns {string|AutocompleteFocusedOption}
* The value of the option, or the whole option if getFull is true
*/
getFocused(getFull = false) {
const focusedOption = this._hoistedOptions.find(option => option.focused);
if (!focusedOption) throw new TypeError(ErrorCodes.AutocompleteInteractionOptionNoFocusedOption);
return getFull ? focusedOption : focusedOption.value;
}
}

module.exports = CommandInteractionOptionResolver;
module.exports = ChatInputCommandInteractionOptionResolver;
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

const { ApplicationCommandOptionType } = require('discord-api-types/v10');
const CommandInteraction = require('./CommandInteraction');
const CommandInteractionOptionResolver = require('./CommandInteractionOptionResolver');
const ContextMenuCommandInteractionOptionResolver = require('./ContextMenuCommandInteractionOptionResolver');
const { lazy } = require('../util/Util');

const getMessage = lazy(() => require('./Message').Message);
Expand All @@ -18,7 +18,7 @@ class ContextMenuCommandInteraction extends CommandInteraction {
* The target of the interaction, parsed into options
* @type {CommandInteractionOptionResolver}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* @type {CommandInteractionOptionResolver}
* @type {ContextMenuCommandInteractionOptionResolver}

*/
this.options = new CommandInteractionOptionResolver(
this.options = new ContextMenuCommandInteractionOptionResolver(
this.client,
this.resolveContextMenuOptions(data.data),
this.transformResolved(data.data.resolved),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
'use strict';

const { ApplicationCommandOptionType } = require('discord-api-types/v10');
const InteractionOptionResolver = require('./InteractionOptionResolver');

/**
* A resolver for context menu interaction options.
* @extends {InteractionOptionResolver}
*/
class ContextMenuCommandInteractionOptionResolver extends InteractionOptionResolver {
/**
* Gets a user option.
* @param {boolean} [required=false] Whether to throw an error if the option is not found.
* @returns {?User} The value of the option, or null if not set and not required.
*/
getUser(required = false) {
const option = this._getTypedOption('user', ApplicationCommandOptionType.User, ['user'], required);
return option?.user ?? null;
}

/**
* Gets a member option.
* @returns {?(GuildMember|APIGuildMember)}
* The value of the option, or null if the user is not present in the guild or the option is not set.
*/
getMember() {
const option = this._getTypedOption('user', ApplicationCommandOptionType.User, ['member'], false);
return option?.member ?? null;
}

/**
* Gets a message option.
* @param {boolean} [required=false] Whether to throw an error if the option is not found.
* @returns {?Message}
* The value of the option, or null if not set and not required.
*/
getMessage(required = false) {
const option = this._getTypedOption('message', '_MESSAGE', ['message'], required);
return option?.message ?? null;
}
}

module.exports = ContextMenuCommandInteractionOptionResolver;
Loading