prompt: We are currently solving the following issue within our repository. Here is the issue text:
  --- BEGIN ISSUE ---
  Tags can't be edited due to using the POST method with the API
  Currently, the `!tag edit` subcommand is just an alias of `!tag set`. This means that if we try to edit an existing tag, the bot will use the POST http method to communicate with the API. Since we're not posting a new tag, but editing an existing entry, the API will reject this request. 
  
  Instead of using POST, we should be using PATCH, since we're only partially updating the entry in the database.
  
  --- 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/tags.py`
  Content:
  ```
  1 import logging
  2 import time
  3 
  4 from discord import Colour, Embed
  5 from discord.ext.commands import Bot, Cog, Context, group
  6 
  7 from bot.constants import Channels, Cooldowns, MODERATION_ROLES, Roles
  8 from bot.converters import TagContentConverter, TagNameConverter
  9 from bot.decorators import with_role
  10 from bot.pagination import LinePaginator
  11 
  12 
  13 log = logging.getLogger(__name__)
  14 
  15 TEST_CHANNELS = (
  16     Channels.devtest,
  17     Channels.bot,
  18     Channels.helpers
  19 )
  20 
  21 
  22 class Tags(Cog):
  23     """Save new tags and fetch existing tags."""
  24 
  25     def __init__(self, bot: Bot):
  26         self.bot = bot
  27         self.tag_cooldowns = {}
  28 
  29     @group(name='tags', aliases=('tag', 't'), invoke_without_command=True)
  30     async def tags_group(self, ctx: Context, *, tag_name: TagNameConverter = None) -> None:
  31         """Show all known tags, a single tag, or run a subcommand."""
  32         await ctx.invoke(self.get_command, tag_name=tag_name)
  33 
  34     @tags_group.command(name='get', aliases=('show', 'g'))
  35     async def get_command(self, ctx: Context, *, tag_name: TagNameConverter = None) -> None:
  36         """Get a specified tag, or a list of all tags if no tag is specified."""
  37         def _command_on_cooldown(tag_name: str) -> bool:
  38             """
  39             Check if the command is currently on cooldown, on a per-tag, per-channel basis.
  40 
  41             The cooldown duration is set in constants.py.
  42             """
  43             now = time.time()
  44 
  45             cooldown_conditions = (
  46                 tag_name
  47                 and tag_name in self.tag_cooldowns
  48                 and (now - self.tag_cooldowns[tag_name]["time"]) < Cooldowns.tags
  49                 and self.tag_cooldowns[tag_name]["channel"] == ctx.channel.id
  50             )
  51 
  52             if cooldown_conditions:
  53                 return True
  54             return False
  55 
  56         if _command_on_cooldown(tag_name):
  57             time_left = Cooldowns.tags - (time.time() - self.tag_cooldowns[tag_name]["time"])
  58             log.warning(f"{ctx.author} tried to get the '{tag_name}' tag, but the tag is on cooldown. "
  59                         f"Cooldown ends in {time_left:.1f} seconds.")
  60             return
  61 
  62         if tag_name is not None:
  63             tag = await self.bot.api_client.get(f'bot/tags/{tag_name}')
  64             if ctx.channel.id not in TEST_CHANNELS:
  65                 self.tag_cooldowns[tag_name] = {
  66                     "time": time.time(),
  67                     "channel": ctx.channel.id
  68                 }
  69             await ctx.send(embed=Embed.from_dict(tag['embed']))
  70 
  71         else:
  72             tags = await self.bot.api_client.get('bot/tags')
  73             if not tags:
  74                 await ctx.send(embed=Embed(
  75                     description="**There are no tags in the database!**",
  76                     colour=Colour.red()
  77                 ))
  78             else:
  79                 embed: Embed = Embed(title="**Current tags**")
  80                 await LinePaginator.paginate(
  81                     sorted(f"**»**   {tag['title']}" for tag in tags),
  82                     ctx,
  83                     embed,
  84                     footer_text="To show a tag, type !tags <tagname>.",
  85                     empty=False,
  86                     max_lines=15
  87                 )
  88 
  89     @tags_group.command(name='set', aliases=('add', 'edit', 's'))
  90     @with_role(*MODERATION_ROLES)
  91     async def set_command(
  92         self,
  93         ctx: Context,
  94         tag_name: TagNameConverter,
  95         *,
  96         tag_content: TagContentConverter,
  97     ) -> None:
  98         """Create a new tag or update an existing one."""
  99         body = {
  100             'title': tag_name.lower().strip(),
  101             'embed': {
  102                 'title': tag_name,
  103                 'description': tag_content
  104             }
  105         }
  106 
  107         await self.bot.api_client.post('bot/tags', json=body)
  108 
  109         log.debug(f"{ctx.author} successfully added the following tag to our database: \n"
  110                   f"tag_name: {tag_name}\n"
  111                   f"tag_content: '{tag_content}'\n")
  112 
  113         await ctx.send(embed=Embed(
  114             title="Tag successfully added",
  115             description=f"**{tag_name}** added to tag database.",
  116             colour=Colour.blurple()
  117         ))
  118 
  119     @tags_group.command(name='delete', aliases=('remove', 'rm', 'd'))
  120     @with_role(Roles.admin, Roles.owner)
  121     async def delete_command(self, ctx: Context, *, tag_name: TagNameConverter) -> None:
  122         """Remove a tag from the database."""
  123         await self.bot.api_client.delete(f'bot/tags/{tag_name}')
  124 
  125         log.debug(f"{ctx.author} successfully deleted the tag called '{tag_name}'")
  126         await ctx.send(embed=Embed(
  127             title=tag_name,
  128             description=f"Tag successfully removed: {tag_name}.",
  129             colour=Colour.blurple()
  130         ))
  131 
  132 
  133 def setup(bot: Bot) -> None:
  134     """Tags cog load."""
  135     bot.add_cog(Tags(bot))
  136     log.info("Cog loaded: Tags")
  137
  ```
  --- 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()
  
  ```
  
