Migration de Kuby vers disnake

This commit is contained in:
Mathis 2026-05-06 17:07:09 +02:00
parent dc6e235f27
commit c8c579eefe
36 changed files with 1205 additions and 1253 deletions

View file

@ -1,6 +1,5 @@
import discord
from discord import app_commands
from discord.ext import commands
import disnake
from disnake.ext import commands
import json
import os
from typing import Optional
@ -70,9 +69,9 @@ def is_immune(guild, user) -> bool:
# --- MODALS DE CONFIGURATION ---
class SpamThresholdModal(discord.ui.Modal, title="🛡️ Seuil Anti-Spam"):
class SpamThresholdModal(disnake.ui.Modal, title="🛡️ Seuil Anti-Spam"):
"""Modal pour configurer le seuil anti-spam"""
threshold_input = discord.ui.TextInput(
threshold_input = disnake.ui.TextInput(
label="Messages max autorisés",
placeholder="Entre 3 et 10 (défaut: 5)",
min_length=1,
@ -86,7 +85,7 @@ class SpamThresholdModal(discord.ui.Modal, title="🛡️ Seuil Anti-Spam"):
self.settings = settings
self.parent_view = view
async def on_submit(self, interaction: discord.Interaction):
async def on_submit(self, interaction: disnake.Interaction):
if not self.threshold_input.value.isdigit():
return await interaction.response.send_message(
"❌ Erreur : Veuillez entrer un nombre entier.",
@ -111,9 +110,9 @@ class SpamThresholdModal(discord.ui.Modal, title="🛡️ Seuil Anti-Spam"):
ephemeral=True
)
class RaidThresholdModal(discord.ui.Modal, title="⚔️ Seuil Anti-Raid"):
class RaidThresholdModal(disnake.ui.Modal, title="⚔️ Seuil Anti-Raid"):
"""Modal pour configurer le seuil anti-raid"""
threshold_input = discord.ui.TextInput(
threshold_input = disnake.ui.TextInput(
label="Joins max en 10 secondes",
placeholder="Entre 2 et 10 (défaut: 3)",
min_length=1,
@ -127,7 +126,7 @@ class RaidThresholdModal(discord.ui.Modal, title="⚔️ Seuil Anti-Raid"):
self.settings = settings
self.parent_view = view
async def on_submit(self, interaction: discord.Interaction):
async def on_submit(self, interaction: disnake.Interaction):
if not self.threshold_input.value.isdigit():
return await interaction.response.send_message(
"❌ Erreur : Veuillez entrer un nombre entier.",
@ -154,9 +153,9 @@ class RaidThresholdModal(discord.ui.Modal, title="⚔️ Seuil Anti-Raid"):
# --- MODALS DE CONFIGURATION WHITELIST ---
class WhitelistModal(discord.ui.Modal, title="👤 Ajouter à la Whitelist"):
class WhitelistModal(disnake.ui.Modal, title="👤 Ajouter à la Whitelist"):
"""Modal pour ajouter un utilisateur à la whitelist"""
user_input = discord.ui.TextInput(
user_input = disnake.ui.TextInput(
label="ID de l'utilisateur",
placeholder="Entrez l'ID Discord de l'utilisateur",
min_length=15,
@ -169,7 +168,7 @@ class WhitelistModal(discord.ui.Modal, title="👤 Ajouter à la Whitelist"):
self.guild_id = guild_id
self.parent_view = view
async def on_submit(self, interaction: discord.Interaction):
async def on_submit(self, interaction: disnake.Interaction):
if not self.user_input.value.isdigit():
return await interaction.response.send_message(
"❌ Erreur : ID invalide.",
@ -213,10 +212,10 @@ class WhitelistModal(discord.ui.Modal, title="👤 Ajouter à la Whitelist"):
# --- VUES DE L'INTERFACE ---
class SecurityView(discord.ui.View):
class SecurityView(disnake.ui.View):
"""Vue principale du panneau de sécurité"""
def __init__(self, interaction: discord.Interaction, settings: dict, category: Optional[str] = None, bot=None):
def __init__(self, interaction: disnake.Interaction, settings: dict, category: Optional[str] = None, bot=None):
super().__init__(timeout=300) # 5 minutes timeout
self.interaction = interaction
self.guild = interaction.guild
@ -270,18 +269,18 @@ class SecurityView(discord.ui.View):
# Bouton de configuration du seuil
threshold = self.settings.get("spam_threshold", 5)
btn_threshold = discord.ui.Button(
btn_threshold = disnake.ui.Button(
label=f"🔢 Seuil: {threshold} msg/5s",
style=discord.ButtonStyle.blurple,
style=disnake.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(
btn_back = disnake.ui.Button(
label="Retour",
style=discord.ButtonStyle.gray,
style=disnake.ButtonStyle.gray,
emoji="⬅️"
)
btn_back.callback = self.back_to_main
@ -311,18 +310,18 @@ class SecurityView(discord.ui.View):
# Bouton de configuration du seuil
threshold = self.settings.get("raid_threshold", 3)
btn_threshold = discord.ui.Button(
btn_threshold = disnake.ui.Button(
label=f"🔢 Seuil: {threshold} join/10s",
style=discord.ButtonStyle.blurple,
style=disnake.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(
btn_back = disnake.ui.Button(
label="Retour",
style=discord.ButtonStyle.gray,
style=disnake.ButtonStyle.gray,
emoji="⬅️"
)
btn_back.callback = self.back_to_main
@ -351,9 +350,9 @@ class SecurityView(discord.ui.View):
self.add_item(btn_role)
# Bouton retour
btn_back = discord.ui.Button(
btn_back = disnake.ui.Button(
label="Retour",
style=discord.ButtonStyle.gray,
style=disnake.ButtonStyle.gray,
emoji="⬅️"
)
btn_back.callback = self.back_to_main
@ -376,23 +375,23 @@ class SecurityView(discord.ui.View):
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(
btn_channel = disnake.ui.Button(
label=f"📌 Channel: {channel.name if channel else 'Invalide'}",
style=discord.ButtonStyle.green,
style=disnake.ButtonStyle.green,
disabled=True
)
else:
btn_channel = discord.ui.Button(
btn_channel = disnake.ui.Button(
label="📌 Aucun channel configuré",
style=discord.ButtonStyle.red,
style=disnake.ButtonStyle.red,
disabled=True
)
self.add_item(btn_channel)
# Bouton retour
btn_back = discord.ui.Button(
btn_back = disnake.ui.Button(
label="Retour",
style=discord.ButtonStyle.gray,
style=disnake.ButtonStyle.gray,
emoji="⬅️"
)
btn_back.callback = self.back_to_main
@ -401,9 +400,9 @@ class SecurityView(discord.ui.View):
def build_whitelist_view(self):
"""Construit la vue Whitelist"""
# Affichage du nombre avec style
btn_count = discord.ui.Button(
btn_count = disnake.ui.Button(
label=f"👥 Whitelist: {self.whitelist_count} utilisateur(s)",
style=discord.ButtonStyle.blurple,
style=disnake.ButtonStyle.blurple,
disabled=True
)
self.add_item(btn_count)
@ -413,17 +412,17 @@ class SecurityView(discord.ui.View):
self.add_item(WhitelistSelect(self.guild, self))
else:
# Message si whitelist vide
btn_empty = discord.ui.Button(
btn_empty = disnake.ui.Button(
label="📭 Aucun utilisateur dans la whitelist",
style=discord.ButtonStyle.gray,
style=disnake.ButtonStyle.gray,
disabled=True
)
self.add_item(btn_empty)
# Bouton ajouter
btn_add = discord.ui.Button(
btn_add = disnake.ui.Button(
label=" Ajouter",
style=discord.ButtonStyle.green,
style=disnake.ButtonStyle.green,
emoji="",
custom_id="whitelist_add"
)
@ -431,9 +430,9 @@ class SecurityView(discord.ui.View):
self.add_item(btn_add)
# Bouton retour
btn_back = discord.ui.Button(
btn_back = disnake.ui.Button(
label="Retour",
style=discord.ButtonStyle.gray,
style=disnake.ButtonStyle.gray,
emoji="⬅️"
)
btn_back.callback = self.back_to_main
@ -458,18 +457,18 @@ class SecurityView(discord.ui.View):
# Bouton pour modifier le titre
btn_title = discord.ui.Button(
btn_title = disnake.ui.Button(
label="Éditer le titre",
style=discord.ButtonStyle.blurple,
style=disnake.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(
btn_content = disnake.ui.Button(
label="Éditer le contenu",
style=discord.ButtonStyle.blurple,
style=disnake.ButtonStyle.blurple,
emoji="📄"
)
btn_content.callback = self.open_rules_content_modal
@ -479,15 +478,15 @@ class SecurityView(discord.ui.View):
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(
btn_channel = disnake.ui.Button(
label=f"Salon: {channel.name if channel else 'Invalide'}",
style=discord.ButtonStyle.green,
style=disnake.ButtonStyle.green,
custom_id="rules_channel"
)
else:
btn_channel = discord.ui.Button(
btn_channel = disnake.ui.Button(
label="Aucun salon configuré",
style=discord.ButtonStyle.red,
style=disnake.ButtonStyle.red,
custom_id="rules_channel"
)
btn_channel.callback = self.open_rules_channel_modal
@ -497,15 +496,15 @@ class SecurityView(discord.ui.View):
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(
btn_role = disnake.ui.Button(
label=f"Rôle: {role.name if role else 'Invalide'}",
style=discord.ButtonStyle.green,
style=disnake.ButtonStyle.green,
custom_id="rules_role"
)
else:
btn_role = discord.ui.Button(
btn_role = disnake.ui.Button(
label="Aucun rôle configuré",
style=discord.ButtonStyle.red,
style=disnake.ButtonStyle.red,
custom_id="rules_role"
)
btn_role.callback = self.open_rules_role_modal
@ -514,9 +513,9 @@ class SecurityView(discord.ui.View):
# 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(
btn_message_type = disnake.ui.Button(
label=f"Type: {type_label}",
style=discord.ButtonStyle.blurple,
style=disnake.ButtonStyle.blurple,
emoji="📝",
custom_id="rules_message_type"
)
@ -524,84 +523,84 @@ class SecurityView(discord.ui.View):
self.add_item(btn_message_type)
# Bouton pour envoyer le règlement
btn_send = discord.ui.Button(
btn_send = disnake.ui.Button(
label="Envoyer le règlement",
style=discord.ButtonStyle.green,
style=disnake.ButtonStyle.green,
emoji="🚀"
)
btn_send.callback = self.send_rules
self.add_item(btn_send)
# Bouton retour
btn_back = discord.ui.Button(
btn_back = disnake.ui.Button(
label="Retour",
style=discord.ButtonStyle.gray,
style=disnake.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):
async def toggle_setting(self, interaction: disnake.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):
async def open_spam_threshold_modal(self, interaction: disnake.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):
async def open_raid_threshold_modal(self, interaction: disnake.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):
async def open_whitelist_modal(self, interaction: disnake.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):
async def open_rules_title_modal(self, interaction: disnake.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):
async def open_rules_content_modal(self, interaction: disnake.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):
async def open_rules_channel_modal(self, interaction: disnake.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):
async def open_rules_role_modal(self, interaction: disnake.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):
async def open_rules_message_type_modal(self, interaction: disnake.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):
async def send_rules(self, interaction: disnake.Interaction):
"""Envoie le règlement dans le salon configuré"""
from commandes.modules_security.rules import RulesCog
@ -670,16 +669,16 @@ class SecurityView(discord.ui.View):
ephemeral=True
)
async def back_to_main(self, interaction: discord.Interaction):
async def back_to_main(self, interaction: disnake.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:
def create_main_embed(self, user: disnake.Member = None) -> disnake.Embed:
"""Crée l'embed du menu principal"""
embed = discord.Embed(
embed = disnake.Embed(
title="🛡️ Centre de Sécurité Kuby",
description="Gérez la protection de votre serveur",
color=0x7289DA
@ -726,9 +725,9 @@ class SecurityView(discord.ui.View):
return embed
def create_category_embed(self, title: str, description: str) -> discord.Embed:
def create_category_embed(self, title: str, description: str) -> disnake.Embed:
"""Crée un embed pour une catégorie"""
embed = discord.Embed(
embed = disnake.Embed(
title=title,
description=description,
color=0x7289DA
@ -740,11 +739,11 @@ class SecurityView(discord.ui.View):
return embed
class ToggleButton(discord.ui.Button):
class ToggleButton(disnake.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
style = disnake.ButtonStyle.green if is_enabled else disnake.ButtonStyle.red
status = "" if is_enabled else ""
super().__init__(
label=f"{label} [{status}]",
@ -753,7 +752,7 @@ class ToggleButton(discord.ui.Button):
emoji=emoji
)
async def callback(self, interaction: discord.Interaction):
async def callback(self, interaction: disnake.Interaction):
view: SecurityView = self.view
key = interaction.data['custom_id']
@ -771,42 +770,42 @@ class ToggleButton(discord.ui.Button):
await interaction.response.edit_message(view=view)
class MainCategorySelect(discord.ui.Select):
class MainCategorySelect(disnake.ui.Select):
"""Menu de sélection des catégories"""
def __init__(self):
options = [
discord.SelectOption(
disnake.SelectOption(
label="Anti-Spam",
emoji="🛡️",
value="spam",
description="Protection contre le spam et les liens"
),
discord.SelectOption(
disnake.SelectOption(
label="Anti-Raid",
emoji="⚔️",
value="raid",
description="Protection contre les raids"
),
discord.SelectOption(
disnake.SelectOption(
label="Anti-Nuke",
emoji="☢️",
value="nuke",
description="Protection contre la destruction du serveur"
),
discord.SelectOption(
disnake.SelectOption(
label="Logs",
emoji="📋",
value="logs",
description="Journalisation des événements"
),
discord.SelectOption(
disnake.SelectOption(
label="Règlement",
emoji="📜",
value="rules",
description="Créer et envoyer un règlement"
),
discord.SelectOption(
disnake.SelectOption(
label="Whitelist",
emoji="👥",
value="whitelist",
@ -820,7 +819,7 @@ class MainCategorySelect(discord.ui.Select):
max_values=1
)
async def callback(self, interaction: discord.Interaction):
async def callback(self, interaction: disnake.Interaction):
view: SecurityView = self.view
view.category = self.values[0]
view.build_view()
@ -871,7 +870,7 @@ class MainCategorySelect(discord.ui.Select):
await interaction.response.edit_message(embed=embed, view=view)
class WhitelistSelect(discord.ui.Select):
class WhitelistSelect(disnake.ui.Select):
"""Menu dropdown pour voir et gérer les utilisateurs whitelistés"""
def __init__(self, guild: discord.Guild, view: 'SecurityView'):
@ -890,7 +889,7 @@ class WhitelistSelect(discord.ui.Select):
label = f"ID: {user_id} (non trouvé)"
emoji = ""
options.append(
discord.SelectOption(
disnake.SelectOption(
label=label[:100], # Discord limite à 100 chars
value=str(user_id),
emoji=emoji,
@ -905,7 +904,7 @@ class WhitelistSelect(discord.ui.Select):
max_values=1
)
async def callback(self, interaction: discord.Interaction):
async def callback(self, interaction: disnake.Interaction):
"""Gère la sélection d'un utilisateur à retirer"""
user_id = int(self.values[0])
@ -955,7 +954,7 @@ class Security(commands.Cog):
name="security",
description="🛡️ Ouvrir le panneau de sécurité Kuby"
)
async def security(self, interaction: discord.Interaction):
async def security(self, interaction: disnake.Interaction):
"""
Commande principale pour ouvrir l'interface de sécurité.
Accessible uniquement aux administrateurs et propriétaires whitelistés.
@ -990,7 +989,7 @@ class Security(commands.Cog):
)
@security.error
async def security_error(self, interaction: discord.Interaction, error: app_commands.AppCommandError):
async def security_error(self, interaction: disnake.Interaction, error: app_commands.AppCommandError):
"""Gestion des erreurs de la commande security"""
# Utilisation de followup si l'interaction est déjà différée ou répondue
send_method = interaction.followup.send if interaction.response.is_done() else interaction.response.send_message