Fix système arriver départ V1
This commit is contained in:
parent
01dd4979d8
commit
d9afa32828
18 changed files with 1367 additions and 291 deletions
|
|
@ -4,13 +4,18 @@ import sqlite3
|
|||
import aiohttp
|
||||
import io
|
||||
import os
|
||||
import logging
|
||||
from PIL import Image, ImageDraw, ImageFont
|
||||
from datetime import datetime, timezone
|
||||
|
||||
kuby_logger = logging.getLogger("KubyBot")
|
||||
|
||||
class Goodbye(commands.Cog):
|
||||
def __init__(self, bot):
|
||||
self.bot = bot
|
||||
self.db_path = os.path.join(os.path.dirname(__file__), "..", "config.db")
|
||||
# Chemin absolu vers la base de données
|
||||
self.base_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
|
||||
self.db_path = os.path.join(self.base_dir, "config.db")
|
||||
self._init_db()
|
||||
|
||||
def _init_db(self):
|
||||
|
|
@ -77,86 +82,139 @@ class Goodbye(commands.Cog):
|
|||
return f"il y a {seconds} {'seconde' if seconds <= 1 else 'secondes'}"
|
||||
|
||||
async def create_goodbye_image(self, username, avatar_url, server_name=None, banner_background=None):
|
||||
current_dir = os.path.dirname(__file__)
|
||||
base_dir = os.path.abspath(os.path.join(current_dir, ".."))
|
||||
|
||||
async with aiohttp.ClientSession() as session:
|
||||
async with session.get(avatar_url) as resp:
|
||||
avatar_bytes = await resp.read()
|
||||
# Utiliser base_dir déjà calculé dans __init__
|
||||
try:
|
||||
# Télécharger l'avatar
|
||||
async with aiohttp.ClientSession() as session:
|
||||
async with session.get(avatar_url) as resp:
|
||||
if resp.status != 200:
|
||||
kuby_logger.error(f"[Goodbye] Impossible de télécharger l'avatar: {resp.status}")
|
||||
raise ValueError(f"Impossible de télécharger l'avatar (code: {resp.status})")
|
||||
avatar_bytes = await resp.read()
|
||||
|
||||
# Utiliser le fond personnalisé ou celui par défaut
|
||||
bg_path = None
|
||||
if banner_background:
|
||||
# On cherche dans le dossier banners/
|
||||
potential_path = os.path.join(base_dir, "banners", banner_background)
|
||||
if os.path.exists(potential_path):
|
||||
bg_path = potential_path
|
||||
|
||||
if bg_path:
|
||||
# Vérifier et utiliser le fond personnalisé ou celui par défaut
|
||||
bg_path = None
|
||||
if banner_background:
|
||||
potential_path = os.path.join(self.base_dir, "banners", banner_background)
|
||||
if os.path.exists(potential_path):
|
||||
bg_path = potential_path
|
||||
kuby_logger.debug(f"[Goodbye] Fond personnalisé trouvé: {bg_path}")
|
||||
|
||||
if not bg_path:
|
||||
default_bg = os.path.join(self.base_dir, 'background_template_goodbye.png')
|
||||
if not os.path.exists(default_bg):
|
||||
kuby_logger.error(f"[Goodbye] Fond par défaut introuvable: {default_bg}")
|
||||
raise FileNotFoundError(f"Fond par défaut introuvable: {default_bg}")
|
||||
bg_path = default_bg
|
||||
kuby_logger.debug(f"[Goodbye] Utilisation du fond par défaut: {bg_path}")
|
||||
|
||||
# Charger le fond
|
||||
background = Image.open(bg_path)
|
||||
else:
|
||||
background = Image.open(os.path.join(base_dir, 'background_template_goodbye.png'))
|
||||
draw = ImageDraw.Draw(background)
|
||||
|
||||
font_large = ImageFont.truetype(os.path.join(base_dir, 'custom_font_large.otf'), 70)
|
||||
font_small = ImageFont.truetype(os.path.join(base_dir, 'custom_font_small.otf'), 50)
|
||||
font_large_omega = ImageFont.truetype(os.path.join(base_dir, 'custom_font_large.otf'), 55)
|
||||
draw = ImageDraw.Draw(background)
|
||||
|
||||
# Vérifier et charger les polices
|
||||
font_large_path = os.path.join(self.base_dir, 'custom_font_large.otf')
|
||||
font_small_path = os.path.join(self.base_dir, 'custom_font_small.otf')
|
||||
|
||||
for font_path in [font_large_path, font_small_path]:
|
||||
if not os.path.exists(font_path):
|
||||
kuby_logger.error(f"[Goodbye] Police introuvable: {font_path}")
|
||||
raise FileNotFoundError(f"Police introuvable: {font_path}")
|
||||
|
||||
font_large = ImageFont.truetype(font_large_path, 70)
|
||||
font_small = ImageFont.truetype(font_small_path, 50)
|
||||
|
||||
# Dessiner uniquement le nom du serveur (comme pour welcome)
|
||||
display_name = server_name if server_name else "OMEGA KUBE"
|
||||
draw.text((325, 135), display_name, font=font_large, fill=(255,255,255))
|
||||
|
||||
draw.text((325, 70), "A Bientot", font=font_large, fill=(255,255,255))
|
||||
draw.text((325, 135), f"{username} chez", font=font_small, fill=(255,255,255))
|
||||
|
||||
# Afficher le nom du serveur personnalisé s'il existe, sinon OMEGA KUBE
|
||||
display_name = server_name if server_name else "OMEGA KUBE"
|
||||
draw.text((325, 195), display_name, font=font_large_omega, fill=(255,255,255))
|
||||
# Ajouter l'avatar
|
||||
avatar = Image.open(io.BytesIO(avatar_bytes)).convert("RGBA").resize((224, 224), Image.LANCZOS)
|
||||
mask = Image.new('L', avatar.size, 0)
|
||||
mask_draw = ImageDraw.Draw(mask)
|
||||
mask_draw.ellipse((12, 12, 212, 212), fill=255)
|
||||
background.paste(avatar, (40, 40), mask)
|
||||
|
||||
avatar = Image.open(io.BytesIO(avatar_bytes)).convert("RGBA").resize((224, 224), Image.LANCZOS)
|
||||
mask = Image.new('L', avatar.size, 0)
|
||||
mask_draw = ImageDraw.Draw(mask)
|
||||
mask_draw.ellipse((12, 12, 212, 212), fill=255)
|
||||
background.paste(avatar, (40, 40), mask)
|
||||
|
||||
img_byte_arr = io.BytesIO()
|
||||
background.save(img_byte_arr, format='PNG')
|
||||
img_byte_arr.seek(0)
|
||||
return img_byte_arr
|
||||
# Retourner l'image
|
||||
img_byte_arr = io.BytesIO()
|
||||
background.save(img_byte_arr, format='PNG')
|
||||
img_byte_arr.seek(0)
|
||||
return img_byte_arr
|
||||
|
||||
except Exception as e:
|
||||
kuby_logger.error(f"[Goodbye] Erreur lors de la création de l'image: {e}", exc_info=True)
|
||||
return None
|
||||
|
||||
@commands.Cog.listener()
|
||||
async def on_member_remove(self, member):
|
||||
conn = sqlite3.connect(self.db_path)
|
||||
cursor = conn.cursor()
|
||||
cursor.execute('SELECT goodbye_channel, goodbye_server_name, goodbye_message, goodbye_banner_background FROM server_configs WHERE server_id = ?', (member.guild.id,))
|
||||
result = cursor.fetchone()
|
||||
conn.close()
|
||||
kuby_logger.info(f"[Goodbye] Départ détecté: {member.name} (ID: {member.id}) du serveur {member.guild.name}")
|
||||
|
||||
try:
|
||||
conn = sqlite3.connect(self.db_path)
|
||||
cursor = conn.cursor()
|
||||
cursor.execute('SELECT goodbye_channel, goodbye_server_name, goodbye_message, goodbye_banner_background FROM server_configs WHERE server_id = ?', (member.guild.id,))
|
||||
result = cursor.fetchone()
|
||||
conn.close()
|
||||
|
||||
if not result or not result[0]:
|
||||
kuby_logger.debug(f"[Goodbye] Aucune configuration de départ pour le serveur {member.guild.id}")
|
||||
return
|
||||
|
||||
if result and result[0]:
|
||||
goodbye_channel = result[0]
|
||||
server_name = result[1]
|
||||
custom_message = result[2]
|
||||
banner_background = result[3]
|
||||
|
||||
channel = member.guild.get_channel(goodbye_channel)
|
||||
if channel:
|
||||
if not channel:
|
||||
kuby_logger.warning(f"[Goodbye] Salon de départ introuvable (ID: {goodbye_channel}) pour {member.guild.name}")
|
||||
return
|
||||
|
||||
joined_message = self.humanize_time_delta(member.joined_at)
|
||||
|
||||
try:
|
||||
img = await self.create_goodbye_image(
|
||||
member.name,
|
||||
member.avatar.url if member.avatar else member.default_avatar.url,
|
||||
server_name=server_name,
|
||||
banner_background=banner_background
|
||||
)
|
||||
joined_message = self.humanize_time_delta(member.joined_at)
|
||||
|
||||
# Utiliser le message personnalisé ou un par défaut
|
||||
description = custom_message if custom_message else "Nous espérons vous revoir bientôt <a:rain:1317973615714504806>"
|
||||
# Envoyer avec image si disponible
|
||||
display_name = server_name if server_name else member.guild.name
|
||||
default_message = "Nous espérons vous revoir bientôt <a:rain:1317973615714504806>"
|
||||
message_text = custom_message if custom_message else default_message
|
||||
|
||||
components = [
|
||||
disnake.ui.Container(
|
||||
disnake.ui.TextDisplay(f"{member} viens de nous quitter."),
|
||||
disnake.ui.Separator(divider=True),
|
||||
disnake.ui.TextDisplay(description),
|
||||
disnake.ui.TextDisplay(f"⌛ **Avait rejoint :** {joined_message}"),
|
||||
disnake.ui.MediaGallery(disnake.MediaGalleryItem(url="attachment://goodbye.png"))
|
||||
if img:
|
||||
embed = disnake.Embed(
|
||||
description=f"{member.mention} vient de nous quitter.\n\n{message_text}\n\n⌛ **Avait rejoint :** {joined_message}",
|
||||
color=0x7289DA
|
||||
)
|
||||
]
|
||||
await channel.send(components=components, file=disnake.File(img, filename="goodbye.png"))
|
||||
embed.set_image(url="attachment://goodbye.png")
|
||||
await channel.send(embed=embed, file=disnake.File(img, filename="goodbye.png"))
|
||||
kuby_logger.info(f"[Goodbye] Message de départ avec image envoyé pour {member.name}")
|
||||
else:
|
||||
# Fallback : image non générée
|
||||
kuby_logger.warning(f"[Goodbye] Image non générée pour {member.name}, envoi d'un message texte")
|
||||
embed = disnake.Embed(
|
||||
description=f"{member.mention} vient de nous quitter.\n\n{message_text}\n\n⌛ **Avait rejoint :** {joined_message}",
|
||||
color=0x7289DA
|
||||
)
|
||||
await channel.send(embed=embed)
|
||||
|
||||
except Exception as e:
|
||||
kuby_logger.error(f"[Goodbye] Erreur lors de la création ou de l'envoi du message: {e}", exc_info=True)
|
||||
# Fallback avec un message minimal
|
||||
try:
|
||||
message_text_fb = custom_message if custom_message else "Nous espérons vous revoir bientôt"
|
||||
await channel.send(f"{member.mention} vient de nous quitter. {message_text_fb}")
|
||||
kuby_logger.info(f"[Goodbye] Message de fallback envoyé pour {member.name}")
|
||||
except Exception as e2:
|
||||
kuby_logger.error(f"[Goodbye] Échec du message de fallback: {e2}", exc_info=True)
|
||||
except sqlite3.Error as e:
|
||||
kuby_logger.error(f"[Goodbye] Erreur SQLite: {e}", exc_info=True)
|
||||
except Exception as e:
|
||||
kuby_logger.error(f"[Goodbye] Erreur inattendue dans on_member_remove: {e}", exc_info=True)
|
||||
|
||||
async def save_banner_attachment(self, attachment: disnake.Attachment, guild_id: int) -> str:
|
||||
"""Sauvegarde l'attachment et retourne le chemin du fichier"""
|
||||
|
|
@ -190,42 +248,47 @@ class Goodbye(commands.Cog):
|
|||
|
||||
return os.path.basename(banner_path)
|
||||
|
||||
@commands.slash_command(name="setgoodbye", description="Définit le salon d'aurevoir et les paramètres personnalisés")
|
||||
@commands.slash_command(name="setgoodbye", description="Définit le salon d'au revoir et les paramètres personnalisés")
|
||||
@commands.has_permissions(administrator=True)
|
||||
async def set_goodbye(self, interaction: disnake.ApplicationCommandInteraction, channel: disnake.TextChannel, server_name: str = None, message: str = None, banner_file: disnake.Attachment = None):
|
||||
# ✅ DÉFÉRER LA RÉPONSE (le traitement peut prendre > 3s avec les images)
|
||||
await interaction.response.defer(ephemeral=True)
|
||||
|
||||
banner_path = None
|
||||
error_message = None
|
||||
|
||||
# Si un fichier est uploadé, le sauvegarder
|
||||
if banner_file:
|
||||
try:
|
||||
banner_path = await self.save_banner_attachment(banner_file, interaction.guild_id)
|
||||
except Exception as e:
|
||||
await interaction.response.send_message(f"❌ Erreur avec le fichier: {e}", ephemeral=True)
|
||||
return
|
||||
|
||||
conn = sqlite3.connect(self.db_path)
|
||||
cursor = conn.cursor()
|
||||
cursor.execute('''INSERT INTO server_configs (server_id, goodbye_channel, goodbye_server_name, goodbye_message, goodbye_banner_background)
|
||||
VALUES (?, ?, ?, ?, ?) ON CONFLICT(server_id)
|
||||
DO UPDATE SET goodbye_channel=excluded.goodbye_channel,
|
||||
goodbye_server_name=COALESCE(excluded.goodbye_server_name, goodbye_server_name),
|
||||
goodbye_message=COALESCE(excluded.goodbye_message, goodbye_message),
|
||||
goodbye_banner_background=COALESCE(excluded.goodbye_banner_background, goodbye_banner_background)''',
|
||||
(interaction.guild_id, channel.id, server_name, message, banner_path))
|
||||
conn.commit()
|
||||
conn.close()
|
||||
error_message = f"❌ Erreur avec le fichier: {e}"
|
||||
|
||||
msg = f"✅ Salon d'aurevoir défini sur {channel.mention}"
|
||||
if server_name: msg += f"\nNom du serveur: {server_name}"
|
||||
if message: msg += f"\nMessage: {message}"
|
||||
if banner_path: msg += f"\nBannière personnalisée activée."
|
||||
# Si pas d'erreur, sauvegarder en base de données
|
||||
if not error_message:
|
||||
try:
|
||||
conn = sqlite3.connect(self.db_path)
|
||||
cursor = conn.cursor()
|
||||
cursor.execute('''INSERT INTO server_configs (server_id, goodbye_channel, goodbye_server_name, goodbye_message, goodbye_banner_background)
|
||||
VALUES (?, ?, ?, ?, ?) ON CONFLICT(server_id)
|
||||
DO UPDATE SET goodbye_channel=excluded.goodbye_channel,
|
||||
goodbye_server_name=COALESCE(excluded.goodbye_server_name, goodbye_server_name),
|
||||
goodbye_message=COALESCE(excluded.goodbye_message, goodbye_message),
|
||||
goodbye_banner_background=COALESCE(excluded.goodbye_banner_background, goodbye_banner_background)''',
|
||||
(interaction.guild_id, channel.id, server_name, message, banner_path))
|
||||
conn.commit()
|
||||
conn.close()
|
||||
|
||||
msg = f"✅ Salon d'au revoir défini sur {channel.mention}"
|
||||
if server_name: msg += f"\nNom du serveur: {server_name}"
|
||||
if message: msg += f"\nMessage: {message}"
|
||||
if banner_path: msg += f"\nBannière personnalisée activée."
|
||||
error_message = msg
|
||||
except Exception as e:
|
||||
error_message = f"❌ Erreur lors de la sauvegarde en base de données: {e}"
|
||||
|
||||
components = [
|
||||
disnake.ui.Container(
|
||||
disnake.ui.TextDisplay(msg)
|
||||
)
|
||||
]
|
||||
await interaction.response.send_message(components=components, ephemeral=True)
|
||||
# Envoyer la réponse finale (déferrée)
|
||||
await interaction.followup.send(error_message, ephemeral=True)
|
||||
|
||||
def setup(bot):
|
||||
bot.add_cog(Goodbye(bot))
|
||||
Loading…
Add table
Add a link
Reference in a new issue