feat(commands|Polls>propose): feat poll proposition + acceptation

This commit is contained in:
Romain J 2021-01-22 10:22:39 +01:00
parent 37bbf0368e
commit 1fb3e035bd
13 changed files with 294 additions and 116 deletions

View file

@ -16,6 +16,9 @@ update:
update_soft:
$(VENV)/bin/pip install --upgrade .
dev: reformat update_soft
tuxbot dev
# Blackify code
reformat:
$(PYTHON) -m black `git ls-files "*.py"` --line-length=79 && $(PYTHON) -m pylint tuxbot

View file

@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: Tuxbot-bot\n"
"Report-Msgid-Bugs-To: rick@gnous.eu\n"
"POT-Creation-Date: 2021-01-20 17:24+0100\n"
"POT-Creation-Date: 2021-01-22 10:16+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"

View file

@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: Tuxbot-bot\n"
"Report-Msgid-Bugs-To: rick@gnous.eu\n"
"POT-Creation-Date: 2021-01-20 17:24+0100\n"
"POT-Creation-Date: 2021-01-22 10:16+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@ -36,14 +36,14 @@ msgstr ""
msgid "The alias `{alias}` for the command `{command}` was successfully created"
msgstr ""
#: tuxbot/cogs/Custom/functions/converters.py:14
#: tuxbot/cogs/Custom/functions/converters.py:15
msgid "Alias must be like `[command] | [alias]`"
msgstr ""
#: tuxbot/cogs/Custom/functions/converters.py:23
#: tuxbot/cogs/Custom/functions/converters.py:24
msgid "Unknown command"
msgstr ""
#: tuxbot/cogs/Custom/functions/converters.py:26
#: tuxbot/cogs/Custom/functions/converters.py:27
msgid "Command already exists"
msgstr ""

View file

@ -42,12 +42,12 @@ class PollConverter(commands.Converter):
poll = await Poll.get_or_none(id=int(poll_id))
if poll.channel_id != ctx.channel.id:
raise InvalidChannel(_("Please provide a message in this channel"))
if poll is None:
raise BadPoll(_("Unable to find this poll"))
if poll.channel_id != ctx.channel.id:
raise InvalidChannel(_("Please provide a message in this channel"))
return poll

View file

@ -0,0 +1,133 @@
import json
from os.path import dirname
import discord
from tuxbot.cogs import Polls
from tuxbot.core.i18n import Translator
from tuxbot.core.utils.functions.utils import upper_first
from . import emotes as utils_emotes
from .exceptions import InvalidChannel, BadPoll, TooLongProposition
from ..models import Response, Poll, Suggest
_ = Translator("Polls", dirname(__file__))
async def _poll_reaction_add(
self: Polls, pld: discord.RawReactionActionEvent, poll: Poll
):
if poll.is_anonymous:
try:
await self.remove_reaction(pld)
except discord.errors.Forbidden:
pass
choice = utils_emotes.get_index(pld.emoji.name)
response = await Response.get_or_none(
user_id=pld.user_id, choice=choice, poll__id=poll.id
)
if response is not None:
await poll.choices.remove(response)
await response.delete()
else:
res = await Response.create(
user_id=pld.user_id, poll=poll, choice=choice
)
await poll.choices.add(res)
await self.update_poll(poll)
async def _suggest_reaction_add(
self: Polls, pld: discord.RawReactionActionEvent, suggest: Suggest
):
poll = await suggest.poll
if (
poll.author_id == pld.user_id
or (await self.bot.is_owner(discord.Object(pld.user_id)))
or (
(channel := await self.bot.fetch_channel(pld.channel_id))
.permissions_for(await channel.guild.fetch_member(pld.user_id))
.administrator
)
):
if pld.emoji.name == utils_emotes.check[0]:
poll.available_choices += 1
emote = utils_emotes.emotes[poll.available_choices - 1]
content = (
json.loads(poll.content)
if isinstance(poll.content, str)
else poll.content
)
content["fields"].append(
{
"name": f"__{emote} - {upper_first(suggest.proposition)}__",
"value": "**0** vote",
}
)
await poll.save()
channel: discord.TextChannel = await self.bot.fetch_channel(
poll.channel_id
)
message = await channel.fetch_message(poll.message_id)
await message.add_reaction(emote)
await self.update_poll(poll)
channel: discord.TextChannel = await self.bot.fetch_channel(
suggest.channel_id
)
message = await channel.fetch_message(suggest.message_id)
await message.delete()
await suggest.delete()
else:
try:
await self.remove_reaction(pld)
except discord.errors.Forbidden:
pass
async def cog_command_error(self: Polls, ctx, error):
if isinstance(error, (InvalidChannel, BadPoll, TooLongProposition)):
await ctx.send(_(str(error), ctx, self.bot.config))
async def on_raw_reaction_add(
self: Polls, pld: discord.RawReactionActionEvent
):
poll = await self.get_poll(pld)
if poll:
await _poll_reaction_add(self, pld, poll)
elif suggest := await self.get_suggest(pld):
await _suggest_reaction_add(self, pld, suggest)
async def on_raw_reaction_remove(self, pld: discord.RawReactionActionEvent):
poll = await self.get_poll(pld)
if poll:
choice = utils_emotes.get_index(pld.emoji.name)
response = await Response.get_or_none(
user_id=pld.user_id, choice=choice, poll__id=poll.id
)
if response is not None:
await poll.choices.remove(response)
await response.delete()
await self.update_poll(poll)

