refactor: errors (#8068)

Co-authored-by: Parbez <imranbarbhuiya.fsd@gmail.com>
Co-authored-by: A. Román <kyradiscord@gmail.com>
Co-authored-by: SpaceEEC <spaceeec@yahoo.com>
This commit is contained in:
DD
2022-06-20 15:46:35 +03:00
committed by GitHub
parent 358c3f4a46
commit e68effa822
62 changed files with 923 additions and 426 deletions

View File

@@ -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);
}
/**

View File

@@ -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');
}
}
}

View File

@@ -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;
}

View File

@@ -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 });

View File

@@ -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),

View File

@@ -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',
]);

View File

@@ -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;

View File

@@ -1,4 +1,5 @@
'use strict';
module.exports = require('./DJSError');
module.exports.ErrorCodes = require('./ErrorCodes');
module.exports.Messages = require('./Messages');

View File

@@ -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');

View File

@@ -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 }));

View File

@@ -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 };

View File

@@ -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);
}
/**

View File

@@ -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);
}

View File

@@ -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 });
}

View File

@@ -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));

View File

@@ -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);
}

View File

@@ -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: {

View File

@@ -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);

View File

@@ -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 });

View File

@@ -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<GuildScheduledEvent>}
*/
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,

View File

@@ -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;

View File

@@ -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));
}

View File

@@ -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;

View File

@@ -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

View File

@@ -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));

View File

@@ -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));
}

View File

@@ -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');
}
}
}

View File

@@ -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;
}

View File

@@ -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;

View File

@@ -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<ChildProcess>}
*/
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);

View File

@@ -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;
}

View File

@@ -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));

View File

@@ -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: {

View File

@@ -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({

View File

@@ -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;
}
}

View File

@@ -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;
}

View File

@@ -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<GuildChannel>}
*/
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 });
}

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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}`;

View File

@@ -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;

View File

@@ -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),

View File

@@ -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<Message>}
*/
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<ThreadChannel>}
*/
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<Message>}
*/
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<?Webhook>}
*/
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);
}

View File

@@ -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);
}
}

View File

@@ -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;

View File

@@ -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;
}

View File

@@ -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));
}
}

View File

@@ -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 {

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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 });
}

View File

@@ -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<VoiceState>}
*/
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), {

View File

@@ -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<APIMessage>} 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<APIMessage>} 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<void>}
*/
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),

View File

@@ -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);

View File

@@ -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<Message|APIMessage>}
*/
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));
});
});
}

View File

@@ -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);
}
/**

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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);

View File

@@ -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();
}

View File

@@ -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<number>} 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;
}

View File

@@ -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<Name extends string> {
readonly name: `${Name} [${keyof typeof DiscordjsErrorCodes}]`;
get code(): keyof typeof DiscordjsErrorCodes;
}
export function DiscordjsErrorMixin<T, N extends string>(
Base: Constructable<T>,
name: N,
): Constructable<T & DiscordjsErrorFields<N>>;
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 {