diff --git a/pyvolt/http.py b/pyvolt/http.py index 7879e2b..c402410 100644 --- a/pyvolt/http.py +++ b/pyvolt/http.py @@ -6226,6 +6226,7 @@ async def edit_role( *, http_overrides: typing.Optional[HTTPOverrideOptions] = None, name: UndefinedOr[str] = UNDEFINED, + icon: UndefinedOr[typing.Optional[ResolvableResource]] = UNDEFINED, color: UndefinedOr[typing.Optional[str]] = UNDEFINED, hoist: UndefinedOr[bool] = UNDEFINED, rank: UndefinedOr[int] = UNDEFINED, @@ -6248,6 +6249,10 @@ async def edit_role( The HTTP request overrides. name: UndefinedOr[:class:`str`] The new role name. Must be between 1 and 32 characters long. + icon: UndefinedOr[Optional[:class:`.ResolvableResource`]] + The new role icon. + + .. versionadded:: 1.2 color: UndefinedOr[Optional[:class:`str`]] The new role color. Must be a valid CSS color. hoist: UndefinedOr[:class:`bool`] @@ -6285,11 +6290,11 @@ async def edit_role( :class:`NotFound` Possible values for :attr:`~HTTPException.type`: - +--------------+--------------------------------+ - | Value | Reason | - +--------------+--------------------------------+ - | ``NotFound`` | The server/role was not found. | - +--------------+--------------------------------+ + +--------------+-------------------------------------+ + | Value | Reason | + +--------------+-------------------------------------+ + | ``NotFound`` | The server/role/file was not found. | + +--------------+-------------------------------------+ :class:`InternalServerError` Possible values for :attr:`~HTTPException.type`: @@ -6309,6 +6314,11 @@ async def edit_role( if name is not UNDEFINED: payload['name'] = name + if icon is not UNDEFINED: + if icon is None: + remove.append('Icon') + else: + payload['icon'] = await resolve_resource(self.state, icon, tag='icons') if color is not UNDEFINED: if color is None: remove.append('Colour') diff --git a/pyvolt/parser.py b/pyvolt/parser.py index 3c3d356..26061b2 100644 --- a/pyvolt/parser.py +++ b/pyvolt/parser.py @@ -3231,16 +3231,18 @@ def parse_role(self, payload: raw.Role, role_id: str, server_id: str, /) -> Role server_id: :class:`str` The server's ID the role belongs to. - Returns ------- :class:`Role` The parsed role object. """ + icon = payload.get('icon') + return Role( state=self.state, id=role_id, name=payload['name'], + internal_icon=None if icon is None else self.parse_asset(icon), permissions=self.parse_permission_override_field(payload['permissions']), color=payload.get('colour'), hoist=payload.get('hoist', False), @@ -3667,6 +3669,7 @@ def parse_server_role_update_event( data = payload['data'] clear = payload['clear'] + icon = data.get('icon') permissions = data.get('permissions') return RawServerRoleUpdateEvent( @@ -3676,6 +3679,7 @@ def parse_server_role_update_event( id=payload['role_id'], server_id=payload['id'], name=data.get('name', UNDEFINED), + internal_icon=None if 'Icon' in clear else UNDEFINED if icon is None else self.parse_asset(icon), permissions=UNDEFINED if permissions is None else self.parse_permission_override_field(permissions), color=None if 'Colour' in clear else data.get('colour', UNDEFINED), hoist=data.get('hoist', UNDEFINED), diff --git a/pyvolt/raw/servers.py b/pyvolt/raw/servers.py index e623afe..949edb8 100644 --- a/pyvolt/raw/servers.py +++ b/pyvolt/raw/servers.py @@ -49,6 +49,7 @@ class PartialServer(typing.TypedDict): class Role(typing.TypedDict): name: str + icon: typing_extensions.NotRequired[File] permissions: OverrideField colour: typing_extensions.NotRequired[str] hoist: typing_extensions.NotRequired[bool] @@ -57,6 +58,7 @@ class Role(typing.TypedDict): class PartialRole(typing.TypedDict): name: typing_extensions.NotRequired[str] + icon: typing_extensions.NotRequired[File] permissions: typing_extensions.NotRequired[OverrideField] colour: typing_extensions.NotRequired[str] hoist: typing_extensions.NotRequired[bool] @@ -64,7 +66,7 @@ class PartialRole(typing.TypedDict): FieldsServer = typing.Literal['Description', 'Categories', 'SystemMessages', 'Icon', 'Banner'] -FieldsRole = typing.Literal['Colour'] +FieldsRole = typing.Literal['Colour', 'Icon'] class Category(typing.TypedDict): @@ -128,6 +130,7 @@ class DataEditServer(typing.TypedDict): class DataEditRole(typing.TypedDict): name: typing_extensions.NotRequired[str] + icon: typing_extensions.NotRequired[str] colour: typing_extensions.NotRequired[str] hoist: typing_extensions.NotRequired[bool] rank: typing_extensions.NotRequired[int] diff --git a/pyvolt/server.py b/pyvolt/server.py index cd9722e..0171d9e 100644 --- a/pyvolt/server.py +++ b/pyvolt/server.py @@ -384,6 +384,7 @@ async def edit( *, http_overrides: typing.Optional[HTTPOverrideOptions] = None, name: UndefinedOr[str] = UNDEFINED, + icon: UndefinedOr[typing.Optional[ResolvableResource]] = UNDEFINED, color: UndefinedOr[typing.Optional[str]] = UNDEFINED, hoist: UndefinedOr[bool] = UNDEFINED, rank: UndefinedOr[int] = UNDEFINED, @@ -402,6 +403,10 @@ async def edit( The HTTP request overrides. name: UndefinedOr[:class:`str`] The new role name. Must be between 1 and 32 characters long. + icon: UndefinedOr[Optional[:class:`.ResolvableResource`]] + The new role icon. + + .. versionadded:: 1.2 color: UndefinedOr[Optional[:class:`str`]] The new role color. Must be a valid CSS color. hoist: UndefinedOr[:class:`bool`] @@ -439,11 +444,11 @@ async def edit( :class:`NotFound` Possible values for :attr:`~HTTPException.type`: - +--------------+--------------------------------+ - | Value | Reason | - +--------------+--------------------------------+ - | ``NotFound`` | The server/role was not found. | - +--------------+--------------------------------+ + +--------------+-------------------------------------+ + | Value | Reason | + +--------------+-------------------------------------+ + | ``NotFound`` | The server/role/file was not found. | + +--------------+-------------------------------------+ :class:`InternalServerError` Possible values for :attr:`~HTTPException.type`: @@ -463,6 +468,7 @@ async def edit( self.id, http_overrides=http_overrides, name=name, + icon=icon, color=color, hoist=hoist, rank=rank, @@ -554,6 +560,12 @@ class PartialRole(BaseRole): name: UndefinedOr[str] = field(repr=True, kw_only=True) """UndefinedOr[:class:`str`]: The new role's name.""" + internal_icon: UndefinedOr[typing.Optional[StatelessAsset]] = field(repr=True, kw_only=True) + """UndefinedOr[Optional[:class:`.StatelessAsset`]]: The new role's icon, if any. + + .. versionadded:: 1.2 + """ + permissions: UndefinedOr[PermissionOverride] = field(repr=True, kw_only=True) """UndefinedOr[:class:`.PermissionOverride`]: The new role's permissions.""" @@ -570,6 +582,7 @@ def into_full(self) -> typing.Optional[Role]: """Optional[:class:`.Role`]: Tries transform this partial role into full object. This is useful when caching role.""" if ( self.name is not UNDEFINED + and self.internal_icon is not UNDEFINED and self.permissions is not UNDEFINED and self.hoist is not UNDEFINED and self.rank is not UNDEFINED @@ -580,12 +593,21 @@ def into_full(self) -> typing.Optional[Role]: id=self.id, server_id=self.server_id, name=self.name, + internal_icon=self.internal_icon, permissions=self.permissions, color=color, hoist=self.hoist, rank=self.rank, ) + @property + def icon(self) -> UndefinedOr[typing.Optional[Asset]]: + """UndefinedOr[Optional[:class:`.Asset`]]: The stateful role icon. + + .. versionadded:: 1.2 + """ + return self.internal_icon and self.internal_icon.attach_state(self.state, 'icons') + @define(slots=True) class Role(BaseRole): @@ -597,6 +619,12 @@ class Role(BaseRole): name: str = field(repr=True, kw_only=True) """:class:`str`: The role's name.""" + internal_icon: typing.Optional[StatelessAsset] = field(repr=True, kw_only=True) + """Optional[:class:`.StatelessAsset`]: The new server's icon, if any. + + .. versionadded:: 1.2 + """ + permissions: PermissionOverride = field(repr=True, kw_only=True) """:class:`.PermissionOverride`: Permissions available to this role.""" @@ -617,6 +645,8 @@ def locally_update(self, data: PartialRole, /) -> None: """ if data.name is not UNDEFINED: self.name = data.name + if data.internal_icon is not UNDEFINED: + self.internal_icon = data.internal_icon if data.permissions is not UNDEFINED: self.permissions = data.permissions if data.color is not UNDEFINED: @@ -626,20 +656,27 @@ def locally_update(self, data: PartialRole, /) -> None: if data.rank is not UNDEFINED: self.rank = data.rank + @property + def icon(self) -> UndefinedOr[typing.Optional[Asset]]: + """UndefinedOr[Optional[:class:`.Asset`]]: The stateful role icon. + + .. versionadded:: 1.2 + """ + return self.internal_icon and self.internal_icon.attach_state(self.state, 'icons') + def to_dict(self) -> raw.Role: """:class:`dict`: Convert role to raw data.""" - if self.color is None: - payload = { - 'name': self.name, - 'permissions': self.permissions.to_field_dict(), - } - else: - payload = { - 'name': self.name, - 'permissions': self.permissions.to_field_dict(), - 'colour': self.color, - } + payload: dict[str, typing.Any] = { + 'name': self.name, + } + if self.internal_icon is not None: + payload['icon'] = self.internal_icon.to_dict('icons') + + payload['permissions'] = self.permissions.to_field_dict() + + if self.color is not None: + payload['colour'] = self.color if self.hoist: payload['hoist'] = self.hoist