aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGravatar MarkKoz <[email protected]>2020-08-11 15:54:47 -0700
committerGravatar MarkKoz <[email protected]>2020-08-11 15:54:47 -0700
commitddd5536c731c57e7f995727fcb631a83d54d09d2 (patch)
tree7b3863f673c1f5642ed20d787fbe8fe58e39e1a4
parentResolve 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.py4
-rw-r--r--bot/converters.py49
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.
"""