import discord from discord.ext import commands from discord import app_commands import sqlite3 import aiohttp import io import os from PIL import Image, ImageDraw, ImageFont from datetime import datetime, timezone class Goodbye(commands.Cog): def __init__(self, bot): self.bot = bot self.db_path = os.path.join(os.path.dirname(__file__), "..", "config.db") self._init_db() def _init_db(self): conn = sqlite3.connect(self.db_path) cursor = conn.cursor() # On s'assure que la table existe au cas où (normalement gérée par welcome.py) cursor.execute(''' CREATE TABLE IF NOT EXISTS server_configs ( server_id INTEGER PRIMARY KEY, welcome_channel INTEGER, goodbye_channel INTEGER, welcome_dm_message TEXT, welcome_server_name TEXT, welcome_banner_background TEXT, welcome_channel_message TEXT, goodbye_server_name TEXT, goodbye_message TEXT, goodbye_banner_background TEXT ) ''') # Migration pour ajouter les colonnes si elles n'existent pas cursor.execute("PRAGMA table_info(server_configs)") columns = [col[1] for col in cursor.fetchall()] if 'goodbye_server_name' not in columns: cursor.execute('ALTER TABLE server_configs ADD COLUMN goodbye_server_name TEXT') if 'goodbye_message' not in columns: cursor.execute('ALTER TABLE server_configs ADD COLUMN goodbye_message TEXT') if 'goodbye_banner_background' not in columns: cursor.execute('ALTER TABLE server_configs ADD COLUMN goodbye_banner_background TEXT') conn.commit() conn.close() def humanize_time_delta(self, date): if date is None: return "date inconnue" if date.tzinfo is None: date = date.replace(tzinfo=timezone.utc) now = datetime.now(timezone.utc) delta = now - date total_days = delta.total_seconds() / (3600 * 24) rounded_days = round(total_days) years = rounded_days // 365 months = rounded_days // 30 weeks = rounded_days // 7 days = rounded_days if years > 0: return f"il y a {years} {'an' if years == 1 else 'ans'}" elif months > 0: return f"il y a {months} mois" elif weeks > 0: return f"il y a {weeks} {'semaine' if weeks == 1 else 'semaines'}" elif days > 0: return f"il y a {days} {'jour' if days == 1 else 'jours'}" elif delta.seconds // 3600 > 0: hours = delta.seconds // 3600 return f"il y a {hours} {'heure' if hours == 1 else 'heures'}" elif (delta.seconds % 3600) // 60 > 0: minutes = (delta.seconds % 3600) // 60 return f"il y a {minutes} {'minute' if minutes == 1 else 'minutes'}" else: seconds = delta.seconds % 60 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 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: 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.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)) 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 @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() 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: 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 " embed = discord.Embed(title=f"{member} viens de nous quitter.", description=description, color=discord.Color.dark_magenta()) embed.set_footer(text=f"Avait rejoint {joined_message}") embed.set_image(url="attachment://goodbye.png") await channel.send(embed=embed, file=discord.File(img, filename="goodbye.png")) async def save_banner_attachment(self, attachment: discord.Attachment, guild_id: int) -> str: """Sauvegarde l'attachment et retourne le chemin du fichier""" base_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "banners")) os.makedirs(base_dir, exist_ok=True) # Vérifier que c'est une image if not attachment.content_type or not attachment.content_type.startswith('image/'): raise ValueError("Le fichier doit être une image.") # Vérifier le format PNG if not attachment.filename.lower().endswith('.png'): raise ValueError("Le fichier doit être au format PNG.") # Vérifier les dimensions banner_path = os.path.join(base_dir, f"banner_goodbye_{guild_id}.png") async with aiohttp.ClientSession() as session: async with session.get(attachment.url) as resp: if resp.status == 200: # Vérifier les dimensions avant de sauvegarder img_data = await resp.read() from io import BytesIO with Image.open(BytesIO(img_data)) as img: width, height = img.size if width != 1000 or height != 300: raise ValueError(f"Les dimensions doivent être 1000x300 pixels (votre image: {width}x{height})") with open(banner_path, 'wb') as f: f.write(img_data) return os.path.basename(banner_path) @app_commands.command(name="setgoodbye", description="Définit le salon d'aurevoir et les paramètres personnalisés") @app_commands.checks.has_permissions(administrator=True) async def set_goodbye(self, interaction: discord.Interaction, channel: discord.TextChannel, server_name: str = None, message: str = None, banner_file: discord.Attachment = None): banner_path = 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() 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." await interaction.response.send_message(msg, ephemeral=True) async def setup(bot): await bot.add_cog(Goodbye(bot))