diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c66e9d5 --- /dev/null +++ b/.gitignore @@ -0,0 +1,16 @@ +# IDE Folders +\.idea/ + +# PY VENV +venv/ + +# Tokens and Passwwords +essentials/secrets\.py + +pollmaster\.log + +cogs/__pycache__/ + +essentials/__pycache__/ + +utils/__pycache__/ diff --git a/cogs/config.py b/cogs/config.py index 835fa61..eb4e060 100644 --- a/cogs/config.py +++ b/cogs/config.py @@ -1,26 +1,15 @@ import logging - -import discord from discord.ext import commands -class Config: - +class Config(commands.Cog): def __init__(self, bot): self.bot = bot - @commands.command(pass_context=True) - @commands.has_permissions(manage_server=True) + @commands.command() + @commands.has_permissions(manage_guild=True) async def prefix(self, ctx, *, pre): '''Set a custom prefix for the server.''' - server = ctx.message.server - # result = await self.bot.db.config.find_one({'_id': str(server_id)}) - # print(f'result: `{result}`') - # if not result: - # await self.bot.db.config.insert_one({'_id': str(server_id)}, {'$set': {'_id': str(server_id), 'prefix': str(pre)}}) - # self.bot.say(f'The server prefix has been set to `{pre}` Use `{pre}prefix ` to change it again.') - # return - #result['prefix'] = str(pre) - + server = ctx.message.guild if pre.endswith('\w'): pre = pre[:-2]+' ' msg = f'The server prefix has been set to `{pre}` Use `{pre}prefix ` to change it again.' @@ -30,51 +19,51 @@ class Config: await self.bot.db.config.update_one({'_id': str(server.id)}, {'$set': {'prefix': str(pre)}}, upsert=True) self.bot.pre[str(server.id)] = str(pre) - await self.bot.say(msg) + await ctx.send(msg) - @commands.command(pass_context=True) - @commands.has_permissions(manage_server=True) + @commands.command() + @commands.has_permissions(manage_guild=True) async def adminrole(self, ctx, *, role=None): '''Set or show the Admin Role. Members with this role can create polls and manage ALL polls. Parameter: (optional)''' - server = ctx.message.server + server = ctx.message.guild if not role: result = await self.bot.db.config.find_one({'_id': str(server.id)}) if result and result.get('admin_role'): - await self.bot.say(f'The admin role restricts which users are able to create and manage ALL polls on this server. \n' + await ctx.send(f'The admin role restricts which users are able to create and manage ALL polls on this server. \n' f'The current admin role is `{result.get("admin_role")}`. ' f'To change it type `{result.get("prefix")}adminrole `') else: - await self.bot.say(f'The admin role restricts which users are able to create and manage ALL polls on this server. \n' + await ctx.send(f'The admin role restricts which users are able to create and manage ALL polls on this server. \n' f'No admin role set. ' f'To set one type `{result["prefix"]}adminrole `') elif role in [r.name for r in server.roles]: await self.bot.db.config.update_one({'_id': str(server.id)}, {'$set': {'admin_role': str(role)}}, upsert=True) - await self.bot.say(f'Server role `{role}` can now manage all polls.') + await ctx.send(f'Server role `{role}` can now manage all polls.') else: - await self.bot.say(f'Server role `{role}` not found.') + await ctx.send(f'Server role `{role}` not found.') - @commands.command(pass_context=True) - @commands.has_permissions(manage_server=True) + @commands.command() + @commands.has_permissions(manage_guild=True) async def userrole(self, ctx, *, role=None): '''Set or show the User Role. Members with this role can create polls and manage their own polls. Parameter: (optional)''' - server = ctx.message.server + server = ctx.message.guild if not role: result = await self.bot.db.config.find_one({'_id': str(server.id)}) if result and result.get('user_role'): - await self.bot.say(f'The user role restricts which users are able to create and manage their own polls. \n' + await ctx.send(f'The user role restricts which users are able to create and manage their own polls. \n' f'The current user role is `{result.get("user_role")}`. ' f'To change it type `{result.get("prefix")}userrole `') else: - await self.bot.say(f'The user role restricts which users are able to create and manage their own polls. \n' + await ctx.send(f'The user role restricts which users are able to create and manage their own polls. \n' f'No user role set. ' f'To set one type `{result.get("prefix")}userrole `') elif role in [r.name for r in server.roles]: await self.bot.db.config.update_one({'_id': str(server.id)}, {'$set': {'user_role': str(role)}}, upsert=True) - await self.bot.say(f'Server role `{role}` can now create and manage their own polls.') + await ctx.send(f'Server role `{role}` can now create and manage their own polls.') else: - await self.bot.say(f'Server role `{role}` not found.') + await ctx.send(f'Server role `{role}` not found.') def setup(bot): global logger diff --git a/cogs/db_api.py b/cogs/db_api.py index 33ec75f..9a6426f 100644 --- a/cogs/db_api.py +++ b/cogs/db_api.py @@ -2,10 +2,12 @@ import dbl import asyncio import logging +from discord.ext import commands + from essentials.settings import SETTINGS -class DiscordBotsOrgAPI: +class DiscordBotsOrgAPI(commands.Cog): """Handles interactions with the discordbots.org API""" def __init__(self, bot): @@ -22,7 +24,7 @@ class DiscordBotsOrgAPI: try: if SETTINGS.mode == 'production': await self.dblpy.post_server_count() - logger.info('posted server count ({})'.format(len(self.bot.servers))) + logger.info('posted server count ({})'.format(len(self.bot.guilds))) except Exception as e: logger.exception('Failed to post server count\n{}: {}'.format(type(e).__name__, e)) await asyncio.sleep(1800) diff --git a/cogs/help.py b/cogs/help.py index ffea820..51664e9 100644 --- a/cogs/help.py +++ b/cogs/help.py @@ -1,3 +1,4 @@ +import asyncio import logging import discord @@ -7,37 +8,38 @@ from essentials.multi_server import get_server_pre, ask_for_server from essentials.settings import SETTINGS -class Help: +class Help(commands.Cog): def __init__(self, bot): self.bot = bot self.pages = ['šŸ ', 'šŸ†•', 'šŸ”', 'šŸ•¹', 'šŸ› ', 'šŸ’–'] - async def embed_list_reaction_handler(self, page, pre, msg=None): + async def embed_list_reaction_handler(self, ctx, page, pre, msg=None): embed = self.get_help_embed(page, pre) if msg is None: - msg = await self.bot.say(embed=embed) + msg = await ctx.send(embed=embed) # add reactions for emoji in self.pages: - await self.bot.add_reaction(msg, emoji) + await msg.add_reaction(emoji) else: - await self.bot.edit_message(msg, embed=embed) + await msg.edit(embed=embed) - # wait for reactions (2 minutes) - def check(reaction, user): - return reaction.emoji if user != self.bot.user else False + # wait for reactions (3 minutes) + def check(rct, usr): + return True if usr != self.bot.user and str(rct.emoji) in self.pages and rct.message.id == msg.id else False - res = await self.bot.wait_for_reaction(emoji=self.pages, message=msg, timeout=180, check=check) - - # redirect on reaction - if res is None: - await self.bot.delete_message(msg) + try: + reaction, user = await self.bot.wait_for('reaction_add', timeout=180, check=check) + except asyncio.TimeoutError: + await msg.delete() return None else: - if str(res.reaction.message.channel.type) != 'private': - await self.bot.remove_reaction(res.reaction.message, res.reaction.emoji, res.user) - return res + if isinstance(reaction.message.channel, discord.TextChannel): + await reaction.message.remove_reaction(reaction.emoji, user) + return reaction + + def get_help_embed(self, page, pre): @@ -202,22 +204,22 @@ class Help: return embed - @commands.command(pass_context=True) + @commands.command() async def help(self, ctx, *, topic=None): server = await ask_for_server(self.bot, ctx.message) pre = await get_server_pre(self.bot, server) - res = 1 - while res is not None: - if res == 1: + rct = 1 + while rct is not None: + if rct == 1: page = 'šŸ ' msg = None else: - page = res.reaction.emoji - msg = res.reaction.message - res = await self.embed_list_reaction_handler(page, pre, msg) + page = rct.emoji + msg = rct.message + rct = await self.embed_list_reaction_handler(ctx, page, pre, msg) # print(res.user, res.reaction, res.reaction.emoji) # cleanup - await self.bot.delete_message(ctx.message) + await ctx.message.delete() def setup(bot): diff --git a/cogs/poll.py b/cogs/poll.py index e46c2f9..3932477 100644 --- a/cogs/poll.py +++ b/cogs/poll.py @@ -4,18 +4,18 @@ import datetime import logging import os import re -from uuid import uuid4 -from string import ascii_lowercase, printable - import dateparser import pytz import regex +import discord + +from uuid import uuid4 +from string import ascii_lowercase from matplotlib import rcParams from matplotlib.afm import AFM from pytz import UnknownTimeZoneError from unidecode import unidecode -import discord from essentials.multi_server import get_pre from essentials.exceptions import * from essentials.settings import SETTINGS @@ -32,8 +32,9 @@ with open(afm_fname, 'rb') as fh: ## A-Z Emojis for Discord AZ_EMOJIS = [(b'\\U0001f1a'.replace(b'a', bytes(hex(224 + (6 + i))[2:], "utf-8"))).decode("unicode-escape") for i in range(26)] -class Poll: + +class Poll: def __init__(self, bot, ctx=None, server=None, channel=None, load=False): self.bot = bot @@ -41,7 +42,7 @@ class Poll: if not load and ctx: if server is None: - server = ctx.message.server + server = ctx.message.guild if channel is None: channel = ctx.message.channel @@ -97,41 +98,45 @@ class Poll: await self.save_to_db() return self.active - async def wizard_says(self, text, footer=True): + async def wizard_says(self, ctx, text, footer=True): embed = discord.Embed(title="Poll creation Wizard", description=text, color=SETTINGS.color) - if footer: embed.set_footer(text="Type `stop` to cancel the wizard.") - return await self.bot.say(embed=embed) + if footer: + embed.set_footer(text="Type `stop` to cancel the wizard.") + return await ctx.send(embed=embed) async def wizard_says_edit(self, message, text, add=False): if add and message.embeds.__len__() > 0: - text = message.embeds[0]['description'] + text + text = message.embeds[0].description + text embed = discord.Embed(title="Poll creation Wizard", description=text, color=SETTINGS.color) embed.set_footer(text="Type `stop` to cancel the wizard.") - return await self.bot.edit_message(message, embed=embed) + return await message.edit(embed=embed) async def add_error(self, message, error): text = '' if message.embeds.__len__() > 0: - text = message.embeds[0]['description'] + '\n\n:exclamation: ' + error + text = message.embeds[0].description + '\n\n:exclamation: ' + error return await self.wizard_says_edit(message, text) async def add_vaild(self, message, string): text = '' if message.embeds.__len__() > 0: - text = message.embeds[0]['description'] + '\n\nāœ… ' + string + text = message.embeds[0].description + '\n\nāœ… ' + string return await self.wizard_says_edit(message, text) - async def get_user_reply(self): + async def get_user_reply(self, ctx): """Pre-parse user input for wizard""" - reply = await self.bot.wait_for_message(author=self.author) + def check(m): + return m.author == self.author + + reply = await self.bot.wait_for('message', check=check) if reply and reply.content: if reply.content.startswith(await get_pre(self.bot, reply)): - await self.wizard_says(f'You can\'t use bot commands during the Poll Creation Wizard.\n' + await self.wizard_says(ctx, f'You can\'t use bot commands during the Poll Creation Wizard.\n' f'Stopping the Wizard and then executing the command:\n`{reply.content}`', footer=False) raise StopWizard elif reply.content.lower() == 'stop': - await self.wizard_says('Poll Wizard stopped.', footer=False) + await self.wizard_says(ctx, 'Poll Wizard stopped.', footer=False) raise StopWizard else: @@ -149,7 +154,7 @@ class Poll: raise InvalidInput return string - async def set_name(self, force=None): + async def set_name(self, ctx, force=None): """Set the Question / Name of the Poll.""" async def get_valid(in_reply): if not in_reply: @@ -172,7 +177,7 @@ class Poll: text = ("**What is the question of your poll?**\n" "Try to be descriptive without writing more than one sentence.") - message = await self.wizard_says(text) + message = await self.wizard_says(ctx, text) while True: try: @@ -180,14 +185,14 @@ class Poll: reply = force force = None else: - reply = await self.get_user_reply() + reply = await self.get_user_reply(ctx) self.name = await get_valid(reply) await self.add_vaild(message, self.name) break except InvalidInput: await self.add_error(message, '**Keep the name between 3 and 200 valid characters**') - async def set_short(self, force=None): + async def set_short(self, ctx, force=None): """Set the label of the Poll.""" async def get_valid(in_reply): if not in_reply: @@ -214,7 +219,7 @@ class Poll: text = """Great. **Now type a unique one word identifier, a label, for your poll.** This label will be used to refer to the poll. Keep it short and significant.""" - message = await self.wizard_says(text) + message = await self.wizard_says(ctx, text) while True: try: @@ -222,7 +227,7 @@ class Poll: reply = force force = None else: - reply = await self.get_user_reply() + reply = await self.get_user_reply(ctx) self.short = await get_valid(reply) await self.add_vaild(message, self.short) break @@ -235,7 +240,7 @@ class Poll: f'**The label `{reply}` is not unique on this server. Choose a different one!**') - async def set_preparation(self, force=None): + async def set_preparation(self, ctx, force=None): """Set the preparation conditions for the Poll.""" async def get_valid(in_reply): if not in_reply: @@ -278,7 +283,7 @@ class Poll: "it manually. **Type `0` to activate it manually or tell me when you want to activate it** by " "typing an absolute or relative date. You can specify a timezone if you want.\n" "Examples: `in 2 days`, `next week CET`, `may 3rd 2019`, `9.11.2019 9pm EST` ") - message = await self.wizard_says(text) + message = await self.wizard_says(ctx, text) while True: try: @@ -286,7 +291,7 @@ class Poll: reply = force force = None else: - reply = await self.get_user_reply() + reply = await self.get_user_reply(ctx) dt = await get_valid(reply) self.activation = dt if self.activation == 0: @@ -303,7 +308,7 @@ class Poll: except DateOutOfRange as e: await self.add_error(message, f'**{e.date.strftime("%d-%b-%Y %H:%M")} is in the past.**') - async def set_anonymous(self, force=None): + async def set_anonymous(self, ctx, force=None): """Determine if poll is anonymous.""" async def get_valid(in_reply): if not in_reply: @@ -334,7 +339,7 @@ class Poll: "An anonymous poll has the following effects:\n" "šŸ”¹ You will never see who voted for which option\n" "šŸ”¹ Once the poll is closed, you will see who participated (but not their choice)") - message = await self.wizard_says(text) + message = await self.wizard_says(ctx, text) while True: try: @@ -342,40 +347,14 @@ class Poll: reply = force force = None else: - reply = await self.get_user_reply() + reply = await self.get_user_reply(ctx) self.anonymous = await get_valid(reply) await self.add_vaild(message, f'{"Yes" if self.anonymous else "No"}') break except InvalidInput: await self.add_error(message, '**You can only answer with `yes` | `1` or `no` | `0`!**') - - # async def set_reaction(self, force=None): - # ''' Currently everything is reaction, this is not needed''' - # if force is not None and force in [True, False]: - # self.reaction = force - # return - # if self.stopped: return - # text = """**Do you want your users to vote by adding reactions to the poll? Type `yes` or `no`.** - # Reaction voting typically has the following properties: - # :small_blue_diamond: Voting is quick and painless (no typing required) - # :small_blue_diamond: Multiple votes are possible - # :small_blue_diamond: Not suited for a large number of options - # :small_blue_diamond: Not suited for long running polls""" - # message = await self.wizard_says(text) - # - # reply = '' - # while reply not in ['yes', 'no']: - # if reply != '': - # await self.add_error(message, '**You can only answer with `yes` or `no`!**') - # reply = await self.get_user_reply() - # if self.stopped: break - # if isinstance(reply, str): reply = reply.lower() - # - # self.reaction = reply == 'yes' - # return self.reaction - - async def set_multiple_choice(self, force=None): + async def set_multiple_choice(self, ctx, force=None): """Determine if poll is multiple choice.""" async def get_valid(in_reply): if not in_reply: @@ -406,7 +385,7 @@ class Poll: "\n" "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) + message = await self.wizard_says(ctx, text) while True: try: @@ -414,7 +393,7 @@ class Poll: reply = force force = None else: - reply = await self.get_user_reply() + reply = await self.get_user_reply(ctx) self.multiple_choice = await get_valid(reply) await self.add_vaild(message, f'{self.multiple_choice if self.multiple_choice > 0 else "No Limit"}') break @@ -426,7 +405,7 @@ class Poll: await self.add_error(message, '**You can\'t have more choices than options.**') - async def set_options_reaction(self, force=None): + async def set_options_reaction(self, ctx, force=None): """Set the answers / options of the Poll.""" async def get_valid(in_reply): if not in_reply: @@ -482,7 +461,7 @@ class Poll: "\n" "Example for custom options:\n" "**apple juice, banana ice cream, kiwi slices** ") - message = await self.wizard_says(text) + message = await self.wizard_says(ctx, text) while True: try: @@ -490,7 +469,7 @@ class Poll: reply = force force = None else: - reply = await self.get_user_reply() + reply = await self.get_user_reply(ctx) options = await get_valid(reply) self.options_reaction_default = False if isinstance(options, int): @@ -510,7 +489,7 @@ class Poll: '**You need more than 1 option! Type them in a comma separated list.**') - async def set_roles(self, force=None): + async def set_roles(self, ctx, force=None): """Set role restrictions for the Poll.""" async def get_valid(in_reply, roles): n_roles = roles.__len__() @@ -563,7 +542,7 @@ class Poll: "Type `0`, `all` or `everyone` to have no restrictions.\n" "Type out the role names, separated by a comma, to restrict voting to specific roles:\n" "`moderators, Editors, vips` (hint: role names are case sensitive!)\n") - message = await self.wizard_says(text) + message = await self.wizard_says(ctx, text) while True: try: @@ -571,7 +550,7 @@ class Poll: reply = force force = None else: - reply = await self.get_user_reply() + reply = await self.get_user_reply(ctx) self.roles = await get_valid(reply, roles) await self.add_vaild(message, f'{", ".join(self.roles)}') break @@ -584,7 +563,7 @@ class Poll: except InvalidRoles as e: await self.add_error(message, f'**The following roles are invalid: {e.roles}**') - async def set_weights(self, force=None): + async def set_weights(self, ctx, force=None): """Set role weights for the poll.""" async def get_valid(in_reply, server_roles): if not in_reply: @@ -626,7 +605,7 @@ class Poll: "A weight for the role `moderator` of `2` for example will automatically count the votes of all the moderators twice.\n" "To assign weights type the role, followed by a colon, followed by the weight like this:\n" "`moderator: 2, newbie: 0.5`") - message = await self.wizard_says(text) + message = await self.wizard_says(ctx, text) while True: try: @@ -634,7 +613,7 @@ class Poll: reply = force force = None else: - reply = await self.get_user_reply() + reply = await self.get_user_reply(ctx) w_n = await get_valid(reply, self.server.roles) self.weights_roles = w_n[0] self.weights_numbers = w_n[1] @@ -654,7 +633,7 @@ class Poll: except WrongNumberOfArguments: await self.add_error(message, f'**Not every role has a weight assigned.**') - async def set_duration(self, force=None): + async def set_duration(self, ctx, force=None): """Set the duration /deadline for the Poll.""" async def get_valid(in_reply): if not in_reply: @@ -693,7 +672,7 @@ class Poll: "You can specify a timezone if you want.\n" "\n" "Examples: `in 6 hours` or `next week CET` or `aug 15th 5:10` or `15.8.2019 11pm EST`") - message = await self.wizard_says(text) + message = await self.wizard_says(ctx, text) while True: try: @@ -701,7 +680,7 @@ class Poll: reply = force force = None else: - reply = await self.get_user_reply() + reply = await self.get_user_reply(ctx) dt = await get_valid(reply) self.duration = dt if self.duration == 0: @@ -807,15 +786,15 @@ class Poll: for user_id in self.votes: member = self.server.get_member(user_id) - if self.votes[user_id]['choices'].__len__() == 0: + if self.votes[str(member.id)]['choices'].__len__() == 0: continue name = member.nick if not name: name = member.name export += f'\n{name}' - if self.votes[user_id]['weight'] != 1: - export += f' (weight: {self.votes[user_id]["weight"]})' - export += ': ' + ', '.join([self.options_reaction[c] for c in self.votes[user_id]['choices']]) + if self.votes[str(member.id)]['weight'] != 1: + export += f' (weight: {self.votes[str(user_id)]["weight"]})' + export += ': ' + ', '.join([self.options_reaction[c] for c in self.votes[str(user_id)]['choices']]) export += '\n' else: export += '--------------------------------------------\n' \ @@ -824,15 +803,15 @@ class Poll: for user_id in self.votes: member = self.server.get_member(user_id) - if self.votes[user_id]['choices'].__len__() == 0: + if self.votes[str(user_id)]['choices'].__len__() == 0: continue name = member.nick if not name: name = member.name export += f'\n{name}' - if self.votes[user_id]['weight'] != 1: - export += f' (weight: {self.votes[user_id]["weight"]})' - # export += ': ' + ', '.join([self.options_reaction[c] for c in self.votes[user_id]['choices']]) + if self.votes[str(user_id)]['weight'] != 1: + export += f' (weight: {self.votes[str(user_id)]["weight"]})' + # export += ': ' + ', '.join([self.options_reaction[c] for c in self.votes[str(user_id)]['choices']]) export += '\n' export += ('--------------------------------------------\n' @@ -859,11 +838,10 @@ class Poll: async def from_dict(self, d): self.id = d['_id'] - self.server = self.bot.get_server(str(d['server_id'])) - self.channel = self.bot.get_channel(str(d['channel_id'])) - # self.author = await self.bot.get_user_info(str(d['author'])) + self.server = self.bot.get_guild(int(d['server_id'])) + self.channel = self.bot.get_channel(int(d['channel_id'])) if self.server: - self.author = self.server.get_member(d['author']) + self.author = self.server.get_member(int(d['author'])) else: self.author = None self.name = d['name'] @@ -911,7 +889,7 @@ class Poll: @staticmethod async def load_from_db(bot, server_id, short, ctx=None, ): - query = await bot.db.polls.find_one({'server_id': str(server_id), 'short': str(short)}) + query = await bot.db.polls.find_one({'server_id': str(server_id), 'short': short}) if query is not None: p = Poll(bot, ctx, load=True) await p.from_dict(query) @@ -1018,34 +996,25 @@ 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='React with ā” to get info. It is not a vote option.') return embed - async def post_embed(self, destination=None): - if destination is None: - msg = await self.bot.say(embed=await self.generate_embed()) - else: - msg = await self.bot.send_message(destination=destination, embed= await self.generate_embed()) + async def post_embed(self, destination): + msg = await destination.send(embed=await self.generate_embed()) if self.reaction and await self.is_open() and await self.is_active(): if self.options_reaction_default: for r in self.options_reaction: - await self.bot.add_reaction( - msg, - r - ) - await self.bot.add_reaction(msg, 'ā”') + await msg.add_reaction(r) + await msg.add_reaction('ā”') return msg else: for i, r in enumerate(self.options_reaction): - await self.bot.add_reaction( - msg, - AZ_EMOJIS[i] - ) - await self.bot.add_reaction(msg, 'ā”') + await msg.add_reaction(AZ_EMOJIS[i]) + await msg.add_reaction('ā”') return msg elif not await self.is_open(): - await self.bot.add_reaction(msg, 'ā”') - await self.bot.add_reaction(msg, 'šŸ“Ž') + await msg.add_reaction('ā”') + await msg.add_reaction('šŸ“Ž') else: return msg @@ -1135,8 +1104,8 @@ class Poll: async def vote(self, user, option, message, lock): if not await self.is_open(): # refresh to show closed poll - await self.bot.edit_message(message, embed=await self.generate_embed()) - await self.bot.clear_reactions(message) + await message.edit(embed=await self.generate_embed()) + await message.clear_reactions() return elif not await self.is_active(): return @@ -1153,9 +1122,9 @@ class Poll: weight = max(valid_weights) if str(user.id) not in self.votes: - self.votes[user.id] = {'weight': weight, 'choices': []} + self.votes[str(user.id)] = {'weight': weight, 'choices': []} else: - self.votes[user.id]['weight'] = weight + self.votes[str(user.id)]['weight'] = weight if self.options_reaction_default: if option in self.options_reaction: @@ -1166,23 +1135,23 @@ class Poll: if choice != 'invalid': # if self.multiple_choice != 1: # more than 1 choice (0 = no limit) - if choice in self.votes[user.id]['choices']: + if choice in self.votes[str(user.id)]['choices']: if self.anonymous: # anonymous multiple choice -> can't unreact so we toggle with react logger.warning("Unvoting, should not happen for non anon polls.") await self.unvote(user, option, message, lock) # refresh_poll = False else: - if self.multiple_choice > 0 and self.votes[user.id]['choices'].__len__() >= self.multiple_choice: + if self.multiple_choice > 0 and self.votes[str(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) + await user.send(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'])) + self.votes[str(user.id)]['choices'].append(choice) + self.votes[str(user.id)]['choices'] = list(set(self.votes[str(user.id)]['choices'])) # else: # if [choice] == self.votes[user.id]['choices']: # # refresh_poll = False @@ -1198,26 +1167,9 @@ class Poll: return # commit - #if lock._waiters.__len__() == 0: - # updating DB, clearing cache and refresh if necessary await self.save_to_db() - # await self.bot.poll_refresh_q.put_unique_id( - # {'id': self.id, 'msg': message, 'sid': self.server.id, 'label': self.short, 'lock': lock}) - # if self.bot.poll_cache.get(str(self.server.id) + self.short): - # del self.bot.poll_cache[str(self.server.id) + self.short] - # refresh - # if refresh_poll: - # edit message if there is a real change - # await self.bot.edit_message(message, embed=await self.generate_embed()) - # self.bot.poll_refresh_q.append(str(self.id)) - #else: - # cache the poll until the queue is empty - #self.bot.poll_cache[str(self.server.id)+self.short] = self - # await self.bot.edit_message(message, embed=await self.generate_embed()) - asyncio.ensure_future(self.bot.edit_message(message, embed=await self.generate_embed())) + asyncio.ensure_future(message.edit(embed=await self.generate_embed())) - # if refresh_poll: - # await self.bot.poll_refresh_q.put_unique_id({'id': self.id, 'msg': message, 'sid': self.server.id, 'label': self.short, 'lock': lock}) @@ -1225,13 +1177,15 @@ class Poll: async def unvote(self, user, option, message, lock): if not await self.is_open(): # refresh to show closed poll - await self.bot.edit_message(message, embed=await self.generate_embed()) - await self.bot.clear_reactions(message) + await message.edit(embed=await self.generate_embed()) + if not isinstance(message.channel, discord.abc.PrivateChannel): + await message.clear_reactions() return elif not await self.is_active(): return - if str(user.id) not in self.votes: return + if str(user.id) not in self.votes: + return choice = 'invalid' @@ -1242,24 +1196,14 @@ class Poll: if option in AZ_EMOJIS: choice = AZ_EMOJIS.index(option) - if choice != 'invalid' and choice in self.votes[user.id]['choices']: + if choice != 'invalid' and choice in self.votes[str(user.id)]['choices']: try: - self.votes[user.id]['choices'].remove(choice) + self.votes[str(user.id)]['choices'].remove(choice) await self.save_to_db() - asyncio.ensure_future(self.bot.edit_message(message, embed=await self.generate_embed())) - # if lock._waiters.__len__() == 0: - # # updating DB, clearing cache and refreshing message - # await self.save_to_db() - # await self.bot.poll_refresh_q.put_unique_id( - # {'id': self.id, 'msg': message, 'sid': self.server.id, 'label': self.short, 'lock': lock}) - # if self.bot.poll_cache.get(str(self.server.id) + self.short): - # del self.bot.poll_cache[str(self.server.id) + self.short] - # # await self.bot.edit_message(message, embed=await self.generate_embed()) - # else: - # # cache the poll until the queue is empty - # self.bot.poll_cache[str(self.server.id) + self.short] = self + asyncio.ensure_future(message.edit(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 03777d1..4810682 100644 --- a/cogs/poll_controls.py +++ b/cogs/poll_controls.py @@ -1,13 +1,9 @@ import argparse import asyncio -import copy import datetime -import json import logging import re import shlex -import string -import traceback import discord import pytz @@ -26,13 +22,25 @@ from essentials.exceptions import StopWizard AZ_EMOJIS = [(b'\\U0001f1a'.replace(b'a', bytes(hex(224 + (6 + i))[2:], "utf-8"))).decode("unicode-escape") for i in range(26)] -class PollControls: +class PollControls(commands.Cog): def __init__(self, bot): self.bot = bot - self.bot.loop.create_task(self.close_polls()) + #self.bot.loop.create_task(self.close_polls()) self.ignore_next_removed_reaction = {} + # + # # General Methods + + def get_label(self, message : discord.Message): + label = None + if message and message.embeds: + embed = message.embeds[0] + label_object = embed.author + if label_object: + label_full = label_object.name + if label_full and label_full.startswith('>> '): + label = label_full[3:] + return label - # General Methods async def close_polls(self): """This function runs every 60 seconds to schedule prepared polls and close expired polls""" while True: @@ -46,8 +54,8 @@ class PollControls: continue if p.active: try: - await self.bot.send_message(p.channel, 'This poll has been scheduled and is active now!') - await p.post_embed(destination=p.channel) + await p.channel.send('This poll has been scheduled and is active now!') + await p.post_embed(p.channel) except: continue @@ -60,8 +68,8 @@ class PollControls: continue if not p.open: try: - await self.bot.send_message(p.channel, 'This poll has reached the deadline and is closed!') - await p.post_embed(destination=p.channel) + await p.channel.send('This poll has reached the deadline and is closed!') + await p.post_embed(p.channel) except: continue except AttributeError as ae: @@ -78,15 +86,15 @@ class PollControls: await asyncio.sleep(30) def get_lock(self, server_id): - if not self.bot.locks.get(str(server_id)): + if not self.bot.locks.get(server_id): self.bot.locks[server_id] = asyncio.Lock() - return self.bot.locks.get(str(server_id)) + return self.bot.locks.get(server_id) async def is_admin_or_creator(self, ctx, server, owner_id, error_msg=None): member = server.get_member(ctx.message.author.id) if member.id == owner_id: return True - elif member.server_permissions.manage_server: + elif member.guild_permissions.manage_guild: return True else: result = await self.bot.db.config.find_one({'_id': str(server.id)}) @@ -94,7 +102,7 @@ class PollControls: return True else: if error_msg is not None: - await self.bot.send_message(ctx.message.author, error_msg) + await ctx.send(ctx.message.author, error_msg) return False async def say_error(self, ctx, error_text, footer_text=None): @@ -102,17 +110,17 @@ class PollControls: 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) + await ctx.send(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.author_icon) if footer_text is not None: embed.set_footer(text=footer_text) - await self.bot.say(embed=embed) + await ctx.send(embed=embed) - # Commands - @commands.command(pass_context=True) + # # Commands + @commands.command() async def activate(self, ctx, *, short=None): """Activate a prepared poll. Parameter: