diff --git a/discord/member.py b/discord/member.py index 07082de55..609bdce3b 100644 --- a/discord/member.py +++ b/discord/member.py @@ -77,6 +77,7 @@ if TYPE_CHECKING: from .user import Note from .relationship import Relationship from .calls import PrivateCall + from .enums import PremiumType VocalGuildChannel = Union[VoiceChannel, StageChannel] ConnectableChannel = Union[VocalGuildChannel, DMChannel, GroupChannel] @@ -302,6 +303,7 @@ class Member(discord.abc.Messageable, discord.abc.Connectable, _UserTag): remove_friend: Callable[[], Awaitable[None]] fetch_mutual_friends: Callable[[], Awaitable[List[User]]] public_flags: PublicUserFlags + premium_type: Optional[PremiumType] banner: Optional[Asset] accent_color: Optional[Colour] accent_colour: Optional[Colour] diff --git a/discord/profile.py b/discord/profile.py index b714b3132..e0ca090c4 100644 --- a/discord/profile.py +++ b/discord/profile.py @@ -67,6 +67,7 @@ class Profile: def __init__(self, **kwargs) -> None: data: ProfilePayload = kwargs.pop('data') user = data['user'] + profile = data.get('user_profile') mutual_friends: List[PartialUserPayload] = kwargs.pop('mutual_friends', None) member = data.get('guild_member') @@ -91,10 +92,10 @@ class Profile: self.guild_premium_since = guild_premium_since self.premium_type: Optional[PremiumType] = ( - try_enum(PremiumType, data['premium_type']) if user.get('premium_type') else None + try_enum(PremiumType, data.get('premium_type') or 0) if profile else None ) - self.premium_since: Optional[datetime] = utils.parse_time(data['premium_since']) - self.premium_guild_since: Optional[datetime] = utils.parse_time(data['premium_guild_since']) + self.premium_since: Optional[datetime] = utils.parse_time(data.get('premium_since')) + self.premium_guild_since: Optional[datetime] = utils.parse_time(data.get('premium_guild_since')) self.connections: List[PartialConnection] = [PartialConnection(d) for d in data['connected_accounts']] self.badges: List[ProfileBadge] = [ @@ -351,6 +352,10 @@ class UserProfile(Profile, User): Returns the user's name with discriminator. + .. note:: + + Information may be missing or inaccurate if the user has blocked the client user. + .. versionadded:: 2.0 Attributes @@ -360,7 +365,11 @@ class UserProfile(Profile, User): bio: Optional[:class:`str`] The user's "about me" field. Could be ``None``. premium_type: Optional[:class:`PremiumType`] - Specifies the type of premium a user has (i.e. Nitro, Nitro Classic, or Nitro Basic). Could be None if the user is not premium. + Specifies the type of premium a user has (i.e. Nitro, Nitro Classic, or Nitro Basic). + + .. versionchanged:: 2.1 + + This is now :attr:`PremiumType.none` instead of ``None`` if the user is not premium. premium_since: Optional[:class:`datetime.datetime`] An aware datetime object that specifies how long a user has been premium (had Nitro). ``None`` if the user is not a premium user. @@ -430,6 +439,10 @@ class MemberProfile(Profile, Member): Returns the member's name with the discriminator. + .. note:: + + Information may be missing or inaccurate if the user has blocked the client user. + .. versionadded:: 2.0 Attributes @@ -448,7 +461,11 @@ class MemberProfile(Profile, Member): This is renamed from :attr:`Member.premium_since` because of name collisions. premium_type: Optional[:class:`PremiumType`] - Specifies the type of premium a user has (i.e. Nitro, Nitro Classic, or Nitro Basic). Could be ``None`` if the user is not premium. + Specifies the type of premium a user has (i.e. Nitro, Nitro Classic, or Nitro Basic). + + .. versionchanged:: 2.1 + + This is now :attr:`PremiumType.none` instead of ``None`` if the user is not premium. premium_since: Optional[:class:`datetime.datetime`] An aware datetime object that specifies how long a user has been premium (had Nitro). ``None`` if the user is not a premium user. diff --git a/discord/types/user.py b/discord/types/user.py index b692d659d..7999aab7f 100644 --- a/discord/types/user.py +++ b/discord/types/user.py @@ -41,6 +41,7 @@ class PartialUser(TypedDict): bot: NotRequired[bool] system: NotRequired[bool] global_name: Optional[str] + premium_type: NotRequired[PremiumType] ConnectionType = Literal[ diff --git a/discord/user.py b/discord/user.py index cf09eefd9..c8cb87151 100644 --- a/discord/user.py +++ b/discord/user.py @@ -250,6 +250,7 @@ class BaseUser(_UserTag): 'bot', 'system', '_public_flags', + 'premium_type', '_cs_note', '_state', ) @@ -261,6 +262,7 @@ class BaseUser(_UserTag): global_name: Optional[str] bot: bool system: bool + premium_type: Optional[PremiumType] _state: ConnectionState _avatar: Optional[str] _avatar_decoration: Optional[str] @@ -302,6 +304,7 @@ class BaseUser(_UserTag): self._banner = data.get('banner', None) self._accent_colour = data.get('accent_color', None) self._public_flags = data.get('public_flags', 0) + self.premium_type = try_enum(PremiumType, data['premium_type'] or 0) if 'premium_type' in data else None self.bot = data.get('bot', False) self.system = data.get('system', False) @@ -349,6 +352,9 @@ class BaseUser(_UserTag): 'banner': self._banner, 'accent_color': self._accent_colour, } + if self.premium_type is not None: + user['premium_type'] = self.premium_type.value + return user @property @@ -392,6 +398,11 @@ class BaseUser(_UserTag): """ return self.avatar or self.default_avatar + @property + def premium(self) -> bool: + """Indicates if the user is a premium user (i.e. has Discord Nitro).""" + return bool(self.premium_type.value) if self.premium_type else False + @property def avatar_decoration(self) -> Optional[Asset]: """Optional[:class:`Asset`]: Returns an :class:`Asset` for the avatar decoration the user has. @@ -701,8 +712,12 @@ class ClientUser(BaseUser): This now returns a :class:`str` instead of an :class:`int` to match the API. mfa_enabled: :class:`bool` Specifies if the user has MFA turned on and working. - premium_type: Optional[:class:`PremiumType`] - Specifies the type of premium a user has (i.e. Nitro, Nitro Classic, or Nitro Basic). Could be None if the user is not premium. + premium_type: :class:`PremiumType` + Specifies the type of premium a user has (i.e. Nitro, Nitro Classic, or Nitro Basic). + + .. versionchanged:: 2.1 + + This is now :attr:`PremiumType.none` instead of ``None`` if the user is not premium. note: :class:`Note` The user's note. Not pre-fetched. @@ -730,7 +745,6 @@ class ClientUser(BaseUser): 'mfa_enabled', 'email', 'phone', - 'premium_type', 'note', 'bio', 'nsfw_allowed', @@ -747,7 +761,7 @@ class ClientUser(BaseUser): _locale: str _flags: int mfa_enabled: bool - premium_type: Optional[PremiumType] + premium_type: PremiumType bio: Optional[str] nsfw_allowed: Optional[bool] @@ -772,7 +786,7 @@ class ClientUser(BaseUser): self._purchased_flags = data.get('purchased_flags', 0) self._premium_usage_flags = data.get('premium_usage_flags', 0) self.mfa_enabled = data.get('mfa_enabled', False) - self.premium_type = try_enum(PremiumType, data.get('premium_type')) if data.get('premium_type') else None + self.premium_type = try_enum(PremiumType, data.get('premium_type') or 0) self.bio = data.get('bio') or None self.nsfw_allowed = data.get('nsfw_allowed') self.desktop: bool = data.get('desktop', False) @@ -787,11 +801,6 @@ class ClientUser(BaseUser): """:class:`Locale`: The IETF language tag used to identify the language the user is using.""" return self._state.settings.locale if self._state.settings else try_enum(Locale, self._locale) - @property - def premium(self) -> bool: - """Indicates if the user is a premium user (i.e. has Discord Nitro).""" - return self.premium_type is not None - @property def flags(self) -> PrivateUserFlags: """:class:`PrivateUserFlags`: Returns the user's flags (including private). @@ -1051,6 +1060,14 @@ class User(BaseUser, discord.abc.Connectable, discord.abc.Messageable): Specifies if the user is a bot account. system: :class:`bool` Specifies if the user is a system user (i.e. represents Discord officially). + premium_type: Optional[:class:`PremiumType`] + Specifies the type of premium a user has (i.e. Nitro, Nitro Classic, or Nitro Basic). + + .. note:: + + This information is only available in certain contexts. + + .. versionadded:: 2.1 """ __slots__ = ('__weakref__',) diff --git a/docs/api.rst b/docs/api.rst index 82d09b5f9..cb542905f 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -3872,6 +3872,12 @@ of :class:`enum.Enum`. Represents the user's Discord Nitro subscription type. + .. attribute:: none + + The user does not have a Discord Nitro subscription. + + .. versionadded:: 2.0 + .. attribute:: nitro Represents the new, full Discord Nitro.