[Utils] Finish and Refactor Predicate Utility (#2169)

* Uses classmethods to create predicates
* Classmethods allow using a combination of different parameters to describe context
* Some predicates assign a captured `result` to the predicate object on success
* Added `ReactionPredicate` equivalent to `MessagePredicate`
* Added `utils.menus.start_adding_reactions`, a non-blocking method for adding reactions asynchronously
* Added documentation
* Uses these new utils throughout the core bot
Happened to also find some bugs in places, and places where we were waiting for events without catching `asyncio.TimeoutError`

Signed-off-by: Toby Harradine <tobyharradine@gmail.com>
This commit is contained in:
Toby Harradine
2018-10-06 08:07:09 +10:00
committed by GitHub
parent 5d44bfabed
commit dea9dde637
15 changed files with 1229 additions and 320 deletions

View File

@@ -11,6 +11,8 @@ from redbot.core import checks, commands, config
from redbot.core.bot import Red
from redbot.core.i18n import Translator, cog_i18n
from redbot.core.utils.chat_formatting import box
from redbot.core.utils.menus import start_adding_reactions
from redbot.core.utils.predicates import ReactionPredicate, MessagePredicate
from .converters import CogOrCommand, RuleType, ClearableRuleType
@@ -20,9 +22,6 @@ COG = "COG"
COMMAND = "COMMAND"
GLOBAL = 0
# noinspection PyDictDuplicateKeys
REACTS = {"\N{WHITE HEAVY CHECK MARK}": True, "\N{NEGATIVE SQUARED CROSS MARK}": False}
Y_OR_N = {"y": True, "yes": True, "n": False, "no": False}
# The strings in the schema are constants and should get extracted, but not translated until
# runtime.
translate = _
@@ -566,35 +565,29 @@ class Permissions(commands.Cog):
"""Ask "Are you sure?" and get the response as a bool."""
if ctx.guild is None or ctx.guild.me.permissions_in(ctx.channel).add_reactions:
msg = await ctx.send(_("Are you sure?"))
for emoji in REACTS.keys():
await msg.add_reaction(emoji)
# noinspection PyAsyncCall
task = start_adding_reactions(msg, ReactionPredicate.YES_OR_NO_EMOJIS, ctx.bot.loop)
pred = ReactionPredicate.yes_or_no(msg, ctx.author)
try:
reaction, user = await ctx.bot.wait_for(
"reaction_add",
check=lambda r, u: (
r.message.id == msg.id and u == ctx.author and r.emoji in REACTS
),
timeout=30,
)
await ctx.bot.wait_for("reaction_add", check=pred, timeout=30)
except asyncio.TimeoutError:
agreed = False
await ctx.send(_("Response timed out."))
return False
else:
agreed = REACTS.get(reaction.emoji)
task.cancel()
agreed = pred.result
finally:
await msg.delete()
else:
await ctx.send(_("Are you sure? (y/n)"))
pred = MessagePredicate.yes_or_no(ctx)
try:
message = await ctx.bot.wait_for(
"message",
check=lambda m: m.author == ctx.author
and m.channel == ctx.channel
and m.content in Y_OR_N,
timeout=30,
)
await ctx.bot.wait_for("message", check=pred, timeout=30)
except asyncio.TimeoutError:
agreed = False
await ctx.send(_("Response timed out."))
return False
else:
agreed = Y_OR_N.get(message.content.lower())
agreed = pred.result
if agreed is False:
await ctx.send(_("Action cancelled."))