issue #3 adding "commandline" feature

This commit is contained in:
matnad 2019-02-14 17:48:24 +01:00
parent 44b8d3328a
commit 3df015d132
4 changed files with 166 additions and 68 deletions

View File

@ -49,7 +49,7 @@ class Help:
if page == '🏠':
## POLL CREATION SHORT
embed.add_field(name='🆕 Making New Polls',
value=f'`{pre}quick` | `{pre}new` | `{pre}prepare`', inline=False)
value=f'`{pre}quick` | `{pre}new` | `{pre}prepare` | `{pre}cmd <args>`', inline=False)
# embed.add_field(name='Commands', value=f'`{pre}quick` | `{pre}new` | `{pre}prepared`', inline=False)
# embed.add_field(name='Arguments', value=f'Arguments: `<poll question>` (optional)', inline=False)
# embed.add_field(name='Examples', value=f'Examples: `{pre}new` | `{pre}quick What is the greenest color?`',
@ -108,6 +108,14 @@ class Help:
f'and/or if you would like to manually `{pre}activate` it. '
'Perfect if you are preparing for a team meeting!',
inline=False)
embed.add_field(name=f'🔹 **-Advanced- Commandline:** `{pre}cmd <args>`',
value=f'For the full syntax type `{pre}cmd help`\n'
f'Similar to version 1 of the bot, with this command you can create a poll in one message. '
f'Pass all the options you need via command line arguments, the rest will be set to '
f'default values. The wizard will step in for invalid arguments.\n'
f'Example: `{pre}cmd -q "Which colors?" -l colors -o "green, blue, red" -mc -a`',
inline=False)
elif page == '🔍':
embed.add_field(name='🔍 Show Polls',
value='All users can display and list polls, with the exception of prepared polls. '

View File

@ -165,7 +165,11 @@ class Poll:
while True:
try:
reply = await self.get_user_reply()
if force:
reply = force
force = None
else:
reply = await self.get_user_reply()
self.name = await get_valid(reply)
await self.add_vaild(message, self.name)
break
@ -203,7 +207,11 @@ class Poll:
while True:
try:
reply = await self.get_user_reply()
if force:
reply = force
force = None
else:
reply = await self.get_user_reply()
self.short = await get_valid(reply)
await self.add_vaild(message, self.short)
break
@ -263,7 +271,11 @@ class Poll:
while True:
try:
reply = await self.get_user_reply()
if force:
reply = force
force = None
else:
reply = await self.get_user_reply()
dt = await get_valid(reply)
self.activation = dt
if self.activation == 0:
@ -315,7 +327,11 @@ class Poll:
while True:
try:
reply = await self.get_user_reply()
if force:
reply = force
force = None
else:
reply = await self.get_user_reply()
self.anonymous = await get_valid(reply)
await self.add_vaild(message, f'{"Yes" if self.anonymous else "No"}')
break
@ -382,7 +398,11 @@ class Poll:
while True:
try:
reply = await self.get_user_reply()
if force:
reply = force
force = None
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"}')
break
@ -450,7 +470,11 @@ class Poll:
while True:
try:
reply = await self.get_user_reply()
if force:
reply = force
force = None
else:
reply = await self.get_user_reply()
options = await get_valid(reply)
self.options_reaction_default = False
if isinstance(options, int):
@ -527,7 +551,11 @@ class Poll:
while True:
try:
reply = await self.get_user_reply()
if force:
reply = force
force = None
else:
reply = await self.get_user_reply()
self.roles = await get_valid(reply, roles)
await self.add_vaild(message, f'{", ".join(self.roles)}')
break
@ -540,43 +568,6 @@ class Poll:
except InvalidRoles as e:
await self.add_error(message, f'**The following roles are invalid: {e.roles}**')
# async def set_options_traditional(self, force=None):
# '''Currently not used as everything is reaction based'''
# if force is not None:
# self.options_traditional = force
# return
# if self.stopped: return
# text = """**Next you chose from the possible set of options/answers for your poll.**
# Type the corresponding number or type your own options, separated by commas.
#
# **1** - yes, no
# **2** - in favour, against, abstain
# **3** - love it, like it, don't care, meh, hate it
#
# If you write your own options they will be listed and can be voted for.
# To use your custom options type them like this:
# **apple juice, banana ice cream, kiwi slices**"""
# message = await self.wizard_says(text)
#
# reply = ''
# while reply == '' or (reply.split(",").__len__() < 2 and reply not in ['1', '2', '3']) \
# or (reply.split(",").__len__() > 99 and reply not in ['1', '2', '3']):
# if reply != '':
# await self.add_error(message,
# '**Invalid entry. Type `1` `2` or `3` or a comma separated list (max. 99 options).**')
# reply = await self.get_user_reply()
# if self.stopped: break
#
# if reply == '1':
# self.options_traditional = ['yes, no']
# elif reply == '2':
# self.options_traditional = ['in favour', 'against', 'abstain']
# elif reply == '3':
# self.options_traditional = ['love it', 'like it', 'don\'t care', 'meh', 'hate it']
# else:
# self.options_traditional = [r.strip() for r in reply.split(",")]
# return self.options_traditional
async def set_weights(self, force=None):
"""Set role weights for the poll."""
async def get_valid(in_reply, server_roles):
@ -623,7 +614,11 @@ class Poll:
while True:
try:
reply = await self.get_user_reply()
if force:
reply = force
force = None
else:
reply = await self.get_user_reply()
w_n = await get_valid(reply, self.server.roles)
self.weights_roles = w_n[0]
self.weights_numbers = w_n[1]
@ -686,7 +681,11 @@ class Poll:
while True:
try:
reply = await self.get_user_reply()
if force:
reply = force
force = None
else:
reply = await self.get_user_reply()
dt = await get_valid(reply)
self.duration = dt
if self.duration == 0:
@ -705,7 +704,6 @@ class Poll:
def finalize(self):
self.time_created = datetime.datetime.utcnow().replace(tzinfo=pytz.utc)
async def to_dict(self):
if self.channel is None:
cid = 0
@ -965,7 +963,7 @@ class Poll:
# else:
# embed = await self.add_field_custom(name='**Options**', value=', '.join(self.get_options()), embed=embed)
embed.set_footer(text='bot is in development')
# embed.set_footer(text='bot is in development')
return embed
@ -994,12 +992,6 @@ class Poll:
else:
return msg
# def get_options(self):
# if self.reaction:
# return self.options_reaction
# else:
# return self.options_traditional
def get_duration_with_tz(self):
if self.duration == 0:
return 0
@ -1077,7 +1069,6 @@ class Poll:
else:
return sum([1 for c in [u for u in self.votes] if option in self.votes[c]['choices']])
async def vote(self, user, option, message):
if not await self.is_open():
# refresh to show closed poll

View File

@ -1,9 +1,14 @@
import argparse
import copy
import json
import logging
import shlex
import discord
from discord.ext import commands
from utils.misc import CustomFormatter
from .poll import Poll
from utils.paginator import embed_list_paginated
from essentials.multi_server import get_server_pre, ask_for_server, ask_for_channel
@ -34,14 +39,14 @@ class PollControls:
async def say_error(self, ctx, error_text, footer_text=None):
embed = discord.Embed(title='', description=error_text, colour=SETTINGS.color)
embed.set_author(name='Error', icon_url=SETTINGS.title_icon)
embed.set_author(name='Error', icon_url=SETTINGS.author_icon)
if footer_text is not None:
embed.set_footer(text=footer_text)
await self.bot.say(embed=embed)
async def say_embed(self, ctx, say_text='', title='Pollmaster', footer_text=None):
embed = discord.Embed(title='', description=say_text, colour=SETTINGS.color)
embed.set_author(name=title, icon_url=SETTINGS.title_icon)
embed.set_author(name=title, icon_url=SETTINGS.author_icon)
if footer_text is not None:
embed.set_footer(text=footer_text)
await self.bot.say(embed=embed)
@ -222,7 +227,7 @@ class PollControls:
title = f' Listing {short} polls'
embed = discord.Embed(title='', description='', colour=SETTINGS.color)
embed.set_author(name=title, icon_url=SETTINGS.title_icon)
embed.set_author(name=title, icon_url=SETTINGS.author_icon)
# await self.bot.say(embed=await self.embed_list_paginated(polls, item_fct, embed))
# msg = await self.embed_list_paginated(ctx, polls, item_fct, embed, per_page=8)
pre = await get_server_pre(self.bot, server)
@ -242,13 +247,76 @@ class PollControls:
footer = f'Type {pre}show to display all polls'
await self.say_error(ctx, error, footer)
@commands.command(pass_context=True)
async def cmd(self, ctx, *, cmd=None):
'''The old, command style way paired with the wizard.'''
server = await ask_for_server(self.bot, ctx.message)
if not server:
return
pre = await get_server_pre(self.bot, server)
# generate the argparser and handle invalid stuff
descr = 'Accept poll settings via commandstring. \n\n' \
'**Wrap all arguments in quotes like this:** \n' \
f'{pre}cmd -question \"What tea do you like?\" -o \"green, black, chai\"\n\n' \
'The Order of arguments doesn\'t matter. If an argument is missing, it will use the default value. ' \
'If an argument is invalid, the wizard will step in. ' \
'If the command string is invalid, you will get this error :)'
parser = argparse.ArgumentParser(description=descr, formatter_class=CustomFormatter, add_help=False)
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('-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 ")
if cmd and cmd == 'help':
await self.say_embed(ctx, say_text=helpstring)
return
try:
cmds = shlex.split(cmd)
except ValueError:
await self.say_error(ctx, error_text=helpstring)
return
try:
args = parser.parse_args(cmds)
except SystemExit:
await self.say_error(ctx, error_text=helpstring)
return
# pass arguments to the wizard
async def route(poll):
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_roles(force=args.roles)
await poll.set_weights(force=args.weights)
await poll.set_duration(force=args.duration)
poll = await self.wizard(ctx, route, server)
if poll:
await poll.post_embed()
@commands.command(pass_context=True)
async def quick(self, ctx, *, cmd=None):
'''Create a quick poll with just a question and some options. Parameters: <Question> (optional)'''
server = await ask_for_server(self.bot, ctx.message)
if not server:
return
async def route(poll):
await poll.set_name(force=cmd)
await poll.set_short(force=str(await generate_word(self.bot, ctx.message.server.id)))
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()
@ -256,13 +324,16 @@ class PollControls:
await poll.set_weights(force='none')
await poll.set_duration(force='0')
poll = await self.wizard(ctx, route)
poll = await self.wizard(ctx, route, server)
if poll:
await poll.post_embed()
@commands.command(pass_context=True)
async def prepare(self, ctx, *, cmd=None):
'''Prepare a poll to use later. Parameters: <Question> (optional) '''
server = await ask_for_server(self.bot, ctx.message)
if not server:
return
async def route(poll):
await poll.set_name(force=cmd)
@ -278,13 +349,16 @@ class PollControls:
await poll.set_weights()
await poll.set_duration()
poll = await self.wizard(ctx, route)
poll = await self.wizard(ctx, route, server)
if poll:
await poll.post_embed(destination=ctx.message.author)
@commands.command(pass_context=True)
async def new(self, ctx, *, cmd=None):
'''Start the poll wizard to create a new poll step by step. Parameters: <Question> (optional) '''
server = await ask_for_server(self.bot, ctx.message)
if not server:
return
async def route(poll):
await poll.set_name(force=cmd)
@ -299,16 +373,12 @@ class PollControls:
await poll.set_weights()
await poll.set_duration()
poll = await self.wizard(ctx, route)
poll = await self.wizard(ctx, route, server)
if poll:
await poll.post_embed()
# The Wizard!
async def wizard(self, ctx, route):
server = await ask_for_server(self.bot, ctx.message)
if not server:
return
async def wizard(self, ctx, route, server):
channel = await ask_for_channel(self.bot, server, ctx.message)
if not channel:
return

View File

@ -1,6 +1,35 @@
import argparse
import pytz
import datetime as dt
class CustomFormatter(argparse.RawTextHelpFormatter):
def _format_action_invocation(self, action):
if not action.option_strings:
metavar, = self._metavar_formatter(action, action.dest)(1)
return metavar
else:
parts = []
# if the Optional doesn't take a value, format is:
# -s, --long
if action.nargs == 0:
parts.extend(action.option_strings)
# if the Optional takes a value, format is:
# -s ARGS, --long ARGS
# change to
# -s, --long ARGS
else:
default = action.dest.upper()
args_string = self._format_args(action, default)
for option_string in action.option_strings:
# parts.append('%s %s' % (option_string, args_string))
parts.append('%s' % option_string)
parts[-1] += ' %s' % args_string
return ', '.join(parts)
def possible_timezones(tz_offset, common_only=True):
# pick one of the timezone collections
timezones = pytz.common_timezones if common_only else pytz.all_timezones