diff --git a/commandes/PENDING_TASKS.md b/commandes/PENDING_TASKS.md new file mode 100644 index 0000000..0d14d57 --- /dev/null +++ b/commandes/PENDING_TASKS.md @@ -0,0 +1,19 @@ +# À RÉGLER - Session Suivante (Ticket Whitelist) + +### Bug Identifié +La commande **"validé"** par message (on_message) n'est pas détectée ou ne réagit pas si le ticket a été refermé puis réouvert avec le statut "Ouvert". + +### État Actuel +- Le bouton "Fermer" fonctionne et détecte l'ID via Regex. +- Le bouton "Réouvrir" synchronise maintenant les rôles (Écrit/Oral/Accepté). +- Le design des MPs (Orange/Vert) est restauré conforme aux captures. +- **Reste** : Vérifier pourquoi `on_message` ignore parfois le message "validé" (probablement un problème de sujet (topic) ou de permissions au moment de la lecture). + +### Prochaine Étape (À faire lors de la reprise) +1. **Ticket Ouvert/Réouvert** : Le bot est actuellement en ligne avec des messages de debug (❌). +2. **Test "validé"** : Taper "validé" dans un ticket (neuf ou réouvert). +3. **Analyser les retours** : + - `❌ Debug: message.channel.topic est vide ou None.` (Cache d.py obsolète ?) + - `❌ Debug: Topic trouvé mais pas d'ID matché.` (Format de topic invalide ?) + - `❌ Debug: Permission refusée.` (Rôle Staff ou Permissions Admin non reconnus ?) +4. **Action** : Une fois la cause identifiée, implémenter le "fetch_channel" ou une méthode alternative de récupération de l'owner. diff --git a/commandes/bug_report.py b/commandes/bug_report.py index 5d0d7b5..729a197 100644 --- a/commandes/bug_report.py +++ b/commandes/bug_report.py @@ -9,10 +9,12 @@ import os import asyncio kuby_logger = logging.getLogger("KubyBot") -REPORTS_FILE = "data/gitlab_reports.json" +# Utilisation de chemins absolus pour la persistance après reboot +BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) +REPORTS_FILE = os.path.join(BASE_DIR, "data", "gitlab_reports.json") def save_report(issue_iid, user_id): - os.makedirs("data", exist_ok=True) + os.makedirs(os.path.dirname(REPORTS_FILE), exist_ok=True) try: if os.path.exists(REPORTS_FILE): with open(REPORTS_FILE, "r") as f: @@ -116,16 +118,59 @@ class BugReport(commands.Cog): self.web_app.router.add_post('/bot_event', self.handle_bot_event) self.runner = web.AppRunner(self.web_app) await self.runner.setup() - self.site = web.TCPSite(self.runner, '127.0.0.1', 5001) - await self.site.start() - kuby_logger.info("Internal Bot Webhook Server started on 127.0.0.1:5001") + + max_retries = 5 + retry_delay = 2 + + for attempt in range(1, max_retries + 1): + try: + self.site = web.TCPSite(self.runner, '127.0.0.1', 5001) + await self.site.start() + kuby_logger.info(f"Internal Bot Webhook Server started on 127.0.0.1:5001 (Attempt {attempt})") + return + except OSError as e: + if e.errno == 98: # Address already in use + if attempt < max_retries: + kuby_logger.warning(f"Port 5001 already in use, retrying in {retry_delay}s... ({attempt}/{max_retries})") + await asyncio.sleep(retry_delay) + else: + kuby_logger.error(f"Failed to bind to port 5001 after {max_retries} attempts.") + raise e + else: + raise e def cog_unload(self): if hasattr(self, 'runner'): - self.bot.loop.create_task(self.runner.cleanup()) + # On utilise create_task car cog_unload est synchrone + async def cleanup(): + try: + if hasattr(self, 'site'): + await self.site.stop() + await self.runner.cleanup() + kuby_logger.info("Internal Bot Webhook Server stopped and cleaned up.") + except Exception as e: + kuby_logger.error(f"Error during webhook server cleanup: {e}") + + self.bot.loop.create_task(cleanup()) async def handle_bot_event(self, request): + # Vérification de la sécurité (Secret Token GitLab) + webhook_secret = os.getenv("GITLAB_WEBHOOK_SECRET") + if webhook_secret: + provided_token = request.headers.get("X-Gitlab-Token") + if provided_token != webhook_secret: + kuby_logger.warning("Unauthorised webhook attempt detected (invalid X-Gitlab-Token).") + return web.json_response({"status": "unauthorised"}, status=401) + try: + # On attend que le bot soit prêt si on vient de rebooter + if not self.bot.is_ready(): + kuby_logger.info("Webhook received but bot is not ready yet. Waiting up to 10s...") + try: + await asyncio.wait_for(self.bot.wait_until_ready(), timeout=10.0) + except asyncio.TimeoutError: + kuby_logger.warning("Bot still not ready after 10s. Proceeding anyway (best effort).") + payload = await request.json() event_type = payload.get("object_kind") @@ -187,13 +232,22 @@ class BugReport(commands.Cog): color=discord.Color.blue() ) embed.add_field(name="Nouveaux Labels / Statuts", value=current_labels_str) - await user.send(embed=embed) - if dev: - await dev.send(f"✅ [FIABLE] L'utilisateur {user} ({user.id}) a bien reçu la notification de statut **{current_labels_str}** pour l'issue #{issue_iid} ({issue_title}).") + try: + await user.send(embed=embed) + if dev and dev.id != user.id: + await dev.send(f"✅ [FIABLE] L'utilisateur {user} ({user.id}) a bien reçu la notification de statut **{current_labels_str}** pour l'issue #{issue_iid} ({issue_title}).") + except discord.Forbidden: + kuby_logger.warning(f"Unable to DM user {user} ({user.id}) - DMs are disabled or bot is blocked.") + if dev and dev.id != user.id: + try: + await dev.send(f"⚠️ [DM BLOQUÉ] Impossible de notifier l'utilisateur {user} ({user.id}) pour l'issue #{issue_iid} (DMs désactivés).") + except discord.Forbidden: + pass + except discord.HTTPException as e: + kuby_logger.error(f"HTTP error sending DM to {user}: {e}") except Exception as e: - if dev: - await dev.send(f"❌ [ERREUR] Impossible de notifier l'utilisateur {user} pour l'issue #{issue_iid} : {e}") + kuby_logger.error(f"Unexpected error in label update notification: {e}") # Check if it was closed if action == "close" or payload.get("object_attributes", {}).get("state") == "closed": @@ -205,14 +259,22 @@ class BugReport(commands.Cog): ) embed.add_field(name="Prochaine étape", value="La mise à jour arrive prochainement (ou est déjà là) !") embed.set_footer(text="Merci pour votre aide !") - await user.send(embed=embed) - - if dev: - await dev.send(f"✅ [FIABLE] L'utilisateur {user} ({user.id}) a bien reçu la notification de CLÔTURE pour l'issue #{issue_iid}.") + try: + await user.send(embed=embed) + if dev and dev.id != user.id: + await dev.send(f"✅ [FIABLE] L'utilisateur {user} ({user.id}) a bien reçu la notification de CLÔTURE pour l'issue #{issue_iid}.") + except discord.Forbidden: + kuby_logger.warning(f"Unable to DM user {user} ({user.id}) - DMs are disabled or bot is blocked during closure.") + if dev and dev.id != user.id: + try: + await dev.send(f"⚠️ [DM BLOQUÉ] Impossible d'annoncer la clôture à {user} ({user.id}) pour l'issue #{issue_iid} (DMs désactivés).") + except discord.Forbidden: + pass + except discord.HTTPException as e: + kuby_logger.error(f"HTTP error sending closure DM to {user}: {e}") except Exception as e: - if dev: - await dev.send(f"❌ [ERREUR] Impossible d'annoncer la clôture à {user} pour l'issue #{issue_iid} : {e}") + kuby_logger.error(f"Unexpected error in closure notification: {e}") elif event_type == "note": note_attr = payload.get("object_attributes", {}) @@ -229,14 +291,22 @@ class BugReport(commands.Cog): color=discord.Color.orange() ) embed.set_footer(text=f"Par {author_name}") - await user.send(embed=embed) - - if dev: - await dev.send(f"✅ [FIABLE] L'utilisateur {user} ({user.id}) a bien reçu votre commentaire pour l'issue #{issue_iid}.") - + + try: + await user.send(embed=embed) + if dev and dev.id != user.id: + await dev.send(f"✅ [FIABLE] L'utilisateur {user} ({user.id}) a bien reçu votre commentaire pour l'issue #{issue_iid}.") + except discord.Forbidden: + kuby_logger.warning(f"Unable to DM user {user} ({user.id}) - DMs are disabled or bot is blocked during comment notification.") + if dev and dev.id != user.id: + try: + await dev.send(f"⚠️ [DM BLOQUÉ] Impossible d'envoyer votre commentaire à {user} ({user.id}) pour l'issue #{issue_iid} (DMs désactivés).") + except discord.Forbidden: + pass + except discord.HTTPException as e: + kuby_logger.error(f"HTTP error sending comment DM to {user}: {e}") except Exception as e: - if dev: - await dev.send(f"❌ [ERREUR] Impossible d'envoyer votre commentaire à {user} pour l'issue #{issue_iid} : {e}") + kuby_logger.error(f"Unexpected error in comment notification: {e}") return web.json_response({"status": "success"}, status=200) diff --git a/commandes/security.py b/commandes/security.py index 6305efa..5a461a0 100755 --- a/commandes/security.py +++ b/commandes/security.py @@ -960,53 +960,49 @@ class Security(commands.Cog): Commande principale pour ouvrir l'interface de sécurité. Accessible uniquement aux administrateurs et propriétaires whitelistés. """ + # On diffère la réponse immédiatement car le chargement des réglages peut prendre du temps + await interaction.response.defer(ephemeral=True) + # Vérification des permissions if not is_immune(interaction.guild, interaction.user): - if interaction.response.is_done(): - return await interaction.followup.send( - "❌ **Accès refusé**\n\nVous devez être administrateur, propriétaire du serveur, ou être dans la whitelist pour accéder à ce panneau.", - ephemeral=True - ) - return await interaction.response.send_message( + return await interaction.followup.send( "❌ **Accès refusé**\n\nVous devez être administrateur, propriétaire du serveur, ou être dans la whitelist pour accéder à ce panneau.", ephemeral=True ) - # Chargement des paramètres - settings = load_settings(interaction.guild.id) + try: + # Chargement des paramètres + settings = load_settings(interaction.guild.id) - # Création de la vue - view = SecurityView(interaction, settings, bot=self.bot) - embed = view.create_main_embed(interaction.user) + # Création de la vue + view = SecurityView(interaction, settings, bot=self.bot) + embed = view.create_main_embed(interaction.user) - if interaction.response.is_done(): await interaction.followup.send( embed=embed, view=view, ephemeral=True ) - else: - await interaction.response.send_message( - embed=embed, - view=view, + except Exception as e: + await interaction.followup.send( + f"❌ Une erreur est survenue lors de l'ouverture du panneau : {e}", ephemeral=True ) @security.error async def security_error(self, interaction: discord.Interaction, error: app_commands.AppCommandError): """Gestion des erreurs de la commande security""" - # Ne pas envoyer de message si l'interaction a déjà été traitée - if interaction.response.is_done(): - return + # Utilisation de followup si l'interaction est déjà différée ou répondue + send_method = interaction.followup.send if interaction.response.is_done() else interaction.response.send_message if isinstance(error, app_commands.MissingPermissions): - await interaction.response.send_message( + await send_method( "❌ Permissions insuffisantes.", ephemeral=True ) else: - await interaction.response.send_message( - f"❌ Une erreur est survenue.", + await send_method( + f"❌ Une erreur est survenue lors de l'exécution de la commande : {error}", ephemeral=True ) diff --git a/commandes/ticket/__init__.py b/commandes/ticket/__init__.py index f30db60..b92e829 100644 --- a/commandes/ticket/__init__.py +++ b/commandes/ticket/__init__.py @@ -1707,14 +1707,14 @@ class ConfigPanelView(discord.ui.View): self.category_btn.callback = self._category_callback self.add_item(self.category_btn) - # Ajouter catégorie - self.add_cat_btn = discord.ui.Button( + # Gérer les catégories + self.manage_cat_btn = discord.ui.Button( style=discord.ButtonStyle.blurple, - label="Ajouter Catégorie", - custom_id="config_add_cat" + label="📂 Gérer les catégories", + custom_id="config_manage_cat" ) - self.add_cat_btn.callback = self._add_cat_callback - self.add_item(self.add_cat_btn) + self.manage_cat_btn.callback = self._manage_cat_callback + self.add_item(self.manage_cat_btn) # Staff permissions staff_btn = discord.ui.Button( @@ -1756,9 +1756,9 @@ class ConfigPanelView(discord.ui.View): item.style = discord.ButtonStyle.green if self.config.enabled else discord.ButtonStyle.red await interaction.message.edit(view=self) - async def _add_cat_callback(self, interaction: discord.Interaction): - modal = AddCategoryModal(self.bot, self.storage, self.config) - await interaction.response.send_modal(modal) + async def _manage_cat_callback(self, interaction: discord.Interaction): + view = CategoryManagerView(self.bot, self.storage, self.config) + await interaction.response.edit_message(embed=view._get_embed(), view=view) async def _staff_perms_callback(self, interaction: discord.Interaction): view = PermissionConfigView(self.bot, self.storage, self.config, interaction.guild, "staff") @@ -1849,6 +1849,251 @@ class AddCategoryModal(discord.ui.Modal): await interaction.response.send_message(f"Catégorie **{self.name.value}** créée!", ephemeral=True) +class EditCategoryModal(discord.ui.Modal): + """Modal pour modifier une catégorie existante""" + + def __init__(self, bot, storage, config, category: TicketCategory, parent_view): + super().__init__(title=f"Modifier: {category.name}") + self.bot = bot + self.storage = storage + self.config = config + self.category = category + self.parent_view = parent_view + + self.name = discord.ui.TextInput( + label="Nom", + default=category.name, + placeholder="Ex: Support", + required=True + ) + self.add_item(self.name) + + self.description = discord.ui.TextInput( + label="Description", + default=category.description, + placeholder="Description...", + required=False, + style=discord.TextStyle.paragraph + ) + self.add_item(self.description) + + self.emoji = discord.ui.TextInput( + label="Emoji", + default=category.emoji, + placeholder="🎫", + required=False, + max_length=5 + ) + self.add_item(self.emoji) + + async def on_submit(self, interaction: discord.Interaction): + # Update category object + self.category.name = self.name.value + self.category.description = self.description.value or "" + self.category.emoji = self.emoji.value or "🎫" + + # Save config + self.storage.save_config(interaction.guild_id, self.config) + + # Refresh the parent view's message + await interaction.response.edit_message(embed=self.parent_view._get_embed(), view=self.parent_view) + await interaction.followup.send(f"✅ Catégorie **{self.category.name}** mise à jour !", ephemeral=True) + + +class CategoryManagerView(discord.ui.View): + """Vue pour lister et gérer les catégories""" + + def __init__(self, bot, storage, config: GuildTicketConfig): + super().__init__(timeout=None) + self.bot = bot + self.storage = storage + self.config = config + self._add_category_select() + + def _add_category_select(self): + self.clear_items() + + if self.config.categories: + options = [] + for cat in self.config.categories[:25]: # Limite Select menu + options.append(discord.SelectOption( + label=cat.name, + value=cat.id, + description=cat.description[:100] if cat.description else "Aucune description", + emoji=cat.emoji + )) + + select = discord.ui.Select( + placeholder="Sélectionnez une catégorie à gérer...", + options=options, + custom_id="manage_cat_select" + ) + select.callback = self._category_select_callback + self.add_item(select) + + # Bouton Ajouter + add_btn = discord.ui.Button( + style=discord.ButtonStyle.success, + label="➕ Ajouter une catégorie", + custom_id="manage_cat_add" + ) + add_btn.callback = self._add_cat_callback + self.add_item(add_btn) + + # Bouton Retour + back_btn = discord.ui.Button( + style=discord.ButtonStyle.secondary, + label="⬅️ Retour", + custom_id="manage_cat_back" + ) + back_btn.callback = self._back_callback + self.add_item(back_btn) + + def _get_embed(self): + embed = discord.Embed( + title="📂 Gestion des Catégories", + description=f"Il y a actuellement **{len(self.config.categories)}** catégorie(s) configurée(s).\n\n" + "Sélectionnez une catégorie dans le menu pour la modifier ou la supprimer.", + color=discord.Color.blue() + ) + + if not self.config.categories: + embed.description += "\n\n⚠️ **Aucune catégorie configurée.** Commencez par en ajouter une !" + + for cat in self.config.categories[:10]: + embed.add_field( + name=f"{cat.emoji} {cat.name}", + value=cat.description or "Pas de description", + inline=False + ) + + return embed + + async def _category_select_callback(self, interaction: discord.Interaction): + # En utilisant interaction.data.get('values') pour obtenir la valeur sélectionnée + cat_id = interaction.data.get('values', [None])[0] + category = self.config.get_category(cat_id) + + if not category: + await interaction.response.send_message("Catégorie introuvable.", ephemeral=True) + return + + view = CategoryActionView(self.bot, self.storage, self.config, category, self) + await interaction.response.edit_message(embed=view._get_embed(), view=view) + + async def _add_cat_callback(self, interaction: discord.Interaction): + # Utilisation de la classe AddCategoryModal existante + class AddCategoryManagerModal(AddCategoryModal): + def __init__(self, bot, storage, config, parent_view): + super().__init__(bot, storage, config) + self.parent_view = parent_view + + async def on_submit(self, interaction: discord.Interaction): + import uuid + cat_id = str(uuid.uuid4())[:8] + category = TicketCategory( + id=cat_id, + name=self.name.value, + description=self.description.value or "", + emoji=self.emoji.value or "🎫" + ) + self.config.categories.append(category) + self.storage.save_config(interaction.guild_id, self.config) + + # Refresh parent view + self.parent_view._add_category_select() + # On doit utiliser edit_message sur l'interaction d'origine ou renvoyer un embed + await interaction.response.edit_message( + embed=self.parent_view._get_embed(), + view=self.parent_view + ) + await interaction.followup.send(f"Catégorie **{self.name.value}** créée!", ephemeral=True) + + modal = AddCategoryManagerModal(self.bot, self.storage, self.config, self) + await interaction.response.send_modal(modal) + + async def _back_callback(self, interaction: discord.Interaction): + view = ConfigPanelView(self.bot, self.storage, self.config) + embed = discord.Embed( + title="Configuration des Tickets", + description="Gérez la configuration du système de tickets", + color=discord.Color.blue() + ) + await interaction.response.edit_message(embed=embed, view=view) + + +class CategoryActionView(discord.ui.View): + """Actions pour une catégorie spécifique""" + + def __init__(self, bot, storage, config, category, parent_view): + super().__init__(timeout=None) + self.bot = bot + self.storage = storage + self.config = config + self.category = category + self.parent_view = parent_view + + def _get_embed(self): + embed = discord.Embed( + title=f"Gestion: {self.category.name}", + description=f"**ID:** `{self.category.id}`\n" + f"**Emoji:** {self.category.emoji}\n" + f"**Description:** {self.category.description or 'Aucune'}\n" + f"**Couleur:** `{self.category.color}`", + color=discord.Color.blue() + ) + return embed + + @discord.ui.button(label="📝 Modifier", style=discord.ButtonStyle.primary) + async def edit_callback(self, interaction: discord.Interaction, button: discord.ui.Button): + modal = EditCategoryModal(self.bot, self.storage, self.config, self.category, self) + await interaction.response.send_modal(modal) + + @discord.ui.button(label="🗑️ Supprimer", style=discord.ButtonStyle.danger) + async def delete_callback(self, interaction: discord.Interaction, button: discord.ui.Button): + view = DeleteCategoryConfirmationView(self.bot, self.storage, self.config, self.category, self.parent_view) + embed = discord.Embed( + title="⚠️ Confirmer la suppression", + description=f"Êtes-vous sûr de vouloir supprimer la catégorie **{self.category.name}** ?\n" + "Cette action est irréversible.", + color=discord.Color.red() + ) + await interaction.response.edit_message(embed=embed, view=view) + + @discord.ui.button(label="⬅️ Retour", style=discord.ButtonStyle.secondary) + async def back_callback(self, interaction: discord.Interaction, button: discord.ui.Button): + self.parent_view._add_category_select() + await interaction.response.edit_message(embed=self.parent_view._get_embed(), view=self.parent_view) + + +class DeleteCategoryConfirmationView(discord.ui.View): + def __init__(self, bot, storage, config, category, parent_view): + super().__init__(timeout=None) + self.bot = bot + self.storage = storage + self.config = config + self.category = category + self.parent_view = parent_view + + @discord.ui.button(label="✅ Confirmer", style=discord.ButtonStyle.danger) + async def confirm_callback(self, interaction: discord.Interaction, button: discord.ui.Button): + if self.category in self.config.categories: + self.config.categories.remove(self.category) + self.storage.save_config(interaction.guild_id, self.config) + + self.parent_view._add_category_select() + await interaction.response.edit_message( + embed=self.parent_view._get_embed(), + view=self.parent_view + ) + await interaction.followup.send(f"✅ Catégorie **{self.category.name}** supprimée.", ephemeral=True) + + @discord.ui.button(label="❌ Annuler", style=discord.ButtonStyle.secondary) + async def cancel_callback(self, interaction: discord.Interaction, button: discord.ui.Button): + view = CategoryActionView(self.bot, self.storage, self.config, self.category, self.parent_view) + await interaction.response.edit_message(embed=view._get_embed(), view=view) + + class SetChannelModal(discord.ui.Modal): """Modal pour définir le canal de logs""" diff --git a/commandes/ticket_whitelist.py b/commandes/ticket_whitelist.py index c730e3d..4cb37ff 100644 --- a/commandes/ticket_whitelist.py +++ b/commandes/ticket_whitelist.py @@ -1,9 +1,40 @@ import discord +import re from discord.ext import commands from discord import app_commands, ui import json import os +import asyncio from typing import Optional +from src.logger import kuby_logger +import time + +# Dictionnaire global pour suivre les renommages de salons +# Format: {channel_id: [timestamp1, timestamp2, ...]} +channel_rename_timestamps = {} + +def check_channel_rename_rate_limit(channel_id: int) -> int: + """Vérifie si le renommage va atteindre la limite de Discord (2 edits / 10 min).""" + now = time.time() + if channel_id not in channel_rename_timestamps: + channel_rename_timestamps[channel_id] = [] + + timestamps = [ts for ts in channel_rename_timestamps[channel_id] if now - ts < 600] + channel_rename_timestamps[channel_id] = timestamps + + if len(timestamps) >= 2: + return max(0, int(600 - (now - timestamps[0]))) + return 0 + +def log_channel_rename(channel_id: int): + """Enregistre un renommage de salon.""" + now = time.time() + if channel_id not in channel_rename_timestamps: + channel_rename_timestamps[channel_id] = [] + + timestamps = [ts for ts in channel_rename_timestamps[channel_id] if now - ts < 600] + timestamps.append(now) + channel_rename_timestamps[channel_id] = timestamps TICKET_WHITELIST_SETTINGS_FILE = "data/ticket_whitelist_settings.json" @@ -32,6 +63,31 @@ def save_settings(guild_id: int, settings: dict) -> None: with open(TICKET_WHITELIST_SETTINGS_FILE, "w", encoding='utf-8') as f: json.dump(data, f, indent=4, ensure_ascii=False) +async def get_ticket_user_id(channel: discord.TextChannel) -> Optional[int]: + """Récupère l'ID du propriétaire du ticket de manière robuste (gère le lag de cache)""" + import re + topic = getattr(channel, "topic", None) + + # Tentative d'utilisation du cache d.py (plus rapide) + if topic: + match = re.search(r"(\d{17,20})", topic) + if match: + return int(match.group(1)) + + # Si le topic est vide ou ID introuvable, on tente de forcer un refresh via l'API + try: + # On utilise fetch_channel pour avoir un objet tout neuf avec le topic à jour + refreshed_channel = await channel.guild.fetch_channel(channel.id) + topic = getattr(refreshed_channel, "topic", None) + if topic: + match = re.search(r"(\d{17,20})", topic) + if match: + return int(match.group(1)) + except: + pass + + return None + class TicketWhitelistConfigView(ui.View): def __init__(self, guild_id: int): super().__init__(timeout=60) @@ -43,12 +99,6 @@ class TicketWhitelistConfigView(ui.View): def check(m): return m.author == interaction.user and m.channel == interaction.channel - try: - # We use an explicit import here to avoid clashing if __import__ syntax is weird, but using standard asyncio module is fine if we import it at top. Let's add import asyncio at the top instead! - pass - except __import__("asyncio").TimeoutError: - pass - try: msg = await interaction.client.wait_for('message', check=check, timeout=30) target = None @@ -323,52 +373,73 @@ class TicketManagementView(discord.ui.View): def __init__(self): super().__init__(timeout=None) + async def get_ticket_owner(self, interaction: discord.Interaction): + """Récupère le propriétaire du ticket via le helper robuste""" + owner_id = await get_ticket_user_id(interaction.channel) + if owner_id: + try: + member = interaction.guild.get_member(owner_id) + if not member: + member = await interaction.guild.fetch_member(owner_id) + return member + except: + return None + return None + @discord.ui.button(label="🔒 Fermer le Ticket", style=discord.ButtonStyle.red, custom_id="close_ticket") async def close_ticket(self, interaction: discord.Interaction, button: discord.ui.Button): """Ferme le ticket en désactivant l'envoi de messages pour le membre""" + await interaction.response.defer(ephemeral=True) + settings = load_settings(interaction.guild.id) staff_role_id = settings.get("staff_role_id") - if not discord.utils.get(interaction.user.roles, id=int(staff_role_id) if staff_role_id else 0) and not interaction.user.guild_permissions.administrator: - return await interaction.response.send_message("❌ Seuls les staffs peuvent fermer un ticket.", ephemeral=True) + is_staff = any(r.id == int(staff_role_id) for r in interaction.user.roles) if staff_role_id else False + if not is_staff and not interaction.user.guild_permissions.administrator: + return await interaction.followup.send("❌ Seuls les staffs peuvent fermer un ticket.", ephemeral=True) + + owner = await self.get_ticket_owner(interaction) + if not owner: + return await interaction.followup.send("❌ Impossible de trouver le propriétaire du ticket à fermer.", ephemeral=True) - topic = interaction.channel.topic or "" - if not topic.startswith("Ticket de "): - return await interaction.response.send_message("❌ Impossible de trouver le propriétaire du ticket à fermer.", ephemeral=True) - try: - owner_id = int(topic.split("Ticket de ")[1]) - ticket_owner = interaction.guild.get_member(owner_id) - if not ticket_owner: - ticket_owner = await interaction.guild.fetch_member(owner_id) - except (IndexError, ValueError, discord.NotFound): - return await interaction.response.send_message("❌ Membre introuvable.", ephemeral=True) + await interaction.channel.set_permissions(owner, read_messages=False, send_messages=False) + log_channel_rename(interaction.channel.id) + asyncio.create_task(interaction.channel.edit(name=f"fermé-{owner.display_name}".replace(" ", "-").lower()[:100])) + + # Envoi du message de fermeture dans le salon + new_view = ClosedTicketView() + await interaction.channel.send(f"🔒 Ce ticket a été fermé par {interaction.user.mention}.", view=new_view) + await interaction.followup.send("✅ Ticket fermé avec succès.", ephemeral=True) + except Exception as e: + kuby_logger.error(f"Erreur lors de la fermeture du ticket: {e}") + await interaction.followup.send(f"❌ Erreur lors de la fermeture : {e}", ephemeral=True) - await interaction.channel.set_permissions(ticket_owner, read_messages=False, send_messages=False) - await interaction.channel.edit(name="ticket-fermé") - await interaction.response.send_message("🔒 Le ticket a été fermé. Vous ne pouvez plus le voir.", ephemeral=True) - - # Mettre à jour la vue pour afficher uniquement les boutons Réouvrir et Supprimer - new_view = ClosedTicketView() - await interaction.channel.send("Le ticket a été fermé.", view=new_view) - - @discord.ui.button(label="👤 Prendre en charge le Ticket", style=discord.ButtonStyle.blurple, custom_id="claim_ticket") + @discord.ui.button(label="👤 Claim le Ticket", style=discord.ButtonStyle.blurple, custom_id="claim_ticket") async def claim_ticket(self, interaction: discord.Interaction, button: discord.ui.Button): """Un staff prend en charge le ticket""" + await interaction.response.defer(ephemeral=False) + settings = load_settings(interaction.guild.id) staff_role_id = settings.get("staff_role_id") - if not discord.utils.get(interaction.user.roles, id=int(staff_role_id) if staff_role_id else 0) and not interaction.user.guild_permissions.administrator: - return await interaction.response.send_message("❌ Seuls les staffs peuvent claim un ticket.", ephemeral=True) - - staff_role = discord.utils.get(interaction.guild.roles, id=int(staff_role_id)) if staff_role_id else None - - claimed_by = interaction.user - if staff_role: - await interaction.channel.set_permissions(staff_role, send_messages=False, read_messages=True) - - await interaction.channel.set_permissions(claimed_by, read_messages=True, send_messages=True) - await interaction.response.send_message(f"👤 {claimed_by.mention} a claim ce ticket.", ephemeral=False) + is_staff = any(r.id == int(staff_role_id) for r in interaction.user.roles) if staff_role_id else False + if not is_staff and not interaction.user.guild_permissions.administrator: + return await interaction.followup.send("❌ Seuls les staffs peuvent claim un ticket.") + + try: + staff_role = interaction.guild.get_role(int(staff_role_id)) if staff_role_id else None + + # Retrait des permissions du rôle staff pour forcer le claimer à parler seul + if staff_role: + await interaction.channel.set_permissions(staff_role, send_messages=False, read_messages=True) + + # Donner les permissions au claimer + await interaction.channel.set_permissions(interaction.user, read_messages=True, send_messages=True) + await interaction.followup.send(f"👥 {interaction.user.mention} a pris en charge ce ticket.") + except Exception as e: + kuby_logger.error(f"Erreur lors du claim du ticket: {e}") + await interaction.followup.send(f"❌ Erreur lors du claim : {e}") class ClosedTicketView(discord.ui.View): """Vue pour les tickets fermés avec les boutons Réouvrir et Supprimer""" @@ -376,45 +447,133 @@ class ClosedTicketView(discord.ui.View): def __init__(self): super().__init__(timeout=None) + async def get_ticket_owner(self, interaction: discord.Interaction): + """Récupère le propriétaire du ticket via le helper robuste""" + owner_id = await get_ticket_user_id(interaction.channel) + if owner_id: + try: + member = interaction.guild.get_member(owner_id) + if not member: + member = await interaction.guild.fetch_member(owner_id) + return member + except: + return None + return None + @discord.ui.button(label="♻ Réouvrir le Ticket", style=discord.ButtonStyle.green, custom_id="reopen_ticket") async def reopen_ticket(self, interaction: discord.Interaction, button: discord.ui.Button): """Réouvre le ticket pour le membre""" + await interaction.response.defer(ephemeral=True) + settings = load_settings(interaction.guild.id) staff_role_id = settings.get("staff_role_id") - if not discord.utils.get(interaction.user.roles, id=int(staff_role_id) if staff_role_id else 0) and not interaction.user.guild_permissions.administrator: - return await interaction.response.send_message("❌ Seuls les staffs peuvent réouvrir un ticket.", ephemeral=True) + is_staff = any(r.id == int(staff_role_id) for r in interaction.user.roles) if staff_role_id else False + if not is_staff and not interaction.user.guild_permissions.administrator: + return await interaction.followup.send("❌ Seuls les staffs peuvent réouvrir un ticket.", ephemeral=True) - topic = interaction.channel.topic or "" - if not topic.startswith("Ticket de "): - return await interaction.response.send_message("❌ Impossible de trouver le propriétaire du ticket à réouvrir.", ephemeral=True) + owner = await self.get_ticket_owner(interaction) + if not owner: + return await interaction.followup.send("❌ Impossible de trouver le propriétaire du ticket à réouvrir.", ephemeral=True) try: - owner_id = int(topic.split("Ticket de ")[1]) - ticket_owner = interaction.guild.get_member(owner_id) - if not ticket_owner: - ticket_owner = await interaction.guild.fetch_member(owner_id) - except (IndexError, ValueError, discord.NotFound): - return await interaction.response.send_message("❌ Membre introuvable.", ephemeral=True) - - await interaction.channel.set_permissions(ticket_owner, read_messages=True, send_messages=True) - await interaction.response.send_message("✅ Le ticket a été réouvert.", ephemeral=True) + # On demande au staff quel statut remettre + embed = discord.Embed( + title="♻️ Réouverture du Ticket", + description=f"Choisissez le statut à appliquer pour **{owner.display_name}**.", + color=discord.Color.blue() + ) + view = ReopenStatusView(owner) + await interaction.channel.send(embed=embed, view=view) + await interaction.followup.send("✅ Choisissez le statut ci-dessous.", ephemeral=True) + except Exception as e: + kuby_logger.error(f"Erreur lors de la préparation de réouverture: {e}") + await interaction.followup.send(f"❌ Erreur : {e}", ephemeral=True) - # Mettre à jour la vue pour afficher tous les boutons - new_view = TicketManagementView() - await interaction.channel.send("Le ticket a été réouvert.", view=new_view) +class ReopenStatusView(discord.ui.View): + """Vue pour choisir le statut lors d'une réouverture""" + def __init__(self, owner: discord.Member): + super().__init__(timeout=180) + self.owner = owner + + @discord.ui.button(label="Écrit (Ouvert)", style=discord.ButtonStyle.secondary) + async def set_written(self, interaction: discord.Interaction, button: discord.ui.Button): + await self.apply_status(interaction, "ouvert") + + @discord.ui.button(label="Oral (À faire)", style=discord.ButtonStyle.primary) + async def set_oral(self, interaction: discord.Interaction, button: discord.ui.Button): + await self.apply_status(interaction, "oral-à-faire") + + @discord.ui.button(label="Accepté", style=discord.ButtonStyle.success) + async def set_accepted(self, interaction: discord.Interaction, button: discord.ui.Button): + await self.apply_status(interaction, "accepté") + + async def apply_status(self, interaction: discord.Interaction, prefix: str): + await interaction.response.defer(ephemeral=True) + try: + settings = load_settings(interaction.guild.id) + candidature_a_faire_role_id = settings.get("candidature_a_faire_role_id") + attente_oral_role_id = settings.get("attente_oral_role_id") + citoyen_role_id = settings.get("citoyen_role_id") + + # On récupère les objets rôles + role_écrit = interaction.guild.get_role(int(candidature_a_faire_role_id)) if candidature_a_faire_role_id else None + role_oral = interaction.guild.get_role(int(attente_oral_role_id)) if attente_oral_role_id else None + role_citoyen = interaction.guild.get_role(int(citoyen_role_id)) if citoyen_role_id else None + + # Synchronisation des RÔLES en fonction du statut choisi + roles_to_remove = [] + role_to_add = None + + if prefix == "ouvert": + role_to_add = role_écrit + roles_to_remove = [r for r in [role_oral, role_citoyen] if r] + elif prefix == "oral-à-faire": + role_to_add = role_oral + roles_to_remove = [r for r in [role_écrit, role_citoyen] if r] + elif prefix == "accepté": + role_to_add = role_citoyen + roles_to_remove = [r for r in [role_écrit, role_oral] if r] + + # Mise à jour des rôles sur le membre + if roles_to_remove: + await self.owner.remove_roles(*roles_to_remove, reason="Réouverture du ticket - Maj Statut") + if role_to_add: + await self.owner.add_roles(role_to_add, reason="Réouverture du ticket - Maj Statut") + + # Mise à jour du SALON + await interaction.channel.set_permissions(self.owner, read_messages=True, send_messages=True) + new_name = f"{prefix}-{self.owner.display_name}".replace(" ", "-").lower()[:100] + log_channel_rename(interaction.channel.id) + asyncio.create_task(interaction.channel.edit(name=new_name, topic=f"Ticket de {self.owner.id}")) + + new_view = TicketManagementView() + await interaction.channel.send(f"✅ Ticket réouvert par {interaction.user.mention} (Statut: **{prefix}**).", view=new_view) + await interaction.followup.send(f"✅ Statut **{prefix}** appliqué et rôles synchronisés.", ephemeral=True) + self.stop() + except Exception as e: + kuby_logger.error(f"Erreur lors de la réouverture/synchro: {e}") + await interaction.followup.send(f"❌ Erreur système : {e}", ephemeral=True) @discord.ui.button(label="❌ Supprimer le Ticket", style=discord.ButtonStyle.danger, custom_id="delete_ticket") async def delete_ticket(self, interaction: discord.Interaction, button: discord.ui.Button): """Supprime le salon du ticket""" + await interaction.response.defer(ephemeral=True) + settings = load_settings(interaction.guild.id) staff_role_id = settings.get("staff_role_id") - if not discord.utils.get(interaction.user.roles, id=int(staff_role_id) if staff_role_id else 0) and not interaction.user.guild_permissions.administrator: - return await interaction.response.send_message("❌ Seuls les staffs peuvent supprimer un ticket.", ephemeral=True) + is_staff = any(r.id == int(staff_role_id) for r in interaction.user.roles) if staff_role_id else False + if not is_staff and not interaction.user.guild_permissions.administrator: + return await interaction.followup.send("❌ Seuls les staffs peuvent supprimer un ticket.", ephemeral=True) - await interaction.response.send_message("❌ Suppression du ticket...", ephemeral=True) - await interaction.channel.delete() + try: + await interaction.followup.send("❌ Suppression du ticket...", ephemeral=True) + await interaction.channel.delete() + except Exception as e: + kuby_logger.error(f"Erreur lors de la suppression du ticket: {e}") + try: await interaction.followup.send(f"❌ Erreur lors de la suppression : {e}", ephemeral=True) + except: pass class TicketView(discord.ui.View): """Vue avec le bouton pour ouvrir le ticket""" @@ -433,6 +592,12 @@ class Ticket(commands.Cog): def __init__(self, bot): self.bot = bot + async def cog_load(self): + """Enregistre les vues persistantes""" + self.bot.add_view(TicketView()) + self.bot.add_view(TicketManagementView()) + self.bot.add_view(ClosedTicketView()) + @app_commands.command(name="ticket_whitelist", description="Envoie l'embed pour ouvrir un ticket de whitelist.") @app_commands.checks.has_permissions(administrator=True) async def ticket_whitelist(self, interaction: discord.Interaction): @@ -476,50 +641,134 @@ class Ticket(commands.Cog): @commands.Cog.listener() async def on_message(self, message): - """Surveille les messages des staffs dans les tickets""" + """Surveille les messages des staffs dans les tickets pour validation automatique""" if message.guild is None or message.author.bot: return - if not message.channel.topic or not message.channel.topic.startswith("Ticket de"): + + # On ne traite que si le message contient "validé" + if "validé" not in message.content.lower(): + return + + # Détection du format du topic du ticket via helper (robuste) + user_id = await get_ticket_user_id(message.channel) + if not user_id: + await message.channel.send("❌ Debug: Impossible de récupérer l'ID utilisateur via le topic (même après refresh API).") return settings = load_settings(message.guild.id) staff_role_id = settings.get("staff_role_id") - attente_oral_role_id = settings.get("attente_oral_role_id") - candidature_a_faire_role_id = settings.get("candidature_a_faire_role_id") - citoyen_role_id = settings.get("citoyen_role_id") - if not staff_role_id: + # Vérification si l'auteur est staff ou admin + is_staff = False + if staff_role_id: + is_staff = any(r.id == int(staff_role_id) for r in message.author.roles) + + if not is_staff and not message.author.guild_permissions.administrator: + if "validé" in message.content.lower(): + await message.channel.send("❌ Debug: Permission refusée. Vous n'êtes ni Staff ni Admin.") return - staff_role = discord.utils.get(message.guild.roles, id=int(staff_role_id)) - - if staff_role in message.author.roles and "validé" in message.content.lower(): + try: + # L'ID est déjà trouvé par le helper ci-dessus + user_id = user_id try: - user_id = message.channel.topic.split("Ticket de ")[1] - user = await message.guild.fetch_member(int(user_id)) + user = await message.guild.fetch_member(user_id) + except (discord.NotFound, discord.HTTPException): + await message.channel.send("❌ Utilisateur introuvable. A-t-il quitté le serveur ?", delete_after=10) + return - if not attente_oral_role_id or not candidature_a_faire_role_id or not citoyen_role_id: - await message.channel.send("❌ Erreur de configuration : Impossible d'attribuer les rôles, ils ne sont pas tous définis dans `/ticket_whitelist_config`.", delete_after=10) - return + attente_oral_role_id = settings.get("attente_oral_role_id") + candidature_a_faire_role_id = settings.get("candidature_a_faire_role_id") + citoyen_role_id = settings.get("citoyen_role_id") - # Vérifier si le rôle "attente oral" est déjà attribué - attente_oral_role = discord.utils.get(message.guild.roles, id=int(attente_oral_role_id)) - candidature_a_faire_role = discord.utils.get(message.guild.roles, id=int(candidature_a_faire_role_id)) - citoyen_role = discord.utils.get(message.guild.roles, id=int(citoyen_role_id)) + if not attente_oral_role_id or not candidature_a_faire_role_id or not citoyen_role_id: + await message.channel.send("❌ Erreur de configuration : Impossible d'attribuer les rôles, ils ne sont pas tous définis dans `/ticket_whitelist_config`.", delete_after=10) + return - if attente_oral_role in user.roles: - # Deuxième "validé" détecté - if attente_oral_role: await user.remove_roles(attente_oral_role) - if citoyen_role: await user.add_roles(citoyen_role) - await message.channel.edit(name=f"{user.name}-citoyen") - else: - # Premier "validé" détecté - if candidature_a_faire_role: await user.remove_roles(candidature_a_faire_role) - if attente_oral_role: await user.add_roles(attente_oral_role) - await message.channel.edit(name=f"{user.name}-oral") + attente_oral_role = message.guild.get_role(int(attente_oral_role_id)) + candidature_a_faire_role = message.guild.get_role(int(candidature_a_faire_role_id)) + citoyen_role = message.guild.get_role(int(citoyen_role_id)) - except Exception as e: - print(f"Erreur lors du traitement du ticket : {e}") + if (attente_oral_role and attente_oral_role in user.roles) or "oral-à-faire" in message.channel.name.lower(): + # DEUXIÈME VALIDATION -> ACCEPTÉ / CITOYEN + if attente_oral_role: await user.remove_roles(attente_oral_role) + if citoyen_role: await user.add_roles(citoyen_role) + + # Mise à jour du nom du salon + new_name = f"accepté-{user.display_name}".replace(" ", "-").lower()[:100] + wait_time = check_channel_rename_rate_limit(message.channel.id) + log_channel_rename(message.channel.id) + asyncio.create_task(message.channel.edit(name=new_name)) + + # Confirmation dans le salon (Feedback visuel) + embed_val = discord.Embed( + title="🎉 Whitelist Validée !", + description=f"Félicitations {user.mention}, ton entretien oral a été validé par {message.author.mention} !\nTu es désormais **Citoyen**.", + color=discord.Color.green() + ) + await message.channel.send(embed=embed_val) + + # DM au joueur (RESTAURATION DESIGN CAPTURE) + try: + embed_dm = discord.Embed( + title="🎉 Félicitations - Citoyenneté Validée !", + description=f"Nous avons le plaisir de t'annoncer que ton passage sur **{message.guild.name}** a été validé avec succès !", + color=discord.Color.green() + ) + embed_dm.add_field(name="🏙️ Nouveau Statut", value="Citoyen", inline=True) + embed_dm.add_field(name="📍 Serveur", value=message.guild.name, inline=True) + embed_dm.add_field(name="📋 Prochaine étape", value="Tu peux désormais te connecter au serveur et commencer ton aventure RP. N'oublie pas de consulter les salons d'informations !", inline=False) + if wait_time > 0: + m, s = divmod(wait_time, 60) + embed_dm.add_field(name="⏳ Nom du ticket", value=f"Le nom de ton ticket sera actualisé d'ici {m}m et {s}s environ (limite de sécurité Discord).", inline=False) + embed_dm.add_field(name="🔗 Lien vers ton ticket", value=f"[Clique ici pour voir ton ticket]({message.channel.jump_url})", inline=False) + embed_dm.set_footer(text=f"L'équipe {message.guild.name}") + if message.guild.icon: embed_dm.set_thumbnail(url=message.guild.icon.url) + + await user.send(embed=embed_dm) + except: pass + + else: + # PREMIÈRE VALIDATION -> ORAL À FAIRE + if candidature_a_faire_role: await user.remove_roles(candidature_a_faire_role) + if attente_oral_role: await user.add_roles(attente_oral_role) + + # Mise à jour du nom du salon + new_name = f"oral-à-faire-{user.display_name}".replace(" ", "-").lower()[:100] + wait_time = check_channel_rename_rate_limit(message.channel.id) + log_channel_rename(message.channel.id) + asyncio.create_task(message.channel.edit(name=new_name)) + + # Confirmation dans le salon + embed_val = discord.Embed( + title="📝 Étape Écrite Validée !", + description=f"Ta candidature écrite a été validée par {message.author.mention}.\nTu passes désormais en **Attente Oral**.", + color=discord.Color.orange() + ) + await message.channel.send(embed=embed_val) + + # DM au joueur (RESTAURATION DESIGN CAPTURE) + try: + embed_dm = discord.Embed( + title="📝 Validation Étape Écrite", + description=f"Ta candidature écrite sur **{message.guild.name}** a été examinée et validée par notre équipe !", + color=discord.Color.orange() + ) + embed_dm.add_field(name="🎤 Statut Actuel", value="Attente Oral", inline=True) + embed_dm.add_field(name="🛠️ Action Requise", value="Merci de te rendre dans le salon vocal 'Attente Oral' dès que tu es disponible pour passer ton entretien.", inline=False) + if wait_time > 0: + m, s = divmod(wait_time, 60) + embed_dm.add_field(name="⏳ Nom du ticket", value=f"Le nom de ton ticket sera actualisé d'ici {m}m et {s}s environ (limite de sécurité Discord).", inline=False) + embed_dm.add_field(name="🔗 Lien vers ton ticket", value=f"[Clique ici pour voir ton ticket]({message.channel.jump_url})", inline=False) + embed_dm.set_footer(text=f"L'équipe {message.guild.name}") + if message.guild.icon: embed_dm.set_thumbnail(url=message.guild.icon.url) + + await user.send(embed=embed_dm) + except: pass + + except Exception as e: + kuby_logger.error(f"Erreur lors de la validation du ticket: {e}", exc_info=True) + await message.channel.send(f"❌ Une erreur système est survenue : {e}") async def setup(bot): await bot.add_cog(Ticket(bot)) diff --git a/commandes/website_sync.py b/commandes/website_sync.py index 78aad56..4fdc55e 100644 --- a/commandes/website_sync.py +++ b/commandes/website_sync.py @@ -64,11 +64,11 @@ class WebsiteSync(commands.Cog, name="Website Sync"): def cog_unload(self): self.daily_export.cancel() - # ── Tâche de fond : génération (30s pour test) ──────────────────────────── - @tasks.loop(seconds=30) + # ── Tâche de fond : génération (1 heure) ──────────────────────────── + @tasks.loop(hours=1) async def daily_export(self): - """Génère les JSON toutes les 30 secondes (Mode Test).""" - logger.info("[JSON Export] Génération automatique (toutes les 30s) déclenchée.") + """Génère les JSON toutes les heures.""" + logger.info("[JSON Export] Génération automatique déclenchée.") await generate_all(self.bot) @daily_export.before_loop diff --git a/commandes/welcome.py b/commandes/welcome.py index a0078c2..8b34a32 100644 --- a/commandes/welcome.py +++ b/commandes/welcome.py @@ -141,11 +141,17 @@ class Welcome(commands.Cog): servername=member.guild.name, member_count=member.guild.member_count ) - await member.send(formatted_message) - await member.send("https://i.giphy.com/media/v1.Y2lkPTc5MGI3NjExNXV1NW02NTk0bGF2ZHcyMGFna2F6amRrbGpobGpsMHowOW5xa2E5eCZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/ZtFuW4rsUbfrRzPauJ/giphy.gif") - kuby_logger.info(f"✅ [Welcome] DM envoyé avec succès à {member.name}") + # On combine le texte et le GIF dans un SEUL envoi pour réduire les requêtes API (limite de débit) + gif_url = "https://i.giphy.com/media/v1.Y2lkPTc5MGI3NjExNXV1NW02NTk0bGF2ZHcyMGFna2F6amRrbGpobGpsMHowOW5xa2E5eCZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/ZtFuW4rsUbfrRzPauJ/giphy.gif" + await member.send(f"{formatted_message}\n\n{gif_url}") + kuby_logger.info(f"✅ [Welcome] DM envoyé avec succès à {member.name} (envoi groupé)") except discord.Forbidden: kuby_logger.warning(f"🚫 [Welcome] Impossible d'envoyer le DM à {member.name} (DMs fermés)") + except discord.HTTPException as e: + if e.code == 40003: # Rate limited (Opening DMs too fast) + kuby_logger.warning(f"⏳ [Welcome] Limite de débit Discord atteinte pour les DMs : {member.name} n'a pas reçu son message.") + else: + kuby_logger.error(f"❌ [Welcome] Erreur HTTP lors de l'envoi du DM à {member.name}: {e}") except Exception as e: kuby_logger.error(f"❌ [Welcome] Erreur lors de l'envoi du DM à {member.name}: {e}", exc_info=True) else: diff --git a/data/json-export-history.json b/data/json-export-history.json index faef25a..460c6cc 100644 --- a/data/json-export-history.json +++ b/data/json-export-history.json @@ -1,180 +1,180 @@ [ { - "date": "2026-03-14T14:26:12.694595+00:00", + "date": "2026-03-31T20:07:49.786458+00:00", "trigger": "auto", "serversCount": 1, - "membersCount": 13, + "membersCount": 12, "guildName": "Omega Kube Bêta Test", "errors": [], "success": true }, { - "date": "2026-03-14T14:25:42.467679+00:00", + "date": "2026-03-31T20:06:47.718241+00:00", "trigger": "auto", "serversCount": 1, - "membersCount": 13, + "membersCount": 12, "guildName": "Omega Kube Bêta Test", "errors": [], "success": true }, { - "date": "2026-03-14T14:25:11.992208+00:00", + "date": "2026-03-31T20:06:07.381254+00:00", "trigger": "auto", "serversCount": 1, - "membersCount": 13, + "membersCount": 12, "guildName": "Omega Kube Bêta Test", "errors": [], "success": true }, { - "date": "2026-03-14T14:24:42.190744+00:00", + "date": "2026-03-31T20:00:43.918120+00:00", "trigger": "auto", "serversCount": 1, - "membersCount": 13, + "membersCount": 12, "guildName": "Omega Kube Bêta Test", "errors": [], "success": true }, { - "date": "2026-03-14T14:24:12.614886+00:00", + "date": "2026-03-29T20:16:08.626593+00:00", "trigger": "auto", "serversCount": 1, - "membersCount": 13, + "membersCount": 12, "guildName": "Omega Kube Bêta Test", "errors": [], "success": true }, { - "date": "2026-03-14T14:23:42.012764+00:00", + "date": "2026-03-29T19:16:08.300074+00:00", "trigger": "auto", "serversCount": 1, - "membersCount": 13, + "membersCount": 12, "guildName": "Omega Kube Bêta Test", "errors": [], "success": true }, { - "date": "2026-03-14T14:23:12.416066+00:00", + "date": "2026-03-29T18:16:08.376405+00:00", "trigger": "auto", "serversCount": 1, - "membersCount": 13, + "membersCount": 12, "guildName": "Omega Kube Bêta Test", "errors": [], "success": true }, { - "date": "2026-03-14T14:22:42.200744+00:00", + "date": "2026-03-29T17:16:08.514497+00:00", "trigger": "auto", "serversCount": 1, - "membersCount": 13, + "membersCount": 12, "guildName": "Omega Kube Bêta Test", "errors": [], "success": true }, { - "date": "2026-03-14T14:22:12.128982+00:00", + "date": "2026-03-29T17:15:42.635599+00:00", "trigger": "auto", "serversCount": 1, - "membersCount": 13, + "membersCount": 12, "guildName": "Omega Kube Bêta Test", "errors": [], "success": true }, { - "date": "2026-03-14T14:21:41.879828+00:00", + "date": "2026-03-29T17:15:41.574258+00:00", "trigger": "auto", "serversCount": 1, - "membersCount": 13, + "membersCount": 12, "guildName": "Omega Kube Bêta Test", "errors": [], "success": true }, { - "date": "2026-03-14T14:21:25.894956+00:00", + "date": "2026-03-29T14:18:49.697634+00:00", "trigger": "auto", "serversCount": 1, - "membersCount": 13, + "membersCount": 12, "guildName": "Omega Kube Bêta Test", "errors": [], "success": true }, { - "date": "2026-03-14T14:20:56.179577+00:00", + "date": "2026-03-29T14:16:08.163385+00:00", "trigger": "auto", "serversCount": 1, - "membersCount": 13, + "membersCount": 12, "guildName": "Omega Kube Bêta Test", "errors": [], "success": true }, { - "date": "2026-03-14T14:20:25.382325+00:00", + "date": "2026-03-29T14:09:18.074641+00:00", "trigger": "auto", "serversCount": 1, - "membersCount": 13, + "membersCount": 12, "guildName": "Omega Kube Bêta Test", "errors": [], "success": true }, { - "date": "2026-03-14T14:19:55.416432+00:00", + "date": "2026-03-29T13:56:24.990061+00:00", "trigger": "auto", "serversCount": 1, - "membersCount": 13, + "membersCount": 12, "guildName": "Omega Kube Bêta Test", "errors": [], "success": true }, { - "date": "2026-03-14T14:19:25.281242+00:00", + "date": "2026-03-29T13:54:43.766970+00:00", "trigger": "auto", "serversCount": 1, - "membersCount": 13, + "membersCount": 12, "guildName": "Omega Kube Bêta Test", "errors": [], "success": true }, { - "date": "2026-03-14T14:18:55.315861+00:00", + "date": "2026-03-29T13:48:39.075696+00:00", "trigger": "auto", "serversCount": 1, - "membersCount": 13, + "membersCount": 12, "guildName": "Omega Kube Bêta Test", "errors": [], "success": true }, { - "date": "2026-03-14T14:18:25.672058+00:00", + "date": "2026-03-29T13:48:08.960324+00:00", "trigger": "auto", "serversCount": 1, - "membersCount": 13, + "membersCount": 12, "guildName": "Omega Kube Bêta Test", "errors": [], "success": true }, { - "date": "2026-03-14T14:17:55.409042+00:00", + "date": "2026-03-29T13:47:39.191350+00:00", "trigger": "auto", "serversCount": 1, - "membersCount": 13, + "membersCount": 12, "guildName": "Omega Kube Bêta Test", "errors": [], "success": true }, { - "date": "2026-03-14T14:17:25.447376+00:00", + "date": "2026-03-29T13:47:09.059722+00:00", "trigger": "auto", "serversCount": 1, - "membersCount": 13, + "membersCount": 12, "guildName": "Omega Kube Bêta Test", "errors": [], "success": true }, { - "date": "2026-03-14T14:16:55.368379+00:00", + "date": "2026-03-29T13:46:39.263054+00:00", "trigger": "auto", "serversCount": 1, - "membersCount": 13, + "membersCount": 12, "guildName": "Omega Kube Bêta Test", "errors": [], "success": true diff --git a/data/logs/member_events_2025-08-19.json b/data/logs/member_events_2025-08-19.json deleted file mode 100755 index 93e927c..0000000 --- a/data/logs/member_events_2025-08-19.json +++ /dev/null @@ -1,100 +0,0 @@ -[ - { - "timestamp": "2025-08-19T14:50:31.191194", - "event_type": "member_join", - "member_id": 318312854816161792, - "member_name": "DraftBot#0535", - "member_discriminator": "0535", - "member_avatar": "https://cdn.discordapp.com/avatars/318312854816161792/f1d4dc93441273f3f8985a07e0bad164.png?size=1024" - }, - { - "timestamp": "2025-08-19T14:51:48.780957", - "event_type": "member_leave", - "member_id": 828239898975666216, - "member_name": "docteur_w.d_axolotl", - "member_discriminator": "0", - "member_avatar": "https://cdn.discordapp.com/avatars/828239898975666216/1835a7eaede10e9dbb78649beeed070c.png?size=1024", - "roles": { - "role_ids": [], - "role_names": [], - "permissions": [] - }, - "joined_at": "2025-05-09T18:36:32.808000+00:00" - }, - { - "timestamp": "2025-08-19T14:58:16.673562", - "event_type": "member_leave", - "member_id": 318312854816161792, - "member_name": "DraftBot#0535", - "member_discriminator": "0535", - "member_avatar": "https://cdn.discordapp.com/avatars/318312854816161792/f1d4dc93441273f3f8985a07e0bad164.png?size=1024", - "roles": { - "role_ids": [ - 1403041729330024508 - ], - "role_names": [ - "🗝️" - ], - "permissions": [ - 8 - ] - }, - "joined_at": "2025-08-19T12:50:31.007000+00:00" - }, - { - "timestamp": "2025-08-19T14:59:06.458094", - "event_type": "member_leave", - "member_id": 353942721997832195, - "member_name": "mg_studios_", - "member_discriminator": "0", - "member_avatar": "https://cdn.discordapp.com/avatars/353942721997832195/99c6b7514ce56e19cf87b2c8f0d41316.png?size=1024", - "roles": { - "role_ids": [ - 1396130252144508939, - 1369669999366770724, - 1369669999366770726 - ], - "role_names": [ - "mg_studios_", - "👔 | Co-Directeur", - "🔑" - ], - "permissions": [ - 0, - 0, - 8 - ] - }, - "joined_at": "2025-05-07T13:41:48.145000+00:00" - }, - { - "timestamp": "2025-08-19T14:59:19.218122", - "event_type": "member_join", - "member_id": 353942721997832195, - "member_name": "mg_studios_", - "member_discriminator": "0", - "member_avatar": "https://cdn.discordapp.com/avatars/353942721997832195/99c6b7514ce56e19cf87b2c8f0d41316.png?size=1024" - }, - { - "timestamp": "2025-08-19T21:41:25.723957", - "event_type": "member_join", - "member_id": 1407448905817391215, - "member_name": "test_33878", - "member_discriminator": "0", - "member_avatar": null - }, - { - "timestamp": "2025-08-19T23:48:12.347482", - "event_type": "member_leave", - "member_id": 1407448905817391215, - "member_name": "test_33878", - "member_discriminator": "0", - "member_avatar": null, - "roles": { - "role_ids": [], - "role_names": [], - "permissions": [] - }, - "joined_at": "2025-08-19T19:41:25.391607+00:00" - } -] \ No newline at end of file diff --git a/data/logs/member_events_2025-08-20.json b/data/logs/member_events_2025-08-20.json deleted file mode 100755 index 1bdd5af..0000000 --- a/data/logs/member_events_2025-08-20.json +++ /dev/null @@ -1,68 +0,0 @@ -[ - { - "timestamp": "2025-08-20T00:40:57.820048", - "event_type": "member_join", - "member_id": 1407448905817391215, - "member_name": "test_33878", - "member_discriminator": "0", - "member_avatar": null - }, - { - "timestamp": "2025-08-20T00:42:00.543535", - "event_type": "member_leave", - "member_id": 1407448905817391215, - "member_name": "test_33878", - "member_discriminator": "0", - "member_avatar": null, - "roles": { - "role_ids": [], - "role_names": [], - "permissions": [] - }, - "joined_at": "2025-08-19T22:40:57.711576+00:00" - }, - { - "timestamp": "2025-08-20T00:47:10.265057", - "event_type": "member_join", - "member_id": 1407448905817391215, - "member_name": "test_33878", - "member_discriminator": "0", - "member_avatar": null - }, - { - "timestamp": "2025-08-20T00:47:23.923297", - "event_type": "member_leave", - "member_id": 1407448905817391215, - "member_name": "test_33878", - "member_discriminator": "0", - "member_avatar": null, - "roles": { - "role_ids": [], - "role_names": [], - "permissions": [] - }, - "joined_at": "2025-08-19T22:47:10.020304+00:00" - }, - { - "timestamp": "2025-08-20T12:24:08.056140", - "event_type": "member_leave", - "member_id": 1407670411164127242, - "member_name": "test_109375", - "member_discriminator": "0", - "member_avatar": null, - "roles": { - "role_ids": [], - "role_names": [], - "permissions": [] - }, - "joined_at": "2025-08-20T10:21:05.730620+00:00" - }, - { - "timestamp": "2025-08-20T12:27:27.999521", - "event_type": "member_join", - "member_id": 1407670411164127242, - "member_name": "test_109375", - "member_discriminator": "0", - "member_avatar": null - } -] \ No newline at end of file diff --git a/data/logs/member_events_2025-08-22.json b/data/logs/member_events_2025-08-22.json deleted file mode 100755 index 6a9f2bc..0000000 --- a/data/logs/member_events_2025-08-22.json +++ /dev/null @@ -1,22 +0,0 @@ -[ - { - "timestamp": "2025-08-22T19:06:00.143653", - "event_type": "member_leave", - "member_id": 872192738920108074, - "member_name": "titis_zfr_7007", - "member_discriminator": "0", - "member_avatar": "https://cdn.discordapp.com/avatars/872192738920108074/986a18e279578123ec855f63defcc23f.png?size=1024", - "roles": { - "role_ids": [ - 1369769836028231730 - ], - "role_names": [ - "⚒️ | Bêta Testeurs" - ], - "permissions": [ - 1032 - ] - }, - "joined_at": "2025-07-18T21:59:11.510000+00:00" - } -] \ No newline at end of file diff --git a/data/logs/member_events_2025-12-30.json b/data/logs/member_events_2025-12-30.json deleted file mode 100644 index fed3ba6..0000000 --- a/data/logs/member_events_2025-12-30.json +++ /dev/null @@ -1,106 +0,0 @@ -[ - { - "timestamp": "2025-12-30T01:03:48.950150", - "event_type": "member_join", - "member_id": 1003956683040624672, - "member_name": "eva56_", - "member_discriminator": "0", - "member_avatar": "https://cdn.discordapp.com/avatars/1003956683040624672/c364667c914a821cd24212d32f7762b8.png?size=1024" - }, - { - "timestamp": "2025-12-30T01:04:52.974501", - "event_type": "member_join", - "member_id": 1013376581458210986, - "member_name": "kayrixtv", - "member_discriminator": "0", - "member_avatar": "https://cdn.discordapp.com/avatars/1013376581458210986/42d93e13065339ae40c50bcbf7a7b667.png?size=1024" - }, - { - "timestamp": "2025-12-30T01:05:11.496294", - "event_type": "member_leave", - "member_id": 1003956683040624672, - "member_name": "eva56_", - "member_discriminator": "0", - "member_avatar": "https://cdn.discordapp.com/avatars/1003956683040624672/c364667c914a821cd24212d32f7762b8.png?size=1024", - "roles": { - "role_ids": [], - "role_names": [], - "permissions": [] - }, - "joined_at": "2025-12-30T00:03:48.810667+00:00" - }, - { - "timestamp": "2025-12-30T01:05:38.027616", - "event_type": "member_join", - "member_id": 1003956683040624672, - "member_name": "eva56_", - "member_discriminator": "0", - "member_avatar": "https://cdn.discordapp.com/avatars/1003956683040624672/c364667c914a821cd24212d32f7762b8.png?size=1024" - }, - { - "timestamp": "2025-12-30T01:06:16.615009", - "event_type": "member_leave", - "member_id": 1003956683040624672, - "member_name": "eva56_", - "member_discriminator": "0", - "member_avatar": "https://cdn.discordapp.com/avatars/1003956683040624672/c364667c914a821cd24212d32f7762b8.png?size=1024", - "roles": { - "role_ids": [], - "role_names": [], - "permissions": [] - }, - "joined_at": "2025-12-30T00:05:37.900874+00:00" - }, - { - "timestamp": "2025-12-30T01:06:36.640980", - "event_type": "member_join", - "member_id": 1003956683040624672, - "member_name": "eva56_", - "member_discriminator": "0", - "member_avatar": "https://cdn.discordapp.com/avatars/1003956683040624672/c364667c914a821cd24212d32f7762b8.png?size=1024" - }, - { - "timestamp": "2025-12-30T01:09:36.950883", - "event_type": "member_leave", - "member_id": 1003956683040624672, - "member_name": "eva56_", - "member_discriminator": "0", - "member_avatar": "https://cdn.discordapp.com/avatars/1003956683040624672/c364667c914a821cd24212d32f7762b8.png?size=1024", - "roles": { - "role_ids": [], - "role_names": [], - "permissions": [] - }, - "joined_at": "2025-12-30T00:06:36.510813+00:00" - }, - { - "timestamp": "2025-12-30T01:13:30.386228", - "event_type": "member_join", - "member_id": 1003956683040624672, - "member_name": "eva56_", - "member_discriminator": "0", - "member_avatar": "https://cdn.discordapp.com/avatars/1003956683040624672/c364667c914a821cd24212d32f7762b8.png?size=1024" - }, - { - "timestamp": "2025-12-30T01:13:47.629282", - "event_type": "member_leave", - "member_id": 1003956683040624672, - "member_name": "eva56_", - "member_discriminator": "0", - "member_avatar": "https://cdn.discordapp.com/avatars/1003956683040624672/c364667c914a821cd24212d32f7762b8.png?size=1024", - "roles": { - "role_ids": [], - "role_names": [], - "permissions": [] - }, - "joined_at": "2025-12-30T00:13:30.252167+00:00" - }, - { - "timestamp": "2025-12-30T01:15:07.364595", - "event_type": "member_join", - "member_id": 1003956683040624672, - "member_name": "eva56_", - "member_discriminator": "0", - "member_avatar": "https://cdn.discordapp.com/avatars/1003956683040624672/c364667c914a821cd24212d32f7762b8.png?size=1024" - } -] \ No newline at end of file diff --git a/data/logs/member_events_2026-01-01.json b/data/logs/member_events_2026-01-01.json deleted file mode 100644 index 58b38aa..0000000 --- a/data/logs/member_events_2026-01-01.json +++ /dev/null @@ -1,10 +0,0 @@ -[ - { - "timestamp": "2026-01-01T22:00:26.255907", - "event_type": "member_join", - "member_id": 1456390746167967932, - "member_name": "juibu_66388", - "member_discriminator": "0", - "member_avatar": null - } -] \ No newline at end of file diff --git a/data/logs/member_events_2026-01-03.json b/data/logs/member_events_2026-01-03.json deleted file mode 100644 index a9007a8..0000000 --- a/data/logs/member_events_2026-01-03.json +++ /dev/null @@ -1,58 +0,0 @@ -[ - { - "timestamp": "2026-01-03T21:25:17.730927", - "event_type": "member_leave", - "member_id": 1456390746167967932, - "member_name": "juibu_66388", - "member_discriminator": "0", - "member_avatar": null, - "roles": { - "role_ids": [ - 1369669999366770721 - ], - "role_names": [ - "Faux banquier" - ], - "permissions": [ - 8192 - ] - }, - "joined_at": "2026-01-01T21:00:26.074000+00:00" - }, - { - "timestamp": "2026-01-03T21:25:28.894683", - "event_type": "member_join", - "member_id": 1456390746167967932, - "member_name": "juibu_66388", - "member_discriminator": "0", - "member_avatar": null - }, - { - "timestamp": "2026-01-03T21:49:33.975462", - "event_type": "member_leave", - "member_id": 1456390746167967932, - "member_name": "juibu_66388", - "member_discriminator": "0", - "member_avatar": null, - "roles": { - "role_ids": [ - 1369669999366770721 - ], - "role_names": [ - "Faux banquier" - ], - "permissions": [ - 8192 - ] - }, - "joined_at": "2026-01-03T20:25:28.737000+00:00" - }, - { - "timestamp": "2026-01-03T21:49:55.375389", - "event_type": "member_join", - "member_id": 1456390746167967932, - "member_name": "juibu_66388", - "member_discriminator": "0", - "member_avatar": null - } -] \ No newline at end of file diff --git a/data/logs/member_events_2026-01-10.json b/data/logs/member_events_2026-01-10.json deleted file mode 100644 index 917bc0f..0000000 --- a/data/logs/member_events_2026-01-10.json +++ /dev/null @@ -1,10 +0,0 @@ -[ - { - "timestamp": "2026-01-10T16:19:17.701722", - "event_type": "member_join", - "member_id": 1459567012031234198, - "member_name": "fequjifenq_85720", - "member_discriminator": "0", - "member_avatar": null - } -] \ No newline at end of file diff --git a/data/logs/member_events_2026-01-24.json b/data/logs/member_events_2026-01-24.json deleted file mode 100644 index 8a3ac40..0000000 --- a/data/logs/member_events_2026-01-24.json +++ /dev/null @@ -1,10 +0,0 @@ -[ - { - "timestamp": "2026-01-24T15:13:01.488356", - "event_type": "member_join", - "member_id": 1038035891450556507, - "member_name": "celness", - "member_discriminator": "0", - "member_avatar": "https://cdn.discordapp.com/avatars/1038035891450556507/e57596b7be5db3a5f3baf75f5fd4720b.png?size=1024" - } -] \ No newline at end of file diff --git a/data/logs/member_events_2026-02-08.json b/data/logs/member_events_2026-02-08.json deleted file mode 100644 index 4eb5876..0000000 --- a/data/logs/member_events_2026-02-08.json +++ /dev/null @@ -1,24 +0,0 @@ -[ - { - "timestamp": "2026-02-08T11:49:12.200722", - "event_type": "member_join", - "member_id": 1470008249322438678, - "member_name": "truc_19275", - "member_discriminator": "0", - "member_avatar": null - }, - { - "timestamp": "2026-02-08T12:38:12.823655", - "event_type": "member_leave", - "member_id": 1470008249322438678, - "member_name": "truc_19275", - "member_discriminator": "0", - "member_avatar": null, - "roles": { - "role_ids": [], - "role_names": [], - "permissions": [] - }, - "joined_at": "2026-02-08T10:49:12.106083+00:00" - } -] \ No newline at end of file diff --git a/data/logs/messages_2025-08-15.json b/data/logs/messages_2025-08-15.json deleted file mode 100755 index b126a54..0000000 --- a/data/logs/messages_2025-08-15.json +++ /dev/null @@ -1,241 +0,0 @@ -[ - { - "timestamp": "2025-08-15T17:03:39.778010", - "event_type": "deleted", - "message_id": 1405929928641351842, - "channel_id": 1369669999677145101, - "channel_name": "┇🔧・salon-de-tests", - "author_id": 1324439906046574693, - "author_name": "_radio_demon77_", - "author_roles": [ - 1369669999345537145, - 1403041729330024508 - ], - "content": "test", - "attachments": [], - "embeds": [], - "details": {} - }, - { - "timestamp": "2025-08-15T17:04:15.541259", - "event_type": "deleted", - "message_id": 1405930092764336172, - "channel_id": 1369669999677145101, - "channel_name": "┇🔧・salon-de-tests", - "author_id": 1324439906046574693, - "author_name": "_radio_demon77_", - "author_roles": [ - 1369669999345537145, - 1403041729330024508 - ], - "content": "bl", - "attachments": [], - "embeds": [], - "details": {} - }, - { - "timestamp": "2025-08-15T17:04:26.128394", - "event_type": "deleted", - "message_id": 1405930138096635996, - "channel_id": 1369669999677145101, - "channel_name": "┇🔧・salon-de-tests", - "author_id": 353942721997832195, - "author_name": "mg_studios_", - "author_roles": [ - 1369669999345537145, - 1396130252144508939, - 1369669999366770724, - 1369669999366770726 - ], - "content": "fe", - "attachments": [], - "embeds": [], - "details": {} - }, - { - "timestamp": "2025-08-15T17:04:26.581875", - "event_type": "deleted", - "message_id": 1405930139358859386, - "channel_id": 1369669999677145101, - "channel_name": "┇🔧・salon-de-tests", - "author_id": 353942721997832195, - "author_name": "mg_studios_", - "author_roles": [ - 1369669999345537145, - 1396130252144508939, - 1369669999366770724, - 1369669999366770726 - ], - "content": "fe", - "attachments": [], - "embeds": [], - "details": {} - }, - { - "timestamp": "2025-08-15T17:04:27.110643", - "event_type": "deleted", - "message_id": 1405930141888282686, - "channel_id": 1369669999677145101, - "channel_name": "┇🔧・salon-de-tests", - "author_id": 353942721997832195, - "author_name": "mg_studios_", - "author_roles": [ - 1369669999345537145, - 1396130252144508939, - 1369669999366770724, - 1369669999366770726 - ], - "content": "fe", - "attachments": [], - "embeds": [], - "details": {} - }, - { - "timestamp": "2025-08-15T17:04:27.420456", - "event_type": "deleted", - "message_id": 1405930143549100153, - "channel_id": 1369669999677145101, - "channel_name": "┇🔧・salon-de-tests", - "author_id": 353942721997832195, - "author_name": "mg_studios_", - "author_roles": [ - 1369669999345537145, - 1396130252144508939, - 1369669999366770724, - 1369669999366770726 - ], - "content": "fef", - "attachments": [], - "embeds": [], - "details": {} - }, - { - "timestamp": "2025-08-15T17:04:27.895168", - "event_type": "deleted", - "message_id": 1405930145155514388, - "channel_id": 1369669999677145101, - "channel_name": "┇🔧・salon-de-tests", - "author_id": 353942721997832195, - "author_name": "mg_studios_", - "author_roles": [ - 1369669999345537145, - 1396130252144508939, - 1369669999366770724, - 1369669999366770726 - ], - "content": "ef", - "attachments": [], - "embeds": [], - "details": {} - }, - { - "timestamp": "2025-08-15T17:04:55.151271", - "event_type": "deleted", - "message_id": 1405930245793517729, - "channel_id": 1369669999677145101, - "channel_name": "┇🔧・salon-de-tests", - "author_id": 1324439906046574693, - "author_name": "_radio_demon77_", - "author_roles": [ - 1369669999345537145, - 1403041729330024508 - ], - "content": "carré pour ça", - "attachments": [], - "embeds": [], - "details": {} - }, - { - "timestamp": "2025-08-15T17:07:29.586201", - "event_type": "edited", - "message_id": 1405930871201988708, - "channel_id": 1369669999677145101, - "channel_name": "┇🔧・salon-de-tests", - "author_id": 353942721997832195, - "author_name": "mg_studios_", - "author_roles": [ - 1369669999345537145, - 1396130252144508939, - 1369669999366770724, - 1369669999366770726 - ], - "content": "il est pas kick du serv juste il peux plus écrire", - "attachments": [], - "embeds": [], - "details": { - "before_content": "il est pas kick du serv juste il peux plus écrire", - "after_content": "il est pas kick du serv juste il peut plus écrire" - } - }, - { - "timestamp": "2025-08-15T17:08:24.314935", - "event_type": "edited", - "message_id": 1405931135254528171, - "channel_id": 1369669999677145101, - "channel_name": "┇🔧・salon-de-tests", - "author_id": 1324439906046574693, - "author_name": "_radio_demon77_", - "author_roles": [ - 1369669999345537145, - 1403041729330024508 - ], - "content": "[fae](https://cdn.discordapp.com/emojis/1377581019649151077.webp?size=48&name=fae)", - "attachments": [], - "embeds": [], - "details": { - "before_content": "[fae](https://cdn.discordapp.com/emojis/1377581019649151077.webp?size=48&name=fae)", - "after_content": "[fae](https://cdn.discordapp.com/emojis/1377581019649151077.webp?size=48&name=fae)" - } - }, - { - "timestamp": "2025-08-15T17:51:19.548967", - "event_type": "deleted", - "message_id": 1405941938007314614, - "channel_id": 1369669999677145101, - "channel_name": "┇🔧・salon-de-tests", - "author_id": 1324439906046574693, - "author_name": "_radio_demon77_", - "author_roles": [ - 1369669999345537145, - 1403041729330024508 - ], - "content": "k", - "attachments": [], - "embeds": [], - "details": {} - }, - { - "timestamp": "2025-08-15T17:51:20.415679", - "event_type": "deleted", - "message_id": 1405941942227046552, - "channel_id": 1369669999677145101, - "channel_name": "┇🔧・salon-de-tests", - "author_id": 1324439906046574693, - "author_name": "_radio_demon77_", - "author_roles": [ - 1369669999345537145, - 1403041729330024508 - ], - "content": "k", - "attachments": [], - "embeds": [], - "details": {} - }, - { - "timestamp": "2025-08-15T17:52:26.716628", - "event_type": "deleted", - "message_id": 1405942220049219737, - "channel_id": 1369669999677145101, - "channel_name": "┇🔧・salon-de-tests", - "author_id": 1324439906046574693, - "author_name": "_radio_demon77_", - "author_roles": [ - 1369669999345537145, - 1403041729330024508 - ], - "content": "<@353942721997832195> il me faut qqn de pas op pour tester le spam", - "attachments": [], - "embeds": [], - "details": {} - } -] \ No newline at end of file diff --git a/data/logs/messages_2025-08-16.json b/data/logs/messages_2025-08-16.json deleted file mode 100755 index 2d502c7..0000000 --- a/data/logs/messages_2025-08-16.json +++ /dev/null @@ -1,39 +0,0 @@ -[ - { - "timestamp": "2025-08-16T17:11:20.495657", - "event_type": "deleted", - "message_id": 1406294262861140100, - "channel_id": 1369669999677145101, - "channel_name": "┇🔧・salon-de-tests", - "author_id": 1324439906046574693, - "author_name": "_radio_demon77_", - "author_roles": [ - 1369669999345537145, - 1403041729330024508 - ], - "content": "ou @here", - "attachments": [], - "embeds": [], - "details": {} - }, - { - "timestamp": "2025-08-16T17:22:10.841132", - "event_type": "edited", - "message_id": 1406296949967290461, - "channel_id": 1369669999677145101, - "channel_name": "┇🔧・salon-de-tests", - "author_id": 1324439906046574693, - "author_name": "_radio_demon77_", - "author_roles": [ - 1369669999345537145, - 1403041729330024508 - ], - "content": "je dois yalelr en plus", - "attachments": [], - "embeds": [], - "details": { - "before_content": "je dois yalelr en plus", - "after_content": "je dois y aller en plus" - } - } -] \ No newline at end of file diff --git a/data/logs/messages_2025-08-17.json b/data/logs/messages_2025-08-17.json deleted file mode 100755 index 197063e..0000000 --- a/data/logs/messages_2025-08-17.json +++ /dev/null @@ -1,87 +0,0 @@ -[ - { - "timestamp": "2025-08-17T13:14:10.888477", - "event_type": "deleted", - "message_id": 1406596967433244725, - "channel_id": 1369669999677145101, - "channel_name": "┇🔧・salon-de-tests", - "author_id": 1324439906046574693, - "author_name": "_radio_demon77_", - "author_roles": [ - 1369669999345537145, - 1403041729330024508 - ], - "content": "h", - "attachments": [], - "embeds": [], - "details": {} - }, - { - "timestamp": "2025-08-17T13:14:11.627298", - "event_type": "deleted", - "message_id": 1406596971757568060, - "channel_id": 1369669999677145101, - "channel_name": "┇🔧・salon-de-tests", - "author_id": 1324439906046574693, - "author_name": "_radio_demon77_", - "author_roles": [ - 1369669999345537145, - 1403041729330024508 - ], - "content": "h", - "attachments": [], - "embeds": [], - "details": {} - }, - { - "timestamp": "2025-08-17T13:14:11.996001", - "event_type": "deleted", - "message_id": 1406596972982308935, - "channel_id": 1369669999677145101, - "channel_name": "┇🔧・salon-de-tests", - "author_id": 1324439906046574693, - "author_name": "_radio_demon77_", - "author_roles": [ - 1369669999345537145, - 1403041729330024508 - ], - "content": "h", - "attachments": [], - "embeds": [], - "details": {} - }, - { - "timestamp": "2025-08-17T13:14:12.397997", - "event_type": "deleted", - "message_id": 1406596974714818569, - "channel_id": 1369669999677145101, - "channel_name": "┇🔧・salon-de-tests", - "author_id": 1324439906046574693, - "author_name": "_radio_demon77_", - "author_roles": [ - 1369669999345537145, - 1403041729330024508 - ], - "content": "h", - "attachments": [], - "embeds": [], - "details": {} - }, - { - "timestamp": "2025-08-17T13:15:46.771221", - "event_type": "deleted", - "message_id": 1406597370661310564, - "channel_id": 1369669999677145101, - "channel_name": "┇🔧・salon-de-tests", - "author_id": 1324439906046574693, - "author_name": "_radio_demon77_", - "author_roles": [ - 1369669999345537145, - 1403041729330024508 - ], - "content": "h", - "attachments": [], - "embeds": [], - "details": {} - } -] \ No newline at end of file diff --git a/data/logs/messages_2025-08-19.json b/data/logs/messages_2025-08-19.json deleted file mode 100755 index 75aeee0..0000000 --- a/data/logs/messages_2025-08-19.json +++ /dev/null @@ -1,303 +0,0 @@ -[ - { - "timestamp": "2025-08-19T14:31:07.894788", - "event_type": "edited", - "message_id": 1407341109302267986, - "channel_id": 1369669999677145101, - "channel_name": "┇🔧・salon-de-tests", - "author_id": 971446412690722826, - "author_name": "gameurpro12", - "author_roles": [ - 1369669999345537145, - 1396112611292090368, - 1369669999366770726 - ], - "content": "https://music.youtube.com/watch?v=ZjHjIvlokkg&list=RDAMVMZjHjIvlokkg", - "attachments": [], - "embeds": [], - "details": { - "before_content": "https://music.youtube.com/watch?v=ZjHjIvlokkg&list=RDAMVMZjHjIvlokkg", - "after_content": "https://music.youtube.com/watch?v=ZjHjIvlokkg&list=RDAMVMZjHjIvlokkg" - } - }, - { - "timestamp": "2025-08-19T14:31:07.908824", - "event_type": "deleted", - "message_id": 1407341109302267986, - "channel_id": 1369669999677145101, - "channel_name": "┇🔧・salon-de-tests", - "author_id": 971446412690722826, - "author_name": "gameurpro12", - "author_roles": [ - 1369669999345537145, - 1396112611292090368, - 1369669999366770726 - ], - "content": "https://music.youtube.com/watch?v=ZjHjIvlokkg&list=RDAMVMZjHjIvlokkg", - "attachments": [], - "embeds": [ - { - "thumbnail": { - "width": 1280, - "url": "https://i.ytimg.com/vi/ZjHjIvlokkg/maxresdefault.jpg", - "proxy_url": "https://images-ext-1.discordapp.net/external/rqEye0Kbh537345_yiwGFQmGsNDfxFg10JHdnuiizrI/https/i.ytimg.com/vi/ZjHjIvlokkg/maxresdefault.jpg", - "placeholder_version": 1, - "placeholder": "BQgGDIIIplqHd3h4iIUIfHXzJw==", - "height": 720, - "flags": 0, - "content_type": "image/jpeg" - }, - "video": { - "width": 1280, - "url": "https://www.youtube.com/embed/ZjHjIvlokkg", - "placeholder_version": 1, - "placeholder": "BQgGDIIIplqHd3h4iIUIfHXzJw==", - "height": 720, - "flags": 0 - }, - "provider": { - "url": "https://www.youtube.com", - "name": "YouTube" - }, - "author": { - "url": "https://www.youtube.com/channel/UCQgovvTTLPxAazVQE6X2ivQ", - "name": "Fredz" - }, - "color": 16711680, - "type": "video", - "description": "Les belles filles - Fredz ft. Leslie Medina\nlive au Badaboum à PARIS le 16/12/22\nJ'espère que ça vous plaira ! Merci pour tout\n\nRéalisation: Noémie Fray (@nomzoa)\nÉtalonnage: Stéphane Martins (@smartins_dop)\n\nRetrouvez Fredz sur :\nInstagram : https://www.instagram.com/fredz_musicc/\nTik Tok : https://vm.tiktok.com/ZSv9b5US/\nFacebook : http...", - "url": "https://www.youtube.com/watch?v=ZjHjIvlokkg", - "title": "FREDZ feat. Leslie Medina - Les belles filles (Live à Paris)" - } - ], - "details": {} - }, - { - "timestamp": "2025-08-19T14:31:25.362973", - "event_type": "deleted", - "message_id": 1407341171679826030, - "channel_id": 1369669999677145101, - "channel_name": "┇🔧・salon-de-tests", - "author_id": 971446412690722826, - "author_name": "gameurpro12", - "author_roles": [ - 1369669999345537145, - 1396112611292090368, - 1369669999366770726 - ], - "content": "https://music.youtube.com/watch?v=ZjHjIvlokkg&list=RDAMVMZjHjIvlokkg", - "attachments": [], - "embeds": [ - { - "thumbnail": { - "width": 1280, - "url": "https://i.ytimg.com/vi/ZjHjIvlokkg/maxresdefault.jpg", - "proxy_url": "https://images-ext-1.discordapp.net/external/rqEye0Kbh537345_yiwGFQmGsNDfxFg10JHdnuiizrI/https/i.ytimg.com/vi/ZjHjIvlokkg/maxresdefault.jpg", - "placeholder_version": 1, - "placeholder": "BQgGDIIIplqHd3h4iIUIfHXzJw==", - "height": 720, - "flags": 0, - "content_type": "image/jpeg" - }, - "video": { - "width": 1280, - "url": "https://www.youtube.com/embed/ZjHjIvlokkg", - "placeholder_version": 1, - "placeholder": "BQgGDIIIplqHd3h4iIUIfHXzJw==", - "height": 720, - "flags": 0 - }, - "provider": { - "url": "https://www.youtube.com", - "name": "YouTube" - }, - "author": { - "url": "https://www.youtube.com/channel/UCQgovvTTLPxAazVQE6X2ivQ", - "name": "Fredz" - }, - "color": 16711680, - "type": "video", - "description": "Les belles filles - Fredz ft. Leslie Medina\nlive au Badaboum à PARIS le 16/12/22\nJ'espère que ça vous plaira ! Merci pour tout\n\nRéalisation: Noémie Fray (@nomzoa)\nÉtalonnage: Stéphane Martins (@smartins_dop)\n\nRetrouvez Fredz sur :\nInstagram : https://www.instagram.com/fredz_musicc/\nTik Tok : https://vm.tiktok.com/ZSv9b5US/\nFacebook : http...", - "url": "https://www.youtube.com/watch?v=ZjHjIvlokkg", - "title": "FREDZ feat. Leslie Medina - Les belles filles (Live à Paris)" - } - ], - "details": {} - }, - { - "timestamp": "2025-08-19T14:31:30.378149", - "event_type": "deleted", - "message_id": 1407341203208278036, - "channel_id": 1369669999677145101, - "channel_name": "┇🔧・salon-de-tests", - "author_id": 971446412690722826, - "author_name": "gameurpro12", - "author_roles": [ - 1369669999345537145, - 1396112611292090368, - 1369669999366770726 - ], - "content": "https://music.youtube.com/watch?v=ZjHjIvlokkg&list=RDAMVMZjHjIvlokkg", - "attachments": [], - "embeds": [ - { - "thumbnail": { - "width": 1280, - "url": "https://i.ytimg.com/vi/ZjHjIvlokkg/maxresdefault.jpg", - "proxy_url": "https://images-ext-1.discordapp.net/external/rqEye0Kbh537345_yiwGFQmGsNDfxFg10JHdnuiizrI/https/i.ytimg.com/vi/ZjHjIvlokkg/maxresdefault.jpg", - "placeholder_version": 1, - "placeholder": "BQgGDIIIplqHd3h4iIUIfHXzJw==", - "height": 720, - "flags": 0, - "content_type": "image/jpeg" - }, - "video": { - "width": 1280, - "url": "https://www.youtube.com/embed/ZjHjIvlokkg", - "placeholder_version": 1, - "placeholder": "BQgGDIIIplqHd3h4iIUIfHXzJw==", - "height": 720, - "flags": 0 - }, - "provider": { - "url": "https://www.youtube.com", - "name": "YouTube" - }, - "author": { - "url": "https://www.youtube.com/channel/UCQgovvTTLPxAazVQE6X2ivQ", - "name": "Fredz" - }, - "color": 16711680, - "type": "video", - "description": "Les belles filles - Fredz ft. Leslie Medina\nlive au Badaboum à PARIS le 16/12/22\nJ'espère que ça vous plaira ! Merci pour tout\n\nRéalisation: Noémie Fray (@nomzoa)\nÉtalonnage: Stéphane Martins (@smartins_dop)\n\nRetrouvez Fredz sur :\nInstagram : https://www.instagram.com/fredz_musicc/\nTik Tok : https://vm.tiktok.com/ZSv9b5US/\nFacebook : http...", - "url": "https://www.youtube.com/watch?v=ZjHjIvlokkg", - "title": "FREDZ feat. Leslie Medina - Les belles filles (Live à Paris)" - } - ], - "details": {} - }, - { - "timestamp": "2025-08-19T20:00:33.051055", - "event_type": "edited", - "message_id": 1407423989206356008, - "channel_id": 1369669999677145098, - "channel_name": "╭💬・général", - "author_id": 971446412690722826, - "author_name": "gameurpro12", - "author_roles": [ - 1369669999345537145, - 1396112611292090368, - 1369669999366770723, - 1369669999366770726 - ], - "content": "Viens ici mo cobay préférer", - "attachments": [], - "embeds": [], - "details": { - "before_content": "Viens ici mo cobay préférer", - "after_content": "Viens ici mon cobay préférer" - } - }, - { - "timestamp": "2025-08-19T20:09:09.967269", - "event_type": "deleted", - "message_id": 1407426178486374564, - "channel_id": 1369669999677145098, - "channel_name": "╭💬・général", - "author_id": 971446412690722826, - "author_name": "gameurpro12", - "author_roles": [ - 1369669999345537145, - 1396112611292090368, - 1369669999366770723, - 1369669999366770726 - ], - "content": "@everyone qui veut faire le cobay avec moi", - "attachments": [], - "embeds": [], - "details": {} - }, - { - "timestamp": "2025-08-19T22:19:00.115805", - "event_type": "edited", - "message_id": 1407458853196529705, - "channel_id": 1369669999677145098, - "channel_name": "╭💬・général", - "author_id": 353942721997832195, - "author_name": "mg_studios_", - "author_roles": [ - 1369669999345537145, - 1396130252144508939, - 1369669999366770724 - ], - "content": "https://tenor.com/view/14th-doctor-doctor-who-david-tennant-wild-blue-yonder-rage-gif-12474509552290072827", - "attachments": [], - "embeds": [], - "details": { - "before_content": "https://tenor.com/view/14th-doctor-doctor-who-david-tennant-wild-blue-yonder-rage-gif-12474509552290072827", - "after_content": "https://tenor.com/view/14th-doctor-doctor-who-david-tennant-wild-blue-yonder-rage-gif-12474509552290072827" - } - }, - { - "timestamp": "2025-08-19T22:19:14.095772", - "event_type": "edited", - "message_id": 1407458911929503875, - "channel_id": 1369669999677145098, - "channel_name": "╭💬・général", - "author_id": 353942721997832195, - "author_name": "mg_studios_", - "author_roles": [ - 1369669999345537145, - 1396130252144508939, - 1369669999366770724 - ], - "content": "https://tenor.com/view/dr-who-doctor-who-david-tennant-sad-annoyed-gif-4879151", - "attachments": [], - "embeds": [], - "details": { - "before_content": "https://tenor.com/view/dr-who-doctor-who-david-tennant-sad-annoyed-gif-4879151", - "after_content": "https://tenor.com/view/dr-who-doctor-who-david-tennant-sad-annoyed-gif-4879151" - } - }, - { - "timestamp": "2025-08-19T22:24:31.079905", - "event_type": "edited", - "message_id": 1407460213409452235, - "channel_id": 1369669999677145101, - "channel_name": "┇🔧・salon-de-tests", - "author_id": 971446412690722826, - "author_name": "gameurpro12", - "author_roles": [ - 1369669999345537145, - 1396112611292090368, - 1369669999366770723, - 1369669999366770726 - ], - "content": "OUi", - "attachments": [], - "embeds": [], - "details": { - "before_content": "OUi", - "after_content": "Oui" - } - }, - { - "timestamp": "2025-08-19T22:50:46.776609", - "event_type": "deleted", - "message_id": 1407466849981038592, - "channel_id": 1369669999677145101, - "channel_name": "┇🔧・salon-de-tests", - "author_id": 971446412690722826, - "author_name": "gameurpro12", - "author_roles": [ - 1369669999345537145, - 1396112611292090368, - 1369669999366770723, - 1369669999366770726 - ], - "content": "https://www.perplexity.ai", - "attachments": [], - "embeds": [], - "details": {} - } -] \ No newline at end of file diff --git a/data/logs/messages_2025-08-20.json b/data/logs/messages_2025-08-20.json deleted file mode 100755 index b96a701..0000000 --- a/data/logs/messages_2025-08-20.json +++ /dev/null @@ -1,34 +0,0 @@ -[ - { - "timestamp": "2025-08-20T00:42:00.965568", - "event_type": "deleted", - "message_id": 1407494580709883994, - "channel_id": 1369669999677145098, - "channel_name": "╭💬・général", - "author_id": 1407448905817391215, - "author_name": "test_33878", - "author_roles": [ - 1369669999345537145 - ], - "content": "", - "attachments": [], - "embeds": [], - "details": {} - }, - { - "timestamp": "2025-08-20T00:47:24.265760", - "event_type": "deleted", - "message_id": 1407496141343559791, - "channel_id": 1369669999677145098, - "channel_name": "╭💬・général", - "author_id": 1407448905817391215, - "author_name": "test_33878", - "author_roles": [ - 1369669999345537145 - ], - "content": "", - "attachments": [], - "embeds": [], - "details": {} - } -] \ No newline at end of file diff --git a/data/logs/messages_2025-08-23.json b/data/logs/messages_2025-08-23.json deleted file mode 100755 index 79bb89c..0000000 --- a/data/logs/messages_2025-08-23.json +++ /dev/null @@ -1,40 +0,0 @@ -[ - { - "timestamp": "2025-08-23T20:54:00.125408", - "event_type": "deleted", - "message_id": 1408887012731129877, - "channel_id": 1369669999677145098, - "channel_name": "╭💬・général", - "author_id": 971446412690722826, - "author_name": "gameurpro12", - "author_roles": [ - 1369669999345537145, - 1396112611292090368, - 1369669999366770723, - 1369669999366770726 - ], - "content": "G", - "attachments": [], - "embeds": [], - "details": {} - }, - { - "timestamp": "2025-08-23T20:54:00.775382", - "event_type": "deleted", - "message_id": 1408887015570804787, - "channel_id": 1369669999677145098, - "channel_name": "╭💬・général", - "author_id": 971446412690722826, - "author_name": "gameurpro12", - "author_roles": [ - 1369669999345537145, - 1396112611292090368, - 1369669999366770723, - 1369669999366770726 - ], - "content": "G", - "attachments": [], - "embeds": [], - "details": {} - } -] \ No newline at end of file diff --git a/data/logs/messages_2025-12-30.json b/data/logs/messages_2025-12-30.json deleted file mode 100644 index 36f9842..0000000 --- a/data/logs/messages_2025-12-30.json +++ /dev/null @@ -1,705 +0,0 @@ -[ - { - "timestamp": "2025-12-30T14:52:01.432517", - "event_type": "deleted", - "message_id": 1455559052359631046, - "channel_id": 1369669999677145098, - "channel_name": "╭💬・général", - "author_id": 971446412690722826, - "author_name": "gameurpro12", - "author_roles": [ - 1369669999345537145, - 1369669999366770723, - 1403041729330024508, - 1369669999366770726 - ], - "content": "g", - "attachments": [], - "embeds": [], - "details": {} - }, - { - "timestamp": "2025-12-30T14:52:01.860802", - "event_type": "deleted", - "message_id": 1455559053878235177, - "channel_id": 1369669999677145098, - "channel_name": "╭💬・général", - "author_id": 971446412690722826, - "author_name": "gameurpro12", - "author_roles": [ - 1369669999345537145, - 1369669999366770723, - 1403041729330024508, - 1369669999366770726 - ], - "content": "g", - "attachments": [], - "embeds": [], - "details": {} - }, - { - "timestamp": "2025-12-30T14:52:02.287326", - "event_type": "deleted", - "message_id": 1455559055304167619, - "channel_id": 1369669999677145098, - "channel_name": "╭💬・général", - "author_id": 971446412690722826, - "author_name": "gameurpro12", - "author_roles": [ - 1369669999345537145, - 1369669999366770723, - 1403041729330024508, - 1369669999366770726 - ], - "content": "g", - "attachments": [], - "embeds": [], - "details": {} - }, - { - "timestamp": "2025-12-30T14:52:02.656882", - "event_type": "deleted", - "message_id": 1455559057019633797, - "channel_id": 1369669999677145098, - "channel_name": "╭💬・général", - "author_id": 971446412690722826, - "author_name": "gameurpro12", - "author_roles": [ - 1369669999345537145, - 1369669999366770723, - 1403041729330024508, - 1369669999366770726 - ], - "content": "g", - "attachments": [], - "embeds": [], - "details": {} - }, - { - "timestamp": "2025-12-30T14:52:13.025829", - "event_type": "deleted", - "message_id": 1455559100954837196, - "channel_id": 1369669999677145098, - "channel_name": "╭💬・général", - "author_id": 971446412690722826, - "author_name": "gameurpro12", - "author_roles": [ - 1369669999345537145, - 1369669999366770723, - 1403041729330024508, - 1369669999366770726 - ], - "content": "g", - "attachments": [], - "embeds": [], - "details": {} - }, - { - "timestamp": "2025-12-30T14:52:13.582926", - "event_type": "deleted", - "message_id": 1455559103081353248, - "channel_id": 1369669999677145098, - "channel_name": "╭💬・général", - "author_id": 971446412690722826, - "author_name": "gameurpro12", - "author_roles": [ - 1369669999345537145, - 1369669999366770723, - 1403041729330024508, - 1369669999366770726 - ], - "content": "g", - "attachments": [], - "embeds": [], - "details": {} - }, - { - "timestamp": "2025-12-30T14:52:13.780030", - "event_type": "deleted", - "message_id": 1455559104306217063, - "channel_id": 1369669999677145098, - "channel_name": "╭💬・général", - "author_id": 971446412690722826, - "author_name": "gameurpro12", - "author_roles": [ - 1369669999345537145, - 1369669999366770723, - 1403041729330024508, - 1369669999366770726 - ], - "content": "g", - "attachments": [], - "embeds": [], - "details": {} - }, - { - "timestamp": "2025-12-30T14:52:14.165698", - "event_type": "deleted", - "message_id": 1455559105845395639, - "channel_id": 1369669999677145098, - "channel_name": "╭💬・général", - "author_id": 971446412690722826, - "author_name": "gameurpro12", - "author_roles": [ - 1369669999345537145, - 1369669999366770723, - 1403041729330024508, - 1369669999366770726 - ], - "content": "g", - "attachments": [], - "embeds": [], - "details": {} - }, - { - "timestamp": "2025-12-30T14:52:14.612894", - "event_type": "deleted", - "message_id": 1455559107791556762, - "channel_id": 1369669999677145098, - "channel_name": "╭💬・général", - "author_id": 971446412690722826, - "author_name": "gameurpro12", - "author_roles": [ - 1369669999345537145, - 1369669999366770723, - 1403041729330024508, - 1369669999366770726 - ], - "content": "g", - "attachments": [], - "embeds": [], - "details": {} - }, - { - "timestamp": "2025-12-30T14:52:14.798228", - "event_type": "deleted", - "message_id": 1455559108798185593, - "channel_id": 1369669999677145098, - "channel_name": "╭💬・général", - "author_id": 971446412690722826, - "author_name": "gameurpro12", - "author_roles": [ - 1369669999345537145, - 1369669999366770723, - 1403041729330024508, - 1369669999366770726 - ], - "content": "g", - "attachments": [], - "embeds": [], - "details": {} - }, - { - "timestamp": "2025-12-30T14:52:15.528887", - "event_type": "deleted", - "message_id": 1455559111071629509, - "channel_id": 1369669999677145098, - "channel_name": "╭💬・général", - "author_id": 971446412690722826, - "author_name": "gameurpro12", - "author_roles": [ - 1369669999345537145, - 1369669999366770723, - 1403041729330024508, - 1369669999366770726 - ], - "content": "g", - "attachments": [], - "embeds": [], - "details": {} - }, - { - "timestamp": "2025-12-30T14:52:16.499969", - "event_type": "deleted", - "message_id": 1455559109825790114, - "channel_id": 1369669999677145098, - "channel_name": "╭💬・général", - "author_id": 971446412690722826, - "author_name": "gameurpro12", - "author_roles": [ - 1369669999345537145, - 1369669999366770723, - 1403041729330024508, - 1369669999366770726 - ], - "content": "g\ng", - "attachments": [], - "embeds": [], - "details": {} - }, - { - "timestamp": "2025-12-30T14:52:17.142340", - "event_type": "deleted", - "message_id": 1455559112875049105, - "channel_id": 1369669999677145098, - "channel_name": "╭💬・général", - "author_id": 971446412690722826, - "author_name": "gameurpro12", - "author_roles": [ - 1369669999345537145, - 1369669999366770723, - 1403041729330024508, - 1369669999366770726 - ], - "content": "g", - "attachments": [], - "embeds": [], - "details": {} - }, - { - "timestamp": "2025-12-30T14:52:18.196550", - "event_type": "deleted", - "message_id": 1455559117165826120, - "channel_id": 1369669999677145098, - "channel_name": "╭💬・général", - "author_id": 971446412690722826, - "author_name": "gameurpro12", - "author_roles": [ - 1369669999345537145, - 1369669999366770723, - 1403041729330024508, - 1369669999366770726 - ], - "content": "g", - "attachments": [], - "embeds": [], - "details": {} - }, - { - "timestamp": "2025-12-30T14:52:19.152467", - "event_type": "deleted", - "message_id": 1455559114519482421, - "channel_id": 1369669999677145098, - "channel_name": "╭💬・général", - "author_id": 971446412690722826, - "author_name": "gameurpro12", - "author_roles": [ - 1369669999345537145, - 1369669999366770723, - 1403041729330024508, - 1369669999366770726 - ], - "content": "gg", - "attachments": [], - "embeds": [], - "details": {} - }, - { - "timestamp": "2025-12-30T14:52:21.275434", - "event_type": "deleted", - "message_id": 1455559116280828005, - "channel_id": 1369669999677145098, - "channel_name": "╭💬・général", - "author_id": 971446412690722826, - "author_name": "gameurpro12", - "author_roles": [ - 1369669999345537145, - 1369669999366770723, - 1403041729330024508, - 1369669999366770726 - ], - "content": "g", - "attachments": [], - "embeds": [], - "details": {} - }, - { - "timestamp": "2025-12-30T14:52:21.407804", - "event_type": "deleted", - "message_id": 1455559118264729792, - "channel_id": 1369669999677145098, - "channel_name": "╭💬・général", - "author_id": 971446412690722826, - "author_name": "gameurpro12", - "author_roles": [ - 1369669999345537145, - 1369669999366770723, - 1403041729330024508, - 1369669999366770726 - ], - "content": "g", - "attachments": [], - "embeds": [], - "details": {} - }, - { - "timestamp": "2025-12-30T15:00:35.602064", - "event_type": "deleted", - "message_id": 1455561207493955786, - "channel_id": 1369669999677145098, - "channel_name": "╭💬・général", - "author_id": 971446412690722826, - "author_name": "gameurpro12", - "author_roles": [ - 1369669999345537145, - 1369669999366770723, - 1403041729330024508, - 1369669999366770726 - ], - "content": "g", - "attachments": [], - "embeds": [], - "details": {} - }, - { - "timestamp": "2025-12-30T15:00:35.638746", - "event_type": "deleted", - "message_id": 1455561208953442438, - "channel_id": 1369669999677145098, - "channel_name": "╭💬・général", - "author_id": 971446412690722826, - "author_name": "gameurpro12", - "author_roles": [ - 1369669999345537145, - 1369669999366770723, - 1403041729330024508, - 1369669999366770726 - ], - "content": "gg", - "attachments": [], - "embeds": [], - "details": {} - }, - { - "timestamp": "2025-12-30T15:00:36.363545", - "event_type": "deleted", - "message_id": 1455561211679604820, - "channel_id": 1369669999677145098, - "channel_name": "╭💬・général", - "author_id": 971446412690722826, - "author_name": "gameurpro12", - "author_roles": [ - 1369669999345537145, - 1369669999366770723, - 1403041729330024508, - 1369669999366770726 - ], - "content": "gg", - "attachments": [], - "embeds": [], - "details": {} - }, - { - "timestamp": "2025-12-30T15:00:36.826764", - "event_type": "deleted", - "message_id": 1455561214045454437, - "channel_id": 1369669999677145098, - "channel_name": "╭💬・général", - "author_id": 971446412690722826, - "author_name": "gameurpro12", - "author_roles": [ - 1369669999345537145, - 1369669999366770723, - 1403041729330024508, - 1369669999366770726 - ], - "content": "g", - "attachments": [], - "embeds": [], - "details": {} - }, - { - "timestamp": "2025-12-30T15:00:37.001960", - "event_type": "deleted", - "message_id": 1455561215060480071, - "channel_id": 1369669999677145098, - "channel_name": "╭💬・général", - "author_id": 971446412690722826, - "author_name": "gameurpro12", - "author_roles": [ - 1369669999345537145, - 1369669999366770723, - 1403041729330024508, - 1369669999366770726 - ], - "content": "g", - "attachments": [], - "embeds": [], - "details": {} - }, - { - "timestamp": "2025-12-30T15:00:37.594651", - "event_type": "deleted", - "message_id": 1455561215970644102, - "channel_id": 1369669999677145098, - "channel_name": "╭💬・général", - "author_id": 971446412690722826, - "author_name": "gameurpro12", - "author_roles": [ - 1369669999345537145, - 1369669999366770723, - 1403041729330024508, - 1369669999366770726 - ], - "content": "g\ng", - "attachments": [], - "embeds": [], - "details": {} - }, - { - "timestamp": "2025-12-30T15:00:37.727681", - "event_type": "deleted", - "message_id": 1455561217610612777, - "channel_id": 1369669999677145098, - "channel_name": "╭💬・général", - "author_id": 971446412690722826, - "author_name": "gameurpro12", - "author_roles": [ - 1369669999345537145, - 1369669999366770723, - 1403041729330024508, - 1369669999366770726 - ], - "content": "g", - "attachments": [], - "embeds": [], - "details": {} - }, - { - "timestamp": "2025-12-30T15:00:40.753645", - "event_type": "deleted", - "message_id": 1455561219493728380, - "channel_id": 1369669999677145098, - "channel_name": "╭💬・général", - "author_id": 971446412690722826, - "author_name": "gameurpro12", - "author_roles": [ - 1369669999345537145, - 1369669999366770723, - 1403041729330024508, - 1369669999366770726 - ], - "content": "g", - "attachments": [], - "embeds": [], - "details": {} - }, - { - "timestamp": "2025-12-30T15:04:21.499784", - "event_type": "deleted", - "message_id": 1455562138142642443, - "channel_id": 1369669999677145098, - "channel_name": "╭💬・général", - "author_id": 971446412690722826, - "author_name": "gameurpro12", - "author_roles": [ - 1369669999345537145, - 1369669999366770723, - 1403041729330024508, - 1369669999366770726 - ], - "content": "g", - "attachments": [], - "embeds": [], - "details": {} - }, - { - "timestamp": "2025-12-30T15:04:23.080095", - "event_type": "deleted", - "message_id": 1455562136209199266, - "channel_id": 1369669999677145098, - "channel_name": "╭💬・général", - "author_id": 971446412690722826, - "author_name": "gameurpro12", - "author_roles": [ - 1369669999345537145, - 1369669999366770723, - 1403041729330024508, - 1369669999366770726 - ], - "content": "g", - "attachments": [], - "embeds": [], - "details": {} - }, - { - "timestamp": "2025-12-30T15:04:24.026167", - "event_type": "deleted", - "message_id": 1455562134749581559, - "channel_id": 1369669999677145098, - "channel_name": "╭💬・général", - "author_id": 971446412690722826, - "author_name": "gameurpro12", - "author_roles": [ - 1369669999345537145, - 1369669999366770723, - 1403041729330024508, - 1369669999366770726 - ], - "content": "g", - "attachments": [], - "embeds": [], - "details": {} - }, - { - "timestamp": "2025-12-30T15:04:25.951553", - "event_type": "deleted", - "message_id": 1455562133088637020, - "channel_id": 1369669999677145098, - "channel_name": "╭💬・général", - "author_id": 971446412690722826, - "author_name": "gameurpro12", - "author_roles": [ - 1369669999345537145, - 1369669999366770723, - 1403041729330024508, - 1369669999366770726 - ], - "content": "g", - "attachments": [], - "embeds": [], - "details": {} - }, - { - "timestamp": "2025-12-30T15:04:27.294471", - "event_type": "deleted", - "message_id": 1455562131473961092, - "channel_id": 1369669999677145098, - "channel_name": "╭💬・général", - "author_id": 971446412690722826, - "author_name": "gameurpro12", - "author_roles": [ - 1369669999345537145, - 1369669999366770723, - 1403041729330024508, - 1369669999366770726 - ], - "content": "g", - "attachments": [], - "embeds": [], - "details": {} - }, - { - "timestamp": "2025-12-30T15:04:28.245179", - "event_type": "deleted", - "message_id": 1455562129750102081, - "channel_id": 1369669999677145098, - "channel_name": "╭💬・général", - "author_id": 971446412690722826, - "author_name": "gameurpro12", - "author_roles": [ - 1369669999345537145, - 1369669999366770723, - 1403041729330024508, - 1369669999366770726 - ], - "content": "g", - "attachments": [], - "embeds": [], - "details": {} - }, - { - "timestamp": "2025-12-30T15:04:29.191484", - "event_type": "deleted", - "message_id": 1455562128076312689, - "channel_id": 1369669999677145098, - "channel_name": "╭💬・général", - "author_id": 971446412690722826, - "author_name": "gameurpro12", - "author_roles": [ - 1369669999345537145, - 1369669999366770723, - 1403041729330024508, - 1369669999366770726 - ], - "content": "gg", - "attachments": [], - "embeds": [], - "details": {} - }, - { - "timestamp": "2025-12-30T15:04:30.278917", - "event_type": "deleted", - "message_id": 1455562127220801537, - "channel_id": 1369669999677145098, - "channel_name": "╭💬・général", - "author_id": 971446412690722826, - "author_name": "gameurpro12", - "author_roles": [ - 1369669999345537145, - 1369669999366770723, - 1403041729330024508, - 1369669999366770726 - ], - "content": "g", - "attachments": [], - "embeds": [], - "details": {} - }, - { - "timestamp": "2025-12-30T15:36:52.387291", - "event_type": "deleted", - "message_id": 1455570313935847598, - "channel_id": 1369669999677145098, - "channel_name": "╭💬・général", - "author_id": 971446412690722826, - "author_name": "gameurpro12", - "author_roles": [ - 1369669999345537145, - 1369669999366770723, - 1403041729330024508, - 1369669999366770726 - ], - "content": "g", - "attachments": [], - "embeds": [], - "details": {} - }, - { - "timestamp": "2025-12-30T15:37:05.116440", - "event_type": "deleted", - "message_id": 1455570329702367242, - "channel_id": 1369669999677145098, - "channel_name": "╭💬・général", - "author_id": 971446412690722826, - "author_name": "gameurpro12", - "author_roles": [ - 1369669999345537145, - 1369669999366770723, - 1403041729330024508, - 1369669999366770726 - ], - "content": "gg", - "attachments": [], - "embeds": [], - "details": {} - }, - { - "timestamp": "2025-12-30T15:42:16.699177", - "event_type": "deleted", - "message_id": 1455571624299462862, - "channel_id": 1369669999677145098, - "channel_name": "╭💬・général", - "author_id": 971446412690722826, - "author_name": "gameurpro12", - "author_roles": [ - 1369669999345537145, - 1369669999366770723, - 1403041729330024508, - 1369669999366770726 - ], - "content": "https://www.ecoledirecte.com/login?cameFrom=%2FE%2F5835%2FEmploiDuTemps", - "attachments": [], - "embeds": [], - "details": {} - }, - { - "timestamp": "2025-12-30T15:42:24.379846", - "event_type": "deleted", - "message_id": 1455571718037962854, - "channel_id": 1369669999677145098, - "channel_name": "╭💬・général", - "author_id": 971446412690722826, - "author_name": "gameurpro12", - "author_roles": [ - 1369669999345537145, - 1369669999366770723, - 1403041729330024508, - 1369669999366770726 - ], - "content": "https://www.ecoledirecte.com/login?cameFrom=%2FE%2F5835%2FEmploiDuTemps", - "attachments": [], - "embeds": [], - "details": {} - } -] \ No newline at end of file diff --git a/data/logs/messages_2026-01-01.json b/data/logs/messages_2026-01-01.json deleted file mode 100644 index 18036c1..0000000 --- a/data/logs/messages_2026-01-01.json +++ /dev/null @@ -1,284 +0,0 @@ -[ - { - "timestamp": "2026-01-01T21:05:28.276062", - "event_type": "deleted", - "message_id": 1456377795595730994, - "channel_id": 1369669999677145098, - "channel_name": "╭💬・général", - "author_id": 971446412690722826, - "author_name": "gameurpro12", - "author_roles": [ - 1369669999345537145, - 1369669999366770723, - 1403041729330024508, - 1369669999366770726 - ], - "content": "g", - "attachments": [], - "embeds": [], - "details": {} - }, - { - "timestamp": "2026-01-01T21:12:06.156108", - "event_type": "deleted", - "message_id": 1456379476211531970, - "channel_id": 1369669999677145098, - "channel_name": "╭💬・général", - "author_id": 971446412690722826, - "author_name": "gameurpro12", - "author_roles": [ - 1369669999345537145, - 1369669999366770723, - 1403041729330024508, - 1369669999366770726 - ], - "content": "g", - "attachments": [], - "embeds": [], - "details": {} - }, - { - "timestamp": "2026-01-01T21:12:06.462625", - "event_type": "deleted", - "message_id": 1456379478606348421, - "channel_id": 1369669999677145098, - "channel_name": "╭💬・général", - "author_id": 971446412690722826, - "author_name": "gameurpro12", - "author_roles": [ - 1369669999345537145, - 1369669999366770723, - 1403041729330024508, - 1369669999366770726 - ], - "content": "gg", - "attachments": [], - "embeds": [], - "details": {} - }, - { - "timestamp": "2026-01-01T21:12:12.104726", - "event_type": "deleted", - "message_id": 1456379484264595700, - "channel_id": 1369669999677145098, - "channel_name": "╭💬・général", - "author_id": 971446412690722826, - "author_name": "gameurpro12", - "author_roles": [ - 1369669999345537145, - 1369669999366770723, - 1403041729330024508, - 1369669999366770726 - ], - "content": "g", - "attachments": [], - "embeds": [], - "details": {} - }, - { - "timestamp": "2026-01-01T21:35:44.759055", - "event_type": "deleted", - "message_id": 1456385391564296297, - "channel_id": 1369669999677145098, - "channel_name": "╭💬・général", - "author_id": 971446412690722826, - "author_name": "gameurpro12", - "author_roles": [ - 1369669999345537145, - 1369669999366770723, - 1403041729330024508, - 1369669999366770726 - ], - "content": "g", - "attachments": [], - "embeds": [], - "details": {} - }, - { - "timestamp": "2026-01-01T21:35:51.064634", - "event_type": "deleted", - "message_id": 1456385392482844682, - "channel_id": 1369669999677145098, - "channel_name": "╭💬・général", - "author_id": 971446412690722826, - "author_name": "gameurpro12", - "author_roles": [ - 1369669999345537145, - 1369669999366770723, - 1403041729330024508, - 1369669999366770726 - ], - "content": "g", - "attachments": [], - "embeds": [], - "details": {} - }, - { - "timestamp": "2026-01-01T21:40:18.016792", - "event_type": "deleted", - "message_id": 1456386513947463855, - "channel_id": 1369669999677145098, - "channel_name": "╭💬・général", - "author_id": 971446412690722826, - "author_name": "gameurpro12", - "author_roles": [ - 1369669999345537145, - 1369669999366770723, - 1403041729330024508, - 1369669999366770726 - ], - "content": "g", - "attachments": [], - "embeds": [], - "details": {} - }, - { - "timestamp": "2026-01-01T21:40:21.147093", - "event_type": "deleted", - "message_id": 1456386515096703242, - "channel_id": 1369669999677145098, - "channel_name": "╭💬・général", - "author_id": 971446412690722826, - "author_name": "gameurpro12", - "author_roles": [ - 1369669999345537145, - 1369669999366770723, - 1403041729330024508, - 1369669999366770726 - ], - "content": "g", - "attachments": [], - "embeds": [], - "details": {} - }, - { - "timestamp": "2026-01-01T21:40:24.322926", - "event_type": "deleted", - "message_id": 1456386516439007288, - "channel_id": 1369669999677145098, - "channel_name": "╭💬・général", - "author_id": 971446412690722826, - "author_name": "gameurpro12", - "author_roles": [ - 1369669999345537145, - 1369669999366770723, - 1403041729330024508, - 1369669999366770726 - ], - "content": "g", - "attachments": [], - "embeds": [], - "details": {} - }, - { - "timestamp": "2026-01-01T21:43:53.352930", - "event_type": "deleted", - "message_id": 1456387431472435231, - "channel_id": 1369669999677145098, - "channel_name": "╭💬・général", - "author_id": 971446412690722826, - "author_name": "gameurpro12", - "author_roles": [ - 1369669999345537145, - 1369669999366770723, - 1403041729330024508, - 1369669999366770726 - ], - "content": "g", - "attachments": [], - "embeds": [], - "details": {} - }, - { - "timestamp": "2026-01-01T21:44:15.896248", - "event_type": "deleted", - "message_id": 1456387423477956670, - "channel_id": 1369669999677145098, - "channel_name": "╭💬・général", - "author_id": 971446412690722826, - "author_name": "gameurpro12", - "author_roles": [ - 1369669999345537145, - 1369669999366770723, - 1403041729330024508, - 1369669999366770726 - ], - "content": "g", - "attachments": [], - "embeds": [], - "details": {} - }, - { - "timestamp": "2026-01-01T21:44:32.743792", - "event_type": "deleted", - "message_id": 1456387525080776788, - "channel_id": 1369669999677145098, - "channel_name": "╭💬・général", - "author_id": 971446412690722826, - "author_name": "gameurpro12", - "author_roles": [ - 1369669999345537145, - 1369669999366770723, - 1403041729330024508, - 1369669999366770726 - ], - "content": "https://github.com/Omega-Kube/Kuby", - "attachments": [], - "embeds": [], - "details": {} - }, - { - "timestamp": "2026-01-01T21:44:43.533530", - "event_type": "deleted", - "message_id": 1456387651883237437, - "channel_id": 1369669999677145098, - "channel_name": "╭💬・général", - "author_id": 971446412690722826, - "author_name": "gameurpro12", - "author_roles": [ - 1369669999345537145, - 1369669999366770723, - 1403041729330024508, - 1369669999366770726 - ], - "content": "https://github.com/Omega-Kube/Kuby", - "attachments": [], - "embeds": [], - "details": {} - }, - { - "timestamp": "2026-01-01T21:51:23.069501", - "event_type": "deleted", - "message_id": 1456389356301582459, - "channel_id": 1369669999677145098, - "channel_name": "╭💬・général", - "author_id": 971446412690722826, - "author_name": "gameurpro12", - "author_roles": [ - 1369669999345537145, - 1369669999366770723, - 1403041729330024508, - 1369669999366770726 - ], - "content": "[DEBUG] anti_link_enabled: True, settings: {'anti_spam': True, 'anti_perm': True, 'anti_salon': True, 'anti_raid': True, 'anti_nuke': True, 'anti_link': True, 'anti_mention': True, 'verification_enabled': True, 'spam_threshold': 5, 'raid_threshold': 3, 'nuke_threshold': 3, 'log_channel_id': 1396088860412346428, 'lockdown_enabled': False, 'auto_ban': True, 'webhook_protection': True, 'mass_delete_protection': True, 'invite_filter': True, 'file_scanning': True, 'ai_moderation': True, 'channel_block_enabled': False, 'server_logs': True, 'anti_channel_delete': True, 'anti_role_create': True}", - "attachments": [], - "embeds": [], - "details": {} - }, - { - "timestamp": "2026-01-01T22:01:31.237385", - "event_type": "deleted", - "message_id": 1456391913694433361, - "channel_id": 1369669999677145098, - "channel_name": "╭💬・général", - "author_id": 1456390746167967932, - "author_name": "juibu_66388", - "author_roles": [ - 1369669999345537145 - ], - "content": "https://github.com/Omega-Kube/Kuby", - "attachments": [], - "embeds": [], - "details": {} - } -] \ No newline at end of file diff --git a/data/logs/messages_2026-01-02.json b/data/logs/messages_2026-01-02.json deleted file mode 100644 index b4bebd2..0000000 --- a/data/logs/messages_2026-01-02.json +++ /dev/null @@ -1,59 +0,0 @@ -[ - { - "timestamp": "2026-01-02T13:30:27.725564", - "event_type": "deleted", - "message_id": 1456625676537823243, - "channel_id": 1369669999677145098, - "channel_name": "╭💬・général", - "author_id": 971446412690722826, - "author_name": "gameurpro12", - "author_roles": [ - 1369669999345537145, - 1369669999366770723, - 1403041729330024508, - 1369669999366770726 - ], - "content": "gg", - "attachments": [], - "embeds": [], - "details": {} - }, - { - "timestamp": "2026-01-02T13:30:27.754495", - "event_type": "deleted", - "message_id": 1456625676537823243, - "channel_id": 1369669999677145098, - "channel_name": "╭💬・général", - "author_id": 971446412690722826, - "author_name": "gameurpro12", - "author_roles": [ - 1369669999345537145, - 1369669999366770723, - 1403041729330024508, - 1369669999366770726 - ], - "content": "gg", - "attachments": [], - "embeds": [], - "details": {} - }, - { - "timestamp": "2026-01-02T13:30:37.215876", - "event_type": "deleted", - "message_id": 1456625680153444448, - "channel_id": 1369669999677145098, - "channel_name": "╭💬・général", - "author_id": 971446412690722826, - "author_name": "gameurpro12", - "author_roles": [ - 1369669999345537145, - 1369669999366770723, - 1403041729330024508, - 1369669999366770726 - ], - "content": "gg", - "attachments": [], - "embeds": [], - "details": {} - } -] \ No newline at end of file diff --git a/data/logs/messages_2026-01-10.json b/data/logs/messages_2026-01-10.json deleted file mode 100644 index e4dede0..0000000 --- a/data/logs/messages_2026-01-10.json +++ /dev/null @@ -1,25 +0,0 @@ -[ - { - "timestamp": "2026-01-10T00:57:48.563188", - "event_type": "edited", - "message_id": 1459335326660300935, - "channel_id": 1369669999677145098, - "channel_name": "╭💬・général", - "author_id": 971446412690722826, - "author_name": "gameurpro12", - "author_roles": [ - 1369669999345537145, - 1369769836028231730, - 1369669999366770723, - 1403041729330024508, - 1369669999366770726 - ], - "content": "Non c'est gentil t'inqiutes", - "attachments": [], - "embeds": [], - "details": { - "before_content": "Non c'est gentil t'inqiutes", - "after_content": "Non c'est gentil t’inquiètes" - } - } -] \ No newline at end of file diff --git a/data/logs/messages_2026-01-24.json b/data/logs/messages_2026-01-24.json deleted file mode 100644 index 7d4b1ae..0000000 --- a/data/logs/messages_2026-01-24.json +++ /dev/null @@ -1,77 +0,0 @@ -[ - { - "timestamp": "2026-01-24T15:05:23.725825", - "event_type": "edited", - "message_id": 1464622068598575155, - "channel_id": 1464621977376522291, - "channel_name": "gameurpro12-écrit", - "author_id": 971446412690722826, - "author_name": "gameurpro12", - "author_roles": [ - 1366461904976871565, - 1453708383235473459, - 1454035592391299234, - 1454034686979346495, - 1453708344526246071, - 1454034988080038040, - 1366876636259024946 - ], - "content": "Les tickets prendront cette forme", - "attachments": [], - "embeds": [], - "details": { - "before_content": "Les tickets prendront cette forme", - "after_content": "Les tickets whitelist prendront cette forme" - } - }, - { - "timestamp": "2026-01-24T15:05:52.316927", - "event_type": "edited", - "message_id": 1464622206439919658, - "channel_id": 1460048825665454344, - "channel_name": "🔢・nombres-infinis", - "author_id": 1355564506754973767, - "author_name": "alto1500", - "author_roles": [ - 1366461904976871565, - 1453708383235473459, - 1454042474141192316, - 1453708358417912075 - ], - "content": "24", - "attachments": [], - "embeds": [], - "details": { - "before_content": "24", - "after_content": "240" - } - }, - { - "timestamp": "2026-01-24T15:23:49.868209", - "event_type": "edited", - "message_id": 1464626575415705761, - "channel_id": 1453386193311105188, - "channel_name": "📡・annonces", - "author_id": 983747660265693258, - "author_name": "mr_reiko_", - "author_roles": [ - 1366461904976871565, - 1453708383235473459, - 1454035592391299234, - 1454033803998920769, - 1454034686979346495, - 1464424459393040667, - 1454034988080038040, - 1453395626128707656, - 1369003041927200858, - 1366876636259024946 - ], - "content": "> # **__Annonce – Ouverture officielle des Whitelist__** <:emoji_1:1458776441041850430> \n> \n> **Nous vous informons que les Whitelist sont officiellement ouvertes.** \n> \n> - Pour effectuer votre WL,** il vous suffit de créer un ticket dans le salon** <#1450002116537090149> / <#1463081283679817822> prévu à cet effet.\n> Une fois le ticket ouvert, **un membre du staff prendra votre demande en charge,** vous expliquera l’ensemble de la procédure et **vous accompagnera tout au long du processus.** \n> \n> - __**Rôles disponibles en semi FA**__\n> **Les rôles suivants sont actuellement disponibles en semi FA pour les joueurs WL ecrite effectuée :**\n> \n> <@&1453708467700502561> \n> \n> <@&1453708401933553849> \n> \n> <@&1453708560511926273> \n> \n> <@&1453708517302337695> \n> \n> <@&1453708475132674070> \n> \n> <@&1453708562474860617> \n> \n> - **Merci de respecter les consignes et de faire preuve de sérieux lors de votre démarche.**\n> **Nous restons à votre disposition via les tickets pour toute question.** <:50773approvedids:1464433520301572096> £\n> \n> **Cordialement <@&1458084692803981354> / <@&1453708378336399392> /** \n> \n> ||@everyone ||", - "attachments": [], - "embeds": [], - "details": { - "before_content": "> # **__Annonce – Ouverture officielle des Whitelist__** <:emoji_1:1458776441041850430> \n> \n> **Nous vous informons que les Whitelist sont officiellement ouvertes.** \n> \n> - Pour effectuer votre WL,** il vous suffit de créer un ticket dans le salon** <#1450002116537090149> / <#1463081283679817822> prévu à cet effet.\n> Une fois le ticket ouvert, **un membre du staff prendra votre demande en charge,** vous expliquera l’ensemble de la procédure et **vous accompagnera tout au long du processus.** \n> \n> - __**Rôles disponibles en semi FA**__\n> **Les rôles suivants sont actuellement disponibles en semi FA pour les joueurs WL ecrite effectuée :**\n> \n> <@&1453708467700502561> \n> \n> <@&1453708401933553849> \n> \n> <@&1453708560511926273> \n> \n> <@&1453708517302337695> \n> \n> <@&1453708475132674070> \n> \n> <@&1453708562474860617> \n> \n> - **Merci de respecter les consignes et de faire preuve de sérieux lors de votre démarche.**\n> **Nous restons à votre disposition via les tickets pour toute question.** <:50773approvedids:1464433520301572096> £\n> \n> **Cordialement <@&1458084692803981354> / <@&1453708378336399392> /** \n> \n> ||@everyone ||", - "after_content": "> # **__Annonce – Ouverture officielle des Whitelist__** <:emoji_1:1458776441041850430> \n> \n> **Nous vous informons que les Whitelist sont officiellement ouvertes.** \n> \n> - Pour effectuer votre WL,** il vous suffit de créer un ticket dans le salon** <#1450002116537090149> / <#1463081283679817822> prévu à cet effet.\n> Une fois le ticket ouvert, **un membre du staff prendra votre demande en charge,** vous expliquera l’ensemble de la procédure et **vous accompagnera tout au long du processus.** \n> \n> - __**Rôles disponibles en semi FA**__\n> **Les rôles suivants sont actuellement disponibles en semi FA pour les joueurs WL ecrite effectuée :**\n> \n> <@&1453708467700502561> \n> \n> <@&1453708401933553849> \n> \n> <@&1453708560511926273> \n> \n> <@&1453708517302337695> \n> \n> <@&1453708475132674070> \n> \n> <@&1453708562474860617> \n> \n> - **Merci de respecter les consignes et de faire preuve de sérieux lors de votre démarche.**\n> **Nous restons à votre disposition via les tickets pour toute question.** <:50773approvedids:1464433520301572096> \n> \n> **Cordialement <@&1458084692803981354> / <@&1453708378336399392> /** \n> \n> ||@everyone ||" - } - } -] \ No newline at end of file diff --git a/data/logs/messages_2026-02-27.json b/data/logs/messages_2026-02-27.json deleted file mode 100644 index ec16e47..0000000 --- a/data/logs/messages_2026-02-27.json +++ /dev/null @@ -1,38 +0,0 @@ -[ - { - "timestamp": "2026-02-27T16:51:53.636507", - "event_type": "deleted", - "message_id": 1476970101038125148, - "channel_id": 1369669999677145103, - "channel_name": "╰📂・rapports", - "author_id": 971446412690722826, - "author_name": "gameurpro12", - "author_roles": [ - 1369669999345537145, - 1369769836028231730, - 1369669999366770726 - ], - "content": "1369669999677145103", - "attachments": [], - "embeds": [], - "details": {} - }, - { - "timestamp": "2026-02-27T16:52:08.494696", - "event_type": "deleted", - "message_id": 1476970163726188639, - "channel_id": 1369669999677145103, - "channel_name": "╰📂・rapports", - "author_id": 971446412690722826, - "author_name": "gameurpro12", - "author_roles": [ - 1369669999345537145, - 1369769836028231730, - 1369669999366770726 - ], - "content": "<@&1369669999345537154>", - "attachments": [], - "embeds": [], - "details": {} - } -] \ No newline at end of file diff --git a/data/logs/voice_2025-08-19.json b/data/logs/voice_2025-08-19.json deleted file mode 100755 index 3d4c9c7..0000000 --- a/data/logs/voice_2025-08-19.json +++ /dev/null @@ -1,146 +0,0 @@ -[ - { - "timestamp": "2025-08-19T20:20:09.529504", - "event_type": "joined", - "member_id": 353942721997832195, - "member_name": "mg_studios_", - "channel_id": 1369669999677145105, - "channel_name": "╭🎤・Général", - "channel_type": "voice" - }, - { - "timestamp": "2025-08-19T20:20:10.360577", - "event_type": "joined", - "member_id": 971446412690722826, - "member_name": "gameurpro12", - "channel_id": 1369669999677145105, - "channel_name": "╭🎤・Général", - "channel_type": "voice" - }, - { - "timestamp": "2025-08-19T20:20:16.614354", - "event_type": "joined", - "member_id": 872192738920108074, - "member_name": "titis_zfr_7007", - "channel_id": 1369669999677145105, - "channel_name": "╭🎤・Général", - "channel_type": "voice" - }, - { - "timestamp": "2025-08-19T20:53:27.763722", - "event_type": "left", - "member_id": 872192738920108074, - "member_name": "titis_zfr_7007", - "channel_id": 1369669999677145105, - "channel_name": "╭🎤・Général", - "channel_type": "voice" - }, - { - "timestamp": "2025-08-19T21:22:53.512388", - "event_type": "joined", - "member_id": 768915179995004988, - "member_name": "fantom_arn", - "channel_id": 1369669999677145105, - "channel_name": "╭🎤・Général", - "channel_type": "voice" - }, - { - "timestamp": "2025-08-19T21:23:56.602997", - "event_type": "left", - "member_id": 768915179995004988, - "member_name": "fantom_arn", - "channel_id": 1369669999677145105, - "channel_name": "╭🎤・Général", - "channel_type": "voice" - }, - { - "timestamp": "2025-08-19T21:39:13.888020", - "event_type": "joined", - "member_id": 768915179995004988, - "member_name": "fantom_arn", - "channel_id": 1369669999677145105, - "channel_name": "╭🎤・Général", - "channel_type": "voice" - }, - { - "timestamp": "2025-08-19T21:40:19.180475", - "event_type": "left", - "member_id": 768915179995004988, - "member_name": "fantom_arn", - "channel_id": 1369669999677145105, - "channel_name": "╭🎤・Général", - "channel_type": "voice" - }, - { - "timestamp": "2025-08-19T21:44:18.819339", - "event_type": "joined", - "member_id": 1108087282692522105, - "member_name": "tricks_zz", - "channel_id": 1369669999677145105, - "channel_name": "╭🎤・Général", - "channel_type": "voice" - }, - { - "timestamp": "2025-08-19T21:54:19.095370", - "event_type": "joined", - "member_id": 1069386396764221450, - "member_name": "matlo13", - "channel_id": 1369669999677145105, - "channel_name": "╭🎤・Général", - "channel_type": "voice" - }, - { - "timestamp": "2025-08-19T22:12:16.991281", - "event_type": "left", - "member_id": 1108087282692522105, - "member_name": "tricks_zz", - "channel_id": 1369669999677145105, - "channel_name": "╭🎤・Général", - "channel_type": "voice" - }, - { - "timestamp": "2025-08-19T22:14:16.428151", - "event_type": "joined", - "member_id": 768915179995004988, - "member_name": "fantom_arn", - "channel_id": 1369669999677145105, - "channel_name": "╭🎤・Général", - "channel_type": "voice" - }, - { - "timestamp": "2025-08-19T22:21:48.361355", - "event_type": "left", - "member_id": 971446412690722826, - "member_name": "gameurpro12", - "channel_id": 1369669999677145105, - "channel_name": "╭🎤・Général", - "channel_type": "voice" - }, - { - "timestamp": "2025-08-19T22:21:50.542994", - "event_type": "left", - "member_id": 353942721997832195, - "member_name": "mg_studios_", - "channel_id": 1369669999677145105, - "channel_name": "╭🎤・Général", - "channel_type": "voice" - }, - { - "timestamp": "2025-08-19T22:23:14.826669", - "event_type": "left", - "member_id": 768915179995004988, - "member_name": "fantom_arn", - "channel_id": 1369669999677145105, - "channel_name": "╭🎤・Général", - "channel_type": "voice" - }, - { - "timestamp": "2025-08-19T22:23:15.993478", - "event_type": "left", - "member_id": 1069386396764221450, - "member_name": "matlo13", - "channel_id": 1369669999677145105, - "channel_name": "╭🎤・Général", - "channel_type": "voice" - } -] \ No newline at end of file diff --git a/data/logs/voice_2025-08-20.json b/data/logs/voice_2025-08-20.json deleted file mode 100755 index eed28d4..0000000 --- a/data/logs/voice_2025-08-20.json +++ /dev/null @@ -1,20 +0,0 @@ -[ - { - "timestamp": "2025-08-20T12:28:09.674559", - "event_type": "joined", - "member_id": 1407670411164127242, - "member_name": "test_109375", - "channel_id": 1369669999677145105, - "channel_name": "╭🎤・Général", - "channel_type": "voice" - }, - { - "timestamp": "2025-08-20T12:28:17.126525", - "event_type": "left", - "member_id": 1407670411164127242, - "member_name": "test_109375", - "channel_id": 1369669999677145105, - "channel_name": "╭🎤・Général", - "channel_type": "voice" - } -] \ No newline at end of file diff --git a/data/logs/voice_2026-01-24.json b/data/logs/voice_2026-01-24.json deleted file mode 100644 index 234ef8a..0000000 --- a/data/logs/voice_2026-01-24.json +++ /dev/null @@ -1,245 +0,0 @@ -[ - { - "timestamp": "2026-01-24T15:04:34.353302", - "event_type": "left", - "member_id": 1453734314088075306, - "member_name": "patalhuile", - "channel_id": 1407722372588376084, - "channel_name": "〚🌟〛𝐊𝐀𝐘𝐑𝐈𝐗", - "channel_type": "voice" - }, - { - "timestamp": "2026-01-24T15:06:06.060691", - "event_type": "joined", - "member_id": 971446412690722826, - "member_name": "gameurpro12", - "channel_id": 1457073413616046080, - "channel_name": "👑・Bureau Fondation", - "channel_type": "voice" - }, - { - "timestamp": "2026-01-24T15:06:12.905500", - "event_type": "left", - "member_id": 971446412690722826, - "member_name": "gameurpro12", - "channel_id": 1457073413616046080, - "channel_name": "👑・Bureau Fondation", - "channel_type": "voice" - }, - { - "timestamp": "2026-01-24T15:12:23.224137", - "event_type": "joined", - "member_id": 983747660265693258, - "member_name": "mr_reiko_", - "channel_id": 1453397135234957442, - "channel_name": "📓・Bureaux 1", - "channel_type": "voice" - }, - { - "timestamp": "2026-01-24T15:12:25.430175", - "event_type": "left", - "member_id": 983747660265693258, - "member_name": "mr_reiko_", - "channel_id": 1453397135234957442, - "channel_name": "📓・Bureaux 1", - "channel_type": "voice" - }, - { - "timestamp": "2026-01-24T15:12:30.909163", - "event_type": "left", - "member_id": 1263573205268828231, - "member_name": "arthena_931", - "channel_id": 1457073413616046080, - "channel_name": "👑・Bureau Fondation", - "channel_type": "voice" - }, - { - "timestamp": "2026-01-24T15:13:02.024297", - "event_type": "left", - "member_id": 353942721997832195, - "member_name": "mg_studios_", - "channel_id": 1457073413616046080, - "channel_name": "👑・Bureau Fondation", - "channel_type": "voice" - }, - { - "timestamp": "2026-01-24T15:13:17.531634", - "event_type": "joined", - "member_id": 1263573205268828231, - "member_name": "arthena_931", - "channel_id": 1447649079952802055, - "channel_name": "🔊・Publique 1", - "channel_type": "voice" - }, - { - "timestamp": "2026-01-24T15:13:26.906602", - "event_type": "left", - "member_id": 1263573205268828231, - "member_name": "arthena_931", - "channel_id": 1447649079952802055, - "channel_name": "🔊・Publique 1", - "channel_type": "voice" - }, - { - "timestamp": "2026-01-24T15:15:50.817844", - "event_type": "joined", - "member_id": 1453734314088075306, - "member_name": "patalhuile", - "channel_id": 1407722372588376084, - "channel_name": "〚🌟〛𝐊𝐀𝐘𝐑𝐈𝐗", - "channel_type": "voice" - }, - { - "timestamp": "2026-01-24T15:18:40.775427", - "event_type": "left", - "member_id": 945580436732641320, - "member_name": "melhoan", - "channel_id": 1407722372588376084, - "channel_name": "〚🌟〛𝐊𝐀𝐘𝐑𝐈𝐗", - "channel_type": "voice" - }, - { - "timestamp": "2026-01-24T15:18:44.516024", - "event_type": "joined", - "member_id": 945580436732641320, - "member_name": "melhoan", - "channel_id": 1407722372588376084, - "channel_name": "〚🌟〛𝐊𝐀𝐘𝐑𝐈𝐗", - "channel_type": "voice" - }, - { - "timestamp": "2026-01-24T15:20:36.058676", - "event_type": "left", - "member_id": 1453734314088075306, - "member_name": "patalhuile", - "channel_id": 1407722372588376084, - "channel_name": "〚🌟〛𝐊𝐀𝐘𝐑𝐈𝐗", - "channel_type": "voice" - }, - { - "timestamp": "2026-01-24T15:27:14.017625", - "event_type": "left", - "member_id": 945580436732641320, - "member_name": "melhoan", - "channel_id": 1407722372588376084, - "channel_name": "〚🌟〛𝐊𝐀𝐘𝐑𝐈𝐗", - "channel_type": "voice" - }, - { - "timestamp": "2026-01-24T15:29:52.855600", - "event_type": "joined", - "member_id": 1263573205268828231, - "member_name": "arthena_931", - "channel_id": 1407780201462497522, - "channel_name": "〚🔈〛𝐀𝐓𝐓𝐄𝐍𝐓𝐄 𝐌𝐎𝐎𝐕", - "channel_type": "voice" - }, - { - "timestamp": "2026-01-24T15:31:33.895832", - "event_type": "joined", - "member_id": 1296914730669183066, - "member_name": "iikileur_02378", - "channel_id": 1447649079952802055, - "channel_name": "🔊・Publique 1", - "channel_type": "voice" - }, - { - "timestamp": "2026-01-24T15:31:44.051002", - "event_type": "moved_from", - "member_id": 534003230435442690, - "member_name": "nixfix06", - "channel_id": 1457073413616046080, - "channel_name": "👑・Bureau Fondation", - "channel_type": "voice" - }, - { - "timestamp": "2026-01-24T15:31:44.051366", - "event_type": "moved_to", - "member_id": 534003230435442690, - "member_name": "nixfix06", - "channel_id": 1447649079952802055, - "channel_name": "🔊・Publique 1", - "channel_type": "voice" - }, - { - "timestamp": "2026-01-24T15:33:48.580429", - "event_type": "joined", - "member_id": 1320118755090759711, - "member_name": "jl_xixo", - "channel_id": 1447649079952802055, - "channel_name": "🔊・Publique 1", - "channel_type": "voice" - }, - { - "timestamp": "2026-01-24T15:33:51.104140", - "event_type": "joined", - "member_id": 1389671060902777056, - "member_name": "elite_v.2_70323", - "channel_id": 1407780201462497522, - "channel_name": "〚🔈〛𝐀𝐓𝐓𝐄𝐍𝐓𝐄 𝐌𝐎𝐎𝐕", - "channel_type": "voice" - }, - { - "timestamp": "2026-01-24T15:35:01.627777", - "event_type": "left", - "member_id": 1389671060902777056, - "member_name": "elite_v.2_70323", - "channel_id": 1407780201462497522, - "channel_name": "〚🔈〛𝐀𝐓𝐓𝐄𝐍𝐓𝐄 𝐌𝐎𝐎𝐕", - "channel_type": "voice" - }, - { - "timestamp": "2026-01-24T15:35:02.783293", - "event_type": "left", - "member_id": 1296914730669183066, - "member_name": "iikileur_02378", - "channel_id": 1447649079952802055, - "channel_name": "🔊・Publique 1", - "channel_type": "voice" - }, - { - "timestamp": "2026-01-24T15:35:09.067327", - "event_type": "left", - "member_id": 1263573205268828231, - "member_name": "arthena_931", - "channel_id": 1407780201462497522, - "channel_name": "〚🔈〛𝐀𝐓𝐓𝐄𝐍𝐓𝐄 𝐌𝐎𝐎𝐕", - "channel_type": "voice" - }, - { - "timestamp": "2026-01-24T15:35:15.317831", - "event_type": "moved_from", - "member_id": 534003230435442690, - "member_name": "nixfix06", - "channel_id": 1447649079952802055, - "channel_name": "🔊・Publique 1", - "channel_type": "voice" - }, - { - "timestamp": "2026-01-24T15:35:15.318377", - "event_type": "moved_to", - "member_id": 534003230435442690, - "member_name": "nixfix06", - "channel_id": 1457073756764639355, - "channel_name": "👔・Bureau Direction", - "channel_type": "voice" - }, - { - "timestamp": "2026-01-24T15:35:20.522454", - "event_type": "left", - "member_id": 1320118755090759711, - "member_name": "jl_xixo", - "channel_id": 1447649079952802055, - "channel_name": "🔊・Publique 1", - "channel_type": "voice" - }, - { - "timestamp": "2026-01-24T15:37:06.015710", - "event_type": "joined", - "member_id": 1433834565042180149, - "member_name": "alexandre07_08_13804", - "channel_id": 1447649079952802055, - "channel_name": "🔊・Publique 1", - "channel_type": "voice" - } -] \ No newline at end of file diff --git a/data/member_logs/1003956683040624672.json b/data/member_logs/1003956683040624672.json deleted file mode 100644 index 2e3df3b..0000000 --- a/data/member_logs/1003956683040624672.json +++ /dev/null @@ -1,58 +0,0 @@ -[ - { - "timestamp": "2025-12-30T01:05:11.496294", - "event_type": "member_leave", - "member_id": 1003956683040624672, - "member_name": "eva56_", - "member_discriminator": "0", - "member_avatar": "https://cdn.discordapp.com/avatars/1003956683040624672/c364667c914a821cd24212d32f7762b8.png?size=1024", - "roles": { - "role_ids": [], - "role_names": [], - "permissions": [] - }, - "joined_at": "2025-12-30T00:03:48.810667+00:00" - }, - { - "timestamp": "2025-12-30T01:06:16.615009", - "event_type": "member_leave", - "member_id": 1003956683040624672, - "member_name": "eva56_", - "member_discriminator": "0", - "member_avatar": "https://cdn.discordapp.com/avatars/1003956683040624672/c364667c914a821cd24212d32f7762b8.png?size=1024", - "roles": { - "role_ids": [], - "role_names": [], - "permissions": [] - }, - "joined_at": "2025-12-30T00:05:37.900874+00:00" - }, - { - "timestamp": "2025-12-30T01:09:36.950883", - "event_type": "member_leave", - "member_id": 1003956683040624672, - "member_name": "eva56_", - "member_discriminator": "0", - "member_avatar": "https://cdn.discordapp.com/avatars/1003956683040624672/c364667c914a821cd24212d32f7762b8.png?size=1024", - "roles": { - "role_ids": [], - "role_names": [], - "permissions": [] - }, - "joined_at": "2025-12-30T00:06:36.510813+00:00" - }, - { - "timestamp": "2025-12-30T01:13:47.629282", - "event_type": "member_leave", - "member_id": 1003956683040624672, - "member_name": "eva56_", - "member_discriminator": "0", - "member_avatar": "https://cdn.discordapp.com/avatars/1003956683040624672/c364667c914a821cd24212d32f7762b8.png?size=1024", - "roles": { - "role_ids": [], - "role_names": [], - "permissions": [] - }, - "joined_at": "2025-12-30T00:13:30.252167+00:00" - } -] \ No newline at end of file diff --git a/data/member_logs/1407448905817391215.json b/data/member_logs/1407448905817391215.json deleted file mode 100755 index 6077a0d..0000000 --- a/data/member_logs/1407448905817391215.json +++ /dev/null @@ -1,44 +0,0 @@ -[ - { - "timestamp": "2025-08-19T23:48:12.347482", - "event_type": "member_leave", - "member_id": 1407448905817391215, - "member_name": "test_33878", - "member_discriminator": "0", - "member_avatar": null, - "roles": { - "role_ids": [], - "role_names": [], - "permissions": [] - }, - "joined_at": "2025-08-19T19:41:25.391607+00:00" - }, - { - "timestamp": "2025-08-20T00:42:00.543535", - "event_type": "member_leave", - "member_id": 1407448905817391215, - "member_name": "test_33878", - "member_discriminator": "0", - "member_avatar": null, - "roles": { - "role_ids": [], - "role_names": [], - "permissions": [] - }, - "joined_at": "2025-08-19T22:40:57.711576+00:00" - }, - { - "timestamp": "2025-08-20T00:47:23.923297", - "event_type": "member_leave", - "member_id": 1407448905817391215, - "member_name": "test_33878", - "member_discriminator": "0", - "member_avatar": null, - "roles": { - "role_ids": [], - "role_names": [], - "permissions": [] - }, - "joined_at": "2025-08-19T22:47:10.020304+00:00" - } -] \ No newline at end of file diff --git a/data/member_logs/1407670411164127242.json b/data/member_logs/1407670411164127242.json deleted file mode 100755 index a409d36..0000000 --- a/data/member_logs/1407670411164127242.json +++ /dev/null @@ -1,16 +0,0 @@ -[ - { - "timestamp": "2025-08-20T12:24:08.056140", - "event_type": "member_leave", - "member_id": 1407670411164127242, - "member_name": "test_109375", - "member_discriminator": "0", - "member_avatar": null, - "roles": { - "role_ids": [], - "role_names": [], - "permissions": [] - }, - "joined_at": "2025-08-20T10:21:05.730620+00:00" - } -] \ No newline at end of file diff --git a/data/member_logs/1456390746167967932.json b/data/member_logs/1456390746167967932.json deleted file mode 100644 index a9cc32a..0000000 --- a/data/member_logs/1456390746167967932.json +++ /dev/null @@ -1,42 +0,0 @@ -[ - { - "timestamp": "2026-01-03T21:25:17.730927", - "event_type": "member_leave", - "member_id": 1456390746167967932, - "member_name": "juibu_66388", - "member_discriminator": "0", - "member_avatar": null, - "roles": { - "role_ids": [ - 1369669999366770721 - ], - "role_names": [ - "Faux banquier" - ], - "permissions": [ - 8192 - ] - }, - "joined_at": "2026-01-01T21:00:26.074000+00:00" - }, - { - "timestamp": "2026-01-03T21:49:33.975462", - "event_type": "member_leave", - "member_id": 1456390746167967932, - "member_name": "juibu_66388", - "member_discriminator": "0", - "member_avatar": null, - "roles": { - "role_ids": [ - 1369669999366770721 - ], - "role_names": [ - "Faux banquier" - ], - "permissions": [ - 8192 - ] - }, - "joined_at": "2026-01-03T20:25:28.737000+00:00" - } -] \ No newline at end of file diff --git a/data/member_logs/1470008249322438678.json b/data/member_logs/1470008249322438678.json deleted file mode 100644 index 6ad54b7..0000000 --- a/data/member_logs/1470008249322438678.json +++ /dev/null @@ -1,16 +0,0 @@ -[ - { - "timestamp": "2026-02-08T12:38:12.823655", - "event_type": "member_leave", - "member_id": 1470008249322438678, - "member_name": "truc_19275", - "member_discriminator": "0", - "member_avatar": null, - "roles": { - "role_ids": [], - "role_names": [], - "permissions": [] - }, - "joined_at": "2026-02-08T10:49:12.106083+00:00" - } -] \ No newline at end of file diff --git a/data/member_logs/318312854816161792.json b/data/member_logs/318312854816161792.json deleted file mode 100755 index d161b94..0000000 --- a/data/member_logs/318312854816161792.json +++ /dev/null @@ -1,22 +0,0 @@ -[ - { - "timestamp": "2025-08-19T14:58:16.673562", - "event_type": "member_leave", - "member_id": 318312854816161792, - "member_name": "DraftBot#0535", - "member_discriminator": "0535", - "member_avatar": "https://cdn.discordapp.com/avatars/318312854816161792/f1d4dc93441273f3f8985a07e0bad164.png?size=1024", - "roles": { - "role_ids": [ - 1403041729330024508 - ], - "role_names": [ - "🗝️" - ], - "permissions": [ - 8 - ] - }, - "joined_at": "2025-08-19T12:50:31.007000+00:00" - } -] \ No newline at end of file diff --git a/data/member_logs/353942721997832195.json b/data/member_logs/353942721997832195.json deleted file mode 100755 index 2fb4712..0000000 --- a/data/member_logs/353942721997832195.json +++ /dev/null @@ -1,28 +0,0 @@ -[ - { - "timestamp": "2025-08-19T14:59:06.458094", - "event_type": "member_leave", - "member_id": 353942721997832195, - "member_name": "mg_studios_", - "member_discriminator": "0", - "member_avatar": "https://cdn.discordapp.com/avatars/353942721997832195/99c6b7514ce56e19cf87b2c8f0d41316.png?size=1024", - "roles": { - "role_ids": [ - 1396130252144508939, - 1369669999366770724, - 1369669999366770726 - ], - "role_names": [ - "mg_studios_", - "👔 | Co-Directeur", - "🔑" - ], - "permissions": [ - 0, - 0, - 8 - ] - }, - "joined_at": "2025-05-07T13:41:48.145000+00:00" - } -] \ No newline at end of file diff --git a/data/member_logs/828239898975666216.json b/data/member_logs/828239898975666216.json deleted file mode 100755 index a644d71..0000000 --- a/data/member_logs/828239898975666216.json +++ /dev/null @@ -1,16 +0,0 @@ -[ - { - "timestamp": "2025-08-19T14:51:48.780957", - "event_type": "member_leave", - "member_id": 828239898975666216, - "member_name": "docteur_w.d_axolotl", - "member_discriminator": "0", - "member_avatar": "https://cdn.discordapp.com/avatars/828239898975666216/1835a7eaede10e9dbb78649beeed070c.png?size=1024", - "roles": { - "role_ids": [], - "role_names": [], - "permissions": [] - }, - "joined_at": "2025-05-09T18:36:32.808000+00:00" - } -] \ No newline at end of file diff --git a/data/member_logs/872192738920108074.json b/data/member_logs/872192738920108074.json deleted file mode 100755 index 6a9f2bc..0000000 --- a/data/member_logs/872192738920108074.json +++ /dev/null @@ -1,22 +0,0 @@ -[ - { - "timestamp": "2025-08-22T19:06:00.143653", - "event_type": "member_leave", - "member_id": 872192738920108074, - "member_name": "titis_zfr_7007", - "member_discriminator": "0", - "member_avatar": "https://cdn.discordapp.com/avatars/872192738920108074/986a18e279578123ec855f63defcc23f.png?size=1024", - "roles": { - "role_ids": [ - 1369769836028231730 - ], - "role_names": [ - "⚒️ | Bêta Testeurs" - ], - "permissions": [ - 1032 - ] - }, - "joined_at": "2025-07-18T21:59:11.510000+00:00" - } -] \ No newline at end of file diff --git a/serveur_flask.py b/serveur_flask.py index c942905..01ea1aa 100644 --- a/serveur_flask.py +++ b/serveur_flask.py @@ -33,16 +33,16 @@ def send_discord_log(status, message, color, author=None, commit_msg=None): except Exception as e: print(f"[!] Erreur envoi Discord : {e}") -def run_mep_logic(author, commit_msg): +def run_mep_logic(author, commit_msg, branch="main"): start_time = datetime.now() - send_discord_log("EN COURS", "Déploiement lancé sur la machine de guerre...", 3447003, author, commit_msg) + send_discord_log("EN COURS", f"Déploiement branch **{branch}** lancé sur la machine de guerre...", 3447003, author, commit_msg) try: os.chdir(PROJECT_PATH) # 1. GIT subprocess.run(["git", "fetch", "origin"], check=True) - subprocess.run(["git", "reset", "--hard", "origin/main"], check=True) + subprocess.run(["git", "reset", "--hard", f"origin/{branch}"], check=True) duration = (datetime.now() - start_time).total_seconds() msg = f"✅ **Déploiement réussi en {duration:.2f}s**\nLe bot est à jour et redémarré." @@ -70,8 +70,12 @@ def deploy(): author = data['commits'][0].get('author', {}).get('name', 'Inconnu') commit_msg = data['commits'][0].get('message', 'Pas de message') + # Extraction de la branche + ref = data.get('ref', 'refs/heads/main') + branch = ref.split('/')[-1] if ref else 'main' + # Threading pour répondre vite à GitLab - threading.Thread(target=run_mep_logic, args=(author, commit_msg)).start() + threading.Thread(target=run_mep_logic, args=(author, commit_msg, branch)).start() return {"status": "accepted"}, 202 diff --git a/src/advanced_logger.py b/src/advanced_logger.py index 54aaeba..e8a34fb 100755 --- a/src/advanced_logger.py +++ b/src/advanced_logger.py @@ -34,7 +34,7 @@ class AdvancedLogger: "details": details or {} } - self._write_log("messages", log_data) + asyncio.create_task(self._write_log_async("messages", log_data)) def log_role_event(self, member: discord.Member, role: discord.Role, event_type: str): """Log role changes (added/removed)""" @@ -49,7 +49,7 @@ class AdvancedLogger: "role_permissions": role.permissions.value } - self._write_log("roles", log_data) + asyncio.create_task(self._write_log_async("roles", log_data)) def log_voice_event(self, member: discord.Member, channel: discord.VoiceChannel, event_type: str): """Log voice channel events (join/leave)""" @@ -63,7 +63,7 @@ class AdvancedLogger: "channel_type": str(channel.type) } - self._write_log("voice", log_data) + asyncio.create_task(self._write_log_async("voice", log_data)) def log_member_leave(self, member: discord.Member): """Log when a member leaves the server""" @@ -85,10 +85,10 @@ class AdvancedLogger: } # Save to member-specific log - self._write_member_log(member.id, log_data) + asyncio.create_task(self._write_member_log_async(member.id, log_data)) # Also save to general logs - self._write_log("member_events", log_data) + asyncio.create_task(self._write_log_async("member_events", log_data)) def log_member_join(self, member: discord.Member): """Log when a member joins the server""" @@ -101,10 +101,13 @@ class AdvancedLogger: "member_avatar": str(member.avatar.url) if member.avatar else None } - self._write_log("member_events", log_data) + asyncio.create_task(self._write_log_async("member_events", log_data)) def check_previous_roles(self, member_id: int) -> Optional[Dict]: """Check if member has previous role data when rejoining""" + # This one stays synchronous for now as it's called in on_member_join and needs immediate result + # but since it only reads ONE file, it's less problematic than writing to cumulative logs. + # Format update: it now needs to read NDJSON. member_log_file = f"{self.member_logs_dir}/{member_id}.json" if not os.path.exists(member_log_file): @@ -112,7 +115,8 @@ class AdvancedLogger: try: with open(member_log_file, 'r', encoding='utf-8') as f: - logs = json.load(f) + # Read line by line for NDJSON + logs = [json.loads(line) for line in f if line.strip()] # Find the most recent leave event leave_events = [log for log in logs if log.get("event_type") == "member_leave"] @@ -123,47 +127,39 @@ class AdvancedLogger: kuby_logger.error(f"Error checking previous roles for {member_id}: {e}") return None - - def _write_log(self, log_type: str, data: Dict): - """Write log data to appropriate file""" - date_str = datetime.now().strftime("%Y-%m-%d") - log_file = f"{self.logs_dir}/{log_type}_{date_str}.json" - + + async def _write_log_async(self, log_type: str, data: Dict): + """Asynchronous wrapper for log writing""" + return await asyncio.to_thread(self._write_log_sync, log_type, data) + + async def _write_member_log_async(self, member_id: int, data: Dict): + """Asynchronous wrapper for member log writing""" + return await asyncio.to_thread(self._write_member_log_sync, member_id, data) + + def _write_log_sync(self, log_type: str, data: Dict): + """Write log data to appropriate file using NDJSON (Append-only)""" try: - # Append to existing file or create new - if os.path.exists(log_file): - with open(log_file, 'r', encoding='utf-8') as f: - existing_logs = json.load(f) - else: - existing_logs = [] - - existing_logs.append(data) + date_str = datetime.now().strftime("%Y-%m-%d") + log_file = f"{self.logs_dir}/{log_type}_{date_str}.json" - with open(log_file, 'w', encoding='utf-8') as f: - json.dump(existing_logs, f, indent=2, ensure_ascii=False) + # Simple append in NDJSON format + with open(log_file, 'a', encoding='utf-8') as f: + f.write(json.dumps(data, ensure_ascii=False) + "\n") except Exception as e: - kuby_logger.error(f"Error writing log for {log_type}: {e}") + # We don't use kuby_logger here to avoid infinite recursion if nested + print(f"CRITICAL ERROR in AdvancedLogger._write_log_sync: {e}") - def _write_member_log(self, member_id: int, data: Dict): - """Write member-specific log""" - member_log_file = f"{self.member_logs_dir}/{member_id}.json" - + def _write_member_log_sync(self, member_id: int, data: Dict): + """Write member-specific log using NDJSON (Append-only)""" try: - # Append to existing file or create new - if os.path.exists(member_log_file): - with open(member_log_file, 'r', encoding='utf-8') as f: - existing_logs = json.load(f) - else: - existing_logs = [] - - existing_logs.append(data) + member_log_file = f"{self.member_logs_dir}/{member_id}.json" - with open(member_log_file, 'w', encoding='utf-8') as f: - json.dump(existing_logs, f, indent=2, ensure_ascii=False) + with open(member_log_file, 'a', encoding='utf-8') as f: + f.write(json.dumps(data, ensure_ascii=False) + "\n") except Exception as e: - kuby_logger.error(f"Error writing member log for {member_id}: {e}") + print(f"CRITICAL ERROR in AdvancedLogger._write_member_log_sync: {e}") async def send_leave_notification(self, member: discord.Member, channel: discord.TextChannel): """Send notification when member leaves""" diff --git a/src/json_export_service.py b/src/json_export_service.py index d25fd38..bf7210d 100644 --- a/src/json_export_service.py +++ b/src/json_export_service.py @@ -188,10 +188,10 @@ async def generate_servers_json(bot: discord.Client) -> dict: entry["verificationLevel"] = str(guild.verification_level) if fields.get("memberCount"): - try: - members = [m async for m in guild.fetch_members(limit=None)] - entry["memberCount"] = sum(1 for m in members if not m.bot) - except Exception: + # Use cache if available (intents.members is True), otherwise fallback to count + if guild.members: + entry["memberCount"] = sum(1 for m in guild.members if not m.bot) + else: entry["memberCount"] = guild.member_count if fields.get("owner"): @@ -209,22 +209,27 @@ async def generate_servers_json(bot: discord.Client) -> dict: pass # Membres avec un rôle dont le nom correspond à founderRoles - try: - async for member in guild.fetch_members(limit=None): - if member.bot or member.id == guild.owner_id: - continue - for role in member.roles: - if any(key in role.name.lower() for key in founder_keys): - founders.append({ - "id": str(member.id), - "username": member.name, - "displayName": member.display_name, - "avatar": str(member.display_avatar.url), - "isOwner": False, - }) - break - except Exception: - pass + # Use cache if possible + members_to_check = guild.members if guild.members else [] + if not members_to_check: + try: + members_to_check = [m async for m in guild.fetch_members(limit=None)] + except Exception: + pass + + for member in members_to_check: + if member.bot or member.id == guild.owner_id: + continue + for role in member.roles: + if any(key in role.name.lower() for key in founder_keys): + founders.append({ + "id": str(member.id), + "username": member.name, + "displayName": member.display_name, + "avatar": str(member.display_avatar.url), + "isOwner": False, + }) + break entry["founders"] = founders @@ -259,7 +264,14 @@ async def generate_members_json(bot: discord.Client, guild_id: Optional[int] = N raise ValueError(f"Serveur introuvable : {target_id}") members_list = [] - async for member in guild.fetch_members(limit=None): + # Use cached members if available, or fetch if not + source = guild.members if guild.members else [] + if not source: + async for m in guild.fetch_members(limit=None): + if not m.bot: + source.append(m) + + for member in source: if member.bot: continue diff --git a/src/logger.py b/src/logger.py index a5a5057..d04aaaa 100755 --- a/src/logger.py +++ b/src/logger.py @@ -3,10 +3,14 @@ import sys import time import functools import asyncio +import json +import os from datetime import datetime from typing import Any, Callable import traceback +REPORTED_ERRORS_FILE = "data/automated_reports.json" + class KubyLogger: def __init__(self, name="KubyBot"): self.logger = logging.getLogger(name) @@ -53,37 +57,69 @@ class KubyLogger: def __init__(self, client): super().__init__() self.client = client - self.last_errors = {} self.cooldown = 3600 # 1 hour cooldown per unique error + self.last_errors = self._load_reported_errors() + + def _load_reported_errors(self): + if os.path.exists(REPORTED_ERRORS_FILE): + try: + with open(REPORTED_ERRORS_FILE, "r") as f: + return json.load(f) + except Exception as e: + print(f"Error loading reported errors: {e}") + return {} + + def _save_reported_errors(self): + try: + os.makedirs(os.path.dirname(REPORTED_ERRORS_FILE), exist_ok=True) + with open(REPORTED_ERRORS_FILE, "w") as f: + json.dump(self.last_errors, f, indent=4) + except Exception as e: + print(f"Error saving reported errors: {e}") def emit(self, record): if record.levelno >= logging.ERROR: - # Extract message and first line of traceback if available msg = record.getMessage() - error_key = msg - if record.exc_info: - error_key += str(record.exc_info[1]) + # Use message + location + simplified traceback for unique key + # This key is used for the local cooldown + error_key = f"{record.name}.{record.funcName}:{record.lineno} - {msg}" current_time = time.time() - if error_key in self.last_errors and current_time - self.last_errors[error_key] < self.cooldown: - return # Skip if same error within cooldown + last_time = self.last_errors.get(error_key, 0) + + if current_time - last_time < self.cooldown: + return self.last_errors[error_key] = current_time + self._save_reported_errors() - # Create issue (Fire and forget, we don't want to block logging) + # Prepare issue data description = f"**Type:** {record.levelname}\n" description += f"**Message:** {msg}\n" description += f"**Source:** {record.name}.{record.funcName}:{record.lineno}\n\n" if record.exc_info: description += f"**Traceback:**\n```python\n{traceback.format_exc()}\n```" - title = f"[AUTO] Error: {msg[:50]}..." + title = f"[AUTO] Error: {msg[:100]}..." + labels = ["bug", "auto-report"] + + # Async reporting flow + async def report_flow(client, t, d, l): + try: + # Check if an open issue with the SAME title already exists on GitLab + existing = await client.find_issue_by_title(t, state="opened") + if existing: + # If it exists, we don't create a new one to avoid clutter + return + + # Otherwise, create it + await client.create_issue(t, d, l) + except Exception as e: + print(f"Error in automated reporting flow: {e}") + loop = asyncio.get_event_loop() if loop.is_running(): - loop.create_task(self.client.create_issue(title, description, labels=["bug", "auto-report"])) - else: - # Fallback if loop is not running (rare for this bot) - pass + loop.create_task(report_flow(self.client, title, description, labels)) gitlab_handler = GitLabHandler(gitlab_client) gitlab_handler.setLevel(logging.ERROR) diff --git a/utils/gitlab_client.py b/utils/gitlab_client.py index b2bb7d4..40d7993 100644 --- a/utils/gitlab_client.py +++ b/utils/gitlab_client.py @@ -63,6 +63,35 @@ class GitLabClient: gitlab_internal_logger.error(f"❌ Error connecting to GitLab API: {e}") return None + async def find_issue_by_title(self, title: str, state: str = "opened") -> Optional[dict]: + """ + Searches for an issue by its exact title and state. + """ + if not self.token or not self.project_id: + return None + + headers = {"PRIVATE-TOKEN": self.token} + params = { + "search": title, + "state": state + } + + async with aiohttp.ClientSession() as session: + try: + # search param is fuzzy on GitLab, so we must double-check for exact match + async with session.get(self.api_url, headers=headers, params=params) as response: + if response.status == 200: + issues = await response.json() + for issue in issues: + if issue.get("title") == title: + return issue + return None + else: + return None + except Exception as e: + gitlab_internal_logger.error(f"❌ Error searching GitLab issues: {e}") + return None + async def get_closed_issues(self) -> List[dict]: """ Fetches recently closed issues.