diff --git a/packages/discord.js/src/managers/GuildMemberRoleManager.js b/packages/discord.js/src/managers/GuildMemberRoleManager.js
index 7d19bf778..81c5a1fe7 100644
--- a/packages/discord.js/src/managers/GuildMemberRoleManager.js
+++ b/packages/discord.js/src/managers/GuildMemberRoleManager.js
@@ -65,7 +65,7 @@ class GuildMemberRoleManager extends DataManager {
* @readonly
*/
get color() {
- const coloredRoles = this.cache.filter(role => role.color);
+ const coloredRoles = this.cache.filter(role => role.colors.primaryColor);
if (!coloredRoles.size) return null;
return coloredRoles.reduce((prev, role) => (role.comparePositionTo(prev) > 0 ? role : prev));
}
diff --git a/packages/discord.js/src/managers/RoleManager.js b/packages/discord.js/src/managers/RoleManager.js
index a69018f54..f711d8ad9 100644
--- a/packages/discord.js/src/managers/RoleManager.js
+++ b/packages/discord.js/src/managers/RoleManager.js
@@ -12,6 +12,8 @@ const PermissionsBitField = require('../util/PermissionsBitField');
const { setPosition, resolveColor } = require('../util/Util');
let cacheWarningEmitted = false;
+let deprecationEmittedForCreate = false;
+let deprecationEmittedForEdit = false;
/**
* Manages API methods for roles and stores their cache.
@@ -112,11 +114,24 @@ class RoleManager extends CachedManager {
* @returns {?Snowflake}
*/
+ /**
+ * @typedef {Object} RoleColorsResolvable
+ * @property {ColorResolvable} primaryColor The primary color of the role
+ * @property {ColorResolvable} [secondaryColor] The secondary color of the role.
+ * This will make the role a gradient between the other provided colors
+ * @property {ColorResolvable} [tertiaryColor] The tertiary color of the role.
+ * When sending `tertiaryColor` the API enforces the role color to be a holographic style
+ * with values of `primaryColor = 11127295`, `secondaryColor = 16759788`, and `tertiaryColor = 16761760`.
+ * These values are available as a constant: `Constants.HolographicStyle`
+ */
+
/**
* Options used to create a new role.
* @typedef {Object} RoleCreateOptions
* @property {string} [name] The name of the new role
* @property {ColorResolvable} [color] The data to create the role with
+ * This property is deprecated. Use `colors` instead.
+ * @property {RoleColorsResolvable} [colors] The colors to create the role with
* @property {boolean} [hoist] Whether or not the new role should be hoisted
* @property {PermissionResolvable} [permissions] The permissions for the new role
* @property {number} [position] The position of the new role
@@ -142,15 +157,30 @@ class RoleManager extends CachedManager {
* // Create a new role with data and a reason
* guild.roles.create({
* name: 'Super Cool Blue People',
- * color: Colors.Blue,
* reason: 'we needed a role for Super Cool People',
+ * colors: {
+ * primaryColor: Colors.Blue,
+ * },
+ * })
+ * .then(console.log)
+ * .catch(console.error);
+ * @example
+ * // Create a role with holographic colors
+ * guild.roles.create({
+ * name: 'Holographic Role',
+ * reason: 'Creating a role with holographic effect',
+ * colors: {
+ * primaryColor: Constants.HolographicStyle.Primary,
+ * secondaryColor: Constants.HolographicStyle.Secondary,
+ * tertiaryColor: Constants.HolographicStyle.Tertiary,
+ * },
* })
* .then(console.log)
* .catch(console.error);
*/
async create(options = {}) {
- let { name, color, hoist, permissions, position, mentionable, reason, icon, unicodeEmoji } = options;
- color &&= resolveColor(color);
+ let { permissions, icon } = options;
+ const { name, color, hoist, position, mentionable, reason, unicodeEmoji } = options;
if (permissions !== undefined) permissions = new PermissionsBitField(permissions);
if (icon) {
const guildEmojiURL = this.guild.emojis.resolve(icon)?.imageURL();
@@ -158,10 +188,30 @@ class RoleManager extends CachedManager {
if (typeof icon !== 'string') icon = undefined;
}
+ let colors = options.colors && {
+ primary_color: resolveColor(options.colors.primaryColor),
+ secondary_color: options.colors.secondaryColor && resolveColor(options.colors.secondaryColor),
+ tertiary_color: options.colors.tertiaryColor && resolveColor(options.colors.tertiaryColor),
+ };
+
+ if (color !== undefined) {
+ if (!deprecationEmittedForCreate) {
+ process.emitWarning(`Passing "color" to RoleManager#create() is deprecated. Use "colors" instead.`);
+ }
+
+ deprecationEmittedForCreate = true;
+
+ colors = {
+ primary_color: resolveColor(color),
+ secondary_color: null,
+ tertiary_color: null,
+ };
+ }
+
const data = await this.client.rest.post(Routes.guildRoles(this.guild.id), {
body: {
name,
- color,
+ colors,
hoist,
permissions,
mentionable,
@@ -210,9 +260,29 @@ class RoleManager extends CachedManager {
if (typeof icon !== 'string') icon = undefined;
}
+ let colors = options.colors && {
+ primary_color: resolveColor(options.colors.primaryColor),
+ secondary_color: options.colors.secondaryColor && resolveColor(options.colors.secondaryColor),
+ tertiary_color: options.colors.tertiaryColor && resolveColor(options.colors.tertiaryColor),
+ };
+
+ if (options.color !== undefined) {
+ if (!deprecationEmittedForEdit) {
+ process.emitWarning(`Passing "color" to RoleManager#edit() is deprecated. Use "colors" instead.`);
+ }
+
+ deprecationEmittedForEdit = true;
+
+ colors = {
+ primary_color: resolveColor(options.color),
+ secondary_color: null,
+ tertiary_color: null,
+ };
+ }
+
const body = {
name: options.name,
- color: options.color === undefined ? undefined : resolveColor(options.color),
+ colors,
hoist: options.hoist,
permissions: options.permissions === undefined ? undefined : new PermissionsBitField(options.permissions),
mentionable: options.mentionable,
diff --git a/packages/discord.js/src/structures/Role.js b/packages/discord.js/src/structures/Role.js
index 06cbac60b..2bea358e0 100644
--- a/packages/discord.js/src/structures/Role.js
+++ b/packages/discord.js/src/structures/Role.js
@@ -54,11 +54,37 @@ class Role extends Base {
if ('color' in data) {
/**
* The base 10 color of the role
+ *
* @type {number}
+ * @deprecated Use {@link Role#colors} instead.
*/
this.color = data.color;
}
+ /**
+ * @typedef {Object} RoleColors
+ * @property {number} primaryColor The primary color of the role
+ * @property {?number} secondaryColor The secondary color of the role.
+ * This will make the role a gradient between the other provided colors
+ * @property {?number} tertiaryColor The tertiary color of the role.
+ * When sending `tertiaryColor` the API enforces the role color to be a holographic style
+ * with values of `primaryColor = 11127295`, `secondaryColor = 16759788`, and `tertiaryColor = 16761760`.
+ * These values are available as a constant: `Constants.HolographicStyle`
+ */
+
+ if ('colors' in data) {
+ /**
+ * The colors of the role
+ *
+ * @type {RoleColors}
+ */
+ this.colors = {
+ primaryColor: data.colors.primary_color,
+ secondaryColor: data.colors.secondary_color,
+ tertiaryColor: data.colors.tertiary_color,
+ };
+ }
+
if ('hoist' in data) {
/**
* If true, users that are part of this role will appear in a separate category in the users list
@@ -231,6 +257,8 @@ class Role extends Base {
* @typedef {Object} RoleData
* @property {string} [name] The name of the role
* @property {ColorResolvable} [color] The color of the role, either a hex string or a base 10 number
+ * This property is deprecated. Use `colors` instead.
+ * @property {RoleColorsResolvable} [colors] The colors of the role
* @property {boolean} [hoist] Whether or not the role should be hoisted
* @property {number} [position] The position of the role
* @property {PermissionResolvable} [permissions] The permissions of the role
@@ -286,17 +314,39 @@ class Role extends Base {
/**
* Sets a new color for the role.
+ *
* @param {ColorResolvable} color The color of the role
* @param {string} [reason] Reason for changing the role's color
* @returns {Promise}
+ * @deprecated Use {@link Role#setColors} instead.
+ */
+ async setColor(color, reason) {
+ return this.edit({ color, reason });
+ }
+
+ /**
+ * Sets new colors for the role.
+ *
+ * @param {RoleColorsResolvable} colors The colors of the role
+ * @param {string} [reason] Reason for changing the role's colors
+ * @returns {Promise}
* @example
- * // Set the color of a role
- * role.setColor('#FF0000')
- * .then(updated => console.log(`Set color of role to ${updated.color}`))
+ * // Set the colors of a role
+ * role.setColors({ primaryColor: '#FF0000', secondaryColor: '#00FF00', tertiaryColor: '#0000FF' })
+ * .then(updated => console.log(`Set colors of role to ${updated.colors}`))
+ * .catch(console.error);
+ * @example
+ * // Set holographic colors using constants
+ * role.setColors({
+ * primaryColor: Constants.HolographicStyle.Primary,
+ * secondaryColor: Constants.HolographicStyle.Secondary,
+ * tertiaryColor: Constants.HolographicStyle.Tertiary,
+ * })
+ * .then(updated => console.log(`Set holographic colors for role ${updated.name}`))
* .catch(console.error);
*/
- setColor(color, reason) {
- return this.edit({ color, reason });
+ async setColors(colors, reason) {
+ return this.edit({ colors, reason });
}
/**
@@ -434,7 +484,9 @@ class Role extends Base {
role &&
this.id === role.id &&
this.name === role.name &&
- this.color === role.color &&
+ this.colors.primaryColor === role.colors.primaryColor &&
+ this.colors.secondaryColor === role.colors.secondaryColor &&
+ this.colors.tertiaryColor === role.colors.tertiaryColor &&
this.hoist === role.hoist &&
this.position === role.position &&
this.permissions.bitfield === role.permissions.bitfield &&
diff --git a/packages/discord.js/src/util/Constants.js b/packages/discord.js/src/util/Constants.js
index 8babdfdbe..528d11f8b 100644
--- a/packages/discord.js/src/util/Constants.js
+++ b/packages/discord.js/src/util/Constants.js
@@ -254,6 +254,21 @@ exports.StickerFormatExtensionMap = {
[StickerFormatType.GIF]: ImageFormat.GIF,
};
+/**
+ * Holographic color values for role styling.
+ * When using `tertiaryColor`, the API enforces these specific values for holographic effect.
+ *
+ * @typedef {Object} HolographicStyle
+ * @property {number} Primary 11127295 (0xA9FFFF)
+ * @property {number} Secondary 16759788 (0xFFCCCC)
+ * @property {number} Tertiary 16761760 (0xFFE0A0)
+ */
+exports.HolographicStyle = {
+ Primary: 11_127_295,
+ Secondary: 16_759_788,
+ Tertiary: 16_761_760,
+};
+
/**
* @typedef {Object} Constants Constants that can be used in an enum or object-like way.
* @property {number} MaxBulkDeletableMessageAge Max bulk deletable message age
@@ -264,4 +279,5 @@ exports.StickerFormatExtensionMap = {
* @property {VoiceBasedChannelTypes} VoiceBasedChannelTypes The types of channels that are voice-based
* @property {SelectMenuTypes} SelectMenuTypes The types of components that are select menus.
* @property {Object} StickerFormatExtensionMap A mapping between sticker formats and their respective image formats.
+ * @property {HolographicStyle} HolographicStyle Holographic color values for role styling.
*/
diff --git a/packages/discord.js/typings/index.d.ts b/packages/discord.js/typings/index.d.ts
index 1b3254c8b..a306b4dbc 100644
--- a/packages/discord.js/typings/index.d.ts
+++ b/packages/discord.js/typings/index.d.ts
@@ -3080,9 +3080,23 @@ export class RichPresenceAssets {
public smallImageURL(options?: ImageURLOptions): string | null;
}
+export interface RoleColors {
+ primaryColor: number;
+ secondaryColor: number | null;
+ tertiaryColor: number | null;
+}
+
+export interface RoleColorsResolvable {
+ primaryColor: ColorResolvable;
+ secondaryColor?: ColorResolvable;
+ tertiaryColor?: ColorResolvable;
+}
+
export class Role extends Base {
private constructor(client: Client, data: RawRoleData, guild: Guild);
+ /** @deprecated Use {@link Role.colors} instead. */
public color: number;
+ public colors: RoleColors;
public get createdAt(): Date;
public get createdTimestamp(): number;
public get editable(): boolean;
@@ -3110,7 +3124,9 @@ export class Role extends Base {
channel: NonThreadGuildBasedChannel | Snowflake,
checkAdmin?: boolean,
): Readonly;
+ /** @deprecated Use {@link Role.setColors} instead. */
public setColor(color: ColorResolvable, reason?: string): Promise;
+ public setColors(colors: RoleColorsResolvable, reason?: string): Promise;
public setHoist(hoist?: boolean, reason?: string): Promise;
public setMentionable(mentionable?: boolean, reason?: string): Promise;
public setName(name: string, reason?: string): Promise;
@@ -4262,6 +4278,11 @@ export type DeletableMessageType =
| MessageType.UserJoin;
export const Constants: {
+ HolographicStyle: {
+ Primary: 11_127_295;
+ Secondary: 16_759_788;
+ Tertiary: 16_761_760;
+ };
MaxBulkDeletableMessageAge: 1_209_600_000;
SweeperKeys: SweeperKey[];
NonSystemMessageTypes: NonSystemMessageType[];
@@ -7372,7 +7393,9 @@ export interface ResolvedOverwriteOptions {
export interface RoleData {
name?: string;
+ /** @deprecated Use {@link RoleData.colors} instead. */
color?: ColorResolvable;
+ colors?: RoleColorsResolvable;
hoist?: boolean;
position?: number;
permissions?: PermissionResolvable;