Repository: python-discord/bot

Issue:
Minor improvements on the !clean command
There are a couple of bugs that need to fixed with the !clean command, and a few new features I'd like to see.

## Optional channel parameter
I'd like to be able to clean a channel from the comfort of another channel.

For example:
`!clean all 200 #python-discussion` should run this command inside the #python-discussion channel instead of in the channel it was invoked from.

When this happens, the bot should output its feedback into the channel you invoked the command in. So, if I was in #admins and I used this command, the bot should post its feedback in #admins, not in #python-discussion.

## Don't hide it from showing up in !help
Currently, this command doesn't show up when you do a `!help`, even if you have access to it. There's no reason why this should be true, it should be shown in `!help` output for anyone who has access to the command.

## New alias
Please add `purge` as an alias for `!clean` so that @scragly will be able to remember what this command is called. :grin: 


Discussion:

Resolving patch:
diff --git a/bot/cogs/clean.py b/bot/cogs/clean.py
--- a/bot/cogs/clean.py
+++ b/bot/cogs/clean.py
@@ -3,7 +3,7 @@
 import re
 from typing import Optional
 
-from discord import Colour, Embed, Message, User
+from discord import Colour, Embed, Message, TextChannel, User
 from discord.ext.commands import Cog, Context, group
 
 from bot.bot import Bot
@@ -38,9 +38,13 @@ def mod_log(self) -> ModLog:
         return self.bot.get_cog("ModLog")
 
     async def _clean_messages(
-            self, amount: int, ctx: Context,
-            bots_only: bool = False, user: User = None,
-            regex: Optional[str] = None
+        self,
+        amount: int,
+        ctx: Context,
+        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:
@@ -105,6 +109,10 @@ def predicate_regex(message: Message) -> bool:
         else:
             predicate = None                     # Delete all messages
 
+        # Default to using the invoking context's channel
+        if not channel:
+            channel = ctx.channel
+
         # Look through the history and retrieve message data
         messages = []
         message_ids = []
@@ -112,7 +120,7 @@ def predicate_regex(message: Message) -> bool:
         invocation_deleted = False
 
         # To account for the invocation message, we index `amount + 1` messages.
-        async for message in ctx.channel.history(limit=amount + 1):
+        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:
@@ -136,7 +144,7 @@ def predicate_regex(message: Message) -> bool:
         self.mod_log.ignore(Event.message_delete, *message_ids)
 
         # Use bulk delete to actually do the cleaning. It's far faster.
-        await ctx.channel.purge(
+        await channel.purge(
             limit=amount,
             check=predicate
         )
@@ -156,7 +164,7 @@ def predicate_regex(message: Message) -> bool:
 
         # Build the embed and send it
         message = (
-            f"**{len(message_ids)}** messages deleted in <#{ctx.channel.id}> by **{ctx.author.name}**\n\n"
+            f"**{len(message_ids)}** messages deleted in <#{channel.id}> by **{ctx.author.name}**\n\n"
             f"A log of the deleted messages can be found [here]({log_url})."
         )
 
@@ -168,7 +176,7 @@ def predicate_regex(message: Message) -> bool:
             channel_id=Channels.modlog,
         )
 
-    @group(invoke_without_command=True, name="clean", hidden=True)
+    @group(invoke_without_command=True, name="clean", aliases=["purge"])
     @with_role(*MODERATION_ROLES)
     async def clean_group(self, ctx: Context) -> None:
         """Commands for cleaning messages in channels."""
@@ -176,27 +184,49 @@ async def clean_group(self, ctx: Context) -> None:
 
     @clean_group.command(name="user", aliases=["users"])
     @with_role(*MODERATION_ROLES)
-    async def clean_user(self, ctx: Context, user: User, amount: int = 10) -> None:
+    async def clean_user(
+        self,
+        ctx: Context,
+        user: User,
+        amount: Optional[int] = 10,
+        channel: TextChannel = None
+    ) -> None:
         """Delete messages posted by the provided user, stop cleaning after traversing `amount` messages."""
-        await self._clean_messages(amount, ctx, user=user)
+        await self._clean_messages(amount, ctx, user=user, channel=channel)
 
     @clean_group.command(name="all", aliases=["everything"])
     @with_role(*MODERATION_ROLES)
-    async def clean_all(self, ctx: Context, amount: int = 10) -> None:
+    async def clean_all(
+        self,
+        ctx: Context,
+        amount: Optional[int] = 10,
+        channel: TextChannel = None
+    ) -> None:
         """Delete all messages, regardless of poster, stop cleaning after traversing `amount` messages."""
-        await self._clean_messages(amount, ctx)
+        await self._clean_messages(amount, ctx, channel=channel)
 
     @clean_group.command(name="bots", aliases=["bot"])
     @with_role(*MODERATION_ROLES)
-    async def clean_bots(self, ctx: Context, amount: int = 10) -> None:
+    async def clean_bots(
+        self,
+        ctx: Context,
+        amount: Optional[int] = 10,
+        channel: 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)
+        await self._clean_messages(amount, ctx, bots_only=True, channel=channel)
 
     @clean_group.command(name="regex", aliases=["word", "expression"])
     @with_role(*MODERATION_ROLES)
-    async def clean_regex(self, ctx: Context, regex: str, amount: int = 10) -> None:
+    async def clean_regex(
+        self,
+        ctx: Context,
+        regex: str,
+        amount: Optional[int] = 10,
+        channel: 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)
+        await self._clean_messages(amount, ctx, regex=regex, channel=channel)
 
     @clean_group.command(name="stop", aliases=["cancel", "abort"])
     @with_role(*MODERATION_ROLES)


Test patch: