We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Add pep8-naming to linting toolchain
We've decided to use this plugin in our linting toolchain, which allows us to test things like variable name casing (no camelCase please!) and whether classes and methods use `cls` and `self`.

## Implementation
Here are the errors it can catch:

code | sample message
-- | --
N801 | class names should use CapWords convention
N802 | function name should be lowercase
N803 | argument name should be lowercase
N804 | first argument of a classmethod should be named 'cls'
N805 | first argument of a method should be named 'self'
N806 | variable in function should be lowercase
N807 | function name should not start and end with '__'
  |  
N811 | constant imported as non constant
N812 | lowercase imported as non lowercase
N813 | camelcase imported as lowercase
N814 | camelcase imported as constant
N815 | mixedCase variable in class scope
N816 | mixedCase variable in global scope

We have not decided exactly which of these we should ignore, so it will be up to the person who handles this issue to discuss this with the core devs and come to a consensus on whether we should ignore some of these. It will probably be easier to see which ones here are not necessary after actually running a lint with this plugin on our current codebase.

The person implementing this will also need to fix all the linting errors that occur once this has been added to the toolchain.

## Steps
- [ ] Add the plugin to the toolchain
- [ ] Run it and look through the errors that are created. Are any of them out of touch with our style guidelines, and should we ignore some of these rules? Discuss with the core developers if you think so.
- [ ] Fix all the linting problems.

