const Channel = require('./Channel'); const PermissionOverwrites = require('./PermissionOverwrites'); const EvaluatedPermissions = require('./EvaluatedPermissions'); const Constants = require('../util/Constants'); 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 Guild Channel (i.e. Text Channels and Voice Channels) * @extends {Channel} */ class GuildChannel extends Channel { constructor(guild, data) { super(guild.client, data, guild); } setup(data) { super.setup(data); /** * The type of the Guild Channel * @type {Number} */ this.type = data.type; /** * The topic of the Guild Channel, if there is one. * @type {?String} */ this.topic = data.topic; /** * The position of the channel in the list. * @type {Number} */ this.position = data.position; /** * The name of the Guild Channel * @type {String} */ this.name = data.name; /** * The ID of the last message in the channel, if one was sent. * @type {?String} */ this.lastMessageID = data.last_message_id; this.ow = data.permission_overwrites; /** * A list of permission overwrites in this channel for roles and users. * @type {Array} */ this.permissionOverwrites = []; if (data.permission_overwrites) { for (const overwrite of data.permission_overwrites) { this.permissionOverwrites.push(new PermissionOverwrites(this, overwrite)); } } } /** * 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 * @returns {Boolean} */ equals(other) { let base = ( this.type === other.type && this.topic === other.topic && this.position === other.position && this.name === other.name && this.id === other.id ); if (base) { if (other.permission_overwrites) { const thisIDSet = this.permissionOverwrites.map(overwrite => overwrite.id); const otherIDSet = other.permission_overwrites.map(overwrite => overwrite.id); if (arraysEqual(thisIDSet, otherIDSet)) { base = true; } else { base = false; } } else { base = false; } } return base; } /** * 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 * @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); } const roles = member.roles; let permissions = 0; const overwrites = this.overwritesFor(member, true); for (const role of roles) { 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; } return new EvaluatedPermissions(member, permissions); } return null; } overwritesFor(member, verified) { // for speed if (!verified) member = this.client.resolver.resolveGuildMember(this.guild, member); if (member) { const memberRoles = member._roles; const roleOverwrites = []; const memberOverwrites = []; for (const overwrite of this.permissionOverwrites) { if (overwrite.id === member.id) { memberOverwrites.push(overwrite); } else if (memberRoles.indexOf(overwrite.id) > -1) { roleOverwrites.push(overwrite); } } return { role: roleOverwrites, member: memberOverwrites, }; } return []; } edit(data) { return this.client.rest.methods.updateChannel(this, data); } /** * Set a new name for the Guild Channel * @param {String} name the new name for the guild channel * @returns {Promise} * @example * // set a new channel name * channel.setName('not general') * .then(newChannel => console.log(`Channel's new name is ${newChannel.name}`)) * .catch(console.log); */ setName(name) { return this.client.rest.methods.updateChannel(this, { name }); } /** * Set a new position for the Guild Channel * @param {Number} position the new position for the guild channel * @returns {Promise} * @example * // set a new channel position * channel.setPosition(2) * .then(newChannel => console.log(`Channel's new position is ${newChannel.position}`)) * .catch(console.log); */ setPosition(position) { return this.rest.client.rest.methods.updateChannel(this, { position }); } /** * Set a new topic for the Guild Channel * @param {String} topic the new topic for the guild channel * @returns {Promise} * @example * // set a new channel topic * channel.setTopic('needs more rate limiting') * .then(newChannel => console.log(`Channel's new topic is ${newChannel.topic}`)) * .catch(console.log); */ setTopic(topic) { return this.rest.client.rest.methods.updateChannel(this, { topic }); } /** * When concatenated with a String, this automatically concatenates the Channel's name instead of the Channel object. * @returns {String} * @example * // Outputs: Hello from general * console.log(`Hello from ${channel}`); * @example * // Outputs: Hello from general * console.log('Hello from ' + ${channel}); */ toString() { return this.name; } } module.exports = GuildChannel;