Kuby/commandes/security.py

1017 lines
36 KiB
Python
Executable file
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import discord
from discord import app_commands
from discord.ext import commands
import json
import os
from typing import Optional
# Chemins des fichiers
SETTINGS_FILE = "data/security_settings.json"
WHITELIST_FILE = "data/whitelist.json"
# --- UTILITAIRES ---
def load_settings(guild_id: int) -> dict:
"""Charge les paramètres de sécurité pour un serveur"""
if not os.path.exists(SETTINGS_FILE):
return {}
try:
with open(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 sécurité"""
data = {}
if os.path.exists(SETTINGS_FILE):
try:
with open(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(SETTINGS_FILE), exist_ok=True)
with open(SETTINGS_FILE, "w", encoding='utf-8') as f:
json.dump(data, f, indent=4, ensure_ascii=False)
def load_whitelist(guild_id: int) -> list:
"""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:
with open(WHITELIST_FILE, "r", encoding='utf-8') as f:
data = json.load(f)
return user_id in data.get(str(guild_id), [])
except (json.JSONDecodeError, FileNotFoundError):
return False
def is_immune(guild, user) -> bool:
"""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(
label="Messages max autorisés",
placeholder="Entre 3 et 10 (défaut: 5)",
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 > 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)
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-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
)
# --- MODALS DE CONFIGURATION WHITELIST ---
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 ---
class SecurityView(discord.ui.View):
"""Vue principale du panneau de sécurité"""
def __init__(self, interaction: discord.Interaction, settings: dict, category: Optional[str] = None, bot=None):
super().__init__(timeout=300) # 5 minutes timeout
self.interaction = interaction
self.guild = interaction.guild
self.settings = settings
self.category = category
self.bot = bot
self.whitelist_count = len(load_whitelist(self.guild.id))
self.build_view()
def build_view(self):
"""Construit les boutons selon la catégorie"""
self.clear_items()
if self.category is None:
# 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()
elif self.category == "rules":
self.build_rules_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:
btn_channel = discord.ui.Button(
label="📌 Aucun channel configuré",
style=discord.ButtonStyle.red,
disabled=True
)
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)
def build_whitelist_view(self):
"""Construit la vue Whitelist"""
# Affichage du nombre avec style
btn_count = discord.ui.Button(
label=f"👥 Whitelist: {self.whitelist_count} utilisateur(s)",
style=discord.ButtonStyle.blurple,
disabled=True
)
self.add_item(btn_count)
# Menu dropdown pour gérer la whitelist
if self.whitelist_count > 0:
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)
def build_rules_view(self):
"""Construit la vue Règlement"""
from commandes.modules_security.rules import (
RulesTitleModal, RulesContentModal, RulesChannelModal,
RulesRoleModal, ToggleRulesButton, save_settings
)
# Toggle Activer/Désactiver
rules_enabled = self.settings.get("rules_enabled", False)
btn_toggle = ToggleButton(
label="Règlement",
emoji="📜",
is_enabled=rules_enabled,
custom_id="rules_enabled"
)
# Bouton pour modifier le titre
btn_title = discord.ui.Button(
label="Éditer le titre",
style=discord.ButtonStyle.blurple,
emoji="📝"
)
btn_title.callback = self.open_rules_title_modal
self.add_item(btn_title)
# Bouton pour modifier le contenu
btn_content = discord.ui.Button(
label="Éditer le contenu",
style=discord.ButtonStyle.blurple,
emoji="📄"
)
btn_content.callback = self.open_rules_content_modal
self.add_item(btn_content)
# Bouton pour choisir le salon
rules_channel_id = self.settings.get("rules_channel_id")
if rules_channel_id:
channel = self.guild.get_channel(int(rules_channel_id))
btn_channel = discord.ui.Button(
label=f"Salon: {channel.name if channel else 'Invalide'}",
style=discord.ButtonStyle.green,
custom_id="rules_channel"
)
else:
btn_channel = discord.ui.Button(
label="Aucun salon configuré",
style=discord.ButtonStyle.red,
custom_id="rules_channel"
)
btn_channel.callback = self.open_rules_channel_modal
self.add_item(btn_channel)
# Bouton pour choisir le rôle d'acceptation
rules_role_id = self.settings.get("rules_accept_role_id")
if rules_role_id:
role = self.guild.get_role(int(rules_role_id))
btn_role = discord.ui.Button(
label=f"Rôle: {role.name if role else 'Invalide'}",
style=discord.ButtonStyle.green,
custom_id="rules_role"
)
else:
btn_role = discord.ui.Button(
label="Aucun rôle configuré",
style=discord.ButtonStyle.red,
custom_id="rules_role"
)
btn_role.callback = self.open_rules_role_modal
self.add_item(btn_role)
# Bouton pour choisir le type de message
message_type = self.settings.get("rules_message_type", "embed")
type_label = "Embed" if message_type == "embed" else "Message simple"
btn_message_type = discord.ui.Button(
label=f"Type: {type_label}",
style=discord.ButtonStyle.blurple,
emoji="📝",
custom_id="rules_message_type"
)
btn_message_type.callback = self.open_rules_message_type_modal
self.add_item(btn_message_type)
# Bouton pour envoyer le règlement
btn_send = discord.ui.Button(
label="Envoyer le règlement",
style=discord.ButtonStyle.green,
emoji="🚀"
)
btn_send.callback = self.send_rules
self.add_item(btn_send)
# 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)
async def toggle_setting(self, interaction: discord.Interaction, key: str):
"""Bascule un paramètre"""
self.settings[key] = not self.settings.get(key, False)
save_settings(self.guild.id, self.settings)
self.build_view()
await interaction.response.edit_message(view=self)
async def open_spam_threshold_modal(self, interaction: discord.Interaction):
"""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 open_rules_title_modal(self, interaction: discord.Interaction):
"""Ouvre le modal de configuration du titre du règlement"""
from .modules_security.rules import RulesTitleModal
await interaction.response.send_modal(
RulesTitleModal(self.guild.id, self.settings)
)
async def open_rules_content_modal(self, interaction: discord.Interaction):
"""Ouvre le modal de configuration du contenu du règlement"""
from .modules_security.rules import RulesContentModal
await interaction.response.send_modal(
RulesContentModal(self.guild.id, self.settings)
)
async def open_rules_channel_modal(self, interaction: discord.Interaction):
"""Ouvre le modal de sélection du salon du règlement"""
from .modules_security.rules import RulesChannelModal
await interaction.response.send_modal(
RulesChannelModal(self.guild.id, self.settings)
)
async def open_rules_role_modal(self, interaction: discord.Interaction):
"""Ouvre le modal de configuration du rôle d'acceptation"""
from .modules_security.rules import RulesRoleModal
await interaction.response.send_modal(
RulesRoleModal(self.guild.id, self.settings)
)
async def open_rules_message_type_modal(self, interaction: discord.Interaction):
"""Ouvre le modal de sélection du type de message"""
from .modules_security.rules import RulesMessageTypeModal
await interaction.response.send_modal(
RulesMessageTypeModal(self.guild.id, self.settings)
)
async def send_rules(self, interaction: discord.Interaction):
"""Envoie le règlement dans le salon configuré"""
from commandes.modules_security.rules import RulesCog
# Vérifier que tous les paramètres sont configurés
rules_channel_id = self.settings.get("rules_channel_id")
rules_accept_role_id = self.settings.get("rules_accept_role_id")
if not rules_channel_id:
return await interaction.response.send_message(
"❌ Aucun salon configuré pour l'envoi du règlement.",
ephemeral=True
)
if not rules_accept_role_id:
return await interaction.response.send_message(
"❌ Aucun rôle d'acceptation configuré.",
ephemeral=True
)
# Vérifier le salon
channel = self.guild.get_channel(int(rules_channel_id))
if not channel:
return await interaction.response.send_message(
"❌ Le salon configuré n'existe plus.",
ephemeral=True
)
# Vérifier le rôle
role = self.guild.get_role(int(rules_accept_role_id))
if not role:
return await interaction.response.send_message(
"❌ Le rôle d'acceptation n'existe plus.",
ephemeral=True
)
# Envoyer le règlement
try:
rules_cog = self.bot.get_cog("RulesCog")
if rules_cog:
message = await rules_cog.send_rules_message(channel, self.settings)
if message:
# Activer le règlement
self.settings["rules_enabled"] = True
save_settings(self.guild.id, self.settings)
self.settings = load_settings(self.guild.id)
self.build_view()
await interaction.response.edit_message(view=self)
await interaction.followup.send(
f"✅ Règlement envoyé avec succès dans {channel.mention} !",
ephemeral=True
)
else:
await interaction.followup.send(
"❌ Erreur lors de l'envoi du règlement.",
ephemeral=True
)
else:
await interaction.followup.send(
"❌ Le cog Rules n'est pas chargé.",
ephemeral=True
)
except Exception as e:
await interaction.followup.send(
f"❌ Erreur: {e}",
ephemeral=True
)
async def back_to_main(self, interaction: discord.Interaction):
"""Retour au menu principal"""
self.category = None
self.build_view()
embed = self.create_main_embed(interaction.user)
await interaction.response.edit_message(embed=embed, view=self)
def create_main_embed(self, user: discord.Member = None) -> discord.Embed:
"""Crée l'embed du menu principal"""
embed = discord.Embed(
title="🛡️ Centre de Sécurité Kuby",
description="Gérez la protection de votre serveur",
color=0x7289DA
)
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
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=0x7289DA
)
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):
options = [
discord.SelectOption(
label="Anti-Spam",
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="Règlement",
emoji="📜",
value="rules",
description="Créer et envoyer un règlement"
),
discord.SelectOption(
label="Whitelist",
emoji="👥",
value="whitelist",
description="Gérer les utilisateurs immunisés"
)
]
super().__init__(
placeholder="Sélectionnez une catégorie...",
options=options,
min_values=1,
max_values=1
)
async def callback(self, interaction: discord.Interaction):
view: SecurityView = self.view
view.category = self.values[0]
view.build_view()
# 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"
),
"rules": view.create_category_embed(
"Configuration Règlement",
"Créez et envoyez un règlement pour votre serveur.\n\n"
"• **Titre** : Personnalisez le titre du règlement\n"
"• **Contenu** : Rédigez vos règles\n"
"• **Salon** : Choisissez où l'envoyer\n"
"• **Rôle** : Rôle attribué à l'acceptation\n\n"
"Les membres devront accepter le règlement pour accéder au serveur."
),
"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)
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):
"""Cog principal de gestion de la sécurité"""
def __init__(self, bot):
self.bot = bot
@app_commands.command(
name="security",
description="🛡️ Ouvrir le panneau de sécurité Kuby"
)
async def security(self, interaction: discord.Interaction):
"""
Commande principale pour ouvrir l'interface de sécurité.
Accessible uniquement aux administrateurs et propriétaires whitelistés.
"""
# 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)
# Création de la vue
view = SecurityView(interaction, settings, bot=self.bot)
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):
"""Setup du cog de sécurité"""
await bot.add_cog(Security(bot))