From 87f6ce11958102787cc383ca5f86940bc13a3500 Mon Sep 17 00:00:00 2001 From: Puncher <65789180+Puncher1@users.noreply.github.com> Date: Fri, 7 Apr 2023 19:55:44 +0200 Subject: [PATCH 01/10] Support for avatar decoration --- discord/abc.py | 5 +++++ discord/asset.py | 9 +++++++++ discord/member.py | 17 ++++++++++++++--- discord/types/gateway.py | 1 + discord/types/member.py | 3 +++ discord/types/user.py | 2 ++ discord/user.py | 16 ++++++++++++++++ discord/webhook/async_.py | 8 +++++++- 8 files changed, 57 insertions(+), 4 deletions(-) diff --git a/discord/abc.py b/discord/abc.py index c6b63920f1fc..be2e4411d7e7 100644 --- a/discord/abc.py +++ b/discord/abc.py @@ -246,6 +246,11 @@ def avatar(self) -> Optional[Asset]: """Optional[:class:`~discord.Asset`]: Returns an Asset that represents the user's avatar, if present.""" raise NotImplementedError + @property + def avatar_decoration(self) -> Optional[Asset]: + """Optional[:class:`~discord.Asset`]: Returns an Asset that represents the user's avatar decoration, if present.""" + raise NotImplementedError + @property def default_avatar(self) -> Asset: """:class:`~discord.Asset`: Returns the default avatar for a given user. This is calculated by the user's discriminator.""" diff --git a/discord/asset.py b/discord/asset.py index d88ebb945d44..d08635632015 100644 --- a/discord/asset.py +++ b/discord/asset.py @@ -246,6 +246,15 @@ def _from_guild_avatar(cls, state: _State, guild_id: int, member_id: int, avatar animated=animated, ) + @classmethod + def _from_avatar_decoration(cls, state: _State, avatar_decoration: str) -> Self: + return cls( + state, + url=f'{cls.BASE}/avatar-decoration-presets/{avatar_decoration}.png?size=96', + key=avatar_decoration, + animated=True, + ) + @classmethod def _from_icon(cls, state: _State, object_id: int, icon_hash: str, path: str) -> Self: return cls( diff --git a/discord/member.py b/discord/member.py index ee303f5bc290..e3ce577260b2 100644 --- a/discord/member.py +++ b/discord/member.py @@ -323,6 +323,7 @@ class Member(discord.abc.Messageable, _UserTag): '_state', '_avatar', '_flags', + '_avatar_decoration', ) if TYPE_CHECKING: @@ -341,6 +342,7 @@ class Member(discord.abc.Messageable, _UserTag): banner: Optional[Asset] accent_color: Optional[Colour] accent_colour: Optional[Colour] + avatar_decoration: Optional[Asset] def __init__(self, *, data: MemberWithUserPayload, guild: Guild, state: ConnectionState): self._state: ConnectionState = state @@ -356,6 +358,7 @@ def __init__(self, *, data: MemberWithUserPayload, guild: Guild, state: Connecti self._avatar: Optional[str] = data.get('avatar') self._permissions: Optional[int] self._flags: int = data['flags'] + self._avatar_decoration: Optional[str] = data.get('avatar_decoration') try: self._permissions = int(data['permissions']) except KeyError: @@ -424,6 +427,7 @@ def _copy(cls, member: Self) -> Self: self._permissions = member._permissions self._state = member._state self._avatar = member._avatar + self._avatar_decoration = member._avatar_decoration # Reference will not be copied unless necessary by PRESENCE_UPDATE # See below @@ -452,6 +456,7 @@ def _update(self, data: GuildMemberUpdateEvent) -> None: self._roles = utils.SnowflakeList(map(int, data['roles'])) self._avatar = data.get('avatar') self._flags = data.get('flags', 0) + self._avatar_decoration = data.get('avatar_decoration') def _presence_update(self, data: PartialPresenceUpdate, user: UserPayload) -> Optional[Tuple[User, User]]: self.activities = tuple(create_activity(d, self._state) for d in data['activities']) @@ -463,12 +468,18 @@ def _presence_update(self, data: PartialPresenceUpdate, user: UserPayload) -> Op def _update_inner_user(self, user: UserPayload) -> Optional[Tuple[User, User]]: u = self._user - original = (u.name, u._avatar, u.discriminator, u._public_flags) + original = (u.name, u._avatar, u.discriminator, u._public_flags, u._avatar_decoration) # These keys seem to always be available - modified = (user['username'], user['avatar'], user['discriminator'], user.get('public_flags', 0)) + modified = ( + user['username'], + user['avatar'], + user['discriminator'], + user.get('public_flags', 0), + user.get('avatar_decoration'), + ) if original != modified: to_return = User._copy(self._user) - u.name, u._avatar, u.discriminator, u._public_flags = modified + u.name, u._avatar, u.discriminator, u._public_flags, u._avatar_decoration = modified # Signal to dispatch on_user_update return to_return, u diff --git a/discord/types/gateway.py b/discord/types/gateway.py index a87b101f0f2b..0e0affcee262 100644 --- a/discord/types/gateway.py +++ b/discord/types/gateway.py @@ -223,6 +223,7 @@ class GuildMemberUpdateEvent(TypedDict): mute: NotRequired[bool] pending: NotRequired[bool] communication_disabled_until: NotRequired[str] + avatar_decoration: NotRequired[str] class GuildEmojisUpdateEvent(TypedDict): diff --git a/discord/types/member.py b/discord/types/member.py index ad9e49008a12..c4ffbe14fe82 100644 --- a/discord/types/member.py +++ b/discord/types/member.py @@ -25,6 +25,7 @@ from typing import Optional, TypedDict from .snowflake import SnowflakeList from .user import User +from typing_extensions import NotRequired class Nickname(TypedDict): @@ -47,6 +48,7 @@ class Member(PartialMember, total=False): pending: bool permissions: str communication_disabled_until: str + avatar_decoration: NotRequired[str] class _OptionalMemberWithUser(PartialMember, total=False): @@ -56,6 +58,7 @@ class _OptionalMemberWithUser(PartialMember, total=False): pending: bool permissions: str communication_disabled_until: str + avatar_decoration: NotRequired[str] class MemberWithUser(_OptionalMemberWithUser): diff --git a/discord/types/user.py b/discord/types/user.py index fba5aef5bfd4..d1733be79c62 100644 --- a/discord/types/user.py +++ b/discord/types/user.py @@ -24,6 +24,7 @@ from .snowflake import Snowflake from typing import Literal, Optional, TypedDict +from typing_extensions import NotRequired class PartialUser(TypedDict): @@ -31,6 +32,7 @@ class PartialUser(TypedDict): username: str discriminator: str avatar: Optional[str] + avatar_decoration: NotRequired[str] PremiumType = Literal[0, 1, 2] diff --git a/discord/user.py b/discord/user.py index 23868712cd3f..ca0912ac2eea 100644 --- a/discord/user.py +++ b/discord/user.py @@ -72,6 +72,7 @@ class BaseUser(_UserTag): 'system', '_public_flags', '_state', + '_avatar_decoration', ) if TYPE_CHECKING: @@ -85,6 +86,7 @@ class BaseUser(_UserTag): _banner: Optional[str] _accent_colour: Optional[int] _public_flags: int + _avatar_decoration: Optional[str] def __init__(self, *, state: ConnectionState, data: Union[UserPayload, PartialUserPayload]) -> None: self._state = state @@ -118,6 +120,7 @@ def _update(self, data: Union[UserPayload, PartialUserPayload]) -> None: self._public_flags = data.get('public_flags', 0) self.bot = data.get('bot', False) self.system = data.get('system', False) + self._avatar_decoration = data.get('avatar_decoration') @classmethod def _copy(cls, user: Self) -> Self: @@ -132,6 +135,7 @@ def _copy(cls, user: Self) -> Self: self.bot = user.bot self._state = user._state self._public_flags = user._public_flags + self._avatar_decoration = user._avatar_decoration return self @@ -175,6 +179,18 @@ def display_avatar(self) -> Asset: """ return self.avatar or self.default_avatar + @property + def avatar_decoration(self) -> Optional[Asset]: + """Optional[:class:`Asset`]: Returns an :class:`Asset` for the avatar decoration the user has. + + If the user has not set an avatar decoration, ``None`` is returned. + + .. versionadded:: 2.3 + """ + if self._avatar_decoration is not None: + return Asset._from_avatar_decoration(self._state, self._avatar_decoration) + return None + @property def banner(self) -> Optional[Asset]: """Optional[:class:`Asset`]: Returns the user's banner asset, if available. diff --git a/discord/webhook/async_.py b/discord/webhook/async_.py index f9b03193a613..eb554ceeab2f 100644 --- a/discord/webhook/async_.py +++ b/discord/webhook/async_.py @@ -1301,7 +1301,13 @@ def _as_follower(cls, data, *, channel, user) -> Self: 'name': name, 'channel_id': channel.id, 'guild_id': channel.guild.id, - 'user': {'username': user.name, 'discriminator': user.discriminator, 'id': user.id, 'avatar': user._avatar}, + 'user': { + 'username': user.name, + 'discriminator': user.discriminator, + 'id': user.id, + 'avatar': user._avatar, + 'avatar_decoration': user._avatar_decoration, + }, } state = channel._state From b838e966c9aec1eec372fb62d7e4d27452828016 Mon Sep 17 00:00:00 2001 From: Puncher <65789180+Puncher1@users.noreply.github.com> Date: Fri, 7 Apr 2023 19:58:15 +0200 Subject: [PATCH 02/10] Add versionadded tag --- discord/abc.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/discord/abc.py b/discord/abc.py index be2e4411d7e7..4a0a9eb43fa4 100644 --- a/discord/abc.py +++ b/discord/abc.py @@ -248,7 +248,10 @@ def avatar(self) -> Optional[Asset]: @property def avatar_decoration(self) -> Optional[Asset]: - """Optional[:class:`~discord.Asset`]: Returns an Asset that represents the user's avatar decoration, if present.""" + """Optional[:class:`~discord.Asset`]: Returns an Asset that represents the user's avatar decoration, if present. + + .. versionadded:: 2.3 + """ raise NotImplementedError @property From d6efc01c3b73b7f0280380afee4f85c01b2c0b2d Mon Sep 17 00:00:00 2001 From: Puncher <65789180+Puncher1@users.noreply.github.com> Date: Tue, 18 Apr 2023 13:49:48 +0200 Subject: [PATCH 03/10] Remove size from url --- discord/asset.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/discord/asset.py b/discord/asset.py index d08635632015..0dc7835fdb18 100644 --- a/discord/asset.py +++ b/discord/asset.py @@ -250,7 +250,7 @@ def _from_guild_avatar(cls, state: _State, guild_id: int, member_id: int, avatar def _from_avatar_decoration(cls, state: _State, avatar_decoration: str) -> Self: return cls( state, - url=f'{cls.BASE}/avatar-decoration-presets/{avatar_decoration}.png?size=96', + url=f'{cls.BASE}/avatar-decoration-presets/{avatar_decoration}.png', key=avatar_decoration, animated=True, ) From 4cd87008f8e5667bab367ceb197b98c3422da35f Mon Sep 17 00:00:00 2001 From: Puncher <65789180+Puncher1@users.noreply.github.com> Date: Tue, 18 Apr 2023 14:03:08 +0200 Subject: [PATCH 04/10] Revert "Remove size from url" This reverts commit d6efc01c3b73b7f0280380afee4f85c01b2c0b2d. --- discord/asset.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/discord/asset.py b/discord/asset.py index 0dc7835fdb18..d08635632015 100644 --- a/discord/asset.py +++ b/discord/asset.py @@ -250,7 +250,7 @@ def _from_guild_avatar(cls, state: _State, guild_id: int, member_id: int, avatar def _from_avatar_decoration(cls, state: _State, avatar_decoration: str) -> Self: return cls( state, - url=f'{cls.BASE}/avatar-decoration-presets/{avatar_decoration}.png', + url=f'{cls.BASE}/avatar-decoration-presets/{avatar_decoration}.png?size=96', key=avatar_decoration, animated=True, ) From bcd4a5fe998b9193102564eb6f648b88d529d10d Mon Sep 17 00:00:00 2001 From: Puncher <65789180+Puncher1@users.noreply.github.com> Date: Sun, 17 Sep 2023 17:13:34 +0200 Subject: [PATCH 05/10] Change versionchanged to 2.4 --- discord/abc.py | 2 +- discord/user.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/discord/abc.py b/discord/abc.py index 45f5d2ea162a..26e44982c294 100644 --- a/discord/abc.py +++ b/discord/abc.py @@ -252,7 +252,7 @@ def avatar(self) -> Optional[Asset]: def avatar_decoration(self) -> Optional[Asset]: """Optional[:class:`~discord.Asset`]: Returns an Asset that represents the user's avatar decoration, if present. - .. versionadded:: 2.3 + .. versionadded:: 2.4 """ raise NotImplementedError diff --git a/discord/user.py b/discord/user.py index 2e317a5fcede..0fe4ffefd1fc 100644 --- a/discord/user.py +++ b/discord/user.py @@ -197,7 +197,7 @@ def avatar_decoration(self) -> Optional[Asset]: If the user has not set an avatar decoration, ``None`` is returned. - .. versionadded:: 2.3 + .. versionadded:: 2.4 """ if self._avatar_decoration is not None: return Asset._from_avatar_decoration(self._state, self._avatar_decoration) From bea96692a502aecd652bc5f8f256b06062510e40 Mon Sep 17 00:00:00 2001 From: Puncher <65789180+Puncher1@users.noreply.github.com> Date: Sun, 1 Oct 2023 18:14:26 +0200 Subject: [PATCH 06/10] Implement new field avatar_decoration_data --- discord/member.py | 17 +++++++++-------- discord/types/gateway.py | 4 ++-- discord/types/member.py | 6 +++--- discord/types/user.py | 7 ++++++- discord/user.py | 29 +++++++++++++++++++---------- discord/webhook/async_.py | 5 +---- 6 files changed, 40 insertions(+), 28 deletions(-) diff --git a/discord/member.py b/discord/member.py index 9dcf1a06c391..c447f1f5fcb6 100644 --- a/discord/member.py +++ b/discord/member.py @@ -67,7 +67,7 @@ UserWithMember as UserWithMemberPayload, ) from .types.gateway import GuildMemberUpdateEvent - from .types.user import User as UserPayload + from .types.user import User as UserPayload, AvatarDecorationData from .abc import Snowflake from .state import ConnectionState from .message import Message @@ -323,7 +323,7 @@ class Member(discord.abc.Messageable, _UserTag): '_state', '_avatar', '_flags', - '_avatar_decoration', + '_avatar_decoration_data', ) if TYPE_CHECKING: @@ -344,6 +344,7 @@ class Member(discord.abc.Messageable, _UserTag): accent_color: Optional[Colour] accent_colour: Optional[Colour] avatar_decoration: Optional[Asset] + avatar_decoration_sku_id: Optional[int] def __init__(self, *, data: MemberWithUserPayload, guild: Guild, state: ConnectionState): self._state: ConnectionState = state @@ -359,7 +360,7 @@ def __init__(self, *, data: MemberWithUserPayload, guild: Guild, state: Connecti self._avatar: Optional[str] = data.get('avatar') self._permissions: Optional[int] self._flags: int = data['flags'] - self._avatar_decoration: Optional[str] = data.get('avatar_decoration') + self._avatar_decoration_data: Optional[AvatarDecorationData] = data.get('avatar_decoration_data') try: self._permissions = int(data['permissions']) except KeyError: @@ -428,7 +429,7 @@ def _copy(cls, member: Self) -> Self: self._permissions = member._permissions self._state = member._state self._avatar = member._avatar - self._avatar_decoration = member._avatar_decoration + self._avatar_decoration_data = member._avatar_decoration_data # Reference will not be copied unless necessary by PRESENCE_UPDATE # See below @@ -457,7 +458,7 @@ def _update(self, data: GuildMemberUpdateEvent) -> None: self._roles = utils.SnowflakeList(map(int, data['roles'])) self._avatar = data.get('avatar') self._flags = data.get('flags', 0) - self._avatar_decoration = data.get('avatar_decoration') + self._avatar_decoration_data = data.get('avatar_decoration_data') def _presence_update(self, data: PartialPresenceUpdate, user: UserPayload) -> Optional[Tuple[User, User]]: self.activities = tuple(create_activity(d, self._state) for d in data['activities']) @@ -469,7 +470,7 @@ def _presence_update(self, data: PartialPresenceUpdate, user: UserPayload) -> Op def _update_inner_user(self, user: UserPayload) -> Optional[Tuple[User, User]]: u = self._user - original = (u.name, u.discriminator, u._avatar, u.global_name, u._public_flags, u._avatar_decoration) + original = (u.name, u.discriminator, u._avatar, u.global_name, u._public_flags, u._avatar_decoration_data) # These keys seem to always be available modified = ( user['username'], @@ -477,11 +478,11 @@ def _update_inner_user(self, user: UserPayload) -> Optional[Tuple[User, User]]: user['avatar'], user.get('global_name'), user.get('public_flags', 0), - user.get('avatar_decoration'), + user.get('avatar_decoration_data'), ) if original != modified: to_return = User._copy(self._user) - u.name, u.discriminator, u._avatar, u.global_name, u._public_flags, u._avatar_decoration = modified + u.name, u.discriminator, u._avatar, u.global_name, u._public_flags, u._avatar_decoration_data = modified # Signal to dispatch on_user_update return to_return, u diff --git a/discord/types/gateway.py b/discord/types/gateway.py index 21510ca08319..866c32946cc3 100644 --- a/discord/types/gateway.py +++ b/discord/types/gateway.py @@ -40,7 +40,7 @@ from .sticker import GuildSticker from .appinfo import GatewayAppInfo, PartialAppInfo from .guild import Guild, UnavailableGuild -from .user import User +from .user import User, AvatarDecorationData from .threads import Thread, ThreadMember from .scheduled_event import GuildScheduledEvent from .audit_log import AuditLogEntry @@ -224,7 +224,7 @@ class GuildMemberUpdateEvent(TypedDict): mute: NotRequired[bool] pending: NotRequired[bool] communication_disabled_until: NotRequired[str] - avatar_decoration: NotRequired[str] + avatar_decoration_data: NotRequired[AvatarDecorationData] class GuildEmojisUpdateEvent(TypedDict): diff --git a/discord/types/member.py b/discord/types/member.py index c4ffbe14fe82..6968edb6f47f 100644 --- a/discord/types/member.py +++ b/discord/types/member.py @@ -24,7 +24,7 @@ from typing import Optional, TypedDict from .snowflake import SnowflakeList -from .user import User +from .user import User, AvatarDecorationData from typing_extensions import NotRequired @@ -48,7 +48,7 @@ class Member(PartialMember, total=False): pending: bool permissions: str communication_disabled_until: str - avatar_decoration: NotRequired[str] + avatar_decoration_data: NotRequired[AvatarDecorationData] class _OptionalMemberWithUser(PartialMember, total=False): @@ -58,7 +58,7 @@ class _OptionalMemberWithUser(PartialMember, total=False): pending: bool permissions: str communication_disabled_until: str - avatar_decoration: NotRequired[str] + avatar_decoration_data: NotRequired[AvatarDecorationData] class MemberWithUser(_OptionalMemberWithUser): diff --git a/discord/types/user.py b/discord/types/user.py index 85bde0cdc777..1f027ce9d9ac 100644 --- a/discord/types/user.py +++ b/discord/types/user.py @@ -27,13 +27,18 @@ from typing_extensions import NotRequired +class AvatarDecorationData(TypedDict): + asset: str + sku_id: Snowflake + + class PartialUser(TypedDict): id: Snowflake username: str discriminator: str avatar: Optional[str] global_name: Optional[str] - avatar_decoration: NotRequired[str] + avatar_decoration_data: NotRequired[AvatarDecorationData] PremiumType = Literal[0, 1, 2, 3] diff --git a/discord/user.py b/discord/user.py index 0fe4ffefd1fc..8207c62aa769 100644 --- a/discord/user.py +++ b/discord/user.py @@ -43,10 +43,7 @@ from .message import Message from .state import ConnectionState from .types.channel import DMChannel as DMChannelPayload - from .types.user import ( - PartialUser as PartialUserPayload, - User as UserPayload, - ) + from .types.user import PartialUser as PartialUserPayload, User as UserPayload, AvatarDecorationData __all__ = ( @@ -73,7 +70,7 @@ class BaseUser(_UserTag): 'system', '_public_flags', '_state', - '_avatar_decoration', + '_avatar_decoration_data', ) if TYPE_CHECKING: @@ -88,7 +85,7 @@ class BaseUser(_UserTag): _banner: Optional[str] _accent_colour: Optional[int] _public_flags: int - _avatar_decoration: Optional[str] + _avatar_decoration_data: Optional[AvatarDecorationData] def __init__(self, *, state: ConnectionState, data: Union[UserPayload, PartialUserPayload]) -> None: self._state = state @@ -125,7 +122,7 @@ def _update(self, data: Union[UserPayload, PartialUserPayload]) -> None: self._public_flags = data.get('public_flags', 0) self.bot = data.get('bot', False) self.system = data.get('system', False) - self._avatar_decoration = data.get('avatar_decoration') + self._avatar_decoration_data = data.get('avatar_decoration_data') @classmethod def _copy(cls, user: Self) -> Self: @@ -141,7 +138,7 @@ def _copy(cls, user: Self) -> Self: self.bot = user.bot self._state = user._state self._public_flags = user._public_flags - self._avatar_decoration = user._avatar_decoration + self._avatar_decoration_data = user._avatar_decoration_data return self @@ -199,8 +196,20 @@ def avatar_decoration(self) -> Optional[Asset]: .. versionadded:: 2.4 """ - if self._avatar_decoration is not None: - return Asset._from_avatar_decoration(self._state, self._avatar_decoration) + if self._avatar_decoration_data is not None: + return Asset._from_avatar_decoration(self._state, self._avatar_decoration_data['asset']) + return None + + @property + def avatar_decoration_sku_id(self) -> Optional[int]: + """Optional[:class:`int`]: Returns the SKU ID of the avatar decoration the user has. + + If the user has not set an avatar decoration, ``None`` is returned. + + .. versionadded:: 2.4 + """ + if self._avatar_decoration_data is not None: + return int(self._avatar_decoration_data['sku_id']) return None @property diff --git a/discord/webhook/async_.py b/discord/webhook/async_.py index e44f4ff0e777..2834aedcea24 100644 --- a/discord/webhook/async_.py +++ b/discord/webhook/async_.py @@ -1306,11 +1306,8 @@ def _as_follower(cls, data, *, channel, user) -> Self: 'discriminator': user.discriminator, 'id': user.id, 'avatar': user._avatar, - 'avatar_decoration': user._avatar_decoration, + 'avatar_decoration_data': user._avatar_decoration_data, 'global_name': user.global_name, - 'id': user.id, - 'avatar': user._avatar, - 'avatar_decoration': user._avatar_decoration, }, } From 6e60621c8c0b947af31c9706ae5b98faca490e68 Mon Sep 17 00:00:00 2001 From: Puncher <65789180+Puncher1@users.noreply.github.com> Date: Sun, 1 Oct 2023 18:17:45 +0200 Subject: [PATCH 07/10] Add avatar_decoration_sku_id to ABC class --- discord/abc.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/discord/abc.py b/discord/abc.py index 26e44982c294..7843161d38b8 100644 --- a/discord/abc.py +++ b/discord/abc.py @@ -256,6 +256,14 @@ def avatar_decoration(self) -> Optional[Asset]: """ raise NotImplementedError + @property + def avatar_decoration_sku_id(self) -> Optional[int]: + """Optional[:class:`int`]: Returns an integer that represents the user's avatar decoration SKU ID, if present. + + .. versionadded:: 2.4 + """ + raise NotImplementedError + @property def default_avatar(self) -> Asset: """:class:`~discord.Asset`: Returns the default avatar for a given user.""" From e0aec7219e328dbde1007b1e73d94fe588695ef9 Mon Sep 17 00:00:00 2001 From: Puncher <65789180+Puncher1@users.noreply.github.com> Date: Mon, 2 Oct 2023 19:18:09 +0200 Subject: [PATCH 08/10] Use get_as_snowflake for sku_id Co-Authored-By: Danny <1695103+Rapptz@users.noreply.github.com> --- discord/user.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/discord/user.py b/discord/user.py index 8207c62aa769..7e4ce984a3c2 100644 --- a/discord/user.py +++ b/discord/user.py @@ -31,7 +31,7 @@ from .colour import Colour from .enums import DefaultAvatar from .flags import PublicUserFlags -from .utils import snowflake_time, _bytes_to_base64_data, MISSING +from .utils import snowflake_time, _bytes_to_base64_data, MISSING, _get_as_snowflake if TYPE_CHECKING: from typing_extensions import Self @@ -208,9 +208,7 @@ def avatar_decoration_sku_id(self) -> Optional[int]: .. versionadded:: 2.4 """ - if self._avatar_decoration_data is not None: - return int(self._avatar_decoration_data['sku_id']) - return None + return _get_as_snowflake(self._avatar_decoration_data, 'sku_id') @property def banner(self) -> Optional[Asset]: From 9426f283dcb3c02becb88a275df7b9d3aad09959 Mon Sep 17 00:00:00 2001 From: Puncher <65789180+Puncher1@users.noreply.github.com> Date: Mon, 2 Oct 2023 20:31:32 +0200 Subject: [PATCH 09/10] Check avatar decoration with sku_id Co-Authored-By: Danny <1695103+Rapptz@users.noreply.github.com> --- discord/member.py | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/discord/member.py b/discord/member.py index c447f1f5fcb6..4e7c5875f51c 100644 --- a/discord/member.py +++ b/discord/member.py @@ -470,7 +470,16 @@ def _presence_update(self, data: PartialPresenceUpdate, user: UserPayload) -> Op def _update_inner_user(self, user: UserPayload) -> Optional[Tuple[User, User]]: u = self._user - original = (u.name, u.discriminator, u._avatar, u.global_name, u._public_flags, u._avatar_decoration_data) + original = ( + u.name, + u.discriminator, + u._avatar, + u.global_name, + u._public_flags, + u._avatar_decoration_data['sku_id'] if u._avatar_decoration_data is not None else None, + ) + + decoration_payload = user.get('avatar_decoration_data') # These keys seem to always be available modified = ( user['username'], @@ -478,11 +487,18 @@ def _update_inner_user(self, user: UserPayload) -> Optional[Tuple[User, User]]: user['avatar'], user.get('global_name'), user.get('public_flags', 0), - user.get('avatar_decoration_data'), + decoration_payload['sku_id'] if decoration_payload is not None else None, ) if original != modified: to_return = User._copy(self._user) - u.name, u.discriminator, u._avatar, u.global_name, u._public_flags, u._avatar_decoration_data = modified + u.name, u.discriminator, u._avatar, u.global_name, u._public_flags, u._avatar_decoration_data = ( + user['username'], + user['discriminator'], + user['avatar'], + user.get('global_name'), + user.get('public_flags', 0), + decoration_payload, + ) # Signal to dispatch on_user_update return to_return, u From 0ce61dc28a5203c145e148bafb2e4a4a473c9bf0 Mon Sep 17 00:00:00 2001 From: Andrin S <65789180+Puncher1@users.noreply.github.com> Date: Wed, 11 Oct 2023 17:27:17 +0200 Subject: [PATCH 10/10] Prevent TypeError Co-authored-by: owocado <24418520+owocado@users.noreply.github.com> --- discord/user.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/discord/user.py b/discord/user.py index 7e4ce984a3c2..b0ba869cbd37 100644 --- a/discord/user.py +++ b/discord/user.py @@ -208,7 +208,9 @@ def avatar_decoration_sku_id(self) -> Optional[int]: .. versionadded:: 2.4 """ - return _get_as_snowflake(self._avatar_decoration_data, 'sku_id') + if self._avatar_decoration_data is not None: + return _get_as_snowflake(self._avatar_decoration_data, 'sku_id') + return None @property def banner(self) -> Optional[Asset]: