We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
[V3 Cleanup]  Cleanup messages not deleting if parameter bigger then channel size
### Type:

- [ ] Suggestion
- [X] Bug

### Brief description of the problem

On 3.0.0b11, when `[p]cleanup messages` is used and the number parameter is bigger then the messages in the channel, Red will not delete all messages.  Since the bug is regarding deletion added walkthrough screenshots.

![1](https://user-images.githubusercontent.com/841141/38463983-a517448e-3ad3-11e8-9427-4a0e160ca094.PNG)
![2](https://user-images.githubusercontent.com/841141/38463985-a6e8e09c-3ad3-11e8-86d7-2c3d55d12db7.PNG)


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

{"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@@ -55,6 +55,7 @@\n         too_old = False\n \n         while not too_old and len(to_delete) - 1 < number:\n+            message = None\n             async for message in channel.history(limit=limit,\n                                                  before=before,\n                                                  after=after):\n@@ -66,6 +67,9 @@\n                     break\n                 elif number and len(to_delete) >= number:\n                     break\n+            if message is None:\n+                break\n+            else:\n                 before = message\n         return to_delete\n", "issue": "[V3 Cleanup]  Cleanup messages not deleting if parameter bigger then channel size\n### Type:\r\n\r\n- [ ] Suggestion\r\n- [X] Bug\r\n\r\n### Brief description of the problem\r\n\r\nOn 3.0.0b11, when `[p]cleanup messages` is used and the number parameter is bigger then the messages in the channel, Red will not delete all messages.  Since the bug is regarding deletion added walkthrough screenshots.\r\n\r\n![1](https://user-images.githubusercontent.com/841141/38463983-a517448e-3ad3-11e8-9427-4a0e160ca094.PNG)\r\n![2](https://user-images.githubusercontent.com/841141/38463985-a6e8e09c-3ad3-11e8-86d7-2c3d55d12db7.PNG)\r\n\n", "before_files": [{"content": "import re\n\nimport discord\nfrom discord.ext import commands\n\nfrom redbot.core import checks, RedContext\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\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    @staticmethod\n    async def check_100_plus(ctx: RedContext, number: int) -> bool:\n        \"\"\"\n        Called when trying to delete more than 100 messages at once\n\n        Prompts the user to choose whether they want to continue or not\n        \"\"\"\n        def author_check(message):\n            return message.author == ctx.author\n\n        await ctx.send(_('Are you sure you want to delete {} messages? (y/n)').format(number))\n        response = await ctx.bot.wait_for('message', check=author_check)\n\n        if response.content.lower().startswith('y'):\n            return True\n        else:\n            await ctx.send(_('Cancelled.'))\n            return False\n\n    @staticmethod\n    async def get_messages_for_deletion(\n            ctx: RedContext, channel: discord.TextChannel, number,\n            check=lambda x: True, limit=100, before=None, after=None\n    ) -> list:\n        \"\"\"\n        Gets a list of messages meeting the requirements to be deleted.\n\n        Generally, the requirements are:\n        - We don't have the number of messages to be deleted already\n        - The message passes a provided check (if no check is provided,\n          this is automatically true)\n        - The message is less than 14 days old\n        \"\"\"\n        to_delete = []\n        too_old = False\n\n        while not too_old and len(to_delete) - 1 < number:\n            async for message in channel.history(limit=limit,\n                                                 before=before,\n                                                 after=after):\n                if (not number or len(to_delete) - 1 < number) and check(message) \\\n                        and (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 number and len(to_delete) >= number:\n                    break\n                before = message\n        return to_delete\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: RedContext, 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        if number > 100:\n            cont = await self.check_100_plus(ctx, number)\n            if not cont:\n                return\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 = await self.get_messages_for_deletion(\n            ctx, channel, number, check=check, limit=1000, before=ctx.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: RedContext, 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        if number > 100:\n            cont = await self.check_100_plus(ctx, number)\n            if not cont:\n                return\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 = await self.get_messages_for_deletion(\n            ctx, channel, number, check=check, limit=1000, before=ctx.message\n        )\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: RedContext, 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 = await self.get_messages_for_deletion(\n            ctx, channel, 0, limit=None, after=after\n        )\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: RedContext, 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        if number > 100:\n            cont = await self.check_100_plus(ctx, number)\n            if not cont:\n                return\n\n        to_delete = await self.get_messages_for_deletion(\n            ctx, channel, number, limit=1000, before=ctx.message\n        )\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: RedContext, 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        if number > 100:\n            cont = await self.check_100_plus(ctx, number)\n            if not cont:\n                return\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 = await self.get_messages_for_deletion(\n            ctx, channel, number, check=check, limit=1000, before=ctx.message\n        )\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: RedContext, 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        if number > 100:\n            cont = await self.check_100_plus(ctx, number)\n            if not cont:\n                return\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 = await self.get_messages_for_deletion(\n            ctx, channel, number, check=check, limit=1000, before=ctx.message\n        )\n\n        # Selfbot convenience, delete trigger message\n        if author == self.bot.user:\n            to_delete.append(ctx.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 re\n\nimport discord\nfrom discord.ext import commands\n\nfrom redbot.core import checks, RedContext\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\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    @staticmethod\n    async def check_100_plus(ctx: RedContext, number: int) -> bool:\n        \"\"\"\n        Called when trying to delete more than 100 messages at once\n\n        Prompts the user to choose whether they want to continue or not\n        \"\"\"\n        def author_check(message):\n            return message.author == ctx.author\n\n        await ctx.send(_('Are you sure you want to delete {} messages? (y/n)').format(number))\n        response = await ctx.bot.wait_for('message', check=author_check)\n\n        if response.content.lower().startswith('y'):\n            return True\n        else:\n            await ctx.send(_('Cancelled.'))\n            return False\n\n    @staticmethod\n    async def get_messages_for_deletion(\n            ctx: RedContext, channel: discord.TextChannel, number,\n            check=lambda x: True, limit=100, before=None, after=None\n    ) -> list:\n        \"\"\"\n        Gets a list of messages meeting the requirements to be deleted.\n\n        Generally, the requirements are:\n        - We don't have the number of messages to be deleted already\n        - The message passes a provided check (if no check is provided,\n          this is automatically true)\n        - The message is less than 14 days old\n        \"\"\"\n        to_delete = []\n        too_old = False\n\n        while not too_old and len(to_delete) - 1 < number:\n            message = None\n            async for message in channel.history(limit=limit,\n                                                 before=before,\n                                                 after=after):\n                if (not number or len(to_delete) - 1 < number) and check(message) \\\n                        and (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 number and len(to_delete) >= number:\n                    break\n            if message is None:\n                break\n            else:\n                before = message\n        return to_delete\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: RedContext, 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        if number > 100:\n            cont = await self.check_100_plus(ctx, number)\n            if not cont:\n                return\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 = await self.get_messages_for_deletion(\n            ctx, channel, number, check=check, limit=1000, before=ctx.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: RedContext, 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        if number > 100:\n            cont = await self.check_100_plus(ctx, number)\n            if not cont:\n                return\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 = await self.get_messages_for_deletion(\n            ctx, channel, number, check=check, limit=1000, before=ctx.message\n        )\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: RedContext, 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 = await self.get_messages_for_deletion(\n            ctx, channel, 0, limit=None, after=after\n        )\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: RedContext, 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        if number > 100:\n            cont = await self.check_100_plus(ctx, number)\n            if not cont:\n                return\n\n        to_delete = await self.get_messages_for_deletion(\n            ctx, channel, number, limit=1000, before=ctx.message\n        )\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: RedContext, 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        if number > 100:\n            cont = await self.check_100_plus(ctx, number)\n            if not cont:\n                return\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 = await self.get_messages_for_deletion(\n            ctx, channel, number, check=check, limit=1000, before=ctx.message\n        )\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: RedContext, 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        if number > 100:\n            cont = await self.check_100_plus(ctx, number)\n            if not cont:\n                return\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 = await self.get_messages_for_deletion(\n            ctx, channel, number, check=check, limit=1000, before=ctx.message\n        )\n\n        # Selfbot convenience, delete trigger message\n        if author == self.bot.user:\n            to_delete.append(ctx.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"}]}