diff options
| author | 2019-10-21 17:33:27 -0700 | |
|---|---|---|
| committer | 2019-10-25 15:32:20 -0700 | |
| commit | 832c7129d57a919f15919e883607845df6b6dacc (patch) | |
| tree | 4aceafdc489a484f352aba5a198a41529edb6b3b | |
| parent | Moderation: create a class to handle scheduling of infractions (diff) | |
InfractionScheduler: delegate type-specific pardon code to subclasses
* Create an abstract method for performing type-specific infraction
deactivation
* Move infraction-specific pardon code to separate functions
| -rw-r--r-- | bot/cogs/moderation/infractions.py | 61 | ||||
| -rw-r--r-- | bot/cogs/moderation/scheduler.py | 51 |
2 files changed, 77 insertions, 35 deletions
diff --git a/bot/cogs/moderation/infractions.py b/bot/cogs/moderation/infractions.py index 9d65dfa8a..fbfcb8ae3 100644 --- a/bot/cogs/moderation/infractions.py +++ b/bot/cogs/moderation/infractions.py @@ -209,7 +209,7 @@ class Infractions(InfractionScheduler, commands.Cog): await self.pardon_infraction(ctx, "ban", user) # endregion - # region: Base infraction functions + # region: Base apply functions async def apply_mute(self, ctx: Context, user: Member, reason: str, **kwargs) -> None: """Apply a mute infraction with kwargs passed to `post_infraction`.""" @@ -253,6 +253,65 @@ class Infractions(InfractionScheduler, commands.Cog): await self.apply_infraction(ctx, infraction, user, action) # endregion + # region: Base pardon functions + + async def pardon_mute(self, user_id: int, guild: discord.Guild, reason: str) -> t.Dict[str, str]: + """Remove a user's muted role, DM them a notification, and return a log dict.""" + user = guild.get_member(user_id) + log_text = {} + + if user: + # Remove the muted role. + self.mod_log.ignore(Event.member_update, user.id) + await user.remove_roles(self._muted_role, reason=reason) + + # DM the user about the expiration. + notified = await utils.notify_pardon( + user=user, + title="You have been unmuted.", + content="You may now send messages in the server.", + icon_url=utils.INFRACTION_ICONS["mute"][1] + ) + + log_text["Member"] = f"{user.mention}(`{user.id}`)" + log_text["DM"] = "Sent" if notified else "**Failed**" + else: + log.info(f"Failed to unmute user {user_id}: user not found") + log_text["Failure"] = "User was not found in the guild." + + return log_text + + async def pardon_ban(self, user_id: int, guild: discord.Guild, reason: str) -> t.Dict[str, str]: + """Remove a user's ban on the Discord guild and return a log dict.""" + user = discord.Object(user_id) + log_text = {} + + self.mod_log.ignore(Event.member_unban, user_id) + + try: + await guild.unban(user, reason=reason) + except discord.NotFound: + log.info(f"Failed to unban user {user_id}: no active ban found on Discord") + log_text["Note"] = "No active ban found on Discord." + + return log_text + + async def _pardon_action(self, infraction: utils.Infraction) -> t.Optional[t.Dict[str, str]]: + """ + Execute deactivation steps specific to the infraction's type and return a log dict. + + If an infraction type is unsupported, return None instead. + """ + guild = self.bot.get_guild(constants.Guild.id) + user_id = infraction["user"] + reason = f"Infraction #{infraction['id']} expired or was pardoned." + + if infraction["type"] == "mute": + return await self.pardon_mute(user_id, guild, reason) + elif infraction["type"] == "ban": + return await self.pardon_ban(user_id, guild, reason) + + # endregion # This cannot be static (must have a __func__ attribute). def cog_check(self, ctx: Context) -> bool: diff --git a/bot/cogs/moderation/scheduler.py b/bot/cogs/moderation/scheduler.py index 408e9943e..045b0bd13 100644 --- a/bot/cogs/moderation/scheduler.py +++ b/bot/cogs/moderation/scheduler.py @@ -1,16 +1,16 @@ import logging import textwrap import typing as t +from abc import abstractmethod from gettext import ngettext import dateutil.parser import discord -from discord.ext import commands -from discord.ext.commands import Context +from discord.ext.commands import Bot, Context from bot import constants from bot.api import ResponseCodeError -from bot.constants import Colours, Event, STAFF_CHANNELS +from bot.constants import Colours, STAFF_CHANNELS from bot.utils import time from bot.utils.scheduling import Scheduler from . import utils @@ -23,7 +23,7 @@ log = logging.getLogger(__name__) class InfractionScheduler(Scheduler): """Handles the application, pardoning, and expiration of infractions.""" - def __init__(self, bot: commands.Bot): + def __init__(self, bot: Bot): super().__init__() self.bot = bot @@ -240,14 +240,13 @@ class InfractionScheduler(Scheduler): expiration task cancelled. If `send_log` is True, a mod log is sent for the deactivation of the infraction. - Supported infraction types are mute and ban. Other types will raise a ValueError. + Infractions of unsupported types will raise a ValueError. """ guild = self.bot.get_guild(constants.Guild.id) mod_role = guild.get_role(constants.Roles.moderator) user_id = infraction["user"] _type = infraction["type"] _id = infraction["id"] - reason = f"Infraction #{_id} expired or was pardoned." log.debug(f"Marking infraction #{_id} as inactive (expired).") @@ -259,34 +258,9 @@ class InfractionScheduler(Scheduler): } try: - if _type == "mute": - user = guild.get_member(user_id) - if user: - # Remove the muted role. - self.mod_log.ignore(Event.member_update, user.id) - await user.remove_roles(self._muted_role, reason=reason) - - # DM the user about the expiration. - notified = await utils.notify_pardon( - user=user, - title="You have been unmuted.", - content="You may now send messages in the server.", - icon_url=utils.INFRACTION_ICONS["mute"][1] - ) - - log_text["Member"] = f"{user.mention}(`{user.id}`)" - log_text["DM"] = "Sent" if notified else "**Failed**" - else: - log.info(f"Failed to unmute user {user_id}: user not found") - log_text["Failure"] = "User was not found in the guild." - elif _type == "ban": - user = discord.Object(user_id) - self.mod_log.ignore(Event.member_unban, user_id) - try: - await guild.unban(user, reason=reason) - except discord.NotFound: - log.info(f"Failed to unban user {user_id}: no active ban found on Discord") - log_text["Note"] = "No active ban found on Discord." + returned_log = await self._pardon_action(infraction) + if returned_log is not None: + log_text = {**log_text, **returned_log} # Merge the logs together else: raise ValueError( f"Attempted to deactivate an unsupported infraction #{_id} ({_type})!" @@ -352,6 +326,15 @@ class InfractionScheduler(Scheduler): return log_text + @abstractmethod + async def _pardon_action(self, infraction: utils.Infraction) -> t.Optional[t.Dict[str, str]]: + """ + Execute deactivation steps specific to the infraction's type and return a log dict. + + If an infraction type is unsupported, return None instead. + """ + raise NotImplementedError + async def _scheduled_task(self, infraction: utils.Infraction) -> None: """ Marks an infraction expired after the delay from time of scheduling to time of expiration. |