Merge pull request #5 from matnad/issue_3_4

Solve issues 3, 4, 6 and implement the info () feature
This commit is contained in:
Matthias Nadler 2019-02-19 18:33:35 +01:00 committed by GitHub
commit bf5797ea76
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 358 additions and 140 deletions

View File

@ -1,3 +1,10 @@
# Changelog for Version 2.1
## New features
- React to a poll with ❔ to get personalised info
- Added back the command line feature from version 1. Type *pm!cmd help* to get started
- Multiple choice is no longer a flag, but a number of how many options each voter can choose
# Changelog for Version 2.0
## TL;DR

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

@ -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 = []
@ -165,6 +165,10 @@ class Poll:
while True:
try:
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)
@ -203,6 +207,10 @@ class Poll:
while True:
try:
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)
@ -263,6 +271,10 @@ class Poll:
while True:
try:
if force:
reply = force
force = None
else:
reply = await self.get_user_reply()
dt = await get_valid(reply)
self.activation = dt
@ -315,6 +327,10 @@ class Poll:
while True:
try:
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"}')
@ -353,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
@ -371,23 +387,32 @@ 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:
try:
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"}')
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):
@ -442,7 +467,7 @@ class Poll:
"**1** - :white_check_mark: :negative_squared_cross_mark:\n"
"**2** - :thumbsup: :zipper_mouth: :thumbsdown:\n"
"**3** - :heart_eyes: :thumbsup: :zipper_mouth: :thumbsdown: :nauseated_face:\n"
"**4** - in favour, against, abstain\n"
"**4** - in favour, against, abstaining\n"
"\n"
"Example for custom options:\n"
"**apple juice, banana ice cream, kiwi slices** ")
@ -450,6 +475,10 @@ class Poll:
while True:
try:
if force:
reply = force
force = None
else:
reply = await self.get_user_reply()
options = await get_valid(reply)
self.options_reaction_default = False
@ -481,7 +510,7 @@ class Poll:
if split.__len__() == 1 and split[0] in ['0', 'all', 'everyone']:
return ['@everyone']
if n_roles <= 20:
if n_roles <= 20 and force is None:
if not all([r.isdigit() for r in split]):
raise ExpectedInteger
elif any([int(r) > n_roles for r in split]):
@ -527,6 +556,10 @@ class Poll:
while True:
try:
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)}')
@ -540,43 +573,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,6 +619,10 @@ class Poll:
while True:
try:
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]
@ -686,6 +686,10 @@ class Poll:
while True:
try:
if force:
reply = force
force = None
else:
reply = await self.get_user_reply()
dt = await get_valid(reply)
self.duration = dt
@ -705,7 +709,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
@ -844,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']
@ -938,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**'
@ -951,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'*Final Results of the {"multiple choice" if self.multiple_choice else "single choice"} Poll.*'
text += f'*You have {self.multiple_choice} choices and can change them.*'
else:
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(
@ -965,7 +995,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
@ -981,6 +1011,7 @@ class Poll:
msg,
r
)
await self.bot.add_reaction(msg, '')
return msg
else:
for i, r in enumerate(self.options_reaction):
@ -988,18 +1019,14 @@ class Poll:
msg,
AZ_EMOJIS[i]
)
await self.bot.add_reaction(msg, '')
return msg
elif not await self.is_open():
await self.bot.add_reaction(msg, '')
await self.bot.add_reaction(msg, '📎')
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 +1104,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
@ -1088,7 +1114,7 @@ class Poll:
return
choice = 'invalid'
already_voted = False
refresh_poll = True
# get weight
weight = 1
@ -1112,18 +1138,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:
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)
# 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']))
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)
@ -1137,7 +1172,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())
@ -1154,7 +1189,6 @@ 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)

View File

