Kuby/commandes/modules_security/rules.py

503 lines
19 KiB
Python
Raw Normal View History

2026-05-06 17:07:09 +02:00
import disnake
from disnake.ext import commands
import json
import os
from typing import Optional
# Chemins des fichiers
SETTINGS_FILE = "data/security_settings.json"
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)
# --- MODALS DE CONFIGURATION DU RÈGLEMENT (BACK-END) ---
class RulesTitleModal(disnake.ui.Modal):
"""Modal pour configurer le titre du règlement"""
2026-05-06 17:07:09 +02:00
title_input = disnake.ui.TextInput(
label="Titre du règlement",
placeholder="Ex: 📜 Règlement de [Nom du Serveur]",
min_length=1,
max_length=100,
required=True
)
def __init__(self, guild_id: int, settings: dict, on_update_callback=None):
super().__init__(title="📜 Titre du Règlement", components=[self.title_input])
self.guild_id = guild_id
self.settings = settings
self.on_update_callback = on_update_callback
async def callback(self, interaction: disnake.Interaction):
self.settings["rules_title"] = self.title_input.value
save_settings(self.guild_id, self.settings)
if self.on_update_callback:
await self.on_update_callback(interaction, self.settings)
else:
await interaction.response.edit_message(view=self._create_view(interaction))
await interaction.followup.send(
f"✅ Titre du règlement mis à jour.",
ephemeral=True
)
def _create_view(self, interaction):
"""Crée la vue mise à jour"""
from commandes.security import SecurityView
# Essayer d'obtenir le bot depuis l'interaction
bot = getattr(interaction, 'client', None)
view = SecurityView(interaction, self.settings, category="rules", bot=bot)
return view
class RulesContentModal(disnake.ui.Modal):
"""Modal pour configurer le contenu du règlement"""
2026-05-06 17:07:09 +02:00
content_input = disnake.ui.TextInput(
label="Contenu du règlement",
placeholder="Entrez les règles de votre serveur...",
style=disnake.TextInputStyle.paragraph,
min_length=10,
max_length=4000,
required=True
)
def __init__(self, guild_id: int, settings: dict, on_update_callback=None):
super().__init__(title="📝 Contenu du Règlement", components=[self.content_input])
self.guild_id = guild_id
self.settings = settings
self.on_update_callback = on_update_callback
async def callback(self, interaction: disnake.Interaction):
self.settings["rules_content"] = self.content_input.value
save_settings(self.guild_id, self.settings)
if self.on_update_callback:
await self.on_update_callback(interaction, self.settings)
else:
await interaction.response.edit_message(view=self._create_view(interaction))
await interaction.followup.send(
f"✅ Contenu du règlement mis à jour.",
ephemeral=True
)
def _create_view(self, interaction):
"""Crée la vue mise à jour"""
from commandes.security import SecurityView
# Essayer d'obtenir le bot depuis l'interaction
bot = getattr(interaction, 'client', None)
view = SecurityView(interaction, self.settings, category="rules", bot=bot)
return view
class RulesChannelModal(disnake.ui.Modal):
"""Modal pour sélectionner le salon du règlement"""
2026-05-06 17:07:09 +02:00
channel_input = disnake.ui.TextInput(
label="ID du salon",
placeholder="Entrez l'ID du salon où envoyer le règlement",
min_length=15,
max_length=20,
required=True
)
def __init__(self, guild_id: int, settings: dict, on_update_callback=None):
super().__init__(title="📌 Salon du Règlement", components=[self.channel_input])
self.guild_id = guild_id
self.settings = settings
self.on_update_callback = on_update_callback
async def callback(self, interaction: disnake.Interaction):
if not self.channel_input.value.isdigit():
return await interaction.response.send_message(
"❌ Erreur : ID invalide.",
ephemeral=True
)
channel_id = int(self.channel_input.value)
channel = interaction.guild.get_channel(channel_id)
if not channel:
return await interaction.response.send_message(
"❌ Salon non trouvé. Vérifiez l'ID.",
ephemeral=True
)
self.settings["rules_channel_id"] = channel_id
save_settings(self.guild_id, self.settings)
if self.on_update_callback:
await self.on_update_callback(interaction, self.settings)
else:
await interaction.response.edit_message(view=self._create_view(interaction))
await interaction.followup.send(
f"✅ Salon du règlement défini sur {channel.mention}.",
ephemeral=True
)
def _create_view(self, interaction):
"""Crée la vue mise à jour"""
from commandes.security import SecurityView
# Essayer d'obtenir le bot depuis l'interaction
bot = getattr(interaction, 'client', None)
view = SecurityView(interaction, self.settings, category="rules", bot=bot)
return view
class RulesRoleModal(disnake.ui.Modal):
"""Modal pour configurer le rôle attribué à l'acceptation"""
2026-05-06 17:07:09 +02:00
role_input = disnake.ui.TextInput(
label="ID du rôle",
placeholder="Entrez l'ID du rôle à attribuer",
min_length=15,
max_length=20,
required=True
)
def __init__(self, guild_id: int, settings: dict, on_update_callback=None):
super().__init__(title="✅ Rôle d'Acceptation", components=[self.role_input])
self.guild_id = guild_id
self.settings = settings
self.on_update_callback = on_update_callback
async def callback(self, interaction: disnake.Interaction):
if not self.role_input.value.isdigit():
return await interaction.response.send_message(
"❌ Erreur : ID invalide.",
ephemeral=True
)
role_id = int(self.role_input.value)
role = interaction.guild.get_role(role_id)
if not role:
return await interaction.response.send_message(
"❌ Rôle non trouvé. Vérifiez l'ID.",
ephemeral=True
)
self.settings["rules_accept_role_id"] = role_id
save_settings(self.guild_id, self.settings)
if self.on_update_callback:
await self.on_update_callback(interaction, self.settings)
else:
await interaction.response.edit_message(view=self._create_view(interaction))
await interaction.followup.send(
f"✅ Rôle d'acceptation défini sur **{role.name}**.",
ephemeral=True
)
def _create_view(self, interaction):
"""Crée la vue mise à jour"""
from commandes.security import SecurityView
# Essayer d'obtenir le bot depuis l'interaction
bot = getattr(interaction, 'client', None)
view = SecurityView(interaction, self.settings, category="rules", bot=bot)
return view
class RulesMessageTypeModal(disnake.ui.Modal):
"""Modal pour choisir le type de message du règlement"""
2026-05-06 17:07:09 +02:00
message_type = disnake.ui.TextInput(
label="Type de message",
placeholder="Tapez 'embed' pour embed ou 'simple' pour message simple",
min_length=5,
max_length=6,
required=True
)
def __init__(self, guild_id: int, settings: dict, on_update_callback=None):
super().__init__(title="📝 Type de Message", components=[self.message_type])
self.guild_id = guild_id
self.settings = settings
self.on_update_callback = on_update_callback
async def callback(self, interaction: disnake.Interaction):
msg_type = self.message_type.value.lower().strip()
if msg_type not in ["embed", "simple"]:
return await interaction.response.send_message(
"❌ Erreur : Tapez 'embed' ou 'simple'.",
ephemeral=True
)
self.settings["rules_message_type"] = msg_type
save_settings(self.guild_id, self.settings)
if self.on_update_callback:
await self.on_update_callback(interaction, self.settings)
else:
await interaction.response.edit_message(view=self._create_view(interaction))
type_name = "Embed" if msg_type == "embed" else "Message simple"
await interaction.followup.send(
f"✅ Type de message défini sur **{type_name}**.",
ephemeral=True
)
def _create_view(self, interaction):
"""Crée la vue mise à jour"""
from commandes.security import SecurityView
# Essayer d'obtenir le bot depuis l'interaction
bot = getattr(interaction, 'client', None)
view = SecurityView(interaction, self.settings, category="rules", bot=bot)
return view
2026-05-06 17:07:09 +02:00
class ToggleRulesButton(disnake.ui.Button):
"""Bouton de bascule pour activer/désactiver le règlement"""
def __init__(self, is_enabled: bool):
2026-05-06 17:07:09 +02:00
style = disnake.ButtonStyle.green if is_enabled else disnake.ButtonStyle.red
status = "" if is_enabled else ""
super().__init__(
label=f"Règlement [{status}]",
style=style,
custom_id="rules_enabled",
emoji="📜"
)
2026-05-06 17:07:09 +02:00
async def callback(self, interaction: disnake.Interaction):
view = self.view
settings = view.settings
guild = interaction.guild
# Basculer le paramètre
settings["rules_enabled"] = not settings.get("rules_enabled", False)
save_settings(guild.id, settings)
# Reconstruire la vue
from commandes.security import SecurityView
# Essayer d'obtenir le bot depuis l'interaction
bot = getattr(interaction, 'client', None)
new_view = SecurityView(interaction, settings, category="rules", bot=bot)
# Recréer l'embed
embed = new_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."
)
await interaction.response.edit_message(embed=embed, view=new_view)
2026-05-06 17:07:09 +02:00
class RulesAcceptButton(disnake.ui.View):
"""Bouton persistant pour accepter le règlement"""
def __init__(self, guild_id: int, accept_role_id: int):
super().__init__(timeout=None) # Pas de timeout - bouton persistant
self.guild_id = guild_id
self.accept_role_id = accept_role_id
@disnake.ui.button(
label="J'accepte le règlement",
emoji="",
2026-05-06 17:07:09 +02:00
style=disnake.ButtonStyle.green,
custom_id="rules_accept_button"
)
async def accept_rules(self, button: disnake.ui.Button, interaction: disnake.Interaction):
"""Gère le clic sur le bouton d'acceptation"""
guild = interaction.guild
# Vérifier si le bouton est toujours activé (activé dans les paramètres)
settings = load_settings(guild.id)
if not settings.get("rules_enabled", False):
return await interaction.response.send_message(
"❌ Cette fonctionnalité a été désactivée.",
ephemeral=True
)
# Vérifier si l'utilisateur a déjà le rôle
role = guild.get_role(self.accept_role_id)
if not role:
return await interaction.response.send_message(
"❌ Le rôle d'acceptation n'est plus configuré sur ce serveur.",
ephemeral=True
)
member = interaction.user
if role in member.roles:
return await interaction.response.send_message(
"✅ Vous avez déjà accepté le règlement !",
ephemeral=True
)
# Attribuer le rôle à l'utilisateur
try:
await member.add_roles(role)
# Envoyer une confirmation éphémère
await interaction.response.send_message(
f"{member.mention}, vous avez accepté le règlement !\n"
f"Le rôle **{role.name}** vous a été attribué.",
ephemeral=True
)
2026-05-06 17:07:09 +02:00
except disnake.Forbidden:
await interaction.response.send_message(
"❌ Je n'ai pas les permissions pour attribuer ce rôle.",
ephemeral=True
)
except Exception as e:
await interaction.response.send_message(
f"❌ Une erreur est survenue: {e}",
ephemeral=True
)
class RulesCog(commands.Cog):
"""Cog pour la gestion du règlement"""
def __init__(self, bot):
self.bot = bot
self.persistent_views_added = False
async def cog_load(self):
"""Called when cog is loaded - setup persistent views"""
await self.setup_persistent_views()
async def setup_persistent_views(self):
"""Configure les views persistantes pour les règlements existants"""
if self.persistent_views_added:
return
# Charger les paramètres de tous les serveurs
if not os.path.exists(SETTINGS_FILE):
return
try:
with open(SETTINGS_FILE, "r", encoding='utf-8') as f:
all_settings = json.load(f)
# Pour chaque serveur avec le règlement activé
for guild_id_str, settings in all_settings.items():
if settings.get("rules_enabled", False) and settings.get("rules_accept_role_id"):
# Ajouter la view persistente
view = RulesAcceptButton(int(guild_id_str), settings["rules_accept_role_id"])
self.bot.add_view(view)
print(f"[Rules] View registered for guild {guild_id_str}")
self.persistent_views_added = True
except (json.JSONDecodeError, FileNotFoundError):
pass
@commands.Cog.listener()
async def on_ready(self):
"""Appelé quand le bot est prêt"""
print("🎫 Module Règles chargé avec succès!")
2026-05-06 17:07:09 +02:00
async def send_rules_message(self, channel: disnake.TextChannel, settings: dict) -> Optional[disnake.Message]:
"""Envoie le message du règlement dans un salon"""
title = settings.get("rules_title", "📜 Règlement du Serveur")
content = settings.get("rules_content", "")
accept_role_id = settings.get("rules_accept_role_id")
message_type = settings.get("rules_message_type", "embed") # Par défaut embed
if not accept_role_id:
return None
# Créer la view avec le bouton persistant
view = RulesAcceptButton(channel.guild.id, accept_role_id)
if message_type == "simple":
# Message simple
text_message = (
f"📜 **{title}**\n\n"
f"{content}\n\n"
"Bienvenue ! Pour accéder à l'ensemble des canaux du serveur, "
"vous devez accepter le règlement en cliquant sur le bouton ci-dessous.\n\n"
"En acceptant, vous confirmez avoir lu et compris les règles du serveur."
)
# Envoyer le message simple
try:
message = await channel.send(
content=text_message,
view=view
)
return message
except Exception as e:
print(f"Erreur lors de l'envoi du règlement simple: {e}")
return None
else: # embed
# Créer l'embed
2026-05-06 17:07:09 +02:00
embed = disnake.Embed(
title=title,
description=content,
color=0x2b2d31
)
embed.set_footer(text="Cliquez sur le bouton ci-dessous pour accepter le règlement")
# Envoyer seulement l'embed
try:
message = await channel.send(
embed=embed,
view=view
)
return message
except Exception as e:
print(f"Erreur lors de l'envoi du règlement embed: {e}")
return None
def format_default_rules(self, guild_name: str) -> tuple:
"""Retourne le titre et contenu par défaut du règlement"""
title = f"📜 Règlement de {guild_name}"
content = (
"Bienvenue sur notre serveur ! Avant de commencer à interagir avec la communauté, "
"veuille lire et accepter notre règlement.\n\n"
"**📌 Règles à respecter :**\n\n"
"1. **Respectez les autres membres** - Pas d'insultes, de harcèlement ou de discrimination.\n"
"2. **Pas de contenu inapproprié** - Aucun contenu NSFW, violent ou choquant.\n"
"3. **Pas de spam ou de flood** - Évitez les messages répétitifs ou les emojis excessifs.\n"
"4. **Pas de publicité** - Ne partagez pas de liens vers d'autres serveurs sans autorisation.\n"
"5. **Restez dans les salons appropriés** - Respectez la thématique de chaque canal.\n\n"
"**⚠️ Sanctions :**\n"
"Le non-respect de ces règles peut entraîner des avertissements, des mute temporaires, "
"ou un banissement définitif selon la gravité des infractions.\n\n"
"*En cliquant sur \"J'accepte\", vous confirmez avoir lu et accepter ce règlement.*"
)
return title, content
def setup(bot):
"""Setup du cog de règlement"""
cog = RulesCog(bot)
bot.add_cog(cog)