golden_diff: diff --git a/bot/cogs/tags.py b/bot/cogs/tags.py
  --- a/bot/cogs/tags.py
  +++ b/bot/cogs/tags.py
  @@ -86,7 +86,7 @@
                       max_lines=15
                   )
   
  -    @tags_group.command(name='set', aliases=('add', 'edit', 's'))
  +    @tags_group.command(name='set', aliases=('add', 's'))
       @with_role(*MODERATION_ROLES)
       async def set_command(
           self,
  @@ -95,7 +95,7 @@
           *,
           tag_content: TagContentConverter,
       ) -> None:
  -        """Create a new tag or update an existing one."""
  +        """Create a new tag."""
           body = {
               'title': tag_name.lower().strip(),
               'embed': {
  @@ -116,6 +116,35 @@
               colour=Colour.blurple()
           ))
   
  +    @tags_group.command(name='edit', aliases=('e', ))
  +    @with_role(*MODERATION_ROLES)
  +    async def edit_command(
  +        self,
  +        ctx: Context,
  +        tag_name: TagNameConverter,
  +        *,
  +        tag_content: TagContentConverter,
  +    ) -> None:
  +        """Edit an existing tag."""
  +        body = {
  +            'embed': {
  +                'title': tag_name,
  +                'description': tag_content
  +            }
  +        }
  +
  +        await self.bot.api_client.patch(f'bot/tags/{tag_name}', json=body)
  +
  +        log.debug(f"{ctx.author} successfully edited the following tag in our database: \n"
  +                  f"tag_name: {tag_name}\n"
  +                  f"tag_content: '{tag_content}'\n")
  +
  +        await ctx.send(embed=Embed(
  +            title="Tag successfully edited",
  +            description=f"**{tag_name}** edited in the database.",
  +            colour=Colour.blurple()
  +        ))
  +
       @tags_group.command(name='delete', aliases=('remove', 'rm', 'd'))
       @with_role(Roles.admin, Roles.owner)
       async def delete_command(self, ctx: Context, *, tag_name: TagNameConverter) -> None:
  