View file

@ -18,32 +18,40 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: tuxbot/cogs/Polls/polls.py:92
#: tuxbot/cogs/Polls/polls.py:55 tuxbot/cogs/Polls/polls.py:176
msgid "**Preparation**"
msgstr ""
#: tuxbot/cogs/Polls/polls.py:221
#: tuxbot/cogs/Polls/polls.py:204
#, python-brace-format
msgid "Proposed addition for survey #{id}"
msgid "Proposed addition for poll #{id}"
msgstr ""
#: tuxbot/cogs/Polls/polls.py:226
#: tuxbot/cogs/Polls/polls.py:209
msgid "Poll"
msgstr ""
#: tuxbot/cogs/Polls/polls.py:211
msgid "here"
msgstr ""
#: tuxbot/cogs/Polls/polls.py:215
#, python-brace-format
msgid "Requested by {author}"
msgstr ""
#: tuxbot/cogs/Polls/functions/converters.py:20
#: tuxbot/cogs/Polls/functions/converters.py:45
#: tuxbot/cogs/Polls/functions/converters.py:21
#: tuxbot/cogs/Polls/functions/converters.py:49
msgid "Please provide a message in this channel"
msgstr ""
#: tuxbot/cogs/Polls/functions/converters.py:29
#: tuxbot/cogs/Polls/functions/converters.py:35
#: tuxbot/cogs/Polls/functions/converters.py:40
#: tuxbot/cogs/Polls/functions/converters.py:48
#: tuxbot/cogs/Polls/functions/converters.py:30
#: tuxbot/cogs/Polls/functions/converters.py:36
#: tuxbot/cogs/Polls/functions/converters.py:41
#: tuxbot/cogs/Polls/functions/converters.py:46
msgid "Unable to find this poll"
msgstr ""
#: tuxbot/cogs/Polls/functions/converters.py:57
#: tuxbot/cogs/Polls/functions/converters.py:58
msgid "Your proposal must be smaller than 30"
msgstr ""

View file

@ -18,32 +18,41 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
#: tuxbot/cogs/Polls/polls.py:92
#: tuxbot/cogs/Polls/polls.py:55 tuxbot/cogs/Polls/polls.py:176
msgid "**Preparation**"
msgstr "**Préparation**"
#: tuxbot/cogs/Polls/polls.py:221
#: tuxbot/cogs/Polls/polls.py:204
#, python-brace-format
msgid "Proposed addition for poll #{id}"
msgstr "Proposition d'ajout pour le sondage #{id}"
#: tuxbot/cogs/Polls/polls.py:226
#: tuxbot/cogs/Polls/polls.py:209
msgid "Poll"
msgstr "Sondage"
#: tuxbot/cogs/Polls/polls.py:211
msgid "here"
msgstr "ici"
#: tuxbot/cogs/Polls/polls.py:215
#, python-brace-format
msgid "Requested by {author}"
msgstr "Demandé par {author}"
msgstr "Demandée par {author}"
#: tuxbot/cogs/Polls/functions/converters.py:20
#: tuxbot/cogs/Polls/functions/converters.py:45
#: tuxbot/cogs/Polls/functions/converters.py:21
#: tuxbot/cogs/Polls/functions/converters.py:49
msgid "Please provide a message in this channel"
msgstr "Veuillez fournir un message dans ce salon"
#: tuxbot/cogs/Polls/functions/converters.py:29
#: tuxbot/cogs/Polls/functions/converters.py:35
#: tuxbot/cogs/Polls/functions/converters.py:40
#: tuxbot/cogs/Polls/functions/converters.py:48
#: tuxbot/cogs/Polls/functions/converters.py:30
#: tuxbot/cogs/Polls/functions/converters.py:36
#: tuxbot/cogs/Polls/functions/converters.py:41
#: tuxbot/cogs/Polls/functions/converters.py:46
msgid "Unable to find this poll"
msgstr "Impossible de trouver ce sondage"
#: tuxbot/cogs/Polls/functions/converters.py:57
#: tuxbot/cogs/Polls/functions/converters.py:58
msgid "Your proposal must be smaller than 30"
msgstr "Votre proposition doit être inférieure à 30"

