2026-02-07 14:42:39 +01:00
import discord
from discord . ext import commands
2026-03-14 15:40:19 +01:00
from discord import app_commands , ui
import json
import os
from typing import Optional
TICKET_WHITELIST_SETTINGS_FILE = " data/ticket_whitelist_settings.json "
def load_settings ( guild_id : int ) - > dict :
""" Charge les paramètres de ticket pour un serveur """
if not os . path . exists ( TICKET_WHITELIST_SETTINGS_FILE ) :
return { }
try :
with open ( TICKET_WHITELIST_SETTINGS_FILE , " r " , encoding = ' utf-8 ' ) as f :
return json . load ( f ) . get ( str ( guild_id ) , { } )
except ( json . JSONDecodeError , FileNotFoundError ) :
return { }
def save_settings ( guild_id : int , settings : dict ) - > None :
""" Sauvegarde les paramètres de ticket """
data = { }
if os . path . exists ( TICKET_WHITELIST_SETTINGS_FILE ) :
try :
with open ( TICKET_WHITELIST_SETTINGS_FILE , " r " , encoding = ' utf-8 ' ) as f :
data = json . load ( f )
except ( json . JSONDecodeError , FileNotFoundError ) :
pass
data [ str ( guild_id ) ] = settings
os . makedirs ( os . path . dirname ( TICKET_WHITELIST_SETTINGS_FILE ) , exist_ok = True )
with open ( TICKET_WHITELIST_SETTINGS_FILE , " w " , encoding = ' utf-8 ' ) as f :
json . dump ( data , f , indent = 4 , ensure_ascii = False )
class TicketWhitelistConfigView ( ui . View ) :
def __init__ ( self , guild_id : int ) :
super ( ) . __init__ ( timeout = 60 )
self . guild_id = guild_id
async def prompt_for_id ( self , interaction : discord . Interaction , prompt_text : str , is_category : bool = False ) :
await interaction . response . send_message ( prompt_text , ephemeral = True )
def check ( m ) :
return m . author == interaction . user and m . channel == interaction . channel
try :
# We use an explicit import here to avoid clashing if __import__ syntax is weird, but using standard asyncio module is fine if we import it at top. Let's add import asyncio at the top instead!
pass
except __import__ ( " asyncio " ) . TimeoutError :
pass
try :
msg = await interaction . client . wait_for ( ' message ' , check = check , timeout = 30 )
target = None
if is_category :
if msg . content . isdigit ( ) :
target = discord . utils . get ( interaction . guild . categories , id = int ( msg . content ) )
if not target :
await interaction . followup . send ( " ❌ Catégorie introuvable avec cet ID. " , ephemeral = True )
return None
else :
await interaction . followup . send ( " ❌ Veuillez envoyer l ' ID (les chiffres) de la catégorie. " , ephemeral = True )
return None
else :
if msg . role_mentions :
target = msg . role_mentions [ 0 ]
elif msg . content . isdigit ( ) :
target = interaction . guild . get_role ( int ( msg . content ) )
if not target :
await interaction . followup . send ( " ❌ ID ou mention invalide. " , ephemeral = True )
return None
try : await msg . delete ( )
except : pass
return target
except __import__ ( " asyncio " ) . TimeoutError :
await interaction . followup . send ( " ❌ Temps écoulé. " , ephemeral = True )
return None
@ui.button ( label = " Catégorie Tickets " , style = discord . ButtonStyle . primary , row = 0 , emoji = " 📁 " )
async def set_category ( self , interaction : discord . Interaction , button : ui . Button ) :
category = await self . prompt_for_id ( interaction , " Envoie l ' ID de la **catégorie** où créer les tickets : " , is_category = True )
if category :
settings = load_settings ( self . guild_id )
settings [ " ticket_category_id " ] = category . id
save_settings ( self . guild_id , settings )
await interaction . followup . send ( f " ✅ Catégorie configurée sur ** { category . name } ** " , ephemeral = True )
@ui.button ( label = " Rôle Staff " , style = discord . ButtonStyle . primary , row = 0 , emoji = " 🛡️ " )
async def set_staff_role ( self , interaction : discord . Interaction , button : ui . Button ) :
role = await self . prompt_for_id ( interaction , " Mentionne le **rôle Staff** ou envoie son ID : " )
if role :
settings = load_settings ( self . guild_id )
settings [ " staff_role_id " ] = role . id
save_settings ( self . guild_id , settings )
await interaction . followup . send ( f " ✅ Rôle Staff configuré sur ** { role . name } ** " , ephemeral = True )
@ui.button ( label = " Rôle Équipe Staff " , style = discord . ButtonStyle . primary , row = 0 , emoji = " 👥 " )
async def set_team_staff_role ( self , interaction : discord . Interaction , button : ui . Button ) :
role = await self . prompt_for_id ( interaction , " Mentionne le **rôle Équipe Staff** (à ping pour les nouveaux tickets) ou envoie son ID : " )
if role :
settings = load_settings ( self . guild_id )
settings [ " team_staff_role_id " ] = role . id
save_settings ( self . guild_id , settings )
await interaction . followup . send ( f " ✅ Rôle Équipe Staff configuré sur ** { role . name } ** " , ephemeral = True )
@ui.button ( label = " Rôle Attente Oral " , style = discord . ButtonStyle . secondary , row = 1 , emoji = " 🎤 " )
async def set_attente_oral_role ( self , interaction : discord . Interaction , button : ui . Button ) :
role = await self . prompt_for_id ( interaction , " Mentionne le rôle **Attente Oral** ou envoie son ID : " )
if role :
settings = load_settings ( self . guild_id )
settings [ " attente_oral_role_id " ] = role . id
save_settings ( self . guild_id , settings )
await interaction . followup . send ( f " ✅ Rôle Attente Oral configuré sur ** { role . name } ** " , ephemeral = True )
@ui.button ( label = " Rôle Candidature à faire " , style = discord . ButtonStyle . secondary , row = 1 , emoji = " 📝 " )
async def set_candidature_a_faire_role ( self , interaction : discord . Interaction , button : ui . Button ) :
role = await self . prompt_for_id ( interaction , " Mentionne le rôle **Candidature à faire** ou envoie son ID : " )
if role :
settings = load_settings ( self . guild_id )
settings [ " candidature_a_faire_role_id " ] = role . id
save_settings ( self . guild_id , settings )
await interaction . followup . send ( f " ✅ Rôle Candidature à faire configuré sur ** { role . name } ** " , ephemeral = True )
@ui.button ( label = " Rôle Citoyen " , style = discord . ButtonStyle . success , row = 1 , emoji = " 🏙️ " )
async def set_citoyen_role ( self , interaction : discord . Interaction , button : ui . Button ) :
role = await self . prompt_for_id ( interaction , " Mentionne le rôle **Citoyen** ou envoie son ID : " )
if role :
settings = load_settings ( self . guild_id )
settings [ " citoyen_role_id " ] = role . id
save_settings ( self . guild_id , settings )
await interaction . followup . send ( f " ✅ Rôle Citoyen configuré sur ** { role . name } ** " , ephemeral = True )
2026-02-07 14:42:39 +01:00
class ContinueFormView ( discord . ui . View ) :
""" Vue avec un bouton pour continuer à l ' étape suivante """
def __init__ ( self , current_step , step1_data = None , step2_part1_data = None , step2_part2_data = None ) :
super ( ) . __init__ ( timeout = 180.0 ) # Définir un timeout pour la vue
self . current_step = current_step
self . step1_data = step1_data
self . step2_part1_data = step2_part1_data
self . step2_part2_data = step2_part2_data
@discord.ui.button ( label = " 📋 Continuer le formulaire " , style = discord . ButtonStyle . green , custom_id = " continue_form " )
async def continue_form ( self , interaction : discord . Interaction , button : discord . ui . Button ) :
""" Passe à l ' étape suivante du formulaire """
if self . current_step == 1 :
await interaction . response . send_modal ( TicketStep2Part1 ( self . step1_data ) )
elif self . current_step == 2 :
await interaction . response . send_modal ( TicketStep2Part2 ( self . step1_data , self . step2_part1_data ) )
elif self . current_step == 3 :
await interaction . response . send_modal ( TicketStep3 ( self . step1_data , self . step2_part1_data , self . step2_part2_data ) )
# Vérifier si le message existe toujours avant de le modifier
try :
await interaction . message . edit ( view = self )
except discord . errors . NotFound :
# Si le message n'existe plus, nous ne faisons rien
pass
class TicketStep1 ( discord . ui . Modal , title = " 📋 Informations Personnelles " ) :
""" Étape 1 du formulaire - Infos personnelles """
pseudo = discord . ui . TextInput ( label = " Pseudo Epic (Fortnite) " , required = True , max_length = 16 )
age = discord . ui . TextInput ( label = " Âge " , required = True , max_length = 2 )
experience = discord . ui . TextInput ( label = " Expérience RP ? " , style = discord . TextStyle . paragraph , required = True , max_length = 50 )
decouverte = discord . ui . TextInput ( label = " Comment as-tu découvert le serveur ? " , required = True , max_length = 20 )
micro = discord . ui . TextInput ( label = " As-tu un micro de qualité ? " , required = True , max_length = 3 )
async def on_submit ( self , interaction : discord . Interaction ) :
""" Envoie un bouton pour passer à l ' étape 2 """
view = ContinueFormView ( current_step = 1 , step1_data = self )
await interaction . response . send_message ( " ✅ Première étape terminée ! Cliquez ci-dessous pour continuer. " , view = view , ephemeral = True )
class TicketStep2Part1 ( discord . ui . Modal , title = " 👤 Infos sur ton personnage (Partie 1) " ) :
""" Étape 2 du formulaire - Infos RP (Partie 1) """
def __init__ ( self , step1_data ) :
super ( ) . __init__ ( )
self . step1_data = step1_data
prenom = discord . ui . TextInput ( label = " Prénom " , required = True )
nom = discord . ui . TextInput ( label = " Nom " , required = True )
age_perso = discord . ui . TextInput ( label = " Âge du personnage " , required = True )
genre = discord . ui . TextInput ( label = " Genre " , required = True , max_length = 5 )
naissance = discord . ui . TextInput ( label = " Date et lieu de naissance " , required = True )
async def on_submit ( self , interaction : discord . Interaction ) :
""" Envoie un bouton pour passer à l ' étape suivante """
view = ContinueFormView ( current_step = 2 , step1_data = self . step1_data , step2_part1_data = self )
await interaction . response . send_message ( " ✅ Deuxième partie terminée ! Cliquez ci-dessous pour continuer. " , view = view , ephemeral = True )
class TicketStep2Part2 ( discord . ui . Modal , title = " 👤 Infos sur ton personnage (Partie 2) " ) :
""" Étape 2 du formulaire - Infos RP (Partie 2) """
def __init__ ( self , step1_data , step2_part1_data ) :
super ( ) . __init__ ( )
self . step1_data = step1_data
self . step2_part1_data = step2_part1_data
traits = discord . ui . TextInput ( label = " Traits de caractère " , required = True )
nationalite = discord . ui . TextInput ( label = " Nationalité " , required = True )
skin_rp = discord . ui . TextInput ( label = " Skin RP " , required = True )
metier_souhaite = discord . ui . TextInput ( label = " Métier souhaité " , required = True )
async def on_submit ( self , interaction : discord . Interaction ) :
""" Envoie un bouton pour passer à l ' étape 3 """
view = ContinueFormView ( current_step = 3 , step1_data = self . step1_data , step2_part1_data = self . step2_part1_data , step2_part2_data = self )
await interaction . response . send_message ( " ✅ Deuxième étape terminée ! Cliquez ci-dessous pour continuer. " , view = view , ephemeral = True )
class TicketStep3 ( discord . ui . Modal , title = " 📋 Développement du Personnage " ) :
""" Étape 3 du formulaire - Histoire du personnage """
def __init__ ( self , step1_data , step2_part1_data , step2_part2_data ) :
super ( ) . __init__ ( )
self . step1_data = step1_data
self . step2_part1_data = step2_part1_data
self . step2_part2_data = step2_part2_data
histoire = discord . ui . TextInput (
label = " Histoire du personnage " ,
style = discord . TextStyle . paragraph ,
required = True ,
min_length = 425 ,
max_length = 500 # Limite de caractères maximum
)
objectifs = discord . ui . TextInput ( label = " Objectifs RP (court et long terme) " , style = discord . TextStyle . paragraph , required = True , max_length = 100 )
async def on_submit ( self , interaction : discord . Interaction ) :
""" Création du ticket après validation """
guild = interaction . guild
user = interaction . user
2026-03-14 15:40:19 +01:00
settings = load_settings ( guild . id )
category_id = settings . get ( " ticket_category_id " )
staff_role_id = settings . get ( " staff_role_id " )
team_staff_role_id = settings . get ( " team_staff_role_id " )
if not category_id or not staff_role_id or not team_staff_role_id :
return await interaction . response . send_message ( " ❌ Erreur : Le système de tickets n ' est pas entièrement configuré sur ce serveur (Catégorie, Rôle Staff ou Rôle Équipe Staff manquant). " , ephemeral = True )
2026-02-07 14:42:39 +01:00
2026-03-14 15:40:19 +01:00
category = discord . utils . get ( guild . categories , id = int ( category_id ) )
2026-02-07 14:42:39 +01:00
if category is None :
2026-03-14 15:40:19 +01:00
return await interaction . response . send_message ( " ❌ Erreur : La catégorie des tickets configurée n ' existe plus ! " , ephemeral = True )
2026-02-07 14:42:39 +01:00
2026-03-14 15:40:19 +01:00
staff_role = discord . utils . get ( guild . roles , id = int ( staff_role_id ) )
team_staff_role = discord . utils . get ( guild . roles , id = int ( team_staff_role_id ) )
if staff_role is None or team_staff_role is None :
return await interaction . response . send_message ( " ❌ Erreur : L ' un des rôles staff configurés n ' existe plus ! " , ephemeral = True )
2026-02-07 14:42:39 +01:00
# Vérifier si l'utilisateur a déjà un ticket ouvert
for channel in category . text_channels :
if channel . topic == f " Ticket de { user . id } " :
2026-03-14 15:40:19 +01:00
return await interaction . response . send_message ( f " ❌ Vous avez déjà un ticket ouvert : { channel . mention } " , ephemeral = True )
2026-02-07 14:42:39 +01:00
# Définir les permissions du salon du ticket
overwrites = {
guild . default_role : discord . PermissionOverwrite ( read_messages = False ) ,
user : discord . PermissionOverwrite ( read_messages = True , send_messages = True ) ,
staff_role : discord . PermissionOverwrite ( read_messages = True , send_messages = True ) ,
2026-03-14 15:40:19 +01:00
team_staff_role : discord . PermissionOverwrite ( read_messages = True , send_messages = False )
2026-02-07 14:42:39 +01:00
}
2026-03-14 15:40:19 +01:00
# Créer un salon de ticket avec le nom [pseudo]-écrit
2026-02-07 14:42:39 +01:00
ticket_channel = await category . create_text_channel (
name = f " { user . name } -écrit " ,
topic = f " Ticket de { user . id } " ,
overwrites = overwrites
)
# Création de l'embed avec les infos du formulaire
embed = discord . Embed ( title = " 📩 Ticket Whitelist " , color = 0x00ff00 )
embed . add_field ( name = " 📋 - Informations Personnelles " , value = (
f " > - Pseudo Epic : { self . step1_data . pseudo . value } \n "
f " > - Âge : { self . step1_data . age . value } \n "
f " > - Expérience RP : { self . step1_data . experience . value } \n "
f " > - Découverte du serveur : { self . step1_data . decouverte . value } \n "
f " > - Micro de qualité : { self . step1_data . micro . value } "
) , inline = False )
embed . add_field ( name = " 👤 - Informations RP " , value = (
f " > - Prénom : { self . step2_part1_data . prenom . value } \n "
f " > - Nom : { self . step2_part1_data . nom . value } \n "
f " > - Âge du personnage : { self . step2_part1_data . age_perso . value } \n "
f " > - Genre : { self . step2_part1_data . genre . value } \n "
f " > - Naissance : { self . step2_part1_data . naissance . value } \n "
f " > - Traits de caractère : { self . step2_part2_data . traits . value } \n "
f " > - Nationalité : { self . step2_part2_data . nationalite . value } \n "
f " > - Skin RP : { self . step2_part2_data . skin_rp . value } \n "
f " > - Métier souhaité : { self . step2_part2_data . metier_souhaite . value } "
) , inline = False )
embed . add_field ( name = " 📋 - Développement du Personnage " , value = (
f " > - Histoire : { self . histoire . value } \n "
f " > - Objectifs : { self . objectifs . value } "
) , inline = False )
# Envoyer l'embed avec les boutons d'actions pour les staff
2026-03-14 15:40:19 +01:00
await ticket_channel . send ( embed = embed , view = TicketManagementView ( ) )
2026-02-07 14:42:39 +01:00
# Envoyer un message pour mentionner l'équipe staff
await ticket_channel . send ( f " { team_staff_role . mention } - Nouveau ticket créé ! " )
# Mettre à jour le pseudo du joueur si le bot a les permissions nécessaires
if guild . me . guild_permissions . manage_nicknames :
new_nickname = f " { self . step2_part1_data . prenom . value } { self . step2_part1_data . nom . value } | { self . step1_data . pseudo . value } "
if len ( new_nickname ) > 32 :
new_nickname = new_nickname [ : 32 ]
2026-03-14 15:40:19 +01:00
try : await user . edit ( nick = new_nickname )
except discord . Forbidden : pass
2026-02-07 14:42:39 +01:00
# Envoyer un message de confirmation
try :
await interaction . response . send_message ( f " ✅ Ticket ouvert : { ticket_channel . mention } " , ephemeral = True )
except discord . errors . NotFound :
await interaction . followup . send ( f " ✅ Ticket ouvert : { ticket_channel . mention } " , ephemeral = True )
class TicketManagementView ( discord . ui . View ) :
""" Vue pour gérer le ticket avec les boutons Fermer, Réouvrir, Claim et Supprimer """
2026-03-14 15:40:19 +01:00
def __init__ ( self ) :
2026-02-07 14:42:39 +01:00
super ( ) . __init__ ( timeout = None )
@discord.ui.button ( label = " 🔒 Fermer le Ticket " , style = discord . ButtonStyle . red , custom_id = " close_ticket " )
async def close_ticket ( self , interaction : discord . Interaction , button : discord . ui . Button ) :
""" Ferme le ticket en désactivant l ' envoi de messages pour le membre """
2026-03-14 15:40:19 +01:00
settings = load_settings ( interaction . guild . id )
staff_role_id = settings . get ( " staff_role_id " )
if not discord . utils . get ( interaction . user . roles , id = int ( staff_role_id ) if staff_role_id else 0 ) and not interaction . user . guild_permissions . administrator :
return await interaction . response . send_message ( " ❌ Seuls les staffs peuvent fermer un ticket. " , ephemeral = True )
topic = interaction . channel . topic or " "
if not topic . startswith ( " Ticket de " ) :
return await interaction . response . send_message ( " ❌ Impossible de trouver le propriétaire du ticket à fermer. " , ephemeral = True )
try :
owner_id = int ( topic . split ( " Ticket de " ) [ 1 ] )
ticket_owner = interaction . guild . get_member ( owner_id )
if not ticket_owner :
ticket_owner = await interaction . guild . fetch_member ( owner_id )
except ( IndexError , ValueError , discord . NotFound ) :
return await interaction . response . send_message ( " ❌ Membre introuvable. " , ephemeral = True )
await interaction . channel . set_permissions ( ticket_owner , read_messages = False , send_messages = False )
2026-02-07 14:42:39 +01:00
await interaction . channel . edit ( name = " ticket-fermé " )
await interaction . response . send_message ( " 🔒 Le ticket a été fermé. Vous ne pouvez plus le voir. " , ephemeral = True )
# Mettre à jour la vue pour afficher uniquement les boutons Réouvrir et Supprimer
2026-03-14 15:40:19 +01:00
new_view = ClosedTicketView ( )
2026-02-07 14:42:39 +01:00
await interaction . channel . send ( " Le ticket a été fermé. " , view = new_view )
@discord.ui.button ( label = " 👤 Prendre en charge le Ticket " , style = discord . ButtonStyle . blurple , custom_id = " claim_ticket " )
async def claim_ticket ( self , interaction : discord . Interaction , button : discord . ui . Button ) :
""" Un staff prend en charge le ticket """
2026-03-14 15:40:19 +01:00
settings = load_settings ( interaction . guild . id )
staff_role_id = settings . get ( " staff_role_id " )
if not discord . utils . get ( interaction . user . roles , id = int ( staff_role_id ) if staff_role_id else 0 ) and not interaction . user . guild_permissions . administrator :
return await interaction . response . send_message ( " ❌ Seuls les staffs peuvent claim un ticket. " , ephemeral = True )
staff_role = discord . utils . get ( interaction . guild . roles , id = int ( staff_role_id ) ) if staff_role_id else None
claimed_by = interaction . user
if staff_role :
await interaction . channel . set_permissions ( staff_role , send_messages = False , read_messages = True )
await interaction . channel . set_permissions ( claimed_by , read_messages = True , send_messages = True )
await interaction . response . send_message ( f " 👤 { claimed_by . mention } a claim ce ticket. " , ephemeral = False )
2026-02-07 14:42:39 +01:00
class ClosedTicketView ( discord . ui . View ) :
""" Vue pour les tickets fermés avec les boutons Réouvrir et Supprimer """
2026-03-14 15:40:19 +01:00
def __init__ ( self ) :
2026-02-07 14:42:39 +01:00
super ( ) . __init__ ( timeout = None )
@discord.ui.button ( label = " ♻ Réouvrir le Ticket " , style = discord . ButtonStyle . green , custom_id = " reopen_ticket " )
async def reopen_ticket ( self , interaction : discord . Interaction , button : discord . ui . Button ) :
""" Réouvre le ticket pour le membre """
2026-03-14 15:40:19 +01:00
settings = load_settings ( interaction . guild . id )
staff_role_id = settings . get ( " staff_role_id " )
if not discord . utils . get ( interaction . user . roles , id = int ( staff_role_id ) if staff_role_id else 0 ) and not interaction . user . guild_permissions . administrator :
return await interaction . response . send_message ( " ❌ Seuls les staffs peuvent réouvrir un ticket. " , ephemeral = True )
topic = interaction . channel . topic or " "
if not topic . startswith ( " Ticket de " ) :
return await interaction . response . send_message ( " ❌ Impossible de trouver le propriétaire du ticket à réouvrir. " , ephemeral = True )
try :
owner_id = int ( topic . split ( " Ticket de " ) [ 1 ] )
ticket_owner = interaction . guild . get_member ( owner_id )
if not ticket_owner :
ticket_owner = await interaction . guild . fetch_member ( owner_id )
except ( IndexError , ValueError , discord . NotFound ) :
return await interaction . response . send_message ( " ❌ Membre introuvable. " , ephemeral = True )
await interaction . channel . set_permissions ( ticket_owner , read_messages = True , send_messages = True )
2026-02-07 14:42:39 +01:00
await interaction . response . send_message ( " ✅ Le ticket a été réouvert. " , ephemeral = True )
# Mettre à jour la vue pour afficher tous les boutons
2026-03-14 15:40:19 +01:00
new_view = TicketManagementView ( )
2026-02-07 14:42:39 +01:00
await interaction . channel . send ( " Le ticket a été réouvert. " , view = new_view )
@discord.ui.button ( label = " ❌ Supprimer le Ticket " , style = discord . ButtonStyle . danger , custom_id = " delete_ticket " )
async def delete_ticket ( self , interaction : discord . Interaction , button : discord . ui . Button ) :
""" Supprime le salon du ticket """
2026-03-14 15:40:19 +01:00
settings = load_settings ( interaction . guild . id )
staff_role_id = settings . get ( " staff_role_id " )
if not discord . utils . get ( interaction . user . roles , id = int ( staff_role_id ) if staff_role_id else 0 ) and not interaction . user . guild_permissions . administrator :
return await interaction . response . send_message ( " ❌ Seuls les staffs peuvent supprimer un ticket. " , ephemeral = True )
2026-02-07 14:42:39 +01:00
await interaction . response . send_message ( " ❌ Suppression du ticket... " , ephemeral = True )
await interaction . channel . delete ( )
class TicketView ( discord . ui . View ) :
""" Vue avec le bouton pour ouvrir le ticket """
def __init__ ( self ) :
super ( ) . __init__ ( timeout = None )
@discord.ui.button ( label = " 📑 Ouvrir un Ticket Whitelist " , style = discord . ButtonStyle . red , custom_id = " open_ticket " )
async def open_ticket ( self , interaction : discord . Interaction , button : discord . ui . Button ) :
""" Affiche le premier formulaire """
await interaction . response . send_modal ( TicketStep1 ( ) )
class Ticket ( commands . Cog ) :
""" Commande pour envoyer l ' embed de ticket """
def __init__ ( self , bot ) :
self . bot = bot
@app_commands.command ( name = " ticket_whitelist " , description = " Envoie l ' embed pour ouvrir un ticket de whitelist. " )
2026-03-14 15:40:19 +01:00
@app_commands.checks.has_permissions ( administrator = True )
2026-02-07 14:42:39 +01:00
async def ticket_whitelist ( self , interaction : discord . Interaction ) :
""" Envoie l ' embed avec le bouton d ' ouverture de ticket """
embed = discord . Embed (
title = " 📩 Ticket Whitelist " ,
description = " Pour réaliser votre whitelist, ouvrez un ticket et remplissez le formulaire. " ,
color = 0x00ff00
)
view = TicketView ( )
await interaction . channel . send ( embed = embed , view = view )
await interaction . response . send_message ( " ✅ Embed des tickets envoyé. " , ephemeral = True )
2026-03-14 15:40:19 +01:00
@app_commands.command ( name = " ticket_whitelist_config " , description = " Configurer le système de tickets whitelist (Admin) " )
@app_commands.checks.has_permissions ( administrator = True )
async def ticket_whitelist_config ( self , interaction : discord . Interaction ) :
""" Interface de configuration des tickets """
settings = load_settings ( interaction . guild . id )
embed = discord . Embed (
title = " ⚙️ Configuration Tickets Whitelist " ,
description = " Utilisez les boutons ci-dessous pour lier la catégorie et les rôles. " ,
color = discord . Color . blue ( )
)
cat_id = settings . get ( " ticket_category_id " )
s_role_id = settings . get ( " staff_role_id " )
ts_role_id = settings . get ( " team_staff_role_id " )
ao_role_id = settings . get ( " attente_oral_role_id " )
caf_role_id = settings . get ( " candidature_a_faire_role_id " )
c_role_id = settings . get ( " citoyen_role_id " )
embed . add_field ( name = " 📁 Catégorie Tickets " , value = f " Id: { cat_id } " if cat_id else " ❌ Non configuré " , inline = True )
embed . add_field ( name = " 🛡️ Staff " , value = f " <@& { s_role_id } > " if s_role_id else " ❌ Non configuré " , inline = True )
embed . add_field ( name = " 👥 Équipe Staff " , value = f " <@& { ts_role_id } > " if ts_role_id else " ❌ Non configuré " , inline = True )
embed . add_field ( name = " 🎤 Attente Oral " , value = f " <@& { ao_role_id } > " if ao_role_id else " ❌ Non configuré " , inline = True )
embed . add_field ( name = " 📝 Candidature à faire " , value = f " <@& { caf_role_id } > " if caf_role_id else " ❌ Non configuré " , inline = True )
embed . add_field ( name = " 🏙️ Citoyen " , value = f " <@& { c_role_id } > " if c_role_id else " ❌ Non configuré " , inline = True )
await interaction . response . send_message ( embed = embed , view = TicketWhitelistConfigView ( interaction . guild . id ) , ephemeral = True )
2026-02-07 14:42:39 +01:00
@commands.Cog.listener ( )
async def on_message ( self , message ) :
""" Surveille les messages des staffs dans les tickets """
if message . guild is None or message . author . bot :
return
if not message . channel . topic or not message . channel . topic . startswith ( " Ticket de " ) :
return
2026-03-14 15:40:19 +01:00
settings = load_settings ( message . guild . id )
staff_role_id = settings . get ( " staff_role_id " )
attente_oral_role_id = settings . get ( " attente_oral_role_id " )
candidature_a_faire_role_id = settings . get ( " candidature_a_faire_role_id " )
citoyen_role_id = settings . get ( " citoyen_role_id " )
if not staff_role_id :
return
staff_role = discord . utils . get ( message . guild . roles , id = int ( staff_role_id ) )
2026-02-07 14:42:39 +01:00
if staff_role in message . author . roles and " validé " in message . content . lower ( ) :
try :
user_id = message . channel . topic . split ( " Ticket de " ) [ 1 ]
2026-03-14 15:40:19 +01:00
user = await message . guild . fetch_member ( int ( user_id ) )
if not attente_oral_role_id or not candidature_a_faire_role_id or not citoyen_role_id :
await message . channel . send ( " ❌ Erreur de configuration : Impossible d ' attribuer les rôles, ils ne sont pas tous définis dans `/ticket_whitelist_config`. " , delete_after = 10 )
return
2026-02-07 14:42:39 +01:00
# Vérifier si le rôle "attente oral" est déjà attribué
2026-03-14 15:40:19 +01:00
attente_oral_role = discord . utils . get ( message . guild . roles , id = int ( attente_oral_role_id ) )
candidature_a_faire_role = discord . utils . get ( message . guild . roles , id = int ( candidature_a_faire_role_id ) )
citoyen_role = discord . utils . get ( message . guild . roles , id = int ( citoyen_role_id ) )
2026-02-07 14:42:39 +01:00
if attente_oral_role in user . roles :
# Deuxième "validé" détecté
2026-03-14 15:40:19 +01:00
if attente_oral_role : await user . remove_roles ( attente_oral_role )
if citoyen_role : await user . add_roles ( citoyen_role )
2026-02-07 14:42:39 +01:00
await message . channel . edit ( name = f " { user . name } -citoyen " )
else :
# Premier "validé" détecté
2026-03-14 15:40:19 +01:00
if candidature_a_faire_role : await user . remove_roles ( candidature_a_faire_role )
if attente_oral_role : await user . add_roles ( attente_oral_role )
2026-02-07 14:42:39 +01:00
await message . channel . edit ( name = f " { user . name } -oral " )
except Exception as e :
2026-03-14 15:40:19 +01:00
print ( f " Erreur lors du traitement du ticket : { e } " )
2026-02-07 14:42:39 +01:00
async def setup ( bot ) :
2026-03-14 15:40:19 +01:00
await bot . add_cog ( Ticket ( bot ) )
bot . add_view ( TicketView ( ) )
bot . add_view ( TicketManagementView ( ) )
bot . add_view ( ClosedTicketView ( ) )