Browse Source

Expose more information from partial invites, along with counts.

This adds the following information.

* `PartialInviteGuild` to replace `Object` patching
* `PartialInviteChannel` to replace `Object` patching
* Invite.approximate_member_count and Invite.approximate_presence_count

The new partial objects provide better documentation on what is
expected when you fetch random invites.

Fixes #1830
pull/1896/head
Rapptz 6 years ago
parent
commit
5d78f43e55
  1. 2
      discord/__init__.py
  2. 14
      discord/client.py
  3. 10
      discord/guild.py
  4. 7
      discord/http.py
  5. 170
      discord/invite.py
  6. 11
      docs/api.rst

2
discord/__init__.py

@ -35,7 +35,7 @@ from .permissions import Permissions, PermissionOverwrite
from .role import Role
from .file import File
from .colour import Color, Colour
from .invite import Invite
from .invite import Invite, PartialInviteChannel, PartialInviteGuild
from .object import Object
from .reaction import Reaction
from . import utils, opus, abc

14
discord/client.py

@ -882,7 +882,7 @@ class Client:
# Invite management
async def get_invite(self, url):
async def get_invite(self, url, *, with_counts=True):
"""|coro|
Gets an :class:`Invite` from a discord.gg URL or ID.
@ -890,13 +890,17 @@ class Client:
Note
------
If the invite is for a guild you have not joined, the guild and channel
attributes of the returned invite will be :class:`Object` with the names
patched in.
attributes of the returned :class:`Invite` will be :class:`PartialInviteGuild` and
:class:`PartialInviteChannel` respectively.
Parameters
-----------
url : str
url: :class:`str`
The discord invite ID or URL (must be a discord.gg URL).
with_counts: :class:`bool`
Whether to include count information in the invite. This fills the
:attr:`Invite.approximate_member_count` and :attr:`Invite.approximate_presence_count`
fields.
Raises
-------
@ -912,7 +916,7 @@ class Client:
"""
invite_id = self._resolve_invite(url)
data = await self.http.get_invite(invite_id)
data = await self.http.get_invite(invite_id, with_counts=with_counts)
return Invite.from_incomplete(state=self._connection, data=data)
async def delete_invite(self, invite):

10
discord/guild.py

