Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a method to fetch a preview of a discoverable guild #9986

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 22 additions & 1 deletion discord/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@
from .invite import Invite
from .template import Template
from .widget import Widget
from .guild import Guild
from .guild import Guild, GuildPreview
from .emoji import Emoji
from .channel import _threaded_channel_factory, PartialMessageable
from .enums import ChannelType, EntitlementOwnerType
Expand Down Expand Up @@ -2356,6 +2356,27 @@ async def fetch_guild(self, guild_id: int, /, *, with_counts: bool = True) -> Gu
data = await self.http.get_guild(guild_id, with_counts=with_counts)
return Guild(data=data, state=self._connection)

async def fetch_guild_preview(self, guild_id: int) -> GuildPreview:
"""|coro|

Retrieves a preview of a :class:`Guild` from an ID. If the guild is discoverable, you don't have to be
a member of it.
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Retrieves a preview of a :class:`Guild` from an ID. If the guild is discoverable, you don't have to be
a member of it.
Retrieves a preview of a :class:`Guild` from an ID. If the guild is discoverable,
you don't have to be a member of it.
.. versionadded:: 2.5


Raises
------
NotFound
The guild doesn't exist, or is not discoverable and you are not in it.
HTTPException
Getting the guild failed.

Returns
--------
:class:`.GuildPreview`
The guild preview from the ID.
"""
data = await self.http.get_guild_preview(guild_id)
return GuildPreview(data=data, state=self._connection)

