diff options
| author | 2022-10-07 16:53:52 +0300 | |
|---|---|---|
| committer | 2022-10-07 16:53:52 +0300 | |
| commit | 7e227f6a71458bd6b8126332d1d9da73bfa7bfeb (patch) | |
| tree | 824889fc5e8283410f6cd3ed5586111c921364c5 | |
| parent | domain/exact -> domain/subdomains (diff) | |
Make auto-infraction actually work
| -rw-r--r-- | bot/exts/filtering/_settings_types/actions/infraction_and_notification.py | 80 | ||||
| -rw-r--r-- | bot/exts/moderation/infraction/infractions.py | 2 |
2 files changed, 66 insertions, 16 deletions
diff --git a/bot/exts/filtering/_settings_types/actions/infraction_and_notification.py b/bot/exts/filtering/_settings_types/actions/infraction_and_notification.py index 4fcf2aa65..d4095590e 100644 --- a/bot/exts/filtering/_settings_types/actions/infraction_and_notification.py +++ b/bot/exts/filtering/_settings_types/actions/infraction_and_notification.py @@ -1,18 +1,52 @@ +from dataclasses import dataclass from datetime import timedelta from enum import Enum, auto from typing import ClassVar import arrow -from discord import Colour, Embed +import discord.abc +from discord import Colour, Embed, Member, User from discord.errors import Forbidden from pydantic import validator -import bot +import bot as bot_module from bot.constants import Channels, Guild from bot.exts.filtering._filter_context import FilterContext from bot.exts.filtering._settings_types.settings_entry import ActionEntry +@dataclass +class FakeContext: + """ + A class representing a context-like object that can be sent to infraction commands. + + The goal is to be able to apply infractions without depending on the existence of a message or an interaction + (which are the two ways to create a Context), e.g. in API events which aren't message-driven, or in custom filtering + events. + """ + + channel: discord.abc.Messageable + bot: bot_module.bot.Bot | None = None + guild: discord.Guild | None = None + author: discord.Member | discord.User | None = None + me: discord.Member | None = None + + def __post_init__(self): + """Initialize the missing information.""" + if not self.bot: + self.bot = bot_module.instance + if not self.guild: + self.guild = self.bot.get_guild(Guild.id) + if not self.me: + self.me = self.guild.me + if not self.author: + self.author = self.me + + async def send(self, *args, **kwargs) -> discord.Message: + """A wrapper for channel.send.""" + return await self.channel.send(*args, **kwargs) + + class Infraction(Enum): """An enumeration of infraction types. The lower the value, the higher it is on the hierarchy.""" @@ -28,6 +62,30 @@ class Infraction(Enum): def __str__(self) -> str: return self.name + async def invoke( + self, + user: Member | User, + channel: discord.abc.Messageable | None, + duration: float | None = None, + reason: str | None = None + ) -> None: + """Invokes the command matching the infraction name.""" + alerts_channel = bot_module.instance.get_channel(Channels.mod_alerts) + if not channel: + channel = alerts_channel + + command_name = self.name.lower() + command = bot_module.instance.get_command(command_name) + if not command: + await alerts_channel.send(f":warning: Could not apply {command_name} to {user.mention}: command not found.") + + ctx = FakeContext(channel) + if self.name in ("KICK", "WARNING", "WATCH", "NOTE"): + await command(ctx, user, reason=reason) + else: + duration = arrow.utcnow() + timedelta(seconds=duration) if duration else None + await command(ctx, user, duration, reason=reason) + class InfractionAndNotification(ActionEntry): """ @@ -87,21 +145,13 @@ class InfractionAndNotification(ActionEntry): except Forbidden: ctx.action_descriptions.append("notified (failed)") - msg_ctx = await bot.instance.get_context(ctx.message) - msg_ctx.guild = bot.instance.get_guild(Guild.id) - msg_ctx.author = ctx.author - msg_ctx.channel = ctx.channel - if self.infraction_type is not None: if self.infraction_type == Infraction.BAN or not hasattr(ctx.channel, "guild"): - msg_ctx.channel = bot.instance.get_channel(Channels.mod_alerts) - msg_ctx.command = bot.instance.get_command(self.infraction_type.name.lower()) - await msg_ctx.invoke( - msg_ctx.command, - ctx.author, - arrow.utcnow() + timedelta(seconds=self.infraction_duration) - if self.infraction_duration is not None else None, - reason=self.infraction_reason + infrac_channel = None + else: + infrac_channel = ctx.channel + await self.infraction_type.invoke( + ctx.author, infrac_channel, self.infraction_duration, self.infraction_reason ) ctx.action_descriptions.append(self.infraction_type.name.lower()) diff --git a/bot/exts/moderation/infraction/infractions.py b/bot/exts/moderation/infraction/infractions.py index 999f9ba7f..1ef43a945 100644 --- a/bot/exts/moderation/infraction/infractions.py +++ b/bot/exts/moderation/infraction/infractions.py @@ -79,7 +79,7 @@ class Infractions(InfractionScheduler, commands.Cog): # region: Permanent infractions - @command() + @command(aliases=("warning",)) async def warn(self, ctx: Context, user: UnambiguousMemberOrUser, *, reason: t.Optional[str] = None) -> None: """Warn a user for the given reason.""" if not isinstance(user, Member): |