mirror of
https://github.com/Rapptz/discord.py.git
synced 2025-06-07 12:18:59 +00:00
Support for avatar decorations
Co-authored-by: Danny <1695103+Rapptz@users.noreply.github.com> Co-authored-by: owocado <24418520+owocado@users.noreply.github.com>
This commit is contained in:
parent
070ae24d8d
commit
e25b7ff3f8
@ -248,6 +248,22 @@ class User(Snowflake, Protocol):
|
||||
"""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.
|
||||
|
||||
.. versionadded:: 2.4
|
||||
"""
|
||||
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."""
|
||||
|
@ -246,6 +246,15 @@ class Asset(AssetMixin):
|
||||
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(
|
||||
|
@ -67,7 +67,7 @@ if TYPE_CHECKING:
|
||||
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,6 +323,7 @@ class Member(discord.abc.Messageable, _UserTag):
|
||||
'_state',
|
||||
'_avatar',
|
||||
'_flags',
|
||||
'_avatar_decoration_data',
|
||||
)
|
||||
|
||||
if TYPE_CHECKING:
|
||||
@ -342,6 +343,8 @@ class Member(discord.abc.Messageable, _UserTag):
|
||||
banner: Optional[Asset]
|
||||
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
|
||||
@ -357,6 +360,7 @@ class Member(discord.abc.Messageable, _UserTag):
|
||||
self._avatar: Optional[str] = data.get('avatar')
|
||||
self._permissions: Optional[int]
|
||||
self._flags: int = data['flags']
|
||||
self._avatar_decoration_data: Optional[AvatarDecorationData] = data.get('avatar_decoration_data')
|
||||
try:
|
||||
self._permissions = int(data['permissions'])
|
||||
except KeyError:
|
||||
@ -425,6 +429,7 @@ class Member(discord.abc.Messageable, _UserTag):
|
||||
self._permissions = member._permissions
|
||||
self._state = member._state
|
||||
self._avatar = member._avatar
|
||||
self._avatar_decoration_data = member._avatar_decoration_data
|
||||
|
||||
# Reference will not be copied unless necessary by PRESENCE_UPDATE
|
||||
# See below
|
||||
@ -453,6 +458,7 @@ class Member(discord.abc.Messageable, _UserTag):
|
||||
self._roles = utils.SnowflakeList(map(int, data['roles']))
|
||||
self._avatar = data.get('avatar')
|
||||
self._flags = data.get('flags', 0)
|
||||
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'])
|
||||
@ -464,7 +470,16 @@ class Member(discord.abc.Messageable, _UserTag):
|
||||
|
||||
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)
|
||||
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'],
|
||||
@ -472,10 +487,18 @@ class Member(discord.abc.Messageable, _UserTag):
|
||||
user['avatar'],
|
||||
user.get('global_name'),
|
||||
user.get('public_flags', 0),
|
||||
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 = 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
|
||||
|
||||
|
@ -41,7 +41,7 @@ from .message import Message
|
||||
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
|
||||
@ -228,6 +228,7 @@ class GuildMemberUpdateEvent(TypedDict):
|
||||
mute: NotRequired[bool]
|
||||
pending: NotRequired[bool]
|
||||
communication_disabled_until: NotRequired[str]
|
||||
avatar_decoration_data: NotRequired[AvatarDecorationData]
|
||||
|
||||
|
||||
class GuildEmojisUpdateEvent(TypedDict):
|
||||
|
@ -24,7 +24,8 @@ DEALINGS IN THE SOFTWARE.
|
||||
|
||||
from typing import Optional, TypedDict
|
||||
from .snowflake import SnowflakeList
|
||||
from .user import User
|
||||
from .user import User, AvatarDecorationData
|
||||
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_data: NotRequired[AvatarDecorationData]
|
||||
|
||||
|
||||
class _OptionalMemberWithUser(PartialMember, total=False):
|
||||
@ -56,6 +58,7 @@ class _OptionalMemberWithUser(PartialMember, total=False):
|
||||
pending: bool
|
||||
permissions: str
|
||||
communication_disabled_until: str
|
||||
avatar_decoration_data: NotRequired[AvatarDecorationData]
|
||||
|
||||
|
||||
class MemberWithUser(_OptionalMemberWithUser):
|
||||
|
@ -24,6 +24,12 @@ DEALINGS IN THE SOFTWARE.
|
||||
|
||||
from .snowflake import Snowflake
|
||||
from typing import Literal, Optional, TypedDict
|
||||
from typing_extensions import NotRequired
|
||||
|
||||
|
||||
class AvatarDecorationData(TypedDict):
|
||||
asset: str
|
||||
sku_id: Snowflake
|
||||
|
||||
|
||||
class PartialUser(TypedDict):
|
||||
@ -32,6 +38,7 @@ class PartialUser(TypedDict):
|
||||
discriminator: str
|
||||
avatar: Optional[str]
|
||||
global_name: Optional[str]
|
||||
avatar_decoration_data: NotRequired[AvatarDecorationData]
|
||||
|
||||
|
||||
PremiumType = Literal[0, 1, 2, 3]
|
||||
|
@ -31,7 +31,7 @@ from .asset import Asset
|
||||
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
|
||||
@ -43,10 +43,7 @@ if TYPE_CHECKING:
|
||||
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,6 +70,7 @@ class BaseUser(_UserTag):
|
||||
'system',
|
||||
'_public_flags',
|
||||
'_state',
|
||||
'_avatar_decoration_data',
|
||||
)
|
||||
|
||||
if TYPE_CHECKING:
|
||||
@ -87,6 +85,7 @@ class BaseUser(_UserTag):
|
||||
_banner: Optional[str]
|
||||
_accent_colour: Optional[int]
|
||||
_public_flags: int
|
||||
_avatar_decoration_data: Optional[AvatarDecorationData]
|
||||
|
||||
def __init__(self, *, state: ConnectionState, data: Union[UserPayload, PartialUserPayload]) -> None:
|
||||
self._state = state
|
||||
@ -123,6 +122,7 @@ class BaseUser(_UserTag):
|
||||
self._public_flags = data.get('public_flags', 0)
|
||||
self.bot = data.get('bot', False)
|
||||
self.system = data.get('system', False)
|
||||
self._avatar_decoration_data = data.get('avatar_decoration_data')
|
||||
|
||||
@classmethod
|
||||
def _copy(cls, user: Self) -> Self:
|
||||
@ -138,6 +138,7 @@ class BaseUser(_UserTag):
|
||||
self.bot = user.bot
|
||||
self._state = user._state
|
||||
self._public_flags = user._public_flags
|
||||
self._avatar_decoration_data = user._avatar_decoration_data
|
||||
|
||||
return self
|
||||
|
||||
@ -187,6 +188,30 @@ class BaseUser(_UserTag):
|
||||
"""
|
||||
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.4
|
||||
"""
|
||||
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 _get_as_snowflake(self._avatar_decoration_data, 'sku_id')
|
||||
return None
|
||||
|
||||
@property
|
||||
def banner(self) -> Optional[Asset]:
|
||||
"""Optional[:class:`Asset`]: Returns the user's banner asset, if available.
|
||||
|
@ -1305,9 +1305,10 @@ class Webhook(BaseWebhook):
|
||||
'user': {
|
||||
'username': user.name,
|
||||
'discriminator': user.discriminator,
|
||||
'global_name': user.global_name,
|
||||
'id': user.id,
|
||||
'avatar': user._avatar,
|
||||
'avatar_decoration_data': user._avatar_decoration_data,
|
||||
'global_name': user.global_name,
|
||||
},
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user