From cedf79a1cf2cee878205cfdd1ed5b7d93e6842a5 Mon Sep 17 00:00:00 2001 From: n0Oo0Oo0b Date: Sun, 13 Aug 2023 19:33:15 +0800 Subject: Create ModifyConfirmationView --- bot/exts/utils/reminders.py | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/bot/exts/utils/reminders.py b/bot/exts/utils/reminders.py index fcf291d1b..43d643e8e 100644 --- a/bot/exts/utils/reminders.py +++ b/bot/exts/utils/reminders.py @@ -6,6 +6,7 @@ from operator import itemgetter import discord from dateutil.parser import isoparse +from discord import Interaction from discord.ext.commands import Cog, Context, Greedy, group from pydis_core.site_api import ResponseCodeError from pydis_core.utils import scheduling @@ -37,11 +38,43 @@ log = get_logger(__name__) LOCK_NAMESPACE = "reminder" WHITELISTED_CHANNELS = Guild.reminder_whitelist MAXIMUM_REMINDERS = 5 +CONFIRMATION_TIMEOUT = 60 Mentionable = discord.Member | discord.Role ReminderMention = UnambiguousUser | discord.Role +class ModifyConfirmationView(discord.ui.View): + """A view to confirm modifying someone else's reminder.""" + + def __init__(self, author: discord.Member | discord.User): + super().__init__(timeout=CONFIRMATION_TIMEOUT) + self.author = author + self.result: bool | None = None + + async def interaction_check(self, interaction: Interaction) -> bool: + """Only allow interactions from the command invoker.""" + return interaction.user.id == self.author.id + + async def on_timeout(self) -> None: + """Default to not modifying if the user doesn't respond.""" + self.result = False + + @discord.ui.button(label="Confirm", style=discord.ButtonStyle.blurple, row=0) + async def confirm(self, interaction: Interaction, button: discord.ui.Button) -> None: + """Confirm the reminder modification.""" + await interaction.response.edit_message(view=None) + self.result = True + self.stop() + + @discord.ui.button(label="Cancel", row=0) + async def cancel(self, interaction: Interaction, button: discord.ui.Button) -> None: + """Cancel the reminder modification.""" + await interaction.response.edit_message(content="🚫 Operation canceled.", view=None) + self.result = False + self.stop() + + class Reminders(Cog): """Provide in-channel reminder functionality.""" -- cgit v1.2.3 From e82ae2885b8f493e2d233e19d994b4dcfb4b3069 Mon Sep 17 00:00:00 2001 From: n0Oo0Oo0b Date: Sun, 13 Aug 2023 19:36:25 +0800 Subject: Ask for confirmation when an admin modifies another user's reminders --- bot/exts/utils/reminders.py | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/bot/exts/utils/reminders.py b/bot/exts/utils/reminders.py index 43d643e8e..6853fff05 100644 --- a/bot/exts/utils/reminders.py +++ b/bot/exts/utils/reminders.py @@ -571,17 +571,30 @@ class Reminders(Cog): return False raise e - if await has_any_role_check(ctx, Roles.admins): + if api_response["author"] == ctx.author.id: + log.debug(f"{ctx.author} is the reminder author and passes the check.") return True - if api_response["author"] != ctx.author.id: - log.debug(f"{ctx.author} is not the reminder author and does not pass the check.") - if send_on_denial: - await send_denial(ctx, "You can't modify reminders of other users!") - return False + if await has_any_role_check(ctx, Roles.admins): + log.debug(f"{ctx.author} is an admin, asking for confirmation to modify the reminder.") - log.debug(f"{ctx.author} is the reminder author and passes the check.") - return True + confirmation_view = ModifyConfirmationView(ctx.author) + await ctx.send( + "Are you sure you want to modify someone else's reminder?", + view=confirmation_view, + ) + await confirmation_view.wait() + + if confirmation_view.result: + log.debug(f"{ctx.author} has confirmed reminder modification.") + else: + log.debug(f"{ctx.author} has cancelled reminder modification.") + return confirmation_view.result + + log.debug(f"{ctx.author} is not the reminder author and does not pass the check.") + if send_on_denial: + await send_denial(ctx, "You can't modify reminders of other users!") + return False async def setup(bot: Bot) -> None: -- cgit v1.2.3 From 30ff6480ecbbdbefbfc23262dc9615579fd942f5 Mon Sep 17 00:00:00 2001 From: n0Oo0Oo0b Date: Sun, 13 Aug 2023 19:36:57 +0800 Subject: Update docstring to better match function behavior --- bot/exts/utils/reminders.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/utils/reminders.py b/bot/exts/utils/reminders.py index 6853fff05..b1160fb80 100644 --- a/bot/exts/utils/reminders.py +++ b/bot/exts/utils/reminders.py @@ -560,7 +560,7 @@ class Reminders(Cog): """ Check whether the reminder can be modified by the ctx author. - The check passes when the user is an admin, or if they created the reminder. + The check passes if the user created the reminder, or if they are an admin (with confirmation). """ try: api_response = await self.bot.api_client.get(f"bot/reminders/{reminder_id}") -- cgit v1.2.3 From 3bf7e32c49cbf622e9eb5b62d908dc600927d66d Mon Sep 17 00:00:00 2001 From: Daniel Gu Date: Mon, 14 Aug 2023 00:23:13 +0800 Subject: Apply suggestions from code review Co-authored-by: Amrou Bellalouna --- bot/exts/utils/reminders.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/bot/exts/utils/reminders.py b/bot/exts/utils/reminders.py index b1160fb80..56df25923 100644 --- a/bot/exts/utils/reminders.py +++ b/bot/exts/utils/reminders.py @@ -38,14 +38,14 @@ log = get_logger(__name__) LOCK_NAMESPACE = "reminder" WHITELISTED_CHANNELS = Guild.reminder_whitelist MAXIMUM_REMINDERS = 5 -CONFIRMATION_TIMEOUT = 60 +REMINDER_EDIT_CONFIRMATION_TIMEOUT = 60 Mentionable = discord.Member | discord.Role ReminderMention = UnambiguousUser | discord.Role -class ModifyConfirmationView(discord.ui.View): - """A view to confirm modifying someone else's reminder.""" +class ModifyReminderConfirmationView(discord.ui.View): + """A view to confirm modifying someone else's reminder by admins.""" def __init__(self, author: discord.Member | discord.User): super().__init__(timeout=CONFIRMATION_TIMEOUT) @@ -572,11 +572,11 @@ class Reminders(Cog): raise e if api_response["author"] == ctx.author.id: - log.debug(f"{ctx.author} is the reminder author and passes the check.") + log.debug(f"{ctx.author} is the reminder's author and passes the check.") return True if await has_any_role_check(ctx, Roles.admins): - log.debug(f"{ctx.author} is an admin, asking for confirmation to modify the reminder.") + log.debug(f"{ctx.author} is an admin, asking for confirmation to modify someone else's.") confirmation_view = ModifyConfirmationView(ctx.author) await ctx.send( -- cgit v1.2.3 From cac8e34dd1179a18ee1f14bdb57b0665df49b9b0 Mon Sep 17 00:00:00 2001 From: Daniel Gu Date: Mon, 14 Aug 2023 00:23:35 +0800 Subject: Apply suggestions from code review Co-authored-by: Amrou Bellalouna --- bot/exts/utils/reminders.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/utils/reminders.py b/bot/exts/utils/reminders.py index 56df25923..cf12e279f 100644 --- a/bot/exts/utils/reminders.py +++ b/bot/exts/utils/reminders.py @@ -591,7 +591,7 @@ class Reminders(Cog): log.debug(f"{ctx.author} has cancelled reminder modification.") return confirmation_view.result - log.debug(f"{ctx.author} is not the reminder author and does not pass the check.") + log.debug(f"{ctx.author} is not the reminder's author and thus does not pass the check.") if send_on_denial: await send_denial(ctx, "You can't modify reminders of other users!") return False -- cgit v1.2.3 From 41e71d9b4618f1dfd2a4dc77e87ac91ea602a7ec Mon Sep 17 00:00:00 2001 From: Daniel Gu Date: Mon, 14 Aug 2023 00:27:24 +0800 Subject: Reply to original message when sending confirmation prompt --- bot/exts/utils/reminders.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/utils/reminders.py b/bot/exts/utils/reminders.py index cf12e279f..1e3df4a53 100644 --- a/bot/exts/utils/reminders.py +++ b/bot/exts/utils/reminders.py @@ -579,7 +579,7 @@ class Reminders(Cog): log.debug(f"{ctx.author} is an admin, asking for confirmation to modify someone else's.") confirmation_view = ModifyConfirmationView(ctx.author) - await ctx.send( + await ctx.reply( "Are you sure you want to modify someone else's reminder?", view=confirmation_view, ) -- cgit v1.2.3 From 38ee27015a9c6a6207ab81a6f5ab669ab1bc1e57 Mon Sep 17 00:00:00 2001 From: Daniel Gu Date: Mon, 14 Aug 2023 09:54:58 +0800 Subject: Apply name changes from code review everywhere --- bot/exts/utils/reminders.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bot/exts/utils/reminders.py b/bot/exts/utils/reminders.py index 1e3df4a53..885643100 100644 --- a/bot/exts/utils/reminders.py +++ b/bot/exts/utils/reminders.py @@ -48,7 +48,7 @@ class ModifyReminderConfirmationView(discord.ui.View): """A view to confirm modifying someone else's reminder by admins.""" def __init__(self, author: discord.Member | discord.User): - super().__init__(timeout=CONFIRMATION_TIMEOUT) + super().__init__(timeout=REMINDER_EDIT_CONFIRMATION_TIMEOUT) self.author = author self.result: bool | None = None @@ -578,7 +578,7 @@ class Reminders(Cog): if await has_any_role_check(ctx, Roles.admins): log.debug(f"{ctx.author} is an admin, asking for confirmation to modify someone else's.") - confirmation_view = ModifyConfirmationView(ctx.author) + confirmation_view = ModifyReminderConfirmationView(ctx.author) await ctx.reply( "Are you sure you want to modify someone else's reminder?", view=confirmation_view, -- cgit v1.2.3 From 1c6c02cff0af3f87cd9117f2e8d7f7b406197c20 Mon Sep 17 00:00:00 2001 From: Daniel Gu Date: Mon, 14 Aug 2023 09:56:56 +0800 Subject: Mention reminder owner in confirmation message --- bot/exts/utils/reminders.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/bot/exts/utils/reminders.py b/bot/exts/utils/reminders.py index 885643100..c8d7b05b9 100644 --- a/bot/exts/utils/reminders.py +++ b/bot/exts/utils/reminders.py @@ -570,8 +570,9 @@ class Reminders(Cog): if e.status == 404: return False raise e + owner_id = api_response["author"] - if api_response["author"] == ctx.author.id: + if owner_id == ctx.author.id: log.debug(f"{ctx.author} is the reminder's author and passes the check.") return True @@ -580,7 +581,7 @@ class Reminders(Cog): confirmation_view = ModifyReminderConfirmationView(ctx.author) await ctx.reply( - "Are you sure you want to modify someone else's reminder?", + "Are you sure you want to modify <@{owner_id}>'s reminder?", view=confirmation_view, ) await confirmation_view.wait() -- cgit v1.2.3 From 0f4bdc0cd217ed1a9c2eb0259d853442efdb8d79 Mon Sep 17 00:00:00 2001 From: n0Oo0Oo0b Date: Tue, 15 Aug 2023 22:13:34 +0800 Subject: Apply changes from code review --- bot/exts/utils/reminders.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bot/exts/utils/reminders.py b/bot/exts/utils/reminders.py index c8d7b05b9..a3150c6b8 100644 --- a/bot/exts/utils/reminders.py +++ b/bot/exts/utils/reminders.py @@ -47,7 +47,7 @@ ReminderMention = UnambiguousUser | discord.Role class ModifyReminderConfirmationView(discord.ui.View): """A view to confirm modifying someone else's reminder by admins.""" - def __init__(self, author: discord.Member | discord.User): + def __init__(self, author: discord.Member): super().__init__(timeout=REMINDER_EDIT_CONFIRMATION_TIMEOUT) self.author = author self.result: bool | None = None @@ -581,7 +581,7 @@ class Reminders(Cog): confirmation_view = ModifyReminderConfirmationView(ctx.author) await ctx.reply( - "Are you sure you want to modify <@{owner_id}>'s reminder?", + f"Are you sure you want to modify <@{owner_id}>'s reminder?", view=confirmation_view, ) await confirmation_view.wait() -- cgit v1.2.3 From bbeda717f2e42bfb454cb914833c0ed9d824bd37 Mon Sep 17 00:00:00 2001 From: n0Oo0Oo0b Date: Wed, 16 Aug 2023 09:13:49 +0800 Subject: Specify which action is being performed in confirmation message --- bot/exts/utils/reminders.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/bot/exts/utils/reminders.py b/bot/exts/utils/reminders.py index a3150c6b8..5159db71f 100644 --- a/bot/exts/utils/reminders.py +++ b/bot/exts/utils/reminders.py @@ -579,9 +579,14 @@ class Reminders(Cog): if await has_any_role_check(ctx, Roles.admins): log.debug(f"{ctx.author} is an admin, asking for confirmation to modify someone else's.") + if ctx.command == self.delete_reminder: + modify_action = "delete" + else: + modify_action = "edit" + confirmation_view = ModifyReminderConfirmationView(ctx.author) await ctx.reply( - f"Are you sure you want to modify <@{owner_id}>'s reminder?", + f"Are you sure you want to {modify_action} <@{owner_id}>'s reminder?", view=confirmation_view, ) await confirmation_view.wait() -- cgit v1.2.3 From 24fb1b452f4a1af1e424678dc3c19a0ac9b8a1fc Mon Sep 17 00:00:00 2001 From: n0Oo0Oo0b Date: Fri, 18 Aug 2023 19:49:24 +0800 Subject: Ensure view gets deleted if view times out --- bot/exts/utils/reminders.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/bot/exts/utils/reminders.py b/bot/exts/utils/reminders.py index 5159db71f..f7d29eff2 100644 --- a/bot/exts/utils/reminders.py +++ b/bot/exts/utils/reminders.py @@ -585,11 +585,14 @@ class Reminders(Cog): modify_action = "edit" confirmation_view = ModifyReminderConfirmationView(ctx.author) - await ctx.reply( + confirmation_message = await ctx.reply( f"Are you sure you want to {modify_action} <@{owner_id}>'s reminder?", view=confirmation_view, ) - await confirmation_view.wait() + view_timed_out = await confirmation_view.wait() + # We don't have access to the message in `on_timeout` so we have to delete the view here + if view_timed_out: + await confirmation_message.edit(view=None) if confirmation_view.result: log.debug(f"{ctx.author} has confirmed reminder modification.") -- cgit v1.2.3 From 2bdd46a496c94bd56181c6a4c41a2b7ebaf3bac8 Mon Sep 17 00:00:00 2001 From: n0Oo0Oo0b Date: Fri, 18 Aug 2023 19:52:31 +0800 Subject: Prevent ghost pings when reminder modification is cancelled --- bot/exts/utils/reminders.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/bot/exts/utils/reminders.py b/bot/exts/utils/reminders.py index f7d29eff2..07e014dd0 100644 --- a/bot/exts/utils/reminders.py +++ b/bot/exts/utils/reminders.py @@ -70,7 +70,7 @@ class ModifyReminderConfirmationView(discord.ui.View): @discord.ui.button(label="Cancel", row=0) async def cancel(self, interaction: Interaction, button: discord.ui.Button) -> None: """Cancel the reminder modification.""" - await interaction.response.edit_message(content="🚫 Operation canceled.", view=None) + await interaction.response.edit_message(view=None) self.result = False self.stop() @@ -597,6 +597,7 @@ class Reminders(Cog): if confirmation_view.result: log.debug(f"{ctx.author} has confirmed reminder modification.") else: + await ctx.send("🚫 Operation canceled.") log.debug(f"{ctx.author} has cancelled reminder modification.") return confirmation_view.result -- cgit v1.2.3