2026-01-01 18:48:25 +01:00
import discord
from discord . ext import commands
from discord import app_commands
import sqlite3
import aiohttp
import io
import os
import logging
from PIL import Image , ImageDraw , ImageFont
2026-02-08 14:42:40 +01:00
kuby_logger = logging . getLogger ( " KubyBot " )
2026-01-01 18:48:25 +01:00
class Welcome ( commands . Cog ) :
def __init__ ( self , bot ) :
self . bot = bot
# Base de données à la racine
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 ( )
cursor . execute ( '''
CREATE TABLE IF NOT EXISTS server_configs (
server_id INTEGER PRIMARY KEY ,
welcome_channel INTEGER ,
goodbye_channel INTEGER ,
2026-01-03 22:13:37 +01:00
welcome_dm_message TEXT ,
welcome_server_name TEXT ,
welcome_banner_background TEXT ,
welcome_channel_message TEXT
2026-01-01 18:48:25 +01:00
)
''' )
2026-01-03 22:13:37 +01:00
# 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 ' welcome_server_name ' not in columns :
cursor . execute ( ' ALTER TABLE server_configs ADD COLUMN welcome_server_name TEXT ' )
if ' welcome_banner_background ' not in columns :
cursor . execute ( ' ALTER TABLE server_configs ADD COLUMN welcome_banner_background TEXT ' )
if ' welcome_channel_message ' not in columns :
cursor . execute ( ' ALTER TABLE server_configs ADD COLUMN welcome_channel_message TEXT ' )
2026-02-20 13:44:07 +01:00
# Migration: Conversion des chemins absolus en chemins relatifs pour les bannières
cursor . execute ( " SELECT server_id, welcome_banner_background FROM server_configs WHERE welcome_banner_background IS NOT NULL " )
for server_id , path in cursor . fetchall ( ) :
if path and os . path . isabs ( path ) :
filename = os . path . basename ( path )
# Vérifier si c'est bien une bannière dans le dossier banners
if " banner_ " in filename :
cursor . execute ( " UPDATE server_configs SET welcome_banner_background = ? WHERE server_id = ? " , ( filename , server_id ) )
2026-01-01 18:48:25 +01:00
conn . commit ( )
conn . close ( )
2026-01-03 22:13:37 +01:00
async def create_welcome_image ( self , username , avatar_url , server_name = None , banner_background = None ) :
2026-01-01 18:48:25 +01:00
current_dir = os . path . dirname ( __file__ )
base_dir = os . path . abspath ( os . path . join ( current_dir , " .. " ) )
try :
async with aiohttp . ClientSession ( ) as session :
async with session . get ( avatar_url ) as resp :
avatar_bytes = await resp . read ( )
2026-01-03 22:13:37 +01:00
# Utiliser le fond personnalisé ou celui par défaut
2026-02-20 13:44:07 +01:00
bg_path = None
if banner_background :
# Si c'est un chemin absolu (pour compatibilité temporaire)
if os . path . isabs ( banner_background ) and os . path . exists ( banner_background ) :
bg_path = banner_background
else :
# Sinon 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 )
2026-01-03 22:13:37 +01:00
else :
background = Image . open ( os . path . join ( base_dir , ' background_template.png ' ) )
2026-01-01 18:48:25 +01:00
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 )
draw . text ( ( 325 , 70 ) , " Bienvenue " , font = font_large , fill = ( 255 , 255 , 255 ) )
2026-01-03 22:13:37 +01:00
draw . text ( ( 325 , 135 ) , f " { username } " , font = font_small , fill = ( 255 , 255 , 255 ) )
# Afficher le nom du serveur uniquement si configuré
if server_name :
font_server = ImageFont . truetype ( os . path . join ( base_dir , ' custom_font_large.otf ' ) , 55 )
draw . text ( ( 325 , 195 ) , server_name , font = font_server , fill = ( 255 , 255 , 255 ) )
2026-01-01 18:48:25 +01:00
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
except Exception as e :
logging . error ( f " Erreur image: { e } " )
return None
@commands.Cog.listener ( )
async def on_member_join ( self , member ) :
2026-02-08 14:42:40 +01:00
kuby_logger . info ( f " 📥 [Welcome] Événement on_member_join reçu pour { member . name } (ID: { member . id } ) sur { member . guild . name } " )
2026-01-01 18:48:25 +01:00
2026-02-08 14:42:40 +01:00
try :
conn = sqlite3 . connect ( self . db_path )
cursor = conn . cursor ( )
cursor . execute ( ' SELECT welcome_channel, welcome_dm_message, welcome_server_name, welcome_banner_background, welcome_channel_message FROM server_configs WHERE server_id = ? ' , ( member . guild . id , ) )
result = cursor . fetchone ( )
conn . close ( )
if not result :
kuby_logger . warning ( f " ⚠️ [Welcome] Aucune configuration trouvée dans la base de données pour le serveur { member . guild . id } " )
return
welcome_channel = result [ 0 ]
welcome_dm_text = result [ 1 ]
server_name = result [ 2 ]
banner_background = result [ 3 ]
welcome_channel_message = result [ 4 ]
kuby_logger . debug ( f " 🔍 [Welcome] Config récupérée: DM= { bool ( welcome_dm_text ) } , Channel= { welcome_channel } " )
# 1. Gestion du DM
if welcome_dm_text :
kuby_logger . info ( f " ✉️ [Welcome] Tentative d ' envoi de DM à { member . name } " )
try :
formatted_message = welcome_dm_text . format (
username = member . name ,
servername = member . guild . name ,
member_count = member . guild . member_count
)
await member . send ( formatted_message )
await member . send ( " https://i.giphy.com/media/v1.Y2lkPTc5MGI3NjExNXV1NW02NTk0bGF2ZHcyMGFna2F6amRrbGpobGpsMHowOW5xa2E5eCZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/ZtFuW4rsUbfrRzPauJ/giphy.gif " )
kuby_logger . info ( f " ✅ [Welcome] DM envoyé avec succès à { member . name } " )
except discord . Forbidden :
kuby_logger . warning ( f " 🚫 [Welcome] Impossible d ' envoyer le DM à { member . name } (DMs fermés) " )
except Exception as e :
kuby_logger . error ( f " ❌ [Welcome] Erreur lors de l ' envoi du DM à { member . name } : { e } " , exc_info = True )
else :
kuby_logger . info ( f " ℹ ️ [Welcome] Aucun message de DM configuré pour le serveur { member . guild . id } " )
except Exception as e :
kuby_logger . error ( f " ❌ [Welcome] Erreur critique dans on_member_join: { e } " , exc_info = True )
2026-01-01 18:48:25 +01:00
2026-01-03 22:13:37 +01:00
# 2. Gestion du salon de bienvenue
if welcome_channel :
channel = member . guild . get_channel ( welcome_channel )
2026-01-01 18:48:25 +01:00
if channel :
2026-01-03 22:13:37 +01:00
img = await self . create_welcome_image (
member . name ,
member . avatar . url if member . avatar else member . default_avatar . url ,
server_name = server_name ,
banner_background = banner_background
)
2026-01-01 18:48:25 +01:00
if img :
2026-01-03 22:13:37 +01:00
# Utiliser le message personnalisé ou un message par défaut
channel_description = welcome_channel_message . format (
username = member . name ,
servername = member . guild . name ,
member_count = member . guild . member_count
) if welcome_channel_message else " Bienvenue dans le serveur ! \n Si vous cherchez des RolePlay de qualité, \n Vous êtes au bon endroit. <a:sip:1316891821858619452> "
2026-01-01 18:48:25 +01:00
embed = discord . Embed ( title = f " Merci d ' accueillir { member } 🤝 " ,
2026-01-03 22:13:37 +01:00
description = channel_description ,
2026-01-01 18:48:25 +01:00
color = discord . Color . dark_purple ( ) )
embed . set_image ( url = " attachment://welcome.png " )
await channel . send ( content = member . mention , embed = embed , file = discord . File ( img , filename = " welcome.png " ) )
2026-01-03 22:13:37 +01:00
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_ { 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 )
2026-02-20 13:44:07 +01:00
return os . path . basename ( banner_path )
2026-01-03 22:13:37 +01:00
@app_commands.command ( name = " setwelcome " , description = " Définit le salon, les messages de bienvenue et le fond de la bannière " )
2026-01-01 18:48:25 +01:00
@app_commands.checks.has_permissions ( administrator = True )
2026-01-03 22:13:37 +01:00
async def set_welcome ( self , interaction : discord . Interaction , channel : discord . TextChannel , message_dm : str , message_salon : str , server_name : str , 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
2026-01-01 18:48:25 +01:00
conn = sqlite3 . connect ( self . db_path )
cursor = conn . cursor ( )
2026-01-03 22:13:37 +01:00
cursor . execute ( ''' INSERT INTO server_configs (server_id, welcome_channel, welcome_dm_message, welcome_server_name, welcome_banner_background, welcome_channel_message)
VALUES ( ? , ? , ? , ? , ? , ? ) ON CONFLICT ( server_id )
2026-01-01 18:48:25 +01:00
DO UPDATE SET welcome_channel = excluded . welcome_channel ,
2026-01-03 22:13:37 +01:00
welcome_dm_message = excluded . welcome_dm_message ,
welcome_server_name = excluded . welcome_server_name ,
welcome_banner_background = COALESCE ( excluded . welcome_banner_background , welcome_banner_background ) ,
welcome_channel_message = excluded . welcome_channel_message ''' ,
( interaction . guild_id , channel . id , message_dm , server_name , banner_path , message_salon ) )
2026-01-01 18:48:25 +01:00
conn . commit ( )
conn . close ( )
2026-01-03 22:13:37 +01:00
bg_used = " background_template.png " if not banner_path else ( banner_path . split ( ' / ' ) [ - 1 ] )
await interaction . response . send_message ( f " ✅ Configuration de bienvenida enregistrée ! \n Fond utilisé: { bg_used } " , ephemeral = True )
2026-01-01 18:48:25 +01:00
async def setup ( bot ) :
2026-01-03 22:13:37 +01:00
await bot . add_cog ( Welcome ( bot ) )