Repository: python-discord/bot

Issue:
Add `reason` to infraction message when the actor is the bot itself
We recently removed the `reason` from the infraction confirmation message. The reason was that if the infraction was manually invoked, both the message that contained the command and the confirmation message would contain the `reason`, which would take up a lot of vertical space when the reason had some length.

However, when an infraction was invoked automatically (e.g., by the `AntiSpam` cog), having the reason in the infraction notification actually provided context to what's going on. That context is now missing. (See the examples at the bottom of this issue.)

## Proposal

- Add the `reason` field back to the infraction confirmation message when the actor of the infraction is the bot itself.

**Edit:** @MarkKoz is currently doing major work on the moderation cog. To prevent major merge conflicts, I've added the stalled status for now.

**Note:** Currently, the way the `AntiSpam` cog gets `Context` for the infraction causes the actor to be set to the *offender*, not the bot:

https://github.com/python-discord/bot/blob/71a3ac9382851845dcb26609d64299bd69b0f0f5/bot/cogs/antispam.py#L205-L212

One way to get around that is to patch the `context` object by setting the `author` attribute to the bot itself. This is a fix that should probably happen regardless of the `reason` field, but it makes sense to include it here.

## Examples of before and after

#### Before the removal of `reason`

![mute_reaction](https://user-images.githubusercontent.com/33516116/65955152-ca56ae00-e447-11e9-8530-3f0023237f05.PNG)

#### After the removal of `reason`

![mute_no_reason](https://user-images.githubusercontent.com/33516116/65955170-d2aee900-e447-11e9-857a-827f475a6e51.PNG)

Add `reason` to infraction message when the actor is the bot itself
We recently removed the `reason` from the infraction confirmation message. The reason was that if the infraction was manually invoked, both the message that contained the command and the confirmation message would contain the `reason`, which would take up a lot of vertical space when the reason had some length.

However, when an infraction was invoked automatically (e.g., by the `AntiSpam` cog), having the reason in the infraction notification actually provided context to what's going on. That context is now missing. (See the examples at the bottom of this issue.)

## Proposal

- Add the `reason` field back to the infraction confirmation message when the actor of the infraction is the bot itself.

**Edit:** @MarkKoz is currently doing major work on the moderation cog. To prevent major merge conflicts, I've added the stalled status for now.

**Note:** Currently, the way the `AntiSpam` cog gets `Context` for the infraction causes the actor to be set to the *offender*, not the bot:

https://github.com/python-discord/bot/blob/71a3ac9382851845dcb26609d64299bd69b0f0f5/bot/cogs/antispam.py#L205-L212

One way to get around that is to patch the `context` object by setting the `author` attribute to the bot itself. This is a fix that should probably happen regardless of the `reason` field, but it makes sense to include it here.

## Examples of before and after

#### Before the removal of `reason`

![mute_reaction](https://user-images.githubusercontent.com/33516116/65955152-ca56ae00-e447-11e9-8530-3f0023237f05.PNG)

#### After the removal of `reason`

![mute_no_reason](https://user-images.githubusercontent.com/33516116/65955170-d2aee900-e447-11e9-857a-827f475a6e51.PNG)



Discussion:

Resolving patch:
diff --git a/bot/cogs/antispam.py b/bot/cogs/antispam.py
--- a/bot/cogs/antispam.py
+++ b/bot/cogs/antispam.py
@@ -207,8 +207,10 @@ async def punish(self, msg: Message, member: Member, reason: str) -> None:
         if not any(role.id == self.muted_role.id for role in member.roles):
             remove_role_after = AntiSpamConfig.punishment['remove_after']
 
-            # We need context, let's get it
+            # Get context and make sure the bot becomes the actor of infraction by patching the `author` attributes
             context = await self.bot.get_context(msg)
+            context.author = self.bot.user
+            context.message.author = self.bot.user
 
             # Since we're going to invoke the tempmute command directly, we need to manually call the converter.
             dt_remove_role_after = await self.expiration_date_converter.convert(context, f"{remove_role_after}S")
diff --git a/bot/cogs/moderation/infractions.py b/bot/cogs/moderation/infractions.py
--- a/bot/cogs/moderation/infractions.py
+++ b/bot/cogs/moderation/infractions.py
@@ -416,6 +416,7 @@ async def apply_infraction(
         expiry_log_text = f"Expires: {expiry}" if expiry else ""
         log_title = "applied"
         log_content = None
+        reason_msg = ""
 
         # DM the user about the infraction if it's not a shadow/hidden infraction.
         if not infraction["hidden"]:
@@ -430,6 +431,9 @@ async def apply_infraction(
                 dm_log_text = "\nDM: **Failed**"
                 log_content = ctx.author.mention
 
+        if infraction["actor"] == self.bot.user.id:
+            reason_msg = f" (reason: {infraction['reason']})"
+
         # Execute the necessary actions to apply the infraction on Discord.
         if action_coro:
             try:
@@ -445,7 +449,7 @@ async def apply_infraction(
                 log_title = "failed to apply"
 
         # Send a confirmation message to the invoking context.
-        await ctx.send(f"{dm_result}{confirm_msg} **{infr_type}** to {user.mention}{expiry_msg}.")
+        await ctx.send(f"{dm_result}{confirm_msg} **{infr_type}** to {user.mention}{expiry_msg}{reason_msg}.")
 
         # Send a log message to the mod log.
         await self.mod_log.send_log_message(


Test patch: