Mise à jour du système de whitelist, sécurité et logging
This commit is contained in:
parent
d82f704c75
commit
1e384032be
7 changed files with 199 additions and 145 deletions
|
|
@ -1,4 +1,5 @@
|
|||
import discord
|
||||
import re
|
||||
from discord.ext import commands
|
||||
from discord import app_commands, ui
|
||||
import json
|
||||
|
|
@ -313,26 +314,28 @@ class TicketStep3(discord.ui.Modal, title="📋 Développement du Personnage"):
|
|||
except discord.errors.NotFound:
|
||||
await interaction.followup.send(f"✅ Ticket ouvert : {ticket_channel.mention}", ephemeral=True)
|
||||
|
||||
class TicketManagementView(discord.ui.view.View):
|
||||
class TicketManagementView(discord.ui.View):
|
||||
"""Vue pour gérer le ticket avec les boutons Fermer, Réouvrir, Claim et Supprimer"""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__(timeout=None)
|
||||
|
||||
async def get_ticket_owner(self, interaction: discord.Interaction):
|
||||
"""Récupère le propriétaire du ticket via le topic du salon (robuste)"""
|
||||
if interaction.channel.topic and "Ticket de" in interaction.channel.topic:
|
||||
"""Récupère le propriétaire du ticket via le topic du salon (robuste via regex)"""
|
||||
import re
|
||||
topic = interaction.channel.topic
|
||||
if not topic:
|
||||
return None
|
||||
|
||||
match = re.search(r"(\d{17,20})", topic)
|
||||
if match:
|
||||
owner_id = int(match.group(1))
|
||||
try:
|
||||
user_id_str = interaction.channel.topic.replace("Ticket de ", "").strip()
|
||||
owner_id = int(user_id_str)
|
||||
member = interaction.guild.get_member(owner_id)
|
||||
if not member:
|
||||
try:
|
||||
member = await interaction.guild.fetch_member(owner_id)
|
||||
except (discord.NotFound, discord.HTTPException):
|
||||
return None
|
||||
member = await interaction.guild.fetch_member(owner_id)
|
||||
return member
|
||||
except (ValueError, IndexError):
|
||||
except:
|
||||
return None
|
||||
return None
|
||||
|
||||
|
|
@ -390,26 +393,28 @@ class TicketManagementView(discord.ui.view.View):
|
|||
kuby_logger.error(f"Erreur lors du claim du ticket: {e}")
|
||||
await interaction.followup.send(f"❌ Erreur lors du claim : {e}")
|
||||
|
||||
class ClosedTicketView(discord.ui.view.View):
|
||||
class ClosedTicketView(discord.ui.View):
|
||||
"""Vue pour les tickets fermés avec les boutons Réouvrir et Supprimer"""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__(timeout=None)
|
||||
|
||||
async def get_ticket_owner(self, interaction: discord.Interaction):
|
||||
"""Récupère le propriétaire du ticket via le topic du salon (robuste)"""
|
||||
if interaction.channel.topic and "Ticket de" in interaction.channel.topic:
|
||||
"""Récupère le propriétaire du ticket via le topic du salon (robuste via regex)"""
|
||||
import re
|
||||
topic = interaction.channel.topic
|
||||
if not topic:
|
||||
return None
|
||||
|
||||
match = re.search(r"(\d{17,20})", topic)
|
||||
if match:
|
||||
owner_id = int(match.group(1))
|
||||
try:
|
||||
user_id_str = interaction.channel.topic.replace("Ticket de ", "").strip()
|
||||
owner_id = int(user_id_str)
|
||||
member = interaction.guild.get_member(owner_id)
|
||||
if not member:
|
||||
try:
|
||||
member = await interaction.guild.fetch_member(owner_id)
|
||||
except (discord.NotFound, discord.HTTPException):
|
||||
return None
|
||||
member = await interaction.guild.fetch_member(owner_id)
|
||||
return member
|
||||
except (ValueError, IndexError):
|
||||
except:
|
||||
return None
|
||||
return None
|
||||
|
||||
|
|
@ -443,7 +448,7 @@ class ClosedTicketView(discord.ui.view.View):
|
|||
kuby_logger.error(f"Erreur lors de la préparation de réouverture: {e}")
|
||||
await interaction.followup.send(f"❌ Erreur : {e}", ephemeral=True)
|
||||
|
||||
class ReopenStatusView(discord.ui.view.View):
|
||||
class ReopenStatusView(discord.ui.View):
|
||||
"""Vue pour choisir le statut lors d'une réouverture"""
|
||||
def __init__(self, owner: discord.Member):
|
||||
super().__init__(timeout=180)
|
||||
|
|
@ -464,16 +469,48 @@ class ReopenStatusView(discord.ui.view.View):
|
|||
async def apply_status(self, interaction: discord.Interaction, prefix: str):
|
||||
await interaction.response.defer(ephemeral=True)
|
||||
try:
|
||||
settings = load_settings(interaction.guild.id)
|
||||
candidature_a_faire_role_id = settings.get("candidature_a_faire_role_id")
|
||||
attente_oral_role_id = settings.get("attente_oral_role_id")
|
||||
citoyen_role_id = settings.get("citoyen_role_id")
|
||||
|
||||
# On récupère les objets rôles
|
||||
role_écrit = interaction.guild.get_role(int(candidature_a_faire_role_id)) if candidature_a_faire_role_id else None
|
||||
role_oral = interaction.guild.get_role(int(attente_oral_role_id)) if attente_oral_role_id else None
|
||||
role_citoyen = interaction.guild.get_role(int(citoyen_role_id)) if citoyen_role_id else None
|
||||
|
||||
# Synchronisation des RÔLES en fonction du statut choisi
|
||||
roles_to_remove = []
|
||||
role_to_add = None
|
||||
|
||||
if prefix == "ouvert":
|
||||
role_to_add = role_écrit
|
||||
roles_to_remove = [r for r in [role_oral, role_citoyen] if r]
|
||||
elif prefix == "oral-à-faire":
|
||||
role_to_add = role_oral
|
||||
roles_to_remove = [r for r in [role_écrit, role_citoyen] if r]
|
||||
elif prefix == "accepté":
|
||||
role_to_add = role_citoyen
|
||||
roles_to_remove = [r for r in [role_écrit, role_oral] if r]
|
||||
|
||||
# Mise à jour des rôles sur le membre
|
||||
if roles_to_remove:
|
||||
await self.owner.remove_roles(*roles_to_remove, reason="Réouverture du ticket - Maj Statut")
|
||||
if role_to_add:
|
||||
await self.owner.add_roles(role_to_add, reason="Réouverture du ticket - Maj Statut")
|
||||
|
||||
# Mise à jour du SALON
|
||||
await interaction.channel.set_permissions(self.owner, read_messages=True, send_messages=True)
|
||||
new_name = f"{prefix}-{self.owner.display_name}".replace(" ", "-").lower()[:100]
|
||||
await interaction.channel.edit(name=new_name)
|
||||
await interaction.channel.edit(name=new_name, topic=f"Ticket de {self.owner.id}")
|
||||
|
||||
new_view = TicketManagementView()
|
||||
await interaction.channel.send(f"✅ Ticket réouvert par {interaction.user.mention} (Statut: {prefix}).", view=new_view)
|
||||
await interaction.followup.send(f"✅ Statut {prefix} appliqué.", ephemeral=True)
|
||||
await interaction.channel.send(f"✅ Ticket réouvert par {interaction.user.mention} (Statut: **{prefix}**).", view=new_view)
|
||||
await interaction.followup.send(f"✅ Statut **{prefix}** appliqué et rôles synchronisés.", ephemeral=True)
|
||||
self.stop()
|
||||
except Exception as e:
|
||||
await interaction.followup.send(f"❌ Erreur : {e}", ephemeral=True)
|
||||
kuby_logger.error(f"Erreur lors de la réouverture/synchro: {e}")
|
||||
await interaction.followup.send(f"❌ Erreur système : {e}", ephemeral=True)
|
||||
|
||||
@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):
|
||||
|
|
@ -565,8 +602,12 @@ class Ticket(commands.Cog):
|
|||
if message.guild is None or message.author.bot:
|
||||
return
|
||||
|
||||
# Détection du format du topic du ticket
|
||||
if not message.channel.topic or not message.channel.topic.startswith("Ticket de"):
|
||||
# Détection du format du topic du ticket via regex (robuste)
|
||||
if not message.channel.topic:
|
||||
return
|
||||
|
||||
match = re.search(r"(\d{17,20})", message.channel.topic)
|
||||
if not match:
|
||||
return
|
||||
|
||||
# On ne traite que si le message contient "validé"
|
||||
|
|
@ -585,12 +626,12 @@ class Ticket(commands.Cog):
|
|||
return
|
||||
|
||||
try:
|
||||
# Extraction propre de l'ID utilisateur depuis le topic
|
||||
# L'ID est déjà trouvé par le regex ci-dessus
|
||||
user_id = int(match.group(1))
|
||||
try:
|
||||
user_id_str = message.channel.topic.replace("Ticket de ", "").strip()
|
||||
user = await message.guild.fetch_member(int(user_id_str))
|
||||
except (ValueError, IndexError, discord.NotFound, discord.HTTPException):
|
||||
kuby_logger.warning(f"Impossible de trouver l'utilisateur du ticket via le topic: {message.channel.topic}")
|
||||
user = await message.guild.fetch_member(user_id)
|
||||
except (discord.NotFound, discord.HTTPException):
|
||||
await message.channel.send("❌ Utilisateur introuvable. A-t-il quitté le serveur ?", delete_after=10)
|
||||
return
|
||||
|
||||
attente_oral_role_id = settings.get("attente_oral_role_id")
|
||||
|
|
@ -605,7 +646,7 @@ class Ticket(commands.Cog):
|
|||
candidature_a_faire_role = message.guild.get_role(int(candidature_a_faire_role_id))
|
||||
citoyen_role = message.guild.get_role(int(citoyen_role_id))
|
||||
|
||||
if attente_oral_role in user.roles:
|
||||
if (attente_oral_role and attente_oral_role in user.roles) or "oral-à-faire" in message.channel.name.lower():
|
||||
# DEUXIÈME VALIDATION -> ACCEPTÉ / CITOYEN
|
||||
if attente_oral_role: await user.remove_roles(attente_oral_role)
|
||||
if citoyen_role: await user.add_roles(citoyen_role)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue