From 175be88aceb101c41ed980bd0cd6f57aabf54b9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micka=C3=ABl?= <159157671+ArcElewyn@users.noreply.github.com> Date: Wed, 13 Aug 2025 14:04:34 +0200 Subject: [PATCH] Update RFT.py Added bosses difficulties --- RFT.py | 900 +++++++++++++++++++++++++++++++++++---------------------- 1 file changed, 550 insertions(+), 350 deletions(-) diff --git a/RFT.py b/RFT.py index fba0c03..ea275e0 100644 --- a/RFT.py +++ b/RFT.py @@ -1,73 +1,84 @@ """ -DISCORD BOT - Personal Best Tracker for Multiple Bosses -====================================================== +DISCORD BOT - Personal Best Tracker with Multiple Boss Difficulties +================================================================== COMMANDS DOCUMENTATION: ===================== 📊 PERSONAL BEST COMMANDS: -------------------------- -!pbhydra → Show your Hydra PB + screenshot -!pbhydra → Show username's Hydra PB + screenshot -!pbhydra → Submit new Hydra PB (requires screenshot attachment) +HYDRA: +!pbhydra normal → Submit Normal Hydra PB (requires screenshot) +!pbhydra hard → Submit Hard Hydra PB (requires screenshot) +!pbhydra brutal → Submit Brutal Hydra PB (requires screenshot) +!pbhydra nightmare → Submit Nightmare Hydra PB (requires screenshot) +!pbhydra normal → Show your Normal Hydra PB + screenshot +!pbhydra hard → Show username's Hard Hydra PB + screenshot -!pbchimera → Show your Chimera PB + screenshot -!pbchimera → Show username's Chimera PB + screenshot -!pbchimera → Submit new Chimera PB (requires screenshot attachment) +CHIMERA: +!pbchimera easy → Submit Easy Chimera PB (requires screenshot) +!pbchimera normal → Submit Normal Chimera PB (requires screenshot) +!pbchimera hard → Submit Hard Chimera PB (requires screenshot) +!pbchimera brutal → Submit Brutal Chimera PB (requires screenshot) +!pbchimera nightmare → Submit Nightmare Chimera PB (requires screenshot) +!pbchimera ultra → Submit Ultra Nightmare Chimera PB (requires screenshot) +!pbchimera easy → Show your Easy Chimera PB + screenshot +!pbchimera normal → Show username's Normal Chimera PB + screenshot -!pbcvc → Show your CvC PB + screenshot -!pbcvc → Show username's CvC PB + screenshot -!pbcvc → Submit new CvC PB (requires screenshot attachment) +CVC (unchanged): +!pbcvc → Submit CvC PB (requires screenshot) +!pbcvc → Show your CvC PB + screenshot +!pbcvc → Show username's CvC PB + screenshot 🏆 LEADERBOARD COMMANDS (GLOBAL): -------------------------------- -!top10hydra → Top 10 Hydra records (all clans) -!top10chimera → Top 10 Chimera records (all clans) -!top10cvc → Top 10 CvC records (all clans) +!top10hydra → Top 10 Hydra records for specific difficulty +!top10chimera → Top 10 Chimera records for specific difficulty +!top10cvc → Top 10 CvC records ⭐ LEADERBOARD COMMANDS (RTF CLAN): ---------------------------------- -!rtfhydra → Top 10 Hydra records (RTF clan only) -!rtfchimera → Top 10 Chimera records (RTF clan only) -!rtfcvc → Top 10 CvC records (RTF clan only) +!rtfhydra → RTF clan Hydra records for specific difficulty +!rtfchimera → RTF clan Chimera records for specific difficulty +!rtfcvc → RTF clan CvC records 🔥 LEADERBOARD COMMANDS (RTFC CLAN): ----------------------------------- -!rtfchydra → Top 10 Hydra records (RTFC clan only) -!rtfcchimera → Top 10 Chimera records (RTFC clan only) -!rtfccvc → Top 10 CvC records (RTFC clan only) +!rtfchydra → RTFC clan Hydra records for specific difficulty +!rtfcchimera → RTFC clan Chimera records for specific difficulty +!rtfccvc → RTFC clan CvC records ⚡ LEADERBOARD COMMANDS (RTFR CLAN): ----------------------------------- -!rtfrhydra → Top 10 Hydra records (RTFR clan only) -!rtfrchimera → Top 10 Chimera records (RTFR clan only) -!rtfrcvc → Top 10 CvC records (RTFR clan only) +!rtfrhydra → RTFR clan Hydra records for specific difficulty +!rtfrchimera → RTFR clan Chimera records for specific difficulty +!rtfrcvc → RTFR clan CvC records 📈 STATS COMMANDS: ----------------- -!mystats → Show all your PBs across all bosses -!mystats → Show all PBs for specified user -!help → Show user-friendly command list +!mystats → Show all your PBs across all bosses and difficulties +!mystats → Show all PBs for specified user +!help → Show user-friendly command list 💡 USAGE EXAMPLES: ----------------- -!pbhydra [RTF]Alice → Shows Alice's Hydra PB and screenshot -!pbchimera 850000 → Submit 850k damage (with screenshot attached) -!rtfhydra → Shows RTF clan's top 10 Hydra records -!mystats [RTFC]Bob → Shows Bob's complete PB overview +!pbhydra normal 1500000 → Submit 1.5M damage on Normal Hydra (with screenshot) +!pbchimera brutal [RTF]Alice → Shows Alice's Brutal Chimera PB +!rtfhydra nightmare → Shows RTF clan's Nightmare Hydra top 10 🏛️ CLAN SYSTEM: -------------- Clans are auto-detected from username prefixes: -- RTF members: [RTF]Username or RTFUsername -- RTFC members: [RTFC]Username or RTFCUsername -- RTFR members: [RTFR]Username or RTFRUsername +- RTF members: [RTF] Username or [RTF]Username +- RTFC members: [RTFC] Username or [RTFC]Username +- RTFR members: [RTFR] Username or [RTFR]Username 🔧 REQUIREMENTS: --------------- - Screenshot must be attached when submitting new PBs - Only works in authorized channel - Supported image formats: PNG, JPG, JPEG, GIF, WEBP +- Old screenshots are automatically deleted when new PB is set """ import discord @@ -104,21 +115,26 @@ CLAN_CONFIG = { 'color': 0x1e90ff } } + +# Configuration des boss avec difficultés BOSS_CONFIG = { 'hydra': { 'name': 'Hydra', - 'emoji': '🔥', - 'color': 0xff6b35 + 'emoji': '🐍', + 'color': 0xff6b35, + 'difficulties': ['normal', 'hard', 'brutal', 'nightmare'] }, 'chimera': { 'name': 'Chimera', - 'emoji': '⚡', - 'color': 0x9932cc + 'emoji': '🦁', + 'color': 0x9932cc, + 'difficulties': ['easy', 'normal', 'hard', 'brutal', 'nightmare', 'ultra'] }, 'cvc': { 'name': 'Clan vs Clan', 'emoji': '⚔️', - 'color': 0xff0000 + 'color': 0xff0000, + 'difficulties': [] # Pas de difficultés pour CvC } } @@ -128,24 +144,56 @@ class DatabaseManager: self.init_database() def init_database(self): - """Initialise la base de données""" + """Initialise la base de données avec les nouvelles colonnes pour les difficultés""" os.makedirs(os.path.dirname(self.db_path), exist_ok=True) conn = sqlite3.connect(self.db_path) cursor = conn.cursor() + # Table principale avec toutes les difficultés cursor.execute(''' CREATE TABLE IF NOT EXISTS users ( id INTEGER PRIMARY KEY AUTOINCREMENT, discord_username TEXT UNIQUE, - pb_hydra INTEGER DEFAULT 0, - pb_hydra_screenshot TEXT, - pb_hydra_date TIMESTAMP, - pb_chimera INTEGER DEFAULT 0, - pb_chimera_screenshot TEXT, - pb_chimera_date TIMESTAMP, + + -- Hydra difficulties + pb_hydra_normal INTEGER DEFAULT 0, + pb_hydra_normal_screenshot TEXT, + pb_hydra_normal_date TIMESTAMP, + pb_hydra_hard INTEGER DEFAULT 0, + pb_hydra_hard_screenshot TEXT, + pb_hydra_hard_date TIMESTAMP, + pb_hydra_brutal INTEGER DEFAULT 0, + pb_hydra_brutal_screenshot TEXT, + pb_hydra_brutal_date TIMESTAMP, + pb_hydra_nightmare INTEGER DEFAULT 0, + pb_hydra_nightmare_screenshot TEXT, + pb_hydra_nightmare_date TIMESTAMP, + + -- Chimera difficulties + pb_chimera_easy INTEGER DEFAULT 0, + pb_chimera_easy_screenshot TEXT, + pb_chimera_easy_date TIMESTAMP, + pb_chimera_normal INTEGER DEFAULT 0, + pb_chimera_normal_screenshot TEXT, + pb_chimera_normal_date TIMESTAMP, + pb_chimera_hard INTEGER DEFAULT 0, + pb_chimera_hard_screenshot TEXT, + pb_chimera_hard_date TIMESTAMP, + pb_chimera_brutal INTEGER DEFAULT 0, + pb_chimera_brutal_screenshot TEXT, + pb_chimera_brutal_date TIMESTAMP, + pb_chimera_nightmare INTEGER DEFAULT 0, + pb_chimera_nightmare_screenshot TEXT, + pb_chimera_nightmare_date TIMESTAMP, + pb_chimera_ultra INTEGER DEFAULT 0, + pb_chimera_ultra_screenshot TEXT, + pb_chimera_ultra_date TIMESTAMP, + + -- CvC (unchanged) pb_cvc INTEGER DEFAULT 0, pb_cvc_screenshot TEXT, pb_cvc_date TIMESTAMP, + total_attempts INTEGER DEFAULT 0, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ) @@ -157,6 +205,7 @@ class DatabaseManager: id INTEGER PRIMARY KEY AUTOINCREMENT, username TEXT, boss_type TEXT, + difficulty TEXT, damage INTEGER, screenshot_filename TEXT, date TIMESTAMP DEFAULT CURRENT_TIMESTAMP @@ -166,13 +215,18 @@ class DatabaseManager: conn.commit() conn.close() - def get_user_pb(self, username, boss_type): - """Récupère le PB d'un utilisateur pour un boss spécifique""" + def get_user_pb(self, username, boss_type, difficulty=None): + """Récupère le PB d'un utilisateur pour un boss et difficulté spécifique""" conn = sqlite3.connect(self.db_path) cursor = conn.cursor() + if difficulty: + column_prefix = f"pb_{boss_type}_{difficulty}" + else: + column_prefix = f"pb_{boss_type}" + cursor.execute( - f"SELECT pb_{boss_type}, pb_{boss_type}_screenshot, pb_{boss_type}_date FROM users WHERE discord_username = ?", + f"SELECT {column_prefix}, {column_prefix}_screenshot, {column_prefix}_date FROM users WHERE discord_username = ?", (username.lower(),) ) result = cursor.fetchone() @@ -180,57 +234,68 @@ class DatabaseManager: return result if result else (0, None, None) - def update_user_pb(self, username, boss_type, damage, screenshot_filename): - """Met à jour le PB d'un utilisateur pour un boss spécifique""" + def update_user_pb(self, username, boss_type, damage, screenshot_filename, difficulty=None): + """Met à jour le PB d'un utilisateur et supprime l'ancien screenshot""" conn = sqlite3.connect(self.db_path) cursor = conn.cursor() + # Récupérer l'ancien screenshot pour le supprimer + old_data = self.get_user_pb(username, boss_type, difficulty) + old_screenshot = old_data[1] if old_data else None + + if difficulty: + column_prefix = f"pb_{boss_type}_{difficulty}" + else: + column_prefix = f"pb_{boss_type}" + # Créer l'utilisateur s'il n'existe pas, sinon mettre à jour cursor.execute(f''' - INSERT INTO users (discord_username, pb_{boss_type}, pb_{boss_type}_screenshot, pb_{boss_type}_date, total_attempts) + INSERT INTO users (discord_username, {column_prefix}, {column_prefix}_screenshot, {column_prefix}_date, total_attempts) VALUES (?, ?, ?, CURRENT_TIMESTAMP, 1) ON CONFLICT(discord_username) DO UPDATE SET - pb_{boss_type} = ?, - pb_{boss_type}_screenshot = ?, - pb_{boss_type}_date = CURRENT_TIMESTAMP, + {column_prefix} = ?, + {column_prefix}_screenshot = ?, + {column_prefix}_date = CURRENT_TIMESTAMP, total_attempts = total_attempts + 1 ''', (username.lower(), damage, screenshot_filename, damage, screenshot_filename)) # Ajouter à l'historique cursor.execute(''' - INSERT INTO pb_history (username, boss_type, damage, screenshot_filename) - VALUES (?, ?, ?, ?) - ''', (username.lower(), boss_type, damage, screenshot_filename)) + INSERT INTO pb_history (username, boss_type, difficulty, damage, screenshot_filename) + VALUES (?, ?, ?, ?, ?) + ''', (username.lower(), boss_type, difficulty or 'none', damage, screenshot_filename)) conn.commit() conn.close() + + return old_screenshot - def get_leaderboard(self, boss_type, limit=10, clan=None): - """Récupère le classement pour un boss spécifique, optionnellement filtré par clan""" + def get_leaderboard(self, boss_type, difficulty=None, limit=10, clan=None): + """Récupère le classement pour un boss et difficulté spécifique""" conn = sqlite3.connect(self.db_path) cursor = conn.cursor() - if clan: - cursor.execute(f''' - SELECT discord_username, pb_{boss_type}, pb_{boss_type}_date - FROM users - WHERE pb_{boss_type} > 0 AND ( - discord_username LIKE '[{clan}]%' OR - discord_username LIKE '{clan}%' - ) - ORDER BY pb_{boss_type} DESC - LIMIT ? - ''', (limit,)) + if difficulty: + column_prefix = f"pb_{boss_type}_{difficulty}" else: - cursor.execute(f''' - SELECT discord_username, pb_{boss_type}, pb_{boss_type}_date - FROM users - WHERE pb_{boss_type} > 0 - ORDER BY pb_{boss_type} DESC - LIMIT ? - ''', (limit,)) + column_prefix = f"pb_{boss_type}" + base_query = f''' + SELECT discord_username, {column_prefix}, {column_prefix}_date + FROM users + WHERE {column_prefix} > 0 + ''' + + if clan: + base_query += ''' AND ( + discord_username LIKE '[''' + clan + '''] %' OR + discord_username LIKE '[''' + clan + ''']%' + )''' + + base_query += f' ORDER BY {column_prefix} DESC LIMIT ?' + + cursor.execute(base_query, (limit,)) results = cursor.fetchall() conn.close() return results @@ -240,29 +305,43 @@ class DatabaseManager: conn = sqlite3.connect(self.db_path) cursor = conn.cursor() - cursor.execute(''' - SELECT pb_hydra, pb_hydra_date, pb_chimera, pb_chimera_date, pb_cvc, pb_cvc_date - FROM users WHERE discord_username = ? - ''', (username.lower(),)) - + # Récupérer toutes les colonnes de PB + cursor.execute('SELECT * FROM users WHERE discord_username = ?', (username.lower(),)) result = cursor.fetchone() conn.close() - return result + + if not result: + return None + + # Convertir en dictionnaire pour faciliter l'accès + columns = [desc[0] for desc in cursor.description] + return dict(zip(columns, result)) if result else None class ScreenshotManager: def __init__(self, base_path=SCREENSHOTS_BASE_PATH): self.base_path = base_path - # Créer les dossiers pour chaque boss + # Créer les dossiers pour chaque boss et difficulté for boss_type in BOSS_CONFIG.keys(): - os.makedirs(os.path.join(base_path, boss_type), exist_ok=True) + boss_path = os.path.join(base_path, boss_type) + os.makedirs(boss_path, exist_ok=True) + + # Créer sous-dossiers pour les difficultés + for difficulty in BOSS_CONFIG[boss_type]['difficulties']: + difficulty_path = os.path.join(boss_path, difficulty) + os.makedirs(difficulty_path, exist_ok=True) - async def save_screenshot(self, attachment, username, damage, boss_type): + async def save_screenshot(self, attachment, username, damage, boss_type, difficulty=None): """Sauvegarde la screenshot localement""" try: timestamp = int(datetime.now().timestamp()) file_extension = attachment.filename.split('.')[-1].lower() filename = f"{username.lower()}_{damage}_{timestamp}.{file_extension}" - boss_path = os.path.join(self.base_path, boss_type) + + if difficulty: + boss_path = os.path.join(self.base_path, boss_type, difficulty) + else: + boss_path = os.path.join(self.base_path, boss_type) + filepath = os.path.join(boss_path, filename) async with aiohttp.ClientSession() as session: @@ -277,19 +356,41 @@ class ScreenshotManager: print(f"Erreur sauvegarde screenshot: {e}") return None - def get_screenshot_path(self, filename, boss_type): + def get_screenshot_path(self, filename, boss_type, difficulty=None): """Retourne le chemin complet de la screenshot""" if filename: - return os.path.join(self.base_path, boss_type, filename) + if difficulty: + return os.path.join(self.base_path, boss_type, difficulty, filename) + else: + return os.path.join(self.base_path, boss_type, filename) return None + + def delete_old_screenshot(self, filename, boss_type, difficulty=None): + """Supprime l'ancien screenshot""" + if filename: + old_path = self.get_screenshot_path(filename, boss_type, difficulty) + if old_path and os.path.exists(old_path): + try: + os.remove(old_path) + print(f"Ancien screenshot supprimé: {filename}") + except Exception as e: + print(f"Erreur suppression screenshot: {e}") # Fonctions utilitaires def get_user_clan(username): - """Détermine le clan d'un utilisateur basé sur son pseudo""" + """Détermine le clan d'un utilisateur basé sur son pseudo - Version corrigée""" username_upper = username.upper() - for clan_tag in ['[RTF]', '[RTFC]', '[RTFR]', 'RTF', 'RTFC', 'RTFR']: + + # Chercher les tags avec crochets et espace + for clan_tag in ['[RTF] ', '[RTFC] ', '[RTFR] ']: + if username_upper.startswith(clan_tag): + return clan_tag.replace('[', '').replace(']', '').strip() + + # Chercher les tags avec crochets sans espace + for clan_tag in ['[RTF]', '[RTFC]', '[RTFR]']: if username_upper.startswith(clan_tag): return clan_tag.replace('[', '').replace(']', '') + return None def format_datetime(date_str): @@ -312,6 +413,18 @@ def format_date_only(date_str): except: return None +def get_difficulty_display_name(difficulty): + """Convertit le nom de difficulté en nom d'affichage""" + difficulty_names = { + 'ultra': 'Ultra Nightmare', + 'nightmare': 'Nightmare', + 'brutal': 'Brutal', + 'hard': 'Hard', + 'normal': 'Normal', + 'easy': 'Easy' + } + return difficulty_names.get(difficulty, difficulty.title()) + # Initialisation des managers db_manager = DatabaseManager() screenshot_manager = ScreenshotManager() @@ -320,239 +433,208 @@ screenshot_manager = ScreenshotManager() async def on_ready(): print(f"Bot connected as {bot.user}") -async def handle_pb_command(ctx, boss_type, target_user=None, damage=None): - """Fonction générique pour gérer toutes les commandes PB""" +async def handle_pb_command(ctx, boss_type, arg1=None, arg2=None): + """Fonction générique pour gérer toutes les commandes PB avec difficultés""" if ctx.channel.id != AUTHORIZED_CHANNEL_ID: return boss_info = BOSS_CONFIG[boss_type] + difficulties = boss_info['difficulties'] try: - # Cas 1: !pb{boss} username (afficher le PB d'un autre utilisateur) - if target_user and damage is None and not target_user.isdigit(): - pb_data = db_manager.get_user_pb(target_user, boss_type) - pb_damage, screenshot_filename, pb_date = pb_data - - if pb_damage == 0: - await ctx.send(f"❌ **{target_user}** has no {boss_info['name']} PB recorded yet.") - return - - embed = discord.Embed( - title=f"{boss_info['emoji']} {target_user}'s {boss_info['name']} PB", - description=f"**{pb_damage:,} damage**", - color=boss_info['color'] - ) - if pb_date: - formatted_date = format_datetime(pb_date) - if formatted_date: - embed.add_field(name="📅 Record Date", value=formatted_date, inline=False) - - # Envoyer la screenshot si elle existe - if screenshot_filename: - screenshot_path = screenshot_manager.get_screenshot_path(screenshot_filename, boss_type) - if screenshot_path and os.path.exists(screenshot_path): - file = discord.File(screenshot_path, filename=f"{target_user}_{boss_type}_pb.png") - embed.set_image(url=f"attachment://{target_user}_{boss_type}_pb.png") - await ctx.send(embed=embed, file=file) - return - - await ctx.send(embed=embed) + # Pour CvC (pas de difficultés) + if not difficulties: + # Utiliser l'ancienne logique pour CvC + if arg1 and arg1.isdigit(): + damage = int(arg1) + await handle_pb_submission(ctx, boss_type, None, damage) + elif arg1: # Username + await show_user_pb(ctx, boss_type, None, arg1) + else: # Montrer son propre PB + await show_user_pb(ctx, boss_type, None, ctx.author.name) return - # Cas 2: !pb{boss} 1500000 (soumettre un nouveau PB) - if target_user and target_user.isdigit(): - damage = int(target_user) # Le premier argument est en fait le damage + # Pour Hydra et Chimera (avec difficultés) + if not arg1: + # !pbhydra sans arguments - montrer aide + difficulty_list = " | ".join([d.title() for d in difficulties]) + await ctx.send( + f"❌ Please specify difficulty and damage!\n" + f"**Available difficulties:** {difficulty_list}\n" + f"**Examples:**\n" + f"`!pb{boss_type} normal 1500000` - Submit PB with screenshot\n" + f"`!pb{boss_type} hard` - Show your Hard PB\n" + f"`!pb{boss_type} brutal username` - Show user's Brutal PB" + ) + return + + # Vérifier si arg1 est une difficulté valide + if arg1.lower() in difficulties: + difficulty = arg1.lower() - if not ctx.message.attachments: - await ctx.send("❌ Please attach a screenshot to validate your PB!") - return - - attachment = ctx.message.attachments[0] - if not any(attachment.filename.lower().endswith(ext) for ext in ['.png', '.jpg', '.jpeg', '.gif', '.webp']): - await ctx.send("❌ Please attach a valid image file!") - return - - username = ctx.author.name - current_pb, _, _ = db_manager.get_user_pb(username, boss_type) - - if damage > current_pb: - # Sauvegarder la screenshot - screenshot_filename = await screenshot_manager.save_screenshot(attachment, username, damage, boss_type) - - if screenshot_filename: - # Mettre à jour la base - db_manager.update_user_pb(username, boss_type, damage, screenshot_filename) - - improvement = damage - current_pb if current_pb > 0 else damage - embed = discord.Embed( - title=f"🎉 NEW {boss_info['name'].upper()} PB! 🎉", - description=f"**{username}** just hit **{damage:,} damage** on {boss_info['name']}!", - color=0x00ff00 - ) - embed.add_field(name="📈 Improvement", value=f"+{improvement:,} damage", inline=True) - embed.set_image(url=attachment.url) - - await ctx.send(embed=embed) - else: - await ctx.send("❌ Failed to save screenshot. Please try again.") + if arg2 and arg2.isdigit(): + # !pbhydra normal 1500000 - Soumission PB + damage = int(arg2) + await handle_pb_submission(ctx, boss_type, difficulty, damage) + elif arg2: + # !pbhydra normal username - Voir PB d'un utilisateur + await show_user_pb(ctx, boss_type, difficulty, arg2) else: - embed = discord.Embed( - title="💪 Nice attempt!", - description=f"Your damage: **{damage:,}**\nCurrent PB: **{current_pb:,}**", - color=0xffa500 - ) - embed.add_field( - name="Keep going!", - value=f"You need **{current_pb - damage + 1:,}** more damage for a new PB!", - inline=False - ) - await ctx.send(embed=embed) - return - - # Cas 3: !pb{boss} (afficher son propre PB) - username = ctx.author.name - pb_data = db_manager.get_user_pb(username, boss_type) - pb_damage, screenshot_filename, pb_date = pb_data - - if pb_damage == 0: - embed = discord.Embed( - title=f"{boss_info['emoji']} Your {boss_info['name']} PB", - description="**No record yet**", - color=0x666666 + # !pbhydra normal - Voir son propre PB + await show_user_pb(ctx, boss_type, difficulty, ctx.author.name) + else: + # arg1 n'est pas une difficulté valide + difficulty_list = " | ".join([d.title() for d in difficulties]) + await ctx.send( + f"❌ Invalid difficulty: `{arg1}`\n" + f"**Available difficulties:** {difficulty_list}" ) - embed.add_field( - name="💡 Get started!", - value=f"Use `!pb{boss_type} ` with a screenshot to set your first record!", - inline=False - ) - await ctx.send(embed=embed) - return - - embed = discord.Embed( - title=f"{boss_info['emoji']} {username}'s {boss_info['name']} PB", - description=f"**{pb_damage:,} damage**", - color=boss_info['color'] - ) - if pb_date: - formatted_date = format_datetime(pb_date) - if formatted_date: - embed.add_field(name="📅 Record Date", value=formatted_date, inline=False) - - # Envoyer la screenshot si elle existe - if screenshot_filename: - screenshot_path = screenshot_manager.get_screenshot_path(screenshot_filename, boss_type) - if screenshot_path and os.path.exists(screenshot_path): - file = discord.File(screenshot_path, filename=f"{username}_{boss_type}_pb.png") - embed.set_image(url=f"attachment://{username}_{boss_type}_pb.png") - await ctx.send(embed=embed, file=file) - return - - await ctx.send(embed=embed) - + except ValueError: - await ctx.send(f"❌ Please provide a valid damage number!\nExample: `!pb{boss_type} 1500000`") + await ctx.send(f"❌ Please provide a valid damage number!") except Exception as e: await ctx.send(f"❌ Error: {e}") +async def handle_pb_submission(ctx, boss_type, difficulty, damage): + """Gère la soumission d'un nouveau PB""" + if not ctx.message.attachments: + await ctx.send("❌ Please attach a screenshot to validate your PB!") + return + + attachment = ctx.message.attachments[0] + if not any(attachment.filename.lower().endswith(ext) for ext in ['.png', '.jpg', '.jpeg', '.gif', '.webp']): + await ctx.send("❌ Please attach a valid image file!") + return + + username = ctx.author.name + current_pb, _, _ = db_manager.get_user_pb(username, boss_type, difficulty) + + if damage > current_pb: + # Sauvegarder la nouvelle screenshot + screenshot_filename = await screenshot_manager.save_screenshot( + attachment, username, damage, boss_type, difficulty + ) + + if screenshot_filename: + # Mettre à jour la base et récupérer l'ancien screenshot + old_screenshot = db_manager.update_user_pb( + username, boss_type, damage, screenshot_filename, difficulty + ) + + # Supprimer l'ancien screenshot + if old_screenshot: + screenshot_manager.delete_old_screenshot(old_screenshot, boss_type, difficulty) + + improvement = damage - current_pb if current_pb > 0 else damage + boss_info = BOSS_CONFIG[boss_type] + difficulty_name = get_difficulty_display_name(difficulty) if difficulty else "" + + embed = discord.Embed( + title=f"🎉 NEW {boss_info['name'].upper()} PB! 🎉", + description=f"**{username}** just hit **{damage:,} damage** on {difficulty_name} {boss_info['name']}!", + color=0x00ff00 + ) + embed.add_field(name="📈 Improvement", value=f"+{improvement:,} damage", inline=True) + embed.set_image(url=attachment.url) + + await ctx.send(embed=embed) + else: + await ctx.send("❌ Failed to save screenshot. Please try again.") + else: + difficulty_name = get_difficulty_display_name(difficulty) if difficulty else "" + embed = discord.Embed( + title="💪 Nice attempt!", + description=f"Your damage: **{damage:,}**\nCurrent PB: **{current_pb:,}**", + color=0xffa500 + ) + embed.add_field( + name="Keep going!", + value=f"You need **{current_pb - damage + 1:,}** more damage for a new {difficulty_name} PB!", + inline=False + ) + await ctx.send(embed=embed) + +async def show_user_pb(ctx, boss_type, difficulty, username): + """Affiche le PB d'un utilisateur""" + pb_data = db_manager.get_user_pb(username, boss_type, difficulty) + pb_damage, screenshot_filename, pb_date = pb_data + + boss_info = BOSS_CONFIG[boss_type] + difficulty_name = get_difficulty_display_name(difficulty) if difficulty else "" + + if pb_damage == 0: + embed = discord.Embed( + title=f"{boss_info['emoji']} {username}'s {difficulty_name} {boss_info['name']} PB", + description="**No record yet**", + color=0x666666 + ) + embed.add_field( + name="💡 Get started!", + value=f"Use `!pb{boss_type} {difficulty} ` with a screenshot to set your first record!", + inline=False + ) + await ctx.send(embed=embed) + return + + embed = discord.Embed( + title=f"{boss_info['emoji']} {username}'s {difficulty_name} {boss_info['name']} PB", + description=f"**{pb_damage:,} damage**", + color=boss_info['color'] + ) + if pb_date: + formatted_date = format_datetime(pb_date) + if formatted_date: + embed.add_field(name="📅 Record Date", value=formatted_date, inline=False) + + # Envoyer la screenshot si elle existe + if screenshot_filename: + screenshot_path = screenshot_manager.get_screenshot_path(screenshot_filename, boss_type, difficulty) + if screenshot_path and os.path.exists(screenshot_path): + file = discord.File(screenshot_path, filename=f"{username}_{boss_type}_{difficulty}_pb.png") + embed.set_image(url=f"attachment://{username}_{boss_type}_{difficulty}_pb.png") + await ctx.send(embed=embed, file=file) + return + + await ctx.send(embed=embed) + # Commandes pour chaque boss @bot.command() -async def pbhydra(ctx, target_user: str = None, damage: int = None): - """Commande !pbhydra""" - await handle_pb_command(ctx, 'hydra', target_user, damage) +async def pbhydra(ctx, arg1: str = None, arg2: str = None): + """Commande !pbhydra avec gestion des difficultés""" + await handle_pb_command(ctx, 'hydra', arg1, arg2) @bot.command() -async def pbchimera(ctx, target_user: str = None, damage: int = None): - """Commande !pbchimera""" - await handle_pb_command(ctx, 'chimera', target_user, damage) +async def pbchimera(ctx, arg1: str = None, arg2: str = None): + """Commande !pbchimera avec gestion des difficultés""" + await handle_pb_command(ctx, 'chimera', arg1, arg2) @bot.command() -async def pbcvc(ctx, target_user: str = None, damage: int = None): - """Commande !pbcvc""" - await handle_pb_command(ctx, 'cvc', target_user, damage) +async def pbcvc(ctx, target_user: str = None): + """Commande !pbcvc (sans difficultés)""" + await handle_pb_command(ctx, 'cvc', target_user) -# Commandes de classement global -@bot.command() -async def top10hydra(ctx): - """Affiche le top 10 des PB Hydra (tous clans)""" - await show_leaderboard(ctx, 'hydra') - -@bot.command() -async def top10chimera(ctx): - """Affiche le top 10 des PB Chimera (tous clans)""" - await show_leaderboard(ctx, 'chimera') - -@bot.command() -async def top10cvc(ctx): - """Affiche le top 10 des PB CvC (tous clans)""" - await show_leaderboard(ctx, 'cvc') - -# Commandes de classement par clan - RTF -@bot.command() -async def rtfhydra(ctx): - """Affiche le top 10 Hydra du clan RTF""" - await show_leaderboard(ctx, 'hydra', 'RTF') - -@bot.command() -async def rtfchimera(ctx): - """Affiche le top 10 Chimera du clan RTF""" - await show_leaderboard(ctx, 'chimera', 'RTF') - -@bot.command() -async def rtfcvc(ctx): - """Affiche le top 10 CvC du clan RTF""" - await show_leaderboard(ctx, 'cvc', 'RTF') - -# Commandes de classement par clan - RTFC -@bot.command() -async def rtfchydra(ctx): - """Affiche le top 10 Hydra du clan RTFC""" - await show_leaderboard(ctx, 'hydra', 'RTFC') - -@bot.command() -async def rtfcchimera(ctx): - """Affiche le top 10 Chimera du clan RTFC""" - await show_leaderboard(ctx, 'chimera', 'RTFC') - -@bot.command() -async def rtfccvc(ctx): - """Affiche le top 10 CvC du clan RTFC""" - await show_leaderboard(ctx, 'cvc', 'RTFC') - -# Commandes de classement par clan - RTFR -@bot.command() -async def rtfrhydra(ctx): - """Affiche le top 10 Hydra du clan RTFR""" - await show_leaderboard(ctx, 'hydra', 'RTFR') - -@bot.command() -async def rtfrchimera(ctx): - """Affiche le top 10 Chimera du clan RTFR""" - await show_leaderboard(ctx, 'chimera', 'RTFR') - -@bot.command() -async def rtfrcvc(ctx): - """Affiche le top 10 CvC du clan RTFR""" - await show_leaderboard(ctx, 'cvc', 'RTFR') - -async def show_leaderboard(ctx, boss_type, clan=None): +async def show_leaderboard(ctx, boss_type, difficulty=None, clan=None): """Fonction générique pour afficher les classements""" if ctx.channel.id != AUTHORIZED_CHANNEL_ID: return try: boss_info = BOSS_CONFIG[boss_type] - leaderboard = db_manager.get_leaderboard(boss_type, 10, clan) + leaderboard = db_manager.get_leaderboard(boss_type, difficulty, 10, clan) if not leaderboard: clan_text = f" for clan {clan}" if clan else "" - await ctx.send(f"❌ No {boss_info['name']} records found{clan_text} yet!") + difficulty_text = f" {get_difficulty_display_name(difficulty)}" if difficulty else "" + await ctx.send(f"❌ No{difficulty_text} {boss_info['name']} records found{clan_text} yet!") return - # Titre avec clan si spécifié - title = f"🏆 {boss_info['name']} Leaderboard - Top 10" + # Titre avec clan et difficulté si spécifiés + difficulty_name = get_difficulty_display_name(difficulty) if difficulty else "" + title = f"🏆 {difficulty_name} {boss_info['name']} Leaderboard - Top 10" + if clan: clan_info = CLAN_CONFIG.get(clan, {'name': clan, 'emoji': '🏛️'}) - title = f"{clan_info['emoji']} {clan_info['name']} - {boss_info['name']} Top 10" + title = f"{clan_info['emoji']} {clan_info['name']} - {difficulty_name} {boss_info['name']} Top 10" embed = discord.Embed( title=title, @@ -587,9 +669,105 @@ async def show_leaderboard(ctx, boss_type, clan=None): except Exception as e: await ctx.send(f"❌ Error: {e}") +# Commandes de classement global avec difficultés +@bot.command() +async def top10hydra(ctx, difficulty: str = None): + """Affiche le top 10 des PB Hydra pour une difficulté""" + if difficulty and difficulty.lower() in BOSS_CONFIG['hydra']['difficulties']: + await show_leaderboard(ctx, 'hydra', difficulty.lower()) + else: + difficulties = " | ".join(BOSS_CONFIG['hydra']['difficulties']) + await ctx.send(f"❌ Please specify difficulty: `!top10hydra `\n**Available:** {difficulties}") + +@bot.command() +async def top10chimera(ctx, difficulty: str = None): + """Affiche le top 10 des PB Chimera pour une difficulté""" + if difficulty and difficulty.lower() in BOSS_CONFIG['chimera']['difficulties']: + await show_leaderboard(ctx, 'chimera', difficulty.lower()) + else: + difficulties = " | ".join(BOSS_CONFIG['chimera']['difficulties']) + await ctx.send(f"❌ Please specify difficulty: `!top10chimera `\n**Available:** {difficulties}") + +@bot.command() +async def top10cvc(ctx): + """Affiche le top 10 des PB CvC""" + await show_leaderboard(ctx, 'cvc') + +# Commandes de classement par clan - RTF +@bot.command() +async def rtfhydra(ctx, difficulty: str = None): + """Affiche le top 10 Hydra du clan RTF""" + if difficulty and difficulty.lower() in BOSS_CONFIG['hydra']['difficulties']: + await show_leaderboard(ctx, 'hydra', difficulty.lower(), 'RTF') + else: + difficulties = " | ".join(BOSS_CONFIG['hydra']['difficulties']) + await ctx.send(f"❌ Please specify difficulty: `!rtfhydra `\n**Available:** {difficulties}") + +@bot.command() +async def rtfchimera(ctx, difficulty: str = None): + """Affiche le top 10 Chimera du clan RTF""" + if difficulty and difficulty.lower() in BOSS_CONFIG['chimera']['difficulties']: + await show_leaderboard(ctx, 'chimera', difficulty.lower(), 'RTF') + else: + difficulties = " | ".join(BOSS_CONFIG['chimera']['difficulties']) + await ctx.send(f"❌ Please specify difficulty: `!rtfchimera `\n**Available:** {difficulties}") + +@bot.command() +async def rtfcvc(ctx): + """Affiche le top 10 CvC du clan RTF""" + await show_leaderboard(ctx, 'cvc', None, 'RTF') + +# Commandes de classement par clan - RTFC +@bot.command() +async def rtfchydra(ctx, difficulty: str = None): + """Affiche le top 10 Hydra du clan RTFC""" + if difficulty and difficulty.lower() in BOSS_CONFIG['hydra']['difficulties']: + await show_leaderboard(ctx, 'hydra', difficulty.lower(), 'RTFC') + else: + difficulties = " | ".join(BOSS_CONFIG['hydra']['difficulties']) + await ctx.send(f"❌ Please specify difficulty: `!rtfchydra `\n**Available:** {difficulties}") + +@bot.command() +async def rtfcchimera(ctx, difficulty: str = None): + """Affiche le top 10 Chimera du clan RTFC""" + if difficulty and difficulty.lower() in BOSS_CONFIG['chimera']['difficulties']: + await show_leaderboard(ctx, 'chimera', difficulty.lower(), 'RTFC') + else: + difficulties = " | ".join(BOSS_CONFIG['chimera']['difficulties']) + await ctx.send(f"❌ Please specify difficulty: `!rtfcchimera `\n**Available:** {difficulties}") + +@bot.command() +async def rtfccvc(ctx): + """Affiche le top 10 CvC du clan RTFC""" + await show_leaderboard(ctx, 'cvc', None, 'RTFC') + +# Commandes de classement par clan - RTFR +@bot.command() +async def rtfrhydra(ctx, difficulty: str = None): + """Affiche le top 10 Hydra du clan RTFR""" + if difficulty and difficulty.lower() in BOSS_CONFIG['hydra']['difficulties']: + await show_leaderboard(ctx, 'hydra', difficulty.lower(), 'RTFR') + else: + difficulties = " | ".join(BOSS_CONFIG['hydra']['difficulties']) + await ctx.send(f"❌ Please specify difficulty: `!rtfrhydra `\n**Available:** {difficulties}") + +@bot.command() +async def rtfrchimera(ctx, difficulty: str = None): + """Affiche le top 10 Chimera du clan RTFR""" + if difficulty and difficulty.lower() in BOSS_CONFIG['chimera']['difficulties']: + await show_leaderboard(ctx, 'chimera', difficulty.lower(), 'RTFR') + else: + difficulties = " | ".join(BOSS_CONFIG['chimera']['difficulties']) + await ctx.send(f"❌ Please specify difficulty: `!rtfrchimera `\n**Available:** {difficulties}") + +@bot.command() +async def rtfrcvc(ctx): + """Affiche le top 10 CvC du clan RTFR""" + await show_leaderboard(ctx, 'cvc', None, 'RTFR') + @bot.command() async def mystats(ctx, target_user: str = None): - """Affiche tous les PB d'un utilisateur""" + """Affiche tous les PB d'un utilisateur avec les nouvelles difficultés""" if ctx.channel.id != AUTHORIZED_CHANNEL_ID: return @@ -601,40 +779,61 @@ async def mystats(ctx, target_user: str = None): await ctx.send(f"❌ No data found for **{username}**.") return - hydra_pb, hydra_date, chimera_pb, chimera_date, cvc_pb, cvc_date = user_data - embed = discord.Embed( title=f"📊 {username}'s Complete Stats", color=0x00bfff ) - # Hydra - hydra_text = f"**{hydra_pb:,} damage**" if hydra_pb > 0 else "No record" - if hydra_pb > 0 and hydra_date: - formatted_date = format_date_only(hydra_date) - if formatted_date: - hydra_text += f"\n📅 {formatted_date}" - embed.add_field(name="🔥 Hydra PB", value=hydra_text, inline=True) + # Hydra - toutes les difficultés + hydra_stats = [] + for difficulty in BOSS_CONFIG['hydra']['difficulties']: + pb_key = f'pb_hydra_{difficulty}' + date_key = f'pb_hydra_{difficulty}_date' + + if pb_key in user_data and user_data[pb_key] > 0: + pb_value = user_data[pb_key] + pb_date = user_data.get(date_key) + date_text = f" • {format_date_only(pb_date)}" if pb_date else "" + hydra_stats.append(f"**{difficulty.title()}:** {pb_value:,}{date_text}") - # Chimera - chimera_text = f"**{chimera_pb:,} damage**" if chimera_pb > 0 else "No record" - if chimera_pb > 0 and chimera_date: - formatted_date = format_date_only(chimera_date) - if formatted_date: - chimera_text += f"\n📅 {formatted_date}" - embed.add_field(name="⚡ Chimera PB", value=chimera_text, inline=True) + hydra_text = "\n".join(hydra_stats) if hydra_stats else "No records" + embed.add_field(name="🐍 Hydra PBs", value=hydra_text, inline=False) + + # Chimera - toutes les difficultés + chimera_stats = [] + for difficulty in BOSS_CONFIG['chimera']['difficulties']: + pb_key = f'pb_chimera_{difficulty}' + date_key = f'pb_chimera_{difficulty}_date' + + if pb_key in user_data and user_data[pb_key] > 0: + pb_value = user_data[pb_key] + pb_date = user_data.get(date_key) + date_text = f" • {format_date_only(pb_date)}" if pb_date else "" + display_name = "Ultra Nightmare" if difficulty == "ultra" else difficulty.title() + chimera_stats.append(f"**{display_name}:** {pb_value:,}{date_text}") + + chimera_text = "\n".join(chimera_stats) if chimera_stats else "No records" + embed.add_field(name="🦁 Chimera PBs", value=chimera_text, inline=False) # CvC + cvc_pb = user_data.get('pb_cvc', 0) + cvc_date = user_data.get('pb_cvc_date') cvc_text = f"**{cvc_pb:,} damage**" if cvc_pb > 0 else "No record" if cvc_pb > 0 and cvc_date: formatted_date = format_date_only(cvc_date) if formatted_date: - cvc_text += f"\n📅 {formatted_date}" - embed.add_field(name="⚔️ CvC PB", value=cvc_text, inline=True) + cvc_text += f" • {formatted_date}" + embed.add_field(name="⚔️ CvC PB", value=cvc_text, inline=False) - # Total des PB - total_damage = (hydra_pb or 0) + (chimera_pb or 0) + (cvc_pb or 0) - embed.add_field(name="💯 Total Combined", value=f"**{total_damage:,} damage**", inline=False) + # Total combiné + total_damage = 0 + for difficulty in BOSS_CONFIG['hydra']['difficulties']: + total_damage += user_data.get(f'pb_hydra_{difficulty}', 0) + for difficulty in BOSS_CONFIG['chimera']['difficulties']: + total_damage += user_data.get(f'pb_chimera_{difficulty}', 0) + total_damage += user_data.get('pb_cvc', 0) + + embed.add_field(name="💯 Total Combined Damage", value=f"**{total_damage:,}**", inline=False) await ctx.send(embed=embed) @@ -643,7 +842,7 @@ async def mystats(ctx, target_user: str = None): @bot.command() async def help(ctx): - """Affiche la liste des commandes disponibles - Notice utilisateur""" + """Affiche la liste des commandes disponibles avec les nouvelles difficultés""" if ctx.channel.id != AUTHORIZED_CHANNEL_ID: return @@ -653,41 +852,51 @@ async def help(ctx): color=0x00bfff ) - # Commandes PB + # Commandes PB Hydra embed.add_field( - name="📊 Personal Best Commands", - value="`!pbhydra` `!pbchimera` `!pbcvc`\n" + - "• Use alone to see your PB\n" + - "• Add username to see someone else's PB\n" + - "• Add damage + screenshot to submit new PB", + name="🐍 Hydra Commands", + value="**Difficulties:** Normal | Hard | Brutal | Nightmare\n" + + "`!pbhydra ` - Submit PB + screenshot\n" + + "`!pbhydra ` - Show your PB\n" + + "`!pbhydra ` - Show user's PB", + inline=False + ) + + # Commandes PB Chimera + embed.add_field( + name="🦁 Chimera Commands", + value="**Difficulties:** Easy | Normal | Hard | Brutal | Nightmare | Ultra\n" + + "`!pbchimera ` - Submit PB + screenshot\n" + + "`!pbchimera ` - Show your PB\n" + + "`!pbchimera ` - Show user's PB", + inline=False + ) + + # Commandes PB CvC + embed.add_field( + name="⚔️ CvC Commands", + value="`!pbcvc ` - Submit PB + screenshot\n" + + "`!pbcvc` - Show your PB\n" + + "`!pbcvc ` - Show user's PB", inline=False ) # Classements globaux embed.add_field( name="🌍 Global Leaderboards", - value="`!top10hydra` `!top10chimera` `!top10cvc`\n" + - "Shows top 10 records across all clans", + value="`!top10hydra ` - Global Hydra rankings\n" + + "`!top10chimera ` - Global Chimera rankings\n" + + "`!top10cvc` - Global CvC rankings", inline=False ) # Classements par clan embed.add_field( - name="⭐ RTF Clan Rankings", - value="`!rtfhydra` `!rtfchimera` `!rtfcvc`", - inline=True - ) - - embed.add_field( - name="🔥 RTFC Clan Rankings", - value="`!rtfchydra` `!rtfcchimera` `!rtfccvc`", - inline=True - ) - - embed.add_field( - name="⚡ RTFR Clan Rankings", - value="`!rtfrhydra` `!rtfrchimera` `!rtfrcvc`", - inline=True + name="🏛️ Clan Leaderboards", + value="**RTF:** `!rtfhydra ` `!rtfchimera ` `!rtfcvc`\n" + + "**RTFC:** `!rtfchydra ` `!rtfcchimera ` `!rtfccvc`\n" + + "**RTFR:** `!rtfrhydra ` `!rtfrchimera ` `!rtfrcvc`", + inline=False ) # Stats et aide @@ -701,24 +910,15 @@ async def help(ctx): # Instructions embed.add_field( - name="💡 How to Submit a PB", - value="1. Type `!pbhydra ` (example: `!pbhydra 1500000`)\n" + - "2. **Attach a screenshot** to your message\n" + - "3. Bot will automatically save if it's a new record!", + name="💡 Examples", + value="`!pbhydra brutal 1500000` - Submit Brutal Hydra PB\n" + + "`!pbchimera ultra` - Show your Ultra Nightmare PB\n" + + "`!rtfhydra nightmare` - RTF clan Nightmare rankings\n" + + "**Always attach screenshot when submitting PBs!**", inline=False ) - # Note sur les clans - embed.add_field( - name="🏛️ Clan Detection", - value="Your clan is auto-detected from your username:\n" + - "• RTF: `[RTF]Name` or `RTFName`\n" + - "• RTFC: `[RTFC]Name` or `RTFCName`\n" + - "• RTFR: `[RTFR]Name` or `RTFRName`", - inline=False - ) - - embed.set_footer(text="🎮 Good luck with your records!") + embed.set_footer(text="🎮 Old screenshots are automatically deleted when you set new PBs!") await ctx.send(embed=embed)