fix(security): fix high security breach
7
.gitignore
vendored
|
@ -1,9 +1,10 @@
|
||||||
#Don't track user data
|
|
||||||
data/users/
|
|
||||||
|
|
||||||
#Python
|
#Python
|
||||||
__pycache__/
|
__pycache__/
|
||||||
*.pyc
|
*.pyc
|
||||||
.env
|
.env
|
||||||
config.py
|
config.py
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
private.py
|
||||||
|
|
||||||
|
#jetbrains
|
||||||
|
.idea/
|
17
bot.py
|
@ -24,10 +24,8 @@ l_extensions = (
|
||||||
'cogs.atc',
|
'cogs.atc',
|
||||||
'cogs.basics',
|
'cogs.basics',
|
||||||
'cogs.ci',
|
'cogs.ci',
|
||||||
'cogs.cog_manager',
|
|
||||||
'cogs.filter_messages',
|
'cogs.filter_messages',
|
||||||
'cogs.funs',
|
'cogs.funs',
|
||||||
'cogs.passport',
|
|
||||||
'cogs.role',
|
'cogs.role',
|
||||||
'cogs.search',
|
'cogs.search',
|
||||||
'cogs.send_logs',
|
'cogs.send_logs',
|
||||||
|
@ -46,8 +44,7 @@ class TuxBot(commands.Bot):
|
||||||
super().__init__(command_prefix=self.config.prefix[0],
|
super().__init__(command_prefix=self.config.prefix[0],
|
||||||
description=self.config.description,
|
description=self.config.description,
|
||||||
pm_help=None,
|
pm_help=None,
|
||||||
help_command = None
|
help_command=None)
|
||||||
)
|
|
||||||
|
|
||||||
self.client_id = self.config.client_id
|
self.client_id = self.config.client_id
|
||||||
self.session = aiohttp.ClientSession(loop=self.loop)
|
self.session = aiohttp.ClientSession(loop=self.loop)
|
||||||
|
@ -67,10 +64,10 @@ class TuxBot(commands.Bot):
|
||||||
|
|
||||||
async def on_command_error(self, ctx, error):
|
async def on_command_error(self, ctx, error):
|
||||||
if isinstance(error, commands.NoPrivateMessage):
|
if isinstance(error, commands.NoPrivateMessage):
|
||||||
await ctx.author.send('Cette commande ne peut pas être utilisée '
|
await ctx.author.send('Cette commande ne peut pas être utilisee '
|
||||||
'en message privé.')
|
'en message privee.')
|
||||||
elif isinstance(error, commands.DisabledCommand):
|
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.')
|
'elle ne peut donc pas être utilisée.')
|
||||||
elif isinstance(error, commands.CommandInvokeError):
|
elif isinstance(error, commands.CommandInvokeError):
|
||||||
print(f'In {ctx.command.qualified_name}:', file=sys.stderr)
|
print(f'In {ctx.command.qualified_name}:', file=sys.stderr)
|
||||||
|
@ -79,13 +76,13 @@ class TuxBot(commands.Bot):
|
||||||
file=sys.stderr)
|
file=sys.stderr)
|
||||||
|
|
||||||
async def on_ready(self):
|
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('\n\n---------------------')
|
||||||
print('CONNECTÉ :')
|
print('CONNECTÉ :')
|
||||||
print(f'Nom d\'utilisateur: {self.user} {colors.text_style.DIM}'
|
print(f'Nom d\'utilisateur: {self.user} {colors.text_style.DIM}'
|
||||||
f'(ID: {self.user.id}){colors.ENDC}')
|
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}')
|
f'(ID: {log_channel_id.id}){colors.ENDC}')
|
||||||
print(f'Prefix: {self.config.prefix[0]}')
|
print(f'Prefix: {self.config.prefix[0]}')
|
||||||
print('Merci d\'utiliser TuxBot')
|
print('Merci d\'utiliser TuxBot')
|
||||||
|
@ -93,7 +90,7 @@ class TuxBot(commands.Bot):
|
||||||
|
|
||||||
await self.change_presence(status=discord.Status.dnd,
|
await self.change_presence(status=discord.Status.dnd,
|
||||||
activity=discord.Game(
|
activity=discord.Game(
|
||||||
name=self.config.game),
|
name=self.config.game)
|
||||||
)
|
)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
|
214
cogs/admin.py
|
@ -1,16 +1,10 @@
|
||||||
|
import aiohttp
|
||||||
from discord.ext import commands
|
from discord.ext import commands
|
||||||
import discord
|
import discord
|
||||||
import asyncio
|
|
||||||
from .utils import checks
|
from .utils import checks
|
||||||
|
|
||||||
from .utils.checks import get_user
|
from .utils.checks import get_user
|
||||||
|
|
||||||
import traceback
|
|
||||||
import textwrap
|
|
||||||
from contextlib import redirect_stdout
|
|
||||||
import inspect
|
|
||||||
import io
|
|
||||||
|
|
||||||
|
|
||||||
class Admin(commands.Cog):
|
class Admin(commands.Cog):
|
||||||
"""Commandes secrètes d'administration."""
|
"""Commandes secrètes d'administration."""
|
||||||
|
@ -20,20 +14,37 @@ class Admin(commands.Cog):
|
||||||
self._last_result = None
|
self._last_result = None
|
||||||
self.sessions = set()
|
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)
|
@checks.has_permissions(administrator=True)
|
||||||
@commands.command(pass_context=True)
|
@commands.command(name="upload", pass_context=True)
|
||||||
async def ban(self, ctx, user, *, reason=""):
|
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"""
|
"""Ban user"""
|
||||||
user = get_user(ctx.message, user)
|
user = get_user(ctx.message, user)
|
||||||
if user:
|
if user and str(user.id) not in self.bot.config.unkickable_id:
|
||||||
try:
|
try:
|
||||||
await user.ban(reason=reason)
|
await user.ban(reason=reason)
|
||||||
return_msg = f"`{user.mention}` a été banni\n"
|
return_msg = f"`{user.mention}` a été banni\n"
|
||||||
|
@ -50,11 +61,11 @@ class Admin(commands.Cog):
|
||||||
"""---------------------------------------------------------------------"""
|
"""---------------------------------------------------------------------"""
|
||||||
|
|
||||||
@checks.has_permissions(administrator=True)
|
@checks.has_permissions(administrator=True)
|
||||||
@commands.command(pass_context=True)
|
@commands.command(name="kick", pass_context=True)
|
||||||
async def kick(self, ctx, user, *, reason=""):
|
async def _kick(self, ctx, user, *, reason=""):
|
||||||
"""Kick a user"""
|
"""Kick a user"""
|
||||||
user = get_user(ctx.message, user)
|
user = get_user(ctx.message, user)
|
||||||
if user:
|
if user and str(user.id) not in self.bot.config.unkickable_id:
|
||||||
try:
|
try:
|
||||||
await user.kick(reason=reason)
|
await user.kick(reason=reason)
|
||||||
return_msg = f"`{user.mention}` a été kické\n"
|
return_msg = f"`{user.mention}` a été kické\n"
|
||||||
|
@ -155,7 +166,7 @@ class Admin(commands.Cog):
|
||||||
|
|
||||||
@checks.has_permissions(administrator=True)
|
@checks.has_permissions(administrator=True)
|
||||||
@commands.command(name='editsay', pass_context=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"""
|
"""Edit a bot's message"""
|
||||||
try:
|
try:
|
||||||
try:
|
try:
|
||||||
|
@ -163,10 +174,10 @@ class Admin(commands.Cog):
|
||||||
except Exception:
|
except Exception:
|
||||||
print(f"Impossible de supprimer le message "
|
print(f"Impossible de supprimer le message "
|
||||||
f"\"{str(ctx.message.content)}\"")
|
f"\"{str(ctx.message.content)}\"")
|
||||||
toedit = await ctx.channel.get_message(id)
|
toedit = await ctx.channel.get_message(message_id)
|
||||||
except discord.errors.NotFound:
|
except discord.errors.NotFound:
|
||||||
await ctx.send(f"Impossible de trouver le message avec l'id "
|
await ctx.send(f"Impossible de trouver le message avec l'id "
|
||||||
f"`{id}` sur ce salon")
|
f"`{message_id}` sur ce salon")
|
||||||
return
|
return
|
||||||
try:
|
try:
|
||||||
await toedit.edit(content=str(new_content))
|
await toedit.edit(content=str(new_content))
|
||||||
|
@ -177,7 +188,7 @@ class Admin(commands.Cog):
|
||||||
|
|
||||||
@checks.has_permissions(administrator=True)
|
@checks.has_permissions(administrator=True)
|
||||||
@commands.command(name='addreaction', pass_context=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"""
|
"""Add reactions to a message"""
|
||||||
try:
|
try:
|
||||||
try:
|
try:
|
||||||
|
@ -185,10 +196,10 @@ class Admin(commands.Cog):
|
||||||
except Exception:
|
except Exception:
|
||||||
print(f"Impossible de supprimer le message "
|
print(f"Impossible de supprimer le message "
|
||||||
f"\"{str(ctx.message.content)}\"")
|
f"\"{str(ctx.message.content)}\"")
|
||||||
toadd = await ctx.channel.get_message(id)
|
toadd = await ctx.channel.get_message(message_id)
|
||||||
except discord.errors.NotFound:
|
except discord.errors.NotFound:
|
||||||
await ctx.send(f"Impossible de trouver le message avec l'id "
|
await ctx.send(f"Impossible de trouver le message avec l'id "
|
||||||
f"`{id}` sur ce salon")
|
f"`{message_id}` sur ce salon")
|
||||||
return
|
return
|
||||||
try:
|
try:
|
||||||
await toadd.add_reaction(reaction)
|
await toadd.add_reaction(reaction)
|
||||||
|
@ -199,7 +210,7 @@ class Admin(commands.Cog):
|
||||||
|
|
||||||
@checks.has_permissions(administrator=True)
|
@checks.has_permissions(administrator=True)
|
||||||
@commands.command(name='delete', pass_context=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"""
|
"""Delete message in current channel"""
|
||||||
try:
|
try:
|
||||||
try:
|
try:
|
||||||
|
@ -207,10 +218,10 @@ class Admin(commands.Cog):
|
||||||
except Exception:
|
except Exception:
|
||||||
print(f"Impossible de supprimer le message "
|
print(f"Impossible de supprimer le message "
|
||||||
f"\"{str(ctx.message.content)}\"")
|
f"\"{str(ctx.message.content)}\"")
|
||||||
todelete = await ctx.channel.get_message(id)
|
todelete = await ctx.channel.get_message(message_id)
|
||||||
except discord.errors.NotFound:
|
except discord.errors.NotFound:
|
||||||
await ctx.send(f"Impossible de trouver le message avec l'id "
|
await ctx.send(f"Impossible de trouver le message avec l'id "
|
||||||
f"`{id}` sur ce salon")
|
f"`{message_id}` sur ce salon")
|
||||||
return
|
return
|
||||||
try:
|
try:
|
||||||
await todelete.delete()
|
await todelete.delete()
|
||||||
|
@ -221,7 +232,7 @@ class Admin(commands.Cog):
|
||||||
|
|
||||||
@checks.has_permissions(administrator=True)
|
@checks.has_permissions(administrator=True)
|
||||||
@commands.command(name='deletefrom', pass_context=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"""
|
"""Delete message in <chan_id> channel"""
|
||||||
try:
|
try:
|
||||||
chan = self.bot.get_channel(chan_id)
|
chan = self.bot.get_channel(chan_id)
|
||||||
|
@ -391,151 +402,6 @@ class Admin(commands.Cog):
|
||||||
|
|
||||||
await ctx.send(embed=embed)
|
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):
|
def setup(bot):
|
||||||
bot.add_cog(Admin(bot))
|
bot.add_cog(Admin(bot))
|
||||||
|
|
|
@ -61,10 +61,10 @@ class Basics(commands.Cog):
|
||||||
async def help(self, ctx):
|
async def help(self, ctx):
|
||||||
"""Affiches l'aide du bot"""
|
"""Affiches l'aide du bot"""
|
||||||
text = open('texts/help.md').read()
|
text = open('texts/help.md').read()
|
||||||
text = text.split("[split]")
|
em = discord.Embed(title='Commandes de TuxBot', description=text,
|
||||||
for text_result in text:
|
colour=0x89C4F9)
|
||||||
em = discord.Embed(title='Commandes de TuxBot', description=text_result,colour=0x89C4F9)
|
await ctx.send(embed=em)
|
||||||
await ctx.send(embed=em)
|
|
||||||
|
|
||||||
def setup(bot):
|
def setup(bot):
|
||||||
bot.add_cog(Basics(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()
|
@commands.Cog.listener()
|
||||||
async def on_ready(self):
|
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 😃",
|
em = discord.Embed(title="Je suis opérationnel 😃",
|
||||||
description=f"*Instance lancée sur "
|
description=f"*Instance lancée sur "
|
||||||
f"{socket.gethostname()}*", colour=0x5cb85c)
|
f"{socket.gethostname()}*", colour=0x5cb85c)
|
||||||
|
|
|
@ -15,76 +15,63 @@ class Sondage(commands.Cog):
|
||||||
if msg != "help":
|
if msg != "help":
|
||||||
await ctx.message.delete()
|
await ctx.message.delete()
|
||||||
options = msg.split(" | ")
|
options = msg.split(" | ")
|
||||||
time = [x for x in options if x.startswith("time=")]
|
|
||||||
if time:
|
times = [x for x in options if x.startswith("time=")]
|
||||||
time = time[0]
|
|
||||||
if time:
|
if times:
|
||||||
options.remove(time)
|
time = int(times[0].strip("time="))
|
||||||
|
options.remove(times[0])
|
||||||
|
else:
|
||||||
|
time = 0
|
||||||
|
|
||||||
if len(options) <= 1:
|
if len(options) <= 1:
|
||||||
raise commands.errors.MissingRequiredArgument
|
raise commands.errors.MissingRequiredArgument
|
||||||
if len(options) >= 22:
|
if len(options) >= 22:
|
||||||
return await ctx.send(f"{ctx.message.author.mention}> "
|
return await ctx.send(f"{ctx.message.author.mention}> "
|
||||||
f":octagonal_sign: Vous ne pouvez pas "
|
f":octagonal_sign: Vous ne pouvez pas "
|
||||||
f"mettre plus de 20 réponses !")
|
f"mettre plus de 20 réponses !")
|
||||||
if time:
|
|
||||||
time = int(time.strip("time="))
|
emoji = ['1⃣', '2⃣', '3⃣', '4⃣', '5⃣', '6⃣', '7⃣', '8⃣', '9⃣', '🔟', '0⃣',
|
||||||
else:
|
'🇦', '🇧', '🇨', '🇩', '🇪', '🇫', '🇬', '🇭', '🇮']
|
||||||
time = 0
|
|
||||||
emoji = ['1⃣',
|
|
||||||
'2⃣',
|
|
||||||
'3⃣',
|
|
||||||
'4⃣',
|
|
||||||
'5⃣',
|
|
||||||
'6⃣',
|
|
||||||
'7⃣',
|
|
||||||
'8⃣',
|
|
||||||
'9⃣',
|
|
||||||
'🔟',
|
|
||||||
'0⃣',
|
|
||||||
'🇦',
|
|
||||||
'🇧',
|
|
||||||
'🇨',
|
|
||||||
'🇩',
|
|
||||||
'🇪',
|
|
||||||
'🇫',
|
|
||||||
'🇬',
|
|
||||||
'🇭',
|
|
||||||
'🇮'
|
|
||||||
]
|
|
||||||
to_react = []
|
to_react = []
|
||||||
confirmation_msg = f"**{options[0].rstrip('?')}?**:\n\n"
|
confirmation_msg = f"**{options[0].rstrip('?')}?**:\n\n"
|
||||||
|
|
||||||
for idx, option in enumerate(options[1:]):
|
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])
|
to_react.append(emoji[idx])
|
||||||
|
|
||||||
confirmation_msg += "*Sondage proposé par* " + \
|
confirmation_msg += "*Sondage proposé par* " + \
|
||||||
str(ctx.message.author.mention)
|
str(ctx.message.author.mention)
|
||||||
if time == 0:
|
if time == 0:
|
||||||
confirmation_msg += ""
|
confirmation_msg += ""
|
||||||
else:
|
else:
|
||||||
confirmation_msg += "\n\nVous avez {} secondes pour voter!". \
|
confirmation_msg += f"\n\nVous avez {time} secondes pour voter!"
|
||||||
format(time)
|
|
||||||
poll_msg = await ctx.send(confirmation_msg)
|
poll_msg = await ctx.send(confirmation_msg)
|
||||||
for emote in to_react:
|
for emote in to_react:
|
||||||
await poll_msg.add_reaction(emote)
|
await poll_msg.add_reaction(emote)
|
||||||
|
|
||||||
if time != 0:
|
if time != 0:
|
||||||
await asyncio.sleep(time)
|
await asyncio.sleep(time)
|
||||||
|
|
||||||
if time != 0:
|
|
||||||
async for message in ctx.message.channel.history():
|
async for message in ctx.message.channel.history():
|
||||||
if message.id == poll_msg.id:
|
if message.id == poll_msg.id:
|
||||||
poll_msg = message
|
poll_msg = message
|
||||||
|
|
||||||
results = {}
|
results = {}
|
||||||
|
|
||||||
for reaction in poll_msg.reactions:
|
for reaction in poll_msg.reactions:
|
||||||
if reaction.emoji in to_react:
|
if reaction.emoji in to_react:
|
||||||
results[reaction.emoji] = reaction.count - 1
|
results[reaction.emoji] = reaction.count - 1
|
||||||
end_msg = "Le sondage est términé. Les résultats sont:\n\n"
|
end_msg = "Le sondage est términé. Les résultats sont:\n\n"
|
||||||
|
|
||||||
for result in results:
|
for result in results:
|
||||||
end_msg += "{} {} - {} votes\n". \
|
end_msg += "{} {} - {} votes\n". \
|
||||||
format(result,
|
format(result,
|
||||||
options[emoji.index(result)+1],
|
options[emoji.index(result)+1],
|
||||||
results[result])
|
results[result])
|
||||||
|
|
||||||
top_result = max(results, key=lambda key: results[key])
|
top_result = max(results, key=lambda key: results[key])
|
||||||
|
|
||||||
if len([x for x in results
|
if len([x for x in results
|
||||||
if results[x] == results[top_result]]) > 1:
|
if results[x] == results[top_result]]) > 1:
|
||||||
top_results = []
|
top_results = []
|
||||||
|
|
|
@ -3,7 +3,6 @@ import json
|
||||||
import pytz
|
import pytz
|
||||||
import random
|
import random
|
||||||
import urllib
|
import urllib
|
||||||
import socket
|
|
||||||
|
|
||||||
import discord
|
import discord
|
||||||
import requests
|
import requests
|
||||||
|
@ -178,7 +177,6 @@ class Utility(commands.Cog):
|
||||||
em.set_thumbnail(url = img)
|
em.set_thumbnail(url = img)
|
||||||
await ctx.send(embed=em)
|
await ctx.send(embed=em)
|
||||||
|
|
||||||
|
|
||||||
"""---------------------------------------------------------------------"""
|
"""---------------------------------------------------------------------"""
|
||||||
|
|
||||||
@commands.command()
|
@commands.command()
|
||||||
|
@ -213,8 +211,7 @@ class Utility(commands.Cog):
|
||||||
"""---------------------------------------------------------------------"""
|
"""---------------------------------------------------------------------"""
|
||||||
|
|
||||||
@commands.command(name='iplocalise', pass_context=True)
|
@commands.command(name='iplocalise', pass_context=True)
|
||||||
async def _iplocalise(self, ctx, ipaddress, iptype=""):
|
async def _iplocalise(self, ctx, ipaddress):
|
||||||
realipaddress = ipaddress
|
|
||||||
"""Recup headers."""
|
"""Recup headers."""
|
||||||
if ipaddress.startswith("http://"):
|
if ipaddress.startswith("http://"):
|
||||||
if ipaddress[-1:] == '/':
|
if ipaddress[-1:] == '/':
|
||||||
|
@ -225,39 +222,44 @@ class Utility(commands.Cog):
|
||||||
ipaddress = ipaddress[:-1]
|
ipaddress = ipaddress[:-1]
|
||||||
ipaddress = ipaddress.split("https://")[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..._")
|
iploading = await ctx.send("_réfléchis..._")
|
||||||
ipapi = urllib.request.urlopen("http://ip-api.com/json/" + ipaddress)
|
ipapi = urllib.request.urlopen("http://ip-api.com/json/" + ipaddress)
|
||||||
ipinfo = json.loads(ipapi.read().decode())
|
ipinfo = json.loads(ipapi.read().decode())
|
||||||
|
|
||||||
if ipinfo["status"] != "fail":
|
if ipinfo["status"] != "fail":
|
||||||
if ipinfo['query']:
|
|
||||||
embed = discord.Embed(title=f"Informations pour ``{realipaddress}`` *`({ipinfo['query']})`*", color=0x5858d7)
|
|
||||||
|
|
||||||
if ipinfo['org']:
|
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']:
|
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['country']:
|
||||||
if ipinfo['regionName']:
|
country = ipinfo['country']
|
||||||
regionName = ipinfo['regionName']
|
else:
|
||||||
else:
|
country = 'n/a'
|
||||||
regionName = "N/A"
|
|
||||||
embed.add_field(name="Region :", value=f"{regionName} ({ipinfo['country']})", inline = True)
|
|
||||||
|
|
||||||
|
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")
|
embed.set_thumbnail(url=f"https://www.countryflags.io/{ipinfo['countryCode']}/flat/64.png")
|
||||||
await ctx.send(embed=embed)
|
await ctx.send(embed=embed)
|
||||||
else:
|
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()
|
await iploading.delete()
|
||||||
|
|
||||||
"""---------------------------------------------------------------------"""
|
"""---------------------------------------------------------------------"""
|
||||||
|
@ -310,7 +312,7 @@ class Utility(commands.Cog):
|
||||||
"""Pour voir mon code"""
|
"""Pour voir mon code"""
|
||||||
text = "How tu veux voir mon repos Gitea pour me disséquer ? " \
|
text = "How tu veux voir mon repos Gitea pour me disséquer ? " \
|
||||||
"Pas de soucis ! Je suis un Bot, je ne ressens pas la " \
|
"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 = discord.Embed(title='Repos TuxBot-Bot', description=text, colour=0xE9D460)
|
||||||
em.set_author(name='Gnous', icon_url="https://cdn.discordapp.com/"
|
em.set_author(name='Gnous', icon_url="https://cdn.discordapp.com/"
|
||||||
"icons/280805240977227776/"
|
"icons/280805240977227776/"
|
||||||
|
@ -318,6 +320,41 @@ class Utility(commands.Cog):
|
||||||
".png")
|
".png")
|
||||||
await ctx.send(embed=em)
|
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):
|
def setup(bot):
|
||||||
bot.add_cog(Utility(bot))
|
bot.add_cog(Utility(bot))
|
||||||
|
|
|
@ -2,100 +2,123 @@ from discord.ext import commands
|
||||||
|
|
||||||
|
|
||||||
def is_owner_check(message):
|
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 is_owner(warn=True):
|
||||||
def check(ctx, warn):
|
def check(ctx, log):
|
||||||
owner = is_owner_check(ctx.message)
|
owner = is_owner_check(ctx.message)
|
||||||
if not owner and warn:
|
if not owner and log:
|
||||||
print(ctx.message.author.name + " à essayer d'executer " + ctx.message.content + " sur le serveur " + ctx.message.guild.name)
|
print(ctx.message.author.name + " à essayer d'executer " + ctx.message.content + " sur le serveur " + ctx.message.guild.name)
|
||||||
return owner
|
return owner
|
||||||
|
|
||||||
owner = commands.check(lambda ctx: check(ctx, warn))
|
owner = commands.check(lambda ctx: check(ctx, warn))
|
||||||
return owner
|
return owner
|
||||||
|
|
||||||
|
|
||||||
|
"""-------------------------------------------------------------------------"""
|
||||||
|
|
||||||
"""--------------------------------------------------------------------------------------------------------------------------"""
|
|
||||||
|
|
||||||
async def check_permissions(ctx, perms, *, check=all):
|
async def check_permissions(ctx, perms, *, check=all):
|
||||||
is_owner = await ctx.bot.is_owner(ctx.author)
|
is_owner = await ctx.bot.is_owner(ctx.author)
|
||||||
if is_owner or is_owner_check(ctx.message) == True:
|
if is_owner or is_owner_check(ctx.message) is True:
|
||||||
return 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):
|
def has_permissions(*, check=all, **perms):
|
||||||
async def pred(ctx):
|
async def pred(ctx):
|
||||||
return await check_permissions(ctx, perms, check=check)
|
return await check_permissions(ctx, perms, check=check)
|
||||||
return commands.check(pred)
|
|
||||||
|
return commands.check(pred)
|
||||||
|
|
||||||
|
|
||||||
async def check_guild_permissions(ctx, perms, *, check=all):
|
async def check_guild_permissions(ctx, perms, *, check=all):
|
||||||
is_owner = await ctx.bot.is_owner(ctx.author)
|
is_owner = await ctx.bot.is_owner(ctx.author)
|
||||||
if is_owner:
|
if is_owner:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
if ctx.guild is None:
|
if ctx.guild is None:
|
||||||
return False
|
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):
|
def has_guild_permissions(*, check=all, **perms):
|
||||||
async def pred(ctx):
|
async def pred(ctx):
|
||||||
return await check_guild_permissions(ctx, perms, check=check)
|
return await check_guild_permissions(ctx, perms, check=check)
|
||||||
return commands.check(pred)
|
|
||||||
|
return commands.check(pred)
|
||||||
|
|
||||||
|
|
||||||
# These do not take channel overrides into account
|
# These do not take channel overrides into account
|
||||||
|
|
||||||
|
|
||||||
def is_mod():
|
def is_mod():
|
||||||
async def pred(ctx):
|
async def pred(ctx):
|
||||||
return await check_guild_permissions(ctx, {'manage_guild': True})
|
return await check_guild_permissions(ctx, {'manage_guild': True})
|
||||||
return commands.check(pred)
|
|
||||||
|
return commands.check(pred)
|
||||||
|
|
||||||
|
|
||||||
def is_admin():
|
def is_admin():
|
||||||
async def pred(ctx):
|
async def pred(ctx):
|
||||||
return await check_guild_permissions(ctx, {'administrator': True})
|
return await check_guild_permissions(ctx, {'administrator': True})
|
||||||
return commands.check(pred)
|
|
||||||
|
return commands.check(pred)
|
||||||
|
|
||||||
|
|
||||||
def mod_or_permissions(**perms):
|
def mod_or_permissions(**perms):
|
||||||
perms['manage_guild'] = True
|
perms['manage_guild'] = True
|
||||||
async def predicate(ctx):
|
|
||||||
return await check_guild_permissions(ctx, perms, check=any)
|
async def predicate(ctx):
|
||||||
return commands.check(predicate)
|
return await check_guild_permissions(ctx, perms, check=any)
|
||||||
|
|
||||||
|
return commands.check(predicate)
|
||||||
|
|
||||||
|
|
||||||
def admin_or_permissions(**perms):
|
def admin_or_permissions(**perms):
|
||||||
perms['administrator'] = True
|
perms['administrator'] = True
|
||||||
async def predicate(ctx):
|
|
||||||
return await check_guild_permissions(ctx, perms, check=any)
|
async def predicate(ctx):
|
||||||
return commands.check(predicate)
|
return await check_guild_permissions(ctx, perms, check=any)
|
||||||
|
|
||||||
|
return commands.check(predicate)
|
||||||
|
|
||||||
|
|
||||||
def is_in_guilds(*guild_ids):
|
def is_in_guilds(*guild_ids):
|
||||||
def predicate(ctx):
|
def predicate(ctx):
|
||||||
guild = ctx.guild
|
guild = ctx.guild
|
||||||
if guild is None:
|
if guild is None:
|
||||||
return False
|
return False
|
||||||
return guild.id in guild_ids
|
return guild.id in guild_ids
|
||||||
return commands.check(predicate)
|
|
||||||
|
return commands.check(predicate)
|
||||||
|
|
||||||
def is_lounge_cpp():
|
|
||||||
return is_in_guilds(145079846832308224)
|
|
||||||
|
|
||||||
def get_user(message, user):
|
def get_user(message, user):
|
||||||
try:
|
try:
|
||||||
member = message.mentions[0]
|
member = message.mentions[0]
|
||||||
except:
|
except:
|
||||||
member = message.guild.get_member_named(user)
|
member = message.guild.get_member_named(user)
|
||||||
if not member:
|
if not member:
|
||||||
try:
|
try:
|
||||||
member = message.guild.get_member(int(user))
|
member = message.guild.get_member(int(user))
|
||||||
except ValueError:
|
except ValueError:
|
||||||
pass
|
pass
|
||||||
if not member:
|
if not member:
|
||||||
return None
|
return None
|
||||||
return member
|
return member
|
||||||
|
|
||||||
|
|
||||||
def check_date(date: str):
|
def check_date(date: str):
|
||||||
if len(date) == 1:
|
if len(date) == 1:
|
||||||
return f"0{date}"
|
return f"0{date}"
|
||||||
else:
|
else:
|
||||||
return date
|
return date
|
||||||
|
|
|
@ -16,6 +16,7 @@ class text_colors:
|
||||||
LIGHT_CYAN = '\033[96m'
|
LIGHT_CYAN = '\033[96m'
|
||||||
WHITE = '\033[97m'
|
WHITE = '\033[97m'
|
||||||
|
|
||||||
|
|
||||||
class bg_colors:
|
class bg_colors:
|
||||||
BLACK = '\033[40m'
|
BLACK = '\033[40m'
|
||||||
RED = '\033[41m'
|
RED = '\033[41m'
|
||||||
|
@ -34,10 +35,12 @@ class bg_colors:
|
||||||
LIGHT_CYAN = '\033[106m'
|
LIGHT_CYAN = '\033[106m'
|
||||||
WHITE = '\033[107m'
|
WHITE = '\033[107m'
|
||||||
|
|
||||||
|
|
||||||
class text_style:
|
class text_style:
|
||||||
BOLD = '\033[1m'
|
BOLD = '\033[1m'
|
||||||
DIM = '\033[2m'
|
DIM = '\033[2m'
|
||||||
UNDERLINE = '\033[4m'
|
UNDERLINE = '\033[4m'
|
||||||
BLINK = '\033[5m'
|
BLINK = '\033[5m'
|
||||||
|
|
||||||
|
|
||||||
ENDC = '\033[0m'
|
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"
|
token = "INSERT TOKEN HERE"
|
||||||
client_id = "INSERT_CLIENT_ID_HERE"
|
client_id = <INSERT_CLIENT_ID_HERE (in int)>
|
||||||
log_channel_id = "INSERT_LOG_CHANNEL_HERE"
|
log_channel_id = <INSERT_LOG_CHANNEL_HERE (in int)>
|
||||||
main_server_id = "INSERT_MAIN_CHANNEL_ID_HERE"
|
main_server_id = <INSERT_MAIN_CHANNEL_ID_HERE (in int)>
|
||||||
|
|
||||||
game = "PLAYING_GAME_HERE"
|
game = "PLAYING_GAME_HERE"
|
||||||
prefix = ["."]
|
prefix = ["."]
|
||||||
|
@ -17,10 +17,4 @@ mysql = {
|
||||||
}
|
}
|
||||||
|
|
||||||
authorized_id = ['admin ids here']
|
authorized_id = ['admin ids here']
|
||||||
|
unkickable_id = ['unkickable ids here']
|
||||||
## Passport settings
|
|
||||||
|
|
||||||
fonts = {
|
|
||||||
"normal": "NotoSansCJK-Regular.ttc",
|
|
||||||
"bold": "NotoSansCJK-Bold.ttc"
|
|
||||||
}
|
|
||||||
|
|
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. --
|
|