Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 34 additions & 1 deletion bot/cogs/clean.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ async def _clean_messages(
bots_only: bool = False,
user: User = None,
regex: Optional[str] = None,
until_message: Optional[Message] = None,
) -> None:
"""A helper function that does the actual message cleaning."""
def predicate_bots_only(message: Message) -> bool:
Expand Down Expand Up @@ -129,6 +130,20 @@ def predicate_regex(message: Message) -> bool:
if not self.cleaning:
return

# If we are looking for specific message.
if until_message:

# we could use ID's here however in case if the message we are looking for gets deleted,
# we won't have a way to figure that out thus checking for datetime should be more reliable
if message.created_at < until_message.created_at:
# means we have found the message until which we were supposed to be deleting.
break

# Since we will be using `delete_messages` method of a TextChannel and we need message objects to
# use it as well as to send logs we will start appending messages here instead adding them from
# purge.
messages.append(message)

# If the message passes predicate, let's save it.
if predicate is None or predicate(message):
message_ids.append(message.id)
Expand All @@ -138,7 +153,14 @@ def predicate_regex(message: Message) -> bool:
# Now let's delete the actual messages with purge.
self.mod_log.ignore(Event.message_delete, *message_ids)
for channel in channels:
messages += await channel.purge(limit=amount, check=predicate)
if until_message:
for i in range(0, len(messages), 100):
# while purge automatically handles the amount of messages
# delete_messages only allows for up to 100 messages at once
# thus we need to paginate the amount to always be <= 100
await channel.delete_messages(messages[i:i + 100])
else:
messages += await channel.purge(limit=amount, check=predicate)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since you've essentially written all the code needed, can we get rid of purge? It redundantly iterates message history. We already have message objects from our own history search, so let's use those. That being said, one strange thing purge does is use a single delete strategy if messages are older than 2 weeks. I don't know if that's important.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reason why it does single deletes is because you can't delete messages older than 14 days via bulk delete endpoint, I didn't include that edge case in the code as (at least to me) it appears unlikely for moderators to attempt and purge messages older than that.
That being said attempts of deleting the messages older than 14 days via delete_messages will raise following error:

discord.errors.HTTPException: 400 Bad Request (error code: 50034): You can only bulk delete messages that are under 14 days old.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you think we can do away with purge though?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think purge is necessary considering we iterate the history either way, I am all up to rewriting current system so it doesn't use purge in separate PR as it isn't exactly the point of this PR


# Reverse the list to restore chronological order
if messages:
Expand Down Expand Up @@ -221,6 +243,17 @@ async def clean_regex(
"""Delete all messages that match a certain regex, stop cleaning after traversing `amount` messages."""
await self._clean_messages(amount, ctx, regex=regex, channels=channels)

@clean_group.command(name="message", aliases=["messages"])
@with_role(*MODERATION_ROLES)
async def clean_message(self, ctx: Context, message: Message) -> None:
"""Delete all messages until certain message, stop cleaning after hitting the `message`."""
await self._clean_messages(
CleanMessages.message_limit,
ctx,
channels=[message.channel],
until_message=message
)

@clean_group.command(name="stop", aliases=["cancel", "abort"])
@with_role(*MODERATION_ROLES)
async def clean_cancel(self, ctx: Context) -> None:
Expand Down