aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--bot/exts/moderation/infraction/_scheduler.py18
-rw-r--r--bot/exts/moderation/infraction/_utils.py38
-rw-r--r--bot/exts/moderation/infraction/superstarify.py4
-rw-r--r--tests/bot/exts/moderation/infraction/test_utils.py4
4 files changed, 45 insertions, 19 deletions
diff --git a/bot/exts/moderation/infraction/_scheduler.py b/bot/exts/moderation/infraction/_scheduler.py
index 8286d3635..19402d01d 100644
--- a/bot/exts/moderation/infraction/_scheduler.py
+++ b/bot/exts/moderation/infraction/_scheduler.py
@@ -162,20 +162,12 @@ class InfractionScheduler:
# apply kick/ban infractions first, this would mean that we'd make it
# impossible for us to deliver a DM. See python-discord/bot#982.
if not infraction["hidden"]:
- dm_result = f"{constants.Emojis.failmail} "
- dm_log_text = "\nDM: **Failed**"
-
- # Sometimes user is a discord.Object; make it a proper user.
- try:
- if not isinstance(user, (discord.Member, discord.User)):
- user = await self.bot.fetch_user(user.id)
- except discord.HTTPException as e:
- log.error(f"Failed to DM {user.id}: could not fetch user (status {e.status})")
+ if await _utils.notify_infraction(infraction, user, user_reason):
+ dm_result = ":incoming_envelope: "
+ dm_log_text = "\nDM: Sent"
else:
- # Accordingly display whether the user was successfully notified via DM.
- if await _utils.notify_infraction(user, infr_type.replace("_", " ").title(), expiry, user_reason, icon):
- dm_result = ":incoming_envelope: "
- dm_log_text = "\nDM: Sent"
+ dm_result = f"{constants.Emojis.failmail} "
+ dm_log_text = "\nDM: **Failed**"
end_msg = ""
if infraction["actor"] == self.bot.user.id:
diff --git a/bot/exts/moderation/infraction/_utils.py b/bot/exts/moderation/infraction/_utils.py
index adbc641fa..a6f180c8c 100644
--- a/bot/exts/moderation/infraction/_utils.py
+++ b/bot/exts/moderation/infraction/_utils.py
@@ -5,9 +5,11 @@ from datetime import datetime
import discord
from discord.ext.commands import Context
+import bot
from bot.api import ResponseCodeError
from bot.constants import Colours, Icons
from bot.errors import InvalidInfractedUserError
+from bot.utils import time
log = logging.getLogger(__name__)
@@ -152,7 +154,7 @@ async def get_active_infraction(
log.trace(f"{user} does not have active infractions of type {infr_type}.")
-async def notify_infraction(
+async def send_infraction_embed(
user: UserObject,
infr_type: str,
expires_at: t.Optional[str] = None,
@@ -188,6 +190,40 @@ async def notify_infraction(
return await send_private_embed(user, embed)
+async def notify_infraction(
+ infraction: Infraction,
+ user: t.Optional[UserSnowflake] = None,
+ reason: t.Optional[str] = None
+) -> bool:
+ """
+ DM a user about their new infraction and return True if the DM is successful.
+
+ `user` and `reason` can be used to override what is in `infraction`. Otherwise, this data will
+ be retrieved from `infraction`.
+
+ Also return False if the user needs to be fetched but fails to be fetched.
+ """
+ if user is None:
+ user = discord.Object(infraction["user"])
+
+ # Sometimes user is a discord.Object; make it a proper user.
+ try:
+ if not isinstance(user, (discord.Member, discord.User)):
+ user = await bot.instance.fetch_user(user.id)
+ except discord.HTTPException as e:
+ log.error(f"Failed to DM {user.id}: could not fetch user (status {e.status})")
+ return False
+
+ type_ = infraction["type"].replace("_", " ").title()
+ icon = INFRACTION_ICONS[infraction["type"]][0]
+ expiry = time.format_infraction_with_duration(infraction["expires_at"])
+
+ if reason is None:
+ reason = infraction["reason"]
+
+ return await send_infraction_embed(user, type_, expiry, reason, icon)
+
+
async def notify_pardon(
user: UserObject,
title: str,
diff --git a/bot/exts/moderation/infraction/superstarify.py b/bot/exts/moderation/infraction/superstarify.py
index 07e79b9fe..6dd9924ad 100644
--- a/bot/exts/moderation/infraction/superstarify.py
+++ b/bot/exts/moderation/infraction/superstarify.py
@@ -70,15 +70,13 @@ class Superstarify(InfractionScheduler, Cog):
)
notified = await _utils.notify_infraction(
+ infraction=infraction,
user=after,
- infr_type="Superstarify",
- expires_at=format_infraction(infraction["expires_at"]),
reason=(
"You have tried to change your nickname on the **Python Discord** server "
f"from **{before.display_name}** to **{after.display_name}**, but as you "
"are currently in superstar-prison, you do not have permission to do so."
),
- icon_url=_utils.INFRACTION_ICONS["superstar"][0]
)
if not notified:
diff --git a/tests/bot/exts/moderation/infraction/test_utils.py b/tests/bot/exts/moderation/infraction/test_utils.py
index 50a717bb5..d35120992 100644
--- a/tests/bot/exts/moderation/infraction/test_utils.py
+++ b/tests/bot/exts/moderation/infraction/test_utils.py
@@ -124,7 +124,7 @@ class ModerationUtilsTests(unittest.IsolatedAsyncioTestCase):
self.ctx.send.assert_not_awaited()
@patch("bot.exts.moderation.infraction._utils.send_private_embed")
- async def test_notify_infraction(self, send_private_embed_mock):
+ async def test_send_infraction_embed(self, send_private_embed_mock):
"""
Should send an embed of a certain format as a DM and return `True` if DM successful.
@@ -230,7 +230,7 @@ class ModerationUtilsTests(unittest.IsolatedAsyncioTestCase):
send_private_embed_mock.reset_mock()
send_private_embed_mock.return_value = case["send_result"]
- result = await utils.notify_infraction(*case["args"])
+ result = await utils.send_infraction_embed(*case["args"])
self.assertEqual(case["send_result"], result)