Initial commit: bot Discord Vicy V1.8

This commit is contained in:
utilisateurqu 2026-08-16 19:37:15 +02:00
commit 56958c5832
70 changed files with 8469 additions and 0 deletions

19
.gitignore vendored Normal file
View file

@ -0,0 +1,19 @@
# Secrets
.env
config.json
# Python
__pycache__/
*.pyc
*.pyo
# Virtual environment
.venv/
# Data & logs
data/
log.txt
# OS
Thumbs.db
Desktop.ini

133
__main__.py Normal file
View file

@ -0,0 +1,133 @@
import discord
import asyncio
import logging
from bot import bot
from utils.logger import log_info, log_error
from discord.ext import commands
from discord import app_commands, Intents
from dotenv import load_dotenv
import os
from discord import Intents, Permissions
from utils.stats_manager import enregistrer_utilisation, get_stats
# Configuration du logger Discord (niveau DEBUG pour afficher tous les événements importants)
logging.basicConfig(level=logging.DEBUG)
discord.utils.setup_logging(level=logging.DEBUG, root=False)
# Configuration des intents
intents = Intents.default()
intents.message_content = True
intents.guilds = True
intents.members = True
intents.reactions = True
# Permissions du bot
permissions = Permissions()
permissions.update(
manage_nicknames=True,
send_messages=True,
read_messages=True,
manage_channels=True,
manage_messages=True,
add_reactions=True,
use_application_commands=True,
view_channel=True
)
# Initialisation du bot
bot = commands.Bot(command_prefix="!", application_id="1310235313461002241", intents=intents)
async def load_cogs():
"""Charge toutes les commandes."""
extensions = [
"commandes.message",
"commandes.identite",
"commandes.voir_carte_identite",
"commandes.virement",
"commandes.transactions",
"commandes.ajouter_argent",
"commandes.ticket",
"commandes.restranscription_staff",
"utils.welcome_message",
"commandes.permis",
"commandes.ouvrir_compte",
"commandes.compte",
"commandes.change_nickname",
"commandes.rename_all_on_join",
"commandes.message_tous",
"commandes.retirer_argent",
"commandes.ouvrir_comptes_entreprises",
"commandes.compte_gouvernement",
"commandes.comptes_entreprises",
"commandes.compte_urssaf",
"commandes.compte_banque_nationale",
"commandes.virement_entreprise",
"commandes.virement_gouvernement",
"commandes.valider_visite_médicale",
"commandes.abonnement",
"commandes.virement_banquier",
"commandes.virement_banquier_entreprise",
"commandes.compte_fbm",
"commandes.virement_fbm",
"commandes.virement_ursaff",
"commandes.stats_commandes",
"commandes.taches",
"commandes.roll",
"commandes.annonces_sessions", # Fait : V1.7 correction des annonces de sessions
"commandes.retrait_gouv",
"commandes.absence_staff", # Fait : V1.7 pouvoir voir plus de 3 absences staff (pagination)
"commandes.liste_taches", # Fait : V1.7 Rebuild du système de taches pour le rendre plus moderne
"commandes.counting"
# Pas fait : V1.8 Amélioration des stats de commandes ajouts de courbes et de graphiques + Voir le top 10 des personnes qui utilisent le plus les commandes
]
for ext in extensions:
try:
await bot.load_extension(ext)
log_info(f"Extension chargée : {ext}")
except Exception as e:
log_error(f"Erreur lors du chargement de {ext} : {e}")
@bot.event
async def on_ready():
# Affiche que le bot est prêt et synchronise les commandes slash
log_info(f"Connecté en tant que {bot.user}")
await load_cogs()
await bot.tree.sync()
await bot.change_presence(activity=discord.Game(name="la V1.6")) # Ajout du status
log_info("Commandes synchronisées.")
# Gère les erreurs de commandes classiques
@bot.event
async def on_command_error(ctx, error):
log_error(f"Erreur dans la commande {ctx.command}: {error}")
# Gère toutes les autres erreurs inattendues
@bot.event
async def on_error(event_method, *args, **kwargs):
log_error(f"Erreur non gérée dans l'événement {event_method}", exc_info=True)
# 👉 Enregistre chaque commande slash utilisée
@bot.event
async def on_app_command_completion(interaction: discord.Interaction, command: app_commands.Command):
try:
user_id = interaction.user.id
guild_id = interaction.guild.id if interaction.guild else 0
enregistrer_utilisation(command.name, user_id, guild_id)
except Exception as e:
log_error(f"Erreur en enregistrant les stats : {e}")
async def main():
# Charge les variables d'environnement et démarre le bot
load_dotenv()
token = os.getenv("TOKEN")
try:
log_info("Démarrage du bot...")
await bot.start(token)
except Exception as e:
log_error(f"Erreur critique: {e}", exc_info=True)
if __name__ == "__main__":
asyncio.run(main())

218
abonnements.json Normal file
View file

@ -0,0 +1,218 @@
[
{
"nom": "Test",
"rib_emetteur": "22144 59424 87742058459 72",
"rib_receveur": "66427 49104 57218570654 78",
"montant": 1.0,
"banque": "NGC Bank",
"jour": "samedi"
},
{
"nom": "Frais_bancaire_et_de_gestion_1",
"rib_emetteur": "36495 80083 39542816185 27",
"rib_receveur": "15156 38436 35920 54357",
"montant": 5.0,
"banque": "NGC Bank",
"jour": "dimanche"
},
{
"nom": "Frais_bancaire_et_de_gestion_1",
"rib_emetteur": "26384 93370 75890178377 91",
"rib_receveur": "15156 38436 35920 54357",
"montant": 5.0,
"banque": "NGC Bank",
"jour": "dimanche"
},
{
"nom": "Frais_bancaire_et_de_gestion_1",
"rib_emetteur": "22144 59424 87742058459 72",
"rib_receveur": "15156 38436 35920 54357",
"montant": 5.0,
"banque": "NGC Bank",
"jour": "dimanche"
},
{
"nom": "Frais_bancaire_et_de_gestion_1",
"rib_emetteur": "85047 60626 80894743346 94",
"rib_receveur": "15156 38436 35920 54357",
"montant": 5.0,
"banque": "NGC Bank",
"jour": "dimanche"
},
{
"nom": "Frais_bancaire_et_de_gestion_1",
"rib_emetteur": "28777 24580 29765721710 42",
"rib_receveur": "15156 38436 35920 54357",
"montant": 5.0,
"banque": "NGC Bank",
"jour": "dimanche"
},
{
"nom": "Frais_bancaire_et_de_gestion_1",
"rib_emetteur": "24483 98056 61791340797 80",
"rib_receveur": "15156 38436 35920 54357",
"montant": 5.0,
"banque": "NGC Bank",
"jour": "dimanche"
},
{
"nom": "Frais_bancaire_et_de_gestion_1",
"rib_emetteur": "10009 40554 56452790470 78",
"rib_receveur": "15156 38436 35920 54357",
"montant": 5.0,
"banque": "NGC Bank",
"jour": "dimanche"
},
{
"nom": "Frais_bancaire_et_de_gestion_1",
"rib_emetteur": "76644 32901 90493249064 16",
"rib_receveur": "15156 38436 35920 54357",
"montant": 5.0,
"banque": "NGC Bank",
"jour": "dimanche"
},
{
"nom": "Frais_bancaire_et_de_gestion_1",
"rib_emetteur": "15563 69899 51361446295 84",
"rib_receveur": "15156 38436 35920 54357",
"montant": 5.0,
"banque": "NGC Bank",
"jour": "dimanche"
},
{
"nom": "Frais_bancaire_et_de_gestion_1",
"rib_emetteur": "22128 86524 35982837421 58",
"rib_receveur": "15156 38436 35920 54357",
"montant": 5.0,
"banque": "NGC Bank",
"jour": "dimanche"
},
{
"nom": "Frais_bancaire_et_de_gestion_1",
"rib_emetteur": "55868 11389 89049343014 38",
"rib_receveur": "15156 38436 35920 54357",
"montant": 5.0,
"banque": "NGC Bank",
"jour": "dimanche"
},
{
"nom": "Frais_bancaire_et_de_gestion_1",
"rib_emetteur": "93266 82343 74845298309 22",
"rib_receveur": "15156 38436 35920 54357",
"montant": 5.0,
"banque": "NGC Bank",
"jour": "dimanche"
},
{
"nom": "Frais_bancaire_et_de_gestion_1",
"rib_emetteur": "10234 24218 10477731114 78",
"rib_receveur": "15156 38436 35920 54357",
"montant": 5.0,
"banque": "NGC Bank",
"jour": "dimanche"
},
{
"nom": "Frais_bancaire_et_de_gestion_1",
"rib_emetteur": "50970 51336 33017058086 50",
"rib_receveur": "15156 38436 35920 54357",
"montant": 5.0,
"banque": "NGC Bank",
"jour": "dimanche"
},
{
"nom": "Frais_bancaire_et_de_gestion_1",
"rib_emetteur": "87292 81104 84784268868 12",
"rib_receveur": "15156 38436 35920 54357",
"montant": 5.0,
"banque": "NGC Bank",
"jour": "dimanche"
},
{
"nom": "Frais_bancaire_et_de_gestion_1",
"rib_emetteur": "51229 80290 86979675797 93",
"rib_receveur": "15156 38436 35920 54357",
"montant": 5.0,
"banque": "NGC Bank",
"jour": "dimanche"
},
{
"nom": "Frais_bancaire_et_de_gestion_1",
"rib_emetteur": "28794 77878 41312953164 71",
"rib_receveur": "15156 38436 35920 54357",
"montant": 5.0,
"banque": "NGC Bank",
"jour": "dimanche"
},
{
"nom": "Frais_bancaire_et_de_gestion_1",
"rib_emetteur": "78740 15751 70195185799 30",
"rib_receveur": "15156 38436 35920 54357",
"montant": 5.0,
"banque": "NGC Bank",
"jour": "dimanche"
},
{
"nom": "Frais_bancaire_et_de_gestion_1",
"rib_emetteur": "92871 52823 19649316403 70",
"rib_receveur": "15156 38436 35920 54357",
"montant": 5.0,
"banque": "NGC Bank",
"jour": "dimanche"
},
{
"nom": "Frais_bancaire_et_de_gestion_1",
"rib_emetteur": "73834 54818 11833486819 17",
"rib_receveur": "15156 38436 35920 54357",
"montant": 5.0,
"banque": "NGC Bank",
"jour": "dimanche"
},
{
"nom": "Frog",
"rib_emetteur": "48003 84424 36961155485 64",
"rib_receveur": "15156 38436 35920 54357",
"montant": 700.0,
"banque": "NGC Bank",
"jour": "dimanche"
},
{
"nom": "Di regia",
"rib_emetteur": "10009 40554 56452790470 78",
"rib_receveur": "15156 38436 35920 54357",
"montant": 4000.0,
"banque": "NGC Bank",
"jour": "dimanche"
},
{
"nom": "Leonardo",
"rib_emetteur": "24756 54403 68357307735 95",
"rib_receveur": "15156 38436 35920 54357",
"montant": 4000.0,
"banque": "NGC Bank",
"jour": "dimanche"
},
{
"nom": "Correa",
"rib_emetteur": "28794 77878 41312953164 71",
"rib_receveur": "15156 38436 35920 54357",
"montant": 4300.0,
"banque": "NGC Bank",
"jour": "dimanche"
},
{
"nom": "jose",
"rib_emetteur": "87292 81104 84784268868 12",
"rib_receveur": "15156 38436 35920 54357",
"montant": 800.0,
"banque": "NGC Bank",
"jour": "dimanche"
},
{
"nom": "Paye_de_Salvatore_Di_Regia",
"rib_emetteur": "15156 38436 35920 54357",
"rib_receveur": "10009 40554 56452790470 78",
"montant": 5000.0,
"banque": "NGC Bank",
"jour": "dimanche"
}
]

47
absences.json Normal file
View file

@ -0,0 +1,47 @@
{
"534003230435442690": {
"pseudo": "nixfix06",
"debut": "25/06/2025",
"fin": "01/09/2025",
"motif": "je travaille",
"superieur": "Aucun",
"message_id": 1386993295766323202,
"etat": "en cours"
},
"828923448453103637": {
"pseudo": "blancandwhite",
"debut": "04/07/2025",
"fin": "28/07/2025",
"motif": "vacances",
"superieur": "<@1108087282692522105>",
"message_id": 1389892250317750322,
"etat": "en cours"
},
"914118793918312480": {
"pseudo": "neyzizz",
"debut": "20/07/2025",
"fin": "27/07/2025",
"motif": "vacances en montagne",
"superieur": "Aucun",
"message_id": 1390747833958662154,
"etat": "en cours"
},
"727188532669448509": {
"pseudo": "nuggets.png",
"debut": "21/07/2025",
"fin": "22/07/2025",
"motif": "je fouette mg",
"superieur": "<@1069386396764221450>",
"message_id": 1396650241679294515,
"etat": "en cours"
},
"1069386396764221450": {
"pseudo": "matlo13",
"debut": "21/07/2025",
"fin": "22/07/2025",
"motif": "text",
"superieur": "Aucun",
"message_id": 1396650262252355584,
"etat": "en cours"
}
}

1
banque_data.json Normal file
View file

@ -0,0 +1 @@
{}

25
bot.py Normal file
View file

@ -0,0 +1,25 @@
from utils.logger import log_info, log_error
from discord.ext import commands
import discord
intents = discord.Intents.default()
intents.message_content = True
intents.members = True
bot = commands.Bot(command_prefix='/', intents=intents)
@bot.event
async def on_ready():
try:
log_info(f"Le bot {bot.user} a démarré avec succès.")
await bot.change_presence(activity=discord.Game(name="la version 1.2"))
except Exception as e:
log_error(f"Erreur lors du démarrage du bot: {e}")
@bot.event
async def on_ready():
try:
synced = await bot.tree.sync()
print(f"Synchronisé {len(synced)} commande(s)")
except Exception as e:
print(f"Erreur lors de la synchronisation des commandes : {e}")

View file

@ -0,0 +1,66 @@
import discord
from discord.ext import commands
from discord import app_commands
from datetime import datetime, timedelta
class AbsenceStaff(commands.Cog):
def __init__(self, bot):
self.bot = bot
self.absences = {}
@app_commands.command(name="absence_staff", description="Indiquez votre absence en tant que staff.")
async def absence_staff(self, interaction: discord.Interaction, duree: str, debut: str, fin: str, raison: str, superieur: discord.Member):
"""Indiquez votre absence en tant que staff."""
try:
# Convertir les dates
debut_date = datetime.strptime(debut, "%d/%m/%Y")
fin_date = datetime.strptime(fin, "%d/%m/%Y")
# Calculer la durée en jours
if duree.endswith('s'): # Si la durée est en semaines
duree_jours = int(duree[:-1]) * 7
else:
duree_jours = int(duree)
# Vérifier la cohérence des dates
if (fin_date - debut_date).days != duree_jours:
await interaction.response.send_message("❌ Erreur : La durée spécifiée ne correspond pas aux dates fournies.")
return
# Enregistrer l'absence
self.absences[interaction.user.id] = {
"debut": debut_date,
"fin": fin_date,
"raison": raison,
"superieur": superieur.id
}
await interaction.response.send_message(f"✅ Votre absence a été enregistrée, {interaction.user.mention}.")
except Exception as e:
await interaction.response.send_message(f"❌ Erreur : {str(e)}")
@commands.Cog.listener()
async def on_message(self, message):
# Ignorer les messages envoyés par le bot
if message.author.bot:
return
# Vérifier si un utilisateur absent est mentionné
for user_id, absence in self.absences.items():
user = self.bot.get_user(user_id)
if user and user in message.mentions:
# Répondre avec un message indiquant l'absence
await message.channel.send(
f"{user.display_name} est actuellement absent(e).\n"
f"Raison : {absence['raison']}\n"
f"Retour prévu le : {absence['fin'].strftime('%d/%m/%Y')}"
)
# Supprimer le message avec la mention
await message.delete()
break
async def setup(bot):
await bot.add_cog(AbsenceStaff(bot))
print("Commande /absence_staff chargée !")

5
commandes/__init__.py Normal file
View file

@ -0,0 +1,5 @@
# Pour la V1.2 Ajouter des statistiques d'utlisation du bot
# Pour la V1.3 Corriger le système de claim des tickets WL (Les anciens propriétaires de ticket garde les permission même après une reprise par quelqu'un d'autre)
# Pour la V1.3 Ajouter la commande /compte_utilisateur @[de_l'utilisateur] : affiche le compte d'un autre utilisateur (Banquier Only)
# Pour la V1.3 Pouvoir voir les cartes d'identités des autres quand on la le rôle gouvernement
# Pour la V1.3 Pouvoir fermer des comptes bancaires ou/et pouvoir régénérer un rib si on est banquier

148
commandes/abonnement.py Normal file
View file

@ -0,0 +1,148 @@
import discord
from discord import app_commands
from discord.ext import commands, tasks
import json
import datetime
ABONNEMENTS_FILE = "abonnements.json"
BANQUES_DISPONIBLES = [
"Victoria's Credit Bank",
"NGC Bank"
]
JOURS_SEMAINE = [
"lundi", "mardi", "mercredi", "jeudi", "vendredi", "samedi", "dimanche"
]
class Abonnement(commands.Cog):
def __init__(self, bot):
self.bot = bot
self.abonnements = self.load_abonnements()
self.executer_abonnements.start()
def load_abonnements(self):
try:
with open(ABONNEMENTS_FILE, 'r', encoding='utf-8') as f:
return json.load(f)
except (FileNotFoundError, json.JSONDecodeError):
return []
def save_abonnements(self):
with open(ABONNEMENTS_FILE, 'w', encoding='utf-8') as f:
json.dump(self.abonnements, f, ensure_ascii=False, indent=4)
# 🎯 Auto-complétion pour les banques
async def autocomplete_banque(self, interaction: discord.Interaction, current: str):
return [
app_commands.Choice(name=b, value=b)
for b in BANQUES_DISPONIBLES if current.lower() in b.lower()
]
# 🎯 Auto-complétion pour les jours
async def autocomplete_jour(self, interaction: discord.Interaction, current: str):
return [
app_commands.Choice(name=day.capitalize(), value=day)
for day in JOURS_SEMAINE if current.lower() in day
]
@app_commands.command(name="abonnement", description="Crée un abonnement bancaire hebdomadaire.")
@app_commands.describe(
nom="Nom de l'abonnement",
rib_emetteur="RIB de l'expéditeur",
montant="Montant à envoyer",
rib_receveur="RIB du bénéficiaire",
banque="Nom de la banque",
jour="Jour de la semaine du virement"
)
@app_commands.autocomplete(banque=autocomplete_banque, jour=autocomplete_jour)
async def abonnement(
self,
interaction: discord.Interaction,
nom: str,
rib_emetteur: str,
montant: float,
rib_receveur: str,
banque: str,
jour: str
):
if jour.lower() not in JOURS_SEMAINE:
await interaction.response.send_message("❌ Jour invalide. Choisissez un jour valide de la semaine.", ephemeral=True)
return
abonnement = {
"nom": nom,
"rib_emetteur": rib_emetteur,
"rib_receveur": rib_receveur,
"montant": montant,
"banque": banque,
"jour": jour.lower()
}
self.abonnements.append(abonnement)
self.save_abonnements()
await interaction.response.send_message(f"✅ Abonnement **{nom}** enregistré. Virement automatique tous les **{jour}**.", ephemeral=True)
@tasks.loop(minutes=60)
async def executer_abonnements(self):
today = datetime.datetime.now().strftime("%A").lower()
try:
with open("comptes_bancaires.json", "r", encoding="utf-8") as f:
comptes = json.load(f)
except:
comptes = {}
updated = False
for abonnement in self.abonnements:
if abonnement["jour"] != today:
continue
banque = abonnement["banque"]
rib_emetteur = abonnement["rib_emetteur"]
rib_receveur = abonnement["rib_receveur"]
montant = abonnement["montant"]
destinataire_id = None
emetteur_id = None
for user_id, data in comptes.get(banque, {}).items():
if data.get("rib") == rib_emetteur:
emetteur_id = user_id
if data.get("rib") == rib_receveur:
destinataire_id = user_id
if not emetteur_id or not destinataire_id:
continue
if comptes[banque][emetteur_id]["solde"] < montant:
continue
comptes[banque][emetteur_id]["solde"] -= montant
comptes[banque][emetteur_id]["transactions"].append({
"date": datetime.datetime.now().strftime("%d/%m/%Y %H:%M:%S"),
"type": "Virement automatique (abonnement)",
"source": f"Vers RIB {rib_receveur}",
"montant": -montant
})
comptes[banque][destinataire_id]["solde"] += montant
comptes[banque][destinataire_id]["transactions"].append({
"date": datetime.datetime.now().strftime("%d/%m/%Y %H:%M:%S"),
"type": "Virement reçu (abonnement)",
"source": f"{rib_emetteur}",
"montant": montant
})
updated = True
if updated:
with open("comptes_bancaires.json", 'w', encoding='utf-8') as f:
json.dump(comptes, f, ensure_ascii=False, indent=4)
@executer_abonnements.before_loop
async def before_abonnement_loop(self):
await self.bot.wait_until_ready()
async def setup(bot):
await bot.add_cog(Abonnement(bot))

224
commandes/absence_staff.py Normal file
View file

@ -0,0 +1,224 @@
from discord.ext import commands, tasks
from discord import app_commands, ui
import discord
import json
import asyncio
import os
from datetime import datetime
ABSENCE_CHANNEL_ID = 1369669999677145099
ABSENCE_FILE = "absences.json"
ABSENCE_FILE_TMP = "absences.json.tmp"
file_lock = asyncio.Lock()
async def load_absences():
async with file_lock:
try:
with open(ABSENCE_FILE, "r") as f:
return json.load(f)
except FileNotFoundError:
return {}
async def save_absences(data):
async with file_lock:
with open(ABSENCE_FILE_TMP, "w") as f:
json.dump(data, f, indent=4)
os.replace(ABSENCE_FILE_TMP, ABSENCE_FILE)
class Absence(commands.Cog):
def __init__(self, bot):
self.bot = bot
self.check_absences.start()
@commands.hybrid_command(name="absence", with_app_command=True)
async def absence(self, ctx, motif: str, date_debut: str, date_fin: str, superieur: discord.Member = None):
try:
debut = datetime.strptime(date_debut, "%d/%m/%Y")
fin = datetime.strptime(date_fin, "%d/%m/%Y")
except ValueError:
return await ctx.send("❌ Format de date invalide. Utilise JJ/MM/AAAA.", ephemeral=True)
if debut > fin:
return await ctx.send("❌ La date de début doit précéder la date de fin.", ephemeral=True)
abs_data = await load_absences()
if str(ctx.author.id) in abs_data:
return await ctx.send("❌ Tu as déjà déclaré une absence. Utilise `/fin_absence` pour la terminer.", ephemeral=True)
duree = (fin - debut).days + 1
etat = "à venir" if debut > datetime.today() else "en cours"
abs_data[str(ctx.author.id)] = {
"pseudo": ctx.author.name,
"debut": date_debut,
"fin": date_fin,
"motif": motif,
"superieur": superieur.mention if superieur else "Aucun",
"message_id": None,
"etat": etat
}
await save_absences(abs_data)
channel = self.bot.get_channel(ABSENCE_CHANNEL_ID)
if not channel:
return await ctx.send("❌ Salon d'absence introuvable. Vérifie l'ID.", ephemeral=True)
absence_message = await channel.send(
f"# ABSENCE STAFF :\n"
f"- **Pseudo Discord** : {ctx.author.mention}\n"
f"- **Durée** : {duree} jours\n"
f"- **Débute le** : {date_debut}\n"
f"- **Termine le** : {date_fin}\n"
f"- **Motif** : {motif}\n"
f"- **Mention du supérieur hiérarchique** : {superieur.mention if superieur else 'Aucun'}"
)
abs_data[str(ctx.author.id)]["message_id"] = absence_message.id
await save_absences(abs_data)
await ctx.send("✅ Absence enregistrée avec succès !", ephemeral=True)
@commands.hybrid_command(name="fin_absence", with_app_command=True)
async def fin_absence(self, ctx):
abs_data = await load_absences()
user_id = str(ctx.author.id)
if user_id not in abs_data:
return await ctx.send("❌ Tu n'as pas d'absence en cours.", ephemeral=True)
message_id = abs_data[user_id].get("message_id")
channel = self.bot.get_channel(ABSENCE_CHANNEL_ID)
if message_id and channel:
try:
msg = await channel.fetch_message(message_id)
await msg.delete()
except Exception:
pass
del abs_data[user_id]
await save_absences(abs_data)
await ctx.send("✅ Ton absence a été retirée.", ephemeral=True)
@commands.hybrid_command(name="liste_absences", with_app_command=True)
async def liste_absences(self, ctx):
abs_data = await load_absences()
absences = [
(user_id, info) for user_id, info in abs_data.items()
if info.get("etat") == "en cours"
]
if not absences:
return await ctx.send("Aucune absence staff en cours.", ephemeral=True)
per_page = 3
pages = [absences[i:i + per_page] for i in range(0, len(absences), per_page)]
def create_embed(page_index: int) -> discord.Embed:
embed = discord.Embed(
title=f"📋 Absences staff en cours (Page {page_index + 1}/{len(pages)})",
color=discord.Color.orange()
)
for _, info in pages[page_index]:
embed.add_field(
name=info["pseudo"],
value=(
f"**Début** : {info['debut']}\n"
f"**Fin** : {info['fin']}\n"
f"**Motif** : {info['motif']}\n"
f"**Supérieur** : {info['superieur']}"
),
inline=False
)
embed.set_footer(text="Gestion des absences du staff")
return embed
class PaginatorView(ui.View):
def __init__(self):
super().__init__(timeout=60)
self.page = 0
@ui.button(label="⬅️ Précédent", style=discord.ButtonStyle.secondary, disabled=True)
async def previous(self, interaction: discord.Interaction, button: discord.ui.Button):
self.page -= 1
await interaction.response.edit_message(embed=create_embed(self.page), view=self)
self.update_buttons()
@ui.button(label="➡️ Suivant", style=discord.ButtonStyle.secondary, disabled=len(pages) == 1)
async def next(self, interaction: discord.Interaction, button: discord.ui.Button):
self.page += 1
await interaction.response.edit_message(embed=create_embed(self.page), view=self)
self.update_buttons()
def update_buttons(self):
self.children[0].disabled = self.page == 0
self.children[1].disabled = self.page >= len(pages) - 1
view = PaginatorView()
await ctx.send(embed=create_embed(0), view=view, ephemeral=True)
@tasks.loop(minutes=1)
async def check_absences(self):
abs_data = await load_absences()
today = datetime.today()
to_remove = []
for user_id, info in abs_data.items():
debut = datetime.strptime(info["debut"], "%d/%m/%Y")
fin = datetime.strptime(info["fin"], "%d/%m/%Y")
etat = info.get("etat", "en cours")
if etat == "à venir" and debut <= today:
for guild in self.bot.guilds:
member = guild.get_member(int(user_id))
if member:
await self.deactivate_mentions(member)
abs_data[user_id]["etat"] = "en cours"
if etat == "en cours" and fin < today:
for guild in self.bot.guilds:
member = guild.get_member(int(user_id))
if member:
await self.activate_mentions(member)
to_remove.append(user_id)
for user_id in to_remove:
abs_data.pop(user_id, None)
await save_absences(abs_data)
async def deactivate_mentions(self, member):
role = discord.utils.get(member.guild.roles, name="Absence")
if not role:
role = await member.guild.create_role(name="Absence", reason="Gestion des absences staff")
await member.add_roles(role, reason="Début d'absence staff")
async def activate_mentions(self, member):
role = discord.utils.get(member.guild.roles, name="Absence")
if role:
await member.remove_roles(role, reason="Fin d'absence staff")
@commands.Cog.listener()
async def on_message(self, message):
if message.author.bot:
return
abs_data = await load_absences()
absent_ids = {k for k, v in abs_data.items() if v.get("etat") == "en cours"}
if str(message.author.id) in absent_ids:
await message.channel.send(
f"{message.author.mention}, tu es actuellement déclaré absent. Merci de prévenir si tu es de retour !",
delete_after=30
)
for user_id in absent_ids:
if f"<@{user_id}>" in message.content or f"<@!{user_id}>" in message.content:
msg_id = abs_data[user_id].get("message_id")
absence_url = f"https://discord.com/channels/{message.guild.id}/{ABSENCE_CHANNEL_ID}/{msg_id}" if msg_id else ""
await message.reply(
f"{message.author.mention}, cet utilisateur est absent. Plus d'infos : {absence_url}",
delete_after=60
)
async def setup(bot):
await bot.add_cog(Absence(bot))

