diff --git a/discord/types/widget.py b/discord/types/widget.py index be1c196b0..d0db3b5eb 100644 --- a/discord/types/widget.py +++ b/discord/types/widget.py @@ -46,18 +46,15 @@ class WidgetMember(User, total=False): suppress: bool -class _WidgetOptional(TypedDict, total=False): +class Widget(TypedDict): + id: Snowflake + name: str + instant_invite: Optional[str] channels: List[WidgetChannel] members: List[WidgetMember] presence_count: int -class Widget(_WidgetOptional): - id: Snowflake - name: str - instant_invite: str - - class WidgetSettings(TypedDict): enabled: bool channel_id: Optional[Snowflake] diff --git a/discord/widget.py b/discord/widget.py index b10b63c31..0a9736cad 100644 --- a/discord/widget.py +++ b/discord/widget.py @@ -231,7 +231,7 @@ class Widget: channels: List[:class:`WidgetChannel`] The accessible voice channels in the guild. members: List[:class:`Member`] - The online members in the server. Offline members + The online members in the guild. Offline members do not appear in the widget. .. note:: @@ -240,10 +240,15 @@ class Widget: the users will be "anonymized" with linear IDs and discriminator information being incorrect. Likewise, the number of members retrieved is capped. + presence_count: :class:`int` + The approximate number of online members in the guild. + Offline members are not included in this count. + + .. versionadded:: 2.0 """ - __slots__ = ('_state', 'channels', '_invite', 'id', 'members', 'name') + __slots__ = ('_state', 'channels', '_invite', 'id', 'members', 'name', 'presence_count') def __init__(self, *, state: ConnectionState, data: WidgetPayload) -> None: self._state = state @@ -268,6 +273,8 @@ class Widget: self.members.append(WidgetMember(state=self._state, data=member, connected_channel=connected_channel)) + self.presence_count: int = data['presence_count'] + def __str__(self) -> str: return self.json_url @@ -290,11 +297,11 @@ class Widget: return f"https://discord.com/api/guilds/{self.id}/widget.json" @property - def invite_url(self) -> str: + def invite_url(self) -> Optional[str]: """Optional[:class:`str`]: The invite URL for the guild, if available.""" return self._invite - async def fetch_invite(self, *, with_counts: bool = True) -> Invite: + async def fetch_invite(self, *, with_counts: bool = True) -> Optional[Invite]: """|coro| Retrieves an :class:`Invite` from the widget's invite URL. @@ -310,9 +317,11 @@ class Widget: Returns -------- - :class:`Invite` - The invite from the widget's invite URL. + Optional[:class:`Invite`] + The invite from the widget's invite URL, if available. """ - resolved = resolve_invite(self._invite) - data = await self._state.http.get_invite(resolved.code, with_counts=with_counts) - return Invite.from_incomplete(state=self._state, data=data) + if self._invite: + resolved = resolve_invite(self._invite) + data = await self._state.http.get_invite(resolved.code, with_counts=with_counts) + return Invite.from_incomplete(state=self._state, data=data) + return None diff --git a/docs/migrating.rst b/docs/migrating.rst index 7d529e1ba..7f2e2e677 100644 --- a/docs/migrating.rst +++ b/docs/migrating.rst @@ -927,6 +927,7 @@ The following changes have been made: - :attr:`DMChannel.recipient` may now be ``None``. - :meth:`Guild.vanity_invite` may now be ``None``. This has been done to fix an issue with the method returning a broken :class:`Invite` object. +- :meth:`Widget.fetch_invite` may now be ``None``. - :attr:`Guild.shard_id` is now ``0`` instead of ``None`` if :class:`AutoShardedClient` is not used. - :attr:`Guild.mfa_level` is now of type :class:`MFALevel`. - :attr:`Guild.member_count` is now of type Optional[:class:`int`].