From 0b908f5bce9ba49cebd99f0480fbe0dfa74d6e8d Mon Sep 17 00:00:00 2001 From: Schuyler Cebulskie Date: Sun, 4 Sep 2016 05:08:09 -0400 Subject: [PATCH] Cleanup Part 2: Electric Boogaloo (Reloaded) (#594) * Cleanup Part 2: Electric Boogaloo (Reloaded) * Moar cleanup * Tweak NOT_A_PERMISSION error --- src/client/Client.js | 20 +- src/client/ClientDataManager.js | 34 +- src/client/ClientDataResolver.js | 117 +++--- src/client/ClientManager.js | 20 +- src/client/actions/Action.js | 2 - src/client/actions/ActionsManager.js | 1 - src/client/actions/ChannelCreate.js | 1 - src/client/actions/ChannelDelete.js | 1 - src/client/actions/ChannelUpdate.js | 12 +- src/client/actions/GuildBanRemove.js | 1 - src/client/actions/GuildDelete.js | 6 +- src/client/actions/GuildMemberRemove.js | 6 +- src/client/actions/GuildRoleCreate.js | 12 +- src/client/actions/GuildRoleDelete.js | 12 +- src/client/actions/GuildRoleUpdate.js | 14 +- src/client/actions/GuildSync.js | 1 - src/client/actions/GuildUpdate.js | 12 +- src/client/actions/MessageCreate.js | 1 - src/client/actions/MessageDelete.js | 1 - src/client/actions/MessageDeleteBulk.js | 5 +- src/client/actions/MessageUpdate.js | 12 +- src/client/actions/UserGet.js | 1 - src/client/actions/UserUpdate.js | 12 +- src/client/rest/APIRequest.js | 9 +- src/client/rest/RESTManager.js | 1 - src/client/rest/RESTMethods.js | 293 +++++---------- .../rest/RequestHandlers/RequestHandler.js | 7 +- src/client/rest/RequestHandlers/Sequential.js | 14 +- src/client/voice/ClientVoiceManager.js | 32 +- src/client/voice/VoiceConnection.js | 37 +- src/client/voice/VoiceConnectionUDPClient.js | 12 +- src/client/voice/VoiceConnectionWebSocket.js | 12 +- .../voice/dispatcher/StreamDispatcher.js | 77 ++-- src/client/voice/opus/NodeOpusEngine.js | 1 - src/client/voice/opus/OpusEngineList.js | 14 +- src/client/voice/opus/OpusScriptEngine.js | 7 +- src/client/voice/pcm/ConverterEngine.js | 2 - src/client/voice/pcm/FfmpegConverterEngine.js | 20 +- src/client/voice/player/BasePlayer.js | 10 +- src/client/voice/player/DefaultPlayer.js | 1 - src/client/voice/receiver/VoiceReadable.js | 6 +- src/client/voice/receiver/VoiceReceiver.js | 46 +-- src/client/websocket/WebSocketManager.js | 68 ++-- .../packets/WebSocketPacketManager.js | 11 +- .../packets/handlers/AbstractHandler.js | 1 - .../packets/handlers/ChannelCreate.js | 19 +- .../packets/handlers/ChannelDelete.js | 19 +- .../packets/handlers/ChannelPinsUpdate.js | 25 +- .../packets/handlers/ChannelUpdate.js | 5 +- .../websocket/packets/handlers/GuildBanAdd.js | 21 +- .../packets/handlers/GuildBanRemove.js | 16 +- .../websocket/packets/handlers/GuildCreate.js | 5 +- .../websocket/packets/handlers/GuildDelete.js | 19 +- .../packets/handlers/GuildMemberAdd.js | 6 +- .../packets/handlers/GuildMemberRemove.js | 5 +- .../packets/handlers/GuildMemberUpdate.js | 9 +- .../packets/handlers/GuildMembersChunk.js | 19 +- .../packets/handlers/GuildRoleCreate.js | 5 +- .../packets/handlers/GuildRoleDelete.js | 5 +- .../packets/handlers/GuildRoleUpdate.js | 5 +- .../websocket/packets/handlers/GuildSync.js | 5 +- .../websocket/packets/handlers/GuildUpdate.js | 5 +- .../packets/handlers/MessageCreate.js | 19 +- .../packets/handlers/MessageDelete.js | 19 +- .../packets/handlers/MessageDeleteBulk.js | 14 +- .../packets/handlers/MessageUpdate.js | 5 +- .../packets/handlers/PresenceUpdate.js | 34 +- .../websocket/packets/handlers/Ready.js | 22 +- .../websocket/packets/handlers/TypingStart.js | 58 ++- .../websocket/packets/handlers/UserUpdate.js | 5 +- .../packets/handlers/VoiceServerUpdate.js | 5 +- .../packets/handlers/VoiceStateUpdate.js | 25 +- src/structures/Channel.js | 8 +- src/structures/ClientUser.js | 14 +- src/structures/DMChannel.js | 2 +- src/structures/Emoji.js | 8 +- src/structures/EvaluatedPermissions.js | 23 +- src/structures/GroupDMChannel.js | 26 +- src/structures/Guild.js | 147 ++++---- src/structures/GuildChannel.js | 70 ++-- src/structures/GuildMember.js | 40 +-- src/structures/Invite.js | 2 +- src/structures/Message.js | 94 ++--- src/structures/MessageEmbed.js | 112 +++--- src/structures/PermissionOverwrites.js | 6 +- src/structures/Role.js | 61 ++-- src/structures/TextChannel.js | 1 - src/structures/User.js | 25 +- src/structures/VoiceChannel.js | 12 +- src/structures/interface/TextBasedChannel.js | 336 ++++++++---------- src/util/ArraysEqual.js | 14 + src/util/CloneObject.js | 1 - src/util/Collection.js | 72 ++-- src/util/Constants.js | 7 +- src/util/MergeDefault.js | 7 +- 95 files changed, 946 insertions(+), 1526 deletions(-) create mode 100644 src/util/ArraysEqual.js diff --git a/src/client/Client.js b/src/client/Client.js index 79f32165b..368f5163f 100644 --- a/src/client/Client.js +++ b/src/client/Client.js @@ -15,10 +15,8 @@ const Collection = require('../util/Collection'); * @extends {EventEmitter} */ class Client extends EventEmitter { - /** - * Creates an instance of Client. - * @param {ClientOptions} [options] options to pass to the client + * @param {ClientOptions} [options] Options for the client */ constructor(options) { super(); @@ -129,17 +127,13 @@ class Client extends EventEmitter { * client.login(email, password); */ login(emailOrToken, password) { - if (password) { - // login with email and password - return this.rest.methods.loginEmailPassword(emailOrToken, password); - } - // login with token + if (password) return this.rest.methods.loginEmailPassword(emailOrToken, password); return this.rest.methods.loginToken(emailOrToken); } /** - * Destroys the client and logs out. Resolves with null if successful. - * @returns {Promise} + * Destroys the client and logs out. + * @returns {Promise} */ destroy() { return new Promise((resolve, reject) => { @@ -152,8 +146,7 @@ class Client extends EventEmitter { this._timeouts = []; this._intervals = []; resolve(); - }) - .catch(reject); + }).catch(reject); }); } @@ -176,7 +169,7 @@ class Client extends EventEmitter { /** * This shouldn't really be necessary to most developers as it is automatically invoked every 30 seconds, however * if you wish to force a sync of Guild data, you can use this. Only applicable to user accounts. - * @param {array} [guilds=this.guilds.array()] An array of guilds to sync. + * @param {Guild[]} [guilds=this.guilds.array()] An array of guilds to sync */ syncGuilds(guilds = this.guilds.array()) { if (!this.user.bot) { @@ -215,7 +208,6 @@ class Client extends EventEmitter { get uptime() { return this.readyTime ? Date.now() - this.readyTime : null; } - } module.exports = Client; diff --git a/src/client/ClientDataManager.js b/src/client/ClientDataManager.js index 62924904e..5c2201810 100644 --- a/src/client/ClientDataManager.js +++ b/src/client/ClientDataManager.js @@ -23,11 +23,10 @@ class ClientDataManager { this.client.guilds.set(guild.id, guild); if (this.pastReady && !already) { /** - * Emitted whenever the client joins a Guild. - * - * @event Client#guildCreate - * @param {Guild} guild the created guild. - */ + * Emitted whenever the client joins a Guild. + * @event Client#guildCreate + * @param {Guild} guild The created guild + */ this.client.emit(Constants.Events.GUILD_CREATE, guild); } @@ -35,16 +34,13 @@ class ClientDataManager { } newUser(data) { - if (this.client.users.has(data.id)) { - return this.client.users.get(data.id); - } + if (this.client.users.has(data.id)) return this.client.users.get(data.id); const user = new User(this.client, data); this.client.users.set(user.id, user); return user; } - newChannel(data, $guild) { - let guild = $guild; + newChannel(data, guild) { const already = this.client.channels.has(data.id); let channel; if (data.type === Constants.ChannelTypes.DM) { @@ -65,22 +61,18 @@ class ClientDataManager { } if (channel) { - if (this.pastReady && !already) { - this.client.emit(Constants.Events.CHANNEL_CREATE, channel); - } - + if (this.pastReady && !already) this.client.emit(Constants.Events.CHANNEL_CREATE, channel); this.client.channels.set(channel.id, channel); return channel; } + return null; } killGuild(guild) { const already = this.client.guilds.has(guild.id); this.client.guilds.delete(guild.id); - if (already && this.pastReady) { - this.client.emit(Constants.Events.GUILD_DELETE, guild); - } + if (already && this.pastReady) this.client.emit(Constants.Events.GUILD_DELETE, guild); } killUser(user) { @@ -89,17 +81,13 @@ class ClientDataManager { killChannel(channel) { this.client.channels.delete(channel.id); - if (channel instanceof GuildChannel) { - channel.guild.channels.delete(channel.id); - } + if (channel instanceof GuildChannel) channel.guild.channels.delete(channel.id); } updateGuild(currentGuild, newData) { const oldGuild = cloneObject(currentGuild); currentGuild.setup(newData); - if (this.pastReady) { - this.client.emit(Constants.Events.GUILD_UPDATE, oldGuild, currentGuild); - } + if (this.pastReady) this.client.emit(Constants.Events.GUILD_UPDATE, oldGuild, currentGuild); } updateChannel(currentChannel, newData) { diff --git a/src/client/ClientDataResolver.js b/src/client/ClientDataResolver.js index 6ba335e05..df451124e 100644 --- a/src/client/ClientDataResolver.js +++ b/src/client/ClientDataResolver.js @@ -2,13 +2,12 @@ const path = require('path'); const fs = require('fs'); const request = require('superagent'); -const getStructure = name => require(`../structures/${name}`); - -const User = getStructure('User'); -const Message = getStructure('Message'); -const Guild = getStructure('Guild'); -const Channel = getStructure('Channel'); -const GuildMember = getStructure('GuildMember'); +const Constants = require('../util/constants'); +const User = require(`../structures/User`); +const Message = require(`../structures/Message`); +const Guild = require(`../structures/Guild`); +const Channel = require(`../structures/Channel`); +const GuildMember = require(`../structures/GuildMember`); /** * The DataResolver identifies different objects and tries to resolve a specific piece of information from them, e.g. @@ -16,7 +15,9 @@ const GuildMember = getStructure('GuildMember'); * @private */ class ClientDataResolver { - + /** + * @param {Client} client The client the resolver is for + */ constructor(client) { this.client = client; } @@ -33,7 +34,7 @@ class ClientDataResolver { /** * Resolves a UserResolvable to a User object - * @param {UserResolvable} user the UserResolvable to identify + * @param {UserResolvable} user The UserResolvable to identify * @returns {?User} */ resolveUser(user) { @@ -60,18 +61,12 @@ class ClientDataResolver { /** * Resolves a GuildResolvable to a Guild object - * @param {GuildResolvable} guild the GuildResolvable to identify + * @param {GuildResolvable} guild The GuildResolvable to identify * @returns {?Guild} */ resolveGuild(guild) { - if (guild instanceof Guild) { - return guild; - } - - if (typeof guild === 'string') { - return this.client.guilds.get(guild); - } - + if (guild instanceof Guild) return guild; + if (typeof guild === 'string') return this.client.guilds.get(guild); return null; } @@ -84,21 +79,16 @@ class ClientDataResolver { /** * Resolves a GuildMemberResolvable to a GuildMember object - * @param {GuildResolvable} guild the guild that the member is part of - * @param {UserResolvable} user the user that is part of the guild + * @param {GuildResolvable} guild The guild that the member is part of + * @param {UserResolvable} user The user that is part of the guild * @returns {?GuildMember} */ resolveGuildMember(guild, user) { - if (user instanceof GuildMember) { - return user; - } + if (user instanceof GuildMember) return user; guild = this.resolveGuild(guild); user = this.resolveUser(user); - - if (!guild || !user) { - return null; - } + if (!guild || !user) return null; return guild.members.get(user.id); } @@ -112,14 +102,11 @@ class ClientDataResolver { /** * Resolves a Base64Resolvable to a Base 64 image - * @param {Base64Resolvable} data the base 64 resolvable you want to resolve + * @param {Base64Resolvable} data The base 64 resolvable you want to resolve * @returns {?string} */ resolveBase64(data) { - if (data instanceof Buffer) { - return `data:image/jpg;base64,${data.toString('base64')}`; - } - + if (data instanceof Buffer) return `data:image/jpg;base64,${data.toString('base64')}`; return data; } @@ -132,43 +119,49 @@ class ClientDataResolver { /** * Resolves a ChannelResolvable to a Channel object - * @param {ChannelResolvable} channel the channel resolvable to resolve + * @param {ChannelResolvable} channel The channel resolvable to resolve * @returns {?Channel} */ resolveChannel(channel) { - if (channel instanceof Channel) { - return channel; - } - - if (typeof channel === 'string') { - return this.client.channels.get(channel.id); - } - + if (channel instanceof Channel) return channel; + if (typeof channel === 'string') return this.client.channels.get(channel.id); return null; } + /** + * Data that can be resolved to give a permission number. This can be: + * * A string + * * A permission number + * @typedef {string|number} PermissionResolvable + */ + + /** + * Resolves a PermissionResolvable to a permission number + * @param {PermissionResolvable} permission The permission resolvable to resolve + * @returns {number} + */ + resolvePermission(permission) { + if (typeof permission === 'string') permission = Constants.PermissionFlags[permission]; + if (!permission) throw Constants.Errors.NOT_A_PERMISSION; + return permission; + } + /** * Data that can be resolved to give a string. This can be: * * A string * * An Array (joined with a new line delimiter to give a string) - * * Any object - * @typedef {string|Array|Object} StringResolvable + * * Any value + * @typedef {string|Array|*} StringResolvable */ /** * Resolves a StringResolvable to a string - * @param {StringResolvable} data the string resolvable to resolve + * @param {StringResolvable} data The string resolvable to resolve * @returns {string} */ resolveString(data) { - if (typeof data === 'string') { - return data; - } - - if (data instanceof Array) { - return data.join('\n'); - } - + if (typeof data === 'string') return data; + if (data instanceof Array) return data.join('\n'); return String(data); } @@ -182,8 +175,8 @@ class ClientDataResolver { /** * Resolves a FileResolvable to a Buffer - * @param {FileResolvable} resource the file resolvable to resolve - * @returns {string|Buffer} + * @param {FileResolvable} resource The file resolvable to resolve + * @returns {Promise} */ resolveFile(resource) { if (typeof resource === 'string') { @@ -194,19 +187,19 @@ class ClientDataResolver { .end((err, res) => err ? reject(err) : resolve(res.body)); } else { const file = path.resolve(resource); - const stat = fs.statSync(file); - if (!stat.isFile()) { - throw new Error(`The file could not be found: ${file}`); - } - - fs.readFile(file, (err, data) => { - if (err) reject(err); else resolve(data); + fs.stat(file, (err, stats) => { + if (err) reject(err); + if (!stats.isFile()) throw new Error(`The file could not be found: ${file}`); + fs.readFile(file, (err2, data) => { + if (err2) reject(err2); else resolve(data); + }); }); } }); } - return Promise.resolve(resource); + if (resource instanceof Buffer) return Promise.resolve(resource); + return Promise.reject(new TypeError('resource is not a string or Buffer')); } } diff --git a/src/client/ClientManager.js b/src/client/ClientManager.js index 7b1f411ad..cf4b26284 100644 --- a/src/client/ClientManager.js +++ b/src/client/ClientManager.js @@ -5,7 +5,6 @@ const Constants = require('../util/Constants'); * @private */ class ClientManager { - constructor(client) { /** * The Client that instantiated this Manager @@ -21,25 +20,22 @@ class ClientManager { /** * Connects the Client to the WebSocket - * @param {string} token the authorization token - * @param {function} resolve function to run when connection is successful - * @param {function} reject function to run when connection fails + * @param {string} token The authorization token + * @param {function} resolve Function to run when connection is successful + * @param {function} reject Function to run when connection fails */ connectToWebSocket(token, resolve, reject) { this.client.token = token; - this.client.rest.methods.getGateway() - .then(gateway => { - this.client.ws.connect(gateway); - this.client.once(Constants.Events.READY, () => resolve(token)); - }) - .catch(reject); - + this.client.rest.methods.getGateway().then(gateway => { + this.client.ws.connect(gateway); + this.client.once(Constants.Events.READY, () => resolve(token)); + }).catch(reject); this.client.setTimeout(() => reject(Constants.Errors.TOOK_TOO_LONG), 1000 * 300); } /** * Sets up a keep-alive interval to keep the Client's connection valid - * @param {number} time the interval in milliseconds at which heartbeat packets should be sent + * @param {number} time The interval in milliseconds at which heartbeat packets should be sent */ setupKeepAlive(time) { this.heartbeatInterval = this.client.setInterval(() => { diff --git a/src/client/actions/Action.js b/src/client/actions/Action.js index 78c5f146a..8fdadc92b 100644 --- a/src/client/actions/Action.js +++ b/src/client/actions/Action.js @@ -11,7 +11,6 @@ that WebSocket events don't clash with REST methods. */ class GenericAction { - constructor(client) { this.client = client; } @@ -19,7 +18,6 @@ class GenericAction { handle(data) { return data; } - } module.exports = GenericAction; diff --git a/src/client/actions/ActionsManager.js b/src/client/actions/ActionsManager.js index 13fbfeae4..e70cdcbd8 100644 --- a/src/client/actions/ActionsManager.js +++ b/src/client/actions/ActionsManager.js @@ -1,5 +1,4 @@ class ActionsManager { - constructor(client) { this.client = client; diff --git a/src/client/actions/ChannelCreate.js b/src/client/actions/ChannelCreate.js index 2d5079b43..dc4704152 100644 --- a/src/client/actions/ChannelCreate.js +++ b/src/client/actions/ChannelCreate.js @@ -1,7 +1,6 @@ const Action = require('./Action'); class ChannelCreateAction extends Action { - handle(data) { const client = this.client; const channel = client.dataManager.newChannel(data); diff --git a/src/client/actions/ChannelDelete.js b/src/client/actions/ChannelDelete.js index 62fda7f9f..b54783bcc 100644 --- a/src/client/actions/ChannelDelete.js +++ b/src/client/actions/ChannelDelete.js @@ -1,7 +1,6 @@ const Action = require('./Action'); class ChannelDeleteAction extends Action { - constructor(client) { super(client); this.deleted = new Map(); diff --git a/src/client/actions/ChannelUpdate.js b/src/client/actions/ChannelUpdate.js index 67688c06e..636addd2e 100644 --- a/src/client/actions/ChannelUpdate.js +++ b/src/client/actions/ChannelUpdate.js @@ -3,7 +3,6 @@ const Constants = require('../../util/Constants'); const cloneObject = require('../../util/CloneObject'); class ChannelUpdateAction extends Action { - handle(data) { const client = this.client; @@ -26,11 +25,10 @@ class ChannelUpdateAction extends Action { } /** -* Emitted whenever a channel is updated - e.g. name change, topic change. -* -* @event Client#channelUpdate -* @param {Channel} oldChannel the channel before the update -* @param {Channel} newChannel the channel after the update -*/ + * Emitted whenever a channel is updated - e.g. name change, topic change. + * @event Client#channelUpdate + * @param {Channel} oldChannel The channel before the update + * @param {Channel} newChannel The channel after the update + */ module.exports = ChannelUpdateAction; diff --git a/src/client/actions/GuildBanRemove.js b/src/client/actions/GuildBanRemove.js index ffed90a28..0276a523f 100644 --- a/src/client/actions/GuildBanRemove.js +++ b/src/client/actions/GuildBanRemove.js @@ -2,7 +2,6 @@ const Action = require('./Action'); const Constants = require('../../util/Constants'); class GuildBanRemove extends Action { - handle(data) { const client = this.client; const guild = client.guilds.get(data.guild_id); diff --git a/src/client/actions/GuildDelete.js b/src/client/actions/GuildDelete.js index 2dda23af4..a8a6a3bbf 100644 --- a/src/client/actions/GuildDelete.js +++ b/src/client/actions/GuildDelete.js @@ -2,7 +2,6 @@ const Action = require('./Action'); const Constants = require('../../util/Constants'); class GuildDeleteAction extends Action { - constructor(client) { super(client); this.deleted = new Map(); @@ -45,9 +44,8 @@ class GuildDeleteAction extends Action { /** * Emitted whenever a guild becomes unavailable, likely due to a server outage. - * * @event Client#guildUnavailable - * @param {Guild} guild the guild that has become unavailable. -*/ + * @param {Guild} guild The guild that has become unavailable. + */ module.exports = GuildDeleteAction; diff --git a/src/client/actions/GuildMemberRemove.js b/src/client/actions/GuildMemberRemove.js index c0eb18e64..7b21db16a 100644 --- a/src/client/actions/GuildMemberRemove.js +++ b/src/client/actions/GuildMemberRemove.js @@ -2,7 +2,6 @@ const Action = require('./Action'); const Constants = require('../../util/Constants'); class GuildMemberRemoveAction extends Action { - constructor(client) { super(client); this.deleted = new Map(); @@ -43,10 +42,9 @@ class GuildMemberRemoveAction extends Action { /** * Emitted whenever a member leaves a guild, or is kicked. - * * @event Client#guildMemberRemove - * @param {Guild} guild the guild that the member has left. - * @param {GuildMember} member the member that has left the guild. + * @param {Guild} guild The guild that the member has left. + * @param {GuildMember} member The member that has left the guild. */ module.exports = GuildMemberRemoveAction; diff --git a/src/client/actions/GuildRoleCreate.js b/src/client/actions/GuildRoleCreate.js index a6aa9d909..206fb043a 100644 --- a/src/client/actions/GuildRoleCreate.js +++ b/src/client/actions/GuildRoleCreate.js @@ -3,7 +3,6 @@ const Constants = require('../../util/Constants'); const Role = require('../../structures/Role'); class GuildRoleCreate extends Action { - handle(data) { const client = this.client; @@ -25,11 +24,10 @@ class GuildRoleCreate extends Action { } /** -* Emitted whenever a guild role is created. -* -* @event Client#guildRoleCreate -* @param {Guild} guild the guild that the role was created in. -* @param {Role} role the role that was created. -*/ + * Emitted whenever a guild role is created. + * @event Client#guildRoleCreate + * @param {Guild} guild The guild that the role was created in. + * @param {Role} role The role that was created. + */ module.exports = GuildRoleCreate; diff --git a/src/client/actions/GuildRoleDelete.js b/src/client/actions/GuildRoleDelete.js index 8cdb89491..fe4edcceb 100644 --- a/src/client/actions/GuildRoleDelete.js +++ b/src/client/actions/GuildRoleDelete.js @@ -2,7 +2,6 @@ const Action = require('./Action'); const Constants = require('../../util/Constants'); class GuildRoleDeleteAction extends Action { - constructor(client) { super(client); this.deleted = new Map(); @@ -39,11 +38,10 @@ class GuildRoleDeleteAction extends Action { } /** -* Emitted whenever a guild role is deleted. -* -* @event Client#guildRoleDelete -* @param {Guild} guild the guild that the role was deleted in. -* @param {Role} role the role that was deleted. -*/ + * Emitted whenever a guild role is deleted. + * @event Client#guildRoleDelete + * @param {Guild} guild The guild that the role was deleted in. + * @param {Role} role The role that was deleted. + */ module.exports = GuildRoleDeleteAction; diff --git a/src/client/actions/GuildRoleUpdate.js b/src/client/actions/GuildRoleUpdate.js index 3ca6db0db..81baf652d 100644 --- a/src/client/actions/GuildRoleUpdate.js +++ b/src/client/actions/GuildRoleUpdate.js @@ -3,7 +3,6 @@ const Constants = require('../../util/Constants'); const cloneObject = require('../../util/CloneObject'); class GuildRoleUpdateAction extends Action { - handle(data) { const client = this.client; @@ -33,12 +32,11 @@ class GuildRoleUpdateAction extends Action { } /** -* Emitted whenever a guild role is updated. -* -* @event Client#guildRoleUpdated -* @param {Guild} guild the guild that the role was updated in. -* @param {Role} oldRole the role before the update. -* @param {Role} newRole the role after the update. -*/ + * Emitted whenever a guild role is updated. + * @event Client#guildRoleUpdated + * @param {Guild} guild The guild that the role was updated in. + * @param {Role} oldRole The role before the update. + * @param {Role} newRole The role after the update. + */ module.exports = GuildRoleUpdateAction; diff --git a/src/client/actions/GuildSync.js b/src/client/actions/GuildSync.js index ae4bd5daa..763acf788 100644 --- a/src/client/actions/GuildSync.js +++ b/src/client/actions/GuildSync.js @@ -1,7 +1,6 @@ const Action = require('./Action'); class GuildSync extends Action { - handle(data) { const client = this.client; diff --git a/src/client/actions/GuildUpdate.js b/src/client/actions/GuildUpdate.js index 6aa0cb2d3..c1cdf6d6c 100644 --- a/src/client/actions/GuildUpdate.js +++ b/src/client/actions/GuildUpdate.js @@ -3,7 +3,6 @@ const Constants = require('../../util/Constants'); const cloneObject = require('../../util/CloneObject'); class GuildUpdateAction extends Action { - handle(data) { const client = this.client; @@ -26,11 +25,10 @@ class GuildUpdateAction extends Action { } /** -* Emitted whenever a guild is updated - e.g. name change. -* -* @event Client#guildUpdate -* @param {Guild} oldGuild the guild before the update. -* @param {Guild} newGuild the guild after the update. -*/ + * Emitted whenever a guild is updated - e.g. name change. + * @event Client#guildUpdate + * @param {Guild} oldGuild The guild before the update. + * @param {Guild} newGuild The guild after the update. + */ module.exports = GuildUpdateAction; diff --git a/src/client/actions/MessageCreate.js b/src/client/actions/MessageCreate.js index c6dbe3b50..10626179c 100644 --- a/src/client/actions/MessageCreate.js +++ b/src/client/actions/MessageCreate.js @@ -2,7 +2,6 @@ const Action = require('./Action'); const Message = require('../../structures/Message'); class MessageCreateAction extends Action { - handle(data) { const client = this.client; diff --git a/src/client/actions/MessageDelete.js b/src/client/actions/MessageDelete.js index 66981cc06..5618efacb 100644 --- a/src/client/actions/MessageDelete.js +++ b/src/client/actions/MessageDelete.js @@ -1,7 +1,6 @@ const Action = require('./Action'); class MessageDeleteAction extends Action { - constructor(client) { super(client); this.deleted = new Map(); diff --git a/src/client/actions/MessageDeleteBulk.js b/src/client/actions/MessageDeleteBulk.js index 2fda09d1f..6a12ef19b 100644 --- a/src/client/actions/MessageDeleteBulk.js +++ b/src/client/actions/MessageDeleteBulk.js @@ -3,7 +3,6 @@ const Collection = require('../../util/Collection'); const Constants = require('../../util/Constants'); class MessageDeleteBulkAction extends Action { - handle(data) { const client = this.client; const channel = client.channels.get(data.channel_id); @@ -12,9 +11,7 @@ class MessageDeleteBulkAction extends Action { const messages = new Collection(); for (const id of ids) { const message = channel.messages.get(id); - if (message) { - messages.set(message.id, message); - } + if (message) messages.set(message.id, message); } if (messages.size > 0) client.emit(Constants.Events.MESSAGE_BULK_DELETE, messages); diff --git a/src/client/actions/MessageUpdate.js b/src/client/actions/MessageUpdate.js index 9041c090b..59c84dd30 100644 --- a/src/client/actions/MessageUpdate.js +++ b/src/client/actions/MessageUpdate.js @@ -3,7 +3,6 @@ const Constants = require('../../util/Constants'); const cloneObject = require('../../util/CloneObject'); class MessageUpdateAction extends Action { - handle(data) { const client = this.client; @@ -34,11 +33,10 @@ class MessageUpdateAction extends Action { } /** -* Emitted whenever a message is updated - e.g. embed or content change. -* -* @event Client#messageUpdate -* @param {Message} oldMessage the message before the update. -* @param {Message} newMessage the message after the update. -*/ + * Emitted whenever a message is updated - e.g. embed or content change. + * @event Client#messageUpdate + * @param {Message} oldMessage The message before the update. + * @param {Message} newMessage The message after the update. + */ module.exports = MessageUpdateAction; diff --git a/src/client/actions/UserGet.js b/src/client/actions/UserGet.js index 33c3ae84c..65e7c9540 100644 --- a/src/client/actions/UserGet.js +++ b/src/client/actions/UserGet.js @@ -1,7 +1,6 @@ const Action = require('./Action'); class UserGetAction extends Action { - handle(data) { const client = this.client; const user = client.dataManager.newUser(data); diff --git a/src/client/actions/UserUpdate.js b/src/client/actions/UserUpdate.js index ae865f579..a8d479465 100644 --- a/src/client/actions/UserUpdate.js +++ b/src/client/actions/UserUpdate.js @@ -3,7 +3,6 @@ const Constants = require('../../util/Constants'); const cloneObject = require('../../util/CloneObject'); class UserUpdateAction extends Action { - handle(data) { const client = this.client; @@ -32,11 +31,10 @@ class UserUpdateAction extends Action { } /** -* Emitted whenever a detail of the logged in User changes - e.g. username. -* -* @event Client#userUpdate -* @param {ClientUser} oldClientUser the client user before the update. -* @param {ClientUser} newClientUser the client user after the update. -*/ + * Emitted whenever a detail of the logged in User changes - e.g. username. + * @event Client#userUpdate + * @param {ClientUser} oldClientUser The client user before the update. + * @param {ClientUser} newClientUser The client user after the update. + */ module.exports = UserUpdateAction; diff --git a/src/client/rest/APIRequest.js b/src/client/rest/APIRequest.js index c56d1ebf1..e8e9c594f 100644 --- a/src/client/rest/APIRequest.js +++ b/src/client/rest/APIRequest.js @@ -26,17 +26,12 @@ class APIRequest { gen() { const apiRequest = request[this.method](this.url); - if (this.auth) { - apiRequest.set('authorization', this.getAuth()); - } + if (this.auth) apiRequest.set('authorization', this.getAuth()); if (this.file && this.file.file) { apiRequest.set('Content-Type', 'multipart/form-data'); apiRequest.attach('file', this.file.file, this.file.name); } - if (this.data) { - apiRequest.send(this.data); - } - + if (this.data) apiRequest.send(this.data); apiRequest.set('User-Agent', this.rest.userAgentManager.userAgent); return apiRequest; } diff --git a/src/client/rest/RESTManager.js b/src/client/rest/RESTManager.js index 716b30279..9af4712da 100644 --- a/src/client/rest/RESTManager.js +++ b/src/client/rest/RESTManager.js @@ -5,7 +5,6 @@ const APIRequest = require('./APIRequest'); const Constants = require('../../util/Constants'); class RESTManager { - constructor(client) { this.client = client; this.handlers = {}; diff --git a/src/client/rest/RESTMethods.js b/src/client/rest/RESTMethods.js index 795643e88..884841c5e 100644 --- a/src/client/rest/RESTMethods.js +++ b/src/client/rest/RESTMethods.js @@ -1,11 +1,11 @@ const Constants = require('../../util/Constants'); const Collection = require('../../util/Collection'); -const getStructure = name => require(`../../structures/${name}`); -const User = getStructure('User'); -const GuildMember = getStructure('GuildMember'); -const Role = getStructure('Role'); -const Invite = getStructure('Invite'); +const requireStructure = name => require(`../../structures/${name}`); +const User = requireStructure('User'); +const GuildMember = requireStructure('GuildMember'); +const Role = requireStructure('Role'); +const Invite = requireStructure('Invite'); class RESTMethods { constructor(restManager) { @@ -45,10 +45,9 @@ class RESTMethods { }); } - sendMessage($channel, content, tts, nonce, file) { + sendMessage(channel, content, tts, nonce, file) { return new Promise((resolve, reject) => { const $this = this; - let channel = $channel; function req() { $this.rest.makeRequest('post', Constants.Endpoints.channelMessages(channel.id), true, { @@ -101,11 +100,9 @@ class RESTMethods { return new Promise((resolve, reject) => { this.rest.makeRequest('patch', Constants.Endpoints.channelMessage(message.channel.id, message.id), true, { content, - }) - .then(data => { + }).then(data => { resolve(this.rest.client.actions.MessageUpdate.handle(data).updated); - }) - .catch(reject); + }).catch(reject); }); } @@ -114,11 +111,9 @@ class RESTMethods { this.rest.makeRequest('post', Constants.Endpoints.guildChannels(guild.id), true, { name: channelName, type: channelType, - }) - .then(data => { - resolve(this.rest.client.actions.ChannelCreate.handle(data).channel); - }) - .catch(reject); + }).then(data => { + resolve(this.rest.client.actions.ChannelCreate.handle(data).channel); + }).catch(reject); }); } @@ -126,98 +121,73 @@ class RESTMethods { const dmChannel = Array.from(this.rest.client.channels.values()) .filter(channel => channel.recipient) .filter(channel => channel.recipient.id === recipient.id); - return dmChannel[0]; } createDM(recipient) { return new Promise((resolve, reject) => { const dmChannel = this.getExistingDM(recipient); - - if (dmChannel) { - return resolve(dmChannel); - } - + if (dmChannel) return resolve(dmChannel); return this.rest.makeRequest('post', Constants.Endpoints.userChannels(this.rest.client.user.id), true, { recipient_id: recipient.id, - }) - .then(data => resolve(this.rest.client.actions.ChannelCreate.handle(data).channel)) - .catch(reject); + }).then(data => resolve(this.rest.client.actions.ChannelCreate.handle(data).channel)).catch(reject); }); } - deleteChannel($channel) { - let channel = $channel; + deleteChannel(channel) { return new Promise((resolve, reject) => { - if (channel instanceof User || channel instanceof GuildMember) { - channel = this.getExistingDM(channel); - } - - this.rest.makeRequest('del', Constants.Endpoints.channel(channel.id), true) - .then($data => { - const data = $data; - data.id = channel.id; - resolve(this.rest.client.actions.ChannelDelete.handle(data).channel); - }) - .catch(reject); + if (channel instanceof User || channel instanceof GuildMember) channel = this.getExistingDM(channel); + this.rest.makeRequest('del', Constants.Endpoints.channel(channel.id), true).then(data => { + data.id = channel.id; + resolve(this.rest.client.actions.ChannelDelete.handle(data).channel); + }).catch(reject); }); } - updateChannel(channel, $data) { - const data = $data; + updateChannel(channel, data) { return new Promise((resolve, reject) => { data.name = (data.name || channel.name).trim(); data.topic = data.topic || channel.topic; data.position = data.position || channel.position; data.bitrate = data.bitrate || channel.bitrate; - this.rest.makeRequest('patch', Constants.Endpoints.channel(channel.id), true, data) - .then(newData => { - resolve(this.rest.client.actions.ChannelUpdate.handle(newData).updated); - }) - .catch(reject); + this.rest.makeRequest('patch', Constants.Endpoints.channel(channel.id), true, data).then(newData => { + resolve(this.rest.client.actions.ChannelUpdate.handle(newData).updated); + }).catch(reject); }); } leaveGuild(guild) { - if (guild.ownerID === this.rest.client.user.id) { - return this.deleteGuild(guild); - } + if (guild.ownerID === this.rest.client.user.id) return this.deleteGuild(guild); return new Promise((resolve, reject) => { - this.rest.makeRequest('del', Constants.Endpoints.meGuild(guild.id), true) - .then(() => { - resolve(this.rest.client.actions.GuildDelete.handle({ id: guild.id }).guild); - }) - .catch(reject); + this.rest.makeRequest('del', Constants.Endpoints.meGuild(guild.id), true).then(() => { + resolve(this.rest.client.actions.GuildDelete.handle({ id: guild.id }).guild); + }).catch(reject); }); } // untested but probably will work deleteGuild(guild) { return new Promise((resolve, reject) => { - this.rest.makeRequest('del', Constants.Endpoints.guild(guild.id), true) - .then(() => { - resolve(this.rest.client.actions.GuildDelete.handle({ id: guild.id }).guild); - }) - .catch(reject); + this.rest.makeRequest('del', Constants.Endpoints.guild(guild.id), true).then(() => { + resolve(this.rest.client.actions.GuildDelete.handle({ id: guild.id }).guild); + }).catch(reject); }); } getUser(userID) { return new Promise((resolve, reject) => { - this.rest.makeRequest('get', Constants.Endpoints.user(userID), true) - .then((data) => { - resolve(this.rest.client.actions.UserGet.handle(data).user); - }) - .catch(reject); + this.rest.makeRequest('get', Constants.Endpoints.user(userID), true).then((data) => { + resolve(this.rest.client.actions.UserGet.handle(data).user); + }).catch(reject); }); } updateCurrentUser(_data) { return new Promise((resolve, reject) => { const user = this.rest.client.user; - const data = {}; + const data = {}; data.username = _data.username || user.username; data.avatar = this.rest.client.resolver.resolveBase64(_data.avatar) || user.avatar; if (!user.bot) { @@ -234,44 +204,15 @@ class RESTMethods { updateGuild(guild, _data) { return new Promise((resolve, reject) => { - /* - can contain: - name, region, verificationLevel, afkChannel, afkTimeout, icon, owner, splash - */ - const data = {}; - - if (_data.name) { - data.name = _data.name; - } - - if (_data.region) { - data.region = _data.region; - } - - if (_data.verificationLevel) { - data.verification_level = Number(_data.verificationLevel); - } - - if (_data.afkChannel) { - data.afk_channel_id = this.rest.client.resolver.resolveChannel(_data.afkChannel).id; - } - - if (_data.afkTimeout) { - data.afk_timeout = Number(_data.afkTimeout); - } - - if (_data.icon) { - data.icon = this.rest.client.resolver.resolveBase64(_data.icon); - } - - if (_data.owner) { - data.owner_id = this.rest.client.resolver.resolveUser(_data.owner).id; - } - - if (_data.splash) { - data.splash = this.rest.client.resolver.resolveBase64(_data.splash); - } + if (_data.name) data.name = _data.name; + if (_data.region) data.region = _data.region; + if (_data.verificationLevel) data.verification_level = Number(_data.verificationLevel); + if (_data.afkChannel) data.afk_channel_id = this.rest.client.resolver.resolveChannel(_data.afkChannel).id; + if (_data.afkTimeout) data.afk_timeout = Number(_data.afkTimeout); + if (_data.icon) data.icon = this.rest.client.resolver.resolveBase64(_data.icon); + if (_data.owner) data.owner_id = this.rest.client.resolver.resolveUser(_data.owner).id; + if (_data.splash) data.splash = this.rest.client.resolver.resolveBase64(_data.splash); this.rest.makeRequest('patch', Constants.Endpoints.guild(guild.id), true, data) .then(newData => resolve(this.rest.client.actions.GuildUpdate.handle(newData).updated)) @@ -281,40 +222,34 @@ class RESTMethods { kickGuildMember(guild, member) { return new Promise((resolve, reject) => { - this.rest.makeRequest('del', Constants.Endpoints.guildMember(guild.id, member.id), true) - .then(() => { - resolve(this.rest.client.actions.GuildMemberRemove.handle({ - guild_id: guild.id, - user: member.user, - }).member); - }) - .catch(reject); + this.rest.makeRequest('del', Constants.Endpoints.guildMember(guild.id, member.id), true).then(() => { + resolve(this.rest.client.actions.GuildMemberRemove.handle({ + guild_id: guild.id, + user: member.user, + }).member); + }).catch(reject); }); } createGuildRole(guild) { return new Promise((resolve, reject) => { - this.rest.makeRequest('post', Constants.Endpoints.guildRoles(guild.id), true) - .then(role => { - resolve(this.rest.client.actions.GuildRoleCreate.handle({ - guild_id: guild.id, - role, - }).role); - }) - .catch(reject); + this.rest.makeRequest('post', Constants.Endpoints.guildRoles(guild.id), true).then(role => { + resolve(this.rest.client.actions.GuildRoleCreate.handle({ + guild_id: guild.id, + role, + }).role); + }).catch(reject); }); } deleteGuildRole(role) { return new Promise((resolve, reject) => { - this.rest.makeRequest('del', Constants.Endpoints.guildRole(role.guild.id, role.id), true) - .then(() => { - resolve(this.rest.client.actions.GuildRoleDelete.handle({ - guild_id: role.guild.id, - role_id: role.id, - }).role); - }) - .catch(reject); + this.rest.makeRequest('del', Constants.Endpoints.guildRole(role.guild.id, role.id), true).then(() => { + resolve(this.rest.client.actions.GuildRoleDelete.handle({ + guild_id: role.guild.id, + role_id: role.id, + }).role); + }).catch(reject); }); } @@ -338,23 +273,14 @@ class RESTMethods { getChannelMessages(channel, payload = {}) { return new Promise((resolve, reject) => { const params = []; - if (payload.limit) { - params.push(`limit=${payload.limit}`); - } - if (payload.around) { - params.push(`around=${payload.around}`); - } else if (payload.before) { - params.push(`before=${payload.before}`); - } else if (payload.after) { - params.push(`after=${payload.after}`); - } + if (payload.limit) params.push(`limit=${payload.limit}`); + if (payload.around) params.push(`around=${payload.around}`); + else if (payload.before) params.push(`before=${payload.before}`); + else if (payload.after) params.push(`after=${payload.after}`); - let request = Constants.Endpoints.channelMessages(channel.id); - if (params.length > 0) { - request += `?${params.join('&')}`; - } - - this.rest.makeRequest('get', request, true) + let endpoint = Constants.Endpoints.channelMessages(channel.id); + if (params.length > 0) endpoint += `?${params.join('&')}`; + this.rest.makeRequest('get', endpoint, true) .then(resolve) .catch(reject); }); @@ -362,13 +288,9 @@ class RESTMethods { updateGuildMember(member, data) { return new Promise((resolve, reject) => { - if (data.channel) { - data.channel_id = this.rest.client.resolver.resolveChannel(data.channel).id; - } + if (data.channel) data.channel_id = this.rest.client.resolver.resolveChannel(data.channel).id; if (data.roles) { - if (data.roles instanceof Map) { - data.roles = data.roles.array(); - } + if (data.roles instanceof Collection) data.roles = data.roles.array(); data.roles = data.roles.map(role => role instanceof Role ? role.id : role); } @@ -379,6 +301,7 @@ class RESTMethods { endpoint = Constants.Endpoints.stupidInconsistentGuildEndpoint(member.guild.id); } } + this.rest.makeRequest('patch', endpoint, true, data) .then(resData => resolve(member.guild._updateMember(member, resData).mem)) .catch(reject); @@ -407,9 +330,7 @@ class RESTMethods { unbanGuildMember(guild, member) { return new Promise((resolve, reject) => { member = this.rest.client.resolver.resolveUser(member); - if (!member) { - throw new Error('cannot unban a user that is not a user resolvable'); - } + if (!member) throw new Error('cannot unban a user that is not a user resolvable'); const listener = (eGuild, eUser) => { if (guild.id === guild.id && member.id === eUser.id) { this.rest.client.removeListener(Constants.Events.GUILD_BAN_REMOVE, listener); @@ -423,48 +344,30 @@ class RESTMethods { getGuildBans(guild) { return new Promise((resolve, reject) => { - this.rest.makeRequest('get', Constants.Endpoints.guildBans(guild.id), true) - .then(banItems => { - const bannedUsers = new Collection(); - for (const banItem of banItems) { - const user = this.rest.client.dataManager.newUser(banItem.user); - bannedUsers.set(user.id, user); - } - resolve(bannedUsers); - }) - .catch(reject); + this.rest.makeRequest('get', Constants.Endpoints.guildBans(guild.id), true).then(banItems => { + const bannedUsers = new Collection(); + for (const banItem of banItems) { + const user = this.rest.client.dataManager.newUser(banItem.user); + bannedUsers.set(user.id, user); + } + resolve(bannedUsers); + }).catch(reject); }); } updateGuildRole(role, _data) { return new Promise((resolve, reject) => { - /* - can contain: - name, position, permissions, color, hoist - */ - const data = {}; - data.name = _data.name || role.name; data.position = _data.position || role.position; data.color = _data.color || role.color; - - if (data.color.startsWith('#')) { - data.color = parseInt(data.color.replace('#', ''), 16); - } - - if (typeof _data.hoist !== 'undefined') { - data.hoist = _data.hoist; - } else { - data.hoist = role.hoist; - } + if (data.color.startsWith('#')) data.color = parseInt(data.color.replace('#', ''), 16); + data.hoist = typeof _data.hoist !== 'undefined' ? _data.hoist : role.hoist; if (_data.permissions) { let perms = 0; for (let perm of _data.permissions) { - if (typeof perm === 'string') { - perm = Constants.PermissionFlags[perm]; - } + if (typeof perm === 'string') perm = Constants.PermissionFlags[perm]; perms |= perm; } data.permissions = perms; @@ -472,14 +375,12 @@ class RESTMethods { data.permissions = role.permissions; } - this.rest.makeRequest('patch', Constants.Endpoints.guildRole(role.guild.id, role.id), true, data) - .then(_role => { - resolve(this.rest.client.actions.GuildRoleUpdate.handle({ - role: _role, - guild_id: role.guild.id, - }).updated); - }) - .catch(reject); + this.rest.makeRequest('patch', Constants.Endpoints.guildRole(role.guild.id, role.id), true, data).then(_role => { + resolve(this.rest.client.actions.GuildRoleUpdate.handle({ + role: _role, + guild_id: role.guild.id, + }).updated); + }).catch(reject); }); } @@ -526,16 +427,14 @@ class RESTMethods { getGuildInvites(guild) { return new Promise((resolve, reject) => { - this.rest.makeRequest('get', Constants.Endpoints.guildInvites(guild.id), true) - .then(inviteItems => { - const invites = new Collection(); - for (const inviteItem of inviteItems) { - const invite = new Invite(this.rest.client, inviteItem); - invites.set(invite.code, invite); - } - resolve(invites); - }) - .catch(reject); + this.rest.makeRequest('get', Constants.Endpoints.guildInvites(guild.id), true).then(inviteItems => { + const invites = new Collection(); + for (const inviteItem of inviteItems) { + const invite = new Invite(this.rest.client, inviteItem); + invites.set(invite.code, invite); + } + resolve(invites); + }).catch(reject); }); } } diff --git a/src/client/rest/RequestHandlers/RequestHandler.js b/src/client/rest/RequestHandlers/RequestHandler.js index 0a7d3e53f..a1a2f3475 100644 --- a/src/client/rest/RequestHandlers/RequestHandler.js +++ b/src/client/rest/RequestHandlers/RequestHandler.js @@ -3,6 +3,9 @@ * @private */ class RequestHandler { + /** + * @param {RESTManager} restManager The REST manager to use + */ constructor(restManager) { /** * The RESTManager that instantiated this RequestHandler @@ -12,7 +15,7 @@ class RequestHandler { /** * A list of requests that have yet to be processed. - * @type {Array} + * @type {APIRequest[]} */ this.queue = []; } @@ -31,7 +34,7 @@ class RequestHandler { /** * Push a new API request into this bucket - * @param {APIRequest} request the new request to push into the queue + * @param {APIRequest} request The new request to push into the queue */ push(request) { this.queue.push(request); diff --git a/src/client/rest/RequestHandlers/Sequential.js b/src/client/rest/RequestHandlers/Sequential.js index 613ccf148..c971c198f 100644 --- a/src/client/rest/RequestHandlers/Sequential.js +++ b/src/client/rest/RequestHandlers/Sequential.js @@ -8,7 +8,10 @@ const RequestHandler = require('./RequestHandler'); * @private */ class SequentialRequestHandler extends RequestHandler { - + /** + * @param {RESTManager} restManager The REST manager to use + * @param {string} endpoint The endpoint to handle + */ constructor(restManager, endpoint) { super(restManager, endpoint); @@ -39,8 +42,8 @@ class SequentialRequestHandler extends RequestHandler { /** * Performs a request then resolves a promise to indicate its readiness for a new request - * @param {APIRequest} item the item to execute - * @returns {Promise} + * @param {APIRequest} item The item to execute + * @returns {Promise} */ execute(item) { return new Promise(resolve => { @@ -88,9 +91,8 @@ class SequentialRequestHandler extends RequestHandler { handle() { super.handle(); - if (this.waiting || this.queue.length === 0 || this.globalLimit) { - return; - } + + if (this.waiting || this.queue.length === 0 || this.globalLimit) return; this.waiting = true; const item = this.queue[0]; diff --git a/src/client/voice/ClientVoiceManager.js b/src/client/voice/ClientVoiceManager.js index 3120c4e0a..0b119914b 100644 --- a/src/client/voice/ClientVoiceManager.js +++ b/src/client/voice/ClientVoiceManager.js @@ -33,9 +33,7 @@ class ClientVoiceManager { */ _checkPendingReady(guildID) { const pendingRequest = this.pending.get(guildID); - if (!pendingRequest) { - throw new Error('Guild not pending'); - } + if (!pendingRequest) throw new Error('Guild not pending'); if (pendingRequest.token && pendingRequest.sessionID && pendingRequest.endpoint) { const { channel, token, sessionID, endpoint, resolve, reject } = pendingRequest; const voiceConnection = new VoiceConnection(this, channel, token, sessionID, endpoint, resolve, reject); @@ -49,15 +47,13 @@ class ClientVoiceManager { /** * Called when the Client receives information about this voice server update. - * @param {string} guildID the ID of the Guild - * @param {string} token the token to authorise with - * @param {string} endpoint the endpoint to connect to + * @param {string} guildID The ID of the Guild + * @param {string} token The token to authorise with + * @param {string} endpoint The endpoint to connect to */ _receivedVoiceServer(guildID, token, endpoint) { const pendingRequest = this.pending.get(guildID); - if (!pendingRequest) { - throw new Error('Guild not pending'); - } + if (!pendingRequest) throw new Error('Guild not pending'); pendingRequest.token = token; // remove the port otherwise it errors ¯\_(ツ)_/¯ pendingRequest.endpoint = endpoint.match(/([^:]*)/)[0]; @@ -66,22 +62,20 @@ class ClientVoiceManager { /** * Called when the Client receives information about the voice state update. - * @param {string} guildID the ID of the Guild - * @param {string} sessionID the session id to authorise with + * @param {string} guildID The ID of the Guild + * @param {string} sessionID The session id to authorise with */ _receivedVoiceStateUpdate(guildID, sessionID) { const pendingRequest = this.pending.get(guildID); - if (!pendingRequest) { - throw new Error('Guild not pending'); - } + if (!pendingRequest) throw new Error('Guild not pending'); pendingRequest.sessionID = sessionID; this._checkPendingReady(guildID); } /** * Sends a request to the main gateway to join a voice channel - * @param {VoiceChannel} channel the channel to join - * @param {Object} [options] the options to provide + * @param {VoiceChannel} channel The channel to join + * @param {Object} [options] The options to provide */ _sendWSJoin(channel, options = {}) { options = mergeDefault({ @@ -98,14 +92,12 @@ class ClientVoiceManager { /** * Sets up a request to join a voice channel - * @param {VoiceChannel} channel the voice channel to join + * @param {VoiceChannel} channel The voice channel to join * @returns {Promise} */ joinChannel(channel) { return new Promise((resolve, reject) => { - if (this.pending.get(channel.guild.id)) { - throw new Error('already connecting to a channel in this guild'); - } + if (this.pending.get(channel.guild.id)) throw new Error('already connecting to a channel in this guild'); const existingConn = this.connections.get(channel.guild.id); if (existingConn) { if (existingConn.channel.id !== channel.id) { diff --git a/src/client/voice/VoiceConnection.js b/src/client/voice/VoiceConnection.js index b14f68bba..d719b4560 100644 --- a/src/client/voice/VoiceConnection.js +++ b/src/client/voice/VoiceConnection.js @@ -71,14 +71,14 @@ class VoiceConnection extends EventEmitter { /** * Executed whenever an error occurs with the UDP/WebSocket sub-client * @private - * @param {Error} err The error that occurred + * @param {Error} err The encountered error */ _onError(err) { this._reject(err); /** * Emitted whenever the connection encounters a fatal error. * @event VoiceConnection#error - * @param {Error} error the encountered error + * @param {Error} error The encountered error */ this.emit('error', err); this._shutdown(err); @@ -86,7 +86,7 @@ class VoiceConnection extends EventEmitter { /** * Disconnects the Client from the Voice Channel - * @param {string} [reason='user requested'] the reason of the disconnection + * @param {string} [reason='user requested'] The reason of the disconnection */ disconnect(reason = 'user requested') { this.manager.client.ws.send({ @@ -107,19 +107,15 @@ class VoiceConnection extends EventEmitter { } _shutdown(e) { - if (!this.ready) { - return; - } + if (!this.ready) return; this.ready = false; this.websocket._shutdown(); this.player._shutdown(); - if (this.udp) { - this.udp._shutdown(); - } + if (this.udp) this.udp._shutdown(); /** * Emit once the voice connection has disconnected. * @event VoiceConnection#disconnected - * @param {Error} error the error, if any + * @param {Error} error The encountered error, if any */ this.emit('disconnected', e); } @@ -149,9 +145,7 @@ class VoiceConnection extends EventEmitter { }); this.once('ready', () => { setImmediate(() => { - for (const item of this.queue) { - this.emit(...item); - } + for (const item of this.queue) this.emit(...item); this.queue = []; }); }); @@ -197,21 +191,18 @@ class VoiceConnection extends EventEmitter { /** * Emitted whenever a user starts/stops speaking * @event VoiceConnection#speaking - * @param {User} user the user that has started/stopped speaking - * @param {boolean} speaking whether or not the user is speaking + * @param {User} user The user that has started/stopped speaking + * @param {boolean} speaking Whether or not the user is speaking */ - if (this.ready) { - this.emit('speaking', user, data.speaking); - } else { - this.queue.push(['speaking', user, data.speaking]); - } + if (this.ready) this.emit('speaking', user, data.speaking); + else this.queue.push(['speaking', user, data.speaking]); guild._memberSpeakUpdate(data.user_id, data.speaking); }); } /** * Play the given file in the voice connection - * @param {string} file the path to the file + * @param {string} file The path to the file * @returns {StreamDispatcher} * @example * // play files natively @@ -227,7 +218,7 @@ class VoiceConnection extends EventEmitter { /** * Plays and converts an audio stream in the voice connection - * @param {ReadableStream} stream the audio stream to play + * @param {ReadableStream} stream The audio stream to play * @returns {StreamDispatcher} * @example * // play streams using ytdl-core @@ -245,7 +236,7 @@ class VoiceConnection extends EventEmitter { /** * Plays a stream of 16-bit signed stereo PCM at 48KHz. - * @param {ReadableStream} stream the audio stream to play. + * @param {ReadableStream} stream The audio stream to play. * @returns {StreamDispatcher} */ playConvertedStream(stream) { diff --git a/src/client/voice/VoiceConnectionUDPClient.js b/src/client/voice/VoiceConnectionUDPClient.js index 7bd058a02..add7b9c4f 100644 --- a/src/client/voice/VoiceConnectionUDPClient.js +++ b/src/client/voice/VoiceConnectionUDPClient.js @@ -4,7 +4,6 @@ const Constants = require('../../util/Constants'); const EventEmitter = require('events').EventEmitter; class VoiceConnectionUDPClient extends EventEmitter { - constructor(voiceConnection, data) { super(); this.voiceConnection = voiceConnection; @@ -38,9 +37,7 @@ class VoiceConnectionUDPClient extends EventEmitter { try { this.udpSocket.close(); } catch (err) { - if (err.message !== 'Not running') { - this.emit('error', err); - } + if (err.message !== 'Not running') this.emit('error', err); } this.udpSocket = null; } @@ -55,10 +52,7 @@ class VoiceConnectionUDPClient extends EventEmitter { this.udpSocket.once('message', message => { const packet = new Buffer(message); this.localIP = ''; - for (let i = 4; i < packet.indexOf(0, i); i++) { - this.localIP += String.fromCharCode(packet[i]); - } - + for (let i = 4; i < packet.indexOf(0, i); i++) this.localIP += String.fromCharCode(packet[i]); this.localPort = parseInt(packet.readUIntLE(packet.length - 2, 2).toString(10), 10); this.voiceConnection.websocket.send({ @@ -77,7 +71,6 @@ class VoiceConnectionUDPClient extends EventEmitter { this.udpSocket.on('error', (error, message) => { this.emit('error', { error, message }); }); - this.udpSocket.on('close', error => { this.emit('close', error); }); @@ -86,7 +79,6 @@ class VoiceConnectionUDPClient extends EventEmitter { blankMessage.writeUIntBE(this.data.ssrc, 0, 4); this.send(blankMessage); } - } module.exports = VoiceConnectionUDPClient; diff --git a/src/client/voice/VoiceConnectionWebSocket.js b/src/client/voice/VoiceConnectionWebSocket.js index 6e47b1bfc..1c4543eed 100644 --- a/src/client/voice/VoiceConnectionWebSocket.js +++ b/src/client/voice/VoiceConnectionWebSocket.js @@ -26,15 +26,11 @@ class VoiceConnectionWebSocket extends EventEmitter { } send(data) { - if (this.ws.readyState === WebSocket.OPEN) { - this.ws.send(JSON.stringify(data)); - } + if (this.ws.readyState === WebSocket.OPEN) this.ws.send(JSON.stringify(data)); } _shutdown() { - if (this.ws) { - this.ws.close(); - } + if (this.ws) this.ws.close(); clearInterval(this.heartbeat); } @@ -97,9 +93,7 @@ class VoiceConnectionWebSocket extends EventEmitter { case Constants.VoiceOPCodes.SESSION_DESCRIPTION: this.encryptionMode = packet.d.mode; this.secretKey = new Uint8Array(new ArrayBuffer(packet.d.secret_key.length)); - for (const index in packet.d.secret_key) { - this.secretKey[index] = packet.d.secret_key[index]; - } + for (const index in packet.d.secret_key) this.secretKey[index] = packet.d.secret_key[index]; this.emit('ready', this.secretKey); break; case Constants.VoiceOPCodes.SPEAKING: diff --git a/src/client/voice/dispatcher/StreamDispatcher.js b/src/client/voice/dispatcher/StreamDispatcher.js index 2408c235c..8088017f1 100644 --- a/src/client/voice/dispatcher/StreamDispatcher.js +++ b/src/client/voice/dispatcher/StreamDispatcher.js @@ -34,10 +34,10 @@ class StreamDispatcher extends EventEmitter { } /** - * Emitted when the dispatcher starts/stops speaking - * @event StreamDispatcher#speaking - * @param {boolean} value whether or not the dispatcher is speaking - */ + * Emitted when the dispatcher starts/stops speaking + * @event StreamDispatcher#speaking + * @param {boolean} value Whether or not the dispatcher is speaking + */ _setSpeaking(value) { this.speaking = value; this.emit('speaking', value); @@ -62,27 +62,18 @@ class StreamDispatcher extends EventEmitter { packetBuffer.copy(nonce, 0, 0, 12); buffer = NaCl.secretbox(buffer, nonce, this.player.connection.data.secret); - for (let i = 0; i < buffer.length; i++) { - packetBuffer[i + 12] = buffer[i]; - } + for (let i = 0; i < buffer.length; i++) packetBuffer[i + 12] = buffer[i]; return packetBuffer; } _applyVolume(buffer) { - if (this._volume === 1) { - return buffer; - } + if (this._volume === 1) return buffer; const out = new Buffer(buffer.length); - for (let i = 0; i < buffer.length; i += 2) { - if (i >= buffer.length - 1) { - break; - } - + if (i >= buffer.length - 1) break; const uint = Math.min(32767, Math.max(-32767, Math.floor(this._volume * buffer.readInt16LE(i)))); - out.writeInt16LE(uint, i); } @@ -95,20 +86,24 @@ class StreamDispatcher extends EventEmitter { this._setSpeaking(false); return; } + const data = this.streamingData; + if (data.missed >= 5) { this._triggerTerminalState('error', new Error('stream is not generating fast enough')); return; } + if (this.paused) { data.timestamp = data.timestamp + 4294967295 ? data.timestamp + 960 : 0; this.player.connection.manager.client.setTimeout(() => this._send(), data.length * 10); return; } - const bufferLength = 1920 * data.channels; - this._setSpeaking(true); - let buffer = this.stream.read(bufferLength); + this._setSpeaking(true); + + const bufferLength = 1920 * data.channels; + let buffer = this.stream.read(bufferLength); if (!buffer) { data.missed++; this.player.connection.manager.client.setTimeout(() => this._send(), data.length * 10); @@ -132,7 +127,6 @@ class StreamDispatcher extends EventEmitter { this._sendBuffer(buffer, data.sequence, data.timestamp); const nextTime = data.startTime + (data.count * data.length); - this.player.connection.manager.client.setTimeout(() => this._send(), data.length + (nextTime - Date.now())); } catch (e) { this._triggerTerminalState('error', e); @@ -140,33 +134,31 @@ class StreamDispatcher extends EventEmitter { } /** - * Emitted once the stream has ended. Attach a `once` listener to this. - * @event StreamDispatcher#end - */ + * Emitted once the stream has ended. Attach a `once` listener to this. + * @event StreamDispatcher#end + */ _triggerEnd() { this.emit('end'); } /** - * Emitted once the stream has encountered an error. Attach a `once` listener to this. Also emits `end`. - * @event StreamDispatcher#error - * @param {Error} err the error encountered - */ + * Emitted once the stream has encountered an error. Attach a `once` listener to this. Also emits `end`. + * @event StreamDispatcher#error + * @param {Error} err The encountered error + */ _triggerError(err) { this.emit('end'); this.emit('error', err); } _triggerTerminalState(state, err) { - if (this._triggered) { - return; - } + if (this._triggered) return; /** - * Emitted when the stream wants to give debug information. - * @event StreamDispatcher#debug - * @param {string} information the debug information - */ + * Emitted when the stream wants to give debug information. + * @event StreamDispatcher#debug + * @param {string} information The debug information + */ this.emit('debug', `triggered terminal state ${state} - stream is now dead`); this._triggered = true; this._setSpeaking(false); @@ -188,17 +180,20 @@ class StreamDispatcher extends EventEmitter { this.emit('error', 'no stream'); return; } + this.stream.on('end', err => this._triggerTerminalState('end', err)); this.stream.on('error', err => this._triggerTerminalState('error', err)); + const data = this.streamingData; data.length = 20; data.missed = 0; data.startTime = Date.now(); + this.stream.once('readable', () => this._send()); } - _pause(value) { - if (value) { + _setPaused(paused) { + if (paused) { this.paused = true; this._setSpeaking(false); } else { @@ -225,7 +220,7 @@ class StreamDispatcher extends EventEmitter { /** * Sets the volume relative to the input stream - i.e. 1 is normal, 0.5 is half, 2 is double. - * @param {number} volume the volume that you want to set + * @param {number} volume The volume that you want to set */ setVolume(volume) { this._volume = volume; @@ -233,7 +228,7 @@ class StreamDispatcher extends EventEmitter { /** * Set the volume in decibels - * @param {number} db the decibels + * @param {number} db The decibels */ setVolumeDecibels(db) { this._volume = Math.pow(10, db / 20); @@ -241,7 +236,7 @@ class StreamDispatcher extends EventEmitter { /** * Set the volume so that a perceived value of 0.5 is half the perceived volume etc. - * @param {number} value the value for the volume + * @param {number} value The value for the volume */ setVolumeLogarithmic(value) { this._volume = Math.pow(value, 1.660964); @@ -251,14 +246,14 @@ class StreamDispatcher extends EventEmitter { * Stops sending voice packets to the voice connection (stream may still progress however) */ pause() { - this._pause(true); + this._setPaused(true); } /** * Resumes sending voice packets to the voice connection (may be further on in the stream than when paused) */ resume() { - this._pause(false); + this._setPaused(false); } } diff --git a/src/client/voice/opus/NodeOpusEngine.js b/src/client/voice/opus/NodeOpusEngine.js index 52d1d7b0e..10f287b9d 100644 --- a/src/client/voice/opus/NodeOpusEngine.js +++ b/src/client/voice/opus/NodeOpusEngine.js @@ -6,7 +6,6 @@ class NodeOpusEngine extends OpusEngine { constructor(player) { super(player); try { - // eslint-disable-next-line import/no-unresolved opus = require('node-opus'); } catch (err) { throw err; diff --git a/src/client/voice/opus/OpusEngineList.js b/src/client/voice/opus/OpusEngineList.js index 89b9aa07a..a7f225f22 100644 --- a/src/client/voice/opus/OpusEngineList.js +++ b/src/client/voice/opus/OpusEngineList.js @@ -3,10 +3,6 @@ const list = [ require('./OpusScriptEngine'), ]; -exports.add = encoder => { - list.push(encoder); -}; - function fetch(Encoder) { try { return new Encoder(); @@ -15,12 +11,14 @@ function fetch(Encoder) { } } +exports.add = encoder => { + list.push(encoder); +}; + exports.fetch = () => { for (const encoder of list) { - const success = fetch(encoder); - if (success) { - return success; - } + const fetched = fetch(encoder); + if (fetched) return fetched; } throw new Error('could not find an opus engine'); }; diff --git a/src/client/voice/opus/OpusScriptEngine.js b/src/client/voice/opus/OpusScriptEngine.js index 79df01163..33b4ff5a6 100644 --- a/src/client/voice/opus/OpusScriptEngine.js +++ b/src/client/voice/opus/OpusScriptEngine.js @@ -1,17 +1,16 @@ const OpusEngine = require('./BaseOpusEngine'); -let Opusscript; +let OpusScript; class NodeOpusEngine extends OpusEngine { constructor(player) { super(player); try { - // eslint-disable-next-line import/no-unresolved - Opusscript = require('opusscript'); + OpusScript = require('opusscript'); } catch (err) { throw err; } - this.encoder = new Opusscript(48000, 2); + this.encoder = new OpusScript(48000, 2); } encode(buffer) { diff --git a/src/client/voice/pcm/ConverterEngine.js b/src/client/voice/pcm/ConverterEngine.js index d8d2d424e..6b7502f90 100644 --- a/src/client/voice/pcm/ConverterEngine.js +++ b/src/client/voice/pcm/ConverterEngine.js @@ -1,7 +1,6 @@ const EventEmitter = require('events').EventEmitter; class ConverterEngine extends EventEmitter { - constructor(player) { super(); this.player = player; @@ -10,7 +9,6 @@ class ConverterEngine extends EventEmitter { createConvertStream() { return; } - } module.exports = ConverterEngine; diff --git a/src/client/voice/pcm/FfmpegConverterEngine.js b/src/client/voice/pcm/FfmpegConverterEngine.js index b5dd7e389..64dca3692 100644 --- a/src/client/voice/pcm/FfmpegConverterEngine.js +++ b/src/client/voice/pcm/FfmpegConverterEngine.js @@ -1,15 +1,6 @@ const ConverterEngine = require('./ConverterEngine'); const ChildProcess = require('child_process'); -function chooseCommand() { - for (const cmd of ['ffmpeg', 'avconv', './ffmpeg', './avconv']) { - if (!ChildProcess.spawnSync(cmd, ['-h']).error) { - return cmd; - } - } - return null; -} - class FfmpegConverterEngine extends ConverterEngine { constructor(player) { super(player); @@ -17,9 +8,7 @@ class FfmpegConverterEngine extends ConverterEngine { } handleError(encoder, err) { - if (encoder.destroy) { - encoder.destroy(); - } + if (encoder.destroy) encoder.destroy(); this.emit('error', err); } @@ -41,4 +30,11 @@ class FfmpegConverterEngine extends ConverterEngine { } } +function chooseCommand() { + for (const cmd of ['ffmpeg', 'avconv', './ffmpeg', './avconv']) { + if (!ChildProcess.spawnSync(cmd, ['-h']).error) return cmd; + } + return null; +} + module.exports = FfmpegConverterEngine; diff --git a/src/client/voice/player/BasePlayer.js b/src/client/voice/player/BasePlayer.js index aa0a35dca..e41048d0a 100644 --- a/src/client/voice/player/BasePlayer.js +++ b/src/client/voice/player/BasePlayer.js @@ -5,7 +5,6 @@ const StreamDispatcher = require('../dispatcher/StreamDispatcher'); const EventEmitter = require('events').EventEmitter; class VoiceConnectionPlayer extends EventEmitter { - constructor(connection) { super(); this.connection = connection; @@ -38,9 +37,7 @@ class VoiceConnectionPlayer extends EventEmitter { _shutdown() { this.speaking = false; - for (const stream of this.processMap.keys()) { - this.killStream(stream); - } + for (const stream of this.processMap.keys()) this.killStream(stream); } killStream(stream) { @@ -79,9 +76,7 @@ class VoiceConnectionPlayer extends EventEmitter { } setSpeaking(value) { - if (this.speaking === value) { - return; - } + if (this.speaking === value) return; this.speaking = value; this.connection.websocket.send({ op: Constants.VoiceOPCodes.SPEAKING, @@ -100,7 +95,6 @@ class VoiceConnectionPlayer extends EventEmitter { this.dispatcher = dispatcher; return dispatcher; } - } module.exports = VoiceConnectionPlayer; diff --git a/src/client/voice/player/DefaultPlayer.js b/src/client/voice/player/DefaultPlayer.js index 383e0668b..bd3b688bc 100644 --- a/src/client/voice/player/DefaultPlayer.js +++ b/src/client/voice/player/DefaultPlayer.js @@ -2,7 +2,6 @@ const BasePlayer = require('./BasePlayer'); const fs = require('fs'); class DefaultPlayer extends BasePlayer { - playFile(file) { return this.playStream(fs.createReadStream(file)); } diff --git a/src/client/voice/receiver/VoiceReadable.js b/src/client/voice/receiver/VoiceReadable.js index f68b7f458..50ace27ae 100644 --- a/src/client/voice/receiver/VoiceReadable.js +++ b/src/client/voice/receiver/VoiceReadable.js @@ -11,10 +11,8 @@ class VoiceReadable extends Readable { return; } - $push(d) { - if (this.open) { - this.push(d); - } + _push(d) { + if (this.open) this.push(d); } } diff --git a/src/client/voice/receiver/VoiceReceiver.js b/src/client/voice/receiver/VoiceReceiver.js index 6862378f8..f28fbe463 100644 --- a/src/client/voice/receiver/VoiceReceiver.js +++ b/src/client/voice/receiver/VoiceReceiver.js @@ -53,7 +53,7 @@ class VoiceReceiver extends EventEmitter { /** * Creates a readable stream for a user that provides opus data while the user is speaking. When the user * stops speaking, the stream is destroyed. - * @param {UserResolvable} user the user to create the stream for + * @param {UserResolvable} user The user to create the stream for * @returns {ReadableStream} */ createOpusStream(user) { @@ -72,17 +72,13 @@ class VoiceReceiver extends EventEmitter { /** * Creates a readable stream for a user that provides PCM data while the user is speaking. When the user * stops speaking, the stream is destroyed. The stream is 16-bit signed stereo PCM at 48KHz. - * @param {UserResolvable} user the user to create the stream for + * @param {UserResolvable} user The user to create the stream for * @returns {ReadableStream} */ createPCMStream(user) { user = this.connection.manager.client.resolver.resolveUser(user); - if (!user) { - throw new Error('invalid user object supplied'); - } - if (this.pcmStreams.get(user.id)) { - throw new Error('there is already an existing stream for that user!'); - } + if (!user) throw new Error('invalid user object supplied'); + if (this.pcmStreams.get(user.id)) throw new Error('there is already an existing stream for that user!'); const stream = new Readable(); this.pcmStreams.set(user.id, stream); return stream; @@ -95,34 +91,30 @@ class VoiceReceiver extends EventEmitter { /** * Emitted whenever a voice packet cannot be decrypted * @event VoiceReceiver#warn - * @param {string} message the warning message + * @param {string} message The warning message */ this.emit('warn', 'failed to decrypt voice packet'); return; } data = new Buffer(data); - /** - * Emitted whenever voice data is received from the voice connection. This is _always_ emitted (unlike PCM). - * @event VoiceReceiver#opus - * @param {User} user the user that is sending the buffer (is speaking) - * @param {Buffer} buffer the opus buffer - */ - if (this.opusStreams.get(user.id)) { - this.opusStreams.get(user.id).$push(data); - } + if (this.opusStreams.get(user.id)) this.opusStreams.get(user.id)._push(data); + /** + * Emitted whenever voice data is received from the voice connection. This is _always_ emitted (unlike PCM). + * @event VoiceReceiver#opus + * @param {User} user The user that is sending the buffer (is speaking) + * @param {Buffer} buffer The opus buffer + */ this.emit('opus', user, data); if (this.listenerCount('pcm') > 0 || this.pcmStreams.size > 0) { /** - * Emits decoded voice data when it's received. For performance reasons, the decoding will only - * happen if there is at least one `pcm` listener on this receiver. - * @event VoiceReceiver#pcm - * @param {User} user the user that is sending the buffer (is speaking) - * @param {Buffer} buffer the decoded buffer - */ + * Emits decoded voice data when it's received. For performance reasons, the decoding will only + * happen if there is at least one `pcm` listener on this receiver. + * @event VoiceReceiver#pcm + * @param {User} user The user that is sending the buffer (is speaking) + * @param {Buffer} buffer The decoded buffer + */ const pcm = this.connection.player.opusEncoder.decode(data); - if (this.pcmStreams.get(user.id)) { - this.pcmStreams.get(user.id).$push(pcm); - } + if (this.pcmStreams.get(user.id)) this.pcmStreams.get(user.id)._push(pcm); this.emit('pcm', user, pcm); } } diff --git a/src/client/websocket/WebSocketManager.js b/src/client/websocket/WebSocketManager.js index 22e84da56..7bdf08294 100644 --- a/src/client/websocket/WebSocketManager.js +++ b/src/client/websocket/WebSocketManager.js @@ -8,14 +8,12 @@ const PacketManager = require('./packets/WebSocketPacketManager'); * @private */ class WebSocketManager { - constructor(client) { /** * The Client that instantiated this WebSocketManager * @type {Client} */ this.client = client; - this.ws = null; /** * A WebSocket Packet manager, it handles all the messages * @type {PacketManager} @@ -26,43 +24,40 @@ class WebSocketManager { * @type {number} */ this.status = Constants.Status.IDLE; - /** * The session ID of the connection, null if not yet available. * @type {?string} */ this.sessionID = null; - /** * The packet count of the client, null if not yet available. * @type {?number} */ this.sequence = -1; - /** * The gateway address for this WebSocket connection, null if not yet available. * @type {?string} */ this.gateway = null; - /** * Whether READY was emitted normally (all packets received) or not * @type {boolean} */ this.normalReady = false; - } - - /** - * Connects the client to a given gateway - * @param {string} gateway the gateway to connect to - */ - connect(gateway) { - this.normalReady = false; - this.status = Constants.Status.CONNECTING; /** * The WebSocket connection to the gateway * @type {?WebSocket} */ + this.ws = null; + } + + /** + * Connects the client to a given gateway + * @param {string} gateway The gateway to connect to + */ + connect(gateway) { + this.normalReady = false; + this.status = Constants.Status.CONNECTING; this.ws = new WebSocket(gateway); this.ws.onopen = () => this.eventOpen(); this.ws.onclose = (d) => this.eventClose(d); @@ -113,15 +108,12 @@ class WebSocketManager { * Run whenever the gateway connections opens up */ eventOpen() { - if (this.reconnecting) { - this._sendResume(); - } else { - this._sendNewIdentify(); - } + if (this.reconnecting) this._sendResume(); + else this._sendNewIdentify(); } /** - * Sends a gatway resume packet, in cases of unexpected disconnections. + * Sends a gateway resume packet, in cases of unexpected disconnections. */ _sendResume() { const payload = { @@ -155,30 +147,23 @@ class WebSocketManager { /** * Run whenever the connection to the gateway is closed, it will try to reconnect the client. - * @param {Object} event the event + * @param {Object} event The received websocket data */ eventClose(event) { - if (event.code === 4004) { - throw Constants.Errors.BAD_LOGIN; - } - if (!this.reconnecting && event.code !== 1000) { - this.tryReconnect(); - } + if (event.code === 4004) throw Constants.Errors.BAD_LOGIN; + if (!this.reconnecting && event.code !== 1000) this.tryReconnect(); } /** * Run whenever a message is received from the WebSocket. Returns `true` if the message * was handled properly. - * @param {Object} event the received websocket data + * @param {Object} event The received websocket data * @returns {boolean} */ eventMessage(event) { let packet; try { - if (event.binary) { - event.data = zlib.inflateSync(event.data).toString(); - } - + if (event.binary) event.data = zlib.inflateSync(event.data).toString(); packet = JSON.parse(event.data); } catch (e) { return this.eventError(Constants.Errors.BAD_WS_MESSAGE); @@ -186,22 +171,19 @@ class WebSocketManager { this.client.emit('raw', packet); - if (packet.op === 10) { - this.client.manager.setupKeepAlive(packet.d.heartbeat_interval); - } - + if (packet.op === 10) this.client.manager.setupKeepAlive(packet.d.heartbeat_interval); return this.packetManager.handle(packet); } /** * Run whenever an error occurs with the WebSocket connection. Tries to reconnect - * @param {Error} err the error that occurred + * @param {Error} err The encountered error */ eventError(err) { /** * Emitted whenever the Client encounters a serious connection error * @event Client#error - * @param {Error} error the encountered error + * @param {Error} error The encountered error */ this.client.emit('error', err); this.tryReconnect(); @@ -210,7 +192,6 @@ class WebSocketManager { _emitReady(normal = true) { /** * Emitted when the Client becomes ready to start working - * * @event Client#ready */ this.status = Constants.Status.READY; @@ -252,10 +233,9 @@ class WebSocketManager { this.ws.close(); this.packetManager.handleQueue(); /** - * Emitted when the Client tries to reconnect after being disconnected - * - * @event Client#reconnecting - */ + * Emitted when the Client tries to reconnect after being disconnected + * @event Client#reconnecting + */ this.client.emit(Constants.Events.RECONNECTING); this.connect(this.client.ws.gateway); } diff --git a/src/client/websocket/packets/WebSocketPacketManager.js b/src/client/websocket/packets/WebSocketPacketManager.js index 2aeb1d531..bf81fd538 100644 --- a/src/client/websocket/packets/WebSocketPacketManager.js +++ b/src/client/websocket/packets/WebSocketPacketManager.js @@ -10,7 +10,6 @@ const BeforeReadyWhitelist = [ ]; class WebSocketPacketManager { - constructor(websocketManager) { this.ws = websocketManager; this.handlers = {}; @@ -62,9 +61,7 @@ class WebSocketPacketManager { } setSequence(s) { - if (s && s > this.ws.sequence) { - this.ws.sequence = s; - } + if (s && s > this.ws.sequence) this.ws.sequence = s; } handle(packet) { @@ -93,13 +90,9 @@ class WebSocketPacketManager { } } - if (this.handlers[packet.t]) { - return this.handlers[packet.t].handle(packet); - } - + if (this.handlers[packet.t]) return this.handlers[packet.t].handle(packet); return false; } - } module.exports = WebSocketPacketManager; diff --git a/src/client/websocket/packets/handlers/AbstractHandler.js b/src/client/websocket/packets/handlers/AbstractHandler.js index ec744c487..c1c2a5a2d 100644 --- a/src/client/websocket/packets/handlers/AbstractHandler.js +++ b/src/client/websocket/packets/handlers/AbstractHandler.js @@ -1,5 +1,4 @@ class AbstractHandler { - constructor(packetManager) { this.packetManager = packetManager; } diff --git a/src/client/websocket/packets/handlers/ChannelCreate.js b/src/client/websocket/packets/handlers/ChannelCreate.js index e4c36db14..166371642 100644 --- a/src/client/websocket/packets/handlers/ChannelCreate.js +++ b/src/client/websocket/packets/handlers/ChannelCreate.js @@ -3,25 +3,18 @@ const AbstractHandler = require('./AbstractHandler'); const Constants = require('../../../../util/Constants'); class ChannelCreateHandler extends AbstractHandler { - handle(packet) { - const data = packet.d; const client = this.packetManager.client; - + const data = packet.d; const response = client.actions.ChannelCreate.handle(data); - - if (response.channel) { - client.emit(Constants.Events.CHANNEL_CREATE, response.channel); - } + if (response.channel) client.emit(Constants.Events.CHANNEL_CREATE, response.channel); } - } /** -* Emitted whenever a Channel is created. -* -* @event Client#channelCreate -* @param {Channel} channel The channel that was created -*/ + * Emitted whenever a Channel is created. + * @event Client#channelCreate + * @param {Channel} channel The channel that was created + */ module.exports = ChannelCreateHandler; diff --git a/src/client/websocket/packets/handlers/ChannelDelete.js b/src/client/websocket/packets/handlers/ChannelDelete.js index b93ca51c1..ec49df0d5 100644 --- a/src/client/websocket/packets/handlers/ChannelDelete.js +++ b/src/client/websocket/packets/handlers/ChannelDelete.js @@ -3,25 +3,18 @@ const AbstractHandler = require('./AbstractHandler'); const Constants = require('../../../../util/Constants'); class ChannelDeleteHandler extends AbstractHandler { - handle(packet) { - const data = packet.d; const client = this.packetManager.client; - + const data = packet.d; const response = client.actions.ChannelDelete.handle(data); - - if (response.channel) { - client.emit(Constants.Events.CHANNEL_DELETE, response.channel); - } + if (response.channel) client.emit(Constants.Events.CHANNEL_DELETE, response.channel); } - } /** -* Emitted whenever a Channel is deleted. -* -* @event Client#channelDelete -* @param {Channel} channel The channel that was deleted -*/ + * Emitted whenever a Channel is deleted. + * @event Client#channelDelete + * @param {Channel} channel The channel that was deleted + */ module.exports = ChannelDeleteHandler; diff --git a/src/client/websocket/packets/handlers/ChannelPinsUpdate.js b/src/client/websocket/packets/handlers/ChannelPinsUpdate.js index c597a04fa..65d862959 100644 --- a/src/client/websocket/packets/handlers/ChannelPinsUpdate.js +++ b/src/client/websocket/packets/handlers/ChannelPinsUpdate.js @@ -8,31 +8,24 @@ const Constants = require('../../../../util/Constants'); d: { last_pin_timestamp: '2016-08-28T17:37:13.171774+00:00', channel_id: '314866471639044027' } } - */ +*/ class ChannelPinsUpdate extends AbstractHandler { - handle(packet) { - const data = packet.d; const client = this.packetManager.client; - + const data = packet.d; const channel = client.channels.get(data.channel_id); const time = new Date(data.last_pin_timestamp); - - if (channel && time) { - client.emit(Constants.Events.CHANNEL_PINS_UPDATE, channel, time); - } + if (channel && time) client.emit(Constants.Events.CHANNEL_PINS_UPDATE, channel, time); } - } /** -* Emitted whenever the pins of a Channel are updated. Due to the nature of the WebSocket event, not much information -* can be provided easily here - you need to manually check the pins yourself. -* -* @event Client#channelPinsUpdate -* @param {Channel} channel The channel that the pins update occured in -* @param {Date} time the time of the pins update -*/ + * Emitted whenever the pins of a Channel are updated. Due to the nature of the WebSocket event, not much information + * can be provided easily here - you need to manually check the pins yourself. + * @event Client#channelPinsUpdate + * @param {Channel} channel The channel that the pins update occured in + * @param {Date} time The time of the pins update + */ module.exports = ChannelPinsUpdate; diff --git a/src/client/websocket/packets/handlers/ChannelUpdate.js b/src/client/websocket/packets/handlers/ChannelUpdate.js index 070cdcd24..fa535b143 100644 --- a/src/client/websocket/packets/handlers/ChannelUpdate.js +++ b/src/client/websocket/packets/handlers/ChannelUpdate.js @@ -1,14 +1,11 @@ const AbstractHandler = require('./AbstractHandler'); class ChannelUpdateHandler extends AbstractHandler { - handle(packet) { - const data = packet.d; const client = this.packetManager.client; - + const data = packet.d; client.actions.ChannelUpdate.handle(data); } - } module.exports = ChannelUpdateHandler; diff --git a/src/client/websocket/packets/handlers/GuildBanAdd.js b/src/client/websocket/packets/handlers/GuildBanAdd.js index 400651f75..60ce72d0a 100644 --- a/src/client/websocket/packets/handlers/GuildBanAdd.js +++ b/src/client/websocket/packets/handlers/GuildBanAdd.js @@ -4,27 +4,20 @@ const AbstractHandler = require('./AbstractHandler'); const Constants = require('../../../../util/Constants'); class GuildBanAddHandler extends AbstractHandler { - handle(packet) { - const data = packet.d; const client = this.packetManager.client; - + const data = packet.d; const guild = client.guilds.get(data.guild_id); const user = client.users.get(data.user.id); - - if (guild && user) { - client.emit(Constants.Events.GUILD_BAN_ADD, guild, user); - } + if (guild && user) client.emit(Constants.Events.GUILD_BAN_ADD, guild, user); } - } /** -* Emitted whenever a member is banned from a guild. -* -* @event Client#guildBanAdd -* @param {Guild} guild The guild that the ban occurred in -* @param {User} user The user that was banned -*/ + * Emitted whenever a member is banned from a guild. + * @event Client#guildBanAdd + * @param {Guild} guild The guild that the ban occurred in + * @param {User} user The user that was banned + */ module.exports = GuildBanAddHandler; diff --git a/src/client/websocket/packets/handlers/GuildBanRemove.js b/src/client/websocket/packets/handlers/GuildBanRemove.js index 18aba54b4..c4edbdeb6 100644 --- a/src/client/websocket/packets/handlers/GuildBanRemove.js +++ b/src/client/websocket/packets/handlers/GuildBanRemove.js @@ -3,22 +3,18 @@ const AbstractHandler = require('./AbstractHandler'); class GuildBanRemoveHandler extends AbstractHandler { - handle(packet) { - const data = packet.d; const client = this.packetManager.client; - + const data = packet.d; client.actions.GuildBanRemove.handle(data); } - } /** -* Emitted whenever a member is unbanned from a guild. -* -* @event Client#guildBanRemove -* @param {Guild} guild The guild that the unban occurred in -* @param {User} user The user that was unbanned -*/ + * Emitted whenever a member is unbanned from a guild. + * @event Client#guildBanRemove + * @param {Guild} guild The guild that the unban occurred in + * @param {User} user The user that was unbanned + */ module.exports = GuildBanRemoveHandler; diff --git a/src/client/websocket/packets/handlers/GuildCreate.js b/src/client/websocket/packets/handlers/GuildCreate.js index d3bac1c7e..c7fbd7e7c 100644 --- a/src/client/websocket/packets/handlers/GuildCreate.js +++ b/src/client/websocket/packets/handlers/GuildCreate.js @@ -1,13 +1,11 @@ const AbstractHandler = require('./AbstractHandler'); class GuildCreateHandler extends AbstractHandler { - handle(packet) { - const data = packet.d; const client = this.packetManager.client; + const data = packet.d; const guild = client.guilds.get(data.id); - if (guild) { if (!guild.available && !data.unavailable) { // a newly available guild @@ -19,7 +17,6 @@ class GuildCreateHandler extends AbstractHandler { client.dataManager.newGuild(data); } } - } module.exports = GuildCreateHandler; diff --git a/src/client/websocket/packets/handlers/GuildDelete.js b/src/client/websocket/packets/handlers/GuildDelete.js index 237652d7f..9b74d56f6 100644 --- a/src/client/websocket/packets/handlers/GuildDelete.js +++ b/src/client/websocket/packets/handlers/GuildDelete.js @@ -2,25 +2,18 @@ const AbstractHandler = require('./AbstractHandler'); const Constants = require('../../../../util/Constants'); class GuildDeleteHandler extends AbstractHandler { - handle(packet) { - const data = packet.d; const client = this.packetManager.client; - + const data = packet.d; const response = client.actions.GuildDelete.handle(data); - - if (response.guild) { - client.emit(Constants.Events.GUILD_DELETE, response.guild); - } + if (response.guild) client.emit(Constants.Events.GUILD_DELETE, response.guild); } - } /** -* Emitted whenever a Guild is deleted/left. -* -* @event Client#guildDelete -* @param {Guild} guild The guild that was deleted -*/ + * Emitted whenever a Guild is deleted/left. + * @event Client#guildDelete + * @param {Guild} guild The guild that was deleted + */ module.exports = GuildDeleteHandler; diff --git a/src/client/websocket/packets/handlers/GuildMemberAdd.js b/src/client/websocket/packets/handlers/GuildMemberAdd.js index cfdb6a263..d4d122f70 100644 --- a/src/client/websocket/packets/handlers/GuildMemberAdd.js +++ b/src/client/websocket/packets/handlers/GuildMemberAdd.js @@ -3,19 +3,15 @@ const AbstractHandler = require('./AbstractHandler'); class GuildMemberAddHandler extends AbstractHandler { - handle(packet) { - const data = packet.d; const client = this.packetManager.client; - + const data = packet.d; const guild = client.guilds.get(data.guild_id); - if (guild) { guild.memberCount++; guild._addMember(data); } } - } module.exports = GuildMemberAddHandler; diff --git a/src/client/websocket/packets/handlers/GuildMemberRemove.js b/src/client/websocket/packets/handlers/GuildMemberRemove.js index 2f8571e58..6ec1bfe64 100644 --- a/src/client/websocket/packets/handlers/GuildMemberRemove.js +++ b/src/client/websocket/packets/handlers/GuildMemberRemove.js @@ -3,14 +3,11 @@ const AbstractHandler = require('./AbstractHandler'); class GuildMemberRemoveHandler extends AbstractHandler { - handle(packet) { - const data = packet.d; const client = this.packetManager.client; - + const data = packet.d; client.actions.GuildMemberRemove.handle(data); } - } module.exports = GuildMemberRemoveHandler; diff --git a/src/client/websocket/packets/handlers/GuildMemberUpdate.js b/src/client/websocket/packets/handlers/GuildMemberUpdate.js index cca3422c0..94ac71f47 100644 --- a/src/client/websocket/packets/handlers/GuildMemberUpdate.js +++ b/src/client/websocket/packets/handlers/GuildMemberUpdate.js @@ -3,21 +3,16 @@ const AbstractHandler = require('./AbstractHandler'); class GuildMemberUpdateHandler extends AbstractHandler { - handle(packet) { - const data = packet.d; const client = this.packetManager.client; + const data = packet.d; const guild = client.guilds.get(data.guild_id); - if (guild) { const member = guild.members.get(data.user.id); - if (member) { - guild._updateMember(member, data); - } + if (member) guild._updateMember(member, data); } } - } module.exports = GuildMemberUpdateHandler; diff --git a/src/client/websocket/packets/handlers/GuildMembersChunk.js b/src/client/websocket/packets/handlers/GuildMembersChunk.js index bca64ed99..532d73bed 100644 --- a/src/client/websocket/packets/handlers/GuildMembersChunk.js +++ b/src/client/websocket/packets/handlers/GuildMembersChunk.js @@ -4,31 +4,26 @@ const AbstractHandler = require('./AbstractHandler'); const Constants = require('../../../../util/Constants'); class GuildMembersChunkHandler extends AbstractHandler { - handle(packet) { - const data = packet.d; const client = this.packetManager.client; + const data = packet.d; const guild = client.guilds.get(data.guild_id); const members = []; if (guild) { - for (const member of data.members) { - members.push(guild._addMember(member, true)); - } + for (const member of data.members) members.push(guild._addMember(member, true)); } guild._checkChunks(); client.emit(Constants.Events.GUILD_MEMBERS_CHUNK, guild, members); } - } /** -* Emitted whenever a chunk of Guild members is received -* -* @event Client#guildMembersChunk -* @param {Guild} guild The guild that the chunks relate to -* @param {Array} members The members in the chunk -*/ + * Emitted whenever a chunk of Guild members is received + * @event Client#guildMembersChunk + * @param {Guild} guild The guild that the chunks relate to + * @param {GuildMember[]} members The members in the chunk + */ module.exports = GuildMembersChunkHandler; diff --git a/src/client/websocket/packets/handlers/GuildRoleCreate.js b/src/client/websocket/packets/handlers/GuildRoleCreate.js index 6b791bfb3..8581d53f6 100644 --- a/src/client/websocket/packets/handlers/GuildRoleCreate.js +++ b/src/client/websocket/packets/handlers/GuildRoleCreate.js @@ -1,14 +1,11 @@ const AbstractHandler = require('./AbstractHandler'); class GuildRoleCreateHandler extends AbstractHandler { - handle(packet) { - const data = packet.d; const client = this.packetManager.client; - + const data = packet.d; client.actions.GuildRoleCreate.handle(data); } - } module.exports = GuildRoleCreateHandler; diff --git a/src/client/websocket/packets/handlers/GuildRoleDelete.js b/src/client/websocket/packets/handlers/GuildRoleDelete.js index 17135e20a..63439b0fe 100644 --- a/src/client/websocket/packets/handlers/GuildRoleDelete.js +++ b/src/client/websocket/packets/handlers/GuildRoleDelete.js @@ -1,14 +1,11 @@ const AbstractHandler = require('./AbstractHandler'); class GuildRoleDeleteHandler extends AbstractHandler { - handle(packet) { - const data = packet.d; const client = this.packetManager.client; - + const data = packet.d; client.actions.GuildRoleDelete.handle(data); } - } module.exports = GuildRoleDeleteHandler; diff --git a/src/client/websocket/packets/handlers/GuildRoleUpdate.js b/src/client/websocket/packets/handlers/GuildRoleUpdate.js index 8fea7271f..6fbdc109a 100644 --- a/src/client/websocket/packets/handlers/GuildRoleUpdate.js +++ b/src/client/websocket/packets/handlers/GuildRoleUpdate.js @@ -1,14 +1,11 @@ const AbstractHandler = require('./AbstractHandler'); class GuildRoleUpdateHandler extends AbstractHandler { - handle(packet) { - const data = packet.d; const client = this.packetManager.client; - + const data = packet.d; client.actions.GuildRoleUpdate.handle(data); } - } module.exports = GuildRoleUpdateHandler; diff --git a/src/client/websocket/packets/handlers/GuildSync.js b/src/client/websocket/packets/handlers/GuildSync.js index 941ea9f15..0b9f5aa86 100644 --- a/src/client/websocket/packets/handlers/GuildSync.js +++ b/src/client/websocket/packets/handlers/GuildSync.js @@ -1,14 +1,11 @@ const AbstractHandler = require('./AbstractHandler'); class GuildSyncHandler extends AbstractHandler { - handle(packet) { - const data = packet.d; const client = this.packetManager.client; - + const data = packet.d; client.actions.GuildSync.handle(data); } - } module.exports = GuildSyncHandler; diff --git a/src/client/websocket/packets/handlers/GuildUpdate.js b/src/client/websocket/packets/handlers/GuildUpdate.js index b022fb688..70eff52c4 100644 --- a/src/client/websocket/packets/handlers/GuildUpdate.js +++ b/src/client/websocket/packets/handlers/GuildUpdate.js @@ -1,14 +1,11 @@ const AbstractHandler = require('./AbstractHandler'); class GuildUpdateHandler extends AbstractHandler { - handle(packet) { - const data = packet.d; const client = this.packetManager.client; - + const data = packet.d; client.actions.GuildUpdate.handle(data); } - } module.exports = GuildUpdateHandler; diff --git a/src/client/websocket/packets/handlers/MessageCreate.js b/src/client/websocket/packets/handlers/MessageCreate.js index 25de28166..058dc854e 100644 --- a/src/client/websocket/packets/handlers/MessageCreate.js +++ b/src/client/websocket/packets/handlers/MessageCreate.js @@ -2,25 +2,18 @@ const AbstractHandler = require('./AbstractHandler'); const Constants = require('../../../../util/Constants'); class MessageCreateHandler extends AbstractHandler { - handle(packet) { - const data = packet.d; const client = this.packetManager.client; - + const data = packet.d; const response = client.actions.MessageCreate.handle(data); - - if (response.message) { - client.emit(Constants.Events.MESSAGE_CREATE, response.message); - } + if (response.message) client.emit(Constants.Events.MESSAGE_CREATE, response.message); } - } /** -* Emitted whenever a message is created -* -* @event Client#message -* @param {Message} message The created message -*/ + * Emitted whenever a message is created + * @event Client#message + * @param {Message} message The created message + */ module.exports = MessageCreateHandler; diff --git a/src/client/websocket/packets/handlers/MessageDelete.js b/src/client/websocket/packets/handlers/MessageDelete.js index 7acc80114..b06ce9887 100644 --- a/src/client/websocket/packets/handlers/MessageDelete.js +++ b/src/client/websocket/packets/handlers/MessageDelete.js @@ -2,25 +2,18 @@ const AbstractHandler = require('./AbstractHandler'); const Constants = require('../../../../util/Constants'); class MessageDeleteHandler extends AbstractHandler { - handle(packet) { - const data = packet.d; const client = this.packetManager.client; - + const data = packet.d; const response = client.actions.MessageDelete.handle(data); - - if (response.message) { - client.emit(Constants.Events.MESSAGE_DELETE, response.message); - } + if (response.message) client.emit(Constants.Events.MESSAGE_DELETE, response.message); } - } /** -* Emitted whenever a message is deleted -* -* @event Client#messageDelete -* @param {Message} message The deleted message -*/ + * Emitted whenever a message is deleted + * @event Client#messageDelete + * @param {Message} message The deleted message + */ module.exports = MessageDeleteHandler; diff --git a/src/client/websocket/packets/handlers/MessageDeleteBulk.js b/src/client/websocket/packets/handlers/MessageDeleteBulk.js index b1da3b777..6cd36484e 100644 --- a/src/client/websocket/packets/handlers/MessageDeleteBulk.js +++ b/src/client/websocket/packets/handlers/MessageDeleteBulk.js @@ -1,21 +1,17 @@ const AbstractHandler = require('./AbstractHandler'); class MessageDeleteBulkHandler extends AbstractHandler { - handle(packet) { - const data = packet.d; const client = this.packetManager.client; - + const data = packet.d; client.actions.MessageDeleteBulk.handle(data); } - } /** -* Emitted whenever a messages are deleted in bulk -* -* @event Client#messageDeleteBulk -* @param {Collection} messages The deleted messages, mapped by their ID -*/ + * Emitted whenever messages are deleted in bulk + * @event Client#messageDeleteBulk + * @param {Collection} messages The deleted messages, mapped by their ID + */ module.exports = MessageDeleteBulkHandler; diff --git a/src/client/websocket/packets/handlers/MessageUpdate.js b/src/client/websocket/packets/handlers/MessageUpdate.js index 56b8992b4..527632d4e 100644 --- a/src/client/websocket/packets/handlers/MessageUpdate.js +++ b/src/client/websocket/packets/handlers/MessageUpdate.js @@ -1,14 +1,11 @@ const AbstractHandler = require('./AbstractHandler'); class MessageUpdateHandler extends AbstractHandler { - handle(packet) { - const data = packet.d; const client = this.packetManager.client; - + const data = packet.d; client.actions.MessageUpdate.handle(data); } - } module.exports = MessageUpdateHandler; diff --git a/src/client/websocket/packets/handlers/PresenceUpdate.js b/src/client/websocket/packets/handlers/PresenceUpdate.js index 98b22e2d0..3ba602cda 100644 --- a/src/client/websocket/packets/handlers/PresenceUpdate.js +++ b/src/client/websocket/packets/handlers/PresenceUpdate.js @@ -3,21 +3,16 @@ const Constants = require('../../../../util/Constants'); const cloneObject = require('../../../../util/CloneObject'); class PresenceUpdateHandler extends AbstractHandler { - handle(packet) { - const data = packet.d; const client = this.packetManager.client; + const data = packet.d; let user = client.users.get(data.user.id); const guild = client.guilds.get(data.guild_id); - function makeUser(newUser) { - return client.dataManager.newUser(newUser); - } - // step 1 if (!user) { if (data.user.username) { - user = makeUser(data.user); + user = client.dataManager.newUser(data.user); } else { return; } @@ -39,7 +34,6 @@ class PresenceUpdateHandler extends AbstractHandler { data.user.username = data.user.username || user.username; data.user.id = data.user.id || user.id; data.user.discriminator = data.user.discriminator || user.discriminator; - // comment out avatar patching as it causes bugs (see #297) // data.user.avatar = data.user.avatar || user.avatar; data.user.status = data.status || user.status; @@ -58,24 +52,20 @@ class PresenceUpdateHandler extends AbstractHandler { client.emit(Constants.Events.PRESENCE_UPDATE, oldUser, user); } } - } /** -* Emitted whenever a user changes one of their details or starts/stop playing a game -* -* @event Client#presenceUpdate -* @param {User} oldUser the user before the presence update -* @param {User} newUser the user after the presence update -*/ + * Emitted whenever a user changes one of their details or starts/stop playing a game + * @event Client#presenceUpdate + * @param {User} oldUser The user before the presence update + * @param {User} newUser The user after the presence update + */ /** -* Emitted whenever a member becomes available in a large Guild -* -* @event Client#guildMemberAvailable -* @param {Guild} guild The guild that the member became available in -* @param {GuildMember} member the member that became available -*/ - + * Emitted whenever a member becomes available in a large Guild + * @event Client#guildMemberAvailable + * @param {Guild} guild The guild that the member became available in + * @param {GuildMember} member The member that became available + */ module.exports = PresenceUpdateHandler; diff --git a/src/client/websocket/packets/handlers/Ready.js b/src/client/websocket/packets/handlers/Ready.js index 1283d46b0..7cbb46784 100644 --- a/src/client/websocket/packets/handlers/Ready.js +++ b/src/client/websocket/packets/handlers/Ready.js @@ -4,40 +4,28 @@ const getStructure = name => require(`../../../../structures/${name}`); const ClientUser = getStructure('ClientUser'); class ReadyHandler extends AbstractHandler { - handle(packet) { - const data = packet.d; const client = this.packetManager.client; + const data = packet.d; const clientUser = new ClientUser(client, data.user); client.user = clientUser; client.readyTime = Date.now(); client.users.set(clientUser.id, clientUser); - for (const guild of data.guilds) { - client.dataManager.newGuild(guild); - } - for (const privateDM of data.private_channels) { - client.dataManager.newChannel(privateDM); - } - - if (!client.user.bot) { - client.setInterval(client.syncGuilds.bind(client), 30000); - } + for (const guild of data.guilds) client.dataManager.newGuild(guild); + for (const privateDM of data.private_channels) client.dataManager.newChannel(privateDM); + if (!client.user.bot) client.setInterval(client.syncGuilds.bind(client), 30000); client.once('ready', client.syncGuilds.bind(client)); client.setTimeout(() => { - if (!client.ws.normalReady) { - client.ws._emitReady(false); - } + if (!client.ws.normalReady) client.ws._emitReady(false); }, 1200 * data.guilds.length); this.packetManager.ws.sessionID = data.session_id; - this.packetManager.ws.checkIfReady(); } - } module.exports = ReadyHandler; diff --git a/src/client/websocket/packets/handlers/TypingStart.js b/src/client/websocket/packets/handlers/TypingStart.js index f751bfb4d..faaa49865 100644 --- a/src/client/websocket/packets/handlers/TypingStart.js +++ b/src/client/websocket/packets/handlers/TypingStart.js @@ -1,28 +1,10 @@ const AbstractHandler = require('./AbstractHandler'); const Constants = require('../../../../util/Constants'); -class TypingData { - constructor(since, lastTimestamp, _timeout) { - this.since = since; - this.lastTimestamp = lastTimestamp; - this._timeout = _timeout; - } - - resetTimeout(_timeout) { - clearTimeout(this._timeout); - this._timeout = _timeout; - } - - get elapsedTime() { - return Date.now() - this.since; - } -} - class TypingStartHandler extends AbstractHandler { - handle(packet) { - const data = packet.d; const client = this.packetManager.client; + const data = packet.d; const channel = client.channels.get(data.channel_id); const user = client.users.get(data.user_id); const timestamp = new Date(data.timestamp * 1000); @@ -46,23 +28,37 @@ class TypingStartHandler extends AbstractHandler { } } } +} +class TypingData { + constructor(since, lastTimestamp, _timeout) { + this.since = since; + this.lastTimestamp = lastTimestamp; + this._timeout = _timeout; + } + + resetTimeout(_timeout) { + clearTimeout(this._timeout); + this._timeout = _timeout; + } + + get elapsedTime() { + return Date.now() - this.since; + } } /** -* 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 + */ /** -* Emitted whenever a user stops typing in a channel -* -* @event Client#typingStop -* @param {Channel} channel the channel the user stopped typing in -* @param {User} user the user that stopped typing -*/ + * Emitted whenever a user stops typing in a channel + * @event Client#typingStop + * @param {Channel} channel The channel the user stopped typing in + * @param {User} user The user that stopped typing + */ module.exports = TypingStartHandler; diff --git a/src/client/websocket/packets/handlers/UserUpdate.js b/src/client/websocket/packets/handlers/UserUpdate.js index f1d6db65c..bc34f347d 100644 --- a/src/client/websocket/packets/handlers/UserUpdate.js +++ b/src/client/websocket/packets/handlers/UserUpdate.js @@ -1,14 +1,11 @@ const AbstractHandler = require('./AbstractHandler'); class UserUpdateHandler extends AbstractHandler { - handle(packet) { - const data = packet.d; const client = this.packetManager.client; - + const data = packet.d; client.actions.UserUpdate.handle(data); } - } module.exports = UserUpdateHandler; diff --git a/src/client/websocket/packets/handlers/VoiceServerUpdate.js b/src/client/websocket/packets/handlers/VoiceServerUpdate.js index 4af1b7e4a..146a3efc3 100644 --- a/src/client/websocket/packets/handlers/VoiceServerUpdate.js +++ b/src/client/websocket/packets/handlers/VoiceServerUpdate.js @@ -9,16 +9,13 @@ const AbstractHandler = require('./AbstractHandler'); */ class VoiceServerUpdate extends AbstractHandler { - handle(packet) { - const data = packet.d; const client = this.packetManager.client; - + const data = packet.d; if (client.voice.pending.has(data.guild_id)) { client.voice._receivedVoiceServer(data.guild_id, data.token, data.endpoint); } } - } module.exports = VoiceServerUpdate; diff --git a/src/client/websocket/packets/handlers/VoiceStateUpdate.js b/src/client/websocket/packets/handlers/VoiceStateUpdate.js index ba7fe4e81..639da210b 100644 --- a/src/client/websocket/packets/handlers/VoiceStateUpdate.js +++ b/src/client/websocket/packets/handlers/VoiceStateUpdate.js @@ -4,12 +4,11 @@ const Constants = require('../../../../util/Constants'); const cloneObject = require('../../../../util/CloneObject'); class VoiceStateUpdateHandler extends AbstractHandler { - handle(packet) { - const data = packet.d; const client = this.packetManager.client; - const guild = client.guilds.get(data.guild_id); + const data = packet.d; + const guild = client.guilds.get(data.guild_id); if (guild) { const member = guild.members.get(data.user_id); if (member) { @@ -19,18 +18,14 @@ class VoiceStateUpdateHandler extends AbstractHandler { } // if the member left the voice channel, unset their speaking property - if (!data.channel_id) { - member.speaking = null; - } + if (!data.channel_id) member.speaking = null; if (client.voice.pending.has(guild.id) && member.user.id === client.user.id && data.channel_id) { client.voice._receivedVoiceStateUpdate(data.guild_id, data.session_id); } const newChannel = client.channels.get(data.channel_id); - if (newChannel) { - newChannel.members.set(member.user.id, member); - } + if (newChannel) newChannel.members.set(member.user.id, member); member.serverMute = data.mute; member.serverDeaf = data.deaf; @@ -42,15 +37,13 @@ class VoiceStateUpdateHandler extends AbstractHandler { } } } - } /** -* Emitted whenever a user changes voice state - e.g. joins/leaves a channel, mutes/unmutes. -* -* @event Client#voiceStateUpdate -* @param {GuildMember} oldMember the member before the voice state update -* @param {GuildMember} newMember the member before the voice state update -*/ + * Emitted whenever a user changes voice state - e.g. joins/leaves a channel, mutes/unmutes. + * @event Client#voiceStateUpdate + * @param {GuildMember} oldMember The member before the voice state update + * @param {GuildMember} newMember The member after the voice state update + */ module.exports = VoiceStateUpdateHandler; diff --git a/src/structures/Channel.js b/src/structures/Channel.js index 0a9113696..ae36545ac 100644 --- a/src/structures/Channel.js +++ b/src/structures/Channel.js @@ -10,9 +10,7 @@ class Channel { this.client = client; this.typingMap = {}; this.typingTimeouts = []; - if (guild) { - this.guild = guild; - } + if (guild) this.guild = guild; /** * The type of the channel, either: * * `dm` - a DM channel @@ -22,9 +20,7 @@ class Channel { * @type {string} */ this.type = null; - if (data) { - this.setup(data); - } + if (data) this.setup(data); } setup(data) { diff --git a/src/structures/ClientUser.js b/src/structures/ClientUser.js index 3d34b61b0..80c8988e4 100644 --- a/src/structures/ClientUser.js +++ b/src/structures/ClientUser.js @@ -25,7 +25,7 @@ class ClientUser extends User { * Set the username of the logged in Client. * Changing usernames in Discord is heavily rate limited, with only 2 requests * every hour. Use this sparingly! - * @param {string} username the new username + * @param {string} username The new username * @returns {Promise} * @example * // set username @@ -40,7 +40,7 @@ class ClientUser extends User { /** * If this user is a "self bot" or logged in using a normal user's details (which should be avoided), you can set the * email here. - * @param {string} email the new email + * @param {string} email The new email * @returns {Promise} * @example * // set email @@ -55,7 +55,7 @@ class ClientUser extends User { /** * If this user is a "self bot" or logged in using a normal user's details (which should be avoided), you can set the * password here. - * @param {string} password the new password + * @param {string} password The new password * @returns {Promise} * @example * // set password @@ -69,7 +69,7 @@ class ClientUser extends User { /** * Set the avatar of the logged in Client. - * @param {Base64Resolvable} avatar the new avatar + * @param {Base64Resolvable} avatar The new avatar * @returns {Promise} * @example * // set avatar @@ -83,9 +83,9 @@ class ClientUser extends User { /** * Set the status and playing game of the logged in client. - * @param {string} [status] the status, can be `online` or `idle`. - * @param {string|Object} [game] the game that is being played - * @returns {Promise} + * @param {string} [status] The status, can be `online` or `idle` + * @param {string|Object} [game] The game that is being played + * @returns {Promise} * @example * // set status * client.user.setStatus('status', 'game') diff --git a/src/structures/DMChannel.js b/src/structures/DMChannel.js index 8cf633f2a..c590bb4c9 100644 --- a/src/structures/DMChannel.js +++ b/src/structures/DMChannel.js @@ -1,6 +1,6 @@ +const User = require('./User'); const Channel = require('./Channel'); const TextBasedChannel = require('./interface/TextBasedChannel'); -const User = require('./User'); const Collection = require('../util/Collection'); /** diff --git a/src/structures/Emoji.js b/src/structures/Emoji.js index 6a9660749..28c93bb98 100644 --- a/src/structures/Emoji.js +++ b/src/structures/Emoji.js @@ -1,5 +1,5 @@ -const Collection = require('../util/Collection'); const Constants = require('../util/Constants'); +const Collection = require('../util/Collection'); /** * Represents a Custom Emoji @@ -44,16 +44,14 @@ class Emoji { } /** - * A collection of roles this emoji is active for (empty if all). Mapped by role ID. + * A collection of roles this emoji is active for (empty if all), mapped by role ID. * @type {Collection} * @readonly */ get roles() { const roles = new Collection(); for (const role of this.roleIDS) { - if (this.guild.roles.get(role)) { - roles.set(role, this.guild.roles.get(role)); - } + if (this.guild.roles.get(role)) roles.set(role, this.guild.roles.get(role)); } return roles; } diff --git a/src/structures/EvaluatedPermissions.js b/src/structures/EvaluatedPermissions.js index 511972baf..0c66cd437 100644 --- a/src/structures/EvaluatedPermissions.js +++ b/src/structures/EvaluatedPermissions.js @@ -28,31 +28,18 @@ class EvaluatedPermissions { for (const permissionName in Constants.PermissionFlags) { serializedPermissions[permissionName] = this.hasPermission(permissionName); } - return serializedPermissions; } /** - * Checks whether a user has a certain permission, e.g. `READ_MESSAGES`. - * @param {string} permission the permission to check for - * @param {boolean} [explicit=false] whether the user should explicitly have the permission. + * Checks whether the user has a certain permission, e.g. `READ_MESSAGES`. + * @param {PermissionResolvable} permission The permission to check for + * @param {boolean} [explicit=false] Whether to require the user to explicitly have the exact permission * @returns {boolean} */ hasPermission(permission, explicit = false) { - if (typeof permission === 'string') { - permission = Constants.PermissionFlags[permission]; - } - - if (!permission) { - throw Constants.Errors.NOT_A_PERMISSION; - } - - if (!explicit) { - if ((this.permissions & Constants.PermissionFlags.ADMINISTRATOR) > 0) { - return true; - } - } - + permission = this.member.client.resolver.resolvePermission(permission); + if (!explicit && (this.permissions & Constants.PermissionFlags.ADMINISTRATOR) > 0) return true; return (this.permissions & permission) > 0; } } diff --git a/src/structures/GroupDMChannel.js b/src/structures/GroupDMChannel.js index e663fff8e..1c788fc0e 100644 --- a/src/structures/GroupDMChannel.js +++ b/src/structures/GroupDMChannel.js @@ -1,6 +1,7 @@ const Channel = require('./Channel'); const TextBasedChannel = require('./interface/TextBasedChannel'); const Collection = require('../util/Collection'); +const arraysEqual = require('../util/ArraysEqual'); /* { type: 3, @@ -24,49 +25,31 @@ const Collection = require('../util/Collection'); icon: null } */ - -function arraysEqual(a, b) { - if (a === b) return true; - if (a.length !== b.length) return false; - - for (const itemInd in a) { - const item = a[itemInd]; - const ind = b.indexOf(item); - if (ind) { - b.splice(ind, 1); - } - } - - return b.length === 0; -} - /** * Represents a Group DM on Discord * @extends {Channel} * @implements {TextBasedChannel} */ class GroupDMChannel extends Channel { - constructor(client, data) { super(client, data); - this.messages = new Collection(); } equals(other) { - const base = other && + const equal = other && this.id === other.id && this.name === other.name && this.icon === other.icon && this.owner.id === other.owner_id; - if (base) { + if (equal) { const thisIDs = this.recipients.array().map(r => r.id); const otherIDs = other.recipients.map(r => r.id); return arraysEqual(thisIDs, otherIDs); } - return base; + return equal; } setup(data) { @@ -86,6 +69,7 @@ class GroupDMChannel extends Channel { this.recipients.set(user.id, user); } } + /** * The name of this Group DM, can be null if one isn't set. * @type {string} diff --git a/src/structures/Guild.js b/src/structures/Guild.js index 2d0b76059..374996eab 100644 --- a/src/structures/Guild.js +++ b/src/structures/Guild.js @@ -1,25 +1,11 @@ const User = require('./User'); +const Role = require('./Role'); +const Emoji = require('./Emoji'); const GuildMember = require('./GuildMember'); const Constants = require('../util/Constants'); -const cloneObject = require('../util/CloneObject'); -const Role = require('./Role'); const Collection = require('../util/Collection'); -const Emoji = require('./Emoji'); - -function arraysEqual(a, b) { - if (a === b) return true; - if (a.length !== b.length) return false; - - for (const itemInd in a) { - const item = a[itemInd]; - const ind = b.indexOf(item); - if (ind) { - b.splice(ind, 1); - } - } - - return b.length === 0; -} +const cloneObject = require('../util/CloneObject'); +const arraysEqual = require('../util/ArraysEqual'); /** * Represents a Guild (or a Server) on Discord. @@ -50,9 +36,7 @@ class Guild { */ this.roles = new Collection(); - if (!data) { - return; - } + if (!data) return; if (data.unavailable) { /** @@ -100,12 +84,11 @@ class Guild { this.channels.get(voiceState.channel_id).members.set(member.user.id, member); } /** - * Emitted whenever a user joins a guild. - * - * @event Client#guildMemberAdd - * @param {Guild} guild the guild that the user has joined - * @param {GuildMember} member the member that has joined - */ + * Emitted whenever a user joins a guild. + * @event Client#guildMemberAdd + * @param {Guild} guild The guild that the user has joined + * @param {GuildMember} member The member that has joined + */ if (this.client.ws.status === Constants.Status.READY && !noEvent) { this.client.emit(Constants.Events.GUILD_MEMBER_ADD, this, member); } @@ -117,25 +100,22 @@ class Guild { _updateMember(member, data) { const oldMember = cloneObject(member); - if (data.roles) { - member._roles = data.roles; - } else { - member.nickname = data.nick; - } + if (data.roles) member._roles = data.roles; + else member.nickname = data.nick; const notSame = member.nickname !== oldMember.nickname && !arraysEqual(member._roles, oldMember._roles); if (this.client.ws.status === Constants.Status.READY && notSame) { /** - * Emitted whenever a Guild Member changes - i.e. new role, removed role, nickname - * - * @event Client#guildMemberUpdate - * @param {Guild} guild the guild that the update affects - * @param {GuildMember} oldMember the member before the update - * @param {GuildMember} newMember the member after the update - */ + * Emitted whenever a Guild Member changes - i.e. new role, removed role, nickname + * @event Client#guildMemberUpdate + * @param {Guild} guild The guild that the update affects + * @param {GuildMember} oldMember The member before the update + * @param {GuildMember} newMember The member after the update + */ this.client.emit(Constants.Events.GUILD_MEMBER_UPDATE, this, oldMember, member); } + return { old: oldMember, mem: member, @@ -163,7 +143,7 @@ class Guild { /** * Returns the GuildMember form of a User object, if the User is present in the guild. - * @param {UserResolvable} user the user that you want to obtain the GuildMember of. + * @param {UserResolvable} user The user that you want to obtain the GuildMember of * @returns {GuildMember|null} * @example * // get the guild member of a user @@ -177,11 +157,11 @@ class Guild { * Whether this Guild equals another Guild. It compares all properties, so for most operations * it is advisable to just compare `guild.id === guild2.id` as it is much faster and is often * what most users need. - * @param {Guild} guild the guild to compare + * @param {Guild} guild The guild to compare * @returns {boolean} */ equals(guild) { - let base = + let equal = guild && this.id === guild.id && this.available === !guild.unavailable && @@ -196,17 +176,15 @@ class Guild { this.verificationLevel === guild.verification_level && this.embedEnabled === guild.embed_enabled; - if (base) { + if (equal) { if (this.embedChannel) { - if (this.embedChannel.id !== guild.embed_channel_id) { - base = false; - } + if (this.embedChannel.id !== guild.embed_channel_id) equal = false; } else if (guild.embed_channel_id) { - base = false; + equal = false; } } - return base; + return equal; } _memberSpeakUpdate(user, speaking) { @@ -216,8 +194,8 @@ class Guild { /** * Emitted once a Guild Member starts/stops speaking * @event Client#guildMemberSpeaking - * @param {GuildMember} member the member that started/stopped speaking - * @param {boolean} speaking whether or not the member is speaking + * @param {GuildMember} member The member that started/stopped speaking + * @param {boolean} speaking Whether or not the member is speaking */ this.client.emit(Constants.Events.GUILD_MEMBER_SPEAKING, member, speaking); } @@ -225,7 +203,7 @@ class Guild { /** * Sets up the Guild - * @param {*} data the raw data of the guild + * @param {*} data The raw data of the guild * @private */ setup(data) { @@ -264,12 +242,12 @@ class Guild { this.icon = data.icon; /** * An array of guild features. - * @type {Array} + * @type {Object[]} */ this.features = data.features; /** * An array of guild emojis. - * @type {Array} + * @type {Object[]} */ this.emojis = new Collection(); for (const emoji of data.emojis) { @@ -351,6 +329,7 @@ class Guild { } } } + /** * The date at which the logged-in client joined the guild. * @type {Date} @@ -361,9 +340,9 @@ class Guild { /** * Creates a new Channel in the Guild. - * @param {string} name the name of the new channel. - * @param {string} type the type of the new channel, either `text` or `voice`. - * @returns {Promise} + * @param {string} name The name of the new channel + * @param {string} type The type of the new channel, either `text` or `voice` + * @returns {Promise} * @example * // create a new text channel * guild.createChannel('new general', 'text') @@ -376,7 +355,7 @@ class Guild { /** * Creates a new role in the guild, as of now this is just a blank role. - * @returns {Promise} + * @returns {Promise} * @example * // create a new role * guild.createRole() @@ -389,7 +368,7 @@ class Guild { /** * Causes the Client to leave the guild. - * @returns {Promise} + * @returns {Promise} * @example * // leave a guild * guild.leave() @@ -402,7 +381,7 @@ class Guild { /** * Causes the Client to delete the guild. - * @returns {Promise} + * @returns {Promise} * @example * // delete a guild * guild.delete() @@ -415,8 +394,8 @@ class Guild { /** * Updates the Guild with new information - e.g. a new name. - * @param {GuildEditData} data the data to update the guild with. - * @returns {Promise} + * @param {GuildEditData} data The data to update the guild with + * @returns {Promise} * @example * // set the guild name and region * guild.edit({ @@ -432,8 +411,8 @@ class Guild { /** * Edit the name of the Guild. - * @param {string} name the new name of the Guild. - * @returns {Promise} + * @param {string} name The new name of the Guild + * @returns {Promise} * @example * // edit the guild name * guild.setName('Discord Guild') @@ -446,8 +425,8 @@ class Guild { /** * Edit the region of the Guild. - * @param {Region} region the new region of the guild. - * @returns {Promise} + * @param {Region} region The new region of the guild. + * @returns {Promise} * @example * // edit the guild region * guild.setRegion('london') @@ -460,8 +439,8 @@ class Guild { /** * Edit the verification level of the Guild. - * @param {VerificationLevel} verificationLevel the new verification level of the guild. - * @returns {Promise} + * @param {VerificationLevel} verificationLevel The new verification level of the guild + * @returns {Promise} * @example * // edit the guild verification level * guild.setVerificationLevel(1) @@ -474,8 +453,8 @@ class Guild { /** * Edit the AFK channel of the Guild. - * @param {GuildChannelResolvable} afkChannel the new AFK channel. - * @returns {Promise} + * @param {GuildChannelResolvable} afkChannel The new AFK channel + * @returns {Promise} * @example * // edit the guild AFK channel * guild.setAFKChannel(channel) @@ -488,8 +467,8 @@ class Guild { /** * Edit the AFK timeout of the Guild. - * @param {number} afkTimeout the time in seconds that a user must be idle to be considered AFK. - * @returns {Promise} + * @param {number} afkTimeout The time in seconds that a user must be idle to be considered AFK + * @returns {Promise} * @example * // edit the guild AFK channel * guild.setAFKTimeout(60) @@ -502,8 +481,8 @@ class Guild { /** * Set a new Guild Icon. - * @param {Base64Resolvable} icon the new icon of the guild. - * @returns {Promise} + * @param {Base64Resolvable} icon The new icon of the guild + * @returns {Promise} * @example * // edit the guild icon * guild.setIcon(fs.readFileSync('./icon.png')) @@ -516,8 +495,8 @@ class Guild { /** * Sets a new owner of the Guild. - * @param {GuildMemberResolvable} owner the new owner of the Guild. - * @returns {Promise} + * @param {GuildMemberResolvable} owner The new owner of the Guild + * @returns {Promise} * @example * // edit the guild owner * guild.setOwner(guilds.members[0]) @@ -530,8 +509,8 @@ class Guild { /** * Set a new Guild Splash Logo. - * @param {Base64Resolvable} splash the new splash screen of the guild. - * @returns {Promise} + * @param {Base64Resolvable} splash The new splash screen of the guild + * @returns {Promise} * @example * // edit the guild splash * guild.setIcon(fs.readFileSync('./splash.png')) @@ -544,8 +523,8 @@ class Guild { /** * Unbans a member from the Guild - * @param {UserResolvable} member the member to unban - * @returns {Promise} + * @param {UserResolvable} member The member to unban + * @returns {Promise} * @example * // unban a member * guild.unban('123123123123') @@ -558,7 +537,7 @@ class Guild { /** * Fetch a Collection of banned users in this Guild. - * @returns {Promise, Error>} + * @returns {Promise>} */ fetchBans() { return this.client.rest.methods.getGuildBans(this); @@ -566,7 +545,7 @@ class Guild { /** * Fetch a Collection of invites to this Guild. Resolves with a Collection mapping invites by their codes. - * @returns {Promise, Error>} + * @returns {Promise>} */ fetchInvites() { return this.client.rest.methods.getGuildInvites(this); @@ -576,7 +555,7 @@ 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 - * @returns {Promise} + * @returns {Promise} */ fetchMembers(query = '') { return new Promise((resolve, reject) => { @@ -607,9 +586,7 @@ class Guild { * @readonly */ get iconURL() { - if (!this.icon) { - return null; - } + if (!this.icon) return null; return Constants.Endpoints.guildIcon(this.id, this.icon); } diff --git a/src/structures/GuildChannel.js b/src/structures/GuildChannel.js index f345216cd..aeef11603 100644 --- a/src/structures/GuildChannel.js +++ b/src/structures/GuildChannel.js @@ -1,24 +1,10 @@ const Channel = require('./Channel'); -const PermissionOverwrites = require('./PermissionOverwrites'); const Role = require('./Role'); +const PermissionOverwrites = require('./PermissionOverwrites'); const EvaluatedPermissions = require('./EvaluatedPermissions'); const Constants = require('../util/Constants'); const Collection = require('../util/Collection'); - -function arraysEqual(a, b) { - if (a === b) return true; - if (a.length !== b.length) return false; - - for (const itemInd in a) { - const item = a[itemInd]; - const ind = b.indexOf(item); - if (ind) { - b.splice(ind, 1); - } - } - - return b.length === 0; -} +const arraysEqual = require('../util/ArraysEqual'); /** * Represents a Guild Channel (i.e. Text Channels and Voice Channels) @@ -62,67 +48,57 @@ class GuildChannel extends Channel { /** * Checks if this channel has the same type, topic, position, name, overwrites and ID as another channel. * In most cases, a simple `channel.id === channel2.id` will do, and is much faster too. - * @param {GuildChannel} channel the channel to compare this channel to + * @param {GuildChannel} channel The channel to compare this channel to * @returns {boolean} */ equals(channel) { - let base = channel && + let equal = channel && this.type === channel.type && this.topic === channel.topic && this.position === channel.position && this.name === channel.name && this.id === channel.id; - if (base) { + if (equal) { if (channel.permission_overwrites) { const thisIDSet = Array.from(this.permissionOverwrites.keys()); const otherIDSet = channel.permission_overwrites.map(overwrite => overwrite.id); - if (arraysEqual(thisIDSet, otherIDSet)) { - base = true; - } else { - base = false; - } + equal = arraysEqual(thisIDSet, otherIDSet); } else { - base = false; + equal = false; } } - return base; + return equal; } /** * Gets the overall set of permissions for a user in this channel, taking into account roles and permission * overwrites. - * @param {GuildMemberResolvable} member the user that you want to obtain the overall permissions for + * @param {GuildMemberResolvable} member The user that you want to obtain the overall permissions for * @returns {?EvaluatedPermissions} */ permissionsFor(member) { member = this.client.resolver.resolveGuildMember(this.guild, member); if (member) { - if (this.guild.owner.id === member.id) { - return new EvaluatedPermissions(member, Constants.ALL_PERMISSIONS); - } + if (this.guild.owner.id === member.id) return new EvaluatedPermissions(member, Constants.ALL_PERMISSIONS); const roles = member.roles; let permissions = 0; const overwrites = this.overwritesFor(member, true); - for (const role of roles.values()) { - permissions |= role.permissions; - } - + for (const role of roles.values()) permissions |= role.permissions; for (const overwrite of overwrites.role.concat(overwrites.member)) { permissions &= ~overwrite.denyData; permissions |= overwrite.allowData; } const admin = Boolean(permissions & (Constants.PermissionFlags.ADMINISTRATOR)); - if (admin) { - permissions = Constants.ALL_PERMISSIONS; - } + if (admin) permissions = Constants.ALL_PERMISSIONS; return new EvaluatedPermissions(member, permissions); } + return null; } @@ -165,9 +141,9 @@ class GuildChannel extends Channel { /** * Overwrites the permissions for a user or role in this channel. - * @param {Role|UserResolvable} userOrRole the user or role to update - * @param {PermissionOverwriteOptions} options the configuration for the update - * @returns {Promise} + * @param {Role|UserResolvable} userOrRole The user or role to update + * @param {PermissionOverwriteOptions} options The configuration for the update + * @returns {Promise} * @example * // overwrite permissions for a message author * message.channel.overwritePermissions(message.author, { @@ -187,9 +163,7 @@ class GuildChannel extends Channel { } else { userOrRole = this.client.resolver.resolveUser(userOrRole); payload.type = 'member'; - if (!userOrRole) { - return Promise.reject('supplied parameter was neither a user or a role'); - } + if (!userOrRole) return Promise.reject(new TypeError('supplied parameter was neither a user or a role')); } payload.id = userOrRole.id; @@ -220,7 +194,7 @@ class GuildChannel extends Channel { /** * Set a new name for the Guild Channel - * @param {string} name the new name for the guild channel + * @param {string} name The new name for the guild channel * @returns {Promise} * @example * // set a new channel name @@ -234,7 +208,7 @@ class GuildChannel extends Channel { /** * Set a new position for the Guild Channel - * @param {number} position the new position for the guild channel + * @param {number} position The new position for the guild channel * @returns {Promise} * @example * // set a new channel position @@ -248,7 +222,7 @@ class GuildChannel extends Channel { /** * Set a new topic for the Guild Channel - * @param {string} topic the new topic for the guild channel + * @param {string} topic The new topic for the guild channel * @returns {Promise} * @example * // set a new channel topic @@ -288,8 +262,8 @@ class GuildChannel extends Channel { /** * Create an invite to this Guild Channel - * @param {InviteOptions} [options={}] the options to provide when creating the invite - * @returns {Promise} + * @param {InviteOptions} [options={}] The options for the invite + * @returns {Promise} */ createInvite(options = {}) { return this.client.rest.methods.createChannelInvite(this, options); diff --git a/src/structures/GuildMember.js b/src/structures/GuildMember.js index ef3083e2e..e6a0e291b 100644 --- a/src/structures/GuildMember.js +++ b/src/structures/GuildMember.js @@ -23,9 +23,7 @@ class GuildMember { */ this.user = {}; this._roles = []; - if (data) { - this.setup(data); - } + if (data) this.setup(data); } setup(data) { @@ -91,15 +89,11 @@ class GuildMember { const list = new Collection(); const everyoneRole = this.guild.roles.get(this.guild.id); - if (everyoneRole) { - list.set(everyoneRole.id, everyoneRole); - } + if (everyoneRole) list.set(everyoneRole.id, everyoneRole); for (const roleID of this._roles) { const role = this.guild.roles.get(roleID); - if (role) { - list.set(role.id, role); - } + if (role) list.set(role.id, role); } return list; @@ -143,8 +137,8 @@ class GuildMember { /** * Mute/unmute a user - * @param {boolean} mute whether or not the member should be muted - * @returns {Promise} + * @param {boolean} mute Whether or not the member should be muted + * @returns {Promise} */ setMute(mute) { return this.edit({ mute }); @@ -152,8 +146,8 @@ class GuildMember { /** * Deafen/undeafen a user - * @param {boolean} deaf whether or not the member should be deafened - * @returns {Promise} + * @param {boolean} deaf Whether or not the member should be deafened + * @returns {Promise} */ setDeaf(deaf) { return this.edit({ deaf }); @@ -161,8 +155,8 @@ class GuildMember { /** * Moves the Guild Member to the given channel. - * @param {ChannelResolvable} channel the channel to move the member to - * @returns {Promise} + * @param {ChannelResolvable} channel The channel to move the member to + * @returns {Promise} */ setVoiceChannel(channel) { return this.edit({ channel }); @@ -170,8 +164,8 @@ class GuildMember { /** * Sets the Roles applied to the member. - * @param {Collection|Array} roles the roles to apply - * @returns {Promise} + * @param {Collection|Role[]} roles The roles to apply + * @returns {Promise} */ setRoles(roles) { return this.edit({ roles }); @@ -179,8 +173,8 @@ class GuildMember { /** * Set the nickname for the Guild Member - * @param {string} nick the nickname for the Guild Member - * @returns {Promise} + * @param {string} nick The nickname for the Guild Member + * @returns {Promise} */ setNickname(nick) { return this.edit({ nick }); @@ -188,15 +182,15 @@ class GuildMember { /** * Edit a Guild Member - * @param {GuildmemberEditData} data the data to edit the member with - * @returns {Promise} + * @param {GuildmemberEditData} data The data to edit the member with + * @returns {Promise} */ edit(data) { return this.client.rest.methods.updateGuildMember(this, data); } /** - * Deletes any DM's with this Guild Member + * Deletes any DMs with this Guild Member * @returns {Promise} */ deleteDM() { @@ -215,7 +209,7 @@ class GuildMember { * Ban this Guild Member * @param {number} [deleteDays=0] The amount of days worth of messages from this member that should * also be deleted. Between `0` and `7`. - * @returns {Promise} + * @returns {Promise} * @example * // ban a guild member * guildMember.ban(7); diff --git a/src/structures/Invite.js b/src/structures/Invite.js index c82ae92fa..1c09e6425 100644 --- a/src/structures/Invite.js +++ b/src/structures/Invite.js @@ -98,7 +98,7 @@ class Invite { /** * Deletes this invite - * @returns {Promise} + * @returns {Promise} */ delete() { return this.client.rest.methods.deleteInvite(this); diff --git a/src/structures/Message.js b/src/structures/Message.js index a681c8b57..f0411460b 100644 --- a/src/structures/Message.js +++ b/src/structures/Message.js @@ -1,6 +1,7 @@ -const Collection = require('../util/Collection'); const Attachment = require('./MessageAttachment'); const Embed = require('./MessageEmbed'); +const Collection = require('../util/Collection'); + /** * Represents a Message on Discord */ @@ -26,9 +27,7 @@ class Message { * @type {Client} */ this.client = client; - if (data) { - this.setup(data); - } + if (data) this.setup(data); } setup(data) { @@ -69,7 +68,7 @@ class Message { this.nonce = data.nonce; /** * A list of embeds in the message - e.g. YouTube Player - * @type {Array} + * @type {Embed[]} */ this.embeds = data.embeds.map(e => new Embed(this, e)); /** @@ -77,17 +76,15 @@ class Message { * @type {Collection} */ this.attachments = new Collection(); - for (const attachment of data.attachments) { - this.attachments.set(attachment.id, new Attachment(this, attachment)); - } + for (const attachment of data.attachments) this.attachments.set(attachment.id, new Attachment(this, attachment)); /** * An object containing a further users, roles or channels collections * @type {Object} * @property {Collection} mentions.users Mentioned users, maps their ID to the user object. * @property {Collection} mentions.roles Mentioned roles, maps their ID to the role object. - * @property {Collection} - * mentions.channels Mentioned channels, maps their ID to the channel object. - * @property {boolean} mentions.everyone whether or not @everyone was mentioned. + * @property {Collection} mentions.channels Mentioned channels, + * maps their ID to the channel object. + * @property {boolean} mentions.everyone Whether or not @everyone was mentioned. */ this.mentions = { users: new Collection(), @@ -114,9 +111,7 @@ class Message { if (data.mention_roles) { for (const mention of data.mention_roles) { const role = this.channel.guild.roles.get(mention); - if (role) { - this.mentions.roles.set(role.id, role); - } + if (role) this.mentions.roles.set(role.id, role); } } @@ -124,9 +119,7 @@ class Message { 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]); - if (chan) { - this.mentions.channels.set(chan.id, chan); - } + if (chan) this.mentions.channels.set(chan.id, chan); } } @@ -135,9 +128,7 @@ class Message { * @type {boolean} */ this.system = false; - if (data.type === 6) { - this.system = true; - } + if (data.type === 6) this.system = true; } /** * When the message was sent @@ -158,31 +149,17 @@ class Message { patch(data) { // eslint-disable-line complexity if (data.author) { this.author = this.client.users.get(data.author.id); - if (this.guild) { - this.member = this.guild.member(this.author); - } - } - if (data.content) { - this.content = data.content; - } - if (data.timestamp) { - this._timestamp = new Date(data.timestamp).getTime(); + if (this.guild) this.member = this.guild.member(this.author); } + if (data.content) this.content = data.content; + if (data.timestamp) this._timestamp = new Date(data.timestamp).getTime(); if (data.edited_timestamp) { this._editedTimestamp = data.edited_timestamp ? new Date(data.edited_timestamp).getTime() : null; } - if ('tts' in data) { - this.tts = data.tts; - } - if ('mention_everyone' in data) { - this.mentions.everyone = data.mention_everyone; - } - if (data.nonce) { - this.nonce = data.nonce; - } - if (data.embeds) { - this.embeds = data.embeds.map(e => new Embed(this, e)); - } + if ('tts' in data) this.tts = data.tts; + if ('mention_everyone' in data) this.mentions.everyone = data.mention_everyone; + if (data.nonce) this.nonce = data.nonce; + if (data.embeds) this.embeds = data.embeds.map(e => new Embed(this, e)); if (data.type > -1) { this.system = false; if (data.type === 6) { @@ -214,9 +191,7 @@ class Message { } } } - if (data.id) { - this.id = data.id; - } + if (data.id) this.id = data.id; if (this.channel.guild && data.content) { const channMentionsRaw = data.content.match(/<#([0-9]{14,20})>/g) || []; for (const raw of channMentionsRaw) { @@ -237,14 +212,11 @@ class Message { * @returns {boolean} */ equals(message, rawData) { + if (!message) return false; const embedUpdate = !message.author && !message.attachments; + if (embedUpdate) return this.id === message.id && this.embeds.length === message.embeds.length; - if (embedUpdate) { - const base = this.id === message.id && - this.embeds.length === message.embeds.length; - return base; - } - let base = this.id === message.id && + let equal = this.id === message.id && this.author.id === message.author.id && this.content === message.content && this.tts === message.tts && @@ -252,19 +224,19 @@ class Message { this.embeds.length === message.embeds.length && this.attachments.length === message.attachments.length; - if (base && rawData) { - base = this.mentions.everyone === message.mentions.everyone && + if (equal && rawData) { + equal = this.mentions.everyone === message.mentions.everyone && this._timestamp === new Date(rawData.timestamp).getTime() && this._editedTimestamp === new Date(rawData.edited_timestamp).getTime(); } - return base; + return equal; } /** * Deletes the message * @param {number} [timeout=0] How long to wait to delete the message in milliseconds - * @returns {Promise} + * @returns {Promise} * @example * // delete a message * message.delete() @@ -283,8 +255,8 @@ class Message { /** * Edit the content of a message - * @param {string} content the new content of a message - * @returns {Promise} + * @param {string} content The new content for the message + * @returns {Promise} * @example * // update the content of a message * message.edit('This is my new content!') @@ -297,9 +269,9 @@ class Message { /** * Reply to a message - * @param {string} content the content of the message - * @param {MessageOptions} [options = {}] the options to provide - * @returns {Promise} + * @param {string} content The content for the message + * @param {MessageOptions} [options = {}] The options to provide + * @returns {Promise} * @example * // reply to a message * message.reply('Hey, I'm a reply!') @@ -313,7 +285,7 @@ class Message { /** * Pins this message to the channel's pinned messages - * @returns {Promise} + * @returns {Promise} */ pin() { return this.client.rest.methods.pinMessage(this); @@ -321,7 +293,7 @@ class Message { /** * Unpins this message from the channel's pinned messages - * @returns {Promise} + * @returns {Promise} */ unpin() { return this.client.rest.methods.unpinMessage(this); diff --git a/src/structures/MessageEmbed.js b/src/structures/MessageEmbed.js index ed426da2e..50f3c2d90 100644 --- a/src/structures/MessageEmbed.js +++ b/src/structures/MessageEmbed.js @@ -1,3 +1,59 @@ +/** + * Represents an embed in an image - e.g. preview of image + */ +class MessageEmbed { + constructor(message, data) { + /** + * The message this embed is part of + * @type {Message} + */ + this.message = message; + /** + * The client that instantiated this embed + * @type {Client} + */ + this.client = message.client; + this.setup(data); + } + + setup(data) { + /** + * The title of this embed, if there is one + * @type {?string} + */ + this.title = data.title; + /** + * The type of this embed + * @type {string} + */ + this.type = data.type; + /** + * The description of this embed, if there is one + * @type {?string} + */ + this.description = data.description; + /** + * The URL of this embed + * @type {string} + */ + this.url = data.url; + if (data.thumbnail) { + /** + * The thumbnail of this embed, if there is one + * @type {MessageEmbedThumbnail} + */ + this.thumbnail = new MessageEmbedThumbnail(this, data.thumbnail); + } + if (data.provider) { + /** + * The provider of this embed, if there is one + * @type {MessageEmbedProvider} + */ + this.provider = new MessageEmbedProvider(this, data.provider); + } + } +} + /** * Represents a thumbnail for a Message embed */ @@ -62,60 +118,4 @@ class MessageEmbedProvider { } } -/** - * Represents an embed in an image - e.g. preview of image - */ -class MessageEmbed { - constructor(message, data) { - /** - * The message this embed is part of - * @type {Message} - */ - this.message = message; - /** - * The client that instantiated this embed - * @type {Client} - */ - this.client = message.client; - this.setup(data); - } - - setup(data) { - /** - * The title of this embed, if there is one - * @type {?string} - */ - this.title = data.title; - /** - * The type of this embed - * @type {string} - */ - this.type = data.type; - /** - * The description of this embed, if there is one - * @type {?string} - */ - this.description = data.description; - /** - * The URL of this embed - * @type {string} - */ - this.url = data.url; - if (data.thumbnail) { - /** - * The thumbnail of this embed, if there is one - * @type {MessageEmbedThumbnail} - */ - this.thumbnail = new MessageEmbedThumbnail(this, data.thumbnail); - } - if (data.provider) { - /** - * The provider of this embed, if there is one - * @type {MessageEmbedProvider} - */ - this.provider = new MessageEmbedProvider(this, data.provider); - } - } -} - module.exports = MessageEmbed; diff --git a/src/structures/PermissionOverwrites.js b/src/structures/PermissionOverwrites.js index b15e27889..7c31c7fc2 100644 --- a/src/structures/PermissionOverwrites.js +++ b/src/structures/PermissionOverwrites.js @@ -8,9 +8,7 @@ class PermissionOverwrites { * @type {GuildChannel} */ this.channel = guildChannel; - if (data) { - this.setup(data); - } + if (data) this.setup(data); } setup(data) { @@ -30,7 +28,7 @@ class PermissionOverwrites { /** * Delete this Permission Overwrite. - * @returns {Promise} + * @returns {Promise} */ delete() { return this.channel.client.rest.methods.deletePermissionOverwrites(this); diff --git a/src/structures/Role.js b/src/structures/Role.js index d33ef48d6..febe970b9 100644 --- a/src/structures/Role.js +++ b/src/structures/Role.js @@ -15,22 +15,18 @@ class Role { * @type {Client} */ this.client = guild.client; - if (data) { - this.setup(data); - } + if (data) this.setup(data); } 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 === role.permissions && - this.managed === role.managed - ); + this.managed === role.managed; } setup(data) { @@ -73,7 +69,7 @@ class Role { /** * Deletes the role - * @returns {Promise} + * @returns {Promise} * @example * // delete a role * role.delete() @@ -86,8 +82,8 @@ class Role { /** * Edits the role - * @param {RoleData} data the new data for the role - * @returns {Promise} + * @param {RoleData} data The new data for the role + * @returns {Promise} * @example * // edit a role * role.edit({name: 'new role'}) @@ -100,8 +96,8 @@ class Role { /** * Set a new name for the role - * @param {string} name the new name of the role - * @returns {Promise} + * @param {string} name The new name of the role + * @returns {Promise} * @example * // set the name of the role * role.setName('new role') @@ -114,8 +110,8 @@ class Role { /** * Set a new color for the role - * @param {number|string} color the new color for the role, either a hex string or a base 10 number - * @returns {Promise} + * @param {number|string} color The new color for the role, either a hex string or a base 10 number + * @returns {Promise} * @example * // set the color of a role * role.setColor('#FF0000') @@ -128,8 +124,8 @@ class Role { /** * Set whether or not the role should be hoisted - * @param {boolean} hoist whether or not to hoist the role - * @returns {Promise} + * @param {boolean} hoist Whether or not to hoist the role + * @returns {Promise} * @example * // set the hoist of the role * role.setHoist(true) @@ -142,8 +138,8 @@ class Role { /** * Set the position of the role - * @param {number} position the position of the role - * @returns {Promise} + * @param {number} position The position of the role + * @returns {Promise} * @example * // set the position of the role * role.setPosition(1) @@ -156,8 +152,8 @@ class Role { /** * Set the permissions of the role - * @param {Array} permissions the permissions of the role - * @returns {Promise} + * @param {string[]} permissions The permissions of the role + * @returns {Promise} * @example * // set the permissions of the role * role.setPermissions(['KICK_MEMBERS', 'BAN_MEMBERS']) @@ -180,14 +176,13 @@ class Role { for (const permissionName in Constants.PermissionFlags) { serializedPermissions[permissionName] = this.hasPermission(permissionName); } - return serializedPermissions; } /** * Whether or not the role includes the given permission - * @param {string} permission the name of the permission to test - * @param {boolean} [explicit=false] whether or not the inclusion of the permission is explicit + * @param {PermissionResolvable} permission The name of the permission to test + * @param {boolean} [explicit=false] Whether to require the role to explicitly have the exact permission * @returns {boolean} * @example * // see if a role can ban a member @@ -198,20 +193,8 @@ class Role { * } */ hasPermission(permission, explicit = false) { - if (typeof permission === 'string') { - permission = Constants.PermissionFlags[permission]; - } - - if (!permission) { - throw Constants.Errors.NOT_A_PERMISSION; - } - - if (!explicit) { - if ((this.permissions & Constants.PermissionFlags.ADMINISTRATOR) > 0) { - return true; - } - } - + permission = this.client.resolver.resolvePermission(permission); + if (!explicit && (this.permissions & Constants.PermissionFlags.ADMINISTRATOR) > 0) return true; return (this.permissions & permission) > 0; } @@ -230,9 +213,7 @@ class Role { */ get hexColor() { let col = this.color.toString(16); - while (col.length < 6) { - col = `0${col}`; - } + while (col.length < 6) col = `0${col}`; return `#${col}`; } } diff --git a/src/structures/TextChannel.js b/src/structures/TextChannel.js index 8aab8454e..671ee9aac 100644 --- a/src/structures/TextChannel.js +++ b/src/structures/TextChannel.js @@ -8,7 +8,6 @@ const Collection = require('../util/Collection'); * @implements {TextBasedChannel} */ class TextChannel extends GuildChannel { - constructor(guild, data) { super(guild, data); this.messages = new Collection(); diff --git a/src/structures/User.js b/src/structures/User.js index c1d3ee5e1..53a0926c8 100644 --- a/src/structures/User.js +++ b/src/structures/User.js @@ -8,9 +8,7 @@ const Constants = require('../util/Constants'); class User { constructor(client, data) { this.client = client; - if (data) { - this.setup(data); - } + if (data) this.setup(data); } setup(data) { @@ -72,9 +70,7 @@ class User { * @readonly */ get avatarURL() { - if (!this.avatar) { - return null; - } + if (!this.avatar) return null; return Constants.Endpoints.avatar(this.id, this.avatar); } @@ -89,28 +85,23 @@ class User { /** * Checks if the user is equal to another. It compares username, ID, discriminator, status and the game being played. * It is recommended to compare equality by using `user.id === user2.id` unless you want to compare all properties. - * @param {User} user the user to compare + * @param {User} user The user to compare * @returns {boolean} */ equals(user) { - let base = user && + let equal = user && this.username === user.username && this.id === user.id && this.discriminator === user.discriminator && this.avatar === user.avatar && this.bot === Boolean(user.bot); - if (base) { - if (user.status) { - base = this.status === user.status; - } - - if (user.game) { - base = this.game === user.game; - } + if (equal) { + if (user.status) equal = this.status === user.status; + if (equal && user.game) equal = this.game === user.game; } - return base; + return equal; } sendMessage() { diff --git a/src/structures/VoiceChannel.js b/src/structures/VoiceChannel.js index b33d5dbd9..b5174c3d6 100644 --- a/src/structures/VoiceChannel.js +++ b/src/structures/VoiceChannel.js @@ -32,7 +32,7 @@ class VoiceChannel extends GuildChannel { /** * Sets the bitrate of the channel - * @param {number} bitrate the new bitrate + * @param {number} bitrate The new bitrate * @returns {Promise} * @example * // set the bitrate of a voice channel @@ -46,7 +46,7 @@ class VoiceChannel extends GuildChannel { /** * Attempts to join this Voice Channel - * @returns {Promise} + * @returns {Promise} * @example * // join a voice channel * voiceChannel.join() @@ -64,12 +64,8 @@ class VoiceChannel extends GuildChannel { * voiceChannel.leave(); */ leave() { - const exists = this.client.voice.connections.get(this.guild.id); - if (exists) { - if (exists.channel.id === this.id) { - exists.disconnect(); - } - } + const connection = this.client.voice.connections.get(this.guild.id); + if (connection && connection.channel.id === this.id) connection.disconnect(); } } diff --git a/src/structures/interface/TextBasedChannel.js b/src/structures/interface/TextBasedChannel.js index e5d719a22..17cfa92fb 100644 --- a/src/structures/interface/TextBasedChannel.js +++ b/src/structures/interface/TextBasedChannel.js @@ -1,128 +1,13 @@ -const Collection = require('../../util/Collection'); -const Message = require('../Message'); const path = require('path'); const EventEmitter = require('events').EventEmitter; - -/** - * A function that takes a Message object and a MessageCollector and returns a boolean. - * ```js - * function(message, collector) { - * if (message.content.includes('discord')) { - * return true; // passed the filter test - * } - * return false; // failed the filter test - * } - * ``` - * @typedef {function} CollectorFilterFunction - */ - -/** - * An object containing options used to configure a MessageCollector. All properties are optional. - * ```js - * { - * time: null, // time in milliseconds. If specified, the collector ends after this amount of time. - * max: null, // the maximum amount of messages to handle before ending. - * } - * ``` - * @typedef {Object} CollectorOptions - */ - -/** - * Collects messages based on a specified filter, then emits them. - * @extends {EventEmitter} - */ -class MessageCollector extends EventEmitter { - constructor(channel, filter, options = {}) { - super(); - /** - * The channel this collector is operating on - * @type {Channel} - */ - this.channel = channel; - /** - * A function used to filter messages that the collector collects. - * @type {CollectorFilterFunction} - */ - this.filter = filter; - /** - * Options for the collecor. - * @type {CollectorOptions} - */ - this.options = options; - /** - * Whether this collector has stopped collecting Messages. - * @type {boolean} - */ - this.ended = false; - this.listener = message => this.verify(message); - this.channel.client.on('message', this.listener); - /** - * A collection of collected messages, mapped by message ID. - * @type {Collection} - */ - this.collected = new Collection(); - if (options.time) { - this.channel.client.setTimeout(() => this.stop('time'), options.time); - } - } - - /** - * Verifies a message against the filter and options - * @private - * @param {Message} message the message - * @returns {boolean} - */ - verify(message) { - if (this.channel ? this.channel.id !== message.channel.id : false) { - return false; - } - if (this.filter(message, this)) { - this.collected.set(message.id, message); - /** - * Emitted whenever the Collector receives a Message that passes the filter test. - * @param {Message} message the received message - * @param {MessageCollector} collector the collector the message passed through. - * @event MessageCollector#message - */ - this.emit('message', message, this); - if (this.options.max && this.collected.size === this.options.max) { - this.stop('limit'); - } - return true; - } - return false; - } - - /** - * Stops the collector and emits `end`. - * @param {string} [reason='user'] an optional reason for stopping the collector. - */ - stop(reason = 'user') { - if (this.ended) { - return; - } - this.ended = true; - this.channel.client.removeListener('message', this.listener); - /** - * Emitted when the Collector stops collecting. - * @param {Collection} collection A collection of messages collected - * during the lifetime of the Collector. - * Mapped by the ID of the Messages. - * @param {string} reason The reason for the end of the collector. If it ended because it reached the specified time - * limit, this would be `time`. If you invoke `.stop()` without specifying a reason, this would be `user`. If it - * ended because it reached its message limit, it will be `limit`. - * @event MessageCollector#end - */ - this.emit('end', this.collected, reason); - } -} +const Message = require('../Message'); +const Collection = require('../../util/Collection'); /** * Interface for classes that have text-channel-like features * @interface */ class TextBasedChannel { - constructor() { /** * A Collection containing the messages sent to this channel. @@ -133,19 +18,16 @@ class TextBasedChannel { /** * Bulk delete a given Collection or Array of messages in one go. Returns the deleted messages after. - * @param {Map|Array} messages the messages to delete + * @param {Collection|Message[]} messages The messages to delete * @returns {Collection} */ bulkDelete(messages) { - if (messages instanceof Map) { - messages = messages.array(); - } - if (!(messages instanceof Array)) { - return Promise.reject('pass an array or map'); - } + if (messages instanceof Collection) messages = messages.array(); + if (!(messages instanceof Array)) return Promise.reject(new TypeError('messages must be an array or collection')); const messageIDs = messages.map(m => m.id); return this.client.rest.methods.bulkDeleteMessages(this, messageIDs); } + /** * Options that can be passed into sendMessage or sendTTSMessage: * ```js @@ -156,10 +38,11 @@ class TextBasedChannel { * ``` * @typedef {Object} MessageOptions */ + /** * Send a message to this channel - * @param {string} content the content to send - * @param {MessageOptions} [options={}] the options to provide + * @param {string} content The content to send + * @param {MessageOptions} [options={}] The options to provide * @returns {Promise} * @example * // send a message @@ -170,10 +53,11 @@ class TextBasedChannel { sendMessage(content, options = {}) { return this.client.rest.methods.sendMessage(this, content, options.tts, options.nonce); } + /** * Send a text-to-speech message to this channel - * @param {string} content the content to send - * @param {MessageOptions} [options={}] the options to provide + * @param {string} content The content to send + * @param {MessageOptions} [options={}] The options to provide * @returns {Promise} * @example * // send a TTS message @@ -184,6 +68,7 @@ class TextBasedChannel { sendTTSMessage(content, options = {}) { return this.client.rest.methods.sendMessage(this, content, true, options.nonce); } + /** * Send a file to this channel * @param {FileResolvable} attachment The file to send @@ -201,16 +86,15 @@ class TextBasedChannel { } } return new Promise((resolve, reject) => { - this.client.resolver.resolveFile(attachment) - .then(file => { + this.client.resolver.resolveFile(attachment).then(file => { this.client.rest.methods.sendMessage(this, undefined, false, undefined, { file, name: fileName, }).then(resolve).catch(reject); - }) - .catch(reject); + }).catch(reject); }); } + /** * The parameters to pass in when requesting previous messages from a channel. `around`, `before` and * `after` are mutually exclusive. All the parameters are optional. @@ -227,8 +111,8 @@ class TextBasedChannel { /** * Gets the past messages sent in this channel. Resolves with a Collection mapping message ID's to Message objects. - * @param {ChannelLogsQueryOptions} [options={}] the query parameters to pass in - * @returns {Promise, Error>} + * @param {ChannelLogsQueryOptions} [options={}] The query parameters to pass in + * @returns {Promise>} * @example * // get messages * channel.fetchMessages({limit: 10}) @@ -237,17 +121,15 @@ class TextBasedChannel { */ fetchMessages(options = {}) { return new Promise((resolve, reject) => { - this.client.rest.methods.getChannelMessages(this, options) - .then(data => { - const messages = new Collection(); - for (const message of data) { - const msg = new Message(this, message, this.client); - messages.set(message.id, msg); - this._cacheMessage(msg); - } - resolve(messages); - }) - .catch(reject); + this.client.rest.methods.getChannelMessages(this, options).then(data => { + const messages = new Collection(); + for (const message of data) { + const msg = new Message(this, message, this.client); + messages.set(message.id, msg); + this._cacheMessage(msg); + } + resolve(messages); + }).catch(reject); }); } @@ -278,7 +160,7 @@ class TextBasedChannel { * Stops the typing indicator in the channel. * The indicator will only stop if this is called as many times as startTyping(). * It can take a few seconds for the Client User to stop typing. - * @param {boolean} [force=false] whether or not to force the indicator to stop regardless of call count + * @param {boolean} [force=false] Whether or not to reset the call count and force the indicator to stop * @example * // stop typing in a channel * channel.stopTyping(); @@ -316,8 +198,8 @@ class TextBasedChannel { /** * Creates a Message Collector - * @param {CollectorFilterFunction} filter the filter to create the collector with - * @param {CollectorOptions} [options={}] the options to pass to the collector + * @param {CollectorFilterFunction} filter The filter to create the collector with + * @param {CollectorOptions} [options={}] The options to pass to the collector * @returns {MessageCollector} * @example * // create a message collector @@ -329,8 +211,7 @@ class TextBasedChannel { * collector.on('end', collected => console.log(`Collected ${collected.size} items`)); */ createCollector(filter, options = {}) { - const collector = new MessageCollector(this, filter, options); - return collector; + return new MessageCollector(this, filter, options); } /** @@ -346,8 +227,8 @@ class TextBasedChannel { /** * Similar to createCollector but in Promise form. Resolves with a Collection of messages that pass the specified * filter. - * @param {CollectorFilterFunction} filter the filter function to use - * @param {AwaitMessagesOptions} [options={}] optional options to pass to the internal collector + * @param {CollectorFilterFunction} filter The filter function to use + * @param {AwaitMessagesOptions} [options={}] Optional options to pass to the internal collector * @returns {Promise>} * @example * // await !vote messages @@ -372,43 +253,140 @@ class TextBasedChannel { _cacheMessage(message) { const maxSize = this.client.options.max_message_cache; - if (maxSize === 0) { - // saves on performance - return null; - } - - if (this.messages.size >= maxSize) { - this.messages.delete(this.messages.keys().next().value); - } + if (maxSize === 0) return null; + if (this.messages.size >= maxSize) this.messages.delete(this.messages.keys().next().value); this.messages.set(message.id, message); - return message; } /** * Fetches the pinned messages of this Channel and returns a Collection of them. - * @returns {Promise, Error>} + * @returns {Promise>} */ fetchPinnedMessages() { return new Promise((resolve, reject) => { - this.client.rest.methods.getChannelPinnedMessages(this) - .then(data => { - const messages = new Collection(); - for (const message of data) { - const msg = new Message(this, message, this.client); - messages.set(message.id, msg); - this._cacheMessage(msg); - } - resolve(messages); - }) - .catch(reject); + this.client.rest.methods.getChannelPinnedMessages(this).then(data => { + const messages = new Collection(); + for (const message of data) { + const msg = new Message(this, message, this.client); + messages.set(message.id, msg); + this._cacheMessage(msg); + } + resolve(messages); + }).catch(reject); }); } } -function applyProp(structure, prop) { - Object.defineProperty(structure.prototype, prop, Object.getOwnPropertyDescriptor(TextBasedChannel.prototype, prop)); +/** + * Collects messages based on a specified filter, then emits them. + * @extends {EventEmitter} + */ +class MessageCollector extends EventEmitter { + /** + * A function that takes a Message object and a MessageCollector and returns a boolean. + * ```js + * function(message, collector) { + * if (message.content.includes('discord')) { + * return true; // passed the filter test + * } + * return false; // failed the filter test + * } + * ``` + * @typedef {function} CollectorFilterFunction + */ + + /** + * An object containing options used to configure a MessageCollector. All properties are optional. + * ```js + * { + * time: null, // time in milliseconds. If specified, the collector ends after this amount of time. + * max: null, // the maximum amount of messages to handle before ending. + * } + * ``` + * @typedef {Object} CollectorOptions + */ + + /** + * @param {Channel} channel The channel to collect messages in + * @param {CollectorFilterFunction} filter The filter function + * @param {CollectorOptions} [options] Options for the collector + */ + constructor(channel, filter, options = {}) { + super(); + /** + * The channel this collector is operating on + * @type {Channel} + */ + this.channel = channel; + /** + * A function used to filter messages that the collector collects. + * @type {CollectorFilterFunction} + */ + this.filter = filter; + /** + * Options for the collecor. + * @type {CollectorOptions} + */ + this.options = options; + /** + * Whether this collector has stopped collecting Messages. + * @type {boolean} + */ + this.ended = false; + this.listener = message => this.verify(message); + this.channel.client.on('message', this.listener); + /** + * A collection of collected messages, mapped by message ID. + * @type {Collection} + */ + this.collected = new Collection(); + if (options.time) this.channel.client.setTimeout(() => this.stop('time'), options.time); + } + + /** + * Verifies a message against the filter and options + * @private + * @param {Message} message The message + * @returns {boolean} + */ + verify(message) { + if (this.channel ? this.channel.id !== message.channel.id : false) return false; + if (this.filter(message, this)) { + this.collected.set(message.id, message); + /** + * Emitted whenever the Collector receives a Message that passes the filter test. + * @param {Message} message The received message + * @param {MessageCollector} collector The collector the message passed through + * @event MessageCollector#message + */ + this.emit('message', message, this); + if (this.options.max && this.collected.size === this.options.max) this.stop('limit'); + return true; + } + return false; + } + + /** + * Stops the collector and emits `end`. + * @param {string} [reason='user'] An optional reason for stopping the collector + */ + stop(reason = 'user') { + if (this.ended) return; + this.ended = true; + this.channel.client.removeListener('message', this.listener); + /** + * Emitted when the Collector stops collecting. + * @param {Collection} collection A collection of messages collected + * during the lifetime of the Collector, mapped by the ID of the Messages. + * @param {string} reason The reason for the end of the collector. If it ended because it reached the specified time + * limit, this would be `time`. If you invoke `.stop()` without specifying a reason, this would be `user`. If it + * ended because it reached its message limit, it will be `limit`. + * @event MessageCollector#end + */ + this.emit('end', this.collected, reason); + } } exports.applyToClass = (structure, full = false) => { @@ -425,7 +403,9 @@ exports.applyToClass = (structure, full = false) => { props.push('createCollector'); props.push('awaitMessages'); } - for (const prop of props) { - applyProp(structure, prop); - } + for (const prop of props) applyProp(structure, prop); }; + +function applyProp(structure, prop) { + Object.defineProperty(structure.prototype, prop, Object.getOwnPropertyDescriptor(TextBasedChannel.prototype, prop)); +} diff --git a/src/util/ArraysEqual.js b/src/util/ArraysEqual.js new file mode 100644 index 000000000..efd8275c9 --- /dev/null +++ b/src/util/ArraysEqual.js @@ -0,0 +1,14 @@ +module.exports = function arraysEqual(a, b) { + if (a === b) return true; + if (a.length !== b.length) return false; + + for (const itemInd in a) { + const item = a[itemInd]; + const ind = b.indexOf(item); + if (ind) { + b.splice(ind, 1); + } + } + + return b.length === 0; +}; diff --git a/src/util/CloneObject.js b/src/util/CloneObject.js index 726712fb6..13366a7b7 100644 --- a/src/util/CloneObject.js +++ b/src/util/CloneObject.js @@ -1,6 +1,5 @@ module.exports = function cloneObject(obj) { const cloned = Object.create(obj); Object.assign(cloned, obj); - return cloned; }; diff --git a/src/util/Collection.js b/src/util/Collection.js index 9c85d7353..16612b904 100644 --- a/src/util/Collection.js +++ b/src/util/Collection.js @@ -3,10 +3,9 @@ * @extends {Map} */ class Collection extends Map { - /** * Returns an ordered array of the values of this collection. - * @returns {Array} + * @returns {*[]} * @example * // identical to: * Array.from(collection.values()); @@ -17,18 +16,19 @@ class Collection extends Map { /** * Returns the first item in this collection. - * @returns {Object} + * @returns {*} * @example * // identical to: * Array.from(collection.values())[0]; */ first() { - return this.array()[0]; + return this.values().next().value; } /** - * Returns the last item in this collection. - * @returns {Object} + * Returns the last item in this collection. This is a relatively slow operation, + * since an array copy of the values must be made to find the last element. + * @returns {*} */ last() { const arr = this.array(); @@ -36,8 +36,9 @@ class Collection extends Map { } /** - * Returns a random item from this collection. - * @returns {Object} + * Returns a random item from this collection. This is a relatively slow operation, + * since an array copy of the values must be made to find a random element. + * @returns {*} */ random() { const arr = this.array(); @@ -47,32 +48,21 @@ class Collection extends Map { /** * If the items in this collection have a delete method (e.g. messages), invoke * the delete method. Returns an array of promises - * @returns {Array} + * @returns {Promise[]} */ deleteAll() { const returns = []; for (const item of this.values()) { - if (item.delete) { - returns.push(item.delete()); - } + if (item.delete) returns.push(item.delete()); } return returns; } - /** - * The length (size) of this collection. - * @readonly - * @type {number} - */ - get length() { - return this.size; - } - /** * Returns an array of items where `item[key] === value` of the collection - * @param {string} key the key to filter by - * @param {*} value the expected value - * @returns {Array} + * @param {string} key The key to filter by + * @param {*} value The expected value + * @returns {*[]} * @example * collection.getAll('username', 'Bob'); */ @@ -81,18 +71,16 @@ class Collection extends Map { if (typeof value === 'undefined') throw new Error('value must be specified'); const results = []; for (const item of this.values()) { - if (item[key] === value) { - results.push(item); - } + if (item[key] === value) results.push(item); } return results; } /** * Returns a single item where `item[key] === value` - * @param {string} key the key to filter by - * @param {*} value the expected value - * @returns {Object} + * @param {string} key The key to filter by + * @param {*} value The expected value + * @returns {*} * @example * collection.get('id', '123123...'); */ @@ -100,17 +88,15 @@ class Collection extends Map { if (typeof key !== 'string') throw new TypeError('key must be a string'); if (typeof value === 'undefined') throw new Error('value must be specified'); for (const item of this.values()) { - if (item[key] === value) { - return item; - } + if (item[key] === value) return item; } return null; } /** * Returns true if the collection has an item where `item[key] === value` - * @param {string} key the key to filter by - * @param {*} value the expected value + * @param {string} key The key to filter by + * @param {*} value The expected value * @returns {boolean} * @example * if (collection.exists('id', '123123...')) { @@ -121,32 +107,26 @@ class Collection extends Map { return Boolean(this.find(key, value)); } - _arrayMethod(method, args) { - return Array.prototype[method].apply(this.array(), args); - } - /** * Identical to * [Array.filter()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter), * but returns a Collection instead of an Array. - * @param {function} callback the callback used to filter - * @param {Object} [thisArg] value to set as this when filtering + * @param {function} callback Function used to filter (should return a boolean) + * @param {Object} [thisArg] Value to set as this when filtering * @returns {Collection} */ filter(...args) { const newArray = this.array().filter(...args); const collection = new Collection(); - for (const item of newArray) { - collection.set(item.id, item); - } + for (const item of newArray) collection.set(item.id, item); return collection; } /** * Functionally identical shortcut to `collection.array().map(...)`. - * @param {function} callback Function that produces an element of the new Array, taking three arguments. + * @param {function} callback Function that produces an element of the new Array, taking three arguments * @param {*} [thisArg] Optional. Value to use as this when executing callback. - * @returns {array} + * @returns {*[]} */ map(...args) { return this.array().map(...args); diff --git a/src/util/Constants.js b/src/util/Constants.js index db3bb220b..fbf359937 100644 --- a/src/util/Constants.js +++ b/src/util/Constants.js @@ -67,7 +67,7 @@ exports.Errors = { NO_BOT_ACCOUNT: new Error('you should ideally be using a bot account!'), BAD_WS_MESSAGE: new Error('a bad message was received from the websocket - bad compression or not json'), TOOK_TOO_LONG: new Error('something took too long to do'), - NOT_A_PERMISSION: new Error('that is not a valid permission number'), + NOT_A_PERMISSION: new Error('that is not a valid permission string or number'), INVALID_RATE_LIMIT_METHOD: new Error('unknown rate limiting method'), BAD_LOGIN: new Error('incorrect login details were provided'), }; @@ -235,10 +235,7 @@ const PermissionFlags = exports.PermissionFlags = { }; let _ALL_PERMISSIONS = 0; - -for (const key in PermissionFlags) { - _ALL_PERMISSIONS |= PermissionFlags[key]; -} +for (const key in PermissionFlags) _ALL_PERMISSIONS |= PermissionFlags[key]; exports.ALL_PERMISSIONS = _ALL_PERMISSIONS; diff --git a/src/util/MergeDefault.js b/src/util/MergeDefault.js index d07044cdc..b09f9701b 100644 --- a/src/util/MergeDefault.js +++ b/src/util/MergeDefault.js @@ -1,10 +1,5 @@ module.exports = function merge(def, given) { - if (!given) { - return def; - } - - given = given || {}; - + if (!given) return def; for (const key in def) { if (!{}.hasOwnProperty.call(given, key)) { given[key] = def[key];