mirror of
https://github.com/Rapptz/discord.py.git
synced 2025-06-07 20:28:38 +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."""
|
"""Optional[:class:`~discord.Asset`]: Returns an Asset that represents the user's avatar, if present."""
|
||||||
raise NotImplementedError
|
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
|
@property
|
||||||
def default_avatar(self) -> Asset:
|
def default_avatar(self) -> Asset:
|
||||||
""":class:`~discord.Asset`: Returns the default avatar for a given user."""
|
""":class:`~discord.Asset`: Returns the default avatar for a given user."""
|
||||||
|
@ -246,6 +246,15 @@ class Asset(AssetMixin):
|
|||||||
animated=animated,
|
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
|
@classmethod
|
||||||
def _from_icon(cls, state: _State, object_id: int, icon_hash: str, path: str) -> Self:
|
def _from_icon(cls, state: _State, object_id: int, icon_hash: str, path: str) -> Self:
|
||||||
return cls(
|
return cls(
|
||||||
|
@ -67,7 +67,7 @@ if TYPE_CHECKING:
|
|||||||
UserWithMember as UserWithMemberPayload,
|
UserWithMember as UserWithMemberPayload,
|
||||||
)
|
)
|
||||||
from .types.gateway import GuildMemberUpdateEvent
|
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 .abc import Snowflake
|
||||||
from .state import ConnectionState
|
from .state import ConnectionState
|
||||||
from .message import Message
|
from .message import Message
|
||||||
@ -323,6 +323,7 @@ class Member(discord.abc.Messageable, _UserTag):
|
|||||||
'_state',
|
'_state',
|
||||||
'_avatar',
|
'_avatar',
|
||||||
'_flags',
|
'_flags',
|
||||||
|
'_avatar_decoration_data',
|
||||||
)
|
)
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
@ -342,6 +343,8 @@ class Member(discord.abc.Messageable, _UserTag):
|
|||||||
banner: Optional[Asset]
|
banner: Optional[Asset]
|
||||||
accent_color: Optional[Colour]
|
accent_color: Optional[Colour]
|
||||||
accent_colour: 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):
|
def __init__(self, *, data: MemberWithUserPayload, guild: Guild, state: ConnectionState):
|
||||||
self._state: ConnectionState = state
|
self._state: ConnectionState = state
|
||||||
@ -357,6 +360,7 @@ class Member(discord.abc.Messageable, _UserTag):
|
|||||||
self._avatar: Optional[str] = data.get('avatar')
|
self._avatar: Optional[str] = data.get('avatar')
|
||||||
self._permissions: Optional[int]
|
self._permissions: Optional[int]
|
||||||
self._flags: int = data['flags']
|
self._flags: int = data['flags']
|
||||||
|
self._avatar_decoration_data: Optional[AvatarDecorationData] = data.get('avatar_decoration_data')
|
||||||
try:
|
try:
|
||||||
self._permissions = int(data['permissions'])
|
self._permissions = int(data['permissions'])
|
||||||
except KeyError:
|
except KeyError:
|
||||||
@ -425,6 +429,7 @@ class Member(discord.abc.Messageable, _UserTag):
|
|||||||
self._permissions = member._permissions
|
self._permissions = member._permissions
|
||||||
self._state = member._state
|
self._state = member._state
|
||||||
self._avatar = member._avatar
|
self._avatar = member._avatar
|
||||||
|
self._avatar_decoration_data = member._avatar_decoration_data
|
||||||
|
|
||||||
# Reference will not be copied unless necessary by PRESENCE_UPDATE
|
# Reference will not be copied unless necessary by PRESENCE_UPDATE
|
||||||
# See below
|
# See below
|
||||||
@ -453,6 +458,7 @@ class Member(discord.abc.Messageable, _UserTag):
|
|||||||
self._roles = utils.SnowflakeList(map(int, data['roles']))
|
self._roles = utils.SnowflakeList(map(int, data['roles']))
|
||||||
self._avatar = data.get('avatar')
|
self._avatar = data.get('avatar')
|
||||||
self._flags = data.get('flags', 0)
|
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]]:
|
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'])
|
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]]:
|
def _update_inner_user(self, user: UserPayload) -> Optional[Tuple[User, User]]:
|
||||||
u = self._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
|
# These keys seem to always be available
|
||||||
modified = (
|
modified = (
|
||||||
user['username'],
|
user['username'],
|
||||||
@ -472,10 +487,18 @@ class Member(discord.abc.Messageable, _UserTag):
|
|||||||
user['avatar'],
|
user['avatar'],
|
||||||
user.get('global_name'),
|
user.get('global_name'),
|
||||||
user.get('public_flags', 0),
|
user.get('public_flags', 0),
|
||||||
|
decoration_payload['sku_id'] if decoration_payload is not None else None,
|
||||||
)
|
)
|
||||||
if original != modified:
|
if original != modified:
|
||||||
to_return = User._copy(self._user)
|
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
|
# Signal to dispatch on_user_update
|
||||||
return to_return, u
|
return to_return, u
|
||||||
|
|
||||||
|
@ -41,7 +41,7 @@ from .message import Message
|
|||||||
from .sticker import GuildSticker
|
from .sticker import GuildSticker
|
||||||
from .appinfo import GatewayAppInfo, PartialAppInfo
|
from .appinfo import GatewayAppInfo, PartialAppInfo
|
||||||
from .guild import Guild, UnavailableGuild
|
from .guild import Guild, UnavailableGuild
|
||||||
from .user import User
|
from .user import User, AvatarDecorationData
|
||||||
from .threads import Thread, ThreadMember
|
from .threads import Thread, ThreadMember
|
||||||
from .scheduled_event import GuildScheduledEvent
|
from .scheduled_event import GuildScheduledEvent
|
||||||
from .audit_log import AuditLogEntry
|
from .audit_log import AuditLogEntry
|
||||||
@ -228,6 +228,7 @@ class GuildMemberUpdateEvent(TypedDict):
|
|||||||
mute: NotRequired[bool]
|
mute: NotRequired[bool]
|
||||||
pending: NotRequired[bool]
|
pending: NotRequired[bool]
|
||||||
communication_disabled_until: NotRequired[str]
|
communication_disabled_until: NotRequired[str]
|
||||||
|
avatar_decoration_data: NotRequired[AvatarDecorationData]
|
||||||
|
|
||||||
|
|
||||||
class GuildEmojisUpdateEvent(TypedDict):
|
class GuildEmojisUpdateEvent(TypedDict):
|
||||||
|
@ -24,7 +24,8 @@ DEALINGS IN THE SOFTWARE.
|
|||||||
|
|
||||||
from typing import Optional, TypedDict
|
from typing import Optional, TypedDict
|
||||||
from .snowflake import SnowflakeList
|
from .snowflake import SnowflakeList
|
||||||
from .user import User
|
from .user import User, AvatarDecorationData
|
||||||
|
from typing_extensions import NotRequired
|
||||||
|
|
||||||
|
|
||||||
class Nickname(TypedDict):
|
class Nickname(TypedDict):
|
||||||
@ -47,6 +48,7 @@ class Member(PartialMember, total=False):
|
|||||||
pending: bool
|
pending: bool
|
||||||
permissions: str
|
permissions: str
|
||||||
communication_disabled_until: str
|
communication_disabled_until: str
|
||||||
|
avatar_decoration_data: NotRequired[AvatarDecorationData]
|
||||||
|
|
||||||
|
|
||||||
class _OptionalMemberWithUser(PartialMember, total=False):
|
class _OptionalMemberWithUser(PartialMember, total=False):
|
||||||
@ -56,6 +58,7 @@ class _OptionalMemberWithUser(PartialMember, total=False):
|
|||||||
pending: bool
|
pending: bool
|
||||||
permissions: str
|
permissions: str
|
||||||
communication_disabled_until: str
|
communication_disabled_until: str
|
||||||
|
avatar_decoration_data: NotRequired[AvatarDecorationData]
|
||||||
|
|
||||||
|
|
||||||
class MemberWithUser(_OptionalMemberWithUser):
|
class MemberWithUser(_OptionalMemberWithUser):
|
||||||
|
@ -24,6 +24,12 @@ DEALINGS IN THE SOFTWARE.
|
|||||||
|
|
||||||
from .snowflake import Snowflake
|
from .snowflake import Snowflake
|
||||||
from typing import Literal, Optional, TypedDict
|
from typing import Literal, Optional, TypedDict
|
||||||
|
from typing_extensions import NotRequired
|
||||||
|
|
||||||
|
|
||||||
|
class AvatarDecorationData(TypedDict):
|
||||||
|
asset: str
|
||||||
|
sku_id: Snowflake
|
||||||
|
|
||||||
|
|
||||||
class PartialUser(TypedDict):
|
class PartialUser(TypedDict):
|
||||||
@ -32,6 +38,7 @@ class PartialUser(TypedDict):
|
|||||||
discriminator: str
|
discriminator: str
|
||||||
avatar: Optional[str]
|
avatar: Optional[str]
|
||||||
global_name: Optional[str]
|
global_name: Optional[str]
|
||||||
|
avatar_decoration_data: NotRequired[AvatarDecorationData]
|
||||||
|
|
||||||
|
|
||||||
PremiumType = Literal[0, 1, 2, 3]
|
PremiumType = Literal[0, 1, 2, 3]
|
||||||
|
@ -31,7 +31,7 @@ from .asset import Asset
|
|||||||
from .colour import Colour
|
from .colour import Colour
|
||||||
from .enums import DefaultAvatar
|
from .enums import DefaultAvatar
|
||||||
from .flags import PublicUserFlags
|
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:
|
if TYPE_CHECKING:
|
||||||
from typing_extensions import Self
|
from typing_extensions import Self
|
||||||
@ -43,10 +43,7 @@ if TYPE_CHECKING:
|
|||||||
from .message import Message
|
from .message import Message
|
||||||
from .state import ConnectionState
|
from .state import ConnectionState
|
||||||
from .types.channel import DMChannel as DMChannelPayload
|
from .types.channel import DMChannel as DMChannelPayload
|
||||||
from .types.user import (
|
from .types.user import PartialUser as PartialUserPayload, User as UserPayload, AvatarDecorationData
|
||||||
PartialUser as PartialUserPayload,
|
|
||||||
User as UserPayload,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
__all__ = (
|
__all__ = (
|
||||||
@ -73,6 +70,7 @@ class BaseUser(_UserTag):
|
|||||||
'system',
|
'system',
|
||||||
'_public_flags',
|
'_public_flags',
|
||||||
'_state',
|
'_state',
|
||||||
|
'_avatar_decoration_data',
|
||||||
)
|
)
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
@ -87,6 +85,7 @@ class BaseUser(_UserTag):
|
|||||||
_banner: Optional[str]
|
_banner: Optional[str]
|
||||||
_accent_colour: Optional[int]
|
_accent_colour: Optional[int]
|
||||||
_public_flags: int
|
_public_flags: int
|
||||||
|
_avatar_decoration_data: Optional[AvatarDecorationData]
|
||||||
|
|
||||||
def __init__(self, *, state: ConnectionState, data: Union[UserPayload, PartialUserPayload]) -> None:
|
def __init__(self, *, state: ConnectionState, data: Union[UserPayload, PartialUserPayload]) -> None:
|
||||||
self._state = state
|
self._state = state
|
||||||
@ -123,6 +122,7 @@ class BaseUser(_UserTag):
|
|||||||
self._public_flags = data.get('public_flags', 0)
|
self._public_flags = data.get('public_flags', 0)
|
||||||
self.bot = data.get('bot', False)
|
self.bot = data.get('bot', False)
|
||||||
self.system = data.get('system', False)
|
self.system = data.get('system', False)
|
||||||
|
self._avatar_decoration_data = data.get('avatar_decoration_data')
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _copy(cls, user: Self) -> Self:
|
def _copy(cls, user: Self) -> Self:
|
||||||
@ -138,6 +138,7 @@ class BaseUser(_UserTag):
|
|||||||
self.bot = user.bot
|
self.bot = user.bot
|
||||||
self._state = user._state
|
self._state = user._state
|
||||||
self._public_flags = user._public_flags
|
self._public_flags = user._public_flags
|
||||||
|
self._avatar_decoration_data = user._avatar_decoration_data
|
||||||
|
|
||||||
return self
|
return self
|
||||||
|
|
||||||
@ -187,6 +188,30 @@ class BaseUser(_UserTag):
|
|||||||
"""
|
"""
|
||||||
return self.avatar or self.default_avatar
|
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
|
@property
|
||||||
def banner(self) -> Optional[Asset]:
|
def banner(self) -> Optional[Asset]:
|
||||||
"""Optional[:class:`Asset`]: Returns the user's banner asset, if available.
|
"""Optional[:class:`Asset`]: Returns the user's banner asset, if available.
|
||||||
|
@ -1305,9 +1305,10 @@ class Webhook(BaseWebhook):
|
|||||||
'user': {
|
'user': {
|
||||||
'username': user.name,
|
'username': user.name,
|
||||||
'discriminator': user.discriminator,
|
'discriminator': user.discriminator,
|
||||||
'global_name': user.global_name,
|
|
||||||
'id': user.id,
|
'id': user.id,
|
||||||
'avatar': user._avatar,
|
'avatar': user._avatar,
|
||||||
|
'avatar_decoration_data': user._avatar_decoration_data,
|
||||||
|
'global_name': user.global_name,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user