refactor(command|sondage): continue rewrite of sondage
known issues: datas are not commited in database on reaction on
This commit is contained in:
parent
d5f1f71a0a
commit
76e845e5be
18 changed files with 313 additions and 101 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -3,7 +3,6 @@ __pycache__/
|
|||
*.pyc
|
||||
.env
|
||||
config.py
|
||||
!cogs/utils/*
|
||||
.DS_Store
|
||||
private.py
|
||||
|
||||
|
@ -11,4 +10,4 @@ private.py
|
|||
.idea/
|
||||
|
||||
#other
|
||||
logs/*
|
||||
*.log
|
|
@ -6,12 +6,12 @@
|
|||
- [ ] Alias system for commands (e.g. `.alias .ci show .cs`)
|
||||
- [ ] Migrate MySQL to postgresql
|
||||
- [x] Prepare bot for python 3.8 and discord.py 1.3.0
|
||||
- [x] Create launcher
|
||||
- [ ] Create launcher
|
||||
- [ ] Create documentation
|
||||
|
||||
## Launcher requirements :
|
||||
|
||||
- [x] Can install the bot
|
||||
- [ ] Can install the bot
|
||||
- [x] Can launch the bot
|
||||
- [x] Can propose updates
|
||||
|
||||
|
@ -82,5 +82,5 @@
|
|||
|
||||
---
|
||||
|
||||
# Cogs.sondage commands `(renamed as cogs.poll)`
|
||||
# Cogs.sondage commands `(renamed as cogs.poll)` `canceled until the frontend development`
|
||||
- [ ] sondage (help?)
|
||||
|
|
7
bot.py
7
bot.py
|
@ -26,6 +26,7 @@ l_extensions = (
|
|||
'cogs.basics',
|
||||
'cogs.utility',
|
||||
'cogs.logs',
|
||||
'cogs.poll',
|
||||
'jishaku',
|
||||
)
|
||||
|
||||
|
@ -127,10 +128,12 @@ class TuxBot(commands.AutoShardedBot):
|
|||
@property
|
||||
def logs_webhook(self) -> discord.Webhook:
|
||||
logs_webhook = self.config.logs_webhook
|
||||
webhook = discord.Webhook.partial(id=logs_webhook.get('id'),
|
||||
webhook = discord.Webhook.partial(
|
||||
id=logs_webhook.get('id'),
|
||||
token=logs_webhook.get('token'),
|
||||
adapter=discord.AsyncWebhookAdapter(
|
||||
self.session)
|
||||
self.session
|
||||
)
|
||||
)
|
||||
|
||||
return webhook
|
||||
|
|
|
@ -9,9 +9,8 @@ import humanize
|
|||
from discord.ext import commands
|
||||
|
||||
from bot import TuxBot
|
||||
from .utils.models.lang import Lang
|
||||
from .utils.lang import Texts
|
||||
from .utils.models.warn import Warn
|
||||
from .utils.models import Warn, Lang
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
@ -87,7 +86,8 @@ class Admin(commands.Cog):
|
|||
message_id)
|
||||
await message.edit(content=content)
|
||||
except (discord.errors.NotFound, discord.errors.Forbidden):
|
||||
await ctx.send(Texts('utils', ctx).get("Unable to find the message"),
|
||||
await ctx.send(
|
||||
Texts('utils', ctx).get("Unable to find the message"),
|
||||
delete_after=5)
|
||||
|
||||
@_say.command(name='to')
|
||||
|
@ -120,10 +120,12 @@ class Admin(commands.Cog):
|
|||
|
||||
await ctx.send(embed=e)
|
||||
except discord.Forbidden:
|
||||
await ctx.send(Texts('admin', ctx).get("Unable to ban this user"),
|
||||
await ctx.send(
|
||||
Texts('admin', ctx).get("Unable to ban this user"),
|
||||
delete_after=5)
|
||||
except discord.errors.NotFound:
|
||||
await ctx.send(Texts('utils', ctx).get("Unable to find the user..."),
|
||||
await ctx.send(
|
||||
Texts('utils', ctx).get("Unable to find the user..."),
|
||||
delete_after=5)
|
||||
|
||||
"""---------------------------------------------------------------------"""
|
||||
|
@ -145,10 +147,12 @@ class Admin(commands.Cog):
|
|||
|
||||
await ctx.send(embed=e)
|
||||
except discord.Forbidden:
|
||||
await ctx.send(Texts('admin', ctx).get("Unable to kick this user"),
|
||||
await ctx.send(
|
||||
Texts('admin', ctx).get("Unable to kick this user"),
|
||||
delete_after=5)
|
||||
except discord.errors.NotFound:
|
||||
await ctx.send(Texts('utils', ctx).get("Unable to find the user..."),
|
||||
await ctx.send(
|
||||
Texts('utils', ctx).get("Unable to find the user..."),
|
||||
delete_after=5)
|
||||
|
||||
"""---------------------------------------------------------------------"""
|
||||
|
@ -180,7 +184,8 @@ class Admin(commands.Cog):
|
|||
for emoji in emojis:
|
||||
await message.add_reaction(emoji)
|
||||
except discord.errors.NotFound:
|
||||
await ctx.send(Texts('utils', ctx).get("Unable to find the message"),
|
||||
await ctx.send(
|
||||
Texts('utils', ctx).get("Unable to find the message"),
|
||||
delete_after=5)
|
||||
|
||||
@_react.command(name='clear')
|
||||
|
@ -190,7 +195,8 @@ class Admin(commands.Cog):
|
|||
message_id)
|
||||
await message.clear_reactions()
|
||||
except discord.errors.NotFound:
|
||||
await ctx.send(Texts('utils', ctx).get("Unable to find the message"),
|
||||
await ctx.send(
|
||||
Texts('utils', ctx).get("Unable to find the message"),
|
||||
delete_after=5)
|
||||
|
||||
"""---------------------------------------------------------------------"""
|
||||
|
@ -207,7 +213,8 @@ class Admin(commands.Cog):
|
|||
message_id)
|
||||
await message.delete()
|
||||
except (discord.errors.NotFound, discord.errors.Forbidden):
|
||||
await ctx.send(Texts('utils', ctx).get("Unable to find the message"),
|
||||
await ctx.send(
|
||||
Texts('utils', ctx).get("Unable to find the message"),
|
||||
delete_after=5)
|
||||
|
||||
@_delete.command(name='from', aliases=['to', 'in'])
|
||||
|
@ -223,7 +230,8 @@ class Admin(commands.Cog):
|
|||
message_id)
|
||||
await message.delete()
|
||||
except (discord.errors.NotFound, discord.errors.Forbidden):
|
||||
await ctx.send(Texts('utils', ctx).get("Unable to find the message"),
|
||||
await ctx.send(
|
||||
Texts('utils', ctx).get("Unable to find the message"),
|
||||
delete_after=5)
|
||||
|
||||
"""---------------------------------------------------------------------"""
|
||||
|
@ -401,7 +409,8 @@ class Admin(commands.Cog):
|
|||
.split(', ')
|
||||
|
||||
if locale.lower() not in available:
|
||||
await ctx.send(Texts('admin', ctx).get('Unable to find this language'))
|
||||
await ctx.send(
|
||||
Texts('admin', ctx).get('Unable to find this language'))
|
||||
else:
|
||||
current = self.bot.engine \
|
||||
.query(Lang) \
|
||||
|
@ -416,7 +425,8 @@ class Admin(commands.Cog):
|
|||
self.bot.engine.add(new_row)
|
||||
self.bot.engine.commit()
|
||||
|
||||
await ctx.send(Texts('admin', ctx).get('Language changed successfully'))
|
||||
await ctx.send(
|
||||
Texts('admin', ctx).get('Language changed successfully'))
|
||||
|
||||
|
||||
def setup(bot: TuxBot):
|
||||
|
|
|
@ -55,7 +55,8 @@ class Basics(commands.Cog):
|
|||
file_amount += 1
|
||||
with open(file_dir, "r", encoding="utf-8") as file:
|
||||
for line in file:
|
||||
if not line.strip().startswith("#") or not line.strip():
|
||||
if not line.strip().startswith("#") \
|
||||
or not line.strip():
|
||||
total += 1
|
||||
|
||||
return total, file_amount
|
||||
|
@ -68,7 +69,7 @@ class Basics(commands.Cog):
|
|||
with proc.oneshot():
|
||||
mem = proc.memory_full_info()
|
||||
e = discord.Embed(
|
||||
title=f"{Texts('basics', ctx).get('Information about TuxBot')}",
|
||||
title=Texts('basics', ctx).get('Information about TuxBot'),
|
||||
color=0x89C4F9)
|
||||
|
||||
e.add_field(
|
||||
|
@ -129,7 +130,7 @@ class Basics(commands.Cog):
|
|||
name=f"__:link: {Texts('basics', ctx).get('Links')}__",
|
||||
value="[tuxbot.gnous.eu](https://tuxbot.gnous.eu/) "
|
||||
"| [gnous.eu](https://gnous.eu/) "
|
||||
f"| [{Texts('basics').get('Invite')}](https://discordapp.com/oauth2/authorize?client_id=301062143942590465&scope=bot&permissions=268749888)",
|
||||
f"| [{Texts('basics', ctx).get('Invite')}](https://discordapp.com/oauth2/authorize?client_id=301062143942590465&scope=bot&permissions=268749888)",
|
||||
inline=False
|
||||
)
|
||||
|
||||
|
|
123
cogs/poll.py
123
cogs/poll.py
|
@ -1,22 +1,133 @@
|
|||
from typing import Union
|
||||
|
||||
import discord
|
||||
import bcrypt
|
||||
from discord.ext import commands
|
||||
|
||||
from bot import TuxBot
|
||||
from .utils.lang import Texts
|
||||
from .utils.models import Poll
|
||||
from .utils import emotes as utils_emotes
|
||||
|
||||
|
||||
class Poll(commands.Cog):
|
||||
class Polls(commands.Cog):
|
||||
|
||||
def __init__(self, bot: TuxBot):
|
||||
self.bot = bot
|
||||
|
||||
def get_poll(self, pld) -> Union[bool, Poll]:
|
||||
if pld.user_id != self.bot.user.id:
|
||||
poll = self.bot.engine \
|
||||
.query(Poll) \
|
||||
.filter(Poll.message_id == pld.message_id) \
|
||||
.one_or_none()
|
||||
|
||||
if poll is not None:
|
||||
emotes = utils_emotes.get(len(poll.responses))
|
||||
|
||||
if pld.emoji.name in emotes:
|
||||
return poll
|
||||
|
||||
return False
|
||||
|
||||
async def remove_reaction(self, pld):
|
||||
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)
|
||||
|
||||
@commands.Cog.listener()
|
||||
async def on_raw_reaction_add(self, pld: discord.RawReactionActionEvent):
|
||||
poll = self.get_poll(pld)
|
||||
|
||||
if poll:
|
||||
if poll.is_anonymous:
|
||||
await self.remove_reaction(pld)
|
||||
|
||||
user_id = str(pld.user_id).encode()
|
||||
responses = poll.responses
|
||||
|
||||
choice = utils_emotes.get_index(pld.emoji.name) + 1
|
||||
responders = responses.get(str(choice))
|
||||
|
||||
if not responders:
|
||||
print(responders, 'before0')
|
||||
user_id_hash = bcrypt.hashpw(user_id, bcrypt.gensalt())
|
||||
responders.append(user_id_hash)
|
||||
print(responders, 'after0')
|
||||
else:
|
||||
for i, responder in enumerate(responders):
|
||||
if bcrypt.checkpw(user_id, responder.encode()):
|
||||
print(responders, 'before1')
|
||||
responders.pop(i)
|
||||
print(responders, 'after1')
|
||||
else:
|
||||
print(responders, 'before2')
|
||||
user_id_hash = bcrypt.hashpw(user_id, bcrypt.gensalt())
|
||||
responders.append(user_id_hash)
|
||||
print(responders, 'after2')
|
||||
|
||||
poll.responses = responses
|
||||
print(poll.responses)
|
||||
self.bot.engine.commit()
|
||||
|
||||
return 1
|
||||
|
||||
"""---------------------------------------------------------------------"""
|
||||
|
||||
async def make_poll(self, ctx: commands.Context, poll: str, anonymous):
|
||||
question = (poll.split('|')[0]).strip()
|
||||
responses = [response.strip() for response in poll.split('|')[1:]]
|
||||
responses_row = {}
|
||||
emotes = utils_emotes.get(len(responses))
|
||||
|
||||
stmt = await ctx.send(Texts('poll', ctx).get('**Preparation...**'))
|
||||
|
||||
poll_row = Poll()
|
||||
self.bot.engine.add(poll_row)
|
||||
self.bot.engine.flush()
|
||||
|
||||
e = discord.Embed(description=f"**{question}**")
|
||||
e.set_author(
|
||||
name=ctx.author,
|
||||
icon_url='https://cdn.pixabay.com/photo/2017/05/15/23/48/survey-2316468_960_720.png'
|
||||
)
|
||||
for i, response in enumerate(responses):
|
||||
responses_row[str(i+1)] = []
|
||||
e.add_field(
|
||||
name=f"{emotes[i]} __{response.capitalize()}__",
|
||||
value="**0** vote"
|
||||
)
|
||||
e.set_footer(text=f"ID: {poll_row.id}")
|
||||
|
||||
poll_row.message_id = stmt.id
|
||||
poll_row.poll = e.to_dict()
|
||||
poll_row.is_anonymous = anonymous
|
||||
poll_row.responses = responses_row
|
||||
|
||||
self.bot.engine.commit()
|
||||
|
||||
await stmt.edit(content='', embed=e)
|
||||
for emote in range(len(responses)):
|
||||
await stmt.add_reaction(emotes[emote])
|
||||
|
||||
@commands.group(name='sondage', aliases=['poll'])
|
||||
async def _poll(self, ctx):
|
||||
"""
|
||||
todo: refer to readme.md
|
||||
"""
|
||||
async def _poll(self, ctx: commands.Context):
|
||||
if ctx.invoked_subcommand is None:
|
||||
...
|
||||
|
||||
@_poll.group(name='create', aliases=['new', 'nouveau'])
|
||||
async def _poll_create(self, ctx: commands.Context, *, poll: str):
|
||||
is_anonymous = '--anonyme' in poll
|
||||
poll = poll.replace('--anonyme', '')
|
||||
|
||||
await self.make_poll(ctx, poll, anonymous=is_anonymous)
|
||||
|
||||
|
||||
def setup(bot: TuxBot):
|
||||
bot.add_cog(Poll(bot))
|
||||
bot.add_cog(Polls(bot))
|
||||
|
|
|
@ -5,6 +5,7 @@ import discord
|
|||
from discord.ext import commands
|
||||
from bot import TuxBot
|
||||
import socket
|
||||
from socket import AF_INET6
|
||||
|
||||
from .utils.lang import Texts
|
||||
|
||||
|
@ -23,16 +24,18 @@ class Utility(commands.Cog):
|
|||
|
||||
await ctx.trigger_typing()
|
||||
|
||||
try:
|
||||
if ip_type in ('v6', 'ipv6'):
|
||||
try:
|
||||
ip = socket.getaddrinfo(addr, None, socket.AF_INET6)[1][4][0]
|
||||
ip = socket.getaddrinfo(addr, None, AF_INET6)[1][4][0]
|
||||
except socket.gaierror:
|
||||
return await ctx.send(
|
||||
Texts('utility', ctx).get('ipv6 not available'))
|
||||
else:
|
||||
ip = socket.gethostbyname(addr)
|
||||
|
||||
async with self.bot.session.get(f"http://ip-api.com/json/{ip}") as s:
|
||||
async with self.bot.session.get(f"http://ip-api.com/json/{ip}") \
|
||||
as s:
|
||||
response: dict = await s.json()
|
||||
|
||||
if response.get('status') == 'success':
|
||||
|
@ -71,6 +74,11 @@ class Utility(commands.Cog):
|
|||
content=f"{Texts('utility', ctx).get('info not available')}"
|
||||
f"``{response.get('query')}``")
|
||||
|
||||
except Exception:
|
||||
await ctx.send(
|
||||
f"{Texts('utility', ctx).get('Cannot connect to host')} {addr}"
|
||||
)
|
||||
|
||||
"""---------------------------------------------------------------------"""
|
||||
|
||||
@commands.command(name='getheaders')
|
||||
|
@ -78,9 +86,10 @@ class Utility(commands.Cog):
|
|||
if (addr.startswith('http') or addr.startswith('ftp')) is not True:
|
||||
addr = f"http://{addr}"
|
||||
|
||||
await ctx.trigger_typing()
|
||||
|
||||
try:
|
||||
async with self.bot.session.get(addr) as s:
|
||||
await ctx.trigger_typing()
|
||||
e = discord.Embed(
|
||||
title=f"{Texts('utility', ctx).get('Headers of')} {addr}",
|
||||
color=0xd75858
|
||||
|
@ -95,9 +104,10 @@ class Utility(commands.Cog):
|
|||
e.add_field(name=key, value=value, inline=True)
|
||||
await ctx.send(embed=e)
|
||||
|
||||
except aiohttp.client_exceptions.ClientConnectorError:
|
||||
await ctx.send(f"{Texts('utility', ctx).get('Cannot connect to host')} "
|
||||
f"{addr}")
|
||||
except aiohttp.client_exceptions.ClientError:
|
||||
await ctx.send(
|
||||
f"{Texts('utility', ctx).get('Cannot connect to host')} {addr}"
|
||||
)
|
||||
|
||||
"""---------------------------------------------------------------------"""
|
||||
|
||||
|
|
10
cogs/utils/emotes.py
Normal file
10
cogs/utils/emotes.py
Normal file
|
@ -0,0 +1,10 @@
|
|||
emotes = ['1⃣', '2⃣', '3⃣', '4⃣', '5⃣', '6⃣', '7⃣', '8⃣', '9⃣', '🔟', '0⃣',
|
||||
'🇦', '🇧', '🇨', '🇩', '🇪', '🇫', '🇬', '🇭', '🇮']
|
||||
|
||||
|
||||
def get(count):
|
||||
return emotes[:count]
|
||||
|
||||
|
||||
def get_index(emote):
|
||||
return emotes.index(emote)
|
3
cogs/utils/models/__init__.py
Normal file
3
cogs/utils/models/__init__.py
Normal file
|
@ -0,0 +1,3 @@
|
|||
from .lang import Lang
|
||||
from .warn import Warn
|
||||
from .poll import Poll
|
|
@ -4,7 +4,7 @@ Base = declarative_base()
|
|||
|
||||
|
||||
class Lang(Base):
|
||||
__tablename__ = 'lang'
|
||||
__tablename__ = 'langs'
|
||||
|
||||
key = Column(String, primary_key=True)
|
||||
value = Column(String)
|
||||
|
|
20
cogs/utils/models/poll.py
Normal file
20
cogs/utils/models/poll.py
Normal file
|
@ -0,0 +1,20 @@
|
|||
from sqlalchemy.ext.declarative import declarative_base
|
||||
from sqlalchemy import Column, Integer, Boolean, BigInteger, JSON
|
||||
|
||||
Base = declarative_base()
|
||||
|
||||
|
||||
class Poll(Base):
|
||||
__tablename__ = 'polls'
|
||||
|
||||
id = Column(Integer, primary_key=True)
|
||||
message_id = Column(BigInteger)
|
||||
poll = Column(JSON)
|
||||
is_anonymous = Column(Boolean)
|
||||
responses = Column(JSON, nullable=True)
|
||||
|
||||
def __repr__(self):
|
||||
return "<Poll(id='%s', message_id='%s', poll='%s', " \
|
||||
"is_anonymous='%s', responses='%s')>" % \
|
||||
(self.id, self.message_id, self.poll,
|
||||
self.is_anonymous, self.responses)
|
|
@ -2,6 +2,7 @@ import datetime
|
|||
|
||||
from sqlalchemy.ext.declarative import declarative_base
|
||||
from sqlalchemy import Column, Integer, String, BIGINT, TIMESTAMP
|
||||
|
||||
Base = declarative_base()
|
||||
|
||||
|
||||
|
|
BIN
extras/locales/en/LC_MESSAGES/poll.mo
Normal file
BIN
extras/locales/en/LC_MESSAGES/poll.mo
Normal file
Binary file not shown.
20
extras/locales/en/LC_MESSAGES/poll.po
Normal file
20
extras/locales/en/LC_MESSAGES/poll.po
Normal file
|
@ -0,0 +1,20 @@
|
|||
# SOME DESCRIPTIVE TITLE.
|
||||
# Copyright (C) YEAR ORGANIZATION
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"POT-Creation-Date: 2019-09-08 19:04+0200\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"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Generated-By: pygettext.py 1.5\n"
|
||||
|
||||
|
||||
#: launcher.py:51
|
||||
msgid "**Preparation...**"
|
||||
msgstr ""
|
BIN
extras/locales/fr/LC_MESSAGES/poll.mo
Normal file
BIN
extras/locales/fr/LC_MESSAGES/poll.mo
Normal file
Binary file not shown.
20
extras/locales/fr/LC_MESSAGES/poll.po
Normal file
20
extras/locales/fr/LC_MESSAGES/poll.po
Normal file
|
@ -0,0 +1,20 @@
|
|||
# SOME DESCRIPTIVE TITLE.
|
||||
# Copyright (C) YEAR ORGANIZATION
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"POT-Creation-Date: 2019-09-08 19:04+0200\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"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Generated-By: pygettext.py 1.5\n"
|
||||
|
||||
|
||||
#: launcher.py:51
|
||||
msgid "**Preparation...**"
|
||||
msgstr "**Préparation...**"
|
|
@ -1,5 +1,8 @@
|
|||
{
|
||||
"280805240977227776": [
|
||||
"b."
|
||||
"b!"
|
||||
],
|
||||
"303633056944881686": [
|
||||
"b! "
|
||||
]
|
||||
}
|
|
@ -8,3 +8,4 @@ sqlalchemy
|
|||
gitpython
|
||||
requests
|
||||
psutil
|
||||
bcrypt
|
Loading…
Reference in a new issue