verification_info: {"golden_diff": "diff --git a/bot/cogs/tags.py b/bot/cogs/tags.py\n--- a/bot/cogs/tags.py\n+++ b/bot/cogs/tags.py\n@@ -86,7 +86,7 @@\n                     max_lines=15\n                 )\n \n-    @tags_group.command(name='set', aliases=('add', 'edit', 's'))\n+    @tags_group.command(name='set', aliases=('add', 's'))\n     @with_role(*MODERATION_ROLES)\n     async def set_command(\n         self,\n@@ -95,7 +95,7 @@\n         *,\n         tag_content: TagContentConverter,\n     ) -> None:\n-        \"\"\"Create a new tag or update an existing one.\"\"\"\n+        \"\"\"Create a new tag.\"\"\"\n         body = {\n             'title': tag_name.lower().strip(),\n             'embed': {\n@@ -116,6 +116,35 @@\n             colour=Colour.blurple()\n         ))\n \n+    @tags_group.command(name='edit', aliases=('e', ))\n+    @with_role(*MODERATION_ROLES)\n+    async def edit_command(\n+        self,\n+        ctx: Context,\n+        tag_name: TagNameConverter,\n+        *,\n+        tag_content: TagContentConverter,\n+    ) -> None:\n+        \"\"\"Edit an existing tag.\"\"\"\n+        body = {\n+            'embed': {\n+                'title': tag_name,\n+                'description': tag_content\n+            }\n+        }\n+\n+        await self.bot.api_client.patch(f'bot/tags/{tag_name}', json=body)\n+\n+        log.debug(f\"{ctx.author} successfully edited the following tag in our database: \\n\"\n+                  f\"tag_name: {tag_name}\\n\"\n+                  f\"tag_content: '{tag_content}'\\n\")\n+\n+        await ctx.send(embed=Embed(\n+            title=\"Tag successfully edited\",\n+            description=f\"**{tag_name}** edited in the database.\",\n+            colour=Colour.blurple()\n+        ))\n+\n     @tags_group.command(name='delete', aliases=('remove', 'rm', 'd'))\n     @with_role(Roles.admin, Roles.owner)\n     async def delete_command(self, ctx: Context, *, tag_name: TagNameConverter) -> None:\n", "issue": "Tags can't be edited due to using the POST method with the API\nCurrently, the `!tag edit` subcommand is just an alias of `!tag set`. This means that if we try to edit an existing tag, the bot will use the POST http method to communicate with the API. Since we're not posting a new tag, but editing an existing entry, the API will reject this request. \r\n\r\nInstead of using POST, we should be using PATCH, since we're only partially updating the entry in the database.\n", "before_files": [{"content": "import logging\nimport time\n\nfrom discord import Colour, Embed\nfrom discord.ext.commands import Bot, Cog, Context, group\n\nfrom bot.constants import Channels, Cooldowns, MODERATION_ROLES, Roles\nfrom bot.converters import TagContentConverter, TagNameConverter\nfrom bot.decorators import with_role\nfrom bot.pagination import LinePaginator\n\n\nlog = logging.getLogger(__name__)\n\nTEST_CHANNELS = (\n    Channels.devtest,\n    Channels.bot,\n    Channels.helpers\n)\n\n\nclass Tags(Cog):\n    \"\"\"Save new tags and fetch existing tags.\"\"\"\n\n    def __init__(self, bot: Bot):\n        self.bot = bot\n        self.tag_cooldowns = {}\n\n    @group(name='tags', aliases=('tag', 't'), invoke_without_command=True)\n    async def tags_group(self, ctx: Context, *, tag_name: TagNameConverter = None) -> None:\n        \"\"\"Show all known tags, a single tag, or run a subcommand.\"\"\"\n        await ctx.invoke(self.get_command, tag_name=tag_name)\n\n    @tags_group.command(name='get', aliases=('show', 'g'))\n    async def get_command(self, ctx: Context, *, tag_name: TagNameConverter = None) -> None:\n        \"\"\"Get a specified tag, or a list of all tags if no tag is specified.\"\"\"\n        def _command_on_cooldown(tag_name: str) -> bool:\n            \"\"\"\n            Check if the command is currently on cooldown, on a per-tag, per-channel basis.\n\n            The cooldown duration is set in constants.py.\n            \"\"\"\n            now = time.time()\n\n            cooldown_conditions = (\n                tag_name\n                and tag_name in self.tag_cooldowns\n                and (now - self.tag_cooldowns[tag_name][\"time\"]) < Cooldowns.tags\n                and self.tag_cooldowns[tag_name][\"channel\"] == ctx.channel.id\n            )\n\n            if cooldown_conditions:\n                return True\n            return False\n\n        if _command_on_cooldown(tag_name):\n            time_left = Cooldowns.tags - (time.time() - self.tag_cooldowns[tag_name][\"time\"])\n            log.warning(f\"{ctx.author} tried to get the '{tag_name}' tag, but the tag is on cooldown. \"\n                        f\"Cooldown ends in {time_left:.1f} seconds.\")\n            return\n\n        if tag_name is not None:\n            tag = await self.bot.api_client.get(f'bot/tags/{tag_name}')\n            if ctx.channel.id not in TEST_CHANNELS:\n                self.tag_cooldowns[tag_name] = {\n                    \"time\": time.time(),\n                    \"channel\": ctx.channel.id\n                }\n            await ctx.send(embed=Embed.from_dict(tag['embed']))\n\n        else:\n            tags = await self.bot.api_client.get('bot/tags')\n            if not tags:\n                await ctx.send(embed=Embed(\n                    description=\"**There are no tags in the database!**\",\n                    colour=Colour.red()\n                ))\n            else:\n                embed: Embed = Embed(title=\"**Current tags**\")\n                await LinePaginator.paginate(\n                    sorted(f\"**\u00bb**   {tag['title']}\" for tag in tags),\n                    ctx,\n                    embed,\n                    footer_text=\"To show a tag, type !tags <tagname>.\",\n                    empty=False,\n                    max_lines=15\n                )\n\n    @tags_group.command(name='set', aliases=('add', 'edit', 's'))\n    @with_role(*MODERATION_ROLES)\n    async def set_command(\n        self,\n        ctx: Context,\n        tag_name: TagNameConverter,\n        *,\n        tag_content: TagContentConverter,\n    ) -> None:\n        \"\"\"Create a new tag or update an existing one.\"\"\"\n        body = {\n            'title': tag_name.lower().strip(),\n            'embed': {\n                'title': tag_name,\n                'description': tag_content\n            }\n        }\n\n        await self.bot.api_client.post('bot/tags', json=body)\n\n        log.debug(f\"{ctx.author} successfully added the following tag to our database: \\n\"\n                  f\"tag_name: {tag_name}\\n\"\n                  f\"tag_content: '{tag_content}'\\n\")\n\n        await ctx.send(embed=Embed(\n            title=\"Tag successfully added\",\n            description=f\"**{tag_name}** added to tag database.\",\n            colour=Colour.blurple()\n        ))\n\n    @tags_group.command(name='delete', aliases=('remove', 'rm', 'd'))\n    @with_role(Roles.admin, Roles.owner)\n    async def delete_command(self, ctx: Context, *, tag_name: TagNameConverter) -> None:\n        \"\"\"Remove a tag from the database.\"\"\"\n        await self.bot.api_client.delete(f'bot/tags/{tag_name}')\n\n        log.debug(f\"{ctx.author} successfully deleted the tag called '{tag_name}'\")\n        await ctx.send(embed=Embed(\n            title=tag_name,\n            description=f\"Tag successfully removed: {tag_name}.\",\n            colour=Colour.blurple()\n        ))\n\n\ndef setup(bot: Bot) -> None:\n    \"\"\"Tags cog load.\"\"\"\n    bot.add_cog(Tags(bot))\n    log.info(\"Cog loaded: Tags\")\n", "path": "bot/cogs/tags.py"}], "after_files": [{"content": "import logging\nimport time\n\nfrom discord import Colour, Embed\nfrom discord.ext.commands import Bot, Cog, Context, group\n\nfrom bot.constants import Channels, Cooldowns, MODERATION_ROLES, Roles\nfrom bot.converters import TagContentConverter, TagNameConverter\nfrom bot.decorators import with_role\nfrom bot.pagination import LinePaginator\n\n\nlog = logging.getLogger(__name__)\n\nTEST_CHANNELS = (\n    Channels.devtest,\n    Channels.bot,\n    Channels.helpers\n)\n\n\nclass Tags(Cog):\n    \"\"\"Save new tags and fetch existing tags.\"\"\"\n\n    def __init__(self, bot: Bot):\n        self.bot = bot\n        self.tag_cooldowns = {}\n\n    @group(name='tags', aliases=('tag', 't'), invoke_without_command=True)\n    async def tags_group(self, ctx: Context, *, tag_name: TagNameConverter = None) -> None:\n        \"\"\"Show all known tags, a single tag, or run a subcommand.\"\"\"\n        await ctx.invoke(self.get_command, tag_name=tag_name)\n\n    @tags_group.command(name='get', aliases=('show', 'g'))\n    async def get_command(self, ctx: Context, *, tag_name: TagNameConverter = None) -> None:\n        \"\"\"Get a specified tag, or a list of all tags if no tag is specified.\"\"\"\n        def _command_on_cooldown(tag_name: str) -> bool:\n            \"\"\"\n            Check if the command is currently on cooldown, on a per-tag, per-channel basis.\n\n            The cooldown duration is set in constants.py.\n            \"\"\"\n            now = time.time()\n\n            cooldown_conditions = (\n                tag_name\n                and tag_name in self.tag_cooldowns\n                and (now - self.tag_cooldowns[tag_name][\"time\"]) < Cooldowns.tags\n                and self.tag_cooldowns[tag_name][\"channel\"] == ctx.channel.id\n            )\n\n            if cooldown_conditions:\n                return True\n            return False\n\n        if _command_on_cooldown(tag_name):\n            time_left = Cooldowns.tags - (time.time() - self.tag_cooldowns[tag_name][\"time\"])\n            log.warning(f\"{ctx.author} tried to get the '{tag_name}' tag, but the tag is on cooldown. \"\n                        f\"Cooldown ends in {time_left:.1f} seconds.\")\n            return\n\n        if tag_name is not None:\n            tag = await self.bot.api_client.get(f'bot/tags/{tag_name}')\n            if ctx.channel.id not in TEST_CHANNELS:\n                self.tag_cooldowns[tag_name] = {\n                    \"time\": time.time(),\n                    \"channel\": ctx.channel.id\n                }\n            await ctx.send(embed=Embed.from_dict(tag['embed']))\n\n        else:\n            tags = await self.bot.api_client.get('bot/tags')\n            if not tags:\n                await ctx.send(embed=Embed(\n                    description=\"**There are no tags in the database!**\",\n                    colour=Colour.red()\n                ))\n            else:\n                embed: Embed = Embed(title=\"**Current tags**\")\n                await LinePaginator.paginate(\n                    sorted(f\"**\u00bb**   {tag['title']}\" for tag in tags),\n                    ctx,\n                    embed,\n                    footer_text=\"To show a tag, type !tags <tagname>.\",\n                    empty=False,\n                    max_lines=15\n                )\n\n    @tags_group.command(name='set', aliases=('add', 's'))\n    @with_role(*MODERATION_ROLES)\n    async def set_command(\n        self,\n        ctx: Context,\n        tag_name: TagNameConverter,\n        *,\n        tag_content: TagContentConverter,\n    ) -> None:\n        \"\"\"Create a new tag.\"\"\"\n        body = {\n            'title': tag_name.lower().strip(),\n            'embed': {\n                'title': tag_name,\n                'description': tag_content\n            }\n        }\n\n        await self.bot.api_client.post('bot/tags', json=body)\n\n        log.debug(f\"{ctx.author} successfully added the following tag to our database: \\n\"\n                  f\"tag_name: {tag_name}\\n\"\n                  f\"tag_content: '{tag_content}'\\n\")\n\n        await ctx.send(embed=Embed(\n            title=\"Tag successfully added\",\n            description=f\"**{tag_name}** added to tag database.\",\n            colour=Colour.blurple()\n        ))\n\n    @tags_group.command(name='edit', aliases=('e', ))\n    @with_role(*MODERATION_ROLES)\n    async def edit_command(\n        self,\n        ctx: Context,\n        tag_name: TagNameConverter,\n        *,\n        tag_content: TagContentConverter,\n    ) -> None:\n        \"\"\"Edit an existing tag.\"\"\"\n        body = {\n            'embed': {\n                'title': tag_name,\n                'description': tag_content\n            }\n        }\n\n        await self.bot.api_client.patch(f'bot/tags/{tag_name}', json=body)\n\n        log.debug(f\"{ctx.author} successfully edited the following tag in our database: \\n\"\n                  f\"tag_name: {tag_name}\\n\"\n                  f\"tag_content: '{tag_content}'\\n\")\n\n        await ctx.send(embed=Embed(\n            title=\"Tag successfully edited\",\n            description=f\"**{tag_name}** edited in the database.\",\n            colour=Colour.blurple()\n        ))\n\n    @tags_group.command(name='delete', aliases=('remove', 'rm', 'd'))\n    @with_role(Roles.admin, Roles.owner)\n    async def delete_command(self, ctx: Context, *, tag_name: TagNameConverter) -> None:\n        \"\"\"Remove a tag from the database.\"\"\"\n        await self.bot.api_client.delete(f'bot/tags/{tag_name}')\n\n        log.debug(f\"{ctx.author} successfully deleted the tag called '{tag_name}'\")\n        await ctx.send(embed=Embed(\n            title=tag_name,\n            description=f\"Tag successfully removed: {tag_name}.\",\n            colour=Colour.blurple()\n        ))\n\n\ndef setup(bot: Bot) -> None:\n    \"\"\"Tags cog load.\"\"\"\n    bot.add_cog(Tags(bot))\n    log.info(\"Cog loaded: Tags\")\n", "path": "bot/cogs/tags.py"}]}