View file

@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: Tuxbot-bot\n"
"Report-Msgid-Bugs-To: rick@gnous.eu\n"
"POT-Creation-Date: 2021-01-20 17:24+0100\n"
"POT-Creation-Date: 2021-01-22 10:16+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@ -17,32 +17,40 @@ msgstr ""
"Content-Type: text/plain; charset=CHARSET\n"
"Content-Transfer-Encoding: 8bit\n"
#: tuxbot/cogs/Polls/polls.py:92
#: tuxbot/cogs/Polls/polls.py:55 tuxbot/cogs/Polls/polls.py:176
msgid "**Preparation**"
msgstr ""
#: tuxbot/cogs/Polls/polls.py:221
#: tuxbot/cogs/Polls/polls.py:204
#, python-brace-format
msgid "Proposed addition for poll #{id}"
msgstr ""
#: tuxbot/cogs/Polls/polls.py:226
#: tuxbot/cogs/Polls/polls.py:209
msgid "Poll"
msgstr ""
#: tuxbot/cogs/Polls/polls.py:211
msgid "here"
msgstr ""
#: tuxbot/cogs/Polls/polls.py:215
#, python-brace-format
msgid "Requested by {author}"
msgstr ""
#: tuxbot/cogs/Polls/functions/converters.py:20
#: tuxbot/cogs/Polls/functions/converters.py:45
#: tuxbot/cogs/Polls/functions/converters.py:21
#: tuxbot/cogs/Polls/functions/converters.py:49
msgid "Please provide a message in this channel"
msgstr ""
#: tuxbot/cogs/Polls/functions/converters.py:29
#: tuxbot/cogs/Polls/functions/converters.py:35
#: tuxbot/cogs/Polls/functions/converters.py:40
#: tuxbot/cogs/Polls/functions/converters.py:48
#: tuxbot/cogs/Polls/functions/converters.py:30
#: tuxbot/cogs/Polls/functions/converters.py:36
#: tuxbot/cogs/Polls/functions/converters.py:41
#: tuxbot/cogs/Polls/functions/converters.py:46
msgid "Unable to find this poll"
msgstr ""
#: tuxbot/cogs/Polls/functions/converters.py:57
#: tuxbot/cogs/Polls/functions/converters.py:58
msgid "Your proposal must be smaller than 30"
msgstr ""

View file

@ -5,7 +5,9 @@ from tortoise import fields
class Suggest(tortoise.Model):
suggest_id = fields.BigIntField(pk=True)
poll = fields.ForeignKeyField("models.Poll")
user_id = fields.BigIntField()
channel_id = fields.BigIntField()
message_id = fields.BigIntField()
author_id = fields.BigIntField()
proposition = fields.CharField(max_length=30)
@ -15,7 +17,7 @@ class Suggest(tortoise.Model):
def __str__(self):
return (
f"<suggest_id poll={self.poll} "
f"user_id={self.user_id} "
f"author_id={self.author_id} "
f"proposition={self.proposition}>"
)

View file

