Repository: python-discord/bot

Issue:
Upon receiving a non-ban infraction, the DM from the bot should tell them about ModMail
Currently, users get a message like this one when they get an infraction:

![image](https://user-images.githubusercontent.com/32915757/104851448-0c1c9980-58c3-11eb-98cc-b80f25f9dbbb.png)

For infractions other than bans (regardless of duration), the embed should tell them to use the ModMail bot if they want to discuss or ask questions about the infraction. We might also except Superstarify infractions.

I'm not sure if this should await the redesign of the infraction system or if we can implement it without sabotaging Scragly's efforts.


Discussion:
Seems like something we can go for

Resolving patch:
diff --git a/bot/exts/moderation/infraction/_utils.py b/bot/exts/moderation/infraction/_utils.py
--- a/bot/exts/moderation/infraction/_utils.py
+++ b/bot/exts/moderation/infraction/_utils.py
@@ -22,7 +22,6 @@
     "voice_ban": (Icons.voice_state_red, Icons.voice_state_green),
 }
 RULES_URL = "https://pythondiscord.com/pages/rules"
-APPEALABLE_INFRACTIONS = ("ban", "mute", "voice_ban")
 
 # Type aliases
 UserObject = t.Union[discord.Member, discord.User]
@@ -31,8 +30,12 @@
 
 APPEAL_EMAIL = "appeals@pythondiscord.com"
 
-INFRACTION_TITLE = f"Please review our rules over at {RULES_URL}"
-INFRACTION_APPEAL_FOOTER = f"To appeal this infraction, send an e-mail to {APPEAL_EMAIL}"
+INFRACTION_TITLE = "Please review our rules"
+INFRACTION_APPEAL_EMAIL_FOOTER = f"To appeal this infraction, send an e-mail to {APPEAL_EMAIL}"
+INFRACTION_APPEAL_MODMAIL_FOOTER = (
+    'If you would like to discuss or appeal this infraction, '
+    'send a message to the ModMail bot'
+)
 INFRACTION_AUTHOR_NAME = "Infraction information"
 
 INFRACTION_DESCRIPTION_TEMPLATE = (
@@ -71,13 +74,13 @@ async def post_user(ctx: Context, user: UserSnowflake) -> t.Optional[dict]:
 
 
 async def post_infraction(
-    ctx: Context,
-    user: UserSnowflake,
-    infr_type: str,
-    reason: str,
-    expires_at: datetime = None,
-    hidden: bool = False,
-    active: bool = True
+        ctx: Context,
+        user: UserSnowflake,
+        infr_type: str,
+        reason: str,
+        expires_at: datetime = None,
+        hidden: bool = False,
+        active: bool = True
 ) -> t.Optional[dict]:
     """Posts an infraction to the API."""
     if isinstance(user, (discord.Member, discord.User)) and user.bot:
@@ -150,11 +153,11 @@ async def get_active_infraction(
 
 
 async def notify_infraction(
-    user: UserObject,
-    infr_type: str,
-    expires_at: t.Optional[str] = None,
-    reason: t.Optional[str] = None,
-    icon_url: str = Icons.token_removed
+        user: UserObject,
+        infr_type: str,
+        expires_at: t.Optional[str] = None,
+        reason: t.Optional[str] = None,
+        icon_url: str = Icons.token_removed
 ) -> bool:
     """DM a user about their new infraction and return True if the DM is successful."""
     log.trace(f"Sending {user} a DM about their {infr_type} infraction.")
@@ -178,17 +181,18 @@ async def notify_infraction(
     embed.title = INFRACTION_TITLE
     embed.url = RULES_URL
 
-    if infr_type in APPEALABLE_INFRACTIONS:
-        embed.set_footer(text=INFRACTION_APPEAL_FOOTER)
+    embed.set_footer(
+        text=INFRACTION_APPEAL_EMAIL_FOOTER if infr_type == 'Ban' else INFRACTION_APPEAL_MODMAIL_FOOTER
+    )
 
     return await send_private_embed(user, embed)
 
 
 async def notify_pardon(
-    user: UserObject,
-    title: str,
-    content: str,
-    icon_url: str = Icons.user_verified
+        user: UserObject,
+        title: str,
+        content: str,
+        icon_url: str = Icons.user_verified
 ) -> bool:
     """DM a user about their pardoned infraction and return True if the DM is successful."""
     log.trace(f"Sending {user} a DM about their pardoned infraction.")


Test patch:
diff --git a/tests/bot/exts/moderation/infraction/test_utils.py b/tests/bot/exts/moderation/infraction/test_utils.py
--- a/tests/bot/exts/moderation/infraction/test_utils.py
+++ b/tests/bot/exts/moderation/infraction/test_utils.py
@@ -146,7 +146,7 @@ async def test_notify_infraction(self, send_private_embed_mock):
                     name=utils.INFRACTION_AUTHOR_NAME,
                     url=utils.RULES_URL,
                     icon_url=Icons.token_removed
-                ).set_footer(text=utils.INFRACTION_APPEAL_FOOTER),
+                ).set_footer(text=utils.INFRACTION_APPEAL_MODMAIL_FOOTER),
                 "send_result": True
             },
             {
@@ -164,9 +164,11 @@ async def test_notify_infraction(self, send_private_embed_mock):
                     name=utils.INFRACTION_AUTHOR_NAME,
                     url=utils.RULES_URL,
                     icon_url=Icons.token_removed
-                ),
+                ).set_footer(text=utils.INFRACTION_APPEAL_MODMAIL_FOOTER),
                 "send_result": False
             },
+            # Note that this test case asserts that the DM that *would* get sent to the user is formatted
+            # correctly, even though that message is deliberately never sent.
             {
                 "args": (self.user, "note", None, None, Icons.defcon_denied),
                 "expected_output": Embed(
@@ -182,7 +184,7 @@ async def test_notify_infraction(self, send_private_embed_mock):
                     name=utils.INFRACTION_AUTHOR_NAME,
                     url=utils.RULES_URL,
                     icon_url=Icons.defcon_denied
-                ),
+                ).set_footer(text=utils.INFRACTION_APPEAL_MODMAIL_FOOTER),
                 "send_result": False
             },
             {
@@ -200,7 +202,7 @@ async def test_notify_infraction(self, send_private_embed_mock):
                     name=utils.INFRACTION_AUTHOR_NAME,
                     url=utils.RULES_URL,
                     icon_url=Icons.defcon_denied
-                ).set_footer(text=utils.INFRACTION_APPEAL_FOOTER),
+                ).set_footer(text=utils.INFRACTION_APPEAL_MODMAIL_FOOTER),
                 "send_result": False
             },
             {
@@ -218,7 +220,7 @@ async def test_notify_infraction(self, send_private_embed_mock):
                     name=utils.INFRACTION_AUTHOR_NAME,
                     url=utils.RULES_URL,
                     icon_url=Icons.defcon_denied
-                ).set_footer(text=utils.INFRACTION_APPEAL_FOOTER),
+                ).set_footer(text=utils.INFRACTION_APPEAL_MODMAIL_FOOTER),
                 "send_result": True
             }
         ]