diff options
| author | 2019-12-22 19:15:32 -0800 | |
|---|---|---|
| committer | 2019-12-22 19:22:22 -0800 | |
| commit | 562af4fbef7d2b093eb76f563c5f1c635124e299 (patch) | |
| tree | db0b45e17b3887ec104a63b2f812b109ea711c24 | |
| parent | Use log.warning instead of the deprecated log.warn (diff) | |
Make FetchedUser a subclass of UserConverter
| -rw-r--r-- | bot/cogs/alias.py | 17 | ||||
| -rw-r--r-- | bot/cogs/moderation/infractions.py | 18 | ||||
| -rw-r--r-- | bot/cogs/moderation/management.py | 4 | ||||
| -rw-r--r-- | bot/cogs/watchchannels/bigbrother.py | 6 | ||||
| -rw-r--r-- | bot/cogs/watchchannels/talentpool.py | 9 | ||||
| -rw-r--r-- | bot/converters.py | 48 |
6 files changed, 52 insertions, 50 deletions
diff --git a/bot/cogs/alias.py b/bot/cogs/alias.py index 03c49c2f4..e709be85d 100644 --- a/bot/cogs/alias.py +++ b/bot/cogs/alias.py @@ -1,8 +1,7 @@ import inspect import logging -from typing import Union -from discord import Colour, Embed, Member, User +from discord import Colour, Embed from discord.ext.commands import Cog, Command, Context, clean_content, command, group from bot.bot import Bot @@ -60,18 +59,12 @@ class Alias (Cog): await self.invoke(ctx, "site tools") @command(name="watch", hidden=True) - async def bigbrother_watch_alias( - self, - ctx: Context, - user: Union[Member, User, FetchedUser], - *, - reason: str - ) -> None: + async def bigbrother_watch_alias(self, ctx: Context, user: FetchedUser, *, reason: str) -> None: """Alias for invoking <prefix>bigbrother watch [user] [reason].""" await self.invoke(ctx, "bigbrother watch", user, reason=reason) @command(name="unwatch", hidden=True) - async def bigbrother_unwatch_alias(self, ctx: Context, user: Union[User, FetchedUser], *, reason: str) -> None: + async def bigbrother_unwatch_alias(self, ctx: Context, user: FetchedUser, *, reason: str) -> None: """Alias for invoking <prefix>bigbrother unwatch [user] [reason].""" await self.invoke(ctx, "bigbrother unwatch", user, reason=reason) @@ -137,12 +130,12 @@ class Alias (Cog): await self.invoke(ctx, "docs get", symbol) @command(name="nominate", hidden=True) - async def nomination_add_alias(self, ctx: Context, user: Union[Member, User, FetchedUser], *, reason: str) -> None: + async def nomination_add_alias(self, ctx: Context, user: FetchedUser, *, reason: str) -> None: """Alias for invoking <prefix>talentpool add [user] [reason].""" await self.invoke(ctx, "talentpool add", user, reason=reason) @command(name="unnominate", hidden=True) - async def nomination_end_alias(self, ctx: Context, user: Union[User, FetchedUser], *, reason: str) -> None: + async def nomination_end_alias(self, ctx: Context, user: FetchedUser, *, reason: str) -> None: """Alias for invoking <prefix>nomination end [user] [reason].""" await self.invoke(ctx, "nomination end", user, reason=reason) diff --git a/bot/cogs/moderation/infractions.py b/bot/cogs/moderation/infractions.py index 5b6a63dbb..264d8bcf5 100644 --- a/bot/cogs/moderation/infractions.py +++ b/bot/cogs/moderation/infractions.py @@ -14,12 +14,10 @@ from bot.decorators import respect_role_hierarchy from bot.utils.checks import with_role_check from . import utils from .scheduler import InfractionScheduler -from .utils import MemberObject, UserTypes +from .utils import MemberObject log = logging.getLogger(__name__) -MemberConverter = t.Union[UserTypes, FetchedUser] - class Infractions(InfractionScheduler, commands.Cog): """Apply and pardon infractions on users for moderation purposes.""" @@ -68,7 +66,7 @@ class Infractions(InfractionScheduler, commands.Cog): await self.apply_kick(ctx, user, reason, active=False) @command() - async def ban(self, ctx: Context, user: MemberConverter, *, reason: str = None) -> None: + async def ban(self, ctx: Context, user: FetchedUser, *, reason: str = None) -> None: """Permanently ban a user for the given reason.""" await self.apply_ban(ctx, user, reason) @@ -95,7 +93,7 @@ class Infractions(InfractionScheduler, commands.Cog): await self.apply_mute(ctx, user, reason, expires_at=duration) @command() - async def tempban(self, ctx: Context, user: MemberConverter, duration: utils.Expiry, *, reason: str = None) -> None: + async def tempban(self, ctx: Context, user: FetchedUser, duration: utils.Expiry, *, reason: str = None) -> None: """ Temporarily ban a user for the given reason and duration. @@ -117,7 +115,7 @@ class Infractions(InfractionScheduler, commands.Cog): # region: Permanent shadow infractions @command(hidden=True) - async def note(self, ctx: Context, user: MemberConverter, *, reason: str = None) -> None: + async def note(self, ctx: Context, user: FetchedUser, *, reason: str = None) -> None: """Create a private note for a user with the given reason without notifying the user.""" infraction = await utils.post_infraction(ctx, user, "note", reason, hidden=True, active=False) if infraction is None: @@ -131,7 +129,7 @@ class Infractions(InfractionScheduler, commands.Cog): await self.apply_kick(ctx, user, reason, hidden=True, active=False) @command(hidden=True, aliases=['shadowban', 'sban']) - async def shadow_ban(self, ctx: Context, user: MemberConverter, *, reason: str = None) -> None: + async def shadow_ban(self, ctx: Context, user: FetchedUser, *, reason: str = None) -> None: """Permanently ban a user for the given reason without notifying the user.""" await self.apply_ban(ctx, user, reason, hidden=True) @@ -161,7 +159,7 @@ class Infractions(InfractionScheduler, commands.Cog): async def shadow_tempban( self, ctx: Context, - user: MemberConverter, + user: FetchedUser, duration: utils.Expiry, *, reason: str = None @@ -187,12 +185,12 @@ class Infractions(InfractionScheduler, commands.Cog): # region: Remove infractions (un- commands) @command() - async def unmute(self, ctx: Context, user: MemberConverter) -> None: + async def unmute(self, ctx: Context, user: FetchedUser) -> None: """Prematurely end the active mute infraction for the user.""" await self.pardon_infraction(ctx, "mute", user) @command() - async def unban(self, ctx: Context, user: MemberConverter) -> None: + async def unban(self, ctx: Context, user: FetchedUser) -> None: """Prematurely end the active ban infraction for the user.""" await self.pardon_infraction(ctx, "ban", user) diff --git a/bot/cogs/moderation/management.py b/bot/cogs/moderation/management.py index fff86e9ea..2ec69960e 100644 --- a/bot/cogs/moderation/management.py +++ b/bot/cogs/moderation/management.py @@ -20,8 +20,6 @@ from .modlog import ModLog log = logging.getLogger(__name__) -UserConverter = t.Union[discord.User, proxy_user] - class ModManagement(commands.Cog): """Management of infractions.""" @@ -182,7 +180,7 @@ class ModManagement(commands.Cog): await ctx.invoke(self.search_reason, query) @infraction_search_group.command(name="user", aliases=("member", "id")) - async def search_user(self, ctx: Context, user: UserConverter) -> None: + async def search_user(self, ctx: Context, user: t.Union[discord.User, proxy_user]) -> None: """Search for infractions by member.""" infraction_list = await self.bot.api_client.get( 'bot/infractions', diff --git a/bot/cogs/watchchannels/bigbrother.py b/bot/cogs/watchchannels/bigbrother.py index 7a30d5033..b2cb99368 100644 --- a/bot/cogs/watchchannels/bigbrother.py +++ b/bot/cogs/watchchannels/bigbrother.py @@ -1,8 +1,6 @@ import logging from collections import ChainMap -from typing import Union -from discord import User from discord.ext.commands import Cog, Context, group from bot.bot import Bot @@ -47,7 +45,7 @@ class BigBrother(WatchChannel, Cog, name="Big Brother"): @bigbrother_group.command(name='watch', aliases=('w',)) @with_role(*MODERATION_ROLES) - async def watch_command(self, ctx: Context, user: Union[User, FetchedUser], *, reason: str) -> None: + async def watch_command(self, ctx: Context, user: FetchedUser, *, reason: str) -> None: """ Relay messages sent by the given `user` to the `#big-brother` channel. @@ -94,7 +92,7 @@ class BigBrother(WatchChannel, Cog, name="Big Brother"): @bigbrother_group.command(name='unwatch', aliases=('uw',)) @with_role(*MODERATION_ROLES) - async def unwatch_command(self, ctx: Context, user: Union[User, FetchedUser], *, reason: str) -> None: + async def unwatch_command(self, ctx: Context, user: FetchedUser, *, reason: str) -> None: """Stop relaying messages by the given `user`.""" active_watches = await self.bot.api_client.get( self.api_endpoint, diff --git a/bot/cogs/watchchannels/talentpool.py b/bot/cogs/watchchannels/talentpool.py index 62be3bc3b..776f0ea87 100644 --- a/bot/cogs/watchchannels/talentpool.py +++ b/bot/cogs/watchchannels/talentpool.py @@ -1,9 +1,8 @@ import logging import textwrap from collections import ChainMap -from typing import Union -from discord import Color, Embed, Member, User +from discord import Color, Embed, Member from discord.ext.commands import Cog, Context, group from bot.api import ResponseCodeError @@ -50,7 +49,7 @@ class TalentPool(WatchChannel, Cog, name="Talentpool"): @nomination_group.command(name='watch', aliases=('w', 'add', 'a')) @with_role(*STAFF_ROLES) - async def watch_command(self, ctx: Context, user: Union[Member, User, FetchedUser], *, reason: str) -> None: + async def watch_command(self, ctx: Context, user: FetchedUser, *, reason: str) -> None: """ Relay messages sent by the given `user` to the `#talent-pool` channel. @@ -115,7 +114,7 @@ class TalentPool(WatchChannel, Cog, name="Talentpool"): @nomination_group.command(name='history', aliases=('info', 'search')) @with_role(*MODERATION_ROLES) - async def history_command(self, ctx: Context, user: Union[User, FetchedUser]) -> None: + async def history_command(self, ctx: Context, user: FetchedUser) -> None: """Shows the specified user's nomination history.""" result = await self.bot.api_client.get( self.api_endpoint, @@ -144,7 +143,7 @@ class TalentPool(WatchChannel, Cog, name="Talentpool"): @nomination_group.command(name='unwatch', aliases=('end', )) @with_role(*MODERATION_ROLES) - async def unwatch_command(self, ctx: Context, user: Union[User, FetchedUser], *, reason: str) -> None: + async def unwatch_command(self, ctx: Context, user: FetchedUser, *, reason: str) -> None: """ Ends the active nomination of the specified user with the given reason. diff --git a/bot/converters.py b/bot/converters.py index a2e445d74..4fb800a01 100644 --- a/bot/converters.py +++ b/bot/converters.py @@ -9,7 +9,7 @@ import dateutil.tz import discord from aiohttp import ClientConnectorError from dateutil.relativedelta import relativedelta -from discord.ext.commands import BadArgument, Context, Converter +from discord.ext.commands import BadArgument, Context, Converter, UserConverter log = logging.getLogger(__name__) @@ -303,30 +303,46 @@ def proxy_user(user_id: str) -> discord.Object: return user -class FetchedUser(Converter): +class FetchedUser(UserConverter): """ - Fetches from the Discord API and returns a `discord.User` or `discord.Object` object, given an ID. + Converts to a `discord.User` or, if it fails, a `discord.Object`. - If the fetching is successful, a `discord.User` object is returned. If it fails and - the error doesn't imply the user doesn't exist, then a `discord.Object` is returned - via the `user_proxy` function. + Unlike the default `UserConverter`, which only does lookups via the global user cache, this + converter attempts to fetch the user via an API call to Discord when the using the cache is + unsuccessful. + + If the fetch also fails and the error doesn't imply the user doesn't exist, then a + `discord.Object` is returned via the `user_proxy` converter. + + The lookup strategy is as follows (in order): + + 1. Lookup by ID. + 2. Lookup by mention. + 3. Lookup by name#discrim + 4. Lookup by name + 5. Lookup via API + 6. Create a proxy user with discord.Object """ - @staticmethod - async def convert(ctx: Context, user_id: str) -> t.Union[discord.User, discord.Object]: - """Convert `user_id` to a `discord.User` object, after fetching from the Discord API.""" + async def convert(self, ctx: Context, arg: str) -> t.Union[discord.User, discord.Object]: + """Convert the `arg` to a `discord.User` or `discord.Object`.""" + try: + return await super().convert(ctx, arg) + except BadArgument: + pass + try: - user_id = int(user_id) + user_id = int(arg) log.trace(f"Fetching user {user_id}...") return await ctx.bot.fetch_user(user_id) except ValueError: - log.debug(f"Failed to fetch user {user_id}: could not convert to int.") - raise BadArgument(f"The provided argument can't be turned into integer: `{user_id}`") + log.debug(f"Failed to fetch user {arg}: could not convert to int.") + raise BadArgument(f"The provided argument can't be turned into integer: `{arg}`") except discord.HTTPException as e: - # If the Discord error isn't `Unknown user`, save it in the log and return a proxy instead + # If the Discord error isn't `Unknown user`, return a proxy instead if e.code != 10013: log.warning(f"Failed to fetch user, returning a proxy instead: status {e.status}") - return proxy_user(user_id) + return proxy_user(arg) - log.debug(f"Failed to fetch user {user_id}: user does not exist.") - raise BadArgument(f"User `{user_id}` does not exist") + log.debug(f"Failed to fetch user {arg}: user does not exist.") + raise BadArgument(f"User `{arg}` does not exist") |