146
commandes/ajouter_argent.py Normal file
View file

@ -0,0 +1,146 @@
import discord
from discord import app_commands
from discord.ext import commands
import json
from datetime import datetime
BANQUIER_ROLE_ID = 1360314740567576616 # ID du rôle des banquiers # ID du rôle banquier # ID du rôle banquier
class AjouterArgent(commands.Cog):
def __init__(self, bot):
self.bot = bot
# Choix de banques automatiques
banque_choices = [
app_commands.Choice(name="Victoria's Credit Bank", value="Victoria's Credit Bank"),
app_commands.Choice(name="NGC Bank", value="NGC Bank"),
]
@app_commands.command(
name="ajouter_argent",
description="Ajoute de l'argent à un compte (réservé aux banquiers)."
)
@app_commands.choices(banque=banque_choices)
@app_commands.describe(
rib="Le RIB du compte",
montant="Montant à ajouter",
banque="Banque cible (facultatif)"
)
async def ajouter_argent(
self,
interaction: discord.Interaction,
rib: str,
montant: float,
banque: app_commands.Choice[str] = None
):
# Vérification du rôle
if not any(role.id == BANQUIER_ROLE_ID for role in interaction.user.roles):
await interaction.response.send_message(
"❌ Vous n'avez pas la permission d'utiliser cette commande.",
ephemeral=True
)
return
if montant <= 0:
await interaction.response.send_message(
"❌ Le montant doit être supérieur à zéro.",
ephemeral=True
)
return
banque_nom = banque.value if banque else None
# Chargement des fichiers
with open("comptes_bancaires.json", "r", encoding="utf-8") as f:
comptes = json.load(f)
with open("comptes_entreprises.json", "r", encoding="utf-8") as f:
entreprises = json.load(f)
cible_data = None
user_id = None
is_entreprise = False
# Recherche dans la banque spécifiée (si précisée)
if banque_nom and banque_nom in comptes:
for uid, info in comptes[banque_nom].items():
if info.get("rib") == rib:
cible_data = comptes
user_id = uid
break
if not user_id and banque_nom and banque_nom in entreprises:
for nom_entreprise, info in entreprises[banque_nom].items():
if info.get("rib") == rib:
cible_data = entreprises
user_id = nom_entreprise
is_entreprise = True
break
# Si banque non précisée ou compte non trouvé, recherche dans toutes les banques
if not user_id and not banque_nom:
# Recherche dans toutes les banques utilisateurs
for banque_courante, comptes_banque in comptes.items():
for uid, info in comptes_banque.items():
if info.get("rib") == rib:
cible_data = comptes
user_id = uid
banque_nom = banque_courante
break
if user_id:
break
if not user_id and not is_entreprise:
# Recherche dans toutes les banques entreprises
for banque_courante, entreprises_banque in entreprises.items():
for nom_entreprise, info in entreprises_banque.items():
if info.get("rib") == rib:
cible_data = entreprises
user_id = nom_entreprise
banque_nom = banque_courante
is_entreprise = True
break
if user_id:
break
if not user_id:
await interaction.response.send_message(
"❌ Aucun compte trouvé avec ce RIB.",
ephemeral=True
)
return
# Ajout du montant
cible_data[banque_nom][user_id]["solde"] += montant
transaction = {
"date": datetime.now().strftime("%d/%m/%Y %H:%M:%S"),
"type": "Dépôt",
"source": "Banque",
"montant": montant,
"emetteur": interaction.user.id,
"destinataire": user_id
}
cible_data[banque_nom][user_id]["transactions"].append(transaction)
# Sauvegarde
if is_entreprise:
with open("comptes_entreprises.json", "w", encoding="utf-8") as f:
json.dump(entreprises, f, ensure_ascii=False, indent=4)
else:
with open("comptes_bancaires.json", "w", encoding="utf-8") as f:
json.dump(comptes, f, ensure_ascii=False, indent=4)
# Envoyer un MP à l'utilisateur (sauf entreprise)
try:
user = await self.bot.fetch_user(int(user_id))
await user.send(
f"💰 Un dépôt de **{montant:.2f}€** a été effectué sur votre compte dans **{banque_nom}**."
)
except Exception:
pass # Si l'utilisateur a désactivé les MP
await interaction.response.send_message(
f"✅ **{montant:.2f}€** ajoutés avec succès au compte `{rib}` dans la banque **{banque_nom}**.",
ephemeral=True
)
async def setup(bot):
await bot.add_cog(AjouterArgent(bot))

View file

@ -0,0 +1,220 @@
import discord
from discord import app_commands
from discord.ext import commands, tasks
import dateparser
from datetime import datetime, timezone
ROLE_PING_ID = 1278319901651632138 # ID du rôle citoyen # Rôle pour l'annonce principale
REMINDER_ROLE_ID = 1303750986900439151 # ID du rôle rappels sessions # Rôle pour les rappels (à adapter !)
presence_data = {}
class PresenceView(discord.ui.View):
def __init__(self, message_id):
super().__init__(timeout=None)
self.message_id = message_id
async def update_message(self, interaction, state):
user_id = interaction.user.id
if self.message_id not in presence_data:
presence_data[self.message_id] = {
"present": set(), "absent": set(), "retard": set(),
"date": None, "sent_dm": False,
"reminder1": None, "reminder2": None,
"reminder1_sent": False, "reminder2_sent": False,
"channel_id": None, "guild_id": None
}
for key in ["present", "absent", "retard"]:
presence_data[self.message_id][key].discard(user_id)
presence_data[self.message_id][state].add(user_id)
n_present = len(presence_data[self.message_id]["present"])
n_absent = len(presence_data[self.message_id]["absent"])
n_retard = len(presence_data[self.message_id]["retard"])
msg = await interaction.channel.fetch_message(self.message_id)
lines = msg.content.split('\n')
# On modifie uniquement les lignes de présence
for i, line in enumerate(lines):
if line.startswith("> - Je serai présent"):
lines[i] = f"> - Je serai présent : <:check:1297296859206778911> **({n_present})**"
elif line.startswith("> - Je serai absent"):
lines[i] = f"> - Je serai absent : <:uncheck:1297296882778902598> **({n_absent})**"
elif line.startswith("> - Je serai en retard"):
lines[i] = f"> - Je serai en retard : ⏰ **({n_retard})**"
new_content = '\n'.join(lines)
await msg.edit(content=new_content, view=self)
@discord.ui.button(label="Présent", style=discord.ButtonStyle.success, emoji="✅", custom_id="presence_present")
async def present(self, interaction: discord.Interaction, button: discord.ui.Button):
await self.update_message(interaction, "present")
await interaction.response.send_message("Merci, tu as été noté comme **présent** à la session RP !", ephemeral=True)
@discord.ui.button(label="Absent", style=discord.ButtonStyle.danger, emoji="❌", custom_id="presence_absent")
async def absent(self, interaction: discord.Interaction, button: discord.ui.Button):
await self.update_message(interaction, "absent")
await interaction.response.send_message("Merci, tu as été noté comme **absent** à la session RP.", ephemeral=True)
@discord.ui.button(label="En retard", style=discord.ButtonStyle.primary, emoji="⏰", custom_id="presence_retard")
async def retard(self, interaction: discord.Interaction, button: discord.ui.Button):
await self.update_message(interaction, "retard")
await interaction.response.send_message("Merci, tu as été noté comme **en retard** pour la session RP.", ephemeral=True)
class AnnonceSession(commands.Cog):
def __init__(self, bot):
self.bot = bot
self.check_sessions.start()
def cog_unload(self):
self.check_sessions.cancel()
@app_commands.command(
name="annonce_session",
description="Annonce une session RP avec image, rappels et boutons de présence."
)
@app_commands.describe(
date="Date de la session (ex: Vendredi 20 Juin à 20h45)",
reminder1="Date/heure du 1er rappel (ex: Jeudi 19 Juin à 19h)",
reminder2="Date/heure du 2e rappel (ex: Vendredi 20 Juin à 15h)",
image="Image à joindre (obligatoire)"
)
async def annonce_session(
self,
interaction: discord.Interaction,
date: str,
reminder1: str,
reminder2: str,
image: discord.Attachment
):
if not image.content_type or not image.content_type.startswith("image/"):
await interaction.response.send_message(
"❌ Le fichier joint doit être une image valide.", ephemeral=True
)
return
# On parse toutes les dates en Europe/Paris
session_date = dateparser.parse(
date, languages=['fr'],
settings={"TIMEZONE": "Europe/Paris", "RETURN_AS_TIMEZONE_AWARE": True}
)
reminder1_date = dateparser.parse(
reminder1, languages=['fr'],
settings={"TIMEZONE": "Europe/Paris", "RETURN_AS_TIMEZONE_AWARE": True}
)
reminder2_date = dateparser.parse(
reminder2, languages=['fr'],
settings={"TIMEZONE": "Europe/Paris", "RETURN_AS_TIMEZONE_AWARE": True}
)
if not session_date or not reminder1_date or not reminder2_date:
await interaction.response.send_message(
"❌ Une des dates n'a pas été comprise. Vérifie le format.", ephemeral=True
)
return
session_date = session_date.astimezone(timezone.utc)
reminder1_date = reminder1_date.astimezone(timezone.utc)
reminder2_date = reminder2_date.astimezone(timezone.utc)
message = (
"# <:VV:1339237574593609780> - **SESSION VICTORIA’S VICE**\n"
"\n"
"```📅 - HORAIRE DE LA SESSION```\n"
f"> - Nous vous donnons rendez-vous le **{date}** pour une **session officielle** sur Victoria’s Vice. 🗓️\n"
"---\n"
"```🤔 - COMMENT REJOINDRE LA SESSION ?```\n"
"> - Pour lancer la Map, veuillez entrer le code ci-dessous sur la barre de recherche de Fortnite, par la suite lancer la Map en **public** afin de pouvoir nous rejoindre.\n"
"> - **`CODE DE LA MAP`** <:arrow:1297491509201080362> [0986-1836-1625]\n"
"> - **`LIEN DIRECT DE LA MAP`** <:arrow:1297491509201080362> <https://play.fn.gg/island/0986-1836-1625>\n"
"---\n"
"```🖐 - SEREZ-VOUS PRÉSENT(E) EN SESSION ?```\n"
"> - Je serai présent : <:check:1297296859206778911> **(0)**\n"
"> - Je serai absent : <:uncheck:1297296882778902598> **(0)**\n"
"> - Je serai en retard : ⏰ **(0)**\n"
"---"
f"> - **`PING`** : <@&{ROLE_PING_ID}>"
)
await interaction.response.send_message(
content=message,
files=[await image.to_file()]
)
msg = await interaction.original_response()
presence_data[msg.id] = {
"present": set(),
"absent": set(),
"retard": set(),
"date": session_date,
"sent_dm": False,
"reminder1": reminder1_date,
"reminder2": reminder2_date,
"reminder1_sent": False,
"reminder2_sent": False,
"channel_id": msg.channel.id,
"guild_id": msg.guild.id
}
await msg.edit(view=PresenceView(msg.id))
@app_commands.command(
name="liste_presence",
description="Liste les présences pour une session RP."
)
@app_commands.describe(
message_id="ID du message d'annonce"
)
async def liste_presence(self, interaction: discord.Interaction, message_id: str):
try:
mid = int(message_id)
except ValueError:
await interaction.response.send_message("ID de message invalide.", ephemeral=True)
return
if mid not in presence_data:
await interaction.response.send_message("Aucune donnée de présence pour ce message.", ephemeral=True)
return
data = presence_data[mid]
def mentions(ids):
return ', '.join(f"<@{uid}>" for uid in ids) if ids else "Aucun"
await interaction.response.send_message(
f"**Présents ({len(data['present'])})** : {mentions(data['present'])}\n"
f"**Absents ({len(data['absent'])})** : {mentions(data['absent'])}\n"
f"**En retard ({len(data['retard'])})** : {mentions(data['retard'])}",
ephemeral=True
)
@tasks.loop(seconds=30)
async def check_sessions(self):
now = datetime.now(timezone.utc)
for msg_id, data in list(presence_data.items()):
# Premier rappel
if data.get("reminder1") and not data.get("reminder1_sent", False) and now >= data["reminder1"]:
await self.send_reminder(data, msg_id, "Premier rappel !")
data["reminder1_sent"] = True
# Deuxième rappel
if data.get("reminder2") and not data.get("reminder2_sent", False) and now >= data["reminder2"]:
await self.send_reminder(data, msg_id, "Deuxième rappel !")
data["reminder2_sent"] = True
# Envoi du MP à l'heure de la session
if data["date"] and not data.get("sent_dm", False) and now >= data["date"]:
for user_id in data["present"]:
try:
user = await self.bot.fetch_user(user_id)
channel = self.bot.get_channel(data["channel_id"])
if channel:
msg = await channel.fetch_message(msg_id)
await user.send(f"La session RP commence ! Voici le lien de l'annonce : {msg.jump_url}")
except Exception as e:
print(f"Erreur lors de l'envoi du DM à {user_id} : {e}")
data["sent_dm"] = True
async def send_reminder(self, data, msg_id, label):
channel = self.bot.get_channel(data["channel_id"])
if channel:
msg = await channel.fetch_message(msg_id)
await channel.send(
f"<@&{REMINDER_ROLE_ID}> ⏰ **{label}**\nLa session RP approche !\nAnnonce : {msg.jump_url}"
)
@check_sessions.before_loop
async def before_check_sessions(self):
await self.bot.wait_until_ready()
async def setup(bot):
await bot.add_cog(AnnonceSession(bot))

View file

@ -0,0 +1,45 @@
import discord
from discord import app_commands
from discord.ext import commands
import re
class FormatAllNicknames(commands.Cog):
def __init__(self, bot):
self.bot = bot
@app_commands.command(name="formatall", description="Force tous les pseudos au format 'Prénom Nom | Epic'")
@app_commands.checks.has_permissions(manage_nicknames=True)
async def format_all(self, interaction: discord.Interaction):
await interaction.response.defer(thinking=True)
guild = interaction.guild
modified_count = 0
# Regex : deux mots avant le "|"
pattern_correct = re.compile(r"^[^\|]+\s[^\|]+\s\| .+")
for member in guild.members:
if member.bot:
continue
current_nick = member.nick or member.name
# S'il respecte déjà le bon format ("Prénom Nom | Quelquechose"), on skip
if pattern_correct.match(current_nick):
continue
try:
# Sinon, forcer à "Prénom Nom | Epic"
new_nick = "Prénom Nom | Epic"
await member.edit(nick=new_nick)
modified_count += 1
except discord.Forbidden:
continue
except discord.HTTPException:
continue
await interaction.followup.send(f"✅ Renommage terminé : {modified_count} membres modifiés.")
async def setup(bot):
await bot.add_cog(FormatAllNicknames(bot))

93
commandes/compte.py Normal file
View file

@ -0,0 +1,93 @@
import discord
from discord import app_commands
from discord.ext import commands
from utils.banque_manager import BanqueManager
banque_manager = BanqueManager()
class Compte(commands.Cog):
def __init__(self, bot):
self.bot = bot
@app_commands.command(
name="compte",
description="Afficher vos comptes bancaires et vos 3 dernières transactions"
)
@app_commands.describe(
membre="Afficher le compte d'un autre membre (réservé au gouvernement)"
)
async def compte(self, interaction: discord.Interaction, membre: discord.Member = None):
"""Affiche les soldes et dernières transactions de l'utilisateur ou d'un autre membre (si gouvernement)"""
banque_manager.load_data()
# Par défaut, on cible l'utilisateur qui exécute la commande
cible = interaction.user
user_id = str(cible.id)
# Si un autre membre est précisé, on vérifie le rôle
if membre and membre != interaction.user:
role_gouv = discord.utils.get(interaction.guild.roles, name="🌍│Gouvernement")
if role_gouv not in interaction.user.roles:
await interaction.response.send_message(
"❌ Vous devez avoir le rôle **gouvernement** pour consulter le compte d'un autre membre.",
ephemeral=True
)
return
cible = membre
user_id = str(membre.id)
comptes = banque_manager.comptes
embed = discord.Embed(
title=f"💳 Comptes bancaires de {cible.display_name}",
color=discord.Color.blue()
)
banques_trouvees = False
for banque, data in comptes.items():
if not isinstance(data, dict) or user_id not in data:
continue # Ce n'est pas une banque valide pour cet utilisateur
compte = data[user_id]
rib = compte.get("rib", "Non défini")
solde = compte.get("solde", 0)
transactions = compte.get("transactions", [])
# Récupérer les 3 dernières transactions avec date
last_transactions = transactions[-3:] if transactions else []
tx_str = ""
for tx in last_transactions:
date = tx.get("date", "Date inconnue")
tx_type = tx.get("type", "Inconnu")
montant = tx.get("montant", 0)
source = tx.get("emetteur", tx.get("source", "Inconnu"))
dest = tx.get("destinataire", "Inconnu")
if tx_type.lower() == "virement" or tx_type.lower().startswith("virement"):
tx_str += f"- 🕒 {date} : **{tx_type}** de **{montant}€** à <@{dest}>\n"
elif tx_type.lower() == "virement reçu":
tx_str += f"- 🕒 {date} : **{tx_type}** de **{montant}€** de <@{source}>\n"
else:
tx_str += f"- 🕒 {date} : **{tx_type}** de **{montant}€**\n"
if not tx_str:
tx_str = "*Aucune transaction.*"
embed.add_field(
name=f"🏦 {banque}",
value=f"💳 **RIB** : `{rib}`\n💰 **Solde** : `{solde}€`\n📜 **Transactions récentes :**\n{tx_str}",
inline=False
)
banques_trouvees = True
if not banques_trouvees:
embed.description = "❌ Aucun compte bancaire trouvé dans les banques enregistrées pour cet utilisateur."
await interaction.response.send_message(embed=embed, ephemeral=True)
async def setup(bot):
await bot.add_cog(Compte(bot))

View file

@ -0,0 +1,102 @@
import discord
from discord import app_commands
from discord.ext import commands
import json
import math
BANQUE_NATIONALE_ROLE_ID = 1362031086959329470 # Remplace par l’ID réel du rôle banque_nationale
class CompteBanqueNationale(commands.Cog):
def __init__(self, bot):
self.bot = bot
def get_banque_nationale_data(self):
try:
with open("comptes_entreprises.json", "r+", encoding="utf-8") as f:
data = json.load(f)
# Crée la section si elle n'existe pas
if "banque_nationale" not in data:
data["banque_nationale"] = {
"BANQUE": {
"rib": "11111 22222 33333333333 44",
"solde": 0.0,
"transactions": [{
"date": "Création automatique",
"type": "Création de compte",
"source": "Système",
"montant": 0.0
}]
}
}
f.seek(0)
json.dump(data, f, indent=4, ensure_ascii=False)
f.truncate()
banque_data = data["banque_nationale"].get("BANQUE", {})
return banque_data.get("solde", 0.0), banque_data.get("transactions", [])
except Exception as e:
print(f"Erreur lecture ou création banque_nationale : {e}")
return 0.0, []
@app_commands.command(name="voir_banque_nationale", description="Voir solde et transactions du compte banque nationale")
async def voir_banque_nationale(self, interaction: discord.Interaction):
if not any(role.id == BANQUE_NATIONALE_ROLE_ID for role in interaction.user.roles):
await interaction.response.send_message("❌ Vous n'avez pas la permission d'accéder à ce compte.", ephemeral=True)
return
solde, transactions = self.get_banque_nationale_data()
if not transactions:
await interaction.response.send_message("📄 Aucune transaction trouvée sur le compte banque nationale.", ephemeral=True)
return
# Pagination
pages = []
per_page = 5
total_pages = math.ceil(len(transactions) / per_page)
for i in range(total_pages):
start = i * per_page
end = start + per_page
chunk = transactions[start:end]
embed = discord.Embed(
title="🏦 Banque Nationale - Historique",
description=f"Page {i+1}/{total_pages}",
color=discord.Color.blue()
)
embed.add_field(name="💰 Solde", value=f"{solde:.2f}€", inline=False)
for tx in chunk:
description = (
f"**Date**: {tx.get('date', 'N/A')}\n"
f"**Type**: {tx.get('type', 'N/A')}\n"
f"**Montant**: {tx.get('montant', 0)}€\n"
f"**Source**: {tx.get('source', 'N/A')}"
)
embed.add_field(name="📄 Transaction", value=description, inline=False)
pages.append(embed)
class Pagination(discord.ui.View):
def __init__(self):
super().__init__(timeout=None)
self.current = 0
@discord.ui.button(label="⬅️", style=discord.ButtonStyle.grey)
async def previous(self, interaction_btn: discord.Interaction, button: discord.ui.Button):
if self.current > 0:
self.current -= 1
await interaction_btn.response.edit_message(embed=pages[self.current], view=self)
@discord.ui.button(label="➡️", style=discord.ButtonStyle.grey)
async def next(self, interaction_btn: discord.Interaction, button: discord.ui.Button):
if self.current < len(pages) - 1:
self.current += 1
await interaction_btn.response.edit_message(embed=pages[self.current], view=self)
await interaction.response.send_message(embed=pages[0], view=Pagination(), ephemeral=True)
async def setup(bot):
await bot.add_cog(CompteBanqueNationale(bot))

92
commandes/compte_fbm.py Normal file
View file

@ -0,0 +1,92 @@
import discord
from discord import app_commands
from discord.ext import commands
import json
import math
FBM_ROLE_ID = 1353610672818360330 # ID du rôle autorisé à accéder au compte FBM
class CompteFBM(commands.Cog):
def __init__(self, bot):
self.bot = bot
def get_all_transactions(self):
try:
with open("comptes_entreprises.json", "r", encoding="utf-8") as f:
data = json.load(f)
fbm_data = data.setdefault("fbm", {}).setdefault("FBM", {
"solde": 0.0,
"transactions": []
})
return fbm_data["solde"], fbm_data["transactions"]
except Exception as e:
print(f"Erreur lecture comptes_entreprises.json : {e}")
return 0.0, []
@app_commands.command(name="compte_fbm", description="Affiche le solde et l'historique du compte FBM (réservé au personnel FBM).")
async def compte_fbm(self, interaction: discord.Interaction):
if not any(role.id == FBM_ROLE_ID for role in interaction.user.roles):
await interaction.response.send_message("❌ Vous n'avez pas la permission d'accéder à ce compte.", ephemeral=True)
return
solde, transactions = self.get_all_transactions()
if not transactions:
embed = discord.Embed(
title="📄 Compte FBM",
description="Aucune transaction enregistrée.",
color=discord.Color.orange()
)
embed.add_field(name="💰 Solde actuel", value=f"{solde:.2f}€", inline=False)
await interaction.response.send_message(embed=embed, ephemeral=True)
return
# Pagination
pages = []
per_page = 5
total_pages = math.ceil(len(transactions) / per_page)
for i in range(total_pages):
start = i * per_page
end = start + per_page
chunk = transactions[start:end]
embed = discord.Embed(
title="📄 Historique FBM",
description=f"Page {i+1}/{total_pages}",
color=discord.Color.orange()
)
embed.add_field(name="💰 Solde actuel", value=f"{solde:.2f}€", inline=False)
for tx in chunk:
desc = (
f"**Date**: {tx.get('date', 'N/A')}\n"
f"**Type**: {tx.get('type', 'N/A')}\n"
f"**Montant**: {tx.get('montant', 0)}€\n"
f"**Source**: {tx.get('source', 'N/A')}"
)
embed.add_field(name="🔸 Transaction", value=desc, inline=False)
pages.append(embed)
class Pagination(discord.ui.View):
def __init__(self):
super().__init__(timeout=None)
self.current = 0
@discord.ui.button(label="⬅️", style=discord.ButtonStyle.grey)
async def previous(self, interaction_btn: discord.Interaction, button: discord.ui.Button):
if self.current > 0:
self.current -= 1
await interaction_btn.response.edit_message(embed=pages[self.current], view=self)
@discord.ui.button(label="➡️", style=discord.ButtonStyle.grey)
async def next(self, interaction_btn: discord.Interaction, button: discord.ui.Button):
if self.current < len(pages) - 1:
self.current += 1
await interaction_btn.response.edit_message(embed=pages[self.current], view=self)
await interaction.response.send_message(embed=pages[0], view=Pagination(), ephemeral=True)
async def setup(bot):
await bot.add_cog(CompteFBM(bot))

View file

@ -0,0 +1,84 @@
import discord
from discord import app_commands
from discord.ext import commands
import json
import math
GOUVERNEMENT_ROLE_ID = 1278305482926194701 # Remplace avec l’ID de ton rôle Gouvernement # Remplace avec l’ID de ton rôle Gouvernement
class CompteGouvernement(commands.Cog):
def __init__(self, bot):
self.bot = bot
def get_gouv_data(self):
try:
with open("comptes_entreprises.json", "r", encoding="utf-8") as f:
data = json.load(f)
gouv_data = data.get("Gouvernement", {}).get("GOUV", {})
return gouv_data.get("solde", 0.0), gouv_data.get("transactions", [])
except Exception as e:
print(f"Erreur lecture comptes_entreprises.json : {e}")
return 0.0, []
@app_commands.command(name="compte_gouvernement", description="Affiche le solde et les transactions du compte Gouvernement.")
async def compte_gouvernement(self, interaction: discord.Interaction):
if not any(role.id == GOUVERNEMENT_ROLE_ID for role in interaction.user.roles):
await interaction.response.send_message("❌ Vous n'avez pas la permission d'accéder à ce compte.", ephemeral=True)
return
solde, transactions = self.get_gouv_data()
if not transactions:
await interaction.response.send_message("📄 Aucune transaction trouvée pour le gouvernement.", ephemeral=True)
return
# Pagination setup
pages = []
per_page = 5
total_pages = math.ceil(len(transactions) / per_page)
for i in range(total_pages):
start = i * per_page
end = start + per_page
chunk = transactions[start:end]
embed = discord.Embed(
title="🏛️ Compte Gouvernement",
description=f"Page {i+1}/{total_pages}",
color=discord.Color.dark_gold()
)
embed.add_field(name="💰 Solde", value=f"{solde:.2f}€", inline=False)
for tx in chunk:
desc = (
f"**Date**: {tx.get('date', 'N/A')}\n"
f"**Type**: {tx.get('type', 'N/A')}\n"
f"**Montant**: {tx.get('montant', 0)}€\n"
f"**Source**: {tx.get('source', 'N/A')}"
)
embed.add_field(name="📄 Transaction", value=desc, inline=False)
pages.append(embed)
# Vue de pagination
class Pagination(discord.ui.View):
def __init__(self):
super().__init__(timeout=None)
self.current = 0
@discord.ui.button(label="⬅️", style=discord.ButtonStyle.grey)
async def previous(self, interaction_btn: discord.Interaction, button: discord.ui.Button):
if self.current > 0:
self.current -= 1
await interaction_btn.response.edit_message(embed=pages[self.current], view=self)
@discord.ui.button(label="➡️", style=discord.ButtonStyle.grey)
async def next(self, interaction_btn: discord.Interaction, button: discord.ui.Button):
if self.current < len(pages) - 1:
self.current += 1
await interaction_btn.response.edit_message(embed=pages[self.current], view=self)
await interaction.response.send_message(embed=pages[0], view=Pagination(), ephemeral=True)
async def setup(bot):
await bot.add_cog(CompteGouvernement(bot))