@ -1,9 +1,16 @@
import argparse
import copy
import datetime
import json
import logging
import shlex
import discord
import pytz
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 +41,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)
@ -217,12 +224,12 @@ class PollControls:
else:
return
def item_fct(item):
def item_fct(i,item):
return f':black_small_square: **{item["short"]}**: {item["name"]}'
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,73 +249,132 @@ 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('-multiple_choice', '-mc', default='1')
parser.add_argument('-roles', '-r', default='all')
parser.add_argument('-weights', '-w', default='none')
parser.add_argument('-deadline', '-d', default='0')
parser.add_argument('-anonymous', '-a', 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_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.deadline)
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()
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')
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)
await poll.set_short()
await poll.set_preparation()
await poll.set_anonymous()
await poll.set_multiple_choice()
if poll.reaction:
await poll.set_options_reaction()
else:
await poll.set_options_traditional()
await poll.set_multiple_choice()
await poll.set_roles()
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)
await poll.set_short()
await poll.set_anonymous()
await poll.set_multiple_choice()
if poll.reaction:
await poll.set_options_reaction()
else:
await poll.set_options_traditional()
await poll.set_multiple_choice()
await poll.set_roles()
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
@ -460,8 +526,67 @@ class PollControls:
)
return
# no rights, terminate function
# info
member = server.get_member(user_id)
if emoji == '':
is_open = await p.is_open()
embed = discord.Embed(title=f"Info for the {'CLOSED ' if not is_open else ''}poll \"{p.name}\"",
description='', color=SETTINGS.color)
embed.set_author(name=f" >> {p.short}", icon_url=SETTINGS.author_icon)
# vote rights
vote_rights = await p.has_required_role(member)
embed.add_field(name=f'{"Can you vote?" if is_open else "Could you vote?"}',
value=f'{"" if vote_rights else ""}', inline=False)
# edit rights
edit_rights = False
if str(member.id) == str(p.author):
edit_rights = True
elif member.server_permissions.manage_server:
edit_rights = True
else:
result = await self.bot.db.config.find_one({'_id': str(server.id)})
if result and result.get('admin_role') in [r.name for r in member.roles]:
edit_rights = True
embed.add_field(name='Can you manage the poll?', value=f'{"" if edit_rights else ""}', inline=False)
# choices
choices = 'You have not voted yet.' if vote_rights else 'You can\'t vote in this poll.'
if user.id in p.votes:
if p.votes[user.id]['choices'].__len__() > 0:
choices = ', '.join([p.options_reaction[c] for c in p.votes[user.id]['choices']])
embed.add_field(name=f'{"Your current votes (can be changed as long as the poll is open):" if is_open else "Your final votes:"}',
value=choices, inline=False)
# weight
if vote_rights:
weight = 1
if p.weights_roles.__len__() > 0:
valid_weights = [p.weights_numbers[p.weights_roles.index(r)] for r in
list(set([n.name for n in member.roles]).intersection(set(p.weights_roles)))]
if valid_weights.__len__() > 0:
weight = max(valid_weights)
else:
weight = 'You can\'t vote in this poll.'
embed.add_field(name='Weight of your votes:', value=weight, inline=False)
# time left
deadline = p.get_duration_with_tz()
if not is_open:
time_left = 'This poll is closed.'
elif deadline == 0:
time_left = 'Until manually closed.'
else:
time_left = str(deadline-datetime.datetime.utcnow().replace(tzinfo=pytz.utc)).split('.', 2)[0]
embed.add_field(name='Time left in the poll:', value=time_left, inline=False)
await self.bot.send_message(user, embed=embed)
return
# Assume: User wants to vote with reaction
# no rights, terminate function
if not await p.has_required_role(member):
await self.bot.remove_reaction(message, emoji, user)
await self.bot.send_message(user, f'You are not allowed to vote in this poll. Only users with '
@ -477,7 +602,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:

View File

@ -1,6 +1,10 @@
import time
import discord
from essentials.settings import SETTINGS
from utils.paginator import embed_list_paginated
async def get_pre(bot, message):
'''Gets the prefix for a message.'''
@ -107,13 +111,22 @@ async def ask_for_channel(bot, server, message):
return False
# otherwise ask for a channel
text = 'Polls are bound to a specific channel on a server. Please select the channel for this poll by typing the corresponding number.\n'
i = 1
text = 'Polls are bound to a specific channel on a server. Please select the channel for this poll by typing the corresponding number.\n'
for name in [c.name for c in channel_list]:
text += f'\n**{i}** - {name}'
i += 1
to_add = f'\n**{i}** - {name}'
# check if length doesn't exceed allowed maximum or split it into multiple messages
if text.__len__() + to_add.__len__() > 2048:
embed = discord.Embed(title="Select a channel", description=text, color=SETTINGS.color)
server_msg = await bot.say(embed=embed)
await bot.say(embed=embed)
text = 'Polls are bound to a specific channel on a server. Please select the channel for this poll by typing the corresponding number.\n'
else:
text += to_add
i += 1
embed = discord.Embed(title="Select a channel", description=text, color=SETTINGS.color)
await bot.say(embed=embed)
valid_reply = False
nr = 1

View File

@ -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

View File

@ -53,7 +53,7 @@ async def on_ready():
bot.db = mongo.pollmaster
bot.session = aiohttp.ClientSession()
print(bot.db)
await bot.change_presence(game=discord.Game(name=f'V2 IS HERE >> pm!help'))
await bot.change_presence(game=discord.Game(name=f'pm!help - v2.1 is live!'))
# check discord server configs
try:

View File

@ -1,4 +1,4 @@
aiohttp==3.5.4
aiohttp==1.0.5
async-timeout==3.0.1
attrs==18.2.0
beautifulsoup4==4.7.1

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

View File

@ -4,8 +4,9 @@ async def embed_list_paginated(bot, pre, items, item_fct, base_embed, footer_pre
# generate list
embed.title = f'{items.__len__()} entries'
text = '\n'
for item in items[start:start+per_page]:
text += item_fct(item) + '\n'
for i,item in enumerate(items[start:start+per_page]):
j = i+start
text += item_fct(j,item) + '\n'
embed.description = text
# footer text
@ -21,6 +22,7 @@ async def embed_list_paginated(bot, pre, items, item_fct, base_embed, footer_pre
# post / edit message
if msg is not None:
await bot.edit_message(msg, embed=embed)
if str(msg.channel.type) != 'private':
await bot.clear_reactions(msg)
else:
msg = await bot.say(embed=embed)