import Client from "../Client.js";
import { CDN_BASE_URL, TO_JSON_TYPES_ENUM } from "../constants.js";
import GluonCacheOptions from "../managers/GluonCacheOptions.js";
import GuildCacheOptions from "../managers/GuildCacheOptions.js";
import util from "util";
/**
* Represents a role belonging to a guild.
*/
class Role {
#_client;
#_guild_id;
#_id;
#name;
#color;
#position;
#_icon;
#permissions;
#_attributes;
#tags;
/**
* Creates the structure for a role.
* @param {Client} client The client instance.
* @param {Object} data The raw role data from Discord.
* @param {Object} options Additional options for this structure.
* @param {String} options.guildId The id of the guild that the role belongs to.
* @param {Boolean?} options.nocache Whether this role should be cached or not.
* @see {@link https://discord.com/developers/docs/topics/permissions#role-object-role-structure}
*/
constructor(client, data, { guildId, nocache = false } = { nocache: false }) {
if (!(client instanceof Client))
throw new TypeError("GLUON: Client must be an instance of Client");
if (typeof data !== "object")
throw new TypeError("GLUON: Data must be an object");
if (typeof guildId !== "string")
throw new TypeError("GLUON: Guild ID must be a string");
if (typeof nocache !== "boolean")
throw new TypeError("GLUON: No cache must be a boolean");
/**
* The client instance.
* @type {Client}
* @private
*/
this.#_client = client;
/**
* The id of the guild that this role belongs to.
* @type {BigInt}
* @private
*/
this.#_guild_id = BigInt(guildId);
/**
* The id of the role.
* @type {BigInt}
* @private
*/
this.#_id = BigInt(data.id);
/**
* The name of the role.
* @type {String}
* @private
*/
this.#name = data.name;
/**
* The color of the role.
* @type {Number}
* @private
*/
this.#color = data.color;
/**
* The position of the role.
* @type {Number}
* @private
*/
this.#position = data.position;
/**
* The role icon hash.
* @type {String?}
* @private
*/
if (data.icon) this.#_icon = BigInt(`0x${data.icon}`);
/**
* The permissions for the role.
* @type {BigInt}
* @private
*/
this.#permissions = BigInt(data.permissions);
/**
* The attributes of the role.
* @type {Number}
* @private
*/
this.#_attributes = data._attributes ?? 0;
if (data.hoist == true) this.#_attributes |= 0b1 << 0;
if (data.managed == true) this.#_attributes |= 0b1 << 1;
if (data.mentionable == true) this.#_attributes |= 0b1 << 2;
if (data.tags) this.#tags = data.tags;
if (
nocache === false &&
Role.shouldCache(this.#_client._cacheOptions, this.guild._cacheOptions)
)
this.guild.roles.set(data.id, this);
}
/**
* The ID of the role.
* @type {String}
* @readonly
* @public
*/
get id() {
return String(this.#_id);
}
/**
* Whether the role is hoisted.
* @readonly
* @returns {Boolean}
* @public
*/
get hoist() {
return (this.#_attributes & (0b1 << 0)) == 0b1 << 0;
}
/**
* Whether the role is managed (by an application).
* @readonly
* @returns {Boolean}
* @public
*/
get managed() {
return (this.#_attributes & (0b1 << 1)) == 0b1 << 1;
}
/**
* Whether the role is mentionable.
* @readonly
* @returns {Boolean}
* @public
*/
get mentionable() {
return (this.#_attributes & (0b1 << 2)) == 0b1 << 2;
}
/**
* The hash of the role's avatar, as it was received from Discord.
* @readonly
* @type {String?}
* @private
*/
get #_originalIconHash() {
return this.#_icon
? // eslint-disable-next-line quotes
`${this.#_formattedIconHash}`
: null;
}
/**
* The hash of the role icon as a string.
* @readonly
* @type {String}
* @private
*/
get #_formattedIconHash() {
if (!this.#_icon) return null;
let formattedHash = this.#_icon.toString(16);
while (formattedHash.length != 32)
// eslint-disable-next-line quotes
formattedHash = `0${formattedHash}`;
return formattedHash;
}
/**
* The icon URL of the role.
* @readonly
* @type {String?}
* @public
*/
get displayIconURL() {
return Role.getIconUrl(this.id, this.#_originalIconHash);
}
/**
* The guild that this role belongs to.
* @type {Guild?}
* @readonly
* @public
*/
get guild() {
return this.#_client.guilds.get(this.guildId) || null;
}
/**
* The ID of the guild that this role belongs to.
* @type {String}
* @readonly
* @public
*/
get guildId() {
return String(this.#_guild_id);
}
/**
* The name of the role.
* @type {String}
* @readonly
* @public
*/
get name() {
return this.#name;
}
/**
* The color of the role.
* @type {Number}
* @readonly
* @public
*/
get color() {
return this.#color;
}
/**
* The position of the role.
* @type {Number}
* @readonly
* @public
*/
get position() {
return this.#position;
}
/**
* The permissions for the role.
* @type {String}
* @readonly
* @public
*/
get permissions() {
return String(this.#permissions);
}
/**
* The attributes of the role.
* @type {Object}
* @readonly
* @public
*/
get tags() {
return this.#tags;
}
/**
* Returns a mention for the role.
* @type {String}
* @readonly
* @public
*/
get mention() {
return Role.getMention(this.id);
}
/**
* Returns a mention for the role.
* @param {String} roleId The ID of the role to mention.
* @returns {String}
* @public
* @static
* @method
*/
static getMention(roleId) {
if (typeof roleId !== "string")
throw new TypeError("GLUON: Role ID must be a string.");
return `<@&${roleId}>`;
}
/**
* Returns the URL of the role's icon.
* @param {String} id The ID of the role.
* @param {String?} hash The hash of the role's icon.
* @returns {String}
*/
static getIconUrl(id, hash) {
if (typeof id !== "string")
throw new TypeError("GLUON: Role id must be a string.");
if (hash && typeof hash !== "string")
throw new TypeError("GLUON: Role icon hash must be a string.");
return hash
? `${CDN_BASE_URL}/role-icons/${id}/${hash}.${
hash.startsWith("a_") ? "gif" : "png"
}`
: null;
}
/**
* Determines whether the role should be cached.
* @param {GluonCacheOptions} gluonCacheOptions The cache options for the client.
* @param {GuildCacheOptions} guildCacheOptions The cache options for the guild.
* @returns {Boolean}
* @public
* @static
* @method
*/
static shouldCache(gluonCacheOptions, guildCacheOptions) {
if (!(gluonCacheOptions instanceof GluonCacheOptions))
throw new TypeError(
"GLUON: Gluon cache options must be a GluonCacheOptions.",
);
if (!(guildCacheOptions instanceof GuildCacheOptions))
throw new TypeError(
"GLUON: Guild cache options must be a GuildCacheOptions.",
);
if (gluonCacheOptions.cacheRoles === false) return false;
if (guildCacheOptions.roleCaching === false) return false;
return true;
}
/**
* @method
* @public
*/
toString() {
return `<Role: ${this.id}>`;
}
/**
* @method
* @public
*/
[util.inspect.custom]() {
return this.toString();
}
/**
* Returns the JSON representation of this structure.
* @param {Number} format The format to return the data in.
* @returns {Object}
* @public
* @method
*/
toJSON(format) {
switch (format) {
case TO_JSON_TYPES_ENUM.CACHE_FORMAT:
case TO_JSON_TYPES_ENUM.STORAGE_FORMAT: {
return {
id: this.id,
name: this.name,
color: this.color,
position: this.position,
permissions: this.permissions,
icon: this.#_originalIconHash,
_attributes: this.#_attributes,
tags: this.tags,
};
}
case TO_JSON_TYPES_ENUM.DISCORD_FORMAT:
default: {
return {
id: this.id,
name: this.name,
color: this.color,
position: this.position,
permissions: this.permissions,
icon: this.#_originalIconHash,
tags: this.tags,
hoist: this.hoist,
managed: this.managed,
mentionable: this.mentionable,
};
}
}
}
}
export default Role;