From 8edf4332557c3b980cf4eabc4bed4cbab9a01934 Mon Sep 17 00:00:00 2001 From: Leonardo Date: Wed, 12 Feb 2025 11:49:50 +0100 Subject: [PATCH] Implement rich role.move interface Co-authored-by: Danny <1695103+Rapptz@users.noreply.github.com> --- discord/role.py | 108 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 107 insertions(+), 1 deletion(-) diff --git a/discord/role.py b/discord/role.py index 8530d4a90a46..571ab9f20bbf 100644 --- a/discord/role.py +++ b/discord/role.py @@ -23,7 +23,7 @@ """ from __future__ import annotations -from typing import Any, Dict, List, Optional, Union, TYPE_CHECKING +from typing import Any, Dict, List, Optional, Union, overload, TYPE_CHECKING from .asset import Asset from .permissions import Permissions @@ -522,6 +522,112 @@ async def edit( 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) + @overload + async def move(self, *, beginning: bool, offset: int = ..., reason: Optional[str] = ...): + ... + + @overload + async def move(self, *, end: bool, offset: int = ..., reason: Optional[str] = ...): + ... + + @overload + async def move(self, *, above: Role, offset: int = ..., reason: Optional[str] = ...): + ... + + @overload + async def move(self, *, below: Role, offset: int = ..., reason: Optional[str] = ...): + ... + + async def move( + self, + *, + beginning: bool = MISSING, + end: bool = MISSING, + above: Role = MISSING, + below: Role = MISSING, + offset: int = 0, + reason: Optional[str] = None, + ): + """|coro| + + A rich interface to help move a role relative to other roles. + + You must have :attr:`~discord.Permissions.manage_roles` to do this, + and you cannot move roles above the client's top role in the guild. + + .. versionadded:: 2.5 + + Parameters + ----------- + beginning: :class:`bool` + Whether to move this at the beginning of the role list, above the default role. + This is mutually exclusive with `end`, `above`, and `below`. + end: :class:`bool` + Whether to move this at the end of the role list. + This is mutually exclusive with `beginning`, `above`, and `below`. + above: :class:`Role` + The role that should be above our current role. + This mutually exclusive with `beginning`, `end`, and `below`. + below: :class:`Role` + The role that should be below our current role. + This mutually exclusive with `beginning`, `end`, and `above`. + offset: :class:`int` + The number of roles to offset the move by. For example, + an offset of ``2`` with ``beginning=True`` would move + it 2 above the beginning. A positive number moves it above + while a negative number moves it below. Note that this + number is relative and computed after the ``beginning``, + ``end``, ``before``, and ``after`` parameters. + reason: Optional[:class:`str`] + The reason for editing this role. Shows up on the audit log. + + Raises + ------- + Forbidden + You cannot move the role there, or lack permissions to do so. + HTTPException + Moving the role failed. + TypeError + A bad mix of arguments were passed. + ValueError + An invalid role was passed. + + Returns + -------- + List[:class:`Role`] + A list of all the roles in the guild. + """ + if sum(bool(a) for a in (beginning, end, above, below)) > 1: + raise TypeError('Only one of [beginning, end, above, below] can be used.') + + target = above or below + guild = self.guild + guild_roles = guild.roles + + if target: + if target not in guild_roles: + raise ValueError('Target role is from a different guild') + if above == guild.default_role: + raise ValueError('Role cannot be moved below the default role') + if self == target: + raise ValueError('Target role cannot be itself') + + roles = [r for r in guild_roles if r != self] + if beginning: + index = 1 + elif end: + index = len(roles) + elif above in roles: + index = roles.index(above) + elif below in roles: + index = roles.index(below) + 1 + else: + index = guild_roles.index(self) + roles.insert(max((index + offset), 1), self) + + payload: List[RolePositionUpdate] = [{'id': role.id, 'position': idx} for idx, role in enumerate(roles)] + await self._state.http.move_role_position(guild.id, payload, reason=reason) + async def delete(self, *, reason: Optional[str] = None) -> None: """|coro|