@ -7,14 +7,14 @@ from discord.ext import commands
from yarl import URL
from tuxbot.core.utils.functions.extra import ContextPlus, group_extra
from tuxbot.core.utils.functions.utils import upper_first
from tuxbot.core.bot import Tux
from tuxbot.core.i18n import (
Translator,
)
from .functions import emotes as utils_emotes
from .functions import emotes as utils_emotes, listeners
from .functions.converters import NewPropositionConvertor, PollConverter
from .functions.exceptions import InvalidChannel, BadPoll, TooLongProposition
from .models import Poll, Response
from .models import Poll
from .models.suggests import Suggest
log = logging.getLogger("tuxbot.cogs.Polls")
@ -26,54 +26,17 @@ class Polls(commands.Cog, name="Polls"):
self.bot = bot
async def cog_command_error(self, ctx, error):
if isinstance(error, (InvalidChannel, BadPoll, TooLongProposition)):
await ctx.send(_(str(error), ctx, self.bot.config))
await listeners.cog_command_error(self, ctx, error)
@commands.Cog.listener()
async def on_raw_reaction_add(self, pld: discord.RawReactionActionEvent):
poll = await self.get_poll(pld)
if poll:
if poll.is_anonymous:
try:
await self.remove_reaction(pld)
except discord.errors.Forbidden:
pass
choice = utils_emotes.get_index(pld.emoji.name)
response = await Response.get_or_none(
user_id=pld.user_id, choice=choice, poll__id=poll.id
)
if response is not None:
await poll.choices.remove(response)
await response.delete()
else:
res = await Response.create(
user_id=pld.user_id, poll=poll, choice=choice
)
await poll.choices.add(res)
await self.update_poll(poll)
await listeners.on_raw_reaction_add(self, pld)
@commands.Cog.listener()
async def on_raw_reaction_remove(
self, pld: discord.RawReactionActionEvent
):
poll = await self.get_poll(pld)
if poll:
choice = utils_emotes.get_index(pld.emoji.name)
response = await Response.get_or_none(
user_id=pld.user_id, choice=choice, poll__id=poll.id
)
if response is not None:
await poll.choices.remove(response)
await response.delete()
await self.update_poll(poll)
await listeners.on_raw_reaction_remove(self, pld)
# =========================================================================
# =========================================================================
@ -113,7 +76,7 @@ class Polls(commands.Cog, name="Polls"):
)
for i, answer in enumerate(answers):
e.add_field(
name=f"__{emotes[i]} - {answer.capitalize()}__",
name=f"__{emotes[i]} - {upper_first(answer)}__",
value="**0** vote",
)
e.set_footer(text=f"ID: #{poll_row.id}")
@ -205,21 +168,49 @@ class Polls(commands.Cog, name="Polls"):
await message.remove_reaction(pld.emoji.name, user)
async def propose_new(
async def created_suggest(
self, ctx: ContextPlus, poll: PollConverter, new: str
):
await Suggest.create(poll=poll, user_id=ctx.author.id, proposition=new)
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
if isinstance(poll, Poll):
# pylint: disable=pointless-string-statement
"""Just to change type for PyCharm"""
suggest_row.poll = poll
suggest_row.proposition = new
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)
e = discord.Embed(
title=_(
"Proposed addition for poll #{id}", ctx, self.bot.config
).format(id=poll.id),
description=new,
)
e.add_field(
name=_("Poll", ctx, self.bot.config),
value="[{}]({})".format(
_("here", ctx, self.bot.config), poll_message.jump_url
),
)
e.set_footer(
text=_("Requested by {author}", ctx, self.bot.config).format(
author=ctx.author.name
@ -227,10 +218,21 @@ class Polls(commands.Cog, name="Polls"):
icon_url=ctx.author.avatar_url_as(format="png"),
)
message = await ctx.send(embed=e)
await stmt.edit(content="", embed=e)
for emote in utils_emotes.check:
await message.add_reaction(emote)
await stmt.add_reaction(emote)
async def get_suggest(
self, pld: discord.RawReactionActionEvent
) -> Union[bool, Suggest]:
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
# =========================================================================
# =========================================================================
@ -242,13 +244,13 @@ class Polls(commands.Cog, name="Polls"):
@_poll.command(name="create", aliases=["new", "nouveau"])
async def _poll_create(self, ctx: ContextPlus, *, poll: str):
args: list = poll.lower().split()
args: list = poll.split()
is_anonymous = False
if "--anonymous" in args:
if "--anonymous" in map(str.lower, args):
is_anonymous = True
args.remove("--anonymous")
elif "--anonyme" in args:
elif "--anonyme" in map(str.lower, args):
is_anonymous = True
args.remove("--anonyme")
@ -257,14 +259,14 @@ class Polls(commands.Cog, name="Polls"):
delimiters = [i for i, val in enumerate(args) if val == "|"]
question = " ".join(args[: delimiters[0]]).capitalize()
question = upper_first(" ".join(args[: delimiters[0]]))
answers = []
for i in range(len(delimiters) - 1):
start = delimiters[i] + 1
end = delimiters[i + 1]
answers.append(" ".join(args[start:end]).capitalize())
answers.append(upper_first(" ".join(args[start:end])))
await self.create_poll(ctx, question, answers, anonymous=is_anonymous)
@ -276,4 +278,4 @@ class Polls(commands.Cog, name="Polls"):
*,
new: NewPropositionConvertor,
):
await self.propose_new(ctx, poll, str(new))
await self.created_suggest(ctx, poll, str(new))

