diff --git a/__main__.py b/__main__.py index 766f201..210973a 100644 --- a/__main__.py +++ b/__main__.py @@ -33,7 +33,7 @@ permissions.update( ) # Initialisation du bot -bot = commands.Bot(command_prefix="!", application_id="1310235313461002241", intents=intents) +bot = commands.Bot(command_prefix="!", application_id="1337733859814543410", intents=intents) async def load_cogs(): """Charge toutes les commandes.""" @@ -42,38 +42,25 @@ async def load_cogs(): "commandes.identite", "commandes.voir_carte_identite", "commandes.virement", - "commandes.transactions", - "commandes.ajouter_argent", + "commandes.compte", + "commandes.mes_ribs", + "commandes.gouv", + "commandes.banque", + "commandes.audit", "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.roll", "commandes.annonces_sessions", - "commandes.retrait_gouv", "commandes.counting", - "commandes.config" + "commandes.config", + "commandes.panel" ] for ext in extensions: @@ -89,7 +76,7 @@ async def on_ready(): 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.8")) # Ajout du status + await bot.change_presence(activity=discord.Game(name="la V2.0")) log_info("Commandes synchronisées.") # Gère les erreurs de commandes classiques diff --git a/abonnements.json b/abonnements.json deleted file mode 100644 index 49819bb..0000000 --- a/abonnements.json +++ /dev/null @@ -1,218 +0,0 @@ -[ - { - "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/banque_data.json b/banque_data.json deleted file mode 100644 index 9e26dfe..0000000 --- a/banque_data.json +++ /dev/null @@ -1 +0,0 @@ -{} \ No newline at end of file diff --git a/commandes/abonnement.py b/commandes/abonnement.py deleted file mode 100644 index 17750d5..0000000 --- a/commandes/abonnement.py +++ /dev/null @@ -1,148 +0,0 @@ -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/ajouter_argent.py b/commandes/ajouter_argent.py deleted file mode 100644 index e90a458..0000000 --- a/commandes/ajouter_argent.py +++ /dev/null @@ -1,145 +0,0 @@ -import discord -from discord import app_commands -from discord.ext import commands -import json -from datetime import datetime -from utils.guild_config import has_role - -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 has_role(interaction.user, "banquier"): - 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/audit.py b/commandes/audit.py new file mode 100644 index 0000000..900f261 --- /dev/null +++ b/commandes/audit.py @@ -0,0 +1,75 @@ +import discord +from discord import app_commands +from discord.ext import commands + +from utils.guild_config import has_role, get_guild_role +from utils.bank_manager import BankManager + +bank_manager = BankManager() + + +class Audit(commands.Cog): + def __init__(self, bot): + self.bot = bot + + audit_group = app_commands.Group(name="audit", description="Audit bancaire (institutions)") + + @audit_group.command(name="consulter-banque", description="Consulter les comptes d'une banque (institutions avec audit)") + @app_commands.describe(nom_banque="Nom de la banque à auditer") + async def consulter_banque(self, interaction: discord.Interaction, nom_banque: str): + user_roles = [r.id for r in interaction.user.roles] + institutions = await bank_manager.lister_institutions(interaction.guild.id) + + authorized = None + for inst in institutions: + if inst["role_id"] and inst["role_id"] in user_roles and inst["can_audit_all"]: + authorized = inst + break + + if not authorized: + await interaction.response.send_message( + "❌ Vous n'avez pas le droit d'audit. Réservé aux institutions avec le droit d'audit activé.", + ephemeral=True, + ) + return + + banque = await bank_manager.get_banque_by_name(interaction.guild.id, nom_banque) + if not banque: + banques = await bank_manager.lister_banques(interaction.guild.id) + noms = ", ".join(b["name"] for b in banques) or "aucune" + await interaction.response.send_message( + f"❌ Banque **{nom_banque}** introuvable. Banques disponibles : {noms}", + ephemeral=True, + ) + return + + comptes = await bank_manager.get_comptes_banque(interaction.guild.id, banque["id"]) + if not comptes: + await interaction.response.send_message("Aucun compte dans cette banque.", ephemeral=True) + return + + embed = discord.Embed( + title=f"🔍 Audit : {banque['name']}", + description=f"Effectué par l'institution **{authorized['name']}**", + color=discord.Color.orange(), + ) + + for c in comptes: + member = interaction.guild.get_member(c["user_id"]) + name = member.display_name if member else f"User#{c['user_id']}" + tva_text = f" | TVA: {c['tva_rate']}%" if c["account_type"] == "entreprise" else "" + embed.add_field( + name=f"{name} ({c['account_type']})", + value=( + f"**RIB :** `{c['rib']}`\n" + f"**Solde :** `{c['solde']:.2f}€`{tva_text}" + ), + inline=False, + ) + + embed.set_footer(text=f"Total : {len(comptes)} compte(s) | Réserve banque : {banque['vault_balance']:.2f}€") + await interaction.response.send_message(embed=embed, ephemeral=True) + + +async def setup(bot): + await bot.add_cog(Audit(bot)) diff --git a/commandes/banque.py b/commandes/banque.py new file mode 100644 index 0000000..1a89ce6 --- /dev/null +++ b/commandes/banque.py @@ -0,0 +1,245 @@ +import discord +from discord import app_commands +from discord.ext import commands + +from utils.guild_config import has_role, get_guild_role +from utils.bank_manager import BankManager +from utils.rib import decoder_rib + +bank_manager = BankManager() + + +class Banque(commands.Cog): + def __init__(self, bot): + self.bot = bot + + banque_group = app_commands.Group(name="banque", description="Opérations bancaires") + + async def _get_user_bank(self, interaction: discord.Interaction) -> dict: + banques = await bank_manager.lister_banques(interaction.guild.id) + for b in banques: + role = interaction.guild.get_role(b["role_id"]) + if role and role in interaction.user.roles: + return b + return None + + @banque_group.command(name="ouvrir-compte", description="Ouvrir un compte pour un membre") + @app_commands.describe( + membre="Le membre qui recevra le compte", + type_compte="Type de compte", + taux_tva="Taux TVA (entreprises uniquement, défaut 0%)" + ) + @app_commands.choices( + type_compte=[ + app_commands.Choice(name="Particulier", value="particulier"), + app_commands.Choice(name="Entreprise", value="entreprise"), + ] + ) + async def ouvrir_compte( + self, + interaction: discord.Interaction, + membre: discord.Member, + type_compte: app_commands.Choice[str], + taux_tva: float = 0.0, + ): + bank = await self._get_user_bank(interaction) + if not bank: + await interaction.response.send_message( + "❌ Vous n'avez pas de rôle de banquier sur ce serveur.", ephemeral=True + ) + return + + if type_compte.value == "entreprise" and taux_tva < 0: + await interaction.response.send_message("❌ Le taux TVA ne peut pas être négatif.", ephemeral=True) + return + + try: + result = await bank_manager.ouvrir_compte( + guild_id=interaction.guild.id, + bank_id=bank["id"], + user_id=membre.id, + account_type=type_compte.value, + tva_rate=taux_tva if type_compte.value == "entreprise" else 0.0, + ) + + embed = discord.Embed( + title="💳 Compte ouvert", + description=( + f"**Banque :** {bank['name']}\n" + f"**Membre :** {membre.mention}\n" + f"**Type :** {type_compte.value.title()}\n" + f"**RIB :** `{result['rib']}`\n" + ), + color=discord.Color.green(), + ) + if type_compte.value == "entreprise": + embed.add_field(name="Taux TVA", value=f"{taux_tva}%") + + await interaction.response.send_message(embed=embed, ephemeral=True) + + try: + await membre.send( + f"💳 Votre compte **{type_compte.value}** chez **{bank['name']}** a été ouvert.\n" + f"**RIB :** `{result['rib']}`" + ) + except discord.Forbidden: + pass + + except ValueError as e: + await interaction.response.send_message(f"❌ {e}", ephemeral=True) + + @banque_group.command(name="modifier-tva", description="Modifier le taux TVA d'une entreprise") + @app_commands.describe( + rib="RIB de l'entreprise", + nouveau_taux="Nouveau taux TVA (ex: 5.5, 20)" + ) + async def modifier_tva(self, interaction: discord.Interaction, rib: str, nouveau_taux: float): + bank = await self._get_user_bank(interaction) + if not bank: + await interaction.response.send_message( + "❌ Vous n'avez pas de rôle de banquier sur ce serveur.", ephemeral=True + ) + return + + if nouveau_taux < 0 or nouveau_taux > 100: + await interaction.response.send_message("❌ Le taux doit être entre 0 et 100%.", ephemeral=True) + return + + compte = await bank_manager.get_compte_by_rib(interaction.guild.id, rib) + if not compte: + await interaction.response.send_message("❌ Compte introuvable.", ephemeral=True) + return + if compte["bank_id"] != bank["id"]: + await interaction.response.send_message("❌ Ce RIB n'appartient pas à votre banque.", ephemeral=True) + return + if compte["account_type"] != "entreprise": + await interaction.response.send_message("❌ La TVA ne s'applique qu'aux comptes entreprise.", ephemeral=True) + return + + ok = await bank_manager.modifier_tva(interaction.guild.id, rib, nouveau_taux) + if ok: + await interaction.response.send_message( + f"✅ Taux TVA mis à jour : **{nouveau_taux}%** pour `{rib}`.", ephemeral=True + ) + else: + await interaction.response.send_message("❌ Erreur lors de la mise à jour.", ephemeral=True) + + @banque_group.command(name="consulter-clients", description="Voir tous les comptes de votre banque") + async def consulter_clients(self, interaction: discord.Interaction): + bank = await self._get_user_bank(interaction) + if not bank: + await interaction.response.send_message( + "❌ Vous n'avez pas de rôle de banquier sur ce serveur.", ephemeral=True + ) + return + + comptes = await bank_manager.get_comptes_banque(interaction.guild.id, bank["id"]) + if not comptes: + await interaction.response.send_message("Aucun compte dans cette banque.", ephemeral=True) + return + + embed = discord.Embed( + title=f"🏦 Clients de {bank['name']}", + color=discord.Color.blue(), + ) + + for c in comptes: + member = interaction.guild.get_member(c["user_id"]) + name = member.display_name if member else f"<@{c['user_id']}>" + tva_text = f" | TVA: {c['tva_rate']}%" if c["account_type"] == "entreprise" else "" + embed.add_field( + name=f"{name} ({c['account_type']})", + value=( + f"**RIB :** `{c['rib']}`\n" + f"**Solde :** `{c['solde']:.2f}€`{tva_text}" + ), + inline=False, + ) + + embed.set_footer(text=f"{len(comptes)} compte(s)") + await interaction.response.send_message(embed=embed, ephemeral=True) + + @banque_group.command(name="info", description="Informations sur votre banque") + async def info_banque(self, interaction: discord.Interaction): + bank = await self._get_user_bank(interaction) + if not bank: + await interaction.response.send_message( + "❌ Vous n'avez pas de rôle de banquier sur ce serveur.", ephemeral=True + ) + return + + role = interaction.guild.get_role(bank["role_id"]) + comptes = await bank_manager.get_comptes_banque(interaction.guild.id, bank["id"]) + nb_particuliers = sum(1 for c in comptes if c["account_type"] == "particulier") + nb_entreprises = sum(1 for c in comptes if c["account_type"] == "entreprise") + + embed = discord.Embed( + title=f"🏦 {bank['name']}", + color=discord.Color.blue(), + ) + embed.add_field(name="Rôle", value=role.mention if role else "Inconnu", inline=True) + embed.add_field(name="Code établissement", value=f"`{bank['establishment_code']}`", inline=True) + embed.add_field(name="Solde propre (réserve)", value=f"`{bank['vault_balance']:.2f}€`", inline=True) + embed.add_field(name="Encours clients", value=f"`{bank['clients_total_balance']:.2f}€`", inline=True) + embed.add_field(name="Comptes particuliers", value=str(nb_particuliers), inline=True) + embed.add_field(name="Comptes entreprises", value=str(nb_entreprises), inline=True) + + await interaction.response.send_message(embed=embed, ephemeral=True) + + @banque_group.command(name="depot", description="Déposer de l'argent sur un compte") + @app_commands.describe(rib="RIB du compte", montant="Montant à déposer") + async def depot(self, interaction: discord.Interaction, rib: str, montant: float): + bank = await self._get_user_bank(interaction) + if not bank: + await interaction.response.send_message( + "❌ Vous n'avez pas de rôle de banquier sur ce serveur.", ephemeral=True + ) + return + + compte = await bank_manager.get_compte_by_rib(interaction.guild.id, rib) + if not compte: + await interaction.response.send_message("❌ Compte introuvable.", ephemeral=True) + return + if compte["bank_id"] != bank["id"]: + await interaction.response.send_message("❌ Ce RIB n'appartient pas à votre banque.", ephemeral=True) + return + + try: + result = await bank_manager.depot_banquier(interaction.guild.id, rib, montant, interaction.user.id) + await interaction.response.send_message( + f"✅ Dépôt de **{montant:.2f}€** sur `{rib}`. Nouveau solde : **{result['nouveau_solde']:.2f}€**", + ephemeral=True, + ) + except ValueError as e: + await interaction.response.send_message(f"❌ {e}", ephemeral=True) + + @banque_group.command(name="retrait", description="Retirer de l'argent d'un compte") + @app_commands.describe(rib="RIB du compte", montant="Montant à retirer") + async def retrait(self, interaction: discord.Interaction, rib: str, montant: float): + bank = await self._get_user_bank(interaction) + if not bank: + await interaction.response.send_message( + "❌ Vous n'avez pas de rôle de banquier sur ce serveur.", ephemeral=True + ) + return + + compte = await bank_manager.get_compte_by_rib(interaction.guild.id, rib) + if not compte: + await interaction.response.send_message("❌ Compte introuvable.", ephemeral=True) + return + if compte["bank_id"] != bank["id"]: + await interaction.response.send_message("❌ Ce RIB n'appartient pas à votre banque.", ephemeral=True) + return + + try: + result = await bank_manager.retrait_banquier(interaction.guild.id, rib, montant, interaction.user.id) + await interaction.response.send_message( + f"✅ Retrait de **{montant:.2f}€** sur `{rib}`. Nouveau solde : **{result['nouveau_solde']:.2f}€**", + ephemeral=True, + ) + except ValueError as e: + await interaction.response.send_message(f"❌ {e}", ephemeral=True) + + +async def setup(bot): + await bot.add_cog(Banque(bot)) diff --git a/commandes/compte.py b/commandes/compte.py index 27e35ae..5c20cb1 100644 --- a/commandes/compte.py +++ b/commandes/compte.py @@ -1,93 +1,62 @@ import discord from discord import app_commands from discord.ext import commands -from utils.banque_manager import BanqueManager -banque_manager = BanqueManager() +from utils.bank_manager import BankManager + +bank_manager = BankManager() + 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)""" + @app_commands.command(name="compte", description="Afficher vos comptes bancaires et vos dernières transactions") + async def compte(self, interaction: discord.Interaction): + comptes = await bank_manager.get_comptes_user(interaction.guild.id, interaction.user.id) - 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 + if not comptes: + await interaction.response.send_message( + "❌ Vous n'avez aucun compte bancaire. Utilisez `/mes-ribs` pour voir vos RIBs.", + ephemeral=True, + ) + return embed = discord.Embed( - title=f"💳 Comptes bancaires de {cible.display_name}", - color=discord.Color.blue() + title=f"💳 Comptes de {interaction.user.display_name}", + color=discord.Color.blue(), ) - banques_trouvees = False + for c in comptes: + transactions = await bank_manager.get_transactions(interaction.guild.id, c["rib"], limit=5) + tva_text = f" | TVA: {c['tva_rate']}%" if c["account_type"] == "entreprise" else "" - 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" + tx_lines = [] + for tx in transactions: + is_outgoing = tx["emetteur_rib"] == c["rib"] + direction = "→" if is_outgoing else "←" + other_rib = tx["destinataire_rib"] if is_outgoing else tx["emetteur_rib"] + signe = "-" if is_outgoing else "+" + tx_type = tx["type"] + if tx_type == "tva_prelevee": + tx_lines.append(f"- {direction} **TVA** : `{signe}{tx['montant']:.2f}€`") else: - tx_str += f"- 🕒 {date} : **{tx_type}** de **{montant}€**\n" + tx_lines.append(f"- {direction} **{tx_type}** : `{signe}{tx['montant']:.2f}€` vers `{other_rib or '?'}`") - if not tx_str: - tx_str = "*Aucune transaction.*" + tx_text = "\n".join(tx_lines) if tx_lines else "*Aucune transaction récente.*" embed.add_field( - name=f"🏦 {banque}", - value=f"💳 **RIB** : `{rib}`\n💰 **Solde** : `{solde}€`\n📜 **Transactions récentes :**\n{tx_str}", - inline=False + name=f"🏦 {c['bank_name']} ({c['account_type']}){tva_text}", + value=( + f"**RIB :** `{c['rib']}`\n" + f"**Solde :** `{c['solde']:.2f}€`\n\n" + f"**Transactions récentes :**\n{tx_text}" + ), + 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 deleted file mode 100644 index 08aa9b8..0000000 --- a/commandes/compte_banque_nationale.py +++ /dev/null @@ -1,101 +0,0 @@ -import discord -from discord import app_commands -from discord.ext import commands -import json -import math -from utils.guild_config import has_role - -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 has_role(interaction.user, "banque_nationale"): - 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 deleted file mode 100644 index 5b84105..0000000 --- a/commandes/compte_fbm.py +++ /dev/null @@ -1,91 +0,0 @@ -import discord -from discord import app_commands -from discord.ext import commands -import json -import math -from utils.guild_config import has_role - -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 has_role(interaction.user, "fbm"): - 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 deleted file mode 100644 index b79a5c8..0000000 --- a/commandes/compte_gouvernement.py +++ /dev/null @@ -1,84 +0,0 @@ -import discord -from discord import app_commands -from discord.ext import commands -import json -import math - -from utils.guild_config import has_role - -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 has_role(interaction.user, "gouvernement"): - 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 deleted file mode 100644 index 197c5cb..0000000 --- a/commandes/compte_urssaf.py +++ /dev/null @@ -1,95 +0,0 @@ -import discord -from discord import app_commands -from discord.ext import commands -import json -import math -from utils.guild_config import has_role - -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 has_role(interaction.user, "ursaff"): - 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 deleted file mode 100644 index f6bb519..0000000 --- a/commandes/comptes_entreprises.py +++ /dev/null @@ -1,73 +0,0 @@ -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/gouv.py b/commandes/gouv.py new file mode 100644 index 0000000..6bc504a --- /dev/null +++ b/commandes/gouv.py @@ -0,0 +1,194 @@ +import discord +from discord import app_commands +from discord.ext import commands + +from utils.guild_config import has_role +from utils.bank_manager import BankManager + +bank_manager = BankManager() + + +class Gouv(commands.Cog): + def __init__(self, bot): + self.bot = bot + + gouv_group = app_commands.Group(name="gouv", description="Commandes gouvernementales") + + def _check_gouv(self, interaction: discord.Interaction) -> bool: + return has_role(interaction.user, "gouvernement") + + @gouv_group.command(name="creer-banque", description="Créer une nouvelle banque commerciale") + @app_commands.describe( + nom="Nom de la banque (ex: NGC Bank)", + role="Le rôle Discord des banquiers de cette banque" + ) + async def creer_banque(self, interaction: discord.Interaction, nom: str, role: discord.Role): + if not self._check_gouv(interaction): + await interaction.response.send_message("❌ Réservé au rôle **Gouvernement**.", ephemeral=True) + return + + try: + result = await bank_manager.creer_banque(interaction.guild.id, nom, role.id) + embed = discord.Embed( + title="🏦 Banque créée", + description=( + f"**{nom}** a été créée avec succès.\n\n" + f"**Rôle banquiers :** {role.mention}\n" + f"**Code établissement :** `{result['establishment_code']}`\n" + f"**ID banque :** `{result['id']}`" + ), + color=discord.Color.green(), + ) + await interaction.response.send_message(embed=embed, ephemeral=True) + except Exception as e: + await interaction.response.send_message(f"❌ Erreur : {e}", ephemeral=True) + + @gouv_group.command(name="creer-institution", description="Créer une institution (Fisc, URSAFF, etc.)") + @app_commands.describe( + nom="Nom de l'institution", + role="Le rôle Discord qui gère cette institution", + audit="Peut-elle consulter les comptes de toutes les banques ?" + ) + async def creer_institution( + self, + interaction: discord.Interaction, + nom: str, + role: discord.Role, + audit: bool = False, + ): + if not self._check_gouv(interaction): + await interaction.response.send_message("❌ Réservé au rôle **Gouvernement**.", ephemeral=True) + return + + try: + result = await bank_manager.creer_institution( + interaction.guild.id, nom, role.id, audit + ) + embed = discord.Embed( + title="🏛️ Institution créée", + description=( + f"**{nom}** a été créée.\n\n" + f"**Rôle :** {role.mention}\n" + f"**Droit d'audit :** {'✅ Oui' if audit else '❌ Non'}\n" + f"**ID :** `{result['id']}`" + ), + color=discord.Color.blue(), + ) + await interaction.response.send_message(embed=embed, ephemeral=True) + except Exception as e: + await interaction.response.send_message(f"❌ Erreur : {e}", ephemeral=True) + + @gouv_group.command(name="config-fisc", description="Définir quelle institution reçoit la TVA") + @app_commands.describe( + institution="L'institution qui servira de Fisc" + ) + async def config_fisc(self, interaction: discord.Interaction, institution: str): + if not self._check_gouv(interaction): + await interaction.response.send_message("❌ Réservé au rôle **Gouvernement**.", ephemeral=True) + return + + institutions = await bank_manager.lister_institutions(interaction.guild.id) + target = None + for inst in institutions: + if inst["name"].lower() == institution.lower(): + target = inst + break + + if not target: + await interaction.response.send_message( + f"❌ Institution **{institution}** introuvable. Institutions disponibles : {', '.join(i['name'] for i in institutions) or 'aucune'}", + ephemeral=True, + ) + return + + ok = await bank_manager.configurer_fisc(interaction.guild.id, target["id"]) + if ok: + embed = discord.Embed( + title="🏛️ Fisc configuré", + description=f"**{target['name']}** reçoit désormais la TVA de tous les virements Particulier → Entreprise.", + color=discord.Color.green(), + ) + await interaction.response.send_message(embed=embed, ephemeral=True) + else: + await interaction.response.send_message("❌ Erreur lors de la configuration.", ephemeral=True) + + @gouv_group.command(name="lister-banques", description="Lister toutes les banques du serveur") + async def lister_banques(self, interaction: discord.Interaction): + if not self._check_gouv(interaction): + await interaction.response.send_message("❌ Réservé au rôle **Gouvernement**.", ephemeral=True) + return + + banques = await bank_manager.lister_banques(interaction.guild.id) + if not banques: + await interaction.response.send_message("Aucune banque configurée.", ephemeral=True) + return + + embed = discord.Embed(title="🏦 Banques du serveur", color=discord.Color.blue()) + for b in banques: + role = interaction.guild.get_role(b["role_id"]) + role_text = role.mention if role else f"`{b['role_id']}` introuvable" + embed.add_field( + name=b["name"], + value=( + f"**Rôle :** {role_text}\n" + f"**Code établissement :** `{b['establishment_code']}`\n" + f"**Réserve :** `{b['vault_balance']:.2f}€`\n" + f"**Encours clients :** `{b['clients_total_balance']:.2f}€`" + ), + inline=False, + ) + await interaction.response.send_message(embed=embed, ephemeral=True) + + @gouv_group.command(name="lister-institutions", description="Lister toutes les institutions du serveur") + async def lister_institutions(self, interaction: discord.Interaction): + if not self._check_gouv(interaction): + await interaction.response.send_message("❌ Réservé au rôle **Gouvernement**.", ephemeral=True) + return + + institutions = await bank_manager.lister_institutions(interaction.guild.id) + if not institutions: + await interaction.response.send_message("Aucune institution configurée.", ephemeral=True) + return + + fisc_config = await bank_manager.get_fisc_config(interaction.guild.id) + fisc_id = fisc_config["institution_id"] if fisc_config else None + + embed = discord.Embed(title="🏛️ Institutions du serveur", color=discord.Color.blue()) + for inst in institutions: + role = interaction.guild.get_role(inst["role_id"]) if inst["role_id"] else None + role_text = role.mention if role else "Non assigné" + is_fisc = " 💰 **(Fisc)**" if inst["id"] == fisc_id else "" + embed.add_field( + name=f"{inst['name']}{is_fisc}", + value=( + f"**Rôle :** {role_text}\n" + f"**Audit :** {'✅ Toutes les banques' if inst['can_audit_all'] else '❌ Non'}\n" + f"**ID :** `{inst['id']}`" + ), + inline=False, + ) + await interaction.response.send_message(embed=embed, ephemeral=True) + + @gouv_group.command(name="supprimer-banque", description="Supprimer une banque") + @app_commands.describe(nom="Nom de la banque à supprimer") + async def supprimer_banque(self, interaction: discord.Interaction, nom: str): + if not self._check_gouv(interaction): + await interaction.response.send_message("❌ Réservé au rôle **Gouvernement**.", ephemeral=True) + return + + banque = await bank_manager.get_banque_by_name(interaction.guild.id, nom) + if not banque: + await interaction.response.send_message(f"❌ Banque **{nom}** introuvable.", ephemeral=True) + return + + ok = await bank_manager.supprimer_banque(interaction.guild.id, banque["id"]) + if ok: + await interaction.response.send_message( + f"✅ Banque **{nom}** supprimée avec tous ses comptes.", ephemeral=True + ) + else: + await interaction.response.send_message("❌ Erreur lors de la suppression.", ephemeral=True) + + +async def setup(bot): + await bot.add_cog(Gouv(bot)) diff --git a/commandes/mes_ribs.py b/commandes/mes_ribs.py new file mode 100644 index 0000000..3d60f6f --- /dev/null +++ b/commandes/mes_ribs.py @@ -0,0 +1,44 @@ +import discord +from discord import app_commands +from discord.ext import commands + +from utils.bank_manager import BankManager + +bank_manager = BankManager() + + +class MesRibs(commands.Cog): + def __init__(self, bot): + self.bot = bot + + @app_commands.command(name="mes-ribs", description="Afficher tous vos RIBs") + async def mes_ribs(self, interaction: discord.Interaction): + comptes = await bank_manager.get_comptes_user(interaction.guild.id, interaction.user.id) + + if not comptes: + await interaction.response.send_message( + "❌ Vous n'avez aucun compte bancaire.", ephemeral=True + ) + return + + embed = discord.Embed( + title=f"💳 RIBs de {interaction.user.display_name}", + color=discord.Color.blue(), + ) + + for c in comptes: + tva_text = f" | TVA: {c['tva_rate']}%" if c["account_type"] == "entreprise" else "" + embed.add_field( + name=f"{c['bank_name']} ({c['account_type']})", + value=( + f"**RIB :** `{c['rib']}`\n" + f"**Solde :** `{c['solde']:.2f}€`{tva_text}" + ), + inline=False, + ) + + await interaction.response.send_message(embed=embed, ephemeral=True) + + +async def setup(bot): + await bot.add_cog(MesRibs(bot)) diff --git a/commandes/ouvrir_compte.py b/commandes/ouvrir_compte.py deleted file mode 100644 index 8f4b979..0000000 --- a/commandes/ouvrir_compte.py +++ /dev/null @@ -1,74 +0,0 @@ -import discord -from discord import app_commands -from discord.ext import commands -from utils.banque_manager import BanqueManager -import random -from utils.guild_config import has_role - -# 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 has_role(interaction.user, "banquier"): - 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 deleted file mode 100644 index 72494f7..0000000 --- a/commandes/ouvrir_comptes_entreprises.py +++ /dev/null @@ -1,93 +0,0 @@ -import json -import os -import random -import string -import discord -from discord import app_commands -from discord.ext import commands - -from utils.guild_config import has_role - -COMPTES_ENTREPRISES_PATH = "comptes_entreprises.json" - -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 has_role(interaction.user, "banquier"): - 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/panel.py b/commandes/panel.py new file mode 100644 index 0000000..a84641e --- /dev/null +++ b/commandes/panel.py @@ -0,0 +1,2389 @@ +""" +Panel centralise — Container V2 (discord.py 2.7) +Menus adaptatifs selon les roles de l'utilisateur. +Banquiers : UserSelect pour choisir l'utilisateur cible. +""" + +import discord +from discord.ext import commands +from discord import app_commands +import json +import math +import random +import string +import os +import asyncio +from datetime import datetime, timedelta +from utils.guild_config import ( + has_role, get_guild_role, get_guild_channel, get_guild_category, + load_guild_config, save_guild_config, set_config_value, + ROLE_LABELS, CHANNEL_LABELS, CATEGORY_LABELS, get_role_id +) +from utils.data_manager import DataManager + +C_MAIN = 0x2F3136 +C_BANK = 0xF1C40F +C_GOV = 0x1ABC9C +C_ID = 0x3498DB +C_TICKET = 0xE74C3C +C_SESSION = 0x9B59B6 +C_CONFIG = 0x95A5A6 +C_FUN = 0xE67E22 +C_BANQ_OP = 0x2ECC71 + +BANQUES = ["Victoria's Credit Bank", "NGC Bank"] + + +def _load_json(path, default=None): + if default is None: + default = {} + if not os.path.exists(path): + return default + try: + with open(path, "r", encoding="utf-8") as f: + return json.load(f) + except (json.JSONDecodeError, IOError): + return default + + +def _save_json(path, data): + with open(path, "w", encoding="utf-8") as f: + json.dump(data, f, indent=4, ensure_ascii=False) + + +def _find_account_by_rib(rib, comptes, entreprises, banque_nom=None): + if banque_nom: + if banque_nom in comptes: + for uid, data in comptes[banque_nom].items(): + if data.get("rib") == rib: + return comptes, banque_nom, uid, False + if banque_nom in entreprises: + for nom, data in entreprises[banque_nom].items(): + if data.get("rib") == rib: + return entreprises, banque_nom, nom, True + for bq, users in comptes.items(): + for uid, data in users.items(): + if data.get("rib") == rib: + return comptes, bq, uid, False + for bq, ents in entreprises.items(): + for nom, data in ents.items(): + if data.get("rib") == rib: + return entreprises, bq, nom, True + return None, None, None, None + + +def _get_user_accounts(uid_str, comptes): + accounts = [] + for banque, users in comptes.items(): + if uid_str in users: + c = users[uid_str] + accounts.append((banque, c)) + return accounts + + +# ═══════════════════════════════════════════════════════════════════ +# USER SELECT (Banquiers uniquement) +# ═══════════════════════════════════════════════════════════════════ + +class TargetUserSelect(discord.ui.UserSelect): + def __init__(self, custom_id="pnl_usel", placeholder="Choisir un utilisateur (defaut: vous)"): + super().__init__(placeholder=placeholder, custom_id=custom_id, min_values=1, max_values=1) + + async def callback(self, interaction: discord.Interaction): + self.view.selected_user = self.values[0] + await interaction.response.defer() + + +# ═══════════════════════════════════════════════════════════════════ +# MODALS — COMPTES (tout le monde) +# ═══════════════════════════════════════════════════════════════════ + +class CompteModal(discord.ui.Modal, title="💰 Voir un Compte"): + def __init__(self, target_user=None): + super().__init__() + self.target_user = target_user + + async def on_submit(self, interaction: discord.Interaction): + if self.target_user: + uid_str = str(self.target_user.id) + member = self.target_user + else: + uid_str = str(interaction.user.id) + member = interaction.user + comptes = _load_json("comptes_bancaires.json") + accounts = _get_user_accounts(uid_str, comptes) + if not accounts: + await interaction.response.send_message( + f"❌ Aucun compte trouvé pour **{member.display_name}**.", ephemeral=True + ) + return + embed = discord.Embed( + title=f"💰 Comptes de {member.display_name}", + color=C_BANK, + ) + for banque, c in accounts: + txs = c.get("transactions", []) + tx_text = "" + for t in txs[-3:]: + tx_text += ( + f"> 📅 {t.get('date', 'N/A')} — **{t.get('type', 'N/A')}** — " + f"{t.get('montant', 0)}€\n" + ) + if not tx_text: + tx_text = "> Aucune transaction récente.\n" + embed.add_field( + name=f"🏦 {banque}", + value=( + f"**RIB** : `{c.get('rib', 'N/A')}`\n" + f"**Solde** : **{c.get('solde', 0):.2f}€**\n" + f"**Dernières transactions** :\n{tx_text}" + ), + inline=False, + ) + await interaction.response.send_message(embed=embed, ephemeral=True) + + +class VirementModal(discord.ui.Modal, title="📤 Effectuer un Virement"): + rib_dest = discord.ui.TextInput( + label="RIB destinataire", required=True, max_length=40 + ) + montant = discord.ui.TextInput(label="Montant", required=True, max_length=10) + banque = discord.ui.TextInput( + label="Banque (laisser vide = auto)", required=False, max_length=50 + ) + + async def on_submit(self, interaction: discord.Interaction): + try: + montant_val = float(self.montant.value.strip()) + except ValueError: + await interaction.response.send_message( + "❌ Montant invalide.", ephemeral=True + ) + return + if montant_val <= 0: + await interaction.response.send_message( + "❌ Le montant doit être supérieur à 0.", ephemeral=True + ) + return + rib_dest_val = self.rib_dest.value.strip() + banque_nom = self.banque.value.strip() if self.banque.value else None + comptes = _load_json("comptes_bancaires.json") + entreprises = _load_json("comptes_entreprises.json") + uid_str = str(interaction.user.id) + source = None + source_bq = None + for bq, users in comptes.items(): + if uid_str in users: + source = users[uid_str] + source_bq = bq + break + if not source: + await interaction.response.send_message( + "❌ Vous n'avez pas de compte bancaire.", ephemeral=True + ) + return + if source["solde"] < montant_val: + await interaction.response.send_message( + "❌ Solde insuffisant.", ephemeral=True + ) + return + dest_store, dest_bq, dest_id, dest_ent = _find_account_by_rib( + rib_dest_val, comptes, entreprises, banque_nom + ) + if not dest_store: + await interaction.response.send_message( + "❌ RIB destinataire introuvable.", ephemeral=True + ) + return + now = datetime.now().strftime("%d/%m/%Y %H:%M:%S") + source["solde"] -= montant_val + source.setdefault("transactions", []).append( + { + "date": now, + "type": "Virement", + "source": f"Vers {rib_dest_val}", + "montant": -montant_val, + } + ) + dest_store[dest_bq][dest_id]["solde"] += montant_val + dest_store[dest_bq][dest_id].setdefault("transactions", []).append( + { + "date": now, + "type": "Virement reçu", + "source": interaction.user.name, + "montant": montant_val, + } + ) + _save_json("comptes_bancaires.json", comptes) + _save_json("comptes_entreprises.json", entreprises) + await interaction.response.send_message( + f"✅ Virement de **{montant_val:.2f}€** effectué vers `{rib_dest_val}` dans **{dest_bq}**.", + ephemeral=False, + ) + + +class AbonnementModal(discord.ui.Modal, title="🔄 Créer un Abonnement"): + nom = discord.ui.TextInput( + label="Nom de l'abonnement", required=True, max_length=50 + ) + rib_emetteur = discord.ui.TextInput( + label="RIB émetteur", required=True, max_length=40 + ) + montant = discord.ui.TextInput(label="Montant", required=True, max_length=10) + rib_receveur = discord.ui.TextInput( + label="RIB receveur", required=True, max_length=40 + ) + banque = discord.ui.TextInput(label="Banque", required=True, max_length=50) + jour = discord.ui.TextInput( + label="Jour (lundi, mardi...)", required=True, max_length=10 + ) + + async def on_submit(self, interaction: discord.Interaction): + JOURS = [ + "lundi", "mardi", "mercredi", "jeudi", + "vendredi", "samedi", "dimanche", + ] + jour_val = self.jour.value.strip().lower() + if jour_val not in JOURS: + await interaction.response.send_message( + f"❌ Jour invalide. {', '.join(JOURS)}", ephemeral=True + ) + return + try: + montant_val = float(self.montant.value.strip()) + except ValueError: + await interaction.response.send_message( + "❌ Montant invalide.", ephemeral=True + ) + return + abos = _load_json("abonnements.json", []) + abos.append( + { + "nom": self.nom.value.strip(), + "rib_emetteur": self.rib_emetteur.value.strip(), + "rib_receveur": self.rib_receveur.value.strip(), + "montant": montant_val, + "banque": self.banque.value.strip(), + "jour": jour_val, + } + ) + _save_json("abonnements.json", abos) + await interaction.response.send_message( + f"✅ Abonnement **{self.nom.value.strip()}** enregistré. " + f"Virement automatique tous les **{jour_val}**.", + ephemeral=True, + ) + + +# ═══════════════════════════════════════════════════════════════════ +# MODALS — OPERATIONS BANQUIER +# ═══════════════════════════════════════════════════════════════════ + +class OuvrirCompteModal(discord.ui.Modal, title="🏦 Ouvrir un Compte"): + def __init__(self, target_user=None): + super().__init__() + self.target_user = target_user + + async def on_submit(self, interaction: discord.Interaction): + if not has_role(interaction.user, "banquier"): + await interaction.response.send_message( + "❌ Réservé aux banquiers.", ephemeral=True + ) + return + member = self.target_user or interaction.user + uid = member.id + embed = discord.Embed( + title="🏦 Ouvrir un Compte", + description=f"Compte pour : **{member.display_name}**", + color=C_BANQ_OP, + ) + await interaction.response.send_message(embed=embed, ephemeral=True) + + async def _actual_open(self, interaction, banque_nom, uid, member): + comptes = _load_json("comptes_bancaires.json") + if banque_nom not in comptes: + comptes[banque_nom] = {} + uid_str = str(uid) + if uid_str in comptes[banque_nom]: + return False, f"{member.mention} a déjà un compte dans **{banque_nom}**." + rib = ( + f"{random.randint(10000, 99999)} {random.randint(10000, 99999)} " + f"{random.randint(10000000000, 99999999999)} {random.randint(10, 99)}" + ) + comptes[banque_nom][uid_str] = { + "rib": rib, + "solde": 0.0, + "transactions": [], + } + _save_json("comptes_bancaires.json", comptes) + try: + await member.send( + f"🏦 Un compte **{banque_nom}** a été créé pour vous.\nRIB : `{rib}`" + ) + except discord.Forbidden: + pass + return True, f"✅ Compte créé pour {member.mention} dans **{banque_nom}**. RIB: `{rib}`" + + +class OuvrirCompteSimpleModal(discord.ui.Modal, title="🏦 Ouvrir Compte Personnel"): + banque = discord.ui.TextInput( + label="Banque (V'Toria's Credit Bank / NGC Bank)", + required=True, + max_length=50, + ) + + def __init__(self, target_user=None): + super().__init__() + self.target_user = target_user + + async def on_submit(self, interaction: discord.Interaction): + if not has_role(interaction.user, "banquier"): + await interaction.response.send_message( + "❌ Réservé aux banquiers.", ephemeral=True + ) + return + banque_nom = self.banque.value.strip() + if banque_nom not in BANQUES: + await interaction.response.send_message( + f"❌ Banque invalide. Choisissez : {', '.join(BANQUES)}", + ephemeral=True, + ) + return + member = self.target_user or interaction.user + uid = member.id + uid_str = str(uid) + comptes = _load_json("comptes_bancaires.json") + if banque_nom not in comptes: + comptes[banque_nom] = {} + if uid_str in comptes[banque_nom]: + await interaction.response.send_message( + f"❌ {member.mention} a déjà un compte dans **{banque_nom}**.", + ephemeral=True, + ) + return + rib = ( + f"{random.randint(10000, 99999)} {random.randint(10000, 99999)} " + f"{random.randint(10000000000, 99999999999)} {random.randint(10, 99)}" + ) + comptes[banque_nom][uid_str] = { + "rib": rib, + "solde": 0.0, + "transactions": [], + } + _save_json("comptes_bancaires.json", comptes) + try: + await member.send( + f"🏦 Un compte **{banque_nom}** a été créé pour vous.\nRIB : `{rib}`" + ) + except discord.Forbidden: + pass + await interaction.response.send_message( + f"✅ Compte créé pour {member.mention} dans **{banque_nom}**. RIB: `{rib}`", + ephemeral=False, + ) + + +class OuvrirCompteEntModal(discord.ui.Modal, title="🏢 Ouvrir Compte Entreprise"): + entreprise = discord.ui.TextInput( + label="Nom de l'entreprise", required=True, max_length=50 + ) + banque = discord.ui.TextInput( + label="Banque (V'Toria's Credit Bank / NGC Bank)", + required=True, + max_length=50, + ) + tva = discord.ui.TextInput(label="TVA (2, 5, 10 ou 20)", required=True, max_length=3) + + async def on_submit(self, interaction: discord.Interaction): + if not has_role(interaction.user, "banquier"): + await interaction.response.send_message( + "❌ Réservé aux banquiers.", ephemeral=True + ) + return + banque_nom = self.banque.value.strip() + if banque_nom not in BANQUES: + await interaction.response.send_message( + f"❌ Banque invalide. Choisissez : {', '.join(BANQUES)}", + ephemeral=True, + ) + return + try: + tva_val = int(self.tva.value.strip()) + if tva_val not in (2, 5, 10, 20): + raise ValueError + except ValueError: + await interaction.response.send_message( + "❌ TVA invalide (2, 5, 10 ou 20).", ephemeral=True + ) + return + nom_ent = self.entreprise.value.strip() + entreprises = _load_json("comptes_entreprises.json") + if banque_nom not in entreprises: + entreprises[banque_nom] = {} + if nom_ent in entreprises[banque_nom]: + await interaction.response.send_message( + f"❌ Un compte existe déjà pour **{nom_ent}** dans **{banque_nom}**.", + ephemeral=True, + ) + return + rib = " ".join( + ["".join(random.choices(string.digits, k=5)) for _ in range(4)] + ) + mdp = "".join(random.choices(string.ascii_letters + string.digits, k=8)) + entreprises[banque_nom][nom_ent] = { + "rib": rib, + "solde": 0, + "mdp": mdp, + "tva": tva_val, + "transactions": [], + } + _save_json("comptes_entreprises.json", entreprises) + try: + await interaction.user.send( + f"🏦 Compte entreprise **{nom_ent}** ouvert dans **{banque_nom}** !\n" + f"💳 RIB : `{rib}`\n🔐 Mot de passe : `{mdp}`\n💼 TVA : `{tva_val}%`" + ) + except discord.Forbidden: + pass + await interaction.response.send_message( + f"✅ Compte entreprise **{nom_ent}** créé dans **{banque_nom}**.", + ephemeral=True, + ) + + +class VirementBanquierModal(discord.ui.Modal, title="🏦 Virement Banquier"): + rib_emetteur = discord.ui.TextInput( + label="RIB émetteur", required=True, max_length=40 + ) + rib_destinataire = discord.ui.TextInput( + label="RIB destinataire", required=True, max_length=40 + ) + montant = discord.ui.TextInput(label="Montant", required=True, max_length=10) + banque = discord.ui.TextInput( + label="Banque (V'Toria's Credit Bank / NGC Bank)", + required=True, + max_length=50, + ) + + async def on_submit(self, interaction: discord.Interaction): + if not has_role(interaction.user, "banquier"): + await interaction.response.send_message( + "❌ Réservé aux banquiers.", ephemeral=True + ) + return + try: + montant_val = float(self.montant.value.strip()) + except ValueError: + await interaction.response.send_message( + "❌ Montant invalide.", ephemeral=True + ) + return + banque_nom = self.banque.value.strip() + comptes = _load_json("comptes_bancaires.json") + if banque_nom not in comptes: + await interaction.response.send_message( + "❌ Banque introuvable.", ephemeral=True + ) + return + emetteur_id = None + destinataire_id = None + for uid, data in comptes[banque_nom].items(): + if data.get("rib") == self.rib_emetteur.value.strip(): + emetteur_id = uid + if data.get("rib") == self.rib_destinataire.value.strip(): + destinataire_id = uid + if not emetteur_id: + await interaction.response.send_message( + "❌ RIB émetteur introuvable.", ephemeral=True + ) + return + if not destinataire_id: + await interaction.response.send_message( + "❌ RIB destinataire introuvable.", ephemeral=True + ) + return + if comptes[banque_nom][emetteur_id]["solde"] < montant_val: + await interaction.response.send_message( + "❌ Solde insuffisant.", ephemeral=True + ) + return + now = datetime.now().strftime("%d/%m/%Y %H:%M:%S") + comptes[banque_nom][emetteur_id]["solde"] -= montant_val + comptes[banque_nom][emetteur_id]["transactions"].append( + { + "date": now, + "type": "Virement banquier", + "source": f"Vers {self.rib_destinataire.value}", + "montant": -montant_val, + } + ) + comptes[banque_nom][destinataire_id]["solde"] += montant_val + comptes[banque_nom][destinataire_id]["transactions"].append( + { + "date": now, + "type": "Virement reçu (banquier)", + "source": f"De {self.rib_emetteur.value}", + "montant": montant_val, + } + ) + _save_json("comptes_bancaires.json", comptes) + await interaction.response.send_message( + f"✅ Virement banquier de **{montant_val:.2f}€** de " + f"`{self.rib_emetteur.value}` vers `{self.rib_destinataire.value}` " + f"dans **{banque_nom}**.", + ephemeral=True, + ) + + +class VirementBanquierEntModal(discord.ui.Modal, title="🏢 Vir. Banquier → Entreprise"): + banque = discord.ui.TextInput( + label="Banque (V'Toria's Credit Bank / NGC Bank)", + required=True, + max_length=50, + ) + entreprise = discord.ui.TextInput( + label="Nom entreprise", required=True, max_length=50 + ) + mdp = discord.ui.TextInput( + label="Mot de passe entreprise", required=True, max_length=20 + ) + rib_dest = discord.ui.TextInput( + label="RIB destinataire", required=True, max_length=40 + ) + montant = discord.ui.TextInput(label="Montant", required=True, max_length=10) + + async def on_submit(self, interaction: discord.Interaction): + if not has_role(interaction.user, "banquier"): + await interaction.response.send_message( + "❌ Réservé aux banquiers.", ephemeral=True + ) + return + try: + montant_val = float(self.montant.value.strip()) + except ValueError: + await interaction.response.send_message( + "❌ Montant invalide.", ephemeral=True + ) + return + banque_nom = self.banque.value.strip() + entreprises = _load_json("comptes_entreprises.json") + comptes = _load_json("comptes_bancaires.json") + if ( + banque_nom not in entreprises + or self.entreprise.value.strip() not in entreprises[banque_nom] + ): + await interaction.response.send_message( + "❌ Compte entreprise introuvable.", ephemeral=True + ) + return + compte_ent = entreprises[banque_nom][self.entreprise.value.strip()] + if compte_ent.get("mdp") != self.mdp.value.strip(): + await interaction.response.send_message( + "❌ Mot de passe incorrect.", ephemeral=True + ) + return + if compte_ent["solde"] < montant_val: + await interaction.response.send_message( + "❌ Solde insuffisant.", ephemeral=True + ) + return + dest_store, dest_bq, dest_id, _ = _find_account_by_rib( + self.rib_dest.value.strip(), comptes, entreprises + ) + if not dest_store: + await interaction.response.send_message( + "❌ RIB destinataire introuvable.", ephemeral=True + ) + return + now = datetime.now().strftime("%d/%m/%Y %H:%M:%S") + compte_ent["solde"] -= montant_val + compte_ent.setdefault("transactions", []).append( + { + "date": now, + "type": "Virement", + "source": f"Vers {self.rib_dest.value}", + "montant": -montant_val, + } + ) + dest_store[dest_bq][dest_id]["solde"] += montant_val + dest_store[dest_bq][dest_id].setdefault("transactions", []).append( + { + "date": now, + "type": "Virement reçu", + "source": self.entreprise.value.strip(), + "montant": montant_val, + } + ) + _save_json("comptes_entreprises.json", entreprises) + _save_json("comptes_bancaires.json", comptes) + await interaction.response.send_message( + f"✅ Virement de **{montant_val:.2f}€** depuis " + f"**{self.entreprise.value.strip()}** vers `{self.rib_dest.value.strip()}`.", + ephemeral=True, + ) + + +class AjouterArgentModal(discord.ui.Modal, title="➕ Ajouter de l'Argent"): + rib = discord.ui.TextInput(label="RIB du compte", required=True, max_length=40) + montant = discord.ui.TextInput(label="Montant", required=True, max_length=10) + banque = discord.ui.TextInput( + label="Banque (optionnel)", required=False, max_length=50 + ) + + async def on_submit(self, interaction: discord.Interaction): + if not has_role(interaction.user, "banquier"): + await interaction.response.send_message( + "❌ Réservé aux banquiers.", ephemeral=True + ) + return + try: + montant_val = float(self.montant.value.strip()) + except ValueError: + await interaction.response.send_message( + "❌ Montant invalide.", ephemeral=True + ) + return + if montant_val <= 0: + await interaction.response.send_message( + "❌ Montant doit être > 0.", ephemeral=True + ) + return + banque_nom = self.banque.value.strip() if self.banque.value else None + comptes = _load_json("comptes_bancaires.json") + entreprises = _load_json("comptes_entreprises.json") + store, bq, uid, is_ent = _find_account_by_rib( + self.rib.value.strip(), comptes, entreprises, banque_nom + ) + if not store: + await interaction.response.send_message( + "❌ Aucun compte trouvé avec ce RIB.", ephemeral=True + ) + return + now = datetime.now().strftime("%d/%m/%Y %H:%M:%S") + store[bq][uid]["solde"] += montant_val + store[bq][uid].setdefault("transactions", []).append( + { + "date": now, + "type": "Dépôt", + "source": "Banque", + "montant": montant_val, + "emetteur": str(interaction.user.id), + "destinataire": uid, + } + ) + if is_ent: + _save_json("comptes_entreprises.json", entreprises) + else: + _save_json("comptes_bancaires.json", comptes) + try: + user = await interaction.client.fetch_user(int(uid)) + await user.send( + f"💰 Dépôt de **{montant_val:.2f}€** sur votre compte **{bq}**." + ) + except Exception: + pass + await interaction.response.send_message( + f"✅ **{montant_val:.2f}€** ajoutés au compte `{self.rib.value.strip()}` dans **{bq}**.", + ephemeral=True, + ) + + +class RetirerArgentModal(discord.ui.Modal, title="➖ Retirer de l'Argent"): + rib = discord.ui.TextInput(label="RIB du compte", required=True, max_length=40) + montant = discord.ui.TextInput(label="Montant", required=True, max_length=10) + banque = discord.ui.TextInput( + label="Banque (optionnel)", required=False, max_length=50 + ) + + async def on_submit(self, interaction: discord.Interaction): + if not has_role(interaction.user, "banquier"): + await interaction.response.send_message( + "❌ Réservé aux banquiers.", ephemeral=True + ) + return + try: + montant_val = float(self.montant.value.strip()) + except ValueError: + await interaction.response.send_message( + "❌ Montant invalide.", ephemeral=True + ) + return + banque_nom = self.banque.value.strip() if self.banque.value else None + comptes = _load_json("comptes_bancaires.json") + entreprises = _load_json("comptes_entreprises.json") + store, bq, uid, is_ent = _find_account_by_rib( + self.rib.value.strip(), comptes, entreprises, banque_nom + ) + if not store: + await interaction.response.send_message( + "❌ Aucun compte trouvé avec ce RIB.", ephemeral=True + ) + return + if store[bq][uid]["solde"] < montant_val: + await interaction.response.send_message( + "❌ Solde insuffisant.", ephemeral=True + ) + return + now = datetime.now().strftime("%d/%m/%Y %H:%M:%S") + store[bq][uid]["solde"] -= montant_val + store[bq][uid].setdefault("transactions", []).append( + { + "date": now, + "type": "Retrait", + "source": "Banque", + "montant": -montant_val, + "emetteur": str(interaction.user.id), + "destinataire": uid, + } + ) + if is_ent: + _save_json("comptes_entreprises.json", entreprises) + else: + _save_json("comptes_bancaires.json", comptes) + embed = discord.Embed( + title="💸 Retrait effectué", + description=( + f"💼 Banque : **{bq}**\n" + f"RIB : `{self.rib.value.strip()}`\n" + f"Montant : **{montant_val:.2f}€**" + ), + color=discord.Color.red(), + ) + await interaction.response.send_message(embed=embed, ephemeral=False) + + +class TransactionsModal(discord.ui.Modal, title="📜 Voir Transactions"): + def __init__(self, target_user=None): + super().__init__() + self.target_user = target_user + + async def on_submit(self, interaction: discord.Interaction): + if not has_role(interaction.user, "banquier"): + await interaction.response.send_message( + "❌ Réservé aux banquiers.", ephemeral=True + ) + return + member = self.target_user or interaction.user + uid_str = str(member.id) + comptes = _load_json("comptes_bancaires.json") + all_txs = [] + for banque, users in comptes.items(): + if uid_str in users: + for t in users[uid_str].get("transactions", []): + all_txs.append(t) + if not all_txs: + await interaction.response.send_message( + f"📜 {member.mention} n'a aucune transaction.", ephemeral=True + ) + return + embed = discord.Embed( + title=f"📜 Transactions de {member.display_name}", + color=discord.Color.gold(), + ) + for t in all_txs[-10:]: + embed.add_field( + name=f"📅 {t.get('date', 'N/A')}", + value=( + f"**Type**: {t.get('type', 'N/A')}\n" + f"💰 **Montant**: {t.get('montant', 0)}€\n" + f"📤 **Source**: {t.get('source', 'N/A')}" + ), + inline=False, + ) + embed.set_footer(text="10 dernières transactions") + await interaction.response.send_message(embed=embed, ephemeral=True) + + +# ═══════════════════════════════════════════════════════════════════ +# MODALS — GOUVERNEMENT +# ═══════════════════════════════════════════════════════════════════ + +class GouvVirerModal(discord.ui.Modal, title="💸 Virement Gouvernement"): + rib = discord.ui.TextInput( + label="RIB destinataire", required=True, max_length=40 + ) + montant = discord.ui.TextInput(label="Montant", required=True, max_length=10) + banque = discord.ui.TextInput( + label="Banque (optionnel)", required=False, max_length=50 + ) + + async def on_submit(self, interaction: discord.Interaction): + if not has_role(interaction.user, "gouvernement"): + await interaction.response.send_message( + "❌ Réservé au gouvernement.", ephemeral=True + ) + return + try: + montant_val = float(self.montant.value.strip()) + except ValueError: + await interaction.response.send_message( + "❌ Montant invalide.", ephemeral=True + ) + return + if montant_val <= 0: + await interaction.response.send_message( + "❌ Montant doit être > 0.", ephemeral=True + ) + return + banque_nom = self.banque.value.strip() if self.banque.value else None + comptes = _load_json("comptes_bancaires.json") + entreprises = _load_json("comptes_entreprises.json") + gouv = entreprises.get("Gouvernement", {}).get("GOUV") + if not gouv: + await interaction.response.send_message( + "❌ Compte Gouvernement introuvable.", ephemeral=True + ) + return + if gouv["solde"] < montant_val: + await interaction.response.send_message( + "❌ Fonds insuffisants.", ephemeral=True + ) + return + dest_store, dest_bq, dest_id, _ = _find_account_by_rib( + self.rib.value.strip(), comptes, entreprises, banque_nom + ) + if not dest_store: + await interaction.response.send_message( + "❌ RIB destinataire introuvable.", ephemeral=True + ) + return + now = datetime.now().strftime("%d/%m/%Y %H:%M:%S") + gouv["solde"] -= montant_val + gouv.setdefault("transactions", []).append( + { + "date": now, + "type": "Virement", + "source": "Gouvernement", + "montant": -montant_val, + "destinataire": self.rib.value.strip(), + } + ) + dest_store[dest_bq][dest_id]["solde"] += montant_val + dest_store[dest_bq][dest_id].setdefault("transactions", []).append( + { + "date": now, + "type": "Virement reçu", + "source": "Gouvernement", + "montant": montant_val, + } + ) + _save_json("comptes_entreprises.json", entreprises) + _save_json("comptes_bancaires.json", comptes) + embed = discord.Embed( + title="🏛️ Virement Gouvernemental effectué", + description=( + f"RIB : `{self.rib.value.strip()}`\n" + f"Montant : **{montant_val:.2f}€**\n" + f"Banque : **{dest_bq}**" + ), + color=discord.Color.gold(), + ) + await interaction.response.send_message(embed=embed) + + +class RetraitGouvModal(discord.ui.Modal, title="🏦 Retrait Gouvernement"): + montant = discord.ui.TextInput(label="Montant", required=True, max_length=10) + + async def on_submit(self, interaction: discord.Interaction): + if not has_role(interaction.user, "gouvernement"): + await interaction.response.send_message( + "❌ Réservé au gouvernement.", ephemeral=True + ) + return + try: + montant_val = float(self.montant.value.strip()) + except ValueError: + await interaction.response.send_message( + "❌ Montant invalide.", ephemeral=True + ) + return + entreprises = _load_json("comptes_entreprises.json") + gouv = entreprises.get("Gouvernement", {}).get("GOUV") + if not gouv: + await interaction.response.send_message( + "❌ Compte Gouvernement introuvable.", ephemeral=True + ) + return + if gouv["solde"] < montant_val: + await interaction.response.send_message( + "❌ Fonds insuffisants.", ephemeral=True + ) + return + gouv["solde"] -= montant_val + now = datetime.now().strftime("%d/%m/%Y %H:%M:%S") + gouv.setdefault("transactions", []).append( + { + "date": now, + "type": "Retrait", + "source": "Gouvernement", + "montant": -montant_val, + } + ) + _save_json("comptes_entreprises.json", entreprises) + embed = discord.Embed( + title="🏦 Retrait Gouvernement", + description=( + f"Montant : **{montant_val:.2f}€**\n" + f"Solde restant : **{gouv['solde']:.2f}€**" + ), + color=discord.Color.gold(), + ) + await interaction.response.send_message(embed=embed) + + +class VirementFBMModal(discord.ui.Modal, title="📤 Virement vers FBM"): + montant = discord.ui.TextInput(label="Montant", required=True, max_length=10) + banque = discord.ui.TextInput( + label="Banque (V'Toria's Credit Bank / NGC Bank)", + required=True, + max_length=50, + ) + + async def on_submit(self, interaction: discord.Interaction): + try: + montant_val = float(self.montant.value.strip()) + except ValueError: + await interaction.response.send_message( + "❌ Montant invalide.", ephemeral=True + ) + return + if montant_val <= 0: + await interaction.response.send_message( + "❌ Montant doit être > 0.", ephemeral=True + ) + return + banque_nom = self.banque.value.strip() + uid_str = str(interaction.user.id) + comptes = _load_json("comptes_bancaires.json") + entreprises = _load_json("comptes_entreprises.json") + if banque_nom not in comptes or uid_str not in comptes[banque_nom]: + await interaction.response.send_message( + "❌ Vous n'avez pas de compte dans cette banque.", ephemeral=True + ) + return + compte = comptes[banque_nom][uid_str] + if compte["solde"] < montant_val: + await interaction.response.send_message( + "❌ Solde insuffisant.", ephemeral=True + ) + return + 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") + compte["solde"] -= montant_val + compte.setdefault("transactions", []).append( + { + "date": now, + "type": "Virement", + "source": "Vers FBM", + "montant": -montant_val, + } + ) + entreprises["fbm"]["FBM"]["solde"] += montant_val + entreprises["fbm"]["FBM"].setdefault("transactions", []).append( + { + "date": now, + "type": "Virement reçu", + "source": interaction.user.name, + "montant": montant_val, + } + ) + _save_json("comptes_bancaires.json", comptes) + _save_json("comptes_entreprises.json", entreprises) + await interaction.response.send_message( + f"✅ Virement de **{montant_val:.2f}€** envoyé à **FBM** depuis **{banque_nom}**.", + ephemeral=True, + ) + + +class VirementURSAFFModal(discord.ui.Modal, title="📤 Virement vers URSAFF"): + montant = discord.ui.TextInput(label="Montant", required=True, max_length=10) + banque = discord.ui.TextInput( + label="Banque (V'Toria's Credit Bank / NGC Bank)", + required=True, + max_length=50, + ) + + async def on_submit(self, interaction: discord.Interaction): + try: + montant_val = float(self.montant.value.strip()) + except ValueError: + await interaction.response.send_message( + "❌ Montant invalide.", ephemeral=True + ) + return + if montant_val <= 0: + await interaction.response.send_message( + "❌ Montant doit être > 0.", ephemeral=True + ) + return + banque_nom = self.banque.value.strip() + uid_str = str(interaction.user.id) + comptes = _load_json("comptes_bancaires.json") + entreprises = _load_json("comptes_entreprises.json") + if banque_nom not in comptes or uid_str not in comptes[banque_nom]: + await interaction.response.send_message( + "❌ Vous n'avez pas de compte dans cette banque.", ephemeral=True + ) + return + compte = comptes[banque_nom][uid_str] + if compte["solde"] < montant_val: + await interaction.response.send_message( + "❌ Solde insuffisant.", ephemeral=True + ) + return + 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") + compte["solde"] -= montant_val + compte.setdefault("transactions", []).append( + { + "date": now, + "type": "Virement", + "source": "Vers URSAFF", + "montant": -montant_val, + } + ) + entreprises["ursaff"]["URSAFF"]["solde"] += montant_val + entreprises["ursaff"]["URSAFF"].setdefault("transactions", []).append( + { + "date": now, + "type": "Virement reçu", + "source": interaction.user.name, + "montant": montant_val, + } + ) + _save_json("comptes_bancaires.json", comptes) + _save_json("comptes_entreprises.json", entreprises) + await interaction.response.send_message( + f"✅ Virement de **{montant_val:.2f}€** envoyé à **URSAFF** depuis **{banque_nom}**.", + ephemeral=True, + ) + + +# ═══════════════════════════════════════════════════════════════════ +# MODALS — IDENTITE +# ═══════════════════════════════════════════════════════════════════ + +class CreerCarteModal(discord.ui.Modal, title="🪪 Créer une Carte d'Identité"): + utilisateur = discord.ui.TextInput( + label="ID Utilisateur", required=True, max_length=20 + ) + nom_complet = discord.ui.TextInput( + label="Nom complet (Ex: Dupont Jean)", required=True, max_length=100 + ) + date_naissance = discord.ui.TextInput( + label="Date de naissance", required=True, max_length=20 + ) + lieu_naissance = discord.ui.TextInput( + label="Lieu de naissance", required=True, max_length=50 + ) + sexe = discord.ui.TextInput( + label="Sexe (Homme/Femme/Autre)", required=True, max_length=6 + ) + + async def on_submit(self, interaction: discord.Interaction): + if not has_role(interaction.user, "gouvernement"): + await interaction.response.send_message( + "❌ Réservé au gouvernement.", ephemeral=True + ) + return + uid = self.utilisateur.value.strip() + parts = self.nom_complet.value.strip().split(maxsplit=1) + nom_val = parts[0].upper() if parts else "" + prenom_val = parts[1].capitalize() if len(parts) > 1 else "" + dm = DataManager() + date_creation = datetime.now() + date_exp = (date_creation + timedelta(days=730)).strftime("%d/%m/%Y") + dm.identities[uid] = { + "nom": nom_val, + "prenom": prenom_val, + "date_naissance": self.date_naissance.value.strip(), + "lieu_naissance": self.lieu_naissance.value.strip(), + "sexe": self.sexe.value.strip().capitalize(), + "date_expiration": date_exp, + } + dm.save_data() + await interaction.response.send_message( + f"✅ Carte d'identité créée pour <@{uid}>.", ephemeral=False + ) + + +class VoirCarteModal(discord.ui.Modal, title="🪪 Voir une Carte d'Identité"): + def __init__(self, target_user=None): + super().__init__() + self.target_user = target_user + + async def on_submit(self, interaction: discord.Interaction): + if self.target_user: + uid = str(self.target_user.id) + member = self.target_user + else: + uid = str(interaction.user.id) + member = interaction.user + dm = DataManager() + identity = dm.identities.get(uid) + if not identity: + await interaction.response.send_message( + "❌ Aucune carte trouvée.", ephemeral=True + ) + return + try: + from utils.card_generator import CardGenerator + + cg = CardGenerator() + card_buffer = await cg.create_identity_card(member, identity) + file = discord.File(card_buffer, filename="carte_identite.png") + embed = discord.Embed(title="🪪 Carte d'identité", color=C_ID) + embed.set_image(url="attachment://carte_identite.png") + await interaction.response.send_message(file=file, embed=embed) + except Exception as e: + await interaction.response.send_message( + f"❌ Erreur : {e}", ephemeral=True + ) + + +class PermisModal(discord.ui.Modal, title="🚗 Ajouter un Permis"): + membre = discord.ui.TextInput(label="ID du membre", required=True, max_length=20) + + async def on_submit(self, interaction: discord.Interaction): + if not has_role(interaction.user, "auto_ecole"): + await interaction.response.send_message( + "❌ Réservé à l'auto-école.", ephemeral=True + ) + return + try: + member = await interaction.guild.fetch_member( + int(self.membre.value.strip()) + ) + role = get_guild_role(interaction.guild, "permis") + if not role: + await interaction.response.send_message( + "❌ Rôle permis introuvable.", ephemeral=True + ) + return + await member.add_roles(role) + await interaction.response.send_message( + f"✅ Permis attribué à {member.mention}.", ephemeral=True + ) + except (ValueError, discord.NotFound): + await interaction.response.send_message( + "❌ Membre introuvable.", ephemeral=True + ) + + +class VisiteMedModal(discord.ui.Modal, title="🏥 Valider Visite Médicale"): + membre = discord.ui.TextInput(label="ID du membre", required=True, max_length=20) + + async def on_submit(self, interaction: discord.Interaction): + if not has_role(interaction.user, "ems"): + await interaction.response.send_message( + "❌ Réservé aux EMS.", ephemeral=True + ) + return + try: + member = await interaction.guild.fetch_member( + int(self.membre.value.strip()) + ) + role = get_guild_role(interaction.guild, "visite_medicale") + if not role: + await interaction.response.send_message( + "❌ Rôle visite médicale introuvable.", ephemeral=True + ) + return + await member.add_roles(role) + fichier = "visite_médicale.json" + data = _load_json(fichier) + expiration = (datetime.now() + timedelta(days=30)).strftime( + "%Y-%m-%d %H:%M:%S" + ) + data[str(member.id)] = {"role_id": role.id, "expiration": expiration} + _save_json(fichier, data) + await interaction.response.send_message( + f"✅ Visite médicale validée pour {member.mention} jusqu'au {expiration}.", + ephemeral=True, + ) + except (ValueError, discord.NotFound): + await interaction.response.send_message( + "❌ Membre introuvable.", ephemeral=True + ) + + +# ═══════════════════════════════════════════════════════════════════ +# MODALS — TICKETS & MESSAGES +# ═══════════════════════════════════════════════════════════════════ + +class MessageModal(discord.ui.Modal, title="✉️ Envoyer un MP"): + membre = discord.ui.TextInput( + label="ID du membre", required=True, max_length=20 + ) + message = discord.ui.TextInput( + label="Message", + style=discord.TextStyle.paragraph, + required=True, + max_length=2000, + ) + + async def on_submit(self, interaction: discord.Interaction): + if not interaction.user.guild_permissions.administrator: + await interaction.response.send_message( + "❌ Réservé aux administrateurs.", ephemeral=True + ) + return + try: + user = await interaction.client.fetch_user(int(self.membre.value.strip())) + await user.send(self.message.value) + await interaction.response.send_message( + f"✅ MP envoyé à {user.name}.", ephemeral=True + ) + except (ValueError, discord.NotFound): + await interaction.response.send_message( + "❌ Membre introuvable.", ephemeral=True + ) + except discord.Forbidden: + await interaction.response.send_message( + "❌ Impossible d'envoyer le MP.", ephemeral=True + ) + + +class MessageTousModal(discord.ui.Modal, title="📢 MP à Tous"): + message = discord.ui.TextInput( + label="Message", + style=discord.TextStyle.paragraph, + required=True, + max_length=2000, + ) + + async def on_submit(self, interaction: discord.Interaction): + if not interaction.user.guild_permissions.administrator: + await interaction.response.send_message( + "❌ Réservé aux administrateurs.", ephemeral=True + ) + return + await interaction.response.defer(ephemeral=True) + sent, failed = 0, 0 + for member in interaction.guild.members: + if not member.bot: + try: + await member.send(self.message.value) + sent += 1 + except Exception: + failed += 1 + await interaction.followup.send( + f"✅ MP envoyés : {sent} | Échecs : {failed}", ephemeral=True + ) + + +# ═══════════════════════════════════════════════════════════════════ +# MODALS — SESSIONS +# ═══════════════════════════════════════════════════════════════════ + +class AnnonceSessionModal(discord.ui.Modal, title="📢 Annoncer une Session"): + date = discord.ui.TextInput( + label="Date (ex: Vendredi 20 Juin à 20h45)", + required=True, + max_length=100, + ) + reminder1 = discord.ui.TextInput( + label="1er rappel (ex: Jeudi 19 Juin à 19h)", + required=True, + max_length=100, + ) + reminder2 = discord.ui.TextInput( + label="2e rappel (ex: Vendredi 20 Juin à 15h)", + required=True, + max_length=100, + ) + + async def on_submit(self, interaction: discord.Interaction): + import dateparser + from datetime import timezone + + session_date = dateparser.parse( + self.date.value, + languages=["fr"], + settings={ + "TIMEZONE": "Europe/Paris", + "RETURN_AS_TIMEZONE_AWARE": True, + }, + ) + r1 = dateparser.parse( + self.reminder1.value, + languages=["fr"], + settings={ + "TIMEZONE": "Europe/Paris", + "RETURN_AS_TIMEZONE_AWARE": True, + }, + ) + r2 = dateparser.parse( + self.reminder2.value, + languages=["fr"], + settings={ + "TIMEZONE": "Europe/Paris", + "RETURN_AS_TIMEZONE_AWARE": True, + }, + ) + if not session_date or not r1 or not r2: + await interaction.response.send_message( + "❌ Une des dates n'a pas été comprise.", ephemeral=True + ) + return + session_date = session_date.astimezone(timezone.utc) + r1 = r1.astimezone(timezone.utc) + r2 = r2.astimezone(timezone.utc) + ping_role_id = get_role_id(interaction.guild.id, "citoyen") + message = ( + "# <:VV:1339237574593609780> - **SESSION VICTORIA'S VICE**\n\n" + "```📅 - HORAIRE DE LA SESSION```\n" + f"> - Nous vous donnons rendez-vous le **{self.date.value}** " + "pour une **session officielle** sur Victoria's Vice. 🗓️\n" + "---\n" + "```🤔 - COMMENT REJOINDRE LA SESSION ?```\n" + "> - Pour lancer la Map, entrez le code sur la barre de recherche de Fortnite.\n" + "> - **`CODE DE LA MAP`** <:arrow:1297491509201080362> [0986-1836-1625]\n" + "> - **`LIEN DIRECT`** <:arrow:1297491509201080362> " + "\n" + "---\n" + "```🖐 - SEREZ-VOUS PRÉSENT(E) ?```\n" + "> - Je serai présent : <:check:1297296859206778911> **(0)**\n" + "> - Je serai absent : <:uncheck:1297296882778902598> **(0)**\n" + "> - Je serai en retard : ⏰ **(0)**\n" + "---\n" + f"> - **`PING`** : <@&{ping_role_id}>" + ) + await interaction.response.send_message(content=message) + msg = await interaction.original_response() + from commandes.annonces_sessions import PresenceView + + await msg.edit(view=PresenceView(msg.id)) + + +class ListePresenceModal(discord.ui.Modal, title="📋 Liste des Présences"): + message_id = discord.ui.TextInput( + label="ID du message d'annonce", required=True, max_length=20 + ) + + async def on_submit(self, interaction: discord.Interaction): + from commandes.annonces_sessions import presence_data + + try: + mid = int(self.message_id.value.strip()) + except ValueError: + await interaction.response.send_message( + "❌ ID 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, + ) + + +# ═══════════════════════════════════════════════════════════════════ +# MODALS — CONFIG +# ═══════════════════════════════════════════════════════════════════ + +class ConfigRoleModal(discord.ui.Modal, title="🎭 Définir un Rôle"): + cle = discord.ui.TextInput( + label="Clé (banquier, citoyen, ems...)", + required=True, + max_length=30, + ) + role_id = discord.ui.TextInput( + label="ID du rôle Discord", required=True, max_length=20 + ) + + async def on_submit(self, interaction: discord.Interaction): + if not interaction.user.guild_permissions.manage_guild: + await interaction.response.send_message( + "❌ Permission requise.", ephemeral=True + ) + return + cle = self.cle.value.strip().lower() + try: + rid = int(self.role_id.value.strip()) + except ValueError: + await interaction.response.send_message( + "❌ ID invalide.", ephemeral=True + ) + return + role = interaction.guild.get_role(rid) + if not role: + await interaction.response.send_message( + "❌ Rôle introuvable.", ephemeral=True + ) + return + set_config_value(interaction.guild.id, "roles", cle, rid) + await interaction.response.send_message( + f"✅ Rôle **{ROLE_LABELS.get(cle, cle)}** → {role.mention}", + ephemeral=True, + ) + + +class ConfigChannelModal(discord.ui.Modal, title="📢 Définir un Salon"): + cle = discord.ui.TextInput( + label="Clé (welcome, candidature...)", + required=True, + max_length=30, + ) + channel_id = discord.ui.TextInput( + label="ID du salon Discord", required=True, max_length=20 + ) + + async def on_submit(self, interaction: discord.Interaction): + if not interaction.user.guild_permissions.manage_guild: + await interaction.response.send_message( + "❌ Permission requise.", ephemeral=True + ) + return + cle = self.cle.value.strip().lower() + try: + cid = int(self.channel_id.value.strip()) + except ValueError: + await interaction.response.send_message( + "❌ ID invalide.", ephemeral=True + ) + return + ch = interaction.guild.get_channel(cid) + if not ch: + await interaction.response.send_message( + "❌ Salon introuvable.", ephemeral=True + ) + return + set_config_value(interaction.guild.id, "channels", cle, cid) + await interaction.response.send_message( + f"✅ Salon **{CHANNEL_LABELS.get(cle, cle)}** → {ch.mention}", + ephemeral=True, + ) + + +class ConfigCategoryModal(discord.ui.Modal, title="📁 Définir une Catégorie"): + cle = discord.ui.TextInput(label="Clé (ticket)", required=True, max_length=30) + category_id = discord.ui.TextInput( + label="ID de la catégorie Discord", required=True, max_length=20 + ) + + async def on_submit(self, interaction: discord.Interaction): + if not interaction.user.guild_permissions.manage_guild: + await interaction.response.send_message( + "❌ Permission requise.", ephemeral=True + ) + return + cle = self.cle.value.strip().lower() + try: + cid = int(self.category_id.value.strip()) + except ValueError: + await interaction.response.send_message( + "❌ ID invalide.", ephemeral=True + ) + return + cat = interaction.guild.get_channel(cid) + if not cat: + await interaction.response.send_message( + "❌ Catégorie introuvable.", ephemeral=True + ) + return + set_config_value(interaction.guild.id, "categories", cle, cid) + await interaction.response.send_message( + f"✅ Catégorie **{CATEGORY_LABELS.get(cle, cle)}** → **{cat.name}**", + ephemeral=True, + ) + + +# ═══════════════════════════════════════════════════════════════════ +# MODALS — UTILITAIRES +# ═══════════════════════════════════════════════════════════════════ + +class RollModal(discord.ui.Modal, title="🎲 Lancer un Dé"): + minimum = discord.ui.TextInput( + label="Valeur minimale", required=True, max_length=5 + ) + maximum = discord.ui.TextInput( + label="Valeur maximale", required=True, max_length=5 + ) + raison = discord.ui.TextInput(label="Raison", required=True, max_length=100) + + async def on_submit(self, interaction: discord.Interaction): + try: + min_val = int(self.minimum.value.strip()) + max_val = int(self.maximum.value.strip()) + except ValueError: + await interaction.response.send_message( + "❌ Valeurs invalides.", ephemeral=True + ) + return + if min_val > max_val: + await interaction.response.send_message( + "❌ Min > Max.", ephemeral=True + ) + return + result = random.randint(min_val, max_val) + embed = discord.Embed( + title="🎲 Résultat du dé", + description=( + f"**{result}** (entre {min_val} et {max_val})\n" + f"*Raison : {self.raison.value.strip()}*" + ), + color=C_FUN, + ) + await interaction.response.send_message(embed=embed) + + +# ═══════════════════════════════════════════════════════════════════ +# PANELS — VUES +# ═══════════════════════════════════════════════════════════════════ + +def _btn(label, style, cid, cb): + b = discord.ui.Button(label=label, style=style, custom_id=cid) + b.callback = cb + return b + + +def _back_btn(cid, cb): + return _btn("◀ Retour", discord.ButtonStyle.secondary, cid, cb) + + +# ─── MON COMPTE (tout le monde, banquiers ont UserSelect) ─────── + +class ComptePanelView(discord.ui.LayoutView): + def __init__(self, author, bot): + super().__init__(timeout=300) + self.author = author + self.bot_ref = bot + self.selected_user = None + + is_banquier = has_role(author, "banquier") + target_label = "👤 **Vous-même**" + + items = [ + discord.ui.TextDisplay("## 💰 Mes Comptes"), + discord.ui.TextDisplay( + "Consultez vos comptes, faites des virements et gérez vos abonnements." + ), + discord.ui.Separator(), + ] + + if is_banquier: + items.append(discord.ui.ActionRow(TargetUserSelect())) + items.append( + discord.ui.TextDisplay("* Sélectionnez un utilisateur ci-dessus pour opérer sur son compte." + )) + items.append(discord.ui.Separator()) + + items.extend([ + discord.ui.ActionRow( + _btn("💰 Voir Compte", discord.ButtonStyle.primary, "cp_voir", self._voir), + _btn("📤 Virement", discord.ButtonStyle.primary, "cp_vir", self._vir), + _btn("🔄 Abonnement", discord.ButtonStyle.primary, "cp_abo", self._abo), + ), + discord.ui.ActionRow( + _btn("🏛️ Vir. Gouvernement", discord.ButtonStyle.primary, "cp_vg", self._vg), + _btn("🏢 Vir. FBM", discord.ButtonStyle.primary, "cp_vfbm", self._vfbm), + _btn("🏢 Vir. URSAFF", discord.ButtonStyle.primary, "cp_vurs", self._vurs), + ), + discord.ui.Separator(), + discord.ui.ActionRow(_back_btn("back_cp", self._back)), + ]) + + self.add_item(discord.ui.Container( + *items, + accent_colour=discord.Colour(C_BANK), + )) + + def _target(self, interaction): + return self.selected_user or interaction.user + + async def _back(self, interaction): + await interaction.response.edit_message( + view=MainPanelView(interaction.user, self.bot_ref) + ) + + async def _voir(self, interaction): + await interaction.response.send_modal( + CompteModal(target_user=self._target(interaction)) + ) + + async def _vir(self, interaction): + await interaction.response.send_modal(VirementModal()) + + async def _abo(self, interaction): + await interaction.response.send_modal(AbonnementModal()) + + async def _vg(self, interaction): + await interaction.response.send_modal(VirementModal()) + + async def _vfbm(self, interaction): + await interaction.response.send_modal(VirementFBMModal()) + + async def _vurs(self, interaction): + await interaction.response.send_modal(VirementURSAFFModal()) + + +# ─── OPERATIONS BANQUIER (banquiers uniquement) ────────────────── + +class BanquierPanelView(discord.ui.LayoutView): + def __init__(self, author, bot): + super().__init__(timeout=300) + self.author = author + self.bot_ref = bot + self.selected_user = None + + items = [ + discord.ui.TextDisplay("## 🏦 Opérations Banquier"), + discord.ui.TextDisplay( + "Ouvrez des comptes, ajoutez/retirez de l'argent et consultez les transactions." + ), + discord.ui.Separator(), + discord.ui.ActionRow(TargetUserSelect(custom_id="bp_usel")), + discord.ui.Separator(), + discord.ui.ActionRow( + _btn("🏦 Ouvrir Compte", discord.ButtonStyle.success, "bp_ouvrir", self._ouvrir), + _btn("🏢 Ouvrir Compte ENT", discord.ButtonStyle.success, "bp_ouvrir_ent", self._ouvrir_ent), + ), + discord.ui.ActionRow( + _btn("➕ Ajouter Argent", discord.ButtonStyle.primary, "bp_ajouter", self._ajouter), + _btn("➖ Retirer Argent", discord.ButtonStyle.danger, "bp_retirer", self._retirer), + ), + discord.ui.ActionRow( + _btn("🏦 Vir. Banquier", discord.ButtonStyle.primary, "bp_vir_bq", self._vir_bq), + _btn("🏢 Vir. Banquier ENT", discord.ButtonStyle.primary, "bp_vir_bq_ent", self._vir_bq_ent), + ), + discord.ui.ActionRow( + _btn("📜 Transactions", discord.ButtonStyle.secondary, "bp_tx", self._tx), + ), + discord.ui.Separator(), + discord.ui.ActionRow(_back_btn("back_bp", self._back)), + ] + + self.add_item(discord.ui.Container( + *items, + accent_colour=discord.Colour(C_BANQ_OP), + )) + + def _target(self, interaction): + return self.selected_user or interaction.user + + async def _back(self, interaction): + await interaction.response.edit_message( + view=MainPanelView(interaction.user, self.bot_ref) + ) + + async def _ouvrir(self, interaction): + await interaction.response.send_modal( + OuvrirCompteSimpleModal(target_user=self._target(interaction)) + ) + + async def _ouvrir_ent(self, interaction): + await interaction.response.send_modal(OuvrirCompteEntModal()) + + async def _ajouter(self, interaction): + await interaction.response.send_modal(AjouterArgentModal()) + + async def _retirer(self, interaction): + await interaction.response.send_modal(RetirerArgentModal()) + + async def _vir_bq(self, interaction): + await interaction.response.send_modal(VirementBanquierModal()) + + async def _vir_bq_ent(self, interaction): + await interaction.response.send_modal(VirementBanquierEntModal()) + + async def _tx(self, interaction): + await interaction.response.send_modal( + TransactionsModal(target_user=self._target(interaction)) + ) + + +# ─── GOUVERNEMENT (gouvernement uniquement) ───────────────────── + +class GouvernementPanelView(discord.ui.LayoutView): + def __init__(self, author, bot): + super().__init__(timeout=300) + self.author = author + self.bot_ref = bot + + items = [ + discord.ui.TextDisplay("## 🏛️ Gouvernement"), + discord.ui.TextDisplay( + "Comptes étatiques, FBM, URSAFF, Banque Nationale." + ), + discord.ui.Separator(), + discord.ui.ActionRow( + _btn("🏛️ Compte Gouv", discord.ButtonStyle.primary, "gp_cg", self._cg), + _btn("💸 Virement Gouv", discord.ButtonStyle.primary, "gp_vg", self._vg), + _btn("🏦 Retrait Gouv", discord.ButtonStyle.danger, "gp_rg", self._rg), + ), + discord.ui.ActionRow( + _btn("📄 Compte FBM", discord.ButtonStyle.primary, "gp_cfbm", self._cfbm), + _btn("📤 Virement FBM", discord.ButtonStyle.primary, "gp_vfbm", self._vfbm), + ), + discord.ui.ActionRow( + _btn("📄 Compte URSAFF", discord.ButtonStyle.primary, "gp_curs", self._curs), + _btn("📤 Virement URSAFF", discord.ButtonStyle.primary, "gp_vurs", self._vurs), + ), + discord.ui.ActionRow( + _btn("🏦 Banque Nationale", discord.ButtonStyle.primary, "gp_bn", self._bn), + ), + discord.ui.Separator(), + discord.ui.ActionRow(_back_btn("back_gp", self._back)), + ] + + self.add_item(discord.ui.Container( + *items, + accent_colour=discord.Colour(C_GOV), + )) + + async def _back(self, interaction): + await interaction.response.edit_message( + view=MainPanelView(interaction.user, self.bot_ref) + ) + + async def _cg(self, interaction): + if not has_role(interaction.user, "gouvernement"): + await interaction.response.send_message( + "❌ Réservé au gouvernement.", ephemeral=True + ) + return + entreprises = _load_json("comptes_entreprises.json") + gouv = entreprises.get("Gouvernement", {}).get("GOUV") + if not gouv: + await interaction.response.send_message( + "❌ Compte Gouvernement introuvable.", ephemeral=True + ) + return + solde = gouv.get("solde", 0) + txs = gouv.get("transactions", []) + embed = discord.Embed( + title="🏛️ Compte Gouvernement", + color=discord.Color.dark_gold(), + ) + embed.add_field(name="💰 Solde", value=f"{solde:.2f}€", inline=False) + for tx in txs[-5:]: + embed.add_field( + name=f"📄 {tx.get('date', 'N/A')}", + value=( + f"**{tx.get('type', 'N/A')}** — " + f"{tx.get('montant', 0)}€ — {tx.get('source', 'N/A')}" + ), + inline=False, + ) + if txs: + embed.set_footer(text=f"{len(txs)} transaction(s) au total") + await interaction.response.send_message(embed=embed, ephemeral=True) + + async def _vg(self, interaction): + await interaction.response.send_modal(GouvVirerModal()) + + async def _rg(self, interaction): + await interaction.response.send_modal(RetraitGouvModal()) + + async def _cfbm(self, interaction): + if not has_role(interaction.user, "gouvernement"): + await interaction.response.send_message( + "❌ Réservé au gouvernement.", ephemeral=True + ) + return + entreprises = _load_json("comptes_entreprises.json") + fbm = entreprises.get("fbm", {}).get("FBM", {}) + solde = fbm.get("solde", 0) + txs = fbm.get("transactions", []) + embed = discord.Embed( + title="📄 Compte FBM", color=discord.Color.orange() + ) + embed.add_field(name="💰 Solde", value=f"{solde:.2f}€", inline=False) + for tx in txs[-5:]: + embed.add_field( + name=f"📄 {tx.get('date', 'N/A')}", + value=f"**{tx.get('type', 'N/A')}** — {tx.get('montant', 0)}€", + inline=False, + ) + await interaction.response.send_message(embed=embed, ephemeral=True) + + async def _vfbm(self, interaction): + await interaction.response.send_modal(VirementFBMModal()) + + async def _curs(self, interaction): + if not has_role(interaction.user, "gouvernement"): + await interaction.response.send_message( + "❌ Réservé au gouvernement.", ephemeral=True + ) + return + entreprises = _load_json("comptes_entreprises.json") + urs = entreprises.get("ursaff", {}).get("URSAFF", {}) + solde = urs.get("solde", 0) + txs = urs.get("transactions", []) + embed = discord.Embed( + title="📄 Compte URSAFF", color=discord.Color.orange() + ) + embed.add_field(name="💰 Solde", value=f"{solde:.2f}€", inline=False) + for tx in txs[-5:]: + embed.add_field( + name=f"📄 {tx.get('date', 'N/A')}", + value=f"**{tx.get('type', 'N/A')}** — {tx.get('montant', 0)}€", + inline=False, + ) + await interaction.response.send_message(embed=embed, ephemeral=True) + + async def _vurs(self, interaction): + await interaction.response.send_modal(VirementURSAFFModal()) + + async def _bn(self, interaction): + if not has_role(interaction.user, "gouvernement"): + await interaction.response.send_message( + "❌ Réservé au gouvernement.", ephemeral=True + ) + return + entreprises = _load_json("comptes_entreprises.json") + bn = entreprises.get("banque_nationale", {}).get("BN", {}) + solde = bn.get("solde", 0) + txs = bn.get("transactions", []) + embed = discord.Embed( + title="🏦 Banque Nationale", color=discord.Color.blue() + ) + embed.add_field(name="💰 Solde", value=f"{solde:.2f}€", inline=False) + for tx in txs[-5:]: + embed.add_field( + name=f"📄 {tx.get('date', 'N/A')}", + value=f"**{tx.get('type', 'N/A')}** — {tx.get('montant', 0)}€", + inline=False, + ) + await interaction.response.send_message(embed=embed, ephemeral=True) + + +# ─── IDENTITE (tout le monde, boutons conditionnels) ──────────── + +class IdentitePanelView(discord.ui.LayoutView): + def __init__(self, author, bot): + super().__init__(timeout=300) + self.author = author + self.bot_ref = bot + + items = [ + discord.ui.TextDisplay("## 🪪 Identité"), + discord.ui.TextDisplay( + "Cartes d'identité, permis, visites médicales." + ), + discord.ui.Separator(), + discord.ui.ActionRow( + _btn("🪪 Ma Carte", discord.ButtonStyle.primary, "ip_carte", self._carte), + ), + ] + + if has_role(author, "gouvernement"): + items.append( + discord.ui.ActionRow( + _btn("🪪 Créer Carte", discord.ButtonStyle.success, "ip_ccar", self._ccar), + ) + ) + + action_items = [] + if has_role(author, "auto_ecole"): + action_items.append( + _btn("🚗 Permis", discord.ButtonStyle.primary, "ip_permis", self._permis) + ) + if has_role(author, "ems"): + action_items.append( + _btn("🏥 Visite Médicale", discord.ButtonStyle.primary, "ip_vismed", self._vismed) + ) + if action_items: + items.append(discord.ui.ActionRow(*action_items)) + + items.extend([ + discord.ui.Separator(), + discord.ui.ActionRow(_back_btn("back_ip", self._back)), + ]) + + self.add_item(discord.ui.Container( + *items, + accent_colour=discord.Colour(C_ID), + )) + + async def _back(self, interaction): + await interaction.response.edit_message( + view=MainPanelView(interaction.user, self.bot_ref) + ) + + async def _carte(self, interaction): + uid = str(interaction.user.id) + dm = DataManager() + identity = dm.identities.get(uid) + if not identity: + await interaction.response.send_message( + "❌ Aucune carte trouvée.", ephemeral=True + ) + return + try: + from utils.card_generator import CardGenerator + + cg = CardGenerator() + card_buffer = await cg.create_identity_card(interaction.user, identity) + file = discord.File(card_buffer, filename="carte_identite.png") + embed = discord.Embed(title="🪪 Carte d'identité", color=C_ID) + embed.set_image(url="attachment://carte_identite.png") + await interaction.response.send_message(file=file, embed=embed) + except Exception as e: + await interaction.response.send_message( + f"❌ Erreur : {e}", ephemeral=True + ) + + async def _ccar(self, interaction): + await interaction.response.send_modal(CreerCarteModal()) + + async def _permis(self, interaction): + await interaction.response.send_modal(PermisModal()) + + async def _vismed(self, interaction): + await interaction.response.send_modal(VisiteMedModal()) + + +# ─── TICKETS & MESSAGES (admin uniquement) ────────────────────── + +class TicketsPanelView(discord.ui.LayoutView): + def __init__(self, author, bot): + super().__init__(timeout=300) + self.author = author + self.bot_ref = bot + + items = [ + discord.ui.TextDisplay("## 📩 Tickets & Messages"), + discord.ui.TextDisplay( + "Gestion des tickets et messages privés." + ), + discord.ui.Separator(), + discord.ui.ActionRow( + _btn("📑 Embed Ticket", discord.ButtonStyle.success, "tp_ticket", self._ticket), + _btn("✉️ Envoyer MP", discord.ButtonStyle.primary, "tp_mp", self._mp), + _btn("📢 MP à Tous", discord.ButtonStyle.danger, "tp_mptous", self._mptous), + ), + discord.ui.Separator(), + discord.ui.ActionRow(_back_btn("back_tp", self._back)), + ] + + self.add_item(discord.ui.Container( + *items, + accent_colour=discord.Colour(C_TICKET), + )) + + async def _back(self, interaction): + await interaction.response.edit_message( + view=MainPanelView(interaction.user, self.bot_ref) + ) + + async def _ticket(self, interaction): + if not interaction.user.guild_permissions.manage_guild: + await interaction.response.send_message( + "❌ Permission requise.", ephemeral=True + ) + return + embed = discord.Embed( + title="📩 Ticket Whitelist", + description=( + "Pour réaliser votre whitelist, ouvrez un ticket " + "et remplissez le formulaire." + ), + color=0x00FF00, + ) + from commandes.ticket import TicketView + + await interaction.channel.send(embed=embed, view=TicketView()) + await interaction.response.send_message( + "✅ Embed des tickets envoyé.", ephemeral=True + ) + + async def _mp(self, interaction): + await interaction.response.send_modal(MessageModal()) + + async def _mptous(self, interaction): + await interaction.response.send_modal(MessageTousModal()) + + +# ─── SESSIONS RP (tout le monde) ──────────────────────────────── + +class SessionsPanelView(discord.ui.LayoutView): + def __init__(self, author, bot): + super().__init__(timeout=300) + self.author = author + self.bot_ref = bot + + items = [ + discord.ui.TextDisplay("## 📅 Sessions RP"), + discord.ui.TextDisplay( + "Annonces de sessions et gestion des présences." + ), + discord.ui.Separator(), + discord.ui.ActionRow( + _btn("📢 Annoncer Session", discord.ButtonStyle.success, "sp_annonce", self._annonce), + _btn("📋 Liste Présences", discord.ButtonStyle.primary, "sp_liste", self._liste), + ), + discord.ui.Separator(), + discord.ui.ActionRow(_back_btn("back_sp", self._back)), + ] + + self.add_item(discord.ui.Container( + *items, + accent_colour=discord.Colour(C_SESSION), + )) + + async def _back(self, interaction): + await interaction.response.edit_message( + view=MainPanelView(interaction.user, self.bot_ref) + ) + + async def _annonce(self, interaction): + await interaction.response.send_modal(AnnonceSessionModal()) + + async def _liste(self, interaction): + await interaction.response.send_modal(ListePresenceModal()) + + +# ─── CONFIGURATION (admin uniquement) ─────────────────────────── + +class ConfigPanelView(discord.ui.LayoutView): + def __init__(self, author, bot): + super().__init__(timeout=300) + self.author = author + self.bot_ref = bot + + items = [ + discord.ui.TextDisplay("## ⚙️ Configuration"), + discord.ui.TextDisplay( + "Configurer les rôles, salons et catégories de ce serveur." + ), + discord.ui.Separator(), + discord.ui.ActionRow( + _btn("🎭 Définir Rôle", discord.ButtonStyle.primary, "cfg_role", self._crole), + _btn("📢 Définir Salon", discord.ButtonStyle.primary, "cfg_chan", self._cchan), + _btn("📁 Définir Catégorie", discord.ButtonStyle.primary, "cfg_cat", self._ccat), + ), + discord.ui.ActionRow( + _btn("👁️ Voir Config", discord.ButtonStyle.secondary, "cfg_voir", self._cvoir), + _btn("🗑️ Reset Config", discord.ButtonStyle.danger, "cfg_reset", self._creset), + ), + discord.ui.Separator(), + discord.ui.ActionRow(_back_btn("back_cfg", self._back)), + ] + + self.add_item(discord.ui.Container( + *items, + accent_colour=discord.Colour(C_CONFIG), + )) + + async def _back(self, interaction): + await interaction.response.edit_message( + view=MainPanelView(interaction.user, self.bot_ref) + ) + + async def _crole(self, interaction): + await interaction.response.send_modal(ConfigRoleModal()) + + async def _cchan(self, interaction): + await interaction.response.send_modal(ConfigChannelModal()) + + async def _ccat(self, interaction): + await interaction.response.send_modal(ConfigCategoryModal()) + + async def _cvoir(self, interaction): + if not interaction.user.guild_permissions.manage_guild: + await interaction.response.send_message( + "❌ Permission requise.", ephemeral=True + ) + return + config = load_guild_config(interaction.guild.id) + embed = discord.Embed( + title="⚙️ Configuration actuelle", color=C_CONFIG + ) + roles_text = "" + for key, label in ROLE_LABELS.items(): + rid = config.get("roles", {}).get(key) + if rid: + role = interaction.guild.get_role(int(rid)) + roles_text += f"> {label} : {role.mention if role else f'❌ {rid}'}\n" + else: + roles_text += f"> {label} : ⚠️ Non défini\n" + embed.add_field(name="🎭 Rôles", value=roles_text, inline=False) + channels_text = "" + for key, label in CHANNEL_LABELS.items(): + cid = config.get("channels", {}).get(key) + if cid: + ch = interaction.guild.get_channel(int(cid)) + channels_text += ( + f"> {label} : {ch.mention if ch else f'❌ {cid}'}\n" + ) + else: + channels_text += f"> {label} : ⚠️ Non défini\n" + embed.add_field(name="📢 Salons", value=channels_text, inline=False) + cats_text = "" + for key, label in CATEGORY_LABELS.items(): + catid = config.get("categories", {}).get(key) + if catid: + cat = interaction.guild.get_channel(int(catid)) + cats_text += ( + f"> {label} : **{cat.name}**\n" if cat + else f"> {label} : ❌ {catid}\n" + ) + else: + cats_text += f"> {label} : ⚠️ Non défini\n" + embed.add_field(name="📁 Catégories", value=cats_text, inline=False) + await interaction.response.send_message(embed=embed, ephemeral=True) + + async def _creset(self, interaction): + if not interaction.user.guild_permissions.administrator: + await interaction.response.send_message( + "❌ Réservé aux administrateurs.", ephemeral=True + ) + return + save_guild_config( + interaction.guild.id, + {"roles": {}, "channels": {}, "categories": {}}, + ) + await interaction.response.send_message( + "✅ Configuration réinitialisée.", ephemeral=True + ) + + +# ─── DIVERS (tout le monde) ───────────────────────────────────── + +class DiversPanelView(discord.ui.LayoutView): + def __init__(self, author, bot): + super().__init__(timeout=300) + self.author = author + self.bot_ref = bot + + items = [ + discord.ui.TextDisplay("## 🎲 Divers"), + discord.ui.TextDisplay("Lancer de dé et autres outils."), + discord.ui.Separator(), + discord.ui.ActionRow( + _btn("🎲 Lancer un Dé", discord.ButtonStyle.primary, "dp_roll", self._roll), + ), + ] + + if author.guild_permissions.administrator: + items.append( + discord.ui.ActionRow( + _btn("📊 Stats Bot", discord.ButtonStyle.secondary, "dp_stats", self._stats), + ) + ) + + items.extend([ + discord.ui.Separator(), + discord.ui.ActionRow(_back_btn("back_dp", self._back)), + ]) + + self.add_item(discord.ui.Container( + *items, + accent_colour=discord.Colour(C_FUN), + )) + + async def _back(self, interaction): + await interaction.response.edit_message( + view=MainPanelView(interaction.user, self.bot_ref) + ) + + async def _roll(self, interaction): + await interaction.response.send_modal(RollModal()) + + async def _stats(self, interaction): + from utils.stats_manager import get_stats_detailed + import io + + stats = get_stats_detailed() + if not stats: + await interaction.response.send_message( + "📊 Aucune statistique disponible.", ephemeral=True + ) + return + await interaction.response.send_message( + f"📊 **Statistiques du bot**\n```json\n{json.dumps(stats, indent=2, ensure_ascii=False)[:1800]}\n```", + ephemeral=True, + ) + + +# ═══════════════════════════════════════════════════════════════════ +# PANEL PRINCIPAL — MENU ADAPTATIF +# ═══════════════════════════════════════════════════════════════════ + +class CategorySelect(discord.ui.Select): + def __init__(self, author, bot): + options = [ + discord.SelectOption( + label="Mes Comptes", + description="Comptes, virements, abonnements", + emoji="💰", + value="compte", + ), + ] + if has_role(author, "banquier"): + options.append( + discord.SelectOption( + label="Opérations Banquier", + description="Ouvrir comptes, ajouter/retirer, transactions", + emoji="🏦", + value="banquier", + ) + ) + if has_role(author, "gouvernement"): + options.append( + discord.SelectOption( + label="Gouvernement", + description="Comptes étatiques, FBM, URSAFF", + emoji="🏛️", + value="gouvernement", + ) + ) + options.append( + discord.SelectOption( + label="Identité", + description="Cartes d'identité, permis, visite médicale", + emoji="🪪", + value="identite", + ) + ) + if author.guild_permissions.administrator: + options.append( + discord.SelectOption( + label="Tickets & Messages", + description="Tickets, MP privés", + emoji="📩", + value="tickets", + ) + ) + options.append( + discord.SelectOption( + label="Sessions RP", + description="Annonces, présences", + emoji="📅", + value="sessions", + ) + ) + options.append( + discord.SelectOption( + label="Divers", + description="Dé, stats", + emoji="🎲", + value="divers", + ) + ) + if author.guild_permissions.manage_guild: + options.append( + discord.SelectOption( + label="Configuration", + description="Configurer le bot", + emoji="⚙️", + value="config", + ) + ) + super().__init__( + placeholder="Sélectionnez une catégorie...", + options=options, + custom_id="pnl_catselect", + ) + self._bot = bot + + async def callback(self, interaction: discord.Interaction): + view_map = { + "compte": ComptePanelView, + "banquier": BanquierPanelView, + "gouvernement": GouvernementPanelView, + "identite": IdentitePanelView, + "tickets": TicketsPanelView, + "sessions": SessionsPanelView, + "config": ConfigPanelView, + "divers": DiversPanelView, + } + cls = view_map.get(self.values[0]) + if cls: + await interaction.response.edit_message( + view=cls(interaction.user, self._bot) + ) + + +class MainPanelView(discord.ui.LayoutView): + def __init__(self, author, bot): + super().__init__(timeout=300) + self.author = author + self.bot_ref = bot + self.message = None + + role_parts = [] + if has_role(author, "banquier"): + role_parts.append("🏦 Banquier") + if has_role(author, "gouvernement"): + role_parts.append("🏛️ Gouvernement") + if has_role(author, "ems"): + role_parts.append("🏥 EMS") + if has_role(author, "auto_ecole"): + role_parts.append("🚗 Auto-école") + if author.guild_permissions.administrator: + role_parts.append("👑 Admin") + role_text = " • ".join(role_parts) if role_parts else "👤 Citoyen" + + self.add_item(discord.ui.Container( + discord.ui.TextDisplay("## 🏦 Victoria's Vice — Panel de Commandes"), + discord.ui.TextDisplay(f"**Vos rôles :** {role_text}"), + discord.ui.TextDisplay( + "Sélectionnez une catégorie ci-dessous pour accéder aux commandes." + ), + discord.ui.Separator(), + discord.ui.ActionRow( + CategorySelect(author, bot) + ), + accent_colour=discord.Colour(C_MAIN), + )) + + async def on_timeout(self): + if self.message: + try: + await self.message.edit( + content="⏰ Panel expiré. Utilisez `/panel` pour le rouvrir.", + view=None, + ) + except discord.HTTPException: + pass + + +# ═══════════════════════════════════════════════════════════════════ +# COG +# ═══════════════════════════════════════════════════════════════════ + +class Panel(commands.Cog): + def __init__(self, bot): + self.bot = bot + + @app_commands.command( + name="panel", description="Ouvre le panneau de commandes du bot" + ) + async def panel(self, interaction: discord.Interaction): + view = MainPanelView(interaction.user, self.bot) + await interaction.response.send_message(view=view, ephemeral=False) + view.message = await interaction.original_response() + + +async def setup(bot): + await bot.add_cog(Panel(bot)) diff --git a/commandes/pret.py b/commandes/pret.py deleted file mode 100644 index 4deffa2..0000000 --- a/commandes/pret.py +++ /dev/null @@ -1,74 +0,0 @@ -import discord -from discord import app_commands -from discord.ext import commands -from utils.banque_manager import BanqueManager -from utils.guild_config import has_role - -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 has_role(interaction.user, "banquier"): - 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/retirer_argent.py b/commandes/retirer_argent.py deleted file mode 100644 index 860712d..0000000 --- a/commandes/retirer_argent.py +++ /dev/null @@ -1,146 +0,0 @@ -import discord -from discord import app_commands -from discord.ext import commands -import json -from datetime import datetime -from utils.guild_config import has_role - -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 has_role(interaction.user, "banquier"): - 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 deleted file mode 100644 index c6cf43c..0000000 --- a/commandes/retrait_gouv.py +++ /dev/null @@ -1,184 +0,0 @@ -import discord -from discord import app_commands -from discord.ext import commands -import json -from datetime import datetime - -from utils.guild_config import has_role -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 has_role(interaction.user, "gouvernement"): - 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/transactions.py b/commandes/transactions.py deleted file mode 100644 index d370f5b..0000000 --- a/commandes/transactions.py +++ /dev/null @@ -1,66 +0,0 @@ -import discord -from discord import app_commands -from discord.ext import commands -from utils.banque_manager import BanqueManager -from utils.guild_config import has_role - -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 has_role(member, "banquier"): - 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/virement.py b/commandes/virement.py index 0aef577..1a1f9f8 100644 --- a/commandes/virement.py +++ b/commandes/virement.py @@ -1,138 +1,71 @@ -import json import discord from discord import app_commands from discord.ext import commands -from datetime import datetime + +from utils.bank_manager import BankManager +from utils.rib import decoder_rib + +bank_manager = BankManager() + 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) + @app_commands.command(name="virement", description="Faire un virement à un autre compte via RIB") + @app_commands.describe( + rib_source="Votre RIB (compte source)", + rib_dest="RIB du destinataire", + montant="Montant à envoyer", + ) + async def virement( + self, + interaction: discord.Interaction, + rib_source: str, + rib_dest: str, + montant: float, + ): + if montant <= 0: + await interaction.response.send_message("❌ Le montant doit être supérieur à 0.", 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) + compte_source = await bank_manager.get_compte_by_rib(interaction.guild.id, rib_source) + if not compte_source: + await interaction.response.send_message("❌ RIB source introuvable.", ephemeral=True) + return + if compte_source["user_id"] != interaction.user.id: + await interaction.response.send_message("❌ Ce RIB ne vous appartient pas.", ephemeral=True) return - # Rechercher le destinataire (utilisateur ou entreprise) - destinataire_id = None - banque_destinataire = None - is_entreprise = False + try: + result = await bank_manager.virement( + guild_id=interaction.guild.id, + rib_source=rib_source, + rib_dest=rib_dest, + montant=montant, + emetteur_id=interaction.user.id, + ) - # 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 + msg = f"✅ Virement de **{result['montant_envoye']:.2f}€** effectué." + if result["tva_prelevee"] > 0: + msg += f"\n💰 TVA prélevée : **{result['tva_prelevee']:.2f}€** → Fisc." + msg += f"\n**Reçu par le destinataire :** {result['montant_recu']:.2f}€" - # 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 + await interaction.response.send_message(msg, ephemeral=True) - 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 + dest_compte = await bank_manager.get_compte_by_rib(interaction.guild.id, rib_dest) + if dest_compte and dest_compte["account_type"] == "particulier": + dest_user = await self.bot.fetch_user(dest_compte["user_id"]) + await dest_user.send( + f"📥 Vous avez reçu **{result['montant_recu']:.2f}€** de la part de **{interaction.user.display_name}**." + ) + except (discord.Forbidden, Exception): + pass + + except ValueError as e: + await interaction.response.send_message(f"❌ {e}", ephemeral=True) - 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 + await bot.add_cog(Virement(bot)) diff --git a/commandes/virement_banquier.py b/commandes/virement_banquier.py deleted file mode 100644 index 25de0a0..0000000 --- a/commandes/virement_banquier.py +++ /dev/null @@ -1,85 +0,0 @@ -import json -import discord -from discord import app_commands -from discord.ext import commands -from datetime import datetime -from utils.guild_config import has_role - -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 has_role(interaction.user, "banquier"): - 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 deleted file mode 100644 index 6ec1bb2..0000000 --- a/commandes/virement_banquier_entreprise.py +++ /dev/null @@ -1,121 +0,0 @@ -import discord -from discord import app_commands -from discord.ext import commands -import json -from datetime import datetime -from utils.guild_config import has_role - -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 has_role(interaction.user, "banquier"): - 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 deleted file mode 100644 index f156633..0000000 --- a/commandes/virement_entreprise.py +++ /dev/null @@ -1,149 +0,0 @@ -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 deleted file mode 100644 index dbef678..0000000 --- a/commandes/virement_fbm.py +++ /dev/null @@ -1,97 +0,0 @@ -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 deleted file mode 100644 index 9ee1015..0000000 --- a/commandes/virement_gouvernement.py +++ /dev/null @@ -1,86 +0,0 @@ -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 deleted file mode 100644 index 84dbe51..0000000 --- a/commandes/virement_ursaff.py +++ /dev/null @@ -1,93 +0,0 @@ -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/comptes_bancaires.json b/comptes_bancaires.json deleted file mode 100644 index 217bebc..0000000 --- a/comptes_bancaires.json +++ /dev/null @@ -1,2111 +0,0 @@ -{ - "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 deleted file mode 100644 index 4f16f50..0000000 --- a/comptes_entreprises.json +++ /dev/null @@ -1,492 +0,0 @@ -{ - "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/ressources/data.json b/ressources/data.json index 6f60fb7..4ea2a7b 100644 --- a/ressources/data.json +++ b/ressources/data.json @@ -1,12 +1,12 @@ { "identities": { "353942721997832195": { - "nom": "RICCI", - "prenom": "Aldo", - "date_naissance": "22/05/1967", - "lieu_naissance": "Victoria's Vice", + "nom": "JOHN", + "prenom": "Marston", + "date_naissance": "22/05/2008", + "lieu_naissance": "Victorias", "sexe": "Homme", - "date_expiration": "01/12/2026" + "date_expiration": "17/08/2028" }, "1096453463849828362": { "nom": "DE MONTROSE", diff --git a/utils/bank_manager.py b/utils/bank_manager.py new file mode 100644 index 0000000..ac6393a --- /dev/null +++ b/utils/bank_manager.py @@ -0,0 +1,500 @@ +import json +from datetime import datetime +from typing import Optional + +import aiosqlite + +from utils.db import get_db +from utils.rib import generer_rib, valider_rib, decoder_rib + +GUIICHET_PARTICULIER = 4 +GUIICHET_ENTREPRISE = 3 +GUIICHET_BANQUE = 2 +GUIICHET_INSTITUTION = 1 + + +class BankManager: + + async def creer_banque(self, guild_id: int, name: str, role_id: int) -> dict: + db = await get_db() + try: + cursor = await db.execute( + "SELECT COALESCE(MAX(establishment_code), 9999) FROM banks WHERE guild_id = ?", + (guild_id,), + ) + row = await cursor.fetchone() + last_code = row[0] + new_code = last_code + 1 + if new_code > 89999: + raise ValueError("Plus de codes d'établissement disponibles.") + + cursor = await db.execute( + "INSERT INTO banks (guild_id, name, role_id, establishment_code) VALUES (?, ?, ?, ?)", + (guild_id, name, role_id, new_code), + ) + await db.commit() + bank_id = cursor.lastrowid + return {"id": bank_id, "name": name, "role_id": role_id, "establishment_code": new_code} + finally: + await db.close() + + async def supprimer_banque(self, guild_id: int, bank_id: int) -> bool: + db = await get_db() + try: + cursor = await db.execute( + "DELETE FROM banks WHERE id = ? AND guild_id = ?", (bank_id, guild_id) + ) + await db.commit() + return cursor.rowcount > 0 + finally: + await db.close() + + async def get_banque(self, guild_id: int, bank_id: int) -> Optional[dict]: + db = await get_db() + try: + cursor = await db.execute( + "SELECT * FROM banks WHERE id = ? AND guild_id = ?", (bank_id, guild_id) + ) + row = await cursor.fetchone() + return dict(row) if row else None + finally: + await db.close() + + async def get_banque_by_role(self, guild_id: int, role_id: int) -> Optional[dict]: + db = await get_db() + try: + cursor = await db.execute( + "SELECT * FROM banks WHERE guild_id = ? AND role_id = ?", (guild_id, role_id) + ) + row = await cursor.fetchone() + return dict(row) if row else None + finally: + await db.close() + + async def get_banque_by_name(self, guild_id: int, name: str) -> Optional[dict]: + db = await get_db() + try: + cursor = await db.execute( + "SELECT * FROM banks WHERE guild_id = ? AND name = ?", (guild_id, name) + ) + row = await cursor.fetchone() + return dict(row) if row else None + finally: + await db.close() + + async def lister_banques(self, guild_id: int) -> list: + db = await get_db() + try: + cursor = await db.execute( + "SELECT * FROM banks WHERE guild_id = ? ORDER BY name", (guild_id,) + ) + rows = await cursor.fetchall() + return [dict(r) for r in rows] + finally: + await db.close() + + async def ouvrir_compte( + self, + guild_id: int, + bank_id: int, + user_id: int, + account_type: str, + tva_rate: float = 0.0, + password: str = None, + ) -> dict: + db = await get_db() + try: + cursor = await db.execute( + "SELECT * FROM banks WHERE id = ? AND guild_id = ?", (bank_id, guild_id) + ) + bank = await cursor.fetchone() + if not bank: + raise ValueError("Banque introuvable.") + + guichet = GUIICHET_ENTREPRISE if account_type == "entreprise" else GUIICHET_PARTICULIER + + cursor = await db.execute( + "SELECT COUNT(*) FROM accounts WHERE guild_id = ? AND bank_id = ? AND user_id = ? AND account_type = ?", + (guild_id, bank_id, user_id, account_type), + ) + count = (await cursor.fetchone())[0] + if count > 0: + raise ValueError(f"Ce membre a déjà un compte {account_type} dans cette banque.") + + rib = generer_rib(bank["establishment_code"], guichet) + + cursor = await db.execute( + "INSERT INTO accounts (guild_id, bank_id, user_id, rib, account_type, tva_rate, password) VALUES (?, ?, ?, ?, ?, ?, ?)", + (guild_id, bank_id, user_id, rib, account_type, tva_rate, password), + ) + await db.commit() + return {"id": cursor.lastrowid, "rib": rib, "account_type": account_type, "tva_rate": tva_rate} + finally: + await db.close() + + async def get_compte_by_rib(self, guild_id: int, rib: str) -> Optional[dict]: + db = await get_db() + try: + cursor = await db.execute( + """SELECT a.*, b.name as bank_name, b.establishment_code, b.role_id as bank_role_id + FROM accounts a JOIN banks b ON a.bank_id = b.id + WHERE a.rib = ? AND a.guild_id = ?""", + (rib, guild_id), + ) + row = await cursor.fetchone() + return dict(row) if row else None + finally: + await db.close() + + async def get_comptes_user(self, guild_id: int, user_id: int) -> list: + db = await get_db() + try: + cursor = await db.execute( + """SELECT a.*, b.name as bank_name, b.establishment_code + FROM accounts a JOIN banks b ON a.bank_id = b.id + WHERE a.guild_id = ? AND a.user_id = ? + ORDER BY b.name, a.account_type""", + (guild_id, user_id), + ) + rows = await cursor.fetchall() + return [dict(r) for r in rows] + finally: + await db.close() + + async def get_comptes_banque(self, guild_id: int, bank_id: int) -> list: + db = await get_db() + try: + cursor = await db.execute( + """SELECT a.*, b.name as bank_name + FROM accounts a JOIN banks b ON a.bank_id = b.id + WHERE a.guild_id = ? AND a.bank_id = ? + ORDER BY a.user_id, a.account_type""", + (guild_id, bank_id), + ) + rows = await cursor.fetchall() + return [dict(r) for r in rows] + finally: + await db.close() + + async def modifier_tva(self, guild_id: int, rib: str, nouveau_taux: float) -> bool: + db = await get_db() + try: + cursor = await db.execute( + "UPDATE accounts SET tva_rate = ? WHERE rib = ? AND guild_id = ? AND account_type = 'entreprise'", + (nouveau_taux, rib, guild_id), + ) + await db.commit() + return cursor.rowcount > 0 + finally: + await db.close() + + async def virement( + self, + guild_id: int, + rib_source: str, + rib_dest: str, + montant: float, + emetteur_id: int = None, + destinataire_id: int = None, + ) -> dict: + if montant <= 0: + raise ValueError("Le montant doit être supérieur à 0.") + if rib_source == rib_dest: + raise ValueError("Impossible de se virer à soi-même.") + + db = await get_db() + try: + source = await self._get_account(db, guild_id, rib_source) + if not source: + raise ValueError("Compte source introuvable.") + dest = await self._get_account(db, guild_id, rib_dest) + if not dest: + raise ValueError("Compte destination introuvable.") + + if source["solde"] < montant: + raise ValueError(f"Solde insuffisant. Disponible : {source['solde']:.2f}€") + + tva_montant = 0.0 + montant_net = montant + + if source["account_type"] == "particulier" and dest["account_type"] == "entreprise": + tva_rate = dest["tva_rate"] or 0.0 + if tva_rate > 0: + tva_montant = round(montant * tva_rate / 100, 2) + montant_net = round(montant - tva_montant, 2) + + cursor = await db.execute( + "UPDATE accounts SET solde = solde - ? WHERE rib = ? AND guild_id = ?", + (montant, rib_source, guild_id), + ) + cursor = await db.execute( + "UPDATE accounts SET solde = solde + ? WHERE rib = ? AND guild_id = ?", + (montant_net, rib_dest, guild_id), + ) + + if tva_montant > 0: + fisc_rib = await self._get_fisc_rib(db, guild_id) + if fisc_rib: + cursor = await db.execute( + "UPDATE accounts SET solde = solde + ? WHERE rib = ? AND guild_id = ?", + (tva_montant, fisc_rib, guild_id), + ) + + cursor = await db.execute( + "UPDATE banks SET clients_total_balance = clients_total_balance - ? WHERE id = ? AND guild_id = ?", + (montant, source["bank_id"], guild_id), + ) + cursor = await db.execute( + "UPDATE banks SET clients_total_balance = clients_total_balance + ? WHERE id = ? AND guild_id = ?", + (montant_net, dest["bank_id"], guild_id), + ) + + now = datetime.now().strftime("%Y-%m-%d %H:%M:%S") + await db.execute( + """INSERT INTO transactions (guild_id, emetteur_rib, destinataire_rib, emetteur_id, destinataire_id, montant, type, date) + VALUES (?, ?, ?, ?, ?, ?, ?, ?)""", + (guild_id, rib_source, rib_dest, emetteur_id or source["user_id"], destinataire_id or dest["user_id"], montant, "virement", now), + ) + if tva_montant > 0 and fisc_rib: + await db.execute( + """INSERT INTO transactions (guild_id, destinataire_rib, montant, type, date, metadata) + VALUES (?, ?, ?, ?, ?, ?)""", + (guild_id, fisc_rib, tva_montant, "tva_prelevee", now, json.dumps({"source_rib": rib_source, "dest_rib": rib_dest})), + ) + + await db.commit() + + result = { + "montant_envoye": montant, + "montant_recu": montant_net, + "tva_prelevee": tva_montant, + } + if tva_montant > 0: + result["tva_dest"] = fisc_rib + return result + finally: + await db.close() + + async def depot_banquier( + self, guild_id: int, rib: str, montant: float, banquier_id: int + ) -> dict: + if montant <= 0: + raise ValueError("Le montant doit être supérieur à 0.") + + db = await get_db() + try: + compte = await self._get_account(db, guild_id, rib) + if not compte: + raise ValueError("Compte introuvable.") + + cursor = await db.execute( + "UPDATE accounts SET solde = solde + ? WHERE rib = ? AND guild_id = ?", + (montant, rib, guild_id), + ) + cursor = await db.execute( + "UPDATE banks SET clients_total_balance = clients_total_balance + ? WHERE id = ? AND guild_id = ?", + (montant, compte["bank_id"], guild_id), + ) + + now = datetime.now().strftime("%Y-%m-%d %H:%M:%S") + await db.execute( + """INSERT INTO transactions (guild_id, destinataire_rib, destinataire_id, montant, type, date, metadata) + VALUES (?, ?, ?, ?, ?, ?, ?)""", + (guild_id, rib, compte["user_id"], montant, "depot_banquier", now, json.dumps({"banquier_id": banquier_id})), + ) + await db.commit() + return {"nouveau_solde": compte["solde"] + montant} + finally: + await db.close() + + async def retrait_banquier( + self, guild_id: int, rib: str, montant: float, banquier_id: int + ) -> dict: + if montant <= 0: + raise ValueError("Le montant doit être supérieur à 0.") + + db = await get_db() + try: + compte = await self._get_account(db, guild_id, rib) + if not compte: + raise ValueError("Compte introuvable.") + if compte["solde"] < montant: + raise ValueError(f"Solde insuffisant. Disponible : {compte['solde']:.2f}€") + + cursor = await db.execute( + "UPDATE accounts SET solde = solde - ? WHERE rib = ? AND guild_id = ?", + (montant, rib, guild_id), + ) + cursor = await db.execute( + "UPDATE banks SET clients_total_balance = clients_total_balance - ? WHERE id = ? AND guild_id = ?", + (montant, compte["bank_id"], guild_id), + ) + + now = datetime.now().strftime("%Y-%m-%d %H:%M:%S") + await db.execute( + """INSERT INTO transactions (guild_id, emetteur_rib, emetteur_id, montant, type, date, metadata) + VALUES (?, ?, ?, ?, ?, ?, ?)""", + (guild_id, rib, compte["user_id"], montant, "retrait_banquier", now, json.dumps({"banquier_id": banquier_id})), + ) + await db.commit() + return {"nouveau_solde": compte["solde"] - montant} + finally: + await db.close() + + async def get_transactions(self, guild_id: int, rib: str, limit: int = 10) -> list: + db = await get_db() + try: + cursor = await db.execute( + """SELECT * FROM transactions + WHERE guild_id = ? AND (emetteur_rib = ? OR destinataire_rib = ?) + ORDER BY date DESC LIMIT ?""", + (guild_id, rib, rib, limit), + ) + rows = await cursor.fetchall() + return [dict(r) for r in rows] + finally: + await db.close() + + async def creer_institution( + self, guild_id: int, name: str, role_id: int = None, can_audit_all: bool = False + ) -> dict: + db = await get_db() + try: + cursor = await db.execute( + "INSERT INTO institutions (guild_id, name, role_id, can_audit_all) VALUES (?, ?, ?, ?)", + (guild_id, name, role_id, int(can_audit_all)), + ) + await db.commit() + return {"id": cursor.lastrowid, "name": name, "can_audit_all": can_audit_all} + finally: + await db.close() + + async def get_institution(self, guild_id: int, institution_id: int) -> Optional[dict]: + db = await get_db() + try: + cursor = await db.execute( + "SELECT * FROM institutions WHERE id = ? AND guild_id = ?", + (institution_id, guild_id), + ) + row = await cursor.fetchone() + return dict(row) if row else None + finally: + await db.close() + + async def get_institution_by_role(self, guild_id: int, role_id: int) -> Optional[dict]: + db = await get_db() + try: + cursor = await db.execute( + "SELECT * FROM institutions WHERE guild_id = ? AND role_id = ?", + (guild_id, role_id), + ) + row = await cursor.fetchone() + return dict(row) if row else None + finally: + await db.close() + + async def lister_institutions(self, guild_id: int) -> list: + db = await get_db() + try: + cursor = await db.execute( + "SELECT * FROM institutions WHERE guild_id = ? ORDER BY name", (guild_id,) + ) + rows = await cursor.fetchall() + return [dict(r) for r in rows] + finally: + await db.close() + + async def configurer_fisc(self, guild_id: int, institution_id: int) -> bool: + db = await get_db() + try: + inst = await self.get_institution(guild_id, institution_id) + if not inst: + return False + + guichet = GUIICHET_INSTITUTION + bank = await self._get_gouvernement_bank(db, guild_id) + if not bank: + cursor = await db.execute( + "INSERT INTO banks (guild_id, name, role_id, establishment_code) VALUES (?, ?, ?, ?)", + (guild_id, "Gouvernement", 0, 1), + ) + bank_id = cursor.lastrowid + bank = {"id": bank_id, "establishment_code": 1} + else: + bank_id = bank["id"] + + rib = generer_rib(bank["establishment_code"], guichet) + + existing = await db.execute( + "SELECT rib FROM accounts WHERE guild_id = ? AND bank_id = ? AND user_id = ? AND account_type = 'entreprise'", + (guild_id, bank_id, institution_id), + ) + existing_row = await existing.fetchone() + if existing_row: + rib = existing_row[0] + else: + cursor = await db.execute( + "INSERT INTO accounts (guild_id, bank_id, user_id, rib, account_type, solde) VALUES (?, ?, ?, ?, 'entreprise', 0.0)", + (guild_id, bank_id, institution_id, rib), + ) + + await db.execute( + "INSERT OR REPLACE INTO fisc_config (guild_id, institution_id) VALUES (?, ?)", + (guild_id, institution_id), + ) + await db.commit() + return True + finally: + await db.close() + + async def get_fisc_config(self, guild_id: int) -> Optional[dict]: + db = await get_db() + try: + cursor = await db.execute( + """SELECT fc.*, i.name as institution_name, i.role_id as institution_role_id + FROM fisc_config fc + JOIN institutions i ON fc.institution_id = i.id + WHERE fc.guild_id = ?""", + (guild_id,), + ) + row = await cursor.fetchone() + return dict(row) if row else None + finally: + await db.close() + + async def get_next_bank_codes(self, guild_id: int) -> list: + db = await get_db() + try: + cursor = await db.execute( + "SELECT establishment_code FROM banks WHERE guild_id = ? ORDER BY establishment_code", + (guild_id,), + ) + rows = await cursor.fetchall() + return [r[0] for r in rows] + finally: + await db.close() + + async def _get_account(self, db: aiosqlite.Connection, guild_id: int, rib: str) -> Optional[dict]: + cursor = await db.execute( + "SELECT * FROM accounts WHERE rib = ? AND guild_id = ?", (rib, guild_id) + ) + row = await cursor.fetchone() + return dict(row) if row else None + + async def _get_fisc_rib(self, db: aiosqlite.Connection, guild_id: int) -> Optional[str]: + cursor = await db.execute( + """SELECT a.rib FROM accounts a + JOIN fisc_config fc ON a.user_id = fc.institution_id + WHERE fc.guild_id = ? AND a.account_type = 'entreprise'""", + (guild_id,), + ) + row = await cursor.fetchone() + return row[0] if row else None + + async def _get_gouvernement_bank(self, db: aiosqlite.Connection, guild_id: int) -> Optional[dict]: + cursor = await db.execute( + "SELECT * FROM banks WHERE guild_id = ? AND name = 'Gouvernement'", + (guild_id,), + ) + row = await cursor.fetchone() + return dict(row) if row else None diff --git a/utils/banque_manager.py b/utils/banque_manager.py deleted file mode 100644 index 4b594be..0000000 --- a/utils/banque_manager.py +++ /dev/null @@ -1,58 +0,0 @@ -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 index b580b93..a14cd3c 100644 --- a/utils/card_generator.py +++ b/utils/card_generator.py @@ -16,7 +16,8 @@ class CardGenerator: def create_template(self): try: - CI_path = "/home/discord/Bot/Vicy/ressources/assets/CIITAL.png" + base_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + CI_path = os.path.join(base_dir, "ressources", "assets", "CIITAL.png") if not os.path.exists(CI_path): raise FileNotFoundError(f"Image de fond introuvable : {CI_path}") diff --git a/utils/db.py b/utils/db.py new file mode 100644 index 0000000..44b22d6 --- /dev/null +++ b/utils/db.py @@ -0,0 +1,82 @@ +import aiosqlite +import os + +DB_PATH = os.path.join(os.path.dirname(__file__), "..", "data", "bank_v2.db") + +SCHEMA = """ +CREATE TABLE IF NOT EXISTS banks ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + guild_id INTEGER NOT NULL, + name TEXT NOT NULL, + role_id INTEGER NOT NULL, + establishment_code INTEGER NOT NULL, + vault_balance REAL DEFAULT 0.0, + clients_total_balance REAL DEFAULT 0.0, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + UNIQUE(guild_id, name), + UNIQUE(guild_id, establishment_code) +); + +CREATE TABLE IF NOT EXISTS accounts ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + guild_id INTEGER NOT NULL, + bank_id INTEGER NOT NULL, + user_id INTEGER NOT NULL, + rib TEXT NOT NULL UNIQUE, + account_type TEXT NOT NULL CHECK(account_type IN ('particulier', 'entreprise')), + solde REAL DEFAULT 0.0, + tva_rate REAL DEFAULT 0.0, + password TEXT, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + FOREIGN KEY (bank_id) REFERENCES banks(id) ON DELETE CASCADE, + UNIQUE(guild_id, bank_id, user_id, account_type) +); + +CREATE TABLE IF NOT EXISTS institutions ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + guild_id INTEGER NOT NULL, + name TEXT NOT NULL, + role_id INTEGER, + can_audit_all INTEGER DEFAULT 0, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + UNIQUE(guild_id, name) +); + +CREATE TABLE IF NOT EXISTS fisc_config ( + guild_id INTEGER PRIMARY KEY, + institution_id INTEGER, + FOREIGN KEY (institution_id) REFERENCES institutions(id) ON DELETE SET NULL +); + +CREATE TABLE IF NOT EXISTS transactions ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + guild_id INTEGER NOT NULL, + emetteur_rib TEXT, + destinataire_rib TEXT, + emetteur_id INTEGER, + destinataire_id INTEGER, + montant REAL NOT NULL, + type TEXT NOT NULL, + date TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + metadata TEXT +); + +CREATE INDEX IF NOT EXISTS idx_accounts_guild_user ON accounts(guild_id, user_id); +CREATE INDEX IF NOT EXISTS idx_accounts_rib ON accounts(rib); +CREATE INDEX IF NOT EXISTS idx_accounts_bank ON accounts(guild_id, bank_id); +CREATE INDEX IF NOT EXISTS idx_transactions_guild ON transactions(guild_id); +CREATE INDEX IF NOT EXISTS idx_transactions_ribs ON transactions(emetteur_rib, destinataire_rib); +CREATE INDEX IF NOT EXISTS idx_banks_guild ON banks(guild_id); +CREATE INDEX IF NOT EXISTS idx_institutions_guild ON institutions(guild_id); +""" + + +async def get_db() -> aiosqlite.Connection: + os.makedirs(os.path.dirname(DB_PATH), exist_ok=True) + db = await aiosqlite.connect(DB_PATH) + db.row_factory = aiosqlite.Row + await db.execute("PRAGMA journal_mode=WAL") + await db.execute("PRAGMA foreign_keys=ON") + await db.executescript(SCHEMA) + await db.commit() + return db diff --git a/utils/guild_config.py b/utils/guild_config.py index 431137f..70f8be8 100644 --- a/utils/guild_config.py +++ b/utils/guild_config.py @@ -18,9 +18,6 @@ DEFAULT_CONFIG = { "visite_medicale": None, "permis": None, "auto_ecole": None, - "fbm": None, - "banque_nationale": None, - "ursaff": None, "session_reminders": None, }, "channels": { @@ -47,9 +44,6 @@ ROLE_LABELS = { "visite_medicale": "Visite Médicale", "permis": "Permis", "auto_ecole": "Auto-École", - "fbm": "FBM", - "banque_nationale": "Banque Nationale", - "ursaff": "URSAFF", "session_reminders": "Session Reminders", } diff --git a/utils/rib.py b/utils/rib.py new file mode 100644 index 0000000..0ac8377 --- /dev/null +++ b/utils/rib.py @@ -0,0 +1,55 @@ +import random +import string + + +def _checksum(digits: str) -> int: + return (97 - (int(digits) % 97)) % 97 + + +def generer_rib(establishment_code: int, guichet_code: int) -> str: + etab = str(establishment_code).zfill(5) + guichet = str(guichet_code).zfill(5) + numero = "".join(random.choices(string.digits, k=11)) + checksum_digits = f"76{etab}{guichet}{numero}" + cle = str(_checksum(checksum_digits)).zfill(2) + return f"FR76 {etab} {guichet} {numero} {cle}" + + +def valider_rib(rib: str) -> bool: + parts = rib.replace("FR76 ", "").split() + if len(parts) != 3: + return False + etab, guichet, rest = parts + if len(etab) != 5 or len(guichet) != 5: + return False + if len(rest) != 13: + return False + numero = rest[:11] + cle = rest[11:] + if not etab.isdigit() or not guichet.isdigit() or not numero.isdigit() or not cle.isdigit(): + return False + checksum_digits = f"76{etab}{guichet}{numero}" + return _checksum(checksum_digits) == int(cle) + + +def decoder_rib(rib: str) -> dict: + parts = rib.replace("FR76 ", "").split() + if len(parts) != 3: + return None + etab, guichet, rest = parts + numero = rest[:11] + cle = rest[11:] + etab_int = int(etab) + guichet_int = int(guichet) + + TYPE_MAP = {4: "particulier", 3: "entreprise", 2: "banque", 1: "institution"} + + return { + "etablissement": etab_int, + "guichet": guichet_int, + "numero": numero, + "cle": int(cle), + "type_compte": TYPE_MAP.get(guichet_int, "inconnu"), + "est_gouvernement": etab_int < 100, + "valide": valider_rib(rib), + }