View file

@ -0,0 +1,96 @@
import discord
from discord import app_commands
from discord.ext import commands
import json
import math
URSAFF_ROLE_ID = 1362056014584615213 # Remplace par l’ID réel du rôle URSAFF # Remplace par l’ID réel du rôle URSAFF
class CompteUrssaf(commands.Cog):
def __init__(self, bot):
self.bot = bot
def get_all_transactions(self):
try:
with open("comptes_entreprises.json", "r", encoding="utf-8") as f:
data = json.load(f)
except Exception as e:
print(f"Erreur lecture comptes_entreprises.json : {e}")
return 0.0, []
# Si URSAFF n'existe pas, on le crée
if "ursaff" not in data:
data["ursaff"] = {
"URSSAFF": {
"rib": "00000 00000 00000000000 01",
"solde": 0.0,
"transactions": []
}
}
with open("comptes_entreprises.json", "w", encoding="utf-8") as f:
json.dump(data, f, ensure_ascii=False, indent=4)
ursaff_data = data.get("ursaff", {}).get("URSAFF", {})
return ursaff_data.get("solde", 0.0), ursaff_data.get("transactions", [])
@app_commands.command(name="compte_ursaff", description="Affiche le solde et l'historique complet du compte URSAFF (réservé aux membres URSAFF).")
async def compte_ursaff(self, interaction: discord.Interaction):
# Vérifie que l'utilisateur a le rôle URSAFF
if not any(role.id == URSAFF_ROLE_ID for role in interaction.user.roles):
await interaction.response.send_message("❌ Vous n'avez pas la permission d'accéder à ce compte.", ephemeral=True)
return
solde, transactions = self.get_all_transactions()
# Pagination setup
pages = []
per_page = 5
total_pages = max(1, math.ceil(len(transactions) / per_page))
for i in range(total_pages):
start = i * per_page
end = start + per_page
chunk = transactions[start:end]
embed = discord.Embed(
title="📄 Historique URSSAF",
description=f"Page {i+1}/{total_pages}",
color=discord.Color.dark_purple()
)
embed.add_field(name="💼 Solde actuel", value=f"{solde:.2f}€", inline=False)
if chunk:
for tx in chunk:
description = (
f"**Date**: {tx.get('date', 'N/A')}\n"
f"**Type**: {tx.get('type', 'N/A')}\n"
f"**Montant**: {tx.get('montant', 0)}€\n"
f"**Source**: {tx.get('source', 'N/A')}"
)
embed.add_field(name="🔹 Transaction", value=description, inline=False)
else:
embed.add_field(name="📄 Transactions", value="Aucune transaction récente.", inline=False)
pages.append(embed)
class Pagination(discord.ui.View):
def __init__(self):
super().__init__(timeout=None)
self.current = 0
@discord.ui.button(label="⬅️", style=discord.ButtonStyle.grey)
async def previous(self, interaction_btn: discord.Interaction, button: discord.ui.Button):
if self.current > 0:
self.current -= 1
await interaction_btn.response.edit_message(embed=pages[self.current], view=self)
@discord.ui.button(label="➡️", style=discord.ButtonStyle.grey)
async def next(self, interaction_btn: discord.Interaction, button: discord.ui.Button):
if self.current < len(pages) - 1:
self.current += 1
await interaction_btn.response.edit_message(embed=pages[self.current], view=self)
await interaction.response.send_message(embed=pages[0], view=Pagination(), ephemeral=True)
async def setup(bot):
await bot.add_cog(CompteUrssaf(bot))

View file

@ -0,0 +1,73 @@
import discord
from discord import app_commands
from discord.ext import commands
import json
class CompteEntreprise(commands.Cog):
def __init__(self, bot):
self.bot = bot
banque_choices = [
app_commands.Choice(name="Victoria's Credit Bank", value="Victoria's Credit Bank"),
app_commands.Choice(name="NGC Bank", value="NGC Bank"),
]
@app_commands.command(name="compte_entreprise", description="Consulte le solde et les transactions d'une entreprise.")
@app_commands.choices(banque=banque_choices)
async def compte_entreprise(
self,
interaction: discord.Interaction,
banque: app_commands.Choice[str],
nom_entreprise: str,
mot_de_passe: str
):
banque_nom = banque.value
try:
with open("comptes_entreprises.json", "r", encoding="utf-8") as f:
comptes = json.load(f)
except Exception:
await interaction.response.send_message("❌ Erreur de lecture des données bancaires.", ephemeral=True)
return
if banque_nom not in comptes or nom_entreprise not in comptes[banque_nom]:
await interaction.response.send_message("❌ Ce compte entreprise n'existe pas.", ephemeral=True)
return
compte = comptes[banque_nom][nom_entreprise]
# 🔐 Vérification du mot de passe avec la bonne clé
mdp_stocke = compte.get("mdp", "").strip()
mdp_fournit = mot_de_passe.strip()
if mdp_stocke != mdp_fournit:
await interaction.response.send_message("❌ Mot de passe incorrect.", ephemeral=True)
return
solde = compte.get("solde", 0)
transactions = compte.get("transactions", [])
embed = discord.Embed(
title=f"🏢 Compte Entreprise : {nom_entreprise}",
color=discord.Color.teal()
)
embed.add_field(name="🏦 Banque", value=banque_nom, inline=True)
embed.add_field(name="💰 Solde", value=f"{solde:.2f}€", inline=False)
if transactions:
last_transactions = transactions[-3:]
for tx in reversed(last_transactions):
desc = (
f"**Date**: {tx.get('date', 'N/A')}\n"
f"**Type**: {tx.get('type', 'N/A')}\n"
f"**Montant**: {tx.get('montant', 0)}€\n"
f"**Source**: {tx.get('source', 'N/A')}"
)
embed.add_field(name="📄 Transaction", value=desc, inline=False)
else:
embed.add_field(name="📄 Transactions", value="Aucune transaction récente.", inline=False)
await interaction.response.send_message(embed=embed, ephemeral=True)
async def setup(bot):
await bot.add_cog(CompteEntreprise(bot))

68
commandes/counting.py Normal file
View file

@ -0,0 +1,68 @@
import discord
from discord.ext import commands
from discord import app_commands
import json
import os
COUNTING_FILE = "counting_data.json"
counting_channels = {}
def load_counting_channels():
global counting_channels
if os.path.isfile(COUNTING_FILE):
with open(COUNTING_FILE, "r", encoding="utf-8") as f:
data = json.load(f)
counting_channels = {int(k): v for k, v in data.items()}
else:
counting_channels = {}
def save_counting_channels():
with open(COUNTING_FILE, "w", encoding="utf-8") as f:
json.dump({str(k): v for k, v in counting_channels.items()}, f, ensure_ascii=False, indent=2)
class Counting(commands.Cog):
def __init__(self, bot):
self.bot = bot
load_counting_channels()
async def cog_load(self):
# Synchronise les slash commands à l'ajout du cog
await self.bot.tree.sync()
print("Slash commands synchronisées pour Counting.")
@app_commands.command(name="start_counting", description="Démarre un channel de counting.")
async def start_counting(self, interaction: discord.Interaction):
counting_channels[interaction.channel.id] = {"current": 0, "last_user": None}
save_counting_channels()
await interaction.response.send_message("Le counting commence ! Envoyez 1 pour débuter.")
@commands.Cog.listener()
async def on_message(self, message):
if message.author.bot:
return
channel_id = message.channel.id
if channel_id not in counting_channels:
return
state = counting_channels[channel_id]
try:
number = int(message.content.strip())
except ValueError:
return
if number == state["current"] + 1 and message.author.id != state["last_user"]:
state["current"] = number
state["last_user"] = message.author.id
save_counting_channels()
await message.add_reaction("✅")
else:
await message.add_reaction("❌")
await message.channel.send(
f"Erreur ! <@{message.author.id}> a cassé la chaîne à {number}. On repart à 1 !"
)
counting_channels[channel_id] = {"current": 0, "last_user": None}
save_counting_channels()
async def setup(bot):
await bot.add_cog(Counting(bot))

72
commandes/identite.py Normal file
View file

@ -0,0 +1,72 @@
import discord
from discord import app_commands
from discord.ext import commands
from utils.data_manager import DataManager
from datetime import datetime, timedelta
ADMIN_ROLE_ID = 1278305482926194701 # ID du rôle gouvernement # ID du rôle gouvernement
data_manager = DataManager()
class CreerIdentite(commands.Cog):
def __init__(self, bot):
self.bot = bot
@app_commands.command(name="creer_carte_identite", description="Créer une carte d'identité (Réservé aux admins)")
@app_commands.choices(sexe=[
app_commands.Choice(name="Homme", value="Homme"),
app_commands.Choice(name="Femme", value="Femme"),
app_commands.Choice(name="Autre", value="Autre")
])
async def creer_carte_identite(
self,
interaction: discord.Interaction,
utilisateur: discord.Member,
nom: str,
prenom: str,
date_naissance: str,
lieu_naissance: str,
sexe: app_commands.Choice[str]
):
"""Commande réservée aux admins pour créer une carte d'identité"""
if not any(role.id == ADMIN_ROLE_ID for role in interaction.user.roles):
await interaction.response.send_message(
"❌ Vous n'avez pas la permission de créer des cartes d'identité.",
ephemeral=True
)
return
user_id = str(utilisateur.id)
# ➡️ Plus de refus : on écrase directement l'ancienne carte
ancienne_carte = user_id in data_manager.identities
# 📅 Dates de création et d'expiration
date_creation = datetime.now()
date_expiration = (date_creation + timedelta(days=730)).strftime("%d/%m/%Y")
# 📦 Données à enregistrer
identity_data = {
"nom": nom.upper(),
"prenom": prenom.capitalize(),
"date_naissance": date_naissance,
"lieu_naissance": lieu_naissance,
"sexe": sexe.value,
"date_expiration": date_expiration
}
# 💾 Enregistrement
data_manager.identities[user_id] = identity_data
data_manager.save_data()
# 📢 Message différent selon si c'était une ancienne carte ou non
if ancienne_carte:
message = f"✅ Carte d'identité de {utilisateur.mention} **mise à jour** avec succès !"
else:
message = f"✅ Carte d'identité créée pour {utilisateur.mention} avec succès !"
await interaction.response.send_message(message, ephemeral=False)
async def setup(bot):
await bot.add_cog(CreerIdentite(bot))

60
commandes/liste_taches.py Normal file
View file

@ -0,0 +1,60 @@
import discord
from discord.ext import commands
from discord import app_commands
import json
from collections import defaultdict
class Taches(commands.Cog):
def __init__(self, bot):
self.bot = bot
@app_commands.command(name="lister_taches", description="Liste toutes les tâches assignées à tous les utilisateurs.")
async def lister_taches(self, interaction: discord.Interaction):
try:
with open("taches.json", "r", encoding="utf-8") as f:
data = json.load(f)
except Exception:
await interaction.response.send_message("❌ Impossible de lire le fichier des tâches.", ephemeral=True)
return
taches = data.get("taches", {})
if not taches:
await interaction.response.send_message("Aucune tâche trouvée.", ephemeral=True)
return
# Regrouper les tâches par utilisateur assigné
taches_par_utilisateur = defaultdict(list)
for tache_id, tache_info in taches.items():
assigne_id = tache_info.get("assigné")
if assigne_id:
taches_par_utilisateur[assigne_id].append(tache_info)
else:
taches_par_utilisateur[None].append(tache_info) # Pour tâches non assignées
embed = discord.Embed(
title="📋 Liste des tâches assignées",
color=discord.Color.blue()
)
for user_id, user_taches in taches_par_utilisateur.items():
if user_id is None:
nom_utilisateur = "Non assigné"
else:
try:
user = await self.bot.fetch_user(int(user_id))
nom_utilisateur = user.display_name
except Exception:
nom_utilisateur = f"Utilisateur inconnu ({user_id})"
taches_text = ""
for tache in user_taches:
titre = tache.get("titre", "Sans titre")
statut = tache.get("statut", "inconnu")
taches_text += f"- **{titre}** *(état: {statut})*\n"
embed.add_field(name=f"{nom_utilisateur}", value=taches_text, inline=False)
await interaction.response.send_message(embed=embed, ephemeral=False)
async def setup(bot):
await bot.add_cog(Taches(bot))

22
commandes/message.py Normal file
View file

@ -0,0 +1,22 @@
import discord
from discord import app_commands
from discord.ext import commands
from utils.logger import log_info, log_error
class Message(commands.Cog):
def __init__(self, bot):
self.bot = bot
@app_commands.command(name="message", description="Envoyer un message privé")
@app_commands.checks.has_permissions(administrator=True)
async def message_utilisateur(self, interaction: discord.Interaction, membre: discord.Member, message: str):
try:
await membre.send(message)
await interaction.response.send_message(f"Message envoyé à {membre.mention}.", ephemeral=True)
log_info(f"Message envoyé à {membre.name}")
except discord.Forbidden:
await interaction.response.send_message("Impossible d'envoyer un MP.", ephemeral=True)
log_error(f"Échec d'envoi à {membre.name}")
async def setup(bot):
await bot.add_cog(Message(bot))

38
commandes/message_tous.py Normal file
View file

@ -0,0 +1,38 @@
import discord
from discord import app_commands
from discord.ext import commands
class MessageTous(commands.Cog):
def __init__(self, bot: commands.Bot):
self.bot = bot
@app_commands.command(
name="messagetous",
description="Envoyer un message privé à tous les membres du serveur."
)
@app_commands.checks.has_permissions(administrator=True)
async def message_tous(self, interaction: discord.Interaction, message: str):
await interaction.response.defer(ephemeral=True)
succes = 0
erreurs = 0
await interaction.followup.send("Envoi du message en cours...", ephemeral=False)
for member in interaction.guild.members:
if not member.bot and member != interaction.user:
try:
await member.send(message)
succes += 1
except discord.Forbidden:
erreurs += 1
except Exception as e:
erreurs += 1
await interaction.followup.send(
f"✅ Message envoyé à {succes} membres.\n❌ {erreurs} erreurs.",
ephemeral=False
)
async def setup(bot: commands.Bot):
await bot.add_cog(MessageTous(bot))

View file

@ -0,0 +1,76 @@
import discord
from discord import app_commands
from discord.ext import commands
from utils.banque_manager import BanqueManager
import random
# Rôle nécessaire pour créer un compte
BANQUIER_ROLE_ID = 1360314740567576616 # ID du rôle des banquiers # ID du rôle banquier
# Liste des banques disponibles (id interne : nom affiché)
BANQUES = {
"Victoria's Credit Bank": "Victoria's Credit Bank",
"NGC Bank": "Nexus Global Corporation Bank"
}
banque_manager = BanqueManager()
class OuvrirCompte(commands.Cog):
def __init__(self, bot):
self.bot = bot
@app_commands.command(name="ouvrir_compte", description="Ouvre un compte bancaire pour un utilisateur (banquier uniquement).")
@app_commands.choices(
banque=[app_commands.Choice(name=nom, value=id_banque) for id_banque, nom in BANQUES.items()]
)
async def ouvrir_compte(self, interaction: discord.Interaction, utilisateur: discord.Member, banque: app_commands.Choice[str]):
banque_id = banque.value
banque_nom = BANQUES[banque_id]
# Vérification du rôle de banquier
if not any(role.id == BANQUIER_ROLE_ID for role in interaction.user.roles):
await interaction.response.send_message("❌ Vous n'avez pas la permission d'ouvrir un compte.", ephemeral=True)
return
user_id = str(utilisateur.id)
banque_manager.load_data()
# Conversion automatique si l'ancien format est utilisé
if all(isinstance(v, dict) and 'rib' in v for v in banque_manager.comptes.values()):
banque_manager.comptes = {"vicynationale": banque_manager.comptes}
# Vérification si la banque existe dans les données
if banque_id not in banque_manager.comptes:
banque_manager.comptes[banque_id] = {}
# Vérification si l'utilisateur a déjà un compte dans cette banque
if user_id in banque_manager.comptes[banque_id]:
await interaction.response.send_message(
f"❌ {utilisateur.mention} possède déjà un compte à la {banque_nom} !",
ephemeral=True
)
return
# Génération du RIB aléatoire
rib = f"{random.randint(10000, 99999)} {random.randint(10000, 99999)} {random.randint(10000000000, 99999999999)} {random.randint(10, 99)}"
# Création du compte
banque_manager.comptes[banque_id][user_id] = {
"rib": rib,
"solde": 0.0,
"transactions": []
}
banque_manager.save_data()
# Envoi du RIB en MP à l'utilisateur cible
try:
await utilisateur.send(f"🏦 Un compte a été créé pour vous à la **{banque_nom}**.\nVoici votre RIB : `{rib}`")
except discord.Forbidden:
await interaction.response.send_message("❌ Impossible d'envoyer le RIB en message privé (l'utilisateur a les MP fermés).", ephemeral=True)
return
await interaction.response.send_message(f"✅ Compte créé avec succès pour {utilisateur.mention} à la **{banque_nom}**.", ephemeral=True)
async def setup(bot):
await bot.add_cog(OuvrirCompte(bot))

View file

@ -0,0 +1,92 @@
import json
import os
import random
import string
import discord
from discord import app_commands
from discord.ext import commands
COMPTES_ENTREPRISES_PATH = "comptes_entreprises.json"
BANQUIER_ROLE_ID = 1360314740567576616 # ID du rôle des banquiers # ID du rôle banquier
def generer_rib():
return " ".join(["".join(random.choices(string.digits, k=5)) for _ in range(4)])
def generer_mdp():
return "".join(random.choices(string.ascii_letters + string.digits, k=8))
class OuvrirCompteEntreprise(commands.Cog):
def __init__(self, bot):
self.bot = bot
banque_choix = [
app_commands.Choice(name="Victoria's Credit Bank", value="Victoria's Credit Bank"),
app_commands.Choice(name="NGC Bank", value="NGC Bank"),
]
tva_choix = [
app_commands.Choice(name="2%", value=2),
app_commands.Choice(name="5%", value=5),
app_commands.Choice(name="10%", value=10),
app_commands.Choice(name="20%", value=20),
]
@app_commands.command(name="ouvrir_compte_entreprise", description="Ouvre un compte bancaire pour une entreprise")
@app_commands.choices(banque=banque_choix, tva=tva_choix)
async def ouvrir_compte_entreprise(self, interaction: discord.Interaction, entreprise: str, banque: app_commands.Choice[str], tva: app_commands.Choice[int]):
# ✅ Vérifie que l'utilisateur est banquier
if not any(role.id == BANQUIER_ROLE_ID for role in interaction.user.roles):
await interaction.response.send_message("❌ Vous n'avez pas la permission d'exécuter cette commande.", ephemeral=True)
return
banque_name = banque.value
entreprise = entreprise.strip()
# ✅ Chargement sécurisé du fichier
if os.path.exists(COMPTES_ENTREPRISES_PATH):
try:
with open(COMPTES_ENTREPRISES_PATH, "r", encoding="utf-8") as f:
comptes = json.load(f)
except json.JSONDecodeError:
comptes = {}
else:
comptes = {}
if banque_name not in comptes:
comptes[banque_name] = {}
if entreprise in comptes[banque_name]:
await interaction.response.send_message(f"❌ Un compte existe déjà pour l'entreprise **{entreprise}** dans la banque **{banque_name}**.", ephemeral=True)
return
rib = generer_rib()
mdp = generer_mdp()
comptes[banque_name][entreprise] = {
"rib": rib,
"solde": 0,
"mdp": mdp,
"tva": tva.value,
"transactions": []
}
with open(COMPTES_ENTREPRISES_PATH, "w", encoding="utf-8") as f:
json.dump(comptes, f, ensure_ascii=False, indent=4)
# ✅ MP à l'utilisateur avec le RIB et le mot de passe
try:
await interaction.user.send(
f"🏦 Compte de l'entreprise **{entreprise}** ouvert avec succès dans la banque **{banque_name}** !\n"
f"💳 RIB : `{rib}`\n"
f"🔐 Mot de passe : `{mdp}`\n"
f"💼 Taux de TVA : `{tva.value}%`\n\n"
f"Conservez bien ces informations."
)
except discord.Forbidden:
await interaction.response.send_message("⚠️ Impossible de vous envoyer un message privé avec les informations du compte.", ephemeral=True)
return
await interaction.response.send_message(f"✅ Le compte de l'entreprise **{entreprise}** a été créé avec succès.", ephemeral=True)
async def setup(bot):
await bot.add_cog(OuvrirCompteEntreprise(bot))

32
commandes/permis.py Normal file
View file

@ -0,0 +1,32 @@
import discord
from discord.ext import commands
from discord import app_commands
# IDs des rôles
PERMIS_ROLE_ID = 1357027980056199258
AUTO_ECOLE_ROLE_ID = 1338844772118298634
class PermisCommand(commands.Cog):
def __init__(self, bot):
self.bot = bot
@app_commands.command(name="ajouter_permis", description="Ajoute le rôle 'permis' à un membre.")
async def ajouter_permis(self, interaction: discord.Interaction, membre: discord.Member):
"""Ajoute le rôle 'permis' à un membre sélectionné."""
# Vérifier si l'utilisateur a le rôle "auto-école"
if AUTO_ECOLE_ROLE_ID not in [role.id for role in interaction.user.roles]:
await interaction.response.send_message("❌ Erreur : Vous n'avez pas la permission d'utiliser cette commande.")
return
# Vérifier si le rôle "permis" existe
permis_role = interaction.guild.get_role(PERMIS_ROLE_ID)
if not permis_role:
await interaction.response.send_message("❌ Erreur : Le rôle 'permis' n'existe pas.")
return
# Ajouter le rôle "permis" au membre
await membre.add_roles(permis_role)
await interaction.response.send_message(f"✅ Le rôle 'permis' a été ajouté à {membre.mention}.")
async def setup(bot):
await bot.add_cog(PermisCommand(bot))

View file

@ -0,0 +1,38 @@
import discord
from discord import app_commands
from discord.ext import commands
# Import ou partage de presence_data selon l'architecture
from commandes.annonces_sessions import presence_data # adapte le chemin si besoin
class ListePresence(commands.Cog):
def __init__(self, bot):
self.bot = bot
@app_commands.command(
name="liste_presence",
description="Liste les présences pour une session RP."
)
@app_commands.describe(
message_id="ID du message d'annonce"
)
async def liste_presence(self, interaction: discord.Interaction, message_id: str):
try:
mid = int(message_id)
except ValueError:
await interaction.response.send_message("ID de message invalide.", ephemeral=True)
return
if mid not in presence_data:
await interaction.response.send_message("Aucune donnée de présence pour ce message.", ephemeral=True)
return
data = presence_data[mid]
def mentions(ids):
return ', '.join(f"<@{uid}>" for uid in ids) if ids else "Aucun"
await interaction.response.send_message(
f"**Présents ({len(data['present'])})** : {mentions(data['present'])}\n"
f"**Absents ({len(data['absent'])})** : {mentions(data['absent'])}\n"
f"**En retard ({len(data['retard'])})** : {mentions(data['retard'])}",
ephemeral=True
)
async def setup(bot):
await bot.add_cog(ListePresence(bot))

75
commandes/pret.py Normal file
View file

@ -0,0 +1,75 @@
import discord
from discord import app_commands
from discord.ext import commands
from utils.banque_manager import BanqueManager
BANQUIER_ROLE_ID = 1360314740567576616 # ID du rôle des banquiers # ID du rôle banquier
banque_manager = BanqueManager()
class Pret(commands.Cog):
def __init__(self, bot):
self.bot = bot
@app_commands.command(name="calculer_pret", description="Accorde un prêt bancaire à un utilisateur (Réservé aux banquiers).")
async def calculer_pret(
self,
interaction: discord.Interaction,
utilisateur: discord.Member,
banque: str,
montant: float,
duree_annees: int
):
"""Les banquiers peuvent accorder un prêt à un utilisateur si son solde le permet."""
# Vérification de si le joueur executant la commande est un banquier
if not any(role.id == BANQUIER_ROLE_ID for role in interaction.user.roles):
await interaction.response.send_message(
"❌ Vous n'avez pas la permission d'accorder un prêt.",
ephemeral=True
)
return
user_id = str(utilisateur.id)
success, result = banque_manager.ajouter_pret(user_id, banque, montant, duree_annees)
if not success:
await interaction.response.send_message(result, ephemeral=True)
return
total_a_rembourser = result
taux_interet = banque_manager.comptes[user_id]['prets'][-1]['taux_interet'] * 100
# Vérifier si l'utilisateur a assez d'argent pour rembourser
solde_utilisateur = banque_manager.comptes[user_id].get("solde", 0)
if solde_utilisateur < total_a_rembourser:
await interaction.response.send_message(
f"❌ {utilisateur.mention} n'a pas assez d'argent pour rembourser ce prêt.\n"
f"💰 **Solde actuel** : {solde_utilisateur}€\n"
f"💳 **Montant à rembourser** : {total_a_rembourser}€",
ephemeral=True
)
return
# Débiter le solde de l'utilisateur
banque_manager.comptes[user_id]["solde"] -= total_a_rembourser
banque_manager.save_data()
# Création de l'embed
embed = discord.Embed(
title="🏦 Prêt bancaire accordé",
description=f"Le prêt a été accordé par {interaction.user.mention} pour {utilisateur.mention}.",
color=discord.Color.green()
)
embed.set_thumbnail(url="https://cdn-icons-png.flaticon.com/512/2910/2910827.png") # Icône banque
embed.add_field(name="💰 Montant emprunté", value=f"{montant}€", inline=True)
embed.add_field(name="📅 Durée", value=f"{duree_annees} ans", inline=True)
embed.add_field(name="📈 Taux d'intérêt", value=f"{taux_interet}%", inline=True)
embed.add_field(name="💳 Montant total à rembourser", value=f"{total_a_rembourser}€", inline=False)
embed.add_field(name="💰 Solde après prêt", value=f"{banque_manager.comptes[user_id]['solde']}€", inline=True)
embed.set_footer(text="Prêt enregistré dans le compte bancaire ✅")
await interaction.response.send_message(embed=embed)
async def setup(bot):
await bot.add_cog(Pret(bot))

View file

@ -0,0 +1,24 @@
import discord
from discord.ext import commands
from utils.logger import log_info, log_error
class RenameMember(commands.Cog):
def __init__(self, bot):
self.bot = bot
@commands.Cog.listener()
async def on_member_join(self, member):
try:
# Vérifie si le bot a la permission de changer les pseudos
if member.guild.me.guild_permissions.manage_nicknames:
# Renommer le membre avec le format "Prénom Nom | Epic"
await member.edit(nick="Prénom Nom | Epic")
log_info(f"[✅] Pseudo modifié pour {member.display_name}")
else:
log_error(f"[❌] Pas la permission de modifier {member.display_name}")
except Exception as e:
log_error(f"[⚠️] Erreur pour {member.display_name} : {e}")
# Fonction pour ajouter le cog à ton bot
async def setup(bot):
await bot.add_cog(RenameMember(bot))

View file

