aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--bot/cogs/clean.py78
-rw-r--r--bot/cogs/moderation/modlog.py35
2 files changed, 73 insertions, 40 deletions
diff --git a/bot/cogs/clean.py b/bot/cogs/clean.py
index b5d9132cb..892c638b8 100644
--- a/bot/cogs/clean.py
+++ b/bot/cogs/clean.py
@@ -1,16 +1,16 @@
import logging
import random
import re
-from typing import Optional
+from typing import Iterable, Optional
from discord import Colour, Embed, Message, TextChannel, User
+from discord.ext import commands
from discord.ext.commands import Cog, Context, group
from bot.bot import Bot
from bot.cogs.moderation import ModLog
from bot.constants import (
- Channels, CleanMessages, Colours, Event,
- Icons, MODERATION_ROLES, NEGATIVE_REPLIES
+ Channels, CleanMessages, Colours, Icons, MODERATION_ROLES, NEGATIVE_REPLIES
)
from bot.decorators import with_role
@@ -41,10 +41,10 @@ class Clean(Cog):
self,
amount: int,
ctx: Context,
+ channels: Iterable[TextChannel],
bots_only: bool = False,
user: User = None,
regex: Optional[str] = None,
- channel: Optional[TextChannel] = None
) -> None:
"""A helper function that does the actual message cleaning."""
def predicate_bots_only(message: Message) -> bool:
@@ -110,44 +110,40 @@ class Clean(Cog):
predicate = None # Delete all messages
# Default to using the invoking context's channel
- if not channel:
- channel = ctx.channel
+ if not channels:
+ channels = [ctx.channel]
+
+ # Delete the invocation first
+ with self.mod_log.ignore_all():
+ await ctx.message.delete()
# Look through the history and retrieve message data
+ # This is only done so we can create a log to upload.
messages = []
message_ids = []
self.cleaning = True
- invocation_deleted = False
-
- # To account for the invocation message, we index `amount + 1` messages.
- async for message in channel.history(limit=amount + 1):
- # If at any point the cancel command is invoked, we should stop.
- if not self.cleaning:
- return
+ for channel in channels:
+ async for message in channel.history(limit=amount):
- # Always start by deleting the invocation
- if not invocation_deleted:
- self.mod_log.ignore(Event.message_delete, message.id)
- await message.delete()
- invocation_deleted = True
- continue
+ # If at any point the cancel command is invoked, we should stop.
+ if not self.cleaning:
+ return
- # If the message passes predicate, let's save it.
- if predicate is None or predicate(message):
- message_ids.append(message.id)
- messages.append(message)
+ # If the message passes predicate, let's save it.
+ if predicate is None or predicate(message):
+ message_ids.append(message.id)
+ messages.append(message)
self.cleaning = False
- # We should ignore the ID's we stored, so we don't get mod-log spam.
- self.mod_log.ignore(Event.message_delete, *message_ids)
-
- # Use bulk delete to actually do the cleaning. It's far faster.
- await channel.purge(
- limit=amount,
- check=predicate
- )
+ # Now let's delete the actual messages with purge.
+ with self.mod_log.ignore_all():
+ for channel in channels:
+ await channel.purge(
+ limit=amount,
+ check=predicate
+ )
# Reverse the list to restore chronological order
if messages:
@@ -163,8 +159,10 @@ class Clean(Cog):
return
# Build the embed and send it
+ target_channels = ", ".join(channel.mention for channel in channels)
+
message = (
- f"**{len(message_ids)}** messages deleted in <#{channel.id}> by **{ctx.author.name}**\n\n"
+ f"**{len(message_ids)}** messages deleted in {target_channels} by **{ctx.author.name}**\n\n"
f"A log of the deleted messages can be found [here]({log_url})."
)
@@ -189,10 +187,10 @@ class Clean(Cog):
ctx: Context,
user: User,
amount: Optional[int] = 10,
- channel: TextChannel = None
+ channels: commands.Greedy[TextChannel] = None
) -> None:
"""Delete messages posted by the provided user, stop cleaning after traversing `amount` messages."""
- await self._clean_messages(amount, ctx, user=user, channel=channel)
+ await self._clean_messages(amount, ctx, user=user, channels=channels)
@clean_group.command(name="all", aliases=["everything"])
@with_role(*MODERATION_ROLES)
@@ -200,10 +198,10 @@ class Clean(Cog):
self,
ctx: Context,
amount: Optional[int] = 10,
- channel: TextChannel = None
+ channels: commands.Greedy[TextChannel] = None
) -> None:
"""Delete all messages, regardless of poster, stop cleaning after traversing `amount` messages."""
- await self._clean_messages(amount, ctx, channel=channel)
+ await self._clean_messages(amount, ctx, channels=channels)
@clean_group.command(name="bots", aliases=["bot"])
@with_role(*MODERATION_ROLES)
@@ -211,10 +209,10 @@ class Clean(Cog):
self,
ctx: Context,
amount: Optional[int] = 10,
- channel: TextChannel = None
+ channels: commands.Greedy[TextChannel] = None
) -> None:
"""Delete all messages posted by a bot, stop cleaning after traversing `amount` messages."""
- await self._clean_messages(amount, ctx, bots_only=True, channel=channel)
+ await self._clean_messages(amount, ctx, bots_only=True, channels=channels)
@clean_group.command(name="regex", aliases=["word", "expression"])
@with_role(*MODERATION_ROLES)
@@ -223,10 +221,10 @@ class Clean(Cog):
ctx: Context,
regex: str,
amount: Optional[int] = 10,
- channel: TextChannel = None
+ channels: commands.Greedy[TextChannel] = None
) -> None:
"""Delete all messages that match a certain regex, stop cleaning after traversing `amount` messages."""
- await self._clean_messages(amount, ctx, regex=regex, channel=channel)
+ await self._clean_messages(amount, ctx, regex=regex, channels=channels)
@clean_group.command(name="stop", aliases=["cancel", "abort"])
@with_role(*MODERATION_ROLES)
diff --git a/bot/cogs/moderation/modlog.py b/bot/cogs/moderation/modlog.py
index 9d28030d9..b3ae8e215 100644
--- a/bot/cogs/moderation/modlog.py
+++ b/bot/cogs/moderation/modlog.py
@@ -3,6 +3,7 @@ import difflib
import itertools
import logging
import typing as t
+from contextlib import contextmanager
from datetime import datetime
from itertools import zip_longest
@@ -40,6 +41,7 @@ class ModLog(Cog, name="ModLog"):
def __init__(self, bot: Bot):
self.bot = bot
self._ignored = {event: [] for event in Event}
+ self._ignore_all = False
self._cached_deletes = []
self._cached_edits = []
@@ -81,6 +83,15 @@ class ModLog(Cog, name="ModLog"):
if item not in self._ignored[event]:
self._ignored[event].append(item)
+ @contextmanager
+ def ignore_all(self) -> None:
+ """Ignore all events while inside this context scope."""
+ self._ignore_all = True
+ try:
+ yield
+ finally:
+ self._ignore_all = False
+
async def send_log_message(
self,
icon_url: t.Optional[str],
@@ -191,6 +202,9 @@ class ModLog(Cog, name="ModLog"):
self._ignored[Event.guild_channel_update].remove(before.id)
return
+ if self._ignore_all:
+ return
+
# Two channel updates are sent for a single edit: 1 for topic and 1 for category change.
# TODO: remove once support is added for ignoring multiple occurrences for the same channel.
help_categories = (Categories.help_available, Categories.help_dormant, Categories.help_in_use)
@@ -386,6 +400,9 @@ class ModLog(Cog, name="ModLog"):
self._ignored[Event.member_ban].remove(member.id)
return
+ if self._ignore_all:
+ return
+
await self.send_log_message(
Icons.user_ban, Colours.soft_red,
"User banned", f"{member} (`{member.id}`)",
@@ -426,6 +443,9 @@ class ModLog(Cog, name="ModLog"):
self._ignored[Event.member_remove].remove(member.id)
return
+ if self._ignore_all:
+ return
+
member_str = escape_markdown(str(member))
await self.send_log_message(
Icons.sign_out, Colours.soft_red,
@@ -444,6 +464,9 @@ class ModLog(Cog, name="ModLog"):
self._ignored[Event.member_unban].remove(member.id)
return
+ if self._ignore_all:
+ return
+
member_str = escape_markdown(str(member))
await self.send_log_message(
Icons.user_unban, Colour.blurple(),
@@ -462,6 +485,9 @@ class ModLog(Cog, name="ModLog"):
self._ignored[Event.member_update].remove(before.id)
return
+ if self._ignore_all:
+ return
+
diff = DeepDiff(before, after)
changes = []
done = []
@@ -564,6 +590,9 @@ class ModLog(Cog, name="ModLog"):
self._ignored[Event.message_delete].remove(message.id)
return
+ if self._ignore_all:
+ return
+
if author.bot:
return
@@ -623,6 +652,9 @@ class ModLog(Cog, name="ModLog"):
self._ignored[Event.message_delete].remove(event.message_id)
return
+ if self._ignore_all:
+ return
+
channel = self.bot.get_channel(event.channel_id)
if channel.category:
@@ -797,6 +829,9 @@ class ModLog(Cog, name="ModLog"):
self._ignored[Event.voice_state_update].remove(member.id)
return
+ if self._ignore_all:
+ return
+
# Exclude all channel attributes except the name.
diff = DeepDiff(
before,