220 lines
10 KiB
Python
220 lines
10 KiB
Python
import discord
|
||
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 !)
|
||
|
||
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)
|
||
|
||
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`** : <@&{ROLE_PING_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)
|
||
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))
|