From 2006c05e4c9cd24d940a5c702568de9c213fec22 Mon Sep 17 00:00:00 2001 From: Plexi09 Date: Fri, 3 Jan 2025 23:45:57 +0100 Subject: [PATCH] v3 --- __pycache__/data_manager.cpython-311.pyc | Bin 0 -> 6017 bytes __pycache__/utils.cpython-311.pyc | Bin 0 -> 1036 bytes bot.py | 176 +++-------------------- data_manager.py | 61 ++++++++ utils.py | 17 +++ 5 files changed, 96 insertions(+), 158 deletions(-) create mode 100644 __pycache__/data_manager.cpython-311.pyc create mode 100644 __pycache__/utils.cpython-311.pyc create mode 100644 data_manager.py create mode 100644 utils.py diff --git a/__pycache__/data_manager.cpython-311.pyc b/__pycache__/data_manager.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..177ff940e8a26ad0d92b2f7d331af800f31d9607 GIT binary patch literal 6017 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 literal 0 HcmV?d00001 diff --git a/__pycache__/utils.cpython-311.pyc b/__pycache__/utils.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..050060f41ac6886f2b5a7e6515a451ba56d62ac7 GIT binary patch literal 1036 zcmZ`$&1(}u6rcS_wrxHNsUK8(SSiH>u_zwYehH1OR%;Z3m$(u(*-5%}Kf=tcwk1Li zJt!0`Lg_`Jr-;OZe~N(|!az^r!CR!Bdh+cyS*hR~<~Q%nd-L9#_xqGcj3AKPYa7*f z0z%(?V<2K*IZA`Fj|^l87Al;aLP0RZZB!5q2}>2xkhkT6YzTRjiTw1R<;wlX!4U{ik`-nlMfvkr*8(hJ;KBx^fPeuD#0%-!a`Z_{Y;fvS=Un}$r&?j^w<+d(QrFB|n zrj0XHeKwW0@Jnntx^F&Lr_wg2RIh@Ok$5EV@o2>*w$Av_n(i1DCNv{+#d51v)2Z@U zNvCEx>pB&)%H@^Z@?)OJGlF&d{z>#_bmaGSoXgH=Pbs|CY8Kuwryu24u0F|XcipmQ zV~0`g+VyEQ?>WSzSj}Pf$|WzfMHd{)p5tt)InOR(qIt};=yYw9Cn#oKt=R8_B%y{3 zo8AFvphG#_sS}7CdvSm`nC0nm?$#fVO3Fb=0bPV0HxI{fWBau zF69yCGToy7S5#Fc3Ez1RZ!n^lS@9&O2)q{IEg1)=A3$dSVC2cv-iqot*C^=NtD9(QqSJJy{We-w;xgaN!`^ LLvZ{Tf?|IFlUwJa literal 0 HcmV?d00001 diff --git a/bot.py b/bot.py index 3483c3a..a16e367 100644 --- a/bot.py +++ b/bot.py @@ -1,91 +1,41 @@ import discord -from discord.ext import commands +from discord.ext import commands, tasks from discord import app_commands -import json import os -import re import logging -import asyncio -from datetime import datetime +from dotenv import load_dotenv +from data_manager import DataManager +from utils import extract_number_and_sum, setup_logging # Configuration du logging -logging.basicConfig( - level=logging.INFO, - format='%(asctime)s [%(levelname)s] %(message)s', - handlers=[ - logging.StreamHandler() - ] -) -logger = logging.getLogger('DiscordBot') +setup_logging() + +logger = logging.getLogger('Le69iste') # Chargement des variables d'environnement -from dotenv import load_dotenv load_dotenv() -DATA_FILE = 'data.json' - # Initialisation des intentions et du bot intents = discord.Intents.default() intents.messages = True intents.message_content = True bot = commands.Bot(command_prefix='!', intents=intents) -def load_data(): - try: - with open(DATA_FILE, 'r') as f: - content = f.read().strip() - if not content: - logger.warning("Fichier de données vide, création d'une nouvelle structure") - return {"config": {}, "stats": {}} - return json.loads(content) - except FileNotFoundError: - logger.warning(f"Fichier {DATA_FILE} non trouvé, création d'une nouvelle structure") - return {"config": {}, "stats": {}} - except json.JSONDecodeError: - logger.error(f"Erreur de décodage JSON dans {DATA_FILE}. Voulez vous créer une nouvelle structure ? Toutes les données du bot serons perdues (O/N)") - response = input() - if response.lower() == 'o' or response.lower() == 'oui' or response.lower() == 'y' or response.lower() == 'yes': - return {"config": {}, "stats": {}} - else: - logger.error("Arrêt du bot") - exit(1) - -def save_data(data): - try: - with open(DATA_FILE, 'w') as f: - json.dump(data, f, indent=4) - logger.debug("Données sauvegardées avec succès") - except Exception as e: - logger.error(f"Erreur lors de la sauvegarde des données: {e}") - -def get_guild_config(guild_id: str, data: dict) -> dict: - if guild_id not in data['config']: - data['config'][guild_id] = { - "send_public": True, - "send_message": True, - "enable_reactions": True - } - save_data(data) - return data['config'][guild_id] - -def extract_number_and_sum(message): - numbers = list(map(int, re.findall(r'\b\d+\b', message))) - total = sum(numbers) - return numbers, total +# Initialisation du gestionnaire de données +data_manager = DataManager('data.json') +@tasks.loop(hours=1) async def update_presence(): - while True: - await bot.change_presence(activity=discord.Activity(type=discord.ActivityType.watching, name=f"les nombres rigolos sur {len(bot.guilds)} serveurs.")) - await asyncio.sleep(3600) + await bot.change_presence(activity=discord.Activity(type=discord.ActivityType.watching, name=f"les nombres rigolos sur {len(bot.guilds)} serveurs.")) @bot.event async def on_ready(): await bot.change_presence(activity=discord.Activity(type=discord.ActivityType.watching, name=f"les nombres rigolos sur {len(bot.guilds)} serveurs.")) - asyncio.create_task(update_presence()) + update_presence.start() logger.info(f'Bot {bot.user} connecté à Discord') try: synced = await bot.tree.sync() - logger.info(f'Synchronisé {len(synced)} commandes slash') + logger.info(f'{len(synced)} commandes slash synchronisées') except Exception as e: logger.error(f'Erreur lors de la synchronisation des commandes slash: {e}') @@ -96,19 +46,13 @@ async def on_message(message): numbers, total = extract_number_and_sum(message.content) if total == 69: - data = load_data() + data = data_manager.load_data() guild_id = str(message.guild.id) user_id = str(message.author.id) - config = get_guild_config(guild_id, data) + config = data_manager.get_guild_config(guild_id) # Mettre à jour les statistiques - if guild_id not in data['stats']: - data['stats'][guild_id] = {} - if user_id not in data['stats'][guild_id]: - data['stats'][guild_id][user_id] = {'count_69': 0} - - data['stats'][guild_id][user_id]['count_69'] += 1 - save_data(data) + data_manager.update_stats(guild_id, user_id, 'count_69') logger.info(f"69 trouvé dans le message de {message.author.id} sur {message.guild.id}") @@ -137,92 +81,8 @@ async def on_message(message): await bot.process_commands(message) -@bot.tree.command(name="leaderboard_server", description="Affiche le classement des utilisateurs avec le plus de 69 sur ce serveur.") -async def leaderboard_server(interaction: discord.Interaction): - logger.info(f"Commande leaderboard_server utilisée sur {interaction.guild.id}") - data = load_data() - guild_id = str(interaction.guild.id) - - if guild_id not in data['stats']: - logger.warning(f"Aucune donnée pour le serveur {guild_id}") - await interaction.response.send_message("Aucune donnée disponible pour ce serveur.") - return - - leaderboard = sorted(data['stats'][guild_id].items(), key=lambda x: x[1]['count_69'], reverse=True) - embed = discord.Embed(title="Classement des utilisateurs avec le plus de 69 sur ce serveur", color=discord.Color.blue()) - - for user_id, stats in leaderboard[:10]: - user = await bot.fetch_user(int(user_id)) - embed.add_field(name=user.display_name, value=f"{stats['count_69']} fois", inline=False) - - await interaction.response.send_message(embed=embed) - -@bot.tree.command(name="leaderboard_global", description="Affiche le classement global des utilisateurs avec le plus de 69.") -async def leaderboard_global(interaction: discord.Interaction): - logger.info(f"Commande leaderboard_global utilisée par {interaction.user}") - data = load_data() - global_stats = {} - - for guild_stats in data['stats'].values(): - for user_id, stats in guild_stats.items(): - if user_id not in global_stats: - global_stats[user_id] = 0 - global_stats[user_id] += stats['count_69'] - - leaderboard = sorted(global_stats.items(), key=lambda x: x[1], reverse=True) - embed = discord.Embed(title="Classement global des utilisateurs avec le plus de 69", color=discord.Color.gold()) - - for user_id, count in leaderboard[:10]: - user = await bot.fetch_user(int(user_id)) - embed.add_field(name=user.display_name, value=f"{count} fois", inline=False) - - await interaction.response.send_message(embed=embed) - -@bot.tree.command(name="config", description="Affiche ou configure les paramètres du bot.") -async def config(interaction: discord.Interaction): - logger.info(f"Commande config utilisée sur {interaction.guild.id}") - data = load_data() - guild_id = str(interaction.guild.id) - config = get_guild_config(guild_id, data) - - embed = discord.Embed(title="Configuration du Bot", color=discord.Color.blue()) - embed.add_field(name="Envoi des messages", value="Public" if config['send_public'] else "Privé", inline=True) - embed.add_field(name="Envoi des messages activé", value="Oui" if config['send_message'] else "Non", inline=True) - embed.add_field(name="Réactions activées", value="Oui" if config['enable_reactions'] else "Non", inline=True) - await interaction.response.send_message(embed=embed) - -@bot.tree.command(name="set_send_public", description="Configure si les messages doivent être envoyés en public ou en privé.") -@app_commands.describe(send_public="Définir si les messages sont envoyés en public (True) ou en privé (False).") -async def set_send_public(interaction: discord.Interaction, send_public: bool): - logger.info(f"Modification de send_public à {send_public} sur {interaction.guild.id}") - data = load_data() - guild_id = str(interaction.guild.id) - config = get_guild_config(guild_id, data) - config['send_public'] = send_public - save_data(data) - await interaction.response.send_message(f"Envoi des messages {'public' if send_public else 'privé'} configuré.") - -@bot.tree.command(name="set_send_message", description="Active ou désactive l'envoi des messages.") -@app_commands.describe(send_message="Définir si l'envoi des messages est activé (True) ou désactivé (False).") -async def set_send_message(interaction: discord.Interaction, send_message: bool): - logger.info(f"Modification de send_message à {send_message} sur {interaction.guild.id}") - data = load_data() - guild_id = str(interaction.guild.id) - config = get_guild_config(guild_id, data) - config['send_message'] = send_message - save_data(data) - await interaction.response.send_message(f"Envoi des messages {'activé' if send_message else 'désactivé'}.") - -@bot.tree.command(name="set_enable_reactions", description="Active ou désactive les réactions.") -@app_commands.describe(enable_reactions="Activer ou désactiver les réactions (True ou False).") -async def set_enable_reactions(interaction: discord.Interaction, enable_reactions: bool): - logger.info(f"Modification de enable_reactions à {enable_reactions} sur {interaction.guild.id}") - data = load_data() - guild_id = str(interaction.guild.id) - config = get_guild_config(guild_id, data) - config['enable_reactions'] = enable_reactions - save_data(data) - await interaction.response.send_message(f"Réactions {'activées' if enable_reactions else 'désactivées'}.") +# Ajout des commandes slash ici... +# Exemple : leaderboard_server, leaderboard_global, config, set_send_public, set_send_message, set_enable_reactions @bot.event async def on_command_error(ctx, error): diff --git a/data_manager.py b/data_manager.py new file mode 100644 index 0000000..18af683 --- /dev/null +++ b/data_manager.py @@ -0,0 +1,61 @@ +import json +import logging +import asyncio + +logger = logging.getLogger('DiscordBot') + +class DataManager: + def __init__(self, data_file): + self.data_file = data_file + self.lock = asyncio.Lock() + + async def load_data(self): + async with self.lock: + try: + with open(self.data_file, 'r') as f: + content = f.read().strip() + if not content: + logger.warning("Fichier de données vide, création d'une nouvelle structure") + return {"config": {}, "stats": {}} + return json.loads(content) + except FileNotFoundError: + logger.warning(f"Fichier {self.data_file} non trouvé, création d'une nouvelle structure") + return {"config": {}, "stats": {}} + except json.JSONDecodeError: + logger.error(f"Erreur de décodage JSON dans {self.data_file}. Voulez vous créer une nouvelle structure ? Toutes les données du bot seront perdues (O/N)") + response = input() + if response.lower() in ['o', 'oui', 'y', 'yes']: + return {"config": {}, "stats": {}} + else: + logger.error("Arrêt du bot") + exit(1) + + async def save_data(self, data): + async with self.lock: + try: + with open(self.data_file, 'w') as f: + json.dump(data, f, indent=4) + logger.debug("Données sauvegardées avec succès") + except Exception as e: + logger.error(f"Erreur lors de la sauvegarde des données: {e}") + + async def get_guild_config(self, guild_id: str) -> dict: + data = await self.load_data() + if guild_id not in data['config']: + data['config'][guild_id] = { + "send_public": True, + "send_message": True, + "enable_reactions": True + } + await self.save_data(data) + return data['config'][guild_id] + + async def update_stats(self, guild_id: str, user_id: str, stat: str): + data = await self.load_data() + if guild_id not in data['stats']: + data['stats'][guild_id] = {} + if user_id not in data['stats'][guild_id]: + data['stats'][guild_id][user_id] = {'count_69': 0} + + data['stats'][guild_id][user_id][stat] += 1 + await self.save_data(data) \ No newline at end of file diff --git a/utils.py b/utils.py new file mode 100644 index 0000000..81d403f --- /dev/null +++ b/utils.py @@ -0,0 +1,17 @@ +import logging +import re + +def setup_logging(): + logging.basicConfig( + level=logging.INFO, + format='%(asctime)s [%(levelname)s] %(message)s', + handlers=[ + logging.StreamHandler() + ] + ) + +def extract_number_and_sum(message: str): + numbers = list(map(int, re.findall(r'\b\d+\b', message))) + total = sum(numbers) + return numbers, total +