commit 56958c583218897338e358ad1a719460047bba34 Author: utilisateurqu <112496102+utilisateurqu@users.noreply.github.com> Date: Sun Aug 16 19:37:15 2026 +0200 Initial commit: bot Discord Vicy V1.8 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..26b369d --- /dev/null +++ b/.gitignore @@ -0,0 +1,19 @@ +# Secrets +.env +config.json + +# Python +__pycache__/ +*.pyc +*.pyo + +# Virtual environment +.venv/ + +# Data & logs +data/ +log.txt + +# OS +Thumbs.db +Desktop.ini diff --git a/__main__.py b/__main__.py new file mode 100644 index 0000000..f841bfc --- /dev/null +++ b/__main__.py @@ -0,0 +1,133 @@ +import discord +import asyncio +import logging +from bot import bot +from utils.logger import log_info, log_error +from discord.ext import commands +from discord import app_commands, Intents +from dotenv import load_dotenv +import os +from discord import Intents, Permissions +from utils.stats_manager import enregistrer_utilisation, get_stats + +# Configuration du logger Discord (niveau DEBUG pour afficher tous les événements importants) +logging.basicConfig(level=logging.DEBUG) +discord.utils.setup_logging(level=logging.DEBUG, root=False) + +# Configuration des intents +intents = Intents.default() +intents.message_content = True +intents.guilds = True +intents.members = True +intents.reactions = True + +# Permissions du bot +permissions = Permissions() +permissions.update( + manage_nicknames=True, + send_messages=True, + read_messages=True, + manage_channels=True, + manage_messages=True, + add_reactions=True, + use_application_commands=True, + view_channel=True +) + +# Initialisation du bot +bot = commands.Bot(command_prefix="!", application_id="1310235313461002241", intents=intents) + +async def load_cogs(): + """Charge toutes les commandes.""" + extensions = [ + "commandes.message", + "commandes.identite", + "commandes.voir_carte_identite", + "commandes.virement", + "commandes.transactions", + "commandes.ajouter_argent", + "commandes.ticket", + "commandes.restranscription_staff", + "utils.welcome_message", + "commandes.permis", + "commandes.ouvrir_compte", + "commandes.compte", + "commandes.change_nickname", + "commandes.rename_all_on_join", + "commandes.message_tous", + "commandes.retirer_argent", + "commandes.ouvrir_comptes_entreprises", + "commandes.compte_gouvernement", + "commandes.comptes_entreprises", + "commandes.compte_urssaf", + "commandes.compte_banque_nationale", + "commandes.virement_entreprise", + "commandes.virement_gouvernement", + "commandes.valider_visite_médicale", + "commandes.abonnement", + "commandes.virement_banquier", + "commandes.virement_banquier_entreprise", + "commandes.compte_fbm", + "commandes.virement_fbm", + "commandes.virement_ursaff", + "commandes.stats_commandes", + "commandes.taches", + "commandes.roll", + "commandes.annonces_sessions", # Fait : V1.7 correction des annonces de sessions + "commandes.retrait_gouv", + "commandes.absence_staff", # Fait : V1.7 pouvoir voir plus de 3 absences staff (pagination) + "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: + try: + await bot.load_extension(ext) + log_info(f"Extension chargée : {ext}") + except Exception as e: + log_error(f"Erreur lors du chargement de {ext} : {e}") + +@bot.event +async def on_ready(): + # Affiche que le bot est prêt et synchronise les commandes slash + log_info(f"Connecté en tant que {bot.user}") + await load_cogs() + await bot.tree.sync() + await bot.change_presence(activity=discord.Game(name="la V1.6")) # Ajout du status + log_info("Commandes synchronisées.") + +# Gère les erreurs de commandes classiques +@bot.event +async def on_command_error(ctx, error): + log_error(f"Erreur dans la commande {ctx.command}: {error}") + +# Gère toutes les autres erreurs inattendues +@bot.event +async def on_error(event_method, *args, **kwargs): + log_error(f"Erreur non gérée dans l'événement {event_method}", exc_info=True) + +# 👉 Enregistre chaque commande slash utilisée +@bot.event +async def on_app_command_completion(interaction: discord.Interaction, command: app_commands.Command): + try: + user_id = interaction.user.id + guild_id = interaction.guild.id if interaction.guild else 0 + enregistrer_utilisation(command.name, user_id, guild_id) + except Exception as e: + log_error(f"Erreur en enregistrant les stats : {e}") + +async def main(): + # Charge les variables d'environnement et démarre le bot + load_dotenv() + token = os.getenv("TOKEN") + try: + log_info("Démarrage du bot...") + await bot.start(token) + except Exception as e: + log_error(f"Erreur critique: {e}", exc_info=True) + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/abonnements.json b/abonnements.json new file mode 100644 index 0000000..49819bb --- /dev/null +++ b/abonnements.json @@ -0,0 +1,218 @@ +[ + { + "nom": "Test", + "rib_emetteur": "22144 59424 87742058459 72", + "rib_receveur": "66427 49104 57218570654 78", + "montant": 1.0, + "banque": "NGC Bank", + "jour": "samedi" + }, + { + "nom": "Frais_bancaire_et_de_gestion_1", + "rib_emetteur": "36495 80083 39542816185 27", + "rib_receveur": "15156 38436 35920 54357", + "montant": 5.0, + "banque": "NGC Bank", + "jour": "dimanche" + }, + { + "nom": "Frais_bancaire_et_de_gestion_1", + "rib_emetteur": "26384 93370 75890178377 91", + "rib_receveur": "15156 38436 35920 54357", + "montant": 5.0, + "banque": "NGC Bank", + "jour": "dimanche" + }, + { + "nom": "Frais_bancaire_et_de_gestion_1", + "rib_emetteur": "22144 59424 87742058459 72", + "rib_receveur": "15156 38436 35920 54357", + "montant": 5.0, + "banque": "NGC Bank", + "jour": "dimanche" + }, + { + "nom": "Frais_bancaire_et_de_gestion_1", + "rib_emetteur": "85047 60626 80894743346 94", + "rib_receveur": "15156 38436 35920 54357", + "montant": 5.0, + "banque": "NGC Bank", + "jour": "dimanche" + }, + { + "nom": "Frais_bancaire_et_de_gestion_1", + "rib_emetteur": "28777 24580 29765721710 42", + "rib_receveur": "15156 38436 35920 54357", + "montant": 5.0, + "banque": "NGC Bank", + "jour": "dimanche" + }, + { + "nom": "Frais_bancaire_et_de_gestion_1", + "rib_emetteur": "24483 98056 61791340797 80", + "rib_receveur": "15156 38436 35920 54357", + "montant": 5.0, + "banque": "NGC Bank", + "jour": "dimanche" + }, + { + "nom": "Frais_bancaire_et_de_gestion_1", + "rib_emetteur": "10009 40554 56452790470 78", + "rib_receveur": "15156 38436 35920 54357", + "montant": 5.0, + "banque": "NGC Bank", + "jour": "dimanche" + }, + { + "nom": "Frais_bancaire_et_de_gestion_1", + "rib_emetteur": "76644 32901 90493249064 16", + "rib_receveur": "15156 38436 35920 54357", + "montant": 5.0, + "banque": "NGC Bank", + "jour": "dimanche" + }, + { + "nom": "Frais_bancaire_et_de_gestion_1", + "rib_emetteur": "15563 69899 51361446295 84", + "rib_receveur": "15156 38436 35920 54357", + "montant": 5.0, + "banque": "NGC Bank", + "jour": "dimanche" + }, + { + "nom": "Frais_bancaire_et_de_gestion_1", + "rib_emetteur": "22128 86524 35982837421 58", + "rib_receveur": "15156 38436 35920 54357", + "montant": 5.0, + "banque": "NGC Bank", + "jour": "dimanche" + }, + { + "nom": "Frais_bancaire_et_de_gestion_1", + "rib_emetteur": "55868 11389 89049343014 38", + "rib_receveur": "15156 38436 35920 54357", + "montant": 5.0, + "banque": "NGC Bank", + "jour": "dimanche" + }, + { + "nom": "Frais_bancaire_et_de_gestion_1", + "rib_emetteur": "93266 82343 74845298309 22", + "rib_receveur": "15156 38436 35920 54357", + "montant": 5.0, + "banque": "NGC Bank", + "jour": "dimanche" + }, + { + "nom": "Frais_bancaire_et_de_gestion_1", + "rib_emetteur": "10234 24218 10477731114 78", + "rib_receveur": "15156 38436 35920 54357", + "montant": 5.0, + "banque": "NGC Bank", + "jour": "dimanche" + }, + { + "nom": "Frais_bancaire_et_de_gestion_1", + "rib_emetteur": "50970 51336 33017058086 50", + "rib_receveur": "15156 38436 35920 54357", + "montant": 5.0, + "banque": "NGC Bank", + "jour": "dimanche" + }, + { + "nom": "Frais_bancaire_et_de_gestion_1", + "rib_emetteur": "87292 81104 84784268868 12", + "rib_receveur": "15156 38436 35920 54357", + "montant": 5.0, + "banque": "NGC Bank", + "jour": "dimanche" + }, + { + "nom": "Frais_bancaire_et_de_gestion_1", + "rib_emetteur": "51229 80290 86979675797 93", + "rib_receveur": "15156 38436 35920 54357", + "montant": 5.0, + "banque": "NGC Bank", + "jour": "dimanche" + }, + { + "nom": "Frais_bancaire_et_de_gestion_1", + "rib_emetteur": "28794 77878 41312953164 71", + "rib_receveur": "15156 38436 35920 54357", + "montant": 5.0, + "banque": "NGC Bank", + "jour": "dimanche" + }, + { + "nom": "Frais_bancaire_et_de_gestion_1", + "rib_emetteur": "78740 15751 70195185799 30", + "rib_receveur": "15156 38436 35920 54357", + "montant": 5.0, + "banque": "NGC Bank", + "jour": "dimanche" + }, + { + "nom": "Frais_bancaire_et_de_gestion_1", + "rib_emetteur": "92871 52823 19649316403 70", + "rib_receveur": "15156 38436 35920 54357", + "montant": 5.0, + "banque": "NGC Bank", + "jour": "dimanche" + }, + { + "nom": "Frais_bancaire_et_de_gestion_1", + "rib_emetteur": "73834 54818 11833486819 17", + "rib_receveur": "15156 38436 35920 54357", + "montant": 5.0, + "banque": "NGC Bank", + "jour": "dimanche" + }, + { + "nom": "Frog", + "rib_emetteur": "48003 84424 36961155485 64", + "rib_receveur": "15156 38436 35920 54357", + "montant": 700.0, + "banque": "NGC Bank", + "jour": "dimanche" + }, + { + "nom": "Di regia", + "rib_emetteur": "10009 40554 56452790470 78", + "rib_receveur": "15156 38436 35920 54357", + "montant": 4000.0, + "banque": "NGC Bank", + "jour": "dimanche" + }, + { + "nom": "Leonardo", + "rib_emetteur": "24756 54403 68357307735 95", + "rib_receveur": "15156 38436 35920 54357", + "montant": 4000.0, + "banque": "NGC Bank", + "jour": "dimanche" + }, + { + "nom": "Correa", + "rib_emetteur": "28794 77878 41312953164 71", + "rib_receveur": "15156 38436 35920 54357", + "montant": 4300.0, + "banque": "NGC Bank", + "jour": "dimanche" + }, + { + "nom": "jose", + "rib_emetteur": "87292 81104 84784268868 12", + "rib_receveur": "15156 38436 35920 54357", + "montant": 800.0, + "banque": "NGC Bank", + "jour": "dimanche" + }, + { + "nom": "Paye_de_Salvatore_Di_Regia", + "rib_emetteur": "15156 38436 35920 54357", + "rib_receveur": "10009 40554 56452790470 78", + "montant": 5000.0, + "banque": "NGC Bank", + "jour": "dimanche" + } +] \ No newline at end of file diff --git a/absences.json b/absences.json new file mode 100644 index 0000000..f66a0eb --- /dev/null +++ b/absences.json @@ -0,0 +1,47 @@ +{ + "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" + } +} \ No newline at end of file diff --git a/banque_data.json b/banque_data.json new file mode 100644 index 0000000..9e26dfe --- /dev/null +++ b/banque_data.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/bot.py b/bot.py new file mode 100644 index 0000000..db66cd7 --- /dev/null +++ b/bot.py @@ -0,0 +1,25 @@ +from utils.logger import log_info, log_error +from discord.ext import commands +import discord + +intents = discord.Intents.default() +intents.message_content = True +intents.members = True + +bot = commands.Bot(command_prefix='/', intents=intents) + +@bot.event +async def on_ready(): + try: + log_info(f"Le bot {bot.user} a démarré avec succès.") + await bot.change_presence(activity=discord.Game(name="la version 1.2")) + except Exception as e: + log_error(f"Erreur lors du démarrage du bot: {e}") + +@bot.event +async def on_ready(): + try: + synced = await bot.tree.sync() + print(f"Synchronisé {len(synced)} commande(s)") + except Exception as e: + print(f"Erreur lors de la synchronisation des commandes : {e}") \ No newline at end of file diff --git a/commandes/Backup/absence_staff.py b/commandes/Backup/absence_staff.py new file mode 100644 index 0000000..47fa3ab --- /dev/null +++ b/commandes/Backup/absence_staff.py @@ -0,0 +1,66 @@ +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 !") diff --git a/commandes/__init__.py b/commandes/__init__.py new file mode 100644 index 0000000..e496dbb --- /dev/null +++ b/commandes/__init__.py @@ -0,0 +1,5 @@ + # Pour la V1.2 Ajouter des statistiques d'utlisation du bot + # Pour la V1.3 Corriger le système de claim des tickets WL (Les anciens propriétaires de ticket garde les permission même après une reprise par quelqu'un d'autre) + # Pour la V1.3 Ajouter la commande /compte_utilisateur @[de_l'utilisateur] : affiche le compte d'un autre utilisateur (Banquier Only) + # Pour la V1.3 Pouvoir voir les cartes d'identités des autres quand on la le rôle gouvernement + # Pour la V1.3 Pouvoir fermer des comptes bancaires ou/et pouvoir régénérer un rib si on est banquier \ No newline at end of file diff --git a/commandes/abonnement.py b/commandes/abonnement.py new file mode 100644 index 0000000..17750d5 --- /dev/null +++ b/commandes/abonnement.py @@ -0,0 +1,148 @@ +import discord +from discord import app_commands +from discord.ext import commands, tasks +import json +import datetime + +ABONNEMENTS_FILE = "abonnements.json" + +BANQUES_DISPONIBLES = [ + "Victoria's Credit Bank", + "NGC Bank" +] + +JOURS_SEMAINE = [ + "lundi", "mardi", "mercredi", "jeudi", "vendredi", "samedi", "dimanche" +] + +class Abonnement(commands.Cog): + def __init__(self, bot): + self.bot = bot + self.abonnements = self.load_abonnements() + self.executer_abonnements.start() + + def load_abonnements(self): + try: + with open(ABONNEMENTS_FILE, 'r', encoding='utf-8') as f: + return json.load(f) + except (FileNotFoundError, json.JSONDecodeError): + return [] + + def save_abonnements(self): + with open(ABONNEMENTS_FILE, 'w', encoding='utf-8') as f: + json.dump(self.abonnements, f, ensure_ascii=False, indent=4) + + # 🎯 Auto-complétion pour les banques + async def autocomplete_banque(self, interaction: discord.Interaction, current: str): + return [ + app_commands.Choice(name=b, value=b) + for b in BANQUES_DISPONIBLES if current.lower() in b.lower() + ] + + # 🎯 Auto-complétion pour les jours + async def autocomplete_jour(self, interaction: discord.Interaction, current: str): + return [ + app_commands.Choice(name=day.capitalize(), value=day) + for day in JOURS_SEMAINE if current.lower() in day + ] + + @app_commands.command(name="abonnement", description="Crée un abonnement bancaire hebdomadaire.") + @app_commands.describe( + nom="Nom de l'abonnement", + rib_emetteur="RIB de l'expéditeur", + montant="Montant à envoyer", + rib_receveur="RIB du bénéficiaire", + banque="Nom de la banque", + jour="Jour de la semaine du virement" + ) + @app_commands.autocomplete(banque=autocomplete_banque, jour=autocomplete_jour) + async def abonnement( + self, + interaction: discord.Interaction, + nom: str, + rib_emetteur: str, + montant: float, + rib_receveur: str, + banque: str, + jour: str + ): + if jour.lower() not in JOURS_SEMAINE: + await interaction.response.send_message("❌ Jour invalide. Choisissez un jour valide de la semaine.", ephemeral=True) + return + + abonnement = { + "nom": nom, + "rib_emetteur": rib_emetteur, + "rib_receveur": rib_receveur, + "montant": montant, + "banque": banque, + "jour": jour.lower() + } + + self.abonnements.append(abonnement) + self.save_abonnements() + + await interaction.response.send_message(f"✅ Abonnement **{nom}** enregistré. Virement automatique tous les **{jour}**.", ephemeral=True) + + @tasks.loop(minutes=60) + async def executer_abonnements(self): + today = datetime.datetime.now().strftime("%A").lower() + try: + with open("comptes_bancaires.json", "r", encoding="utf-8") as f: + comptes = json.load(f) + except: + comptes = {} + + updated = False + for abonnement in self.abonnements: + if abonnement["jour"] != today: + continue + + banque = abonnement["banque"] + rib_emetteur = abonnement["rib_emetteur"] + rib_receveur = abonnement["rib_receveur"] + montant = abonnement["montant"] + + destinataire_id = None + emetteur_id = None + + for user_id, data in comptes.get(banque, {}).items(): + if data.get("rib") == rib_emetteur: + emetteur_id = user_id + if data.get("rib") == rib_receveur: + destinataire_id = user_id + + if not emetteur_id or not destinataire_id: + continue + + if comptes[banque][emetteur_id]["solde"] < montant: + continue + + comptes[banque][emetteur_id]["solde"] -= montant + comptes[banque][emetteur_id]["transactions"].append({ + "date": datetime.datetime.now().strftime("%d/%m/%Y %H:%M:%S"), + "type": "Virement automatique (abonnement)", + "source": f"Vers RIB {rib_receveur}", + "montant": -montant + }) + + comptes[banque][destinataire_id]["solde"] += montant + comptes[banque][destinataire_id]["transactions"].append({ + "date": datetime.datetime.now().strftime("%d/%m/%Y %H:%M:%S"), + "type": "Virement reçu (abonnement)", + "source": f"{rib_emetteur}", + "montant": montant + }) + + updated = True + + if updated: + with open("comptes_bancaires.json", 'w', encoding='utf-8') as f: + json.dump(comptes, f, ensure_ascii=False, indent=4) + + @executer_abonnements.before_loop + async def before_abonnement_loop(self): + await self.bot.wait_until_ready() + +async def setup(bot): + await bot.add_cog(Abonnement(bot)) \ No newline at end of file diff --git a/commandes/absence_staff.py b/commandes/absence_staff.py new file mode 100644 index 0000000..ed9a334 --- /dev/null +++ b/commandes/absence_staff.py @@ -0,0 +1,224 @@ +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)) \ No newline at end of file diff --git a/commandes/ajouter_argent.py b/commandes/ajouter_argent.py new file mode 100644 index 0000000..80b386f --- /dev/null +++ b/commandes/ajouter_argent.py @@ -0,0 +1,146 @@ +import discord +from discord import app_commands +from discord.ext import commands +import json +from datetime import datetime + +BANQUIER_ROLE_ID = 1360314740567576616 # ID du rôle des banquiers # ID du rôle banquier # ID du rôle banquier + +class AjouterArgent(commands.Cog): + def __init__(self, bot): + self.bot = bot + + # Choix de banques automatiques + banque_choices = [ + app_commands.Choice(name="Victoria's Credit Bank", value="Victoria's Credit Bank"), + app_commands.Choice(name="NGC Bank", value="NGC Bank"), + ] + + @app_commands.command( + name="ajouter_argent", + description="Ajoute de l'argent à un compte (réservé aux banquiers)." + ) + @app_commands.choices(banque=banque_choices) + @app_commands.describe( + rib="Le RIB du compte", + montant="Montant à ajouter", + banque="Banque cible (facultatif)" + ) + async def ajouter_argent( + self, + interaction: discord.Interaction, + rib: str, + montant: float, + banque: app_commands.Choice[str] = None + ): + # Vérification du rôle + if not any(role.id == BANQUIER_ROLE_ID for role in interaction.user.roles): + await interaction.response.send_message( + "❌ Vous n'avez pas la permission d'utiliser cette commande.", + ephemeral=True + ) + return + + if montant <= 0: + await interaction.response.send_message( + "❌ Le montant doit être supérieur à zéro.", + ephemeral=True + ) + return + + banque_nom = banque.value if banque else None + + # Chargement des fichiers + with open("comptes_bancaires.json", "r", encoding="utf-8") as f: + comptes = json.load(f) + with open("comptes_entreprises.json", "r", encoding="utf-8") as f: + entreprises = json.load(f) + + cible_data = None + user_id = None + is_entreprise = False + + # Recherche dans la banque spécifiée (si précisée) + if banque_nom and banque_nom in comptes: + for uid, info in comptes[banque_nom].items(): + if info.get("rib") == rib: + cible_data = comptes + user_id = uid + break + + if not user_id and banque_nom and banque_nom in entreprises: + for nom_entreprise, info in entreprises[banque_nom].items(): + if info.get("rib") == rib: + cible_data = entreprises + user_id = nom_entreprise + is_entreprise = True + break + + # Si banque non précisée ou compte non trouvé, recherche dans toutes les banques + if not user_id and not banque_nom: + # Recherche dans toutes les banques utilisateurs + for banque_courante, comptes_banque in comptes.items(): + for uid, info in comptes_banque.items(): + if info.get("rib") == rib: + cible_data = comptes + user_id = uid + banque_nom = banque_courante + break + if user_id: + break + + if not user_id and not is_entreprise: + # Recherche dans toutes les banques entreprises + for banque_courante, entreprises_banque in entreprises.items(): + for nom_entreprise, info in entreprises_banque.items(): + if info.get("rib") == rib: + cible_data = entreprises + user_id = nom_entreprise + banque_nom = banque_courante + is_entreprise = True + break + if user_id: + break + + if not user_id: + await interaction.response.send_message( + "❌ Aucun compte trouvé avec ce RIB.", + ephemeral=True + ) + return + + # Ajout du montant + cible_data[banque_nom][user_id]["solde"] += montant + transaction = { + "date": datetime.now().strftime("%d/%m/%Y %H:%M:%S"), + "type": "Dépôt", + "source": "Banque", + "montant": montant, + "emetteur": interaction.user.id, + "destinataire": user_id + } + cible_data[banque_nom][user_id]["transactions"].append(transaction) + + # Sauvegarde + if is_entreprise: + with open("comptes_entreprises.json", "w", encoding="utf-8") as f: + json.dump(entreprises, f, ensure_ascii=False, indent=4) + else: + with open("comptes_bancaires.json", "w", encoding="utf-8") as f: + json.dump(comptes, f, ensure_ascii=False, indent=4) + # Envoyer un MP à l'utilisateur (sauf entreprise) + try: + user = await self.bot.fetch_user(int(user_id)) + await user.send( + f"💰 Un dépôt de **{montant:.2f}€** a été effectué sur votre compte dans **{banque_nom}**." + ) + except Exception: + pass # Si l'utilisateur a désactivé les MP + + await interaction.response.send_message( + f"✅ **{montant:.2f}€** ajoutés avec succès au compte `{rib}` dans la banque **{banque_nom}**.", + ephemeral=True + ) + +async def setup(bot): + await bot.add_cog(AjouterArgent(bot)) \ No newline at end of file diff --git a/commandes/annonces_sessions.py b/commandes/annonces_sessions.py new file mode 100644 index 0000000..dfdd2ae --- /dev/null +++ b/commandes/annonces_sessions.py @@ -0,0 +1,220 @@ +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> \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)) diff --git a/commandes/change_nickname.py b/commandes/change_nickname.py new file mode 100644 index 0000000..3ddcdaa --- /dev/null +++ b/commandes/change_nickname.py @@ -0,0 +1,45 @@ +import discord +from discord import app_commands +from discord.ext import commands +import re + +class FormatAllNicknames(commands.Cog): + def __init__(self, bot): + self.bot = bot + + @app_commands.command(name="formatall", description="Force tous les pseudos au format 'Prénom Nom | Epic'") + @app_commands.checks.has_permissions(manage_nicknames=True) + async def format_all(self, interaction: discord.Interaction): + await interaction.response.defer(thinking=True) + guild = interaction.guild + modified_count = 0 + + # Regex : deux mots avant le "|" + pattern_correct = re.compile(r"^[^\|]+\s[^\|]+\s\| .+") + + for member in guild.members: + if member.bot: + continue + + current_nick = member.nick or member.name + + # S'il respecte déjà le bon format ("Prénom Nom | Quelquechose"), on skip + if pattern_correct.match(current_nick): + continue + + try: + # Sinon, forcer à "Prénom Nom | Epic" + new_nick = "Prénom Nom | Epic" + + await member.edit(nick=new_nick) + modified_count += 1 + + except discord.Forbidden: + continue + except discord.HTTPException: + continue + + await interaction.followup.send(f"✅ Renommage terminé : {modified_count} membres modifiés.") + +async def setup(bot): + await bot.add_cog(FormatAllNicknames(bot)) \ No newline at end of file diff --git a/commandes/compte.py b/commandes/compte.py new file mode 100644 index 0000000..27e35ae --- /dev/null +++ b/commandes/compte.py @@ -0,0 +1,93 @@ +import discord +from discord import app_commands +from discord.ext import commands +from utils.banque_manager import BanqueManager + +banque_manager = BanqueManager() + +class Compte(commands.Cog): + def __init__(self, bot): + self.bot = bot + + @app_commands.command( + name="compte", + description="Afficher vos comptes bancaires et vos 3 dernières transactions" + ) + @app_commands.describe( + membre="Afficher le compte d'un autre membre (réservé au gouvernement)" + ) + async def compte(self, interaction: discord.Interaction, membre: discord.Member = None): + """Affiche les soldes et dernières transactions de l'utilisateur ou d'un autre membre (si gouvernement)""" + + banque_manager.load_data() + + # Par défaut, on cible l'utilisateur qui exécute la commande + cible = interaction.user + user_id = str(cible.id) + + # Si un autre membre est précisé, on vérifie le rôle + if membre and membre != interaction.user: + role_gouv = discord.utils.get(interaction.guild.roles, name="🌍│Gouvernement") + if role_gouv not in interaction.user.roles: + await interaction.response.send_message( + "❌ Vous devez avoir le rôle **gouvernement** pour consulter le compte d'un autre membre.", + ephemeral=True + ) + return + cible = membre + user_id = str(membre.id) + + comptes = banque_manager.comptes + + embed = discord.Embed( + title=f"💳 Comptes bancaires de {cible.display_name}", + color=discord.Color.blue() + ) + + banques_trouvees = False + + for banque, data in comptes.items(): + if not isinstance(data, dict) or user_id not in data: + continue # Ce n'est pas une banque valide pour cet utilisateur + + compte = data[user_id] + rib = compte.get("rib", "Non défini") + solde = compte.get("solde", 0) + transactions = compte.get("transactions", []) + + # Récupérer les 3 dernières transactions avec date + last_transactions = transactions[-3:] if transactions else [] + + tx_str = "" + for tx in last_transactions: + date = tx.get("date", "Date inconnue") + tx_type = tx.get("type", "Inconnu") + montant = tx.get("montant", 0) + source = tx.get("emetteur", tx.get("source", "Inconnu")) + dest = tx.get("destinataire", "Inconnu") + + if tx_type.lower() == "virement" or tx_type.lower().startswith("virement"): + tx_str += f"- 🕒 {date} : **{tx_type}** de **{montant}€** à <@{dest}>\n" + elif tx_type.lower() == "virement reçu": + tx_str += f"- 🕒 {date} : **{tx_type}** de **{montant}€** de <@{source}>\n" + else: + tx_str += f"- 🕒 {date} : **{tx_type}** de **{montant}€**\n" + + if not tx_str: + tx_str = "*Aucune transaction.*" + + embed.add_field( + name=f"🏦 {banque}", + value=f"💳 **RIB** : `{rib}`\n💰 **Solde** : `{solde}€`\n📜 **Transactions récentes :**\n{tx_str}", + inline=False + ) + + banques_trouvees = True + + if not banques_trouvees: + embed.description = "❌ Aucun compte bancaire trouvé dans les banques enregistrées pour cet utilisateur." + + await interaction.response.send_message(embed=embed, ephemeral=True) + +async def setup(bot): + await bot.add_cog(Compte(bot)) diff --git a/commandes/compte_banque_nationale.py b/commandes/compte_banque_nationale.py new file mode 100644 index 0000000..0bb2109 --- /dev/null +++ b/commandes/compte_banque_nationale.py @@ -0,0 +1,102 @@ +import discord +from discord import app_commands +from discord.ext import commands +import json +import math + +BANQUE_NATIONALE_ROLE_ID = 1362031086959329470 # Remplace par l’ID réel du rôle banque_nationale + +class CompteBanqueNationale(commands.Cog): + def __init__(self, bot): + self.bot = bot + + def get_banque_nationale_data(self): + try: + with open("comptes_entreprises.json", "r+", encoding="utf-8") as f: + data = json.load(f) + + # Crée la section si elle n'existe pas + if "banque_nationale" not in data: + data["banque_nationale"] = { + "BANQUE": { + "rib": "11111 22222 33333333333 44", + "solde": 0.0, + "transactions": [{ + "date": "Création automatique", + "type": "Création de compte", + "source": "Système", + "montant": 0.0 + }] + } + } + f.seek(0) + json.dump(data, f, indent=4, ensure_ascii=False) + f.truncate() + + banque_data = data["banque_nationale"].get("BANQUE", {}) + return banque_data.get("solde", 0.0), banque_data.get("transactions", []) + except Exception as e: + print(f"Erreur lecture ou création banque_nationale : {e}") + return 0.0, [] + + @app_commands.command(name="voir_banque_nationale", description="Voir solde et transactions du compte banque nationale") + async def voir_banque_nationale(self, interaction: discord.Interaction): + if not any(role.id == BANQUE_NATIONALE_ROLE_ID for role in interaction.user.roles): + await interaction.response.send_message("❌ Vous n'avez pas la permission d'accéder à ce compte.", ephemeral=True) + return + + solde, transactions = self.get_banque_nationale_data() + + if not transactions: + await interaction.response.send_message("📄 Aucune transaction trouvée sur le compte banque nationale.", ephemeral=True) + return + + # Pagination + pages = [] + per_page = 5 + total_pages = math.ceil(len(transactions) / per_page) + + for i in range(total_pages): + start = i * per_page + end = start + per_page + chunk = transactions[start:end] + + embed = discord.Embed( + title="🏦 Banque Nationale - Historique", + description=f"Page {i+1}/{total_pages}", + color=discord.Color.blue() + ) + embed.add_field(name="💰 Solde", value=f"{solde:.2f}€", inline=False) + + for tx in chunk: + description = ( + f"**Date**: {tx.get('date', 'N/A')}\n" + f"**Type**: {tx.get('type', 'N/A')}\n" + f"**Montant**: {tx.get('montant', 0)}€\n" + f"**Source**: {tx.get('source', 'N/A')}" + ) + embed.add_field(name="📄 Transaction", value=description, inline=False) + + pages.append(embed) + + class Pagination(discord.ui.View): + def __init__(self): + super().__init__(timeout=None) + self.current = 0 + + @discord.ui.button(label="⬅️", style=discord.ButtonStyle.grey) + async def previous(self, interaction_btn: discord.Interaction, button: discord.ui.Button): + if self.current > 0: + self.current -= 1 + await interaction_btn.response.edit_message(embed=pages[self.current], view=self) + + @discord.ui.button(label="➡️", style=discord.ButtonStyle.grey) + async def next(self, interaction_btn: discord.Interaction, button: discord.ui.Button): + if self.current < len(pages) - 1: + self.current += 1 + await interaction_btn.response.edit_message(embed=pages[self.current], view=self) + + await interaction.response.send_message(embed=pages[0], view=Pagination(), ephemeral=True) + +async def setup(bot): + await bot.add_cog(CompteBanqueNationale(bot)) diff --git a/commandes/compte_fbm.py b/commandes/compte_fbm.py new file mode 100644 index 0000000..2254747 --- /dev/null +++ b/commandes/compte_fbm.py @@ -0,0 +1,92 @@ +import discord +from discord import app_commands +from discord.ext import commands +import json +import math + +FBM_ROLE_ID = 1353610672818360330 # ID du rôle autorisé à accéder au compte FBM + +class CompteFBM(commands.Cog): + def __init__(self, bot): + self.bot = bot + + def get_all_transactions(self): + try: + with open("comptes_entreprises.json", "r", encoding="utf-8") as f: + data = json.load(f) + fbm_data = data.setdefault("fbm", {}).setdefault("FBM", { + "solde": 0.0, + "transactions": [] + }) + return fbm_data["solde"], fbm_data["transactions"] + except Exception as e: + print(f"Erreur lecture comptes_entreprises.json : {e}") + return 0.0, [] + + @app_commands.command(name="compte_fbm", description="Affiche le solde et l'historique du compte FBM (réservé au personnel FBM).") + async def compte_fbm(self, interaction: discord.Interaction): + if not any(role.id == FBM_ROLE_ID for role in interaction.user.roles): + await interaction.response.send_message("❌ Vous n'avez pas la permission d'accéder à ce compte.", ephemeral=True) + return + + solde, transactions = self.get_all_transactions() + + if not transactions: + embed = discord.Embed( + title="📄 Compte FBM", + description="Aucune transaction enregistrée.", + color=discord.Color.orange() + ) + embed.add_field(name="💰 Solde actuel", value=f"{solde:.2f}€", inline=False) + await interaction.response.send_message(embed=embed, ephemeral=True) + return + + # Pagination + pages = [] + per_page = 5 + total_pages = math.ceil(len(transactions) / per_page) + + for i in range(total_pages): + start = i * per_page + end = start + per_page + chunk = transactions[start:end] + + embed = discord.Embed( + title="📄 Historique FBM", + description=f"Page {i+1}/{total_pages}", + color=discord.Color.orange() + ) + embed.add_field(name="💰 Solde actuel", value=f"{solde:.2f}€", inline=False) + + for tx in chunk: + desc = ( + f"**Date**: {tx.get('date', 'N/A')}\n" + f"**Type**: {tx.get('type', 'N/A')}\n" + f"**Montant**: {tx.get('montant', 0)}€\n" + f"**Source**: {tx.get('source', 'N/A')}" + ) + embed.add_field(name="🔸 Transaction", value=desc, inline=False) + + pages.append(embed) + + class Pagination(discord.ui.View): + def __init__(self): + super().__init__(timeout=None) + self.current = 0 + + @discord.ui.button(label="⬅️", style=discord.ButtonStyle.grey) + async def previous(self, interaction_btn: discord.Interaction, button: discord.ui.Button): + if self.current > 0: + self.current -= 1 + await interaction_btn.response.edit_message(embed=pages[self.current], view=self) + + @discord.ui.button(label="➡️", style=discord.ButtonStyle.grey) + async def next(self, interaction_btn: discord.Interaction, button: discord.ui.Button): + if self.current < len(pages) - 1: + self.current += 1 + await interaction_btn.response.edit_message(embed=pages[self.current], view=self) + + await interaction.response.send_message(embed=pages[0], view=Pagination(), ephemeral=True) + +async def setup(bot): + await bot.add_cog(CompteFBM(bot)) \ No newline at end of file diff --git a/commandes/compte_gouvernement.py b/commandes/compte_gouvernement.py new file mode 100644 index 0000000..26d7a3f --- /dev/null +++ b/commandes/compte_gouvernement.py @@ -0,0 +1,84 @@ +import discord +from discord import app_commands +from discord.ext import commands +import json +import math + +GOUVERNEMENT_ROLE_ID = 1278305482926194701 # Remplace avec l’ID de ton rôle Gouvernement # Remplace avec l’ID de ton rôle Gouvernement + +class CompteGouvernement(commands.Cog): + def __init__(self, bot): + self.bot = bot + + def get_gouv_data(self): + try: + with open("comptes_entreprises.json", "r", encoding="utf-8") as f: + data = json.load(f) + gouv_data = data.get("Gouvernement", {}).get("GOUV", {}) + return gouv_data.get("solde", 0.0), gouv_data.get("transactions", []) + except Exception as e: + print(f"Erreur lecture comptes_entreprises.json : {e}") + return 0.0, [] + + @app_commands.command(name="compte_gouvernement", description="Affiche le solde et les transactions du compte Gouvernement.") + async def compte_gouvernement(self, interaction: discord.Interaction): + if not any(role.id == GOUVERNEMENT_ROLE_ID for role in interaction.user.roles): + await interaction.response.send_message("❌ Vous n'avez pas la permission d'accéder à ce compte.", ephemeral=True) + return + + solde, transactions = self.get_gouv_data() + + if not transactions: + await interaction.response.send_message("📄 Aucune transaction trouvée pour le gouvernement.", ephemeral=True) + return + + # Pagination setup + pages = [] + per_page = 5 + total_pages = math.ceil(len(transactions) / per_page) + + for i in range(total_pages): + start = i * per_page + end = start + per_page + chunk = transactions[start:end] + + embed = discord.Embed( + title="🏛️ Compte Gouvernement", + description=f"Page {i+1}/{total_pages}", + color=discord.Color.dark_gold() + ) + embed.add_field(name="💰 Solde", value=f"{solde:.2f}€", inline=False) + + for tx in chunk: + desc = ( + f"**Date**: {tx.get('date', 'N/A')}\n" + f"**Type**: {tx.get('type', 'N/A')}\n" + f"**Montant**: {tx.get('montant', 0)}€\n" + f"**Source**: {tx.get('source', 'N/A')}" + ) + embed.add_field(name="📄 Transaction", value=desc, inline=False) + + pages.append(embed) + + # Vue de pagination + class Pagination(discord.ui.View): + def __init__(self): + super().__init__(timeout=None) + self.current = 0 + + @discord.ui.button(label="⬅️", style=discord.ButtonStyle.grey) + async def previous(self, interaction_btn: discord.Interaction, button: discord.ui.Button): + if self.current > 0: + self.current -= 1 + await interaction_btn.response.edit_message(embed=pages[self.current], view=self) + + @discord.ui.button(label="➡️", style=discord.ButtonStyle.grey) + async def next(self, interaction_btn: discord.Interaction, button: discord.ui.Button): + if self.current < len(pages) - 1: + self.current += 1 + await interaction_btn.response.edit_message(embed=pages[self.current], view=self) + + await interaction.response.send_message(embed=pages[0], view=Pagination(), ephemeral=True) + +async def setup(bot): + await bot.add_cog(CompteGouvernement(bot)) diff --git a/commandes/compte_urssaf.py b/commandes/compte_urssaf.py new file mode 100644 index 0000000..e4a30b4 --- /dev/null +++ b/commandes/compte_urssaf.py @@ -0,0 +1,96 @@ +import discord +from discord import app_commands +from discord.ext import commands +import json +import math + +URSAFF_ROLE_ID = 1362056014584615213 # Remplace par l’ID réel du rôle URSAFF # Remplace par l’ID réel du rôle URSAFF + +class CompteUrssaf(commands.Cog): + def __init__(self, bot): + self.bot = bot + + def get_all_transactions(self): + try: + with open("comptes_entreprises.json", "r", encoding="utf-8") as f: + data = json.load(f) + except Exception as e: + print(f"Erreur lecture comptes_entreprises.json : {e}") + return 0.0, [] + + # Si URSAFF n'existe pas, on le crée + if "ursaff" not in data: + data["ursaff"] = { + "URSSAFF": { + "rib": "00000 00000 00000000000 01", + "solde": 0.0, + "transactions": [] + } + } + with open("comptes_entreprises.json", "w", encoding="utf-8") as f: + json.dump(data, f, ensure_ascii=False, indent=4) + + ursaff_data = data.get("ursaff", {}).get("URSAFF", {}) + return ursaff_data.get("solde", 0.0), ursaff_data.get("transactions", []) + + @app_commands.command(name="compte_ursaff", description="Affiche le solde et l'historique complet du compte URSAFF (réservé aux membres URSAFF).") + async def compte_ursaff(self, interaction: discord.Interaction): + # Vérifie que l'utilisateur a le rôle URSAFF + if not any(role.id == URSAFF_ROLE_ID for role in interaction.user.roles): + await interaction.response.send_message("❌ Vous n'avez pas la permission d'accéder à ce compte.", ephemeral=True) + return + + solde, transactions = self.get_all_transactions() + + # Pagination setup + pages = [] + per_page = 5 + total_pages = max(1, math.ceil(len(transactions) / per_page)) + + for i in range(total_pages): + start = i * per_page + end = start + per_page + chunk = transactions[start:end] + + embed = discord.Embed( + title="📄 Historique URSSAF", + description=f"Page {i+1}/{total_pages}", + color=discord.Color.dark_purple() + ) + embed.add_field(name="💼 Solde actuel", value=f"{solde:.2f}€", inline=False) + + if chunk: + for tx in chunk: + description = ( + f"**Date**: {tx.get('date', 'N/A')}\n" + f"**Type**: {tx.get('type', 'N/A')}\n" + f"**Montant**: {tx.get('montant', 0)}€\n" + f"**Source**: {tx.get('source', 'N/A')}" + ) + embed.add_field(name="🔹 Transaction", value=description, inline=False) + else: + embed.add_field(name="📄 Transactions", value="Aucune transaction récente.", inline=False) + + pages.append(embed) + + class Pagination(discord.ui.View): + def __init__(self): + super().__init__(timeout=None) + self.current = 0 + + @discord.ui.button(label="⬅️", style=discord.ButtonStyle.grey) + async def previous(self, interaction_btn: discord.Interaction, button: discord.ui.Button): + if self.current > 0: + self.current -= 1 + await interaction_btn.response.edit_message(embed=pages[self.current], view=self) + + @discord.ui.button(label="➡️", style=discord.ButtonStyle.grey) + async def next(self, interaction_btn: discord.Interaction, button: discord.ui.Button): + if self.current < len(pages) - 1: + self.current += 1 + await interaction_btn.response.edit_message(embed=pages[self.current], view=self) + + await interaction.response.send_message(embed=pages[0], view=Pagination(), ephemeral=True) + +async def setup(bot): + await bot.add_cog(CompteUrssaf(bot)) diff --git a/commandes/comptes_entreprises.py b/commandes/comptes_entreprises.py new file mode 100644 index 0000000..f6bb519 --- /dev/null +++ b/commandes/comptes_entreprises.py @@ -0,0 +1,73 @@ +import discord +from discord import app_commands +from discord.ext import commands +import json + +class CompteEntreprise(commands.Cog): + def __init__(self, bot): + self.bot = bot + + banque_choices = [ + app_commands.Choice(name="Victoria's Credit Bank", value="Victoria's Credit Bank"), + app_commands.Choice(name="NGC Bank", value="NGC Bank"), + ] + + @app_commands.command(name="compte_entreprise", description="Consulte le solde et les transactions d'une entreprise.") + @app_commands.choices(banque=banque_choices) + async def compte_entreprise( + self, + interaction: discord.Interaction, + banque: app_commands.Choice[str], + nom_entreprise: str, + mot_de_passe: str + ): + banque_nom = banque.value + + try: + with open("comptes_entreprises.json", "r", encoding="utf-8") as f: + comptes = json.load(f) + except Exception: + await interaction.response.send_message("❌ Erreur de lecture des données bancaires.", ephemeral=True) + return + + if banque_nom not in comptes or nom_entreprise not in comptes[banque_nom]: + await interaction.response.send_message("❌ Ce compte entreprise n'existe pas.", ephemeral=True) + return + + compte = comptes[banque_nom][nom_entreprise] + + # 🔐 Vérification du mot de passe avec la bonne clé + mdp_stocke = compte.get("mdp", "").strip() + mdp_fournit = mot_de_passe.strip() + + if mdp_stocke != mdp_fournit: + await interaction.response.send_message("❌ Mot de passe incorrect.", ephemeral=True) + return + + solde = compte.get("solde", 0) + transactions = compte.get("transactions", []) + + embed = discord.Embed( + title=f"🏢 Compte Entreprise : {nom_entreprise}", + color=discord.Color.teal() + ) + embed.add_field(name="🏦 Banque", value=banque_nom, inline=True) + embed.add_field(name="💰 Solde", value=f"{solde:.2f}€", inline=False) + + if transactions: + last_transactions = transactions[-3:] + for tx in reversed(last_transactions): + desc = ( + f"**Date**: {tx.get('date', 'N/A')}\n" + f"**Type**: {tx.get('type', 'N/A')}\n" + f"**Montant**: {tx.get('montant', 0)}€\n" + f"**Source**: {tx.get('source', 'N/A')}" + ) + embed.add_field(name="📄 Transaction", value=desc, inline=False) + else: + embed.add_field(name="📄 Transactions", value="Aucune transaction récente.", inline=False) + + await interaction.response.send_message(embed=embed, ephemeral=True) + +async def setup(bot): + await bot.add_cog(CompteEntreprise(bot)) diff --git a/commandes/counting.py b/commandes/counting.py new file mode 100644 index 0000000..d053be0 --- /dev/null +++ b/commandes/counting.py @@ -0,0 +1,68 @@ +import discord +from discord.ext import commands +from discord import app_commands +import json +import os + +COUNTING_FILE = "counting_data.json" +counting_channels = {} + +def load_counting_channels(): + global counting_channels + if os.path.isfile(COUNTING_FILE): + with open(COUNTING_FILE, "r", encoding="utf-8") as f: + data = json.load(f) + counting_channels = {int(k): v for k, v in data.items()} + else: + counting_channels = {} + +def save_counting_channels(): + with open(COUNTING_FILE, "w", encoding="utf-8") as f: + json.dump({str(k): v for k, v in counting_channels.items()}, f, ensure_ascii=False, indent=2) + +class Counting(commands.Cog): + def __init__(self, bot): + self.bot = bot + load_counting_channels() + + async def cog_load(self): + # Synchronise les slash commands à l'ajout du cog + await self.bot.tree.sync() + print("Slash commands synchronisées pour Counting.") + + @app_commands.command(name="start_counting", description="Démarre un channel de counting.") + async def start_counting(self, interaction: discord.Interaction): + counting_channels[interaction.channel.id] = {"current": 0, "last_user": None} + save_counting_channels() + await interaction.response.send_message("Le counting commence ! Envoyez 1 pour débuter.") + + @commands.Cog.listener() + async def on_message(self, message): + if message.author.bot: + return + + channel_id = message.channel.id + if channel_id not in counting_channels: + return + + state = counting_channels[channel_id] + try: + number = int(message.content.strip()) + except ValueError: + return + + if number == state["current"] + 1 and message.author.id != state["last_user"]: + state["current"] = number + state["last_user"] = message.author.id + save_counting_channels() + await message.add_reaction("✅") + else: + await message.add_reaction("❌") + await message.channel.send( + f"Erreur ! <@{message.author.id}> a cassé la chaîne à {number}. On repart à 1 !" + ) + counting_channels[channel_id] = {"current": 0, "last_user": None} + save_counting_channels() + +async def setup(bot): + await bot.add_cog(Counting(bot)) diff --git a/commandes/identite.py b/commandes/identite.py new file mode 100644 index 0000000..2939422 --- /dev/null +++ b/commandes/identite.py @@ -0,0 +1,72 @@ +import discord +from discord import app_commands +from discord.ext import commands +from utils.data_manager import DataManager +from datetime import datetime, timedelta + +ADMIN_ROLE_ID = 1278305482926194701 # ID du rôle gouvernement # ID du rôle gouvernement + +data_manager = DataManager() + +class CreerIdentite(commands.Cog): + def __init__(self, bot): + self.bot = bot + + @app_commands.command(name="creer_carte_identite", description="Créer une carte d'identité (Réservé aux admins)") + @app_commands.choices(sexe=[ + app_commands.Choice(name="Homme", value="Homme"), + app_commands.Choice(name="Femme", value="Femme"), + app_commands.Choice(name="Autre", value="Autre") + ]) + async def creer_carte_identite( + self, + interaction: discord.Interaction, + utilisateur: discord.Member, + nom: str, + prenom: str, + date_naissance: str, + lieu_naissance: str, + sexe: app_commands.Choice[str] + ): + """Commande réservée aux admins pour créer une carte d'identité""" + + if not any(role.id == ADMIN_ROLE_ID for role in interaction.user.roles): + await interaction.response.send_message( + "❌ Vous n'avez pas la permission de créer des cartes d'identité.", + ephemeral=True + ) + return + + user_id = str(utilisateur.id) + + # ➡️ Plus de refus : on écrase directement l'ancienne carte + ancienne_carte = user_id in data_manager.identities + + # 📅 Dates de création et d'expiration + date_creation = datetime.now() + date_expiration = (date_creation + timedelta(days=730)).strftime("%d/%m/%Y") + + # 📦 Données à enregistrer + identity_data = { + "nom": nom.upper(), + "prenom": prenom.capitalize(), + "date_naissance": date_naissance, + "lieu_naissance": lieu_naissance, + "sexe": sexe.value, + "date_expiration": date_expiration + } + + # 💾 Enregistrement + data_manager.identities[user_id] = identity_data + data_manager.save_data() + + # 📢 Message différent selon si c'était une ancienne carte ou non + if ancienne_carte: + message = f"✅ Carte d'identité de {utilisateur.mention} **mise à jour** avec succès !" + else: + message = f"✅ Carte d'identité créée pour {utilisateur.mention} avec succès !" + + await interaction.response.send_message(message, ephemeral=False) + +async def setup(bot): + await bot.add_cog(CreerIdentite(bot)) diff --git a/commandes/liste_taches.py b/commandes/liste_taches.py new file mode 100644 index 0000000..10b5e71 --- /dev/null +++ b/commandes/liste_taches.py @@ -0,0 +1,60 @@ +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)) diff --git a/commandes/message.py b/commandes/message.py new file mode 100644 index 0000000..4c06992 --- /dev/null +++ b/commandes/message.py @@ -0,0 +1,22 @@ +import discord +from discord import app_commands +from discord.ext import commands +from utils.logger import log_info, log_error + +class Message(commands.Cog): + def __init__(self, bot): + self.bot = bot + + @app_commands.command(name="message", description="Envoyer un message privé") + @app_commands.checks.has_permissions(administrator=True) + async def message_utilisateur(self, interaction: discord.Interaction, membre: discord.Member, message: str): + try: + await membre.send(message) + await interaction.response.send_message(f"Message envoyé à {membre.mention}.", ephemeral=True) + log_info(f"Message envoyé à {membre.name}") + except discord.Forbidden: + await interaction.response.send_message("Impossible d'envoyer un MP.", ephemeral=True) + log_error(f"Échec d'envoi à {membre.name}") + +async def setup(bot): + await bot.add_cog(Message(bot)) diff --git a/commandes/message_tous.py b/commandes/message_tous.py new file mode 100644 index 0000000..5e5c4ae --- /dev/null +++ b/commandes/message_tous.py @@ -0,0 +1,38 @@ +import discord +from discord import app_commands +from discord.ext import commands + +class MessageTous(commands.Cog): + def __init__(self, bot: commands.Bot): + self.bot = bot + + @app_commands.command( + name="messagetous", + description="Envoyer un message privé à tous les membres du serveur." + ) + @app_commands.checks.has_permissions(administrator=True) + async def message_tous(self, interaction: discord.Interaction, message: str): + await interaction.response.defer(ephemeral=True) + + succes = 0 + erreurs = 0 + + await interaction.followup.send("Envoi du message en cours...", ephemeral=False) + + for member in interaction.guild.members: + if not member.bot and member != interaction.user: + try: + await member.send(message) + succes += 1 + except discord.Forbidden: + erreurs += 1 + except Exception as e: + erreurs += 1 + + await interaction.followup.send( + f"✅ Message envoyé à {succes} membres.\n❌ {erreurs} erreurs.", + ephemeral=False + ) + +async def setup(bot: commands.Bot): + await bot.add_cog(MessageTous(bot)) \ No newline at end of file diff --git a/commandes/ouvrir_compte.py b/commandes/ouvrir_compte.py new file mode 100644 index 0000000..3c5c28d --- /dev/null +++ b/commandes/ouvrir_compte.py @@ -0,0 +1,76 @@ +import discord +from discord import app_commands +from discord.ext import commands +from utils.banque_manager import BanqueManager +import random + +# Rôle nécessaire pour créer un compte +BANQUIER_ROLE_ID = 1360314740567576616 # ID du rôle des banquiers # ID du rôle banquier + +# Liste des banques disponibles (id interne : nom affiché) +BANQUES = { + "Victoria's Credit Bank": "Victoria's Credit Bank", + "NGC Bank": "Nexus Global Corporation Bank" +} + +banque_manager = BanqueManager() + +class OuvrirCompte(commands.Cog): + def __init__(self, bot): + self.bot = bot + + @app_commands.command(name="ouvrir_compte", description="Ouvre un compte bancaire pour un utilisateur (banquier uniquement).") + @app_commands.choices( + banque=[app_commands.Choice(name=nom, value=id_banque) for id_banque, nom in BANQUES.items()] + ) + async def ouvrir_compte(self, interaction: discord.Interaction, utilisateur: discord.Member, banque: app_commands.Choice[str]): + banque_id = banque.value + banque_nom = BANQUES[banque_id] + + # Vérification du rôle de banquier + if not any(role.id == BANQUIER_ROLE_ID for role in interaction.user.roles): + await interaction.response.send_message("❌ Vous n'avez pas la permission d'ouvrir un compte.", ephemeral=True) + return + + user_id = str(utilisateur.id) + banque_manager.load_data() + + # Conversion automatique si l'ancien format est utilisé + if all(isinstance(v, dict) and 'rib' in v for v in banque_manager.comptes.values()): + banque_manager.comptes = {"vicynationale": banque_manager.comptes} + + # Vérification si la banque existe dans les données + if banque_id not in banque_manager.comptes: + banque_manager.comptes[banque_id] = {} + + # Vérification si l'utilisateur a déjà un compte dans cette banque + if user_id in banque_manager.comptes[banque_id]: + await interaction.response.send_message( + f"❌ {utilisateur.mention} possède déjà un compte à la {banque_nom} !", + ephemeral=True + ) + return + + # Génération du RIB aléatoire + rib = f"{random.randint(10000, 99999)} {random.randint(10000, 99999)} {random.randint(10000000000, 99999999999)} {random.randint(10, 99)}" + + # Création du compte + banque_manager.comptes[banque_id][user_id] = { + "rib": rib, + "solde": 0.0, + "transactions": [] + } + + banque_manager.save_data() + + # Envoi du RIB en MP à l'utilisateur cible + try: + await utilisateur.send(f"🏦 Un compte a été créé pour vous à la **{banque_nom}**.\nVoici votre RIB : `{rib}`") + except discord.Forbidden: + await interaction.response.send_message("❌ Impossible d'envoyer le RIB en message privé (l'utilisateur a les MP fermés).", ephemeral=True) + return + + await interaction.response.send_message(f"✅ Compte créé avec succès pour {utilisateur.mention} à la **{banque_nom}**.", ephemeral=True) + +async def setup(bot): + await bot.add_cog(OuvrirCompte(bot)) \ No newline at end of file diff --git a/commandes/ouvrir_comptes_entreprises.py b/commandes/ouvrir_comptes_entreprises.py new file mode 100644 index 0000000..f33290a --- /dev/null +++ b/commandes/ouvrir_comptes_entreprises.py @@ -0,0 +1,92 @@ +import json +import os +import random +import string +import discord +from discord import app_commands +from discord.ext import commands + +COMPTES_ENTREPRISES_PATH = "comptes_entreprises.json" +BANQUIER_ROLE_ID = 1360314740567576616 # ID du rôle des banquiers # ID du rôle banquier + +def generer_rib(): + return " ".join(["".join(random.choices(string.digits, k=5)) for _ in range(4)]) + +def generer_mdp(): + return "".join(random.choices(string.ascii_letters + string.digits, k=8)) + +class OuvrirCompteEntreprise(commands.Cog): + def __init__(self, bot): + self.bot = bot + + banque_choix = [ + app_commands.Choice(name="Victoria's Credit Bank", value="Victoria's Credit Bank"), + app_commands.Choice(name="NGC Bank", value="NGC Bank"), + ] + + tva_choix = [ + app_commands.Choice(name="2%", value=2), + app_commands.Choice(name="5%", value=5), + app_commands.Choice(name="10%", value=10), + app_commands.Choice(name="20%", value=20), + ] + + @app_commands.command(name="ouvrir_compte_entreprise", description="Ouvre un compte bancaire pour une entreprise") + @app_commands.choices(banque=banque_choix, tva=tva_choix) + async def ouvrir_compte_entreprise(self, interaction: discord.Interaction, entreprise: str, banque: app_commands.Choice[str], tva: app_commands.Choice[int]): + # ✅ Vérifie que l'utilisateur est banquier + if not any(role.id == BANQUIER_ROLE_ID for role in interaction.user.roles): + await interaction.response.send_message("❌ Vous n'avez pas la permission d'exécuter cette commande.", ephemeral=True) + return + + banque_name = banque.value + entreprise = entreprise.strip() + + # ✅ Chargement sécurisé du fichier + if os.path.exists(COMPTES_ENTREPRISES_PATH): + try: + with open(COMPTES_ENTREPRISES_PATH, "r", encoding="utf-8") as f: + comptes = json.load(f) + except json.JSONDecodeError: + comptes = {} + else: + comptes = {} + + if banque_name not in comptes: + comptes[banque_name] = {} + + if entreprise in comptes[banque_name]: + await interaction.response.send_message(f"❌ Un compte existe déjà pour l'entreprise **{entreprise}** dans la banque **{banque_name}**.", ephemeral=True) + return + + rib = generer_rib() + mdp = generer_mdp() + + comptes[banque_name][entreprise] = { + "rib": rib, + "solde": 0, + "mdp": mdp, + "tva": tva.value, + "transactions": [] + } + + with open(COMPTES_ENTREPRISES_PATH, "w", encoding="utf-8") as f: + json.dump(comptes, f, ensure_ascii=False, indent=4) + + # ✅ MP à l'utilisateur avec le RIB et le mot de passe + try: + await interaction.user.send( + f"🏦 Compte de l'entreprise **{entreprise}** ouvert avec succès dans la banque **{banque_name}** !\n" + f"💳 RIB : `{rib}`\n" + f"🔐 Mot de passe : `{mdp}`\n" + f"💼 Taux de TVA : `{tva.value}%`\n\n" + f"Conservez bien ces informations." + ) + except discord.Forbidden: + await interaction.response.send_message("⚠️ Impossible de vous envoyer un message privé avec les informations du compte.", ephemeral=True) + return + + await interaction.response.send_message(f"✅ Le compte de l'entreprise **{entreprise}** a été créé avec succès.", ephemeral=True) + +async def setup(bot): + await bot.add_cog(OuvrirCompteEntreprise(bot)) diff --git a/commandes/permis.py b/commandes/permis.py new file mode 100644 index 0000000..1396ff6 --- /dev/null +++ b/commandes/permis.py @@ -0,0 +1,32 @@ +import discord +from discord.ext import commands +from discord import app_commands + +# IDs des rôles +PERMIS_ROLE_ID = 1357027980056199258 +AUTO_ECOLE_ROLE_ID = 1338844772118298634 + +class PermisCommand(commands.Cog): + def __init__(self, bot): + self.bot = bot + + @app_commands.command(name="ajouter_permis", description="Ajoute le rôle 'permis' à un membre.") + async def ajouter_permis(self, interaction: discord.Interaction, membre: discord.Member): + """Ajoute le rôle 'permis' à un membre sélectionné.""" + # Vérifier si l'utilisateur a le rôle "auto-école" + if AUTO_ECOLE_ROLE_ID not in [role.id for role in interaction.user.roles]: + await interaction.response.send_message("❌ Erreur : Vous n'avez pas la permission d'utiliser cette commande.") + return + + # Vérifier si le rôle "permis" existe + permis_role = interaction.guild.get_role(PERMIS_ROLE_ID) + if not permis_role: + await interaction.response.send_message("❌ Erreur : Le rôle 'permis' n'existe pas.") + return + + # Ajouter le rôle "permis" au membre + await membre.add_roles(permis_role) + await interaction.response.send_message(f"✅ Le rôle 'permis' a été ajouté à {membre.mention}.") + +async def setup(bot): + await bot.add_cog(PermisCommand(bot)) \ No newline at end of file diff --git a/commandes/presence_session.py b/commandes/presence_session.py new file mode 100644 index 0000000..dedaebb --- /dev/null +++ b/commandes/presence_session.py @@ -0,0 +1,38 @@ +import discord +from discord import app_commands +from discord.ext import commands +# Import ou partage de presence_data selon l'architecture +from commandes.annonces_sessions import presence_data # adapte le chemin si besoin + +class ListePresence(commands.Cog): + def __init__(self, bot): + self.bot = bot + + @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 + ) + +async def setup(bot): + await bot.add_cog(ListePresence(bot)) \ No newline at end of file diff --git a/commandes/pret.py b/commandes/pret.py new file mode 100644 index 0000000..7fa1a21 --- /dev/null +++ b/commandes/pret.py @@ -0,0 +1,75 @@ +import discord +from discord import app_commands +from discord.ext import commands +from utils.banque_manager import BanqueManager + +BANQUIER_ROLE_ID = 1360314740567576616 # ID du rôle des banquiers # ID du rôle banquier + +banque_manager = BanqueManager() + +class Pret(commands.Cog): + def __init__(self, bot): + self.bot = bot + + @app_commands.command(name="calculer_pret", description="Accorde un prêt bancaire à un utilisateur (Réservé aux banquiers).") + async def calculer_pret( + self, + interaction: discord.Interaction, + utilisateur: discord.Member, + banque: str, + montant: float, + duree_annees: int + ): + """Les banquiers peuvent accorder un prêt à un utilisateur si son solde le permet.""" + + # Vérification de si le joueur executant la commande est un banquier + if not any(role.id == BANQUIER_ROLE_ID for role in interaction.user.roles): + await interaction.response.send_message( + "❌ Vous n'avez pas la permission d'accorder un prêt.", + ephemeral=True + ) + return + + user_id = str(utilisateur.id) + success, result = banque_manager.ajouter_pret(user_id, banque, montant, duree_annees) + + if not success: + await interaction.response.send_message(result, ephemeral=True) + return + + total_a_rembourser = result + taux_interet = banque_manager.comptes[user_id]['prets'][-1]['taux_interet'] * 100 + + # Vérifier si l'utilisateur a assez d'argent pour rembourser + solde_utilisateur = banque_manager.comptes[user_id].get("solde", 0) + if solde_utilisateur < total_a_rembourser: + await interaction.response.send_message( + f"❌ {utilisateur.mention} n'a pas assez d'argent pour rembourser ce prêt.\n" + f"💰 **Solde actuel** : {solde_utilisateur}€\n" + f"💳 **Montant à rembourser** : {total_a_rembourser}€", + ephemeral=True + ) + return + + # Débiter le solde de l'utilisateur + banque_manager.comptes[user_id]["solde"] -= total_a_rembourser + banque_manager.save_data() + + # Création de l'embed + embed = discord.Embed( + title="🏦 Prêt bancaire accordé", + description=f"Le prêt a été accordé par {interaction.user.mention} pour {utilisateur.mention}.", + color=discord.Color.green() + ) + embed.set_thumbnail(url="https://cdn-icons-png.flaticon.com/512/2910/2910827.png") # Icône banque + embed.add_field(name="💰 Montant emprunté", value=f"{montant}€", inline=True) + embed.add_field(name="📅 Durée", value=f"{duree_annees} ans", inline=True) + embed.add_field(name="📈 Taux d'intérêt", value=f"{taux_interet}%", inline=True) + embed.add_field(name="💳 Montant total à rembourser", value=f"{total_a_rembourser}€", inline=False) + embed.add_field(name="💰 Solde après prêt", value=f"{banque_manager.comptes[user_id]['solde']}€", inline=True) + embed.set_footer(text="Prêt enregistré dans le compte bancaire ✅") + + await interaction.response.send_message(embed=embed) + +async def setup(bot): + await bot.add_cog(Pret(bot)) \ No newline at end of file diff --git a/commandes/rename_all_on_join.py b/commandes/rename_all_on_join.py new file mode 100644 index 0000000..69e5670 --- /dev/null +++ b/commandes/rename_all_on_join.py @@ -0,0 +1,24 @@ +import discord +from discord.ext import commands +from utils.logger import log_info, log_error + +class RenameMember(commands.Cog): + def __init__(self, bot): + self.bot = bot + + @commands.Cog.listener() + async def on_member_join(self, member): + try: + # Vérifie si le bot a la permission de changer les pseudos + if member.guild.me.guild_permissions.manage_nicknames: + # Renommer le membre avec le format "Prénom Nom | Epic" + await member.edit(nick="Prénom Nom | Epic") + log_info(f"[✅] Pseudo modifié pour {member.display_name}") + else: + log_error(f"[❌] Pas la permission de modifier {member.display_name}") + except Exception as e: + log_error(f"[⚠️] Erreur pour {member.display_name} : {e}") + +# Fonction pour ajouter le cog à ton bot +async def setup(bot): + await bot.add_cog(RenameMember(bot)) diff --git a/commandes/restranscription_staff.py b/commandes/restranscription_staff.py new file mode 100644 index 0000000..f893a1d --- /dev/null +++ b/commandes/restranscription_staff.py @@ -0,0 +1,49 @@ +import discord +from discord.ext import commands +from discord import app_commands + +class RestranscriptionStaff(commands.Cog): + def __init__(self, bot): + self.bot = bot + + @commands.Cog.listener() + async def on_message(self, message): + # Ignorer les messages du bot lui-même + if message.author == self.bot.user: + return + + # Ignorer les messages d'autres bots + if message.author.bot: + return + + # Réceptionner les MPs + if isinstance(message.channel, discord.DMChannel): + channel_reception = self.bot.get_channel(1277951933105049601) # Remplacez par votre ID de salon + + embed = discord.Embed(description=message.content, + color=discord.Color.blue()) + embed.set_author(name=message.author.name, + icon_url=message.author.avatar.url + if message.author.avatar else None) + + await channel_reception.send(embed=embed) + + # Traiter les autres commandes si nécessaire + await self.bot.process_commands(message) + + # Exemple de commande slash avec gestion des erreurs + @app_commands.command() + async def message_tous(self, interaction: discord.Interaction): + await interaction.response.send_message("Commande test!") + + @message_tous.error + async def message_tous_error(self, interaction: discord.Interaction, + error: app_commands.AppCommandError): + if isinstance(error, app_commands.MissingPermissions): + await interaction.response.send_message( + "Vous n'avez pas la permission d'utiliser cette commande.", + ephemeral=True) + +# Fonction de configuration pour ajouter le cog au bot +async def setup(bot): + await bot.add_cog(RestranscriptionStaff(bot)) \ No newline at end of file diff --git a/commandes/retirer_argent.py b/commandes/retirer_argent.py new file mode 100644 index 0000000..4988414 --- /dev/null +++ b/commandes/retirer_argent.py @@ -0,0 +1,147 @@ +import discord +from discord import app_commands +from discord.ext import commands +import json +from datetime import datetime + +BANQUIER_ROLE_ID = 1360314740567576616 # ID du rôle des banquiers # ID du rôle banquier # ID du rôle banquier + +class RetirerArgent(commands.Cog): + def __init__(self, bot): + self.bot = bot + + banque_choices = [ + app_commands.Choice(name="Victoria's Credit Bank", value="Victoria's Credit Bank"), + app_commands.Choice(name="NGC Bank", value="NGC Bank"), + ] + + @app_commands.command( + name="retirer_argent", + description="Retire de l'argent d'un compte via son RIB (utilisateur ou entreprise)." + ) + @app_commands.choices(banque=banque_choices) + @app_commands.describe( + rib="Le RIB du compte", + montant="Montant à retirer", + banque="Banque cible (facultatif)" + ) + async def retirer_argent( + self, + interaction: discord.Interaction, + rib: str, + montant: float, + banque: app_commands.Choice[str] = None + ): + if not any(role.id == BANQUIER_ROLE_ID for role in interaction.user.roles): + await interaction.response.send_message("❌ Vous n'avez pas la permission de faire cela.", ephemeral=True) + return + + if montant <= 0: + await interaction.response.send_message("❌ Le montant doit être supérieur à zéro.", ephemeral=True) + return + + banque_nom = banque.value if banque else None + + # Charger les fichiers + try: + with open("comptes_bancaires.json", "r", encoding="utf-8") as f: + comptes_bancaires = json.load(f) + with open("comptes_entreprises.json", "r", encoding="utf-8") as f: + comptes_entreprises = json.load(f) + except Exception: + await interaction.response.send_message("❌ Erreur lors de la lecture des fichiers JSON.", ephemeral=True) + return + + compte = None + source = None # "utilisateur" ou "entreprise" + user_id = None + + # Recherche dans la banque spécifiée (si précisée) + if banque_nom and banque_nom in comptes_bancaires: + for uid, data in comptes_bancaires[banque_nom].items(): + if data.get("rib") == rib: + compte = data + source = "utilisateur" + user_id = uid + break + + if not compte and banque_nom and banque_nom in comptes_entreprises: + for entreprise, data in comptes_entreprises[banque_nom].items(): + if data.get("rib") == rib: + compte = data + source = "entreprise" + user_id = entreprise + break + + # Si banque non précisée ou compte non trouvé, recherche dans toutes les banques + if not compte and not banque_nom: + # Recherche dans toutes les banques utilisateurs + for banque_courante, comptes_banque in comptes_bancaires.items(): + for uid, data in comptes_banque.items(): + if data.get("rib") == rib: + compte = data + source = "utilisateur" + user_id = uid + banque_nom = banque_courante + break + if compte: + break + + if not compte: + # Recherche dans toutes les banques entreprises + for banque_courante, entreprises_banque in comptes_entreprises.items(): + for entreprise, data in entreprises_banque.items(): + if data.get("rib") == rib: + compte = data + source = "entreprise" + user_id = entreprise + banque_nom = banque_courante + break + if compte: + break + + if not compte: + await interaction.response.send_message("❌ Aucun compte trouvé avec ce RIB.", ephemeral=True) + return + + if compte["solde"] < montant: + await interaction.response.send_message("❌ Solde insuffisant pour retirer cette somme.", ephemeral=True) + return + + # Appliquer le retrait + compte["solde"] -= montant + transaction = { + "date": datetime.now().strftime("%d/%m/%Y %H:%M:%S"), + "type": "Retrait", + "source": "Banque", + "montant": -montant, + "emetteur": interaction.user.id, + "destinataire": user_id + } + compte.setdefault("transactions", []).append(transaction) + + # Sauvegarde dans le bon fichier + if source == "utilisateur": + comptes_bancaires[banque_nom][user_id] = compte + with open("comptes_bancaires.json", "w", encoding="utf-8") as f: + json.dump(comptes_bancaires, f, indent=4, ensure_ascii=False) + else: + comptes_entreprises[banque_nom][user_id] = compte + with open("comptes_entreprises.json", "w", encoding="utf-8") as f: + json.dump(comptes_entreprises, f, indent=4, ensure_ascii=False) + + # Message de confirmation + embed = discord.Embed( + title="💸 Retrait effectué", + description=f"💼 Banque : **{banque_nom}**\n📌 Type : **{source.capitalize()}**", + color=discord.Color.red() + ) + embed.add_field(name="RIB", value=rib, inline=False) + embed.add_field(name="Montant retiré", value=f"{montant:.2f}€", inline=True) + embed.add_field(name="Effectué par", value=interaction.user.mention, inline=True) + embed.set_footer(text="Transaction enregistrée ✅") + + await interaction.response.send_message(embed=embed, ephemeral=False) + +async def setup(bot): + await bot.add_cog(RetirerArgent(bot)) \ No newline at end of file diff --git a/commandes/retrait_gouv.py b/commandes/retrait_gouv.py new file mode 100644 index 0000000..84fa721 --- /dev/null +++ b/commandes/retrait_gouv.py @@ -0,0 +1,184 @@ +import discord +from discord import app_commands +from discord.ext import commands +import json +from datetime import datetime + +GOUVERNEMENT_ROLE_ID = 1278305482926194701 # Remplace avec l’ID de ton rôle Gouvernement # Remplace par l'ID du rôle Gouvernement +GOUV_BANQUE = "Gouvernement" +GOUV_COMPTE = "GOUV" +GOUV_RIB = "00000 00000 00000000000 00" + +class GouvVirement(commands.Cog): + def __init__(self, bot): + self.bot = bot + + # Liste des banques à jour (ajoute ici toutes les banques possibles de ton système) + banque_choices = [ + app_commands.Choice(name="Victoria's Credit Bank", value="Victoria's Credit Bank"), + app_commands.Choice(name="NGC Bank", value="NGC Bank"), + ] + + @app_commands.command( + name="gouv_virer", + description="Le Gouvernement vire de l'argent de son compte vers un autre RIB (entreprise ou particulier)." + ) + @app_commands.choices(banque=banque_choices) + @app_commands.describe( + rib="Le RIB destinataire", + montant="Montant à virer", + banque="Banque cible (facultatif)" + ) + async def gouv_virer( + self, + interaction: discord.Interaction, + rib: str, + montant: float, + banque: app_commands.Choice[str] = None + ): + # Vérification du rôle Gouvernement + if not any(role.id == GOUVERNEMENT_ROLE_ID for role in interaction.user.roles): + await interaction.response.send_message("❌ Seul le Gouvernement peut utiliser cette commande.", ephemeral=True) + return + + if montant <= 0: + await interaction.response.send_message("❌ Le montant doit être supérieur à zéro.", ephemeral=True) + return + + banque_nom = banque.value if banque else None + + # Charger les fichiers + try: + with open("comptes_bancaires.json", "r", encoding="utf-8") as f: + comptes_bancaires = json.load(f) + except Exception: + comptes_bancaires = {} + + try: + with open("comptes_entreprises.json", "r", encoding="utf-8") as f: + comptes_entreprises = json.load(f) + except Exception: + await interaction.response.send_message("❌ Erreur lors de la lecture des fichiers JSON.", ephemeral=True) + return + + # Récupérer le compte du Gouvernement + try: + gouv_compte = comptes_entreprises[GOUV_BANQUE][GOUV_COMPTE] + except KeyError: + await interaction.response.send_message("❌ Compte Gouvernement introuvable.", ephemeral=True) + return + + if gouv_compte["solde"] < montant: + await interaction.response.send_message("❌ Le Gouvernement n'a pas assez de fonds.", ephemeral=True) + return + + # Chercher le destinataire + destinataire_compte = None + destinataire_banque = None + destinataire_nom = None + destinataire_type = None + + # Si banque précisée, chercher d'abord dans cette banque (entreprises puis particuliers) + if banque_nom: + # Entreprises + if banque_nom in comptes_entreprises: + for nom, data in comptes_entreprises[banque_nom].items(): + if data.get("rib") == rib: + destinataire_compte = data + destinataire_banque = banque_nom + destinataire_nom = nom + destinataire_type = "entreprise" + break + # Particuliers + if not destinataire_compte and banque_nom in comptes_bancaires: + for uid, data in comptes_bancaires[banque_nom].items(): + if data.get("rib") == rib: + destinataire_compte = data + destinataire_banque = banque_nom + destinataire_nom = uid + destinataire_type = "particulier" + break + # Si banque non précisée, chercher dans toutes les banques + else: + # Entreprises + for banque_courante, entreprises in comptes_entreprises.items(): + for nom, data in entreprises.items(): + if data.get("rib") == rib: + destinataire_compte = data + destinataire_banque = banque_courante + destinataire_nom = nom + destinataire_type = "entreprise" + break + if destinataire_compte: + break + # Particuliers + if not destinataire_compte: + for banque_courante, comptes in comptes_bancaires.items(): + for uid, data in comptes.items(): + if data.get("rib") == rib: + destinataire_compte = data + destinataire_banque = banque_courante + destinataire_nom = uid + destinataire_type = "particulier" + break + if destinataire_compte: + break + + if not destinataire_compte: + await interaction.response.send_message("❌ Aucun compte trouvé avec ce RIB dans la banque spécifiée." if banque_nom else "❌ Aucun compte trouvé avec ce RIB.", ephemeral=True) + return + + # Effectuer le virement + gouv_compte["solde"] -= montant + destinataire_compte["solde"] += montant + + now = datetime.now().strftime("%d/%m/%Y %H:%M:%S") + + # Transaction côté Gouvernement (sortant) + gouv_compte.setdefault("transactions", []).append({ + "date": now, + "type": "Virement", + "source": "Gouvernement", + "montant": -montant, + "emetteur": "Gouvernement", + "destinataire": destinataire_nom + }) + + # Transaction côté destinataire (entrant) + destinataire_compte.setdefault("transactions", []).append({ + "date": now, + "type": "Virement", + "source": "Gouvernement", + "montant": montant, + "emetteur": "Gouvernement", + "destinataire": destinataire_nom + }) + + # Sauvegarde + comptes_entreprises[GOUV_BANQUE][GOUV_COMPTE] = gouv_compte + if destinataire_type == "entreprise": + comptes_entreprises[destinataire_banque][destinataire_nom] = destinataire_compte + with open("comptes_entreprises.json", "w", encoding="utf-8") as f: + json.dump(comptes_entreprises, f, indent=4, ensure_ascii=False) + else: + comptes_bancaires[destinataire_banque][destinataire_nom] = destinataire_compte + with open("comptes_entreprises.json", "w", encoding="utf-8") as f: + json.dump(comptes_entreprises, f, indent=4, ensure_ascii=False) + with open("comptes_bancaires.json", "w", encoding="utf-8") as f: + json.dump(comptes_bancaires, f, indent=4, ensure_ascii=False) + + # Message de confirmation + embed = discord.Embed( + title="🏛️ Virement Gouvernemental effectué", + description=f"💼 Banque destinataire : **{destinataire_banque}**\n📌 Type : **{destinataire_type.capitalize()}**", + color=discord.Color.gold() + ) + embed.add_field(name="RIB destinataire", value=rib, inline=False) + embed.add_field(name="Montant viré", value=f"{montant:.2f}€", inline=True) + embed.add_field(name="Effectué par", value=interaction.user.mention, inline=True) + embed.set_footer(text="Transaction enregistrée ✅") + + await interaction.response.send_message(embed=embed, ephemeral=False) + +async def setup(bot): + await bot.add_cog(GouvVirement(bot)) diff --git a/commandes/roll.py b/commandes/roll.py new file mode 100644 index 0000000..bcf0e1e --- /dev/null +++ b/commandes/roll.py @@ -0,0 +1,30 @@ +import random +from discord import app_commands + +# Supposons que tu as accès à l'objet bot ou client principal +# Utilise bot.tree ou client.tree directement + +@app_commands.command( + name="roll", + description="Lancer un dé entre deux valeurs avec une raison." +) +@app_commands.describe( + minimum="Valeur minimale", + maximum="Valeur maximale", + action="Raison du lancer de dé" +) +async def roll(interaction, minimum: int, maximum: int, action: str): + if minimum > maximum: + await interaction.response.send_message( + "❌ La valeur minimale doit être inférieure ou égale à la valeur maximale.", ephemeral=True + ) + return + result = random.randint(minimum, maximum) + await interaction.response.send_message( + f"🎲 **{interaction.user.display_name}** a lancé un dé pour l'action *{action}*\n" + f"Résultat : **{result}** (entre {minimum} et {maximum})" + ) + +# Pour enregistrer la commande dans le bot : +async def setup(bot): + bot.tree.add_command(roll) diff --git a/commandes/stats_commandes.py b/commandes/stats_commandes.py new file mode 100644 index 0000000..d889af1 --- /dev/null +++ b/commandes/stats_commandes.py @@ -0,0 +1,144 @@ +import discord +from discord import app_commands +from discord.ext import commands +import matplotlib.pyplot as plt +import io +import datetime +from utils.stats_manager import get_stats_detailed # fonction fictive améliorée + +class StatsCommandes(commands.Cog): + def __init__(self, bot): + self.bot = bot + + def plot_barh(self, labels, values, title, xlabel, color='cornflowerblue'): + plt.figure(figsize=(8,4)) + plt.barh(labels[::-1], values[::-1], color=color) + plt.title(title) + plt.xlabel(xlabel) + plt.tight_layout() + buf = io.BytesIO() + plt.savefig(buf, format='png') + plt.close() + buf.seek(0) + return buf + + def plot_line(self, dates, values, title, xlabel, ylabel, color='tab:blue'): + plt.figure(figsize=(10,5)) + plt.plot(dates, values, marker='o', color=color) + plt.title(title) + plt.xlabel(xlabel) + plt.ylabel(ylabel) + plt.grid(True, linestyle='--', alpha=0.7) + plt.tight_layout() + buf = io.BytesIO() + plt.savefig(buf, format='png') + plt.close() + buf.seek(0) + return buf + + def plot_pie(self, labels, values, title): + plt.figure(figsize=(6,6)) + plt.pie(values, labels=labels, autopct='%1.1f%%', startangle=140, colors=plt.cm.Paired.colors) + plt.title(title) + plt.tight_layout() + buf = io.BytesIO() + plt.savefig(buf, format='png') + plt.close() + buf.seek(0) + return buf + + @app_commands.command(name="stats_bot", description="Affiche toutes les statistiques d'utilisation du bot.") + async def stats_bot(self, interaction: discord.Interaction): + # get_stats_detailed() doit retourner un dict avec les clés suivantes (exemple) : + # { + # "total_commandes": int, + # "top_commandes": [(nom, nb), ...], + # "top_utilisateurs": [(user_id, nb), ...], + # "evolution_commandes": [(date, nb), ...], # list de tuples date (datetime), nb commandes + # "repartition_statuts": {statut: nb, ...}, + # "commandes_par_jour": {jour_semaine: nb, ...} # ex: "Lundi": 123 + # } + + stats = get_stats_detailed() + + embed = discord.Embed( + title="📊 Statistiques Complètes du Bot", + description=f"Total commandes utilisées : **{stats['total_commandes']}**", + color=discord.Color.blue() + ) + + # Top commandes en texte + if stats["top_commandes"]: + top_cmds_text = "\n".join([f"• `{nom}` : **{nb}** fois" for nom, nb in stats["top_commandes"][:5]]) + else: + top_cmds_text = "Aucune commande utilisée." + embed.add_field(name="🏆 Top 5 des commandes", value=top_cmds_text, inline=False) + + # Top utilisateurs en texte + if stats["top_utilisateurs"]: + top_users_text = "\n".join( + [f"• <@{uid}> : **{nb}** commandes" for uid, nb in stats["top_utilisateurs"][:5]] + ) + else: + top_users_text = "Aucun utilisateur enregistré." + embed.add_field(name="👤 Top 5 utilisateurs", value=top_users_text, inline=False) + + # Génération des graphiques + + files = [] + + # Graphique 1 : Top commandes (barh) + if stats["top_commandes"]: + noms_cmds = [x[0] for x in stats["top_commandes"][:5]] + vals_cmds = [x[1] for x in stats["top_commandes"][:5]] + buf1 = self.plot_barh(noms_cmds, vals_cmds, "Top 5 Commandes les plus utilisées", "Nombre d'utilisations") + files.append(discord.File(fp=buf1, filename="top_commandes.png")) + embed.set_image(url="attachment://top_commandes.png") + + # Graphique 2 : Top utilisateurs (barh) + if stats["top_utilisateurs"]: + noms_users = [f"User {uid}" for uid, _ in stats["top_utilisateurs"][:5]] # On peut remplacer par mention si on fetch + vals_users = [nb for _, nb in stats["top_utilisateurs"][:5]] + buf2 = self.plot_barh(noms_users, vals_users, "Top 5 Utilisateurs actifs", "Nombre de commandes", color='mediumseagreen') + files.append(discord.File(fp=buf2, filename="top_utilisateurs.png")) + + # Graphique 3 : Évolution commandes dans le temps (line) + if stats.get("evolution_commandes"): + dates = [d for d, _ in stats["evolution_commandes"]] + vals = [v for _, v in stats["evolution_commandes"]] + # Convertir dates datetime en string format court + dates_str = [d.strftime("%d/%m") for d in dates] + buf3 = self.plot_line(dates_str, vals, "Évolution des commandes dans le temps", "Date", "Nombre de commandes") + files.append(discord.File(fp=buf3, filename="evolution_commandes.png")) + + # Graphique 4 : Répartition statuts (pie) + if stats.get("repartition_statuts"): + labels = list(stats["repartition_statuts"].keys()) + values = list(stats["repartition_statuts"].values()) + buf4 = self.plot_pie(labels, values, "Répartition des statuts des tâches") + files.append(discord.File(fp=buf4, filename="repartition_statuts.png")) + + # Graphique 5 : Commandes par jour de la semaine (bar) + if stats.get("commandes_par_jour"): + jours = list(stats["commandes_par_jour"].keys()) + valeurs = list(stats["commandes_par_jour"].values()) + plt.figure(figsize=(8,4)) + plt.bar(jours, valeurs, color='salmon') + plt.title("Commandes par jour de la semaine") + plt.xlabel("Jour") + plt.ylabel("Nombre de commandes") + plt.tight_layout() + buf5 = io.BytesIO() + plt.savefig(buf5, format='png') + plt.close() + buf5.seek(0) + files.append(discord.File(fp=buf5, filename="commandes_par_jour.png")) + + # Envoi embed + fichiers graphiques + if files: + await interaction.response.send_message(embed=embed, files=files) + else: + await interaction.response.send_message(embed=embed) + +async def setup(bot: commands.Bot): + await bot.add_cog(StatsCommandes(bot)) diff --git a/commandes/taches.py b/commandes/taches.py new file mode 100644 index 0000000..36336ad --- /dev/null +++ b/commandes/taches.py @@ -0,0 +1,259 @@ +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)) \ No newline at end of file diff --git a/commandes/ticket.py b/commandes/ticket.py new file mode 100644 index 0000000..673a765 --- /dev/null +++ b/commandes/ticket.py @@ -0,0 +1,329 @@ +import discord +from discord.ext import commands +from discord import app_commands + +TICKET_CATEGORY_ID = 1301142883876737085 # ID de la catégorie des tickets # ID de la catégorie des tickets +STAFF_ROLE_ID = 1277638784095092816 # ID du rôle staff # ID du rôle staff +TEAM_STAFF_ROLE_ID = 1277638784095092816 # ID du rôle équipe staff # ID du rôle équipe staff +ATTENTE_ORAL_ROLE_ID = 1278343007996870699 # ID du rôle "attente oral" # ID du rôle "attente oral" +CANDIDATURE_A_FAIRE_ROLE_ID = 1278343541936099412 # ID du rôle "candidature à faire" # ID du rôle "candidature à faire" +CITOYEN_ROLE_ID = 1278319901651632138 # ID du rôle "citoyen" # ID du rôle "citoyen" + +class ContinueFormView(discord.ui.View): + """Vue avec un bouton pour continuer à l'étape suivante""" + + def __init__(self, current_step, step1_data=None, step2_part1_data=None, step2_part2_data=None): + super().__init__(timeout=180.0) # Définir un timeout pour la vue + self.current_step = current_step + self.step1_data = step1_data + self.step2_part1_data = step2_part1_data + self.step2_part2_data = step2_part2_data + + @discord.ui.button(label="📋 Continuer le formulaire", style=discord.ButtonStyle.green, custom_id="continue_form") + async def continue_form(self, interaction: discord.Interaction, button: discord.ui.Button): + """Passe à l'étape suivante du formulaire""" + if self.current_step == 1: + await interaction.response.send_modal(TicketStep2Part1(self.step1_data)) + elif self.current_step == 2: + await interaction.response.send_modal(TicketStep2Part2(self.step1_data, self.step2_part1_data)) + elif self.current_step == 3: + await interaction.response.send_modal(TicketStep3(self.step1_data, self.step2_part1_data, self.step2_part2_data)) + + # Vérifier si le message existe toujours avant de le modifier + try: + await interaction.message.edit(view=self) + except discord.errors.NotFound: + # Si le message n'existe plus, nous ne faisons rien + pass + +class TicketStep1(discord.ui.Modal, title="📋 Informations Personnelles"): + """Étape 1 du formulaire - Infos personnelles""" + + pseudo = discord.ui.TextInput(label="Pseudo Epic (Fortnite)", required=True, max_length=16) + age = discord.ui.TextInput(label="Âge", required=True, max_length=2) + experience = discord.ui.TextInput(label="Expérience RP ?", style=discord.TextStyle.paragraph, required=True, max_length=50) + decouverte = discord.ui.TextInput(label="Comment as-tu découvert le serveur ?", required=True, max_length=20) + micro = discord.ui.TextInput(label="As-tu un micro de qualité ?", required=True, max_length=3) + + async def on_submit(self, interaction: discord.Interaction): + """Envoie un bouton pour passer à l'étape 2""" + view = ContinueFormView(current_step=1, step1_data=self) + await interaction.response.send_message("✅ Première étape terminée ! Cliquez ci-dessous pour continuer.", view=view, ephemeral=True) + +class TicketStep2Part1(discord.ui.Modal, title="👤 Infos sur ton personnage (Partie 1)"): + """Étape 2 du formulaire - Infos RP (Partie 1)""" + + def __init__(self, step1_data): + super().__init__() + self.step1_data = step1_data + + prenom = discord.ui.TextInput(label="Prénom", required=True) + nom = discord.ui.TextInput(label="Nom", required=True) + age_perso = discord.ui.TextInput(label="Âge du personnage", required=True) + genre = discord.ui.TextInput(label="Genre", required=True, max_length=5) + naissance = discord.ui.TextInput(label="Date et lieu de naissance", required=True) + + async def on_submit(self, interaction: discord.Interaction): + """Envoie un bouton pour passer à l'étape suivante""" + view = ContinueFormView(current_step=2, step1_data=self.step1_data, step2_part1_data=self) + await interaction.response.send_message("✅ Deuxième partie terminée ! Cliquez ci-dessous pour continuer.", view=view, ephemeral=True) + +class TicketStep2Part2(discord.ui.Modal, title="👤 Infos sur ton personnage (Partie 2)"): + """Étape 2 du formulaire - Infos RP (Partie 2)""" + + def __init__(self, step1_data, step2_part1_data): + super().__init__() + self.step1_data = step1_data + self.step2_part1_data = step2_part1_data + + traits = discord.ui.TextInput(label="Traits de caractère", required=True) + nationalite = discord.ui.TextInput(label="Nationalité", required=True) + skin_rp = discord.ui.TextInput(label="Skin RP", required=True) + metier_souhaite = discord.ui.TextInput(label="Métier souhaité", required=True) + + async def on_submit(self, interaction: discord.Interaction): + """Envoie un bouton pour passer à l'étape 3""" + view = ContinueFormView(current_step=3, step1_data=self.step1_data, step2_part1_data=self.step2_part1_data, step2_part2_data=self) + await interaction.response.send_message("✅ Deuxième étape terminée ! Cliquez ci-dessous pour continuer.", view=view, ephemeral=True) + +class TicketStep3(discord.ui.Modal, title="📋 Développement du Personnage"): + """Étape 3 du formulaire - Histoire du personnage""" + + def __init__(self, step1_data, step2_part1_data, step2_part2_data): + super().__init__() + self.step1_data = step1_data + self.step2_part1_data = step2_part1_data + self.step2_part2_data = step2_part2_data + + histoire = discord.ui.TextInput( + label="Histoire du personnage", + style=discord.TextStyle.paragraph, + required=True, + min_length=425, + max_length=500 # Limite de caractères maximum + ) + objectifs = discord.ui.TextInput(label="Objectifs RP (court et long terme)", style=discord.TextStyle.paragraph, required=True, max_length=100) + + async def on_submit(self, interaction: discord.Interaction): + """Création du ticket après validation""" + guild = interaction.guild + user = interaction.user + category = discord.utils.get(guild.categories, id=TICKET_CATEGORY_ID) + + # Vérifier si la catégorie existe + if category is None: + await interaction.response.send_message("❌ Erreur : La catégorie des tickets n'existe pas !", ephemeral=True) + return + + # Vérifier si le rôle staff existe + staff_role = discord.utils.get(guild.roles, id=STAFF_ROLE_ID) + if staff_role is None: + await interaction.response.send_message("❌ Erreur : Le rôle staff n'existe pas !", ephemeral=True) + return + + # Vérifier si l'utilisateur a déjà un ticket ouvert + for channel in category.text_channels: + if channel.topic == f"Ticket de {user.id}": + await interaction.response.send_message(f"❌ Vous avez déjà un ticket ouvert : {channel.mention}", ephemeral=True) + return + + # Définir les permissions du salon du ticket + overwrites = { + guild.default_role: discord.PermissionOverwrite(read_messages=False), + user: discord.PermissionOverwrite(read_messages=True, send_messages=True), + staff_role: discord.PermissionOverwrite(read_messages=True, send_messages=True), + discord.utils.get(guild.roles, id=TEAM_STAFF_ROLE_ID): discord.PermissionOverwrite(read_messages=True, send_messages=False) + } + + # Créer un salon de ticket avec le nom [pseudo_du_membre]-écrit + ticket_channel = await category.create_text_channel( + name=f"{user.name}-écrit", + topic=f"Ticket de {user.id}", + overwrites=overwrites + ) + + # Récupérer le rôle "équipe staff" + team_staff_role = discord.utils.get(guild.roles, id=TEAM_STAFF_ROLE_ID) + + # Création de l'embed avec les infos du formulaire + embed = discord.Embed(title="📩 Ticket Whitelist", color=0x00ff00) + embed.add_field(name="📋 - Informations Personnelles", value=( + f"> - Pseudo Epic : {self.step1_data.pseudo.value}\n" + f"> - Âge : {self.step1_data.age.value}\n" + f"> - Expérience RP : {self.step1_data.experience.value}\n" + f"> - Découverte du serveur : {self.step1_data.decouverte.value}\n" + f"> - Micro de qualité : {self.step1_data.micro.value}" + ), inline=False) + embed.add_field(name="👤 - Informations RP", value=( + f"> - Prénom : {self.step2_part1_data.prenom.value}\n" + f"> - Nom : {self.step2_part1_data.nom.value}\n" + f"> - Âge du personnage : {self.step2_part1_data.age_perso.value}\n" + f"> - Genre : {self.step2_part1_data.genre.value}\n" + f"> - Naissance : {self.step2_part1_data.naissance.value}\n" + f"> - Traits de caractère : {self.step2_part2_data.traits.value}\n" + f"> - Nationalité : {self.step2_part2_data.nationalite.value}\n" + f"> - Skin RP : {self.step2_part2_data.skin_rp.value}\n" + f"> - Métier souhaité : {self.step2_part2_data.metier_souhaite.value}" + ), inline=False) + embed.add_field(name="📋 - Développement du Personnage", value=( + f"> - Histoire : {self.histoire.value}\n" + f"> - Objectifs : {self.objectifs.value}" + ), inline=False) + + # Envoyer l'embed avec les boutons d'actions pour les staff + await ticket_channel.send(embed=embed, view=TicketManagementView(user)) + + # Envoyer un message pour mentionner l'équipe staff + await ticket_channel.send(f"{team_staff_role.mention} - Nouveau ticket créé !") + + # Mettre à jour le pseudo du joueur si le bot a les permissions nécessaires + if guild.me.guild_permissions.manage_nicknames: + new_nickname = f"{self.step2_part1_data.prenom.value} {self.step2_part1_data.nom.value} | {self.step1_data.pseudo.value}" + if len(new_nickname) > 32: + new_nickname = new_nickname[:32] + await user.edit(nick=new_nickname) + else: + await interaction.followup.send("❌ Le bot n'a pas les permissions nécessaires pour modifier le pseudo.", ephemeral=True) + + # Envoyer un message de confirmation + try: + await interaction.response.send_message(f"✅ Ticket ouvert : {ticket_channel.mention}", ephemeral=True) + except discord.errors.NotFound: + await interaction.followup.send(f"✅ Ticket ouvert : {ticket_channel.mention}", ephemeral=True) + +class TicketManagementView(discord.ui.View): + """Vue pour gérer le ticket avec les boutons Fermer, Réouvrir, Claim et Supprimer""" + + def __init__(self, ticket_owner): + super().__init__(timeout=None) + self.ticket_owner = ticket_owner + self.claimed_by = None # Stocke le staff qui claim le ticket + + @discord.ui.button(label="🔒 Fermer le Ticket", style=discord.ButtonStyle.red, custom_id="close_ticket") + async def close_ticket(self, interaction: discord.Interaction, button: discord.ui.Button): + """Ferme le ticket en désactivant l'envoi de messages pour le membre""" + if not discord.utils.get(interaction.user.roles, id=STAFF_ROLE_ID): + await interaction.response.send_message("❌ Seuls les staffs peuvent fermer un ticket.", ephemeral=True) + return + + await interaction.channel.set_permissions(self.ticket_owner, read_messages=False, send_messages=False) + await interaction.channel.edit(name="ticket-fermé") + await interaction.response.send_message("🔒 Le ticket a été fermé. Vous ne pouvez plus le voir.", ephemeral=True) + + # Mettre à jour la vue pour afficher uniquement les boutons Réouvrir et Supprimer + new_view = ClosedTicketView(self.ticket_owner) + await interaction.channel.send("Le ticket a été fermé.", view=new_view) + + @discord.ui.button(label="👤 Prendre en charge le Ticket", style=discord.ButtonStyle.blurple, custom_id="claim_ticket") + async def claim_ticket(self, interaction: discord.Interaction, button: discord.ui.Button): + """Un staff prend en charge le ticket""" + if not discord.utils.get(interaction.user.roles, id=STAFF_ROLE_ID): + await interaction.response.send_message("❌ Seuls les staffs peuvent claim un ticket.", ephemeral=True) + return + self.claimed_by = interaction.user + await interaction.channel.set_permissions(discord.utils.get(interaction.guild.roles, id=STAFF_ROLE_ID), send_messages=False, read_messages=True) + await interaction.channel.set_permissions(self.claimed_by, read_messages=True, send_messages=True) + await interaction.response.send_message(f"👤 {self.claimed_by.mention} a claim ce ticket.", ephemeral=False) + +class ClosedTicketView(discord.ui.View): + """Vue pour les tickets fermés avec les boutons Réouvrir et Supprimer""" + + def __init__(self, ticket_owner): + super().__init__(timeout=None) + self.ticket_owner = ticket_owner + + @discord.ui.button(label="♻ Réouvrir le Ticket", style=discord.ButtonStyle.green, custom_id="reopen_ticket") + async def reopen_ticket(self, interaction: discord.Interaction, button: discord.ui.Button): + """Réouvre le ticket pour le membre""" + if not discord.utils.get(interaction.user.roles, id=STAFF_ROLE_ID): + await interaction.response.send_message("❌ Seuls les staffs peuvent réouvrir un ticket.", ephemeral=True) + return + await interaction.channel.set_permissions(self.ticket_owner, read_messages=True, send_messages=True) + await interaction.response.send_message("✅ Le ticket a été réouvert.", ephemeral=True) + + # Mettre à jour la vue pour afficher tous les boutons + new_view = TicketManagementView(self.ticket_owner) + await interaction.channel.send("Le ticket a été réouvert.", view=new_view) + + @discord.ui.button(label="❌ Supprimer le Ticket", style=discord.ButtonStyle.danger, custom_id="delete_ticket") + async def delete_ticket(self, interaction: discord.Interaction, button: discord.ui.Button): + """Supprime le salon du ticket""" + if not discord.utils.get(interaction.user.roles, id=STAFF_ROLE_ID): + await interaction.response.send_message("❌ Seuls les staffs peuvent supprimer un ticket.", ephemeral=True) + return + await interaction.response.send_message("❌ Suppression du ticket...", ephemeral=True) + await interaction.channel.delete() + +class TicketView(discord.ui.View): + """Vue avec le bouton pour ouvrir le ticket""" + + def __init__(self): + super().__init__(timeout=None) + + @discord.ui.button(label="📑 Ouvrir un Ticket Whitelist", style=discord.ButtonStyle.red, custom_id="open_ticket") + async def open_ticket(self, interaction: discord.Interaction, button: discord.ui.Button): + """Affiche le premier formulaire""" + await interaction.response.send_modal(TicketStep1()) + +class Ticket(commands.Cog): + """Commande pour envoyer l'embed de ticket""" + + def __init__(self, bot): + self.bot = bot + + @app_commands.command(name="ticket", description="Envoie l'embed pour ouvrir un ticket.") + async def ticket(self, interaction: discord.Interaction): + """Envoie l'embed avec le bouton d'ouverture de ticket""" + embed = discord.Embed( + title="📩 Ticket Whitelist", + description="Pour réaliser votre whitelist, ouvrez un ticket et remplissez le formulaire.", + color=0x00ff00 + ) + view = TicketView() + await interaction.channel.send(embed=embed, view=view) + await interaction.response.send_message("✅ Embed des tickets envoyé.", ephemeral=True) + + @commands.Cog.listener() + async def on_message(self, message): + """Surveille les messages des staffs dans les tickets""" + if message.guild is None or message.author.bot: + return + if not message.channel.topic or not message.channel.topic.startswith("Ticket de"): + return + + staff_role = discord.utils.get(message.guild.roles, id=STAFF_ROLE_ID) + if staff_role in message.author.roles and "validé" in message.content.lower(): + try: + user_id = message.channel.topic.split("Ticket de ")[1] + user = await message.guild.fetch_member(user_id) + + # Vérifier si le rôle "attente oral" est déjà attribué + attente_oral_role = discord.utils.get(message.guild.roles, id=ATTENTE_ORAL_ROLE_ID) + candidature_a_faire_role = discord.utils.get(message.guild.roles, id=CANDIDATURE_A_FAIRE_ROLE_ID) + citoyen_role = discord.utils.get(message.guild.roles, id=CITOYEN_ROLE_ID) + + if attente_oral_role in user.roles: + # Deuxième "validé" détecté + await user.remove_roles(attente_oral_role) + await user.add_roles(citoyen_role) + await message.channel.edit(name=f"{user.name}-citoyen") + else: + # Premier "validé" détecté + await user.remove_roles(candidature_a_faire_role) + await user.add_roles(attente_oral_role) + await message.channel.edit(name=f"{user.name}-oral") + + # Mettre à jour le pseudo du joueur si le bot a les permissions nécessaires + if message.guild.me.guild_permissions.manage_nicknames: + new_nickname = f"{self.step2_part1_data.prenom.value} {self.step2_part1_data.nom.value} | {self.step1_data.pseudo.value}" + if len(new_nickname) > 32: + new_nickname = new_nickname[:32] + await user.edit(nick=new_nickname) + else: + await message.channel.send("❌ Le bot n'a pas les permissions nécessaires pour modifier le pseudo.", delete_after=10) + + except Exception as e: + print(f"Erreur lors du renommage du ticket: {e}") + +async def setup(bot): + await bot.add_cog(Ticket(bot)) diff --git a/commandes/transactions.py b/commandes/transactions.py new file mode 100644 index 0000000..4fe7b9f --- /dev/null +++ b/commandes/transactions.py @@ -0,0 +1,67 @@ +import discord +from discord import app_commands +from discord.ext import commands +from utils.banque_manager import BanqueManager + +BANQUIER_ROLE_ID = 1360314740567576616 # ID du rôle des banquiers # ID du rôle banquier # ID du rôle des banquiers + +banque_manager = BanqueManager() + +class Transactions(commands.Cog): + def __init__(self, bot): + self.bot = bot + + @app_commands.command(name="transactions", description="Affiche les transactions d'un utilisateur (réservé aux banquiers).") + async def transactions(self, interaction: discord.Interaction, utilisateur: discord.Member): + """Affiche l'historique des transactions d'un utilisateur (banquiers uniquement).""" + + # ✅ Récupération du vrai membre pour avoir les rôles + member = interaction.guild.get_member(interaction.user.id) + if not member: + await interaction.response.send_message("❌ Impossible de vérifier vos permissions.", ephemeral=True) + return + + # ✅ Vérification du rôle banquier + if not any(role.id == BANQUIER_ROLE_ID for role in member.roles): + await interaction.response.send_message("❌ Vous n'avez pas la permission de voir les transactions.", ephemeral=True) + return + + banque_manager.load_data() + + user_id = str(utilisateur.id) + + # ✅ Vérification si l'utilisateur a des transactions + if user_id not in banque_manager.comptes or "transactions" not in banque_manager.comptes[user_id]: + await interaction.response.send_message(f"❌ {utilisateur.mention} n'a pas d'historique de transactions.", ephemeral=True) + return + + transactions = banque_manager.comptes[user_id].get("transactions", []) + + if not transactions: + await interaction.response.send_message(f"📜 {utilisateur.mention} n'a encore effectué aucune transaction.", ephemeral=True) + return + + # ✅ Création de l'embed + embed = discord.Embed( + title=f"📜 Historique des transactions de {utilisateur.name}", + color=discord.Color.gold() + ) + + for t in transactions[-10:]: + embed.add_field( + name=f"📅 {t['date']}", + value=( + f"**Type**: {t['type']}\n" + f"💰 **Montant**: {t['montant']}€\n" + f"📤 **Émetteur**: <@{t['emetteur']}>\n" + f"📥 **Destinataire**: <@{t['destinataire']}>" + ), + inline=False + ) + + embed.set_footer(text="Seules les 10 dernières transactions sont affichées.") + + await interaction.response.send_message(embed=embed) + +async def setup(bot): + await bot.add_cog(Transactions(bot)) diff --git a/commandes/valider_visite_médicale.py b/commandes/valider_visite_médicale.py new file mode 100644 index 0000000..882fdf7 --- /dev/null +++ b/commandes/valider_visite_médicale.py @@ -0,0 +1,77 @@ +import discord +from discord import app_commands +from discord.ext import commands, tasks +import json +from datetime import datetime, timedelta +import os + +EMS_ROLE_ID = 1278317212675084328 # Remplace par l’ID du rôle EMS # Remplace par l’ID du rôle EMS +ROLE_A_ATTRIBUER_ID = 1367198876548993045 # Remplace par l’ID du rôle visite médicale # Remplace par l’ID du rôle à attribuer +FICHIER_ROLES = "visite_médicale.json" + +class AttribuerRole(commands.Cog): + def __init__(self, bot): + self.bot = bot + self.roles_temporaire = self.charger_roles() + self.verification_roles.start() + + def charger_roles(self): + if os.path.exists(FICHIER_ROLES): + with open(FICHIER_ROLES, "r", encoding="utf-8") as f: + return json.load(f) + return {} + + def sauvegarder_roles(self): + with open(FICHIER_ROLES, "w", encoding="utf-8") as f: + json.dump(self.roles_temporaire, f, indent=4, ensure_ascii=False) + + @app_commands.command(name="valider_visite_médicale", description="Attribue un rôle temporaire suite à une visite médicale (EMS uniquement).") + async def valider_visite(self, interaction: discord.Interaction, membre: discord.Member): + if not any(role.id == EMS_ROLE_ID for role in interaction.user.roles): + await interaction.response.send_message("❌ Seuls les EMS peuvent utiliser cette commande.", ephemeral=True) + return + + role = interaction.guild.get_role(ROLE_A_ATTRIBUER_ID) + if not role: + await interaction.response.send_message("❌ Rôle à attribuer introuvable.", ephemeral=True) + return + + await membre.add_roles(role) + expiration = (datetime.now() + timedelta(days=30)).strftime("%Y-%m-%d %H:%M:%S") + self.roles_temporaire[str(membre.id)] = { + "role_id": role.id, + "expiration": expiration + } + self.sauvegarder_roles() + + await interaction.response.send_message(f"✅ Rôle temporaire attribué à {membre.mention} jusqu'au {expiration}.", ephemeral=True) + + @tasks.loop(hours=1) + async def verification_roles(self): + now = datetime.now() + to_remove = [] + + for user_id, data in self.roles_temporaire.items(): + expiration = datetime.strptime(data["expiration"], "%Y-%m-%d %H:%M:%S") + if now >= expiration: + to_remove.append(user_id) + + for user_id in to_remove: + member = self.bot.get_user(int(user_id)) or await self.bot.fetch_user(int(user_id)) + guild = discord.utils.get(self.bot.guilds) + member = guild.get_member(int(user_id)) + role = guild.get_role(ROLE_A_ATTRIBUER_ID) + + if member and role: + await member.remove_roles(role) + del self.roles_temporaire[user_id] + + if to_remove: + self.sauvegarder_roles() + + @verification_roles.before_loop + async def before_verification(self): + await self.bot.wait_until_ready() + +async def setup(bot): + await bot.add_cog(AttribuerRole(bot)) diff --git a/commandes/virement.py b/commandes/virement.py new file mode 100644 index 0000000..0aef577 --- /dev/null +++ b/commandes/virement.py @@ -0,0 +1,138 @@ +import json +import discord +from discord import app_commands +from discord.ext import commands +from datetime import datetime + +class Virement(commands.Cog): + def __init__(self, bot): + self.bot = bot + + banque_choices = [ + app_commands.Choice(name="Victoria's Credit Bank", value="Victoria's Credit Bank"), + app_commands.Choice(name="NGC Bank", value="NGC Bank"), + ] + + @app_commands.command(name="virement", description="Fait un virement bancaire à un autre membre via son RIB") + @app_commands.choices(banque=banque_choices) + @app_commands.describe(rib="Le RIB du destinataire", montant="Le montant à envoyer", banque="Banque d'où retirer l'argent") + async def virement(self, interaction: discord.Interaction, rib: str, montant: float, banque: app_commands.Choice[str]): + auteur_id = str(interaction.user.id) + banque_source = banque.value + + # Charger les comptes utilisateurs + with open('comptes_bancaires.json', 'r', encoding='utf-8') as f: + comptes = json.load(f) + + # Charger les comptes entreprises + with open('comptes_entreprises.json', 'r', encoding='utf-8') as f: + entreprises = json.load(f) + + # Vérifier que l'utilisateur a un compte dans cette banque + if banque_source not in comptes or auteur_id not in comptes[banque_source]: + await interaction.response.send_message(f"❌ Tu n'as pas de compte dans la banque **{banque_source}**.", ephemeral=True) + return + + compte_emetteur = comptes[banque_source][auteur_id] + if compte_emetteur["solde"] < montant: + await interaction.response.send_message("❌ Ton solde est insuffisant pour effectuer ce virement.", ephemeral=True) + return + + # Rechercher le destinataire (utilisateur ou entreprise) + destinataire_id = None + banque_destinataire = None + is_entreprise = False + + # Recherche dans comptes utilisateurs + for bq_name, bq_data in comptes.items(): + if isinstance(bq_data, dict): + for user_id, info in bq_data.items(): + if isinstance(info, dict) and info.get("rib") == rib: + destinataire_id = user_id + banque_destinataire = bq_name + break + if destinataire_id: + break + + # Sinon, recherche dans les entreprises + if not destinataire_id: + for bq_name, bq_data in entreprises.items(): + if isinstance(bq_data, dict): + for ent_id, info in bq_data.items(): + if isinstance(info, dict) and info.get("rib") == rib: + destinataire_id = ent_id + banque_destinataire = bq_name + is_entreprise = True + break + if destinataire_id: + break + + if not destinataire_id: + await interaction.response.send_message("❌ Aucun compte trouvé avec ce RIB.", ephemeral=True) + return + + # Appliquer la TVA si entreprise + montant_net = montant + tva_envoyee = 0 + if is_entreprise: + taux = entreprises[banque_destinataire][destinataire_id].get("tva", 0) + tva_envoyee = round(montant * taux / 100, 2) + montant_net = montant - tva_envoyee + + # Ajouter la TVA au Gouvernement + if "Gouvernement" not in entreprises: + entreprises["Gouvernement"] = {} + if "GOUV" not in entreprises["Gouvernement"]: + entreprises["Gouvernement"]["GOUV"] = { + "rib": "00000 00000 00000000000 00", + "solde": 0.0, + "transactions": [] + } + entreprises["Gouvernement"]["GOUV"]["solde"] += tva_envoyee + entreprises["Gouvernement"]["GOUV"]["transactions"].append({ + "date": datetime.now().strftime("%d/%m/%Y %H:%M:%S"), + "type": "TVA reçue", + "source": auteur_id, + "montant": tva_envoyee + }) + + # Déduire du compte émetteur + comptes[banque_source][auteur_id]["solde"] -= montant + comptes[banque_source][auteur_id]["transactions"].append({ + "date": datetime.now().strftime("%d/%m/%Y %H:%M:%S"), + "type": "Virement", + "source": f"Vers RIB {rib}", + "montant": -montant + }) + + # Ajouter au compte destinataire + cible_data = entreprises if is_entreprise else comptes + cible_data[banque_destinataire][destinataire_id]["solde"] += montant_net + cible_data[banque_destinataire][destinataire_id]["transactions"].append({ + "date": datetime.now().strftime("%d/%m/%Y %H:%M:%S"), + "type": "Virement reçu", + "source": interaction.user.name, + "montant": montant_net + }) + + # Sauvegarde + with open('comptes_bancaires.json', 'w', encoding='utf-8') as f: + json.dump(comptes, f, ensure_ascii=False, indent=4) + with open('comptes_entreprises.json', 'w', encoding='utf-8') as f: + json.dump(entreprises, f, ensure_ascii=False, indent=4) + + # Envoi MP au destinataire si c'est un utilisateur + if not is_entreprise: + try: + user_dest = await self.bot.fetch_user(int(destinataire_id)) + await user_dest.send(f"📥 Vous avez reçu un virement de **{montant:.2f}€** de la part de **{interaction.user.display_name}**.") + except: + pass # Si l'utilisateur a les MP désactivés + + await interaction.response.send_message( + f"✅ Virement de **{montant:.2f}€** effectué.{' TVA de ' + str(tva_envoyee) + '€ envoyée au Gouvernement.' if is_entreprise else ''}", + ephemeral=True + ) + +async def setup(bot): + await bot.add_cog(Virement(bot)) \ No newline at end of file diff --git a/commandes/virement_banquier.py b/commandes/virement_banquier.py new file mode 100644 index 0000000..814bfca --- /dev/null +++ b/commandes/virement_banquier.py @@ -0,0 +1,86 @@ +import json +import discord +from discord import app_commands +from discord.ext import commands +from datetime import datetime + +BANQUIER_ROLE_ID = 1360314740567576616 # ID du rôle des banquiers # ID du rôle banquier # À remplacer par l’ID réel du rôle banquier + +class VirementBanquier(commands.Cog): + def __init__(self, bot): + self.bot = bot + + banque_choices = [ + app_commands.Choice(name="Victoria's Credit Bank", value="Victoria's Credit Bank"), + app_commands.Choice(name="NGC Bank", value="NGC Bank"), + ] + + @app_commands.command(name="virement_banquier", description="Effectue un virement au nom d'un client (réservé aux banquiers).") + @app_commands.choices(banque=banque_choices) + async def virement_banquier( + self, + interaction: discord.Interaction, + rib_emetteur: str, + montant: float, + rib_destinataire: str, + banque: app_commands.Choice[str] + ): + if not any(role.id == BANQUIER_ROLE_ID for role in interaction.user.roles): + await interaction.response.send_message("❌ Vous n'avez pas la permission d'utiliser cette commande.", ephemeral=True) + return + + banque_nom = banque.value + + try: + with open("comptes_bancaires.json", "r", encoding="utf-8") as f: + comptes = json.load(f) + except: + await interaction.response.send_message("❌ Erreur de lecture des comptes bancaires.", ephemeral=True) + return + + # Trouver les ID à partir des RIB + emetteur_id = None + destinataire_id = None + + for user_id, data in comptes.get(banque_nom, {}).items(): + if data.get("rib") == rib_emetteur: + emetteur_id = user_id + if data.get("rib") == rib_destinataire: + destinataire_id = user_id + + if not emetteur_id: + await interaction.response.send_message("❌ Le RIB émetteur est introuvable dans cette banque.", ephemeral=True) + return + if not destinataire_id: + await interaction.response.send_message("❌ Le RIB destinataire est introuvable dans cette banque.", ephemeral=True) + return + + # Vérifier le solde + if comptes[banque_nom][emetteur_id]["solde"] < montant: + await interaction.response.send_message("❌ Le solde du compte émetteur est insuffisant.", ephemeral=True) + return + + # Exécuter le virement + comptes[banque_nom][emetteur_id]["solde"] -= montant + comptes[banque_nom][emetteur_id]["transactions"].append({ + "date": datetime.now().strftime("%d/%m/%Y %H:%M:%S"), + "type": "Virement manuel (par banquier)", + "source": f"Vers RIB {rib_destinataire}", + "montant": -montant + }) + + comptes[banque_nom][destinataire_id]["solde"] += montant + comptes[banque_nom][destinataire_id]["transactions"].append({ + "date": datetime.now().strftime("%d/%m/%Y %H:%M:%S"), + "type": "Virement reçu (par banquier)", + "source": f"RIB {rib_emetteur}", + "montant": montant + }) + + with open("comptes_bancaires.json", "w", encoding="utf-8") as f: + json.dump(comptes, f, ensure_ascii=False, indent=4) + + await interaction.response.send_message(f"✅ Virement de **{montant:.2f}€** effectué de **{rib_emetteur}** vers **{rib_destinataire}** dans **{banque_nom}**.", ephemeral=True) + +async def setup(bot): + await bot.add_cog(VirementBanquier(bot)) diff --git a/commandes/virement_banquier_entreprise.py b/commandes/virement_banquier_entreprise.py new file mode 100644 index 0000000..e0de763 --- /dev/null +++ b/commandes/virement_banquier_entreprise.py @@ -0,0 +1,122 @@ +import discord +from discord import app_commands +from discord.ext import commands +import json +from datetime import datetime + +BANQUIER_ROLE_ID = 1360314740567576616 # ID du rôle des banquiers # ID du rôle banquier # ID du rôle des banquiers + +class VirementBanquierEntreprise(commands.Cog): + def __init__(self, bot): + self.bot = bot + + banques = [ + app_commands.Choice(name="Victoria's Credit Bank", value="Victoria's Credit Bank"), + app_commands.Choice(name="NGC Bank", value="NGC Bank") + ] + + @app_commands.command(name="virement_banquier_entreprise", description="Fait un virement depuis une entreprise avec mot de passe (réservé aux banquiers).") + @app_commands.choices(banque=banques) + async def virement_banquier_entreprise( + self, + interaction: discord.Interaction, + banque: app_commands.Choice[str], + nom_entreprise: str, + mot_de_passe: str, + rib_destinataire: str, + montant: float + ): + if not any(role.id == BANQUIER_ROLE_ID for role in interaction.user.roles): + await interaction.response.send_message("❌ Vous n'avez pas la permission de faire cette commande.", ephemeral=True) + return + + banque_nom = banque.value + + # Chargement des fichiers + try: + with open("comptes_entreprises.json", "r", encoding="utf-8") as f: + entreprises = json.load(f) + with open("comptes_bancaires.json", "r", encoding="utf-8") as f: + particuliers = json.load(f) + except Exception: + await interaction.response.send_message("❌ Erreur de lecture des fichiers JSON.", ephemeral=True) + return + + # Vérification de l'entreprise + if banque_nom not in entreprises or nom_entreprise not in entreprises[banque_nom]: + await interaction.response.send_message("❌ Compte entreprise introuvable.", ephemeral=True) + return + + compte_entreprise = entreprises[banque_nom][nom_entreprise] + + if compte_entreprise.get("mdp") != mot_de_passe: + await interaction.response.send_message("❌ Mot de passe incorrect.", ephemeral=True) + return + + if compte_entreprise["solde"] < montant: + await interaction.response.send_message("❌ Solde insuffisant pour ce virement.", ephemeral=True) + return + + # Recherche du compte destinataire (entreprise ou particulier) + destinataire_id = None + banque_destinataire = None + is_entreprise = False + + # Recherche dans entreprises + for bq, entreprises_data in entreprises.items(): + for nom, infos in entreprises_data.items(): + if infos.get("rib") == rib_destinataire: + destinataire_id = nom + banque_destinataire = bq + is_entreprise = True + break + if destinataire_id: + break + + # Sinon, recherche dans particuliers + if not destinataire_id: + for bq, clients in particuliers.items(): + for user_id, infos in clients.items(): + if infos.get("rib") == rib_destinataire: + destinataire_id = user_id + banque_destinataire = bq + is_entreprise = False + break + if destinataire_id: + break + + if not destinataire_id: + await interaction.response.send_message("❌ RIB destinataire introuvable.", ephemeral=True) + return + + # Retrait du montant + compte_entreprise["solde"] -= montant + compte_entreprise["transactions"].append({ + "date": datetime.now().strftime("%d/%m/%Y %H:%M:%S"), + "type": "Virement", + "source": f"Entreprise {nom_entreprise}", + "montant": -montant, + "destinataire": rib_destinataire + }) + + # Créditer le destinataire + compte_cible = entreprises if is_entreprise else particuliers + compte_data = compte_cible[banque_destinataire][destinataire_id] + compte_data["solde"] += montant + compte_data["transactions"].append({ + "date": datetime.now().strftime("%d/%m/%Y %H:%M:%S"), + "type": "Virement reçu", + "source": nom_entreprise, + "montant": montant + }) + + # Sauvegarde + with open("comptes_entreprises.json", "w", encoding="utf-8") as f: + json.dump(entreprises, f, ensure_ascii=False, indent=4) + with open("comptes_bancaires.json", "w", encoding="utf-8") as f: + json.dump(particuliers, f, ensure_ascii=False, indent=4) + + await interaction.response.send_message(f"✅ Virement de **{montant:.2f}€** effectué depuis **{nom_entreprise}** vers **{rib_destinataire}**.") + +async def setup(bot): + await bot.add_cog(VirementBanquierEntreprise(bot)) diff --git a/commandes/virement_entreprise.py b/commandes/virement_entreprise.py new file mode 100644 index 0000000..f156633 --- /dev/null +++ b/commandes/virement_entreprise.py @@ -0,0 +1,149 @@ +import discord +from discord import app_commands +from discord.ext import commands +import json +from datetime import datetime + +class VirementEntreprise(commands.Cog): + def __init__(self, bot): + self.bot = bot + + banque_choices = [ + app_commands.Choice(name="Victoria's Credit Bank", value="Victoria's Credit Bank"), + app_commands.Choice(name="NGC Bank", value="NGC Bank"), + ] + + @app_commands.command(name="virement_entreprise", description="Effectuer un virement depuis une entreprise vers un RIB.") + @app_commands.choices(banque=banque_choices) + async def virement_entreprise( + self, + interaction: discord.Interaction, + rib_entreprise: str, + montant: float, + mot_de_passe: str, + rib_destinataire: str, + banque: app_commands.Choice[str] + ): + banque_nom = banque.value + + # Chargement des données + try: + with open("comptes_entreprises.json", "r", encoding="utf-8") as f: + comptes_entreprises = json.load(f) + with open("comptes_bancaires.json", "r", encoding="utf-8") as f: + comptes_bancaires = json.load(f) + except Exception: + await interaction.response.send_message("❌ Erreur de lecture des données bancaires.", ephemeral=True) + return + + # 🔐 Vérification de l'émetteur + entreprise_id = None + for nom_entreprise, infos in comptes_entreprises.get(banque_nom, {}).items(): + if infos.get("rib") == rib_entreprise: + if infos.get("mdp") != mot_de_passe: + await interaction.response.send_message("❌ Mot de passe incorrect.", ephemeral=True) + return + entreprise_id = nom_entreprise + break + + if not entreprise_id: + await interaction.response.send_message("❌ Aucun compte entreprise trouvé avec ce RIB.", ephemeral=True) + return + + compte_emetteur = comptes_entreprises[banque_nom][entreprise_id] + + if compte_emetteur["solde"] < montant: + await interaction.response.send_message("❌ Solde insuffisant.", ephemeral=True) + return + + # 🔎 Recherche du destinataire + destinataire_data = None + destinataire_type = None + destinataire_nom = None + banque_dest = None + + for banque_cible, comptes in comptes_entreprises.items(): + for nom, infos in comptes.items(): + if infos.get("rib") == rib_destinataire: + destinataire_data = infos + destinataire_type = "entreprise" + destinataire_nom = nom + banque_dest = banque_cible + break + if destinataire_data: + break + + if not destinataire_data: + for banque_cible, comptes in comptes_bancaires.items(): + for uid, infos in comptes.items(): + if infos.get("rib") == rib_destinataire: + destinataire_data = infos + destinataire_type = "particulier" + destinataire_nom = uid + banque_dest = banque_cible + break + if destinataire_data: + break + + if not destinataire_data: + await interaction.response.send_message("❌ RIB destinataire introuvable.", ephemeral=True) + return + + # 📉 Débit du compte entreprise + compte_emetteur["solde"] -= montant + now = datetime.now().strftime("%d/%m/%Y %H:%M:%S") + compte_emetteur.setdefault("transactions", []).append({ + "date": now, + "type": "Virement", + "source": f"Vers RIB {rib_destinataire}", + "montant": -montant + }) + + # 📈 Crédit du destinataire (avec TVA si entreprise) + montant_net = montant + tva_envoyee = 0 + + if destinataire_type == "entreprise": + tva = destinataire_data.get("tva", 0) + tva_envoyee = round(montant * tva / 100, 2) + montant_net = montant - tva_envoyee + + # Crédit TVA à l'État + if "Gouvernement" not in comptes_entreprises: + comptes_entreprises["Gouvernement"] = {} + if "GOUV" not in comptes_entreprises["Gouvernement"]: + comptes_entreprises["Gouvernement"]["GOUV"] = { + "rib": "00000 00000 00000000000 00", + "solde": 0.0, + "transactions": [] + } + + comptes_entreprises["Gouvernement"]["GOUV"]["solde"] += tva_envoyee + comptes_entreprises["Gouvernement"]["GOUV"]["transactions"].append({ + "date": now, + "type": "TVA reçue", + "source": entreprise_id, + "montant": tva_envoyee + }) + + destinataire_data["solde"] += montant_net + destinataire_data.setdefault("transactions", []).append({ + "date": now, + "type": "Virement reçu", + "source": entreprise_id, + "montant": montant_net + }) + + # Sauvegardes + with open("comptes_entreprises.json", "w", encoding="utf-8") as f: + json.dump(comptes_entreprises, f, ensure_ascii=False, indent=4) + with open("comptes_bancaires.json", "w", encoding="utf-8") as f: + json.dump(comptes_bancaires, f, ensure_ascii=False, indent=4) + + await interaction.response.send_message( + f"✅ Virement effectué avec succès.{' TVA de ' + str(tva_envoyee) + '€ envoyée au Gouvernement.' if tva_envoyee else ''}", + ephemeral=True + ) + +async def setup(bot): + await bot.add_cog(VirementEntreprise(bot)) diff --git a/commandes/virement_fbm.py b/commandes/virement_fbm.py new file mode 100644 index 0000000..dbef678 --- /dev/null +++ b/commandes/virement_fbm.py @@ -0,0 +1,97 @@ +import discord +from discord import app_commands +from discord.ext import commands +import json +from datetime import datetime + +class VirementFBM(commands.Cog): + def __init__(self, bot): + self.bot = bot + + banque_choices = [ + app_commands.Choice(name="Victoria's Credit Bank", value="Victoria's Credit Bank"), + app_commands.Choice(name="NGC Bank", value="NGC Bank"), + ] + + @app_commands.command(name="virement_fbm", description="Fait un virement au compte indépendant FBM.") + @app_commands.choices(banque=banque_choices) + @app_commands.describe(montant="Montant à transférer", banque="Banque à utiliser") + async def virement_fbm(self, interaction: discord.Interaction, montant: float, banque: app_commands.Choice[str]): + auteur_id = str(interaction.user.id) + banque_nom = banque.value + + if montant <= 0: + await interaction.response.send_message("❌ Le montant doit être supérieur à 0.", ephemeral=True) + return + + # Charger les fichiers + try: + with open("comptes_bancaires.json", "r", encoding="utf-8") as f: + comptes = json.load(f) + except Exception: + await interaction.response.send_message("❌ Erreur lors de la lecture des comptes bancaires.", ephemeral=True) + return + + try: + with open("comptes_entreprises.json", "r", encoding="utf-8") as f: + entreprises = json.load(f) + except: + entreprises = {} + + # Vérification du compte de l'utilisateur + if banque_nom not in comptes or auteur_id not in comptes[banque_nom]: + await interaction.response.send_message("❌ Tu n'as pas de compte dans cette banque.", ephemeral=True) + return + + compte = comptes[banque_nom][auteur_id] + if compte["solde"] < montant: + await interaction.response.send_message("❌ Solde insuffisant pour effectuer le virement.", ephemeral=True) + return + + # Initialiser FBM si nécessaire + if "fbm" not in entreprises: + entreprises["fbm"] = {} + if "FBM" not in entreprises["fbm"]: + entreprises["fbm"]["FBM"] = { + "rib": "00000 00000 00000000000 03", + "solde": 0.0, + "transactions": [] + } + + now = datetime.now().strftime("%d/%m/%Y %H:%M:%S") + + # Mise à jour des soldes + compte["solde"] -= montant + entreprises["fbm"]["FBM"]["solde"] += montant + + # Historique utilisateur + compte["transactions"].append({ + "date": now, + "type": "Virement", + "source": f"Vers FBM", + "montant": -montant + }) + + # Historique FBM + entreprises["fbm"]["FBM"]["transactions"].append({ + "date": now, + "type": "Virement reçu", + "source": interaction.user.name, + "montant": montant + }) + + # Sauvegarde + with open("comptes_bancaires.json", "w", encoding="utf-8") as f: + json.dump(comptes, f, indent=4, ensure_ascii=False) + + with open("comptes_entreprises.json", "w", encoding="utf-8") as f: + json.dump(entreprises, f, indent=4, ensure_ascii=False) + + # Confirmation + await interaction.response.send_message( + f"✅ Virement de **{montant:.2f}€** envoyé au compte **FBM** depuis **{banque_nom}**.", + ephemeral=True + ) + +async def setup(bot): + await bot.add_cog(VirementFBM(bot)) diff --git a/commandes/virement_gouvernement.py b/commandes/virement_gouvernement.py new file mode 100644 index 0000000..9ee1015 --- /dev/null +++ b/commandes/virement_gouvernement.py @@ -0,0 +1,86 @@ +import discord +from discord import app_commands +from discord.ext import commands +import json +from datetime import datetime + +class VirementGouvernement(commands.Cog): + def __init__(self, bot): + self.bot = bot + + banque_choices = [ + app_commands.Choice(name="Victoria's Credit Bank", value="Victoria's Credit Bank"), + app_commands.Choice(name="NGC Bank", value="NGC Bank"), + ] + + @app_commands.command(name="virement_gouvernement", description="Effectue un virement au Gouvernement.") + @app_commands.choices(banque=banque_choices) + @app_commands.describe(montant="Le montant à envoyer", banque="Banque à utiliser pour le virement") + async def virement_gouvernement(self, interaction: discord.Interaction, montant: float, banque: app_commands.Choice[str]): + auteur_id = str(interaction.user.id) + banque_source = banque.value + + if montant <= 0: + await interaction.response.send_message("❌ Le montant doit être supérieur à zéro.", ephemeral=True) + return + + # Charger les fichiers + try: + with open('comptes_bancaires.json', 'r', encoding='utf-8') as f: + comptes = json.load(f) + with open('comptes_entreprises.json', 'r', encoding='utf-8') as f: + entreprises = json.load(f) + except Exception as e: + await interaction.response.send_message(f"❌ Erreur de lecture des fichiers : {e}", ephemeral=True) + return + + if banque_source not in comptes or auteur_id not in comptes[banque_source]: + await interaction.response.send_message(f"❌ Tu n'as pas de compte dans la banque **{banque_source}**.", ephemeral=True) + return + + compte_user = comptes[banque_source][auteur_id] + if compte_user["solde"] < montant: + await interaction.response.send_message("❌ Solde insuffisant pour effectuer ce virement.", ephemeral=True) + return + + # Vérifier ou créer le compte Gouvernement + if "Gouvernement" not in entreprises: + entreprises["Gouvernement"] = {} + if "GOUV" not in entreprises["Gouvernement"]: + entreprises["Gouvernement"]["GOUV"] = { + "rib": "00000 00000 00000000000 00", + "solde": 0.0, + "transactions": [] + } + + # Effectuer le virement + now = datetime.now().strftime("%d/%m/%Y %H:%M:%S") + compte_user["solde"] -= montant + entreprises["Gouvernement"]["GOUV"]["solde"] += montant + + compte_user["transactions"].append({ + "date": now, + "type": "Virement", + "source": f"Vers Gouvernement", + "montant": -montant + }) + entreprises["Gouvernement"]["GOUV"]["transactions"].append({ + "date": now, + "type": "Virement reçu", + "source": interaction.user.name, + "montant": montant + }) + + # Sauvegarde + with open('comptes_bancaires.json', 'w', encoding='utf-8') as f: + json.dump(comptes, f, indent=4, ensure_ascii=False) + with open('comptes_entreprises.json', 'w', encoding='utf-8') as f: + json.dump(entreprises, f, indent=4, ensure_ascii=False) + + await interaction.response.send_message( + f"✅ Virement de **{montant:.2f}€** envoyé au Gouvernement avec succès.", + ephemeral=True + ) + +async def setup(bot): + await bot.add_cog(VirementGouvernement(bot)) \ No newline at end of file diff --git a/commandes/virement_ursaff.py b/commandes/virement_ursaff.py new file mode 100644 index 0000000..84dbe51 --- /dev/null +++ b/commandes/virement_ursaff.py @@ -0,0 +1,93 @@ +import discord +from discord import app_commands +from discord.ext import commands +import json +from datetime import datetime + +class VirementUrssaf(commands.Cog): + def __init__(self, bot): + self.bot = bot + + banque_choices = [ + app_commands.Choice(name="Victoria's Credit Bank", value="Victoria's Credit Bank"), + app_commands.Choice(name="NGC Bank", value="NGC Bank"), + ] + + @app_commands.command(name="virement_ursaff", description="Fait un virement vers le compte indépendant URSAFF.") + @app_commands.choices(banque=banque_choices) + @app_commands.describe(montant="Montant à transférer", banque="Banque à utiliser") + async def virement_ursaff(self, interaction: discord.Interaction, montant: float, banque: app_commands.Choice[str]): + auteur_id = str(interaction.user.id) + banque_nom = banque.value + + if montant <= 0: + await interaction.response.send_message("❌ Le montant doit être supérieur à 0.", ephemeral=True) + return + + try: + with open("comptes_bancaires.json", "r", encoding="utf-8") as f: + comptes = json.load(f) + except Exception: + await interaction.response.send_message("❌ Erreur lors de la lecture des comptes bancaires.", ephemeral=True) + return + + try: + with open("comptes_entreprises.json", "r", encoding="utf-8") as f: + entreprises = json.load(f) + except: + entreprises = {} + + if banque_nom not in comptes or auteur_id not in comptes[banque_nom]: + await interaction.response.send_message("❌ Tu n'as pas de compte dans cette banque.", ephemeral=True) + return + + compte = comptes[banque_nom][auteur_id] + if compte["solde"] < montant: + await interaction.response.send_message("❌ Solde insuffisant pour effectuer le virement.", ephemeral=True) + return + + # Initialiser URSAFF si nécessaire + if "ursaff" not in entreprises: + entreprises["ursaff"] = {} + if "URSAFF" not in entreprises["ursaff"]: + entreprises["ursaff"]["URSAFF"] = { + "rib": "00000 00000 00000000000 02", + "solde": 0.0, + "transactions": [] + } + + now = datetime.now().strftime("%d/%m/%Y %H:%M:%S") + + # Mise à jour des soldes + compte["solde"] -= montant + entreprises["ursaff"]["URSAFF"]["solde"] += montant + + # Historique utilisateur + compte["transactions"].append({ + "date": now, + "type": "Virement", + "source": "Vers URSAFF", + "montant": -montant + }) + + # Historique URSAFF + entreprises["ursaff"]["URSAFF"]["transactions"].append({ + "date": now, + "type": "Virement reçu", + "source": interaction.user.name, + "montant": montant + }) + + # Sauvegarde + with open("comptes_bancaires.json", "w", encoding="utf-8") as f: + json.dump(comptes, f, indent=4, ensure_ascii=False) + with open("comptes_entreprises.json", "w", encoding="utf-8") as f: + json.dump(entreprises, f, indent=4, ensure_ascii=False) + + await interaction.response.send_message( + f"✅ Virement de **{montant:.2f}€** envoyé au compte **URSAFF** depuis **{banque_nom}**.", + ephemeral=True + ) + +async def setup(bot): + await bot.add_cog(VirementUrssaf(bot)) diff --git a/commandes/voir_carte_identite.py b/commandes/voir_carte_identite.py new file mode 100644 index 0000000..986885f --- /dev/null +++ b/commandes/voir_carte_identite.py @@ -0,0 +1,65 @@ +import discord +from discord import app_commands +from discord.ext import commands +from utils.data_manager import DataManager +from utils.card_generator import CardGenerator + +card_generator = CardGenerator() + +class VoirIdentite(commands.Cog): + def __init__(self, bot): + self.bot = bot + + @app_commands.command(name="voir_carte_identite", description="Afficher votre carte d'identité ou celle d'un autre utilisateur") + async def voir_carte_identite(self, interaction: discord.Interaction, utilisateur: discord.Member = None): + """Affiche la carte d'identité d'un utilisateur""" + + # Remplacer par l'ID de ton rôle gouvernement + GOUVERNEMENT_ROLE_ID = 1360236421872484403 + + target_user = utilisateur or interaction.user + + # 🔒 Vérification si l'utilisateur peut voir la carte d'un autre + if utilisateur and utilisateur != interaction.user: + gouvernement_role = interaction.guild.get_role(GOUVERNEMENT_ROLE_ID) + if gouvernement_role not in interaction.user.roles: + await interaction.response.send_message( + "🚫 Vous n'avez pas l'autorisation de voir la carte d'identité d'un autre utilisateur.", + ephemeral=True + ) + return + + user_id = str(target_user.id) + + # 🧠 Recréer un DataManager à chaque commande + data_manager = DataManager() + + # 🔎 Récupération des données + identity_data = data_manager.identities.get(user_id) + + if identity_data is None: + await interaction.response.send_message( + "❌ Cet utilisateur ne possède pas de carte d'identité.", + ephemeral=True + ) + return + + # 🛡️ Vérification que l'identité est complète + if "date_expiration" not in identity_data: + identity_data["date_expiration"] = "Non spécifiée" + + # 🖼️ Génération de l'image de la carte + card_buffer = await card_generator.create_identity_card(target_user, identity_data) + + # 📩 Envoi du fichier + embed + file = discord.File(card_buffer, filename="carte_identite.png") + embed = discord.Embed( + title=f"🪪 Carte d'identité de {target_user.display_name}", + color=0x2ecc71 + ) + embed.set_image(url="attachment://carte_identite.png") + + await interaction.response.send_message(file=file, embed=embed, ephemeral=False) + +async def setup(bot): + await bot.add_cog(VoirIdentite(bot)) diff --git a/commandes/welcome_message.py b/commandes/welcome_message.py new file mode 100644 index 0000000..c3d6d62 --- /dev/null +++ b/commandes/welcome_message.py @@ -0,0 +1,30 @@ +import discord +from discord.ext import commands +from utils.logger import log_info, log_error + +class WelcomeMessage(commands.Cog): + def __init__(self, bot): + self.bot = bot + + @commands.Cog.listener() + async def on_member_join(self, member): + try: + # ID du salon discussion + welcome_channel_id = 1278483527783944242 + welcome_channel = member.guild.get_channel(welcome_channel_id) + + if welcome_channel: + welcome_message = ( + f"## BIENVENUE <:panda_welcome:1297298999849517056> !\n\n" + f"> **L'équipe staff te souhaite la bienvenue sur Victoria's Vice {member.mention} !**\n\n" + f"> Pour ta candidature <:arrow_1:1297491509201080362> <#1278477608392196176> / <#1278477152215371806> \n\n" + f"> Si tu as des questions <:arrow_1:1297491509201080362> <#1279037583820718120>\n\n" + f"*> Bien cordialement, L'équipe staff de Victoria's Vice*" + ) + + await welcome_channel.send(welcome_message) + except Exception as e: + print(f"Erreur lors de l'envoi du message de bienvenue: {e}") + +async def setup(bot): + await bot.add_cog(WelcomeMessage(bot)) diff --git a/comptes_bancaires.json b/comptes_bancaires.json new file mode 100644 index 0000000..217bebc --- /dev/null +++ b/comptes_bancaires.json @@ -0,0 +1,2111 @@ +{ + "NGC Bank": { + "353942721997832195": { + "rib": "36495 80083 39542816185 27", + "solde": 4970.0, + "transactions": [ + { + "date": "", + "type": "Dépôt", + "source": "Banque", + "montant": 3500.0, + "emetteur": 353942721997832195, + "destinataire": 353942721997832195 + }, + { + "date": "26/04/2025 14:03:26", + "type": "Virement", + "source": "Vers RIB 97593 41945 69675546643 66", + "montant": -3400.0 + }, + { + "date": "26/04/2025 14:03:32", + "type": "Virement", + "source": "Vers RIB 97593 41945 69675546643 66", + "montant": -100.0 + }, + { + "date": "", + "type": "Dépôt", + "source": "Banque", + "montant": 36000.0, + "emetteur": 353942721997832195, + "destinataire": 353942721997832195 + }, + { + "date": "04/05/2025 13:33:43", + "type": "Virement", + "source": "Vers RIB 24193 46448 36423591130 82", + "montant": -650.0 + }, + { + "date": "05/05/2025 16:09:39", + "type": "Virement", + "source": "Vers RIB 96241 34733 44548 77079", + "montant": -2000.0 + }, + { + "date": "23/05/2025 18:39:25", + "type": "Dépôt", + "source": "Banque", + "montant": 36000.0, + "emetteur": 353942721997832195, + "destinataire": "353942721997832195" + }, + { + "date": "23/05/2025 20:04:57", + "type": "Virement reçu", + "source": "mg_studios_", + "montant": 3501.0 + }, + { + "date": "05/06/2025 15:33:55", + "type": "Virement", + "source": "Vers RIB 97593 41945 69675546643 66", + "montant": -72851.0 + }, + { + "date": "05/06/2025 20:47:43", + "type": "Dépôt", + "source": "Banque", + "montant": 13500.0, + "emetteur": 353942721997832195, + "destinataire": "353942721997832195" + }, + { + "date": "05/06/2025 20:49:10", + "type": "Virement", + "source": "Vers RIB 97593 41945 69675546643 66", + "montant": -13500.0 + } + ] + }, + "942073262333112351": { + "rib": "26384 93370 75890178377 91", + "solde": 5000.0, + "transactions": [] + }, + "971446412690722826": { + "rib": "22144 59424 87742058459 72", + "solde": 15948.8, + "transactions": [ + { + "date": "", + "type": "Dépôt", + "source": "Banque", + "montant": 3450.0, + "emetteur": 353942721997832195, + "destinataire": 971446412690722826 + }, + { + "date": "04/05/2025 14:43:10", + "type": "Dépôt", + "source": "Banque", + "montant": 5000.0, + "emetteur": 534003230435442690, + "destinataire": "971446412690722826" + }, + { + "date": "04/05/2025 16:49:40", + "type": "Virement", + "source": "Vers RIB 10009 40554 56452790470 78", + "montant": -0.1 + }, + { + "date": "04/05/2025 16:51:20", + "type": "Virement", + "source": "Vers RIB 68229 13765 90163 35585", + "montant": -0.1 + }, + { + "date": "07/05/2025 19:45:13", + "type": "Virement", + "source": "Vers Gouvernement", + "montant": -1.0 + }, + { + "date": "23/05/2025 18:36:09", + "type": "Dépôt", + "source": "Banque", + "montant": 4500.0, + "emetteur": 353942721997832195, + "destinataire": "971446412690722826" + } + ] + }, + "1200313720195264613": { + "rib": "85047 60626 80894743346 94", + "solde": 0.0, + "transactions": [ + { + "date": "", + "type": "Dépôt", + "source": "Banque", + "montant": 3990.0, + "emetteur": 353942721997832195, + "destinataire": 1200313720195264613 + }, + { + "date": "21/04/2025 15:05:29", + "type": "Virement", + "source": "Vers RIB 15563 69899 51361446295 84", + "montant": -390.0 + }, + { + "date": "", + "type": "Dépôt", + "source": "Banque", + "montant": 32000.0, + "emetteur": 353942721997832195, + "destinataire": 1200313720195264613 + }, + { + "date": "03/05/2025 17:09:30", + "type": "Virement", + "source": "Vers RIB 76644 32901 90493249064 16", + "montant": -32000.0 + }, + { + "date": "04/05/2025 08:32:17", + "type": "Dépôt", + "source": "Banque", + "montant": 45000.0, + "emetteur": 534003230435442690, + "destinataire": "1200313720195264613" + }, + { + "date": "04/05/2025 09:49:49", + "type": "Virement", + "source": "Vers RIB 76644 32901 90493249064 16", + "montant": -45000.0 + }, + { + "date": "09/05/2025 17:20:17", + "type": "Dépôt", + "source": "Banque", + "montant": 8000.0, + "emetteur": 353942721997832195, + "destinataire": "1200313720195264613" + }, + { + "date": "09/05/2025 17:23:13", + "type": "Virement", + "source": "Vers RIB 54201 75079 71658 35659", + "montant": -390.0 + }, + { + "date": "09/05/2025 19:09:30", + "type": "Retrait", + "source": "Banque", + "montant": -5000.0, + "emetteur": 534003230435442690, + "destinataire": "1200313720195264613" + }, + { + "date": "19/05/2025 11:55:01", + "type": "Virement", + "source": "Vers RIB 54201 75079 71658 35659", + "montant": -390.0 + }, + { + "date": "03/06/2025 17:38:01", + "type": "Virement", + "source": "Vers RIB 85047 60626 80894743346 94", + "montant": -2220.0 + }, + { + "date": "03/06/2025 17:38:01", + "type": "Virement reçu", + "source": "agent_chaos_officiel", + "montant": 2220.0 + }, + { + "date": "03/06/2025 17:39:36", + "type": "Virement", + "source": "Vers RIB 24756 54403 68357307735 95", + "montant": -2220.0 + } + ] + }, + "1361493382911758528": { + "rib": "28777 24580 29765721710 42", + "solde": 5000.0, + "transactions": [ + { + "date": "", + "type": "Dépôt", + "source": "Banque", + "montant": 5000.0, + "emetteur": 353942721997832195, + "destinataire": 1361493382911758528 + } + ] + }, + "1096453463849828362": { + "rib": "24483 98056 61791340797 80", + "solde": 5400.0, + "transactions": [ + { + "date": "", + "type": "Dépôt", + "source": "Banque", + "montant": 5150.0, + "emetteur": 353942721997832195, + "destinataire": 1096453463849828362 + }, + { + "date": "03/05/2025 15:54:14", + "type": "Dépôt", + "source": "Banque", + "montant": 250.0, + "emetteur": 534003230435442690, + "destinataire": "1096453463849828362" + } + ] + }, + "534003230435442690": { + "rib": "10009 40554 56452790470 78", + "solde": 11556.989999999998, + "transactions": [ + { + "date": "", + "type": "Dépôt", + "source": "Banque", + "montant": 4900.0, + "emetteur": 353942721997832195, + "destinataire": 534003230435442690 + }, + { + "date": "", + "type": "Dépôt", + "source": "Banque", + "montant": 1000.0, + "emetteur": 534003230435442690, + "destinataire": 534003230435442690 + }, + { + "date": "26/04/2025 15:55:13", + "type": "Virement", + "source": "Vers RIB 11940 73588 95258 04768", + "montant": -1.0 + }, + { + "date": "26/04/2025 17:24:15", + "type": "Virement", + "source": "Vers RIB 87681 15340 34475481972 89", + "montant": -0.1 + }, + { + "date": "26/04/2025 17:26:37", + "type": "Virement reçu", + "source": "sz94d", + "montant": 10.0 + }, + { + "date": "27/04/2025 10:21:57", + "type": "Virement", + "source": "Vers RIB 00000 00000 00000000000 00", + "montant": -0.01 + }, + { + "date": "27/04/2025 13:34:55", + "type": "Virement", + "source": "Vers RIB 11940 73588 95258 04768", + "montant": -1.0 + }, + { + "date": "27/04/2025 14:01:32", + "type": "Virement", + "source": "Vers RIB 11940 73588 95258 04768", + "montant": -1.0 + }, + { + "date": "03/05/2025 15:37:30", + "type": "Dépôt", + "source": "Banque", + "montant": 62500.0, + "emetteur": 534003230435442690, + "destinataire": "534003230435442690" + }, + { + "date": "03/05/2025 17:39:12", + "type": "Virement", + "source": "Vers RIB 79307 68952 45977 60249", + "montant": -62500.0 + }, + { + "date": "04/05/2025 14:12:44", + "type": "Retrait", + "source": "Banque", + "montant": -20.0, + "emetteur": 534003230435442690, + "destinataire": "534003230435442690" + }, + { + "date": "04/05/2025 14:28:36", + "type": "Retrait", + "source": "Banque", + "montant": -70.0, + "emetteur": 534003230435442690, + "destinataire": "534003230435442690" + }, + { + "date": "04/05/2025 16:49:40", + "type": "Virement reçu", + "source": "gameurpro12", + "montant": 0.1 + }, + { + "date": "05/05/2025 16:02:28", + "type": "Virement", + "source": "Vers RIB 22128 86524 35982837421 58", + "montant": -0.1 + }, + { + "date": "05/05/2025 16:08:51", + "type": "Virement reçu", + "source": "docteur_w.d_axolotl", + "montant": 0.1 + }, + { + "date": "04/06/2025 16:27:22", + "type": "Dépôt", + "source": "Banque", + "montant": 45000.0, + "emetteur": 534003230435442690, + "destinataire": "534003230435442690" + }, + { + "date": "04/06/2025 16:28:42", + "type": "Retrait", + "source": "Banque", + "montant": -36000.0, + "emetteur": 534003230435442690, + "destinataire": "534003230435442690" + } + ] + }, + "376796341071773706": { + "rib": "76644 32901 90493249064 16", + "solde": 219751.0, + "transactions": [ + { + "date": "", + "type": "Dépôt", + "source": "Banque", + "montant": 5000.0, + "emetteur": 353942721997832195, + "destinataire": 376796341071773706 + }, + { + "date": "03/05/2025 17:09:30", + "type": "Virement reçu", + "source": "agent_chaos_officiel", + "montant": 32000.0 + }, + { + "date": "03/05/2025 17:11:51", + "type": "Virement reçu", + "source": ".1all_might1.", + "montant": 28751.0 + }, + { + "date": "03/05/2025 17:12:58", + "type": "Virement reçu", + "source": "vaasbey", + "montant": 30000.0 + }, + { + "date": "04/05/2025 09:49:49", + "type": "Virement reçu", + "source": "agent_chaos_officiel", + "montant": 45000.0 + }, + { + "date": "04/05/2025 15:18:00", + "type": "Retrait", + "source": "Banque", + "montant": -4000.0, + "emetteur": 534003230435442690, + "destinataire": "376796341071773706" + }, + { + "date": "11/05/2025 09:52:08", + "type": "Virement reçu", + "source": "neyzizz", + "montant": 140000.0 + }, + { + "date": "18/05/2025 15:34:58", + "type": "Virement", + "source": "Vers RIB 79307 68952 45977 60249", + "montant": -12000.0 + }, + { + "date": "18/05/2025 15:34:58", + "type": "Virement", + "source": "Vers RIB 79307 68952 45977 60249", + "montant": -12000.0 + }, + { + "date": "29/05/2025 13:51:53", + "type": "Virement", + "source": "Vers RIB 79307 68952 45977 60249", + "montant": -30000.0 + }, + { + "date": "29/05/2025 13:58:09", + "type": "Virement", + "source": "Vers RIB 41884 29541 59229304698 34", + "montant": -3000.0 + } + ] + }, + "449424004663607307": { + "rib": "15563 69899 51361446295 84", + "solde": 4800.0, + "transactions": [ + { + "date": "", + "type": "Dépôt", + "source": "Banque", + "montant": 5000.0, + "emetteur": 353942721997832195, + "destinataire": 449424004663607307 + }, + { + "date": "21/04/2025 15:05:29", + "type": "Virement reçu", + "source": "agent_chaos_officiel", + "montant": 390.0 + } + ] + }, + "828239898975666216": { + "rib": "22128 86524 35982837421 58", + "solde": 0.0, + "transactions": [ + { + "date": "", + "type": "Dépôt", + "source": "Banque", + "montant": 5000.0, + "emetteur": 353942721997832195, + "destinataire": 828239898975666216 + }, + { + "date": "05/05/2025 16:02:28", + "type": "Virement reçu", + "source": "nixfix06", + "montant": 0.1 + }, + { + "date": "05/05/2025 16:08:51", + "type": "Virement", + "source": "Vers RIB 10009 40554 56452790470 78", + "montant": -0.1 + }, + { + "date": "05/05/2025 16:15:05", + "type": "Virement reçu", + "source": "lagrenouillebeka", + "montant": 500.0 + }, + { + "date": "05/05/2025 16:23:40", + "type": "Virement", + "source": "Vers RIB 93884 11674 79519 40904", + "montant": -500.0 + } + ] + }, + "946068504023556186": { + "rib": "55868 11389 89049343014 38", + "solde": 1300.0, + "transactions": [ + { + "date": "", + "type": "Dépôt", + "source": "Banque", + "montant": 4700.0, + "emetteur": 353942721997832195, + "destinataire": 946068504023556186 + }, + { + "date": "", + "type": "Dépôt", + "source": "Banque", + "montant": 24000.0, + "emetteur": 353942721997832195, + "destinataire": 946068504023556186 + }, + { + "date": "03/05/2025 15:22:24", + "type": "Dépôt", + "source": "Banque", + "montant": 1648.0, + "emetteur": 534003230435442690, + "destinataire": "946068504023556186" + }, + { + "date": "03/05/2025 17:11:51", + "type": "Virement", + "source": "Vers RIB 76644 32901 90493249064 16", + "montant": -28751.0 + }, + { + "date": "17/05/2025 16:46:12", + "type": "Virement reçu", + "source": "leandro_6548.", + "montant": 1300.0 + } + ] + }, + "1069386396764221450": { + "rib": "93266 82343 74845298309 22", + "solde": 31250.0, + "transactions": [ + { + "date": "", + "type": "Dépôt", + "source": "Banque", + "montant": 4500.0, + "emetteur": 353942721997832195, + "destinataire": 1069386396764221450 + }, + { + "date": "", + "type": "Dépôt", + "source": "Banque", + "montant": 10000.0, + "emetteur": 353942721997832195, + "destinataire": 1069386396764221450 + }, + { + "date": "08/05/2025 15:49:41", + "type": "Dépôt", + "source": "Banque", + "montant": 6000.0, + "emetteur": 353942721997832195, + "destinataire": "1069386396764221450" + }, + { + "date": "05/06/2025 20:44:23", + "type": "Dépôt", + "source": "Banque", + "montant": 11250.0, + "emetteur": 353942721997832195, + "destinataire": "1069386396764221450" + }, + { + "date": "25/06/2025 16:01:31", + "type": "Retrait", + "source": "Banque", + "montant": -500.0, + "emetteur": 906574786308734986, + "destinataire": "1069386396764221450" + } + ] + }, + "828923448453103637": { + "rib": "10234 24218 10477731114 78", + "solde": 34000.0, + "transactions": [ + { + "date": "", + "type": "Dépôt", + "source": "Banque", + "montant": 5000.0, + "emetteur": 353942721997832195, + "destinataire": 828923448453103637 + }, + { + "date": "", + "type": "Dépôt", + "source": "Banque", + "montant": 20000.0, + "emetteur": 353942721997832195, + "destinataire": 828923448453103637 + }, + { + "date": "05/06/2025 20:45:18", + "type": "Dépôt", + "source": "Banque", + "montant": 9000.0, + "emetteur": 353942721997832195, + "destinataire": "828923448453103637" + } + ] + }, + "1311384140905709578": { + "rib": "50970 51336 33017058086 50", + "solde": 5000.0, + "transactions": [ + { + "date": "", + "type": "Dépôt", + "source": "Banque", + "montant": 5000.0, + "emetteur": 353942721997832195, + "destinataire": 1311384140905709578 + } + ] + }, + "1126995714552111215": { + "rib": "87292 81104 84784268868 12", + "solde": 0.0, + "transactions": [ + { + "date": "", + "type": "Dépôt", + "source": "Banque", + "montant": 5000.0, + "emetteur": 353942721997832195, + "destinataire": 1126995714552111215 + }, + { + "date": "21/04/2025 20:59:04", + "type": "Virement reçu", + "source": "neyzizz", + "montant": 120.0 + }, + { + "date": "03/05/2025 15:12:32", + "type": "Dépôt", + "source": "Banque", + "montant": 2075.0, + "emetteur": 534003230435442690, + "destinataire": "1126995714552111215" + }, + { + "date": "03/05/2025 17:19:15", + "type": "Virement", + "source": "Vers RIB 24193 46448 36423591130 82", + "montant": -102.0 + }, + { + "date": "18/05/2025 15:17:43", + "type": "Virement", + "source": "Vers RIB 79307 68952 45977 60249", + "montant": -12000.0 + }, + { + "date": "18/05/2025 15:24:40", + "type": "Retrait", + "source": "Banque", + "montant": -3000.0, + "emetteur": 534003230435442690, + "destinataire": "1126995714552111215" + }, + { + "date": "18/05/2025 16:06:44", + "type": "Virement reçu", + "source": "tricks212_zz", + "montant": 17575.0 + }, + { + "date": "23/05/2025 19:27:39", + "type": "Virement", + "source": "Vers RIB 73834 54818 11833486819 17", + "montant": -17575.0 + } + ] + }, + "642831860409565225": { + "rib": "51229 80290 86979675797 93", + "solde": 4000.0, + "transactions": [ + { + "date": "", + "type": "Dépôt", + "source": "Banque", + "montant": 4000.0, + "emetteur": 353942721997832195, + "destinataire": 642831860409565225 + } + ] + }, + "914118793918312480": { + "rib": "28794 77878 41312953164 71", + "solde": 17690.0, + "transactions": [ + { + "date": "", + "type": "Dépôt", + "source": "Banque", + "montant": 4000.0, + "emetteur": 353942721997832195, + "destinataire": 914118793918312480 + }, + { + "date": "21/04/2025 20:59:04", + "type": "Virement", + "source": "Vers RIB 87292 81104 84784268868 12", + "montant": -120.0 + }, + { + "date": "", + "type": "Dépôt", + "source": "Banque", + "montant": 26000.0, + "emetteur": 353942721997832195, + "destinataire": 914118793918312480 + }, + { + "date": "01/05/2025 08:56:08", + "type": "Virement", + "source": "Vers RIB 54201 75079 71658 35659", + "montant": -260.0 + }, + { + "date": "10/05/2025 20:01:32", + "type": "Dépôt", + "source": "Banque", + "montant": 120000.0, + "emetteur": 534003230435442690, + "destinataire": "914118793918312480" + }, + { + "date": "11/05/2025 09:52:08", + "type": "Virement", + "source": "Vers RIB 76644 32901 90493249064 16", + "montant": -140000.0 + }, + { + "date": "23/05/2025 18:33:43", + "type": "Dépôt", + "source": "Banque", + "montant": 9750.0, + "emetteur": 353942721997832195, + "destinataire": "914118793918312480" + } + ] + }, + "1351957361333370932": { + "rib": "78740 15751 70195185799 30", + "solde": 7250.0, + "transactions": [ + { + "date": "", + "type": "Dépôt", + "source": "Banque", + "montant": 5000.0, + "emetteur": 353942721997832195, + "destinataire": 1351957361333370932 + }, + { + "date": "", + "type": "Dépôt", + "source": "Banque", + "montant": 2250.0, + "emetteur": 353942721997832195, + "destinataire": 1351957361333370932 + } + ] + }, + "1018567127269122058": { + "rib": "92871 52823 19649316403 70", + "solde": 5000.0, + "transactions": [ + { + "date": "", + "type": "Dépôt", + "source": "Banque", + "montant": 5000.0, + "emetteur": 353942721997832195, + "destinataire": 1018567127269122058 + } + ] + }, + "1108087282692522105": { + "rib": "73834 54818 11833486819 17", + "solde": 17575.0, + "transactions": [ + { + "date": "", + "type": "Dépôt", + "source": "Banque", + "montant": 4500.0, + "emetteur": 353942721997832195, + "destinataire": 1108087282692522105 + }, + { + "date": "28/04/2025 08:53:44", + "type": "Virement", + "source": "Vers RIB 54201 75079 71658 35659", + "montant": -425.0 + }, + { + "date": "", + "type": "Dépôt", + "source": "Banque", + "montant": 12000.0, + "emetteur": 353942721997832195, + "destinataire": 1108087282692522105 + }, + { + "date": "", + "type": "Dépôt", + "source": "Banque", + "montant": 1500.0, + "emetteur": 353942721997832195, + "destinataire": 1108087282692522105 + }, + { + "date": "18/05/2025 16:06:44", + "type": "Virement", + "source": "Vers RIB 87292 81104 84784268868 12", + "montant": -17575.0 + }, + { + "date": "23/05/2025 18:34:44", + "type": "Dépôt", + "source": "Banque", + "montant": 6000.0, + "emetteur": 353942721997832195, + "destinataire": "1108087282692522105" + }, + { + "date": "23/05/2025 18:35:15", + "type": "Retrait", + "source": "Banque", + "montant": -2000.0, + "emetteur": 353942721997832195, + "destinataire": "1108087282692522105" + }, + { + "date": "23/05/2025 19:27:39", + "type": "Virement reçu", + "source": "jl_wesley.f1", + "montant": 17575.0 + }, + { + "date": "22/06/2025 13:32:09", + "type": "Retrait", + "source": "Banque", + "montant": -4000.0, + "emetteur": 534003230435442690, + "destinataire": "1108087282692522105" + } + ] + }, + "501080933282218005": { + "rib": "24193 46448 36423591130 82", + "solde": 16828.0, + "transactions": [ + { + "date": "", + "type": "Dépôt", + "source": "Banque", + "montant": 5121.0, + "emetteur": 353942721997832195, + "destinataire": 501080933282218005 + }, + { + "date": "", + "type": "Dépôt", + "source": "Banque", + "montant": 5250.0, + "emetteur": 353942721997832195, + "destinataire": 501080933282218005 + }, + { + "date": "01/05/2025 15:42:09", + "type": "Virement reçu", + "source": "hvk_pompotfraicz", + "montant": 100.0 + }, + { + "date": "01/05/2025 18:31:48", + "type": "Virement reçu", + "source": "e.93130", + "montant": 130.0 + }, + { + "date": "03/05/2025 17:19:15", + "type": "Virement reçu", + "source": "jl_wesley.f1", + "montant": 102.0 + }, + { + "date": "04/05/2025 13:16:52", + "type": "Virement reçu", + "source": "leandro_6548.", + "montant": 5.0 + }, + { + "date": "04/05/2025 13:33:43", + "type": "Virement reçu", + "source": "mg_studios_", + "montant": 650.0 + }, + { + "date": "04/05/2025 13:33:58", + "type": "Dépôt", + "source": "Banque", + "montant": 650.0, + "emetteur": 353942721997832195, + "destinataire": "501080933282218005" + }, + { + "date": "09/05/2025 20:37:43", + "type": "Virement reçu", + "source": "sz94d", + "montant": 1400.0 + }, + { + "date": "09/05/2025 21:05:26", + "type": "Virement reçu", + "source": "sz94d", + "montant": 1.0 + }, + { + "date": "09/05/2025 21:06:02", + "type": "Virement reçu", + "source": "sz94d", + "montant": 699.0 + }, + { + "date": "11/05/2025 13:26:45", + "type": "Virement reçu", + "source": "vaasbey", + "montant": 14.0 + }, + { + "date": "11/05/2025 13:31:05", + "type": "Virement reçu", + "source": "vaasbey", + "montant": 14000.0 + }, + { + "date": "11/05/2025 13:59:15", + "type": "Virement reçu", + "source": "e.93130", + "montant": 300.0 + }, + { + "date": "11/05/2025 14:50:02", + "type": "Virement", + "source": "Vers RIB 79307 68952 45977 60249", + "montant": -12000.0 + }, + { + "date": "11/05/2025 21:50:04", + "type": "Virement reçu", + "source": "leandro_6548.", + "montant": 2000.0 + } + ] + }, + "1072112886509473792": { + "rib": "48003 84424 36961155485 64", + "solde": 12555.0, + "transactions": [ + { + "date": "", + "type": "Dépôt", + "source": "Banque", + "montant": 5000.0, + "emetteur": 353942721997832195, + "destinataire": 1072112886509473792 + }, + { + "date": "", + "type": "Dépôt", + "source": "Banque", + "montant": 70000.0, + "emetteur": 534003230435442690, + "destinataire": 1072112886509473792 + }, + { + "date": "05/05/2025 16:15:05", + "type": "Virement", + "source": "Vers RIB 22128 86524 35982837421 58", + "montant": -500.0 + }, + { + "date": "05/05/2025 20:34:40", + "type": "Dépôt", + "source": "Banque", + "montant": 2500.0, + "emetteur": 353942721997832195, + "destinataire": "1072112886509473792" + }, + { + "date": "05/05/2025 20:37:02", + "type": "Dépôt", + "source": "Banque", + "montant": 1000.0, + "emetteur": 353942721997832195, + "destinataire": "1072112886509473792" + }, + { + "date": "09/05/2025 19:19:02", + "type": "Retrait", + "source": "Banque", + "montant": -500.0, + "emetteur": 534003230435442690, + "destinataire": "1072112886509473792" + }, + { + "date": "11/05/2025 14:49:46", + "type": "Virement", + "source": "Vers RIB 79307 68952 45977 60249", + "montant": -70000.0 + }, + { + "date": "29/05/2025 14:48:33", + "type": "Retrait", + "source": "Banque", + "montant": -5000.0, + "emetteur": 534003230435442690, + "destinataire": "1072112886509473792" + }, + { + "date": "07/06/2025 10:24:05", + "type": "Dépôt", + "source": "Banque", + "montant": 9375.0, + "emetteur": 353942721997832195, + "destinataire": "1072112886509473792" + }, + { + "date": "25/06/2025 16:03:43", + "type": "Dépôt", + "source": "Banque", + "montant": 800.0, + "emetteur": 906574786308734986, + "destinataire": "1072112886509473792" + } + ] + }, + "844278742204547072": { + "rib": "70314 58525 37531515072 79", + "solde": 4900.0, + "transactions": [ + { + "date": "", + "type": "Dépôt", + "source": "Banque", + "montant": 4900.0, + "emetteur": 353942721997832195, + "destinataire": 844278742204547072 + } + ] + }, + "1108451968202068018": { + "rib": "Non défini", + "solde": 4500.0, + "transactions": [ + { + "date": "", + "type": "Dépôt", + "source": "Banque", + "montant": 4500.0, + "emetteur": 353942721997832195, + "destinataire": 1108451968202068018 + } + ] + }, + "730574544821157988": { + "rib": "41884 29541 59229304698 34", + "solde": 3302.0, + "transactions": [ + { + "date": "", + "type": "Dépôt", + "source": "Banque", + "montant": 152.0, + "emetteur": 534003230435442690, + "destinataire": 730574544821157988 + }, + { + "date": "04/05/2025 14:44:53", + "type": "Retrait", + "source": "Banque", + "montant": -5000.0, + "emetteur": 534003230435442690, + "destinataire": "730574544821157988" + }, + { + "date": "29/05/2025 13:58:09", + "type": "Virement reçu", + "source": "bitehoven", + "montant": 3000.0 + } + ] + }, + "1322531063012134943": { + "rib": "84826 38542 51049432190 83", + "solde": 2500.0, + "transactions": [] + }, + "906574786308734986": { + "rib": "25056 97158 55482257586 96", + "solde": 8000.0, + "transactions": [ + { + "date": "", + "type": "Dépôt", + "source": "Banque", + "montant": 10000.0, + "emetteur": 353942721997832195, + "destinataire": 906574786308734986 + } + ] + }, + "1203485889251115089": { + "rib": "68632 83140 20843525313 96", + "solde": 4675.0, + "transactions": [ + { + "date": "", + "type": "Dépôt", + "source": "Banque", + "montant": 1125.0, + "emetteur": 353942721997832195, + "destinataire": 1203485889251115089 + }, + { + "date": "08/06/2025 16:56:28", + "type": "Retrait", + "source": "Banque", + "montant": -100.0, + "emetteur": 906574786308734986, + "destinataire": "1203485889251115089" + }, + { + "date": "18/06/2025 17:47:09", + "type": "Virement reçu", + "source": "sz94d", + "montant": 350.0 + }, + { + "date": "18/06/2025 17:50:11", + "type": "Virement reçu", + "source": "sz94d", + "montant": 10.0 + }, + { + "date": "18/06/2025 17:51:22", + "type": "Virement reçu", + "source": "sz94d", + "montant": 290.0 + } + ] + }, + "1321933664535183395": { + "rib": "45478 99228 35351766504 94", + "solde": 5000.0, + "transactions": [] + }, + "1363308330721738842": { + "rib": "44601 40897 89067421979 42", + "solde": 3700.0, + "transactions": [ + { + "date": "", + "type": "Dépôt", + "source": "Banque", + "montant": 1750.0, + "emetteur": 353942721997832195, + "destinataire": 1363308330721738842 + }, + { + "date": "04/05/2025 13:16:52", + "type": "Virement", + "source": "Vers RIB 24193 46448 36423591130 82", + "montant": -5.0 + }, + { + "date": "10/05/2025 15:30:47", + "type": "Virement", + "source": "Vers RIB 80550 19549 91213737952 91", + "montant": -2509.0 + }, + { + "date": "11/05/2025 21:50:04", + "type": "Virement", + "source": "Vers RIB 24193 46448 36423591130 82", + "montant": -2000.0 + }, + { + "date": "17/05/2025 16:46:12", + "type": "Virement", + "source": "Vers RIB 55868 11389 89049343014 38", + "montant": -1300.0 + } + ] + }, + "1263573205268828231": { + "rib": "39039 90455 15160936704 26", + "solde": 1650.0, + "transactions": [ + { + "date": "28/04/2025 10:42:38", + "type": "Virement", + "source": "Vers RIB 54201 75079 71658 35659", + "montant": -380.0 + }, + { + "date": "01/05/2025 18:31:48", + "type": "Virement", + "source": "Vers RIB 24193 46448 36423591130 82", + "montant": -130.0 + }, + { + "date": "04/05/2025 14:47:21", + "type": "Dépôt", + "source": "Banque", + "montant": 20.0, + "emetteur": 534003230435442690, + "destinataire": "1263573205268828231" + }, + { + "date": "06/05/2025 10:49:55", + "type": "Virement", + "source": "Vers RIB 93884 11674 79519 40904", + "montant": -400.0 + }, + { + "date": "06/05/2025 18:19:03", + "type": "Virement", + "source": "Vers RIB 93884 11674 79519 40904", + "montant": -50.0 + }, + { + "date": "09/05/2025 18:31:46", + "type": "Virement", + "source": "Vers RIB 54201 75079 71658 35659", + "montant": -380.0 + }, + { + "date": "10/05/2025 15:11:48", + "type": "Retrait", + "source": "Banque", + "montant": -100.0, + "emetteur": 534003230435442690, + "destinataire": "1263573205268828231" + }, + { + "date": "11/05/2025 13:29:57", + "type": "Virement", + "source": "Vers RIB 87681 15340 34475481972 89", + "montant": -1000.0 + }, + { + "date": "11/05/2025 13:59:15", + "type": "Virement", + "source": "Vers RIB 24193 46448 36423591130 82", + "montant": -300.0 + }, + { + "date": "12/05/2025 14:43:08", + "type": "Virement reçu", + "source": "sz94d", + "montant": 1000.0 + }, + { + "date": "12/05/2025 18:35:06", + "type": "Virement", + "source": "Vers RIB 54201 75079 71658 35659", + "montant": -380.0 + }, + { + "date": "29/05/2025 15:03:17", + "type": "Retrait", + "source": "Banque", + "montant": -100.0, + "emetteur": 534003230435442690, + "destinataire": "1263573205268828231" + }, + { + "date": "25/06/2025 15:55:38", + "type": "Retrait", + "source": "Banque", + "montant": -1000.0, + "emetteur": 906574786308734986, + "destinataire": "1263573205268828231" + } + ] + }, + "1267066015238586452": { + "rib": "69911 89589 18696488896 93", + "solde": 2500.0, + "transactions": [] + }, + "1244182195300601920": { + "rib": "76375 45991 71507488931 20", + "solde": 18475.0, + "transactions": [ + { + "date": "", + "type": "Dépôt", + "source": "Banque", + "montant": 4000.0, + "emetteur": 534003230435442690, + "destinataire": 1244182195300601920 + }, + { + "date": "28/04/2025 15:37:27", + "type": "Virement", + "source": "Vers RIB 54201 75079 71658 35659", + "montant": -425.0 + }, + { + "date": "", + "type": "Dépôt", + "source": "Banque", + "montant": 10000.0, + "emetteur": 353942721997832195, + "destinataire": 1244182195300601920 + }, + { + "date": "01/05/2025 15:42:09", + "type": "Virement", + "source": "Vers RIB 24193 46448 36423591130 82", + "montant": -100.0 + }, + { + "date": "23/05/2025 18:34:21", + "type": "Dépôt", + "source": "Banque", + "montant": 7500.0, + "emetteur": 353942721997832195, + "destinataire": "1244182195300601920" + }, + { + "date": "23/05/2025 18:35:47", + "type": "Retrait", + "source": "Banque", + "montant": -2500.0, + "emetteur": 353942721997832195, + "destinataire": "1244182195300601920" + } + ] + }, + "1321863072242995332": { + "rib": "87681 15340 34475481972 89", + "solde": 0.0, + "transactions": [ + { + "date": "", + "type": "Dépôt", + "source": "Banque", + "montant": 5000.0, + "emetteur": 534003230435442690, + "destinataire": 1321863072242995332 + }, + { + "date": "26/04/2025 17:24:15", + "type": "Virement reçu", + "source": "nixfix06", + "montant": 0.1 + }, + { + "date": "26/04/2025 17:26:37", + "type": "Virement", + "source": "Vers RIB 10009 40554 56452790470 78", + "montant": -10.0 + }, + { + "date": "01/05/2025 13:08:59", + "type": "Virement", + "source": "Vers RIB 11940 73588 95258 04768", + "montant": -1500.0 + }, + { + "date": "09/05/2025 20:37:43", + "type": "Virement", + "source": "Vers RIB 24193 46448 36423591130 82", + "montant": -1400.0 + }, + { + "date": "09/05/2025 21:05:26", + "type": "Virement", + "source": "Vers RIB 24193 46448 36423591130 82", + "montant": -1.0 + }, + { + "date": "09/05/2025 21:06:02", + "type": "Virement", + "source": "Vers RIB 24193 46448 36423591130 82", + "montant": -699.0 + }, + { + "date": "11/05/2025 13:06:24", + "type": "Virement", + "source": "Vers RIB 11940 73588 95258 04768", + "montant": -1650.0 + }, + { + "date": "11/05/2025 13:29:57", + "type": "Virement reçu", + "source": "e.93130", + "montant": 1000.0 + }, + { + "date": "12/05/2025 14:43:08", + "type": "Virement", + "source": "Vers RIB 39039 90455 15160936704 26", + "montant": -1000.0 + }, + { + "date": "29/05/2025 15:01:57", + "type": "Retrait", + "source": "Banque", + "montant": -500.0, + "emetteur": 534003230435442690, + "destinataire": "1321863072242995332" + }, + { + "date": "18/06/2025 17:09:58", + "type": "Dépôt", + "source": "Banque", + "montant": 5000.0, + "emetteur": 534003230435442690, + "destinataire": "1321863072242995332" + }, + { + "date": "18/06/2025 17:47:09", + "type": "Virement", + "source": "Vers RIB 68632 83140 20843525313 96", + "montant": -350.0 + }, + { + "date": "18/06/2025 17:50:11", + "type": "Virement", + "source": "Vers RIB 68632 83140 20843525313 96", + "montant": -10.0 + }, + { + "date": "18/06/2025 17:51:22", + "type": "Virement", + "source": "Vers RIB 68632 83140 20843525313 96", + "montant": -290.0 + }, + { + "date": "20/06/2025 19:12:18", + "type": "Virement", + "source": "Vers RIB 80550 19549 91213737952 91", + "montant": -9350.0 + } + ] + }, + "1244372888702816458": { + "rib": "16619 49256 77035167664 43", + "solde": 0.0, + "transactions": [ + { + "date": "10/05/2025 16:58:38", + "type": "Virement reçu", + "source": "klan0x.", + "montant": 5000.0 + }, + { + "date": "18/06/2025 17:08:56", + "type": "Dépôt", + "source": "Banque", + "montant": 5000.0, + "emetteur": 534003230435442690, + "destinataire": "1244372888702816458" + }, + { + "date": "20/06/2025 19:06:14", + "type": "Virement", + "source": "Vers RIB 79307 68952 45977 60249", + "montant": -10000.0 + } + ] + }, + "768915179995004988": { + "rib": "96557 20123 28425623538 63", + "solde": 5000.0, + "transactions": [ + { + "date": "", + "type": "Dépôt", + "source": "Banque", + "montant": 5000.0, + "emetteur": 534003230435442690, + "destinataire": 768915179995004988 + } + ] + }, + "1203763950341333036": { + "rib": "75329 10724 41787874253 71", + "solde": 0.0, + "transactions": [] + }, + "1143294357613592636": { + "rib": "Non défini", + "solde": 1800.0, + "transactions": [ + { + "date": "", + "type": "Dépôt", + "source": "Banque", + "montant": 1800.0, + "emetteur": 353942721997832195, + "destinataire": 1143294357613592636 + } + ] + }, + "936337534760386580": { + "rib": "Non défini", + "solde": 1125.0, + "transactions": [ + { + "date": "", + "type": "Dépôt", + "source": "Banque", + "montant": 1125.0, + "emetteur": 353942721997832195, + "destinataire": 936337534760386580 + } + ] + }, + "599130976806764545": { + "rib": "97593 41945 69874546643 67", + "solde": 39986.0, + "transactions": [ + { + "date": "", + "type": "Dépôt", + "source": "Banque", + "montant": 30000.0, + "emetteur": 353942721997832195, + "destinataire": 599130976806764545 + }, + { + "date": "03/05/2025 17:12:58", + "type": "Virement", + "source": "Vers RIB 76644 32901 90493249064 16", + "montant": -30000.0 + }, + { + "date": "11/05/2025 13:26:45", + "type": "Virement", + "source": "Vers RIB 24193 46448 36423591130 82", + "montant": -14.0 + }, + { + "date": "11/05/2025 13:31:05", + "type": "Virement", + "source": "Vers RIB 24193 46448 36423591130 82", + "montant": -14000.0 + }, + { + "date": "23/05/2025 18:38:36", + "type": "Dépôt", + "source": "Banque", + "montant": 30000.0, + "emetteur": 353942721997832195, + "destinataire": "599130976806764545" + }, + { + "date": "31/05/2025 15:06:44", + "type": "Dépôt", + "source": "Banque", + "montant": 6000.0, + "emetteur": 599130976806764545, + "destinataire": "599130976806764545" + } + ] + }, + "727188532669448509": { + "rib": "87859 84337 20502661021 14", + "solde": 14900.0, + "transactions": [ + { + "date": "03/05/2025 18:05:28", + "type": "Dépôt", + "source": "Banque", + "montant": 5000.0, + "emetteur": 534003230435442690, + "destinataire": "727188532669448509" + }, + { + "date": "23/05/2025 18:40:52", + "type": "Dépôt", + "source": "Banque", + "montant": 6250.0, + "emetteur": 353942721997832195, + "destinataire": "727188532669448509" + }, + { + "date": "05/06/2025 20:45:52", + "type": "Dépôt", + "source": "Banque", + "montant": 3750.0, + "emetteur": 353942721997832195, + "destinataire": "727188532669448509" + }, + { + "date": "08/06/2025 16:19:25", + "type": "Retrait", + "source": "Banque", + "montant": -100.0, + "emetteur": 906574786308734986, + "destinataire": "727188532669448509" + }, + { + "date": "08/06/2025 16:46:43", + "type": "Dépôt", + "source": "Banque", + "montant": 4900.0, + "emetteur": 906574786308734986, + "destinataire": "727188532669448509" + }, + { + "date": "08/06/2025 16:47:10", + "type": "Retrait", + "source": "Banque", + "montant": -4900.0, + "emetteur": 906574786308734986, + "destinataire": "727188532669448509" + } + ] + }, + "1364012189052178533": { + "rib": "86715 65080 26418448685 60", + "solde": 0.0, + "transactions": [ + { + "date": "10/05/2025 15:10:23", + "type": "Dépôt", + "source": "Banque", + "montant": 5000.0, + "emetteur": 534003230435442690, + "destinataire": "1364012189052178533" + }, + { + "date": "10/05/2025 16:58:38", + "type": "Virement", + "source": "Vers RIB 16619 49256 77035167664 43", + "montant": -5000.0 + } + ] + }, + "561485865562406914": { + "rib": "79142 88647 23408115057 25", + "solde": 10000.0, + "transactions": [ + { + "date": "10/05/2025 16:13:36", + "type": "Retrait", + "source": "Banque", + "montant": -1000.0, + "emetteur": 534003230435442690, + "destinataire": "561485865562406914" + }, + { + "date": "23/05/2025 18:40:11", + "type": "Dépôt", + "source": "Banque", + "montant": 6000.0, + "emetteur": 353942721997832195, + "destinataire": "561485865562406914" + } + ] + }, + "826145675506745395": { + "rib": "30204 64719 14505846550 36", + "solde": 5000.0, + "transactions": [] + }, + "1347013326453870656": { + "rib": "99907 94138 56638773514 98", + "solde": 5000.0, + "transactions": [] + }, + "1350161151539024007": { + "rib": "81269 87978 38399849321 94", + "solde": 0.0, + "transactions": [] + }, + "977949307623850135": { + "rib": "14755 88795 24131636074 92", + "solde": 4250.0, + "transactions": [ + { + "date": "08/06/2025 16:48:40", + "type": "Dépôt", + "source": "Banque", + "montant": 4900.0, + "emetteur": 906574786308734986, + "destinataire": "977949307623850135" + }, + { + "date": "08/06/2025 16:53:51", + "type": "Retrait", + "source": "Banque", + "montant": -350.0, + "emetteur": 906574786308734986, + "destinataire": "977949307623850135" + } + ] + }, + "1312077385780301865": { + "rib": "94450 86334 91357304463 40", + "solde": 4600.0, + "transactions": [ + { + "date": "08/06/2025 16:58:39", + "type": "Dépôt", + "source": "Banque", + "montant": 4900.0, + "emetteur": 906574786308734986, + "destinataire": "1312077385780301865" + }, + { + "date": "08/06/2025 17:01:12", + "type": "Retrait", + "source": "Banque", + "montant": -300.0, + "emetteur": 906574786308734986, + "destinataire": "1312077385780301865" + } + ] + }, + "1351650470392893512": { + "rib": "32259 29619 24154724316 64", + "solde": 5000.0, + "transactions": [ + { + "date": "18/06/2025 16:39:30", + "type": "Dépôt", + "source": "Banque", + "montant": 5000.0, + "emetteur": 534003230435442690, + "destinataire": "1351650470392893512" + } + ] + }, + "897082541033545739": { + "rib": "82618 14302 76335397919 55", + "solde": 5500.0, + "transactions": [] + }, + "1217095995804876950": { + "rib": "22882 95249 65296295159 62", + "solde": 4900.0, + "transactions": [] + }, + "690202239536857174": { + "rib": "30454 15704 84863297282 13", + "solde": 2500.0, + "transactions": [ + { + "date": "25/06/2025 15:48:03", + "type": "Dépôt", + "source": "Banque", + "montant": 5000.0, + "emetteur": 906574786308734986, + "destinataire": "690202239536857174" + }, + { + "date": "25/06/2025 15:50:32", + "type": "Retrait", + "source": "Banque", + "montant": -2500.0, + "emetteur": 906574786308734986, + "destinataire": "690202239536857174" + } + ] + }, + "1290382773013577802": { + "rib": "32052 42311 92980767491 29", + "solde": 2500.0, + "transactions": [ + { + "date": "25/06/2025 15:48:23", + "type": "Dépôt", + "source": "Banque", + "montant": 5000.0, + "emetteur": 906574786308734986, + "destinataire": "1290382773013577802" + }, + { + "date": "25/06/2025 15:53:41", + "type": "Retrait", + "source": "Banque", + "montant": -2500.0, + "emetteur": 906574786308734986, + "destinataire": "1290382773013577802" + } + ] + } + }, + "Victoria's Credit Bank": { + "353942721997832195": { + "rib": "97593 41945 69675546643 66", + "solde": 199006.0, + "transactions": [ + { + "date": "21/04/2025 14:05:03", + "type": "Virement reçu", + "source": "gameurpro12", + "montant": 1.0 + }, + { + "date": "26/04/2025 14:03:26", + "type": "Virement reçu", + "source": "mg_studios_", + "montant": 3400.0 + }, + { + "date": "26/04/2025 14:03:32", + "type": "Virement reçu", + "source": "mg_studios_", + "montant": 100.0 + }, + { + "date": "23/05/2025 20:04:57", + "type": "Virement", + "source": "Vers RIB 36495 80083 39542816185 27", + "montant": -3501.0 + }, + { + "date": "23/05/2025 20:05:57", + "type": "Dépôt", + "source": "Banque", + "montant": 5000.0, + "emetteur": 599130976806764545, + "destinataire": "353942721997832195" + }, + { + "date": "29/05/2025 15:16:42", + "type": "Virement", + "source": "Vers RIB 19440 99822 26785 73807", + "montant": -135.0 + }, + { + "date": "05/06/2025 15:33:55", + "type": "Virement reçu", + "source": "mg_studios_", + "montant": 72851.0 + }, + { + "date": "05/06/2025 20:49:10", + "type": "Virement reçu", + "source": "mg_studios_", + "montant": 13500.0 + }, + { + "date": "06/06/2025 20:09:01", + "type": "Virement", + "source": "Vers RIB 96241 34733 44548 77079", + "montant": -210.0 + }, + { + "date": "22/06/2025 14:07:23", + "type": "Dépôt", + "source": "Banque", + "montant": 108000.0, + "emetteur": 353942721997832195, + "destinataire": "353942721997832195" + } + ] + }, + "942073262333112351": { + "rib": "Non défini", + "solde": 5000.0, + "transactions": [ + { + "date": "", + "type": "Dépôt", + "source": "Banque", + "montant": 5000.0, + "emetteur": 353942721997832195, + "destinataire": 942073262333112351 + } + ] + }, + "971446412690722826": { + "rib": "66427 49104 57218570654 78", + "solde": 0.0, + "transactions": [ + { + "date": "", + "type": "Dépôt", + "source": "Banque", + "montant": 1.0, + "emetteur": 353942721997832195, + "destinataire": 971446412690722826 + }, + { + "date": "21/04/2025 14:05:03", + "type": "Virement", + "source": "Vers RIB 97593 41945 69675546643 66", + "montant": -1.0 + } + ] + }, + "1244372888702816458": { + "rib": "80550 19549 91213737952 91", + "solde": 16859.0, + "transactions": [ + { + "date": "10/05/2025 15:29:00", + "type": "Dépôt", + "source": "Banque", + "montant": 5000.0, + "emetteur": 1244372888702816458, + "destinataire": "1244372888702816458" + }, + { + "date": "10/05/2025 15:30:47", + "type": "Virement reçu", + "source": "leandro_6548.", + "montant": 2509.0 + }, + { + "date": "20/06/2025 19:12:18", + "type": "Virement reçu", + "source": "sz94d", + "montant": 9350.0 + } + ] + }, + "1200313720195264613": { + "rib": "24756 54403 68357307735 95", + "solde": 11220.0, + "transactions": [ + { + "date": "23/05/2025 20:12:38", + "type": "Dépôt", + "source": "Banque", + "montant": 5000.0, + "emetteur": 599130976806764545, + "destinataire": "1200313720195264613" + }, + { + "date": "03/06/2025 17:38:48", + "type": "Virement", + "source": "Vers RIB 24756 54403 68357307735 95", + "montant": -2220.0 + }, + { + "date": "03/06/2025 17:38:48", + "type": "Virement reçu", + "source": "agent_chaos_officiel", + "montant": 2220.0 + }, + { + "date": "03/06/2025 17:39:36", + "type": "Virement reçu", + "source": "agent_chaos_officiel", + "montant": 2220.0 + }, + { + "date": "25/06/2025 16:36:41", + "type": "Dépôt", + "source": "Banque", + "montant": 4000.0, + "emetteur": 599130976806764545, + "destinataire": "1200313720195264613" + } + ] + }, + "930211793043873812": { + "rib": "12926 69165 46900233170 18", + "solde": 5000.0, + "transactions": [ + { + "date": "29/05/2025 15:11:37", + "type": "Dépôt", + "source": "Banque", + "montant": 5000.0, + "emetteur": 599130976806764545, + "destinataire": "930211793043873812" + } + ] + }, + "814537316928192612": { + "rib": "21023 25426 38540910521 82", + "solde": 5000.0, + "transactions": [ + { + "date": "29/05/2025 15:15:42", + "type": "Dépôt", + "source": "Banque", + "montant": 5000.0, + "emetteur": 599130976806764545, + "destinataire": "814537316928192612" + } + ] + }, + "599130976806764545": { + "rib": "33029 91143 11912574367 76", + "solde": 35000.0, + "transactions": [ + { + "date": "25/06/2025 16:23:04", + "type": "Dépôt", + "source": "Banque", + "montant": 35000.0, + "emetteur": 599130976806764545, + "destinataire": "599130976806764545" + } + ] + }, + "1243889299615776858": { + "rib": "26592 50802 82203454342 14", + "solde": 5000.0, + "transactions": [ + { + "date": "31/05/2025 15:27:01", + "type": "Dépôt", + "source": "Banque", + "montant": 5000.0, + "emetteur": 599130976806764545, + "destinataire": "1243889299615776858" + } + ] + }, + "1000680453025562654": { + "rib": "70129 70587 44243598314 97", + "solde": 5000.0, + "transactions": [ + { + "date": "08/06/2025 15:45:03", + "type": "Dépôt", + "source": "Banque", + "montant": 5000.0, + "emetteur": 599130976806764545, + "destinataire": "1000680453025562654" + } + ] + }, + "977949307623850135": { + "rib": "25970 14570 53378914845 57", + "solde": 0.0, + "transactions": [] + }, + "991781071085633586": { + "rib": "73275 97402 32123800084 66", + "solde": 0.0, + "transactions": [] + }, + "478564181356838922": { + "rib": "75866 83992 30954667601 95", + "solde": 5000.0, + "transactions": [ + { + "date": "09/06/2025 13:36:39", + "type": "Dépôt", + "source": "Banque", + "montant": 5000.0, + "emetteur": 599130976806764545, + "destinataire": "478564181356838922" + } + ] + }, + "1341203038609739889": { + "rib": "99281 88462 50557875393 60", + "solde": 0.0, + "transactions": [] + } + } +} \ No newline at end of file diff --git a/comptes_entreprises.json b/comptes_entreprises.json new file mode 100644 index 0000000..4f16f50 --- /dev/null +++ b/comptes_entreprises.json @@ -0,0 +1,492 @@ +{ + "ngc_bank": {}, + "Gouvernement": { + "GOUV": { + "rib": "00000 00000 00000000000 00", + "solde": 10085849.01, + "transactions": [ + { + "date": "26/04/2025 15:55:13", + "type": "TVA reçue", + "source": "534003230435442690", + "montant": 0.2 + }, + { + "date": "27/04/2025 14:01:32", + "type": "TVA reçue", + "source": "534003230435442690", + "montant": 0.2 + }, + { + "date": "28/04/2025 08:53:44", + "type": "TVA reçue", + "source": "1108087282692522105", + "montant": 8.5 + }, + { + "date": "28/04/2025 10:42:38", + "type": "TVA reçue", + "source": "1263573205268828231", + "montant": 7.6 + }, + { + "date": "28/04/2025 15:37:27", + "type": "TVA reçue", + "source": "1244182195300601920", + "montant": 8.5 + }, + { + "date": "01/05/2025 08:56:08", + "type": "TVA reçue", + "source": "914118793918312480", + "montant": 5.2 + }, + { + "date": "01/05/2025 13:08:59", + "type": "TVA reçue", + "source": "1321863072242995332", + "montant": 300.0 + }, + { + "date": "04/05/2025 16:51:20", + "type": "TVA reçue", + "source": "971446412690722826", + "montant": 0.01 + }, + { + "date": "05/05/2025 16:09:39", + "type": "TVA reçue", + "source": "353942721997832195", + "montant": 400.0 + }, + { + "date": "05/05/2025 16:23:40", + "type": "TVA reçue", + "source": "828239898975666216", + "montant": 10.0 + }, + { + "date": "05/05/2025 16:24:07", + "type": "TVA reçue", + "source": "534003230435442690", + "montant": 100.0 + }, + { + "date": "06/05/2025 10:49:55", + "type": "TVA reçue", + "source": "1263573205268828231", + "montant": 8.0 + }, + { + "date": "06/05/2025 18:19:03", + "type": "TVA reçue", + "source": "1263573205268828231", + "montant": 1.0 + }, + { + "date": "07/05/2025 19:45:13", + "type": "Virement reçu", + "source": "gameurpro12", + "montant": 1.0 + }, + { + "date": "09/05/2025 17:23:13", + "type": "TVA reçue", + "source": "1200313720195264613", + "montant": 7.8 + }, + { + "date": "09/05/2025 18:31:46", + "type": "TVA reçue", + "source": "1263573205268828231", + "montant": 7.6 + }, + { + "date": "09/05/2025 19:10:15", + "type": "TVA reçue", + "source": "534003230435442690", + "montant": 100.0 + }, + { + "date": "09/05/2025 19:15:25", + "type": "TVA reçue", + "source": "534003230435442690", + "montant": 100.0 + }, + { + "date": "11/05/2025 13:06:24", + "type": "TVA reçue", + "source": "1321863072242995332", + "montant": 330.0 + }, + { + "date": "11/05/2025 14:49:46", + "type": "TVA reçue", + "source": "1072112886509473792", + "montant": 14000.0 + }, + { + "date": "11/05/2025 14:50:02", + "type": "TVA reçue", + "source": "501080933282218005", + "montant": 2400.0 + }, + { + "date": "12/05/2025 18:35:06", + "type": "TVA reçue", + "source": "1263573205268828231", + "montant": 7.6 + }, + { + "date": "18/05/2025 15:17:43", + "type": "TVA reçue", + "source": "1126995714552111215", + "montant": 2400.0 + }, + { + "date": "18/05/2025 15:34:58", + "type": "TVA reçue", + "source": "376796341071773706", + "montant": 2400.0 + }, + { + "date": "18/05/2025 15:34:58", + "type": "TVA reçue", + "source": "376796341071773706", + "montant": 2400.0 + }, + { + "date": "19/05/2025 11:55:01", + "type": "TVA reçue", + "source": "1200313720195264613", + "montant": 7.8 + }, + { + "date": "29/05/2025 13:51:53", + "type": "TVA reçue", + "source": "376796341071773706", + "montant": 6000.0 + }, + { + "date": "29/05/2025 15:16:42", + "type": "TVA reçue", + "source": "353942721997832195", + "montant": 27.0 + }, + { + "date": "06/06/2025 20:09:01", + "type": "TVA reçue", + "source": "353942721997832195", + "montant": 42.0 + }, + { + "date": "20/06/2025 19:06:14", + "type": "TVA reçue", + "source": "1244372888702816458", + "montant": 2000.0 + }, + { + "date": "29/06/2025 12:31:17", + "type": "Dépôt", + "source": "Banque", + "montant": 15869.0, + "emetteur": 353942721997832195, + "destinataire": "GOUV" + } + ] + } + }, + "NGC Bank": { + "Journal en onde": { + "rib": "93884 11674 79519 40904", + "solde": 6031.0, + "mdp": "E96zM0Jj", + "tva": 2, + "transactions": [ + { + "date": "05/05/2025 16:23:40", + "type": "Virement reçu", + "source": "docteur_w.d_axolotl", + "montant": 490.0 + }, + { + "date": "05/05/2025 16:24:07", + "type": "Dépôt", + "source": "Banque", + "montant": 4900.0, + "emetteur": 534003230435442690, + "destinataire": "Journal en onde" + }, + { + "date": "06/05/2025 10:49:55", + "type": "Virement reçu", + "source": "e.93130", + "montant": 392.0 + }, + { + "date": "06/05/2025 18:19:03", + "type": "Virement reçu", + "source": "e.93130", + "montant": 49.0 + } + ] + }, + "Milano Motors": { + "rib": "79307 68952 45977 60249", + "solde": 126400.0, + "mdp": "9YcjNvud", + "tva": 20, + "transactions": [ + { + "date": "11/05/2025 14:49:46", + "type": "Virement reçu", + "source": "lagrenouillebeka", + "montant": 56000.0 + }, + { + "date": "11/05/2025 14:50:02", + "type": "Virement reçu", + "source": "justeme", + "montant": 9600.0 + }, + { + "date": "18/05/2025 15:17:43", + "type": "Virement reçu", + "source": "jl_wesley.f1", + "montant": 9600.0 + }, + { + "date": "18/05/2025 15:34:58", + "type": "Virement reçu", + "source": "bitehoven", + "montant": 9600.0 + }, + { + "date": "18/05/2025 15:34:58", + "type": "Virement reçu", + "source": "bitehoven", + "montant": 9600.0 + }, + { + "date": "29/05/2025 13:51:53", + "type": "Virement reçu", + "source": "bitehoven", + "montant": 24000.0 + }, + { + "date": "20/06/2025 19:06:14", + "type": "Virement reçu", + "source": "oghacker1337", + "montant": 8000.0 + } + ] + }, + "Sicilia mia": { + "rib": "19440 99822 26785 73807", + "solde": 108.0, + "mdp": "8sqmuoz4", + "tva": 20, + "transactions": [ + { + "date": "29/05/2025 15:16:42", + "type": "Virement reçu", + "source": "mg_studios_", + "montant": 108.0 + } + ] + }, + "Domaine de vins": { + "rib": "96241 34733 44548 77079", + "solde": 1768.0, + "mdp": "hIJj81ci", + "tva": 20, + "transactions": [ + { + "date": "05/05/2025 16:09:39", + "type": "Virement reçu", + "source": "mg_studios_", + "montant": 1600.0 + }, + { + "date": "06/06/2025 20:09:01", + "type": "Virement reçu", + "source": "mg_studios_", + "montant": 168.0 + } + ] + }, + "Umbrella Assurances": { + "rib": "54201 75079 71658 35659", + "solde": 2969.4, + "mdp": "KDRQGd3R", + "tva": 2, + "transactions": [ + { + "date": "28/04/2025 08:53:44", + "type": "Virement reçu", + "source": "tricks212_zz", + "montant": 416.5 + }, + { + "date": "28/04/2025 10:42:38", + "type": "Virement reçu", + "source": "e.93130", + "montant": 372.4 + }, + { + "date": "28/04/2025 15:37:27", + "type": "Virement reçu", + "source": "hvk_pompotfraicz", + "montant": 416.5 + }, + { + "date": "01/05/2025 08:56:08", + "type": "Virement reçu", + "source": "neyzizz", + "montant": 254.8 + }, + { + "date": "09/05/2025 17:23:13", + "type": "Virement reçu", + "source": "agent_chaos_officiel", + "montant": 382.2 + }, + { + "date": "09/05/2025 18:31:46", + "type": "Virement reçu", + "source": "e.93130", + "montant": 372.4 + }, + { + "date": "12/05/2025 18:35:06", + "type": "Virement reçu", + "source": "e.93130", + "montant": 372.4 + }, + { + "date": "19/05/2025 11:55:01", + "type": "Virement reçu", + "source": "agent_chaos_officiel", + "montant": 382.2 + } + ] + }, + "Auto école": { + "rib": "11940 73588 95258 04768", + "solde": 15971.0, + "mdp": "zcEKe45Y", + "tva": 20, + "transactions": [ + { + "date": "26/04/2025 15:55:13", + "type": "Virement reçu", + "source": "nixfix06", + "montant": 0.8 + }, + { + "date": "27/04/2025 14:01:32", + "type": "Virement reçu", + "source": "nixfix06", + "montant": 0.8 + }, + { + "date": "01/05/2025 13:08:59", + "type": "Virement reçu", + "source": "sz94d", + "montant": 1200.0 + }, + { + "date": "11/05/2025 13:06:24", + "type": "Virement reçu", + "source": "sz94d", + "montant": 1320.0 + } + ] + }, + "Sicilia Mia": { + "rib": "68229 13765 90163 35585", + "solde": 1907.09, + "mdp": "6uTORm3r", + "tva": 10, + "transactions": [ + { + "date": "04/05/2025 16:51:20", + "type": "Virement reçu", + "source": "gameurpro12", + "montant": 0.09000000000000001 + } + ] + }, + "NGC Bank": { + "rib": "15156 38436 35920 54357", + "solde": 1040900.0, + "mdp": "nxDcskg0", + "tva": 2, + "transactions": [ + { + "date": "09/05/2025 19:10:15", + "type": "Dépôt", + "source": "Banque", + "montant": 4900.0, + "emetteur": 534003230435442690, + "destinataire": "NGC Bank" + }, + { + "date": "09/05/2025 19:15:25", + "type": "Dépôt", + "source": "Banque", + "montant": 4900.0, + "emetteur": 534003230435442690, + "destinataire": "NGC Bank" + }, + { + "date": "09/05/2025 19:15:51", + "type": "Retrait", + "source": "Banque", + "montant": -4900.0, + "emetteur": 534003230435442690, + "destinataire": "NGC Bank" + }, + { + "date": "24/05/2025 13:44:09", + "type": "Dépôt", + "source": "Banque", + "montant": 1000000.0, + "emetteur": 534003230435442690, + "destinataire": "NGC Bank" + }, + { + "date": "04/06/2025 16:30:19", + "type": "Dépôt", + "source": "Banque", + "montant": 36000.0, + "emetteur": 534003230435442690, + "destinataire": "NGC Bank" + } + ] + }, + "Agence immobilière.": { + "rib": "54044 32109 51269 69180", + "solde": 108000.0, + "mdp": "tCH7QK7G", + "tva": 20, + "transactions": [ + { + "date": "03/07/2025 22:35:20", + "type": "Dépôt", + "source": "Banque", + "montant": 108000.0, + "emetteur": 353942721997832195, + "destinataire": "Agence immobilière." + } + ] + }, + "La Concordia": { + "rib": "09860 71327 12935 47361", + "solde": 0, + "mdp": "3vo66Wqu", + "tva": 10, + "transactions": [] + } + } +} \ No newline at end of file diff --git a/counting_data.json b/counting_data.json new file mode 100644 index 0000000..4055ae1 --- /dev/null +++ b/counting_data.json @@ -0,0 +1,6 @@ +{ + "1390076581892063354": { + "current": 4, + "last_user": 1200313720195264613 + } +} \ No newline at end of file diff --git a/id_compteur.json b/id_compteur.json new file mode 100644 index 0000000..0774e47 --- /dev/null +++ b/id_compteur.json @@ -0,0 +1,3 @@ +{ + "dernier_id": 152 +} \ No newline at end of file diff --git a/ressources/assets/CIITAL.png b/ressources/assets/CIITAL.png new file mode 100644 index 0000000..c19bcf9 Binary files /dev/null and b/ressources/assets/CIITAL.png differ diff --git a/ressources/assets/Code_HTML_Vice_TMV2.html b/ressources/assets/Code_HTML_Vice_TMV2.html new file mode 100644 index 0000000..2a91cc8 --- /dev/null +++ b/ressources/assets/Code_HTML_Vice_TMV2.html @@ -0,0 +1,174 @@ +
+
+
+
+
+
+
+
Utilisateur
+
+
+
+
+
+
+
+
+
+
Employer :
+
+
+
+
+
Nom du joueur
+
+
+
+
+
+
Nom du joueur
+
+
+
+
+
+
Nom du joueur
+
+
+
+
+
+
Nom du joueur
+
+
+
+
+
Suggestion :
+
+
+
+
+
+
+
+
+
Nom de l’entreprise
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Information :
+
+
+
+
+
+
+
+
+
+
+
Victoria’s Vice™
+
+
+
+
+
+
+
+
+
+
Utilisateur
+
+
+
+
+
+
+
+
+
+
Employer :
+
+
+
+
+
Nom du joueur
+
+
+
+
+
+
Nom du joueur
+
+
+
+
+
+
Nom du joueur
+
+
+
+
+
+
Nom du joueur
+
+
+
+
+
Suggestion :
+
+
+
+
+
+
+
+
+
Nom de l’entreprise
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Information :
+
+
+
+
+
+
+
+
+
+
+
Victoria’s Vice™
+
+
diff --git a/ressources/assets/Victorias_ViceTMV2.png b/ressources/assets/Victorias_ViceTMV2.png new file mode 100644 index 0000000..d491adb Binary files /dev/null and b/ressources/assets/Victorias_ViceTMV2.png differ diff --git a/ressources/assets/template.png b/ressources/assets/template.png new file mode 100644 index 0000000..5b24bc0 Binary files /dev/null and b/ressources/assets/template.png differ diff --git a/ressources/banque_data.json b/ressources/banque_data.json new file mode 100644 index 0000000..0967ef4 --- /dev/null +++ b/ressources/banque_data.json @@ -0,0 +1 @@ +{} diff --git a/ressources/data.json b/ressources/data.json new file mode 100644 index 0000000..6f60fb7 --- /dev/null +++ b/ressources/data.json @@ -0,0 +1,500 @@ +{ + "identities": { + "353942721997832195": { + "nom": "RICCI", + "prenom": "Aldo", + "date_naissance": "22/05/1967", + "lieu_naissance": "Victoria's Vice", + "sexe": "Homme", + "date_expiration": "01/12/2026" + }, + "1096453463849828362": { + "nom": "DE MONTROSE", + "prenom": "Warren", + "date_naissance": "04/04/1991", + "lieu_naissance": "Londres", + "sexe": "Homme", + "date_expiration": "20/06/2027" + }, + "914118793918312480": { + "nom": "CORREA", + "prenom": "Mattia", + "date_naissance": "28/04/1986", + "lieu_naissance": "Milan", + "sexe": "Homme", + "date_expiration": "01/12/2026" + }, + "768915179995004988": { + "nom": "KASHMIRI", + "prenom": "Khalid", + "date_naissance": "19/07/1988", + "lieu_naissance": "Algérie", + "sexe": "Homme", + "date_expiration": "13/04/2027" + }, + "828923448453103637": { + "nom": "NORLOCK", + "prenom": "Bucky", + "date_naissance": "04/06/1999", + "lieu_naissance": "Venise", + "sexe": "Homme", + "date_expiration": "24/06/2027" + }, + "971446412690722826": { + "nom": "PAS GENTIL", + "prenom": "Pas gentil", + "date_naissance": "04/04/2025", + "lieu_naissance": "Pas Gentil", + "sexe": "Autre", + "date_expiration": "11/04/2027" + }, + "1324439906046574693": { + "nom": "DZQDQDZ", + "prenom": "Dzqdqzdz", + "date_naissance": "01/01/2000", + "lieu_naissance": "dzqd", + "sexe": "Femme", + "date_expiration": "12/03/2027" + }, + "1200313720195264613": { + "nom": "HAMILTON", + "prenom": "Leonardo", + "date_naissance": "09/01/1985", + "lieu_naissance": "Canada Québec", + "sexe": "Homme", + "date_expiration": "01/05/2027" + }, + "942073262333112351": { + "nom": "MURDOCK", + "prenom": "Matthew", + "date_naissance": "12/03/1990", + "lieu_naissance": "Hells Kitchen", + "sexe": "Homme", + "date_expiration": "11/04/2027" + }, + "828239898975666216": { + "nom": "VINDERTECH", + "prenom": "Félix", + "date_naissance": "11/05/1990", + "lieu_naissance": "Paris", + "sexe": "Homme", + "date_expiration": "11/04/2027" + }, + "836606840254496788": { + "nom": "MARECHAL", + "prenom": "Charles", + "date_naissance": "08/01/1995", + "lieu_naissance": "Paris", + "sexe": "Homme", + "date_expiration": "11/04/2027" + }, + "1126995714552111215": { + "nom": "FIGLINI", + "prenom": "José", + "date_naissance": "28/12/2001", + "lieu_naissance": "Victoria's Vice", + "sexe": "Homme", + "date_expiration": "11/04/2027" + }, + "1108087282692522105": { + "nom": "JULES", + "prenom": "Delcourt", + "date_naissance": "18/04/2004", + "lieu_naissance": "Paris", + "sexe": "Homme", + "date_expiration": "25/06/2027" + }, + "1351957361333370932": { + "nom": "DURAND", + "prenom": "Mehdi", + "date_naissance": "20/11/2002", + "lieu_naissance": "Paris", + "sexe": "Homme", + "date_expiration": "13/04/2027" + }, + "936337534760386580": { + "nom": "HAKERMAN", + "prenom": "Malo", + "date_naissance": "25/11/2000", + "lieu_naissance": "Paris", + "sexe": "Homme", + "date_expiration": "13/04/2027" + }, + "449424004663607307": { + "nom": "DOLOS", + "prenom": "Arturo", + "date_naissance": "01/04/2001", + "lieu_naissance": "Salina", + "sexe": "Homme", + "date_expiration": "13/04/2027" + }, + "881841899634323456": { + "nom": "LAVIBE", + "prenom": "Rachid", + "date_naissance": "28/03/2001", + "lieu_naissance": "Portugal", + "sexe": "Homme", + "date_expiration": "13/04/2027" + }, + "946068504023556186": { + "nom": "MARSTON", + "prenom": "John", + "date_naissance": "09/10/1974", + "lieu_naissance": "Montreal", + "sexe": "Homme", + "date_expiration": "13/04/2027" + }, + "1203485889251115089": { + "nom": "CARTER", + "prenom": "Ethan", + "date_naissance": "16/11/2000", + "lieu_naissance": "Midnight city", + "sexe": "Homme", + "date_expiration": "13/04/2027" + }, + "1324527110425018438": { + "nom": "DEMARCHI", + "prenom": "Stella", + "date_naissance": "18/08/2001", + "lieu_naissance": "Milan", + "sexe": "Femme", + "date_expiration": "13/04/2027" + }, + "901856948184559676": { + "nom": "PILISO", + "prenom": "Dario", + "date_naissance": "25/02/1980", + "lieu_naissance": "Milan", + "sexe": "Homme", + "date_expiration": "13/04/2027" + }, + "906574786308734986": { + "nom": "FERRARI", + "prenom": "Pedro", + "date_naissance": "28/02/2000", + "lieu_naissance": "Rome", + "sexe": "Homme", + "date_expiration": "10/05/2027" + }, + "1311384140905709578": { + "nom": "LOPEZ", + "prenom": "Kayz", + "date_naissance": "04/07/2000", + "lieu_naissance": "Venise", + "sexe": "Homme", + "date_expiration": "22/06/2027" + }, + "501080933282218005": { + "nom": "GIORGINO", + "prenom": "Antoine", + "date_naissance": "17/06/2004", + "lieu_naissance": "Paris", + "sexe": "Homme", + "date_expiration": "21/04/2027" + }, + "826145675506745395": { + "nom": "GALLERY", + "prenom": "Valentino", + "date_naissance": "19/06/1994", + "lieu_naissance": "Nice", + "sexe": "Homme", + "date_expiration": "21/04/2027" + }, + "534003230435442690": { + "nom": "DI REGIA", + "prenom": "Salvatore", + "date_naissance": "09/11/1990", + "lieu_naissance": "Rome", + "sexe": "Homme", + "date_expiration": "21/04/2027" + }, + "1304410873015832596": { + "nom": "EL MASRI", + "prenom": "Rayan", + "date_naissance": "21/10/1999", + "lieu_naissance": "Algerie", + "sexe": "Homme", + "date_expiration": "25/04/2027" + }, + "1321933664535183395": { + "nom": "MICKALSON", + "prenom": "Jackson", + "date_naissance": "08/02/1995", + "lieu_naissance": "Paris", + "sexe": "Homme", + "date_expiration": "25/04/2027" + }, + "1267066015238586452": { + "nom": "BLOSSOM", + "prenom": "Kira", + "date_naissance": "23/02/2003", + "lieu_naissance": "Miami", + "sexe": "Homme", + "date_expiration": "25/04/2027" + }, + "1263573205268828231": { + "nom": "FERNANDEZ", + "prenom": "Mattéo", + "date_naissance": "07/05/1999", + "lieu_naissance": "Mexique", + "sexe": "Homme", + "date_expiration": "25/04/2027" + }, + "730574544821157988": { + "nom": "ZENTORNO", + "prenom": "Neiz", + "date_naissance": "10/06/2000", + "lieu_naissance": "Algérie", + "sexe": "Homme", + "date_expiration": "25/04/2027" + }, + "1244372888702816458": { + "nom": "LOPEZ", + "prenom": "Oussama", + "date_naissance": "11/07/2002", + "lieu_naissance": "chatelet les halles", + "sexe": "Homme", + "date_expiration": "27/04/2027" + }, + "1321863072242995332": { + "nom": "RAMINEZ", + "prenom": "Rios", + "date_naissance": "10/02/2004", + "lieu_naissance": "Rome", + "sexe": "Homme", + "date_expiration": "09/06/2027" + }, + "1363308330721738842": { + "nom": "BELFORD", + "prenom": "Jordan", + "date_naissance": "13/11/2004", + "lieu_naissance": "Manhattan New York", + "sexe": "Homme", + "date_expiration": "16/05/2027" + }, + "599130976806764545": { + "nom": "HAMILTON", + "prenom": "William", + "date_naissance": "23/08/1977", + "lieu_naissance": "Québec", + "sexe": "Homme", + "date_expiration": "01/05/2027" + }, + "1268584648611729418": { + "nom": "VINCE", + "prenom": "Kyle", + "date_naissance": "08/07/2000", + "lieu_naissance": "Paris", + "sexe": "Homme", + "date_expiration": "03/05/2027" + }, + "727188532669448509": { + "nom": "LEROY", + "prenom": "Mady", + "date_naissance": "19/03/1997", + "lieu_naissance": "montreal", + "sexe": "Femme", + "date_expiration": "08/05/2027" + }, + "1322531063012134943": { + "nom": "REVERSE", + "prenom": "Stanle", + "date_naissance": "07/10/2002", + "lieu_naissance": "Los Angeles", + "sexe": "Homme", + "date_expiration": "03/05/2027" + }, + "1361493382911758528": { + "nom": "AMARA", + "prenom": "Madja", + "date_naissance": "12/06/2000", + "lieu_naissance": "Annaba", + "sexe": "Femme", + "date_expiration": "10/05/2027" + }, + "561485865562406914": { + "nom": "ASVALT", + "prenom": "Harmonium", + "date_naissance": "01/01/2000", + "lieu_naissance": "Luxembourg", + "sexe": "Homme", + "date_expiration": "10/05/2027" + }, + "1282306873563021357": { + "nom": "GRIMM", + "prenom": "Djelys", + "date_naissance": "25/09/1999", + "lieu_naissance": "Paris", + "sexe": "Homme", + "date_expiration": "10/05/2027" + }, + "1238099342288752661": { + "nom": "MAJO GARO", + "prenom": "Wireless", + "date_naissance": "1984/10/28", + "lieu_naissance": "Mexique", + "sexe": "Homme", + "date_expiration": "17/05/2027" + }, + "1058418257142501417": { + "nom": "LOUÏE", + "prenom": "Jean", + "date_naissance": "02/07/2005", + "lieu_naissance": "Toulouse", + "sexe": "Homme", + "date_expiration": "29/05/2027" + }, + "1351821260140707862": { + "nom": "RICCI", + "prenom": "Antonio", + "date_naissance": "30/03/2000", + "lieu_naissance": "Rome", + "sexe": "Homme", + "date_expiration": "07/06/2027" + }, + "558327236378361857": { + "nom": "MUSULIN", + "prenom": "Tony", + "date_naissance": "15/11/1987", + "lieu_naissance": "New York", + "sexe": "Homme", + "date_expiration": "07/06/2027" + }, + "1364012189052178533": { + "nom": "LORAS", + "prenom": "Philippe", + "date_naissance": "06/03/2000", + "lieu_naissance": "Kinshasa", + "sexe": "Homme", + "date_expiration": "20/06/2027" + }, + "1350161151539024007": { + "nom": "PILISO", + "prenom": "Antonio", + "date_naissance": "30/11/2006", + "lieu_naissance": "Bogotta", + "sexe": "Homme", + "date_expiration": "07/06/2027" + }, + "1312077385780301865": { + "nom": "LONDUHAUT", + "prenom": "Yanis", + "date_naissance": "07/03/2001", + "lieu_naissance": "New York", + "sexe": "Homme", + "date_expiration": "09/06/2027" + }, + "1217095995804876950": { + "nom": "SMITH", + "prenom": "Franklin", + "date_naissance": "11/08/2004", + "lieu_naissance": "Los Angeles", + "sexe": "Homme", + "date_expiration": "22/06/2027" + }, + "376796341071773706": { + "nom": "FRICADELLO", + "prenom": "Niccolo", + "date_naissance": "24/11/1987", + "lieu_naissance": "Chihuahua", + "sexe": "Homme", + "date_expiration": "15/06/2027" + }, + "478564181356838922": { + "nom": "ECCLESTON", + "prenom": "John", + "date_naissance": "21 aout 1990", + "lieu_naissance": "Londres", + "sexe": "Homme", + "date_expiration": "15/06/2027" + }, + "897082541033545739": { + "nom": "SALVADOR", + "prenom": "Diego", + "date_naissance": "18/06/2003", + "lieu_naissance": "Mexico", + "sexe": "Homme", + "date_expiration": "22/06/2027" + }, + "1048991928496177152": { + "nom": "BRABUS", + "prenom": "Selim", + "date_naissance": "08/01/2002", + "lieu_naissance": "Rome", + "sexe": "Homme", + "date_expiration": "15/06/2027" + }, + "1351650470392893512": { + "nom": "LOPEZ", + "prenom": "Jason", + "date_naissance": "19/09/1990", + "lieu_naissance": "Milano", + "sexe": "Homme", + "date_expiration": "19/06/2027" + }, + "1218642392043884685": { + "nom": "MANCINI", + "prenom": "Salvatore", + "date_naissance": "22/11/1965", + "lieu_naissance": "Venice", + "sexe": "Homme", + "date_expiration": "19/06/2027" + }, + "1246814346139668553": { + "nom": "MENDEZ", + "prenom": "Arthur", + "date_naissance": "20/03/2000", + "lieu_naissance": "Guillerval", + "sexe": "Homme", + "date_expiration": "19/06/2027" + }, + "1072112886509473792": { + "nom": "FROG", + "prenom": "Beka", + "date_naissance": "19/10/1994", + "lieu_naissance": "Toronto", + "sexe": "Homme", + "date_expiration": "20/06/2027" + }, + "690202239536857174": { + "nom": "DO SANTOS", + "prenom": "Raphaël", + "date_naissance": "19/06/1998", + "lieu_naissance": "Sao Paulo", + "sexe": "Homme", + "date_expiration": "22/06/2027" + }, + "1290382773013577802": { + "nom": "DO SANTOS", + "prenom": "Fernando", + "date_naissance": "04/02/2003", + "lieu_naissance": "Sao Paulo", + "sexe": "Homme", + "date_expiration": "25/06/2027" + }, + "930869581931745410": { + "nom": "REAPER", + "prenom": "Tommy", + "date_naissance": "25/01/2003", + "lieu_naissance": "New York", + "sexe": "Homme", + "date_expiration": "25/06/2027" + }, + "667478308870422568": { + "nom": "SUPER", + "prenom": "Giovanni", + "date_naissance": "08/10/1993", + "lieu_naissance": "Stockholm", + "sexe": "Homme", + "date_expiration": "02/07/2027" + }, + "1341203038609739889": { + "nom": "MARAZEL", + "prenom": "Tyson", + "date_naissance": "26/08/2002", + "lieu_naissance": "Marseille", + "sexe": "Homme", + "date_expiration": "04/07/2027" + } + } +} \ No newline at end of file diff --git a/stats_commandes.json b/stats_commandes.json new file mode 100644 index 0000000..0967ef4 --- /dev/null +++ b/stats_commandes.json @@ -0,0 +1 @@ +{} diff --git a/taches.json b/taches.json new file mode 100644 index 0000000..c393e93 --- /dev/null +++ b/taches.json @@ -0,0 +1,64 @@ +{ + "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": [] + } + } +} \ No newline at end of file diff --git a/utils/__init__.py b/utils/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/utils/banque_manager.py b/utils/banque_manager.py new file mode 100644 index 0000000..4b594be --- /dev/null +++ b/utils/banque_manager.py @@ -0,0 +1,58 @@ +import json +import os +from datetime import datetime +from .logger import log_error + +class BanqueManager: + def __init__(self): + + self.file_path = "comptes_bancaires.json" + self.comptes = {} + self.load_data() + + def load_data(self): + """Charge les données bancaires depuis un fichier JSON.""" + if os.path.exists(self.file_path): + try: + with open(self.file_path, "r", encoding="utf-8") as f: + data = f.read().strip() + self.comptes = json.loads(data) if data else {} + except json.JSONDecodeError: + log_error("Erreur de lecture dans comptes_bancaires.json, réinitialisation...") + self.comptes = {} + self.save_data() + + def save_data(self): + """Sauvegarde les comptes et transactions dans comptes_bancaires.json.""" + with open(self.file_path, "w", encoding="utf-8") as f: + json.dump(self.comptes, f, indent=4, ensure_ascii=False) + + def enregistrer_transaction(self, emetteur_id, destinataire_id, montant, type_transaction): + """ + Enregistre une transaction dans l'historique et recharge les données en temps réel. + """ + self.load_data() + + transaction = { + "date": datetime.now().strftime("%d/%m/%Y %H:%M:%S"), + "type": type_transaction, + "montant": montant, + "emetteur": emetteur_id, + "destinataire": destinataire_id + } + + if emetteur_id not in self.comptes: + self.comptes[emetteur_id] = {"solde": 0, "prets": [], "transactions": []} + elif "transactions" not in self.comptes[emetteur_id]: + self.comptes[emetteur_id]["transactions"] = [] + + self.comptes[emetteur_id]["transactions"].append(transaction) + + if destinataire_id not in self.comptes: + self.comptes[destinataire_id] = {"solde": 0, "prets": [], "transactions": []} + elif "transactions" not in self.comptes[destinataire_id]: + self.comptes[destinataire_id]["transactions"] = [] + + self.comptes[destinataire_id]["transactions"].append(transaction) + + self.save_data() diff --git a/utils/card_generator.py b/utils/card_generator.py new file mode 100644 index 0000000..b580b93 --- /dev/null +++ b/utils/card_generator.py @@ -0,0 +1,126 @@ +import os +import aiohttp +import discord +from io import BytesIO +from PIL import Image, ImageDraw, ImageFont + +class CardGenerator: + def __init__(self): + self.session = None + self.template = None + self.watermark = None + self.font_regular = None + self.font_bold = None + self.font_large = None + self.create_template() + + def create_template(self): + try: + CI_path = "/home/discord/Bot/Vicy/ressources/assets/CIITAL.png" + + if not os.path.exists(CI_path): + raise FileNotFoundError(f"Image de fond introuvable : {CI_path}") + + # Chargement de l'image de base + self.template = Image.open(CI_path).convert("RGBA") + self.template = self.template.resize((800, 500)) + + # Vérifier que l'image a bien été chargée + if self.template is None: + raise ValueError("L'image de fond n'a pas été chargée correctement.") + + # Chargement des polices + try: + self.font_regular = ImageFont.load_default() + self.font_bold = ImageFont.load_default() + self.font_large = ImageFont.load_default() + except Exception as e: + print(f"Erreur lors du chargement des polices: {e}") + self.font_regular = None + self.font_bold = None + self.font_large = None + + # Créer un filigrane transparent + self.watermark = Image.new('RGBA', (800, 500), (255, 255, 255, 0)) + watermark_draw = ImageDraw.Draw(self.watermark) + + for i in range(0, 800, 100): + for j in range(0, 500, 100): + watermark_draw.text((i, j), "", fill=(200, 200, 200, 100)) + + except Exception as e: + print(f"Erreur lors de la création du template: {e}") + self.template = None + self.watermark = None + + async def get_session(self): + if self.session is None: + self.session = aiohttp.ClientSession() + return self.session + + async def download_avatar(self, user): + if not user.avatar: + return Image.new('RGBA', (200, 200), (128, 128, 128, 255)) + + try: + session = await self.get_session() + async with session.get(str(user.avatar.url)) as response: + if response.status == 200: + avatar_data = await response.read() + return Image.open(BytesIO(avatar_data)).convert('RGBA') + except Exception as e: + print(f"Erreur lors du téléchargement de l'avatar: {e}") + return Image.new('RGBA', (200, 200), (128, 128, 128, 255)) + + async def create_identity_card(self, user, identity_data): + if not self.template: + self.create_template() + if not self.template: + raise Exception("Impossible de créer le template") + + card = self.template.copy() + draw = ImageDraw.Draw(card) + + try: + # Photo de profil + avatar = await self.download_avatar(user) + avatar = avatar.resize((200, 200), Image.LANCZOS) + mask = Image.new('L', avatar.size, 0) + mask_draw = ImageDraw.Draw(mask) + mask_draw.ellipse((0, 0, 200, 200), fill=255) + card.paste(avatar, (50, 120), mask) + + # Informations personnelles + text_positions = { + 'nom': (328, 169), + 'prenom': (329, 212), + 'date_naissance': (329, 254), + 'lieu_naissance': (489, 169), + 'sexe': (511, 212), + 'date_expiration': (26, 408) + } + + for field, pos in text_positions.items(): + value = str(identity_data[field]) + + draw.text(pos, + value, + fill='black', + font=self.font_bold if field in ['nom', 'prenom'] else self.font_regular) + + if self.watermark: + card.paste(self.watermark, (0, 0), self.watermark) + + buffer = BytesIO() + card.save(buffer, format='PNG') + buffer.seek(0) + return buffer + + except Exception as e: + print(f"Erreur lors de la création de la carte: {e}") + raise + + async def cleanup(self): + if self.session: + await self.session.close() + self.session = None diff --git a/utils/data_manager.py b/utils/data_manager.py new file mode 100644 index 0000000..b095ce9 --- /dev/null +++ b/utils/data_manager.py @@ -0,0 +1,61 @@ +import json +import os +from datetime import datetime, timedelta + + +class DataManager: + """ Gère la sauvegarde et le chargement des données. """ + + def __init__(self): + self.identities = {} + self.load_data() + + def save_data(self): + """ Sauvegarde les identités dans un fichier JSON. """ + try: + with open('ressources/data.json', 'w', encoding='utf-8') as f: + json.dump({'identities': self.identities}, f, ensure_ascii=False, indent=4) + except Exception as e: + print(f"Erreur lors de la sauvegarde des données: {e}") + + def load_data(self): + """ Charge les identités depuis un fichier JSON. """ + try: + if os.path.exists('ressources/data.json'): + with open('ressources/data.json', 'r', encoding='utf-8') as f: + data = json.load(f) + self.identities = data.get('identities', {}) + except Exception as e: + print(f"Erreur lors du chargement des données: {e}") + self.identities = {} + +class DataManager: + def __init__(self): + """Initialise le gestionnaire de données et charge les identités existantes.""" + self.file_path = os.path.join(os.path.dirname(__file__), "../ressources/data.json") + self.identities = self.load_data() # Charge les données existantes + + def load_data(self): + """Charge les identités depuis le fichier JSON.""" + if not os.path.exists(self.file_path): + print(f"⚠️ Fichier {self.file_path} introuvable. Création d'un nouveau fichier...") + self.save_data() # Crée un fichier vide + return {} + + try: + with open(self.file_path, "r", encoding="utf-8") as f: + data = json.load(f) + return data.get("identities", {}) + except (json.JSONDecodeError, IOError) as e: + print(f"❌ Erreur lors du chargement des données : {e}") + return {} + + def save_data(self): + """Sauvegarde les identités dans un fichier JSON.""" + try: + os.makedirs(os.path.dirname(self.file_path), exist_ok=True) + with open(self.file_path, "w", encoding="utf-8") as f: + json.dump({"identities": self.identities}, f, ensure_ascii=False, indent=4) + print(f"✅ Données sauvegardées dans {self.file_path}") + except IOError as e: + print(f"❌ Erreur lors de la sauvegarde des données : {e}") diff --git a/utils/gestion_taches.py b/utils/gestion_taches.py new file mode 100644 index 0000000..f35a1b6 --- /dev/null +++ b/utils/gestion_taches.py @@ -0,0 +1,89 @@ +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 diff --git a/utils/image_generator.py b/utils/image_generator.py new file mode 100644 index 0000000..f2c3505 --- /dev/null +++ b/utils/image_generator.py @@ -0,0 +1,50 @@ +import json +from PIL import Image, ImageDraw, ImageFont +import os + +def generate_bank_image(user_id, balance, transactions): + """Génère une image avec les informations bancaires de l'utilisateur.""" + + image_path = "ressources/assets/template.png" + if not os.path.exists(image_path): + print(f"❌ Erreur : Fichier {image_path} introuvable.") + return None + + image = Image.open(image_path) + draw = ImageDraw.Draw(image) + + # Vérifier que la police existe + font_path = "ressources/font.ttf" + if not os.path.exists(font_path): + print("❌ Erreur : Police introuvable, chargement de la police par défaut.") + font_large = ImageFont.load_default() + font_small = ImageFont.load_default() + else: + font_large = ImageFont.truetype(font_path, 120) + font_small = ImageFont.truetype(font_path, 30) + + # Centrer le solde en dessous de la fenêtre bleue + balance_text = f"Solde : {balance} €" + balance_x = (image.width - draw.textlength(balance_text, font=font_large)) // 2 + balance_y = 850 # Ajustez cette valeur pour positionner le texte en dessous de la fenêtre bleue + draw.text((balance_x, balance_y), balance_text, fill="white", font=font_large) + + # Affichage des transactions + start_x = 50 # Ajustez cette valeur pour l'alignement horizontal + start_y = 400 # Ajustez cette valeur pour l'alignement vertical + spacing_y = 60 # Espace entre les lignes de transaction + + for transaction in transactions[:3]: # Max 3 transactions + transaction_text = f"{transaction['date']} - {transaction['type']} : {transaction['montant']}€" + draw.text((start_x, start_y), transaction_text, fill="black", font=font_small) + start_y += spacing_y + + # Supprimer le texte "Victoria’s Vice™" + erase_box = (30, image.height - 50, 250, image.height - 20) + draw.rectangle(erase_box, fill=(255, 255, 255)) + + # Sauvegarde de l'image générée + output_path = f"ressources/bank_{user_id}.png" + image.save(output_path) + print(f"✅ Image bancaire générée : {output_path}") + return output_path diff --git a/utils/logger.py b/utils/logger.py new file mode 100644 index 0000000..69bad5d --- /dev/null +++ b/utils/logger.py @@ -0,0 +1,34 @@ +import logging +from datetime import datetime +import pytz # Nécessite le package pytz + +# Définir le fuseau horaire France +paris_tz = pytz.timezone("Europe/Paris") + +class FranceFormatter(logging.Formatter): + def formatTime(self, record, datefmt=None): + dt = datetime.fromtimestamp(record.created, tz=paris_tz) + if datefmt: + return dt.strftime(datefmt) + return dt.isoformat() + +# Création du logger +logger = logging.getLogger() +logger.setLevel(logging.INFO) + +# Handler vers fichier +file_handler = logging.FileHandler("log.txt", encoding="utf-8") +formatter = FranceFormatter("%(asctime)s - %(levelname)s - %(message)s", datefmt="%Y-%m-%d %H:%M:%S") +file_handler.setFormatter(formatter) + +logger.addHandler(file_handler) + +# Fonctions de log +def log_info(message): + logger.info(message) + +def log_warning(message): + logger.warning(message) + +def log_error(message): + logger.error(message) diff --git a/utils/stats_manager.py b/utils/stats_manager.py new file mode 100644 index 0000000..1c9b41a --- /dev/null +++ b/utils/stats_manager.py @@ -0,0 +1,82 @@ +import sqlite3 +import datetime +import os + +# Chemin de la base de données (dans un sous-dossier "data" pour garder le projet propre) +DB_PATH = "data/stats.db" + +# Crée le dossier "data" si besoin +os.makedirs(os.path.dirname(DB_PATH), exist_ok=True) + +# Connexion à la base de données +conn = sqlite3.connect(DB_PATH) +cursor = conn.cursor() + +# Création de la table si elle n'existe pas +cursor.execute(''' +CREATE TABLE IF NOT EXISTS commandes ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + nom TEXT, + user_id INTEGER, + guild_id INTEGER, + date TIMESTAMP +) +''') +conn.commit() + +def enregistrer_utilisation(nom_commande, user_id, guild_id): + """Enregistre l'utilisation d'une commande.""" + cursor.execute( + "INSERT INTO commandes (nom, user_id, guild_id, date) VALUES (?, ?, ?, ?)", + (nom_commande, user_id, guild_id, datetime.datetime.now()) + ) + conn.commit() + +def get_stats(): + """Retourne les statistiques d'utilisation du bot.""" + cursor.execute("SELECT COUNT(*) FROM commandes") + total = cursor.fetchone()[0] + + cursor.execute("SELECT nom, COUNT(*) as nb FROM commandes GROUP BY nom ORDER BY nb DESC LIMIT 5") + top_commandes = cursor.fetchall() + + cursor.execute("SELECT user_id, COUNT(*) as nb FROM commandes GROUP BY user_id ORDER BY nb DESC LIMIT 1") + top_user = cursor.fetchone() + + return total, top_commandes, top_user + + +def get_stats_detailed(): + # Exemple de données simulées, à remplacer par tes vraies données + from datetime import datetime, timedelta + + total_commandes = 1234 + top_commandes = [("help", 250), ("stats_bot", 180), ("play", 160), ("ban", 100), ("kick", 90)] + top_utilisateurs = [(123456789012345678, 300), (987654321098765432, 220), (111222333444555666, 180), (444555666777888999, 160), (555666777888999000, 130)] + + today = datetime.today() + evolution_commandes = [] + for i in range(7): # dernière semaine + day = today - timedelta(days=6 - i) + evolution_commandes.append((day, 150 + i*10)) + + repartition_statuts = {"À faire": 50, "En cours": 30, "Terminée": 70, "Bloqué": 10} + + commandes_par_jour = { + "Lundi": 180, + "Mardi": 210, + "Mercredi": 150, + "Jeudi": 200, + "Vendredi": 220, + "Samedi": 90, + "Dimanche": 40, + } + + return { + "total_commandes": total_commandes, + "top_commandes": top_commandes, + "top_utilisateurs": top_utilisateurs, + "evolution_commandes": evolution_commandes, + "repartition_statuts": repartition_statuts, + "commandes_par_jour": commandes_par_jour + } diff --git a/utils/welcome_message.py b/utils/welcome_message.py new file mode 100644 index 0000000..c3d6d62 --- /dev/null +++ b/utils/welcome_message.py @@ -0,0 +1,30 @@ +import discord +from discord.ext import commands +from utils.logger import log_info, log_error + +class WelcomeMessage(commands.Cog): + def __init__(self, bot): + self.bot = bot + + @commands.Cog.listener() + async def on_member_join(self, member): + try: + # ID du salon discussion + welcome_channel_id = 1278483527783944242 + welcome_channel = member.guild.get_channel(welcome_channel_id) + + if welcome_channel: + welcome_message = ( + f"## BIENVENUE <:panda_welcome:1297298999849517056> !\n\n" + f"> **L'équipe staff te souhaite la bienvenue sur Victoria's Vice {member.mention} !**\n\n" + f"> Pour ta candidature <:arrow_1:1297491509201080362> <#1278477608392196176> / <#1278477152215371806> \n\n" + f"> Si tu as des questions <:arrow_1:1297491509201080362> <#1279037583820718120>\n\n" + f"*> Bien cordialement, L'équipe staff de Victoria's Vice*" + ) + + await welcome_channel.send(welcome_message) + except Exception as e: + print(f"Erreur lors de l'envoi du message de bienvenue: {e}") + +async def setup(bot): + await bot.add_cog(WelcomeMessage(bot)) diff --git a/visite_médicale.json b/visite_médicale.json new file mode 100644 index 0000000..0967ef4 --- /dev/null +++ b/visite_médicale.json @@ -0,0 +1 @@ +{}