aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--bot/cogs/alias.py17
-rw-r--r--bot/cogs/moderation/infractions.py18
-rw-r--r--bot/cogs/moderation/management.py4
-rw-r--r--bot/cogs/watchchannels/bigbrother.py6
-rw-r--r--bot/cogs/watchchannels/talentpool.py9
-rw-r--r--bot/converters.py48
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")