diff --git a/discord/activity.py b/discord/activity.py index c692443f987a..58d43984c440 100644 --- a/discord/activity.py +++ b/discord/activity.py @@ -28,7 +28,7 @@ from typing import Any, Dict, List, Optional, TYPE_CHECKING, Union, overload from .asset import Asset -from .enums import ActivityType, try_enum +from .enums import ActivityType, try_enum, HangStatusType from .colour import Colour from .partial_emoji import PartialEmoji from .utils import _get_as_snowflake @@ -40,6 +40,7 @@ 'Game', 'Spotify', 'CustomActivity', + 'HangStatus', ) """If curious, this is the current schema for an activity. @@ -109,6 +110,7 @@ class BaseActivity: - :class:`Game` - :class:`Streaming` - :class:`CustomActivity` + - :class:`HangStatus` Note that although these types are considered user-settable by the library, Discord typically ignores certain combinations of activity depending on @@ -147,6 +149,7 @@ class Activity(BaseActivity): - :class:`Game` - :class:`Streaming` + - :class:`HangStatus` Attributes ------------ @@ -825,7 +828,107 @@ def __repr__(self) -> str: return f'' -ActivityTypes = Union[Activity, Game, CustomActivity, Streaming, Spotify] +class HangStatus(BaseActivity): + """A slimmed down version of :class:`Activity` that represents a Discord hang status. + + This is typically displayed via **Right now, I'm -** on the official Discord client. + + .. container:: operations + + .. describe:: x == y + + Checks if two hang statuses are equal. + + .. describe:: x != y + + Checks if two hang statuses are not equal. + + .. describe:: hash(x) + + Returns the hang status' hash. + + .. describe:: str(x) + + Returns the hang status' name. + + .. versionadded: 2.5 + + Attributes + ----------- + state: :class:`HangStatusType` + The state of hang status. + name: :class:`str` + The name of the hang status. + emoji: Optional[:class:`PartialEmoji`] + The emoji of the hang status if any. + """ + + __slots__ = ('state', 'name', 'emoji') + + def __init__(self, state: HangStatusType, **extra: Any) -> None: + super().__init__(**extra) + self.state: HangStatusType = state + + self.name: str + if self.state == HangStatusType.custom: + details = extra.get('details') + if details is None: + raise ValueError('hang status state cannot be custom') + self.name = details + else: + self.name = self.state.value + + self.emoji: Optional[PartialEmoji] + emoji = extra.get('emoji') + if emoji is None: + self.emoji = emoji + elif isinstance(emoji, dict): + self.emoji = PartialEmoji.from_dict(emoji) + elif isinstance(emoji, str): + self.emoji = PartialEmoji(name=emoji) + elif isinstance(emoji, PartialEmoji): + self.emoji = emoji + else: + raise TypeError(f'Expected str, PartialEmoji, or None, received {type(emoji)!r} instead.') + + @property + def type(self) -> ActivityType: + """:class:`ActivityType`: Returns the activity's type. This is for compatibility with :class:`Activity`. + + It always returns :attr:`ActivityType.hang`. + """ + return ActivityType.hang + + def to_dict(self) -> Dict[str, Any]: + ret = { + 'type': ActivityType.hang.value, + 'name': 'Hang Status', + 'state': self.state.value, + } + if self.state == HangStatusType.custom: + ret['details'] = self.name + if self.emoji: + ret['emoji'] = self.emoji.to_dict() + + return ret + + def __str__(self) -> str: + return str(self.name) + + def __repr__(self) -> str: + return f'' + + def __eq__(self, other: object) -> bool: + return isinstance(other, HangStatus) and other.name == self.name and other.emoji == self.emoji + + def __ne__(self, other: object) -> bool: + return not self.__eq__(other) + + def __hash__(self) -> int: + return hash((self.name, str(self.emoji))) + + +ActivityTypes = Union[Activity, Game, CustomActivity, Streaming, Spotify, HangStatus] @overload @@ -862,6 +965,9 @@ def create_activity(data: Optional[ActivityPayload], state: ConnectionState) -> return Activity(**data) elif game_type is ActivityType.listening and 'sync_id' in data and 'session_id' in data: return Spotify(**data) + elif game_type is ActivityType.hang: + hang_state = try_enum(HangStatusType, data.pop('state')) + return HangStatus(state=hang_state, **data) # type: ignore else: ret = Activity(**data) diff --git a/discord/enums.py b/discord/enums.py index eaf8aef5e058..27002befac85 100644 --- a/discord/enums.py +++ b/discord/enums.py @@ -74,6 +74,7 @@ 'EntitlementType', 'EntitlementOwnerType', 'PollLayoutType', + 'HangStatusType', ) @@ -525,11 +526,23 @@ class ActivityType(Enum): watching = 3 custom = 4 competing = 5 + hang = 6 def __int__(self) -> int: return self.value +class HangStatusType(Enum): + chilling = 'chilling' + gaming = 'gaming' + focusing = 'focusing' + brb = 'brb' + eating = 'eating' + in_transit = 'in-transit' + watching = 'watching' + custom = 'custom' + + class TeamMembershipState(Enum): invited = 1 accepted = 2 diff --git a/discord/types/activity.py b/discord/types/activity.py index f57334936338..0f2b8dbf63ca 100644 --- a/discord/types/activity.py +++ b/discord/types/activity.py @@ -76,7 +76,7 @@ class ActivityEmoji(TypedDict): animated: NotRequired[bool] -ActivityType = Literal[0, 1, 2, 4, 5] +ActivityType = Literal[0, 1, 2, 4, 5, 6] class SendableActivity(TypedDict): diff --git a/docs/api.rst b/docs/api.rst index 41cf6549d169..e8ac2186e098 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -1911,6 +1911,12 @@ of :class:`enum.Enum`. .. versionadded:: 1.5 + .. attribute:: hang + + A hang status activity type. + + .. versionadded:: 2.5 + .. class:: VerificationLevel Specifies a :class:`Guild`\'s verification level, which is the criteria in @@ -3663,6 +3669,45 @@ of :class:`enum.Enum`. A burst reaction, also known as a "super reaction". +.. class:: HangStatusType + + Represents the type of an hang status. + + .. versionadded:: 2.5 + + .. attribute:: chilling + + The default hang status "Chilling". + + .. attribute:: gaming + + The default hang status "GAMING". + + .. attribute:: focusing + + The default hang status "In the zone". + + .. attribute:: brb + + The default hang status "Gonna BRB". + + .. attribute:: eating + + The default hang status "Grubbin". + + .. attribute:: in_transit + + The default hang status "Wandering IRL". + + .. attribute:: watching + + The default hang status "Watchin' stuff". + + .. attribute:: custom + + A custom hang status set by the user. + + .. _discord-api-audit-logs: Audit Log Data @@ -5310,6 +5355,14 @@ CustomActivity .. autoclass:: CustomActivity :members: +HangStatus +~~~~~~~~~~~ + +.. attributetable:: HangStatus + +.. autoclass:: HangStatus + :members: + Permissions ~~~~~~~~~~~~