Remove absence and taches commands, utils and data files
This commit is contained in:
parent
c839a198d5
commit
bc3cb3f3d5
8 changed files with 3 additions and 817 deletions
10
__main__.py
10
__main__.py
|
|
@ -69,16 +69,10 @@ async def load_cogs():
|
||||||
"commandes.virement_fbm",
|
"commandes.virement_fbm",
|
||||||
"commandes.virement_ursaff",
|
"commandes.virement_ursaff",
|
||||||
"commandes.stats_commandes",
|
"commandes.stats_commandes",
|
||||||
"commandes.taches",
|
|
||||||
"commandes.roll",
|
"commandes.roll",
|
||||||
"commandes.annonces_sessions", # Fait : V1.7 correction des annonces de sessions
|
"commandes.annonces_sessions",
|
||||||
"commandes.retrait_gouv",
|
"commandes.retrait_gouv",
|
||||||
"commandes.absence_staff", # Fait : V1.7 pouvoir voir plus de 3 absences staff (pagination)
|
"commandes.counting"
|
||||||
"commandes.liste_taches", # Fait : V1.7 Rebuild du système de taches pour le rendre plus moderne
|
|
||||||
"commandes.counting"
|
|
||||||
|
|
||||||
|
|
||||||
# Pas fait : V1.8 Amélioration des stats de commandes ajouts de courbes et de graphiques + Voir le top 10 des personnes qui utilisent le plus les commandes
|
|
||||||
]
|
]
|
||||||
|
|
||||||
for ext in extensions:
|
for ext in extensions:
|
||||||
|
|
|
||||||
|
|
@ -1,47 +1 @@
|
||||||
{
|
{}
|
||||||
"534003230435442690": {
|
|
||||||
"pseudo": "nixfix06",
|
|
||||||
"debut": "25/06/2025",
|
|
||||||
"fin": "01/09/2025",
|
|
||||||
"motif": "je travaille",
|
|
||||||
"superieur": "Aucun",
|
|
||||||
"message_id": 1386993295766323202,
|
|
||||||
"etat": "en cours"
|
|
||||||
},
|
|
||||||
"828923448453103637": {
|
|
||||||
"pseudo": "blancandwhite",
|
|
||||||
"debut": "04/07/2025",
|
|
||||||
"fin": "28/07/2025",
|
|
||||||
"motif": "vacances",
|
|
||||||
"superieur": "<@1108087282692522105>",
|
|
||||||
"message_id": 1389892250317750322,
|
|
||||||
"etat": "en cours"
|
|
||||||
},
|
|
||||||
"914118793918312480": {
|
|
||||||
"pseudo": "neyzizz",
|
|
||||||
"debut": "20/07/2025",
|
|
||||||
"fin": "27/07/2025",
|
|
||||||
"motif": "vacances en montagne",
|
|
||||||
"superieur": "Aucun",
|
|
||||||
"message_id": 1390747833958662154,
|
|
||||||
"etat": "en cours"
|
|
||||||
},
|
|
||||||
"727188532669448509": {
|
|
||||||
"pseudo": "nuggets.png",
|
|
||||||
"debut": "21/07/2025",
|
|
||||||
"fin": "22/07/2025",
|
|
||||||
"motif": "je fouette mg",
|
|
||||||
"superieur": "<@1069386396764221450>",
|
|
||||||
"message_id": 1396650241679294515,
|
|
||||||
"etat": "en cours"
|
|
||||||
},
|
|
||||||
"1069386396764221450": {
|
|
||||||
"pseudo": "matlo13",
|
|
||||||
"debut": "21/07/2025",
|
|
||||||
"fin": "22/07/2025",
|
|
||||||
"motif": "text",
|
|
||||||
"superieur": "Aucun",
|
|
||||||
"message_id": 1396650262252355584,
|
|
||||||
"etat": "en cours"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,66 +0,0 @@
|
||||||
import discord
|
|
||||||
from discord.ext import commands
|
|
||||||
from discord import app_commands
|
|
||||||
from datetime import datetime, timedelta
|
|
||||||
|
|
||||||
class AbsenceStaff(commands.Cog):
|
|
||||||
def __init__(self, bot):
|
|
||||||
self.bot = bot
|
|
||||||
self.absences = {}
|
|
||||||
|
|
||||||
@app_commands.command(name="absence_staff", description="Indiquez votre absence en tant que staff.")
|
|
||||||
async def absence_staff(self, interaction: discord.Interaction, duree: str, debut: str, fin: str, raison: str, superieur: discord.Member):
|
|
||||||
"""Indiquez votre absence en tant que staff."""
|
|
||||||
try:
|
|
||||||
# Convertir les dates
|
|
||||||
debut_date = datetime.strptime(debut, "%d/%m/%Y")
|
|
||||||
fin_date = datetime.strptime(fin, "%d/%m/%Y")
|
|
||||||
|
|
||||||
# Calculer la durée en jours
|
|
||||||
if duree.endswith('s'): # Si la durée est en semaines
|
|
||||||
duree_jours = int(duree[:-1]) * 7
|
|
||||||
else:
|
|
||||||
duree_jours = int(duree)
|
|
||||||
|
|
||||||
# Vérifier la cohérence des dates
|
|
||||||
if (fin_date - debut_date).days != duree_jours:
|
|
||||||
await interaction.response.send_message("❌ Erreur : La durée spécifiée ne correspond pas aux dates fournies.")
|
|
||||||
return
|
|
||||||
|
|
||||||
# Enregistrer l'absence
|
|
||||||
self.absences[interaction.user.id] = {
|
|
||||||
"debut": debut_date,
|
|
||||||
"fin": fin_date,
|
|
||||||
"raison": raison,
|
|
||||||
"superieur": superieur.id
|
|
||||||
}
|
|
||||||
|
|
||||||
await interaction.response.send_message(f"✅ Votre absence a été enregistrée, {interaction.user.mention}.")
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
await interaction.response.send_message(f"❌ Erreur : {str(e)}")
|
|
||||||
|
|
||||||
@commands.Cog.listener()
|
|
||||||
async def on_message(self, message):
|
|
||||||
# Ignorer les messages envoyés par le bot
|
|
||||||
if message.author.bot:
|
|
||||||
return
|
|
||||||
|
|
||||||
# Vérifier si un utilisateur absent est mentionné
|
|
||||||
for user_id, absence in self.absences.items():
|
|
||||||
user = self.bot.get_user(user_id)
|
|
||||||
if user and user in message.mentions:
|
|
||||||
# Répondre avec un message indiquant l'absence
|
|
||||||
await message.channel.send(
|
|
||||||
f"{user.display_name} est actuellement absent(e).\n"
|
|
||||||
f"Raison : {absence['raison']}\n"
|
|
||||||
f"Retour prévu le : {absence['fin'].strftime('%d/%m/%Y')}"
|
|
||||||
)
|
|
||||||
|
|
||||||
# Supprimer le message avec la mention
|
|
||||||
await message.delete()
|
|
||||||
break
|
|
||||||
|
|
||||||
async def setup(bot):
|
|
||||||
await bot.add_cog(AbsenceStaff(bot))
|
|
||||||
print("Commande /absence_staff chargée !")
|
|
||||||
|
|
@ -1,224 +0,0 @@
|
||||||
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))
|
|
||||||
|
|
@ -1,60 +0,0 @@
|
||||||
import discord
|
|
||||||
from discord.ext import commands
|
|
||||||
from discord import app_commands
|
|
||||||
import json
|
|
||||||
from collections import defaultdict
|
|
||||||
|
|
||||||
class Taches(commands.Cog):
|
|
||||||
def __init__(self, bot):
|
|
||||||
self.bot = bot
|
|
||||||
|
|
||||||
@app_commands.command(name="lister_taches", description="Liste toutes les tâches assignées à tous les utilisateurs.")
|
|
||||||
async def lister_taches(self, interaction: discord.Interaction):
|
|
||||||
try:
|
|
||||||
with open("taches.json", "r", encoding="utf-8") as f:
|
|
||||||
data = json.load(f)
|
|
||||||
except Exception:
|
|
||||||
await interaction.response.send_message("❌ Impossible de lire le fichier des tâches.", ephemeral=True)
|
|
||||||
return
|
|
||||||
|
|
||||||
taches = data.get("taches", {})
|
|
||||||
if not taches:
|
|
||||||
await interaction.response.send_message("Aucune tâche trouvée.", ephemeral=True)
|
|
||||||
return
|
|
||||||
|
|
||||||
# Regrouper les tâches par utilisateur assigné
|
|
||||||
taches_par_utilisateur = defaultdict(list)
|
|
||||||
for tache_id, tache_info in taches.items():
|
|
||||||
assigne_id = tache_info.get("assigné")
|
|
||||||
if assigne_id:
|
|
||||||
taches_par_utilisateur[assigne_id].append(tache_info)
|
|
||||||
else:
|
|
||||||
taches_par_utilisateur[None].append(tache_info) # Pour tâches non assignées
|
|
||||||
|
|
||||||
embed = discord.Embed(
|
|
||||||
title="📋 Liste des tâches assignées",
|
|
||||||
color=discord.Color.blue()
|
|
||||||
)
|
|
||||||
|
|
||||||
for user_id, user_taches in taches_par_utilisateur.items():
|
|
||||||
if user_id is None:
|
|
||||||
nom_utilisateur = "Non assigné"
|
|
||||||
else:
|
|
||||||
try:
|
|
||||||
user = await self.bot.fetch_user(int(user_id))
|
|
||||||
nom_utilisateur = user.display_name
|
|
||||||
except Exception:
|
|
||||||
nom_utilisateur = f"Utilisateur inconnu ({user_id})"
|
|
||||||
|
|
||||||
taches_text = ""
|
|
||||||
for tache in user_taches:
|
|
||||||
titre = tache.get("titre", "Sans titre")
|
|
||||||
statut = tache.get("statut", "inconnu")
|
|
||||||
taches_text += f"- **{titre}** *(état: {statut})*\n"
|
|
||||||
|
|
||||||
embed.add_field(name=f"{nom_utilisateur}", value=taches_text, inline=False)
|
|
||||||
|
|
||||||
await interaction.response.send_message(embed=embed, ephemeral=False)
|
|
||||||
|
|
||||||
async def setup(bot):
|
|
||||||
await bot.add_cog(Taches(bot))
|
|
||||||
|
|
@ -1,259 +0,0 @@
|
||||||
import discord
|
|
||||||
from discord import app_commands
|
|
||||||
from discord.ext import commands, tasks
|
|
||||||
from datetime import datetime
|
|
||||||
from zoneinfo import ZoneInfo
|
|
||||||
from utils.gestion_taches import (
|
|
||||||
ajouter_tache, lister_taches_utilisateur, taches_proches,
|
|
||||||
update_tache_status, charger_taches, supprimer_tache, sauvegarder_taches
|
|
||||||
)
|
|
||||||
|
|
||||||
PRIORITES = ["Haute", "Moyenne", "Basse", "Critique", "Faible"]
|
|
||||||
|
|
||||||
class RappelTacheView(discord.ui.View):
|
|
||||||
def __init__(self, tid: str):
|
|
||||||
super().__init__(timeout=None)
|
|
||||||
self.tid = tid
|
|
||||||
|
|
||||||
@discord.ui.button(label="✅ Marquer comme faite", style=discord.ButtonStyle.success, custom_id="btn_mark_done")
|
|
||||||
async def mark_done(self, interaction: discord.Interaction, button: discord.ui.Button):
|
|
||||||
success = await update_tache_status(self.tid, "Terminée")
|
|
||||||
if success:
|
|
||||||
await interaction.response.send_message(
|
|
||||||
f"✅ La tâche #{self.tid} a été marquée comme terminée !", ephemeral=True)
|
|
||||||
else:
|
|
||||||
await interaction.response.send_message(
|
|
||||||
"❌ Impossible de mettre à jour la tâche. Peut-être déjà terminée ?", ephemeral=True)
|
|
||||||
self.disable_all_items()
|
|
||||||
await interaction.message.edit(view=self)
|
|
||||||
|
|
||||||
class MesTachesPaginator(discord.ui.View):
|
|
||||||
def __init__(self, interaction: discord.Interaction, taches_list: list[tuple], per_page=5):
|
|
||||||
super().__init__(timeout=120)
|
|
||||||
self.interaction = interaction
|
|
||||||
self.taches = taches_list
|
|
||||||
self.per_page = per_page
|
|
||||||
self.page = 0
|
|
||||||
self.max_page = (len(taches_list) - 1) // per_page
|
|
||||||
|
|
||||||
def get_embed(self):
|
|
||||||
embed = discord.Embed(title="📝 Vos Tâches Assignées", color=discord.Color.green())
|
|
||||||
start = self.page * self.per_page
|
|
||||||
end = start + self.per_page
|
|
||||||
for tid, t in self.taches[start:end]:
|
|
||||||
embed.add_field(
|
|
||||||
name=f"#{tid} - {t['titre']}",
|
|
||||||
value=f"📅 Échéance : {t['échéance']}\n⚠️ Priorité : {t['priorité']}\n📌 Statut : {t['statut']}",
|
|
||||||
inline=False
|
|
||||||
)
|
|
||||||
embed.set_footer(text=f"Page {self.page + 1} / {self.max_page + 1}")
|
|
||||||
return embed
|
|
||||||
|
|
||||||
@discord.ui.button(label="⬅️", style=discord.ButtonStyle.secondary)
|
|
||||||
async def previous_page(self, interaction: discord.Interaction, button: discord.ui.Button):
|
|
||||||
if interaction.user.id != self.interaction.user.id:
|
|
||||||
await interaction.response.send_message("❌ Ce n'est pas votre pagination.", ephemeral=True)
|
|
||||||
return
|
|
||||||
if self.page > 0:
|
|
||||||
self.page -= 1
|
|
||||||
await interaction.response.edit_message(embed=self.get_embed(), view=self)
|
|
||||||
else:
|
|
||||||
await interaction.response.defer()
|
|
||||||
|
|
||||||
@discord.ui.button(label="➡️", style=discord.ButtonStyle.secondary)
|
|
||||||
async def next_page(self, interaction: discord.Interaction, button: discord.ui.Button):
|
|
||||||
if interaction.user.id != self.interaction.user.id:
|
|
||||||
await interaction.response.send_message("❌ Ce n'est pas votre pagination.", ephemeral=True)
|
|
||||||
return
|
|
||||||
if self.page < self.max_page:
|
|
||||||
self.page += 1
|
|
||||||
await interaction.response.edit_message(embed=self.get_embed(), view=self)
|
|
||||||
else:
|
|
||||||
await interaction.response.defer()
|
|
||||||
|
|
||||||
class JiraDiscord(commands.Cog):
|
|
||||||
def __init__(self, bot):
|
|
||||||
self.bot = bot
|
|
||||||
self.rappels.start()
|
|
||||||
|
|
||||||
def cog_unload(self):
|
|
||||||
self.rappels.cancel()
|
|
||||||
|
|
||||||
async def autocomplete_priorite(self, interaction: discord.Interaction, current: str):
|
|
||||||
return [
|
|
||||||
app_commands.Choice(name=prio, value=prio)
|
|
||||||
for prio in PRIORITES if current.lower() in prio.lower()
|
|
||||||
][:5]
|
|
||||||
|
|
||||||
@app_commands.command(name="nouvelle_tache", description="Créer une nouvelle tâche")
|
|
||||||
@app_commands.describe(
|
|
||||||
titre="Titre de la tâche",
|
|
||||||
description="Description de la tâche",
|
|
||||||
assigne="Utilisateur assigné",
|
|
||||||
priorite="Niveau de priorité (ex: Haute, Moyenne, Basse)",
|
|
||||||
echeance="Date d'échéance au format JJ/MM/AAAA"
|
|
||||||
)
|
|
||||||
async def nouvelle_tache(
|
|
||||||
self, interaction: discord.Interaction, titre: str, description: str,
|
|
||||||
assigne: discord.User, priorite: str, echeance: str
|
|
||||||
):
|
|
||||||
data = await charger_taches()
|
|
||||||
for tid, tache in data.get("taches", {}).items():
|
|
||||||
if tache["assigné"] == assigne.id and tache["titre"].lower() == titre.lower():
|
|
||||||
return await interaction.response.send_message(
|
|
||||||
f"❌ {assigne.mention} a déjà une tâche avec ce titre.", ephemeral=True)
|
|
||||||
|
|
||||||
try:
|
|
||||||
date_obj = datetime.strptime(echeance, "%d/%m/%Y")
|
|
||||||
date_str = date_obj.strftime("%Y-%m-%d")
|
|
||||||
except ValueError:
|
|
||||||
return await interaction.response.send_message(
|
|
||||||
"❌ Format de date invalide (JJ/MM/AAAA).", ephemeral=True)
|
|
||||||
|
|
||||||
id_tache = await ajouter_tache(titre, description, assigne.id, priorite, date_str, interaction.user.id)
|
|
||||||
|
|
||||||
embed = discord.Embed(
|
|
||||||
title=f"📌 Nouvelle Tâche #{id_tache}",
|
|
||||||
description=description,
|
|
||||||
color=discord.Color.blurple()
|
|
||||||
)
|
|
||||||
embed.set_author(name=f"Assignée à {assigne.display_name}", icon_url=assigne.display_avatar.url)
|
|
||||||
embed.add_field(name="🗂 Titre", value=titre, inline=False)
|
|
||||||
embed.add_field(name="⚠️ Priorité", value=priorite, inline=True)
|
|
||||||
embed.add_field(name="📅 Échéance", value=echeance, inline=True)
|
|
||||||
embed.set_footer(text=f"Créée par {interaction.user.display_name}", icon_url=interaction.user.display_avatar.url)
|
|
||||||
|
|
||||||
await interaction.response.send_message(embed=embed)
|
|
||||||
|
|
||||||
try:
|
|
||||||
await assigne.send(
|
|
||||||
f"👋 Bonjour {assigne.display_name},\n\nUne nouvelle tâche t'a été assignée par {interaction.user.mention} :",
|
|
||||||
embed=embed
|
|
||||||
)
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
|
|
||||||
@nouvelle_tache.autocomplete("priorite")
|
|
||||||
async def autocomplete_priorite_handler(self, interaction: discord.Interaction, current: str):
|
|
||||||
return await self.autocomplete_priorite(interaction, current)
|
|
||||||
|
|
||||||
@app_commands.command(name="mes_taches", description="Lister mes tâches assignées")
|
|
||||||
async def mes_taches(self, interaction: discord.Interaction):
|
|
||||||
taches = await lister_taches_utilisateur(interaction.user.id)
|
|
||||||
if not taches:
|
|
||||||
await interaction.response.send_message("🚫 Aucune tâche ne vous est assignée.", ephemeral=True)
|
|
||||||
return
|
|
||||||
|
|
||||||
taches_list = list(taches.items())
|
|
||||||
if len(taches_list) <= 5:
|
|
||||||
embed = discord.Embed(title="📝 Vos Tâches Assignées", color=discord.Color.green())
|
|
||||||
for tid, t in taches_list:
|
|
||||||
embed.add_field(
|
|
||||||
name=f"#{tid} - {t['titre']}",
|
|
||||||
value=f"📅 Échéance : {t['échéance']}\n⚠️ Priorité : {t['priorité']}\n📌 Statut : {t['statut']}",
|
|
||||||
inline=False
|
|
||||||
)
|
|
||||||
await interaction.response.send_message(embed=embed, ephemeral=True)
|
|
||||||
else:
|
|
||||||
view = MesTachesPaginator(interaction, taches_list)
|
|
||||||
await interaction.response.send_message(embed=view.get_embed(), view=view, ephemeral=True)
|
|
||||||
|
|
||||||
@app_commands.command(name="update_tache", description="Mettre à jour le statut et la priorité d'une tâche")
|
|
||||||
@app_commands.describe(
|
|
||||||
id_tache="ID de la tâche à mettre à jour",
|
|
||||||
statut="Nouveau statut de la tâche",
|
|
||||||
priorite="Nouvelle priorité (optionnel)"
|
|
||||||
)
|
|
||||||
async def update_tache(
|
|
||||||
self, interaction: discord.Interaction, id_tache: str, statut: str, priorite: str = None
|
|
||||||
):
|
|
||||||
data = await charger_taches()
|
|
||||||
if id_tache not in data.get("taches", {}):
|
|
||||||
return await interaction.response.send_message("❌ Tâche non trouvée.", ephemeral=True)
|
|
||||||
|
|
||||||
tache = data["taches"][id_tache]
|
|
||||||
if tache["assigné"] != interaction.user.id:
|
|
||||||
return await interaction.response.send_message("❌ Vous ne pouvez pas mettre à jour le statut de cette tâche.", ephemeral=True)
|
|
||||||
|
|
||||||
statut_update = await update_tache_status(id_tache, statut)
|
|
||||||
priorite_update = True
|
|
||||||
if priorite:
|
|
||||||
tache["priorité"] = priorite
|
|
||||||
await sauvegarder_taches(data)
|
|
||||||
|
|
||||||
if statut_update and priorite_update:
|
|
||||||
await interaction.response.send_message(
|
|
||||||
f"✅ Le statut de la tâche #{id_tache} a été mis à jour à **{statut}**." +
|
|
||||||
(f"\nLa priorité est maintenant **{priorite}**." if priorite else ""),
|
|
||||||
ephemeral=True
|
|
||||||
)
|
|
||||||
if tache["créé_par"] != interaction.user.id:
|
|
||||||
try:
|
|
||||||
creator = await self.bot.fetch_user(tache["créé_par"])
|
|
||||||
await creator.send(
|
|
||||||
f"🔔 La tâche #{id_tache} que vous avez créée a été mise à jour par {interaction.user.mention} :\n"
|
|
||||||
f"**Nouveau statut** : {statut}" +
|
|
||||||
(f"\n**Nouvelle priorité** : {priorite}" if priorite else "")
|
|
||||||
)
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
else:
|
|
||||||
await interaction.response.send_message("❌ Erreur lors de la mise à jour de la tâche.", ephemeral=True)
|
|
||||||
|
|
||||||
@update_tache.autocomplete("priorite")
|
|
||||||
async def autocomplete_priorite_update(self, interaction: discord.Interaction, current: str):
|
|
||||||
return await self.autocomplete_priorite(interaction, current)
|
|
||||||
|
|
||||||
@app_commands.command(name="supprimer_tache", description="Supprimer une tâche")
|
|
||||||
@app_commands.describe(
|
|
||||||
id_tache="ID de la tâche à supprimer"
|
|
||||||
)
|
|
||||||
async def supprimer_tache_cmd(self, interaction: discord.Interaction, id_tache: str):
|
|
||||||
data = await charger_taches()
|
|
||||||
if id_tache not in data.get("taches", {}):
|
|
||||||
return await interaction.response.send_message("❌ Tâche non trouvée.", ephemeral=True)
|
|
||||||
|
|
||||||
tache = data["taches"][id_tache]
|
|
||||||
perms = interaction.user.guild_permissions
|
|
||||||
can_delete = (
|
|
||||||
tache["créé_par"] == interaction.user.id or
|
|
||||||
tache["assigné"] == interaction.user.id or
|
|
||||||
perms.administrator
|
|
||||||
)
|
|
||||||
if not can_delete:
|
|
||||||
return await interaction.response.send_message("❌ Vous ne pouvez pas supprimer cette tâche.", ephemeral=True)
|
|
||||||
|
|
||||||
success = await supprimer_tache(id_tache)
|
|
||||||
if success:
|
|
||||||
await interaction.response.send_message(f"🗑️ La tâche #{id_tache} a été supprimée.", ephemeral=True)
|
|
||||||
else:
|
|
||||||
await interaction.response.send_message("❌ Erreur lors de la suppression de la tâche.", ephemeral=True)
|
|
||||||
|
|
||||||
@tasks.loop(seconds=30)
|
|
||||||
async def rappels(self):
|
|
||||||
now = datetime.now(ZoneInfo("Europe/Paris"))
|
|
||||||
if now.hour == 9 and now.minute == 0:
|
|
||||||
taches = await taches_proches()
|
|
||||||
for tid, t in taches.items():
|
|
||||||
try:
|
|
||||||
user = await self.bot.fetch_user(t["assigné"])
|
|
||||||
embed = discord.Embed(
|
|
||||||
title=f"🔔 Rappel Tâche #{tid}",
|
|
||||||
description=t["description"],
|
|
||||||
color=discord.Color.orange()
|
|
||||||
)
|
|
||||||
embed.add_field(name="🗂 Titre", value=t["titre"], inline=False)
|
|
||||||
embed.add_field(name="⚠️ Priorité", value=t["priorité"], inline=True)
|
|
||||||
embed.add_field(name="📅 Échéance", value=t["échéance"], inline=True)
|
|
||||||
embed.set_footer(text="Clique sur le bouton si tu as fini !")
|
|
||||||
view = RappelTacheView(tid)
|
|
||||||
await user.send(embed=embed, view=view)
|
|
||||||
except Exception:
|
|
||||||
continue
|
|
||||||
|
|
||||||
@rappels.before_loop
|
|
||||||
async def avant_rappel(self):
|
|
||||||
await self.bot.wait_until_ready()
|
|
||||||
|
|
||||||
async def setup(bot):
|
|
||||||
await bot.add_cog(JiraDiscord(bot))
|
|
||||||
64
taches.json
64
taches.json
|
|
@ -1,64 +0,0 @@
|
||||||
{
|
|
||||||
"taches": {
|
|
||||||
"7": {
|
|
||||||
"titre": "Contactez White",
|
|
||||||
"description": "Faire des vidéos de la map",
|
|
||||||
"assigné": 534003230435442690,
|
|
||||||
"statut": "À faire",
|
|
||||||
"priorité": "Critique",
|
|
||||||
"échéance": "2025-06-30",
|
|
||||||
"créé_par": 971446412690722826,
|
|
||||||
"commentaires": []
|
|
||||||
},
|
|
||||||
"10": {
|
|
||||||
"titre": "Vocal en rapport avec les sanctions récente",
|
|
||||||
"description": "J'aimerais que tu me programme une voc **URGENTE** avec Nooxi au plus vite et toi pour que je discute avec vous a propos des sanctions à votre encontre",
|
|
||||||
"assigné": 1108087282692522105,
|
|
||||||
"statut": "Terminée",
|
|
||||||
"priorité": "Critique",
|
|
||||||
"échéance": "2025-07-04",
|
|
||||||
"créé_par": 971446412690722826,
|
|
||||||
"commentaires": []
|
|
||||||
},
|
|
||||||
"8": {
|
|
||||||
"titre": "PRÉSENTATION DU SERVEUR",
|
|
||||||
"description": "refaire la présentation du serveur",
|
|
||||||
"assigné": 914118793918312480,
|
|
||||||
"statut": "À faire",
|
|
||||||
"priorité": "Haute",
|
|
||||||
"échéance": "2025-02-07",
|
|
||||||
"créé_par": 914118793918312480,
|
|
||||||
"commentaires": []
|
|
||||||
},
|
|
||||||
"9": {
|
|
||||||
"titre": "Liste Métier counter",
|
|
||||||
"description": "Bot qui ajoute automatiquement +1 ou -1 sur le poste quand un joueur reçoit le rôle",
|
|
||||||
"assigné": 971446412690722826,
|
|
||||||
"statut": "🛠️ V1.7 de Vicy en préparation. 📅 Sortie à définir.",
|
|
||||||
"priorité": "Moyenne",
|
|
||||||
"échéance": "2025-07-04",
|
|
||||||
"créé_par": 667478308870422568,
|
|
||||||
"commentaires": []
|
|
||||||
},
|
|
||||||
"3": {
|
|
||||||
"titre": "Mise en production de Vicy publique",
|
|
||||||
"description": "Création de Vicy publique",
|
|
||||||
"assigné": 971446412690722826,
|
|
||||||
"statut": "À faire",
|
|
||||||
"priorité": "Basse",
|
|
||||||
"échéance": "2025-09-01",
|
|
||||||
"créé_par": 971446412690722826,
|
|
||||||
"commentaires": []
|
|
||||||
},
|
|
||||||
"151": {
|
|
||||||
"titre": "dzqdz",
|
|
||||||
"description": "dzqqdz",
|
|
||||||
"assigné": 971446412690722826,
|
|
||||||
"statut": "Terminée",
|
|
||||||
"priorité": "Critique",
|
|
||||||
"échéance": "2025-07-22",
|
|
||||||
"créé_par": 971446412690722826,
|
|
||||||
"commentaires": []
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,89 +0,0 @@
|
||||||
import json
|
|
||||||
import os
|
|
||||||
import asyncio
|
|
||||||
from datetime import datetime
|
|
||||||
|
|
||||||
FICHIER_TACHES = "taches.json"
|
|
||||||
FICHIER_TACHES_TMP = "taches.json.tmp"
|
|
||||||
FICHIER_ID = "id_compteur.json"
|
|
||||||
file_lock = asyncio.Lock()
|
|
||||||
|
|
||||||
async def charger_taches():
|
|
||||||
async with file_lock:
|
|
||||||
if not os.path.exists(FICHIER_TACHES):
|
|
||||||
with open(FICHIER_TACHES, "w", encoding='utf-8') as f:
|
|
||||||
json.dump({"taches": {}}, f, indent=4)
|
|
||||||
with open(FICHIER_TACHES, "r", encoding='utf-8') as f:
|
|
||||||
return json.load(f)
|
|
||||||
|
|
||||||
async def sauvegarder_taches(donnees):
|
|
||||||
async with file_lock:
|
|
||||||
with open(FICHIER_TACHES_TMP, "w", encoding='utf-8') as f:
|
|
||||||
json.dump(donnees, f, indent=4, ensure_ascii=False)
|
|
||||||
os.replace(FICHIER_TACHES_TMP, FICHIER_TACHES)
|
|
||||||
|
|
||||||
async def generer_id_unique():
|
|
||||||
async with file_lock:
|
|
||||||
if not os.path.exists(FICHIER_ID):
|
|
||||||
with open(FICHIER_ID, "w", encoding='utf-8') as f:
|
|
||||||
json.dump({"dernier_id": 0}, f, indent=4)
|
|
||||||
|
|
||||||
with open(FICHIER_ID, "r+", encoding='utf-8') as f:
|
|
||||||
data = json.load(f)
|
|
||||||
data["dernier_id"] += 1
|
|
||||||
f.seek(0)
|
|
||||||
json.dump(data, f, indent=4)
|
|
||||||
f.truncate()
|
|
||||||
return str(data["dernier_id"])
|
|
||||||
|
|
||||||
async def ajouter_tache(titre, description, user_id, priorite, date_echeance, auteur_id):
|
|
||||||
data = await charger_taches()
|
|
||||||
taches = data["taches"]
|
|
||||||
id_tache = await generer_id_unique()
|
|
||||||
|
|
||||||
taches[id_tache] = {
|
|
||||||
"titre": titre,
|
|
||||||
"description": description,
|
|
||||||
"assigné": user_id,
|
|
||||||
"statut": "À faire",
|
|
||||||
"priorité": priorite,
|
|
||||||
"échéance": date_echeance,
|
|
||||||
"créé_par": auteur_id,
|
|
||||||
"commentaires": []
|
|
||||||
}
|
|
||||||
|
|
||||||
await sauvegarder_taches(data)
|
|
||||||
return id_tache
|
|
||||||
|
|
||||||
async def update_tache_status(id_tache, statut):
|
|
||||||
data = await charger_taches()
|
|
||||||
if id_tache in data["taches"]:
|
|
||||||
data["taches"][id_tache]["statut"] = statut
|
|
||||||
await sauvegarder_taches(data)
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
async def supprimer_tache(id_tache):
|
|
||||||
data = await charger_taches()
|
|
||||||
if id_tache in data["taches"]:
|
|
||||||
del data["taches"][id_tache]
|
|
||||||
await sauvegarder_taches(data)
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
async def lister_taches_utilisateur(user_id):
|
|
||||||
data = await charger_taches()
|
|
||||||
return {tid: t for tid, t in data["taches"].items() if t["assigné"] == user_id}
|
|
||||||
|
|
||||||
async def taches_proches():
|
|
||||||
data = await charger_taches()
|
|
||||||
proches = {}
|
|
||||||
now = datetime.now()
|
|
||||||
for tid, t in data["taches"].items():
|
|
||||||
try:
|
|
||||||
date_obj = datetime.strptime(t["échéance"], "%Y-%m-%d")
|
|
||||||
if 0 <= (date_obj - now).days <= 2:
|
|
||||||
proches[tid] = t
|
|
||||||
except ValueError:
|
|
||||||
continue
|
|
||||||
return proches
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue