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

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))