From a2be41dded907dcfbea26736b4d12f9f82fc1d4f Mon Sep 17 00:00:00 2001 From: matnad Date: Thu, 14 Feb 2019 19:05:38 +0100 Subject: [PATCH] issue #3 adding "commandline" feature --- cogs/poll.py | 132 +++++++++++++++++++++++++++-------------- cogs/poll_controls.py | 18 ++---- essentials/settings.py | 6 +- 3 files changed, 95 insertions(+), 61 deletions(-) diff --git a/cogs/poll.py b/cogs/poll.py index ec69370..b58f621 100644 --- a/cogs/poll.py +++ b/cogs/poll.py @@ -52,7 +52,7 @@ class Poll: self.short = str(uuid4())[0:23] self.anonymous = False self.reaction = True - self.multiple_choice = False + self.multiple_choice = 1 self.options_reaction = ['yes', 'no'] self.options_reaction_default = False # self.options_traditional = [] @@ -369,15 +369,15 @@ class Poll: async def get_valid(in_reply): if not in_reply: raise InvalidInput - is_true = ['yes', '1'] - is_false = ['no', '0'] in_reply = self.sanitize_string(in_reply) if not in_reply: raise InvalidInput - elif in_reply.lower() in is_true: - return True - elif in_reply.lower() in is_false: - return False + elif not in_reply.isdigit(): + raise ExpectedInteger + elif int(in_reply) > self.options_reaction.__len__(): + raise OutOfRange + elif int(in_reply) <= self.options_reaction.__len__() >= 0: + return int(in_reply) else: raise InvalidInput @@ -387,13 +387,14 @@ class Poll: except InputError: pass - text = ("**Should users be able to vote for multiple options?**\n" + text = ("**How many options should the voters be able choose?**\n" "\n" - "`0 - No`\n" - "`1 - Yes`\n" + "`0 - No Limit: Multiple Choice`\n" + "`1 - Single Choice`\n" + "`2+ - Specify exactly how many Choices`\n" "\n" - "If you type `0` or `no`, a new vote will override the old vote. " - "Otherwise the users can vote for as many options as they like.") + "If the maximum choices are reached for a voter, they have to unvote an option before being able to " + "vote for a different one.") message = await self.wizard_says(text) while True: @@ -404,10 +405,14 @@ class Poll: else: reply = await self.get_user_reply() self.multiple_choice = await get_valid(reply) - await self.add_vaild(message, f'{"Yes" if self.multiple_choice else "No"}') + await self.add_vaild(message, f'{self.multiple_choice if self.multiple_choice > 0 else "No Limit"}') break except InvalidInput: - await self.add_error(message, '**You can only answer with `yes` | `1` or `no` | `0`!**') + await self.add_error(message, '**Invalid Input**') + except ExpectedInteger: + await self.add_error(message, '**Enter a positive number**') + except OutOfRange: + await self.add_error(message, '**You can\'t have more choices than options.**') async def set_options_reaction(self, force=None): @@ -842,7 +847,20 @@ class Poll: self.short = d['short'] self.anonymous = d['anonymous'] self.reaction = d['reaction'] - self.multiple_choice = d['multiple_choice'] + + # backwards compatibility for multiple choice + if isinstance(d['multiple_choice'], bool): + if d['multiple_choice']: + self.multiple_choice = 0 + else: + self.multiple_choice = 1 + else: + try: + self.multiple_choice = int(d['multiple_choice']) + except ValueError: + logger.exception('Multiple Choice not an int or bool.') + self.multiple_choice = 0 # default + self.options_reaction = d['options_reaction'] self.options_reaction_default = d['reaction_default'] # self.options_traditional = d['options_traditional'] @@ -936,8 +954,12 @@ class Poll: if self.options_reaction_default: if await self.is_open(): text = f'**Score** ' - text += '*(Multiple Choice)*' if self.multiple_choice \ - else '*(Single Choice)*' + if self.multiple_choice == 0: + text += f'(Multiple Choice)' + elif self.multiple_choice == 1: + text += f'(Single Choice)' + else: + text += f'({self.multiple_choice} Choices)' else: text = f'**Final Score**' @@ -949,10 +971,20 @@ class Poll: embed.add_field(name='\u200b', value='\u200b', inline=False) if await self.is_open(): text = f'*Vote by adding reactions to the poll*. ' - text += '*You can vote for multiple options.*' if self.multiple_choice \ - else '*You have 1 vote, but can change it.*' + if self.multiple_choice == 0: + text += '*You can vote for multiple options.*' + elif self.multiple_choice == 1: + text += '*You have 1 vote, but can change it.*' + else: + text += f'*You have {self.multiple_choice} choices and can change them.*' else: - text = f'*Final Results of the {"multiple choice" if self.multiple_choice else "single choice"} Poll.*' + text = f'*Final Results of the Poll *' + if self.multiple_choice == 0: + text += '*(Multiple Choice).*' + elif self.multiple_choice == 1: + text += '*(Single Choice).*' + else: + text += f'*(With up to {self.multiple_choice} choices).*' embed = await self.add_field_custom(name='**Options**', value=text, embed=embed) for i, r in enumerate(self.options_reaction): embed = await self.add_field_custom( @@ -1079,7 +1111,7 @@ class Poll: return choice = 'invalid' - already_voted = False + refresh_poll = True # get weight weight = 1 @@ -1103,18 +1135,27 @@ class Poll: choice = AZ_EMOJIS.index(option) if choice != 'invalid': - if self.multiple_choice: - if choice in self.votes[user.id]['choices'] and self.anonymous: - # anonymous multiple choice -> can't unreact so we toggle with react - await self.unvote(user, option, message) - return - self.votes[user.id]['choices'].append(choice) - # if len(self.votes[user.id]['choices']) > len(set(self.votes[user.id]['choices'])): - # already_voted = True - self.votes[user.id]['choices'] = list(set(self.votes[user.id]['choices'])) + if self.multiple_choice != 1: # more than 1 choice (0 = no limit) + if choice in self.votes[user.id]['choices']: + if self.anonymous: + # anonymous multiple choice -> can't unreact so we toggle with react + await self.unvote(user, option, message) + return + refresh_poll = False + else: + if self.multiple_choice > 0 and self.votes[user.id]['choices'].__len__() >= self.multiple_choice: + say_text = f'You have reached the **maximum choices of {self.multiple_choice}** for this poll. ' \ + f'Before you can vote again, you need to unvote one of your choices.' + embed = discord.Embed(title='', description=say_text, colour=SETTINGS.color) + embed.set_author(name='Pollmaster', icon_url=SETTINGS.author_icon) + await self.bot.send_message(user, embed=embed) + refresh_poll = False + else: + self.votes[user.id]['choices'].append(choice) + self.votes[user.id]['choices'] = list(set(self.votes[user.id]['choices'])) else: if [choice] == self.votes[user.id]['choices']: - already_voted = True + refresh_poll = False if self.anonymous: # undo anonymous vote await self.unvote(user, option, message) @@ -1128,7 +1169,7 @@ class Poll: await self.save_to_db() # refresh - if not already_voted: + if refresh_poll: # edit message if there is a real change await self.bot.edit_message(message, embed=await self.generate_embed()) @@ -1145,21 +1186,20 @@ class Poll: if str(user.id) not in self.votes: return choice = 'invalid' - if self.reaction: - if self.options_reaction_default: - if option in self.options_reaction: - choice = self.options_reaction.index(option) - else: - if option in AZ_EMOJIS: - choice = AZ_EMOJIS.index(option) + if self.options_reaction_default: + if option in self.options_reaction: + choice = self.options_reaction.index(option) + else: + if option in AZ_EMOJIS: + choice = AZ_EMOJIS.index(option) - if choice != 'invalid' and choice in self.votes[user.id]['choices']: - try: - self.votes[user.id]['choices'].remove(choice) - await self.save_to_db() - await self.bot.edit_message(message, embed=await self.generate_embed()) - except ValueError: - pass + if choice != 'invalid' and choice in self.votes[user.id]['choices']: + try: + self.votes[user.id]['choices'].remove(choice) + await self.save_to_db() + await self.bot.edit_message(message, embed=await self.generate_embed()) + except ValueError: + pass async def has_required_role(self, user): return not set([r.name for r in user.roles]).isdisjoint(self.roles) diff --git a/cogs/poll_controls.py b/cogs/poll_controls.py index da6f82a..f7b7a05 100644 --- a/cogs/poll_controls.py +++ b/cogs/poll_controls.py @@ -266,11 +266,11 @@ class PollControls: parser.add_argument('-question', '-q') parser.add_argument('-label', '-l', default=str(await generate_word(self.bot, server.id))) parser.add_argument('-options', '-o') + parser.add_argument('-multiple_choice', '-mc', default='1') parser.add_argument('-roles', '-r', default='all') parser.add_argument('-weights', '-w', default='none') parser.add_argument('-duration', '-d', default='0') parser.add_argument('-anonymous', '-a', action="store_true") - parser.add_argument('-multiple_choice', '-mc', action="store_true") helpstring = parser.format_help() helpstring = helpstring.replace("pollmaster.py", f"{pre}cmd ") @@ -296,8 +296,8 @@ class PollControls: await poll.set_name(force=args.question) await poll.set_short(force=args.label) await poll.set_anonymous(force=f'{"yes" if args.anonymous else "no"}') - await poll.set_multiple_choice(force=f'{"yes" if args.multiple_choice else "no"}') await poll.set_options_reaction(force=args.options) + await poll.set_multiple_choice(force=args.multiple_choice) await poll.set_roles(force=args.roles) await poll.set_weights(force=args.weights) await poll.set_duration(force=args.duration) @@ -318,8 +318,8 @@ class PollControls: await poll.set_name(force=cmd) await poll.set_short(force=str(await generate_word(self.bot, server.id))) await poll.set_anonymous(force='no') - await poll.set_multiple_choice(force='no') await poll.set_options_reaction() + await poll.set_multiple_choice(force='1') await poll.set_roles(force='all') await poll.set_weights(force='none') await poll.set_duration(force='0') @@ -340,11 +340,8 @@ class PollControls: await poll.set_short() await poll.set_preparation() await poll.set_anonymous() + await poll.set_options_reaction() await poll.set_multiple_choice() - if poll.reaction: - await poll.set_options_reaction() - else: - await poll.set_options_traditional() await poll.set_roles() await poll.set_weights() await poll.set_duration() @@ -364,11 +361,8 @@ class PollControls: await poll.set_name(force=cmd) await poll.set_short() await poll.set_anonymous() + await poll.set_options_reaction() await poll.set_multiple_choice() - if poll.reaction: - await poll.set_options_reaction() - else: - await poll.set_options_traditional() await poll.set_roles() await poll.set_weights() await poll.set_duration() @@ -547,7 +541,7 @@ class PollControls: if p.anonymous: # immediately remove reaction and to be safe, remove all reactions await self.bot.remove_reaction(message, emoji, user) - elif not p.multiple_choice: + elif p.multiple_choice == 1: # remove all other reactions for r in message.reactions: if r.emoji and r.emoji != emoji: diff --git a/essentials/settings.py b/essentials/settings.py index 912f46c..0bca921 100644 --- a/essentials/settings.py +++ b/essentials/settings.py @@ -6,9 +6,9 @@ from essentials.secrets import SECRETS class Settings: def __init__(self): self.color = discord.Colour(int('7289da', 16)) - self.title_icon = "http://mnadler.ch/img/tag.png" - self.author_icon = "http://mnadler.ch/img/tag.jpg" - self.report_icon = "http://mnadler.ch/img/report.png" + self.title_icon = "https://i.imgur.com/vtLsAl8.jpg" #PM + self.author_icon = "https://i.imgur.com/TYbBtwB.jpg" #tag + self.report_icon = "https://i.imgur.com/YksGRLN.png" #report self.owner_id = 117687652278468610 self.msg_errors = False self.log_errors = True