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. """ |