diff options
author | 2024-01-08 01:18:01 +0000 | |
---|---|---|
committer | 2024-01-08 01:18:01 +0000 | |
commit | e753bb3e9e05e7dcd28febdef851c04fe9f20ce7 (patch) | |
tree | b7d9c665d3cb97045c0950c3ebf407889ecbd2d2 | |
parent | Merge pull request #2880 from python-discord/update-unambiguous-converters (diff) |
Move modlog method to util (#2877)
-rw-r--r-- | bot/exts/moderation/clean.py | 4 | ||||
-rw-r--r-- | bot/exts/moderation/defcon.py | 13 | ||||
-rw-r--r-- | bot/exts/moderation/infraction/_scheduler.py | 10 | ||||
-rw-r--r-- | bot/exts/moderation/infraction/management.py | 10 | ||||
-rw-r--r-- | bot/exts/moderation/modlog.py | 211 | ||||
-rw-r--r-- | bot/exts/moderation/watchchannels/_watchchannel.py | 13 | ||||
-rw-r--r-- | bot/utils/modlog.py | 69 | ||||
-rw-r--r-- | tests/bot/exts/moderation/test_modlog.py | 4 |
8 files changed, 203 insertions, 131 deletions
diff --git a/bot/exts/moderation/clean.py b/bot/exts/moderation/clean.py index 1c73736d7..d38de15e0 100644 --- a/bot/exts/moderation/clean.py +++ b/bot/exts/moderation/clean.py @@ -21,6 +21,7 @@ from bot.exts.moderation.modlog import ModLog from bot.log import get_logger from bot.utils.channel import is_mod_channel from bot.utils.messages import upload_log +from bot.utils.modlog import send_log_message log = get_logger(__name__) @@ -367,7 +368,8 @@ class Clean(Cog): f"A log of the deleted messages can be found [here]({log_url})." ) - await self.mod_log.send_log_message( + await send_log_message( + self.bot, icon_url=Icons.message_bulk_delete, colour=Colour(Colours.soft_red), title="Bulk message delete", diff --git a/bot/exts/moderation/defcon.py b/bot/exts/moderation/defcon.py index 3c16f8e0e..3196c4a1a 100644 --- a/bot/exts/moderation/defcon.py +++ b/bot/exts/moderation/defcon.py @@ -21,6 +21,7 @@ from bot.exts.moderation.modlog import ModLog from bot.log import get_logger from bot.utils import time from bot.utils.messages import format_user +from bot.utils.modlog import send_log_message log = get_logger(__name__) @@ -136,9 +137,13 @@ class Defcon(Cog): if not message_sent: message = f"{message}\n\nUnable to send rejection message via DM; they probably have DMs disabled." - await (await self.get_mod_log()).send_log_message( - Icons.defcon_denied, Colours.soft_red, "Entry denied", - message, member.display_avatar.url + await send_log_message( + self.bot, + Icons.defcon_denied, + Colours.soft_red, + "Entry denied", + message, + thumbnail=member.display_avatar.url ) @group(name="defcon", aliases=("dc",), invoke_without_command=True) @@ -304,7 +309,7 @@ class Defcon(Cog): ) status_msg = f"DEFCON {action.name.lower()}" - await (await self.get_mod_log()).send_log_message(info.icon, info.color, status_msg, log_msg) + await send_log_message(self.bot, info.icon, info.color, status_msg, log_msg) def _update_notifier(self) -> None: """Start or stop the notifier according to the DEFCON status.""" diff --git a/bot/exts/moderation/infraction/_scheduler.py b/bot/exts/moderation/infraction/_scheduler.py index a079f775e..aed6210a2 100644 --- a/bot/exts/moderation/infraction/_scheduler.py +++ b/bot/exts/moderation/infraction/_scheduler.py @@ -20,6 +20,7 @@ from bot.exts.moderation.modlog import ModLog from bot.log import get_logger from bot.utils import messages, time from bot.utils.channel import is_mod_channel +from bot.utils.modlog import send_log_message log = get_logger(__name__) @@ -270,7 +271,8 @@ class InfractionScheduler: # Send a log message to the mod log. # Don't use ctx.message.author for the actor; antispam only patches ctx.author. log.trace(f"Sending apply mod log for infraction #{id_}.") - await self.mod_log.send_log_message( + await send_log_message( + self.bot, icon_url=icon, colour=Colours.soft_red, title=f"Infraction {log_title}: {' '.join(infr_type.split('_'))}", @@ -369,7 +371,8 @@ class InfractionScheduler: log_text["Reason"] = log_text.pop("Reason") # Send a log message to the mod log. - await self.mod_log.send_log_message( + await send_log_message( + self.bot, icon_url=_utils.INFRACTION_ICONS[infr_type][1], colour=Colours.soft_green, title=f"Infraction {log_title}: {' '.join(infr_type.split('_'))}", @@ -507,7 +510,8 @@ class InfractionScheduler: log_text["Reason"] = log_text.pop("Reason") log.trace(f"Sending deactivation mod log for infraction #{id_}.") - await self.mod_log.send_log_message( + await send_log_message( + self.bot, icon_url=_utils.INFRACTION_ICONS[type_][1], colour=Colours.soft_green, title=f"Infraction {log_title}: {type_}", diff --git a/bot/exts/moderation/infraction/management.py b/bot/exts/moderation/infraction/management.py index 9bf3a4dbb..93959042b 100644 --- a/bot/exts/moderation/infraction/management.py +++ b/bot/exts/moderation/infraction/management.py @@ -16,11 +16,11 @@ from bot.decorators import ensure_future_timestamp from bot.errors import InvalidInfractionError from bot.exts.moderation.infraction import _utils from bot.exts.moderation.infraction.infractions import Infractions -from bot.exts.moderation.modlog import ModLog from bot.log import get_logger from bot.pagination import LinePaginator from bot.utils import messages, time from bot.utils.channel import is_in_category, is_mod_channel +from bot.utils.modlog import send_log_message from bot.utils.time import unpack_duration log = get_logger(__name__) @@ -58,11 +58,6 @@ class ModManagement(commands.Cog): command.help += f"\n{SYMBOLS_GUIDE}" @property - def mod_log(self) -> ModLog: - """Get currently loaded ModLog cog instance.""" - return self.bot.get_cog("ModLog") - - @property def infractions_cog(self) -> Infractions: """Get currently loaded Infractions cog instance.""" return self.bot.get_cog("Infractions") @@ -260,7 +255,8 @@ class ModManagement(commands.Cog): else: jump_url = f"[Click here.]({ctx.message.jump_url})" - await self.mod_log.send_log_message( + await send_log_message( + self.bot, icon_url=constants.Icons.pencil, colour=discord.Colour.og_blurple(), title="Infraction edited", diff --git a/bot/exts/moderation/modlog.py b/bot/exts/moderation/modlog.py index b349f4d5d..3c256396a 100644 --- a/bot/exts/moderation/modlog.py +++ b/bot/exts/moderation/modlog.py @@ -8,7 +8,7 @@ from dateutil.relativedelta import relativedelta from deepdiff import DeepDiff from discord import Colour, Message, Thread from discord.abc import GuildChannel -from discord.ext.commands import Cog, Context +from discord.ext.commands import Cog from discord.utils import escape_markdown, format_dt, snowflake_time from bot.bot import Bot @@ -16,6 +16,7 @@ from bot.constants import Channels, Colours, Emojis, Event, Guild as GuildConsta from bot.log import get_logger from bot.utils import time from bot.utils.messages import format_user, upload_log +from bot.utils.modlog import send_log_message log = get_logger(__name__) @@ -47,63 +48,6 @@ class ModLog(Cog, name="ModLog"): if item not in self._ignored[event]: self._ignored[event].append(item) - async def send_log_message( - self, - icon_url: str | None, - colour: discord.Colour | int, - title: str | None, - text: str, - thumbnail: str | discord.Asset | None = None, - channel_id: int = Channels.mod_log, - ping_everyone: bool = False, - files: list[discord.File] | None = None, - content: str | None = None, - additional_embeds: list[discord.Embed] | None = None, - timestamp_override: datetime | None = None, - footer: str | None = None, - ) -> Context: - """Generate log embed and send to logging channel.""" - await self.bot.wait_until_guild_available() - # Truncate string directly here to avoid removing newlines - embed = discord.Embed( - description=text[:4093] + "..." if len(text) > 4096 else text - ) - - if title and icon_url: - embed.set_author(name=title, icon_url=icon_url) - - embed.colour = colour - embed.timestamp = timestamp_override or datetime.now(tz=UTC) - - if footer: - embed.set_footer(text=footer) - - if thumbnail: - embed.set_thumbnail(url=thumbnail) - - if ping_everyone: - if content: - content = f"<@&{Roles.moderators}> {content}" - else: - content = f"<@&{Roles.moderators}>" - - # Truncate content to 2000 characters and append an ellipsis. - if content and len(content) > 2000: - content = content[:2000 - 3] + "..." - - channel = self.bot.get_channel(channel_id) - log_message = await channel.send( - content=content, - embed=embed, - files=files - ) - - if additional_embeds: - for additional_embed in additional_embeds: - await channel.send(embed=additional_embed) - - return await self.bot.get_context(log_message) # Optionally return for use with antispam - @Cog.listener() async def on_guild_channel_create(self, channel: GUILD_CHANNEL) -> None: """Log channel create event to mod log.""" @@ -128,7 +72,7 @@ class ModLog(Cog, name="ModLog"): else: message = f"{channel.name} (`{channel.id}`)" - await self.send_log_message(Icons.hash_green, Colours.soft_green, title, message) + await send_log_message(self.bot, Icons.hash_green, Colours.soft_green, title, message) @Cog.listener() async def on_guild_channel_delete(self, channel: GUILD_CHANNEL) -> None: @@ -148,9 +92,12 @@ class ModLog(Cog, name="ModLog"): else: message = f"{channel.name} (`{channel.id}`)" - await self.send_log_message( - Icons.hash_red, Colours.soft_red, - title, message + await send_log_message( + self.bot, + Icons.hash_red, + Colours.soft_red, + title, + message ) @Cog.listener() @@ -211,9 +158,12 @@ class ModLog(Cog, name="ModLog"): else: message = f"**#{after.name}** (`{after.id}`)\n{message}" - await self.send_log_message( - Icons.hash_blurple, Colour.og_blurple(), - "Channel updated", message + await send_log_message( + self.bot, + Icons.hash_blurple, + Colour.og_blurple(), + "Channel updated", + message ) @Cog.listener() @@ -222,9 +172,12 @@ class ModLog(Cog, name="ModLog"): if role.guild.id != GuildConstant.id: return - await self.send_log_message( - Icons.crown_green, Colours.soft_green, - "Role created", f"`{role.id}`" + await send_log_message( + self.bot, + Icons.crown_green, + Colours.soft_green, + "Role created", + f"`{role.id}`" ) @Cog.listener() @@ -233,9 +186,12 @@ class ModLog(Cog, name="ModLog"): if role.guild.id != GuildConstant.id: return - await self.send_log_message( - Icons.crown_red, Colours.soft_red, - "Role removed", f"{role.name} (`{role.id}`)" + await send_log_message( + self.bot, + Icons.crown_red, + Colours.soft_red, + "Role removed", + f"{role.name} (`{role.id}`)" ) @Cog.listener() @@ -286,9 +242,12 @@ class ModLog(Cog, name="ModLog"): message = f"**{after.name}** (`{after.id}`)\n{message}" - await self.send_log_message( - Icons.crown_blurple, Colour.og_blurple(), - "Role updated", message + await send_log_message( + self.bot, + Icons.crown_blurple, + Colour.og_blurple(), + "Role updated", + message ) @Cog.listener() @@ -336,9 +295,12 @@ class ModLog(Cog, name="ModLog"): message = f"**{after.name}** (`{after.id}`)\n{message}" - await self.send_log_message( - Icons.guild_update, Colour.og_blurple(), - "Guild updated", message, + await send_log_message( + self.bot, + Icons.guild_update, + Colour.og_blurple(), + "Guild updated", + message, thumbnail=after.icon.with_static_format("png") ) @@ -352,9 +314,12 @@ class ModLog(Cog, name="ModLog"): self._ignored[Event.member_ban].remove(member.id) return - await self.send_log_message( - Icons.user_ban, Colours.soft_red, - "User banned", format_user(member), + await send_log_message( + self.bot, + Icons.user_ban, + Colours.soft_red, + "User banned", + format_user(member), thumbnail=member.display_avatar.url, channel_id=Channels.user_log ) @@ -373,9 +338,12 @@ class ModLog(Cog, name="ModLog"): if difference.days < 1 and difference.months < 1 and difference.years < 1: # New user account! message = f"{Emojis.new} {message}" - await self.send_log_message( - Icons.sign_in, Colours.soft_green, - "User joined", message, + await send_log_message( + self.bot, + Icons.sign_in, + Colours.soft_green, + "User joined", + message, thumbnail=member.display_avatar.url, channel_id=Channels.user_log ) @@ -390,9 +358,12 @@ class ModLog(Cog, name="ModLog"): self._ignored[Event.member_remove].remove(member.id) return - await self.send_log_message( - Icons.sign_out, Colours.soft_red, - "User left", format_user(member), + await send_log_message( + self.bot, + Icons.sign_out, + Colours.soft_red, + "User left", + format_user(member), thumbnail=member.display_avatar.url, channel_id=Channels.user_log ) @@ -407,9 +378,12 @@ class ModLog(Cog, name="ModLog"): self._ignored[Event.member_unban].remove(member.id) return - await self.send_log_message( - Icons.user_unban, Colour.og_blurple(), - "User unbanned", format_user(member), + await send_log_message( + self.bot, + Icons.user_unban, + Colour.og_blurple(), + "User unbanned", + format_user(member), thumbnail=member.display_avatar.url, channel_id=Channels.mod_log ) @@ -471,7 +445,8 @@ class ModLog(Cog, name="ModLog"): message = f"{format_user(after)}\n{message}" - await self.send_log_message( + await send_log_message( + self.bot, icon_url=Icons.user_update, colour=Colour.og_blurple(), title="Member updated", @@ -565,8 +540,10 @@ class ModLog(Cog, name="ModLog"): response += f"{content}" - await self.send_log_message( - Icons.message_delete, Colours.soft_red, + await send_log_message( + self.bot, + Icons.message_delete, + Colours.soft_red, "Message deleted", response, channel_id=Channels.message_log @@ -606,8 +583,10 @@ class ModLog(Cog, name="ModLog"): "This message was not cached, so the message content cannot be displayed." ) - await self.send_log_message( - Icons.message_delete, Colours.soft_red, + await send_log_message( + self.bot, + Icons.message_delete, + Colours.soft_red, "Message deleted", response, channel_id=Channels.message_log @@ -687,9 +666,15 @@ class ModLog(Cog, name="ModLog"): timestamp = msg_before.created_at footer = None - await self.send_log_message( - Icons.message_edit, Colour.og_blurple(), "Message edited", response, - channel_id=Channels.message_log, timestamp_override=timestamp, footer=footer + await send_log_message( + self.bot, + Icons.message_edit, + Colour.og_blurple(), + "Message edited", + response, + channel_id=Channels.message_log, + timestamp_override=timestamp, + footer=footer ) @Cog.listener() @@ -734,14 +719,22 @@ class ModLog(Cog, name="ModLog"): f"{message.clean_content}" ) - await self.send_log_message( - Icons.message_edit, Colour.og_blurple(), "Message edited (Before)", - before_response, channel_id=Channels.message_log + await send_log_message( + self.bot, + Icons.message_edit, + Colour.og_blurple(), + "Message edited (Before)", + before_response, + channel_id=Channels.message_log ) - await self.send_log_message( - Icons.message_edit, Colour.og_blurple(), "Message edited (After)", - after_response, channel_id=Channels.message_log + await send_log_message( + self.bot, + Icons.message_edit, + Colour.og_blurple(), + "Message edited (After)", + after_response, + channel_id=Channels.message_log ) @Cog.listener() @@ -752,7 +745,8 @@ class ModLog(Cog, name="ModLog"): return if before.name != after.name: - await self.send_log_message( + await send_log_message( + self.bot, Icons.hash_blurple, Colour.og_blurple(), "Thread name edited", @@ -774,7 +768,8 @@ class ModLog(Cog, name="ModLog"): else: return - await self.send_log_message( + await send_log_message( + self.bot, icon, colour, f"Thread {action}", @@ -792,7 +787,8 @@ class ModLog(Cog, name="ModLog"): log.trace("Ignoring deletion of thread %s (%d)", thread.mention, thread.id) return - await self.send_log_message( + await send_log_message( + self.bot, Icons.hash_red, Colours.soft_red, "Thread deleted", @@ -868,7 +864,8 @@ class ModLog(Cog, name="ModLog"): message = "\n".join(f"{Emojis.bullet} {item}" for item in sorted(changes)) message = f"{format_user(member)}\n{message}" - await self.send_log_message( + await send_log_message( + self.bot, icon_url=icon, colour=colour, title="Voice state updated", diff --git a/bot/exts/moderation/watchchannels/_watchchannel.py b/bot/exts/moderation/watchchannels/_watchchannel.py index d0fc0de44..71600e9df 100644 --- a/bot/exts/moderation/watchchannels/_watchchannel.py +++ b/bot/exts/moderation/watchchannels/_watchchannel.py @@ -19,10 +19,10 @@ from bot.bot import Bot from bot.constants import BigBrother as BigBrotherConfig, Guild as GuildConfig, Icons from bot.exts.filtering._filters.unique.discord_token import DiscordTokenFilter from bot.exts.filtering._filters.unique.webhook import WEBHOOK_URL_RE -from bot.exts.moderation.modlog import ModLog from bot.log import get_logger from bot.pagination import LinePaginator from bot.utils import CogABCMeta, messages, time +from bot.utils.modlog import send_log_message log = get_logger(__name__) @@ -73,11 +73,6 @@ class WatchChannel(metaclass=CogABCMeta): self.disable_header = disable_header @property - def modlog(self) -> ModLog: - """Provides access to the ModLog cog for alert purposes.""" - return self.bot.get_cog("ModLog") - - @property def consuming_messages(self) -> bool: """Checks if a consumption task is currently running.""" if self._consume_task is None: @@ -122,7 +117,8 @@ class WatchChannel(metaclass=CogABCMeta): """ ) - await self.modlog.send_log_message( + await send_log_message( + self.bot, title=f"Error: Failed to initialize the {self.__class__.__name__} watch channel", text=message, ping_everyone=True, @@ -134,7 +130,8 @@ class WatchChannel(metaclass=CogABCMeta): return if not await self.fetch_user_cache(): - await self.modlog.send_log_message( + await send_log_message( + self.bot, title=f"Warning: Failed to retrieve user cache for the {self.__class__.__name__} watch channel", text=( "Could not retrieve the list of watched users from the API. " diff --git a/bot/utils/modlog.py b/bot/utils/modlog.py new file mode 100644 index 000000000..6a432e65e --- /dev/null +++ b/bot/utils/modlog.py @@ -0,0 +1,69 @@ +from datetime import UTC, datetime + +import discord + +from bot.bot import Bot +from bot.constants import Channels, Roles + + +async def send_log_message( + bot: Bot, + icon_url: str | None, + colour: discord.Colour | int, + title: str | None, + text: str, + *, + thumbnail: str | discord.Asset | None = None, + channel_id: int = Channels.mod_log, + ping_everyone: bool = False, + files: list[discord.File] | None = None, + content: str | None = None, + additional_embeds: list[discord.Embed] | None = None, + timestamp_override: datetime | None = None, + footer: str | None = None, +) -> discord.Message: + """Generate log embed and send to logging channel.""" + await bot.wait_until_guild_available() + # Truncate string directly here to avoid removing newlines + embed = discord.Embed( + description=text[:4093] + "..." if len(text) > 4096 else text + ) + + if title and icon_url: + embed.set_author(name=title, icon_url=icon_url) + elif title: + raise ValueError("title cannot be set without icon_url") + elif icon_url: + raise ValueError("icon_url cannot be set without title") + + embed.colour = colour + embed.timestamp = timestamp_override or datetime.now(tz=UTC) + + if footer: + embed.set_footer(text=footer) + + if thumbnail: + embed.set_thumbnail(url=thumbnail) + + if ping_everyone: + if content: + content = f"<@&{Roles.moderators}> {content}" + else: + content = f"<@&{Roles.moderators}>" + + # Truncate content to 2000 characters and append an ellipsis. + if content and len(content) > 2000: + content = content[:2000 - 3] + "..." + + channel = bot.get_channel(channel_id) + log_message = await channel.send( + content=content, + embed=embed, + files=files + ) + + if additional_embeds: + for additional_embed in additional_embeds: + await channel.send(embed=additional_embed) + + return log_message diff --git a/tests/bot/exts/moderation/test_modlog.py b/tests/bot/exts/moderation/test_modlog.py index 79e04837d..f2b02bd1b 100644 --- a/tests/bot/exts/moderation/test_modlog.py +++ b/tests/bot/exts/moderation/test_modlog.py @@ -3,6 +3,7 @@ import unittest import discord from bot.exts.moderation.modlog import ModLog +from bot.utils.modlog import send_log_message from tests.helpers import MockBot, MockTextChannel @@ -17,7 +18,8 @@ class ModLogTests(unittest.IsolatedAsyncioTestCase): async def test_log_entry_description_truncation(self): """Test that embed description for ModLog entry is truncated.""" self.bot.get_channel.return_value = self.channel - await self.cog.send_log_message( + await send_log_message( + self.bot, icon_url="foo", colour=discord.Colour.blue(), title="bar", |