fix(security): fix high security breach
7
.gitignore
vendored
|
@ -1,9 +1,10 @@
|
|||
#Don't track user data
|
||||
data/users/
|
||||
|
||||
#Python
|
||||
__pycache__/
|
||||
*.pyc
|
||||
.env
|
||||
config.py
|
||||
.DS_Store
|
||||
private.py
|
||||
|
||||
#jetbrains
|
||||
.idea/
|
17
bot.py
|
@ -24,10 +24,8 @@ l_extensions = (
|
|||
'cogs.atc',
|
||||
'cogs.basics',
|
||||
'cogs.ci',
|
||||
'cogs.cog_manager',
|
||||
'cogs.filter_messages',
|
||||
'cogs.funs',
|
||||
'cogs.passport',
|
||||
'cogs.role',
|
||||
'cogs.search',
|
||||
'cogs.send_logs',
|
||||
|
@ -46,8 +44,7 @@ class TuxBot(commands.Bot):
|
|||
super().__init__(command_prefix=self.config.prefix[0],
|
||||
description=self.config.description,
|
||||
pm_help=None,
|
||||
help_command = None
|
||||
)
|
||||
help_command=None)
|
||||
|
||||
self.client_id = self.config.client_id
|
||||
self.session = aiohttp.ClientSession(loop=self.loop)
|
||||
|
@ -67,10 +64,10 @@ class TuxBot(commands.Bot):
|
|||
|
||||
async def on_command_error(self, ctx, error):
|
||||
if isinstance(error, commands.NoPrivateMessage):
|
||||
await ctx.author.send('Cette commande ne peut pas être utilisée '
|
||||
'en message privé.')
|
||||
await ctx.author.send('Cette commande ne peut pas être utilisee '
|
||||
'en message privee.')
|
||||
elif isinstance(error, commands.DisabledCommand):
|
||||
await ctx.author.send('Desolé mais cette commande est desactivée, '
|
||||
await ctx.author.send('Desoler mais cette commande est desactive, '
|
||||
'elle ne peut donc pas être utilisée.')
|
||||
elif isinstance(error, commands.CommandInvokeError):
|
||||
print(f'In {ctx.command.qualified_name}:', file=sys.stderr)
|
||||
|
@ -79,13 +76,13 @@ class TuxBot(commands.Bot):
|
|||
file=sys.stderr)
|
||||
|
||||
async def on_ready(self):
|
||||
log_channel_id = self.get_channel(int(self.config.log_channel_id))
|
||||
log_channel_id = await self.fetch_channel(self.config.log_channel_id)
|
||||
|
||||
print('\n\n---------------------')
|
||||
print('CONNECTÉ :')
|
||||
print(f'Nom d\'utilisateur: {self.user} {colors.text_style.DIM}'
|
||||
f'(ID: {self.user.id}){colors.ENDC}')
|
||||
print(f'Salon de journalisation: {log_channel_id} {colors.text_style.DIM}'
|
||||
print(f'Channel de log: {log_channel_id} {colors.text_style.DIM}'
|
||||
f'(ID: {log_channel_id.id}){colors.ENDC}')
|
||||
print(f'Prefix: {self.config.prefix[0]}')
|
||||
print('Merci d\'utiliser TuxBot')
|
||||
|
@ -93,7 +90,7 @@ class TuxBot(commands.Bot):
|
|||
|
||||
await self.change_presence(status=discord.Status.dnd,
|
||||
activity=discord.Game(
|
||||
name=self.config.game),
|
||||
name=self.config.game)
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
|
|
214
cogs/admin.py
|
@ -1,16 +1,10 @@
|
|||
import aiohttp
|
||||
from discord.ext import commands
|
||||
import discord
|
||||
import asyncio
|
||||
from .utils import checks
|
||||
|
||||
from .utils.checks import get_user
|
||||
|
||||
import traceback
|
||||
import textwrap
|
||||
from contextlib import redirect_stdout
|
||||
import inspect
|
||||
import io
|
||||
|
||||
|
||||
class Admin(commands.Cog):
|
||||
"""Commandes secrètes d'administration."""
|
||||
|
@ -20,20 +14,37 @@ class Admin(commands.Cog):
|
|||
self._last_result = None
|
||||
self.sessions = set()
|
||||
|
||||
@staticmethod
|
||||
def cleanup_code(content):
|
||||
if content.startswith('```') and content.endswith('```'):
|
||||
return '\n'.join(content.split('\n')[1:-1])
|
||||
return content.strip('` \n')
|
||||
|
||||
"""---------------------------------------------------------------------"""
|
||||
|
||||
@checks.has_permissions(administrator=True)
|
||||
@commands.command(pass_context=True)
|
||||
async def ban(self, ctx, user, *, reason=""):
|
||||
@commands.command(name="upload", pass_context=True)
|
||||
async def _upload(self, ctx, *, url=""):
|
||||
if len(ctx.message.attachments) >= 1:
|
||||
file = ctx.message.attachments[0].url
|
||||
elif url != "":
|
||||
file = url
|
||||
else:
|
||||
em = discord.Embed(title='Une erreur est survenue',
|
||||
description="Fichier introuvable.",
|
||||
colour=0xDC3546)
|
||||
await ctx.send(embed=em)
|
||||
return
|
||||
|
||||
async with aiohttp.ClientSession() as session:
|
||||
async with session.get(file) as r:
|
||||
image = await r.content.read()
|
||||
|
||||
with open(f"data/tmp/{str(ctx.author.id)}.png", 'wb') as f:
|
||||
f.write(image)
|
||||
f.close()
|
||||
await ctx.send(file=discord.File(f"data/tmp/{str(ctx.author.id)}.png"))
|
||||
|
||||
@checks.has_permissions(administrator=True)
|
||||
@commands.command(name="ban", pass_context=True)
|
||||
async def _ban(self, ctx, user, *, reason=""):
|
||||
"""Ban user"""
|
||||
user = get_user(ctx.message, user)
|
||||
if user:
|
||||
if user and str(user.id) not in self.bot.config.unkickable_id:
|
||||
try:
|
||||
await user.ban(reason=reason)
|
||||
return_msg = f"`{user.mention}` a été banni\n"
|
||||
|
@ -50,11 +61,11 @@ class Admin(commands.Cog):
|
|||
"""---------------------------------------------------------------------"""
|
||||
|
||||
@checks.has_permissions(administrator=True)
|
||||
@commands.command(pass_context=True)
|
||||
async def kick(self, ctx, user, *, reason=""):
|
||||
@commands.command(name="kick", pass_context=True)
|
||||
async def _kick(self, ctx, user, *, reason=""):
|
||||
"""Kick a user"""
|
||||
user = get_user(ctx.message, user)
|
||||
if user:
|
||||
if user and str(user.id) not in self.bot.config.unkickable_id:
|
||||
try:
|
||||
await user.kick(reason=reason)
|
||||
return_msg = f"`{user.mention}` a été kické\n"
|
||||
|
@ -155,7 +166,7 @@ class Admin(commands.Cog):
|
|||
|
||||
@checks.has_permissions(administrator=True)
|
||||
@commands.command(name='editsay', pass_context=True)
|
||||
async def _editsay(self, ctx, id: int, *, new_content: str):
|
||||
async def _editsay(self, ctx, message_id: int, *, new_content: str):
|
||||
"""Edit a bot's message"""
|
||||
try:
|
||||
try:
|
||||
|
@ -163,10 +174,10 @@ class Admin(commands.Cog):
|
|||
except Exception:
|
||||
print(f"Impossible de supprimer le message "
|
||||
f"\"{str(ctx.message.content)}\"")
|
||||
toedit = await ctx.channel.get_message(id)
|
||||
toedit = await ctx.channel.get_message(message_id)
|
||||
except discord.errors.NotFound:
|
||||
await ctx.send(f"Impossible de trouver le message avec l'id "
|
||||
f"`{id}` sur ce salon")
|
||||
f"`{message_id}` sur ce salon")
|
||||
return
|
||||
try:
|
||||
await toedit.edit(content=str(new_content))
|
||||
|
@ -177,7 +188,7 @@ class Admin(commands.Cog):
|
|||
|
||||
@checks.has_permissions(administrator=True)
|
||||
@commands.command(name='addreaction', pass_context=True)
|
||||
async def _addreaction(self, ctx, id: int, reaction: str):
|
||||
async def _addreaction(self, ctx, message_id: int, reaction: str):
|
||||
"""Add reactions to a message"""
|
||||
try:
|
||||
try:
|
||||
|
@ -185,10 +196,10 @@ class Admin(commands.Cog):
|
|||
except Exception:
|
||||
print(f"Impossible de supprimer le message "
|
||||
f"\"{str(ctx.message.content)}\"")
|
||||
toadd = await ctx.channel.get_message(id)
|
||||
toadd = await ctx.channel.get_message(message_id)
|
||||
except discord.errors.NotFound:
|
||||
await ctx.send(f"Impossible de trouver le message avec l'id "
|
||||
f"`{id}` sur ce salon")
|
||||
f"`{message_id}` sur ce salon")
|
||||
return
|
||||
try:
|
||||
await toadd.add_reaction(reaction)
|
||||
|
@ -199,7 +210,7 @@ class Admin(commands.Cog):
|
|||
|
||||
@checks.has_permissions(administrator=True)
|
||||
@commands.command(name='delete', pass_context=True)
|
||||
async def _delete(self, ctx, id: int):
|
||||
async def _delete(self, ctx, message_id: int):
|
||||
"""Delete message in current channel"""
|
||||
try:
|
||||
try:
|
||||
|
@ -207,10 +218,10 @@ class Admin(commands.Cog):
|
|||
except Exception:
|
||||
print(f"Impossible de supprimer le message "
|
||||
f"\"{str(ctx.message.content)}\"")
|
||||
todelete = await ctx.channel.get_message(id)
|
||||
todelete = await ctx.channel.get_message(message_id)
|
||||
except discord.errors.NotFound:
|
||||
await ctx.send(f"Impossible de trouver le message avec l'id "
|
||||
f"`{id}` sur ce salon")
|
||||
f"`{message_id}` sur ce salon")
|
||||
return
|
||||
try:
|
||||
await todelete.delete()
|
||||
|
@ -221,7 +232,7 @@ class Admin(commands.Cog):
|
|||
|
||||
@checks.has_permissions(administrator=True)
|
||||
@commands.command(name='deletefrom', pass_context=True)
|
||||
async def _deletefrom(self, ctx, chan_id: int, *, message_id: str):
|
||||
async def _deletefrom(self, ctx, chan_id: int, *, message_id: int):
|
||||
"""Delete message in <chan_id> channel"""
|
||||
try:
|
||||
chan = self.bot.get_channel(chan_id)
|
||||
|
@ -391,151 +402,6 @@ class Admin(commands.Cog):
|
|||
|
||||
await ctx.send(embed=embed)
|
||||
|
||||
"""---------------------------------------------------------------------"""
|
||||
|
||||
@checks.has_permissions(administrator=True)
|
||||
@commands.command(pass_context=True, hidden=True)
|
||||
async def repl(self, ctx):
|
||||
"""Launches an interactive REPL session."""
|
||||
variables = {
|
||||
'ctx': ctx,
|
||||
'bot': self.bot,
|
||||
'message': ctx.message,
|
||||
'guild': ctx.guild,
|
||||
'channel': ctx.channel,
|
||||
'author': ctx.author,
|
||||
'_': None,
|
||||
}
|
||||
|
||||
if ctx.channel.id in self.sessions:
|
||||
await ctx.send('Already running a REPL session in this channel.'
|
||||
' Exit it with `quit`.')
|
||||
return
|
||||
|
||||
self.sessions.add(ctx.channel.id)
|
||||
await ctx.send(
|
||||
'Enter code to execute or evaluate. `exit()` or `quit` to exit.')
|
||||
|
||||
def check(m):
|
||||
return m.author.id == ctx.author.id and \
|
||||
m.channel.id == ctx.channel.id and \
|
||||
m.content.startswith('`')
|
||||
|
||||
while True:
|
||||
try:
|
||||
response = await self.bot.wait_for('message', check=check,
|
||||
timeout=10.0 * 60.0)
|
||||
except asyncio.TimeoutError:
|
||||
await ctx.send('Exiting REPL session.')
|
||||
self.sessions.remove(ctx.channel.id)
|
||||
break
|
||||
|
||||
cleaned = self.cleanup_code(response.content)
|
||||
|
||||
if cleaned in ('quit', 'exit', 'exit()'):
|
||||
await ctx.send('Exiting.')
|
||||
self.sessions.remove(ctx.channel.id)
|
||||
return
|
||||
|
||||
executor = exec
|
||||
if cleaned.count('\n') == 0:
|
||||
# single statement, potentially 'eval'
|
||||
try:
|
||||
code = compile(cleaned, '<repl session>', 'eval')
|
||||
except SyntaxError:
|
||||
pass
|
||||
else:
|
||||
executor = eval
|
||||
|
||||
if executor is exec:
|
||||
try:
|
||||
code = compile(cleaned, '<repl session>', 'exec')
|
||||
except SyntaxError as e:
|
||||
await ctx.send(self.get_syntax_error(e))
|
||||
continue
|
||||
|
||||
variables['message'] = response
|
||||
|
||||
fmt = None
|
||||
stdout = io.StringIO()
|
||||
|
||||
try:
|
||||
with redirect_stdout(stdout):
|
||||
result = executor(code, variables)
|
||||
if inspect.isawaitable(result):
|
||||
result = await result
|
||||
except Exception as e:
|
||||
value = stdout.getvalue()
|
||||
fmt = f'```py\n{value}{traceback.format_exc()}\n```'
|
||||
else:
|
||||
value = stdout.getvalue()
|
||||
if result is not None:
|
||||
fmt = f'```py\n{value}{result}\n```'
|
||||
variables['_'] = result
|
||||
elif value:
|
||||
fmt = f'```py\n{value}\n```'
|
||||
|
||||
try:
|
||||
if fmt is not None:
|
||||
if len(fmt) > 2000:
|
||||
await ctx.send('Content too big to be printed.')
|
||||
else:
|
||||
await ctx.send(fmt)
|
||||
except discord.Forbidden:
|
||||
pass
|
||||
except discord.HTTPException as e:
|
||||
await ctx.send(f'Unexpected error: `{e}`')
|
||||
|
||||
"""---------------------------------------------------------------------"""
|
||||
|
||||
@checks.has_permissions(administrator=True)
|
||||
@commands.command(pass_context=True, hidden=True, name='eval')
|
||||
async def _eval(self, ctx, *, body: str):
|
||||
"""Evaluates a code"""
|
||||
|
||||
env = {
|
||||
'bot': self.bot,
|
||||
'ctx': ctx,
|
||||
'channel': ctx.channel,
|
||||
'author': ctx.author,
|
||||
'guild': ctx.guild,
|
||||
'message': ctx.message,
|
||||
'_': self._last_result
|
||||
}
|
||||
|
||||
env.update(globals())
|
||||
|
||||
body = self.cleanup_code(body)
|
||||
stdout = io.StringIO()
|
||||
|
||||
to_compile = f'async def func():\n{textwrap.indent(body, " ")}'
|
||||
|
||||
try:
|
||||
exec(to_compile, env)
|
||||
except Exception as e:
|
||||
return await ctx.send(f'```py\n{e.__class__.__name__}: {e}\n```')
|
||||
|
||||
func = env['func']
|
||||
try:
|
||||
with redirect_stdout(stdout):
|
||||
ret = await func()
|
||||
except Exception:
|
||||
value = stdout.getvalue()
|
||||
await ctx.send(f'```py\n{value}{traceback.format_exc()}\n```')
|
||||
else:
|
||||
value = stdout.getvalue()
|
||||
try:
|
||||
await ctx.message.add_reaction('\u2705')
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
if ret is None:
|
||||
if value:
|
||||
await ctx.send(f'```py\n{value}\n```')
|
||||
else:
|
||||
self._last_result = ret
|
||||
await ctx.send(f'```py\n{value}{ret}\n```')
|
||||
|
||||
|
||||
def setup(bot):
|
||||
bot.add_cog(Admin(bot))
|
||||
|
|
|
@ -61,10 +61,10 @@ class Basics(commands.Cog):
|
|||
async def help(self, ctx):
|
||||
"""Affiches l'aide du bot"""
|
||||
text = open('texts/help.md').read()
|
||||
text = text.split("[split]")
|
||||
for text_result in text:
|
||||
em = discord.Embed(title='Commandes de TuxBot', description=text_result,colour=0x89C4F9)
|
||||
await ctx.send(embed=em)
|
||||
em = discord.Embed(title='Commandes de TuxBot', description=text,
|
||||
colour=0x89C4F9)
|
||||
await ctx.send(embed=em)
|
||||
|
||||
|
||||
def setup(bot):
|
||||
bot.add_cog(Basics(bot))
|
||||
|
|
|
@ -1,116 +0,0 @@
|
|||
from discord.ext import commands
|
||||
import discord
|
||||
from .utils import checks
|
||||
from .utils.paginator import HelpPaginator
|
||||
|
||||
|
||||
class CogManager(commands.Cog):
|
||||
"""Gestionnaire des cogs"""
|
||||
|
||||
def __init__(self, bot):
|
||||
self.bot = bot
|
||||
self.help = discord.Embed(title='Tuxbot - Commandes cogs', description="Tuxbot - Commandes cogs\n"
|
||||
"-> .cogs <load/unload/reload/info> *{cog}* : <load/unload/reload/info> *{cog}*\n"
|
||||
"-> .cogs <list> : donne la liste de tous les cogs\n"
|
||||
"-> .cogs <null/!(load/unload/reload)>: affiche cette aide", colour=0x89C4F9)
|
||||
|
||||
"""---------------------------------------------------------------------"""
|
||||
|
||||
@checks.has_permissions(administrator=True)
|
||||
@commands.group(name="cogs", no_pm=True, pass_context=True,
|
||||
case_insensitive=True)
|
||||
async def _cogs(self, ctx):
|
||||
"""show help about 'cogs' command"""
|
||||
|
||||
if ctx.invoked_subcommand is None:
|
||||
await ctx.send(embed=self.help)
|
||||
|
||||
"""---------------------------------------------------------------------"""
|
||||
|
||||
@_cogs.command(name="load", pass_context=True)
|
||||
async def cogs_load(self, ctx, cog: str = ""):
|
||||
"""load a cog"""
|
||||
if cog != "":
|
||||
try:
|
||||
self.bot.load_extension(cog)
|
||||
|
||||
await ctx.send('\N{OK HAND SIGN}')
|
||||
print("cog : " + str(cog) + " chargé")
|
||||
except Exception as e:
|
||||
await ctx.send('\N{PISTOL}')
|
||||
await ctx.send(f'{type(e).__name__}: {e}')
|
||||
else:
|
||||
await ctx.send(embed=self.help)
|
||||
|
||||
"""---------------------------------------------------------------------"""
|
||||
|
||||
@_cogs.command(name="unload", pass_context=True)
|
||||
async def cogs_unload(self, ctx, cog: str = ""):
|
||||
"""unload a cog"""
|
||||
if cog != "":
|
||||
try:
|
||||
self.bot.unload_extension(cog)
|
||||
|
||||
await ctx.send('\N{OK HAND SIGN}')
|
||||
print("cog : " + str(cog) + " déchargé")
|
||||
except Exception as e:
|
||||
await ctx.send('\N{PISTOL}')
|
||||
await ctx.send(f'{type(e).__name__}: {e}')
|
||||
else:
|
||||
await ctx.send(embed=self.help)
|
||||
|
||||
"""---------------------------------------------------------------------"""
|
||||
|
||||
@_cogs.command(name="reload", pass_context=True)
|
||||
async def cogs_reload(self, ctx, cog: str = ""):
|
||||
"""reload a cog"""
|
||||
if cog != "":
|
||||
try:
|
||||
self.bot.unload_extension(cog)
|
||||
self.bot.load_extension(cog)
|
||||
|
||||
await ctx.send('\N{OK HAND SIGN}')
|
||||
print("cog : " + str(cog) + " rechargé")
|
||||
except Exception as e:
|
||||
await ctx.send('\N{PISTOL}')
|
||||
await ctx.send(f'{type(e).__name__}: {e}')
|
||||
else:
|
||||
await ctx.send(embed=self.help)
|
||||
|
||||
"""---------------------------------------------------------------------"""
|
||||
|
||||
@_cogs.command(name="info", pass_context=True)
|
||||
async def cogs_info(self, ctx, cog: str = ""):
|
||||
"""show info about a cog"""
|
||||
if cog != "":
|
||||
try:
|
||||
entity = self.bot.get_cog(cog)
|
||||
|
||||
if entity is None:
|
||||
clean = cog.replace('@', '@\u200b')
|
||||
await ctx.send(f'Command or category "{clean}" not found.')
|
||||
else:
|
||||
p = await HelpPaginator.from_cog(ctx, entity)
|
||||
await p.paginate()
|
||||
|
||||
except Exception as e:
|
||||
await ctx.send('\N{PISTOL}')
|
||||
await ctx.send(f'{type(e).__name__}: {e}')
|
||||
else:
|
||||
await ctx.send(embed=self.help)
|
||||
|
||||
"""---------------------------------------------------------------------"""
|
||||
|
||||
@_cogs.command(name="list", pass_context=True)
|
||||
async def cogs_list(self, ctx):
|
||||
"""list all cogs"""
|
||||
|
||||
cogs = ""
|
||||
for cog in self.bot.cogs:
|
||||
cogs += f"{cog} *`({self.bot.cogs[cog].__module__})`*:" \
|
||||
f" {len(self.bot.get_cog_commands(cog))} commandes\n"
|
||||
await ctx.send(cogs)
|
||||
|
||||
|
||||
def setup(bot):
|
||||
bot.add_cog(CogManager(bot))
|
224
cogs/passport.py
|
@ -1,224 +0,0 @@
|
|||
import aiohttp
|
||||
import datetime
|
||||
import discord
|
||||
import imghdr
|
||||
import os
|
||||
import shutil
|
||||
from PIL import Image
|
||||
from PIL import ImageOps
|
||||
from discord.ext import commands
|
||||
|
||||
from .utils import db
|
||||
from .utils.checks import get_user
|
||||
from .utils.passport_generator import generate_passport
|
||||
|
||||
|
||||
class Passport(commands.Cog):
|
||||
"""Commandes des passeports ."""
|
||||
|
||||
def __init__(self, bot):
|
||||
self.bot = bot
|
||||
|
||||
self.conn = db.connect_to_db(self)
|
||||
self.cursor = self.conn.cursor()
|
||||
|
||||
self.cursor.execute("""SHOW TABLES LIKE 'passport'""")
|
||||
result = self.cursor.fetchone()
|
||||
|
||||
if not result:
|
||||
# Creation table Passport si premiere fois
|
||||
sql = "CREATE TABLE passport ( `id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY, userid TEXT null, os TEXT null, config TEXT null, languages TEXT null, pays TEXT null, passportdate TEXT null, theme CHAR(5) DEFAULT 'dark');"
|
||||
self.cursor.execute(sql)
|
||||
|
||||
@commands.group(pass_context=True)
|
||||
async def passeport(self, ctx):
|
||||
"""Passeport"""
|
||||
|
||||
if ctx.invoked_subcommand is None:
|
||||
text = open('texts/passport-info.md').read()
|
||||
em = discord.Embed(title='Commandes de carte de passeport de TuxBot', description=text, colour=0x89C4F9)
|
||||
await ctx.send(embed=em)
|
||||
|
||||
@passeport.command(pass_context=True)
|
||||
async def show(self, ctx, user: str = None):
|
||||
self.conn = db.connect_to_db(self)
|
||||
self.cursor = self.conn.cursor()
|
||||
|
||||
if user == None:
|
||||
user = get_user(ctx.message, ctx.message.author.name)
|
||||
else:
|
||||
user = get_user(ctx.message, user)
|
||||
|
||||
wait_message = await ctx.send(f"Je vais chercher le passeport de {user.name} dans les archives, je vous prie de bien vouloir patienter...")
|
||||
|
||||
card = await generate_passport(self, user)
|
||||
s = 'data/users/cards/{0}.png'.format(user.id)
|
||||
|
||||
card.save(s, 'png')
|
||||
|
||||
with open('data/users/cards/{0}.png'.format(user.id), 'rb') as g:
|
||||
await ctx.message.channel.send(file=discord.File(g))
|
||||
await wait_message.delete()
|
||||
|
||||
@passeport.command(name="config", pass_context=True)
|
||||
async def passeport_config(self, ctx):
|
||||
self.conn = db.connect_to_db(self)
|
||||
self.cursor = self.conn.cursor()
|
||||
|
||||
await ctx.send('Un message privé vous a été envoyé pour configurer votre passeport.')
|
||||
|
||||
questions = ["Système(s) d'exploitation :", "Configuration Système :", "Langages de programmation préférés :", "Pays :"]
|
||||
answers = {}
|
||||
|
||||
user_dm = await ctx.author.create_dm()
|
||||
|
||||
try:
|
||||
await user_dm.send("Salut ! Je vais vous posez quelques questions afin de configurer votre passeport, si vous ne voulez pas répondre à une question, envoyez `skip` pour passer à la question suivante.")
|
||||
except discord.HTTPException:
|
||||
await ctx.send(f"{str(ctx.message.author.mention)}> il m'est impossible de vous envoyer les messages nécessaire a la configuration de votre passeport :sob:")
|
||||
return
|
||||
|
||||
for x, question in enumerate(questions):
|
||||
await user_dm.send(question)
|
||||
|
||||
def check(m):
|
||||
return m.channel.id == user_dm.id and m.author.id == user_dm.recipient.id
|
||||
|
||||
answer = await self.bot.wait_for('message', check=check)
|
||||
if answer.content.lower() == 'skip':
|
||||
answers[x] = 'n/a'
|
||||
else:
|
||||
answers[x] = answer.content
|
||||
try:
|
||||
self.cursor.execute("""SELECT id, userid FROM passport WHERE userid = %s""", str(user_dm.recipient.id))
|
||||
result = self.cursor.fetchone()
|
||||
|
||||
if result:
|
||||
self.cursor.execute("""UPDATE passport SET os = %s, config = %s, languages = %s, pays = %s WHERE userid = %s""", (str(answers[0]), str(answers[1]), str(answers[2]), str(answers[3]), str(ctx.message.author.id)))
|
||||
self.conn.commit()
|
||||
else:
|
||||
now = datetime.datetime.now()
|
||||
|
||||
self.cursor.execute("""INSERT INTO passport(userid, os, config, languages, pays, passportdate, theme) VALUES(%s, %s, %s, %s, %s, %s, %s)""", (str(ctx.message.author.id), str(answers[0]), str(answers[1]), str(answers[2]), str(answers[3]), now, "dark"))
|
||||
self.conn.commit()
|
||||
await user_dm.send('Configuration de votre passeport terminée avec succès, vous pouvez désormais la voir en faisant `.passeport show`.')
|
||||
except Exception as e:
|
||||
await user_dm.send(f':sob: Une erreur est survenue : \n {type(e).__name__}: {e}')
|
||||
|
||||
@passeport.command(name="background", pass_context=True)
|
||||
async def passeport_background(self, ctx, *, url=""):
|
||||
try:
|
||||
background = ctx.message.attachments[0].url
|
||||
except:
|
||||
if url != "":
|
||||
background = url
|
||||
else:
|
||||
em = discord.Embed(title='Une erreur est survenue', description="Image ou URL introuvable.", colour=0xDC3546)
|
||||
await ctx.send(embed=em)
|
||||
return
|
||||
|
||||
user = ctx.message.author
|
||||
try:
|
||||
async with aiohttp.ClientSession() as session:
|
||||
async with session.get(background) as r:
|
||||
image = await r.content.read()
|
||||
except:
|
||||
em = discord.Embed(title='Une erreur est survenue', description="Image ou URL introuvable.", colour=0xDC3546)
|
||||
await ctx.send(embed=em)
|
||||
return
|
||||
|
||||
with open(f"data/users/backgrounds/{str(user.id)}.png",'wb') as f:
|
||||
f.write(image)
|
||||
|
||||
isImage = imghdr.what(f"data/users/backgrounds/{str(user.id)}.png")
|
||||
|
||||
if isImage == 'png' or isImage == 'jpeg' or isImage == 'jpg' or isImage == 'gif':
|
||||
f.close()
|
||||
em = discord.Embed(title='Configuration terminée', description="Fond d'écran enregistré et configuré avec succes", colour=0x28a745)
|
||||
await ctx.send(embed=em)
|
||||
else:
|
||||
f.close()
|
||||
os.remove(f"data/users/backgrounds/{str(user.id)}.png")
|
||||
em = discord.Embed(title='Une erreur est survenue', description="Est-ce bien une image que vous avez envoyé ? :thinking:", colour=0xDC3546)
|
||||
await ctx.send(embed=em)
|
||||
|
||||
@passeport.command(name="theme", aliases=["thème"], pass_context=True)
|
||||
async def passeport_theme(self, ctx, theme: str = ""):
|
||||
self.conn = db.connect_to_db(self)
|
||||
self.cursor = self.conn.cursor()
|
||||
|
||||
possible_theme = ["dark", "light", "preview"]
|
||||
|
||||
if theme.lower() in possible_theme:
|
||||
if theme.lower() == "dark":
|
||||
self.cursor.execute("""UPDATE passport SET theme = %s WHERE userid = %s""", ("dark", str(ctx.message.author.id)))
|
||||
self.conn.commit()
|
||||
|
||||
em = discord.Embed(title='Configuration terminée', description="Thème enregistré avec succes", colour=0x28a745)
|
||||
await ctx.send(embed=em)
|
||||
elif theme.lower() == "light":
|
||||
self.cursor.execute("""UPDATE passport SET theme = %s WHERE userid = %s""", ("light", str(ctx.message.author.id)))
|
||||
self.conn.commit()
|
||||
|
||||
em = discord.Embed(title='Configuration terminée', description="Thème enregistré avec succes", colour=0x28a745)
|
||||
await ctx.send(embed=em)
|
||||
else:
|
||||
wait_message = await ctx.send(f"Laissez moi juste le temps de superposer les 2 passeports, je vous prie de bien vouloir patienter...")
|
||||
cardbg = Image.new('RGBA', (1600, 500), (0, 0, 0, 255))
|
||||
|
||||
card_dark = await generate_passport(self, ctx.author, "dark")
|
||||
card_dark.save(f'data/tmp/{ctx.author.id}_dark.png', 'png')
|
||||
|
||||
card_light = await generate_passport(self, ctx.author, "light")
|
||||
card_light.save(f'data/tmp/{ctx.author.id}_light.png', 'png')
|
||||
|
||||
saved_card_dark = Image.open(f'data/tmp/{ctx.author.id}_dark.png')
|
||||
saved_card_light = Image.open(f'data/tmp/{ctx.author.id}_light.png')
|
||||
|
||||
saved_card_dark = ImageOps.fit(saved_card_dark, (800, 500))
|
||||
saved_card_light = ImageOps.fit(saved_card_light, (800, 500))
|
||||
|
||||
cardbg.paste(saved_card_dark, (0, 0))
|
||||
cardbg.paste(saved_card_light, (800, 0))
|
||||
|
||||
cardbg.save(f'data/tmp/{ctx.author.id}.png', 'png')
|
||||
|
||||
with open(f'data/tmp/{ctx.author.id}.png', 'rb') as g:
|
||||
await ctx.send(file=discord.File(g))
|
||||
await wait_message.delete()
|
||||
await ctx.send(f"Et voila {ctx.author.mention} ! à gauche votre passeport avec le thème \"dark\" et à droite avec le thème \"light\" :wink:")
|
||||
|
||||
shutil.rmtree("data/tmp")
|
||||
os.mkdir("data/tmp")
|
||||
|
||||
else:
|
||||
em = discord.Embed(title='Une erreur est survenue', description="Les choix possible pour cette commande sont : `dark`, `light`, `preview`", colour=0xDC3546)
|
||||
await ctx.send(embed=em)
|
||||
|
||||
@passeport.command(name="delete", pass_context=True)
|
||||
async def passeport_delete(self, ctx):
|
||||
self.conn = db.connect_to_db(self)
|
||||
self.cursor = self.conn.cursor()
|
||||
|
||||
self.cursor.execute("""SELECT id, userid FROM passport WHERE userid = %s""", str(ctx.author.id))
|
||||
result = self.cursor.fetchone()
|
||||
|
||||
if result:
|
||||
def check(m):
|
||||
return m.author.id == ctx.author.id and \
|
||||
m.channel.id == ctx.channel.id
|
||||
|
||||
await ctx.send(f"{str(ctx.message.author.mention)}> envoyez `CONFIRMER` afin de supprimer vos données conformément à l'article 17 du `règlement général sur la protection des données` sur le `Droit à l'effacement`")
|
||||
response = await self.bot.wait_for('message', check=check, timeout=10.0 * 60.0)
|
||||
if response.content == "CONFIRMER":
|
||||
os.remove(f"data/users/backgrounds/{str(ctx.author.id)}.png")
|
||||
self.cursor.execute("""DELETE FROM passport WHERE userid =%s""", (str(ctx.message.author.id)))
|
||||
self.conn.commit()
|
||||
|
||||
em = discord.Embed(title='Suppression confirmée', description="Vos données ont été supprimées avec succès", colour=0x28a745)
|
||||
await ctx.send(embed=em)
|
||||
else:
|
||||
await ctx.send("Déja configure ton passeport avant de la supprimer u_u (après c'est pas logique...)")
|
||||
|
||||
def setup(bot):
|
||||
bot.add_cog(Passport(bot))
|
|
@ -23,7 +23,7 @@ class SendLogs(commands.Cog):
|
|||
|
||||
@commands.Cog.listener()
|
||||
async def on_ready(self):
|
||||
self.log_channel = self.bot.get_channel(int(self.bot.config.log_channel_id))
|
||||
self.log_channel = await self.bot.fetch_channel(int(self.bot.config.log_channel_id))
|
||||
em = discord.Embed(title="Je suis opérationnel 😃",
|
||||
description=f"*Instance lancée sur "
|
||||
f"{socket.gethostname()}*", colour=0x5cb85c)
|
||||
|
|
|
@ -15,76 +15,63 @@ class Sondage(commands.Cog):
|
|||
if msg != "help":
|
||||
await ctx.message.delete()
|
||||
options = msg.split(" | ")
|
||||
time = [x for x in options if x.startswith("time=")]
|
||||
if time:
|
||||
time = time[0]
|
||||
if time:
|
||||
options.remove(time)
|
||||
|
||||
times = [x for x in options if x.startswith("time=")]
|
||||
|
||||
if times:
|
||||
time = int(times[0].strip("time="))
|
||||
options.remove(times[0])
|
||||
else:
|
||||
time = 0
|
||||
|
||||
if len(options) <= 1:
|
||||
raise commands.errors.MissingRequiredArgument
|
||||
if len(options) >= 22:
|
||||
return await ctx.send(f"{ctx.message.author.mention}> "
|
||||
f":octagonal_sign: Vous ne pouvez pas "
|
||||
f"mettre plus de 20 réponses !")
|
||||
if time:
|
||||
time = int(time.strip("time="))
|
||||
else:
|
||||
time = 0
|
||||
emoji = ['1⃣',
|
||||
'2⃣',
|
||||
'3⃣',
|
||||
'4⃣',
|
||||
'5⃣',
|
||||
'6⃣',
|
||||
'7⃣',
|
||||
'8⃣',
|
||||
'9⃣',
|
||||
'🔟',
|
||||
'0⃣',
|
||||
'🇦',
|
||||
'🇧',
|
||||
'🇨',
|
||||
'🇩',
|
||||
'🇪',
|
||||
'🇫',
|
||||
'🇬',
|
||||
'🇭',
|
||||
'🇮'
|
||||
]
|
||||
|
||||
emoji = ['1⃣', '2⃣', '3⃣', '4⃣', '5⃣', '6⃣', '7⃣', '8⃣', '9⃣', '🔟', '0⃣',
|
||||
'🇦', '🇧', '🇨', '🇩', '🇪', '🇫', '🇬', '🇭', '🇮']
|
||||
to_react = []
|
||||
confirmation_msg = f"**{options[0].rstrip('?')}?**:\n\n"
|
||||
|
||||
for idx, option in enumerate(options[1:]):
|
||||
confirmation_msg += "{} - {}\n".format(emoji[idx], option)
|
||||
confirmation_msg += f"{emoji[idx]} - {option}\n"
|
||||
to_react.append(emoji[idx])
|
||||
|
||||
confirmation_msg += "*Sondage proposé par* " + \
|
||||
str(ctx.message.author.mention)
|
||||
if time == 0:
|
||||
confirmation_msg += ""
|
||||
else:
|
||||
confirmation_msg += "\n\nVous avez {} secondes pour voter!". \
|
||||
format(time)
|
||||
confirmation_msg += f"\n\nVous avez {time} secondes pour voter!"
|
||||
|
||||
poll_msg = await ctx.send(confirmation_msg)
|
||||
for emote in to_react:
|
||||
await poll_msg.add_reaction(emote)
|
||||
|
||||
if time != 0:
|
||||
await asyncio.sleep(time)
|
||||
|
||||
if time != 0:
|
||||
async for message in ctx.message.channel.history():
|
||||
if message.id == poll_msg.id:
|
||||
poll_msg = message
|
||||
|
||||
results = {}
|
||||
|
||||
for reaction in poll_msg.reactions:
|
||||
if reaction.emoji in to_react:
|
||||
results[reaction.emoji] = reaction.count - 1
|
||||
end_msg = "Le sondage est términé. Les résultats sont:\n\n"
|
||||
|
||||
for result in results:
|
||||
end_msg += "{} {} - {} votes\n". \
|
||||
format(result,
|
||||
options[emoji.index(result)+1],
|
||||
results[result])
|
||||
|
||||
top_result = max(results, key=lambda key: results[key])
|
||||
|
||||
if len([x for x in results
|
||||
if results[x] == results[top_result]]) > 1:
|
||||
top_results = []
|
||||
|
|
|
@ -3,7 +3,6 @@ import json
|
|||
import pytz
|
||||
import random
|
||||
import urllib
|
||||
import socket
|
||||
|
||||
import discord
|
||||
import requests
|
||||
|
@ -178,7 +177,6 @@ class Utility(commands.Cog):
|
|||
em.set_thumbnail(url = img)
|
||||
await ctx.send(embed=em)
|
||||
|
||||
|
||||
"""---------------------------------------------------------------------"""
|
||||
|
||||
@commands.command()
|
||||
|
@ -213,8 +211,7 @@ class Utility(commands.Cog):
|
|||
"""---------------------------------------------------------------------"""
|
||||
|
||||
@commands.command(name='iplocalise', pass_context=True)
|
||||
async def _iplocalise(self, ctx, ipaddress, iptype=""):
|
||||
realipaddress = ipaddress
|
||||
async def _iplocalise(self, ctx, ipaddress):
|
||||
"""Recup headers."""
|
||||
if ipaddress.startswith("http://"):
|
||||
if ipaddress[-1:] == '/':
|
||||
|
@ -225,39 +222,44 @@ class Utility(commands.Cog):
|
|||
ipaddress = ipaddress[:-1]
|
||||
ipaddress = ipaddress.split("https://")[1]
|
||||
|
||||
if(iptype=="ipv6" or iptype=="v6"):
|
||||
try:
|
||||
ipaddress = socket.getaddrinfo(ipaddress, None, socket.AF_INET6)[1][4][0]
|
||||
except:
|
||||
await ctx.send("Erreur, cette adresse n'est pas disponible en IPv6.")
|
||||
return
|
||||
|
||||
iploading = await ctx.send("_réfléchis..._")
|
||||
ipapi = urllib.request.urlopen("http://ip-api.com/json/" + ipaddress)
|
||||
ipinfo = json.loads(ipapi.read().decode())
|
||||
|
||||
if ipinfo["status"] != "fail":
|
||||
if ipinfo['query']:
|
||||
embed = discord.Embed(title=f"Informations pour ``{realipaddress}`` *`({ipinfo['query']})`*", color=0x5858d7)
|
||||
|
||||
if ipinfo['org']:
|
||||
embed.add_field(name="Appartient à :", value=ipinfo['org'], inline = False)
|
||||
org = ipinfo['org']
|
||||
else:
|
||||
org = 'n/a'
|
||||
|
||||
if ipinfo['query']:
|
||||
ip = ipinfo['query']
|
||||
else:
|
||||
ip = 'n/a'
|
||||
|
||||
if ipinfo['city']:
|
||||
embed.add_field(name="Se situe à :", value=ipinfo['city'], inline = True)
|
||||
city = ipinfo['city']
|
||||
else:
|
||||
city = 'n/a'
|
||||
|
||||
if ipinfo['regionName']:
|
||||
regionName = ipinfo['regionName']
|
||||
else:
|
||||
regionName = 'n/a'
|
||||
|
||||
if ipinfo['country']:
|
||||
if ipinfo['regionName']:
|
||||
regionName = ipinfo['regionName']
|
||||
else:
|
||||
regionName = "N/A"
|
||||
embed.add_field(name="Region :", value=f"{regionName} ({ipinfo['country']})", inline = True)
|
||||
country = ipinfo['country']
|
||||
else:
|
||||
country = 'n/a'
|
||||
|
||||
embed = discord.Embed(title=f"Informations pour {ipaddress} *`({ip})`*", color=0x5858d7)
|
||||
embed.add_field(name="Appartient à :", value=org, inline = False)
|
||||
embed.add_field(name="Se situe à :", value=city, inline = True)
|
||||
embed.add_field(name="Region :", value=f"{regionName} ({country})", inline = True)
|
||||
embed.set_thumbnail(url=f"https://www.countryflags.io/{ipinfo['countryCode']}/flat/64.png")
|
||||
await ctx.send(embed=embed)
|
||||
else:
|
||||
await ctx.send(content=f"Erreur, impossible d'avoir des informations sur l'adresse IP ``{ipinfo['query']}``")
|
||||
await ctx.send(content=f"Erreur, impossible d'avoir des informations sur l'adresse IP {ipinfo['query']}")
|
||||
await iploading.delete()
|
||||
|
||||
"""---------------------------------------------------------------------"""
|
||||
|
@ -310,7 +312,7 @@ class Utility(commands.Cog):
|
|||
"""Pour voir mon code"""
|
||||
text = "How tu veux voir mon repos Gitea pour me disséquer ? " \
|
||||
"Pas de soucis ! Je suis un Bot, je ne ressens pas la " \
|
||||
"douleur !\n https://git.gnous.eu/gnous/tuxbot"
|
||||
"douleur !\n https://git.gnous.eu/gnouseu/tuxbot-bot"
|
||||
em = discord.Embed(title='Repos TuxBot-Bot', description=text, colour=0xE9D460)
|
||||
em.set_author(name='Gnous', icon_url="https://cdn.discordapp.com/"
|
||||
"icons/280805240977227776/"
|
||||
|
@ -318,6 +320,41 @@ class Utility(commands.Cog):
|
|||
".png")
|
||||
await ctx.send(embed=em)
|
||||
|
||||
"""---------------------------------------------------------------------"""
|
||||
|
||||
@commands.command(name='quote', pass_context=True)
|
||||
async def _quote(self, ctx, quote_id):
|
||||
global quoted_message
|
||||
|
||||
async def get_message(message_id: int):
|
||||
for channel in ctx.message.guild.channels:
|
||||
if isinstance(channel, discord.TextChannel):
|
||||
test_chan = await self.bot.fetch_channel(channel.id)
|
||||
try:
|
||||
return await test_chan.fetch_message(message_id)
|
||||
except discord.NotFound:
|
||||
pass
|
||||
return None
|
||||
|
||||
quoted_message = await get_message(int(quote_id))
|
||||
|
||||
if quoted_message is not None:
|
||||
embed = discord.Embed(colour=quoted_message.author.colour,
|
||||
description=quoted_message.clean_content,
|
||||
timestamp=quoted_message.created_at)
|
||||
embed.set_author(name=quoted_message.author.display_name,
|
||||
icon_url=quoted_message.author.avatar_url_as(
|
||||
format="jpg"))
|
||||
if len(quoted_message.attachments) >= 1:
|
||||
embed.set_image(url=quoted_message.attachments[0].url)
|
||||
embed.add_field(name="**Original**",
|
||||
value=f"[Go!]({quoted_message.jump_url})")
|
||||
embed.set_footer(text="#" + quoted_message.channel.name)
|
||||
|
||||
await ctx.send(embed=embed)
|
||||
else:
|
||||
await ctx.send("Impossible de trouver le message.")
|
||||
|
||||
|
||||
def setup(bot):
|
||||
bot.add_cog(Utility(bot))
|
||||
|
|
|
@ -2,100 +2,123 @@ from discord.ext import commands
|
|||
|
||||
|
||||
def is_owner_check(message):
|
||||
return str(message.author.id) in ['171685542553976832', '269156684155453451']
|
||||
return str(message.author.id) in ['171685542553976832',
|
||||
'269156684155453451']
|
||||
|
||||
|
||||
def is_owner(warn=True):
|
||||
def check(ctx, warn):
|
||||
owner = is_owner_check(ctx.message)
|
||||
if not owner and warn:
|
||||
print(ctx.message.author.name + " à essayer d'executer " + ctx.message.content + " sur le serveur " + ctx.message.guild.name)
|
||||
return owner
|
||||
def check(ctx, log):
|
||||
owner = is_owner_check(ctx.message)
|
||||
if not owner and log:
|
||||
print(ctx.message.author.name + " à essayer d'executer " + ctx.message.content + " sur le serveur " + ctx.message.guild.name)
|
||||
return owner
|
||||
|
||||
owner = commands.check(lambda ctx: check(ctx, warn))
|
||||
return owner
|
||||
owner = commands.check(lambda ctx: check(ctx, warn))
|
||||
return owner
|
||||
|
||||
|
||||
"""-------------------------------------------------------------------------"""
|
||||
|
||||
"""--------------------------------------------------------------------------------------------------------------------------"""
|
||||
|
||||
async def check_permissions(ctx, perms, *, check=all):
|
||||
is_owner = await ctx.bot.is_owner(ctx.author)
|
||||
if is_owner or is_owner_check(ctx.message) == True:
|
||||
return True
|
||||
is_owner = await ctx.bot.is_owner(ctx.author)
|
||||
if is_owner or is_owner_check(ctx.message) is True:
|
||||
return True
|
||||
|
||||
resolved = ctx.channel.permissions_for(ctx.author)
|
||||
return check(getattr(resolved, name, None) == value for name, value in
|
||||
perms.items())
|
||||
|
||||
resolved = ctx.channel.permissions_for(ctx.author)
|
||||
return check(getattr(resolved, name, None) == value for name, value in perms.items())
|
||||
|
||||
def has_permissions(*, check=all, **perms):
|
||||
async def pred(ctx):
|
||||
return await check_permissions(ctx, perms, check=check)
|
||||
return commands.check(pred)
|
||||
async def pred(ctx):
|
||||
return await check_permissions(ctx, perms, check=check)
|
||||
|
||||
return commands.check(pred)
|
||||
|
||||
|
||||
async def check_guild_permissions(ctx, perms, *, check=all):
|
||||
is_owner = await ctx.bot.is_owner(ctx.author)
|
||||
if is_owner:
|
||||
return True
|
||||
is_owner = await ctx.bot.is_owner(ctx.author)
|
||||
if is_owner:
|
||||
return True
|
||||
|
||||
if ctx.guild is None:
|
||||
return False
|
||||
if ctx.guild is None:
|
||||
return False
|
||||
|
||||
resolved = ctx.author.guild_permissions
|
||||
return check(getattr(resolved, name, None) == value for name, value in
|
||||
perms.items())
|
||||
|
||||
resolved = ctx.author.guild_permissions
|
||||
return check(getattr(resolved, name, None) == value for name, value in perms.items())
|
||||
|
||||
def has_guild_permissions(*, check=all, **perms):
|
||||
async def pred(ctx):
|
||||
return await check_guild_permissions(ctx, perms, check=check)
|
||||
return commands.check(pred)
|
||||
async def pred(ctx):
|
||||
return await check_guild_permissions(ctx, perms, check=check)
|
||||
|
||||
return commands.check(pred)
|
||||
|
||||
|
||||
# These do not take channel overrides into account
|
||||
|
||||
|
||||
def is_mod():
|
||||
async def pred(ctx):
|
||||
return await check_guild_permissions(ctx, {'manage_guild': True})
|
||||
return commands.check(pred)
|
||||
async def pred(ctx):
|
||||
return await check_guild_permissions(ctx, {'manage_guild': True})
|
||||
|
||||
return commands.check(pred)
|
||||
|
||||
|
||||
def is_admin():
|
||||
async def pred(ctx):
|
||||
return await check_guild_permissions(ctx, {'administrator': True})
|
||||
return commands.check(pred)
|
||||
async def pred(ctx):
|
||||
return await check_guild_permissions(ctx, {'administrator': True})
|
||||
|
||||
return commands.check(pred)
|
||||
|
||||
|
||||
def mod_or_permissions(**perms):
|
||||
perms['manage_guild'] = True
|
||||
async def predicate(ctx):
|
||||
return await check_guild_permissions(ctx, perms, check=any)
|
||||
return commands.check(predicate)
|
||||
perms['manage_guild'] = True
|
||||
|
||||
async def predicate(ctx):
|
||||
return await check_guild_permissions(ctx, perms, check=any)
|
||||
|
||||
return commands.check(predicate)
|
||||
|
||||
|
||||
def admin_or_permissions(**perms):
|
||||
perms['administrator'] = True
|
||||
async def predicate(ctx):
|
||||
return await check_guild_permissions(ctx, perms, check=any)
|
||||
return commands.check(predicate)
|
||||
perms['administrator'] = True
|
||||
|
||||
async def predicate(ctx):
|
||||
return await check_guild_permissions(ctx, perms, check=any)
|
||||
|
||||
return commands.check(predicate)
|
||||
|
||||
|
||||
def is_in_guilds(*guild_ids):
|
||||
def predicate(ctx):
|
||||
guild = ctx.guild
|
||||
if guild is None:
|
||||
return False
|
||||
return guild.id in guild_ids
|
||||
return commands.check(predicate)
|
||||
def predicate(ctx):
|
||||
guild = ctx.guild
|
||||
if guild is None:
|
||||
return False
|
||||
return guild.id in guild_ids
|
||||
|
||||
return commands.check(predicate)
|
||||
|
||||
def is_lounge_cpp():
|
||||
return is_in_guilds(145079846832308224)
|
||||
|
||||
def get_user(message, user):
|
||||
try:
|
||||
member = message.mentions[0]
|
||||
except:
|
||||
member = message.guild.get_member_named(user)
|
||||
if not member:
|
||||
try:
|
||||
member = message.guild.get_member(int(user))
|
||||
except ValueError:
|
||||
pass
|
||||
if not member:
|
||||
return None
|
||||
return member
|
||||
try:
|
||||
member = message.mentions[0]
|
||||
except:
|
||||
member = message.guild.get_member_named(user)
|
||||
if not member:
|
||||
try:
|
||||
member = message.guild.get_member(int(user))
|
||||
except ValueError:
|
||||
pass
|
||||
if not member:
|
||||
return None
|
||||
return member
|
||||
|
||||
|
||||
def check_date(date: str):
|
||||
if len(date) == 1:
|
||||
return f"0{date}"
|
||||
else:
|
||||
return date
|
||||
if len(date) == 1:
|
||||
return f"0{date}"
|
||||
else:
|
||||
return date
|
||||
|
|
|
@ -16,6 +16,7 @@ class text_colors:
|
|||
LIGHT_CYAN = '\033[96m'
|
||||
WHITE = '\033[97m'
|
||||
|
||||
|
||||
class bg_colors:
|
||||
BLACK = '\033[40m'
|
||||
RED = '\033[41m'
|
||||
|
@ -34,10 +35,12 @@ class bg_colors:
|
|||
LIGHT_CYAN = '\033[106m'
|
||||
WHITE = '\033[107m'
|
||||
|
||||
|
||||
class text_style:
|
||||
BOLD = '\033[1m'
|
||||
DIM = '\033[2m'
|
||||
UNDERLINE = '\033[4m'
|
||||
BLINK = '\033[5m'
|
||||
|
||||
|
||||
ENDC = '\033[0m'
|
|
@ -1,245 +0,0 @@
|
|||
from PIL import Image
|
||||
from PIL import ImageOps
|
||||
from PIL import ImageDraw
|
||||
from PIL import ImageFont
|
||||
|
||||
from .checks import check_date
|
||||
import aiohttp, imghdr, textwrap, math, datetime
|
||||
|
||||
async def generate_passport(self, user, theme: str = None):
|
||||
name = user.name
|
||||
userid = user.id
|
||||
avatar = user.avatar_url
|
||||
def_avatar = user.default_avatar_url
|
||||
created = datetime.datetime.fromisoformat(str(user.created_at))
|
||||
nick = user.display_name
|
||||
discr = user.discriminator
|
||||
roles = user.roles
|
||||
|
||||
top_role_color = str(user.top_role.color)[1:]
|
||||
user_color = tuple(int(top_role_color[i:i+2], 16) for i in (0, 2 ,4))
|
||||
user_color += (255,)
|
||||
|
||||
color = {
|
||||
"dark": {
|
||||
"background": 39,
|
||||
"question": 220,
|
||||
"answer": 150
|
||||
},
|
||||
"light": {
|
||||
"background": 216,
|
||||
"question": 35,
|
||||
"answer": 61
|
||||
}
|
||||
}
|
||||
|
||||
user_birth_day = check_date(str(created.day))
|
||||
user_birth_month = check_date(str(created.month))
|
||||
|
||||
formated_user_birth = str(user_birth_day) + "/" + str(user_birth_month) + "/" + str(created.year)
|
||||
formated_passportdate = "n/a"
|
||||
|
||||
roleImages = {}
|
||||
|
||||
def draw_underlined_text(draw, pos, text, font, **options):
|
||||
twidth, theight = draw.textsize(text, font=font)
|
||||
lx, ly = pos[0], pos[1] + theight
|
||||
draw.text(pos, text, font=font, **options)
|
||||
draw.line((lx, ly, lx + twidth, ly), **options)
|
||||
|
||||
def break_line(draw, pos, text, font, **options):
|
||||
lines = text.split("\n")
|
||||
current_y = pos[1]
|
||||
for line in lines:
|
||||
twidth, theight = draw.textsize(line, font=font)
|
||||
lx, ly = pos[0], current_y
|
||||
if textwrap.fill(line, 60) == line:
|
||||
draw.text((lx, ly), textwrap.fill(line, 60), font=font, **options)
|
||||
current_y += math.floor(theight)
|
||||
else:
|
||||
draw.text((lx, ly), textwrap.fill(line, 60), font=font, **options)
|
||||
current_y += math.floor(theight*2)
|
||||
|
||||
for x, role in enumerate(roles):
|
||||
try:
|
||||
roleImages[role.name] = Image.open(f"data/images/roles/small/{role.name.lower().replace(' user', '')}.png")
|
||||
except Exception as e:
|
||||
next
|
||||
|
||||
if avatar == '':
|
||||
async with aiohttp.ClientSession() as session:
|
||||
async with session.get(def_avatar) as r:
|
||||
image = await r.content.read()
|
||||
else:
|
||||
async with aiohttp.ClientSession() as session:
|
||||
async with session.get(avatar) as r:
|
||||
image = await r.content.read()
|
||||
with open('data/users/avatars/{}.png'.format(user.id), 'wb') as f:
|
||||
f.write(image)
|
||||
|
||||
checked = False
|
||||
|
||||
while checked == False:
|
||||
checks = 0
|
||||
isImage = imghdr.what('data/users/avatars/{}.png'.format(user.id))
|
||||
|
||||
if checks > 4:
|
||||
checked = True
|
||||
|
||||
if isImage != 'None':
|
||||
checked = True
|
||||
else:
|
||||
checks += 1
|
||||
|
||||
av = Image.open('data/users/avatars/{}.png'.format(user.id))
|
||||
userAvatar = av.resize((128, 128), resample=Image.BILINEAR).convert('RGBA')
|
||||
maxsize = ( 800, 500)
|
||||
try:
|
||||
bg = Image.open('data/users/backgrounds/{0}.png'.format(user.id))
|
||||
bg_width, bg_height = bg.size
|
||||
|
||||
bg = ImageOps.fit(bg,maxsize)
|
||||
|
||||
except:
|
||||
bg = Image.open('data/images/background_default.png')
|
||||
|
||||
fontFace = 'data/fonts/{}'.format(self.bot.config.fonts['normal'])
|
||||
fontFace_bold = 'data/fonts/{}'.format(self.bot.config.fonts['bold'])
|
||||
|
||||
fontSize = 18
|
||||
fontSizeVeryTiny = 16
|
||||
|
||||
descSizeQuestion = 10
|
||||
descSizeAnswer = 10
|
||||
|
||||
headerSize = 32
|
||||
headerSizeTiny = 24
|
||||
headerSizeVeryTiny = 16
|
||||
|
||||
header_font = ImageFont.truetype(fontFace_bold, headerSize)
|
||||
header_font_tiny = ImageFont.truetype(fontFace_bold, headerSizeTiny)
|
||||
header_font_very_tiny = ImageFont.truetype(fontFace_bold, headerSizeVeryTiny)
|
||||
|
||||
font = ImageFont.truetype(fontFace, fontSize)
|
||||
font_very_tiny = ImageFont.truetype(fontFace, fontSizeVeryTiny)
|
||||
|
||||
desc_font_question = ImageFont.truetype(fontFace_bold, descSizeQuestion)
|
||||
desc_font_answer = ImageFont.truetype(fontFace, descSizeAnswer)
|
||||
font_bold = font = ImageFont.truetype(fontFace_bold, fontSize)
|
||||
|
||||
answers = None
|
||||
|
||||
self.cursor.execute("SELECT os, config, languages, pays, passportdate, theme FROM passport WHERE userid=%s", str(user.id))
|
||||
answers = self.cursor.fetchone()
|
||||
|
||||
if not theme:
|
||||
if answers:
|
||||
theme = str(answers[5])
|
||||
else:
|
||||
theme = "dark"
|
||||
|
||||
cardbg = Image.new('RGBA', (800, 500), (0, 0, 0, 255))
|
||||
d = ImageDraw.Draw(cardbg)
|
||||
|
||||
d.rectangle([(0, 0), 800, 500], fill=(255, 255, 255, 255))
|
||||
cardbg.paste(bg, (0, 0))
|
||||
|
||||
cardfg = Image.new('RGBA', (800, 500), (255, 255, 255, 0))
|
||||
dd = ImageDraw.Draw(cardfg)
|
||||
|
||||
# Info Box Top
|
||||
dd.rectangle([(60, 60), (600, 191)], fill=(color[theme]["background"], color[theme]["background"], color[theme]["background"], 200))
|
||||
dd.rectangle([(60, 60), (600, 134)], fill=(color[theme]["background"], color[theme]["background"], color[theme]["background"], 255))
|
||||
|
||||
# Avatar box
|
||||
if user_color == (0, 0, 0, 255):
|
||||
user_color = (color[theme]["background"], color[theme]["background"], color[theme]["background"], 255)
|
||||
dd.rectangle([(609, 60), (740, 191)], fill=user_color)
|
||||
cardfg.paste(userAvatar, (611, 62))
|
||||
|
||||
# Profile Information
|
||||
if textwrap.fill(nick, 25) != nick:
|
||||
dd.text((70, 70), nick, fill=(color[theme]["question"], color[theme]["question"], color[theme]["question"], 220), font=header_font_very_tiny)
|
||||
dd.text((70, 106), '@' + name + '#' + discr, fill=(color[theme]["answer"], color[theme]["answer"], color[theme]["answer"], 225), font=font_very_tiny)
|
||||
elif textwrap.fill(nick, 15) != nick:
|
||||
dd.text((70, 70), nick, fill=(color[theme]["question"], color[theme]["question"], color[theme]["question"], 220), font=header_font_tiny)
|
||||
dd.text((70, 106), '@' + name + '#' + discr, fill=(color[theme]["answer"], color[theme]["answer"], color[theme]["answer"], 225), font=font)
|
||||
else:
|
||||
dd.text((70, 64), nick, fill=(color[theme]["question"], color[theme]["question"], color[theme]["question"], 220), font=header_font)
|
||||
dd.text((70, 106), '@' + name + '#' + discr, fill=(color[theme]["answer"], color[theme]["answer"], color[theme]["answer"], 225), font=font)
|
||||
|
||||
draw_underlined_text(dd, (380, 75), "Date de parution sur discord :", fill=(color[theme]["question"], color[theme]["question"], color[theme]["question"], 220), font=desc_font_question)
|
||||
dd.text((542, 75), formated_user_birth, fill=(color[theme]["answer"], color[theme]["answer"], color[theme]["answer"], 225), font=desc_font_answer)
|
||||
|
||||
# Roles
|
||||
for idy, ii in enumerate(roleImages):
|
||||
|
||||
startx = int((270 - (30 * len(roleImages))) / 2)
|
||||
|
||||
cardfg.paste(roleImages[ii], (197 + startx + (30 * idy),152), roleImages[ii])
|
||||
|
||||
|
||||
#Info Box Bottom
|
||||
dd.rectangle([(60, 200), (740, 450)], fill=(color[theme]["background"], color[theme]["background"], color[theme]["background"], 200))
|
||||
|
||||
if answers:
|
||||
passportdate = datetime.datetime.fromisoformat(answers[4])
|
||||
passportdate_day = check_date(str(passportdate.day))
|
||||
passportdate_month = check_date(str(passportdate.month))
|
||||
|
||||
formated_passportdate = str(passportdate_day) + "/" + str(passportdate_month) + "/" + str(passportdate.year)
|
||||
|
||||
draw_underlined_text(dd, (80, 220), "Système(s) d'exploitation :", desc_font_question, fill=(color[theme]["question"], color[theme]["question"], color[theme]["question"], 255))
|
||||
break_line(
|
||||
dd,
|
||||
(80, 240),
|
||||
answers[0],
|
||||
fill=(color[theme]["answer"], color[theme]["answer"], color[theme]["answer"], 255),
|
||||
font=desc_font_answer
|
||||
)
|
||||
|
||||
draw_underlined_text(dd, (80, 300), "Langages de programmation préférés :", desc_font_question, fill=(color[theme]["question"], color[theme]["question"], color[theme]["question"], 255))
|
||||
break_line(
|
||||
dd,
|
||||
(80, 320),
|
||||
answers[2],
|
||||
fill=(color[theme]["answer"], color[theme]["answer"], color[theme]["answer"], 255),
|
||||
font=desc_font_answer
|
||||
)
|
||||
|
||||
draw_underlined_text(dd, (80, 380), "Pays :", desc_font_question, fill=(color[theme]["question"], color[theme]["question"], color[theme]["question"], 255))
|
||||
break_line(
|
||||
dd,
|
||||
(80, 400),
|
||||
answers[3],
|
||||
fill=(color[theme]["answer"], color[theme]["answer"], color[theme]["answer"], 255),
|
||||
font=desc_font_answer
|
||||
)
|
||||
|
||||
dd.line((400, 220, 400, 430), fill=(color[theme]["answer"], color[theme]["answer"], color[theme]["answer"], 255))
|
||||
|
||||
draw_underlined_text(dd, (410, 220), "Configuration Système :", desc_font_question, fill=(color[theme]["question"], color[theme]["question"], color[theme]["question"], 255))
|
||||
break_line(
|
||||
dd,
|
||||
(410, 240),
|
||||
answers[1],
|
||||
fill=(color[theme]["answer"], color[theme]["answer"], color[theme]["answer"], 255),
|
||||
font=desc_font_answer
|
||||
)
|
||||
|
||||
else:
|
||||
dd.text(
|
||||
(370, 300),
|
||||
"Non renseigné.",
|
||||
fill=(color[theme]["question"], color[theme]["question"], color[theme]["question"], 255),
|
||||
font=desc_font_question
|
||||
)
|
||||
|
||||
draw_underlined_text(dd, (380, 100), "Date de création du passeport :", fill=(color[theme]["question"], color[theme]["question"], color[theme]["question"], 220), font=desc_font_question)
|
||||
dd.text((542, 100), formated_passportdate, fill=(color[theme]["answer"], color[theme]["answer"], color[theme]["answer"], 225), font=desc_font_answer)
|
||||
|
||||
card = Image.new('RGBA', (800, 500), (255, 255, 255, 255))
|
||||
card = Image.alpha_composite(card, cardbg)
|
||||
card = Image.alpha_composite(card, cardfg)
|
||||
|
||||
return card
|
|
@ -1,7 +1,7 @@
|
|||
token = "INSERT TOKEN HERE"
|
||||
client_id = "INSERT_CLIENT_ID_HERE"
|
||||
log_channel_id = "INSERT_LOG_CHANNEL_HERE"
|
||||
main_server_id = "INSERT_MAIN_CHANNEL_ID_HERE"
|
||||
client_id = <INSERT_CLIENT_ID_HERE (in int)>
|
||||
log_channel_id = <INSERT_LOG_CHANNEL_HERE (in int)>
|
||||
main_server_id = <INSERT_MAIN_CHANNEL_ID_HERE (in int)>
|
||||
|
||||
game = "PLAYING_GAME_HERE"
|
||||
prefix = ["."]
|
||||
|
@ -17,10 +17,4 @@ mysql = {
|
|||
}
|
||||
|
||||
authorized_id = ['admin ids here']
|
||||
|
||||
## Passport settings
|
||||
|
||||
fonts = {
|
||||
"normal": "NotoSansCJK-Regular.ttc",
|
||||
"bold": "NotoSansCJK-Bold.ttc"
|
||||
}
|
||||
unkickable_id = ['unkickable ids here']
|
||||
|
|
BIN
data/.DS_Store
vendored
BIN
data/images/.DS_Store
vendored
Before Width: | Height: | Size: 627 KiB |
Before Width: | Height: | Size: 1.3 MiB |
Before Width: | Height: | Size: 1,012 B |
Before Width: | Height: | Size: 1.6 KiB |
Before Width: | Height: | Size: 763 B |
Before Width: | Height: | Size: 845 B |
Before Width: | Height: | Size: 1 KiB |
Before Width: | Height: | Size: 2 KiB |
Before Width: | Height: | Size: 962 B |
Before Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 239 B |
Before Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 518 B |
Before Width: | Height: | Size: 2.4 KiB |
Before Width: | Height: | Size: 1.7 KiB |
Before Width: | Height: | Size: 1.6 KiB |
Before Width: | Height: | Size: 2 KiB |
Before Width: | Height: | Size: 478 B |
Before Width: | Height: | Size: 2 KiB |
Before Width: | Height: | Size: 2.2 KiB |
Before Width: | Height: | Size: 610 B |
Before Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 2 KiB |
Before Width: | Height: | Size: 2.5 KiB |
Before Width: | Height: | Size: 917 B |
Before Width: | Height: | Size: 1.6 KiB |
Before Width: | Height: | Size: 2.3 KiB |
Before Width: | Height: | Size: 2.2 KiB |
Before Width: | Height: | Size: 261 B |
Before Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 911 B |
|
@ -1,2 +0,0 @@
|
|||
-- This file is only for keeping the folder alive in GitHub. --
|
||||
-- Ce fichier ne sert qu'à garder ce dossier en vie dans GitHub. --
|