aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGravatar MarkKoz <[email protected]>2019-10-01 17:33:55 -0700
committerGravatar MarkKoz <[email protected]>2019-10-01 18:25:34 -0700
commit73ee225784dfcdaec363acf8f150aec92633bd42 (patch)
tree72e7e03f98520e6b874ed897c6b49fcf5cf631dc
parentAdd comments and improve docstrings in the infractions cog (diff)
Move DM notification functions to moderation utils module
-rw-r--r--bot/cogs/moderation/infractions.py123
-rw-r--r--bot/cogs/moderation/superstarify.py14
-rw-r--r--bot/cogs/moderation/utils.py77
3 files changed, 104 insertions, 110 deletions
diff --git a/bot/cogs/moderation/infractions.py b/bot/cogs/moderation/infractions.py
index 3d6ccd72a..4c903debd 100644
--- a/bot/cogs/moderation/infractions.py
+++ b/bot/cogs/moderation/infractions.py
@@ -3,39 +3,24 @@ import textwrap
from typing import Awaitable, Dict, Optional, Union
import dateutil.parser
-from discord import (
- Colour, Embed, Forbidden, HTTPException, Member, NotFound, Object, User
-)
+from discord import Colour, Forbidden, HTTPException, Member, NotFound, Object, User
from discord.ext.commands import BadUnionArgument, Bot, Cog, Context, command
from bot import constants
from bot.api import ResponseCodeError
-from bot.constants import Colours, Event, Icons
+from bot.constants import Colours, Event
from bot.converters import Duration
from bot.decorators import respect_role_hierarchy
from bot.utils.checks import with_role_check
from bot.utils.scheduling import Scheduler
from bot.utils.time import format_infraction, wait_until
+from . import utils
from .modlog import ModLog
-from .utils import (
- Infraction, MemberObject, already_has_active_infraction, post_infraction, proxy_user
-)
+from .utils import MemberObject
log = logging.getLogger(__name__)
-# apply icon, pardon icon
-INFRACTION_ICONS = {
- "mute": (Icons.user_mute, Icons.user_unmute),
- "kick": (Icons.sign_out, None),
- "ban": (Icons.user_ban, Icons.user_unban),
- "warning": (Icons.user_warn, None),
- "note": (Icons.user_warn, None),
-}
-RULES_URL = "https://pythondiscord.com/pages/rules"
-APPEALABLE_INFRACTIONS = ("ban", "mute")
-
-
-MemberConverter = Union[Member, User, proxy_user]
+MemberConverter = Union[Member, User, utils.proxy_user]
class Infractions(Scheduler, Cog):
@@ -67,7 +52,7 @@ class Infractions(Scheduler, Cog):
@command()
async def warn(self, ctx: Context, user: MemberConverter, *, reason: str = None) -> None:
"""Warn a user for the given reason."""
- infraction = await post_infraction(ctx, user, reason, "warning")
+ infraction = await utils.post_infraction(ctx, user, reason, "warning")
if infraction is None:
return
@@ -112,7 +97,7 @@ class Infractions(Scheduler, Cog):
@command(hidden=True)
async def note(self, ctx: Context, user: MemberConverter, *, reason: str = None) -> None:
"""Create a private note for a user with the given reason without notifying the user."""
- infraction = await post_infraction(ctx, user, reason, "note", hidden=True)
+ infraction = await utils.post_infraction(ctx, user, reason, "note", hidden=True)
if infraction is None:
return
@@ -173,10 +158,10 @@ class Infractions(Scheduler, Cog):
async def apply_mute(self, ctx: Context, user: Member, reason: str, **kwargs) -> None:
"""Apply a mute infraction with kwargs passed to `post_infraction`."""
- if await already_has_active_infraction(ctx, user, "mute"):
+ if await utils.already_has_active_infraction(ctx, user, "mute"):
return
- infraction = await post_infraction(ctx, user, "mute", reason, **kwargs)
+ infraction = await utils.post_infraction(ctx, user, "mute", reason, **kwargs)
if infraction is None:
return
@@ -188,7 +173,7 @@ class Infractions(Scheduler, Cog):
@respect_role_hierarchy()
async def apply_kick(self, ctx: Context, user: Member, reason: str, **kwargs) -> None:
"""Apply a kick infraction with kwargs passed to `post_infraction`."""
- infraction = await post_infraction(ctx, user, "kick", reason, **kwargs)
+ infraction = await utils.post_infraction(ctx, user, "kick", reason, **kwargs)
if infraction is None:
return
@@ -200,10 +185,10 @@ class Infractions(Scheduler, Cog):
@respect_role_hierarchy()
async def apply_ban(self, ctx: Context, user: MemberObject, reason: str, **kwargs) -> None:
"""Apply a ban infraction with kwargs passed to `post_infraction`."""
- if await already_has_active_infraction(ctx, user, "ban"):
+ if await utils.already_has_active_infraction(ctx, user, "ban"):
return
- infraction = await post_infraction(ctx, user, "ban", reason, **kwargs)
+ infraction = await utils.post_infraction(ctx, user, "ban", reason, **kwargs)
if infraction is None:
return
@@ -216,7 +201,7 @@ class Infractions(Scheduler, Cog):
# endregion
# region: Utility functions
- async def _scheduled_task(self, infraction: Infraction) -> None:
+ async def _scheduled_task(self, infraction: utils.Infraction) -> None:
"""
Marks an infraction expired after the delay from time of scheduling to time of expiration.
@@ -233,7 +218,7 @@ class Infractions(Scheduler, Cog):
async def deactivate_infraction(
self,
- infraction: Infraction,
+ infraction: utils.Infraction,
send_log: bool = True
) -> Dict[str, str]:
"""
@@ -265,11 +250,11 @@ class Infractions(Scheduler, Cog):
await user.remove_roles(self._muted_role, reason=reason)
# DM the user about the expiration.
- notified = await self.notify_pardon(
+ notified = await utils.notify_pardon(
user=user,
title="You have been unmuted.",
content="You may now send messages in the server.",
- icon_url=INFRACTION_ICONS["mute"][1]
+ icon_url=utils.INFRACTION_ICONS["mute"][1]
)
log_text["Member"] = f"{user.mention}(`{user.id}`)"
@@ -321,7 +306,7 @@ class Infractions(Scheduler, Cog):
log_title = f"expiration failed" if "Failure" in log_text else "expired"
await self.mod_log.send_log_message(
- icon_url=INFRACTION_ICONS[_type][1],
+ icon_url=utils.INFRACTION_ICONS[_type][1],
colour=Colour(Colours.soft_green),
title=f"Infraction {log_title}: {_type}",
text="\n".join(f"{k}: {v}" for k, v in log_text.items()),
@@ -330,79 +315,16 @@ class Infractions(Scheduler, Cog):
return log_text
- async def notify_infraction(
- self,
- user: MemberObject,
- infr_type: str,
- expires_at: Optional[str] = None,
- reason: Optional[str] = None
- ) -> bool:
- """DM a user about their new infraction and return True if the DM is successful."""
- embed = Embed(
- description=textwrap.dedent(f"""
- **Type:** {infr_type.capitalize()}
- **Expires:** {expires_at or "N/A"}
- **Reason:** {reason or "No reason provided."}
- """),
- colour=Colour(Colours.soft_red)
- )
-
- icon_url = INFRACTION_ICONS[infr_type][0]
- embed.set_author(name="Infraction Information", icon_url=icon_url, url=RULES_URL)
- embed.title = f"Please review our rules over at {RULES_URL}"
- embed.url = RULES_URL
-
- if infr_type in APPEALABLE_INFRACTIONS:
- embed.set_footer(text="To appeal this infraction, send an e-mail to [email protected]")
-
- return await self.send_private_embed(user, embed)
-
- async def notify_pardon(
- self,
- user: MemberObject,
- 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."""
- embed = Embed(
- description=content,
- colour=Colour(Colours.soft_green)
- )
-
- embed.set_author(name=title, icon_url=icon_url)
-
- return await self.send_private_embed(user, embed)
-
- async def send_private_embed(self, user: MemberObject, embed: Embed) -> bool:
- """
- A helper method for sending an embed to a user's DMs.
-
- Returns a boolean indicator of DM success.
- """
- try:
- # sometimes `user` is a `discord.Object`, so let's make it a proper user.
- user = await self.bot.fetch_user(user.id)
-
- await user.send(embed=embed)
- return True
- except (HTTPException, Forbidden, NotFound):
- log.debug(
- f"Infraction-related information could not be sent to user {user} ({user.id}). "
- "The user either could not be retrieved or probably disabled their DMs."
- )
- return False
-
async def apply_infraction(
self,
ctx: Context,
- infraction: Infraction,
+ infraction: utils.Infraction,
user: MemberObject,
action_coro: Optional[Awaitable] = None
) -> None:
"""Apply an infraction to the user, log the infraction, and optionally notify the user."""
infr_type = infraction["type"]
- icon = INFRACTION_ICONS[infr_type][0]
+ icon = utils.INFRACTION_ICONS[infr_type][0]
reason = infraction["reason"]
expiry = infraction["expires_at"]
@@ -420,8 +342,11 @@ class Infractions(Scheduler, Cog):
# DM the user about the infraction if it's not a shadow/hidden infraction.
if not infraction["hidden"]:
+ # Sometimes user is a discord.Object; make it a proper user.
+ await self.bot.fetch_user(user.id)
+
# Accordingly display whether the user was successfully notified via DM.
- if await self.notify_infraction(user, infr_type, expiry, reason):
+ if await utils.notify_infraction(user, infr_type, expiry, reason):
dm_result = ":incoming_envelope: "
dm_log_text = "\nDM: Sent"
else:
@@ -538,7 +463,7 @@ class Infractions(Scheduler, Cog):
# Send a log message to the mod log.
await self.mod_log.send_log_message(
- icon_url=INFRACTION_ICONS[infr_type][1],
+ icon_url=utils.INFRACTION_ICONS[infr_type][1],
colour=Colour(Colours.soft_green),
title=f"Infraction {log_title}: {infr_type}",
thumbnail=user.avatar_url_as(static_format="png"),
diff --git a/bot/cogs/moderation/superstarify.py b/bot/cogs/moderation/superstarify.py
index 28516dd1c..0c805a385 100644
--- a/bot/cogs/moderation/superstarify.py
+++ b/bot/cogs/moderation/superstarify.py
@@ -11,9 +11,8 @@ from bot.constants import Icons, MODERATION_ROLES, POSITIVE_REPLIES
from bot.converters import Duration
from bot.decorators import with_role
from bot.utils.time import format_infraction
-from .infractions import Infractions
+from . import utils
from .modlog import ModLog
-from .utils import post_infraction
log = logging.getLogger(__name__)
NICKNAME_POLICY_URL = "https://pythondiscord.com/pages/rules/#wiki-toc-nickname-policy"
@@ -29,11 +28,6 @@ class Superstarify(Cog):
self.bot = bot
@property
- def infractions_cog(self) -> Infractions:
- """Get currently loaded Infractions cog instance."""
- return self.bot.get_cog("Infractions")
-
- @property
def modlog(self) -> ModLog:
"""Get currently loaded ModLog cog instance."""
return self.bot.get_cog("ModLog")
@@ -176,7 +170,7 @@ class Superstarify(Cog):
)
return
- infraction = await post_infraction(
+ infraction = await utils.post_infraction(
ctx, member,
type='superstar', reason=reason or ('old nick: ' + member.display_name),
expires_at=expiration
@@ -210,7 +204,7 @@ class Superstarify(Cog):
thumbnail=member.avatar_url_as(static_format="png")
)
- await self.infractions_cog.notify_infraction(
+ await utils.notify_infraction(
user=member,
infr_type="Superstarify",
expires_at=expiration,
@@ -253,7 +247,7 @@ class Superstarify(Cog):
embed.description = "User has been released from superstar-prison."
embed.title = random.choice(POSITIVE_REPLIES)
- await self.infractions_cog.notify_pardon(
+ await utils.notify_pardon(
user=member,
title="You are no longer superstarified.",
content="You may now change your nickname on the server."
diff --git a/bot/cogs/moderation/utils.py b/bot/cogs/moderation/utils.py
index 48ebe422c..0879eb927 100644
--- a/bot/cogs/moderation/utils.py
+++ b/bot/cogs/moderation/utils.py
@@ -1,4 +1,5 @@
import logging
+import textwrap
import typing as t
from datetime import datetime
@@ -7,10 +8,23 @@ from discord.ext import commands
from discord.ext.commands import Context
from bot.api import ResponseCodeError
+from bot.constants import Colours, Icons
log = logging.getLogger(__name__)
-MemberObject = t.Union[discord.Member, discord.User, discord.Object]
+# apply icon, pardon icon
+INFRACTION_ICONS = {
+ "mute": (Icons.user_mute, Icons.user_unmute),
+ "kick": (Icons.sign_out, None),
+ "ban": (Icons.user_ban, Icons.user_unban),
+ "warning": (Icons.user_warn, None),
+ "note": (Icons.user_warn, None),
+}
+RULES_URL = "https://pythondiscord.com/pages/rules"
+APPEALABLE_INFRACTIONS = ("ban", "mute")
+
+UserTypes = t.Union[discord.Member, discord.User]
+MemberObject = t.Union[UserTypes, discord.Object]
Infraction = t.Dict[str, t.Union[str, int, bool]]
@@ -85,3 +99,64 @@ async def already_has_active_infraction(ctx: Context, user: MemberObject, type:
return True
else:
return False
+
+
+async def notify_infraction(
+ user: UserTypes,
+ infr_type: str,
+ expires_at: t.Optional[str] = None,
+ reason: t.Optional[str] = None
+) -> bool:
+ """DM a user about their new infraction and return True if the DM is successful."""
+ embed = discord.Embed(
+ description=textwrap.dedent(f"""
+ **Type:** {infr_type.capitalize()}
+ **Expires:** {expires_at or "N/A"}
+ **Reason:** {reason or "No reason provided."}
+ """),
+ colour=discord.Colour(Colours.soft_red)
+ )
+
+ icon_url = INFRACTION_ICONS[infr_type][0]
+ embed.set_author(name="Infraction Information", icon_url=icon_url, url=RULES_URL)
+ embed.title = f"Please review our rules over at {RULES_URL}"
+ embed.url = RULES_URL
+
+ if infr_type in APPEALABLE_INFRACTIONS:
+ embed.set_footer(text="To appeal this infraction, send an e-mail to [email protected]")
+
+ return await send_private_embed(user, embed)
+
+
+async def notify_pardon(
+ user: UserTypes,
+ 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."""
+ embed = discord.Embed(
+ description=content,
+ colour=discord.Colour(Colours.soft_green)
+ )
+
+ embed.set_author(name=title, icon_url=icon_url)
+
+ return await send_private_embed(user, embed)
+
+
+async def send_private_embed(user: UserTypes, embed: discord.Embed) -> bool:
+ """
+ A helper method for sending an embed to a user's DMs.
+
+ Returns a boolean indicator of DM success.
+ """
+ try:
+ await user.send(embed=embed)
+ return True
+ except (discord.HTTPException, discord.Forbidden, discord.NotFound):
+ log.debug(
+ f"Infraction-related information could not be sent to user {user} ({user.id}). "
+ "The user either could not be retrieved or probably disabled their DMs."
+ )
+ return False