diff options
author | sowgro <tpoke.ferrari@gmail.com> | 2023-09-02 19:12:47 -0400 |
---|---|---|
committer | sowgro <tpoke.ferrari@gmail.com> | 2023-09-02 19:12:47 -0400 |
commit | e4450c8417624b71d779cb4f41692538f9165e10 (patch) | |
tree | b70826542223ecdf8a7a259f61b0a1abb8a217d8 /node_modules/discord.js/src/structures/GuildChannel.js | |
download | sowbot3-e4450c8417624b71d779cb4f41692538f9165e10.tar.gz sowbot3-e4450c8417624b71d779cb4f41692538f9165e10.tar.bz2 sowbot3-e4450c8417624b71d779cb4f41692538f9165e10.zip |
first commit
Diffstat (limited to 'node_modules/discord.js/src/structures/GuildChannel.js')
-rw-r--r-- | node_modules/discord.js/src/structures/GuildChannel.js | 472 |
1 files changed, 472 insertions, 0 deletions
diff --git a/node_modules/discord.js/src/structures/GuildChannel.js b/node_modules/discord.js/src/structures/GuildChannel.js new file mode 100644 index 0000000..c066c71 --- /dev/null +++ b/node_modules/discord.js/src/structures/GuildChannel.js @@ -0,0 +1,472 @@ +'use strict'; + +const { Snowflake } = require('@sapphire/snowflake'); +const { PermissionFlagsBits, ChannelType } = require('discord-api-types/v10'); +const { BaseChannel } = require('./BaseChannel'); +const { DiscordjsError, ErrorCodes } = require('../errors'); +const PermissionOverwriteManager = require('../managers/PermissionOverwriteManager'); +const { VoiceBasedChannelTypes } = require('../util/Constants'); +const PermissionsBitField = require('../util/PermissionsBitField'); +const { getSortableGroupTypes } = require('../util/Util'); + +/** + * Represents a guild channel from any of the following: + * - {@link TextChannel} + * - {@link VoiceChannel} + * - {@link CategoryChannel} + * - {@link NewsChannel} + * - {@link StageChannel} + * - {@link ForumChannel} + * @extends {BaseChannel} + * @abstract + */ +class GuildChannel extends BaseChannel { + constructor(guild, data, client, immediatePatch = true) { + super(client, data, false); + + /** + * The guild the channel is in + * @type {Guild} + */ + this.guild = guild; + + /** + * The id of the guild the channel is in + * @type {Snowflake} + */ + this.guildId = guild?.id ?? data.guild_id; + /** + * A manager of permission overwrites that belong to this channel + * @type {PermissionOverwriteManager} + */ + this.permissionOverwrites = new PermissionOverwriteManager(this); + + if (data && immediatePatch) this._patch(data); + } + + _patch(data) { + super._patch(data); + + if ('name' in data) { + /** + * The name of the guild channel + * @type {string} + */ + this.name = data.name; + } + + if ('position' in data) { + /** + * The raw position of the channel from Discord + * @type {number} + */ + this.rawPosition = data.position; + } + + if ('guild_id' in data) { + this.guildId = data.guild_id; + } + + if ('parent_id' in data) { + /** + * The id of the category parent of this channel + * @type {?Snowflake} + */ + this.parentId = data.parent_id; + } else { + this.parentId ??= null; + } + + if ('permission_overwrites' in data) { + this.permissionOverwrites.cache.clear(); + for (const overwrite of data.permission_overwrites) { + this.permissionOverwrites._add(overwrite); + } + } + } + + _clone() { + const clone = super._clone(); + clone.permissionOverwrites = new PermissionOverwriteManager(clone, this.permissionOverwrites.cache.values()); + return clone; + } + + /** + * The category parent of this channel + * @type {?CategoryChannel} + * @readonly + */ + get parent() { + return this.guild.channels.resolve(this.parentId); + } + + /** + * If the permissionOverwrites match the parent channel, null if no parent + * @type {?boolean} + * @readonly + */ + get permissionsLocked() { + if (!this.parent) return null; + + // Get all overwrites + const overwriteIds = new Set([ + ...this.permissionOverwrites.cache.keys(), + ...this.parent.permissionOverwrites.cache.keys(), + ]); + + // Compare all overwrites + return [...overwriteIds].every(key => { + const channelVal = this.permissionOverwrites.cache.get(key); + const parentVal = this.parent.permissionOverwrites.cache.get(key); + + // Handle empty overwrite + if ( + (!channelVal && + parentVal.deny.bitfield === PermissionsBitField.DefaultBit && + parentVal.allow.bitfield === PermissionsBitField.DefaultBit) || + (!parentVal && + channelVal.deny.bitfield === PermissionsBitField.DefaultBit && + channelVal.allow.bitfield === PermissionsBitField.DefaultBit) + ) { + return true; + } + + // Compare overwrites + return ( + channelVal !== undefined && + parentVal !== undefined && + channelVal.deny.bitfield === parentVal.deny.bitfield && + channelVal.allow.bitfield === parentVal.allow.bitfield + ); + }); + } + + /** + * The position of the channel + * @type {number} + * @readonly + */ + get position() { + const selfIsCategory = this.type === ChannelType.GuildCategory; + const types = getSortableGroupTypes(this.type); + + let count = 0; + for (const channel of this.guild.channels.cache.values()) { + if (!types.includes(channel.type)) continue; + if (!selfIsCategory && channel.parentId !== this.parentId) continue; + if (this.rawPosition === channel.rawPosition) { + if (Snowflake.compare(channel.id, this.id) === -1) count++; + } else if (this.rawPosition > channel.rawPosition) { + count++; + } + } + + return count; + } + + /** + * Gets the overall set of permissions for a member or role in this channel, taking into account channel overwrites. + * @param {GuildMemberResolvable|RoleResolvable} memberOrRole The member or role to obtain the overall permissions for + * @param {boolean} [checkAdmin=true] Whether having the {@link PermissionFlagsBits.Administrator} permission + * will return all permissions + * @returns {?Readonly<PermissionsBitField>} + */ + permissionsFor(memberOrRole, checkAdmin = true) { + const member = this.guild.members.resolve(memberOrRole); + if (member) return this.memberPermissions(member, checkAdmin); + const role = this.guild.roles.resolve(memberOrRole); + return role && this.rolePermissions(role, checkAdmin); + } + + overwritesFor(member, verified = false, roles = null) { + if (!verified) member = this.guild.members.resolve(member); + if (!member) return []; + + roles ??= member.roles.cache; + const roleOverwrites = []; + let memberOverwrites; + let everyoneOverwrites; + + for (const overwrite of this.permissionOverwrites.cache.values()) { + if (overwrite.id === this.guild.id) { + everyoneOverwrites = overwrite; + } else if (roles.has(overwrite.id)) { + roleOverwrites.push(overwrite); + } else if (overwrite.id === member.id) { + memberOverwrites = overwrite; + } + } + + return { + everyone: everyoneOverwrites, + roles: roleOverwrites, + member: memberOverwrites, + }; + } + + /** + * Gets the overall set of permissions for a member in this channel, taking into account channel overwrites. + * @param {GuildMember} member The member to obtain the overall permissions for + * @param {boolean} checkAdmin Whether having the {@link PermissionFlagsBits.Administrator} permission + * will return all permissions + * @returns {Readonly<PermissionsBitField>} + * @private + */ + memberPermissions(member, checkAdmin) { + if (checkAdmin && member.id === this.guild.ownerId) { + return new PermissionsBitField(PermissionsBitField.All).freeze(); + } + + const roles = member.roles.cache; + const permissions = new PermissionsBitField(roles.map(role => role.permissions)); + + if (checkAdmin && permissions.has(PermissionFlagsBits.Administrator)) { + return new PermissionsBitField(PermissionsBitField.All).freeze(); + } + + const overwrites = this.overwritesFor(member, true, roles); + + return permissions + .remove(overwrites.everyone?.deny ?? PermissionsBitField.DefaultBit) + .add(overwrites.everyone?.allow ?? PermissionsBitField.DefaultBit) + .remove(overwrites.roles.length > 0 ? overwrites.roles.map(role => role.deny) : PermissionsBitField.DefaultBit) + .add(overwrites.roles.length > 0 ? overwrites.roles.map(role => role.allow) : PermissionsBitField.DefaultBit) + .remove(overwrites.member?.deny ?? PermissionsBitField.DefaultBit) + .add(overwrites.member?.allow ?? PermissionsBitField.DefaultBit) + .freeze(); + } + + /** + * Gets the overall set of permissions for a role in this channel, taking into account channel overwrites. + * @param {Role} role The role to obtain the overall permissions for + * @param {boolean} checkAdmin Whether having the {@link PermissionFlagsBits.Administrator} permission + * will return all permissions + * @returns {Readonly<PermissionsBitField>} + * @private + */ + rolePermissions(role, checkAdmin) { + if (checkAdmin && role.permissions.has(PermissionFlagsBits.Administrator)) { + return new PermissionsBitField(PermissionsBitField.All).freeze(); + } + + const everyoneOverwrites = this.permissionOverwrites.cache.get(this.guild.id); + const roleOverwrites = this.permissionOverwrites.cache.get(role.id); + + return role.permissions + .remove(everyoneOverwrites?.deny ?? PermissionsBitField.DefaultBit) + .add(everyoneOverwrites?.allow ?? PermissionsBitField.DefaultBit) + .remove(roleOverwrites?.deny ?? PermissionsBitField.DefaultBit) + .add(roleOverwrites?.allow ?? PermissionsBitField.DefaultBit) + .freeze(); + } + + /** + * Locks in the permission overwrites from the parent channel. + * @returns {Promise<GuildChannel>} + */ + lockPermissions() { + if (!this.parent) return Promise.reject(new DiscordjsError(ErrorCodes.GuildChannelOrphan)); + const permissionOverwrites = this.parent.permissionOverwrites.cache.map(overwrite => overwrite.toJSON()); + return this.edit({ permissionOverwrites }); + } + + /** + * A collection of cached members of this channel, mapped by their ids. + * Members that can view this channel, if the channel is text-based. + * Members in the channel, if the channel is voice-based. + * @type {Collection<Snowflake, GuildMember>} + * @readonly + */ + get members() { + return this.guild.members.cache.filter(m => this.permissionsFor(m).has(PermissionFlagsBits.ViewChannel, false)); + } + + /** + * Edits the channel. + * @param {GuildChannelEditOptions} options The options to provide + * @returns {Promise<GuildChannel>} + * @example + * // Edit a channel + * channel.edit({ name: 'new-channel' }) + * .then(console.log) + * .catch(console.error); + */ + edit(options) { + return this.guild.channels.edit(this, options); + } + + /** + * Sets a new name for the guild channel. + * @param {string} name The new name for the guild channel + * @param {string} [reason] Reason for changing the guild channel's name + * @returns {Promise<GuildChannel>} + * @example + * // Set a new channel name + * channel.setName('not_general') + * .then(newChannel => console.log(`Channel's new name is ${newChannel.name}`)) + * .catch(console.error); + */ + setName(name, reason) { + return this.edit({ name, reason }); + } + + /** + * Options used to set the parent of a channel. + * @typedef {Object} SetParentOptions + * @property {boolean} [lockPermissions=true] Whether to lock the permissions to what the parent's permissions are + * @property {string} [reason] The reason for modifying the parent of the channel + */ + + /** + * Sets the parent of this channel. + * @param {?CategoryChannelResolvable} channel The category channel to set as the parent + * @param {SetParentOptions} [options={}] The options for setting the parent + * @returns {Promise<GuildChannel>} + * @example + * // Add a parent to a channel + * message.channel.setParent('355908108431917066', { lockPermissions: false }) + * .then(channel => console.log(`New parent of ${message.channel.name}: ${channel.name}`)) + * .catch(console.error); + */ + setParent(channel, { lockPermissions = true, reason } = {}) { + return this.edit({ + parent: channel ?? null, + lockPermissions, + reason, + }); + } + + /** + * Options used to set the position of a channel. + * @typedef {Object} SetChannelPositionOptions + * @property {boolean} [relative=false] Whether or not to change the position relative to its current value + * @property {string} [reason] The reason for changing the position + */ + + /** + * Sets a new position for the guild channel. + * @param {number} position The new position for the guild channel + * @param {SetChannelPositionOptions} [options] Options for setting position + * @returns {Promise<GuildChannel>} + * @example + * // Set a new channel position + * channel.setPosition(2) + * .then(newChannel => console.log(`Channel's new position is ${newChannel.position}`)) + * .catch(console.error); + */ + setPosition(position, options = {}) { + return this.guild.channels.setPosition(this, position, options); + } + + /** + * Options used to clone a guild channel. + * @typedef {GuildChannelCreateOptions} GuildChannelCloneOptions + * @property {string} [name=this.name] Name of the new channel + */ + + /** + * Clones this channel. + * @param {GuildChannelCloneOptions} [options] The options for cloning this channel + * @returns {Promise<GuildChannel>} + */ + clone(options = {}) { + return this.guild.channels.create({ + name: options.name ?? this.name, + permissionOverwrites: this.permissionOverwrites.cache, + topic: this.topic, + type: this.type, + nsfw: this.nsfw, + parent: this.parent, + bitrate: this.bitrate, + userLimit: this.userLimit, + rateLimitPerUser: this.rateLimitPerUser, + position: this.rawPosition, + reason: null, + ...options, + }); + } + + /** + * 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 Channel to compare with + * @returns {boolean} + */ + equals(channel) { + let equal = + channel && + this.id === channel.id && + this.type === channel.type && + this.topic === channel.topic && + this.position === channel.position && + this.name === channel.name; + + if (equal) { + if (this.permissionOverwrites && channel.permissionOverwrites) { + equal = this.permissionOverwrites.cache.equals(channel.permissionOverwrites.cache); + } else { + equal = !this.permissionOverwrites && !channel.permissionOverwrites; + } + } + + return equal; + } + + /** + * Whether the channel is deletable by the client user + * @type {boolean} + * @readonly + */ + get deletable() { + return this.manageable && this.guild.rulesChannelId !== this.id && this.guild.publicUpdatesChannelId !== this.id; + } + + /** + * Whether the channel is manageable by the client user + * @type {boolean} + * @readonly + */ + get manageable() { + if (this.client.user.id === this.guild.ownerId) return true; + const permissions = this.permissionsFor(this.client.user); + if (!permissions) return false; + + // This flag allows managing even if timed out + if (permissions.has(PermissionFlagsBits.Administrator, false)) return true; + if (this.guild.members.me.communicationDisabledUntilTimestamp > Date.now()) return false; + + const bitfield = VoiceBasedChannelTypes.includes(this.type) + ? PermissionFlagsBits.ManageChannels | PermissionFlagsBits.Connect + : PermissionFlagsBits.ViewChannel | PermissionFlagsBits.ManageChannels; + return permissions.has(bitfield, false); + } + + /** + * Whether the channel is viewable by the client user + * @type {boolean} + * @readonly + */ + get viewable() { + if (this.client.user.id === this.guild.ownerId) return true; + const permissions = this.permissionsFor(this.client.user); + if (!permissions) return false; + return permissions.has(PermissionFlagsBits.ViewChannel, false); + } + + /** + * Deletes this channel. + * @param {string} [reason] Reason for deleting this channel + * @returns {Promise<GuildChannel>} + * @example + * // Delete the channel + * channel.delete('making room for new channels') + * .then(console.log) + * .catch(console.error); + */ + async delete(reason) { + await this.guild.channels.delete(this.id, reason); + return this; + } +} + +module.exports = GuildChannel; |