@ -0,0 +1,49 @@
import discord
from discord.ext import commands
from discord import app_commands
class RestranscriptionStaff(commands.Cog):
def __init__(self, bot):
self.bot = bot
@commands.Cog.listener()
async def on_message(self, message):
# Ignorer les messages du bot lui-même
if message.author == self.bot.user:
return
# Ignorer les messages d'autres bots
if message.author.bot:
return
# Réceptionner les MPs
if isinstance(message.channel, discord.DMChannel):
channel_reception = self.bot.get_channel(1277951933105049601) # Remplacez par votre ID de salon
embed = discord.Embed(description=message.content,
color=discord.Color.blue())
embed.set_author(name=message.author.name,
icon_url=message.author.avatar.url
if message.author.avatar else None)
await channel_reception.send(embed=embed)
# Traiter les autres commandes si nécessaire
await self.bot.process_commands(message)
# Exemple de commande slash avec gestion des erreurs
@app_commands.command()
async def message_tous(self, interaction: discord.Interaction):
await interaction.response.send_message("Commande test!")
@message_tous.error
async def message_tous_error(self, interaction: discord.Interaction,
error: app_commands.AppCommandError):
if isinstance(error, app_commands.MissingPermissions):
await interaction.response.send_message(
"Vous n'avez pas la permission d'utiliser cette commande.",
ephemeral=True)
# Fonction de configuration pour ajouter le cog au bot
async def setup(bot):
await bot.add_cog(RestranscriptionStaff(bot))

147
commandes/retirer_argent.py Normal file
View file

@ -0,0 +1,147 @@
import discord
from discord import app_commands
from discord.ext import commands
import json
from datetime import datetime
BANQUIER_ROLE_ID = 1360314740567576616 # ID du rôle des banquiers # ID du rôle banquier # ID du rôle banquier
class RetirerArgent(commands.Cog):
def __init__(self, bot):
self.bot = bot
banque_choices = [
app_commands.Choice(name="Victoria's Credit Bank", value="Victoria's Credit Bank"),
app_commands.Choice(name="NGC Bank", value="NGC Bank"),
]
@app_commands.command(
name="retirer_argent",
description="Retire de l'argent d'un compte via son RIB (utilisateur ou entreprise)."
)
@app_commands.choices(banque=banque_choices)
@app_commands.describe(
rib="Le RIB du compte",
montant="Montant à retirer",
banque="Banque cible (facultatif)"
)
async def retirer_argent(
self,
interaction: discord.Interaction,
rib: str,
montant: float,
banque: app_commands.Choice[str] = None
):
if not any(role.id == BANQUIER_ROLE_ID for role in interaction.user.roles):
await interaction.response.send_message("❌ Vous n'avez pas la permission de faire cela.", ephemeral=True)
return
if montant <= 0:
await interaction.response.send_message("❌ Le montant doit être supérieur à zéro.", ephemeral=True)
return
banque_nom = banque.value if banque else None
# Charger les fichiers
try:
with open("comptes_bancaires.json", "r", encoding="utf-8") as f:
comptes_bancaires = json.load(f)
with open("comptes_entreprises.json", "r", encoding="utf-8") as f:
comptes_entreprises = json.load(f)
except Exception:
await interaction.response.send_message("❌ Erreur lors de la lecture des fichiers JSON.", ephemeral=True)
return
compte = None
source = None # "utilisateur" ou "entreprise"
user_id = None
# Recherche dans la banque spécifiée (si précisée)
if banque_nom and banque_nom in comptes_bancaires:
for uid, data in comptes_bancaires[banque_nom].items():
if data.get("rib") == rib:
compte = data
source = "utilisateur"
user_id = uid
break
if not compte and banque_nom and banque_nom in comptes_entreprises:
for entreprise, data in comptes_entreprises[banque_nom].items():
if data.get("rib") == rib:
compte = data
source = "entreprise"
user_id = entreprise
break
# Si banque non précisée ou compte non trouvé, recherche dans toutes les banques
if not compte and not banque_nom:
# Recherche dans toutes les banques utilisateurs
for banque_courante, comptes_banque in comptes_bancaires.items():
for uid, data in comptes_banque.items():
if data.get("rib") == rib:
compte = data
source = "utilisateur"
user_id = uid
banque_nom = banque_courante
break
if compte:
break
if not compte:
# Recherche dans toutes les banques entreprises
for banque_courante, entreprises_banque in comptes_entreprises.items():
for entreprise, data in entreprises_banque.items():
if data.get("rib") == rib:
compte = data
source = "entreprise"
user_id = entreprise
banque_nom = banque_courante
break
if compte:
break
if not compte:
await interaction.response.send_message("❌ Aucun compte trouvé avec ce RIB.", ephemeral=True)
return
if compte["solde"] < montant:
await interaction.response.send_message("❌ Solde insuffisant pour retirer cette somme.", ephemeral=True)
return
# Appliquer le retrait
compte["solde"] -= montant
transaction = {
"date": datetime.now().strftime("%d/%m/%Y %H:%M:%S"),
"type": "Retrait",
"source": "Banque",
"montant": -montant,
"emetteur": interaction.user.id,
"destinataire": user_id
}
compte.setdefault("transactions", []).append(transaction)
# Sauvegarde dans le bon fichier
if source == "utilisateur":
comptes_bancaires[banque_nom][user_id] = compte
with open("comptes_bancaires.json", "w", encoding="utf-8") as f:
json.dump(comptes_bancaires, f, indent=4, ensure_ascii=False)
else:
comptes_entreprises[banque_nom][user_id] = compte
with open("comptes_entreprises.json", "w", encoding="utf-8") as f:
json.dump(comptes_entreprises, f, indent=4, ensure_ascii=False)
# Message de confirmation
embed = discord.Embed(
title="💸 Retrait effectué",
description=f"💼 Banque : **{banque_nom}**\n📌 Type : **{source.capitalize()}**",
color=discord.Color.red()
)
embed.add_field(name="RIB", value=rib, inline=False)
embed.add_field(name="Montant retiré", value=f"{montant:.2f}€", inline=True)
embed.add_field(name="Effectué par", value=interaction.user.mention, inline=True)
embed.set_footer(text="Transaction enregistrée ✅")
await interaction.response.send_message(embed=embed, ephemeral=False)
async def setup(bot):
await bot.add_cog(RetirerArgent(bot))

184
commandes/retrait_gouv.py Normal file
View file

@ -0,0 +1,184 @@
import discord
from discord import app_commands
from discord.ext import commands
import json
from datetime import datetime
GOUVERNEMENT_ROLE_ID = 1278305482926194701 # Remplace avec l’ID de ton rôle Gouvernement # Remplace par l'ID du rôle Gouvernement
GOUV_BANQUE = "Gouvernement"
GOUV_COMPTE = "GOUV"
GOUV_RIB = "00000 00000 00000000000 00"
class GouvVirement(commands.Cog):
def __init__(self, bot):
self.bot = bot
# Liste des banques à jour (ajoute ici toutes les banques possibles de ton système)
banque_choices = [
app_commands.Choice(name="Victoria's Credit Bank", value="Victoria's Credit Bank"),
app_commands.Choice(name="NGC Bank", value="NGC Bank"),
]
@app_commands.command(
name="gouv_virer",
description="Le Gouvernement vire de l'argent de son compte vers un autre RIB (entreprise ou particulier)."
)
@app_commands.choices(banque=banque_choices)
@app_commands.describe(
rib="Le RIB destinataire",
montant="Montant à virer",
banque="Banque cible (facultatif)"
)
async def gouv_virer(
self,
interaction: discord.Interaction,
rib: str,
montant: float,
banque: app_commands.Choice[str] = None
):
# Vérification du rôle Gouvernement
if not any(role.id == GOUVERNEMENT_ROLE_ID for role in interaction.user.roles):
await interaction.response.send_message("❌ Seul le Gouvernement peut utiliser cette commande.", ephemeral=True)
return
if montant <= 0:
await interaction.response.send_message("❌ Le montant doit être supérieur à zéro.", ephemeral=True)
return
banque_nom = banque.value if banque else None
# Charger les fichiers
try:
with open("comptes_bancaires.json", "r", encoding="utf-8") as f:
comptes_bancaires = json.load(f)
except Exception:
comptes_bancaires = {}
try:
with open("comptes_entreprises.json", "r", encoding="utf-8") as f:
comptes_entreprises = json.load(f)
except Exception:
await interaction.response.send_message("❌ Erreur lors de la lecture des fichiers JSON.", ephemeral=True)
return
# Récupérer le compte du Gouvernement
try:
gouv_compte = comptes_entreprises[GOUV_BANQUE][GOUV_COMPTE]
except KeyError:
await interaction.response.send_message("❌ Compte Gouvernement introuvable.", ephemeral=True)
return
if gouv_compte["solde"] < montant:
await interaction.response.send_message("❌ Le Gouvernement n'a pas assez de fonds.", ephemeral=True)
return
# Chercher le destinataire
destinataire_compte = None
destinataire_banque = None
destinataire_nom = None
destinataire_type = None
# Si banque précisée, chercher d'abord dans cette banque (entreprises puis particuliers)
if banque_nom:
# Entreprises
if banque_nom in comptes_entreprises:
for nom, data in comptes_entreprises[banque_nom].items():
if data.get("rib") == rib:
destinataire_compte = data
destinataire_banque = banque_nom
destinataire_nom = nom
destinataire_type = "entreprise"
break
# Particuliers
if not destinataire_compte and banque_nom in comptes_bancaires:
for uid, data in comptes_bancaires[banque_nom].items():
if data.get("rib") == rib:
destinataire_compte = data
destinataire_banque = banque_nom
destinataire_nom = uid
destinataire_type = "particulier"
break
# Si banque non précisée, chercher dans toutes les banques
else:
# Entreprises
for banque_courante, entreprises in comptes_entreprises.items():
for nom, data in entreprises.items():
if data.get("rib") == rib:
destinataire_compte = data
destinataire_banque = banque_courante
destinataire_nom = nom
destinataire_type = "entreprise"
break
if destinataire_compte:
break
# Particuliers
if not destinataire_compte:
for banque_courante, comptes in comptes_bancaires.items():
for uid, data in comptes.items():
if data.get("rib") == rib:
destinataire_compte = data
destinataire_banque = banque_courante
destinataire_nom = uid
destinataire_type = "particulier"
break
if destinataire_compte:
break
if not destinataire_compte:
await interaction.response.send_message("❌ Aucun compte trouvé avec ce RIB dans la banque spécifiée." if banque_nom else "❌ Aucun compte trouvé avec ce RIB.", ephemeral=True)
return
# Effectuer le virement
gouv_compte["solde"] -= montant
destinataire_compte["solde"] += montant
now = datetime.now().strftime("%d/%m/%Y %H:%M:%S")
# Transaction côté Gouvernement (sortant)
gouv_compte.setdefault("transactions", []).append({
"date": now,
"type": "Virement",
"source": "Gouvernement",
"montant": -montant,
"emetteur": "Gouvernement",
"destinataire": destinataire_nom
})
# Transaction côté destinataire (entrant)
destinataire_compte.setdefault("transactions", []).append({
"date": now,
"type": "Virement",
"source": "Gouvernement",
"montant": montant,
"emetteur": "Gouvernement",
"destinataire": destinataire_nom
})
# Sauvegarde
comptes_entreprises[GOUV_BANQUE][GOUV_COMPTE] = gouv_compte
if destinataire_type == "entreprise":
comptes_entreprises[destinataire_banque][destinataire_nom] = destinataire_compte
with open("comptes_entreprises.json", "w", encoding="utf-8") as f:
json.dump(comptes_entreprises, f, indent=4, ensure_ascii=False)
else:
comptes_bancaires[destinataire_banque][destinataire_nom] = destinataire_compte
with open("comptes_entreprises.json", "w", encoding="utf-8") as f:
json.dump(comptes_entreprises, f, indent=4, ensure_ascii=False)
with open("comptes_bancaires.json", "w", encoding="utf-8") as f:
json.dump(comptes_bancaires, f, indent=4, ensure_ascii=False)
# Message de confirmation
embed = discord.Embed(
title="🏛️ Virement Gouvernemental effectué",
description=f"💼 Banque destinataire : **{destinataire_banque}**\n📌 Type : **{destinataire_type.capitalize()}**",
color=discord.Color.gold()
)
embed.add_field(name="RIB destinataire", value=rib, inline=False)
embed.add_field(name="Montant viré", value=f"{montant:.2f}€", inline=True)
embed.add_field(name="Effectué par", value=interaction.user.mention, inline=True)
embed.set_footer(text="Transaction enregistrée ✅")
await interaction.response.send_message(embed=embed, ephemeral=False)
async def setup(bot):
await bot.add_cog(GouvVirement(bot))

30
commandes/roll.py Normal file
View file

@ -0,0 +1,30 @@
import random
from discord import app_commands
# Supposons que tu as accès à l'objet bot ou client principal
# Utilise bot.tree ou client.tree directement
@app_commands.command(
name="roll",
description="Lancer un dé entre deux valeurs avec une raison."
)
@app_commands.describe(
minimum="Valeur minimale",
maximum="Valeur maximale",
action="Raison du lancer de dé"
)
async def roll(interaction, minimum: int, maximum: int, action: str):
if minimum > maximum:
await interaction.response.send_message(
"❌ La valeur minimale doit être inférieure ou égale à la valeur maximale.", ephemeral=True
)
return
result = random.randint(minimum, maximum)
await interaction.response.send_message(
f"🎲 **{interaction.user.display_name}** a lancé un dé pour l'action *{action}*\n"
f"Résultat : **{result}** (entre {minimum} et {maximum})"
)
# Pour enregistrer la commande dans le bot :
async def setup(bot):
bot.tree.add_command(roll)

View file

@ -0,0 +1,144 @@
import discord
from discord import app_commands
from discord.ext import commands
import matplotlib.pyplot as plt
import io
import datetime
from utils.stats_manager import get_stats_detailed # fonction fictive améliorée
class StatsCommandes(commands.Cog):
def __init__(self, bot):
self.bot = bot
def plot_barh(self, labels, values, title, xlabel, color='cornflowerblue'):
plt.figure(figsize=(8,4))
plt.barh(labels[::-1], values[::-1], color=color)
plt.title(title)
plt.xlabel(xlabel)
plt.tight_layout()
buf = io.BytesIO()
plt.savefig(buf, format='png')
plt.close()
buf.seek(0)
return buf
def plot_line(self, dates, values, title, xlabel, ylabel, color='tab:blue'):
plt.figure(figsize=(10,5))
plt.plot(dates, values, marker='o', color=color)
plt.title(title)
plt.xlabel(xlabel)
plt.ylabel(ylabel)
plt.grid(True, linestyle='--', alpha=0.7)
plt.tight_layout()
buf = io.BytesIO()
plt.savefig(buf, format='png')
plt.close()
buf.seek(0)
return buf
def plot_pie(self, labels, values, title):
plt.figure(figsize=(6,6))
plt.pie(values, labels=labels, autopct='%1.1f%%', startangle=140, colors=plt.cm.Paired.colors)
plt.title(title)
plt.tight_layout()
buf = io.BytesIO()
plt.savefig(buf, format='png')
plt.close()
buf.seek(0)
return buf
@app_commands.command(name="stats_bot", description="Affiche toutes les statistiques d'utilisation du bot.")
async def stats_bot(self, interaction: discord.Interaction):
# get_stats_detailed() doit retourner un dict avec les clés suivantes (exemple) :
# {
# "total_commandes": int,
# "top_commandes": [(nom, nb), ...],
# "top_utilisateurs": [(user_id, nb), ...],
# "evolution_commandes": [(date, nb), ...], # list de tuples date (datetime), nb commandes
# "repartition_statuts": {statut: nb, ...},
# "commandes_par_jour": {jour_semaine: nb, ...} # ex: "Lundi": 123
# }
stats = get_stats_detailed()
embed = discord.Embed(
title="📊 Statistiques Complètes du Bot",
description=f"Total commandes utilisées : **{stats['total_commandes']}**",
color=discord.Color.blue()
)
# Top commandes en texte
if stats["top_commandes"]:
top_cmds_text = "\n".join([f"• `{nom}` : **{nb}** fois" for nom, nb in stats["top_commandes"][:5]])
else:
top_cmds_text = "Aucune commande utilisée."
embed.add_field(name="🏆 Top 5 des commandes", value=top_cmds_text, inline=False)
# Top utilisateurs en texte
if stats["top_utilisateurs"]:
top_users_text = "\n".join(
[f"• <@{uid}> : **{nb}** commandes" for uid, nb in stats["top_utilisateurs"][:5]]
)
else:
top_users_text = "Aucun utilisateur enregistré."
embed.add_field(name="👤 Top 5 utilisateurs", value=top_users_text, inline=False)
# Génération des graphiques
files = []
# Graphique 1 : Top commandes (barh)
if stats["top_commandes"]:
noms_cmds = [x[0] for x in stats["top_commandes"][:5]]
vals_cmds = [x[1] for x in stats["top_commandes"][:5]]
buf1 = self.plot_barh(noms_cmds, vals_cmds, "Top 5 Commandes les plus utilisées", "Nombre d'utilisations")
files.append(discord.File(fp=buf1, filename="top_commandes.png"))
embed.set_image(url="attachment://top_commandes.png")
# Graphique 2 : Top utilisateurs (barh)
if stats["top_utilisateurs"]:
noms_users = [f"User {uid}" for uid, _ in stats["top_utilisateurs"][:5]] # On peut remplacer par mention si on fetch
vals_users = [nb for _, nb in stats["top_utilisateurs"][:5]]
buf2 = self.plot_barh(noms_users, vals_users, "Top 5 Utilisateurs actifs", "Nombre de commandes", color='mediumseagreen')
files.append(discord.File(fp=buf2, filename="top_utilisateurs.png"))
# Graphique 3 : Évolution commandes dans le temps (line)
if stats.get("evolution_commandes"):
dates = [d for d, _ in stats["evolution_commandes"]]
vals = [v for _, v in stats["evolution_commandes"]]
# Convertir dates datetime en string format court
dates_str = [d.strftime("%d/%m") for d in dates]
buf3 = self.plot_line(dates_str, vals, "Évolution des commandes dans le temps", "Date", "Nombre de commandes")
files.append(discord.File(fp=buf3, filename="evolution_commandes.png"))
# Graphique 4 : Répartition statuts (pie)
if stats.get("repartition_statuts"):
labels = list(stats["repartition_statuts"].keys())
values = list(stats["repartition_statuts"].values())
buf4 = self.plot_pie(labels, values, "Répartition des statuts des tâches")
files.append(discord.File(fp=buf4, filename="repartition_statuts.png"))
# Graphique 5 : Commandes par jour de la semaine (bar)
if stats.get("commandes_par_jour"):
jours = list(stats["commandes_par_jour"].keys())
valeurs = list(stats["commandes_par_jour"].values())
plt.figure(figsize=(8,4))
plt.bar(jours, valeurs, color='salmon')
plt.title("Commandes par jour de la semaine")
plt.xlabel("Jour")
plt.ylabel("Nombre de commandes")
plt.tight_layout()
buf5 = io.BytesIO()
plt.savefig(buf5, format='png')
plt.close()
buf5.seek(0)
files.append(discord.File(fp=buf5, filename="commandes_par_jour.png"))
# Envoi embed + fichiers graphiques
if files:
await interaction.response.send_message(embed=embed, files=files)
else:
await interaction.response.send_message(embed=embed)
async def setup(bot: commands.Bot):
await bot.add_cog(StatsCommandes(bot))

259
commandes/taches.py Normal file
View file

@ -0,0 +1,259 @@
import discord
from discord import app_commands
from discord.ext import commands, tasks
from datetime import datetime
from zoneinfo import ZoneInfo
from utils.gestion_taches import (
ajouter_tache, lister_taches_utilisateur, taches_proches,
update_tache_status, charger_taches, supprimer_tache, sauvegarder_taches
)
PRIORITES = ["Haute", "Moyenne", "Basse", "Critique", "Faible"]
class RappelTacheView(discord.ui.View):
def __init__(self, tid: str):
super().__init__(timeout=None)
self.tid = tid
@discord.ui.button(label="✅ Marquer comme faite", style=discord.ButtonStyle.success, custom_id="btn_mark_done")
async def mark_done(self, interaction: discord.Interaction, button: discord.ui.Button):
success = await update_tache_status(self.tid, "Terminée")
if success:
await interaction.response.send_message(
f"✅ La tâche #{self.tid} a été marquée comme terminée !", ephemeral=True)
else:
await interaction.response.send_message(
"❌ Impossible de mettre à jour la tâche. Peut-être déjà terminée ?", ephemeral=True)
self.disable_all_items()
await interaction.message.edit(view=self)
class MesTachesPaginator(discord.ui.View):
def __init__(self, interaction: discord.Interaction, taches_list: list[tuple], per_page=5):
super().__init__(timeout=120)
self.interaction = interaction
self.taches = taches_list
self.per_page = per_page
self.page = 0
self.max_page = (len(taches_list) - 1) // per_page
def get_embed(self):
embed = discord.Embed(title="📝 Vos Tâches Assignées", color=discord.Color.green())
start = self.page * self.per_page
end = start + self.per_page
for tid, t in self.taches[start:end]:
embed.add_field(
name=f"#{tid} - {t['titre']}",
value=f"📅 Échéance : {t['échéance']}\n⚠️ Priorité : {t['priorité']}\n📌 Statut : {t['statut']}",
inline=False
)
embed.set_footer(text=f"Page {self.page + 1} / {self.max_page + 1}")
return embed
@discord.ui.button(label="⬅️", style=discord.ButtonStyle.secondary)
async def previous_page(self, interaction: discord.Interaction, button: discord.ui.Button):
if interaction.user.id != self.interaction.user.id:
await interaction.response.send_message("❌ Ce n'est pas votre pagination.", ephemeral=True)
return
if self.page > 0:
self.page -= 1
await interaction.response.edit_message(embed=self.get_embed(), view=self)
else:
await interaction.response.defer()
@discord.ui.button(label="➡️", style=discord.ButtonStyle.secondary)
async def next_page(self, interaction: discord.Interaction, button: discord.ui.Button):
if interaction.user.id != self.interaction.user.id:
await interaction.response.send_message("❌ Ce n'est pas votre pagination.", ephemeral=True)
return
if self.page < self.max_page:
self.page += 1
await interaction.response.edit_message(embed=self.get_embed(), view=self)
else:
await interaction.response.defer()
class JiraDiscord(commands.Cog):
def __init__(self, bot):
self.bot = bot
self.rappels.start()
def cog_unload(self):
self.rappels.cancel()
async def autocomplete_priorite(self, interaction: discord.Interaction, current: str):
return [
app_commands.Choice(name=prio, value=prio)
for prio in PRIORITES if current.lower() in prio.lower()
][:5]
@app_commands.command(name="nouvelle_tache", description="Créer une nouvelle tâche")
@app_commands.describe(
titre="Titre de la tâche",
description="Description de la tâche",
assigne="Utilisateur assigné",
priorite="Niveau de priorité (ex: Haute, Moyenne, Basse)",
echeance="Date d'échéance au format JJ/MM/AAAA"
)
async def nouvelle_tache(
self, interaction: discord.Interaction, titre: str, description: str,
assigne: discord.User, priorite: str, echeance: str
):
data = await charger_taches()
for tid, tache in data.get("taches", {}).items():
if tache["assigné"] == assigne.id and tache["titre"].lower() == titre.lower():
return await interaction.response.send_message(
f"❌ {assigne.mention} a déjà une tâche avec ce titre.", ephemeral=True)
try:
date_obj = datetime.strptime(echeance, "%d/%m/%Y")
date_str = date_obj.strftime("%Y-%m-%d")
except ValueError:
return await interaction.response.send_message(
"❌ Format de date invalide (JJ/MM/AAAA).", ephemeral=True)
id_tache = await ajouter_tache(titre, description, assigne.id, priorite, date_str, interaction.user.id)
embed = discord.Embed(
title=f"📌 Nouvelle Tâche #{id_tache}",
description=description,
color=discord.Color.blurple()
)
embed.set_author(name=f"Assignée à {assigne.display_name}", icon_url=assigne.display_avatar.url)
embed.add_field(name="🗂 Titre", value=titre, inline=False)
embed.add_field(name="⚠️ Priorité", value=priorite, inline=True)
embed.add_field(name="📅 Échéance", value=echeance, inline=True)
embed.set_footer(text=f"Créée par {interaction.user.display_name}", icon_url=interaction.user.display_avatar.url)
await interaction.response.send_message(embed=embed)
try:
await assigne.send(
f"👋 Bonjour {assigne.display_name},\n\nUne nouvelle tâche t'a été assignée par {interaction.user.mention} :",
embed=embed
)
except Exception:
pass
@nouvelle_tache.autocomplete("priorite")
async def autocomplete_priorite_handler(self, interaction: discord.Interaction, current: str):
return await self.autocomplete_priorite(interaction, current)
@app_commands.command(name="mes_taches", description="Lister mes tâches assignées")
async def mes_taches(self, interaction: discord.Interaction):
taches = await lister_taches_utilisateur(interaction.user.id)
if not taches:
await interaction.response.send_message("🚫 Aucune tâche ne vous est assignée.", ephemeral=True)
return
taches_list = list(taches.items())
if len(taches_list) <= 5:
embed = discord.Embed(title="📝 Vos Tâches Assignées", color=discord.Color.green())
for tid, t in taches_list:
embed.add_field(
name=f"#{tid} - {t['titre']}",
value=f"📅 Échéance : {t['échéance']}\n⚠️ Priorité : {t['priorité']}\n📌 Statut : {t['statut']}",
inline=False
)
await interaction.response.send_message(embed=embed, ephemeral=True)
else:
view = MesTachesPaginator(interaction, taches_list)
await interaction.response.send_message(embed=view.get_embed(), view=view, ephemeral=True)
@app_commands.command(name="update_tache", description="Mettre à jour le statut et la priorité d'une tâche")
@app_commands.describe(
id_tache="ID de la tâche à mettre à jour",
statut="Nouveau statut de la tâche",
priorite="Nouvelle priorité (optionnel)"
)
async def update_tache(
self, interaction: discord.Interaction, id_tache: str, statut: str, priorite: str = None
):
data = await charger_taches()
if id_tache not in data.get("taches", {}):
return await interaction.response.send_message("❌ Tâche non trouvée.", ephemeral=True)
tache = data["taches"][id_tache]
if tache["assigné"] != interaction.user.id:
return await interaction.response.send_message("❌ Vous ne pouvez pas mettre à jour le statut de cette tâche.", ephemeral=True)
statut_update = await update_tache_status(id_tache, statut)
priorite_update = True
if priorite:
tache["priorité"] = priorite
await sauvegarder_taches(data)
if statut_update and priorite_update:
await interaction.response.send_message(
f"✅ Le statut de la tâche #{id_tache} a été mis à jour à **{statut}**." +
(f"\nLa priorité est maintenant **{priorite}**." if priorite else ""),
ephemeral=True
)
if tache["créé_par"] != interaction.user.id:
try:
creator = await self.bot.fetch_user(tache["créé_par"])
await creator.send(
f"🔔 La tâche #{id_tache} que vous avez créée a été mise à jour par {interaction.user.mention} :\n"
f"**Nouveau statut** : {statut}" +
(f"\n**Nouvelle priorité** : {priorite}" if priorite else "")
)
except Exception:
pass
else:
await interaction.response.send_message("❌ Erreur lors de la mise à jour de la tâche.", ephemeral=True)
@update_tache.autocomplete("priorite")
async def autocomplete_priorite_update(self, interaction: discord.Interaction, current: str):
return await self.autocomplete_priorite(interaction, current)
@app_commands.command(name="supprimer_tache", description="Supprimer une tâche")
@app_commands.describe(
id_tache="ID de la tâche à supprimer"
)
async def supprimer_tache_cmd(self, interaction: discord.Interaction, id_tache: str):
data = await charger_taches()
if id_tache not in data.get("taches", {}):
return await interaction.response.send_message("❌ Tâche non trouvée.", ephemeral=True)
tache = data["taches"][id_tache]
perms = interaction.user.guild_permissions
can_delete = (
tache["créé_par"] == interaction.user.id or
tache["assigné"] == interaction.user.id or
perms.administrator
)
if not can_delete:
return await interaction.response.send_message("❌ Vous ne pouvez pas supprimer cette tâche.", ephemeral=True)
success = await supprimer_tache(id_tache)
if success:
await interaction.response.send_message(f"🗑️ La tâche #{id_tache} a été supprimée.", ephemeral=True)
else:
await interaction.response.send_message("❌ Erreur lors de la suppression de la tâche.", ephemeral=True)
@tasks.loop(seconds=30)
async def rappels(self):
now = datetime.now(ZoneInfo("Europe/Paris"))
if now.hour == 9 and now.minute == 0:
taches = await taches_proches()
for tid, t in taches.items():
try:
user = await self.bot.fetch_user(t["assigné"])
embed = discord.Embed(
title=f"🔔 Rappel Tâche #{tid}",
description=t["description"],
color=discord.Color.orange()
)
embed.add_field(name="🗂 Titre", value=t["titre"], inline=False)
embed.add_field(name="⚠️ Priorité", value=t["priorité"], inline=True)
embed.add_field(name="📅 Échéance", value=t["échéance"], inline=True)
embed.set_footer(text="Clique sur le bouton si tu as fini !")
view = RappelTacheView(tid)
await user.send(embed=embed, view=view)
except Exception:
continue
@rappels.before_loop
async def avant_rappel(self):
await self.bot.wait_until_ready()
async def setup(bot):
await bot.add_cog(JiraDiscord(bot))

