From 4533180ef0ce79ab6b81a714b89bc5aacfcba4e1 Mon Sep 17 00:00:00 2001 From: ArcElewyn Date: Tue, 26 Aug 2025 15:02:34 +0200 Subject: [PATCH] syncro modif prod --- Dockerfile | 2 +- bot.py | 23 +++++++++++++-------- cogs/guide.py | 26 +++++++++++------------ cogs/mercy.py | 20 +++++++++--------- cogs/mystats.py | 30 +++++++++++++-------------- docker-compose.yml | 8 ++++---- utils/leaderboard_handler.py | 22 ++++++++++---------- utils/pb_handler.py | 40 ++++++++++++++++++------------------ 8 files changed, 89 insertions(+), 82 deletions(-) diff --git a/Dockerfile b/Dockerfile index b8051df..d00907e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -12,7 +12,7 @@ COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt # Copy source code -COPY bot.py . +COPY . . # Create required folders RUN mkdir -p screenshots/hydra/normal screenshots/hydra/hard screenshots/hydra/brutal screenshots/hydra/nightmare \ diff --git a/bot.py b/bot.py index 64e6bad..ca5c6f4 100644 --- a/bot.py +++ b/bot.py @@ -1,21 +1,18 @@ import os +import asyncio import discord from discord.ext import commands from config import DISCORD_TOKEN - -# Import des managers from utils.DatabaseManager_class import DatabaseManager from utils.ScreenshotManager_class import ScreenshotManager intents = discord.Intents.default() -intents.message_content = True +intents.message_content = True bot = commands.Bot(command_prefix="!", intents=intents) -# Initialisation unique des managers db_manager = DatabaseManager() screenshot_manager = ScreenshotManager() -# Liste des Cogs à charger initial_cogs = [ "cogs.guide", "cogs.pbhydra", @@ -26,12 +23,22 @@ initial_cogs = [ "cogs.mercy", ] -for cog in initial_cogs: - bot.load_extension(cog) +async def load_all_cogs(): + for cog in initial_cogs: + try: + await bot.load_extension(cog) + print(f"[OK] Cog {cog} chargé") + except Exception as e: + print(f"[ERREUR] Impossible de charger {cog}: {e}") @bot.event async def on_ready(): print(f"{bot.user.name} est connecté !") -bot.run(DISCORD_TOKEN) +async def main(): + await load_all_cogs() + await bot.start(DISCORD_TOKEN) + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/cogs/guide.py b/cogs/guide.py index 2e7eb81..3c260b5 100644 --- a/cogs/guide.py +++ b/cogs/guide.py @@ -8,19 +8,19 @@ class Guide(commands.Cog): @commands.command(name="guide") async def guide(self, ctx): - """Affiche la liste des commandes disponibles avec les nouvelles difficultés""" + """Affiche la liste des commandes disponibles avec les nouvelles difficultés""" if ctx.channel.id != AUTHORIZED_CHANNEL_ID: return embed = discord.Embed( - title="🤖 RTF Bot - Commands Guide", + title="🤖 RTF Bot - Commands Guide", description="Here are all available commands for tracking your Personal Bests!", color=0x00bfff ) - # Info sur les formats de dégâts + # Info sur les formats de dégâts embed.add_field( - name="💰 Damage Formats", + name="💰 Damage Formats", value="**Accepted formats:** `1500000`, `1.5M`, `500K`, `2B`\n" + "**Suffixes:** K = thousands, M = millions, B = billions\n" + "**Shortcuts:** `nm` = Nightmare, `unm` = Ultra Nightmare", @@ -29,7 +29,7 @@ class Guide(commands.Cog): # Commandes PB Hydra embed.add_field( - name="🐍 Hydra Commands", + name="🐍 Hydra Commands", value="**Difficulties:** Normal | Hard | Brutal | Nightmare (nm)\n" + "`!pbhydra ` - Submit PB + screenshot\n" + "`!pbhydra ` - Show your PB\n" + @@ -39,7 +39,7 @@ class Guide(commands.Cog): # Commandes PB Chimera embed.add_field( - name="🦁 Chimera Commands", + name="🦁 Chimera Commands", value="**Difficulties:** Easy | Normal | Hard | Brutal | Nightmare (nm) | Ultra (unm)\n" + "`!pbchimera ` - Submit PB + screenshot\n" + "`!pbchimera ` - Show your PB\n" + @@ -49,7 +49,7 @@ class Guide(commands.Cog): # Commandes PB CvC embed.add_field( - name="⚔️ CvC Commands", + name="⚔️ CvC Commands", value="`!pbcvc ` - Submit PB + screenshot\n" + "`!pbcvc` - Show your PB\n" + "`!pbcvc ` - Show user's PB", @@ -58,7 +58,7 @@ class Guide(commands.Cog): # Commandes Mercy embed.add_field( - name="🎲 Mercy Commands", + name="🎲 Mercy Commands", value="`!mercy show` - Show your current mercy pulls\n" + "`!mercy add ` - Add pulls to a shard type\n" + "`!mercy reset ` - Reset pulls for a shard type\n" + @@ -68,7 +68,7 @@ class Guide(commands.Cog): # Classements globaux embed.add_field( - name="🌍 Global Leaderboards", + name="🌍 Global Leaderboards", value="`!top10hydra ` - Global Hydra rankings\n" + "`!top10chimera ` - Global Chimera rankings\n" + "`!top10cvc` - Global CvC rankings", @@ -77,7 +77,7 @@ class Guide(commands.Cog): # Classements par clan embed.add_field( - name="🏛️ Clan Leaderboards", + name="🏛️ Clan Leaderboards", value="**RTF:** `!rtfhydra ` `!rtfchimera ` `!rtfcvc`\n" + "**RTFC:** `!rtfchydra ` `!rtfcchimera ` `!rtfccvc`\n" + "**RTFR:** `!rtfrhydra ` `!rtfrchimera ` `!rtfrcvc`", @@ -86,7 +86,7 @@ class Guide(commands.Cog): # Stats et aide embed.add_field( - name="📈 Stats & Info", + name="📈 Stats & Info", value="`!mystats` - View all your PBs\n" + "`!mystats ` - View someone's PBs\n" + "`!guide` - Show this help message", @@ -95,7 +95,7 @@ class Guide(commands.Cog): # Instructions embed.add_field( - name="💡 Examples", + name="💡 Examples", value="`!pbhydra brutal 1.5M` - Submit Brutal Hydra PB\n" + "`!pbchimera unm 500K` - Submit Ultra Nightmare PB\n" + "`!pbcvc 2.3M` - Submit CvC PB\n" + @@ -106,7 +106,7 @@ class Guide(commands.Cog): inline=False ) - embed.set_footer(text="🎮 Old screenshots are automatically deleted when you set new PBs!") + embed.set_footer(text="🎮 Old screenshots are automatically deleted when you set new PBs!") await ctx.send(embed=embed) diff --git a/cogs/mercy.py b/cogs/mercy.py index 6f5abe1..aadb857 100644 --- a/cogs/mercy.py +++ b/cogs/mercy.py @@ -21,10 +21,10 @@ class Mercy(commands.Cog): if action == "show": pulls_dict = self.mercy_manager.get_all_pulls(user_id) if not pulls_dict: - await ctx.send("❌ You don't have any mercy data yet.") + await ctx.send("❌ You don't have any mercy data yet.") return - embed = discord.Embed(title=f"🎲 Mercy Status for {ctx.author.display_name}", color=0x00bfff) + embed = discord.Embed(title=f"🎲 Mercy Status for {ctx.author.display_name}", color=0x00bfff) for shard_type, pulls in pulls_dict.items(): if shard_type == "primal": @@ -33,7 +33,7 @@ class Mercy(commands.Cog): guaranteed_text = f" (Guaranteed at {int(guaranteed_at)} pulls)" if guaranteed_at else "" embed.add_field( name=sub_type.replace("_", " ").title(), - value=f"Pulled: **{pulls} shards** → {chance:.1f}% chance{guaranteed_text}", + value=f"Pulled: **{pulls} shards** → {chance:.1f}% chance{guaranteed_text}", inline=False ) else: @@ -41,7 +41,7 @@ class Mercy(commands.Cog): guaranteed_text = f" (Guaranteed at {int(guaranteed_at)} pulls)" if guaranteed_at else "" embed.add_field( name=shard_type.replace("_", " ").title(), - value=f"Pulled: **{pulls} shards** → {chance:.1f}% chance{guaranteed_text}", + value=f"Pulled: **{pulls} shards** → {chance:.1f}% chance{guaranteed_text}", inline=False ) @@ -51,28 +51,28 @@ class Mercy(commands.Cog): try: pulls_to_add = int(arg1) except ValueError: - await ctx.send("❌ Number of pulls must be an integer.") + await ctx.send("❌ Number of pulls must be an integer.") return shard_type = arg2.lower() if shard_type not in VALID_SHARDS: - await ctx.send(f"❌ Invalid shard type. Available: {', '.join(VALID_SHARDS)}") + await ctx.send(f"❌ Invalid shard type. Available: {', '.join(VALID_SHARDS)}") return new_pulls = self.mercy_manager.add_pulls(user_id, shard_type, pulls_to_add) - await ctx.send(f"✅ Added {pulls_to_add} pulls to **{shard_type}** mercy. Total: {new_pulls}") + await ctx.send(f"✅ Added {pulls_to_add} pulls to **{shard_type}** mercy. Total: {new_pulls}") elif action == "reset" and arg1: shard_type = arg1.lower() if shard_type not in VALID_SHARDS: - await ctx.send(f"❌ Invalid shard type. Available: {', '.join(VALID_SHARDS)}") + await ctx.send(f"❌ Invalid shard type. Available: {', '.join(VALID_SHARDS)}") return self.mercy_manager.reset_pulls(user_id, shard_type) - await ctx.send(f"🔄 Mercy for **{shard_type}** has been reset.") + await ctx.send(f"🔄 Mercy for **{shard_type}** has been reset.") else: - await ctx.send("❌ Usage: `!mercy add `, `!mercy reset `, `!mercy show`") + await ctx.send("❌ Usage: `!mercy add `, `!mercy reset `, `!mercy show`") async def setup(bot): await bot.add_cog(Mercy(bot)) diff --git a/cogs/mystats.py b/cogs/mystats.py index 2f142e2..9e25528 100644 --- a/cogs/mystats.py +++ b/cogs/mystats.py @@ -2,7 +2,7 @@ import discord from discord.ext import commands from config import AUTHORIZED_CHANNEL_ID, BOSS_CONFIG from utils.helpers import format_damage_display, format_date_only -from utils.pb_handler import db_manager # ou set_db_manager si nécessaire +from utils.pb_handler import db_manager # ou set_db_manager si nécessaire class MyStats(commands.Cog): def __init__(self, bot): @@ -10,7 +10,7 @@ class MyStats(commands.Cog): @commands.command(name="mystats") async def mystats(self, ctx, target_user: str = None): - """Affiche tous les PB d'un utilisateur avec les nouvelles difficultés""" + """Affiche tous les PB d'un utilisateur avec les nouvelles difficultés""" if ctx.channel.id != AUTHORIZED_CHANNEL_ID: return @@ -19,15 +19,15 @@ class MyStats(commands.Cog): user_data = db_manager.get_user_all_pbs(username) if not user_data: - await ctx.send(f"❌ No data found for **{username}**.") + await ctx.send(f"❌ No data found for **{username}**.") return embed = discord.Embed( - title=f"📊 {username}'s Complete Stats", + title=f"📊 {username}'s Complete Stats", color=0x00bfff ) - # Hydra - toutes les difficultés + # Hydra - toutes les difficultés hydra_stats = [] for difficulty in BOSS_CONFIG['hydra']['difficulties']: pb_key = f'pb_hydra_{difficulty}' @@ -36,13 +36,13 @@ class MyStats(commands.Cog): 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 "" + date_text = f" • {format_date_only(pb_date)}" if pb_date else "" hydra_stats.append(f"**{difficulty.title()}:** {format_damage_display(pb_value)}{date_text}") hydra_text = "\n".join(hydra_stats) if hydra_stats else "No records" - embed.add_field(name="🐍 Hydra PBs", value=hydra_text, inline=False) + embed.add_field(name="🐍 Hydra PBs", value=hydra_text, inline=False) - # Chimera - toutes les difficultés + # Chimera - toutes les difficultés chimera_stats = [] for difficulty in BOSS_CONFIG['chimera']['difficulties']: pb_key = f'pb_chimera_{difficulty}' @@ -51,12 +51,12 @@ class MyStats(commands.Cog): 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 "" + 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}:** {format_damage_display(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) + embed.add_field(name="🦁 Chimera PBs", value=chimera_text, inline=False) # CvC cvc_pb = user_data.get('pb_cvc', 0) @@ -65,10 +65,10 @@ class MyStats(commands.Cog): if cvc_pb > 0 and cvc_date: formatted_date = format_date_only(cvc_date) if formatted_date: - cvc_text += f" • {formatted_date}" - embed.add_field(name="⚔️ CvC PB", value=cvc_text, inline=False) + cvc_text += f" • {formatted_date}" + embed.add_field(name="⚔️ CvC PB", value=cvc_text, inline=False) - # Total combiné + # Total combiné total_damage = 0 for difficulty in BOSS_CONFIG['hydra']['difficulties']: total_damage += user_data.get(f'pb_hydra_{difficulty}', 0) @@ -76,12 +76,12 @@ class MyStats(commands.Cog): 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"**{format_damage_display(total_damage)}**", inline=False) + embed.add_field(name="💯 Total Combined Damage", value=f"**{format_damage_display(total_damage)}**", inline=False) await ctx.send(embed=embed) except Exception as e: - await ctx.send(f"❌ Error: {e}") + await ctx.send(f"❌ Error: {e}") # Pour charger le Cog diff --git a/docker-compose.yml b/docker-compose.yml index 45ab03d..b5188f0 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -8,11 +8,11 @@ services: - .env volumes: # Montre le code source pour hot-reload - - ./:/app-dev + - ./:/app # Montre les dossiers de stockage persistants - - ./screenshots:/app-dev/screenshots - - ./bot_data.db:/app-dev/bot_data.db - - ./logs:/app-dev/logs + - ./screenshots:/app/screenshots + - ./bot_data.db:/app/bot_data.db + - ./logs:/app/logs environment: - TZ=Europe/Paris container_name: rtf-discord-bot diff --git a/utils/leaderboard_handler.py b/utils/leaderboard_handler.py index d99ac8b..6bc5ce0 100644 --- a/utils/leaderboard_handler.py +++ b/utils/leaderboard_handler.py @@ -11,17 +11,17 @@ def set_db_manager(db): db_manager = db async def show_leaderboard(ctx, boss_type, difficulty=None, clan=None): - """Fonction générique pour afficher les classements""" + """Fonction générique pour afficher les classements""" if ctx.channel.id != AUTHORIZED_CHANNEL_ID: return try: - # Normaliser la difficulté si spécifiée + # Normaliser la difficulté si spécifiée if difficulty: difficulty = normalize_difficulty(difficulty) if difficulty not in BOSS_CONFIG[boss_type]['difficulties']: difficulties = " | ".join(BOSS_CONFIG[boss_type]['difficulties']) - await ctx.send(f"❌ Invalid difficulty. Available: {difficulties}") + await ctx.send(f"❌ Invalid difficulty. Available: {difficulties}") return boss_info = BOSS_CONFIG[boss_type] @@ -30,15 +30,15 @@ async def show_leaderboard(ctx, boss_type, difficulty=None, clan=None): if not leaderboard: clan_text = f" for clan {clan}" if clan else "" 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!") + await ctx.send(f"❌ No{difficulty_text} {boss_info['name']} records found{clan_text} yet!") return - # Titre avec clan et difficulté si spécifiés + # 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" + title = f"🏆 {difficulty_name} {boss_info['name']} Leaderboard - Top 10" if clan: - clan_info = CLAN_CONFIG.get(clan, {'name': clan, 'emoji': '🏛️'}) + clan_info = CLAN_CONFIG.get(clan, {'name': clan, 'emoji': '🏛️'}) title = f"{clan_info['emoji']} {clan_info['name']} - {difficulty_name} {boss_info['name']} Top 10" embed = discord.Embed( @@ -46,21 +46,21 @@ async def show_leaderboard(ctx, boss_type, difficulty=None, clan=None): color=boss_info['color'] if not clan else CLAN_CONFIG.get(clan, {'color': boss_info['color']})['color'] ) - medals = ["🥇", "🥈", "🥉"] + ["🏅"] * 7 + medals = ["🥇", "🥈", "🥉"] + ["🏅"] * 7 for i, (username, damage, date) in enumerate(leaderboard): date_text = "" if date: formatted_date = format_date_only(date) if formatted_date: - date_text = f" • {formatted_date}" + date_text = f" • {formatted_date}" # Afficher le clan dans le nom si pas de filtre par clan display_name = username if not clan: user_clan = get_user_clan(username) if user_clan: - clan_emoji = CLAN_CONFIG.get(user_clan, {'emoji': '🏛️'})['emoji'] + clan_emoji = CLAN_CONFIG.get(user_clan, {'emoji': '🏛️'})['emoji'] display_name = f"{clan_emoji} {username}" embed.add_field( @@ -72,4 +72,4 @@ async def show_leaderboard(ctx, boss_type, difficulty=None, clan=None): await ctx.send(embed=embed) except Exception as e: - await ctx.send(f"❌ Error: {e}") \ No newline at end of file + await ctx.send(f"❌ Error: {e}") \ No newline at end of file diff --git a/utils/pb_handler.py b/utils/pb_handler.py index 328dfa3..0a398a8 100644 --- a/utils/pb_handler.py +++ b/utils/pb_handler.py @@ -13,13 +13,13 @@ db_manager = None screenshot_manager = None def set_managers(db, ss): - """Injection des managers (appelée une seule fois depuis bot.py)""" + """Injection des managers (appelée une seule fois depuis bot.py)""" global db_manager, screenshot_manager db_manager = db screenshot_manager = ss 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""" + """Fonction générique pour gérer toutes les commandes PB avec difficultés""" if ctx.channel.id != AUTHORIZED_CHANNEL_ID: return @@ -27,7 +27,7 @@ async def handle_pb_command(ctx, boss_type, arg1=None, arg2=None): difficulties = boss_info['difficulties'] try: - # Pour CvC (pas de difficultés) + # Pour CvC (pas de difficultés) if not difficulties: # Utiliser l'ancienne logique pour CvC avec parsing des montants if arg1: @@ -40,12 +40,12 @@ async def handle_pb_command(ctx, boss_type, arg1=None, arg2=None): await show_user_pb(ctx, boss_type, None, ctx.author.display_name) return - # Pour Hydra et Chimera (avec difficultés) + # 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"❌ Please specify difficulty and damage!\n" f"**Available difficulties:** {difficulty_list}\n" f"**Shortcuts:** `nm` = Nightmare, `unm` = Ultra Nightmare\n" f"**Examples:**\n" @@ -56,10 +56,10 @@ async def handle_pb_command(ctx, boss_type, arg1=None, arg2=None): ) return - # Normaliser la difficulté (gérer les diminutifs) + # Normaliser la difficulté (gérer les diminutifs) normalized_difficulty = normalize_difficulty(arg1) - # Vérifier si arg1 est une difficulté valide + # Vérifier si arg1 est une difficulté valide if normalized_difficulty in difficulties: difficulty = normalized_difficulty @@ -75,26 +75,26 @@ async def handle_pb_command(ctx, boss_type, arg1=None, arg2=None): # !pbhydra normal - Voir son propre PB await show_user_pb(ctx, boss_type, difficulty, ctx.author.display_name) else: - # arg1 n'est pas une difficulté valide + # 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"❌ Invalid difficulty: `{arg1}`\n" f"**Available difficulties:** {difficulty_list}\n" f"**Shortcuts:** `nm` = Nightmare, `unm` = Ultra Nightmare" ) except Exception as e: - await ctx.send(f"❌ Error: {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""" + """Gère la soumission d'un nouveau PB""" if not ctx.message.attachments: - await ctx.send("❌ Please attach a screenshot to validate your PB!") + 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!") + await ctx.send("❌ Please attach a valid image file!") return username = ctx.author.display_name @@ -107,7 +107,7 @@ async def handle_pb_submission(ctx, boss_type, difficulty, damage): ) if screenshot_filename: - # Mettre à jour la base et récupérer l'ancien screenshot + # 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 ) @@ -121,20 +121,20 @@ async def handle_pb_submission(ctx, boss_type, difficulty, damage): difficulty_name = get_difficulty_display_name(difficulty) if difficulty else "" embed = discord.Embed( - title=f"🎉 NEW {boss_info['name'].upper()} PB! 🎉", + title=f"🎉 NEW {boss_info['name'].upper()} PB! 🎉", description=f"**{username}** just hit **{format_damage_display(damage)} damage** on {difficulty_name} {boss_info['name']}!", color=0x00ff00 ) - embed.add_field(name="📈 Improvement", value=f"+{format_damage_display(improvement)} damage", inline=True) + embed.add_field(name="📈 Improvement", value=f"+{format_damage_display(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.") + 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!", + title="💪 Nice attempt!", description=f"Your damage: **{format_damage_display(damage)}**\nCurrent PB: **{format_damage_display(current_pb)}**", color=0xffa500 ) @@ -161,7 +161,7 @@ async def show_user_pb(ctx, boss_type, difficulty, username): color=0x666666 ) embed.add_field( - name="💡 Get started!", + name="💡 Get started!", value=f"Use `!pb{boss_type} {difficulty} ` with a screenshot to set your first record!\nAccepts K/M/B suffixes: `1.5M`, `500K`, etc.", inline=False ) @@ -176,7 +176,7 @@ async def show_user_pb(ctx, boss_type, difficulty, username): if pb_date: formatted_date = format_datetime(pb_date) if formatted_date: - embed.add_field(name="📅 Record Date", value=formatted_date, inline=False) + embed.add_field(name="📅 Record Date", value=formatted_date, inline=False) # Envoyer la screenshot si elle existe if screenshot_filename: