From 44fb360a835bd5fdcdb1d69affe3ca2f570bc49d Mon Sep 17 00:00:00 2001 From: makerze <99765898+makerze@users.noreply.github.com> Date: Fri, 20 Jun 2025 20:25:54 +0100 Subject: [PATCH] Add new role parameters for creation of gradient and holographic roles --- discord/guild.py | 50 ++++++++++++++++++++++++++++++++++++++++++ discord/http.py | 2 +- discord/role.py | 57 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 108 insertions(+), 1 deletion(-) diff --git a/discord/guild.py b/discord/guild.py index 20a50d4e9..8badf1239 100644 --- a/discord/guild.py +++ b/discord/guild.py @@ -3614,6 +3614,9 @@ class Guild(Hashable): hoist: bool = ..., display_icon: Union[bytes, str] = MISSING, mentionable: bool = ..., + primary_color: Union[Colour, int, None] = ..., + secondary_color: Union[Colour, int, None] = ..., + tertiary_color: Union[Colour, int, None] = ..., ) -> Role: ... @@ -3628,6 +3631,9 @@ class Guild(Hashable): hoist: bool = ..., display_icon: Union[bytes, str] = MISSING, mentionable: bool = ..., + primary_color: Union[Colour, int, None] = ..., + secondary_color: Union[Colour, int, None] = ..., + tertiary_color: Union[Colour, int, None] = ..., ) -> Role: ... @@ -3642,6 +3648,9 @@ class Guild(Hashable): display_icon: Union[bytes, str] = MISSING, mentionable: bool = MISSING, reason: Optional[str] = None, + primary_color: Union[Colour, int, None] = MISSING, + secondary_color: Union[Colour, int, None] = MISSING, + tertiary_color: Union[Colour, int, None] = MISSING, ) -> Role: """|coro| @@ -3670,6 +3679,14 @@ class Guild(Hashable): colour: Union[:class:`Colour`, :class:`int`] The colour for the role. Defaults to :meth:`Colour.default`. This is aliased to ``color`` as well. + primary_color: Union[:class:`Colour`, :class:`int`, None] + The primary color for the role. If provided, must be an integer or :class:`Colour`. + secondary_color: Union[:class:`Colour`, :class:`int`, None] + The secondary color for the role. Requires ``primary_color`` to also be set. + tertiary_color: Union[:class:`Colour`, :class:`int`, None] + The tertiary_color color for the role. Used for holographic role. + The holographic preset is: + {"primary_color": 11127295, "secondary_color": 16759788, "tertiary_color": 16761760} hoist: :class:`bool` Indicates if the role should be shown separately in the member list. Defaults to ``False``. @@ -3710,6 +3727,39 @@ class Guild(Hashable): else: fields['color'] = actual_colour.value + colors_payload: Dict[str, Any]= {} + if primary_color is not MISSING: + if primary_color is None: + colors_payload['primary_color'] = None + elif isinstance(primary_color, int): + colors_payload['primary_color'] = primary_color + else: + colors_payload['primary_color'] = primary_color.value + if secondary_color is not MISSING: + if secondary_color is None: + colors_payload['secondary_color'] = None + elif isinstance(secondary_color, int): + colors_payload['secondary_color'] = secondary_color + else: + colors_payload['secondary_color'] = secondary_color.value + if tertiary_color is not MISSING: + if tertiary_color is None: + colors_payload['tertiary_color'] = None + elif isinstance(tertiary_color, int): + colors_payload['tertiary_color'] = tertiary_color + else: + colors_payload['tertiary_color'] = tertiary_color.value + + if colors_payload: + fields['colors'] = colors_payload + + if not colors_payload: + actual_colour = colour or color or Colour.default() + if isinstance(actual_colour, int): + fields['color'] = actual_colour + else: + fields['color'] = actual_colour.value + if hoist is not MISSING: fields['hoist'] = hoist diff --git a/discord/http.py b/discord/http.py index 6617efa27..187126ed9 100644 --- a/discord/http.py +++ b/discord/http.py @@ -1889,7 +1889,7 @@ class HTTPClient: self, guild_id: Snowflake, role_id: Snowflake, *, reason: Optional[str] = None, **fields: Any ) -> Response[role.Role]: r = Route('PATCH', '/guilds/{guild_id}/roles/{role_id}', guild_id=guild_id, role_id=role_id) - valid_keys = ('name', 'permissions', 'color', 'hoist', 'icon', 'unicode_emoji', 'mentionable') + valid_keys = ('name', 'permissions', 'color', 'hoist', 'icon', 'unicode_emoji', 'mentionable', 'colors') payload = {k: v for k, v in fields.items() if k in valid_keys} return self.request(r, json=payload, reason=reason) diff --git a/discord/role.py b/discord/role.py index d7fe1e08b..076b0b9f7 100644 --- a/discord/role.py +++ b/discord/role.py @@ -222,6 +222,9 @@ class Role(Hashable): 'tags', '_flags', '_state', + '_primary_color', + '_secondary_color', + '_tertiary_color', ) def __init__(self, *, guild: Guild, state: ConnectionState, data: RolePayload): @@ -284,6 +287,10 @@ class Role(Hashable): self.mentionable: bool = data.get('mentionable', False) self.tags: Optional[RoleTags] self._flags: int = data.get('flags', 0) + colors = data.get('colors', {}) + self._primary_color = colors.get('primary_color', None) + self._secondary_color = colors.get('secondary_color', None) + self._tertiary_color = colors.get('tertiary_color', None) try: self.tags = RoleTags(data['tags']) # pyright: ignore[reportTypedDictNotRequiredAccess] @@ -324,6 +331,20 @@ class Role(Hashable): return not self.is_default() and not self.managed and (me.top_role > self or me.id == self.guild.owner_id) @property + def primary_color(self) -> Optional[Colour]: + """Optional[:class:`Colour`]: The role's primary color.""" + return Colour(self._primary_color) if self._primary_color is not None else None + + @property + def secondary_color(self) -> Optional[Colour]: + """Optional[:class:`Colour`]: The role's secondary color.""" + return Colour(self._secondary_color) if self._secondary_color is not None else None + + @property + def tertiary_color(self) -> Optional[Colour]: + """Optional[:class:`Colour`]: The role's tertiary color.""" + return Colour(self._tertiary_color) if self._tertiary_color is not None else None + @property def permissions(self) -> Permissions: """:class:`Permissions`: Returns the role's permissions.""" return Permissions(self._permissions) @@ -425,6 +446,9 @@ class Role(Hashable): mentionable: bool = MISSING, position: int = MISSING, reason: Optional[str] = MISSING, + primary_color: Union[Colour, int, None] = MISSING, + secondary_color: Union[Colour, int, None] = MISSING, + tertiary_color: Union[Colour, int, None] = MISSING, ) -> Optional[Role]: """|coro| @@ -470,6 +494,14 @@ class Role(Hashable): position or it will fail. reason: Optional[:class:`str`] The reason for editing this role. Shows up on the audit log. + primary_color: Union[:class:`Colour`, :class:`int`, None] + The primary color for the role. If provided, must be an integer or :class:`Colour`. + secondary_color: Union[:class:`Colour`, :class:`int`, None] + The secondary color for the role. + tertiary_color: Union[:class:`Colour`, :class:`int`, None] + The tertiary_color color for the role. Used for holographic role. + The holographic preset is: + {"primary_color": 11127295, "secondary_color": 16759788, "tertiary_color": 16761760} Raises ------- @@ -519,6 +551,31 @@ class Role(Hashable): if mentionable is not MISSING: payload['mentionable'] = mentionable + colors_payload: Dict[str, Any] = {} + if primary_color is not MISSING: + if primary_color is None: + colors_payload['primary_color'] = None + elif isinstance(primary_color, int): + colors_payload['primary_color'] = primary_color + else: + colors_payload['primary_color'] = primary_color.value + if secondary_color is not MISSING: + if secondary_color is None: + colors_payload['secondary_color'] = None + elif isinstance(secondary_color, int): + colors_payload['secondary_color'] = secondary_color + else: + colors_payload['secondary_color'] = secondary_color.value + if tertiary_color is not MISSING: + if tertiary_color is None: + colors_payload['tertiary_color'] = None + elif isinstance(tertiary_color, int): + colors_payload['tertiary_color'] = tertiary_color + else: + colors_payload['tertiary_color'] = tertiary_color.value + if colors_payload: + payload['colors'] = colors_payload + data = await self._state.http.edit_role(self.guild.id, self.id, reason=reason, **payload) return Role(guild=self.guild, data=data, state=self._state)