329
commandes/ticket.py Normal file
View file

@ -0,0 +1,329 @@
import discord
from discord.ext import commands
from discord import app_commands
TICKET_CATEGORY_ID = 1301142883876737085 # ID de la catégorie des tickets # ID de la catégorie des tickets
STAFF_ROLE_ID = 1277638784095092816 # ID du rôle staff # ID du rôle staff
TEAM_STAFF_ROLE_ID = 1277638784095092816 # ID du rôle équipe staff # ID du rôle équipe staff
ATTENTE_ORAL_ROLE_ID = 1278343007996870699 # ID du rôle "attente oral" # ID du rôle "attente oral"
CANDIDATURE_A_FAIRE_ROLE_ID = 1278343541936099412 # ID du rôle "candidature à faire" # ID du rôle "candidature à faire"
CITOYEN_ROLE_ID = 1278319901651632138 # ID du rôle "citoyen" # ID du rôle "citoyen"
class ContinueFormView(discord.ui.View):
"""Vue avec un bouton pour continuer à l'étape suivante"""
def __init__(self, current_step, step1_data=None, step2_part1_data=None, step2_part2_data=None):
super().__init__(timeout=180.0) # Définir un timeout pour la vue
self.current_step = current_step
self.step1_data = step1_data
self.step2_part1_data = step2_part1_data
self.step2_part2_data = step2_part2_data
@discord.ui.button(label="📋 Continuer le formulaire", style=discord.ButtonStyle.green, custom_id="continue_form")
async def continue_form(self, interaction: discord.Interaction, button: discord.ui.Button):
"""Passe à l'étape suivante du formulaire"""
if self.current_step == 1:
await interaction.response.send_modal(TicketStep2Part1(self.step1_data))
elif self.current_step == 2:
await interaction.response.send_modal(TicketStep2Part2(self.step1_data, self.step2_part1_data))
elif self.current_step == 3:
await interaction.response.send_modal(TicketStep3(self.step1_data, self.step2_part1_data, self.step2_part2_data))
# Vérifier si le message existe toujours avant de le modifier
try:
await interaction.message.edit(view=self)
except discord.errors.NotFound:
# Si le message n'existe plus, nous ne faisons rien
pass
class TicketStep1(discord.ui.Modal, title="📋 Informations Personnelles"):
"""Étape 1 du formulaire - Infos personnelles"""
pseudo = discord.ui.TextInput(label="Pseudo Epic (Fortnite)", required=True, max_length=16)
age = discord.ui.TextInput(label="Âge", required=True, max_length=2)
experience = discord.ui.TextInput(label="Expérience RP ?", style=discord.TextStyle.paragraph, required=True, max_length=50)
decouverte = discord.ui.TextInput(label="Comment as-tu découvert le serveur ?", required=True, max_length=20)
micro = discord.ui.TextInput(label="As-tu un micro de qualité ?", required=True, max_length=3)
async def on_submit(self, interaction: discord.Interaction):
"""Envoie un bouton pour passer à l'étape 2"""
view = ContinueFormView(current_step=1, step1_data=self)
await interaction.response.send_message("✅ Première étape terminée ! Cliquez ci-dessous pour continuer.", view=view, ephemeral=True)
class TicketStep2Part1(discord.ui.Modal, title="👤 Infos sur ton personnage (Partie 1)"):
"""Étape 2 du formulaire - Infos RP (Partie 1)"""
def __init__(self, step1_data):
super().__init__()
self.step1_data = step1_data
prenom = discord.ui.TextInput(label="Prénom", required=True)
nom = discord.ui.TextInput(label="Nom", required=True)
age_perso = discord.ui.TextInput(label="Âge du personnage", required=True)
genre = discord.ui.TextInput(label="Genre", required=True, max_length=5)
naissance = discord.ui.TextInput(label="Date et lieu de naissance", required=True)
async def on_submit(self, interaction: discord.Interaction):
"""Envoie un bouton pour passer à l'étape suivante"""
view = ContinueFormView(current_step=2, step1_data=self.step1_data, step2_part1_data=self)
await interaction.response.send_message("✅ Deuxième partie terminée ! Cliquez ci-dessous pour continuer.", view=view, ephemeral=True)
class TicketStep2Part2(discord.ui.Modal, title="👤 Infos sur ton personnage (Partie 2)"):
"""Étape 2 du formulaire - Infos RP (Partie 2)"""
def __init__(self, step1_data, step2_part1_data):
super().__init__()
self.step1_data = step1_data
self.step2_part1_data = step2_part1_data
traits = discord.ui.TextInput(label="Traits de caractère", required=True)
nationalite = discord.ui.TextInput(label="Nationalité", required=True)
skin_rp = discord.ui.TextInput(label="Skin RP", required=True)
metier_souhaite = discord.ui.TextInput(label="Métier souhaité", required=True)
async def on_submit(self, interaction: discord.Interaction):
"""Envoie un bouton pour passer à l'étape 3"""
view = ContinueFormView(current_step=3, step1_data=self.step1_data, step2_part1_data=self.step2_part1_data, step2_part2_data=self)
await interaction.response.send_message("✅ Deuxième étape terminée ! Cliquez ci-dessous pour continuer.", view=view, ephemeral=True)
class TicketStep3(discord.ui.Modal, title="📋 Développement du Personnage"):
"""Étape 3 du formulaire - Histoire du personnage"""
def __init__(self, step1_data, step2_part1_data, step2_part2_data):
super().__init__()
self.step1_data = step1_data
self.step2_part1_data = step2_part1_data
self.step2_part2_data = step2_part2_data
histoire = discord.ui.TextInput(
label="Histoire du personnage",
style=discord.TextStyle.paragraph,
required=True,
min_length=425,
max_length=500 # Limite de caractères maximum
)
objectifs = discord.ui.TextInput(label="Objectifs RP (court et long terme)", style=discord.TextStyle.paragraph, required=True, max_length=100)
async def on_submit(self, interaction: discord.Interaction):
"""Création du ticket après validation"""
guild = interaction.guild
user = interaction.user
category = discord.utils.get(guild.categories, id=TICKET_CATEGORY_ID)
# Vérifier si la catégorie existe
if category is None:
await interaction.response.send_message("❌ Erreur : La catégorie des tickets n'existe pas !", ephemeral=True)
return
# Vérifier si le rôle staff existe
staff_role = discord.utils.get(guild.roles, id=STAFF_ROLE_ID)
if staff_role is None:
await interaction.response.send_message("❌ Erreur : Le rôle staff n'existe pas !", ephemeral=True)
return
# Vérifier si l'utilisateur a déjà un ticket ouvert
for channel in category.text_channels:
if channel.topic == f"Ticket de {user.id}":
await interaction.response.send_message(f"❌ Vous avez déjà un ticket ouvert : {channel.mention}", ephemeral=True)
return
# Définir les permissions du salon du ticket
overwrites = {
guild.default_role: discord.PermissionOverwrite(read_messages=False),
user: discord.PermissionOverwrite(read_messages=True, send_messages=True),
staff_role: discord.PermissionOverwrite(read_messages=True, send_messages=True),
discord.utils.get(guild.roles, id=TEAM_STAFF_ROLE_ID): discord.PermissionOverwrite(read_messages=True, send_messages=False)
}
# Créer un salon de ticket avec le nom [pseudo_du_membre]-écrit
ticket_channel = await category.create_text_channel(
name=f"{user.name}-écrit",
topic=f"Ticket de {user.id}",
overwrites=overwrites
)
# Récupérer le rôle "équipe staff"
team_staff_role = discord.utils.get(guild.roles, id=TEAM_STAFF_ROLE_ID)
# Création de l'embed avec les infos du formulaire
embed = discord.Embed(title="📩 Ticket Whitelist", color=0x00ff00)
embed.add_field(name="📋 - Informations Personnelles", value=(
f"> - Pseudo Epic : {self.step1_data.pseudo.value}\n"
f"> - Âge : {self.step1_data.age.value}\n"
f"> - Expérience RP : {self.step1_data.experience.value}\n"
f"> - Découverte du serveur : {self.step1_data.decouverte.value}\n"
f"> - Micro de qualité : {self.step1_data.micro.value}"
), inline=False)
embed.add_field(name="👤 - Informations RP", value=(
f"> - Prénom : {self.step2_part1_data.prenom.value}\n"
f"> - Nom : {self.step2_part1_data.nom.value}\n"
f"> - Âge du personnage : {self.step2_part1_data.age_perso.value}\n"
f"> - Genre : {self.step2_part1_data.genre.value}\n"
f"> - Naissance : {self.step2_part1_data.naissance.value}\n"
f"> - Traits de caractère : {self.step2_part2_data.traits.value}\n"
f"> - Nationalité : {self.step2_part2_data.nationalite.value}\n"
f"> - Skin RP : {self.step2_part2_data.skin_rp.value}\n"
f"> - Métier souhaité : {self.step2_part2_data.metier_souhaite.value}"
), inline=False)
embed.add_field(name="📋 - Développement du Personnage", value=(
f"> - Histoire : {self.histoire.value}\n"
f"> - Objectifs : {self.objectifs.value}"
), inline=False)
# Envoyer l'embed avec les boutons d'actions pour les staff
await ticket_channel.send(embed=embed, view=TicketManagementView(user))
# Envoyer un message pour mentionner l'équipe staff
await ticket_channel.send(f"{team_staff_role.mention} - Nouveau ticket créé !")
# Mettre à jour le pseudo du joueur si le bot a les permissions nécessaires
if guild.me.guild_permissions.manage_nicknames:
new_nickname = f"{self.step2_part1_data.prenom.value} {self.step2_part1_data.nom.value} | {self.step1_data.pseudo.value}"
if len(new_nickname) > 32:
new_nickname = new_nickname[:32]
await user.edit(nick=new_nickname)
else:
await interaction.followup.send("❌ Le bot n'a pas les permissions nécessaires pour modifier le pseudo.", ephemeral=True)
# Envoyer un message de confirmation
try:
await interaction.response.send_message(f"✅ Ticket ouvert : {ticket_channel.mention}", ephemeral=True)
except discord.errors.NotFound:
await interaction.followup.send(f"✅ Ticket ouvert : {ticket_channel.mention}", ephemeral=True)
class TicketManagementView(discord.ui.View):
"""Vue pour gérer le ticket avec les boutons Fermer, Réouvrir, Claim et Supprimer"""
def __init__(self, ticket_owner):
super().__init__(timeout=None)
self.ticket_owner = ticket_owner
self.claimed_by = None # Stocke le staff qui claim le ticket
@discord.ui.button(label="🔒 Fermer le Ticket", style=discord.ButtonStyle.red, custom_id="close_ticket")
async def close_ticket(self, interaction: discord.Interaction, button: discord.ui.Button):
"""Ferme le ticket en désactivant l'envoi de messages pour le membre"""
if not discord.utils.get(interaction.user.roles, id=STAFF_ROLE_ID):
await interaction.response.send_message("❌ Seuls les staffs peuvent fermer un ticket.", ephemeral=True)
return
await interaction.channel.set_permissions(self.ticket_owner, read_messages=False, send_messages=False)
await interaction.channel.edit(name="ticket-fermé")
await interaction.response.send_message("🔒 Le ticket a été fermé. Vous ne pouvez plus le voir.", ephemeral=True)
# Mettre à jour la vue pour afficher uniquement les boutons Réouvrir et Supprimer
new_view = ClosedTicketView(self.ticket_owner)
await interaction.channel.send("Le ticket a été fermé.", view=new_view)
@discord.ui.button(label="👤 Prendre en charge le Ticket", style=discord.ButtonStyle.blurple, custom_id="claim_ticket")
async def claim_ticket(self, interaction: discord.Interaction, button: discord.ui.Button):
"""Un staff prend en charge le ticket"""
if not discord.utils.get(interaction.user.roles, id=STAFF_ROLE_ID):
await interaction.response.send_message("❌ Seuls les staffs peuvent claim un ticket.", ephemeral=True)
return
self.claimed_by = interaction.user
await interaction.channel.set_permissions(discord.utils.get(interaction.guild.roles, id=STAFF_ROLE_ID), send_messages=False, read_messages=True)
await interaction.channel.set_permissions(self.claimed_by, read_messages=True, send_messages=True)
await interaction.response.send_message(f"👤 {self.claimed_by.mention} a claim ce ticket.", ephemeral=False)
class ClosedTicketView(discord.ui.View):
"""Vue pour les tickets fermés avec les boutons Réouvrir et Supprimer"""
def __init__(self, ticket_owner):
super().__init__(timeout=None)
self.ticket_owner = ticket_owner
@discord.ui.button(label="♻ Réouvrir le Ticket", style=discord.ButtonStyle.green, custom_id="reopen_ticket")
async def reopen_ticket(self, interaction: discord.Interaction, button: discord.ui.Button):
"""Réouvre le ticket pour le membre"""
if not discord.utils.get(interaction.user.roles, id=STAFF_ROLE_ID):
await interaction.response.send_message("❌ Seuls les staffs peuvent réouvrir un ticket.", ephemeral=True)
return
await interaction.channel.set_permissions(self.ticket_owner, read_messages=True, send_messages=True)
await interaction.response.send_message("✅ Le ticket a été réouvert.", ephemeral=True)
# Mettre à jour la vue pour afficher tous les boutons
new_view = TicketManagementView(self.ticket_owner)
await interaction.channel.send("Le ticket a été réouvert.", view=new_view)
@discord.ui.button(label="❌ Supprimer le Ticket", style=discord.ButtonStyle.danger, custom_id="delete_ticket")
async def delete_ticket(self, interaction: discord.Interaction, button: discord.ui.Button):
"""Supprime le salon du ticket"""
if not discord.utils.get(interaction.user.roles, id=STAFF_ROLE_ID):
await interaction.response.send_message("❌ Seuls les staffs peuvent supprimer un ticket.", ephemeral=True)
return
await interaction.response.send_message("❌ Suppression du ticket...", ephemeral=True)
await interaction.channel.delete()
class TicketView(discord.ui.View):
"""Vue avec le bouton pour ouvrir le ticket"""
def __init__(self):
super().__init__(timeout=None)
@discord.ui.button(label="📑 Ouvrir un Ticket Whitelist", style=discord.ButtonStyle.red, custom_id="open_ticket")
async def open_ticket(self, interaction: discord.Interaction, button: discord.ui.Button):
"""Affiche le premier formulaire"""
await interaction.response.send_modal(TicketStep1())
class Ticket(commands.Cog):
"""Commande pour envoyer l'embed de ticket"""
def __init__(self, bot):
self.bot = bot
@app_commands.command(name="ticket", description="Envoie l'embed pour ouvrir un ticket.")
async def ticket(self, interaction: discord.Interaction):
"""Envoie l'embed avec le bouton d'ouverture de ticket"""
embed = discord.Embed(
title="📩 Ticket Whitelist",
description="Pour réaliser votre whitelist, ouvrez un ticket et remplissez le formulaire.",
color=0x00ff00
)
view = TicketView()
await interaction.channel.send(embed=embed, view=view)
await interaction.response.send_message("✅ Embed des tickets envoyé.", ephemeral=True)
@commands.Cog.listener()
async def on_message(self, message):
"""Surveille les messages des staffs dans les tickets"""
if message.guild is None or message.author.bot:
return
if not message.channel.topic or not message.channel.topic.startswith("Ticket de"):
return
staff_role = discord.utils.get(message.guild.roles, id=STAFF_ROLE_ID)
if staff_role in message.author.roles and "validé" in message.content.lower():
try:
user_id = message.channel.topic.split("Ticket de ")[1]
user = await message.guild.fetch_member(user_id)
# Vérifier si le rôle "attente oral" est déjà attribué
attente_oral_role = discord.utils.get(message.guild.roles, id=ATTENTE_ORAL_ROLE_ID)
candidature_a_faire_role = discord.utils.get(message.guild.roles, id=CANDIDATURE_A_FAIRE_ROLE_ID)
citoyen_role = discord.utils.get(message.guild.roles, id=CITOYEN_ROLE_ID)
if attente_oral_role in user.roles:
# Deuxième "validé" détecté
await user.remove_roles(attente_oral_role)
await user.add_roles(citoyen_role)
await message.channel.edit(name=f"{user.name}-citoyen")
else:
# Premier "validé" détecté
await user.remove_roles(candidature_a_faire_role)
await user.add_roles(attente_oral_role)
await message.channel.edit(name=f"{user.name}-oral")
# Mettre à jour le pseudo du joueur si le bot a les permissions nécessaires
if message.guild.me.guild_permissions.manage_nicknames:
new_nickname = f"{self.step2_part1_data.prenom.value} {self.step2_part1_data.nom.value} | {self.step1_data.pseudo.value}"
if len(new_nickname) > 32:
new_nickname = new_nickname[:32]
await user.edit(nick=new_nickname)
else:
await message.channel.send("❌ Le bot n'a pas les permissions nécessaires pour modifier le pseudo.", delete_after=10)
except Exception as e:
print(f"Erreur lors du renommage du ticket: {e}")
async def setup(bot):
await bot.add_cog(Ticket(bot))

67
commandes/transactions.py Normal file
View file

@ -0,0 +1,67 @@
import discord
from discord import app_commands
from discord.ext import commands
from utils.banque_manager import BanqueManager
BANQUIER_ROLE_ID = 1360314740567576616 # ID du rôle des banquiers # ID du rôle banquier # ID du rôle des banquiers
banque_manager = BanqueManager()
class Transactions(commands.Cog):
def __init__(self, bot):
self.bot = bot
@app_commands.command(name="transactions", description="Affiche les transactions d'un utilisateur (réservé aux banquiers).")
async def transactions(self, interaction: discord.Interaction, utilisateur: discord.Member):
"""Affiche l'historique des transactions d'un utilisateur (banquiers uniquement)."""
# ✅ Récupération du vrai membre pour avoir les rôles
member = interaction.guild.get_member(interaction.user.id)
if not member:
await interaction.response.send_message("❌ Impossible de vérifier vos permissions.", ephemeral=True)
return
# ✅ Vérification du rôle banquier
if not any(role.id == BANQUIER_ROLE_ID for role in member.roles):
await interaction.response.send_message("❌ Vous n'avez pas la permission de voir les transactions.", ephemeral=True)
return
banque_manager.load_data()
user_id = str(utilisateur.id)
# ✅ Vérification si l'utilisateur a des transactions
if user_id not in banque_manager.comptes or "transactions" not in banque_manager.comptes[user_id]:
await interaction.response.send_message(f"❌ {utilisateur.mention} n'a pas d'historique de transactions.", ephemeral=True)
return
transactions = banque_manager.comptes[user_id].get("transactions", [])
if not transactions:
await interaction.response.send_message(f"📜 {utilisateur.mention} n'a encore effectué aucune transaction.", ephemeral=True)
return
# ✅ Création de l'embed
embed = discord.Embed(
title=f"📜 Historique des transactions de {utilisateur.name}",
color=discord.Color.gold()
)
for t in transactions[-10:]:
embed.add_field(
name=f"📅 {t['date']}",
value=(
f"**Type**: {t['type']}\n"
f"💰 **Montant**: {t['montant']}€\n"
f"📤 **Émetteur**: <@{t['emetteur']}>\n"
f"📥 **Destinataire**: <@{t['destinataire']}>"
),
inline=False
)
embed.set_footer(text="Seules les 10 dernières transactions sont affichées.")
await interaction.response.send_message(embed=embed)
async def setup(bot):
await bot.add_cog(Transactions(bot))

View file

@ -0,0 +1,77 @@
import discord
from discord import app_commands
from discord.ext import commands, tasks
import json
from datetime import datetime, timedelta
import os
EMS_ROLE_ID = 1278317212675084328 # Remplace par l’ID du rôle EMS # Remplace par l’ID du rôle EMS
ROLE_A_ATTRIBUER_ID = 1367198876548993045 # Remplace par l’ID du rôle visite médicale # Remplace par l’ID du rôle à attribuer
FICHIER_ROLES = "visite_médicale.json"
class AttribuerRole(commands.Cog):
def __init__(self, bot):
self.bot = bot
self.roles_temporaire = self.charger_roles()
self.verification_roles.start()
def charger_roles(self):
if os.path.exists(FICHIER_ROLES):
with open(FICHIER_ROLES, "r", encoding="utf-8") as f:
return json.load(f)
return {}
def sauvegarder_roles(self):
with open(FICHIER_ROLES, "w", encoding="utf-8") as f:
json.dump(self.roles_temporaire, f, indent=4, ensure_ascii=False)
@app_commands.command(name="valider_visite_médicale", description="Attribue un rôle temporaire suite à une visite médicale (EMS uniquement).")
async def valider_visite(self, interaction: discord.Interaction, membre: discord.Member):
if not any(role.id == EMS_ROLE_ID for role in interaction.user.roles):
await interaction.response.send_message("❌ Seuls les EMS peuvent utiliser cette commande.", ephemeral=True)
return
role = interaction.guild.get_role(ROLE_A_ATTRIBUER_ID)
if not role:
await interaction.response.send_message("❌ Rôle à attribuer introuvable.", ephemeral=True)
return
await membre.add_roles(role)
expiration = (datetime.now() + timedelta(days=30)).strftime("%Y-%m-%d %H:%M:%S")
self.roles_temporaire[str(membre.id)] = {
"role_id": role.id,
"expiration": expiration
}
self.sauvegarder_roles()
await interaction.response.send_message(f"✅ Rôle temporaire attribué à {membre.mention} jusqu'au {expiration}.", ephemeral=True)
@tasks.loop(hours=1)
async def verification_roles(self):
now = datetime.now()
to_remove = []
for user_id, data in self.roles_temporaire.items():
expiration = datetime.strptime(data["expiration"], "%Y-%m-%d %H:%M:%S")
if now >= expiration:
to_remove.append(user_id)
for user_id in to_remove:
member = self.bot.get_user(int(user_id)) or await self.bot.fetch_user(int(user_id))
guild = discord.utils.get(self.bot.guilds)
member = guild.get_member(int(user_id))
role = guild.get_role(ROLE_A_ATTRIBUER_ID)
if member and role:
await member.remove_roles(role)
del self.roles_temporaire[user_id]
if to_remove:
self.sauvegarder_roles()
@verification_roles.before_loop
async def before_verification(self):
await self.bot.wait_until_ready()
async def setup(bot):
await bot.add_cog(AttribuerRole(bot))

138
commandes/virement.py Normal file
View file

@ -0,0 +1,138 @@
import json
import discord
from discord import app_commands
from discord.ext import commands
from datetime import datetime
class Virement(commands.Cog):
def __init__(self, bot):
self.bot = bot
banque_choices = [
app_commands.Choice(name="Victoria's Credit Bank", value="Victoria's Credit Bank"),
app_commands.Choice(name="NGC Bank", value="NGC Bank"),
]
@app_commands.command(name="virement", description="Fait un virement bancaire à un autre membre via son RIB")
@app_commands.choices(banque=banque_choices)
@app_commands.describe(rib="Le RIB du destinataire", montant="Le montant à envoyer", banque="Banque d'où retirer l'argent")
async def virement(self, interaction: discord.Interaction, rib: str, montant: float, banque: app_commands.Choice[str]):
auteur_id = str(interaction.user.id)
banque_source = banque.value
# Charger les comptes utilisateurs
with open('comptes_bancaires.json', 'r', encoding='utf-8') as f:
comptes = json.load(f)
# Charger les comptes entreprises
with open('comptes_entreprises.json', 'r', encoding='utf-8') as f:
entreprises = json.load(f)
# Vérifier que l'utilisateur a un compte dans cette banque
if banque_source not in comptes or auteur_id not in comptes[banque_source]:
await interaction.response.send_message(f"❌ Tu n'as pas de compte dans la banque **{banque_source}**.", ephemeral=True)
return
compte_emetteur = comptes[banque_source][auteur_id]
if compte_emetteur["solde"] < montant:
await interaction.response.send_message("❌ Ton solde est insuffisant pour effectuer ce virement.", ephemeral=True)
return
# Rechercher le destinataire (utilisateur ou entreprise)
destinataire_id = None
banque_destinataire = None
is_entreprise = False
# Recherche dans comptes utilisateurs
for bq_name, bq_data in comptes.items():
if isinstance(bq_data, dict):
for user_id, info in bq_data.items():
if isinstance(info, dict) and info.get("rib") == rib:
destinataire_id = user_id
banque_destinataire = bq_name
break
if destinataire_id:
break
# Sinon, recherche dans les entreprises
if not destinataire_id:
for bq_name, bq_data in entreprises.items():
if isinstance(bq_data, dict):
for ent_id, info in bq_data.items():
if isinstance(info, dict) and info.get("rib") == rib:
destinataire_id = ent_id
banque_destinataire = bq_name
is_entreprise = True
break
if destinataire_id:
break
if not destinataire_id:
await interaction.response.send_message("❌ Aucun compte trouvé avec ce RIB.", ephemeral=True)
return
# Appliquer la TVA si entreprise
montant_net = montant
tva_envoyee = 0
if is_entreprise:
taux = entreprises[banque_destinataire][destinataire_id].get("tva", 0)
tva_envoyee = round(montant * taux / 100, 2)
montant_net = montant - tva_envoyee
# Ajouter la TVA au Gouvernement
if "Gouvernement" not in entreprises:
entreprises["Gouvernement"] = {}
if "GOUV" not in entreprises["Gouvernement"]:
entreprises["Gouvernement"]["GOUV"] = {
"rib": "00000 00000 00000000000 00",
"solde": 0.0,
"transactions": []
}
entreprises["Gouvernement"]["GOUV"]["solde"] += tva_envoyee
entreprises["Gouvernement"]["GOUV"]["transactions"].append({
"date": datetime.now().strftime("%d/%m/%Y %H:%M:%S"),
"type": "TVA reçue",
"source": auteur_id,
"montant": tva_envoyee
})
# Déduire du compte émetteur
comptes[banque_source][auteur_id]["solde"] -= montant
comptes[banque_source][auteur_id]["transactions"].append({
"date": datetime.now().strftime("%d/%m/%Y %H:%M:%S"),
"type": "Virement",
"source": f"Vers RIB {rib}",
"montant": -montant
})
# Ajouter au compte destinataire
cible_data = entreprises if is_entreprise else comptes
cible_data[banque_destinataire][destinataire_id]["solde"] += montant_net
cible_data[banque_destinataire][destinataire_id]["transactions"].append({
"date": datetime.now().strftime("%d/%m/%Y %H:%M:%S"),
"type": "Virement reçu",
"source": interaction.user.name,
"montant": montant_net
})
# Sauvegarde
with open('comptes_bancaires.json', 'w', encoding='utf-8') as f:
json.dump(comptes, f, ensure_ascii=False, indent=4)
with open('comptes_entreprises.json', 'w', encoding='utf-8') as f:
json.dump(entreprises, f, ensure_ascii=False, indent=4)
# Envoi MP au destinataire si c'est un utilisateur
if not is_entreprise:
try:
user_dest = await self.bot.fetch_user(int(destinataire_id))
await user_dest.send(f"📥 Vous avez reçu un virement de **{montant:.2f}€** de la part de **{interaction.user.display_name}**.")
except:
pass # Si l'utilisateur a les MP désactivés
await interaction.response.send_message(
f"✅ Virement de **{montant:.2f}€** effectué.{' TVA de ' + str(tva_envoyee) + '€ envoyée au Gouvernement.' if is_entreprise else ''}",
ephemeral=True
)
async def setup(bot):
await bot.add_cog(Virement(bot))

