Ajout: Syncronisation automatique des pp discord des agent O.K sur le site via Kuby
This commit is contained in:
parent
886bfa4b1e
commit
5a9e9b52d6
2 changed files with 180 additions and 1 deletions
5
.gitignore
vendored
5
.gitignore
vendored
|
|
@ -36,4 +36,7 @@ logs/
|
||||||
.vscode/
|
.vscode/
|
||||||
.idea/
|
.idea/
|
||||||
.DS_Store
|
.DS_Store
|
||||||
.roo/
|
.roo/
|
||||||
|
.continue/mcpServers/new-mcp-server.yaml
|
||||||
|
.continue/mcpServers/new-mcp-server-1.yaml
|
||||||
|
.gitignore
|
||||||
|
|
|
||||||
176
commandes/team_json.py
Normal file
176
commandes/team_json.py
Normal file
|
|
@ -0,0 +1,176 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
Team JSON Generator
|
||||||
|
===================
|
||||||
|
Ce module parcourt les membres d'un serveur Discord, filtre ceux qui possèdent
|
||||||
|
un ou plusieurs rôles spécifiés, télécharge leurs avatars et génère un fichier
|
||||||
|
JSON compatible avec le site de production.
|
||||||
|
|
||||||
|
Le JSON attendu ressemble à :
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"id": "123456789012345678",
|
||||||
|
"name": "Nom affiché",
|
||||||
|
"role": "Nom du rôle filtré",
|
||||||
|
"avatar": "123456789012345678.png",
|
||||||
|
"bio": "",
|
||||||
|
"stats": [],
|
||||||
|
"skills": [],
|
||||||
|
"social": {"discord":"","twitter":"",...}
|
||||||
|
},
|
||||||
|
...
|
||||||
|
]
|
||||||
|
|
||||||
|
Le champ ``avatar`` correspond au nom de fichier stocké dans ``website/avatars/``.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
import json
|
||||||
|
import aiohttp
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import List
|
||||||
|
import disnake
|
||||||
|
from disnake.ext import commands, tasks
|
||||||
|
|
||||||
|
# ----------------------------------------------------------------------
|
||||||
|
# Configuration – variables d'environnement (dans le .env du projet)
|
||||||
|
# ----------------------------------------------------------------------
|
||||||
|
TARGET_GUILD_ID = os.getenv("TARGET_GUILD_ID") # ID du serveur contenant l'équipe
|
||||||
|
# Liste d'IDs de rôles séparés par des virgules (ex. "123,456,789")
|
||||||
|
TEAM_ROLE_IDS = os.getenv("TEAM_ROLE_IDS", "").split(",") if os.getenv("TEAM_ROLE_IDS") else []
|
||||||
|
|
||||||
|
# Dossiers où seront stockés les avatars et le JSON final
|
||||||
|
AVATAR_ROOT = Path(__file__).parents[2] / "website" / "avatars"
|
||||||
|
AVATAR_ROOT.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
JSON_OUTPUT = Path(__file__).parents[2] / "website" / "team.json"
|
||||||
|
|
||||||
|
# ----------------------------------------------------------------------
|
||||||
|
class TeamJSON(commands.Cog):
|
||||||
|
"""Cog responsable de la génération du JSON d'équipe et du téléchargement des avatars."""
|
||||||
|
|
||||||
|
def __init__(self, bot: commands.Bot):
|
||||||
|
self.bot = bot
|
||||||
|
# Démarre la tâche quotidienne (synchronisation 24h/24)
|
||||||
|
self.sync_daily.start()
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
@tasks.loop(hours=24)
|
||||||
|
async def sync_daily(self):
|
||||||
|
"""Tâche planifiée exécutée chaque jour (cron)."""
|
||||||
|
await self._generate_team(trigger="cron")
|
||||||
|
|
||||||
|
@sync_daily.before_loop
|
||||||
|
async def before_sync(self):
|
||||||
|
await self.bot.wait_until_ready()
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
async def _download_avatar(self, session: aiohttp.ClientSession, member: disnake.Member) -> str:
|
||||||
|
"""Télécharge l'avatar du *member* et le sauvegarde dans ``AVATAR_ROOT``.
|
||||||
|
Retourne le nom de fichier (ex. ``"123456789.png"``)."""
|
||||||
|
avatar_url = str(member.display_avatar.url)
|
||||||
|
filename = f"{member.id}.png"
|
||||||
|
dest_path = AVATAR_ROOT / filename
|
||||||
|
try:
|
||||||
|
async with session.get(avatar_url) as resp:
|
||||||
|
if resp.status == 200:
|
||||||
|
dest_path.write_bytes(await resp.read())
|
||||||
|
else:
|
||||||
|
self.bot.logger.warning(
|
||||||
|
f"[TeamJSON] Avatar download failed for {member.id} – HTTP {resp.status}"
|
||||||
|
)
|
||||||
|
except Exception as exc:
|
||||||
|
self.bot.logger.error(f"[TeamJSON] Exception while downloading avatar {member.id}: {exc}")
|
||||||
|
return filename
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
async def _generate_team(self, *, trigger: str = "manual") -> None:
|
||||||
|
"""Construit le JSON de l'équipe et télécharge les avatars.
|
||||||
|
|
||||||
|
Le JSON produit suit exactement le format attendu par le site de production.
|
||||||
|
Les champs ``stats``, ``skills`` et ``social`` sont laissés vides car ils ne
|
||||||
|
sont pas disponibles via l'API bot.
|
||||||
|
"""
|
||||||
|
if not TARGET_GUILD_ID:
|
||||||
|
self.bot.logger.error("[TeamJSON] TARGET_GUILD_ID non défini – arrêt du générateur.")
|
||||||
|
return
|
||||||
|
if not TEAM_ROLE_IDS:
|
||||||
|
self.bot.logger.error("[TeamJSON] TEAM_ROLE_IDS non défini – arrêt du générateur.")
|
||||||
|
return
|
||||||
|
|
||||||
|
# Récupération de la guilde
|
||||||
|
guild = self.bot.get_guild(int(TARGET_GUILD_ID))
|
||||||
|
if not guild:
|
||||||
|
try:
|
||||||
|
guild = await self.bot.fetch_guild(int(TARGET_GUILD_ID))
|
||||||
|
except Exception as exc:
|
||||||
|
self.bot.logger.error(f"[TeamJSON] Impossible de récupérer la guilde {TARGET_GUILD_ID}: {exc}")
|
||||||
|
return
|
||||||
|
|
||||||
|
# Convertir les IDs de rôle en entiers pour la comparaison
|
||||||
|
role_ids = {int(r) for r in TEAM_ROLE_IDS if r.strip()}
|
||||||
|
members_data: List[dict] = []
|
||||||
|
|
||||||
|
async with aiohttp.ClientSession() as session:
|
||||||
|
async for member in guild.fetch_members(limit=None):
|
||||||
|
if member.bot:
|
||||||
|
continue
|
||||||
|
# Intersection entre les rôles du membre et les rôles ciblés
|
||||||
|
member_role_ids = {r.id for r in member.roles}
|
||||||
|
intersect = member_role_ids.intersection(role_ids)
|
||||||
|
if not intersect:
|
||||||
|
continue # le membre ne possède aucun des rôles recherchés
|
||||||
|
|
||||||
|
# On prend le premier rôle correspondant comme "role" dans le JSON
|
||||||
|
role_name = next((r.name for r in member.roles if r.id in intersect), "")
|
||||||
|
avatar_file = await self._download_avatar(session, member)
|
||||||
|
|
||||||
|
entry = {
|
||||||
|
"id": str(member.id),
|
||||||
|
"name": member.display_name,
|
||||||
|
"role": role_name,
|
||||||
|
"avatar": avatar_file,
|
||||||
|
"bio": "", # non récupérable via l'API bot
|
||||||
|
"stats": [],
|
||||||
|
"skills": [],
|
||||||
|
"social": {
|
||||||
|
"discord": "",
|
||||||
|
"twitter": "",
|
||||||
|
"instagram": "",
|
||||||
|
"youtube": "",
|
||||||
|
"tiktok": "",
|
||||||
|
"github": "",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
members_data.append(entry)
|
||||||
|
|
||||||
|
# Écriture du JSON final
|
||||||
|
try:
|
||||||
|
JSON_OUTPUT.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
JSON_OUTPUT.write_text(
|
||||||
|
json.dumps(members_data, indent=2, ensure_ascii=False),
|
||||||
|
encoding="utf-8",
|
||||||
|
)
|
||||||
|
self.bot.logger.info(
|
||||||
|
f"[TeamJSON] JSON généré ({len(members_data)} membres) → {JSON_OUTPUT}"
|
||||||
|
)
|
||||||
|
except Exception as exc:
|
||||||
|
self.bot.logger.error(f"[TeamJSON] Échec de l'écriture du JSON : {exc}")
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
@commands.slash_command(
|
||||||
|
name="generate_team_json",
|
||||||
|
description="Génère le JSON de l'équipe (admin only)"
|
||||||
|
)
|
||||||
|
@commands.has_permissions(administrator=True)
|
||||||
|
async def generate_team_json(self, inter: disnake.ApplicationCommandInteraction):
|
||||||
|
"""Commande manuelle pour (re)générer le JSON et les avatars."""
|
||||||
|
await inter.response.defer(ephemeral=True)
|
||||||
|
await self._generate_team(trigger="manual")
|
||||||
|
await inter.edit_original_response(
|
||||||
|
content="✅ JSON de l'équipe généré et avatars synchronisés."
|
||||||
|
)
|
||||||
|
|
||||||
|
# ----------------------------------------------------------------------
|
||||||
|
def setup(bot: commands.Bot):
|
||||||
|
bot.add_cog(TeamJSON(bot))
|
||||||
Loading…
Add table
Add a link
Reference in a new issue