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 == '🏠': if page == '🏠':
## POLL CREATION SHORT ## POLL CREATION SHORT
embed.add_field(name='🆕 Making New Polls', 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='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='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?`', # 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. ' f'and/or if you would like to manually `{pre}activate` it. '
'Perfect if you are preparing for a team meeting!', 'Perfect if you are preparing for a team meeting!',
inline=False) 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 == '🔍': elif page == '🔍':
embed.add_field(name='🔍 Show Polls', embed.add_field(name='🔍 Show Polls',
value='All users can display and list polls, with the exception of prepared 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: while True:
try: 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) self.name = await get_valid(reply)
await self.add_vaild(message, self.name) await self.add_vaild(message, self.name)
break break
@ -203,7 +207,11 @@ class Poll:
while True: while True:
try: 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) self.short = await get_valid(reply)
await self.add_vaild(message, self.short) await self.add_vaild(message, self.short)
break break
@ -263,7 +271,11 @@ class Poll:
while True: while True:
try: 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) dt = await get_valid(reply)
self.activation = dt self.activation = dt
if self.activation == 0: if self.activation == 0:
@ -315,7 +327,11 @@ class Poll:
while True: while True:
try: 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) self.anonymous = await get_valid(reply)
await self.add_vaild(message, f'{"Yes" if self.anonymous else "No"}') await self.add_vaild(message, f'{"Yes" if self.anonymous else "No"}')
break break
@ -382,7 +398,11 @@ class Poll:
while True: while True:
try: 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) 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'{"Yes" if self.multiple_choice else "No"}')
break break
@ -450,7 +470,11 @@ class Poll:
while True: while True:
try: 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) options = await get_valid(reply)
self.options_reaction_default = False self.options_reaction_default = False
if isinstance(options, int): if isinstance(options, int):
@ -527,7 +551,11 @@ class Poll:
while True: while True:
try: 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) self.roles = await get_valid(reply, roles)
await self.add_vaild(message, f'{", ".join(self.roles)}') await self.add_vaild(message, f'{", ".join(self.roles)}')
break break
@ -540,43 +568,6 @@ class Poll:
except InvalidRoles as e: except InvalidRoles as e:
await self.add_error(message, f'**The following roles are invalid: {e.roles}**') 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): async def set_weights(self, force=None):
"""Set role weights for the poll.""" """Set role weights for the poll."""
async def get_valid(in_reply, server_roles): async def get_valid(in_reply, server_roles):
@ -623,7 +614,11 @@ class Poll:
while True: while True:
try: 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) w_n = await get_valid(reply, self.server.roles)
self.weights_roles = w_n[0] self.weights_roles = w_n[0]
self.weights_numbers = w_n[1] self.weights_numbers = w_n[1]
@ -686,7 +681,11 @@ class Poll:
while True: while True:
try: 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) dt = await get_valid(reply)
self.duration = dt self.duration = dt
if self.duration == 0: if self.duration == 0:
@ -705,7 +704,6 @@ class Poll:
def finalize(self): def finalize(self):
self.time_created = datetime.datetime.utcnow().replace(tzinfo=pytz.utc) self.time_created = datetime.datetime.utcnow().replace(tzinfo=pytz.utc)
async def to_dict(self): async def to_dict(self):
if self.channel is None: if self.channel is None:
cid = 0 cid = 0
@ -965,7 +963,7 @@ class Poll:
# else: # else:
# embed = await self.add_field_custom(name='**Options**', value=', '.join(self.get_options()), embed=embed) # 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 return embed
@ -994,12 +992,6 @@ class Poll:
else: else:
return msg return msg
# def get_options(self):
# if self.reaction:
# return self.options_reaction
# else:
# return self.options_traditional
def get_duration_with_tz(self): def get_duration_with_tz(self):
if self.duration == 0: if self.duration == 0:
return 0 return 0
@ -1077,7 +1069,6 @@ class Poll:
else: else:
return sum([1 for c in [u for u in self.votes] if option in self.votes[c]['choices']]) 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): async def vote(self, user, option, message):
if not await self.is_open(): if not await self.is_open():
# refresh to show closed poll # refresh to show closed poll

View File

@ -1,9 +1,14 @@
import argparse
import copy import copy
import json import json
import logging import logging
import shlex
import discord import discord
from discord.ext import commands from discord.ext import commands
from utils.misc import CustomFormatter
from .poll import Poll from .poll import Poll
from utils.paginator import embed_list_paginated from utils.paginator import embed_list_paginated
from essentials.multi_server import get_server_pre, ask_for_server, ask_for_channel 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): async def say_error(self, ctx, error_text, footer_text=None):
embed = discord.Embed(title='', description=error_text, colour=SETTINGS.color) 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: if footer_text is not None:
embed.set_footer(text=footer_text) embed.set_footer(text=footer_text)
await self.bot.say(embed=embed) await self.bot.say(embed=embed)
async def say_embed(self, ctx, say_text='', title='Pollmaster', footer_text=None): async def say_embed(self, ctx, say_text='', title='Pollmaster', footer_text=None):
embed = discord.Embed(title='', description=say_text, colour=SETTINGS.color) 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: if footer_text is not None:
embed.set_footer(text=footer_text) embed.set_footer(text=footer_text)
await self.bot.say(embed=embed) await self.bot.say(embed=embed)
@ -222,7 +227,7 @@ class PollControls:
title = f' Listing {short} polls' title = f' Listing {short} polls'
embed = discord.Embed(title='', description='', colour=SETTINGS.color) 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)) # 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) # msg = await self.embed_list_paginated(ctx, polls, item_fct, embed, per_page=8)
pre = await get_server_pre(self.bot, server) pre = await get_server_pre(self.bot, server)
@ -242,13 +247,76 @@ class PollControls:
footer = f'Type {pre}show to display all polls' footer = f'Type {pre}show to display all polls'
await self.say_error(ctx, error, footer) 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) @commands.command(pass_context=True)
async def quick(self, ctx, *, cmd=None): async def quick(self, ctx, *, cmd=None):
'''Create a quick poll with just a question and some options. Parameters: <Question> (optional)''' '''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): async def route(poll):
await poll.set_name(force=cmd) 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_anonymous(force='no')
await poll.set_multiple_choice(force='no') await poll.set_multiple_choice(force='no')
await poll.set_options_reaction() await poll.set_options_reaction()
@ -256,13 +324,16 @@ class PollControls:
await poll.set_weights(force='none') await poll.set_weights(force='none')
await poll.set_duration(force='0') await poll.set_duration(force='0')
poll = await self.wizard(ctx, route) poll = await self.wizard(ctx, route, server)
if poll: if poll:
await poll.post_embed() await poll.post_embed()
@commands.command(pass_context=True) @commands.command(pass_context=True)
async def prepare(self, ctx, *, cmd=None): async def prepare(self, ctx, *, cmd=None):
'''Prepare a poll to use later. Parameters: <Question> (optional) ''' '''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): async def route(poll):
await poll.set_name(force=cmd) await poll.set_name(force=cmd)
@ -278,13 +349,16 @@ class PollControls:
await poll.set_weights() await poll.set_weights()
await poll.set_duration() await poll.set_duration()
poll = await self.wizard(ctx, route) poll = await self.wizard(ctx, route, server)
if poll: if poll:
await poll.post_embed(destination=ctx.message.author) await poll.post_embed(destination=ctx.message.author)
@commands.command(pass_context=True) @commands.command(pass_context=True)
async def new(self, ctx, *, cmd=None): async def new(self, ctx, *, cmd=None):
'''Start the poll wizard to create a new poll step by step. Parameters: <Question> (optional) ''' '''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): async def route(poll):
await poll.set_name(force=cmd) await poll.set_name(force=cmd)
@ -299,16 +373,12 @@ class PollControls:
await poll.set_weights() await poll.set_weights()
await poll.set_duration() await poll.set_duration()
poll = await self.wizard(ctx, route) poll = await self.wizard(ctx, route, server)
if poll: if poll:
await poll.post_embed() await poll.post_embed()
# The Wizard! # The Wizard!
async def wizard(self, ctx, route): async def wizard(self, ctx, route, server):
server = await ask_for_server(self.bot, ctx.message)
if not server:
return
channel = await ask_for_channel(self.bot, server, ctx.message) channel = await ask_for_channel(self.bot, server, ctx.message)
if not channel: if not channel:
return return

View File

@ -1,6 +1,35 @@
import argparse
import pytz import pytz
import datetime as dt 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): def possible_timezones(tz_offset, common_only=True):
# pick one of the timezone collections # pick one of the timezone collections
timezones = pytz.common_timezones if common_only else pytz.all_timezones timezones = pytz.common_timezones if common_only else pytz.all_timezones