View file

@ -0,0 +1,86 @@
import json
import discord
from discord import app_commands
from discord.ext import commands
from datetime import datetime
BANQUIER_ROLE_ID = 1360314740567576616 # ID du rôle des banquiers # ID du rôle banquier # À remplacer par l’ID réel du rôle banquier
class VirementBanquier(commands.Cog):
def __init__(self, bot):
self.bot = bot
banque_choices = [
app_commands.Choice(name="Victoria's Credit Bank", value="Victoria's Credit Bank"),
app_commands.Choice(name="NGC Bank", value="NGC Bank"),
]
@app_commands.command(name="virement_banquier", description="Effectue un virement au nom d'un client (réservé aux banquiers).")
@app_commands.choices(banque=banque_choices)
async def virement_banquier(
self,
interaction: discord.Interaction,
rib_emetteur: str,
montant: float,
rib_destinataire: str,
banque: app_commands.Choice[str]
):
if not any(role.id == BANQUIER_ROLE_ID for role in interaction.user.roles):
await interaction.response.send_message("❌ Vous n'avez pas la permission d'utiliser cette commande.", ephemeral=True)
return
banque_nom = banque.value
try:
with open("comptes_bancaires.json", "r", encoding="utf-8") as f:
comptes = json.load(f)
except:
await interaction.response.send_message("❌ Erreur de lecture des comptes bancaires.", ephemeral=True)
return
# Trouver les ID à partir des RIB
emetteur_id = None
destinataire_id = None
for user_id, data in comptes.get(banque_nom, {}).items():
if data.get("rib") == rib_emetteur:
emetteur_id = user_id
if data.get("rib") == rib_destinataire:
destinataire_id = user_id
if not emetteur_id:
await interaction.response.send_message("❌ Le RIB émetteur est introuvable dans cette banque.", ephemeral=True)
return
if not destinataire_id:
await interaction.response.send_message("❌ Le RIB destinataire est introuvable dans cette banque.", ephemeral=True)
return
# Vérifier le solde
if comptes[banque_nom][emetteur_id]["solde"] < montant:
await interaction.response.send_message("❌ Le solde du compte émetteur est insuffisant.", ephemeral=True)
return
# Exécuter le virement
comptes[banque_nom][emetteur_id]["solde"] -= montant
comptes[banque_nom][emetteur_id]["transactions"].append({
"date": datetime.now().strftime("%d/%m/%Y %H:%M:%S"),
"type": "Virement manuel (par banquier)",
"source": f"Vers RIB {rib_destinataire}",
"montant": -montant
})
comptes[banque_nom][destinataire_id]["solde"] += montant
comptes[banque_nom][destinataire_id]["transactions"].append({
"date": datetime.now().strftime("%d/%m/%Y %H:%M:%S"),
"type": "Virement reçu (par banquier)",
"source": f"RIB {rib_emetteur}",
"montant": montant
})
with open("comptes_bancaires.json", "w", encoding="utf-8") as f:
json.dump(comptes, f, ensure_ascii=False, indent=4)
await interaction.response.send_message(f"✅ Virement de **{montant:.2f}€** effectué de **{rib_emetteur}** vers **{rib_destinataire}** dans **{banque_nom}**.", ephemeral=True)
async def setup(bot):
await bot.add_cog(VirementBanquier(bot))

View file

@ -0,0 +1,122 @@
import discord
from discord import app_commands
from discord.ext import commands
import json
from datetime import datetime
BANQUIER_ROLE_ID = 1360314740567576616 # ID du rôle des banquiers # ID du rôle banquier # ID du rôle des banquiers
class VirementBanquierEntreprise(commands.Cog):
def __init__(self, bot):
self.bot = bot
banques = [
app_commands.Choice(name="Victoria's Credit Bank", value="Victoria's Credit Bank"),
app_commands.Choice(name="NGC Bank", value="NGC Bank")
]
@app_commands.command(name="virement_banquier_entreprise", description="Fait un virement depuis une entreprise avec mot de passe (réservé aux banquiers).")
@app_commands.choices(banque=banques)
async def virement_banquier_entreprise(
self,
interaction: discord.Interaction,
banque: app_commands.Choice[str],
nom_entreprise: str,
mot_de_passe: str,
rib_destinataire: str,
montant: float
):
if not any(role.id == BANQUIER_ROLE_ID for role in interaction.user.roles):
await interaction.response.send_message("❌ Vous n'avez pas la permission de faire cette commande.", ephemeral=True)
return
banque_nom = banque.value
# Chargement des fichiers
try:
with open("comptes_entreprises.json", "r", encoding="utf-8") as f:
entreprises = json.load(f)
with open("comptes_bancaires.json", "r", encoding="utf-8") as f:
particuliers = json.load(f)
except Exception:
await interaction.response.send_message("❌ Erreur de lecture des fichiers JSON.", ephemeral=True)
return
# Vérification de l'entreprise
if banque_nom not in entreprises or nom_entreprise not in entreprises[banque_nom]:
await interaction.response.send_message("❌ Compte entreprise introuvable.", ephemeral=True)
return
compte_entreprise = entreprises[banque_nom][nom_entreprise]
if compte_entreprise.get("mdp") != mot_de_passe:
await interaction.response.send_message("❌ Mot de passe incorrect.", ephemeral=True)
return
if compte_entreprise["solde"] < montant:
await interaction.response.send_message("❌ Solde insuffisant pour ce virement.", ephemeral=True)
return
# Recherche du compte destinataire (entreprise ou particulier)
destinataire_id = None
banque_destinataire = None
is_entreprise = False
# Recherche dans entreprises
for bq, entreprises_data in entreprises.items():
for nom, infos in entreprises_data.items():
if infos.get("rib") == rib_destinataire:
destinataire_id = nom
banque_destinataire = bq
is_entreprise = True
break
if destinataire_id:
break
# Sinon, recherche dans particuliers
if not destinataire_id:
for bq, clients in particuliers.items():
for user_id, infos in clients.items():
if infos.get("rib") == rib_destinataire:
destinataire_id = user_id
banque_destinataire = bq
is_entreprise = False
break
if destinataire_id:
break
if not destinataire_id:
await interaction.response.send_message("❌ RIB destinataire introuvable.", ephemeral=True)
return
# Retrait du montant
compte_entreprise["solde"] -= montant
compte_entreprise["transactions"].append({
"date": datetime.now().strftime("%d/%m/%Y %H:%M:%S"),
"type": "Virement",
"source": f"Entreprise {nom_entreprise}",
"montant": -montant,
"destinataire": rib_destinataire
})
# Créditer le destinataire
compte_cible = entreprises if is_entreprise else particuliers
compte_data = compte_cible[banque_destinataire][destinataire_id]
compte_data["solde"] += montant
compte_data["transactions"].append({
"date": datetime.now().strftime("%d/%m/%Y %H:%M:%S"),
"type": "Virement reçu",
"source": nom_entreprise,
"montant": montant
})
# Sauvegarde
with open("comptes_entreprises.json", "w", encoding="utf-8") as f:
json.dump(entreprises, f, ensure_ascii=False, indent=4)
with open("comptes_bancaires.json", "w", encoding="utf-8") as f:
json.dump(particuliers, f, ensure_ascii=False, indent=4)
await interaction.response.send_message(f"✅ Virement de **{montant:.2f}€** effectué depuis **{nom_entreprise}** vers **{rib_destinataire}**.")
async def setup(bot):
await bot.add_cog(VirementBanquierEntreprise(bot))

View file

@ -0,0 +1,149 @@
import discord
from discord import app_commands
from discord.ext import commands
import json
from datetime import datetime
class VirementEntreprise(commands.Cog):
def __init__(self, bot):
self.bot = bot
banque_choices = [
app_commands.Choice(name="Victoria's Credit Bank", value="Victoria's Credit Bank"),
app_commands.Choice(name="NGC Bank", value="NGC Bank"),
]
@app_commands.command(name="virement_entreprise", description="Effectuer un virement depuis une entreprise vers un RIB.")
@app_commands.choices(banque=banque_choices)
async def virement_entreprise(
self,
interaction: discord.Interaction,
rib_entreprise: str,
montant: float,
mot_de_passe: str,
rib_destinataire: str,
banque: app_commands.Choice[str]
):
banque_nom = banque.value
# Chargement des données
try:
with open("comptes_entreprises.json", "r", encoding="utf-8") as f:
comptes_entreprises = json.load(f)
with open("comptes_bancaires.json", "r", encoding="utf-8") as f:
comptes_bancaires = json.load(f)
except Exception:
await interaction.response.send_message("❌ Erreur de lecture des données bancaires.", ephemeral=True)
return
# 🔐 Vérification de l'émetteur
entreprise_id = None
for nom_entreprise, infos in comptes_entreprises.get(banque_nom, {}).items():
if infos.get("rib") == rib_entreprise:
if infos.get("mdp") != mot_de_passe:
await interaction.response.send_message("❌ Mot de passe incorrect.", ephemeral=True)
return
entreprise_id = nom_entreprise
break
if not entreprise_id:
await interaction.response.send_message("❌ Aucun compte entreprise trouvé avec ce RIB.", ephemeral=True)
return
compte_emetteur = comptes_entreprises[banque_nom][entreprise_id]
if compte_emetteur["solde"] < montant:
await interaction.response.send_message("❌ Solde insuffisant.", ephemeral=True)
return
# 🔎 Recherche du destinataire
destinataire_data = None
destinataire_type = None
destinataire_nom = None
banque_dest = None
for banque_cible, comptes in comptes_entreprises.items():
for nom, infos in comptes.items():
if infos.get("rib") == rib_destinataire:
destinataire_data = infos
destinataire_type = "entreprise"
destinataire_nom = nom
banque_dest = banque_cible
break
if destinataire_data:
break
if not destinataire_data:
for banque_cible, comptes in comptes_bancaires.items():
for uid, infos in comptes.items():
if infos.get("rib") == rib_destinataire:
destinataire_data = infos
destinataire_type = "particulier"
destinataire_nom = uid
banque_dest = banque_cible
break
if destinataire_data:
break
if not destinataire_data:
await interaction.response.send_message("❌ RIB destinataire introuvable.", ephemeral=True)
return
# 📉 Débit du compte entreprise
compte_emetteur["solde"] -= montant
now = datetime.now().strftime("%d/%m/%Y %H:%M:%S")
compte_emetteur.setdefault("transactions", []).append({
"date": now,
"type": "Virement",
"source": f"Vers RIB {rib_destinataire}",
"montant": -montant
})
# 📈 Crédit du destinataire (avec TVA si entreprise)
montant_net = montant
tva_envoyee = 0
if destinataire_type == "entreprise":
tva = destinataire_data.get("tva", 0)
tva_envoyee = round(montant * tva / 100, 2)
montant_net = montant - tva_envoyee
# Crédit TVA à l'État
if "Gouvernement" not in comptes_entreprises:
comptes_entreprises["Gouvernement"] = {}
if "GOUV" not in comptes_entreprises["Gouvernement"]:
comptes_entreprises["Gouvernement"]["GOUV"] = {
"rib": "00000 00000 00000000000 00",
"solde": 0.0,
"transactions": []
}
comptes_entreprises["Gouvernement"]["GOUV"]["solde"] += tva_envoyee
comptes_entreprises["Gouvernement"]["GOUV"]["transactions"].append({
"date": now,
"type": "TVA reçue",
"source": entreprise_id,
"montant": tva_envoyee
})
destinataire_data["solde"] += montant_net
destinataire_data.setdefault("transactions", []).append({
"date": now,
"type": "Virement reçu",
"source": entreprise_id,
"montant": montant_net
})
# Sauvegardes
with open("comptes_entreprises.json", "w", encoding="utf-8") as f:
json.dump(comptes_entreprises, f, ensure_ascii=False, indent=4)
with open("comptes_bancaires.json", "w", encoding="utf-8") as f:
json.dump(comptes_bancaires, f, ensure_ascii=False, indent=4)
await interaction.response.send_message(
f"✅ Virement effectué avec succès.{' TVA de ' + str(tva_envoyee) + '€ envoyée au Gouvernement.' if tva_envoyee else ''}",
ephemeral=True
)
async def setup(bot):
await bot.add_cog(VirementEntreprise(bot))

97
commandes/virement_fbm.py Normal file
View file

@ -0,0 +1,97 @@
import discord
from discord import app_commands
from discord.ext import commands
import json
from datetime import datetime
class VirementFBM(commands.Cog):
def __init__(self, bot):
self.bot = bot
banque_choices = [
app_commands.Choice(name="Victoria's Credit Bank", value="Victoria's Credit Bank"),
app_commands.Choice(name="NGC Bank", value="NGC Bank"),
]
@app_commands.command(name="virement_fbm", description="Fait un virement au compte indépendant FBM.")
@app_commands.choices(banque=banque_choices)
@app_commands.describe(montant="Montant à transférer", banque="Banque à utiliser")
async def virement_fbm(self, interaction: discord.Interaction, montant: float, banque: app_commands.Choice[str]):
auteur_id = str(interaction.user.id)
banque_nom = banque.value
if montant <= 0:
await interaction.response.send_message("❌ Le montant doit être supérieur à 0.", ephemeral=True)
return
# Charger les fichiers
try:
with open("comptes_bancaires.json", "r", encoding="utf-8") as f:
comptes = json.load(f)
except Exception:
await interaction.response.send_message("❌ Erreur lors de la lecture des comptes bancaires.", ephemeral=True)
return
try:
with open("comptes_entreprises.json", "r", encoding="utf-8") as f:
entreprises = json.load(f)
except:
entreprises = {}
# Vérification du compte de l'utilisateur
if banque_nom not in comptes or auteur_id not in comptes[banque_nom]:
await interaction.response.send_message("❌ Tu n'as pas de compte dans cette banque.", ephemeral=True)
return
compte = comptes[banque_nom][auteur_id]
if compte["solde"] < montant:
await interaction.response.send_message("❌ Solde insuffisant pour effectuer le virement.", ephemeral=True)
return
# Initialiser FBM si nécessaire
if "fbm" not in entreprises:
entreprises["fbm"] = {}
if "FBM" not in entreprises["fbm"]:
entreprises["fbm"]["FBM"] = {
"rib": "00000 00000 00000000000 03",
"solde": 0.0,
"transactions": []
}
now = datetime.now().strftime("%d/%m/%Y %H:%M:%S")
# Mise à jour des soldes
compte["solde"] -= montant
entreprises["fbm"]["FBM"]["solde"] += montant
# Historique utilisateur
compte["transactions"].append({
"date": now,
"type": "Virement",
"source": f"Vers FBM",
"montant": -montant
})
# Historique FBM
entreprises["fbm"]["FBM"]["transactions"].append({
"date": now,
"type": "Virement reçu",
"source": interaction.user.name,
"montant": montant
})
# Sauvegarde
with open("comptes_bancaires.json", "w", encoding="utf-8") as f:
json.dump(comptes, f, indent=4, ensure_ascii=False)
with open("comptes_entreprises.json", "w", encoding="utf-8") as f:
json.dump(entreprises, f, indent=4, ensure_ascii=False)
# Confirmation
await interaction.response.send_message(
f"✅ Virement de **{montant:.2f}€** envoyé au compte **FBM** depuis **{banque_nom}**.",
ephemeral=True
)
async def setup(bot):
await bot.add_cog(VirementFBM(bot))

View file

@ -0,0 +1,86 @@
import discord
from discord import app_commands
from discord.ext import commands
import json
from datetime import datetime
class VirementGouvernement(commands.Cog):
def __init__(self, bot):
self.bot = bot
banque_choices = [
app_commands.Choice(name="Victoria's Credit Bank", value="Victoria's Credit Bank"),
app_commands.Choice(name="NGC Bank", value="NGC Bank"),
]
@app_commands.command(name="virement_gouvernement", description="Effectue un virement au Gouvernement.")
@app_commands.choices(banque=banque_choices)
@app_commands.describe(montant="Le montant à envoyer", banque="Banque à utiliser pour le virement")
async def virement_gouvernement(self, interaction: discord.Interaction, montant: float, banque: app_commands.Choice[str]):
auteur_id = str(interaction.user.id)
banque_source = banque.value
if montant <= 0:
await interaction.response.send_message("❌ Le montant doit être supérieur à zéro.", ephemeral=True)
return
# Charger les fichiers
try:
with open('comptes_bancaires.json', 'r', encoding='utf-8') as f:
comptes = json.load(f)
with open('comptes_entreprises.json', 'r', encoding='utf-8') as f:
entreprises = json.load(f)
except Exception as e:
await interaction.response.send_message(f"❌ Erreur de lecture des fichiers : {e}", ephemeral=True)
return
if banque_source not in comptes or auteur_id not in comptes[banque_source]:
await interaction.response.send_message(f"❌ Tu n'as pas de compte dans la banque **{banque_source}**.", ephemeral=True)
return
compte_user = comptes[banque_source][auteur_id]
if compte_user["solde"] < montant:
await interaction.response.send_message("❌ Solde insuffisant pour effectuer ce virement.", ephemeral=True)
return
# Vérifier ou créer le compte Gouvernement
if "Gouvernement" not in entreprises:
entreprises["Gouvernement"] = {}
if "GOUV" not in entreprises["Gouvernement"]:
entreprises["Gouvernement"]["GOUV"] = {
"rib": "00000 00000 00000000000 00",
"solde": 0.0,
"transactions": []
}
# Effectuer le virement
now = datetime.now().strftime("%d/%m/%Y %H:%M:%S")
compte_user["solde"] -= montant
entreprises["Gouvernement"]["GOUV"]["solde"] += montant
compte_user["transactions"].append({
"date": now,
"type": "Virement",
"source": f"Vers Gouvernement",
"montant": -montant
})
entreprises["Gouvernement"]["GOUV"]["transactions"].append({
"date": now,
"type": "Virement reçu",
"source": interaction.user.name,
"montant": montant
})
# Sauvegarde
with open('comptes_bancaires.json', 'w', encoding='utf-8') as f:
json.dump(comptes, f, indent=4, ensure_ascii=False)
with open('comptes_entreprises.json', 'w', encoding='utf-8') as f:
json.dump(entreprises, f, indent=4, ensure_ascii=False)
await interaction.response.send_message(
f"✅ Virement de **{montant:.2f}€** envoyé au Gouvernement avec succès.",
ephemeral=True
)
async def setup(bot):
await bot.add_cog(VirementGouvernement(bot))

View file

@ -0,0 +1,93 @@
import discord
from discord import app_commands
from discord.ext import commands
import json
from datetime import datetime
class VirementUrssaf(commands.Cog):
def __init__(self, bot):
self.bot = bot
banque_choices = [
app_commands.Choice(name="Victoria's Credit Bank", value="Victoria's Credit Bank"),
app_commands.Choice(name="NGC Bank", value="NGC Bank"),
]
@app_commands.command(name="virement_ursaff", description="Fait un virement vers le compte indépendant URSAFF.")
@app_commands.choices(banque=banque_choices)
@app_commands.describe(montant="Montant à transférer", banque="Banque à utiliser")
async def virement_ursaff(self, interaction: discord.Interaction, montant: float, banque: app_commands.Choice[str]):
auteur_id = str(interaction.user.id)
banque_nom = banque.value
if montant <= 0:
await interaction.response.send_message("❌ Le montant doit être supérieur à 0.", ephemeral=True)
return
try:
with open("comptes_bancaires.json", "r", encoding="utf-8") as f:
comptes = json.load(f)
except Exception:
await interaction.response.send_message("❌ Erreur lors de la lecture des comptes bancaires.", ephemeral=True)
return
try:
with open("comptes_entreprises.json", "r", encoding="utf-8") as f:
entreprises = json.load(f)
except:
entreprises = {}
if banque_nom not in comptes or auteur_id not in comptes[banque_nom]:
await interaction.response.send_message("❌ Tu n'as pas de compte dans cette banque.", ephemeral=True)
return
compte = comptes[banque_nom][auteur_id]
if compte["solde"] < montant:
await interaction.response.send_message("❌ Solde insuffisant pour effectuer le virement.", ephemeral=True)
return
# Initialiser URSAFF si nécessaire
if "ursaff" not in entreprises:
entreprises["ursaff"] = {}
if "URSAFF" not in entreprises["ursaff"]:
entreprises["ursaff"]["URSAFF"] = {
"rib": "00000 00000 00000000000 02",
"solde": 0.0,
"transactions": []
}
now = datetime.now().strftime("%d/%m/%Y %H:%M:%S")
# Mise à jour des soldes
compte["solde"] -= montant
entreprises["ursaff"]["URSAFF"]["solde"] += montant
# Historique utilisateur
compte["transactions"].append({
"date": now,
"type": "Virement",
"source": "Vers URSAFF",
"montant": -montant
})
# Historique URSAFF
entreprises["ursaff"]["URSAFF"]["transactions"].append({
"date": now,
"type": "Virement reçu",
"source": interaction.user.name,
"montant": montant
})
# Sauvegarde
with open("comptes_bancaires.json", "w", encoding="utf-8") as f:
json.dump(comptes, f, indent=4, ensure_ascii=False)
with open("comptes_entreprises.json", "w", encoding="utf-8") as f:
json.dump(entreprises, f, indent=4, ensure_ascii=False)
await interaction.response.send_message(
f"✅ Virement de **{montant:.2f}€** envoyé au compte **URSAFF** depuis **{banque_nom}**.",
ephemeral=True
)
async def setup(bot):
await bot.add_cog(VirementUrssaf(bot))

View file

@ -0,0 +1,65 @@
import discord
from discord import app_commands
from discord.ext import commands
from utils.data_manager import DataManager
from utils.card_generator import CardGenerator
card_generator = CardGenerator()
class VoirIdentite(commands.Cog):
def __init__(self, bot):
self.bot = bot
@app_commands.command(name="voir_carte_identite", description="Afficher votre carte d'identité ou celle d'un autre utilisateur")
async def voir_carte_identite(self, interaction: discord.Interaction, utilisateur: discord.Member = None):
"""Affiche la carte d'identité d'un utilisateur"""
# Remplacer par l'ID de ton rôle gouvernement
GOUVERNEMENT_ROLE_ID = 1360236421872484403
target_user = utilisateur or interaction.user
# 🔒 Vérification si l'utilisateur peut voir la carte d'un autre
if utilisateur and utilisateur != interaction.user:
gouvernement_role = interaction.guild.get_role(GOUVERNEMENT_ROLE_ID)
if gouvernement_role not in interaction.user.roles:
await interaction.response.send_message(
"🚫 Vous n'avez pas l'autorisation de voir la carte d'identité d'un autre utilisateur.",
ephemeral=True
)
return
user_id = str(target_user.id)
# 🧠 Recréer un DataManager à chaque commande
data_manager = DataManager()
# 🔎 Récupération des données
identity_data = data_manager.identities.get(user_id)
if identity_data is None:
await interaction.response.send_message(
"❌ Cet utilisateur ne possède pas de carte d'identité.",
ephemeral=True
)
return
# 🛡️ Vérification que l'identité est complète
if "date_expiration" not in identity_data:
identity_data["date_expiration"] = "Non spécifiée"
# 🖼️ Génération de l'image de la carte
card_buffer = await card_generator.create_identity_card(target_user, identity_data)
# 📩 Envoi du fichier + embed
file = discord.File(card_buffer, filename="carte_identite.png")
embed = discord.Embed(
title=f"🪪 Carte d'identité de {target_user.display_name}",
color=0x2ecc71
)
embed.set_image(url="attachment://carte_identite.png")
await interaction.response.send_message(file=file, embed=embed, ephemeral=False)
async def setup(bot):
await bot.add_cog(VoirIdentite(bot))

View file

@ -0,0 +1,30 @@
import discord
from discord.ext import commands
from utils.logger import log_info, log_error
class WelcomeMessage(commands.Cog):
def __init__(self, bot):
self.bot = bot
@commands.Cog.listener()
async def on_member_join(self, member):
try:
# ID du salon discussion
welcome_channel_id = 1278483527783944242
welcome_channel = member.guild.get_channel(welcome_channel_id)
if welcome_channel:
welcome_message = (
f"## BIENVENUE <:panda_welcome:1297298999849517056> !\n\n"
f"> **L'équipe staff te souhaite la bienvenue sur Victoria's Vice {member.mention} !**\n\n"
f"> Pour ta candidature <:arrow_1:1297491509201080362> <#1278477608392196176> / <#1278477152215371806> \n\n"
f"> Si tu as des questions <:arrow_1:1297491509201080362> <#1279037583820718120>\n\n"
f"*> Bien cordialement, L'équipe staff de Victoria's Vice*"
)
await welcome_channel.send(welcome_message)
except Exception as e:
print(f"Erreur lors de l'envoi du message de bienvenue: {e}")
async def setup(bot):
await bot.add_cog(WelcomeMessage(bot))

2111
comptes_bancaires.json Normal file

File diff suppressed because it is too large Load diff

492
comptes_entreprises.json Normal file
View file

