Ajout du système de règlement dans le menu principal de Kuby (testé § fonctionnel)
This commit is contained in:
parent
fd2d2a74dc
commit
44eefeac6c
4 changed files with 735 additions and 25 deletions
498
commandes/modules_security/rules.py
Normal file
498
commandes/modules_security/rules.py
Normal file
|
|
@ -0,0 +1,498 @@
|
|||
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"
|
||||
|
||||
|
||||
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(discord.ui.Modal, title="📜 Titre du Règlement"):
|
||||
"""Modal pour configurer le titre du règlement"""
|
||||
title_input = discord.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__()
|
||||
self.guild_id = guild_id
|
||||
self.settings = settings
|
||||
self.on_update_callback = on_update_callback
|
||||
|
||||
async def on_submit(self, interaction: discord.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(discord.ui.Modal, title="📝 Contenu du Règlement"):
|
||||
"""Modal pour configurer le contenu du règlement"""
|
||||
content_input = discord.ui.TextInput(
|
||||
label="Contenu du règlement",
|
||||
placeholder="Entrez les règles de votre serveur...",
|
||||
style=discord.TextStyle.paragraph,
|
||||
min_length=10,
|
||||
max_length=4000,
|
||||
required=True
|
||||
)
|
||||
|
||||
def __init__(self, guild_id: int, settings: dict, on_update_callback=None):
|
||||
super().__init__()
|
||||
self.guild_id = guild_id
|
||||
self.settings = settings
|
||||
self.on_update_callback = on_update_callback
|
||||
|
||||
async def on_submit(self, interaction: discord.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(discord.ui.Modal, title="📌 Salon du Règlement"):
|
||||
"""Modal pour sélectionner le salon du règlement"""
|
||||
channel_input = discord.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__()
|
||||
self.guild_id = guild_id
|
||||
self.settings = settings
|
||||
self.on_update_callback = on_update_callback
|
||||
|
||||
async def on_submit(self, interaction: discord.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(discord.ui.Modal, title="✅ Rôle d'Acceptation"):
|
||||
"""Modal pour configurer le rôle attribué à l'acceptation"""
|
||||
role_input = discord.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__()
|
||||
self.guild_id = guild_id
|
||||
self.settings = settings
|
||||
self.on_update_callback = on_update_callback
|
||||
|
||||
async def on_submit(self, interaction: discord.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(discord.ui.Modal, title="📝 Type de Message"):
|
||||
"""Modal pour choisir le type de message du règlement"""
|
||||
message_type = discord.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__()
|
||||
self.guild_id = guild_id
|
||||
self.settings = settings
|
||||
self.on_update_callback = on_update_callback
|
||||
|
||||
async def on_submit(self, interaction: discord.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
|
||||
|
||||
|
||||
class ToggleRulesButton(discord.ui.Button):
|
||||
"""Bouton de bascule pour activer/désactiver le règlement"""
|
||||
|
||||
def __init__(self, is_enabled: bool):
|
||||
style = discord.ButtonStyle.green if is_enabled else discord.ButtonStyle.red
|
||||
status = "✅" if is_enabled else "❌"
|
||||
super().__init__(
|
||||
label=f"Règlement [{status}]",
|
||||
style=style,
|
||||
custom_id="rules_enabled",
|
||||
emoji="📜"
|
||||
)
|
||||
|
||||
async def callback(self, interaction: discord.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)
|
||||
|
||||
|
||||
class RulesAcceptButton(discord.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
|
||||
|
||||
@discord.ui.button(
|
||||
label="J'accepte le règlement",
|
||||
emoji="✅",
|
||||
style=discord.ButtonStyle.green,
|
||||
custom_id="rules_accept_button"
|
||||
)
|
||||
async def accept_rules(self, interaction: discord.Interaction, button: discord.ui.Button):
|
||||
"""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
|
||||
)
|
||||
except discord.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 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)
|
||||
|
||||
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"""
|
||||
await self.setup_persistent_views()
|
||||
|
||||
async def send_rules_message(self, channel: discord.TextChannel, settings: dict) -> Optional[discord.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
|
||||
embed = discord.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
|
||||
|
||||
|
||||
async def setup(bot):
|
||||
"""Setup du cog de règlement"""
|
||||
cog = RulesCog(bot)
|
||||
await bot.add_cog(cog)
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue