'use strict'; const { Collection } = require('@discordjs/collection'); const DataManager = require('./DataManager'); const { TypeError } = require('../errors'); const { Role } = require('../structures/Role'); /** * Manages API methods for roles of a GuildMember and stores their cache. * @extends {DataManager} */ class GuildMemberRoleManager extends DataManager { constructor(member) { super(member.client, Role); /** * The GuildMember this manager belongs to * @type {GuildMember} */ this.member = member; /** * The Guild this manager belongs to * @type {Guild} */ this.guild = member.guild; } /** * The roles of this member * @type {Collection} * @readonly */ get cache() { const everyone = this.guild.roles.everyone; return this.guild.roles.cache.filter(role => this.member._roles.includes(role.id)).set(everyone.id, everyone); } /** * The role of the member used to hoist them in a separate category in the users list * @type {?Role} * @readonly */ get hoist() { const hoistedRoles = this.cache.filter(role => role.hoist); if (!hoistedRoles.size) return null; return hoistedRoles.reduce((prev, role) => (role.comparePositionTo(prev) > 0 ? role : prev)); } /** * The role of the member used to set their role icon * @type {?Role} * @readonly */ get icon() { const iconRoles = this.cache.filter(role => role.icon || role.unicodeEmoji); if (!iconRoles.size) return null; return iconRoles.reduce((prev, role) => (role.comparePositionTo(prev) > 0 ? role : prev)); } /** * The role of the member used to set their color * @type {?Role} * @readonly */ get color() { const coloredRoles = this.cache.filter(role => role.color); if (!coloredRoles.size) return null; return coloredRoles.reduce((prev, role) => (role.comparePositionTo(prev) > 0 ? role : prev)); } /** * The role of the member with the highest position * @type {Role} * @readonly */ get highest() { return this.cache.reduce((prev, role) => (role.comparePositionTo(prev) > 0 ? role : prev), this.cache.first()); } /** * The premium subscriber role of the guild, if present on the member * @type {?Role} * @readonly */ get premiumSubscriberRole() { return this.cache.find(role => role.tags?.premiumSubscriberRole) ?? null; } /** * The managed role this member created when joining the guild, if any * Only ever available on bots * @type {?Role} * @readonly */ get botRole() { if (!this.member.user.bot) return null; return this.cache.find(role => role.tags?.botId === this.member.user.id) ?? null; } /** * Adds a role (or multiple roles) to the member. * @param {RoleResolvable|RoleResolvable[]|Collection} roleOrRoles The role or roles to add * @param {string} [reason] Reason for adding the role(s) * @returns {Promise} */ async add(roleOrRoles, reason) { if (roleOrRoles instanceof Collection || Array.isArray(roleOrRoles)) { const resolvedRoles = []; for (const role of roleOrRoles.values()) { const resolvedRole = this.guild.roles.resolveId(role); if (!resolvedRole) throw new TypeError('INVALID_ELEMENT', 'Array or Collection', 'roles', role); resolvedRoles.push(resolvedRole); } const newRoles = [...new Set(resolvedRoles.concat(...this.cache.keys()))]; return this.set(newRoles, reason); } else { roleOrRoles = this.guild.roles.resolveId(roleOrRoles); if (roleOrRoles === null) { throw new TypeError('INVALID_TYPE', 'roles', 'Role, Snowflake or Array or Collection of Roles or Snowflakes'); } await this.client.api.guilds[this.guild.id].members[this.member.id].roles[roleOrRoles].put({ reason }); const clone = this.member._clone(); clone._roles = [...this.cache.keys(), roleOrRoles]; return clone; } } /** * Removes a role (or multiple roles) from the member. * @param {RoleResolvable|RoleResolvable[]|Collection} roleOrRoles The role or roles to remove * @param {string} [reason] Reason for removing the role(s) * @returns {Promise} */ async remove(roleOrRoles, reason) { if (roleOrRoles instanceof Collection || Array.isArray(roleOrRoles)) { const resolvedRoles = []; for (const role of roleOrRoles.values()) { const resolvedRole = this.guild.roles.resolveId(role); if (!resolvedRole) throw new TypeError('INVALID_ELEMENT', 'Array or Collection', 'roles', role); resolvedRoles.push(resolvedRole); } const newRoles = this.cache.filter(role => !resolvedRoles.includes(role.id)); return this.set(newRoles, reason); } else { roleOrRoles = this.guild.roles.resolveId(roleOrRoles); if (roleOrRoles === null) { throw new TypeError('INVALID_TYPE', 'roles', 'Role, Snowflake or Array or Collection of Roles or Snowflakes'); } await this.client.api.guilds[this.guild.id].members[this.member.id].roles[roleOrRoles].delete({ reason }); const clone = this.member._clone(); const newRoles = this.cache.filter(role => role.id !== roleOrRoles); clone._roles = [...newRoles.keys()]; return clone; } } /** * Sets the roles applied to the member. * @param {Collection|RoleResolvable[]} roles The roles or role ids to apply * @param {string} [reason] Reason for applying the roles * @returns {Promise} * @example * // Set the member's roles to a single role * guildMember.roles.set(['391156570408615936']) * .then(console.log) * .catch(console.error); * @example * // Remove all the roles from a member * guildMember.roles.set([]) * .then(member => console.log(`Member roles is now of ${member.roles.cache.size} size`)) * .catch(console.error); */ set(roles, reason) { return this.member.edit({ roles }, reason); } clone() { const clone = new this.constructor(this.member); clone.member._roles = [...this.cache.keys()]; return clone; } } module.exports = GuildMemberRoleManager;