@ -0,0 +1,492 @@
{
"ngc_bank": {},
"Gouvernement": {
"GOUV": {
"rib": "00000 00000 00000000000 00",
"solde": 10085849.01,
"transactions": [
{
"date": "26/04/2025 15:55:13",
"type": "TVA reçue",
"source": "534003230435442690",
"montant": 0.2
},
{
"date": "27/04/2025 14:01:32",
"type": "TVA reçue",
"source": "534003230435442690",
"montant": 0.2
},
{
"date": "28/04/2025 08:53:44",
"type": "TVA reçue",
"source": "1108087282692522105",
"montant": 8.5
},
{
"date": "28/04/2025 10:42:38",
"type": "TVA reçue",
"source": "1263573205268828231",
"montant": 7.6
},
{
"date": "28/04/2025 15:37:27",
"type": "TVA reçue",
"source": "1244182195300601920",
"montant": 8.5
},
{
"date": "01/05/2025 08:56:08",
"type": "TVA reçue",
"source": "914118793918312480",
"montant": 5.2
},
{
"date": "01/05/2025 13:08:59",
"type": "TVA reçue",
"source": "1321863072242995332",
"montant": 300.0
},
{
"date": "04/05/2025 16:51:20",
"type": "TVA reçue",
"source": "971446412690722826",
"montant": 0.01
},
{
"date": "05/05/2025 16:09:39",
"type": "TVA reçue",
"source": "353942721997832195",
"montant": 400.0
},
{
"date": "05/05/2025 16:23:40",
"type": "TVA reçue",
"source": "828239898975666216",
"montant": 10.0
},
{
"date": "05/05/2025 16:24:07",
"type": "TVA reçue",
"source": "534003230435442690",
"montant": 100.0
},
{
"date": "06/05/2025 10:49:55",
"type": "TVA reçue",
"source": "1263573205268828231",
"montant": 8.0
},
{
"date": "06/05/2025 18:19:03",
"type": "TVA reçue",
"source": "1263573205268828231",
"montant": 1.0
},
{
"date": "07/05/2025 19:45:13",
"type": "Virement reçu",
"source": "gameurpro12",
"montant": 1.0
},
{
"date": "09/05/2025 17:23:13",
"type": "TVA reçue",
"source": "1200313720195264613",
"montant": 7.8
},
{
"date": "09/05/2025 18:31:46",
"type": "TVA reçue",
"source": "1263573205268828231",
"montant": 7.6
},
{
"date": "09/05/2025 19:10:15",
"type": "TVA reçue",
"source": "534003230435442690",
"montant": 100.0
},
{
"date": "09/05/2025 19:15:25",
"type": "TVA reçue",
"source": "534003230435442690",
"montant": 100.0
},
{
"date": "11/05/2025 13:06:24",
"type": "TVA reçue",
"source": "1321863072242995332",
"montant": 330.0
},
{
"date": "11/05/2025 14:49:46",
"type": "TVA reçue",
"source": "1072112886509473792",
"montant": 14000.0
},
{
"date": "11/05/2025 14:50:02",
"type": "TVA reçue",
"source": "501080933282218005",
"montant": 2400.0
},
{
"date": "12/05/2025 18:35:06",
"type": "TVA reçue",
"source": "1263573205268828231",
"montant": 7.6
},
{
"date": "18/05/2025 15:17:43",
"type": "TVA reçue",
"source": "1126995714552111215",
"montant": 2400.0
},
{
"date": "18/05/2025 15:34:58",
"type": "TVA reçue",
"source": "376796341071773706",
"montant": 2400.0
},
{
"date": "18/05/2025 15:34:58",
"type": "TVA reçue",
"source": "376796341071773706",
"montant": 2400.0
},
{
"date": "19/05/2025 11:55:01",
"type": "TVA reçue",
"source": "1200313720195264613",
"montant": 7.8
},
{
"date": "29/05/2025 13:51:53",
"type": "TVA reçue",
"source": "376796341071773706",
"montant": 6000.0
},
{
"date": "29/05/2025 15:16:42",
"type": "TVA reçue",
"source": "353942721997832195",
"montant": 27.0
},
{
"date": "06/06/2025 20:09:01",
"type": "TVA reçue",
"source": "353942721997832195",
"montant": 42.0
},
{
"date": "20/06/2025 19:06:14",
"type": "TVA reçue",
"source": "1244372888702816458",
"montant": 2000.0
},
{
"date": "29/06/2025 12:31:17",
"type": "Dépôt",
"source": "Banque",
"montant": 15869.0,
"emetteur": 353942721997832195,
"destinataire": "GOUV"
}
]
}
},
"NGC Bank": {
"Journal en onde": {
"rib": "93884 11674 79519 40904",
"solde": 6031.0,
"mdp": "E96zM0Jj",
"tva": 2,
"transactions": [
{
"date": "05/05/2025 16:23:40",
"type": "Virement reçu",
"source": "docteur_w.d_axolotl",
"montant": 490.0
},
{
"date": "05/05/2025 16:24:07",
"type": "Dépôt",
"source": "Banque",
"montant": 4900.0,
"emetteur": 534003230435442690,
"destinataire": "Journal en onde"
},
{
"date": "06/05/2025 10:49:55",
"type": "Virement reçu",
"source": "e.93130",
"montant": 392.0
},
{
"date": "06/05/2025 18:19:03",
"type": "Virement reçu",
"source": "e.93130",
"montant": 49.0
}
]
},
"Milano Motors": {
"rib": "79307 68952 45977 60249",
"solde": 126400.0,
"mdp": "9YcjNvud",
"tva": 20,
"transactions": [
{
"date": "11/05/2025 14:49:46",
"type": "Virement reçu",
"source": "lagrenouillebeka",
"montant": 56000.0
},
{
"date": "11/05/2025 14:50:02",
"type": "Virement reçu",
"source": "justeme",
"montant": 9600.0
},
{
"date": "18/05/2025 15:17:43",
"type": "Virement reçu",
"source": "jl_wesley.f1",
"montant": 9600.0
},
{
"date": "18/05/2025 15:34:58",
"type": "Virement reçu",
"source": "bitehoven",
"montant": 9600.0
},
{
"date": "18/05/2025 15:34:58",
"type": "Virement reçu",
"source": "bitehoven",
"montant": 9600.0
},
{
"date": "29/05/2025 13:51:53",
"type": "Virement reçu",
"source": "bitehoven",
"montant": 24000.0
},
{
"date": "20/06/2025 19:06:14",
"type": "Virement reçu",
"source": "oghacker1337",
"montant": 8000.0
}
]
},
"Sicilia mia": {
"rib": "19440 99822 26785 73807",
"solde": 108.0,
"mdp": "8sqmuoz4",
"tva": 20,
"transactions": [
{
"date": "29/05/2025 15:16:42",
"type": "Virement reçu",
"source": "mg_studios_",
"montant": 108.0
}
]
},
"Domaine de vins": {
"rib": "96241 34733 44548 77079",
"solde": 1768.0,
"mdp": "hIJj81ci",
"tva": 20,
"transactions": [
{
"date": "05/05/2025 16:09:39",
"type": "Virement reçu",
"source": "mg_studios_",
"montant": 1600.0
},
{
"date": "06/06/2025 20:09:01",
"type": "Virement reçu",
"source": "mg_studios_",
"montant": 168.0
}
]
},
"Umbrella Assurances": {
"rib": "54201 75079 71658 35659",
"solde": 2969.4,
"mdp": "KDRQGd3R",
"tva": 2,
"transactions": [
{
"date": "28/04/2025 08:53:44",
"type": "Virement reçu",
"source": "tricks212_zz",
"montant": 416.5
},
{
"date": "28/04/2025 10:42:38",
"type": "Virement reçu",
"source": "e.93130",
"montant": 372.4
},
{
"date": "28/04/2025 15:37:27",
"type": "Virement reçu",
"source": "hvk_pompotfraicz",
"montant": 416.5
},
{
"date": "01/05/2025 08:56:08",
"type": "Virement reçu",
"source": "neyzizz",
"montant": 254.8
},
{
"date": "09/05/2025 17:23:13",
"type": "Virement reçu",
"source": "agent_chaos_officiel",
"montant": 382.2
},
{
"date": "09/05/2025 18:31:46",
"type": "Virement reçu",
"source": "e.93130",
"montant": 372.4
},
{
"date": "12/05/2025 18:35:06",
"type": "Virement reçu",
"source": "e.93130",
"montant": 372.4
},
{
"date": "19/05/2025 11:55:01",
"type": "Virement reçu",
"source": "agent_chaos_officiel",
"montant": 382.2
}
]
},
"Auto école": {
"rib": "11940 73588 95258 04768",
"solde": 15971.0,
"mdp": "zcEKe45Y",
"tva": 20,
"transactions": [
{
"date": "26/04/2025 15:55:13",
"type": "Virement reçu",
"source": "nixfix06",
"montant": 0.8
},
{
"date": "27/04/2025 14:01:32",
"type": "Virement reçu",
"source": "nixfix06",
"montant": 0.8
},
{
"date": "01/05/2025 13:08:59",
"type": "Virement reçu",
"source": "sz94d",
"montant": 1200.0
},
{
"date": "11/05/2025 13:06:24",
"type": "Virement reçu",
"source": "sz94d",
"montant": 1320.0
}
]
},
"Sicilia Mia": {
"rib": "68229 13765 90163 35585",
"solde": 1907.09,
"mdp": "6uTORm3r",
"tva": 10,
"transactions": [
{
"date": "04/05/2025 16:51:20",
"type": "Virement reçu",
"source": "gameurpro12",
"montant": 0.09000000000000001
}
]
},
"NGC Bank": {
"rib": "15156 38436 35920 54357",
"solde": 1040900.0,
"mdp": "nxDcskg0",
"tva": 2,
"transactions": [
{
"date": "09/05/2025 19:10:15",
"type": "Dépôt",
"source": "Banque",
"montant": 4900.0,
"emetteur": 534003230435442690,
"destinataire": "NGC Bank"
},
{
"date": "09/05/2025 19:15:25",
"type": "Dépôt",
"source": "Banque",
"montant": 4900.0,
"emetteur": 534003230435442690,
"destinataire": "NGC Bank"
},
{
"date": "09/05/2025 19:15:51",
"type": "Retrait",
"source": "Banque",
"montant": -4900.0,
"emetteur": 534003230435442690,
"destinataire": "NGC Bank"
},
{
"date": "24/05/2025 13:44:09",
"type": "Dépôt",
"source": "Banque",
"montant": 1000000.0,
"emetteur": 534003230435442690,
"destinataire": "NGC Bank"
},
{
"date": "04/06/2025 16:30:19",
"type": "Dépôt",
"source": "Banque",
"montant": 36000.0,
"emetteur": 534003230435442690,
"destinataire": "NGC Bank"
}
]
},
"Agence immobilière.": {
"rib": "54044 32109 51269 69180",
"solde": 108000.0,
"mdp": "tCH7QK7G",
"tva": 20,
"transactions": [
{
"date": "03/07/2025 22:35:20",
"type": "Dépôt",
"source": "Banque",
"montant": 108000.0,
"emetteur": 353942721997832195,
"destinataire": "Agence immobilière."
}
]
},
"La Concordia": {
"rib": "09860 71327 12935 47361",
"solde": 0,
"mdp": "3vo66Wqu",
"tva": 10,
"transactions": []
}
}
}

6
counting_data.json Normal file
View file

@ -0,0 +1,6 @@
{
"1390076581892063354": {
"current": 4,
"last_user": 1200313720195264613
}
}

3
id_compteur.json Normal file
View file

