We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Delete messages when `!pypi` module is invalid
When users use the `!pypi` command with an invalid module, you currently get a response saying `"Package could not be found."`. 

What I'm proposing it that after a delay (I believe we have `bot.constants.RedirectOutput.delete_delay` for this) the bot should delete both its response and the invocation message, like [`!d` currently does](https://github.com/python-discord/bot/blob/master/bot/exts/info/doc.py#L363-L365).

This would help reduce channel flooding when someone is trying to remember the name of a module, and since it's not a trivial change I'm happy to try and implement this myself if it's something we decide we want.

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `bot/exts/info/pypi.py`
Content:
```
1 import itertools
2 import logging
3 import random
4 import re
5 
6 from discord import Embed
7 from discord.ext.commands import Cog, Context, command
8 from discord.utils import escape_markdown
9 
10 from bot.bot import Bot
11 from bot.constants import Colours, NEGATIVE_REPLIES
12 
13 URL = "https://pypi.org/pypi/{package}/json"
14 FIELDS = ("author", "requires_python", "summary", "license")
15 PYPI_ICON = "https://cdn.discordapp.com/emojis/766274397257334814.png"
16 
17 PYPI_COLOURS = itertools.cycle((Colours.yellow, Colours.blue, Colours.white))
18 
19 ILLEGAL_CHARACTERS = re.compile(r"[^a-zA-Z0-9-.]+")
20 
21 log = logging.getLogger(__name__)
22 
23 
24 class PyPi(Cog):
25     """Cog for getting information about PyPi packages."""
26 
27     def __init__(self, bot: Bot):
28         self.bot = bot
29 
30     @command(name="pypi", aliases=("package", "pack"))
31     async def get_package_info(self, ctx: Context, package: str) -> None:
32         """Provide information about a specific package from PyPI."""
33         embed = Embed(
34             title=random.choice(NEGATIVE_REPLIES),
35             colour=Colours.soft_red
36         )
37         embed.set_thumbnail(url=PYPI_ICON)
38 
39         if (character := re.search(ILLEGAL_CHARACTERS, package)) is not None:
40             embed.description = f"Illegal character passed into command: '{escape_markdown(character.group(0))}'"
41             await ctx.send(embed=embed)
42             return
43 
44         async with self.bot.http_session.get(URL.format(package=package)) as response:
45             if response.status == 404:
46                 embed.description = "Package could not be found."
47 
48             elif response.status == 200 and response.content_type == "application/json":
49                 response_json = await response.json()
50                 info = response_json["info"]
51 
52                 embed.title = f"{info['name']} v{info['version']}"
53                 embed.url = info['package_url']
54                 embed.colour = next(PYPI_COLOURS)
55 
56                 for field in FIELDS:
57                     field_data = info[field]
58 
59                     # Field could be completely empty, in some cases can be a string with whitespaces, or None.
60                     if field_data and not field_data.isspace():
61                         if '\n' in field_data and field == "license":
62                             field_data = field_data.split('\n')[0]
63 
64                         embed.add_field(
65                             name=field.replace("_", " ").title(),
66                             value=escape_markdown(field_data),
67                             inline=False,
68                         )
69 
70             else:
71                 embed.description = "There was an error when fetching your PyPi package."
72                 log.trace(f"Error when fetching PyPi package: {response.status}.")
73 
74         await ctx.send(embed=embed)
75 
76 
77 def setup(bot: Bot) -> None:
78     """Load the PyPi cog."""
79     bot.add_cog(PyPi(bot))
80
```
--- 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/bot/exts/info/pypi.py b/bot/exts/info/pypi.py
--- a/bot/exts/info/pypi.py
+++ b/bot/exts/info/pypi.py
@@ -8,7 +8,7 @@
 from discord.utils import escape_markdown
 
 from bot.bot import Bot
-from bot.constants import Colours, NEGATIVE_REPLIES
+from bot.constants import Colours, NEGATIVE_REPLIES, RedirectOutput
 
 URL = "https://pypi.org/pypi/{package}/json"
 FIELDS = ("author", "requires_python", "summary", "license")
@@ -17,6 +17,7 @@
 PYPI_COLOURS = itertools.cycle((Colours.yellow, Colours.blue, Colours.white))
 
 ILLEGAL_CHARACTERS = re.compile(r"[^a-zA-Z0-9-.]+")
+INVALID_INPUT_DELETE_DELAY = RedirectOutput.delete_delay
 
 log = logging.getLogger(__name__)
 
@@ -36,42 +37,49 @@
         )
         embed.set_thumbnail(url=PYPI_ICON)
 
