diff --git a/discord/ext/commands/converter.py b/discord/ext/commands/converter.py index f3d7b2571..155b1a4e2 100644 --- a/discord/ext/commands/converter.py +++ b/discord/ext/commands/converter.py @@ -122,13 +122,49 @@ class MemberConverter(IDConverter): .. versionchanged:: 1.5 Raise :exc:`.MemberNotFound` instead of generic :exc:`.BadArgument` + + .. versionchanged:: 1.5.1 + This converter now lazily fetches members from the gateway and HTTP APIs, + optionally caching the result if :attr:`.MemberCacheFlags.joined` is enabled. """ + async def query_member_named(self, guild, argument): + cache = guild._state._member_cache_flags.joined + if len(argument) > 5 and argument[-5] == '#': + username, _, discriminator = argument.rpartition('#') + members = await guild.query_members(username, limit=100, cache=cache) + return discord.utils.get(members, name=username, discriminator=discriminator) + else: + members = await guild.query_members(argument, limit=100, cache=cache) + return discord.utils.find(lambda m: m.name == argument or m.nick == argument, members) + + async def query_member_by_id(self, bot, guild, user_id): + ws = bot._get_websocket(shard_id=guild.shard_id) + cache = guild._state._member_cache_flags.joined + if ws.is_ratelimited(): + # If we're being rate limited on the WS, then fall back to using the HTTP API + # So we don't have to wait ~60 seconds for the query to finish + try: + member = await guild.fetch_member(user_id) + except discord.HTTPException: + return None + + if cache: + guild._add_member(member) + return member + + # If we're not being rate limited then we can use the websocket to actually query + members = await guild.query_members(limit=1, user_ids=[user_id], cache=cache) + if not members: + return None + return members[0] + async def convert(self, ctx, argument): bot = ctx.bot match = self._get_id_match(argument) or re.match(r'<@!?([0-9]+)>$', argument) guild = ctx.guild result = None + user_id = None if match is None: # not a mention... if guild: @@ -143,7 +179,16 @@ class MemberConverter(IDConverter): result = _get_from_guilds(bot, 'get_member', user_id) if result is None: - raise MemberNotFound(argument) + if guild is None: + raise MemberNotFound(argument) + + if user_id is not None: + result = await self.query_member_by_id(bot, guild, user_id) + else: + result = await self.query_member_named(guild, argument) + + if not result: + raise MemberNotFound(argument) return result