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