@ -0,0 +1,3 @@
{
"dernier_id": 152
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 KiB

View file

@ -0,0 +1,174 @@
<div className="w-[1920px] h-[1080px] relative bg-white overflow-hidden">
<div className="w-80 h-[1080px] left-0 top-0 absolute bg-gray-700 inline-flex flex-col justify-between items-start">
<div className="self-stretch h-28 px-3.5 inline-flex justify-between items-center">
<div className="w-40 h-7 flex justify-start items-center">
<div className="w-12 h-12 relative overflow-hidden">
<div className="w-11 h-11 left-[3.20px] top-[3.20px] absolute bg-white" />
</div>
<div className="w-28 h-7 text-center justify-center text-white text-xl font-normal font-['Inter']">Utilisateur</div>
<div className="w-10 h-10 relative overflow-hidden">
<div className="w-4 h-2 left-[11.72px] top-[15.86px] absolute outline outline-2 outline-offset-[-1px] outline-white" />
</div>
</div>
<div className="w-10 h-10 relative origin-top-left rotate-180 overflow-hidden">
<div className="w-6 h-7 left-[9.19px] top-[7.44px] absolute outline outline-2 outline-offset-[-1px] outline-white" />
</div>
</div>
<div className="self-stretch h-[507px] px-4 py-2.5 inline-flex justify-start items-start gap-3.5 flex-wrap content-start">
<div className="w-80 justify-center text-white text-xl font-normal font-['Inter']">Employer :</div>
<div className="w-72 h-16 px-[5px] bg-slate-800 rounded-[10px] flex justify-start items-center gap-2.5">
<div className="w-12 h-12 relative overflow-hidden">
<div className="w-11 h-11 left-[3.20px] top-[3.20px] absolute bg-violet-500" />
</div>
<div className="justify-center text-white text-xl font-normal font-['Inter']">Nom du joueur</div>
</div>
<div className="w-72 h-16 px-[5px] bg-slate-800 rounded-[10px] flex justify-start items-center gap-2.5">
<div className="w-12 h-12 relative overflow-hidden">
<div className="w-11 h-11 left-[3.20px] top-[3.20px] absolute bg-violet-500" />
</div>
<div className="justify-center text-white text-xl font-normal font-['Inter']">Nom du joueur</div>
</div>
<div className="w-72 h-16 px-[5px] bg-slate-800 rounded-[10px] flex justify-start items-center gap-2.5">
<div className="w-12 h-12 relative overflow-hidden">
<div className="w-11 h-11 left-[3.20px] top-[3.20px] absolute bg-violet-500" />
</div>
<div className="justify-center text-white text-xl font-normal font-['Inter']">Nom du joueur</div>
</div>
<div className="flex-1 h-16 px-[5px] bg-slate-800 rounded-[10px] flex justify-start items-center gap-2.5">
<div className="w-12 h-12 relative overflow-hidden">
<div className="w-11 h-11 left-[3.20px] top-[3.20px] absolute bg-violet-500" />
</div>
<div className="justify-center text-white text-xl font-normal font-['Inter']">Nom du joueur</div>
</div>
</div>
<div className="self-stretch h-80 p-2.5 bg-violet-500 rounded-tl-[20px] rounded-tr-[20px] inline-flex justify-center items-start gap-2.5 flex-wrap content-start">
<div className="w-80 h-9 inline-flex flex-col justify-between items-start">
<div className="justify-center text-white text-xl font-normal font-['Inter']">Suggestion :</div>
</div>
</div>
</div>
<div className="w-[1590px] h-28 px-2.5 left-[330px] top-0 absolute bg-slate-500/20 inline-flex justify-between items-center">
<div className="flex justify-start items-center gap-3.5">
<div className="w-20 h-20 relative overflow-hidden">
<div className="w-10 h-9 left-[19.79px] top-[20px] absolute bg-black" />
</div>
<div className="w-80 h-10 text-center justify-center text-black text-2xl font-normal font-['Inter']">Nom de l’entreprise</div>
</div>
<div className="px-12 flex justify-start items-center gap-10">
<div className="w-10 h-10 relative overflow-hidden">
<div className="w-8 h-8 left-[3.32px] top-[3.28px] absolute bg-black" />
</div>
<div className="w-10 h-10 relative overflow-hidden">
<div className="w-8 h-8 left-[3.33px] top-[3.33px] absolute bg-black" />
</div>
<div className="w-10 h-10 relative overflow-hidden">
<div className="w-8 h-6 left-[3.33px] top-[8.33px] absolute bg-black" />
</div>
<div className="w-10 h-10 relative overflow-hidden">
<div className="w-8 h-9 left-[3.33px] top-[2.99px] absolute bg-black" />
</div>
</div>
</div>
<div className="w-[1590px] h-[970px] p-14 left-[330px] top-[110px] absolute inline-flex justify-start items-start gap-20 flex-wrap content-start">
<div className="w-[1442px] flex justify-start items-center gap-2.5">
<div className="text-center justify-center text-black text-3xl font-normal font-['Inter']">Information :</div>
</div>
<div className="w-[530px] h-72 bg-white rounded-[20px] shadow-[0px_4px_43.79999923706055px_0px_rgba(0,0,0,0.25)]" />
<div className="w-[530px] h-72 bg-white rounded-[20px] shadow-[0px_4px_43.79999923706055px_0px_rgba(0,0,0,0.25)]" />
<div className="w-40 h-72 bg-white rounded-[20px] shadow-[0px_4px_43.79999923706055px_0px_rgba(0,0,0,0.25)]" />
<div className="w-[530px] h-72 bg-white rounded-[20px] shadow-[0px_4px_43.79999923706055px_0px_rgba(0,0,0,0.25)]" />
<div className="w-36 h-72 bg-white rounded-[20px] shadow-[0px_4px_43.79999923706055px_0px_rgba(0,0,0,0.25)]" />
<div className="w-[535px] h-72 bg-white rounded-[20px] shadow-[0px_4px_43.79999923706055px_0px_rgba(0,0,0,0.25)]" />
</div>
<div className="w-5 h-5 left-[290px] top-[756px] absolute bg-white" />
<div className="p-2.5 left-[1641px] top-[1016px] absolute inline-flex justify-center items-center gap-2.5">
<div className="text-center justify-start text-black/20 text-4xl font-normal font-['Inter']">Victoria’s Vice™</div>
</div>
</div>
<div class="w-[1920px] h-[1080px] relative bg-white overflow-hidden">
<div class="w-80 h-[1080px] left-0 top-0 absolute bg-gray-700 inline-flex flex-col justify-between items-start">
<div class="self-stretch h-28 px-3.5 inline-flex justify-between items-center">
<div class="w-40 h-7 flex justify-start items-center">
<div class="w-12 h-12 relative overflow-hidden">
<div class="w-11 h-11 left-[3.20px] top-[3.20px] absolute bg-white"></div>
</div>
<div class="w-28 h-7 text-center justify-center text-white text-xl font-normal font-['Inter']">Utilisateur</div>
<div class="w-10 h-10 relative overflow-hidden">
<div class="w-4 h-2 left-[11.72px] top-[15.86px] absolute outline outline-2 outline-offset-[-1px] outline-white"></div>
</div>
</div>
<div class="w-10 h-10 relative origin-top-left rotate-180 overflow-hidden">
<div class="w-6 h-7 left-[9.19px] top-[7.44px] absolute outline outline-2 outline-offset-[-1px] outline-white"></div>
</div>
</div>
<div class="self-stretch h-[507px] px-4 py-2.5 inline-flex justify-start items-start gap-3.5 flex-wrap content-start">
<div class="w-80 justify-center text-white text-xl font-normal font-['Inter']">Employer :</div>
<div class="w-72 h-16 px-[5px] bg-slate-800 rounded-[10px] flex justify-start items-center gap-2.5">
<div class="w-12 h-12 relative overflow-hidden">
<div class="w-11 h-11 left-[3.20px] top-[3.20px] absolute bg-violet-500"></div>
</div>
<div class="justify-center text-white text-xl font-normal font-['Inter']">Nom du joueur</div>
</div>
<div class="w-72 h-16 px-[5px] bg-slate-800 rounded-[10px] flex justify-start items-center gap-2.5">
<div class="w-12 h-12 relative overflow-hidden">
<div class="w-11 h-11 left-[3.20px] top-[3.20px] absolute bg-violet-500"></div>
</div>
<div class="justify-center text-white text-xl font-normal font-['Inter']">Nom du joueur</div>
</div>
<div class="w-72 h-16 px-[5px] bg-slate-800 rounded-[10px] flex justify-start items-center gap-2.5">
<div class="w-12 h-12 relative overflow-hidden">
<div class="w-11 h-11 left-[3.20px] top-[3.20px] absolute bg-violet-500"></div>
</div>
<div class="justify-center text-white text-xl font-normal font-['Inter']">Nom du joueur</div>
</div>
<div class="flex-1 h-16 px-[5px] bg-slate-800 rounded-[10px] flex justify-start items-center gap-2.5">
<div class="w-12 h-12 relative overflow-hidden">
<div class="w-11 h-11 left-[3.20px] top-[3.20px] absolute bg-violet-500"></div>
</div>
<div class="justify-center text-white text-xl font-normal font-['Inter']">Nom du joueur</div>
</div>
</div>
<div class="self-stretch h-80 p-2.5 bg-violet-500 rounded-tl-[20px] rounded-tr-[20px] inline-flex justify-center items-start gap-2.5 flex-wrap content-start">
<div class="w-80 h-9 inline-flex flex-col justify-between items-start">
<div class="justify-center text-white text-xl font-normal font-['Inter']">Suggestion :</div>
</div>
</div>
</div>
<div class="w-[1590px] h-28 px-2.5 left-[330px] top-0 absolute bg-slate-500/20 inline-flex justify-between items-center">
<div class="flex justify-start items-center gap-3.5">
<div class="w-20 h-20 relative overflow-hidden">
<div class="w-10 h-9 left-[19.79px] top-[20px] absolute bg-black"></div>
</div>
<div class="w-80 h-10 text-center justify-center text-black text-2xl font-normal font-['Inter']">Nom de l’entreprise</div>
</div>
<div class="px-12 flex justify-start items-center gap-10">
<div class="w-10 h-10 relative overflow-hidden">
<div class="w-8 h-8 left-[3.32px] top-[3.28px] absolute bg-black"></div>
</div>
<div class="w-10 h-10 relative overflow-hidden">
<div class="w-8 h-8 left-[3.33px] top-[3.33px] absolute bg-black"></div>
</div>
<div class="w-10 h-10 relative overflow-hidden">
<div class="w-8 h-6 left-[3.33px] top-[8.33px] absolute bg-black"></div>
</div>
<div class="w-10 h-10 relative overflow-hidden">
<div class="w-8 h-9 left-[3.33px] top-[2.99px] absolute bg-black"></div>
</div>
</div>
</div>
<div class="w-[1590px] h-[970px] p-14 left-[330px] top-[110px] absolute inline-flex justify-start items-start gap-20 flex-wrap content-start">
<div class="w-[1442px] flex justify-start items-center gap-2.5">
<div class="text-center justify-center text-black text-3xl font-normal font-['Inter']">Information :</div>
</div>
<div class="w-[530px] h-72 bg-white rounded-[20px] shadow-[0px_4px_43.79999923706055px_0px_rgba(0,0,0,0.25)]"></div>
<div class="w-[530px] h-72 bg-white rounded-[20px] shadow-[0px_4px_43.79999923706055px_0px_rgba(0,0,0,0.25)]"></div>
<div class="w-40 h-72 bg-white rounded-[20px] shadow-[0px_4px_43.79999923706055px_0px_rgba(0,0,0,0.25)]"></div>
<div class="w-[530px] h-72 bg-white rounded-[20px] shadow-[0px_4px_43.79999923706055px_0px_rgba(0,0,0,0.25)]"></div>
<div class="w-36 h-72 bg-white rounded-[20px] shadow-[0px_4px_43.79999923706055px_0px_rgba(0,0,0,0.25)]"></div>
<div class="w-[535px] h-72 bg-white rounded-[20px] shadow-[0px_4px_43.79999923706055px_0px_rgba(0,0,0,0.25)]"></div>
</div>
<div class="w-5 h-5 left-[290px] top-[756px] absolute bg-white"></div>
<div class="p-2.5 left-[1641px] top-[1016px] absolute inline-flex justify-center items-center gap-2.5">
<div class="text-center justify-start text-black/20 text-4xl font-normal font-['Inter']">Victoria’s Vice™</div>
</div>
</div>

Binary file not shown.

After

Width:  |  Height:  |  Size: 171 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 174 KiB

View file

@ -0,0 +1 @@
{}

500
ressources/data.json Normal file
View file

@ -0,0 +1,500 @@
{
"identities": {
"353942721997832195": {
"nom": "RICCI",
"prenom": "Aldo",
"date_naissance": "22/05/1967",
"lieu_naissance": "Victoria's Vice",
"sexe": "Homme",
"date_expiration": "01/12/2026"
},
"1096453463849828362": {
"nom": "DE MONTROSE",
"prenom": "Warren",
"date_naissance": "04/04/1991",
"lieu_naissance": "Londres",
"sexe": "Homme",
"date_expiration": "20/06/2027"
},
"914118793918312480": {
"nom": "CORREA",
"prenom": "Mattia",
"date_naissance": "28/04/1986",
"lieu_naissance": "Milan",
"sexe": "Homme",
"date_expiration": "01/12/2026"
},
"768915179995004988": {
"nom": "KASHMIRI",
"prenom": "Khalid",
"date_naissance": "19/07/1988",
"lieu_naissance": "Algérie",
"sexe": "Homme",
"date_expiration": "13/04/2027"
},
"828923448453103637": {
"nom": "NORLOCK",
"prenom": "Bucky",
"date_naissance": "04/06/1999",
"lieu_naissance": "Venise",
"sexe": "Homme",
"date_expiration": "24/06/2027"
},
"971446412690722826": {
"nom": "PAS GENTIL",
"prenom": "Pas gentil",
"date_naissance": "04/04/2025",
"lieu_naissance": "Pas Gentil",
"sexe": "Autre",
"date_expiration": "11/04/2027"
},
"1324439906046574693": {
"nom": "DZQDQDZ",
"prenom": "Dzqdqzdz",
"date_naissance": "01/01/2000",
"lieu_naissance": "dzqd",
"sexe": "Femme",
"date_expiration": "12/03/2027"
},
"1200313720195264613": {
"nom": "HAMILTON",
"prenom": "Leonardo",
"date_naissance": "09/01/1985",
"lieu_naissance": "Canada Québec",
"sexe": "Homme",
"date_expiration": "01/05/2027"
},
"942073262333112351": {
"nom": "MURDOCK",
"prenom": "Matthew",
"date_naissance": "12/03/1990",
"lieu_naissance": "Hells Kitchen",
"sexe": "Homme",
"date_expiration": "11/04/2027"
},
"828239898975666216": {
"nom": "VINDERTECH",
"prenom": "Félix",
"date_naissance": "11/05/1990",
"lieu_naissance": "Paris",
"sexe": "Homme",
"date_expiration": "11/04/2027"
},
"836606840254496788": {
"nom": "MARECHAL",
"prenom": "Charles",
"date_naissance": "08/01/1995",
"lieu_naissance": "Paris",
"sexe": "Homme",
"date_expiration": "11/04/2027"
},
"1126995714552111215": {
"nom": "FIGLINI",
"prenom": "José",
"date_naissance": "28/12/2001",
"lieu_naissance": "Victoria's Vice",
"sexe": "Homme",
"date_expiration": "11/04/2027"
},
"1108087282692522105": {
"nom": "JULES",
"prenom": "Delcourt",
"date_naissance": "18/04/2004",
"lieu_naissance": "Paris",
"sexe": "Homme",
"date_expiration": "25/06/2027"
},
"1351957361333370932": {
"nom": "DURAND",
"prenom": "Mehdi",
"date_naissance": "20/11/2002",
"lieu_naissance": "Paris",
"sexe": "Homme",
"date_expiration": "13/04/2027"
},
"936337534760386580": {
"nom": "HAKERMAN",
"prenom": "Malo",
"date_naissance": "25/11/2000",
"lieu_naissance": "Paris",
"sexe": "Homme",
"date_expiration": "13/04/2027"
},
"449424004663607307": {
"nom": "DOLOS",
"prenom": "Arturo",
"date_naissance": "01/04/2001",
"lieu_naissance": "Salina",
"sexe": "Homme",
"date_expiration": "13/04/2027"
},
"881841899634323456": {
"nom": "LAVIBE",
"prenom": "Rachid",
"date_naissance": "28/03/2001",
"lieu_naissance": "Portugal",
"sexe": "Homme",
"date_expiration": "13/04/2027"
},
"946068504023556186": {
"nom": "MARSTON",
"prenom": "John",
"date_naissance": "09/10/1974",
"lieu_naissance": "Montreal",
"sexe": "Homme",
"date_expiration": "13/04/2027"
},
"1203485889251115089": {
"nom": "CARTER",
"prenom": "Ethan",
"date_naissance": "16/11/2000",
"lieu_naissance": "Midnight city",
"sexe": "Homme",
"date_expiration": "13/04/2027"
},
"1324527110425018438": {
"nom": "DEMARCHI",
"prenom": "Stella",
"date_naissance": "18/08/2001",
"lieu_naissance": "Milan",
"sexe": "Femme",
"date_expiration": "13/04/2027"
},
"901856948184559676": {
"nom": "PILISO",
"prenom": "Dario",
"date_naissance": "25/02/1980",
"lieu_naissance": "Milan",
"sexe": "Homme",
"date_expiration": "13/04/2027"
},
"906574786308734986": {
"nom": "FERRARI",
"prenom": "Pedro",
"date_naissance": "28/02/2000",
"lieu_naissance": "Rome",
"sexe": "Homme",
"date_expiration": "10/05/2027"
},
"1311384140905709578": {
"nom": "LOPEZ",
"prenom": "Kayz",
"date_naissance": "04/07/2000",
"lieu_naissance": "Venise",
"sexe": "Homme",
"date_expiration": "22/06/2027"
},
"501080933282218005": {
"nom": "GIORGINO",
"prenom": "Antoine",
"date_naissance": "17/06/2004",
"lieu_naissance": "Paris",
"sexe": "Homme",
"date_expiration": "21/04/2027"
},
"826145675506745395": {
"nom": "GALLERY",
"prenom": "Valentino",
"date_naissance": "19/06/1994",
"lieu_naissance": "Nice",
"sexe": "Homme",
"date_expiration": "21/04/2027"
},
"534003230435442690": {
"nom": "DI REGIA",
"prenom": "Salvatore",
"date_naissance": "09/11/1990",
"lieu_naissance": "Rome",
"sexe": "Homme",
"date_expiration": "21/04/2027"
},
"1304410873015832596": {
"nom": "EL MASRI",
"prenom": "Rayan",
"date_naissance": "21/10/1999",
"lieu_naissance": "Algerie",
"sexe": "Homme",
"date_expiration": "25/04/2027"
},
"1321933664535183395": {
"nom": "MICKALSON",
"prenom": "Jackson",
"date_naissance": "08/02/1995",
"lieu_naissance": "Paris",
"sexe": "Homme",
"date_expiration": "25/04/2027"
},
"1267066015238586452": {
"nom": "BLOSSOM",
"prenom": "Kira",
"date_naissance": "23/02/2003",
"lieu_naissance": "Miami",
"sexe": "Homme",
"date_expiration": "25/04/2027"
},
"1263573205268828231": {
"nom": "FERNANDEZ",
"prenom": "Mattéo",
"date_naissance": "07/05/1999",
"lieu_naissance": "Mexique",
"sexe": "Homme",
"date_expiration": "25/04/2027"
},
"730574544821157988": {
"nom": "ZENTORNO",
"prenom": "Neiz",
"date_naissance": "10/06/2000",
"lieu_naissance": "Algérie",
"sexe": "Homme",
"date_expiration": "25/04/2027"
},
"1244372888702816458": {
"nom": "LOPEZ",
"prenom": "Oussama",
"date_naissance": "11/07/2002",
"lieu_naissance": "chatelet les halles",
"sexe": "Homme",
"date_expiration": "27/04/2027"
},
"1321863072242995332": {
"nom": "RAMINEZ",
"prenom": "Rios",
"date_naissance": "10/02/2004",
"lieu_naissance": "Rome",
"sexe": "Homme",
"date_expiration": "09/06/2027"
},
"1363308330721738842": {
"nom": "BELFORD",
"prenom": "Jordan",
"date_naissance": "13/11/2004",
"lieu_naissance": "Manhattan New York",
"sexe": "Homme",
"date_expiration": "16/05/2027"
},
"599130976806764545": {
"nom": "HAMILTON",
"prenom": "William",
"date_naissance": "23/08/1977",
"lieu_naissance": "Québec",
"sexe": "Homme",
"date_expiration": "01/05/2027"
},
"1268584648611729418": {
"nom": "VINCE",
"prenom": "Kyle",
"date_naissance": "08/07/2000",
"lieu_naissance": "Paris",
"sexe": "Homme",
"date_expiration": "03/05/2027"
},
"727188532669448509": {
"nom": "LEROY",
"prenom": "Mady",
"date_naissance": "19/03/1997",
"lieu_naissance": "montreal",
"sexe": "Femme",
"date_expiration": "08/05/2027"
},
"1322531063012134943": {
"nom": "REVERSE",
"prenom": "Stanle",
"date_naissance": "07/10/2002",
"lieu_naissance": "Los Angeles",
"sexe": "Homme",
"date_expiration": "03/05/2027"
},
"1361493382911758528": {
"nom": "AMARA",
"prenom": "Madja",
"date_naissance": "12/06/2000",
"lieu_naissance": "Annaba",
"sexe": "Femme",
"date_expiration": "10/05/2027"
},
"561485865562406914": {
"nom": "ASVALT",
"prenom": "Harmonium",
"date_naissance": "01/01/2000",
"lieu_naissance": "Luxembourg",
"sexe": "Homme",
"date_expiration": "10/05/2027"
},
"1282306873563021357": {
"nom": "GRIMM",
"prenom": "Djelys",
"date_naissance": "25/09/1999",
"lieu_naissance": "Paris",
"sexe": "Homme",
"date_expiration": "10/05/2027"
},
"1238099342288752661": {
"nom": "MAJO GARO",
"prenom": "Wireless",
"date_naissance": "1984/10/28",
"lieu_naissance": "Mexique",
"sexe": "Homme",
"date_expiration": "17/05/2027"
},
"1058418257142501417": {
"nom": "LOUÏE",
"prenom": "Jean",
"date_naissance": "02/07/2005",
"lieu_naissance": "Toulouse",
"sexe": "Homme",
"date_expiration": "29/05/2027"
},
"1351821260140707862": {
"nom": "RICCI",
"prenom": "Antonio",
"date_naissance": "30/03/2000",
"lieu_naissance": "Rome",
"sexe": "Homme",
"date_expiration": "07/06/2027"
},
"558327236378361857": {
"nom": "MUSULIN",
"prenom": "Tony",
"date_naissance": "15/11/1987",
"lieu_naissance": "New York",
"sexe": "Homme",
"date_expiration": "07/06/2027"
},
"1364012189052178533": {
"nom": "LORAS",
"prenom": "Philippe",
"date_naissance": "06/03/2000",
"lieu_naissance": "Kinshasa",
"sexe": "Homme",
"date_expiration": "20/06/2027"
},
"1350161151539024007": {
"nom": "PILISO",
"prenom": "Antonio",
"date_naissance": "30/11/2006",
"lieu_naissance": "Bogotta",
"sexe": "Homme",
"date_expiration": "07/06/2027"
},
"1312077385780301865": {
"nom": "LONDUHAUT",
"prenom": "Yanis",
"date_naissance": "07/03/2001",
"lieu_naissance": "New York",
"sexe": "Homme",
"date_expiration": "09/06/2027"
},
"1217095995804876950": {
"nom": "SMITH",
"prenom": "Franklin",
"date_naissance": "11/08/2004",
"lieu_naissance": "Los Angeles",
"sexe": "Homme",
"date_expiration": "22/06/2027"
},
"376796341071773706": {
"nom": "FRICADELLO",
"prenom": "Niccolo",
"date_naissance": "24/11/1987",
"lieu_naissance": "Chihuahua",
"sexe": "Homme",
"date_expiration": "15/06/2027"
},
"478564181356838922": {
"nom": "ECCLESTON",
"prenom": "John",
"date_naissance": "21 aout 1990",
"lieu_naissance": "Londres",
"sexe": "Homme",
"date_expiration": "15/06/2027"
},
"897082541033545739": {
"nom": "SALVADOR",
"prenom": "Diego",
"date_naissance": "18/06/2003",
"lieu_naissance": "Mexico",
"sexe": "Homme",
"date_expiration": "22/06/2027"
},
"1048991928496177152": {
"nom": "BRABUS",
"prenom": "Selim",
"date_naissance": "08/01/2002",
"lieu_naissance": "Rome",
"sexe": "Homme",
"date_expiration": "15/06/2027"
},
"1351650470392893512": {
"nom": "LOPEZ",
"prenom": "Jason",
"date_naissance": "19/09/1990",
"lieu_naissance": "Milano",
"sexe": "Homme",
"date_expiration": "19/06/2027"
},
"1218642392043884685": {
"nom": "MANCINI",
"prenom": "Salvatore",
"date_naissance": "22/11/1965",
"lieu_naissance": "Venice",
"sexe": "Homme",
"date_expiration": "19/06/2027"
},
"1246814346139668553": {
"nom": "MENDEZ",
"prenom": "Arthur",
"date_naissance": "20/03/2000",
"lieu_naissance": "Guillerval",
"sexe": "Homme",
"date_expiration": "19/06/2027"
},
"1072112886509473792": {
"nom": "FROG",
"prenom": "Beka",
"date_naissance": "19/10/1994",
"lieu_naissance": "Toronto",
"sexe": "Homme",
"date_expiration": "20/06/2027"
},
"690202239536857174": {
"nom": "DO SANTOS",
"prenom": "Raphaël",
"date_naissance": "19/06/1998",
"lieu_naissance": "Sao Paulo",
"sexe": "Homme",
"date_expiration": "22/06/2027"
},
"1290382773013577802": {
"nom": "DO SANTOS",
"prenom": "Fernando",
"date_naissance": "04/02/2003",
"lieu_naissance": "Sao Paulo",
"sexe": "Homme",
"date_expiration": "25/06/2027"
},
"930869581931745410": {
"nom": "REAPER",
"prenom": "Tommy",
"date_naissance": "25/01/2003",
"lieu_naissance": "New York",
"sexe": "Homme",
"date_expiration": "25/06/2027"
},
"667478308870422568": {
"nom": "SUPER",
"prenom": "Giovanni",
"date_naissance": "08/10/1993",
"lieu_naissance": "Stockholm",
"sexe": "Homme",
"date_expiration": "02/07/2027"
},
"1341203038609739889": {
"nom": "MARAZEL",
"prenom": "Tyson",
"date_naissance": "26/08/2002",
"lieu_naissance": "Marseille",
"sexe": "Homme",
"date_expiration": "04/07/2027"
}
}
}

1
stats_commandes.json Normal file
View file

@ -0,0 +1 @@
{}

64
taches.json Normal file
View file

@ -0,0 +1,64 @@
{
"taches": {
"7": {
"titre": "Contactez White",
"description": "Faire des vidéos de la map",
"assigné": 534003230435442690,
"statut": "À faire",
"priorité": "Critique",
"échéance": "2025-06-30",
"créé_par": 971446412690722826,
"commentaires": []
},
"10": {
"titre": "Vocal en rapport avec les sanctions récente",
"description": "J'aimerais que tu me programme une voc **URGENTE** avec Nooxi au plus vite et toi pour que je discute avec vous a propos des sanctions à votre encontre",
"assigné": 1108087282692522105,
"statut": "Terminée",
"priorité": "Critique",
"échéance": "2025-07-04",
"créé_par": 971446412690722826,
"commentaires": []
},
"8": {
"titre": "PRÉSENTATION DU SERVEUR",
"description": "refaire la présentation du serveur",
"assigné": 914118793918312480,
"statut": "À faire",
"priorité": "Haute",
"échéance": "2025-02-07",
"créé_par": 914118793918312480,
"commentaires": []
},
"9": {
"titre": "Liste Métier counter",
"description": "Bot qui ajoute automatiquement +1 ou -1 sur le poste quand un joueur reçoit le rôle",
"assigné": 971446412690722826,
"statut": "🛠️ V1.7 de Vicy en préparation. 📅 Sortie à définir.",
"priorité": "Moyenne",
"échéance": "2025-07-04",
"créé_par": 667478308870422568,
"commentaires": []
},
"3": {
"titre": "Mise en production de Vicy publique",
"description": "Création de Vicy publique",
"assigné": 971446412690722826,
"statut": "À faire",
"priorité": "Basse",
"échéance": "2025-09-01",
"créé_par": 971446412690722826,
"commentaires": []
},
"151": {
"titre": "dzqdz",
"description": "dzqqdz",
"assigné": 971446412690722826,
"statut": "Terminée",
"priorité": "Critique",
"échéance": "2025-07-22",
"créé_par": 971446412690722826,
"commentaires": []
}
}
}

0
utils/__init__.py Normal file
View file

58
utils/banque_manager.py Normal file
View file

@ -0,0 +1,58 @@
import json
import os
from datetime import datetime
from .logger import log_error
class BanqueManager:
def __init__(self):
self.file_path = "comptes_bancaires.json"
self.comptes = {}
self.load_data()
def load_data(self):
"""Charge les données bancaires depuis un fichier JSON."""
if os.path.exists(self.file_path):
try:
with open(self.file_path, "r", encoding="utf-8") as f:
data = f.read().strip()
self.comptes = json.loads(data) if data else {}
except json.JSONDecodeError:
log_error("Erreur de lecture dans comptes_bancaires.json, réinitialisation...")
self.comptes = {}
self.save_data()
def save_data(self):
"""Sauvegarde les comptes et transactions dans comptes_bancaires.json."""
with open(self.file_path, "w", encoding="utf-8") as f:
json.dump(self.comptes, f, indent=4, ensure_ascii=False)
def enregistrer_transaction(self, emetteur_id, destinataire_id, montant, type_transaction):
"""
Enregistre une transaction dans l'historique et recharge les données en temps réel.
"""
self.load_data()
transaction = {
"date": datetime.now().strftime("%d/%m/%Y %H:%M:%S"),
"type": type_transaction,
"montant": montant,
"emetteur": emetteur_id,
"destinataire": destinataire_id
}
if emetteur_id not in self.comptes:
self.comptes[emetteur_id] = {"solde": 0, "prets": [], "transactions": []}
elif "transactions" not in self.comptes[emetteur_id]:
self.comptes[emetteur_id]["transactions"] = []
self.comptes[emetteur_id]["transactions"].append(transaction)
if destinataire_id not in self.comptes:
self.comptes[destinataire_id] = {"solde": 0, "prets": [], "transactions": []}
elif "transactions" not in self.comptes[destinataire_id]:
self.comptes[destinataire_id]["transactions"] = []
self.comptes[destinataire_id]["transactions"].append(transaction)
self.save_data()

126
utils/card_generator.py Normal file
View file

@ -0,0 +1,126 @@
import os
import aiohttp
import discord
from io import BytesIO
from PIL import Image, ImageDraw, ImageFont
class CardGenerator:
def __init__(self):
self.session = None
self.template = None
self.watermark = None
self.font_regular = None
self.font_bold = None
self.font_large = None
self.create_template()
def create_template(self):
try:
CI_path = "/home/discord/Bot/Vicy/ressources/assets/CIITAL.png"
if not os.path.exists(CI_path):
raise FileNotFoundError(f"Image de fond introuvable : {CI_path}")
# Chargement de l'image de base
self.template = Image.open(CI_path).convert("RGBA")
self.template = self.template.resize((800, 500))
# Vérifier que l'image a bien été chargée
if self.template is None:
raise ValueError("L'image de fond n'a pas été chargée correctement.")
# Chargement des polices
try:
self.font_regular = ImageFont.load_default()
self.font_bold = ImageFont.load_default()
self.font_large = ImageFont.load_default()
except Exception as e:
print(f"Erreur lors du chargement des polices: {e}")
self.font_regular = None
self.font_bold = None
self.font_large = None
# Créer un filigrane transparent
self.watermark = Image.new('RGBA', (800, 500), (255, 255, 255, 0))
watermark_draw = ImageDraw.Draw(self.watermark)
for i in range(0, 800, 100):
for j in range(0, 500, 100):
watermark_draw.text((i, j), "", fill=(200, 200, 200, 100))
except Exception as e:
print(f"Erreur lors de la création du template: {e}")
self.template = None
self.watermark = None
async def get_session(self):
if self.session is None:
self.session = aiohttp.ClientSession()
return self.session
async def download_avatar(self, user):
if not user.avatar:
return Image.new('RGBA', (200, 200), (128, 128, 128, 255))
try:
session = await self.get_session()
async with session.get(str(user.avatar.url)) as response:
if response.status == 200:
avatar_data = await response.read()
return Image.open(BytesIO(avatar_data)).convert('RGBA')
except Exception as e:
print(f"Erreur lors du téléchargement de l'avatar: {e}")
return Image.new('RGBA', (200, 200), (128, 128, 128, 255))
async def create_identity_card(self, user, identity_data):
if not self.template:
self.create_template()
if not self.template:
raise Exception("Impossible de créer le template")
card = self.template.copy()
draw = ImageDraw.Draw(card)
try:
# Photo de profil
avatar = await self.download_avatar(user)
avatar = avatar.resize((200, 200), Image.LANCZOS)
mask = Image.new('L', avatar.size, 0)
mask_draw = ImageDraw.Draw(mask)
mask_draw.ellipse((0, 0, 200, 200), fill=255)
card.paste(avatar, (50, 120), mask)
# Informations personnelles
text_positions = {
'nom': (328, 169),
'prenom': (329, 212),
'date_naissance': (329, 254),
'lieu_naissance': (489, 169),
'sexe': (511, 212),
'date_expiration': (26, 408)
}
for field, pos in text_positions.items():
value = str(identity_data[field])
draw.text(pos,
value,
fill='black',
font=self.font_bold if field in ['nom', 'prenom'] else self.font_regular)
if self.watermark:
card.paste(self.watermark, (0, 0), self.watermark)
buffer = BytesIO()
card.save(buffer, format='PNG')
buffer.seek(0)
return buffer
except Exception as e:
print(f"Erreur lors de la création de la carte: {e}")
raise
async def cleanup(self):
if self.session:
await self.session.close()
self.session = None

61
utils/data_manager.py Normal file
View file

@ -0,0 +1,61 @@
import json
import os
from datetime import datetime, timedelta
class DataManager:
""" Gère la sauvegarde et le chargement des données. """
def __init__(self):
self.identities = {}
self.load_data()
def save_data(self):
""" Sauvegarde les identités dans un fichier JSON. """
try:
with open('ressources/data.json', 'w', encoding='utf-8') as f:
json.dump({'identities': self.identities}, f, ensure_ascii=False, indent=4)
except Exception as e:
print(f"Erreur lors de la sauvegarde des données: {e}")
def load_data(self):
""" Charge les identités depuis un fichier JSON. """
try:
if os.path.exists('ressources/data.json'):
with open('ressources/data.json', 'r', encoding='utf-8') as f:
data = json.load(f)
self.identities = data.get('identities', {})
except Exception as e:
print(f"Erreur lors du chargement des données: {e}")
self.identities = {}
class DataManager:
def __init__(self):
"""Initialise le gestionnaire de données et charge les identités existantes."""
self.file_path = os.path.join(os.path.dirname(__file__), "../ressources/data.json")
self.identities = self.load_data() # Charge les données existantes
def load_data(self):
"""Charge les identités depuis le fichier JSON."""
if not os.path.exists(self.file_path):
print(f"⚠️ Fichier {self.file_path} introuvable. Création d'un nouveau fichier...")
self.save_data() # Crée un fichier vide
return {}
try:
with open(self.file_path, "r", encoding="utf-8") as f:
data = json.load(f)
return data.get("identities", {})
except (json.JSONDecodeError, IOError) as e:
print(f"❌ Erreur lors du chargement des données : {e}")
return {}
def save_data(self):
"""Sauvegarde les identités dans un fichier JSON."""
try:
os.makedirs(os.path.dirname(self.file_path), exist_ok=True)
with open(self.file_path, "w", encoding="utf-8") as f:
json.dump({"identities": self.identities}, f, ensure_ascii=False, indent=4)
print(f"✅ Données sauvegardées dans {self.file_path}")
except IOError as e:
print(f"❌ Erreur lors de la sauvegarde des données : {e}")

89
utils/gestion_taches.py Normal file
View file

@ -0,0 +1,89 @@
import json
import os
import asyncio
from datetime import datetime
FICHIER_TACHES = "taches.json"
FICHIER_TACHES_TMP = "taches.json.tmp"
FICHIER_ID = "id_compteur.json"
file_lock = asyncio.Lock()
async def charger_taches():
async with file_lock:
if not os.path.exists(FICHIER_TACHES):
with open(FICHIER_TACHES, "w", encoding='utf-8') as f:
json.dump({"taches": {}}, f, indent=4)
with open(FICHIER_TACHES, "r", encoding='utf-8') as f:
return json.load(f)
async def sauvegarder_taches(donnees):
async with file_lock:
with open(FICHIER_TACHES_TMP, "w", encoding='utf-8') as f:
json.dump(donnees, f, indent=4, ensure_ascii=False)
os.replace(FICHIER_TACHES_TMP, FICHIER_TACHES)
async def generer_id_unique():
async with file_lock:
if not os.path.exists(FICHIER_ID):
with open(FICHIER_ID, "w", encoding='utf-8') as f:
json.dump({"dernier_id": 0}, f, indent=4)
with open(FICHIER_ID, "r+", encoding='utf-8') as f:
data = json.load(f)
data["dernier_id"] += 1
f.seek(0)
json.dump(data, f, indent=4)
f.truncate()
return str(data["dernier_id"])
async def ajouter_tache(titre, description, user_id, priorite, date_echeance, auteur_id):
data = await charger_taches()
taches = data["taches"]
id_tache = await generer_id_unique()
taches[id_tache] = {
"titre": titre,
"description": description,
"assigné": user_id,
"statut": "À faire",
"priorité": priorite,
"échéance": date_echeance,
"créé_par": auteur_id,
"commentaires": []
}
await sauvegarder_taches(data)
return id_tache
async def update_tache_status(id_tache, statut):
data = await charger_taches()
if id_tache in data["taches"]:
data["taches"][id_tache]["statut"] = statut
await sauvegarder_taches(data)
return True
return False
async def supprimer_tache(id_tache):
data = await charger_taches()
if id_tache in data["taches"]:
del data["taches"][id_tache]
await sauvegarder_taches(data)
return True
return False
async def lister_taches_utilisateur(user_id):
data = await charger_taches()
return {tid: t for tid, t in data["taches"].items() if t["assigné"] == user_id}
async def taches_proches():
data = await charger_taches()
proches = {}
now = datetime.now()
for tid, t in data["taches"].items():
try:
date_obj = datetime.strptime(t["échéance"], "%Y-%m-%d")
if 0 <= (date_obj - now).days <= 2:
proches[tid] = t
except ValueError:
continue
return proches

50
utils/image_generator.py Normal file
View file

@ -0,0 +1,50 @@
import json
from PIL import Image, ImageDraw, ImageFont
import os
def generate_bank_image(user_id, balance, transactions):
"""Génère une image avec les informations bancaires de l'utilisateur."""
image_path = "ressources/assets/template.png"
if not os.path.exists(image_path):
print(f"❌ Erreur : Fichier {image_path} introuvable.")
return None
image = Image.open(image_path)
draw = ImageDraw.Draw(image)
# Vérifier que la police existe
font_path = "ressources/font.ttf"
if not os.path.exists(font_path):
print("❌ Erreur : Police introuvable, chargement de la police par défaut.")
font_large = ImageFont.load_default()
font_small = ImageFont.load_default()
else:
font_large = ImageFont.truetype(font_path, 120)
font_small = ImageFont.truetype(font_path, 30)
# Centrer le solde en dessous de la fenêtre bleue
balance_text = f"Solde : {balance} €"
balance_x = (image.width - draw.textlength(balance_text, font=font_large)) // 2
balance_y = 850 # Ajustez cette valeur pour positionner le texte en dessous de la fenêtre bleue
draw.text((balance_x, balance_y), balance_text, fill="white", font=font_large)
# Affichage des transactions
start_x = 50 # Ajustez cette valeur pour l'alignement horizontal
start_y = 400 # Ajustez cette valeur pour l'alignement vertical
spacing_y = 60 # Espace entre les lignes de transaction
for transaction in transactions[:3]: # Max 3 transactions
transaction_text = f"{transaction['date']} - {transaction['type']} : {transaction['montant']}€"
draw.text((start_x, start_y), transaction_text, fill="black", font=font_small)
start_y += spacing_y
# Supprimer le texte "Victoria’s Vice™"
erase_box = (30, image.height - 50, 250, image.height - 20)
draw.rectangle(erase_box, fill=(255, 255, 255))
# Sauvegarde de l'image générée
output_path = f"ressources/bank_{user_id}.png"
image.save(output_path)
print(f"✅ Image bancaire générée : {output_path}")
return output_path

34
utils/logger.py Normal file
View file

@ -0,0 +1,34 @@
import logging
from datetime import datetime
import pytz # Nécessite le package pytz
# Définir le fuseau horaire France
paris_tz = pytz.timezone("Europe/Paris")
class FranceFormatter(logging.Formatter):
def formatTime(self, record, datefmt=None):
dt = datetime.fromtimestamp(record.created, tz=paris_tz)
if datefmt:
return dt.strftime(datefmt)
return dt.isoformat()
# Création du logger
logger = logging.getLogger()
logger.setLevel(logging.INFO)
# Handler vers fichier
file_handler = logging.FileHandler("log.txt", encoding="utf-8")
formatter = FranceFormatter("%(asctime)s - %(levelname)s - %(message)s", datefmt="%Y-%m-%d %H:%M:%S")
file_handler.setFormatter(formatter)
logger.addHandler(file_handler)
# Fonctions de log
def log_info(message):
logger.info(message)
def log_warning(message):
logger.warning(message)
def log_error(message):
logger.error(message)

82
utils/stats_manager.py Normal file
View file

@ -0,0 +1,82 @@
import sqlite3
import datetime
import os
# Chemin de la base de données (dans un sous-dossier "data" pour garder le projet propre)
DB_PATH = "data/stats.db"
# Crée le dossier "data" si besoin
os.makedirs(os.path.dirname(DB_PATH), exist_ok=True)
# Connexion à la base de données
conn = sqlite3.connect(DB_PATH)
cursor = conn.cursor()
# Création de la table si elle n'existe pas
cursor.execute('''
CREATE TABLE IF NOT EXISTS commandes (
id INTEGER PRIMARY KEY AUTOINCREMENT,
nom TEXT,
user_id INTEGER,
guild_id INTEGER,
date TIMESTAMP
)
''')
conn.commit()
def enregistrer_utilisation(nom_commande, user_id, guild_id):
"""Enregistre l'utilisation d'une commande."""
cursor.execute(
"INSERT INTO commandes (nom, user_id, guild_id, date) VALUES (?, ?, ?, ?)",
(nom_commande, user_id, guild_id, datetime.datetime.now())
)
conn.commit()
def get_stats():
"""Retourne les statistiques d'utilisation du bot."""
cursor.execute("SELECT COUNT(*) FROM commandes")
total = cursor.fetchone()[0]
cursor.execute("SELECT nom, COUNT(*) as nb FROM commandes GROUP BY nom ORDER BY nb DESC LIMIT 5")
top_commandes = cursor.fetchall()
cursor.execute("SELECT user_id, COUNT(*) as nb FROM commandes GROUP BY user_id ORDER BY nb DESC LIMIT 1")
top_user = cursor.fetchone()
return total, top_commandes, top_user
def get_stats_detailed():
# Exemple de données simulées, à remplacer par tes vraies données
from datetime import datetime, timedelta
total_commandes = 1234
top_commandes = [("help", 250), ("stats_bot", 180), ("play", 160), ("ban", 100), ("kick", 90)]
top_utilisateurs = [(123456789012345678, 300), (987654321098765432, 220), (111222333444555666, 180), (444555666777888999, 160), (555666777888999000, 130)]
today = datetime.today()
evolution_commandes = []
for i in range(7): # dernière semaine
day = today - timedelta(days=6 - i)
evolution_commandes.append((day, 150 + i*10))
repartition_statuts = {"À faire": 50, "En cours": 30, "Terminée": 70, "Bloqué": 10}
commandes_par_jour = {
"Lundi": 180,
"Mardi": 210,
"Mercredi": 150,
"Jeudi": 200,
"Vendredi": 220,
"Samedi": 90,
"Dimanche": 40,
}
return {
"total_commandes": total_commandes,
"top_commandes": top_commandes,
"top_utilisateurs": top_utilisateurs,
"evolution_commandes": evolution_commandes,
"repartition_statuts": repartition_statuts,
"commandes_par_jour": commandes_par_jour
}

30
utils/welcome_message.py Normal file
View file

@ -0,0 +1,30 @@
import discord
from discord.ext import commands
from utils.logger import log_info, log_error
class WelcomeMessage(commands.Cog):
def __init__(self, bot):
self.bot = bot
@commands.Cog.listener()
async def on_member_join(self, member):
try:
# ID du salon discussion
welcome_channel_id = 1278483527783944242
welcome_channel = member.guild.get_channel(welcome_channel_id)
if welcome_channel:
welcome_message = (
f"## BIENVENUE <:panda_welcome:1297298999849517056> !\n\n"
f"> **L'équipe staff te souhaite la bienvenue sur Victoria's Vice {member.mention} !**\n\n"
f"> Pour ta candidature <:arrow_1:1297491509201080362> <#1278477608392196176> / <#1278477152215371806> \n\n"
f"> Si tu as des questions <:arrow_1:1297491509201080362> <#1279037583820718120>\n\n"
f"*> Bien cordialement, L'équipe staff de Victoria's Vice*"
)
await welcome_channel.send(welcome_message)
except Exception as e:
print(f"Erreur lors de l'envoi du message de bienvenue: {e}")
async def setup(bot):
await bot.add_cog(WelcomeMessage(bot))

1
visite_médicale.json Normal file
View file

@ -0,0 +1 @@
{}