mirror of
https://github.com/discordjs/discord.js.git
synced 2026-03-09 16:13:31 +01:00
Merge branch 'master' into indev-prism
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
# Contributing
|
||||
If you wish to contribute to the discord.js codebase or documentation, feel free to fork the repository and submit a
|
||||
pull request. We use ESLint to enforce a consistent coding style, so having that set up in your editor of choice
|
||||
is a great boon to your coding process.
|
||||
is a great boon to your development process.
|
||||
|
||||
## Setup
|
||||
To get ready to work on the codebase, please do the following:
|
||||
|
||||
23
README.md
23
README.md
@@ -23,11 +23,11 @@ discord.js is a powerful node.js module that allows you to interact with the
|
||||
- Object-oriented
|
||||
- Predictable abstractions
|
||||
- Performant
|
||||
- Nearly 100% coverage of the Discord API
|
||||
- 100% coverage of the Discord API
|
||||
|
||||
## Installation
|
||||
**Node.js 6.0.0 or newer is required.**
|
||||
Ignore any warnings about unmet peer dependencies - all peer dependencies are optional.
|
||||
Ignore any warnings about unmet peer dependencies, as they're optional.
|
||||
|
||||
Without voice support: `npm install discord.js --save`
|
||||
With voice support ([node-opus](https://www.npmjs.com/package/node-opus)): `npm install discord.js node-opus --save`
|
||||
@@ -39,10 +39,13 @@ Using opusscript is only recommended for development environments where node-opu
|
||||
For production bots, using node-opus should be considered a necessity, especially if they're going to be running on multiple servers.
|
||||
|
||||
### Optional packages
|
||||
- [uws](https://www.npmjs.com/package/uws) for a much faster WebSocket connection (`npm install uws --save`)
|
||||
- [bufferutil](https://www.npmjs.com/package/bufferutil) to greatly speed up the `ws` WebSocket connection (`npm install bufferutil --save`)
|
||||
- [erlpack](https://github.com/hammerandchisel/erlpack) for significantly faster WebSocket data (de)serialisation (`npm install hammerandchisel/erlpack --save`)
|
||||
- [uws](https://www.npmjs.com/package/uws) for a much faster WebSocket connection (`npm install uws --save`)
|
||||
**Note:** This package does not handle disconnects entirely correctly, which causes automatic reconnection to Discord to not function.
|
||||
If you use this package, it may be wise to destroy + recreate the client entirely or restart the process upon disconnect.
|
||||
|
||||
## Example Usage
|
||||
## Example usage
|
||||
```js
|
||||
const Discord = require('discord.js');
|
||||
const client = new Discord.Client();
|
||||
@@ -62,19 +65,11 @@ client.login('your token');
|
||||
|
||||
A bot template using discord.js can be generated using [generator-discordbot](https://www.npmjs.com/package/generator-discordbot).
|
||||
|
||||
## Web distributions
|
||||
Web builds of discord.js that are fully capable of running in browsers are available [here](https://github.com/hydrabolt/discord.js/tree/webpack).
|
||||
These are built using [Webpack 2](https://webpack.js.org/). The API is identical, but rather than using `require('discord.js')`,
|
||||
the entire `Discord` object is available as a global (on the `window` object).
|
||||
The ShardingManager and any voice-related functionality is unavailable in these builds.
|
||||
|
||||
## Links
|
||||
* [Website](https://discord.js.org/)
|
||||
* [Website](https://discord.js.org/) ([source](https://github.com/hydrabolt/discord.js-site))
|
||||
* [Documentation](https://discord.js.org/#/docs)
|
||||
* [Discord.js server](https://discord.gg/bRCvFy9)
|
||||
* [Discord API server](https://discord.gg/rV4BwdK)
|
||||
* [Documentation](https://discord.js.org/#/docs)
|
||||
* [Legacy (v8) documentation](http://discordjs.readthedocs.io/en/8.2.0/docs_client.html)
|
||||
* [Examples](https://github.com/hydrabolt/discord.js/tree/master/docs/examples)
|
||||
* [GitHub](https://github.com/hydrabolt/discord.js)
|
||||
* [NPM](https://www.npmjs.com/package/discord.js)
|
||||
* [Related libraries](https://discordapi.com/unofficial/libs.html) (see also [discord-rpc](https://www.npmjs.com/package/discord-rpc))
|
||||
|
||||
@@ -5,15 +5,15 @@ set -e
|
||||
|
||||
function tests {
|
||||
npm run lint
|
||||
npm run test-docs
|
||||
VERSIONED=false npm run web-dist
|
||||
npm run docs:test
|
||||
VERSIONED=false npm run webpack
|
||||
exit 0
|
||||
}
|
||||
|
||||
function build {
|
||||
npm run lint
|
||||
npm run docs
|
||||
VERSIONED=false npm run web-dist
|
||||
VERSIONED=false npm run webpack
|
||||
}
|
||||
|
||||
# For revert branches, do nothing
|
||||
|
||||
@@ -6,19 +6,19 @@
|
||||
const Discord = require('discord.js');
|
||||
|
||||
// create an instance of a Discord Client, and call it bot
|
||||
const bot = new Discord.Client();
|
||||
const client = new Discord.Client();
|
||||
|
||||
// the token of your bot - https://discordapp.com/developers/applications/me
|
||||
const token = 'your bot token here';
|
||||
|
||||
// the ready event is vital, it means that your bot will only start reacting to information
|
||||
// from Discord _after_ ready is emitted.
|
||||
bot.on('ready', () => {
|
||||
client.on('ready', () => {
|
||||
console.log('I am ready!');
|
||||
});
|
||||
|
||||
// create an event listener for messages
|
||||
bot.on('message', message => {
|
||||
client.on('message', message => {
|
||||
// if the message is "what is my avatar",
|
||||
if (message.content === 'what is my avatar') {
|
||||
// send the user's avatar URL
|
||||
@@ -27,4 +27,4 @@ bot.on('message', message => {
|
||||
});
|
||||
|
||||
// log our bot in
|
||||
bot.login(token);
|
||||
client.login(token);
|
||||
|
||||
@@ -5,20 +5,20 @@
|
||||
// import the discord.js module
|
||||
const Discord = require('discord.js');
|
||||
|
||||
// create an instance of a Discord Client, and call it bot
|
||||
const bot = new Discord.Client();
|
||||
// create an instance of a Discord Client
|
||||
const client = new Discord.Client();
|
||||
|
||||
// the token of your bot - https://discordapp.com/developers/applications/me
|
||||
const token = 'your bot token here';
|
||||
|
||||
// the ready event is vital, it means that your bot will only start reacting to information
|
||||
// from Discord _after_ ready is emitted.
|
||||
bot.on('ready', () => {
|
||||
client.on('ready', () => {
|
||||
console.log('I am ready!');
|
||||
});
|
||||
|
||||
// create an event listener for messages
|
||||
bot.on('message', message => {
|
||||
client.on('message', message => {
|
||||
// if the message is "ping",
|
||||
if (message.content === 'ping') {
|
||||
// send "pong" to the same channel.
|
||||
@@ -27,4 +27,4 @@ bot.on('message', message => {
|
||||
});
|
||||
|
||||
// log our bot in
|
||||
bot.login(token);
|
||||
client.login(token);
|
||||
|
||||
@@ -27,11 +27,11 @@ discord.js is a powerful node.js module that allows you to interact with the
|
||||
- Object-oriented
|
||||
- Predictable abstractions
|
||||
- Performant
|
||||
- Nearly 100% coverage of the Discord API
|
||||
- 100% coverage of the Discord API
|
||||
|
||||
## Installation
|
||||
**Node.js 6.0.0 or newer is required.**
|
||||
Ignore any warnings about unmet peer dependencies - all of them are optional.
|
||||
Ignore any warnings about unmet peer dependencies, as they're optional.
|
||||
|
||||
Without voice support: `npm install discord.js --save`
|
||||
With voice support ([node-opus](https://www.npmjs.com/package/node-opus)): `npm install discord.js node-opus --save`
|
||||
@@ -43,29 +43,45 @@ Using opusscript is only recommended for development environments where node-opu
|
||||
For production bots, using node-opus should be considered a necessity, especially if they're going to be running on multiple servers.
|
||||
|
||||
### Optional packages
|
||||
- [uws](https://www.npmjs.com/package/uws) for much a much faster WebSocket connection (`npm install uws --save`)
|
||||
- [bufferutil](https://www.npmjs.com/package/bufferutil) to greatly speed up the `ws` WebSocket connection (`npm install bufferutil --save`)
|
||||
- [erlpack](https://github.com/hammerandchisel/erlpack) for significantly faster WebSocket data (de)serialisation (`npm install hammerandchisel/erlpack --save`)
|
||||
- [uws](https://www.npmjs.com/package/uws) for a much faster WebSocket connection (`npm install uws --save`)
|
||||
**Note:** This package does not handle disconnects entirely correctly, which causes automatic reconnection to Discord to not function.
|
||||
If you use this package, it may be wise to destroy + recreate the client entirely or restart the process upon disconnect.
|
||||
|
||||
## Web distributions
|
||||
Web builds of discord.js that are fully capable of running in browsers are available [here](https://github.com/hydrabolt/discord.js/tree/webpack).
|
||||
These are built by [Webpack 2](https://webpack.js.org/). The API is identical, but rather than using `require('discord.js')`,
|
||||
the entire `Discord` object is available as a global (on the `window` object).
|
||||
The ShardingManager and any voice-related functionality is unavailable in these builds.
|
||||
## Example usage
|
||||
```js
|
||||
const Discord = require('discord.js');
|
||||
const client = new Discord.Client();
|
||||
|
||||
## Guides
|
||||
* [LuckyEvie's general guide](https://eslachance.gitbooks.io/discord-js-bot-guide/content/)
|
||||
* [York's v9 upgrade guide](https://yorkaargh.wordpress.com/2016/09/03/updating-discord-js-bots/)
|
||||
client.on('ready', () => {
|
||||
console.log('I am ready!');
|
||||
});
|
||||
|
||||
client.on('message', message => {
|
||||
if (message.content === 'ping') {
|
||||
message.reply('pong');
|
||||
}
|
||||
});
|
||||
|
||||
client.login('your token');
|
||||
```
|
||||
|
||||
A bot template using discord.js can be generated using [generator-discordbot](https://www.npmjs.com/package/generator-discordbot).
|
||||
|
||||
## Links
|
||||
* [Website](https://discord.js.org/)
|
||||
* [Website](https://discord.js.org/) ([source](https://github.com/hydrabolt/discord.js-site))
|
||||
* [Documentation](https://discord.js.org/#/docs)
|
||||
* [Discord.js server](https://discord.gg/bRCvFy9)
|
||||
* [Discord API server](https://discord.gg/rV4BwdK)
|
||||
* [Documentation](https://discord.js.org/#/docs)
|
||||
* [Legacy (v8) documentation](http://discordjs.readthedocs.io/en/8.2.0/docs_client.html)
|
||||
* [Examples](https://github.com/hydrabolt/discord.js/tree/master/docs/examples)
|
||||
* [GitHub](https://github.com/hydrabolt/discord.js)
|
||||
* [NPM](https://www.npmjs.com/package/discord.js)
|
||||
* [Related libraries](https://discordapi.com/unofficial/libs.html)
|
||||
* [Related libraries](https://discordapi.com/unofficial/libs.html) (see also [discord-rpc](https://www.npmjs.com/package/discord-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](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
|
||||
|
||||
@@ -10,6 +10,8 @@
|
||||
files:
|
||||
- name: Voice
|
||||
path: voice.md
|
||||
- name: Web builds
|
||||
path: web.md
|
||||
- name: Examples
|
||||
files:
|
||||
- name: Ping
|
||||
|
||||
38
docs/topics/web.md
Normal file
38
docs/topics/web.md
Normal file
@@ -0,0 +1,38 @@
|
||||
# Web builds
|
||||
In addition to your usual Node applications, discord.js has special distributions available that are capable of running in web browsers.
|
||||
This is useful for client-side web apps that need to interact with the Discord API.
|
||||
[Webpack 2](https://webpack.js.org/) is used to build these.
|
||||
|
||||
## Usage
|
||||
You can obtain your desired version of discord.js' web build from the [webpack branch](https://github.com/hydrabolt/discord.js/tree/webpack) of the GitHub repository.
|
||||
There is a file for each branch and version of the library, and the ones ending in `.min.js` are minified to substantially reduce the size of the source code.
|
||||
|
||||
Include the file on the page just as you would any other JS library, like so:
|
||||
```html
|
||||
<script type="text/javascript" src="discord.VERSION.min.js"></script>
|
||||
```
|
||||
|
||||
Rather than importing discord.js with `require('discord.js')`, the entire `Discord` object is available as a global (on the `window`) object.
|
||||
The usage of the API isn't any different from using it in Node.js.
|
||||
|
||||
## Restrictions
|
||||
- Any voice-related functionality is unavailable, as there is currently no audio encoding/decoding capabilities without external native libraries,
|
||||
which web browsers do not support.
|
||||
- The ShardingManager cannot be used, since it relies on being able to spawn child processes for shards.
|
||||
- None of the optional packages are usable, since they're native libraries.
|
||||
|
||||
## Example
|
||||
```html
|
||||
<script type="text/javascript" src="discord.11.0.0.min.js"></script>
|
||||
<script type="text/javascript">
|
||||
const client = new Discord.Client();
|
||||
|
||||
client.on('message', msg => {
|
||||
const guildTag = msg.channel.type === 'text' ? `[${msg.guild.name}]` : '[DM]';
|
||||
const channelTag = msg.channel.type === 'text' ? `[#${msg.channel.name}]` : '';
|
||||
console.log(`${guildTag}${channelTag} ${msg.author.username}#${msg.author.discriminator}: ${msg.content}`);
|
||||
});
|
||||
|
||||
client.login('some crazy token');
|
||||
</script>
|
||||
```
|
||||
14
package.json
14
package.json
@@ -5,11 +5,12 @@
|
||||
"main": "./src/index",
|
||||
"types": "./typings/index.d.ts",
|
||||
"scripts": {
|
||||
"test": "eslint src && docgen --source src --custom docs/index.yml",
|
||||
"test": "npm run lint && npm run docs:test",
|
||||
"docs": "docgen --source src --custom docs/index.yml --output docs/docs.json",
|
||||
"test-docs": "docgen --source src --custom docs/index.yml",
|
||||
"docs:test": "docgen --source src --custom docs/index.yml",
|
||||
"lint": "eslint src",
|
||||
"web-dist": "node ./node_modules/parallel-webpack/bin/run.js"
|
||||
"lint:fix": "eslint --fix src",
|
||||
"webpack": "parallel-webpack"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -37,12 +38,13 @@
|
||||
"prism-media": "hydrabolt/prism-media#master",
|
||||
"superagent": "^3.3.0",
|
||||
"tweetnacl": "^0.14.0",
|
||||
"ws": "^1.1.1"
|
||||
"ws": "^2.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"bufferutil": "^1.3.0",
|
||||
"erlpack": "hammerandchisel/erlpack",
|
||||
"node-opus": "^0.2.0",
|
||||
"opusscript": "^0.0.1",
|
||||
"opusscript": "^0.0.2",
|
||||
"uws": "^0.12.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -50,7 +52,7 @@
|
||||
"eslint": "^3.13.0",
|
||||
"parallel-webpack": "^1.6.0",
|
||||
"uglify-js": "mishoo/UglifyJS2#harmony",
|
||||
"webpack": "2.2.0-rc.4"
|
||||
"webpack": "^2.2.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.0.0"
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
const os = require('os');
|
||||
const EventEmitter = require('events').EventEmitter;
|
||||
const mergeDefault = require('../util/MergeDefault');
|
||||
const Constants = require('../util/Constants');
|
||||
@@ -223,7 +224,7 @@ class Client extends EventEmitter {
|
||||
* @readonly
|
||||
*/
|
||||
get browser() {
|
||||
return typeof window !== 'undefined';
|
||||
return os.platform() === 'browser';
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -123,6 +123,7 @@ class ClientDataManager {
|
||||
const oldEmoji = cloneObject(currentEmoji);
|
||||
currentEmoji.setup(newData);
|
||||
this.client.emit(Constants.Events.GUILD_EMOJI_UPDATE, oldEmoji, currentEmoji);
|
||||
return currentEmoji;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,10 @@ const Action = require('./Action');
|
||||
|
||||
class GuildEmojiUpdateAction extends Action {
|
||||
handle(oldEmoji, newEmoji) {
|
||||
this.client.dataManager.updateEmoji(oldEmoji, newEmoji);
|
||||
const emoji = this.client.dataManager.updateEmoji(oldEmoji, newEmoji);
|
||||
return {
|
||||
emoji,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -14,17 +14,32 @@ class MessageCreateAction extends Action {
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
messages[i] = channel._cacheMessage(new Message(channel, data[i], client));
|
||||
}
|
||||
channel.lastMessageID = messages[messages.length - 1].id;
|
||||
if (user) user.lastMessageID = messages[messages.length - 1].id;
|
||||
if (member) member.lastMessageID = messages[messages.length - 1].id;
|
||||
const lastMessage = messages[messages.length - 1];
|
||||
channel.lastMessageID = lastMessage.id;
|
||||
channel.lastMessage = lastMessage;
|
||||
if (user) {
|
||||
user.lastMessageID = lastMessage.id;
|
||||
user.lastMessage = lastMessage;
|
||||
}
|
||||
if (member) {
|
||||
member.lastMessageID = lastMessage.id;
|
||||
member.lastMessage = lastMessage;
|
||||
}
|
||||
return {
|
||||
messages,
|
||||
};
|
||||
} else {
|
||||
const message = channel._cacheMessage(new Message(channel, data, client));
|
||||
channel.lastMessageID = data.id;
|
||||
if (user) user.lastMessageID = data.id;
|
||||
if (member) member.lastMessageID = data.id;
|
||||
channel.lastMessage = message;
|
||||
if (user) {
|
||||
user.lastMessageID = data.id;
|
||||
user.lastMessage = message;
|
||||
}
|
||||
if (member) {
|
||||
member.lastMessageID = data.id;
|
||||
member.lastMessage = message;
|
||||
}
|
||||
return {
|
||||
message,
|
||||
};
|
||||
|
||||
@@ -97,7 +97,7 @@ class RESTMethods {
|
||||
const options = index === list.length ? { tts, embed } : { tts };
|
||||
chan.send(list[index], options, index === list.length ? file : null).then((message) => {
|
||||
messages.push(message);
|
||||
if (index >= list.length) return resolve(messages);
|
||||
if (index >= list.length - 1) return resolve(messages);
|
||||
return sendChunk(list, ++index);
|
||||
});
|
||||
}(content, 0));
|
||||
@@ -165,8 +165,9 @@ class RESTMethods {
|
||||
|
||||
search(target, options) {
|
||||
options = transformSearchOptions(options, this.client);
|
||||
for (const key in options) if (options[key] === undefined) delete options[key];
|
||||
|
||||
const queryString = querystring.stringify(options);
|
||||
const queryString = (querystring.stringify(options).match(/[^=&?]+=[^=&?]+/g) || []).join('&');
|
||||
|
||||
let type;
|
||||
if (target instanceof Channel) {
|
||||
@@ -323,8 +324,9 @@ class RESTMethods {
|
||||
);
|
||||
}
|
||||
|
||||
createGuildRole(guild) {
|
||||
return this.rest.makeRequest('post', Constants.Endpoints.guildRoles(guild.id), true).then(role =>
|
||||
createGuildRole(guild, data) {
|
||||
if (data.color) data.color = this.client.resolver.resolveColor(data.color);
|
||||
return this.rest.makeRequest('post', Constants.Endpoints.guildRoles(guild.id), true, data).then(role =>
|
||||
this.client.actions.GuildRoleCreate.handle({
|
||||
guild_id: guild.id,
|
||||
role,
|
||||
@@ -557,14 +559,24 @@ class RESTMethods {
|
||||
.then(data => data.pruned);
|
||||
}
|
||||
|
||||
createEmoji(guild, image, name) {
|
||||
return this.rest.makeRequest('post', `${Constants.Endpoints.guildEmojis(guild.id)}`, true, { name, image })
|
||||
.then(data => this.client.actions.EmojiCreate.handle(data, guild).emoji);
|
||||
createEmoji(guild, image, name, roles) {
|
||||
const data = { image, name };
|
||||
if (roles) data.roles = roles.map(r => r.id ? r.id : r);
|
||||
return this.rest.makeRequest('post', `${Constants.Endpoints.guildEmojis(guild.id)}`, true, data)
|
||||
.then(emoji => this.client.actions.GuildEmojiCreate.handle(guild, emoji).emoji);
|
||||
}
|
||||
|
||||
updateEmoji(emoji, _data) {
|
||||
const data = {};
|
||||
if (_data.name) data.name = _data.name;
|
||||
if (_data.roles) data.roles = _data.roles.map(r => r.id ? r.id : r);
|
||||
return this.rest.makeRequest('patch', Constants.Endpoints.guildEmoji(emoji.guild.id, emoji.id), true, data)
|
||||
.then(newEmoji => this.client.actions.GuildEmojiUpdate.handle(emoji, newEmoji).emoji);
|
||||
}
|
||||
|
||||
deleteEmoji(emoji) {
|
||||
return this.rest.makeRequest('delete', `${Constants.Endpoints.guildEmojis(emoji.guild.id)}/${emoji.id}`, true)
|
||||
.then(() => this.client.actions.EmojiDelete.handle(emoji).data);
|
||||
.then(() => this.client.actions.GuildEmojiDelete.handle(emoji).data);
|
||||
}
|
||||
|
||||
getWebhook(id, token) {
|
||||
@@ -693,7 +705,7 @@ class RESTMethods {
|
||||
removeMessageReaction(message, emoji, user) {
|
||||
let endpoint = Constants.Endpoints.selfMessageReaction(message.channel.id, message.id, emoji);
|
||||
if (user !== this.client.user.id) {
|
||||
endpoint = Constants.Endpoints.userMessageReaction(message.channel.id, message.id, emoji, null, user.id);
|
||||
endpoint = Constants.Endpoints.userMessageReaction(message.channel.id, message.id, emoji, null, user);
|
||||
}
|
||||
return this.rest.makeRequest('delete', endpoint, true).then(() =>
|
||||
this.client.actions.MessageReactionRemove.handle({
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
const browser = typeof window !== 'undefined';
|
||||
const browser = require('os').platform() === 'browser';
|
||||
const EventEmitter = require('events').EventEmitter;
|
||||
const Constants = require('../../util/Constants');
|
||||
const convertArrayBuffer = require('../../util/ConvertArrayBuffer');
|
||||
@@ -155,7 +155,7 @@ class WebSocketManager extends EventEmitter {
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.ws.close(1000);
|
||||
if (this.ws) this.ws.close(1000);
|
||||
this._queue = [];
|
||||
this.status = Constants.Status.IDLE;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// ##untested##
|
||||
|
||||
const AbstractHandler = require('./AbstractHandler');
|
||||
const Constants = require('../../../../util/Constants');
|
||||
// uncomment in v12
|
||||
// const Collection = require('../../../../util/Collection');
|
||||
|
||||
class GuildMembersChunkHandler extends AbstractHandler {
|
||||
handle(packet) {
|
||||
@@ -10,10 +10,14 @@ class GuildMembersChunkHandler extends AbstractHandler {
|
||||
const guild = client.guilds.get(data.guild_id);
|
||||
if (!guild) return;
|
||||
|
||||
// uncomment in v12
|
||||
// const members = new Collection();
|
||||
//
|
||||
// for (const member of data.members) members.set(member.id, guild._addMember(member, false));
|
||||
|
||||
const members = data.members.map(member => guild._addMember(member, false));
|
||||
|
||||
guild._checkChunks();
|
||||
client.emit(Constants.Events.GUILD_MEMBERS_CHUNK, members);
|
||||
client.emit(Constants.Events.GUILD_MEMBERS_CHUNK, members, guild);
|
||||
|
||||
client.ws.lastHeartbeatAck = true;
|
||||
}
|
||||
@@ -22,7 +26,8 @@ class GuildMembersChunkHandler extends AbstractHandler {
|
||||
/**
|
||||
* Emitted whenever a chunk of guild members is received (all members come from the same guild)
|
||||
* @event Client#guildMembersChunk
|
||||
* @param {GuildMember[]} members The members in the chunk
|
||||
* @param {Collection<GuildMember>} members The members in the chunk
|
||||
* @param {Guild} guild The guild related to the member chunk
|
||||
*/
|
||||
|
||||
module.exports = GuildMembersChunkHandler;
|
||||
|
||||
@@ -46,4 +46,4 @@ module.exports = {
|
||||
Constants: require('./util/Constants'),
|
||||
};
|
||||
|
||||
if (typeof window !== 'undefined') window.Discord = module.exports; // eslint-disable-line no-undef
|
||||
if (require('os').platform() === 'browser') window.Discord = module.exports; // eslint-disable-line no-undef
|
||||
|
||||
@@ -101,6 +101,27 @@ class Emoji {
|
||||
return encodeURIComponent(this.name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Data for editing an emoji
|
||||
* @typedef {Object} EmojiEditData
|
||||
* @property {string} [name] The name of the emoji
|
||||
* @property {Collection<string, Role>|Array<string|Role>} [roles] Roles to restrict emoji to
|
||||
*/
|
||||
|
||||
/**
|
||||
* Edits the emoji
|
||||
* @param {EmojiEditData} data The new data for the emoji
|
||||
* @returns {Promise<Emoji>}
|
||||
* @example
|
||||
* // edit a emoji
|
||||
* emoji.edit({name: 'newemoji'})
|
||||
* .then(e => console.log(`Edited emoji ${e}`))
|
||||
* .catch(console.error);
|
||||
*/
|
||||
edit(data) {
|
||||
return this.client.rest.methods.updateEmoji(this, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* When concatenated with a string, this automatically returns the emoji mention rather than the object.
|
||||
* @returns {string}
|
||||
|
||||
@@ -337,7 +337,6 @@ class Guild {
|
||||
* @returns {Promise<GuildMember>}
|
||||
*/
|
||||
fetchMember(user, cache = true) {
|
||||
if (this._fetchWaiter) return Promise.reject(new Error('Already fetching guild members.'));
|
||||
user = this.client.resolver.resolveUser(user);
|
||||
if (!user) return Promise.reject(new Error('User is not cached. Use Client.fetchUser first.'));
|
||||
if (this.members.has(user.id)) return Promise.resolve(this.members.get(user.id));
|
||||
@@ -347,26 +346,37 @@ class Guild {
|
||||
/**
|
||||
* Fetches all the members in the guild, even if they are offline. If the guild has less than 250 members,
|
||||
* this should not be necessary.
|
||||
* @param {string} [query=''] An optional query to provide when fetching members
|
||||
* @param {string} [query=''] Limit fetch to members with similar usernames
|
||||
* @param {number} [limit=0] Maximum number of members to request
|
||||
* @returns {Promise<Guild>}
|
||||
*/
|
||||
fetchMembers(query = '') {
|
||||
fetchMembers(query = '', limit = 0) {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (this._fetchWaiter) throw new Error(`Already fetching guild members in ${this.id}.`);
|
||||
if (this.memberCount === this.members.size) {
|
||||
// uncomment in v12
|
||||
// resolve(this.members)
|
||||
resolve(this);
|
||||
return;
|
||||
}
|
||||
this._fetchWaiter = resolve;
|
||||
this.client.ws.send({
|
||||
op: Constants.OPCodes.REQUEST_GUILD_MEMBERS,
|
||||
d: {
|
||||
guild_id: this.id,
|
||||
query,
|
||||
limit: 0,
|
||||
limit,
|
||||
},
|
||||
});
|
||||
this._checkChunks();
|
||||
const handler = (members, guild) => {
|
||||
if (guild.id !== this.id) return;
|
||||
if (this.memberCount === this.members.size || members.length < 1000) {
|
||||
this.client.removeListener(Constants.Events.GUILD_MEMBERS_CHUNK, handler);
|
||||
// uncomment in v12
|
||||
// resolve(this.members)
|
||||
resolve(this);
|
||||
return;
|
||||
}
|
||||
};
|
||||
this.client.on(Constants.Events.GUILD_MEMBERS_CHUNK, handler);
|
||||
this.client.setTimeout(() => reject(new Error('Members didn\'t arrive in time.')), 120 * 1000);
|
||||
});
|
||||
}
|
||||
@@ -608,7 +618,7 @@ class Guild {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new role in the guild, and optionally updates it with the given information.
|
||||
* Creates a new role in the guild with given information
|
||||
* @param {RoleData} [data] The data to update the role with
|
||||
* @returns {Promise<Role>}
|
||||
* @example
|
||||
@@ -618,14 +628,15 @@ class Guild {
|
||||
* .catch(console.error);
|
||||
* @example
|
||||
* // create a new role with data
|
||||
* guild.createRole({ name: 'Super Cool People' })
|
||||
* .then(role => console.log(`Created role ${role}`))
|
||||
* .catch(console.error)
|
||||
* guild.createRole({
|
||||
* name: 'Super Cool People',
|
||||
* color: 'BLUE',
|
||||
* })
|
||||
* .then(role => console.log(`Created role ${role}`))
|
||||
* .catch(console.error)
|
||||
*/
|
||||
createRole(data) {
|
||||
const create = this.client.rest.methods.createGuildRole(this);
|
||||
if (!data) return create;
|
||||
return create.then(role => role.edit(data));
|
||||
return this.client.rest.methods.createGuildRole(this, data);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -671,6 +682,7 @@ class Guild {
|
||||
* Creates a new custom emoji in the guild.
|
||||
* @param {BufferResolvable|Base64Resolvable} attachment The image for the emoji.
|
||||
* @param {string} name The name for the emoji.
|
||||
* @param {Collection<Role>|Role[]} [roles] Roles to limit the emoji to
|
||||
* @returns {Promise<Emoji>} The created emoji.
|
||||
* @example
|
||||
* // create a new emoji from a url
|
||||
@@ -683,13 +695,13 @@ class Guild {
|
||||
* .then(emoji => console.log(`Created new emoji with name ${emoji.name}!`))
|
||||
* .catch(console.error);
|
||||
*/
|
||||
createEmoji(attachment, name) {
|
||||
createEmoji(attachment, name, roles) {
|
||||
return new Promise(resolve => {
|
||||
if (typeof attachment === 'string' && attachment.startsWith('data:')) {
|
||||
resolve(this.client.rest.methods.createEmoji(this, attachment, name));
|
||||
resolve(this.client.rest.methods.createEmoji(this, attachment, name, roles));
|
||||
} else {
|
||||
this.client.resolver.resolveBuffer(attachment).then(data =>
|
||||
resolve(this.client.rest.methods.createEmoji(this, data, name))
|
||||
resolve(this.client.rest.methods.createEmoji(this, data, name, roles))
|
||||
);
|
||||
}
|
||||
});
|
||||
@@ -811,7 +823,6 @@ class Guild {
|
||||
this.client.emit(Constants.Events.GUILD_MEMBER_ADD, member);
|
||||
}
|
||||
|
||||
this._checkChunks();
|
||||
return member;
|
||||
}
|
||||
|
||||
@@ -841,7 +852,6 @@ class Guild {
|
||||
|
||||
_removeMember(guildMember) {
|
||||
this.members.delete(guildMember.id);
|
||||
this._checkChunks();
|
||||
}
|
||||
|
||||
_memberSpeakUpdate(user, speaking) {
|
||||
@@ -865,15 +875,6 @@ class Guild {
|
||||
}
|
||||
this.presences.set(id, new Presence(presence));
|
||||
}
|
||||
|
||||
_checkChunks() {
|
||||
if (this._fetchWaiter) {
|
||||
if (this.members.size === this.memberCount) {
|
||||
this._fetchWaiter(this);
|
||||
this._fetchWaiter = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Guild;
|
||||
|
||||
@@ -281,6 +281,16 @@ class GuildChannel extends Channel {
|
||||
return equal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the channel is deletable by the client user.
|
||||
* @type {boolean}
|
||||
* @readonly
|
||||
*/
|
||||
get deletable() {
|
||||
return this.id !== this.guild.id &&
|
||||
this.permissionsFor(this.client.user).hasPermission(Constants.PermissionFlags.MANAGE_CHANNELS);
|
||||
}
|
||||
|
||||
/**
|
||||
* When concatenated with a string, this automatically returns the channel's mention instead of the Channel object.
|
||||
* @returns {string}
|
||||
|
||||
@@ -39,6 +39,12 @@ class GuildMember {
|
||||
* @type {?Snowflake}
|
||||
*/
|
||||
this.lastMessageID = null;
|
||||
|
||||
/**
|
||||
* The Message object of the last message sent by the member in their guild, if one was sent.
|
||||
* @type {?Message}
|
||||
*/
|
||||
this.lastMessage = null;
|
||||
}
|
||||
|
||||
setup(data) {
|
||||
@@ -389,12 +395,20 @@ class GuildMember {
|
||||
return this.edit({ nick });
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a DM channel between the client and the member
|
||||
* @returns {Promise<DMChannel>}
|
||||
*/
|
||||
createDM() {
|
||||
return this.user.createDM();
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes any DMs with this guild member
|
||||
* @returns {Promise<DMChannel>}
|
||||
*/
|
||||
deleteDM() {
|
||||
return this.client.rest.methods.deleteChannel(this);
|
||||
return this.user.deleteDM();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -197,12 +197,13 @@ class Message {
|
||||
if (data.type === 6) this.system = true;
|
||||
}
|
||||
if (data.attachments) {
|
||||
this.attachments = new Collection();
|
||||
this.attachments.clear();
|
||||
for (const attachment of data.attachments) {
|
||||
this.attachments.set(attachment.id, new Attachment(this, attachment));
|
||||
}
|
||||
}
|
||||
if (data.mentions) {
|
||||
this.mentions.users.clear();
|
||||
for (const mention of data.mentions) {
|
||||
let user = this.client.users.get(mention.id);
|
||||
if (user) {
|
||||
@@ -214,6 +215,7 @@ class Message {
|
||||
}
|
||||
}
|
||||
if (data.mention_roles) {
|
||||
this.mentions.roles.clear();
|
||||
for (const mention of data.mention_roles) {
|
||||
const role = this.channel.guild.roles.get(mention);
|
||||
if (role) this.mentions.roles.set(role.id, role);
|
||||
@@ -221,6 +223,7 @@ class Message {
|
||||
}
|
||||
if (data.id) this.id = data.id;
|
||||
if (this.channel.guild && data.content) {
|
||||
this.mentions.channels.clear();
|
||||
const channMentionsRaw = data.content.match(/<#([0-9]{14,20})>/g) || [];
|
||||
for (const raw of channMentionsRaw) {
|
||||
const chan = this.channel.guild.channels.get(raw.match(/([0-9]{14,20})/g)[0]);
|
||||
@@ -228,11 +231,11 @@ class Message {
|
||||
}
|
||||
}
|
||||
if (data.reactions) {
|
||||
this.reactions = new Collection();
|
||||
this.reactions.clear();
|
||||
if (data.reactions.length > 0) {
|
||||
for (const reaction of data.reactions) {
|
||||
const id = reaction.emoji.id ? `${reaction.emoji.name}:${reaction.emoji.id}` : reaction.emoji.name;
|
||||
this.reactions.set(id, new MessageReaction(this, data.emoji, data.count, data.me));
|
||||
this.reactions.set(id, new MessageReaction(this, reaction.emoji, reaction.count, reaction.me));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,6 +55,12 @@ class User {
|
||||
* @type {?Snowflake}
|
||||
*/
|
||||
this.lastMessageID = null;
|
||||
|
||||
/**
|
||||
* The Message object of the last message sent by the user, if one was sent.
|
||||
* @type {?Message}
|
||||
*/
|
||||
this.lastMessage = null;
|
||||
}
|
||||
|
||||
patch(data) {
|
||||
@@ -173,6 +179,14 @@ class User {
|
||||
return this.client.channels.filter(c => c.type === 'dm').find(c => c.recipient.id === this.id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a DM channel between the client and the user
|
||||
* @returns {Promise<DMChannel>}
|
||||
*/
|
||||
createDM() {
|
||||
return this.client.rest.methods.createDM(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a DM channel (if one exists) between the client and the user. Resolves with the channel if successful.
|
||||
* @returns {Promise<DMChannel>}
|
||||
|
||||
@@ -42,6 +42,12 @@ class UserProfile {
|
||||
*/
|
||||
this.premium = data.premium;
|
||||
|
||||
/**
|
||||
* The date since which the user has had Discord Premium
|
||||
* @type {?Date}
|
||||
*/
|
||||
this.premiumSince = data.premium_since ? new Date(data.premium_since) : null;
|
||||
|
||||
for (const guild of data.mutual_guilds) {
|
||||
if (this.client.guilds.has(guild.id)) {
|
||||
this.mutualGuilds.set(guild.id, this.client.guilds.get(guild.id));
|
||||
|
||||
@@ -20,6 +20,12 @@ class TextBasedChannel {
|
||||
* @type {?Snowflake}
|
||||
*/
|
||||
this.lastMessageID = null;
|
||||
|
||||
/**
|
||||
* The Message object of the last message in the channel, if one was sent.
|
||||
* @type {?Message}
|
||||
*/
|
||||
this.lastMessage = null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -137,7 +137,8 @@ class Collection extends Map {
|
||||
* Searches for a single item where its specified property's value is identical to the given value
|
||||
* (`item[prop] === value`), or the given function returns a truthy value. In the latter case, this is identical to
|
||||
* [Array.find()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find).
|
||||
* <warn>Do not use this to obtain an item by its ID. Instead, use `collection.get(id)`. See
|
||||
* <warn>All collections used in Discord.js are mapped using their `id` property, and if you want to find by id you
|
||||
* should use the `get` method. See
|
||||
* [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/get) for details.</warn>
|
||||
* @param {string|Function} propOrFn The property to test against, or the function to test with
|
||||
* @param {*} [value] The expected value - only applicable and required if using a property for the first argument
|
||||
@@ -150,7 +151,6 @@ class Collection extends Map {
|
||||
find(propOrFn, value) {
|
||||
if (typeof propOrFn === 'string') {
|
||||
if (typeof value === 'undefined') throw new Error('Value must be specified.');
|
||||
if (propOrFn === 'id') throw new RangeError('Don\'t use .find() with IDs. Instead, use .get(id).');
|
||||
for (const item of this.values()) {
|
||||
if (item[propOrFn] === value) return item;
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@ exports.DefaultOptions = {
|
||||
*/
|
||||
ws: {
|
||||
large_threshold: 250,
|
||||
compress: typeof window === 'undefined',
|
||||
compress: require('os').platform() !== 'browser',
|
||||
properties: {
|
||||
$os: process ? process.platform : 'discord.js',
|
||||
$browser: 'discord.js',
|
||||
@@ -125,6 +125,7 @@ const Endpoints = exports.Endpoints = {
|
||||
guildMemberNickname: (guildID) => `${Endpoints.guildMember(guildID, '@me')}/nick`,
|
||||
guildChannels: (guildID) => `${Endpoints.guild(guildID)}/channels`,
|
||||
guildEmojis: (guildID) => `${Endpoints.guild(guildID)}/emojis`,
|
||||
guildEmoji: (guildID, emojiID) => `${Endpoints.guildEmojis(guildID)}/${emojiID}`,
|
||||
guildSearch: (guildID) => `${Endpoints.guild(guildID)}/messages/search`,
|
||||
guildVoiceRegions: (guildID) => `${Endpoints.guild(guildID)}/regions`,
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
ONLY RUN BUILDS WITH `npm run web-dist`!
|
||||
ONLY RUN BUILDS WITH `npm run webpack`!
|
||||
DO NOT USE NORMAL WEBPACK! IT WILL NOT WORK!
|
||||
*/
|
||||
|
||||
|
||||
Reference in New Issue
Block a user