refactor: using eslint-config-neon on mainlib (#10876)

* refactor: using eslint-config-neon on mainlib

* fix: lint

* fix: lint

* fix: the way we lint

* chore: lint some more

* fix: more lint changes

* fix: type tests

* chore: port eslint rule

* refactor: lintstaged doesn't need this

* fix: eslint was a bit too eager

* fix: forgot Client

* Apply suggestions from code review

Co-authored-by: Almeida <github@almeidx.dev>

* chore: more lint fixes

* fix: remove useless Boolean()

* fix: get docs back

* fix: snowflake docs

* refactor: don't use typescript lint rules

* fix: code review

* fix: tidy up disabled rules

* chore: code review

* chore: code review

* chore: code review

* fix: consistent spacing in typings

* fix: tests

* fix: unsort ErrorCodes

* chore: get comments back

* Update packages/discord.js/src/client/websocket/handlers/THREAD_LIST_SYNC.js

Co-authored-by: Jiralite <33201955+Jiralite@users.noreply.github.com>

* fix: remove unused parameter

* fix: merge messed up types

* fix: more type mess from merge

* fix: generate script for ActionsManager

* fix: code review

* Update packages/discord.js/src/structures/MessageMentions.js

Co-authored-by: Jiralite <33201955+Jiralite@users.noreply.github.com>

* Update packages/discord.js/src/structures/Presence.js

Co-authored-by: Jiralite <33201955+Jiralite@users.noreply.github.com>

* fix: replace is faster, unicorn is wrong

* fix: consistency

* fix: delete obsolete file

* fix: minor nit in test file

---------

Co-authored-by: Almeida <github@almeidx.dev>
Co-authored-by: Jiralite <33201955+Jiralite@users.noreply.github.com>
This commit is contained in:
Qjuh
2025-06-02 19:23:40 +02:00
committed by GitHub
parent ef2c1bfa77
commit b03c65c34c
293 changed files with 5121 additions and 3366 deletions

View File

@@ -21,6 +21,7 @@ const nodeRuleset = merge(...node, { files: [`**/*${commonFiles}`] });
const typeScriptRuleset = merge(...typescript, {
files: [`**/*${commonFiles}`],
ignores: [`packages/discord.js/**/*.{js,mjs,cjs}`],
languageOptions: {
parserOptions: {
warnOnUnsupportedTypeScriptVersion: false,
@@ -88,7 +89,6 @@ export default tseslint.config(
'**/storybook-static/',
'**/.next/',
'**/shiki.bundle.ts',
'packages/discord.js/',
],
},
commonRuleset,
@@ -137,6 +137,101 @@ export default tseslint.config(
'@typescript-eslint/no-empty-object-type': 0,
},
},
{
files: [`packages/discord.js/**/*.{js,cjs}`],
languageOptions: {
sourceType: 'commonjs',
parserOptions: {
ecmaFeatures: {
impliedStrict: false,
},
},
},
settings: {
jsdoc: {
tagNamePreference: {
augments: 'extends',
fires: 'emits',
function: 'method',
},
preferredTypes: {
object: 'Object',
null: 'void',
},
},
},
rules: {
'jsdoc/no-undefined-types': 0,
'jsdoc/no-defaults': 0,
'no-eq-null': 0,
strict: ['error', 'global'],
'no-restricted-syntax': [
'error',
{
selector: "AssignmentExpression[left.object.name='module'][left.property.name='exports']",
message: 'Use named exports instead of module.exports',
},
{
selector:
"VariableDeclarator[init.callee.name='require'][init.arguments.0.value=/^\\./]:not([id.type='ObjectPattern'])",
message: 'Use object destructuring when requiring local modules',
},
],
},
},
{
files: [`packages/discord.js/src/client/websocket/handlers/*.js`],
rules: {
'no-restricted-syntax': [
'error',
{
selector:
"VariableDeclarator[init.callee.name='require'][init.arguments.0.value=/^\\./]:not([id.type='ObjectPattern'])",
message: 'Use object destructuring when requiring local modules',
},
],
},
},
{
files: [`packages/discord.js/typings/*{d.ts,test-d.ts,d.mts,test-d.mts}`],
rules: {
'@typescript-eslint/no-unsafe-declaration-merging': 0,
'@typescript-eslint/no-empty-object-type': 0,
'@typescript-eslint/no-use-before-define': 0,
'@typescript-eslint/consistent-type-imports': 0,
'@stylistic/ts/lines-between-class-members': 0,
'no-restricted-syntax': [
2,
{
selector:
'MethodDefinition[key.name!=on][key.name!=once][key.name!=off] > TSEmptyBodyFunctionExpression > Identifier :not(TSTypeOperator[operator=readonly]) > TSArrayType',
message: 'Array parameters on methods must be readonly',
},
{
selector:
'MethodDefinition > TSEmptyBodyFunctionExpression > Identifier TSTypeReference > Identifier[name=Collection]',
message: 'Parameters of type Collection on methods must use ReadonlyCollection',
},
{
selector: 'TSDeclareFunction > Identifier :not(TSTypeOperator[operator=readonly]) > TSArrayType',
message: 'Array parameters on functions must be readonly',
},
{
selector: 'TSDeclareFunction Identifier TSTypeReference > Identifier[name=Collection]',
message: 'Parameters of type Collection on functions must use ReadonlyCollection',
},
{
selector: 'TSInterfaceDeclaration TSPropertySignature :not(TSTypeOperator[operator=readonly]) > TSArrayType',
message: 'Array properties on interfaces must be readonly',
},
{
selector: 'TSInterfaceDeclaration TSPropertySignature TSTypeReference > Identifier[name=Collection]',
message: 'Interface properties of type Collection must use ReadonlyCollection',
},
],
},
},
{
files: [`packages/rest/**/*${commonFiles}`],
rules: {

View File

@@ -1,283 +0,0 @@
{
"$schema": "https://json.schemastore.org/eslintrc.json",
"root": true,
"overrides": [
{
"files": ["src/**/*.js", "test/**/*.js"],
"extends": ["eslint:recommended"],
"plugins": ["import", "jsdoc"],
"parserOptions": {
"ecmaVersion": 2022
},
"env": {
"es2022": true,
"node": true
},
"settings": {
"jsdoc": {
"tagNamePreference": {
"augments": "extends",
"return": "returns",
"arg": "param",
"fires": "emits",
"function": "method"
},
"preferredTypes": {
"String": "string",
"Number": "number",
"Boolean": "boolean",
"Symbol": "symbol",
"object": "Object",
"function": "Function",
"array": "Array",
"date": "Date",
"error": "Error",
"null": "void"
}
}
},
"rules": {
"import/order": [
"error",
{
"groups": ["builtin", "external", "internal", "index", "sibling", "parent"],
"alphabetize": {
"order": "asc"
}
}
],
"import/extensions": ["error", "ignorePackages"],
"strict": ["error", "global"],
"no-await-in-loop": "warn",
"no-compare-neg-zero": "error",
"no-template-curly-in-string": "error",
"no-unsafe-negation": "error",
"jsdoc/require-returns": "off",
"jsdoc/require-returns-description": "off",
"jsdoc/check-tag-names": "error",
"jsdoc/check-types": "error",
"accessor-pairs": "warn",
"array-callback-return": "error",
"consistent-return": "error",
"curly": ["error", "multi-line", "consistent"],
"dot-location": ["error", "property"],
"dot-notation": "error",
"eqeqeq": "error",
"no-empty-function": "error",
"no-floating-decimal": "error",
"no-implied-eval": "error",
"no-invalid-this": "error",
"no-lone-blocks": "error",
"no-multi-spaces": "error",
"no-new-func": "error",
"no-new-wrappers": "error",
"no-new": "error",
"no-octal-escape": "error",
"no-return-assign": "error",
"no-return-await": "error",
"no-self-compare": "error",
"no-sequences": "error",
"no-throw-literal": "error",
"no-unmodified-loop-condition": "error",
"no-unused-expressions": "error",
"no-useless-call": "error",
"no-useless-concat": "error",
"no-useless-escape": "error",
"no-useless-return": "error",
"no-void": "error",
"no-warning-comments": "warn",
"prefer-promise-reject-errors": "error",
"require-await": "off",
"wrap-iife": "error",
"yoda": "error",
"no-label-var": "error",
"no-shadow": "error",
"no-undef-init": "error",
"callback-return": "error",
"getter-return": "off",
"handle-callback-err": "error",
"no-mixed-requires": "error",
"no-new-require": "error",
"no-path-concat": "error",
"array-bracket-spacing": "error",
"block-spacing": "error",
"brace-style": ["error", "1tbs", { "allowSingleLine": true }],
"capitalized-comments": ["error", "always", { "ignoreConsecutiveComments": true }],
"comma-dangle": ["error", "always-multiline"],
"comma-spacing": "error",
"comma-style": "error",
"computed-property-spacing": "error",
"consistent-this": ["error", "$this"],
"eol-last": "error",
"func-names": "error",
"func-name-matching": "error",
"func-style": ["error", "declaration", { "allowArrowFunctions": true }],
"key-spacing": "error",
"keyword-spacing": "error",
"max-depth": "error",
"max-len": ["error", 120, 2],
"max-nested-callbacks": ["error", { "max": 4 }],
"max-statements-per-line": ["error", { "max": 2 }],
"new-cap": "off",
"newline-per-chained-call": ["error", { "ignoreChainWithDepth": 3 }],
"no-array-constructor": "error",
"no-inline-comments": "error",
"no-lonely-if": "error",
"no-multiple-empty-lines": ["error", { "max": 2, "maxEOF": 1, "maxBOF": 0 }],
"no-new-object": "error",
"no-spaced-func": "error",
"no-trailing-spaces": "error",
"no-unneeded-ternary": "error",
"no-whitespace-before-property": "error",
"nonblock-statement-body-position": "error",
"object-curly-spacing": ["error", "always"],
"operator-assignment": "error",
"padded-blocks": ["error", "never"],
"quote-props": ["error", "as-needed"],
"quotes": ["error", "single", { "avoidEscape": true, "allowTemplateLiterals": true }],
"semi-spacing": "error",
"semi": "error",
"space-before-blocks": "error",
"space-before-function-paren": [
"error",
{
"anonymous": "never",
"named": "never",
"asyncArrow": "always"
}
],
"space-in-parens": "error",
"space-infix-ops": "error",
"space-unary-ops": "error",
"spaced-comment": "error",
"template-tag-spacing": "error",
"unicode-bom": "error",
"arrow-body-style": "error",
"arrow-parens": ["error", "as-needed"],
"arrow-spacing": "error",
"no-duplicate-imports": "error",
"no-useless-computed-key": "error",
"no-useless-constructor": "error",
"prefer-arrow-callback": "error",
"prefer-numeric-literals": "error",
"prefer-rest-params": "error",
"prefer-spread": "error",
"prefer-template": "error",
"prefer-object-has-own": "error",
"rest-spread-spacing": "error",
"template-curly-spacing": "error",
"yield-star-spacing": "error",
"no-param-reassign": "error",
"no-restricted-globals": [
"error",
{
"name": "Buffer",
"message": "Import Buffer from `node:buffer` instead"
},
{
"name": "process",
"message": "Import process from `node:process` instead"
},
{
"name": "setTimeout",
"message": "Import setTimeout from `node:timers` instead"
},
{
"name": "setInterval",
"message": "Import setInterval from `node:timers` instead"
},
{
"name": "setImmediate",
"message": "Import setImmediate from `node:timers` instead"
},
{
"name": "clearTimeout",
"message": "Import clearTimeout from `node:timers` instead"
},
{
"name": "clearInterval",
"message": "Import clearInterval from `node:timers` instead"
}
],
"no-restricted-syntax": [
"error",
{
"selector": "AssignmentExpression[left.object.name='module'][left.property.name='exports']",
"message": "Use named exports instead of module.exports"
},
{
"selector": "VariableDeclarator[init.callee.name='require'][init.arguments.0.value=/^\\./]:not([id.type='ObjectPattern'])",
"message": "Use object destructuring when requiring local modules"
}
]
}
},
{
"files": ["src/client/websocket/handlers/*.js"],
"rules": {
"no-restricted-syntax": [
"error",
{
"selector": "VariableDeclarator[init.callee.name='require'][init.arguments.0.value=/^\\./]:not([id.type='ObjectPattern'])",
"message": "Use object destructuring when requiring local modules"
}
]
}
},
{
"files": ["typings/*.ts", "scripts/*.mjs"],
"parser": "@typescript-eslint/parser",
"parserOptions": {
"project": "./tsconfig.json"
},
"plugins": ["@typescript-eslint"],
"rules": {
"@typescript-eslint/naming-convention": [
2,
{
"selector": "typeParameter",
"format": ["PascalCase"],
"custom": {
"regex": "^\\w{3,}",
"match": true
}
}
],
"no-restricted-syntax": [
2,
{
"selector": "MethodDefinition[key.name!=on][key.name!=once][key.name!=off] > TSEmptyBodyFunctionExpression > Identifier :not(TSTypeOperator[operator=readonly]) > TSArrayType",
"message": "Array parameters on methods must be readonly"
},
{
"selector": "MethodDefinition > TSEmptyBodyFunctionExpression > Identifier TSTypeReference > Identifier[name=Collection]",
"message": "Parameters of type Collection on methods must use ReadonlyCollection"
},
{
"selector": "TSDeclareFunction > Identifier :not(TSTypeOperator[operator=readonly]) > TSArrayType",
"message": "Array parameters on functions must be readonly"
},
{
"selector": "TSDeclareFunction Identifier TSTypeReference > Identifier[name=Collection]",
"message": "Parameters of type Collection on functions must use ReadonlyCollection"
},
{
"selector": "TSInterfaceDeclaration TSPropertySignature :not(TSTypeOperator[operator=readonly]) > TSArrayType",
"message": "Array properties on interfaces must be readonly"
},
{
"selector": "TSInterfaceDeclaration TSPropertySignature TSTypeReference > Identifier[name=Collection]",
"message": "Interface properties of type Collection must use ReadonlyCollection"
}
]
}
}
]
}

View File

@@ -1,5 +1,5 @@
{
"$schema": "https://json.schemastore.org/lintstagedrc.schema.json",
"*": "prettier --ignore-unknown --write",
"{src/**,test/**,typings/**,scripts/**}.{mjs,js,ts}": "cross-env ESLINT_USE_FLAT_CONFIG=false eslint --fix --format=pretty"
"{src/**,typings/**,scripts/**}.{mjs,js,ts}": "cross-env eslint --fix --format=pretty"
}

View File

@@ -6,8 +6,8 @@
"scripts": {
"test": "pnpm run docs:test && pnpm run test:typescript",
"test:typescript": "tsc --noEmit && tsd",
"lint": "prettier --check . && tslint typings/index.d.ts && cross-env ESLINT_USE_FLAT_CONFIG=false eslint --format=pretty src typings",
"format": "prettier --write . && cross-env ESLINT_USE_FLAT_CONFIG=false eslint --fix --format=pretty src",
"lint": "prettier --check . && cross-env TIMING=1 eslint --format=pretty scripts src typings",
"format": "prettier --write . && cross-env TIMING=1 eslint --fix --format=pretty scripts src typings",
"fmt": "pnpm run format",
"docs": "docgen -i \"./src/*.js\" \"./src/**/*.js\" -c ./docs/index.json -r ../../ -o ./docs/docs.json && pnpm run docs:new",
"docs:test": "docgen -i \"./src/*.js\" \"./src/**/*.js\" -c ./docs/index.json -r ../../",
@@ -87,11 +87,9 @@
"@discordjs/scripts": "workspace:^",
"@favware/cliff-jumper": "^4.1.0",
"@types/node": "^22.15.2",
"@typescript-eslint/eslint-plugin": "^8.29.0",
"@typescript-eslint/parser": "^8.29.0",
"cross-env": "^7.0.3",
"dtslint": "4.2.1",
"eslint": "^9.25.1",
"eslint-config-neon": "^0.2.7",
"eslint-formatter-compact": "^8.40.0",
"eslint-formatter-pretty": "^5.0.0",
"eslint-plugin-import": "^2.31.0",
@@ -99,7 +97,6 @@
"jest": "29.7.0",
"prettier": "^3.5.3",
"tsd": "^0.31.2",
"tslint": "6.1.3",
"turbo": "^2.5.2",
"typescript": "~5.8.3"
},

View File

@@ -1,4 +1,5 @@
import { cp } from 'node:fs/promises';
import { URL } from 'node:url';
const rawIndexDTS = new URL('../typings/index.d.ts', import.meta.url);
const rawIndexMTS = new URL('../typings/index.d.mts', import.meta.url);

View File

@@ -1,4 +1,5 @@
import { readdir, writeFile } from 'node:fs/promises';
import { URL } from 'node:url';
async function writeWebsocketHandlerImports() {
const lines = ["'use strict';\n", 'const PacketHandlers = Object.fromEntries(['];
@@ -25,8 +26,8 @@ async function writeClientActionImports() {
' // These symbols represent fully built data that we inject at times when calling actions manually.',
' // Action#getUser, for example, will return the injected data (which is assumed to be a built structure)',
' // instead of trying to make it from provided data',
" injectedUser = Symbol('djs.actions.injectedUser');",
" injectedChannel = Symbol('djs.actions.injectedChannel');",
" injectedUser = Symbol('djs.actions.injectedUser');\n",
" injectedChannel = Symbol('djs.actions.injectedChannel');\n",
" injectedMessage = Symbol('djs.actions.injectedMessage');\n",
' constructor(client) {',
' this.client = client;\n',

View File

@@ -10,6 +10,7 @@ const { flatten } = require('../util/Util.js');
/**
* The base class for all clients.
*
* @extends {AsyncEventEmitter}
*/
class BaseClient extends AsyncEventEmitter {
@@ -23,6 +24,7 @@ class BaseClient extends AsyncEventEmitter {
const defaultOptions = Options.createDefault();
/**
* The options the client was instantiated with
*
* @type {ClientOptions}
*/
this.options = {
@@ -51,6 +53,7 @@ class BaseClient extends AsyncEventEmitter {
/**
* The REST manager of the client
*
* @type {REST}
*/
this.rest = new REST(this.options.rest);
@@ -60,6 +63,7 @@ class BaseClient extends AsyncEventEmitter {
/**
* Destroys all assets used by the base client.
*
* @returns {void}
*/
destroy() {
@@ -69,6 +73,7 @@ class BaseClient extends AsyncEventEmitter {
/**
* Options used for deleting a webhook.
*
* @typedef {Object} WebhookDeleteOptions
* @property {string} [token] Token of the webhook
* @property {string} [reason] The reason for deleting the webhook
@@ -76,6 +81,7 @@ class BaseClient extends AsyncEventEmitter {
/**
* Deletes a webhook.
*
* @param {Snowflake} id The webhook's id
* @param {WebhookDeleteOptions} [options] Options for deleting the webhook
* @returns {Promise<void>}
@@ -86,6 +92,7 @@ class BaseClient extends AsyncEventEmitter {
/**
* Increments max listeners by one, if they are not zero.
*
* @private
*/
incrementMaxListeners() {
@@ -97,6 +104,7 @@ class BaseClient extends AsyncEventEmitter {
/**
* Decrements max listeners by one, if they are not zero.
*
* @private
*/
decrementMaxListeners() {

View File

@@ -6,10 +6,6 @@ const { Collection } = require('@discordjs/collection');
const { makeURLSearchParams } = require('@discordjs/rest');
const { WebSocketManager, WebSocketShardEvents, WebSocketShardStatus } = require('@discordjs/ws');
const { GatewayDispatchEvents, GatewayIntentBits, OAuth2Scopes, Routes } = require('discord-api-types/v10');
const { BaseClient } = require('./BaseClient.js');
const { ActionsManager } = require('./actions/ActionsManager.js');
const { ClientVoiceManager } = require('./voice/ClientVoiceManager.js');
const { PacketHandlers } = require('./websocket/handlers/index.js');
const { DiscordjsError, DiscordjsTypeError, ErrorCodes } = require('../errors/index.js');
const { ChannelManager } = require('../managers/ChannelManager.js');
const { GuildManager } = require('../managers/GuildManager.js');
@@ -32,6 +28,10 @@ const { Options } = require('../util/Options.js');
const { PermissionsBitField } = require('../util/PermissionsBitField.js');
const { Status } = require('../util/Status.js');
const { Sweepers } = require('../util/Sweepers.js');
const { BaseClient } = require('./BaseClient.js');
const { ActionsManager } = require('./actions/ActionsManager.js');
const { ClientVoiceManager } = require('./voice/ClientVoiceManager.js');
const { PacketHandlers } = require('./websocket/handlers/index.js');
const WaitingForGuildEvents = [GatewayDispatchEvents.GuildCreate, GatewayDispatchEvents.GuildDelete];
const BeforeReadyWhitelist = [
@@ -46,6 +46,7 @@ const BeforeReadyWhitelist = [
/**
* The main hub for interacting with the Discord API, and the starting point for any bot.
*
* @extends {BaseClient}
*/
class Client extends BaseClient {
@@ -69,6 +70,7 @@ class Client extends BaseClient {
/**
* The presence of the Client
*
* @private
* @type {ClientPresence}
*/
@@ -78,6 +80,7 @@ class Client extends BaseClient {
/**
* The current status of this Client
*
* @type {Status}
* @private
*/
@@ -85,6 +88,7 @@ class Client extends BaseClient {
/**
* A set of guild ids this Client expects to receive
*
* @name Client#expectedGuilds
* @type {Set<string>}
* @private
@@ -93,6 +97,7 @@ class Client extends BaseClient {
/**
* The ready timeout
*
* @name Client#readyTimeout
* @type {?NodeJS.Timeout}
* @private
@@ -101,6 +106,7 @@ class Client extends BaseClient {
/**
* The action manager of the client
*
* @type {ActionsManager}
* @private
*/
@@ -108,6 +114,7 @@ class Client extends BaseClient {
/**
* The user manager of this client
*
* @type {UserManager}
*/
this.users = new UserManager(this);
@@ -115,6 +122,7 @@ class Client extends BaseClient {
/**
* A manager of all the guilds the client is currently handling -
* as long as sharding isn't being used, this will be *every* guild the bot is a member of
*
* @type {GuildManager}
*/
this.guilds = new GuildManager(this);
@@ -124,12 +132,14 @@ class Client extends BaseClient {
* as long as sharding isn't being used, this will be *every* channel in *every* guild the bot
* is a member of. Note that DM channels will not be initially cached, and thus not be present
* in the Manager without their explicit fetching or use.
*
* @type {ChannelManager}
*/
this.channels = new ChannelManager(this);
/**
* The sweeping functions and their intervals used to periodically sweep caches
*
* @type {Sweepers}
*/
this.sweepers = new Sweepers(this, this.options.sweepers);
@@ -140,6 +150,7 @@ class Client extends BaseClient {
* Authorization token for the logged in bot.
* If present, this defaults to `process.env.DISCORD_TOKEN` when instantiating the client
* <warn>This should be kept private at all times.</warn>
*
* @type {?string}
*/
this.token = process.env.DISCORD_TOKEN;
@@ -159,12 +170,14 @@ class Client extends BaseClient {
/**
* The WebSocket manager of the client
*
* @type {WebSocketManager}
*/
this.ws = new WebSocketManager(wsOptions);
/**
* Shard helpers for the client (only if the process was spawned from a {@link ShardingManager})
*
* @type {?ShardClientUtil}
*/
this.shard = process.env.SHARDING_MANAGER
@@ -173,42 +186,49 @@ class Client extends BaseClient {
/**
* The voice manager of the client
*
* @type {ClientVoiceManager}
*/
this.voice = new ClientVoiceManager(this);
/**
* User that the client is logged in as
*
* @type {?ClientUser}
*/
this.user = null;
/**
* The application of this bot
*
* @type {?ClientApplication}
*/
this.application = null;
/**
* The latencies of the WebSocketShard connections
*
* @type {Collection<number, number>}
*/
this.pings = new Collection();
/**
* The last time a ping was sent (a timestamp) for each WebSocketShard connection
*
* @type {Collection<number, number>}
*/
this.lastPingTimestamps = new Collection();
/**
* Timestamp of the time the client was last {@link Status.Ready} at
*
* @type {?number}
*/
this.readyTimestamp = null;
/**
* An array of queued events before this Client became ready
*
* @type {Object[]}
* @private
* @name Client#incomingPacketQueue
@@ -221,6 +241,7 @@ class Client extends BaseClient {
/**
* Time at which the client was last regarded as being in the {@link Status.Ready} state
* (each time the client disconnects and successfully reconnects, this will be overwritten)
*
* @type {?Date}
* @readonly
*/
@@ -230,6 +251,7 @@ class Client extends BaseClient {
/**
* How long it has been since the client last entered the {@link Status.Ready} state in milliseconds
*
* @type {?number}
* @readonly
*/
@@ -239,6 +261,7 @@ class Client extends BaseClient {
/**
* Logs the client in, establishing a WebSocket connection to Discord.
*
* @param {string} [token=this.token] Token of the account to log in with
* @returns {Promise<string>} Token of the account used
* @example
@@ -246,7 +269,7 @@ class Client extends BaseClient {
*/
async login(token = this.token) {
if (!token || typeof token !== 'string') throw new DiscordjsError(ErrorCodes.TokenInvalid);
this.token = token.replace(/^(Bot|Bearer)\s*/i, '');
this.token = token.replace(/^bot\s*/i, '');
this.rest.setToken(this.token);
@@ -266,6 +289,7 @@ class Client extends BaseClient {
/**
* Checks if the client can be marked as ready
*
* @private
*/
async _checkReady() {
@@ -274,6 +298,7 @@ class Client extends BaseClient {
clearTimeout(this.readyTimeout);
this.readyTimeout = null;
}
// Step 1. If we don't have any other guilds pending, we are ready
if (
!this.expectedGuilds.size &&
@@ -284,6 +309,7 @@ class Client extends BaseClient {
this._triggerClientReady();
return;
}
const hasGuildsIntent = this.options.intents.has(GatewayIntentBits.Guilds);
// Step 2. Create a timeout that will mark the client as ready if there are still unavailable guilds
// * The timeout is 15 seconds by default
@@ -311,6 +337,7 @@ class Client extends BaseClient {
/**
* Attaches event handlers to the WebSocketShardManager from `@discordjs/ws`.
*
* @private
*/
_attachEvents() {
@@ -319,12 +346,13 @@ class Client extends BaseClient {
);
this.ws.on(WebSocketShardEvents.Dispatch, this._handlePacket.bind(this));
this.ws.on(WebSocketShardEvents.Ready, data => {
this.ws.on(WebSocketShardEvents.Ready, async data => {
for (const guild of data.guilds) {
this.expectedGuilds.add(guild.id);
}
this.status = Status.WaitingForGuilds;
this._checkReady();
await this._checkReady();
});
this.ws.on(WebSocketShardEvents.HeartbeatComplete, ({ heartbeatAt, latency }, shardId) => {
@@ -336,18 +364,19 @@ class Client extends BaseClient {
/**
* Processes a packet and queues it if this WebSocketManager is not ready.
*
* @param {GatewayDispatchPayload} packet The packet to be handled
* @param {number} shardId The shardId that received this packet
* @private
*/
_handlePacket(packet, shardId) {
async _handlePacket(packet, shardId) {
if (this.status !== Status.Ready && !BeforeReadyWhitelist.includes(packet.t)) {
this.incomingPacketQueue.push({ packet, shardId });
} else {
if (this.incomingPacketQueue.length) {
const item = this.incomingPacketQueue.shift();
setImmediate(() => {
this._handlePacket(item.packet, item.shardId);
setImmediate(async () => {
await this._handlePacket(item.packet, item.shardId);
}).unref();
}
@@ -357,13 +386,14 @@ class Client extends BaseClient {
if (this.status === Status.WaitingForGuilds && WaitingForGuildEvents.includes(packet.t)) {
this.expectedGuilds.delete(packet.d.id);
this._checkReady();
await this._checkReady();
}
}
}
/**
* Broadcasts a packet to every shard of this client handles.
*
* @param {Object} packet The packet to send
* @private
*/
@@ -374,6 +404,7 @@ class Client extends BaseClient {
/**
* Causes the client to be marked as ready and emits the ready event.
*
* @private
*/
_triggerClientReady() {
@@ -383,6 +414,7 @@ class Client extends BaseClient {
/**
* Emitted when the client becomes ready to start working.
*
* @event Client#clientReady
* @param {Client} client The client
*/
@@ -392,6 +424,7 @@ class Client extends BaseClient {
/**
* Returns whether the client has logged in, indicative of being able to access
* properties such as `user` and `application`.
*
* @returns {boolean}
*/
isReady() {
@@ -400,6 +433,7 @@ class Client extends BaseClient {
/**
* The average ping of all WebSocketShards
*
* @type {?number}
* @readonly
*/
@@ -409,6 +443,7 @@ class Client extends BaseClient {
/**
* Logs out, terminates the connection to Discord, and destroys the client.
*
* @returns {Promise<void>}
*/
async destroy() {
@@ -422,6 +457,7 @@ class Client extends BaseClient {
/**
* Options used when fetching an invite from Discord.
*
* @typedef {Object} ClientFetchInviteOptions
* @property {Snowflake} [guildScheduledEventId] The id of the guild scheduled event to include with
* the invite
@@ -429,6 +465,7 @@ class Client extends BaseClient {
/**
* Obtains an invite from Discord.
*
* @param {InviteResolvable} invite Invite code or URL
* @param {ClientFetchInviteOptions} [options] Options for fetching the invite
* @returns {Promise<Invite>}
@@ -449,6 +486,7 @@ class Client extends BaseClient {
/**
* Obtains a template from Discord.
*
* @param {GuildTemplateResolvable} template Template code or URL
* @returns {Promise<GuildTemplate>}
* @example
@@ -464,6 +502,7 @@ class Client extends BaseClient {
/**
* Obtains a webhook from Discord.
*
* @param {Snowflake} id The webhook's id
* @param {string} [token] Token for the webhook
* @returns {Promise<Webhook>}
@@ -479,6 +518,7 @@ class Client extends BaseClient {
/**
* Obtains the available voice regions from Discord.
*
* @returns {Promise<Collection<string, VoiceRegion>>}
* @example
* client.fetchVoiceRegions()
@@ -494,6 +534,7 @@ class Client extends BaseClient {
/**
* Obtains a sticker from Discord.
*
* @param {Snowflake} id The sticker's id
* @returns {Promise<Sticker>}
* @example
@@ -508,12 +549,14 @@ class Client extends BaseClient {
/**
* Options for fetching sticker packs.
*
* @typedef {Object} StickerPackFetchOptions
* @property {Snowflake} [packId] The id of the sticker pack to fetch
*/
/**
* Obtains the list of available sticker packs.
*
* @param {StickerPackFetchOptions} [options={}] Options for fetching sticker packs
* @returns {Promise<Collection<Snowflake, StickerPack>|StickerPack>}
* A collection of sticker packs, or a single sticker pack if a packId was provided
@@ -528,8 +571,8 @@ class Client extends BaseClient {
*/
async fetchStickerPacks({ packId } = {}) {
if (packId) {
const data = await this.rest.get(Routes.stickerPack(packId));
return new StickerPack(this, data);
const innerData = await this.rest.get(Routes.stickerPack(packId));
return new StickerPack(this, innerData);
}
const data = await this.rest.get(Routes.stickerPacks());
@@ -538,6 +581,7 @@ class Client extends BaseClient {
/**
* Obtains the list of default soundboard sounds.
*
* @returns {Promise<Collection<string, SoundboardSound>>}
* @example
* client.fetchDefaultSoundboardSounds()
@@ -551,6 +595,7 @@ class Client extends BaseClient {
/**
* Obtains a guild preview from Discord, available for all guilds the bot is in and all Discoverable guilds.
*
* @param {GuildResolvable} guild The guild to fetch the preview for
* @returns {Promise<GuildPreview>}
*/
@@ -563,6 +608,7 @@ class Client extends BaseClient {
/**
* Obtains the widget data of a guild from Discord, available for guilds with the widget enabled.
*
* @param {GuildResolvable} guild The guild to fetch the widget data for
* @returns {Promise<Widget>}
*/
@@ -575,6 +621,7 @@ class Client extends BaseClient {
/**
* Options for {@link Client#generateInvite}.
*
* @typedef {Object} InviteGenerationOptions
* @property {OAuth2Scopes[]} scopes Scopes that should be requested
* @property {PermissionResolvable} [permissions] Permissions to request
@@ -584,6 +631,7 @@ class Client extends BaseClient {
/**
* Generates a link that can be used to invite the bot to a guild.
*
* @param {InviteGenerationOptions} [options={}] Options for the invite
* @returns {string}
* @example
@@ -610,15 +658,19 @@ class Client extends BaseClient {
if (scopes === undefined) {
throw new DiscordjsTypeError(ErrorCodes.InvalidMissingScopes);
}
if (!Array.isArray(scopes)) {
throw new DiscordjsTypeError(ErrorCodes.InvalidType, 'scopes', 'Array of Invite Scopes', true);
}
if (!scopes.some(scope => [OAuth2Scopes.Bot, OAuth2Scopes.ApplicationsCommands].includes(scope))) {
throw new DiscordjsTypeError(ErrorCodes.InvalidMissingScopes);
}
if (!scopes.includes(OAuth2Scopes.Bot) && options.permissions) {
throw new DiscordjsTypeError(ErrorCodes.InvalidScopesWithPermissions);
}
const validScopes = Object.values(OAuth2Scopes);
const invalidScope = scopes.find(scope => !validScopes.includes(scope));
if (invalidScope) {
@@ -654,6 +706,7 @@ class Client extends BaseClient {
/**
* Partially censored client token for debug logging purposes.
*
* @type {?string}
* @readonly
* @private
@@ -663,23 +716,26 @@ class Client extends BaseClient {
return this.token
.split('.')
.map((val, i) => (i > 1 ? val.replace(/./g, '*') : val))
.map((val, index) => (index > 1 ? val.replaceAll(/./g, '*') : val))
.join('.');
}
/**
* Calls {@link https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/eval} on a script
* with the client as `this`.
*
* @param {string} script Script to eval
* @returns {*}
* @private
*/
_eval(script) {
// eslint-disable-next-line no-eval
return eval(script);
}
/**
* Validates the client options.
*
* @param {ClientOptions} [options=this.options] Options to validate
* @private
*/
@@ -689,30 +745,38 @@ class Client extends BaseClient {
} else {
options.intents = new IntentsBitField(options.intents ?? options.ws.intents).freeze();
}
if (typeof options.sweepers !== 'object' || options.sweepers === null) {
throw new DiscordjsTypeError(ErrorCodes.ClientInvalidOption, 'sweepers', 'an object');
}
if (!Array.isArray(options.partials)) {
throw new DiscordjsTypeError(ErrorCodes.ClientInvalidOption, 'partials', 'an Array');
}
if (typeof options.waitGuildTimeout !== 'number' || isNaN(options.waitGuildTimeout)) {
if (typeof options.waitGuildTimeout !== 'number' || Number.isNaN(options.waitGuildTimeout)) {
throw new DiscordjsTypeError(ErrorCodes.ClientInvalidOption, 'waitGuildTimeout', 'a number');
}
if (typeof options.failIfNotExists !== 'boolean') {
throw new DiscordjsTypeError(ErrorCodes.ClientInvalidOption, 'failIfNotExists', 'a boolean');
}
if (typeof options.enforceNonce !== 'boolean') {
throw new DiscordjsTypeError(ErrorCodes.ClientInvalidOption, 'enforceNonce', 'a boolean');
}
if (
(typeof options.allowedMentions !== 'object' && options.allowedMentions !== undefined) ||
options.allowedMentions === null
) {
throw new DiscordjsTypeError(ErrorCodes.ClientInvalidOption, 'allowedMentions', 'an object');
}
if (typeof options.ws !== 'object' || options.ws === null) {
throw new DiscordjsTypeError(ErrorCodes.ClientInvalidOption, 'ws', 'an object');
}
if (
(typeof options.presence !== 'object' || options.presence === null) &&
options.ws.initialPresence === undefined
@@ -721,9 +785,11 @@ class Client extends BaseClient {
} else {
options.ws.initialPresence = options.ws.initialPresence ?? this.presence._parse(this.options.presence);
}
if (typeof options.rest !== 'object' || options.rest === null) {
throw new DiscordjsTypeError(ErrorCodes.ClientInvalidOption, 'rest', 'an object');
}
if (typeof options.jsonTransformer !== 'function') {
throw new DiscordjsTypeError(ErrorCodes.ClientInvalidOption, 'jsonTransformer', 'a function');
}
@@ -754,17 +820,20 @@ exports.Client = Client;
* 000000111011000111100001101001000101000000 00001 00000 000000000000
* number of milliseconds since Discord epoch worker pid increment
* ```
*
* @typedef {string} Snowflake
*/
/**
* Emitted for general debugging information.
*
* @event Client#debug
* @param {string} info The debug information
*/
/**
* Emitted for general warnings.
*
* @event Client#warn
* @param {string} info The warning
*/

View File

@@ -1,18 +1,20 @@
'use strict';
const { BaseClient } = require('./BaseClient.js');
const { DiscordjsError, ErrorCodes } = require('../errors/index.js');
const { Webhook } = require('../structures/Webhook.js');
const { parseWebhookURL } = require('../util/Util.js');
const { BaseClient } = require('./BaseClient.js');
/**
* The webhook client.
*
* @implements {Webhook}
* @extends {BaseClient}
*/
class WebhookClient extends BaseClient {
/**
* Represents the credentials used for a webhook in the form of its id and token.
*
* @typedef {Object} WebhookClientDataIdWithToken
* @property {Snowflake} id The webhook's id
* @property {string} token The webhook's token
@@ -20,17 +22,20 @@ class WebhookClient extends BaseClient {
/**
* Represents the credentials used for a webhook in the form of a URL.
*
* @typedef {Object} WebhookClientDataURL
* @property {string} url The full URL for the webhook
*/
/**
* Represents the credentials used for a webhook.
*
* @typedef {WebhookClientDataIdWithToken|WebhookClientDataURL} WebhookClientData
*/
/**
* Options for a webhook client.
*
* @typedef {Object} WebhookClientOptions
* @property {MessageMentionOptions} [allowedMentions] Default value for {@link BaseMessageOptions#allowedMentions}
* @property {RESTOptions} [rest] Options for the REST manager
@@ -60,41 +65,52 @@ class WebhookClient extends BaseClient {
/**
* The options the webhook client was instantiated with.
*
* @type {WebhookClientOptions}
* @name WebhookClient#options
*/
// These are here only for documentation purposes - they are implemented by Webhook
/* eslint-disable no-empty-function */
/* eslint-disable jsdoc/check-param-names, getter-return */
/**
* Sends a message with this webhook.
*
* @param {string|MessagePayload|WebhookMessageCreateOptions} options The content for the reply
* @returns {Promise<APIMessage>}
*/
send() {}
async send() {}
/**
* Gets a message that was sent by this webhook.
*
* @param {Snowflake} message The id of the message to fetch
* @param {WebhookFetchMessageOptions} [options={}] The options to provide to fetch the message.
* @param {WebhookFetchMessageOptions} [options] The options to provide to fetch the message.
* @returns {Promise<APIMessage>} Returns the message sent by this webhook
*/
fetchMessage() {}
async fetchMessage() {}
/**
* Edits a message that was sent by this webhook.
*
* @param {MessageResolvable} message The message to edit
* @param {string|MessagePayload|WebhookMessageEditOptions} options The options to provide
* @returns {Promise<APIMessage>} Returns the message edited by this webhook
*/
editMessage() {}
async editMessage() {}
sendSlackMessage() {}
edit() {}
delete() {}
deleteMessage() {}
get createdTimestamp() {}
get createdAt() {}
get url() {}
}

View File

@@ -114,6 +114,7 @@ class Action {
return this.client.users._add(data.member.user);
}
}
return this.getUser(data);
}

View File

@@ -5,7 +5,9 @@ class ActionsManager {
// Action#getUser, for example, will return the injected data (which is assumed to be a built structure)
// instead of trying to make it from provided data
injectedUser = Symbol('djs.actions.injectedUser');
injectedChannel = Symbol('djs.actions.injectedChannel');
injectedMessage = Symbol('djs.actions.injectedMessage');
constructor(client) {

View File

@@ -1,7 +1,7 @@
'use strict';
const { Action } = require('./Action.js');
const { Events } = require('../../util/Events.js');
const { Action } = require('./Action.js');
class ChannelCreateAction extends Action {
handle(data) {
@@ -11,11 +11,13 @@ class ChannelCreateAction extends Action {
if (!existing && channel) {
/**
* Emitted whenever a guild channel is created.
*
* @event Client#channelCreate
* @param {GuildChannel} channel The channel that was created
*/
client.emit(Events.ChannelCreate, channel);
}
return { channel };
}
}

View File

@@ -1,7 +1,7 @@
'use strict';
const { Action } = require('./Action.js');
const { Events } = require('../../util/Events.js');
const { Action } = require('./Action.js');
class ChannelDeleteAction extends Action {
handle(data) {
@@ -12,6 +12,7 @@ class ChannelDeleteAction extends Action {
client.channels._remove(channel.id);
/**
* Emitted whenever a channel is deleted.
*
* @event Client#channelDelete
* @param {DMChannel|GuildChannel} channel The channel that was deleted
*/

View File

@@ -1,7 +1,7 @@
'use strict';
const { Action } = require('./Action.js');
const { createChannel } = require('../../util/Channels.js');
const { Action } = require('./Action.js');
class ChannelUpdateAction extends Action {
handle(data) {

View File

@@ -1,7 +1,7 @@
'use strict';
const { Action } = require('./Action.js');
const { Events } = require('../../util/Events.js');
const { Action } = require('./Action.js');
class GuildEmojiCreateAction extends Action {
handle(guild, createdEmoji) {
@@ -9,6 +9,7 @@ class GuildEmojiCreateAction extends Action {
const emoji = guild.emojis._add(createdEmoji);
/**
* Emitted whenever a custom emoji is created in a guild.
*
* @event Client#emojiCreate
* @param {GuildEmoji} emoji The emoji that was created
*/

View File

@@ -1,13 +1,14 @@
'use strict';
const { Action } = require('./Action.js');
const { Events } = require('../../util/Events.js');
const { Action } = require('./Action.js');
class GuildEmojiDeleteAction extends Action {
handle(emoji) {
emoji.guild.emojis.cache.delete(emoji.id);
/**
* Emitted whenever a custom emoji is deleted in a guild.
*
* @event Client#emojiDelete
* @param {GuildEmoji} emoji The emoji that was deleted
*/

View File

@@ -1,13 +1,14 @@
'use strict';
const { Action } = require('./Action.js');
const { Events } = require('../../util/Events.js');
const { Action } = require('./Action.js');
class GuildEmojiUpdateAction extends Action {
handle(current, data) {
const old = current._update(data);
/**
* Emitted whenever a custom emoji is updated in a guild.
*
* @event Client#emojiUpdate
* @param {GuildEmoji} oldEmoji The old emoji
* @param {GuildEmoji} newEmoji The new emoji

View File

@@ -1,7 +1,7 @@
'use strict';
const { Action } = require('./Action.js');
const { Events } = require('../../util/Events.js');
const { Action } = require('./Action.js');
class GuildMemberRemoveAction extends Action {
handle(data) {
@@ -15,14 +15,17 @@ class GuildMemberRemoveAction extends Action {
guild.members.cache.delete(member.id);
/**
* Emitted whenever a member leaves a guild, or is kicked.
*
* @event Client#guildMemberRemove
* @param {GuildMember} member The member that has left/been kicked from the guild
*/
client.emit(Events.GuildMemberRemove, member);
}
guild.presences.cache.delete(data.user.id);
guild.voiceStates.cache.delete(data.user.id);
}
return { guild, member };
}
}

View File

@@ -1,7 +1,7 @@
'use strict';
const { Action } = require('./Action.js');
const { Events } = require('../../util/Events.js');
const { Action } = require('./Action.js');
class GuildMemberUpdateAction extends Action {
handle(data) {
@@ -22,6 +22,7 @@ class GuildMemberUpdateAction extends Action {
const old = member._update(data);
/**
* Emitted whenever a guild member changes - i.e. new role, removed role, nickname.
*
* @event Client#guildMemberUpdate
* @param {GuildMember} oldMember The member before the update
* @param {GuildMember} newMember The member after the update
@@ -31,6 +32,7 @@ class GuildMemberUpdateAction extends Action {
const newMember = guild.members._add(data);
/**
* Emitted whenever a member becomes available.
*
* @event Client#guildMemberAvailable
* @param {GuildMember} member The member that became available
*/

View File

@@ -1,7 +1,7 @@
'use strict';
const { Action } = require('./Action.js');
const { Events } = require('../../util/Events.js');
const { Action } = require('./Action.js');
class GuildRoleCreateAction extends Action {
handle(data) {
@@ -13,11 +13,13 @@ class GuildRoleCreateAction extends Action {
role = guild.roles._add(data.role);
/**
* Emitted whenever a role is created.
*
* @event Client#roleCreate
* @param {Role} role The role that was created
*/
if (!already) client.emit(Events.GuildRoleCreate, role);
}
return { role };
}
}

View File

@@ -1,7 +1,7 @@
'use strict';
const { Action } = require('./Action.js');
const { Events } = require('../../util/Events.js');
const { Action } = require('./Action.js');
class GuildRoleDeleteAction extends Action {
handle(data) {
@@ -15,6 +15,7 @@ class GuildRoleDeleteAction extends Action {
guild.roles.cache.delete(data.role_id);
/**
* Emitted whenever a guild role is deleted.
*
* @event Client#roleDelete
* @param {Role} role The role that was deleted
*/

View File

@@ -1,7 +1,7 @@
'use strict';
const { Action } = require('./Action.js');
const { Events } = require('../../util/Events.js');
const { Action } = require('./Action.js');
class GuildScheduledEventDeleteAction extends Action {
handle(data) {
@@ -15,6 +15,7 @@ class GuildScheduledEventDeleteAction extends Action {
/**
* Emitted whenever a guild scheduled event is deleted.
*
* @event Client#guildScheduledEventDelete
* @param {GuildScheduledEvent} guildScheduledEvent The deleted guild scheduled event
*/

View File

@@ -1,7 +1,7 @@
'use strict';
const { Action } = require('./Action.js');
const { Events } = require('../../util/Events.js');
const { Action } = require('./Action.js');
class GuildScheduledEventUserAddAction extends Action {
handle(data) {
@@ -15,6 +15,7 @@ class GuildScheduledEventUserAddAction extends Action {
if (guildScheduledEvent && user) {
/**
* Emitted whenever a user subscribes to a guild scheduled event
*
* @event Client#guildScheduledEventUserAdd
* @param {GuildScheduledEvent} guildScheduledEvent The guild scheduled event
* @param {User} user The user who subscribed

View File

@@ -1,7 +1,7 @@
'use strict';
const { Action } = require('./Action.js');
const { Events } = require('../../util/Events.js');
const { Action } = require('./Action.js');
class GuildScheduledEventUserRemoveAction extends Action {
handle(data) {
@@ -15,6 +15,7 @@ class GuildScheduledEventUserRemoveAction extends Action {
if (guildScheduledEvent && user) {
/**
* Emitted whenever a user unsubscribes from a guild scheduled event
*
* @event Client#guildScheduledEventUserRemove
* @param {GuildScheduledEvent} guildScheduledEvent The guild scheduled event
* @param {User} user The user who unsubscribed

View File

@@ -1,7 +1,7 @@
'use strict';
const { Action } = require('./Action.js');
const { Events } = require('../../util/Events.js');
const { Action } = require('./Action.js');
class GuildSoundboardSoundDeleteAction extends Action {
handle(data) {
@@ -16,6 +16,7 @@ class GuildSoundboardSoundDeleteAction extends Action {
/**
* Emitted whenever a soundboard sound is deleted in a guild.
*
* @event Client#guildSoundboardSoundDelete
* @param {SoundboardSound} soundboardSound The soundboard sound that was deleted
*/

View File

@@ -1,7 +1,7 @@
'use strict';
const { Action } = require('./Action.js');
const { Events } = require('../../util/Events.js');
const { Action } = require('./Action.js');
class GuildStickerCreateAction extends Action {
handle(guild, createdSticker) {
@@ -9,6 +9,7 @@ class GuildStickerCreateAction extends Action {
const sticker = guild.stickers._add(createdSticker);
/**
* Emitted whenever a custom sticker is created in a guild.
*
* @event Client#stickerCreate
* @param {Sticker} sticker The sticker that was created
*/

View File

@@ -1,13 +1,14 @@
'use strict';
const { Action } = require('./Action.js');
const { Events } = require('../../util/Events.js');
const { Action } = require('./Action.js');
class GuildStickerDeleteAction extends Action {
handle(sticker) {
sticker.guild.stickers.cache.delete(sticker.id);
/**
* Emitted whenever a custom sticker is deleted in a guild.
*
* @event Client#stickerDelete
* @param {Sticker} sticker The sticker that was deleted
*/

View File

@@ -1,13 +1,14 @@
'use strict';
const { Action } = require('./Action.js');
const { Events } = require('../../util/Events.js');
const { Action } = require('./Action.js');
class GuildStickerUpdateAction extends Action {
handle(current, data) {
const old = current._update(data);
/**
* Emitted whenever a custom sticker is updated in a guild.
*
* @event Client#stickerUpdate
* @param {Sticker} oldSticker The old sticker
* @param {Sticker} newSticker The new sticker

View File

@@ -1,7 +1,7 @@
'use strict';
const { Action } = require('./Action.js');
const { Events } = require('../../util/Events.js');
const { Action } = require('./Action.js');
class GuildUpdateAction extends Action {
handle(data) {
@@ -12,6 +12,7 @@ class GuildUpdateAction extends Action {
const old = guild._update(data);
/**
* Emitted whenever a guild is updated - e.g. name change.
*
* @event Client#guildUpdate
* @param {Guild} oldGuild The guild before the update
* @param {Guild} newGuild The guild after the update

View File

@@ -1,7 +1,6 @@
'use strict';
const { InteractionType, ComponentType, ApplicationCommandType } = require('discord-api-types/v10');
const { Action } = require('./Action.js');
const { AutocompleteInteraction } = require('../../structures/AutocompleteInteraction.js');
const { ButtonInteraction } = require('../../structures/ButtonInteraction.js');
const { ChannelSelectMenuInteraction } = require('../../structures/ChannelSelectMenuInteraction.js');
@@ -15,6 +14,7 @@ const { StringSelectMenuInteraction } = require('../../structures/StringSelectMe
const { UserContextMenuCommandInteraction } = require('../../structures/UserContextMenuCommandInteraction.js');
const { UserSelectMenuInteraction } = require('../../structures/UserSelectMenuInteraction.js');
const { Events } = require('../../util/Events.js');
const { Action } = require('./Action.js');
class InteractionCreateAction extends Action {
handle(data) {
@@ -49,6 +49,7 @@ class InteractionCreateAction extends Action {
);
return;
}
break;
case InteractionType.MessageComponent:
if (channel && !channel.isTextBased()) return;
@@ -79,6 +80,7 @@ class InteractionCreateAction extends Action {
);
return;
}
break;
case InteractionType.ApplicationCommandAutocomplete:
InteractionClass = AutocompleteInteraction;
@@ -95,6 +97,7 @@ class InteractionCreateAction extends Action {
/**
* Emitted when an interaction is created.
*
* @event Client#interactionCreate
* @param {BaseInteraction} interaction The interaction which was created
*/

View File

@@ -1,7 +1,7 @@
'use strict';
const { Action } = require('./Action.js');
const { Events } = require('../../util/Events.js');
const { Action } = require('./Action.js');
class MessageCreateAction extends Action {
handle(data) {
@@ -26,6 +26,7 @@ class MessageCreateAction extends Action {
/**
* Emitted whenever a message is created.
*
* @event Client#messageCreate
* @param {Message} message The created message
*/

View File

@@ -1,7 +1,7 @@
'use strict';
const { Action } = require('./Action.js');
const { Events } = require('../../util/Events.js');
const { Action } = require('./Action.js');
class MessageDeleteAction extends Action {
handle(data) {
@@ -18,6 +18,7 @@ class MessageDeleteAction extends Action {
channel.messages.cache.delete(message.id);
/**
* Emitted whenever a message is deleted.
*
* @event Client#messageDelete
* @param {Message} message The deleted message
*/

View File

@@ -1,8 +1,8 @@
'use strict';
const { Collection } = require('@discordjs/collection');
const { Action } = require('./Action.js');
const { Events } = require('../../util/Events.js');
const { Action } = require('./Action.js');
class MessageDeleteBulkAction extends Action {
handle(data) {
@@ -33,6 +33,7 @@ class MessageDeleteBulkAction extends Action {
/**
* Emitted whenever messages are deleted in bulk.
*
* @event Client#messageDeleteBulk
* @param {Collection<Snowflake, Message>} messages The deleted messages, mapped by their id
* @param {GuildTextBasedChannel} channel The channel that the messages were deleted in
@@ -40,6 +41,7 @@ class MessageDeleteBulkAction extends Action {
if (messages.size > 0) client.emit(Events.MessageBulkDelete, messages, channel);
return { messages };
}
return {};
}
}

View File

@@ -1,7 +1,7 @@
'use strict';
const { Action } = require('./Action.js');
const { Events } = require('../../util/Events.js');
const { Action } = require('./Action.js');
class MessagePollVoteAddAction extends Action {
handle(data) {
@@ -27,6 +27,7 @@ class MessagePollVoteAddAction extends Action {
/**
* Emitted whenever a user votes in a poll.
*
* @event Client#messagePollVoteAdd
* @param {PollAnswer} pollAnswer The answer that was voted on
* @param {Snowflake} userId The id of the user that voted

View File

@@ -1,7 +1,7 @@
'use strict';
const { Action } = require('./Action.js');
const { Events } = require('../../util/Events.js');
const { Action } = require('./Action.js');
class MessagePollVoteRemoveAction extends Action {
handle(data) {
@@ -25,6 +25,7 @@ class MessagePollVoteRemoveAction extends Action {
/**
* Emitted whenever a user removes their vote in a poll.
*
* @event Client#messagePollVoteRemove
* @param {PollAnswer} pollAnswer The answer where the vote was removed
* @param {Snowflake} userId The id of the user that removed their vote

View File

@@ -1,8 +1,8 @@
'use strict';
const { Action } = require('./Action.js');
const { Events } = require('../../util/Events.js');
const { Partials } = require('../../util/Partials.js');
const { Action } = require('./Action.js');
/*
{ user_id: 'id',
@@ -50,12 +50,14 @@ class MessageReactionAddAction extends Action {
if (fromStructure) return { message, reaction, user };
/**
* Provides additional information about altered reaction
*
* @typedef {Object} MessageReactionEventDetails
* @property {ReactionType} type The type of the reaction
* @property {boolean} burst Determines whether a super reaction was used
*/
/**
* Emitted whenever a reaction is added to a cached message.
*
* @event Client#messageReactionAdd
* @param {MessageReaction} messageReaction The reaction object
* @param {User} user The user that applied the guild or reaction emoji

View File

@@ -1,7 +1,7 @@
'use strict';
const { Action } = require('./Action.js');
const { Events } = require('../../util/Events.js');
const { Action } = require('./Action.js');
/*
{ user_id: 'id',
@@ -36,6 +36,7 @@ class MessageReactionRemoveAction extends Action {
reaction._remove(user, data.burst);
/**
* Emitted whenever a reaction is removed from a cached message.
*
* @event Client#messageReactionRemove
* @param {MessageReaction} messageReaction The reaction object
* @param {User} user The user whose emoji or reaction emoji was removed

View File

@@ -1,7 +1,7 @@
'use strict';
const { Action } = require('./Action.js');
const { Events } = require('../../util/Events.js');
const { Action } = require('./Action.js');
class MessageReactionRemoveAllAction extends Action {
handle(data) {
@@ -25,6 +25,7 @@ class MessageReactionRemoveAllAction extends Action {
/**
* Emitted whenever all reactions are removed from a cached message.
*
* @event Client#messageReactionRemoveAll
* @param {Message} message The message the reactions were removed from
* @param {Collection<string|Snowflake, MessageReaction>} reactions The cached message reactions that were removed.

View File

@@ -1,7 +1,7 @@
'use strict';
const { Action } = require('./Action.js');
const { Events } = require('../../util/Events.js');
const { Action } = require('./Action.js');
class MessageReactionRemoveEmojiAction extends Action {
handle(data) {
@@ -17,6 +17,7 @@ class MessageReactionRemoveEmojiAction extends Action {
/**
* Emitted when a bot removes an emoji reaction from a cached message.
*
* @event Client#messageReactionRemoveEmoji
* @param {MessageReaction} reaction The reaction that was removed
*/

View File

@@ -1,7 +1,7 @@
'use strict';
const { Action } = require('./Action.js');
const { Events } = require('../../util/Events.js');
const { Action } = require('./Action.js');
class StageInstanceCreateAction extends Action {
handle(data) {
@@ -13,6 +13,7 @@ class StageInstanceCreateAction extends Action {
/**
* Emitted whenever a stage instance is created.
*
* @event Client#stageInstanceCreate
* @param {StageInstance} stageInstance The created stage instance
*/

View File

@@ -1,7 +1,7 @@
'use strict';
const { Action } = require('./Action.js');
const { Events } = require('../../util/Events.js');
const { Action } = require('./Action.js');
class StageInstanceDeleteAction extends Action {
handle(data) {
@@ -15,6 +15,7 @@ class StageInstanceDeleteAction extends Action {
/**
* Emitted whenever a stage instance is deleted.
*
* @event Client#stageInstanceDelete
* @param {StageInstance} stageInstance The deleted stage instance
*/

View File

@@ -1,7 +1,7 @@
'use strict';
const { Action } = require('./Action.js');
const { Events } = require('../../util/Events.js');
const { Action } = require('./Action.js');
class StageInstanceUpdateAction extends Action {
handle(data) {
@@ -14,6 +14,7 @@ class StageInstanceUpdateAction extends Action {
/**
* Emitted whenever a stage instance gets updated - e.g. change in topic or privacy level
*
* @event Client#stageInstanceUpdate
* @param {?StageInstance} oldStageInstance The stage instance before the update
* @param {StageInstance} newStageInstance The stage instance after the update

View File

@@ -1,7 +1,7 @@
'use strict';
const { Action } = require('./Action.js');
const { Events } = require('../../util/Events.js');
const { Action } = require('./Action.js');
class ThreadCreateAction extends Action {
handle(data) {
@@ -11,12 +11,14 @@ class ThreadCreateAction extends Action {
if (!existing && thread) {
/**
* Emitted whenever a thread is created or when the client user is added to a thread.
*
* @event Client#threadCreate
* @param {ThreadChannel} thread The thread that was created
* @param {boolean} newlyCreated Whether the thread was newly created
*/
client.emit(Events.ThreadCreate, thread, data.newly_created ?? false);
}
return { thread };
}
}

View File

@@ -1,8 +1,8 @@
'use strict';
const { Collection } = require('@discordjs/collection');
const { Action } = require('./Action.js');
const { Events } = require('../../util/Events.js');
const { Action } = require('./Action.js');
class ThreadMembersUpdateAction extends Action {
handle(data) {
@@ -33,6 +33,7 @@ class ThreadMembersUpdateAction extends Action {
/**
* Emitted whenever members are added or removed from a thread.
* <info>This event requires the {@link GatewayIntentBits.GuildMembers} privileged gateway intent.</info>
*
* @event Client#threadMembersUpdate
* @param {Collection<Snowflake, ThreadMember>} addedMembers The members that were added
* @param {Collection<Snowflake, ThreadMember>} removedMembers The members that were removed
@@ -40,6 +41,7 @@ class ThreadMembersUpdateAction extends Action {
*/
client.emit(Events.ThreadMembersUpdate, addedMembers, removedMembers, thread);
}
return {};
}
}

View File

@@ -1,8 +1,8 @@
'use strict';
const { Action } = require('./Action.js');
const { Typing } = require('../../structures/Typing.js');
const { Events } = require('../../util/Events.js');
const { Action } = require('./Action.js');
class TypingStartAction extends Action {
handle(data) {
@@ -18,6 +18,7 @@ class TypingStartAction extends Action {
if (user) {
/**
* Emitted whenever a user starts typing in a channel.
*
* @event Client#typingStart
* @param {Typing} typing The typing state
*/

View File

@@ -1,7 +1,7 @@
'use strict';
const { Action } = require('./Action.js');
const { Events } = require('../../util/Events.js');
const { Action } = require('./Action.js');
class UserUpdateAction extends Action {
handle(data) {
@@ -15,6 +15,7 @@ class UserUpdateAction extends Action {
* Emitted whenever a user's details (e.g. username) are changed.
* Triggered by the Discord gateway events {@link Events.UserUpdate},
* {@link Events.GuildMemberUpdate}, and {@link Events.PresenceUpdate}.
*
* @event Client#userUpdate
* @param {User} oldUser The user before the update
* @param {User} newUser The user after the update

View File

@@ -9,6 +9,7 @@ class ClientVoiceManager {
constructor(client) {
/**
* The client that instantiated this voice manager
*
* @type {Client}
* @readonly
* @name ClientVoiceManager#client
@@ -17,6 +18,7 @@ class ClientVoiceManager {
/**
* Maps guild ids to voice adapters created for use with `@discordjs/voice`.
*
* @type {Map<Snowflake, Object>}
*/
this.adapters = new Map();

View File

@@ -7,6 +7,7 @@ module.exports = (client, { d: data }) => {
* Emitted whenever permissions for an application command in a guild were updated.
* <warn>This includes permission updates for other applications in addition to the logged in client,
* check `data.applicationId` to verify which application the update is for</warn>
*
* @event Client#applicationCommandPermissionsUpdate
* @param {ApplicationCommandPermissionsUpdateData} data The updated permissions
*/

View File

@@ -10,6 +10,7 @@ module.exports = (client, { d: data }) => {
/**
* Emitted whenever an auto moderation rule is triggered.
* <info>This event requires the {@link PermissionFlagsBits.ManageGuild} permission.</info>
*
* @event Client#autoModerationActionExecution
* @param {AutoModerationActionExecution} autoModerationActionExecution The data of the execution
*/

View File

@@ -11,6 +11,7 @@ module.exports = (client, { d: data }) => {
/**
* Emitted whenever an auto moderation rule is created.
* <info>This event requires the {@link PermissionFlagsBits.ManageGuild} permission.</info>
*
* @event Client#autoModerationRuleCreate
* @param {AutoModerationRule} autoModerationRule The created auto moderation rule
*/

View File

@@ -14,6 +14,7 @@ module.exports = (client, { d: data }) => {
/**
* Emitted whenever an auto moderation rule is deleted.
* <info>This event requires the {@link PermissionFlagsBits.ManageGuild} permission.</info>
*
* @event Client#autoModerationRuleDelete
* @param {AutoModerationRule} autoModerationRule The deleted auto moderation rule
*/

View File

@@ -12,6 +12,7 @@ module.exports = (client, { d: data }) => {
/**
* Emitted whenever an auto moderation rule gets updated.
* <info>This event requires the {@link PermissionFlagsBits.ManageGuild} permission.</info>
*
* @event Client#autoModerationRuleUpdate
* @param {?AutoModerationRule} oldAutoModerationRule The auto moderation rule before the update
* @param {AutoModerationRule} newAutoModerationRule The auto moderation rule after the update

View File

@@ -13,6 +13,7 @@ module.exports = (client, { d: data }) => {
/**
* Emitted whenever the pins of a channel are updated. Due to the nature of the WebSocket event,
* not much information can be provided easily here - you need to manually check the pins yourself.
*
* @event Client#channelPinsUpdate
* @param {TextBasedChannels} channel The channel that the pins update occurred in
* @param {Date} time The time of the pins update

View File

@@ -7,6 +7,7 @@ module.exports = (client, packet) => {
if (old && updated) {
/**
* Emitted whenever a channel is updated - e.g. name change, topic change, channel type change.
*
* @event Client#channelUpdate
* @param {DMChannel|GuildChannel} oldChannel The channel before the update
* @param {DMChannel|GuildChannel} newChannel The channel after the update

View File

@@ -7,6 +7,7 @@ module.exports = (client, { d: data }) => {
/**
* Emitted whenever an entitlement is created.
*
* @event Client#entitlementCreate
* @param {Entitlement} entitlement The entitlement that was created
*/

View File

@@ -11,6 +11,7 @@ module.exports = (client, { d: data }) => {
* Emitted whenever an entitlement is deleted.
* <warn>Entitlements are not deleted when they expire.
* This is only triggered when Discord issues a refund or deletes the entitlement manually.</warn>
*
* @event Client#entitlementDelete
* @param {Entitlement} entitlement The entitlement that was deleted
*/

View File

@@ -8,6 +8,7 @@ module.exports = (client, { d: data }) => {
/**
* Emitted whenever an entitlement is updated - i.e. when a user's subscription renews.
*
* @event Client#entitlementUpdate
* @param {?Entitlement} oldEntitlement The entitlement before the update
* @param {Entitlement} newEntitlement The entitlement after the update

View File

@@ -11,6 +11,7 @@ module.exports = (client, { d: data }) => {
/**
* Emitted whenever a guild audit log entry is created.
*
* @event Client#guildAuditLogEntryCreate
* @param {GuildAuditLogsEntry} auditLogEntry The entry that was created
* @param {Guild} guild The guild where the entry was created

View File

@@ -8,6 +8,7 @@ module.exports = (client, { d: data }) => {
/**
* Emitted whenever a member is banned from a guild.
*
* @event Client#guildBanAdd
* @param {GuildBan} ban The ban that occurred
*/

View File

@@ -13,6 +13,7 @@ module.exports = (client, { d: data }) => {
/**
* Emitted whenever a member is unbanned from a guild.
*
* @event Client#guildBanRemove
* @param {GuildBan} ban The ban that was removed
*/

View File

@@ -12,6 +12,7 @@ module.exports = (client, { d: data }, shardId) => {
/**
* Emitted whenever a guild becomes available.
*
* @event Client#guildAvailable
* @param {Guild} guild The guild that became available
*/
@@ -24,6 +25,7 @@ module.exports = (client, { d: data }, shardId) => {
if (client.status === Status.Ready) {
/**
* Emitted whenever the client joins a guild.
*
* @event Client#guildCreate
* @param {Guild} guild The created guild
*/

View File

@@ -11,6 +11,7 @@ module.exports = (client, { d: data }) => {
/**
* Emitted whenever a guild becomes unavailable, likely due to a server outage.
*
* @event Client#guildUnavailable
* @param {Guild} guild The guild that has become unavailable
*/
@@ -28,6 +29,7 @@ module.exports = (client, { d: data }) => {
/**
* Emitted whenever a guild kicks the client or the guild is deleted/left.
*
* @event Client#guildDelete
* @param {Guild} guild The guild that was deleted
*/

View File

@@ -8,6 +8,7 @@ module.exports = (client, { d: data }) => {
/**
* Emitted whenever a guild integration is updated
*
* @event Client#guildIntegrationsUpdate
* @param {Guild} guild The guild whose integrations were updated
*/

View File

@@ -15,6 +15,7 @@ module.exports = (client, { d: data }) => {
/**
* Represents the properties of a guild members chunk
*
* @typedef {Object} GuildMembersChunk
* @property {number} index Index of the received chunk
* @property {number} count Number of chunks the client should receive
@@ -25,6 +26,7 @@ module.exports = (client, { d: data }) => {
/**
* Emitted whenever a chunk of guild members is received (all members come from the same guild).
*
* @event Client#guildMembersChunk
* @param {Collection<Snowflake, GuildMember>} members The members in the chunk
* @param {Guild} guild The guild related to the member chunk

View File

@@ -9,6 +9,7 @@ module.exports = (client, { d: data }) => {
const member = guild.members._add(data);
/**
* Emitted whenever a user joins a guild.
*
* @event Client#guildMemberAdd
* @param {GuildMember} member The member that has joined a guild
*/

View File

@@ -13,6 +13,7 @@ module.exports = (client, { d: data }) => {
/**
* Emitted whenever a guild role is updated.
*
* @event Client#roleUpdate
* @param {Role} oldRole The role before the update
* @param {Role} newRole The role after the update

View File

@@ -10,6 +10,7 @@ module.exports = (client, { d: data }) => {
/**
* Emitted whenever a guild scheduled event is created.
*
* @event Client#guildScheduledEventCreate
* @param {GuildScheduledEvent} guildScheduledEvent The created guild scheduled event
*/

View File

@@ -11,6 +11,7 @@ module.exports = (client, { d: data }) => {
/**
* Emitted whenever a guild scheduled event gets updated.
*
* @event Client#guildScheduledEventUpdate
* @param {?GuildScheduledEvent} oldGuildScheduledEvent The guild scheduled event object before the update
* @param {GuildScheduledEvent} newGuildScheduledEvent The guild scheduled event object after the update

View File

@@ -16,6 +16,7 @@ module.exports = (client, { d: data }) => {
/**
* Emitted whenever multiple guild soundboard sounds are updated.
*
* @event Client#guildSoundboardSoundsUpdate
* @param {Collection<Snowflake, SoundboardSound>} soundboardSounds The updated soundboard sounds
* @param {Guild} guild The guild that the soundboard sounds are from

View File

@@ -11,6 +11,7 @@ module.exports = (client, { d: data }) => {
/**
* Emitted whenever a guild soundboard sound is created.
*
* @event Client#guildSoundboardSoundCreate
* @param {SoundboardSound} soundboardSound The created guild soundboard sound
*/

View File

@@ -12,6 +12,7 @@ module.exports = (client, { d: data }) => {
/**
* Emitted whenever a guild soundboard sound is updated.
*
* @event Client#guildSoundboardSoundUpdate
* @param {?SoundboardSound} oldGuildSoundboardSound The guild soundboard sound before the update
* @param {SoundboardSound} newGuildSoundboardSound The guild soundboard sound after the update

View File

@@ -13,6 +13,7 @@ module.exports = (client, { d: data }) => {
/**
* Emitted when an invite is created.
* <info>This event requires the {@link PermissionFlagsBits.ManageChannels} permission for the channel.</info>
*
* @event Client#inviteCreate
* @param {Invite} invite The invite that was created
*/

View File

@@ -16,6 +16,7 @@ module.exports = (client, { d: data }) => {
/**
* Emitted when an invite is deleted.
* <info>This event requires the {@link PermissionFlagsBits.ManageChannels} permission for the channel.</info>
*
* @event Client#inviteDelete
* @param {Invite} invite The invite that was deleted
*/

View File

@@ -7,6 +7,7 @@ module.exports = (client, packet) => {
if (old && updated) {
/**
* Emitted whenever a message is updated - e.g. embed or content change.
*
* @event Client#messageUpdate
* @param {Message} oldMessage The message before the update
* @param {Message} newMessage The message after the update

View File

@@ -8,11 +8,10 @@ module.exports = (client, { d: data }) => {
if (!user && ('username' in data.user || client.options.partials.includes(Partials.User))) {
user = client.users._add(data.user);
}
if (!user) return;
if (data.user.username) {
if (!user._equals(data.user)) client.actions.UserUpdate.handle(data.user);
}
if (data.user.username && !user._equals(data.user)) client.actions.UserUpdate.handle(data.user);
const guild = client.guilds.cache.get(data.guild_id);
if (!guild) return;
@@ -34,6 +33,7 @@ module.exports = (client, { d: data }) => {
if (client.listenerCount(Events.PresenceUpdate) > 0 && !newPresence.equals(oldPresence)) {
/**
* Emitted whenever a guild member's presence (e.g. status, activity) is changed.
*
* @event Client#presenceUpdate
* @param {?Presence} oldPresence The presence before the update, if one at all
* @param {Presence} newPresence The presence after the update

View File

@@ -1,6 +1,7 @@
'use strict';
const { ClientApplication } = require('../../../structures/ClientApplication.js');
let ClientUser;
module.exports = (client, { d: data }, shardId) => {

View File

@@ -16,6 +16,7 @@ module.exports = (client, { d: data }) => {
/**
* Emitted whenever soundboard sounds are received (all soundboard sounds come from the same guild).
*
* @event Client#soundboardSounds
* @param {Collection<Snowflake, SoundboardSound>} soundboardSounds The sounds received
* @param {Guild} guild The guild that the soundboard sounds are from

View File

@@ -7,6 +7,7 @@ module.exports = (client, { d: data }) => {
/**
* Emitted whenever a subscription is created.
*
* @event Client#subscriptionCreate
* @param {Subscription} subscription The subscription that was created
*/

View File

@@ -9,6 +9,7 @@ module.exports = (client, { d: data }) => {
/**
* Emitted whenever a subscription is deleted.
*
* @event Client#subscriptionDelete
* @param {Subscription} subscription The subscription that was deleted
*/

View File

@@ -8,6 +8,7 @@ module.exports = (client, { d: data }) => {
/**
* Emitted whenever a subscription is updated - i.e. when a user's subscription renews.
*
* @event Client#subscriptionUpdate
* @param {?Subscription} oldSubscription The subscription before the update
* @param {Subscription} newSubscription The subscription after the update

View File

@@ -10,6 +10,7 @@ module.exports = (client, { d: data }) => {
/**
* Emitted whenever a thread is deleted.
*
* @event Client#threadDelete
* @param {ThreadChannel} thread The thread that was deleted
*/

View File

@@ -33,6 +33,7 @@ module.exports = (client, { d: data }) => {
/**
* Emitted whenever the client user gains access to a text or announcement channel that contains threads
*
* @event Client#threadListSync
* @param {Collection<Snowflake, ThreadChannel>} threads The threads that were synced
* @param {Guild} guild The guild that the threads were synced in
@@ -41,9 +42,11 @@ module.exports = (client, { d: data }) => {
};
function removeStaleThreads(client, channel) {
channel.threads?.cache.forEach(thread => {
if (!channel.threads) return;
for (const thread of channel.threads.cache.values()) {
if (!thread.archived) {
client.channels._remove(thread.id);
}
});
}
}

View File

@@ -17,6 +17,7 @@ module.exports = (client, { d: data }) => {
/**
* Emitted whenever the client user's thread member is updated.
*
* @event Client#threadMemberUpdate
* @param {ThreadMember} oldMember The member before the update
* @param {ThreadMember} newMember The member after the update

View File

@@ -7,6 +7,7 @@ module.exports = (client, packet) => {
if (old && updated) {
/**
* Emitted whenever a thread is updated - e.g. name change, archive state change, locked state change.
*
* @event Client#threadUpdate
* @param {ThreadChannel} oldThread The thread before the update
* @param {ThreadChannel} newThread The thread after the update

View File

@@ -9,6 +9,7 @@ module.exports = (client, { d: data }) => {
/**
* Emitted when someone sends an effect, such as an emoji reaction, in a voice channel the client is connected to.
*
* @event Client#voiceChannelEffectSend
* @param {VoiceChannelEffect} voiceChannelEffect The sent voice channel effect
*/

View File

@@ -29,6 +29,7 @@ module.exports = (client, { d: data }) => {
/**
* Emitted whenever a member changes voice state - e.g. joins/leaves a channel, mutes/unmutes.
*
* @event Client#voiceStateUpdate
* @param {VoiceState} oldState The voice state before the update
* @param {VoiceState} newState The voice state after the update

View File

@@ -8,6 +8,7 @@ module.exports = (client, { d: data }) => {
/**
* Emitted whenever a channel has its webhooks changed.
*
* @event Client#webhooksUpdate
* @param {TextChannel|AnnouncementChannel|VoiceChannel|StageChannel|ForumChannel|MediaChannel} channel
* The channel that had a webhook update

View File

@@ -6,6 +6,7 @@ const { Messages } = require('./Messages.js');
/**
* Extend an error of some sort into a DiscordjsError.
*
* @param {Error} Base Base error to extend
* @returns {DiscordjsError}
* @ignore
@@ -26,6 +27,7 @@ function makeDiscordjsError(Base) {
/**
* Format the message for an error.
*
* @param {string} code The error code
* @param {Array<*>} args Arguments to pass for util format or as function args
* @returns {string} Formatted string

View File

@@ -1,19 +1,19 @@
/* eslint-disable jsdoc/tag-lines, jsdoc/require-property-description */
'use strict';
/**
* @typedef {Object} DiscordjsErrorCodes
* @property {'ClientInvalidOption'} ClientInvalidOption
* @property {'ClientInvalidProvidedShards'} ClientInvalidProvidedShards
* @property {'ClientMissingIntents'} ClientMissingIntents
* @property {'ClientNotReady'} ClientNotReady
*
* @property {'TokenInvalid'} TokenInvalid
* @property {'TokenMissing'} TokenMissing
* @property {'ApplicationCommandPermissionsTokenMissing'} ApplicationCommandPermissionsTokenMissing
*
* @property {'BitFieldInvalid'} BitFieldInvalid
*
* @property {'ShardingNoShards'} ShardingNoShards
* @property {'ShardingInProcess'} ShardingInProcess
* @property {'ShardingInvalidEvalBroadcast'} ShardingInvalidEvalBroadcast
@@ -26,35 +26,35 @@
* @property {'ShardingReadyDied'} ShardingReadyDied
* @property {'ShardingNoChildExists'} ShardingNoChildExists
* @property {'ShardingShardMiscalculation'} ShardingShardMiscalculation
*
* @property {'ColorRange'} ColorRange
* @property {'ColorConvert'} ColorConvert
*
* @property {'InviteOptionsMissingChannel'} InviteOptionsMissingChannel
*
* @property {'InteractionCollectorError'} InteractionCollectorError
*
* @property {'FileNotFound'} FileNotFound
*
* @property {'UserNoDMChannel'} UserNoDMChannel
*
* @property {'VoiceNotStageChannel'} VoiceNotStageChannel
*
* @property {'VoiceStateNotOwn'} VoiceStateNotOwn
* @property {'VoiceStateInvalidType'} VoiceStateInvalidType
*
* @property {'ReqResourceType'} ReqResourceType
*
* @property {'MessageBulkDeleteType'} MessageBulkDeleteType
* @property {'MessageContentType'} MessageContentType
* @property {'MessageNonceRequired'} MessageNonceRequired
* @property {'MessageNonceType'} MessageNonceType
*
* @property {'BanResolveId'} BanResolveId
* @property {'FetchBanResolveId'} FetchBanResolveId
*
* @property {'PruneDaysType'} PruneDaysType
*
* @property {'GuildChannelResolve'} GuildChannelResolve
* @property {'GuildVoiceChannelResolve'} GuildVoiceChannelResolve
* @property {'GuildChannelOrphan'} GuildChannelOrphan
@@ -67,46 +67,45 @@
* @property {'StageChannelResolve'} StageChannelResolve
* @property {'GuildScheduledEventResolve'} GuildScheduledEventResolve
* @property {'FetchOwnerId'} FetchOwnerId
*
* @property {'InvalidType'} InvalidType
* @property {'InvalidElement'} InvalidElement
*
* @property {'MessageThreadParent'} MessageThreadParent
* @property {'MessageExistingThread'} MessageExistingThread
* @property {'ThreadInvitableType'} ThreadInvitableType
* @property {'NotAThreadOfParent'} NotAThreadOfParent
*
* @property {'WebhookMessage'} WebhookMessage
* @property {'WebhookTokenUnavailable'} WebhookTokenUnavailable
* @property {'WebhookURLInvalid'} WebhookURLInvalid
* @property {'WebhookApplication'} WebhookApplication
*
* @property {'MessageReferenceMissing'} MessageReferenceMissing
*
* @property {'EmojiType'} EmojiType
* @property {'EmojiManaged'} EmojiManaged
* @property {'MissingManageGuildExpressionsPermission'} MissingManageGuildExpressionsPermission
*
* @property {'NotGuildSoundboardSound'} NotGuildSoundboardSound
* @property {'NotGuildSticker'} NotGuildSticker
*
* @property {'ReactionResolveUser'} ReactionResolveUser
*
* @property {'InviteResolveCode'} InviteResolveCode
* @property {'InviteNotFound'} InviteNotFound
*
* @property {'DeleteGroupDMChannel'} DeleteGroupDMChannel
* @property {'FetchGroupDMChannel'} FetchGroupDMChannel
*
* @property {'MemberFetchNonceLength'} MemberFetchNonceLength
*
* @property {'GlobalCommandPermissions'} GlobalCommandPermissions
* @property {'GuildUncachedEntityResolve'} GuildUncachedEntityResolve
*
* @property {'InteractionAlreadyReplied'} InteractionAlreadyReplied
* @property {'InteractionNotReplied'} InteractionNotReplied
*
* @property {'CommandInteractionOptionNotFound'} CommandInteractionOptionNotFound
* @property {'CommandInteractionOptionType'} CommandInteractionOptionType
* @property {'CommandInteractionOptionEmpty'} CommandInteractionOptionEmpty
@@ -114,25 +113,25 @@
* @property {'CommandInteractionOptionNoSubcommandGroup'} CommandInteractionOptionNoSubcommandGroup
* @property {'CommandInteractionOptionInvalidChannelType'} CommandInteractionOptionInvalidChannelType
* @property {'AutocompleteInteractionOptionNoFocusedOption'} AutocompleteInteractionOptionNoFocusedOption
*
* @property {'ModalSubmitInteractionFieldNotFound'} ModalSubmitInteractionFieldNotFound
* @property {'ModalSubmitInteractionFieldType'} ModalSubmitInteractionFieldType
*
* @property {'InvalidMissingScopes'} InvalidMissingScopes
* @property {'InvalidScopesWithPermissions'} InvalidScopesWithPermissions
*
* @property {'NotImplemented'} NotImplemented
*
* @property {'GuildForumMessageRequired'} GuildForumMessageRequired
*
* @property {'SweepFilterReturn'} SweepFilterReturn
*
* @property {'EntitlementCreateInvalidOwner'} EntitlementCreateInvalidOwner
*
* @property {'BulkBanUsersOptionEmpty'} BulkBanUsersOptionEmpty
*
* @property {'PollAlreadyExpired'} PollAlreadyExpired
*
* @property {'PermissionOverwritesTypeMandatory'} PermissionOverwritesTypeMandatory
* @property {'PermissionOverwritesTypeMismatch'} PermissionOverwritesTypeMismatch
*/
@@ -215,6 +214,7 @@ const keys = [
'WebhookTokenUnavailable',
'WebhookURLInvalid',
'WebhookApplication',
'MessageReferenceMissing',
'EmojiType',
@@ -227,7 +227,6 @@ const keys = [
'ReactionResolveUser',
'InviteResolveCode',
'InviteNotFound',
'DeleteGroupDMChannel',

View File

@@ -85,6 +85,7 @@ const Messages = {
[ErrorCodes.WebhookTokenUnavailable]: 'This action requires a webhook token, but none is available.',
[ErrorCodes.WebhookURLInvalid]: 'The provided webhook URL is not valid.',
[ErrorCodes.WebhookApplication]: 'This message webhook belongs to an application and cannot be fetched.',
[ErrorCodes.MessageReferenceMissing]: 'The message does not reference another message',
[ErrorCodes.EmojiType]: 'Emoji must be a string or GuildEmoji/ReactionEmoji',
@@ -99,7 +100,6 @@ const Messages = {
[ErrorCodes.ReactionResolveUser]: "Couldn't resolve the user id to remove from the reaction.",
[ErrorCodes.InviteResolveCode]: 'Could not resolve the code to fetch the invite.',
[ErrorCodes.InviteNotFound]: 'Could not find the requested invite.',
[ErrorCodes.DeleteGroupDMChannel]: "Bots don't have access to Group DM Channels and cannot delete them",

View File

@@ -4,14 +4,15 @@ const { Collection } = require('@discordjs/collection');
const { makeURLSearchParams } = require('@discordjs/rest');
const { isJSONEncodable } = require('@discordjs/util');
const { Routes } = require('discord-api-types/v10');
const { ApplicationCommandPermissionsManager } = require('./ApplicationCommandPermissionsManager.js');
const { CachedManager } = require('./CachedManager.js');
const { DiscordjsTypeError, ErrorCodes } = require('../errors/index.js');
const { ApplicationCommand } = require('../structures/ApplicationCommand.js');
const { PermissionsBitField } = require('../util/PermissionsBitField.js');
const { ApplicationCommandPermissionsManager } = require('./ApplicationCommandPermissionsManager.js');
const { CachedManager } = require('./CachedManager.js');
/**
* Manages API methods for application commands and stores their cache.
*
* @extends {CachedManager}
*/
class ApplicationCommandManager extends CachedManager {
@@ -20,6 +21,7 @@ class ApplicationCommandManager extends CachedManager {
/**
* The manager for permissions of arbitrary commands on arbitrary guilds
*
* @type {ApplicationCommandPermissionsManager}
*/
this.permissions = new ApplicationCommandPermissionsManager(this);
@@ -27,6 +29,7 @@ class ApplicationCommandManager extends CachedManager {
/**
* The cache of this manager
*
* @type {Collection<Snowflake, ApplicationCommand>}
* @name ApplicationCommandManager#cache
*/
@@ -37,6 +40,8 @@ class ApplicationCommandManager extends CachedManager {
/**
* The APIRouter path to the commands
*
* @param {Object} [options] The options
* @param {Snowflake} [options.id] The application command's id
* @param {Snowflake} [options.guildId] The guild's id to use in the path,
* ignored when using a {@link GuildApplicationCommandManager}
@@ -61,18 +66,21 @@ class ApplicationCommandManager extends CachedManager {
/**
* Data that resolves to give an ApplicationCommand object. This can be:
* * An ApplicationCommand object
* * A Snowflake
* - An ApplicationCommand object
* - A Snowflake
*
* @typedef {ApplicationCommand|Snowflake} ApplicationCommandResolvable
*/
/**
* Data that resolves to the data of an ApplicationCommand
*
* @typedef {ApplicationCommandData|APIApplicationCommand} ApplicationCommandDataResolvable
*/
/**
* Options used to fetch data from Discord
*
* @typedef {Object} BaseFetchOptions
* @property {boolean} [cache=true] Whether to cache the fetched data if it wasn't already
* @property {boolean} [force=false] Whether to skip the cache check and request the API
@@ -80,6 +88,7 @@ class ApplicationCommandManager extends CachedManager {
/**
* Options used to fetch Application Commands from Discord
*
* @typedef {BaseFetchOptions} FetchApplicationCommandOptions
* @property {Snowflake} [id] The command's id to fetch
* @property {Snowflake} [guildId] The guild's id to fetch commands for, for when the guild is not cached
@@ -89,6 +98,7 @@ class ApplicationCommandManager extends CachedManager {
/**
* Obtains one or multiple application commands from Discord, or the cache if it's already available.
*
* @param {Snowflake|FetchApplicationCommandOptions} [options] Options for fetching application command(s)
* @returns {Promise<ApplicationCommand|Collection<Snowflake, ApplicationCommand>>}
* @example
@@ -147,6 +157,7 @@ class ApplicationCommandManager extends CachedManager {
/**
* Creates an application command.
*
* @param {ApplicationCommandDataResolvable} command The command
* @param {Snowflake} [guildId] The guild's id to create this command in,
* ignored when using a {@link GuildApplicationCommandManager}
@@ -169,6 +180,7 @@ class ApplicationCommandManager extends CachedManager {
/**
* Sets all the commands for this application or guild.
*
* @param {ApplicationCommandDataResolvable[]} commands The commands
* @param {Snowflake} [guildId] The guild's id to create the commands in,
* ignored when using a {@link GuildApplicationCommandManager}
@@ -201,6 +213,7 @@ class ApplicationCommandManager extends CachedManager {
/**
* Edits an application command.
*
* @param {ApplicationCommandResolvable} command The command to edit
* @param {Partial<ApplicationCommandDataResolvable>} data The data to update the command with
* @param {Snowflake} [guildId] The guild's id where the command registered,
@@ -226,6 +239,7 @@ class ApplicationCommandManager extends CachedManager {
/**
* Deletes an application command.
*
* @param {ApplicationCommandResolvable} command The command to delete
* @param {Snowflake} [guildId] The guild's id where the command is registered,
* ignored when using a {@link GuildApplicationCommandManager}
@@ -249,6 +263,7 @@ class ApplicationCommandManager extends CachedManager {
/**
* Transforms an {@link ApplicationCommandData} object into something that can be used with the API.
*
* @param {ApplicationCommandDataResolvable} command The command to transform
* @returns {APIApplicationCommand}
* @private
@@ -266,9 +281,9 @@ class ApplicationCommandManager extends CachedManager {
if ('defaultMemberPermissions' in command) {
default_member_permissions =
command.defaultMemberPermissions !== null
? new PermissionsBitField(command.defaultMemberPermissions).bitfield.toString()
: command.defaultMemberPermissions;
command.defaultMemberPermissions === null
? command.defaultMemberPermissions
: new PermissionsBitField(command.defaultMemberPermissions).bitfield.toString();
}
return {

View File

@@ -2,11 +2,12 @@
const { Collection } = require('@discordjs/collection');
const { ApplicationCommandPermissionType, RESTJSONErrorCodes, Routes } = require('discord-api-types/v10');
const { BaseManager } = require('./BaseManager.js');
const { DiscordjsError, DiscordjsTypeError, ErrorCodes } = require('../errors/index.js');
const { BaseManager } = require('./BaseManager.js');
/**
* Manages API methods for permissions of Application Commands.
*
* @extends {BaseManager}
*/
class ApplicationCommandPermissionsManager extends BaseManager {
@@ -15,6 +16,7 @@ class ApplicationCommandPermissionsManager extends BaseManager {
/**
* The manager or command that this manager belongs to
*
* @type {ApplicationCommandManager|ApplicationCommand}
* @private
*/
@@ -22,18 +24,21 @@ class ApplicationCommandPermissionsManager extends BaseManager {
/**
* The guild that this manager acts on
*
* @type {?Guild}
*/
this.guild = manager.guild ?? null;
/**
* The id of the guild that this manager acts on
*
* @type {?Snowflake}
*/
this.guildId = manager.guildId ?? manager.guild?.id ?? null;
/**
* The id of the command this manager acts on
*
* @type {?Snowflake}
*/
this.commandId = manager.id ?? null;
@@ -41,6 +46,7 @@ class ApplicationCommandPermissionsManager extends BaseManager {
/**
* The APIRouter path to the commands
*
* @param {Snowflake} guildId The guild's id to use in the path,
* @param {Snowflake} [commandId] The application command's id
* @returns {string}
@@ -54,21 +60,21 @@ class ApplicationCommandPermissionsManager extends BaseManager {
return Routes.guildApplicationCommandsPermissions(this.client.application.id, guildId);
}
/* eslint-disable max-len */
/**
* The object returned when fetching permissions for an application command.
*
* @typedef {Object} ApplicationCommandPermissions
* @property {Snowflake} id The role, user, or channel's id. Can also be a
* {@link https://discord.com/developers/docs/interactions/application-commands#application-command-permissions-object-application-command-permissions-constants permission constant}.
* @property {ApplicationCommandPermissionType} type Whether this permission is for a role or a user
* @property {boolean} permission Whether the role or user has the permission to use this command
*/
/* eslint-enable max-len */
/**
* Options for managing permissions for one or more Application Commands
* <warn>When passing these options to a manager where `guildId` is `null`,
* `guild` is a required parameter</warn>
*
* @typedef {Object} BaseApplicationCommandPermissionsOptions
* @property {GuildResolvable} [guild] The guild to modify / check permissions for
* <warn>Ignored when the manager has a non-null `guildId` property</warn>
@@ -78,7 +84,8 @@ class ApplicationCommandPermissionsManager extends BaseManager {
/**
* Fetches the permissions for one or multiple commands. Providing the client's id as the "command id" will fetch
* *only* the guild level permissions
* _only_ the guild level permissions
*
* @param {BaseApplicationCommandPermissionsOptions} [options] Options used to fetch permissions
* @returns {Promise<ApplicationCommandPermissions[]|Collection<Snowflake, ApplicationCommandPermissions[]>>}
* @example
@@ -100,8 +107,8 @@ class ApplicationCommandPermissionsManager extends BaseManager {
async fetch({ guild, command } = {}) {
const { guildId, commandId } = this._validateOptions(guild, command);
if (commandId) {
const data = await this.client.rest.get(this.permissionsPath(guildId, commandId));
return data.permissions;
const innerData = await this.client.rest.get(this.permissionsPath(guildId, commandId));
return innerData.permissions;
}
const data = await this.client.rest.get(this.permissionsPath(guildId));
@@ -112,6 +119,7 @@ class ApplicationCommandPermissionsManager extends BaseManager {
* Options used to set permissions for one or more Application Commands in a guild
* <warn>Omitting the `command` parameter edits the guild wide permissions
* when the manager's `commandId` is `null`</warn>
*
* @typedef {BaseApplicationCommandPermissionsOptions} ApplicationCommandPermissionsEditOptions
* @property {ApplicationCommandPermissions[]} permissions The new permissions for the guild or overwrite
* @property {string} token The bearer token to use that authorizes the permission edit
@@ -119,6 +127,7 @@ class ApplicationCommandPermissionsManager extends BaseManager {
/**
* Sets the permissions for the guild or a command overwrite.
*
* @param {ApplicationCommandPermissionsEditOptions} options Options used to set permissions
* @returns {Promise<ApplicationCommandPermissions[]|Collection<Snowflake, ApplicationCommandPermissions[]>>}
* @example
@@ -155,7 +164,9 @@ class ApplicationCommandPermissionsManager extends BaseManager {
if (!token) {
throw new DiscordjsError(ErrorCodes.ApplicationCommandPermissionsTokenMissing);
}
let { guildId, commandId } = this._validateOptions(guild, command);
const options = this._validateOptions(guild, command);
let { commandId } = options;
if (!Array.isArray(permissions)) {
throw new DiscordjsTypeError(
@@ -166,10 +177,9 @@ class ApplicationCommandPermissionsManager extends BaseManager {
);
}
if (!commandId) {
commandId = this.client.user.id;
}
const data = await this.client.rest.put(this.permissionsPath(guildId, commandId), {
commandId ??= this.client.user.id;
const data = await this.client.rest.put(this.permissionsPath(options.guildId, commandId), {
body: { permissions },
auth: false,
headers: { Authorization: `Bearer ${token}` },
@@ -179,6 +189,7 @@ class ApplicationCommandPermissionsManager extends BaseManager {
/**
* Add permissions to a command.
*
* @param {ApplicationCommandPermissionsEditOptions} options Options used to add permissions
* @returns {Promise<ApplicationCommandPermissions[]>}
* @example
@@ -197,10 +208,11 @@ class ApplicationCommandPermissionsManager extends BaseManager {
if (!token) {
throw new DiscordjsError(ErrorCodes.ApplicationCommandPermissionsTokenMissing);
}
let { guildId, commandId } = this._validateOptions(guild, command);
if (!commandId) {
commandId = this.client.user.id;
}
const options = this._validateOptions(guild, command);
let { commandId } = options;
commandId ??= this.client.user.id;
if (!Array.isArray(permissions)) {
throw new DiscordjsTypeError(
ErrorCodes.InvalidType,
@@ -212,7 +224,7 @@ class ApplicationCommandPermissionsManager extends BaseManager {
let existingPermissions = [];
try {
existingPermissions = await this.fetch({ guild: guildId, command: commandId });
existingPermissions = await this.fetch({ guild: options.guildId, command: commandId });
} catch (error) {
if (error.code !== RESTJSONErrorCodes.UnknownApplicationCommandPermissions) throw error;
}
@@ -224,18 +236,20 @@ class ApplicationCommandPermissionsManager extends BaseManager {
}
}
return this.set({ guild: guildId, command: commandId, permissions: newPermissions, token });
return this.set({ guild: options.guildId, command: commandId, permissions: newPermissions, token });
}
/**
* A static snowflake that identifies the everyone role for application command permissions.
* It is the same as the guild id
*
* @typedef {Snowflake} RolePermissionConstant
*/
/**
* A static snowflake that identifies the "all channels" entity for application command permissions.
* It will be the result of the calculation `guildId - 1`
*
* @typedef {Snowflake} ChannelPermissionConstant
*/
@@ -244,6 +258,7 @@ class ApplicationCommandPermissionsManager extends BaseManager {
* <warn>Omitting the `command` parameter removes from the guild wide permissions
* when the managers `commandId` is `null`</warn>
* <warn>At least one of `users`, `roles`, and `channels` is required</warn>
*
* @typedef {BaseApplicationCommandPermissionsOptions} RemoveApplicationCommandPermissionsOptions
* @property {string} token The bearer token to use that authorizes the permission removal
* @property {UserResolvable[]} [users] The user(s) to remove
@@ -253,6 +268,7 @@ class ApplicationCommandPermissionsManager extends BaseManager {
/**
* Remove permissions from a command.
*
* @param {RemoveApplicationCommandPermissionsOptions} options Options used to remove permissions
* @returns {Promise<ApplicationCommandPermissions[]>}
* @example
@@ -274,16 +290,16 @@ class ApplicationCommandPermissionsManager extends BaseManager {
if (!token) {
throw new DiscordjsError(ErrorCodes.ApplicationCommandPermissionsTokenMissing);
}
let { guildId, commandId } = this._validateOptions(guild, command);
if (!commandId) {
commandId = this.client.user.id;
}
const options = this._validateOptions(guild, command);
let { commandId } = options;
commandId ??= this.client.user.id;
if (!users && !roles && !channels) {
throw new DiscordjsTypeError(ErrorCodes.InvalidType, 'users OR roles OR channels', 'Array or Resolvable', true);
}
let resolvedUserIds = [];
const resolvedUserIds = [];
if (Array.isArray(users)) {
for (const user of users) {
const userId = this.client.users.resolveId(user);
@@ -292,13 +308,14 @@ class ApplicationCommandPermissionsManager extends BaseManager {
}
}
let resolvedRoleIds = [];
const resolvedRoleIds = [];
if (Array.isArray(roles)) {
for (const role of roles) {
if (typeof role === 'string') {
resolvedRoleIds.push(role);
continue;
}
if (!this.guild) throw new DiscordjsError(ErrorCodes.GuildUncachedEntityResolve, 'roles');
const roleId = this.guild.roles.resolveId(role);
if (!roleId) throw new DiscordjsTypeError(ErrorCodes.InvalidElement, 'Array', 'users', role);
@@ -306,13 +323,14 @@ class ApplicationCommandPermissionsManager extends BaseManager {
}
}
let resolvedChannelIds = [];
const resolvedChannelIds = [];
if (Array.isArray(channels)) {
for (const channel of channels) {
if (typeof channel === 'string') {
resolvedChannelIds.push(channel);
continue;
}
if (!this.guild) throw new DiscordjsError(ErrorCodes.GuildUncachedEntityResolve, 'channels');
const channelId = this.guild.channels.resolveId(channel);
if (!channelId) throw new DiscordjsTypeError(ErrorCodes.InvalidElement, 'Array', 'channels', channel);
@@ -322,7 +340,7 @@ class ApplicationCommandPermissionsManager extends BaseManager {
let existing = [];
try {
existing = await this.fetch({ guild: guildId, command: commandId });
existing = await this.fetch({ guild: options.guildId, command: commandId });
} catch (error) {
if (error.code !== RESTJSONErrorCodes.UnknownApplicationCommandPermissions) throw error;
}
@@ -335,16 +353,18 @@ class ApplicationCommandPermissionsManager extends BaseManager {
return !resolvedUserIds.includes(perm.id);
case ApplicationCommandPermissionType.Channel:
return !resolvedChannelIds.includes(perm.id);
}
default:
return true;
}
});
return this.set({ guild: guildId, command: commandId, permissions, token });
return this.set({ guild: options.guildId, command: commandId, permissions, token });
}
/**
* Options used to check the existence of permissions on a command
* <warn>The `command` parameter is not optional when the managers `commandId` is `null`</warn>
*
* @typedef {BaseApplicationCommandPermissionsOptions} HasApplicationCommandPermissionsOptions
* @property {ApplicationCommandPermissionIdResolvable} permissionId The entity to check if a permission exists for
* on this command.
@@ -353,6 +373,7 @@ class ApplicationCommandPermissionsManager extends BaseManager {
/**
* Check whether a permission exists for a user, role, or channel
*
* @param {HasApplicationCommandPermissionsOptions} options Options used to check permissions
* @returns {Promise<boolean>}
* @example
@@ -371,6 +392,7 @@ class ApplicationCommandPermissionsManager extends BaseManager {
'UserResolvable, RoleResolvable, ChannelResolvable, or Permission Constant',
);
}
let resolvedId = permissionId;
if (typeof permissionId !== 'string') {
resolvedId = this.client.users.resolveId(permissionId);
@@ -378,9 +400,9 @@ class ApplicationCommandPermissionsManager extends BaseManager {
if (!this.guild) throw new DiscordjsError(ErrorCodes.GuildUncachedEntityResolve, 'roles');
resolvedId = this.guild.roles.resolveId(permissionId);
}
if (!resolvedId) {
resolvedId = this.guild.channels.resolveId(permissionId);
}
resolvedId ??= this.guild.channels.resolveId(permissionId);
if (!resolvedId) {
throw new DiscordjsTypeError(
ErrorCodes.InvalidType,
@@ -410,19 +432,21 @@ class ApplicationCommandPermissionsManager extends BaseManager {
if (!commandId && this.guild) {
commandId = this.guild.commands.resolveId(command);
}
commandId ??= this.client.application?.commands.resolveId(command);
if (!commandId) {
throw new DiscordjsTypeError(ErrorCodes.InvalidType, 'command', 'ApplicationCommandResolvable', true);
}
}
return { guildId, commandId };
}
}
exports.ApplicationCommandPermissionsManager = ApplicationCommandPermissionsManager;
/* eslint-disable max-len */
/**
* Data that resolves to an id used for an application command permission
*
* @typedef {UserResolvable|RoleResolvable|GuildChannelResolvable|RolePermissionConstant|ChannelPermissionConstant} ApplicationCommandPermissionIdResolvable
*/

View File

@@ -2,13 +2,14 @@
const { Collection } = require('@discordjs/collection');
const { Routes } = require('discord-api-types/v10');
const { CachedManager } = require('./CachedManager.js');
const { DiscordjsTypeError, ErrorCodes } = require('../errors/index.js');
const { ApplicationEmoji } = require('../structures/ApplicationEmoji.js');
const { resolveImage } = require('../util/DataResolver.js');
const { CachedManager } = require('./CachedManager.js');
/**
* Manages API methods for ApplicationEmojis and stores their cache.
*
* @extends {CachedManager}
*/
class ApplicationEmojiManager extends CachedManager {
@@ -17,6 +18,7 @@ class ApplicationEmojiManager extends CachedManager {
/**
* The application this manager belongs to
*
* @type {ClientApplication}
*/
this.application = application;
@@ -28,6 +30,7 @@ class ApplicationEmojiManager extends CachedManager {
/**
* Options used for creating an emoji of the application
*
* @typedef {Object} ApplicationEmojiCreateOptions
* @property {BufferResolvable|Base64Resolvable} attachment The image for the emoji
* @property {string} name The name for the emoji
@@ -35,6 +38,7 @@ class ApplicationEmojiManager extends CachedManager {
/**
* Creates a new custom emoji of the application.
*
* @param {ApplicationEmojiCreateOptions} options Options for creating the emoji
* @returns {Promise<Emoji>} The created emoji
* @example
@@ -60,6 +64,7 @@ class ApplicationEmojiManager extends CachedManager {
/**
* Obtains one or more emojis from Discord, or the emoji cache if they're already available.
*
* @param {Snowflake} [id] The emoji's id
* @param {BaseFetchOptions} [options] Additional options for this fetch
* @returns {Promise<ApplicationEmoji|Collection<Snowflake, ApplicationEmoji>>}
@@ -80,6 +85,7 @@ class ApplicationEmojiManager extends CachedManager {
const existing = this.cache.get(id);
if (existing) return existing;
}
const emoji = await this.client.rest.get(Routes.applicationEmoji(this.application.id, id));
return this._add(emoji, cache);
}
@@ -92,6 +98,7 @@ class ApplicationEmojiManager extends CachedManager {
/**
* Deletes an emoji.
*
* @param {EmojiResolvable} emoji The Emoji resolvable to delete
* @returns {Promise<void>}
*/
@@ -103,6 +110,7 @@ class ApplicationEmojiManager extends CachedManager {
/**
* Edits an emoji.
*
* @param {EmojiResolvable} emoji The Emoji resolvable to edit
* @param {ApplicationEmojiEditOptions} options The options to provide
* @returns {Promise<ApplicationEmoji>}
@@ -121,11 +129,13 @@ class ApplicationEmojiManager extends CachedManager {
existing._patch(newData);
return existing;
}
return this._add(newData);
}
/**
* Fetches the author for this emoji
*
* @param {EmojiResolvable} emoji The emoji to fetch the author of
* @returns {Promise<User>}
*/

View File

@@ -2,11 +2,12 @@
const { Collection } = require('@discordjs/collection');
const { Routes } = require('discord-api-types/v10');
const { CachedManager } = require('./CachedManager.js');
const { AutoModerationRule } = require('../structures/AutoModerationRule.js');
const { CachedManager } = require('./CachedManager.js');
/**
* Manages API methods for auto moderation rules and stores their cache.
*
* @extends {CachedManager}
*/
class AutoModerationRuleManager extends CachedManager {
@@ -15,6 +16,7 @@ class AutoModerationRuleManager extends CachedManager {
/**
* The guild this manager belongs to.
*
* @type {Guild}
*/
this.guild = guild;
@@ -22,12 +24,14 @@ class AutoModerationRuleManager extends CachedManager {
/**
* The cache of this manager
*
* @type {Collection<Snowflake, AutoModerationRule>}
* @name AutoModerationRuleManager#cache
*/
/**
* Resolves an {@link AutoModerationRuleResolvable} to an {@link AutoModerationRule} object.
*
* @method resolve
* @memberof AutoModerationRuleManager
* @instance
@@ -37,6 +41,7 @@ class AutoModerationRuleManager extends CachedManager {
/**
* Resolves an {@link AutoModerationRuleResolvable} to a {@link AutoModerationRule} id.
*
* @method resolveId
* @memberof AutoModerationRuleManager
* @instance
@@ -50,6 +55,7 @@ class AutoModerationRuleManager extends CachedManager {
/**
* Options used to set the trigger metadata of an auto moderation rule.
*
* @typedef {Object} AutoModerationTriggerMetadataOptions
* @property {string[]} [keywordFilter] The substrings that will be searched for in the content
* @property {string[]} [regexPatterns] The regular expression patterns which will be matched against the content
@@ -66,6 +72,7 @@ class AutoModerationRuleManager extends CachedManager {
/**
* Options used to set the actions of an auto moderation rule.
*
* @typedef {Object} AutoModerationActionOptions
* @property {AutoModerationActionType} type The type of this auto moderation rule action
* @property {AutoModerationActionMetadataOptions} [metadata] Additional metadata needed during execution
@@ -75,6 +82,7 @@ class AutoModerationRuleManager extends CachedManager {
/**
* Options used to set the metadata of an auto moderation rule action.
*
* @typedef {Object} AutoModerationActionMetadataOptions
* @property {GuildTextChannelResolvable|ThreadChannel} [channel] The channel to which content will be logged
* @property {number} [durationSeconds] The timeout duration in seconds
@@ -83,6 +91,7 @@ class AutoModerationRuleManager extends CachedManager {
/**
* Options used to create an auto moderation rule.
*
* @typedef {Object} AutoModerationRuleCreateOptions
* @property {string} name The name of the auto moderation rule
* @property {AutoModerationRuleEventType} eventType The event type of the auto moderation rule
@@ -105,6 +114,7 @@ class AutoModerationRuleManager extends CachedManager {
/**
* Creates a new auto moderation rule.
*
* @param {AutoModerationRuleCreateOptions} options Options for creating the auto moderation rule
* @returns {Promise<AutoModerationRule>}
*/
@@ -152,6 +162,7 @@ class AutoModerationRuleManager extends CachedManager {
/**
* Options used to edit an auto moderation rule.
*
* @typedef {Object} AutoModerationRuleEditOptions
* @property {string} [name] The name of the auto moderation rule
* @property {AutoModerationRuleEventType} [eventType] The event type of the auto moderation rule
@@ -168,6 +179,7 @@ class AutoModerationRuleManager extends CachedManager {
/**
* Edits an auto moderation rule.
*
* @param {AutoModerationRuleResolvable} autoModerationRule The auto moderation rule to edit
* @param {AutoModerationRuleEditOptions} options Options for editing the auto moderation rule
* @returns {Promise<AutoModerationRule>}
@@ -210,25 +222,29 @@ class AutoModerationRuleManager extends CachedManager {
/**
* Data that can be resolved to give an AutoModerationRule object. This can be:
* * An AutoModerationRule
* * A Snowflake
* - An AutoModerationRule
* - A Snowflake
*
* @typedef {AutoModerationRule|Snowflake} AutoModerationRuleResolvable
*/
/**
* Options used to fetch a single auto moderation rule from a guild.
*
* @typedef {BaseFetchOptions} FetchAutoModerationRuleOptions
* @property {AutoModerationRuleResolvable} autoModerationRule The auto moderation rule to fetch
*/
/**
* Options used to fetch all auto moderation rules from a guild.
*
* @typedef {Object} FetchAutoModerationRulesOptions
* @property {boolean} [cache] Whether to cache the fetched auto moderation rules
*/
/**
* Fetches auto moderation rules from Discord.
*
* @param {AutoModerationRuleResolvable|FetchAutoModerationRuleOptions|FetchAutoModerationRulesOptions} [options]
* Options for fetching auto moderation rule(s)
* @returns {Promise<AutoModerationRule|Collection<Snowflake, AutoModerationRule>>}
@@ -248,13 +264,14 @@ class AutoModerationRuleManager extends CachedManager {
* .then(console.log)
* .catch(console.error)
*/
fetch(options) {
async fetch(options) {
if (!options) return this._fetchMany();
const { autoModerationRule, cache, force } = options;
const resolvedAutoModerationRule = this.resolveId(autoModerationRule ?? options);
if (resolvedAutoModerationRule) {
return this._fetchSingle({ autoModerationRule: resolvedAutoModerationRule, cache, force });
}
return this._fetchMany(options);
}
@@ -279,6 +296,7 @@ class AutoModerationRuleManager extends CachedManager {
/**
* Deletes an auto moderation rule.
*
* @param {AutoModerationRuleResolvable} autoModerationRule The auto moderation rule to delete
* @param {string} [reason] The reason for deleting the auto moderation rule
* @returns {Promise<void>}

View File

@@ -2,12 +2,14 @@
/**
* Manages the API methods of a data model.
*
* @abstract
*/
class BaseManager {
constructor(client) {
/**
* The client that instantiated this Manager
*
* @name BaseManager#client
* @type {Client}
* @readonly

View File

@@ -1,10 +1,11 @@
'use strict';
const { DataManager } = require('./DataManager.js');
const { MakeCacheOverrideSymbol } = require('../util/Symbols.js');
const { DataManager } = require('./DataManager.js');
/**
* Manages the API methods of a data model with a mutable cache of instances.
*
* @extends {DataManager}
* @abstract
*/
@@ -14,6 +15,7 @@ class CachedManager extends DataManager {
/**
* The private cache of items for this manager.
*
* @type {Collection}
* @private
* @readonly
@@ -36,6 +38,7 @@ class CachedManager extends DataManager {
/**
* The cache of items for this manager.
*
* @type {Collection}
* @abstract
*/
@@ -50,6 +53,7 @@ class CachedManager extends DataManager {
existing._patch(data);
return existing;
}
const clone = existing._clone();
clone._patch(data);
return clone;

View File

@@ -1,10 +1,11 @@
'use strict';
const { DataManager } = require('./DataManager.js');
const { GuildChannel } = require('../structures/GuildChannel.js');
const { DataManager } = require('./DataManager.js');
/**
* Manages API methods for CategoryChannels' children.
*
* @extends {DataManager}
*/
class CategoryChannelChildManager extends DataManager {
@@ -12,6 +13,7 @@ class CategoryChannelChildManager extends DataManager {
super(channel.client, GuildChannel);
/**
* The category channel this manager belongs to
*
* @type {CategoryChannel}
*/
this.channel = channel;
@@ -19,6 +21,7 @@ class CategoryChannelChildManager extends DataManager {
/**
* The channels that are a part of this category
*
* @type {Collection<Snowflake, GuildChannel>}
* @readonly
*/
@@ -28,6 +31,7 @@ class CategoryChannelChildManager extends DataManager {
/**
* The guild this manager belongs to
*
* @type {Guild}
* @readonly
*/
@@ -37,6 +41,7 @@ class CategoryChannelChildManager extends DataManager {
/**
* Options for creating a channel using {@link CategoryChannelChildManager#create}.
*
* @typedef {Object} CategoryCreateChannelOptions
* @property {string} name The name for the new channel
* @property {ChannelType} [type=ChannelType.GuildText] The type of the new channel.
@@ -65,10 +70,11 @@ class CategoryChannelChildManager extends DataManager {
/**
* Creates a new channel within this category.
* <info>You cannot create a channel of type {@link ChannelType.GuildCategory} inside a CategoryChannel.</info>
*
* @param {CategoryCreateChannelOptions} options Options for creating the new channel
* @returns {Promise<GuildChannel>}
*/
create(options) {
async create(options) {
return this.guild.channels.create({
...options,
parent: this.channel.id,

View File

@@ -3,12 +3,12 @@
const process = require('node:process');
const { lazy } = require('@discordjs/util');
const { Routes } = require('discord-api-types/v10');
const { CachedManager } = require('./CachedManager.js');
const { BaseChannel } = require('../structures/BaseChannel.js');
const { MessagePayload } = require('../structures/MessagePayload.js');
const { createChannel } = require('../util/Channels.js');
const { ThreadChannelTypes } = require('../util/Constants.js');
const { Events } = require('../util/Events.js');
const { CachedManager } = require('./CachedManager.js');
const getMessage = lazy(() => require('../structures/Message.js').Message);
@@ -16,6 +16,7 @@ let cacheWarningEmitted = false;
/**
* A manager of channels belonging to a client
*
* @extends {CachedManager}
*/
class ChannelManager extends CachedManager {
@@ -36,6 +37,7 @@ class ChannelManager extends CachedManager {
/**
* The cache of Channels
*
* @type {Collection<Snowflake, BaseChannel>}
* @name ChannelManager#cache
*/
@@ -48,6 +50,7 @@ class ChannelManager extends CachedManager {
if (ThreadChannelTypes.includes(existing.type)) {
existing.parent?.threads?._add(existing);
}
return existing;
}
@@ -84,13 +87,15 @@ class ChannelManager extends CachedManager {
/**
* Data that can be resolved to give a Channel object. This can be:
* * A Channel object
* * A Snowflake
* - A Channel object
* - A Snowflake
*
* @typedef {BaseChannel|Snowflake} ChannelResolvable
*/
/**
* Resolves a ChannelResolvable to a Channel object.
*
* @method resolve
* @memberof ChannelManager
* @instance
@@ -100,6 +105,7 @@ class ChannelManager extends CachedManager {
/**
* Resolves a ChannelResolvable to a channel id string.
*
* @method resolveId
* @memberof ChannelManager
* @instance
@@ -109,6 +115,7 @@ class ChannelManager extends CachedManager {
/**
* Options for fetching a channel from Discord
*
* @typedef {BaseFetchOptions} FetchChannelOptions
* @property {boolean} [allowUnknownGuild=false] Allows the channel to be returned even if the guild is not in cache,
* it will not be cached. <warn>Many of the properties and methods on the returned channel will throw errors</warn>
@@ -116,6 +123,7 @@ class ChannelManager extends CachedManager {
/**
* Obtains a channel from Discord, or the channel cache if it's already available.
*
* @param {Snowflake} id The channel's id
* @param {FetchChannelOptions} [options] Additional options for this fetch
* @returns {Promise<?BaseChannel>}
@@ -137,6 +145,7 @@ class ChannelManager extends CachedManager {
/**
* Creates a message in a channel.
*
* @param {TextChannelResolvable} channel The channel to send the message to
* @param {string|MessagePayload|MessageCreateOptions} options The options to provide
* @returns {Promise<Message>}

View File

@@ -4,11 +4,13 @@ const { MessageManager } = require('./MessageManager.js');
/**
* Manages API methods for messages in direct message channels and holds their cache.
*
* @extends {MessageManager}
*/
class DMMessageManager extends MessageManager {
/**
* The channel that the messages belong to
*
* @name DMMessageManager#channel
* @type {DMChannel}
*/

View File

@@ -1,10 +1,11 @@
'use strict';
const { BaseManager } = require('./BaseManager.js');
const { DiscordjsError, ErrorCodes } = require('../errors/index.js');
const { BaseManager } = require('./BaseManager.js');
/**
* Manages the API methods of a data model along with a collection of instances.
*
* @extends {BaseManager}
* @abstract
*/
@@ -14,6 +15,7 @@ class DataManager extends BaseManager {
/**
* The data structure belonging to this manager.
*
* @name DataManager#holds
* @type {Function}
* @private
@@ -24,6 +26,7 @@ class DataManager extends BaseManager {
/**
* The cache of items for this manager.
*
* @type {Collection}
* @abstract
*/
@@ -33,7 +36,8 @@ class DataManager extends BaseManager {
/**
* Resolves a data entry to a data Object.
* @param {string|Object} idOrInstance The id or instance of something in this Manager
*
* @param {string | Object} idOrInstance The id or instance of something in this Manager
* @returns {?Object} An instance from this Manager
*/
resolve(idOrInstance) {
@@ -44,7 +48,8 @@ class DataManager extends BaseManager {
/**
* Resolves a data entry to an instance id.
* @param {string|Object} idOrInstance The id or instance of something in this Manager
*
* @param {string | Object} idOrInstance The id or instance of something in this Manager
* @returns {?Snowflake}
*/
resolveId(idOrInstance) {

Some files were not shown because too many files have changed in this diff Show More