Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[V3 Utils] Predicate Utility #1986

Merged
merged 2 commits into from
Oct 5, 2018
Merged

[V3 Utils] Predicate Utility #1986

merged 2 commits into from
Oct 5, 2018

Conversation

Tobotimus
Copy link
Member

@Tobotimus Tobotimus commented Aug 6, 2018

Type

  • New Feature

Tasks

  • Reaction equivalent to MessagePredicate
  • (maybe) Use of @classmethods for instantiation
  • Unit tests
  • Documentation
    ... probably more to be added

Description of the changes

(PR Description copied from #1985, originally written by @Redjumpman)

I often find myself and other cog creators having to write a bunch of boiler plate checks either through functions or lambdas for multiple cogs that need waiting events. Specifically, for when you need to validate a response. Suppose the following scenario:

# Common boilerplate check
def confirm(m):
    return m.author == ctx.author and m.content.lower() in ('yes', 'y', 'no', 'n')

try:
    response = await ctx.bot.wait_for('message', timeout=30, check=confirm)
except asyncio.TimeoutError:
    return await ctx.send('Response timed out')

Because ctx is often tightly coupled in these scenarios, you may find yourself writing simple predicates like the above confirm function or an equivalent lambda over and over across multiple commands or cogs.

Enter the new Predicates core utility module! This solves the problem with reusable predicates. Take the following example into consideration:

from redbot.core import commands
from redbot.core.utils.predicates import Predicate

class CogExample:
    # setup code
    @commands.command()
    async def example(self, ctx):
        await ctx.send("Please set a new welcome message.")
        
        try:
            welcome_message = await self.handler(ctx)
        except asycnio.TimeoutError:
            await ctx.send("Response timed out.")
        except RuntimeError:
            await ctx.send("Cancelled setting a new welcome message.")
        # rest of your code

    async def handler(self, ctx):
        check = Predicate(ctx.author, length=2000)
        message = await ctx.bot.wait_for('message', timeout=30, check=check.length_under)
        await ctx.send("Are you sure you wish to change your message?")
        choice = await ctx.bot.wait_for('message', timeout=30, check=check.confirm)
        if choice.content.lower() in ('y', 'yes'):
            return message.content
        else:
            raise RuntimeError

In the above code, we bypass having to write two separate checks by using Predicate. The Predicate class comes with the following methods:

  • same - sender and message author are the same
  • confirm - message is yes, y, no, or no
  • valid_int - message is an integer
  • valid_float - message is a float
  • positive - message is a positive integer
  • valid_role - message is the same as a role on the server
  • has_role - message is a role the name of a role the sender has
  • equal - message is equal to a set value
  • greater - message is a integer or float that is greater than a set value
  • less - message is a integer or float that is less than a set value
  • member - message is the name of a member on the server.
  • length_less - message has a length that is less than the the predicate's set length
  • length_greater - message has a length that is greater than the the predicate's set length
  • contained - message is a part of the predicate's set collection.

Note: All methods require that the sender and the message author be the same.

@Tobotimus Tobotimus added V3 Type: Feature New feature or request. labels Aug 6, 2018
@Tobotimus Tobotimus added this to the Future release milestone Aug 7, 2018
@mikeshardmind
Copy link
Contributor

mikeshardmind commented Aug 10, 2018

I'd like a mixed listener that allows for waiting for a reaction or message meeting criteria, but this is going to require working with socket_raw_recieve, I'll write something for this later. and it shouldnt hold up the initial work on this PR being merged if I dont have it ready by the time this is ready for review.

@Tobotimus Tobotimus modified the milestones: Beta 20, Beta 21 Aug 23, 2018
@Tobotimus Tobotimus removed this from the Beta 21 milestone Sep 6, 2018
* Add files via upload

* Update predicates.py

Changed sender from a discord.Member object to ctx.
Added a channel check.
Combined the same method and channel method into a validator and applied through-out.
valid_role and has_role methods now check for either an id or a name.
contained now uses string.lower() when testing for membership in a collection.

Signed-off-by: Redjumpman <redjumpman@users.noreply.github.com>
@Tobotimus
Copy link
Member Author

About to rebase+force push this PR branch.

* 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>
@Tobotimus Tobotimus added this to the RC 1 milestone Oct 5, 2018
@Tobotimus
Copy link
Member Author

Just want to post this for the record, the header of this PR is out of date and should not be used as a guide for this feature; see the documentation after this PR is merged for details 😃

@Tobotimus Tobotimus merged commit fb83908 into V3/develop Oct 5, 2018
@Tobotimus Tobotimus deleted the V3/feature/predicates branch October 5, 2018 22:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Type: Feature New feature or request.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants