Vicy_Publique/commandes/absence_staff.py
2026-08-16 19:37:15 +02:00

224 lines
No EOL
8.6 KiB
Python

from discord.ext import commands, tasks
from discord import app_commands, ui
import discord
import json
import asyncio
import os
from datetime import datetime
ABSENCE_CHANNEL_ID = 1369669999677145099
ABSENCE_FILE = "absences.json"
ABSENCE_FILE_TMP = "absences.json.tmp"
file_lock = asyncio.Lock()
async def load_absences():
async with file_lock:
try:
with open(ABSENCE_FILE, "r") as f:
return json.load(f)
except FileNotFoundError:
return {}
async def save_absences(data):
async with file_lock:
with open(ABSENCE_FILE_TMP, "w") as f:
json.dump(data, f, indent=4)
os.replace(ABSENCE_FILE_TMP, ABSENCE_FILE)
class Absence(commands.Cog):
def __init__(self, bot):
self.bot = bot
self.check_absences.start()
@commands.hybrid_command(name="absence", with_app_command=True)
async def absence(self, ctx, motif: str, date_debut: str, date_fin: str, superieur: discord.Member = None):
try:
debut = datetime.strptime(date_debut, "%d/%m/%Y")
fin = datetime.strptime(date_fin, "%d/%m/%Y")
except ValueError:
return await ctx.send("❌ Format de date invalide. Utilise JJ/MM/AAAA.", ephemeral=True)
if debut > fin:
return await ctx.send("❌ La date de début doit précéder la date de fin.", ephemeral=True)
abs_data = await load_absences()
if str(ctx.author.id) in abs_data:
return await ctx.send("❌ Tu as déjà déclaré une absence. Utilise `/fin_absence` pour la terminer.", ephemeral=True)
duree = (fin - debut).days + 1
etat = "à venir" if debut > datetime.today() else "en cours"
abs_data[str(ctx.author.id)] = {
"pseudo": ctx.author.name,
"debut": date_debut,
"fin": date_fin,
"motif": motif,
"superieur": superieur.mention if superieur else "Aucun",
"message_id": None,
"etat": etat
}
await save_absences(abs_data)
channel = self.bot.get_channel(ABSENCE_CHANNEL_ID)
if not channel:
return await ctx.send("❌ Salon d'absence introuvable. Vérifie l'ID.", ephemeral=True)
absence_message = await channel.send(
f"# ABSENCE STAFF :\n"
f"- **Pseudo Discord** : {ctx.author.mention}\n"
f"- **Durée** : {duree} jours\n"
f"- **Débute le** : {date_debut}\n"
f"- **Termine le** : {date_fin}\n"
f"- **Motif** : {motif}\n"
f"- **Mention du supérieur hiérarchique** : {superieur.mention if superieur else 'Aucun'}"
)
abs_data[str(ctx.author.id)]["message_id"] = absence_message.id
await save_absences(abs_data)
await ctx.send("✅ Absence enregistrée avec succès !", ephemeral=True)
@commands.hybrid_command(name="fin_absence", with_app_command=True)
async def fin_absence(self, ctx):
abs_data = await load_absences()
user_id = str(ctx.author.id)
if user_id not in abs_data:
return await ctx.send("❌ Tu n'as pas d'absence en cours.", ephemeral=True)
message_id = abs_data[user_id].get("message_id")
channel = self.bot.get_channel(ABSENCE_CHANNEL_ID)
if message_id and channel:
try:
msg = await channel.fetch_message(message_id)
await msg.delete()
except Exception:
pass
del abs_data[user_id]
await save_absences(abs_data)
await ctx.send("✅ Ton absence a été retirée.", ephemeral=True)
@commands.hybrid_command(name="liste_absences", with_app_command=True)
async def liste_absences(self, ctx):
abs_data = await load_absences()
absences = [
(user_id, info) for user_id, info in abs_data.items()
if info.get("etat") == "en cours"
]
if not absences:
return await ctx.send("Aucune absence staff en cours.", ephemeral=True)
per_page = 3
pages = [absences[i:i + per_page] for i in range(0, len(absences), per_page)]
def create_embed(page_index: int) -> discord.Embed:
embed = discord.Embed(
title=f"📋 Absences staff en cours (Page {page_index + 1}/{len(pages)})",
color=discord.Color.orange()
)
for _, info in pages[page_index]:
embed.add_field(
name=info["pseudo"],
value=(
f"**Début** : {info['debut']}\n"
f"**Fin** : {info['fin']}\n"
f"**Motif** : {info['motif']}\n"
f"**Supérieur** : {info['superieur']}"
),
inline=False
)
embed.set_footer(text="Gestion des absences du staff")
return embed
class PaginatorView(ui.View):
def __init__(self):
super().__init__(timeout=60)
self.page = 0
@ui.button(label="⬅️ Précédent", style=discord.ButtonStyle.secondary, disabled=True)
async def previous(self, interaction: discord.Interaction, button: discord.ui.Button):
self.page -= 1
await interaction.response.edit_message(embed=create_embed(self.page), view=self)
self.update_buttons()
@ui.button(label="➡️ Suivant", style=discord.ButtonStyle.secondary, disabled=len(pages) == 1)
async def next(self, interaction: discord.Interaction, button: discord.ui.Button):
self.page += 1
await interaction.response.edit_message(embed=create_embed(self.page), view=self)
self.update_buttons()
def update_buttons(self):
self.children[0].disabled = self.page == 0
self.children[1].disabled = self.page >= len(pages) - 1
view = PaginatorView()
await ctx.send(embed=create_embed(0), view=view, ephemeral=True)
@tasks.loop(minutes=1)
async def check_absences(self):
abs_data = await load_absences()
today = datetime.today()
to_remove = []
for user_id, info in abs_data.items():
debut = datetime.strptime(info["debut"], "%d/%m/%Y")
fin = datetime.strptime(info["fin"], "%d/%m/%Y")
etat = info.get("etat", "en cours")
if etat == "à venir" and debut <= today:
for guild in self.bot.guilds:
member = guild.get_member(int(user_id))
if member:
await self.deactivate_mentions(member)
abs_data[user_id]["etat"] = "en cours"
if etat == "en cours" and fin < today:
for guild in self.bot.guilds:
member = guild.get_member(int(user_id))
if member:
await self.activate_mentions(member)
to_remove.append(user_id)
for user_id in to_remove:
abs_data.pop(user_id, None)
await save_absences(abs_data)
async def deactivate_mentions(self, member):
role = discord.utils.get(member.guild.roles, name="Absence")
if not role:
role = await member.guild.create_role(name="Absence", reason="Gestion des absences staff")
await member.add_roles(role, reason="Début d'absence staff")
async def activate_mentions(self, member):
role = discord.utils.get(member.guild.roles, name="Absence")
if role:
await member.remove_roles(role, reason="Fin d'absence staff")
@commands.Cog.listener()
async def on_message(self, message):
if message.author.bot:
return
abs_data = await load_absences()
absent_ids = {k for k, v in abs_data.items() if v.get("etat") == "en cours"}
if str(message.author.id) in absent_ids:
await message.channel.send(
f"{message.author.mention}, tu es actuellement déclaré absent. Merci de prévenir si tu es de retour !",
delete_after=30
)
for user_id in absent_ids:
if f"<@{user_id}>" in message.content or f"<@!{user_id}>" in message.content:
msg_id = abs_data[user_id].get("message_id")
absence_url = f"https://discord.com/channels/{message.guild.id}/{ABSENCE_CHANNEL_ID}/{msg_id}" if msg_id else ""
await message.reply(
f"{message.author.mention}, cet utilisateur est absent. Plus d'infos : {absence_url}",
delete_after=60
)
async def setup(bot):
await bot.add_cog(Absence(bot))