From 2e38d05152ed197c4d2ecd964c0e1d13c5528c9b Mon Sep 17 00:00:00 2001 From: utilisateurqu <112496102+utilisateurqu@users.noreply.github.com> Date: Mon, 17 Aug 2026 00:12:57 +0200 Subject: [PATCH] Modularize bot: per-guild config system for roles/channels/categories - Add utils/guild_config.py: centralized config loader per guild (data/guilds/{id}.json) - Add commandes/config.py: /config set_role, /config set_channel, /config set_category, /config voir, /config reset - Replace ALL hardcoded role IDs (banquier, gouvernement, citoyen, staff, ems, permis, fbm, ursaff, etc.) with guild_config lookups - Replace ALL hardcoded channel IDs (welcome, candidature, questions, mp_reception) with guild_config lookups - Replace ALL hardcoded category IDs (ticket) with guild_config lookups - Remove duplicate commandes/welcome_message.py (kept utils/ version) - Remove 20+ hardcoded snowflake IDs across 22 files - Guilds configure via /config on Discord, no code changes needed --- __main__.py | 3 +- commandes/ajouter_argent.py | 5 +- commandes/annonces_sessions.py | 12 +- commandes/compte_banque_nationale.py | 5 +- commandes/compte_fbm.py | 5 +- commandes/compte_gouvernement.py | 4 +- commandes/compte_urssaf.py | 5 +- commandes/config.py | 129 ++++++++++++++++++ commandes/identite.py | 5 +- commandes/ouvrir_compte.py | 6 +- commandes/ouvrir_comptes_entreprises.py | 5 +- commandes/permis.py | 9 +- commandes/pret.py | 5 +- commandes/restranscription_staff.py | 5 +- commandes/retirer_argent.py | 5 +- commandes/retrait_gouv.py | 4 +- commandes/ticket.py | 34 ++--- commandes/transactions.py | 5 +- commandes/valider_visite_médicale.py | 9 +- commandes/virement_banquier.py | 5 +- commandes/virement_banquier_entreprise.py | 5 +- commandes/voir_carte_identite.py | 19 +-- commandes/welcome_message.py | 30 ----- utils/guild_config.py | 157 ++++++++++++++++++++++ utils/welcome_message.py | 17 ++- 25 files changed, 368 insertions(+), 125 deletions(-) create mode 100644 commandes/config.py delete mode 100644 commandes/welcome_message.py create mode 100644 utils/guild_config.py diff --git a/__main__.py b/__main__.py index c374705..766f201 100644 --- a/__main__.py +++ b/__main__.py @@ -72,7 +72,8 @@ async def load_cogs(): "commandes.roll", "commandes.annonces_sessions", "commandes.retrait_gouv", - "commandes.counting" + "commandes.counting", + "commandes.config" ] for ext in extensions: diff --git a/commandes/ajouter_argent.py b/commandes/ajouter_argent.py index 80b386f..e90a458 100644 --- a/commandes/ajouter_argent.py +++ b/commandes/ajouter_argent.py @@ -3,8 +3,7 @@ from discord import app_commands from discord.ext import commands import json from datetime import datetime - -BANQUIER_ROLE_ID = 1360314740567576616 # ID du rôle des banquiers # ID du rôle banquier # ID du rôle banquier +from utils.guild_config import has_role class AjouterArgent(commands.Cog): def __init__(self, bot): @@ -34,7 +33,7 @@ class AjouterArgent(commands.Cog): banque: app_commands.Choice[str] = None ): # Vérification du rôle - if not any(role.id == BANQUIER_ROLE_ID for role in interaction.user.roles): + if not has_role(interaction.user, "banquier"): await interaction.response.send_message( "❌ Vous n'avez pas la permission d'utiliser cette commande.", ephemeral=True diff --git a/commandes/annonces_sessions.py b/commandes/annonces_sessions.py index dfdd2ae..ec06b66 100644 --- a/commandes/annonces_sessions.py +++ b/commandes/annonces_sessions.py @@ -3,9 +3,7 @@ from discord import app_commands from discord.ext import commands, tasks import dateparser from datetime import datetime, timezone - -ROLE_PING_ID = 1278319901651632138 # ID du rôle citoyen # Rôle pour l'annonce principale -REMINDER_ROLE_ID = 1303750986900439151 # ID du rôle rappels sessions # Rôle pour les rappels (à adapter !) +from utils.guild_config import get_role_id presence_data = {} @@ -114,6 +112,9 @@ class AnnonceSession(commands.Cog): reminder1_date = reminder1_date.astimezone(timezone.utc) reminder2_date = reminder2_date.astimezone(timezone.utc) + ping_role_id = get_role_id(interaction.guild.id, "citoyen") + reminder_role_id = get_role_id(interaction.guild.id, "session_reminders") + message = ( "# <:VV:1339237574593609780> - **SESSION VICTORIA’S VICE**\n" "\n" @@ -130,7 +131,7 @@ class AnnonceSession(commands.Cog): "> - Je serai absent : <:uncheck:1297296882778902598> **(0)**\n" "> - Je serai en retard : ⏰ **(0)**\n" "---" - f"> - **`PING`** : <@&{ROLE_PING_ID}>" + f"> - **`PING`** : <@&{ping_role_id}>" ) await interaction.response.send_message( @@ -208,8 +209,9 @@ class AnnonceSession(commands.Cog): channel = self.bot.get_channel(data["channel_id"]) if channel: msg = await channel.fetch_message(msg_id) + reminder_role_id = get_role_id(data.get("guild_id", 0), "session_reminders") await channel.send( - f"<@&{REMINDER_ROLE_ID}> ⏰ **{label}**\nLa session RP approche !\nAnnonce : {msg.jump_url}" + f"<@&{reminder_role_id}> ⏰ **{label}**\nLa session RP approche !\nAnnonce : {msg.jump_url}" ) @check_sessions.before_loop diff --git a/commandes/compte_banque_nationale.py b/commandes/compte_banque_nationale.py index 0bb2109..08aa9b8 100644 --- a/commandes/compte_banque_nationale.py +++ b/commandes/compte_banque_nationale.py @@ -3,8 +3,7 @@ from discord import app_commands from discord.ext import commands import json import math - -BANQUE_NATIONALE_ROLE_ID = 1362031086959329470 # Remplace par l’ID réel du rôle banque_nationale +from utils.guild_config import has_role class CompteBanqueNationale(commands.Cog): def __init__(self, bot): @@ -41,7 +40,7 @@ class CompteBanqueNationale(commands.Cog): @app_commands.command(name="voir_banque_nationale", description="Voir solde et transactions du compte banque nationale") async def voir_banque_nationale(self, interaction: discord.Interaction): - if not any(role.id == BANQUE_NATIONALE_ROLE_ID for role in interaction.user.roles): + if not has_role(interaction.user, "banque_nationale"): await interaction.response.send_message("❌ Vous n'avez pas la permission d'accéder à ce compte.", ephemeral=True) return diff --git a/commandes/compte_fbm.py b/commandes/compte_fbm.py index 2254747..5b84105 100644 --- a/commandes/compte_fbm.py +++ b/commandes/compte_fbm.py @@ -3,8 +3,7 @@ from discord import app_commands from discord.ext import commands import json import math - -FBM_ROLE_ID = 1353610672818360330 # ID du rôle autorisé à accéder au compte FBM +from utils.guild_config import has_role class CompteFBM(commands.Cog): def __init__(self, bot): @@ -25,7 +24,7 @@ class CompteFBM(commands.Cog): @app_commands.command(name="compte_fbm", description="Affiche le solde et l'historique du compte FBM (réservé au personnel FBM).") async def compte_fbm(self, interaction: discord.Interaction): - if not any(role.id == FBM_ROLE_ID for role in interaction.user.roles): + if not has_role(interaction.user, "fbm"): await interaction.response.send_message("❌ Vous n'avez pas la permission d'accéder à ce compte.", ephemeral=True) return diff --git a/commandes/compte_gouvernement.py b/commandes/compte_gouvernement.py index 26d7a3f..b79a5c8 100644 --- a/commandes/compte_gouvernement.py +++ b/commandes/compte_gouvernement.py @@ -4,7 +4,7 @@ from discord.ext import commands import json import math -GOUVERNEMENT_ROLE_ID = 1278305482926194701 # Remplace avec l’ID de ton rôle Gouvernement # Remplace avec l’ID de ton rôle Gouvernement +from utils.guild_config import has_role class CompteGouvernement(commands.Cog): def __init__(self, bot): @@ -22,7 +22,7 @@ class CompteGouvernement(commands.Cog): @app_commands.command(name="compte_gouvernement", description="Affiche le solde et les transactions du compte Gouvernement.") async def compte_gouvernement(self, interaction: discord.Interaction): - if not any(role.id == GOUVERNEMENT_ROLE_ID for role in interaction.user.roles): + if not has_role(interaction.user, "gouvernement"): await interaction.response.send_message("❌ Vous n'avez pas la permission d'accéder à ce compte.", ephemeral=True) return diff --git a/commandes/compte_urssaf.py b/commandes/compte_urssaf.py index e4a30b4..197c5cb 100644 --- a/commandes/compte_urssaf.py +++ b/commandes/compte_urssaf.py @@ -3,8 +3,7 @@ from discord import app_commands from discord.ext import commands import json import math - -URSAFF_ROLE_ID = 1362056014584615213 # Remplace par l’ID réel du rôle URSAFF # Remplace par l’ID réel du rôle URSAFF +from utils.guild_config import has_role class CompteUrssaf(commands.Cog): def __init__(self, bot): @@ -36,7 +35,7 @@ class CompteUrssaf(commands.Cog): @app_commands.command(name="compte_ursaff", description="Affiche le solde et l'historique complet du compte URSAFF (réservé aux membres URSAFF).") async def compte_ursaff(self, interaction: discord.Interaction): # Vérifie que l'utilisateur a le rôle URSAFF - if not any(role.id == URSAFF_ROLE_ID for role in interaction.user.roles): + if not has_role(interaction.user, "ursaff"): await interaction.response.send_message("❌ Vous n'avez pas la permission d'accéder à ce compte.", ephemeral=True) return diff --git a/commandes/config.py b/commandes/config.py new file mode 100644 index 0000000..ad0dce0 --- /dev/null +++ b/commandes/config.py @@ -0,0 +1,129 @@ +import discord +from discord import app_commands +from discord.ext import commands +from utils.guild_config import ( + load_guild_config, save_guild_config, set_config_value, + DEFAULT_CONFIG, ROLE_LABELS, CHANNEL_LABELS, CATEGORY_LABELS, + is_configured, get_role_id, get_channel_id, get_category_id +) + + +class GuildConfig(commands.Cog): + def __init__(self, bot): + self.bot = bot + + config_group = app_commands.Group(name="config", description="Configuration du bot pour ce serveur") + + @config_group.command(name="set_role", description="Associer un rôle du serveur à une fonction du bot") + @app_commands.describe( + cle="Nom de la fonction", + role="Le rôle Discord à associer" + ) + @app_commands.choices(cle=[app_commands.Choice(name=f"{ROLE_LABELS[k]} ({k})", value=k) for k in ROLE_LABELS]) + async def set_role(self, interaction: discord.Interaction, cle: app_commands.Choice[str], role: discord.Role): + if not interaction.user.guild_permissions.manage_guild: + await interaction.response.send_message("❌ Il faut la permission **Gérer le serveur**.", ephemeral=True) + return + set_config_value(interaction.guild.id, "roles", cle.value, role.id) + await interaction.response.send_message( + f"✅ Rôle **{ROLE_LABELS.get(cle.value, cle.value)}** associé à {role.mention}", + ephemeral=True + ) + + @config_group.command(name="set_channel", description="Associer un salon du serveur à une fonction du bot") + @app_commands.describe( + cle="Nom de la fonction", + channel="Le salon Discord à associer" + ) + @app_commands.choices(cle=[app_commands.Choice(name=f"{CHANNEL_LABELS[k]} ({k})", value=k) for k in CHANNEL_LABELS]) + async def set_channel(self, interaction: discord.Interaction, cle: app_commands.Choice[str], channel: discord.TextChannel): + if not interaction.user.guild_permissions.manage_guild: + await interaction.response.send_message("❌ Il faut la permission **Gérer le serveur**.", ephemeral=True) + return + set_config_value(interaction.guild.id, "channels", cle.value, channel.id) + await interaction.response.send_message( + f"✅ Salon **{CHANNEL_LABELS.get(cle.value, cle.value)}** associé à {channel.mention}", + ephemeral=True + ) + + @config_group.command(name="set_category", description="Associer une catégorie du serveur à une fonction du bot") + @app_commands.describe( + cle="Nom de la fonction", + category="La catégorie Discord à associer" + ) + @app_commands.choices(cle=[app_commands.Choice(name=f"{CATEGORY_LABELS[k]} ({k})", value=k) for k in CATEGORY_LABELS]) + async def set_category(self, interaction: discord.Interaction, cle: app_commands.Choice[str], category: discord.CategoryChannel): + if not interaction.user.guild_permissions.manage_guild: + await interaction.response.send_message("❌ Il faut la permission **Gérer le serveur**.", ephemeral=True) + return + set_config_value(interaction.guild.id, "categories", cle.value, category.id) + await interaction.response.send_message( + f"✅ Catégorie **{CATEGORY_LABELS.get(cle.value, cle.value)}** associée à **{category.name}**", + ephemeral=True + ) + + @config_group.command(name="voir", description="Voir la configuration actuelle du bot sur ce serveur") + async def voir_config(self, interaction: discord.Interaction): + if not interaction.user.guild_permissions.manage_guild: + await interaction.response.send_message("❌ Il faut la permission **Gérer le serveur**.", ephemeral=True) + return + + config = load_guild_config(interaction.guild.id) + guild = interaction.guild + + embed = discord.Embed( + title="⚙️ Configuration du bot", + color=discord.Color.blue() + ) + + # Roles + roles_text = "" + for key, label in ROLE_LABELS.items(): + rid = config.get("roles", {}).get(key) + if rid: + role = guild.get_role(int(rid)) + roles_text += f"> {label} : {role.mention if role else f'❌ ID {rid} introuvable'}\n" + else: + roles_text += f"> {label} : ⚠️ Non configuré\n" + embed.add_field(name="🎭 Rôles", value=roles_text, inline=False) + + # Channels + channels_text = "" + for key, label in CHANNEL_LABELS.items(): + cid = config.get("channels", {}).get(key) + if cid: + ch = guild.get_channel(int(cid)) + channels_text += f"> {label} : {ch.mention if ch else f'❌ ID {cid} introuvable'}\n" + else: + channels_text += f"> {label} : ⚠️ Non configuré\n" + embed.add_field(name="📢 Salons", value=channels_text, inline=False) + + # Categories + cats_text = "" + for key, label in CATEGORY_LABELS.items(): + catid = config.get("categories", {}).get(key) + if catid: + cat = guild.get_channel(int(catid)) + cats_text += f"> {label} : **{cat.name if cat else f'❌ ID {catid} introuvable'}**\n" + else: + cats_text += f"> {label} : ⚠️ Non configuré\n" + embed.add_field(name="📁 Catégories", value=cats_text, inline=False) + + missing = is_configured(interaction.guild.id) + total = len(DEFAULT_CONFIG["roles"]) + len(DEFAULT_CONFIG["channels"]) + len(DEFAULT_CONFIG["categories"]) + configured = total - sum(len(v) for v in missing.values()) + embed.set_footer(text=f"{configured}/{total} paramètres configurés") + + await interaction.response.send_message(embed=embed, ephemeral=True) + + @config_group.command(name="reset", description="Réinitialiser la configuration du bot pour ce serveur") + async def reset_config(self, interaction: discord.Interaction): + if not interaction.user.guild_permissions.administrator: + await interaction.response.send_message("❌ Il faut les permissions **Administrateur**.", ephemeral=True) + return + save_guild_config(interaction.guild.id, {"roles": {}, "channels": {}, "categories": {}}) + await interaction.response.send_message("✅ Configuration réinitialisée.", ephemeral=True) + + +async def setup(bot): + await bot.add_cog(GuildConfig(bot)) diff --git a/commandes/identite.py b/commandes/identite.py index 2939422..693cb3f 100644 --- a/commandes/identite.py +++ b/commandes/identite.py @@ -3,8 +3,7 @@ from discord import app_commands from discord.ext import commands from utils.data_manager import DataManager from datetime import datetime, timedelta - -ADMIN_ROLE_ID = 1278305482926194701 # ID du rôle gouvernement # ID du rôle gouvernement +from utils.guild_config import has_role data_manager = DataManager() @@ -30,7 +29,7 @@ class CreerIdentite(commands.Cog): ): """Commande réservée aux admins pour créer une carte d'identité""" - if not any(role.id == ADMIN_ROLE_ID for role in interaction.user.roles): + if not has_role(interaction.user, "gouvernement"): await interaction.response.send_message( "❌ Vous n'avez pas la permission de créer des cartes d'identité.", ephemeral=True diff --git a/commandes/ouvrir_compte.py b/commandes/ouvrir_compte.py index 3c5c28d..8f4b979 100644 --- a/commandes/ouvrir_compte.py +++ b/commandes/ouvrir_compte.py @@ -3,9 +3,7 @@ from discord import app_commands from discord.ext import commands from utils.banque_manager import BanqueManager import random - -# Rôle nécessaire pour créer un compte -BANQUIER_ROLE_ID = 1360314740567576616 # ID du rôle des banquiers # ID du rôle banquier +from utils.guild_config import has_role # Liste des banques disponibles (id interne : nom affiché) BANQUES = { @@ -28,7 +26,7 @@ class OuvrirCompte(commands.Cog): banque_nom = BANQUES[banque_id] # Vérification du rôle de banquier - if not any(role.id == BANQUIER_ROLE_ID for role in interaction.user.roles): + if not has_role(interaction.user, "banquier"): await interaction.response.send_message("❌ Vous n'avez pas la permission d'ouvrir un compte.", ephemeral=True) return diff --git a/commandes/ouvrir_comptes_entreprises.py b/commandes/ouvrir_comptes_entreprises.py index f33290a..72494f7 100644 --- a/commandes/ouvrir_comptes_entreprises.py +++ b/commandes/ouvrir_comptes_entreprises.py @@ -6,8 +6,9 @@ import discord from discord import app_commands from discord.ext import commands +from utils.guild_config import has_role + COMPTES_ENTREPRISES_PATH = "comptes_entreprises.json" -BANQUIER_ROLE_ID = 1360314740567576616 # ID du rôle des banquiers # ID du rôle banquier def generer_rib(): return " ".join(["".join(random.choices(string.digits, k=5)) for _ in range(4)]) @@ -35,7 +36,7 @@ class OuvrirCompteEntreprise(commands.Cog): @app_commands.choices(banque=banque_choix, tva=tva_choix) async def ouvrir_compte_entreprise(self, interaction: discord.Interaction, entreprise: str, banque: app_commands.Choice[str], tva: app_commands.Choice[int]): # ✅ Vérifie que l'utilisateur est banquier - if not any(role.id == BANQUIER_ROLE_ID for role in interaction.user.roles): + if not has_role(interaction.user, "banquier"): await interaction.response.send_message("❌ Vous n'avez pas la permission d'exécuter cette commande.", ephemeral=True) return diff --git a/commandes/permis.py b/commandes/permis.py index 1396ff6..095606b 100644 --- a/commandes/permis.py +++ b/commandes/permis.py @@ -1,10 +1,7 @@ import discord from discord.ext import commands from discord import app_commands - -# IDs des rôles -PERMIS_ROLE_ID = 1357027980056199258 -AUTO_ECOLE_ROLE_ID = 1338844772118298634 +from utils.guild_config import has_role, get_guild_role class PermisCommand(commands.Cog): def __init__(self, bot): @@ -14,12 +11,12 @@ class PermisCommand(commands.Cog): async def ajouter_permis(self, interaction: discord.Interaction, membre: discord.Member): """Ajoute le rôle 'permis' à un membre sélectionné.""" # Vérifier si l'utilisateur a le rôle "auto-école" - if AUTO_ECOLE_ROLE_ID not in [role.id for role in interaction.user.roles]: + if not has_role(interaction.user, "auto_ecole"): await interaction.response.send_message("❌ Erreur : Vous n'avez pas la permission d'utiliser cette commande.") return # Vérifier si le rôle "permis" existe - permis_role = interaction.guild.get_role(PERMIS_ROLE_ID) + permis_role = get_guild_role(interaction.guild, "permis") if not permis_role: await interaction.response.send_message("❌ Erreur : Le rôle 'permis' n'existe pas.") return diff --git a/commandes/pret.py b/commandes/pret.py index 7fa1a21..4deffa2 100644 --- a/commandes/pret.py +++ b/commandes/pret.py @@ -2,8 +2,7 @@ import discord from discord import app_commands from discord.ext import commands from utils.banque_manager import BanqueManager - -BANQUIER_ROLE_ID = 1360314740567576616 # ID du rôle des banquiers # ID du rôle banquier +from utils.guild_config import has_role banque_manager = BanqueManager() @@ -23,7 +22,7 @@ class Pret(commands.Cog): """Les banquiers peuvent accorder un prêt à un utilisateur si son solde le permet.""" # Vérification de si le joueur executant la commande est un banquier - if not any(role.id == BANQUIER_ROLE_ID for role in interaction.user.roles): + if not has_role(interaction.user, "banquier"): await interaction.response.send_message( "❌ Vous n'avez pas la permission d'accorder un prêt.", ephemeral=True diff --git a/commandes/restranscription_staff.py b/commandes/restranscription_staff.py index f893a1d..c959717 100644 --- a/commandes/restranscription_staff.py +++ b/commandes/restranscription_staff.py @@ -1,6 +1,7 @@ import discord from discord.ext import commands from discord import app_commands +from utils.guild_config import get_guild_channel class RestranscriptionStaff(commands.Cog): def __init__(self, bot): @@ -18,7 +19,9 @@ class RestranscriptionStaff(commands.Cog): # Réceptionner les MPs if isinstance(message.channel, discord.DMChannel): - channel_reception = self.bot.get_channel(1277951933105049601) # Remplacez par votre ID de salon + channel_reception = get_guild_channel(message.guild, "mp_reception") + if channel_reception is None: + return embed = discord.Embed(description=message.content, color=discord.Color.blue()) diff --git a/commandes/retirer_argent.py b/commandes/retirer_argent.py index 4988414..860712d 100644 --- a/commandes/retirer_argent.py +++ b/commandes/retirer_argent.py @@ -3,8 +3,7 @@ from discord import app_commands from discord.ext import commands import json from datetime import datetime - -BANQUIER_ROLE_ID = 1360314740567576616 # ID du rôle des banquiers # ID du rôle banquier # ID du rôle banquier +from utils.guild_config import has_role class RetirerArgent(commands.Cog): def __init__(self, bot): @@ -32,7 +31,7 @@ class RetirerArgent(commands.Cog): montant: float, banque: app_commands.Choice[str] = None ): - if not any(role.id == BANQUIER_ROLE_ID for role in interaction.user.roles): + if not has_role(interaction.user, "banquier"): await interaction.response.send_message("❌ Vous n'avez pas la permission de faire cela.", ephemeral=True) return diff --git a/commandes/retrait_gouv.py b/commandes/retrait_gouv.py index 84fa721..c6cf43c 100644 --- a/commandes/retrait_gouv.py +++ b/commandes/retrait_gouv.py @@ -4,7 +4,7 @@ from discord.ext import commands import json from datetime import datetime -GOUVERNEMENT_ROLE_ID = 1278305482926194701 # Remplace avec l’ID de ton rôle Gouvernement # Remplace par l'ID du rôle Gouvernement +from utils.guild_config import has_role GOUV_BANQUE = "Gouvernement" GOUV_COMPTE = "GOUV" GOUV_RIB = "00000 00000 00000000000 00" @@ -37,7 +37,7 @@ class GouvVirement(commands.Cog): banque: app_commands.Choice[str] = None ): # Vérification du rôle Gouvernement - if not any(role.id == GOUVERNEMENT_ROLE_ID for role in interaction.user.roles): + if not has_role(interaction.user, "gouvernement"): await interaction.response.send_message("❌ Seul le Gouvernement peut utiliser cette commande.", ephemeral=True) return diff --git a/commandes/ticket.py b/commandes/ticket.py index 673a765..e48ea06 100644 --- a/commandes/ticket.py +++ b/commandes/ticket.py @@ -1,13 +1,7 @@ import discord from discord.ext import commands from discord import app_commands - -TICKET_CATEGORY_ID = 1301142883876737085 # ID de la catégorie des tickets # ID de la catégorie des tickets -STAFF_ROLE_ID = 1277638784095092816 # ID du rôle staff # ID du rôle staff -TEAM_STAFF_ROLE_ID = 1277638784095092816 # ID du rôle équipe staff # ID du rôle équipe staff -ATTENTE_ORAL_ROLE_ID = 1278343007996870699 # ID du rôle "attente oral" # ID du rôle "attente oral" -CANDIDATURE_A_FAIRE_ROLE_ID = 1278343541936099412 # ID du rôle "candidature à faire" # ID du rôle "candidature à faire" -CITOYEN_ROLE_ID = 1278319901651632138 # ID du rôle "citoyen" # ID du rôle "citoyen" +from utils.guild_config import get_guild_role, get_guild_category, has_role class ContinueFormView(discord.ui.View): """Vue avec un bouton pour continuer à l'étape suivante""" @@ -108,7 +102,7 @@ class TicketStep3(discord.ui.Modal, title="📋 Développement du Personnage"): """Création du ticket après validation""" guild = interaction.guild user = interaction.user - category = discord.utils.get(guild.categories, id=TICKET_CATEGORY_ID) + category = get_guild_category(guild, "ticket") # Vérifier si la catégorie existe if category is None: @@ -116,7 +110,7 @@ class TicketStep3(discord.ui.Modal, title="📋 Développement du Personnage"): return # Vérifier si le rôle staff existe - staff_role = discord.utils.get(guild.roles, id=STAFF_ROLE_ID) + staff_role = get_guild_role(guild, "staff") if staff_role is None: await interaction.response.send_message("❌ Erreur : Le rôle staff n'existe pas !", ephemeral=True) return @@ -132,7 +126,7 @@ class TicketStep3(discord.ui.Modal, title="📋 Développement du Personnage"): guild.default_role: discord.PermissionOverwrite(read_messages=False), user: discord.PermissionOverwrite(read_messages=True, send_messages=True), staff_role: discord.PermissionOverwrite(read_messages=True, send_messages=True), - discord.utils.get(guild.roles, id=TEAM_STAFF_ROLE_ID): discord.PermissionOverwrite(read_messages=True, send_messages=False) + get_guild_role(guild, "team_staff"): discord.PermissionOverwrite(read_messages=True, send_messages=False) } # Créer un salon de ticket avec le nom [pseudo_du_membre]-écrit @@ -143,7 +137,7 @@ class TicketStep3(discord.ui.Modal, title="📋 Développement du Personnage"): ) # Récupérer le rôle "équipe staff" - team_staff_role = discord.utils.get(guild.roles, id=TEAM_STAFF_ROLE_ID) + team_staff_role = get_guild_role(guild, "team_staff") # Création de l'embed avec les infos du formulaire embed = discord.Embed(title="📩 Ticket Whitelist", color=0x00ff00) @@ -202,7 +196,7 @@ class TicketManagementView(discord.ui.View): @discord.ui.button(label="🔒 Fermer le Ticket", style=discord.ButtonStyle.red, custom_id="close_ticket") async def close_ticket(self, interaction: discord.Interaction, button: discord.ui.Button): """Ferme le ticket en désactivant l'envoi de messages pour le membre""" - if not discord.utils.get(interaction.user.roles, id=STAFF_ROLE_ID): + if not has_role(interaction.user, "staff"): await interaction.response.send_message("❌ Seuls les staffs peuvent fermer un ticket.", ephemeral=True) return @@ -217,11 +211,11 @@ class TicketManagementView(discord.ui.View): @discord.ui.button(label="👤 Prendre en charge le Ticket", style=discord.ButtonStyle.blurple, custom_id="claim_ticket") async def claim_ticket(self, interaction: discord.Interaction, button: discord.ui.Button): """Un staff prend en charge le ticket""" - if not discord.utils.get(interaction.user.roles, id=STAFF_ROLE_ID): + if not has_role(interaction.user, "staff"): await interaction.response.send_message("❌ Seuls les staffs peuvent claim un ticket.", ephemeral=True) return self.claimed_by = interaction.user - await interaction.channel.set_permissions(discord.utils.get(interaction.guild.roles, id=STAFF_ROLE_ID), send_messages=False, read_messages=True) + await interaction.channel.set_permissions(get_guild_role(interaction.guild, "staff"), send_messages=False, read_messages=True) await interaction.channel.set_permissions(self.claimed_by, read_messages=True, send_messages=True) await interaction.response.send_message(f"👤 {self.claimed_by.mention} a claim ce ticket.", ephemeral=False) @@ -235,7 +229,7 @@ class ClosedTicketView(discord.ui.View): @discord.ui.button(label="♻ Réouvrir le Ticket", style=discord.ButtonStyle.green, custom_id="reopen_ticket") async def reopen_ticket(self, interaction: discord.Interaction, button: discord.ui.Button): """Réouvre le ticket pour le membre""" - if not discord.utils.get(interaction.user.roles, id=STAFF_ROLE_ID): + if not has_role(interaction.user, "staff"): await interaction.response.send_message("❌ Seuls les staffs peuvent réouvrir un ticket.", ephemeral=True) return await interaction.channel.set_permissions(self.ticket_owner, read_messages=True, send_messages=True) @@ -248,7 +242,7 @@ class ClosedTicketView(discord.ui.View): @discord.ui.button(label="❌ Supprimer le Ticket", style=discord.ButtonStyle.danger, custom_id="delete_ticket") async def delete_ticket(self, interaction: discord.Interaction, button: discord.ui.Button): """Supprime le salon du ticket""" - if not discord.utils.get(interaction.user.roles, id=STAFF_ROLE_ID): + if not has_role(interaction.user, "staff"): await interaction.response.send_message("❌ Seuls les staffs peuvent supprimer un ticket.", ephemeral=True) return await interaction.response.send_message("❌ Suppression du ticket...", ephemeral=True) @@ -291,16 +285,16 @@ class Ticket(commands.Cog): if not message.channel.topic or not message.channel.topic.startswith("Ticket de"): return - staff_role = discord.utils.get(message.guild.roles, id=STAFF_ROLE_ID) + staff_role = get_guild_role(message.guild, "staff") if staff_role in message.author.roles and "validé" in message.content.lower(): try: user_id = message.channel.topic.split("Ticket de ")[1] user = await message.guild.fetch_member(user_id) # Vérifier si le rôle "attente oral" est déjà attribué - attente_oral_role = discord.utils.get(message.guild.roles, id=ATTENTE_ORAL_ROLE_ID) - candidature_a_faire_role = discord.utils.get(message.guild.roles, id=CANDIDATURE_A_FAIRE_ROLE_ID) - citoyen_role = discord.utils.get(message.guild.roles, id=CITOYEN_ROLE_ID) + attente_oral_role = get_guild_role(message.guild, "attente_oral") + candidature_a_faire_role = get_guild_role(message.guild, "candidature_a_faire") + citoyen_role = get_guild_role(message.guild, "citoyen") if attente_oral_role in user.roles: # Deuxième "validé" détecté diff --git a/commandes/transactions.py b/commandes/transactions.py index 4fe7b9f..d370f5b 100644 --- a/commandes/transactions.py +++ b/commandes/transactions.py @@ -2,8 +2,7 @@ import discord from discord import app_commands from discord.ext import commands from utils.banque_manager import BanqueManager - -BANQUIER_ROLE_ID = 1360314740567576616 # ID du rôle des banquiers # ID du rôle banquier # ID du rôle des banquiers +from utils.guild_config import has_role banque_manager = BanqueManager() @@ -22,7 +21,7 @@ class Transactions(commands.Cog): return # ✅ Vérification du rôle banquier - if not any(role.id == BANQUIER_ROLE_ID for role in member.roles): + if not has_role(member, "banquier"): await interaction.response.send_message("❌ Vous n'avez pas la permission de voir les transactions.", ephemeral=True) return diff --git a/commandes/valider_visite_médicale.py b/commandes/valider_visite_médicale.py index 882fdf7..bd65f80 100644 --- a/commandes/valider_visite_médicale.py +++ b/commandes/valider_visite_médicale.py @@ -4,9 +4,8 @@ from discord.ext import commands, tasks import json from datetime import datetime, timedelta import os +from utils.guild_config import has_role, get_guild_role -EMS_ROLE_ID = 1278317212675084328 # Remplace par l’ID du rôle EMS # Remplace par l’ID du rôle EMS -ROLE_A_ATTRIBUER_ID = 1367198876548993045 # Remplace par l’ID du rôle visite médicale # Remplace par l’ID du rôle à attribuer FICHIER_ROLES = "visite_médicale.json" class AttribuerRole(commands.Cog): @@ -27,11 +26,11 @@ class AttribuerRole(commands.Cog): @app_commands.command(name="valider_visite_médicale", description="Attribue un rôle temporaire suite à une visite médicale (EMS uniquement).") async def valider_visite(self, interaction: discord.Interaction, membre: discord.Member): - if not any(role.id == EMS_ROLE_ID for role in interaction.user.roles): + if not has_role(interaction.user, "ems"): await interaction.response.send_message("❌ Seuls les EMS peuvent utiliser cette commande.", ephemeral=True) return - role = interaction.guild.get_role(ROLE_A_ATTRIBUER_ID) + role = get_guild_role(interaction.guild, "visite_medicale") if not role: await interaction.response.send_message("❌ Rôle à attribuer introuvable.", ephemeral=True) return @@ -60,7 +59,7 @@ class AttribuerRole(commands.Cog): member = self.bot.get_user(int(user_id)) or await self.bot.fetch_user(int(user_id)) guild = discord.utils.get(self.bot.guilds) member = guild.get_member(int(user_id)) - role = guild.get_role(ROLE_A_ATTRIBUER_ID) + role = get_guild_role(guild, "visite_medicale") if member and role: await member.remove_roles(role) diff --git a/commandes/virement_banquier.py b/commandes/virement_banquier.py index 814bfca..25de0a0 100644 --- a/commandes/virement_banquier.py +++ b/commandes/virement_banquier.py @@ -3,8 +3,7 @@ import discord from discord import app_commands from discord.ext import commands from datetime import datetime - -BANQUIER_ROLE_ID = 1360314740567576616 # ID du rôle des banquiers # ID du rôle banquier # À remplacer par l’ID réel du rôle banquier +from utils.guild_config import has_role class VirementBanquier(commands.Cog): def __init__(self, bot): @@ -25,7 +24,7 @@ class VirementBanquier(commands.Cog): rib_destinataire: str, banque: app_commands.Choice[str] ): - if not any(role.id == BANQUIER_ROLE_ID for role in interaction.user.roles): + if not has_role(interaction.user, "banquier"): await interaction.response.send_message("❌ Vous n'avez pas la permission d'utiliser cette commande.", ephemeral=True) return diff --git a/commandes/virement_banquier_entreprise.py b/commandes/virement_banquier_entreprise.py index e0de763..6ec1bb2 100644 --- a/commandes/virement_banquier_entreprise.py +++ b/commandes/virement_banquier_entreprise.py @@ -3,8 +3,7 @@ from discord import app_commands from discord.ext import commands import json from datetime import datetime - -BANQUIER_ROLE_ID = 1360314740567576616 # ID du rôle des banquiers # ID du rôle banquier # ID du rôle des banquiers +from utils.guild_config import has_role class VirementBanquierEntreprise(commands.Cog): def __init__(self, bot): @@ -26,7 +25,7 @@ class VirementBanquierEntreprise(commands.Cog): rib_destinataire: str, montant: float ): - if not any(role.id == BANQUIER_ROLE_ID for role in interaction.user.roles): + if not has_role(interaction.user, "banquier"): await interaction.response.send_message("❌ Vous n'avez pas la permission de faire cette commande.", ephemeral=True) return diff --git a/commandes/voir_carte_identite.py b/commandes/voir_carte_identite.py index 986885f..3983ddd 100644 --- a/commandes/voir_carte_identite.py +++ b/commandes/voir_carte_identite.py @@ -3,6 +3,7 @@ from discord import app_commands from discord.ext import commands from utils.data_manager import DataManager from utils.card_generator import CardGenerator +from utils.guild_config import has_role card_generator = CardGenerator() @@ -14,20 +15,14 @@ class VoirIdentite(commands.Cog): async def voir_carte_identite(self, interaction: discord.Interaction, utilisateur: discord.Member = None): """Affiche la carte d'identité d'un utilisateur""" - # Remplacer par l'ID de ton rôle gouvernement - GOUVERNEMENT_ROLE_ID = 1360236421872484403 - target_user = utilisateur or interaction.user - # 🔒 Vérification si l'utilisateur peut voir la carte d'un autre - if utilisateur and utilisateur != interaction.user: - gouvernement_role = interaction.guild.get_role(GOUVERNEMENT_ROLE_ID) - if gouvernement_role not in interaction.user.roles: - await interaction.response.send_message( - "🚫 Vous n'avez pas l'autorisation de voir la carte d'identité d'un autre utilisateur.", - ephemeral=True - ) - return + if utilisateur and utilisateur != interaction.user and not has_role(interaction.user, "gouvernement"): + await interaction.response.send_message( + "🚫 Vous n'avez pas l'autorisation de voir la carte d'identité d'un autre utilisateur.", + ephemeral=True + ) + return user_id = str(target_user.id) diff --git a/commandes/welcome_message.py b/commandes/welcome_message.py deleted file mode 100644 index c3d6d62..0000000 --- a/commandes/welcome_message.py +++ /dev/null @@ -1,30 +0,0 @@ -import discord -from discord.ext import commands -from utils.logger import log_info, log_error - -class WelcomeMessage(commands.Cog): - def __init__(self, bot): - self.bot = bot - - @commands.Cog.listener() - async def on_member_join(self, member): - try: - # ID du salon discussion - welcome_channel_id = 1278483527783944242 - welcome_channel = member.guild.get_channel(welcome_channel_id) - - if welcome_channel: - welcome_message = ( - f"## BIENVENUE <:panda_welcome:1297298999849517056> !\n\n" - f"> **L'équipe staff te souhaite la bienvenue sur Victoria's Vice {member.mention} !**\n\n" - f"> Pour ta candidature <:arrow_1:1297491509201080362> <#1278477608392196176> / <#1278477152215371806> \n\n" - f"> Si tu as des questions <:arrow_1:1297491509201080362> <#1279037583820718120>\n\n" - f"*> Bien cordialement, L'équipe staff de Victoria's Vice*" - ) - - await welcome_channel.send(welcome_message) - except Exception as e: - print(f"Erreur lors de l'envoi du message de bienvenue: {e}") - -async def setup(bot): - await bot.add_cog(WelcomeMessage(bot)) diff --git a/utils/guild_config.py b/utils/guild_config.py new file mode 100644 index 0000000..431137f --- /dev/null +++ b/utils/guild_config.py @@ -0,0 +1,157 @@ +import json +import os +import discord +from typing import Optional + +CONFIG_DIR = os.path.join(os.path.dirname(__file__), "..", "data", "guilds") + +DEFAULT_CONFIG = { + "roles": { + "banquier": None, + "gouvernement": None, + "citoyen": None, + "staff": None, + "team_staff": None, + "attente_oral": None, + "candidature_a_faire": None, + "ems": None, + "visite_medicale": None, + "permis": None, + "auto_ecole": None, + "fbm": None, + "banque_nationale": None, + "ursaff": None, + "session_reminders": None, + }, + "channels": { + "welcome": None, + "candidature": None, + "candidature_2": None, + "questions": None, + "mp_reception": None, + }, + "categories": { + "ticket": None, + }, +} + +ROLE_LABELS = { + "banquier": "Banquier", + "gouvernement": "Gouvernement", + "citoyen": "Citoyen", + "staff": "Staff", + "team_staff": "Team Staff", + "attente_oral": "Attente Oral", + "candidature_a_faire": "Candidature A Faire", + "ems": "EMS", + "visite_medicale": "Visite Médicale", + "permis": "Permis", + "auto_ecole": "Auto-École", + "fbm": "FBM", + "banque_nationale": "Banque Nationale", + "ursaff": "URSAFF", + "session_reminders": "Session Reminders", +} + +CHANNEL_LABELS = { + "welcome": "Bienvenue", + "candidature": "Candidature", + "candidature_2": "Candidature 2", + "questions": "Questions", + "mp_reception": "Réception MP", +} + +CATEGORY_LABELS = { + "ticket": "Tickets", +} + + +def _config_path(guild_id: int) -> str: + return os.path.join(CONFIG_DIR, f"{guild_id}.json") + + +def load_guild_config(guild_id: int) -> dict: + path = _config_path(guild_id) + if not os.path.exists(path): + return {"roles": {}, "channels": {}, "categories": {}} + try: + with open(path, "r", encoding="utf-8") as f: + return json.load(f) + except (json.JSONDecodeError, IOError): + return {"roles": {}, "channels": {}, "categories": {}} + + +def save_guild_config(guild_id: int, config: dict): + os.makedirs(CONFIG_DIR, exist_ok=True) + path = _config_path(guild_id) + with open(path, "w", encoding="utf-8") as f: + json.dump(config, f, ensure_ascii=False, indent=4) + + +def get_role_id(guild_id: int, key: str) -> Optional[int]: + config = load_guild_config(guild_id) + val = config.get("roles", {}).get(key) + return int(val) if val else None + + +def get_channel_id(guild_id: int, key: str) -> Optional[int]: + config = load_guild_config(guild_id) + val = config.get("channels", {}).get(key) + return int(val) if val else None + + +def get_category_id(guild_id: int, key: str) -> Optional[int]: + config = load_guild_config(guild_id) + val = config.get("categories", {}).get(key) + return int(val) if val else None + + +def get_guild_role(guild: discord.Guild, key: str) -> Optional[discord.Role]: + role_id = get_role_id(guild.id, key) + if not role_id: + return None + return guild.get_role(role_id) + + +def get_guild_channel(guild: discord.Guild, key: str) -> Optional[discord.abc.Messageable]: + channel_id = get_channel_id(guild.id, key) + if not channel_id: + return None + return guild.get_channel(channel_id) + + +def get_guild_category(guild: discord.Guild, key: str) -> Optional[discord.CategoryChannel]: + cat_id = get_category_id(guild.id, key) + if not cat_id: + return None + return guild.get_channel(cat_id) + + +def has_role(member: discord.Member, key: str) -> bool: + role = get_guild_role(member.guild, key) + if not role: + return False + return role in member.roles + + +def set_config_value(guild_id: int, section: str, key: str, value): + config = load_guild_config(guild_id) + if section not in config: + config[section] = {} + config[section][key] = value + save_guild_config(guild_id, config) + + +def is_configured(guild_id: int) -> dict: + config = load_guild_config(guild_id) + missing = {"roles": [], "channels": [], "categories": []} + for key in DEFAULT_CONFIG["roles"]: + if not config.get("roles", {}).get(key): + missing["roles"].append(key) + for key in DEFAULT_CONFIG["channels"]: + if not config.get("channels", {}).get(key): + missing["channels"].append(key) + for key in DEFAULT_CONFIG["categories"]: + if not config.get("categories", {}).get(key): + missing["categories"].append(key) + return missing diff --git a/utils/welcome_message.py b/utils/welcome_message.py index c3d6d62..8a06860 100644 --- a/utils/welcome_message.py +++ b/utils/welcome_message.py @@ -1,6 +1,7 @@ import discord from discord.ext import commands from utils.logger import log_info, log_error +from utils.guild_config import get_guild_channel class WelcomeMessage(commands.Cog): def __init__(self, bot): @@ -9,16 +10,22 @@ class WelcomeMessage(commands.Cog): @commands.Cog.listener() async def on_member_join(self, member): try: - # ID du salon discussion - welcome_channel_id = 1278483527783944242 - welcome_channel = member.guild.get_channel(welcome_channel_id) + # Récupérer le salon de bienvenue depuis la config + welcome_channel = get_guild_channel(member.guild, "welcome") if welcome_channel: + cand_ch = get_guild_channel(member.guild, "candidature") + cand2_ch = get_guild_channel(member.guild, "candidature_2") + quest_ch = get_guild_channel(member.guild, "questions") + cand_mention = cand_ch.mention if cand_ch else "#candidature" + cand2_mention = cand2_ch.mention if cand2_ch else "#candidature-2" + quest_mention = quest_ch.mention if quest_ch else "#questions" + welcome_message = ( f"## BIENVENUE <:panda_welcome:1297298999849517056> !\n\n" f"> **L'équipe staff te souhaite la bienvenue sur Victoria's Vice {member.mention} !**\n\n" - f"> Pour ta candidature <:arrow_1:1297491509201080362> <#1278477608392196176> / <#1278477152215371806> \n\n" - f"> Si tu as des questions <:arrow_1:1297491509201080362> <#1279037583820718120>\n\n" + f"> Pour ta candidature <:arrow_1:1297491509201080362> {cand_mention} / {cand2_mention} \n\n" + f"> Si tu as des questions <:arrow_1:1297491509201080362> {quest_mention}\n\n" f"*> Bien cordialement, L'équipe staff de Victoria's Vice*" )