We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
feature suggestion: add url in title instead of description in !pep command
currently the link when you use the !pep command is located in the description, but with the discord.Embed.url parameter, the title is now able to become a link (as shown in second image)

![image](https://user-images.githubusercontent.com/50557523/94292440-34e6e080-ff1a-11ea-8986-4e025379fd73.png)
![image](https://user-images.githubusercontent.com/50557523/94292605-695a9c80-ff1a-11ea-99ef-fc02f83e9d61.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: `bot/exts/utils/utils.py`
Content:
```
1 import difflib
2 import logging
3 import re
4 import unicodedata
5 from email.parser import HeaderParser
6 from io import StringIO
7 from typing import Tuple, Union
8 
9 from discord import Colour, Embed, utils
10 from discord.ext.commands import BadArgument, Cog, Context, clean_content, command, has_any_role
11 
12 from bot.bot import Bot
13 from bot.constants import Channels, MODERATION_ROLES, STAFF_ROLES
14 from bot.decorators import in_whitelist
15 from bot.pagination import LinePaginator
16 from bot.utils import messages
17 
18 log = logging.getLogger(__name__)
19 
20 ZEN_OF_PYTHON = """\
21 Beautiful is better than ugly.
22 Explicit is better than implicit.
23 Simple is better than complex.
24 Complex is better than complicated.
25 Flat is better than nested.
26 Sparse is better than dense.
27 Readability counts.
28 Special cases aren't special enough to break the rules.
29 Although practicality beats purity.
30 Errors should never pass silently.
31 Unless explicitly silenced.
32 In the face of ambiguity, refuse the temptation to guess.
33 There should be one-- and preferably only one --obvious way to do it.
34 Although that way may not be obvious at first unless you're Dutch.
35 Now is better than never.
36 Although never is often better than *right* now.
37 If the implementation is hard to explain, it's a bad idea.
38 If the implementation is easy to explain, it may be a good idea.
39 Namespaces are one honking great idea -- let's do more of those!
40 """
41 
42 ICON_URL = "https://www.python.org/static/opengraph-icon-200x200.png"
43 
44 
45 class Utils(Cog):
46     """A selection of utilities which don't have a clear category."""
47 
48     def __init__(self, bot: Bot):
49         self.bot = bot
50 
51         self.base_pep_url = "http://www.python.org/dev/peps/pep-"
52         self.base_github_pep_url = "https://raw.githubusercontent.com/python/peps/master/pep-"
53 
54     @command(name='pep', aliases=('get_pep', 'p'))
55     async def pep_command(self, ctx: Context, pep_number: str) -> None:
56         """Fetches information about a PEP and sends it to the channel."""
57         if pep_number.isdigit():
58             pep_number = int(pep_number)
59         else:
60             await ctx.send_help(ctx.command)
61             return
62 
63         # Handle PEP 0 directly because it's not in .rst or .txt so it can't be accessed like other PEPs.
64         if pep_number == 0:
65             return await self.send_pep_zero(ctx)
66 
67         possible_extensions = ['.txt', '.rst']
68         found_pep = False
69         for extension in possible_extensions:
70             # Attempt to fetch the PEP
71             pep_url = f"{self.base_github_pep_url}{pep_number:04}{extension}"
72             log.trace(f"Requesting PEP {pep_number} with {pep_url}")
73             response = await self.bot.http_session.get(pep_url)
74 
75             if response.status == 200:
76                 log.trace("PEP found")
77                 found_pep = True
78 
79                 pep_content = await response.text()
80 
81                 # Taken from https://github.com/python/peps/blob/master/pep0/pep.py#L179
82                 pep_header = HeaderParser().parse(StringIO(pep_content))
83 
84                 # Assemble the embed
85                 pep_embed = Embed(
86                     title=f"**PEP {pep_number} - {pep_header['Title']}**",
87                     description=f"[Link]({self.base_pep_url}{pep_number:04})",
88                 )
89 
90                 pep_embed.set_thumbnail(url=ICON_URL)
91 
92                 # Add the interesting information
93                 fields_to_check = ("Status", "Python-Version", "Created", "Type")
94                 for field in fields_to_check:
95                     # Check for a PEP metadata field that is present but has an empty value
96                     # embed field values can't contain an empty string
97                     if pep_header.get(field, ""):
98                         pep_embed.add_field(name=field, value=pep_header[field])
99 
100             elif response.status != 404:
101                 # any response except 200 and 404 is expected
102                 found_pep = True  # actually not, but it's easier to display this way
103                 log.trace(f"The user requested PEP {pep_number}, but the response had an unexpected status code: "
104                           f"{response.status}.\n{response.text}")
105 
106                 error_message = "Unexpected HTTP error during PEP search. Please let us know."
107                 pep_embed = Embed(title="Unexpected error", description=error_message)
108                 pep_embed.colour = Colour.red()
109                 break
110 
111         if not found_pep:
112             log.trace("PEP was not found")
113             not_found = f"PEP {pep_number} does not exist."
114             pep_embed = Embed(title="PEP not found", description=not_found)
115             pep_embed.colour = Colour.red()
116 
117         await ctx.message.channel.send(embed=pep_embed)
118 
119     @command()
120     @in_whitelist(channels=(Channels.bot_commands,), roles=STAFF_ROLES)
121     async def charinfo(self, ctx: Context, *, characters: str) -> None:
122         """Shows you information on up to 50 unicode characters."""
123         match = re.match(r"<(a?):(\w+):(\d+)>", characters)
124         if match:
125             return await messages.send_denial(
126                 ctx,
127                 "**Non-Character Detected**\n"
128                 "Only unicode characters can be processed, but a custom Discord emoji "
129                 "was found. Please remove it and try again."
130             )
131 
132         if len(characters) > 50:
133             return await messages.send_denial(ctx, f"Too many characters ({len(characters)}/50)")
134 
135         def get_info(char: str) -> Tuple[str, str]:
136             digit = f"{ord(char):x}"
137             if len(digit) <= 4:
138                 u_code = f"\\u{digit:>04}"
139             else:
140                 u_code = f"\\U{digit:>08}"
141             url = f"https://www.compart.com/en/unicode/U+{digit:>04}"
142             name = f"[{unicodedata.name(char, '')}]({url})"
143             info = f"`{u_code.ljust(10)}`: {name} - {utils.escape_markdown(char)}"
144             return info, u_code
145 
146         char_list, raw_list = zip(*(get_info(c) for c in characters))
147         embed = Embed().set_author(name="Character Info")
148 
149         if len(characters) > 1:
150             # Maximum length possible is 502 out of 1024, so there's no need to truncate.
151             embed.add_field(name='Full Raw Text', value=f"`{''.join(raw_list)}`", inline=False)
152 
153         await LinePaginator.paginate(char_list, ctx, embed, max_lines=10, max_size=2000, empty=False)
154 
155     @command()
156     async def zen(self, ctx: Context, *, search_value: Union[int, str, None] = None) -> None:
157         """
158         Show the Zen of Python.
159 
160         Without any arguments, the full Zen will be produced.
161         If an integer is provided, the line with that index will be produced.
162         If a string is provided, the line which matches best will be produced.
163         """
164         embed = Embed(
165             colour=Colour.blurple(),
166             title="The Zen of Python",
167             description=ZEN_OF_PYTHON
168         )
169 
170         if search_value is None:
171             embed.title += ", by Tim Peters"
172             await ctx.send(embed=embed)
173             return
174 
175         zen_lines = ZEN_OF_PYTHON.splitlines()
176 
177         # handle if it's an index int
178         if isinstance(search_value, int):
179             upper_bound = len(zen_lines) - 1
180             lower_bound = -1 * upper_bound
181             if not (lower_bound <= search_value <= upper_bound):
182                 raise BadArgument(f"Please provide an index between {lower_bound} and {upper_bound}.")
183 
184             embed.title += f" (line {search_value % len(zen_lines)}):"
185             embed.description = zen_lines[search_value]
186             await ctx.send(embed=embed)
187             return
188 
189         # Try to handle first exact word due difflib.SequenceMatched may use some other similar word instead
190         # exact word.
191         for i, line in enumerate(zen_lines):
192             for word in line.split():
193                 if word.lower() == search_value.lower():
194                     embed.title += f" (line {i}):"
195                     embed.description = line
196                     await ctx.send(embed=embed)
197                     return
198 
199         # handle if it's a search string and not exact word
200         matcher = difflib.SequenceMatcher(None, search_value.lower())
201 
202         best_match = ""
203         match_index = 0
204         best_ratio = 0
205 
206         for index, line in enumerate(zen_lines):
207             matcher.set_seq2(line.lower())
208 
209             # the match ratio needs to be adjusted because, naturally,
210             # longer lines will have worse ratios than shorter lines when
211             # fuzzy searching for keywords. this seems to work okay.
212             adjusted_ratio = (len(line) - 5) ** 0.5 * matcher.ratio()
213 
214             if adjusted_ratio > best_ratio:
215                 best_ratio = adjusted_ratio
216                 best_match = line
217                 match_index = index
218 
219         if not best_match:
220             raise BadArgument("I didn't get a match! Please try again with a different search term.")
221 
222         embed.title += f" (line {match_index}):"
223         embed.description = best_match
224         await ctx.send(embed=embed)
225 
226     @command(aliases=("poll",))
227     @has_any_role(*MODERATION_ROLES)
228     async def vote(self, ctx: Context, title: clean_content(fix_channel_mentions=True), *options: str) -> None:
229         """
230         Build a quick voting poll with matching reactions with the provided options.
231 
232         A maximum of 20 options can be provided, as Discord supports a max of 20
233         reactions on a single message.
234         """
235         if len(title) > 256:
236             raise BadArgument("The title cannot be longer than 256 characters.")
237         if len(options) < 2:
238             raise BadArgument("Please provide at least 2 options.")
239         if len(options) > 20:
240             raise BadArgument("I can only handle 20 options!")
241 
242         codepoint_start = 127462  # represents "regional_indicator_a" unicode value
243         options = {chr(i): f"{chr(i)} - {v}" for i, v in enumerate(options, start=codepoint_start)}
244         embed = Embed(title=title, description="\n".join(options.values()))
245         message = await ctx.send(embed=embed)
246         for reaction in options:
247             await message.add_reaction(reaction)
248 
249     async def send_pep_zero(self, ctx: Context) -> None:
250         """Send information about PEP 0."""
251         pep_embed = Embed(
252             title="**PEP 0 - Index of Python Enhancement Proposals (PEPs)**",
253             description="[Link](https://www.python.org/dev/peps/)"
254         )
255         pep_embed.set_thumbnail(url=ICON_URL)
256         pep_embed.add_field(name="Status", value="Active")
257         pep_embed.add_field(name="Created", value="13-Jul-2000")
258         pep_embed.add_field(name="Type", value="Informational")
259 
260         await ctx.send(embed=pep_embed)
261 
262 
263 def setup(bot: Bot) -> None:
264     """Load the Utils cog."""
265     bot.add_cog(Utils(bot))
266
```
--- 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/utils/utils.py b/bot/exts/utils/utils.py
--- a/bot/exts/utils/utils.py
+++ b/bot/exts/utils/utils.py
@@ -84,7 +84,7 @@
                 # Assemble the embed
                 pep_embed = Embed(
                     title=f"**PEP {pep_number} - {pep_header['Title']}**",
-                    description=f"[Link]({self.base_pep_url}{pep_number:04})",
+                    url=f"{self.base_pep_url}{pep_number:04}"
                 )
 
                 pep_embed.set_thumbnail(url=ICON_URL)
@@ -250,7 +250,7 @@
         """Send information about PEP 0."""
         pep_embed = Embed(
             title="**PEP 0 - Index of Python Enhancement Proposals (PEPs)**",
-            description="[Link](https://www.python.org/dev/peps/)"
+            url="https://www.python.org/dev/peps/"
         )
         pep_embed.set_thumbnail(url=ICON_URL)
         pep_embed.add_field(name="Status", value="Active")

{"golden_diff": "diff --git a/bot/exts/utils/utils.py b/bot/exts/utils/utils.py\n--- a/bot/exts/utils/utils.py\n+++ b/bot/exts/utils/utils.py\n@@ -84,7 +84,7 @@\n                 # Assemble the embed\n                 pep_embed = Embed(\n                     title=f\"**PEP {pep_number} - {pep_header['Title']}**\",\n-                    description=f\"[Link]({self.base_pep_url}{pep_number:04})\",\n+                    url=f\"{self.base_pep_url}{pep_number:04}\"\n                 )\n \n                 pep_embed.set_thumbnail(url=ICON_URL)\n@@ -250,7 +250,7 @@\n         \"\"\"Send information about PEP 0.\"\"\"\n         pep_embed = Embed(\n             title=\"**PEP 0 - Index of Python Enhancement Proposals (PEPs)**\",\n-            description=\"[Link](https://www.python.org/dev/peps/)\"\n+            url=\"https://www.python.org/dev/peps/\"\n         )\n         pep_embed.set_thumbnail(url=ICON_URL)\n         pep_embed.add_field(name=\"Status\", value=\"Active\")\n", "issue": "feature suggestion: add url in title instead of description in !pep command\ncurrently the link when you use the !pep command is located in the description, but with the discord.Embed.url parameter, the title is now able to become a link (as shown in second image)\r\n\r\n![image](https://user-images.githubusercontent.com/50557523/94292440-34e6e080-ff1a-11ea-8986-4e025379fd73.png)\r\n![image](https://user-images.githubusercontent.com/50557523/94292605-695a9c80-ff1a-11ea-99ef-fc02f83e9d61.png)\r\n\n", "before_files": [{"content": "import difflib\nimport logging\nimport re\nimport unicodedata\nfrom email.parser import HeaderParser\nfrom io import StringIO\nfrom typing import Tuple, Union\n\nfrom discord import Colour, Embed, utils\nfrom discord.ext.commands import BadArgument, Cog, Context, clean_content, command, has_any_role\n\nfrom bot.bot import Bot\nfrom bot.constants import Channels, MODERATION_ROLES, STAFF_ROLES\nfrom bot.decorators import in_whitelist\nfrom bot.pagination import LinePaginator\nfrom bot.utils import messages\n\nlog = logging.getLogger(__name__)\n\nZEN_OF_PYTHON = \"\"\"\\\nBeautiful is better than ugly.\nExplicit is better than implicit.\nSimple is better than complex.\nComplex is better than complicated.\nFlat is better than nested.\nSparse is better than dense.\nReadability counts.\nSpecial cases aren't special enough to break the rules.\nAlthough practicality beats purity.\nErrors should never pass silently.\nUnless explicitly silenced.\nIn the face of ambiguity, refuse the temptation to guess.\nThere should be one-- and preferably only one --obvious way to do it.\nAlthough that way may not be obvious at first unless you're Dutch.\nNow is better than never.\nAlthough never is often better than *right* now.\nIf the implementation is hard to explain, it's a bad idea.\nIf the implementation is easy to explain, it may be a good idea.\nNamespaces are one honking great idea -- let's do more of those!\n\"\"\"\n\nICON_URL = \"https://www.python.org/static/opengraph-icon-200x200.png\"\n\n\nclass Utils(Cog):\n    \"\"\"A selection of utilities which don't have a clear category.\"\"\"\n\n    def __init__(self, bot: Bot):\n        self.bot = bot\n\n        self.base_pep_url = \"http://www.python.org/dev/peps/pep-\"\n        self.base_github_pep_url = \"https://raw.githubusercontent.com/python/peps/master/pep-\"\n\n    @command(name='pep', aliases=('get_pep', 'p'))\n    async def pep_command(self, ctx: Context, pep_number: str) -> None:\n        \"\"\"Fetches information about a PEP and sends it to the channel.\"\"\"\n        if pep_number.isdigit():\n            pep_number = int(pep_number)\n        else:\n            await ctx.send_help(ctx.command)\n            return\n\n        # Handle PEP 0 directly because it's not in .rst or .txt so it can't be accessed like other PEPs.\n        if pep_number == 0:\n            return await self.send_pep_zero(ctx)\n\n        possible_extensions = ['.txt', '.rst']\n        found_pep = False\n        for extension in possible_extensions:\n            # Attempt to fetch the PEP\n            pep_url = f\"{self.base_github_pep_url}{pep_number:04}{extension}\"\n            log.trace(f\"Requesting PEP {pep_number} with {pep_url}\")\n            response = await self.bot.http_session.get(pep_url)\n\n            if response.status == 200:\n                log.trace(\"PEP found\")\n                found_pep = True\n\n                pep_content = await response.text()\n\n                # Taken from https://github.com/python/peps/blob/master/pep0/pep.py#L179\n                pep_header = HeaderParser().parse(StringIO(pep_content))\n\n                # Assemble the embed\n                pep_embed = Embed(\n                    title=f\"**PEP {pep_number} - {pep_header['Title']}**\",\n                    description=f\"[Link]({self.base_pep_url}{pep_number:04})\",\n                )\n\n                pep_embed.set_thumbnail(url=ICON_URL)\n\n                # Add the interesting information\n                fields_to_check = (\"Status\", \"Python-Version\", \"Created\", \"Type\")\n                for field in fields_to_check:\n                    # Check for a PEP metadata field that is present but has an empty value\n                    # embed field values can't contain an empty string\n                    if pep_header.get(field, \"\"):\n                        pep_embed.add_field(name=field, value=pep_header[field])\n\n            elif response.status != 404:\n                # any response except 200 and 404 is expected\n                found_pep = True  # actually not, but it's easier to display this way\n                log.trace(f\"The user requested PEP {pep_number}, but the response had an unexpected status code: \"\n                          f\"{response.status}.\\n{response.text}\")\n\n                error_message = \"Unexpected HTTP error during PEP search. Please let us know.\"\n                pep_embed = Embed(title=\"Unexpected error\", description=error_message)\n                pep_embed.colour = Colour.red()\n                break\n\n        if not found_pep:\n            log.trace(\"PEP was not found\")\n            not_found = f\"PEP {pep_number} does not exist.\"\n            pep_embed = Embed(title=\"PEP not found\", description=not_found)\n            pep_embed.colour = Colour.red()\n\n        await ctx.message.channel.send(embed=pep_embed)\n\n    @command()\n    @in_whitelist(channels=(Channels.bot_commands,), roles=STAFF_ROLES)\n    async def charinfo(self, ctx: Context, *, characters: str) -> None:\n        \"\"\"Shows you information on up to 50 unicode characters.\"\"\"\n        match = re.match(r\"<(a?):(\\w+):(\\d+)>\", characters)\n        if match:\n            return await messages.send_denial(\n                ctx,\n                \"**Non-Character Detected**\\n\"\n                \"Only unicode characters can be processed, but a custom Discord emoji \"\n                \"was found. Please remove it and try again.\"\n            )\n\n        if len(characters) > 50:\n            return await messages.send_denial(ctx, f\"Too many characters ({len(characters)}/50)\")\n\n        def get_info(char: str) -> Tuple[str, str]:\n            digit = f\"{ord(char):x}\"\n            if len(digit) <= 4:\n                u_code = f\"\\\\u{digit:>04}\"\n            else:\n                u_code = f\"\\\\U{digit:>08}\"\n            url = f\"https://www.compart.com/en/unicode/U+{digit:>04}\"\n            name = f\"[{unicodedata.name(char, '')}]({url})\"\n            info = f\"`{u_code.ljust(10)}`: {name} - {utils.escape_markdown(char)}\"\n            return info, u_code\n\n        char_list, raw_list = zip(*(get_info(c) for c in characters))\n        embed = Embed().set_author(name=\"Character Info\")\n\n        if len(characters) > 1:\n            # Maximum length possible is 502 out of 1024, so there's no need to truncate.\n            embed.add_field(name='Full Raw Text', value=f\"`{''.join(raw_list)}`\", inline=False)\n\n        await LinePaginator.paginate(char_list, ctx, embed, max_lines=10, max_size=2000, empty=False)\n\n    @command()\n    async def zen(self, ctx: Context, *, search_value: Union[int, str, None] = None) -> None:\n        \"\"\"\n        Show the Zen of Python.\n\n        Without any arguments, the full Zen will be produced.\n        If an integer is provided, the line with that index will be produced.\n        If a string is provided, the line which matches best will be produced.\n        \"\"\"\n        embed = Embed(\n            colour=Colour.blurple(),\n            title=\"The Zen of Python\",\n            description=ZEN_OF_PYTHON\n        )\n\n        if search_value is None:\n            embed.title += \", by Tim Peters\"\n            await ctx.send(embed=embed)\n            return\n\n        zen_lines = ZEN_OF_PYTHON.splitlines()\n\n        # handle if it's an index int\n        if isinstance(search_value, int):\n            upper_bound = len(zen_lines) - 1\n            lower_bound = -1 * upper_bound\n            if not (lower_bound <= search_value <= upper_bound):\n                raise BadArgument(f\"Please provide an index between {lower_bound} and {upper_bound}.\")\n\n            embed.title += f\" (line {search_value % len(zen_lines)}):\"\n            embed.description = zen_lines[search_value]\n            await ctx.send(embed=embed)\n            return\n\n        # Try to handle first exact word due difflib.SequenceMatched may use some other similar word instead\n        # exact word.\n        for i, line in enumerate(zen_lines):\n            for word in line.split():\n                if word.lower() == search_value.lower():\n                    embed.title += f\" (line {i}):\"\n                    embed.description = line\n                    await ctx.send(embed=embed)\n                    return\n\n        # handle if it's a search string and not exact word\n        matcher = difflib.SequenceMatcher(None, search_value.lower())\n\n        best_match = \"\"\n        match_index = 0\n        best_ratio = 0\n\n        for index, line in enumerate(zen_lines):\n            matcher.set_seq2(line.lower())\n\n            # the match ratio needs to be adjusted because, naturally,\n            # longer lines will have worse ratios than shorter lines when\n            # fuzzy searching for keywords. this seems to work okay.\n            adjusted_ratio = (len(line) - 5) ** 0.5 * matcher.ratio()\n\n            if adjusted_ratio > best_ratio:\n                best_ratio = adjusted_ratio\n                best_match = line\n                match_index = index\n\n        if not best_match:\n            raise BadArgument(\"I didn't get a match! Please try again with a different search term.\")\n\n        embed.title += f\" (line {match_index}):\"\n        embed.description = best_match\n        await ctx.send(embed=embed)\n\n    @command(aliases=(\"poll\",))\n    @has_any_role(*MODERATION_ROLES)\n    async def vote(self, ctx: Context, title: clean_content(fix_channel_mentions=True), *options: str) -> None:\n        \"\"\"\n        Build a quick voting poll with matching reactions with the provided options.\n\n        A maximum of 20 options can be provided, as Discord supports a max of 20\n        reactions on a single message.\n        \"\"\"\n        if len(title) > 256:\n            raise BadArgument(\"The title cannot be longer than 256 characters.\")\n        if len(options) < 2:\n            raise BadArgument(\"Please provide at least 2 options.\")\n        if len(options) > 20:\n            raise BadArgument(\"I can only handle 20 options!\")\n\n        codepoint_start = 127462  # represents \"regional_indicator_a\" unicode value\n        options = {chr(i): f\"{chr(i)} - {v}\" for i, v in enumerate(options, start=codepoint_start)}\n        embed = Embed(title=title, description=\"\\n\".join(options.values()))\n        message = await ctx.send(embed=embed)\n        for reaction in options:\n            await message.add_reaction(reaction)\n\n    async def send_pep_zero(self, ctx: Context) -> None:\n        \"\"\"Send information about PEP 0.\"\"\"\n        pep_embed = Embed(\n            title=\"**PEP 0 - Index of Python Enhancement Proposals (PEPs)**\",\n            description=\"[Link](https://www.python.org/dev/peps/)\"\n        )\n        pep_embed.set_thumbnail(url=ICON_URL)\n        pep_embed.add_field(name=\"Status\", value=\"Active\")\n        pep_embed.add_field(name=\"Created\", value=\"13-Jul-2000\")\n        pep_embed.add_field(name=\"Type\", value=\"Informational\")\n\n        await ctx.send(embed=pep_embed)\n\n\ndef setup(bot: Bot) -> None:\n    \"\"\"Load the Utils cog.\"\"\"\n    bot.add_cog(Utils(bot))\n", "path": "bot/exts/utils/utils.py"}], "after_files": [{"content": "import difflib\nimport logging\nimport re\nimport unicodedata\nfrom email.parser import HeaderParser\nfrom io import StringIO\nfrom typing import Tuple, Union\n\nfrom discord import Colour, Embed, utils\nfrom discord.ext.commands import BadArgument, Cog, Context, clean_content, command, has_any_role\n\nfrom bot.bot import Bot\nfrom bot.constants import Channels, MODERATION_ROLES, STAFF_ROLES\nfrom bot.decorators import in_whitelist\nfrom bot.pagination import LinePaginator\nfrom bot.utils import messages\n\nlog = logging.getLogger(__name__)\n\nZEN_OF_PYTHON = \"\"\"\\\nBeautiful is better than ugly.\nExplicit is better than implicit.\nSimple is better than complex.\nComplex is better than complicated.\nFlat is better than nested.\nSparse is better than dense.\nReadability counts.\nSpecial cases aren't special enough to break the rules.\nAlthough practicality beats purity.\nErrors should never pass silently.\nUnless explicitly silenced.\nIn the face of ambiguity, refuse the temptation to guess.\nThere should be one-- and preferably only one --obvious way to do it.\nAlthough that way may not be obvious at first unless you're Dutch.\nNow is better than never.\nAlthough never is often better than *right* now.\nIf the implementation is hard to explain, it's a bad idea.\nIf the implementation is easy to explain, it may be a good idea.\nNamespaces are one honking great idea -- let's do more of those!\n\"\"\"\n\nICON_URL = \"https://www.python.org/static/opengraph-icon-200x200.png\"\n\n\nclass Utils(Cog):\n    \"\"\"A selection of utilities which don't have a clear category.\"\"\"\n\n    def __init__(self, bot: Bot):\n        self.bot = bot\n\n        self.base_pep_url = \"http://www.python.org/dev/peps/pep-\"\n        self.base_github_pep_url = \"https://raw.githubusercontent.com/python/peps/master/pep-\"\n\n    @command(name='pep', aliases=('get_pep', 'p'))\n    async def pep_command(self, ctx: Context, pep_number: str) -> None:\n        \"\"\"Fetches information about a PEP and sends it to the channel.\"\"\"\n        if pep_number.isdigit():\n            pep_number = int(pep_number)\n        else:\n            await ctx.send_help(ctx.command)\n            return\n\n        # Handle PEP 0 directly because it's not in .rst or .txt so it can't be accessed like other PEPs.\n        if pep_number == 0:\n            return await self.send_pep_zero(ctx)\n\n        possible_extensions = ['.txt', '.rst']\n        found_pep = False\n        for extension in possible_extensions:\n            # Attempt to fetch the PEP\n            pep_url = f\"{self.base_github_pep_url}{pep_number:04}{extension}\"\n            log.trace(f\"Requesting PEP {pep_number} with {pep_url}\")\n            response = await self.bot.http_session.get(pep_url)\n\n            if response.status == 200:\n                log.trace(\"PEP found\")\n                found_pep = True\n\n                pep_content = await response.text()\n\n                # Taken from https://github.com/python/peps/blob/master/pep0/pep.py#L179\n                pep_header = HeaderParser().parse(StringIO(pep_content))\n\n                # Assemble the embed\n                pep_embed = Embed(\n                    title=f\"**PEP {pep_number} - {pep_header['Title']}**\",\n                    url=f\"{self.base_pep_url}{pep_number:04}\"\n                )\n\n                pep_embed.set_thumbnail(url=ICON_URL)\n\n                # Add the interesting information\n                fields_to_check = (\"Status\", \"Python-Version\", \"Created\", \"Type\")\n                for field in fields_to_check:\n                    # Check for a PEP metadata field that is present but has an empty value\n                    # embed field values can't contain an empty string\n                    if pep_header.get(field, \"\"):\n                        pep_embed.add_field(name=field, value=pep_header[field])\n\n            elif response.status != 404:\n                # any response except 200 and 404 is expected\n                found_pep = True  # actually not, but it's easier to display this way\n                log.trace(f\"The user requested PEP {pep_number}, but the response had an unexpected status code: \"\n                          f\"{response.status}.\\n{response.text}\")\n\n                error_message = \"Unexpected HTTP error during PEP search. Please let us know.\"\n                pep_embed = Embed(title=\"Unexpected error\", description=error_message)\n                pep_embed.colour = Colour.red()\n                break\n\n        if not found_pep:\n            log.trace(\"PEP was not found\")\n            not_found = f\"PEP {pep_number} does not exist.\"\n            pep_embed = Embed(title=\"PEP not found\", description=not_found)\n            pep_embed.colour = Colour.red()\n\n        await ctx.message.channel.send(embed=pep_embed)\n\n    @command()\n    @in_whitelist(channels=(Channels.bot_commands,), roles=STAFF_ROLES)\n    async def charinfo(self, ctx: Context, *, characters: str) -> None:\n        \"\"\"Shows you information on up to 50 unicode characters.\"\"\"\n        match = re.match(r\"<(a?):(\\w+):(\\d+)>\", characters)\n        if match:\n            return await messages.send_denial(\n                ctx,\n                \"**Non-Character Detected**\\n\"\n                \"Only unicode characters can be processed, but a custom Discord emoji \"\n                \"was found. Please remove it and try again.\"\n            )\n\n        if len(characters) > 50:\n            return await messages.send_denial(ctx, f\"Too many characters ({len(characters)}/50)\")\n\n        def get_info(char: str) -> Tuple[str, str]:\n            digit = f\"{ord(char):x}\"\n            if len(digit) <= 4:\n                u_code = f\"\\\\u{digit:>04}\"\n            else:\n                u_code = f\"\\\\U{digit:>08}\"\n            url = f\"https://www.compart.com/en/unicode/U+{digit:>04}\"\n            name = f\"[{unicodedata.name(char, '')}]({url})\"\n            info = f\"`{u_code.ljust(10)}`: {name} - {utils.escape_markdown(char)}\"\n            return info, u_code\n\n        char_list, raw_list = zip(*(get_info(c) for c in characters))\n        embed = Embed().set_author(name=\"Character Info\")\n\n        if len(characters) > 1:\n            # Maximum length possible is 502 out of 1024, so there's no need to truncate.\n            embed.add_field(name='Full Raw Text', value=f\"`{''.join(raw_list)}`\", inline=False)\n\n        await LinePaginator.paginate(char_list, ctx, embed, max_lines=10, max_size=2000, empty=False)\n\n    @command()\n    async def zen(self, ctx: Context, *, search_value: Union[int, str, None] = None) -> None:\n        \"\"\"\n        Show the Zen of Python.\n\n        Without any arguments, the full Zen will be produced.\n        If an integer is provided, the line with that index will be produced.\n        If a string is provided, the line which matches best will be produced.\n        \"\"\"\n        embed = Embed(\n            colour=Colour.blurple(),\n            title=\"The Zen of Python\",\n            description=ZEN_OF_PYTHON\n        )\n\n        if search_value is None:\n            embed.title += \", by Tim Peters\"\n            await ctx.send(embed=embed)\n            return\n\n        zen_lines = ZEN_OF_PYTHON.splitlines()\n\n        # handle if it's an index int\n        if isinstance(search_value, int):\n            upper_bound = len(zen_lines) - 1\n            lower_bound = -1 * upper_bound\n            if not (lower_bound <= search_value <= upper_bound):\n                raise BadArgument(f\"Please provide an index between {lower_bound} and {upper_bound}.\")\n\n            embed.title += f\" (line {search_value % len(zen_lines)}):\"\n            embed.description = zen_lines[search_value]\n            await ctx.send(embed=embed)\n            return\n\n        # Try to handle first exact word due difflib.SequenceMatched may use some other similar word instead\n        # exact word.\n        for i, line in enumerate(zen_lines):\n            for word in line.split():\n                if word.lower() == search_value.lower():\n                    embed.title += f\" (line {i}):\"\n                    embed.description = line\n                    await ctx.send(embed=embed)\n                    return\n\n        # handle if it's a search string and not exact word\n        matcher = difflib.SequenceMatcher(None, search_value.lower())\n\n        best_match = \"\"\n        match_index = 0\n        best_ratio = 0\n\n        for index, line in enumerate(zen_lines):\n            matcher.set_seq2(line.lower())\n\n            # the match ratio needs to be adjusted because, naturally,\n            # longer lines will have worse ratios than shorter lines when\n            # fuzzy searching for keywords. this seems to work okay.\n            adjusted_ratio = (len(line) - 5) ** 0.5 * matcher.ratio()\n\n            if adjusted_ratio > best_ratio:\n                best_ratio = adjusted_ratio\n                best_match = line\n                match_index = index\n\n        if not best_match:\n            raise BadArgument(\"I didn't get a match! Please try again with a different search term.\")\n\n        embed.title += f\" (line {match_index}):\"\n        embed.description = best_match\n        await ctx.send(embed=embed)\n\n    @command(aliases=(\"poll\",))\n    @has_any_role(*MODERATION_ROLES)\n    async def vote(self, ctx: Context, title: clean_content(fix_channel_mentions=True), *options: str) -> None:\n        \"\"\"\n        Build a quick voting poll with matching reactions with the provided options.\n\n        A maximum of 20 options can be provided, as Discord supports a max of 20\n        reactions on a single message.\n        \"\"\"\n        if len(title) > 256:\n            raise BadArgument(\"The title cannot be longer than 256 characters.\")\n        if len(options) < 2:\n            raise BadArgument(\"Please provide at least 2 options.\")\n        if len(options) > 20:\n            raise BadArgument(\"I can only handle 20 options!\")\n\n        codepoint_start = 127462  # represents \"regional_indicator_a\" unicode value\n        options = {chr(i): f\"{chr(i)} - {v}\" for i, v in enumerate(options, start=codepoint_start)}\n        embed = Embed(title=title, description=\"\\n\".join(options.values()))\n        message = await ctx.send(embed=embed)\n        for reaction in options:\n            await message.add_reaction(reaction)\n\n    async def send_pep_zero(self, ctx: Context) -> None:\n        \"\"\"Send information about PEP 0.\"\"\"\n        pep_embed = Embed(\n            title=\"**PEP 0 - Index of Python Enhancement Proposals (PEPs)**\",\n            url=\"https://www.python.org/dev/peps/\"\n        )\n        pep_embed.set_thumbnail(url=ICON_URL)\n        pep_embed.add_field(name=\"Status\", value=\"Active\")\n        pep_embed.add_field(name=\"Created\", value=\"13-Jul-2000\")\n        pep_embed.add_field(name=\"Type\", value=\"Informational\")\n\n        await ctx.send(embed=pep_embed)\n\n\ndef setup(bot: Bot) -> None:\n    \"\"\"Load the Utils cog.\"\"\"\n    bot.add_cog(Utils(bot))\n", "path": "bot/exts/utils/utils.py"}]}