From 85d8ae441c0f4e34fac8fd4323ca7da9dd507582 Mon Sep 17 00:00:00 2001 From: Luna Date: Wed, 17 Aug 2022 17:07:05 -0400 Subject: feat: add reason argument to pardon commands --- bot/exts/moderation/infraction/_scheduler.py | 22 ++++++++++++++++++---- bot/exts/moderation/infraction/infractions.py | 12 ++++++------ 2 files changed, 24 insertions(+), 10 deletions(-) diff --git a/bot/exts/moderation/infraction/_scheduler.py b/bot/exts/moderation/infraction/_scheduler.py index c7f03b2e9..d733c3936 100644 --- a/bot/exts/moderation/infraction/_scheduler.py +++ b/bot/exts/moderation/infraction/_scheduler.py @@ -271,6 +271,7 @@ class InfractionScheduler: ctx: Context, infr_type: str, user: MemberOrUser, + pardon_reason: t.Optional[str] = None, *, send_msg: bool = True, notify: bool = True @@ -278,6 +279,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 infraction object 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. @@ -302,7 +306,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 @@ -355,6 +359,7 @@ class InfractionScheduler: async def deactivate_infraction( self, infraction: _utils.Infraction, + pardon_reason: t.Optional[str] = None, *, send_log: bool = True, notify: bool = True @@ -362,6 +367,9 @@ class InfractionScheduler: """ Deactivate an active infraction and return a dictionary of lines to send in a mod log. + If `pardon_reason` is None, then the infraction object will not receive + appended text explaining why the infraction was pardoned. + 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. @@ -386,7 +394,7 @@ class InfractionScheduler: "Reason": infraction["reason"], "Created": time.format_with_duration(infraction["inserted_at"], infraction["expires_at"]), } - + try: log.trace("Awaiting the pardon action coroutine.") returned_log = await self._pardon_action(infraction, notify) @@ -430,13 +438,19 @@ class InfractionScheduler: except ResponseCodeError: log.exception(f"Failed to fetch watch status for user {user_id}") log_text["Watching"] = "Unknown - failed to fetch watch status." - + 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"] = infraction["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 46fd3381c..a94da0d7c 100644 --- a/bot/exts/moderation/infraction/infractions.py +++ b/bot/exts/moderation/infraction/infractions.py @@ -330,14 +330,14 @@ 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: t.Optional[str] = None) -> 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: @@ -349,9 +349,9 @@ 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 -- cgit v1.2.3 From 6ca8e4822665832609208bd02f2201d5e584479c Mon Sep 17 00:00:00 2001 From: Luna Date: Wed, 17 Aug 2022 17:11:35 -0400 Subject: fix: lint --- bot/exts/moderation/infraction/_scheduler.py | 4 ++-- bot/exts/moderation/infraction/infractions.py | 24 +++++++++++++++++++++--- 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/bot/exts/moderation/infraction/_scheduler.py b/bot/exts/moderation/infraction/_scheduler.py index d733c3936..a6846e239 100644 --- a/bot/exts/moderation/infraction/_scheduler.py +++ b/bot/exts/moderation/infraction/_scheduler.py @@ -394,7 +394,7 @@ class InfractionScheduler: "Reason": infraction["reason"], "Created": time.format_with_duration(infraction["inserted_at"], infraction["expires_at"]), } - + try: log.trace("Awaiting the pardon action coroutine.") returned_log = await self._pardon_action(infraction, notify) @@ -438,7 +438,7 @@ class InfractionScheduler: except ResponseCodeError: log.exception(f"Failed to fetch watch status for user {user_id}") log_text["Watching"] = "Unknown - failed to fetch watch status." - + try: # Mark infraction as inactive in the database. log.trace(f"Marking infraction #{id_} as inactive in the database.") diff --git a/bot/exts/moderation/infraction/infractions.py b/bot/exts/moderation/infraction/infractions.py index a94da0d7c..8e5ee8200 100644 --- a/bot/exts/moderation/infraction/infractions.py +++ b/bot/exts/moderation/infraction/infractions.py @@ -330,12 +330,24 @@ class Infractions(InfractionScheduler, commands.Cog): # region: Remove infractions (un- commands) @command() - async def unmute(self, ctx: Context, user: UnambiguousMemberOrUser, *, pardon_reason: t.Optional[str] = None) -> 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, pardon_reason) @command() - async def unban(self, ctx: Context, user: UnambiguousMemberOrUser, *, pardon_reason: t.Optional[str] = None) -> None: + async def unban( + self, + ctx: Context, + user: UnambiguousMemberOrUser, + *, + pardon_reason: t.Optional[str] = None + ) -> None: """Prematurely end the active ban infraction for the user.""" await self.pardon_infraction(ctx, "ban", user, pardon_reason) @@ -349,7 +361,13 @@ 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, *, pardon_reason: t.Optional[str] = None) -> 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, pardon_reason) -- cgit v1.2.3 From e7188bcead99c74207b61d4f1e05f061c843c423 Mon Sep 17 00:00:00 2001 From: Luna Date: Wed, 17 Aug 2022 17:17:55 -0400 Subject: fix: docstrings --- bot/exts/moderation/infraction/_scheduler.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/bot/exts/moderation/infraction/_scheduler.py b/bot/exts/moderation/infraction/_scheduler.py index a6846e239..b0a2e3359 100644 --- a/bot/exts/moderation/infraction/_scheduler.py +++ b/bot/exts/moderation/infraction/_scheduler.py @@ -279,7 +279,7 @@ class InfractionScheduler: """ Prematurely end an infraction for a user and log the action in the mod log. - If `pardon_reason` is None, then the infraction object will not receive + 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 @@ -367,12 +367,13 @@ class InfractionScheduler: """ Deactivate an active infraction and return a dictionary of lines to send in a mod log. - If `pardon_reason` is None, then the infraction object will not receive - appended text explaining why the infraction was pardoned. - 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. -- cgit v1.2.3 From 1928ab280257d375d5da53f626d25f8a0c84ea18 Mon Sep 17 00:00:00 2001 From: Luna Date: Wed, 17 Aug 2022 17:43:41 -0400 Subject: fix: trailing whitespace --- bot/exts/moderation/infraction/_scheduler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/moderation/infraction/_scheduler.py b/bot/exts/moderation/infraction/_scheduler.py index b0a2e3359..557fda25a 100644 --- a/bot/exts/moderation/infraction/_scheduler.py +++ b/bot/exts/moderation/infraction/_scheduler.py @@ -372,7 +372,7 @@ class InfractionScheduler: 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. -- cgit v1.2.3 From 9434646723e3ab38543576194f2b5fc0f2e680e0 Mon Sep 17 00:00:00 2001 From: Luna Date: Wed, 17 Aug 2022 18:02:21 -0400 Subject: add: test for reasoned and reasonless pardons --- tests/bot/exts/moderation/infraction/test_infractions.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/tests/bot/exts/moderation/infraction/test_infractions.py b/tests/bot/exts/moderation/infraction/test_infractions.py index 052048053..c9006588c 100644 --- a/tests/bot/exts/moderation/infraction/test_infractions.py +++ b/tests/bot/exts/moderation/infraction/test_infractions.py @@ -90,8 +90,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") -- cgit v1.2.3 From ab4c5e9ec2d65df0c3035ef7ca75bec8a518322c Mon Sep 17 00:00:00 2001 From: Luna Date: Thu, 18 Aug 2022 11:16:22 -0400 Subject: change: make unban require pardon reason --- bot/exts/moderation/infraction/infractions.py | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/bot/exts/moderation/infraction/infractions.py b/bot/exts/moderation/infraction/infractions.py index 8e5ee8200..59d8519b7 100644 --- a/bot/exts/moderation/infraction/infractions.py +++ b/bot/exts/moderation/infraction/infractions.py @@ -341,13 +341,7 @@ class Infractions(InfractionScheduler, commands.Cog): await self.pardon_infraction(ctx, "mute", user, pardon_reason) @command() - async def unban( - self, - ctx: Context, - user: UnambiguousMemberOrUser, - *, - pardon_reason: t.Optional[str] = None - ) -> 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, pardon_reason) -- cgit v1.2.3 From 2e34dbd385b592b76f69c87154d412ba91c00901 Mon Sep 17 00:00:00 2001 From: Luna Date: Thu, 18 Aug 2022 15:14:02 -0400 Subject: fix: add check to prevent NoneType from passing into str concatenation --- bot/exts/moderation/infraction/_scheduler.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/bot/exts/moderation/infraction/_scheduler.py b/bot/exts/moderation/infraction/_scheduler.py index 557fda25a..864aff9b2 100644 --- a/bot/exts/moderation/infraction/_scheduler.py +++ b/bot/exts/moderation/infraction/_scheduler.py @@ -443,11 +443,15 @@ class InfractionScheduler: try: # Mark infraction as inactive in the database. log.trace(f"Marking infraction #{id_} as inactive in the database.") - - data = {"active": False} - + + data = {"active": False, "reason": ""} + if pardon_reason is not None: - data["reason"] = infraction["reason"] + f" | Pardoned: {pardon_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_}", -- cgit v1.2.3 From e64572fcfa91b7785b1ac37d5fd54b9e5f479401 Mon Sep 17 00:00:00 2001 From: Luna Date: Thu, 18 Aug 2022 15:15:36 -0400 Subject: fix: lint (again) --- bot/exts/moderation/infraction/_scheduler.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/bot/exts/moderation/infraction/_scheduler.py b/bot/exts/moderation/infraction/_scheduler.py index 864aff9b2..4d06c6b16 100644 --- a/bot/exts/moderation/infraction/_scheduler.py +++ b/bot/exts/moderation/infraction/_scheduler.py @@ -443,14 +443,14 @@ class InfractionScheduler: try: # Mark infraction as inactive in the database. log.trace(f"Marking infraction #{id_} as inactive in the database.") - + data = {"active": False, "reason": ""} - + if pardon_reason is not None: # 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( -- cgit v1.2.3 From ea0f1af039e3fafea8456abd50d60de93ab8fca2 Mon Sep 17 00:00:00 2001 From: Luna Date: Thu, 18 Aug 2022 21:12:13 -0400 Subject: fix: remove chance of empty string overriding database infraction reason --- bot/exts/moderation/infraction/_scheduler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/moderation/infraction/_scheduler.py b/bot/exts/moderation/infraction/_scheduler.py index 4d06c6b16..bc944aa8e 100644 --- a/bot/exts/moderation/infraction/_scheduler.py +++ b/bot/exts/moderation/infraction/_scheduler.py @@ -444,7 +444,7 @@ class InfractionScheduler: # Mark infraction as inactive in the database. log.trace(f"Marking infraction #{id_} as inactive in the database.") - data = {"active": False, "reason": ""} + data = {"active": False} if pardon_reason is not None: # Append pardon reason to infraction in database. -- cgit v1.2.3 From bf0a5172857cb4a9027d68cd8d0b735565952ce4 Mon Sep 17 00:00:00 2001 From: Luna Date: Fri, 19 Aug 2022 13:17:04 -0400 Subject: fix: data dictionary guarantees reason key existence if pardon reason exists --- bot/exts/moderation/infraction/_scheduler.py | 1 + 1 file changed, 1 insertion(+) diff --git a/bot/exts/moderation/infraction/_scheduler.py b/bot/exts/moderation/infraction/_scheduler.py index bc944aa8e..db1204a8d 100644 --- a/bot/exts/moderation/infraction/_scheduler.py +++ b/bot/exts/moderation/infraction/_scheduler.py @@ -447,6 +447,7 @@ class InfractionScheduler: 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 + " | " -- cgit v1.2.3