@ -83,7 +83,7 @@ class Guild(Hashable):
The timeout to get sent to the AFK channel.
afk_channel: Optional[:class:`VoiceChannel`]
The channel that denotes the AFK channel. None if it doesn't exist.
icon: :class:`str`
icon: Optional[:class:`str`]
The guild's icon.
id: :class:`int`
The guild's ID.
@ -114,7 +114,7 @@ class Guild(Hashable):
- ``VERIFIED``: Guild is a "verified" server.
- ``MORE_EMOJI``: Guild is allowed to have more than 50 custom emoji.
splash: :class:`str`
splash: Optional[:class:`str`]
The guild's invite splash.
"""
@ -601,7 +601,7 @@ class Guild(Hashable):
channel upon creation. This parameter expects a :class:`dict` of
overwrites with the target (either a :class:`Member` or a :class:`Role`)
as the key and a :class:`PermissionOverwrite` as the value.
Note
--------
Creating a channel of a specified position will not update the position of
@ -641,7 +641,7 @@ class Guild(Hashable):
The permissions will be automatically synced to category if no
overwrites are provided.
position: :class:`int`
The position in the channel list. This is a number that starts
The position in the channel list. This is a number that starts
at 0. e.g. the top channel is position 0.
topic: Optional[:class:`str`]
The new channel's topic.
@ -679,7 +679,7 @@ class Guild(Hashable):
This is similar to :meth:`create_text_channel` except makes a :class:`VoiceChannel` instead, in addition
to having the following new parameters.
Parameters
-----------
bitrate: :class:`int`

7
discord/http.py

@ -647,8 +647,11 @@ class HTTPClient:
return self.request(r, reason=reason, json=payload)
def get_invite(self, invite_id):
return self.request(Route('GET', '/invite/{invite_id}', invite_id=invite_id))
def get_invite(self, invite_id, *, with_counts=True):
params = {
'with_counts': int(with_counts)
}
return self.request(Route('GET', '/invite/{invite_id}', invite_id=invite_id), params=params)
def invites_from(self, guild_id):
return self.request(Route('GET', '/guilds/{guild_id}/invites', guild_id=guild_id))

170
discord/invite.py

@ -27,6 +27,141 @@ DEALINGS IN THE SOFTWARE.
from .utils import parse_time
from .mixins import Hashable
from .object import Object
from .enums import ChannelType, VerificationLevel, try_enum
from collections import namedtuple
class PartialInviteChannel(namedtuple('PartialInviteChannel', 'id name type')):
"""Represents a "partial" invite channel.
This model will be given when the user is not part of the
guild the :class:`Invite` resolves to.
.. container:: operations
.. describe:: x == y
Checks if two partial channels are the same.
.. describe:: x != y
Checks if two partial channels are not the same.
.. describe:: hash(x)
Return the partial channel's hash.
.. describe:: str(x)
Returns the partial channel's name.
Attributes
-----------
name: :class:`str`
The partial channel's name.
id: :class:`int`
The partial channel's ID.
type: :class:`ChannelType`
The partial channel's type.
"""
__slots__ = ()
def __str__(self):
return self.name
@property
def mention(self):
""":class:`str` : The string that allows you to mention the channel."""
return '<#%s>' % self.id
@property
def created_at(self):
"""Returns the channel's creation time in UTC."""
return utils.snowflake_time(self.id)
class PartialInviteGuild(namedtuple('PartialInviteGuild', 'features icon id name splash verification_level')):
"""Represents a "partial" invite guild.
This model will be given when the user is not part of the
guild the :class:`Invite` resolves to.
.. container:: operations
.. describe:: x == y
Checks if two partial guilds are the same.
.. describe:: x != y
Checks if two partial guilds are not the same.
.. describe:: hash(x)
Return the partial guild's hash.
.. describe:: str(x)
Returns the partial guild's name.
Attributes
-----------
name: :class:`str`
The partial guild's name.
id: :class:`int`
The partial guild's ID.
verification_level: :class:`VerificationLevel`
The partial guild's verification level.
features: List[:class:`str`]
A list of features the guild has. See :attr:`Guild.features` for more information.
icon: Optional[:class:`str`]
The partial guild's icon.
splash: Optional[:class:`str`]
The partial guild's invite splash.
"""
__slots__ = ()
def __str__(self):
return self.name
@property
def created_at(self):
"""Returns the guild's creation time in UTC."""
return utils.snowflake_time(self.id)
@property
def icon_url(self):
"""Returns the URL version of the guild's icon. Returns an empty string if it has no icon."""
return self.icon_url_as()
def icon_url_as(self, *, format='webp', size=1024):
""":class:`str`: The same operation as :meth:`Guild.icon_url_as`."""
if not valid_icon_size(size):
raise InvalidArgument("size must be a power of 2 between 16 and 2048")
if format not in VALID_ICON_FORMATS:
raise InvalidArgument("format must be one of {}".format(VALID_ICON_FORMATS))
if self.icon is None:
return ''
return 'https://cdn.discordapp.com/icons/{0.id}/{0.icon}.{1}?size={2}'.format(self, format, size)
@property
def splash_url(self):
"""Returns the URL version of the guild's invite splash. Returns an empty string if it has no splash."""
return self.splash_url_as()
def splash_url_as(self, *, format='webp', size=2048):
""":class:`str`: The same operation as :meth:`Guild.splash_url_as`."""
if not valid_icon_size(size):
raise InvalidArgument("size must be a power of 2 between 16 and 2048")
if format not in VALID_ICON_FORMATS:
raise InvalidArgument("format must be one of {}".format(VALID_ICON_FORMATS))
if self.splash is None:
return ''
return 'https://cdn.discordapp.com/splashes/{0.id}/{0.splash}.{1}?size={2}'.format(self, format, size)
class Invite(Hashable):
"""Represents a Discord :class:`Guild` or :class:`abc.GuildChannel` invite.
@ -58,7 +193,7 @@ class Invite(Hashable):
How long the before the invite expires in seconds. A value of 0 indicates that it doesn't expire.
code: :class:`str`
The URL fragment used for the invite.
guild: :class:`Guild`
guild: Union[:class:`Guild`, :class:`PartialInviteGuild`]
The guild the invite is for.
revoked: :class:`bool`
Indicates if the invite has been revoked.
@ -73,13 +208,19 @@ class Invite(Hashable):
How many times the invite can be used.
inviter: :class:`User`
The user who created the invite.
channel: :class:`abc.GuildChannel`
approximate_member_count: Optional[:class:`int`]
The approximate number of members in the guild.
approximate_presence_count: Optional[:class:`int`]
The approximate number of members currently active in the guild.
This includes idle, dnd, online, and invisible members. Offline members are excluded.
channel: Union[:class:`abc.GuildChannel`, :class:`PartialInviteChannel`]
The channel the invite is for.
"""
__slots__ = ('max_age', 'code', 'guild', 'revoked', 'created_at', 'uses',
'temporary', 'max_uses', 'inviter', 'channel', '_state')
'temporary', 'max_uses', 'inviter', 'channel', '_state',
'approximate_member_count', 'approximate_presence_count' )
def __init__(self, *, state, data):
self._state = state
@ -91,6 +232,8 @@ class Invite(Hashable):
self.temporary = data.get('temporary')
self.uses = data.get('uses')
self.max_uses = data.get('max_uses')
self.approximate_presence_count = data.get('approximate_presence_count')
self.approximate_member_count = data.get('approximate_member_count')
inviter_data = data.get('inviter')
self.inviter = None if inviter_data is None else self._state.store_user(inviter_data)
@ -104,17 +247,16 @@ class Invite(Hashable):
if guild is not None:
channel = guild.get_channel(channel_id)
else:
guild = Object(id=guild_id)
channel = Object(id=channel_id)
guild.name = data['guild']['name']
guild.splash = data['guild']['splash']
guild.splash_url = ''
if guild.splash:
guild.splash_url = 'https://cdn.discordapp.com/splashes/{0.id}/{0.splash}.jpg?size=2048'.format(guild)
channel.name = data['channel']['name']
channel_data = data['channel']
guild_data = data['guild']
channel_type = try_enum(ChannelType, channel_data['type'])
channel = PartialInviteChannel(id=channel_id, name=channel_data['name'], type=channel_type)
guild = PartialInviteGuild(id=guild_id,
name=guild_data['name'],
features=guild_data.get('features', []),
icon=guild_data.get('icon'),
splash=guild_data.get('splash'),
verification_level=try_enum(VerificationLevel, guild_data.get('verification_level')))
data['guild'] = guild
data['channel'] = channel
return cls(state=state, data=data)

11
docs/api.rst

@ -2035,6 +2035,17 @@ GroupChannel
.. autocomethod:: typing
:async-with:
PartialInviteGuild
~~~~~~~~~~~~~~~~~~~
.. autoclass:: PartialInviteGuild()
:members:
PartialInviteChannel
~~~~~~~~~~~~~~~~~~~~~
.. autoclass:: PartialInviteChannel()
:members:
Invite
~~~~~~~

Loading…
Cancel
Save