mirror of
https://github.com/discordjs/discord.js.git
synced 2026-03-09 16:13:31 +01:00
chore: consistency/prettier (#3852)
* chore: consistency/prettier * chore: rebase * chore: rebase * chore: include typings * fix: include typings file in prettier lint-staged
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"extends": "eslint:recommended",
|
||||
"extends": ["eslint:recommended", "plugin:prettier/recommended"],
|
||||
"plugins": ["import"],
|
||||
"parserOptions": {
|
||||
"ecmaVersion": 2019
|
||||
},
|
||||
@@ -7,42 +8,58 @@
|
||||
"es6": true,
|
||||
"node": true
|
||||
},
|
||||
"overrides": [
|
||||
{ "files": ["*.browser.js"], "env": { "browser": true } }
|
||||
],
|
||||
"overrides": [{ "files": ["*.browser.js"], "env": { "browser": 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"
|
||||
}
|
||||
],
|
||||
"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"],
|
||||
@@ -100,7 +117,6 @@
|
||||
"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",
|
||||
@@ -112,7 +128,6 @@
|
||||
"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",
|
||||
@@ -122,18 +137,20 @@
|
||||
"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",
|
||||
{
|
||||
"anonymous": "never",
|
||||
"named": "never",
|
||||
"asyncArrow": "always"
|
||||
}
|
||||
],
|
||||
"space-in-parens": "error",
|
||||
"space-infix-ops": "error",
|
||||
"space-unary-ops": "error",
|
||||
|
||||
1
.github/CONTRIBUTING.md
vendored
1
.github/CONTRIBUTING.md
vendored
@@ -7,6 +7,7 @@ pull request. We use ESLint to enforce a consistent coding style, so having that
|
||||
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
|
||||
|
||||
8
.github/ISSUE_TEMPLATE/bug_report.md
vendored
8
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -1,12 +1,11 @@
|
||||
---
|
||||
|
||||
name: Bug report
|
||||
about: Report incorrect or unexpected behaviour of discord.js
|
||||
title: ''
|
||||
labels: 's: unverified, type: bug'
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
<!--
|
||||
If you need help with discord.js installation or usage, please go to the discord.js Discord server instead:
|
||||
https://discord.gg/bRCvFy9
|
||||
@@ -16,15 +15,14 @@ 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
|
||||
|
||||
// Place your code here
|
||||
|
||||
```
|
||||
|
||||
**Further details:**
|
||||
|
||||
- discord.js version:
|
||||
- Node.js version:
|
||||
- Operating system:
|
||||
|
||||
3
.github/ISSUE_TEMPLATE/feature_request.md
vendored
3
.github/ISSUE_TEMPLATE/feature_request.md
vendored
@@ -1,12 +1,11 @@
|
||||
---
|
||||
|
||||
name: Feature request
|
||||
about: Request a feature for the core discord.js library
|
||||
title: ''
|
||||
labels: 'type: enhancement'
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
<!--
|
||||
If you need help with discord.js installation or usage, please go to the discord.js Discord server instead:
|
||||
https://discord.gg/bRCvFy9
|
||||
|
||||
5
.github/PULL_REQUEST_TEMPLATE.md
vendored
5
.github/PULL_REQUEST_TEMPLATE.md
vendored
@@ -1,11 +1,12 @@
|
||||
**Please describe the changes this PR makes and why it should be merged:**
|
||||
|
||||
|
||||
**Status**
|
||||
|
||||
- [ ] 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
|
||||
|
||||
**Semantic versioning classification:**
|
||||
**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.
|
||||
|
||||
@@ -1,12 +1,8 @@
|
||||
{
|
||||
"ecmaVersion": 7,
|
||||
"libs": [],
|
||||
"loadEagerly": [
|
||||
"./src/*.js"
|
||||
],
|
||||
"dontLoad": [
|
||||
"node_modules/**"
|
||||
],
|
||||
"loadEagerly": ["./src/*.js"],
|
||||
"dontLoad": ["node_modules/**"],
|
||||
"plugins": {
|
||||
"es_modules": {},
|
||||
"node": {},
|
||||
|
||||
42
README.md
42
README.md
@@ -18,17 +18,19 @@
|
||||
</div>
|
||||
|
||||
## Table of contents
|
||||
|
||||
- [About](#about)
|
||||
- [Installation](#installation)
|
||||
- [Audio engines](#audio-engines)
|
||||
- [Optional packages](#optional-packages)
|
||||
- [Example Usage](#example-usage)
|
||||
- [Links](#links)
|
||||
- [Extensions](#extensions)
|
||||
- [Contributing](#contributing)
|
||||
- [Help](#help)
|
||||
- [Example Usage](#example-usage)
|
||||
- [Links](#links)
|
||||
- [Extensions](#extensions)
|
||||
- [Contributing](#contributing)
|
||||
- [Help](#help)
|
||||
|
||||
## About
|
||||
|
||||
discord.js is a powerful [Node.js](https://nodejs.org) module that allows you to easily interact with the
|
||||
[Discord API](https://discordapp.com/developers/docs/intro).
|
||||
|
||||
@@ -38,6 +40,7 @@ discord.js is a powerful [Node.js](https://nodejs.org) module that allows you to
|
||||
- 100% coverage of the Discord API
|
||||
|
||||
## Installation
|
||||
|
||||
**Node.js 11.0.0 or newer is required.**
|
||||
Ignore any warnings about unmet peer dependencies, as they're all optional.
|
||||
|
||||
@@ -46,20 +49,23 @@ With voice support ([@discordjs/opus](https://www.npmjs.com/package/@discordjs/o
|
||||
With voice support ([opusscript](https://www.npmjs.com/package/opusscript)): `npm install discordjs/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
|
||||
|
||||
- [zlib-sync](https://www.npmjs.com/package/zlib-sync) for WebSocket data compression and inflation (`npm install zlib-sync`)
|
||||
- [erlpack](https://github.com/discordapp/erlpack) for significantly faster WebSocket data (de)serialisation (`npm install discordapp/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`)
|
||||
- [sodium](https://www.npmjs.com/package/sodium) (`npm install sodium`)
|
||||
- [libsodium.js](https://www.npmjs.com/package/libsodium-wrappers) (`npm install libsodium-wrappers`)
|
||||
- [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`)
|
||||
|
||||
## Example usage
|
||||
|
||||
```js
|
||||
const Discord = require('discord.js');
|
||||
const client = new Discord.Client();
|
||||
@@ -78,24 +84,28 @@ 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)) - this is still for stable
|
||||
|
||||
- [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)) - this is still for stable
|
||||
See also the [Update Guide](https://discordjs.guide/additional-info/changes-in-v12.html), including updated and removed items in the library.
|
||||
* [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)
|
||||
- [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/bRCvFy9).
|
||||
|
||||
@@ -24,13 +24,13 @@ client.on('ready', () => {
|
||||
});
|
||||
|
||||
client.on('message', message => {
|
||||
// If the message is '!rip'
|
||||
if (message.content === '!rip') {
|
||||
// Create the attachment using MessageAttachment
|
||||
const attachment = new MessageAttachment('https://i.imgur.com/w3duR07.png');
|
||||
// Send the attachment in the message channel
|
||||
message.channel.send(attachment);
|
||||
}
|
||||
// If the message is '!rip'
|
||||
if (message.content === '!rip') {
|
||||
// Create the attachment using MessageAttachment
|
||||
const attachment = new MessageAttachment('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
|
||||
@@ -59,13 +59,13 @@ client.on('ready', () => {
|
||||
});
|
||||
|
||||
client.on('message', message => {
|
||||
// If the message is '!rip'
|
||||
if (message.content === '!rip') {
|
||||
// Create the attachment using MessageAttachment
|
||||
const attachment = new MessageAttachment('https://i.imgur.com/w3duR07.png');
|
||||
// Send the attachment in the message channel with a content
|
||||
message.channel.send(`${message.author},`, attachment);
|
||||
}
|
||||
// If the message is '!rip'
|
||||
if (message.content === '!rip') {
|
||||
// Create the attachment using MessageAttachment
|
||||
const attachment = new MessageAttachment('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
|
||||
@@ -96,13 +96,13 @@ client.on('ready', () => {
|
||||
});
|
||||
|
||||
client.on('message', message => {
|
||||
// If the message is '!rip'
|
||||
if (message.content === '!rip') {
|
||||
// Create the attachment using MessageAttachment
|
||||
const attachment = new MessageAttachment('./rip.png');
|
||||
// Send the attachment in the message channel with a content
|
||||
message.channel.send(`${message.author},`, attachment);
|
||||
}
|
||||
// If the message is '!rip'
|
||||
if (message.content === '!rip') {
|
||||
// Create the attachment using MessageAttachment
|
||||
const attachment = new MessageAttachment('./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
|
||||
@@ -137,21 +137,21 @@ client.on('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');
|
||||
// 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 MessageAttachment,
|
||||
* overwritting the default file name to 'memes.txt'
|
||||
* Read more about it over at
|
||||
* http://discord.js.org/#/docs/main/master/class/MessageAttachment
|
||||
*/
|
||||
const attachment = new MessageAttachment(buffer, 'memes.txt');
|
||||
// Send the attachment in the message channel with a content
|
||||
message.channel.send(`${message.author}, here are your memes!`, attachment);
|
||||
}
|
||||
/**
|
||||
* Create the attachment using MessageAttachment,
|
||||
* overwritting the default file name to 'memes.txt'
|
||||
* Read more about it over at
|
||||
* http://discord.js.org/#/docs/main/master/class/MessageAttachment
|
||||
*/
|
||||
const attachment = new MessageAttachment(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
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* An example of how you can send embeds
|
||||
*/
|
||||
@@ -26,7 +28,7 @@ client.on('message', message => {
|
||||
// Set the title of the field
|
||||
.setTitle('A slick little embed')
|
||||
// Set the color of the embed
|
||||
.setColor(0xFF0000)
|
||||
.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
|
||||
|
||||
@@ -4,7 +4,7 @@ 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.
|
||||
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
|
||||
@@ -41,24 +41,27 @@ client.on('message', message => {
|
||||
* 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);
|
||||
});
|
||||
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!');
|
||||
message.reply("That user isn't in this guild!");
|
||||
}
|
||||
// Otherwise, if no user was mentioned
|
||||
// Otherwise, if no user was mentioned
|
||||
} else {
|
||||
message.reply('You didn\'t mention the user to kick!');
|
||||
message.reply("You didn't mention the user to kick!");
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -112,26 +115,29 @@ client.on('message', message => {
|
||||
* Read more about what ban options there are over at
|
||||
* https://discord.js.org/#/docs/main/master/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);
|
||||
});
|
||||
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!');
|
||||
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!');
|
||||
// Otherwise, if no user was mentioned
|
||||
message.reply("You didn't mention the user to ban!");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,22 +1,26 @@
|
||||
# Frequently Asked Questions
|
||||
|
||||
These questions are some of the most frequently asked.
|
||||
|
||||
|
||||
## No matter what, I get `SyntaxError: Block-scoped declarations (let, const, function, class) not yet supported outside strict mode`‽
|
||||
|
||||
Update to Node.js 11.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!
|
||||
|
||||
@@ -1,72 +1,87 @@
|
||||
# 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
|
||||
|
||||
- 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
|
||||
message_sweep_interval: 60,
|
||||
});
|
||||
```
|
||||
|
||||
v10 code example:
|
||||
|
||||
```js
|
||||
const client = new Discord.Client({
|
||||
disableEveryone: true,
|
||||
messageCacheMaxSize: 500,
|
||||
messageCacheLifetime: 120,
|
||||
messageSweepInterval: 60
|
||||
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
|
||||
@@ -81,6 +96,7 @@ Additionally, the introduction of the Presence class keeps all of the presence d
|
||||
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
|
||||
@@ -90,41 +106,46 @@ 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) |
|
||||
|
||||
| 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
|
||||
@@ -137,18 +158,21 @@ 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()`
|
||||
|
||||
- `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`)
|
||||
|
||||
- `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:
|
||||
|
||||
@@ -159,7 +183,7 @@ client.getChannelLogs(channel, 100, function(messages) {
|
||||
```
|
||||
|
||||
```js
|
||||
channel.fetchMessages({limit: 100}).then(messages => {
|
||||
channel.fetchMessages({ limit: 100 }).then(messages => {
|
||||
console.log(`${messages.size} messages found`);
|
||||
});
|
||||
```
|
||||
|
||||
@@ -18,12 +18,14 @@
|
||||
</div>
|
||||
|
||||
# Welcome!
|
||||
|
||||
Welcome to the discord.js v12 documentation.
|
||||
|
||||
v12 is still very much a work-in-progress, as we're aiming to make it the best it can possibly be before releasing.
|
||||
Only use it if you are fond of living life on the bleeding edge.
|
||||
|
||||
## About
|
||||
|
||||
discord.js is a powerful [Node.js](https://nodejs.org) module that allows you to easily interact with the
|
||||
[Discord API](https://discordapp.com/developers/docs/intro).
|
||||
|
||||
@@ -33,6 +35,7 @@ discord.js is a powerful [Node.js](https://nodejs.org) module that allows you to
|
||||
- 100% coverage of the Discord API
|
||||
|
||||
## Installation
|
||||
|
||||
**Node.js 11.0.0 or newer is required.**
|
||||
Ignore any warnings about unmet peer dependencies, as they're all optional.
|
||||
|
||||
@@ -41,20 +44,23 @@ With voice support ([@discordjs/opus](https://www.npmjs.com/package/@discordjs/o
|
||||
With voice support ([opusscript](https://www.npmjs.com/package/opusscript)): `npm install discordjs/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
|
||||
|
||||
- [zlib-sync](https://www.npmjs.com/package/zlib-sync) for WebSocket data compression and inflation (`npm install zlib-sync`)
|
||||
- [erlpack](https://github.com/discordapp/erlpack) for significantly faster WebSocket data (de)serialisation (`npm install discordapp/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`)
|
||||
- [sodium](https://www.npmjs.com/package/sodium) (`npm install sodium`)
|
||||
- [libsodium.js](https://www.npmjs.com/package/libsodium-wrappers) (`npm install libsodium-wrappers`)
|
||||
- [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`)
|
||||
|
||||
## Example usage
|
||||
|
||||
```js
|
||||
const Discord = require('discord.js');
|
||||
const client = new Discord.Client();
|
||||
@@ -73,24 +79,28 @@ 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)) - this is still for stable
|
||||
|
||||
- [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)) - this is still for stable
|
||||
See also the WIP [Update Guide](https://discordjs.guide/additional-info/changes-in-v12.html) also including updated and removed items in the library.
|
||||
* [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)
|
||||
- [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/bRCvFy9).
|
||||
|
||||
@@ -36,7 +36,7 @@ client.on('messageDelete', message => {
|
||||
if (!message.partial) {
|
||||
console.log(`It had content: "${message.content}"`);
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
// You can also try to upgrade partials to full instances:
|
||||
client.on('messageReactionAdd', async (reaction, user) => {
|
||||
|
||||
@@ -1,20 +1,23 @@
|
||||
# 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-static`
|
||||
* an opus encoder, choose one from below:
|
||||
* `npm install @discordjs/opus` (better performance)
|
||||
* `npm install opusscript`
|
||||
* a good network connection
|
||||
|
||||
- FFmpeg - `npm install ffmpeg-static`
|
||||
- an opus encoder, choose one from below:
|
||||
- `npm install @discordjs/opus` (better performance)
|
||||
- `npm install opusscript`
|
||||
- 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.
|
||||
|
||||
@@ -41,10 +44,12 @@ client.on('message', async message => {
|
||||
```
|
||||
|
||||
## 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.
|
||||
|
||||
### Introduction to playing on voice connections
|
||||
|
||||
The most basic example of playing audio over a connection would be playing a local file:
|
||||
|
||||
```js
|
||||
@@ -70,7 +75,7 @@ We can also pass in options when we first play the stream:
|
||||
|
||||
```js
|
||||
const dispatcher = connection.play('/home/discord/audio.mp3', {
|
||||
volume: 0.5
|
||||
volume: 0.5,
|
||||
});
|
||||
```
|
||||
|
||||
@@ -81,9 +86,7 @@ Discord.js allows you to play a lot of things:
|
||||
```js
|
||||
// ReadableStreams, in this example YouTube audio
|
||||
const ytdl = require('ytdl-core');
|
||||
connection.play(ytdl(
|
||||
'https://www.youtube.com/watch?v=ZlAU_w7-Xp8',
|
||||
{ filter: 'audioonly' }));
|
||||
connection.play(ytdl('https://www.youtube.com/watch?v=ZlAU_w7-Xp8', { filter: 'audioonly' }));
|
||||
|
||||
// Files on the internet
|
||||
connection.play('http://www.sample-videos.com/audio/mp3/wave.mp3');
|
||||
@@ -96,11 +99,11 @@ New to v12 is the ability to play OggOpus and WebmOpus streams with much better
|
||||
|
||||
```js
|
||||
connection.play(fs.createReadStream('./media.webm'), {
|
||||
type: 'webm/opus'
|
||||
type: 'webm/opus',
|
||||
});
|
||||
|
||||
connection.play(fs.createReadStream('./media.ogg'), {
|
||||
type: 'ogg/opus'
|
||||
type: 'ogg/opus',
|
||||
});
|
||||
```
|
||||
|
||||
@@ -119,7 +122,7 @@ broadcast.on('subscribe', dispatcher => {
|
||||
|
||||
broadcast.on('unsubscribe', dispatcher => {
|
||||
console.log('Channel unsubscribed from broadcast :(');
|
||||
})
|
||||
});
|
||||
```
|
||||
|
||||
`broadcast` is an instance of `VoiceBroadcast`, which has the same `play` method you are used to with regular VoiceConnections:
|
||||
@@ -133,4 +136,5 @@ connection.play(broadcast);
|
||||
It's important to note that the `dispatcher` stored above is a `BroadcastDispatcher` - it controls all the dispatcher subscribed to the broadcast, e.g. setting the volume of this dispatcher affects the volume of all subscribers.
|
||||
|
||||
## Voice Receive
|
||||
|
||||
coming soon™
|
||||
|
||||
@@ -1,26 +1,32 @@
|
||||
# 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.
|
||||
|
||||
## 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 native optional packages are usable.
|
||||
|
||||
### Require Library
|
||||
|
||||
If you are making your own webpack project, you can require `discord.js/browser` wherever you need to use discord.js, like so:
|
||||
|
||||
```js
|
||||
const Discord = require('discord.js/browser');
|
||||
// do something with Discord like you normally would
|
||||
```
|
||||
|
||||
### Webpack File
|
||||
|
||||
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>
|
||||
```
|
||||
@@ -29,6 +35,7 @@ Rather than importing discord.js with `require('discord.js')`, the entire `Disco
|
||||
The usage of the API isn't any different from using it in Node.js.
|
||||
|
||||
#### Example
|
||||
|
||||
```html
|
||||
<script type="text/javascript" src="discord.11.1.0.min.js"></script>
|
||||
<script type="text/javascript">
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
{
|
||||
"plugins": ["node_modules/jsdoc-strip-async-await"]
|
||||
"plugins": ["node_modules/jsdoc-strip-async-await"]
|
||||
}
|
||||
|
||||
16
package.json
16
package.json
@@ -8,9 +8,10 @@
|
||||
"test": "npm run lint && npm run docs:test && npm run lint:typings",
|
||||
"docs": "docgen --source src --custom docs/index.yml --output docs/docs.json",
|
||||
"docs:test": "docgen --source src --custom docs/index.yml",
|
||||
"lint": "eslint src *.js",
|
||||
"lint:fix": "eslint --fix src",
|
||||
"lint": "eslint src",
|
||||
"lint:fix": "eslint src --fix",
|
||||
"lint:typings": "tslint typings/index.d.ts",
|
||||
"prettier": "prettier --write --single-quote --print-width 120 --trailing-comma all --end-of-line lf src/**/*.js typings/**/*.ts",
|
||||
"build:browser": "webpack",
|
||||
"prepublishOnly": "npm run test && NODE_ENV=production npm run build:browser"
|
||||
},
|
||||
@@ -80,9 +81,14 @@
|
||||
"discord.js-docgen": "discordjs/docgen",
|
||||
"dtslint": "^3.0.0",
|
||||
"eslint": "^6.8.0",
|
||||
"eslint-config-prettier": "^6.10.0",
|
||||
"eslint-plugin-import": "^2.20.1",
|
||||
"eslint-plugin-prettier": "^3.1.2",
|
||||
"husky": "^4.2.3",
|
||||
"jest": "^25.1.0",
|
||||
"json-filter-loader": "^1.0.0",
|
||||
"lint-staged": "^10.0.8",
|
||||
"prettier": "^1.19.1",
|
||||
"terser-webpack-plugin": "^1.2.2",
|
||||
"tslint": "^6.0.0",
|
||||
"typescript": "^3.8.2",
|
||||
@@ -126,10 +132,14 @@
|
||||
},
|
||||
"husky": {
|
||||
"hooks": {
|
||||
"pre-commit": "npm test",
|
||||
"pre-commit": "lint-staged",
|
||||
"commit-msg": "commitlint -E HUSKY_GIT_PARAMS"
|
||||
}
|
||||
},
|
||||
"lint-staged": {
|
||||
"*.js": "eslint --fix",
|
||||
"*.ts": "prettier --write --single-quote --print-width 120 --trailing-comma all --end-of-line lf"
|
||||
},
|
||||
"commitlint": {
|
||||
"extends": ["@commitlint/config-angular"],
|
||||
"rules": {
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
require('setimmediate');
|
||||
const EventEmitter = require('events');
|
||||
const RESTManager = require('../rest/RESTManager');
|
||||
const Util = require('../util/Util');
|
||||
const { DefaultOptions } = require('../util/Constants');
|
||||
const Util = require('../util/Util');
|
||||
|
||||
/**
|
||||
* The base class for all clients.
|
||||
|
||||
@@ -1,24 +1,24 @@
|
||||
'use strict';
|
||||
|
||||
const BaseClient = require('./BaseClient');
|
||||
const Permissions = require('../util/Permissions');
|
||||
const ActionsManager = require('./actions/ActionsManager');
|
||||
const ClientVoiceManager = require('./voice/ClientVoiceManager');
|
||||
const WebSocketManager = require('./websocket/WebSocketManager');
|
||||
const ActionsManager = require('./actions/ActionsManager');
|
||||
const Collection = require('../util/Collection');
|
||||
const { Error, TypeError, RangeError } = require('../errors');
|
||||
const ChannelManager = require('../managers/ChannelManager');
|
||||
const GuildEmojiManager = require('../managers/GuildEmojiManager');
|
||||
const GuildManager = require('../managers/GuildManager');
|
||||
const UserManager = require('../managers/UserManager');
|
||||
const ShardClientUtil = require('../sharding/ShardClientUtil');
|
||||
const ClientApplication = require('../structures/ClientApplication');
|
||||
const Invite = require('../structures/Invite');
|
||||
const VoiceRegion = require('../structures/VoiceRegion');
|
||||
const Webhook = require('../structures/Webhook');
|
||||
const Invite = require('../structures/Invite');
|
||||
const ClientApplication = require('../structures/ClientApplication');
|
||||
const ShardClientUtil = require('../sharding/ShardClientUtil');
|
||||
const UserManager = require('../managers/UserManager');
|
||||
const ChannelManager = require('../managers/ChannelManager');
|
||||
const GuildManager = require('../managers/GuildManager');
|
||||
const GuildEmojiManager = require('../managers/GuildEmojiManager');
|
||||
const Collection = require('../util/Collection');
|
||||
const { Events, browser, DefaultOptions } = require('../util/Constants');
|
||||
const DataResolver = require('../util/DataResolver');
|
||||
const Permissions = require('../util/Permissions');
|
||||
const Structures = require('../util/Structures');
|
||||
const { Error, TypeError, RangeError } = require('../errors');
|
||||
|
||||
/**
|
||||
* The main hub for interacting with the Discord API, and the starting point for any bot.
|
||||
@@ -63,9 +63,11 @@ class Client extends BaseClient {
|
||||
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)),
|
||||
)];
|
||||
this.options.shards = [
|
||||
...new Set(
|
||||
this.options.shards.filter(item => !isNaN(item) && item >= 0 && item < Infinity && item === (item | 0)),
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
this._validateOptions();
|
||||
@@ -93,9 +95,10 @@ class Client extends BaseClient {
|
||||
* Shard helpers for the client (only if the process was spawned from a {@link ShardingManager})
|
||||
* @type {?ShardClientUtil}
|
||||
*/
|
||||
this.shard = !browser && process.env.SHARDING_MANAGER ?
|
||||
ShardClientUtil.singleton(this, process.env.SHARDING_MANAGER_MODE) :
|
||||
null;
|
||||
this.shard =
|
||||
!browser && process.env.SHARDING_MANAGER
|
||||
? ShardClientUtil.singleton(this, process.env.SHARDING_MANAGER_MODE)
|
||||
: null;
|
||||
|
||||
/**
|
||||
* All of the {@link User} objects that have been cached at any point, mapped by their IDs
|
||||
@@ -198,8 +201,12 @@ class Client extends BaseClient {
|
||||
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('.')}`,
|
||||
this.emit(
|
||||
Events.DEBUG,
|
||||
`Provided token: ${token
|
||||
.split('.')
|
||||
.map((val, i) => (i > 1 ? val.replace(/./g, '*') : val))
|
||||
.join('.')}`,
|
||||
);
|
||||
|
||||
if (this.options.presence) {
|
||||
@@ -238,7 +245,9 @@ class Client extends BaseClient {
|
||||
*/
|
||||
fetchInvite(invite) {
|
||||
const code = DataResolver.resolveInviteCode(invite);
|
||||
return this.api.invites(code).get({ query: { with_counts: true } })
|
||||
return this.api
|
||||
.invites(code)
|
||||
.get({ query: { with_counts: true } })
|
||||
.then(data => new Invite(this, data));
|
||||
}
|
||||
|
||||
@@ -253,7 +262,10 @@ class Client extends BaseClient {
|
||||
* .catch(console.error);
|
||||
*/
|
||||
fetchWebhook(id, token) {
|
||||
return this.api.webhooks(id, token).get().then(data => new Webhook(this, data));
|
||||
return this.api
|
||||
.webhooks(id, token)
|
||||
.get()
|
||||
.then(data => new Webhook(this, data));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -289,7 +301,7 @@ class Client extends BaseClient {
|
||||
throw new TypeError('INVALID_TYPE', 'lifetime', 'number');
|
||||
}
|
||||
if (lifetime <= 0) {
|
||||
this.emit(Events.DEBUG, 'Didn\'t sweep messages - lifetime is unlimited');
|
||||
this.emit(Events.DEBUG, "Didn't sweep messages - lifetime is unlimited");
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -307,8 +319,10 @@ class Client extends BaseClient {
|
||||
);
|
||||
}
|
||||
|
||||
this.emit(Events.DEBUG,
|
||||
`Swept ${messages} messages older than ${lifetime} seconds in ${channels} text-based channels`);
|
||||
this.emit(
|
||||
Events.DEBUG,
|
||||
`Swept ${messages} messages older than ${lifetime} seconds in ${channels} text-based channels`,
|
||||
);
|
||||
return messages;
|
||||
}
|
||||
|
||||
@@ -317,7 +331,9 @@ class Client extends BaseClient {
|
||||
* @returns {Promise<ClientApplication>}
|
||||
*/
|
||||
fetchApplication() {
|
||||
return this.api.oauth2.applications('@me').get()
|
||||
return this.api.oauth2
|
||||
.applications('@me')
|
||||
.get()
|
||||
.then(app => new ClientApplication(this, app));
|
||||
}
|
||||
|
||||
@@ -364,14 +380,12 @@ class Client extends BaseClient {
|
||||
* @param {ClientOptions} [options=this.options] Options to validate
|
||||
* @private
|
||||
*/
|
||||
_validateOptions(options = this.options) { // eslint-disable-line complexity
|
||||
_validateOptions(options = this.options) {
|
||||
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 (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.shards && !(options.shards === 'auto' || Array.isArray(options.shards))) {
|
||||
throw new TypeError('CLIENT_INVALID_OPTION', 'shards', "'auto', a number or array of numbers");
|
||||
}
|
||||
if (options.shards && !options.shards.length) throw new RangeError('CLIENT_INVALID_PROVIDED_SHARDS');
|
||||
if (typeof options.messageCacheMaxSize !== 'number' || isNaN(options.messageCacheMaxSize)) {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
'use strict';
|
||||
|
||||
const Webhook = require('../structures/Webhook');
|
||||
const BaseClient = require('./BaseClient');
|
||||
const Webhook = require('../structures/Webhook');
|
||||
|
||||
/**
|
||||
* The webhook client.
|
||||
|
||||
@@ -33,38 +33,65 @@ class GenericAction {
|
||||
|
||||
getChannel(data) {
|
||||
const id = data.channel_id || data.id;
|
||||
return data.channel || this.getPayload({
|
||||
id,
|
||||
guild_id: data.guild_id,
|
||||
recipients: [data.author || { id: data.user_id }],
|
||||
}, this.client.channels, id, PartialTypes.CHANNEL);
|
||||
return (
|
||||
data.channel ||
|
||||
this.getPayload(
|
||||
{
|
||||
id,
|
||||
guild_id: data.guild_id,
|
||||
recipients: [data.author || { 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 ? channel.guild.id : null),
|
||||
}, channel.messages, id, PartialTypes.MESSAGE, cache);
|
||||
return (
|
||||
data.message ||
|
||||
this.getPayload(
|
||||
{
|
||||
id,
|
||||
channel_id: channel.id,
|
||||
guild_id: data.guild_id || (channel.guild ? channel.guild.id : null),
|
||||
},
|
||||
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 ? user.id === this.client.user.id : false,
|
||||
}, message.reactions, id, PartialTypes.REACTION);
|
||||
return this.getPayload(
|
||||
{
|
||||
emoji: data.emoji,
|
||||
count: message.partial ? null : 0,
|
||||
me: user ? user.id === this.client.user.id : false,
|
||||
},
|
||||
message.reactions,
|
||||
id,
|
||||
PartialTypes.REACTION,
|
||||
);
|
||||
}
|
||||
|
||||
getMember(data, guild) {
|
||||
const id = data.user.id;
|
||||
return this.getPayload({
|
||||
user: {
|
||||
id,
|
||||
return this.getPayload(
|
||||
{
|
||||
user: {
|
||||
id,
|
||||
},
|
||||
},
|
||||
}, guild.members, id, PartialTypes.GUILD_MEMBER);
|
||||
guild.members,
|
||||
id,
|
||||
PartialTypes.GUILD_MEMBER,
|
||||
);
|
||||
}
|
||||
|
||||
getUser(data) {
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
'use strict';
|
||||
|
||||
const Action = require('./Action');
|
||||
const { Events } = require('../../util/Constants');
|
||||
const DMChannel = require('../../structures/DMChannel');
|
||||
const { Events } = require('../../util/Constants');
|
||||
|
||||
class ChannelDeleteAction extends Action {
|
||||
constructor(client) {
|
||||
|
||||
@@ -17,4 +17,3 @@ class GuildIntegrationsUpdate extends Action {
|
||||
}
|
||||
|
||||
module.exports = GuildIntegrationsUpdate;
|
||||
|
||||
|
||||
@@ -27,5 +27,4 @@ class GuildMemberRemoveAction extends Action {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
module.exports = GuildMemberRemoveAction;
|
||||
|
||||
@@ -22,5 +22,4 @@ class GuildRoleCreate extends Action {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
module.exports = GuildRoleCreate;
|
||||
|
||||
@@ -27,5 +27,4 @@ class GuildRoleDeleteAction extends Action {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
module.exports = GuildRoleDeleteAction;
|
||||
|
||||
@@ -36,5 +36,4 @@ class GuildRoleUpdateAction extends Action {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
module.exports = GuildRoleUpdateAction;
|
||||
|
||||
@@ -30,5 +30,4 @@ class GuildUpdateAction extends Action {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
module.exports = GuildUpdateAction;
|
||||
|
||||
@@ -36,5 +36,4 @@ class MessageCreateAction extends Action {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
module.exports = MessageCreateAction;
|
||||
|
||||
@@ -26,5 +26,4 @@ class MessageDeleteAction extends Action {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
module.exports = MessageDeleteAction;
|
||||
|
||||
@@ -13,10 +13,14 @@ class MessageDeleteBulkAction extends Action {
|
||||
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);
|
||||
const message = this.getMessage(
|
||||
{
|
||||
id,
|
||||
guild_id: data.guild_id,
|
||||
},
|
||||
channel,
|
||||
false,
|
||||
);
|
||||
if (message) {
|
||||
message.deleted = true;
|
||||
messages.set(message.id, message);
|
||||
@@ -36,5 +40,4 @@ class MessageDeleteBulkAction extends Action {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
module.exports = MessageDeleteBulkAction;
|
||||
|
||||
@@ -41,5 +41,4 @@ class MessageReactionRemove extends Action {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
module.exports = MessageReactionRemove;
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
'use strict';
|
||||
|
||||
const Action = require('./Action');
|
||||
const { Events } = require('../../util/Constants');
|
||||
const VoiceState = require('../../structures/VoiceState');
|
||||
const { Events } = require('../../util/Constants');
|
||||
|
||||
class VoiceStateUpdate extends Action {
|
||||
handle(data) {
|
||||
@@ -10,9 +10,9 @@ class VoiceStateUpdate extends Action {
|
||||
const guild = client.guilds.cache.get(data.guild_id);
|
||||
if (guild) {
|
||||
// Update the state
|
||||
const oldState = guild.voiceStates.cache.has(data.user_id) ?
|
||||
guild.voiceStates.cache.get(data.user_id)._clone() :
|
||||
new VoiceState(guild, { user_id: data.user_id });
|
||||
const oldState = guild.voiceStates.cache.has(data.user_id)
|
||||
? guild.voiceStates.cache.get(data.user_id)._clone()
|
||||
: new VoiceState(guild, { user_id: data.user_id });
|
||||
|
||||
const newState = guild.voiceStates.add(data);
|
||||
|
||||
@@ -41,5 +41,4 @@ class VoiceStateUpdate extends Action {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
module.exports = VoiceStateUpdate;
|
||||
|
||||
@@ -16,5 +16,4 @@ class WebhooksUpdate extends Action {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
module.exports = WebhooksUpdate;
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
'use strict';
|
||||
|
||||
const Collection = require('../../util/Collection');
|
||||
const VoiceConnection = require('./VoiceConnection');
|
||||
const VoiceBroadcast = require('./VoiceBroadcast');
|
||||
const VoiceConnection = require('./VoiceConnection');
|
||||
const { Error } = require('../../errors');
|
||||
const Collection = require('../../util/Collection');
|
||||
|
||||
/**
|
||||
* Manages voice connections for the client
|
||||
@@ -83,7 +83,8 @@ class ClientVoiceManager {
|
||||
} else {
|
||||
connection = new VoiceConnection(this, channel);
|
||||
connection.on('debug', msg =>
|
||||
this.client.emit('debug', `[VOICE (${channel.guild.id}:${connection.status})]: ${msg}`));
|
||||
this.client.emit('debug', `[VOICE (${channel.guild.id}:${connection.status})]: ${msg}`),
|
||||
);
|
||||
connection.authenticate();
|
||||
this.connections.set(channel.guild.id, connection);
|
||||
}
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
|
||||
const EventEmitter = require('events');
|
||||
const BroadcastAudioPlayer = require('./player/BroadcastAudioPlayer');
|
||||
const { Events } = require('../../util/Constants');
|
||||
const PlayInterface = require('./util/PlayInterface');
|
||||
const { Events } = require('../../util/Constants');
|
||||
|
||||
/**
|
||||
* A voice broadcast can be played across multiple voice connections for improved shared-stream efficiency.
|
||||
@@ -59,8 +59,9 @@ class VoiceBroadcast extends EventEmitter {
|
||||
* broadcast.play('http://www.sample-videos.com/audio/mp3/wave.mp3');
|
||||
* @returns {BroadcastDispatcher}
|
||||
*/
|
||||
play() { return null; }
|
||||
|
||||
play() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ends the broadcast, unsubscribing all subscribed channels and deleting the broadcast
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
'use strict';
|
||||
|
||||
const VoiceWebSocket = require('./networking/VoiceWebSocket');
|
||||
const EventEmitter = require('events');
|
||||
const VoiceUDP = require('./networking/VoiceUDPClient');
|
||||
const Util = require('../../util/Util');
|
||||
const { OPCodes, VoiceOPCodes, VoiceStatus, Events } = require('../../util/Constants');
|
||||
const VoiceWebSocket = require('./networking/VoiceWebSocket');
|
||||
const AudioPlayer = require('./player/AudioPlayer');
|
||||
const VoiceReceiver = require('./receiver/Receiver');
|
||||
const EventEmitter = require('events');
|
||||
const { Error } = require('../../errors');
|
||||
const PlayInterface = require('./util/PlayInterface');
|
||||
const Speaking = require('../../util/Speaking');
|
||||
const Silence = require('./util/Silence');
|
||||
const { Error } = require('../../errors');
|
||||
const { OPCodes, VoiceOPCodes, VoiceStatus, Events } = require('../../util/Constants');
|
||||
const Speaking = require('../../util/Speaking');
|
||||
const Util = require('../../util/Util');
|
||||
|
||||
// Workaround for Discord now requiring silence to be sent before being able to receive audio
|
||||
class SingleSilence extends Silence {
|
||||
@@ -20,11 +20,7 @@ class SingleSilence extends Silence {
|
||||
}
|
||||
}
|
||||
|
||||
const SUPPORTED_MODES = [
|
||||
'xsalsa20_poly1305_lite',
|
||||
'xsalsa20_poly1305_suffix',
|
||||
'xsalsa20_poly1305',
|
||||
];
|
||||
const SUPPORTED_MODES = ['xsalsa20_poly1305_lite', 'xsalsa20_poly1305_suffix', 'xsalsa20_poly1305'];
|
||||
|
||||
/**
|
||||
* Represents a connection to a guild's voice server.
|
||||
@@ -154,16 +150,18 @@ class VoiceConnection extends EventEmitter {
|
||||
if (this.speaking.equals(value)) return;
|
||||
if (this.status !== VoiceStatus.CONNECTED) return;
|
||||
this.speaking = new Speaking(value).freeze();
|
||||
this.sockets.ws.sendPacket({
|
||||
op: VoiceOPCodes.SPEAKING,
|
||||
d: {
|
||||
speaking: this.speaking.bitfield,
|
||||
delay: 0,
|
||||
ssrc: this.authentication.ssrc,
|
||||
},
|
||||
}).catch(e => {
|
||||
this.emit('debug', e);
|
||||
});
|
||||
this.sockets.ws
|
||||
.sendPacket({
|
||||
op: VoiceOPCodes.SPEAKING,
|
||||
d: {
|
||||
speaking: this.speaking.bitfield,
|
||||
delay: 0,
|
||||
ssrc: this.authentication.ssrc,
|
||||
},
|
||||
})
|
||||
.catch(e => {
|
||||
this.emit('debug', e);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -181,19 +179,25 @@ class VoiceConnection extends EventEmitter {
|
||||
* @private
|
||||
*/
|
||||
sendVoiceStateUpdate(options = {}) {
|
||||
options = Util.mergeDefault({
|
||||
guild_id: this.channel.guild.id,
|
||||
channel_id: this.channel.id,
|
||||
self_mute: this.voice ? this.voice.selfMute : false,
|
||||
self_deaf: this.voice ? this.voice.selfDeaf : false,
|
||||
}, options);
|
||||
options = Util.mergeDefault(
|
||||
{
|
||||
guild_id: this.channel.guild.id,
|
||||
channel_id: this.channel.id,
|
||||
self_mute: this.voice ? this.voice.selfMute : false,
|
||||
self_deaf: this.voice ? this.voice.selfDeaf : false,
|
||||
},
|
||||
options,
|
||||
);
|
||||
|
||||
this.emit('debug', `Sending voice state update: ${JSON.stringify(options)}`);
|
||||
|
||||
return this.channel.guild.shard.send({
|
||||
op: OPCodes.VOICE_STATE_UPDATE,
|
||||
d: options,
|
||||
}, true);
|
||||
return this.channel.guild.shard.send(
|
||||
{
|
||||
op: OPCodes.VOICE_STATE_UPDATE,
|
||||
d: options,
|
||||
},
|
||||
true,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -318,8 +322,7 @@ class VoiceConnection extends EventEmitter {
|
||||
*/
|
||||
authenticate() {
|
||||
this.sendVoiceStateUpdate();
|
||||
this.connectTimeout = this.client.setTimeout(
|
||||
() => this.authenticateFailed('VOICE_CONNECTION_TIMEOUT'), 15000);
|
||||
this.connectTimeout = this.client.setTimeout(() => this.authenticateFailed('VOICE_CONNECTION_TIMEOUT'), 15000);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -371,7 +374,6 @@ class VoiceConnection extends EventEmitter {
|
||||
this.emit('disconnect');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Cleans up after disconnect.
|
||||
* @private
|
||||
@@ -513,7 +515,7 @@ class VoiceConnection extends EventEmitter {
|
||||
}
|
||||
}
|
||||
|
||||
play() { } // eslint-disable-line no-empty-function
|
||||
play() {} // eslint-disable-line no-empty-function
|
||||
}
|
||||
|
||||
PlayInterface.applyToClass(VoiceConnection);
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
'use strict';
|
||||
|
||||
const VolumeInterface = require('../util/VolumeInterface');
|
||||
const { Writable } = require('stream');
|
||||
|
||||
const secretbox = require('../util/Secretbox');
|
||||
const Silence = require('../util/Silence');
|
||||
const VolumeInterface = require('../util/VolumeInterface');
|
||||
|
||||
const FRAME_LENGTH = 20;
|
||||
const CHANNELS = 2;
|
||||
const TIMESTAMP_INC = (48000 / 100) * CHANNELS;
|
||||
|
||||
const MAX_NONCE_SIZE = (2 ** 32) - 1;
|
||||
const MAX_NONCE_SIZE = 2 ** 32 - 1;
|
||||
const nonce = Buffer.alloc(24);
|
||||
|
||||
/**
|
||||
@@ -31,10 +30,7 @@ const nonce = Buffer.alloc(24);
|
||||
* @extends {WritableStream}
|
||||
*/
|
||||
class StreamDispatcher extends Writable {
|
||||
constructor(
|
||||
player,
|
||||
{ seek = 0, volume = 1, fec, plp, bitrate = 96, highWaterMark = 12 } = {},
|
||||
streams) {
|
||||
constructor(player, { seek = 0, volume = 1, fec, plp, bitrate = 96, highWaterMark = 12 } = {}, streams) {
|
||||
const streamOptions = { seek, volume, fec, plp, bitrate, highWaterMark };
|
||||
super(streamOptions);
|
||||
/**
|
||||
@@ -146,7 +142,9 @@ class StreamDispatcher extends Writable {
|
||||
* @type {boolean}
|
||||
* @readonly
|
||||
*/
|
||||
get paused() { return Boolean(this.pausedSince); }
|
||||
get paused() {
|
||||
return Boolean(this.pausedSince);
|
||||
}
|
||||
|
||||
/**
|
||||
* Total time that this dispatcher has been paused in milliseconds
|
||||
@@ -233,7 +231,7 @@ class StreamDispatcher extends Writable {
|
||||
done();
|
||||
};
|
||||
if (!this.streams.broadcast) {
|
||||
const next = FRAME_LENGTH + (this.count * FRAME_LENGTH) - (Date.now() - this.startTime - this._pausedTime);
|
||||
const next = FRAME_LENGTH + this.count * FRAME_LENGTH - (Date.now() - this.startTime - this._pausedTime);
|
||||
setTimeout(() => {
|
||||
if ((!this.pausedSince || this._silence) && this._writeCallback) this._writeCallback();
|
||||
}, next);
|
||||
@@ -261,10 +259,7 @@ class StreamDispatcher extends Writable {
|
||||
this._nonce++;
|
||||
if (this._nonce > MAX_NONCE_SIZE) this._nonce = 0;
|
||||
this._nonceBuffer.writeUInt32BE(this._nonce, 0);
|
||||
return [
|
||||
secretbox.methods.close(buffer, this._nonceBuffer, secret_key),
|
||||
this._nonceBuffer.slice(0, 4),
|
||||
];
|
||||
return [secretbox.methods.close(buffer, this._nonceBuffer, secret_key), this._nonceBuffer.slice(0, 4)];
|
||||
} else if (mode === 'xsalsa20_poly1305_suffix') {
|
||||
const random = secretbox.methods.random(24);
|
||||
return [secretbox.methods.close(buffer, random, secret_key), random];
|
||||
@@ -297,11 +292,10 @@ class StreamDispatcher extends Writable {
|
||||
this.emit('debug', 'Failed to send a packet - no UDP socket');
|
||||
return;
|
||||
}
|
||||
this.player.voiceConnection.sockets.udp.send(packet)
|
||||
.catch(e => {
|
||||
this._setSpeaking(0);
|
||||
this.emit('debug', `Failed to send a packet - ${e}`);
|
||||
});
|
||||
this.player.voiceConnection.sockets.udp.send(packet).catch(e => {
|
||||
this._setSpeaking(0);
|
||||
this.emit('debug', `Failed to send a packet - ${e}`);
|
||||
});
|
||||
}
|
||||
|
||||
_setSpeaking(value) {
|
||||
@@ -316,14 +310,18 @@ class StreamDispatcher extends Writable {
|
||||
this.emit('speaking', value);
|
||||
}
|
||||
|
||||
get volumeEditable() { return Boolean(this.streams.volume); }
|
||||
get volumeEditable() {
|
||||
return Boolean(this.streams.volume);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether or not the Opus bitrate of this stream is editable
|
||||
* @type {boolean}
|
||||
* @readonly
|
||||
*/
|
||||
get bitrateEditable() { return this.streams.opus && this.streams.opus.setBitrate; }
|
||||
get bitrateEditable() {
|
||||
return this.streams.opus && this.streams.opus.setBitrate;
|
||||
}
|
||||
|
||||
// Volume
|
||||
get volume() {
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
'use strict';
|
||||
|
||||
const udp = require('dgram');
|
||||
const { VoiceOPCodes } = require('../../../util/Constants');
|
||||
const EventEmitter = require('events');
|
||||
const { Error } = require('../../../errors');
|
||||
const { VoiceOPCodes } = require('../../../util/Constants');
|
||||
|
||||
/**
|
||||
* Represents a UDP client for a Voice Connection.
|
||||
@@ -90,7 +90,7 @@ class VoiceConnectionUDPClient extends EventEmitter {
|
||||
|
||||
async createUDPSocket(address) {
|
||||
this.discordAddress = address;
|
||||
const socket = this.socket = udp.createSocket('udp4');
|
||||
const socket = (this.socket = udp.createSocket('udp4'));
|
||||
socket.on('error', e => {
|
||||
this.emit('debug', `[UDP] Error: ${e}`);
|
||||
this.emit('error', e);
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
'use strict';
|
||||
|
||||
const { OPCodes, VoiceOPCodes } = require('../../../util/Constants');
|
||||
const EventEmitter = require('events');
|
||||
const { Error } = require('../../../errors');
|
||||
const WebSocket = require('../../../WebSocket');
|
||||
const { Error } = require('../../../errors');
|
||||
const { OPCodes, VoiceOPCodes } = require('../../../util/Constants');
|
||||
|
||||
/**
|
||||
* Represents a Voice Connection's WebSocket.
|
||||
@@ -92,7 +92,8 @@ class VoiceWebSocket extends EventEmitter {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (!this.ws || this.ws.readyState !== WebSocket.OPEN) throw new Error('WS_NOT_OPEN', data);
|
||||
this.ws.send(data, null, error => {
|
||||
if (error) reject(error); else resolve(data);
|
||||
if (error) reject(error);
|
||||
else resolve(data);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,17 +1,11 @@
|
||||
'use strict';
|
||||
|
||||
const EventEmitter = require('events').EventEmitter;
|
||||
const EventEmitter = require('events');
|
||||
const { Readable: ReadableStream } = require('stream');
|
||||
const prism = require('prism-media');
|
||||
const StreamDispatcher = require('../dispatcher/StreamDispatcher');
|
||||
|
||||
const FFMPEG_ARGUMENTS = [
|
||||
'-analyzeduration', '0',
|
||||
'-loglevel', '0',
|
||||
'-f', 's16le',
|
||||
'-ar', '48000',
|
||||
'-ac', '2',
|
||||
];
|
||||
const FFMPEG_ARGUMENTS = ['-analyzeduration', '0', '-loglevel', '0', '-f', 's16le', '-ar', '48000', '-ac', '2'];
|
||||
|
||||
/**
|
||||
* An Audio Player for a Voice Connection.
|
||||
@@ -61,7 +55,7 @@ class BasePlayer extends EventEmitter {
|
||||
|
||||
playPCMStream(stream, options, streams = {}) {
|
||||
this.destroyDispatcher();
|
||||
const opus = streams.opus = new prism.opus.Encoder({ channels: 2, rate: 48000, frameSize: 960 });
|
||||
const opus = (streams.opus = new prism.opus.Encoder({ channels: 2, rate: 48000, frameSize: 960 }));
|
||||
if (options && options.volume === false) {
|
||||
stream.pipe(opus);
|
||||
return this.playOpusStream(opus, options, streams);
|
||||
@@ -90,7 +84,7 @@ class BasePlayer extends EventEmitter {
|
||||
|
||||
createDispatcher(options, streams, broadcast) {
|
||||
this.destroyDispatcher();
|
||||
const dispatcher = this.dispatcher = new StreamDispatcher(this, options, streams, broadcast);
|
||||
const dispatcher = (this.dispatcher = new StreamDispatcher(this, options, streams, broadcast));
|
||||
return dispatcher;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
'use strict';
|
||||
|
||||
const BroadcastDispatcher = require('../dispatcher/BroadcastDispatcher');
|
||||
const BasePlayer = require('./BasePlayer');
|
||||
const BroadcastDispatcher = require('../dispatcher/BroadcastDispatcher');
|
||||
|
||||
/**
|
||||
* An Audio Player for a Voice Connection.
|
||||
@@ -20,7 +20,7 @@ class AudioPlayer extends BasePlayer {
|
||||
|
||||
createDispatcher(options, streams) {
|
||||
this.destroyDispatcher();
|
||||
const dispatcher = this.dispatcher = new BroadcastDispatcher(this, options, streams);
|
||||
const dispatcher = (this.dispatcher = new BroadcastDispatcher(this, options, streams));
|
||||
return dispatcher;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
'use strict';
|
||||
|
||||
const secretbox = require('../util/Secretbox');
|
||||
const EventEmitter = require('events');
|
||||
const secretbox = require('../util/Secretbox');
|
||||
|
||||
// The delay between packets when a user is considered to have stopped speaking
|
||||
// https://github.com/discordjs/discord.js/issues/3524#issuecomment-540373200
|
||||
const DISCORD_SPEAKING_DELAY = 250;
|
||||
|
||||
class Readable extends require('stream').Readable { _read() {} } // eslint-disable-line no-empty-function
|
||||
class Readable extends require('stream').Readable {
|
||||
_read() {} // eslint-disable-line no-empty-function
|
||||
}
|
||||
|
||||
class PacketHandler extends EventEmitter {
|
||||
constructor(receiver) {
|
||||
@@ -59,7 +61,7 @@ class PacketHandler extends EventEmitter {
|
||||
packet = Buffer.from(packet);
|
||||
|
||||
// Strip RTP Header Extensions (one-byte only)
|
||||
if (packet[0] === 0xBE && packet[1] === 0xDE && packet.length > 4) {
|
||||
if (packet[0] === 0xbe && packet[1] === 0xde && packet.length > 4) {
|
||||
const headerExtensionLength = packet.readUInt16BE(2);
|
||||
let offset = 4;
|
||||
for (let i = 0; i < headerExtensionLength; i++) {
|
||||
|
||||
@@ -85,12 +85,12 @@ class PlayInterface {
|
||||
|
||||
static applyToClass(structure) {
|
||||
for (const prop of ['play']) {
|
||||
Object.defineProperty(structure.prototype, prop,
|
||||
Object.getOwnPropertyDescriptor(PlayInterface.prototype, prop));
|
||||
Object.defineProperty(structure.prototype, prop, Object.getOwnPropertyDescriptor(PlayInterface.prototype, prop));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = PlayInterface;
|
||||
|
||||
// eslint-disable-next-line import/order
|
||||
const Broadcast = require('../VoiceBroadcast');
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
const { Readable } = require('stream');
|
||||
|
||||
const SILENCE_FRAME = Buffer.from([0xF8, 0xFF, 0xFE]);
|
||||
const SILENCE_FRAME = Buffer.from([0xf8, 0xff, 0xfe]);
|
||||
|
||||
class Silence extends Readable {
|
||||
_read() {
|
||||
|
||||
@@ -94,19 +94,10 @@ class VolumeInterface extends EventEmitter {
|
||||
}
|
||||
}
|
||||
|
||||
const props = [
|
||||
'volumeDecibels',
|
||||
'volumeLogarithmic',
|
||||
'setVolumeDecibels',
|
||||
'setVolumeLogarithmic',
|
||||
];
|
||||
const props = ['volumeDecibels', 'volumeLogarithmic', 'setVolumeDecibels', 'setVolumeLogarithmic'];
|
||||
|
||||
exports.applyToClass = function applyToClass(structure) {
|
||||
for (const prop of props) {
|
||||
Object.defineProperty(
|
||||
structure.prototype,
|
||||
prop,
|
||||
Object.getOwnPropertyDescriptor(VolumeInterface.prototype, prop),
|
||||
);
|
||||
Object.defineProperty(structure.prototype, prop, Object.getOwnPropertyDescriptor(VolumeInterface.prototype, prop));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
'use strict';
|
||||
|
||||
const EventEmitter = require('events');
|
||||
const WebSocketShard = require('./WebSocketShard');
|
||||
const PacketHandlers = require('./handlers');
|
||||
const { Error: DJSError } = require('../../errors');
|
||||
const Collection = require('../../util/Collection');
|
||||
const Util = require('../../util/Util');
|
||||
const WebSocketShard = require('./WebSocketShard');
|
||||
const { Events, ShardEvents, Status, WSCodes, WSEvents } = require('../../util/Constants');
|
||||
const PacketHandlers = require('./handlers');
|
||||
const Util = require('../../util/Util');
|
||||
|
||||
const BeforeReadyWhitelist = [
|
||||
WSEvents.READY,
|
||||
@@ -18,7 +18,9 @@ const BeforeReadyWhitelist = [
|
||||
WSEvents.GUILD_MEMBER_REMOVE,
|
||||
];
|
||||
|
||||
const UNRECOVERABLE_CLOSE_CODES = Object.keys(WSCodes).slice(1).map(Number);
|
||||
const UNRECOVERABLE_CLOSE_CODES = Object.keys(WSCodes)
|
||||
.slice(1)
|
||||
.map(Number);
|
||||
const UNRESUMABLE_CLOSE_CODES = [1000, 4006, 4007];
|
||||
|
||||
/**
|
||||
|
||||
@@ -237,14 +237,15 @@ class WebSocketShard extends EventEmitter {
|
||||
Gateway : ${gateway}
|
||||
Version : ${client.options.ws.version}
|
||||
Encoding : ${WebSocket.encoding}
|
||||
Compression: ${zlib ? 'zlib-stream' : 'none'}`);
|
||||
Compression: ${zlib ? 'zlib-stream' : 'none'}`,
|
||||
);
|
||||
|
||||
this.status = this.status === Status.DISCONNECTED ? Status.RECONNECTING : Status.CONNECTING;
|
||||
this.setHelloTimeout();
|
||||
|
||||
this.connectedAt = Date.now();
|
||||
|
||||
const ws = this.connection = WebSocket.create(gateway, wsQuery);
|
||||
const ws = (this.connection = WebSocket.create(gateway, wsQuery));
|
||||
ws.onopen = this.onOpen.bind(this);
|
||||
ws.onmessage = this.onMessage.bind(this);
|
||||
ws.onerror = this.onError.bind(this);
|
||||
@@ -271,11 +272,8 @@ class WebSocketShard extends EventEmitter {
|
||||
if (data instanceof ArrayBuffer) data = new Uint8Array(data);
|
||||
if (zlib) {
|
||||
const l = data.length;
|
||||
const flush = l >= 4 &&
|
||||
data[l - 4] === 0x00 &&
|
||||
data[l - 3] === 0x00 &&
|
||||
data[l - 2] === 0xFF &&
|
||||
data[l - 1] === 0xFF;
|
||||
const flush =
|
||||
l >= 4 && data[l - 4] === 0x00 && data[l - 3] === 0x00 && data[l - 2] === 0xff && data[l - 1] === 0xff;
|
||||
|
||||
this.inflate.push(data, flush && zlib.Z_SYNC_FLUSH);
|
||||
if (!flush) return;
|
||||
@@ -529,8 +527,10 @@ class WebSocketShard extends EventEmitter {
|
||||
* @param {boolean} [ignoreHeartbeatAck] If we should send the heartbeat forcefully.
|
||||
* @private
|
||||
*/
|
||||
sendHeartbeat(tag = 'HeartbeatTimer',
|
||||
ignoreHeartbeatAck = [Status.WAITING_FOR_GUILDS, Status.IDENTIFYING, Status.RESUMING].includes(this.status)) {
|
||||
sendHeartbeat(
|
||||
tag = 'HeartbeatTimer',
|
||||
ignoreHeartbeatAck = [Status.WAITING_FOR_GUILDS, Status.IDENTIFYING, Status.RESUMING].includes(this.status),
|
||||
) {
|
||||
if (ignoreHeartbeatAck && !this.lastHeartbeatAcked) {
|
||||
this.debug(`[${tag}] Didn't process heartbeat ack yet but we are still connected. Sending one now.`);
|
||||
} else if (!this.lastHeartbeatAcked) {
|
||||
@@ -538,7 +538,8 @@ class WebSocketShard extends EventEmitter {
|
||||
`[${tag}] Didn't receive a heartbeat ack last time, assuming zombie connection. Destroying and reconnecting.
|
||||
Status : ${STATUS_KEYS[this.status]}
|
||||
Sequence : ${this.sequence}
|
||||
Connection State: ${this.connection ? CONNECTION_STATE[this.connection.readyState] : 'No Connection??'}`);
|
||||
Connection State: ${this.connection ? CONNECTION_STATE[this.connection.readyState] : 'No Connection??'}`,
|
||||
);
|
||||
|
||||
this.destroy({ closeCode: 4009, reset: true });
|
||||
return;
|
||||
@@ -741,10 +742,7 @@ class WebSocketShard extends EventEmitter {
|
||||
* @private
|
||||
*/
|
||||
_cleanupConnection() {
|
||||
this.connection.onopen =
|
||||
this.connection.onclose =
|
||||
this.connection.onerror =
|
||||
this.connection.onmessage = null;
|
||||
this.connection.onopen = this.connection.onclose = this.connection.onerror = this.connection.onmessage = null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -14,4 +14,3 @@ module.exports = (client, packet) => {
|
||||
client.emit(Events.CHANNEL_UPDATE, old, updated);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -10,9 +10,9 @@ module.exports = async (client, { d: data }, shard) => {
|
||||
guild._patch(data);
|
||||
// If the client was ready before and we had unavailable guilds, fetch them
|
||||
if (client.ws.status === Status.READY && client.options.fetchAllMembers) {
|
||||
await guild.members.fetch().catch(err =>
|
||||
client.emit(Events.DEBUG, `Failed to fetch all members: ${err}\n${err.stack}`),
|
||||
);
|
||||
await guild.members
|
||||
.fetch()
|
||||
.catch(err => client.emit(Events.DEBUG, `Failed to fetch all members: ${err}\n${err.stack}`));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -26,9 +26,9 @@ module.exports = async (client, { d: data }, shard) => {
|
||||
* @param {Guild} guild The created guild
|
||||
*/
|
||||
if (client.options.fetchAllMembers) {
|
||||
await guild.members.fetch().catch(err =>
|
||||
client.emit(Events.DEBUG, `Failed to fetch all members: ${err}\n${err.stack}`),
|
||||
);
|
||||
await guild.members
|
||||
.fetch()
|
||||
.catch(err => client.emit(Events.DEBUG, `Failed to fetch all members: ${err}\n${err.stack}`));
|
||||
}
|
||||
client.emit(Events.GUILD_CREATE, guild);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
'use strict';
|
||||
|
||||
const { Events } = require('../../../util/Constants');
|
||||
const Collection = require('../../../util/Collection');
|
||||
const { Events } = require('../../../util/Constants');
|
||||
|
||||
module.exports = (client, { d: data }) => {
|
||||
const guild = client.guilds.cache.get(data.guild_id);
|
||||
|
||||
@@ -8,11 +8,11 @@ module.exports = (client, { d: data }, shard) => {
|
||||
guild.memberCount++;
|
||||
const member = guild.members.add(data);
|
||||
if (shard.status === Status.READY) {
|
||||
/**
|
||||
* Emitted whenever a user joins a guild.
|
||||
* @event Client#guildMemberAdd
|
||||
* @param {GuildMember} member The member that has joined a guild
|
||||
*/
|
||||
/**
|
||||
* Emitted whenever a user joins a guild.
|
||||
* @event Client#guildMemberAdd
|
||||
* @param {GuildMember} member The member that has joined a guild
|
||||
*/
|
||||
client.emit(Events.GUILD_MEMBER_ADD, member);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,12 +7,12 @@ module.exports = (client, { d: data }) => {
|
||||
const user = client.users.cache.get(data.user_id);
|
||||
|
||||
if (channel && user) {
|
||||
/**
|
||||
* Emitted whenever a user starts typing in a channel.
|
||||
* @event Client#typingStart
|
||||
* @param {Channel} channel The channel the user started typing in
|
||||
* @param {User} user The user that started typing
|
||||
*/
|
||||
/**
|
||||
* Emitted whenever a user starts typing in a channel.
|
||||
* @event Client#typingStart
|
||||
* @param {Channel} channel The channel the user started typing in
|
||||
* @param {User} user The user that started typing
|
||||
*/
|
||||
client.emit(Events.TYPING_START, channel, user);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -36,7 +36,7 @@ const Messages = {
|
||||
USER_NO_DMCHANNEL: 'No DM Channel exists!',
|
||||
|
||||
VOICE_INVALID_HEARTBEAT: 'Tried to set voice heartbeat but no valid interval was specified.',
|
||||
VOICE_USER_MISSING: 'Couldn\'t resolve the user to create stream.',
|
||||
VOICE_USER_MISSING: "Couldn't resolve the user to create stream.",
|
||||
VOICE_JOIN_CHANNEL: (full = false) =>
|
||||
`You do not have permission to join this voice channel${full ? '; it is full.' : '.'}`,
|
||||
VOICE_CONNECTION_TIMEOUT: 'Connection not established within 15 seconds.',
|
||||
@@ -71,7 +71,7 @@ const Messages = {
|
||||
SPLIT_MAX_LEN: 'Chunk exceeds the max length and contains no split characters.',
|
||||
|
||||
BAN_RESOLVE_ID: (ban = false) => `Couldn't resolve the user ID to ${ban ? 'ban' : 'unban'}.`,
|
||||
FETCH_BAN_RESOLVE_ID: 'Couldn\'t resolve the user ID to fetch the ban.',
|
||||
FETCH_BAN_RESOLVE_ID: "Couldn't resolve the user ID to fetch the ban.",
|
||||
|
||||
PRUNE_DAYS_TYPE: 'Days must be a number',
|
||||
|
||||
@@ -79,7 +79,7 @@ const Messages = {
|
||||
GUILD_VOICE_CHANNEL_RESOLVE: 'Could not resolve channel to a guild voice channel.',
|
||||
GUILD_CHANNEL_ORPHAN: 'Could not find a parent to this guild channel.',
|
||||
GUILD_OWNED: 'Guild is owned by the client.',
|
||||
GUILD_MEMBERS_TIMEOUT: 'Members didn\'t arrive in time.',
|
||||
GUILD_MEMBERS_TIMEOUT: "Members didn't arrive in time.",
|
||||
GUILD_UNCACHED_ME: 'The client user as a member of this guild is uncached.',
|
||||
|
||||
INVALID_TYPE: (name, expected, an = false) => `Supplied ${name} is not a${an ? 'n' : ''} ${expected}.`,
|
||||
@@ -88,15 +88,15 @@ const Messages = {
|
||||
|
||||
EMOJI_TYPE: 'Emoji must be a string or GuildEmoji/ReactionEmoji',
|
||||
EMOJI_MANAGED: 'Emoji is managed and has no Author.',
|
||||
MISSING_MANAGE_EMOJIS_PERMISSION:
|
||||
guild => `Client must have Manage Emoji permission in guild ${guild} to see emoji authors.`,
|
||||
MISSING_MANAGE_EMOJIS_PERMISSION: guild =>
|
||||
`Client must have Manage Emoji permission in guild ${guild} to see emoji authors.`,
|
||||
|
||||
REACTION_RESOLVE_USER: 'Couldn\'t resolve the user ID to remove from the reaction.',
|
||||
REACTION_RESOLVE_USER: "Couldn't resolve the user ID to remove from the reaction.",
|
||||
|
||||
VANITY_URL: 'This guild does not have the VANITY_URL feature enabled.',
|
||||
|
||||
DELETE_GROUP_DM_CHANNEL: 'Bots don\'t have access to Group DM Channels and cannot delete them',
|
||||
FETCH_GROUP_DM_CHANNEL: 'Bots don\'t have access to Group DM Channels and cannot fetch them',
|
||||
DELETE_GROUP_DM_CHANNEL: "Bots don't have access to Group DM Channels and cannot delete them",
|
||||
FETCH_GROUP_DM_CHANNEL: "Bots don't have access to Group DM Channels and cannot fetch them",
|
||||
};
|
||||
|
||||
for (const [name, message] of Object.entries(Messages)) register(name, message);
|
||||
|
||||
@@ -34,9 +34,9 @@ class BaseManager {
|
||||
this.cacheType = cacheType;
|
||||
|
||||
/**
|
||||
* Holds the cache for the data model
|
||||
* @type {Collection}
|
||||
*/
|
||||
* Holds the cache for the data model
|
||||
* @type {Collection}
|
||||
*/
|
||||
this.cache = new cacheType(...cacheOptions);
|
||||
if (iterable) for (const i of iterable) this.add(i);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
'use strict';
|
||||
|
||||
const Channel = require('../structures/Channel');
|
||||
const BaseManager = require('./BaseManager');
|
||||
const Channel = require('../structures/Channel');
|
||||
const { Events } = require('../util/Constants');
|
||||
|
||||
/**
|
||||
@@ -14,10 +14,10 @@ class ChannelManager extends BaseManager {
|
||||
}
|
||||
|
||||
/**
|
||||
* The cache of Channels
|
||||
* @type {Collection<Snowflake, Channel>}
|
||||
* @name ChannelManager#cache
|
||||
*/
|
||||
* The cache of Channels
|
||||
* @type {Collection<Snowflake, Channel>}
|
||||
* @name ChannelManager#cache
|
||||
*/
|
||||
|
||||
add(data, guild, cache = true) {
|
||||
const existing = this.cache.get(data.id);
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
'use strict';
|
||||
|
||||
const { ChannelTypes } = require('../util/Constants');
|
||||
const BaseManager = require('./BaseManager');
|
||||
const GuildChannel = require('../structures/GuildChannel');
|
||||
const PermissionOverwrites = require('../structures/PermissionOverwrites');
|
||||
const { ChannelTypes } = require('../util/Constants');
|
||||
|
||||
/**
|
||||
* Manages API methods for GuildChannels and stores their cache.
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
'use strict';
|
||||
|
||||
const Collection = require('../util/Collection');
|
||||
const BaseManager = require('./BaseManager');
|
||||
const { TypeError } = require('../errors');
|
||||
const GuildEmoji = require('../structures/GuildEmoji');
|
||||
const ReactionEmoji = require('../structures/ReactionEmoji');
|
||||
const Collection = require('../util/Collection');
|
||||
const DataResolver = require('../util/DataResolver');
|
||||
const { TypeError } = require('../errors');
|
||||
|
||||
/**
|
||||
* Manages API methods for GuildEmojis and stores their cache.
|
||||
@@ -22,10 +22,10 @@ class GuildEmojiManager extends BaseManager {
|
||||
}
|
||||
|
||||
/**
|
||||
* The cache of GuildEmojis
|
||||
* @type {Collection<Snowflake, GuildEmoji>}
|
||||
* @name GuildEmojiManager#cache
|
||||
*/
|
||||
* The cache of GuildEmojis
|
||||
* @type {Collection<Snowflake, GuildEmoji>}
|
||||
* @name GuildEmojiManager#cache
|
||||
*/
|
||||
|
||||
add(data, cache) {
|
||||
return super.add(data, cache, { extras: [this.guild] });
|
||||
@@ -58,14 +58,17 @@ class GuildEmojiManager extends BaseManager {
|
||||
for (let role of roles instanceof Collection ? roles.values() : roles) {
|
||||
role = this.guild.roles.resolve(role);
|
||||
if (!role) {
|
||||
return Promise.reject(new TypeError('INVALID_TYPE', 'options.roles',
|
||||
'Array or Collection of Roles or Snowflakes', true));
|
||||
return Promise.reject(
|
||||
new TypeError('INVALID_TYPE', 'options.roles', 'Array or Collection of Roles or Snowflakes', true),
|
||||
);
|
||||
}
|
||||
data.roles.push(role.id);
|
||||
}
|
||||
}
|
||||
|
||||
return this.client.api.guilds(this.guild.id).emojis.post({ data, reason })
|
||||
return this.client.api
|
||||
.guilds(this.guild.id)
|
||||
.emojis.post({ data, reason })
|
||||
.then(emoji => this.client.actions.GuildEmojiCreate.handle(this.guild, emoji).emoji);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
'use strict';
|
||||
|
||||
const Collection = require('../util/Collection');
|
||||
const { TypeError } = require('../errors');
|
||||
const Collection = require('../util/Collection');
|
||||
|
||||
/**
|
||||
* Manages API methods for roles belonging to emojis and stores their cache.
|
||||
@@ -56,8 +56,7 @@ class GuildEmojiRoleManager {
|
||||
roleOrRoles = roleOrRoles.map(r => this.guild.roles.resolve(r));
|
||||
|
||||
if (roleOrRoles.includes(null)) {
|
||||
return Promise.reject(new TypeError('INVALID_TYPE', 'roles',
|
||||
'Array or Collection of Roles or Snowflakes', true));
|
||||
return Promise.reject(new TypeError('INVALID_TYPE', 'roles', 'Array or Collection of Roles or Snowflakes', true));
|
||||
}
|
||||
|
||||
const newRoles = [...new Set(roleOrRoles.concat(...this._roles.values()))];
|
||||
@@ -75,8 +74,7 @@ class GuildEmojiRoleManager {
|
||||
roleOrRoles = roleOrRoles.map(r => this.guild.roles.resolveID(r));
|
||||
|
||||
if (roleOrRoles.includes(null)) {
|
||||
return Promise.reject(new TypeError('INVALID_TYPE', 'roles',
|
||||
'Array or Collection of Roles or Snowflakes', true));
|
||||
return Promise.reject(new TypeError('INVALID_TYPE', 'roles', 'Array or Collection of Roles or Snowflakes', true));
|
||||
}
|
||||
|
||||
const newRoles = this._roles.keyArray().filter(role => !roleOrRoles.includes(role));
|
||||
|
||||
@@ -1,21 +1,21 @@
|
||||
'use strict';
|
||||
|
||||
const BaseManager = require('./BaseManager');
|
||||
const DataResolver = require('../util/DataResolver');
|
||||
const Guild = require('../structures/Guild');
|
||||
const GuildChannel = require('../structures/GuildChannel');
|
||||
const GuildEmoji = require('../structures/GuildEmoji');
|
||||
const GuildMember = require('../structures/GuildMember');
|
||||
const Invite = require('../structures/Invite');
|
||||
const Role = require('../structures/Role');
|
||||
const {
|
||||
Events,
|
||||
VerificationLevels,
|
||||
DefaultMessageNotifications,
|
||||
ExplicitContentFilterLevels,
|
||||
} = require('../util/Constants');
|
||||
const Guild = require('../structures/Guild');
|
||||
const GuildChannel = require('../structures/GuildChannel');
|
||||
const GuildMember = require('../structures/GuildMember');
|
||||
const DataResolver = require('../util/DataResolver');
|
||||
const Permissions = require('../util/Permissions');
|
||||
const { resolveColor } = require('../util/Util');
|
||||
const GuildEmoji = require('../structures/GuildEmoji');
|
||||
const Invite = require('../structures/Invite');
|
||||
const Role = require('../structures/Role');
|
||||
|
||||
/**
|
||||
* Manages API methods for Guilds and stores their cache.
|
||||
@@ -91,11 +91,15 @@ class GuildManager extends BaseManager {
|
||||
* @returns {?Guild}
|
||||
*/
|
||||
resolve(guild) {
|
||||
if (guild instanceof GuildChannel ||
|
||||
if (
|
||||
guild instanceof GuildChannel ||
|
||||
guild instanceof GuildMember ||
|
||||
guild instanceof GuildEmoji ||
|
||||
guild instanceof Role ||
|
||||
(guild instanceof Invite && guild.guild)) return super.resolve(guild.guild);
|
||||
(guild instanceof Invite && guild.guild)
|
||||
) {
|
||||
return super.resolve(guild.guild);
|
||||
}
|
||||
return super.resolve(guild);
|
||||
}
|
||||
|
||||
@@ -108,11 +112,15 @@ class GuildManager extends BaseManager {
|
||||
* @returns {?Snowflake}
|
||||
*/
|
||||
resolveID(guild) {
|
||||
if (guild instanceof GuildChannel ||
|
||||
if (
|
||||
guild instanceof GuildChannel ||
|
||||
guild instanceof GuildMember ||
|
||||
guild instanceof GuildEmoji ||
|
||||
guild instanceof Role ||
|
||||
(guild instanceof Invite && guild.guild)) return super.resolveID(guild.guild.id);
|
||||
(guild instanceof Invite && guild.guild)
|
||||
) {
|
||||
return super.resolveID(guild.guild.id);
|
||||
}
|
||||
return super.resolveID(guild);
|
||||
}
|
||||
|
||||
@@ -132,15 +140,18 @@ class GuildManager extends BaseManager {
|
||||
* @param {VerificationLevel} [options.verificationLevel] The verification level for the guild
|
||||
* @returns {Promise<Guild>} The guild that was created
|
||||
*/
|
||||
async create(name, {
|
||||
channels = [],
|
||||
defaultMessageNotifications,
|
||||
explicitContentFilter,
|
||||
icon = null,
|
||||
region,
|
||||
roles = [],
|
||||
verificationLevel,
|
||||
} = {}) {
|
||||
async create(
|
||||
name,
|
||||
{
|
||||
channels = [],
|
||||
defaultMessageNotifications,
|
||||
explicitContentFilter,
|
||||
icon = null,
|
||||
region,
|
||||
roles = [],
|
||||
verificationLevel,
|
||||
} = {},
|
||||
) {
|
||||
icon = await DataResolver.resolveImage(icon);
|
||||
if (typeof verificationLevel !== 'undefined' && typeof verificationLevel !== 'number') {
|
||||
verificationLevel = VerificationLevels.indexOf(verificationLevel);
|
||||
@@ -167,16 +178,19 @@ class GuildManager extends BaseManager {
|
||||
if (role.permissions) role.permissions = Permissions.resolve(role.permissions);
|
||||
}
|
||||
return new Promise((resolve, reject) =>
|
||||
this.client.api.guilds.post({ data: {
|
||||
name,
|
||||
region,
|
||||
icon,
|
||||
verification_level: verificationLevel,
|
||||
default_message_notifications: defaultMessageNotifications,
|
||||
explicit_content_filter: explicitContentFilter,
|
||||
channels,
|
||||
roles,
|
||||
} })
|
||||
this.client.api.guilds
|
||||
.post({
|
||||
data: {
|
||||
name,
|
||||
region,
|
||||
icon,
|
||||
verification_level: verificationLevel,
|
||||
default_message_notifications: defaultMessageNotifications,
|
||||
explicit_content_filter: explicitContentFilter,
|
||||
channels,
|
||||
roles,
|
||||
},
|
||||
})
|
||||
.then(data => {
|
||||
if (this.client.guilds.cache.has(data.id)) return resolve(this.client.guilds.cache.get(data.id));
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
'use strict';
|
||||
|
||||
const BaseManager = require('./BaseManager');
|
||||
const GuildMember = require('../structures/GuildMember');
|
||||
const { Events, OPCodes } = require('../util/Constants');
|
||||
const Collection = require('../util/Collection');
|
||||
const { Error, TypeError } = require('../errors');
|
||||
const GuildMember = require('../structures/GuildMember');
|
||||
const Collection = require('../util/Collection');
|
||||
const { Events, OPCodes } = require('../util/Constants');
|
||||
|
||||
/**
|
||||
* Manages API methods for GuildMembers and stores their cache.
|
||||
@@ -148,10 +148,15 @@ class GuildMemberManager extends BaseManager {
|
||||
*/
|
||||
prune({ days = 7, dry = false, count = true, reason } = {}) {
|
||||
if (typeof days !== 'number') throw new TypeError('PRUNE_DAYS_TYPE');
|
||||
return this.client.api.guilds(this.guild.id).prune[dry ? 'get' : 'post']({ query: {
|
||||
days,
|
||||
compute_prune_count: count,
|
||||
}, reason })
|
||||
return this.client.api
|
||||
.guilds(this.guild.id)
|
||||
.prune[dry ? 'get' : 'post']({
|
||||
query: {
|
||||
days,
|
||||
compute_prune_count: count,
|
||||
},
|
||||
reason,
|
||||
})
|
||||
.then(data => data.pruned);
|
||||
}
|
||||
|
||||
@@ -174,7 +179,9 @@ class GuildMemberManager extends BaseManager {
|
||||
if (options.days) options['delete-message-days'] = options.days;
|
||||
const id = this.client.users.resolveID(user);
|
||||
if (!id) return Promise.reject(new Error('BAN_RESOLVE_ID', true));
|
||||
return this.client.api.guilds(this.guild.id).bans[id].put({ query: options })
|
||||
return this.client.api
|
||||
.guilds(this.guild.id)
|
||||
.bans[id].put({ query: options })
|
||||
.then(() => {
|
||||
if (user instanceof GuildMember) return user;
|
||||
const _user = this.client.users.resolve(id);
|
||||
@@ -200,21 +207,25 @@ class GuildMemberManager extends BaseManager {
|
||||
unban(user, reason) {
|
||||
const id = this.client.users.resolveID(user);
|
||||
if (!id) return Promise.reject(new Error('BAN_RESOLVE_ID'));
|
||||
return this.client.api.guilds(this.guild.id).bans[id].delete({ reason })
|
||||
return this.client.api
|
||||
.guilds(this.guild.id)
|
||||
.bans[id].delete({ reason })
|
||||
.then(() => this.client.users.resolve(user));
|
||||
}
|
||||
|
||||
|
||||
_fetchSingle({ user, cache }) {
|
||||
const existing = this.cache.get(user);
|
||||
if (existing && !existing.partial) return Promise.resolve(existing);
|
||||
return this.client.api.guilds(this.guild.id).members(user).get()
|
||||
return this.client.api
|
||||
.guilds(this.guild.id)
|
||||
.members(user)
|
||||
.get()
|
||||
.then(data => this.add(data, cache));
|
||||
}
|
||||
|
||||
_fetchMany({ limit = 0, withPresences: presences = false, user: user_ids, query } = {}) {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (this.guild.memberCount === this.cache.size && (!query && !limit && !presences && !user_ids)) {
|
||||
if (this.guild.memberCount === this.cache.size && !query && !limit && !presences && !user_ids) {
|
||||
resolve(this.cache);
|
||||
return;
|
||||
}
|
||||
@@ -237,9 +248,11 @@ class GuildMemberManager extends BaseManager {
|
||||
for (const member of members.values()) {
|
||||
if (option) fetchedMembers.set(member.id, member);
|
||||
}
|
||||
if (this.guild.memberCount <= this.cache.size ||
|
||||
if (
|
||||
this.guild.memberCount <= this.cache.size ||
|
||||
(option && members.size < 1000) ||
|
||||
(limit && fetchedMembers.size >= limit)) {
|
||||
(limit && fetchedMembers.size >= limit)
|
||||
) {
|
||||
this.guild.client.removeListener(Events.GUILD_MEMBERS_CHUNK, handler);
|
||||
let fetched = option ? fetchedMembers : this.cache;
|
||||
if (user_ids && !Array.isArray(user_ids) && fetched.size) fetched = fetched.first();
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
'use strict';
|
||||
|
||||
const Collection = require('../util/Collection');
|
||||
const { TypeError } = require('../errors');
|
||||
const Collection = require('../util/Collection');
|
||||
|
||||
/**
|
||||
* Manages API methods for roles of a GuildMember and stores their cache.
|
||||
@@ -49,7 +49,7 @@ class GuildMemberRoleManager {
|
||||
get hoist() {
|
||||
const hoistedRoles = this._roles.filter(role => role.hoist);
|
||||
if (!hoistedRoles.size) return null;
|
||||
return hoistedRoles.reduce((prev, role) => !prev || role.comparePositionTo(prev) > 0 ? role : prev);
|
||||
return hoistedRoles.reduce((prev, role) => (!prev || role.comparePositionTo(prev) > 0 ? role : prev));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -60,7 +60,7 @@ class GuildMemberRoleManager {
|
||||
get color() {
|
||||
const coloredRoles = this._roles.filter(role => role.color);
|
||||
if (!coloredRoles.size) return null;
|
||||
return coloredRoles.reduce((prev, role) => !prev || role.comparePositionTo(prev) > 0 ? role : prev);
|
||||
return coloredRoles.reduce((prev, role) => (!prev || role.comparePositionTo(prev) > 0 ? role : prev));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -69,7 +69,7 @@ class GuildMemberRoleManager {
|
||||
* @readonly
|
||||
*/
|
||||
get highest() {
|
||||
return this._roles.reduce((prev, role) => role.comparePositionTo(prev) > 0 ? role : prev, this._roles.first());
|
||||
return this._roles.reduce((prev, role) => (role.comparePositionTo(prev) > 0 ? role : prev), this._roles.first());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -82,8 +82,7 @@ class GuildMemberRoleManager {
|
||||
if (roleOrRoles instanceof Collection || Array.isArray(roleOrRoles)) {
|
||||
roleOrRoles = roleOrRoles.map(r => this.guild.roles.resolve(r));
|
||||
if (roleOrRoles.includes(null)) {
|
||||
throw new TypeError('INVALID_TYPE', 'roles',
|
||||
'Array or Collection of Roles or Snowflakes', true);
|
||||
throw new TypeError('INVALID_TYPE', 'roles', 'Array or Collection of Roles or Snowflakes', true);
|
||||
}
|
||||
|
||||
const newRoles = [...new Set(roleOrRoles.concat(...this._roles.values()))];
|
||||
@@ -91,8 +90,7 @@ class GuildMemberRoleManager {
|
||||
} else {
|
||||
roleOrRoles = this.guild.roles.resolve(roleOrRoles);
|
||||
if (roleOrRoles === null) {
|
||||
throw new TypeError('INVALID_TYPE', 'roles',
|
||||
'Array or Collection of Roles or Snowflakes', true);
|
||||
throw new TypeError('INVALID_TYPE', 'roles', 'Array or Collection of Roles or Snowflakes', true);
|
||||
}
|
||||
|
||||
await this.client.api.guilds[this.guild.id].members[this.member.id].roles[roleOrRoles.id].put({ reason });
|
||||
@@ -113,8 +111,7 @@ class GuildMemberRoleManager {
|
||||
if (roleOrRoles instanceof Collection || Array.isArray(roleOrRoles)) {
|
||||
roleOrRoles = roleOrRoles.map(r => this.guild.roles.resolve(r));
|
||||
if (roleOrRoles.includes(null)) {
|
||||
throw new TypeError('INVALID_TYPE', 'roles',
|
||||
'Array or Collection of Roles or Snowflakes', true);
|
||||
throw new TypeError('INVALID_TYPE', 'roles', 'Array or Collection of Roles or Snowflakes', true);
|
||||
}
|
||||
|
||||
const newRoles = this._roles.filter(role => !roleOrRoles.includes(role));
|
||||
@@ -122,8 +119,7 @@ class GuildMemberRoleManager {
|
||||
} else {
|
||||
roleOrRoles = this.guild.roles.resolve(roleOrRoles);
|
||||
if (roleOrRoles === null) {
|
||||
throw new TypeError('INVALID_TYPE', 'roles',
|
||||
'Array or Collection of Roles or Snowflakes', true);
|
||||
throw new TypeError('INVALID_TYPE', 'roles', 'Array or Collection of Roles or Snowflakes', true);
|
||||
}
|
||||
|
||||
await this.client.api.guilds[this.guild.id].members[this.member.id].roles[roleOrRoles.id].delete({ reason });
|
||||
|
||||
@@ -2,82 +2,82 @@
|
||||
|
||||
const BaseManager = require('./BaseManager');
|
||||
const Message = require('../structures/Message');
|
||||
const LimitedCollection = require('../util/LimitedCollection');
|
||||
const Collection = require('../util/Collection');
|
||||
const LimitedCollection = require('../util/LimitedCollection');
|
||||
|
||||
/**
|
||||
* Manages API methods for Messages and holds their cache.
|
||||
* @extends {BaseManager}
|
||||
*/
|
||||
* Manages API methods for Messages and holds their cache.
|
||||
* @extends {BaseManager}
|
||||
*/
|
||||
class MessageManager extends BaseManager {
|
||||
constructor(channel, iterable) {
|
||||
super(channel.client, iterable, Message, LimitedCollection, channel.client.options.messageCacheMaxSize);
|
||||
/**
|
||||
* The channel that the messages belong to
|
||||
* @type {TextBasedChannel}
|
||||
*/
|
||||
* The channel that the messages belong to
|
||||
* @type {TextBasedChannel}
|
||||
*/
|
||||
this.channel = channel;
|
||||
}
|
||||
|
||||
/**
|
||||
* The cache of Messages
|
||||
* @type {LimitedCollection<Snowflake, Message>}
|
||||
* @name MessageManager#cache
|
||||
*/
|
||||
* The cache of Messages
|
||||
* @type {LimitedCollection<Snowflake, Message>}
|
||||
* @name MessageManager#cache
|
||||
*/
|
||||
|
||||
add(data, cache) {
|
||||
return super.add(data, cache, { extras: [this.channel] });
|
||||
}
|
||||
|
||||
/**
|
||||
* The parameters to pass in when requesting previous messages from a channel. `around`, `before` and
|
||||
* `after` are mutually exclusive. All the parameters are optional.
|
||||
* @typedef {Object} ChannelLogsQueryOptions
|
||||
* @property {number} [limit=50] Number of messages to acquire
|
||||
* @property {Snowflake} [before] ID of a message to get the messages that were posted before it
|
||||
* @property {Snowflake} [after] ID of a message to get the messages that were posted after it
|
||||
* @property {Snowflake} [around] ID of a message to get the messages that were posted around it
|
||||
*/
|
||||
* The parameters to pass in when requesting previous messages from a channel. `around`, `before` and
|
||||
* `after` are mutually exclusive. All the parameters are optional.
|
||||
* @typedef {Object} ChannelLogsQueryOptions
|
||||
* @property {number} [limit=50] Number of messages to acquire
|
||||
* @property {Snowflake} [before] ID of a message to get the messages that were posted before it
|
||||
* @property {Snowflake} [after] ID of a message to get the messages that were posted after it
|
||||
* @property {Snowflake} [around] ID of a message to get the messages that were posted around it
|
||||
*/
|
||||
|
||||
/**
|
||||
* Gets a message, or messages, from this channel.
|
||||
* <info>The returned Collection does not contain reaction users of the messages if they were not cached.
|
||||
* Those need to be fetched separately in such a case.</info>
|
||||
* @param {Snowflake|ChannelLogsQueryOptions} [message] The ID of the message to fetch, or query parameters.
|
||||
* @param {boolean} [cache=true] Whether to cache the message(s)
|
||||
* @returns {Promise<Message>|Promise<Collection<Snowflake, Message>>}
|
||||
* @example
|
||||
* // Get message
|
||||
* channel.messages.fetch('99539446449315840')
|
||||
* .then(message => console.log(message.content))
|
||||
* .catch(console.error);
|
||||
* @example
|
||||
* // Get messages
|
||||
* channel.messages.fetch({ limit: 10 })
|
||||
* .then(messages => console.log(`Received ${messages.size} messages`))
|
||||
* .catch(console.error);
|
||||
* @example
|
||||
* // Get messages and filter by user ID
|
||||
* channel.messages.fetch()
|
||||
* .then(messages => console.log(`${messages.filter(m => m.author.id === '84484653687267328').size} messages`))
|
||||
* .catch(console.error);
|
||||
*/
|
||||
* Gets a message, or messages, from this channel.
|
||||
* <info>The returned Collection does not contain reaction users of the messages if they were not cached.
|
||||
* Those need to be fetched separately in such a case.</info>
|
||||
* @param {Snowflake|ChannelLogsQueryOptions} [message] The ID of the message to fetch, or query parameters.
|
||||
* @param {boolean} [cache=true] Whether to cache the message(s)
|
||||
* @returns {Promise<Message>|Promise<Collection<Snowflake, Message>>}
|
||||
* @example
|
||||
* // Get message
|
||||
* channel.messages.fetch('99539446449315840')
|
||||
* .then(message => console.log(message.content))
|
||||
* .catch(console.error);
|
||||
* @example
|
||||
* // Get messages
|
||||
* channel.messages.fetch({ limit: 10 })
|
||||
* .then(messages => console.log(`Received ${messages.size} messages`))
|
||||
* .catch(console.error);
|
||||
* @example
|
||||
* // Get messages and filter by user ID
|
||||
* channel.messages.fetch()
|
||||
* .then(messages => console.log(`${messages.filter(m => m.author.id === '84484653687267328').size} messages`))
|
||||
* .catch(console.error);
|
||||
*/
|
||||
fetch(message, cache = true) {
|
||||
return typeof message === 'string' ? this._fetchId(message, cache) : this._fetchMany(message, cache);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the pinned messages of this channel and returns a collection of them.
|
||||
* <info>The returned Collection does not contain any reaction data of the messages.
|
||||
* Those need to be fetched separately.</info>
|
||||
* @param {boolean} [cache=true] Whether to cache the message(s)
|
||||
* @returns {Promise<Collection<Snowflake, Message>>}
|
||||
* @example
|
||||
* // Get pinned messages
|
||||
* channel.fetchPinned()
|
||||
* .then(messages => console.log(`Received ${messages.size} messages`))
|
||||
* .catch(console.error);
|
||||
*/
|
||||
* Fetches the pinned messages of this channel and returns a collection of them.
|
||||
* <info>The returned Collection does not contain any reaction data of the messages.
|
||||
* Those need to be fetched separately.</info>
|
||||
* @param {boolean} [cache=true] Whether to cache the message(s)
|
||||
* @returns {Promise<Collection<Snowflake, Message>>}
|
||||
* @example
|
||||
* // Get pinned messages
|
||||
* channel.fetchPinned()
|
||||
* .then(messages => console.log(`Received ${messages.size} messages`))
|
||||
* .catch(console.error);
|
||||
*/
|
||||
fetchPinned(cache = true) {
|
||||
return this.client.api.channels[this.channel.id].pins.get().then(data => {
|
||||
const messages = new Collection();
|
||||
@@ -94,23 +94,22 @@ class MessageManager extends BaseManager {
|
||||
*/
|
||||
|
||||
/**
|
||||
* Resolves a MessageResolvable to a Message object.
|
||||
* @method resolve
|
||||
* @memberof MessageManager
|
||||
* @instance
|
||||
* @param {MessageResolvable} message The message resolvable to resolve
|
||||
* @returns {?Message}
|
||||
*/
|
||||
* Resolves a MessageResolvable to a Message object.
|
||||
* @method resolve
|
||||
* @memberof MessageManager
|
||||
* @instance
|
||||
* @param {MessageResolvable} message The message resolvable to resolve
|
||||
* @returns {?Message}
|
||||
*/
|
||||
|
||||
/**
|
||||
* Resolves a MessageResolvable to a Message ID string.
|
||||
* @method resolveID
|
||||
* @memberof MessageManager
|
||||
* @instance
|
||||
* @param {MessageResolvable} message The message resolvable to resolve
|
||||
* @returns {?Snowflake}
|
||||
*/
|
||||
|
||||
* Resolves a MessageResolvable to a Message ID string.
|
||||
* @method resolveID
|
||||
* @memberof MessageManager
|
||||
* @instance
|
||||
* @param {MessageResolvable} message The message resolvable to resolve
|
||||
* @returns {?Snowflake}
|
||||
*/
|
||||
|
||||
/**
|
||||
* Deletes a message, even if it's not cached.
|
||||
@@ -119,7 +118,12 @@ class MessageManager extends BaseManager {
|
||||
*/
|
||||
async delete(message, reason) {
|
||||
message = this.resolveID(message);
|
||||
if (message) await this.client.api.channels(this.channel.id).messages(message).delete({ reason });
|
||||
if (message) {
|
||||
await this.client.api
|
||||
.channels(this.channel.id)
|
||||
.messages(message)
|
||||
.delete({ reason });
|
||||
}
|
||||
}
|
||||
|
||||
async _fetchId(messageID, cache) {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
'use strict';
|
||||
|
||||
const { Presence } = require('../structures/Presence');
|
||||
const BaseManager = require('./BaseManager');
|
||||
const { Presence } = require('../structures/Presence');
|
||||
|
||||
/**
|
||||
* Manages API methods for Presences and holds their cache.
|
||||
@@ -13,10 +13,10 @@ class PresenceManager extends BaseManager {
|
||||
}
|
||||
|
||||
/**
|
||||
* The cache of Presences
|
||||
* @type {Collection<Snowflake, Presence>}
|
||||
* @name PresenceManager#cache
|
||||
*/
|
||||
* The cache of Presences
|
||||
* @type {Collection<Snowflake, Presence>}
|
||||
* @name PresenceManager#cache
|
||||
*/
|
||||
|
||||
add(data, cache) {
|
||||
const existing = this.cache.get(data.user.id);
|
||||
@@ -32,10 +32,10 @@ class PresenceManager extends BaseManager {
|
||||
*/
|
||||
|
||||
/**
|
||||
* Resolves a PresenceResolvable to a Presence object.
|
||||
* @param {PresenceResolvable} presence The presence resolvable to resolve
|
||||
* @returns {?Presence}
|
||||
*/
|
||||
* Resolves a PresenceResolvable to a Presence object.
|
||||
* @param {PresenceResolvable} presence The presence resolvable to resolve
|
||||
* @returns {?Presence}
|
||||
*/
|
||||
resolve(presence) {
|
||||
const presenceResolvable = super.resolve(presence);
|
||||
if (presenceResolvable) return presenceResolvable;
|
||||
@@ -44,10 +44,10 @@ class PresenceManager extends BaseManager {
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves a PresenceResolvable to a Presence ID string.
|
||||
* @param {PresenceResolvable} presence The presence resolvable to resolve
|
||||
* @returns {?Snowflake}
|
||||
*/
|
||||
* Resolves a PresenceResolvable to a Presence ID string.
|
||||
* @param {PresenceResolvable} presence The presence resolvable to resolve
|
||||
* @returns {?Snowflake}
|
||||
*/
|
||||
resolveID(presence) {
|
||||
const presenceResolvable = super.resolveID(presence);
|
||||
if (presenceResolvable) return presenceResolvable;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
'use strict';
|
||||
|
||||
const MessageReaction = require('../structures/MessageReaction');
|
||||
const BaseManager = require('./BaseManager');
|
||||
const MessageReaction = require('../structures/MessageReaction');
|
||||
|
||||
/**
|
||||
* Manages API methods for reactions and holds their cache.
|
||||
@@ -36,29 +36,32 @@ class ReactionManager extends BaseManager {
|
||||
*/
|
||||
|
||||
/**
|
||||
* Resolves a MessageReactionResolvable to a MessageReaction object.
|
||||
* @method resolve
|
||||
* @memberof ReactionManager
|
||||
* @instance
|
||||
* @param {MessageReactionResolvable} reaction The MessageReaction to resolve
|
||||
* @returns {?MessageReaction}
|
||||
*/
|
||||
* Resolves a MessageReactionResolvable to a MessageReaction object.
|
||||
* @method resolve
|
||||
* @memberof ReactionManager
|
||||
* @instance
|
||||
* @param {MessageReactionResolvable} reaction The MessageReaction to resolve
|
||||
* @returns {?MessageReaction}
|
||||
*/
|
||||
|
||||
/**
|
||||
* Resolves a MessageReactionResolvable to a MessageReaction ID string.
|
||||
* @method resolveID
|
||||
* @memberof ReactionManager
|
||||
* @instance
|
||||
* @param {MessageReactionResolvable} reaction The MessageReaction to resolve
|
||||
* @returns {?Snowflake}
|
||||
*/
|
||||
* Resolves a MessageReactionResolvable to a MessageReaction ID string.
|
||||
* @method resolveID
|
||||
* @memberof ReactionManager
|
||||
* @instance
|
||||
* @param {MessageReactionResolvable} reaction The MessageReaction to resolve
|
||||
* @returns {?Snowflake}
|
||||
*/
|
||||
|
||||
/**
|
||||
* Removes all reactions from a message.
|
||||
* @returns {Promise<Message>}
|
||||
*/
|
||||
removeAll() {
|
||||
return this.client.api.channels(this.message.channel.id).messages(this.message.id).reactions.delete()
|
||||
return this.client.api
|
||||
.channels(this.message.channel.id)
|
||||
.messages(this.message.id)
|
||||
.reactions.delete()
|
||||
.then(() => this.message);
|
||||
}
|
||||
|
||||
@@ -72,7 +75,10 @@ class ReactionManager extends BaseManager {
|
||||
const id = reactionEmoji.id || reactionEmoji.name;
|
||||
const existing = this.cache.get(id);
|
||||
if (!this._partial(reactionEmoji)) return existing;
|
||||
const data = await this.client.api.channels(this.message.channel.id).messages(this.message.id).get();
|
||||
const data = await this.client.api
|
||||
.channels(this.message.channel.id)
|
||||
.messages(this.message.id)
|
||||
.get();
|
||||
if (this.message.partial) this.message._patch(data);
|
||||
if (!data.reactions || !data.reactions.some(r => (r.emoji.id || r.emoji.name) === id)) {
|
||||
reactionEmoji.reaction._patch({ count: 0 });
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
'use strict';
|
||||
|
||||
const Collection = require('../util/Collection');
|
||||
const BaseManager = require('./BaseManager');
|
||||
const { Error } = require('../errors');
|
||||
const Collection = require('../util/Collection');
|
||||
|
||||
/**
|
||||
* Manages API methods for users who reacted to a reaction and stores their cache.
|
||||
@@ -34,9 +34,9 @@ class ReactionUserManager extends BaseManager {
|
||||
*/
|
||||
async fetch({ limit = 100, after, before } = {}) {
|
||||
const message = this.reaction.message;
|
||||
const data = await this.client.api.channels[message.channel.id].messages[message.id]
|
||||
.reactions[this.reaction.emoji.identifier]
|
||||
.get({ query: { limit, before, after } });
|
||||
const data = await this.client.api.channels[message.channel.id].messages[message.id].reactions[
|
||||
this.reaction.emoji.identifier
|
||||
].get({ query: { limit, before, after } });
|
||||
const users = new Collection();
|
||||
for (const rawUser of data) {
|
||||
const user = this.client.users.add(rawUser);
|
||||
@@ -55,8 +55,9 @@ class ReactionUserManager extends BaseManager {
|
||||
const message = this.reaction.message;
|
||||
const userID = message.client.users.resolveID(user);
|
||||
if (!userID) return Promise.reject(new Error('REACTION_RESOLVE_USER'));
|
||||
return message.client.api.channels[message.channel.id].messages[message.id]
|
||||
.reactions[this.reaction.emoji.identifier][userID === message.client.user.id ? '@me' : userID]
|
||||
return message.client.api.channels[message.channel.id].messages[message.id].reactions[
|
||||
this.reaction.emoji.identifier
|
||||
][userID === message.client.user.id ? '@me' : userID]
|
||||
.delete()
|
||||
.then(() => this.reaction);
|
||||
}
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
|
||||
const BaseManager = require('./BaseManager');
|
||||
const Role = require('../structures/Role');
|
||||
const { resolveColor } = require('../util/Util');
|
||||
const Permissions = require('../util/Permissions');
|
||||
const { resolveColor } = require('../util/Util');
|
||||
|
||||
/**
|
||||
* Manages API methods for roles and stores their cache.
|
||||
@@ -110,14 +110,17 @@ class RoleManager extends BaseManager {
|
||||
if (data.color) data.color = resolveColor(data.color);
|
||||
if (data.permissions) data.permissions = Permissions.resolve(data.permissions);
|
||||
|
||||
return this.guild.client.api.guilds(this.guild.id).roles.post({ data, reason }).then(r => {
|
||||
const { role } = this.client.actions.GuildRoleCreate.handle({
|
||||
guild_id: this.guild.id,
|
||||
role: r,
|
||||
return this.guild.client.api
|
||||
.guilds(this.guild.id)
|
||||
.roles.post({ data, reason })
|
||||
.then(r => {
|
||||
const { role } = this.client.actions.GuildRoleCreate.handle({
|
||||
guild_id: this.guild.id,
|
||||
role: r,
|
||||
});
|
||||
if (data.position) return role.setPosition(data.position, reason);
|
||||
return role;
|
||||
});
|
||||
if (data.position) return role.setPosition(data.position, reason);
|
||||
return role;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -135,7 +138,7 @@ class RoleManager extends BaseManager {
|
||||
* @readonly
|
||||
*/
|
||||
get highest() {
|
||||
return this.cache.reduce((prev, role) => role.comparePositionTo(prev) > 0 ? role : prev, this.cache.first());
|
||||
return this.cache.reduce((prev, role) => (role.comparePositionTo(prev) > 0 ? role : prev), this.cache.first());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
'use strict';
|
||||
|
||||
const BaseManager = require('./BaseManager');
|
||||
const User = require('../structures/User');
|
||||
const GuildMember = require('../structures/GuildMember');
|
||||
const Message = require('../structures/Message');
|
||||
const User = require('../structures/User');
|
||||
|
||||
/**
|
||||
* Manages API methods for users and stores their cache.
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
'use strict';
|
||||
|
||||
const FormData = require('form-data');
|
||||
const https = require('https');
|
||||
const { browser, UserAgent } = require('../util/Constants');
|
||||
const fetch = require('node-fetch');
|
||||
const AbortController = require('abort-controller');
|
||||
const FormData = require('form-data');
|
||||
const fetch = require('node-fetch');
|
||||
const { browser, UserAgent } = require('../util/Constants');
|
||||
|
||||
if (https.Agent) var agent = new https.Agent({ keepAlive: true });
|
||||
|
||||
@@ -26,8 +26,10 @@ class APIRequest {
|
||||
}
|
||||
|
||||
make() {
|
||||
const API = this.options.versioned === false ? this.client.options.http.api :
|
||||
`${this.client.options.http.api}/v${this.client.options.http.version}`;
|
||||
const API =
|
||||
this.options.versioned === false
|
||||
? this.client.options.http.api
|
||||
: `${this.client.options.http.api}/v${this.client.options.http.version}`;
|
||||
const url = API + this.path;
|
||||
let headers = {};
|
||||
|
||||
@@ -42,7 +44,8 @@ class APIRequest {
|
||||
for (const file of this.options.files) if (file && file.file) body.append(file.name, file.file, file.name);
|
||||
if (typeof this.options.data !== 'undefined') body.append('payload_json', JSON.stringify(this.options.data));
|
||||
if (!browser) headers = Object.assign(headers, body.getHeaders());
|
||||
} else if (this.options.data != null) { // eslint-disable-line eqeqeq
|
||||
// eslint-disable-next-line eqeqeq
|
||||
} else if (this.options.data != null) {
|
||||
body = JSON.stringify(this.options.data);
|
||||
headers['Content-Type'] = 'application/json';
|
||||
}
|
||||
|
||||
@@ -3,8 +3,12 @@
|
||||
const noop = () => {}; // eslint-disable-line no-empty-function
|
||||
const methods = ['get', 'post', 'delete', 'patch', 'put'];
|
||||
const reflectors = [
|
||||
'toString', 'valueOf', 'inspect', 'constructor',
|
||||
Symbol.toPrimitive, Symbol.for('nodejs.util.inspect.custom'),
|
||||
'toString',
|
||||
'valueOf',
|
||||
'inspect',
|
||||
'constructor',
|
||||
Symbol.toPrimitive,
|
||||
Symbol.for('nodejs.util.inspect.custom'),
|
||||
];
|
||||
|
||||
function buildRoute(manager) {
|
||||
@@ -22,10 +26,18 @@ function buildRoute(manager) {
|
||||
// All other parts of the route should be considered as part of the bucket identifier
|
||||
else routeBucket.push(route[i]);
|
||||
}
|
||||
return options => manager.request(name, route.join('/'), Object.assign({
|
||||
versioned: manager.versioned,
|
||||
route: routeBucket.join('/'),
|
||||
}, options));
|
||||
return options =>
|
||||
manager.request(
|
||||
name,
|
||||
route.join('/'),
|
||||
Object.assign(
|
||||
{
|
||||
versioned: manager.versioned,
|
||||
route: routeBucket.join('/'),
|
||||
},
|
||||
options,
|
||||
),
|
||||
);
|
||||
}
|
||||
route.push(name);
|
||||
return new Proxy(noop, handler);
|
||||
|
||||
@@ -48,7 +48,7 @@ class DiscordAPIError extends Error {
|
||||
|
||||
for (const [k, v] of Object.entries(obj)) {
|
||||
if (k === 'message') continue;
|
||||
const newKey = key ? isNaN(k) ? `${key}.${k}` : `${key}[${k}]` : k;
|
||||
const newKey = key ? (isNaN(k) ? `${key}.${k}` : `${key}[${k}]`) : k;
|
||||
|
||||
if (v._errors) {
|
||||
messages.push(`${newKey}: ${v._errors.map(e => e.message).join(' ')}`);
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
'use strict';
|
||||
|
||||
const RequestHandler = require('./RequestHandler');
|
||||
const APIRequest = require('./APIRequest');
|
||||
const routeBuilder = require('./APIRouter');
|
||||
const RequestHandler = require('./RequestHandler');
|
||||
const { Error } = require('../errors');
|
||||
const { Endpoints } = require('../util/Constants');
|
||||
const Collection = require('../util/Collection');
|
||||
const { Endpoints } = require('../util/Constants');
|
||||
|
||||
class RESTManager {
|
||||
constructor(client, tokenPrefix = 'Bot') {
|
||||
@@ -37,12 +37,14 @@ class RESTManager {
|
||||
|
||||
push(handler, apiRequest) {
|
||||
return new Promise((resolve, reject) => {
|
||||
handler.push({
|
||||
request: apiRequest,
|
||||
resolve,
|
||||
reject,
|
||||
retries: 0,
|
||||
}).catch(reject);
|
||||
handler
|
||||
.push({
|
||||
request: apiRequest,
|
||||
resolve,
|
||||
reject,
|
||||
retries: 0,
|
||||
})
|
||||
.catch(reject);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -2,8 +2,11 @@
|
||||
|
||||
const DiscordAPIError = require('./DiscordAPIError');
|
||||
const HTTPError = require('./HTTPError');
|
||||
const {
|
||||
Events: { RATE_LIMIT },
|
||||
browser,
|
||||
} = require('../util/Constants');
|
||||
const Util = require('../util/Util');
|
||||
const { Events: { RATE_LIMIT }, browser } = require('../util/Constants');
|
||||
|
||||
function parseResponse(res) {
|
||||
if (res.headers.get('content-type').startsWith('application/json')) return res.json();
|
||||
@@ -52,7 +55,6 @@ class RequestHandler {
|
||||
return this.queue.length === 0 && !this.limited && this.busy !== true;
|
||||
}
|
||||
|
||||
/* eslint-disable-next-line complexity */
|
||||
async execute(item) {
|
||||
// Insert item back to the beginning if currently busy
|
||||
if (this.busy) {
|
||||
@@ -102,9 +104,7 @@ class RequestHandler {
|
||||
} catch (error) {
|
||||
// NodeFetch error expected for all "operational" errors, such as 500 status code
|
||||
this.busy = false;
|
||||
return reject(
|
||||
new HTTPError(error.message, error.constructor.name, error.status, request.method, request.path),
|
||||
);
|
||||
return reject(new HTTPError(error.message, error.constructor.name, error.status, request.method, request.path));
|
||||
}
|
||||
|
||||
if (res && res.headers) {
|
||||
@@ -171,9 +171,7 @@ class RequestHandler {
|
||||
}
|
||||
return null;
|
||||
} catch (err) {
|
||||
return reject(
|
||||
new HTTPError(err.message, err.constructor.name, err.status, request.method, request.path),
|
||||
);
|
||||
return reject(new HTTPError(err.message, err.constructor.name, err.status, request.method, request.path));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
|
||||
const EventEmitter = require('events');
|
||||
const path = require('path');
|
||||
const Util = require('../util/Util');
|
||||
const { Error } = require('../errors');
|
||||
const Util = require('../util/Util');
|
||||
let childProcess = null;
|
||||
let Worker = null;
|
||||
|
||||
@@ -111,9 +111,11 @@ class Shard extends EventEmitter {
|
||||
if (this.worker) throw new Error('SHARDING_WORKER_EXISTS', this.id);
|
||||
|
||||
if (this.manager.mode === 'process') {
|
||||
this.process = childProcess.fork(path.resolve(this.manager.file), this.args, {
|
||||
env: this.env, execArgv: this.execArgv,
|
||||
})
|
||||
this.process = childProcess
|
||||
.fork(path.resolve(this.manager.file), this.args, {
|
||||
env: this.env,
|
||||
execArgv: this.execArgv,
|
||||
})
|
||||
.on('message', this._handleMessage.bind(this))
|
||||
.on('exit', this._exitListener);
|
||||
} else if (this.manager.mode === 'worker') {
|
||||
@@ -203,7 +205,8 @@ class Shard extends EventEmitter {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (this.process) {
|
||||
this.process.send(message, err => {
|
||||
if (err) reject(err); else resolve(this);
|
||||
if (err) reject(err);
|
||||
else resolve(this);
|
||||
});
|
||||
} else {
|
||||
this.worker.postMessage(message);
|
||||
@@ -261,7 +264,8 @@ class Shard extends EventEmitter {
|
||||
if (!message || message._eval !== script) return;
|
||||
child.removeListener('message', listener);
|
||||
this._evals.delete(script);
|
||||
if (!message._error) resolve(message._result); else reject(Util.makeError(message._error));
|
||||
if (!message._error) resolve(message._result);
|
||||
else reject(Util.makeError(message._error));
|
||||
};
|
||||
child.on('message', listener);
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
'use strict';
|
||||
|
||||
const Util = require('../util/Util');
|
||||
const { Events } = require('../util/Constants');
|
||||
const Util = require('../util/Util');
|
||||
|
||||
/**
|
||||
* Helper class for sharded clients spawned as a child process/worker, such as from a {@link ShardingManager}.
|
||||
@@ -33,15 +33,27 @@ class ShardClientUtil {
|
||||
|
||||
if (mode === 'process') {
|
||||
process.on('message', this._handleMessage.bind(this));
|
||||
client.on('ready', () => { process.send({ _ready: true }); });
|
||||
client.on('disconnect', () => { process.send({ _disconnect: true }); });
|
||||
client.on('reconnecting', () => { process.send({ _reconnecting: true }); });
|
||||
client.on('ready', () => {
|
||||
process.send({ _ready: true });
|
||||
});
|
||||
client.on('disconnect', () => {
|
||||
process.send({ _disconnect: true });
|
||||
});
|
||||
client.on('reconnecting', () => {
|
||||
process.send({ _reconnecting: true });
|
||||
});
|
||||
} else if (mode === 'worker') {
|
||||
this.parentPort = require('worker_threads').parentPort;
|
||||
this.parentPort.on('message', this._handleMessage.bind(this));
|
||||
client.on('ready', () => { this.parentPort.postMessage({ _ready: true }); });
|
||||
client.on('disconnect', () => { this.parentPort.postMessage({ _disconnect: true }); });
|
||||
client.on('reconnecting', () => { this.parentPort.postMessage({ _reconnecting: true }); });
|
||||
client.on('ready', () => {
|
||||
this.parentPort.postMessage({ _ready: true });
|
||||
});
|
||||
client.on('disconnect', () => {
|
||||
this.parentPort.postMessage({ _disconnect: true });
|
||||
});
|
||||
client.on('reconnecting', () => {
|
||||
this.parentPort.postMessage({ _reconnecting: true });
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,7 +84,8 @@ class ShardClientUtil {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (this.mode === 'process') {
|
||||
process.send(message, err => {
|
||||
if (err) reject(err); else resolve();
|
||||
if (err) reject(err);
|
||||
else resolve();
|
||||
});
|
||||
} else if (this.mode === 'worker') {
|
||||
this.parentPort.postMessage(message);
|
||||
@@ -98,7 +111,8 @@ class ShardClientUtil {
|
||||
const listener = message => {
|
||||
if (!message || message._sFetchProp !== prop) return;
|
||||
parent.removeListener('message', listener);
|
||||
if (!message._error) resolve(message._result); else reject(Util.makeError(message._error));
|
||||
if (!message._error) resolve(message._result);
|
||||
else reject(Util.makeError(message._error));
|
||||
};
|
||||
parent.on('message', listener);
|
||||
|
||||
@@ -127,7 +141,8 @@ class ShardClientUtil {
|
||||
const listener = message => {
|
||||
if (!message || message._sEval !== script) return;
|
||||
parent.removeListener('message', listener);
|
||||
if (!message._error) resolve(message._result); else reject(Util.makeError(message._error));
|
||||
if (!message._error) resolve(message._result);
|
||||
else reject(Util.makeError(message._error));
|
||||
};
|
||||
parent.on('message', listener);
|
||||
|
||||
@@ -201,8 +216,10 @@ class ShardClientUtil {
|
||||
if (!this._singleton) {
|
||||
this._singleton = new this(client, mode);
|
||||
} else {
|
||||
client.emit(Events.WARN,
|
||||
'Multiple clients created in child process/worker; only the first will handle sharding helpers.');
|
||||
client.emit(
|
||||
Events.WARN,
|
||||
'Multiple clients created in child process/worker; only the first will handle sharding helpers.',
|
||||
);
|
||||
}
|
||||
return this._singleton;
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
'use strict';
|
||||
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
const EventEmitter = require('events');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const Shard = require('./Shard');
|
||||
const { Error, TypeError, RangeError } = require('../errors');
|
||||
const Collection = require('../util/Collection');
|
||||
const Util = require('../util/Util');
|
||||
const { Error, TypeError, RangeError } = require('../errors');
|
||||
|
||||
/**
|
||||
* This is a utility class that makes multi-process sharding of a bot an easy and painless experience.
|
||||
@@ -41,14 +41,17 @@ class ShardingManager extends EventEmitter {
|
||||
*/
|
||||
constructor(file, options = {}) {
|
||||
super();
|
||||
options = Util.mergeDefault({
|
||||
totalShards: 'auto',
|
||||
mode: 'process',
|
||||
respawn: true,
|
||||
shardArgs: [],
|
||||
execArgv: [],
|
||||
token: process.env.DISCORD_TOKEN,
|
||||
}, options);
|
||||
options = Util.mergeDefault(
|
||||
{
|
||||
totalShards: 'auto',
|
||||
mode: 'process',
|
||||
respawn: true,
|
||||
shardArgs: [],
|
||||
execArgv: [],
|
||||
token: process.env.DISCORD_TOKEN,
|
||||
},
|
||||
options,
|
||||
);
|
||||
|
||||
/**
|
||||
* Path to the shard script file
|
||||
@@ -71,8 +74,11 @@ class ShardingManager extends EventEmitter {
|
||||
}
|
||||
this.shardList = [...new Set(this.shardList)];
|
||||
if (this.shardList.length < 1) throw new RangeError('CLIENT_INVALID_OPTION', 'shardList', 'at least 1 ID.');
|
||||
if (this.shardList.some(shardID => typeof shardID !== 'number' || isNaN(shardID) ||
|
||||
!Number.isInteger(shardID) || shardID < 0)) {
|
||||
if (
|
||||
this.shardList.some(
|
||||
shardID => typeof shardID !== 'number' || isNaN(shardID) || !Number.isInteger(shardID) || shardID < 0,
|
||||
)
|
||||
) {
|
||||
throw new TypeError('CLIENT_INVALID_OPTION', 'shardList', 'an array of positive integers.');
|
||||
}
|
||||
}
|
||||
@@ -186,8 +192,11 @@ class ShardingManager extends EventEmitter {
|
||||
}
|
||||
|
||||
if (this.shardList.some(shardID => shardID >= amount)) {
|
||||
throw new RangeError('CLIENT_INVALID_OPTION', 'Amount of shards',
|
||||
'bigger than the highest shardID in the shardList option.');
|
||||
throw new RangeError(
|
||||
'CLIENT_INVALID_OPTION',
|
||||
'Amount of shards',
|
||||
'bigger than the highest shardID in the shardList option.',
|
||||
);
|
||||
}
|
||||
|
||||
// Spawn the shards
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
'use strict';
|
||||
|
||||
const DataResolver = require('../util/DataResolver');
|
||||
const MessageEmbed = require('./MessageEmbed');
|
||||
const MessageAttachment = require('./MessageAttachment');
|
||||
const { browser } = require('../util/Constants');
|
||||
const Util = require('../util/Util');
|
||||
const MessageEmbed = require('./MessageEmbed');
|
||||
const { RangeError } = require('../errors');
|
||||
const { browser } = require('../util/Constants');
|
||||
const DataResolver = require('../util/DataResolver');
|
||||
const MessageFlags = require('../util/MessageFlags');
|
||||
const Util = require('../util/Util');
|
||||
|
||||
/**
|
||||
* Represents a message to be sent to the API.
|
||||
@@ -78,7 +78,7 @@ class APIMessage {
|
||||
* Makes the content of this message.
|
||||
* @returns {?(string|string[])}
|
||||
*/
|
||||
makeContent() { // eslint-disable-line complexity
|
||||
makeContent() {
|
||||
const GuildMember = require('./GuildMember');
|
||||
|
||||
let content;
|
||||
@@ -88,13 +88,14 @@ class APIMessage {
|
||||
content = Util.resolveString(this.options.content);
|
||||
}
|
||||
|
||||
const disableMentions = typeof this.options.disableMentions === 'undefined' ?
|
||||
this.target.client.options.disableMentions :
|
||||
this.options.disableMentions;
|
||||
const disableMentions =
|
||||
typeof this.options.disableMentions === 'undefined'
|
||||
? this.target.client.options.disableMentions
|
||||
: this.options.disableMentions;
|
||||
if (disableMentions === 'all') {
|
||||
content = Util.removeMentions(content || '');
|
||||
} else if (disableMentions === 'everyone') {
|
||||
content = (content || '').replace(/@([^<>@ ]*)/gsmu, (match, target) => {
|
||||
content = (content || '').replace(/@([^<>@ ]*)/gmsu, (match, target) => {
|
||||
if (target.match(/^[&!]?\d+$/)) {
|
||||
return `@${target}`;
|
||||
} else {
|
||||
@@ -270,7 +271,8 @@ class APIMessage {
|
||||
return 'file.jpg';
|
||||
};
|
||||
|
||||
const ownAttachment = typeof fileLike === 'string' ||
|
||||
const ownAttachment =
|
||||
typeof fileLike === 'string' ||
|
||||
fileLike instanceof (browser ? ArrayBuffer : Buffer) ||
|
||||
typeof fileLike.pipe === 'function';
|
||||
if (ownAttachment) {
|
||||
|
||||
@@ -20,7 +20,9 @@ class Base {
|
||||
return Object.assign(Object.create(this), this);
|
||||
}
|
||||
|
||||
_patch(data) { return data; }
|
||||
_patch(data) {
|
||||
return data;
|
||||
}
|
||||
|
||||
_update(data) {
|
||||
const clone = this._clone();
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
'use strict';
|
||||
|
||||
const Snowflake = require('../util/Snowflake');
|
||||
const Base = require('./Base');
|
||||
const { ChannelTypes } = require('../util/Constants');
|
||||
const Snowflake = require('../util/Snowflake');
|
||||
|
||||
/**
|
||||
* Represents any channel on Discord.
|
||||
@@ -82,7 +82,10 @@ class Channel extends Base {
|
||||
* .catch(console.error);
|
||||
*/
|
||||
delete() {
|
||||
return this.client.api.channels(this.id).delete().then(() => this);
|
||||
return this.client.api
|
||||
.channels(this.id)
|
||||
.delete()
|
||||
.then(() => this);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
'use strict';
|
||||
|
||||
const Snowflake = require('../util/Snowflake');
|
||||
const { ClientApplicationAssetTypes, Endpoints } = require('../util/Constants');
|
||||
const Base = require('./Base');
|
||||
const Team = require('./Team');
|
||||
const { ClientApplicationAssetTypes, Endpoints } = require('../util/Constants');
|
||||
const Snowflake = require('../util/Snowflake');
|
||||
|
||||
const AssetTypes = Object.keys(ClientApplicationAssetTypes);
|
||||
|
||||
@@ -70,11 +70,7 @@ class ClientApplication extends Base {
|
||||
* The owner of this OAuth application
|
||||
* @type {?User|Team}
|
||||
*/
|
||||
this.owner = data.team ?
|
||||
new Team(this.client, data.team) :
|
||||
data.owner ?
|
||||
this.client.users.add(data.owner) :
|
||||
null;
|
||||
this.owner = data.team ? new Team(this.client, data.team) : data.owner ? this.client.users.add(data.owner) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -112,9 +108,7 @@ class ClientApplication extends Base {
|
||||
*/
|
||||
coverImage({ format, size } = {}) {
|
||||
if (!this.cover) return null;
|
||||
return Endpoints
|
||||
.CDN(this.client.options.http.cdn)
|
||||
.AppIcon(this.id, this.cover, { format, size });
|
||||
return Endpoints.CDN(this.client.options.http.cdn).AppIcon(this.id, this.cover, { format, size });
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -130,12 +124,16 @@ class ClientApplication extends Base {
|
||||
* @returns {Promise<Array<ClientAsset>>}
|
||||
*/
|
||||
fetchAssets() {
|
||||
return this.client.api.oauth2.applications(this.id).assets.get()
|
||||
.then(assets => assets.map(a => ({
|
||||
id: a.id,
|
||||
name: a.name,
|
||||
type: AssetTypes[a.type - 1],
|
||||
})));
|
||||
return this.client.api.oauth2
|
||||
.applications(this.id)
|
||||
.assets.get()
|
||||
.then(assets =>
|
||||
assets.map(a => ({
|
||||
id: a.id,
|
||||
name: a.name,
|
||||
type: AssetTypes[a.type - 1],
|
||||
})),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
'use strict';
|
||||
|
||||
const { Presence } = require('./Presence');
|
||||
const { TypeError } = require('../errors');
|
||||
const Collection = require('../util/Collection');
|
||||
const { ActivityTypes, OPCodes } = require('../util/Constants');
|
||||
const { TypeError } = require('../errors');
|
||||
|
||||
class ClientPresence extends Presence {
|
||||
/**
|
||||
@@ -29,7 +29,7 @@ class ClientPresence extends Presence {
|
||||
return this;
|
||||
}
|
||||
|
||||
async _parse({ status, since, afk, activity }) { // eslint-disable-line complexity
|
||||
async _parse({ status, since, afk, activity }) {
|
||||
const applicationID = activity && (activity.application ? activity.application.id || activity.application : null);
|
||||
let assets = new Collection();
|
||||
if (activity) {
|
||||
@@ -47,24 +47,28 @@ class ClientPresence extends Presence {
|
||||
afk: afk != null ? afk : false, // eslint-disable-line eqeqeq
|
||||
since: since != null ? since : null, // eslint-disable-line eqeqeq
|
||||
status: status || this.status,
|
||||
game: activity ? {
|
||||
type: activity.type,
|
||||
name: activity.name,
|
||||
url: activity.url,
|
||||
details: activity.details || undefined,
|
||||
state: activity.state || undefined,
|
||||
assets: activity.assets ? {
|
||||
large_text: activity.assets.largeText || undefined,
|
||||
small_text: activity.assets.smallText || undefined,
|
||||
large_image: assets.get(activity.assets.largeImage) || activity.assets.largeImage,
|
||||
small_image: assets.get(activity.assets.smallImage) || activity.assets.smallImage,
|
||||
} : undefined,
|
||||
timestamps: activity.timestamps || undefined,
|
||||
party: activity.party || undefined,
|
||||
application_id: applicationID || undefined,
|
||||
secrets: activity.secrets || undefined,
|
||||
instance: activity.instance || undefined,
|
||||
} : null,
|
||||
game: activity
|
||||
? {
|
||||
type: activity.type,
|
||||
name: activity.name,
|
||||
url: activity.url,
|
||||
details: activity.details || undefined,
|
||||
state: activity.state || undefined,
|
||||
assets: activity.assets
|
||||
? {
|
||||
large_text: activity.assets.largeText || undefined,
|
||||
small_text: activity.assets.smallText || undefined,
|
||||
large_image: assets.get(activity.assets.largeImage) || activity.assets.largeImage,
|
||||
small_image: assets.get(activity.assets.smallImage) || activity.assets.smallImage,
|
||||
}
|
||||
: undefined,
|
||||
timestamps: activity.timestamps || undefined,
|
||||
party: activity.party || undefined,
|
||||
application_id: applicationID || undefined,
|
||||
secrets: activity.secrets || undefined,
|
||||
instance: activity.instance || undefined,
|
||||
}
|
||||
: null,
|
||||
};
|
||||
|
||||
if ((status || afk || since) && !activity) {
|
||||
@@ -72,8 +76,8 @@ class ClientPresence extends Presence {
|
||||
}
|
||||
|
||||
if (packet.game) {
|
||||
packet.game.type = typeof packet.game.type === 'number' ?
|
||||
packet.game.type : ActivityTypes.indexOf(packet.game.type);
|
||||
packet.game.type =
|
||||
typeof packet.game.type === 'number' ? packet.game.type : ActivityTypes.indexOf(packet.game.type);
|
||||
}
|
||||
|
||||
return packet;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
'use strict';
|
||||
|
||||
const Structures = require('../util/Structures');
|
||||
const DataResolver = require('../util/DataResolver');
|
||||
const Structures = require('../util/Structures');
|
||||
|
||||
/**
|
||||
* Represents the logged in client's Discord user.
|
||||
@@ -47,7 +47,9 @@ class ClientUser extends Structures.get('User') {
|
||||
}
|
||||
|
||||
edit(data) {
|
||||
return this.client.api.users('@me').patch({ data })
|
||||
return this.client.api
|
||||
.users('@me')
|
||||
.patch({ data })
|
||||
.then(newData => {
|
||||
this.client.token = newData.token;
|
||||
const { updated } = this.client.actions.UserUpdate.handle(newData);
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
'use strict';
|
||||
|
||||
const Snowflake = require('../util/Snowflake');
|
||||
const Base = require('./Base');
|
||||
|
||||
const Snowflake = require('../util/Snowflake');
|
||||
|
||||
/**
|
||||
* Represents an emoji, see {@link GuildEmoji} and {@link ReactionEmoji}.
|
||||
|
||||
@@ -1,10 +1,18 @@
|
||||
'use strict';
|
||||
|
||||
const Invite = require('./Invite');
|
||||
const Integration = require('./Integration');
|
||||
const Base = require('./Base');
|
||||
const GuildAuditLogs = require('./GuildAuditLogs');
|
||||
const Webhook = require('./Webhook');
|
||||
const Integration = require('./Integration');
|
||||
const Invite = require('./Invite');
|
||||
const VoiceRegion = require('./VoiceRegion');
|
||||
const Webhook = require('./Webhook');
|
||||
const GuildChannelManager = require('../src/managers/GuildChannelManager');
|
||||
const GuildEmojiManager = require('../src/managers/GuildEmojiManager');
|
||||
const GuildMemberManager = require('../src/managers/GuildMemberManager');
|
||||
const PresenceManager = require('../src/managers/PresenceManager');
|
||||
const RoleManager = require('../src/managers/RoleManager');
|
||||
const VoiceStateManager = require('../src/managers/VoiceStateManager');
|
||||
const Collection = require('../util/Collection');
|
||||
const {
|
||||
ChannelTypes,
|
||||
DefaultMessageNotifications,
|
||||
@@ -12,19 +20,10 @@ const {
|
||||
VerificationLevels,
|
||||
ExplicitContentFilterLevels,
|
||||
} = require('../util/Constants');
|
||||
const Collection = require('../util/Collection');
|
||||
const Util = require('../util/Util');
|
||||
const DataResolver = require('../util/DataResolver');
|
||||
const Snowflake = require('../util/Snowflake');
|
||||
const SystemChannelFlags = require('../util/SystemChannelFlags');
|
||||
const GuildMemberManager = require('../managers/GuildMemberManager');
|
||||
const RoleManager = require('../managers/RoleManager');
|
||||
const GuildEmojiManager = require('../managers/GuildEmojiManager');
|
||||
const GuildChannelManager = require('../managers/GuildChannelManager');
|
||||
const PresenceManager = require('../managers/PresenceManager');
|
||||
const VoiceStateManager = require('../managers/VoiceStateManager');
|
||||
const Base = require('./Base');
|
||||
const { Error, TypeError } = require('../errors');
|
||||
const Util = require('../util/Util');
|
||||
|
||||
/**
|
||||
* Represents a guild (or a server) on Discord.
|
||||
@@ -110,7 +109,6 @@ class Guild extends Base {
|
||||
return this.client.ws.shards.get(this.shardID);
|
||||
}
|
||||
|
||||
/* eslint-disable complexity */
|
||||
/**
|
||||
* Sets up the guild.
|
||||
* @param {*} data The raw data of the guild
|
||||
@@ -279,8 +277,8 @@ class Guild extends Base {
|
||||
* The value set for the guild's default message notifications
|
||||
* @type {DefaultMessageNotifications|number}
|
||||
*/
|
||||
this.defaultMessageNotifications = DefaultMessageNotifications[data.default_message_notifications] ||
|
||||
data.default_message_notifications;
|
||||
this.defaultMessageNotifications =
|
||||
DefaultMessageNotifications[data.default_message_notifications] || data.default_message_notifications;
|
||||
|
||||
/**
|
||||
* The value set for the guild's system channel flags
|
||||
@@ -483,9 +481,12 @@ class Guild extends Base {
|
||||
* @readonly
|
||||
*/
|
||||
get owner() {
|
||||
return this.members.cache.get(this.ownerID) || (this.client.options.partials.includes(PartialTypes.GUILD_MEMBER) ?
|
||||
this.members.add({ user: { id: this.ownerID } }, true) :
|
||||
null);
|
||||
return (
|
||||
this.members.cache.get(this.ownerID) ||
|
||||
(this.client.options.partials.includes(PartialTypes.GUILD_MEMBER)
|
||||
? this.members.add({ user: { id: this.ownerID } }, true)
|
||||
: null)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -550,10 +551,12 @@ class Guild extends Base {
|
||||
* @readonly
|
||||
*/
|
||||
get me() {
|
||||
return this.members.cache.get(this.client.user.id) ||
|
||||
(this.client.options.partials.includes(PartialTypes.GUILD_MEMBER) ?
|
||||
this.members.add({ user: { id: this.client.user.id } }, true) :
|
||||
null);
|
||||
return (
|
||||
this.members.cache.get(this.client.user.id) ||
|
||||
(this.client.options.partials.includes(PartialTypes.GUILD_MEMBER)
|
||||
? this.members.add({ user: { id: this.client.user.id } }, true)
|
||||
: null)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -582,10 +585,13 @@ class Guild extends Base {
|
||||
* @returns {Promise<Guild>}
|
||||
*/
|
||||
fetch() {
|
||||
return this.client.api.guilds(this.id).get().then(data => {
|
||||
this._patch(data);
|
||||
return this;
|
||||
});
|
||||
return this.client.api
|
||||
.guilds(this.id)
|
||||
.get()
|
||||
.then(data => {
|
||||
this._patch(data);
|
||||
return this;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -596,14 +602,17 @@ class Guild extends Base {
|
||||
*/
|
||||
|
||||
/**
|
||||
* Fetches information on a banned user from this guild.
|
||||
* Fetches information on a banned user from this guild.
|
||||
* @param {UserResolvable} user The User to fetch the ban info of
|
||||
* @returns {Promise<BanInfo>}
|
||||
*/
|
||||
fetchBan(user) {
|
||||
const id = this.client.users.resolveID(user);
|
||||
if (!id) throw new Error('FETCH_BAN_RESOLVE_ID');
|
||||
return this.client.api.guilds(this.id).bans(id).get()
|
||||
return this.client.api
|
||||
.guilds(this.id)
|
||||
.bans(id)
|
||||
.get()
|
||||
.then(ban => ({
|
||||
reason: ban.reason,
|
||||
user: this.client.users.add(ban.user),
|
||||
@@ -615,15 +624,18 @@ class Guild extends Base {
|
||||
* @returns {Promise<Collection<Snowflake, BanInfo>>}
|
||||
*/
|
||||
fetchBans() {
|
||||
return this.client.api.guilds(this.id).bans.get().then(bans =>
|
||||
bans.reduce((collection, ban) => {
|
||||
collection.set(ban.user.id, {
|
||||
reason: ban.reason,
|
||||
user: this.client.users.add(ban.user),
|
||||
});
|
||||
return collection;
|
||||
}, new Collection()),
|
||||
);
|
||||
return this.client.api
|
||||
.guilds(this.id)
|
||||
.bans.get()
|
||||
.then(bans =>
|
||||
bans.reduce((collection, ban) => {
|
||||
collection.set(ban.user.id, {
|
||||
reason: ban.reason,
|
||||
user: this.client.users.add(ban.user),
|
||||
});
|
||||
return collection;
|
||||
}, new Collection()),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -637,11 +649,15 @@ class Guild extends Base {
|
||||
* .catch(console.error);
|
||||
*/
|
||||
fetchIntegrations() {
|
||||
return this.client.api.guilds(this.id).integrations.get().then(data =>
|
||||
data.reduce((collection, integration) =>
|
||||
collection.set(integration.id, new Integration(this.client, integration, this)),
|
||||
new Collection()),
|
||||
);
|
||||
return this.client.api
|
||||
.guilds(this.id)
|
||||
.integrations.get()
|
||||
.then(data =>
|
||||
data.reduce(
|
||||
(collection, integration) => collection.set(integration.id, new Integration(this.client, integration, this)),
|
||||
new Collection(),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -658,7 +674,9 @@ class Guild extends Base {
|
||||
* @returns {Promise<Guild>}
|
||||
*/
|
||||
createIntegration(data, reason) {
|
||||
return this.client.api.guilds(this.id).integrations.post({ data, reason })
|
||||
return this.client.api
|
||||
.guilds(this.id)
|
||||
.integrations.post({ data, reason })
|
||||
.then(() => this);
|
||||
}
|
||||
|
||||
@@ -678,7 +696,9 @@ class Guild extends Base {
|
||||
* .catch(console.error);
|
||||
*/
|
||||
fetchInvites() {
|
||||
return this.client.api.guilds(this.id).invites.get()
|
||||
return this.client.api
|
||||
.guilds(this.id)
|
||||
.invites.get()
|
||||
.then(inviteItems => {
|
||||
const invites = new Collection();
|
||||
for (const inviteItem of inviteItems) {
|
||||
@@ -705,7 +725,9 @@ class Guild extends Base {
|
||||
if (!this.features.includes('VANITY_URL')) {
|
||||
return Promise.reject(new Error('VANITY_URL'));
|
||||
}
|
||||
return this.client.api.guilds(this.id, 'vanity-url').get()
|
||||
return this.client.api
|
||||
.guilds(this.id, 'vanity-url')
|
||||
.get()
|
||||
.then(res => res.code);
|
||||
}
|
||||
|
||||
@@ -719,11 +741,14 @@ class Guild extends Base {
|
||||
* .catch(console.error);
|
||||
*/
|
||||
fetchWebhooks() {
|
||||
return this.client.api.guilds(this.id).webhooks.get().then(data => {
|
||||
const hooks = new Collection();
|
||||
for (const hook of data) hooks.set(hook.id, new Webhook(this.client, hook));
|
||||
return hooks;
|
||||
});
|
||||
return this.client.api
|
||||
.guilds(this.id)
|
||||
.webhooks.get()
|
||||
.then(data => {
|
||||
const hooks = new Collection();
|
||||
for (const hook of data) hooks.set(hook.id, new Webhook(this.client, hook));
|
||||
return hooks;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -731,11 +756,14 @@ class Guild extends Base {
|
||||
* @returns {Promise<Collection<string, VoiceRegion>>}
|
||||
*/
|
||||
fetchVoiceRegions() {
|
||||
return this.client.api.guilds(this.id).regions.get().then(res => {
|
||||
const regions = new Collection();
|
||||
for (const region of res) regions.set(region.id, new VoiceRegion(region));
|
||||
return regions;
|
||||
});
|
||||
return this.client.api
|
||||
.guilds(this.id)
|
||||
.regions.get()
|
||||
.then(res => {
|
||||
const regions = new Collection();
|
||||
for (const region of res) regions.set(region.id, new VoiceRegion(region));
|
||||
return regions;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -755,10 +783,13 @@ class Guild extends Base {
|
||||
* .catch(console.error);
|
||||
*/
|
||||
fetchEmbed() {
|
||||
return this.client.api.guilds(this.id).embed.get().then(data => ({
|
||||
enabled: data.enabled,
|
||||
channel: data.channel_id ? this.channels.cache.get(data.channel_id) : null,
|
||||
}));
|
||||
return this.client.api
|
||||
.guilds(this.id)
|
||||
.embed.get()
|
||||
.then(data => ({
|
||||
enabled: data.enabled,
|
||||
channel: data.channel_id ? this.channels.cache.get(data.channel_id) : null,
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -779,12 +810,16 @@ class Guild extends Base {
|
||||
if (options.before && options.before instanceof GuildAuditLogs.Entry) options.before = options.before.id;
|
||||
if (typeof options.type === 'string') options.type = GuildAuditLogs.Actions[options.type];
|
||||
|
||||
return this.client.api.guilds(this.id)['audit-logs'].get({ query: {
|
||||
before: options.before,
|
||||
limit: options.limit,
|
||||
user_id: this.client.users.resolveID(options.user),
|
||||
action_type: options.type,
|
||||
} })
|
||||
return this.client.api
|
||||
.guilds(this.id)
|
||||
['audit-logs'].get({
|
||||
query: {
|
||||
before: options.before,
|
||||
limit: options.limit,
|
||||
user_id: this.client.users.resolveID(options.user),
|
||||
action_type: options.type,
|
||||
},
|
||||
})
|
||||
.then(data => GuildAuditLogs.build(this, data));
|
||||
}
|
||||
|
||||
@@ -811,14 +846,18 @@ class Guild extends Base {
|
||||
for (let role of options.roles instanceof Collection ? options.roles.values() : options.roles) {
|
||||
role = this.roles.resolve(role);
|
||||
if (!role) {
|
||||
return Promise.reject(new TypeError('INVALID_TYPE', 'options.roles',
|
||||
'Array or Collection of Roles or Snowflakes', true));
|
||||
return Promise.reject(
|
||||
new TypeError('INVALID_TYPE', 'options.roles', 'Array or Collection of Roles or Snowflakes', true),
|
||||
);
|
||||
}
|
||||
roles.push(role.id);
|
||||
}
|
||||
options.roles = roles;
|
||||
}
|
||||
return this.client.api.guilds(this.id).members(user).put({ data: options })
|
||||
return this.client.api
|
||||
.guilds(this.id)
|
||||
.members(user)
|
||||
.put({ data: options })
|
||||
.then(data => this.members.add(data));
|
||||
}
|
||||
|
||||
@@ -859,9 +898,10 @@ class Guild extends Base {
|
||||
if (data.name) _data.name = data.name;
|
||||
if (data.region) _data.region = data.region;
|
||||
if (typeof data.verificationLevel !== 'undefined') {
|
||||
_data.verification_level = typeof data.verificationLevel === 'number' ?
|
||||
Number(data.verificationLevel) :
|
||||
ExplicitContentFilterLevels.indexOf(data.verificationLevel);
|
||||
_data.verification_level =
|
||||
typeof data.verificationLevel === 'number'
|
||||
? Number(data.verificationLevel)
|
||||
: ExplicitContentFilterLevels.indexOf(data.verificationLevel);
|
||||
}
|
||||
if (typeof data.afkChannel !== 'undefined') {
|
||||
_data.afk_channel_id = this.client.channels.resolveID(data.afkChannel);
|
||||
@@ -875,19 +915,23 @@ class Guild extends Base {
|
||||
if (data.splash) _data.splash = data.splash;
|
||||
if (data.banner) _data.banner = data.banner;
|
||||
if (typeof data.explicitContentFilter !== 'undefined') {
|
||||
_data.explicit_content_filter = typeof data.explicitContentFilter === 'number' ?
|
||||
data.explicitContentFilter :
|
||||
ExplicitContentFilterLevels.indexOf(data.explicitContentFilter);
|
||||
_data.explicit_content_filter =
|
||||
typeof data.explicitContentFilter === 'number'
|
||||
? data.explicitContentFilter
|
||||
: ExplicitContentFilterLevels.indexOf(data.explicitContentFilter);
|
||||
}
|
||||
if (typeof data.defaultMessageNotifications !== 'undefined') {
|
||||
_data.default_message_notifications = typeof data.defaultMessageNotifications === 'string' ?
|
||||
DefaultMessageNotifications.indexOf(data.defaultMessageNotifications) :
|
||||
data.defaultMessageNotifications;
|
||||
_data.default_message_notifications =
|
||||
typeof data.defaultMessageNotifications === 'string'
|
||||
? DefaultMessageNotifications.indexOf(data.defaultMessageNotifications)
|
||||
: data.defaultMessageNotifications;
|
||||
}
|
||||
if (typeof data.systemChannelFlags !== 'undefined') {
|
||||
_data.system_channel_flags = SystemChannelFlags.resolve(data.systemChannelFlags);
|
||||
}
|
||||
return this.client.api.guilds(this.id).patch({ data: _data, reason })
|
||||
return this.client.api
|
||||
.guilds(this.id)
|
||||
.patch({ data: _data, reason })
|
||||
.then(newData => this.client.actions.GuildUpdate.handle(newData).updated);
|
||||
}
|
||||
|
||||
@@ -1094,12 +1138,16 @@ class Guild extends Base {
|
||||
position: r.position,
|
||||
}));
|
||||
|
||||
return this.client.api.guilds(this.id).channels.patch({ data: updatedChannels }).then(() =>
|
||||
this.client.actions.GuildChannelsPositionUpdate.handle({
|
||||
guild_id: this.id,
|
||||
channels: updatedChannels,
|
||||
}).guild,
|
||||
);
|
||||
return this.client.api
|
||||
.guilds(this.id)
|
||||
.channels.patch({ data: updatedChannels })
|
||||
.then(
|
||||
() =>
|
||||
this.client.actions.GuildChannelsPositionUpdate.handle({
|
||||
guild_id: this.id,
|
||||
channels: updatedChannels,
|
||||
}).guild,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1126,14 +1174,18 @@ class Guild extends Base {
|
||||
}));
|
||||
|
||||
// Call the API to update role positions
|
||||
return this.client.api.guilds(this.id).roles.patch({
|
||||
data: rolePositions,
|
||||
}).then(() =>
|
||||
this.client.actions.GuildRolePositionUpdate.handle({
|
||||
guild_id: this.id,
|
||||
roles: rolePositions,
|
||||
}).guild,
|
||||
);
|
||||
return this.client.api
|
||||
.guilds(this.id)
|
||||
.roles.patch({
|
||||
data: rolePositions,
|
||||
})
|
||||
.then(
|
||||
() =>
|
||||
this.client.actions.GuildRolePositionUpdate.handle({
|
||||
guild_id: this.id,
|
||||
roles: rolePositions,
|
||||
}).guild,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1143,13 +1195,16 @@ class Guild extends Base {
|
||||
* @returns {Promise<Guild>}
|
||||
*/
|
||||
setEmbed(embed, reason) {
|
||||
return this.client.api.guilds(this.id).embed.patch({
|
||||
data: {
|
||||
enabled: embed.enabled,
|
||||
channel_id: this.channels.resolveID(embed.channel),
|
||||
},
|
||||
reason,
|
||||
}).then(() => this);
|
||||
return this.client.api
|
||||
.guilds(this.id)
|
||||
.embed.patch({
|
||||
data: {
|
||||
enabled: embed.enabled,
|
||||
channel_id: this.channels.resolveID(embed.channel),
|
||||
},
|
||||
reason,
|
||||
})
|
||||
.then(() => this);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1163,7 +1218,10 @@ class Guild extends Base {
|
||||
*/
|
||||
leave() {
|
||||
if (this.ownerID === this.client.user.id) return Promise.reject(new Error('GUILD_OWNED'));
|
||||
return this.client.api.users('@me').guilds(this.id).delete()
|
||||
return this.client.api
|
||||
.users('@me')
|
||||
.guilds(this.id)
|
||||
.delete()
|
||||
.then(() => this.client.actions.GuildDelete.handle({ id: this.id }).guild);
|
||||
}
|
||||
|
||||
@@ -1177,7 +1235,9 @@ class Guild extends Base {
|
||||
* .catch(console.error);
|
||||
*/
|
||||
delete() {
|
||||
return this.client.api.guilds(this.id).delete()
|
||||
return this.client.api
|
||||
.guilds(this.id)
|
||||
.delete()
|
||||
.then(() => this.client.actions.GuildDelete.handle({ id: this.id }).guild);
|
||||
}
|
||||
|
||||
@@ -1203,10 +1263,9 @@ class Guild extends Base {
|
||||
this.ownerID === guild.ownerID &&
|
||||
this.verificationLevel === guild.verificationLevel &&
|
||||
this.embedEnabled === guild.embedEnabled &&
|
||||
(this.features === guild.features || (
|
||||
this.features.length === guild.features.length &&
|
||||
this.features.every((feat, i) => feat === guild.features[i]))
|
||||
);
|
||||
(this.features === guild.features ||
|
||||
(this.features.length === guild.features.length &&
|
||||
this.features.every((feat, i) => feat === guild.features[i])));
|
||||
|
||||
if (equal) {
|
||||
if (this.embedChannel) {
|
||||
@@ -1261,9 +1320,9 @@ class Guild extends Base {
|
||||
*/
|
||||
_sortedChannels(channel) {
|
||||
const category = channel.type === ChannelTypes.CATEGORY;
|
||||
return Util.discordSort(this.channels.cache.filter(c =>
|
||||
c.type === channel.type && (category || c.parent === channel.parent),
|
||||
));
|
||||
return Util.discordSort(
|
||||
this.channels.cache.filter(c => c.type === channel.type && (category || c.parent === channel.parent)),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
'use strict';
|
||||
|
||||
const Collection = require('../util/Collection');
|
||||
const Integration = require('./Integration');
|
||||
const Snowflake = require('../util/Snowflake');
|
||||
const Webhook = require('./Webhook');
|
||||
const Util = require('../util/Util');
|
||||
const Collection = require('../util/Collection');
|
||||
const { PartialTypes } = require('../util/Constants');
|
||||
const Snowflake = require('../util/Snowflake');
|
||||
const Util = require('../util/Util');
|
||||
|
||||
/**
|
||||
* The target type of an entry, e.g. `GUILD`. Here are the available types:
|
||||
@@ -125,7 +125,6 @@ const Actions = {
|
||||
INTEGRATION_DELETE: 82,
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Audit logs entries are held in this class.
|
||||
*/
|
||||
@@ -223,49 +222,61 @@ class GuildAuditLogs {
|
||||
* @returns {AuditLogActionType}
|
||||
*/
|
||||
static actionType(action) {
|
||||
if ([
|
||||
Actions.CHANNEL_CREATE,
|
||||
Actions.CHANNEL_OVERWRITE_CREATE,
|
||||
Actions.MEMBER_BAN_REMOVE,
|
||||
Actions.BOT_ADD,
|
||||
Actions.ROLE_CREATE,
|
||||
Actions.INVITE_CREATE,
|
||||
Actions.WEBHOOK_CREATE,
|
||||
Actions.EMOJI_CREATE,
|
||||
Actions.MESSAGE_PIN,
|
||||
Actions.INTEGRATION_CREATE,
|
||||
].includes(action)) return 'CREATE';
|
||||
if (
|
||||
[
|
||||
Actions.CHANNEL_CREATE,
|
||||
Actions.CHANNEL_OVERWRITE_CREATE,
|
||||
Actions.MEMBER_BAN_REMOVE,
|
||||
Actions.BOT_ADD,
|
||||
Actions.ROLE_CREATE,
|
||||
Actions.INVITE_CREATE,
|
||||
Actions.WEBHOOK_CREATE,
|
||||
Actions.EMOJI_CREATE,
|
||||
Actions.MESSAGE_PIN,
|
||||
Actions.INTEGRATION_CREATE,
|
||||
].includes(action)
|
||||
) {
|
||||
return 'CREATE';
|
||||
}
|
||||
|
||||
if ([
|
||||
Actions.CHANNEL_DELETE,
|
||||
Actions.CHANNEL_OVERWRITE_DELETE,
|
||||
Actions.MEMBER_KICK,
|
||||
Actions.MEMBER_PRUNE,
|
||||
Actions.MEMBER_BAN_ADD,
|
||||
Actions.MEMBER_DISCONNECT,
|
||||
Actions.ROLE_DELETE,
|
||||
Actions.INVITE_DELETE,
|
||||
Actions.WEBHOOK_DELETE,
|
||||
Actions.EMOJI_DELETE,
|
||||
Actions.MESSAGE_DELETE,
|
||||
Actions.MESSAGE_BULK_DELETE,
|
||||
Actions.MESSAGE_UNPIN,
|
||||
Actions.INTEGRATION_DELETE,
|
||||
].includes(action)) return 'DELETE';
|
||||
if (
|
||||
[
|
||||
Actions.CHANNEL_DELETE,
|
||||
Actions.CHANNEL_OVERWRITE_DELETE,
|
||||
Actions.MEMBER_KICK,
|
||||
Actions.MEMBER_PRUNE,
|
||||
Actions.MEMBER_BAN_ADD,
|
||||
Actions.MEMBER_DISCONNECT,
|
||||
Actions.ROLE_DELETE,
|
||||
Actions.INVITE_DELETE,
|
||||
Actions.WEBHOOK_DELETE,
|
||||
Actions.EMOJI_DELETE,
|
||||
Actions.MESSAGE_DELETE,
|
||||
Actions.MESSAGE_BULK_DELETE,
|
||||
Actions.MESSAGE_UNPIN,
|
||||
Actions.INTEGRATION_DELETE,
|
||||
].includes(action)
|
||||
) {
|
||||
return 'DELETE';
|
||||
}
|
||||
|
||||
if ([
|
||||
Actions.GUILD_UPDATE,
|
||||
Actions.CHANNEL_UPDATE,
|
||||
Actions.CHANNEL_OVERWRITE_UPDATE,
|
||||
Actions.MEMBER_UPDATE,
|
||||
Actions.MEMBER_ROLE_UPDATE,
|
||||
Actions.MEMBER_MOVE,
|
||||
Actions.ROLE_UPDATE,
|
||||
Actions.INVITE_UPDATE,
|
||||
Actions.WEBHOOK_UPDATE,
|
||||
Actions.EMOJI_UPDATE,
|
||||
Actions.INTEGRATION_UPDATE,
|
||||
].includes(action)) return 'UPDATE';
|
||||
if (
|
||||
[
|
||||
Actions.GUILD_UPDATE,
|
||||
Actions.CHANNEL_UPDATE,
|
||||
Actions.CHANNEL_OVERWRITE_UPDATE,
|
||||
Actions.MEMBER_UPDATE,
|
||||
Actions.MEMBER_ROLE_UPDATE,
|
||||
Actions.MEMBER_MOVE,
|
||||
Actions.ROLE_UPDATE,
|
||||
Actions.INVITE_UPDATE,
|
||||
Actions.WEBHOOK_UPDATE,
|
||||
Actions.EMOJI_UPDATE,
|
||||
Actions.INTEGRATION_UPDATE,
|
||||
].includes(action)
|
||||
) {
|
||||
return 'UPDATE';
|
||||
}
|
||||
|
||||
return 'ALL';
|
||||
}
|
||||
@@ -279,7 +290,7 @@ class GuildAuditLogs {
|
||||
* Audit logs entry.
|
||||
*/
|
||||
class GuildAuditLogsEntry {
|
||||
constructor(logs, guild, data) { // eslint-disable-line complexity
|
||||
constructor(logs, guild, data) {
|
||||
const targetType = GuildAuditLogs.targetType(data.action_type);
|
||||
/**
|
||||
* The target type of this entry
|
||||
@@ -309,9 +320,9 @@ class GuildAuditLogsEntry {
|
||||
* The user that executed this entry
|
||||
* @type {User}
|
||||
*/
|
||||
this.executor = guild.client.options.partials.includes(PartialTypes.USER) ?
|
||||
guild.client.users.add({ id: data.user_id }) :
|
||||
guild.client.users.cache.get(data.user_id);
|
||||
this.executor = guild.client.options.partials.includes(PartialTypes.USER)
|
||||
? guild.client.users.add({ id: data.user_id })
|
||||
: guild.client.users.cache.get(data.user_id);
|
||||
|
||||
/**
|
||||
* An entry in the audit log representing a specific change.
|
||||
@@ -374,13 +385,15 @@ class GuildAuditLogsEntry {
|
||||
case Actions.CHANNEL_OVERWRITE_DELETE:
|
||||
switch (data.options.type) {
|
||||
case 'member':
|
||||
this.extra = guild.members.cache.get(data.options.id) ||
|
||||
{ id: data.options.id, type: 'member' };
|
||||
this.extra = guild.members.cache.get(data.options.id) || { id: data.options.id, type: 'member' };
|
||||
break;
|
||||
|
||||
case 'role':
|
||||
this.extra = guild.roles.cache.get(data.options.id) ||
|
||||
{ id: data.options.id, name: data.options.role_name, type: 'role' };
|
||||
this.extra = guild.roles.cache.get(data.options.id) || {
|
||||
id: data.options.id,
|
||||
name: data.options.role_name,
|
||||
type: 'role',
|
||||
};
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -405,21 +418,27 @@ class GuildAuditLogsEntry {
|
||||
this.target.id = data.target_id;
|
||||
// MEMBER_DISCONNECT and similar types do not provide a target_id.
|
||||
} else if (targetType === Targets.USER && data.target_id) {
|
||||
this.target = guild.client.options.partials.includes(PartialTypes.USER) ?
|
||||
guild.client.users.add({ id: data.target_id }) :
|
||||
guild.client.users.cache.get(data.target_id);
|
||||
this.target = guild.client.options.partials.includes(PartialTypes.USER)
|
||||
? guild.client.users.add({ id: data.target_id })
|
||||
: guild.client.users.cache.get(data.target_id);
|
||||
} else if (targetType === Targets.GUILD) {
|
||||
this.target = guild.client.guilds.cache.get(data.target_id);
|
||||
} else if (targetType === Targets.WEBHOOK) {
|
||||
this.target = logs.webhooks.get(data.target_id) ||
|
||||
new Webhook(guild.client,
|
||||
this.changes.reduce((o, c) => {
|
||||
o[c.key] = c.new || c.old;
|
||||
return o;
|
||||
}, {
|
||||
id: data.target_id,
|
||||
guild_id: guild.id,
|
||||
}));
|
||||
this.target =
|
||||
logs.webhooks.get(data.target_id) ||
|
||||
new Webhook(
|
||||
guild.client,
|
||||
this.changes.reduce(
|
||||
(o, c) => {
|
||||
o[c.key] = c.new || c.old;
|
||||
return o;
|
||||
},
|
||||
{
|
||||
id: data.target_id,
|
||||
guild_id: guild.id,
|
||||
},
|
||||
),
|
||||
);
|
||||
} else if (targetType === Targets.INVITE) {
|
||||
this.target = guild.members.fetch(guild.client.user.id).then(me => {
|
||||
if (me.permissions.has('MANAGE_GUILD')) {
|
||||
@@ -437,15 +456,24 @@ class GuildAuditLogsEntry {
|
||||
});
|
||||
} else if (targetType === Targets.MESSAGE) {
|
||||
// Discord sends a channel id for the MESSAGE_BULK_DELETE action type.
|
||||
this.target = data.action_type === Actions.MESSAGE_BULK_DELETE ?
|
||||
guild.channels.cache.get(data.target_id) || { id: data.target_id } :
|
||||
guild.client.users.cache.get(data.target_id);
|
||||
this.target =
|
||||
data.action_type === Actions.MESSAGE_BULK_DELETE
|
||||
? guild.channels.cache.get(data.target_id) || { id: data.target_id }
|
||||
: guild.client.users.cache.get(data.target_id);
|
||||
} else if (targetType === Targets.INTEGRATION) {
|
||||
this.target = logs.integrations.get(data.target_id) ||
|
||||
new Integration(guild.client, this.changes.reduce((o, c) => {
|
||||
o[c.key] = c.new || c.old;
|
||||
return o;
|
||||
}, { id: data.target_id }), guild);
|
||||
this.target =
|
||||
logs.integrations.get(data.target_id) ||
|
||||
new Integration(
|
||||
guild.client,
|
||||
this.changes.reduce(
|
||||
(o, c) => {
|
||||
o[c.key] = c.new || c.old;
|
||||
return o;
|
||||
},
|
||||
{ id: data.target_id },
|
||||
),
|
||||
guild,
|
||||
);
|
||||
} else if (data.target_id) {
|
||||
this.target = guild[`${targetType.toLowerCase()}s`].cache.get(data.target_id) || { id: data.target_id };
|
||||
}
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
'use strict';
|
||||
|
||||
const Channel = require('./Channel');
|
||||
const Role = require('./Role');
|
||||
const Invite = require('./Invite');
|
||||
const PermissionOverwrites = require('./PermissionOverwrites');
|
||||
const Util = require('../util/Util');
|
||||
const Permissions = require('../util/Permissions');
|
||||
const Collection = require('../util/Collection');
|
||||
const Role = require('./Role');
|
||||
const { Error, TypeError } = require('../errors');
|
||||
const Collection = require('../util/Collection');
|
||||
const Permissions = require('../util/Permissions');
|
||||
const Util = require('../util/Util');
|
||||
|
||||
/**
|
||||
* Represents a guild channel from any of the following:
|
||||
@@ -85,9 +85,11 @@ class GuildChannel extends Channel {
|
||||
if (this.permissionOverwrites.size !== this.parent.permissionOverwrites.size) return false;
|
||||
return this.permissionOverwrites.every((value, key) => {
|
||||
const testVal = this.parent.permissionOverwrites.get(key);
|
||||
return testVal !== undefined &&
|
||||
return (
|
||||
testVal !== undefined &&
|
||||
testVal.deny.bitfield === value.deny.bitfield &&
|
||||
testVal.allow.bitfield === value.allow.bitfield;
|
||||
testVal.allow.bitfield === value.allow.bitfield
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -202,12 +204,9 @@ class GuildChannel extends Channel {
|
||||
*/
|
||||
overwritePermissions(overwrites, reason) {
|
||||
if (!Array.isArray(overwrites) && !(overwrites instanceof Collection)) {
|
||||
return Promise.reject(new TypeError(
|
||||
'INVALID_TYPE',
|
||||
'overwrites',
|
||||
'Array or Collection of Permission Overwrites',
|
||||
true,
|
||||
));
|
||||
return Promise.reject(
|
||||
new TypeError('INVALID_TYPE', 'overwrites', 'Array or Collection of Permission Overwrites', true),
|
||||
);
|
||||
}
|
||||
return this.edit({ permissionOverwrites: overwrites, reason }).then(() => this);
|
||||
}
|
||||
@@ -256,8 +255,12 @@ class GuildChannel extends Channel {
|
||||
const type = userOrRole instanceof Role ? 'role' : 'member';
|
||||
const { allow, deny } = PermissionOverwrites.resolveOverwriteOptions(options);
|
||||
|
||||
return this.client.api.channels(this.id).permissions[userOrRole.id]
|
||||
.put({ data: { id: userOrRole.id, type, allow: allow.bitfield, deny: deny.bitfield }, reason })
|
||||
return this.client.api
|
||||
.channels(this.id)
|
||||
.permissions[userOrRole.id].put({
|
||||
data: { id: userOrRole.id, type, allow: allow.bitfield, deny: deny.bitfield },
|
||||
reason,
|
||||
})
|
||||
.then(() => this);
|
||||
}
|
||||
|
||||
@@ -316,18 +319,23 @@ class GuildChannel extends Channel {
|
||||
*/
|
||||
async edit(data, reason) {
|
||||
if (typeof data.position !== 'undefined') {
|
||||
await Util.setPosition(this, data.position, false,
|
||||
this.guild._sortedChannels(this), this.client.api.guilds(this.guild.id).channels, reason)
|
||||
.then(updatedChannels => {
|
||||
this.client.actions.GuildChannelsPositionUpdate.handle({
|
||||
guild_id: this.guild.id,
|
||||
channels: updatedChannels,
|
||||
});
|
||||
await Util.setPosition(
|
||||
this,
|
||||
data.position,
|
||||
false,
|
||||
this.guild._sortedChannels(this),
|
||||
this.client.api.guilds(this.guild.id).channels,
|
||||
reason,
|
||||
).then(updatedChannels => {
|
||||
this.client.actions.GuildChannelsPositionUpdate.handle({
|
||||
guild_id: this.guild.id,
|
||||
channels: updatedChannels,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
const permission_overwrites = data.permissionOverwrites &&
|
||||
data.permissionOverwrites.map(o => PermissionOverwrites.resolve(o, this.guild));
|
||||
const permission_overwrites =
|
||||
data.permissionOverwrites && data.permissionOverwrites.map(o => PermissionOverwrites.resolve(o, this.guild));
|
||||
|
||||
const newData = await this.client.api.channels(this.id).patch({
|
||||
data: {
|
||||
@@ -378,11 +386,14 @@ class GuildChannel extends Channel {
|
||||
* .catch(console.error);
|
||||
*/
|
||||
setParent(channel, { lockPermissions = true, reason } = {}) {
|
||||
return this.edit({
|
||||
// eslint-disable-next-line no-prototype-builtins
|
||||
parentID: channel !== null ? channel.hasOwnProperty('id') ? channel.id : channel : null,
|
||||
lockPermissions,
|
||||
}, reason);
|
||||
return this.edit(
|
||||
{
|
||||
// eslint-disable-next-line no-prototype-builtins
|
||||
parentID: channel !== null ? (channel.hasOwnProperty('id') ? channel.id : channel) : null,
|
||||
lockPermissions,
|
||||
},
|
||||
reason,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -414,15 +425,20 @@ class GuildChannel extends Channel {
|
||||
* .catch(console.error);
|
||||
*/
|
||||
setPosition(position, { relative, reason } = {}) {
|
||||
return Util.setPosition(this, position, relative,
|
||||
this.guild._sortedChannels(this), this.client.api.guilds(this.guild.id).channels, reason)
|
||||
.then(updatedChannels => {
|
||||
this.client.actions.GuildChannelsPositionUpdate.handle({
|
||||
guild_id: this.guild.id,
|
||||
channels: updatedChannels,
|
||||
});
|
||||
return this;
|
||||
return Util.setPosition(
|
||||
this,
|
||||
position,
|
||||
relative,
|
||||
this.guild._sortedChannels(this),
|
||||
this.client.api.guilds(this.guild.id).channels,
|
||||
reason,
|
||||
).then(updatedChannels => {
|
||||
this.client.actions.GuildChannelsPositionUpdate.handle({
|
||||
guild_id: this.guild.id,
|
||||
channels: updatedChannels,
|
||||
});
|
||||
return this;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -442,9 +458,17 @@ class GuildChannel extends Channel {
|
||||
* .catch(console.error);
|
||||
*/
|
||||
createInvite({ temporary = false, maxAge = 86400, maxUses = 0, unique, reason } = {}) {
|
||||
return this.client.api.channels(this.id).invites.post({ data: {
|
||||
temporary, max_age: maxAge, max_uses: maxUses, unique,
|
||||
}, reason })
|
||||
return this.client.api
|
||||
.channels(this.id)
|
||||
.invites.post({
|
||||
data: {
|
||||
temporary,
|
||||
max_age: maxAge,
|
||||
max_uses: maxUses,
|
||||
unique,
|
||||
},
|
||||
reason,
|
||||
})
|
||||
.then(invite => new Invite(this.client, invite));
|
||||
}
|
||||
|
||||
@@ -481,18 +505,21 @@ class GuildChannel extends Channel {
|
||||
* @returns {Promise<GuildChannel>}
|
||||
*/
|
||||
clone(options = {}) {
|
||||
Util.mergeDefault({
|
||||
name: this.name,
|
||||
permissionOverwrites: this.permissionOverwrites,
|
||||
topic: this.topic,
|
||||
type: this.type,
|
||||
nsfw: this.nsfw,
|
||||
parent: this.parent,
|
||||
bitrate: this.bitrate,
|
||||
userLimit: this.userLimit,
|
||||
rateLimitPerUser: this.rateLimitPerUser,
|
||||
reason: null,
|
||||
}, options);
|
||||
Util.mergeDefault(
|
||||
{
|
||||
name: this.name,
|
||||
permissionOverwrites: this.permissionOverwrites,
|
||||
topic: this.topic,
|
||||
type: this.type,
|
||||
nsfw: this.nsfw,
|
||||
parent: this.parent,
|
||||
bitrate: this.bitrate,
|
||||
userLimit: this.userLimit,
|
||||
rateLimitPerUser: this.rateLimitPerUser,
|
||||
reason: null,
|
||||
},
|
||||
options,
|
||||
);
|
||||
return this.guild.channels.create(options.name, options);
|
||||
}
|
||||
/* eslint-enable max-len */
|
||||
@@ -504,7 +531,8 @@ class GuildChannel extends Channel {
|
||||
* @returns {boolean}
|
||||
*/
|
||||
equals(channel) {
|
||||
let equal = channel &&
|
||||
let equal =
|
||||
channel &&
|
||||
this.id === channel.id &&
|
||||
this.type === channel.type &&
|
||||
this.topic === channel.topic &&
|
||||
@@ -565,7 +593,10 @@ class GuildChannel extends Channel {
|
||||
* .catch(console.error);
|
||||
*/
|
||||
delete(reason) {
|
||||
return this.client.api.channels(this.id).delete({ reason }).then(() => this);
|
||||
return this.client.api
|
||||
.channels(this.id)
|
||||
.delete({ reason })
|
||||
.then(() => this);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
'use strict';
|
||||
|
||||
const Emoji = require('./Emoji');
|
||||
const { Error } = require('../errors');
|
||||
const GuildEmojiRoleManager = require('../managers/GuildEmojiRoleManager');
|
||||
const Permissions = require('../util/Permissions');
|
||||
const { Error } = require('../errors');
|
||||
const Emoji = require('./Emoji');
|
||||
|
||||
/**
|
||||
* Represents a custom emoji.
|
||||
@@ -74,8 +74,7 @@ class GuildEmoji extends Emoji {
|
||||
*/
|
||||
get deletable() {
|
||||
if (!this.guild.me) throw new Error('GUILD_UNCACHED_ME');
|
||||
return !this.managed &&
|
||||
this.guild.me.hasPermission(Permissions.FLAGS.MANAGE_EMOJIS);
|
||||
return !this.managed && this.guild.me.hasPermission(Permissions.FLAGS.MANAGE_EMOJIS);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -100,7 +99,10 @@ class GuildEmoji extends Emoji {
|
||||
return Promise.reject(new Error('MISSING_MANAGE_EMOJIS_PERMISSION', this.guild));
|
||||
}
|
||||
}
|
||||
return this.client.api.guilds(this.guild.id).emojis(this.id).get()
|
||||
return this.client.api
|
||||
.guilds(this.guild.id)
|
||||
.emojis(this.id)
|
||||
.get()
|
||||
.then(emoji => this.client.users.add(emoji.user));
|
||||
}
|
||||
|
||||
@@ -124,11 +126,16 @@ class GuildEmoji extends Emoji {
|
||||
*/
|
||||
edit(data, reason) {
|
||||
const roles = data.roles ? data.roles.map(r => r.id || r) : undefined;
|
||||
return this.client.api.guilds(this.guild.id).emojis(this.id)
|
||||
.patch({ data: {
|
||||
name: data.name,
|
||||
roles,
|
||||
}, reason })
|
||||
return this.client.api
|
||||
.guilds(this.guild.id)
|
||||
.emojis(this.id)
|
||||
.patch({
|
||||
data: {
|
||||
name: data.name,
|
||||
roles,
|
||||
},
|
||||
reason,
|
||||
})
|
||||
.then(newData => {
|
||||
const clone = this._clone();
|
||||
clone._patch(newData);
|
||||
@@ -152,7 +159,10 @@ class GuildEmoji extends Emoji {
|
||||
* @returns {Promise<GuildEmoji>}
|
||||
*/
|
||||
delete(reason) {
|
||||
return this.client.api.guilds(this.guild.id).emojis(this.id).delete({ reason })
|
||||
return this.client.api
|
||||
.guilds(this.guild.id)
|
||||
.emojis(this.id)
|
||||
.delete({ reason })
|
||||
.then(() => this);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
'use strict';
|
||||
|
||||
const TextBasedChannel = require('./interfaces/TextBasedChannel');
|
||||
const Role = require('./Role');
|
||||
const Permissions = require('../util/Permissions');
|
||||
const GuildMemberRoleManager = require('../managers/GuildMemberRoleManager');
|
||||
const Base = require('./Base');
|
||||
const VoiceState = require('./VoiceState');
|
||||
const { Presence } = require('./Presence');
|
||||
const Role = require('./Role');
|
||||
const VoiceState = require('./VoiceState');
|
||||
const TextBasedChannel = require('./interfaces/TextBasedChannel');
|
||||
const { Error } = require('../errors');
|
||||
const GuildMemberRoleManager = require('../managers/GuildMemberRoleManager');
|
||||
const Permissions = require('../util/Permissions');
|
||||
|
||||
/**
|
||||
* Represents a member of a guild on Discord.
|
||||
@@ -152,12 +152,15 @@ class GuildMember extends Base {
|
||||
* @readonly
|
||||
*/
|
||||
get presence() {
|
||||
return this.guild.presences.cache.get(this.id) || new Presence(this.client, {
|
||||
user: {
|
||||
id: this.id,
|
||||
},
|
||||
guild: this.guild,
|
||||
});
|
||||
return (
|
||||
this.guild.presences.cache.get(this.id) ||
|
||||
new Presence(this.client, {
|
||||
user: {
|
||||
id: this.id,
|
||||
},
|
||||
guild: this.guild,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -293,7 +296,7 @@ class GuildMember extends Base {
|
||||
data.channel_id = null;
|
||||
data.channel = undefined;
|
||||
}
|
||||
if (data.roles) data.roles = data.roles.map(role => role instanceof Role ? role.id : role);
|
||||
if (data.roles) data.roles = data.roles.map(role => (role instanceof Role ? role.id : role));
|
||||
let endpoint = this.client.api.guilds(this.guild.id);
|
||||
if (this.user.id === this.client.user.id) {
|
||||
const keys = Object.keys(data);
|
||||
@@ -342,7 +345,10 @@ class GuildMember extends Base {
|
||||
* @returns {Promise<GuildMember>}
|
||||
*/
|
||||
kick(reason) {
|
||||
return this.client.api.guilds(this.guild.id).members(this.user.id).delete({ reason })
|
||||
return this.client.api
|
||||
.guilds(this.guild.id)
|
||||
.members(this.user.id)
|
||||
.delete({ reason })
|
||||
.then(() => this);
|
||||
}
|
||||
|
||||
|
||||
@@ -98,7 +98,10 @@ class Integration extends Base {
|
||||
*/
|
||||
sync() {
|
||||
this.syncing = true;
|
||||
return this.client.api.guilds(this.guild.id).integrations(this.id).post()
|
||||
return this.client.api
|
||||
.guilds(this.guild.id)
|
||||
.integrations(this.id)
|
||||
.post()
|
||||
.then(() => {
|
||||
this.syncing = false;
|
||||
this.syncedAt = Date.now();
|
||||
@@ -129,7 +132,10 @@ class Integration extends Base {
|
||||
data.expireGracePeriod = null;
|
||||
}
|
||||
// The option enable_emoticons is only available for Twitch at this moment
|
||||
return this.client.api.guilds(this.guild.id).integrations(this.id).patch({ data, reason })
|
||||
return this.client.api
|
||||
.guilds(this.guild.id)
|
||||
.integrations(this.id)
|
||||
.patch({ data, reason })
|
||||
.then(() => {
|
||||
this._patch(data);
|
||||
return this;
|
||||
@@ -142,7 +148,10 @@ class Integration extends Base {
|
||||
* @param {string} [reason] Reason for deleting this integration
|
||||
*/
|
||||
delete(reason) {
|
||||
return this.client.api.guilds(this.guild.id).integrations(this.id).delete({ reason })
|
||||
return this.client.api
|
||||
.guilds(this.guild.id)
|
||||
.integrations(this.id)
|
||||
.delete({ reason })
|
||||
.then(() => this);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
'use strict';
|
||||
|
||||
const Base = require('./Base');
|
||||
const { Endpoints } = require('../util/Constants');
|
||||
const Permissions = require('../util/Permissions');
|
||||
const Base = require('./Base');
|
||||
|
||||
/**
|
||||
* Represents an invitation to a guild channel.
|
||||
@@ -119,8 +119,10 @@ class Invite extends Base {
|
||||
const guild = this.guild;
|
||||
if (!guild || !this.client.guilds.cache.has(guild.id)) return false;
|
||||
if (!guild.me) throw new Error('GUILD_UNCACHED_ME');
|
||||
return this.channel.permissionsFor(this.client.user).has(Permissions.FLAGS.MANAGE_CHANNELS, false) ||
|
||||
guild.me.permissions.has(Permissions.FLAGS.MANAGE_GUILD);
|
||||
return (
|
||||
this.channel.permissionsFor(this.client.user).has(Permissions.FLAGS.MANAGE_CHANNELS, false) ||
|
||||
guild.me.permissions.has(Permissions.FLAGS.MANAGE_GUILD)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -129,7 +131,7 @@ class Invite extends Base {
|
||||
* @readonly
|
||||
*/
|
||||
get expiresTimestamp() {
|
||||
return this.createdTimestamp && this.maxAge ? this.createdTimestamp + (this.maxAge * 1000) : null;
|
||||
return this.createdTimestamp && this.maxAge ? this.createdTimestamp + this.maxAge * 1000 : null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
'use strict';
|
||||
|
||||
const Mentions = require('./MessageMentions');
|
||||
const APIMessage = require('./APIMessage');
|
||||
const Base = require('./Base');
|
||||
const ClientApplication = require('./ClientApplication');
|
||||
const MessageAttachment = require('./MessageAttachment');
|
||||
const Embed = require('./MessageEmbed');
|
||||
const Mentions = require('./MessageMentions');
|
||||
const ReactionCollector = require('./ReactionCollector');
|
||||
const ClientApplication = require('./ClientApplication');
|
||||
const Util = require('../util/Util');
|
||||
const Collection = require('../util/Collection');
|
||||
const ReactionManager = require('../managers/ReactionManager');
|
||||
const { MessageTypes } = require('../util/Constants');
|
||||
const Permissions = require('../util/Permissions');
|
||||
const Base = require('./Base');
|
||||
const { Error, TypeError } = require('../errors');
|
||||
const APIMessage = require('./APIMessage');
|
||||
const ReactionManager = require('../managers/ReactionManager');
|
||||
const Collection = require('../util/Collection');
|
||||
const { MessageTypes } = require('../util/Constants');
|
||||
const MessageFlags = require('../util/MessageFlags');
|
||||
const Permissions = require('../util/Permissions');
|
||||
const Util = require('../util/Util');
|
||||
|
||||
/**
|
||||
* Represents a message on Discord.
|
||||
@@ -43,7 +43,7 @@ class Message extends Base {
|
||||
if (data) this._patch(data);
|
||||
}
|
||||
|
||||
_patch(data) { // eslint-disable-line complexity
|
||||
_patch(data) {
|
||||
/**
|
||||
* The ID of the message
|
||||
* @type {Snowflake}
|
||||
@@ -107,9 +107,7 @@ class Message extends Base {
|
||||
this.attachments = new Collection();
|
||||
if (data.attachments) {
|
||||
for (const attachment of data.attachments) {
|
||||
this.attachments.set(attachment.id, new MessageAttachment(
|
||||
attachment.url, attachment.filename, attachment,
|
||||
));
|
||||
this.attachments.set(attachment.id, new MessageAttachment(attachment.url, attachment.filename, attachment));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -158,10 +156,12 @@ class Message extends Base {
|
||||
* Group activity
|
||||
* @type {?MessageActivity}
|
||||
*/
|
||||
this.activity = data.activity ? {
|
||||
partyID: data.activity.party_id,
|
||||
type: data.activity.type,
|
||||
} : null;
|
||||
this.activity = data.activity
|
||||
? {
|
||||
partyID: data.activity.party_id,
|
||||
type: data.activity.type,
|
||||
}
|
||||
: null;
|
||||
|
||||
/**
|
||||
* The previous versions of the message, sorted with the most recent first
|
||||
@@ -194,11 +194,13 @@ class Message extends Base {
|
||||
* Message reference data
|
||||
* @type {?MessageReference}
|
||||
*/
|
||||
this.reference = data.message_reference ? {
|
||||
channelID: data.message_reference.channel_id,
|
||||
guildID: data.message_reference.guild_id,
|
||||
messageID: data.message_reference.message_id,
|
||||
} : null;
|
||||
this.reference = data.message_reference
|
||||
? {
|
||||
channelID: data.message_reference.channel_id,
|
||||
guildID: data.message_reference.guild_id,
|
||||
messageID: data.message_reference.message_id,
|
||||
}
|
||||
: null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -229,9 +231,7 @@ class Message extends Base {
|
||||
if ('attachments' in data) {
|
||||
this.attachments = new Collection();
|
||||
for (const attachment of data.attachments) {
|
||||
this.attachments.set(attachment.id, new MessageAttachment(
|
||||
attachment.url, attachment.filename, attachment,
|
||||
));
|
||||
this.attachments.set(attachment.id, new MessageAttachment(attachment.url, attachment.filename, attachment));
|
||||
}
|
||||
} else {
|
||||
this.attachments = new Collection(this.attachments);
|
||||
@@ -377,9 +377,11 @@ class Message extends Base {
|
||||
* @readonly
|
||||
*/
|
||||
get deletable() {
|
||||
return !this.deleted && (this.author.id === this.client.user.id || (this.guild &&
|
||||
this.channel.permissionsFor(this.client.user).has(Permissions.FLAGS.MANAGE_MESSAGES, false)
|
||||
));
|
||||
return (
|
||||
!this.deleted &&
|
||||
(this.author.id === this.client.user.id ||
|
||||
(this.guild && this.channel.permissionsFor(this.client.user).has(Permissions.FLAGS.MANAGE_MESSAGES, false)))
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -388,8 +390,10 @@ class Message extends Base {
|
||||
* @readonly
|
||||
*/
|
||||
get pinnable() {
|
||||
return this.type === 'DEFAULT' && (!this.guild ||
|
||||
this.channel.permissionsFor(this.client.user).has(Permissions.FLAGS.MANAGE_MESSAGES, false));
|
||||
return (
|
||||
this.type === 'DEFAULT' &&
|
||||
(!this.guild || this.channel.permissionsFor(this.client.user).has(Permissions.FLAGS.MANAGE_MESSAGES, false))
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -412,16 +416,13 @@ class Message extends Base {
|
||||
* .catch(console.error);
|
||||
*/
|
||||
edit(content, options) {
|
||||
const { data } = content instanceof APIMessage ?
|
||||
content.resolveData() :
|
||||
APIMessage.create(this, content, options).resolveData();
|
||||
return this.client.api.channels[this.channel.id].messages[this.id]
|
||||
.patch({ data })
|
||||
.then(d => {
|
||||
const clone = this._clone();
|
||||
clone._patch(d);
|
||||
return clone;
|
||||
});
|
||||
const { data } =
|
||||
content instanceof APIMessage ? content.resolveData() : APIMessage.create(this, content, options).resolveData();
|
||||
return this.client.api.channels[this.channel.id].messages[this.id].patch({ data }).then(d => {
|
||||
const clone = this._clone();
|
||||
clone._patch(d);
|
||||
return clone;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -429,7 +430,10 @@ class Message extends Base {
|
||||
* @returns {Promise<Message>}
|
||||
*/
|
||||
pin() {
|
||||
return this.client.api.channels(this.channel.id).pins(this.id).put()
|
||||
return this.client.api
|
||||
.channels(this.channel.id)
|
||||
.pins(this.id)
|
||||
.put()
|
||||
.then(() => this);
|
||||
}
|
||||
|
||||
@@ -438,7 +442,10 @@ class Message extends Base {
|
||||
* @returns {Promise<Message>}
|
||||
*/
|
||||
unpin() {
|
||||
return this.client.api.channels(this.channel.id).pins(this.id).delete()
|
||||
return this.client.api
|
||||
.channels(this.channel.id)
|
||||
.pins(this.id)
|
||||
.delete()
|
||||
.then(() => this);
|
||||
}
|
||||
|
||||
@@ -461,14 +468,20 @@ class Message extends Base {
|
||||
emoji = this.client.emojis.resolveIdentifier(emoji);
|
||||
if (!emoji) throw new TypeError('EMOJI_TYPE');
|
||||
|
||||
return this.client.api.channels(this.channel.id).messages(this.id).reactions(emoji, '@me')
|
||||
return this.client.api
|
||||
.channels(this.channel.id)
|
||||
.messages(this.id)
|
||||
.reactions(emoji, '@me')
|
||||
.put()
|
||||
.then(() => this.client.actions.MessageReactionAdd.handle({
|
||||
user: this.client.user,
|
||||
channel: this.channel,
|
||||
message: this,
|
||||
emoji: Util.parseEmoji(emoji),
|
||||
}).reaction);
|
||||
.then(
|
||||
() =>
|
||||
this.client.actions.MessageReactionAdd.handle({
|
||||
user: this.client.user,
|
||||
channel: this.channel,
|
||||
message: this,
|
||||
emoji: Util.parseEmoji(emoji),
|
||||
}).reaction,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -509,9 +522,10 @@ class Message extends Base {
|
||||
* .catch(console.error);
|
||||
*/
|
||||
reply(content, options) {
|
||||
return this.channel.send(content instanceof APIMessage ?
|
||||
content :
|
||||
APIMessage.transformOptions(content, options, { reply: this.member || this.author }),
|
||||
return this.channel.send(
|
||||
content instanceof APIMessage
|
||||
? content
|
||||
: APIMessage.transformOptions(content, options, { reply: this.member || this.author }),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -562,16 +576,18 @@ class Message extends Base {
|
||||
const embedUpdate = !message.author && !message.attachments;
|
||||
if (embedUpdate) return this.id === message.id && this.embeds.length === message.embeds.length;
|
||||
|
||||
let equal = this.id === message.id &&
|
||||
this.author.id === message.author.id &&
|
||||
this.content === message.content &&
|
||||
this.tts === message.tts &&
|
||||
this.nonce === message.nonce &&
|
||||
this.embeds.length === message.embeds.length &&
|
||||
this.attachments.length === message.attachments.length;
|
||||
let equal =
|
||||
this.id === message.id &&
|
||||
this.author.id === message.author.id &&
|
||||
this.content === message.content &&
|
||||
this.tts === message.tts &&
|
||||
this.nonce === message.nonce &&
|
||||
this.embeds.length === message.embeds.length &&
|
||||
this.attachments.length === message.attachments.length;
|
||||
|
||||
if (equal && rawData) {
|
||||
equal = this.mentions.everyone === message.mentions.everyone &&
|
||||
equal =
|
||||
this.mentions.everyone === message.mentions.everyone &&
|
||||
this.createdTimestamp === new Date(rawData.timestamp).getTime() &&
|
||||
this.editedTimestamp === new Date(rawData.edited_timestamp).getTime();
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
'use strict';
|
||||
|
||||
const Util = require('../util/Util');
|
||||
const { RangeError } = require('../errors');
|
||||
const Util = require('../util/Util');
|
||||
|
||||
/**
|
||||
* Represents an embed in a message (image/video preview, rich embed, etc.)
|
||||
@@ -11,7 +11,7 @@ class MessageEmbed {
|
||||
this.setup(data);
|
||||
}
|
||||
|
||||
setup(data) { // eslint-disable-line complexity
|
||||
setup(data) {
|
||||
/**
|
||||
* The type of this embed, either:
|
||||
* * `rich` - a rich embed
|
||||
@@ -79,12 +79,14 @@ class MessageEmbed {
|
||||
* The thumbnail of this embed (if there is one)
|
||||
* @type {?MessageEmbedThumbnail}
|
||||
*/
|
||||
this.thumbnail = data.thumbnail ? {
|
||||
url: data.thumbnail.url,
|
||||
proxyURL: data.thumbnail.proxyURL || data.thumbnail.proxy_url,
|
||||
height: data.thumbnail.height,
|
||||
width: data.thumbnail.width,
|
||||
} : null;
|
||||
this.thumbnail = data.thumbnail
|
||||
? {
|
||||
url: data.thumbnail.url,
|
||||
proxyURL: data.thumbnail.proxyURL || data.thumbnail.proxy_url,
|
||||
height: data.thumbnail.height,
|
||||
width: data.thumbnail.width,
|
||||
}
|
||||
: null;
|
||||
|
||||
/**
|
||||
* @typedef {Object} MessageEmbedImage
|
||||
@@ -98,12 +100,14 @@ class MessageEmbed {
|
||||
* The image of this embed, if there is one
|
||||
* @type {?MessageEmbedImage}
|
||||
*/
|
||||
this.image = data.image ? {
|
||||
url: data.image.url,
|
||||
proxyURL: data.image.proxyURL || data.image.proxy_url,
|
||||
height: data.image.height,
|
||||
width: data.image.width,
|
||||
} : null;
|
||||
this.image = data.image
|
||||
? {
|
||||
url: data.image.url,
|
||||
proxyURL: data.image.proxyURL || data.image.proxy_url,
|
||||
height: data.image.height,
|
||||
width: data.image.width,
|
||||
}
|
||||
: null;
|
||||
|
||||
/**
|
||||
* @typedef {Object} MessageEmbedVideo
|
||||
@@ -118,12 +122,14 @@ class MessageEmbed {
|
||||
* @type {?MessageEmbedVideo}
|
||||
* @readonly
|
||||
*/
|
||||
this.video = data.video ? {
|
||||
url: data.video.url,
|
||||
proxyURL: data.video.proxyURL || data.video.proxy_url,
|
||||
height: data.video.height,
|
||||
width: data.video.width,
|
||||
} : null;
|
||||
this.video = data.video
|
||||
? {
|
||||
url: data.video.url,
|
||||
proxyURL: data.video.proxyURL || data.video.proxy_url,
|
||||
height: data.video.height,
|
||||
width: data.video.width,
|
||||
}
|
||||
: null;
|
||||
|
||||
/**
|
||||
* @typedef {Object} MessageEmbedAuthor
|
||||
@@ -137,12 +143,14 @@ class MessageEmbed {
|
||||
* The author of this embed (if there is one)
|
||||
* @type {?MessageEmbedAuthor}
|
||||
*/
|
||||
this.author = data.author ? {
|
||||
name: data.author.name,
|
||||
url: data.author.url,
|
||||
iconURL: data.author.iconURL || data.author.icon_url,
|
||||
proxyIconURL: data.author.proxyIconURL || data.author.proxy_icon_url,
|
||||
} : null;
|
||||
this.author = data.author
|
||||
? {
|
||||
name: data.author.name,
|
||||
url: data.author.url,
|
||||
iconURL: data.author.iconURL || data.author.icon_url,
|
||||
proxyIconURL: data.author.proxyIconURL || data.author.proxy_icon_url,
|
||||
}
|
||||
: null;
|
||||
|
||||
/**
|
||||
* @typedef {Object} MessageEmbedProvider
|
||||
@@ -154,10 +162,12 @@ class MessageEmbed {
|
||||
* The provider of this embed (if there is one)
|
||||
* @type {?MessageEmbedProvider}
|
||||
*/
|
||||
this.provider = data.provider ? {
|
||||
name: data.provider.name,
|
||||
url: data.provider.name,
|
||||
} : null;
|
||||
this.provider = data.provider
|
||||
? {
|
||||
name: data.provider.name,
|
||||
url: data.provider.name,
|
||||
}
|
||||
: null;
|
||||
|
||||
/**
|
||||
* @typedef {Object} MessageEmbedFooter
|
||||
@@ -170,11 +180,13 @@ class MessageEmbed {
|
||||
* The footer of this embed
|
||||
* @type {?MessageEmbedFooter}
|
||||
*/
|
||||
this.footer = data.footer ? {
|
||||
text: data.footer.text,
|
||||
iconURL: data.footer.iconURL || data.footer.icon_url,
|
||||
proxyIconURL: data.footer.proxyIconURL || data.footer.proxy_icon_url,
|
||||
} : null;
|
||||
this.footer = data.footer
|
||||
? {
|
||||
text: data.footer.text,
|
||||
iconURL: data.footer.iconURL || data.footer.icon_url,
|
||||
proxyIconURL: data.footer.proxyIconURL || data.footer.proxy_icon_url,
|
||||
}
|
||||
: null;
|
||||
|
||||
/**
|
||||
* The files of this embed
|
||||
@@ -210,9 +222,11 @@ class MessageEmbed {
|
||||
return (
|
||||
(this.title ? this.title.length : 0) +
|
||||
(this.description ? this.description.length : 0) +
|
||||
(this.fields.length >= 1 ? this.fields.reduce((prev, curr) =>
|
||||
prev + curr.name.length + curr.value.length, 0) : 0) +
|
||||
(this.footer ? this.footer.text.length : 0));
|
||||
(this.fields.length >= 1
|
||||
? this.fields.reduce((prev, curr) => prev + curr.name.length + curr.value.length, 0)
|
||||
: 0) +
|
||||
(this.footer ? this.footer.text.length : 0)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -371,15 +385,19 @@ class MessageEmbed {
|
||||
fields: this.fields,
|
||||
thumbnail: this.thumbnail,
|
||||
image: this.image,
|
||||
author: this.author ? {
|
||||
name: this.author.name,
|
||||
url: this.author.url,
|
||||
icon_url: this.author.iconURL,
|
||||
} : null,
|
||||
footer: this.footer ? {
|
||||
text: this.footer.text,
|
||||
icon_url: this.footer.iconURL,
|
||||
} : null,
|
||||
author: this.author
|
||||
? {
|
||||
name: this.author.name,
|
||||
url: this.author.url,
|
||||
icon_url: this.author.iconURL,
|
||||
}
|
||||
: null,
|
||||
footer: this.footer
|
||||
? {
|
||||
text: this.footer.text,
|
||||
icon_url: this.footer.iconURL,
|
||||
}
|
||||
: null,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -399,11 +417,11 @@ class MessageEmbed {
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {Object} EmbedFieldData
|
||||
* @property {StringResolvable} name The name of this field
|
||||
* @property {StringResolvable} value The value of this field
|
||||
* @property {boolean} [inline=false] If this field will be displayed inline
|
||||
*/
|
||||
* @typedef {Object} EmbedFieldData
|
||||
* @property {StringResolvable} name The name of this field
|
||||
* @property {StringResolvable} value The value of this field
|
||||
* @property {boolean} [inline] If this field will be displayed inline
|
||||
*/
|
||||
|
||||
/**
|
||||
* Normalizes field input and resolves strings.
|
||||
@@ -411,13 +429,15 @@ class MessageEmbed {
|
||||
* @returns {EmbedField[]}
|
||||
*/
|
||||
static normalizeFields(...fields) {
|
||||
return fields.flat(2).map(field =>
|
||||
this.normalizeField(
|
||||
field && field.name,
|
||||
field && field.value,
|
||||
field && typeof field.inline === 'boolean' ? field.inline : false,
|
||||
),
|
||||
);
|
||||
return fields
|
||||
.flat(2)
|
||||
.map(field =>
|
||||
this.normalizeField(
|
||||
field && field.name,
|
||||
field && field.value,
|
||||
field && typeof field.inline === 'boolean' ? field.inline : false,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
'use strict';
|
||||
|
||||
const Collection = require('../util/Collection');
|
||||
const Util = require('../util/Util');
|
||||
const GuildMember = require('./GuildMember');
|
||||
const Collection = require('../util/Collection');
|
||||
const { ChannelTypes } = require('../util/Constants');
|
||||
const Util = require('../util/Util');
|
||||
|
||||
/**
|
||||
* Keeps track of mentions in a {@link Message}.
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
'use strict';
|
||||
|
||||
const GuildEmoji = require('./GuildEmoji');
|
||||
const Util = require('../util/Util');
|
||||
const ReactionEmoji = require('./ReactionEmoji');
|
||||
const ReactionUserManager = require('../managers/ReactionUserManager');
|
||||
const Util = require('../util/Util');
|
||||
|
||||
/**
|
||||
* Represents a reaction to a message.
|
||||
@@ -59,7 +59,10 @@ class MessageReaction {
|
||||
* @returns {Promise<MessageReaction>}
|
||||
*/
|
||||
async remove() {
|
||||
await this.client.api.channels(this.message.channel.id).messages(this.message.id).reactions(this._emoji.identifier)
|
||||
await this.client.api
|
||||
.channels(this.message.channel.id)
|
||||
.messages(this.message.id)
|
||||
.reactions(this._emoji.identifier)
|
||||
.delete();
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
'use strict';
|
||||
|
||||
const Role = require('./Role');
|
||||
const { TypeError } = require('../errors');
|
||||
const Permissions = require('../util/Permissions');
|
||||
const Util = require('../util/Util');
|
||||
const { TypeError } = require('../errors');
|
||||
|
||||
/**
|
||||
* Represents a permission overwrite for a role or member in a guild channel.
|
||||
@@ -70,8 +70,12 @@ class PermissionOverwrites {
|
||||
update(options, reason) {
|
||||
const { allow, deny } = this.constructor.resolveOverwriteOptions(options, this);
|
||||
|
||||
return this.channel.client.api.channels(this.channel.id).permissions[this.id]
|
||||
.put({ data: { id: this.id, type: this.type, allow: allow.bitfield, deny: deny.bitfield }, reason })
|
||||
return this.channel.client.api
|
||||
.channels(this.channel.id)
|
||||
.permissions[this.id].put({
|
||||
data: { id: this.id, type: this.type, allow: allow.bitfield, deny: deny.bitfield },
|
||||
reason,
|
||||
})
|
||||
.then(() => this);
|
||||
}
|
||||
|
||||
@@ -81,9 +85,7 @@ class PermissionOverwrites {
|
||||
* @returns {Promise<PermissionOverwrites>}
|
||||
*/
|
||||
delete(reason) {
|
||||
return this.channel.client.api.channels[this.channel.id].permissions[this.id]
|
||||
.delete({ reason })
|
||||
.then(() => this);
|
||||
return this.channel.client.api.channels[this.channel.id].permissions[this.id].delete({ reason }).then(() => this);
|
||||
}
|
||||
|
||||
toJSON() {
|
||||
@@ -168,10 +170,7 @@ class PermissionOverwrites {
|
||||
static resolve(overwrite, guild) {
|
||||
if (overwrite instanceof this) return overwrite.toJSON();
|
||||
if (typeof overwrite.id === 'string' && ['role', 'member'].includes(overwrite.type)) {
|
||||
return { ...overwrite,
|
||||
allow: Permissions.resolve(overwrite.allow),
|
||||
deny: Permissions.resolve(overwrite.deny),
|
||||
};
|
||||
return { ...overwrite, allow: Permissions.resolve(overwrite.allow), deny: Permissions.resolve(overwrite.deny) };
|
||||
}
|
||||
|
||||
const userOrRole = guild.roles.resolve(overwrite.id) || guild.client.users.resolve(overwrite.id);
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
'use strict';
|
||||
|
||||
const Util = require('../util/Util');
|
||||
const Emoji = require('./Emoji');
|
||||
const ActivityFlags = require('../util/ActivityFlags');
|
||||
const { ActivityTypes } = require('../util/Constants');
|
||||
const Emoji = require('./Emoji');
|
||||
const Util = require('../util/Util');
|
||||
|
||||
/**
|
||||
* Activity sent in a message.
|
||||
@@ -121,14 +121,15 @@ class Presence {
|
||||
* @returns {boolean}
|
||||
*/
|
||||
equals(presence) {
|
||||
return this === presence || (
|
||||
presence &&
|
||||
this.status === presence.status &&
|
||||
this.activities.length === presence.activities.length &&
|
||||
this.activities.every((activity, index) => activity.equals(presence.activities[index])) &&
|
||||
this.clientStatus.web === presence.clientStatus.web &&
|
||||
this.clientStatus.mobile === presence.clientStatus.mobile &&
|
||||
this.clientStatus.desktop === presence.clientStatus.desktop
|
||||
return (
|
||||
this === presence ||
|
||||
(presence &&
|
||||
this.status === presence.status &&
|
||||
this.activities.length === presence.activities.length &&
|
||||
this.activities.every((activity, index) => activity.equals(presence.activities[index])) &&
|
||||
this.clientStatus.web === presence.clientStatus.web &&
|
||||
this.clientStatus.mobile === presence.clientStatus.mobile &&
|
||||
this.clientStatus.desktop === presence.clientStatus.desktop)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -186,10 +187,12 @@ class Activity {
|
||||
* @prop {?Date} start When the activity started
|
||||
* @prop {?Date} end When the activity will end
|
||||
*/
|
||||
this.timestamps = data.timestamps ? {
|
||||
start: data.timestamps.start ? new Date(Number(data.timestamps.start)) : null,
|
||||
end: data.timestamps.end ? new Date(Number(data.timestamps.end)) : null,
|
||||
} : null;
|
||||
this.timestamps = data.timestamps
|
||||
? {
|
||||
start: data.timestamps.start ? new Date(Number(data.timestamps.start)) : null,
|
||||
end: data.timestamps.end ? new Date(Number(data.timestamps.end)) : null,
|
||||
}
|
||||
: null;
|
||||
|
||||
/**
|
||||
* Party of the activity
|
||||
@@ -232,11 +235,9 @@ class Activity {
|
||||
* @returns {boolean}
|
||||
*/
|
||||
equals(activity) {
|
||||
return this === activity || (
|
||||
activity &&
|
||||
this.name === activity.name &&
|
||||
this.type === activity.type &&
|
||||
this.url === activity.url
|
||||
return (
|
||||
this === activity ||
|
||||
(activity && this.name === activity.name && this.type === activity.type && this.url === activity.url)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -303,8 +304,10 @@ class RichPresenceAssets {
|
||||
*/
|
||||
smallImageURL({ format, size } = {}) {
|
||||
if (!this.smallImage) return null;
|
||||
return this.activity.presence.client.rest.cdn
|
||||
.AppAsset(this.activity.applicationID, this.smallImage, { format, size });
|
||||
return this.activity.presence.client.rest.cdn.AppAsset(this.activity.applicationID, this.smallImage, {
|
||||
format,
|
||||
size,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -321,8 +324,10 @@ class RichPresenceAssets {
|
||||
} else if (/^twitch:/.test(this.largeImage)) {
|
||||
return `https://static-cdn.jtvnw.net/previews-ttv/live_user_${this.largeImage.slice(7)}.png`;
|
||||
}
|
||||
return this.activity.presence.client.rest.cdn
|
||||
.AppAsset(this.activity.applicationID, this.largeImage, { format, size });
|
||||
return this.activity.presence.client.rest.cdn.AppAsset(this.activity.applicationID, this.largeImage, {
|
||||
format,
|
||||
size,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -118,8 +118,7 @@ class ReactionCollector extends Collector {
|
||||
* @param {MessageReaction} reaction The reaction that was removed
|
||||
* @param {User} user The user that removed the reaction
|
||||
*/
|
||||
if (this.collected.has(ReactionCollector.key(reaction)) &&
|
||||
this.users.has(user.id)) {
|
||||
if (this.collected.has(ReactionCollector.key(reaction)) && this.users.has(user.id)) {
|
||||
this.emit('remove', reaction, user);
|
||||
}
|
||||
return reaction.count ? null : ReactionCollector.key(reaction);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
'use strict';
|
||||
|
||||
const Util = require('../util/Util');
|
||||
const Emoji = require('./Emoji');
|
||||
const Util = require('../util/Util');
|
||||
|
||||
/**
|
||||
* Represents a limited emoji set used for both custom and unicode emojis. Custom emojis
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
'use strict';
|
||||
|
||||
const Snowflake = require('../util/Snowflake');
|
||||
const Permissions = require('../util/Permissions');
|
||||
const Util = require('../util/Util');
|
||||
const Base = require('./Base');
|
||||
const { Error, TypeError } = require('../errors');
|
||||
const Permissions = require('../util/Permissions');
|
||||
const Snowflake = require('../util/Snowflake');
|
||||
const Util = require('../util/Util');
|
||||
|
||||
/**
|
||||
* Represents a role on Discord.
|
||||
@@ -180,25 +180,31 @@ class Role extends Base {
|
||||
if (typeof data.permissions !== 'undefined') data.permissions = Permissions.resolve(data.permissions);
|
||||
else data.permissions = this.permissions.bitfield;
|
||||
if (typeof data.position !== 'undefined') {
|
||||
await Util.setPosition(this, data.position, false, this.guild._sortedRoles(),
|
||||
this.client.api.guilds(this.guild.id).roles, reason)
|
||||
.then(updatedRoles => {
|
||||
this.client.actions.GuildRolesPositionUpdate.handle({
|
||||
guild_id: this.guild.id,
|
||||
roles: updatedRoles,
|
||||
});
|
||||
await Util.setPosition(
|
||||
this,
|
||||
data.position,
|
||||
false,
|
||||
this.guild._sortedRoles(),
|
||||
this.client.api.guilds(this.guild.id).roles,
|
||||
reason,
|
||||
).then(updatedRoles => {
|
||||
this.client.actions.GuildRolesPositionUpdate.handle({
|
||||
guild_id: this.guild.id,
|
||||
roles: updatedRoles,
|
||||
});
|
||||
});
|
||||
}
|
||||
return this.client.api.guilds[this.guild.id].roles[this.id].patch({
|
||||
data: {
|
||||
name: data.name || this.name,
|
||||
color: data.color !== null ? Util.resolveColor(data.color || this.color) : null,
|
||||
hoist: typeof data.hoist !== 'undefined' ? data.hoist : this.hoist,
|
||||
permissions: data.permissions,
|
||||
mentionable: typeof data.mentionable !== 'undefined' ? data.mentionable : this.mentionable,
|
||||
},
|
||||
reason,
|
||||
})
|
||||
return this.client.api.guilds[this.guild.id].roles[this.id]
|
||||
.patch({
|
||||
data: {
|
||||
name: data.name || this.name,
|
||||
color: data.color !== null ? Util.resolveColor(data.color || this.color) : null,
|
||||
hoist: typeof data.hoist !== 'undefined' ? data.hoist : this.hoist,
|
||||
permissions: data.permissions,
|
||||
mentionable: typeof data.mentionable !== 'undefined' ? data.mentionable : this.mentionable,
|
||||
},
|
||||
reason,
|
||||
})
|
||||
.then(role => {
|
||||
const clone = this._clone();
|
||||
clone._patch(role);
|
||||
@@ -312,15 +318,20 @@ class Role extends Base {
|
||||
* .catch(console.error);
|
||||
*/
|
||||
setPosition(position, { relative, reason } = {}) {
|
||||
return Util.setPosition(this, position, relative,
|
||||
this.guild._sortedRoles(), this.client.api.guilds(this.guild.id).roles, reason)
|
||||
.then(updatedRoles => {
|
||||
this.client.actions.GuildRolesPositionUpdate.handle({
|
||||
guild_id: this.guild.id,
|
||||
roles: updatedRoles,
|
||||
});
|
||||
return this;
|
||||
return Util.setPosition(
|
||||
this,
|
||||
position,
|
||||
relative,
|
||||
this.guild._sortedRoles(),
|
||||
this.client.api.guilds(this.guild.id).roles,
|
||||
reason,
|
||||
).then(updatedRoles => {
|
||||
this.client.actions.GuildRolesPositionUpdate.handle({
|
||||
guild_id: this.guild.id,
|
||||
roles: updatedRoles,
|
||||
});
|
||||
return this;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -334,11 +345,10 @@ class Role extends Base {
|
||||
* .catch(console.error);
|
||||
*/
|
||||
delete(reason) {
|
||||
return this.client.api.guilds[this.guild.id].roles[this.id].delete({ reason })
|
||||
.then(() => {
|
||||
this.client.actions.GuildRoleDelete.handle({ guild_id: this.guild.id, role_id: this.id });
|
||||
return this;
|
||||
});
|
||||
return this.client.api.guilds[this.guild.id].roles[this.id].delete({ reason }).then(() => {
|
||||
this.client.actions.GuildRoleDelete.handle({ guild_id: this.guild.id, role_id: this.id });
|
||||
return this;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -349,14 +359,16 @@ class Role extends Base {
|
||||
* @returns {boolean}
|
||||
*/
|
||||
equals(role) {
|
||||
return role &&
|
||||
return (
|
||||
role &&
|
||||
this.id === role.id &&
|
||||
this.name === role.name &&
|
||||
this.color === role.color &&
|
||||
this.hoist === role.hoist &&
|
||||
this.position === role.position &&
|
||||
this.permissions.bitfield === role.permissions.bitfield &&
|
||||
this.managed === role.managed;
|
||||
this.managed === role.managed
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user