+        error = True
+
         if (character := re.search(ILLEGAL_CHARACTERS, package)) is not None:
             embed.description = f"Illegal character passed into command: '{escape_markdown(character.group(0))}'"
-            await ctx.send(embed=embed)
-            return
 
-        async with self.bot.http_session.get(URL.format(package=package)) as response:
-            if response.status == 404:
-                embed.description = "Package could not be found."
+        else:
+            async with self.bot.http_session.get(URL.format(package=package)) as response:
+                if response.status == 404:
+                    embed.description = "Package could not be found."
 
-            elif response.status == 200 and response.content_type == "application/json":
-                response_json = await response.json()
-                info = response_json["info"]
+                elif response.status == 200 and response.content_type == "application/json":
+                    response_json = await response.json()
+                    info = response_json["info"]
 
-                embed.title = f"{info['name']} v{info['version']}"
-                embed.url = info['package_url']
-                embed.colour = next(PYPI_COLOURS)
+                    embed.title = f"{info['name']} v{info['version']}"
+                    embed.url = info['package_url']
+                    embed.colour = next(PYPI_COLOURS)
 
-                for field in FIELDS:
-                    field_data = info[field]
+                    for field in FIELDS:
+                        field_data = info[field]
 
-                    # Field could be completely empty, in some cases can be a string with whitespaces, or None.
-                    if field_data and not field_data.isspace():
-                        if '\n' in field_data and field == "license":
-                            field_data = field_data.split('\n')[0]
+                        # Field could be completely empty, in some cases can be a string with whitespaces, or None.
+                        if field_data and not field_data.isspace():
+                            if '\n' in field_data and field == "license":
+                                field_data = field_data.split('\n')[0]
 
-                        embed.add_field(
-                            name=field.replace("_", " ").title(),
-                            value=escape_markdown(field_data),
-                            inline=False,
-                        )
+                            embed.add_field(
+                                name=field.replace("_", " ").title(),
+                                value=escape_markdown(field_data),
+                                inline=False,
+                            )
 
-            else:
-                embed.description = "There was an error when fetching your PyPi package."
-                log.trace(f"Error when fetching PyPi package: {response.status}.")
+                    error = False
 
-        await ctx.send(embed=embed)
+                else:
+                    embed.description = "There was an error when fetching your PyPi package."
+                    log.trace(f"Error when fetching PyPi package: {response.status}.")
+
+        if error:
+            await ctx.send(embed=embed, delete_after=INVALID_INPUT_DELETE_DELAY)
+            await ctx.message.delete(delay=INVALID_INPUT_DELETE_DELAY)
+        else:
+            await ctx.send(embed=embed)
 
 
 def setup(bot: Bot) -> None:

