Browse Source

Finish profile and application install param implementations, fix miscallenous related things

pull/10109/head
dolfies 3 years ago
parent
commit
8a315c48e6
  1. 124
      discord/appinfo.py
  2. 11
      discord/asset.py
  3. 34
      discord/member.py
  4. 148
      discord/profile.py
  5. 81
      discord/user.py
  6. 9
      docs/api.rst

124
discord/appinfo.py

@ -48,6 +48,7 @@ __all__ = (
'ApplicationBot', 'ApplicationBot',
'ApplicationCompany', 'ApplicationCompany',
'ApplicationExecutable', 'ApplicationExecutable',
'ApplicationInstallParams',
'Application', 'Application',
'PartialApplication', 'PartialApplication',
'InteractionApplication', 'InteractionApplication',
@ -61,6 +62,24 @@ class ApplicationBot(User):
.. versionadded:: 2.0 .. versionadded:: 2.0
.. container:: operations
.. describe:: x == y
Checks if two bots are equal.
.. describe:: x != y
Checks if two bots are not equal.
.. describe:: hash(x)
Return the bot's hash.
.. describe:: str(x)
Returns the bot's name with discriminator.
Attributes Attributes
----------- -----------
application: :class:`Application` application: :class:`Application`
@ -188,6 +207,12 @@ class ApplicationExecutable:
.. versionadded:: 2.0 .. versionadded:: 2.0
.. container:: operations
.. describe:: str(x)
Returns the executable's name.
Attributes Attributes
----------- -----------
name: :class:`str` name: :class:`str`
@ -213,6 +238,53 @@ class ApplicationExecutable:
self.launcher: bool = data['is_launcher'] self.launcher: bool = data['is_launcher']
self.application = application self.application = application
def __repr__(self) -> str:
return f'<ApplicationExecutable name={self.name!r} os={self.os!r} launcher={self.launcher!r}>'
def __str__(self) -> str:
return self.name
class ApplicationInstallParams:
"""Represents an application's authorization parameters.
.. versionadded:: 2.0
.. container:: operations
.. describe:: str(x)
Returns the authorization URL.
Attributes
----------
id: :class:`int`
The application's ID.
scopes: List[:class:`str`]
The list of `OAuth2 scopes <https://discord.com/developers/docs/topics/oauth2#shared-resources-oauth2-scopes>`_
to add the application with.
permissions: :class:`Permissions`
The permissions to grant to the added bot.
"""
__slots__ = ('id', 'scopes', 'permissions')
def __init__(self, id: int, data: dict):
self.id: int = id
self.scopes: List[str] = data.get('scopes', [])
self.permissions: Permissions = Permissions(int(data.get('permissions', 0)))
def __repr__(self) -> str:
return f'<ApplicationInstallParams id={self.id} scopes={self.scopes!r} permissions={self.permissions!r}>'
def __str__(self) -> str:
return self.url
@property
def url(self) -> str:
""":class:`str`: The URL to add the application with the parameters."""
return utils.oauth_url(self.id, permissions=self.permissions, scopes=self.scopes)
class PartialApplication(Hashable): class PartialApplication(Hashable):
"""Represents a partial Application. """Represents a partial Application.
@ -280,6 +352,10 @@ class PartialApplication(Hashable):
A list of publishers that published the application. Only available for specific applications. A list of publishers that published the application. Only available for specific applications.
executables: List[:class:`ApplicationExecutable`] executables: List[:class:`ApplicationExecutable`]
A list of executables that are the application's. Only available for specific applications. A list of executables that are the application's. Only available for specific applications.
custom_install_url: Optional[:class:`str`]
The custom URL to use for authorizing the application, if specified.
install_params: Optional[:class:`ApplicationInstallParams`]
The parameters to use for authorizing the application, if specified.
""" """
__slots__ = ( __slots__ = (
@ -302,13 +378,14 @@ class PartialApplication(Hashable):
'premium_tier_level', 'premium_tier_level',
'tags', 'tags',
'max_participants', 'max_participants',
'install_url',
'overlay', 'overlay',
'overlay_compatibility_hook', 'overlay_compatibility_hook',
'aliases', 'aliases',
'developers', 'developers',
'publishers', 'publishers',
'executables', 'executables',
'custom_install_url',
'install_params',
) )
def __init__(self, *, state: ConnectionState, data: PartialAppInfoPayload): def __init__(self, *, state: ConnectionState, data: PartialAppInfoPayload):
@ -351,15 +428,10 @@ class PartialApplication(Hashable):
self.overlay: bool = data.get('overlay', False) self.overlay: bool = data.get('overlay', False)
self.overlay_compatibility_hook: bool = data.get('overlay_compatibility_hook', False) self.overlay_compatibility_hook: bool = data.get('overlay_compatibility_hook', False)
install_params = data.get('install_params', {}) params = data.get('install_params')
self.install_url = ( self.custom_install_url: Optional[str] = data.get('custom_install_url')
data.get('custom_install_url') self.install_params: Optional[ApplicationInstallParams] = (
if not install_params ApplicationInstallParams(self.id, params) if params else None
else utils.oauth_url(
self.id,
permissions=Permissions(int(install_params.get('permissions', 0))),
scopes=install_params.get('scopes', utils.MISSING),
)
) )
self.public: bool = data.get( self.public: bool = data.get(
@ -401,12 +473,35 @@ class PartialApplication(Hashable):
""":class:`ApplicationFlags`: The flags of this application.""" """:class:`ApplicationFlags`: The flags of this application."""
return ApplicationFlags._from_value(self._flags) return ApplicationFlags._from_value(self._flags)
@property
def install_url(self) -> Optional[str]:
""":class:`str`: The URL to install the application."""
return self.custom_install_url or self.install_params.url if self.install_params else None
class Application(PartialApplication): class Application(PartialApplication):
"""Represents application info for an application you own. """Represents application info for an application you own.
.. versionadded:: 2.0 .. versionadded:: 2.0
.. container:: operations
.. describe:: x == y
Checks if two applications are equal.
.. describe:: x != y
Checks if two applications are not equal.
.. describe:: hash(x)
Return the application's hash.
.. describe:: str(x)
Returns the application's name.
Attributes Attributes
------------- -------------
owner: :class:`abc.User` owner: :class:`abc.User`
@ -416,12 +511,9 @@ class Application(PartialApplication):
bot: Optional[:class:`ApplicationBot`] bot: Optional[:class:`ApplicationBot`]
The bot attached to the application, if any. The bot attached to the application, if any.
guild_id: Optional[:class:`int`] guild_id: Optional[:class:`int`]
If this application is a game sold on Discord, The guild ID this application is linked to, if any.
this field will be the guild to which it has been linked to.
primary_sku_id: Optional[:class:`int`] primary_sku_id: Optional[:class:`int`]
If this application is a game sold on Discord, The application's primary SKU ID, if any.
this field will be the id of the "Game SKU" that is created,
if it exists.
slug: Optional[:class:`str`] slug: Optional[:class:`str`]
If this application is a game sold on Discord, If this application is a game sold on Discord,
this field will be the URL slug that links to the store page. this field will be the URL slug that links to the store page.
@ -678,6 +770,8 @@ class InteractionApplication(Hashable):
The application description. The application description.
type: Optional[:class:`ApplicationType`] type: Optional[:class:`ApplicationType`]
The type of application. The type of application.
primary_sku_id: Optional[:class:`int`]
The application's primary SKU ID, if any.
""" """
__slots__ = ( __slots__ = (

11
discord/asset.py

@ -235,6 +235,17 @@ class Asset(AssetMixin):
animated=animated, animated=animated,
) )
@classmethod
def _from_guild_banner(cls, state: _State, guild_id: int, member_id: int, avatar: str) -> Self:
animated = avatar.startswith('a_')
format = 'gif' if animated else 'png'
return cls(
state,
url=f"{cls.BASE}/guilds/{guild_id}/users/{member_id}/banners/{avatar}.{format}?size=512",
key=avatar,
animated=animated,
)
@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(

34
discord/member.py

@ -215,9 +215,9 @@ class _ClientStatus:
def flatten_user(cls: Any) -> Type[Member]: def flatten_user(cls: Any) -> Type[Member]:
for attr, value in itertools.chain(BaseUser.__dict__.items(), User.__dict__.items()): for attr, value in itertools.chain(BaseUser.__dict__.items(), User.__dict__.items()):
# Ignore private/special methods (or not) # Ignore private/special methods
# if attr.startswith('_'): if attr.startswith('_'):
# continue continue
# Don't override what we already have # Don't override what we already have
if attr in cls.__dict__: if attr in cls.__dict__:
@ -461,6 +461,16 @@ class Member(discord.abc.Messageable, discord.abc.Connectable, _UserTag):
# Signal to dispatch user_update # Signal to dispatch user_update
return to_return, u return to_return, u
def _get_voice_client_key(self) -> Tuple[int, str]:
return self._state.self_id, 'self_id' # type: ignore # self_id is always set at this point
def _get_voice_state_pair(self) -> Tuple[int, int]:
return self._state.self_id, self.dm_channel.id # type: ignore # self_id is always set at this point
async def _get_channel(self) -> DMChannel:
ch = await self.create_dm()
return ch
@property @property
def status(self) -> Status: def status(self) -> Status:
""":class:`Status`: The member's overall status. If the value is unknown, then it will be a :class:`str` instead.""" """:class:`Status`: The member's overall status. If the value is unknown, then it will be a :class:`str` instead."""
@ -733,6 +743,8 @@ class Member(discord.abc.Messageable, discord.abc.Connectable, _UserTag):
voice_channel: Optional[VocalGuildChannel] = MISSING, voice_channel: Optional[VocalGuildChannel] = MISSING,
timed_out_until: Optional[datetime.datetime] = MISSING, timed_out_until: Optional[datetime.datetime] = MISSING,
avatar: Optional[bytes] = MISSING, avatar: Optional[bytes] = MISSING,
banner: Optional[bytes] = MISSING,
bio: Optional[str] = MISSING,
reason: Optional[str] = None, reason: Optional[str] = None,
) -> Optional[Member]: ) -> Optional[Member]:
"""|coro| """|coro|
@ -798,6 +810,16 @@ class Member(discord.abc.Messageable, discord.abc.Connectable, _UserTag):
The member's new guild avatar. Pass ``None`` to remove the avatar. The member's new guild avatar. Pass ``None`` to remove the avatar.
You can only change your own guild avatar. You can only change your own guild avatar.
.. versionadded:: 2.0
banner: Optional[:class:`bytes`]
The member's new guild banner. Pass ``None`` to remove the banner.
You can only change your own guild banner.
.. versionadded:: 2.0
bio: Optional[:class:`str`]
The member's new guild "about me". Pass ``None`` to remove the bio.
You can only change your own guild bio.
.. versionadded:: 2.0 .. versionadded:: 2.0
reason: Optional[:class:`str`] reason: Optional[:class:`str`]
The reason for editing this member. Shows up on the audit log. The reason for editing this member. Shows up on the audit log.
@ -829,6 +851,12 @@ class Member(discord.abc.Messageable, discord.abc.Connectable, _UserTag):
if avatar is not MISSING: if avatar is not MISSING:
payload['avatar'] = utils._bytes_to_base64_data(avatar) if avatar is not None else None payload['avatar'] = utils._bytes_to_base64_data(avatar) if avatar is not None else None
if banner is not MISSING:
payload['banner'] = utils._bytes_to_base64_data(banner) if banner is not None else None
if bio is not MISSING:
payload['bio'] = bio or ''
if me and payload: if me and payload:
data = await http.edit_me(self.guild.id, **payload) data = await http.edit_me(self.guild.id, **payload)
payload = {} payload = {}

148
discord/profile.py

@ -24,14 +24,18 @@ DEALINGS IN THE SOFTWARE.
from __future__ import annotations from __future__ import annotations
from typing import List, Optional, TYPE_CHECKING from typing import TYPE_CHECKING, List, Optional
from . import utils
from .appinfo import ApplicationInstallParams
from .asset import Asset
from .connections import PartialConnection from .connections import PartialConnection
from .enums import PremiumType, try_enum
from .flags import ApplicationFlags
from .member import Member from .member import Member
from .mixins import Hashable
from .object import Object from .object import Object
from .permissions import Permissions
from .user import Note, User from .user import Note, User
from . import utils
if TYPE_CHECKING: if TYPE_CHECKING:
from datetime import datetime from datetime import datetime
@ -40,14 +44,13 @@ if TYPE_CHECKING:
from .state import ConnectionState from .state import ConnectionState
__all__ = ( __all__ = (
'ApplicationProfile',
'UserProfile', 'UserProfile',
'MemberProfile', 'MemberProfile',
) )
class Profile: class Profile:
"""Represents a Discord profile."""
if TYPE_CHECKING: if TYPE_CHECKING:
id: int id: int
application_id: Optional[int] application_id: Optional[int]
@ -65,10 +68,17 @@ class Profile:
super().__init__(**kwargs) super().__init__(**kwargs)
self._flags: int = user.pop('flags', 0) self.bio: Optional[str] = user.pop('bio', None) or None
self.bio: Optional[str] = user.pop('bio') or None self.note: Note = Note(kwargs['state'], self.id, user=getattr(self, '_user', self)) # type: ignore
self.note: Note = Note(kwargs['state'], self.id, user=self)
# We need to do a bit of a hack here because premium_since is massively overloaded
guild_premium_since = getattr(self, 'premium_since', utils.MISSING)
if guild_premium_since is not utils.MISSING:
self.guild_premium_since = guild_premium_since
self.premium_type: Optional[PremiumType] = (
try_enum(PremiumType, user.pop('premium_type')) if user.get('premium_type') else None
)
self.premium_since: Optional[datetime] = utils.parse_time(data['premium_since']) self.premium_since: Optional[datetime] = utils.parse_time(data['premium_since'])
self.boosting_since: Optional[datetime] = utils.parse_time(data['premium_guild_since']) self.boosting_since: Optional[datetime] = utils.parse_time(data['premium_guild_since'])
self.connections: List[PartialConnection] = [PartialConnection(d) for d in data['connected_accounts']] self.connections: List[PartialConnection] = [PartialConnection(d) for d in data['connected_accounts']]
@ -77,9 +87,7 @@ class Profile:
self.mutual_friends: Optional[List[User]] = self._parse_mutual_friends(data.get('mutual_friends')) self.mutual_friends: Optional[List[User]] = self._parse_mutual_friends(data.get('mutual_friends'))
application = data.get('application', {}) application = data.get('application', {})
install_params = application.get('install_params', {}) self.application: Optional[ApplicationProfile] = ApplicationProfile(data=application) if application else None
self.application_id = app_id = utils._get_as_snowflake(application, 'id')
self.install_url = application.get('custom_install_url') if not install_params else utils.oauth_url(app_id, permissions=Permissions(int(install_params.get('permissions', 0))), scopes=install_params.get('scopes', utils.MISSING)) # type: ignore # app_id is always present here
def _parse_mutual_guilds(self, mutual_guilds) -> Optional[List[Guild]]: def _parse_mutual_guilds(self, mutual_guilds) -> Optional[List[Guild]]:
if mutual_guilds is None: if mutual_guilds is None:
@ -90,7 +98,7 @@ class Profile:
def get_guild(guild): def get_guild(guild):
return state._get_guild(int(guild['id'])) or Object(id=int(guild['id'])) return state._get_guild(int(guild['id'])) or Object(id=int(guild['id']))
return list(filter(None, map(get_guild, mutual_guilds))) # type: ignore # Lying for better developer UX return list(map(get_guild, mutual_guilds)) # type: ignore # Lying for better developer UX
def _parse_mutual_friends(self, mutual_friends) -> Optional[List[User]]: def _parse_mutual_friends(self, mutual_friends) -> Optional[List[User]]:
if mutual_friends is None: if mutual_friends is None:
@ -105,17 +113,79 @@ class Profile:
return self.premium_since is not None return self.premium_since is not None
class ApplicationProfile(Hashable):
"""Represents a Discord application profile.
.. versionadded:: 2.0
.. container:: operations
.. describe:: x == y
Checks if two applications are equal.
.. describe:: x != y
Checks if two applications are not equal.
.. describe:: hash(x)
Return the applications's hash.
Attributes
------------
id: :class:`int`
The application's ID.
verified: :class:`bool`
Indicates if the application is verified.
popular_application_command_ids: List[:class:`int`]
A list of the IDs of the application's popular commands.
primary_sku_id: Optional[:class:`int`]
The application's primary SKU ID, if any.
custom_install_url: Optional[:class:`str`]
The custom URL to use for authorizing the application, if specified.
install_params: Optional[:class:`ApplicationInstallParams`]
The parameters to use for authorizing the application, if specified.
"""
def __init__(self, data: dict) -> None:
self.id: int = int(data['id'])
self.verified: bool = data.get('verified', False)
self.popular_application_command_ids: List[int] = [int(id) for id in data.get('popular_application_command_ids', [])]
self.primary_sku_id: Optional[int] = utils._get_as_snowflake(data, 'primary_sku_id')
self._flags: int = data.get('flags', 0)
params = data.get('install_params')
self.custom_install_url: Optional[str] = data.get('custom_install_url')
self.install_params: Optional[ApplicationInstallParams] = (
ApplicationInstallParams(self.id, params) if params else None
)
def __repr__(self) -> str:
return f'<ApplicationProfile id={self.id} verified={self.verified}>'
@property
def flags(self) -> ApplicationFlags:
""":class:`ApplicationFlags`: The flags of this application."""
return ApplicationFlags._from_value(self._flags)
@property
def install_url(self) -> Optional[str]:
""":class:`str`: The URL to install the application."""
return self.custom_install_url or self.install_params.url if self.install_params else None
class UserProfile(Profile, User): class UserProfile(Profile, User):
"""Represents a Discord user's profile. This is a :class:`User` with extended attributes. """Represents a Discord user's profile. This is a :class:`User` with extended attributes.
Attributes Attributes
----------- -----------
application_id: Optional[:class:`int`] application: Optional[:class:`ApplicationProfile`]
The ID of the application that this user is attached to, if applicable. The application profile of the user, if a bot.
install_url: Optional[:class:`str`]
The URL to invite the application to your guild with.
bio: Optional[:class:`str`] bio: Optional[:class:`str`]
The user's "about me" field. Could be ``None``. 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.
premium_since: Optional[:class:`datetime.datetime`] premium_since: Optional[:class:`datetime.datetime`]
An aware datetime object that specifies how long a user has been premium (had Nitro). An aware datetime object that specifies how long a user has been premium (had Nitro).
``None`` if the user is not a premium user. ``None`` if the user is not a premium user.
@ -142,17 +212,28 @@ class MemberProfile(Profile, Member):
Attributes Attributes
----------- -----------
application_id: Optional[:class:`int`] application: Optional[:class:`ApplicationProfile`]
The ID of the application that this user is attached to, if applicable. The application profile of the user, if a bot.
install_url: Optional[:class:`str`]
The URL to invite the application to your guild with.
bio: Optional[:class:`str`] bio: Optional[:class:`str`]
The user's "about me" field. Could be ``None``. The user's "about me" field. Could be ``None``.
guild_bio: Optional[:class:`str`]
The user's "about me" field for the guild. Could be ``None``.
guild_premium_since: Optional[:class:`datetime.datetime`]
An aware datetime object that specifies the date and time in UTC when the member used their
"Nitro boost" on the guild, if available. This could be ``None``.
.. note::
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.
premium_since: Optional[:class:`datetime.datetime`] premium_since: Optional[:class:`datetime.datetime`]
An aware datetime object that specifies how long a user has been premium (had Nitro). An aware datetime object that specifies how long a user has been premium (had Nitro).
``None`` if the user is not a premium user. ``None`` if the user is not a premium user.
.. note::
This is not the same as :attr:`Member.premium_since`. That is renamed to :attr:`guild_premium_since`
boosting_since: Optional[:class:`datetime.datetime`] boosting_since: Optional[:class:`datetime.datetime`]
An aware datetime object that specifies when a user first boosted a guild. An aware datetime object that specifies when a user first boosted any guild.
connections: Optional[List[:class:`PartialConnection`]] connections: Optional[List[:class:`PartialConnection`]]
The connected accounts that show up on the profile. The connected accounts that show up on the profile.
note: :class:`Note` note: :class:`Note`
@ -165,8 +246,33 @@ class MemberProfile(Profile, Member):
``None`` if you didn't fetch mutuals. ``None`` if you didn't fetch mutuals.
""" """
def __init__(self, *, state: ConnectionState, data: dict, guild: Guild):
super().__init__(state=state, guild=guild, data=data)
member = data['guild_member']
self._banner: Optional[str] = member.get('banner')
self.guild_bio: Optional[str] = member.get('bio') or None
def __repr__(self) -> str: def __repr__(self) -> str:
return ( return (
f'<MemberProfile id={self._user.id} name={self._user.name!r} discriminator={self._user.discriminator!r}' f'<MemberProfile id={self._user.id} name={self._user.name!r} discriminator={self._user.discriminator!r}'
f' bot={self._user.bot} nick={self.nick!r} premium={self.premium} guild={self.guild!r}>' f' bot={self._user.bot} nick={self.nick!r} premium={self.premium} guild={self.guild!r}>'
) )
@property
def display_banner(self) -> Optional[Asset]:
"""Optional[:class:`Asset`]: Returns the member's display banner.
For regular members this is just their banner (if available), but
if they have a guild specific banner then that
is returned instead.
"""
return self.guild_banner or self._user.banner
@property
def guild_banner(self) -> Optional[Asset]:
"""Optional[:class:`Asset`]: Returns an :class:`Asset` for the guild banner
the member has. If unavailable, ``None`` is returned.
"""
if self._banner is None:
return None
return Asset._from_guild_banner(self._state, self.guild.id, self.id, self._banner)

81
discord/user.py

@ -75,6 +75,8 @@ __all__ = (
class Note: class Note:
"""Represents a Discord note. """Represents a Discord note.
.. versionadded:: 2.0
.. container:: operations .. container:: operations
.. describe:: x == y .. describe:: x == y
@ -95,22 +97,26 @@ class Note:
.. describe:: len(x) .. describe:: len(x)
Returns the note's length. Returns the note's length.
Attributes
-----------
user_id: :class:`int`
The user ID the note is for.
""" """
__slots__ = ('_state', '_note', '_user_id', '_user') __slots__ = ('_state', '_note', 'user_id', '_user')
def __init__( def __init__(
self, state: ConnectionState, user_id: int, *, user: _Snowflake = MISSING, note: Optional[str] = MISSING self, state: ConnectionState, user_id: int, *, user: Optional[User] = None, note: Optional[str] = MISSING
) -> None: ) -> None:
self._state = state self._state = state
self._user_id = user_id self._note: Optional[str] = note
self._note = note self.user_id: int = user_id
if user is not MISSING: self._user: Optional[User] = user
self._user = user
@property @property
def note(self) -> Optional[str]: def note(self) -> Optional[str]:
"""Returns the note. """Optional[:class:`str`]: Returns the note.
There is an alias for this called :attr:`value`. There is an alias for this called :attr:`value`.
@ -125,7 +131,7 @@ class Note:
@property @property
def value(self) -> Optional[str]: def value(self) -> Optional[str]:
"""Returns the note. """Optional[:class:`str`]: Returns the note.
This is an alias of :attr:`note`. This is an alias of :attr:`note`.
@ -136,15 +142,10 @@ class Note:
""" """
return self.note return self.note
@cached_slot_property('_user') @property
def user(self) -> _Snowflake: def user(self) -> Optional[User]:
""":class:`~abc.Snowflake`: Returns the :class:`User` or :class:`Object` the note belongs to.""" """Optional[:class:`User`]: Returns the :class:`User` the note belongs to."""
user_id = self._user_id return self._user or self._state.get_user(self.user_id)
user = self._state.get_user(user_id)
if user is None:
user = Object(user_id)
return user
async def fetch(self) -> Optional[str]: async def fetch(self) -> Optional[str]:
"""|coro| """|coro|
@ -162,7 +163,7 @@ class Note:
The note or ``None`` if it doesn't exist. The note or ``None`` if it doesn't exist.
""" """
try: try:
data = await self._state.http.get_note(self.user.id) data = await self._state.http.get_note(self.user_id)
self._note = data['note'] self._note = data['note']
return data['note'] return data['note']
except NotFound: # 404 = no note except NotFound: # 404 = no note
@ -179,7 +180,7 @@ class Note:
HTTPException HTTPException
Changing the note failed. Changing the note failed.
""" """
await self._state.http.set_note(self._user_id, note=note) await self._state.http.set_note(self.user_id, note=note)
self._note = note self._note = note
async def delete(self) -> None: async def delete(self) -> None:
@ -206,32 +207,25 @@ class Note:
note = self._note note = self._note
if note is MISSING: if note is MISSING:
raise ClientException('Note is not fetched') raise ClientException('Note is not fetched')
elif note is None: return note or ''
return ''
else:
return note
def __bool__(self) -> bool: def __bool__(self) -> bool:
try: return bool(str(self))
return bool(self._note)
except TypeError:
return False
def __eq__(self, other: object) -> bool: def __eq__(self, other: object) -> bool:
return isinstance(other, Note) and self._note == other._note and self._user_id == other._user_id return isinstance(other, Note) and self._note == other._note and self.user_id == other.user_id
def __ne__(self, other: object) -> bool: def __ne__(self, other: object) -> bool:
if isinstance(other, Note): if isinstance(other, Note):
return self._note != other._note or self._user_id != other._user_id return self._note != other._note or self.user_id != other.user_id
return True return True
def __hash__(self) -> int: def __hash__(self) -> int:
return hash((self._note, self._user_id)) return hash((self._note, self.user_id))
def __len__(self) -> int: def __len__(self) -> int:
if note := self._note: note = str(self)
return len(note) return len(note) if note else 0
return 0
class _UserTag: class _UserTag:
@ -367,7 +361,6 @@ class BaseUser(_UserTag):
.. versionadded:: 2.0 .. versionadded:: 2.0
.. note:: .. note::
This information is only available via :meth:`Client.fetch_user`. This information is only available via :meth:`Client.fetch_user`.
""" """
@ -375,6 +368,16 @@ class BaseUser(_UserTag):
return None return None
return Asset._from_user_banner(self._state, self.id, self._banner) return Asset._from_user_banner(self._state, self.id, self._banner)
@property
def display_banner(self) -> Optional[Asset]:
"""Optional[:class:`Asset`]: Returns the user's banner asset, if available.
This is the same as :attr:`banner` and is here for compatibility.
.. versionadded:: 2.0
"""
return self.banner
@property @property
def accent_colour(self) -> Optional[Colour]: def accent_colour(self) -> Optional[Colour]:
"""Optional[:class:`Colour`]: Returns the user's accent colour, if applicable. """Optional[:class:`Colour`]: Returns the user's accent colour, if applicable.
@ -518,7 +521,7 @@ class ClientUser(BaseUser):
mfa_enabled: :class:`bool` mfa_enabled: :class:`bool`
Specifies if the user has MFA turned on and working. Specifies if the user has MFA turned on and working.
premium_type: Optional[:class:`PremiumType`] premium_type: Optional[:class:`PremiumType`]
Specifies the type of premium a user has (i.e. Nitro or Nitro Classic). 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). Could be None if the user is not premium.
note: :class:`Note` note: :class:`Note`
The user's note. Not pre-fetched. The user's note. Not pre-fetched.
@ -579,7 +582,7 @@ class ClientUser(BaseUser):
self._premium_usage_flags = data.get('premium_usage_flags', 0) self._premium_usage_flags = data.get('premium_usage_flags', 0)
self.mfa_enabled = data.get('mfa_enabled', False) self.mfa_enabled = data.get('mfa_enabled', False)
self.premium_type = try_enum(PremiumType, data['premium_type']) if 'premium_type' in data else None self.premium_type = try_enum(PremiumType, data['premium_type']) if 'premium_type' in data else None
self.bio = data.get('bio') self.bio = data.get('bio') or None
self.nsfw_allowed = data.get('nsfw_allowed') self.nsfw_allowed = data.get('nsfw_allowed')
def get_relationship(self, user_id: int) -> Optional[Relationship]: def get_relationship(self, user_id: int) -> Optional[Relationship]:
@ -721,8 +724,8 @@ class ClientUser(BaseUser):
accent_colour/_color: :class:`Colour` accent_colour/_color: :class:`Colour`
A :class:`Colour` object of the colour you want to set your profile to. A :class:`Colour` object of the colour you want to set your profile to.
bio: :class:`str` bio: :class:`str`
Your 'about me' section. Your "about me" section.
Could be ``None`` to represent no 'about me'. Could be ``None`` to represent no bio.
date_of_birth: :class:`datetime.datetime` date_of_birth: :class:`datetime.datetime`
Your date of birth. Can only ever be set once. Your date of birth. Can only ever be set once.
@ -743,7 +746,7 @@ class ClientUser(BaseUser):
""" """
args: Dict[str, Any] = {} args: Dict[str, Any] = {}
if any(x is not MISSING for x in ('new_password', 'email', 'username', 'discriminator')): if any(x is not MISSING for x in (new_password, email, username, discriminator)):
if password is MISSING: if password is MISSING:
raise ValueError('Password is required') raise ValueError('Password is required')
args['password'] = password args['password'] = password

9
docs/api.rst

@ -4266,6 +4266,11 @@ Application
.. autoclass:: PartialApplication() .. autoclass:: PartialApplication()
:members: :members:
.. attributetable:: InteractionApplication
.. autoclass:: InteractionApplication()
:members:
.. attributetable:: ApplicationCompany .. attributetable:: ApplicationCompany
.. autoclass:: ApplicationCompany() .. autoclass:: ApplicationCompany()
@ -4276,9 +4281,9 @@ Application
.. autoclass:: ApplicationExecutable() .. autoclass:: ApplicationExecutable()
:members: :members:
.. attributetable:: InteractionApplication .. attributetable:: ApplicationInstallParams
.. autoclass:: InteractionApplication() .. autoclass:: ApplicationInstallParams()
:members: :members:
Team Team

Loading…
Cancel
Save