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
This commit is contained in:
utilisateurqu 2026-08-17 00:12:57 +02:00
parent bc3cb3f3d5
commit 2e38d05152
25 changed files with 368 additions and 125 deletions

View file

@ -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:

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

129
commandes/config.py Normal file
View file

@ -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))

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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())

View file

@ -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

View file

@ -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

View file

@ -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é

View file

@ -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

View file

@ -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)

View file

@ -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

View file

@ -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

View file

@ -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,15 +15,9 @@ 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:
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

View file

@ -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))

157
utils/guild_config.py Normal file
View file

@ -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

View file

@ -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*"
)