{"golden_diff": "diff --git a/bot/exts/info/pypi.py b/bot/exts/info/pypi.py\n--- a/bot/exts/info/pypi.py\n+++ b/bot/exts/info/pypi.py\n@@ -8,7 +8,7 @@\n from discord.utils import escape_markdown\n \n from bot.bot import Bot\n-from bot.constants import Colours, NEGATIVE_REPLIES\n+from bot.constants import Colours, NEGATIVE_REPLIES, RedirectOutput\n \n URL = \"https://pypi.org/pypi/{package}/json\"\n FIELDS = (\"author\", \"requires_python\", \"summary\", \"license\")\n@@ -17,6 +17,7 @@\n PYPI_COLOURS = itertools.cycle((Colours.yellow, Colours.blue, Colours.white))\n \n ILLEGAL_CHARACTERS = re.compile(r\"[^a-zA-Z0-9-.]+\")\n+INVALID_INPUT_DELETE_DELAY = RedirectOutput.delete_delay\n \n log = logging.getLogger(__name__)\n \n@@ -36,42 +37,49 @@\n         )\n         embed.set_thumbnail(url=PYPI_ICON)\n \n+        error = True\n+\n         if (character := re.search(ILLEGAL_CHARACTERS, package)) is not None:\n             embed.description = f\"Illegal character passed into command: '{escape_markdown(character.group(0))}'\"\n-            await ctx.send(embed=embed)\n-            return\n \n-        async with self.bot.http_session.get(URL.format(package=package)) as response:\n-            if response.status == 404:\n-                embed.description = \"Package could not be found.\"\n+        else:\n+            async with self.bot.http_session.get(URL.format(package=package)) as response:\n+                if response.status == 404:\n+                    embed.description = \"Package could not be found.\"\n \n-            elif response.status == 200 and response.content_type == \"application/json\":\n-                response_json = await response.json()\n-                info = response_json[\"info\"]\n+                elif response.status == 200 and response.content_type == \"application/json\":\n+                    response_json = await response.json()\n+                    info = response_json[\"info\"]\n \n-                embed.title = f\"{info['name']} v{info['version']}\"\n-                embed.url = info['package_url']\n-                embed.colour = next(PYPI_COLOURS)\n+                    embed.title = f\"{info['name']} v{info['version']}\"\n+                    embed.url = info['package_url']\n+                    embed.colour = next(PYPI_COLOURS)\n \n-                for field in FIELDS:\n-                    field_data = info[field]\n+                    for field in FIELDS:\n+                        field_data = info[field]\n \n-                    # Field could be completely empty, in some cases can be a string with whitespaces, or None.\n-                    if field_data and not field_data.isspace():\n-                        if '\\n' in field_data and field == \"license\":\n-                            field_data = field_data.split('\\n')[0]\n+                        # Field could be completely empty, in some cases can be a string with whitespaces, or None.\n+                        if field_data and not field_data.isspace():\n+                            if '\\n' in field_data and field == \"license\":\n+                                field_data = field_data.split('\\n')[0]\n \n-                        embed.add_field(\n-                            name=field.replace(\"_\", \" \").title(),\n-                            value=escape_markdown(field_data),\n-                            inline=False,\n-                        )\n+                            embed.add_field(\n+                                name=field.replace(\"_\", \" \").title(),\n+                                value=escape_markdown(field_data),\n+                                inline=False,\n+                            )\n \n-            else:\n-                embed.description = \"There was an error when fetching your PyPi package.\"\n-                log.trace(f\"Error when fetching PyPi package: {response.status}.\")\n+                    error = False\n \n-        await ctx.send(embed=embed)\n+                else:\n+                    embed.description = \"There was an error when fetching your PyPi package.\"\n+                    log.trace(f\"Error when fetching PyPi package: {response.status}.\")\n+\n+        if error:\n+            await ctx.send(embed=embed, delete_after=INVALID_INPUT_DELETE_DELAY)\n+            await ctx.message.delete(delay=INVALID_INPUT_DELETE_DELAY)\n+        else:\n+            await ctx.send(embed=embed)\n \n \n def setup(bot: Bot) -> None:\n", "issue": "Delete messages when `!pypi` module is invalid\nWhen users use the `!pypi` command with an invalid module, you currently get a response saying `\"Package could not be found.\"`. \r\n\r\nWhat I'm proposing it that after a delay (I believe we have `bot.constants.RedirectOutput.delete_delay` for this) the bot should delete both its response and the invocation message, like [`!d` currently does](https://github.com/python-discord/bot/blob/master/bot/exts/info/doc.py#L363-L365).\r\n\r\nThis would help reduce channel flooding when someone is trying to remember the name of a module, and since it's not a trivial change I'm happy to try and implement this myself if it's something we decide we want.\n", "before_files": [{"content": "import itertools\nimport logging\nimport random\nimport re\n\nfrom discord import Embed\nfrom discord.ext.commands import Cog, Context, command\nfrom discord.utils import escape_markdown\n\nfrom bot.bot import Bot\nfrom bot.constants import Colours, NEGATIVE_REPLIES\n\nURL = \"https://pypi.org/pypi/{package}/json\"\nFIELDS = (\"author\", \"requires_python\", \"summary\", \"license\")\nPYPI_ICON = \"https://cdn.discordapp.com/emojis/766274397257334814.png\"\n\nPYPI_COLOURS = itertools.cycle((Colours.yellow, Colours.blue, Colours.white))\n\nILLEGAL_CHARACTERS = re.compile(r\"[^a-zA-Z0-9-.]+\")\n\nlog = logging.getLogger(__name__)\n\n\nclass PyPi(Cog):\n    \"\"\"Cog for getting information about PyPi packages.\"\"\"\n\n    def __init__(self, bot: Bot):\n        self.bot = bot\n\n    @command(name=\"pypi\", aliases=(\"package\", \"pack\"))\n    async def get_package_info(self, ctx: Context, package: str) -> None:\n        \"\"\"Provide information about a specific package from PyPI.\"\"\"\n        embed = Embed(\n            title=random.choice(NEGATIVE_REPLIES),\n            colour=Colours.soft_red\n        )\n        embed.set_thumbnail(url=PYPI_ICON)\n\n        if (character := re.search(ILLEGAL_CHARACTERS, package)) is not None:\n            embed.description = f\"Illegal character passed into command: '{escape_markdown(character.group(0))}'\"\n            await ctx.send(embed=embed)\n            return\n\n        async with self.bot.http_session.get(URL.format(package=package)) as response:\n            if response.status == 404:\n                embed.description = \"Package could not be found.\"\n\n            elif response.status == 200 and response.content_type == \"application/json\":\n                response_json = await response.json()\n                info = response_json[\"info\"]\n\n                embed.title = f\"{info['name']} v{info['version']}\"\n                embed.url = info['package_url']\n                embed.colour = next(PYPI_COLOURS)\n\n                for field in FIELDS:\n                    field_data = info[field]\n\n                    # Field could be completely empty, in some cases can be a string with whitespaces, or None.\n                    if field_data and not field_data.isspace():\n                        if '\\n' in field_data and field == \"license\":\n                            field_data = field_data.split('\\n')[0]\n\n                        embed.add_field(\n                            name=field.replace(\"_\", \" \").title(),\n                            value=escape_markdown(field_data),\n                            inline=False,\n                        )\n\n            else:\n                embed.description = \"There was an error when fetching your PyPi package.\"\n                log.trace(f\"Error when fetching PyPi package: {response.status}.\")\n\n        await ctx.send(embed=embed)\n\n\ndef setup(bot: Bot) -> None:\n    \"\"\"Load the PyPi cog.\"\"\"\n    bot.add_cog(PyPi(bot))\n", "path": "bot/exts/info/pypi.py"}], "after_files": [{"content": "import itertools\nimport logging\nimport random\nimport re\n\nfrom discord import Embed\nfrom discord.ext.commands import Cog, Context, command\nfrom discord.utils import escape_markdown\n\nfrom bot.bot import Bot\nfrom bot.constants import Colours, NEGATIVE_REPLIES, RedirectOutput\n\nURL = \"https://pypi.org/pypi/{package}/json\"\nFIELDS = (\"author\", \"requires_python\", \"summary\", \"license\")\nPYPI_ICON = \"https://cdn.discordapp.com/emojis/766274397257334814.png\"\n\nPYPI_COLOURS = itertools.cycle((Colours.yellow, Colours.blue, Colours.white))\n\nILLEGAL_CHARACTERS = re.compile(r\"[^a-zA-Z0-9-.]+\")\nINVALID_INPUT_DELETE_DELAY = RedirectOutput.delete_delay\n\nlog = logging.getLogger(__name__)\n\n\nclass PyPi(Cog):\n    \"\"\"Cog for getting information about PyPi packages.\"\"\"\n\n    def __init__(self, bot: Bot):\n        self.bot = bot\n\n    @command(name=\"pypi\", aliases=(\"package\", \"pack\"))\n    async def get_package_info(self, ctx: Context, package: str) -> None:\n        \"\"\"Provide information about a specific package from PyPI.\"\"\"\n        embed = Embed(\n            title=random.choice(NEGATIVE_REPLIES),\n            colour=Colours.soft_red\n        )\n        embed.set_thumbnail(url=PYPI_ICON)\n\n        error = True\n\n        if (character := re.search(ILLEGAL_CHARACTERS, package)) is not None:\n            embed.description = f\"Illegal character passed into command: '{escape_markdown(character.group(0))}'\"\n\n        else:\n            async with self.bot.http_session.get(URL.format(package=package)) as response:\n                if response.status == 404:\n                    embed.description = \"Package could not be found.\"\n\n                elif response.status == 200 and response.content_type == \"application/json\":\n                    response_json = await response.json()\n                    info = response_json[\"info\"]\n\n                    embed.title = f\"{info['name']} v{info['version']}\"\n                    embed.url = info['package_url']\n                    embed.colour = next(PYPI_COLOURS)\n\n                    for field in FIELDS:\n                        field_data = info[field]\n\n                        # Field could be completely empty, in some cases can be a string with whitespaces, or None.\n                        if field_data and not field_data.isspace():\n                            if '\\n' in field_data and field == \"license\":\n                                field_data = field_data.split('\\n')[0]\n\n                            embed.add_field(\n                                name=field.replace(\"_\", \" \").title(),\n                                value=escape_markdown(field_data),\n                                inline=False,\n                            )\n\n                    error = False\n\n                else:\n                    embed.description = \"There was an error when fetching your PyPi package.\"\n                    log.trace(f\"Error when fetching PyPi package: {response.status}.\")\n\n        if error:\n            await ctx.send(embed=embed, delete_after=INVALID_INPUT_DELETE_DELAY)\n            await ctx.message.delete(delay=INVALID_INPUT_DELETE_DELAY)\n        else:\n            await ctx.send(embed=embed)\n\n\ndef setup(bot: Bot) -> None:\n    \"\"\"Load the PyPi cog.\"\"\"\n    bot.add_cog(PyPi(bot))\n", "path": "bot/exts/info/pypi.py"}]}