Historique des évolutions LB'XMB (site, bot Discord, forum, boutique, intégrations). Cette page est synchronisée avec le document interne de récapitulatif.
Patch Notes (journal horodaté)
### 2026-04-26 21:02 (UTC+2)
Affichage des salons configurés avec trop d'indicateurs (✅/❌, 📎/🖼️, doublon du nom), lecture peu claire.
Le bouton retour renvoyait souvent au menu principal au lieu de la page parent logique.
Besoin de traduction des panels et réponses bot selon la langue client Discord.
### 2026-04-26 21:08 (UTC+2)
Impossible de garantir 100% sans observer les chaînes qui passent en fallback.
Besoin d'un comportement persistant : journaliser chaque changement en patch note dans ce document, même si la conversation change.
### 2026-04-26 21:12 (UTC+2)
Les informations existaient dans un document interne, mais pas dans une page web dédiée et proprement présentée.
La nouvelle page devait être accessible sans URL tapée manuellement.
### 2026-04-26 21:10 (UTC+2)
Après activation de l’i18n globale, certaines interactions pouvaient dépasser le délai Discord (3s) ou échouer dans le patch i18n.
### 2026-04-26 21:15 (UTC+2)
Plusieurs textes restaient en français dans les embeds, boutons et descriptions d’options, et le bouton retour affichait Back / Back.
### 2026-04-26 21:30 (UTC+2)
Plusieurs retours de panneau ne répondaient plus.
Un grand nombre de textes restaient partiellement en français (titres, descriptions, placeholders, variables, statuts, labels d’actions).
### 2026-04-26 22:00 (UTC+2)
La description de la commande affichée dans le sélecteur / liste des commandes restait en français pour tous les clients.
### 2026-04-26 22:40 (UTC+2)
### 2026-04-26 22:43 (UTC+2)
### 2026-04-26 22:44 (UTC+2)
### 2026-04-26 22:46 (UTC+2)
### 2026-04-26 23:00 (UTC+2)
### 2026-04-26 23:20 (UTC+2)
### 2026-04-26 23:45 (UTC+2)
### 2026-04-27 00:00 (UTC+2)
### 2026-04-27 00:15 (UTC+2)
### 2026-04-26 23:35 (UTC+2)
### 2026-04-27 16:38 (UTC+2)
### 2026-04-28 09:34 (UTC)
### 2026-04-28 10:20 (UTC)
### 2026-04-28 12:37 (UTC)
### 2026-04-28 12:55 (UTC)
### 2026-04-28 13:06 (UTC)
### 2026-04-28 13:19 (UTC)
### 2026-05-01 (heure locale serveur)
1. POST génère un lien temporaire signé (2 minutes)
2. GET exige un token valide + cohérent avec l'utilisateur + nom de fichier ;
Souci (UI Image Thread trop chargée)
Correctif : simplification de la liste en format minimal • #salon ; renommage du bouton de config vers Options par salon pour rendre l'action plus explicite.
Impact : panneau plus lisible, navigation de configuration plus claire pour les admins.
Fichier : bot/commands/setup_server.py
Souci (bouton Retour revenait à l'accueil)
Correctif : logique de retour contextuel (détection de l'écran actuel et remontée vers le parent : messages automatiques, catégorie arrivée, modération, communauté, etc.).
Impact : UX cohérente en configuration, moins d'allers-retours.
Fichier : bot/commands/setup_server.py
Souci (langues limitées / traduction partielle)
Correctif : ajout d'une couche i18n globale branchée au démarrage, appliquée aux content, embeds, vues (labels, placeholders, options) et followups.
Langues : fr, en, es, pt, it, de, ru, ar (fallback anglais hors fr et hors langue supportée).
Impact : couverture multi-langue étendue sur les interactions du bot.
Fichiers : bot/utils/global_i18n.py, bot/main.py
Souci (vérifier les textes encore non mappés)
Correctif : ajout d'un mode debug fallback i18n (I18N_FALLBACK_DEBUG=1) avec logs des traductions via handler LibreTranslate.
Impact : audit facilité pour finir la couverture “full propre” des textes.
Fichier : bot/utils/global_i18n.py
Souci (suivi des modifications entre sessions)
Correctif : ajout d'une règle projet Cursor “always apply” imposant l'ajout d'entrées patch notes dans ce fichier.
Impact : traçabilité continue des interventions.
Fichier : .cursor/rules/patch-notes-journal.mdc
Souci (absence d'une page publique de changelog sur le site)
Correctif : création de la page \/changelogs côté Next.js, avec lecture du fichier récap, parsing des sections (##), rendu en cartes, et priorité visuelle pour la section patch notes.
Impact : page publique centralisée des évolutions site/bot, maintenable depuis un seul fichier source.
Fichier : web/src/app/changelogs/page.tsx
Ressources & expérience lecture
Changelogs sur la fiche ressource (/ressources/[slug]) : le détail n’est plus entièrement déplié par défaut ; on affiche la version (et l’essentiel), avec possibilité d’ouvrir le reste (comportement proche d’une description repliée), pour un affichage plus propre.
Lien de téléchargement / fichier manquant (exemple corrigé) : lorsqu’une fiche pointait vers un nom de fichier qui n’existait pas sur le serveur (tandis qu’un autre binaire présent sur le disque devait être utilisé), la configuration (structure.json + base) a été alignée sur le fichier réellement hébergé pour supprimer les erreurs 404 côté /api/uploads/....
Intégration Pterodactyl (hébergement)
Création de service : le provisionnement sur Pterodactyl est devenu systématique à la création d’un hébergement depuis le dashboard (plus de dépendance à une case à cocher oubliée).
Requêtes API : possibilité de journaliser les appels (mode debug) pour diagnostiquer les réponses Ptero (variable d’environnement côté backend).
Résolution des œufs (eggs) : logique plus robuste (différents formats de réponse API, listes, fallbacks) ; variables d’environnement pour forcer l’UUID d’un œuf par type d’offre ; bouton « Œufs Ptero » sur le dashboard des services pour lister les œufs réellement présents sur le panel.
Création de serveur : le corps de requête inclut correctement image Docker et variables d’environnement issus de l’œuf, afin d’éviter les erreurs classiques du type *docker image required* / *environment must be present*.
Gestion côté staff : ébauche de panneau (suspendre, supprimer, dates, propriétaire, offre) dans la continuité des besoins d’exploitation.
Clé API Client : rappel important — les actions fichiers / console / état d’alimentation côté site passent par la clé client Pterodactyl ; en cas d’401 sur l’API client, vérifier PTERODACTYL_CLIENT_API_KEY (ou équivalent) et le redémarrage de l’app.
Synchronisation des releases GitHub (ressources)
Démon / cron : mécanisme pour déclencher périodiquement la route protégée de synchro (ex. via PM2 + CRON_SECRET), afin de ne plus dépendre uniquement d’un déclenchement manuel.
Détection des mises à jour : au-delà de l’ID GitHub de release, une empreinte (hash) du contenu (tag, titre, texte, assets, etc.) permet de re-synchroniser quand un développeur modifie la dernière release au lieu d’en créer une nouvelle (cas fréquent). Le changelog existant pour une même version peut être mis à jour plutôt que dupliqué.
Base de données : colonne de type github_last_sync_fingerprint (ajout géré côté app + script SQL de référence).
Espace « Mes services » (utilisateurs)
Accès : la page et le lien de menu « Mes services » limités aux rôles fondateur et admin pour ne pas exposer des versions encore en travail.
Liste & détail : parcours pour voir les services, interface type « panel » (console, fichiers, backups, configuration, utilisateurs).
Comptes applicatifs : section pour renseigner / gérer des infos (type mail, Jellyfin, Nextcloud) — évolution progressive.
Partage d’accès : onglet Utilisateurs pour donner des droits granulaires (console, fichiers, backups, config, etc.) sur un service, avec routes API associées.
Pterodactyl sans ouvrir le panel : simplification de l’onglet configuration (démarrage, image / runtime) ; indicateur d’état réseau côté serveur ; retrait de raccourcis redondants vers le panel.
Permissions & sync : prise en charge des comptes staff sur les mêmes actions que le propriétaire ; amélioration de la résolution d’instance et du power (alignement des états start/stop avec le panel, messages d’erreur si la clé client n’est pas valide).
Ordre des onglets : Utilisateurs positionné après Fichiers, comme demandé.
Bot Discord (configuration serveur)
Messages automatiques (config) : le sélecteur de salons accepte les fils (threads) en plus des salons texte / forum / annonces, avec multi-sélection (jusqu’à 25 salons) pour appliquer le même contenu sur plusieurs cibles d’un coup.
Résolution des salons : correction importante — en Python, guild.get_channel() ne voit pas les threads ; le code résout maintenant get_thread + fetch_channel quand c’est nécessaire, y compris à l’envoi des messages (bienvenue, départ, périodiques) pour ne pas cibler un ID de fil « introuvable » par erreur.
Boutique
Parcours vendeur : soumission d’un produit (nom, prix, description, visuels, catégorie, stock, lien vidéo, options type variantes) ; statut en attente jusqu’à validation côté équipe.
Base : support des product_options (groupes d’options) en JSON pour décrire des choix côté fiche, sans surcharger l’écran d’achat d’un formulaire monolithique.
Visibilité côté staff : notification aux administrateurs en plus de la file Discord (voir section suivante) pour traiter la demande rapidement.
Lien canonique : l’annonce automatisée pointe vers la boutique sur le site (/shop).
Partenariats
Espace dédié au dashboard : gestion des fiches partenaires (logo, texte, liens Discord / site / réseaux) avec un schéma de liens sociaux extensible (YouTube, Twitch, GitHub, TikTok, moyens de soutien, etc.).
Compte rattaché : possibilité d’associer un compte utilisateur du site à une fiche partenaire (recherche de profil) pour l’identification côté communauté.
API : persistance et lecture via les routes partenaires (/api/partners et variantes) alignées sur la page d’administration.
Description du site & aperçus (Open Graph)
Métadonnées globales : titre, description et mots-clés du site définis dans le layout principal (metadata Next.js) avec champs Open Graph et Twitter (cartes de partage cohérentes sur les réseaux et messageries).
Module og-lbxmb : helpers pour ligne de méta (ex. Ressource / console / type / nom), troncature, nettoyage HTML / markdown — logique proche des aperçus type embed (Description + extrait) pour un rendu unifié entre Discord et le web.
Fiches détaillées : certaines pages (ex. ressource par slug) enrichissent title / description / image par contenu pour un partage plus parlant qu’un simple titre générique.
Événements site → Discord (file partagée)
Mécanisme : le site enfile des commandes dans un fichier JSON (web/data/discord_communication/commands.json) via enqueueDiscordCommand ; le bot (ou un worker) les consomme pour poster dans les bons salons.
Types d’événements (exemples côté site) : nouvelle ressource validée, nouveau guide publié, demande d’ajout en boutique (produit en attente), création de compte (log avec lien profil) — chaque type transporte un petit payload (titre, auteur, URL, visuels, etc.).
Dons : un type dédié peut aussi alimenter le salon lors d’un don confirmé (hors sujet des autres sections mais même pipeline).
Forum (groupes) — programmation des posts
Champs côté base : scheduled_for (date/heure cible) et is_published pour distinguer brouillon / en attente / visible.
Publication : un job logique (requête au chargement de la liste) publie les messages dont l’heure programmée est passée, pour qu’ils apparaissent dans le fil sans action manuelle au moment T.
Tri : la liste s’appuie sur scheduled_for quand il est renseigné, sinon sur la date de création, pour un ordre cohérent avec l’intention de publication (y compris différée).
Fichiers techniques touchés (aperçu)
Site : web/src/app/ressources/..., web/src/lib/pterodactyl*.ts, web/src/lib/sync-resource-github-release.ts, web/src/lib/ensure-resource-github-columns.ts, web/src/app/mes-services/..., routes API mes-services, cron/sync-github-resources, dashboard/..., boutique (api/shop/..., app/shop/...), partenaires (dashboard/partners, api/partners, lib/partner-social-links), Open Graph (app/layout.tsx, lib/og-lbxmb.ts, layouts ressource), Discord (lib/discord-commands.ts, api/resources/add, api/guides/create, lib/auth pour les comptes, api/shop/request), forum groupes (api/forum-social/groups/[slug]/posts), etc.
Données / config : uploads/Structure/.../structure.json, ressources en base, ecosystem PM2 pour le sync GitHub, etc.
Remerciements
Ces changements s’inscrivent dans une amélioration continue du site, de l’hébergement, et de l’expérience sur Discord. Merci à la communauté pour les retours et la patience sur les itérations.
Souci (accès rapide à la page changelogs)
Correctif : ajout d’un lien Changelogs dans le footer (bloc légal).
Impact : découverte simplifiée de l’historique des mises à jour.
Fichier : web/src/components/layout/Footer.tsx
Souci critique (commande Discord “The application did not respond” en locale EN)
Correctif :
fallback de traduction via handler (LibreTranslate) désactivé par défaut (I18N_ENABLE_HANDLER_FALLBACK=0),
wrappers i18n sécurisés avec try/catch + repli automatique vers les méthodes Discord originales.
Impact : suppression du blocage des commandes en anglais, réponse immédiate restaurée.
Fichier : bot/utils/global_i18n.py
Souci (traduction incomplète dans le flux /config)
Correctif :
enrichissement de la base de traduction pour les textes les plus utilisés dans setup_server.py (placeholders, actions, statuts, libellés),
correction de l’algorithme de remplacement (priorité aux chaînes longues pour éviter les collisions de type Retour / Back),
remise du label source du bouton retour à Retour pour laisser la couche i18n produire Back proprement.
Impact : meilleure couverture de traduction sur tout le panneau /config (embeds + menus + boutons), suppression du bug visuel Back / Back.
Cause : BackButton appelait des méthodes statiques sur la mauvaise classe (SetupServerSelect), provoquant des AttributeError en runtime.
Correctif : reroutage vers la classe qui porte réellement ces méthodes statiques (ModerationCategorySelect) + nettoyage d’une référence invalide dans une méthode statique catégorie arrivée.
Impact : les boutons Back redeviennent fonctionnels sur les sous-panneaux concernés.
Fichier : bot/commands/setup_server.py
Souci (fragments FR persistants dans /config EN)
Correctif : enrichissement massif des mappings i18n globaux (phrases complètes + remplacements partiels) pour couvrir les cas du panneau /config et des messages d’action associés.
Impact : meilleure cohérence de traduction des embeds, menus déroulants, descriptions d’options, footers et messages contextuels.
Fichier : bot/utils/global_i18n.py
Déploiement correctif
Action : redémarrage du process PM2 bot-python après patch.
Impact : correctifs actifs immédiatement en production.
Commande /help (affichage côté client Discord)
Correctif : description par défaut en anglais, avec description_localizations pour le français (en-US / en-GB et fr).
Impact : les utilisateurs en locale anglophone voient une description cohérente dans l’UI Discord ; les clients en français voient le texte FR.
Fichier : bot/commands/help_command.py
Note : un resync des commandes d’application (sync) est nécessaire pour que Discord prenne en compte les nouvelles localisations.
Souci : beaucoup de chaînes restaient en français hors /config (notamment modération, utilitaires, gestion de salons, profil, VIP, AI).
Correctif : enrichissement important des mappings globaux (_BASE_FULL + _BASE_PARTIAL) avec de nouvelles clés FR→EN couvrant :
titres et descriptions d’embed,
labels de boutons et placeholders,
retours d’erreurs / confirmations,
textes de commandes fréquentes (moderation, utility, channel_management, profil, vip, ai).
Impact : meilleure couverture de traduction en runtime sur un périmètre plus large du bot pour les locales non-FR (avec propagation vers ES/PT/IT/DE/RU/AR via la couche existante).
Fichier : bot/utils/global_i18n.py
Extension i18n (passe 2) sur vues et commandes supplémentaires
Correctif : ajout d’un nouveau lot de traductions globales pour les zones suivantes :
lecteur musique (youtube_music : !play / groupe slash /musique),
gestion mails (mail),
panneau staff (staff),
système tickets (tickets_view),
vue d’accueil/règles/disclaimer (welcome_view).
Impact : davantage de textes d’UI traduits (boutons, placeholders, états, confirmations/erreurs, libellés d’embed) pour les utilisateurs non-FR.
Fichier : bot/utils/global_i18n.py
Extension i18n (passe 3) sur commandes et support
Correctif : ajout d’un lot supplémentaire de clés FR→EN pour :
casier, connexion, resolu,
send_welcome,
export_resources_forum,
support_view (tickets, recrutement, signalement, messages d’action et d’erreur).
Impact : meilleure traduction des flux de support et des commandes de modération/outillage restantes pour les locales non-FR.
Fichier : bot/utils/global_i18n.py
Extension i18n (passe 4) sur modules secondaires
Correctif : ajout de clés FR→EN supplémentaires pour les commandes/vues :
base64_command, translate_command, temps (voctime),
acces, page, dmall.
Impact : réduction des derniers textes FR visibles dans les flux utilitaires/administration et meilleure homogénéité des messages en locales non-FR.
Fichier : bot/utils/global_i18n.py
Extension i18n (passe 5) sur vues annexes
Correctif : ajout d’un lot de clés FR→EN (phrases entières + partiels) pour translation_view, vote_view (embed par défaut, commentaires, suggestions, stats) et github_releases_config (panneau releases, dépôts, salon, rôle, intervalle).
Partiels : Traduire en , Note moyenne :, évaluation / évaluations, 💬 Commentaire de, liens de vote ⭐ à, suite des dépôts … et / autre(s), etc.
Fichier : bot/utils/global_i18n.py
Déploiement : pm2 restart bot-python pour activer le lot en production.
Correctif : ajout massif de clés FR→EN pour accueil_view (erreurs courtes, messages partagés avec d’autres vues), script_panel_view (placeholder, service de traduction, en-têtes, erreurs, confirmation d’édition du message public), giveaway_view (modals, embeds, boutons, relances, participants, gagnant), et le bloc WELCOME_MESSAGE (aligné sur la version anglaise déjà présente dans TRANSLATED_MESSAGES).
Détail : correction de la clé d’apostrophe typographique pour ❌ Le service de traduction n’est pas disponible. (\u2019) afin d’assortir le texte de script_panel_view.py.
Fichier : bot/utils/global_i18n.py
Déploiement : pm2 restart bot-python.
Parité ES/PT/IT/DE/RU/AR sur la couche i18n (valeurs = anglais → locale)
Problème : pour it, de, ru, ar, la table réutilisait surtout les valeurs anglaises de _BASE_FULL (quelques clés seulement surchargées) ; le résultat restait en pratique très anglophone. ES/PT avaient une longue chaîne de .replace sur l’anglais, non alignée sur les autres.
Correctif :
nouveau module utils/i18n_en_chains.py : blocs de libellés UI + phrases fréquentes, avec regroupement par ligne (une colonne par langue) et tri des remplacements par longueur d’anglais décroissante (évite de couper de plus longues chaînes) ;
_LANG_REPLACEMENTS_FULL pour es / pt / it / de / ru / ar est désormais construit par build_localized_base_full(_BASE_FULL, lang) : pour chaque clé FR, la valeur anglaise est passée par la même chaîne EN→langue cible.
I18n « fluide » : LibreTranslate par défaut, timeout, pas d’écran d’erreur, embeds/vues en parallèle
Dictionnaire + chaînes, puis texte restant : LibreTranslate (désactiver : I18N_ENABLE_HANDLER_FALLBACK=0) ; 2,3 s max par appel (I18N_HANDLER_TIMEOUT) → sinon texte source ; les réponses d’échec ❌ Erreur de traduction du handler sont ignorées côté affichage.
Fichier : bot/utils/global_i18n.py
Déploiement : pm2 restart bot-python.
Franglais dans les descriptions des menus déroulants (Traduire en …)
Cause : le partiel Traduire en → Translate to n’existait que pour la locale en ; les autres langues gardaient le préfixe français.
Correctif : partiels dédiés es / pt / it / de / ru / ar + quelques fragments FR supplémentaires (vote, Choisir/Sélectionner).
Fichier : bot/utils/global_i18n.py
Déploiement : pm2 restart bot-python.
Cap (captcha self-hosted) déployé sur cap.lbxmb.fr
Contexte : finir l'intégration Cap commencée plus tôt — l'instance Standalone n'était pas encore en ligne. Sous-domaine cap.lbxmb.fr créé côté Cloudflare par le mainteneur.
Changements :
Installation de docker-compose (1.29.2) et certbot (2.1.0) sur l'hôte.
web/cap/.env : ADMIN_KEY aléatoire (32 octets, openssl rand -hex 32), exclu de git via .env*.
Conf nginx : nouveaux server blocks pour cap.lbxmb.fr (HTTP→HTTPS + HTTPS sur 127.0.0.1:4443 proxy vers 127.0.0.1:3010), avec propagation des headers CF-Connecting-IP, HSTS, Referrer-Policy, X-Content-Type-Options.
Certificat ECDSA Let's Encrypt obtenu via webroot (/var/www/html/.well-known/acme-challenge/), expire le 2026-07-25.
web/cap/README.md mis à jour (suppression du bloc nginx d'exemple, ajout du flot dashboard / .env.local).
Tests : https://cap.lbxmb.fr répond HTTP/2 200 avec x-powered-by: Cap Standalone. Redirection 80→443 OK. nginx -t OK.
Reste à faire (humain) : se connecter au dashboard, créer un site key, reporter CAP_SITE_KEY / CAP_SECRET_KEY dans web/.env.local, basculer CAP_DISABLED=0 puis pm2 restart web.
Login : Discord mis en avant, alternatives discrètes
Contexte/problème : les utilisateurs connectés via Discord choisissaient souvent Google/GitHub, ce qui casse les avantages liés au serveur Discord.
Changements appliqués : sur la page de login, le bouton Discord reste principal et visible immédiatement ; Google/GitHub sont masqués derrière un bouton discret Je n'ai pas Discord.
Impact visible / technique : parcours d’auth recentré sur Discord, baisse attendue des comptes liés via mauvais provider.
Contexte/problème : la bannière demandée devait reprendre la structure visuelle de /presentation avec un rendu plus propre et des données réelles.
Changements appliqués : remplacement du HTML de bannière par une version 960x540 avec fond/effets proches de la présentation, layout propre et statistiques dynamiques chargées via API (ressources, stockage, utilisateurs, guides).
Impact visible / technique : rendu homogène avec l’identité visuelle du site, chiffres affichés automatiquement depuis les endpoints du site.
Salon ressources : vrai auteur + avatar dans la miniature
Contexte/problème : certaines annonces affichaient Utilisateur au lieu du vrai nom d’auteur/créateur, et la miniature n’utilisait pas l’avatar de l’auteur.
Changements appliqués : enrichissement du payload site_new_resource avec author_name et author_avatar; côté bot, fallback intelligent pour remplacer les placeholders (Utilisateur, unknown, etc.) par le vrai auteur, puis priorité avatar auteur pour thumbnail.
Impact visible / technique : embeds de nouvelles ressources plus fiables et lisibles dans le salon 1497192095679774823.
Login : Discord seul par défaut + alternatives à la demande
Contexte/problème : la page de connexion affichait trop de providers immédiatement, alors que le flux attendu est centré Discord.
Changements appliqués : conservation du bouton Discord visible en premier, ajout d’un texte-action Je n'ai pas Discord qui dévoile uniquement Google et GitHub.
Impact visible / technique : parcours simplifié et orienté Discord, avec alternatives disponibles seulement si nécessaire.
Contexte/problème : impossible de programmer l’envoi d’une annonce depuis l’UI de création de post.
Changements appliqués : ajout d’un champ datetime-local dans l’éditeur d’annonce, envoi de scheduledFor vers l’API, reset après envoi et message explicite quand l’annonce est planifiée.
Impact visible / technique : les annonces peuvent être publiées immédiatement ou différées à une date/heure précise.
Image en Thread : affichage salons + réglages avancés
Contexte/problème : les salons configurés s’affichaient mal, il manquait une vraie section de configuration des réactions, et l’option de suppression thread/message source devait être pilotable.
Changements appliqués : correction d’affichage des salons (<#id> (nom)), ajout des réactions globales visibles/éditables, ajout des réactions par salon, ajout d’un bouton pour basculer la suppression du thread lié quand le message source est supprimé, enrichissement de l’embed de configuration.
Impact visible / technique : configuration imagethread plus lisible et complète (réactions + comportement de suppression) directement depuis le panel bot.
Contexte/problème : la commande /deplacer n’était plus disponible et devait être remplacée.
Changements appliqués : ajout de la commande slash /move (déplacer un membre ciblé ou tout le vocal courant), mise à jour de l’aide, et remplacement de la clé de permission affichée en configuration.
Impact visible / technique : commande de modération vocale rétablie avec nom anglais unique.
Programmation des posts (date/heure) étendue aux discussions
Contexte/problème : la programmation semblait ne pas fonctionner pour les posts de discussion.
Changements appliqués : ajout du sélecteur date/heure aussi dans l’éditeur discussion; côté API, création du thread avec created_at planifié si date future, filtrage des threads futurs en lecture, et retour d’état scheduledFor.
Impact visible / technique : les posts discussion et annonces peuvent être publiés à l’heure prévue.
Image en Thread : suppression des réglages globaux réactions + suppression thread en option salon
Contexte/problème : les réactions globales n’étaient pas souhaitées et l’option de suppression thread devait être déplacée au niveau salon.
Changements appliqués : retrait de la section réactions globales, ajout du flag deleteThreadOnSourceDelete par salon, ajout du toggle dans les options du salon, et adaptation de la suppression on_message_delete pour lire le paramètre par salon (fallback legacy conservé).
Impact visible / technique : configuration plus fine et plus cohérente par canal imagethread.
Banner/Logo : import HTML déplacé dans “Configurer” + pièce jointe Discord
Contexte/problème : l’import HTML via web externe et le bouton auto-update n’étaient pas adaptés au flux voulu.
Changements appliqués : suppression des boutons dédiés “import HTML” et “auto-update” de la vue principale, ajout d’un choix dans “Configurer Bannière/Logo” (URL ou import HTML), import HTML direct depuis une pièce jointe envoyée dans Discord, activation automatique permanente des updates, et tentative de mise à jour immédiate après configuration.
Impact visible / technique : flux unifié dans la config Discord, sans dépendance à la page web d’upload.
Fichiers touchés : bot/commands/setup_server.py
Forum : programmation visible lors de la création d’un post
Contexte/problème : sur la page forum principale, l’option de programmation n’était pas affichée dans le formulaire de création de sujet.
Changements appliqués : ajout du champ datetime-local “Programmer l’envoi (optionnel)” dans les deux variantes du formulaire (desktop/mobile), envoi de scheduled_for à l’API, reset après création.
Impact visible / technique : l’utilisateur peut programmer date/heure directement depuis la page forum.
Fichiers touchés : web/src/app/forum/page.tsx
API création thread : prise en charge de la programmation
Contexte/problème : même avec un champ front, l’API thread standard ne traitait pas la date planifiée.
Changements appliqués : ajout de scheduled_for dans thread/create, validation de date, insertion de created_at/updated_at à la date future si programmation.
Impact visible / technique : la publication est différée correctement sans créer un post visible immédiatement.
Contexte/problème : après création d’un post planifié, il manquait un retour visuel immédiat indiquant que l’envoi est différé.
Changements appliqués : ajout d’un état scheduledNotice et affichage d’un bandeau/badge “Post programmé pour …” sur la page forum; en cas de post planifié, suppression de la redirection immédiate vers le thread.
Impact visible / technique : confirmation claire que le post est en file d’attente de publication.
Fichiers touchés : web/src/app/forum/page.tsx
Posts programmés visibles uniquement à l’auteur dans la catégorie
Contexte/problème : les posts programmés devaient apparaître dans la liste de la catégorie pour l’auteur uniquement (pas pour les autres utilisateurs).
Changements appliqués : filtre API des threads ajusté pour inclure les posts futurs seulement si l’utilisateur courant est l’auteur; conservation du masquage pour les autres.
Impact visible / technique : l’auteur peut suivre ses posts programmés directement dans le forum sans fuite de visibilité.
Contexte/problème : besoin d’une indication claire sur les posts programmés (date de publication + temps restant).
Changements appliqués : ajout du badge/icône “Programmé” sur les threads programmés de l’auteur avec infobulle (title) affichant date prévue et temps restant calculé.
Impact visible / technique : meilleure lisibilité de l’état des publications planifiées.
Fichiers touchés : web/src/app/forum/page.tsx
Visibilité personnalisée des posts (public / followers)
Contexte/problème : besoin de choisir si un post est visible par tout le monde ou uniquement par les followers.
Changements appliqués : ajout du champ visibility en création de thread, ajout/maintenance du schéma SQL côté API (forum_threads.visibility), filtrage d’accès dans la liste des threads et sur la route détail thread.
Impact visible / technique : publication ciblée possible avec contrôle d’accès cohérent en lecture liste + détail.
UX création post : actions programmation/visibilité près de Média
Contexte/problème : la programmation devait être pilotée via un bouton près de “Média”, avec calendrier + horloge, et la visibilité au même endroit.
Changements appliqués : ajout de boutons Programmer et Visibilité dans la barre d’actions, ouverture de panneaux inline avec sélecteur date (type=date) + heure (type=time) et choix visibilité Tout le monde/Mes followers.
Impact visible / technique : flux de publication plus clair et ergonomique, aligné sur la demande produit.
Fichiers touchés : web/src/app/forum/page.tsx
Correction position boutons Programmer/Visibilité dans le formulaire post
Contexte/problème : les boutons n’étaient pas réellement affichés juste à côté du bouton Média dans toutes les variantes du formulaire.
Changements appliqués : restructuration de la barre d’actions pour grouper Média, Programmer et Visibilité sur la même ligne (modal + version inline), suppression du bloc dupliqué, maintien des panneaux date/heure et visibilité après la barre.
Impact visible / technique : les boutons demandés apparaissent désormais au bon endroit, au même niveau que Média.
Fichiers touchés : web/src/app/forum/page.tsx
Programmation forum : correction fuseau horaire (publication + badge)
Contexte/problème : un post programmé pouvait rester marqué “Programmé” et non visible publiquement après l’heure attendue.
Changements appliqués : conversion date+heure locale en ISO timezone-aware (toISOString()) avant envoi vers l’API de création.
Impact visible / technique : la date de publication est interprétée correctement côté serveur, ce qui rétablit la bascule de visibilité et la disparition du statut “Programmé” au bon moment.
Fichiers touchés : web/src/app/forum/page.tsx
Publication programmée forum : bascule fiable après échéance
Contexte/problème : certains posts restaient bloqués en “programmé” après l’heure prévue et n’étaient pas visibles aux autres utilisateurs.
Changements appliqués : passage à un modèle dédié scheduled_for + is_published pour forum_threads, publication automatique côté API quand scheduled_for <= NOW(), et migration legacy des anciens posts programmés (détection via created_at > NOW()).
Impact visible / technique : la visibilité publique se débloque correctement à l’échéance, et le badge “programmé” ne reste plus bloqué indéfiniment.
Contexte/problème : les deux menus pouvaient s’ouvrir en même temps.
Changements appliqués : ajout de toggles exclusifs (toggleScheduleMenu, toggleVisibilityMenu) pour fermer automatiquement l’autre menu à l’ouverture.
Impact visible / technique : interaction plus claire, un seul panneau de configuration visible à la fois.
Fichiers touchés : web/src/app/forum/page.tsx
Endpoint de publication immédiate d’un thread programmé
Contexte/problème : besoin d’une action manuelle pour publier un thread sans attendre l’échéance planifiée.
Changements appliqués : ajout d’un handler POST sur la route thread détail qui force is_published = 1, vide scheduled_for, et met à jour updated_at; autorisation limitée à l’auteur du thread ou un admin/fondateur.
Impact visible / technique : possibilité de débloquer instantanément un post planifié en cas de correction manuelle.
Page VIP : VIP Booster vs Premium, hub Jellyfin + API
Contexte/problème : afficher la source VIP (Discord vs paiement site), distinguer avantages booster / premium (retirer hébergement, Jellyfin, Nextcloud annoncé 30 Go, webmail côté booster ; ajouter Nextcloud aux premium), onglets de gestion des accès pour les abonnés payés avec création Jellyfin via API et suspension/réactivation selon vip_subscriptions.
Changements appliqués : fetchPaidPremiumState + resolveVipTier dans /api/vip/status, sync Jellyfin syncJellyfinForVipPaidState, route POST/GET /api/vip/provision/jellyfin, badge vip_source = stripe à l’activation checkout, colonne user_id BIGINT dans vip_jellyfin_accounts (+ ALTER si ancienne INT), page vip refactor (comparaison marketing, grille avantages selon tier, Tabs Premium avec Jellyfin + placeholders), clés vipPage FR/EN.
Impact visible / technique : texte métier clarifié pour les tiers ; Jellyfin créé à la demande pour Premium actif avec mot de passe affiché une fois ; compte Jellyfin désactivé quand l’abonnement payé n’est plus actif (lecture status resync).
Jellyfin : sync sur perte / retour du rôle VIP (permissions)
Contexte/problème : la suspension ne tenait compte que de l’abonnement payé, pas du retrait du flag vip en base (rôle Discord synchronisé avec le site).
Changements appliqués : syncJellyfinForVipEntitlement + jellyfinVipShouldBeActive : compte réactivé uniquement si à la fois hasVipPermission et abonnement Premium actif ; suspension dès qu’il manque le rôle VIP ou la période payée. /api/vip/status passe isVip et paidPremiumActive.
Impact visible / technique : un utilisateur qui perd le VIP côté site se voit désactiver Jellyfin au prochain passage sur la page VIP (ou tout appel /api/vip/status) ; retour du rôle + Premium actif réactive.
Contexte/problème : avantage « −10% » unique ; souhait −5% VIP Booster vs −10% VIP Premium sur les achats boutique.
Changements appliqués : resolveBuyerVipDiscountPercent dans vip-tier ; application sur POST /api/stripe/checkout (montant line item + commission calculée sur prix payé ; métadonnées buyer_discount_pct, list_price_cents) ; GET /api/vip/shop-buyer-discount ; page produit shop/[id] : prix barré + prix VIP ; page VIP + vipPage FR/EN avec textes séparés booster/premium.
Impact visible / technique : remise cohérente entre fiche produit et session Stripe ; tiers non-VIP inchangé (0%).
Traduction automatique Discord : mise en forme + menu réinitialisé
Contexte/problème : préfixe « Traduit » en français quel que soit la langue cible ; liste déroulante qui ne permettait pas de rechoisir la même langue.
Changements appliqués : en-tête <traduction> <drapeau> avec libellés statiques par langue cible (Traduction, Translation, Übersetzung, etc.) puis texte ; après réponse éphémère, edit du message avec le menu pour régénérer la vue Discord.
Impact visible / technique : message traduit lisible comme section localisée + re-sélection de la même langue possible.
/config : ancrage message + rafraîchissement sans bouton dans plusieurs écrans
Contexte/problème : suivre une consigne utilisateur après ajout/suppression hors message principal ; correctifs éviter messages « Cliquez Actualiser » orphelins.
Changements appliqués : enregistrement du message éphem /config par (guild,user) (_register_config_panel_anchor / refresh_config_panel_anchor), traduction auto : toggle met à jour l’embed directement ; ajouts/retraits de salons via liste éphémère rafraîchissent l’embed si titre encore « Traduction Automatique » ; messages périodiques : toggle + retrait synchro embed ; même logique pour retrait messages Welcome/Goodbye/DM avec garde titre ; bouton Rafraîchir retiré uniquement pour Traduction automatique, les autres sous-menus (notifications, tickets, anti-mots, etc.) gardent leur Actualiser ; votes : toggle fait edit_original_response après followup confirmation.
Impact visible / technique : moins de clics où le flux était brisé après sed ; hors périmètre : nombreux autres textes « Actualiser » restent tant que pas branchés sur edit ou ancrage titre.
Contexte/problème : slugs séparés côté site (vip_premium achat Stripe, vip_booster boost Discord) ; ne plus attribuer automatiquement le rôle cosmétique Discord 1385546866942410813 lorsque l’utilisateur a le booster 1308907900751577181 ; l’API check-vip doit représenter uniquement le Premium payé avec abonnement actif.
Changements appliqués : activation paiement ajoute vip_premium ; Jellyfin vérifie qualifiesForPaidPremiumBenefits ; checkout vendeur/remise acheteur via vip-tier ; synchro Discord vip_premium (+ legacy vip) vers le rôle doré, pas vip_booster ; connexion découpant les permissions CSV ; UI (header, sidebar forum, dashboard édition, profil) ; bot : synchro DB vip_booster depuis Discord, giveaways + ticket avantages VIP incluent booster ; Flask sync_user_roles connaît vip_premium.
Impact visible / technique : Premium doré sur Discord réservé au mapping site hors booster ; booster identifié sur le site après synchro cron ; giveaways/tickets peuvent autoriser booster sans rôle VIP Discord.
Contexte/problème : page /dashboard/maintenance cassait avec l’erreur React minifiée #31 (« object with keys code, summary, detail ») quand npm renvoie une erreur structurée : le JSX rendait {packagesStatus.error} alors que ce champ était un objet ; besoin de lancer bun run deploy:prod en arrière-plan et de piloter PM2 (bot-python).
Changements appliqués : normalisation serveur du JSON npm outdated avec extraction texte { code, summary, detail } ; helpers maintenanceErrorMessage + affichage sûr (Stripe / PM2 / NPM) ; cellules tableau outdated forcées en chaîne ; carte « Actions serveur » avec confirmations — enqueueDeployProd (spawn détaché, cwd projet Next), pm2RestartBot / pm2StartBot / pm2StopBot, nom process LBXMB_PM2_BOT_NAME (défaut bot-python).
Impact visible / technique : la page maintenance ne plante plus sur erreur npm objet ; équipe peut déclencher build/deploy et lifecycle bot depuis le dashboard (droits inchangés : fondateur / admin / développeur, comme le reste de l’onglet maintenance). Les erreurs réseau WebSocket ou notifications (AxiosError: Network Error) relèvent du proxy / TLS / CSP côté navigateur, hors périmètre de ce correctif.
Contexte/problème : un utilisateur à la fois boosteur Discord et abonné Premium voyait la logique ou vip_source=boost l’emporter (hasVipPremiumSlot faux avec slug vip + source boost), alors que le payant doit primer (API /api/bot/check-vip, −10 % boutique, affichage tier).
Changements appliqués : hasVipPremiumSlot(..., paidPremiumActive) : abonnement actif + ancien slug vip ⇒ slot premium même si vip_source mal synchronisé ; resolveVipTier et resolveBuyerVipDiscountPercent s’appuient là-dessus ; checkout acheteur charge vip_source ; bot : si booster + (vip_premium OU source stripe OU ligne vip_subscriptions encore active), on n’écrit plus vip_source=boost (effacement si c’était boost).
Impact visible / technique : même compte (« LoupBlanke », booster + Premium) : le payant est reconnu sur le site et pour /api/bot/check-vip ; la synchro booster n’écrit plus vip_source=boost tant qu’un abonnement actif existe.
2026-05-01 — Maintenance : deux actions seulement (Build Site, Redémarrer Bot)
Changements appliqués : retrait des boutons PM2 start/stop sur /dashboard/maintenance ; libellés « Build Site » et « Redémarrer Bot » ; suppression des server actions pm2StartBot / pm2StopBot.
Impact visible : carte « Actions serveur » limitée aux deux commandes demandées.
Contexte/problème : trois entrées VIP dont l’ancien slug vip et des mentions entre parenthèses réservées au contexte dev.
Changements appliqués : liste PERMISSIONS réduite à VIP Premium et VIP Booster (sans parenthèses) ; suppression de la case « VIP (ancien slug) » côté formulaire. Migration à l’affichage et au PUT : token vip remplacé par vip_premium ou vip_booster selon vip_source (boost → booster, sinon premium) ; champ vip_source exposé sur GET /api/dashboard/users/[id].
Impact visible : deux rôles affichés ; anciens comptes vip ne restent pas orphelins à l’enregistrement.
2026-05-01 — Rôle partenaire : hors sélection manuelle dashboard
Contexte/problème : « partenaires » ne doit plus être ajouté à la main dans les badges utilisateur ; il doit suivre uniquement la fiche partenaire.
Changements appliqués : retrait de partenaires de la liste PERMISSIONS ; pastille lecture seule « Partenaires » si le slug est encore en base ; PUT /api/dashboard/users/[id] retire partenaires/partenaire si aucune ligne partners pour users.id ; synchro Discord inclut toujours l’ID rôle partenaire quand le slug est présent.
2026-05-01 — Page VIP : CTA « Passer en VIP Premium » pour les boosters
Contexte/problème : les boosters devaient pouvoir souscrire au Premium depuis /vip avec message clair sur le cumul et la préseance du payant.
Changements appliqués : bouton vers POST /api/vip/checkout (plan mensuel) lorsque tier === "boost" ; clés i18n dédiées ; upgradeFromBoostHint réécrit en ce sens.
2026-05-01 — VIP tier : Premium si abonnement actif même sans jeton vip_premium en base
Contexte/problème : comptes boost + paiement Stripe vus comme « boost » uniquement si la colonne permission ne contenait plus vip/vip_premium alors que vip_subscriptions est actif (stripStale/décalage webhook).
Changements appliqués : hasVipPremiumSlot retourne vrai lorsque paidPremiumActive ; resolveVipTier teste l’abo avant la branche « aucun jeton » ; qualifiesForPaidPremiumBenefits délègue au slot Premium.
Impact visible : /api/vip/status → tier: "premium" et hub Premium (tabs Jellyfin etc.) tant que Stripe est actif ; bot /api/bot/check-vip aligné sur l’abo.
Contexte/problème : hub Premium invisible pour certains payeurs (tier resté « boost » dès que vip_booster était présent ; ligne vip_subscriptions reliée au mauvais identifiant user_id vs users.id/session).
Changements appliqués : resolveVipTier teste hasVipPremiumSlot(..., false) avant tout jugement booster ; fetchPaidPremiumState accepte plusieurs clés (subscriberLookupKeys auth + ligne users) ; statuts Stripe complete/succeeded comptés comme actifs tant que la date de fin est valide ; /api/vip/status, /api/vip/portal, remise boutique, Jellyfin provisioning alignés sur l’identifiant canonique et la recherche multi-clés.
Impact visible : onglets Premium sur /vip, portail Stripe et Jellyfin si l’abo est retrouvé sur une clé plausible ; booster + badge Premium → affichage Premium.
Contexte/problème : besoin d’un point d’entrée unique (desktop + mobile) pour chercher ressources, guides, boutique, membres, sujets/réponses forum publics, offres services.
Changements appliqués : GET /api/site-search (requêtes SQL parallèles, filtre ressources listables, fil visibilité forum public + publié) ; composant GlobalSiteSearch (dialog plein écran sur viewport mobile, debounce) ; bouton loupe desktop dans Header ; entrée Recherche dans MobileBottomNav entre Messages et Notifications (utilisateurs connectés) ; clés nav.siteSearchAria, globalSearch, mobileNav.search ; lien secondaire vers liste ressources + prise en charge ?search= sur /ressources.
Impact visible : modal de recherche depuis la barre (desktop) ou la barre du bas (mobile) ; résultats groupés par type avec liens profil / ressource / guide / boutique / forum / services/detail.
Contexte/problème : liste conversations sans group_owner_user_id / group_avatar_url en SQL ; avatars de groupe absents ; nom « Groupe (n) » ; compositeur ne vidait pas la recherche après ajout d’un contact ; add_member non réservé au créateur ; suppression des messages limitée à l’auteur ; besoin d’UI minimale de gestion groupe (nom, logo URL, retrait membres).
Changements appliqués : SELECT conversations enrichi + other_photo groupe depuis group_avatar_url ; GET conversation groupe : other_photo aligné ; POST /api/messages/group/create : titre auto à partir des pseudos (le client n’envoie plus groupName) ; PATCH add_member réservé au propriétaire ; DELETE message vendor : auteur ou propriétaire d’un groupe ; MessageBell : reset composerQuery / résultats à l’ajout d’un destinataire ; menus message : suppression pour modération propriétaire ; en-tête groupe : avatar + sous-titre membres ; dialogue réglages (renommer, URL logo, retirer membre) pour le créateur.
Impact visible : groupes nommés lisiblement ; compositeur plus fluide ; contrôle cohérent côté créateur ; modération des messages des autres dans les groupes.
Contexte/problème : plusieurs libellés boutique restaient codés en dur (FR) et n’étaient pas traduits côté site (badges vendeur, sécurité/promo, options produit, anti-bot, CTA contact, labels galerie/statistiques).
Changements appliqués :
remplacement des textes en dur sur shop/page.tsx par des clés sitePages.shop (verifiedSeller, securePayment, stats et bandeau confiance) ;
remplacement des textes en dur sur shop/[id]/page.tsx (optionsTitle, previousImageAria, nextImageAria) ;
internationalisation complète des libellés restants du formulaire boutique (BoutiqueProductForm) : anti-bot, options produit, placeholders options, boutons d’ajout, placeholder YouTube ;
internationalisation du bouton ContacterVendeurButton via shopProductPage.contactSeller ;
ajout des nouvelles clés FR/EN dans web/messages/fr.json et web/messages/en.json.
Impact visible / technique : les pages et actions liées à la boutique passent désormais par i18n (FR/EN) sans texte FR figé dans l’UI.
2026-05-01 13:30 — Refonte présentation boutique + création produit enrichie
Contexte/problème : besoin d’un style de card proche de la maquette fournie (visuel haut + garanties + CTA), d’un choix logo/bannière à la création, d’un système de garanties personnalisées, d’une gestion catégorie existante/nouvelle et d’un filtre vendeur côté listing.
Changements appliqués :
Cards boutique (shop/page) : nouveau layout visuel type showcase (header image/logo, prix badge, garanties listées, bouton achat, fallback garanties) + support display_mode (logo/banner) et banner_image.
Filtres listing (shop/page) : ajout filtres par catégorie et vendeur en plus du tri/recherche ; synchronisation URL via query params (category, seller) ; reset global des filtres.
API shop : migration douce SQL (ALTER TABLE ... ADD COLUMN IF NOT EXISTS) pour banner_image, display_mode, guarantees + persistance/lecture dans POST /api/shop/request, PUT /api/shop/[id], GET /api/shop et parse JSON des garanties.
i18n FR/EN : ajout des nouvelles clés boutique (sitePages.shop, boutiqueFormPage) pour labels, filtres et garanties.
Impact visible / technique : la boutique affiche désormais des cards orientées “offre/service” plus proches de la capture ; la création d’article devient plus flexible (branding visuel, garanties marketing, catégories exploitables pour filtrage) ; le listing peut être filtré par vendeur.
2026-05-01 15:45 — Options boutique avec ajustement de prix · /vip (UI + thème booster) · grille catalogue logo + cartes élargies
Contexte/problème : variante produit (ex. carte SD) doit faire varier le prix affiché et facturé ; page VIP encore trop “ambre halo” sur la carte facturation, badges Premium/À renouveler à retirer, textes Discord/boost à retirer, CTA Stripe en bas de carte ; boosters demandent une ambiance violette ; listing boutique doit forcer la vignette logo, réafficher clairement note + nombre d’avis + photo vendeur, colonnes plus larges.
Options prix
Changements : nouveau module web/src/lib/shop-product-options.ts ; options persistées sous forme { label, price_delta } (compat strings historiques → delta 0) ; validation + somme des deltas dans POST /api/stripe/checkout ; prix catalogue et remises VIP calculés sur prix base + deltas ; formulaire création : par variante libellé + champ € (signé) ; fiche /shop/[id] : prix total dynamique et libellé “prix de base / options”.
Changements : suppression des deux paragraphes Discord/premium differentiation sous le titre ; suppression des badges “Premium” / “À renouveler” ; bouton « Gérer la facturation » déplacé en pied de carte (aligné à droite) avec style contour (plus de halo ambre focalisé) ; utilisateur VIP booster uniquement : fond dégradés + halos violet/indigo + accent titre + grille avantages Spotlight violette + CTA upgrade violet ; aide Stripe compactée ; carte calendrier : bord neutre sans surbrillance dorée parasite.
Fichiers touchés : web/src/app/vip/page.tsx
Listing /shop
Changements : la grille liste toujours en mode vignette/logo (sans bannière) ; photo vendeur (getPhotoUrl) + ligne étoiles + note + nombre d’avis ; max-w-[90rem], grille xl:grid-cols-3 et espacements plus larges pour des cards plus imposing.
Fichiers touchés : web/src/app/shop/page.tsx
i18n formulaire boutique : complément des clés manquantes dans le namespace boutiqueFormPage (catégorie, visuel, garanties, options, anti-bot, YouTube) pour éviter MISSING_MESSAGE sur le flux ajout/modification — FR/EN.
Contexte/problème : vignettes vendeurs mal rendues ; besoin de règles visuelles claires par carte et d’une page d’accueil boutique simplifiée (logo, titre, accroche, CTA, stats discrètes, filtres légers, grille aérée responsive).
Changements appliqués :
Avatars vendeur : résolution d’URL (/api/ déjà complet, sinon getPhotoUrl après normalisation des / initiaux) + object-cover, taille petite (28px), repli lettre si chargement KO (onError).
Cartes produits : image dominante (aspect-square pour viser ~60–67 % de hauteur), prix en badge haut-droite sur l’image, titre gras agrandi, description line-clamp-2, 2–3 pastilles garanties mini en ligne horizontale, ligne vendeur (mini photo + pseudo + étoiles + note), CTA plein largeur dégradé bleu clair.
Haut de page : logo /lbxmb.png, titre via heroHeadline (« LA BOUTIQUE LB'XMB » en FR), accroche heroTagline, bouton « Ajouter un produit » avec icône + (vert).
Stats : deux petites cartes seulement (vendeurs vérifiés = nombre de vendeurs uniques Stripe ok, pas le nombre de produits ; avis clients total).
Zone catalogue : suppression du bandeau « trust » trois colonnes ; grille 1 col mobile, 2 cols md, 3 cols xl, gap-x/gap-y très larges (max-w-[82rem]).
Impact visible / technique : listing plus lisible et moins chargé ; avatars vendeurs stabilisés sur chemins relatifs/absolus variables ; grille plus aérée sur desktop.
2026-05-01 16:05 — Boutique /shop : alignement « ce qui colle bien » (CM-like sans copier CM)
Contexte/problème : matérialiser la feuille de route discutée avec l’utilisateur : image forte ~70 %, ligne type « by vendeur » + confiance, badges standardisés, filtre vendeurs vérifiés, respir / premium.
Changements appliqués :
Carte : vignette aspect-[5/7] ; titre line-clamp-1 ; ligne Par [lien vendeur] (/shop?seller=…) + dans {category} · note/avis ; prix + pastille vérifié (Stripe) sur l’image ; pastilles marketing (max 3) = Populaire si ≥ 5 avis + garanties sinon défauts rapide/safe ; overlay cliquable vers fiche produit + lien vendeur pointer-events-auto (sans <a> imbriqués).
Filtres : case à cocher Vendeurs vérifiés uniquement + query verified=1 ; chips filtres + message vide / reset cohérents.
Contexte/problème : compléter la feuille de route type Creative Market avec une rangée de vendeurs mis en avant au-dessus de la grille produits.
Changements appliqués :
Agrégation par seller_user_id depuis tout le catalogue chargé (/api/shop) : nb d’annonces, avis totaux, note moyenne (pondérée par les avis si possible, sinon moyenne sur fiches notées) ; tri vérifié Stripe d’abord, puis note, volume d’avis, nb de produits ; max 6 vendeurs.
UI : section titre + sous-texte, cartes cliquables (/shop?seller=…, conserve verified=1 si le filtre global est actif), avatar lg, badge check sur photo si vérifié, pastille offres + ligne avis + CTA « Voir la boutique » (icône Store).
SellerAvatar : variante taille lg (56px) pour les vedettes.
i18n : featuredSellersTitle, featuredSellersSubtitle, featuredSellerOffers, featuredSellerReviews, featuredSellerNoReviews, featuredSellerSeeShop (FR/EN + locales déjà enrichies du namespace sitePages.shop).
Impact visible / technique : découverte des vendeurs sans ouvrir le select ; liens cohérents avec le filtre vendeur existant ; section masquée si aucun vendeur identifié après chargement (edge case rare).
2026-05-01 — /shop liste : visuel produit en paysage
Contexte/problème : vignette carte en portrait (aspect-[5/7]). Souhait d’une zone image horizontale (format paysage), sans modifier la grille ni le style général des cartes.
Changements appliqués : conteneur visuel → aspect-video (ratio 16:9) ; object-cover conservé pour le remplissage de l’image.
Impact visible : rendu type bannière sur toute la largeur ; bloc texte/CTA reprend davantage la hauteur sous le visuel (carte globalement un peu moins haute à largeur égale).
2026-05-01 16:15 — Boutique + menu utilisateur + page commandes
Contexte/problème : besoin d’un rendu boutique plus lisible/cohérent (description, couleurs, densité, 4 colonnes, filtres plus confortables, suppression du « vendeur vérifié » et des tags « 100% fiable »), puis ajout d’une section utilisateur « Mes commandes » avec vue vendeur des achats.
retrait du badge visuel « vérifié » + retrait du filtre « vendeurs vérifiés » ;
suppression des fallback tags defaultGuaranteeFast/defaultGuaranteeSafe (affichage seulement des garanties réelles + tag « Populaire » si applicable) ;
grille catalogue à 4 cartes/ligne en desktop large (xl:grid-cols-4) ;
zone “Vendeurs en vedette” gardée mais triée désormais sur qualité/activité, et texte sous-titre aligné à gauche.
Menu utilisateur : ajout entrée Mes commandes (desktop dropdown + sheet mobile) via clé i18n profileMenu.myOrders.
Nouvelle API : GET /api/mes-commandes :
récupère les sessions Stripe complete,
construit les ventes liées au vendeur courant (metadata.seller_id) et les achats du compte courant (metadata.buyer_id),
enrichit avec pseudo acheteur/vendeur depuis users,
expose montant, méthode de paiement, date/heure de paiement, email acheteur (si dispo).
Nouvelle page : /mes-commandes avec deux onglets :
Ventes (vendeur : qui a acheté, quel produit, date+heure, moyen de paiement, montant),
Achats (historique acheteur).
i18n : ajout profileMenu.myOrders sur les locales actives ; adaptation du sous-titre featuredSellersSubtitle FR/EN pour supprimer la logique “vérifié en priorité”.
Impact visible / technique : page boutique plus homogène visuellement et moins “marketing absolu”; menu compte enrichi; traçabilité des commandes côté vendeur disponible en self-service.
2026-05-01 15:22 — Open Graph / Discord : descriptions ressources en format embed
Contexte/problème : souhait d’un texte og:description aligné sur le rendu type embed Discord (titre Markdown, ligne méta discrète, sous-titre « Description », extrait dans un bloc).
Changements appliqués : nouvelle fonction buildOgResourceDiscordDescription dans web/src/lib/og-lbxmb.ts (# titre, -# + méta entre backticks via metaLineResource, ### Description :, corps tronqué/strip dans une fence markdown) ; generateMetadata des layouts ressources/[slug] et resources/[id] bascule de buildOgDescriptionBlock à ce builder pour les métadonnées (guides/forum/boutique inchangés).
Impact visible / technique : lors du collage d’un lien ressource (Discord ou client OG), la description structurée se rapproche du format attendu ; limite d’extrait inchangée (480 car. + ellipses) ; guillemets simples si backtick parasite dans la méta.
2026-05-01 16:27 — Boutique : ajout des jeux (multi-sélection optionnelle) + filtre/tri côté listing
Contexte/problème : besoin de trier/filtrer la boutique par jeux, et de permettre au vendeur d’associer zéro, un ou plusieurs jeux à son produit lors de l’ajout/modification.
Changements appliqués :
Backend shop : ajout colonne games (JSON) avec migration défensive ADD COLUMN IF NOT EXISTS sur les routes /api/shop, /api/shop/[id], /api/shop/request; normalisation (trim, suppression vides, max 12) et persistance JSON à la création/édition.
API lecture : parsing JSON de games dans les réponses produits (/api/shop et /api/shop/[id]).
Formulaire boutique : nouveau bloc Jeux (optionnel) dans BoutiqueProductForm avec sélection multi depuis jeux existants + ajout manuel d’un jeu (Enter/bouton), tags retirables, envoi games dans le payload.
Listing /shop : nouveau filtre game (query string partagée), chips de filtre actif, clear global, et nouveau tri game_asc (A→Z).
Impact visible / technique : vendeur plus précis sur la cible produit ; navigation acheteur plus rapide via filtre jeu ; compatibilité conservée pour anciens produits sans jeux.
2026-05-01 16:30 — Boutique /shop : alignement des menus filtres/tri
Contexte/problème : les menus filtres/tri se cassaient visuellement sur deux lignes non alignées selon la largeur (ex. tri passant en dessous), ce qui donnait un rendu irrégulier.
Changements appliqués : refonte du layout de la barre de filtres en grille responsive (1 → 2 → 4 colonnes) pour les 4 selects (catégorie, vendeur, jeu, tri) ; suppression des largeurs fixes par select ; compteur de résultats déplacé sous la barre et aligné à droite.
Impact visible / technique : menus visuellement alignés et cohérents, sans décalage parasite entre lignes ; meilleure stabilité responsive.
2026-05-01 16:39 — Mes commandes : lien profil acheteur + boutique : correction finale alignement filtres
Contexte/problème : dans Mes commandes, l’acheteur n’était pas cliquable ; sur /shop, les menus de filtre restaient visuellement superposés selon la largeur.
Changements appliqués :
Mes commandes : en onglet ventes, buyerPseudo devient un lien vers /profil/[pseudo] (URL encodée), avec fallback “Acheteur inconnu”.
Shop filtres : séparation claire en 2 lignes (recherche puis grille filtres), grille des 4 selects forcée en sm:2 / lg:4 colonnes pour éviter l’empilement incohérent observé.
Impact visible / technique : navigation directe vers le profil acheteur depuis les ventes ; filtres boutique stables et alignés sans chevauchement.
Contexte/problème : erreurs HTTP 500 en GET/PUT /api/shop/{id} pendant l’édition boutique (stack navigateur + échec PUT), dues à l’usage direct de la colonne games selon l’état réel du schéma DB.
Changements appliqués :
ajout d’un garde-fou runtime hasGamesColumn() sur /api/shop/[id] (lecture information_schema) ;
fallback si colonne absente : lecture sans games, games=[] côté payload ;
PUT /api/shop/[id] : SQL dynamique avec/sans games pour éviter tout crash ;
POST /api/shop/request : insertion dynamique avec/sans games selon schéma disponible ;
/api/shop : retrait de l’ALTER auto games qui pouvait provoquer un échec de route selon contexte DB.
Impact visible / technique : fin des 500 sur fiche produit/édition lorsque la colonne games n’est pas encore présente ; compatibilité ascendante maintenue sans bloquer la boutique.
2026-05-01 16:59 — Boutique : réorganisation UX du formulaire ajout/modification produit
Contexte/problème : le formulaire /boutique/ajouter / /boutique/modifier était perçu comme “en bazar” (ordre des champs et densité visuelle peu lisibles).
Changements appliqués :
restructuration du contenu en sections visuelles cohérentes : Informations produit, Classement et jeux, Présentation visuelle et médias, Garanties & options ;
regroupement logique des champs (prix/stock ensemble, catégorie + jeux ensemble, mode visuel + YouTube ensemble, médias principaux côte à côte sur desktop) ;
amélioration du rythme vertical (espacements, blocs card internes homogènes) tout en conservant la logique métier et les validations existantes.
Impact visible / technique : formulaire plus lisible, hiérarchie claire, meilleure navigation lors de la création/édition sans régression fonctionnelle.
2026-05-01 17:01 — Correctif Internal Server Error sur création demande boutique
Contexte/problème : retour “Internal Server Error” après soumission produit ; cause probable sur la vérification de schéma (information_schema) dans POST /api/shop/request.
Changements appliqués : encapsulation du check de colonne games dans un try/catch avec fallback propre (gamesColumnAvailable = false) et warning serveur non bloquant.
Impact visible / technique : la soumission continue même si la vérification de schéma échoue (droits SQL restreints, environnement spécifique), ce qui évite les 500 côté utilisateur.
2026-05-01 17:10 — Boutique : bypass validation pour Dexus + suppression depuis l’édition
Contexte/problème : besoin de publier directement les produits de l’utilisateur Dexus sans étape de validation admin ; besoin d’un bouton de suppression sur la page de modification produit.
Changements appliqués :
POST /api/shop/request : si user.pseudo === "dexus" (insensible à la casse), insertion en status='approved' (pas pending) + pendingReview=false et pas de notification de validation admin.
Formulaire BoutiqueProductForm (mode édition) : ajout d’un bouton Supprimer le produit avec confirmation, appel DELETE /api/shop/{id}, état de chargement dédié, puis redirection vers /shop.
Impact visible / technique : Dexus publie immédiatement ses produits ; suppression possible sans passer par d’autres écrans lors de l’édition.
Contexte/problème : vignettes boutique en ratio paysage alors que les médias sont carrés ; besoin après achat de contacter le vendeur (messagerie) et de signaler un problème ; section dashboard commandes doit distinguer paiements réussis et signalements boutique.
Changements appliqués :
Shop listing : zone visuel des cartes produit en aspect-square (au lieu de aspect-video), min-h carte légèrement ajusté.
Mes commandes (onglet Achats) : boutons Contacter le vendeur (POST /api/messages/vendor/create-or-get puis ouverture panneau messagerie) et Signaler un problème (modale + description 15–8000 car.) ; badge Signalé si déjà en base ; GET /api/mes-commandes enrichi avec hasReport par session Stripe.
Persistance : table auto-créée boutique_order_reports (session Stripe unique, acheteur/vendeur/meta produit, montant, description, statut open) ; POST /api/shop/order-report vérifie acheteur via Stripe + idempotence ; GET /api/dashboard/boutique-order-reports pour le staff.
Notifications : notifyBoutiqueOrderReport notifie le vendeur (users.id), puis fondateurs + admins (pas modérateurs/développeurs) ; type boutique_order_report inclus pour DM Discord si activé ; lien staff /dashboard/orders?tab=signalements .
Dashboard → Commandes : onglets Radix Commandes réussies (filtres Toutes/Atelier/Boutique inchangés) vs Signalements boutique (cartes détail + compteur badge, chargement dédié), Suspense pour useSearchParams, rappel total signalements au chargement.
Contacter le vendeur : ContacterVendeurButton accepte vendorUserId / productId string ou number pour cohérence avec les user_id Stripe.
Impact visible / technique : grille boutique alignée sur des images carrées ; parcours acheteur clair ; staff centralise les litiges boutique ; schéma créé à la volée sur première utilisation (MySQL).
2026-05-02 — Bannière Discord HTML : CSS cassé après export navigateur
Contexte/problème : /discord/banner/1232966743241130005 servait une page « Enregistrer sous » avec des <link href="LA RÉFÉRENCE DU MODDING_files/….css"> et scripts locaux introuvables à cette URL relative → mise en page sans styles (classes Tailwind non appliquées).
Changements appliqués : helper sanitizeDiscordExportedBannerHtml — suppression scripts et feuilles de style relatives, preload invalides ; src/href vers lbxmb.png passés en absolu (NEXT_PUBLIC_SITE_URL sinon https://lbxmb.fr) ; injection de Tailwind Play CDN + couleurs background/foreground + CSS pour .text-gradient et .animate-float. Utilisation dans GET /discord/banner/[guildId] et /discord/logo/[guildId].
Impact visible / technique : rendu proche de la capture quand ouvert dans un navigateur (JS étranger cdn.tailwindcss.com). Si un proxy impose une CSP très stricte sans ce domaine, il faudra une CSS statique même origine.
2026-05-02 — Nouveau logo LB’XMB (fichier 1000232690-removebg-preview.png)
Contexte/problème : mise à jour de l’identité visuelle sur le site, les URLs Flask/bot (/static/…), Open Graph / Twitter et embarquements Discord qui pointaient sur l’ancien lbxmb.png.
Changements appliqués :
Remplacement binaire du logo public : web/public/lbxmb.png (500×500, fond transparent).
Miroirs pour URLs historiques bot / webhooks Flask : web/public/static/lbxmb.png, web/public/static/images/logo.png, et fichiers équivalents sous web/static/ pour server.py (QR 2FA, image par défaut upload).
Open Graph / Twitter : layout.tsx racine dimensions 500×500 + twitter.images ; fallback siteIcon en 500×500 dans les layouts forum / ressources.
PWA : manifest.json entrée icône 512 remplacée par 500×500 (toujours une entrée 192 pour compatibilité).
Bot : site_content_embeds.py — auteur embed « LB'XMB.FR » utilise par défaut {LBXMB_PUBLIC_URL}/lbxmb.png (surcharge possible DISCORD_GUILD_ICON_URL).
Impact visible / technique : previews sociaux et UI site servent le nouveau visuel ; footers/embeds utilisant déjà https://lbxmb.fr/static/images/logo.png reçoivent le même fichier. Avatar Discord de l’application bot reste celui défini dans le portail développeur Discord (hors dépôt) — à mettre à jour manuellement avec le même visuel si souhaité.
Contexte/problème : élargir les moyens de paiement aux achats ponctuels (boutique, dons, services one-shot, VIP) en plus de la carte.
Changements appliqués :
Module web/src/lib/stripe-payment-method-types.ts : défaut card, paypal, amazon_pay ; variables STRIPE_CHECKOUT_PAYMENT_METHOD_TYPES (global) et STRIPE_VIP_PAYMENT_METHOD_TYPES (VIP seul si défini — sinon même liste que le global après parse).
Branchement dans POST /api/stripe/checkout (boutique), /api/checkout (services : carte uniquement si mode subscription / hébergement récurrent pour éviter incompatibilités), /api/donate/checkout, /api/vip/checkout.
GET /api/mes-commandes : libellés français PayPal / Amazon Pay dans normalizePaymentMethod.
.env.example : commentaires sur les variables Stripe Checkout.
Impact visible / technique : la page Stripe Checkout affichera PayPal et Amazon Pay si activés dans le Dashboard Stripe pour le compte. Les abonnements services restent carte seule jusqu’à support explicite.
Contexte/problème : messages type « Content-Security-Policy warnings », police WOFF2 préchargée peu utilisée vite, « WebGL context was lost », lignes « Feature Policy: Skipping unsupported feature name … » (iframes / scripts tiers).
Changements appliqués :
web/src/lib/csp.ts : connect-src + frame-src étendus pour Amazon Pay (domaines *.amazon.com, *.amazonpay.com, pay.amazon.com, static-fe.payments-amazon.com) et iframe https://checkout.stripe.com pour cohérence avec Checkout hébergé.
web/src/styles/font.ts : suppression de Press Start 2P (jamais utilisée dans les pages alors que le module font.ts était chargé avec Montserrat) pour retirer préchargements / @font-face inutiles à l’accueil.
web/src/components/LightRays.jsx + web/src/components/ui/backgrounds/LightRays.tsx : respect de prefers-reduced-motion: reduce (pas d’init WebGL), écoute webglcontextlost avec arrêt propre de la boucle, suppression de WEBGL_lose_context.loseContext() au démontage (évitait des logs « context was lost » volontaires lors des changements de route).
web/next.config.js : commentaire précisant que Next.js 16 utilise src/proxy.ts (export proxy) — ne pas ajouter en parallèle middleware.ts (conflit build).
Impact visible / technique : CSP chargée via le Proxy (Middleware) comme sur le build (ƒ Proxy) ; moins de bruit précharge / WebGL lors de la navigation ; Amazon Pay moins exposé aux blocages CSP. Les avertissements Feature Policy liés à YouTube/embeds tiers restent du côté navigateur / contenu intégré, pas corrigibles localement sans retirer ces intégrations.
Contexte/problème : (1) réponses API /api/banner/import et config JSON avec ancienne URL /banner/{guildId} ; (2) page /discord/banner/{guildId} à valoriser pour affichage large + logo/stats ; (3) ne plus exposer /banner/ comme contenu servi ; (4) Stripe : aligner la version API sur le SDK npm ; (5) dashboard trop compact.
Changements appliqués :
Bot : normalize_lbxmb_discord_banner_url (/banner/(\d+)→/discord/banner/\2) dans banner_update_handler.load_config (persist JSON si correction) ; même normalisation lors de la soumission du modal Configurer l’URL bannière (setup_server.py).
POST /api/banner/import : écrit désormais data/discord/banner_html/{guildId}/banner.html, met à jour banner_config.json via setBannerConfigUrls, retourne {base}/discord/banner/{guildId}.
GET /banner/[guildId] : redirection 308 permanente vers /discord/banner/{guildId} (plus de rendu depuis uploads/banner-pages sur cette route).
GET /discord/banner/[guildId] : lecture HTML custom → fallback legacy uploads/banner-pages/.../index.html → sinon template défaut buildDiscordDefaultBannerHtml (stats MySQL ressources, guides, utilisateurs, Go) ; custom toujours passé par sanitizeDiscordExportedBannerHtml.
Impact visible / technique : anciens bookmarks /banner/id rejoignent la route canonique ; sans fichier HTML custom pour un guild, les captures bot voient une bannière « premium » mise à jour côté serveur ; mise à niveau npm stripe continuera à piloter la version API attendue ; UI staff plus aérée. Pour remplacer un vieux HTML importé par le nouveau template, supprimer ou remplacer le fichier data/discord/banner_html/{guildId}/banner.html sur le serveur.
Contexte/problème : /discord/banner/{guildId} s’affiche sans mise en forme (polices serif, mise en page plate) ; l’upload HTML prioritaire continuait à servir l’ancienne « présentation » sans Guides ; la CSP globale (strict-dynamic + nonce) bloquait cdn.tailwindcss.com donc aucune classe Tailwind ne s’appliquait.
Changements appliqués :
web/src/proxy.ts : pas d’en-tête CSP sur /discord/banner/* et /discord/logo/* pour permettre CDN / exports « Enregistrer sous ».
web/src/lib/discord-default-banner-html.ts : gabarit par défaut entièrement en CSS inline (aucun script, aucun CDN) — conforme CSP même sans exception de route.
web/src/lib/discord-banner-html-sanitize.ts : si le HTML importé n’a pas de <head>, enveloppe <!DOCTYPE html>… et injecte le patch (Tailwind + styles de secours).
GET /discord/banner/[guildId] : paramètres ?default ou ?template=default pour forcer le template serveur même si banner.html existe (aperçu sans supprimer le fichier).
Impact visible / technique : URL sans fichier custom affiche déjà une bannière stylée sans dépendre du CDN ; avec fichier ancien exporter, soit utiliser ?default, soit supprimer data/discord/banner_html/{guildId}/banner.html pour que le bot utilise le rendu défaut ; exports avec <head> manquant récupèrent Tailwind où la CSP route est assouplie.
2026-05-02 — Dashboard : bordures et zones plus lisibles (sidebar, stats, guides, todos, analytics)
Contexte/problème : zones admin perçues comme trop serrées ; bordures à faible contraste (border-white/5) ; carte Guides avec rounded-[2.5rem] qui nuisait à l’alignement visuel avec la sidebar ; fil d’Ariane « flottant » sans lien avec les cartons de contenu.
Changements appliqués :
Sidebar : bordure droite et séparateurs en white/[0.10–0.12], fond un peu plus marqué, espacement vertical entre liens (space-y-2, py-3), anneau léger sur l’élément actif / hover.
Header : barre inférieure plus visible ; fil d’Ariane encapsulé dans un pilule (ring + fond) pour le dissocier du contenu sans le coller aux bords.
DashboardStatsv2 : cartes rounded-2xl, bordure white/[0.14], ombre intérieure ; grille 3 colonnes jusqu’à 2xl (6 cols seulement sur très grand écran) ; gaps et paddings internes augmentés.
Vue d’ensemble : space-y-10 / 12, bloc trafic avec séparateur supérieur plus net.
AnalyticsDashboard : même logique de bordure et en-tête de section.
Todos (tasks-table-main) : carte arrondie, bordures et en-tête de carte plus marqués, padding tableau augmenté.
Guides : carte rounded-2xl uniquement ; bandeau titre séparé par une bordure basse ; tableau desktop dans un cadre rounded-xl + border avec en-tête sur fond léger et lignes hover.
Impact visible / technique : hiérarchie des blocs plus claire sans refonte de maquette ; cohérence des approx. 14 % blanc sur les contours.
Montserrat (Google Fonts 600–800) pour badge, titre, ligne « par », pastilles consoles.
Textes alignés messages/fr.json → home : badge, titleLine1 / titleLine2, sous-titre, stats Ressources / Utilisateurs / Guides / Stockage (données live inchangées) ; ligne par LoupBlanke · Interverti comme le Hero ; pastilles Xbox / Nintendo / PlayStation (couleurs type Hero).
Dégradé titre MODDING #60a5fa → #2563eb ; grille stats avec séparateur en dégradé comme l’accueil ; bloc logo avec halobleu / drop-shadow.
Impact visible / technique : GET .../discord/banner/{guildId}?default (ou sans fichier banner.html) affiche une bannière lisible comme une capture du hero, sans animation ni interactif ; charge fonts.googleapis.com (CSP déjà levée sur cette route).
2026-05-03 — Discord : plus d’embeds auto dans le salon vitrine, /script → website
Contexte/problème : le salon 1486674093121077379 recevait des mises à jour répétées (statut site ~2 min + vitrine ~6 h). Souhait : arrêter l’envoi / l’édition continue et publier à la demande une carte « site » (état, stats, stockage).
Changements appliqués :
LBXMB_SITE_EMBED_AUTO_ENABLED : la boucle LbxmbSiteEmbedHandler (édition périodique du message statut + boutons) ne démarre plus par défaut ; mettre à true / 1 pour réactiver. Pied d’embed adapté selon ce réglage.
SITE_SHOWCASE_AUTO_ENABLED : la boucle SiteShowcaseHandler (vitrine /api/stats/home) ne démarre plus par défaut ; true / 1 pour réactiver.
Commande /script : nouveau choix website — envoie un embed (état HTTP du site, membres, ressources, guides, stockage depuis /api/stats/home) + les boutons lien (Site, Forum, Ressources, Guides).
Impact visible / technique : plus de spam automatique dans ce salon tant que les variables ne sont pas activées ; le staff publie la carte via /script → website après sync slash du bot.
2026-05-03 ~14:00 — Web : fluidité / perf (accueil + layout, mobile & desktop)
Contexte/problème : objectif expérience plus fluide (LCP, moins de JS initial, WebGL et images moins agressifs sur mobile) après constats type Lighthouse.
Changements appliqués :
Accueil : HomeBackdrop charge LightRays en dynamic(..., { ssr: false }) avec fond CSS immédiat (dégradés type néons) ; bundle ogl sort du chemin critique.
HomeBelowFold : sections sous le hero (FeaturedMods, annonces, témoignages, partenaires, FAQ, Footer) en imports dynamiques avec placeholders légers.
LightRays.jsx : DPR plafonné selon largeur d’écran (mobile plus bas) pour soulager GPU / batterie.
Hero : image mockup avec sizes="(max-width: 1023px) 0px, …", priority={false} (colonne lg masquée sur mobile — évite gros chargement hors écran).
next.config.js : experimental.optimizePackageImports pour lucide-react et react-icons.
Layout : DeferredClientWidgets (client) regroupe Umami, Service Worker, modal d’accueil en dynamic ssr: false (compatible Next 16 — pas de dynamic ssr:false dans le Server Component racine).
app/loading.tsx : indicateur minimal pour navigations / attente segment.
Impact visible / technique : premier paint plus léger sur / ; scroll et interactions moins monolithiques ; à valider en prod avec Lighthouse + Network throttling mobile. Poursuites possibles : même logique sur Header (icônes), pages lourdes (dashboard, forum), entêtes cache Nginx sur assets.
Contexte/problème : poursuivre la fluidité au-delà de l’accueil : moins de WebGL/JS sur les parcours listés (ressources, guides, shop, etc.) + menu global.
Changements appliqués :
web/src/components/ui/DeferredLightRays.tsx : wrapper unique (fond CSS + dynamic(LightRays, { ssr: false })). HomeBackdrop re-exporte ce module pour l’existant.
Remplacement de l’import direct LightRays par DeferredLightRays sur : ressources/page, ressources/[slug]/ResourceDetailClient, guides/page, support/page, shop/page, shop/[id]/page, download, checker, demarrer-modding/*Client, etc.
Footer en next/dynamic sur les mêmes pages lourdes + forum (liste) pour scinder le chunk.
Header : chargement différé de recherche globale, menu profil sheet, cloches notif / messages, barre mobile basse (chunks séparés ; recherche + profil en ssr: false).
dashboard/layout : DashboardMobileSidebar en import dynamique (visible surtout sur petit écran).
Impact visible / technique : ogl n’est plus dans le bundle initial de ces pages ; en-têtes cache /_next/static déjà gérés côté Nginx (nginx-lbxmb.fr.conf) — pas de changement serveur requis.
2026-05-03 ~21:05 — Web : liste forum /forum, chunks dynamiques création sujet + création groupe
Contexte/problème : poursuivre l’allègement JS de la page liste forum en sortant Dialog / MentionTextarea / TagsMenu et le panneau création groupe du bundle initial synchrone.
Changements appliqués :
forum/page.tsx : remplacement du gros JSX inline par ForumThreadComposerLazy et ForumGroupCreatePanelLazy (next/dynamic, ssr: false, loading: () => null) ; filtrage d’affichage activeTab === 'fil' / 'groupe' comme avant fonctionnel ; imports retirés (Dialog, TagsMenu, MentionTextarea, Textarea, doubles Lucide), ForumCategoryRow réutilisé depuis forum-page-types.
ForumThreadComposer : props popularTags / allTags typées TagItem[] ; suppression des props setShowScheduleOptions / setShowVisibilityOptions (non utilisées dans le composant — toggles pilotés depuis la page).
TagsMenu.tsx : export du type TagItem pour le typage commun.
Impact visible / technique : premier chargement liste forum : MentionTextarea, TagsMenu, Dialog et panneau groupe chargés à la demande ; npm run build OK.
2026-05-03 ~22:15 — Bot Discord : VIP cosmétique vs booster, retour sur le serveur (pseudo + rôles site)
Contexte/problème : (1) les membres avec le rôle booster Discord 1308907900751577181 recevaient aussi le rôle cosmétique Premium 1385546866942410813 via _sync_booster_role_to_site et/ou la colonne vip du site. (2) au rejoin, compte lié : besoin de réappliquer pseudo + rôles issus du site (ex. permissions type support_playstation + table roles / roster).
Booster : plus d’ajout du rôle Discord 1385546866942410813 lors du sync boost (aligné sur GET /api/bot/check-vip = Premium payant uniquement).
get_expected_roles_for_user(..., paid_vip_discord=...) : le rôle VIP cosmétique n’est ajouté que si check_vip_api renvoie vrai ; appels mis à jour (on_member_join, on_member_update, sync_all_members_roles).
on_member_join : charge l’utilisateur via get_user_from_site (inclut roles, badges, all_permissions avec normalisation CSV/JSON depuis permission + all_permissions) ; sync_on_join_linked ( défaut true dans auto_nickname ) pour appliquer le pseudo au retour même si enabled est à false ; retrait du rôle cosmétique si présent alors que check-vip est faux.
Import check_vip_api depuis commands/vip (variable WEBSITE_BASE pour joindre l’API).
Impact visible / technique : boost seul ⇒ rôle serveur booster, pas le rôle Premium cosmétique ; abo actif ⇒ inchangé. Rejoin ⇒ nick + rôles site (dont mapping DB roles → Discord). Pour désactiver le nick auto au rejoin : config serveur auto_nickname.sync_on_join_linked = false.
Alignement synchro fichier / login : plus de mapping direct vip → 138554… dans les routes web et partner-role-sync ; sync_user_roles (Flask) reçoit vip_source et n’attribue le rôle cosmétique pour le slug vip que si ce n’est pas boost (sinon vip_premium / vip_booster côté site).
Contexte/problème : éviter une commande slash ; besoin d’un script CLI analysant les membres dont username / pseudo serveur / nom global correspondent exactement à user_12345 (préfixe + _ + 5 chiffres par défaut).
Changements appliqués :
Suppression de bot/commands/temp_accounts_audit.py (/comptes-temporaires retirée).
Nouveau bot/scripts/list_temp_pattern_members.py : API REST (urllib uniquement), pagination membres /guilds/{id}/members, motifs --prefix (défaut user) et --digits, export TSV (défaut bot/scripts/liste_comptes_user_pattern.txt).
.gitignore : bot/scripts/liste_comptes_*.txt pour ne pas versionner les listes membres (PII).
Impact visible / technique : python3 bot/scripts/list_temp_pattern_members.py ; options --mode contains, --stats, diagnostic automatique si --mode exact et 0 ligne (histogramme préfixe_ + chiffres).
2026-05-03 fin de journée — Bot : script comptes temp, motif type luigi_45892 / lol45_78965
Contexte/problème : le motif n’est pas limité à user_##### : toute partie gauche alphanum minuscule + _ + N chiffres (ex. luigi_45892, lol45_78965).
Changements appliqués :
list_temp_pattern_members.py : sans --prefix, filtre exact / contains sur ^[a-z0-9]{min…max}_\d{N}$ (bornes --min-left / --max-left, défaut 1–32) ; avec --prefix luigi, uniquement ce préfixe + --digits.
Sortie TSV par défaut : bot/scripts/liste_comptes_temp_pattern.txt (toujours ignoré via liste_comptes_*.txt).
Correction en-tête commentaire TSV : parenthèse fermée sur la ligne « Motif décrit ».
Impact visible / technique : sur le serveur de test, 315 membres matchés en mode exact (5 chiffres) ; relancer sur le bon guild avec un token GUILD_MEMBERS pour la prod.
2026-05-04 — Bot : script temp, même jour création (~snowflake) vs joined_at
Contexte/problème : mesurer parmi les pseudo au motif xxx_XXXXX combien ont rejoint le serveur le même jour calendaire (UTC) que la création du compte approximée par le snowflake utilisateur ; signal complémentaire « join quasi immédiat ».
Changements appliqués : list_temp_pattern_members.py — colonnes TSV join_serveur_date_utc, meme_jour_crea_join ; après export, lignes stdout avec décompte même jour et sous-total joined_at dans les 120 s après timestamp snowflake.
Impact visible / technique : exemple exécution locale (guild configuré, 315 hits motif) : 152 / 315 même jour UTC ; 92 avec join ≤ 120 s après création approx ; joined_at toujours présent sur cet échantillon (0 inconnu).
2026-05-04 (suite) — Bot : export dédié « même jour » (--only-same-day)
Contexte/problème : où consulter la liste des comptes au motif xxx_XXXXX avec création et join même jour UTC sans filtrer le TSV à la main.
Changements appliqués : --only-same-day — TSV et aperçu console limités à meme_jour_crea_join=oui ; sortie par défaut bot/scripts/liste_comptes_temp_same_day.txt (-o pour un autre chemin ; toujours couvert par liste_comptes_*.txt). En-têtes du fichier résument au motif / même jour.
Contexte/problème : le site tournait en rechargement infini : trafic HTML / navigation Next.js passait par Anubis alors que /_next/static, WebSocket tickets, etc. contournent déjà → incohérences (RSC, cookies, challenge) côté client.
Changements appliqués : location / pour lbxmb.fr / www.lbxmb.fr : proxy_pass vers web_cluster au lieu de anubis_lbxmb ; commentaires expliquant la cause ; timeouts lecture/envoi 120s (aligné pages lourdes). L’upstream anubis_lbxmb reste dans le fichier pour une réactivation ciblée ultérieure.
Impact visible / technique : plus de boucle de challenge sur le site principal ; protection anti-bot Anubis inactive sur ce vhost jusqu’à reconfiguration (ex. autre sous-domaine ou règles fines). À faire sur le serveur : sudo nginx -t && sudo systemctl reload nginx après déploiement du fichier.
Contexte/problème : aligner l’ajout du bot sur les règles discutées : Premium payant (API check-vip) sans limite de serveurs uniquement si le Discord a le rôle VIP (couronne) sur le serveur principal ; sinon le bot quitte. Booster / Partenaire (sans ce flux Premium) restent à 3 serveurs max.
Changements appliqués : _has_premium_crown_on_main (présence de VIP_ROLE_ID sur MAIN_GUILD_ID). Branchement vip_site avant booster_or_partner ; embeds MP dédiés (couronne manquante, limite 3, accueil illimité / cap 3). Suppression de _has_privileged_access (logique inline).
Impact visible / technique : abo Premium sans rôle sur LB’XMB ⇒ refus d’entrée ; Nitro booster / partenaire sans abo Premium site ⇒ inchangé hormis textes d’embed limite ; Premium + couronne ⇒ plus de plafond à 3 pour cet utilisateur.
Contexte/problème : suite des idées (profil bot par serveur, backups, commandes masse rôles/salons, permissions /backup orientées propriétaire par défaut).
Changements appliqués :
/config : entrée Profil du bot (pseudo via guild.me.edit, avatar/bannière via PATCH .../members/@me, description stockée dans bot_guild_profile), ligne d’aperçu dans l’embed d’accueil + retour BackButton.
/backup : stockage bot/data/guild_backups/ (snapshots + index 25 max / utilisateur), création snapshot (guild_snapshot : rôles, salons, overwrites, bans, rôles-membres plafonnés), liste menu, suppression (modal SUPPRIMER), application métadonnées serveur (nom, description, icône, bannière si feature BANNER) avec modal APPLIQUER ; accès backup : créateur ou propriétaire du serveur cible ; permission globale via has_backup_command_permission.
Site : web/messages/discordBot/fr.json & en.json (entrées commandes).
Impact visible / technique : resynchroniser les commandes slash (tree.sync / redémarrage bot). Restauration rôles/salons depuis backup : non implémentée dans ce lot (données déjà capturées pour évolution). /role//salon : défaut Admin/Support si pas de rôles dédiés dans /config (comme les autres commandes modéré).
Contexte/problème : aligner l’UI Discord sur un embed type Notification vidéos (état, nombre de suivis, intervalle par défaut, liste des suivis avec salon / options) ; boutons Ajouter / Modifier / Retirer + Retour ; plus de bouton Actualiser — le panneau ancré doit se mettre à jour après chaque action ; par suivi : intervalle, actif, message, rôle mention, salon (à l’ajout via modal après choix du salon).
Changements appliqués :
VideosNotificationsView : suppression des contrôles globaux (toggle global, rôle global, intervalle global UI, actualiser) au profit du flux ci-dessus ; refresh_config_panel_anchor(..., title_must_contain="vidé") après ajout / édition / retrait.
VideoFinishModal : fin de configuration après ChannelSelect (intervalle, message optionnel, actif, ID rôle optionnel) ; custom_message jusqu’à 1900 caractères.
EditVideoModal : max 5 champs Discord (pas d’édition du rôle dans ce modal) ; défaut salon si ID absent ; comparaison d’intervalle par suivi dans l’embed avec garde TypeError/ValueError ; on_submit : erreurs via response.is_done() + followup.
_sync_videos_master_enabled : flag global videos.enabled si au moins un suivi actif.
Impact visible / technique : l’embed titre « 🎥 Notification vidéos » reste filtré par vidé pour l’ancre ; pour modifier rôle ou message personnalisé après coup : soit recréer le suivi, soit évolution ultérieure (2ᵉ modal / select rôle).
Contexte/problème : backup = JSON serveur complet + application destructive contrôlée ; pas de modal APPLIQUER mais menu multi-sélection ; une seule commande /roles (plus de groupe /role) ; /salons (ex-/salon) sans hiérarchie rôle sur les overwrites ; /backup, /roles, /salons réservés au propriétaire par défaut (comme backup) ; option propriétaire pour autoriser l’attribution de rôles au-dessus du modérateur (sous le bot).
Changements appliqués :
guild_snapshot v2 : rôles non gérés + champs salons (topic toujours, nsfw/slowmode texte, bitrate/user_limit vocal/stage) ; unicode_emoji rôle si présent.
guild_backup_restore : suppression salons (profondeur) → rôles gérables → recréation rôles + positions → catégories/salons + overwrites (map IDs ou fallback nom si pas de restauration rôles) → apply_guild_branding → bans optionnels.
guild_backup.py : application via embed + ApplyPartsSelect (branding / roles / channels / bans) + bouton Lancer ; apply_guild_branding déplacé dans le module restore ; embed d’aide mis à jour.
bulk_roles_channels.py : /roles (menu action ajouter/retirer + rôles + membres) ; /salons ; permissions has_roles_command_permission / has_salons_command_permission (fallback clés role / salon pour anciennes configs) ; salons : manage_channels modérateur + bot par salon, plus de _hierarchy_ok sur le rôle cible.
Contexte/problème : après application, rôles dans le sens inverse du serveur d’origine ; création des salons incomplète (position à la création sans réordonnancement) ; métadonnées peu fiables (un seul guild.edit + téléchargement CDN sans en-têtes) ; besoin d’exclure encore les rôles bots / intégrations de la sauvegarde.
Changements appliqués :
guild_snapshot v3 : fonction _exclude_role_from_backup (managed, tags.bot_id, tags.integration_id) ; champ hierarchy_rank (ordre bas→haut parmi les rôles sauvegardés).
guild_backup_restore : tri restauration avec hierarchy_rank puis position ; après création, Role.edit(position=…) sous le rôle du bot (base + k) plutôt que edit_role_positions seul ; téléchargement icône/bannière avec User-Agent puis guild.edit séparés pour nom/description / icône / bannière ; salons sans position à la création, puis reorder_restored_channels (catégories puis groupes parent + bucket Discord) via ch.edit(position=i) ; petite pause asyncio.sleep anti rate-limit ; unicode_emoji au create_role avec repli si TypeError.
Impact visible / technique : recréer une nouvelle backup après déploiement pour embarquer hierarchy_rank (les anciennes backups retombent sur position API). Métadonnées : si une étape échoue, le rapport liste les segments en erreur sans tout bloquer d’un coup.
Contexte/problème : vérification après nouvelles captures : le JSON de backup contient bien les rôles/salons attendus, mais l'application pouvait encore produire un ordre de rôles inversé (effet de décalage pendant role.edit(position=...)), des salons manquants/non comptés et des métadonnées partiellement ignorées si description échouait dans le même guild.edit.
Changements appliqués :
guild_snapshot : ordre source des rôles basé sur guild.roles (ordre hiérarchique natif discord.py) au lieu d'un tri manuel (position, id).
guild_backup_restore :
apply_guild_branding en appels séparés (nom, puis description, puis icône, puis bannière) pour éviter qu'une erreur sur un champ bloque le reste.
_finalize_role_positions applique désormais les positions du haut vers le bas (reversed(zip(...))) pour supprimer l'inversion observée.
_map_overwrites(..., include_member_overwrites=False) : restauration structurelle centrée sur les overwrites de rôles (plus stable, moins d'échecs intermittents).
recreate_channels compte aussi les catégories créées et ajoute un diagnostic explicite des canaux manquants (Canaux manquants: X/Y + exemples noms).
Impact visible / technique : le snapshot 1fa830a8-854c-4ff2-9ec6-c794ae639575 est structurellement correct (17 rôles, 59 salons, types text/voice/news/forum/category). Le problème principal était bien côté application de la backup ; le rapport de restauration expose désormais clairement les éventuels restants.
Contexte/problème : après déploiement des correctifs précédents (« reversed » + Role.edit(position=…)), constat aucun changement côté utilisateur : ordre des rôles encore incohérent ; salons qui échouent sans détail exploitable.
Changements appliqués :
_finalize_role_positions : abandon des déplacements séquentiels (collisions avec les rôles managed encore présents sous le bot) ; liste managed (ordre actuel) + autres non gérés sous le bot + rôles recréés (backup) puis guild.edit_role_positions en une passe (entrées triées par position croissante).
recreate_channels : erreurs salon avec code HTTP et extrait de réponse Discord (forum/news/permissions notamment).
Impact visible / technique : redémarrer le bot pour charger le module ; le rôle du bot doit rester au-dessus des rôles repositionnés. En cas d’échec salon, le rapport doit afficher HTTP xxx exploitable pour la suite du correctif (features serveur / overwrites).
Contexte/problème : refaire le système de bout en bout pour un usage type Xenon : capturer un serveur puis l’appliquer sur un autre — l’ancienne logique mélangait tous les salons sans respecter l’ordre racine (salons sans catégorie vs catégories), ne restituait pas les réglages serveur Discord, ne réappliquait pas les rôles membres, et forum/news plantaient sans filet sur serveur cible plus pauvre en features.
Changements appliqués :
guild_snapshot v4 : sorting_bucket par salon ; guild_features liste triée ; bloc guild étendu (verification_level, default_notifications, explicit_content_filter, default_role_permissions).
guild_backup_restore : recréation salons en deux passes (entrées racine triées par position = catégories + canaux sans parent, puis enfants par parent_id et sorting_bucket/position) ; création centralisée _create_channel_from_snapshot avec forum→texte si pas COMMUNITY, news→texte en secours, fallback texte générique sur erreur HTTP ; apply_guild_server_settings (guild.edit groupé + default_role.edit) ; restore_member_roles_from_snapshot ; run_restore enrichi (avertissement features manquantes, ordre canaux → branding → réglages serveur → membres → bans) ; option server_settings séparée de branding`**.
guild_backup.py : menu d’application étendu (réglages serveur, rôles des membres) ; textes embed alignés sur le flux template.
Impact visible / technique : nouvelle backup pour profiter des champs v4 (les anciennes gardent un tri enfant moins précis sur le bucket). Cocher rôles + membres pour une restitution des rôles-utilisateurs ; limite 1200 lignes member_roles traitées par passe. Redémarrage bot + resync slash si besoin.
2026-05-04 23:50 — Backup : un rôle en bas à tort + salons sans Communauté
Contexte/problème : après restauration, un rôle (ex. Support PlayStation) restait tout en bas alors que le reste de la hiérarchie était correct ; salons forums/stages incomplets parce que le serveur cible n’avait pas la Communauté Discord activée alors que la backup source en dépendait.
Changements appliqués :
Rôles : éviter les doublons de même `id source dans le JSON (ne plus recréer ni dupliquer le map) ; dédoublonnage des entrées snapshot avant repositionnement ; refresh puis bulk systématique sur tout le bloc cible avec deuxième passe si Discord n’a pas appliqué les positions au premier coup ; recherche get_role + discord.utils.get` avant d’écarter une entrée ; snapshot : ignore les doubles rôles même id en boucle (garde-fou Discord).
Communauté : si la backup impose COMMUNITY (feature source ou salons forum / stage), tentative d’activation avant recréation des salons : niveau vérif/filtres tendance communautaire, guild.edit(community=True, rules_channel=…, public_updates_channel=…) avec création préalable de lbxmb-backup-reglement et lbxmb-backup-annonces ; flag community_effective propagé aux créations (cache `features` parfois en retard sur le client discord.py).
Salons : scène sans communautaire → création vocal avec note ; rapport [communauté] ….
Impact visible / technique : il faut Gérer le serveur sur le bot pour activer Communauté ; si Discord refuse (non-proprio, quotas, …), fallback forum/stage comme avant. Nouvelle backup utile après correctif snapshot doublons.
2026-05-05 00:25 — Communauté : vrais salons règlement + annonces depuis la backup
Contexte/problème : l’utilisateur veut le flux « comme Discord » : créer d’abord le salon annonces (texte), activer Communauté avec les mêmes salons que sur le serveur source (règlement + annonces), puis poursuivre la restauration sans ignorer ces canaux.
Changements appliqués :
guild_snapshot v5 : enregistrement rules_channel_id et public_updates_channel_id (API serveur source).
bootstrap_community_from_snapshot : résolution ids backup puis heuristiques (noms règle/charte/CGU ; type news ou noms annonces/actus) ; création texte + catégories parentes si besoin ; ch_id_map rempli pour que la suite saute la recréation de ces salons ; guild.edit(community=True, …) ; après réordonnancement, passage news pour le salon annonces si la source était news (pas le salon règlement).
Boucle enfants : parcourt toutes les catégories du JSON présentes dans la map (y compris catégories créées au bootstrap).
Impact visible / technique : nouvelle backup recommandée pour avoir les ids officiels ; sinon heuristique + repli lbxmb-backup-*. Toujours Gérer le serveur + Gérer les salons.
2026-05-05 00:45 — Restauration salons : enfants de catégorie + passe secours
Contexte/problème : après bootstrap Communauté (règlement + annonces OK), le reste des salons n’était pas créé.
Changements appliqués :
Boucle enfants : ne plus abandonner silencieusement si `guild.get_channel ne voit pas la catégorie — résolution via utils.get puis guild.fetch_channel ; si la catégorie reste introuvable, création des enfants à la racine avec avertissement au lieu de continue` (comportement ancien qui sautait toute la catégorie).
Passe secours après les boucles : tout salon encore absent (catégorie source absente de la map, parent invalide, etc.) est créé avec parent résolu ou sans catégorie.
`_snap_parent_id : 0 traité comme absence de parent ; snapshot : n’écrire parent_id que si category_id` est non nul.
Impact visible / technique : rapport plus explicite (⚠ catégorie cache / secours) ; redémarrage bot.
2026-05-05 01:10 — Salons sous catégories : regroupement par parent + types Discord
Contexte/problème : catégories recréées mais tous les salons à l’intérieur absents (capture LB’XMB bac à sable).
Changements appliqués :
_norm_snowflake : ids/parents normalisés (évite échecs de matching int/str).
fetch_channels() après la passe racine pour rafraîchir le cache guild avant les enfants.
Enfants : plus d’itération « une catégorie du JSON → liste des enfants » seule ; construction by_parent en parcourant tous les salons dont le parent est une catégorie présente dans ch_id_map (même logique de filtrage, autre ordre d’agrégation).
_is_category_snap : type entier 4 (API), chaînes 4, guild_category, .category.
_dtype_from_channel_entry : type salon entier API (0 texte, 5 news, 15 forum, etc.) en plus des noms d’enum.
Heuristiques règlement/annonces alignées sur _dtype_from_channel_entry.
Impact visible / technique : redémarrage bot ; nouvelle restauration ; si encore des trous, le rapport « Canaux manquants » + passes secours restent actifs.
2026-05-05 01:40 — Salons dans les catégories : @everyone source + objet catégorie + bitrate vocal
Contexte/problème : après les passes précédentes, constat utilisateur toujours : catégories visibles mais peu ou pas de salons à l’intérieur (souvent quelques salons racine seulement).
Changements appliqués :
_map_overwrites : si `source_guild_id (champ source_guild_id du JSON) est connu, l’overwrite rôle dont l’id vaut ce snowflake est mappé sur le @everyone du serveur cible (sur la source, c’est le même id que le guild ; ce rôle n’est pas dans la liste roles` de la backup).
recreate_channels : dictionnaire id catégorie source → objet `CategoryChannel rempli à la création ; complétion pour les catégories déjà dans ch_id_map (ex. bootstrap) puis résolution parent enfants/secours en priorité sur cet objet (évite get_channel`/cache fragile juste après création).
_ensure_parent_category_for_bootstrap : retour du parent via `_resolve_guild_category au lieu d’un simple get_channel`.
_create_channel_from_snapshot : bitrate vocal/scène plafonné à `guild.bitrate_limit` (serveur cible moins boosté que la source).
Journal restauration : jusqu’à 55 lignes d’erreurs salons (au lieu de 25) pour diagnostiquer les échecs résiduels.
Impact visible / technique : les salons sous catégories devraient être créés avec la bonne visibilité (deny/allow @everyone) et restés sous la bonne catégorie ; vocaux moins susceptibles d’échouer sur bitrate trop haut. Redémarrage bot.
Contexte/problème : besoin de voir ce qui se passe pendant création backup et restauration (jalons, groupes par catégorie, échecs HTTP) sans dépendre uniquement du rapport Discord tronqué.
Changements appliqués :
_restore_log dans guild_backup_restore : préfixe commun `[restore guild=… backup=… src=…] ; INFO pour jalons (run_restore, passes salons, bootstrap Communauté, fin secours / complet) ; DEBUG pour détail (groupes by_parent, résolution cat_obj) ; WARNING échecs création enfant/racine/secours, map incomplète, create_snapshot` définitif.
guild_snapshot.build_guild_snapshot : ligne INFO `[backup snapshot guild=… backup_id=…]` avec compteurs.
guild_backup.py : INFO après création JSON ; avant/après run_restore (parts + taille rapport).
Impact visible / technique : configurer le logger racine ou utils.guild_backup_restore / utils.guild_snapshot / commands.guild_backup en DEBUG pour le détail ; défaut INFO suffit pour les jalons. Redémarrage bot.
Contexte/problème : d’après `bot-python-error-*.log, la restauration allait jusqu’à by_parent (48 enfants sur 10 catégories) puis plantait sur Guild.create_text_channel() (argument type` / salon news) — d’où serveur sans tous les salons ; le message Discord ne montrait qu’une partie du rapport.
Changements appliqués :
`mk_text : n’envoie topic que si non None ; try/except TypeError sur create_text_channel puis repli sans news` (texte classique).
`_create_channel_from_snapshot : le except principal inclut aussi TypeError` avec message texte dans le fallback.
`run_restore retourne (log, Path | None) et écrit tout le rapport dans bot/data/guild_backups/restore_reports/restore_g\<guild\>_\<backup\>_\<ts\>.txt` ; une ligne dans le rapport indique le chemin.
`guild_backup.py` : prévisualisation 70 lignes + pièce jointe du fichier rapport si < 7,5 Mo.
Impact visible / technique : relance pm2 restart bot-python ; après restauration, ouvrir le fichier joint ou le dossier sur le serveur pour tout le détail ; les salons news ne doivent plus bloquer toute la suite.
Contexte/problème : besoin de retirer un avertissement depuis le flux casier avec choix interactif, et exposition d’une commande dédiée /unwarn (même logique).
Changements appliqués :
sanctions_handler : remove_warn_at_index(guild_id, user_id, index) pour supprimer une entrée dans warns (persisté dans sanctions.json).
views/warn_removal_view.py : menu Select (jusqu’à 25 warns les plus récents si liste longue), callback qui met à jour le message en succès.
/casier : paramètre action (« Consulter le casier » / « Retirer un avertissement ») ; retrait sous permission unwarn, même hiérarchie que pour les sanctions (helper can_moderate_sanctions_target ; membre absent du serveur = pas de check de rôles).
/unwarn : même menu de retrait, permission unwarn ; ajout dans PUBLIC_MODERATION_COMMANDS (setup_server), /help, i18n global_i18n pour les nouvelles descriptions.
Impact visible / technique : redémarrage / resync des commandes slash pour voir /unwarn et l’option action sur /casier ; en /config serveur, la commande unwarn peut recevoir des rôles dédiés (sinon même défaut Admin/Support que les autres mod_cmds).
Contexte/problème : besoin d’un équivalent analytics Discord directement via slash commands (sans dépendre de salons logs), d’un suivi DM bot, et correction de blocage /roles quand un rôle cible est au-dessus du modérateur mais sous le bot.
Changements appliqués :
Nouveau AnalyticsHandler (bot/handlers/analytics_handler.py) avec stockage JSON (bot/data/analytics/server_analytics.json) : comptage messages (total, par heure, par jour, par user, par salon), joins (avec tentative de code d’invite), événements récents, DMs reçus par le bot.
Hook runtime : initialisation dans main.py; collecte message/DM dans events/on_message.py; collecte join/leave + cache invitations dans events/logs_events.py.
Nouvelle commande /analytique (bot/commands/analytique.py) avec vues : générale, activité messages (tops users/salons), comptes temporaires (regex + même jour création/rejoin), invitations (top codes utilisés).
Nouvelle commande /logs (bot/commands/logs.py) pour consulter les événements récents en embed ; vue spéciale DM bot réservée aux owners bot.
Permissions/config UX : ajout analytique + logs dans les commandes mod configurables (PUBLIC_MODERATION_COMMANDS), descriptions dans /help, ajouts i18n.
Correction /roles (bulk_roles_channels.py) : un utilisateur avec permission administrateur peut attribuer des rôles au-dessus de lui tant que le rôle reste sous le bot.
Impact visible / technique : après redémarrage/sync, les nouvelles commandes apparaissent ; les analytics sont progressifs (plus de données au fil des heures/jours) ; les DM au bot sont consultables depuis /logs type=dm (owner bot).
Contexte/problème : la section Mini-Jeux n’apparaissait pas dans le menu /config.
Changements appliqués :
ajout de l’option Mini-Jeux dans le sélecteur principal /config (SetupServerSelect) ;
ajout du routage de callback vers une vue dédiée (MiniGamesView) ;
ajout d’un embed Mini-Jeux (_create_mini_games_embed) avec état Compteur, Suite de mots, Pendu ;
ajout de la ligne d’état Mini-Jeux dans le résumé principal /config ;
ajout du bloc mini_games dans la config par défaut serveur (server_config_handler) avec paramètres de base (counter/word_chain/hangman).
Impact visible / technique : la section Mini-Jeux est maintenant visible et ouvrable dans /config ; base de configuration prête pour brancher les boutons/handlers de gameplay.
2026-05-07 19:13 — Site : permissions spécifiques séparées (annonces/actualités/contenus)
Contexte/problème : les grades existent, mais besoin de permissions fines et séparées (annonces, actualités, ressources, guides, boutique, ajout de contenu) pour éviter de dépendre systématiquement d’un admin.
Changements appliqués :
Dashboard utilisateurs : ajout d’une section UI Permissions spécifiques (séparées) avec toggles dédiés :
ajout de checkers dédiés (canPublishResourceDirectly, canPublishGuideDirectly, canPublishShopDirectly, canWriteAnnouncements, canWriteNews) ;
requiresGuideChangeModeration / requiresResourceChangeModeration reposent maintenant sur ces permissions dédiées (et non plus uniquement fondateur/admin).
Routes API reliées :
resources/add : publication directe autorisée si permission ressource dédiée.
shop/request : auto-approbation autorisée si permission boutique dédiée.
forum-social/thread/create : contrôle explicite pour catégories annonce / actualite avec permissions dédiées.
Impact visible / technique : les comptes peuvent recevoir des droits de publication ciblés sans monter en grade global ; publication directe possible selon permission sans intervention admin sur les flux concernés.
2026-05-07 19:15 — Refacto “permissions spécifiques” (propre, centralisée, sans dépendre des rôles)
Contexte/problème : besoin d’un système de permissions fines soigné (pas du cas par cas resources/add) et clairement utilisable pour accorder des privilèges sans attribuer un rôle global.
Changements appliqués :
création d’un catalogue central web/src/lib/specific-permissions.ts :
clés canoniques, labels, descriptions ;
normalisation d’alias historiques vers clés officielles ;
utilitaire de normalisation des tokens.
resource-permissions.ts branché sur cette normalisation :
permission globale add_content utilisée comme “master switch” contenu.
dashboard édition utilisateur : UI branchée sur le catalogue central (labels/desc cohérents, maintenance facilitée).
API update user (dashboard/users/[id]) : normalisation des permissions au save (évite doublons/slug incohérents en base).
Impact visible / technique : système plus lisible, stable et extensible ; les privilèges spécifiques restent décorrélés des rôles globaux et s’appliquent de manière uniforme sur les routes métier.
2026-05-07 19:23 — Site : auto-remplissage GitHub (nom associé + version) lors d’ajout de ressource
Contexte/problème : lors de l’ajout d’une ressource, saisir un lien GitHub devait automatiquement compléter le nom associé (auteur GitHub) et la version (dernière release), pour éviter les saisies manuelles.
Changements appliqués :
ajout d’une nouvelle route API GET /api/github/repo-info qui :
valide et normalise l’URL GitHub ;
récupère les infos dépôt (owner, repo) ;
récupère la dernière release exploitable pour proposer un tag de version.
auto-remplissage de auteur avec le login owner GitHub ;
auto-remplissage de version avec le dernier tag release ;
protection anti-écrasement : si l’admin modifie manuellement auteur/version, l’auto-remplissage ne force plus ces champs.
Impact visible / technique : UX plus rapide à la création de ressource ; cohérence des métadonnées GitHub améliorée ; comportement robuste en cas d’erreur/rate limit GitHub (pas de blocage du formulaire).
2026-05-07 19:25 — Traduction : conservation des emojis (profil/ressources/vente/forum)
Contexte/problème : lors d’une traduction (ex. anglais -> français), les emojis présents dans les descriptions pouvaient disparaître dans le texte traduit.
Changements appliqués :
mise à jour de web/src/lib/libretranslate.ts :
ajout d’un masquage des emojis via jetons stables avant l’appel API LibreTranslate ;
restauration des emojis après traduction ;
adaptation de la clé de cache pour prendre en compte la version masquée.
Impact visible / technique : les emojis sont désormais conservés dans les contenus traduits sur toutes les zones utilisant /api/translate (forum, blocs description profil/ressource, etc.).
Contexte/problème : l’URL profil basée uniquement sur le pseudo devient fragile quand un utilisateur change de pseudo.
Changements appliqués :
ajout d’un système d’identifiant public stable (public_id) via web/src/lib/user-public-id.ts :
création de colonne users.public_id (auto-ensure) + index unique ;
si compte lié Discord, public_id = discord_id (quand possible) ;
sinon génération d’un identifiant unique pour comptes non Discord.
hooks/auth :
web/src/lib/auth.ts : nouveaux comptes initialisés avec public_id (Discord ID ou ID généré) ;
liaison Discord existante : attribution de public_id si manquant.
web/src/lib/auth-helpers.ts : backfill public_id à la volée pour comptes existants sans identifiant.
API profil :
web/src/app/api/users/[id]/route.ts accepte désormais public_id en lookup (/api/users/{id|public_id|pseudo|id_interne}) ;
expose publicId dans la réponse.
URL canonique :
web/src/app/profil/page.tsx redirige automatiquement vers /profil/<pseudo-courant> si l’accès se fait via ID ou ancien pseudo.
Impact visible / technique : un lien type /profil/<id> continue de fonctionner même après changement de pseudo, puis se corrige automatiquement vers l’URL pseudo ; identifiant stable unifié pour nouveaux comptes et comptes historiques touchés.
2026-05-07 19:34 — Dashboard admin : affichage/copie de l’identifiant public (PID)
Contexte/problème : besoin d’exposer l’identifiant stable dans l’interface admin pour faciliter le support, la modération et le partage de liens robustes.
Changements appliqués :
web/src/app/api/dashboard/users/route.ts :
inclut public_id dans la liste utilisateurs ;
assure/backfill public_id à la volée pour chaque ligne (Discord ID prioritaire, sinon généré).
web/src/app/api/dashboard/users/[id]/route.ts :
accepte public_id dans la résolution utilisateur ;
retourne public_id dans la payload de détail.
web/src/app/dashboard/users/page.tsx :
affiche PID sous le pseudo (mobile + desktop) ;
ajoute un bouton de copie rapide (feedback visuel check).
web/src/app/dashboard/users/[id]/edit/page.tsx :
affiche PID dans l’en-tête fiche utilisateur ;
bouton de copie dédié avec feedback visuel.
Impact visible / technique : les admins disposent immédiatement d’un identifiant stable copiable, indépendant des changements de pseudo ; lookup API dashboard compatible avec cet identifiant.
Contexte/problème : besoin d’éviter la multiplication de commandes (/acces_retirer, variantes ajouter/retirer) et d’avoir un flux unique dans /acces, avec un retour visible (non éphémère) pour le destinataire.
Changements appliqués :
refonte de bot/commands/acces.py :
/acces accepte maintenant une option action (Ajouter / Retirer) ;
suppression de la commande dédiée /acces_retirer (logique intégrée dans /acces) ;
duree rendue optionnelle (par défaut illimite pour l’ajout).
messages de confirmation :
réponses d’ajout/retrait envoyées en non éphémère (visibles dans le salon), avec mention explicite de l’utilisateur concerné ;
erreurs de permission/validation conservées en éphémère.
Impact visible / technique : UX simplifiée côté modération propriétaire (une seule commande /acces) ; suppression des anciennes commandes d’ajout/retrait ; notification visible immédiatement par le destinataire dans le salon.
2026-05-07 19:39 — Bot /acces : notification en MP au destinataire (pas de message salon)
Contexte/problème : la confirmation ne devait pas être envoyée dans le salon, mais directement en message privé à l’utilisateur ciblé.
Changements appliqués :
bot/commands/acces.py :
ajout/retrait d’accès envoie désormais un embed en DM à l’utilisateur concerné ;
réponse au propriétaire conservée en éphémère avec statut d’envoi (MP envoyé / DM fermés).
Impact visible / technique : plus de notification publique dans le salon ; information transmise discrètement au destinataire en MP ; meilleur contrôle en cas de DMs fermés.
Contexte/problème : le déploiement bun run deploy:prod échouait sur Turbopack avec erreur defined multiple times dans src/app/api/forum-social/thread/create/route.ts.
Changements appliqués :
suppression d’une ligne d’import dupliquée (parseUserPermissions, canWriteAnnouncements, canWriteNews) dans la route de création de thread.
relance du build local (bun run build) pour validation.
Impact visible / technique : l’erreur bloquante de compilation est levée ; build terminé avec succès. Il reste uniquement des warnings NFT liés à next.config.js (non bloquants pour cette release).
2026-05-07 20:22 — Rôles support unifiés + menu permissions/plateformes en édition utilisateur
Contexte/problème : besoin de supprimer le rôle gestionnaire_actualite, fusionner les rôles support Xbox/PlayStation/Nintendo en un rôle unique support_technique, et disposer d’un menu permissions ergonomique comme les menus rôles/badges.
Changements appliqués :
web/src/app/dashboard/users/[id]/edit/page.tsx :
retrait des rôles support_xbox, support_playstation, support_nintendo, gestionnaire_actualite de la sélection manuelle ;
ajout du rôle unique support_technique ;
ajout d’une sous-section plateformes support (Xbox, PlayStation, Nintendo et variantes demandées) avec sélection dédiée ;
refonte de la section permissions spécifiques vers un menu déroulant multi-sélection (style menu rôles/badges).
alignement des mappings de synchro Discord vers le rôle unifié support :
les anciens tokens support_xbox, support_playstation, support_nintendo restent acceptés en compatibilité mais mappent désormais vers le rôle Discord support unique.
accès dashboard mis à jour pour reconnaître support_technique :
web/src/context/DashboardContext.tsx
web/src/components/layout/Header.tsx
web/src/components/dashboard/DashboardHeader.tsx
web/src/lib/dashboard-utils.ts
publication forum catégorie actualités adaptée :
web/src/components/forum/ForumThreadComposer.tsx
web/src/app/forum/preview/page.tsx
acceptation de post_news (avec compatibilité gestionnaire_actualite conservée).
style profil ajouté pour le nouveau token support_technique :
web/src/app/profil/page.tsx.
Impact visible / technique : l’admin assigne maintenant un rôle support unifié, peut détailler les plateformes dans une sous-section dédiée, et gère les permissions spécifiques via un menu plus propre ; la synchro Discord reste rétrocompatible pour les anciens comptes.
2026-05-07 20:30 — Forum Actualité : correction lecture permissions + Guides : toast discret à la soumission
Contexte/problème : un utilisateur avec permission de publication actualités ne pouvait pas sélectionner la catégorie Actualité sur le forum ; en parallèle, l’envoi de guide affichait encore des modales navigateur (alert) au lieu d’une notification discrète.
correction de la normalisation des permissions utilisateur ;
découpe des permissions CSV/; (ex: membres,post_news) en tokens exploitables avant vérification catégorie.
web/src/app/forum/preview/page.tsx :
même correction de normalisation côté preview forum.
web/src/app/guides/ajouter/page.tsx :
remplacement des alert(...) par des toasts via useToast();
message discret de succès lors d’une soumission en attente de validation admin ;
redirection légèrement différée pour laisser le toast visible.
Impact visible / technique : la catégorie Actualité devient sélectionnable dès que la permission est réellement présente dans les tokens ; l’expérience d’envoi guide est modernisée avec notifications discrètes en bas d’écran (plus de popup navigateur bloquant).
2026-05-07 20:37 — Remplacement complémentaire des alert(...) par toasts discrets (UX)
Contexte/problème : plusieurs écrans utilisaient encore des modales navigateur bloquantes (alert) au lieu de notifications discrètes cohérentes avec l’UI.
2026-05-07 20:35 (UTC+2) — Fin du nettoyage global alert(...) → toasts (dont chat)
Contexte/problème : il restait encore des alert(...) bloquants sur plusieurs écrans majeurs, surtout chat, ce qui cassait la cohérence UX des notifications discrètes.
Changements appliqués :
conversion complète des alert(...) en toasts sur les écrans restants :
web/src/app/chat/page.tsx
web/src/app/checker/components/Checkers.tsx
web/src/app/dashboard/services/page.tsx
web/src/app/projects/[slug]/page.tsx
web/src/components/chat/VoiceChannel.tsx
ajout/intégration du hook toast (useToast) là où nécessaire ;
adaptation des appels avec niveaux (error/success/info) via wrappers typés compatibles avec l’API toast interne.
vérification de lint sur tous les fichiers modifiés (aucune erreur restante).
Impact visible / technique : plus de popup navigateur native dans le front (web/src) ; feedback utilisateur homogène en bas d’écran sur erreurs/succès/copie/actions de modération ; base UI plus stable pour les prochaines itérations.
Contexte/problème : besoin de transformer la section “Vérification” en “Outils” avec onglets dédiés, d’ajouter un convertisseur PSN→Base64 (site + bot), de corriger le rendu OpenGraph des ressources (markdown Discord inadapté + priorité média), d’ajuster la logique des permissions de publication sans validation, et de fiabiliser l’affichage avatar auteur sur certaines ressources.
ajout d’un outil PSN Base64 côté web avec deux sorties :
Version Simple (8 premiers caractères, padding \0, base64),
Version Unique (Recommandée) (SHA256, 8 premiers bytes, base64).
Commande bot /psn-base64 :
ajout d’une fonction Python build_psn_base64_versions() (base64 + sha256 digest tronqué) ;
nouvelle slash commande /psn-base64 avec embed :
titre PSN Account-ID,
champs Version Simple et Version Unique (Recommandée) en bloc code.
OpenGraph ressources :
abandon du format markdown Discord dans la description OG, remplacé par un bloc texte propre ;
priorité de l’image OG au média/rendu (incluant miniatures YouTube déjà calculées côté add), fallback logo ;
ajout de l’icône (logo/site) via icons metadata pour favoriser le petit visuel de carte ;
twitter:card passe en summary_large_image seulement si média dispo, sinon summary.
Permissions publication sans validation :
publish_resources, publish_guides, publish_shop restent les permissions spécifiques de bypass ;
retrait du bypass implicite via add_content pour ressources/guides/boutique ;
clarification du catalogue permissions : add_content devient permission héritée de création, sans auto-publication.
Avatar auteur ressource :
fallback avatar créateur amélioré (profil_auteur puis author_photo) pour éviter les cartes auteur sans image (cas type RaiDougie SPRX).
Impact visible / technique : navigation alignée avec “Outils”, checker plus clair par onglets, convertisseur PSN disponible sur site et Discord, embeds ressources plus robustes pour Discord/OpenGraph, permissions conformes au flux “tout le monde soumet / certains publient direct”, et affichage avatar auteur plus fiable.
2026-05-08 07:20 (UTC) — Embed Discord ressources : thumbnail/logo prioritaire + image large média
Contexte/problème : sur Discord, certains embeds “nouvelle ressource” affichaient l’avatar auteur en miniature au lieu du logo ressource, ce qui contredisait le rendu souhaité (logo en petit visuel, média/YT en grand visuel).
priorité forcée du thumbnail sur icon_url (logo ressource) ;
fallback sur author_avatar uniquement si logo absent ;
conservation de image_url pour la grande image (rendu/image ou miniatura YouTube).
Impact visible / technique : les prochains embeds Discord “nouvelle ressource” respectent le pattern demandé : logo en miniature et média principal en image large.
2026-05-08 07:31 (UTC) — Harmonisation embeds site (guides + boutique) : miniature/logo + image large
Contexte/problème : nécessité d’aligner les embeds “site” sur le même rendu visuel que les ressources (logo en thumbnail, média en grande image) pour guides et boutique.
Changements appliqués :
bot/handlers/flask_handler.py :
extension de _send_site_embed(...) avec support image_url en plus de thumbnail_url ;
application du rendu image large si URL distincte de la miniature ;
site_new_guide et site_new_shop câblés sur ce nouveau format.
web/src/app/api/guides/create/route.ts :
enrichissement de la commande Discord site_new_guide avec icon_url/image_url récupérés à partir de la première ressource liée (si présente) ;
conversion vers URLs absolues (/api/uploads/...) pour compatibilité Discord.
web/src/app/api/shop/request/route.ts :
enrichissement de la commande Discord site_new_shop avec image_url (priorité miniature YouTube, sinon galerie, sinon bannière) ;
conversion des assets (icon_url/images) en URLs absolues pour embeds Discord.
Impact visible / technique : les prochains embeds guides et boutique suivent la même logique visuelle que les ressources (logo en petit visuel + rendu média en grand visuel), avec des URLs directement exploitables par Discord.
Contexte/problème : besoin de n’afficher que la version unique pour PSN Account-ID, de passer la navigation sur une URL explicite /outils, et d’utiliser une icône “outils” dans la navbar.
Changements appliqués :
web/src/app/checker/components/Checkers.tsx :
retrait de l’affichage Version Simple dans l’outil PSN Account-ID ;
rendu conservé uniquement sur Version Unique (Recommandée).
bot/commands/utility.py :
embed /psn-base64 simplifié pour afficher uniquement Version Unique (Recommandée).
web/src/components/layout/Header.tsx :
lien navbar “Outils” redirigé vers /outils ;
icône remplacée par une icône dédiée outils (Wrench).
web/src/app/outils/page.tsx :
ajout de la route /outils (pointant sur la page outils actuelle).
Impact visible / technique : la section Outils est accessible via /outils depuis la navbar avec une icône adaptée, et la conversion PSN Account-ID n’expose plus la version simple sur le site et le bot.
Contexte/problème : malgré l’URL /outils, le contenu de page conservait un wording orienté “checker”, et l’onglet navigateur n’avait pas de metadata dédiée à la section Outils.
Changements appliqués :
web/messages/fr.json :
adaptation des textes de la section checker vers un wording “Outils” (badge, heroTitle, heroSubtitle, footerTitle, footerBody) ;
ajout de sitePages.outils.metaTitle et sitePages.outils.metaDescription.
web/messages/en.json :
mêmes ajustements de wording et ajout des clés metadata sitePages.outils.
web/src/app/outils/layout.tsx :
ajout d’un layout avec generateMetadata() pour définir le titre/description (OpenGraph + Twitter) spécifiques à /outils.
Impact visible / technique : la page /outils affiche désormais un intitulé cohérent “Outils” dans son hero et son bloc bas de page, et l’onglet navigateur utilise un titre/description propres à cette section.
Contexte/problème : la section "Codes d'erreur" ne couvrait que la PS5, alors que le besoin etait d'inclure aussi PS4, PS3 et Xbox 360 avec une lecture des codes et des pistes de resolution.
ajout des checkers PS4ErrorChecker, PS3ErrorChecker, Xbox360ErrorChecker.
web/src/app/checker/page.tsx :
integration des 3 nouvelles cartes dans l'onglet Codes d'erreur ;
conservation des checkers de compatibilite dans l'onglet dedie via filtrage d'IDs.
Ajout de bases locales versionnees :
web/public/json/ps4-error-codes.json
web/public/json/ps3-error-codes.json
web/public/json/xbox360-error-codes.json
chaque entree contient code, description, solution.
web/messages/fr.json et web/messages/en.json :
ajout des libelles de cartes, placeholders, etat de chargement, messages "introuvable", titre d'erreur par console, et label solution.
Impact visible / technique : l'onglet erreurs devient multi-consoles (PS5/PS4/PS3/Xbox 360) avec consultation immediate de codes et solutions de base, extensible simplement par enrichissement des JSON publics.
2026-05-08 09:58 (UTC+2) — Script de synchronisation base erreurs (PS4 officiel)
Contexte/problème : besoin d'une base "vivante" des codes d'erreur avec mise a jour automatique depuis une source fiable, plutot qu'un simple JSON statique maintenu a la main.
Changements appliqués :
ajout du script web/scripts/sync-error-codes.mjs :
telechargement de la page officielle PS4 (playstation.com/.../error-codes/ps4) ;
parsing automatique des codes + descriptions ;
generation de solutions generiques par famille de code (NW, SU, WS, etc.) ;
merge intelligent avec la base locale (conservation des solutions/overrides manuels existants) ;
ecriture de web/public/json/ps4-error-codes.json avec metadata de source et horodatage.
ajout d'une commande npm :
web/package.json -> sync:error-codes pour lancer la synchro en une commande.
execution de la synchro :
resultat: parsed=215, written=216 dans la base PS4 locale.
Impact visible / technique : la base PS4 peut maintenant etre maintenue automatiquement depuis la source officielle tout en preservant les enrichissements locaux ; l'equipe peut rafraichir les codes via npm run sync:error-codes.
2026-05-08 10:40 (UTC+2) — Ressources OpenGraph: format description style Discohook (# [Titre](url) + meta en code inline)
Contexte/problème : demande d'aligner le rendu OpenGraph des ressources sur un style Discord/Discohook avec titre cliquable dans la description et ligne meta visuelle en code inline.
Changements appliqués :
web/src/lib/og-lbxmb.ts :
adaptation de buildOgResourceDiscordDescription(...) pour generer :
# [Titre](url) quand l'URL est disponible ;
ligne meta en inline code : ` Ressource / ... ` ;
description nettoyee en texte simple (sans heading/bloc code additionnel).
web/src/app/ressources/[slug]/layout.tsx :
bascule de la generation de description OG vers buildOgResourceDiscordDescription(...) ;
passage de l'URL ressource canonique a la fonction pour produire un titre cliquable.
Impact visible / technique : les embeds de ressources utilisent maintenant un format markdown plus proche de ton usage Discohook dans la description ; rendu plus propre avec titre-lien et ligne meta sur fond code (selon support client Discord).
2026-05-08 11:06 (UTC+2) — Outils PSN: retrait "(Recommandée)" + URL directe des onglets via query param
Contexte/problème : demande de simplifier le libelle PSN Base64 ("Version Unique" sans suffixe) et de pouvoir ouvrir directement chaque onglet Outils via une URL dediee.
Changements appliqués :
web/messages/fr.json et web/messages/en.json :
mise a jour du libelle PSN Base64 : suppression de "(Recommandée)/(Recommended)".
web/src/app/checker/page.tsx :
ajout de la lecture du parametre URL tab (compatibility, errors, psn) ;
synchronisation de l'etat d'onglet avec l'URL ;
mise a jour de l'URL a chaque changement d'onglet (sans reload complet) via router.replace.
Impact visible / technique : l'outil PSN affiche maintenant "Version Unique" uniquement ; chaque onglet est partageable et accessible en lien direct (ex: ?tab=psn, ?tab=errors).
2026-05-08 11:18 (UTC+2) — OpenGraph ressources: gestion propre des videos pour la grande image Discord
Contexte/problème : verification du rendu OpenGraph Discord quand une ressource contient une video ; Discord attend une image pour og:image (pas un flux video brut).
Changements appliqués :
web/src/app/ressources/[slug]/layout.tsx :
ajout du support video_youtube dans la generation metadata ;
priorite a la miniature YouTube (img.youtube.com/.../maxresdefault.jpg) comme grande image OG quand un lien YouTube est present ;
filtrage des medias media_rendu/medias pour ne garder que les formats image (ignore les .mp4, .webm, etc.) ;
fallback maintenu vers logo/site icon si aucune image exploitable.
Impact visible / technique : les liens ressources avec video YouTube obtiennent une grande image stable dans l'embed Discord ; les medias video locaux ne cassent plus le summary_large_image car seuls les medias image sont utilises pour og:image.
Contexte/problème : besoin d'un rendu Discord plus propre et homogene pour les ressources, avec focus visuel sur le titre-lien, la meta lisible et une description compacte.
Changements appliqués :
web/src/lib/og-lbxmb.ts :
ajustement du format OG ressources pour inclure la description en bloc code, tout en gardant :
Contexte/problème : le deploy etait bloque par une erreur TypeScript sur buildOgResourceDiscordDescription(...) (signature mise a jour, appel legacy dans la route /resources/[id]), et un lien direct vers un .apk retournait 404.
Changements appliqués :
web/src/app/resources/[id]/layout.tsx :
alignement de l'appel buildOgResourceDiscordDescription(...) avec la nouvelle signature (ajout de resourceUrl) ;
reutilisation de resourceUrl dans openGraph.url.
verification stockage uploads :
controle du dossier /root/lbxmb.fr/uploads/Ressources/PS4/Tools/Chiaki : seul chiaki.nro present ;
aucun fichier Chiaki-v2.2.0-Android.apk trouve sur le serveur (d'ou le 404).
Impact visible / technique : build debloque cote TypeScript pour ce point ; le 404 du lien .apk est confirme comme un fichier manquant (pas un bug route OpenGraph).
2026-05-08 11:54 (UTC+2) — Audit automatique des liens Chiaki (detection de liens morts)
Contexte/problème : besoin d'un scan rapide et repetable pour detecter les liens telechargement morts des ressources Chiaki, au lieu d'un controle manuel URL par URL.
Changements appliqués :
ajout du script web/scripts/audit-chiaki-links.mjs :
recupere les ressources ciblees (nom/description/dossier_ressource contenant chiaki) ;
inspecte les chemins de telechargement (DB, telechargements.json, variantes via /api/resources/{id}) ;
verifie l'existence physique des fichiers dans /root/lbxmb.fr/uploads ;
genere un rapport JSON : web/scripts/audit-chiaki-links-report.json.
ajout de la commande npm :
web/package.json -> audit:chiaki-links.
execution du scan :
1 ressource inspectee, 1 ressource avec liens morts ;
liens manquants detectes sur variantes Windows, Linux, MacOS, Android de Chiaki.
Impact visible / technique : diagnostic des 404 Chiaki automatisé et reproductible ; permet de lister immediatement les fichiers manquants a uploader/corriger.
2026-05-08 12:18 (UTC+2) — /analytique temp_accounts : grille de risque + bouton liste utilisateurs
Contexte/problème : besoin de qualifier rapidement si les ratios comptes suspects sont "normaux" et d'obtenir un accès direct a la liste des utilisateurs concernes.
Changements appliqués :
bot/handlers/analytics_handler.py :
enrichissement du resume statistique avec les IDs :
temp_account_ids
same_day_account_ids.
bot/commands/analytique.py :
ajout d'une grille de seuils visuelle (vert/orange/rouge) :
regex pseudo : avertissement 3%, critique 7%
cree+rejoint meme jour : avertissement 8%, critique 12%
ajout d'un bouton interactif Afficher la liste des utilisateurs ;
envoi d'un embed detaille listant les membres detectes dans chaque categorie.
Impact visible / technique : la vue temp_accounts devient actionnable (lecture du niveau de risque + drill-down immediat des comptes), sans quitter Discord.
Contexte/problème : les uploads de variantes (Chiaki) provoquaient des 500 pendant l'edition car le backend stoppait toute sauvegarde si une ancienne variante pointait vers un fichier absent (Fichier variante introuvable).
Changements appliqués :
web/src/lib/execute-dashboard-resource-put.ts :
dans le traitement des variantes, si node.chemin est une URL HTTP(S), conservation directe sans copie locale ;
si un ancien chemin local est introuvable, conversion du throw en warning non bloquant et conservation des metadonnees du noeud ;
permet une reparation progressive (upload variante par variante) sans echec global.
verification:
build Next.js valide (npm run build OK).
Impact visible / technique : l'UI VariantEditor ne tombe plus en erreur 500 a chaque ajout de fichier lorsqu'il existe deja des variantes orphelines; les modifications peuvent etre enregistrees incrementalement.
2026-05-08 13:07 (UTC+2) — Traduction FR complete de la page Outils
Contexte/problème : la section checker en français contenait encore plusieurs libellés anglais (sous-titres de cartes, intitulés PSN, textes techniques), ce qui donnait une interface mixte FR/EN.
Contexte/problème : besoin d'un onglet dedie pour verifier rapidement numero/mail/domaine (risque + appartenance), et demande d'afficher plus d'infos exploitables pour l'Account-ID PSN (Apollo/Chiaki).
Changements appliqués :
web/src/app/checker/components/Checkers.tsx :
enrichissement du tool PSN avec derivees supplementaires :
Account-ID hexadecimal (0x...)
Account-ID decimal
pseudo normalise + longueur
ajout d'un nouveau composant SafetyIntelTool :
detection du type d'entree (telephone, email, domaine/url)
normalisation de la valeur
score de risque heuristique (faible/moyen/eleve)
tentative de recuperation d'infos de proprietaire/registrar/pays via RDAP.
web/src/app/checker/page.tsx :
ajout d'un 4e onglet tab=safety dans la page /outils
nouvelle section Vérification anti-arnaque avec la carte Analyse rapide anti-arnaque.
web/messages/fr.json et web/messages/en.json :
ajout complet des labels/traductions pour l'onglet sécurité et les nouveaux champs PSN.
Impact visible / technique : la page Outils couvre maintenant un usage anti-phishing de premier niveau (SMS/mail/liens) et fournit des informations PSN plus utiles pour les workflows jailbreak (Apollo/Chiaki).
2026-05-08 13:16 (UTC+2) — Enrichissement base codes erreur PS3 depuis source communautaire
Contexte/problème : besoin d'etendre la couverture des codes PS3 dans l'onglet Outils a partir de la page partagee (ps3tuto.com/codeserreur.php), la base locale etant trop courte.
Changements appliqués :
web/public/json/ps3-error-codes.json :
ajout d'un lot important de codes PS3 manquants (reseau, PSN, mise a jour, disque, stockage, affichage, NAT, DNS) ;
normalisation des descriptions/solutions en format court et actionnable ;
conservation des codes deja presents et dedoublonnage final.
Impact visible / technique : le checker PS3 retourne des resultats sur beaucoup plus de cas concrets, avec des conseils immediats de depannage.
Impact visible / technique : les releases GitHub peuvent maintenant se mettre a jour automatiquement a intervalle raisonnable, sans depender uniquement des modifications manuelles de ressources.
2026-05-08 15:31 (UTC+2) — Ban IP compatible Cloudflare proxied (extraction + blocage global + API admin)
Contexte/problème : avec Cloudflare en mode proxied, l'IP client reelle doit etre lue via les headers Cloudflare; besoin de bannir efficacement une IP depuis le site.
Changements appliqués :
ajout de web/src/lib/client-ip.ts :
extraction/normalisation IP robuste (CF-Connecting-IP, True-Client-IP, X-Forwarded-For, X-Real-IP) ;
normalisation IPv6 mapped (::ffff:) et localhost.
ajout de web/src/lib/ip-bans.ts :
creation auto de la table ip_bans ;
helpers banIp, unbanIp, isIpBanned.
ajout de web/src/app/api/internal/ip-ban/check/route.ts :
endpoint interne securise par IP_BAN_INTERNAL_SECRET pour verifier si une IP est bannie.
ajout de web/src/app/api/admin/ip-bans/route.ts :
GET liste des bans ;
POST ban/maj ban ;
DELETE unban ;
acces restreint aux comptes staff eleves (admin/fondateur).
mise a jour de web/src/proxy.ts :
verification ban IP sur toutes les routes dynamiques (pages + API) via endpoint interne ;
reponse 403 immediate si IP bannie ;
garde-fous anti-boucle (x-ip-ban-check) et fail-open en cas d'erreur interne.
mise a jour de web/src/lib/rate-limit.ts et web/src/app/api/resources/[id]/download/route.ts :
harmonisation de l'extraction IP avec priorite Cloudflare.
Impact visible / technique : bannissement IP possible depuis le back-office API tout en conservant l'IP reelle client derriere Cloudflare; blocage applique en amont sur la navigation et les appels API du site.
2026-05-08 15:45 (UTC+2) — Tracking IP visiteurs + ip-api + association compte + blocage VPN/Datacenter
Contexte/problème : besoin de journaliser les IP dès la visite anonyme, scanner seulement les nouvelles IP via ip-api, associer les IP au compte à la connexion/création, bloquer IP bannies et IP VPN/proxy/datacenter, puis afficher les IP utilisées dans la fiche utilisateur dashboard.
Changements appliqués :
ajout de web/src/lib/ip-tracking.ts :
creation auto des tables ip_intel, ip_visits, user_ips ;
scan ip-api uniquement pour IP nouvelles publiques ;
enregistrement des visites anonymes (visitor_token) ;
association visiteur -> utilisateur ;
verification de blocage (IP bannie, VPN/Proxy, Datacenter/Hosting).
ajout de web/src/app/api/internal/ip-track/hit/route.ts :
endpoint interne securise (IP_TRACK_INTERNAL_SECRET) pour log visite + scan.
mise a jour de web/src/proxy.ts :
creation/maintien d'un cookie visitor_token ;
envoi asynchrone d'un hit de tracking a chaque visite ;
blocage global 403 via verif interne (IP bannie + flags ip-api).
mise a jour de web/src/app/api/auth/login/route.ts :
utilisation du nouveau moteur de controle IP ;
enregistrement IP de connexion et association au compte.
mise a jour de web/src/lib/auth-helpers.ts :
association IP -> utilisateur sur sessions authentifiees (y compris comptes crees automatiquement via BetterAuth).
mise a jour de web/src/app/api/dashboard/users/[id]/ips/route.ts :
suppression de l'ancien decrypt shell Python ;
retour des IP + infos ip-api (pays, ville, ISP, proxy/hosting, date scan).
mise a jour de web/src/app/dashboard/users/[id]/edit/page.tsx :
affichage enrichi des infos IP (geoloc/ISP/flag risque).
Impact visible / technique : tracking IP complet et coherent (anonyme -> compte), scans ip-api optimises (uniquement nouvelles IP), blocage automatique des IP a risque, et meilleure visibilité moderation dans le dashboard utilisateur.
2026-05-08 15:58 (UTC+2) — Sauvegarde disaster-recovery: dump MariaDB complet intégré aux archives
Contexte/problème : les backups existants ne garantissaient pas la restauration complete des donnees applicatives (users, guides, etc.) car la base MariaDB n'etait pas incluse dans l'archive principale.
Changements appliqués :
web/src/app/api/dashboard/backups/route.ts :
ajout d'un dump MariaDB complet (mysqldump puis fallback mariadb-dump) lors de la creation d'un backup dashboard ;
injection d'un dossier backup-meta/ dans l'archive contenant :
mariadb.sql
RESTORE-QUICKSTART.md (commandes de reprise rapide) ;
conservation de l'exclusion uploads/Ressources pour eviter l'explosion de taille (50Go+).
scripts/backup_web.sh :
passage d'un backup "web seul" a un backup complet projet + dump MariaDB ;
ajout de backup-meta/mariadb.sql + guide de restauration dans l'archive cron ;
retention ajustee sur le nouveau pattern lbxmb_backup_*.tar.gz.
Impact visible / technique : en cas de crash serveur, une archive suffit a remettre l'application et la base en ligne rapidement (hors gros assets uploads/Ressources volontairement exclus).
Contexte/problème : la route de téléchargement de backup était directement exploitable via URL si la session était active ; besoin d'un verrou supplémentaire côté serveur.
ajout d'un nonce stocké en Redis (backup-dl:*) pour invalider le lien après 1 utilisation ;
refus explicite sans token / token expiré / token réutilisé.
web/src/app/dashboard/sauvegardes/page.tsx :
le bouton Télécharger passe par la génération du lien temporaire avant ouverture.
Impact visible / technique : même en connaissant la route de fichier, un téléchargement n'est plus possible sans jeton court, signé, lié à l'utilisateur et consommé une seule fois.
Contexte/problème : le déploiement bun run deploy:prod échouait sur plusieurs routes API car getClientIp n'était plus exporté depuis src/lib/rate-limit.ts alors que ces routes l'importaient encore.
Changements appliqués :
web/src/lib/rate-limit.ts :
réintroduction d'un export getClientIp(request) en compatibilité ;
délégation vers le helper central src/lib/client-ip.ts.
Impact visible / technique : build Next.js de production à nouveau valide sans casser les imports existants; pas de régression fonctionnelle attendue.
Contexte/problème : la bannière Discord envoyée par le bot pouvait rester figée sur un ancien rendu (capture de page non rafraîchie) malgré les mises à jour.
Changements appliqués :
banner_generator.py :
ajout d'un cache-buster _banner_ts dans l'URL de capture ;
passage de page.goto(..., wait_until="domcontentloaded") à wait_until="networkidle" pour attendre un rendu plus stable.
bot/handlers/banner_update_handler.py :
ajout d'un cache-buster _bot_ts appliqué aux URLs banner_url et logo_url avant génération Playwright.
Impact visible / technique : chaque update de bannière force désormais un rendu frais (anti-cache CDN/navigateur), ce qui évite l'envoi répétitif d'un ancien screenshot.
Contexte/problème : l'affichage dashboard montrait souvent uniquement l'IPv6 cliente alors que l'objectif est de conserver les deux familles d'adresses quand elles sont présentes.
Changements appliqués :
web/src/lib/client-ip.ts :
ajout de getClientIps(request) pour extraire toutes les IP utiles (CF, True-Client-IP, X-Forwarded-For, X-Real-IP) ;
getClientIp devient un fallback sur la première IP.
web/src/lib/ip-tracking.ts :
ajout de recordIpVisits(...) pour journaliser un lot d'IP (dedup) ;
ajout de checkIpSecurityBlockMany(...) pour valider toutes les IP candidates.
web/src/proxy.ts :
envoi des IP multiples (ips) au hit interne de tracking.
web/src/app/api/internal/ip-track/hit/route.ts :
support du payload ips[] + compat ip unique.
web/src/app/api/auth/login/route.ts :
contrôle sécurité sur toutes les IP détectées ;
enregistrement en base des IP multiples à la connexion.
web/src/lib/auth-helpers.ts :
association session -> utilisateur avec enregistrement des IP multiples.
Impact visible / technique : la fiche utilisateur peut désormais contenir les deux adresses (IPv4 + IPv6) lorsqu'elles transitent dans les headers, avec un tracking cohérent anonyme puis authentifié.
2026-05-08 16:40 (UTC+2) — Filtrage IP locales/privées (suppression bruit 127.0.0.1)
Contexte/problème : la section dashboard des IP affichait des adresses locales (127.0.0.1) issues de trafic interne, ce qui polluait l'historique utile moderation.
Changements appliqués :
web/src/lib/ip-tracking.ts :
arrêt de l'enregistrement des IP locales/privées dans recordIpVisit ;
filtre identique appliqué lors de l'association ip_visits -> user_ips.
Impact visible / technique : la liste "Adresses IP de connexion" n'affiche plus les adresses internes parasites et reste centrée sur les IP clientes publiques réellement exploitables.
Contexte/problème : la bannière ne se mettait pas à jour de façon fiable car banner_config.json contenait plusieurs entrées pour un même guild_id (certaines anciennes en /presentation), ce qui pouvait réinjecter un rendu obsolète selon le flux d'update.
Changements appliqués :
bot/handlers/banner_update_handler.py :
ajout d'un dédoublonnage automatique au chargement (_dedupe_servers) ;
ajout d'un score de priorité (_score_server_config) pour conserver la meilleure entrée par serveur (auto-update actif + URL /discord/banner/ priorisée) ;
fusion des doublons en conservant les attributs utiles et union logique de auto_update.
Impact visible / technique : une seule configuration cohérente par serveur est désormais utilisée, ce qui stabilise les mises à jour de bannière et évite le retour involontaire à des URLs legacy.
Contexte/problème : le menu utilisateur exposait "Mes commandes" alors que le besoin est une entrée unique "Espace Client" regroupant achats, ventes, services, facturation et éléments en attente, avec traduction complète.
Changements appliqués :
web/src/components/layout/ProfileMenuSheet.tsx :
remplacement de l'entrée "Mes commandes" par "Espace Client" pointant vers /espace-client.
web/src/components/layout/Header.tsx :
remplacement du lien dropdown "Mes commandes" par "Espace Client" ;
retrait de l'entrée dédiée "Mes services" dans le dropdown pour éviter les doublons de navigation.
web/src/app/espace-client/page.tsx (nouveau) :
création d'une page centralisée avec KPI + onglets : achats, ventes, services, facturation, en attente ;
agrégation des données via /api/mes-commandes et /api/mes-services ;
ajout d'ouverture du portail Stripe par service via /api/services/portal.
web/messages/fr.json et web/messages/en.json :
ajout des clés i18n complètes pour le menu profil et toute la page clientSpace.
web/src/lib/ticket-notifications.ts :
mise à jour du lien vendeur vers /espace-client (anciennement /mes-commandes).
Impact visible / technique : navigation utilisateur simplifiée (une seule entrée), nouvelle page client plus lisible et transversale, cohérence multilingue FR/EN sur tous les labels de l'espace client.
2026-05-08 17:12 (UTC+2) — Correctif build TypeScript après refonte menu profil
Contexte/problème : next build échouait sur ProfileMenuSheet.tsx avec Property 'requireMesServices' does not exist on type ... après remplacement de "Mes commandes" par "Espace Client".
Changements appliqués :
web/src/components/layout/ProfileMenuSheet.tsx :
suppression de la prop hasMesServicesAccess devenue inutile ;
suppression du filtre item.requireMesServices obsolète.
web/src/components/layout/Header.tsx :
retrait de l'argument hasMesServicesAccess passé à ProfileMenuSheet ;
suppression du helper local hasMesServicesAccess désormais inutilisé.
Impact visible / technique : correction du blocage TypeScript, compilation rétablie sur la partie menu profil.
2026-05-08 17:24 (UTC+2) — Bot /script: option Avantages + traduction publique avec reset FR 2 min
Contexte/problème : besoin d’un nouveau script "Avantages" sans embed, avec le même menu de traduction que l’accueil, et retour automatique au français après 2 minutes. Le libellé de traduction devait aussi devenir plus discret (-#).
publication du message simple avec une vue publique de traduction.
bot/views/script_panel_view.py :
extension du mode "traduction publique" à welcome et advantages ;
ajout d’un reset automatique en français après 120 secondes (annule/remplace les timers précédents sur le même message) ;
ajout de ScriptPublicTranslateView (menu déroulant uniquement) pour les messages publics hors accueil ;
mise à jour du préfixe des traductions éphémères en format discret -# Traduction (...).
Impact visible / technique : les messages publics traduits reviennent automatiquement au français au bout de 2 minutes (accueil + avantages), nouvelle commande prête pour publication des avantages VIP, et rendu des en-têtes de traduction allégé pour les réponses privées.
2026-05-08 17:33 (UTC+2) — Confirmation de traduction multilingue (message staff)
Contexte/problème : le feedback après traduction publique restait en français fixe, ce qui était incohérent lorsque l’utilisateur venait de sélectionner une autre langue.
Changements appliqués :
bot/views/script_panel_view.py :
ajout d’un mapping de messages de succès par langue ;
remplacement du texte fixe par un message localisé selon la langue cible.
Impact visible / technique : la confirmation de traduction est maintenant renvoyée dans la langue choisie, avec la mention de retour automatique en français dans 2 minutes.
Contexte/problème : le fallback des notifications vidéo affichait un rendu brut (UC..., YOUTUBE) peu lisible, et l’édition d’un suivi existant ne permettait pas de modifier le message personnalisé.
Changements appliqués :
bot/handlers/videos_notifications_handler.py :
enrichissement du parsing RSS YouTube avec channel_name (nom de chaîne réel) ;
amélioration du message par défaut (format lisible : plateforme, chaîne, titre, lien) ;
ajout de variables template supplémentaires : {channel_name} et {platform_name}.
bot/commands/setup_server.py :
mise à jour du placeholder de template vidéo (exemple plus propre) ;
ajout du champ Message personnalisé (optionnel) dans le modal Modifier une Vidéo ;
sauvegarde du custom_message lors de l’édition d’un suivi existant.
Impact visible / technique : les notifications sont désormais lisibles même sans template perso, et chaque suivi vidéo peut être ajusté proprement après création sans devoir supprimer/recréer l’abonnement.
Contexte/problème : la traduction via clic droit Discord pouvait rendre un résultat trop littéral (“mot à mot”), avec une fluidité insuffisante.
Changements appliqués :
bot/handlers/translation_handler.py :
ajout d’un moteur prioritaire DeepL (si DEEPL_API_KEY est configurée) pour une traduction plus naturelle ;
fallback automatique vers LibreTranslate en cas d’indisponibilité/erreur ;
application de la même stratégie sur textes courts et textes découpés.
Impact visible / technique : les commandes de traduction existantes (dont clic droit “Traduire ce message”) conservent la même UX, mais produisent des sorties plus fluides dès qu’une clé DeepL est fournie ; sans clé, comportement précédent conservé via LibreTranslate.
Contexte/problème : certaines visites anonymes n’étaient pas enregistrées quand la route interne de tracking rejetait la requête (secret interne non injecté côté runtime/proxy).
Changements appliqués :
web/src/app/api/internal/ip-track/hit/route.ts :
conservation du mode sécurisé standard (secret requis quand configuré) ;
ajout d’un fallback contrôlé si IP_TRACK_INTERNAL_SECRET est absent : autorisation uniquement des appels marqués internes (x-ip-track-hit: 1) pour éviter la perte totale de tracking.
Impact visible / technique : le tracking des visites anonymes continue de fonctionner même en cas de défaut d’injection d’env, tout en gardant le contrôle strict par secret dès qu’il est présent.
Contexte/problème : l’application n’était plus reconnue comme installable (Chrome : pas d’invite « installer » / comportement type raccourci navigateur) ; sur le forum un clic sur une image ouvrait souvent un nouvel onglet ou téléchargeait plutôt qu’un aperçu confortable sur la page.
Changements appliqués :
PWA :
web/public/sw.js : ajout d’un listener fetch (proxy réseau minimal) — condition courante d’éligibilité à l’installation côté Chromium.
web/public/manifest.json : orientation passée à any (meilleur sur desktop) ; entrées d’icônes 512x512 explicites.
web/src/components/ServiceWorkerRegistration.tsx : bump APP_VERSION → 2.1.4 pour forcer prise en compte du nouveau SW après déploiement.
web/src/lib/markdown.ts : prise en charge  avec classe discord-inline-image.
web/src/components/forum/MarkdownRenderer.tsx : clic sur images inline → même modale.
web/src/app/forum/thread/[id]/page.tsx, web/src/app/forum/g/[slug]/page.tsx + variantes preview/* : pièces jointes image branchées sur le lightbox.
i18n : clés forum.imageLightbox* dans web/messages/*.json (locales du site).
Impact visible / technique : après déploiement + un rechargement (ou passage par la mécanique de version SW), Chrome/Edge peuvent proposer l’installation en mode app autonome ; les images de posts s’ouvrent en grand sur le site, avec téléchargement optionnel depuis la modale.
Contexte/problème : faire jouer correctement URLs YouTube / YouTube Music en vocal avec une base « propre » (file, erreurs utilisateur), sans passer par Lavalink pour rester mono-processus Python.
Changements appliqués :
bot/handlers/music_handler.py :
normalisation des URLs (+ YouTube Music) ; playlists uniquement youtube.com/playlist?list= avec cap MUSIC_MAX_PLAYLIST_TRACKS (défaut 25) ;
sémaphore d’extractions concurrentes (MUSIC_MAX_CONCURRENT_EXTRACTS) et timeouts configurables ;
sélection d’URL audio (pick_audio_stream_url) + boucle dans play_next qui ignore les entrées sans flux (plus de récursion infinie sur URL vide) ;
previous : ré-injection depuis l’historique des titres terminés + stop → play_next ;
fin de dernière piste en file : mise en historique du titre terminé puis arrêt ;
FFMPEG_PATH configurable.
bot/handlers/music_voice_cleanup.py (nouveau) + bot/main.py : cog chargé depuis handlers_to_load, déconnexion après MUSIC_EMPTY_CHANNEL_SEC (défaut 120 s) si plus aucun humain dans le salon, nettoyage si le bot quitte le vocal.
bot/views/music_view.py : messages multi-pistes après ajout (playlist), erreurs yt-dlp en langage utilisateur ; retour précis sur previous.
bot/requirements.txt : yt-dlp>=2025.05.22 (tests locaux contre format string obsolète).
Impact visible / technique : meilleure résilience extraction/lecture, playlists courte durée utilisables depuis le même modal, salon vocal libéré quand tous les utilisateurs sont partis après délai, dépendances yt-dlp à jour après pip install -r bot/requirements.txt.
non réalisé (volontaire) — PoC Lavalink / Wavelink
Contexte/problème : la voie B (serveur Lavalink séparé) n’a pas été implémentée dans cette passe ; réservée si la voie ffmpeg+yt-dlp reste insuffisante en prod.
Impact visible / technique : aucun nouveau service Java / conteneur — déployer comme avant après mise à jour des deps Python du bot.
Contexte/problème : lecture vocale Discord (FFmpegPCMAudio) impossible sans binaire ffmpeg sur le PATH ; besoin d’appliquer pip install -r bot/requirements.txt et de recharger le process bot-python.
2026-05-09 00:05 (UTC+2) — Bot musique : bot qui quitte le vocal malgré le message « lecture démarre »
Contexte/problème : l’embed indiquait une lecture démarrée alors que le bot se déconnectait du salon vocal (pas d’audio). Cause probable combinée à (1) VoiceChannel.members dérivée du cache guild.get_member : sur grosses guildes peut être vide alors que des humains ont bien un voice_state, déclenchant à tort MUSIC_EMPTY_CHANNEL_SEC / logique « salon vide » ; (2) branche voice_clients déjà connecté sans move_to si l’utilisateur n’est plus dans le même salon que le bot (bot seul → timer de vide réel ou chaîne FFmpeg sans public).
Changements appliqués :
bot/handlers/music_voice_cleanup.py : décompte humans_in_voice_channel basé sur channel.voice_states + bot filtrés ; synchro sync_empty_disconnect_task après connexion / déplacement du bot (plus seulement au return silencieux) ; prise en charge VocalGuildChannel (stages inclus).
bot/views/music_view.py : si le bot est déjà connecté à un autre salon que interaction.user.voice.channel, move_to avant play_next ; si l’utilisateur n’est plus en vocal au moment du modal, message file d’attente sans prétendre lancer tout de suite.
Impact visible / technique : le cog ne doit plus planifier de déconnexion « vide » alors que des humains sont encore en vocal mais hors cache membres ; l’écoute démarre dans le salon de l’utilisateur qui ajoute une piste.
2026-05-09 00:35 (UTC+2) — Musique : alignement UX / permissions sur le modèle joek13/py-music-bot
Contexte/problème : prise en compte du dépôt open source [joek13/py-music-bot](https://github.com/joek13/py-music-bot) (commandes in_voice_channel, is_audio_requester, admin) pour durcir les boutons du lecteur /music sans changer la stack technique (nous restons sur yt-dlp + handler interne, plus adapté que youtube-dl utilisé en amont par ce repo).
Changements appliqués : bot/views/music_view.py — fonction music_control_denial : pause / skip / previous / volume exigent d’être dans le même salon vocal que le bot lorsqu’il est connecté, puis demandeur de la piste en cours (champ user_id) ou administrateur Discord ; contrôle répété sur le modal volume.
Impact visible / technique : liste d’attente et bouton « Ajouter » inchangés pour tout le monde ; moins de « griefing » depuis le salon texte ou un autre vocal.
2026-05-09 01:05 (UTC+2) — Musique : poursuite corrections « quitte dès que la piste est trouvée »
Contexte/problème : après la première passe (voice_states + move_to), le bot continuait à quitter le vocal au moment où l’extrait yt-dlp / la lecture démarrait — symptôme typique soit d’un sync « salon vide » déclenché trop tôt ou sur des événements non liés au salon du bot, soit d’un délai MUSIC_EMPTY_CHANNEL_SEC très court / état gateway incohérent juste après connect/move.
Changements appliqués :
bot/handlers/music_handler.py : MUSIC_JOIN_GRACE_SEC (défaut 15) + dict voice_join_grace_until ; bump_voice_join_grace avant connect/move ou avant play_next quand le bot était déjà en vocal dans le même salon ; suppression de la fenêtre dans leave_voice.
bot/handlers/music_voice_cleanup.py : on_voice_state_update ne relance sync que si la mise à jour touche le salon où est le voice_client du bot (pas chaque mute ailleurs) ; fenêtre de grâce : pas de programme de déconnexion tant que voice_join_grace_until ; re-vérification différée après la grace si besoin ; tâche de délais respecte encore la grace avant cleanup_guild ; MUSIC_DISABLE_VOICE_EMPTY_CLEANUP pour diagnostic (coupe uniquement la logique salon vide sans toucher lecture) ; humans_in_music_channel sur channel.voice_states.
bot/views/music_view.py : bump_voice_join_grace systématique avant play_next sur la branche « bot déjà connecté » + conservation du bump après move_to (équivalent maintenant avant play).
Impact visible / technique : beaucoup moins de risque de cleanup_guild intempestif après arrivée vocale ou ajout de piste ; observabilité : logs warning sur déconnexion « salon vide » effective ; option env pour désactiver le cleanup sans désactiver /music.
Contexte/problème : besoin de traces PM2 lisibles pour comprendre pourquoi le bot quitte encore le vocal (timing cleanup, FFmpeg, déconnexion Discord, etc.).
Changements appliqués :
bot/handlers/music_handler.py : music_diag_verbose() via env MUSIC_DIAG_LOG (défaut activé ; couper avec MUSIC_DIAG_LOG=0) ; logs WARNING cleanup_guild / leave_voice avec reason ; play_next (état guild.voice_client vs handler), after_playing, join_voice / bump_voice_join_grace en mode verbeux.
bot/handlers/music_voice_cleanup.py : cleanup_guild(..., reason=...) ; logs BOT vsu join/leave ; sync_empty salon vu VIDE avec UIDs vocaux et grace ; empty_disconnect SCHEDULED ; timer qui tire avec humains + uids avant cleanup.
bot/views/music_view.py : log après create_track_list (modal) si diag actif ; import handlers.music_handler.music_diag_verbose (+ sys.path local au module).
Impact visible / technique : après pm2 restart bot-python : grep -E 'music_diag|\\[music\\] (cleanup_guild|leave_voice)' ~/.pm2/logs/*bot* (adapter chemin logs) pour voir si la sortie est cleanup_guild:empty_channel…, leave_voice seul ou BOT vsu LEFT_VOICE (cause externe voice Discord).
2026-05-09 01:50 (UTC+2) — Musique : correction racine identifiée par logs (BOT vsu LEFT + file vidée)
Contexte/problème : traces music_diag montraient une rafale BOT LEFT/JOIN pendant le handshake vocal puis join_voice connect_ok … is_connected=False et play_next … queue_pending=0 (file vide) — alors qu’une piste venait d’être ajoutée.
Cause : sur chaque on_voice_state_update du bot quittant un salon, le cog music_voice_cleanup appelait get_queue(...).clear_all(). Discord peut émettre des LEFT transitoires pendant channel.connect ; la file était vidée plusieurs fois avant play_next.
Changements appliqués :
bot/handlers/music_voice_cleanup.py : sur BOT LEFT — conservation du voice_clients.pop (+ annulation timers) mais plus de clear_all() ; nettoyage file + arrêt forcé réservés à cleanup_guild.
bot/handlers/music_handler.py : après channel.connect(), boucle d’attente jusqu’à is_connected (budget MUSIC_VOICE_CONNECT_READY_SEC, défaut 20) ; play_next petite attente si VC présent mais pas connecté (MUSIC_PLAY_READY_SEC, défaut 8).
Impact visible / technique : la file d’attente survit aux oscillations d’état vocal ; play_next voit encore les pistes après connexion réelle ou quasi-réelle.
2026-05-09 02:15 (UTC+2) — Musique : debounce BOT LEFT + pop voice_clients différé
Contexte/problème : malgré suppression de clear_all() sur BOT LEFT, voice_clients.pop() immédiat retirait encore la référence pendant la rafale gateway LEFT/JOIN / handshake (is_connected encore False alors qu’un VoiceClient existe), ce qui faisait échouer play_next ou laissait la guilde sans lien cohérent avec le handler.
Changements appliqués :
bot/handlers/music_voice_cleanup.py : MUSIC_BOT_LEAVE_DEBOUNCE_SEC (défaut 1) — tâche différée puis pop du handler uniquement si hors MUSIC_JOIN_GRACE_SEC et guild.voice_client is None ; annulation du debounce au BOT JOIN ; plus de pop synchrone au premier LEFT.
bot/handlers/music_handler.py : après attente is_connected sur connect(), ré-assignation voice_clients[guild_id] ; play_next : repli si clé absente mais guild.voice_client présent (resync).
Impact visible / technique : même scénario PM2 qu’avant (LEFT en rafales) ne doit plus désenregistrer le client vocal du handler au mauvais moment.
2026-05-08 (session) — Musique : groupe slash /pmusic (parcours type py-music-bot, isolé)
Contexte/problème : poursuite du diagnostic « le bot quitte le vocal » avec demande utilisateur — tester une voie quasi identique au petit bot GitHub (slash + yt-dlp + FFmpeg) sans partager music_handler.voice_clients avec le panneau /music.
Changements appliqués : bot/commands/simple_music.py — cog SimpleSlashMusic avec app_commands.Group pmusic (sous-commandes leave, skip, queue, now, et play) plus commande /play de premier niveau (même _play_impl que /pmusic play) ; état _states séparée du handler ; même extraction (build_ydl_opts, normalize_youtube_url, _build_track_payload) ; refresh_stream_url via bot.music_handler si disponible ; ne pas mixer panneau /music et ce lecteur sur la même session vocal abusivement.
Impact visible / technique : commandes à resynchroniser côté Discord (redémarrage bot + jusqu’à 1 h pour propagation globale, ou tree.sync(guild=...) selon votre config existante).
Contexte/problème : intégration explicite du code source du dépôt [joek13/py-music-bot](https://github.com/joek13/py-music-bot) (commandes préfixe type !play) comme demandé, sans remplacer le bot principal.
Changements appliqués :
Répertoire bot/vendor/joek13_py_music_bot/ : copie adaptée du paquet upstream (LICENSE MIT) ; extraction yt_dlp à la place de youtube-dl ; display_avatar ; cog CommandErrorHandler non chargé (évite les réponses globales aux commandes inexistantes).
Renommage des cogs en Joek13Music, Joek13Meta, Joek13Tips pour ne pas entrer en conflit avec commands/music.py (/music, classe Music).
bot/commands/joek13_music_integration.py : ajout du dossier vendor au sys.path, lecture TOML (bot/data/joek13_py_music_bot.toml auto-créé ou JOEK13_MUSIC_CONFIG), JOEK13_MUSIC_DISABLED pour désactiver.
bot/requirements.txt : toml>=0.10.2 pour le fichier de config.
Impact visible / technique : avec préfixe ! du bot principal — !play, !leave, !skip, !pause, !volume, !queue, !uptime, !tip, etc. Ne pas mélanger /music + !play joek13 sur la même session vocal sans risque de double lecture / deux gestionnaires audio.
2026-05-09 — Musique : pas d’audio en salon vocal (micro / flux)
Contexte/problème : le bot annonce une lecture mais aucun son en vocal ; confusion possible avec un « micro » — côté Discord le bot envoie un flux audio encodé (FFmpeg → PCM → Opus), pas un micro utilisateur.
Causes identifiées / corrections :
join_voice : si un autre cog avait déjà connecté le bot (guild.voice_client présent sans entrée dans music_handler.voice_clients), VoiceChannel.connect() levait ClientException: Already connected → pas de play_next ou état incohérent. Désormais réutilisation / move_to sur le VoiceClient existant + filet sur la même exception après connect().
Modal « Ajouter » : should_play_now était basé sur queue.is_playing (état logique pouvant rester « lecture » alors que FFmpeg est mort). Désormais actually_playing = voice_client.is_playing() ou is_paused() quand le client est connecté → relance réelle de la lecture quand il n’y a plus de flux audio.
FFmpeg : en-tête User-Agent + -protocol_whitelist sur les options « before » pour les URLs googlevideo / HLS souvent bloquées sans navigateur ; MUSIC_FFMPEG_USER_AGENT et MUSIC_FFMPEG_EXTRA_BEFORE en surcharge.
Impact visible / technique : après pm2 restart bot-python, tester /music puis ajout de piste en étant en vocal ; si besoin grep -E 'music\\]|music_diag|after_playing|lecture erreur' sur les logs erreur PM2 pour voir une erreur FFmpeg résiduelle.
bot/requirements.txt : suppression dépendance toml (utilisée uniquement par joek13).
bot/commands/help_command.py : retrait de music dans EXCLUDED_COMMANDS (ancienne commande slash supprimée).
Impact visible / technique : plus de panneau boutons /music — usage !play <url ou recherche> ou /musique play ; pm2 restart bot-python + resync slash pour /musique.
Contexte/problème : après refonte youtube_music, retour utilisateur « ne marche pas mieux » — causes probables multiples (pas d’opus natif sous PM2, VoiceClient résiduel is_connected=False bloquant un nouveau connect(), recherche ytsearch: trop large).
Changements appliqués :
bot/main.py : tentative discord.opus.load_opus (variable OPUS_LIBRARY ou ctypes.util.find_library("opus")) avant bot.run() ; warning si aucune lib opus.
bot/commands/youtube_music.py : disconnect(force=True) sur client vocal hors ligne avant connect() ; connect(..., self_mute=False, self_deaf=False, timeout=MUSIC_VOICE_CONNECT_TIMEOUT) ; ytsearch1: pour les requêtes texte ; volume par défaut 100% ; MUSIC_FFMPEG_PROTOCOL_WHITELIST (désactiver avec 0 pour retirer -protocol_whitelist) ; exclusion formats http_dash_segments dans pick_stream_url ; tâche _verify_play_started (log si pas de is_playing après 1,2s) ; timer salon vide : ne pas couper si is_playing/is_paused ou file non vide ; MUSIC_DEBUG=1 pour log options FFmpeg.
Impact visible / technique : redémarrage bot ; si toujours muet : vérifier logs [music] Pas de flux audio et paquet libopus0 sur l’hôte ; essayer MUSIC_FFMPEG_PROTOCOL_WHITELIST=0 si ffmpeg refuse le flux.