aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--discord/abc.py2
-rw-r--r--discord/client.py81
-rw-r--r--discord/ext/commands/converter.py4
-rw-r--r--discord/guild.py27
-rw-r--r--discord/http.py18
-rw-r--r--discord/iterators.py132
-rw-r--r--docs/migrating.rst6
7 files changed, 260 insertions, 10 deletions
diff --git a/discord/abc.py b/discord/abc.py
index 3ac9a82e..97383573 100644
--- a/discord/abc.py
+++ b/discord/abc.py
@@ -816,7 +816,7 @@ class Messageable(metaclass=abc.ABCMeta):
"""
return Typing(self)
- async def get_message(self, id):
+ async def fetch_message(self, id):
"""|coro|
Retrieves a single :class:`.Message` from the destination.
diff --git a/discord/client.py b/discord/client.py
index a5cec831..14b3e338 100644
--- a/discord/client.py
+++ b/discord/client.py
@@ -39,6 +39,7 @@ from .user import User, Profile
from .invite import Invite
from .object import Object
from .guild import Guild
+from .member import Member
from .errors import *
from .enums import Status, VoiceRegion
from .gateway import *
@@ -49,6 +50,7 @@ from .state import ConnectionState
from . import utils
from .backoff import ExponentialBackoff
from .webhook import Webhook
+from .iterators import GuildIterator
log = logging.getLogger(__name__)
@@ -841,6 +843,77 @@ class Client:
# Guild stuff
+ def fetch_guilds(self, *, limit=100, before=None, after=None):
+ """|coro|
+
+ Retreives an :class:`AsyncIterator` that enables receiving your guilds.
+
+ All parameters are optional.
+
+ Parameters
+ -----------
+ limit: Optional[:class:`int`]
+ The number of guilds to retrieve.
+ If ``None``, it retrieves every guild you have access to. Note, however,
+ that this would make it a slow operation.
+ Defaults to 100.
+ before: :class:`Snowflake` or `datetime`
+ Retrieves guilds before this date or object.
+ If a date is provided it must be a timezone-naive datetime representing UTC time.
+ after: :class:`Snowflake` or `datetime`
+ Retrieve guilds after this date or object.
+ If a date is provided it must be a timezone-naive datetime representing UTC time.
+
+ Raises
+ ------
+ HTTPException
+ Getting the guilds failed.
+
+ Yields
+ --------
+ :class:`Guild`
+ The guild with the guild data parsed.
+
+ Examples
+ ---------
+
+ Usage ::
+
+ async for guild in client.fetch_guilds(limit=150):
+ print(guild.name)
+
+ Flattening into a list ::
+
+ guilds = await client.fetch_guilds(limit=150).flatten()
+ # guilds is now a list of Guild...
+ """
+ return GuildIterator(self, limit=limit, before=before, after=after)
+
+ async def fetch_guild(self, guild_id):
+ """|coro|
+
+ Retreives a :class:`Guild` from an ID.
+
+ Parameters
+ -----------
+ guild_id: :class:`int`
+ The guild's ID to fetch from.
+
+ Raises
+ ------
+ Forbidden
+ You do not have access to the guild.
+ HTTPException
+ Getting the guild failed.
+
+ Returns
+ --------
+ :class:`Guild`
+ The guild from the ID.
+ """
+ data = await self.http.get_guild(guild_id)
+ return Guild(data=data, state=self._connection)
+
async def create_guild(self, name, region=None, icon=None):
"""|coro|
@@ -885,7 +958,7 @@ class Client:
# Invite management
- async def get_invite(self, url, *, with_counts=True):
+ async def fetch_invite(self, url, *, with_counts=True):
"""|coro|
Gets an :class:`Invite` from a discord.gg URL or ID.
@@ -974,7 +1047,7 @@ class Client:
bot_require_code_grant=data['bot_require_code_grant'],
owner=User(state=self._connection, data=data['owner']))
- async def get_user_info(self, user_id):
+ async def fetch_user(self, user_id):
"""|coro|
Retrieves a :class:`User` based on their ID. This can only
@@ -1002,7 +1075,7 @@ class Client:
data = await self.http.get_user_info(user_id)
return User(state=self._connection, data=data)
- async def get_user_profile(self, user_id):
+ async def fetch_user_profile(self, user_id):
"""|coro|
Gets an arbitrary user's profile. This can only be used by non-bot accounts.
@@ -1040,7 +1113,7 @@ class Client:
user=User(data=user, state=state),
connected_accounts=data['connected_accounts'])
- async def get_webhook_info(self, webhook_id):
+ async def fetch_webhook(self, webhook_id):
"""|coro|
Retrieves a :class:`Webhook` with the specified ID.
diff --git a/discord/ext/commands/converter.py b/discord/ext/commands/converter.py
index 9fc6cf6d..81d3a00e 100644
--- a/discord/ext/commands/converter.py
+++ b/discord/ext/commands/converter.py
@@ -336,11 +336,11 @@ class GameConverter(Converter):
class InviteConverter(Converter):
"""Converts to a :class:`Invite`.
- This is done via an HTTP request using :meth:`.Bot.get_invite`.
+ This is done via an HTTP request using :meth:`.Bot.fetch_invite`.
"""
async def convert(self, ctx, argument):
try:
- invite = await ctx.bot.get_invite(argument)
+ invite = await ctx.bot.fetch_invite(argument)
return invite
except Exception as exc:
raise BadArgument('Invite is invalid or expired') from exc
diff --git a/discord/guild.py b/discord/guild.py
index fd83acf6..e2d704b3 100644
--- a/discord/guild.py
+++ b/discord/guild.py
@@ -944,7 +944,32 @@ class Guild(Hashable):
fields['explicit_content_filter'] = explicit_content_filter.value
await http.edit_guild(self.id, reason=reason, **fields)
- async def get_ban(self, user):
+ async def fetch_member(self, member_id):
+ """|coro|
+
+ Retreives a :class:`Member` from a guild ID, and a member ID.
+
+ Parameters
+ -----------
+ member_id: :class:`int`
+ The member's ID to fetch from.
+
+ Raises
+ -------
+ Forbidden
+ You do not have access to the guild.
+ HTTPException
+ Getting the guild failed.
+
+ Returns
+ --------
+ :class:`Member`
+ The member from the member ID.
+ """
+ data = await self._state.http.get_member(self.id, member_id)
+ return Member(data=data, state=self._state, guild=self)
+
+ async def fetch_ban(self, user):
"""|coro|
Retrieves the :class:`BanEntry` for a user, which is a namedtuple
diff --git a/discord/http.py b/discord/http.py
index 7e34951f..72e9f8ec 100644
--- a/discord/http.py
+++ b/discord/http.py
@@ -553,9 +553,24 @@ class HTTPClient:
# Guild management
+ def get_guilds(self, limit, before=None, after=None):
+ params = {
+ 'limit': limit
+ }
+
+ if before:
+ params['before'] = before
+ if after:
+ params['after'] = after
+
+ return self.request(Route('GET', '/users/@me/guilds'), params=params)
+
def leave_guild(self, guild_id):
return self.request(Route('DELETE', '/users/@me/guilds/{guild_id}', guild_id=guild_id))
+ def get_guild(self, guild_id):
+ return self.request(Route('GET', '/guilds/{guild_id}', guild_id=guild_id))
+
def delete_guild(self, guild_id):
return self.request(Route('DELETE', '/guilds/{guild_id}', guild_id=guild_id))
@@ -593,6 +608,9 @@ class HTTPClient:
payload = {'code': code}
return self.request(Route('PATCH', '/guilds/{guild_id}/vanity-url', guild_id=guild_id), json=payload, reason=reason)
+ def get_member(self, guild_id, member_id):
+ return self.request(Route('GET', '/guilds/{guild_id}/members/{member_id}', guild_id=guild_id, member_id=member_id))
+
def prune_members(self, guild_id, days, *, reason=None):
params = {
'days': days
diff --git a/discord/iterators.py b/discord/iterators.py
index d77592a5..015620bc 100644
--- a/discord/iterators.py
+++ b/discord/iterators.py
@@ -237,7 +237,7 @@ class HistoryIterator(_AsyncIterator):
elif self.limit == 101:
self.limit = 100 # Thanks discord
elif self.limit == 1:
- raise ValueError("Use get_message.")
+ raise ValueError("Use fetch_message.")
self._retrieve_messages = self._retrieve_messages_around_strategy
if self.before and self.after:
@@ -459,3 +459,133 @@ class AuditLogIterator(_AsyncIterator):
continue
await self.entries.put(AuditLogEntry(data=element, users=self._users, guild=self.guild))
+
+
+class GuildIterator(_AsyncIterator):
+ """Iterator for receiving the client's guilds.
+
+ The guilds endpoint has the same two behaviours as described
+ in :class:`HistoryIterator`:
+ If `before` is specified, the guilds endpoint returns the `limit`
+ newest guilds before `before`, sorted with newest first. For filling over
+ 100 guilds, update the `before` parameter to the oldest guild received.
+ Guilds will be returned in order by time.
+ If `after` is specified, it returns the `limit` oldest guilds after `after`,
+ sorted with newest first. For filling over 100 guilds, update the `after`
+ parameter to the newest guild received, If guilds are not reversed, they
+ will be out of order (99-0, 199-100, so on)
+
+ Not that if both before and after are specified, before is ignored by the
+ guilds endpoint.
+
+ Parameters
+ -----------
+ bot: :class:`discord.Client`
+ The client to retrieve the guilds from.
+ limit: :class:`int`
+ Maximum number of guilds to retrieve.
+ before: :class:`Snowflake`
+ Object before which all guilds must be.
+ after: :class:`Snowflake`
+ Object after which all guilds must be.
+ """
+ def __init__(self, bot, limit, before=None, after=None):
+
+ if isinstance(before, datetime.datetime):
+ before = Object(id=time_snowflake(before, high=False))
+ if isinstance(after, datetime.datetime):
+ after = Object(id=time_snowflake(after, high=True))
+
+ self.bot = bot
+ self.limit = limit
+ self.before = before
+ self.after = after
+
+ self._filter = None
+
+ self.state = self.bot._connection
+ self.get_guilds = self.bot.http.get_guilds
+ self.guilds = asyncio.Queue(loop=self.state.loop)
+
+ if self.before and self.after:
+ self._retrieve_guilds = self._retrieve_guilds_before_strategy
+ self._filter = lambda m: int(m['id']) > self.after.id
+ elif self.after:
+ self._retrieve_guilds = self._retrieve_guilds_after_strategy
+ else:
+ self._retrieve_guilds = self._retrieve_guilds_before_strategy
+
+ async def next(self):
+ if self.guilds.empty():
+ await self.fill_guilds()
+
+ try:
+ return self.guilds.get_nowait()
+ except asyncio.QueueEmpty:
+ raise NoMoreItems()
+
+ def _get_retrieve(self):
+ l = self.limit
+ if l is None:
+ r = 100
+ elif l <= 100:
+ r = l
+ else:
+ r = 100
+
+ self.retrieve = r
+ return r > 0
+
+ def create_guild(self, data):
+ from .guild import Guild
+ return Guild(state=self.state, data=data)
+
+ async def flatten(self):
+ result = []
+ while self._get_retrieve():
+ data = await self._retrieve_guilds(self.retrieve)
+ if len(data) < 100:
+ self.limit = 0
+
+ if self._filter:
+ data = filter(self._filter, data)
+
+ for element in data:
+ result.append(self.create_guild(element))
+ return result
+
+ async def fill_guilds(self):
+ if self._get_retrieve():
+ data = await self._retrieve_guilds(self.retrieve)
+ if self.limit is None or len(data) < 100:
+ self.limit = 0
+
+ if self._filter:
+ data = filter(self._filter, data)
+
+ for element in data:
+ await self.guilds.put(self.create_guild(element))
+
+ async def _retrieve_guilds(self, retrieve):
+ """Retrieve guilds and update next parameters."""
+ pass
+
+ async def _retrieve_guilds_before_strategy(self, retrieve):
+ """Retrieve guilds using before parameter."""
+ before = self.before.id if self.before else None
+ data = await self.get_guilds(retrieve, before=before)
+ if len(data):
+ if self.limit is not None:
+ self.limit -= retrieve
+ self.before = Object(id=int(data[-1]['id']))
+ return data
+
+ async def _retrieve_guilds_after_strategy(self, retrieve):
+ """Retrieve guilds using after parameter."""
+ after = self.after.id if self.after else None
+ data = await self.get_guilds(retrieve, after=after)
+ if len(data):
+ if self.limit is not None:
+ self.limit -= retrieve
+ self.after = Object(id=int(data[0]['id']))
+ return data
diff --git a/docs/migrating.rst b/docs/migrating.rst
index f03b08b0..9ea0e5f0 100644
--- a/docs/migrating.rst
+++ b/docs/migrating.rst
@@ -148,10 +148,14 @@ A list of these changes is enumerated below.
+---------------------------------------+------------------------------------------------------------------------------+
| ``Client.get_bans`` | :meth:`Guild.bans` |
+---------------------------------------+------------------------------------------------------------------------------+
-| ``Client.get_message`` | :meth:`abc.Messageable.get_message` |
+| ``Client.get_invite`` | :meth:`Client.fetch_invite` |
++---------------------------------------+------------------------------------------------------------------------------+
+| ``Client.get_message`` | :meth:`abc.Messageable.fetch_message` |
+---------------------------------------+------------------------------------------------------------------------------+
| ``Client.get_reaction_users`` | :meth:`Reaction.users` |
+---------------------------------------+------------------------------------------------------------------------------+
+| ``Client.get_user_info`` | :meth:`Client.fetch_user` |
++---------------------------------------+------------------------------------------------------------------------------+
| ``Client.invites_from`` | :meth:`abc.GuildChannel.invites` or :meth:`Guild.invites` |
+---------------------------------------+------------------------------------------------------------------------------+
| ``Client.join_voice_channel`` | :meth:`VoiceChannel.connect` (see :ref:`migrating_1_0_voice`) |