'use strict'; const process = require('node:process'); const { Collection } = require('@discordjs/collection'); const CachedManager = require('./CachedManager'); const { TypeError } = require('../errors'); const PermissionOverwrites = require('../structures/PermissionOverwrites'); const { Role } = require('../structures/Role'); const { OverwriteTypes } = require('../util/Constants'); let cacheWarningEmitted = false; /** * Manages API methods for guild channel permission overwrites and stores their cache. * @extends {CachedManager} */ class PermissionOverwriteManager extends CachedManager { constructor(channel, iterable) { super(channel.client, PermissionOverwrites); if (!cacheWarningEmitted && this._cache.constructor.name !== 'Collection') { cacheWarningEmitted = true; process.emitWarning( `Overriding the cache handling for ${this.constructor.name} is unsupported and breaks functionality.`, 'UnsupportedCacheOverwriteWarning', ); } /** * The channel of the permission overwrite this manager belongs to * @type {GuildChannel} */ this.channel = channel; if (iterable) { for (const item of iterable) { this._add(item); } } } /** * The cache of this Manager * @type {Collection} * @name PermissionOverwriteManager#cache */ _add(data, cache) { return super._add(data, cache, { extras: [this.channel] }); } /** * Replaces the permission overwrites in this channel. * @param {OverwriteResolvable[]|Collection} overwrites * Permission overwrites the channel gets updated with * @param {string} [reason] Reason for updating the channel overwrites * @returns {Promise} * @example * message.channel.permissionOverwrites.set([ * { * id: message.author.id, * deny: [Permissions.FLAGS.VIEW_CHANNEL], * }, * ], 'Needed to change permissions'); */ set(overwrites, reason) { if (!Array.isArray(overwrites) && !(overwrites instanceof Collection)) { return Promise.reject( new TypeError('INVALID_TYPE', 'overwrites', 'Array or Collection of Permission Overwrites', true), ); } return this.channel.edit({ permissionOverwrites: overwrites, reason }); } /** * Extra information about the overwrite * @typedef {Object} GuildChannelOverwriteOptions * @property {string} [reason] Reason for creating/editing this overwrite * @property {number} [type] The type of overwrite, either `0` for a role or `1` for a member. Use this to bypass * automatic resolution of type that results in an error for uncached structure */ /** * Creates or edits permission overwrites for a user or role in this channel. * @param {RoleResolvable|UserResolvable} userOrRole The user or role to update * @param {PermissionOverwriteOptions} options The options for the update * @param {GuildChannelOverwriteOptions} [overwriteOptions] The extra information for the update * @param {PermissionOverwrites} [existing] The existing overwrites to merge with this update * @returns {Promise} * @private */ async upsert(userOrRole, options, overwriteOptions = {}, existing) { const userOrRoleId = this.channel.guild.roles.resolveId(userOrRole) ?? this.client.users.resolveId(userOrRole); let { type, reason } = overwriteOptions; if (typeof type !== 'number') { userOrRole = this.channel.guild.roles.resolve(userOrRole) ?? this.client.users.resolve(userOrRole); if (!userOrRole) throw new TypeError('INVALID_TYPE', 'parameter', 'User nor a Role'); type = userOrRole instanceof Role ? OverwriteTypes.role : OverwriteTypes.member; } const { allow, deny } = PermissionOverwrites.resolveOverwriteOptions(options, existing); await this.client.api .channels(this.channel.id) .permissions(userOrRoleId) .put({ data: { id: userOrRoleId, type, allow, deny }, reason, }); return this.channel; } /** * Creates permission overwrites for a user or role in this channel, or replaces them if already present. * @param {RoleResolvable|UserResolvable} userOrRole The user or role to update * @param {PermissionOverwriteOptions} options The options for the update * @param {GuildChannelOverwriteOptions} [overwriteOptions] The extra information for the update * @returns {Promise} * @example * // Create or Replace permission overwrites for a message author * message.channel.permissionOverwrites.create(message.author, { * SEND_MESSAGES: false * }) * .then(channel => console.log(channel.permissionOverwrites.cache.get(message.author.id))) * .catch(console.error); */ create(userOrRole, options, overwriteOptions) { return this.upsert(userOrRole, options, overwriteOptions); } /** * Edits permission overwrites for a user or role in this channel, or creates an entry if not already present. * @param {RoleResolvable|UserResolvable} userOrRole The user or role to update * @param {PermissionOverwriteOptions} options The options for the update * @param {GuildChannelOverwriteOptions} [overwriteOptions] The extra information for the update * @returns {Promise} * @example * // Edit or Create permission overwrites for a message author * message.channel.permissionOverwrites.edit(message.author, { * SEND_MESSAGES: false * }) * .then(channel => console.log(channel.permissionOverwrites.cache.get(message.author.id))) * .catch(console.error); */ edit(userOrRole, options, overwriteOptions) { const existing = this.cache.get( this.channel.guild.roles.resolveId(userOrRole) ?? this.client.users.resolveId(userOrRole), ); return this.upsert(userOrRole, options, overwriteOptions, existing); } /** * Deletes permission overwrites for a user or role in this channel. * @param {UserResolvable|RoleResolvable} userOrRole The user or role to delete * @param {string} [reason] The reason for deleting the overwrite * @returns {Promise} */ async delete(userOrRole, reason) { const userOrRoleId = this.channel.guild.roles.resolveId(userOrRole) ?? this.client.users.resolveId(userOrRole); if (!userOrRoleId) throw new TypeError('INVALID_TYPE', 'parameter', 'User nor a Role'); await this.client.api.channels(this.channel.id).permissions(userOrRoleId).delete({ reason }); return this.channel; } } module.exports = PermissionOverwriteManager;