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:
parent
bc3cb3f3d5
commit
2e38d05152
25 changed files with 368 additions and 125 deletions
157
utils/guild_config.py
Normal file
157
utils/guild_config.py
Normal 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
|
||||
|
|
@ -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*"
|
||||
)
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue