diff options
| author | 2020-08-11 15:54:47 -0700 | |
|---|---|---|
| committer | 2020-08-11 15:54:47 -0700 | |
| commit | ddd5536c731c57e7f995727fcb631a83d54d09d2 (patch) | |
| tree | 7b3863f673c1f5642ed20d787fbe8fe58e39e1a4 | |
| parent | Resolve conflicts in filtering.py (diff) | |
Replace InfractionSearchQuery with a generic Snowflake converter
It's unnecessarily precise to do an fetch user API call in order to
distinguish between a user and a reason. Furthermore, a User object
isn't actually required for infraction searches - only an ID is.
| -rw-r--r-- | bot/cogs/moderation/management.py | 4 | ||||
| -rw-r--r-- | bot/converters.py | 49 | 
2 files changed, 40 insertions, 13 deletions
| diff --git a/bot/cogs/moderation/management.py b/bot/cogs/moderation/management.py index 56a601cb7..af736d4de 100644 --- a/bot/cogs/moderation/management.py +++ b/bot/cogs/moderation/management.py @@ -10,7 +10,7 @@ from discord.utils import escape_markdown  from bot import constants  from bot.bot import Bot -from bot.converters import Expiry, InfractionSearchQuery, allowed_strings, proxy_user +from bot.converters import Expiry, Snowflake, allowed_strings, proxy_user  from bot.pagination import LinePaginator  from bot.utils import messages, time  from bot.utils.checks import in_whitelist_check, with_role_check @@ -177,7 +177,7 @@ class ModManagement(commands.Cog):      # region: Search infractions      @infraction_group.group(name="search", invoke_without_command=True) -    async def infraction_search_group(self, ctx: Context, query: InfractionSearchQuery) -> None: +    async def infraction_search_group(self, ctx: Context, query: Snowflake) -> None:          """Searches for infractions in the database."""          if isinstance(query, discord.User):              await ctx.invoke(self.search_user, query) diff --git a/bot/converters.py b/bot/converters.py index 1358cbf1e..4c41d0ece 100644 --- a/bot/converters.py +++ b/bot/converters.py @@ -10,6 +10,7 @@ import discord  from aiohttp import ClientConnectorError  from dateutil.relativedelta import relativedelta  from discord.ext.commands import BadArgument, Bot, Context, Converter, IDConverter, UserConverter +from discord.utils import DISCORD_EPOCH, snowflake_time  from bot.api import ResponseCodeError  from bot.constants import URLs @@ -17,6 +18,8 @@ from bot.utils.regex import INVITE_RE  log = logging.getLogger(__name__) +DISCORD_EPOCH_DT = datetime.utcfromtimestamp(DISCORD_EPOCH / 1000) +  def allowed_strings(*values, preserve_case: bool = False) -> t.Callable[[str], str]:      """ @@ -172,17 +175,42 @@ class ValidURL(Converter):          return url -class InfractionSearchQuery(Converter): -    """A converter that checks if the argument is a Discord user, and if not, falls back to a string.""" +class Snowflake(IDConverter): +    """ +    Converts to an int if the argument is a valid Discord snowflake. + +    A snowflake is valid if: + +    * It consists of 15-21 digits (0-9) +    * Its parsed datetime is after the Discord epoch +    * Its parsed datetime is less than 1 day after the current time +    """ + +    async def convert(self, ctx: Context, arg: str) -> int: +        """ +        Ensure `arg` matches the ID pattern and its timestamp is in range. + +        Return `arg` as an int if it's a valid snowflake. +        """ +        error = f"Invalid snowflake {arg!r}" + +        if not self._get_id_match(arg): +            raise BadArgument(error) + +        snowflake = int(arg) -    @staticmethod -    async def convert(ctx: Context, arg: str) -> t.Union[discord.Member, str]: -        """Check if the argument is a Discord user, and if not, falls back to a string."""          try: -            maybe_snowflake = arg.strip("<@!>") -            return await ctx.bot.fetch_user(maybe_snowflake) -        except (discord.NotFound, discord.HTTPException): -            return arg +            time = snowflake_time(snowflake) +        except (OverflowError, OSError) as e: +            # Not sure if this can ever even happen, but let's be safe. +            raise BadArgument(f"{error}: {e}") + +        if time < DISCORD_EPOCH_DT: +            raise BadArgument(f"{error}: timestamp is before the Discord epoch.") +        elif (datetime.utcnow() - time).days >= 1: +            raise BadArgument(f"{error}: timestamp is too far into the future.") + +        return snowflake  class Subreddit(Converter): @@ -447,8 +475,7 @@ class UserMentionOrID(UserConverter):      """      Converts to a `discord.User`, but only if a mention or userID is provided. -    Unlike the default `UserConverter`, it does allow conversion from name, or name#descrim. - +    Unlike the default `UserConverter`, it doesn't allow conversion from a name or name#descrim.      This is useful in cases where that lookup strategy would lead to ambiguity.      """ | 
