Compare commits

..

4 Commits

Author SHA1 Message Date
iCrawl
de0cacdf32 chore(release): version 2020-03-20 09:06:58 +01:00
izexi
bb4cb3e7fe fix: messageReactionRemove emission (#3966)
* fix: handler event name for MessageReactionRemoveEmoji

* fix: typo in WSEvents key
2020-03-20 08:53:47 +01:00
SpaceEEC
08865a98cd chore(release): publish 2020-03-08 19:38:05 +01:00
SpaceEEC
20075e306b fix(ReactionCollector): only modify users and total on collect (#3905) 2020-03-08 19:33:18 +01:00
407 changed files with 19818 additions and 50533 deletions

View File

@@ -1,26 +0,0 @@
{
"extends": ["@commitlint/config-angular"],
"rules": {
"scope-case": [2, "always", "pascal-case"],
"type-enum": [
2,
"always",
[
"chore",
"build",
"ci",
"docs",
"feat",
"fix",
"perf",
"refactor",
"revert",
"style",
"test",
"types",
"workflow",
"wip"
]
]
}
}

View File

@@ -1,65 +1,44 @@
{
"extends": ["eslint:recommended", "plugin:prettier/recommended"],
"plugins": ["import"],
"extends": "eslint:recommended",
"parserOptions": {
"ecmaVersion": 2021
"ecmaVersion": 6
},
"env": {
"es2021": true,
"es6": true,
"node": true
},
"rules": {
"import/order": [
"error",
{
"groups": ["builtin", "external", "internal", "index", "sibling", "parent"],
"alphabetize": {
"order": "asc"
}
}
],
"prettier/prettier": [
2,
{
"printWidth": 120,
"singleQuote": true,
"quoteProps": "as-needed",
"trailingComma": "all",
"endOfLine": "lf",
"arrowParens": "avoid"
}
],
"strict": ["error", "global"],
"no-await-in-loop": "warn",
"no-compare-neg-zero": "error",
"no-extra-parens": ["warn", "all", {
"nestedBinaryExpressions": false
}],
"no-template-curly-in-string": "error",
"no-unsafe-negation": "error",
"valid-jsdoc": [
"error",
{
"requireReturn": false,
"requireReturnDescription": false,
"prefer": {
"return": "returns",
"arg": "param"
},
"preferType": {
"String": "string",
"Number": "number",
"Boolean": "boolean",
"Symbol": "symbol",
"object": "Object",
"function": "Function",
"array": "Array",
"date": "Date",
"error": "Error",
"null": "void"
}
"valid-jsdoc": ["error", {
"requireReturn": false,
"requireReturnDescription": false,
"prefer": {
"return": "returns",
"arg": "param"
},
"preferType": {
"String": "string",
"Number": "number",
"Boolean": "boolean",
"Symbol": "symbol",
"object": "Object",
"function": "Function",
"array": "Array",
"date": "Date",
"error": "Error",
"null": "void"
}
],
}],
"accessor-pairs": "warn",
"array-callback-return": "error",
"complexity": "warn",
"consistent-return": "error",
"curly": ["error", "multi-line", "consistent"],
"dot-location": ["error", "property"],
@@ -117,6 +96,7 @@
"func-names": "error",
"func-name-matching": "error",
"func-style": ["error", "declaration", { "allowArrowFunctions": true }],
"indent": ["error", 2, { "SwitchCase": 1 }],
"key-spacing": "error",
"keyword-spacing": "error",
"max-depth": "error",
@@ -128,6 +108,7 @@
"no-array-constructor": "error",
"no-inline-comments": "error",
"no-lonely-if": "error",
"no-mixed-operators": "error",
"no-multiple-empty-lines": ["error", { "max": 2, "maxEOF": 1, "maxBOF": 0 }],
"no-new-object": "error",
"no-spaced-func": "error",
@@ -137,20 +118,14 @@
"nonblock-statement-body-position": "error",
"object-curly-spacing": ["error", "always"],
"operator-assignment": "error",
"operator-linebreak": ["error", "after"],
"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-before-function-paren": ["error", "never"],
"space-in-parens": "error",
"space-infix-ops": "error",
"space-unary-ops": "error",

View File

@@ -1,76 +0,0 @@
# Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as
contributors and maintainers pledge to making participation in our project and
our community a harassment-free experience for everyone, regardless of age, body
size, disability, ethnicity, sex characteristics, gender identity and expression,
level of experience, education, socio-economic status, nationality, personal
appearance, race, religion, or sexual identity and orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment
include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or
advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic
address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable
behavior and are expected to take appropriate and fair corrective action in
response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct, or to ban temporarily or
permanently any contributor for other behaviors that they deem inappropriate,
threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces
when an individual is representing the project or its community. Examples of
representing a project or community include using an official project e-mail
address, posting via an official social media account, or acting as an appointed
representative at an online or offline event. Representation of a project may be
further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting the project team at https://discord.gg/djs. All
complaints will be reviewed and investigated and will result in a response that
is deemed necessary and appropriate to the circumstances. The project team is
obligated to maintain confidentiality with regard to the reporter of an incident.
Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good
faith may face temporary or permanent repercussions as determined by other
members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
[homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see
https://www.contributor-covenant.org/faq

View File

@@ -1,17 +1,17 @@
# Contributing
**The issue tracker is only for bug reports and enhancement suggestions. If you have a question, please ask it in the [Discord server](https://discord.gg/djs) instead of opening an issue you will get redirected there anyway.**
**The issue tracker is only for bug reports and enhancement suggestions. If you have a question, please ask it in the [Discord server](https://discord.gg/bRCvFy9) instead of opening an issue you will get redirected there anyway.**
If you wish to contribute to the discord.js codebase or documentation, feel free to fork the repository and submit a
pull request. We use ESLint to enforce a consistent coding style, so having that set up in your editor of choice
is a great boon to your development process.
## Setup
To get ready to work on the codebase, please do the following:
1. Fork & clone the repository, and make sure you're on the **master** branch
2. Run `npm ci`
3. Code your heart out!
4. Run `npm test` to run ESLint and ensure any JSDoc changes are valid
5. [Submit a pull request](https://github.com/discordjs/discord.js/compare) (Make sure you follow the [conventional commit format](https://github.com/discordjs/discord.js-next/blob/master/.github/COMMIT_CONVENTION.md))
2. Run `npm install`
3. If you're working on voice, also run `npm install @discordjs/opus` or `npm install opusscript`
4. Code your heart out!
5. Run `npm test` to run ESLint and ensure any JSDoc changes are valid
6. [Submit a pull request](https://github.com/discordjs/discord.js/compare)

11
.github/FUNDING.yml vendored
View File

@@ -1,11 +0,0 @@
# These are supported funding model platforms
# github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
# patreon: # Replace with a single Patreon username
# open_collective: # Replace with a single Open Collective username
# ko_fi: # Replace with a single Ko-fi username
# tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
# custom: # Replace with a single custom sponsorship URL
github: amishshah
patreon: discordjs

26
.github/ISSUE_TEMPLATE.md vendored Normal file
View File

@@ -0,0 +1,26 @@
<!--
If you need help with discord.js installation or usage, please go to the discord.js Discord server instead:
https://discord.gg/bRCvFy9
This issue tracker is only for bug reports and enhancement suggestions. You won't receive any basic help here.
-->
**Please describe the problem you are having in as much detail as possible:**
**Include a reproducible code sample here, if possible:**
```js
```
**Further details:**
- discord.js version:
- node.js version:
- Operating system:
- Priority this issue should have please be realistic and elaborate if possible:
<!--
Ideally you would also test whether the issue occurs on the latest master branch commit.
If you have, please check the following box and insert the hash of the commit you tested:
-->
- [ ] I have also tested the issue on latest master, commit hash:

View File

@@ -1,38 +0,0 @@
---
name: Bug report
about: Report incorrect or unexpected behavior of discord.js
title: ''
labels: 's: unverified, type: bug'
assignees: ''
---
<!-- Use Discord for questions: https://discord.gg/djs -->
<!-- If you are reporting a voice issue, please post your issue at https://github.com/discordjs/voice/issues -->
**Please describe the problem you are having in as much detail as possible:**
**Include a reproducible code sample here, if possible:**
```js
// Place your code here
```
**Further details:**
- discord.js version:
- Node.js version:
- Operating system:
- Priority this issue should have please be realistic and elaborate if possible:
**Relevant client options:**
- partials: none
- gateway intents: none
- other: none
<!--
Remove the comment and fill out the commit hash if this applies to you:
(While it's not a requirement to test your issue on the master branch, it would make fixing the problem a lot easier for us, so please do so if possible.)
- I have also tested the issue on latest master, commit hash: `xxx`
-->

View File

@@ -1,5 +0,0 @@
blank_issues_enabled: false
contact_links:
- name: Discord server
url: https://discord.gg/djs
about: Please visit our Discord server for questions and support requests.

View File

@@ -1,21 +0,0 @@
---
name: Feature request
about: Request a feature for the core discord.js library
title: ''
labels: 'type: enhancement'
assignees: ''
---
<!-- Use Discord for questions: https://discord.gg/djs -->
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Eg. I'm always frustrated when [...]
**Describe the ideal solution**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.

View File

@@ -1,14 +1,7 @@
**Please describe the changes this PR makes and why it should be merged:**
**Status and versioning classification:**
<!--
Please move lines that apply to you out of the comment:
- Code changes have been tested against the Discord API, or there are no code changes
- I know how to update typings and have done so, or typings don't need updating
- This PR changes the library's interface (methods or parameters added)
- This PR includes breaking changes (methods removed or renamed, parameters moved or removed)
- This PR **only** includes non-code changes, like changes to documentation, README, etc.
-->
**Semantic versioning classification:**
- [ ] This PR changes the library's interface (methods or parameters added)
- [ ] This PR includes breaking changes (methods removed or renamed, parameters moved or removed)
- [ ] This PR **only** includes non-code changes, like changes to documentation, README, etc.

7
.github/SUPPORT.md vendored
View File

@@ -1,7 +0,0 @@
# Seeking support?
We only use this issue tracker for bug reports and feature request. We are not able to provide general support or answer questions in the form of GitHub issues.
For general questions about discord.js installation and use please use the dedicated support channels in our Discord server: https://discord.gg/djs
Any issues that don't directly involve a bug or a feature request will likely be closed and redirected.

18
.github/tsc.json vendored
View File

@@ -1,18 +0,0 @@
{
"problemMatcher": [
{
"owner": "tsc",
"pattern": [
{
"regexp": "^(?:\\s+\\d+\\>)?([^\\s].*)\\((\\d+),(\\d+)\\)\\s*:\\s+(error|warning|info)\\s+(\\w{1,2}\\d+)\\s*:\\s*(.*)$",
"file": 1,
"line": 2,
"column": 3,
"severity": 4,
"code": 5,
"message": 6
}
]
}
]
}

View File

@@ -1,28 +0,0 @@
name: Deployment
on:
push:
branches:
- '*'
- '!docs'
tags:
- '*'
jobs:
docs:
name: Documentation
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v2
- name: Install Node v16
uses: actions/setup-node@v2
with:
node-version: 16
- name: Install dependencies
run: npm ci
- name: Build and deploy documentation
uses: discordjs/action-docs@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@@ -1,33 +0,0 @@
name: Publish dev
on:
workflow_dispatch:
schedule:
- cron: '0 */12 * * *'
jobs:
npm:
name: npm
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v2
- name: Install Node v16
uses: actions/setup-node@v2
with:
node-version: 16
registry-url: https://registry.npmjs.org/
- name: Install dependencies
run: npm ci --ignore-scripts
- name: Deprecate old versions
run: npm deprecate discord.js@"~13.0.0-dev" "no longer supported" || true
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_PUBLISH_TOKEN }}
- name: Publish
run: |
npm version --git-tag-version=false $(jq --raw-output '.version' package.json).t$(date +%s).$(git rev-parse --short HEAD)
npm publish --tag dev || true
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_PUBLISH_TOKEN }}

View File

@@ -1,79 +0,0 @@
name: Testing Cron
on:
schedule:
- cron: '0 */12 * * *'
jobs:
lint:
name: ESLint
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v2
- name: Install Node v16
uses: actions/setup-node@v2
with:
node-version: 16
- name: Install dependencies
run: npm ci
- name: Run ESLint
run: npm run lint
typings:
name: TSLint
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v2
- name: Install Node v16
uses: actions/setup-node@v2
with:
node-version: 16
- name: Install dependencies
run: npm ci
- name: Run TSLint
run: npm run lint:typings
typescript:
name: TypeScript
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v2
- name: Install Node v16
uses: actions/setup-node@v2
with:
node-version: 16
- name: Install dependencies
run: npm ci
- name: Register Problem Matcher
run: echo "##[add-matcher].github/tsc.json"
- name: Run TypeScript compiler
run: npm run test:typescript
docs:
name: Documentation
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v2
- name: Install Node v16
uses: actions/setup-node@v2
with:
node-version: 16
- name: Install dependencies
run: npm ci
- name: Test documentation
run: npm run docs:test

View File

@@ -1,77 +0,0 @@
name: Testing
on: [push, pull_request]
jobs:
lint:
name: ESLint
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v2
- name: Install Node v16
uses: actions/setup-node@v2
with:
node-version: 16
- name: Install dependencies
run: npm ci
- name: Run ESLint
run: npm run lint
typings:
name: TSLint
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v2
- name: Install Node v16
uses: actions/setup-node@v2
with:
node-version: 16
- name: Install dependencies
run: npm ci
- name: Run TSLint
run: npm run lint:typings
typescript:
name: TypeScript
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v2
- name: Install Node v16
uses: actions/setup-node@v2
with:
node-version: 16
- name: Install dependencies
run: npm ci
- name: Register Problem Matcher
run: echo "##[add-matcher].github/tsc.json"
- name: Run TypeScript compiler
run: npm run test:typescript
docs:
name: Documentation
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v2
- name: Install Node v16
uses: actions/setup-node@v2
with:
node-version: 16
- name: Install dependencies
run: npm ci
- name: Test documentation
run: npm run docs:test

6
.gitignore vendored
View File

@@ -17,9 +17,5 @@ deploy/deploy_key.pub
# Miscellaneous
.tmp/
.vscode/
.idea/
docs/docs.json
typings/index.js
# Autogenerated
src/index.mjs
webpack/

1
.husky/.gitignore vendored
View File

@@ -1 +0,0 @@
_

View File

@@ -1,4 +0,0 @@
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
npx --no-install commitlint --edit $1

View File

@@ -1,4 +0,0 @@
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
npx --no-install lint-staged

View File

@@ -1,4 +0,0 @@
{
"*.{mjs,js}": "eslint --fix --ext mjs,js,ts",
"*.{ts,json,yml,yaml}": "prettier --write"
}

28
.npmignore Normal file
View File

@@ -0,0 +1,28 @@
# Packages
node_modules/
yarn.lock
# Log files
logs/
*.log
# Authentication
deploy/
# Miscellaneous
.tmp/
.vscode/
docs/
webpack/
# NPM ignore
.eslintrc.json
.gitattributes
.gitignore
.travis.yml
webpack.config.js
.github/
test/
tsconfig.json
tslint.json

4
.npmrc
View File

@@ -1,3 +1 @@
audit=false
fund=false
legacy-peer-deps=true
package-json=false

View File

@@ -1,7 +0,0 @@
{
"singleQuote": true,
"printWidth": 120,
"trailingComma": "all",
"endOfLine": "lf",
"arrowParens": "avoid"
}

View File

@@ -1,8 +1,12 @@
{
"ecmaVersion": 7,
"libs": [],
"loadEagerly": ["./src/*.js"],
"dontLoad": ["node_modules/**"],
"loadEagerly": [
"./src/*.js"
],
"dontLoad": [
"node_modules/**"
],
"plugins": {
"es_modules": {},
"node": {},
@@ -10,5 +14,8 @@
"fullDocs": true,
"strong": true
},
"webpack": {
"configPath": "./webpack.config.js",
}
}
}

22
.travis.yml Normal file
View File

@@ -0,0 +1,22 @@
language: node_js
node_js:
- "6"
- "8"
- "10"
- "12"
cache:
directories:
- node_modules
install: npm install
script: bash ./deploy/test.sh
jobs:
include:
- stage: build
node_js: "6"
script: bash ./deploy/deploy.sh
env:
global:
- ENCRYPTION_LABEL: "af862fa96d3e"
- COMMIT_AUTHOR_EMAIL: "amishshah.2k@gmail.com"
dist: trusty
sudo: false

View File

@@ -1,314 +0,0 @@
# [13.0.0](https://github.com/discordjs/discord.js/compare/12.5.0...v13.0.0) (2021-08-06)
### Bug Fixes
* **Action:** getChannel interaction DM ([#6280](https://github.com/discordjs/discord.js/issues/6280)) ([a72b5a3](https://github.com/discordjs/discord.js/commit/a72b5a355e443f17edf1b348f63d314c743093b9))
* **BaseGuildTextChannel:** call patch ([#6298](https://github.com/discordjs/discord.js/issues/6298)) ([3eb4140](https://github.com/discordjs/discord.js/commit/3eb41405f412ee2b2d05c4245c4ebb80adfcec6b))
* **Caching:** sweep archived threads in all channel caches ([#6312](https://github.com/discordjs/discord.js/issues/6312)) ([3725dca](https://github.com/discordjs/discord.js/commit/3725dcafc0cbb4a40d3ff66d2a9718e986f47c5b))
* **Channel:** add default for destructured options ([#6203](https://github.com/discordjs/discord.js/issues/6203)) ([6872547](https://github.com/discordjs/discord.js/commit/68725476b39d5ef5793ccf62cfb468073e7d9cb2))
* **ChannelUpdate:** restore accidentally removed line ([#6263](https://github.com/discordjs/discord.js/issues/6263)) ([774f5b7](https://github.com/discordjs/discord.js/commit/774f5b77ec6218c30360c773b5fe6185d1efd146))
* **CommandInteractionOptionResolver:** Export CommandInteractionOptio… ([#6146](https://github.com/discordjs/discord.js/issues/6146)) ([8ccfd6e](https://github.com/discordjs/discord.js/commit/8ccfd6e07b3208568c495110c80990366637818e))
* **CommandInteractionOptionResolver:** type should be USER ([#6148](https://github.com/discordjs/discord.js/issues/6148)) ([02f55f0](https://github.com/discordjs/discord.js/commit/02f55f09712af5d6a6f67b5ac369b1969801e23f))
* **Guild:** add invite manager property, extend CachedManager ([#6049](https://github.com/discordjs/discord.js/issues/6049)) ([e3d877d](https://github.com/discordjs/discord.js/commit/e3d877d542518d0f82c476d921968338a859722b))
* **GuildChannel:** Add missing 'cache' property ([#6019](https://github.com/discordjs/discord.js/issues/6019)) ([7e30011](https://github.com/discordjs/discord.js/commit/7e3001191c1529b9db09b6168ddd0c09931598f5))
* **GuildChannel:** setParent not working ([#6276](https://github.com/discordjs/discord.js/issues/6276)) ([7e0618f](https://github.com/discordjs/discord.js/commit/7e0618f17a786708093bc532501b261191e85561))
* **GuildChannel:** use map method on cache not its manager ([#6032](https://github.com/discordjs/discord.js/issues/6032)) ([d58f0d2](https://github.com/discordjs/discord.js/commit/d58f0d243c9bbd803cff7b8da2056c11e0416bef))
* **GuildMemberManager:** fetchMany fixes ([#6314](https://github.com/discordjs/discord.js/issues/6314)) ([52817fc](https://github.com/discordjs/discord.js/commit/52817fc414eef5220043e055a740e4ad0f8287a0))
* **Message:** fix thread getter ([#6309](https://github.com/discordjs/discord.js/issues/6309)) ([913c9fa](https://github.com/discordjs/discord.js/commit/913c9fa17672fa914776beb556edcee5cd2d2dc8))
* **MessageMentions:** check guild exists before adding roles ([#6313](https://github.com/discordjs/discord.js/issues/6313)) ([1200bba](https://github.com/discordjs/discord.js/commit/1200bba7bcc48dab6a454925e533528a7e3c0cf7))
* export various classes that are exported in the typings ([#6166](https://github.com/discordjs/discord.js/issues/6166)) ([30a58dc](https://github.com/discordjs/discord.js/commit/30a58dc80130d334bf8c85e7b37513c109beda1b))
* remove support for overriding caches that break functionality ([#6282](https://github.com/discordjs/discord.js/issues/6282)) ([a6d4035](https://github.com/discordjs/discord.js/commit/a6d4035176ca784e75bd1cbdf30e039658c62fd4))
* **CommandInteraction:** change options type from Collection to array ([#6139](https://github.com/discordjs/discord.js/issues/6139)) ([77784ac](https://github.com/discordjs/discord.js/commit/77784aca431709ff3c17095bd06f9e4016fbd11d))
* **GuildAuditLog:** Assert `target` to null upon not finding invite codes ([#6171](https://github.com/discordjs/discord.js/issues/6171)) ([c8ca7bf](https://github.com/discordjs/discord.js/commit/c8ca7bfd2c5e7b29e54987a78301e9bdfa4a717b))
* **GuildChannel:** clone its PermissionOverwriteManager too ([#6083](https://github.com/discordjs/discord.js/issues/6083)) ([f72ce7c](https://github.com/discordjs/discord.js/commit/f72ce7c136cf2dfe31a67b190c00e30ba7d70bfa))
* **GuildChannel:** only fetch invites for the specific channel ([#6132](https://github.com/discordjs/discord.js/issues/6132)) ([c40c0f9](https://github.com/discordjs/discord.js/commit/c40c0f934a571c100e4b3aa633a80fe48661d836))
* **InviteScope:** added missing 'bot' scope ([#6052](https://github.com/discordjs/discord.js/issues/6052)) ([93e2c04](https://github.com/discordjs/discord.js/commit/93e2c04ec27c44a8c955e576944023dc25075647))
* **Message:** fix typo in sticker_items ([#6173](https://github.com/discordjs/discord.js/issues/6173)) ([870a0de](https://github.com/discordjs/discord.js/commit/870a0de53c01331c9357df4808fc0979ff17b9a4))
* **Message:** make #channel and #guild getters ([#6271](https://github.com/discordjs/discord.js/issues/6271)) ([6e3236a](https://github.com/discordjs/discord.js/commit/6e3236ab64549d27445c631cbb3d88c2bb9bf289))
* **PermissionOverwriteManager:** pass ID to API correctly ([#6026](https://github.com/discordjs/discord.js/issues/6026)) ([8077e4f](https://github.com/discordjs/discord.js/commit/8077e4f4f132f95c4fa21e9fc7313b93a2c4b9d7))
* **PermissionOverwrites:** throw better error if resolving option fails ([#6219](https://github.com/discordjs/discord.js/issues/6219)) ([42a0313](https://github.com/discordjs/discord.js/commit/42a03130345d3a3841f5271d82a2cb8725b6fa0e))
* **PermissionsOverwrites:** only convert type if number ([#6092](https://github.com/discordjs/discord.js/issues/6092)) ([dee5c83](https://github.com/discordjs/discord.js/commit/dee5c83fc0d1147d5b65151a8f91a4a089687a73))
* **PresenceUpdate:** use added presence over nullable getter ([#6077](https://github.com/discordjs/discord.js/issues/6077)) ([637c8e0](https://github.com/discordjs/discord.js/commit/637c8e0fdfb4ce15361646017718c72c3d6af538))
* **SelectMenuInteraction:** set values to empty array if not provided ([#6045](https://github.com/discordjs/discord.js/issues/6045)) ([34708d6](https://github.com/discordjs/discord.js/commit/34708d6d18f94b5c8d9c582973d057e1f89bfe1f))
* **Sticker:** replace 'this.guildID' (undefined) by 'this.guildId' in fetchUser ([#6160](https://github.com/discordjs/discord.js/issues/6160)) ([07017a9](https://github.com/discordjs/discord.js/commit/07017a9699eecc4af7824ace39dd91e8b689f3c6))
* **Structures:** remove Structures ([#6027](https://github.com/discordjs/discord.js/issues/6027)) ([ab0b3b9](https://github.com/discordjs/discord.js/commit/ab0b3b9a07f5e4987e4f25e41b2a007f2db06322))
* **TeamMember:** Fixed incorrect return types. ([#6044](https://github.com/discordjs/discord.js/issues/6044)) ([fe5d56c](https://github.com/discordjs/discord.js/commit/fe5d56c9b11b3e5f05933c6d746237b9f353b392))
* **TextBasedChannel:** Fix MessageCreate handling ([#6217](https://github.com/discordjs/discord.js/issues/6217)) ([6b85f90](https://github.com/discordjs/discord.js/commit/6b85f900fa8e6cc01f7ee14ae730950cf1635dd5))
* **ThreadChannel:** better property handling ([#6172](https://github.com/discordjs/discord.js/issues/6172)) ([9679b90](https://github.com/discordjs/discord.js/commit/9679b9087200e29d2f488d84d115465449021b51))
* **Util:** fix collection import ([#6256](https://github.com/discordjs/discord.js/issues/6256)) ([93e0239](https://github.com/discordjs/discord.js/commit/93e0239c8054293eac63338819a10490dbd49ff1))
* **WebSocketShard:** mark shard ready if no guilds intent ([#6284](https://github.com/discordjs/discord.js/issues/6284)) ([09471be](https://github.com/discordjs/discord.js/commit/09471be30eea2540999c3d5a2b001a985a0d27cc))
* array/keyArray removed ([#6245](https://github.com/discordjs/discord.js/issues/6245)) ([bf221f2](https://github.com/discordjs/discord.js/commit/bf221f2bef2871f019aa4a6978deb8460fff85b9))
* Use `string` instead of `Snowflake` for invites ([#6202](https://github.com/discordjs/discord.js/issues/6202)) ([f060a3f](https://github.com/discordjs/discord.js/commit/f060a3fcd7e1ad30789d582e8baf28dbdddf8063))
* **Typings:** type attachments to InteractionUpdateOptions ([#6162](https://github.com/discordjs/discord.js/issues/6162)) ([4f8ca29](https://github.com/discordjs/discord.js/commit/4f8ca2936a85109757fb3225e9d6cf9aae9714e2))
* channel type check in actions ([#6086](https://github.com/discordjs/discord.js/issues/6086)) ([d433fe8](https://github.com/discordjs/discord.js/commit/d433fe8a0827e6275e2a7ceed537be38411f4f67))
* Remove `connection.url` from open debug message ([#6018](https://github.com/discordjs/discord.js/issues/6018)) ([39db953](https://github.com/discordjs/discord.js/commit/39db95352c91faf175c2fd8ed365f293f965a0e4))
* **Util:** splitMessage not working with array ([#6008](https://github.com/discordjs/discord.js/issues/6008)) ([bd25ff5](https://github.com/discordjs/discord.js/commit/bd25ff59133ba31713647d3e6a5ef66abc4d54fb))
* correct permissions checks and cache on update ([#6015](https://github.com/discordjs/discord.js/issues/6015)) ([568691c](https://github.com/discordjs/discord.js/commit/568691ce6a7994adc85db2b2a5b2227ece8c8358))
* typedefs and typings for image & webhook options ([#5805](https://github.com/discordjs/discord.js/issues/5805)) ([a5a6e22](https://github.com/discordjs/discord.js/commit/a5a6e223166cf9af430da9003780e6582ea17b1c))
* **ApplicationCommand:** default option.required fix ([#5848](https://github.com/discordjs/discord.js/issues/5848)) ([a1f94f6](https://github.com/discordjs/discord.js/commit/a1f94f670e5b6009b9c33932ce06ed512447b953))
* **ApplicationCommand:** default option.required to false ([#5838](https://github.com/discordjs/discord.js/issues/5838)) ([77c1f15](https://github.com/discordjs/discord.js/commit/77c1f15c9f7562465c07727602c3213ddcf02778))
* **ApplicationCommand:** fix typo in JSDoc ([#5994](https://github.com/discordjs/discord.js/issues/5994)) ([6edf55c](https://github.com/discordjs/discord.js/commit/6edf55c04b970235fdc92c00808ee86002e589b6))
* **ApplicationCommand:** stringType isn't supposed to be sent to the API ([#5916](https://github.com/discordjs/discord.js/issues/5916)) ([ca2a36b](https://github.com/discordjs/discord.js/commit/ca2a36b1d713a743045b15adf99eda69a6fdbec7))
* **ApplicationCommands:** allow managing commands for uncached guilds ([#5729](https://github.com/discordjs/discord.js/issues/5729)) ([24e5868](https://github.com/discordjs/discord.js/commit/24e586881865c187ff0a3044ac37f6e338cc51ee))
* **ClientApplication:** freeze flags ([#5811](https://github.com/discordjs/discord.js/issues/5811)) ([e990c35](https://github.com/discordjs/discord.js/commit/e990c35476fb6f7e1a5449493833140144e0469c))
* **Collector:** docs and types ([#5937](https://github.com/discordjs/discord.js/issues/5937)) ([6c447b1](https://github.com/discordjs/discord.js/commit/6c447b12e3f978328cb2577ea3f81a5ab1531bbf))
* **Guild:** don't patch before instance properties ([#5885](https://github.com/discordjs/discord.js/issues/5885)) ([174b7a7](https://github.com/discordjs/discord.js/commit/174b7a7f9c5f95ab182a2c90dae43ccd4f55357b))
* **Guild:** setChannelPositions null parenting ([#5841](https://github.com/discordjs/discord.js/issues/5841)) ([01a1fd6](https://github.com/discordjs/discord.js/commit/01a1fd615bbf017e18bbffe8e97336345f42564a))
* **GuildChannel:** clone errors when options.name isn't provided ([#5804](https://github.com/discordjs/discord.js/issues/5804)) ([41673b7](https://github.com/discordjs/discord.js/commit/41673b738232f64da2ded3b15be0f798135ae351))
* **GuildChannel:** don't force parentID/permissionOverwrites to empty on create ([#5823](https://github.com/discordjs/discord.js/issues/5823)) ([c585933](https://github.com/discordjs/discord.js/commit/c5859337b616c1fe2a60884595d27db4f13d8fee))
* **GuildChannel:** improve empty overwrite handling for permissionsLocked ([#5821](https://github.com/discordjs/discord.js/issues/5821)) ([6df3623](https://github.com/discordjs/discord.js/commit/6df36232a05e396d31461200725755745526d2ed))
* **GuildChannel:** spread clone options to avoid infinite recursion ([#5800](https://github.com/discordjs/discord.js/issues/5800)) ([2f5424b](https://github.com/discordjs/discord.js/commit/2f5424bdac7c97d0a371dd72084ac02d5e774f1a))
* **GuildManager:** #create throws when systemChannelFlags is undefined ([#5832](https://github.com/discordjs/discord.js/issues/5832)) ([29173bf](https://github.com/discordjs/discord.js/commit/29173bf814e2cd795467b9b3814f0eaf0773e9ae))
* **Hooks:** make husky hooks executable ([#5812](https://github.com/discordjs/discord.js/issues/5812)) ([77e28cf](https://github.com/discordjs/discord.js/commit/77e28cf65b0fb3fc2fb7ab07fc7bb0f2f5a400b5))
* **InteractionResponses:** set replied status on editReply ([#5899](https://github.com/discordjs/discord.js/issues/5899)) ([6d3d00b](https://github.com/discordjs/discord.js/commit/6d3d00b44577a70e840f0187d6894043677c5329))
* **Message:** editedTimestamp defaulting to 0 ([#5847](https://github.com/discordjs/discord.js/issues/5847)) ([671436c](https://github.com/discordjs/discord.js/commit/671436cbb89f7f48bd9ae0ccb9dd75a376cc5281))
* **Message:** flags not being parsed on some edits ([#5886](https://github.com/discordjs/discord.js/issues/5886)) ([2d7c12b](https://github.com/discordjs/discord.js/commit/2d7c12b0e9387f56f1809822bc2c8c4ee52a00e9))
* **Message:** Make author of referenced message nullable ([#5929](https://github.com/discordjs/discord.js/issues/5929)) ([dc671c8](https://github.com/discordjs/discord.js/commit/dc671c8ac418c1f932034e82f38def28575a4b65))
* **PermissionOverwrites:** optional allow/deny OverwriteData ([#5810](https://github.com/discordjs/discord.js/issues/5810)) ([a7ebb21](https://github.com/discordjs/discord.js/commit/a7ebb2145c380214567514906393c4ab87932e95))
* **Permissions:** allow admin to override in the missing method ([#5911](https://github.com/discordjs/discord.js/issues/5911)) ([ee025b0](https://github.com/discordjs/discord.js/commit/ee025b05588493b55057b237ca96f88ecc5f0b02))
* **presenceUpdate:** fire when only state/details change on an activity ([#5846](https://github.com/discordjs/discord.js/issues/5846)) ([1f8f3ab](https://github.com/discordjs/discord.js/commit/1f8f3ab0f8dbd346154bbfa14a98726b8df25d57))
* **Sharding:** strict type context and return ([#5933](https://github.com/discordjs/discord.js/issues/5933)) ([1925d01](https://github.com/discordjs/discord.js/commit/1925d01d8f05ca10b2a39b91f25ffcabe363874b))
* **TextBasedChannel:** allow passing an APIMessage with split ([#5815](https://github.com/discordjs/discord.js/issues/5815)) ([93b0a4e](https://github.com/discordjs/discord.js/commit/93b0a4e005b5b1d371f7936238556db2e36cc982))
* **Thread:** make archive_timestamp not nullable ([#5965](https://github.com/discordjs/discord.js/issues/5965)) ([edab5af](https://github.com/discordjs/discord.js/commit/edab5afff9a4c79e5965c7c52b0a0d5ebb9ba35a))
* **ThreadChannel:** check for existence of properties when patching ([#5961](https://github.com/discordjs/discord.js/issues/5961)) ([9ac6867](https://github.com/discordjs/discord.js/commit/9ac68670d782fc81e266784e790af699f280eb0e))
* **ThreadManager:** fixed wrong private and fetchAll check ([#6012](https://github.com/discordjs/discord.js/issues/6012)) ([e4be666](https://github.com/discordjs/discord.js/commit/e4be666c2c273c56f04b8f965efc88bb9aff0032))
* **ThreadMemberManager:** fix ThreadMemberManager#add and ThreadMemberManager#fetch ([#5927](https://github.com/discordjs/discord.js/issues/5927)) ([adecead](https://github.com/discordjs/discord.js/commit/adecead716670278516fd031f240e05792420c75))
* **Typings:** erronous RawMessage ([5842e35](https://github.com/discordjs/discord.js/commit/5842e35881755350764b557d66b475c2c03f249a))
* **Typings:** Fix BitField toJSON/valueOf return types ([#5806](https://github.com/discordjs/discord.js/issues/5806)) ([935f819](https://github.com/discordjs/discord.js/commit/935f819207ac4219d37f3b99a2508e368626e6da))
* **Typings:** Improve components typings in MessageEditOptions ([#6002](https://github.com/discordjs/discord.js/issues/6002)) ([3a718d8](https://github.com/discordjs/discord.js/commit/3a718d8c625ab45bc9f4dfcd9d6bbdef67ae75b4))
* **User:** fix bot and system properties being incorrect in some cases ([#5923](https://github.com/discordjs/discord.js/issues/5923)) ([e44ae96](https://github.com/discordjs/discord.js/commit/e44ae961005358dac7032c75bfc74be3b719e5a1))
* **Webhook:** resolve non-string avatars too ([#5914](https://github.com/discordjs/discord.js/issues/5914)) ([4714a96](https://github.com/discordjs/discord.js/commit/4714a961b87746b0f85214c756614d276666f285))
* **Webhook:** return void from #delete for consistency. ([#5954](https://github.com/discordjs/discord.js/issues/5954)) ([5ad83a6](https://github.com/discordjs/discord.js/commit/5ad83a6a65e5944ceb3a41fee2df40ba1f5b03e4))
* **Webhook:** throw an error if no token is available when it's required ([#5798](https://github.com/discordjs/discord.js/issues/5798)) ([eb98e33](https://github.com/discordjs/discord.js/commit/eb98e33a85cc9bb235ceb509ed01218bae44ba73))
* **WebSocketShard:** don't catch errors thrown in event handlers ([#5803](https://github.com/discordjs/discord.js/issues/5803)) ([53d8e49](https://github.com/discordjs/discord.js/commit/53d8e49dca2d83fe2e066fb0b3d10418acbbc244))
* don't patch missing properties from partial payloads ([#5796](https://github.com/discordjs/discord.js/issues/5796)) ([097c7b9](https://github.com/discordjs/discord.js/commit/097c7b9cdd5e1bb52b037272eed19f556800ccff))
* add missing imports for custom errors ([#5767](https://github.com/discordjs/discord.js/issues/5767)) ([e980948](https://github.com/discordjs/discord.js/commit/e980948de55e91e59c9e3293ac76bc645a058a53))
* **ApiMessage:** only pass objects as options directly ([#5793](https://github.com/discordjs/discord.js/issues/5793)) ([3578159](https://github.com/discordjs/discord.js/commit/35781597d032fa7821e010e483c89f70ec51926c))
* **BitField:** ensure missing returns an array of strings ([#5795](https://github.com/discordjs/discord.js/issues/5795)) ([68f7aeb](https://github.com/discordjs/discord.js/commit/68f7aebcafcfd62bef02de855ca0c304a54e8d4c))
* **BitField:** throw an error if bit to resolve is undefined ([#5565](https://github.com/discordjs/discord.js/issues/5565)) ([0156f69](https://github.com/discordjs/discord.js/commit/0156f693e08fe2ad75133bf67c4aeb3e9c91a02d))
* **ClientPresence:** produce valid activities for set presences ([#5799](https://github.com/discordjs/discord.js/issues/5799)) ([ea0e06f](https://github.com/discordjs/discord.js/commit/ea0e06f9802fb57b41f471413b39ccd09546bb67))
* add components to MessageOption typedefs ([#5768](https://github.com/discordjs/discord.js/issues/5768)) ([657635c](https://github.com/discordjs/discord.js/commit/657635c1c09aa68211130bc3c56d6e8bb6e8e773))
* remove remnants of awaitMessageComponentInteractions ([#5783](https://github.com/discordjs/discord.js/issues/5783)) ([ae78a33](https://github.com/discordjs/discord.js/commit/ae78a336e1d0d190ec9f525449332dc781e0b3bf))
* **APIMessage:** document Interaction as valid MessageTarget ([#5678](https://github.com/discordjs/discord.js/issues/5678)) ([9f491ff](https://github.com/discordjs/discord.js/commit/9f491ffeb96ff380f2ab5ab2f86201d58be64c41))
* **ApplicationCommand:** return string equivalent of ApplicationCommandOptionType ([#5617](https://github.com/discordjs/discord.js/issues/5617)) ([a6079bc](https://github.com/discordjs/discord.js/commit/a6079bc9ce40ecbb4adace033dbf201897b5459f))
* **ApplicationCommandManager:** fix typo in JSDoc ([#5603](https://github.com/discordjs/discord.js/issues/5603)) ([3392eb7](https://github.com/discordjs/discord.js/commit/3392eb7de270842dbf5a54d19aa3e703dd445ba0))
* **ApplicationCommandManager:** limit permission methods to guilds ([#5613](https://github.com/discordjs/discord.js/issues/5613)) ([03256bd](https://github.com/discordjs/discord.js/commit/03256bd9f88c63dc5c2169e2c09ac8078ea84992))
* **ApplicationCommandOptionData:** options property should be itself ([#5679](https://github.com/discordjs/discord.js/issues/5679)) ([b90b0c3](https://github.com/discordjs/discord.js/commit/b90b0c3cfa2278caa38d1ff41eef2ccf4428b99e))
* **CommandInteraction:** channel type should be text based channels ([#5690](https://github.com/discordjs/discord.js/issues/5690)) ([5141ea4](https://github.com/discordjs/discord.js/commit/5141ea4f0694a60375d8bc0801b1225928bb3bd1))
* **CommandInteraction:** cmds with no options throw error ([#5734](https://github.com/discordjs/discord.js/issues/5734)) ([af2fad9](https://github.com/discordjs/discord.js/commit/af2fad94732eeb620fe17d9b537c279471c567c0))
* **CommandInteraction:** update typings and docs for #editReply ([#5630](https://github.com/discordjs/discord.js/issues/5630)) ([56d44fb](https://github.com/discordjs/discord.js/commit/56d44fbf1c922260c497350e8829d7151eb7a331))
* **DataResolver:** fix circular dependency error with GuildTemplate ([#5622](https://github.com/discordjs/discord.js/issues/5622)) ([b376f31](https://github.com/discordjs/discord.js/commit/b376f31af9881b9cd3f82ac4a42a468947cce482))
* **Emoji:** name can be null ([#5513](https://github.com/discordjs/discord.js/issues/5513)) ([5397021](https://github.com/discordjs/discord.js/commit/5397021efb1f9883cf4b48a0ca78d12b713a61fd))
* **GuildEmojiRoleManager:** bug in #remove ([#5666](https://github.com/discordjs/discord.js/issues/5666)) ([c89bdd7](https://github.com/discordjs/discord.js/commit/c89bdd7566599a95a404b0f9e4b0828a866d0a71))
* **GuildMemberManager:** allow moving members to any non-text channel ([#5681](https://github.com/discordjs/discord.js/issues/5681)) ([d21e6af](https://github.com/discordjs/discord.js/commit/d21e6af1d2b81db9847336b3f964f9d2693394e6))
* **GuildMemberRoleManager:** unable to remove roles when passed an array ([#5556](https://github.com/discordjs/discord.js/issues/5556)) ([9572521](https://github.com/discordjs/discord.js/commit/9572521e3c390e610de8e4dc79e4086b3b1d6e44))
* **HTTPOptions:** change default value in jsdoc to 8 ([#5547](https://github.com/discordjs/discord.js/issues/5547)) ([cb50241](https://github.com/discordjs/discord.js/commit/cb50241e6fa7c95891925c8b18840c17df078620))
* **Interaction:** add missing types and fix docs lists ([#5762](https://github.com/discordjs/discord.js/issues/5762)) ([1d57754](https://github.com/discordjs/discord.js/commit/1d57754d4654c5c95aa14afc13f8abe335314767))
* **Message:** bug in #suppressEmbeds due to [#5612](https://github.com/discordjs/discord.js/issues/5612) ([#5644](https://github.com/discordjs/discord.js/issues/5644)) ([840ad0a](https://github.com/discordjs/discord.js/commit/840ad0a35a344a19c5bb84c421f80802fb186d0b))
* **Message:** editing with MessageEmbed or APIMessage ([#5612](https://github.com/discordjs/discord.js/issues/5612)) ([74e97ef](https://github.com/discordjs/discord.js/commit/74e97ef91b413300c83f163bc3914eaf8bd45d89))
* **Message:** update typings and docs related to #edit ([#5745](https://github.com/discordjs/discord.js/issues/5745)) ([a2f0c11](https://github.com/discordjs/discord.js/commit/a2f0c11474826bfd5b770d2a6990b6bd41c89451))
* **MessageComponentInteraction:** correctly type defer method ([#5760](https://github.com/discordjs/discord.js/issues/5760)) ([f0dad26](https://github.com/discordjs/discord.js/commit/f0dad26a5b8c3139b2519d1895df2fe23352b102))
* **MessageEmbed:** import custom RangeError class ([#5740](https://github.com/discordjs/discord.js/issues/5740)) ([bfe01b5](https://github.com/discordjs/discord.js/commit/bfe01b52ab29df1bb26fc2d385f63fb5adbb12b2))
* **PermissionOverwrites:** fix typo in typedef jsdoc ([#5704](https://github.com/discordjs/discord.js/issues/5704)) ([6567ba8](https://github.com/discordjs/discord.js/commit/6567ba821a54d3dc97d07ce8ac55335fef2f346b))
* **RoleManager:** bug in #create ([#5730](https://github.com/discordjs/discord.js/issues/5730)) ([cf22456](https://github.com/discordjs/discord.js/commit/cf224560bc59c05f7801088b0db2ec76c5369302))
* **ShardingManager:** client error event cannot be emitted ([#5559](https://github.com/discordjs/discord.js/issues/5559)) ([d1c5b6f](https://github.com/discordjs/discord.js/commit/d1c5b6fe9e18b532ad69ed4bd82e1874a6dff4df))
* **Types:** make event listeners accept async callbacks ([#5602](https://github.com/discordjs/discord.js/issues/5602)) ([a73a5cf](https://github.com/discordjs/discord.js/commit/a73a5cf91498cf7b08cea85753ad481c194ec089))
* **Typings:** add missing typings for `HttpError` -> `requestData` ([#5742](https://github.com/discordjs/discord.js/issues/5742)) ([3e9ce35](https://github.com/discordjs/discord.js/commit/3e9ce35023e71ffda1f4eaca6f109b0422ec7d29))
* **Webhook:** #editMessage throws error when content is null ([#5757](https://github.com/discordjs/discord.js/issues/5757)) ([2901fd5](https://github.com/discordjs/discord.js/commit/2901fd595be847a0e6c86155b3229d3341cfad32))
* interfaces not importing due to re-export of Snowflake ([#5723](https://github.com/discordjs/discord.js/issues/5723)) ([086c3f0](https://github.com/discordjs/discord.js/commit/086c3f0799d65c64c4e60d6370246a37a27a1eab))
* lint-staged for typescript files ([5f6ec22](https://github.com/discordjs/discord.js/commit/5f6ec2211d1e6555ab2d501579e4a1d97023c647))
* resolve emoji in Message#react ([#5614](https://github.com/discordjs/discord.js/issues/5614)) ([c733436](https://github.com/discordjs/discord.js/commit/c7334363b36c5f7f1c7880fe77a2e9b2eb1a6442))
* typo in GuildMemberManager ([#5616](https://github.com/discordjs/discord.js/issues/5616)) ([73f4114](https://github.com/discordjs/discord.js/commit/73f4114f59fc2f514d570ac8df3eac7d328cca3c))
* **Sticker:** file renamed to SnowflakeUtil ([#5573](https://github.com/discordjs/discord.js/issues/5573)) ([f830eb7](https://github.com/discordjs/discord.js/commit/f830eb7101d05f90bbbf44ff750e4012ecb72449))
* **VoiceReceiver:** fix memory leak ([#5609](https://github.com/discordjs/discord.js/issues/5609)) ([2eac842](https://github.com/discordjs/discord.js/commit/2eac84296b448907213680690ec766bb5fbe5990))
* typings for 'Message' ([#5518](https://github.com/discordjs/discord.js/issues/5518)) ([0d68ca8](https://github.com/discordjs/discord.js/commit/0d68ca8eb9abdb517acee562fe01374416d225ed))
* **ApiMessage:** remove resolve() from typings ([#5241](https://github.com/discordjs/discord.js/issues/5241)) ([a6bc39d](https://github.com/discordjs/discord.js/commit/a6bc39d3c699eec0b7851cda334335baa892c1de))
* **ApplicationFlags:** export class ([#5465](https://github.com/discordjs/discord.js/issues/5465)) ([404ce57](https://github.com/discordjs/discord.js/commit/404ce57bcc3ce39c807457f25a5679a80e69d1bb))
* **BaseGuildEmoji:** typo in requiresColons ([#5076](https://github.com/discordjs/discord.js/issues/5076)) ([e272fd6](https://github.com/discordjs/discord.js/commit/e272fd6909a17941d2d3e4840e75436d98a41198))
* **ChannelManager:** Avoid crash in remove method with uncached channel ([#4937](https://github.com/discordjs/discord.js/issues/4937)) ([12c909e](https://github.com/discordjs/discord.js/commit/12c909eeccb9ed6ab205d314ac8d63fc58713ae6))
* **Collection:** toJSON() errors if the collection includes empty values ([#5129](https://github.com/discordjs/discord.js/issues/5129)) ([2c2249e](https://github.com/discordjs/discord.js/commit/2c2249ee7314401b65be677c233370657e1d4695))
* **Collector:** throw an error if a non-function was provided as filter ([#5034](https://github.com/discordjs/discord.js/issues/5034)) ([7365f40](https://github.com/discordjs/discord.js/commit/7365f403006eeb28ab10f03cbf85416272678ef7))
* **Guild/GuildChannel:** methods reason arg usage ([#5419](https://github.com/discordjs/discord.js/issues/5419)) ([8411b9e](https://github.com/discordjs/discord.js/commit/8411b9e14211f83fddb00f622088979ee6586803))
* **GuildAuditLogsEntry:** executor can be missing ([#5500](https://github.com/discordjs/discord.js/issues/5500)) ([e9bf206](https://github.com/discordjs/discord.js/commit/e9bf206d88b9307e4098b7f89178105d08b07544))
* **GuildChannel:** check for community required channels in GuildChannel#deletable ([#5170](https://github.com/discordjs/discord.js/issues/5170)) ([b710a43](https://github.com/discordjs/discord.js/commit/b710a432326be823beb10f5f06f2a1e3fcd7c0ee))
* **GuildChannel:** overload permissionsFor and BaseManager#resolve[id] ([#5260](https://github.com/discordjs/discord.js/issues/5260)) ([41bd6c2](https://github.com/discordjs/discord.js/commit/41bd6c2717faeeaa36514d39a4816f7cf65b4c02))
* **GuildChannel:** regression on default channel type ([#5251](https://github.com/discordjs/discord.js/issues/5251)) ([e7c4f36](https://github.com/discordjs/discord.js/commit/e7c4f3672e7059c264ba67a94b87a655ea6e4da5))
* **GuildEmoji:** check for cahnges to available in equals ([#5201](https://github.com/discordjs/discord.js/issues/5201)) ([f95f5dc](https://github.com/discordjs/discord.js/commit/f95f5dcd791b39c6a4d60dc8d64b0287e06ed768))
* **GuildManager:** add missing toString() on Permission#resolve fns ([#5324](https://github.com/discordjs/discord.js/issues/5324)) ([0778926](https://github.com/discordjs/discord.js/commit/077892645bd59d1b5c50e3291701cb4241c0bbdf))
* **GuildMember:** correctly check for premium_since ([#5312](https://github.com/discordjs/discord.js/issues/5312)) ([aff3625](https://github.com/discordjs/discord.js/commit/aff3625d4fc3c738d77325f8492b72077e6345e7))
* **GuildTemplate:** 'guild' getter ([#5040](https://github.com/discordjs/discord.js/issues/5040)) ([53529bd](https://github.com/discordjs/discord.js/commit/53529bd05deb449d5d9bbfa332470c9881d8093c))
* **IntegrationApplication:** add missing export to index.js ([#5475](https://github.com/discordjs/discord.js/issues/5475)) ([8023250](https://github.com/discordjs/discord.js/commit/8023250ee7bb79a5e3f12d7297c29589f91d6b81))
* **InviteDelete:** guild can be missing ([#5457](https://github.com/discordjs/discord.js/issues/5457)) ([6c6b105](https://github.com/discordjs/discord.js/commit/6c6b1053b7a6778a5d0402941a13258ca13378f8))
* **Message:** #system non-zero message types are not guaranteed to be system ([#5108](https://github.com/discordjs/discord.js/issues/5108)) ([bb78120](https://github.com/discordjs/discord.js/commit/bb78120283b671d1926c8707a17a9d4d515aafdd))
* **Message:** update getters to take null permissions into account ([#5066](https://github.com/discordjs/discord.js/issues/5066)) ([98b1c58](https://github.com/discordjs/discord.js/commit/98b1c582189faee9ac40d81963008d94801f3837))
* **MessageEmbed:** include `author.name` in length getter ([#5167](https://github.com/discordjs/discord.js/issues/5167)) ([e37160f](https://github.com/discordjs/discord.js/commit/e37160f4e3d647e8e33b5b03d5f9e6c98b065499))
* **MessageMentions#channels:** Fix type of channels of mentions ([#5370](https://github.com/discordjs/discord.js/issues/5370)) ([565d7b3](https://github.com/discordjs/discord.js/commit/565d7b3747d59ceeb01e2d88b20d761a99927a12))
* **MessageReaction:** set MessageReaction#me in patch method ([#5047](https://github.com/discordjs/discord.js/issues/5047)) ([6b322f4](https://github.com/discordjs/discord.js/commit/6b322f47a0f86115dab71c06c7879fe82ea04ec4))
* **ReactionUserManager:** remove before query option ([#5281](https://github.com/discordjs/discord.js/issues/5281)) ([43bd568](https://github.com/discordjs/discord.js/commit/43bd568f1c38a6df38f56a8d607375ccc9da026a))
* **Resolvables:** valid resolvables throw error when uncached ([#5495](https://github.com/discordjs/discord.js/issues/5495)) ([fa5a37e](https://github.com/discordjs/discord.js/commit/fa5a37e51a14fdd95420092fb8c2acffad132c3b))
* **Role:** pass Permissions class, not the bitfield ([#5321](https://github.com/discordjs/discord.js/issues/5321)) ([d744e51](https://github.com/discordjs/discord.js/commit/d744e51c1bdb4c7a26c0faeea1f2f45baaf5fd3c))
* **RoleManager:** fix ID return value, change return type to collection ([#4935](https://github.com/discordjs/discord.js/issues/4935)) ([12a096b](https://github.com/discordjs/discord.js/commit/12a096b5f1c5ad518e73d1b9f50bb388928117dd))
* **typings:** return types for 'Webhook(Client)#send()' ([#4876](https://github.com/discordjs/discord.js/issues/4876)) ([eb28ee7](https://github.com/discordjs/discord.js/commit/eb28ee7905eee248b9ccd248f7d8275933dd0637))
* **typings:** update GuildMemberRoleManager typings to match implementation ([#5497](https://github.com/discordjs/discord.js/issues/5497)) ([900e576](https://github.com/discordjs/discord.js/commit/900e57657e80833df2557c60862fcd71b35d0df1))
* **UserFlags:** correct early bot dev name, remove deprecated aliases ([#5104](https://github.com/discordjs/discord.js/issues/5104)) ([b509862](https://github.com/discordjs/discord.js/commit/b509862bfa924494824af5e7729bd545315d2c67))
* **Voice:** disconnect if voice channel not cached ([#5467](https://github.com/discordjs/discord.js/issues/5467)) ([3af8179](https://github.com/discordjs/discord.js/commit/3af8179878c093985c8a94ee2fd7e99d45243bda))
* **Voice:** only skip undocumented voice packet byte if present ([#5309](https://github.com/discordjs/discord.js/issues/5309)) ([2b52cdc](https://github.com/discordjs/discord.js/commit/2b52cdc915146c6775bc7565e5ecf31e7c9880e7))
* add presence to ClientPresence from ClientOptions ([#5041](https://github.com/discordjs/discord.js/issues/5041)) ([9c7fe34](https://github.com/discordjs/discord.js/commit/9c7fe34c50808ba080527a1919b1846ed6585d4d))
* filtering of string forms of null and undefined ([#5075](https://github.com/discordjs/discord.js/issues/5075)) ([9042d19](https://github.com/discordjs/discord.js/commit/9042d19c4ef54d9976776f41336610ab0a24db27))
* implement valueOf on pseudomanagers ([#4595](https://github.com/discordjs/discord.js/issues/4595)) ([8883a01](https://github.com/discordjs/discord.js/commit/8883a0144b02e76c767c21ecf28fb430b7223c7f))
* typings for 'WebhookMessageOptions' ([#5476](https://github.com/discordjs/discord.js/issues/5476)) ([67025e6](https://github.com/discordjs/discord.js/commit/67025e63e4e8306a2b3ff62ae1067acf4b468a6a))
* **Voice*:** filter out silent audio from video users ([#5035](https://github.com/discordjs/discord.js/issues/5035)) ([4fcb9eb](https://github.com/discordjs/discord.js/commit/4fcb9ebf300633022e2b9867fa06a586307ff17a))
* **VoiceConnection:** make #dispatcher nullable ([#5217](https://github.com/discordjs/discord.js/issues/5217)) ([0d5de43](https://github.com/discordjs/discord.js/commit/0d5de4333d8afa57826aa75475fc4e3dfe8978c4))
* **WebSocketShard:** key name in WebSocketShard#_send. ([#5304](https://github.com/discordjs/discord.js/issues/5304)) ([56d8b44](https://github.com/discordjs/discord.js/commit/56d8b445ede6c7915aec173a68905cda3d91f0ca)), closes [#3722](https://github.com/discordjs/discord.js/issues/3722)
### Code Refactoring
* removed `code` and `split` options ([#5918](https://github.com/discordjs/discord.js/issues/5918)) ([985d4d6](https://github.com/discordjs/discord.js/commit/985d4d6a438fc9123264f6a1c600f34fccc1825f))
### Features
* **ApplicationCommandOptionType:** add NUMBER (10) ([#6128](https://github.com/discordjs/discord.js/issues/6128)) ([2f1cc1f](https://github.com/discordjs/discord.js/commit/2f1cc1fc27f5af6d9b88cdc353605a3ddf76e579))
* **Channel:** add isThread typeguard for better TS support ([#5978](https://github.com/discordjs/discord.js/issues/5978)) ([b7ed675](https://github.com/discordjs/discord.js/commit/b7ed6752ac98e56e79b6fd9fd3a5e47572454d85))
* **Channel:** add isVoice() ([#6297](https://github.com/discordjs/discord.js/issues/6297)) ([5b4efd1](https://github.com/discordjs/discord.js/commit/5b4efd13c9eced97f4160f9c4c19d1c843360943))
* **Client:** add conditional ready typings ([#6073](https://github.com/discordjs/discord.js/issues/6073)) ([4206e35](https://github.com/discordjs/discord.js/commit/4206e35b2316431c1a009664636dcda85d39fff8))
* **Client:** enforce passing scopes to generateInvite ([#6024](https://github.com/discordjs/discord.js/issues/6024)) ([c6e5521](https://github.com/discordjs/discord.js/commit/c6e55216874f1892727db927f55db3de6a1712d8))
* **Collector:** better types for events ([#6058](https://github.com/discordjs/discord.js/issues/6058)) ([c0a814f](https://github.com/discordjs/discord.js/commit/c0a814fdb35cb1fa7418bb3bdd3cec0a8a130bf5))
* **Collector:** return a boolean on checkEnd ([#6289](https://github.com/discordjs/discord.js/issues/6289)) ([f473f43](https://github.com/discordjs/discord.js/commit/f473f43d081c8c703a0888acaf3fdbaf4bb20c6d))
* **CommandInteraction:** add CommandInteractionOptionResolver ([#6107](https://github.com/discordjs/discord.js/issues/6107)) ([f293132](https://github.com/discordjs/discord.js/commit/f293132345294e33e80866272feaedf2e4a70d45))
* **CommandInteractionOptionResolver:** add sub-command required option ([#6165](https://github.com/discordjs/discord.js/issues/6165)) ([690c121](https://github.com/discordjs/discord.js/commit/690c121aa9575f7b878030229b0fedd56c3a1a87))
* **Constants:** added more error codes ([#6234](https://github.com/discordjs/discord.js/issues/6234)) ([6b2098f](https://github.com/discordjs/discord.js/commit/6b2098f7c7592eee2aedbf19e62d3c262e159cba))
* **FetchRecommendedShardsOptions:** account for large bot sharding ([#6184](https://github.com/discordjs/discord.js/issues/6184)) ([19b242a](https://github.com/discordjs/discord.js/commit/19b242ac10aa9b32c1a45a9178c97481d62a9400))
* **Formatters:** added new URL utilities and docs ([#6014](https://github.com/discordjs/discord.js/issues/6014)) ([98e45a5](https://github.com/discordjs/discord.js/commit/98e45a59957842fd1ee55d59e30ee868f985e15d))
* **Guild:** add fetchWidget() for getting widget data ([#6180](https://github.com/discordjs/discord.js/issues/6180)) ([b22272f](https://github.com/discordjs/discord.js/commit/b22272f86075ac5585abf05f54cf05187c4eabf9))
* **GuildAuditLogs:** add threads ([#6195](https://github.com/discordjs/discord.js/issues/6195)) ([26ba0e1](https://github.com/discordjs/discord.js/commit/26ba0e10368ff7c4b7a79a37450c8c0784185fb7))
* **GuildPreview:** add createdAt & createdTimestamp ([#6130](https://github.com/discordjs/discord.js/issues/6130)) ([9f039a8](https://github.com/discordjs/discord.js/commit/9f039a86798352e360d7e47d62d1b9c011c2ec71))
* **InteractionCollector:** reworked to be more generic ([#5999](https://github.com/discordjs/discord.js/issues/5999)) ([374c779](https://github.com/discordjs/discord.js/commit/374c779f7f8bbaa9bf06fa2b5b16f60da5095b5c))
* **Interactions:** option to auto-fetch replies ([#5831](https://github.com/discordjs/discord.js/issues/5831)) ([5e28ff8](https://github.com/discordjs/discord.js/commit/5e28ff83cbc04850077cc2f97bb2039c55b3b8ea))
* **LimitedCollection:** export LimitedCollection ([#6043](https://github.com/discordjs/discord.js/issues/6043)) ([31d3129](https://github.com/discordjs/discord.js/commit/31d31293d314492562104f02511d4d1e117711f3))
* **Managers:** ✨ Add GuildInviteManager ([#5889](https://github.com/discordjs/discord.js/issues/5889)) ([9e08b02](https://github.com/discordjs/discord.js/commit/9e08b02df2c9b31cfd91eac3ad008dab94855a59))
* **Managers:** add customizable caching for managers ([#6013](https://github.com/discordjs/discord.js/issues/6013)) ([8c7cb0e](https://github.com/discordjs/discord.js/commit/8c7cb0eff8e169836decf3c9843d7fa0998a5e84))
* **Message:** add 'failIfNotExists' to ClientOptions ([#6038](https://github.com/discordjs/discord.js/issues/6038)) ([28c5724](https://github.com/discordjs/discord.js/commit/28c57246d1d3cd7a22384ddc7970ab5263a4ace0))
* **MessageEmbed:** add setFields method ([#6186](https://github.com/discordjs/discord.js/issues/6186)) ([a25e165](https://github.com/discordjs/discord.js/commit/a25e16599a2b3d82aabebcaeaef663680f2982a8))
* **REST:** append additional information to the required User Agent ([#6112](https://github.com/discordjs/discord.js/issues/6112)) ([f200f14](https://github.com/discordjs/discord.js/commit/f200f14a409a56df5efe788de0ae45fc061bb46d))
* **RoleManager:** added `edit` method, alias `Role#edit` ([#5983](https://github.com/discordjs/discord.js/issues/5983)) ([1e73c25](https://github.com/discordjs/discord.js/commit/1e73c25fbfc9b3cb62bed719dc79de25f67707ee))
* **StageChannel:** add createStageInstance method & use better naming convention ([#5951](https://github.com/discordjs/discord.js/issues/5951)) ([71fb33a](https://github.com/discordjs/discord.js/commit/71fb33a5fea7398598b603a888e07519fddd56a9))
* **Sticker:** updates, sticker packs, and guild stickers ([#5867](https://github.com/discordjs/discord.js/issues/5867)) ([54d6a3a](https://github.com/discordjs/discord.js/commit/54d6a3a0708105acd6a3a709a8e1636d00c81fc8))
* **ThreadChannel:** add fetchOwner() method ([#6207](https://github.com/discordjs/discord.js/issues/6207)) ([331a9d3](https://github.com/discordjs/discord.js/commit/331a9d3ffc6e45c068bfb454e05b863130559d42))
* **Util:** add SweptCollection for auto sweeping of caches ([#6110](https://github.com/discordjs/discord.js/issues/6110)) ([dbb59ba](https://github.com/discordjs/discord.js/commit/dbb59ba1b29b2f75dd8faab5c3004ade51598abc))
* **Util:** added formatters ([#5976](https://github.com/discordjs/discord.js/issues/5976)) ([8c7a28f](https://github.com/discordjs/discord.js/commit/8c7a28f211dd05ec67cbce667b1d591ed59a40c6))
* **WebhookClient:** allow creation of clients via URLs ([#6192](https://github.com/discordjs/discord.js/issues/6192)) ([e000af5](https://github.com/discordjs/discord.js/commit/e000af5c98483046db25a46e905ed244bdcfe262))
* allow channels from uncached guilds to be returned from fetch ([#6034](https://github.com/discordjs/discord.js/issues/6034)) ([755c180](https://github.com/discordjs/discord.js/commit/755c180659c125532fe6f8e33e6c3b56e275311b))
* make Instance#fetch force true by default, and fix force parameter ([#6116](https://github.com/discordjs/discord.js/issues/6116)) ([366f3c9](https://github.com/discordjs/discord.js/commit/366f3c910a370ff1e184afae054f957db9a98293))
* PermissionOverwriteManager ([#5318](https://github.com/discordjs/discord.js/issues/5318)) ([e7ad2fe](https://github.com/discordjs/discord.js/commit/e7ad2fe20772915dcf3e9c4ae92a072b9c918a07))
* use enums for consistency and speed ([#5843](https://github.com/discordjs/discord.js/issues/5843)) ([f7eeccb](https://github.com/discordjs/discord.js/commit/f7eeccba4b7015496df811f10cc2da2b0fab0630))
* **Client:** make use of with_expiration in #fetchInvite ([#5764](https://github.com/discordjs/discord.js/issues/5764)) ([bf191df](https://github.com/discordjs/discord.js/commit/bf191df9c033404da3e717f73306cdb3f659fafc))
* **Esm:** use `gen-esm-wrapper` instead of manually making the file ([#5700](https://github.com/discordjs/discord.js/issues/5700)) ([db0d7d4](https://github.com/discordjs/discord.js/commit/db0d7d4ea8e7b2bae4d1548e5617875b5ae0bbd4))
* **Interaction:** add guild guard ([#5955](https://github.com/discordjs/discord.js/issues/5955)) ([87e8cdd](https://github.com/discordjs/discord.js/commit/87e8cdd3eba29ae1d741aa86572f1731b05c12fb))
* **Managers:** new ApplicationCommandPermissionsManager ([#5897](https://github.com/discordjs/discord.js/issues/5897)) ([6264c60](https://github.com/discordjs/discord.js/commit/6264c60e97da93b311a7a9fd92e16e59de94104a))
* **MessageComponentInteraction:** component getter ([#5840](https://github.com/discordjs/discord.js/issues/5840)) ([1439183](https://github.com/discordjs/discord.js/commit/1439183ad3f84b2b7500aaead2cf8779199b47d4))
* **MessageMentions:** add repliedUser ([#5905](https://github.com/discordjs/discord.js/issues/5905)) ([2616125](https://github.com/discordjs/discord.js/commit/261612596d37aa6fb48ae070d358b3fde953c769))
* **MessageSelectMenu:** droppybois ([#5692](https://github.com/discordjs/discord.js/issues/5692)) ([e5fcf0b](https://github.com/discordjs/discord.js/commit/e5fcf0bee53a15d7a87d4a5cf4e206823d6e7d87))
* api v9 and threads ([#5570](https://github.com/discordjs/discord.js/issues/5570)) ([7346621](https://github.com/discordjs/discord.js/commit/7346621d15c96906d5b848c483669750ff9c6e12))
* **InteractionCreate:** move to an Action handler ([#5906](https://github.com/discordjs/discord.js/issues/5906)) ([ea49f7c](https://github.com/discordjs/discord.js/commit/ea49f7ca74892495dd53f8d315086035c1814149))
* add missing APIError codes ([#5898](https://github.com/discordjs/discord.js/issues/5898)) ([d930c81](https://github.com/discordjs/discord.js/commit/d930c812bb4511a688b76d9bf1ac66e28bff033e))
* **GuildChannelManager:** add 'fetch' method ([#4966](https://github.com/discordjs/discord.js/issues/4966)) ([e798fb7](https://github.com/discordjs/discord.js/commit/e798fb720ee5ced008471fe899337f6817936770))
* **Interactions:** add InteractionWebhook for better internals ([#5712](https://github.com/discordjs/discord.js/issues/5712)) ([dec191a](https://github.com/discordjs/discord.js/commit/dec191aa1e4f22690285ca06c6eee7e6086b2930))
* **Interactions:** improve error handling for ephemeral responses ([#5892](https://github.com/discordjs/discord.js/issues/5892)) ([bd9f56a](https://github.com/discordjs/discord.js/commit/bd9f56af9a0a1fb12cfa30d9e2e0ad680eb80949))
* add APIError codes for stage instance ([#5888](https://github.com/discordjs/discord.js/issues/5888)) ([c850ae1](https://github.com/discordjs/discord.js/commit/c850ae10270076c4b2e10b130dd8f88eed4ed201))
* **Message:** applicationID for interaction responses ([#5765](https://github.com/discordjs/discord.js/issues/5765)) ([65dc00f](https://github.com/discordjs/discord.js/commit/65dc00f3210065015684b6d585f6747bd5ebadf1))
* **MessageComponents:** clickybois (MessageButton, MessageActionRow, associated Collectors) ([#5674](https://github.com/discordjs/discord.js/issues/5674)) ([cbd7f2b](https://github.com/discordjs/discord.js/commit/cbd7f2b9aa44a9240947ed716d0e72257ac499f7))
* **Rest:** optional ratelimit errors ([#5659](https://github.com/discordjs/discord.js/issues/5659)) ([16f261e](https://github.com/discordjs/discord.js/commit/16f261e773a353c54a75f38008f9b28435ae6603))
* **Sharding*:** contexts for broadcastEval ([#5756](https://github.com/discordjs/discord.js/issues/5756)) ([c6aeebb](https://github.com/discordjs/discord.js/commit/c6aeebb18d6b969f7c8bdb1b719883d4384dd03e))
* **WelcomeScreen:** welcome screens ([#5490](https://github.com/discordjs/discord.js/issues/5490)) ([44e2ee7](https://github.com/discordjs/discord.js/commit/44e2ee7b20dbec79c993dbc1f30ddb643d943347))
* stage instance invite ([#5856](https://github.com/discordjs/discord.js/issues/5856)) ([2d12db0](https://github.com/discordjs/discord.js/commit/2d12db000f2a0a22a8919d7a63989a6e762ae335))
* document and support embeds field in message create endpoint ([#5792](https://github.com/discordjs/discord.js/issues/5792)) ([99ff715](https://github.com/discordjs/discord.js/commit/99ff7151379fe03a1cfd52f252c0e6fc892d7776))
* **Guild:** add enum for mfa_level ([#5797](https://github.com/discordjs/discord.js/issues/5797)) ([ffabec3](https://github.com/discordjs/discord.js/commit/ffabec3a5e3651e5a0b8bcac83ee26bb909695fa))
* **Guild:** add enum for premium_tier ([#5868](https://github.com/discordjs/discord.js/issues/5868)) ([a3cbcca](https://github.com/discordjs/discord.js/commit/a3cbcca13da1af416c219bd64a0a6e84bb87a057))
* **GuildAuditLogs:** make #target a channel for channel related logs ([#5781](https://github.com/discordjs/discord.js/issues/5781)) ([eb0291d](https://github.com/discordjs/discord.js/commit/eb0291d9a5078836183c1b63ea96461ec112f96e))
* **RequestHandler:** emit more info when a rate limit was hit ([#5801](https://github.com/discordjs/discord.js/issues/5801)) ([18ac72e](https://github.com/discordjs/discord.js/commit/18ac72e457fa137d7f7f7bde876436ff643b4a63))
* **Widget:** wrapper for widget.json ([#5619](https://github.com/discordjs/discord.js/issues/5619)) ([038ee99](https://github.com/discordjs/discord.js/commit/038ee99604cded41d4c67edf4bd6bc7969712f52))
* add new APIErrors ([#5794](https://github.com/discordjs/discord.js/issues/5794)) ([e0ab836](https://github.com/discordjs/discord.js/commit/e0ab836b2d88caf0d9e1f9eba76ae46be9df0554))
* enforce strings ([#4880](https://github.com/discordjs/discord.js/issues/4880)) ([7b85a72](https://github.com/discordjs/discord.js/commit/7b85a7259f563ab14ae6c0a665a3dd43c486fde4))
* **APIRequest:** support setting global headers in HTTPOptions ([#5586](https://github.com/discordjs/discord.js/issues/5586)) ([135abcc](https://github.com/discordjs/discord.js/commit/135abccd9c75c33c8510cdcbe33b0dea4198fe33))
* **CommandInteraction:** ephemeral followup messages ([#5618](https://github.com/discordjs/discord.js/issues/5618)) ([68b40dd](https://github.com/discordjs/discord.js/commit/68b40dd91df70593c8271bd455fd0b3c6d19d334))
* **CommandInteraction:** make options a collection ([#5705](https://github.com/discordjs/discord.js/issues/5705)) ([fdad140](https://github.com/discordjs/discord.js/commit/fdad14099779e61cb84dcd1cb2497e0e853a6144))
* **Guild:** add the new nsfw_level property ([#5660](https://github.com/discordjs/discord.js/issues/5660)) ([3fe7add](https://github.com/discordjs/discord.js/commit/3fe7add2c5c07023d3cc83c06bba846c1328e446))
* **GuildChannel:** createInvite target options ([#5514](https://github.com/discordjs/discord.js/issues/5514)) ([f831872](https://github.com/discordjs/discord.js/commit/f831872125214e39c8866ce1cf7c63159a3dba39))
* **GuildChannel:** make createOverwrite and updateOverwrite not dependent on cache ([#5489](https://github.com/discordjs/discord.js/issues/5489)) ([58763b0](https://github.com/discordjs/discord.js/commit/58763b0e91b78d068121521ea3e853627b3ea325))
* **GuildChannel#clone:** support for position property ([#5236](https://github.com/discordjs/discord.js/issues/5236)) ([d455cb6](https://github.com/discordjs/discord.js/commit/d455cb65a6188e7d7b6720848b5ce37dbf8b5dff))
* **GuildMemberManager:** extend API coverage ([#4872](https://github.com/discordjs/discord.js/issues/4872)) ([2e2464b](https://github.com/discordjs/discord.js/commit/2e2464bf07c2b2e08d396b093126f887d19aec57))
* **Message:** add ReplyMessageOptions for #reply ([#5296](https://github.com/discordjs/discord.js/issues/5296)) ([7ce741d](https://github.com/discordjs/discord.js/commit/7ce741dacd06fd8af0ab501e38be08cf6b506a62))
* **Message:** allow editing files into messages ([#5718](https://github.com/discordjs/discord.js/issues/5718)) ([b212b64](https://github.com/discordjs/discord.js/commit/b212b64214ecee4f6118e78f9b90f3d3da574ecc))
* **Rest:** show the data that is sent to Discord when an errors occurs ([#5701](https://github.com/discordjs/discord.js/issues/5701)) ([ef92339](https://github.com/discordjs/discord.js/commit/ef92339d073f82cdaa2bc69f7be8443ec16789a7))
* add support for fetching multiple guilds ([#5472](https://github.com/discordjs/discord.js/issues/5472)) ([48d6850](https://github.com/discordjs/discord.js/commit/48d6850d9a8c34f407a22b6b401f2ed74415acd0))
* easier guards for channelUpdate ([#5716](https://github.com/discordjs/discord.js/issues/5716)) ([d52bcd4](https://github.com/discordjs/discord.js/commit/d52bcd46ec5985f9f18da37ba9d7d77209f58337))
* general component improvements ([#5787](https://github.com/discordjs/discord.js/issues/5787)) ([c4f1c75](https://github.com/discordjs/discord.js/commit/c4f1c75efa1cff1f9c775a266dccbe581305e79d))
* GuildBanManager ([#5276](https://github.com/discordjs/discord.js/issues/5276)) ([6d09160](https://github.com/discordjs/discord.js/commit/6d09160f5ba878fcd1f8bae88b5e6347e632cd2c))
* InteractionDeferOptions ([#5641](https://github.com/discordjs/discord.js/issues/5641)) ([ed593c9](https://github.com/discordjs/discord.js/commit/ed593c91fb7b87ae8b512c6f127e12f33c9631b6))
* **Guild:** allow description and features in edit ([#5505](https://github.com/discordjs/discord.js/issues/5505)) ([8a059cc](https://github.com/discordjs/discord.js/commit/8a059cccb8ecbd0bf60d2ed395a8de0806b3395a))
* **Guild:** nsfw guilds ([#5525](https://github.com/discordjs/discord.js/issues/5525)) ([5968323](https://github.com/discordjs/discord.js/commit/596832371cefef7739e8d714248d1c6d438eb8df))
* **Guild:** replace `owner` with `fetchOwner` ([#5480](https://github.com/discordjs/discord.js/issues/5480)) ([1be67b8](https://github.com/discordjs/discord.js/commit/1be67b88516b104073e46574a180498bad2aa02b))
* **Guild:** setChannelPositions parent, lockPermissions keys ([#5507](https://github.com/discordjs/discord.js/issues/5507)) ([4866e26](https://github.com/discordjs/discord.js/commit/4866e2672f28bfc481cf03533f7ba259050c80f4))
* **GuildFeatures:** added the new screening features ([#5328](https://github.com/discordjs/discord.js/issues/5328)) ([3c175cb](https://github.com/discordjs/discord.js/commit/3c175cb5116fe50ba3084163565dd244a25b657f))
* **GuildManager:** allow system channel flags in create ([#5504](https://github.com/discordjs/discord.js/issues/5504)) ([d9fa180](https://github.com/discordjs/discord.js/commit/d9fa180cf93f1a339192ae95dfb512482bd0ed0b))
* **Message:** allow removing attachments ([#5557](https://github.com/discordjs/discord.js/issues/5557)) ([ca9e5a0](https://github.com/discordjs/discord.js/commit/ca9e5a0ee1afca544192df1daef744d5a35f1727))
* **Message:** replace referencedMessage with fetchReference ([#5577](https://github.com/discordjs/discord.js/issues/5577)) ([1398431](https://github.com/discordjs/discord.js/commit/1398431bca9a3743758295f1effa2e7f6c35093e))
* **MessageEmbed:** remove normalizeField validation ([#5459](https://github.com/discordjs/discord.js/issues/5459)) ([ff2f737](https://github.com/discordjs/discord.js/commit/ff2f7372f23f901620d3afff215f33be487521d5))
* **MessageManager:** extend API coverage ([#4869](https://github.com/discordjs/discord.js/issues/4869)) ([c56c4a8](https://github.com/discordjs/discord.js/commit/c56c4a8dc86b0f37dd7c9ee9a4d422a52070b50c))
* **ShardingManager:** add options typings ([#5583](https://github.com/discordjs/discord.js/issues/5583)) ([31b4390](https://github.com/discordjs/discord.js/commit/31b4390042e6557f7f9d2f258c79ea50ba4929e2))
* add support for application command events ([#5596](https://github.com/discordjs/discord.js/issues/5596)) ([9f74f95](https://github.com/discordjs/discord.js/commit/9f74f95f69f4aa8a9a23c160e25dc61010c0a8e0))
* interactions ([#5448](https://github.com/discordjs/discord.js/issues/5448)) ([f7643f7](https://github.com/discordjs/discord.js/commit/f7643f7bbe64003ad8b221006190dd15529651e9))
* **Activity:** add missing fields ([#4984](https://github.com/discordjs/discord.js/issues/4984)) ([63ff6a0](https://github.com/discordjs/discord.js/commit/63ff6a07ebcba7e9134e3902e338e8dc6564ee63))
* **APIMessage:** remove disableMentions ([#4836](https://github.com/discordjs/discord.js/issues/4836)) ([4107899](https://github.com/discordjs/discord.js/commit/41078997aefce2a9e683b9805aad6436612a3aa7))
* **Application:** application flags ([#5147](https://github.com/discordjs/discord.js/issues/5147)) ([06e9d86](https://github.com/discordjs/discord.js/commit/06e9d86cb3dd11708c9cdd81f15970979e5b090d))
* **BitField:** move problematic bit into the error message ([#5228](https://github.com/discordjs/discord.js/issues/5228)) ([273e955](https://github.com/discordjs/discord.js/commit/273e9557be68eb1c2466f29e1c41e9b146a777c1))
* **Client:** add InviteGenerationOptions#additionalScopes ([#5215](https://github.com/discordjs/discord.js/issues/5215)) ([ae3c3d8](https://github.com/discordjs/discord.js/commit/ae3c3d80ee603fc46a28140107cb90c81da0afc9))
* **ClientEvents:** add tuple labels to event arguments ([#5225](https://github.com/discordjs/discord.js/issues/5225)) ([764966e](https://github.com/discordjs/discord.js/commit/764966e398e693a5ec868bc22d722f8518656b3a))
* **GuildChannel:** support conversion between text and news ([#5022](https://github.com/discordjs/discord.js/issues/5022)) ([5ac3b57](https://github.com/discordjs/discord.js/commit/5ac3b57f9bd53d1c20549a70942b023826f6f726))
* **GuildEmojiManager:** implement GuildEmojiManager#fetch ([#4933](https://github.com/discordjs/discord.js/issues/4933)) ([ffe3140](https://github.com/discordjs/discord.js/commit/ffe31405ff559202be55473db7e8b34894fbf4a7))
* **GuildMember:** #pending ([#5121](https://github.com/discordjs/discord.js/issues/5121)) ([c4c8171](https://github.com/discordjs/discord.js/commit/c4c817116f868cedb4ec20bcbf90b9b3d382621e))
* **GuildMember:** make GuildMember#setNickname first param nullable ([#5070](https://github.com/discordjs/discord.js/issues/5070)) ([d70127c](https://github.com/discordjs/discord.js/commit/d70127cee69e66e87702a6db4b58ad12aa85f96c))
* **GuildMemberManager:** add 'search' method ([#4154](https://github.com/discordjs/discord.js/issues/4154)) ([0ba2bcb](https://github.com/discordjs/discord.js/commit/0ba2bcb54582b38ee8eec8d1547b979bf1b7c755))
* **GuildMemberManager:** throw TypeError on incorrect GuildMemberManager#ban params ([#4816](https://github.com/discordjs/discord.js/issues/4816)) ([863734a](https://github.com/discordjs/discord.js/commit/863734aba46c5e0d04fbc83d2ed314726bddcbc2))
* **Message:** added string type for message nonce ([#4782](https://github.com/discordjs/discord.js/issues/4782)) ([4b555fd](https://github.com/discordjs/discord.js/commit/4b555fdf4c3b35fa0ea284f9cd56765ecb608b89))
* **MessageAttachment:** support for #contentType ([#5481](https://github.com/discordjs/discord.js/issues/5481)) ([7b161f9](https://github.com/discordjs/discord.js/commit/7b161f93a040a6bdce6e7e26d7a3c3b6c61a04fd))
* **SysChanFlags:** new flag and rename in sync with client ([#5506](https://github.com/discordjs/discord.js/issues/5506)) ([fe93a7e](https://github.com/discordjs/discord.js/commit/fe93a7e084189b54b8af82461809dee1da112b75))
* move internal regular expressions to static properties ([#5384](https://github.com/discordjs/discord.js/issues/5384)) ([207735c](https://github.com/discordjs/discord.js/commit/207735cedcf9a998571a328c7c7b2414d3ebe9d5))
* **Message|TextChannel:** Inline replies ([#4874](https://github.com/discordjs/discord.js/issues/4874)) ([60e5a0e](https://github.com/discordjs/discord.js/commit/60e5a0e46f57cf297b66f1a940d24a20f46b5319))
* **ReactionCollector:** event create ([#4108](https://github.com/discordjs/discord.js/issues/4108)) ([09d1f2f](https://github.com/discordjs/discord.js/commit/09d1f2f18f5ec536bb25156553986fee51c80d1e)), closes [#2844](https://github.com/discordjs/discord.js/issues/2844)
* **Rest:** better handling of global rate limit and invalid request tracking ([#4711](https://github.com/discordjs/discord.js/issues/4711)) ([9d2d606](https://github.com/discordjs/discord.js/commit/9d2d60691eb4bde729f40fb633ae257cf5bc6545))
* **typings:** add ShardingManager.shardList to type definitions ([#5446](https://github.com/discordjs/discord.js/issues/5446)) ([32b0d71](https://github.com/discordjs/discord.js/commit/32b0d71af7e3afc401898753b1e8cb1e991b70e7))
* **typings:** explicitly type PremiumTier and Collectors ([#5458](https://github.com/discordjs/discord.js/issues/5458)) ([7c49612](https://github.com/discordjs/discord.js/commit/7c49612d4bedfe13f7ed676c125cc7f7f33596df))
* jsdelivr default file support ([#5424](https://github.com/discordjs/discord.js/issues/5424)) ([f469402](https://github.com/discordjs/discord.js/commit/f46940228e9f82db4af09ae2f2dad684db0d74ed))
* make changes to PresenceData typings and docs ([#5317](https://github.com/discordjs/discord.js/issues/5317)) ([eb43ce4](https://github.com/discordjs/discord.js/commit/eb43ce4d4fb4d634696c5b0f026174dc0e435fe3))
* promisified single interaction collection ([#5770](https://github.com/discordjs/discord.js/issues/5770)) ([c2b3ed0](https://github.com/discordjs/discord.js/commit/c2b3ed09a0ec7f9b7453d0bcf9f2900e408f5001))
* **MessageTypes:** add 16 and 17 ([#4685](https://github.com/discordjs/discord.js/issues/4685)) ([c9107e3](https://github.com/discordjs/discord.js/commit/c9107e35fa8b74f8ad7a7d3ee7d7178a35790e18))
* stage channels ([#5456](https://github.com/discordjs/discord.js/issues/5456)) ([eec7cf7](https://github.com/discordjs/discord.js/commit/eec7cf7634653fc02ee4f94e970960174a0e6d1b))
* stage instances ([#5749](https://github.com/discordjs/discord.js/issues/5749)) ([918921e](https://github.com/discordjs/discord.js/commit/918921e8211fc16e9b12d2502f3168264246ea22))
* **Browser:** remove browser <20> ([#5113](https://github.com/discordjs/discord.js/issues/5113)) ([0a591a9](https://github.com/discordjs/discord.js/commit/0a591a96974ab8b2aef7d7b9b64ec63d0fbe4ec4))
* **Role:** role tags ([#4628](https://github.com/discordjs/discord.js/issues/4628)) ([d6234b7](https://github.com/discordjs/discord.js/commit/d6234b764ecbf12ebc0a795429a6aa3a650f5a6c)), closes [#1](https://github.com/discordjs/discord.js/issues/1)
* **ShardingManager:** Allow b-Eval/fetchClientValues on a specific shard when not all are ready ([#5222](https://github.com/discordjs/discord.js/issues/5222)) ([001676c](https://github.com/discordjs/discord.js/commit/001676c7a97f4e44c6601dd84aa0354ea94b7c25))
* **Sticker:** added Sticker ([#4909](https://github.com/discordjs/discord.js/issues/4909)) ([026afc2](https://github.com/discordjs/discord.js/commit/026afc2c1a88bc210c973bcf235fef3484571111))
* **Util:** allow array for StringOptions' char ([#5566](https://github.com/discordjs/discord.js/issues/5566)) ([fbcbb29](https://github.com/discordjs/discord.js/commit/fbcbb29884a35308a7af2169f5f9ae5658c458e8))
* **Util:** make `cleanContent` take a channel instead of a message ([#5535](https://github.com/discordjs/discord.js/issues/5535)) ([f1c0c04](https://github.com/discordjs/discord.js/commit/f1c0c043b516f4158ab9d473419e3b5e125a4c03))
* **Voice:** implement support for @discordjs/voice ([#5402](https://github.com/discordjs/discord.js/issues/5402)) ([7b2e12b](https://github.com/discordjs/discord.js/commit/7b2e12b102984abf61132e1057558ef7f04e6d83))
* **Webhook:** add '(edit|delete)Message' methods ([#5223](https://github.com/discordjs/discord.js/issues/5223)) ([7cabc1c](https://github.com/discordjs/discord.js/commit/7cabc1c490ddd9518528e12a58a746d65e43d4eb))
* **Webhook:** add 'fetchMessage' method ([#5530](https://github.com/discordjs/discord.js/issues/5530)) ([63398d6](https://github.com/discordjs/discord.js/commit/63398d6ae46f0487c4d5d8bfe823952a803e4a5a))
* **Webhook:** sourceGuild, sourceChannel, improve owner ([#5508](https://github.com/discordjs/discord.js/issues/5508)) ([116ecf2](https://github.com/discordjs/discord.js/commit/116ecf246e89db4d629a13877a440260c7504e30))
* **WebSocketManager:** let identify throw on depleted limits ([#5283](https://github.com/discordjs/discord.js/issues/5283)) ([624a446](https://github.com/discordjs/discord.js/commit/624a4464ca86bfa0b095ecb2cdaac2e8030cc413))
* BaseGuildEmojiManager ([#4934](https://github.com/discordjs/discord.js/issues/4934)) ([8d650a7](https://github.com/discordjs/discord.js/commit/8d650a72509a3f369ae31ec421d1892d182175e4))
### Reverts
* support for nested arrays of components, fix error handling ([#6081](https://github.com/discordjs/discord.js/issues/6081)) ([1dcad05](https://github.com/discordjs/discord.js/commit/1dcad051a835407bc24de3446dbd0ac3c0efeefc))
* **BitField:** ⏪ Bring back-compatibility after BitField serialization ([#5910](https://github.com/discordjs/discord.js/issues/5910)) ([0a0630c](https://github.com/discordjs/discord.js/commit/0a0630c0498d8ae24e703a2bfdf978541deb9b60))
* 5047 ([#5050](https://github.com/discordjs/discord.js/issues/5050)) ([b2a6720](https://github.com/discordjs/discord.js/commit/b2a672047745b0a47729ef775482e06a20b38db3))
### BREAKING CHANGES
For breaking changes please reference: <https://discordjs.guide/additional-info/changes-in-v13.html>

View File

@@ -175,7 +175,7 @@
END OF TERMS AND CONDITIONS
Copyright 2015 - 2021 Amish Shah
Copyright 2017 Amish Shah
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.

105
README.md
View File

@@ -5,18 +5,20 @@
</p>
<br />
<p>
<a href="https://discord.gg/djs"><img src="https://img.shields.io/discord/222078108977594368?color=5865F2&logo=discord&logoColor=white" alt="Discord server" /></a>
<a href="https://discord.gg/bRCvFy9"><img src="https://discordapp.com/api/guilds/222078108977594368/embed.png" alt="Discord server" /></a>
<a href="https://www.npmjs.com/package/discord.js"><img src="https://img.shields.io/npm/v/discord.js.svg?maxAge=3600" alt="NPM version" /></a>
<a href="https://www.npmjs.com/package/discord.js"><img src="https://img.shields.io/npm/dt/discord.js.svg?maxAge=3600" alt="NPM downloads" /></a>
<a href="https://github.com/discordjs/discord.js/actions"><img src="https://github.com/discordjs/discord.js/workflows/Testing/badge.svg" alt="Build status" /></a>
<a href="https://www.patreon.com/discordjs"><img src="https://img.shields.io/badge/donate-patreon-F96854.svg" alt="Patreon" /></a>
<a href="https://travis-ci.org/discordjs/discord.js"><img src="https://travis-ci.org/discordjs/discord.js.svg" alt="Build status" /></a>
<a href="https://david-dm.org/discordjs/discord.js"><img src="https://img.shields.io/david/discordjs/discord.js.svg?maxAge=3600" alt="Dependencies" /></a>
</p>
<p>
<a href="https://nodei.co/npm/discord.js/"><img src="https://nodei.co/npm/discord.js.png?downloads=true&stars=true" alt="NPM info" /></a>
</p>
</div>
## About
discord.js is a powerful [Node.js](https://nodejs.org) module that allows you to easily interact with the
[Discord API](https://discord.com/developers/docs/intro).
discord.js is a powerful [node.js](https://nodejs.org) module that allows you to interact with the
[Discord API](https://discordapp.com/developers/docs/intro) very easily.
- Object-oriented
- Predictable abstractions
@@ -24,65 +26,37 @@ discord.js is a powerful [Node.js](https://nodejs.org) module that allows you to
- 100% coverage of the Discord API
## Installation
**Node.js 6.0.0 or newer is required.**
Ignore any warnings about unmet peer dependencies, as they're all optional.
**Node.js 16.6.0 or newer is required.**
Without voice support: `npm install discord.js`
With voice support ([@discordjs/opus](https://www.npmjs.com/package/@discordjs/opus)): `npm install discord.js @discordjs/opus`
With voice support ([opusscript](https://www.npmjs.com/package/opusscript)): `npm install discord.js opusscript`
```sh-session
npm install discord.js
```
### Audio engines
The preferred audio engine is @discordjs/opus, as it performs significantly better than opusscript. When both are available, discord.js will automatically choose @discordjs/opus.
Using opusscript is only recommended for development environments where @discordjs/opus is tough to get working.
For production bots, using @discordjs/opus should be considered a necessity, especially if they're going to be running on multiple servers.
### Optional packages
- [zlib-sync](https://www.npmjs.com/package/zlib-sync) for WebSocket data compression and inflation (`npm install zlib-sync`)
- [erlpack](https://github.com/discord/erlpack) for significantly faster WebSocket data (de)serialisation (`npm install discord/erlpack`)
- [bufferutil](https://www.npmjs.com/package/bufferutil) for a much faster WebSocket connection (`npm install bufferutil`)
- [utf-8-validate](https://www.npmjs.com/package/utf-8-validate) in combination with `bufferutil` for much faster WebSocket processing (`npm install utf-8-validate`)
- [@discordjs/voice](https://github.com/discordjs/voice) for interacting with the Discord Voice API
- [bufferutil](https://www.npmjs.com/package/bufferutil) to greatly speed up the WebSocket when *not* using uws (`npm install bufferutil`)
- [erlpack](https://github.com/hammerandchisel/erlpack) for significantly faster WebSocket data (de)serialisation (`npm install hammerandchisel/erlpack`)
- One of the following packages can be installed for faster voice packet encryption and decryption:
- [sodium](https://www.npmjs.com/package/sodium) (`npm install sodium`)
- [libsodium.js](https://www.npmjs.com/package/libsodium-wrappers) (`npm install libsodium-wrappers`)
## Example usage
First, we need to register a slash command against the Discord API:
```js
const { REST } = require('@discordjs/rest');
const { Routes } = require('discord-api-types/v9');
const commands = [{
name: 'ping',
description: 'Replies with Pong!'
}];
const rest = new REST({ version: '9' }).setToken('token');
(async () => {
try {
console.log('Started refreshing application (/) commands.');
await rest.put(
Routes.applicationGuildCommands(CLIENT_ID, GUILD_ID),
{ body: commands },
);
console.log('Successfully reloaded application (/) commands.');
} catch (error) {
console.error(error);
}
})();
```
Afterwards we can create a quite simple example bot:
```js
const { Client, Intents } = require('discord.js');
const client = new Client({ intents: [Intents.FLAGS.GUILDS] });
const Discord = require('discord.js');
const client = new Discord.Client();
client.on('ready', () => {
console.log(`Logged in as ${client.user.tag}!`);
});
client.on('interactionCreate', async interaction => {
if (!interaction.isCommand()) return;
if (interaction.commandName === 'ping') {
await interaction.reply('Pong!');
client.on('message', msg => {
if (msg.content === 'ping') {
msg.reply('pong');
}
});
@@ -90,28 +64,23 @@ client.login('token');
```
## Links
- [Website](https://discord.js.org/) ([source](https://github.com/discordjs/website))
- [Documentation](https://discord.js.org/#/docs/main/master/general/welcome)
- [Guide](https://discordjs.guide/) ([source](https://github.com/discordjs/guide))
See also the [Update Guide](https://discordjs.guide/additional-info/changes-in-v13.html), including updated and removed items in the library.
- [Discord.js Discord server](https://discord.gg/djs)
- [Discord API Discord server](https://discord.gg/discord-api)
- [GitHub](https://github.com/discordjs/discord.js)
- [NPM](https://www.npmjs.com/package/discord.js)
- [Related libraries](https://discord.com/developers/docs/topics/community-resources#libraries)
* [Website](https://discord.js.org/) ([source](https://github.com/discordjs/website))
* [Documentation](https://discord.js.org/#/docs)
* [Guide](https://discordjs.guide/) ([source](https://github.com/discordjs/guide))
* [Discord.js Discord server](https://discord.gg/bRCvFy9)
* [Discord API Discord server](https://discord.gg/discord-api)
* [GitHub](https://github.com/discordjs/discord.js)
* [NPM](https://www.npmjs.com/package/discord.js)
* [Related libraries](https://discordapi.com/unofficial/libs.html)
### Extensions
- [RPC](https://www.npmjs.com/package/discord-rpc) ([source](https://github.com/discordjs/RPC))
* [RPC](https://www.npmjs.com/package/discord-rpc) ([source](https://github.com/discordjs/RPC))
## Contributing
Before creating an issue, please ensure that it hasn't already been reported/suggested, and double-check the
[documentation](https://discord.js.org/#/docs).
See [the contribution guide](https://github.com/discordjs/discord.js/blob/master/.github/CONTRIBUTING.md) if you'd like to submit a PR.
## Help
If you don't understand something in the documentation, you are experiencing problems, or you just need a gentle
nudge in the right direction, please don't hesitate to join our official [Discord.js Server](https://discord.gg/djs).
nudge in the right direction, please don't hesitate to join our official [Discord.js Server](https://discord.gg/bRCvFy9).

9
browser.js Normal file
View File

@@ -0,0 +1,9 @@
const browser = typeof window !== 'undefined';
const webpack = !!process.env.__DISCORD_WEBPACK__;
const Discord = require('./');
module.exports = Discord;
if (browser && webpack) window.Discord = Discord; // eslint-disable-line no-undef
// eslint-disable-next-line no-console
else if (!browser) console.warn('Warning: Attempting to use browser version of Discord.js in a non-browser environment!');

BIN
deploy/deploy-key.enc Normal file

Binary file not shown.

90
deploy/deploy.sh Normal file
View File

@@ -0,0 +1,90 @@
#!/bin/bash
# Adapted from https://gist.github.com/domenic/ec8b0fc8ab45f39403dd.
set -e
function build {
npm run docs
VERSIONED=false npm run webpack
}
# For revert branches, do nothing
if [[ "$TRAVIS_BRANCH" == revert-* ]]; then
echo -e "\e[36m\e[1mBuild triggered for reversion branch \"${TRAVIS_BRANCH}\" - doing nothing."
exit 0
fi
# For PRs, do nothing
if [ "$TRAVIS_PULL_REQUEST" != "false" ]; then
echo -e "\e[36m\e[1mBuild triggered for PR #${TRAVIS_PULL_REQUEST} to branch \"${TRAVIS_BRANCH}\" - doing nothing."
exit 0
fi
# Figure out the source of the build
if [ -n "$TRAVIS_TAG" ]; then
echo -e "\e[36m\e[1mBuild triggered for tag \"${TRAVIS_TAG}\"."
SOURCE=$TRAVIS_TAG
SOURCE_TYPE="tag"
else
echo -e "\e[36m\e[1mBuild triggered for branch \"${TRAVIS_BRANCH}\"."
SOURCE=$TRAVIS_BRANCH
SOURCE_TYPE="branch"
fi
# For Node != 6, do nothing
if [ "$TRAVIS_NODE_VERSION" != "6" ]; then
echo -e "\e[36m\e[1mBuild triggered with Node v${TRAVIS_NODE_VERSION} - doing nothing."
exit 0
fi
build
# Initialise some useful variables
REPO=`git config remote.origin.url`
SSH_REPO=${REPO/https:\/\/github.com\//git@github.com:}
SHA=`git rev-parse --verify HEAD`
# Decrypt and add the ssh key
ENCRYPTED_KEY_VAR="encrypted_${ENCRYPTION_LABEL}_key"
ENCRYPTED_IV_VAR="encrypted_${ENCRYPTION_LABEL}_iv"
ENCRYPTED_KEY=${!ENCRYPTED_KEY_VAR}
ENCRYPTED_IV=${!ENCRYPTED_IV_VAR}
openssl aes-256-cbc -K $ENCRYPTED_KEY -iv $ENCRYPTED_IV -in deploy/deploy-key.enc -out deploy-key -d
chmod 600 deploy-key
eval `ssh-agent -s`
ssh-add deploy-key
# Checkout the repo in the target branch so we can build docs and push to it
TARGET_BRANCH="docs"
git clone $REPO out -b $TARGET_BRANCH
# Move the generated JSON file to the newly-checked-out repo, to be committed and pushed
mv docs/docs.json out/$SOURCE.json
# Commit and push
cd out
git add .
git config user.name "Travis CI"
git config user.email "$COMMIT_AUTHOR_EMAIL"
git commit -m "Docs build for ${SOURCE_TYPE} ${SOURCE}: ${SHA}" || true
git push $SSH_REPO $TARGET_BRANCH
# Clean up...
cd ..
rm -rf out
# ...then do the same once more for the webpack
TARGET_BRANCH="webpack"
git clone $REPO out -b $TARGET_BRANCH
# Move the generated webpack over
mv webpack/discord.js out/discord.$SOURCE.js
mv webpack/discord.min.js out/discord.$SOURCE.min.js
# Commit and push
cd out
git add .
git config user.name "Travis CI"
git config user.email "$COMMIT_AUTHOR_EMAIL"
git commit -m "Webpack build for ${SOURCE_TYPE} ${SOURCE}: ${SHA}" || true
git push $SSH_REPO $TARGET_BRANCH

34
deploy/test.sh Normal file
View File

@@ -0,0 +1,34 @@
#!/bin/bash
set -e
function tests {
npm run lint
npm run docs:test
exit 0
}
# For revert branches, do nothing
if [[ "$TRAVIS_BRANCH" == revert-* ]]; then
echo -e "\e[36m\e[1mTest triggered for reversion branch \"${TRAVIS_BRANCH}\" - doing nothing."
exit 0
fi
# For PRs
if [ "$TRAVIS_PULL_REQUEST" != "false" ]; then
echo -e "\e[36m\e[1mTest triggered for PR #${TRAVIS_PULL_REQUEST} to branch \"${TRAVIS_BRANCH}\" - only running tests."
tests
fi
# Figure out the source of the test
if [ -n "$TRAVIS_TAG" ]; then
echo -e "\e[36m\e[1mTest triggered for tag \"${TRAVIS_TAG}\"."
else
echo -e "\e[36m\e[1mTest triggered for branch \"${TRAVIS_BRANCH}\"."
fi
# For Node != 6
if [ "$TRAVIS_NODE_VERSION" != "6" ]; then
echo -e "\e[36m\e[1mTest triggered with Node v${TRAVIS_NODE_VERSION} - only running tests."
tests
fi

View File

@@ -0,0 +1,163 @@
# Sending Attachments
In here you'll see a few examples showing how you can send an attachment using discord.js.
## Sending an attachment using a URL
There are a few ways you can do this, but we'll show you the easiest.
The following examples use [Attachment](/#/docs/main/stable/class/Attachment).
```js
// Extract the required classes from the discord.js module
const { Client, Attachment } = require('discord.js');
// Create an instance of a Discord client
const client = new Client();
/**
* The ready event is vital, it means that only _after_ this will your bot start reacting to information
* received from Discord
*/
client.on('ready', () => {
console.log('I am ready!');
});
client.on('message', message => {
// If the message is '!rip'
if (message.content === '!rip') {
// Create the attachment using Attachment
const attachment = new Attachment('https://i.imgur.com/w3duR07.png');
// Send the attachment in the message channel
message.channel.send(attachment);
}
});
// Log our bot in using the token from https://discordapp.com/developers/applications/me
client.login('your token here');
```
And here is the result:
![Image showing the result](/static/attachment-example1.png)
But what if you want to send an attachment with a message content? Fear not, for it is easy to do that too! We'll recommend reading [the TextChannel's "send" function documentation](/#/docs/main/stable/class/TextChannel?scrollTo=send) to see what other options are available.
```js
// Extract the required classes from the discord.js module
const { Client, Attachment } = require('discord.js');
// Create an instance of a Discord client
const client = new Client();
/**
* The ready event is vital, it means that only _after_ this will your bot start reacting to information
* received from Discord
*/
client.on('ready', () => {
console.log('I am ready!');
});
client.on('message', message => {
// If the message is '!rip'
if (message.content === '!rip') {
// Create the attachment using Attachment
const attachment = new Attachment('https://i.imgur.com/w3duR07.png');
// Send the attachment in the message channel with a content
message.channel.send(`${message.author},`, attachment);
}
});
// Log our bot in using the token from https://discordapp.com/developers/applications/me
client.login('your token here');
```
And here's the result of this one:
![Image showing the result](/static/attachment-example2.png)
## Sending a local file or buffer
Sending a local file isn't hard either! We'll be using [Attachment](/#/docs/main/stable/class/Attachment) for these examples too.
```js
// Extract the required classes from the discord.js module
const { Client, Attachment } = require('discord.js');
// Create an instance of a Discord client
const client = new Client();
/**
* The ready event is vital, it means that only _after_ this will your bot start reacting to information
* received from Discord
*/
client.on('ready', () => {
console.log('I am ready!');
});
client.on('message', message => {
// If the message is '!rip'
if (message.content === '!rip') {
// Create the attachment using Attachment
const attachment = new Attachment('./rip.png');
// Send the attachment in the message channel with a content
message.channel.send(`${message.author},`, attachment);
}
});
// Log our bot in using the token from https://discordapp.com/developers/applications/me
client.login('your token here');
```
The results are the same as the URL examples:
![Image showing result](/static/attachment-example1.png)
But what if you have a buffer from an image? Or a text document? Well, it's the same as sending a local file or a URL!
In the following example, we'll be getting the buffer from a `memes.txt` file, and send it in the message channel.
You can use any buffer you want, and send it. Just make sure to overwrite the filename if it isn't an image!
```js
// Extract the required classes from the discord.js module
const { Client, Attachment } = require('discord.js');
// Import the native fs module
const fs = require('fs');
// Create an instance of a Discord client
const client = new Client();
/**
* The ready event is vital, it means that only _after_ this will your bot start reacting to information
* received from Discord
*/
client.on('ready', () => {
console.log('I am ready!');
});
client.on('message', message => {
// If the message is '!memes'
if (message.content === '!memes') {
// Get the buffer from the 'memes.txt', assuming that the file exists
const buffer = fs.readFileSync('./memes.txt');
/**
* Create the attachment using Attachment,
* overwritting the default file name to 'memes.txt'
* Read more about it over at
* http://discord.js.org/#/docs/main/stable/class/Attachment
*/
const attachment = new Attachment(buffer, 'memes.txt');
// Send the attachment in the message channel with a content
message.channel.send(`${message.author}, here are your memes!`, attachment);
}
});
// Log our bot in using the token from https://discordapp.com/developers/applications/me
client.login('your token here');
```
And of course, the results are:
![Attachment File example 3](/static/attachment-example3.png)

29
docs/examples/avatars.js Normal file
View File

@@ -0,0 +1,29 @@
/**
* Send a user a link to their avatar
*/
// Import the discord.js module
const Discord = require('discord.js');
// Create an instance of a Discord client
const client = new Discord.Client();
/**
* The ready event is vital, it means that only _after_ this will your bot start reacting to information
* received from Discord
*/
client.on('ready', () => {
console.log('I am ready!');
});
// Create an event listener for messages
client.on('message', message => {
// If the message is "what is my avatar"
if (message.content === 'what is my avatar') {
// Send the user's avatar URL
message.reply(message.author.avatarURL);
}
});
// Log our bot in using the token from https://discordapp.com/developers/applications/me
client.login('your token here');

38
docs/examples/embed.js Normal file
View File

@@ -0,0 +1,38 @@
/**
* An example of how you can send embeds
*/
// Extract the required classes from the discord.js module
const { Client, RichEmbed } = require('discord.js');
// Create an instance of a Discord client
const client = new Client();
/**
* The ready event is vital, it means that only _after_ this will your bot start reacting to information
* received from Discord
*/
client.on('ready', () => {
console.log('I am ready!');
});
client.on('message', message => {
// If the message is "how to embed"
if (message.content === 'how to embed') {
// We can create embeds using the MessageEmbed constructor
// Read more about all that you can do with the constructor
// over at https://discord.js.org/#/docs/main/stable/class/RichEmbed
const embed = new RichEmbed()
// Set the title of the field
.setTitle('A slick little embed')
// Set the color of the embed
.setColor(0xFF0000)
// Set the main content of the embed
.setDescription('Hello, this is a slick embed!');
// Send the embed to the same channel as the message
message.channel.send(embed);
}
});
// Log our bot in using the token from https://discordapp.com/developers/applications/me
client.login('your token here');

30
docs/examples/greeting.js Normal file
View File

@@ -0,0 +1,30 @@
/**
* A bot that welcomes new guild members when they join
*/
// Import the discord.js module
const Discord = require('discord.js');
// Create an instance of a Discord client
const client = new Discord.Client();
/**
* The ready event is vital, it means that only _after_ this will your bot start reacting to information
* received from Discord
*/
client.on('ready', () => {
console.log('I am ready!');
});
// Create an event listener for new guild members
client.on('guildMemberAdd', member => {
// Send the message to a designated channel on a server:
const channel = member.guild.channels.find(ch => ch.name === 'member-log');
// Do nothing if the channel wasn't found on this server
if (!channel) return;
// Send the message, mentioning the member
channel.send(`Welcome to the server, ${member}`);
});
// Log our bot in using the token from https://discordapp.com/developers/applications/me
client.login('your token here');

145
docs/examples/moderation.md Normal file
View File

@@ -0,0 +1,145 @@
# Moderation
In here, you'll see some basic examples for kicking and banning a member.
## Kicking a member
Let's say you have a member that you'd like to kick. Here is an example of how you *can* do it.
```js
// Import the discord.js module
const Discord = require('discord.js');
// Create an instance of a Discord client
const client = new Discord.Client();
/**
* The ready event is vital, it means that only _after_ this will your bot start reacting to information
* received from Discord
*/
client.on('ready', () => {
console.log('I am ready!');
});
client.on('message', message => {
// Ignore messages that aren't from a guild
if (!message.guild) return;
// If the message content starts with "!kick"
if (message.content.startsWith('!kick')) {
// Assuming we mention someone in the message, this will return the user
// Read more about mentions over at https://discord.js.org/#/docs/main/stable/class/MessageMentions
const user = message.mentions.users.first();
// If we have a user mentioned
if (user) {
// Now we get the member from the user
const member = message.guild.member(user);
// If the member is in the guild
if (member) {
/**
* Kick the member
* Make sure you run this on a member, not a user!
* There are big differences between a user and a member
*/
member.kick('Optional reason that will display in the audit logs').then(() => {
// We let the message author know we were able to kick the person
message.reply(`Successfully kicked ${user.tag}`);
}).catch(err => {
// An error happened
// This is generally due to the bot not being able to kick the member,
// either due to missing permissions or role hierarchy
message.reply('I was unable to kick the member');
// Log the error
console.error(err);
});
} else {
// The mentioned user isn't in this guild
message.reply('That user isn\'t in this guild!');
}
// Otherwise, if no user was mentioned
} else {
message.reply('You didn\'t mention the user to kick!');
}
}
});
// Log our bot in using the token from https://discordapp.com/developers/applications/me
client.login('your token here');
```
And the result is:
![Image showing the result](/static/kick-example.png)
## Banning a member
Banning works the same way as kicking, but it has slightly more options that can be changed.
```js
// Import the discord.js module
const Discord = require('discord.js');
// Create an instance of a Discord client
const client = new Discord.Client();
/**
* The ready event is vital, it means that only _after_ this will your bot start reacting to information
* received from Discord
*/
client.on('ready', () => {
console.log('I am ready!');
});
client.on('message', message => {
// Ignore messages that aren't from a guild
if (!message.guild) return;
// if the message content starts with "!ban"
if (message.content.startsWith('!ban')) {
// Assuming we mention someone in the message, this will return the user
// Read more about mentions over at https://discord.js.org/#/docs/main/stable/class/MessageMentions
const user = message.mentions.users.first();
// If we have a user mentioned
if (user) {
// Now we get the member from the user
const member = message.guild.member(user);
// If the member is in the guild
if (member) {
/**
* Ban the member
* Make sure you run this on a member, not a user!
* There are big differences between a user and a member
* Read more about what ban options there are over at
* https://discord.js.org/#/docs/main/stable/class/GuildMember?scrollTo=ban
*/
member.ban({
reason: 'They were bad!',
}).then(() => {
// We let the message author know we were able to ban the person
message.reply(`Successfully banned ${user.tag}`);
}).catch(err => {
// An error happened
// This is generally due to the bot not being able to ban the member,
// either due to missing permissions or role hierarchy
message.reply('I was unable to ban the member');
// Log the error
console.error(err);
});
} else {
// The mentioned user isn't in this guild
message.reply('That user isn\'t in this guild!');
}
} else {
// Otherwise, if no user was mentioned
message.reply('You didn\'t mention the user to ban!');
}
}
});
// Log our bot in using the token from https://discordapp.com/developers/applications/me
client.login('your token here');
```
And the result is:
![Image showing the result](/static/ban-example.png)

29
docs/examples/ping.js Normal file
View File

@@ -0,0 +1,29 @@
/**
* A ping pong bot, whenever you send "ping", it replies "pong".
*/
// Import the discord.js module
const Discord = require('discord.js');
// Create an instance of a Discord client
const client = new Discord.Client();
/**
* The ready event is vital, it means that only _after_ this will your bot start reacting to information
* received from Discord
*/
client.on('ready', () => {
console.log('I am ready!');
});
// Create an event listener for messages
client.on('message', message => {
// If the message is "ping"
if (message.content === 'ping') {
// Send "pong" to the same channel
message.channel.send('pong');
}
});
// Log our bot in using the token from https://discordapp.com/developers/applications/me
client.login('your token here');

12
docs/examples/webhook.js Normal file
View File

@@ -0,0 +1,12 @@
/**
* Send a message using a webhook
*/
// Import the discord.js module
const Discord = require('discord.js');
// Create a new webhook
const hook = new Discord.WebhookClient('webhook id', 'webhook token');
// Send a message using the webhook
hook.send('I am now alive!');

23
docs/general/faq.md Normal file
View File

@@ -0,0 +1,23 @@
# Frequently Asked Questions
These are just questions that get asked frequently, that usually have a common resolution.
If you have issues not listed here, please ask in the [official Discord server](https://discord.gg/bRCvFy9).
Always make sure to read the documentation.
## No matter what, I get `SyntaxError: Block-scoped declarations (let, const, function, class) not yet supported outside strict mode`‽
Update to Node.js 6.0.0 or newer.
## How do I get voice working?
- Install FFMPEG.
- Install either the `@discordjs/opus` package or the `opusscript` package.
@discordjs/opus is greatly preferred, due to it having significantly better performance.
## How do I install FFMPEG?
- **npm:** `npm install ffmpeg-binaries`
- **Ubuntu 16.04:** `sudo apt install ffmpeg`
- **Ubuntu 14.04:** `sudo apt-get install libav-tools`
- **Windows:** `npm install ffmpeg-binaries` or see the [FFMPEG section of AoDude's guide](https://github.com/bdistin/OhGodMusicBot/blob/master/README.md#download-ffmpeg).
## How do I set up @discordjs/opus?
- **Ubuntu:** Simply run `npm install @discordjs/opus`, and it's done. Congrats!
- **Windows:** Run `npm install --global --production windows-build-tools` in an admin command prompt or PowerShell.
Then, running `npm install @discordjs/opus` in your bot's directory should successfully build it. Woo!

185
docs/general/updating.md Normal file
View File

@@ -0,0 +1,185 @@
# Version 11.6.0
v11.6.0 backports new features from the in-development v12, and fixes bugs in the v11.5.x releases.
See [the changelog](https://github.com/discordjs/discord.js/releases/tag/11.6.0) for a full list of changes, including information about deprecations.
# Version 11.5.0
v11.5.0 backports new features from the in-development v12, and fixes bugs in the v11.4.x releases.
See [the changelog](https://github.com/discordjs/discord.js/releases/tag/11.5.0) for a full list of changes, including information about deprecations.
# Version 11.4.0
v11.4.0 backports many new features such as Rich Presence and bugfixes from v11.3.0.
See [the changelog](https://github.com/discordjs/discord.js/releases/tag/11.4.0) for a full list of changes, including information about deprecations.
# Version 11.3.0
v11.3.0 backports many new features and bug fixes from the in-development v12.
See [the changelog](https://github.com/discordjs/discord.js/releases/tag/11.3.0) for a full list of changes, including information about deprecations.
# Version 11.2.0
v11.2.0 fixes a lot of bugs we encountered along the 11.1.0 release, as well as support for new features such as Message Attachments and UserGuildSettings.
See [the changelog](https://github.com/discordjs/discord.js/releases/tag/11.2.0) for a full list of changes, including information about deprecations.
# Version 11.1.0
v11.1.0 features improved voice and gateway stability, as well as support for new features such as audit logs and searching for messages.
See [the changelog](https://github.com/discordjs/discord.js/releases/tag/11.1.0) for a full list of changes, including
information about deprecations.
# Version 11
Version 11 contains loads of new and improved features, optimisations, and bug fixes.
See [the changelog](https://github.com/discordjs/discord.js/releases/tag/11.0.0) for a full list of changes.
## Significant additions
* Message Reactions and Embeds (rich text)
* Support for uws and erlpack for better performance
* OAuthApplication support
* Web distributions
## Breaking changes
### Client.login() no longer supports logging in with email + password
Logging in with an email and password has always been heavily discouraged since the advent of proper token support, but in v11 we have made the decision to completely remove the functionality, since Hammer & Chisel have [officially stated](https://github.com/hammerandchisel/discord-api-docs/issues/69#issuecomment-223886862) it simply shouldn't be done.
User accounts can still log in with tokens just like bot accounts. To obtain the token for a user account, you can log in to Discord with that account, and use Ctrl + Shift + I to open the developer tools. In the console tab, evaluating `localStorage.token` will give you the token for that account.
### ClientUser.setEmail()/setPassword() now require the current password, as well as setUsername() on user accounts
Since you can no longer log in with email and password, you must provide the current account password to the `setEmail()`, `setPassword()`, and `setUsername()` methods for user accounts (self-bots).
### Removed TextBasedChannel.sendTTSMessage()
This method was deemed to be an entirely pointless shortcut that virtually nobody even used.
The same results can be achieved by passing options to `send()` or `sendMessage()`.
Example:
```js
channel.send('Hi there', { tts: true });
```
### Using Collection.find()/exists() with IDs will throw an error
This is simply to help prevent a common mistake that is made frequently.
To find something or check its existence using an ID, you should use `.get()` and `.has()` which are part of the [ES6 Map class](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Map), which Collection is an extension of.
# Version 10
Version 10's non-BC changes focus on cleaning up some inconsistencies that exist in previous versions.
Upgrading from v9 should be quick and painless.
## Client options
All client options have been converted to camelCase rather than snake_case, and `max_message_cache` was renamed to `messageCacheMaxSize`.
v9 code example:
```js
const client = new Discord.Client({
disable_everyone: true,
max_message_cache: 500,
message_cache_lifetime: 120,
message_sweep_interval: 60
});
```
v10 code example:
```js
const client = new Discord.Client({
disableEveryone: true,
messageCacheMaxSize: 500,
messageCacheLifetime: 120,
messageSweepInterval: 60
});
```
## Presences
Presences have been completely restructured.
Previous versions of discord.js assumed that users had the same presence amongst all guilds - with the introduction of sharding, however, this is no longer the case.
v9 discord.js code may look something like this:
```js
User.status; // the status of the user
User.game; // the game that the user is playing
ClientUser.setStatus(status, game, url); // set the new status for the user
```
v10 moves presences to GuildMember instances. For the sake of simplicity, though, User classes also expose presences.
When accessing a presence on a User object, it simply finds the first GuildMember for the user, and uses its presence.
Additionally, the introduction of the Presence class keeps all of the presence data organised.
**It is strongly recommended that you use a GuildMember's presence where available, rather than a User.
A user may have an entirely different presence between two different guilds.**
v10 code:
```js
MemberOrUser.presence.status; // the status of the member or user
MemberOrUser.presence.game; // the game that the member or user is playing
ClientUser.setStatus(status); // online, idle, dnd, offline
ClientUser.setGame(game, streamingURL); // a game
ClientUser.setPresence(fullPresence); // status and game combined
```
## Voice
Voice has been rewritten internally, but in a backwards-compatible manner.
There is only one breaking change here; the `disconnected` event was renamed to `disconnect`.
Several more events have been made available to a VoiceConnection, so see the documentation.
## Events
Many events have been renamed or had their arguments change.
### Client events
| Version 9 | Version 10 |
|------------------------------------------------------|-----------------------------------------------|
| guildMemberAdd(guild, member) | guildMemberAdd(member) |
| guildMemberAvailable(guild, member) | guildMemberAvailable(member) |
| guildMemberRemove(guild, member) | guildMemberRemove(member) |
| guildMembersChunk(guild, members) | guildMembersChunk(members) |
| guildMemberUpdate(guild, oldMember, newMember) | guildMemberUpdate(oldMember, newMember) |
| guildRoleCreate(guild, role) | roleCreate(role) |
| guildRoleDelete(guild, role) | roleDelete(role) |
| guildRoleUpdate(guild, oldRole, newRole) | roleUpdate(oldRole, newRole) |
The guild parameter that has been dropped from the guild-related events can still be derived using `member.guild` or `role.guild`.
### VoiceConnection events
| Version 9 | Version 10 |
|--------------|------------|
| disconnected | disconnect |
## Dates and timestamps
All dates/timestamps on the structures have been refactored to have a consistent naming scheme and availability.
All of them are named similarly to this:
**Date:** `Message.createdAt`
**Timestamp:** `Message.createdTimestamp`
See the docs for each structure to see which date/timestamps are available on them.
# Version 9
The version 9 (v9) rewrite takes a much more object-oriented approach than previous versions,
which allows your code to be much more readable and manageable.
It's been rebuilt from the ground up and should be much more stable, fixing caching issues that affected
older versions. It also has support for newer Discord Features, such as emojis.
Version 9, while containing a sizable number of breaking changes, does not require much change in your code's logic -
most of the concepts are still the same, but loads of functions have been moved around.
The vast majority of methods you're used to using have been moved out of the Client class,
into other more relevant classes where they belong.
Because of this, you will need to convert most of your calls over to the new methods.
Here are a few examples of methods that have changed:
* `Client.sendMessage(channel, message)` ==> `TextChannel.sendMessage(message)`
* `Client.sendMessage(user, message)` ==> `User.sendMessage(message)`
* `Client.updateMessage(message, "New content")` ==> `Message.edit("New Content")`
* `Client.getChannelLogs(channel, limit)` ==> `TextChannel.fetchMessages({options})`
* `Server.detailsOfUser(User)` ==> `Server.members.get(User).properties` (retrieving a member gives a GuildMember object)
* `Client.joinVoiceChannel(voicechannel)` => `VoiceChannel.join()`
A couple more important details:
* `Client.loginWithToken("token")` ==> `client.login("token")`
* `Client.servers.length` ==> `client.guilds.size` (all instances of `server` are now `guild`)
## No more callbacks!
Version 9 eschews callbacks in favour of Promises. This means all code relying on callbacks must be changed.
For example, the following code:
```js
client.getChannelLogs(channel, 100, function(messages) {
console.log(`${messages.length} messages found`);
});
```
```js
channel.fetchMessages({limit: 100}).then(messages => {
console.log(`${messages.size} messages found`);
});
```

95
docs/general/welcome.md Normal file
View File

@@ -0,0 +1,95 @@
<div align="center">
<br />
<p>
<a href="https://discord.js.org"><img src="https://discord.js.org/static/logo.svg" width="546" alt="discord.js" /></a>
</p>
<br />
<p>
<a href="https://discord.gg/bRCvFy9"><img src="https://discordapp.com/api/guilds/222078108977594368/embed.png" alt="Discord server" /></a>
<a href="https://www.npmjs.com/package/discord.js"><img src="https://img.shields.io/npm/v/discord.js.svg?maxAge=3600" alt="NPM version" /></a>
<a href="https://www.npmjs.com/package/discord.js"><img src="https://img.shields.io/npm/dt/discord.js.svg?maxAge=3600" alt="NPM downloads" /></a>
<a href="https://travis-ci.org/discordjs/discord.js"><img src="https://travis-ci.org/discordjs/discord.js.svg" alt="Build status" /></a>
<a href="https://david-dm.org/discordjs/discord.js"><img src="https://img.shields.io/david/discordjs/discord.js.svg?maxAge=3600" alt="Dependencies" /></a>
<a href="https://www.patreon.com/discordjs"><img src="https://img.shields.io/badge/donate-patreon-F96854.svg" alt="Patreon" /></a>
</p>
<p>
<a href="https://nodei.co/npm/discord.js/"><img src="https://nodei.co/npm/discord.js.png?downloads=true&stars=true" alt="NPM info" /></a>
</p>
</div>
# Welcome!
Welcome to the discord.js v11.6 documentation.
The v11.6 release contains bugfixes from v11.5 and backports features from the in-development v12.
v12 is still very much a work-in-progress, as we're aiming to make it the best it can possibly be before releasing.
If you are fond of living life on the bleeding-edge, check out the master branch.
## About
discord.js is a powerful [Node.js](https://nodejs.org) module that allows you to interact with the
[Discord API](https://discordapp.com/developers/docs/intro) very easily.
- Object-oriented
- Predictable abstractions
- Performant
- 100% coverage of the Discord API
## Installation
**Node.js 6.0.0 or newer is required.**
Ignore any warnings about unmet peer dependencies, as they're all optional.
Without voice support: `npm install discord.js`
With voice support ([@discordjs/opus](https://www.npmjs.com/package/@discordjs/opus)): `npm install discord.js @discordjs/opus`
With voice support ([opusscript](https://www.npmjs.com/package/opusscript)): `npm install discord.js opusscript`
### Audio engines
The preferred audio engine is @discordjs/opus, as it performs significantly better than opusscript. When both are available, discord.js will automatically choose @discordjs/opus.
Using opusscript is only recommended for development environments where @discordjs/opus is tough to get working.
For production bots, using @discordjs/opus should be considered a necessity, especially if they're going to be running on multiple servers.
### Optional packages
- [bufferutil](https://www.npmjs.com/package/bufferutil) to greatly speed up the WebSocket when *not* using uws (`npm install bufferutil`)
- [erlpack](https://github.com/hammerandchisel/erlpack) for significantly faster WebSocket data (de)serialisation (`npm install hammerandchisel/erlpack`)
- One of the following packages can be installed for faster voice packet encryption and decryption:
- [sodium](https://www.npmjs.com/package/sodium) (`npm install sodium`)
- [libsodium.js](https://www.npmjs.com/package/libsodium-wrappers) (`npm install libsodium-wrappers`)
- [uws](https://www.npmjs.com/package/@discordjs/uws) for a much faster WebSocket connection (`npm install @discordjs/uws`)
## Example usage
```js
const Discord = require('discord.js');
const client = new Discord.Client();
client.on('ready', () => {
console.log(`Logged in as ${client.user.tag}!`);
});
client.on('message', msg => {
if (msg.content === 'ping') {
msg.reply('pong');
}
});
client.login('token');
```
## Links
* [Website](https://discord.js.org/) ([source](https://github.com/discordjs/website))
* [Documentation](https://discord.js.org/#/docs)
* [Guide](https://discordjs.guide/) ([source](https://github.com/discordjs/guide))
* [Discord.js Discord server](https://discord.gg/bRCvFy9)
* [Discord API Discord server](https://discord.gg/discord-api)
* [GitHub](https://github.com/discordjs/discord.js)
* [NPM](https://www.npmjs.com/package/discord.js)
* [Related libraries](https://discordapi.com/unofficial/libs.html)
### Extensions
* [RPC](https://www.npmjs.com/package/discord-rpc) ([source](https://github.com/discordjs/RPC))
## Contributing
Before creating an issue, please ensure that it hasn't already been reported/suggested, and double-check the
[documentation](https://discord.js.org/#/docs).
See [the contribution guide](https://github.com/discordjs/discord.js/blob/master/.github/CONTRIBUTING.md) if you'd like to submit a PR.
## Help
If you don't understand something in the documentation, you are experiencing problems, or you just need a gentle
nudge in the right direction, please don't hesitate to join our official [Discord.js Server](https://discord.gg/bRCvFy9).

View File

@@ -1,5 +1,30 @@
- name: General
files:
- name: Welcome
id: welcome
path: ../../README.md
path: welcome.md
- name: Updating your code
path: updating.md
- name: FAQ
path: faq.md
- name: Topics
files:
- name: Voice
path: voice.md
- name: Web builds
path: web.md
- name: Examples
files:
- name: Ping
path: ping.js
- name: Avatars
path: avatars.js
- name: Attachments
path: attachments.md
- name: Server greeting
path: greeting.js
- name: Message Embed
path: embed.js
- name: Moderation
path: moderation.md
- name: Webhook
path: webhook.js

113
docs/topics/voice.md Normal file
View File

@@ -0,0 +1,113 @@
# Introduction to Voice
Voice in discord.js can be used for many things, such as music bots, recording or relaying audio.
In discord.js, you can use voice by connecting to a `VoiceChannel` to obtain a `VoiceConnection`, where you can start streaming and receiving audio.
To get started, make sure you have:
* FFmpeg - `npm install ffmpeg-binaries`
* an opus encoder, choose one from below:
* `npm install opusscript`
* `npm install @discordjs/opus`
* a good network connection
The preferred opus engine is @discordjs/opus, as it performs significantly better than opusscript. When both are available, discord.js will automatically choose @discordjs/opus.
Using opusscript is only recommended for development environments where @discordjs/opus is tough to get working.
For production bots, using @discordjs/opus should be considered a necessity, especially if they're going to be running on multiple servers.
## Joining a voice channel
The example below reacts to a message and joins the sender's voice channel, catching any errors. This is important
as it allows us to obtain a `VoiceConnection` that we can start to stream audio with.
```js
const Discord = require('discord.js');
const client = new Discord.Client();
client.login('token here');
client.on('message', message => {
// Voice only works in guilds, if the message does not come from a guild,
// we ignore it
if (!message.guild) return;
if (message.content === '/join') {
// Only try to join the sender's voice channel if they are in one themselves
if (message.member.voiceChannel) {
message.member.voiceChannel.join()
.then(connection => { // Connection is an instance of VoiceConnection
message.reply('I have successfully connected to the channel!');
})
.catch(console.log);
} else {
message.reply('You need to join a voice channel first!');
}
}
});
```
## Streaming to a Voice Channel
In the previous example, we looked at how to join a voice channel in order to obtain a `VoiceConnection`. Now that we
have obtained a voice connection, we can start streaming audio to it. The following example shows how to stream an mp3
file:
**Playing a file:**
```js
// To play a file, we need to give an absolute path to it
const dispatcher = connection.playFile('C:/Users/Discord/Desktop/myfile.mp3');
```
Your file doesn't have to be just an mp3; ffmpeg can convert videos and audios of many formats.
The `dispatcher` variable is an instance of a `StreamDispatcher`, which manages streaming a specific resource to a voice
channel. We can do many things with the dispatcher, such as finding out when the stream ends or changing the volume:
```js
dispatcher.on('end', () => {
// The song has finished
});
dispatcher.on('error', e => {
// Catch any errors that may arise
console.log(e);
});
dispatcher.setVolume(0.5); // Set the volume to 50%
dispatcher.setVolume(1); // Set the volume back to 100%
console.log(dispatcher.time); // The time in milliseconds that the stream dispatcher has been playing for
dispatcher.pause(); // Pause the stream
dispatcher.resume(); // Carry on playing
dispatcher.end(); // End the dispatcher, emits 'end' event
```
If you have an existing [ReadableStream](https://nodejs.org/api/stream.html#stream_readable_streams),
this can also be used:
**Playing a ReadableStream:**
```js
connection.playStream(myReadableStream);
// If you don't want to use absolute paths, you can use
// fs.createReadStream to circumvent it
const fs = require('fs');
const stream = fs.createReadStream('./test.mp3');
connection.playStream(stream);
```
It's important to note that creating a readable stream to a file is less efficient than simply using `connection.playFile()`.
**Playing anything else:**
For anything else, such as a URL to a file, you can use `connection.playArbitraryInput()`. You should consult the [ffmpeg protocol documentation](https://ffmpeg.org/ffmpeg-protocols.html) to see what you can use this for.
```js
// Play an mp3 from a URL
connection.playArbitraryInput('http://mysite.com/sound.mp3');
```
Again, playing a file from a URL like this is more performant than creating a ReadableStream to the file.
## Advanced Topics
soon:tm:

38
docs/topics/web.md Normal file
View File

@@ -0,0 +1,38 @@
# Web builds
In addition to your usual Node applications, discord.js has special distributions available that are capable of running in web browsers.
This is useful for client-side web apps that need to interact with the Discord API.
[Webpack 3](https://webpack.js.org/) is used to build these.
## Usage
You can obtain your desired version of discord.js' web build from the [webpack branch](https://github.com/discordjs/discord.js/tree/webpack) of the GitHub repository.
There is a file for each branch and version of the library, and the ones ending in `.min.js` are minified to substantially reduce the size of the source code.
Include the file on the page just as you would any other JS library, like so:
```html
<script type="text/javascript" src="discord.VERSION.min.js"></script>
```
Rather than importing discord.js with `require('discord.js')`, the entire `Discord` object is available as a global (on the `window`) object.
The usage of the API isn't any different from using it in Node.js.
## Restrictions
- Any voice-related functionality is unavailable, as there is currently no audio encoding/decoding capabilities without external native libraries,
which web browsers do not support.
- The ShardingManager cannot be used, since it relies on being able to spawn child processes for shards.
- None of the optional packages are usable, since they're native libraries.
## Example
```html
<script type="text/javascript" src="discord.11.6.2.min.js"></script>
<script type="text/javascript">
const client = new Discord.Client();
client.on('message', msg => {
const guildTag = msg.channel.type === 'text' ? `[${msg.guild.name}]` : '[DM]';
const channelTag = msg.channel.type === 'text' ? `[#${msg.channel.name}]` : '';
console.log(`${guildTag}${channelTag} ${msg.author.tag}: ${msg.content}`);
});
client.login('some crazy token');
</script>
```

View File

@@ -1,3 +0,0 @@
{
"plugins": ["node_modules/jsdoc-strip-async-await"]
}

20540
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,34 +1,21 @@
{
"name": "discord.js",
"version": "13.0.0",
"version": "11.6.3",
"description": "A powerful library for interacting with the Discord API",
"main": "./src/index.js",
"module": "./src/index.mjs",
"main": "./src/index",
"types": "./typings/index.d.ts",
"files": [
"src",
"typings"
],
"exports": {
"require": "./src/index.js",
"import": "./src/index.mjs"
},
"scripts": {
"test": "npm run lint && npm run docs:test && npm run lint:typings",
"test:typescript": "tsc",
"test": "npm run lint && npm run docs:test",
"docs": "docgen --source src --custom docs/index.yml --output docs/docs.json",
"docs:test": "docgen --source src --custom docs/index.yml",
"lint": "eslint src",
"lint:fix": "eslint src --fix",
"lint:typings": "tslint typings/index.d.ts",
"prettier": "prettier --write src/**/*.js typings/**/*.ts",
"prepublishOnly": "npm run test && gen-esm-wrapper ./src/index.js ./src/index.mjs",
"prepare": "is-ci || husky install",
"changelog": "conventional-changelog -p angular -i RELEASE_CHANGELOG.md -s"
"lint:fix": "eslint --fix src",
"lint:typings": "tslint typings/index.d.ts typings/discord.js-test.ts",
"webpack": "parallel-webpack"
},
"repository": {
"type": "git",
"url": "https://github.com/discordjs/discord.js.git"
"url": "git+https://github.com/discordjs/discord.js.git"
},
"keywords": [
"discord",
@@ -38,45 +25,103 @@
"node",
"discordapp"
],
"author": "Amish Shah <amish@shah.gg>",
"author": "Amish Shah <amishshah.2k@gmail.com>",
"license": "Apache-2.0",
"bugs": {
"url": "https://github.com/discordjs/discord.js/issues"
},
"homepage": "https://github.com/discordjs/discord.js#readme",
"runkitExampleFilename": "./docs/examples/ping.js",
"dependencies": {
"@discordjs/builders": "^0.4.0",
"@discordjs/collection": "^0.2.1",
"@discordjs/form-data": "^3.0.1",
"@sapphire/async-queue": "^1.1.4",
"@types/ws": "^7.4.7",
"discord-api-types": "^0.22.0",
"node-fetch": "^2.6.1",
"ws": "^7.5.1"
"long": "^4.0.0",
"prism-media": "^0.0.4",
"snekfetch": "^3.6.4",
"tweetnacl": "^1.0.0",
"ws": "^6.0.0"
},
"peerDependencies": {
"@discordjs/uws": "^10.149.0",
"bufferutil": "^4.0.0",
"erlpack": "discordapp/erlpack",
"libsodium-wrappers": "^0.7.3",
"@discordjs/opus": "^0.1.0",
"node-opus": "^0.2.7",
"opusscript": "^0.0.6",
"sodium": "^2.0.3"
},
"peerDependenciesMeta": {
"bufferutil": {
"optional": true
},
"erlpack": {
"optional": true
},
"@discordjs/opus": {
"optional": true
},
"node-opus": {
"optional": true
},
"opusscript": {
"optional": true
},
"sodium": {
"optional": true
},
"libsodium-wrappers": {
"optional": true
},
"uws": {
"optional": true
}
},
"devDependencies": {
"@commitlint/cli": "^13.1.0",
"@commitlint/config-angular": "^13.1.0",
"@discordjs/docgen": "^0.10.0",
"@types/node": "^16.4.12",
"conventional-changelog-cli": "^2.1.1",
"cross-env": "^7.0.3",
"dtslint": "^4.1.3",
"eslint": "^7.32.0",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-import": "^2.23.4",
"eslint-plugin-prettier": "^3.4.0",
"gen-esm-wrapper": "^1.1.2",
"husky": "^7.0.1",
"is-ci": "^3.0.0",
"jest": "^27.0.6",
"lint-staged": "^11.1.1",
"prettier": "^2.3.2",
"tslint": "^6.1.3",
"typescript": "^4.3.5"
"@types/node": "^9.4.6",
"discord.js-docgen": "discordjs/docgen",
"eslint": "^5.4.0",
"parallel-webpack": "^2.3.0",
"tslint": "^3.15.1",
"tslint-config-typings": "^0.2.4",
"typescript": "^3.0.1",
"uglifyjs-webpack-plugin": "^1.3.0",
"webpack": "^4.17.0"
},
"engines": {
"node": ">=16.6.0",
"npm": ">=7.0.0"
"node": ">=6.0.0"
},
"browser": {
"ws": false,
"uws": false,
"@discordjs/uws": false,
"erlpack": false,
"prism-media": false,
"opusscript": false,
"node-opus": false,
"@discordjs/opus": false,
"tweetnacl": false,
"sodium": false,
"src/sharding/Shard.js": false,
"src/sharding/ShardClientUtil.js": false,
"src/sharding/ShardingManager.js": false,
"src/client/voice/dispatcher/StreamDispatcher.js": false,
"src/client/voice/opus/BaseOpusEngine.js": false,
"src/client/voice/opus/NodeOpusEngine.js": false,
"src/client/voice/opus/DiscordJsOpusEngine.js": false,
"src/client/voice/opus/OpusEngineList.js": false,
"src/client/voice/opus/OpusScriptEngine.js": false,
"src/client/voice/pcm/ConverterEngine.js": false,
"src/client/voice/pcm/ConverterEngineList.js": false,
"src/client/voice/pcm/FfmpegConverterEngine.js": false,
"src/client/voice/player/AudioPlayer.js": false,
"src/client/voice/receiver/VoiceReadable.js": false,
"src/client/voice/receiver/VoiceReceiver.js": false,
"src/client/voice/util/Secretbox.js": false,
"src/client/voice/util/SecretKey.js": false,
"src/client/voice/util/VolumeInterface.js": false,
"src/client/voice/ClientVoiceManager.js": false,
"src/client/voice/VoiceBroadcast.js": false,
"src/client/voice/VoiceConnection.js": false,
"src/client/voice/VoiceUDPClient.js": false,
"src/client/voice/VoiceWebSocket.js": false
}
}

View File

@@ -1,38 +0,0 @@
'use strict';
let erlpack;
try {
erlpack = require('erlpack');
if (!erlpack.pack) erlpack = null;
} catch {} // eslint-disable-line no-empty
exports.WebSocket = require('ws');
const ab = new TextDecoder();
exports.encoding = erlpack ? 'etf' : 'json';
exports.pack = erlpack ? erlpack.pack : JSON.stringify;
exports.unpack = (data, type) => {
if (exports.encoding === 'json' || type === 'json') {
if (typeof data !== 'string') {
data = ab.decode(data);
}
return JSON.parse(data);
}
if (!Buffer.isBuffer(data)) data = Buffer.from(new Uint8Array(data));
return erlpack.unpack(data);
};
exports.create = (gateway, query = {}, ...args) => {
const [g, q] = gateway.split('?');
query.encoding = exports.encoding;
query = new URLSearchParams(query);
if (q) new URLSearchParams(q).forEach((v, k) => query.set(k, v));
const ws = new exports.WebSocket(`${g}?${query}`, ...args);
return ws;
};
for (const state of ['CONNECTING', 'OPEN', 'CLOSING', 'CLOSED']) exports[state] = exports.WebSocket[state];

View File

@@ -1,75 +0,0 @@
'use strict';
const EventEmitter = require('events');
const RESTManager = require('../rest/RESTManager');
const Options = require('../util/Options');
const Util = require('../util/Util');
/**
* The base class for all clients.
* @extends {EventEmitter}
*/
class BaseClient extends EventEmitter {
constructor(options = {}) {
super();
/**
* The options the client was instantiated with
* @type {ClientOptions}
*/
this.options = Util.mergeDefault(Options.createDefault(), options);
/**
* The REST manager of the client
* @type {RESTManager}
* @private
*/
this.rest = new RESTManager(this, options._tokenType);
}
/**
* API shortcut
* @type {Object}
* @readonly
* @private
*/
get api() {
return this.rest.api;
}
/**
* Destroys all assets used by the base client.
* @returns {void}
*/
destroy() {
if (this.rest.sweepInterval) clearInterval(this.rest.sweepInterval);
}
/**
* Increments max listeners by one, if they are not zero.
* @private
*/
incrementMaxListeners() {
const maxListeners = this.getMaxListeners();
if (maxListeners !== 0) {
this.setMaxListeners(maxListeners + 1);
}
}
/**
* Decrements max listeners by one, if they are not zero.
* @private
*/
decrementMaxListeners() {
const maxListeners = this.getMaxListeners();
if (maxListeners !== 0) {
this.setMaxListeners(maxListeners - 1);
}
}
toJSON(...props) {
return Util.flatten(this, { domain: false }, ...props);
}
}
module.exports = BaseClient;

View File

@@ -1,97 +1,76 @@
'use strict';
const { Collection } = require('@discordjs/collection');
const BaseClient = require('./BaseClient');
const ActionsManager = require('./actions/ActionsManager');
const EventEmitter = require('events');
const Constants = require('../util/Constants');
const Permissions = require('../util/Permissions');
const Util = require('../util/Util');
const RESTManager = require('./rest/RESTManager');
const ClientDataManager = require('./ClientDataManager');
const ClientManager = require('./ClientManager');
const ClientDataResolver = require('./ClientDataResolver');
const ClientVoiceManager = require('./voice/ClientVoiceManager');
const WebSocketManager = require('./websocket/WebSocketManager');
const { Error, TypeError, RangeError } = require('../errors');
const BaseGuildEmojiManager = require('../managers/BaseGuildEmojiManager');
const ChannelManager = require('../managers/ChannelManager');
const GuildManager = require('../managers/GuildManager');
const UserManager = require('../managers/UserManager');
const ActionsManager = require('./actions/ActionsManager');
const Collection = require('../util/Collection');
const Presence = require('../structures/Presence').Presence;
const ShardClientUtil = require('../sharding/ShardClientUtil');
const ClientPresence = require('../structures/ClientPresence');
const GuildPreview = require('../structures/GuildPreview');
const GuildTemplate = require('../structures/GuildTemplate');
const Invite = require('../structures/Invite');
const Sticker = require('../structures/Sticker');
const StickerPack = require('../structures/StickerPack');
const VoiceRegion = require('../structures/VoiceRegion');
const Webhook = require('../structures/Webhook');
const Widget = require('../structures/Widget');
const { Events, InviteScopes, Status } = require('../util/Constants');
const DataResolver = require('../util/DataResolver');
const Intents = require('../util/Intents');
const Options = require('../util/Options');
const Permissions = require('../util/Permissions');
const VoiceBroadcast = require('./voice/VoiceBroadcast');
/**
* The main hub for interacting with the Discord API, and the starting point for any bot.
* @extends {BaseClient}
* @extends {EventEmitter}
*/
class Client extends BaseClient {
class Client extends EventEmitter {
/**
* @param {ClientOptions} options Options for the client
* @param {ClientOptions} [options] Options for the client
*/
constructor(options) {
super(Object.assign({ _tokenType: 'Bot' }, options));
constructor(options = {}) {
super();
const data = require('worker_threads').workerData ?? process.env;
const defaults = Options.createDefault();
if (this.options.shards === defaults.shards) {
if ('SHARDS' in data) {
this.options.shards = JSON.parse(data.SHARDS);
}
}
if (this.options.shardCount === defaults.shardCount) {
if ('SHARD_COUNT' in data) {
this.options.shardCount = Number(data.SHARD_COUNT);
} else if (Array.isArray(this.options.shards)) {
this.options.shardCount = this.options.shards.length;
}
}
const typeofShards = typeof this.options.shards;
if (typeofShards === 'undefined' && typeof this.options.shardCount === 'number') {
this.options.shards = Array.from({ length: this.options.shardCount }, (_, i) => i);
}
if (typeofShards === 'number') this.options.shards = [this.options.shards];
if (Array.isArray(this.options.shards)) {
this.options.shards = [
...new Set(
this.options.shards.filter(item => !isNaN(item) && item >= 0 && item < Infinity && item === (item | 0)),
),
];
}
// Obtain shard details from environment
if (!options.shardId && 'SHARD_ID' in process.env) options.shardId = Number(process.env.SHARD_ID);
if (!options.shardCount && 'SHARD_COUNT' in process.env) options.shardCount = Number(process.env.SHARD_COUNT);
/**
* The options the client was instantiated with
* @type {ClientOptions}
*/
this.options = Util.mergeDefault(Constants.DefaultOptions, options);
this._validateOptions();
/**
* Functions called when a cache is garbage collected or the Client is destroyed
* @type {Set<Function>}
* The REST manager of the client
* @type {RESTManager}
* @private
*/
this._cleanups = new Set();
this.rest = new RESTManager(this);
/**
* The finalizers used to cleanup items.
* @type {FinalizationRegistry}
* The data manager of the client
* @type {ClientDataManager}
* @private
*/
this._finalizers = new FinalizationRegistry(this._finalize.bind(this));
this.dataManager = new ClientDataManager(this);
/**
* The manager of the client
* @type {ClientManager}
* @private
*/
this.manager = new ClientManager(this);
/**
* The WebSocket manager of the client
* @type {WebSocketManager}
* @private
*/
this.ws = new WebSocketManager(this);
/**
* The data resolver of the client
* @type {ClientDataResolver}
* @private
*/
this.resolver = new ClientDataResolver(this);
/**
* The action manager of the client
* @type {ActionsManager}
@@ -100,57 +79,55 @@ class Client extends BaseClient {
this.actions = new ActionsManager(this);
/**
* The voice manager of the client
* @type {ClientVoiceManager}
* The voice manager of the client (`null` in browsers)
* @type {?ClientVoiceManager}
* @private
*/
this.voice = new ClientVoiceManager(this);
this.voice = !this.browser ? new ClientVoiceManager(this) : null;
/**
* Shard helpers for the client (only if the process was spawned from a {@link ShardingManager})
* The shard helpers for the client
* (only if the process was spawned as a child, such as from a {@link ShardingManager})
* @type {?ShardClientUtil}
*/
this.shard = process.env.SHARDING_MANAGER
? ShardClientUtil.singleton(this, process.env.SHARDING_MANAGER_MODE)
: null;
this.shard = process.send ? ShardClientUtil.singleton(this) : null;
/**
* All of the {@link User} objects that have been cached at any point, mapped by their ids
* @type {UserManager}
* All of the {@link User} objects that have been cached at any point, mapped by their IDs
* @type {Collection<Snowflake, User>}
*/
this.users = new UserManager(this);
this.users = new Collection();
/**
* All of the guilds the client is currently handling, mapped by their ids -
* All of the guilds the client is currently handling, mapped by their IDs -
* as long as sharding isn't being used, this will be *every* guild the bot is a member of
* @type {GuildManager}
* @type {Collection<Snowflake, Guild>}
*/
this.guilds = new GuildManager(this);
this.guilds = new Collection();
/**
* All of the {@link Channel}s that the client is currently handling, mapped by their ids -
* 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}
* All of the {@link Channel}s that the client is currently handling, mapped by their IDs -
* as long as sharding isn't being used, this will be *every* channel in *every* guild, and all DM channels
* @type {Collection<Snowflake, Channel>}
*/
this.channels = new ChannelManager(this);
this.channels = new Collection();
/**
* The presence of the Client
* @private
* @type {ClientPresence}
* Presences that have been received for the client user's friends, mapped by user IDs
* <warn>This is only filled when using a user account.</warn>
* @type {Collection<Snowflake, Presence>}
* @deprecated
*/
this.presence = new ClientPresence(this, this.options.presence);
this.presences = new Collection();
Object.defineProperty(this, 'token', { writable: true });
if (!this.token && 'DISCORD_TOKEN' in process.env) {
if (!this.token && 'CLIENT_TOKEN' in process.env) {
/**
* Authorization token for the logged in bot.
* If present, this defaults to `process.env.DISCORD_TOKEN` when instantiating the client
* Authorization token for the logged in user/bot
* <warn>This should be kept private at all times.</warn>
* @type {?string}
*/
this.token = process.env.DISCORD_TOKEN;
this.token = process.env.CLIENT_TOKEN;
} else {
this.token = null;
}
@@ -161,12 +138,6 @@ class Client extends BaseClient {
*/
this.user = null;
/**
* The application of this bot
* @type {?ClientApplication}
*/
this.application = null;
/**
* Time at which the client was last regarded as being in the `READY` state
* (each time the client disconnects and successfully reconnects, this will be overwritten)
@@ -174,38 +145,53 @@ class Client extends BaseClient {
*/
this.readyAt = null;
/**
* Active voice broadcasts that have been created
* @type {VoiceBroadcast[]}
*/
this.broadcasts = [];
/**
* Previous heartbeat pings of the websocket (most recent first, limited to three elements)
* @type {number[]}
*/
this.pings = [];
/**
* Timeouts set by {@link Client#setTimeout} that are still active
* @type {Set<Timeout>}
* @private
*/
this._timeouts = new Set();
/**
* Intervals set by {@link Client#setInterval} that are still active
* @type {Set<Timeout>}
* @private
*/
this._intervals = new Set();
if (this.options.messageSweepInterval > 0) {
process.emitWarning(
'The message sweeping client options are deprecated, use the makeCache option with LimitedCollection instead.',
'DeprecationWarning',
);
this.sweepMessageInterval = setInterval(
this.sweepMessages.bind(this),
this.options.messageSweepInterval * 1000,
).unref();
this.setInterval(this.sweepMessages.bind(this), this.options.messageSweepInterval * 1000);
}
}
/**
* All custom emojis that the client has access to, mapped by their ids
* @type {BaseGuildEmojiManager}
* @readonly
* Timestamp of the latest ping's start time
* @type {number}
* @private
*/
get emojis() {
const emojis = new BaseGuildEmojiManager(this);
for (const guild of this.guilds.cache.values()) {
if (guild.available) for (const emoji of guild.emojis.cache.values()) emojis.cache.set(emoji.id, emoji);
}
return emojis;
get _pingTimestamp() {
return this.ws.connection ? this.ws.connection.lastPingTimestamp : 0;
}
/**
* Timestamp of the time the client was last `READY` at
* @type {?number}
* Current status of the client's connection to Discord
* @type {Status}
* @readonly
*/
get readyTimestamp() {
return this.readyAt?.getTime() ?? null;
get status() {
return this.ws.connection ? this.ws.connection.status : Constants.Status.IDLE;
}
/**
@@ -218,61 +204,119 @@ 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
* client.login('my token');
* Average heartbeat ping of the websocket, obtained by averaging the {@link Client#pings} property
* @type {number}
* @readonly
*/
async login(token = this.token) {
if (!token || typeof token !== 'string') throw new Error('TOKEN_INVALID');
this.token = token = token.replace(/^(Bot|Bearer)\s*/i, '');
this.emit(
Events.DEBUG,
`Provided token: ${token
.split('.')
.map((val, i) => (i > 1 ? val.replace(/./g, '*') : val))
.join('.')}`,
);
if (this.options.presence) {
this.options.ws.presence = this.presence._parse(this.options.presence);
}
this.emit(Events.DEBUG, 'Preparing to connect to the gateway...');
try {
await this.ws.connect();
return this.token;
} catch (error) {
this.destroy();
throw error;
}
get ping() {
return this.pings.reduce((prev, p) => prev + p, 0) / this.pings.length;
}
/**
* Returns whether the client has logged in, indicative of being able to access
* properties such as `user` and `application`.
* @returns {boolean}
* All active voice connections that have been established, mapped by guild ID
* @type {Collection<Snowflake, VoiceConnection>}
* @readonly
*/
isReady() {
return this.ws.status === Status.READY;
get voiceConnections() {
if (this.browser) return new Collection();
return this.voice.connections;
}
/**
* All custom emojis that the client has access to, mapped by their IDs
* @type {Collection<Snowflake, Emoji>}
* @readonly
*/
get emojis() {
const emojis = new Collection();
for (const guild of this.guilds.values()) {
for (const emoji of guild.emojis.values()) emojis.set(emoji.id, emoji);
}
return emojis;
}
/**
* Timestamp of the time the client was last `READY` at
* @type {?number}
* @readonly
*/
get readyTimestamp() {
return this.readyAt ? this.readyAt.getTime() : null;
}
/**
* Whether the client is in a browser environment
* @type {boolean}
* @readonly
*/
get browser() {
return typeof window !== 'undefined';
}
/**
* Creates a voice broadcast.
* @returns {VoiceBroadcast}
*/
createVoiceBroadcast() {
const broadcast = new VoiceBroadcast(this);
this.broadcasts.push(broadcast);
return broadcast;
}
/**
* Logs the client in, establishing a websocket connection to Discord.
* <info>Both bot and regular user accounts are supported, but it is highly recommended to use a bot account whenever
* possible. User accounts are subject to harsher ratelimits and other restrictions that don't apply to bot accounts.
* Bot accounts also have access to many features that user accounts cannot utilise. Automating a user account is
* considered a violation of Discord's ToS.</info>
* @param {string} token Token of the account to log in with
* @returns {Promise<string>} Token of the account used
* @example
* client.login('my token')
* .then(console.log)
* .catch(console.error);
*/
login(token = this.token) {
return this.rest.methods.login(token);
}
/**
* Logs out, terminates the connection to Discord, and destroys the client.
* @returns {void}
* @returns {Promise}
*/
destroy() {
super.destroy();
for (const t of this._timeouts) clearTimeout(t);
for (const i of this._intervals) clearInterval(i);
this._timeouts.clear();
this._intervals.clear();
return this.manager.destroy();
}
for (const fn of this._cleanups) fn();
this._cleanups.clear();
/**
* Requests a sync of guild data with Discord.
* <info>This can be done automatically every 30 seconds by enabling {@link ClientOptions#sync}.</info>
* <warn>This is only available when using a user account.</warn>
* @param {Guild[]|Collection<Snowflake, Guild>} [guilds=this.guilds] An array or collection of guilds to sync
* @deprecated
*/
syncGuilds(guilds = this.guilds) {
if (this.user.bot) return;
this.ws.send({
op: 12,
d: guilds instanceof Collection ? guilds.keyArray() : guilds.map(g => g.id),
});
}
if (this.sweepMessageInterval) clearInterval(this.sweepMessageInterval);
this.ws.destroy();
this.token = null;
/**
* Obtains a user from Discord, or the user cache if it's already available.
* <warn>This is only available when using a bot account.</warn>
* @param {Snowflake} id ID of the user
* @param {boolean} [cache=true] Whether to cache the new user object if it isn't already
* @returns {Promise<User>}
*/
fetchUser(id, cache = true) {
if (this.users.has(id)) return Promise.resolve(this.users.get(id));
return this.rest.methods.getUser(id, cache);
}
/**
@@ -280,34 +324,18 @@ class Client extends BaseClient {
* @param {InviteResolvable} invite Invite code or URL
* @returns {Promise<Invite>}
* @example
* client.fetchInvite('https://discord.gg/djs')
* client.fetchInvite('https://discord.gg/bRCvFy9')
* .then(invite => console.log(`Obtained invite with code: ${invite.code}`))
* .catch(console.error);
*/
async fetchInvite(invite) {
const code = DataResolver.resolveInviteCode(invite);
const data = await this.api.invites(code).get({ query: { with_counts: true, with_expiration: true } });
return new Invite(this, data);
}
/**
* Obtains a template from Discord.
* @param {GuildTemplateResolvable} template Template code or URL
* @returns {Promise<GuildTemplate>}
* @example
* client.fetchGuildTemplate('https://discord.new/FKvmczH2HyUf')
* .then(template => console.log(`Obtained template with code: ${template.code}`))
* .catch(console.error);
*/
async fetchGuildTemplate(template) {
const code = DataResolver.resolveGuildTemplateCode(template);
const data = await this.api.guilds.templates(code).get();
return new GuildTemplate(this, data);
fetchInvite(invite) {
const code = this.resolver.resolveInviteCode(invite);
return this.rest.methods.getInvite(code);
}
/**
* Obtains a webhook from Discord.
* @param {Snowflake} id The webhook's id
* @param {Snowflake} id ID of the webhook
* @param {string} [token] Token for the webhook
* @returns {Promise<Webhook>}
* @example
@@ -315,9 +343,8 @@ class Client extends BaseClient {
* .then(webhook => console.log(`Obtained webhook with name: ${webhook.name}`))
* .catch(console.error);
*/
async fetchWebhook(id, token) {
const data = await this.api.webhooks(id, token).get();
return new Webhook(this, { token, ...data });
fetchWebhook(id, token) {
return this.rest.methods.getWebhook(id, token);
}
/**
@@ -328,56 +355,8 @@ class Client extends BaseClient {
* .then(regions => console.log(`Available regions are: ${regions.map(region => region.name).join(', ')}`))
* .catch(console.error);
*/
async fetchVoiceRegions() {
const apiRegions = await this.api.voice.regions.get();
const regions = new Collection();
for (const region of apiRegions) regions.set(region.id, new VoiceRegion(region));
return regions;
}
/**
* Obtains a sticker from Discord.
* @param {Snowflake} id The sticker's id
* @returns {Promise<Sticker>}
* @example
* client.fetchSticker('id')
* .then(sticker => console.log(`Obtained sticker with name: ${sticker.name}`))
* .catch(console.error);
*/
async fetchSticker(id) {
const data = await this.api.stickers(id).get();
return new Sticker(this, data);
}
/**
* Obtains the list of sticker packs available to Nitro subscribers from Discord.
* @returns {Promise<Collection<Snowflake, StickerPack>>}
* @example
* client.fetchPremiumStickerPacks()
* .then(packs => console.log(`Available sticker packs are: ${packs.map(pack => pack.name).join(', ')}`))
* .catch(console.error);
*/
async fetchPremiumStickerPacks() {
const data = await this.api('sticker-packs').get();
return new Collection(data.sticker_packs.map(p => [p.id, new StickerPack(this, p)]));
}
/**
* A last ditch cleanup function for garbage collection.
* @param {Function} options.cleanup The function called to GC
* @param {string} [options.message] The message to send after a successful GC
* @param {string} [options.name] The name of the item being GCed
* @private
*/
_finalize({ cleanup, message, name }) {
try {
cleanup();
this._cleanups.delete(cleanup);
if (message) {
this.emit(Events.DEBUG, message);
}
} catch {
this.emit(Events.DEBUG, `Garbage collection failed on ${name ?? 'an unknown item'}.`);
}
fetchVoiceRegions() {
return this.rest.methods.fetchVoiceRegions();
}
/**
@@ -387,17 +366,11 @@ class Client extends BaseClient {
* will be removed from the caches. The default is based on {@link ClientOptions#messageCacheLifetime}
* @returns {number} Amount of messages that were removed from the caches,
* or -1 if the message cache lifetime is unlimited
* @example
* // Remove all messages older than 1800 seconds from the messages cache
* const amount = client.sweepMessages(1800);
* console.log(`Successfully removed ${amount} messages from the cache.`);
*/
sweepMessages(lifetime = this.options.messageCacheLifetime) {
if (typeof lifetime !== 'number' || isNaN(lifetime)) {
throw new TypeError('INVALID_TYPE', 'lifetime', 'number');
}
if (typeof lifetime !== 'number' || isNaN(lifetime)) throw new TypeError('The lifetime must be a number.');
if (lifetime <= 0) {
this.emit(Events.DEBUG, "Didn't sweep messages - lifetime is unlimited");
this.emit('debug', 'Didn\'t sweep messages - lifetime is unlimited');
return -1;
}
@@ -406,121 +379,121 @@ class Client extends BaseClient {
let channels = 0;
let messages = 0;
for (const channel of this.channels.cache.values()) {
for (const channel of this.channels.values()) {
if (!channel.messages) continue;
channels++;
messages += channel.messages.cache.sweep(
message => now - (message.editedTimestamp ?? message.createdTimestamp) > lifetimeMs,
messages += channel.messages.sweep(
message => now - (message.editedTimestamp || message.createdTimestamp) > lifetimeMs
);
}
this.emit(
Events.DEBUG,
`Swept ${messages} messages older than ${lifetime} seconds in ${channels} text-based channels`,
);
this.emit('debug', `Swept ${messages} messages older than ${lifetime} seconds in ${channels} text-based channels`);
return messages;
}
/**
* 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>}
* Obtains the OAuth Application of the bot from Discord.
* <warn>Bots can only fetch their own profile.</warn>
* @param {Snowflake} [id='@me'] ID of application to fetch
* @returns {Promise<OAuth2Application>}
* @example
* client.fetchApplication()
* .then(application => console.log(`Obtained application with name: ${application.name}`))
* .catch(console.error);
*/
async fetchGuildPreview(guild) {
const id = this.guilds.resolveId(guild);
if (!id) throw new TypeError('INVALID_TYPE', 'guild', 'GuildResolvable');
const data = await this.api.guilds(id).preview.get();
return new GuildPreview(this, data);
fetchApplication(id = '@me') {
if (id !== '@me') process.emitWarning('fetchApplication: use "@me" as an argument', 'DeprecationWarning');
return this.rest.methods.getApplication(id);
}
/**
* 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>}
*/
async fetchGuildWidget(guild) {
const id = this.guilds.resolveId(guild);
if (!id) throw new TypeError('INVALID_TYPE', 'guild', 'GuildResolvable');
const data = await this.api.guilds(id, 'widget.json').get();
return new Widget(this, data);
}
/**
* Options for {@link Client#generateInvite}.
* @typedef {Object} InviteGenerationOptions
* @property {InviteScope[]} scopes Scopes that should be requested
* @property {PermissionResolvable} [permissions] Permissions to request
* @property {GuildResolvable} [guild] Guild to preselect
* @property {boolean} [disableGuildSelect] Whether to disable the guild selection
*/
/**
* Generates a link that can be used to invite the bot to a guild.
* @param {InviteGenerationOptions} [options={}] Options for the invite
* @returns {string}
* <warn>This is only available when using a bot account.</warn>
* @param {PermissionResolvable} [permissions] Permissions to request
* @returns {Promise<string>}
* @example
* const link = client.generateInvite({
* scopes: ['applications.commands'],
* });
* console.log(`Generated application invite link: ${link}`);
* @example
* const link = client.generateInvite({
* permissions: [
* Permissions.FLAGS.SEND_MESSAGES,
* Permissions.FLAGS.MANAGE_GUILD,
* Permissions.FLAGS.MENTION_EVERYONE,
* ],
* scopes: ['bot'],
* });
* console.log(`Generated bot invite link: ${link}`);
* client.generateInvite(['SEND_MESSAGES', 'MANAGE_GUILD', 'MENTION_EVERYONE'])
* .then(link => console.log(`Generated bot invite link: ${link}`))
* .catch(console.error);
*/
generateInvite(options = {}) {
if (typeof options !== 'object') throw new TypeError('INVALID_TYPE', 'options', 'object', true);
if (!this.application) throw new Error('CLIENT_NOT_READY', 'generate an invite link');
const query = new URLSearchParams({
client_id: this.application.id,
});
const { scopes } = options;
if (typeof scopes === 'undefined') {
throw new TypeError('INVITE_MISSING_SCOPES');
}
if (!Array.isArray(scopes)) {
throw new TypeError('INVALID_TYPE', 'scopes', 'Array of Invite Scopes', true);
}
if (!scopes.some(scope => ['bot', 'applications.commands'].includes(scope))) {
throw new TypeError('INVITE_MISSING_SCOPES');
}
const invalidScope = scopes.find(scope => !InviteScopes.includes(scope));
if (invalidScope) {
throw new TypeError('INVALID_ELEMENT', 'Array', 'scopes', invalidScope);
}
query.set('scope', scopes.join(' '));
if (options.permissions) {
const permissions = Permissions.resolve(options.permissions);
if (permissions) query.set('permissions', permissions);
}
if (options.disableGuildSelect) {
query.set('disable_guild_select', true);
}
if (options.guild) {
const guildId = this.guilds.resolveId(options.guild);
if (!guildId) throw new TypeError('INVALID_TYPE', 'options.guild', 'GuildResolvable');
query.set('guild_id', guildId);
}
return `${this.options.http.api}${this.api.oauth2.authorize}?${query}`;
generateInvite(permissions) {
permissions = Permissions.resolve(permissions);
return this.fetchApplication().then(application =>
`https://discordapp.com/oauth2/authorize?client_id=${application.id}&permissions=${permissions}&scope=bot`
);
}
toJSON() {
return super.toJSON({
readyAt: false,
});
/**
* Sets a timeout that will be automatically cancelled if the client is destroyed.
* @param {Function} fn Function to execute
* @param {number} delay Time to wait before executing (in milliseconds)
* @param {...*} args Arguments for the function
* @returns {Timeout}
*/
setTimeout(fn, delay, ...args) {
const timeout = setTimeout(() => {
fn(...args);
this._timeouts.delete(timeout);
}, delay);
this._timeouts.add(timeout);
return timeout;
}
/**
* Clears a timeout.
* @param {Timeout} timeout Timeout to cancel
*/
clearTimeout(timeout) {
clearTimeout(timeout);
this._timeouts.delete(timeout);
}
/**
* Sets an interval that will be automatically cancelled if the client is destroyed.
* @param {Function} fn Function to execute
* @param {number} delay Time to wait before executing (in milliseconds)
* @param {...*} args Arguments for the function
* @returns {Timeout}
*/
setInterval(fn, delay, ...args) {
const interval = setInterval(fn, delay, ...args);
this._intervals.add(interval);
return interval;
}
/**
* Clears an interval.
* @param {Timeout} interval Interval to cancel
*/
clearInterval(interval) {
clearInterval(interval);
this._intervals.delete(interval);
}
/**
* Adds a ping to {@link Client#pings}.
* @param {number} startTime Starting time of the ping
* @private
*/
_pong(startTime) {
this.pings.unshift(Date.now() - startTime);
if (this.pings.length > 3) this.pings.length = 3;
this.ws.lastHeartbeatAck = true;
}
/**
* Adds/updates a friend's presence in {@link Client#presences}.
* @param {Snowflake} id ID of the user
* @param {Object} presence Raw presence object from Discord
* @private
*/
_setPresence(id, presence) {
if (this.presences.has(id)) {
this.presences.get(id).update(presence);
return;
}
this.presences.set(id, new Presence(presence, this));
}
/**
@@ -539,60 +512,39 @@ class Client extends BaseClient {
* @param {ClientOptions} [options=this.options] Options to validate
* @private
*/
_validateOptions(options = this.options) {
if (typeof options.intents === 'undefined') {
throw new TypeError('CLIENT_MISSING_INTENTS');
} else {
options.intents = Intents.resolve(options.intents);
_validateOptions(options = this.options) { // eslint-disable-line complexity
if (typeof options.shardCount !== 'number' || isNaN(options.shardCount)) {
throw new TypeError('The shardCount option must be a number.');
}
if (typeof options.shardCount !== 'number' || isNaN(options.shardCount) || options.shardCount < 1) {
throw new TypeError('CLIENT_INVALID_OPTION', 'shardCount', 'a number greater than or equal to 1');
if (typeof options.shardId !== 'number' || isNaN(options.shardId)) {
throw new TypeError('The shardId option must be a number.');
}
if (options.shards && !(options.shards === 'auto' || Array.isArray(options.shards))) {
throw new TypeError('CLIENT_INVALID_OPTION', 'shards', "'auto', a number or array of numbers");
if (options.shardCount < 0) throw new RangeError('The shardCount option must be at least 0.');
if (options.shardId < 0) throw new RangeError('The shardId option must be at least 0.');
if (options.shardId !== 0 && options.shardId >= options.shardCount) {
throw new RangeError('The shardId option must be less than shardCount.');
}
if (options.shards && !options.shards.length) throw new RangeError('CLIENT_INVALID_PROVIDED_SHARDS');
if (typeof options.makeCache !== 'function') {
throw new TypeError('CLIENT_INVALID_OPTION', 'makeCache', 'a function');
if (typeof options.messageCacheMaxSize !== 'number' || isNaN(options.messageCacheMaxSize)) {
throw new TypeError('The messageCacheMaxSize option must be a number.');
}
if (typeof options.messageCacheLifetime !== 'number' || isNaN(options.messageCacheLifetime)) {
throw new TypeError('CLIENT_INVALID_OPTION', 'The messageCacheLifetime', 'a number');
throw new TypeError('The messageCacheLifetime option must be a number.');
}
if (typeof options.messageSweepInterval !== 'number' || isNaN(options.messageSweepInterval)) {
throw new TypeError('CLIENT_INVALID_OPTION', 'messageSweepInterval', 'a number');
throw new TypeError('The messageSweepInterval option must be a number.');
}
if (typeof options.invalidRequestWarningInterval !== 'number' || isNaN(options.invalidRequestWarningInterval)) {
throw new TypeError('CLIENT_INVALID_OPTION', 'invalidRequestWarningInterval', 'a number');
if (typeof options.fetchAllMembers !== 'boolean') {
throw new TypeError('The fetchAllMembers option must be a boolean.');
}
if (!Array.isArray(options.partials)) {
throw new TypeError('CLIENT_INVALID_OPTION', 'partials', 'an Array');
if (typeof options.disableEveryone !== 'boolean') {
throw new TypeError('The disableEveryone option must be a boolean.');
}
if (typeof options.restWsBridgeTimeout !== 'number' || isNaN(options.restWsBridgeTimeout)) {
throw new TypeError('CLIENT_INVALID_OPTION', 'restWsBridgeTimeout', 'a number');
}
if (typeof options.restRequestTimeout !== 'number' || isNaN(options.restRequestTimeout)) {
throw new TypeError('CLIENT_INVALID_OPTION', 'restRequestTimeout', 'a number');
}
if (typeof options.restGlobalRateLimit !== 'number' || isNaN(options.restGlobalRateLimit)) {
throw new TypeError('CLIENT_INVALID_OPTION', 'restGlobalRateLimit', 'a number');
}
if (typeof options.restSweepInterval !== 'number' || isNaN(options.restSweepInterval)) {
throw new TypeError('CLIENT_INVALID_OPTION', 'restSweepInterval', 'a number');
throw new TypeError('The restWsBridgeTimeout option must be a number.');
}
if (!(options.disabledEvents instanceof Array)) throw new TypeError('The disabledEvents option must be an Array.');
if (typeof options.retryLimit !== 'number' || isNaN(options.retryLimit)) {
throw new TypeError('CLIENT_INVALID_OPTION', 'retryLimit', 'a number');
}
if (typeof options.failIfNotExists !== 'boolean') {
throw new TypeError('CLIENT_INVALID_OPTION', 'failIfNotExists', 'a boolean');
}
if (!Array.isArray(options.userAgentSuffix)) {
throw new TypeError('CLIENT_INVALID_OPTION', 'userAgentSuffix', 'an array of strings');
}
if (
typeof options.rejectOnRateLimit !== 'undefined' &&
!(typeof options.rejectOnRateLimit === 'function' || Array.isArray(options.rejectOnRateLimit))
) {
throw new TypeError('CLIENT_INVALID_OPTION', 'rejectOnRateLimit', 'an array or a function');
throw new TypeError('The retryLimit options must be a number.');
}
}
}
@@ -610,8 +562,3 @@ module.exports = Client;
* @event Client#debug
* @param {string} info The debug information
*/
/**
* @external Collection
* @see {@link https://discord.js.org/#/docs/collection/master/class/Collection}
*/

View File

@@ -0,0 +1,149 @@
const Constants = require('../util/Constants');
const Util = require('../util/Util');
const Guild = require('../structures/Guild');
const User = require('../structures/User');
const Emoji = require('../structures/Emoji');
const GuildChannel = require('../structures/GuildChannel');
const TextChannel = require('../structures/TextChannel');
const VoiceChannel = require('../structures/VoiceChannel');
const CategoryChannel = require('../structures/CategoryChannel');
const NewsChannel = require('../structures/NewsChannel');
const StoreChannel = require('../structures/StoreChannel');
const DMChannel = require('../structures/DMChannel');
const GroupDMChannel = require('../structures/GroupDMChannel');
class ClientDataManager {
constructor(client) {
this.client = client;
}
get pastReady() {
return this.client.ws.connection.status === Constants.Status.READY;
}
newGuild(data) {
const already = this.client.guilds.has(data.id);
const guild = new Guild(this.client, data);
this.client.guilds.set(guild.id, guild);
if (this.pastReady && !already) {
/**
* Emitted whenever the client joins a guild.
* @event Client#guildCreate
* @param {Guild} guild The created guild
*/
if (this.client.options.fetchAllMembers) {
guild.fetchMembers().then(() => { this.client.emit(Constants.Events.GUILD_CREATE, guild); });
} else {
this.client.emit(Constants.Events.GUILD_CREATE, guild);
}
}
return guild;
}
newUser(data, cache = true) {
if (this.client.users.has(data.id)) return this.client.users.get(data.id);
const user = new User(this.client, data);
if (cache) this.client.users.set(user.id, user);
return user;
}
newChannel(data, guild) {
const already = this.client.channels.has(data.id);
let channel;
if (data.type === Constants.ChannelTypes.DM) {
channel = new DMChannel(this.client, data);
} else if (data.type === Constants.ChannelTypes.GROUP_DM) {
channel = new GroupDMChannel(this.client, data);
} else {
guild = guild || this.client.guilds.get(data.guild_id);
if (already) {
channel = this.client.channels.get(data.id);
} else if (guild) {
switch (data.type) {
case Constants.ChannelTypes.TEXT:
channel = new TextChannel(guild, data);
break;
case Constants.ChannelTypes.VOICE:
channel = new VoiceChannel(guild, data);
break;
case Constants.ChannelTypes.CATEGORY:
channel = new CategoryChannel(guild, data);
break;
case Constants.ChannelTypes.NEWS:
channel = new NewsChannel(guild, data);
break;
case Constants.ChannelTypes.STORE:
channel = new StoreChannel(guild, data);
break;
}
guild.channels.set(channel.id, channel);
}
}
if (channel && !already) {
if (this.pastReady) this.client.emit(Constants.Events.CHANNEL_CREATE, channel);
this.client.channels.set(channel.id, channel);
return channel;
} else if (already) {
return channel;
}
return null;
}
newEmoji(data, guild) {
const already = guild.emojis.has(data.id);
if (data && !already) {
let emoji = new Emoji(guild, data);
this.client.emit(Constants.Events.GUILD_EMOJI_CREATE, emoji);
guild.emojis.set(emoji.id, emoji);
return emoji;
} else if (already) {
return guild.emojis.get(data.id);
}
return null;
}
killEmoji(emoji) {
if (!(emoji instanceof Emoji && emoji.guild)) return;
this.client.emit(Constants.Events.GUILD_EMOJI_DELETE, emoji);
emoji.guild.emojis.delete(emoji.id);
}
killGuild(guild) {
const already = this.client.guilds.has(guild.id);
this.client.guilds.delete(guild.id);
if (already && this.pastReady) this.client.emit(Constants.Events.GUILD_DELETE, guild);
}
killUser(user) {
this.client.users.delete(user.id);
}
killChannel(channel) {
this.client.channels.delete(channel.id);
if (channel instanceof GuildChannel) channel.guild.channels.delete(channel.id);
}
updateGuild(currentGuild, newData) {
const oldGuild = Util.cloneObject(currentGuild);
currentGuild.setup(newData);
if (this.pastReady) this.client.emit(Constants.Events.GUILD_UPDATE, oldGuild, currentGuild);
}
updateChannel(currentChannel, newData) {
currentChannel.setup(newData);
}
updateEmoji(currentEmoji, newData) {
const oldEmoji = Util.cloneObject(currentEmoji);
currentEmoji.setup(newData);
this.client.emit(Constants.Events.GUILD_EMOJI_UPDATE, oldEmoji, currentEmoji);
return currentEmoji;
}
}
module.exports = ClientDataManager;

View File

@@ -0,0 +1,376 @@
const path = require('path');
const fs = require('fs');
const snekfetch = require('snekfetch');
const Constants = require('../util/Constants');
const convertToBuffer = require('../util/Util').convertToBuffer;
const User = require('../structures/User');
const Message = require('../structures/Message');
const Guild = require('../structures/Guild');
const Channel = require('../structures/Channel');
const GuildMember = require('../structures/GuildMember');
const Emoji = require('../structures/Emoji');
const ReactionEmoji = require('../structures/ReactionEmoji');
const Role = require('../structures/Role');
/**
* The DataResolver identifies different objects and tries to resolve a specific piece of information from them, e.g.
* extracting a User from a Message object.
* @private
*/
class ClientDataResolver {
/**
* @param {Client} client The client the resolver is for
*/
constructor(client) {
this.client = client;
}
/**
* Data that resolves to give a User object. This can be:
* * A User object
* * A Snowflake
* * A Message object (resolves to the message author)
* * A Guild object (owner of the guild)
* * A GuildMember object
* @typedef {User|Snowflake|Message|Guild|GuildMember} UserResolvable
*/
/**
* Resolves a UserResolvable to a User object.
* @param {UserResolvable} user The UserResolvable to identify
* @returns {?User}
*/
resolveUser(user) {
if (user instanceof User) return user;
if (typeof user === 'string') return this.client.users.get(user) || null;
if (user instanceof GuildMember) return user.user;
if (user instanceof Message) return user.author;
if (user instanceof Guild) return this.resolveUser(user.ownerID);
return null;
}
/**
* Resolves a UserResolvable to a user ID string.
* @param {UserResolvable} user The UserResolvable to identify
* @returns {?Snowflake}
*/
resolveUserID(user) {
if (user instanceof User || user instanceof GuildMember) return user.id;
if (typeof user === 'string') return user || null;
if (user instanceof Message) return user.author.id;
if (user instanceof Guild) return user.ownerID;
return null;
}
/**
* Data that resolves to give a Guild object. This can be:
* * A Guild object
* * A Snowflake
* @typedef {Guild|Snowflake} GuildResolvable
*/
/**
* Resolves a GuildResolvable to a Guild object.
* @param {GuildResolvable} guild The GuildResolvable to identify
* @returns {?Guild}
*/
resolveGuild(guild) {
if (guild instanceof Guild) return guild;
if (typeof guild === 'string') return this.client.guilds.get(guild) || null;
return null;
}
/**
* Data that resolves to give a GuildMember object. This can be:
* * A GuildMember object
* * A User object
* @typedef {GuildMember|User} GuildMemberResolvable
*/
/**
* Resolves a GuildMemberResolvable to a GuildMember object.
* @param {GuildResolvable} guild The guild that the member is part of
* @param {UserResolvable} user The user that is part of the guild
* @returns {?GuildMember}
*/
resolveGuildMember(guild, user) {
if (user instanceof GuildMember) return user;
guild = this.resolveGuild(guild);
user = this.resolveUser(user);
if (!guild || !user) return null;
return guild.members.get(user.id) || null;
}
/**
* Data that can be resolved to a Role object. This can be:
* * A Role
* * A Snowflake
* @typedef {Role|Snowflake} RoleResolvable
*/
/**
* Resolves a RoleResolvable to a Role object.
* @param {GuildResolvable} guild The guild that this role is part of
* @param {RoleResolvable} role The role resolvable to resolve
* @returns {?Role}
*/
resolveRole(guild, role) {
if (role instanceof Role) return role;
guild = this.resolveGuild(guild);
if (!guild) return null;
if (typeof role === 'string') return guild.roles.get(role);
return null;
}
/**
* Data that can be resolved to give a Channel object. This can be:
* * A Channel object
* * A Message object (the channel the message was sent in)
* * A Guild object (the #general channel)
* * A Snowflake
* @typedef {Channel|Guild|Message|Snowflake} ChannelResolvable
*/
/**
* Resolves a ChannelResolvable to a Channel object.
* @param {ChannelResolvable} channel The channel resolvable to resolve
* @returns {?Channel}
*/
resolveChannel(channel) {
if (channel instanceof Channel) return channel;
if (typeof channel === 'string') return this.client.channels.get(channel) || null;
if (channel instanceof Message) return channel.channel;
if (channel instanceof Guild) return channel.channels.get(channel.id) || null;
return null;
}
/**
* Resolves a ChannelResolvable to a channel ID.
* @param {ChannelResolvable} channel The channel resolvable to resolve
* @returns {?Snowflake}
*/
resolveChannelID(channel) {
if (channel instanceof Channel) return channel.id;
if (typeof channel === 'string') return channel;
if (channel instanceof Message) return channel.channel.id;
if (channel instanceof Guild) return channel.defaultChannel.id;
return null;
}
/**
* Data that can be resolved to give an invite code. This can be:
* * An invite code
* * An invite URL
* @typedef {string} InviteResolvable
*/
/**
* Resolves InviteResolvable to an invite code.
* @param {InviteResolvable} data The invite resolvable to resolve
* @returns {string}
*/
resolveInviteCode(data) {
const inviteRegex = /discord(?:app\.com\/invite|\.gg(?:\/invite)?)\/([\w-]{2,255})/i;
const match = inviteRegex.exec(data);
if (match && match[1]) return match[1];
return data;
}
/**
* Data that can be resolved to give a string. This can be:
* * A string
* * An array (joined with a new line delimiter to give a string)
* * Any value
* @typedef {string|Array|*} StringResolvable
*/
/**
* Resolves a StringResolvable to a string.
* @param {StringResolvable} data The string resolvable to resolve
* @returns {string}
*/
resolveString(data) {
if (typeof data === 'string') return data;
if (data instanceof Array) return data.join('\n');
return String(data);
}
/**
* Resolves a Base64Resolvable, a string, or a BufferResolvable to a Base 64 image.
* @param {BufferResolvable|Base64Resolvable} image The image to be resolved
* @returns {Promise<?string>}
*/
resolveImage(image) {
if (!image) return Promise.resolve(null);
if (typeof image === 'string' && image.startsWith('data:')) {
return Promise.resolve(image);
}
return this.resolveFile(image).then(this.resolveBase64);
}
/**
* Data that resolves to give a Base64 string, typically for image uploading. This can be:
* * A Buffer
* * A base64 string
* @typedef {Buffer|string} Base64Resolvable
*/
/**
* Resolves a Base64Resolvable to a Base 64 image.
* @param {Base64Resolvable} data The base 64 resolvable you want to resolve
* @returns {?string}
*/
resolveBase64(data) {
if (data instanceof Buffer) return `data:image/jpg;base64,${data.toString('base64')}`;
return data;
}
/**
* Data that can be resolved to give a Buffer. This can be:
* * A Buffer
* * The path to a local file
* * A URL
* * A Stream
* @typedef {string|Buffer} BufferResolvable
*/
/**
* @external Stream
* @see {@link https://nodejs.org/api/stream.html}
*/
/**
* Resolves a BufferResolvable to a Buffer.
* @param {BufferResolvable|Stream} resource The buffer or stream resolvable to resolve
* @returns {Promise<Buffer>}
*/
resolveFile(resource) {
if (resource instanceof Buffer) return Promise.resolve(resource);
if (this.client.browser && resource instanceof ArrayBuffer) return Promise.resolve(convertToBuffer(resource));
if (typeof resource === 'string') {
if (/^https?:\/\//.test(resource)) {
return snekfetch.get(resource).then(res => res.body instanceof Buffer ? res.body : Buffer.from(res.text));
}
return new Promise((resolve, reject) => {
const file = path.resolve(resource);
fs.stat(file, (err, stats) => {
if (err) return reject(err);
if (!stats || !stats.isFile()) return reject(new Error(`The file could not be found: ${file}`));
fs.readFile(file, (err2, data) => {
if (err2) reject(err2);
else resolve(data);
});
return null;
});
});
} else if (resource && resource.pipe && typeof resource.pipe === 'function') {
return new Promise((resolve, reject) => {
const buffers = [];
resource.once('error', reject);
resource.on('data', data => buffers.push(data));
resource.once('end', () => resolve(Buffer.concat(buffers)));
});
}
return Promise.reject(new TypeError('The resource must be a string or Buffer.'));
}
/**
* Data that can be resolved to give an emoji identifier. This can be:
* * The unicode representation of an emoji
* * A custom emoji ID
* * An Emoji object
* * A ReactionEmoji object
* @typedef {string|Emoji|ReactionEmoji} EmojiIdentifierResolvable
*/
/**
* Resolves an EmojiResolvable to an emoji identifier.
* @param {EmojiIdentifierResolvable} emoji The emoji resolvable to resolve
* @returns {?string}
*/
resolveEmojiIdentifier(emoji) {
if (emoji instanceof Emoji || emoji instanceof ReactionEmoji) return emoji.identifier;
if (typeof emoji === 'string') {
if (this.client.emojis.has(emoji)) return this.client.emojis.get(emoji).identifier;
else if (!emoji.includes('%')) return encodeURIComponent(emoji);
else return emoji;
}
return null;
}
/**
* Can be a Hex Literal, Hex String, Number, RGB Array, or one of the following
* ```
* [
* 'DEFAULT',
* 'WHITE',
* 'AQUA',
* 'GREEN',
* 'BLUE',
* 'PURPLE',
* 'LUMINOUS_VIVID_PINK',
* 'GOLD',
* 'ORANGE',
* 'RED',
* 'GREY',
* 'DARKER_GREY',
* 'NAVY',
* 'DARK_AQUA',
* 'DARK_GREEN',
* 'DARK_BLUE',
* 'DARK_PURPLE',
* 'DARK_VIVID_PINK',
* 'DARK_GOLD',
* 'DARK_ORANGE',
* 'DARK_RED',
* 'DARK_GREY',
* 'LIGHT_GREY',
* 'DARK_NAVY',
* 'RANDOM',
* ]
* ```
* or something like
* ```
* [255, 0, 255]
* ```
* for purple
* @typedef {string|number|Array} ColorResolvable
*/
/**
* Resolves a ColorResolvable into a color number.
* @param {ColorResolvable} color Color to resolve
* @returns {number} A color
*/
static resolveColor(color) {
if (typeof color === 'string') {
if (color === 'RANDOM') return Math.floor(Math.random() * (0xFFFFFF + 1));
if (color === 'DEFAULT') return 0;
color = Constants.Colors[color] || parseInt(color.replace('#', ''), 16);
} else if (color instanceof Array) {
color = (color[0] << 16) + (color[1] << 8) + color[2];
}
if (color < 0 || color > 0xFFFFFF) {
throw new RangeError('Color must be within the range 0 - 16777215 (0xFFFFFF).');
} else if (color && isNaN(color)) {
throw new TypeError('Unable to convert color to a number.');
}
return color;
}
/**
* @param {ColorResolvable} color Color to resolve
* @returns {number} A color
*/
resolveColor(color) {
return this.constructor.resolveColor(color);
}
}
module.exports = ClientDataResolver;

View File

@@ -0,0 +1,74 @@
const Constants = require('../util/Constants');
const WebSocketConnection = require('./websocket/WebSocketConnection');
/**
* Manages the state and background tasks of the client.
* @private
*/
class ClientManager {
constructor(client) {
/**
* The client that instantiated this Manager
* @type {Client}
*/
this.client = client;
/**
* The heartbeat interval
* @type {?number}
*/
this.heartbeatInterval = null;
}
/**
* The status of the client
* @type {number}
*/
get status() {
return this.connection ? this.connection.status : Constants.Status.IDLE;
}
/**
* Connects the client to the WebSocket.
* @param {string} token The authorization token
* @param {Function} resolve Function to run when connection is successful
* @param {Function} reject Function to run when connection fails
*/
connectToWebSocket(token, resolve, reject) {
this.client.emit(Constants.Events.DEBUG, `Authenticated using token ${token}`);
this.client.token = token;
const timeout = this.client.setTimeout(() => reject(new Error(Constants.Errors.TOOK_TOO_LONG)), 1000 * 300);
this.client.rest.methods.getGateway().then(res => {
const protocolVersion = Constants.DefaultOptions.ws.version;
const gateway = `${res.url}/?v=${protocolVersion}&encoding=${WebSocketConnection.ENCODING}`;
this.client.emit(Constants.Events.DEBUG, `Using gateway ${gateway}`);
this.client.ws.connect(gateway);
this.client.ws.connection.once('error', reject);
this.client.ws.connection.once('close', event => {
if (event.code === 4004) reject(new Error(Constants.Errors.BAD_LOGIN));
if (event.code === 4010) reject(new Error(Constants.Errors.INVALID_SHARD));
if (event.code === 4011) reject(new Error(Constants.Errors.SHARDING_REQUIRED));
});
this.client.once(Constants.Events.READY, () => {
resolve(token);
this.client.clearTimeout(timeout);
});
}, reject);
}
destroy() {
this.client.ws.destroy();
this.client.rest.destroy();
if (!this.client.user) return Promise.resolve();
if (this.client.user.bot) {
this.client.token = null;
return Promise.resolve();
} else {
return this.client.rest.methods.logout().then(() => {
this.client.token = null;
});
}
}
}
module.exports = ClientManager;

View File

@@ -1,65 +1,118 @@
'use strict';
const BaseClient = require('./BaseClient');
const { Error } = require('../errors');
const Webhook = require('../structures/Webhook');
const RESTManager = require('./rest/RESTManager');
const ClientDataResolver = require('./ClientDataResolver');
const Constants = require('../util/Constants');
const Util = require('../util/Util');
/**
* The webhook client.
* @implements {Webhook}
* @extends {BaseClient}
* @extends {Webhook}
*/
class WebhookClient extends BaseClient {
class WebhookClient extends Webhook {
/**
* The data for the webhook client containing either an id and token or just a URL
* @typedef {Object} WebhookClientData
* @property {Snowflake} [id] The id of the webhook
* @property {string} [token] The token of the webhook
* @property {string} [url] The full url for the webhook client
*/
/**
* @param {WebhookClientData} data The data of the webhook
* @param {Snowflake} id ID of the webhook
* @param {string} token Token of the webhook
* @param {ClientOptions} [options] Options for the client
* @example
* // Create a new webhook and send a message
* const hook = new Discord.WebhookClient({ id: '1234', token: 'abcdef' });
* hook.send('This will send a message').catch(console.error);
* const hook = new Discord.WebhookClient('1234', 'abcdef');
* hook.sendMessage('This will send a message').catch(console.error);
*/
constructor(data, options) {
super(options);
Object.defineProperty(this, 'client', { value: this });
let { id, token } = data;
constructor(id, token, options) {
super(null, id, token);
if ('url' in data) {
const url = data.url.match(
// eslint-disable-next-line no-useless-escape
/^https?:\/\/(?:canary|ptb)?\.?discord\.com\/api\/webhooks(?:\/v[0-9]\d*)?\/([^\/]+)\/([^\/]+)/i,
);
/**
* The options the client was instantiated with
* @type {ClientOptions}
*/
this.options = Util.mergeDefault(Constants.DefaultOptions, options);
if (!url || url.length <= 1) throw new Error('WEBHOOK_URL_INVALID');
/**
* The REST manager of the client
* @type {RESTManager}
* @private
*/
this.rest = new RESTManager(this);
[, id, token] = url;
}
/**
* The data resolver of the client
* @type {ClientDataResolver}
* @private
*/
this.resolver = new ClientDataResolver(this);
this.id = id;
Object.defineProperty(this, 'token', { value: token, writable: true, configurable: true });
/**
* Timeouts set by {@link WebhookClient#setTimeout} that are still active
* @type {Set<Timeout>}
* @private
*/
this._timeouts = new Set();
/**
* Intervals set by {@link WebhookClient#setInterval} that are still active
* @type {Set<Timeout>}
* @private
*/
this._intervals = new Set();
}
// These are here only for documentation purposes - they are implemented by Webhook
/* eslint-disable no-empty-function */
send() {}
sendSlackMessage() {}
fetchMessage() {}
edit() {}
editMessage() {}
delete() {}
deleteMessage() {}
get createdTimestamp() {}
get createdAt() {}
get url() {}
/**
* Sets a timeout that will be automatically cancelled if the client is destroyed.
* @param {Function} fn Function to execute
* @param {number} delay Time to wait before executing (in milliseconds)
* @param {...*} args Arguments for the function
* @returns {Timeout}
*/
setTimeout(fn, delay, ...args) {
const timeout = setTimeout(() => {
fn(...args);
this._timeouts.delete(timeout);
}, delay);
this._timeouts.add(timeout);
return timeout;
}
/**
* Clears a timeout.
* @param {Timeout} timeout Timeout to cancel
*/
clearTimeout(timeout) {
clearTimeout(timeout);
this._timeouts.delete(timeout);
}
/**
* Sets an interval that will be automatically cancelled if the client is destroyed.
* @param {Function} fn Function to execute
* @param {number} delay Time to wait before executing (in milliseconds)
* @param {...*} args Arguments for the function
* @returns {Timeout}
*/
setInterval(fn, delay, ...args) {
const interval = setInterval(fn, delay, ...args);
this._intervals.add(interval);
return interval;
}
/**
* Clears an interval.
* @param {Timeout} interval Interval to cancel
*/
clearInterval(interval) {
clearInterval(interval);
this._intervals.delete(interval);
}
/**
* Destroys the client.
*/
destroy() {
for (const t of this._timeouts) clearTimeout(t);
for (const i of this._intervals) clearInterval(i);
this._timeouts.clear();
this._intervals.clear();
}
}
Webhook.applyToClass(WebhookClient);
module.exports = WebhookClient;

View File

@@ -1,7 +1,3 @@
'use strict';
const { PartialTypes } = require('../../util/Constants');
/*
ABOUT ACTIONS
@@ -22,84 +18,6 @@ class GenericAction {
handle(data) {
return data;
}
getPayload(data, manager, id, partialType, cache) {
const existing = manager.cache.get(id);
if (!existing && this.client.options.partials.includes(partialType)) {
return manager._add(data, cache);
}
return existing;
}
getChannel(data) {
const id = data.channel_id ?? data.id;
return (
data.channel ??
this.getPayload(
{
id,
guild_id: data.guild_id,
recipients: [data.author ?? data.user ?? { id: data.user_id }],
},
this.client.channels,
id,
PartialTypes.CHANNEL,
)
);
}
getMessage(data, channel, cache) {
const id = data.message_id ?? data.id;
return (
data.message ??
this.getPayload(
{
id,
channel_id: channel.id,
guild_id: data.guild_id ?? channel.guild?.id,
},
channel.messages,
id,
PartialTypes.MESSAGE,
cache,
)
);
}
getReaction(data, message, user) {
const id = data.emoji.id ?? decodeURIComponent(data.emoji.name);
return this.getPayload(
{
emoji: data.emoji,
count: message.partial ? null : 0,
me: user?.id === this.client.user.id,
},
message.reactions,
id,
PartialTypes.REACTION,
);
}
getMember(data, guild) {
return this.getPayload(data, guild.members, data.user.id, PartialTypes.GUILD_MEMBER);
}
getUser(data) {
const id = data.user_id;
return data.user ?? this.getPayload({ id }, this.client.users, id, PartialTypes.USER);
}
getUserFromMember(data) {
if (data.guild_id && data.member?.user) {
const guild = this.client.guilds.cache.get(data.guild_id);
if (guild) {
return guild.members._add(data.member).user;
} else {
return this.client.users._add(data.member.user);
}
}
return this.getUser(data);
}
}
module.exports = GenericAction;

View File

@@ -1,17 +1,38 @@
'use strict';
const fs = require('fs');
class ActionsManager {
constructor(client) {
this.client = client;
const files = fs.readdirSync(__dirname);
for (const file of files) {
if (['Action.js', 'ActionsManager.js'].includes(file)) continue;
this.register(require(`./${file}`));
}
this.register(require('./MessageCreate'));
this.register(require('./MessageDelete'));
this.register(require('./MessageDeleteBulk'));
this.register(require('./MessageUpdate'));
this.register(require('./MessageReactionAdd'));
this.register(require('./MessageReactionRemove'));
this.register(require('./MessageReactionRemoveEmoji'));
this.register(require('./MessageReactionRemoveAll'));
this.register(require('./ChannelCreate'));
this.register(require('./ChannelDelete'));
this.register(require('./ChannelUpdate'));
this.register(require('./GuildDelete'));
this.register(require('./GuildUpdate'));
this.register(require('./GuildMemberGet'));
this.register(require('./GuildMemberRemove'));
this.register(require('./GuildBanRemove'));
this.register(require('./GuildRoleCreate'));
this.register(require('./GuildRoleDelete'));
this.register(require('./GuildRoleUpdate'));
this.register(require('./InviteCreate'));
this.register(require('./InviteDelete'));
this.register(require('./UserGet'));
this.register(require('./UserUpdate'));
this.register(require('./UserNoteUpdate'));
this.register(require('./GuildSync'));
this.register(require('./GuildEmojiCreate'));
this.register(require('./GuildEmojiDelete'));
this.register(require('./GuildEmojiUpdate'));
this.register(require('./GuildEmojisUpdate'));
this.register(require('./GuildRolesPositionUpdate'));
this.register(require('./GuildChannelsPositionUpdate'));
}
register(Action) {

View File

@@ -1,21 +1,9 @@
'use strict';
const Action = require('./Action');
const { Events } = require('../../util/Constants');
class ChannelCreateAction extends Action {
handle(data) {
const client = this.client;
const existing = client.channels.cache.has(data.id);
const channel = client.channels._add(data);
if (!existing && channel) {
/**
* Emitted whenever a guild channel is created.
* @event Client#channelCreate
* @param {GuildChannel} channel The channel that was created
*/
client.emit(Events.CHANNEL_CREATE, channel);
}
const channel = client.dataManager.newChannel(data);
return { channel };
}
}

View File

@@ -1,8 +1,5 @@
'use strict';
const Action = require('./Action');
const DMChannel = require('../../structures/DMChannel');
const { Events } = require('../../util/Constants');
class ChannelDeleteAction extends Action {
constructor(client) {
@@ -12,26 +9,30 @@ class ChannelDeleteAction extends Action {
handle(data) {
const client = this.client;
const channel = client.channels.cache.get(data.id);
let channel = client.channels.get(data.id);
if (channel) {
client.dataManager.killChannel(channel);
this.deleted.set(channel.id, channel);
this.scheduleForDeletion(channel.id);
} else {
channel = this.deleted.get(data.id) || null;
}
if (channel) {
client.channels._remove(channel.id);
channel.deleted = true;
if (channel.messages && !(channel instanceof DMChannel)) {
for (const message of channel.messages.cache.values()) {
for (const message of channel.messages.values()) {
message.deleted = true;
}
}
/**
* Emitted whenever a channel is deleted.
* @event Client#channelDelete
* @param {DMChannel|GuildChannel} channel The channel that was deleted
*/
client.emit(Events.CHANNEL_DELETE, channel);
channel.deleted = true;
}
return { channel };
}
scheduleForDeletion(id) {
this.client.setTimeout(() => this.deleted.delete(id), this.client.options.restWsBridgeTimeout);
}
}
module.exports = ChannelDeleteAction;

View File

@@ -1,34 +1,74 @@
'use strict';
const Action = require('./Action');
const Channel = require('../../structures/Channel');
const { ChannelTypes } = require('../../util/Constants');
const TextChannel = require('../../structures/TextChannel');
const VoiceChannel = require('../../structures/VoiceChannel');
const CategoryChannel = require('../../structures/CategoryChannel');
const NewsChannel = require('../../structures/NewsChannel');
const StoreChannel = require('../../structures/StoreChannel');
const Constants = require('../../util/Constants');
const ChannelTypes = Constants.ChannelTypes;
const Util = require('../../util/Util');
class ChannelUpdateAction extends Action {
handle(data) {
const client = this.client;
let channel = client.channels.cache.get(data.id);
let channel = client.channels.get(data.id);
if (channel) {
const old = channel._update(data);
const oldChannel = Util.cloneObject(channel);
// If the channel is changing types, we need to follow a different process
if (ChannelTypes[channel.type.toUpperCase()] !== data.type) {
// Determine which channel class we're changing to
let channelClass;
switch (data.type) {
case ChannelTypes.TEXT:
channelClass = TextChannel;
break;
case ChannelTypes.VOICE:
channelClass = VoiceChannel;
break;
case ChannelTypes.CATEGORY:
channelClass = CategoryChannel;
break;
case ChannelTypes.NEWS:
channelClass = NewsChannel;
break;
case ChannelTypes.STORE:
channelClass = StoreChannel;
break;
}
// Create the new channel instance and copy over cached data
const newChannel = new channelClass(channel.guild, data);
if (channel.messages && newChannel.messages) {
for (const [id, message] of channel.messages) newChannel.messages.set(id, message);
}
if (ChannelTypes[channel.type] !== data.type) {
const newChannel = Channel.create(this.client, data, channel.guild);
for (const [id, message] of channel.messages.cache) newChannel.messages.cache.set(id, message);
channel = newChannel;
this.client.channels.cache.set(channel.id, channel);
this.client.channels.set(channel.id, channel);
} else {
channel.setup(data);
}
client.emit(Constants.Events.CHANNEL_UPDATE, oldChannel, channel);
return {
old,
old: oldChannel,
updated: channel,
};
} else {
client.channels._add(data);
}
return {};
return {
old: null,
updated: null,
};
}
}
/**
* Emitted whenever a channel is updated - e.g. name change, topic change.
* @event Client#channelUpdate
* @param {Channel} oldChannel The channel before the update
* @param {Channel} newChannel The channel after the update
*/
module.exports = ChannelUpdateAction;

View File

@@ -1,20 +0,0 @@
'use strict';
const Action = require('./Action');
const { Events } = require('../../util/Constants');
class GuildBanAdd extends Action {
handle(data) {
const client = this.client;
const guild = client.guilds.cache.get(data.guild_id);
/**
* Emitted whenever a member is banned from a guild.
* @event Client#guildBanAdd
* @param {GuildBan} ban The ban that occurred
*/
if (guild) client.emit(Events.GUILD_BAN_ADD, guild.bans._add(data));
}
}
module.exports = GuildBanAdd;

View File

@@ -1,24 +1,12 @@
'use strict';
const Action = require('./Action');
const GuildBan = require('../../structures/GuildBan');
const { Events } = require('../../util/Constants');
const Constants = require('../../util/Constants');
class GuildBanRemove extends Action {
handle(data) {
const client = this.client;
const guild = client.guilds.cache.get(data.guild_id);
/**
* Emitted whenever a member is unbanned from a guild.
* @event Client#guildBanRemove
* @param {GuildBan} ban The ban that was removed
*/
if (guild) {
const ban = guild.bans.cache.get(data.user.id) ?? new GuildBan(client, data, guild);
guild.bans.cache.delete(ban.user.id);
client.emit(Events.GUILD_BAN_REMOVE, ban);
}
const guild = client.guilds.get(data.guild_id);
const user = client.dataManager.newUser(data.user);
if (guild && user) client.emit(Constants.Events.GUILD_BAN_REMOVE, guild, user);
}
}

View File

@@ -1,16 +1,14 @@
'use strict';
const Action = require('./Action');
class GuildChannelsPositionUpdate extends Action {
handle(data) {
const client = this.client;
const guild = client.guilds.cache.get(data.guild_id);
const guild = client.guilds.get(data.guild_id);
if (guild) {
for (const partialChannel of data.channels) {
const channel = guild.channels.cache.get(partialChannel.id);
if (channel) channel.rawPosition = partialChannel.position;
const channel = guild.channels.get(partialChannel.id);
if (channel) channel.position = partialChannel.position;
}
}

View File

@@ -1,7 +1,5 @@
'use strict';
const Action = require('./Action');
const { Events } = require('../../util/Constants');
const Constants = require('../../util/Constants');
class GuildDeleteAction extends Action {
constructor(client) {
@@ -12,18 +10,16 @@ class GuildDeleteAction extends Action {
handle(data) {
const client = this.client;
let guild = client.guilds.cache.get(data.id);
let guild = client.guilds.get(data.id);
if (guild) {
if (data.unavailable) {
for (const channel of guild.channels.values()) {
if (channel.type === 'text') channel.stopTyping(true);
}
if (guild.available && data.unavailable) {
// Guild is unavailable
guild.available = false;
/**
* Emitted whenever a guild becomes unavailable, likely due to a server outage.
* @event Client#guildUnavailable
* @param {Guild} guild The guild that has become unavailable
*/
client.emit(Events.GUILD_UNAVAILABLE, guild);
client.emit(Constants.Events.GUILD_UNAVAILABLE, guild);
// Stops the GuildDelete packet thinking a guild was actually deleted,
// handles emitting of event itself
@@ -32,32 +28,30 @@ class GuildDeleteAction extends Action {
};
}
for (const channel of guild.channels.cache.values()) this.client.channels._remove(channel.id);
client.voice.adapters.get(data.id)?.destroy();
for (const channel of guild.channels.values()) this.client.channels.delete(channel.id);
if (guild.voiceConnection) guild.voiceConnection.disconnect();
// Delete guild
client.guilds.cache.delete(guild.id);
guild.deleted = true;
/**
* Emitted whenever a guild kicks the client or the guild is deleted/left.
* @event Client#guildDelete
* @param {Guild} guild The guild that was deleted
*/
client.emit(Events.GUILD_DELETE, guild);
client.guilds.delete(guild.id);
this.deleted.set(guild.id, guild);
this.scheduleForDeletion(guild.id);
} else {
guild = this.deleted.get(data.id) ?? null;
guild = this.deleted.get(data.id) || null;
}
if (guild) guild.deleted = true;
return { guild };
}
scheduleForDeletion(id) {
setTimeout(() => this.deleted.delete(id), this.client.options.restWsBridgeTimeout).unref();
this.client.setTimeout(() => this.deleted.delete(id), this.client.options.restWsBridgeTimeout);
}
}
/**
* Emitted whenever a guild becomes unavailable, likely due to a server outage.
* @event Client#guildUnavailable
* @param {Guild} guild The guild that has become unavailable
*/
module.exports = GuildDeleteAction;

View File

@@ -1,20 +1,17 @@
'use strict';
const Action = require('./Action');
const { Events } = require('../../util/Constants');
class GuildEmojiCreateAction extends Action {
handle(guild, createdEmoji) {
const already = guild.emojis.cache.has(createdEmoji.id);
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
*/
if (!already) this.client.emit(Events.GUILD_EMOJI_CREATE, emoji);
const client = this.client;
const emoji = client.dataManager.newEmoji(createdEmoji, guild);
return { emoji };
}
}
/**
* Emitted whenever a custom emoji is created in a guild.
* @event Client#emojiCreate
* @param {Emoji} emoji The emoji that was created
*/
module.exports = GuildEmojiCreateAction;

View File

@@ -1,20 +1,18 @@
'use strict';
const Action = require('./Action');
const { Events } = require('../../util/Constants');
class GuildEmojiDeleteAction extends Action {
handle(emoji) {
emoji.guild.emojis.cache.delete(emoji.id);
const client = this.client;
client.dataManager.killEmoji(emoji);
emoji.deleted = true;
/**
* Emitted whenever a custom emoji is deleted in a guild.
* @event Client#emojiDelete
* @param {GuildEmoji} emoji The emoji that was deleted
*/
this.client.emit(Events.GUILD_EMOJI_DELETE, emoji);
return { emoji };
}
}
/**
* Emitted whenever a custom guild emoji is deleted.
* @event Client#emojiDelete
* @param {Emoji} emoji The emoji that was deleted
*/
module.exports = GuildEmojiDeleteAction;

View File

@@ -1,20 +1,17 @@
'use strict';
const Action = require('./Action');
const { Events } = require('../../util/Constants');
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
*/
this.client.emit(Events.GUILD_EMOJI_UPDATE, old, current);
return { emoji: current };
handle(oldEmoji, newEmoji) {
const emoji = this.client.dataManager.updateEmoji(oldEmoji, newEmoji);
return { emoji };
}
}
/**
* Emitted whenever a custom guild emoji is updated.
* @event Client#emojiUpdate
* @param {Emoji} oldEmoji The old emoji
* @param {Emoji} newEmoji The new emoji
*/
module.exports = GuildEmojiUpdateAction;

View File

@@ -1,20 +1,24 @@
'use strict';
const Action = require('./Action');
function mappify(iterable) {
const map = new Map();
for (const x of iterable) map.set(...x);
return map;
}
class GuildEmojisUpdateAction extends Action {
handle(data) {
const guild = this.client.guilds.cache.get(data.guild_id);
if (!guild?.emojis) return;
const guild = this.client.guilds.get(data.guild_id);
if (!guild || !guild.emojis) return;
const deletions = new Map(guild.emojis.cache);
const deletions = mappify(guild.emojis.entries());
for (const emoji of data.emojis) {
// Determine type of emoji event
const cachedEmoji = guild.emojis.cache.get(emoji.id);
const cachedEmoji = guild.emojis.get(emoji.id);
if (cachedEmoji) {
deletions.delete(emoji.id);
if (!cachedEmoji.equals(emoji)) {
if (!cachedEmoji.equals(emoji, true)) {
// Emoji updated
this.client.actions.GuildEmojiUpdate.handle(cachedEmoji, emoji);
}

View File

@@ -1,19 +0,0 @@
'use strict';
const Action = require('./Action');
const { Events } = require('../../util/Constants');
class GuildIntegrationsUpdate extends Action {
handle(data) {
const client = this.client;
const guild = client.guilds.cache.get(data.guild_id);
/**
* Emitted whenever a guild integration is updated
* @event Client#guildIntegrationsUpdate
* @param {Guild} guild The guild whose integrations were updated
*/
if (guild) client.emit(Events.GUILD_INTEGRATIONS_UPDATE, guild);
}
}
module.exports = GuildIntegrationsUpdate;

View File

@@ -0,0 +1,10 @@
const Action = require('./Action');
class GuildMemberGetAction extends Action {
handle(guild, data) {
const member = guild._addMember(data, false);
return { member };
}
}
module.exports = GuildMemberGetAction;

View File

@@ -1,30 +1,41 @@
'use strict';
const Action = require('./Action');
const { Events, Status } = require('../../util/Constants');
const Constants = require('../../util/Constants');
class GuildMemberRemoveAction extends Action {
handle(data, shard) {
constructor(client) {
super(client);
this.deleted = new Map();
}
handle(data) {
const client = this.client;
const guild = client.guilds.cache.get(data.guild_id);
const guild = client.guilds.get(data.guild_id);
let member = null;
if (guild) {
member = this.getMember({ user: data.user }, guild);
member = guild.members.get(data.user.id);
guild.memberCount--;
if (member) {
member.deleted = true;
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
*/
if (shard.status === Status.READY) client.emit(Events.GUILD_MEMBER_REMOVE, member);
guild._removeMember(member);
this.deleted.set(guild.id + data.user.id, member);
if (client.status === Constants.Status.READY) client.emit(Constants.Events.GUILD_MEMBER_REMOVE, member);
this.scheduleForDeletion(guild.id, data.user.id);
} else {
member = this.deleted.get(guild.id + data.user.id) || null;
}
guild.voiceStates.cache.delete(data.user.id);
if (member) member.deleted = true;
}
return { guild, member };
}
scheduleForDeletion(guildID, userID) {
this.client.setTimeout(() => this.deleted.delete(guildID + userID), this.client.options.restWsBridgeTimeout);
}
}
/**
* 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
*/
module.exports = GuildMemberRemoveAction;

View File

@@ -1,44 +0,0 @@
'use strict';
const Action = require('./Action');
const { Status, Events } = require('../../util/Constants');
class GuildMemberUpdateAction extends Action {
handle(data, shard) {
const { client } = this;
if (data.user.username) {
const user = client.users.cache.get(data.user.id);
if (!user) {
client.users._add(data.user);
} else if (!user.equals(data.user)) {
client.actions.UserUpdate.handle(data.user);
}
}
const guild = client.guilds.cache.get(data.guild_id);
if (guild) {
const member = this.getMember({ user: data.user }, guild);
if (member) {
const old = member._update(data);
/**
* Emitted whenever a guild member changes - i.e. new role, removed role, nickname.
* Also emitted when the user's details (e.g. username) change.
* @event Client#guildMemberUpdate
* @param {GuildMember} oldMember The member before the update
* @param {GuildMember} newMember The member after the update
*/
if (shard.status === Status.READY && !member.equals(old)) client.emit(Events.GUILD_MEMBER_UPDATE, old, member);
} else {
const newMember = guild.members._add(data);
/**
* Emitted whenever a member becomes available in a large guild.
* @event Client#guildMemberAvailable
* @param {GuildMember} member The member that became available
*/
this.client.emit(Events.GUILD_MEMBER_AVAILABLE, newMember);
}
}
}
}
module.exports = GuildMemberUpdateAction;

View File

@@ -1,25 +1,26 @@
'use strict';
const Action = require('./Action');
const { Events } = require('../../util/Constants');
const Constants = require('../../util/Constants');
const Role = require('../../structures/Role');
class GuildRoleCreate extends Action {
handle(data) {
const client = this.client;
const guild = client.guilds.cache.get(data.guild_id);
const guild = client.guilds.get(data.guild_id);
let role;
if (guild) {
const already = guild.roles.cache.has(data.role.id);
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.GUILD_ROLE_CREATE, role);
const already = guild.roles.has(data.role.id);
role = new Role(guild, data.role);
guild.roles.set(role.id, role);
if (!already) client.emit(Constants.Events.GUILD_ROLE_CREATE, role);
}
return { role };
}
}
/**
* Emitted whenever a role is created.
* @event Client#roleCreate
* @param {Role} role The role that was created
*/
module.exports = GuildRoleCreate;

View File

@@ -1,30 +1,42 @@
'use strict';
const Action = require('./Action');
const { Events } = require('../../util/Constants');
const Constants = require('../../util/Constants');
class GuildRoleDeleteAction extends Action {
constructor(client) {
super(client);
this.deleted = new Map();
}
handle(data) {
const client = this.client;
const guild = client.guilds.cache.get(data.guild_id);
const guild = client.guilds.get(data.guild_id);
let role;
if (guild) {
role = guild.roles.cache.get(data.role_id);
role = guild.roles.get(data.role_id);
if (role) {
guild.roles.cache.delete(data.role_id);
role.deleted = true;
/**
* Emitted whenever a guild role is deleted.
* @event Client#roleDelete
* @param {Role} role The role that was deleted
*/
client.emit(Events.GUILD_ROLE_DELETE, role);
guild.roles.delete(data.role_id);
this.deleted.set(guild.id + data.role_id, role);
this.scheduleForDeletion(guild.id, data.role_id);
client.emit(Constants.Events.GUILD_ROLE_DELETE, role);
} else {
role = this.deleted.get(guild.id + data.role_id) || null;
}
if (role) role.deleted = true;
}
return { role };
}
scheduleForDeletion(guildID, roleID) {
this.client.setTimeout(() => this.deleted.delete(guildID + roleID), this.client.options.restWsBridgeTimeout);
}
}
/**
* Emitted whenever a guild role is deleted.
* @event Client#roleDelete
* @param {Role} role The role that was deleted
*/
module.exports = GuildRoleDeleteAction;

View File

@@ -1,30 +1,25 @@
'use strict';
const Action = require('./Action');
const { Events } = require('../../util/Constants');
const Constants = require('../../util/Constants');
const Util = require('../../util/Util');
class GuildRoleUpdateAction extends Action {
handle(data) {
const client = this.client;
const guild = client.guilds.cache.get(data.guild_id);
const guild = client.guilds.get(data.guild_id);
if (guild) {
let old = null;
const roleData = data.role;
let oldRole = null;
const role = guild.roles.cache.get(data.role.id);
const role = guild.roles.get(roleData.id);
if (role) {
old = role._update(data.role);
/**
* 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
*/
client.emit(Events.GUILD_ROLE_UPDATE, old, role);
oldRole = Util.cloneObject(role);
role.setup(data.role);
client.emit(Constants.Events.GUILD_ROLE_UPDATE, oldRole, role);
}
return {
old,
old: oldRole,
updated: role,
};
}
@@ -36,4 +31,11 @@ class GuildRoleUpdateAction extends Action {
}
}
/**
* 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
*/
module.exports = GuildRoleUpdateAction;

View File

@@ -1,16 +1,14 @@
'use strict';
const Action = require('./Action');
class GuildRolesPositionUpdate extends Action {
handle(data) {
const client = this.client;
const guild = client.guilds.cache.get(data.guild_id);
const guild = client.guilds.get(data.guild_id);
if (guild) {
for (const partialRole of data.roles) {
const role = guild.roles.cache.get(partialRole.id);
if (role) role.rawPosition = partialRole.position;
const role = guild.roles.get(partialRole.id);
if (role) role.position = partialRole.position;
}
}

View File

@@ -1,20 +0,0 @@
'use strict';
const Action = require('./Action');
const { Events } = require('../../util/Constants');
class GuildStickerCreateAction extends Action {
handle(guild, createdSticker) {
const already = guild.stickers.cache.has(createdSticker.id);
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
*/
if (!already) this.client.emit(Events.GUILD_STICKER_CREATE, sticker);
return { sticker };
}
}
module.exports = GuildStickerCreateAction;

View File

@@ -1,20 +0,0 @@
'use strict';
const Action = require('./Action');
const { Events } = require('../../util/Constants');
class GuildStickerDeleteAction extends Action {
handle(sticker) {
sticker.guild.stickers.cache.delete(sticker.id);
sticker.deleted = true;
/**
* Emitted whenever a custom sticker is deleted in a guild.
* @event Client#stickerDelete
* @param {Sticker} sticker The sticker that was deleted
*/
this.client.emit(Events.GUILD_STICKER_DELETE, sticker);
return { sticker };
}
}
module.exports = GuildStickerDeleteAction;

View File

@@ -1,20 +0,0 @@
'use strict';
const Action = require('./Action');
const { Events } = require('../../util/Constants');
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
*/
this.client.emit(Events.GUILD_STICKER_UPDATE, old, current);
return { sticker: current };
}
}
module.exports = GuildStickerUpdateAction;

View File

@@ -1,34 +0,0 @@
'use strict';
const Action = require('./Action');
class GuildStickersUpdateAction extends Action {
handle(data) {
const guild = this.client.guilds.cache.get(data.guild_id);
if (!guild?.stickers) return;
const deletions = new Map(guild.stickers.cache);
for (const sticker of data.stickers) {
// Determine type of sticker event
const cachedSticker = guild.stickers.cache.get(sticker.id);
if (cachedSticker) {
deletions.delete(sticker.id);
if (!cachedSticker.equals(sticker)) {
// Sticker updated
this.client.actions.GuildStickerUpdate.handle(cachedSticker, sticker);
}
} else {
// Sticker added
this.client.actions.GuildStickerCreate.handle(guild, sticker);
}
}
for (const sticker of deletions.values()) {
// Sticker deleted
this.client.actions.GuildStickerDelete.handle(sticker);
}
}
}
module.exports = GuildStickersUpdateAction;

View File

@@ -0,0 +1,29 @@
const Action = require('./Action');
class GuildSync extends Action {
handle(data) {
const client = this.client;
const guild = client.guilds.get(data.id);
if (guild) {
if (data.presences) {
for (const presence of data.presences) guild._setPresence(presence.user.id, presence);
}
if (data.members) {
for (const syncMember of data.members) {
const member = guild.members.get(syncMember.user.id);
if (member) {
guild._updateMember(member, syncMember);
} else {
guild._addMember(syncMember, false);
}
}
}
if ('large' in data) guild.large = data.large;
}
}
}
module.exports = GuildSync;

View File

@@ -1,24 +1,18 @@
'use strict';
const Action = require('./Action');
const { Events } = require('../../util/Constants');
const Constants = require('../../util/Constants');
const Util = require('../../util/Util');
class GuildUpdateAction extends Action {
handle(data) {
const client = this.client;
const guild = client.guilds.cache.get(data.id);
const guild = client.guilds.get(data.id);
if (guild) {
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
*/
client.emit(Events.GUILD_UPDATE, old, guild);
const oldGuild = Util.cloneObject(guild);
guild.setup(data);
client.emit(Constants.Events.GUILD_UPDATE, oldGuild, guild);
return {
old,
old: oldGuild,
updated: guild,
};
}
@@ -30,4 +24,11 @@ class GuildUpdateAction extends Action {
}
}
/**
* 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
*/
module.exports = GuildUpdateAction;

View File

@@ -1,66 +0,0 @@
'use strict';
const Action = require('./Action');
const ButtonInteraction = require('../../structures/ButtonInteraction');
const CommandInteraction = require('../../structures/CommandInteraction');
const SelectMenuInteraction = require('../../structures/SelectMenuInteraction');
const { Events, InteractionTypes, MessageComponentTypes } = require('../../util/Constants');
let deprecationEmitted = false;
class InteractionCreateAction extends Action {
handle(data) {
const client = this.client;
// Resolve and cache partial channels for Interaction#channel getter
this.getChannel(data);
let InteractionType;
switch (data.type) {
case InteractionTypes.APPLICATION_COMMAND:
InteractionType = CommandInteraction;
break;
case InteractionTypes.MESSAGE_COMPONENT:
switch (data.data.component_type) {
case MessageComponentTypes.BUTTON:
InteractionType = ButtonInteraction;
break;
case MessageComponentTypes.SELECT_MENU:
InteractionType = SelectMenuInteraction;
break;
default:
client.emit(
Events.DEBUG,
`[INTERACTION] Received component interaction with unknown type: ${data.data.component_type}`,
);
return;
}
break;
default:
client.emit(Events.DEBUG, `[INTERACTION] Received interaction with unknown type: ${data.type}`);
return;
}
const interaction = new InteractionType(client, data);
/**
* Emitted when an interaction is created.
* @event Client#interactionCreate
* @param {Interaction} interaction The interaction which was created
*/
client.emit(Events.INTERACTION_CREATE, interaction);
/**
* Emitted when an interaction is created.
* @event Client#interaction
* @param {Interaction} interaction The interaction which was created
* @deprecated Use {@link Client#interactionCreate} instead
*/
if (client.emit('interaction', interaction) && !deprecationEmitted) {
deprecationEmitted = true;
process.emitWarning('The interaction event is deprecated. Use interactionCreate instead', 'DeprecationWarning');
}
}
}
module.exports = InteractionCreateAction;

View File

@@ -1,27 +1,28 @@
'use strict';
const Action = require('./Action');
const Invite = require('../../structures/Invite');
const { Events } = require('../../util/Constants');
class InviteCreateAction extends Action {
handle(data) {
const client = this.client;
const channel = client.channels.cache.get(data.channel_id);
const guild = client.guilds.cache.get(data.guild_id);
if (!channel) return false;
const inviteData = Object.assign(data, { channel, guild });
const invite = guild.invites._add(inviteData);
/**
* Emitted when an invite is created.
* <info> This event only triggers if the client has `MANAGE_GUILD` permissions for the guild,
* or `MANAGE_CHANNEL` permissions for the channel.</info>
* @event Client#inviteCreate
* @param {Invite} invite The invite that was created
*/
client.emit(Events.INVITE_CREATE, invite);
return { invite };
const guild = client.guilds.get(data.guild_id);
const channel = client.channels.get(data.channel_id);
if (guild && channel) {
const inviteData = Object.assign(data, { guild, channel });
const invite = new Invite(client, inviteData);
/**
* Emitted when an invite is created.
* <info> This event only triggers if the client has `MANAGE_GUILD` permissions for the guild,
* or `MANAGE_CHANNEL` permissions for the channel.</info>
* @event Client#inviteCreate
* @param {Invite} invite The invite that was created
*/
client.emit(Events.INVITE_CREATE, invite);
return { invite };
}
return { invite: null };
}
}

View File

@@ -1,5 +1,3 @@
'use strict';
const Action = require('./Action');
const Invite = require('../../structures/Invite');
const { Events } = require('../../util/Constants');
@@ -7,23 +5,20 @@ const { Events } = require('../../util/Constants');
class InviteDeleteAction extends Action {
handle(data) {
const client = this.client;
const channel = client.channels.cache.get(data.channel_id);
const guild = client.guilds.cache.get(data.guild_id);
if (!channel) return false;
const inviteData = Object.assign(data, { channel, guild });
const invite = new Invite(client, inviteData);
guild.invites.cache.delete(invite.code);
/**
* Emitted when an invite is deleted.
* <info> This event only triggers if the client has `MANAGE_GUILD` permissions for the guild,
* or `MANAGE_CHANNEL` permissions for the channel.</info>
* @event Client#inviteDelete
* @param {Invite} invite The invite that was deleted
*/
client.emit(Events.INVITE_DELETE, invite);
return { invite };
const guild = client.guilds.get(data.guild_id);
const channel = client.channels.get(data.channel_id);
if (guild && channel) {
const inviteData = Object.assign(data, { guild, channel });
const invite = new Invite(client, inviteData);
/**
* Emitted when an invite is deleted.
* <info> This event only triggers if the client has `MANAGE_GUILD` permissions for the guild,
* or `MANAGE_CHANNEL` permissions for the channel.</info>
* @event Client#inviteDelete
* @param {Invite} invite The invite that was deleted
*/
client.emit(Events.INVITE_DELETE, invite);
}
}
}

View File

@@ -1,42 +1,52 @@
'use strict';
const Action = require('./Action');
const { Events } = require('../../util/Constants');
let deprecationEmitted = false;
const Message = require('../../structures/Message');
class MessageCreateAction extends Action {
handle(data) {
const client = this.client;
const channel = this.getChannel(data);
const channel = client.channels.get((data instanceof Array ? data[0] : data).channel_id);
const user = client.users.get((data instanceof Array ? data[0] : data).author.id);
if (channel) {
const existing = channel.messages.cache.get(data.id);
if (existing) return { message: existing };
const message = channel.messages._add(data);
channel.lastMessageId = data.id;
/**
* Emitted whenever a message is created.
* @event Client#messageCreate
* @param {Message} message The created message
*/
client.emit(Events.MESSAGE_CREATE, message);
/**
* Emitted whenever a message is created.
* @event Client#message
* @param {Message} message The created message
* @deprecated Use {@link Client#messageCreate} instead
*/
if (client.emit('message', message) && !deprecationEmitted) {
deprecationEmitted = true;
process.emitWarning('The message event is deprecated. Use messageCreate instead', 'DeprecationWarning');
const member = channel.guild ? channel.guild.member(user) : null;
if (data instanceof Array) {
const messages = new Array(data.length);
for (let i = 0; i < data.length; i++) {
messages[i] = channel._cacheMessage(new Message(channel, data[i], client));
}
const lastMessage = messages[messages.length - 1];
channel.lastMessageID = lastMessage.id;
if (user) {
user.lastMessageID = lastMessage.id;
user.lastMessage = lastMessage;
}
if (member) {
member.lastMessageID = lastMessage.id;
member.lastMessage = lastMessage;
}
return {
messages,
};
} else {
const message = channel._cacheMessage(new Message(channel, data, client));
channel.lastMessageID = data.id;
if (user) {
user.lastMessageID = data.id;
user.lastMessage = message;
}
if (member) {
member.lastMessageID = data.id;
member.lastMessage = message;
}
return {
message,
};
}
return { message };
}
return {};
return {
message: null,
};
}
}

View File

@@ -1,29 +1,35 @@
'use strict';
const Action = require('./Action');
const { Events } = require('../../util/Constants');
class MessageDeleteAction extends Action {
constructor(client) {
super(client);
this.deleted = new Map();
}
handle(data) {
const client = this.client;
const channel = this.getChannel(data);
const channel = client.channels.get(data.channel_id);
let message;
if (channel) {
message = this.getMessage(data, channel);
message = channel.messages.get(data.id);
if (message) {
channel.messages.cache.delete(message.id);
message.deleted = true;
/**
* Emitted whenever a message is deleted.
* @event Client#messageDelete
* @param {Message} message The deleted message
*/
client.emit(Events.MESSAGE_DELETE, message);
channel.messages.delete(message.id);
this.deleted.set(channel.id + message.id, message);
this.scheduleForDeletion(channel.id, message.id);
} else {
message = this.deleted.get(channel.id + data.id) || null;
}
if (message) message.deleted = true;
}
return { message };
}
scheduleForDeletion(channelID, messageID) {
this.client.setTimeout(() => this.deleted.delete(channelID + messageID),
this.client.options.restWsBridgeTimeout);
}
}
module.exports = MessageDeleteAction;

View File

@@ -1,42 +1,25 @@
'use strict';
const { Collection } = require('@discordjs/collection');
const Action = require('./Action');
const { Events } = require('../../util/Constants');
const Collection = require('../../util/Collection');
const Constants = require('../../util/Constants');
class MessageDeleteBulkAction extends Action {
handle(data) {
const client = this.client;
const channel = client.channels.cache.get(data.channel_id);
const messages = new Collection();
const channel = this.client.channels.get(data.channel_id);
if (channel) {
const ids = data.ids;
const messages = new Collection();
for (const id of ids) {
const message = this.getMessage(
{
id,
guild_id: data.guild_id,
},
channel,
false,
);
for (const id of data.ids) {
const message = channel.messages.get(id);
if (message) {
message.deleted = true;
messages.set(message.id, message);
channel.messages.cache.delete(id);
channel.messages.delete(id);
}
}
/**
* Emitted whenever messages are deleted in bulk.
* @event Client#messageDeleteBulk
* @param {Collection<Snowflake, Message>} messages The deleted messages, mapped by their id
*/
if (messages.size > 0) client.emit(Events.MESSAGE_BULK_DELETE, messages);
return { messages };
}
return {};
if (messages.size > 0) this.client.emit(Constants.Events.MESSAGE_BULK_DELETE, messages);
return { messages };
}
}

View File

@@ -1,55 +1,37 @@
'use strict';
const Action = require('./Action');
const { Events, VoiceBasedChannelTypes } = require('../../util/Constants');
const { PartialTypes } = require('../../util/Constants');
const Constants = require('../../util/Constants');
/*
{ user_id: 'id',
message_id: 'id',
emoji: { name: '<27>', id: null },
channel_id: 'id',
// If originating from a guild
guild_id: 'id',
member: { ..., user: { ... } } }
channel_id: 'id' } }
*/
class MessageReactionAdd extends Action {
handle(data) {
if (!data.emoji) return false;
const user = this.getUserFromMember(data);
const user = this.client.users.get(data.user_id);
if (!user) return false;
// Verify channel
const channel = this.getChannel(data);
if (!channel || VoiceBasedChannelTypes.includes(channel.type)) return false;
const channel = this.client.channels.get(data.channel_id);
if (!channel || channel.type === 'voice') return false;
// Verify message
const message = this.getMessage(data, channel);
const message = channel.messages.get(data.message_id);
if (!message) return false;
if (!data.emoji) return false;
// Verify reaction
if (message.partial && !this.client.options.partials.includes(PartialTypes.REACTION)) return false;
const existing = message.reactions.cache.get(data.emoji.id ?? data.emoji.name);
if (existing?.users.cache.has(user.id)) return { message, reaction: existing, user };
const reaction = message.reactions._add({
emoji: data.emoji,
count: message.partial ? null : 0,
me: user.id === this.client.user.id,
});
if (!reaction) return false;
reaction._add(user);
/**
* 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
*/
this.client.emit(Events.MESSAGE_REACTION_ADD, reaction, user);
const reaction = message._addReaction(data.emoji, user);
if (reaction) this.client.emit(Constants.Events.MESSAGE_REACTION_ADD, reaction, user);
return { message, reaction, user };
}
}
/**
* 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 emoji or reaction emoji
*/
module.exports = MessageReactionAdd;

View File

@@ -1,45 +1,37 @@
'use strict';
const Action = require('./Action');
const { Events, VoiceBasedChannelTypes } = require('../../util/Constants');
const Constants = require('../../util/Constants');
/*
{ user_id: 'id',
message_id: 'id',
emoji: { name: '<27>', id: null },
channel_id: 'id',
guild_id: 'id' }
channel_id: 'id' } }
*/
class MessageReactionRemove extends Action {
handle(data) {
if (!data.emoji) return false;
const user = this.getUser(data);
const user = this.client.users.get(data.user_id);
if (!user) return false;
// Verify channel
const channel = this.getChannel(data);
if (!channel || VoiceBasedChannelTypes.includes(channel.type)) return false;
const channel = this.client.channels.get(data.channel_id);
if (!channel || channel.type === 'voice') return false;
// Verify message
const message = this.getMessage(data, channel);
const message = channel.messages.get(data.message_id);
if (!message) return false;
if (!data.emoji) return false;
// Verify reaction
const reaction = this.getReaction(data, message, user);
if (!reaction) return false;
reaction._remove(user);
/**
* 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
*/
this.client.emit(Events.MESSAGE_REACTION_REMOVE, reaction, user);
const reaction = message._removeReaction(data.emoji, user);
if (reaction) this.client.emit(Constants.Events.MESSAGE_REACTION_REMOVE, reaction, user);
return { message, reaction, user };
}
}
/**
* 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
*/
module.exports = MessageReactionRemove;

View File

@@ -1,20 +1,16 @@
'use strict';
const Action = require('./Action');
const { Events, VoiceBasedChannelTypes } = require('../../util/Constants');
const Constants = require('../../util/Constants');
class MessageReactionRemoveAll extends Action {
handle(data) {
// Verify channel
const channel = this.getChannel(data);
if (!channel || VoiceBasedChannelTypes.includes(channel.type)) return false;
const channel = this.client.channels.get(data.channel_id);
if (!channel || channel.type === 'voice') return false;
// Verify message
const message = this.getMessage(data, channel);
const message = channel.messages.get(data.message_id);
if (!message) return false;
message.reactions.cache.clear();
this.client.emit(Events.MESSAGE_REACTION_REMOVE_ALL, message);
message._clearReactions();
this.client.emit(Constants.Events.MESSAGE_REACTION_REMOVE_ALL, message);
return { message };
}

View File

@@ -1,28 +1,27 @@
'use strict';
const Action = require('./Action');
const { Events, VoiceBasedChannelTypes } = require('../../util/Constants');
const Constants = require('../../util/Constants');
class MessageReactionRemoveEmoji extends Action {
handle(data) {
const channel = this.getChannel(data);
if (!channel || VoiceBasedChannelTypes.includes(channel.type)) return false;
const message = this.getMessage(data, channel);
// Verify channel
const channel = this.client.channels.get(data.channel_id);
if (!channel || channel.type === 'voice') return false;
// Verify message
const message = channel.messages.get(data.message_id);
if (!message) return false;
if (!data.emoji) return false;
// Verify reaction
const reaction = message._removeReaction(data.emoji);
if (reaction) this.client.emit(Constants.Events.MESSAGE_REACTION_REMOVE_EMOJI, reaction);
const reaction = this.getReaction(data, message);
if (!reaction) return false;
if (!message.partial) message.reactions.cache.delete(reaction.emoji.id ?? reaction.emoji.name);
/**
* Emitted when a bot removes an emoji reaction from a cached message.
* @event Client#messageReactionRemoveEmoji
* @param {MessageReaction} reaction The reaction that was removed
*/
this.client.emit(Events.MESSAGE_REACTION_REMOVE_EMOJI, reaction);
return { reaction };
return { message, reaction };
}
}
/**
* Emitted whenever a reaction emoji is removed from a cached message.
* @event Client#messageReactionRemoveEmoji
* @param {MessageReaction} messageReaction The reaction object
*/
module.exports = MessageReactionRemoveEmoji;

View File

@@ -1,24 +1,40 @@
'use strict';
const Action = require('./Action');
const Constants = require('../../util/Constants');
class MessageUpdateAction extends Action {
handle(data) {
const channel = this.getChannel(data);
const client = this.client;
const channel = client.channels.get(data.channel_id);
if (channel) {
const { id, channel_id, guild_id, author, timestamp, type } = data;
const message = this.getMessage({ id, channel_id, guild_id, author, timestamp, type }, channel);
const message = channel.messages.get(data.id);
if (message) {
const old = message._update(data, true);
message.patch(data);
client.emit(Constants.Events.MESSAGE_UPDATE, message._edits[0], message);
return {
old,
old: message._edits[0],
updated: message,
};
}
return {
old: message,
updated: message,
};
}
return {};
return {
old: null,
updated: null,
};
}
}
/**
* 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
*/
module.exports = MessageUpdateAction;

View File

@@ -1,42 +0,0 @@
'use strict';
const Action = require('./Action');
const { Events } = require('../../util/Constants');
class PresenceUpdateAction extends Action {
handle(data) {
let user = this.client.users.cache.get(data.user.id);
if (!user && data.user?.username) user = this.client.users._add(data.user);
if (!user) return;
if (data.user?.username) {
if (!user.equals(data.user)) this.client.actions.UserUpdate.handle(data.user);
}
const guild = this.client.guilds.cache.get(data.guild_id);
if (!guild) return;
const oldPresence = guild.presences.cache.get(user.id)?._clone() ?? null;
let member = guild.members.cache.get(user.id);
if (!member && data.status !== 'offline') {
member = guild.members._add({
user,
deaf: false,
mute: false,
});
this.client.emit(Events.GUILD_MEMBER_AVAILABLE, member);
}
const newPresence = guild.presences._add(Object.assign(data, { guild }));
if (this.client.listenerCount(Events.PRESENCE_UPDATE) && !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
*/
this.client.emit(Events.PRESENCE_UPDATE, oldPresence, newPresence);
}
}
}
module.exports = PresenceUpdateAction;

View File

@@ -1,28 +0,0 @@
'use strict';
const Action = require('./Action');
const { Events } = require('../../util/Constants');
class StageInstanceCreateAction extends Action {
handle(data) {
const client = this.client;
const channel = this.getChannel(data);
if (channel) {
const stageInstance = channel.guild.stageInstances._add(data);
/**
* Emitted whenever a stage instance is created.
* @event Client#stageInstanceCreate
* @param {StageInstance} stageInstance The created stage instance
*/
client.emit(Events.STAGE_INSTANCE_CREATE, stageInstance);
return { stageInstance };
}
return {};
}
}
module.exports = StageInstanceCreateAction;

View File

@@ -1,32 +0,0 @@
'use strict';
const Action = require('./Action');
const { Events } = require('../../util/Constants');
class StageInstanceDeleteAction extends Action {
handle(data) {
const client = this.client;
const channel = this.getChannel(data);
if (channel) {
const stageInstance = channel.guild.stageInstances._add(data);
if (stageInstance) {
channel.guild.stageInstances.cache.delete(stageInstance.id);
stageInstance.deleted = true;
/**
* Emitted whenever a stage instance is deleted.
* @event Client#stageInstanceDelete
* @param {StageInstance} stageInstance The deleted stage instance
*/
client.emit(Events.STAGE_INSTANCE_DELETE, stageInstance);
return { stageInstance };
}
}
return {};
}
}
module.exports = StageInstanceDeleteAction;

View File

@@ -1,30 +0,0 @@
'use strict';
const Action = require('./Action');
const { Events } = require('../../util/Constants');
class StageInstanceUpdateAction extends Action {
handle(data) {
const client = this.client;
const channel = this.getChannel(data);
if (channel) {
const oldStageInstance = channel.guild.stageInstances.cache.get(data.id)?._clone() ?? null;
const newStageInstance = channel.guild.stageInstances._add(data);
/**
* 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
*/
client.emit(Events.STAGE_INSTANCE_UPDATE, oldStageInstance, newStageInstance);
return { oldStageInstance, newStageInstance };
}
return {};
}
}
module.exports = StageInstanceUpdateAction;

View File

@@ -1,23 +0,0 @@
'use strict';
const Action = require('./Action');
const { Events } = require('../../util/Constants');
class ThreadCreateAction extends Action {
handle(data) {
const client = this.client;
const existing = client.channels.cache.has(data.id);
const thread = client.channels._add(data);
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
*/
client.emit(Events.THREAD_CREATE, thread);
}
return { thread };
}
}
module.exports = ThreadCreateAction;

View File

@@ -1,30 +0,0 @@
'use strict';
const Action = require('./Action');
const { Events } = require('../../util/Constants');
class ThreadDeleteAction extends Action {
handle(data) {
const client = this.client;
const thread = client.channels.cache.get(data.id);
if (thread) {
client.channels._remove(thread.id);
thread.deleted = true;
for (const message of thread.messages.cache.values()) {
message.deleted = true;
}
/**
* Emitted whenever a thread is deleted.
* @event Client#threadDelete
* @param {ThreadChannel} thread The thread that was deleted
*/
client.emit(Events.THREAD_DELETE, thread);
}
return { thread };
}
}
module.exports = ThreadDeleteAction;

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