2021-01-20 14:51:04 +01:00
|
|
|
import json
|
2020-11-12 00:03:01 +01:00
|
|
|
import logging
|
2021-05-16 23:21:27 +02:00
|
|
|
from typing import Dict, Union, List
|
2020-11-12 00:03:01 +01:00
|
|
|
|
2021-01-20 14:51:04 +01:00
|
|
|
import discord
|
2020-11-12 00:03:01 +01:00
|
|
|
from discord.ext import commands
|
2021-01-20 14:51:04 +01:00
|
|
|
from yarl import URL
|
2020-11-12 00:03:01 +01:00
|
|
|
|
|
|
|
from tuxbot.core.utils.functions.extra import ContextPlus, group_extra
|
2021-01-22 10:22:39 +01:00
|
|
|
from tuxbot.core.utils.functions.utils import upper_first
|
2020-11-12 00:03:01 +01:00
|
|
|
from tuxbot.core.bot import Tux
|
|
|
|
from tuxbot.core.i18n import (
|
|
|
|
Translator,
|
|
|
|
)
|
2021-01-22 10:22:39 +01:00
|
|
|
from .functions import emotes as utils_emotes, listeners
|
2021-01-20 17:28:37 +01:00
|
|
|
from .functions.converters import NewPropositionConvertor, PollConverter
|
2021-01-22 10:22:39 +01:00
|
|
|
from .models import Poll
|
2021-01-20 17:28:37 +01:00
|
|
|
from .models.suggests import Suggest
|
2020-11-12 00:03:01 +01:00
|
|
|
|
|
|
|
log = logging.getLogger("tuxbot.cogs.Polls")
|
|
|
|
_ = Translator("Polls", __file__)
|
|
|
|
|
|
|
|
|
2021-04-20 17:12:38 +02:00
|
|
|
class Polls(commands.Cog):
|
2020-11-12 00:03:01 +01:00
|
|
|
def __init__(self, bot: Tux):
|
|
|
|
self.bot = bot
|
|
|
|
|
2021-01-20 17:28:37 +01:00
|
|
|
async def cog_command_error(self, ctx, error):
|
2021-01-22 10:22:39 +01:00
|
|
|
await listeners.cog_command_error(self, ctx, error)
|
2021-01-20 17:28:37 +01:00
|
|
|
|
2021-01-20 14:51:04 +01:00
|
|
|
@commands.Cog.listener()
|
|
|
|
async def on_raw_reaction_add(self, pld: discord.RawReactionActionEvent):
|
2021-01-22 10:22:39 +01:00
|
|
|
await listeners.on_raw_reaction_add(self, pld)
|
2021-01-20 14:51:04 +01:00
|
|
|
|
|
|
|
@commands.Cog.listener()
|
|
|
|
async def on_raw_reaction_remove(
|
|
|
|
self, pld: discord.RawReactionActionEvent
|
|
|
|
):
|
2021-01-22 10:22:39 +01:00
|
|
|
await listeners.on_raw_reaction_remove(self, pld)
|
2021-01-20 14:51:04 +01:00
|
|
|
|
|
|
|
# =========================================================================
|
|
|
|
# =========================================================================
|
|
|
|
|
|
|
|
async def create_poll(
|
|
|
|
self,
|
|
|
|
ctx: ContextPlus,
|
|
|
|
question: str,
|
2021-05-16 23:21:27 +02:00
|
|
|
answers: List[str],
|
2021-01-20 14:51:04 +01:00
|
|
|
anonymous=False,
|
|
|
|
):
|
|
|
|
emotes = utils_emotes.get(len(answers))
|
|
|
|
|
|
|
|
stmt = await ctx.send(
|
|
|
|
_(
|
|
|
|
"**Preparation**",
|
|
|
|
ctx,
|
|
|
|
self.bot.config,
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
|
|
|
poll_row = await Poll()
|
|
|
|
|
|
|
|
poll_row.channel_id = stmt.channel.id
|
|
|
|
poll_row.message_id = stmt.id
|
|
|
|
poll_row.author_id = ctx.author.id
|
2021-04-20 17:12:38 +02:00
|
|
|
poll_row.content = {} # type: ignore
|
2021-01-20 14:51:04 +01:00
|
|
|
poll_row.is_anonymous = anonymous
|
2021-04-20 17:12:38 +02:00
|
|
|
poll_row.available_choices = len(answers) # type: ignore
|
2021-01-20 14:51:04 +01:00
|
|
|
|
|
|
|
await poll_row.save()
|
|
|
|
|
|
|
|
e = discord.Embed(description=f"**{question}**")
|
|
|
|
e.set_author(
|
|
|
|
name=ctx.author,
|
|
|
|
icon_url="https://img.icons8.com/plasticine/100/000000/survey.png",
|
|
|
|
)
|
|
|
|
for i, answer in enumerate(answers):
|
|
|
|
e.add_field(
|
2021-01-22 10:22:39 +01:00
|
|
|
name=f"__{emotes[i]} - {upper_first(answer)}__",
|
2021-01-20 14:51:04 +01:00
|
|
|
value="**0** vote",
|
|
|
|
)
|
|
|
|
e.set_footer(text=f"ID: #{poll_row.id}")
|
|
|
|
|
|
|
|
poll_row.content = e.to_dict()
|
|
|
|
await poll_row.save()
|
|
|
|
|
|
|
|
await stmt.edit(content="", embed=e)
|
|
|
|
for emote in range(len(answers)):
|
|
|
|
await stmt.add_reaction(emotes[emote])
|
|
|
|
|
|
|
|
async def get_poll(
|
|
|
|
self, pld: discord.RawReactionActionEvent
|
2021-05-16 23:21:27 +02:00
|
|
|
) -> Union[bool, Poll]:
|
2021-01-20 14:51:04 +01:00
|
|
|
if pld.user_id != self.bot.user.id:
|
|
|
|
poll = await Poll.get_or_none(message_id=pld.message_id)
|
|
|
|
|
|
|
|
if poll is not None:
|
|
|
|
emotes = utils_emotes.get(poll.available_choices)
|
|
|
|
|
|
|
|
if pld.emoji.name in emotes:
|
|
|
|
return poll
|
|
|
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
async def update_poll(self, poll: Poll):
|
|
|
|
channel: discord.TextChannel = self.bot.get_channel(poll.channel_id)
|
|
|
|
message: discord.Message = await channel.fetch_message(poll.message_id)
|
|
|
|
|
|
|
|
chart_base_url = "https://quickchart.io/chart?backgroundColor=white&c="
|
|
|
|
chart_options = {
|
|
|
|
"type": "pie",
|
|
|
|
"data": {"labels": [], "datasets": [{"data": []}]},
|
|
|
|
}
|
|
|
|
|
|
|
|
content = (
|
2021-04-20 17:12:38 +02:00
|
|
|
json.loads(poll.content) # type: ignore
|
2021-01-20 14:51:04 +01:00
|
|
|
if isinstance(poll.content, str)
|
|
|
|
else poll.content
|
|
|
|
)
|
|
|
|
|
2021-04-20 17:12:38 +02:00
|
|
|
responses: Dict[int, int] = {}
|
2021-01-20 14:51:04 +01:00
|
|
|
|
|
|
|
async for response in poll.choices:
|
|
|
|
if responses.get(response.choice):
|
|
|
|
responses[response.choice] += 1
|
|
|
|
else:
|
|
|
|
responses[response.choice] = 1
|
|
|
|
|
2021-04-20 17:12:38 +02:00
|
|
|
for i, field in enumerate(content.get("fields")): # type: ignore
|
2021-01-20 14:51:04 +01:00
|
|
|
responders = responses.get(i, 0)
|
|
|
|
|
2021-04-20 17:12:38 +02:00
|
|
|
chart_options["data"]["labels"].append( # type: ignore
|
2021-01-21 16:11:29 +01:00
|
|
|
field["name"][6:].replace("__", "")
|
2021-01-20 14:51:04 +01:00
|
|
|
)
|
|
|
|
|
2021-04-20 17:12:38 +02:00
|
|
|
chart_options["data"]["datasets"][0]["data"].append(responders) # type: ignore
|
2021-01-20 14:51:04 +01:00
|
|
|
|
|
|
|
if responders <= 1:
|
|
|
|
field["value"] = f"**{responders}** vote"
|
|
|
|
else:
|
|
|
|
field["value"] = f"**{responders}** votes"
|
|
|
|
|
|
|
|
e = discord.Embed(description=content.get("description"))
|
|
|
|
e.set_author(
|
2021-04-20 17:12:38 +02:00
|
|
|
name=content.get("author").get("name"), # type: ignore
|
|
|
|
icon_url=content.get("author").get("icon_url"), # type: ignore
|
2021-01-20 14:51:04 +01:00
|
|
|
)
|
|
|
|
chart_url = URL(chart_base_url + json.dumps(chart_options))
|
|
|
|
e.set_thumbnail(url=str(chart_url))
|
|
|
|
|
2021-04-20 17:12:38 +02:00
|
|
|
for field in content.get("fields"): # type: ignore
|
2021-01-20 14:51:04 +01:00
|
|
|
e.add_field(
|
|
|
|
name=field.get("name"), value=field.get("value"), inline=True
|
|
|
|
)
|
|
|
|
|
2021-04-20 17:12:38 +02:00
|
|
|
e.set_footer(text=content.get("footer").get("text")) # type: ignore
|
2021-01-20 14:51:04 +01:00
|
|
|
|
|
|
|
await message.edit(embed=e)
|
|
|
|
|
2021-04-20 17:12:38 +02:00
|
|
|
poll.content = json.dumps(content) # type: ignore
|
2021-01-20 14:51:04 +01:00
|
|
|
|
|
|
|
await poll.save()
|
|
|
|
|
|
|
|
async def remove_reaction(self, pld: discord.RawReactionActionEvent):
|
|
|
|
channel: discord.TextChannel = self.bot.get_channel(pld.channel_id)
|
|
|
|
message: discord.Message = await channel.fetch_message(pld.message_id)
|
|
|
|
user: discord.User = await self.bot.fetch_user(pld.user_id)
|
|
|
|
|
|
|
|
await message.remove_reaction(pld.emoji.name, user)
|
|
|
|
|
2021-01-22 10:22:39 +01:00
|
|
|
async def created_suggest(
|
2021-01-20 17:28:37 +01:00
|
|
|
self, ctx: ContextPlus, poll: PollConverter, new: str
|
|
|
|
):
|
2021-01-22 10:22:39 +01:00
|
|
|
stmt = await ctx.send(
|
|
|
|
_(
|
|
|
|
"**Preparation**",
|
|
|
|
ctx,
|
|
|
|
self.bot.config,
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
|
|
|
suggest_row = await Suggest()
|
|
|
|
|
|
|
|
suggest_row.channel_id = ctx.channel.id
|
|
|
|
suggest_row.message_id = stmt.id
|
|
|
|
suggest_row.author_id = ctx.author.id
|
2021-01-20 17:28:37 +01:00
|
|
|
|
|
|
|
if isinstance(poll, Poll):
|
|
|
|
# pylint: disable=pointless-string-statement
|
|
|
|
"""Just to change type for PyCharm"""
|
|
|
|
|
2021-01-22 10:22:39 +01:00
|
|
|
suggest_row.poll = poll
|
2021-04-20 17:12:38 +02:00
|
|
|
suggest_row.proposition = new # type: ignore
|
2021-01-22 10:22:39 +01:00
|
|
|
|
|
|
|
await suggest_row.save()
|
|
|
|
|
|
|
|
poll_channel: discord.TextChannel = await self.bot.fetch_channel(
|
|
|
|
poll.channel_id
|
|
|
|
)
|
|
|
|
poll_message = await poll_channel.fetch_message(poll.message_id)
|
|
|
|
|
2021-01-20 17:28:37 +01:00
|
|
|
e = discord.Embed(
|
|
|
|
title=_(
|
|
|
|
"Proposed addition for poll #{id}", ctx, self.bot.config
|
|
|
|
).format(id=poll.id),
|
|
|
|
description=new,
|
|
|
|
)
|
2021-01-22 10:22:39 +01:00
|
|
|
e.add_field(
|
|
|
|
name=_("Poll", ctx, self.bot.config),
|
|
|
|
value="[{}]({})".format(
|
|
|
|
_("here", ctx, self.bot.config), poll_message.jump_url
|
|
|
|
),
|
|
|
|
)
|
2021-01-20 17:28:37 +01:00
|
|
|
e.set_footer(
|
|
|
|
text=_("Requested by {author}", ctx, self.bot.config).format(
|
|
|
|
author=ctx.author.name
|
|
|
|
),
|
2021-04-20 17:12:38 +02:00
|
|
|
icon_url=ctx.author.avatar.url,
|
2021-01-20 17:28:37 +01:00
|
|
|
)
|
|
|
|
|
2021-01-22 10:22:39 +01:00
|
|
|
await stmt.edit(content="", embed=e)
|
2021-01-20 17:28:37 +01:00
|
|
|
|
|
|
|
for emote in utils_emotes.check:
|
2021-01-22 10:22:39 +01:00
|
|
|
await stmt.add_reaction(emote)
|
|
|
|
|
|
|
|
async def get_suggest(
|
|
|
|
self, pld: discord.RawReactionActionEvent
|
2021-05-16 23:21:27 +02:00
|
|
|
) -> Union[bool, Suggest]:
|
2021-01-22 10:22:39 +01:00
|
|
|
if pld.user_id != self.bot.user.id:
|
|
|
|
suggest = await Suggest.get_or_none(message_id=pld.message_id)
|
|
|
|
|
|
|
|
if suggest is not None and pld.emoji.name in utils_emotes.check:
|
|
|
|
return suggest
|
|
|
|
|
|
|
|
return False
|
2021-01-20 17:28:37 +01:00
|
|
|
|
2020-11-12 00:03:01 +01:00
|
|
|
# =========================================================================
|
|
|
|
# =========================================================================
|
|
|
|
|
|
|
|
@group_extra(name="polls", aliases=["poll", "sondages", "sondage"])
|
2021-01-20 14:51:04 +01:00
|
|
|
async def _poll(self, ctx: commands.Context):
|
2020-11-12 00:03:01 +01:00
|
|
|
if ctx.invoked_subcommand is None:
|
2021-01-20 14:51:04 +01:00
|
|
|
await ctx.send_help("poll")
|
2020-11-12 00:03:01 +01:00
|
|
|
|
2021-01-20 17:28:37 +01:00
|
|
|
@_poll.command(name="create", aliases=["new", "nouveau"])
|
2021-01-20 14:51:04 +01:00
|
|
|
async def _poll_create(self, ctx: ContextPlus, *, poll: str):
|
2021-01-22 10:22:39 +01:00
|
|
|
args: list = poll.split()
|
2021-01-20 14:51:04 +01:00
|
|
|
is_anonymous = False
|
2020-11-12 00:03:01 +01:00
|
|
|
|
2021-01-22 10:22:39 +01:00
|
|
|
if "--anonymous" in map(str.lower, args):
|
2021-01-20 14:51:04 +01:00
|
|
|
is_anonymous = True
|
|
|
|
args.remove("--anonymous")
|
2021-01-22 10:22:39 +01:00
|
|
|
elif "--anonyme" in map(str.lower, args):
|
2021-01-20 14:51:04 +01:00
|
|
|
is_anonymous = True
|
|
|
|
args.remove("--anonyme")
|
2020-11-12 00:03:01 +01:00
|
|
|
|
2021-01-20 14:51:04 +01:00
|
|
|
if args[-1] != "|":
|
|
|
|
args.append("|")
|
2020-11-12 00:03:01 +01:00
|
|
|
|
2021-01-20 14:51:04 +01:00
|
|
|
delimiters = [i for i, val in enumerate(args) if val == "|"]
|
2020-11-12 00:03:01 +01:00
|
|
|
|
2021-01-22 10:22:39 +01:00
|
|
|
question = upper_first(" ".join(args[: delimiters[0]]))
|
2021-01-20 14:51:04 +01:00
|
|
|
answers = []
|
2020-11-12 00:03:01 +01:00
|
|
|
|
2021-01-20 14:51:04 +01:00
|
|
|
for i in range(len(delimiters) - 1):
|
|
|
|
start = delimiters[i] + 1
|
|
|
|
end = delimiters[i + 1]
|
2020-11-12 00:03:01 +01:00
|
|
|
|
2021-01-22 10:22:39 +01:00
|
|
|
answers.append(upper_first(" ".join(args[start:end])))
|
2021-01-20 14:51:04 +01:00
|
|
|
|
|
|
|
await self.create_poll(ctx, question, answers, anonymous=is_anonymous)
|
2021-01-20 17:28:37 +01:00
|
|
|
|
|
|
|
@_poll.command(name="propose", aliases=["suggest", "ajout"])
|
|
|
|
async def _poll_propose(
|
|
|
|
self,
|
|
|
|
ctx: ContextPlus,
|
|
|
|
poll: PollConverter,
|
|
|
|
*,
|
|
|
|
new: NewPropositionConvertor,
|
|
|
|
):
|
2021-01-22 10:22:39 +01:00
|
|
|
await self.created_suggest(ctx, poll, str(new))
|