331 lines
11 KiB
Python
331 lines
11 KiB
Python
import discord
|
||
from discord.ext import commands
|
||
import json
|
||
import aiohttp
|
||
import os
|
||
from dotenv import load_dotenv
|
||
from family import FamilyManager
|
||
from tree import generate_tree
|
||
from typing import Optional
|
||
|
||
# Configuration initiale
|
||
load_dotenv()
|
||
TOKEN = os.getenv("DISCORD_TOKEN")
|
||
|
||
with open("config.json") as f:
|
||
config = json.load(f)
|
||
|
||
intents = discord.Intents.default()
|
||
intents.members = True
|
||
intents.message_content = True
|
||
|
||
bot = commands.Bot(command_prefix="!", intents=intents)
|
||
family = FamilyManager("family.json")
|
||
|
||
async def download_avatar(member: discord.Member) -> Optional[str]:
|
||
"""Télécharge et enregistre l'avatar d'un membre"""
|
||
if not member.avatar:
|
||
return None
|
||
|
||
avatar_dir = "avatars"
|
||
os.makedirs(avatar_dir, exist_ok=True)
|
||
avatar_path = f"{avatar_dir}/{member.id}.png"
|
||
|
||
try:
|
||
async with aiohttp.ClientSession() as session:
|
||
async with session.get(str(member.avatar.url)) as resp:
|
||
if resp.status == 200:
|
||
with open(avatar_path, "wb") as f:
|
||
f.write(await resp.read())
|
||
return avatar_path
|
||
except Exception as e:
|
||
print(f"Erreur de téléchargement avatar {member.id}: {e}")
|
||
return None
|
||
|
||
async def update_member(member: discord.Member):
|
||
"""Met à jour les infos d'un membre dans la famille"""
|
||
avatar_url = str(member.avatar.url) if member.avatar else None
|
||
family.add_member(
|
||
str(member.id),
|
||
member.display_name,
|
||
avatar_url
|
||
)
|
||
await download_avatar(member)
|
||
|
||
@bot.event
|
||
async def on_ready():
|
||
print(f"Connecté en tant que {bot.user} (ID: {bot.user.id})")
|
||
try:
|
||
synced = await bot.tree.sync()
|
||
print(f"Commandes synchronisées ({len(synced)}): {[c.name for c in synced]}")
|
||
except Exception as e:
|
||
print(f"Erreur synchronisation commandes: {e}")
|
||
|
||
@bot.tree.command(name="enregistrer", description="Enregistre ou met à jour son profil")
|
||
async def enregistrer(interaction: discord.Interaction):
|
||
"""Commande pour que les membres enregistrent leurs infos"""
|
||
await update_member(interaction.user)
|
||
await interaction.response.send_message(
|
||
"✅ Profil mis à jour avec succès !",
|
||
ephemeral=True
|
||
)
|
||
@bot.tree.command(name="adopter", description="Adopter un membre")
|
||
async def adopter(interaction: discord.Interaction, enfant: discord.Member):
|
||
parent = interaction.user
|
||
|
||
# Debug
|
||
parent_gen = family.get_generation(str(parent.id))
|
||
enfant_gen = family.get_generation(str(enfant.id))
|
||
print(f"Adoption: {parent.display_name} (gen {parent_gen}) → {enfant.display_name} (gen {enfant_gen})")
|
||
|
||
# Validations
|
||
if parent.id == enfant.id:
|
||
await interaction.response.send_message("❌ Auto-adoption impossible !", ephemeral=True)
|
||
return
|
||
|
||
if enfant_gen > parent_gen:
|
||
await interaction.response.send_message(
|
||
"❌ Impossible d'adopter quelqu'un de plus ancien que vous !",
|
||
ephemeral=True
|
||
)
|
||
return
|
||
|
||
# Vérification relation existante
|
||
existing_parents = family.get_parents(str(enfant.id))
|
||
if any(p["id"] == str(parent.id) for p in existing_parents):
|
||
await interaction.response.send_message(
|
||
"❌ Vous avez déjà adopté cette personne !",
|
||
ephemeral=True
|
||
)
|
||
return
|
||
|
||
# Met à jour les infos des membres
|
||
await update_member(parent)
|
||
await update_member(enfant)
|
||
|
||
# Crée le lien familial
|
||
if not family.add_child(str(parent.id), str(enfant.id)):
|
||
await interaction.response.send_message(
|
||
"❌ Erreur lors de l'adoption (peut-être une relation existante ?)",
|
||
ephemeral=True
|
||
)
|
||
return
|
||
|
||
# Message de succès
|
||
message = f"✅ {parent.mention} a adopté {enfant.mention} !"
|
||
|
||
# Mentionne le partenaire si existe
|
||
partner_id = family.get_partner(str(parent.id))
|
||
if partner_id:
|
||
partner = interaction.guild.get_member(int(partner_id))
|
||
if partner:
|
||
message += f"\n👫 Partenaire : {partner.mention}"
|
||
|
||
await interaction.response.send_message(message)
|
||
|
||
@bot.tree.command(name="racine", description="Définir un membre comme racine")
|
||
@commands.has_permissions(administrator=True)
|
||
async def racine(interaction: discord.Interaction, membre: discord.Member, est_racine: bool):
|
||
success = family.set_as_root(str(membre.id), est_racine)
|
||
await interaction.response.send_message(
|
||
f"✅ {membre.mention} est maintenant {'une racine' if est_racine else 'plus une racine'} !"
|
||
if success else "❌ Erreur",
|
||
ephemeral=True
|
||
)
|
||
|
||
@bot.tree.command(name="couple", description="Officialiser une relation")
|
||
async def couple(interaction: discord.Interaction, partenaire: discord.Member):
|
||
membre = interaction.user
|
||
|
||
# Validations
|
||
if membre.id == partenaire.id:
|
||
await interaction.response.send_message(
|
||
"❌ Impossible de former un couple avec soi-même !",
|
||
ephemeral=True
|
||
)
|
||
return
|
||
|
||
# Met à jour les infos
|
||
await update_member(membre)
|
||
await update_member(partenaire)
|
||
|
||
if family.add_couple(str(membre.id), str(partenaire.id)):
|
||
await interaction.response.send_message(
|
||
f"💑 {membre.mention} et {partenaire.mention} sont maintenant partenaires !"
|
||
)
|
||
else:
|
||
await interaction.response.send_message(
|
||
"❌ Ces membres sont déjà en couple !",
|
||
ephemeral=True
|
||
)
|
||
|
||
|
||
@bot.tree.command(name="renier", description="Renier un enfant")
|
||
async def renier(interaction: discord.Interaction, enfant: discord.Member):
|
||
parent = interaction.user
|
||
|
||
# Supprime d'abord le lien parental
|
||
family.remove_child(str(parent.id), str(enfant.id))
|
||
|
||
# Vérifie si l'enfant n'a plus aucun parent
|
||
parents_restants = family.get_parents(str(enfant.id))
|
||
|
||
if not parents_restants:
|
||
# Suppression complète
|
||
if family.remove_member(str(enfant.id)):
|
||
await interaction.response.send_message(
|
||
f"❌ {parent.mention} a renié et supprimé {enfant.mention} de la famille !"
|
||
)
|
||
else:
|
||
await interaction.response.send_message(
|
||
"❌ Erreur lors de la suppression du membre",
|
||
ephemeral=True
|
||
)
|
||
else:
|
||
remaining_parents = ", ".join([f"<@{p['id']}>" for p in parents_restants])
|
||
await interaction.response.send_message(
|
||
f"⚠️ {enfant.mention} a encore des parents ({remaining_parents}). "
|
||
f"Seul le lien avec {parent.mention} a été rompu."
|
||
)
|
||
|
||
@bot.tree.command(name="arbre", description="Afficher l'arbre généalogique")
|
||
async def arbre(interaction: discord.Interaction):
|
||
await interaction.response.defer()
|
||
|
||
from tree import get_file_hash # Import direct de la fonction
|
||
|
||
json_file = "family.json"
|
||
png_file = "arbre.png"
|
||
hash_file = "family.hash"
|
||
|
||
try:
|
||
# Vérifier si la régénération est nécessaire
|
||
current_hash = get_file_hash(json_file)
|
||
regenerate = True
|
||
|
||
if os.path.exists(png_file):
|
||
try:
|
||
with open(hash_file, "r") as f:
|
||
if current_hash == f.read():
|
||
regenerate = False
|
||
except FileNotFoundError:
|
||
pass
|
||
|
||
if regenerate:
|
||
# Télécharger les avatars
|
||
members = family.get_all_members()
|
||
for member_id in members:
|
||
try:
|
||
member = await interaction.guild.fetch_member(int(member_id))
|
||
await download_avatar(member)
|
||
except:
|
||
continue
|
||
|
||
# Générer le nouvel arbre
|
||
generate_tree(json_file, png_file, config["ROOT_MEMBER_ID"])
|
||
|
||
# Sauvegarder le hash
|
||
with open(hash_file, "w") as f:
|
||
f.write(current_hash)
|
||
|
||
# Envoyer l'image
|
||
await interaction.followup.send(file=discord.File(png_file))
|
||
|
||
except Exception as e:
|
||
await interaction.followup.send(
|
||
"❌ Erreur lors de la génération de l'arbre",
|
||
ephemeral=True
|
||
)
|
||
print(f"Erreur génération arbre: {e}")
|
||
|
||
@bot.tree.command(name="parent", description="Voir les parents d'un membre")
|
||
async def parent(interaction: discord.Interaction, membre: Optional[discord.Member] = None):
|
||
target = membre or interaction.user
|
||
parents = family.get_parents(str(target.id))
|
||
|
||
if not parents:
|
||
await interaction.response.send_message(
|
||
f"ℹ️ {target.display_name} n'a pas de parent enregistré.",
|
||
ephemeral=True
|
||
)
|
||
return
|
||
|
||
parent_mentions = [f"<@{p['id']}>" for p in parents]
|
||
await interaction.response.send_message(
|
||
f"👪 Parents de {target.mention} : {' '.join(parent_mentions)}"
|
||
)
|
||
|
||
@bot.tree.command(name="enfants", description="Lister ses enfants")
|
||
async def enfants(interaction: discord.Interaction):
|
||
"""Commande pour lister ses enfants et son partenaire"""
|
||
membre_id = str(interaction.user.id)
|
||
|
||
# Récupération du partenaire
|
||
partner_id = family.get_partner(membre_id)
|
||
partner_mention = f"<@{partner_id}>" if partner_id else "Aucun"
|
||
|
||
# Récupération des enfants
|
||
enfants = family.get_children(membre_id)
|
||
enfants_mentions = [f"<@{enfant_id}>" for enfant_id in enfants] if enfants else ["Aucun"]
|
||
|
||
await interaction.response.send_message(
|
||
f"👨👩👧👦 **Famille de {interaction.user.mention}**\n"
|
||
f"• Partenaire : {partner_mention}\n"
|
||
f"• Enfant(s) : {' '.join(enfants_mentions)}",
|
||
ephemeral=True
|
||
)
|
||
|
||
@bot.tree.command(name="separer", description="Mettre fin à une relation")
|
||
async def separer(interaction: discord.Interaction, partenaire: discord.Member):
|
||
membre = interaction.user
|
||
|
||
# Validation
|
||
if membre.id == partenaire.id:
|
||
await interaction.response.send_message(
|
||
"❌ Impossible de se séparer de soi-même !",
|
||
ephemeral=True
|
||
)
|
||
return
|
||
|
||
# Vérifie que le couple existe
|
||
if not family.get_partner(str(membre.id)) == str(partenaire.id):
|
||
await interaction.response.send_message(
|
||
"❌ Vous n'êtes pas en couple avec cette personne",
|
||
ephemeral=True
|
||
)
|
||
return
|
||
|
||
# Exécute la séparation
|
||
if family.remove_couple(str(membre.id), str(partenaire.id)):
|
||
# Message avec mention des enfants s'il y en a
|
||
enfants = family.get_children(str(membre.id)) + family.get_children(str(partenaire.id))
|
||
|
||
if enfants:
|
||
enfants_mentions = ' '.join([f"<@{e}>" for e in enfants])
|
||
message = (
|
||
f"💔 {membre.mention} et {partenaire.mention} ont rompu.\n"
|
||
f"👶 Enfant(s) concerné(s) : {enfants_mentions}"
|
||
)
|
||
else:
|
||
message = f"💔 {membre.mention} et {partenaire.mention} ont rompu."
|
||
|
||
await interaction.response.send_message(message)
|
||
else:
|
||
await interaction.response.send_message(
|
||
"❌ Erreur lors de la séparation",
|
||
ephemeral=True
|
||
)
|
||
|
||
@bot.command()
|
||
@commands.has_permissions(administrator=True)
|
||
async def init(ctx):
|
||
"""Initialise tous les membres du serveur (admin seulement)"""
|
||
count = 0
|
||
for member in ctx.guild.members:
|
||
await update_member(member)
|
||
count += 1
|
||
|
||
await ctx.send(f"✅ {count} membres initialisés !")
|
||
|
||
bot.run(TOKEN)
|