From 4fb7e64a39b1889191634f5f60fe9e20ad71ebfe Mon Sep 17 00:00:00 2001 From: bdistin Date: Mon, 15 Jan 2018 18:20:09 -0600 Subject: [PATCH 1/6] Add parent, nsfw, bitrate, and userLimit options to GuildChannel.clone() (#2259) * Add parent, nsfw, bitrate, and userLimit options to GuildChannel.clone() * fix lint --- src/structures/GuildChannel.js | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/structures/GuildChannel.js b/src/structures/GuildChannel.js index 11cc571e9..a7c5b5244 100644 --- a/src/structures/GuildChannel.js +++ b/src/structures/GuildChannel.js @@ -407,11 +407,23 @@ class GuildChannel extends Channel { * @param {boolean} [options.withPermissions=true] Whether to clone the channel with this channel's * permission overwrites * @param {boolean} [options.withTopic=true] Whether to clone the channel with this channel's topic + * @param {boolean} [options.nsfw=this.nsfw] Whether the new channel is nsfw (only text) + * @param {number} [options.bitrate=this.bitrate] Bitrate of the new channel in bits (only voice) + * @param {number} [options.userLimit=this.userLimit] Maximum amount of users allowed in the new channel (only voice) + * @param {ChannelResolvable} [options.parent=this.parent] The parent of the new channel * @param {string} [options.reason] Reason for cloning this channel * @returns {Promise} */ - clone({ name = this.name, withPermissions = true, withTopic = true, reason } = {}) { - const options = { overwrites: withPermissions ? this.permissionOverwrites : [], reason, type: this.type }; + clone({ name = this.name, withPermissions = true, withTopic = true, nsfw, parent, bitrate, userLimit, reason } = {}) { + const options = { + overwrites: withPermissions ? this.permissionOverwrites : [], + nsfw: typeof nsfw === 'undefined' ? this.nsfw : nsfw, + parent: parent || this.parent, + bitrate: bitrate || this.bitrate, + userLimit: userLimit || this.userLimit, + reason, + type: this.type, + }; return this.guild.channels.create(name, options) .then(channel => withTopic ? channel.setTopic(this.topic) : channel); } From 3038d4b2c70f9e72dab4ed1d808e565a22c3c230 Mon Sep 17 00:00:00 2001 From: bdistin Date: Mon, 15 Jan 2018 18:20:36 -0600 Subject: [PATCH 2/6] Address missing application docs in setPresence (#2257) fixes #2103 according to how crawl says it should be fixed in #2104 --- src/structures/ClientUser.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/structures/ClientUser.js b/src/structures/ClientUser.js index 42556ae2c..92fe176f5 100644 --- a/src/structures/ClientUser.js +++ b/src/structures/ClientUser.js @@ -187,7 +187,9 @@ class ClientUser extends Structures.get('User') { * @typedef {Object} PresenceData * @property {PresenceStatus} [status] Status of the user * @property {boolean} [afk] Whether the user is AFK - * @property {Object} [activity] activity the user is playing + * @property {Object} [activity] Activity the user is playing + * @property {Object|string} [activity.application] An application object or application id + * @property {string} [activity.application.id] The id of the application * @property {string} [activity.name] Name of the activity * @property {ActivityType|number} [activity.type] Type of the activity * @property {string} [activity.url] Stream url From 3d32dea5e1a0e6ada1d04b924e6543ac5260c0f5 Mon Sep 17 00:00:00 2001 From: bdistin Date: Mon, 15 Jan 2018 18:20:51 -0600 Subject: [PATCH 3/6] remove pointless function from GuildEmojisUpdate (#2256) --- src/client/actions/GuildEmojisUpdate.js | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/client/actions/GuildEmojisUpdate.js b/src/client/actions/GuildEmojisUpdate.js index 8656a34cd..90f43eeba 100644 --- a/src/client/actions/GuildEmojisUpdate.js +++ b/src/client/actions/GuildEmojisUpdate.js @@ -1,17 +1,11 @@ const Action = require('./Action'); -function mappify(iterable) { - const map = new Map(); - for (const x of iterable) map.set(...x); - return map; -} - class GuildEmojisUpdateAction extends Action { handle(data) { const guild = this.client.guilds.get(data.guild_id); if (!guild || !guild.emojis) return; - const deletions = mappify(guild.emojis.entries()); + const deletions = new Map(guild.emojis); for (const emoji of data.emojis) { // Determine type of emoji event From 4122db027531a0e9382a6d01ce60f3848ca3bb35 Mon Sep 17 00:00:00 2001 From: bdistin Date: Mon, 15 Jan 2018 18:24:19 -0600 Subject: [PATCH 4/6] Return undefined from Collection.find() / findKey() (#2260) To be compliant with Array.find() / findIndex() --- src/util/Collection.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/util/Collection.js b/src/util/Collection.js index 1f3fc6307..acddf0518 100644 --- a/src/util/Collection.js +++ b/src/util/Collection.js @@ -192,12 +192,12 @@ class Collection extends Map { for (const item of this.values()) { if (item[propOrFn] === value) return item; } - return null; + return undefined; } else if (typeof propOrFn === 'function') { for (const [key, val] of this) { if (propOrFn(val, key, this)) return val; } - return null; + return undefined; } else { throw new Error('First argument must be a property string or a function.'); } @@ -223,12 +223,12 @@ class Collection extends Map { for (const [key, val] of this) { if (val[propOrFn] === value) return key; } - return null; + return undefined; } else if (typeof propOrFn === 'function') { for (const [key, val] of this) { if (propOrFn(val, key, this)) return key; } - return null; + return undefined; } else { throw new Error('First argument must be a property string or a function.'); } From 36555c1cea5d559eb5cfbaebec7866b21cd1a835 Mon Sep 17 00:00:00 2001 From: Isabella Date: Mon, 15 Jan 2018 18:32:40 -0600 Subject: [PATCH 5/6] refactor(GuildMember#manageable): refactored kickable and bannable (#2211) * refactor(GuildMember#manageable): merged kickable and bannable code * hydar suggestion --- src/structures/GuildMember.js | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/src/structures/GuildMember.js b/src/structures/GuildMember.js index 80c6ce48c..c7384d875 100644 --- a/src/structures/GuildMember.js +++ b/src/structures/GuildMember.js @@ -259,17 +259,24 @@ class GuildMember extends Base { return new Permissions(this.roles.map(role => role.permissions)).freeze(); } + /** + * Whether the member is manageable in terms of role hierarchy by the client user + * @type {boolean} + * @readonly + */ + get manageable() { + if (this.user.id === this.guild.ownerID) return false; + if (this.user.id === this.client.user.id) return false; + return this.guild.me.highestRole.comparePositionTo(this.highestRole) > 0; + } + /** * Whether the member is kickable by the client user * @type {boolean} * @readonly */ get kickable() { - if (this.user.id === this.guild.ownerID) return false; - if (this.user.id === this.client.user.id) return false; - const clientMember = this.guild.member(this.client.user); - if (!clientMember.permissions.has(Permissions.FLAGS.KICK_MEMBERS)) return false; - return clientMember.highestRole.comparePositionTo(this.highestRole) > 0; + return this.manageable && this.guild.me.permissions.has(Permissions.FLAGS.KICK_MEMBERS); } /** @@ -278,11 +285,7 @@ class GuildMember extends Base { * @readonly */ get bannable() { - if (this.user.id === this.guild.ownerID) return false; - if (this.user.id === this.client.user.id) return false; - const clientMember = this.guild.member(this.client.user); - if (!clientMember.permissions.has(Permissions.FLAGS.BAN_MEMBERS)) return false; - return clientMember.highestRole.comparePositionTo(this.highestRole) > 0; + return this.manageable && this.guild.me.permissions.has(Permissions.FLAGS.BAN_MEMBERS); } /** From e576387fea2a973eefe298a6f3610eb0d8b54999 Mon Sep 17 00:00:00 2001 From: Alex Date: Tue, 16 Jan 2018 02:33:58 +0200 Subject: [PATCH 6/6] Fix ReactionCollector#remove and make Collector interface more consistent (#2221) * Fix ReactionCollector#remove and make Collector interface more consistent * Move those below the doc * Remove object spread * Only emit event arguments * Forgot to delete this line * Update docs * Also fix this * More edits to docs * Snowflake|string --- src/structures/MessageCollector.js | 21 ++++++++++------ src/structures/ReactionCollector.js | 33 ++++++++++++++++++-------- src/structures/interfaces/Collector.js | 12 ++++------ 3 files changed, 41 insertions(+), 25 deletions(-) diff --git a/src/structures/MessageCollector.js b/src/structures/MessageCollector.js index a9260382c..d034a96db 100644 --- a/src/structures/MessageCollector.js +++ b/src/structures/MessageCollector.js @@ -51,24 +51,31 @@ class MessageCollector extends Collector { /** * Handles a message for possible collection. * @param {Message} message The message that could be collected - * @returns {?{key: Snowflake, value: Message}} + * @returns {?Snowflake} * @private */ collect(message) { + /** + * Emitted whenever a message is collected. + * @event MessageCollector#collect + * @param {Message} message The message that was collected + */ if (message.channel.id !== this.channel.id) return null; this.received++; - return { - key: message.id, - value: message, - }; + return message.id; } /** * Handles a message for possible disposal. - * @param {Message} message The message that could be disposed - * @returns {?string} + * @param {Message} message The message that could be disposed of + * @returns {?Snowflake} */ dispose(message) { + /** + * Emitted whenever a message is disposed of. + * @event MessageCollector#dispose + * @param {Message} message The message that was disposed of + */ return message.channel.id === this.channel.id ? message.id : null; } diff --git a/src/structures/ReactionCollector.js b/src/structures/ReactionCollector.js index 60bdabc48..27582893a 100644 --- a/src/structures/ReactionCollector.js +++ b/src/structures/ReactionCollector.js @@ -52,12 +52,12 @@ class ReactionCollector extends Collector { this.client.removeListener(Events.MESSAGE_REACTION_REMOVE_ALL, this.empty); }); - this.on('collect', (collected, reaction, user) => { + this.on('collect', (reaction, user) => { this.total++; this.users.set(user.id, user); }); - this.on('dispose', (disposed, reaction, user) => { + this.on('remove', (reaction, user) => { this.total--; if (!this.collected.some(r => r.users.has(user.id))) this.users.delete(user.id); }); @@ -66,23 +66,33 @@ class ReactionCollector extends Collector { /** * Handles an incoming reaction for possible collection. * @param {MessageReaction} reaction The reaction to possibly collect - * @returns {?{key: Snowflake, value: MessageReaction}} + * @returns {?Snowflake|string} * @private */ collect(reaction) { + /** + * Emitted whenever a reaction is collected. + * @event ReactionCollector#collect + * @param {MessageReaction} reaction The reaction that was collected + * @param {User} user The user that added the reaction + */ if (reaction.message.id !== this.message.id) return null; - return { - key: ReactionCollector.key(reaction), - value: reaction, - }; + return ReactionCollector.key(reaction); } /** * Handles a reaction deletion for possible disposal. - * @param {MessageReaction} reaction The reaction to possibly dispose + * @param {MessageReaction} reaction The reaction to possibly dispose of + * @param {User} user The user that removed the reaction * @returns {?Snowflake|string} */ - dispose(reaction) { + dispose(reaction, user) { + /** + * Emitted whenever a reaction is disposed of. + * @event ReactionCollector#dispose + * @param {MessageReaction} reaction The reaction that was disposed of + * @param {User} user The user that removed the reaction + */ if (reaction.message.id !== this.message.id) return null; /** @@ -91,8 +101,11 @@ class ReactionCollector extends Collector { * is removed. * @event ReactionCollector#remove * @param {MessageReaction} reaction The reaction that was removed + * @param {User} user The user that removed the reaction */ - if (this.collected.has(reaction)) this.emit('remove', reaction); + if (this.collected.has(ReactionCollector.key(reaction))) { + this.emit('remove', reaction, user); + } return reaction.count ? null : ReactionCollector.key(reaction); } diff --git a/src/structures/interfaces/Collector.js b/src/structures/interfaces/Collector.js index 0e7f1f956..0578dadfd 100644 --- a/src/structures/interfaces/Collector.js +++ b/src/structures/interfaces/Collector.js @@ -78,15 +78,14 @@ class Collector extends EventEmitter { const collect = this.collect(...args); if (collect && this.filter(...args, this.collected)) { - this.collected.set(collect.key, collect.value); + this.collected.set(collect, args[0]); /** * Emitted whenever an element is collected. * @event Collector#collect - * @param {*} element The element that got collected * @param {...*} args The arguments emitted by the listener */ - this.emit('collect', collect.value, ...args); + this.emit('collect', ...args); } this.checkEnd(); } @@ -101,17 +100,14 @@ class Collector extends EventEmitter { const dispose = this.dispose(...args); if (!dispose || !this.filter(...args) || !this.collected.has(dispose)) return; - - const value = this.collected.get(dispose); this.collected.delete(dispose); /** - * Emitted whenever an element has been disposed. + * Emitted whenever an element is disposed of. * @event Collector#dispose - * @param {*} element The element that was disposed * @param {...*} args The arguments emitted by the listener */ - this.emit('dispose', value, ...args); + this.emit('dispose', ...args); this.checkEnd(); }