Vicy_Publique/commandes/annonces_sessions.py
utilisateurqu 2e38d05152 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
2026-08-17 00:12:57 +02:00

222 lines
10 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import discord
from discord import app_commands
from discord.ext import commands, tasks
import dateparser
from datetime import datetime, timezone
from utils.guild_config import get_role_id
presence_data = {}
class PresenceView(discord.ui.View):
def __init__(self, message_id):
super().__init__(timeout=None)
self.message_id = message_id
async def update_message(self, interaction, state):
user_id = interaction.user.id
if self.message_id not in presence_data:
presence_data[self.message_id] = {
"present": set(), "absent": set(), "retard": set(),
"date": None, "sent_dm": False,
"reminder1": None, "reminder2": None,
"reminder1_sent": False, "reminder2_sent": False,
"channel_id": None, "guild_id": None
}
for key in ["present", "absent", "retard"]:
presence_data[self.message_id][key].discard(user_id)
presence_data[self.message_id][state].add(user_id)
n_present = len(presence_data[self.message_id]["present"])
n_absent = len(presence_data[self.message_id]["absent"])
n_retard = len(presence_data[self.message_id]["retard"])
msg = await interaction.channel.fetch_message(self.message_id)
lines = msg.content.split('\n')
# On modifie uniquement les lignes de présence
for i, line in enumerate(lines):
if line.startswith("> - Je serai présent"):
lines[i] = f"> - Je serai présent : <:check:1297296859206778911> **({n_present})**"
elif line.startswith("> - Je serai absent"):
lines[i] = f"> - Je serai absent : <:uncheck:1297296882778902598> **({n_absent})**"
elif line.startswith("> - Je serai en retard"):
lines[i] = f"> - Je serai en retard : ⏰ **({n_retard})**"
new_content = '\n'.join(lines)
await msg.edit(content=new_content, view=self)
@discord.ui.button(label="Présent", style=discord.ButtonStyle.success, emoji="✅", custom_id="presence_present")
async def present(self, interaction: discord.Interaction, button: discord.ui.Button):
await self.update_message(interaction, "present")
await interaction.response.send_message("Merci, tu as été noté comme **présent** à la session RP !", ephemeral=True)
@discord.ui.button(label="Absent", style=discord.ButtonStyle.danger, emoji="❌", custom_id="presence_absent")
async def absent(self, interaction: discord.Interaction, button: discord.ui.Button):
await self.update_message(interaction, "absent")
await interaction.response.send_message("Merci, tu as été noté comme **absent** à la session RP.", ephemeral=True)
@discord.ui.button(label="En retard", style=discord.ButtonStyle.primary, emoji="⏰", custom_id="presence_retard")
async def retard(self, interaction: discord.Interaction, button: discord.ui.Button):
await self.update_message(interaction, "retard")
await interaction.response.send_message("Merci, tu as été noté comme **en retard** pour la session RP.", ephemeral=True)
class AnnonceSession(commands.Cog):
def __init__(self, bot):
self.bot = bot
self.check_sessions.start()
def cog_unload(self):
self.check_sessions.cancel()
@app_commands.command(
name="annonce_session",
description="Annonce une session RP avec image, rappels et boutons de présence."
)
@app_commands.describe(
date="Date de la session (ex: Vendredi 20 Juin à 20h45)",
reminder1="Date/heure du 1er rappel (ex: Jeudi 19 Juin à 19h)",
reminder2="Date/heure du 2e rappel (ex: Vendredi 20 Juin à 15h)",
image="Image à joindre (obligatoire)"
)
async def annonce_session(
self,
interaction: discord.Interaction,
date: str,
reminder1: str,
reminder2: str,
image: discord.Attachment
):
if not image.content_type or not image.content_type.startswith("image/"):
await interaction.response.send_message(
"❌ Le fichier joint doit être une image valide.", ephemeral=True
)
return
# On parse toutes les dates en Europe/Paris
session_date = dateparser.parse(
date, languages=['fr'],
settings={"TIMEZONE": "Europe/Paris", "RETURN_AS_TIMEZONE_AWARE": True}
)
reminder1_date = dateparser.parse(
reminder1, languages=['fr'],
settings={"TIMEZONE": "Europe/Paris", "RETURN_AS_TIMEZONE_AWARE": True}
)
reminder2_date = dateparser.parse(
reminder2, languages=['fr'],
settings={"TIMEZONE": "Europe/Paris", "RETURN_AS_TIMEZONE_AWARE": True}
)
if not session_date or not reminder1_date or not reminder2_date:
await interaction.response.send_message(
"❌ Une des dates n'a pas été comprise. Vérifie le format.", ephemeral=True
)
return
session_date = session_date.astimezone(timezone.utc)
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"
"```📅 - HORAIRE DE LA SESSION```\n"
f"> - Nous vous donnons rendez-vous le **{date}** pour une **session officielle** sur Victoria’s Vice. 🗓️\n"
"---\n"
"```🤔 - COMMENT REJOINDRE LA SESSION ?```\n"
"> - Pour lancer la Map, veuillez entrer le code ci-dessous sur la barre de recherche de Fortnite, par la suite lancer la Map en **public** afin de pouvoir nous rejoindre.\n"
"> - **`CODE DE LA MAP`** <:arrow:1297491509201080362> [0986-1836-1625]\n"
"> - **`LIEN DIRECT DE LA MAP`** <:arrow:1297491509201080362> <https://play.fn.gg/island/0986-1836-1625>\n"
"---\n"
"```🖐 - SEREZ-VOUS PRÉSENT(E) EN SESSION ?```\n"
"> - Je serai présent : <:check:1297296859206778911> **(0)**\n"
"> - Je serai absent : <:uncheck:1297296882778902598> **(0)**\n"
"> - Je serai en retard : ⏰ **(0)**\n"
"---"
f"> - **`PING`** : <@&{ping_role_id}>"
)
await interaction.response.send_message(
content=message,
files=[await image.to_file()]
)
msg = await interaction.original_response()
presence_data[msg.id] = {
"present": set(),
"absent": set(),
"retard": set(),
"date": session_date,
"sent_dm": False,
"reminder1": reminder1_date,
"reminder2": reminder2_date,
"reminder1_sent": False,
"reminder2_sent": False,
"channel_id": msg.channel.id,
"guild_id": msg.guild.id
}
await msg.edit(view=PresenceView(msg.id))
@app_commands.command(
name="liste_presence",
description="Liste les présences pour une session RP."
)
@app_commands.describe(
message_id="ID du message d'annonce"
)
async def liste_presence(self, interaction: discord.Interaction, message_id: str):
try:
mid = int(message_id)
except ValueError:
await interaction.response.send_message("ID de message invalide.", ephemeral=True)
return
if mid not in presence_data:
await interaction.response.send_message("Aucune donnée de présence pour ce message.", ephemeral=True)
return
data = presence_data[mid]
def mentions(ids):
return ', '.join(f"<@{uid}>" for uid in ids) if ids else "Aucun"
await interaction.response.send_message(
f"**Présents ({len(data['present'])})** : {mentions(data['present'])}\n"
f"**Absents ({len(data['absent'])})** : {mentions(data['absent'])}\n"
f"**En retard ({len(data['retard'])})** : {mentions(data['retard'])}",
ephemeral=True
)
@tasks.loop(seconds=30)
async def check_sessions(self):
now = datetime.now(timezone.utc)
for msg_id, data in list(presence_data.items()):
# Premier rappel
if data.get("reminder1") and not data.get("reminder1_sent", False) and now >= data["reminder1"]:
await self.send_reminder(data, msg_id, "Premier rappel !")
data["reminder1_sent"] = True
# Deuxième rappel
if data.get("reminder2") and not data.get("reminder2_sent", False) and now >= data["reminder2"]:
await self.send_reminder(data, msg_id, "Deuxième rappel !")
data["reminder2_sent"] = True
# Envoi du MP à l'heure de la session
if data["date"] and not data.get("sent_dm", False) and now >= data["date"]:
for user_id in data["present"]:
try:
user = await self.bot.fetch_user(user_id)
channel = self.bot.get_channel(data["channel_id"])
if channel:
msg = await channel.fetch_message(msg_id)
await user.send(f"La session RP commence ! Voici le lien de l'annonce : {msg.jump_url}")
except Exception as e:
print(f"Erreur lors de l'envoi du DM à {user_id} : {e}")
data["sent_dm"] = True
async def send_reminder(self, data, msg_id, label):
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}"
)
@check_sessions.before_loop
async def before_check_sessions(self):
await self.bot.wait_until_ready()
async def setup(bot):
await bot.add_cog(AnnonceSession(bot))