View file

@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: Tuxbot-bot\n"
"Report-Msgid-Bugs-To: rick@gnous.eu\n"
"POT-Creation-Date: 2021-01-20 17:24+0100\n"
"POT-Creation-Date: 2021-01-22 10:16+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@ -55,62 +55,62 @@ msgstr ""
msgid "class"
msgstr ""
#: tuxbot/cogs/Utils/utils.py:100
#: tuxbot/cogs/Utils/utils.py:101
msgid "functions"
msgstr ""
#: tuxbot/cogs/Utils/utils.py:102
#: tuxbot/cogs/Utils/utils.py:104
msgid "coroutines"
msgstr ""
#: tuxbot/cogs/Utils/utils.py:104
#: tuxbot/cogs/Utils/utils.py:107
msgid "comments"
msgstr ""
#: tuxbot/cogs/Utils/utils.py:109
#: tuxbot/cogs/Utils/utils.py:113
msgid "__Latest changes__"
msgstr ""
#: tuxbot/cogs/Utils/utils.py:115
#: tuxbot/cogs/Utils/utils.py:119
msgid "__:link: Links__"
msgstr ""
#: tuxbot/cogs/Utils/utils.py:120
#: tuxbot/cogs/Utils/utils.py:124
msgid "| [Invite]"
msgstr ""
#: tuxbot/cogs/Utils/utils.py:135
#: tuxbot/cogs/Utils/utils.py:139
msgid "Contributors"
msgstr ""
#: tuxbot/cogs/Utils/utils.py:191
#: tuxbot/cogs/Utils/utils.py:195
msgid "Invite"
msgstr ""
#: tuxbot/cogs/Utils/utils.py:195
#: tuxbot/cogs/Utils/utils.py:199
msgid "Minimal"
msgstr ""
#: tuxbot/cogs/Utils/utils.py:197
#: tuxbot/cogs/Utils/utils.py:201
msgid "The minimum permissions include the strict requirements for the proper functioning of all basics commands.\n"
msgstr ""
#: tuxbot/cogs/Utils/utils.py:202 tuxbot/cogs/Utils/utils.py:214
#: tuxbot/cogs/Utils/utils.py:206 tuxbot/cogs/Utils/utils.py:218
msgid "[Add!]"
msgstr ""
#: tuxbot/cogs/Utils/utils.py:207
#: tuxbot/cogs/Utils/utils.py:211
msgid "Admin"
msgstr ""
#: tuxbot/cogs/Utils/utils.py:209
#: tuxbot/cogs/Utils/utils.py:213
msgid "All minimal permissions + extra permissions for admin commands such as kick and ban\n"
msgstr ""
#: tuxbot/cogs/Utils/utils.py:237
#: tuxbot/cogs/Utils/utils.py:241
msgid "Unable to find `{}`"
msgstr ""
#: tuxbot/cogs/Utils/utils.py:245
#: tuxbot/cogs/Utils/utils.py:249
msgid "Unable to fetch lines for `{}`"
msgstr ""

View file

@ -36,7 +36,7 @@ def is_mod():
def is_admin():
"""Is the user Admin ?"""
"""Is the user Admin ? as @check"""
async def pred(ctx):
if await ctx.bot.is_owner(ctx.author):
@ -49,6 +49,17 @@ def is_admin():
return commands.check(pred)
async def is_user_admin(ctx):
"""Is the user Admin ? as function"""
if await ctx.bot.is_owner(ctx.author):
return True
permissions: discord.Permissions = ctx.channel.permissions_for(ctx.author)
return permissions.administrator
async def check_permissions(ctx: ContextPlus, **perms: Dict[str, bool]):
"""Does a user have any perms ?

View file

@ -0,0 +1,2 @@
def upper_first(string: str) -> str:
return "".join(string[0].upper() + string[1:])