Refonte § Debug du menu principal

This commit is contained in:
Mathis 2026-01-02 19:04:05 +01:00
parent 343a4f879c
commit fd2d2a74dc
2 changed files with 759 additions and 70 deletions

View file

@ -3,49 +3,84 @@ from discord import app_commands
from discord.ext import commands from discord.ext import commands
import json import json
import os import os
from typing import Optional
# Chemins des fichiers # Chemins des fichiers
SETTINGS_FILE = "data/security_settings.json" SETTINGS_FILE = "data/security_settings.json"
WHITELIST_FILE = "data/whitelist.json" WHITELIST_FILE = "data/whitelist.json"
# --- UTILITAIRES --- # --- UTILITAIRES ---
def load_settings(guild_id): def load_settings(guild_id: int) -> dict:
if not os.path.exists(SETTINGS_FILE): return {} """Charge les paramètres de sécurité pour un serveur"""
if not os.path.exists(SETTINGS_FILE):
return {}
try: try:
with open(SETTINGS_FILE, "r", encoding='utf-8') as f: with open(SETTINGS_FILE, "r", encoding='utf-8') as f:
return json.load(f).get(str(guild_id), {}) return json.load(f).get(str(guild_id), {})
except: return {} except (json.JSONDecodeError, FileNotFoundError):
return {}
def save_settings(guild_id, settings): def save_settings(guild_id: int, settings: dict) -> None:
"""Sauvegarde les paramètres de sécurité"""
data = {} data = {}
if os.path.exists(SETTINGS_FILE): if os.path.exists(SETTINGS_FILE):
try: try:
with open(SETTINGS_FILE, "r", encoding='utf-8') as f: data = json.load(f) with open(SETTINGS_FILE, "r", encoding='utf-8') as f:
except: pass data = json.load(f)
except (json.JSONDecodeError, FileNotFoundError):
pass
data[str(guild_id)] = settings data[str(guild_id)] = settings
os.makedirs(os.path.dirname(SETTINGS_FILE), exist_ok=True) os.makedirs(os.path.dirname(SETTINGS_FILE), exist_ok=True)
with open(SETTINGS_FILE, "w", encoding='utf-8') as f: with open(SETTINGS_FILE, "w", encoding='utf-8') as f:
json.dump(data, f, indent=4, ensure_ascii=False) json.dump(data, f, indent=4, ensure_ascii=False)
def is_whitelisted(guild_id, user_id): def load_whitelist(guild_id: int) -> list:
if not os.path.exists(WHITELIST_FILE): return False """Charge la whitelist pour un serveur"""
if not os.path.exists(WHITELIST_FILE):
return []
try:
with open(WHITELIST_FILE, "r", encoding='utf-8') as f:
data = json.load(f)
return data.get(str(guild_id), [])
except (json.JSONDecodeError, FileNotFoundError):
return []
def is_whitelisted(guild_id: int, user_id: int) -> bool:
"""Vérifie si un utilisateur est whitelisté"""
if not os.path.exists(WHITELIST_FILE):
return False
try: try:
with open(WHITELIST_FILE, "r", encoding='utf-8') as f: with open(WHITELIST_FILE, "r", encoding='utf-8') as f:
data = json.load(f) data = json.load(f)
return user_id in data.get(str(guild_id), []) return user_id in data.get(str(guild_id), [])
except: return False except (json.JSONDecodeError, FileNotFoundError):
return False
# --- MODAL DE RÉGLAGE DU SEUIL --- def is_immune(guild, user) -> bool:
class ThresholdModal(discord.ui.Modal, title="Réglage du Seuil Anti-Spam"): """Vérifie l'immunité (propriétaire, admin, ou whitelist)"""
if user.id == guild.owner_id:
return True
if user.guild_permissions.administrator:
return True
if is_whitelisted(guild.id, user.id):
return True
return False
# --- MODALS DE CONFIGURATION ---
class SpamThresholdModal(discord.ui.Modal, title="🛡️ Seuil Anti-Spam"):
"""Modal pour configurer le seuil anti-spam"""
threshold_input = discord.ui.TextInput( threshold_input = discord.ui.TextInput(
label="Messages max autorisés (en 5 secondes)", label="Messages max autorisés",
placeholder="Par défaut : 5", placeholder="Entre 3 et 10 (défaut: 5)",
min_length=1, min_length=1,
max_length=2, max_length=2,
required=True required=True
) )
def __init__(self, guild_id, settings, view): def __init__(self, guild_id: int, settings: dict, view: 'SecurityView'):
super().__init__() super().__init__()
self.guild_id = guild_id self.guild_id = guild_id
self.settings = settings self.settings = settings
@ -53,109 +88,704 @@ class ThresholdModal(discord.ui.Modal, title="Réglage du Seuil Anti-Spam"):
async def on_submit(self, interaction: discord.Interaction): async def on_submit(self, interaction: discord.Interaction):
if not self.threshold_input.value.isdigit(): if not self.threshold_input.value.isdigit():
return await interaction.response.send_message("❌ Erreur : Veuillez entrer un nombre entier.", ephemeral=True) return await interaction.response.send_message(
"❌ Erreur : Veuillez entrer un nombre entier.",
ephemeral=True
)
new_val = int(self.threshold_input.value) value = int(self.threshold_input.value)
self.settings["spam_threshold"] = new_val if value < 1 or value > 10:
return await interaction.response.send_message(
"❌ Erreur : La valeur doit être entre 1 et 10.",
ephemeral=True
)
self.settings["spam_threshold"] = value
save_settings(self.guild_id, self.settings) save_settings(self.guild_id, self.settings)
self.parent_view.settings = self.settings
self.parent_view.build_view()
await interaction.response.send_message(f"✅ Le seuil a été réglé sur **{new_val}** messages.", ephemeral=True) await interaction.response.edit_message(view=self.parent_view)
await interaction.followup.send(
f"✅ Seuil anti-spam configuré sur **{value}** messages/5s",
ephemeral=True
)
class RaidThresholdModal(discord.ui.Modal, title="⚔️ Seuil Anti-Raid"):
"""Modal pour configurer le seuil anti-raid"""
threshold_input = discord.ui.TextInput(
label="Joins max en 10 secondes",
placeholder="Entre 2 et 10 (défaut: 3)",
min_length=1,
max_length=2,
required=True
)
def __init__(self, guild_id: int, settings: dict, view: 'SecurityView'):
super().__init__()
self.guild_id = guild_id
self.settings = settings
self.parent_view = view
async def on_submit(self, interaction: discord.Interaction):
if not self.threshold_input.value.isdigit():
return await interaction.response.send_message(
"❌ Erreur : Veuillez entrer un nombre entier.",
ephemeral=True
)
value = int(self.threshold_input.value)
if value < 1 or value > 20:
return await interaction.response.send_message(
"❌ Erreur : La valeur doit être entre 1 et 20.",
ephemeral=True
)
self.settings["raid_threshold"] = value
save_settings(self.guild_id, self.settings)
self.parent_view.settings = self.settings
self.parent_view.build_view()
await interaction.response.edit_message(view=self.parent_view)
await interaction.followup.send(
f"✅ Seuil anti-raid configuré sur **{value}** joins/10s",
ephemeral=True
)
class WhitelistModal(discord.ui.Modal, title="👤 Ajouter à la Whitelist"):
"""Modal pour ajouter un utilisateur à la whitelist"""
user_input = discord.ui.TextInput(
label="ID de l'utilisateur",
placeholder="Entrez l'ID Discord de l'utilisateur",
min_length=15,
max_length=20,
required=True
)
def __init__(self, guild_id: int, view: 'SecurityView'):
super().__init__()
self.guild_id = guild_id
self.parent_view = view
async def on_submit(self, interaction: discord.Interaction):
if not self.user_input.value.isdigit():
return await interaction.response.send_message(
"❌ Erreur : ID invalide.",
ephemeral=True
)
user_id = int(self.user_input.value)
# Charger et modifier la whitelist
data = {}
if os.path.exists(WHITELIST_FILE):
try:
with open(WHITELIST_FILE, "r", encoding='utf-8') as f:
data = json.load(f)
except (json.JSONDecodeError, FileNotFoundError):
pass
guild_whitelist = data.get(str(self.guild_id), [])
if user_id in guild_whitelist:
return await interaction.response.send_message(
"❌ Cet utilisateur est déjà dans la whitelist.",
ephemeral=True
)
guild_whitelist.append(user_id)
data[str(self.guild_id)] = guild_whitelist
os.makedirs(os.path.dirname(WHITELIST_FILE), exist_ok=True)
with open(WHITELIST_FILE, "w", encoding='utf-8') as f:
json.dump(data, f, indent=4, ensure_ascii=False)
self.parent_view.whitelist_count = len(guild_whitelist)
self.parent_view.build_view()
await interaction.response.edit_message(view=self.parent_view)
await interaction.followup.send(
f"✅ Utilisateur ajouté à la whitelist.",
ephemeral=True
)
# --- VUES DE L'INTERFACE ---
# --- VUE DE GESTION ---
class SecurityView(discord.ui.View): class SecurityView(discord.ui.View):
def __init__(self, interaction, settings, category=None): """Vue principale du panneau de sécurité"""
super().__init__(timeout=None)
def __init__(self, interaction: discord.Interaction, settings: dict, category: Optional[str] = None):
super().__init__(timeout=300) # 5 minutes timeout
self.interaction = interaction self.interaction = interaction
self.guild = interaction.guild self.guild = interaction.guild
self.settings = settings self.settings = settings
self.category = category self.category = category
self.whitelist_count = len(load_whitelist(self.guild.id))
self.build_view() self.build_view()
def build_view(self): def build_view(self):
"""Construit les boutons selon la catégorie"""
self.clear_items() self.clear_items()
if self.category is None: if self.category is None:
self.add_item(CategorySelect()) # Menu principal
self.add_item(MainCategorySelect())
elif self.category == "spam":
self.build_spam_view()
elif self.category == "raid":
self.build_raid_view()
elif self.category == "nuke":
self.build_nuke_view()
elif self.category == "logs":
self.build_logs_view()
elif self.category == "whitelist":
self.build_whitelist_view()
def build_spam_view(self):
"""Construit la vue Anti-Spam"""
# Toggle Anti-Spam
anti_spam = self.settings.get("anti_spam", False)
btn_antispam = ToggleButton(
label="Anti-Spam",
emoji="🛡️",
is_enabled=anti_spam,
custom_id="anti_spam"
)
self.add_item(btn_antispam)
# Toggle Anti-Lien
anti_link = self.settings.get("anti_link", False)
btn_antilink = ToggleButton(
label="Anti-Liens",
emoji="🔗",
is_enabled=anti_link,
custom_id="anti_link"
)
self.add_item(btn_antilink)
# Bouton de configuration du seuil
threshold = self.settings.get("spam_threshold", 5)
btn_threshold = discord.ui.Button(
label=f"🔢 Seuil: {threshold} msg/5s",
style=discord.ButtonStyle.blurple,
custom_id="spam_threshold"
)
btn_threshold.callback = self.open_spam_threshold_modal
self.add_item(btn_threshold)
# Bouton retour
btn_back = discord.ui.Button(
label="Retour",
style=discord.ButtonStyle.gray,
emoji="⬅️"
)
btn_back.callback = self.back_to_main
self.add_item(btn_back)
def build_raid_view(self):
"""Construit la vue Anti-Raid"""
# Toggle Anti-Raid
anti_raid = self.settings.get("anti_raid", False)
btn_antiraid = ToggleButton(
label="Anti-Raid",
emoji="⚔️",
is_enabled=anti_raid,
custom_id="anti_raid"
)
self.add_item(btn_antiraid)
# Toggle Auto-Ban
auto_ban = self.settings.get("auto_ban", False)
btn_autoban = ToggleButton(
label="Auto-Ban",
emoji="🔨",
is_enabled=auto_ban,
custom_id="auto_ban"
)
self.add_item(btn_autoban)
# Bouton de configuration du seuil
threshold = self.settings.get("raid_threshold", 3)
btn_threshold = discord.ui.Button(
label=f"🔢 Seuil: {threshold} join/10s",
style=discord.ButtonStyle.blurple,
custom_id="raid_threshold"
)
btn_threshold.callback = self.open_raid_threshold_modal
self.add_item(btn_threshold)
# Bouton retour
btn_back = discord.ui.Button(
label="Retour",
style=discord.ButtonStyle.gray,
emoji="⬅️"
)
btn_back.callback = self.back_to_main
self.add_item(btn_back)
def build_nuke_view(self):
"""Construit la vue Anti-Nuke"""
# Toggle Anti-Salon
anti_channel = self.settings.get("anti_channel_delete", False)
btn_channel = ToggleButton(
label="Anti-Salon",
emoji="📁",
is_enabled=anti_channel,
custom_id="anti_channel_delete"
)
self.add_item(btn_channel)
# Toggle Anti-Rôle
anti_role = self.settings.get("anti_role_create", False)
btn_role = ToggleButton(
label="Anti-Rôle",
emoji="🎭",
is_enabled=anti_role,
custom_id="anti_role_create"
)
self.add_item(btn_role)
# Bouton retour
btn_back = discord.ui.Button(
label="Retour",
style=discord.ButtonStyle.gray,
emoji="⬅️"
)
btn_back.callback = self.back_to_main
self.add_item(btn_back)
def build_logs_view(self):
"""Construit la vue Logs"""
# Toggle Logs
logs_enabled = self.settings.get("server_logs", False)
btn_logs = ToggleButton(
label="Logs Serveur",
emoji="📋",
is_enabled=logs_enabled,
custom_id="server_logs"
)
self.add_item(btn_logs)
# ID du channel de logs (affichage)
log_channel_id = self.settings.get("log_channel_id")
if log_channel_id:
channel = self.guild.get_channel(int(log_channel_id))
channel_mention = channel.mention if channel else f"`{log_channel_id}`"
btn_channel = discord.ui.Button(
label=f"📌 Channel: {channel.name if channel else 'Invalide'}",
style=discord.ButtonStyle.green,
disabled=True
)
else: else:
config_map = { btn_channel = discord.ui.Button(
"raid": [("anti_raid", "🛡️ Anti-Raid"), ("auto_ban", "🔨 Auto-Ban")], label="📌 Aucun channel configuré",
"spam": [("anti_spam", "🛡️ Anti-Spam"), ("anti_link", "🔗 Anti-Link"), ("anti_mention", "👥 Anti-Mention")], style=discord.ButtonStyle.red,
"nuke": [("anti_channel_delete", "📁 Anti-Salon"), ("anti_role_create", "🎭 Anti-Rôle")], disabled=True
"logs": [("server_logs", "📋 Activer Logs")] )
} self.add_item(btn_channel)
# Bouton retour
btn_back = discord.ui.Button(
label="Retour",
style=discord.ButtonStyle.gray,
emoji="⬅️"
)
btn_back.callback = self.back_to_main
self.add_item(btn_back)
for key, label in config_map.get(self.category, []): def build_whitelist_view(self):
is_on = self.settings.get(key, False) """Construit la vue Whitelist"""
style = discord.ButtonStyle.green if is_on else discord.ButtonStyle.red # Affichage du nombre avec style
status = "" if is_on else "" btn_count = discord.ui.Button(
btn = discord.ui.Button(label=f"{label} [{status}]", style=style, custom_id=key) label=f"👥 Whitelist: {self.whitelist_count} utilisateur(s)",
btn.callback = self.toggle_setting style=discord.ButtonStyle.blurple,
self.add_item(btn) disabled=True
)
# BOUTON SPÉCIAL : RÉGLAGE SEUIL (uniquement dans spam) self.add_item(btn_count)
if self.category == "spam":
btn_threshold = discord.ui.Button(label=f"🔢 Seuil: {self.settings.get('spam_threshold', 5)}", style=discord.ButtonStyle.blurple) # Menu dropdown pour gérer la whitelist
btn_threshold.callback = self.open_threshold_modal if self.whitelist_count > 0:
self.add_item(btn_threshold) self.add_item(WhitelistSelect(self.guild, self))
else:
# Message si whitelist vide
btn_empty = discord.ui.Button(
label="📭 Aucun utilisateur dans la whitelist",
style=discord.ButtonStyle.gray,
disabled=True
)
self.add_item(btn_empty)
# Bouton ajouter
btn_add = discord.ui.Button(
label=" Ajouter",
style=discord.ButtonStyle.green,
emoji="",
custom_id="whitelist_add"
)
btn_add.callback = self.open_whitelist_modal
self.add_item(btn_add)
# Bouton retour
btn_back = discord.ui.Button(
label="Retour",
style=discord.ButtonStyle.gray,
emoji="⬅️"
)
btn_back.callback = self.back_to_main
self.add_item(btn_back)
back_btn = discord.ui.Button(label="⬅️ Retour", style=discord.ButtonStyle.gray) async def toggle_setting(self, interaction: discord.Interaction, key: str):
back_btn.callback = self.back_to_main """Bascule un paramètre"""
self.add_item(back_btn)
async def toggle_setting(self, interaction: discord.Interaction):
key = interaction.data['custom_id']
self.settings[key] = not self.settings.get(key, False) self.settings[key] = not self.settings.get(key, False)
save_settings(self.guild.id, self.settings) save_settings(self.guild.id, self.settings)
self.build_view() self.build_view()
await interaction.response.edit_message(view=self) await interaction.response.edit_message(view=self)
async def open_threshold_modal(self, interaction: discord.Interaction): async def open_spam_threshold_modal(self, interaction: discord.Interaction):
await interaction.response.send_modal(ThresholdModal(self.guild.id, self.settings, self)) """Ouvre le modal de configuration du seuil spam"""
await interaction.response.send_modal(
SpamThresholdModal(self.guild.id, self.settings, self)
)
async def open_raid_threshold_modal(self, interaction: discord.Interaction):
"""Ouvre le modal de configuration du seuil raid"""
await interaction.response.send_modal(
RaidThresholdModal(self.guild.id, self.settings, self)
)
async def open_whitelist_modal(self, interaction: discord.Interaction):
"""Ouvre le modal d'ajout à la whitelist"""
await interaction.response.send_modal(
WhitelistModal(self.guild.id, self)
)
async def back_to_main(self, interaction: discord.Interaction): async def back_to_main(self, interaction: discord.Interaction):
"""Retour au menu principal"""
self.category = None self.category = None
self.build_view() self.build_view()
embed = self.create_main_embed() embed = self.create_main_embed(interaction.user)
await interaction.response.edit_message(embed=embed, view=self) await interaction.response.edit_message(embed=embed, view=self)
def create_main_embed(self): def create_main_embed(self, user: discord.Member = None) -> discord.Embed:
embed = discord.Embed(title="🛡️ Centre de Sécurité Kuby", description="Interface modulaire de protection.", color=0x2b2d31) """Crée l'embed du menu principal"""
if self.guild.icon: embed.set_thumbnail(url=self.guild.icon.url) embed = discord.Embed(
embed.add_field(name="Propriétaire", value=self.guild.owner.mention) title="🛡️ Centre de Sécurité Kuby",
description="Gérez la protection de votre serveur",
color=0x2b2d31
)
if self.guild.icon:
embed.set_thumbnail(url=self.guild.icon.url)
# Informations du serveur
embed.add_field(
name="🏠 Serveur",
value=self.guild.name,
inline=True
)
embed.add_field(
name="👤 Propriétaire",
value=self.guild.owner.mention if self.guild.owner else "Inconnu",
inline=True
)
# Statut global
active_count = sum([
self.settings.get("anti_spam", False),
self.settings.get("anti_link", False),
self.settings.get("anti_raid", False),
self.settings.get("auto_ban", False),
self.settings.get("anti_channel_delete", False),
self.settings.get("anti_role_create", False),
self.settings.get("server_logs", False)
])
total_options = 7
status = "🟢 Actif" if active_count > 0 else "🔴 Inactif"
embed.add_field(
name="⚡ Protection",
value=f"{status} ({active_count}/{total_options})",
inline=True
)
# Avertissement si pas admin
if user and not is_immune(self.guild, user):
embed.set_footer(text="⚠️ Vous n'êtes pas administrateur. Accès restreint.")
return embed return embed
class CategorySelect(discord.ui.Select): def create_category_embed(self, title: str, description: str) -> discord.Embed:
"""Crée un embed pour une catégorie"""
embed = discord.Embed(
title=title,
description=description,
color=0x2b2d31
)
if self.guild.icon:
embed.set_thumbnail(url=self.guild.icon.url)
return embed
class ToggleButton(discord.ui.Button):
"""Bouton de bascule avec indicateur de statut"""
def __init__(self, label: str, emoji: str, is_enabled: bool, custom_id: str):
style = discord.ButtonStyle.green if is_enabled else discord.ButtonStyle.red
status = "" if is_enabled else ""
super().__init__(
label=f"{label} [{status}]",
style=style,
custom_id=custom_id,
emoji=emoji
)
async def callback(self, interaction: discord.Interaction):
view: SecurityView = self.view
key = interaction.data['custom_id']
# Vérifier les permissions
if not is_immune(view.guild, interaction.user):
return await interaction.response.send_message(
"❌ Permissions insuffisantes.",
ephemeral=True
)
# Basculer le paramètre
view.settings[key] = not view.settings.get(key, False)
save_settings(view.guild.id, view.settings)
view.build_view()
await interaction.response.edit_message(view=view)
class MainCategorySelect(discord.ui.Select):
"""Menu de sélection des catégories"""
def __init__(self): def __init__(self):
options = [ options = [
discord.SelectOption(label="Anti-Raid", value="raid", emoji="⚔️"), discord.SelectOption(
discord.SelectOption(label="Anti-Spam", value="spam", emoji="🛡️"), label="Anti-Spam",
discord.SelectOption(label="Anti-Nuke", value="nuke", emoji="☢️"), emoji="🛡️",
discord.SelectOption(label="Logs", value="logs", emoji="📋") value="spam",
description="Protection contre le spam et les liens"
),
discord.SelectOption(
label="Anti-Raid",
emoji="⚔️",
value="raid",
description="Protection contre les raids"
),
discord.SelectOption(
label="Anti-Nuke",
emoji="☢️",
value="nuke",
description="Protection contre la destruction du serveur"
),
discord.SelectOption(
label="Logs",
emoji="📋",
value="logs",
description="Journalisation des événements"
),
discord.SelectOption(
label="Whitelist",
emoji="👥",
value="whitelist",
description="Gérer les utilisateurs immunisés"
)
] ]
super().__init__(placeholder="Sélectionnez un module...", options=options) super().__init__(
placeholder="Sélectionnez une catégorie...",
options=options,
min_values=1,
max_values=1
)
async def callback(self, interaction: discord.Interaction): async def callback(self, interaction: discord.Interaction):
view: SecurityView = self.view view: SecurityView = self.view
view.category = self.values[0] view.category = self.values[0]
view.build_view() view.build_view()
embed = discord.Embed(title=f"⚙️ Configuration : {view.category.upper()}", color=0x2b2d31)
# Créer l'embed de la catégorie
category_embeds = {
"spam": view.create_category_embed(
"Configuration Anti-Spam",
"Paramètres de protection contre le spam et les liens.\n\n"
"• **Anti-Spam** : Détecte les messages répétitifs\n"
"• **Anti-Liens** : Bloque les liens Discord et URLs"
),
"raid": view.create_category_embed(
"Configuration Anti-Raid",
"Paramètres de protection contre les raids.\n\n"
"• **Anti-Raid** : Détecte les vagues de join\n"
"• **Auto-Ban** : Banne automatiquement les raiders"
),
"nuke": view.create_category_embed(
"Configuration Anti-Nuke",
"Paramètres de protection contre la destruction.\n\n"
"• **Anti-Salon** : Protège les salons\n"
"• **Anti-Rôle** : Protège les rôles"
),
"logs": view.create_category_embed(
"Configuration Logs",
"Paramètres de journalisation des événements.\n\n"
"• **Logs Serveur** : Active la journalisation\n"
"• **Channel** : Salon où seront envoyés les logs"
),
"whitelist": view.create_category_embed(
"Gestion Whitelist",
"Gérez les utilisateurs immunisés aux protections.\n\n"
"Ces utilisateurs ne seront jamais sanctionnés."
)
}
embed = category_embeds.get(view.category, view.create_main_embed())
await interaction.response.edit_message(embed=embed, view=view) await interaction.response.edit_message(embed=embed, view=view)
# --- COG ---
class WhitelistSelect(discord.ui.Select):
"""Menu dropdown pour voir et gérer les utilisateurs whitelistés"""
def __init__(self, guild: discord.Guild, view: 'SecurityView'):
self.guild = guild
self.parent_view = view
whitelist = load_whitelist(guild.id)
options = []
for user_id in whitelist:
# Essayer de trouver l'utilisateur
member = guild.get_member(user_id)
if member:
label = f"{member.name} ({member.id})"
emoji = "👤"
else:
label = f"ID: {user_id} (non trouvé)"
emoji = ""
options.append(
discord.SelectOption(
label=label[:100], # Discord limite à 100 chars
value=str(user_id),
emoji=emoji,
description="Cliquez pour retirer de la whitelist"
)
)
super().__init__(
placeholder="👥 Gérer la whitelist (cliquez pour retirer)...",
options=options,
min_values=1,
max_values=1
)
async def callback(self, interaction: discord.Interaction):
"""Gère la sélection d'un utilisateur à retirer"""
user_id = int(self.values[0])
# Charger et modifier la whitelist
data = {}
if os.path.exists(WHITELIST_FILE):
try:
with open(WHITELIST_FILE, "r", encoding='utf-8') as f:
data = json.load(f)
except (json.JSONDecodeError, FileNotFoundError):
pass
guild_whitelist = data.get(str(self.guild.id), [])
if user_id not in guild_whitelist:
return await interaction.response.send_message(
"❌ Utilisateur non trouvé dans la whitelist.",
ephemeral=True
)
# Retirer l'utilisateur
guild_whitelist.remove(user_id)
data[str(self.guild.id)] = guild_whitelist
os.makedirs(os.path.dirname(WHITELIST_FILE), exist_ok=True)
with open(WHITELIST_FILE, "w", encoding='utf-8') as f:
json.dump(data, f, indent=4, ensure_ascii=False)
# Mettre à jour l'affichage
self.parent_view.whitelist_count = len(guild_whitelist)
self.parent_view.build_view()
await interaction.response.edit_message(view=self.parent_view)
await interaction.followup.send(
"✅ Utilisateur retiré de la whitelist.",
ephemeral=True
)
class Security(commands.Cog): class Security(commands.Cog):
"""Cog principal de gestion de la sécurité"""
def __init__(self, bot): def __init__(self, bot):
self.bot = bot self.bot = bot
@app_commands.command(name="security", description="Ouvrir le panneau Kuby") @app_commands.command(
name="security",
description="🛡️ Ouvrir le panneau de sécurité Kuby"
)
async def security(self, interaction: discord.Interaction): async def security(self, interaction: discord.Interaction):
if not (interaction.user.guild_permissions.administrator or """
interaction.user.id == interaction.guild.owner_id or Commande principale pour ouvrir l'interface de sécurité.
is_whitelisted(interaction.guild.id, interaction.user.id)): Accessible uniquement aux administrateurs et propriétaires whitelistés.
return await interaction.response.send_message("❌ Accès refusé.", ephemeral=True) """
# Vérification des permissions
if not is_immune(interaction.guild, interaction.user):
if interaction.response.is_done():
return await interaction.followup.send(
"❌ **Accès refusé**\n\nVous devez être administrateur, propriétaire du serveur, ou être dans la whitelist pour accéder à ce panneau.",
ephemeral=True
)
return await interaction.response.send_message(
"❌ **Accès refusé**\n\nVous devez être administrateur, propriétaire du serveur, ou être dans la whitelist pour accéder à ce panneau.",
ephemeral=True
)
# Chargement des paramètres
settings = load_settings(interaction.guild.id) settings = load_settings(interaction.guild.id)
# Création de la vue
view = SecurityView(interaction, settings) view = SecurityView(interaction, settings)
await interaction.response.send_message(embed=view.create_main_embed(), view=view) embed = view.create_main_embed(interaction.user)
if interaction.response.is_done():
await interaction.followup.send(
embed=embed,
view=view,
ephemeral=True
)
else:
await interaction.response.send_message(
embed=embed,
view=view,
ephemeral=True
)
@security.error
async def security_error(self, interaction: discord.Interaction, error: app_commands.AppCommandError):
"""Gestion des erreurs de la commande security"""
# Ne pas envoyer de message si l'interaction a déjà été traitée
if interaction.response.is_done():
return
if isinstance(error, app_commands.MissingPermissions):
await interaction.response.send_message(
"❌ Permissions insuffisantes.",
ephemeral=True
)
else:
await interaction.response.send_message(
f"❌ Une erreur est survenue.",
ephemeral=True
)
async def setup(bot): async def setup(bot):
await bot.add_cog(Security(bot)) """Setup du cog de sécurité"""
await bot.add_cog(Security(bot))

View file

@ -0,0 +1,59 @@
[
{
"timestamp": "2026-01-02T13:30:27.725564",
"event_type": "deleted",
"message_id": 1456625676537823243,
"channel_id": 1369669999677145098,
"channel_name": "╭💬・général",
"author_id": 971446412690722826,
"author_name": "gameurpro12",
"author_roles": [
1369669999345537145,
1369669999366770723,
1403041729330024508,
1369669999366770726
],
"content": "gg",
"attachments": [],
"embeds": [],
"details": {}
},
{
"timestamp": "2026-01-02T13:30:27.754495",
"event_type": "deleted",
"message_id": 1456625676537823243,
"channel_id": 1369669999677145098,
"channel_name": "╭💬・général",
"author_id": 971446412690722826,
"author_name": "gameurpro12",
"author_roles": [
1369669999345537145,
1369669999366770723,
1403041729330024508,
1369669999366770726
],
"content": "gg",
"attachments": [],
"embeds": [],
"details": {}
},
{
"timestamp": "2026-01-02T13:30:37.215876",
"event_type": "deleted",
"message_id": 1456625680153444448,
"channel_id": 1369669999677145098,
"channel_name": "╭💬・général",
"author_id": 971446412690722826,
"author_name": "gameurpro12",
"author_roles": [
1369669999345537145,
1369669999366770723,
1403041729330024508,
1369669999366770726
],
"content": "gg",
"attachments": [],
"embeds": [],
"details": {}
}
]