async def create_guild(
self,
*,
Expand Down
115 changes: 115 additions & 0 deletions discord/guild.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@

__all__ = (
'Guild',
'GuildPreview',
'BanEntry',
)

Expand All @@ -109,6 +110,7 @@
from .types.guild import (
Ban as BanPayload,
Guild as GuildPayload,
GuildPreview as GuildPreviewPayload,
RolePositionUpdate as RolePositionUpdatePayload,
GuildFeature,
IncidentData,
Expand Down Expand Up @@ -159,6 +161,119 @@ class _GuildLimit(NamedTuple):
bitrate: float
filesize: int

class GuildPreview(Hashable):
"""Represents a preview of a Discord guild.

.. container:: operations

.. describe:: x == y

Checks if two guild previews are equal.

.. describe:: x != y

Checks if two guild previews are not equal.

.. describe:: hash(x)

Returns the guild's hash.

.. describe:: str(x)

Returns the guild's name.

Attributes
----------
name: :class:`str`
The guild preview's name.
id: :class:`int`
The guild preview's ID.
features: List[:class:`str`]
A list of features the guild has. See :attr:`Guild.features` for more information.
description: Optional[:class:`str`]
The guild preview's description.
emojis: Tuple[:class:`Emoji`, ...]
All emojis that the guild owns.
stickers: Tuple[:class:`GuildSticker`, ...]
All stickers that the guild owns.
approximate_member_count: :class:`int`
The approximate number of members in the guild.
approximate_member_count: :class:`int`
gingershaped marked this conversation as resolved.
Show resolved Hide resolved
The approximate number of members currently active in in the guild. Offline members are excluded.
"""

__slots__ = (
'_state',
'_icon',
'_splash',
'_discovery_splash',
'id',
'name',
'emojis',
'stickers',
'features',
'description',
"approximate_member_count",
"approximate_presence_count",
)

def __init__(self, *, data: GuildPreviewPayload, state: ConnectionState) -> None:
self._state: ConnectionState = state
self.id = int(data['id'])
self.name: str = data['name']
self._icon: Optional[str] = data.get('icon')
self._splash: Optional[str] = data.get('splash')
self._discovery_splash: Optional[str] = data.get('discovery_splash')
self.emojis: Tuple[Emoji, ...] = (
tuple(map(lambda d: state.store_emoji(self, d), data.get('emojis', [])))
if state.cache_guild_expressions
else ()
)
gingershaped marked this conversation as resolved.
Show resolved Hide resolved
self.stickers: Tuple[GuildSticker, ...] = (
tuple(map(lambda d: state.store_sticker(self, d), data.get('stickers', [])))
if state.cache_guild_expressions
else ()
gingershaped marked this conversation as resolved.
Show resolved Hide resolved
)
self.features: List[GuildFeature] = data.get('features', [])
self.description: Optional[str] = data.get('description')
self.approximate_member_count: int = data.get('approximate_member_count')
self.approximate_presence_count: int = data.get('approximate_presence_count')

def __str__(self) -> str:
return self.name

def __repr__(self) -> str:
return (
f'<{self.__class__.__name__} id={self.id} name={self.name!r} features={self.features} '
f'description={self.description!r}>'
)

@property
def created_at(self) -> datetime.datetime:
""":class:`datetime.datetime`: Returns the guild's creation time in UTC."""
return utils.snowflake_time(self.id)

@property
def icon(self) -> Optional[Asset]:
"""Optional[:class:`Asset`]: Returns the guild's icon asset, if available."""
if self._icon is None:
return None
return Asset._from_guild_icon(self._state, self.id, self._icon)

@property
def splash(self) -> Optional[Asset]:
"""Optional[:class:`Asset`]: Returns the guild's invite splash asset, if available."""
if self._splash is None:
return None
return Asset._from_guild_image(self._state, self.id, self._splash, path='splashes')

@property
def discovery_splash(self) -> Optional[Asset]:
"""Optional[:class:`Asset`]: Returns the guild's discovery splash asset, if available."""
if self._discovery_splash is None:
return None
return Asset._from_guild_image(self._state, self.id, self._discovery_splash, path='discovery-splashes')


class Guild(Hashable):
"""Represents a Discord guild.
Expand Down
3 changes: 3 additions & 0 deletions discord/http.py
Original file line number Diff line number Diff line change
Expand Up @@ -1451,6 +1451,9 @@ def get_guild(self, guild_id: Snowflake, *, with_counts: bool = True) -> Respons
params = {'with_counts': int(with_counts)}
return self.request(Route('GET', '/guilds/{guild_id}', guild_id=guild_id), params=params)

def get_guild_preview(self, guild_id: Snowflake) -> Response[guild.GuildPreview]:
return self.request(Route('GET', '/guilds/{guild_id}/preview', guild_id=guild_id))

def delete_guild(self, guild_id: Snowflake) -> Response[None]:
return self.request(Route('DELETE', '/guilds/{guild_id}', guild_id=guild_id))

Expand Down
6 changes: 3 additions & 3 deletions discord/state.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@

import os

from .guild import Guild
from .guild import Guild, GuildPreview
from .activity import BaseActivity
from .sku import Entitlement
from .user import User, ClientUser
Expand Down Expand Up @@ -396,13 +396,13 @@ def create_user(self, data: Union[UserPayload, PartialUserPayload]) -> User:
def get_user(self, id: int) -> Optional[User]:
return self._users.get(id)

def store_emoji(self, guild: Guild, data: EmojiPayload) -> Emoji:
def store_emoji(self, guild: Guild | GuildPreview, data: EmojiPayload) -> Emoji:
gingershaped marked this conversation as resolved.
Show resolved Hide resolved
# the id will be present here
emoji_id = int(data['id']) # type: ignore
self._emojis[emoji_id] = emoji = Emoji(guild=guild, state=self, data=data)
return emoji

def store_sticker(self, guild: Guild, data: GuildStickerPayload) -> GuildSticker:
def store_sticker(self, guild: Guild | GuildPreview, data: GuildStickerPayload) -> GuildSticker:
gingershaped marked this conversation as resolved.
Show resolved Hide resolved
sticker_id = int(data['id'])
self._stickers[sticker_id] = sticker = GuildSticker(state=self, data=data)
return sticker
Expand Down
8 changes: 8 additions & 0 deletions docs/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5682,6 +5682,14 @@ CallMessage
.. autoclass:: CallMessage()
:members:

GuildPreview
~~~~~~~~~~~~

.. attributetable:: GuildPreview

.. autoclass:: GuildPreview
:members:

gingershaped marked this conversation as resolved.
Show resolved Hide resolved

Exceptions
------------
Expand Down