We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
[V3 Cleanup] Handle getting coroutine prefixes for [p]cleanup bot
### Type

- Bug

### Description
Coroutine prefixes aren't implemented for `cleanup bot`. This is kind of important since Red's default prefix is a coroutine.

This is pretty easy; we can just use `await bot.get_prefix(ctx.message)`, since this actually just returns a list of server prefixes.

[Relevant Sentry issue](https://sentry.io/will-tekulve/bot-development/issues/420947439/)

```
TypeError: argument of type 'coroutine' is not iterable
  File "discord/ext/commands/core.py", line 62, in wrapped
    ret = yield from coro(*args, **kwargs)
  File "cleanup/cleanup.py", line 234, in cleanup_bot
    if '' in prefixes:

Exception in command 'cleanup bot'
```

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `redbot/cogs/cleanup/cleanup.py`
Content:
```
1 import asyncio
2 import re
3 
4 import discord
5 from discord.ext import commands
6 
7 from redbot.core import checks
8 from redbot.core.bot import Red
9 from redbot.core.i18n import CogI18n
10 from redbot.core.utils.mod import slow_deletion, mass_purge
11 from redbot.cogs.mod.log import log
12 from redbot.core.context import RedContext
13 
14 _ = CogI18n("Cleanup", __file__)
15 
16 
17 class Cleanup:
18     """Commands for cleaning messages"""
19 
20     def __init__(self, bot: Red):
21         self.bot = bot
22 
23     @commands.group()
24     @checks.mod_or_permissions(manage_messages=True)
25     async def cleanup(self, ctx: RedContext):
26         """Deletes messages."""
27         if ctx.invoked_subcommand is None:
28             await ctx.send_help()
29 
30     @cleanup.command()
31     @commands.guild_only()
32     @commands.bot_has_permissions(manage_messages=True)
33     async def text(self, ctx: commands.Context, text: str, number: int):
34         """Deletes last X messages matching the specified text.
35 
36         Example:
37         cleanup text \"test\" 5
38 
39         Remember to use double quotes."""
40 
41         channel = ctx.channel
42         author = ctx.author
43         is_bot = self.bot.user.bot
44 
45         def check(m):
46             if text in m.content:
47                 return True
48             elif m == ctx.message:
49                 return True
50             else:
51                 return False
52 
53         to_delete = [ctx.message]
54         too_old = False
55         tmp = ctx.message
56 
57         while not too_old and len(to_delete) - 1 < number:
58             async for message in channel.history(limit=1000,
59                                                  before=tmp):
60                 if len(to_delete) - 1 < number and check(message) and\
61                         (ctx.message.created_at - message.created_at).days < 14:
62                     to_delete.append(message)
63                 elif (ctx.message.created_at - message.created_at).days >= 14:
64                     too_old = True
65                     break
66                 elif len(to_delete) >= number:
67                     break
68                 tmp = message
69 
70         reason = "{}({}) deleted {} messages "\
71                  " containing '{}' in channel {}".format(author.name,
72                                                          author.id, len(to_delete), text, channel.id)
73         log.info(reason)
74 
75         if is_bot:
76             await mass_purge(to_delete, channel)
77         else:
78             await slow_deletion(to_delete)
79 
80     @cleanup.command()
81     @commands.guild_only()
82     @commands.bot_has_permissions(manage_messages=True)
83     async def user(self, ctx: commands.Context, user: discord.Member or int, number: int):
84         """Deletes last X messages from specified user.
85 
86         Examples:
87         cleanup user @\u200bTwentysix 2
88         cleanup user Red 6"""
89 
90         channel = ctx.channel
91         author = ctx.author
92         is_bot = self.bot.user.bot
93 
94         def check(m):
95             if isinstance(user, discord.Member) and m.author == user:
96                 return True
97             elif m.author.id == user:  # Allow finding messages based on an ID
98                 return True
99             elif m == ctx.message:
100                 return True
101             else:
102                 return False
103 
104         to_delete = []
105         too_old = False
106         tmp = ctx.message
107 
108         while not too_old and len(to_delete) - 1 < number:
109             async for message in channel.history(limit=1000,
110                                                  before=tmp):
111                 if len(to_delete) - 1 < number and check(message) and\
112                         (ctx.message.created_at - message.created_at).days < 14:
113                     to_delete.append(message)
114                 elif (ctx.message.created_at - message.created_at).days >= 14:
115                     too_old = True
116                     break
117                 elif len(to_delete) >= number:
118                     break
119                 tmp = message
120         reason = "{}({}) deleted {} messages "\
121                  " made by {}({}) in channel {}"\
122                  "".format(author.name, author.id, len(to_delete),
123                            user.name, user.id, channel.name)
124         log.info(reason)
125 
126         if is_bot:
127             # For whatever reason the purge endpoint requires manage_messages
128             await mass_purge(to_delete, channel)
129         else:
130             await slow_deletion(to_delete)
131 
132     @cleanup.command()
133     @commands.guild_only()
134     @commands.bot_has_permissions(manage_messages=True)
135     async def after(self, ctx: commands.Context, message_id: int):
136         """Deletes all messages after specified message
137 
138         To get a message id, enable developer mode in Discord's
139         settings, 'appearance' tab. Then right click a message
140         and copy its id.
141 
142         This command only works on bots running as bot accounts.
143         """
144 
145         channel = ctx.channel
146         author = ctx.author
147         is_bot = self.bot.user.bot
148 
149         if not is_bot:
150             await ctx.send(_("This command can only be used on bots with "
151                              "bot accounts."))
152             return
153 
154         after = await channel.get_message(message_id)
155 
156         if not after:
157             await ctx.send(_("Message not found."))
158             return
159 
160         to_delete = []
161 
162         async for message in channel.history(after=after):
163             if (ctx.message.created_at - message.created_at).days < 14:
164                 # Only add messages that are less than
165                 # 14 days old to the deletion queue
166                 to_delete.append(message)
167 
168         reason = "{}({}) deleted {} messages in channel {}"\
169                  "".format(author.name, author.id,
170                            len(to_delete), channel.name)
171         log.info(reason)
172 
173         await mass_purge(to_delete, channel)
174 
175     @cleanup.command()
176     @commands.guild_only()
177     @commands.bot_has_permissions(manage_messages=True)
178     async def messages(self, ctx: commands.Context, number: int):
179         """Deletes last X messages.
180 
181         Example:
182         cleanup messages 26"""
183 
184         channel = ctx.channel
185         author = ctx.author
186 
187         is_bot = self.bot.user.bot
188 
189         to_delete = []
190         tmp = ctx.message
191 
192         done = False
193 
194         while len(to_delete) - 1 < number and not done:
195             async for message in channel.history(limit=1000, before=tmp):
196                 if len(to_delete) - 1 < number and \
197                         (ctx.message.created_at - message.created_at).days < 14:
198                     to_delete.append(message)
199                 elif (ctx.message.created_at - message.created_at).days >= 14:
200                     done = True
201                     break
202                 tmp = message
203 
204         reason = "{}({}) deleted {} messages in channel {}"\
205                  "".format(author.name, author.id,
206                            number, channel.name)
207         log.info(reason)
208 
209         if is_bot:
210             await mass_purge(to_delete, channel)
211         else:
212             await slow_deletion(to_delete)
213 
214     @cleanup.command(name='bot')
215     @commands.guild_only()
216     @commands.bot_has_permissions(manage_messages=True)
217     async def cleanup_bot(self, ctx: commands.Context, number: int):
218         """Cleans up command messages and messages from the bot"""
219 
220         channel = ctx.message.channel
221         author = ctx.message.author
222         is_bot = self.bot.user.bot
223 
224         prefixes = self.bot.command_prefix
225         if isinstance(prefixes, str):
226             prefixes = [prefixes]
227         elif callable(prefixes):
228             if asyncio.iscoroutine(prefixes):
229                 await ctx.send(_('Coroutine prefixes not yet implemented.'))
230                 return
231             prefixes = prefixes(self.bot, ctx.message)
232 
233         # In case some idiot sets a null prefix
234         if '' in prefixes:
235             prefixes.remove('')
236 
237         def check(m):
238             if m.author.id == self.bot.user.id:
239                 return True
240             elif m == ctx.message:
241                 return True
242             p = discord.utils.find(m.content.startswith, prefixes)
243             if p and len(p) > 0:
244                 return m.content[len(p):].startswith(tuple(self.bot.commands))
245             return False
246 
247         to_delete = [ctx.message]
248         too_old = False
249         tmp = ctx.message
250 
251         while not too_old and len(to_delete) - 1 < number:
252             async for message in channel.history(limit=1000, before=tmp):
253                 if len(to_delete) - 1 < number and check(message) and\
254                                 (ctx.message.created_at - message.created_at).days < 14:
255                     to_delete.append(message)
256                 elif (ctx.message.created_at - message.created_at).days >= 14:
257                     too_old = True
258                     break
259                 elif len(to_delete) >= number:
260                     break
261                 tmp = message
262 
263         reason = "{}({}) deleted {} "\
264                  " command messages in channel {}"\
265                  "".format(author.name, author.id, len(to_delete),
266                            channel.name)
267         log.info(reason)
268 
269         if is_bot:
270             await mass_purge(to_delete, channel)
271         else:
272             await slow_deletion(to_delete)
273 
274     @cleanup.command(name='self')
275     async def cleanup_self(self, ctx: commands.Context, number: int, match_pattern: str = None):
276         """Cleans up messages owned by the bot.
277 
278         By default, all messages are cleaned. If a third argument is specified,
279         it is used for pattern matching: If it begins with r( and ends with ),
280         then it is interpreted as a regex, and messages that match it are
281         deleted. Otherwise, it is used in a simple substring test.
282 
283         Some helpful regex flags to include in your pattern:
284         Dots match newlines: (?s); Ignore case: (?i); Both: (?si)
285         """
286         channel = ctx.channel
287         author = ctx.message.author
288         is_bot = self.bot.user.bot
289 
290         # You can always delete your own messages, this is needed to purge
291         can_mass_purge = False
292         if type(author) is discord.Member:
293             me = ctx.guild.me
294             can_mass_purge = channel.permissions_for(me).manage_messages
295 
296         use_re = (match_pattern and match_pattern.startswith('r(') and
297                   match_pattern.endswith(')'))
298 
299         if use_re:
300             match_pattern = match_pattern[1:]  # strip 'r'
301             match_re = re.compile(match_pattern)
302 
303             def content_match(c):
304                 return bool(match_re.match(c))
305         elif match_pattern:
306             def content_match(c):
307                 return match_pattern in c
308         else:
309             def content_match(_):
310                 return True
311 
312         def check(m):
313             if m.author.id != self.bot.user.id:
314                 return False
315             elif content_match(m.content):
316                 return True
317             return False
318 
319         to_delete = []
320         # Selfbot convenience, delete trigger message
321         if author == self.bot.user:
322             to_delete.append(ctx.message)
323             number += 1
324         too_old = False
325         tmp = ctx.message
326         while not too_old and len(to_delete) < number:
327             async for message in channel.history(limit=1000, before=tmp):
328                 if len(to_delete) < number and check(message) and\
329                         (ctx.message.created_at - message.created_at).days < 14:
330                     to_delete.append(message)
331                 elif (ctx.message.created_at - message.created_at).days >= 14:
332                     # Found a message that is 14 or more days old, stop here
333                     too_old = True
334                     break
335                 elif len(to_delete) >= number:
336                     break
337                 tmp = message
338 
339         if channel.name:
340             channel_name = 'channel ' + channel.name
341         else:
342             channel_name = str(channel)
343 
344         reason = "{}({}) deleted {} messages "\
345                  "sent by the bot in {}"\
346                  "".format(author.name, author.id, len(to_delete),
347                            channel_name)
348         log.info(reason)
349 
350         if is_bot and can_mass_purge:
351             await mass_purge(to_delete, channel)
352         else:
353             await slow_deletion(to_delete)
354
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/redbot/cogs/cleanup/cleanup.py b/redbot/cogs/cleanup/cleanup.py
--- a/redbot/cogs/cleanup/cleanup.py
+++ b/redbot/cogs/cleanup/cleanup.py
@@ -221,14 +221,9 @@
         author = ctx.message.author
         is_bot = self.bot.user.bot
 
-        prefixes = self.bot.command_prefix
+        prefixes = await self.bot.get_prefix(ctx.message) # This returns all server prefixes
         if isinstance(prefixes, str):
             prefixes = [prefixes]
-        elif callable(prefixes):
-            if asyncio.iscoroutine(prefixes):
-                await ctx.send(_('Coroutine prefixes not yet implemented.'))
-                return
-            prefixes = prefixes(self.bot, ctx.message)
 
         # In case some idiot sets a null prefix
         if '' in prefixes:
@@ -241,7 +236,8 @@
                 return True
             p = discord.utils.find(m.content.startswith, prefixes)
             if p and len(p) > 0:
-                return m.content[len(p):].startswith(tuple(self.bot.commands))
+                cmd_name = m.content[len(p):].split(' ')[0]
+                return bool(self.bot.get_command(cmd_name))
             return False
 
         to_delete = [ctx.message]

{"golden_diff": "diff --git a/redbot/cogs/cleanup/cleanup.py b/redbot/cogs/cleanup/cleanup.py\n--- a/redbot/cogs/cleanup/cleanup.py\n+++ b/redbot/cogs/cleanup/cleanup.py\n@@ -221,14 +221,9 @@\n         author = ctx.message.author\n         is_bot = self.bot.user.bot\n \n-        prefixes = self.bot.command_prefix\n+        prefixes = await self.bot.get_prefix(ctx.message) # This returns all server prefixes\n         if isinstance(prefixes, str):\n             prefixes = [prefixes]\n-        elif callable(prefixes):\n-            if asyncio.iscoroutine(prefixes):\n-                await ctx.send(_('Coroutine prefixes not yet implemented.'))\n-                return\n-            prefixes = prefixes(self.bot, ctx.message)\n \n         # In case some idiot sets a null prefix\n         if '' in prefixes:\n@@ -241,7 +236,8 @@\n                 return True\n             p = discord.utils.find(m.content.startswith, prefixes)\n             if p and len(p) > 0:\n-                return m.content[len(p):].startswith(tuple(self.bot.commands))\n+                cmd_name = m.content[len(p):].split(' ')[0]\n+                return bool(self.bot.get_command(cmd_name))\n             return False\n \n         to_delete = [ctx.message]\n", "issue": "[V3 Cleanup] Handle getting coroutine prefixes for [p]cleanup bot\n### Type\n\n- Bug\n\n### Description\nCoroutine prefixes aren't implemented for `cleanup bot`. This is kind of important since Red's default prefix is a coroutine.\n\nThis is pretty easy; we can just use `await bot.get_prefix(ctx.message)`, since this actually just returns a list of server prefixes.\n\n[Relevant Sentry issue](https://sentry.io/will-tekulve/bot-development/issues/420947439/)\n\n```\nTypeError: argument of type 'coroutine' is not iterable\n  File \"discord/ext/commands/core.py\", line 62, in wrapped\n    ret = yield from coro(*args, **kwargs)\n  File \"cleanup/cleanup.py\", line 234, in cleanup_bot\n    if '' in prefixes:\n\nException in command 'cleanup bot'\n```\n", "before_files": [{"content": "import asyncio\nimport re\n\nimport discord\nfrom discord.ext import commands\n\nfrom redbot.core import checks\nfrom redbot.core.bot import Red\nfrom redbot.core.i18n import CogI18n\nfrom redbot.core.utils.mod import slow_deletion, mass_purge\nfrom redbot.cogs.mod.log import log\nfrom redbot.core.context import RedContext\n\n_ = CogI18n(\"Cleanup\", __file__)\n\n\nclass Cleanup:\n    \"\"\"Commands for cleaning messages\"\"\"\n\n    def __init__(self, bot: Red):\n        self.bot = bot\n\n    @commands.group()\n    @checks.mod_or_permissions(manage_messages=True)\n    async def cleanup(self, ctx: RedContext):\n        \"\"\"Deletes messages.\"\"\"\n        if ctx.invoked_subcommand is None:\n            await ctx.send_help()\n\n    @cleanup.command()\n    @commands.guild_only()\n    @commands.bot_has_permissions(manage_messages=True)\n    async def text(self, ctx: commands.Context, text: str, number: int):\n        \"\"\"Deletes last X messages matching the specified text.\n\n        Example:\n        cleanup text \\\"test\\\" 5\n\n        Remember to use double quotes.\"\"\"\n\n        channel = ctx.channel\n        author = ctx.author\n        is_bot = self.bot.user.bot\n\n        def check(m):\n            if text in m.content:\n                return True\n            elif m == ctx.message:\n                return True\n            else:\n                return False\n\n        to_delete = [ctx.message]\n        too_old = False\n        tmp = ctx.message\n\n        while not too_old and len(to_delete) - 1 < number:\n            async for message in channel.history(limit=1000,\n                                                 before=tmp):\n                if len(to_delete) - 1 < number and check(message) and\\\n                        (ctx.message.created_at - message.created_at).days < 14:\n                    to_delete.append(message)\n                elif (ctx.message.created_at - message.created_at).days >= 14:\n                    too_old = True\n                    break\n                elif len(to_delete) >= number:\n                    break\n                tmp = message\n\n        reason = \"{}({}) deleted {} messages \"\\\n                 \" containing '{}' in channel {}\".format(author.name,\n                                                         author.id, len(to_delete), text, channel.id)\n        log.info(reason)\n\n        if is_bot:\n            await mass_purge(to_delete, channel)\n        else:\n            await slow_deletion(to_delete)\n\n    @cleanup.command()\n    @commands.guild_only()\n    @commands.bot_has_permissions(manage_messages=True)\n    async def user(self, ctx: commands.Context, user: discord.Member or int, number: int):\n        \"\"\"Deletes last X messages from specified user.\n\n        Examples:\n        cleanup user @\\u200bTwentysix 2\n        cleanup user Red 6\"\"\"\n\n        channel = ctx.channel\n        author = ctx.author\n        is_bot = self.bot.user.bot\n\n        def check(m):\n            if isinstance(user, discord.Member) and m.author == user:\n                return True\n            elif m.author.id == user:  # Allow finding messages based on an ID\n                return True\n            elif m == ctx.message:\n                return True\n            else:\n                return False\n\n        to_delete = []\n        too_old = False\n        tmp = ctx.message\n\n        while not too_old and len(to_delete) - 1 < number:\n            async for message in channel.history(limit=1000,\n                                                 before=tmp):\n                if len(to_delete) - 1 < number and check(message) and\\\n                        (ctx.message.created_at - message.created_at).days < 14:\n                    to_delete.append(message)\n                elif (ctx.message.created_at - message.created_at).days >= 14:\n                    too_old = True\n                    break\n                elif len(to_delete) >= number:\n                    break\n                tmp = message\n        reason = \"{}({}) deleted {} messages \"\\\n                 \" made by {}({}) in channel {}\"\\\n                 \"\".format(author.name, author.id, len(to_delete),\n                           user.name, user.id, channel.name)\n        log.info(reason)\n\n        if is_bot:\n            # For whatever reason the purge endpoint requires manage_messages\n            await mass_purge(to_delete, channel)\n        else:\n            await slow_deletion(to_delete)\n\n    @cleanup.command()\n    @commands.guild_only()\n    @commands.bot_has_permissions(manage_messages=True)\n    async def after(self, ctx: commands.Context, message_id: int):\n        \"\"\"Deletes all messages after specified message\n\n        To get a message id, enable developer mode in Discord's\n        settings, 'appearance' tab. Then right click a message\n        and copy its id.\n\n        This command only works on bots running as bot accounts.\n        \"\"\"\n\n        channel = ctx.channel\n        author = ctx.author\n        is_bot = self.bot.user.bot\n\n        if not is_bot:\n            await ctx.send(_(\"This command can only be used on bots with \"\n                             \"bot accounts.\"))\n            return\n\n        after = await channel.get_message(message_id)\n\n        if not after:\n            await ctx.send(_(\"Message not found.\"))\n            return\n\n        to_delete = []\n\n        async for message in channel.history(after=after):\n            if (ctx.message.created_at - message.created_at).days < 14:\n                # Only add messages that are less than\n                # 14 days old to the deletion queue\n                to_delete.append(message)\n\n        reason = \"{}({}) deleted {} messages in channel {}\"\\\n                 \"\".format(author.name, author.id,\n                           len(to_delete), channel.name)\n        log.info(reason)\n\n        await mass_purge(to_delete, channel)\n\n    @cleanup.command()\n    @commands.guild_only()\n    @commands.bot_has_permissions(manage_messages=True)\n    async def messages(self, ctx: commands.Context, number: int):\n        \"\"\"Deletes last X messages.\n\n        Example:\n        cleanup messages 26\"\"\"\n\n        channel = ctx.channel\n        author = ctx.author\n\n        is_bot = self.bot.user.bot\n\n        to_delete = []\n        tmp = ctx.message\n\n        done = False\n\n        while len(to_delete) - 1 < number and not done:\n            async for message in channel.history(limit=1000, before=tmp):\n                if len(to_delete) - 1 < number and \\\n                        (ctx.message.created_at - message.created_at).days < 14:\n                    to_delete.append(message)\n                elif (ctx.message.created_at - message.created_at).days >= 14:\n                    done = True\n                    break\n                tmp = message\n\n        reason = \"{}({}) deleted {} messages in channel {}\"\\\n                 \"\".format(author.name, author.id,\n                           number, channel.name)\n        log.info(reason)\n\n        if is_bot:\n            await mass_purge(to_delete, channel)\n        else:\n            await slow_deletion(to_delete)\n\n    @cleanup.command(name='bot')\n    @commands.guild_only()\n    @commands.bot_has_permissions(manage_messages=True)\n    async def cleanup_bot(self, ctx: commands.Context, number: int):\n        \"\"\"Cleans up command messages and messages from the bot\"\"\"\n\n        channel = ctx.message.channel\n        author = ctx.message.author\n        is_bot = self.bot.user.bot\n\n        prefixes = self.bot.command_prefix\n        if isinstance(prefixes, str):\n            prefixes = [prefixes]\n        elif callable(prefixes):\n            if asyncio.iscoroutine(prefixes):\n                await ctx.send(_('Coroutine prefixes not yet implemented.'))\n                return\n            prefixes = prefixes(self.bot, ctx.message)\n\n        # In case some idiot sets a null prefix\n        if '' in prefixes:\n            prefixes.remove('')\n\n        def check(m):\n            if m.author.id == self.bot.user.id:\n                return True\n            elif m == ctx.message:\n                return True\n            p = discord.utils.find(m.content.startswith, prefixes)\n            if p and len(p) > 0:\n                return m.content[len(p):].startswith(tuple(self.bot.commands))\n            return False\n\n        to_delete = [ctx.message]\n        too_old = False\n        tmp = ctx.message\n\n        while not too_old and len(to_delete) - 1 < number:\n            async for message in channel.history(limit=1000, before=tmp):\n                if len(to_delete) - 1 < number and check(message) and\\\n                                (ctx.message.created_at - message.created_at).days < 14:\n                    to_delete.append(message)\n                elif (ctx.message.created_at - message.created_at).days >= 14:\n                    too_old = True\n                    break\n                elif len(to_delete) >= number:\n                    break\n                tmp = message\n\n        reason = \"{}({}) deleted {} \"\\\n                 \" command messages in channel {}\"\\\n                 \"\".format(author.name, author.id, len(to_delete),\n                           channel.name)\n        log.info(reason)\n\n        if is_bot:\n            await mass_purge(to_delete, channel)\n        else:\n            await slow_deletion(to_delete)\n\n    @cleanup.command(name='self')\n    async def cleanup_self(self, ctx: commands.Context, number: int, match_pattern: str = None):\n        \"\"\"Cleans up messages owned by the bot.\n\n        By default, all messages are cleaned. If a third argument is specified,\n        it is used for pattern matching: If it begins with r( and ends with ),\n        then it is interpreted as a regex, and messages that match it are\n        deleted. Otherwise, it is used in a simple substring test.\n\n        Some helpful regex flags to include in your pattern:\n        Dots match newlines: (?s); Ignore case: (?i); Both: (?si)\n        \"\"\"\n        channel = ctx.channel\n        author = ctx.message.author\n        is_bot = self.bot.user.bot\n\n        # You can always delete your own messages, this is needed to purge\n        can_mass_purge = False\n        if type(author) is discord.Member:\n            me = ctx.guild.me\n            can_mass_purge = channel.permissions_for(me).manage_messages\n\n        use_re = (match_pattern and match_pattern.startswith('r(') and\n                  match_pattern.endswith(')'))\n\n        if use_re:\n            match_pattern = match_pattern[1:]  # strip 'r'\n            match_re = re.compile(match_pattern)\n\n            def content_match(c):\n                return bool(match_re.match(c))\n        elif match_pattern:\n            def content_match(c):\n                return match_pattern in c\n        else:\n            def content_match(_):\n                return True\n\n        def check(m):\n            if m.author.id != self.bot.user.id:\n                return False\n            elif content_match(m.content):\n                return True\n            return False\n\n        to_delete = []\n        # Selfbot convenience, delete trigger message\n        if author == self.bot.user:\n            to_delete.append(ctx.message)\n            number += 1\n        too_old = False\n        tmp = ctx.message\n        while not too_old and len(to_delete) < number:\n            async for message in channel.history(limit=1000, before=tmp):\n                if len(to_delete) < number and check(message) and\\\n                        (ctx.message.created_at - message.created_at).days < 14:\n                    to_delete.append(message)\n                elif (ctx.message.created_at - message.created_at).days >= 14:\n                    # Found a message that is 14 or more days old, stop here\n                    too_old = True\n                    break\n                elif len(to_delete) >= number:\n                    break\n                tmp = message\n\n        if channel.name:\n            channel_name = 'channel ' + channel.name\n        else:\n            channel_name = str(channel)\n\n        reason = \"{}({}) deleted {} messages \"\\\n                 \"sent by the bot in {}\"\\\n                 \"\".format(author.name, author.id, len(to_delete),\n                           channel_name)\n        log.info(reason)\n\n        if is_bot and can_mass_purge:\n            await mass_purge(to_delete, channel)\n        else:\n            await slow_deletion(to_delete)\n", "path": "redbot/cogs/cleanup/cleanup.py"}], "after_files": [{"content": "import asyncio\nimport re\n\nimport discord\nfrom discord.ext import commands\n\nfrom redbot.core import checks\nfrom redbot.core.bot import Red\nfrom redbot.core.i18n import CogI18n\nfrom redbot.core.utils.mod import slow_deletion, mass_purge\nfrom redbot.cogs.mod.log import log\nfrom redbot.core.context import RedContext\n\n_ = CogI18n(\"Cleanup\", __file__)\n\n\nclass Cleanup:\n    \"\"\"Commands for cleaning messages\"\"\"\n\n    def __init__(self, bot: Red):\n        self.bot = bot\n\n    @commands.group()\n    @checks.mod_or_permissions(manage_messages=True)\n    async def cleanup(self, ctx: RedContext):\n        \"\"\"Deletes messages.\"\"\"\n        if ctx.invoked_subcommand is None:\n            await ctx.send_help()\n\n    @cleanup.command()\n    @commands.guild_only()\n    @commands.bot_has_permissions(manage_messages=True)\n    async def text(self, ctx: commands.Context, text: str, number: int):\n        \"\"\"Deletes last X messages matching the specified text.\n\n        Example:\n        cleanup text \\\"test\\\" 5\n\n        Remember to use double quotes.\"\"\"\n\n        channel = ctx.channel\n        author = ctx.author\n        is_bot = self.bot.user.bot\n\n        def check(m):\n            if text in m.content:\n                return True\n            elif m == ctx.message:\n                return True\n            else:\n                return False\n\n        to_delete = [ctx.message]\n        too_old = False\n        tmp = ctx.message\n\n        while not too_old and len(to_delete) - 1 < number:\n            async for message in channel.history(limit=1000,\n                                                 before=tmp):\n                if len(to_delete) - 1 < number and check(message) and\\\n                        (ctx.message.created_at - message.created_at).days < 14:\n                    to_delete.append(message)\n                elif (ctx.message.created_at - message.created_at).days >= 14:\n                    too_old = True\n                    break\n                elif len(to_delete) >= number:\n                    break\n                tmp = message\n\n        reason = \"{}({}) deleted {} messages \"\\\n                 \" containing '{}' in channel {}\".format(author.name,\n                                                         author.id, len(to_delete), text, channel.id)\n        log.info(reason)\n\n        if is_bot:\n            await mass_purge(to_delete, channel)\n        else:\n            await slow_deletion(to_delete)\n\n    @cleanup.command()\n    @commands.guild_only()\n    @commands.bot_has_permissions(manage_messages=True)\n    async def user(self, ctx: commands.Context, user: discord.Member or int, number: int):\n        \"\"\"Deletes last X messages from specified user.\n\n        Examples:\n        cleanup user @\\u200bTwentysix 2\n        cleanup user Red 6\"\"\"\n\n        channel = ctx.channel\n        author = ctx.author\n        is_bot = self.bot.user.bot\n\n        def check(m):\n            if isinstance(user, discord.Member) and m.author == user:\n                return True\n            elif m.author.id == user:  # Allow finding messages based on an ID\n                return True\n            elif m == ctx.message:\n                return True\n            else:\n                return False\n\n        to_delete = []\n        too_old = False\n        tmp = ctx.message\n\n        while not too_old and len(to_delete) - 1 < number:\n            async for message in channel.history(limit=1000,\n                                                 before=tmp):\n                if len(to_delete) - 1 < number and check(message) and\\\n                        (ctx.message.created_at - message.created_at).days < 14:\n                    to_delete.append(message)\n                elif (ctx.message.created_at - message.created_at).days >= 14:\n                    too_old = True\n                    break\n                elif len(to_delete) >= number:\n                    break\n                tmp = message\n        reason = \"{}({}) deleted {} messages \"\\\n                 \" made by {}({}) in channel {}\"\\\n                 \"\".format(author.name, author.id, len(to_delete),\n                           user.name, user.id, channel.name)\n        log.info(reason)\n\n        if is_bot:\n            # For whatever reason the purge endpoint requires manage_messages\n            await mass_purge(to_delete, channel)\n        else:\n            await slow_deletion(to_delete)\n\n    @cleanup.command()\n    @commands.guild_only()\n    @commands.bot_has_permissions(manage_messages=True)\n    async def after(self, ctx: commands.Context, message_id: int):\n        \"\"\"Deletes all messages after specified message\n\n        To get a message id, enable developer mode in Discord's\n        settings, 'appearance' tab. Then right click a message\n        and copy its id.\n\n        This command only works on bots running as bot accounts.\n        \"\"\"\n\n        channel = ctx.channel\n        author = ctx.author\n        is_bot = self.bot.user.bot\n\n        if not is_bot:\n            await ctx.send(_(\"This command can only be used on bots with \"\n                             \"bot accounts.\"))\n            return\n\n        after = await channel.get_message(message_id)\n\n        if not after:\n            await ctx.send(_(\"Message not found.\"))\n            return\n\n        to_delete = []\n\n        async for message in channel.history(after=after):\n            if (ctx.message.created_at - message.created_at).days < 14:\n                # Only add messages that are less than\n                # 14 days old to the deletion queue\n                to_delete.append(message)\n\n        reason = \"{}({}) deleted {} messages in channel {}\"\\\n                 \"\".format(author.name, author.id,\n                           len(to_delete), channel.name)\n        log.info(reason)\n\n        await mass_purge(to_delete, channel)\n\n    @cleanup.command()\n    @commands.guild_only()\n    @commands.bot_has_permissions(manage_messages=True)\n    async def messages(self, ctx: commands.Context, number: int):\n        \"\"\"Deletes last X messages.\n\n        Example:\n        cleanup messages 26\"\"\"\n\n        channel = ctx.channel\n        author = ctx.author\n\n        is_bot = self.bot.user.bot\n\n        to_delete = []\n        tmp = ctx.message\n\n        done = False\n\n        while len(to_delete) - 1 < number and not done:\n            async for message in channel.history(limit=1000, before=tmp):\n                if len(to_delete) - 1 < number and \\\n                        (ctx.message.created_at - message.created_at).days < 14:\n                    to_delete.append(message)\n                elif (ctx.message.created_at - message.created_at).days >= 14:\n                    done = True\n                    break\n                tmp = message\n\n        reason = \"{}({}) deleted {} messages in channel {}\"\\\n                 \"\".format(author.name, author.id,\n                           number, channel.name)\n        log.info(reason)\n\n        if is_bot:\n            await mass_purge(to_delete, channel)\n        else:\n            await slow_deletion(to_delete)\n\n    @cleanup.command(name='bot')\n    @commands.guild_only()\n    @commands.bot_has_permissions(manage_messages=True)\n    async def cleanup_bot(self, ctx: commands.Context, number: int):\n        \"\"\"Cleans up command messages and messages from the bot\"\"\"\n\n        channel = ctx.message.channel\n        author = ctx.message.author\n        is_bot = self.bot.user.bot\n\n        prefixes = await self.bot.get_prefix(ctx.message) # This returns all server prefixes\n        if isinstance(prefixes, str):\n            prefixes = [prefixes]\n\n        # In case some idiot sets a null prefix\n        if '' in prefixes:\n            prefixes.remove('')\n\n        def check(m):\n            if m.author.id == self.bot.user.id:\n                return True\n            elif m == ctx.message:\n                return True\n            p = discord.utils.find(m.content.startswith, prefixes)\n            if p and len(p) > 0:\n                cmd_name = m.content[len(p):].split(' ')[0]\n                return bool(self.bot.get_command(cmd_name))\n            return False\n\n        to_delete = [ctx.message]\n        too_old = False\n        tmp = ctx.message\n\n        while not too_old and len(to_delete) - 1 < number:\n            async for message in channel.history(limit=1000, before=tmp):\n                if len(to_delete) - 1 < number and check(message) and\\\n                                (ctx.message.created_at - message.created_at).days < 14:\n                    to_delete.append(message)\n                elif (ctx.message.created_at - message.created_at).days >= 14:\n                    too_old = True\n                    break\n                elif len(to_delete) >= number:\n                    break\n                tmp = message\n\n        reason = \"{}({}) deleted {} \"\\\n                 \" command messages in channel {}\"\\\n                 \"\".format(author.name, author.id, len(to_delete),\n                           channel.name)\n        log.info(reason)\n\n        if is_bot:\n            await mass_purge(to_delete, channel)\n        else:\n            await slow_deletion(to_delete)\n\n    @cleanup.command(name='self')\n    async def cleanup_self(self, ctx: commands.Context, number: int, match_pattern: str = None):\n        \"\"\"Cleans up messages owned by the bot.\n\n        By default, all messages are cleaned. If a third argument is specified,\n        it is used for pattern matching: If it begins with r( and ends with ),\n        then it is interpreted as a regex, and messages that match it are\n        deleted. Otherwise, it is used in a simple substring test.\n\n        Some helpful regex flags to include in your pattern:\n        Dots match newlines: (?s); Ignore case: (?i); Both: (?si)\n        \"\"\"\n        channel = ctx.channel\n        author = ctx.message.author\n        is_bot = self.bot.user.bot\n\n        # You can always delete your own messages, this is needed to purge\n        can_mass_purge = False\n        if type(author) is discord.Member:\n            me = ctx.guild.me\n            can_mass_purge = channel.permissions_for(me).manage_messages\n\n        use_re = (match_pattern and match_pattern.startswith('r(') and\n                  match_pattern.endswith(')'))\n\n        if use_re:\n            match_pattern = match_pattern[1:]  # strip 'r'\n            match_re = re.compile(match_pattern)\n\n            def content_match(c):\n                return bool(match_re.match(c))\n        elif match_pattern:\n            def content_match(c):\n                return match_pattern in c\n        else:\n            def content_match(_):\n                return True\n\n        def check(m):\n            if m.author.id != self.bot.user.id:\n                return False\n            elif content_match(m.content):\n                return True\n            return False\n\n        to_delete = []\n        # Selfbot convenience, delete trigger message\n        if author == self.bot.user:\n            to_delete.append(ctx.message)\n            number += 1\n        too_old = False\n        tmp = ctx.message\n        while not too_old and len(to_delete) < number:\n            async for message in channel.history(limit=1000, before=tmp):\n                if len(to_delete) < number and check(message) and\\\n                        (ctx.message.created_at - message.created_at).days < 14:\n                    to_delete.append(message)\n                elif (ctx.message.created_at - message.created_at).days >= 14:\n                    # Found a message that is 14 or more days old, stop here\n                    too_old = True\n                    break\n                elif len(to_delete) >= number:\n                    break\n                tmp = message\n\n        if channel.name:\n            channel_name = 'channel ' + channel.name\n        else:\n            channel_name = str(channel)\n\n        reason = \"{}({}) deleted {} messages \"\\\n                 \"sent by the bot in {}\"\\\n                 \"\".format(author.name, author.id, len(to_delete),\n                           channel_name)\n        log.info(reason)\n\n        if is_bot and can_mass_purge:\n            await mass_purge(to_delete, channel)\n        else:\n            await slow_deletion(to_delete)\n", "path": "redbot/cogs/cleanup/cleanup.py"}]}