--- 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/utils/__init__.py`
Content:
```
1 from abc import ABCMeta
2 from typing import Any, Hashable
3 
4 from discord.ext.commands import CogMeta
5 
6 
7 class CogABCMeta(CogMeta, ABCMeta):
8     """Metaclass for ABCs meant to be implemented as Cogs."""
9 
10     pass
11 
12 
13 class CaseInsensitiveDict(dict):
14     """
15     We found this class on StackOverflow. Thanks to m000 for writing it!
16 
17     https://stackoverflow.com/a/32888599/4022104
18     """
19 
20     @classmethod
21     def _k(cls, key: Hashable) -> Hashable:
22         """Return lowered key if a string-like is passed, otherwise pass key straight through."""
23         return key.lower() if isinstance(key, str) else key
24 
25     def __init__(self, *args, **kwargs):
26         super(CaseInsensitiveDict, self).__init__(*args, **kwargs)
27         self._convert_keys()
28 
29     def __getitem__(self, key: Hashable) -> Any:
30         """Case insensitive __setitem__."""
31         return super(CaseInsensitiveDict, self).__getitem__(self.__class__._k(key))
32 
33     def __setitem__(self, key: Hashable, value: Any):
34         """Case insensitive __setitem__."""
35         super(CaseInsensitiveDict, self).__setitem__(self.__class__._k(key), value)
36 
37     def __delitem__(self, key: Hashable) -> Any:
38         """Case insensitive __delitem__."""
39         return super(CaseInsensitiveDict, self).__delitem__(self.__class__._k(key))
40 
41     def __contains__(self, key: Hashable) -> bool:
42         """Case insensitive __contains__."""
43         return super(CaseInsensitiveDict, self).__contains__(self.__class__._k(key))
44 
45     def pop(self, key: Hashable, *args, **kwargs) -> Any:
46         """Case insensitive pop."""
47         return super(CaseInsensitiveDict, self).pop(self.__class__._k(key), *args, **kwargs)
48 
49     def get(self, key: Hashable, *args, **kwargs) -> Any:
50         """Case insensitive get."""
51         return super(CaseInsensitiveDict, self).get(self.__class__._k(key), *args, **kwargs)
52 
53     def setdefault(self, key: Hashable, *args, **kwargs) -> Any:
54         """Case insensitive setdefault."""
55         return super(CaseInsensitiveDict, self).setdefault(self.__class__._k(key), *args, **kwargs)
56 
57     def update(self, E: Any = None, **F) -> None:
58         """Case insensitive update."""
59         super(CaseInsensitiveDict, self).update(self.__class__(E))
60         super(CaseInsensitiveDict, self).update(self.__class__(**F))
61 
62     def _convert_keys(self) -> None:
63         """Helper method to lowercase all existing string-like keys."""
64         for k in list(self.keys()):
65             v = super(CaseInsensitiveDict, self).pop(k)
66             self.__setitem__(k, v)
67
```

Path: `bot/cogs/free.py`
Content:
```
1 import logging
2 from datetime import datetime
3 from operator import itemgetter
4 
5 from discord import Colour, Embed, Member, utils
6 from discord.ext.commands import Cog, Context, command
7 
8 from bot.bot import Bot
9 from bot.constants import Categories, Channels, Free, STAFF_ROLES
10 from bot.decorators import redirect_output
11 
12 log = logging.getLogger(__name__)
13 
14 TIMEOUT = Free.activity_timeout
15 RATE = Free.cooldown_rate
16 PER = Free.cooldown_per
17 
18 
19 class Free(Cog):
20     """Tries to figure out which help channels are free."""
21 
22     PYTHON_HELP_ID = Categories.python_help
23 
24     @command(name="free", aliases=('f',))
25     @redirect_output(destination_channel=Channels.bot_commands, bypass_roles=STAFF_ROLES)
26     async def free(self, ctx: Context, user: Member = None, seek: int = 2) -> None:
27         """
28         Lists free help channels by likeliness of availability.
29 
30         seek is used only when this command is invoked in a help channel.
31         You cannot override seek without mentioning a user first.
32 
33         When seek is 2, we are avoiding considering the last active message
34         in a channel to be the one that invoked this command.
35 
36         When seek is 3 or more, a user has been mentioned on the assumption
37         that they asked if the channel is free or they asked their question
38         in an active channel, and we want the message before that happened.
39         """
40         free_channels = []
41         python_help = utils.get(ctx.guild.categories, id=self.PYTHON_HELP_ID)
42 
43         if user is not None and seek == 2:
44             seek = 3
45         elif not 0 < seek < 10:
46             seek = 3
47 
48         # Iterate through all the help channels
49         # to check latest activity
50         for channel in python_help.channels:
51             # Seek further back in the help channel
52             # the command was invoked in
53             if channel.id == ctx.channel.id:
54                 messages = await channel.history(limit=seek).flatten()
55                 msg = messages[seek - 1]
56             # Otherwise get last message
57             else:
58                 msg = await channel.history(limit=1).next()  # noqa (False positive)
59 
60             inactive = (datetime.utcnow() - msg.created_at).seconds
61             if inactive > TIMEOUT:
62                 free_channels.append((inactive, channel))
63 
64         embed = Embed()
65         embed.colour = Colour.blurple()
66         embed.title = "**Looking for a free help channel?**"
67 
68         if user is not None:
69             embed.description = f"**Hey {user.mention}!**\n\n"
70         else:
71             embed.description = ""
72 
73         # Display all potentially inactive channels
74         # in descending order of inactivity
75         if free_channels:
76             # Sort channels in descending order by seconds
77             # Get position in list, inactivity, and channel object
78             # For each channel, add to embed.description
79             sorted_channels = sorted(free_channels, key=itemgetter(0), reverse=True)
80 
81             for (inactive, channel) in sorted_channels[:3]:
82                 minutes, seconds = divmod(inactive, 60)
83                 if minutes > 59:
84                     hours, minutes = divmod(minutes, 60)
85                     embed.description += f"{channel.mention} **{hours}h {minutes}m {seconds}s** inactive\n"
86                 else:
87                     embed.description += f"{channel.mention} **{minutes}m {seconds}s** inactive\n"
88 
89             embed.set_footer(text="Please confirm these channels are free before posting")
90         else:
91             embed.description = (
92                 "Doesn't look like any channels are available right now. "
93                 "You're welcome to check for yourself to be sure. "
94                 "If all channels are truly busy, please be patient "
95                 "as one will likely be available soon."
96             )
97 
98         await ctx.send(embed=embed)
99 
100 
101 def setup(bot: Bot) -> None:
102     """Load the Free cog."""
103     bot.add_cog(Free())
104
```
--- 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/free.py b/bot/cogs/free.py
--- a/bot/cogs/free.py
+++ b/bot/cogs/free.py
@@ -55,7 +55,7 @@
                 msg = messages[seek - 1]
             # Otherwise get last message
             else:
-                msg = await channel.history(limit=1).next()  # noqa (False positive)
+                msg = await channel.history(limit=1).next()  # noqa: B305
 
             inactive = (datetime.utcnow() - msg.created_at).seconds
             if inactive > TIMEOUT:
diff --git a/bot/utils/__init__.py b/bot/utils/__init__.py
--- a/bot/utils/__init__.py
+++ b/bot/utils/__init__.py
@@ -1,5 +1,4 @@
 from abc import ABCMeta
-from typing import Any, Hashable
 
 from discord.ext.commands import CogMeta
 
@@ -8,59 +7,3 @@
     """Metaclass for ABCs meant to be implemented as Cogs."""
 
     pass
-
-
-class CaseInsensitiveDict(dict):
-    """
-    We found this class on StackOverflow. Thanks to m000 for writing it!
-
-    https://stackoverflow.com/a/32888599/4022104
-    """
-
-    @classmethod
-    def _k(cls, key: Hashable) -> Hashable:
-        """Return lowered key if a string-like is passed, otherwise pass key straight through."""
-        return key.lower() if isinstance(key, str) else key
-
-    def __init__(self, *args, **kwargs):
-        super(CaseInsensitiveDict, self).__init__(*args, **kwargs)
-        self._convert_keys()
-
-    def __getitem__(self, key: Hashable) -> Any:
-        """Case insensitive __setitem__."""
-        return super(CaseInsensitiveDict, self).__getitem__(self.__class__._k(key))
-
-    def __setitem__(self, key: Hashable, value: Any):
-        """Case insensitive __setitem__."""
-        super(CaseInsensitiveDict, self).__setitem__(self.__class__._k(key), value)
-
-    def __delitem__(self, key: Hashable) -> Any:
-        """Case insensitive __delitem__."""
-        return super(CaseInsensitiveDict, self).__delitem__(self.__class__._k(key))
-
-    def __contains__(self, key: Hashable) -> bool:
-        """Case insensitive __contains__."""
-        return super(CaseInsensitiveDict, self).__contains__(self.__class__._k(key))
-
-    def pop(self, key: Hashable, *args, **kwargs) -> Any:
-        """Case insensitive pop."""
-        return super(CaseInsensitiveDict, self).pop(self.__class__._k(key), *args, **kwargs)
-
-    def get(self, key: Hashable, *args, **kwargs) -> Any:
-        """Case insensitive get."""
-        return super(CaseInsensitiveDict, self).get(self.__class__._k(key), *args, **kwargs)
-
-    def setdefault(self, key: Hashable, *args, **kwargs) -> Any:
-        """Case insensitive setdefault."""
-        return super(CaseInsensitiveDict, self).setdefault(self.__class__._k(key), *args, **kwargs)
-
-    def update(self, E: Any = None, **F) -> None:
-        """Case insensitive update."""
-        super(CaseInsensitiveDict, self).update(self.__class__(E))
-        super(CaseInsensitiveDict, self).update(self.__class__(**F))
-
-    def _convert_keys(self) -> None:
-        """Helper method to lowercase all existing string-like keys."""
-        for k in list(self.keys()):
-            v = super(CaseInsensitiveDict, self).pop(k)
-            self.__setitem__(k, v)

{"golden_diff": "diff --git a/bot/cogs/free.py b/bot/cogs/free.py\n--- a/bot/cogs/free.py\n+++ b/bot/cogs/free.py\n@@ -55,7 +55,7 @@\n                 msg = messages[seek - 1]\n             # Otherwise get last message\n             else:\n-                msg = await channel.history(limit=1).next()  # noqa (False positive)\n+                msg = await channel.history(limit=1).next()  # noqa: B305\n \n             inactive = (datetime.utcnow() - msg.created_at).seconds\n             if inactive > TIMEOUT:\ndiff --git a/bot/utils/__init__.py b/bot/utils/__init__.py\n--- a/bot/utils/__init__.py\n+++ b/bot/utils/__init__.py\n@@ -1,5 +1,4 @@\n from abc import ABCMeta\n-from typing import Any, Hashable\n \n from discord.ext.commands import CogMeta\n \n@@ -8,59 +7,3 @@\n     \"\"\"Metaclass for ABCs meant to be implemented as Cogs.\"\"\"\n \n     pass\n-\n-\n-class CaseInsensitiveDict(dict):\n-    \"\"\"\n-    We found this class on StackOverflow. Thanks to m000 for writing it!\n-\n-    https://stackoverflow.com/a/32888599/4022104\n-    \"\"\"\n-\n-    @classmethod\n-    def _k(cls, key: Hashable) -> Hashable:\n-        \"\"\"Return lowered key if a string-like is passed, otherwise pass key straight through.\"\"\"\n-        return key.lower() if isinstance(key, str) else key\n-\n-    def __init__(self, *args, **kwargs):\n-        super(CaseInsensitiveDict, self).__init__(*args, **kwargs)\n-        self._convert_keys()\n-\n-    def __getitem__(self, key: Hashable) -> Any:\n-        \"\"\"Case insensitive __setitem__.\"\"\"\n-        return super(CaseInsensitiveDict, self).__getitem__(self.__class__._k(key))\n-\n-    def __setitem__(self, key: Hashable, value: Any):\n-        \"\"\"Case insensitive __setitem__.\"\"\"\n-        super(CaseInsensitiveDict, self).__setitem__(self.__class__._k(key), value)\n-\n-    def __delitem__(self, key: Hashable) -> Any:\n-        \"\"\"Case insensitive __delitem__.\"\"\"\n-        return super(CaseInsensitiveDict, self).__delitem__(self.__class__._k(key))\n-\n-    def __contains__(self, key: Hashable) -> bool:\n-        \"\"\"Case insensitive __contains__.\"\"\"\n-        return super(CaseInsensitiveDict, self).__contains__(self.__class__._k(key))\n-\n-    def pop(self, key: Hashable, *args, **kwargs) -> Any:\n-        \"\"\"Case insensitive pop.\"\"\"\n-        return super(CaseInsensitiveDict, self).pop(self.__class__._k(key), *args, **kwargs)\n-\n-    def get(self, key: Hashable, *args, **kwargs) -> Any:\n-        \"\"\"Case insensitive get.\"\"\"\n-        return super(CaseInsensitiveDict, self).get(self.__class__._k(key), *args, **kwargs)\n-\n-    def setdefault(self, key: Hashable, *args, **kwargs) -> Any:\n-        \"\"\"Case insensitive setdefault.\"\"\"\n-        return super(CaseInsensitiveDict, self).setdefault(self.__class__._k(key), *args, **kwargs)\n-\n-    def update(self, E: Any = None, **F) -> None:\n-        \"\"\"Case insensitive update.\"\"\"\n-        super(CaseInsensitiveDict, self).update(self.__class__(E))\n-        super(CaseInsensitiveDict, self).update(self.__class__(**F))\n-\n-    def _convert_keys(self) -> None:\n-        \"\"\"Helper method to lowercase all existing string-like keys.\"\"\"\n-        for k in list(self.keys()):\n-            v = super(CaseInsensitiveDict, self).pop(k)\n-            self.__setitem__(k, v)\n", "issue": "Add pep8-naming to linting toolchain\nWe've decided to use this plugin in our linting toolchain, which allows us to test things like variable name casing (no camelCase please!) and whether classes and methods use `cls` and `self`.\r\n\r\n## Implementation\r\nHere are the errors it can catch:\r\n\r\ncode | sample message\r\n-- | --\r\nN801 | class names should use CapWords convention\r\nN802 | function name should be lowercase\r\nN803 | argument name should be lowercase\r\nN804 | first argument of a classmethod should be named 'cls'\r\nN805 | first argument of a method should be named 'self'\r\nN806 | variable in function should be lowercase\r\nN807 | function name should not start and end with '__'\r\n\u00a0 | \u00a0\r\nN811 | constant imported as non constant\r\nN812 | lowercase imported as non lowercase\r\nN813 | camelcase imported as lowercase\r\nN814 | camelcase imported as constant\r\nN815 | mixedCase variable in class scope\r\nN816 | mixedCase variable in global scope\r\n\r\nWe have not decided exactly which of these we should ignore, so it will be up to the person who handles this issue to discuss this with the core devs and come to a consensus on whether we should ignore some of these. It will probably be easier to see which ones here are not necessary after actually running a lint with this plugin on our current codebase.\r\n\r\nThe person implementing this will also need to fix all the linting errors that occur once this has been added to the toolchain.\r\n\r\n## Steps\r\n- [ ] Add the plugin to the toolchain\r\n- [ ] Run it and look through the errors that are created. Are any of them out of touch with our style guidelines, and should we ignore some of these rules? Discuss with the core developers if you think so.\r\n- [ ] Fix all the linting problems.\n", "before_files": [{"content": "from abc import ABCMeta\nfrom typing import Any, Hashable\n\nfrom discord.ext.commands import CogMeta\n\n\nclass CogABCMeta(CogMeta, ABCMeta):\n    \"\"\"Metaclass for ABCs meant to be implemented as Cogs.\"\"\"\n\n    pass\n\n\nclass CaseInsensitiveDict(dict):\n    \"\"\"\n    We found this class on StackOverflow. Thanks to m000 for writing it!\n\n    https://stackoverflow.com/a/32888599/4022104\n    \"\"\"\n\n    @classmethod\n    def _k(cls, key: Hashable) -> Hashable:\n        \"\"\"Return lowered key if a string-like is passed, otherwise pass key straight through.\"\"\"\n        return key.lower() if isinstance(key, str) else key\n\n    def __init__(self, *args, **kwargs):\n        super(CaseInsensitiveDict, self).__init__(*args, **kwargs)\n        self._convert_keys()\n\n    def __getitem__(self, key: Hashable) -> Any:\n        \"\"\"Case insensitive __setitem__.\"\"\"\n        return super(CaseInsensitiveDict, self).__getitem__(self.__class__._k(key))\n\n    def __setitem__(self, key: Hashable, value: Any):\n        \"\"\"Case insensitive __setitem__.\"\"\"\n        super(CaseInsensitiveDict, self).__setitem__(self.__class__._k(key), value)\n\n    def __delitem__(self, key: Hashable) -> Any:\n        \"\"\"Case insensitive __delitem__.\"\"\"\n        return super(CaseInsensitiveDict, self).__delitem__(self.__class__._k(key))\n\n    def __contains__(self, key: Hashable) -> bool:\n        \"\"\"Case insensitive __contains__.\"\"\"\n        return super(CaseInsensitiveDict, self).__contains__(self.__class__._k(key))\n\n    def pop(self, key: Hashable, *args, **kwargs) -> Any:\n        \"\"\"Case insensitive pop.\"\"\"\n        return super(CaseInsensitiveDict, self).pop(self.__class__._k(key), *args, **kwargs)\n\n    def get(self, key: Hashable, *args, **kwargs) -> Any:\n        \"\"\"Case insensitive get.\"\"\"\n        return super(CaseInsensitiveDict, self).get(self.__class__._k(key), *args, **kwargs)\n\n    def setdefault(self, key: Hashable, *args, **kwargs) -> Any:\n        \"\"\"Case insensitive setdefault.\"\"\"\n        return super(CaseInsensitiveDict, self).setdefault(self.__class__._k(key), *args, **kwargs)\n\n    def update(self, E: Any = None, **F) -> None:\n        \"\"\"Case insensitive update.\"\"\"\n        super(CaseInsensitiveDict, self).update(self.__class__(E))\n        super(CaseInsensitiveDict, self).update(self.__class__(**F))\n\n    def _convert_keys(self) -> None:\n        \"\"\"Helper method to lowercase all existing string-like keys.\"\"\"\n        for k in list(self.keys()):\n            v = super(CaseInsensitiveDict, self).pop(k)\n            self.__setitem__(k, v)\n", "path": "bot/utils/__init__.py"}, {"content": "import logging\nfrom datetime import datetime\nfrom operator import itemgetter\n\nfrom discord import Colour, Embed, Member, utils\nfrom discord.ext.commands import Cog, Context, command\n\nfrom bot.bot import Bot\nfrom bot.constants import Categories, Channels, Free, STAFF_ROLES\nfrom bot.decorators import redirect_output\n\nlog = logging.getLogger(__name__)\n\nTIMEOUT = Free.activity_timeout\nRATE = Free.cooldown_rate\nPER = Free.cooldown_per\n\n\nclass Free(Cog):\n    \"\"\"Tries to figure out which help channels are free.\"\"\"\n\n    PYTHON_HELP_ID = Categories.python_help\n\n    @command(name=\"free\", aliases=('f',))\n    @redirect_output(destination_channel=Channels.bot_commands, bypass_roles=STAFF_ROLES)\n    async def free(self, ctx: Context, user: Member = None, seek: int = 2) -> None:\n        \"\"\"\n        Lists free help channels by likeliness of availability.\n\n        seek is used only when this command is invoked in a help channel.\n        You cannot override seek without mentioning a user first.\n\n        When seek is 2, we are avoiding considering the last active message\n        in a channel to be the one that invoked this command.\n\n        When seek is 3 or more, a user has been mentioned on the assumption\n        that they asked if the channel is free or they asked their question\n        in an active channel, and we want the message before that happened.\n        \"\"\"\n        free_channels = []\n        python_help = utils.get(ctx.guild.categories, id=self.PYTHON_HELP_ID)\n\n        if user is not None and seek == 2:\n            seek = 3\n        elif not 0 < seek < 10:\n            seek = 3\n\n        # Iterate through all the help channels\n        # to check latest activity\n        for channel in python_help.channels:\n            # Seek further back in the help channel\n            # the command was invoked in\n            if channel.id == ctx.channel.id:\n                messages = await channel.history(limit=seek).flatten()\n                msg = messages[seek - 1]\n            # Otherwise get last message\n            else:\n                msg = await channel.history(limit=1).next()  # noqa (False positive)\n\n            inactive = (datetime.utcnow() - msg.created_at).seconds\n            if inactive > TIMEOUT:\n                free_channels.append((inactive, channel))\n\n        embed = Embed()\n        embed.colour = Colour.blurple()\n        embed.title = \"**Looking for a free help channel?**\"\n\n        if user is not None:\n            embed.description = f\"**Hey {user.mention}!**\\n\\n\"\n        else:\n            embed.description = \"\"\n\n        # Display all potentially inactive channels\n        # in descending order of inactivity\n        if free_channels:\n            # Sort channels in descending order by seconds\n            # Get position in list, inactivity, and channel object\n            # For each channel, add to embed.description\n            sorted_channels = sorted(free_channels, key=itemgetter(0), reverse=True)\n\n            for (inactive, channel) in sorted_channels[:3]:\n                minutes, seconds = divmod(inactive, 60)\n                if minutes > 59:\n                    hours, minutes = divmod(minutes, 60)\n                    embed.description += f\"{channel.mention} **{hours}h {minutes}m {seconds}s** inactive\\n\"\n                else:\n                    embed.description += f\"{channel.mention} **{minutes}m {seconds}s** inactive\\n\"\n\n            embed.set_footer(text=\"Please confirm these channels are free before posting\")\n        else:\n            embed.description = (\n                \"Doesn't look like any channels are available right now. \"\n                \"You're welcome to check for yourself to be sure. \"\n                \"If all channels are truly busy, please be patient \"\n                \"as one will likely be available soon.\"\n            )\n\n        await ctx.send(embed=embed)\n\n\ndef setup(bot: Bot) -> None:\n    \"\"\"Load the Free cog.\"\"\"\n    bot.add_cog(Free())\n", "path": "bot/cogs/free.py"}], "after_files": [{"content": "from abc import ABCMeta\n\nfrom discord.ext.commands import CogMeta\n\n\nclass CogABCMeta(CogMeta, ABCMeta):\n    \"\"\"Metaclass for ABCs meant to be implemented as Cogs.\"\"\"\n\n    pass\n", "path": "bot/utils/__init__.py"}, {"content": "import logging\nfrom datetime import datetime\nfrom operator import itemgetter\n\nfrom discord import Colour, Embed, Member, utils\nfrom discord.ext.commands import Cog, Context, command\n\nfrom bot.bot import Bot\nfrom bot.constants import Categories, Channels, Free, STAFF_ROLES\nfrom bot.decorators import redirect_output\n\nlog = logging.getLogger(__name__)\n\nTIMEOUT = Free.activity_timeout\nRATE = Free.cooldown_rate\nPER = Free.cooldown_per\n\n\nclass Free(Cog):\n    \"\"\"Tries to figure out which help channels are free.\"\"\"\n\n    PYTHON_HELP_ID = Categories.python_help\n\n    @command(name=\"free\", aliases=('f',))\n    @redirect_output(destination_channel=Channels.bot_commands, bypass_roles=STAFF_ROLES)\n    async def free(self, ctx: Context, user: Member = None, seek: int = 2) -> None:\n        \"\"\"\n        Lists free help channels by likeliness of availability.\n\n        seek is used only when this command is invoked in a help channel.\n        You cannot override seek without mentioning a user first.\n\n        When seek is 2, we are avoiding considering the last active message\n        in a channel to be the one that invoked this command.\n\n        When seek is 3 or more, a user has been mentioned on the assumption\n        that they asked if the channel is free or they asked their question\n        in an active channel, and we want the message before that happened.\n        \"\"\"\n        free_channels = []\n        python_help = utils.get(ctx.guild.categories, id=self.PYTHON_HELP_ID)\n\n        if user is not None and seek == 2:\n            seek = 3\n        elif not 0 < seek < 10:\n            seek = 3\n\n        # Iterate through all the help channels\n        # to check latest activity\n        for channel in python_help.channels:\n            # Seek further back in the help channel\n            # the command was invoked in\n            if channel.id == ctx.channel.id:\n                messages = await channel.history(limit=seek).flatten()\n                msg = messages[seek - 1]\n            # Otherwise get last message\n            else:\n                msg = await channel.history(limit=1).next()  # noqa: B305\n\n            inactive = (datetime.utcnow() - msg.created_at).seconds\n            if inactive > TIMEOUT:\n                free_channels.append((inactive, channel))\n\n        embed = Embed()\n        embed.colour = Colour.blurple()\n        embed.title = \"**Looking for a free help channel?**\"\n\n        if user is not None:\n            embed.description = f\"**Hey {user.mention}!**\\n\\n\"\n        else:\n            embed.description = \"\"\n\n        # Display all potentially inactive channels\n        # in descending order of inactivity\n        if free_channels:\n            # Sort channels in descending order by seconds\n            # Get position in list, inactivity, and channel object\n            # For each channel, add to embed.description\n            sorted_channels = sorted(free_channels, key=itemgetter(0), reverse=True)\n\n            for (inactive, channel) in sorted_channels[:3]:\n                minutes, seconds = divmod(inactive, 60)\n                if minutes > 59:\n                    hours, minutes = divmod(minutes, 60)\n                    embed.description += f\"{channel.mention} **{hours}h {minutes}m {seconds}s** inactive\\n\"\n                else:\n                    embed.description += f\"{channel.mention} **{minutes}m {seconds}s** inactive\\n\"\n\n            embed.set_footer(text=\"Please confirm these channels are free before posting\")\n        else:\n            embed.description = (\n                \"Doesn't look like any channels are available right now. \"\n                \"You're welcome to check for yourself to be sure. \"\n                \"If all channels are truly busy, please be patient \"\n                \"as one will likely be available soon.\"\n            )\n\n        await ctx.send(embed=embed)\n\n\ndef setup(bot: Bot) -> None:\n    \"\"\"Load the Free cog.\"\"\"\n    bot.add_cog(Free())\n", "path": "bot/cogs/free.py"}]}