diff --git a/README.md b/README.md index 535b8ef..924ec79 100644 --- a/README.md +++ b/README.md @@ -4,14 +4,18 @@ A plugin for [maubot](https://github.com/maubot/maubot) that creates a poll in a ## Usage '!poll new "Question" "Choice1" "Choice2" "Choice3"' - Creates a new poll with given (at least 1) choices -'!poll vote choice' - Has user vote for given choice (int). Users can only vote once per poll - '!poll results' - Displays the results from the poll '!poll close' - Ends the poll +Users vote by adding the matching emoji to the poll (i.e. if the first choice has a :thumbsup: then in order to pick that choice the user has to react with :thumbsup:) + +## Version 2.0 + - Changed voting format to reactions (instead of '!poll vote') -## Wish List +## Wish List - Add user configuration to only allow certain users to create polls - Add auto-timing ability - Add ability to run multiple polls at once +- Make emojis configurable +- Add placeholder emojis on each poll (to let users just click one) diff --git a/maubot.yaml b/maubot.yaml index 361a2ce..27920ef 100644 --- a/maubot.yaml +++ b/maubot.yaml @@ -9,7 +9,7 @@ maubot: 0.1.0 id: casavant.tom.poll # A PEP 440 compliant version string. -version: 1.0.1 +version: 2.0.0 # The SPDX license identifier for the plugin. https://spdx.org/licenses/ # Optional, assumes all rights reserved if omitted. diff --git a/poll.py b/poll.py index 39bd5ee..114dd0c 100644 --- a/poll.py +++ b/poll.py @@ -1,12 +1,15 @@ import re +import random from typing import List, Tuple from mautrix.util.config import BaseProxyConfig, ConfigUpdateHelper +from mautrix.types import (EventType, ReactionEvent) from maubot import Plugin, MessageEvent from maubot.handlers import command QUOTES_REGEX = r"\"?\s*?\"" # Regex to split string between quotes - +# [Thumbs Up, Thumbs Down, Grinning, Ghost, Robot, Okay Hand, Clapping Hands, Hundred] +REACTIONS = ["\U0001F44D", "\U0001F44E", "\U0001F600", "\U0001F47B", "\U0001F916", "\U0001F44C", "\U0001F44F", "\U0001F4AF"] class Poll: def __init__(self, question, choices): @@ -17,9 +20,11 @@ def __init__(self, question, choices): self.active = True # Begins the poll self.total = 0 + self.emojis = random.sample(REACTIONS, len(choices)) # Select a random assortment of emojis + def vote(self, choice, user_id): # Adds a vote to the given choice - self.votes[choice - 1] += 1 + self.votes[choice] += 1 # Adds user to list of users who have already voted self.voters.append(user_id) self.total += 1 @@ -64,6 +69,7 @@ async def poll(self) -> None: pass_raw=True, required=True ) + async def handler(self, evt: MessageEvent, poll_setup: str) -> None: await evt.mark_read() r = re.compile(QUOTES_REGEX) # Compiles regex for quotes @@ -78,30 +84,10 @@ async def handler(self, evt: MessageEvent, poll_setup: str) -> None: self.currentPoll = Poll(question, choices) # Show users active poll choice_list = "
".join( - [f"{i+1}. {choice}" for i, choice in enumerate(choices)] + [f"{self.currentPoll.emojis[i]} - {choice}" for i, choice in enumerate(choices)] ) response = f"{question}
{choice_list}" - - await evt.reply(response, allow_html=True) - - @poll.subcommand("vote", help="Votes for an option") - @command.argument( - "choice", pass_raw=True, required=True - ) - async def handler(self, evt: MessageEvent, choice: int) -> None: - await evt.mark_read() - # Verify the user is able to vote - if self.currentPoll.hasVoted(evt.sender): - await evt.reply("You've already voted in this poll") - elif not self.currentPoll.isActive(): - await evt.reply("I'm sorry this poll has already ended") - else: - # Checks if user entered a valid vote - if self.currentPoll.isAvailable(int(choice)): - # Makes the vote - self.currentPoll.vote(int(choice), evt.sender) - else: - await evt.reply("You must enter a valid choice") + self.currentPoll.event_id = await evt.reply(response, allow_html=True) @poll.subcommand("results", help="Prints out the current results of the poll") async def handler(self, evt: MessageEvent) -> None: @@ -113,3 +99,12 @@ async def handler(self, evt: MessageEvent) -> None: await evt.mark_read() self.currentPoll.close_poll() await evt.reply("This poll is now over. Type !poll results to see the results.") + + @command.passive(regex=r"(?:("+'|'.join(REACTIONS) + r")[\U0001F3FB-\U0001F3FF]?)", + field=lambda evt: evt.content.relates_to.key, + event_type=EventType.REACTION, msgtypes=None) + async def get_react_vote(self, evt: ReactionEvent, _: Tuple[str]) -> None: + if (evt.content.relates_to.event_id == self.currentPoll.event_id): # Is this on the correct message? + if not self.currentPoll.hasVoted(evt.sender): # has the user already voted? + if (evt.content.relates_to.key in self.currentPoll.emojis): # Is this a possible choice? + self.currentPoll.vote(self.currentPoll.emojis.index(evt.content.relates_to.key), evt.sender) # Add vote/sender to poll