aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--bot/exts/moderation/infraction/_scheduler.py28
-rw-r--r--bot/exts/moderation/infraction/infractions.py24
-rw-r--r--tests/bot/exts/moderation/infraction/test_infractions.py8
3 files changed, 49 insertions, 11 deletions
diff --git a/bot/exts/moderation/infraction/_scheduler.py b/bot/exts/moderation/infraction/_scheduler.py
index bd7c4d6f2..4c275a1f0 100644
--- a/bot/exts/moderation/infraction/_scheduler.py
+++ b/bot/exts/moderation/infraction/_scheduler.py
@@ -287,6 +287,7 @@ class InfractionScheduler:
ctx: Context,
infr_type: str,
user: MemberOrUser,
+ pardon_reason: t.Optional[str] = None,
*,
send_msg: bool = True,
notify: bool = True
@@ -294,6 +295,9 @@ class InfractionScheduler:
"""
Prematurely end an infraction for a user and log the action in the mod log.
+ If `pardon_reason` is None, then the database will not receive
+ appended text explaining why the infraction was pardoned.
+
If `send_msg` is True, then a pardoning confirmation message will be sent to
the context channel. Otherwise, no such message will be sent.
@@ -318,7 +322,7 @@ class InfractionScheduler:
return
# Deactivate the infraction and cancel its scheduled expiration task.
- log_text = await self.deactivate_infraction(response[0], send_log=False, notify=notify)
+ log_text = await self.deactivate_infraction(response[0], pardon_reason, send_log=False, notify=notify)
log_text["Member"] = messages.format_user(user)
log_text["Actor"] = ctx.author.mention
@@ -371,6 +375,7 @@ class InfractionScheduler:
async def deactivate_infraction(
self,
infraction: _utils.Infraction,
+ pardon_reason: t.Optional[str] = None,
*,
send_log: bool = True,
notify: bool = True
@@ -379,8 +384,12 @@ class InfractionScheduler:
Deactivate an active infraction and return a dictionary of lines to send in a mod log.
The infraction is removed from Discord, marked as inactive in the database, and has its
- expiration task cancelled. If `send_log` is True, a mod log is sent for the
- deactivation of the infraction.
+ expiration task cancelled.
+
+ If `pardon_reason` is None, then the database will not receive
+ appended text explaining why the infraction was pardoned.
+
+ If `send_log` is True, a mod log is sent for the deactivation of the infraction.
If `notify` is True, notify the user of the pardon via DM where applicable.
@@ -450,9 +459,20 @@ class InfractionScheduler:
try:
# Mark infraction as inactive in the database.
log.trace(f"Marking infraction #{id_} as inactive in the database.")
+
+ data = {"active": False}
+
+ if pardon_reason is not None:
+ data["reason"] = ""
+ # Append pardon reason to infraction in database.
+ if (punish_reason := infraction["reason"]) is not None:
+ data["reason"] = punish_reason + " | "
+
+ data["reason"] += f"Pardoned: {pardon_reason}"
+
await self.bot.api_client.patch(
f"bot/infractions/{id_}",
- json={"active": False}
+ json=data
)
except ResponseCodeError as e:
log.exception(f"Failed to deactivate infraction #{id_} ({type_})")
diff --git a/bot/exts/moderation/infraction/infractions.py b/bot/exts/moderation/infraction/infractions.py
index fb2ab9579..881298408 100644
--- a/bot/exts/moderation/infraction/infractions.py
+++ b/bot/exts/moderation/infraction/infractions.py
@@ -340,14 +340,20 @@ class Infractions(InfractionScheduler, commands.Cog):
# region: Remove infractions (un- commands)
@command()
- async def unmute(self, ctx: Context, user: UnambiguousMemberOrUser) -> None:
+ async def unmute(
+ self,
+ ctx: Context,
+ user: UnambiguousMemberOrUser,
+ *,
+ pardon_reason: t.Optional[str] = None
+ ) -> None:
"""Prematurely end the active mute infraction for the user."""
- await self.pardon_infraction(ctx, "mute", user)
+ await self.pardon_infraction(ctx, "mute", user, pardon_reason)
@command()
- async def unban(self, ctx: Context, user: UnambiguousMemberOrUser) -> None:
+ async def unban(self, ctx: Context, user: UnambiguousMemberOrUser, *, pardon_reason: str) -> None:
"""Prematurely end the active ban infraction for the user."""
- await self.pardon_infraction(ctx, "ban", user)
+ await self.pardon_infraction(ctx, "ban", user, pardon_reason)
@command(aliases=("uvban",))
async def unvoiceban(self, ctx: Context) -> None:
@@ -359,9 +365,15 @@ class Infractions(InfractionScheduler, commands.Cog):
await ctx.send(":x: This command is not yet implemented. Maybe you meant to use `unvoicemute`?")
@command(aliases=("uvmute",))
- async def unvoicemute(self, ctx: Context, user: UnambiguousMemberOrUser) -> None:
+ async def unvoicemute(
+ self,
+ ctx: Context,
+ user: UnambiguousMemberOrUser,
+ *,
+ pardon_reason: t.Optional[str] = None
+ ) -> None:
"""Prematurely end the active voice mute infraction for the user."""
- await self.pardon_infraction(ctx, "voice_mute", user)
+ await self.pardon_infraction(ctx, "voice_mute", user, pardon_reason)
# endregion
# region: Base apply functions
diff --git a/tests/bot/exts/moderation/infraction/test_infractions.py b/tests/bot/exts/moderation/infraction/test_infractions.py
index ca9342550..b78328137 100644
--- a/tests/bot/exts/moderation/infraction/test_infractions.py
+++ b/tests/bot/exts/moderation/infraction/test_infractions.py
@@ -96,8 +96,14 @@ class VoiceMuteTests(unittest.IsolatedAsyncioTestCase):
async def test_voice_unmute(self):
"""Should call infraction pardoning function."""
self.cog.pardon_infraction = AsyncMock()
+ self.assertIsNone(await self.cog.unvoicemute(self.cog, self.ctx, self.user, pardon_reason="foobar"))
+ self.cog.pardon_infraction.assert_awaited_once_with(self.ctx, "voice_mute", self.user, "foobar")
+
+ async def test_voice_unmute_reasonless(self):
+ """Should call infraction pardoning function without a pardon reason."""
+ self.cog.pardon_infraction = AsyncMock()
self.assertIsNone(await self.cog.unvoicemute(self.cog, self.ctx, self.user))
- self.cog.pardon_infraction.assert_awaited_once_with(self.ctx, "voice_mute", self.user)
+ self.cog.pardon_infraction.assert_awaited_once_with(self.ctx, "voice_mute", self.user, None)
@patch("bot.exts.moderation.infraction.infractions._utils.post_infraction")
@patch("bot.exts.moderation.infraction.infractions._utils.get_active_infraction")