Migration to rewrite
some performance chances with message caching/fetching and user fetching
This commit is contained in:
parent
8d183eeb54
commit
dae2a6e494
8
.gitignore
vendored
Normal file
8
.gitignore
vendored
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
# IDE Folders
|
||||||
|
\.idea/
|
||||||
|
|
||||||
|
# PY VENV
|
||||||
|
venv/
|
||||||
|
|
||||||
|
# Tokens and Passwwords
|
||||||
|
essentials/secrets\.py
|
||||||
@ -1,26 +1,15 @@
|
|||||||
import logging
|
import logging
|
||||||
|
|
||||||
import discord
|
|
||||||
from discord.ext import commands
|
from discord.ext import commands
|
||||||
|
|
||||||
class Config:
|
class Config(commands.Cog):
|
||||||
|
|
||||||
def __init__(self, bot):
|
def __init__(self, bot):
|
||||||
self.bot = bot
|
self.bot = bot
|
||||||
|
|
||||||
@commands.command(pass_context=True)
|
@commands.command()
|
||||||
@commands.has_permissions(manage_server=True)
|
@commands.has_permissions(manage_guild=True)
|
||||||
async def prefix(self, ctx, *, pre):
|
async def prefix(self, ctx, *, pre):
|
||||||
'''Set a custom prefix for the server.'''
|
'''Set a custom prefix for the server.'''
|
||||||
server = ctx.message.server
|
server = ctx.message.guild
|
||||||
# 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 <prefix>` to change it again.')
|
|
||||||
# return
|
|
||||||
#result['prefix'] = str(pre)
|
|
||||||
|
|
||||||
if pre.endswith('\w'):
|
if pre.endswith('\w'):
|
||||||
pre = pre[:-2]+' '
|
pre = pre[:-2]+' '
|
||||||
msg = f'The server prefix has been set to `{pre}` Use `{pre}prefix <prefix>` to change it again.'
|
msg = f'The server prefix has been set to `{pre}` Use `{pre}prefix <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)
|
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)
|
self.bot.pre[str(server.id)] = str(pre)
|
||||||
await self.bot.say(msg)
|
await ctx.send(msg)
|
||||||
|
|
||||||
@commands.command(pass_context=True)
|
@commands.command()
|
||||||
@commands.has_permissions(manage_server=True)
|
@commands.has_permissions(manage_guild=True)
|
||||||
async def adminrole(self, ctx, *, role=None):
|
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: <role> (optional)'''
|
'''Set or show the Admin Role. Members with this role can create polls and manage ALL polls. Parameter: <role> (optional)'''
|
||||||
server = ctx.message.server
|
server = ctx.message.guild
|
||||||
|
|
||||||
if not role:
|
if not role:
|
||||||
result = await self.bot.db.config.find_one({'_id': str(server.id)})
|
result = await self.bot.db.config.find_one({'_id': str(server.id)})
|
||||||
if result and result.get('admin_role'):
|
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'The current admin role is `{result.get("admin_role")}`. '
|
||||||
f'To change it type `{result.get("prefix")}adminrole <role name>`')
|
f'To change it type `{result.get("prefix")}adminrole <role name>`')
|
||||||
else:
|
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'No admin role set. '
|
||||||
f'To set one type `{result["prefix"]}adminrole <role name>`')
|
f'To set one type `{result["prefix"]}adminrole <role name>`')
|
||||||
elif role in [r.name for r in server.roles]:
|
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.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:
|
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.command()
|
||||||
@commands.has_permissions(manage_server=True)
|
@commands.has_permissions(manage_guild=True)
|
||||||
async def userrole(self, ctx, *, role=None):
|
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: <role> (optional)'''
|
'''Set or show the User Role. Members with this role can create polls and manage their own polls. Parameter: <role> (optional)'''
|
||||||
server = ctx.message.server
|
server = ctx.message.guild
|
||||||
|
|
||||||
if not role:
|
if not role:
|
||||||
result = await self.bot.db.config.find_one({'_id': str(server.id)})
|
result = await self.bot.db.config.find_one({'_id': str(server.id)})
|
||||||
if result and result.get('user_role'):
|
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'The current user role is `{result.get("user_role")}`. '
|
||||||
f'To change it type `{result.get("prefix")}userrole <role name>`')
|
f'To change it type `{result.get("prefix")}userrole <role name>`')
|
||||||
else:
|
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'No user role set. '
|
||||||
f'To set one type `{result.get("prefix")}userrole <role name>`')
|
f'To set one type `{result.get("prefix")}userrole <role name>`')
|
||||||
elif role in [r.name for r in server.roles]:
|
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.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:
|
else:
|
||||||
await self.bot.say(f'Server role `{role}` not found.')
|
await ctx.send(f'Server role `{role}` not found.')
|
||||||
|
|
||||||
def setup(bot):
|
def setup(bot):
|
||||||
global logger
|
global logger
|
||||||
|
|||||||
@ -2,10 +2,12 @@ import dbl
|
|||||||
import asyncio
|
import asyncio
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
from discord.ext import commands
|
||||||
|
|
||||||
from essentials.settings import SETTINGS
|
from essentials.settings import SETTINGS
|
||||||
|
|
||||||
|
|
||||||
class DiscordBotsOrgAPI:
|
class DiscordBotsOrgAPI(commands.Cog):
|
||||||
"""Handles interactions with the discordbots.org API"""
|
"""Handles interactions with the discordbots.org API"""
|
||||||
|
|
||||||
def __init__(self, bot):
|
def __init__(self, bot):
|
||||||
@ -22,7 +24,7 @@ class DiscordBotsOrgAPI:
|
|||||||
try:
|
try:
|
||||||
if SETTINGS.mode == 'production':
|
if SETTINGS.mode == 'production':
|
||||||
await self.dblpy.post_server_count()
|
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:
|
except Exception as e:
|
||||||
logger.exception('Failed to post server count\n{}: {}'.format(type(e).__name__, e))
|
logger.exception('Failed to post server count\n{}: {}'.format(type(e).__name__, e))
|
||||||
await asyncio.sleep(1800)
|
await asyncio.sleep(1800)
|
||||||
|
|||||||
50
cogs/help.py
50
cogs/help.py
@ -1,3 +1,4 @@
|
|||||||
|
import asyncio
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
import discord
|
import discord
|
||||||
@ -7,37 +8,38 @@ from essentials.multi_server import get_server_pre, ask_for_server
|
|||||||
from essentials.settings import SETTINGS
|
from essentials.settings import SETTINGS
|
||||||
|
|
||||||
|
|
||||||
class Help:
|
class Help(commands.Cog):
|
||||||
|
|
||||||
def __init__(self, bot):
|
def __init__(self, bot):
|
||||||
self.bot = bot
|
self.bot = bot
|
||||||
self.pages = ['🏠', '🆕', '🔍', '🕹', '🛠', '💖']
|
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)
|
embed = self.get_help_embed(page, pre)
|
||||||
|
|
||||||
if msg is None:
|
if msg is None:
|
||||||
msg = await self.bot.say(embed=embed)
|
msg = await ctx.send(embed=embed)
|
||||||
# add reactions
|
# add reactions
|
||||||
for emoji in self.pages:
|
for emoji in self.pages:
|
||||||
await self.bot.add_reaction(msg, emoji)
|
await msg.add_reaction(emoji)
|
||||||
else:
|
else:
|
||||||
await self.bot.edit_message(msg, embed=embed)
|
await msg.edit(embed=embed)
|
||||||
|
|
||||||
# wait for reactions (2 minutes)
|
# wait for reactions (3 minutes)
|
||||||
def check(reaction, user):
|
def check(rct, usr):
|
||||||
return reaction.emoji if user != self.bot.user else False
|
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)
|
try:
|
||||||
|
reaction, user = await self.bot.wait_for('reaction_add', timeout=180, check=check)
|
||||||
# redirect on reaction
|
except asyncio.TimeoutError:
|
||||||
if res is None:
|
await msg.delete()
|
||||||
await self.bot.delete_message(msg)
|
|
||||||
return None
|
return None
|
||||||
else:
|
else:
|
||||||
if str(res.reaction.message.channel.type) != 'private':
|
if isinstance(reaction.message.channel, discord.TextChannel):
|
||||||
await self.bot.remove_reaction(res.reaction.message, res.reaction.emoji, res.user)
|
await reaction.message.remove_reaction(reaction.emoji, user)
|
||||||
return res
|
return reaction
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def get_help_embed(self, page, pre):
|
def get_help_embed(self, page, pre):
|
||||||
|
|
||||||
@ -202,22 +204,22 @@ class Help:
|
|||||||
|
|
||||||
return embed
|
return embed
|
||||||
|
|
||||||
@commands.command(pass_context=True)
|
@commands.command()
|
||||||
async def help(self, ctx, *, topic=None):
|
async def help(self, ctx, *, topic=None):
|
||||||
server = await ask_for_server(self.bot, ctx.message)
|
server = await ask_for_server(self.bot, ctx.message)
|
||||||
pre = await get_server_pre(self.bot, server)
|
pre = await get_server_pre(self.bot, server)
|
||||||
res = 1
|
rct = 1
|
||||||
while res is not None:
|
while rct is not None:
|
||||||
if res == 1:
|
if rct == 1:
|
||||||
page = '🏠'
|
page = '🏠'
|
||||||
msg = None
|
msg = None
|
||||||
else:
|
else:
|
||||||
page = res.reaction.emoji
|
page = rct.emoji
|
||||||
msg = res.reaction.message
|
msg = rct.message
|
||||||
res = await self.embed_list_reaction_handler(page, pre, msg)
|
rct = await self.embed_list_reaction_handler(ctx, page, pre, msg)
|
||||||
# print(res.user, res.reaction, res.reaction.emoji)
|
# print(res.user, res.reaction, res.reaction.emoji)
|
||||||
# cleanup
|
# cleanup
|
||||||
await self.bot.delete_message(ctx.message)
|
await ctx.message.delete()
|
||||||
|
|
||||||
|
|
||||||
def setup(bot):
|
def setup(bot):
|
||||||
|
|||||||
234
cogs/poll.py
234
cogs/poll.py
@ -4,18 +4,18 @@ import datetime
|
|||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
from uuid import uuid4
|
|
||||||
from string import ascii_lowercase, printable
|
|
||||||
|
|
||||||
import dateparser
|
import dateparser
|
||||||
import pytz
|
import pytz
|
||||||
import regex
|
import regex
|
||||||
|
import discord
|
||||||
|
|
||||||
|
from uuid import uuid4
|
||||||
|
from string import ascii_lowercase
|
||||||
from matplotlib import rcParams
|
from matplotlib import rcParams
|
||||||
from matplotlib.afm import AFM
|
from matplotlib.afm import AFM
|
||||||
from pytz import UnknownTimeZoneError
|
from pytz import UnknownTimeZoneError
|
||||||
from unidecode import unidecode
|
from unidecode import unidecode
|
||||||
|
|
||||||
import discord
|
|
||||||
from essentials.multi_server import get_pre
|
from essentials.multi_server import get_pre
|
||||||
from essentials.exceptions import *
|
from essentials.exceptions import *
|
||||||
from essentials.settings import SETTINGS
|
from essentials.settings import SETTINGS
|
||||||
@ -32,8 +32,9 @@ with open(afm_fname, 'rb') as fh:
|
|||||||
## A-Z Emojis for Discord
|
## 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
|
AZ_EMOJIS = [(b'\\U0001f1a'.replace(b'a', bytes(hex(224 + (6 + i))[2:], "utf-8"))).decode("unicode-escape") for i in
|
||||||
range(26)]
|
range(26)]
|
||||||
class Poll:
|
|
||||||
|
|
||||||
|
|
||||||
|
class Poll:
|
||||||
def __init__(self, bot, ctx=None, server=None, channel=None, load=False):
|
def __init__(self, bot, ctx=None, server=None, channel=None, load=False):
|
||||||
|
|
||||||
self.bot = bot
|
self.bot = bot
|
||||||
@ -41,7 +42,7 @@ class Poll:
|
|||||||
|
|
||||||
if not load and ctx:
|
if not load and ctx:
|
||||||
if server is None:
|
if server is None:
|
||||||
server = ctx.message.server
|
server = ctx.message.guild
|
||||||
|
|
||||||
if channel is None:
|
if channel is None:
|
||||||
channel = ctx.message.channel
|
channel = ctx.message.channel
|
||||||
@ -97,41 +98,45 @@ class Poll:
|
|||||||
await self.save_to_db()
|
await self.save_to_db()
|
||||||
return self.active
|
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)
|
embed = discord.Embed(title="Poll creation Wizard", description=text, color=SETTINGS.color)
|
||||||
if footer: embed.set_footer(text="Type `stop` to cancel the wizard.")
|
if footer:
|
||||||
return await self.bot.say(embed=embed)
|
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):
|
async def wizard_says_edit(self, message, text, add=False):
|
||||||
if add and message.embeds.__len__() > 0:
|
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 = discord.Embed(title="Poll creation Wizard", description=text, color=SETTINGS.color)
|
||||||
embed.set_footer(text="Type `stop` to cancel the wizard.")
|
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):
|
async def add_error(self, message, error):
|
||||||
text = ''
|
text = ''
|
||||||
if message.embeds.__len__() > 0:
|
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)
|
return await self.wizard_says_edit(message, text)
|
||||||
|
|
||||||
async def add_vaild(self, message, string):
|
async def add_vaild(self, message, string):
|
||||||
text = ''
|
text = ''
|
||||||
if message.embeds.__len__() > 0:
|
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)
|
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"""
|
"""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 and reply.content:
|
||||||
if reply.content.startswith(await get_pre(self.bot, reply)):
|
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}`',
|
f'Stopping the Wizard and then executing the command:\n`{reply.content}`',
|
||||||
footer=False)
|
footer=False)
|
||||||
raise StopWizard
|
raise StopWizard
|
||||||
elif reply.content.lower() == 'stop':
|
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
|
raise StopWizard
|
||||||
|
|
||||||
else:
|
else:
|
||||||
@ -149,7 +154,7 @@ class Poll:
|
|||||||
raise InvalidInput
|
raise InvalidInput
|
||||||
return string
|
return string
|
||||||
|
|
||||||
async def set_name(self, force=None):
|
async def set_name(self, ctx, force=None):
|
||||||
"""Set the Question / Name of the Poll."""
|
"""Set the Question / Name of the Poll."""
|
||||||
async def get_valid(in_reply):
|
async def get_valid(in_reply):
|
||||||
if not in_reply:
|
if not in_reply:
|
||||||
@ -172,7 +177,7 @@ class Poll:
|
|||||||
|
|
||||||
text = ("**What is the question of your poll?**\n"
|
text = ("**What is the question of your poll?**\n"
|
||||||
"Try to be descriptive without writing more than one sentence.")
|
"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:
|
while True:
|
||||||
try:
|
try:
|
||||||
@ -180,14 +185,14 @@ class Poll:
|
|||||||
reply = force
|
reply = force
|
||||||
force = None
|
force = None
|
||||||
else:
|
else:
|
||||||
reply = await self.get_user_reply()
|
reply = await self.get_user_reply(ctx)
|
||||||
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
|
||||||
except InvalidInput:
|
except InvalidInput:
|
||||||
await self.add_error(message, '**Keep the name between 3 and 200 valid characters**')
|
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."""
|
"""Set the label of the Poll."""
|
||||||
async def get_valid(in_reply):
|
async def get_valid(in_reply):
|
||||||
if not 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.**
|
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."""
|
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:
|
while True:
|
||||||
try:
|
try:
|
||||||
@ -222,7 +227,7 @@ class Poll:
|
|||||||
reply = force
|
reply = force
|
||||||
force = None
|
force = None
|
||||||
else:
|
else:
|
||||||
reply = await self.get_user_reply()
|
reply = await self.get_user_reply(ctx)
|
||||||
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
|
||||||
@ -235,7 +240,7 @@ class Poll:
|
|||||||
f'**The label `{reply}` is not unique on this server. Choose a different one!**')
|
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."""
|
"""Set the preparation conditions for the Poll."""
|
||||||
async def get_valid(in_reply):
|
async def get_valid(in_reply):
|
||||||
if not 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 "
|
"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"
|
"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` ")
|
"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:
|
while True:
|
||||||
try:
|
try:
|
||||||
@ -286,7 +291,7 @@ class Poll:
|
|||||||
reply = force
|
reply = force
|
||||||
force = None
|
force = None
|
||||||
else:
|
else:
|
||||||
reply = await self.get_user_reply()
|
reply = await self.get_user_reply(ctx)
|
||||||
dt = await get_valid(reply)
|
dt = await get_valid(reply)
|
||||||
self.activation = dt
|
self.activation = dt
|
||||||
if self.activation == 0:
|
if self.activation == 0:
|
||||||
@ -303,7 +308,7 @@ class Poll:
|
|||||||
except DateOutOfRange as e:
|
except DateOutOfRange as e:
|
||||||
await self.add_error(message, f'**{e.date.strftime("%d-%b-%Y %H:%M")} is in the past.**')
|
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."""
|
"""Determine if poll is anonymous."""
|
||||||
async def get_valid(in_reply):
|
async def get_valid(in_reply):
|
||||||
if not in_reply:
|
if not in_reply:
|
||||||
@ -334,7 +339,7 @@ class Poll:
|
|||||||
"An anonymous poll has the following effects:\n"
|
"An anonymous poll has the following effects:\n"
|
||||||
"🔹 You will never see who voted for which option\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)")
|
"🔹 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:
|
while True:
|
||||||
try:
|
try:
|
||||||
@ -342,40 +347,14 @@ class Poll:
|
|||||||
reply = force
|
reply = force
|
||||||
force = None
|
force = None
|
||||||
else:
|
else:
|
||||||
reply = await self.get_user_reply()
|
reply = await self.get_user_reply(ctx)
|
||||||
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
|
||||||
except InvalidInput:
|
except InvalidInput:
|
||||||
await self.add_error(message, '**You can only answer with `yes` | `1` or `no` | `0`!**')
|
await self.add_error(message, '**You can only answer with `yes` | `1` or `no` | `0`!**')
|
||||||
|
|
||||||
|
async def set_multiple_choice(self, ctx, force=None):
|
||||||
# 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):
|
|
||||||
"""Determine if poll is multiple choice."""
|
"""Determine if poll is multiple choice."""
|
||||||
async def get_valid(in_reply):
|
async def get_valid(in_reply):
|
||||||
if not in_reply:
|
if not in_reply:
|
||||||
@ -406,7 +385,7 @@ class Poll:
|
|||||||
"\n"
|
"\n"
|
||||||
"If the maximum choices are reached for a voter, they have to unvote an option before being able to "
|
"If the maximum choices are reached for a voter, they have to unvote an option before being able to "
|
||||||
"vote for a different one.")
|
"vote for a different one.")
|
||||||
message = await self.wizard_says(text)
|
message = await self.wizard_says(ctx, text)
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
@ -414,7 +393,7 @@ class Poll:
|
|||||||
reply = force
|
reply = force
|
||||||
force = None
|
force = None
|
||||||
else:
|
else:
|
||||||
reply = await self.get_user_reply()
|
reply = await self.get_user_reply(ctx)
|
||||||
self.multiple_choice = await get_valid(reply)
|
self.multiple_choice = await get_valid(reply)
|
||||||
await self.add_vaild(message, f'{self.multiple_choice if self.multiple_choice > 0 else "No Limit"}')
|
await self.add_vaild(message, f'{self.multiple_choice if self.multiple_choice > 0 else "No Limit"}')
|
||||||
break
|
break
|
||||||
@ -426,7 +405,7 @@ class Poll:
|
|||||||
await self.add_error(message, '**You can\'t have more choices than options.**')
|
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."""
|
"""Set the answers / options of the Poll."""
|
||||||
async def get_valid(in_reply):
|
async def get_valid(in_reply):
|
||||||
if not in_reply:
|
if not in_reply:
|
||||||
@ -482,7 +461,7 @@ class Poll:
|
|||||||
"\n"
|
"\n"
|
||||||
"Example for custom options:\n"
|
"Example for custom options:\n"
|
||||||
"**apple juice, banana ice cream, kiwi slices** ")
|
"**apple juice, banana ice cream, kiwi slices** ")
|
||||||
message = await self.wizard_says(text)
|
message = await self.wizard_says(ctx, text)
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
@ -490,7 +469,7 @@ class Poll:
|
|||||||
reply = force
|
reply = force
|
||||||
force = None
|
force = None
|
||||||
else:
|
else:
|
||||||
reply = await self.get_user_reply()
|
reply = await self.get_user_reply(ctx)
|
||||||
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):
|
||||||
@ -510,7 +489,7 @@ class Poll:
|
|||||||
'**You need more than 1 option! Type them in a comma separated list.**')
|
'**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."""
|
"""Set role restrictions for the Poll."""
|
||||||
async def get_valid(in_reply, roles):
|
async def get_valid(in_reply, roles):
|
||||||
n_roles = roles.__len__()
|
n_roles = roles.__len__()
|
||||||
@ -563,7 +542,7 @@ class Poll:
|
|||||||
"Type `0`, `all` or `everyone` to have no restrictions.\n"
|
"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"
|
"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")
|
"`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:
|
while True:
|
||||||
try:
|
try:
|
||||||
@ -571,7 +550,7 @@ class Poll:
|
|||||||
reply = force
|
reply = force
|
||||||
force = None
|
force = None
|
||||||
else:
|
else:
|
||||||
reply = await self.get_user_reply()
|
reply = await self.get_user_reply(ctx)
|
||||||
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
|
||||||
@ -584,7 +563,7 @@ 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_weights(self, force=None):
|
async def set_weights(self, ctx, 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):
|
||||||
if not in_reply:
|
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"
|
"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"
|
"To assign weights type the role, followed by a colon, followed by the weight like this:\n"
|
||||||
"`moderator: 2, newbie: 0.5`")
|
"`moderator: 2, newbie: 0.5`")
|
||||||
message = await self.wizard_says(text)
|
message = await self.wizard_says(ctx, text)
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
@ -634,7 +613,7 @@ class Poll:
|
|||||||
reply = force
|
reply = force
|
||||||
force = None
|
force = None
|
||||||
else:
|
else:
|
||||||
reply = await self.get_user_reply()
|
reply = await self.get_user_reply(ctx)
|
||||||
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]
|
||||||
@ -654,7 +633,7 @@ class Poll:
|
|||||||
except WrongNumberOfArguments:
|
except WrongNumberOfArguments:
|
||||||
await self.add_error(message, f'**Not every role has a weight assigned.**')
|
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."""
|
"""Set the duration /deadline for the Poll."""
|
||||||
async def get_valid(in_reply):
|
async def get_valid(in_reply):
|
||||||
if not in_reply:
|
if not in_reply:
|
||||||
@ -693,7 +672,7 @@ class Poll:
|
|||||||
"You can specify a timezone if you want.\n"
|
"You can specify a timezone if you want.\n"
|
||||||
"\n"
|
"\n"
|
||||||
"Examples: `in 6 hours` or `next week CET` or `aug 15th 5:10` or `15.8.2019 11pm EST`")
|
"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:
|
while True:
|
||||||
try:
|
try:
|
||||||
@ -701,7 +680,7 @@ class Poll:
|
|||||||
reply = force
|
reply = force
|
||||||
force = None
|
force = None
|
||||||
else:
|
else:
|
||||||
reply = await self.get_user_reply()
|
reply = await self.get_user_reply(ctx)
|
||||||
dt = await get_valid(reply)
|
dt = await get_valid(reply)
|
||||||
self.duration = dt
|
self.duration = dt
|
||||||
if self.duration == 0:
|
if self.duration == 0:
|
||||||
@ -807,15 +786,15 @@ class Poll:
|
|||||||
|
|
||||||
for user_id in self.votes:
|
for user_id in self.votes:
|
||||||
member = self.server.get_member(user_id)
|
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
|
continue
|
||||||
name = member.nick
|
name = member.nick
|
||||||
if not name:
|
if not name:
|
||||||
name = member.name
|
name = member.name
|
||||||
export += f'\n{name}'
|
export += f'\n{name}'
|
||||||
if self.votes[user_id]['weight'] != 1:
|
if self.votes[str(member.id)]['weight'] != 1:
|
||||||
export += f' (weight: {self.votes[user_id]["weight"]})'
|
export += f' (weight: {self.votes[str(user_id)]["weight"]})'
|
||||||
export += ': ' + ', '.join([self.options_reaction[c] for c in self.votes[user_id]['choices']])
|
export += ': ' + ', '.join([self.options_reaction[c] for c in self.votes[str(user_id)]['choices']])
|
||||||
export += '\n'
|
export += '\n'
|
||||||
else:
|
else:
|
||||||
export += '--------------------------------------------\n' \
|
export += '--------------------------------------------\n' \
|
||||||
@ -824,15 +803,15 @@ class Poll:
|
|||||||
|
|
||||||
for user_id in self.votes:
|
for user_id in self.votes:
|
||||||
member = self.server.get_member(user_id)
|
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
|
continue
|
||||||
name = member.nick
|
name = member.nick
|
||||||
if not name:
|
if not name:
|
||||||
name = member.name
|
name = member.name
|
||||||
export += f'\n{name}'
|
export += f'\n{name}'
|
||||||
if self.votes[user_id]['weight'] != 1:
|
if self.votes[str(user_id)]['weight'] != 1:
|
||||||
export += f' (weight: {self.votes[user_id]["weight"]})'
|
export += f' (weight: {self.votes[str(user_id)]["weight"]})'
|
||||||
# export += ': ' + ', '.join([self.options_reaction[c] for c in self.votes[user_id]['choices']])
|
# export += ': ' + ', '.join([self.options_reaction[c] for c in self.votes[str(user_id)]['choices']])
|
||||||
export += '\n'
|
export += '\n'
|
||||||
|
|
||||||
export += ('--------------------------------------------\n'
|
export += ('--------------------------------------------\n'
|
||||||
@ -859,11 +838,10 @@ class Poll:
|
|||||||
async def from_dict(self, d):
|
async def from_dict(self, d):
|
||||||
|
|
||||||
self.id = d['_id']
|
self.id = d['_id']
|
||||||
self.server = self.bot.get_server(str(d['server_id']))
|
self.server = self.bot.get_guild(int(d['server_id']))
|
||||||
self.channel = self.bot.get_channel(str(d['channel_id']))
|
self.channel = self.bot.get_channel(int(d['channel_id']))
|
||||||
# self.author = await self.bot.get_user_info(str(d['author']))
|
|
||||||
if self.server:
|
if self.server:
|
||||||
self.author = self.server.get_member(d['author'])
|
self.author = self.server.get_member(int(d['author']))
|
||||||
else:
|
else:
|
||||||
self.author = None
|
self.author = None
|
||||||
self.name = d['name']
|
self.name = d['name']
|
||||||
@ -911,7 +889,7 @@ class Poll:
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
async def load_from_db(bot, server_id, short, ctx=None, ):
|
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:
|
if query is not None:
|
||||||
p = Poll(bot, ctx, load=True)
|
p = Poll(bot, ctx, load=True)
|
||||||
await p.from_dict(query)
|
await p.from_dict(query)
|
||||||
@ -1018,34 +996,25 @@ 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='React with ❔ to get info. It is not a vote option.')
|
||||||
return embed
|
return embed
|
||||||
|
|
||||||
async def post_embed(self, destination=None):
|
async def post_embed(self, destination):
|
||||||
if destination is None:
|
msg = await destination.send(embed=await self.generate_embed())
|
||||||
msg = await self.bot.say(embed=await self.generate_embed())
|
|
||||||
else:
|
|
||||||
msg = await self.bot.send_message(destination=destination, embed= await self.generate_embed())
|
|
||||||
if self.reaction and await self.is_open() and await self.is_active():
|
if self.reaction and await self.is_open() and await self.is_active():
|
||||||
if self.options_reaction_default:
|
if self.options_reaction_default:
|
||||||
for r in self.options_reaction:
|
for r in self.options_reaction:
|
||||||
await self.bot.add_reaction(
|
await msg.add_reaction(r)
|
||||||
msg,
|
await msg.add_reaction('❔')
|
||||||
r
|
|
||||||
)
|
|
||||||
await self.bot.add_reaction(msg, '❔')
|
|
||||||
return msg
|
return msg
|
||||||
else:
|
else:
|
||||||
for i, r in enumerate(self.options_reaction):
|
for i, r in enumerate(self.options_reaction):
|
||||||
await self.bot.add_reaction(
|
await msg.add_reaction(AZ_EMOJIS[i])
|
||||||
msg,
|
await msg.add_reaction('❔')
|
||||||
AZ_EMOJIS[i]
|
|
||||||
)
|
|
||||||
await self.bot.add_reaction(msg, '❔')
|
|
||||||
return msg
|
return msg
|
||||||
elif not await self.is_open():
|
elif not await self.is_open():
|
||||||
await self.bot.add_reaction(msg, '❔')
|
await msg.add_reaction('❔')
|
||||||
await self.bot.add_reaction(msg, '📎')
|
await msg.add_reaction('📎')
|
||||||
else:
|
else:
|
||||||
return msg
|
return msg
|
||||||
|
|
||||||
@ -1135,8 +1104,8 @@ class Poll:
|
|||||||
async def vote(self, user, option, message, lock):
|
async def vote(self, user, option, message, lock):
|
||||||
if not await self.is_open():
|
if not await self.is_open():
|
||||||
# refresh to show closed poll
|
# refresh to show closed poll
|
||||||
await self.bot.edit_message(message, embed=await self.generate_embed())
|
await message.edit(embed=await self.generate_embed())
|
||||||
await self.bot.clear_reactions(message)
|
await message.clear_reactions()
|
||||||
return
|
return
|
||||||
elif not await self.is_active():
|
elif not await self.is_active():
|
||||||
return
|
return
|
||||||
@ -1153,9 +1122,9 @@ class Poll:
|
|||||||
weight = max(valid_weights)
|
weight = max(valid_weights)
|
||||||
|
|
||||||
if str(user.id) not in self.votes:
|
if str(user.id) not in self.votes:
|
||||||
self.votes[user.id] = {'weight': weight, 'choices': []}
|
self.votes[str(user.id)] = {'weight': weight, 'choices': []}
|
||||||
else:
|
else:
|
||||||
self.votes[user.id]['weight'] = weight
|
self.votes[str(user.id)]['weight'] = weight
|
||||||
|
|
||||||
if self.options_reaction_default:
|
if self.options_reaction_default:
|
||||||
if option in self.options_reaction:
|
if option in self.options_reaction:
|
||||||
@ -1166,23 +1135,23 @@ class Poll:
|
|||||||
|
|
||||||
if choice != 'invalid':
|
if choice != 'invalid':
|
||||||
# if self.multiple_choice != 1: # more than 1 choice (0 = no limit)
|
# 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:
|
if self.anonymous:
|
||||||
# anonymous multiple choice -> can't unreact so we toggle with react
|
# anonymous multiple choice -> can't unreact so we toggle with react
|
||||||
logger.warning("Unvoting, should not happen for non anon polls.")
|
logger.warning("Unvoting, should not happen for non anon polls.")
|
||||||
await self.unvote(user, option, message, lock)
|
await self.unvote(user, option, message, lock)
|
||||||
# refresh_poll = False
|
# refresh_poll = False
|
||||||
else:
|
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. ' \
|
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.'
|
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 = discord.Embed(title='', description=say_text, colour=SETTINGS.color)
|
||||||
embed.set_author(name='Pollmaster', icon_url=SETTINGS.author_icon)
|
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
|
# refresh_poll = False
|
||||||
else:
|
else:
|
||||||
self.votes[user.id]['choices'].append(choice)
|
self.votes[str(user.id)]['choices'].append(choice)
|
||||||
self.votes[user.id]['choices'] = list(set(self.votes[user.id]['choices']))
|
self.votes[str(user.id)]['choices'] = list(set(self.votes[str(user.id)]['choices']))
|
||||||
# else:
|
# else:
|
||||||
# if [choice] == self.votes[user.id]['choices']:
|
# if [choice] == self.votes[user.id]['choices']:
|
||||||
# # refresh_poll = False
|
# # refresh_poll = False
|
||||||
@ -1198,26 +1167,9 @@ class Poll:
|
|||||||
return
|
return
|
||||||
|
|
||||||
# commit
|
# commit
|
||||||
#if lock._waiters.__len__() == 0:
|
|
||||||
# updating DB, clearing cache and refresh if necessary
|
|
||||||
await self.save_to_db()
|
await self.save_to_db()
|
||||||
# await self.bot.poll_refresh_q.put_unique_id(
|
asyncio.ensure_future(message.edit(embed=await self.generate_embed()))
|
||||||
# {'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()))
|
|
||||||
|
|
||||||
# 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):
|
async def unvote(self, user, option, message, lock):
|
||||||
if not await self.is_open():
|
if not await self.is_open():
|
||||||
# refresh to show closed poll
|
# refresh to show closed poll
|
||||||
await self.bot.edit_message(message, embed=await self.generate_embed())
|
await message.edit(embed=await self.generate_embed())
|
||||||
await self.bot.clear_reactions(message)
|
if not isinstance(message.channel, discord.abc.PrivateChannel):
|
||||||
|
await message.clear_reactions()
|
||||||
return
|
return
|
||||||
elif not await self.is_active():
|
elif not await self.is_active():
|
||||||
return
|
return
|
||||||
|
|
||||||
if str(user.id) not in self.votes: return
|
if str(user.id) not in self.votes:
|
||||||
|
return
|
||||||
|
|
||||||
choice = 'invalid'
|
choice = 'invalid'
|
||||||
|
|
||||||
@ -1242,24 +1196,14 @@ class Poll:
|
|||||||
if option in AZ_EMOJIS:
|
if option in AZ_EMOJIS:
|
||||||
choice = AZ_EMOJIS.index(option)
|
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:
|
try:
|
||||||
self.votes[user.id]['choices'].remove(choice)
|
self.votes[str(user.id)]['choices'].remove(choice)
|
||||||
await self.save_to_db()
|
await self.save_to_db()
|
||||||
asyncio.ensure_future(self.bot.edit_message(message, embed=await self.generate_embed()))
|
asyncio.ensure_future(message.edit(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
|
|
||||||
except ValueError:
|
except ValueError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
async def has_required_role(self, user):
|
async def has_required_role(self, user):
|
||||||
return not set([r.name for r in user.roles]).isdisjoint(self.roles)
|
return not set([r.name for r in user.roles]).isdisjoint(self.roles)
|
||||||
|
|
||||||
|
|||||||
@ -1,13 +1,9 @@
|
|||||||
import argparse
|
import argparse
|
||||||
import asyncio
|
import asyncio
|
||||||
import copy
|
|
||||||
import datetime
|
import datetime
|
||||||
import json
|
|
||||||
import logging
|
import logging
|
||||||
import re
|
import re
|
||||||
import shlex
|
import shlex
|
||||||
import string
|
|
||||||
import traceback
|
|
||||||
|
|
||||||
import discord
|
import discord
|
||||||
import pytz
|
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
|
AZ_EMOJIS = [(b'\\U0001f1a'.replace(b'a', bytes(hex(224 + (6 + i))[2:], "utf-8"))).decode("unicode-escape") for i in
|
||||||
range(26)]
|
range(26)]
|
||||||
|
|
||||||
class PollControls:
|
class PollControls(commands.Cog):
|
||||||
def __init__(self, bot):
|
def __init__(self, bot):
|
||||||
self.bot = 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 = {}
|
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):
|
async def close_polls(self):
|
||||||
"""This function runs every 60 seconds to schedule prepared polls and close expired polls"""
|
"""This function runs every 60 seconds to schedule prepared polls and close expired polls"""
|
||||||
while True:
|
while True:
|
||||||
@ -46,8 +54,8 @@ class PollControls:
|
|||||||
continue
|
continue
|
||||||
if p.active:
|
if p.active:
|
||||||
try:
|
try:
|
||||||
await self.bot.send_message(p.channel, 'This poll has been scheduled and is active now!')
|
await p.channel.send('This poll has been scheduled and is active now!')
|
||||||
await p.post_embed(destination=p.channel)
|
await p.post_embed(p.channel)
|
||||||
except:
|
except:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
@ -60,8 +68,8 @@ class PollControls:
|
|||||||
continue
|
continue
|
||||||
if not p.open:
|
if not p.open:
|
||||||
try:
|
try:
|
||||||
await self.bot.send_message(p.channel, 'This poll has reached the deadline and is closed!')
|
await p.channel.send('This poll has reached the deadline and is closed!')
|
||||||
await p.post_embed(destination=p.channel)
|
await p.post_embed(p.channel)
|
||||||
except:
|
except:
|
||||||
continue
|
continue
|
||||||
except AttributeError as ae:
|
except AttributeError as ae:
|
||||||
@ -78,15 +86,15 @@ class PollControls:
|
|||||||
await asyncio.sleep(30)
|
await asyncio.sleep(30)
|
||||||
|
|
||||||
def get_lock(self, server_id):
|
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()
|
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):
|
async def is_admin_or_creator(self, ctx, server, owner_id, error_msg=None):
|
||||||
member = server.get_member(ctx.message.author.id)
|
member = server.get_member(ctx.message.author.id)
|
||||||
if member.id == owner_id:
|
if member.id == owner_id:
|
||||||
return True
|
return True
|
||||||
elif member.server_permissions.manage_server:
|
elif member.guild_permissions.manage_guild:
|
||||||
return True
|
return True
|
||||||
else:
|
else:
|
||||||
result = await self.bot.db.config.find_one({'_id': str(server.id)})
|
result = await self.bot.db.config.find_one({'_id': str(server.id)})
|
||||||
@ -94,7 +102,7 @@ class PollControls:
|
|||||||
return True
|
return True
|
||||||
else:
|
else:
|
||||||
if error_msg is not None:
|
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
|
return False
|
||||||
|
|
||||||
async def say_error(self, ctx, error_text, footer_text=None):
|
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)
|
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 ctx.send(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.author_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 ctx.send(embed=embed)
|
||||||
|
|
||||||
# Commands
|
# # Commands
|
||||||
@commands.command(pass_context=True)
|
@commands.command()
|
||||||
async def activate(self, ctx, *, short=None):
|
async def activate(self, ctx, *, short=None):
|
||||||
"""Activate a prepared poll. Parameter: <label>"""
|
"""Activate a prepared poll. Parameter: <label>"""
|
||||||
server = await ask_for_server(self.bot, ctx.message, short)
|
server = await ask_for_server(self.bot, ctx.message, short)
|
||||||
@ -120,12 +128,12 @@ class PollControls:
|
|||||||
return
|
return
|
||||||
|
|
||||||
if short is None:
|
if short is None:
|
||||||
pre = await get_server_pre(self.bot, ctx.message.server)
|
pre = await get_server_pre(self.bot, ctx.message.guild)
|
||||||
error = f'Please specify the label of a poll after the activate command. \n' \
|
error = f'Please specify the label of a poll after the activate command. \n' \
|
||||||
f'`{pre}activate <poll_label>`'
|
f'`{pre}activate <poll_label>`'
|
||||||
await self.say_error(ctx, error)
|
await self.say_error(ctx, error)
|
||||||
else:
|
else:
|
||||||
p = await Poll.load_from_db(self.bot, str(server.id), short)
|
p = await Poll.load_from_db(self.bot, server.id, short)
|
||||||
if p is not None:
|
if p is not None:
|
||||||
# check if already active, then just do nothing
|
# check if already active, then just do nothing
|
||||||
if await p.is_active():
|
if await p.is_active():
|
||||||
@ -143,25 +151,25 @@ class PollControls:
|
|||||||
await p.save_to_db()
|
await p.save_to_db()
|
||||||
await ctx.invoke(self.show, short)
|
await ctx.invoke(self.show, short)
|
||||||
else:
|
else:
|
||||||
error = f'Poll with label "{short}" was not found.'
|
error = f'Poll with label "{short}" was not found. Listing prepared polls.'
|
||||||
# pre = await get_server_pre(self.bot, ctx.message.server)
|
# pre = await get_server_pre(self.bot, ctx.message.server)
|
||||||
# footer = f'Type {pre}show to display all polls'
|
# footer = f'Type {pre}show to display all polls'
|
||||||
await self.say_error(ctx, error)
|
await self.say_error(ctx, error)
|
||||||
await ctx.invoke(self.show)
|
await ctx.invoke(self.show, 'prepared')
|
||||||
|
|
||||||
@commands.command(pass_context=True)
|
@commands.command()
|
||||||
async def delete(self, ctx, *, short=None):
|
async def delete(self, ctx, *, short=None):
|
||||||
'''Delete a poll. Parameter: <label>'''
|
'''Delete a poll. Parameter: <label>'''
|
||||||
server = await ask_for_server(self.bot, ctx.message, short)
|
server = await ask_for_server(self.bot, ctx.message, short)
|
||||||
if not server:
|
if not server:
|
||||||
return
|
return
|
||||||
if short is None:
|
if short is None:
|
||||||
pre = await get_server_pre(self.bot, ctx.message.server)
|
pre = await get_server_pre(self.bot, ctx.message.guild)
|
||||||
error = f'Please specify the label of a poll after the delete command. \n' \
|
error = f'Please specify the label of a poll after the delete command. \n' \
|
||||||
f'`{pre}delete <poll_label>`'
|
f'`{pre}delete <poll_label>`'
|
||||||
await self.say_error(ctx, error)
|
await self.say_error(ctx, error)
|
||||||
else:
|
else:
|
||||||
p = await Poll.load_from_db(self.bot, str(server.id), short)
|
p = await Poll.load_from_db(self.bot, server.id, short)
|
||||||
if p is not None:
|
if p is not None:
|
||||||
# Permission Check: Admin or Creator
|
# Permission Check: Admin or Creator
|
||||||
if not await self.is_admin_or_creator(
|
if not await self.is_admin_or_creator(
|
||||||
@ -172,18 +180,18 @@ class PollControls:
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
# Delete Poll
|
# Delete Poll
|
||||||
result = await self.bot.db.polls.delete_one({'server_id': server.id, 'short': short})
|
result = await self.bot.db.polls.delete_one({'server_id': str(server.id), 'short': short})
|
||||||
if result.deleted_count == 1:
|
if result.deleted_count == 1:
|
||||||
say = f'Poll with label "{short}" was successfully deleted. This action can\'t be undone!'
|
say = f'Poll with label "{short}" was successfully deleted. This action can\'t be undone!'
|
||||||
title = 'Poll deleted'
|
title = 'Poll deleted'
|
||||||
await self.say_embed(ctx, say, title)
|
await self.say_embed(ctx, say, title)
|
||||||
else:
|
else:
|
||||||
error = f'Action failed. Poll could not be deleted. You should probably report his error to the dev, thanks!`'
|
error = f'Action failed. Poll could not be deleted. You should probably report his error to the dev, thanks!'
|
||||||
await self.say_error(ctx, error)
|
await self.say_error(ctx, error)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
error = f'Poll with label "{short}" was not found.'
|
error = f'Poll with label "{short}" was not found.'
|
||||||
pre = await get_server_pre(self.bot, ctx.message.server)
|
pre = await get_server_pre(self.bot, ctx.message.guild)
|
||||||
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)
|
||||||
|
|
||||||
@ -195,12 +203,12 @@ class PollControls:
|
|||||||
return
|
return
|
||||||
|
|
||||||
if short is None:
|
if short is None:
|
||||||
pre = await get_server_pre(self.bot, ctx.message.server)
|
pre = await get_server_pre(self.bot, ctx.message.guild)
|
||||||
error = f'Please specify the label of a poll after the close command. \n' \
|
error = f'Please specify the label of a poll after the close command. \n' \
|
||||||
f'`{pre}close <poll_label>`'
|
f'`{pre}close <poll_label>`'
|
||||||
await self.say_error(ctx, error)
|
await self.say_error(ctx, error)
|
||||||
else:
|
else:
|
||||||
p = await Poll.load_from_db(self.bot, str(server.id), short)
|
p = await Poll.load_from_db(self.bot, server.id, short)
|
||||||
if p is not None:
|
if p is not None:
|
||||||
# Permission Check: Admin or Creator
|
# Permission Check: Admin or Creator
|
||||||
if not await self.is_admin_or_creator(
|
if not await self.is_admin_or_creator(
|
||||||
@ -215,7 +223,7 @@ class PollControls:
|
|||||||
await p.save_to_db()
|
await p.save_to_db()
|
||||||
await ctx.invoke(self.show, short)
|
await ctx.invoke(self.show, short)
|
||||||
else:
|
else:
|
||||||
error = f'Poll with label "{short}" was not found.'
|
error = f'Poll with label "{short}" was not found. Listing all open polls.'
|
||||||
# pre = await get_server_pre(self.bot, ctx.message.server)
|
# pre = await get_server_pre(self.bot, ctx.message.server)
|
||||||
# footer = f'Type {pre}show to display all polls'
|
# footer = f'Type {pre}show to display all polls'
|
||||||
await self.say_error(ctx, error)
|
await self.say_error(ctx, error)
|
||||||
@ -229,26 +237,29 @@ class PollControls:
|
|||||||
return
|
return
|
||||||
|
|
||||||
if short is None:
|
if short is None:
|
||||||
pre = await get_server_pre(self.bot, ctx.message.server)
|
pre = await get_server_pre(self.bot, ctx.message.guild)
|
||||||
error = f'Please specify the label of a poll after the export command. \n' \
|
error = f'Please specify the label of a poll after the export command. \n' \
|
||||||
f'`{pre}export <poll_label>`'
|
f'`{pre}export <poll_label>`'
|
||||||
await self.say_error(ctx, error)
|
await self.say_error(ctx, error)
|
||||||
else:
|
else:
|
||||||
p = await Poll.load_from_db(self.bot, str(server.id), short)
|
p = await Poll.load_from_db(self.bot, server.id, short)
|
||||||
if p is not None:
|
if p is not None:
|
||||||
if p.open:
|
if p.open:
|
||||||
pre = await get_server_pre(self.bot, ctx.message.server)
|
pre = await get_server_pre(self.bot, ctx.message.guild)
|
||||||
error_text = f'You can only export closed polls. \nPlease `{pre}close {short}` the poll first or wait for the deadline.'
|
error_text = f'You can only export closed polls. \nPlease `{pre}close {short}` the poll first or wait for the deadline.'
|
||||||
await self.say_error(ctx, error_text)
|
await self.say_error(ctx, error_text)
|
||||||
else:
|
else:
|
||||||
# sending file
|
# sending file
|
||||||
file = await p.export()
|
file_name = await p.export()
|
||||||
if file is not None:
|
if file_name is not None:
|
||||||
await self.bot.send_file(
|
await ctx.message.author.send('Sending you the requested export of "{}".'.format(p.short),
|
||||||
ctx.message.author,
|
file=discord.File(file_name)
|
||||||
file,
|
)
|
||||||
content='Sending you the requested export of "{}".'.format(p.short)
|
# await self.bot.send_file(
|
||||||
)
|
# ctx.message.author,
|
||||||
|
# file_name,
|
||||||
|
# content='Sending you the requested export of "{}".'.format(p.short)
|
||||||
|
# )
|
||||||
else:
|
else:
|
||||||
error_text = 'Could not export the requested poll. \nPlease report this to the developer.'
|
error_text = 'Could not export the requested poll. \nPlease report this to the developer.'
|
||||||
await self.say_error(ctx, error_text)
|
await self.say_error(ctx, error_text)
|
||||||
@ -259,7 +270,7 @@ class PollControls:
|
|||||||
await self.say_error(ctx, error)
|
await self.say_error(ctx, error)
|
||||||
await ctx.invoke(self.show)
|
await ctx.invoke(self.show)
|
||||||
|
|
||||||
@commands.command(pass_context=True)
|
@commands.command()
|
||||||
async def show(self, ctx, short='open', start=0):
|
async def show(self, ctx, short='open', start=0):
|
||||||
'''Show a list of open polls or show a specific poll. Parameters: "open" (default), "closed", "prepared" or <label>'''
|
'''Show a list of open polls or show a specific poll. Parameters: "open" (default), "closed", "prepared" or <label>'''
|
||||||
|
|
||||||
@ -292,102 +303,102 @@ class PollControls:
|
|||||||
# 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)
|
||||||
footer_text = f'type {pre}show <label> to display a poll. '
|
footer_text = f'type {pre}show <label> to display a poll. '
|
||||||
msg = await embed_list_paginated(self.bot, pre, polls, item_fct, embed, footer_prefix=footer_text,
|
msg = await embed_list_paginated(ctx, self.bot, pre, polls, item_fct, embed, footer_prefix=footer_text,
|
||||||
per_page=10)
|
per_page=10)
|
||||||
else:
|
else:
|
||||||
p = await Poll.load_from_db(self.bot, str(server.id), short)
|
p = await Poll.load_from_db(self.bot, server.id, short)
|
||||||
if p is not None:
|
if p is not None:
|
||||||
error_msg = 'This poll is inactive and you have no rights to display or view it.'
|
error_msg = 'This poll is inactive and you have no rights to display or view it.'
|
||||||
if not await p.is_active() and not await self.is_admin_or_creator(ctx, server, p.author, error_msg):
|
if not await p.is_active() and not await self.is_admin_or_creator(ctx, server, p.author, error_msg):
|
||||||
return
|
return
|
||||||
await p.post_embed()
|
await p.post_embed(ctx)
|
||||||
else:
|
else:
|
||||||
error = f'Poll with label {short} was not found.'
|
error = f'Poll with label {short} was not found.'
|
||||||
pre = await get_server_pre(self.bot, server)
|
pre = await get_server_pre(self.bot, server)
|
||||||
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)
|
# @commands.command(pass_context=True)
|
||||||
async def cmd(self, ctx, *, cmd=None):
|
# async def cmd(self, ctx, *, cmd=None):
|
||||||
'''The old, command style way paired with the wizard.'''
|
# '''The old, command style way paired with the wizard.'''
|
||||||
await self.say_embed(ctx, say_text='This command is temporarily disabled.')
|
# await self.say_embed(ctx, say_text='This command is temporarily disabled.')
|
||||||
# server = await ask_for_server(self.bot, ctx.message)
|
# # server = await ask_for_server(self.bot, ctx.message)
|
||||||
# if not server:
|
# # if not server:
|
||||||
# return
|
# # return
|
||||||
# pre = await get_server_pre(self.bot, server)
|
# # pre = await get_server_pre(self.bot, server)
|
||||||
# try:
|
# # try:
|
||||||
# # generate the argparser and handle invalid stuff
|
# # # generate the argparser and handle invalid stuff
|
||||||
# descr = 'Accept poll settings via commandstring. \n\n' \
|
# # descr = 'Accept poll settings via commandstring. \n\n' \
|
||||||
# '**Wrap all arguments in quotes like this:** \n' \
|
# # '**Wrap all arguments in quotes like this:** \n' \
|
||||||
# f'{pre}cmd -question \"What tea do you like?\" -o \"green, black, chai\"\n\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. ' \
|
# # '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 an argument is invalid, the wizard will step in. ' \
|
||||||
# 'If the command string is invalid, you will get this error :)'
|
# # 'If the command string is invalid, you will get this error :)'
|
||||||
# parser = argparse.ArgumentParser(description=descr, formatter_class=CustomFormatter, add_help=False)
|
# # parser = argparse.ArgumentParser(description=descr, formatter_class=CustomFormatter, add_help=False)
|
||||||
# parser.add_argument('-question', '-q')
|
# # parser.add_argument('-question', '-q')
|
||||||
# parser.add_argument('-label', '-l', default=str(await generate_word(self.bot, server.id)))
|
# # parser.add_argument('-label', '-l', default=str(await generate_word(self.bot, server.id)))
|
||||||
# parser.add_argument('-options', '-o')
|
# # parser.add_argument('-options', '-o')
|
||||||
# parser.add_argument('-multiple_choice', '-mc', default='1')
|
# # parser.add_argument('-multiple_choice', '-mc', default='1')
|
||||||
# parser.add_argument('-roles', '-r', default='all')
|
# # parser.add_argument('-roles', '-r', default='all')
|
||||||
# parser.add_argument('-weights', '-w', default='none')
|
# # parser.add_argument('-weights', '-w', default='none')
|
||||||
# parser.add_argument('-deadline', '-d', default='0')
|
# # parser.add_argument('-deadline', '-d', default='0')
|
||||||
# parser.add_argument('-anonymous', '-a', action="store_true")
|
# # parser.add_argument('-anonymous', '-a', action="store_true")
|
||||||
#
|
# #
|
||||||
# helpstring = parser.format_help()
|
# # helpstring = parser.format_help()
|
||||||
# helpstring = helpstring.replace("pollmaster.py", f"{pre}cmd ")
|
# # helpstring = helpstring.replace("pollmaster.py", f"{pre}cmd ")
|
||||||
#
|
# #
|
||||||
# if cmd and cmd == 'help':
|
# # if cmd and cmd == 'help':
|
||||||
# await self.say_embed(ctx, say_text=helpstring)
|
# # await self.say_embed(ctx, say_text=helpstring)
|
||||||
# return
|
# # return
|
||||||
#
|
# #
|
||||||
# try:
|
# # try:
|
||||||
# cmds = shlex.split(cmd)
|
# # cmds = shlex.split(cmd)
|
||||||
# except ValueError:
|
# # except ValueError:
|
||||||
# await self.say_error(ctx, error_text=helpstring)
|
# # await self.say_error(ctx, error_text=helpstring)
|
||||||
# return
|
# # return
|
||||||
# except:
|
# # except:
|
||||||
# return
|
# # return
|
||||||
#
|
# #
|
||||||
# try:
|
# # try:
|
||||||
# args, unknown_args = parser.parse_known_args(cmds)
|
# # args, unknown_args = parser.parse_known_args(cmds)
|
||||||
# except SystemExit:
|
# # except SystemExit:
|
||||||
# await self.say_error(ctx, error_text=helpstring)
|
# # await self.say_error(ctx, error_text=helpstring)
|
||||||
# return
|
# # return
|
||||||
# except:
|
# # except:
|
||||||
# return
|
# # return
|
||||||
#
|
# #
|
||||||
# if unknown_args:
|
# # if unknown_args:
|
||||||
# error_text = f'**There was an error reading the command line options!**.\n' \
|
# # error_text = f'**There was an error reading the command line options!**.\n' \
|
||||||
# f'Most likely this is because you didn\'t surround the arguments with double quotes like this: ' \
|
# # f'Most likely this is because you didn\'t surround the arguments with double quotes like this: ' \
|
||||||
# f'`{pre}cmd -q "question of the poll" -o "yes, no, maybe"`' \
|
# # f'`{pre}cmd -q "question of the poll" -o "yes, no, maybe"`' \
|
||||||
# f'\n\nHere are the arguments I could not understand:\n'
|
# # f'\n\nHere are the arguments I could not understand:\n'
|
||||||
# error_text += '`'+'\n'.join(unknown_args)+'`'
|
# # error_text += '`'+'\n'.join(unknown_args)+'`'
|
||||||
# error_text += f'\n\nHere are the arguments which are ok:\n'
|
# # error_text += f'\n\nHere are the arguments which are ok:\n'
|
||||||
# error_text += '`' + '\n'.join([f'{k}: {v}' for k, v in vars(args).items()]) + '`'
|
# # error_text += '`' + '\n'.join([f'{k}: {v}' for k, v in vars(args).items()]) + '`'
|
||||||
#
|
# #
|
||||||
# await self.say_error(ctx, error_text=error_text, footer_text=f'type `{pre}cmd help` for details.')
|
# # await self.say_error(ctx, error_text=error_text, footer_text=f'type `{pre}cmd help` for details.')
|
||||||
# return
|
# # return
|
||||||
#
|
# #
|
||||||
# # pass arguments to the wizard
|
# # # pass arguments to the wizard
|
||||||
# async def route(poll):
|
# # async def route(poll):
|
||||||
# await poll.set_name(force=args.question)
|
# # await poll.set_name(force=args.question)
|
||||||
# await poll.set_short(force=args.label)
|
# # await poll.set_short(force=args.label)
|
||||||
# await poll.set_anonymous(force=f'{"yes" if args.anonymous else "no"}')
|
# # await poll.set_anonymous(force=f'{"yes" if args.anonymous else "no"}')
|
||||||
# await poll.set_options_reaction(force=args.options)
|
# # await poll.set_options_reaction(force=args.options)
|
||||||
# await poll.set_multiple_choice(force=args.multiple_choice)
|
# # await poll.set_multiple_choice(force=args.multiple_choice)
|
||||||
# await poll.set_roles(force=args.roles)
|
# # await poll.set_roles(force=args.roles)
|
||||||
# await poll.set_weights(force=args.weights)
|
# # await poll.set_weights(force=args.weights)
|
||||||
# await poll.set_duration(force=args.deadline)
|
# # await poll.set_duration(force=args.deadline)
|
||||||
#
|
# #
|
||||||
# poll = await self.wizard(ctx, route, server)
|
# # poll = await self.wizard(ctx, route, server)
|
||||||
# if poll:
|
# # if poll:
|
||||||
# await poll.post_embed(destination=poll.channel)
|
# # await poll.post_embed(destination=poll.channel)
|
||||||
# except Exception as error:
|
# # except Exception as error:
|
||||||
# logger.error("ERROR IN pm!cmd")
|
# # logger.error("ERROR IN pm!cmd")
|
||||||
# logger.exception(error)
|
# # logger.exception(error)
|
||||||
|
#
|
||||||
|
#
|
||||||
@commands.command(pass_context=True)
|
@commands.command()
|
||||||
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)
|
server = await ask_for_server(self.bot, ctx.message)
|
||||||
@ -395,71 +406,71 @@ class PollControls:
|
|||||||
return
|
return
|
||||||
|
|
||||||
async def route(poll):
|
async def route(poll):
|
||||||
await poll.set_name(force=cmd)
|
await poll.set_name(ctx, force=cmd)
|
||||||
await poll.set_short(force=str(await generate_word(self.bot, server.id)))
|
await poll.set_short(ctx, force=str(await generate_word(self.bot, server.id)))
|
||||||
await poll.set_anonymous(force='no')
|
await poll.set_anonymous(ctx, force='no')
|
||||||
await poll.set_options_reaction()
|
await poll.set_options_reaction(ctx)
|
||||||
await poll.set_multiple_choice(force='1')
|
await poll.set_multiple_choice(ctx, force='1')
|
||||||
await poll.set_roles(force='all')
|
await poll.set_roles(ctx, force='all')
|
||||||
await poll.set_weights(force='none')
|
await poll.set_weights(ctx, force='none')
|
||||||
await poll.set_duration(force='0')
|
await poll.set_duration(ctx, force='0')
|
||||||
|
|
||||||
poll = await self.wizard(ctx, route, server)
|
poll = await self.wizard(ctx, route, server)
|
||||||
if poll:
|
if poll:
|
||||||
await poll.post_embed(destination=poll.channel)
|
await poll.post_embed(poll.channel)
|
||||||
|
|
||||||
@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)
|
server = await ask_for_server(self.bot, ctx.message)
|
||||||
if not server:
|
if not server:
|
||||||
return
|
return
|
||||||
|
|
||||||
async def route(poll):
|
async def route(poll):
|
||||||
await poll.set_name(force=cmd)
|
await poll.set_name(ctx, force=cmd)
|
||||||
await poll.set_short()
|
await poll.set_short(ctx)
|
||||||
await poll.set_preparation()
|
await poll.set_preparation(ctx)
|
||||||
await poll.set_anonymous()
|
await poll.set_anonymous(ctx)
|
||||||
await poll.set_options_reaction()
|
await poll.set_options_reaction(ctx)
|
||||||
await poll.set_multiple_choice()
|
await poll.set_multiple_choice(ctx)
|
||||||
await poll.set_roles()
|
await poll.set_roles(ctx)
|
||||||
await poll.set_weights()
|
await poll.set_weights(ctx)
|
||||||
await poll.set_duration()
|
await poll.set_duration(ctx)
|
||||||
|
|
||||||
poll = await self.wizard(ctx, route, server)
|
poll = await self.wizard(ctx, route, server)
|
||||||
if poll:
|
if poll:
|
||||||
await poll.post_embed(destination=ctx.message.author)
|
await poll.post_embed(ctx.message.author)
|
||||||
|
|
||||||
@commands.command(pass_context=True)
|
@commands.command()
|
||||||
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)
|
server = await ask_for_server(self.bot, ctx.message)
|
||||||
if not server:
|
if not server:
|
||||||
return
|
return
|
||||||
|
|
||||||
async def route(poll):
|
async def route(poll):
|
||||||
await poll.set_name(force=cmd)
|
await poll.set_name(ctx, force=cmd)
|
||||||
await poll.set_short()
|
await poll.set_short(ctx)
|
||||||
await poll.set_anonymous()
|
await poll.set_anonymous(ctx)
|
||||||
await poll.set_options_reaction()
|
await poll.set_options_reaction(ctx)
|
||||||
await poll.set_multiple_choice()
|
await poll.set_multiple_choice(ctx)
|
||||||
await poll.set_roles()
|
await poll.set_roles(ctx)
|
||||||
await poll.set_weights()
|
await poll.set_weights(ctx)
|
||||||
await poll.set_duration()
|
await poll.set_duration(ctx)
|
||||||
|
|
||||||
poll = await self.wizard(ctx, route, server)
|
poll = await self.wizard(ctx, route, server)
|
||||||
if poll:
|
if poll:
|
||||||
await poll.post_embed(destination=poll.channel)
|
await poll.post_embed(poll.channel)
|
||||||
|
|
||||||
# The Wizard!
|
# The Wizard!
|
||||||
async def wizard(self, ctx, route, server):
|
async def wizard(self, ctx, route, server):
|
||||||
channel = await ask_for_channel(self.bot, server, ctx.message)
|
channel = await ask_for_channel(ctx, self.bot, server, ctx.message)
|
||||||
if not channel:
|
if not channel:
|
||||||
return
|
return
|
||||||
|
|
||||||
# Permission Check
|
# Permission Check
|
||||||
member = server.get_member(ctx.message.author.id)
|
member = server.get_member(ctx.message.author.id)
|
||||||
if not member.server_permissions.manage_server:
|
if not member.guild_permissions.manage_guild:
|
||||||
result = await self.bot.db.config.find_one({'_id': str(server.id)})
|
result = await self.bot.db.config.find_one({'_id': str(server.id)})
|
||||||
if result and result.get('admin_role') not in [r.name for r in member.roles] and result.get(
|
if result and result.get('admin_role') not in [r.name for r in member.roles] and result.get(
|
||||||
'user_role') not in [r.name for r in member.roles]:
|
'user_role') not in [r.name for r in member.roles]:
|
||||||
@ -485,126 +496,156 @@ class PollControls:
|
|||||||
await poll.save_to_db()
|
await poll.save_to_db()
|
||||||
return poll
|
return poll
|
||||||
|
|
||||||
# BOT EVENTS (@bot.event)
|
# # BOT EVENTS (@bot.event)
|
||||||
async def on_socket_raw_receive(self, raw_msg):
|
# async def on_socket_raw_receive(self, raw_msg):
|
||||||
if not isinstance(raw_msg, str):
|
# print(raw_msg)
|
||||||
return
|
# if not isinstance(raw_msg, str):
|
||||||
msg = json.loads(raw_msg)
|
# return
|
||||||
type = msg.get("t")
|
# msg = json.loads(raw_msg)
|
||||||
data = msg.get("d")
|
# type = msg.get("t")
|
||||||
if not data:
|
# data = msg.get("d")
|
||||||
return
|
# if not data:
|
||||||
emoji = data.get("emoji")
|
# return
|
||||||
user_id = data.get("user_id")
|
# # emoji = data.get("emoji")
|
||||||
message_id = data.get("message_id")
|
# # user_id = data.get("user_id")
|
||||||
if type == "MESSAGE_REACTION_ADD":
|
# # message_id = data.get("message_id")
|
||||||
await self.do_on_reaction_add(data)
|
# if type == "MESSAGE_REACTION_ADD":
|
||||||
elif type == "MESSAGE_REACTION_REMOVE":
|
# await self.do_on_reaction_add(data)
|
||||||
await self.do_on_reaction_remove(data)
|
# elif type == "MESSAGE_REACTION_REMOVE":
|
||||||
|
# #await self.do_on_reaction_remove(data)
|
||||||
|
# pass
|
||||||
|
|
||||||
async def do_on_reaction_remove(self, data):
|
@commands.Cog.listener()
|
||||||
|
async def on_raw_reaction_remove(self, data):
|
||||||
# get emoji symbol
|
# get emoji symbol
|
||||||
emoji = data.get('emoji')
|
emoji = data.emoji
|
||||||
if emoji:
|
if emoji:
|
||||||
emoji = emoji.get('name')
|
emoji = emoji.name
|
||||||
if not emoji:
|
if not emoji:
|
||||||
return
|
return
|
||||||
|
|
||||||
# check if removed by the bot.. this is a bit hacky but discord doesn't provide the correct info...
|
# check if removed by the bot.. this is a bit hacky but discord doesn't provide the correct info...
|
||||||
message_id = data.get('message_id')
|
message_id = data.message_id
|
||||||
user_id = data.get('user_id')
|
user_id = data.user_id
|
||||||
if self.ignore_next_removed_reaction.get(str(message_id) + str(emoji)) == user_id:
|
if self.ignore_next_removed_reaction.get(str(message_id) + str(emoji)) == user_id:
|
||||||
del self.ignore_next_removed_reaction[str(message_id) + str(emoji)]
|
del self.ignore_next_removed_reaction[str(message_id) + str(emoji)]
|
||||||
return
|
return
|
||||||
|
|
||||||
# check if we can find a poll label
|
# check if we can find a poll label
|
||||||
channel_id = data.get('channel_id')
|
message_id = data.message_id
|
||||||
|
channel_id = data.channel_id
|
||||||
channel = self.bot.get_channel(channel_id)
|
channel = self.bot.get_channel(channel_id)
|
||||||
# user = await self.bot.get_user_info(user_id) # only do this once
|
|
||||||
|
|
||||||
server_id = data.get('guild_id')
|
if isinstance(channel, discord.TextChannel):
|
||||||
if server_id:
|
server = channel.guild
|
||||||
server = self.bot.get_server(server_id)
|
|
||||||
user = server.get_member(user_id)
|
user = server.get_member(user_id)
|
||||||
else:
|
message = self.bot.message_cache.get(message_id)
|
||||||
user = await self.bot.get_user_info(user_id) # only do this once, this is rate limited
|
if message is None:
|
||||||
|
message = await channel.fetch_message(id=message_id)
|
||||||
|
self.bot.message_cache.put(message_id, message)
|
||||||
|
label = self.get_label(message)
|
||||||
|
if not label:
|
||||||
|
return
|
||||||
|
elif isinstance(channel, discord.DMChannel):
|
||||||
|
user = await self.bot.fetch_user(user_id) # only do this once
|
||||||
|
message = self.bot.message_cache.get(message_id)
|
||||||
|
if message is None:
|
||||||
|
message = await channel.fetch_message(id=message_id)
|
||||||
|
self.bot.message_cache.put(message_id, message)
|
||||||
|
label = self.get_label(message)
|
||||||
|
if not label:
|
||||||
|
return
|
||||||
|
server = await ask_for_server(self.bot, message, label)
|
||||||
|
|
||||||
if not channel:
|
elif not channel:
|
||||||
# discord rapidly closes dm channels by desing
|
# discord rapidly closes dm channels by desing
|
||||||
# put private channels back into the bots cache and try again
|
# put private channels back into the bots cache and try again
|
||||||
await self.bot.start_private_message(user)
|
user = await self.bot.fetch_user(user_id) # only do this once
|
||||||
|
await user.create_dm()
|
||||||
channel = self.bot.get_channel(channel_id)
|
channel = self.bot.get_channel(channel_id)
|
||||||
|
message = self.bot.message_cache.get(message_id)
|
||||||
message = await self.bot.get_message(channel=channel, id=message_id)
|
if message is None:
|
||||||
label = None
|
message = await channel.fetch_message(id=message_id)
|
||||||
if message and message.embeds:
|
self.bot.message_cache.put(message_id, message)
|
||||||
embed = message.embeds[0]
|
label = self.get_label(message)
|
||||||
label_object = embed.get('author')
|
if not label:
|
||||||
if label_object:
|
return
|
||||||
label_full = label_object.get('name')
|
server = await ask_for_server(self.bot, message, label)
|
||||||
if label_full and label_full.startswith('>> '):
|
else:
|
||||||
label = label_full[3:]
|
|
||||||
if not label:
|
|
||||||
return
|
return
|
||||||
|
|
||||||
# create message object for the reaction sender, to get correct server
|
|
||||||
if not server_id:
|
|
||||||
user_msg = copy.deepcopy(message)
|
|
||||||
user_msg.author = user
|
|
||||||
server = await ask_for_server(self.bot, user_msg, label)
|
|
||||||
server_id = server.id
|
|
||||||
|
|
||||||
# this is exclusive
|
# this is exclusive
|
||||||
lock = self.get_lock(server_id)
|
lock = self.get_lock(server.id)
|
||||||
async with lock:
|
async with lock:
|
||||||
p = await Poll.load_from_db(self.bot, server_id, label)
|
p = await Poll.load_from_db(self.bot, server.id, label)
|
||||||
if not isinstance(p, Poll):
|
if not isinstance(p, Poll):
|
||||||
return
|
return
|
||||||
if not p.anonymous:
|
if not p.anonymous:
|
||||||
# for anonymous polls we can't unvote because we need to hide reactions
|
# for anonymous polls we can't unvote because we need to hide reactions
|
||||||
await p.unvote(user, emoji, message, lock)
|
await p.unvote(user, emoji, message, lock)
|
||||||
|
|
||||||
async def do_on_reaction_add(self, data):
|
@commands.Cog.listener()
|
||||||
|
async def on_raw_reaction_add(self, data):
|
||||||
# dont look at bot's own reactions
|
# dont look at bot's own reactions
|
||||||
user_id = data.get('user_id')
|
user_id = data.user_id
|
||||||
if user_id == self.bot.user.id:
|
if user_id == self.bot.user.id:
|
||||||
return
|
return
|
||||||
|
|
||||||
# get emoji symbol
|
# get emoji symbol
|
||||||
emoji = data.get('emoji')
|
emoji = data.emoji
|
||||||
if emoji:
|
if emoji:
|
||||||
emoji = emoji.get('name')
|
emoji = emoji.name
|
||||||
if not emoji:
|
if not emoji:
|
||||||
return
|
return
|
||||||
|
|
||||||
# check if we can find a poll label
|
# check if we can find a poll label
|
||||||
message_id = data.get('message_id')
|
message_id = data.message_id
|
||||||
channel_id = data.get('channel_id')
|
channel_id = data.channel_id
|
||||||
channel = self.bot.get_channel(channel_id)
|
channel = self.bot.get_channel(channel_id)
|
||||||
user = await self.bot.get_user_info(user_id) # only do this once
|
|
||||||
if not channel:
|
if isinstance(channel, discord.TextChannel):
|
||||||
|
server = channel.guild
|
||||||
|
user = server.get_member(user_id)
|
||||||
|
message = self.bot.message_cache.get(message_id)
|
||||||
|
if message is None:
|
||||||
|
message = await channel.fetch_message(id=message_id)
|
||||||
|
self.bot.message_cache.put(message_id, message)
|
||||||
|
label = self.get_label(message)
|
||||||
|
if not label:
|
||||||
|
return
|
||||||
|
elif isinstance(channel, discord.DMChannel):
|
||||||
|
user = await self.bot.fetch_user(user_id) # only do this once
|
||||||
|
message = self.bot.message_cache.get(message_id)
|
||||||
|
if message is None:
|
||||||
|
message = await channel.fetch_message(id=message_id)
|
||||||
|
self.bot.message_cache.put(message_id, message)
|
||||||
|
label = self.get_label(message)
|
||||||
|
if not label:
|
||||||
|
return
|
||||||
|
server = await ask_for_server(self.bot, message, label)
|
||||||
|
|
||||||
|
elif not channel:
|
||||||
# discord rapidly closes dm channels by desing
|
# discord rapidly closes dm channels by desing
|
||||||
# put private channels back into the bots cache and try again
|
# put private channels back into the bots cache and try again
|
||||||
await self.bot.start_private_message(user)
|
user = await self.bot.fetch_user(user_id) # only do this once
|
||||||
|
await user.create_dm()
|
||||||
channel = self.bot.get_channel(channel_id)
|
channel = self.bot.get_channel(channel_id)
|
||||||
message = await self.bot.get_message(channel=channel, id=message_id)
|
message = self.bot.message_cache.get(message_id)
|
||||||
label = None
|
if message is None:
|
||||||
if message and message.embeds:
|
message = await channel.fetch_message(id=message_id)
|
||||||
embed = message.embeds[0]
|
self.bot.message_cache.put(message_id, message)
|
||||||
label_object = embed.get('author')
|
label = self.get_label(message)
|
||||||
if label_object:
|
if not label:
|
||||||
label_full = label_object.get('name')
|
return
|
||||||
if label_full and label_full.startswith('>> '):
|
server = await ask_for_server(self.bot, message, label)
|
||||||
label = label_full[3:]
|
else:
|
||||||
if not label:
|
|
||||||
return
|
return
|
||||||
|
|
||||||
# fetch poll
|
# fetch poll
|
||||||
# create message object for the reaction sender, to get correct server
|
# create message object for the reaction sender, to get correct server
|
||||||
user_msg = copy.deepcopy(message)
|
# user_msg = copy.deepcopy(message)
|
||||||
user_msg.author = user
|
# user_msg.author = user
|
||||||
server = await ask_for_server(self.bot, user_msg, label)
|
# server = await ask_for_server(self.bot, user_msg, label)
|
||||||
|
|
||||||
# this is exclusive to keep database access sequential
|
# this is exclusive to keep database access sequential
|
||||||
# hopefully it will scale well enough or I need a different solution
|
# hopefully it will scale well enough or I need a different solution
|
||||||
@ -617,13 +658,11 @@ class PollControls:
|
|||||||
# export
|
# export
|
||||||
if emoji == '📎':
|
if emoji == '📎':
|
||||||
# sending file
|
# sending file
|
||||||
file = await p.export()
|
file_name = await p.export()
|
||||||
if file is not None:
|
if file_name is not None:
|
||||||
await self.bot.send_file(
|
await user.send('Sending you the requested export of "{}".'.format(p.short),
|
||||||
user,
|
file=discord.File(file_name)
|
||||||
file,
|
)
|
||||||
content='Sending you the requested export of "{}".'.format(p.short)
|
|
||||||
)
|
|
||||||
return
|
return
|
||||||
|
|
||||||
# info
|
# info
|
||||||
@ -643,7 +682,7 @@ class PollControls:
|
|||||||
edit_rights = False
|
edit_rights = False
|
||||||
if str(member.id) == str(p.author):
|
if str(member.id) == str(p.author):
|
||||||
edit_rights = True
|
edit_rights = True
|
||||||
elif member.server_permissions.manage_server:
|
elif member.guild_permissions.manage_guild:
|
||||||
edit_rights = True
|
edit_rights = True
|
||||||
else:
|
else:
|
||||||
result = await self.bot.db.config.find_one({'_id': str(server.id)})
|
result = await self.bot.db.config.find_one({'_id': str(server.id)})
|
||||||
@ -683,7 +722,7 @@ class PollControls:
|
|||||||
|
|
||||||
embed.add_field(name='Time left in the poll:', value=time_left, inline=False)
|
embed.add_field(name='Time left in the poll:', value=time_left, inline=False)
|
||||||
|
|
||||||
await self.bot.send_message(user, embed=embed)
|
await user.send(embed=embed)
|
||||||
|
|
||||||
# send current details of who currently voted for what
|
# send current details of who currently voted for what
|
||||||
if not p.anonymous and p.votes.__len__() > 0:
|
if not p.anonymous and p.votes.__len__() > 0:
|
||||||
@ -708,30 +747,30 @@ class PollControls:
|
|||||||
msg += f' (weight: {p.votes[user_id]["weight"]})'
|
msg += f' (weight: {p.votes[user_id]["weight"]})'
|
||||||
# msg += ': ' + ', '.join([AZ_EMOJIS[c]+" "+p.options_reaction[c] for c in p.votes[user_id]['choices']])
|
# msg += ': ' + ', '.join([AZ_EMOJIS[c]+" "+p.options_reaction[c] for c in p.votes[user_id]['choices']])
|
||||||
if msg.__len__() > 1500:
|
if msg.__len__() > 1500:
|
||||||
await self.bot.send_message(user, msg)
|
await user.send(msg)
|
||||||
msg = ''
|
msg = ''
|
||||||
if c == 0:
|
if c == 0:
|
||||||
msg += '\nNo votes for this option yet.'
|
msg += '\nNo votes for this option yet.'
|
||||||
msg += '\n\n'
|
msg += '\n\n'
|
||||||
|
|
||||||
if msg.__len__() > 0:
|
if msg.__len__() > 0:
|
||||||
await self.bot.send_message(user, msg)
|
await user.send(msg)
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
# Assume: User wants to vote with reaction
|
# Assume: User wants to vote with reaction
|
||||||
# no rights, terminate function
|
# no rights, terminate function
|
||||||
if not await p.has_required_role(member):
|
if not await p.has_required_role(member):
|
||||||
await self.bot.remove_reaction(message, emoji, user)
|
await message.remove_reaction(emoji, user)
|
||||||
await self.bot.send_message(user, f'You are not allowed to vote in this poll. Only users with '
|
await member.send(f'You are not allowed to vote in this poll. Only users with '
|
||||||
f'at least one of these roles can vote:\n{", ".join(p.roles)}')
|
f'at least one of these roles can vote:\n{", ".join(p.roles)}')
|
||||||
return
|
return
|
||||||
|
|
||||||
# check if we need to remove reactions (this will trigger on_reaction_remove)
|
# check if we need to remove reactions (this will trigger on_reaction_remove)
|
||||||
if str(channel.type) != 'private' and p.anonymous:
|
if not isinstance(channel, discord.DMChannel) and p.anonymous:
|
||||||
# immediately remove reaction and to be safe, remove all reactions
|
# immediately remove reaction and to be safe, remove all reactions
|
||||||
self.ignore_next_removed_reaction[str(message.id) + str(emoji)] = user_id
|
self.ignore_next_removed_reaction[str(message.id) + str(emoji)] = user_id
|
||||||
await self.bot.remove_reaction(message, emoji, user)
|
asyncio.ensure_future(message.remove_reaction(emoji, user))
|
||||||
|
|
||||||
|
|
||||||
# order here is crucial since we can't determine if a reaction was removed by the bot or user
|
# order here is crucial since we can't determine if a reaction was removed by the bot or user
|
||||||
# update database with vote
|
# update database with vote
|
||||||
@ -739,14 +778,13 @@ class PollControls:
|
|||||||
|
|
||||||
# cant do this until we figure out how to see who removed the reaction?
|
# cant do this until we figure out how to see who removed the reaction?
|
||||||
# for now MC 1 is like MC x
|
# for now MC 1 is like MC x
|
||||||
if str(channel.type) != 'private' and p.multiple_choice == 1:
|
# if isinstance(channel, discord.TextChannel) and p.multiple_choice == 1:
|
||||||
# remove all other reactions
|
# # remove all other reactions
|
||||||
# if lock._waiters.__len__() == 0:
|
# # if lock._waiters.__len__() == 0:
|
||||||
for r in message.reactions:
|
# for r in message.reactions:
|
||||||
if r.emoji and r.emoji != emoji:
|
# if r.emoji and r.emoji != emoji:
|
||||||
await self.bot.remove_reaction(message, r.emoji, user)
|
# await message.remove_reaction(r.emoji, user)
|
||||||
pass
|
# pass
|
||||||
|
|
||||||
|
|
||||||
def setup(bot):
|
def setup(bot):
|
||||||
global logger
|
global logger
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
"""Exception Classes for the Poll Wizard"""
|
"""Exception Classes for the Poll Wizard"""
|
||||||
|
|
||||||
|
|
||||||
class StopWizard(RuntimeError):
|
class StopWizard(RuntimeError):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|||||||
23
essentials/messagecache.py
Normal file
23
essentials/messagecache.py
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import discord
|
||||||
|
|
||||||
|
|
||||||
|
class MessageCache:
|
||||||
|
def __init__(self, _bot):
|
||||||
|
self._bot = _bot
|
||||||
|
self._cache_dict = {}
|
||||||
|
|
||||||
|
def put(self, key, value: discord.Message):
|
||||||
|
self._cache_dict[key] = value
|
||||||
|
print("cache size:", self._cache_dict.__len__())
|
||||||
|
|
||||||
|
def get(self, key):
|
||||||
|
# Try to find it in this cache, then see if it is cached in the bots own message cache
|
||||||
|
message = self._cache_dict.get(key, None)
|
||||||
|
if message == None:
|
||||||
|
for m in self._bot._connection._messages:
|
||||||
|
if m.id == key:
|
||||||
|
return m
|
||||||
|
return message
|
||||||
|
|
||||||
|
def clear(self):
|
||||||
|
self._cache_dict = {}
|
||||||
@ -1,14 +1,13 @@
|
|||||||
import time
|
import asyncio
|
||||||
|
|
||||||
import discord
|
import discord
|
||||||
|
|
||||||
from essentials.settings import SETTINGS
|
from essentials.settings import SETTINGS
|
||||||
from utils.paginator import embed_list_paginated
|
|
||||||
|
|
||||||
|
|
||||||
async def get_pre(bot, message):
|
async def get_pre(bot, message):
|
||||||
'''Gets the prefix for a message.'''
|
'''Gets the prefix for a message.'''
|
||||||
if str(message.channel.type) == 'private':
|
if isinstance(message.channel, discord.abc.PrivateChannel):
|
||||||
shared_server_list = await get_servers(bot, message)
|
shared_server_list = await get_servers(bot, message)
|
||||||
if shared_server_list.__len__() == 0:
|
if shared_server_list.__len__() == 0:
|
||||||
return 'pm!'
|
return 'pm!'
|
||||||
@ -18,7 +17,7 @@ async def get_pre(bot, message):
|
|||||||
# return a tuple of all prefixes.. this will check them all!
|
# return a tuple of all prefixes.. this will check them all!
|
||||||
return tuple([await get_server_pre(bot, s) for s in shared_server_list])
|
return tuple([await get_server_pre(bot, s) for s in shared_server_list])
|
||||||
else:
|
else:
|
||||||
return await get_server_pre(bot, message.server)
|
return await get_server_pre(bot, message.guild)
|
||||||
|
|
||||||
|
|
||||||
async def get_server_pre(bot, server):
|
async def get_server_pre(bot, server):
|
||||||
@ -35,16 +34,16 @@ async def get_server_pre(bot, server):
|
|||||||
|
|
||||||
async def get_servers(bot, message, short=None):
|
async def get_servers(bot, message, short=None):
|
||||||
'''Get best guess of relevant shared servers'''
|
'''Get best guess of relevant shared servers'''
|
||||||
if message.server is None:
|
if message.guild is None:
|
||||||
list_of_shared_servers = []
|
list_of_shared_servers = []
|
||||||
for s in bot.servers:
|
for s in bot.guilds:
|
||||||
if message.author.id in [m.id for m in s.members]:
|
if message.author.id in [m.id for m in s.members]:
|
||||||
list_of_shared_servers.append(s)
|
list_of_shared_servers.append(s)
|
||||||
if short is not None:
|
if short is not None:
|
||||||
query = bot.db.polls.find({'short': short})
|
query = bot.db.polls.find({'short': short})
|
||||||
if query is not None:
|
if query is not None:
|
||||||
server_ids_with_short = [poll['server_id'] async for poll in query]
|
server_ids_with_short = [poll['server_id'] async for poll in query]
|
||||||
servers_with_short = [bot.get_server(x) for x in server_ids_with_short]
|
servers_with_short = [bot.get_guild(x) for x in server_ids_with_short]
|
||||||
shared_servers_with_short = list(set(servers_with_short).intersection(set(list_of_shared_servers)))
|
shared_servers_with_short = list(set(servers_with_short).intersection(set(list_of_shared_servers)))
|
||||||
if shared_servers_with_short.__len__() >= 1:
|
if shared_servers_with_short.__len__() >= 1:
|
||||||
return shared_servers_with_short
|
return shared_servers_with_short
|
||||||
@ -55,7 +54,7 @@ async def get_servers(bot, message, short=None):
|
|||||||
else:
|
else:
|
||||||
return list_of_shared_servers
|
return list_of_shared_servers
|
||||||
else:
|
else:
|
||||||
return [message.server]
|
return [message.guild]
|
||||||
|
|
||||||
|
|
||||||
async def ask_for_server(bot, message, short=None):
|
async def ask_for_server(bot, message, short=None):
|
||||||
@ -63,7 +62,8 @@ async def ask_for_server(bot, message, short=None):
|
|||||||
if server_list.__len__() == 0:
|
if server_list.__len__() == 0:
|
||||||
if short == None:
|
if short == None:
|
||||||
await bot.say(
|
await bot.say(
|
||||||
'I could not find a common server where we can see eachother. If you think this is an error, please contact the developer.')
|
'I could not find a common server where we can see eachother. If you think this is an error, '
|
||||||
|
'please contact the developer.')
|
||||||
else:
|
else:
|
||||||
await bot.say(f'I could not find a server where the poll {short} exists that we both can see.')
|
await bot.say(f'I could not find a server where the poll {short} exists that we both can see.')
|
||||||
return None
|
return None
|
||||||
@ -76,34 +76,40 @@ async def ask_for_server(bot, message, short=None):
|
|||||||
text += f'\n**{i}** - {name}'
|
text += f'\n**{i}** - {name}'
|
||||||
i += 1
|
i += 1
|
||||||
embed = discord.Embed(title="Select your server", description=text, color=SETTINGS.color)
|
embed = discord.Embed(title="Select your server", description=text, color=SETTINGS.color)
|
||||||
server_msg = await bot.send_message(message.channel, embed=embed)
|
server_msg = await message.channel.send(embed=embed)
|
||||||
|
|
||||||
valid_reply = False
|
valid_reply = False
|
||||||
nr = 1
|
nr = 1
|
||||||
while valid_reply == False:
|
while valid_reply == False:
|
||||||
reply = await bot.wait_for_message(timeout=60, author=message.author)
|
def check(m):
|
||||||
if reply and reply.content:
|
return message.author == m.author
|
||||||
if reply.content.startswith(await get_pre(bot, message)):
|
try:
|
||||||
# await bot.say('You can\'t use bot commands while I am waiting for an answer.'
|
reply = await bot.wait_for('message', timeout=60, check=check)
|
||||||
# '\n I\'ll stop waiting and execute your command.')
|
except asyncio.TimeoutError:
|
||||||
return False
|
pass
|
||||||
if str(reply.content).isdigit():
|
else:
|
||||||
nr = int(reply.content)
|
if reply and reply.content:
|
||||||
if 0 < nr <= server_list.__len__():
|
if reply.content.startswith(await get_pre(bot, message)):
|
||||||
valid_reply = True
|
# await bot.say('You can\'t use bot commands while I am waiting for an answer.'
|
||||||
|
# '\n I\'ll stop waiting and execute your command.')
|
||||||
|
return False
|
||||||
|
if str(reply.content).isdigit():
|
||||||
|
nr = int(reply.content)
|
||||||
|
if 0 < nr <= server_list.__len__():
|
||||||
|
valid_reply = True
|
||||||
|
|
||||||
return server_list[nr - 1]
|
return server_list[nr - 1]
|
||||||
|
|
||||||
|
|
||||||
async def ask_for_channel(bot, server, message):
|
async def ask_for_channel(ctx, bot, server, message):
|
||||||
# if performed from a channel, return that channel
|
# if performed from a channel, return that channel
|
||||||
if str(message.channel.type) == 'text':
|
if not isinstance(message.channel, discord.abc.PrivateChannel):
|
||||||
return message.channel
|
return message.channel
|
||||||
|
|
||||||
# build channel list that the user is allowed to send messages to
|
# build channel list that the user is allowed to send messages to
|
||||||
user = message.author
|
user = message.author
|
||||||
member = server.get_member(user.id)
|
member = server.get_member(user.id)
|
||||||
channel_list = [c for c in server.channels if str(c.type) == 'text' and c.permissions_for(member).send_messages]
|
channel_list = [c for c in server.channels if isinstance(c, discord.TextChannel) and c.permissions_for(member).send_messages]
|
||||||
|
|
||||||
# if exactly 1 channel, return it
|
# if exactly 1 channel, return it
|
||||||
if channel_list.__len__() == 1:
|
if channel_list.__len__() == 1:
|
||||||
@ -111,39 +117,48 @@ async def ask_for_channel(bot, server, message):
|
|||||||
|
|
||||||
# if no channels, display error
|
# if no channels, display error
|
||||||
if channel_list.__len__() == 0:
|
if channel_list.__len__() == 0:
|
||||||
embed = discord.Embed(title="Select a channel", description='No text channels found on this server. Make sure I can see them.', color=SETTINGS.color)
|
embed = discord.Embed(title="Select a channel", description='No text channels found on this server. Make sure '
|
||||||
await bot.say(embed=embed)
|
'I can see them.', color=SETTINGS.color)
|
||||||
|
await ctx.send(embed=embed)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# otherwise ask for a channel
|
# otherwise ask for a channel
|
||||||
i = 1
|
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'
|
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]:
|
for name in [c.name for c in channel_list]:
|
||||||
to_add = f'\n**{i}** - {name}'
|
to_add = f'\n**{i}** - {name}'
|
||||||
|
|
||||||
# check if length doesn't exceed allowed maximum or split it into multiple messages
|
# check if length doesn't exceed allowed maximum or split it into multiple messages
|
||||||
if text.__len__() + to_add.__len__() > 2048:
|
if text.__len__() + to_add.__len__() > 2048:
|
||||||
embed = discord.Embed(title="Select a channel", description=text, color=SETTINGS.color)
|
embed = discord.Embed(title="Select a channel", description=text, color=SETTINGS.color)
|
||||||
await bot.say(embed=embed)
|
await ctx.send(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'
|
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:
|
else:
|
||||||
text += to_add
|
text += to_add
|
||||||
i += 1
|
i += 1
|
||||||
|
|
||||||
embed = discord.Embed(title="Select a channel", description=text, color=SETTINGS.color)
|
embed = discord.Embed(title="Select a channel", description=text, color=SETTINGS.color)
|
||||||
await bot.say(embed=embed)
|
await ctx.send(embed=embed)
|
||||||
|
|
||||||
valid_reply = False
|
valid_reply = False
|
||||||
nr = 1
|
nr = 1
|
||||||
while valid_reply == False:
|
while not valid_reply:
|
||||||
reply = await bot.wait_for_message(timeout=60, author=message.author)
|
def check(m):
|
||||||
if reply and reply.content:
|
return message.author.id == m.author.id
|
||||||
if reply.content.startswith(await get_pre(bot, message)):
|
try:
|
||||||
# await bot.say('You can\'t use bot commands while I am waiting for an answer.'
|
reply = await bot.wait_for('message', timeout=60)
|
||||||
# '\n I\'ll stop waiting and execute your command.')
|
except asyncio.TimeoutError:
|
||||||
return False
|
pass
|
||||||
if str(reply.content).isdigit():
|
else:
|
||||||
nr = int(reply.content)
|
if reply and reply.content:
|
||||||
if 0 < nr <= channel_list.__len__():
|
if reply.content.startswith(await get_pre(bot, message)):
|
||||||
valid_reply = True
|
# await bot.say('You can\'t use bot commands while I am waiting for an answer.'
|
||||||
|
# '\n I\'ll stop waiting and execute your command.')
|
||||||
|
return False
|
||||||
|
if str(reply.content).isdigit():
|
||||||
|
nr = int(reply.content)
|
||||||
|
if 0 < nr <= channel_list.__len__():
|
||||||
|
valid_reply = True
|
||||||
return channel_list[nr - 1]
|
return channel_list[nr - 1]
|
||||||
@ -1,17 +1,17 @@
|
|||||||
import asyncio
|
|
||||||
import sys
|
import sys
|
||||||
import traceback
|
import traceback
|
||||||
import logging
|
|
||||||
import aiohttp
|
import aiohttp
|
||||||
import discord
|
import discord
|
||||||
|
import logging
|
||||||
|
|
||||||
|
|
||||||
|
from essentials.messagecache import MessageCache
|
||||||
from discord.ext import commands
|
from discord.ext import commands
|
||||||
from motor.motor_asyncio import AsyncIOMotorClient
|
from motor.motor_asyncio import AsyncIOMotorClient
|
||||||
|
|
||||||
from essentials.multi_server import get_pre
|
from essentials.multi_server import get_pre
|
||||||
from essentials.settings import SETTINGS
|
from essentials.settings import SETTINGS
|
||||||
from utils.asyncio_unique_queue import UniqueQueue
|
|
||||||
from utils.import_old_database import import_old_database
|
|
||||||
|
|
||||||
bot_config = {
|
bot_config = {
|
||||||
'command_prefix': get_pre,
|
'command_prefix': get_pre,
|
||||||
@ -22,7 +22,7 @@ bot_config = {
|
|||||||
'max_messages': 15000
|
'max_messages': 15000
|
||||||
}
|
}
|
||||||
|
|
||||||
bot = commands.Bot(**bot_config)
|
bot = commands.AutoShardedBot(**bot_config)
|
||||||
bot.remove_command('help')
|
bot.remove_command('help')
|
||||||
|
|
||||||
# logger
|
# logger
|
||||||
@ -50,18 +50,18 @@ for ext in extensions:
|
|||||||
|
|
||||||
@bot.event
|
@bot.event
|
||||||
async def on_ready():
|
async def on_ready():
|
||||||
bot.owner = await bot.get_user_info(str(SETTINGS.owner_id))
|
bot.owner = await bot.fetch_user(SETTINGS.owner_id)
|
||||||
|
|
||||||
mongo = AsyncIOMotorClient(SETTINGS.mongo_db)
|
mongo = AsyncIOMotorClient(SETTINGS.mongo_db)
|
||||||
bot.db = mongo.pollmaster
|
bot.db = mongo.pollmaster
|
||||||
bot.session = aiohttp.ClientSession()
|
bot.session = aiohttp.ClientSession()
|
||||||
print(bot.db)
|
print(bot.db)
|
||||||
await bot.change_presence(game=discord.Game(name=f'pm!help - v2.2'))
|
await bot.change_presence(status=discord.Game(name=f'pm!help - v2.2'))
|
||||||
|
|
||||||
# check discord server configs
|
# check discord server configs
|
||||||
try:
|
try:
|
||||||
db_server_ids = [entry['_id'] async for entry in bot.db.config.find({}, {})]
|
db_server_ids = [entry['_id'] async for entry in bot.db.config.find({}, {})]
|
||||||
for server in bot.servers:
|
for server in bot.guilds:
|
||||||
if server.id not in db_server_ids:
|
if server.id not in db_server_ids:
|
||||||
# create new config entry
|
# create new config entry
|
||||||
await bot.db.config.update_one(
|
await bot.db.config.update_one(
|
||||||
@ -69,30 +69,20 @@ async def on_ready():
|
|||||||
{'$set': {'prefix': 'pm!', 'admin_role': 'polladmin', 'user_role': 'polluser'}},
|
{'$set': {'prefix': 'pm!', 'admin_role': 'polladmin', 'user_role': 'polluser'}},
|
||||||
upsert=True
|
upsert=True
|
||||||
)
|
)
|
||||||
# stopping migration support.
|
|
||||||
# try:
|
|
||||||
# await import_old_database(bot, server)
|
|
||||||
# print(str(server), "updated.")
|
|
||||||
# except:
|
|
||||||
# print(str(server.id), "failed.")
|
|
||||||
except:
|
except:
|
||||||
print("Problem verifying servers.")
|
print("Problem verifying servers.")
|
||||||
|
|
||||||
# cache prefixes
|
# cache prefixes
|
||||||
bot.pre = {entry['_id']: entry['prefix'] async for entry in bot.db.config.find({}, {'_id', 'prefix'})}
|
bot.pre = {entry['_id']: entry['prefix'] async for entry in bot.db.config.find({}, {'_id', 'prefix'})}
|
||||||
|
|
||||||
# global locks and caches for performance when voting rapidly
|
|
||||||
bot.locks = {}
|
bot.locks = {}
|
||||||
# bot.poll_cache = {}
|
bot.message_cache = MessageCache(bot)
|
||||||
# bot.poll_refresh_q = UniqueQueue()
|
|
||||||
|
|
||||||
print("Servers verified. Bot running.")
|
print("Servers verified. Bot running.")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@bot.event
|
@bot.event
|
||||||
async def on_command_error(e, ctx):
|
async def on_command_error(ctx, e):
|
||||||
if SETTINGS.log_errors:
|
if SETTINGS.log_errors:
|
||||||
ignored_exceptions = (
|
ignored_exceptions = (
|
||||||
commands.MissingRequiredArgument,
|
commands.MissingRequiredArgument,
|
||||||
@ -122,22 +112,9 @@ async def on_command_error(e, ctx):
|
|||||||
f"\n\tAuthor: <@{ctx.message.author}>",
|
f"\n\tAuthor: <@{ctx.message.author}>",
|
||||||
timestamp=ctx.message.timestamp
|
timestamp=ctx.message.timestamp
|
||||||
)
|
)
|
||||||
await bot.send_message(bot.owner, embed=e)
|
await ctx.send(bot.owner, embed=e)
|
||||||
|
|
||||||
# if SETTINGS.mode == 'development':
|
# if SETTINGS.mode == 'development':
|
||||||
raise e
|
raise e
|
||||||
|
|
||||||
|
bot.run(SETTINGS.bot_token)
|
||||||
@bot.event
|
|
||||||
async def on_server_join(server):
|
|
||||||
result = await bot.db.config.find_one({'_id': str(server.id)})
|
|
||||||
if result is None:
|
|
||||||
await bot.db.config.update_one(
|
|
||||||
{'_id': str(server.id)},
|
|
||||||
{'$set': {'prefix': 'pm!', 'admin_role': 'polladmin', 'user_role': 'polluser'}},
|
|
||||||
upsert=True
|
|
||||||
)
|
|
||||||
bot.pre[str(server.id)] = 'pm!'
|
|
||||||
|
|
||||||
|
|
||||||
bot.run(SETTINGS.bot_token, reconnect=True)
|
|
||||||
@ -1,4 +1,4 @@
|
|||||||
# Pollmaster V 2.2
|
# Pollmaster V 2.3
|
||||||
|
|
||||||
## Overview
|
## Overview
|
||||||
|
|
||||||
@ -34,7 +34,7 @@ Here is how Pollmaster looks in action:
|
|||||||
| pm!help | Shows an interactive help menu |
|
| pm!help | Shows an interactive help menu |
|
||||||
| pm!new | Starts a new poll with all the settings |
|
| pm!new | Starts a new poll with all the settings |
|
||||||
| pm!quick | Starts a new poll with just a question and options |
|
| pm!quick | Starts a new poll with just a question and options |
|
||||||
| pm!show <label> | Shows poll in a specified channel (can be different from original channel| |
|
| pm!show <label> | Shows poll in a specified channel (can be different from original channel) |
|
||||||
| pm!prefix <new prefix> | Change the prefix for this server |
|
| pm!prefix <new prefix> | Change the prefix for this server |
|
||||||
| pm!userrole <any role> | Set the role that has the rights to use the bot |
|
| pm!userrole <any role> | Set the role that has the rights to use the bot |
|
||||||
|
|
||||||
|
|||||||
@ -1,34 +0,0 @@
|
|||||||
aiohttp==1.0.5
|
|
||||||
async-timeout==3.0.1
|
|
||||||
attrs==18.2.0
|
|
||||||
beautifulsoup4==4.7.1
|
|
||||||
bs4==0.0.1
|
|
||||||
certifi==2018.11.29
|
|
||||||
chardet==3.0.4
|
|
||||||
cycler==0.10.0
|
|
||||||
dateparser==0.7.0
|
|
||||||
dblpy==0.1.6
|
|
||||||
discord==0.16.12
|
|
||||||
discord.py==0.16.12
|
|
||||||
idna==2.8
|
|
||||||
idna-ssl==1.1.0
|
|
||||||
kiwisolver==1.0.1
|
|
||||||
matplotlib==3.0.2
|
|
||||||
motor==2.0.0
|
|
||||||
multidict==4.5.2
|
|
||||||
numpy==1.16.1
|
|
||||||
pymongo==3.7.2
|
|
||||||
pyparsing==2.3.1
|
|
||||||
python-dateutil==2.7.5
|
|
||||||
pytz==2018.9
|
|
||||||
ratelimiter==1.2.0.post0
|
|
||||||
regex==2019.2.7
|
|
||||||
requests==2.21.0
|
|
||||||
six==1.12.0
|
|
||||||
soupsieve==1.7.3
|
|
||||||
typing-extensions==3.7.2
|
|
||||||
tzlocal==1.5.1
|
|
||||||
Unidecode==1.0.23
|
|
||||||
urllib3==1.24.1
|
|
||||||
websockets==3.4
|
|
||||||
yarl==1.3.0
|
|
||||||
@ -1,11 +0,0 @@
|
|||||||
import asyncio
|
|
||||||
|
|
||||||
|
|
||||||
class UniqueQueue(asyncio.Queue):
|
|
||||||
|
|
||||||
async def put_unique_id(self, item):
|
|
||||||
if not item.get('id'):
|
|
||||||
return
|
|
||||||
|
|
||||||
if item.get('id') not in [v.get('id') for v in self._queue]:
|
|
||||||
await self.put(item)
|
|
||||||
@ -1,96 +0,0 @@
|
|||||||
import datetime
|
|
||||||
import json
|
|
||||||
import os
|
|
||||||
import pytz
|
|
||||||
|
|
||||||
|
|
||||||
async def import_old_database(bot, server):
|
|
||||||
"""try to import the old database"""
|
|
||||||
try:
|
|
||||||
clean_server = str(server).replace("/", "")
|
|
||||||
while clean_server.startswith("."):
|
|
||||||
clean_server = clean_server[1:]
|
|
||||||
fn = 'backup/' + clean_server + '.json'
|
|
||||||
with open(fn, 'r') as infile:
|
|
||||||
polls = json.load(infile)
|
|
||||||
for p in polls:
|
|
||||||
#print(polls[p]['short'])
|
|
||||||
wr = []
|
|
||||||
wn = []
|
|
||||||
for r, n in polls[p]['weights'].items():
|
|
||||||
wr.append(r)
|
|
||||||
try:
|
|
||||||
if n.is_integer():
|
|
||||||
n = int(n)
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
wn.append(n)
|
|
||||||
created = datetime.datetime.strptime(polls[p]['datestarted'], '%d-%m-%Y %H:%M').replace(tzinfo=pytz.utc)
|
|
||||||
if polls[p]['duration'] == 0:
|
|
||||||
duration = 0
|
|
||||||
else:
|
|
||||||
duration = created + datetime.timedelta(hours=float(polls[p]['duration']))
|
|
||||||
votes = {}
|
|
||||||
for u,o in polls[p]['votes'].items():
|
|
||||||
# get weight
|
|
||||||
user = server.get_member(u)
|
|
||||||
weight = 1
|
|
||||||
if wr.__len__() > 0:
|
|
||||||
valid_weights = [wn[wr.index(r)] for r in
|
|
||||||
list(set([n.name for n in user.roles]).intersection(set(wr)))]
|
|
||||||
if valid_weights.__len__() > 0:
|
|
||||||
#print(wr, wn)
|
|
||||||
weight = max(valid_weights)
|
|
||||||
choices = []
|
|
||||||
if o in polls[p]['options']:
|
|
||||||
choices = [polls[p]['options'].index(o)]
|
|
||||||
votes[u] = {'weight': weight, 'choices': choices}
|
|
||||||
|
|
||||||
new_format = {
|
|
||||||
'server_id': str(server.id),
|
|
||||||
'channel_id': str(polls[p]['channel']),
|
|
||||||
'author': str(polls[p]['author']),
|
|
||||||
'name': polls[p]['name'],
|
|
||||||
'short': polls[p]['short'],
|
|
||||||
'anonymous': polls[p]['anonymous'],
|
|
||||||
'reaction': True,
|
|
||||||
'multiple_choice': False,
|
|
||||||
'options_reaction': polls[p]['options'],
|
|
||||||
'reaction_default': False,
|
|
||||||
'roles': polls[p]['roles'],
|
|
||||||
'weights_roles': wr,
|
|
||||||
'weights_numbers': wn,
|
|
||||||
'duration': duration,
|
|
||||||
'duration_tz': 'UTC',
|
|
||||||
'time_created': created,
|
|
||||||
'open': polls[p]['open'],
|
|
||||||
'active': True,
|
|
||||||
'activation': 0,
|
|
||||||
'activation_tz': 'UTC',
|
|
||||||
'votes': votes
|
|
||||||
}
|
|
||||||
await bot.db.polls.update_one({'server_id': str(server.id), 'short': polls[p]['short']},
|
|
||||||
{'$set': new_format}, upsert=True)
|
|
||||||
os.remove(fn)
|
|
||||||
except FileNotFoundError:
|
|
||||||
pass
|
|
||||||
except Exception as e:
|
|
||||||
print(e)
|
|
||||||
|
|
||||||
|
|
||||||
# potential update message
|
|
||||||
# text = "**Dear Server Admin!**\n\n" \
|
|
||||||
# "After more than a year in the field, today Pollmaster received it's first big update and I am excited to present you the new Version!\n\n" \
|
|
||||||
# "**TL;DR: A massive overhaul of every function. The new (now customizable) prefix is `pm!` and you can find the rest of the commands with `pm!help`**\n\n" \
|
|
||||||
# "**Here are some more highlights:**\n" \
|
|
||||||
# "🔹 Voting is no longer done per text, but by using reactions\n" \
|
|
||||||
# "🔹 Creating new polls is now an interactive process instead of command lines\n" \
|
|
||||||
# "🔹 There is now a settings for multiple choice polls\n" \
|
|
||||||
# "🔹 You can use all the commands in a private message with Pollmaster to reduce spam in your channels\n\n" \
|
|
||||||
# "For the full changelog, please visit: https://github.com/matnad/pollmaster/blob/master/changelog.md\n" \
|
|
||||||
# "All your old polls should be converted to the new format and accessible with `pm!show` or `pm!show closed`.\n" \
|
|
||||||
# "If you have any problems or questions, please join the support discord: https://discord.gg/Vgk8Nve\n\n" \
|
|
||||||
# "**No action from you is required. But you might want to update permissions for your users. " \
|
|
||||||
# "See pm!help -> configuration**\n\n" \
|
|
||||||
# "I hope you enjoy the new Pollmaster!\n" \
|
|
||||||
# "Regards, Newti#0654"
|
|
||||||
@ -1,4 +1,9 @@
|
|||||||
async def embed_list_paginated(bot, pre, items, item_fct, base_embed, footer_prefix='', msg=None, start=0, per_page=10):
|
import asyncio
|
||||||
|
|
||||||
|
import discord
|
||||||
|
|
||||||
|
|
||||||
|
async def embed_list_paginated(ctx, bot, pre, items, item_fct, base_embed, footer_prefix='', msg=None, start=0, per_page=10):
|
||||||
embed = base_embed
|
embed = base_embed
|
||||||
|
|
||||||
# generate list
|
# generate list
|
||||||
@ -21,27 +26,30 @@ async def embed_list_paginated(bot, pre, items, item_fct, base_embed, footer_pre
|
|||||||
|
|
||||||
# post / edit message
|
# post / edit message
|
||||||
if msg is not None:
|
if msg is not None:
|
||||||
await bot.edit_message(msg, embed=embed)
|
await msg.edit(embed=embed)
|
||||||
if str(msg.channel.type) != 'private':
|
if not isinstance(msg.channel, discord.abc.PrivateChannel):
|
||||||
await bot.clear_reactions(msg)
|
await msg.clear_reactions()
|
||||||
else:
|
else:
|
||||||
msg = await bot.say(embed=embed)
|
msg = await ctx.send(embed=embed)
|
||||||
|
|
||||||
# add reactions
|
# add reactions
|
||||||
if start > 0:
|
if start > 0:
|
||||||
await bot.add_reaction(msg, '⏪')
|
await msg.add_reaction('⏪')
|
||||||
if items.__len__() > start+per_page:
|
if items.__len__() > start+per_page:
|
||||||
await bot.add_reaction(msg, '⏩')
|
await msg.add_reaction('⏩')
|
||||||
|
|
||||||
# wait for reactions (2 minutes)
|
# wait for reactions (2 minutes)
|
||||||
def check(reaction, user):
|
def check(reaction, user):
|
||||||
return reaction.emoji if user != bot.user else False
|
return True if user != bot.user and str(reaction.emoji) in ['⏪', '⏩'] and reaction.message.id == msg.id else False
|
||||||
res = await bot.wait_for_reaction(emoji=['⏪', '⏩'], message=msg, timeout=120, check=check)
|
try:
|
||||||
|
reaction, user = await bot.wait_for('reaction_add', timeout=120, check=check)
|
||||||
# redirect on reaction
|
except asyncio.TimeoutError:
|
||||||
if res is None:
|
pass
|
||||||
return
|
else:
|
||||||
elif res.reaction.emoji == '⏪' and start > 0:
|
# redirect on reaction
|
||||||
await embed_list_paginated(bot, pre, items, item_fct, base_embed, footer_prefix=footer_prefix, msg=msg, start=start-per_page, per_page=per_page)
|
if reaction is None:
|
||||||
elif res.reaction.emoji == '⏩' and items.__len__() > start+per_page:
|
return
|
||||||
await embed_list_paginated(bot, pre, items, item_fct, base_embed, footer_prefix=footer_prefix, msg=msg, start=start+per_page, per_page=per_page)
|
elif reaction.emoji == '⏪' and start > 0:
|
||||||
|
await embed_list_paginated(ctx, bot, pre, items, item_fct, base_embed, footer_prefix=footer_prefix, msg=msg, start=start-per_page, per_page=per_page)
|
||||||
|
elif reaction.emoji == '⏩' and items.__len__() > start+per_page:
|
||||||
|
await embed_list_paginated(ctx, bot, pre, items, item_fct, base_embed, footer_prefix=footer_prefix, msg=msg, start=start+per_page, per_page=per_page)
|
||||||
@ -6,9 +6,9 @@ animals = ['frog', 'newt', 'tadpole', 'toad', 'spider', 'biddy', 'canary', 'crow
|
|||||||
async def generate_word(bot, server_id):
|
async def generate_word(bot, server_id):
|
||||||
exists = 1
|
exists = 1
|
||||||
while exists is not None:
|
while exists is not None:
|
||||||
ad = random.sample(adjs, 2)
|
ad = random.sample(adjs, 1)
|
||||||
an = random.sample(animals, 1)
|
an = random.sample(animals, 1)
|
||||||
short = ad[0].capitalize() + ad[1].capitalize() + an[0].capitalize()
|
short = ad[0].capitalize() + an[0].capitalize()
|
||||||
exists = await bot.db.polls.find_one({'server_id': str(server_id), 'short': short})
|
exists = await bot.db.polls.find_one({'server_id': server_id, 'short': short})
|
||||||
|
|
||||||
return short
|
return short
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user