diff --git a/src/client/Client.js b/src/client/Client.js index 6026a0d33..89d94f00f 100644 --- a/src/client/Client.js +++ b/src/client/Client.js @@ -406,7 +406,7 @@ class Client extends EventEmitter { /** * Generates a link that can be used to invite the bot to a guild. * This is only available when using a bot account. - * @param {PermissionResolvable|PermissionResolvable[]} [permissions] Permissions to request + * @param {PermissionResolvable} [permissions] Permissions to request * @returns {Promise} * @example * client.generateInvite(['SEND_MESSAGES', 'MANAGE_GUILD', 'MENTION_EVERYONE']) diff --git a/src/client/rest/RESTMethods.js b/src/client/rest/RESTMethods.js index a826cd3b0..1dd054b9b 100644 --- a/src/client/rest/RESTMethods.js +++ b/src/client/rest/RESTMethods.js @@ -5,6 +5,7 @@ const Constants = require('../../util/Constants'); const Endpoints = Constants.Endpoints; const Collection = require('../../util/Collection'); const Util = require('../../util/Util'); +const resolvePermissions = require('../../structures/shared/resolvePermissions'); const User = require('../../structures/User'); const GuildMember = require('../../structures/GuildMember'); @@ -251,34 +252,10 @@ class RESTMethods { } createChannel(guild, channelName, channelType, overwrites, reason) { - if (overwrites instanceof Collection || overwrites instanceof Array) { - overwrites = overwrites.map(overwrite => { - let allow = overwrite.allow || overwrite._allowed; - let deny = overwrite.deny || overwrite._denied; - if (allow instanceof Array) allow = Permissions.resolve(allow); - if (deny instanceof Array) deny = Permissions.resolve(deny); - - const role = this.client.resolver.resolveRole(guild, overwrite.id); - if (role) { - overwrite.id = role.id; - overwrite.type = 'role'; - } else { - overwrite.id = this.client.resolver.resolveUserID(overwrite.id); - overwrite.type = 'member'; - } - - return { - allow, - deny, - type: overwrite.type, - id: overwrite.id, - }; - }); - } return this.rest.makeRequest('post', Endpoints.Guild(guild).channels, true, { name: channelName, type: channelType ? Constants.ChannelTypes[channelType.toUpperCase()] : 'text', - permission_overwrites: overwrites, + permission_overwrites: resolvePermissions.call(this, overwrites, guild), }, undefined, reason).then(data => this.client.actions.ChannelCreate.handle(data).channel); } @@ -343,6 +320,8 @@ class RESTMethods { data.bitrate = _data.bitrate || (channel.bitrate ? channel.bitrate * 1000 : undefined); data.user_limit = typeof _data.userLimit !== 'undefined' ? _data.userLimit : channel.userLimit; data.parent_id = _data.parent; + data.permission_overwrites = _data.permissionOverwrites ? + resolvePermissions.call(this, _data.permissionOverwrites, channel.guild) : undefined; return this.rest.makeRequest('patch', Endpoints.Channel(channel), true, data, undefined, reason).then(newData => this.client.actions.ChannelUpdate.handle(newData).updated ); diff --git a/src/structures/Guild.js b/src/structures/Guild.js index 0150d6483..572175f91 100644 --- a/src/structures/Guild.js +++ b/src/structures/Guild.js @@ -969,8 +969,8 @@ class Guild { /** * Can be used to overwrite permissions when creating a channel. * @typedef {Object} ChannelCreationOverwrites - * @property {PermissionResolvable[]|number} [allow] The permissions to allow - * @property {PermissionResolvable[]|number} [deny] The permissions to deny + * @property {PermissionResolvable|number} [allow] The permissions to allow + * @property {PermissionResolvable|number} [deny] The permissions to deny * @property {RoleResolvable|UserResolvable} id ID of the role or member this overwrite is for */ diff --git a/src/structures/GuildChannel.js b/src/structures/GuildChannel.js index 099854f01..0a7901f3a 100644 --- a/src/structures/GuildChannel.js +++ b/src/structures/GuildChannel.js @@ -140,6 +140,28 @@ class GuildChannel extends Channel { }; } + /** + * Replaces the permission overwrites for a channel + * @param {Object} [options] Options + * @param {Array} [options.overwrites] Permission overwrites + * @param {string} [options.reason] Reason for updating the channel overwrites + * @returns {Promise} + * @example + * channel.replacePermissionOverwrites({ + * overwrites: [ + * { + * id: message.author.id, + * denied: ['VIEW_CHANNEL'], + * }, + * ], + * reason: 'Needed to change permissions' + * }); + */ + replacePermissionOverwrites({ overwrites, reason } = {}) { + return this.edit({ permissionOverwrites: overwrites, reason }) + .then(() => this); + } + /** * An object mapping permission flags to `true` (enabled), `false` (disabled), or `null` (not set). * ```js @@ -215,6 +237,21 @@ class GuildChannel extends Channel { return this.client.rest.methods.setChannelOverwrite(this, payload, reason).then(() => this); } + /** + * Locks in the permission overwrites from the parent channel. + * @returns {Promise} + */ + lockPermissions() { + if (!this.parent) return Promise.reject(new TypeError('Could not find a parent to this guild channel.')); + const permissionOverwrites = this.parent.permissionOverwrites.map(overwrite => ({ + deny: overwrite.deny.bitfield, + allow: overwrite.allow.bitfield, + id: overwrite.id, + type: overwrite.type, + })); + return this.edit({ permissionOverwrites }); + } + /** * The data for a guild channel. * @typedef {Object} ChannelData diff --git a/src/structures/GuildMember.js b/src/structures/GuildMember.js index 1a56868ba..98d879769 100644 --- a/src/structures/GuildMember.js +++ b/src/structures/GuildMember.js @@ -305,7 +305,7 @@ class GuildMember { /** * Checks if any of this member's roles have a permission. - * @param {PermissionResolvable|PermissionResolvable[]} permission Permission(s) to check for + * @param {PermissionResolvable} permission Permission(s) to check for * @param {boolean} [explicit=false] Whether to require the role to explicitly have the exact permission * **(deprecated)** * @param {boolean} [checkAdmin] Whether to allow the administrator permission to override @@ -323,7 +323,7 @@ class GuildMember { /** * Checks whether the roles of this member allows them to perform specific actions. - * @param {PermissionResolvable[]} permissions The permissions to check for + * @param {PermissionResolvable} permissions The permissions to check for * @param {boolean} [explicit=false] Whether to require the member to explicitly have the exact permissions * @returns {boolean} * @deprecated @@ -335,11 +335,12 @@ class GuildMember { /** * Checks whether the roles of this member allows them to perform specific actions, and lists any missing permissions. - * @param {PermissionResolvable[]} permissions The permissions to check for + * @param {PermissionResolvable} permissions The permissions to check for * @param {boolean} [explicit=false] Whether to require the member to explicitly have the exact permissions - * @returns {PermissionResolvable[]} + * @returns {PermissionResolvable} */ missingPermissions(permissions, explicit = false) { + if (!(permissions instanceof Array)) permissions = [permissions]; return this.permissions.missing(permissions, explicit); } diff --git a/src/structures/PermissionOverwrites.js b/src/structures/PermissionOverwrites.js index efe9e956b..19630636e 100644 --- a/src/structures/PermissionOverwrites.js +++ b/src/structures/PermissionOverwrites.js @@ -1,3 +1,5 @@ +const Permissions = require('../util/Permissions'); + /** * Represents a permission overwrite for a role or member in a guild channel. */ @@ -27,8 +29,29 @@ class PermissionOverwrites { */ this.type = data.type; + /** + * The permissions that are denied for the user or role as a bitfield. + * @type {number} + */ this.deny = data.deny; + + /** + * The permissions that are allowed for the user or role as a bitfield. + * @type {number} + */ this.allow = data.allow; + + /** + * The permissions that are denied for the user or role. + * @type {Permissions} + */ + this.denied = new Permissions(data.deny).freeze(); + + /** + * The permissions that are allowed for the user or role. + * @type {Permissions} + */ + this.allowed = new Permissions(data.allow).freeze(); } /** diff --git a/src/structures/Role.js b/src/structures/Role.js index 389e96949..056cf98fd 100644 --- a/src/structures/Role.js +++ b/src/structures/Role.js @@ -153,7 +153,7 @@ class Role { /** * Checks if the role has a permission. - * @param {PermissionResolvable|PermissionResolvable[]} permission Permission(s) to check for + * @param {PermissionResolvable} permission Permission(s) to check for * @param {boolean} [explicit=false] Whether to require the role to explicitly have the exact permission * **(deprecated)** * @param {boolean} [checkAdmin] Whether to allow the administrator permission to override @@ -175,7 +175,7 @@ class Role { /** * Checks if the role has all specified permissions. - * @param {PermissionResolvable[]} permissions The permissions to check for + * @param {PermissionResolvable} permissions The permissions to check for * @param {boolean} [explicit=false] Whether to require the role to explicitly have the exact permissions * @returns {boolean} * @deprecated @@ -201,7 +201,7 @@ class Role { * @property {ColorResolvable} [color] The color of the role, either a hex string or a base 10 number * @property {boolean} [hoist] Whether or not the role should be hoisted * @property {number} [position] The position of the role - * @property {PermissionResolvable[]|number} [permissions] The permissions of the role + * @property {PermissionResolvable|number} [permissions] The permissions of the role * @property {boolean} [mentionable] Whether or not the role should be mentionable */ @@ -282,7 +282,7 @@ class Role { /** * Set the permissions of the role. - * @param {PermissionResolvable|PermissionResolvable[]} permissions The permissions of the role + * @param {PermissionResolvable} permissions The permissions of the role * @param {string} [reason] Reason for changing the role's permissions * @returns {Promise} * @example diff --git a/src/structures/shared/resolvePermissions.js b/src/structures/shared/resolvePermissions.js new file mode 100644 index 000000000..c266d46e2 --- /dev/null +++ b/src/structures/shared/resolvePermissions.js @@ -0,0 +1,26 @@ +const Permissions = require('../../util/Permissions'); +const Collection = require('../../util/Collection'); + +module.exports = function resolvePermissions(overwrites, guild) { + if (overwrites instanceof Collection || overwrites instanceof Array) { + overwrites = overwrites.map(overwrite => { + const role = this.client.resolver.resolveRole(guild, overwrite.id); + if (role) { + overwrite.id = role.id; + overwrite.type = 'role'; + } else { + overwrite.id = this.client.resolver.resolveUserID(overwrite.id); + overwrite.type = 'member'; + } + + return { + allow: Permissions.resolve(overwrite.allow || overwrite.allowed || 0), + deny: Permissions.resolve(overwrite.deny || overwrite.denied || 0), + type: overwrite.type, + id: overwrite.id, + }; + }); + } + + return overwrites; +}; diff --git a/src/util/Permissions.js b/src/util/Permissions.js index 4ad64d524..901bed312 100644 --- a/src/util/Permissions.js +++ b/src/util/Permissions.js @@ -9,7 +9,7 @@ const util = require('util'); class Permissions { /** * @param {GuildMember} [member] Member the permissions are for **(deprecated)** - * @param {number|PermissionResolvable[]} permissions Permissions or bitfield to read from + * @param {number|PermissionResolvable} permissions Permissions or bitfield to read from */ constructor(member, permissions) { permissions = typeof member === 'object' && !(member instanceof Array) ? permissions : member; @@ -53,7 +53,7 @@ class Permissions { /** * Checks whether the bitfield has a permission, or multiple permissions. - * @param {PermissionResolvable|PermissionResolvable[]} permission Permission(s) to check for + * @param {PermissionResolvable} permission Permission(s) to check for * @param {boolean} [checkAdmin=true] Whether to allow the administrator permission to override * @returns {boolean} */ @@ -66,11 +66,12 @@ class Permissions { /** * Gets all given permissions that are missing from the bitfield. - * @param {PermissionResolvable[]} permissions Permissions to check for + * @param {PermissionResolvable} permissions Permissions to check for * @param {boolean} [checkAdmin=true] Whether to allow the administrator permission to override - * @returns {PermissionResolvable[]} + * @returns {PermissionResolvable} */ missing(permissions, checkAdmin = true) { + if (!(permissions instanceof Array)) permissions = [permissions]; return permissions.filter(p => !this.has(p, checkAdmin)); } @@ -128,7 +129,7 @@ class Permissions { /** * Checks whether the user has all specified permissions. - * @param {PermissionResolvable[]} permissions The permissions to check for + * @param {PermissionResolvable} permissions The permissions to check for * @param {boolean} [explicit=false] Whether to require the user to explicitly have the exact permissions * @returns {boolean} * @see {@link Permissions#has} @@ -140,9 +141,9 @@ class Permissions { /** * Checks whether the user has all specified permissions, and lists any missing permissions. - * @param {PermissionResolvable[]} permissions The permissions to check for + * @param {PermissionResolvable} permissions The permissions to check for * @param {boolean} [explicit=false] Whether to require the user to explicitly have the exact permissions - * @returns {PermissionResolvable[]} + * @returns {PermissionResolvable} * @see {@link Permissions#missing} * @deprecated */ @@ -150,6 +151,14 @@ class Permissions { return this.missing(permissions, !explicit); } + /** + * Freezes these permissions, making them immutable. + * @returns {Permissions} These permissions + */ + freeze() { + return Object.freeze(this); + } + valueOf() { return this.bitfield; } @@ -158,12 +167,12 @@ class Permissions { * Data that can be resolved to give a permission number. This can be: * * A string (see {@link Permissions.FLAGS}) * * A permission number - * @typedef {string|number} PermissionResolvable + * @typedef {string|number|Permissions|PermissionResolvable[]} PermissionResolvable */ /** * Resolves permissions to their numeric form. - * @param {PermissionResolvable|PermissionResolvable[]} permission - Permission(s) to resolve + * @param {PermissionResolvable} permission - Permission(s) to resolve * @returns {number} */ static resolve(permission) {