We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
!zen command not working as intended
if you do
```
!zen explicit
```
it returns 
```
**The Zen of Python (line 10):**
Unless explicitly silenced.
```

while I do believe that the intent was to have it return line two
```
**The Zen of Python (line 1):**
Explicit is better than implicit.
```


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

{"golden_diff": "diff --git a/bot/cogs/utils.py b/bot/cogs/utils.py\n--- a/bot/cogs/utils.py\n+++ b/bot/cogs/utils.py\n@@ -236,7 +236,17 @@\n             await ctx.send(embed=embed)\n             return\n \n-        # handle if it's a search string\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", "issue": "!zen command not working as intended\nif you do\r\n```\r\n!zen explicit\r\n```\r\nit returns \r\n```\r\n**The Zen of Python (line 10):**\r\nUnless explicitly silenced.\r\n```\r\n\r\nwhile I do believe that the intent was to have it return line two\r\n```\r\n**The Zen of Python (line 1):**\r\nExplicit is better than implicit.\r\n```\r\n\n", "before_files": [{"content": "import difflib\nimport logging\nimport re\nimport unicodedata\nfrom asyncio import TimeoutError, sleep\nfrom email.parser import HeaderParser\nfrom io import StringIO\nfrom typing import Tuple, Union\n\nfrom dateutil import relativedelta\nfrom discord import Colour, Embed, Message, Role\nfrom discord.ext.commands import BadArgument, Cog, Context, command\n\nfrom bot.bot import Bot\nfrom bot.constants import Channels, MODERATION_ROLES, Mention, STAFF_ROLES\nfrom bot.decorators import in_channel, with_role\nfrom bot.utils.time import humanize_delta\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.invoke(self.bot.get_command(\"help\"), \"pep\")\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_channel(Channels.bot_commands, bypass_roles=STAFF_ROLES)\n    async def charinfo(self, ctx: Context, *, characters: str) -> None:\n        \"\"\"Shows you information on up to 25 unicode characters.\"\"\"\n        match = re.match(r\"<(a?):(\\w+):(\\d+)>\", characters)\n        if match:\n            embed = Embed(\n                title=\"Non-Character Detected\",\n                description=(\n                    \"Only unicode characters can be processed, but a custom Discord emoji \"\n                    \"was found. Please remove it and try again.\"\n                )\n            )\n            embed.colour = Colour.red()\n            await ctx.send(embed=embed)\n            return\n\n        if len(characters) > 25:\n            embed = Embed(title=f\"Too many characters ({len(characters)}/25)\")\n            embed.colour = Colour.red()\n            await ctx.send(embed=embed)\n            return\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} - {char}\"\n            return info, u_code\n\n        charlist, rawlist = zip(*(get_info(c) for c in characters))\n\n        embed = Embed(description=\"\\n\".join(charlist))\n        embed.set_author(name=\"Character Info\")\n\n        if len(characters) > 1:\n            embed.add_field(name='Raw', value=f\"`{''.join(rawlist)}`\", inline=False)\n\n        await ctx.send(embed=embed)\n\n    @command()\n    @with_role(*MODERATION_ROLES)\n    async def mention(self, ctx: Context, *, role: Role) -> None:\n        \"\"\"Set a role to be mentionable for a limited time.\"\"\"\n        if role.mentionable:\n            await ctx.send(f\"{role} is already mentionable!\")\n            return\n\n        await role.edit(reason=f\"Role unlocked by {ctx.author}\", mentionable=True)\n\n        human_time = humanize_delta(relativedelta.relativedelta(seconds=Mention.message_timeout))\n        await ctx.send(\n            f\"{role} has been made mentionable. I will reset it in {human_time}, or when someone mentions this role.\"\n        )\n\n        def check(m: Message) -> bool:\n            \"\"\"Checks that the message contains the role mention.\"\"\"\n            return role in m.role_mentions\n\n        try:\n            msg = await self.bot.wait_for(\"message\", check=check, timeout=Mention.message_timeout)\n        except TimeoutError:\n            await role.edit(mentionable=False, reason=\"Automatic role lock - timeout.\")\n            await ctx.send(f\"{ctx.author.mention}, you took too long. I have reset {role} to be unmentionable.\")\n            return\n\n        if any(r.id in MODERATION_ROLES for r in msg.author.roles):\n            await sleep(Mention.reset_delay)\n            await role.edit(mentionable=False, reason=f\"Automatic role lock by {msg.author}\")\n            await ctx.send(\n                f\"{ctx.author.mention}, I have reset {role} to be unmentionable as \"\n                f\"{msg.author if msg.author != ctx.author else 'you'} sent a message mentioning it.\"\n            )\n            return\n\n        await role.edit(mentionable=False, reason=f\"Automatic role lock - unauthorised use by {msg.author}\")\n        await ctx.send(\n            f\"{ctx.author.mention}, I have reset {role} to be unmentionable \"\n            f\"as I detected unauthorised use by {msg.author} (ID: {msg.author.id}).\"\n        )\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        # handle if it's a search string\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    @with_role(*MODERATION_ROLES)\n    async def vote(self, ctx: Context, title: str, *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(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=f\"**PEP 0 - Index of Python Enhancement Proposals (PEPs)**\",\n            description=f\"[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/cogs/utils.py"}], "after_files": [{"content": "import difflib\nimport logging\nimport re\nimport unicodedata\nfrom asyncio import TimeoutError, sleep\nfrom email.parser import HeaderParser\nfrom io import StringIO\nfrom typing import Tuple, Union\n\nfrom dateutil import relativedelta\nfrom discord import Colour, Embed, Message, Role\nfrom discord.ext.commands import BadArgument, Cog, Context, command\n\nfrom bot.bot import Bot\nfrom bot.constants import Channels, MODERATION_ROLES, Mention, STAFF_ROLES\nfrom bot.decorators import in_channel, with_role\nfrom bot.utils.time import humanize_delta\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.invoke(self.bot.get_command(\"help\"), \"pep\")\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_channel(Channels.bot_commands, bypass_roles=STAFF_ROLES)\n    async def charinfo(self, ctx: Context, *, characters: str) -> None:\n        \"\"\"Shows you information on up to 25 unicode characters.\"\"\"\n        match = re.match(r\"<(a?):(\\w+):(\\d+)>\", characters)\n        if match:\n            embed = Embed(\n                title=\"Non-Character Detected\",\n                description=(\n                    \"Only unicode characters can be processed, but a custom Discord emoji \"\n                    \"was found. Please remove it and try again.\"\n                )\n            )\n            embed.colour = Colour.red()\n            await ctx.send(embed=embed)\n            return\n\n        if len(characters) > 25:\n            embed = Embed(title=f\"Too many characters ({len(characters)}/25)\")\n            embed.colour = Colour.red()\n            await ctx.send(embed=embed)\n            return\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} - {char}\"\n            return info, u_code\n\n        charlist, rawlist = zip(*(get_info(c) for c in characters))\n\n        embed = Embed(description=\"\\n\".join(charlist))\n        embed.set_author(name=\"Character Info\")\n\n        if len(characters) > 1:\n            embed.add_field(name='Raw', value=f\"`{''.join(rawlist)}`\", inline=False)\n\n        await ctx.send(embed=embed)\n\n    @command()\n    @with_role(*MODERATION_ROLES)\n    async def mention(self, ctx: Context, *, role: Role) -> None:\n        \"\"\"Set a role to be mentionable for a limited time.\"\"\"\n        if role.mentionable:\n            await ctx.send(f\"{role} is already mentionable!\")\n            return\n\n        await role.edit(reason=f\"Role unlocked by {ctx.author}\", mentionable=True)\n\n        human_time = humanize_delta(relativedelta.relativedelta(seconds=Mention.message_timeout))\n        await ctx.send(\n            f\"{role} has been made mentionable. I will reset it in {human_time}, or when someone mentions this role.\"\n        )\n\n        def check(m: Message) -> bool:\n            \"\"\"Checks that the message contains the role mention.\"\"\"\n            return role in m.role_mentions\n\n        try:\n            msg = await self.bot.wait_for(\"message\", check=check, timeout=Mention.message_timeout)\n        except TimeoutError:\n            await role.edit(mentionable=False, reason=\"Automatic role lock - timeout.\")\n            await ctx.send(f\"{ctx.author.mention}, you took too long. I have reset {role} to be unmentionable.\")\n            return\n\n        if any(r.id in MODERATION_ROLES for r in msg.author.roles):\n            await sleep(Mention.reset_delay)\n            await role.edit(mentionable=False, reason=f\"Automatic role lock by {msg.author}\")\n            await ctx.send(\n                f\"{ctx.author.mention}, I have reset {role} to be unmentionable as \"\n                f\"{msg.author if msg.author != ctx.author else 'you'} sent a message mentioning it.\"\n            )\n            return\n\n        await role.edit(mentionable=False, reason=f\"Automatic role lock - unauthorised use by {msg.author}\")\n        await ctx.send(\n            f\"{ctx.author.mention}, I have reset {role} to be unmentionable \"\n            f\"as I detected unauthorised use by {msg.author} (ID: {msg.author.id}).\"\n        )\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    @with_role(*MODERATION_ROLES)\n    async def vote(self, ctx: Context, title: str, *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(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=f\"**PEP 0 - Index of Python Enhancement Proposals (PEPs)**\",\n            description=f\"[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/cogs/utils.py"}]}