aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGravatar Leon Sandøy <[email protected]>2018-07-29 01:17:29 +0200
committerGravatar Leon Sandøy <[email protected]>2018-07-29 01:17:29 +0200
commitea33140c3eab95ae8f42cd36afda7c83972b41f7 (patch)
tree46b7f480c3d1baff7d0c644c862094a83573ecfc
parentMerge branch 'master' into clean_command (diff)
Added regex cleanups, and now sending over complete embed details and a number of other message details.
-rw-r--r--bot/cogs/clean.py126
-rw-r--r--bot/cogs/modlog.py2
2 files changed, 99 insertions, 29 deletions
diff --git a/bot/cogs/clean.py b/bot/cogs/clean.py
index 8df6d6e83..efedc2dce 100644
--- a/bot/cogs/clean.py
+++ b/bot/cogs/clean.py
@@ -1,6 +1,9 @@
import logging
import random
+import re
+from typing import Optional
+from aiohttp.client_exceptions import ClientResponseError
from discord import Colour, Embed, Message, User
from discord.ext.commands import Bot, Context, group
@@ -39,29 +42,76 @@ class Clean:
json={"log_data": log_data}
)
- data = await response.json()
- log_id = data["log_id"]
+ try:
+ data = await response.json()
+ log_id = data["log_id"]
+ except (KeyError, ClientResponseError):
+ log.debug(
+ "API returned an unexpected result:\n"
+ f"{response.text}"
+ )
+ return
return f"{URLs.site_clean_logs}/{log_id}"
async def _clean_messages(
self, amount: int, ctx: Context,
- bots_only: bool=False, user: User=None
+ bots_only: bool = False, user: User = None,
+ regex: Optional[str] = None
):
"""
A helper function that does the actual message cleaning.
:param bots_only: Set this to True if you only want to delete bot messages.
:param user: Specify a user and it will only delete messages by this user.
+ :param regular_expression: Specify a regular expression and it will only
+ delete messages that match this.
"""
- # Bulk delete checks
- def predicate_bots_only(message: Message):
+ def predicate_bots_only(message: Message) -> bool:
+ """
+ Returns true if the message was sent by a bot
+ """
+
return message.author.bot
- def predicate_specific_user(message: Message):
+ def predicate_specific_user(message: Message) -> bool:
+ """
+ Return True if the message was sent by the
+ user provided in the _clean_messages call.
+ """
+
return message.author == user
+ def predicate_regex(message: Message):
+ """
+ Returns True if the regex provided in the
+ _clean_messages matches the message content
+ or any embed attributes the message may have.
+ """
+
+ content = [message.content]
+
+ # Add the content for all embed attributes
+ for embed in message.embeds:
+ content.append(embed.title)
+ content.append(embed.description)
+ content.append(embed.footer.text)
+ content.append(embed.author.name)
+ for field in embed.fields:
+ content.append(field.name)
+ content.append(field.value)
+
+ # Get rid of empty attributes and turn it into a string
+ content = [attr for attr in content if attr]
+ content = "\n".join(content)
+
+ # Now let's see if there's a regex match
+ if not content:
+ return False
+ else:
+ return bool(re.search(regex.lower(), content.lower()))
+
# Is this an acceptable amount of messages to clean?
if amount > CleanMessages.message_limit:
embed = Embed(
@@ -82,35 +132,52 @@ class Clean:
await ctx.send(embed=embed)
return
+ # Set up the correct predicate
+ if bots_only:
+ predicate = predicate_bots_only # Delete messages from bots
+ elif user:
+ predicate = predicate_specific_user # Delete messages from specific user
+ elif regex:
+ predicate = predicate_regex # Delete messages that match regex
+ else:
+ predicate = None # Delete all messages
+
# Look through the history and retrieve message data
message_log = []
message_ids = []
-
self.cleaning = True
+ invocation_deleted = False
async for message in ctx.channel.history(limit=amount):
+ # If at any point the cancel command is invoked, we should stop.
if not self.cleaning:
return
- delete = (
- bots_only and message.author.bot # Delete bot messages
- or user and message.author == user # Delete user messages
- or not bots_only and not user # Delete all messages
- )
+ # Always start by deleting the invocation
+ if not invocation_deleted:
+ await message.delete()
+ invocation_deleted = True
+ continue
- if delete and message.content or message.embeds:
- content = message.content or message.embeds[0].description
+ # If the message passes predicate, let's save it.
+ if predicate is None or predicate(message):
author = f"{message.author.name}#{message.author.discriminator}"
role = message.author.top_role.name
- # Store the message data
+ content = message.content
+ embeds = [embed.to_dict() for embed in message.embeds]
+ attachments = ["<Attachment>" for _ in message.attachments]
+
message_ids.append(message.id)
message_log.append({
"content": content,
"author": author,
+ "user_id": str(message.author.id),
"role": role.lower(),
- "timestamp": message.created_at.strftime("%D %H:%M")
+ "timestamp": message.created_at.strftime("%D %H:%M"),
+ "attachments": attachments,
+ "embeds": embeds,
})
self.cleaning = False
@@ -119,13 +186,6 @@ class Clean:
self.mod_log.ignore_message_deletion(*message_ids)
# Use bulk delete to actually do the cleaning. It's far faster.
- predicate = None
-
- if bots_only:
- predicate = predicate_bots_only
- elif user:
- predicate = predicate_specific_user
-
await ctx.channel.purge(
limit=amount,
check=predicate
@@ -141,7 +201,7 @@ class Clean:
color=Colour(Colours.soft_red),
description="No matching messages could be found."
)
- await ctx.send(embed=embed)
+ await ctx.send(embed=embed, delete_after=10.0)
return
# Build the embed and send it
@@ -171,7 +231,7 @@ class Clean:
await ctx.invoke(self.bot.get_command("help"), "clean")
- @clean_group.command(aliases=["user"])
+ @clean_group.command(name="user", aliases=["users"])
@with_role(Roles.moderator, Roles.admin, Roles.owner)
async def clean_user(self, ctx: Context, user: User, amount: int = 10):
"""
@@ -181,7 +241,7 @@ class Clean:
await self._clean_messages(amount, ctx, user=user)
- @clean_group.command(aliases=["all"])
+ @clean_group.command(name="all", aliases=["everything"])
@with_role(Roles.moderator, Roles.admin, Roles.owner)
async def clean_all(self, ctx: Context, amount: int = 10):
"""
@@ -191,7 +251,7 @@ class Clean:
await self._clean_messages(amount, ctx)
- @clean_group.command(aliases=["bots"])
+ @clean_group.command(name="bots", aliases=["bot"])
@with_role(Roles.moderator, Roles.admin, Roles.owner)
async def clean_bots(self, ctx: Context, amount: int = 10):
"""
@@ -201,7 +261,17 @@ class Clean:
await self._clean_messages(amount, ctx, bots_only=True)
- @clean_group.command(aliases=["stop", "cancel", "abort"])
+ @clean_group.command(name="regex", aliases=["word", "expression"])
+ @with_role(Roles.moderator, Roles.admin, Roles.owner)
+ async def clean_regex(self, ctx: Context, regex, amount: int = 10):
+ """
+ Delete all messages that match a certain regex,
+ and stop cleaning after traversing `amount` messages.
+ """
+
+ await self._clean_messages(amount, ctx, regex=regex)
+
+ @clean_group.command(name="stop", aliases=["cancel", "abort"])
@with_role(Roles.moderator, Roles.admin, Roles.owner)
async def clean_cancel(self, ctx: Context):
"""
diff --git a/bot/cogs/modlog.py b/bot/cogs/modlog.py
index e8ff19bb5..b5a73d6e0 100644
--- a/bot/cogs/modlog.py
+++ b/bot/cogs/modlog.py
@@ -486,7 +486,7 @@ class ModLog:
response = f"**Attachments:** {len(message.attachments)}\n" + response
await self.send_log_message(
- Icons.message_delete, COLOUR_RED,
+ Icons.message_delete, Colours.soft_red,
"Message deleted",
response,
channel_id=Channels.message_log