We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Reminder details in confirmation message.
The reminder command currently only replies with `Your reminder has been created successfully!`. 

This can be improved by providing the actual reminder contents in the confirmation and the datetime / humanized timedelta the reminder will be due to ensure the intended duration was processed. 

For the message sent once the reminder is due, a jump url to the original command message so we can jump back into context of the conversation when the reminder was made would be good to add. (Previously had mixed up wording for this section).

This would do well as a first issue if it's not gotten to in short time.

--- 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/reminders.py`
Content:
```
1 import asyncio
2 import logging
3 import random
4 import textwrap
5 from datetime import datetime
6 from operator import itemgetter
7 from typing import Optional
8 
9 from dateutil.relativedelta import relativedelta
10 from discord import Colour, Embed, Message
11 from discord.ext.commands import Bot, Cog, Context, group
12 
13 from bot.constants import Channels, Icons, NEGATIVE_REPLIES, POSITIVE_REPLIES, STAFF_ROLES
14 from bot.converters import Duration
15 from bot.pagination import LinePaginator
16 from bot.utils.checks import without_role_check
17 from bot.utils.scheduling import Scheduler
18 from bot.utils.time import humanize_delta, wait_until
19 
20 log = logging.getLogger(__name__)
21 
22 WHITELISTED_CHANNELS = (Channels.bot,)
23 MAXIMUM_REMINDERS = 5
24 
25 
26 class Reminders(Scheduler, Cog):
27     """Provide in-channel reminder functionality."""
28 
29     def __init__(self, bot: Bot):
30         self.bot = bot
31         super().__init__()
32 
33         self.bot.loop.create_task(self.reschedule_reminders())
34 
35     async def reschedule_reminders(self) -> None:
36         """Get all current reminders from the API and reschedule them."""
37         await self.bot.wait_until_ready()
38         response = await self.bot.api_client.get(
39             'bot/reminders',
40             params={'active': 'true'}
41         )
42 
43         now = datetime.utcnow()
44         loop = asyncio.get_event_loop()
45 
46         for reminder in response:
47             remind_at = datetime.fromisoformat(reminder['expiration'][:-1])
48 
49             # If the reminder is already overdue ...
50             if remind_at < now:
51                 late = relativedelta(now, remind_at)
52                 await self.send_reminder(reminder, late)
53 
54             else:
55                 self.schedule_task(loop, reminder["id"], reminder)
56 
57     @staticmethod
58     async def _send_confirmation(ctx: Context, on_success: str) -> None:
59         """Send an embed confirming the reminder change was made successfully."""
60         embed = Embed()
61         embed.colour = Colour.green()
62         embed.title = random.choice(POSITIVE_REPLIES)
63         embed.description = on_success
64         await ctx.send(embed=embed)
65 
66     async def _scheduled_task(self, reminder: dict) -> None:
67         """A coroutine which sends the reminder once the time is reached, and cancels the running task."""
68         reminder_id = reminder["id"]
69         reminder_datetime = datetime.fromisoformat(reminder['expiration'][:-1])
70 
71         # Send the reminder message once the desired duration has passed
72         await wait_until(reminder_datetime)
73         await self.send_reminder(reminder)
74 
75         log.debug(f"Deleting reminder {reminder_id} (the user has been reminded).")
76         await self._delete_reminder(reminder_id)
77 
78         # Now we can begone with it from our schedule list.
79         self.cancel_task(reminder_id)
80 
81     async def _delete_reminder(self, reminder_id: str) -> None:
82         """Delete a reminder from the database, given its ID, and cancel the running task."""
83         await self.bot.api_client.delete('bot/reminders/' + str(reminder_id))
84 
85         # Now we can remove it from the schedule list
86         self.cancel_task(reminder_id)
87 
88     async def _reschedule_reminder(self, reminder: dict) -> None:
89         """Reschedule a reminder object."""
90         loop = asyncio.get_event_loop()
91 
92         self.cancel_task(reminder["id"])
93         self.schedule_task(loop, reminder["id"], reminder)
94 
95     async def send_reminder(self, reminder: dict, late: relativedelta = None) -> None:
96         """Send the reminder."""
97         channel = self.bot.get_channel(reminder["channel_id"])
98         user = self.bot.get_user(reminder["author"])
99 
100         embed = Embed()
101         embed.colour = Colour.blurple()
102         embed.set_author(
103             icon_url=Icons.remind_blurple,
104             name="It has arrived!"
105         )
106 
107         embed.description = f"Here's your reminder: `{reminder['content']}`"
108 
109         if late:
110             embed.colour = Colour.red()
111             embed.set_author(
112                 icon_url=Icons.remind_red,
113                 name=f"Sorry it arrived {humanize_delta(late, max_units=2)} late!"
114             )
115 
116         await channel.send(
117             content=user.mention,
118             embed=embed
119         )
120         await self._delete_reminder(reminder["id"])
121 
122     @group(name="remind", aliases=("reminder", "reminders"), invoke_without_command=True)
123     async def remind_group(self, ctx: Context, expiration: Duration, *, content: str) -> None:
124         """Commands for managing your reminders."""
125         await ctx.invoke(self.new_reminder, expiration=expiration, content=content)
126 
127     @remind_group.command(name="new", aliases=("add", "create"))
128     async def new_reminder(self, ctx: Context, expiration: Duration, *, content: str) -> Optional[Message]:
129         """
130         Set yourself a simple reminder.
131 
132         Expiration is parsed per: http://strftime.org/
133         """
134         embed = Embed()
135 
136         # If the user is not staff, we need to verify whether or not to make a reminder at all.
137         if without_role_check(ctx, *STAFF_ROLES):
138 
139             # If they don't have permission to set a reminder in this channel
140             if ctx.channel.id not in WHITELISTED_CHANNELS:
141                 embed.colour = Colour.red()
142                 embed.title = random.choice(NEGATIVE_REPLIES)
143                 embed.description = "Sorry, you can't do that here!"
144 
145                 return await ctx.send(embed=embed)
146 
147             # Get their current active reminders
148             active_reminders = await self.bot.api_client.get(
149                 'bot/reminders',
150                 params={
151                     'author__id': str(ctx.author.id)
152                 }
153             )
154 
155             # Let's limit this, so we don't get 10 000
156             # reminders from kip or something like that :P
157             if len(active_reminders) > MAXIMUM_REMINDERS:
158                 embed.colour = Colour.red()
159                 embed.title = random.choice(NEGATIVE_REPLIES)
160                 embed.description = "You have too many active reminders!"
161 
162                 return await ctx.send(embed=embed)
163 
164         # Now we can attempt to actually set the reminder.
165         reminder = await self.bot.api_client.post(
166             'bot/reminders',
167             json={
168                 'author': ctx.author.id,
169                 'channel_id': ctx.message.channel.id,
170                 'content': content,
171                 'expiration': expiration.isoformat()
172             }
173         )
174 
175         # Confirm to the user that it worked.
176         await self._send_confirmation(
177             ctx, on_success="Your reminder has been created successfully!"
178         )
179 
180         loop = asyncio.get_event_loop()
181         self.schedule_task(loop, reminder["id"], reminder)
182 
183     @remind_group.command(name="list")
184     async def list_reminders(self, ctx: Context) -> Optional[Message]:
185         """View a paginated embed of all reminders for your user."""
186         # Get all the user's reminders from the database.
187         data = await self.bot.api_client.get(
188             'bot/reminders',
189             params={'author__id': str(ctx.author.id)}
190         )
191 
192         now = datetime.utcnow()
193 
194         # Make a list of tuples so it can be sorted by time.
195         reminders = sorted(
196             (
197                 (rem['content'], rem['expiration'], rem['id'])
198                 for rem in data
199             ),
200             key=itemgetter(1)
201         )
202 
203         lines = []
204 
205         for content, remind_at, id_ in reminders:
206             # Parse and humanize the time, make it pretty :D
207             remind_datetime = datetime.fromisoformat(remind_at[:-1])
208             time = humanize_delta(relativedelta(remind_datetime, now))
209 
210             text = textwrap.dedent(f"""
211             **Reminder #{id_}:** *expires in {time}* (ID: {id_})
212             {content}
213             """).strip()
214 
215             lines.append(text)
216 
217         embed = Embed()
218         embed.colour = Colour.blurple()
219         embed.title = f"Reminders for {ctx.author}"
220 
221         # Remind the user that they have no reminders :^)
222         if not lines:
223             embed.description = "No active reminders could be found."
224             return await ctx.send(embed=embed)
225 
226         # Construct the embed and paginate it.
227         embed.colour = Colour.blurple()
228 
229         await LinePaginator.paginate(
230             lines,
231             ctx, embed,
232             max_lines=3,
233             empty=True
234         )
235 
236     @remind_group.group(name="edit", aliases=("change", "modify"), invoke_without_command=True)
237     async def edit_reminder_group(self, ctx: Context) -> None:
238         """Commands for modifying your current reminders."""
239         await ctx.invoke(self.bot.get_command("help"), "reminders", "edit")
240 
241     @edit_reminder_group.command(name="duration", aliases=("time",))
242     async def edit_reminder_duration(self, ctx: Context, id_: int, expiration: Duration) -> None:
243         """
244          Edit one of your reminder's expiration.
245 
246         Expiration is parsed per: http://strftime.org/
247         """
248         # Send the request to update the reminder in the database
249         reminder = await self.bot.api_client.patch(
250             'bot/reminders/' + str(id_),
251             json={'expiration': expiration.isoformat()}
252         )
253 
254         # Send a confirmation message to the channel
255         await self._send_confirmation(
256             ctx, on_success="That reminder has been edited successfully!"
257         )
258 
259         await self._reschedule_reminder(reminder)
260 
261     @edit_reminder_group.command(name="content", aliases=("reason",))
262     async def edit_reminder_content(self, ctx: Context, id_: int, *, content: str) -> None:
263         """Edit one of your reminder's content."""
264         # Send the request to update the reminder in the database
265         reminder = await self.bot.api_client.patch(
266             'bot/reminders/' + str(id_),
267             json={'content': content}
268         )
269 
270         # Send a confirmation message to the channel
271         await self._send_confirmation(
272             ctx, on_success="That reminder has been edited successfully!"
273         )
274         await self._reschedule_reminder(reminder)
275 
276     @remind_group.command("delete", aliases=("remove",))
277     async def delete_reminder(self, ctx: Context, id_: int) -> None:
278         """Delete one of your active reminders."""
279         await self._delete_reminder(id_)
280         await self._send_confirmation(
281             ctx, on_success="That reminder has been deleted successfully!"
282         )
283 
284 
285 def setup(bot: Bot) -> None:
286     """Reminders cog load."""
287     bot.add_cog(Reminders(bot))
288     log.info("Cog loaded: Reminders")
289
```
--- 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/reminders.py b/bot/cogs/reminders.py
--- a/bot/cogs/reminders.py
+++ b/bot/cogs/reminders.py
@@ -2,7 +2,7 @@
 import logging
 import random
 import textwrap
-from datetime import datetime
+from datetime import datetime, timedelta
 from operator import itemgetter
 from typing import Optional
 
@@ -104,7 +104,10 @@
             name="It has arrived!"
         )
 
-        embed.description = f"Here's your reminder: `{reminder['content']}`"
+        embed.description = f"Here's your reminder: `{reminder['content']}`."
+
+        if reminder.get("jump_url"):  # keep backward compatibility
+            embed.description += f"\n[Jump back to when you created the reminder]({reminder['jump_url']})"
 
         if late:
             embed.colour = Colour.red()
@@ -167,14 +170,18 @@
             json={
                 'author': ctx.author.id,
                 'channel_id': ctx.message.channel.id,
+                'jump_url': ctx.message.jump_url,
                 'content': content,
                 'expiration': expiration.isoformat()
             }
         )
 
+        now = datetime.utcnow() - timedelta(seconds=1)
+
         # Confirm to the user that it worked.
         await self._send_confirmation(
-            ctx, on_success="Your reminder has been created successfully!"
+            ctx,
+            on_success=f"Your reminder will arrive in {humanize_delta(relativedelta(expiration, now))}!"
         )
 
         loop = asyncio.get_event_loop()

{"golden_diff": "diff --git a/bot/cogs/reminders.py b/bot/cogs/reminders.py\n--- a/bot/cogs/reminders.py\n+++ b/bot/cogs/reminders.py\n@@ -2,7 +2,7 @@\n import logging\n import random\n import textwrap\n-from datetime import datetime\n+from datetime import datetime, timedelta\n from operator import itemgetter\n from typing import Optional\n \n@@ -104,7 +104,10 @@\n             name=\"It has arrived!\"\n         )\n \n-        embed.description = f\"Here's your reminder: `{reminder['content']}`\"\n+        embed.description = f\"Here's your reminder: `{reminder['content']}`.\"\n+\n+        if reminder.get(\"jump_url\"):  # keep backward compatibility\n+            embed.description += f\"\\n[Jump back to when you created the reminder]({reminder['jump_url']})\"\n \n         if late:\n             embed.colour = Colour.red()\n@@ -167,14 +170,18 @@\n             json={\n                 'author': ctx.author.id,\n                 'channel_id': ctx.message.channel.id,\n+                'jump_url': ctx.message.jump_url,\n                 'content': content,\n                 'expiration': expiration.isoformat()\n             }\n         )\n \n+        now = datetime.utcnow() - timedelta(seconds=1)\n+\n         # Confirm to the user that it worked.\n         await self._send_confirmation(\n-            ctx, on_success=\"Your reminder has been created successfully!\"\n+            ctx,\n+            on_success=f\"Your reminder will arrive in {humanize_delta(relativedelta(expiration, now))}!\"\n         )\n \n         loop = asyncio.get_event_loop()\n", "issue": "Reminder details in confirmation message.\nThe reminder command currently only replies with `Your reminder has been created successfully!`. \r\n\r\nThis can be improved by providing the actual reminder contents in the confirmation and the datetime / humanized timedelta the reminder will be due to ensure the intended duration was processed. \r\n\r\nFor the message sent once the reminder is due, a jump url to the original command message so we can jump back into context of the conversation when the reminder was made would be good to add. (Previously had mixed up wording for this section).\r\n\r\nThis would do well as a first issue if it's not gotten to in short time.\n", "before_files": [{"content": "import asyncio\nimport logging\nimport random\nimport textwrap\nfrom datetime import datetime\nfrom operator import itemgetter\nfrom typing import Optional\n\nfrom dateutil.relativedelta import relativedelta\nfrom discord import Colour, Embed, Message\nfrom discord.ext.commands import Bot, Cog, Context, group\n\nfrom bot.constants import Channels, Icons, NEGATIVE_REPLIES, POSITIVE_REPLIES, STAFF_ROLES\nfrom bot.converters import Duration\nfrom bot.pagination import LinePaginator\nfrom bot.utils.checks import without_role_check\nfrom bot.utils.scheduling import Scheduler\nfrom bot.utils.time import humanize_delta, wait_until\n\nlog = logging.getLogger(__name__)\n\nWHITELISTED_CHANNELS = (Channels.bot,)\nMAXIMUM_REMINDERS = 5\n\n\nclass Reminders(Scheduler, Cog):\n    \"\"\"Provide in-channel reminder functionality.\"\"\"\n\n    def __init__(self, bot: Bot):\n        self.bot = bot\n        super().__init__()\n\n        self.bot.loop.create_task(self.reschedule_reminders())\n\n    async def reschedule_reminders(self) -> None:\n        \"\"\"Get all current reminders from the API and reschedule them.\"\"\"\n        await self.bot.wait_until_ready()\n        response = await self.bot.api_client.get(\n            'bot/reminders',\n            params={'active': 'true'}\n        )\n\n        now = datetime.utcnow()\n        loop = asyncio.get_event_loop()\n\n        for reminder in response:\n            remind_at = datetime.fromisoformat(reminder['expiration'][:-1])\n\n            # If the reminder is already overdue ...\n            if remind_at < now:\n                late = relativedelta(now, remind_at)\n                await self.send_reminder(reminder, late)\n\n            else:\n                self.schedule_task(loop, reminder[\"id\"], reminder)\n\n    @staticmethod\n    async def _send_confirmation(ctx: Context, on_success: str) -> None:\n        \"\"\"Send an embed confirming the reminder change was made successfully.\"\"\"\n        embed = Embed()\n        embed.colour = Colour.green()\n        embed.title = random.choice(POSITIVE_REPLIES)\n        embed.description = on_success\n        await ctx.send(embed=embed)\n\n    async def _scheduled_task(self, reminder: dict) -> None:\n        \"\"\"A coroutine which sends the reminder once the time is reached, and cancels the running task.\"\"\"\n        reminder_id = reminder[\"id\"]\n        reminder_datetime = datetime.fromisoformat(reminder['expiration'][:-1])\n\n        # Send the reminder message once the desired duration has passed\n        await wait_until(reminder_datetime)\n        await self.send_reminder(reminder)\n\n        log.debug(f\"Deleting reminder {reminder_id} (the user has been reminded).\")\n        await self._delete_reminder(reminder_id)\n\n        # Now we can begone with it from our schedule list.\n        self.cancel_task(reminder_id)\n\n    async def _delete_reminder(self, reminder_id: str) -> None:\n        \"\"\"Delete a reminder from the database, given its ID, and cancel the running task.\"\"\"\n        await self.bot.api_client.delete('bot/reminders/' + str(reminder_id))\n\n        # Now we can remove it from the schedule list\n        self.cancel_task(reminder_id)\n\n    async def _reschedule_reminder(self, reminder: dict) -> None:\n        \"\"\"Reschedule a reminder object.\"\"\"\n        loop = asyncio.get_event_loop()\n\n        self.cancel_task(reminder[\"id\"])\n        self.schedule_task(loop, reminder[\"id\"], reminder)\n\n    async def send_reminder(self, reminder: dict, late: relativedelta = None) -> None:\n        \"\"\"Send the reminder.\"\"\"\n        channel = self.bot.get_channel(reminder[\"channel_id\"])\n        user = self.bot.get_user(reminder[\"author\"])\n\n        embed = Embed()\n        embed.colour = Colour.blurple()\n        embed.set_author(\n            icon_url=Icons.remind_blurple,\n            name=\"It has arrived!\"\n        )\n\n        embed.description = f\"Here's your reminder: `{reminder['content']}`\"\n\n        if late:\n            embed.colour = Colour.red()\n            embed.set_author(\n                icon_url=Icons.remind_red,\n                name=f\"Sorry it arrived {humanize_delta(late, max_units=2)} late!\"\n            )\n\n        await channel.send(\n            content=user.mention,\n            embed=embed\n        )\n        await self._delete_reminder(reminder[\"id\"])\n\n    @group(name=\"remind\", aliases=(\"reminder\", \"reminders\"), invoke_without_command=True)\n    async def remind_group(self, ctx: Context, expiration: Duration, *, content: str) -> None:\n        \"\"\"Commands for managing your reminders.\"\"\"\n        await ctx.invoke(self.new_reminder, expiration=expiration, content=content)\n\n    @remind_group.command(name=\"new\", aliases=(\"add\", \"create\"))\n    async def new_reminder(self, ctx: Context, expiration: Duration, *, content: str) -> Optional[Message]:\n        \"\"\"\n        Set yourself a simple reminder.\n\n        Expiration is parsed per: http://strftime.org/\n        \"\"\"\n        embed = Embed()\n\n        # If the user is not staff, we need to verify whether or not to make a reminder at all.\n        if without_role_check(ctx, *STAFF_ROLES):\n\n            # If they don't have permission to set a reminder in this channel\n            if ctx.channel.id not in WHITELISTED_CHANNELS:\n                embed.colour = Colour.red()\n                embed.title = random.choice(NEGATIVE_REPLIES)\n                embed.description = \"Sorry, you can't do that here!\"\n\n                return await ctx.send(embed=embed)\n\n            # Get their current active reminders\n            active_reminders = await self.bot.api_client.get(\n                'bot/reminders',\n                params={\n                    'author__id': str(ctx.author.id)\n                }\n            )\n\n            # Let's limit this, so we don't get 10 000\n            # reminders from kip or something like that :P\n            if len(active_reminders) > MAXIMUM_REMINDERS:\n                embed.colour = Colour.red()\n                embed.title = random.choice(NEGATIVE_REPLIES)\n                embed.description = \"You have too many active reminders!\"\n\n                return await ctx.send(embed=embed)\n\n        # Now we can attempt to actually set the reminder.\n        reminder = await self.bot.api_client.post(\n            'bot/reminders',\n            json={\n                'author': ctx.author.id,\n                'channel_id': ctx.message.channel.id,\n                'content': content,\n                'expiration': expiration.isoformat()\n            }\n        )\n\n        # Confirm to the user that it worked.\n        await self._send_confirmation(\n            ctx, on_success=\"Your reminder has been created successfully!\"\n        )\n\n        loop = asyncio.get_event_loop()\n        self.schedule_task(loop, reminder[\"id\"], reminder)\n\n    @remind_group.command(name=\"list\")\n    async def list_reminders(self, ctx: Context) -> Optional[Message]:\n        \"\"\"View a paginated embed of all reminders for your user.\"\"\"\n        # Get all the user's reminders from the database.\n        data = await self.bot.api_client.get(\n            'bot/reminders',\n            params={'author__id': str(ctx.author.id)}\n        )\n\n        now = datetime.utcnow()\n\n        # Make a list of tuples so it can be sorted by time.\n        reminders = sorted(\n            (\n                (rem['content'], rem['expiration'], rem['id'])\n                for rem in data\n            ),\n            key=itemgetter(1)\n        )\n\n        lines = []\n\n        for content, remind_at, id_ in reminders:\n            # Parse and humanize the time, make it pretty :D\n            remind_datetime = datetime.fromisoformat(remind_at[:-1])\n            time = humanize_delta(relativedelta(remind_datetime, now))\n\n            text = textwrap.dedent(f\"\"\"\n            **Reminder #{id_}:** *expires in {time}* (ID: {id_})\n            {content}\n            \"\"\").strip()\n\n            lines.append(text)\n\n        embed = Embed()\n        embed.colour = Colour.blurple()\n        embed.title = f\"Reminders for {ctx.author}\"\n\n        # Remind the user that they have no reminders :^)\n        if not lines:\n            embed.description = \"No active reminders could be found.\"\n            return await ctx.send(embed=embed)\n\n        # Construct the embed and paginate it.\n        embed.colour = Colour.blurple()\n\n        await LinePaginator.paginate(\n            lines,\n            ctx, embed,\n            max_lines=3,\n            empty=True\n        )\n\n    @remind_group.group(name=\"edit\", aliases=(\"change\", \"modify\"), invoke_without_command=True)\n    async def edit_reminder_group(self, ctx: Context) -> None:\n        \"\"\"Commands for modifying your current reminders.\"\"\"\n        await ctx.invoke(self.bot.get_command(\"help\"), \"reminders\", \"edit\")\n\n    @edit_reminder_group.command(name=\"duration\", aliases=(\"time\",))\n    async def edit_reminder_duration(self, ctx: Context, id_: int, expiration: Duration) -> None:\n        \"\"\"\n         Edit one of your reminder's expiration.\n\n        Expiration is parsed per: http://strftime.org/\n        \"\"\"\n        # Send the request to update the reminder in the database\n        reminder = await self.bot.api_client.patch(\n            'bot/reminders/' + str(id_),\n            json={'expiration': expiration.isoformat()}\n        )\n\n        # Send a confirmation message to the channel\n        await self._send_confirmation(\n            ctx, on_success=\"That reminder has been edited successfully!\"\n        )\n\n        await self._reschedule_reminder(reminder)\n\n    @edit_reminder_group.command(name=\"content\", aliases=(\"reason\",))\n    async def edit_reminder_content(self, ctx: Context, id_: int, *, content: str) -> None:\n        \"\"\"Edit one of your reminder's content.\"\"\"\n        # Send the request to update the reminder in the database\n        reminder = await self.bot.api_client.patch(\n            'bot/reminders/' + str(id_),\n            json={'content': content}\n        )\n\n        # Send a confirmation message to the channel\n        await self._send_confirmation(\n            ctx, on_success=\"That reminder has been edited successfully!\"\n        )\n        await self._reschedule_reminder(reminder)\n\n    @remind_group.command(\"delete\", aliases=(\"remove\",))\n    async def delete_reminder(self, ctx: Context, id_: int) -> None:\n        \"\"\"Delete one of your active reminders.\"\"\"\n        await self._delete_reminder(id_)\n        await self._send_confirmation(\n            ctx, on_success=\"That reminder has been deleted successfully!\"\n        )\n\n\ndef setup(bot: Bot) -> None:\n    \"\"\"Reminders cog load.\"\"\"\n    bot.add_cog(Reminders(bot))\n    log.info(\"Cog loaded: Reminders\")\n", "path": "bot/cogs/reminders.py"}], "after_files": [{"content": "import asyncio\nimport logging\nimport random\nimport textwrap\nfrom datetime import datetime, timedelta\nfrom operator import itemgetter\nfrom typing import Optional\n\nfrom dateutil.relativedelta import relativedelta\nfrom discord import Colour, Embed, Message\nfrom discord.ext.commands import Bot, Cog, Context, group\n\nfrom bot.constants import Channels, Icons, NEGATIVE_REPLIES, POSITIVE_REPLIES, STAFF_ROLES\nfrom bot.converters import Duration\nfrom bot.pagination import LinePaginator\nfrom bot.utils.checks import without_role_check\nfrom bot.utils.scheduling import Scheduler\nfrom bot.utils.time import humanize_delta, wait_until\n\nlog = logging.getLogger(__name__)\n\nWHITELISTED_CHANNELS = (Channels.bot,)\nMAXIMUM_REMINDERS = 5\n\n\nclass Reminders(Scheduler, Cog):\n    \"\"\"Provide in-channel reminder functionality.\"\"\"\n\n    def __init__(self, bot: Bot):\n        self.bot = bot\n        super().__init__()\n\n        self.bot.loop.create_task(self.reschedule_reminders())\n\n    async def reschedule_reminders(self) -> None:\n        \"\"\"Get all current reminders from the API and reschedule them.\"\"\"\n        await self.bot.wait_until_ready()\n        response = await self.bot.api_client.get(\n            'bot/reminders',\n            params={'active': 'true'}\n        )\n\n        now = datetime.utcnow()\n        loop = asyncio.get_event_loop()\n\n        for reminder in response:\n            remind_at = datetime.fromisoformat(reminder['expiration'][:-1])\n\n            # If the reminder is already overdue ...\n            if remind_at < now:\n                late = relativedelta(now, remind_at)\n                await self.send_reminder(reminder, late)\n\n            else:\n                self.schedule_task(loop, reminder[\"id\"], reminder)\n\n    @staticmethod\n    async def _send_confirmation(ctx: Context, on_success: str) -> None:\n        \"\"\"Send an embed confirming the reminder change was made successfully.\"\"\"\n        embed = Embed()\n        embed.colour = Colour.green()\n        embed.title = random.choice(POSITIVE_REPLIES)\n        embed.description = on_success\n        await ctx.send(embed=embed)\n\n    async def _scheduled_task(self, reminder: dict) -> None:\n        \"\"\"A coroutine which sends the reminder once the time is reached, and cancels the running task.\"\"\"\n        reminder_id = reminder[\"id\"]\n        reminder_datetime = datetime.fromisoformat(reminder['expiration'][:-1])\n\n        # Send the reminder message once the desired duration has passed\n        await wait_until(reminder_datetime)\n        await self.send_reminder(reminder)\n\n        log.debug(f\"Deleting reminder {reminder_id} (the user has been reminded).\")\n        await self._delete_reminder(reminder_id)\n\n        # Now we can begone with it from our schedule list.\n        self.cancel_task(reminder_id)\n\n    async def _delete_reminder(self, reminder_id: str) -> None:\n        \"\"\"Delete a reminder from the database, given its ID, and cancel the running task.\"\"\"\n        await self.bot.api_client.delete('bot/reminders/' + str(reminder_id))\n\n        # Now we can remove it from the schedule list\n        self.cancel_task(reminder_id)\n\n    async def _reschedule_reminder(self, reminder: dict) -> None:\n        \"\"\"Reschedule a reminder object.\"\"\"\n        loop = asyncio.get_event_loop()\n\n        self.cancel_task(reminder[\"id\"])\n        self.schedule_task(loop, reminder[\"id\"], reminder)\n\n    async def send_reminder(self, reminder: dict, late: relativedelta = None) -> None:\n        \"\"\"Send the reminder.\"\"\"\n        channel = self.bot.get_channel(reminder[\"channel_id\"])\n        user = self.bot.get_user(reminder[\"author\"])\n\n        embed = Embed()\n        embed.colour = Colour.blurple()\n        embed.set_author(\n            icon_url=Icons.remind_blurple,\n            name=\"It has arrived!\"\n        )\n\n        embed.description = f\"Here's your reminder: `{reminder['content']}`.\"\n\n        if reminder.get(\"jump_url\"):  # keep backward compatibility\n            embed.description += f\"\\n[Jump back to when you created the reminder]({reminder['jump_url']})\"\n\n        if late:\n            embed.colour = Colour.red()\n            embed.set_author(\n                icon_url=Icons.remind_red,\n                name=f\"Sorry it arrived {humanize_delta(late, max_units=2)} late!\"\n            )\n\n        await channel.send(\n            content=user.mention,\n            embed=embed\n        )\n        await self._delete_reminder(reminder[\"id\"])\n\n    @group(name=\"remind\", aliases=(\"reminder\", \"reminders\"), invoke_without_command=True)\n    async def remind_group(self, ctx: Context, expiration: Duration, *, content: str) -> None:\n        \"\"\"Commands for managing your reminders.\"\"\"\n        await ctx.invoke(self.new_reminder, expiration=expiration, content=content)\n\n    @remind_group.command(name=\"new\", aliases=(\"add\", \"create\"))\n    async def new_reminder(self, ctx: Context, expiration: Duration, *, content: str) -> Optional[Message]:\n        \"\"\"\n        Set yourself a simple reminder.\n\n        Expiration is parsed per: http://strftime.org/\n        \"\"\"\n        embed = Embed()\n\n        # If the user is not staff, we need to verify whether or not to make a reminder at all.\n        if without_role_check(ctx, *STAFF_ROLES):\n\n            # If they don't have permission to set a reminder in this channel\n            if ctx.channel.id not in WHITELISTED_CHANNELS:\n                embed.colour = Colour.red()\n                embed.title = random.choice(NEGATIVE_REPLIES)\n                embed.description = \"Sorry, you can't do that here!\"\n\n                return await ctx.send(embed=embed)\n\n            # Get their current active reminders\n            active_reminders = await self.bot.api_client.get(\n                'bot/reminders',\n                params={\n                    'author__id': str(ctx.author.id)\n                }\n            )\n\n            # Let's limit this, so we don't get 10 000\n            # reminders from kip or something like that :P\n            if len(active_reminders) > MAXIMUM_REMINDERS:\n                embed.colour = Colour.red()\n                embed.title = random.choice(NEGATIVE_REPLIES)\n                embed.description = \"You have too many active reminders!\"\n\n                return await ctx.send(embed=embed)\n\n        # Now we can attempt to actually set the reminder.\n        reminder = await self.bot.api_client.post(\n            'bot/reminders',\n            json={\n                'author': ctx.author.id,\n                'channel_id': ctx.message.channel.id,\n                'jump_url': ctx.message.jump_url,\n                'content': content,\n                'expiration': expiration.isoformat()\n            }\n        )\n\n        now = datetime.utcnow() - timedelta(seconds=1)\n\n        # Confirm to the user that it worked.\n        await self._send_confirmation(\n            ctx,\n            on_success=f\"Your reminder will arrive in {humanize_delta(relativedelta(expiration, now))}!\"\n        )\n\n        loop = asyncio.get_event_loop()\n        self.schedule_task(loop, reminder[\"id\"], reminder)\n\n    @remind_group.command(name=\"list\")\n    async def list_reminders(self, ctx: Context) -> Optional[Message]:\n        \"\"\"View a paginated embed of all reminders for your user.\"\"\"\n        # Get all the user's reminders from the database.\n        data = await self.bot.api_client.get(\n            'bot/reminders',\n            params={'author__id': str(ctx.author.id)}\n        )\n\n        now = datetime.utcnow()\n\n        # Make a list of tuples so it can be sorted by time.\n        reminders = sorted(\n            (\n                (rem['content'], rem['expiration'], rem['id'])\n                for rem in data\n            ),\n            key=itemgetter(1)\n        )\n\n        lines = []\n\n        for content, remind_at, id_ in reminders:\n            # Parse and humanize the time, make it pretty :D\n            remind_datetime = datetime.fromisoformat(remind_at[:-1])\n            time = humanize_delta(relativedelta(remind_datetime, now))\n\n            text = textwrap.dedent(f\"\"\"\n            **Reminder #{id_}:** *expires in {time}* (ID: {id_})\n            {content}\n            \"\"\").strip()\n\n            lines.append(text)\n\n        embed = Embed()\n        embed.colour = Colour.blurple()\n        embed.title = f\"Reminders for {ctx.author}\"\n\n        # Remind the user that they have no reminders :^)\n        if not lines:\n            embed.description = \"No active reminders could be found.\"\n            return await ctx.send(embed=embed)\n\n        # Construct the embed and paginate it.\n        embed.colour = Colour.blurple()\n\n        await LinePaginator.paginate(\n            lines,\n            ctx, embed,\n            max_lines=3,\n            empty=True\n        )\n\n    @remind_group.group(name=\"edit\", aliases=(\"change\", \"modify\"), invoke_without_command=True)\n    async def edit_reminder_group(self, ctx: Context) -> None:\n        \"\"\"Commands for modifying your current reminders.\"\"\"\n        await ctx.invoke(self.bot.get_command(\"help\"), \"reminders\", \"edit\")\n\n    @edit_reminder_group.command(name=\"duration\", aliases=(\"time\",))\n    async def edit_reminder_duration(self, ctx: Context, id_: int, expiration: Duration) -> None:\n        \"\"\"\n         Edit one of your reminder's expiration.\n\n        Expiration is parsed per: http://strftime.org/\n        \"\"\"\n        # Send the request to update the reminder in the database\n        reminder = await self.bot.api_client.patch(\n            'bot/reminders/' + str(id_),\n            json={'expiration': expiration.isoformat()}\n        )\n\n        # Send a confirmation message to the channel\n        await self._send_confirmation(\n            ctx, on_success=\"That reminder has been edited successfully!\"\n        )\n\n        await self._reschedule_reminder(reminder)\n\n    @edit_reminder_group.command(name=\"content\", aliases=(\"reason\",))\n    async def edit_reminder_content(self, ctx: Context, id_: int, *, content: str) -> None:\n        \"\"\"Edit one of your reminder's content.\"\"\"\n        # Send the request to update the reminder in the database\n        reminder = await self.bot.api_client.patch(\n            'bot/reminders/' + str(id_),\n            json={'content': content}\n        )\n\n        # Send a confirmation message to the channel\n        await self._send_confirmation(\n            ctx, on_success=\"That reminder has been edited successfully!\"\n        )\n        await self._reschedule_reminder(reminder)\n\n    @remind_group.command(\"delete\", aliases=(\"remove\",))\n    async def delete_reminder(self, ctx: Context, id_: int) -> None:\n        \"\"\"Delete one of your active reminders.\"\"\"\n        await self._delete_reminder(id_)\n        await self._send_confirmation(\n            ctx, on_success=\"That reminder has been deleted successfully!\"\n        )\n\n\ndef setup(bot: Bot) -> None:\n    \"\"\"Reminders cog load.\"\"\"\n    bot.add_cog(Reminders(bot))\n    log.info(\"Cog loaded: Reminders\")\n", "path": "bot/cogs/reminders.py"}]}