From e68effa822f064a324ed5b92e797c9fc3ce5e211 Mon Sep 17 00:00:00 2001 From: DD Date: Mon, 20 Jun 2022 15:46:35 +0300 Subject: [PATCH] refactor: errors (#8068) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Parbez Co-authored-by: A. Román Co-authored-by: SpaceEEC --- packages/discord.js/src/client/BaseClient.js | 4 +- packages/discord.js/src/client/Client.js | 40 +-- .../discord.js/src/client/WebhookClient.js | 4 +- .../src/client/websocket/WebSocketManager.js | 7 +- packages/discord.js/src/errors/DJSError.js | 35 +- packages/discord.js/src/errors/ErrorCodes.js | 298 ++++++++++++++++++ packages/discord.js/src/errors/Messages.js | 227 ++++++------- packages/discord.js/src/errors/index.js | 1 + packages/discord.js/src/index.js | 7 + .../src/managers/ApplicationCommandManager.js | 6 +- .../ApplicationCommandPermissionsManager.js | 36 +-- .../discord.js/src/managers/DataManager.js | 4 +- .../src/managers/GuildBanManager.js | 10 +- .../src/managers/GuildChannelManager.js | 14 +- .../src/managers/GuildEmojiManager.js | 27 +- .../src/managers/GuildEmojiRoleManager.js | 6 +- .../src/managers/GuildInviteManager.js | 12 +- .../src/managers/GuildMemberManager.js | 29 +- .../src/managers/GuildMemberRoleManager.js | 18 +- .../managers/GuildScheduledEventManager.js | 14 +- .../src/managers/GuildStickerManager.js | 10 +- .../discord.js/src/managers/MessageManager.js | 16 +- .../managers/PermissionOverwriteManager.js | 8 +- .../src/managers/ReactionUserManager.js | 4 +- .../discord.js/src/managers/RoleManager.js | 8 +- .../src/managers/StageInstanceManager.js | 14 +- .../discord.js/src/managers/ThreadManager.js | 8 +- .../src/managers/ThreadMemberManager.js | 4 +- .../discord.js/src/managers/UserManager.js | 3 +- packages/discord.js/src/sharding/Shard.js | 16 +- .../src/sharding/ShardClientUtil.js | 6 +- .../src/sharding/ShardingManager.js | 42 +-- .../src/structures/AutocompleteInteraction.js | 3 +- .../src/structures/ClientPresence.js | 6 +- .../CommandInteractionOptionResolver.js | 14 +- packages/discord.js/src/structures/Guild.js | 10 +- .../discord.js/src/structures/GuildChannel.js | 4 +- .../discord.js/src/structures/GuildEmoji.js | 4 +- .../discord.js/src/structures/GuildMember.js | 10 +- .../src/structures/GuildScheduledEvent.js | 6 +- .../src/structures/InteractionResponse.js | 4 +- packages/discord.js/src/structures/Invite.js | 4 +- packages/discord.js/src/structures/Message.js | 34 +- .../src/structures/MessagePayload.js | 6 +- .../src/structures/ModalSubmitFields.js | 6 +- .../discord.js/src/structures/NewsChannel.js | 4 +- .../src/structures/PartialGroupDMChannel.js | 6 +- .../src/structures/PermissionOverwrites.js | 4 +- packages/discord.js/src/structures/Role.js | 4 +- packages/discord.js/src/structures/Sticker.js | 3 +- .../src/structures/ThreadChannel.js | 4 +- .../discord.js/src/structures/VoiceState.js | 10 +- packages/discord.js/src/structures/Webhook.js | 12 +- .../src/structures/interfaces/Collector.js | 4 +- .../interfaces/InteractionResponses.js | 22 +- .../structures/interfaces/TextBasedChannel.js | 6 +- packages/discord.js/src/util/BitField.js | 4 +- packages/discord.js/src/util/DataResolver.js | 6 +- .../discord.js/src/util/LimitedCollection.js | 8 +- packages/discord.js/src/util/Sweepers.js | 30 +- packages/discord.js/src/util/Util.js | 10 +- packages/discord.js/typings/index.d.ts | 163 ++++++++++ 62 files changed, 923 insertions(+), 426 deletions(-) create mode 100644 packages/discord.js/src/errors/ErrorCodes.js diff --git a/packages/discord.js/src/client/BaseClient.js b/packages/discord.js/src/client/BaseClient.js index 94044806b..74bfccd6c 100644 --- a/packages/discord.js/src/client/BaseClient.js +++ b/packages/discord.js/src/client/BaseClient.js @@ -2,7 +2,7 @@ const EventEmitter = require('node:events'); const { REST } = require('@discordjs/rest'); -const { TypeError } = require('../errors'); +const { TypeError, ErrorCodes } = require('../errors'); const Options = require('../util/Options'); const { mergeDefault, flatten } = require('../util/Util'); @@ -15,7 +15,7 @@ class BaseClient extends EventEmitter { super({ captureRejections: true }); if (typeof options !== 'object' || options === null) { - throw new TypeError('INVALID_TYPE', 'options', 'object', true); + throw new TypeError(ErrorCodes.InvalidType, 'options', 'object', true); } /** diff --git a/packages/discord.js/src/client/Client.js b/packages/discord.js/src/client/Client.js index 53d4044ef..ff07ba3d8 100644 --- a/packages/discord.js/src/client/Client.js +++ b/packages/discord.js/src/client/Client.js @@ -8,7 +8,7 @@ const BaseClient = require('./BaseClient'); const ActionsManager = require('./actions/ActionsManager'); const ClientVoiceManager = require('./voice/ClientVoiceManager'); const WebSocketManager = require('./websocket/WebSocketManager'); -const { Error, TypeError, RangeError } = require('../errors'); +const { Error, TypeError, RangeError, ErrorCodes } = require('../errors'); const BaseGuildEmojiManager = require('../managers/BaseGuildEmojiManager'); const ChannelManager = require('../managers/ChannelManager'); const GuildManager = require('../managers/GuildManager'); @@ -211,7 +211,7 @@ class Client extends BaseClient { * client.login('my token'); */ async login(token = this.token) { - if (!token || typeof token !== 'string') throw new Error('TOKEN_INVALID'); + if (!token || typeof token !== 'string') throw new Error(ErrorCodes.TokenInvalid); this.token = token = token.replace(/^(Bot|Bearer)\s*/i, ''); this.rest.setToken(token); this.emit( @@ -366,7 +366,7 @@ class Client extends BaseClient { */ async fetchGuildPreview(guild) { const id = this.guilds.resolveId(guild); - if (!id) throw new TypeError('INVALID_TYPE', 'guild', 'GuildResolvable'); + if (!id) throw new TypeError(ErrorCodes.InvalidType, 'guild', 'GuildResolvable'); const data = await this.rest.get(Routes.guildPreview(id)); return new GuildPreview(this, data); } @@ -378,7 +378,7 @@ class Client extends BaseClient { */ async fetchGuildWidget(guild) { const id = this.guilds.resolveId(guild); - if (!id) throw new TypeError('INVALID_TYPE', 'guild', 'GuildResolvable'); + if (!id) throw new TypeError(ErrorCodes.InvalidType, 'guild', 'GuildResolvable'); const data = await this.rest.get(Routes.guildWidgetJSON(id)); return new Widget(this, data); } @@ -413,23 +413,23 @@ class Client extends BaseClient { * console.log(`Generated bot invite link: ${link}`); */ generateInvite(options = {}) { - if (typeof options !== 'object') throw new TypeError('INVALID_TYPE', 'options', 'object', true); - if (!this.application) throw new Error('CLIENT_NOT_READY', 'generate an invite link'); + if (typeof options !== 'object') throw new TypeError(ErrorCodes.InvalidType, 'options', 'object', true); + if (!this.application) throw new Error(ErrorCodes.ClientNotReady, 'generate an invite link'); const { scopes } = options; if (typeof scopes === 'undefined') { - throw new TypeError('INVITE_MISSING_SCOPES'); + throw new TypeError(ErrorCodes.InvalidMissingScopes); } if (!Array.isArray(scopes)) { - throw new TypeError('INVALID_TYPE', 'scopes', 'Array of Invite Scopes', true); + throw new TypeError(ErrorCodes.InvalidType, 'scopes', 'Array of Invite Scopes', true); } if (!scopes.some(scope => [OAuth2Scopes.Bot, OAuth2Scopes.ApplicationsCommands].includes(scope))) { - throw new TypeError('INVITE_MISSING_SCOPES'); + throw new TypeError(ErrorCodes.InvalidMissingScopes); } const validScopes = Object.values(OAuth2Scopes); const invalidScope = scopes.find(scope => !validScopes.includes(scope)); if (invalidScope) { - throw new TypeError('INVALID_ELEMENT', 'Array', 'scopes', invalidScope); + throw new TypeError(ErrorCodes.InvalidElement, 'Array', 'scopes', invalidScope); } const query = makeURLSearchParams({ @@ -445,7 +445,7 @@ class Client extends BaseClient { if (options.guild) { const guildId = this.guilds.resolveId(options.guild); - if (!guildId) throw new TypeError('INVALID_TYPE', 'options.guild', 'GuildResolvable'); + if (!guildId) throw new TypeError(ErrorCodes.InvalidType, 'options.guild', 'GuildResolvable'); query.set('guild_id', guildId); } @@ -476,31 +476,31 @@ class Client extends BaseClient { */ _validateOptions(options = this.options) { if (typeof options.intents === 'undefined') { - throw new TypeError('CLIENT_MISSING_INTENTS'); + throw new TypeError(ErrorCodes.ClientMissingIntents); } else { options.intents = IntentsBitField.resolve(options.intents); } if (typeof options.shardCount !== 'number' || isNaN(options.shardCount) || options.shardCount < 1) { - throw new TypeError('CLIENT_INVALID_OPTION', 'shardCount', 'a number greater than or equal to 1'); + throw new TypeError(ErrorCodes.ClientInvalidOption, 'shardCount', 'a number greater than or equal to 1'); } if (options.shards && !(options.shards === 'auto' || Array.isArray(options.shards))) { - throw new TypeError('CLIENT_INVALID_OPTION', 'shards', "'auto', a number or array of numbers"); + throw new TypeError(ErrorCodes.ClientInvalidOption, 'shards', "'auto', a number or array of numbers"); } - if (options.shards && !options.shards.length) throw new RangeError('CLIENT_INVALID_PROVIDED_SHARDS'); + if (options.shards && !options.shards.length) throw new RangeError(ErrorCodes.ClientInvalidProvidedShards); if (typeof options.makeCache !== 'function') { - throw new TypeError('CLIENT_INVALID_OPTION', 'makeCache', 'a function'); + throw new TypeError(ErrorCodes.ClientInvalidOption, 'makeCache', 'a function'); } if (typeof options.sweepers !== 'object' || options.sweepers === null) { - throw new TypeError('CLIENT_INVALID_OPTION', 'sweepers', 'an object'); + throw new TypeError(ErrorCodes.ClientInvalidOption, 'sweepers', 'an object'); } if (!Array.isArray(options.partials)) { - throw new TypeError('CLIENT_INVALID_OPTION', 'partials', 'an Array'); + throw new TypeError(ErrorCodes.ClientInvalidOption, 'partials', 'an Array'); } if (typeof options.waitGuildTimeout !== 'number' || isNaN(options.waitGuildTimeout)) { - throw new TypeError('CLIENT_INVALID_OPTION', 'waitGuildTimeout', 'a number'); + throw new TypeError(ErrorCodes.ClientInvalidOption, 'waitGuildTimeout', 'a number'); } if (typeof options.failIfNotExists !== 'boolean') { - throw new TypeError('CLIENT_INVALID_OPTION', 'failIfNotExists', 'a boolean'); + throw new TypeError(ErrorCodes.ClientInvalidOption, 'failIfNotExists', 'a boolean'); } } } diff --git a/packages/discord.js/src/client/WebhookClient.js b/packages/discord.js/src/client/WebhookClient.js index 1c66194be..2752a1262 100644 --- a/packages/discord.js/src/client/WebhookClient.js +++ b/packages/discord.js/src/client/WebhookClient.js @@ -1,7 +1,7 @@ 'use strict'; const BaseClient = require('./BaseClient'); -const { Error } = require('../errors'); +const { Error, ErrorCodes } = require('../errors'); const Webhook = require('../structures/Webhook'); /** @@ -33,7 +33,7 @@ class WebhookClient extends BaseClient { /https?:\/\/(?:ptb\.|canary\.)?discord\.com\/api(?:\/v\d{1,2})?\/webhooks\/(\d{17,19})\/([\w-]{68})/i, ); - if (!url || url.length <= 1) throw new Error('WEBHOOK_URL_INVALID'); + if (!url || url.length <= 1) throw new Error(ErrorCodes.WebhookURLInvalid); [, id, token] = url; } diff --git a/packages/discord.js/src/client/websocket/WebSocketManager.js b/packages/discord.js/src/client/websocket/WebSocketManager.js index 1a7695ce9..b63ce3bf4 100644 --- a/packages/discord.js/src/client/websocket/WebSocketManager.js +++ b/packages/discord.js/src/client/websocket/WebSocketManager.js @@ -7,7 +7,7 @@ const { Collection } = require('@discordjs/collection'); const { GatewayCloseCodes, GatewayDispatchEvents, Routes } = require('discord-api-types/v10'); const WebSocketShard = require('./WebSocketShard'); const PacketHandlers = require('./handlers'); -const { Error } = require('../../errors'); +const { Error, ErrorCodes } = require('../../errors'); const Events = require('../../util/Events'); const ShardEvents = require('../../util/ShardEvents'); const Status = require('../../util/Status'); @@ -130,7 +130,7 @@ class WebSocketManager extends EventEmitter { * @private */ async connect() { - const invalidToken = new Error(GatewayCloseCodes[GatewayCloseCodes.AuthenticationFailed]); + const invalidToken = new Error(ErrorCodes.AuthenticationFailed); const { url: gatewayURL, shards: recommendedShards, @@ -318,7 +318,8 @@ class WebSocketManager extends EventEmitter { */ destroy() { if (this.destroyed) return; - this.debug(`Manager was destroyed. Called by:\n${new Error('MANAGER_DESTROYED').stack}`); + // TODO: Make a util for getting a stack + this.debug(`Manager was destroyed. Called by:\n${new globalThis.Error().stack}`); this.destroyed = true; this.shardQueue.clear(); for (const shard of this.shards.values()) shard.destroy({ closeCode: 1_000, reset: true, emit: false, log: false }); diff --git a/packages/discord.js/src/errors/DJSError.js b/packages/discord.js/src/errors/DJSError.js index a415dec25..877e3a892 100644 --- a/packages/discord.js/src/errors/DJSError.js +++ b/packages/discord.js/src/errors/DJSError.js @@ -1,9 +1,9 @@ 'use strict'; // Heavily inspired by node's `internal/errors` module - +const ErrorCodes = require('./ErrorCodes'); +const Messages = require('./Messages'); const kCode = Symbol('code'); -const messages = new Map(); /** * Extend an error of some sort into a DiscordjsError. @@ -13,51 +13,40 @@ const messages = new Map(); */ function makeDiscordjsError(Base) { return class DiscordjsError extends Base { - constructor(key, ...args) { - super(message(key, args)); - this[kCode] = key; + constructor(code, ...args) { + super(message(code, args)); + this[kCode] = code; if (Error.captureStackTrace) Error.captureStackTrace(this, DiscordjsError); } get name() { - return `${super.name} [${this[kCode]}]`; + return `${super.name} [${this.code}]`; } get code() { - return this[kCode]; + return ErrorCodes[this[kCode]]; } }; } /** * Format the message for an error. - * @param {string} key Error key + * @param {string} code The error code * @param {Array<*>} args Arguments to pass for util format or as function args * @returns {string} Formatted string * @ignore */ -function message(key, args) { - if (typeof key !== 'string') throw new Error('Error message key must be a string'); - const msg = messages.get(key); - if (!msg) throw new Error(`An invalid error message key was used: ${key}.`); +function message(code, args) { + if (typeof code !== 'number') throw new Error('Error code must be a valid DiscordjsErrorCodes'); + const msg = Messages[code]; + if (!msg) throw new Error(`An invalid error code was used: ${code}.`); if (typeof msg === 'function') return msg(...args); if (!args?.length) return msg; args.unshift(msg); return String(...args); } -/** - * Register an error code and message. - * @param {string} sym Unique name for the error - * @param {*} val Value of the error - * @ignore - */ -function register(sym, val) { - messages.set(sym, typeof val === 'function' ? val : String(val)); -} - module.exports = { - register, Error: makeDiscordjsError(Error), TypeError: makeDiscordjsError(TypeError), RangeError: makeDiscordjsError(RangeError), diff --git a/packages/discord.js/src/errors/ErrorCodes.js b/packages/discord.js/src/errors/ErrorCodes.js new file mode 100644 index 000000000..01071851d --- /dev/null +++ b/packages/discord.js/src/errors/ErrorCodes.js @@ -0,0 +1,298 @@ +'use strict'; + +const { createEnum } = require('../util/Enums'); + +/** + * @typedef {Object} DiscordjsErrorCodes + + * @property {number} ClientInvalidOption + * @property {number} ClientInvalidProvidedShards + * @property {number} ClientMissingIntents + * @property {number} ClientNotReady + + * @property {number} TokenInvalid + * @property {number} TokenMissing + * @property {number} ApplicationCommandPermissionsTokenMissing + + * @property {number} WSCloseRequested + * @property {number} WSConnectionExists + * @property {number} WSNotOpen + * @property {number} ManagerDestroyed + + * @property {number} BitFieldInvalid + + * @property {number} ShardingInvalid + * @property {number} ShardingRequired + * @property {number} InvalidIntents + * @property {number} DisallowedIntents + * @property {number} ShardingNoShards + * @property {number} ShardingInProcess + * @property {number} ShardingInvalidEvalBroadcast + * @property {number} ShardingShardNotFound + * @property {number} ShardingAlreadySpawned + * @property {number} ShardingProcessExists + * @property {number} ShardingWorkerExists + * @property {number} ShardingReadyTimeout + * @property {number} ShardingReadyDisconnected + * @property {number} ShardingReadyDied + * @property {number} ShardingNoChildExists + * @property {number} ShardingShardMiscalculation + + * @property {number} ColorRange + * @property {number} ColorConvert + + * @property {number} InviteOptionsMissingChannel + + * @property {number} ButtonLabel + * @property {number} ButtonURL + * @property {number} ButtonCustomId + + * @property {number} SelectMenuCustomId + * @property {number} SelectMenuPlaceholder + * @property {number} SelectOptionLabel + * @property {number} SelectOptionValue + * @property {number} SelectOptionDescription + + * @property {number} InteractionCollectorError + + * @property {number} FileNotFound + + * @property {number} UserBannerNotFetched + * @property {number} UserNoDMChannel + + * @property {number} VoiceNotStageChannel + + * @property {number} VoiceStateNotOwn + * @property {number} VoiceStateInvalidType + + * @property {number} ReqResourceType + + * @property {number} ImageFormat + * @property {number} ImageSize + + * @property {number} MessageBulkDeleteType + * @property {number} MessageNonceType + * @property {number} MessageContentType + + * @property {number} SplitMaxLen + + * @property {number} BanResolveId + * @property {number} FetchBanResolveId + + * @property {number} PruneDaysType + + * @property {number} GuildChannelResolve + * @property {number} GuildVoiceChannelResolve + * @property {number} GuildChannelOrphan + * @property {number} GuildChannelUnowned + * @property {number} GuildOwned + * @property {number} GuildMembersTimeout + * @property {number} GuildUncachedMe + * @property {number} ChannelNotCached + * @property {number} StageChannelResolve + * @property {number} GuildScheduledEventResolve + * @property {number} FetchOwnerId + + * @property {number} InvalidType + * @property {number} InvalidElement + + * @property {number} MessageThreadParent + * @property {number} MessageExistingThread + * @property {number} ThreadInvitableType + + * @property {number} WebhookMessage + * @property {number} WebhookTokenUnavailable + * @property {number} WebhookURLInvalid + * @property {number} WebhookApplication + * @property {number} MessageReferenceMissing + + * @property {number} EmojiType + * @property {number} EmojiManaged + * @property {number} MissingManageEmojisAndStickersPermission + * @property {number} NotGuildSticker + + * @property {number} ReactionResolveUser + + * @property {number} VanityURL + + * @property {number} InviteResolveCode + + * @property {number} InviteNotFound + + * @property {number} DeleteGroupDMChannel + * @property {number} FetchGroupDMChannel + + * @property {number} MemberFetchNonceLength + + * @property {number} GlobalCommandPermissions + * @property {number} GuildUncachedEntityResolve + + * @property {number} InteractionAlreadyReplied + * @property {number} InteractionNotReplied + * @property {number} InteractionEphemeralReplied + + * @property {number} CommandInteractionOptionNotFound + * @property {number} CommandInteractionOptionType + * @property {number} CommandInteractionOptionEmpty + * @property {number} CommandInteractionOptionNoSubcommand + * @property {number} CommandInteractionOptionNoSubcommandGroup + * @property {number} AutocompleteInteractionOptionNoFocusedOption + + * @property {number} ModalSubmitInteractionFieldNotFound + * @property {number} ModalSubmitInteractionFieldType + + * @property {number} InvalidMissingScopes + + * @property {number} NotImplemented + + * @property {number} SweepFilterReturn + */ + +// JSDoc for intellisense purposes +/** + * @type {DiscordjsErrorCodes} + * @ignore + */ +module.exports = createEnum([ + 'ClientInvalidOption', + 'ClientInvalidProvidedShards', + 'ClientMissingIntents', + 'ClientNotReady', + + 'TokenInvalid', + 'TokenMissing', + 'ApplicationCommandPermissionsTokenMissing', + + 'WSCloseRequested', + 'WSConnectionExists', + 'WSNotOpen', + 'ManagerDestroyed', + + 'BitFieldInvalid', + + 'ShardingInvalid', + 'ShardingRequired', + 'InvalidIntents', + 'DisallowedIntents', + 'ShardingNoShards', + 'ShardingInProcess', + 'ShardingInvalidEvalBroadcast', + 'ShardingShardNotFound', + 'ShardingAlreadySpawned', + 'ShardingProcessExists', + 'ShardingWorkerExists', + 'ShardingReadyTimeout', + 'ShardingReadyDisconnected', + 'ShardingReadyDied', + 'ShardingNoChildExists', + 'ShardingShardMiscalculation', + + 'ColorRange', + 'ColorConvert', + + 'InviteOptionsMissingChannel', + + 'ButtonLabel', + 'ButtonURL', + 'ButtonCustomId', + + 'SelectMenuCustomId', + 'SelectMenuPlaceholder', + 'SelectOptionLabel', + 'SelectOptionValue', + 'SelectOptionDescription', + + 'InteractionCollectorError', + + 'FileNotFound', + + 'UserBannerNotFetched', + 'UserNoDMChannel', + + 'VoiceNotStageChannel', + + 'VoiceStateNotOwn', + 'VoiceStateInvalidType', + + 'ReqResourceType', + + 'ImageFormat', + 'ImageSize', + + 'MessageBulkDeleteType', + 'MessageNonceType', + 'MessageContentType', + + 'SplitMaxLen', + + 'BanResolveId', + 'FetchBanResolveId', + + 'PruneDaysType', + + 'GuildChannelResolve', + 'GuildVoiceChannelResolve', + 'GuildChannelOrphan', + 'GuildChannelUnowned', + 'GuildOwned', + 'GuildMembersTimeout', + 'GuildUncachedMe', + 'ChannelNotCached', + 'StageChannelResolve', + 'GuildScheduledEventResolve', + 'FetchOwnerId', + + 'InvalidType', + 'InvalidElement', + + 'MessageThreadParent', + 'MessageExistingThread', + 'ThreadInvitableType', + + 'WebhookMessage', + 'WebhookTokenUnavailable', + 'WebhookURLInvalid', + 'WebhookApplication', + 'MessageReferenceMissing', + + 'EmojiType', + 'EmojiManaged', + 'MissingManageEmojisAndStickersPermission', + 'NotGuildSticker', + + 'ReactionResolveUser', + + 'VanityURL', + + 'InviteResolveCode', + + 'InviteNotFound', + + 'DeleteGroupDMChannel', + 'FetchGroupDMChannel', + + 'MemberFetchNonceLength', + + 'GlobalCommandPermissions', + 'GuildUncachedEntityResolve', + + 'InteractionAlreadyReplied', + 'InteractionNotReplied', + 'InteractionEphemeralReplied', + + 'CommandInteractionOptionNotFound', + 'CommandInteractionOptionType', + 'CommandInteractionOptionEmpty', + 'CommandInteractionOptionNoSubcommand', + 'CommandInteractionOptionNoSubcommandGroup', + 'AutocompleteInteractionOptionNoFocusedOption', + + 'ModalSubmitInteractionFieldNotFound', + 'ModalSubmitInteractionFieldType', + + 'InvalidMissingScopes', + + 'NotImplemented', + + 'SweepFilterReturn', +]); diff --git a/packages/discord.js/src/errors/Messages.js b/packages/discord.js/src/errors/Messages.js index 15f1de40b..5d3c8c7e5 100644 --- a/packages/discord.js/src/errors/Messages.js +++ b/packages/discord.js/src/errors/Messages.js @@ -1,164 +1,169 @@ 'use strict'; -const { register } = require('./DJSError'); +const DjsErrorCodes = require('./ErrorCodes'); const Messages = { - CLIENT_INVALID_OPTION: (prop, must) => `The ${prop} option must be ${must}`, - CLIENT_INVALID_PROVIDED_SHARDS: 'None of the provided shards were valid.', - CLIENT_MISSING_INTENTS: 'Valid intents must be provided for the Client.', - CLIENT_NOT_READY: action => `The client needs to be logged in to ${action}.`, + [DjsErrorCodes.ClientInvalidOption]: (prop, must) => `The ${prop} option must be ${must}`, + [DjsErrorCodes.ClientInvalidProvidedShards]: 'None of the provided shards were valid.', + [DjsErrorCodes.ClientMissingIntents]: 'Valid intents must be provided for the Client.', + [DjsErrorCodes.ClientNotReady]: action => `The client needs to be logged in to ${action}.`, - TOKEN_INVALID: 'An invalid token was provided.', - TOKEN_MISSING: 'Request to use token, but token was unavailable to the client.', - APPLICATION_COMMAND_PERMISSIONS_TOKEN_MISSING: + [DjsErrorCodes.TokenInvalid]: 'An invalid token was provided.', + [DjsErrorCodes.TokenMissing]: 'Request to use token, but token was unavailable to the client.', + [DjsErrorCodes.ApplicationCommandPermissionsTokenMissing]: 'Editing application command permissions requires an OAuth2 bearer token, but none was provided.', - WS_CLOSE_REQUESTED: 'WebSocket closed due to user request.', - WS_CONNECTION_EXISTS: 'There is already an existing WebSocket connection.', - WS_NOT_OPEN: (data = 'data') => `WebSocket not open to send ${data}`, - MANAGER_DESTROYED: 'Manager was destroyed.', + [DjsErrorCodes.WSCloseRequested]: 'WebSocket closed due to user request.', + [DjsErrorCodes.WSConnectionExists]: 'There is already an existing WebSocket connection.', + [DjsErrorCodes.WSNotOpen]: (data = 'data') => `WebSocket not open to send ${data}`, + [DjsErrorCodes.ManagerDestroyed]: 'Manager was destroyed.', - BITFIELD_INVALID: bit => `Invalid bitfield flag or number: ${bit}.`, + [DjsErrorCodes.BitFieldInvalid]: bit => `Invalid bitfield flag or number: ${bit}.`, - SHARDING_INVALID: 'Invalid shard settings were provided.', - SHARDING_REQUIRED: 'This session would have handled too many guilds - Sharding is required.', - INVALID_INTENTS: 'Invalid intent provided for WebSocket intents.', - DISALLOWED_INTENTS: 'Privileged intent provided is not enabled or whitelisted.', - SHARDING_NO_SHARDS: 'No shards have been spawned.', - SHARDING_IN_PROCESS: 'Shards are still being spawned.', - SHARDING_INVALID_EVAL_BROADCAST: 'Script to evaluate must be a function', - SHARDING_SHARD_NOT_FOUND: id => `Shard ${id} could not be found.`, - SHARDING_ALREADY_SPAWNED: count => `Already spawned ${count} shards.`, - SHARDING_PROCESS_EXISTS: id => `Shard ${id} already has an active process.`, - SHARDING_WORKER_EXISTS: id => `Shard ${id} already has an active worker.`, - SHARDING_READY_TIMEOUT: id => `Shard ${id}'s Client took too long to become ready.`, - SHARDING_READY_DISCONNECTED: id => `Shard ${id}'s Client disconnected before becoming ready.`, - SHARDING_READY_DIED: id => `Shard ${id}'s process exited before its Client became ready.`, - SHARDING_NO_CHILD_EXISTS: id => `Shard ${id} has no active process or worker.`, - SHARDING_SHARD_MISCALCULATION: (shard, guild, count) => + [DjsErrorCodes.ShardingInvalid]: 'Invalid shard settings were provided.', + [DjsErrorCodes.ShardingRequired]: 'This session would have handled too many guilds - Sharding is required.', + [DjsErrorCodes.InvalidIntents]: 'Invalid intent provided for WebSocket intents.', + [DjsErrorCodes.DisallowedIntents]: 'Privileged intent provided is not enabled or whitelisted.', + [DjsErrorCodes.ShardingNoShards]: 'No shards have been spawned.', + [DjsErrorCodes.ShardingInProcess]: 'Shards are still being spawned.', + [DjsErrorCodes.ShardingInvalidEvalBroadcast]: 'Script to evaluate must be a function', + [DjsErrorCodes.ShardingShardNotFound]: id => `Shard ${id} could not be found.`, + [DjsErrorCodes.ShardingAlreadySpawned]: count => `Already spawned ${count} shards.`, + [DjsErrorCodes.ShardingProcessExists]: id => `Shard ${id} already has an active process.`, + [DjsErrorCodes.ShardingWorkerExists]: id => `Shard ${id} already has an active worker.`, + [DjsErrorCodes.ShardingReadyTimeout]: id => `Shard ${id}'s Client took too long to become ready.`, + [DjsErrorCodes.ShardingReadyDisconnected]: id => `Shard ${id}'s Client disconnected before becoming ready.`, + [DjsErrorCodes.ShardingReadyDied]: id => `Shard ${id}'s process exited before its Client became ready.`, + [DjsErrorCodes.ShardingNoChildExists]: id => `Shard ${id} has no active process or worker.`, + [DjsErrorCodes.ShardingShardMiscalculation]: (shard, guild, count) => `Calculated invalid shard ${shard} for guild ${guild} with ${count} shards.`, - COLOR_RANGE: 'Color must be within the range 0 - 16777215 (0xFFFFFF).', - COLOR_CONVERT: 'Unable to convert color to a number.', + [DjsErrorCodes.ColorRange]: 'Color must be within the range 0 - 16777215 (0xFFFFFF).', + [DjsErrorCodes.ColorConvert]: 'Unable to convert color to a number.', - INVITE_OPTIONS_MISSING_CHANNEL: 'A valid guild channel must be provided when GuildScheduledEvent is EXTERNAL.', + [DjsErrorCodes.InviteOptionsMissingChannel]: + 'A valid guild channel must be provided when GuildScheduledEvent is EXTERNAL.', - BUTTON_LABEL: 'MessageButton label must be a string', - BUTTON_URL: 'MessageButton URL must be a string', - BUTTON_CUSTOM_ID: 'MessageButton customId must be a string', + [DjsErrorCodes.ButtonLabel]: 'MessageButton label must be a string', + [DjsErrorCodes.ButtonURL]: 'MessageButton URL must be a string', + [DjsErrorCodes.ButtonCustomId]: 'MessageButton customId must be a string', - SELECT_MENU_CUSTOM_ID: 'MessageSelectMenu customId must be a string', - SELECT_MENU_PLACEHOLDER: 'MessageSelectMenu placeholder must be a string', - SELECT_OPTION_LABEL: 'MessageSelectOption label must be a string', - SELECT_OPTION_VALUE: 'MessageSelectOption value must be a string', - SELECT_OPTION_DESCRIPTION: 'MessageSelectOption description must be a string', + [DjsErrorCodes.SelectMenuCustomId]: 'MessageSelectMenu customId must be a string', + [DjsErrorCodes.SelectMenuPlaceholder]: 'MessageSelectMenu placeholder must be a string', + [DjsErrorCodes.SelectOptionLabel]: 'MessageSelectOption label must be a string', + [DjsErrorCodes.SelectOptionValue]: 'MessageSelectOption value must be a string', + [DjsErrorCodes.SelectOptionDescription]: 'MessageSelectOption description must be a string', - INTERACTION_COLLECTOR_ERROR: reason => `Collector received no interactions before ending with reason: ${reason}`, + [DjsErrorCodes.InteractionCollectorError]: reason => + `Collector received no interactions before ending with reason: ${reason}`, - FILE_NOT_FOUND: file => `File could not be found: ${file}`, + [DjsErrorCodes.FileNotFound]: file => `File could not be found: ${file}`, - USER_BANNER_NOT_FETCHED: "You must fetch this user's banner before trying to generate its URL!", - USER_NO_DM_CHANNEL: 'No DM Channel exists!', + [DjsErrorCodes.UserBannerNotFetched]: "You must fetch this user's banner before trying to generate its URL!", + [DjsErrorCodes.UserNoDMChannel]: 'No DM Channel exists!', - VOICE_NOT_STAGE_CHANNEL: 'You are only allowed to do this in stage channels.', + [DjsErrorCodes.VoiceNotStageChannel]: 'You are only allowed to do this in stage channels.', - VOICE_STATE_NOT_OWN: + [DjsErrorCodes.VoiceStateNotOwn]: 'You cannot self-deafen/mute/request to speak on VoiceStates that do not belong to the ClientUser.', - VOICE_STATE_INVALID_TYPE: name => `${name} must be a boolean.`, + [DjsErrorCodes.VoiceStateInvalidType]: name => `${name} must be a boolean.`, - REQ_RESOURCE_TYPE: 'The resource must be a string, Buffer or a valid file stream.', + [DjsErrorCodes.ReqResourceType]: 'The resource must be a string, Buffer or a valid file stream.', - IMAGE_FORMAT: format => `Invalid image format: ${format}`, - IMAGE_SIZE: size => `Invalid image size: ${size}`, + [DjsErrorCodes.ImageFormat]: format => `Invalid image format: ${format}`, + [DjsErrorCodes.ImageSize]: size => `Invalid image size: ${size}`, - MESSAGE_BULK_DELETE_TYPE: 'The messages must be an Array, Collection, or number.', - MESSAGE_NONCE_TYPE: 'Message nonce must be an integer or a string.', - MESSAGE_CONTENT_TYPE: 'Message content must be a string.', + [DjsErrorCodes.MessageBulkDeleteType]: 'The messages must be an Array, Collection, or number.', + [DjsErrorCodes.MessageNonceType]: 'Message nonce must be an integer or a string.', + [DjsErrorCodes.MessageContentType]: 'Message content must be a string.', - SPLIT_MAX_LEN: 'Chunk exceeds the max length and contains no split characters.', + [DjsErrorCodes.SplitMaxLen]: 'Chunk exceeds the max length and contains no split characters.', - BAN_RESOLVE_ID: (ban = false) => `Couldn't resolve the user id to ${ban ? 'ban' : 'unban'}.`, - FETCH_BAN_RESOLVE_ID: "Couldn't resolve the user id to fetch the ban.", + [DjsErrorCodes.BanResolveId]: (ban = false) => `Couldn't resolve the user id to ${ban ? 'ban' : 'unban'}.`, + [DjsErrorCodes.FetchBanResolveId]: "Couldn't resolve the user id to fetch the ban.", - PRUNE_DAYS_TYPE: 'Days must be a number', + [DjsErrorCodes.PruneDaysType]: 'Days must be a number', - GUILD_CHANNEL_RESOLVE: 'Could not resolve channel to a guild channel.', - GUILD_VOICE_CHANNEL_RESOLVE: 'Could not resolve channel to a guild voice channel.', - GUILD_CHANNEL_ORPHAN: 'Could not find a parent to this guild channel.', - GUILD_CHANNEL_UNOWNED: "The fetched channel does not belong to this manager's guild.", - GUILD_OWNED: 'Guild is owned by the client.', - GUILD_MEMBERS_TIMEOUT: "Members didn't arrive in time.", - GUILD_UNCACHED_ME: 'The client user as a member of this guild is uncached.', - CHANNEL_NOT_CACHED: 'Could not find the channel where this message came from in the cache!', - STAGE_CHANNEL_RESOLVE: 'Could not resolve channel to a stage channel.', - GUILD_SCHEDULED_EVENT_RESOLVE: 'Could not resolve the guild scheduled event.', - FETCH_OWNER_ID: "Couldn't resolve the guild ownerId to fetch the member.", + [DjsErrorCodes.GuildChannelResolve]: 'Could not resolve channel to a guild channel.', + [DjsErrorCodes.GuildVoiceChannelResolve]: 'Could not resolve channel to a guild voice channel.', + [DjsErrorCodes.GuildChannelOrphan]: 'Could not find a parent to this guild channel.', + [DjsErrorCodes.GuildChannelUnowned]: "The fetched channel does not belong to this manager's guild.", + [DjsErrorCodes.GuildOwned]: 'Guild is owned by the client.', + [DjsErrorCodes.GuildMembersTimeout]: "Members didn't arrive in time.", + [DjsErrorCodes.GuildUncachedMe]: 'The client user as a member of this guild is uncached.', + [DjsErrorCodes.ChannelNotCached]: 'Could not find the channel where this message came from in the cache!', + [DjsErrorCodes.StageChannelResolve]: 'Could not resolve channel to a stage channel.', + [DjsErrorCodes.GuildScheduledEventResolve]: 'Could not resolve the guild scheduled event.', + [DjsErrorCodes.FetchOwnerId]: "Couldn't resolve the guild ownerId to fetch the member.", - INVALID_TYPE: (name, expected, an = false) => `Supplied ${name} is not a${an ? 'n' : ''} ${expected}.`, - INVALID_ELEMENT: (type, name, elem) => `Supplied ${type} ${name} includes an invalid element: ${elem}`, + [DjsErrorCodes.InvalidType]: (name, expected, an = false) => `Supplied ${name} is not a${an ? 'n' : ''} ${expected}.`, + [DjsErrorCodes.InvalidElement]: (type, name, elem) => `Supplied ${type} ${name} includes an invalid element: ${elem}`, - MESSAGE_THREAD_PARENT: 'The message was not sent in a guild text or news channel', - MESSAGE_EXISTING_THREAD: 'The message already has a thread', - THREAD_INVITABLE_TYPE: type => `Invitable cannot be edited on ${type}`, + [DjsErrorCodes.MessageThreadParent]: 'The message was not sent in a guild text or news channel', + [DjsErrorCodes.MessageExistingThread]: 'The message already has a thread', + [DjsErrorCodes.ThreadInvitableType]: type => `Invitable cannot be edited on ${type}`, - WEBHOOK_MESSAGE: 'The message was not sent by a webhook.', - WEBHOOK_TOKEN_UNAVAILABLE: 'This action requires a webhook token, but none is available.', - WEBHOOK_URL_INVALID: 'The provided webhook URL is not valid.', - WEBHOOK_APPLICATION: 'This message webhook belongs to an application and cannot be fetched.', - MESSAGE_REFERENCE_MISSING: 'The message does not reference another message', + [DjsErrorCodes.WebhookMessage]: 'The message was not sent by a webhook.', + [DjsErrorCodes.WebhookTokenUnavailable]: 'This action requires a webhook token, but none is available.', + [DjsErrorCodes.WebhookURLInvalid]: 'The provided webhook URL is not valid.', + [DjsErrorCodes.WebhookApplication]: 'This message webhook belongs to an application and cannot be fetched.', + [DjsErrorCodes.MessageReferenceMissing]: 'The message does not reference another message', - EMOJI_TYPE: 'Emoji must be a string or GuildEmoji/ReactionEmoji', - EMOJI_MANAGED: 'Emoji is managed and has no Author.', - MISSING_MANAGE_EMOJIS_AND_STICKERS_PERMISSION: guild => + [DjsErrorCodes.EmojiType]: 'Emoji must be a string or GuildEmoji/ReactionEmoji', + [DjsErrorCodes.EmojiManaged]: 'Emoji is managed and has no Author.', + [DjsErrorCodes.MissingManageEmojisAndStickersPermission]: guild => `Client must have Manage Emojis and Stickers permission in guild ${guild} to see emoji authors.`, - NOT_GUILD_STICKER: 'Sticker is a standard (non-guild) sticker and has no author.', + [DjsErrorCodes.NotGuildSticker]: 'Sticker is a standard (non-guild) sticker and has no author.', - REACTION_RESOLVE_USER: "Couldn't resolve the user id to remove from the reaction.", + [DjsErrorCodes.ReactionResolveUser]: "Couldn't resolve the user id to remove from the reaction.", - VANITY_URL: 'This guild does not have the VANITY_URL feature enabled.', + [DjsErrorCodes.VanityURL]: 'This guild does not have the vanity URL feature enabled.', - INVITE_RESOLVE_CODE: 'Could not resolve the code to fetch the invite.', + [DjsErrorCodes.InviteResolveCode]: 'Could not resolve the code to fetch the invite.', - INVITE_NOT_FOUND: 'Could not find the requested invite.', + [DjsErrorCodes.InviteNotFound]: 'Could not find the requested invite.', - DELETE_GROUP_DM_CHANNEL: "Bots don't have access to Group DM Channels and cannot delete them", - FETCH_GROUP_DM_CHANNEL: "Bots don't have access to Group DM Channels and cannot fetch them", + [DjsErrorCodes.DeleteGroupDMChannel]: "Bots don't have access to Group DM Channels and cannot delete them", + [DjsErrorCodes.FetchGroupDMChannel]: "Bots don't have access to Group DM Channels and cannot fetch them", - MEMBER_FETCH_NONCE_LENGTH: 'Nonce length must not exceed 32 characters.', + [DjsErrorCodes.MemberFetchNonceLength]: 'Nonce length must not exceed 32 characters.', - GLOBAL_COMMAND_PERMISSIONS: + [DjsErrorCodes.GlobalCommandPermissions]: 'Permissions for global commands may only be fetched or modified by providing a GuildResolvable ' + "or from a guild's application command manager.", - GUILD_UNCACHED_ENTITY_RESOLVE: type => `Cannot resolve ${type} from an arbitrary guild, provide an id instead`, + [DjsErrorCodes.GuildUncachedEntityResolve]: type => + `Cannot resolve ${type} from an arbitrary guild, provide an id instead`, - INTERACTION_ALREADY_REPLIED: 'The reply to this interaction has already been sent or deferred.', - INTERACTION_NOT_REPLIED: 'The reply to this interaction has not been sent or deferred.', - INTERACTION_EPHEMERAL_REPLIED: 'Ephemeral responses cannot be deleted.', + [DjsErrorCodes.InteractionAlreadyReplied]: 'The reply to this interaction has already been sent or deferred.', + [DjsErrorCodes.InteractionNotReplied]: 'The reply to this interaction has not been sent or deferred.', + [DjsErrorCodes.InteractionEphemeralReplied]: 'Ephemeral responses cannot be deleted.', - COMMAND_INTERACTION_OPTION_NOT_FOUND: name => `Required option "${name}" not found.`, - COMMAND_INTERACTION_OPTION_TYPE: (name, type, expected) => + [DjsErrorCodes.CommandInteractionOptionNotFound]: name => `Required option "${name}" not found.`, + [DjsErrorCodes.CommandInteractionOptionType]: (name, type, expected) => `Option "${name}" is of type: ${type}; expected ${expected}.`, - COMMAND_INTERACTION_OPTION_EMPTY: (name, type) => + [DjsErrorCodes.CommandInteractionOptionEmpty]: (name, type) => `Required option "${name}" is of type: ${type}; expected a non-empty value.`, - COMMAND_INTERACTION_OPTION_NO_SUB_COMMAND: 'No subcommand specified for interaction.', - COMMAND_INTERACTION_OPTION_NO_SUB_COMMAND_GROUP: 'No subcommand group specified for interaction.', - AUTOCOMPLETE_INTERACTION_OPTION_NO_FOCUSED_OPTION: 'No focused option for autocomplete interaction.', + [DjsErrorCodes.CommandInteractionOptionNoSubcommand]: 'No subcommand specified for interaction.', + [DjsErrorCodes.CommandInteractionOptionNoSubcommandGroup]: 'No subcommand group specified for interaction.', + [DjsErrorCodes.AutocompleteInteractionOptionNoFocusedOption]: 'No focused option for autocomplete interaction.', - MODAL_SUBMIT_INTERACTION_FIELD_NOT_FOUND: customId => `Required field with custom id "${customId}" not found.`, - MODAL_SUBMIT_INTERACTION_FIELD_TYPE: (customId, type, expected) => + [DjsErrorCodes.ModalSubmitInteractionFieldNotFound]: customId => + `Required field with custom id "${customId}" not found.`, + [DjsErrorCodes.ModalSubmitInteractionFieldType]: (customId, type, expected) => `Field with custom id "${customId}" is of type: ${type}; expected ${expected}.`, - INVITE_MISSING_SCOPES: 'At least one valid scope must be provided for the invite', + [DjsErrorCodes.InvalidMissingScopes]: 'At least one valid scope must be provided for the invite', - NOT_IMPLEMENTED: (what, name) => `Method ${what} not implemented on ${name}.`, + [DjsErrorCodes.NotImplemented]: (what, name) => `Method ${what} not implemented on ${name}.`, - SWEEP_FILTER_RETURN: 'The return value of the sweepFilter function was not false or a Function', + [DjsErrorCodes.SweepFilterReturn]: 'The return value of the sweepFilter function was not false or a Function', }; -Messages.AuthenticationFailed = Messages.TOKEN_INVALID; -Messages.InvalidShard = Messages.SHARDING_INVALID; -Messages.ShardingRequired = Messages.SHARDING_REQUIRED; -Messages.InvalidIntents = Messages.INVALID_INTENTS; -Messages.DisallowedIntents = Messages.DISALLOWED_INTENTS; +// Magic needed by WS +Messages.AuthenticationFailed = Messages[DjsErrorCodes.AuthenticationFailed]; +Messages.InvalidShard = Messages[DjsErrorCodes.ShardingInvalid]; +Messages.ShardingRequired = Messages[DjsErrorCodes.ShardingRequired]; +Messages.InvalidIntents = Messages[DjsErrorCodes.InvalidIntents]; +Messages.DisallowedIntents = Messages[DjsErrorCodes.DisallowedIntents]; -for (const [name, message] of Object.entries(Messages)) register(name, message); +module.exports = Messages; diff --git a/packages/discord.js/src/errors/index.js b/packages/discord.js/src/errors/index.js index c94ddc788..78dc5c6ad 100644 --- a/packages/discord.js/src/errors/index.js +++ b/packages/discord.js/src/errors/index.js @@ -1,4 +1,5 @@ 'use strict'; module.exports = require('./DJSError'); +module.exports.ErrorCodes = require('./ErrorCodes'); module.exports.Messages = require('./Messages'); diff --git a/packages/discord.js/src/index.js b/packages/discord.js/src/index.js index f1d490b74..cec87f861 100644 --- a/packages/discord.js/src/index.js +++ b/packages/discord.js/src/index.js @@ -10,6 +10,13 @@ exports.ShardClientUtil = require('./sharding/ShardClientUtil'); exports.ShardingManager = require('./sharding/ShardingManager'); exports.WebhookClient = require('./client/WebhookClient'); +// Errors +const { Error, TypeError, RangeError } = require('./errors/DJSError'); +exports.DiscordjsError = Error; +exports.DiscordjsTypeError = TypeError; +exports.DiscordjsRangeError = RangeError; +exports.DiscordjsErrorCodes = require('./errors/ErrorCodes'); + // Utilities exports.ActivityFlagsBitField = require('./util/ActivityFlagsBitField'); exports.ApplicationFlagsBitField = require('./util/ApplicationFlagsBitField'); diff --git a/packages/discord.js/src/managers/ApplicationCommandManager.js b/packages/discord.js/src/managers/ApplicationCommandManager.js index f66b66a7c..a72871498 100644 --- a/packages/discord.js/src/managers/ApplicationCommandManager.js +++ b/packages/discord.js/src/managers/ApplicationCommandManager.js @@ -4,7 +4,7 @@ const { Collection } = require('@discordjs/collection'); const { Routes } = require('discord-api-types/v10'); const ApplicationCommandPermissionsManager = require('./ApplicationCommandPermissionsManager'); const CachedManager = require('./CachedManager'); -const { TypeError } = require('../errors'); +const { TypeError, ErrorCodes } = require('../errors'); const ApplicationCommand = require('../structures/ApplicationCommand'); const PermissionsBitField = require('../util/PermissionsBitField'); @@ -187,7 +187,7 @@ class ApplicationCommandManager extends CachedManager { */ async edit(command, data, guildId) { const id = this.resolveId(command); - if (!id) throw new TypeError('INVALID_TYPE', 'command', 'ApplicationCommandResolvable'); + if (!id) throw new TypeError(ErrorCodes.InvalidType, 'command', 'ApplicationCommandResolvable'); const patched = await this.client.rest.patch(this.commandPath({ id, guildId }), { body: this.constructor.transformCommand(data), @@ -209,7 +209,7 @@ class ApplicationCommandManager extends CachedManager { */ async delete(command, guildId) { const id = this.resolveId(command); - if (!id) throw new TypeError('INVALID_TYPE', 'command', 'ApplicationCommandResolvable'); + if (!id) throw new TypeError(ErrorCodes.InvalidType, 'command', 'ApplicationCommandResolvable'); await this.client.rest.delete(this.commandPath({ id, guildId })); diff --git a/packages/discord.js/src/managers/ApplicationCommandPermissionsManager.js b/packages/discord.js/src/managers/ApplicationCommandPermissionsManager.js index 22fae07e5..e24988d53 100644 --- a/packages/discord.js/src/managers/ApplicationCommandPermissionsManager.js +++ b/packages/discord.js/src/managers/ApplicationCommandPermissionsManager.js @@ -3,7 +3,7 @@ const { Collection } = require('@discordjs/collection'); const { ApplicationCommandPermissionType, RESTJSONErrorCodes, Routes } = require('discord-api-types/v10'); const BaseManager = require('./BaseManager'); -const { Error, TypeError } = require('../errors'); +const { Error, TypeError, ErrorCodes } = require('../errors'); /** * Manages API methods for permissions of Application Commands. @@ -153,12 +153,12 @@ class ApplicationCommandPermissionsManager extends BaseManager { */ async set({ guild, command, permissions, token } = {}) { if (!token) { - throw new Error('APPLICATION_COMMAND_PERMISSIONS_TOKEN_MISSING'); + throw new Error(ErrorCodes.ApplicationCommandPermissionsTokenMissing); } let { guildId, commandId } = this._validateOptions(guild, command); if (!Array.isArray(permissions)) { - throw new TypeError('INVALID_TYPE', 'permissions', 'Array of ApplicationCommandPermissions', true); + throw new TypeError(ErrorCodes.InvalidType, 'permissions', 'Array of ApplicationCommandPermissions', true); } if (!commandId) { @@ -190,14 +190,14 @@ class ApplicationCommandPermissionsManager extends BaseManager { */ async add({ guild, command, permissions, token } = {}) { if (!token) { - throw new Error('APPLICATION_COMMAND_PERMISSIONS_TOKEN_MISSING'); + throw new Error(ErrorCodes.ApplicationCommandPermissionsTokenMissing); } let { guildId, commandId } = this._validateOptions(guild, command); if (!commandId) { commandId = this.client.user.id; } if (!Array.isArray(permissions)) { - throw new TypeError('INVALID_TYPE', 'permissions', 'Array of ApplicationCommandPermissions', true); + throw new TypeError(ErrorCodes.InvalidType, 'permissions', 'Array of ApplicationCommandPermissions', true); } let existing = []; @@ -262,7 +262,7 @@ class ApplicationCommandPermissionsManager extends BaseManager { */ async remove({ guild, command, users, roles, channels, token } = {}) { if (!token) { - throw new Error('APPLICATION_COMMAND_PERMISSIONS_TOKEN_MISSING'); + throw new Error(ErrorCodes.ApplicationCommandPermissionsTokenMissing); } let { guildId, commandId } = this._validateOptions(guild, command); if (!commandId) { @@ -270,14 +270,14 @@ class ApplicationCommandPermissionsManager extends BaseManager { } if (!users && !roles && !channels) { - throw new TypeError('INVALID_TYPE', 'users OR roles OR channels', 'Array or Resolvable', true); + throw new TypeError(ErrorCodes.InvalidType, 'users OR roles OR channels', 'Array or Resolvable', true); } let resolvedUserIds = []; if (Array.isArray(users)) { for (const user of users) { const userId = this.client.users.resolveId(user); - if (!userId) throw new TypeError('INVALID_ELEMENT', 'Array', 'users', user); + if (!userId) throw new TypeError(ErrorCodes.InvalidElement, 'Array', 'users', user); resolvedUserIds.push(userId); } } @@ -289,9 +289,9 @@ class ApplicationCommandPermissionsManager extends BaseManager { resolvedRoleIds.push(role); continue; } - if (!this.guild) throw new Error('GUILD_UNCACHED_ENTITY_RESOLVE', 'roles'); + if (!this.guild) throw new Error(ErrorCodes.GuildUncachedEntityResolve, 'roles'); const roleId = this.guild.roles.resolveId(role); - if (!roleId) throw new TypeError('INVALID_ELEMENT', 'Array', 'users', role); + if (!roleId) throw new TypeError(ErrorCodes.InvalidElement, 'Array', 'users', role); resolvedRoleIds.push(roleId); } } @@ -303,9 +303,9 @@ class ApplicationCommandPermissionsManager extends BaseManager { resolvedChannelIds.push(channel); continue; } - if (!this.guild) throw new Error('GUILD_UNCACHED_ENTITY_RESOLVE', 'channels'); + if (!this.guild) throw new Error(ErrorCodes.GuildUncachedEntityResolve, 'channels'); const channelId = this.guild.channels.resolveId(channel); - if (!channelId) throw new TypeError('INVALID_ELEMENT', 'Array', 'channels', channel); + if (!channelId) throw new TypeError(ErrorCodes.InvalidElement, 'Array', 'channels', channel); resolvedChannelIds.push(channelId); } } @@ -353,11 +353,11 @@ class ApplicationCommandPermissionsManager extends BaseManager { */ async has({ guild, command, permissionId, permissionType }) { const { guildId, commandId } = this._validateOptions(guild, command); - if (!commandId) throw new TypeError('INVALID_TYPE', 'command', 'ApplicationCommandResolvable'); + if (!commandId) throw new TypeError(ErrorCodes.InvalidType, 'command', 'ApplicationCommandResolvable'); if (!permissionId) { throw new TypeError( - 'INVALID_TYPE', + ErrorCodes.InvalidType, 'permissionId', 'UserResolvable, RoleResolvable, ChannelResolvable, or Permission Constant', ); @@ -366,7 +366,7 @@ class ApplicationCommandPermissionsManager extends BaseManager { if (typeof permissionId !== 'string') { resolvedId = this.client.users.resolveId(permissionId); if (!resolvedId) { - if (!this.guild) throw new Error('GUILD_UNCACHED_ENTITY_RESOLVE', 'roles'); + if (!this.guild) throw new Error(ErrorCodes.GuildUncachedEntityResolve, 'roles'); resolvedId = this.guild.roles.resolveId(permissionId); } if (!resolvedId) { @@ -374,7 +374,7 @@ class ApplicationCommandPermissionsManager extends BaseManager { } if (!resolvedId) { throw new TypeError( - 'INVALID_TYPE', + ErrorCodes.InvalidType, 'permissionId', 'UserResolvable, RoleResolvable, ChannelResolvable, or Permission Constant', ); @@ -394,7 +394,7 @@ class ApplicationCommandPermissionsManager extends BaseManager { _validateOptions(guild, command) { const guildId = this.guildId ?? this.client.guilds.resolveId(guild); - if (!guildId) throw new Error('GLOBAL_COMMAND_PERMISSIONS'); + if (!guildId) throw new Error(ErrorCodes.GlobalCommandPermissions); let commandId = this.commandId; if (command && !commandId) { commandId = this.manager.resolveId?.(command); @@ -403,7 +403,7 @@ class ApplicationCommandPermissionsManager extends BaseManager { } commandId ??= this.client.application?.commands.resolveId(command); if (!commandId) { - throw new TypeError('INVALID_TYPE', 'command', 'ApplicationCommandResolvable', true); + throw new TypeError(ErrorCodes.InvalidType, 'command', 'ApplicationCommandResolvable', true); } } return { guildId, commandId }; diff --git a/packages/discord.js/src/managers/DataManager.js b/packages/discord.js/src/managers/DataManager.js index 6a5c14ca2..d46852bd5 100644 --- a/packages/discord.js/src/managers/DataManager.js +++ b/packages/discord.js/src/managers/DataManager.js @@ -1,7 +1,7 @@ 'use strict'; const BaseManager = require('./BaseManager'); -const { Error } = require('../errors'); +const { Error, ErrorCodes } = require('../errors'); /** * Manages the API methods of a data model along with a collection of instances. @@ -28,7 +28,7 @@ class DataManager extends BaseManager { * @abstract */ get cache() { - throw new Error('NOT_IMPLEMENTED', 'get cache', this.constructor.name); + throw new Error(ErrorCodes.NotImplemented, 'get cache', this.constructor.name); } /** diff --git a/packages/discord.js/src/managers/GuildBanManager.js b/packages/discord.js/src/managers/GuildBanManager.js index bedcf2e83..430860492 100644 --- a/packages/discord.js/src/managers/GuildBanManager.js +++ b/packages/discord.js/src/managers/GuildBanManager.js @@ -4,7 +4,7 @@ const { Collection } = require('@discordjs/collection'); const { makeURLSearchParams } = require('@discordjs/rest'); const { Routes } = require('discord-api-types/v10'); const CachedManager = require('./CachedManager'); -const { TypeError, Error } = require('../errors'); +const { TypeError, Error, ErrorCodes } = require('../errors'); const GuildBan = require('../structures/GuildBan'); const { GuildMember } = require('../structures/GuildMember'); @@ -101,7 +101,7 @@ class GuildBanManager extends CachedManager { if (resolvedUser) return this._fetchSingle({ user: resolvedUser, cache, force }); if (!before && !after && !limit && typeof cache === 'undefined') { - return Promise.reject(new Error('FETCH_BAN_RESOLVE_ID')); + return Promise.reject(new Error(ErrorCodes.FetchBanResolveId)); } return this._fetchMany(options); @@ -146,9 +146,9 @@ class GuildBanManager extends CachedManager { * .catch(console.error); */ async create(user, options = {}) { - if (typeof options !== 'object') throw new TypeError('INVALID_TYPE', 'options', 'object', true); + if (typeof options !== 'object') throw new TypeError(ErrorCodes.InvalidType, 'options', 'object', true); const id = this.client.users.resolveId(user); - if (!id) throw new Error('BAN_RESOLVE_ID', true); + if (!id) throw new Error(ErrorCodes.BanResolveId, true); await this.client.rest.put(Routes.guildBan(this.guild.id, id), { body: { delete_message_days: options.deleteMessageDays }, reason: options.reason, @@ -174,7 +174,7 @@ class GuildBanManager extends CachedManager { */ async remove(user, reason) { const id = this.client.users.resolveId(user); - if (!id) throw new Error('BAN_RESOLVE_ID'); + if (!id) throw new Error(ErrorCodes.BanResolveId); await this.client.rest.delete(Routes.guildBan(this.guild.id, id), { reason }); return this.client.users.resolve(user); } diff --git a/packages/discord.js/src/managers/GuildChannelManager.js b/packages/discord.js/src/managers/GuildChannelManager.js index e1ba72da8..bb64c832b 100644 --- a/packages/discord.js/src/managers/GuildChannelManager.js +++ b/packages/discord.js/src/managers/GuildChannelManager.js @@ -5,7 +5,7 @@ const { Collection } = require('@discordjs/collection'); const { ChannelType, Routes } = require('discord-api-types/v10'); const CachedManager = require('./CachedManager'); const ThreadManager = require('./ThreadManager'); -const { Error, TypeError } = require('../errors'); +const { Error, TypeError, ErrorCodes } = require('../errors'); const GuildChannel = require('../structures/GuildChannel'); const PermissionOverwrites = require('../structures/PermissionOverwrites'); const ThreadChannel = require('../structures/ThreadChannel'); @@ -184,7 +184,7 @@ class GuildChannelManager extends CachedManager { */ async createWebhook({ channel, name, avatar, reason }) { const id = this.resolveId(channel); - if (!id) throw new TypeError('INVALID_TYPE', 'channel', 'GuildChannelResolvable'); + if (!id) throw new TypeError(ErrorCodes.InvalidType, 'channel', 'GuildChannelResolvable'); if (typeof avatar === 'string' && !avatar.startsWith('data:')) { avatar = await DataResolver.resolveImage(avatar); } @@ -234,7 +234,7 @@ class GuildChannelManager extends CachedManager { */ async edit(channel, data) { channel = this.resolve(channel); - if (!channel) throw new TypeError('INVALID_TYPE', 'channel', 'GuildChannelResolvable'); + if (!channel) throw new TypeError(ErrorCodes.InvalidType, 'channel', 'GuildChannelResolvable'); const parent = data.parent && this.client.channels.resolveId(data.parent); @@ -295,7 +295,7 @@ class GuildChannelManager extends CachedManager { */ async setPosition(channel, position, { relative, reason } = {}) { channel = this.resolve(channel); - if (!channel) throw new TypeError('INVALID_TYPE', 'channel', 'GuildChannelResolvable'); + if (!channel) throw new TypeError(ErrorCodes.InvalidType, 'channel', 'GuildChannelResolvable'); const updatedChannels = await setPosition( channel, position, @@ -338,7 +338,7 @@ class GuildChannelManager extends CachedManager { if (id) { const data = await this.client.rest.get(Routes.channel(id)); // Since this is the guild manager, throw if on a different guild - if (this.guild.id !== data.guild_id) throw new Error('GUILD_CHANNEL_UNOWNED'); + if (this.guild.id !== data.guild_id) throw new Error(ErrorCodes.GuildChannelUnowned); return this.client.channels._add(data, this.guild, { cache }); } @@ -360,7 +360,7 @@ class GuildChannelManager extends CachedManager { */ async fetchWebhooks(channel) { const id = this.resolveId(channel); - if (!id) throw new TypeError('INVALID_TYPE', 'channel', 'GuildChannelResolvable'); + if (!id) throw new TypeError(ErrorCodes.InvalidType, 'channel', 'GuildChannelResolvable'); const data = await this.client.rest.get(Routes.channelWebhooks(id)); return data.reduce((hooks, hook) => hooks.set(hook.id, new Webhook(this.client, hook)), new Collection()); } @@ -434,7 +434,7 @@ class GuildChannelManager extends CachedManager { */ async delete(channel, reason) { const id = this.resolveId(channel); - if (!id) throw new TypeError('INVALID_TYPE', 'channel', 'GuildChannelResolvable'); + if (!id) throw new TypeError(ErrorCodes.InvalidType, 'channel', 'GuildChannelResolvable'); await this.client.rest.delete(Routes.channel(id), { reason }); this.client.actions.ChannelDelete.handle({ id }); } diff --git a/packages/discord.js/src/managers/GuildEmojiManager.js b/packages/discord.js/src/managers/GuildEmojiManager.js index 02ede2981..408f650e6 100644 --- a/packages/discord.js/src/managers/GuildEmojiManager.js +++ b/packages/discord.js/src/managers/GuildEmojiManager.js @@ -3,7 +3,7 @@ const { Collection } = require('@discordjs/collection'); const { Routes, PermissionFlagsBits } = require('discord-api-types/v10'); const BaseGuildEmojiManager = require('./BaseGuildEmojiManager'); -const { Error, TypeError } = require('../errors'); +const { Error, TypeError, ErrorCodes } = require('../errors'); const DataResolver = require('../util/DataResolver'); /** @@ -51,17 +51,24 @@ class GuildEmojiManager extends BaseGuildEmojiManager { */ async create({ attachment, name, roles, reason }) { attachment = await DataResolver.resolveImage(attachment); - if (!attachment) throw new TypeError('REQ_RESOURCE_TYPE'); + if (!attachment) throw new TypeError(ErrorCodes.ReqResourceType); const body = { image: attachment, name }; if (roles) { if (!Array.isArray(roles) && !(roles instanceof Collection)) { - throw new TypeError('INVALID_TYPE', 'options.roles', 'Array or Collection of Roles or Snowflakes', true); + throw new TypeError( + ErrorCodes.InvalidType, + 'options.roles', + 'Array or Collection of Roles or Snowflakes', + true, + ); } body.roles = []; for (const role of roles.values()) { const resolvedRole = this.guild.roles.resolveId(role); - if (!resolvedRole) throw new TypeError('INVALID_ELEMENT', 'Array or Collection', 'options.roles', role); + if (!resolvedRole) { + throw new TypeError(ErrorCodes.InvalidElement, 'Array or Collection', 'options.roles', role); + } body.roles.push(resolvedRole); } } @@ -110,7 +117,7 @@ class GuildEmojiManager extends BaseGuildEmojiManager { */ async delete(emoji, reason) { const id = this.resolveId(emoji); - if (!id) throw new TypeError('INVALID_TYPE', 'emoji', 'EmojiResolvable', true); + if (!id) throw new TypeError(ErrorCodes.InvalidType, 'emoji', 'EmojiResolvable', true); await this.client.rest.delete(Routes.guildEmoji(this.guild.id, id), { reason }); } @@ -122,7 +129,7 @@ class GuildEmojiManager extends BaseGuildEmojiManager { */ async edit(emoji, data) { const id = this.resolveId(emoji); - if (!id) throw new TypeError('INVALID_TYPE', 'emoji', 'EmojiResolvable', true); + if (!id) throw new TypeError(ErrorCodes.InvalidType, 'emoji', 'EmojiResolvable', true); const roles = data.roles?.map(r => this.guild.roles.resolveId(r)); const newData = await this.client.rest.patch(Routes.guildEmoji(this.guild.id, id), { body: { @@ -147,15 +154,15 @@ class GuildEmojiManager extends BaseGuildEmojiManager { */ async fetchAuthor(emoji) { emoji = this.resolve(emoji); - if (!emoji) throw new TypeError('INVALID_TYPE', 'emoji', 'EmojiResolvable', true); + if (!emoji) throw new TypeError(ErrorCodes.InvalidType, 'emoji', 'EmojiResolvable', true); if (emoji.managed) { - throw new Error('EMOJI_MANAGED'); + throw new Error(ErrorCodes.EmojiManaged); } const { me } = this.guild.members; - if (!me) throw new Error('GUILD_UNCACHED_ME'); + if (!me) throw new Error(ErrorCodes.GuildUncachedMe); if (!me.permissions.has(PermissionFlagsBits.ManageEmojisAndStickers)) { - throw new Error('MISSING_MANAGE_EMOJIS_AND_STICKERS_PERMISSION', this.guild); + throw new Error(ErrorCodes.MissingManageEmojisAndStickersPermission, this.guild); } const data = await this.client.rest.get(Routes.guildEmoji(this.guild.id, emoji.id)); diff --git a/packages/discord.js/src/managers/GuildEmojiRoleManager.js b/packages/discord.js/src/managers/GuildEmojiRoleManager.js index f8da58d2b..d1abf9207 100644 --- a/packages/discord.js/src/managers/GuildEmojiRoleManager.js +++ b/packages/discord.js/src/managers/GuildEmojiRoleManager.js @@ -2,7 +2,7 @@ const { Collection } = require('@discordjs/collection'); const DataManager = require('./DataManager'); -const { TypeError } = require('../errors'); +const { TypeError, ErrorCodes } = require('../errors'); const { Role } = require('../structures/Role'); /** @@ -46,7 +46,7 @@ class GuildEmojiRoleManager extends DataManager { for (const role of roleOrRoles.values()) { const resolvedRole = this.guild.roles.resolveId(role); if (!resolvedRole) { - return Promise.reject(new TypeError('INVALID_ELEMENT', 'Array or Collection', 'roles', role)); + return Promise.reject(new TypeError(ErrorCodes.InvalidElement, 'Array or Collection', 'roles', role)); } resolvedRoles.push(resolvedRole); } @@ -67,7 +67,7 @@ class GuildEmojiRoleManager extends DataManager { for (const role of roleOrRoles.values()) { const roleId = this.guild.roles.resolveId(role); if (!roleId) { - return Promise.reject(new TypeError('INVALID_ELEMENT', 'Array or Collection', 'roles', role)); + return Promise.reject(new TypeError(ErrorCodes.InvalidElement, 'Array or Collection', 'roles', role)); } resolvedRoleIds.push(roleId); } diff --git a/packages/discord.js/src/managers/GuildInviteManager.js b/packages/discord.js/src/managers/GuildInviteManager.js index 0764e120f..6667db851 100644 --- a/packages/discord.js/src/managers/GuildInviteManager.js +++ b/packages/discord.js/src/managers/GuildInviteManager.js @@ -3,7 +3,7 @@ const { Collection } = require('@discordjs/collection'); const { Routes } = require('discord-api-types/v10'); const CachedManager = require('./CachedManager'); -const { Error } = require('../errors'); +const { Error, ErrorCodes } = require('../errors'); const Invite = require('../structures/Invite'); const DataResolver = require('../util/DataResolver'); @@ -123,18 +123,18 @@ class GuildInviteManager extends CachedManager { if (!options) return this._fetchMany(); if (typeof options === 'string') { const code = DataResolver.resolveInviteCode(options); - if (!code) return Promise.reject(new Error('INVITE_RESOLVE_CODE')); + if (!code) return Promise.reject(new Error(ErrorCodes.InviteResolveCode)); return this._fetchSingle({ code, cache: true }); } if (!options.code) { if (options.channelId) { const id = this.guild.channels.resolveId(options.channelId); - if (!id) return Promise.reject(new Error('GUILD_CHANNEL_RESOLVE')); + if (!id) return Promise.reject(new Error(ErrorCodes.GuildChannelResolve)); return this._fetchChannelMany(id, options.cache); } if ('cache' in options) return this._fetchMany(options.cache); - return Promise.reject(new Error('INVITE_RESOLVE_CODE')); + return Promise.reject(new Error(ErrorCodes.InviteResolveCode)); } return this._fetchSingle({ ...options, @@ -150,7 +150,7 @@ class GuildInviteManager extends CachedManager { const invites = await this._fetchMany(cache); const invite = invites.get(code); - if (!invite) throw new Error('INVITE_NOT_FOUND'); + if (!invite) throw new Error(ErrorCodes.InviteNotFound); return invite; } @@ -180,7 +180,7 @@ class GuildInviteManager extends CachedManager { { temporary, maxAge, maxUses, unique, targetUser, targetApplication, targetType, reason } = {}, ) { const id = this.guild.channels.resolveId(channel); - if (!id) throw new Error('GUILD_CHANNEL_RESOLVE'); + if (!id) throw new Error(ErrorCodes.GuildChannelResolve); const invite = await this.client.rest.post(Routes.channelInvites(id), { body: { diff --git a/packages/discord.js/src/managers/GuildMemberManager.js b/packages/discord.js/src/managers/GuildMemberManager.js index adabe3c93..aa5db057f 100644 --- a/packages/discord.js/src/managers/GuildMemberManager.js +++ b/packages/discord.js/src/managers/GuildMemberManager.js @@ -7,7 +7,7 @@ const { makeURLSearchParams } = require('@discordjs/rest'); const { DiscordSnowflake } = require('@sapphire/snowflake'); const { Routes, GatewayOpcodes } = require('discord-api-types/v10'); const CachedManager = require('./CachedManager'); -const { Error, TypeError, RangeError } = require('../errors'); +const { Error, TypeError, RangeError, ErrorCodes } = require('../errors'); const BaseGuildVoiceChannel = require('../structures/BaseGuildVoiceChannel'); const { GuildMember } = require('../structures/GuildMember'); const { Role } = require('../structures/Role'); @@ -93,7 +93,7 @@ class GuildMemberManager extends CachedManager { */ async add(user, options) { const userId = this.client.users.resolveId(user); - if (!userId) throw new TypeError('INVALID_TYPE', 'user', 'UserResolvable'); + if (!userId) throw new TypeError(ErrorCodes.InvalidType, 'user', 'UserResolvable'); if (!options.force) { const cachedUser = this.cache.get(userId); if (cachedUser) return cachedUser; @@ -106,12 +106,19 @@ class GuildMemberManager extends CachedManager { }; if (options.roles) { if (!Array.isArray(options.roles) && !(options.roles instanceof Collection)) { - throw new TypeError('INVALID_TYPE', 'options.roles', 'Array or Collection of Roles or Snowflakes', true); + throw new TypeError( + ErrorCodes.InvalidType, + 'options.roles', + 'Array or Collection of Roles or Snowflakes', + true, + ); } const resolvedRoles = []; for (const role of options.roles.values()) { const resolvedRole = this.guild.roles.resolveId(role); - if (!resolvedRole) throw new TypeError('INVALID_ELEMENT', 'Array or Collection', 'options.roles', role); + if (!resolvedRole) { + throw new TypeError(ErrorCodes.InvalidElement, 'Array or Collection', 'options.roles', role); + } resolvedRoles.push(resolvedRole); } resolvedOptions.roles = resolvedRoles; @@ -277,12 +284,12 @@ class GuildMemberManager extends CachedManager { */ async edit(user, { reason, ...data }) { const id = this.client.users.resolveId(user); - if (!id) throw new TypeError('INVALID_TYPE', 'user', 'UserResolvable'); + if (!id) throw new TypeError(ErrorCodes.InvalidType, 'user', 'UserResolvable'); if (data.channel) { data.channel = this.guild.channels.resolve(data.channel); if (!(data.channel instanceof BaseGuildVoiceChannel)) { - throw new Error('GUILD_VOICE_CHANNEL_RESOLVE'); + throw new Error(ErrorCodes.GuildVoiceChannelResolve); } data.channel_id = data.channel.id; data.channel = undefined; @@ -346,7 +353,7 @@ class GuildMemberManager extends CachedManager { * .catch(console.error); */ async prune({ days, dry = false, count: compute_prune_count, roles = [], reason } = {}) { - if (typeof days !== 'number') throw new TypeError('PRUNE_DAYS_TYPE'); + if (typeof days !== 'number') throw new TypeError(ErrorCodes.PruneDaysType); const query = { days }; const resolvedRoles = []; @@ -354,7 +361,7 @@ class GuildMemberManager extends CachedManager { for (const role of roles) { const resolvedRole = this.guild.roles.resolveId(role); if (!resolvedRole) { - throw new TypeError('INVALID_ELEMENT', 'Array', 'options.roles', role); + throw new TypeError(ErrorCodes.InvalidElement, 'Array', 'options.roles', role); } resolvedRoles.push(resolvedRole); } @@ -388,7 +395,7 @@ class GuildMemberManager extends CachedManager { */ async kick(user, reason) { const id = this.client.users.resolveId(user); - if (!id) return Promise.reject(new TypeError('INVALID_TYPE', 'user', 'UserResolvable')); + if (!id) return Promise.reject(new TypeError(ErrorCodes.InvalidType, 'user', 'UserResolvable')); await this.client.rest.delete(Routes.guildMember(this.guild.id, id), { reason }); @@ -448,7 +455,7 @@ class GuildMemberManager extends CachedManager { } = {}) { return new Promise((resolve, reject) => { if (!query && !user_ids) query = ''; - if (nonce.length > 32) throw new RangeError('MEMBER_FETCH_NONCE_LENGTH'); + if (nonce.length > 32) throw new RangeError(ErrorCodes.MemberFetchNonceLength); this.guild.shard.send({ op: GatewayOpcodes.RequestGuildMembers, d: { @@ -481,7 +488,7 @@ class GuildMemberManager extends CachedManager { const timeout = setTimeout(() => { this.client.removeListener(Events.GuildMembersChunk, handler); this.client.decrementMaxListeners(); - reject(new Error('GUILD_MEMBERS_TIMEOUT')); + reject(new Error(ErrorCodes.GuildMembersTimeout)); }, time).unref(); this.client.incrementMaxListeners(); this.client.on(Events.GuildMembersChunk, handler); diff --git a/packages/discord.js/src/managers/GuildMemberRoleManager.js b/packages/discord.js/src/managers/GuildMemberRoleManager.js index e6712ded0..9fcf8ace6 100644 --- a/packages/discord.js/src/managers/GuildMemberRoleManager.js +++ b/packages/discord.js/src/managers/GuildMemberRoleManager.js @@ -3,7 +3,7 @@ const { Collection } = require('@discordjs/collection'); const { Routes } = require('discord-api-types/v10'); const DataManager = require('./DataManager'); -const { TypeError } = require('../errors'); +const { TypeError, ErrorCodes } = require('../errors'); const { Role } = require('../structures/Role'); /** @@ -110,7 +110,7 @@ class GuildMemberRoleManager extends DataManager { const resolvedRoles = []; for (const role of roleOrRoles.values()) { const resolvedRole = this.guild.roles.resolveId(role); - if (!resolvedRole) throw new TypeError('INVALID_ELEMENT', 'Array or Collection', 'roles', role); + if (!resolvedRole) throw new TypeError(ErrorCodes.InvalidElement, 'Array or Collection', 'roles', role); resolvedRoles.push(resolvedRole); } @@ -119,7 +119,11 @@ class GuildMemberRoleManager extends DataManager { } else { roleOrRoles = this.guild.roles.resolveId(roleOrRoles); if (roleOrRoles === null) { - throw new TypeError('INVALID_TYPE', 'roles', 'Role, Snowflake or Array or Collection of Roles or Snowflakes'); + throw new TypeError( + ErrorCodes.InvalidType, + 'roles', + 'Role, Snowflake or Array or Collection of Roles or Snowflakes', + ); } await this.client.rest.put(Routes.guildMemberRole(this.guild.id, this.member.id, roleOrRoles), { reason }); @@ -141,7 +145,7 @@ class GuildMemberRoleManager extends DataManager { const resolvedRoles = []; for (const role of roleOrRoles.values()) { const resolvedRole = this.guild.roles.resolveId(role); - if (!resolvedRole) throw new TypeError('INVALID_ELEMENT', 'Array or Collection', 'roles', role); + if (!resolvedRole) throw new TypeError(ErrorCodes.InvalidElement, 'Array or Collection', 'roles', role); resolvedRoles.push(resolvedRole); } @@ -150,7 +154,11 @@ class GuildMemberRoleManager extends DataManager { } else { roleOrRoles = this.guild.roles.resolveId(roleOrRoles); if (roleOrRoles === null) { - throw new TypeError('INVALID_TYPE', 'roles', 'Role, Snowflake or Array or Collection of Roles or Snowflakes'); + throw new TypeError( + ErrorCodes.InvalidType, + 'roles', + 'Role, Snowflake or Array or Collection of Roles or Snowflakes', + ); } await this.client.rest.delete(Routes.guildMemberRole(this.guild.id, this.member.id, roleOrRoles), { reason }); diff --git a/packages/discord.js/src/managers/GuildScheduledEventManager.js b/packages/discord.js/src/managers/GuildScheduledEventManager.js index 34e30bbdd..2f00aa312 100644 --- a/packages/discord.js/src/managers/GuildScheduledEventManager.js +++ b/packages/discord.js/src/managers/GuildScheduledEventManager.js @@ -4,7 +4,7 @@ const { Collection } = require('@discordjs/collection'); const { makeURLSearchParams } = require('@discordjs/rest'); const { GuildScheduledEventEntityType, Routes } = require('discord-api-types/v10'); const CachedManager = require('./CachedManager'); -const { TypeError, Error } = require('../errors'); +const { TypeError, Error, ErrorCodes } = require('../errors'); const { GuildScheduledEvent } = require('../structures/GuildScheduledEvent'); const DataResolver = require('../util/DataResolver'); @@ -69,7 +69,7 @@ class GuildScheduledEventManager extends CachedManager { * @returns {Promise} */ async create(options) { - if (typeof options !== 'object') throw new TypeError('INVALID_TYPE', 'options', 'object', true); + if (typeof options !== 'object') throw new TypeError(ErrorCodes.InvalidType, 'options', 'object', true); let { privacyLevel, entityType, @@ -89,7 +89,7 @@ class GuildScheduledEventManager extends CachedManager { entity_metadata = { location: entityMetadata?.location }; } else { channel_id = this.guild.channels.resolveId(channel); - if (!channel_id) throw new Error('GUILD_VOICE_CHANNEL_RESOLVE'); + if (!channel_id) throw new Error(ErrorCodes.GuildVoiceChannelResolve); entity_metadata = typeof entityMetadata === 'undefined' ? entityMetadata : null; } @@ -188,9 +188,9 @@ class GuildScheduledEventManager extends CachedManager { */ async edit(guildScheduledEvent, options) { const guildScheduledEventId = this.resolveId(guildScheduledEvent); - if (!guildScheduledEventId) throw new Error('GUILD_SCHEDULED_EVENT_RESOLVE'); + if (!guildScheduledEventId) throw new Error(ErrorCodes.GuildScheduledEventResolve); - if (typeof options !== 'object') throw new TypeError('INVALID_TYPE', 'options', 'object', true); + if (typeof options !== 'object') throw new TypeError(ErrorCodes.InvalidType, 'options', 'object', true); let { privacyLevel, entityType, @@ -238,7 +238,7 @@ class GuildScheduledEventManager extends CachedManager { */ async delete(guildScheduledEvent) { const guildScheduledEventId = this.resolveId(guildScheduledEvent); - if (!guildScheduledEventId) throw new Error('GUILD_SCHEDULED_EVENT_RESOLVE'); + if (!guildScheduledEventId) throw new Error(ErrorCodes.GuildScheduledEventResolve); await this.client.rest.delete(Routes.guildScheduledEvent(this.guild.id, guildScheduledEventId)); } @@ -269,7 +269,7 @@ class GuildScheduledEventManager extends CachedManager { */ async fetchSubscribers(guildScheduledEvent, options = {}) { const guildScheduledEventId = this.resolveId(guildScheduledEvent); - if (!guildScheduledEventId) throw new Error('GUILD_SCHEDULED_EVENT_RESOLVE'); + if (!guildScheduledEventId) throw new Error(ErrorCodes.GuildScheduledEventResolve); const query = makeURLSearchParams({ limit: options.limit, diff --git a/packages/discord.js/src/managers/GuildStickerManager.js b/packages/discord.js/src/managers/GuildStickerManager.js index 1d1f37f95..f8b93248e 100644 --- a/packages/discord.js/src/managers/GuildStickerManager.js +++ b/packages/discord.js/src/managers/GuildStickerManager.js @@ -3,7 +3,7 @@ const { Collection } = require('@discordjs/collection'); const { Routes } = require('discord-api-types/v10'); const CachedManager = require('./CachedManager'); -const { TypeError } = require('../errors'); +const { TypeError, ErrorCodes } = require('../errors'); const MessagePayload = require('../structures/MessagePayload'); const { Sticker } = require('../structures/Sticker'); @@ -59,7 +59,7 @@ class GuildStickerManager extends CachedManager { */ async create({ file, name, tags, description, reason } = {}) { const resolvedFile = await MessagePayload.resolveFile(file); - if (!resolvedFile) throw new TypeError('REQ_RESOURCE_TYPE'); + if (!resolvedFile) throw new TypeError(ErrorCodes.ReqResourceType); file = { ...resolvedFile, key: 'file' }; const body = { name, tags, description: description ?? '' }; @@ -106,7 +106,7 @@ class GuildStickerManager extends CachedManager { */ async edit(sticker, data = {}) { const stickerId = this.resolveId(sticker); - if (!stickerId) throw new TypeError('INVALID_TYPE', 'sticker', 'StickerResolvable'); + if (!stickerId) throw new TypeError(ErrorCodes.InvalidType, 'sticker', 'StickerResolvable'); const d = await this.client.rest.patch(Routes.guildSticker(this.guild.id, stickerId), { body: data, @@ -130,7 +130,7 @@ class GuildStickerManager extends CachedManager { */ async delete(sticker, reason) { sticker = this.resolveId(sticker); - if (!sticker) throw new TypeError('INVALID_TYPE', 'sticker', 'StickerResolvable'); + if (!sticker) throw new TypeError(ErrorCodes.InvalidType, 'sticker', 'StickerResolvable'); await this.client.rest.delete(Routes.guildSticker(this.guild.id, sticker), { reason }); } @@ -172,7 +172,7 @@ class GuildStickerManager extends CachedManager { */ async fetchUser(sticker) { sticker = this.resolve(sticker); - if (!sticker) throw new TypeError('INVALID_TYPE', 'sticker', 'StickerResolvable'); + if (!sticker) throw new TypeError(ErrorCodes.InvalidType, 'sticker', 'StickerResolvable'); const data = await this.client.rest.get(Routes.guildSticker(this.guild.id, sticker.id)); sticker._patch(data); return sticker.user; diff --git a/packages/discord.js/src/managers/MessageManager.js b/packages/discord.js/src/managers/MessageManager.js index 36d1115a5..2b188e5e5 100644 --- a/packages/discord.js/src/managers/MessageManager.js +++ b/packages/discord.js/src/managers/MessageManager.js @@ -4,7 +4,7 @@ const { Collection } = require('@discordjs/collection'); const { makeURLSearchParams } = require('@discordjs/rest'); const { Routes } = require('discord-api-types/v10'); const CachedManager = require('./CachedManager'); -const { TypeError } = require('../errors'); +const { TypeError, ErrorCodes } = require('../errors'); const { Message } = require('../structures/Message'); const MessagePayload = require('../structures/MessagePayload'); const { resolvePartialEmoji } = require('../util/Util'); @@ -155,7 +155,7 @@ class MessageManager extends CachedManager { */ async edit(message, options) { const messageId = this.resolveId(message); - if (!messageId) throw new TypeError('INVALID_TYPE', 'message', 'MessageResolvable'); + if (!messageId) throw new TypeError(ErrorCodes.InvalidType, 'message', 'MessageResolvable'); const { body, files } = await (options instanceof MessagePayload ? options @@ -181,7 +181,7 @@ class MessageManager extends CachedManager { */ async crosspost(message) { message = this.resolveId(message); - if (!message) throw new TypeError('INVALID_TYPE', 'message', 'MessageResolvable'); + if (!message) throw new TypeError(ErrorCodes.InvalidType, 'message', 'MessageResolvable'); const data = await this.client.rest.post(Routes.channelMessageCrosspost(this.channel.id, message)); return this.cache.get(data.id) ?? this._add(data); @@ -195,7 +195,7 @@ class MessageManager extends CachedManager { */ async pin(message, reason) { message = this.resolveId(message); - if (!message) throw new TypeError('INVALID_TYPE', 'message', 'MessageResolvable'); + if (!message) throw new TypeError(ErrorCodes.InvalidType, 'message', 'MessageResolvable'); await this.client.rest.put(Routes.channelPin(this.channel.id, message), { reason }); } @@ -208,7 +208,7 @@ class MessageManager extends CachedManager { */ async unpin(message, reason) { message = this.resolveId(message); - if (!message) throw new TypeError('INVALID_TYPE', 'message', 'MessageResolvable'); + if (!message) throw new TypeError(ErrorCodes.InvalidType, 'message', 'MessageResolvable'); await this.client.rest.delete(Routes.channelPin(this.channel.id, message), { reason }); } @@ -221,10 +221,10 @@ class MessageManager extends CachedManager { */ async react(message, emoji) { message = this.resolveId(message); - if (!message) throw new TypeError('INVALID_TYPE', 'message', 'MessageResolvable'); + if (!message) throw new TypeError(ErrorCodes.InvalidType, 'message', 'MessageResolvable'); emoji = resolvePartialEmoji(emoji); - if (!emoji) throw new TypeError('EMOJI_TYPE', 'emoji', 'EmojiIdentifierResolvable'); + if (!emoji) throw new TypeError(ErrorCodes.EmojiType, 'emoji', 'EmojiIdentifierResolvable'); const emojiId = emoji.id ? `${emoji.animated ? 'a:' : ''}${emoji.name}:${emoji.id}` @@ -240,7 +240,7 @@ class MessageManager extends CachedManager { */ async delete(message) { message = this.resolveId(message); - if (!message) throw new TypeError('INVALID_TYPE', 'message', 'MessageResolvable'); + if (!message) throw new TypeError(ErrorCodes.InvalidType, 'message', 'MessageResolvable'); await this.client.rest.delete(Routes.channelMessage(this.channel.id, message)); } diff --git a/packages/discord.js/src/managers/PermissionOverwriteManager.js b/packages/discord.js/src/managers/PermissionOverwriteManager.js index efbaa0779..2f3f4b2dc 100644 --- a/packages/discord.js/src/managers/PermissionOverwriteManager.js +++ b/packages/discord.js/src/managers/PermissionOverwriteManager.js @@ -4,7 +4,7 @@ const process = require('node:process'); const { Collection } = require('@discordjs/collection'); const { OverwriteType, Routes } = require('discord-api-types/v10'); const CachedManager = require('./CachedManager'); -const { TypeError } = require('../errors'); +const { TypeError, ErrorCodes } = require('../errors'); const PermissionOverwrites = require('../structures/PermissionOverwrites'); const { Role } = require('../structures/Role'); @@ -65,7 +65,7 @@ class PermissionOverwriteManager extends CachedManager { set(overwrites, reason) { if (!Array.isArray(overwrites) && !(overwrites instanceof Collection)) { return Promise.reject( - new TypeError('INVALID_TYPE', 'overwrites', 'Array or Collection of Permission Overwrites', true), + new TypeError(ErrorCodes.InvalidType, 'overwrites', 'Array or Collection of Permission Overwrites', true), ); } return this.channel.edit({ permissionOverwrites: overwrites, reason }); @@ -93,7 +93,7 @@ class PermissionOverwriteManager extends CachedManager { let { type, reason } = overwriteOptions; if (typeof type !== 'number') { userOrRole = this.channel.guild.roles.resolve(userOrRole) ?? this.client.users.resolve(userOrRole); - if (!userOrRole) throw new TypeError('INVALID_TYPE', 'parameter', 'User nor a Role'); + if (!userOrRole) throw new TypeError(ErrorCodes.InvalidType, 'parameter', 'User nor a Role'); type = userOrRole instanceof Role ? OverwriteType.Role : OverwriteType.Member; } @@ -152,7 +152,7 @@ class PermissionOverwriteManager extends CachedManager { */ async delete(userOrRole, reason) { const userOrRoleId = this.channel.guild.roles.resolveId(userOrRole) ?? this.client.users.resolveId(userOrRole); - if (!userOrRoleId) throw new TypeError('INVALID_TYPE', 'parameter', 'User nor a Role'); + if (!userOrRoleId) throw new TypeError(ErrorCodes.InvalidType, 'parameter', 'User nor a Role'); await this.client.rest.delete(Routes.channelPermission(this.channel.id, userOrRoleId), { reason }); return this.channel; diff --git a/packages/discord.js/src/managers/ReactionUserManager.js b/packages/discord.js/src/managers/ReactionUserManager.js index 5eb151acd..857badac6 100644 --- a/packages/discord.js/src/managers/ReactionUserManager.js +++ b/packages/discord.js/src/managers/ReactionUserManager.js @@ -4,7 +4,7 @@ const { Collection } = require('@discordjs/collection'); const { makeURLSearchParams } = require('@discordjs/rest'); const { Routes } = require('discord-api-types/v10'); const CachedManager = require('./CachedManager'); -const { Error } = require('../errors'); +const { Error, ErrorCodes } = require('../errors'); const User = require('../structures/User'); /** @@ -63,7 +63,7 @@ class ReactionUserManager extends CachedManager { */ async remove(user = this.client.user) { const userId = this.client.users.resolveId(user); - if (!userId) throw new Error('REACTION_RESOLVE_USER'); + if (!userId) throw new Error(ErrorCodes.ReactionResolveUser); const message = this.reaction.message; const route = userId === this.client.user.id diff --git a/packages/discord.js/src/managers/RoleManager.js b/packages/discord.js/src/managers/RoleManager.js index 5ffa7939a..02f58d048 100644 --- a/packages/discord.js/src/managers/RoleManager.js +++ b/packages/discord.js/src/managers/RoleManager.js @@ -4,7 +4,7 @@ const process = require('node:process'); const { Collection } = require('@discordjs/collection'); const { Routes } = require('discord-api-types/v10'); const CachedManager = require('./CachedManager'); -const { TypeError } = require('../errors'); +const { TypeError, ErrorCodes } = require('../errors'); const { Role } = require('../structures/Role'); const DataResolver = require('../util/DataResolver'); const PermissionsBitField = require('../util/PermissionsBitField'); @@ -183,7 +183,7 @@ class RoleManager extends CachedManager { */ async edit(role, data) { role = this.resolve(role); - if (!role) throw new TypeError('INVALID_TYPE', 'role', 'RoleResolvable'); + if (!role) throw new TypeError(ErrorCodes.InvalidType, 'role', 'RoleResolvable'); if (typeof data.position === 'number') { await this.setPosition(role, data.position, { reason: data.reason }); @@ -244,7 +244,7 @@ class RoleManager extends CachedManager { */ async setPosition(role, position, { relative, reason } = {}) { role = this.resolve(role); - if (!role) throw new TypeError('INVALID_TYPE', 'role', 'RoleResolvable'); + if (!role) throw new TypeError(ErrorCodes.InvalidType, 'role', 'RoleResolvable'); const updatedRoles = await setPosition( role, position, @@ -303,7 +303,7 @@ class RoleManager extends CachedManager { comparePositions(role1, role2) { const resolvedRole1 = this.resolve(role1); const resolvedRole2 = this.resolve(role2); - if (!resolvedRole1 || !resolvedRole2) throw new TypeError('INVALID_TYPE', 'role', 'Role nor a Snowflake'); + if (!resolvedRole1 || !resolvedRole2) throw new TypeError(ErrorCodes.InvalidType, 'role', 'Role nor a Snowflake'); if (resolvedRole1.position === resolvedRole2.position) { return Number(BigInt(resolvedRole2.id) - BigInt(resolvedRole1.id)); diff --git a/packages/discord.js/src/managers/StageInstanceManager.js b/packages/discord.js/src/managers/StageInstanceManager.js index 1217a0969..97a79506f 100644 --- a/packages/discord.js/src/managers/StageInstanceManager.js +++ b/packages/discord.js/src/managers/StageInstanceManager.js @@ -2,7 +2,7 @@ const { Routes } = require('discord-api-types/v10'); const CachedManager = require('./CachedManager'); -const { TypeError, Error } = require('../errors'); +const { TypeError, Error, ErrorCodes } = require('../errors'); const { StageInstance } = require('../structures/StageInstance'); /** @@ -57,8 +57,8 @@ class StageInstanceManager extends CachedManager { */ async create(channel, options) { const channelId = this.guild.channels.resolveId(channel); - if (!channelId) throw new Error('STAGE_CHANNEL_RESOLVE'); - if (typeof options !== 'object') throw new TypeError('INVALID_TYPE', 'options', 'object', true); + if (!channelId) throw new Error(ErrorCodes.StageChannelResolve); + if (typeof options !== 'object') throw new TypeError(ErrorCodes.InvalidType, 'options', 'object', true); let { topic, privacyLevel, sendStartNotification } = options; const data = await this.client.rest.post(Routes.stageInstances(), { @@ -86,7 +86,7 @@ class StageInstanceManager extends CachedManager { */ async fetch(channel, { cache = true, force = false } = {}) { const channelId = this.guild.channels.resolveId(channel); - if (!channelId) throw new Error('STAGE_CHANNEL_RESOLVE'); + if (!channelId) throw new Error(ErrorCodes.StageChannelResolve); if (!force) { const existing = this.cache.find(stageInstance => stageInstance.channelId === channelId); @@ -116,9 +116,9 @@ class StageInstanceManager extends CachedManager { * .catch(console.error); */ async edit(channel, options) { - if (typeof options !== 'object') throw new TypeError('INVALID_TYPE', 'options', 'object', true); + if (typeof options !== 'object') throw new TypeError(ErrorCodes.InvalidType, 'options', 'object', true); const channelId = this.guild.channels.resolveId(channel); - if (!channelId) throw new Error('STAGE_CHANNEL_RESOLVE'); + if (!channelId) throw new Error(ErrorCodes.StageChannelResolve); let { topic, privacyLevel } = options; @@ -145,7 +145,7 @@ class StageInstanceManager extends CachedManager { */ async delete(channel) { const channelId = this.guild.channels.resolveId(channel); - if (!channelId) throw new Error('STAGE_CHANNEL_RESOLVE'); + if (!channelId) throw new Error(ErrorCodes.StageChannelResolve); await this.client.rest.delete(Routes.stageInstance(channelId)); } diff --git a/packages/discord.js/src/managers/ThreadManager.js b/packages/discord.js/src/managers/ThreadManager.js index 3be79b37f..5c9392cca 100644 --- a/packages/discord.js/src/managers/ThreadManager.js +++ b/packages/discord.js/src/managers/ThreadManager.js @@ -4,7 +4,7 @@ const { Collection } = require('@discordjs/collection'); const { makeURLSearchParams } = require('@discordjs/rest'); const { ChannelType, Routes } = require('discord-api-types/v10'); const CachedManager = require('./CachedManager'); -const { TypeError } = require('../errors'); +const { TypeError, ErrorCodes } = require('../errors'); const ThreadChannel = require('../structures/ThreadChannel'); /** @@ -110,14 +110,14 @@ class ThreadManager extends CachedManager { rateLimitPerUser, } = {}) { if (type && typeof type !== 'string' && typeof type !== 'number') { - throw new TypeError('INVALID_TYPE', 'type', 'ThreadChannelType or Number'); + throw new TypeError(ErrorCodes.InvalidType, 'type', 'ThreadChannelType or Number'); } let resolvedType = this.channel.type === ChannelType.GuildNews ? ChannelType.GuildNewsThread : ChannelType.GuildPublicThread; let startMessageId; if (startMessage) { startMessageId = this.channel.messages.resolveId(startMessage); - if (!startMessageId) throw new TypeError('INVALID_TYPE', 'startMessage', 'MessageResolvable'); + if (!startMessageId) throw new TypeError(ErrorCodes.InvalidType, 'startMessage', 'MessageResolvable'); } else if (this.channel.type !== ChannelType.GuildNews) { resolvedType = type ?? resolvedType; } @@ -221,7 +221,7 @@ class ThreadManager extends CachedManager { query.set('before', timestamp); } } catch { - throw new TypeError('INVALID_TYPE', 'before', 'DateResolvable or ThreadChannelResolvable'); + throw new TypeError(ErrorCodes.InvalidType, 'before', 'DateResolvable or ThreadChannelResolvable'); } } } diff --git a/packages/discord.js/src/managers/ThreadMemberManager.js b/packages/discord.js/src/managers/ThreadMemberManager.js index 84003febb..30e393682 100644 --- a/packages/discord.js/src/managers/ThreadMemberManager.js +++ b/packages/discord.js/src/managers/ThreadMemberManager.js @@ -3,7 +3,7 @@ const { Collection } = require('@discordjs/collection'); const { Routes } = require('discord-api-types/v10'); const CachedManager = require('./CachedManager'); -const { TypeError } = require('../errors'); +const { TypeError, ErrorCodes } = require('../errors'); const ThreadMember = require('../structures/ThreadMember'); /** @@ -95,7 +95,7 @@ class ThreadMemberManager extends CachedManager { */ async add(member, reason) { const id = member === '@me' ? member : this.client.users.resolveId(member); - if (!id) throw new TypeError('INVALID_TYPE', 'member', 'UserResolvable'); + if (!id) throw new TypeError(ErrorCodes.InvalidType, 'member', 'UserResolvable'); await this.client.rest.put(Routes.threadMembers(this.thread.id, id), { reason }); return id; } diff --git a/packages/discord.js/src/managers/UserManager.js b/packages/discord.js/src/managers/UserManager.js index 15021f1f1..76a35d720 100644 --- a/packages/discord.js/src/managers/UserManager.js +++ b/packages/discord.js/src/managers/UserManager.js @@ -2,6 +2,7 @@ const { ChannelType, Routes } = require('discord-api-types/v10'); const CachedManager = require('./CachedManager'); +const { ErrorCodes } = require('../errors'); const { GuildMember } = require('../structures/GuildMember'); const { Message } = require('../structures/Message'); const ThreadMember = require('../structures/ThreadMember'); @@ -68,7 +69,7 @@ class UserManager extends CachedManager { async deleteDM(user) { const id = this.resolveId(user); const dmChannel = this.dmChannel(id); - if (!dmChannel) throw new Error('USER_NO_DM_CHANNEL'); + if (!dmChannel) throw new Error(ErrorCodes.UserNoDMChannel); await this.client.rest.delete(Routes.channel(dmChannel.id)); this.client.channels._remove(dmChannel.id); return dmChannel; diff --git a/packages/discord.js/src/sharding/Shard.js b/packages/discord.js/src/sharding/Shard.js index 902e240ac..646e5e21d 100644 --- a/packages/discord.js/src/sharding/Shard.js +++ b/packages/discord.js/src/sharding/Shard.js @@ -5,7 +5,7 @@ const path = require('node:path'); const process = require('node:process'); const { setTimeout, clearTimeout } = require('node:timers'); const { setTimeout: sleep } = require('node:timers/promises'); -const { Error } = require('../errors'); +const { Error, ErrorCodes } = require('../errors'); const { makeError, makePlainError } = require('../util/Util'); let childProcess = null; let Worker = null; @@ -106,8 +106,8 @@ class Shard extends EventEmitter { * @returns {Promise} */ spawn(timeout = 30_000) { - if (this.process) throw new Error('SHARDING_PROCESS_EXISTS', this.id); - if (this.worker) throw new Error('SHARDING_WORKER_EXISTS', this.id); + if (this.process) throw new Error(ErrorCodes.ShardingProcessExists, this.id); + if (this.worker) throw new Error(ErrorCodes.ShardingWorkerExists, this.id); this._exitListener = this._handleExit.bind(this, undefined, timeout); @@ -153,17 +153,17 @@ class Shard extends EventEmitter { const onDisconnect = () => { cleanup(); - reject(new Error('SHARDING_READY_DISCONNECTED', this.id)); + reject(new Error(ErrorCodes.ShardingReadyDisconnected, this.id)); }; const onDeath = () => { cleanup(); - reject(new Error('SHARDING_READY_DIED', this.id)); + reject(new Error(ErrorCodes.ShardingReadyDied, this.id)); }; const onTimeout = () => { cleanup(); - reject(new Error('SHARDING_READY_TIMEOUT', this.id)); + reject(new Error(ErrorCodes.ShardingReadyTimeout, this.id)); }; const spawnTimeoutTimer = setTimeout(onTimeout, timeout); @@ -238,7 +238,7 @@ class Shard extends EventEmitter { */ fetchClientValue(prop) { // Shard is dead (maybe respawning), don't cache anything and error immediately - if (!this.process && !this.worker) return Promise.reject(new Error('SHARDING_NO_CHILD_EXISTS', this.id)); + if (!this.process && !this.worker) return Promise.reject(new Error(ErrorCodes.ShardingNoChildExists, this.id)); // Cached promise from previous call if (this._fetches.has(prop)) return this._fetches.get(prop); @@ -281,7 +281,7 @@ class Shard extends EventEmitter { const _eval = typeof script === 'function' ? `(${script})(this, ${JSON.stringify(context)})` : script; // Shard is dead (maybe respawning), don't cache anything and error immediately - if (!this.process && !this.worker) return Promise.reject(new Error('SHARDING_NO_CHILD_EXISTS', this.id)); + if (!this.process && !this.worker) return Promise.reject(new Error(ErrorCodes.ShardingNoChildExists, this.id)); // Cached promise from previous call if (this._evals.has(_eval)) return this._evals.get(_eval); diff --git a/packages/discord.js/src/sharding/ShardClientUtil.js b/packages/discord.js/src/sharding/ShardClientUtil.js index 478b72ca1..269d5c6c6 100644 --- a/packages/discord.js/src/sharding/ShardClientUtil.js +++ b/packages/discord.js/src/sharding/ShardClientUtil.js @@ -1,7 +1,7 @@ 'use strict'; const process = require('node:process'); -const { Error } = require('../errors'); +const { Error, ErrorCodes } = require('../errors'); const Events = require('../util/Events'); const { makeError, makePlainError } = require('../util/Util'); @@ -141,7 +141,7 @@ class ShardClientUtil { return new Promise((resolve, reject) => { const parent = this.parentPort ?? process; if (typeof script !== 'function') { - reject(new TypeError('SHARDING_INVALID_EVAL_BROADCAST')); + reject(new TypeError(ErrorCodes.ShardingInvalidEvalBroadcast)); return; } script = `(${script})(this, ${JSON.stringify(options.context)})`; @@ -246,7 +246,7 @@ class ShardClientUtil { */ static shardIdForGuildId(guildId, shardCount) { const shard = Number(BigInt(guildId) >> 22n) % shardCount; - if (shard < 0) throw new Error('SHARDING_SHARD_MISCALCULATION', shard, guildId, shardCount); + if (shard < 0) throw new Error(ErrorCodes.ShardingShardMiscalculation, shard, guildId, shardCount); return shard; } diff --git a/packages/discord.js/src/sharding/ShardingManager.js b/packages/discord.js/src/sharding/ShardingManager.js index 2014d7a70..a4b3c87a0 100644 --- a/packages/discord.js/src/sharding/ShardingManager.js +++ b/packages/discord.js/src/sharding/ShardingManager.js @@ -7,7 +7,7 @@ const process = require('node:process'); const { setTimeout: sleep } = require('node:timers/promises'); const { Collection } = require('@discordjs/collection'); const Shard = require('./Shard'); -const { Error, TypeError, RangeError } = require('../errors'); +const { Error, TypeError, RangeError, ErrorCodes } = require('../errors'); const { mergeDefault, fetchRecommendedShards } = require('../util/Util'); /** @@ -64,10 +64,10 @@ class ShardingManager extends EventEmitter { * @type {string} */ this.file = file; - if (!file) throw new Error('CLIENT_INVALID_OPTION', 'File', 'specified.'); + if (!file) throw new Error(ErrorCodes.ClientInvalidOption, 'File', 'specified.'); if (!path.isAbsolute(file)) this.file = path.resolve(process.cwd(), file); const stats = fs.statSync(this.file); - if (!stats.isFile()) throw new Error('CLIENT_INVALID_OPTION', 'File', 'a file'); + if (!stats.isFile()) throw new Error(ErrorCodes.ClientInvalidOption, 'File', 'a file'); /** * List of shards this sharding manager spawns @@ -76,16 +76,18 @@ class ShardingManager extends EventEmitter { this.shardList = options.shardList ?? 'auto'; if (this.shardList !== 'auto') { if (!Array.isArray(this.shardList)) { - throw new TypeError('CLIENT_INVALID_OPTION', 'shardList', 'an array.'); + throw new TypeError(ErrorCodes.ClientInvalidOption, 'shardList', 'an array.'); } this.shardList = [...new Set(this.shardList)]; - if (this.shardList.length < 1) throw new RangeError('CLIENT_INVALID_OPTION', 'shardList', 'at least 1 id.'); + if (this.shardList.length < 1) { + throw new RangeError(ErrorCodes.ClientInvalidOption, 'shardList', 'at least 1 id.'); + } if ( this.shardList.some( shardId => typeof shardId !== 'number' || isNaN(shardId) || !Number.isInteger(shardId) || shardId < 0, ) ) { - throw new TypeError('CLIENT_INVALID_OPTION', 'shardList', 'an array of positive integers.'); + throw new TypeError(ErrorCodes.ClientInvalidOption, 'shardList', 'an array of positive integers.'); } } @@ -96,11 +98,13 @@ class ShardingManager extends EventEmitter { this.totalShards = options.totalShards || 'auto'; if (this.totalShards !== 'auto') { if (typeof this.totalShards !== 'number' || isNaN(this.totalShards)) { - throw new TypeError('CLIENT_INVALID_OPTION', 'Amount of shards', 'a number.'); + throw new TypeError(ErrorCodes.ClientInvalidOption, 'Amount of shards', 'a number.'); + } + if (this.totalShards < 1) { + throw new RangeError(ErrorCodes.ClientInvalidOption, 'Amount of shards', 'at least 1.'); } - if (this.totalShards < 1) throw new RangeError('CLIENT_INVALID_OPTION', 'Amount of shards', 'at least 1.'); if (!Number.isInteger(this.totalShards)) { - throw new RangeError('CLIENT_INVALID_OPTION', 'Amount of shards', 'an integer.'); + throw new RangeError(ErrorCodes.ClientInvalidOption, 'Amount of shards', 'an integer.'); } } @@ -110,7 +114,7 @@ class ShardingManager extends EventEmitter { */ this.mode = options.mode; if (this.mode !== 'process' && this.mode !== 'worker') { - throw new RangeError('CLIENT_INVALID_OPTION', 'Sharding mode', '"process" or "worker"'); + throw new RangeError(ErrorCodes.ClientInvalidOption, 'Sharding mode', '"process" or "worker"'); } /** @@ -186,16 +190,16 @@ class ShardingManager extends EventEmitter { amount = await fetchRecommendedShards(this.token); } else { if (typeof amount !== 'number' || isNaN(amount)) { - throw new TypeError('CLIENT_INVALID_OPTION', 'Amount of shards', 'a number.'); + throw new TypeError(ErrorCodes.ClientInvalidOption, 'Amount of shards', 'a number.'); } - if (amount < 1) throw new RangeError('CLIENT_INVALID_OPTION', 'Amount of shards', 'at least 1.'); + if (amount < 1) throw new RangeError(ErrorCodes.ClientInvalidOption, 'Amount of shards', 'at least 1.'); if (!Number.isInteger(amount)) { - throw new TypeError('CLIENT_INVALID_OPTION', 'Amount of shards', 'an integer.'); + throw new TypeError(ErrorCodes.ClientInvalidOption, 'Amount of shards', 'an integer.'); } } // Make sure this many shards haven't already been spawned - if (this.shards.size >= amount) throw new Error('SHARDING_ALREADY_SPAWNED', this.shards.size); + if (this.shards.size >= amount) throw new Error(ErrorCodes.ShardingAlreadySpawned, this.shards.size); if (this.shardList === 'auto' || this.totalShards === 'auto' || this.totalShards !== amount) { this.shardList = [...Array(amount).keys()]; } @@ -205,7 +209,7 @@ class ShardingManager extends EventEmitter { if (this.shardList.some(shardId => shardId >= amount)) { throw new RangeError( - 'CLIENT_INVALID_OPTION', + ErrorCodes.ClientInvalidOption, 'Amount of shards', 'bigger than the highest shardId in the shardList option.', ); @@ -248,7 +252,7 @@ class ShardingManager extends EventEmitter { * @returns {Promise<*|Array<*>>} Results of the script execution */ broadcastEval(script, options = {}) { - if (typeof script !== 'function') return Promise.reject(new TypeError('SHARDING_INVALID_EVAL_BROADCAST')); + if (typeof script !== 'function') return Promise.reject(new TypeError(ErrorCodes.ShardingInvalidEvalBroadcast)); return this._performOnShards('eval', [`(${script})(this, ${JSON.stringify(options.context)})`], options.shard); } @@ -275,14 +279,14 @@ class ShardingManager extends EventEmitter { * @private */ _performOnShards(method, args, shard) { - if (this.shards.size === 0) return Promise.reject(new Error('SHARDING_NO_SHARDS')); + if (this.shards.size === 0) return Promise.reject(new Error(ErrorCodes.ShardingNoShards)); if (typeof shard === 'number') { if (this.shards.has(shard)) return this.shards.get(shard)[method](...args); - return Promise.reject(new Error('SHARDING_SHARD_NOT_FOUND', shard)); + return Promise.reject(new Error(ErrorCodes.ShardingShardNotFound, shard)); } - if (this.shards.size !== this.shardList.length) return Promise.reject(new Error('SHARDING_IN_PROCESS')); + if (this.shards.size !== this.shardList.length) return Promise.reject(new Error(ErrorCodes.ShardingInProcess)); const promises = []; for (const sh of this.shards.values()) promises.push(sh[method](...args)); diff --git a/packages/discord.js/src/structures/AutocompleteInteraction.js b/packages/discord.js/src/structures/AutocompleteInteraction.js index 8c8174572..403fc97bd 100644 --- a/packages/discord.js/src/structures/AutocompleteInteraction.js +++ b/packages/discord.js/src/structures/AutocompleteInteraction.js @@ -3,6 +3,7 @@ const { InteractionResponseType, Routes } = require('discord-api-types/v10'); const CommandInteractionOptionResolver = require('./CommandInteractionOptionResolver'); const Interaction = require('./Interaction'); +const { ErrorCodes } = require('../errors'); /** * Represents an autocomplete interaction. @@ -80,7 +81,7 @@ class AutocompleteInteraction extends Interaction { * .catch(console.error); */ async respond(options) { - if (this.responded) throw new Error('INTERACTION_ALREADY_REPLIED'); + if (this.responded) throw new Error(ErrorCodes.InteractionAlreadyReplied); await this.client.rest.post(Routes.interactionCallback(this.id, this.token), { body: { diff --git a/packages/discord.js/src/structures/ClientPresence.js b/packages/discord.js/src/structures/ClientPresence.js index 0661ee186..db769d852 100644 --- a/packages/discord.js/src/structures/ClientPresence.js +++ b/packages/discord.js/src/structures/ClientPresence.js @@ -2,7 +2,7 @@ const { GatewayOpcodes } = require('discord-api-types/v10'); const { Presence } = require('./Presence'); -const { TypeError } = require('../errors'); +const { TypeError, ErrorCodes } = require('../errors'); /** * Represents the client's presence. @@ -48,7 +48,9 @@ class ClientPresence extends Presence { }; if (activities?.length) { for (const [i, activity] of activities.entries()) { - if (typeof activity.name !== 'string') throw new TypeError('INVALID_TYPE', `activities[${i}].name`, 'string'); + if (typeof activity.name !== 'string') { + throw new TypeError(ErrorCodes.InvalidType, `activities[${i}].name`, 'string'); + } activity.type ??= 0; data.activities.push({ diff --git a/packages/discord.js/src/structures/CommandInteractionOptionResolver.js b/packages/discord.js/src/structures/CommandInteractionOptionResolver.js index c28945f90..3fe08bbf1 100644 --- a/packages/discord.js/src/structures/CommandInteractionOptionResolver.js +++ b/packages/discord.js/src/structures/CommandInteractionOptionResolver.js @@ -1,7 +1,7 @@ 'use strict'; const { ApplicationCommandOptionType } = require('discord-api-types/v10'); -const { TypeError } = require('../errors'); +const { TypeError, ErrorCodes } = require('../errors'); /** * A resolver for command interaction options. @@ -75,7 +75,7 @@ class CommandInteractionOptionResolver { const option = this._hoistedOptions.find(opt => opt.name === name); if (!option) { if (required) { - throw new TypeError('COMMAND_INTERACTION_OPTION_NOT_FOUND', name); + throw new TypeError(ErrorCodes.CommandInteractionOptionNotFound, name); } return null; } @@ -96,9 +96,9 @@ class CommandInteractionOptionResolver { if (!option) { return null; } else if (option.type !== type) { - throw new TypeError('COMMAND_INTERACTION_OPTION_TYPE', name, 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('COMMAND_INTERACTION_OPTION_EMPTY', name, option.type); + throw new TypeError(ErrorCodes.CommandInteractionOptionEmpty, name, option.type); } return option; } @@ -110,7 +110,7 @@ class CommandInteractionOptionResolver { */ getSubcommand(required = true) { if (required && !this._subcommand) { - throw new TypeError('COMMAND_INTERACTION_OPTION_NO_SUB_COMMAND'); + throw new TypeError(ErrorCodes.CommandInteractionOptionNoSubcommand); } return this._subcommand; } @@ -122,7 +122,7 @@ class CommandInteractionOptionResolver { */ getSubcommandGroup(required = false) { if (required && !this._group) { - throw new TypeError('COMMAND_INTERACTION_OPTION_NO_SUB_COMMAND_GROUP'); + throw new TypeError(ErrorCodes.CommandInteractionOptionNoSubcommandGroup); } return this._group; } @@ -273,7 +273,7 @@ class CommandInteractionOptionResolver { */ getFocused(getFull = false) { const focusedOption = this._hoistedOptions.find(option => option.focused); - if (!focusedOption) throw new TypeError('AUTOCOMPLETE_INTERACTION_OPTION_NO_FOCUSED_OPTION'); + if (!focusedOption) throw new TypeError(ErrorCodes.AutocompleteInteractionOptionNoFocusedOption); return getFull ? focusedOption : focusedOption.value; } } diff --git a/packages/discord.js/src/structures/Guild.js b/packages/discord.js/src/structures/Guild.js index 08fb347d4..edded7807 100644 --- a/packages/discord.js/src/structures/Guild.js +++ b/packages/discord.js/src/structures/Guild.js @@ -11,7 +11,7 @@ const GuildTemplate = require('./GuildTemplate'); const Integration = require('./Integration'); const Webhook = require('./Webhook'); const WelcomeScreen = require('./WelcomeScreen'); -const { Error, TypeError } = require('../errors'); +const { Error, TypeError, ErrorCodes } = require('../errors'); const GuildApplicationCommandManager = require('../managers/GuildApplicationCommandManager'); const GuildBanManager = require('../managers/GuildBanManager'); const GuildChannelManager = require('../managers/GuildChannelManager'); @@ -453,7 +453,7 @@ class Guild extends AnonymousGuild { */ async fetchOwner(options) { if (!this.ownerId) { - throw new Error('FETCH_OWNER_ID'); + throw new Error(ErrorCodes.FetchOwnerId); } const member = await this.members.fetch({ ...options, user: this.ownerId }); return member; @@ -604,7 +604,7 @@ class Guild extends AnonymousGuild { */ async fetchVanityData() { if (!this.features.includes(GuildFeature.VanityURL)) { - throw new Error('VANITY_URL'); + throw new Error(ErrorCodes.VanityURL); } const data = await this.client.rest.get(Routes.guildVanityUrl(this.id)); this.vanityURLCode = data.code; @@ -705,7 +705,7 @@ class Guild extends AnonymousGuild { if (options.user) { const id = this.client.users.resolveId(options.user); - if (!id) throw new TypeError('INVALID_TYPE', 'user', 'UserResolvable'); + if (!id) throw new TypeError(ErrorCodes.InvalidType, 'user', 'UserResolvable'); query.set('user_id', id); } @@ -1164,7 +1164,7 @@ class Guild extends AnonymousGuild { * .catch(console.error); */ async leave() { - if (this.ownerId === this.client.user.id) throw new Error('GUILD_OWNED'); + if (this.ownerId === this.client.user.id) throw new Error(ErrorCodes.GuildOwned); await this.client.rest.delete(Routes.userGuild(this.id)); return this; } diff --git a/packages/discord.js/src/structures/GuildChannel.js b/packages/discord.js/src/structures/GuildChannel.js index a877e435a..3b557e790 100644 --- a/packages/discord.js/src/structures/GuildChannel.js +++ b/packages/discord.js/src/structures/GuildChannel.js @@ -2,7 +2,7 @@ const { PermissionFlagsBits } = require('discord-api-types/v10'); const { Channel } = require('./Channel'); -const { Error } = require('../errors'); +const { Error, ErrorCodes } = require('../errors'); const PermissionOverwriteManager = require('../managers/PermissionOverwriteManager'); const { VoiceBasedChannelTypes } = require('../util/Constants'); const PermissionsBitField = require('../util/PermissionsBitField'); @@ -246,7 +246,7 @@ class GuildChannel extends Channel { * @returns {Promise} */ lockPermissions() { - if (!this.parent) return Promise.reject(new Error('GUILD_CHANNEL_ORPHAN')); + if (!this.parent) return Promise.reject(new Error(ErrorCodes.GuildChannelOrphan)); const permissionOverwrites = this.parent.permissionOverwrites.cache.map(overwrite => overwrite.toJSON()); return this.edit({ permissionOverwrites }); } diff --git a/packages/discord.js/src/structures/GuildEmoji.js b/packages/discord.js/src/structures/GuildEmoji.js index d3c586a3b..baf328ccb 100644 --- a/packages/discord.js/src/structures/GuildEmoji.js +++ b/packages/discord.js/src/structures/GuildEmoji.js @@ -2,7 +2,7 @@ const { PermissionFlagsBits } = require('discord-api-types/v10'); const BaseGuildEmoji = require('./BaseGuildEmoji'); -const { Error } = require('../errors'); +const { Error, ErrorCodes } = require('../errors'); const GuildEmojiRoleManager = require('../managers/GuildEmojiRoleManager'); /** @@ -55,7 +55,7 @@ class GuildEmoji extends BaseGuildEmoji { * @readonly */ get deletable() { - if (!this.guild.members.me) throw new Error('GUILD_UNCACHED_ME'); + if (!this.guild.members.me) throw new Error(ErrorCodes.GuildUncachedMe); return !this.managed && this.guild.members.me.permissions.has(PermissionFlagsBits.ManageEmojisAndStickers); } diff --git a/packages/discord.js/src/structures/GuildMember.js b/packages/discord.js/src/structures/GuildMember.js index 2da967d34..b0ee8458c 100644 --- a/packages/discord.js/src/structures/GuildMember.js +++ b/packages/discord.js/src/structures/GuildMember.js @@ -4,7 +4,7 @@ const { PermissionFlagsBits } = require('discord-api-types/v10'); const Base = require('./Base'); const VoiceState = require('./VoiceState'); const TextBasedChannel = require('./interfaces/TextBasedChannel'); -const { Error } = require('../errors'); +const { Error, ErrorCodes } = require('../errors'); const GuildMemberRoleManager = require('../managers/GuildMemberRoleManager'); const PermissionsBitField = require('../util/PermissionsBitField'); @@ -239,7 +239,7 @@ class GuildMember extends Base { if (this.user.id === this.guild.ownerId) return false; if (this.user.id === this.client.user.id) return false; if (this.client.user.id === this.guild.ownerId) return true; - if (!this.guild.members.me) throw new Error('GUILD_UNCACHED_ME'); + if (!this.guild.members.me) throw new Error(ErrorCodes.GuildUncachedMe); return this.guild.members.me.roles.highest.comparePositionTo(this.roles.highest) > 0; } @@ -249,7 +249,7 @@ class GuildMember extends Base { * @readonly */ get kickable() { - if (!this.guild.members.me) throw new Error('GUILD_UNCACHED_ME'); + if (!this.guild.members.me) throw new Error(ErrorCodes.GuildUncachedMe); return this.manageable && this.guild.members.me.permissions.has(PermissionFlagsBits.KickMembers); } @@ -259,7 +259,7 @@ class GuildMember extends Base { * @readonly */ get bannable() { - if (!this.guild.members.me) throw new Error('GUILD_UNCACHED_ME'); + if (!this.guild.members.me) throw new Error(ErrorCodes.GuildUncachedMe); return this.manageable && this.guild.members.me.permissions.has(PermissionFlagsBits.BanMembers); } @@ -292,7 +292,7 @@ class GuildMember extends Base { */ permissionsIn(channel) { channel = this.guild.channels.resolve(channel); - if (!channel) throw new Error('GUILD_CHANNEL_RESOLVE'); + if (!channel) throw new Error(ErrorCodes.GuildChannelResolve); return channel.permissionsFor(this); } diff --git a/packages/discord.js/src/structures/GuildScheduledEvent.js b/packages/discord.js/src/structures/GuildScheduledEvent.js index ac901a511..390486c87 100644 --- a/packages/discord.js/src/structures/GuildScheduledEvent.js +++ b/packages/discord.js/src/structures/GuildScheduledEvent.js @@ -3,7 +3,7 @@ const { DiscordSnowflake } = require('@sapphire/snowflake'); const { GuildScheduledEventStatus, GuildScheduledEventEntityType, RouteBases } = require('discord-api-types/v10'); const Base = require('./Base'); -const { Error } = require('../errors'); +const { Error, ErrorCodes } = require('../errors'); /** * Represents a scheduled event in a {@link Guild}. @@ -253,9 +253,9 @@ class GuildScheduledEvent extends Base { async createInviteURL(options) { let channelId = this.channelId; if (this.entityType === GuildScheduledEventEntityType.External) { - if (!options?.channel) throw new Error('INVITE_OPTIONS_MISSING_CHANNEL'); + if (!options?.channel) throw new Error(ErrorCodes.InviteOptionsMissingChannel); channelId = this.guild.channels.resolveId(options.channel); - if (!channelId) throw new Error('GUILD_CHANNEL_RESOLVE'); + if (!channelId) throw new Error(ErrorCodes.GuildChannelResolve); } const invite = await this.guild.invites.create(channelId, options); return `${RouteBases.invite}/${invite.code}?event=${this.id}`; diff --git a/packages/discord.js/src/structures/InteractionResponse.js b/packages/discord.js/src/structures/InteractionResponse.js index e1cae7f63..820be6e7d 100644 --- a/packages/discord.js/src/structures/InteractionResponse.js +++ b/packages/discord.js/src/structures/InteractionResponse.js @@ -1,6 +1,7 @@ 'use strict'; const { InteractionType } = require('discord-api-types/v10'); +const { ErrorCodes } = require('../errors'); /** * Represents an interaction's response @@ -38,7 +39,7 @@ class InteractionResponse { collector.once('end', (interactions, reason) => { const interaction = interactions.first(); if (interaction) resolve(interaction); - else reject(new Error('INTERACTION_COLLECTOR_ERROR', reason)); + else reject(new Error(ErrorCodes.InteractionCollectorError, reason)); }); }); } @@ -57,5 +58,6 @@ class InteractionResponse { } } +// eslint-disable-next-line import/order const InteractionCollector = require('./InteractionCollector'); module.exports = InteractionResponse; diff --git a/packages/discord.js/src/structures/Invite.js b/packages/discord.js/src/structures/Invite.js index 03b41500f..d0e9602c7 100644 --- a/packages/discord.js/src/structures/Invite.js +++ b/packages/discord.js/src/structures/Invite.js @@ -5,7 +5,7 @@ const Base = require('./Base'); const { GuildScheduledEvent } = require('./GuildScheduledEvent'); const IntegrationApplication = require('./IntegrationApplication'); const InviteStageInstance = require('./InviteStageInstance'); -const { Error } = require('../errors'); +const { Error, ErrorCodes } = require('../errors'); /** * Represents an invitation to a guild channel. @@ -233,7 +233,7 @@ class Invite extends Base { get deletable() { const guild = this.guild; if (!guild || !this.client.guilds.cache.has(guild.id)) return false; - if (!guild.members.me) throw new Error('GUILD_UNCACHED_ME'); + if (!guild.members.me) throw new Error(ErrorCodes.GuildUncachedMe); return Boolean( this.channel?.permissionsFor(this.client.user).has(PermissionFlagsBits.ManageChannels, false) || guild.members.me.permissions.has(PermissionFlagsBits.ManageGuild), diff --git a/packages/discord.js/src/structures/Message.js b/packages/discord.js/src/structures/Message.js index 4d0f2b26e..c54b5c32a 100644 --- a/packages/discord.js/src/structures/Message.js +++ b/packages/discord.js/src/structures/Message.js @@ -18,7 +18,7 @@ const Mentions = require('./MessageMentions'); const MessagePayload = require('./MessagePayload'); const ReactionCollector = require('./ReactionCollector'); const { Sticker } = require('./Sticker'); -const { Error } = require('../errors'); +const { Error, ErrorCodes } = require('../errors'); const ReactionManager = require('../managers/ReactionManager'); const { createComponent } = require('../util/Components'); const { NonSystemMessageTypes } = require('../util/Constants'); @@ -541,7 +541,7 @@ class Message extends Base { collector.once('end', (interactions, reason) => { const interaction = interactions.first(); if (interaction) resolve(interaction); - else reject(new Error('INTERACTION_COLLECTOR_ERROR', reason)); + else reject(new Error(ErrorCodes.InteractionCollectorError, reason)); }); }); } @@ -607,10 +607,10 @@ class Message extends Base { * @returns {Promise} */ async fetchReference() { - if (!this.reference) throw new Error('MESSAGE_REFERENCE_MISSING'); + if (!this.reference) throw new Error(ErrorCodes.MessageReferenceMissing); const { channelId, messageId } = this.reference; const channel = this.client.channels.resolve(channelId); - if (!channel) throw new Error('GUILD_CHANNEL_RESOLVE'); + if (!channel) throw new Error(ErrorCodes.GuildChannelResolve); const message = await channel.messages.fetch(messageId); return message; } @@ -661,7 +661,7 @@ class Message extends Base { * .catch(console.error); */ edit(options) { - if (!this.channel) return Promise.reject(new Error('CHANNEL_NOT_CACHED')); + if (!this.channel) return Promise.reject(new Error(ErrorCodes.ChannelNotCached)); return this.channel.messages.edit(this, options); } @@ -677,7 +677,7 @@ class Message extends Base { * } */ crosspost() { - if (!this.channel) return Promise.reject(new Error('CHANNEL_NOT_CACHED')); + if (!this.channel) return Promise.reject(new Error(ErrorCodes.ChannelNotCached)); return this.channel.messages.crosspost(this.id); } @@ -692,7 +692,7 @@ class Message extends Base { * .catch(console.error) */ async pin(reason) { - if (!this.channel) throw new Error('CHANNEL_NOT_CACHED'); + if (!this.channel) throw new Error(ErrorCodes.ChannelNotCached); await this.channel.messages.pin(this.id, reason); return this; } @@ -708,7 +708,7 @@ class Message extends Base { * .catch(console.error) */ async unpin(reason) { - if (!this.channel) throw new Error('CHANNEL_NOT_CACHED'); + if (!this.channel) throw new Error(ErrorCodes.ChannelNotCached); await this.channel.messages.unpin(this.id, reason); return this; } @@ -729,7 +729,7 @@ class Message extends Base { * .catch(console.error); */ async react(emoji) { - if (!this.channel) throw new Error('CHANNEL_NOT_CACHED'); + if (!this.channel) throw new Error(ErrorCodes.ChannelNotCached); await this.channel.messages.react(this.id, emoji); return this.client.actions.MessageReactionAdd.handle( @@ -753,7 +753,7 @@ class Message extends Base { * .catch(console.error); */ async delete() { - if (!this.channel) throw new Error('CHANNEL_NOT_CACHED'); + if (!this.channel) throw new Error(ErrorCodes.ChannelNotCached); await this.channel.messages.delete(this.id); return this; } @@ -777,7 +777,7 @@ class Message extends Base { * .catch(console.error); */ reply(options) { - if (!this.channel) return Promise.reject(new Error('CHANNEL_NOT_CACHED')); + if (!this.channel) return Promise.reject(new Error(ErrorCodes.ChannelNotCached)); let data; if (options instanceof MessagePayload) { @@ -820,11 +820,11 @@ class Message extends Base { * @returns {Promise} */ startThread(options = {}) { - if (!this.channel) return Promise.reject(new Error('CHANNEL_NOT_CACHED')); + if (!this.channel) return Promise.reject(new Error(ErrorCodes.ChannelNotCached)); if (![ChannelType.GuildText, ChannelType.GuildNews].includes(this.channel.type)) { - return Promise.reject(new Error('MESSAGE_THREAD_PARENT')); + return Promise.reject(new Error(ErrorCodes.MessageThreadParent)); } - if (this.hasThread) return Promise.reject(new Error('MESSAGE_EXISTING_THREAD')); + if (this.hasThread) return Promise.reject(new Error(ErrorCodes.MessageExistingThread)); return this.channel.threads.create({ ...options, startMessage: this }); } @@ -834,7 +834,7 @@ class Message extends Base { * @returns {Promise} */ fetch(force = true) { - if (!this.channel) return Promise.reject(new Error('CHANNEL_NOT_CACHED')); + if (!this.channel) return Promise.reject(new Error(ErrorCodes.ChannelNotCached)); return this.channel.messages.fetch({ message: this.id, force }); } @@ -843,8 +843,8 @@ class Message extends Base { * @returns {Promise} */ fetchWebhook() { - if (!this.webhookId) return Promise.reject(new Error('WEBHOOK_MESSAGE')); - if (this.webhookId === this.applicationId) return Promise.reject(new Error('WEBHOOK_APPLICATION')); + if (!this.webhookId) return Promise.reject(new Error(ErrorCodes.WebhookMessage)); + if (this.webhookId === this.applicationId) return Promise.reject(new Error(ErrorCodes.WebhookApplication)); return this.client.fetchWebhook(this.webhookId); } diff --git a/packages/discord.js/src/structures/MessagePayload.js b/packages/discord.js/src/structures/MessagePayload.js index 8ab4d4c6e..37edf4818 100644 --- a/packages/discord.js/src/structures/MessagePayload.js +++ b/packages/discord.js/src/structures/MessagePayload.js @@ -4,7 +4,7 @@ const { Buffer } = require('node:buffer'); const { isJSONEncodable } = require('@discordjs/builders'); const { MessageFlags } = require('discord-api-types/v10'); const ActionRowBuilder = require('./ActionRowBuilder'); -const { RangeError } = require('../errors'); +const { RangeError, ErrorCodes } = require('../errors'); const DataResolver = require('../util/DataResolver'); const MessageFlagsBitField = require('../util/MessageFlagsBitField'); const { basename, cloneObject, verifyString } = require('../util/Util'); @@ -105,7 +105,7 @@ class MessagePayload { if (this.options.content === null) { content = ''; } else if (typeof this.options.content !== 'undefined') { - content = verifyString(this.options.content, RangeError, 'MESSAGE_CONTENT_TYPE', true); + content = verifyString(this.options.content, RangeError, ErrorCodes.MessageContentType, true); } return content; @@ -128,7 +128,7 @@ class MessagePayload { nonce = this.options.nonce; // eslint-disable-next-line max-len if (typeof nonce === 'number' ? !Number.isInteger(nonce) : typeof nonce !== 'string') { - throw new RangeError('MESSAGE_NONCE_TYPE'); + throw new RangeError(ErrorCodes.MessageNonceType); } } diff --git a/packages/discord.js/src/structures/ModalSubmitFields.js b/packages/discord.js/src/structures/ModalSubmitFields.js index 8929d54ed..6c289d4d0 100644 --- a/packages/discord.js/src/structures/ModalSubmitFields.js +++ b/packages/discord.js/src/structures/ModalSubmitFields.js @@ -2,7 +2,7 @@ const { Collection } = require('@discordjs/collection'); const { ComponentType } = require('discord-api-types/v10'); -const { TypeError } = require('../errors'); +const { TypeError, ErrorCodes } = require('../errors'); /** * Represents the serialized fields from a modal submit interaction @@ -33,10 +33,10 @@ class ModalSubmitFields { */ getField(customId, type) { const field = this.fields.get(customId); - if (!field) throw new TypeError('MODAL_SUBMIT_INTERACTION_FIELD_NOT_FOUND', customId); + if (!field) throw new TypeError(ErrorCodes.ModalSubmitInteractionFieldNotFound, customId); if (type !== undefined && type !== field.type) { - throw new TypeError('MODAL_SUBMIT_INTERACTION_FIELD_TYPE', customId, field.type, type); + throw new TypeError(ErrorCodes.ModalSubmitInteractionFieldType, customId, field.type, type); } return field; diff --git a/packages/discord.js/src/structures/NewsChannel.js b/packages/discord.js/src/structures/NewsChannel.js index a0cf632c7..959551179 100644 --- a/packages/discord.js/src/structures/NewsChannel.js +++ b/packages/discord.js/src/structures/NewsChannel.js @@ -2,7 +2,7 @@ const { Routes } = require('discord-api-types/v10'); const BaseGuildTextChannel = require('./BaseGuildTextChannel'); -const { Error } = require('../errors'); +const { Error, ErrorCodes } = require('../errors'); /** * Represents a guild news channel on Discord. @@ -23,7 +23,7 @@ class NewsChannel extends BaseGuildTextChannel { */ async addFollower(channel, reason) { const channelId = this.guild.channels.resolveId(channel); - if (!channelId) throw new Error('GUILD_CHANNEL_RESOLVE'); + if (!channelId) throw new Error(ErrorCodes.GuildChannelResolve); await this.client.rest.post(Routes.channelFollowers(this.id), { body: { webhook_channel_id: channelId }, reason }); return this; } diff --git a/packages/discord.js/src/structures/PartialGroupDMChannel.js b/packages/discord.js/src/structures/PartialGroupDMChannel.js index f604e72bb..a946c5a64 100644 --- a/packages/discord.js/src/structures/PartialGroupDMChannel.js +++ b/packages/discord.js/src/structures/PartialGroupDMChannel.js @@ -1,7 +1,7 @@ 'use strict'; const { Channel } = require('./Channel'); -const { Error } = require('../errors'); +const { Error, ErrorCodes } = require('../errors'); /** * Represents a Partial Group DM Channel on Discord. @@ -46,11 +46,11 @@ class PartialGroupDMChannel extends Channel { } delete() { - return Promise.reject(new Error('DELETE_GROUP_DM_CHANNEL')); + return Promise.reject(new Error(ErrorCodes.DeleteGroupDMChannel)); } fetch() { - return Promise.reject(new Error('FETCH_GROUP_DM_CHANNEL')); + return Promise.reject(new Error(ErrorCodes.FetchGroupDMChannel)); } } diff --git a/packages/discord.js/src/structures/PermissionOverwrites.js b/packages/discord.js/src/structures/PermissionOverwrites.js index 49bc066d6..5e5dad2df 100644 --- a/packages/discord.js/src/structures/PermissionOverwrites.js +++ b/packages/discord.js/src/structures/PermissionOverwrites.js @@ -3,7 +3,7 @@ const { OverwriteType } = require('discord-api-types/v10'); const Base = require('./Base'); const { Role } = require('./Role'); -const { TypeError } = require('../errors'); +const { TypeError, ErrorCodes } = require('../errors'); const PermissionsBitField = require('../util/PermissionsBitField'); /** @@ -181,7 +181,7 @@ class PermissionOverwrites extends Base { } const userOrRole = guild.roles.resolve(overwrite.id) ?? guild.client.users.resolve(overwrite.id); - if (!userOrRole) throw new TypeError('INVALID_TYPE', 'parameter', 'User nor a Role'); + if (!userOrRole) throw new TypeError(ErrorCodes.InvalidType, 'parameter', 'User nor a Role'); const type = userOrRole instanceof Role ? OverwriteType.Role : OverwriteType.Member; return { diff --git a/packages/discord.js/src/structures/Role.js b/packages/discord.js/src/structures/Role.js index abba946de..0a8156136 100644 --- a/packages/discord.js/src/structures/Role.js +++ b/packages/discord.js/src/structures/Role.js @@ -3,7 +3,7 @@ const { DiscordSnowflake } = require('@sapphire/snowflake'); const { PermissionFlagsBits } = require('discord-api-types/v10'); const Base = require('./Base'); -const { Error } = require('../errors'); +const { Error, ErrorCodes } = require('../errors'); const PermissionsBitField = require('../util/PermissionsBitField'); /** @@ -228,7 +228,7 @@ class Role extends Base { */ permissionsIn(channel, checkAdmin = true) { channel = this.guild.channels.resolve(channel); - if (!channel) throw new Error('GUILD_CHANNEL_RESOLVE'); + if (!channel) throw new Error(ErrorCodes.GuildChannelResolve); return channel.rolePermissions(this, checkAdmin); } diff --git a/packages/discord.js/src/structures/Sticker.js b/packages/discord.js/src/structures/Sticker.js index ffb36c975..fc078b375 100644 --- a/packages/discord.js/src/structures/Sticker.js +++ b/packages/discord.js/src/structures/Sticker.js @@ -3,6 +3,7 @@ const { DiscordSnowflake } = require('@sapphire/snowflake'); const { Routes, StickerFormatType } = require('discord-api-types/v10'); const Base = require('./Base'); +const { ErrorCodes } = require('../errors'); /** * Represents a Sticker. @@ -190,7 +191,7 @@ class Sticker extends Base { */ async fetchUser() { if (this.partial) await this.fetch(); - if (!this.guildId) throw new Error('NOT_GUILD_STICKER'); + if (!this.guildId) throw new Error(ErrorCodes.NotGuildSticker); return this.guild.stickers.fetchUser(this); } diff --git a/packages/discord.js/src/structures/ThreadChannel.js b/packages/discord.js/src/structures/ThreadChannel.js index 35895f2cc..df0ae0ad0 100644 --- a/packages/discord.js/src/structures/ThreadChannel.js +++ b/packages/discord.js/src/structures/ThreadChannel.js @@ -3,7 +3,7 @@ const { ChannelType, PermissionFlagsBits, Routes } = require('discord-api-types/v10'); const { Channel } = require('./Channel'); const TextBasedChannel = require('./interfaces/TextBasedChannel'); -const { RangeError } = require('../errors'); +const { RangeError, ErrorCodes } = require('../errors'); const MessageManager = require('../managers/MessageManager'); const ThreadMemberManager = require('../managers/ThreadMemberManager'); @@ -369,7 +369,7 @@ class ThreadChannel extends Channel { */ setInvitable(invitable = true, reason) { if (this.type !== ChannelType.GuildPrivateThread) { - return Promise.reject(new RangeError('THREAD_INVITABLE_TYPE', this.type)); + return Promise.reject(new RangeError(ErrorCodes.ThreadInvitableType, this.type)); } return this.edit({ invitable, reason }); } diff --git a/packages/discord.js/src/structures/VoiceState.js b/packages/discord.js/src/structures/VoiceState.js index 2cbff4e4a..5d644a928 100644 --- a/packages/discord.js/src/structures/VoiceState.js +++ b/packages/discord.js/src/structures/VoiceState.js @@ -2,7 +2,7 @@ const { ChannelType, Routes } = require('discord-api-types/v10'); const Base = require('./Base'); -const { Error, TypeError } = require('../errors'); +const { Error, TypeError, ErrorCodes } = require('../errors'); /** * Represents the voice state for a Guild Member. @@ -219,20 +219,20 @@ class VoiceState extends Base { * @returns {Promise} */ async edit(data) { - if (this.channel?.type !== ChannelType.GuildStageVoice) throw new Error('VOICE_NOT_STAGE_CHANNEL'); + if (this.channel?.type !== ChannelType.GuildStageVoice) throw new Error(ErrorCodes.VoiceNotStageChannel); const target = this.client.user.id === this.id ? '@me' : this.id; if (target !== '@me' && typeof data.requestToSpeak !== 'undefined') { - throw new Error('VOICE_STATE_NOT_OWN'); + throw new Error(ErrorCodes.VoiceStateNotOwn); } if (!['boolean', 'undefined'].includes(typeof data.requestToSpeak)) { - throw new TypeError('VOICE_STATE_INVALID_TYPE', 'requestToSpeak'); + throw new TypeError(ErrorCodes.VoiceStateInvalidType, 'requestToSpeak'); } if (!['boolean', 'undefined'].includes(typeof data.suppressed)) { - throw new TypeError('VOICE_STATE_INVALID_TYPE', 'suppressed'); + throw new TypeError(ErrorCodes.VoiceStateInvalidType, 'suppressed'); } await this.client.rest.patch(Routes.guildVoiceState(this.guild.id, target), { diff --git a/packages/discord.js/src/structures/Webhook.js b/packages/discord.js/src/structures/Webhook.js index eb75b8511..00fc8cb74 100644 --- a/packages/discord.js/src/structures/Webhook.js +++ b/packages/discord.js/src/structures/Webhook.js @@ -4,7 +4,7 @@ const { makeURLSearchParams } = require('@discordjs/rest'); const { DiscordSnowflake } = require('@sapphire/snowflake'); const { Routes, WebhookType } = require('discord-api-types/v10'); const MessagePayload = require('./MessagePayload'); -const { Error } = require('../errors'); +const { Error, ErrorCodes } = require('../errors'); const DataResolver = require('../util/DataResolver'); const { lazy } = require('../util/Util'); @@ -194,7 +194,7 @@ class Webhook { * .catch(console.error); */ async send(options) { - if (!this.token) throw new Error('WEBHOOK_TOKEN_UNAVAILABLE'); + if (!this.token) throw new Error(ErrorCodes.WebhookTokenUnavailable); let messagePayload; @@ -235,7 +235,7 @@ class Webhook { * @see {@link https://api.slack.com/messaging/webhooks} */ async sendSlackMessage(body) { - if (!this.token) throw new Error('WEBHOOK_TOKEN_UNAVAILABLE'); + if (!this.token) throw new Error(ErrorCodes.WebhookTokenUnavailable); const data = await this.client.rest.post(Routes.webhookPlatform(this.id, this.token, 'slack'), { query: makeURLSearchParams({ wait: true }), @@ -291,7 +291,7 @@ class Webhook { * @returns {Promise} Returns the message sent by this webhook */ async fetchMessage(message, { threadId } = {}) { - if (!this.token) throw new Error('WEBHOOK_TOKEN_UNAVAILABLE'); + if (!this.token) throw new Error(ErrorCodes.WebhookTokenUnavailable); const data = await this.client.rest.get(Routes.webhookMessage(this.id, this.token, message), { query: threadId ? makeURLSearchParams({ thread_id: threadId }) : undefined, @@ -312,7 +312,7 @@ class Webhook { * @returns {Promise} Returns the message edited by this webhook */ async editMessage(message, options) { - if (!this.token) throw new Error('WEBHOOK_TOKEN_UNAVAILABLE'); + if (!this.token) throw new Error(ErrorCodes.WebhookTokenUnavailable); let messagePayload; @@ -363,7 +363,7 @@ class Webhook { * @returns {Promise} */ async deleteMessage(message, threadId) { - if (!this.token) throw new Error('WEBHOOK_TOKEN_UNAVAILABLE'); + if (!this.token) throw new Error(ErrorCodes.WebhookTokenUnavailable); await this.client.rest.delete( Routes.webhookMessage(this.id, this.token, typeof message === 'string' ? message : message.id), diff --git a/packages/discord.js/src/structures/interfaces/Collector.js b/packages/discord.js/src/structures/interfaces/Collector.js index f0d488fa4..8b1054c14 100644 --- a/packages/discord.js/src/structures/interfaces/Collector.js +++ b/packages/discord.js/src/structures/interfaces/Collector.js @@ -3,7 +3,7 @@ const EventEmitter = require('node:events'); const { setTimeout, clearTimeout } = require('node:timers'); const { Collection } = require('@discordjs/collection'); -const { TypeError } = require('../../errors'); +const { TypeError, ErrorCodes } = require('../../errors'); const { flatten } = require('../../util/Util'); /** @@ -86,7 +86,7 @@ class Collector extends EventEmitter { this._endReason = null; if (typeof this.filter !== 'function') { - throw new TypeError('INVALID_TYPE', 'options.filter', 'function'); + throw new TypeError(ErrorCodes.InvalidType, 'options.filter', 'function'); } this.handleCollect = this.handleCollect.bind(this); diff --git a/packages/discord.js/src/structures/interfaces/InteractionResponses.js b/packages/discord.js/src/structures/interfaces/InteractionResponses.js index e9e1a7907..829c00533 100644 --- a/packages/discord.js/src/structures/interfaces/InteractionResponses.js +++ b/packages/discord.js/src/structures/interfaces/InteractionResponses.js @@ -2,7 +2,7 @@ const { isJSONEncodable } = require('@discordjs/builders'); const { InteractionResponseType, MessageFlags, Routes, InteractionType } = require('discord-api-types/v10'); -const { Error } = require('../../errors'); +const { Error, ErrorCodes } = require('../../errors'); const InteractionCollector = require('../InteractionCollector'); const InteractionResponse = require('../InteractionResponse'); const MessagePayload = require('../MessagePayload'); @@ -63,7 +63,7 @@ class InteractionResponses { * .catch(console.error); */ async deferReply(options = {}) { - if (this.deferred || this.replied) throw new Error('INTERACTION_ALREADY_REPLIED'); + if (this.deferred || this.replied) throw new Error(ErrorCodes.InteractionAlreadyReplied); this.ephemeral = options.ephemeral ?? false; await this.client.rest.post(Routes.interactionCallback(this.id, this.token), { body: { @@ -98,7 +98,7 @@ class InteractionResponses { * .catch(console.error); */ async reply(options) { - if (this.deferred || this.replied) throw new Error('INTERACTION_ALREADY_REPLIED'); + if (this.deferred || this.replied) throw new Error(ErrorCodes.InteractionAlreadyReplied); this.ephemeral = options.ephemeral ?? false; let messagePayload; @@ -146,7 +146,7 @@ class InteractionResponses { * .catch(console.error); */ async editReply(options) { - if (!this.deferred && !this.replied) throw new Error('INTERACTION_NOT_REPLIED'); + if (!this.deferred && !this.replied) throw new Error(ErrorCodes.InteractionNotReplied); const message = await this.webhook.editMessage('@original', options); this.replied = true; return message; @@ -163,7 +163,7 @@ class InteractionResponses { * .catch(console.error); */ async deleteReply() { - if (this.ephemeral) throw new Error('INTERACTION_EPHEMERAL_REPLIED'); + if (this.ephemeral) throw new Error(ErrorCodes.InteractionEphemeralReplied); await this.webhook.deleteMessage('@original'); } @@ -173,7 +173,7 @@ class InteractionResponses { * @returns {Promise} */ followUp(options) { - if (!this.deferred && !this.replied) return Promise.reject(new Error('INTERACTION_NOT_REPLIED')); + if (!this.deferred && !this.replied) return Promise.reject(new Error(ErrorCodes.InteractionNotReplied)); return this.webhook.send(options); } @@ -188,7 +188,7 @@ class InteractionResponses { * .catch(console.error); */ async deferUpdate(options = {}) { - if (this.deferred || this.replied) throw new Error('INTERACTION_ALREADY_REPLIED'); + if (this.deferred || this.replied) throw new Error(ErrorCodes.InteractionAlreadyReplied); await this.client.rest.post(Routes.interactionCallback(this.id, this.token), { body: { type: InteractionResponseType.DeferredMessageUpdate, @@ -214,7 +214,7 @@ class InteractionResponses { * .catch(console.error); */ async update(options) { - if (this.deferred || this.replied) throw new Error('INTERACTION_ALREADY_REPLIED'); + if (this.deferred || this.replied) throw new Error(ErrorCodes.InteractionAlreadyReplied); let messagePayload; if (options instanceof MessagePayload) messagePayload = options; @@ -240,7 +240,7 @@ class InteractionResponses { * @param {APIModal|ModalData|Modal} modal The modal to show */ async showModal(modal) { - if (this.deferred || this.replied) throw new Error('INTERACTION_ALREADY_REPLIED'); + if (this.deferred || this.replied) throw new Error(ErrorCodes.InteractionAlreadyReplied); await this.client.rest.post(Routes.interactionCallback(this.id, this.token), { body: { type: InteractionResponseType.Modal, @@ -270,14 +270,14 @@ class InteractionResponses { * .catch(console.error); */ awaitModalSubmit(options) { - if (typeof options.time !== 'number') throw new Error('INVALID_TYPE', 'time', 'number'); + if (typeof options.time !== 'number') throw new Error(ErrorCodes.InvalidType, 'time', 'number'); const _options = { ...options, max: 1, interactionType: InteractionType.ModalSubmit }; return new Promise((resolve, reject) => { const collector = new InteractionCollector(this.client, _options); collector.once('end', (interactions, reason) => { const interaction = interactions.first(); if (interaction) resolve(interaction); - else reject(new Error('INTERACTION_COLLECTOR_ERROR', reason)); + else reject(new Error(ErrorCodes.InteractionCollectorError, reason)); }); }); } diff --git a/packages/discord.js/src/structures/interfaces/TextBasedChannel.js b/packages/discord.js/src/structures/interfaces/TextBasedChannel.js index a5419f1d9..500c9cc6f 100644 --- a/packages/discord.js/src/structures/interfaces/TextBasedChannel.js +++ b/packages/discord.js/src/structures/interfaces/TextBasedChannel.js @@ -3,7 +3,7 @@ const { Collection } = require('@discordjs/collection'); const { DiscordSnowflake } = require('@sapphire/snowflake'); const { InteractionType, Routes } = require('discord-api-types/v10'); -const { TypeError, Error } = require('../../errors'); +const { TypeError, Error, ErrorCodes } = require('../../errors'); const InteractionCollector = require('../InteractionCollector'); const MessageCollector = require('../MessageCollector'); const MessagePayload = require('../MessagePayload'); @@ -273,7 +273,7 @@ class TextBasedChannel { collector.once('end', (interactions, reason) => { const interaction = interactions.first(); if (interaction) resolve(interaction); - else reject(new Error('INTERACTION_COLLECTOR_ERROR', reason)); + else reject(new Error(ErrorCodes.InteractionCollectorError, reason)); }); }); } @@ -326,7 +326,7 @@ class TextBasedChannel { const msgs = await this.messages.fetch({ limit: messages }); return this.bulkDelete(msgs, filterOld); } - throw new TypeError('MESSAGE_BULK_DELETE_TYPE'); + throw new TypeError(ErrorCodes.MessageBulkDeleteType); } /** diff --git a/packages/discord.js/src/util/BitField.js b/packages/discord.js/src/util/BitField.js index 02536fbe3..4b5d97bdb 100644 --- a/packages/discord.js/src/util/BitField.js +++ b/packages/discord.js/src/util/BitField.js @@ -1,6 +1,6 @@ 'use strict'; -const { RangeError } = require('../errors'); +const { RangeError, ErrorCodes } = require('../errors'); /** * Data structure that makes it easy to interact with a bitfield. @@ -165,7 +165,7 @@ class BitField { if (typeof this.Flags[bit] !== 'undefined') return this.Flags[bit]; if (!isNaN(bit)) return typeof DefaultBit === 'bigint' ? BigInt(bit) : Number(bit); } - throw new RangeError('BITFIELD_INVALID', bit); + throw new RangeError(ErrorCodes.BitFieldInvalid, bit); } } diff --git a/packages/discord.js/src/util/DataResolver.js b/packages/discord.js/src/util/DataResolver.js index 6e4ed0cff..80bff6721 100644 --- a/packages/discord.js/src/util/DataResolver.js +++ b/packages/discord.js/src/util/DataResolver.js @@ -4,7 +4,7 @@ const { Buffer } = require('node:buffer'); const fs = require('node:fs/promises'); const path = require('node:path'); const { fetch } = require('undici'); -const { Error: DiscordError, TypeError } = require('../errors'); +const { Error: DiscordError, TypeError, ErrorCodes } = require('../errors'); const Invite = require('../structures/Invite'); /** @@ -124,11 +124,11 @@ class DataResolver extends null { const file = path.resolve(resource); const stats = await fs.stat(file); - if (!stats.isFile()) throw new DiscordError('FILE_NOT_FOUND', file); + if (!stats.isFile()) throw new DiscordError(ErrorCodes.FileNotFound, file); return fs.readFile(file); } - throw new TypeError('REQ_RESOURCE_TYPE'); + throw new TypeError(ErrorCodes.ReqResourceType); } } diff --git a/packages/discord.js/src/util/LimitedCollection.js b/packages/discord.js/src/util/LimitedCollection.js index 1fa6798af..344bf1ee1 100644 --- a/packages/discord.js/src/util/LimitedCollection.js +++ b/packages/discord.js/src/util/LimitedCollection.js @@ -1,7 +1,7 @@ 'use strict'; const { Collection } = require('@discordjs/collection'); -const { TypeError } = require('../errors/DJSError.js'); +const { TypeError, ErrorCodes } = require('../errors'); /** * Options for defining the behavior of a LimitedCollection @@ -20,15 +20,15 @@ const { TypeError } = require('../errors/DJSError.js'); class LimitedCollection extends Collection { constructor(options = {}, iterable) { if (typeof options !== 'object' || options === null) { - throw new TypeError('INVALID_TYPE', 'options', 'object', true); + throw new TypeError(ErrorCodes.InvalidType, 'options', 'object', true); } const { maxSize = Infinity, keepOverLimit = null } = options; if (typeof maxSize !== 'number') { - throw new TypeError('INVALID_TYPE', 'maxSize', 'number'); + throw new TypeError(ErrorCodes.InvalidType, 'maxSize', 'number'); } if (keepOverLimit !== null && typeof keepOverLimit !== 'function') { - throw new TypeError('INVALID_TYPE', 'keepOverLimit', 'function'); + throw new TypeError(ErrorCodes.InvalidType, 'keepOverLimit', 'function'); } super(iterable); diff --git a/packages/discord.js/src/util/Sweepers.js b/packages/discord.js/src/util/Sweepers.js index bcd7df05d..dd9c16264 100644 --- a/packages/discord.js/src/util/Sweepers.js +++ b/packages/discord.js/src/util/Sweepers.js @@ -3,7 +3,7 @@ const { setInterval, clearInterval } = require('node:timers'); const { ThreadChannelTypes, SweeperKeys } = require('./Constants'); const Events = require('./Events'); -const { TypeError } = require('../errors/DJSError.js'); +const { TypeError, ErrorCodes } = require('../errors'); /** * @typedef {Function} GlobalSweepFilter @@ -131,7 +131,7 @@ class Sweepers { */ sweepMessages(filter) { if (typeof filter !== 'function') { - throw new TypeError('INVALID_TYPE', 'filter', 'function'); + throw new TypeError(ErrorCodes.InvalidType, 'filter', 'function'); } let channels = 0; let messages = 0; @@ -162,7 +162,7 @@ class Sweepers { */ sweepReactions(filter) { if (typeof filter !== 'function') { - throw new TypeError('INVALID_TYPE', 'filter', 'function'); + throw new TypeError(ErrorCodes.InvalidType, 'filter', 'function'); } let channels = 0; let messages = 0; @@ -210,7 +210,7 @@ class Sweepers { */ sweepThreadMembers(filter) { if (typeof filter !== 'function') { - throw new TypeError('INVALID_TYPE', 'filter', 'function'); + throw new TypeError(ErrorCodes.InvalidType, 'filter', 'function'); } let threads = 0; @@ -240,7 +240,7 @@ class Sweepers { */ sweepThreads(filter) { if (typeof filter !== 'function') { - throw new TypeError('INVALID_TYPE', 'filter', 'function'); + throw new TypeError(ErrorCodes.InvalidType, 'filter', 'function'); } let threads = 0; @@ -262,7 +262,7 @@ class Sweepers { */ sweepUsers(filter) { if (typeof filter !== 'function') { - throw new TypeError('INVALID_TYPE', 'filter', 'function'); + throw new TypeError(ErrorCodes.InvalidType, 'filter', 'function'); } const users = this.client.users.cache.sweep(filter); @@ -313,13 +313,13 @@ class Sweepers { excludeFromSweep = () => false, } = {}) { if (typeof lifetime !== 'number') { - throw new TypeError('INVALID_TYPE', 'lifetime', 'number'); + throw new TypeError(ErrorCodes.InvalidType, 'lifetime', 'number'); } if (typeof getComparisonTimestamp !== 'function') { - throw new TypeError('INVALID_TYPE', 'getComparisonTimestamp', 'function'); + throw new TypeError(ErrorCodes.InvalidType, 'getComparisonTimestamp', 'function'); } if (typeof excludeFromSweep !== 'function') { - throw new TypeError('INVALID_TYPE', 'excludeFromSweep', 'function'); + throw new TypeError(ErrorCodes.InvalidType, 'excludeFromSweep', 'function'); } return () => { if (lifetime <= 0) return null; @@ -391,7 +391,7 @@ class Sweepers { */ _sweepGuildDirectProp(key, filter, { emit = true, outputName } = {}) { if (typeof filter !== 'function') { - throw new TypeError('INVALID_TYPE', 'filter', 'function'); + throw new TypeError(ErrorCodes.InvalidType, 'filter', 'function'); } let guilds = 0; @@ -419,20 +419,20 @@ class Sweepers { _validateProperties(key) { const props = this.options[key]; if (typeof props !== 'object') { - throw new TypeError('INVALID_TYPE', `sweepers.${key}`, 'object', true); + throw new TypeError(ErrorCodes.InvalidType, `sweepers.${key}`, 'object', true); } if (typeof props.interval !== 'number') { - throw new TypeError('INVALID_TYPE', `sweepers.${key}.interval`, 'number'); + throw new TypeError(ErrorCodes.InvalidType, `sweepers.${key}.interval`, 'number'); } // Invites, Messages, and Threads can be provided a lifetime parameter, which we use to generate the filter if (['invites', 'messages', 'threads'].includes(key) && !('filter' in props)) { if (typeof props.lifetime !== 'number') { - throw new TypeError('INVALID_TYPE', `sweepers.${key}.lifetime`, 'number'); + throw new TypeError(ErrorCodes.InvalidType, `sweepers.${key}.lifetime`, 'number'); } return; } if (typeof props.filter !== 'function') { - throw new TypeError('INVALID_TYPE', `sweepers.${key}.filter`, 'function'); + throw new TypeError(ErrorCodes.InvalidType, `sweepers.${key}.filter`, 'function'); } } @@ -448,7 +448,7 @@ class Sweepers { this.intervals[intervalKey] = setInterval(() => { const sweepFn = opts.filter(); if (sweepFn === null) return; - if (typeof sweepFn !== 'function') throw new TypeError('SWEEP_FILTER_RETURN'); + if (typeof sweepFn !== 'function') throw new TypeError(ErrorCodes.SweepFilterReturn); this[sweepKey](sweepFn); }, opts.interval * 1_000).unref(); } diff --git a/packages/discord.js/src/util/Util.js b/packages/discord.js/src/util/Util.js index 75b13d0ee..8d4dd7c55 100644 --- a/packages/discord.js/src/util/Util.js +++ b/packages/discord.js/src/util/Util.js @@ -5,7 +5,7 @@ const { Collection } = require('@discordjs/collection'); const { ChannelType, RouteBases, Routes } = require('discord-api-types/v10'); const { fetch } = require('undici'); const Colors = require('./Colors'); -const { Error: DiscordError, RangeError, TypeError } = require('../errors'); +const { Error: DiscordError, RangeError, TypeError, ErrorCodes } = require('../errors'); const isObject = d => typeof d === 'object' && d !== null; /** @@ -223,13 +223,13 @@ function escapeSpoiler(text) { * @returns {Promise} The recommended number of shards */ async function fetchRecommendedShards(token, { guildsPerShard = 1_000, multipleOf = 1 } = {}) { - if (!token) throw new DiscordError('TOKEN_MISSING'); + if (!token) throw new DiscordError(ErrorCodes.TokenMissing); const response = await fetch(RouteBases.api + Routes.gatewayBot(), { method: 'GET', headers: { Authorization: `Bot ${token.replace(/^Bot\s*/i, '')}` }, }); if (!response.ok) { - if (response.status === 401) throw new DiscordError('TOKEN_INVALID'); + if (response.status === 401) throw new DiscordError(ErrorCodes.TokenInvalid); throw response; } const { shards } = await response.json(); @@ -423,8 +423,8 @@ function resolveColor(color) { color = (color[0] << 16) + (color[1] << 8) + color[2]; } - if (color < 0 || color > 0xffffff) throw new RangeError('COLOR_RANGE'); - else if (Number.isNaN(color)) throw new TypeError('COLOR_CONVERT'); + if (color < 0 || color > 0xffffff) throw new RangeError(ErrorCodes.ColorRange); + else if (Number.isNaN(color)) throw new TypeError(ErrorCodes.ColorConvert); return color; } diff --git a/packages/discord.js/typings/index.d.ts b/packages/discord.js/typings/index.d.ts index b83df26b0..7789bd559 100644 --- a/packages/discord.js/typings/index.d.ts +++ b/packages/discord.js/typings/index.d.ts @@ -2995,6 +2995,169 @@ export const version: string; //#endregion +//#region Errors +export enum DiscordjsErrorCodes { + ClientInvalidOption, + ClientInvalidProvidedShards, + ClientMissingIntents, + ClientNotReady, + + TokenInvalid, + TokenMissing, + ApplicationCommandPermissionsTokenMissing, + + WSCloseRequested, + WSConnectionExists, + WSNotOpen, + ManagerDestroyed, + + BitFieldInvalid, + + ShardingInvalid, + ShardingRequired, + InvalidIntents, + DisallowedIntents, + ShardingNoShards, + ShardingInProcess, + ShardingInvalidEvalBroadcast, + ShardingShardNotFound, + ShardingAlreadySpawned, + ShardingProcessExists, + ShardingWorkerExists, + ShardingReadyTimeout, + ShardingReadyDisconnected, + ShardingReadyDied, + ShardingNoChildExists, + ShardingShardMiscalculation, + + ColorRange, + ColorConvert, + + InviteOptionsMissingChannel, + + ButtonLabel, + ButtonURL, + ButtonCustomId, + + SelectMenuCustomId, + SelectMenuPlaceholder, + SelectOptionLabel, + SelectOptionValue, + SelectOptionDescription, + + InteractionCollectorError, + + FileNotFound, + + UserBannerNotFetched, + UserNoDMChannel, + + VoiceNotStageChannel, + + VoiceStateNotOwn, + VoiceStateInvalidType, + + ReqResourceType, + + ImageFormat, + ImageSize, + + MessageBulkDeleteType, + MessageNonceType, + MessageContentType, + + SplitMaxLen, + + BanResolveId, + FetchBanResolveId, + + PruneDaysType, + + GuildChannelResolve, + GuildVoiceChannelResolve, + GuildChannelOrphan, + GuildChannelUnowned, + GuildOwned, + GuildMembersTimeout, + GuildUncachedMe, + ChannelNotCached, + StageChannelResolve, + GuildScheduledEventResolve, + FetchOwnerId, + + InvalidType, + InvalidElement, + + MessageThreadParent, + MessageExistingThread, + ThreadInvitableType, + + WebhookMessage, + WebhookTokenUnavailable, + WebhookURLInvalid, + WebhookApplication, + MessageReferenceMissing, + + EmojiType, + EmojiManaged, + MissingManageEmojisAndStickersPermission, + NotGuildSticker, + + ReactionResolveUser, + + VanityURL, + + InviteResolveCode, + + InviteNotFound, + + DeleteGroupDMChannel, + FetchGroupDMChannel, + + MemberFetchNonceLength, + + GlobalCommandPermissions, + GuildUncachedEntityResolve, + + InteractionAlreadyReplied, + InteractionNotReplied, + InteractionEphemeralReplied, + + CommandInteractionOptionNotFound, + CommandInteractionOptionType, + CommandInteractionOptionEmpty, + CommandInteractionOptionNoSubcommand, + CommandInteractionOptionNoSubcommandGroup, + AutocompleteInteractionOptionNoFocusedOption, + + ModalSubmitInteractionFieldNotFound, + ModalSubmitInteractionFieldType, + + InvalidMissingScopes, + + NotImplemented, + + SweepFilterReturn, +} + +export interface DiscordjsErrorFields { + readonly name: `${Name} [${keyof typeof DiscordjsErrorCodes}]`; + get code(): keyof typeof DiscordjsErrorCodes; +} + +export function DiscordjsErrorMixin( + Base: Constructable, + name: N, +): Constructable>; + +export class DiscordjsError extends DiscordjsErrorMixin(Error, 'Error') {} + +export class DiscordjsTypeError extends DiscordjsErrorMixin(TypeError, 'TypeError') {} + +export class DiscordjsRangeError extends DiscordjsErrorMixin(RangeError, 'RangeError') {} + +//#endregion + //#region Managers export abstract class BaseManager {