Added possibility to confirm some actions

This commit is contained in:
Elnath 2021-06-12 19:44:41 +02:00
parent 15945d839c
commit 03cee9963b
1 changed files with 67 additions and 3 deletions

View File

@ -1,8 +1,10 @@
#!/usr/bin/env python3
import argparse
import asyncio
import logging
import sys
from pathlib import Path
from typing import Dict, Tuple, Coroutine
import discord
import discord.utils
@ -21,11 +23,14 @@ class SecretBot(commands.Cog):
self.bot = commands.Bot(
"!",
help_command = commands.MinimalHelpCommand(),
intents = discord.Intents(guild_messages = True, guilds = True, members = True),
intents = discord.Intents(guild_messages = True, guilds = True, members = True, reactions = True),
allowed_mentions = discord.AllowedMentions.none(), # By default we do not allow mentions, as we will white-list them on each required message
)
self.bot.add_cog(self)
self.games_file = games_file
# Each guild can have a confirmation message to execute an action. We store:
# The confirmation message, the coroutine to execute if the user reacts with :thumbsup: to the confirmation message, a task that will delete the message after a certain time (so that we can stop it if the user confirms instead)
self.confirmation_messages: Dict[discord.guild, Tuple[discord.Message, Coroutine, asyncio.Task]] = {}
def run(self, token):
self.bot.run(token)
@ -114,6 +119,54 @@ class SecretBot(commands.Cog):
await ctx.reply(":warning: You should do this in the admin channel!")
raise utils.CheckFailDoNotNotify
async def confirm_action(self, message_text, text_channel: discord.TextChannel, action: Coroutine, reply_to: discord.Message = None, max_delay = 60):
"""
Asks the user to confirm an action by reacting to a message.
If the user does not react in max_delay seconds, the confirmation message is deleted and the action is canceled.
For now, only one such confirmation per server is allowed. If another confirmation is asked while a previous one exists, the previous one is treated as if the delay passed.
"""
if text_channel.guild in self.confirmation_messages: # If there was already a pending confirmation
# We delete it
message, action, task = self.confirmation_messages[text_channel.guild]
asyncio.create_task(message.delete())
action.close()
task.cancel()
message = await text_channel.send(message_text + "\nReact to this message with :thumbsup: to confirm or :thumbsdown: to cancel", reference = reply_to)
async def add_reaction_prompts():
await message.add_reaction("👍")
await message.add_reaction("👎")
asyncio.create_task(add_reaction_prompts())
async def delete_confirmation_message_after_max_delay():
await asyncio.sleep(max_delay)
del self.confirmation_messages[text_channel.guild]
action.close()
await message.delete()
self.confirmation_messages[text_channel.guild] = (message, action, asyncio.create_task(delete_confirmation_message_after_max_delay()))
@commands.Cog.listener()
async def on_reaction_add(self, reaction: discord.Reaction, user: discord.Member):
if user.bot: # We ignore reactions added by bots
return
if reaction.message.guild in self.confirmation_messages:
message, action, task = self.confirmation_messages[reaction.message.guild]
if reaction.message == message:
# Note that we do not check which user added the reaction.
# The confirmation message is just supposed to be a safeguard against typing mistakes
if reaction.emoji == "👍":
del self.confirmation_messages[reaction.message.guild]
task.cancel()
asyncio.create_task(message.delete())
await action
elif reaction.emoji == "👎":
del self.confirmation_messages[reaction.message.guild]
task.cancel()
action.close()
await message.delete()
@commands.command(help = "See if I'm alive")
async def ping(self, ctx: commands.Context):
await ctx.reply(":ping_pong:", mention_author = True)
@ -130,9 +183,13 @@ class SecretBot(commands.Cog):
await ctx.reply(":white_check_mark: Game started!")
@commands.command("DeleteGame", help = "Delete a running game and all of its associated channels")
async def delete_game_with_confirmation(self, ctx: commands.Context):
await self.get_running_game_or_error_message(ctx)
await self.check_is_administrator_or_gm(ctx)
await self.confirm_action("Do you really want to delete the current game?", ctx.channel, self.delete_game(ctx), ctx.message)
async def delete_game(self, ctx: commands.Context):
game = await self.get_running_game_or_error_message(ctx)
await self.check_is_administrator_or_gm(ctx)
gm_role = game.get_gm_role()
await game.delete()
await ctx.guild.get_member(self.bot.user.id).remove_roles(gm_role)
@ -174,9 +231,16 @@ class SecretBot(commands.Cog):
await self.cast_vote(ctx, False)
@commands.command("StopTheCount")
async def stop_vote(self, ctx: commands.Context):
async def stop_vote_with_confirmation(self, ctx: commands.Context):
game = await self.get_running_game_or_error_message(ctx)
await self.check_is_administrator_or_gm(ctx)
if not game.is_vote_running():
await ctx.reply(":x: No vote is running")
return
await self.confirm_action("Do you really want to stop the vote and reveal all votes?", ctx.channel, self.stop_vote(ctx), ctx.message)
async def stop_vote(self, ctx: commands.Context):
game = await self.get_running_game_or_error_message(ctx)
if not game.is_vote_running():
await ctx.reply(":x: No vote is running")
return