Changelogs
Historique des évolutions LB'XMB.FR (site, bot Discord, forum, boutique, intégrations). Cette page est synchronisée avec le document interne de récapitulatif.
### Contexte / problème
Mettre en place un concours public : classement des ajouts de ressources, 10 € au premier, reset au 5 septembre puis chaque lundi, mention du gagnant, bouton pour voir qui a ajouté quoi.
### Changements appliqués
### Impact visible / technique
### Fichiers touchés
bot-site/src/lib/siteDb.js, bot-site/src/lib/resourceContestStats.js, bot-site/src/index.js, bot-public/src/lib/resourceContest.js, bot-public/src/events/client/ready.js, bot-public/src/events/interaction/interactionCreate.js, bot-public/src/lib/internalHttpServer.js, bot-public/src/handlers/verification/webVerificationQueue.js
- Container Discord live dans le salon 1539198613014716446 (classement + fin de période Discord timestamps).
- Compteur alimenté par resources.date_creation (MariaDB) : requête partagée bot-site/src/lib/resourceContestStats.js (HTTP GET /resource-contest/stats + import direct depuis bot-public).
- Première manche : 18 août 00:00 → 5 septembre 2026 23:59 (Europe/Paris), puis cycles hebdomadaires le lundi 00:00.
- À la clôture : message qui mentionne le gagnant (10 €) et reset du classement à 0.
- Bouton « Voir qui ajoute quoi » (éphémère, filtre par participant, pagination) + lien « Ajouter une ressource ».
- Rafraîchissement auto ~20 s et à chaque site_new_resource.
- Panneau unique édité en place (pas de spam). Mentions du classement sans ping ; ping uniquement à l’annonce du gagnant.
- Intervalle configurable : RESOURCE_CONTEST_INTERVAL_WEEKS (1 = chaque lundi, 2 = un lundi sur deux), RESOURCE_CONTEST_PRIZE_EUR, RESOURCE_CONTEST_CHANNEL_ID.
### 1. Profil : bannière et avatar
#### Contexte / problème
Header sticky z-10 recouvrait l’avatar (-mt-10 dans le flux scrollable).
#### Changements
Sticky = poignée seulement. Bannière + avatar dans le même flux, avatar -mt-10 relative z-[1].
#### Fichiers
ForumGroupServerProfileCard.tsx
### 2. Accueil mobile + vocaux mis en avant
#### Contexte / problème
Pas de retour mobile. Édition Accueil peu claire pour vocal/conférence.
#### Changements
#### Fichiers
ForumGroupHomeView.tsx, forum-group-home.ts, home/route.ts, ForumGroupCommunityPanel.tsx
### 3. Pages : défaut, ACL, salons, rôles, settings mobile
#### Contexte / problème
0 pages, create channel ignore la page, roleIds ne filtraient rien. Settings pages illisible sur mobile.
#### Changements
#### Fichiers
### 1. Profil serveur
#### Contexte / problème
Boutons petits en wrap ; tout le sheet scrollait d’un bloc.
#### Changements
Bannière sticky. Body scroll. Actions empilées pleine largeur (Signaler, Avis, Quitter). Formulaire d’avis sous le bouton.
#### Fichiers
ForumGroupServerProfileCard.tsx
### 2. Liste membres
#### Contexte / problème
3e carte détachée, breakpoints md/lg incohérents.
#### Changements
Membres collés au chat (border-l, md:flex). Drawer md:hidden overlay uniquement la carte chat.
#### Fichiers
ForumGroupCommunityPanel.tsx
### 3. Topic → salon en bas
#### Contexte / problème
PATCH recalé sort_order dès que categoryId était envoyé.
### Contexte / problème
Le profil s’ouvrait en dialog centré. Seul le nom du serveur était cliquable, pas l’icône.
### Changements
### Impact visible / technique
Geste proche de Discord mobile. Overlay + slide 300 ms.
### Fichiers
ForumGroupServerProfileCard.tsx, ForumGroupCommunityPanel.tsx
#### Déploiement
BUILD_ID H5lBbFKcTrDxcGeUhyHi_. web-prod redémarré (5 workers online).
- Panneau en Sheet side="bottom" : glisse du bas vers le haut (coins arrondis, poignée, overlay).
- Clic sur l’icône du serveur (header liste salons) ouvre aussi le profil.
### 1. Profil serveur (clic nom)
#### Contexte / problème
Clic sur le nom du serveur n’ouvrait qu’un petit dialog. Pas d’avis, d’emojis, ni de compteurs.
#### Changements
#### Impact visible / technique
Panneau lecture style Discord. createdAt / creatorPseudo exposés dans GroupInfo.
#### Fichiers
ForumGroupServerProfileCard.tsx, ForumGroupCommunityPanel.tsx, forum-group-reviews.ts, reviews/route.ts, forum-group-selfhost.ts
### 2. Long-press salon = menu Modifier
#### Contexte / problème
Sur tactile, draggable lançait le drag HTML5 au lieu du menu contextuel.
#### Changements
#### Fichiers
ForumGroupCommunityPanel.tsx
### 3. Icônes forum / membres
#### Contexte / problème
### 1. Liste membres
#### Contexte / problème
Header « Fondateur — 1 » puis le nom de rôle répété sur chaque ligne. Groupe « Default » pour les rôles non hoistés.
#### Changements
#### Fichiers
ForumRoleLabel.tsx, ForumGroupCommunityPanel.tsx
### 2. Noms de salons
#### Contexte / problème
TranslatedText traduisait les noms de salons.
#### Changements
Affichage brut {ch.name} (texte + vocal).
#### Fichiers
ForumGroupCommunityPanel.tsx
### 3. Carte utilisateur rail
#### Contexte / problème
Barre user sous les salons, pas dans la liste des serveurs. Mute/casque seulement en vocal.
#### Changements
### 1. Highlight mention / réponse
#### Contexte / problème
Pas de fond Discord-like quand on est mentionné. Les réponses n’étaient pas persistées.
#### Changements
#### Impact visible / technique
Les messages qui te concernent se voient tout de suite.
#### Fichiers
forum-voice.ts, messages/route.ts, ForumGroupCommunityPanel.tsx, MessageLinkEmbeds.tsx, MessageReplyPreview.tsx
### 2. Micro / envoi
#### Contexte / problème
Send lucide toujours visible ; pas de notes vocales dans les salons.
#### Changements
#### Impact visible / technique
Même geste que les MP : micro si rien n’est tapé, envoi vocal au relâchement.
#### Fichiers
MessageComposerBar.tsx, ForumGroupCommunityPanel.tsx, VoiceMessagePlayer.tsx
### 1. Réactions (annonces / actualités)
#### Contexte / problème
Bouton SmilePlus invisible hors hover (opacity-0) : rien au tactile. Cœur Discord -# 👁️ · ❤️ n en texte inerte. Compteur JSON ne montait pas tout de suite.
#### Changements
#### Impact visible / technique
Réagir depuis le mobile ; le compteur +1 tout de suite ; le chip apparaît.
#### Fichiers
MessageReactions.tsx, ForumGroupCommunityPanel.tsx, messages/[messageId]/reactions/route.ts
### 2. Tag du serveur — onglet + styles
#### Contexte / problème
Tag mélangé au profil serveur, style argent unique.
#### Changements
#### Impact visible / technique
Onglet dédié ; chips colorés selon le métal/gemme choisi.
#### Fichiers
forum-server-tag-style.ts, forum-server-tag.ts, forum-group-permissions.ts, ServerTagBadge.tsx, ForumGroupSettingsModal.tsx, groups/[slug]/route.ts, summary/route.ts, invite-domains/[host]/route.ts, me/primary-tag/route.ts
### Contexte / problème
Lot D (hors overwrite tests déjà corrects) : lobby vocal social listait tous les group_id IS NULL ; snowflake Discord dev-test obsolète ; DELETE messages ignorait les overwrites MANAGE_MESSAGES ; settings salons (cats puis salons), overflow mobile, menu membres clipé, rôles mobile, logs EN, bans sans durée/notify.
### Changements
### Impact visible / technique
Lobby vocal public = 2 salons officiels. Overwrite salon peut refuser la suppression de messages. Settings scrollables sur mobile. Bans temporaires + notification optionnelle.
### Fichiers
forum-voice-social-migrate.ts, api/forum/voice/channels/route.ts, ForumVoiceLobby.tsx, community-bridge-map.ts, community-bridge.ts, forum-official-server.ts, forum-group-permissions.ts, forum-group-permissions.resolve.test.ts, forum-group-audit-labels.ts, messages/[messageId]/route.ts, channels/[channelId]/route.ts, roles/[roleId]/route.ts, bans/route.ts, join/route.ts, invites/[code]/route.ts, ticket-notifications.ts, dropdown-menu.tsx, ForumGroupSettingsModal.tsx, ForumGroupRolesPanel.tsx, ForumGroupCommunityPanel.tsx, docs/RECAP-…
### 1. ACL voir / utiliser à l’ajout
#### Contexte / problème
Un seul multi-select roleIds (canView+canUse). Il fallait séparer « voir » et « utiliser » dès le POST.
#### Changements
#### Impact visible / technique
À la création, on peut restreindre la visibilité sans autoriser l’usage, et inversement.
#### Fichiers
ForumGroupExpressionsPanel.tsx, expressions/route.ts
### 2. Limites 4 Mio + trim audio 10 s
#### Contexte / problème
Plafonds trop bas (512 Ko / 1 Mio / 2 Mio) ; pas de coupe à 10 s pour le soundboard.
#### Changements
#### Impact visible / technique
Images plus lourdes acceptées. Un son trop long doit être découpé avant envoi.
### Contexte / problème
bun run build cassait sur globals.css (Missed semicolon) : Oxide scannait .next / copies ptb et générait des utilitaires binaires corrompus.
### Changements
@source not pour .next, .next-staging, ptb-web, web-old-*, forum-expression-tokens.ts.
### Impact visible / technique
Le build PostCSS Tailwind reprend.
### Fichiers
web/src/app/globals.css
### Contexte / problème
Lot A incomplet : CTA invite vanity sans isMember, couleurs de rôle absentes du vocal, ajout de membres au rôle encore en checkboxes inline.
### Changements
### Impact visible / technique
Pseudos du vocal et du chat suivent la couleur du rôle hoisté. Invitation déjà membre → « Voir le serveur » aussi sur domaine vanity.
### Fichiers
invite-domains/[host]/route.ts, ForumGroupRolesPanel.tsx, ForumVoiceSession.tsx, ForumVoiceCallStage.tsx, ForumVoiceSidebar.tsx, ForumRoleLabel.tsx, members/route.ts, ForumGroupCommunityPanel.tsx, use-forum-social.ts, display-roles/route.ts
- GET invite-domains/[host] : isMember si session (bouton Voir le serveur vs Rejoindre).
- Modal « Ajouter des membres » (recherche + multi-select) dans ForumGroupRolesPanel.
- Vocal : displayRole hoisté via /api/forum/voice/channels/[id]/display-roles ; pseudos colorés (tuiles + rail).
### Contexte / problème
Onglet « Analyse du serveur » était un stub. Pas de lectures, vocal, ni départs historisés.
### Changements
### Impact visible / technique
Graphe d’activité dans Paramètres → Analyse. Lectures / vocal / départs à 0 sur l’historique passé, puis remplis à partir de maintenant.
### Fichiers
forum-group-analytics.ts, analytics/route.ts, channels/[channelId]/read/route.ts, ForumGroupAnalyticsPanel.tsx, ForumGroupSettingsModal.tsx, ForumGroupCommunityPanel.tsx, token/route.ts, voice/.../leave/route.ts, leave/route.ts, members/[userId]/route.ts
- Schéma : forum_group_channel_reads, forum_group_analytics_read_days, forum_group_voice_sessions.
- GET /api/forum-social/groups/[slug]/analytics?days=7|14|30 : messages (backfill), lectures (ping), minutes vocal, joins (joined_at), leaves (member.leave / kick / ban).
- Ping POST .../channels/[channelId]/read à l’ouverture d’un salon texte.
### Contexte / problème
bot-public pollait /repos/.../events sur les repos starringés (issues, PR, stars, wiki…). Bruit Discord + rate limit GitHub. updated_at changeait sans push.
### Changements
### Impact visible / technique
Salon starred : uniquement releases et vrais updates de repo. Quota GitHub : 1 liste starred / 15 min + 1 release uniquement si le repo a été poussé.
### Fichiers
bot-public/src/lib/notifications/githubStarred.js
- Plus d’appel /events. Éligibilité : pushed_at, archive, rename (plus updated_at).
- Si push : GET /releases/latest → embed release si nouvel id, sinon un embed « Mise à jour » vers les commits.
- Premier passage : mémorise last_release_id sans poster.
- ghFetch : 429 loggé (Retry-After, remaining), tick arrêté.
### Contexte / problème
Les correctifs saisie + onglet Rentry n’étaient pas encore en prod.
### Changements
### Impact visible / technique
Hard refresh sur /guides/ajouter : saisie stable + onglet « Importer un Rentry ».
### Fichiers
déploiement web-prod
- bun run deploy:web-prod : build OK, pm2 restart web-prod.
### Contexte / problème
1. En prod, chaque lettre dans l’éditeur de guides était effacée : TipTap v3 réapplique content / editorProps / Placeholder à chaque rendu parent (onUpdate → setState).
2. Demande d’un import Rentry à côté de l’onglet README GitHub.
### Changements
### Impact visible / technique
On peut taper dans /guides/ajouter sans perte. Nouvel onglet Rentry à côté de GitHub.
### Fichiers
GuideEditorWord.tsx, GuideRentryImportPanel.tsx, lib/rentry.ts, lib/guide-content-format.ts, api/guides/rentry-preview/route.ts, api/guides/create/route.ts, api/guides/[folder]/route.ts, guides/ajouter/page.tsx, guides/[folder]/page.tsx, GuidePayloadPreview.tsx, messages/{fr,en}.json
- Éditeur isolé (GuideChapterTiptap) : contenu initial figé, extensions/editorProps stables, key = id d’étape (plus de setContent à la frappe).
- Onglet Importer un Rentry : fetch URL (/api/raw puis scrape HTML → markdown), collage markdown en secours.
### Contexte / problème
Les correctifs saisie/UI étaient locaux ; le deploy précédent avait échoué (@tanstack/react-table v9). La prod restait sur l’ancienne page.
### Changements
### Impact visible / technique
/guides/ajouter en prod : espaces dans les titres d’étapes, saisie TipTap stable, barre d’insertion (Image, Question, Spoiler, Encadrés, Code, Tableau). Hard refresh recommandé.
### Fichiers
déploiement web-prod (pas de nouveau diff code)
- bun run deploy:web-prod : build OK, swap .next-staging, pm2 restart web-prod.
- BUILD_ID : UpSEnMVS2msfrWmvTZY_h.
### Contexte / problème
En prod : pas d’espace dans les titres d’étapes (Espace = clic a11y), l’éditeur TipTap se reset à chaque frappe (useEffect sync trop large), nouvelle UI pas déployée (build précédent cassé).
### Changements
### Impact visible / technique
On peut écrire dans le guide et mettre des espaces dans les titres. La barre d’insertion se voit sans taper /.
### Fichiers
GuideEditorWord.tsx, docs/RECAP-…
- stopPropagation sur l’input titre ; Espace/Entrée sur la ligne seulement hors champ.
- Sync TipTap uniquement au changement d’étape.
- Barre d’insertion toujours visible (Image, Question, Spoiler, Encadrés, Code, Tableau).
- Deploy web-prod.
### Contexte / problème
L’éditeur Word était trop lourd (grosse barre d’outils) et ne permettait pas les besoins tutoriels : image spoiler, questions à choix (ex. version Luma 3DS), formatage type Discord/GitHub.
### Changements
### Impact visible / technique
Création plus légère. Un guide peut adapter le contenu selon la réponse (11.00 / 7.00 / 14.50). Format JSON word inchangé (nœuds dans le doc TipTap). Import GitHub toujours en 2e onglet.
### Fichiers
guide-editor-nodes.ts, guide-editor-extensions.ts, guide-editor-extensions-react.ts, GuideEditorWord.tsx, GuideChapterView.tsx, nodes/Guide{Image,Callout,Choice}NodeView.tsx, GuideTranslationContext.tsx, guides/[folder]/page.tsx, guides/ajouter/page.tsx, globals.css, messages/{fr,en}.json
- Barre Word remplacée par menu / + menu flottant (gras, lien, titres, spoiler).
- Nœuds TipTap : image spoiler + légende, spoiler texte ||…||, encadré info/warning/danger, question/choix imbriqués.
### Contexte / problème
deploy:web-prod échouait : Turbopack 14 erreurs sur data-table.tsx (getCoreRowModel, useReactTable, etc. absents). Le code dashboard est API v8 ; le paquet était en ^9.1.2.
### Changements
Pin @tanstack/react-table à ^8.21.3. bun install ; bun audit toujours 0 vuln.
### Impact visible / technique
Les exports v8 (useReactTable, getCoreRowModel, …) sont de nouveau présents. Le build dashboard tasks peut passer.
### Fichiers
web/package.json, web/bun.lock
### Contexte / problème
bun audit dans web/ signalait 10 vulnérabilités (3 high, 6 moderate, 1 low). Les overrides/resolutions épinglaient encore des versions vulnérables (socket.io-parser 4.2.6, ip-address 10.1.x/10.2.0, nested hono 4.12.33, dompurify 3.4.12, nested nanoid 3.3.16).
### Changements
Overrides/resolutions montés : dompurify ^3.4.13, ip-address ^10.3.1, nanoid ^3.3.18, socket.io-parser ^4.2.7, hono ^4.13.2. Puis bun install (lockfile uniquement via ces pins). Pas de bun update --latest.
### Impact visible / technique
bun audit : No vulnerabilities found.
### Fichiers
web/package.json, web/bun.lock
### Contexte / problème
La page /guides/ajouter ne proposait plus que l’import d’un README GitHub. Impossible d’écrire un guide soi-même (éditeur Word, chapitres / pages). L’édition ?edit= était ignorée. L’API create acceptait encore le format word.
### Changements
### Impact visible / technique
On peut à nouveau rédiger un guide avec plusieurs chapitres sans coller une URL GitHub. L’import README reste optionnel. L’édition des deux formats fonctionne.
### Fichiers
web/src/app/guides/ajouter/page.tsx, web/src/components/guides/GuideGithubImportPanel.tsx, web/src/app/api/guides/[folder]/route.ts, web/messages/{fr,en}.json
- Page /guides/ajouter : deux modes — Écrire un guide (défaut, TipTap multi-chapitres) et Importer un README GitHub.
- Restauration ?edit= : précharge Word ou README selon le format.
- Panneau import extrait dans GuideGithubImportPanel.
- PUT /api/guides/[folder] accepte github_readme ; GET renvoie github_url / owner / repo / branch / html_url.
### Contexte
Vague D du plan App UX batch 18 : abonnement Éclipse (gate création serveur hébergé), DnD/dossiers rail mobile, UserProfilePopover avatar-only, traduction limitée aux runs [A-Za-z]+.
### Changements
#### 15. Éclipse subscription
#### 16. Mobile rail DnD + folders
#### 17. Profile only on avatar click
#### 18. Translate A-Za-z only
### Impact visible / technique
Éclipse achetable (Stripe) débloque la création de serveurs hébergés ; Premium legacy toujours OK. Rail mobile réordonnable par long-press. Profils moins « accidentels ». Traduction ignore accents/chiffres/symboles hors A–Z. Pas de déploiement.
### Fichiers
vip-group-create.ts, vip-tier.ts, api/vip/checkout, api/vip/activate, api/vip/status, api/forum-social/groups/route.ts, SiteAppDonatePanel.tsx, SiteAppServerRail.tsx, SiteAppServerRailList.tsx, SiteAppShell.tsx, vip-page-data.ts, VipPageSections.tsx, vip/page.tsx, messages/{fr,en}.json, role-styles.ts, dashboard/users/[id]/edit/page.tsx, ForumGroupCommunityPanel.tsx, libretranslate.ts, TranslatedText.tsx, docs/RECAP-…
### Contexte
Vague C du plan App UX batch 18 : embeds guides/ressources/boutique sur salons, vue forum style Discord, paramètres utilisateur regroupés, badges Fondateur/Admin/Modérateur uniquement.
### Changements
#### 11. content_embed guides/ressources/boutique
#### 12. Forum channel Discord-like
#### 13. User settings regroupés
#### 14. Grade badges profils
### Impact visible / technique
Salons peuvent embarquer catalogues site ; forum posts en liste pleine largeur avec tags/thumb ; settings user style Discord ; grades site limités sur popovers. Pas de déploiement.
### Fichiers
forum-voice.ts, forum-official-server.ts, forum-channel-content-embed.ts, site-profile-grades.ts, api/.../channels/route.ts, api/.../channels/[channelId]/route.ts, api/.../tags/route.ts, api/.../tags/[tagId]/route.ts, api/.../forum-posts/route.ts, api/.../forum-posts/[postId]/route.ts, ForumGroupContentEmbedPanel.tsx, ForumGroupForumChannelView.tsx, ForumGroupChannelEditModal.tsx, ForumGroupCommunityPanel.tsx, SiteAppSettingsModal.tsx, UserProfilePopover.tsx, SiteAppProfileModal.tsx, docs/RECAP-…
### Contexte
Livraison complète du plan App UX batch 18 après vagues A–D.
### Changements
### Impact
Les 18 points (modals, vanity invite, membres, forum Discord-like, settings user, Éclipse, rail mobile, translate A-Za-z) sont en production.
### Fichiers
Déploiement uniquement (deploy.sh).
- Build + swap .next-staging + restart web-prod (BUILD_ID: vnOsDKKzbXNgVygiZi5tI).
### Contexte
Vague B du plan App UX batch 18 : domaine vanity = URL racine, panneau membres enrichi, fix ajout stickers/expressions, retrait de l’onglet Social (?tab=forum).
### Changements
#### 6. Domaine vanity = racine
#### 7. Panneau membres enrichi
#### 8. Fix ajout sticker / expressions
#### 10. Retrait onglet Social
### Impact visible / technique
Lien vanity = landing join sur la racine du domaine custom ; codes éphémères restent sur lbxmb.fr. Membres settings plus informatifs avec actions. Stickers : erreurs visibles. Onglet Social disparu de la nav app. Pas de déploiement.
### Fichiers
forum-group-permissions.ts, proxy.ts, api/.../invite-domains/[host]/route.ts, invite/host/[host]/page.tsx, MessageLinkEmbeds.tsx, ForumGroupSettingsModal.tsx, ForumGroupRolesPanel.tsx, api/.../members/route.ts, use-forum-social.ts, UserProfilePopover.tsx, ForumGroupCommunityPanel.tsx, ForumGroupExpressionsPanel.tsx, site-app-styles.ts, SiteAppSidebar.tsx, SiteAppShell.tsx, docs/RECAP-…
### Contexte
Vague A du plan App UX batch 18 : permissions salon (overlay + picker rôles), paramètres serveur fullscreen mobile, ColorPicker + icône dossier rail, bannière 960×540, DnD salons/catégories, échelle UI / taille messages.
### Changements
### Impact visible / technique
Modales serveur/salon couvrent correctement le viewport (rail inclus) ; dossiers rail personnalisables (couleur + image) ; réordonnancement salons par drag ; densité UI / taille messages réglables côté client. Pas de déploiement.
### Fichiers
ForumGroupChannelEditModal.tsx, ForumGroupSettingsModal.tsx, ColorPicker.tsx, SiteAppServerRailList.tsx, forum-server-rail-layout.ts, api/.../me/server-rail/folder-icon/route.ts, site-app-theme.ts, SiteAppSettingsModal.tsx, SiteAppShell.tsx, ChatMessageRow.tsx, SiteAppDmChat.tsx, ForumGroupCommunityPanel.tsx, docs/RECAP-…
- ForumGroupChannelEditModal : overlay fixed inset-0 z-[100], zone permissions min-w-0 + labels tronqués + scroll fiable + footer sticky ; select natif remplacé par picker rôles searchable (liste + chips).
### Contexte
Le build prod échouait : SiteAppServerRailList (client) importait forum-official-server / forum-server-rail qui tiraient fs via la chaîne serveur.
### Changements
### Impact
/app rebuildable ; rail dossiers + tags en prod.
### Fichiers
forum-official-constants.ts, forum-official-server.ts, forum-server-rail-layout.ts, forum-server-rail.ts, SiteAppServerRailList.tsx, docs/RECAP-…
- forum-official-constants.ts (slug/nom safe client) ; forum-official-server réexporte.
- forum-server-rail-layout.ts (parse/merge purs) ; forum-server-rail.ts garde load/save DB.
- Client rail importe constants + layout uniquement.
- Déploiement web-prod OK (BUILD_ID: OgTdO0w0MpU8l92NLzT1v).
### Contexte
Vague 3 du plan communauté : tags serveur courts avec icône, préférence utilisateur, badge Discord-like ; réordonnancement rail serveurs via layout JSON + dossiers.
### Changements
### Impact visible / technique
Les membres peuvent afficher un tag serveur (≤5) à côté du pseudo ; le rail /app se réordonne et groupe en dossiers. Pas de déploiement.
### Fichiers
forum-group-permissions.ts, forum-voice.ts, forum-server-tag.ts, forum-server-rail.ts, api/.../groups/[slug]/route.ts, api/.../summary/route.ts, api/.../messages/route.ts, api/.../me/primary-tag/route.ts, api/.../me/server-rail/route.ts, api/users/[id]/route.ts, ServerTagBadge.tsx, ForumGroupSettingsModal.tsx, SiteAppSettingsModal.tsx, SiteAppServerRail.tsx, SiteAppServerRailList.tsx, ForumGroupCommunityPanel.tsx, UserProfilePopover.tsx, SiteAppProfileModal.tsx, docs/RECAP-…
- Schéma : forum_groups.tag → VARCHAR(5) ; tag_icon_url VARCHAR(512) ; users.primary_server_tag_group_id ; table forum_user_server_rail (user_id PK, layout_json JSON).
### Contexte
Vague 2 du plan communauté : domaine d'invitation personnalisé au niveau serveur, bannière profil dans SiteApp, factorisation du menu contextuel serveur (rail + en-tête communauté).
### Changements
#### Domaine d'invitation custom
#### Bannière profil SiteApp
#### Menu contextuel serveur partagé
### Impact visible / technique
Liens d'invitation peuvent utiliser un hostname custom ; embeds invite fonctionnent hors lbxmb.fr ; bannière éditable dans /app ; menu serveur cohérent rail ↔ panneau. npx tsc --noEmit OK. Pas de deploy.
### Fichiers
forum-group-permissions.ts, api/.../groups/[slug]/route.ts, api/.../groups/[slug]/invites/route.ts, api/.../invites/[code]/route.ts, MessageLinkEmbeds.tsx, ForumGroupSettingsModal.tsx, ForumGroupCommunityPanel.tsx, ServerContextMenu.tsx, SiteAppServerRail.tsx, SiteAppSettingsModal.tsx, UserProfilePopover.tsx, SiteAppProfileModal.tsx, docs/RECAP-…
### Contexte
Vague 1 du plan communauté : UX expressions (onglets masqués depuis settings, modal d'ajout + rôles), picker emoji dans le composer salon, réordonnancement drag-and-drop des catégories, durcissement anti-fuite jetons traduction [[E0]] / [E0].
### Changements
### Impact visible / technique
Paramètres Expression sans double barre d'onglets ; ajout d'emoji/sticker/son avec restriction de rôles ; insertion d'emojis classiques/serveur dans le chat ; catégories réordonnables ; noms emoji ne montrent plus [[E0]].
### Fichiers
ForumGroupExpressionsPanel.tsx, ForumGroupSettingsModal.tsx, api/.../expressions/route.ts, MessageComposerBar.tsx, ForumGroupCommunityPanel.tsx, libretranslate.ts, TranslatedText.tsx, docs/RECAP-…
- ForumGroupExpressionsPanel : prop hideTabs ; sync type depuis initialType ; formulaire inline remplacé par bouton + modal (nom, fichier, multi-select rôles « canUse ») ; POST FormData + roleIds JSON ; PATCH ACL client si rôles sélectionnés.
### Contexte
Vague C du plan rôles/invites : contenu site (guide Nextendo, catégories ressources), fix traduction emoji sur noms de salons, déplacement salon uniquement via drag-and-drop.
### Changements
### Impact visible / technique
Noms de salons emoji ne fuient plus LBXMB_EMOJI_0 ; filtres ressources Outils/Emulateurs disponibles ; guide stub visible sous Switch. Traduction FR complète Nextendo dès collage du contenu source.
### Fichiers
web/src/lib/libretranslate.ts, web/src/components/i18n/TranslatedText.tsx, web/src/lib/consoles-config.ts, web/src/components/forum/ForumGroupCommunityPanel.tsx, web/content/guides/nextendo-setup.fr.md, guides (DB id 14), docs/RECAP-…
- Guide Nextendo : entrée DB guides id=14 (Nintendo Switch / Pack Jailbreak, format github_readme) + fichier web/content/guides/nextendo-setup.fr.md — avertissement non-officiel conservé ; contenu détaillé en attente du markdown source (rentry /raw protégé).
### Contexte
Vague B du plan rôles/invites : navigation paramètres serveur regroupée (style Discord), invitations forum avec expiration / max uses / rôles à l'arrivée, page de redeem publique, embeds de liens d'invitation dans le chat.
### Changements
### Impact visible / technique
Création d'invitations complètes, join des serveurs privés via code, embeds cliquables dans les messages. /invite/[code] (chat) inchangé. bunx tsc --noEmit OK.
### Fichiers
forum-group-permissions.ts, api/.../groups/[slug]/invites/route.ts, api/.../invites/[code]/route.ts, api/.../groups/[slug]/summary/route.ts, app/invite/g/[code]/page.tsx, app/invite/g/[code]/layout.tsx, ForumGroupInviteCreateModal.tsx, ForumGroupSettingsModal.tsx, ForumGroupExpressionsPanel.tsx, SiteAppServerRail.tsx, MessageLinkEmbeds.tsx, docs/RECAP-…
- Schéma forum_group_invites : colonnes expires_at DATETIME NULL, grant_role_ids JSON NULL (via ensureColumn + CREATE) ; helpers forumGroupInvitePublicUrl, parseInviteGrantRoleIds, inviteIsExpired, inviteIsExhausted.
### Contexte
Compléments à la Vague A (fusion multi-agents) : les tests de hiérarchie n'étaient pas lançables (bun absent ; Node ne résout pas un import TS relatif sans extension), et l'ordre de priorité des rôles n'était pas documenté côté résolution.
### Changements
### Impact visible / technique
npm run test:permissions → 6/6 OK ; npx tsc --noEmit OK sur le projet web ; aucun lint sur les fichiers touchés. La hiérarchie est vérifiable en CI sans dépendance supplémentaire.
### Fichiers
web/src/lib/forum-group-permissions-hierarchy.ts, web/src/lib/forum-group-permissions.resolve.test.ts, web/scripts/test-forum-group-permissions.mjs, web/package.json, web/src/components/forum/ForumGroupChannelEditModal.tsx, docs/RECAP-…
- forum-group-permissions-hierarchy.ts : en-tête documentant l'ordre de priorité — la liste de ForumGroupRolesPanel trie position ASC avec @everyone verrouillé en bas, donc plus petite position = priorité la plus forte et @everyone toujours dernier ; règles 1→5 (owner/ADMINISTRATOR, overwrite membre, overwrites rôles haut→bas premier non neutre, overwrite @everyone, bits de serveur avec @everyone en repli).
### Contexte
Vague A du plan rôles/invites : icône image de rôle, retrait du mode couleur balance, interrupteurs bi/tri-état, hiérarchie de permissions (plus d’OR naïf), menu contextuel liste des rôles.
### Changements
### Impact visible / technique
Icônes de rôle persistées ; résolution d’accès alignée sur la priorité visuelle de la liste ; UX permissions cohérente Discord-like. bun test 4/4 OK ; tsc --noEmit OK.
### Fichiers
forum-group-permissions.ts, forum-group-permissions-hierarchy.ts, forum-group-permissions.resolve.test.ts, forum-voice.ts (saveForumGroupRoleIcon), api/.../roles/route.ts, api/.../roles/[roleId]/route.ts, ForumGroupRolesPanel.tsx, PermissionToggle.tsx, ForumGroupChannelEditModal.tsx, globals.css, docs/RECAP-…
- Schéma forum_group_roles.icon_url via ensureColumn ; upload saveForumGroupRoleIcon (PNG/WebP, scan+sanitize) ; API GET/POST/PATCH rôles exposent iconUrl + multipart iconImage / removeIcon.
### Contexte
Alignement API expressions + planning après fusion multi-agents (pas de double UI).
### Changements
### Impact
CRUD expressions/ACL et planning édition/RSVP opérationnels en prod (déjà inclus au dernier deploy).
### Fichiers
forum-group-expressions.ts, expressions/route.ts, expressions/[assetType]/[assetId]/route.ts, events/route.ts, ForumGroupEventsModal.tsx, forum-group-permissions-bits.ts
- Expressions : bit MANAGE_EXPRESSIONS, ACL @everyone, upload scanné, route asset dédiée.
- Events : PATCH édition, retrait RSVP au reclic, vérif appartenance serveur.
### Contexte
Deux implémentations concurrentes de l'UI rôles (inline dans ForumGroupSettingsModal + composant ForumGroupRolesPanel) : consolidation en une seule, et complétion des manques (multi-ajout membres, verrou @everyone, reorder en lot côté API).
### Changements
### Impact visible / technique
Réglages rôles lisibles et réordonnables à la souris, couleurs avancées persistées. Une seule source de vérité UI rôles (modal allégé de ~500 lignes). tsc --noEmit OK sur le projet web.
### Fichiers
ForumGroupRolesPanel.tsx, ForumGroupSettingsModal.tsx, lib/forum-group-permissions.ts, api/forum-social/groups/[slug]/roles/route.ts, api/forum-social/groups/[slug]/roles/[roleId]/route.ts, app/globals.css, docs/RECAP-…
- ForumGroupRolesPanel.tsx : unique implémentation des réglages rôles (sidebar liste + détail, scrolls indépendants, cartes). Onglets Affichage / Permissions / Membres.
- Affichage : nom, icône (champ texte court, emoji collé — aucun composer emoji), couleur + mode, « afficher séparément », mentionnable, aperçu live.
### Contexte
Lot UX : menus salons, profil popover, download, bridge avatar/doublons, rôles modernes, traduction UGC, recherche, vocaux social→officiel, resize vocal, Nativefier, expressions, planning.
### Changements
#### Vague 1
#### Vague 2
#### Vague 3
#### Vague 4
### Impact
UX Discord-like renforcée sur /app ; bridge plus stable ; expressions & events disponibles.
### Fichiers
Nombreux sous web/src/components/forum|site-app|i18n, APIs forum-social/*, bot-forum-bridge/src/communityBridge.js, docs/NATIVEFIER-VOICE.md, docs/RECAP-…
- Clic droit salon : Modifier → Déplacer → Supprimer (ConfirmDialog).
- Modal édition salon : onglets à gauche ; delete via ConfirmDialog ; emoji dans le nom.
- Download rail → modal « app non disponible ».
### Contexte
Mode « Déplacer » voulu à la Discord (glisser-déposer à la souris). Images Discord→site arrivant en double (race gateway messageCreate + poll REST pendant le téléchargement PJ).
### Changements
#### Drag-and-drop salons /app
#### Anti-doublon bridge
### Impact
Réorganisation salons à la souris. Un message Discord (surtout avec image) ne doit plus créer deux messages site//app.
### Fichiers
ForumGroupCommunityPanel.tsx, api/internal/community-bridge/route.ts, bot-forum-bridge/src/communityBridge.js, docs/RECAP-…
- Salons texte/vocal draggable (staff) : glisser entre catégories / positions avec barre bleue d’indication.
- Drop en fin de catégorie ; API move réutilisée.
- Click fantôme après drop ignoré ; menu « Déplacer… » conservé en secours.
- Claim atomique INSERT IGNORE sur community_bridge_messages.discord_message_id avant download PJ (discord_text / discord_forum_reply).
### Contexte
Assigner des salons à une catégorie faisait « disparaître » les autres (écrasement périodique par le bridge). Pas de UI pour choisir catégorie + position précise.
### Changements
### Impact
Les salons restent dans la catégorie choisie. Déplacement précis via menu contextuel.
### Fichiers
community-bridge.ts, forum-group-channel-move.ts, channels/route.ts, channels/[channelId]/route.ts, ForumGroupCommunityPanel.tsx, ForumGroupSettingsModal.tsx, docs/RECAP-…
- ensureCommunityBridgeChannels : ne réécrit plus category_id / sort_order des salons déjà liés (placement manuel conservé).
- API PATCH …/channels type move : catégorie + position (beforeChannelId / fin).
- Clic droit salon → Déplacer… (catégorie + position avant un salon / haut / bas).
- Création salon : sort_order = max+1 dans la catégorie cible.
- Réglages : flèches haut/bas limitées à la même catégorie ; changement de catégorie via move.
### Contexte
Posts site→Discord sans pseudo/avatar, sans images ; commentaires Discord non synchronisés (« thread non mappé »).
### Changements
### Impact
Nouveaux posts/réponses : webhook avec vrai pseudo + photo + PJ. Commentaires Discord↔site↔/app actifs sur fils déjà mappés. Anciens messages Discord déjà postés sans identité ne sont pas réécrits.
### Fichiers
internal/community-bridge/route.ts, community-bridge-helpers.ts, bot-forum-bridge/src/communityBridge.js, bot-forum-bridge/package.json, docs/RECAP-…
- JOIN auteurs forum site corrigé : users.user_id = forum_threads/posts.author_id (plus users.id).
- Extraction <!-- ATTACHMENTS:[…] --> + envoi fichiers (lecture disque uploads/ prioritaire).
- Avatar .webp converti en PNG public (uploads/bridge-avatars/) via sharp avant avatar_url Discord.
- Sync commentaires : auto-heal mapping community_bridge_threads depuis forum_threads / app posts ; miroirs /app rattrapés (threads 98/100/101).
### Contexte
Besoin de lier les salons Discord LBXMB (entraide, jeux-vidéo, cinéma, culture, technologie, dév-test, hors-sujet) au serveur officiel /app et au forum classique, avec sync bidirectionnelle live (webhooks, pseudo/avatar).
### Changements
#### Map + salons + superusers
#### UX forum Discord-like /app
#### Bridge webhook multi-canal
#### Fantômes + emojis
### Impact
### Fichiers
community-bridge-map.ts, community-bridge.ts, community-bridge-helpers.ts, community_bridge_map.json, bidirectional_sync_config.json, forum-group-permissions.ts, ForumGroupForumChannelView.tsx, ForumGroupCommunityPanel.tsx, forum-posts/route.ts, forum-posts/[postId]/route.ts, internal/community-bridge/route.ts, messages/route.ts, thread/create/route.ts, forum-thread-reply-core.ts, bot-forum-bridge/src/communityBridge.js, bot-forum-bridge/src/index.js, scripts/sync-official-lbxmb-group.ts, docs/RECAP-…
### Contexte
Le côté social /app n’avait pas de serveur par défaut. Objectif : serveur officiel peuplé depuis la guild Discord LBXMB.
### Changements
### Sync initiale
group_id=9 — 22 rôles, 7 catégories, 31 salons texte (23 text / 7 forum / 1 announcement), 3 vocaux, 32 overwrites.
### Impact
Tout user authentifié avec accès groupes rejoint auto LB'XMB.FR ; structure alignée Discord (sans messages/membres Discord).
### Fichiers
forum-official-server.ts, discord-guild-structure-sync.ts, groups/route.ts, groups/[slug]/leave, groups/[slug]/route.ts DELETE, channels/route.ts, official/sync-discord/route.ts, SiteAppServerRail.tsx, ForumGroupCommunityPanel.tsx, use-forum-social.ts, scripts/sync-official-lbxmb-group.ts, docs/RECAP-…
- Seed forum_groups slug lbxmb-fr, name LB'XMB.FR, is_official=1, avatar groups/official-lbxmb.png (copie de /lbxmb.png).
### Contexte
Phase 1 = LiveKit remote only. Besoin de gérer salons, rôles, permissions, profil serveur, bans, messages sur le VPS utilisateur.
### Décision
Hybrid : /app + auth lbxmb.fr ; données serveur + uploads sur agent VPS ; vocal LiveKit remote inchangé.
### Changements
### Impact
Un serveur auto-hébergé avec URL agent stocke salons/rôles/bans/messages sur le VPS ; le client /app ne change pas d’UX. Nouveaux serveurs only (pas de migration auto).
### Fichiers
infra/self-host-agent/*, forum-group-selfhost.ts, forum-group-agent-proxy.ts, routes forum-social/groups/**, internal/selfhost/*, ForumGroupsShell.tsx, ForumGroupSettingsModal.tsx, ForumGroupCommunityPanel.tsx, docs/RECAP-…
- Pack infra/self-host-agent/ : Node agent + Postgres + LiveKit/Coturn, API /v1/* (profil, channels, roles, bans, members, invites, messages, uploads), JWT session, heartbeat, fan-out realtime + ban-sync vers plateforme.
### Contexte
Avatar webhook d’annonces manquant ; boutons réaction « Annonce introuvable » ; traductions FR→FR inutiles ; démarrage self-host LiveKit remote pour serveurs /app.
### A1 — Avatar webhook annonces
### A2 — Boutons réaction annonce
### A3 — Skip même langue (FR→FR)
### B — Self-host vocal phase 1 (média LiveKit remote)
### Impact
Réactionsitions annonces Discord (avatar + réactions) ; plus de reformulation FR→FR ; un serveur /app peut pointer son LiveKit VPS (texte reste central).
### Fichiers
forumWebTranslateHandler.js, libretranslate.ts/js, discordLocale.js, translateForInteraction.js, autoTranslationHandler.js, customCommandTranslate.js, thread/create/route.ts, forumAnnouncementPost.js, discordRest.js, forum-group-selfhost.ts, groups/route.ts, groups/[slug]/route.ts, voice/token/route.ts, ForumGroupsShell.tsx, ForumGroupSettingsModal.tsx, ForumGroupCommunityPanel.tsx, selfhost-heartbeat/route.ts, infra/self-host-voice/*, docs/RECAP-…
### Contexte
PageSpeed : CWV mobile Failed (INP ~313 ms). Lab perf ~67–75. Hydration React #418 probable (perf-mode / dates / Hero).
### Changements
### Impact
Moins de travail main-thread au first paint mobile → INP ; moins de mismatch hydratation.
### Fichiers
AppScrollShell.tsx, DeferredLightRays.tsx, PageLightRaysBackdrop.tsx, Hero.tsx, TextType.tsx, ShinyText.tsx, Footer.tsx, HomeFeedCarousel.tsx, Testimonials.tsx, HomeBelowFold.tsx, Header.tsx, usePreferLiteUi.ts, stable-date.ts, docs/RECAP-…
- AppScrollShell : scroll natif sur mobile/coarse ; scrollbar custom desktop uniquement.
- DeferredLightRays : next/dynamic + idle ; PageLightRaysBackdrop : WebGL desktop only (CSS gradient mobile/perf).
- Hero : chemin lite (mobile/perf) sans TextType/gsap, ShinyText, CountUp, motion ; prefetch={false} CTAs.
- TextType / ShinyText : même arbre au first paint ; animations après mount desktop.
### Contexte
LiveKit Cloud en 429. Refus d’exposer la box 86.241.102.88. 2ᵉ NIC VM Canaux = 151.240.100.62 pour le média WebRTC.
### Changements
### Impact
Signal via nginx wss://lbxmb.fr/livekit. Média/ICE/TURN sur IP publique VM Canaux (pas de NAT box). E2EE vocal conservé. STUN/TCP 3478/7881 OK sur .62.
### Fichiers
livekit/livekit.yaml, livekit/turnserver.conf, web/.env.local, bot-forum-bridge/.env, web/server.js, ecosystem.config.js, /usr/local/sbin/canaux-policy-routing.sh, /etc/systemd/system/canaux-policy-routing.service, docs/RECAP-TRAVAIL-COMMUNAUTE-2026-04.md
- Policy routing persisté : traffic sourcé .62 → gateway 151.240.100.1 via Canaux (canaux-policy-routing.service + /usr/local/sbin/canaux-policy-routing.sh).
- livekit/livekit.yaml : use_external_ip: false, node_ip: 151.240.100.62, TURN WAN .62 + LAN 192.168.1.112.
- livekit/turnserver.conf : relay-ip / external-ip = 151.240.100.62.
### Contexte
Pas d’exposition NAT/box pour le média WebRTC. Self-hosted OK en LAN seulement. Bascule prod vers LiveKit Cloud + réactivation E2EE vocal.
### Changements
### Impact
Média via SFU Cloud (pas de ports maison). E2EE vocal opt-in actif. Attention : le projet Cloud actuel renvoie encore 429 minutes limit exceeded — il faut un nouveau projet / upgrade sur cloud.livekit.io et mettre à jour les clés.
### Fichiers
.env.local, bot-forum-bridge/.env, ForumVoiceSession.tsx
- web/.env.local + bot-forum-bridge/.env : LIVEKIT_USE_CLOUD=1, URLs/clés projet lb-xmb-fr-tvls6smf, NEXT_PUBLIC_E2EE_VOICE=1.
- Message d’erreur si quota Cloud (429 connection minutes).
- Deploy web-prod + restart bridge.
### Contexte
Vocal OK en réseau local, KO depuis Internet. Cause : livekit.yaml avait use_external_ip: false + node_ip: 192.168.1.112 → candidats ICE LAN injoignables hors box.
### Changements
### Impact
Les clients Internet reçoivent des candidats publics / TURN au lieu de 192.168.1.112.
### Prérequis box
Forward vers 192.168.1.112 : UDP/TCP 3478, UDP 49160–49200, TCP 7881, UDP 50000–50200.
### Fichiers
livekit/livekit.yaml, forum-voice.ts, ForumVoiceSession.tsx
- livekit/livekit.yaml : use_external_ip: true, node_ip: 86.241.102.88, TURN public + LAN.
- forum-voice.ts : filtre les STUN IP privées pour clients hors LAN.
- ForumVoiceSession.tsx : clients hors LAN démarrent en iceTransportPolicy: relay (TURN).
- Restart lbxmb-livekit + lbxmb-coturn.
### Contexte
Toujours spinner pour les autres alors qu’ils étaient dans LiveKit. Cause structurelle : chemin late-joiner E2EE faisait connect puis waitOnly/enableRoomE2ee avant setRoom. Flag NEXT_PUBLIC_E2EE_VOICE=0 fragile (défaut ON si process ambigu).
### Changements
### Impact
Présence LiveKit = scène affichée ; plus de spinner infini post-connect.
### Fichiers
e2ee-flags.ts, ForumVoiceSession.tsx
- e2ee-flags.ts : E2EE vocal opt-in uniquement ("1" / "true"), défaut OFF.
- ForumVoiceSession.tsx : connect puis setRoom sans attendre une clé late-joiner ; E2EE late en arrière-plan si jamais réactivé.
### Contexte
Après le fix late-joiner E2EE, d’autres users restaient en chargement alors qu’ils apparaissaient déjà dans LiveKit (forum_voice_1). Aucun seal voice_channel en base ; le blocage était après connect, sur getUserMedia / RNNoise (attente permission micro) avant setRoom.
### Changements
### Impact
Les late joiners voient la scène même si le micro est en attente ; plus de spinner infini lié au GUM. Vocal sans E2EE pour rétablir le multi-user.
### Fichiers
ForumVoiceSession.tsx, web/.env.local, livekit-e2ee.ts
- ForumVoiceSession.tsx : setRoom + fin du connecting dès le signal LiveKit ; micro en arrière-plan avec timeout 12s.
- NEXT_PUBLIC_E2EE_VOICE=0 : chiffrement vocal désactivé tant que la distrib de clés multi-users n’est pas fiable (DM/canaux texte inchangés).
- ensureIdentityPublished : échoue si le POST device-keys échoue.
### Contexte
Un user se connectait au vocal, les autres restaient en chargement. Cause : clé E2EE scellée uniquement pour le 1er ; les suivants avaient hasAny sans ciphertext. De plus, le poll avant connect empêchait le 1er de re-sceller (chicken-egg).
### Changements
### Impact
Les utilisateurs qui rejoignent après le 1er peuvent obtenir la clé E2EE et quitter le spinner.
### Fichiers
resolve-media-room-key.ts, ForumVoiceSession.tsx, api/forum/voice/token/route.ts, forum-voice.ts
- resolve-media-room-key.ts : export publishSeals / resealMediaRoomKeyForUsers / peekMediaRoomSeal ; poll ~22s pour late joiners ; mode waitOnly.
- ForumVoiceSession.tsx : late joiner connecte d’abord puis attend la clé ; re-scellage immédiat sur ParticipantConnected ; timeout connect 25s.
- token/route.ts : e2eeRecipientUserIds inclut les participants LiveKit déjà présents.
- forum-voice.ts : purge e2ee_sealed_room_keys quand la room est vide.
### Contexte
Après le fix CSP, nouvelle erreur : Encountered websocket error during connection establishment. Diagnostic : LiveKit Cloud répond 429 connection minutes limit exceeded. Self-hosted (wss://lbxmb.fr/livekit, container lbxmb-livekit) OK.
### Changements
### Impact
Le token vocal pointe vers wss://lbxmb.fr/livekit (plus de quota Cloud). ICE Coturn maison réactivé hors Cloud.
### Fichiers
web/.env.local, bot-forum-bridge/.env, web/server.js, ForumVoiceSession.tsx, ForumVoiceCallStage.tsx
- web/.env.local + bot-forum-bridge/.env : URLs/clés self-hosted, LIVEKIT_USE_CLOUD=0.
- web/server.js : force-reload des vars LiveKit/TURN depuis .env.local (PM2 gardait l’ancien Cloud).
- Messages d’erreur vocal (quota / websocket) dans ForumVoiceSession + ForumVoiceCallStage.
- Restart web-prod + bot-forum-bridge.
### Contexte
Sur /app, connexion vocal échouait avec could not establish signal connection: NetworkError when attempting to fetch resource. Le message UI pointait à tort vers les permissions micro Waterfox. Cause : connect-src CSP sans https://*.livekit.cloud — le SDK LiveKit fetch régions/validate en HTTPS.
### Changements
### Impact
Les fetch HTTPS LiveKit Cloud ne sont plus bloqués par la CSP ; message utilisateur moins trompeur.
### Fichiers
web/src/lib/csp.ts, web/src/components/forum/ForumVoiceCallStage.tsx
- csp.ts : ajout https://*.livekit.cloud, wss://*.livekit.cloud, https://*.livekit.run, wss://*.livekit.run dans CONNECT_SOURCES.
- ForumVoiceCallStage.tsx : message d’erreur différencié (signal/réseau vs micro vs générique) + détail technique affiché.
### Contexte
Cards catégorie vs produit de tailles/styles incohérents ; icônes produit rognées (object-cover) sur le profil.
### Changements
### Impact
Catalogue + profil plus cohérents ; logos/icônes lisibles dans les cards.
### Fichiers
shop-category.ts, CategoryCard.tsx, ProductCard.tsx, profile/Shop.tsx
- Tokens partagés SHOP_CATALOG_CARD_* (shell, média 4/3, body, titre, meta, prix).
- CategoryCard + ProductCard alignés sur le même chrome.
- Produits : icônes en object-contain + padding (plus de crop agressif).
- Grille profil : mêmes gaps que le catalogue boutique.
### Contexte
Le sheet profil mobile n’avait pas le même flou glass que le menu Naviguer.
### Changements
### Impact
Cohérence visuelle entre Naviguer et options profil sur mobile.
### Fichiers
ProfileMenuSheet.tsx
- ProfileMenuSheet : glass-darker + bordures alignées sur MobileNavigateSheet (au lieu de bg-[#0c0c0e]/95 backdrop-blur-xl).
### Contexte
L'utilisateur veut uniquement la redirect lbxmb.fr/discord → https://discord.gg/kVC27qXsbE, sans page dédiée.
### Changements
### Impact
Plus de landing ; ouverture directe de l'invite Discord.
### Fichiers
next.config.js, discord/page.tsx
- Redirect Next restaurée dans next.config.js.
- discord/page.tsx : redirect(DISCORD_INVITE_URL) (filet de sécurité).
### Contexte
Clarification : modal de join Discord via bouton dans un message Discord (pas via le site).
### Changements
### Impact
/invite dans un salon → bouton cliquable qui ouvre le modal Discord « Rejoindre ».
### Fichiers
bot-public/src/commands/util/invite.js, serverConfig.js, guard.js, help.js
- Nouvelle commande publique /invite : container V2 + bouton Link « Rejoindre le serveur » → https://discord.gg/kVC27qXsbE (ouvre le modal invite Discord).
- Ajoutée à DEFAULT_COMMANDS + PUBLIC_COMMANDS + help.
### Contexte
Tester le join Discord via bouton (ouvre l’invite → modal Discord) plutôt qu’une redirect directe.
### Changements
### Impact
lbxmb.fr/discord affiche la landing ; le bouton ouvre l’invite Discord (modal join côté Discord).
### Fichiers
discord/page.tsx, next.config.js
- Suppression redirect /discord → discord.gg dans next.config.js.
- Restauration de la page /discord (stats + CTA « Rejoindre le Discord » → DISCORD_INVITE_URL en nouvel onglet).
### Contexte
Retirer Discord de la navbar ; /discord → invite directe ; remanier la bottom nav mobile.
### Changements
### Impact
Lien Discord = invite directe ; navigation mobile via sheet bas.
### Fichiers
Header.tsx, MobileBottomNav.tsx, MobileNavigateSheet.tsx, discord/page.tsx, next.config.js, discord.ts, messages/*.json, .env.local
- Navbar desktop : entrée Discord retirée.
- /discord redirige vers https://discord.gg/kVC27qXsbE (next.config + page + invite URL).
- Bottom nav mobile : Accueil → Naviguer (sheet bottom comme notifications) avec Accueil, Guides, Ressources, Forum, Boutique, Outils, Faire un don.
- Icône menu ⋮ (trois points) retirée du header mobile.
### Contexte
1. KV / CID (sans catégorie) invisibles sur le hub /boutique.
2. Salon Discord boutique : fils obsolètes (produits disparus) + nouveaux produits non postés (auto-approve sans hook Discord).
3. /sanction : remettre une modale ; titre container avec ping rôle + allowedMentions.
### Changements
#### Hub boutique
#### Sync Discord boutique
#### /sanction
### Impact
### Fichiers
boutique/page.tsx, api/shop/request/route.ts, sanction.js, communitySanctionHandler.js, discord-site-forum-sync.json
- Produits category vide/NULL affichés en ProductCard à côté des CategoryCard.
- POST /api/shop/request : capture insertId + postSiteContentToDiscordForum si auto-approuvé.
- Nettoyage : 16 fils Discord orphelins supprimés ; backfill 37 produits manquants (KV/CID déjà OK). Total sync shop: = 39.
### Contexte
1. Impossible de modifier correctement les compléments/options d’un produit (édition).
2. Sur le dashboard boutique, la modale de confirmation de suppression était cassée (overlay coincé / mal positionné).
### Changements
#### Options produit (édition)
#### Modale suppression dashboard
### Impact
### Fichiers
BoutiqueProductForm.tsx, ShopRequestsPanel.tsx
- Clés React des variantes basées sur un id stable (plus sur label/price_delta) → plus de perte de focus à chaque frappe.
- Hydratation du formulaire edit une seule fois par produit (ref) pour éviter qu’un refetch React Query écrase les saisies.
- Remplacement de l’overlay maison (fixed dans une card overflow-hidden + blur) par ConfirmDialog (portal Radix, z-[300]).
### Contexte
Le mode performance ne coupait pas le loader @ animé (Framer pathLength) ni le délai 800ms / fade blur sur /profil et /profil/[pseudo]. LogoLoop ignorait aussi perf-mode.
### Changements
### Impact
Appareils faibles : plus d’anim Framer / typewriter / carrousel RAF liés à ces surfaces.
### Fichiers
profil/page.tsx, ui/at-sign.tsx, LogoLoop.tsx
- Profil : en mode perf, pas d’AtSign animé, pas d’overlay 800ms, contenu immédiat (Loader2 statique pendant fetch).
- AtSignIcon : SVG statique + startAnimation no-op si perf.
- LogoLoop : respecte html.perf-mode (plus de RAF).
### Contexte
Le mode perf coupait surtout le CSS ; Framer / TextType / ShinyText / CountUp continuaient.
### Changements
### Fichiers
ReducedMotionObserver.tsx, useReducedMotion.ts, usePerformanceMode.ts, TextType.tsx, ShinyText.tsx, Hero.tsx, globals.css, fr.json, en.json
- MotionConfig reducedMotion="always" si perf.
- useReducedMotion = prefers-reduced-motion ou mode perf.
- TextType : texte complet (pas de typewriter/GSAP).
- ShinyText : texte statique.
- Hero : CountUp instantané + motions sans entrée.
- CSS perf : animate-*, fade/slide-in, transforms hover, view-transitions coupés.
- Libellés FR/EN mis à jour.
### Contexte
En-tête sans # ; besoin d’éditer les messages existants sans renvoyer (pas de MP).
### Changements
### Fichiers
forumPollBridge.js, internalHttpServer.js
- En-tête : # 📊 Titre | <@&rôle>.
- allowRolePing uniquement à la 1ʳᵉ publication (edit/sync sans re-ping Discord).
- editForumPollLayoutInPlace + endpoint /internal/forum/poll-edit-layout (edit only).
- Messages ouverts édités in-place (pas de renvoi → pas de MessageCreate → pas de MP).
### Contexte
Un message membre dans annonces / sondages / etc. déclenchait aussi les MP.
### Changements
### Fichiers
notifChannelDm.js, messageCreate.js
- DM seulement si webhookId ou auteur = le bot.
- Les messages utilisateurs dans ces salons n’envoient plus de MP.
### Changements
### Fichiers
notifChannelDm.js
- Retrait de sondages de NOTIF_DM_DISABLED_KEYS.
- Les publications salon sondages renvoient à nouveau les MP (opt-out bouton).
### Contexte
Retour à un seul container ; mention dans le container (📊 Titre | <@&rôle>). Pas de republish des messages existants.
### Changements
### Fichiers
forumPollBridge.js, notifChannelDm.js
- Payload = 1 Container (plus de TextDisplay hors container).
- En-tête : 📊 Titre | <@&role> + allowedMentions.roles.
- MP sondages toujours coupés ; note TODO : opt-in notifs par défaut (annonces/actu/sondages) + opt-out bouton.
### Contexte
Les messages migrés s’affichaient comme « Forum » sans avatar site.
### Changements
### Fichiers
forumPollBridge.js, internalHttpServer.js, thread/create/route.ts
- Webhook envoie username + avatarURL (résolution HTTPS depuis photo site).
- Stockage authorPseudo / authorAvatar sur le poll.
- Republish récupère l’auteur via /api/forum-social/thread/{id} si besoin, puis renvoie le message (Discord n’applique l’identité qu’à l’envoi).
- Create route : absoluteUploadUrl / getPhotoUrl pour l’avatar.
### Contexte
Deux messages (ping + container) n’étaient pas le rendu voulu.
### Changements
### Fichiers
forumPollBridge.js
- Un seul message Components V2 :
- TextDisplay hors container : # <@&role> | Titre
- Container : description + meta + options/votes + boutons
- Publier / sync / votes / republish alignés sur ce layout
- Suppression de l’ancien message ping séparé au republish
### Contexte
Les publications dans le salon sondages (webhook inclus) déclenchaient des MP massifs alors que le format n’est pas finalisé.
### Changements
### Fichiers
bot-public/src/lib/notifChannelDm.js
- NOTIF_DM_DISABLED_KEYS : clé sondages → aucun DM tant que le layout poll n’est pas stable.
- Annonces / actualités inchangées.
### Contexte
La boutique listait tous les produits (souvent 3 cols desktop). Besoin d’un catalogue par catégories + densification.
### Changements
### Fichiers
shop-category.ts, CategoryCard.tsx, ProductCard.tsx, boutique/page.tsx, boutique/c/[slug]/page.tsx, api/shop/categories/route.ts, boutique/[id]/page.tsx, BoutiqueProductForm.tsx
- /boutique = hub de catégories (cartes image + compteur + prix min).
- /boutique/c/[slug] = produits de la catégorie (recherche, breadcrumb).
- Grille 2 / 3 / 5 cols (min-[700px] / min-[1000px]), indépendante du breakpoint 1340px.
- ProductCard size=shop densifié (~1,5×).
- API GET /api/shop/categories (agrégation SQL, pas de nouvelle table).
- Compat /boutique?category= → redirect slug ; breadcrumb fiche produit mis à jour.
- Aide formulaire vendeur : « la boutique affiche d’abord les catégories ».
### Contexte
Un ban s’appliquait dès 75 % atteint (ex. 3 pour / 1 contre), sans attendre la fin du vote.
### Changements
### Fichiers
communitySanctionHandler.js, communitySanctionStore.js
- Plus de clôture anticipée après un vote.
- À l’expiration 24 h : decideFromVotes (75 %, min 4 votes) → apply / reject / expired si pas de majorité.
- Textes UI mis à jour (« décision à la fin des 24 h »).
### Contexte
Certains membres doivent pouvoir voter aux sanctions communautaires sans attendre 2 semaines sur le serveur.
### Changements
### Fichiers
communitySanctionStore.js, communitySanctionHandler.js
- Whitelist COMMUNITY_VOTE_MEMBERSHIP_BYPASS_IDS : 774721833241018430, 1011936763742277662, 1250019013250121770.
- assertVoterEligibility saute le contrôle d’ancienneté pour ces IDs.
### Contexte / problème
### Changements appliqués
### Impact
### Fichiers
forumPollBridge.js, sondage.js, pollScheduler.js, thread/create/route.ts
- Sondages forum postés en Components V2 (bot) au lieu d’un webhook comme annonces/actus.
- Liste des participants inline illisible dès qu’il y a beaucoup de votes.
- Publication sondage via webhook « Forum Web » : message classique # <@&role> | Titre + corps + barres + boutons.
- Plus de noms de votants dans le message ; bouton Voir les participants → détail éphémère (réponses).
- Même règle pour /sondage natif (plus de liste inline, bouton participants).
- Avatar/pseudo auteur passés au webhook.
- Affichage Discord aligné sur les annonces ; participants à la demande.
### Contexte / problème
Après OAuth sur /verify/{guildId} (ex. 152849…), redirection encore vers /commencer?step=prefs (next OAuth ou confusion DISCORD_GUILD_ID).
### Changements appliqués
### Impact
Vérif d’un serveur tiers reste sur sa page /verify/{id} (succès/échec).
### Fichiers
verify-oauth-callback-logic.ts, authorize/route.ts, VerifyClient.tsx
- Callback : hors guild principale → toujours /verify/{id}?verified=1 (ignore next vers /commencer).
- isMainGuild : uniquement COMMENCER_GUILD_ID / ID LB'XMB fixe (plus de fallback DISCORD_GUILD_ID).
- Authorize + VerifyClient : ne passent plus de next=/commencer pour les autres guilds.
### Contexte / problème
### Changements appliqués
### Impact
### Fichiers touchés
- Sondages forum : pas de synchro Discord type /sondage, boutons cœur inutiles, pas de durée obligatoire, votes site/Discord non liés.
- Salon Discord 1509777735604502589 : message type /sondage (Components V2) avec en-tête # <@&1533535457508462612> | Titre, boutons vote gris, uniquement 🌐 + 🔗 (pas de ❤️/💔).
- Votes MySQL + discord_id : même identité Discord/site = un seul votant (anti-doublon).
- Site : durée obligatoire (presets) dans le composeur sondage ; sync Discord auto à la publication.
- Bridge bot-public : /internal/forum/publish-poll + /internal/forum/poll-sync ; API interne site /api/internal/forum/poll-vote + poll-close.
- Un vote Discord ou site (compte lié) se reflète des deux côtés sans doublon.
- Fin de durée : clôture Discord + site.
- web/src/lib/forum-poll.ts, forum-poll-votes.ts
### Contexte
Les CategoryCard renvoyaient vers /boutique/c/[slug] ; besoin d’un layout type capture (cover + titre + count + fourchette prix) et d’un modal produits.
### Changements
### Fichiers
shop-category.ts, api/shop/categories/route.ts, CategoryCard.tsx, CategoryProductsModal.tsx, boutique/page.tsx, boutique/c/[slug]/page.tsx
- API catégories : maxPrice (MAX(price)).
- CategoryCard : layout cover 16/10 + bandeau sombre, prix bleu min–max, clic onSelect.
- CategoryProductsModal : Dialog grille ProductCard + recherche.
- Hub /boutique : ouvre le modal ; deep link ?c=slug ; ?category= → ?c=.
- /boutique/c/[slug] → redirect /boutique?c=slug.
### Contexte / problème
1. Boutons d’annonces Discord colorés (vert/rouge/bleu) — demander gris.
2. Activation transcript tickets sans forcer l’événement logs ticket_transcript.
3. Format message annonce/actualité : manquait le ping rôle + titre en H1.
### Changements appliqués
### Impact
### Fichiers touchés
- Boutons ❤️ / 💔 / 🌐 en style Secondary (gris) ; lien 🔗 reste Link.
- En-tête message : # <@&role> | Titre puis corps — rôle annonce 1533535452240547840, actualité 1533535454861721620 ; allowed_mentions.roles pour le ping.
- Toggle transcript tickets ON → logs.events.ticket_transcript = true + bucket tickets dans enabled_types.
- Annonces/actus Discord : header + ping rôle + boutons gris.
- Transcripts tickets visibles dans les logs dès activation côté tickets.
- bot-site/src/lib/forumAnnouncementPost.js
### Contexte / problème
### Changements appliqués
### Impact
### Fichiers touchés
- Page /verify/{guildId} (ex. 1528492590683394078) exigeait le règlement Discord (réservé au serveur principal /commencer).
- Après OAuth Discord, redirection forcée vers /commencer au lieu de rester sur /verify/{id} (succès/échec).
- Manque d’options config vérif : multi-comptes/IP, FAI France only, autoriser 4G/5G.
- Cases légales : hors serveur principal → CGU + mentions uniquement (requireReglement=false) ; cookie/API OAuth alignés.
- Callback OAuth : succès → /verify/{guildId}?verified=1 (sauf guild principale → /commencer?step=prefs) ; erreurs déjà sur /verify/{id}.
- Suppression du redirect client auto vers /commencer dans VerifyClient.
- Config bot /config vérif : toggles Multi-IP, FAI FR, 4G/5G (+ persistance allow_same_ip_multi, french_isp_only, allow_mobile).
### Contexte
Panneau /config vérif web renvoyait tous les serveurs vers /commencer (parcours LB'XMB uniquement).
### Changements
### Fichiers
verificationPanel.js, panelsSecurity.js
- Guild principale 1232966743241130005 → https://lbxmb.fr/commencer
- Autres guilds méthode web → https://lbxmb.fr/verify/{guildId} (bouton lien « Me vérifier »)
### Changements
1. Images jointes au post forum envoyées en pièces jointes Discord (webhook multipart).
2. Plus de réactions emoji ; boutons sans texte : ❤️ vert, 💔 rouge, 🌐 bleu (traduction locale), 🔗 gris (lien post).
3. Compteurs -# ❤️ n · 💔 n sous le message ; vote toggle par utilisateur.
### Fichiers
forumAnnouncementPost.js, discordRest.js, forumWebTranslateHandler.js, interactionCreate.js, discord-bot-site.ts, thread/create/route.ts, bot-site/index.js
### Changements
### Fichiers
staticInfoPages.js, post-static-info.js, interactionCreate.js
### Hotfix annonces forum : pas d’intégration lien
- Salon 1535574434683944980 : message présentation des salons (liste structurée) + bouton Translate (locale client, éphémère).
- Salon 1490638171640434748 : règlement + même bouton Translate.
- Handler static_info_tr:* + script scripts/post-static-info.js.
### Contexte
DM trop verbeux + lien « gérer les rôles » ; besoin d’opt-out simple.
### Changements
### Fichiers
notifChannelDm.js, notifChannelDmHandler.js, interactionCreate.js
### Hotfix bot-public (chemin import notif DM)
- Texte DM : Une nouvelle <#salon> a été publiée sur LB'XMB.FR
- Bouton Ne plus m'informer → modal (taper CONFIRMER) → retire le rôle Discord notif ; bouton devient Je veux être informé (re-ajoute le rôle).
- Appliqué à annonces, actualités, sondages (pas giveaway/sanctions/vidéos).
### 1. Miroir posts forum
### 2. Boutique forum Discord
### 3. Notifs Twitch/YT/GitHub/RSS
### 4. Annonces forum → Discord
### Fichiers
discord-site-forum-channels.json, siteForumSync.js, forumAnnouncementPost.js, forumWebTranslateHandler.js, libretranslate.js, notificationsModals.js, routerNotifications.js, youtube.js/streams.js/github.js/rss.js, notifMessage.js, thread/create/route.ts
### Contexte
Lien Discord /boutique/{id} : embed générique (layout liste) — pas le nom / icône / description du produit.
### Changements
### Impact
Embed Discord sur partage produit affiche titre, icône et description du produit.
### Fichiers
shop-product-og.ts, boutique/[id]/layout.tsx, shop/[id]/layout.tsx
- Layout boutique/[id] + lib partagée : og:title = nom, og:image = icône produit (image/icon), og:description = description plain ; Twitter summary (vignette).
- Alignement shop/[id] (redirect) sur le même contenu OG / canonical boutique.
### Contexte
Faux positifs audit OAuth + tokens non fiables ; sécuriser les prochaines vérifs (scopes, persist tokens, pas de revoke race).
### Changements
1. Callback vérif refuse si scopes incomplets (identify, guilds.join, relationships.read) ou sans refresh_token.
2. Upsert account obligatoire (échec → pas de vérif) ; refresh toujours écrasé (pas COALESCE silencieux).
3. Entrée verified_users : oauth_scope + oauth_linked_at ; clear revoked_at / revoke_reason ; drop des revoke_guild_web_verification en file pour ce membre.
4. Audit --apply exige aussi --force (webhook reste la source de vérité).
### Impact
Prochaine vérif via /commencer stocke une app OAuth complète ; re-vérif après retrait d’app ne se fait pas écraser par une commande revoke en attente.
### Fichiers
discord-oauth.ts, verify-oauth-callback-logic.ts, scripts/audit-verified-oauth.mjs
### 1. Pseudo Discord → URL /profil/...
### 2. Bot refile le rôle non-vérif aux légitimes
### 1. Boutique : cards trop grandes
### 2. Forum recherche : bouton Profil
### 3. Bot : rôles vérif incohérents
### 1. /account réservé owners
### 2. App Discord retirée → re-vérif
### 3. Blacklist → MP aux amis OAuth
### Note audit OAuth (rollback)
- Membres : 2000 caractères (texte brut hors markdown léger).
- VIP Premium, VIP Booster, ancien slug vip : 4000.
- UI compteur + blocage saisie + validation API thread/create alignés.
### 1. Menu tags (priorité d’affichage)
### 2. Sidebar sujets PC trop grande
### 3. Fond dalle / « établi » disparu
### Contexte
L’attribution ViewChannel sur les catégories console (overwrite membre) échouait parfois : rate-limits Discord mal gérés, retries faibles, succès UI même si des salons avaient échoué, IDs catégorie stale possibles.
### Changements appliqués
### Impact
### Fichiers
- discordFetch : respect Retry-After / retry_after (429), retries 5xx / réseau.
- applyCommencerChannelAccess : file globale (pas de courses multi-users), concurrence 2, 3 passages sur les échecs, retrait des overwrites des consoles désélectionnées, bits View+ReadHistory+Connect+Send+Speak.
- Plus d’ajout d’ID catégorie absent du guild live.
- APIs /api/commencer et /api/roles : échec explicite si des consoles sont choisies mais l’accès salon n’est pas complet ; passage de previousConsoles.
- Test live : NES+PS4 → 10/10 overwrites en ~2 s.
- bot-site redémarré + web-prod redéployé.
### Contexte
Les votes /sanction n'expiraient pas. Le champ « type de sanction » devait devenir un type de proposition plus large (modo / partenariat).
### Changements appliqués
### Impact
### Fichiers
- Expiration automatique des votes à 24 h (expiresAt, scheduler + filet 5 min) ; statut expired si pas de seuil atteint.
- Champ renommé Type de proposition avec options : Timeout, Ban, Retirer modérateur, Ajouter modérateur, Retirer un partenariat.
- Application rôles : Modérateurs (1532605771005558935), Partenaires (1307424700934258751) + MP « vote communautaire ».
- Affichage du countdown de fin de vote sur le message ; durée (timeout/ban) toujours gérée séparément.
- Une proposition sans 15 votes d'un côté expire sous 24 h sans effet.
- Auto-nomination autorisée uniquement pour « Ajouter modérateur ».
- bot-public/src/lib/moderation/communitySanctionStore.js
### Contexte
Les sanctions appliquées via /sanction (TO/ban) n'affichaient pas clairement l'origine « vote communautaire » dans le MP (les extraLines n'étaient pas rendus dans le payload V2).
### Changements appliqués
### Impact
### Fichiers
- notifySanctionMember / buildSanctionDmPayload : flag communityVote.
- Titre MP : « Vous avez été … suite à un vote communautaire ».
- Champ Origine = Vote communautaire (plus de mention du bot comme modo).
- Détails + entrée store avec communityVote: true ; raison audit Discord déjà préfixée [Vote communautaire].
- L'utilisateur sanctionné par le vote voit clairement que c'est une décision communautaire.
- Les sanctions staff classiques (/ban, /to, …) sont inchangées.
- bot-public/src/lib/moderation/sanctionNotify.js
### Contexte
Mise en place d'un système de proposition de sanction communautaire : modal /sanction, publication anonyme dans un salon de vote, décision à 15 votes « d'accord » ou « pas d'accord ».
### Changements appliqués
### Impact
### Fichiers
- Nouvelle commande publique /sanction ouvrant une modale V2 (sélecteur de membre, type Timeout/Ban, raison, durée optionnelle).
- Publication d'un container Components V2 dans le salon 1532747804852617216 sans afficher le proposant : membre ciblé, type, durée, raison + boutons vert/rouge.
- Compteur de votes (1 vote / utilisateur, bascule possible) ; à 15 « Je suis d'accord » → application TO ou ban + MP sanction ; à 15 « Je ne suis pas d'accord » → rejet, pas de sanction.
- Timeout : durée obligatoire (max 28j). Ban : durée vide = permanent. La cible ne peut pas voter sur sa propre proposition.
- Persistance JSON data/moderation/community_sanctions.json + routage interactions cmsanc:*.
### Contexte
Après le correctif précédent, bun audit confirmait 0 vulnérabilité, mais npm audit continuait d'afficher 32 vulnérabilités (dont 1 "critical" sur better-auth, des CVE sur axios, hono, next, etc.) qui n'ont rien à voir avec l'état réel du projet. Repartagé par le signalant via le terminal pour vérification.
### Investigation
### Changements appliqués
### Impact
### Fichiers
- web/package-lock.json datait du 1er mai 2026 (vs bun.lock mis à jour le jour même) : un fichier totalement gelé/obsolète depuis 3 mois, qui ne reflète ni les dépendances installées, ni les correctifs appliqués depuis (montée cap-widget, patchs de sécurité du jour, etc.).
- Vérifié qu'aucun script, CI ou Dockerfile du projet ne référence package-lock.json (seules occurrences trouvées : un script interne à node_modules/@sentry/cli, sans rapport avec le workflow du projet).
### Contexte
Après le rollback d'urgence (14:58), bun audit repassait de 34 à 18 vulnérabilités : le rollback avait annulé tous les correctifs de sécurité obtenus par bun update --latest, y compris ceux qui étaient légitimes (adm-zip, sharp). Signalé par le signalant en repartageant le terminal : "ya encore des soucis avec les paquets". Objectif : corriger les vraies failles de sécurité sans réintroduire les montées de version majeures et risquées (redis, bullmq, cookie, swiper, @socket.io/sticky, etc. — voir entrée 14:58, ceux-là restent volontairement sur leur ancienne version, aucun CVE ne les concernait).
### Investigation
### Changements appliqués
### Impact
### Fichiers
- Confirmé au passage que npm audit est basé sur package-lock.json, un fichier obsolète depuis le 1er mai 2026 (le projet utilise bun/bun.lock, activement maintenu). Son rapport (32 vulnérabilités dont une "critical" sur better-auth) ne reflète pas les dépendances réellement utilisées en prod — à ignorer/considérer non pertinent tant que bun.lock est la source de vérité. (Cause probable de confusion : npm audit fix --force aurait pu écraser des choses sans rapport avec la réalité de l'app.)
### Contexte
Après le fix du nonce CSP (entrée 14:44), le signalant confirme par capture d'écran que le message "La vérification a échoué ou a expiré." s'affiche toujours juste après un captcha résolu avec succès, et que l'utilisateur est quand même redirigé vers Discord OAuth juste après. Le fix précédent (nonce CSP pour l'instrumentation) était nécessaire mais ne couvrait pas cette cause-là, présente elle aussi depuis le début.
### Investigation
### Changements appliqués
### Impact
### Fichiers
- Lecture du code source de cap-widget (node_modules/cap-widget/src/cap.js) : sa méthode disconnectedCallback() (appelée par le navigateur à chaque fois que l'élément <cap-widget> est retiré du DOM) appelle systématiquement this.reset(), qui déclenche un événement custom "reset" — que le retrait soit une vraie expiration ou un simple démontage React normal.
### Contexte
Pendant le fix du captcha ci-dessus, un bun update puis un bun update --latest ont été lancés manuellement dans un terminal, directement dans ~/lbxmb.fr/web (le checkout de production, celui que deploy.sh build et déploie). bun update --latest ignore les contraintes semver de package.json et saute vers les toutes dernières versions, y compris majeures. Résultat : node_modules/bun.lock de prod se sont retrouvés avec des montées de version majeures et non testées juste avant qu'un deploy.sh (lancé pour livrer le fix Cap) ne rebuild et redéploie — livrant donc ces montées de version en prod sans validation :
| Paquet | Avant | Après (non désiré) |
|---|---|---|
| redis | 5.12.1 | 6.2.0 |
| bullmq | 5.81.3 | 6.0.6 |
| sharp | 0.34.5 | 0.35.3 |
| cookie | 1.1.1 | 2.0.1 |
### Contexte
Signalement : au login (bouton Discord), le captcha Cap (self-hosté, cap.lbxmb.fr) se résout visiblement bien côté utilisateur (case cochée / PoW calculé), mais le site affiche quand même l'erreur générique "La vérification a échoué ou a expiré." et bloque la connexion.
### Investigation
### Changements appliqués
### Impact
### Fichiers
- Le widget cap-widget (npm, embarqué dans web/) était en version 0.1.53, la version publiée la plus récente étant 0.1.56.
- Cap Standalone (conteneur Docker tiago2/cap:latest, web/cap/docker-compose.yml) tournait sur une image figée depuis le 24/05/2026 malgré le tag latest (Docker ne re-pull jamais tout seul une image déjà présente localement).
- Cause racine identifiée (indépendante de la version du widget, présente dès la 0.1.53) : Cap Standalone active par défaut un "instrumentation challenge" (vérification anti-bot supplémentaire côté navigateur, en plus du calcul PoW). Ce challenge s'exécute dans une iframe srcdoc avec un <script> inline injecté dynamiquement par le widget. Notre CSP (script-src à base de nonce + 'strict-dynamic', sans 'unsafe-inline') bloque ce script inline car le site n'exposait jamais window.CAP_SCRIPT_NONCE / window.CAP_CSS_NONCE — variables que cap-widget sait utiliser pour propager le nonce CSP courant vers ses scripts injectés (mécanisme documenté, cf. https://trycap.dev/guide/widget.html et issue amont tiagozip/cap#229).
### Contexte
Suite du fix 14:20 : le signalant a testé fix.html plusieurs fois sur son téléphone (confirmé dans les logs : hits successifs à 12:32, 12:34, 12:36, 12:37) et ça rebouclait quand même immédiatement après, avec exactement le même cookie techaro.lol-anubis-auth périmé (~20h, action: CHALLENGE, jamais une session validée) renvoyé à chaque requête. Un vidage manuel via Chrome > "Effacer les données de navigation" n'a pas non plus suffi dans un premier temps.
### Investigation complémentaire
### Changements appliqués
### Impact
### Fichiers
- Version Anubis obsolète : 1.25.0 installée (paquet .deb local, hors dépôt apt) alors que 1.26.2 est disponible, avec des correctifs pertinents en amont (retries/robustesse de la page de challenge sur connexions instables type mobile, COOKIE_HTTP_ONLY, historique de bugs connus sur les doublons de cookies same-name/different-scope confondant le navigateur — issues GitHub TecharoHQ/anubis #520, #902, #1481). Mise à jour effectuée en production : téléchargement du .deb officiel GitHub Releases, dpkg -i, systemctl restart anubis@lbxmb, daemon-reload. Confirmé fonctionnel (anubis --version → 1.26.2) mais n'a pas suffi seul à débloquer le téléphone concerné (nouvelle boucle observée juste après le restart).
### Contexte
Le fix précédent (13:57) empêche le bug pour les nouveaux Service Workers, mais les visiteurs ayant déjà l'ancien Service Worker cassé en cache restaient bloqués : vérifié en live sur le téléphone du signalant (logs Anubis, IP stable 176.141.187.200, boucle de centaines de challenges/minute). Demande explicite : ne pas dépendre d'un vidage manuel des données par chaque membre de la communauté.
### Investigation
### Changements appliqués
### Impact
### Fichiers
- Tentative kill-switch auto (échec) : déployé une version de sw.js qui se désinstalle elle-même à l'activation (self.registration.unregister() + reload des clients), en misant sur la vérification de mise à jour automatique du navigateur à chaque navigation. Surveillance en direct (journalctl -u anubis@lbxmb -f + tail -f access log) pendant que le signalant retestait en boucle sur son téléphone : aucune vérification de mise à jour ne se déclenche tant que la boucle de navigation tourne aussi vite (confirmé sur 90s / ~150 requêtes) → le navigateur ne récupère jamais le nouveau script tant qu'il est piégé. Cette approche est abandonnée.
### Contexte
Signalement : depuis le passage du site derrière Cloudflare Tunnel (cloudflared sur un LXC Proxmox distinct) + Anubis (anti-bot Proof-of-Work), l'écran Anubis "Making sure you're not a bot!" reste bloqué en chargement infini pour une partie des visiteurs. Seul un vidage complet des données du site (cookies + storage + Service Worker) débloque temporairement le problème.
### Investigation
### Changements appliqués
### Impact
### Fichiers
- Vérifié que le flux Anubis backend est sain : simulation complète (résolution du calcul PoW difficulty=2, appel /.within.website/x/cmd/anubis/api/pass-challenge, obtention du cookie techaro.lol-anubis-auth, re-requête sur / sans nouveau challenge) → 100% fonctionnel côté serveur.
### Contexte
Le rôle 1528010127691485205 restait pour certains malgré le log « retiré » (cache stale, auto-rôle permanent qui le redonne au rejoin, ack file même si échec).
### Changements
### Impact
Retrait systématique dès vérif + réparation auto si un cas glisse.
### Fichiers
- Retrait avec force fetch + retries + vérif « encore présent ? »
- File web : ne consomme pas la commande tant que le rôle est encore là
- Rejoin vérifié : retire toujours non-vérifié (plus lié à onboarding_done)
- Auto-rôles permanents : ne redonnent plus non-vérifié aux déjà vérifiés
- Reconcile toutes les 5 min : strip non-vérifié des verified_users
- bot-public/src/handlers/verification/verificationPanel.js
- bot-public/src/handlers/verification/webVerificationQueue.js
### Contexte
Après OAuth vérif, le rôle 1528010127691485205 restait (retrait différé jusqu’à Terminer).
### Changements
### Impact
Dès qu’un user valide la vérif Discord, le rôle non-vérifié est retiré tout de suite.
### Fichiers
- webVerificationQueue : applyRoles: true à nouveau → retire remove_roles (non-vérifié) dès complete_guild_web_verification
- Cleanup one-shot des membres déjà vérifiés qui avaient encore le rôle
- bot-public/src/handlers/verification/webVerificationQueue.js
### Contexte
1. Après validation consoles/notifs, plus d’accès aux catégories Discord.
2. /legal/reglement n’affichait pas le texte.
### Changements
### Impact
### Fichiers
- Cause permissions : bot-site appelait discord.com → Cloudflare 1015 ; listGuildChannels / overwrites échouaient (channelAccess ok: false)
- bot-site → API Canary (https://canary.discord.com/api) comme bot-public
- Scope live des 31 catégories ; IDs mapping confirmés (PS5, Switch 1&2, Xbox One & Series, etc.)
- Fallback résolution par nom de catégorie si ID manquant
- /api/roles retire aussi le rôle non-vérifié avant overwrites
- Page règlement : retrait Framer Motion (opacity:0 SSR) → contenu visible sans hydratation JS
- Terminer / /roles peut à nouveau poser ViewChannel sur catégories + salons console
- Notifs (Annonces, Actu…) = rôles Discord + DM bot (inchangé)
### Contexte
Les membres qui rejoignent sans se vérifier restaient indéfiniment avec le rôle non-vérifié.
### Changements
### Impact
Sans vérif sous 24h → expulsion + possibilité de revenir via l’invite.
### Fichiers
- Scheduler unverifiedKickScheduler : toutes les 5 min, membres avec rôle non-vérifié + pas d’entrée vérifiée (ou verification_revoked) + arrivée ≥ 24h → DM puis kick
- DM : non vérifié → expulsé + lien https://discord.gg/j4nGqnb7yZ
- Config vérif : kick_unverified_enabled / hours / invite (activé sur le serveur LB'XMB)
- Bouton panneau sécurité « Kick 24h : ON/OFF »
- Exemptions : bots, owners bot, owner guild, Admin / Manage Guild
- bot-public/src/handlers/verification/unverifiedKickScheduler.js
- bot-public/src/lib/securityConfig.js
- bot-public/src/events/client/ready.js
### Contexte
Après vérif Discord, « Terminer » (config rôles) renvoyait « Un autre compte a déjà été créé depuis cette adresse IP ».
### Changements
### Impact
La finalisation des rôles ne passe plus par le garde « 1 IP = 1 compte ».
### Fichiers
- checkCommencerIpGate(..., { skipUniqueAccount: true }) sur POST /api/commencer (utilisateur déjà vérifié)
- userKey normalisé (user_id numérique / discord_id) pour l’enregistrement IP
- web/src/lib/ip-tracking.ts
- web/src/app/api/commencer/route.ts
### Contexte
1. Besoin d’une page pour gérer rôles sans refaire /commencer.
2. DM bot sur publications (annonces, actu, giveaway, sondage, sanction, vidéo).
3. Après acceptation remplacement IP : bloqué / IP encore vue comme prise.
### Changements
### Salons → notifs
### Fichiers
- Page /roles + API /api/roles (auth Discord, consoles + 6 notifs)
- Rôles Discord recréés (Annonces/Actualités/Sondages/Sanction/Vidéo) + Giveaway existant
- Handler messageCreate → DM membres du rôle lié au salon
- findOtherAccountWithIp ignore verification_revoked
- Strip IP plus agressif + DM post-accept → https://lbxmb.fr/roles
- annonces 1378803099212447754
- actualité 1348035878920126545
- giveaway 1431225812710981764
- sondage 1509777735604502589
- sanction 1532747804852617216
### Contexte
1. /commencer ip_taken : pas de bouton pour demander le remplacement.
2. Autoriser IP : l’ancien compte devait récupérer non-vérifié ; le nouveau le perdre.
3. « Terminer » : salons/rôles pas toujours appliqués (POST plantait avant Discord sur Truncated DECIMAL).
### Changements
### Impact
Flux remplacement IP actionnable ; Terminer plus robuste pour les accès salons.
### Fichiers
- Bouton « Demander le remplacement de compte » sur /commencer (ip_taken)
- OAuth : plus d’auto-queue ; demande uniquement via le bouton
- Autoriser : nouveau = retire non-vérifié ; ancien = redonne non-vérifié + verification_revoked
- /api/commencer POST : Discord (rôles/salons) avant SQL ; UPDATE via discord_id ; retries channel-access
- web/src/app/commencer/page.tsx
- web/src/app/api/commencer/route.ts
### Contexte
Les échecs ip_taken apparaissaient dans /vérification mais sans demande pending → pas de boutons. Cause : callback OAuth live (verify-oauth-callback.cjs) ne créait pas d’entrée dans ip-share-requests.json.
### Changements
### Impact
Staff peut traiter les remplacements IP depuis /vérification → menu Remplacer le compte.
### Fichiers
- CJS OAuth : queueIpShareReplaceRequest à chaque ip_taken (+ notif échec avec other_id)
- Section « Remplacer le compte » : boutons Autoriser / Refuser
- Après action : panneau rafraîchi (reste sur la section) + bannière résultat
- Backfill pending depuis les échecs ip_taken récents (résolution other via config serveur)
- web/lib/verify-oauth-callback.cjs
- bot-public/src/commands/moderation/verification.js
- bot-public/src/handlers/verification/verificationPanel.js
- bot-public/src/handlers/verification/webVerificationQueue.js
### Contexte
Container V2 unique trop dense (réussites + échecs + IP + ISP + blobs enc:v1).
### Changements
### Impact
UX admin lisible ; IP déchiffrées pour le staff.
### Fichiers
- /vérification : menu StringSelect (Réussites / Échecs / Remplacer compte / Ajouter opérateur)
- Une section à la fois + boutons d’action dans la section
- IP admin affichées en clair via revealIp (plus de blob chiffré)
- Handler verify:admin:nav dans interactionCreate
- bot-public/src/commands/moderation/verification.js
- bot-public/src/events/interaction/interactionCreate.js
### Contexte
Liste multi-sujets : acceptation légale + règlement, IPs chiffrées, guides README GitHub, hero 2 lignes, faux erreur captcha, suppression RGPD, cache permissions, erreur MySQL DECIMAL à la vérif.
### Changements
1. Vérif : version cookie légal 2026-08-02 + rappel règlement dans les checkboxes ; page règlement à jour
2. Mentions légales : section AES-256-GCM / HMAC pour IPs ; /legal/rgpd → redirect mentions ; liens Footer/Login/CGU nettoyés
3. Guides : GET renvoie markdown/github ; guideId alias create ; fix JSX + icône GitHub ; import README inchangé mais affichage réparé
4. Hero : LA RÉFÉRENCE / DU MODDING en 2 lignes (toujours flex-col)
5. Cap : ignore erreurs post-solve ; Anubis COOKIE_PARTITIONED=false
6. Auth cache : staleTime:0 + refetch focus ; dashboard re-check 60s/focus
7. DECIMAL : users-lookup.ts + commencer/auth/me ; fallback auth-helpers ne met plus d’UUID dans user_id
### Contexte
Vérif refusée isp_denied pour « Cogeco Connexion inc » (Québec). Demandes pending dans isp-requests.json.
### Changements
### Impact
Match Cogeco Connexion inc → OK (CA). Relancer la vérif Discord devrait passer côté opérateur.
### Fichiers
- Ajout Canada → Cogeco Connexion (/\bcogeco\b/i) dans web/lib/allowed-isps.cjs
- 2 demandes ISP Cogeco marquées approved
- pm2 restart web-prod (reload du module CJS)
- web/lib/allowed-isps.cjs
- data/verify/isp-requests.json
### Contexte
Token tunnel fourni ; sortie via IP Proxmox .62 bloquée en HTTPS → default route basculée sur LAN 192.168.1.1.
### Changements
### Impact
Tunnel Healthy côté connecteur. Reste à publier les hostnames → http://127.0.0.1:8080 et supprimer les A vers .62.
### Note
Default route LAN non persistée au reboot (à figer dans la conf réseau CT).
- Installé cloudflared 2026.7.3 (deb GitHub)
- Service systemd cloudflared enabled + running
- Tunnel ID cb79de95-… connecté (cdg*, QUIC OK)
- Warning temporaire : pas encore d’ingress dashboard → 503 jusqu’aux Published application routes
### Contexte
discord.com renvoie Error 1015 (rate-limit CF) depuis l’IP FAI → bot offline + login OAuth invalid_code. canary.discord.com / ptb.discord.com répondent 200.
### Changements
### Impact
### Fichiers
- bot-public : REST https://canary.discord.com/api (src/lib/discordApi.js, Client principal + secondaire + deploy-commands)
- web : rewrite fetch dans server.js (Better Auth) + URLs canary dans libs/routes Discord + instrumentation.ts + CSP
- Override possible : DISCORD_API_BASE
- bot-public reconnecté : Connecté en tant que LB'XMB.FR#3591 (10 guilds), health ready:true
- Login Discord site : échange token via canary (patch runtime server.js)
- bot-public/src/{index.js,deploy-commands.js,lib/discordApi.js,lib/secondaryBot.js}
- web/server.js, web/src/instrumentation.ts, web/src/lib/discord-api-base.ts, libs/routes Discord, web/lib/verify-oauth-callback.cjs
### Contexte
Objectif : exposer la CT via cloudflared (LAN) sans dépendre de l’IP tunnel Proxmox 151.240.100.62.
### Changements appliqués
### Impact
### Fichiers
- nginx : écoute locale 127.0.0.1:8080 (HTTP) en plus du 4443 PROXY protocol — origine pour le tunnel
- map X-Forwarded-Proto pour respecter le proto Cloudflare Tunnel
- Fichiers : infra/cloudflared/config.example.yml, install-from-token.sh
- Bloqueur : pas d’HTTPS sortant depuis la CT → binaire cloudflared non téléchargeable pour l’instant
- Origine tunnel prête : http://127.0.0.1:8080
- Reste : installer le binaire + token Zero Trust + DNS CNAME tunnel (pas d’A vers .62)
- /etc/nginx/sites-enabled/lbxmb.fr, actp, game, pterodactyl
- infra/cloudflared/*
### Contexte / problème
Le proxy Cloudflare Free bloque les requêtes > ~100 Mo. Les uploads (cloud, ZIP ressources, staff-chat, etc.) partaient en un seul POST multipart et échouaient au-delà. Pas de tunnel cloudflared (stack nginx + orange cloud conservée).
### Changements appliqués
### Impact visible / technique
### Fichiers touchés
- Protocole chunks partagé : POST /api/uploads/chunks (init) → PUT …/[id] (Content-Range) → POST …/complete → champ chunkUploadId / chunkUploadId_* sur l’endpoint métier.
- Taille chunk : 95 MiB ; TTL sessions 24h sous uploads/_chunks/.
- Helper serveur resolveFormUpload / expandChunkFieldsInFormData branché sur cloud, ressources, staff-chat, shop, guides, tickets, profil, chat, forum, projects, vendor, shared-games, etc.
- Helper client chunked-upload-client.ts (uploadFileWithChunks, prepareFormDataWithChunks) branché sur les UIs d’upload.
### Contexte
Tunnel global (autre CT) pointe vers http://192.168.1.112:8080 → Cloudflare 502 car nginx n’écoutait que 127.0.0.1:8080 (connection refused depuis le LAN).
### Changements
### Impact
### Fichiers
- Remplacé listen 127.0.0.1:8080 → listen 8080 (0.0.0.0) dans sites-enabled : lbxmb.fr, actp, game, pterodactyl (+ copies sites-available)
- Backups déplacés hors de sites-enabled (/etc/nginx/backups-lan8080/) pour éviter duplicate upstream
- systemctl restart nginx (reload insuffisant pour changer le bind)
- Doc exemple tunnel : origine LAN 192.168.1.112:8080
- ss : 0.0.0.0:8080 LISTEN
- http://192.168.1.112:8080 joignable ; https://lbxmb.fr → HTTP 200 via Cloudflare
- Curl sans CF-Connecting-IP → Anubis 500 (attendu) ; avec header CF → 200
- /etc/nginx/sites-enabled/{lbxmb.fr,actp.lbxmb.fr.conf,game.lbxmb.fr.conf,pterodactyl.conf}
### 1. Chiffrement IPs at-rest (AES-256-GCM)
### 2. Compte désactivé (libc + flux contestation)
### 3. Quantité boutique
### 4. Responsive ≤1340px = mobile
### 5–6. Guides : chapitrage latéral + icônes OS
### 7. Upload 413 (fichiers ~190 Mo)
- Passphrase IP_ENCRYPTION_PASSPHRASE (web + bot)
- Format enc:v1:… + ip_hash HMAC pour lookups
- Migration : user_ips / ip_visits / ip_intel / JSON guildes
- Admin : spoiler masqué + bouton « Déchiffrer / révéler »
- Bot /account : déchiffre puis spoiler Discord ||ip||
- Réactivation desactive=0 pour libc.prx
- Notifications vérif en DM trop bruyantes
- Après « Terminer » onboarding, ViewChannel user non appliqué sur catégories/salons
- Stop DM admin / demandeur (succès, échecs, IP replace, ISP)
- Journal web/data/verify/admin-inbox.json + lecture pending IP/ISP
- Commande slash /vérification (éphémère Components V2) : réussites, échecs, boutons Remplacer/Refuser & Ajouter opérateur
- Sanitize audit reason latin1 dans discordRest.js
- Restart bot-site / bot-public
- Callback .cjs : lit next du state ; succès → /commencer?step=prefs&verified=1 (ou next si fourni)
- Miroir TS + filet client VerifyClient : redirect auto vers /commencer si verified=1
- Restart web-prod pour charger le .cjs
- Toujours enfiler ip_share_request si absent de la file (même pending existant)
- Un seul DM admin pour IP prise (refus + Remplacer/Refuser), plus l’alerte séparée
- Relance manuelle de la demande pending → DM envoyé
- « Me vérifier » → Discord OAuth direct (plus de /verify intermédiaire)
- Erreurs OAuth → retour /commencer (si onboarding)
- IP déjà liée : demande auto + DM uniquement 1139951514694193323 avec Remplacer / Refuser
- Opérateur : 1 bouton « Ajouter mon opérateur » → DM admin Ajouter l’opérateur / Refuser
- Alertes refus ISP/IP : plus de DM spam au demandeur ni aux autres owners
- Plus de court-circuit onboardingDone → done
- Connecté sans verified=1 → étape vérification obligatoire (IP/ISP)
- Prefs uniquement après retour OAuth vérif (verified=1)
- Callback login Discord → ?step=verify (plus step=prefs)
- Remplacement par invite permanente active j4nGqnb7yZ (salon accéder-au-serveur)
- Fallbacks + NEXT_PUBLIC_DISCORD_INVITE_URL mis à jour
### Sous-entrée A — Panneau Discord
### Sous-entrée B — Rôles différés jusqu’à Terminer
### Sous-entrée C — Parcours web /commencer
- Remplacement du panneau « Me vérifier » (méthode web) par accueil Commencer (lien /commencer) + Traduire (LibreTranslate + ?lang= sur le lien)
- Style bouton Commencer = Link (contrainte Discord URL)
- applyMemberVerification(..., { applyRoles: false }) après vérif web / IP-share
- Restore au rejoin : rôles seulement si verified_users[].onboarding_done === true
- Phases : landing → login Discord → needVerify → prefs (consoles par marque + Annonces/Actualités/Giveaway/Sondages) → Terminer
- OAuth vérif accepte next (retour /commencer?step=prefs)
- POST Terminer : retire non vérifié 1528010127691485205, assigne notifs (dont giveaway 1533097995015553126), ViewChannel catégories console, marque onboarding_done
- Entrées TN Tunisie Telecom + XX Starlink (SpaceX) dans BASE_ALLOWED_ISPS
- Labels UI : Tunisie / Autres · Satellite
- Upsert account (access/refresh/scope) dès l’échange OAuth vérif
- Lien better_auth_user_id à la création compte site
- Message d’erreur join plus clair + refresh avant check scope
- INTERNAL_API_KEY du bot ≠ secret attendu par web-prod → HTTP 401 « Non autorisé »
- BOT_SITE_URL pointait vers http://127.0.0.1:8787 (bot-site) au lieu de https://lbxmb.fr
- Alignement secrets bot ↔ web ; BOT_SITE_URL=https://lbxmb.fr
- account.js / friends.js : résolution URL qui ignore localhost ; priorité BOT_INTERNAL_SECRET
- parseOAuthScopes / oauthScopeHas (virgules + espaces)
- Checks relationships.* et guilds.join corrigés
- Refresh token plus robuste si access token absent/expiré
- Patterns Orange FR : france télécom / wanadoo
- ip-api vérif : champs org, as, asname (AS3215 Orange S.A.)
- DM refus utilisateur : plus d’IP ni d’ISP (staff conserve IP/ISP)
- DM succès staff : affiche l’opérateur whitelisté (isp_match)
- Redirect OAuth isp_denied (et ip_taken / blacklist) inclut ip=
- Page /verify : IP affichée soulignée sous le message « Opérateur non autorisé »
- File bot verify_attempt_notify → DM staff (owner) + DM à l’utilisateur ayant tenté
1. Page /verify affichait -51 membres (Discord approximate_member_count négatif)
2. Retrait de l'appli Discord / rejoin annulait de facto la vérif (rôles non réappliqués)
3. /account sans actions rapides staff
### 1. Compteur membres
### 2. Persistance vérification
### 3. /account — actions rapides
- Au guildMemberAdd, si présent dans verified_users → applyMemberVerification (rôles restaurés)
- verified_at conservé au restore
- Dissociation OAuth site Discord ne nullifie plus discord_id / discord_verified
1. Créer un compte site à chaque vérif (si absent)
2. Rôle 1528010127691485205 ne doit plus voir aucun salon
3. /account / vérif ne doivent plus accepter/afficher 127.0.0.1
- ensureSiteAccount dans le callback TS (CJS l’avait déjà) — create-if-missing
- Vérif refusée si IP non publique (loopback/privée) — CJS + TS
- mergeIpHistory ignore les IPs privées/loopback
- Nettoyage JSON : 39 entrées verified_users sans 127.0.0.1
- /account filtre les IPs junk
- Sync overwrites : rôle non vérifié → ViewChannel: false sur tous les salons (appliqué immédiatement sur LBXMB)
### 1. /account
### 2. Sync IPs vérif ↔ site
### 3. Translate panneau Discord
### 4. Config vérif — rôles + salons
### 5. IP déjà utilisée — coexistence
- Défaut DISCORD_INVITE_URL → https://discord.gg/hZH5jCszN (web/src/lib/discord.ts)
- Fallback apps/services → même URL (web/src/lib/applications-links.ts)
- NEXT_PUBLIC_DISCORD_INVITE_URL ajouté dans web/.env.local
- Fallback bot-site (config.js, activity.js, .env.example) alignés
- Remplacement in-place dans .next-staging + restart web-prod / bot-site
- Branchement cfg:sec:joins, cfg:sec:jg:toggle, cfg:sec:jg:edit dans routerSecurity.js
- Modal seuils cfg:sec:jg:modal:joins + submit dans securityModals.js
- Role select exempt cfg:sec:jg:exempt:roles reconnu et persisté via patchRegularization
1. Campagne « Je veux rester » : garder les ~200 keepers, expulser le reste pour inactivité
2. Vérification Discord : ne plus se fier au résidentiel ; whitelist FAI mondiale + 1 IP / compte
### 1. Warn-kick — expulsion forcée
### 2. Vérification — ISP only + IP
### Bilan kick (fin de pass)
- expiresAt forcé dans le passé sur la tâche active (wk_5da664af…)
- Pass d’expiration lancé (DM + kick hors keepers / bots / owners)
- Raison kick + DM : « Vous avez été expulsé pour inactivité. Vous auriez dû appuyer sur le bouton Je veux rester pour ne pas être expulsé. »
- Whitelist FAI 24 pays (Orange/SFR/Bouygues/Free FR, opérateurs DE/UK/ES/IT/BE/NL/CH/PT/PL/US/CA/MX/BR/CN/IN/JP/KR/ID/MA/EG/ZA/AU/TR…)
- Hors liste → vérification refusée (reason=isp_denied)
- 1 IP = 1 compte Discord ; conflit → reason=ip_taken
- Re-vérif : ajoute l’IP (max 2 IPs distinctes, pas de remplacement aveugle)
1. Blacklist sans MP → pas de bouton Contester
2. /signalement : bouton Whitelist à la place d'Ignorer ; besoin Sanctionner / Ignorer
3. /config : section Activité du serveur (régularisation)
4. Anti-raid : boucle leave/rejoin
### 1. Blacklist — DM avant ban
### 2. /signalement — Sanctionner / Ignorer
### 3. /config → Activité du serveur
### 4. Anti leave/rejoin
- Boutons Sanctionner + Ignorer (plus Whitelist)
- Menu : Avertissement (raison) ou Blacklist (durée vide = permanente + raison)
- MP pour warn et blacklist ; ignore = ferme uniquement le signalement
- Hub conditions (offline, messages, réactions, interactions, vocal min), délais, grâce, invite, rôles exemptés
- Garde d'arrivées (plafond + rafale → kick + MP + invite)
- bot-site lit la config guilde ; délai expulsion_delay_days avant kick
- DM ban/kick/to/warn/mute/blacklist en Components V2 (container + thumbnail serveur, accent #a0000a)
- Bouton Contester (1×) sur ban, warn, blacklist uniquement → owner serveur (ban/warn) ; blacklist = stocké sans MP owners
- /blacklist sans options → panneau : Ajouter / Retirer / Liste / Contestation
- Blacklistés peuvent toujours cliquer Contester malgré le gate global
1. saveAutoRolesOnLeave existait mais n’était jamais appelé au GuildMemberRemove
2. Au leave, Discord n’envoie plus nick/rôles si le membre n’est pas en cache → sauvegarde vide
- Wiring saveAutoRolesOnLeave + saveNickOnLeave sur leave
- Store member-snapshots mis à jour à chaque guildMemberUpdate / join
- Warm snapshots au démarrage (guild.members.fetch) si restauration active
- Leave : fallback snapshot si cache vide
- Logs role-restore / nick-restore leave+join
1. /to plantait : Cannot access 'contestable' before initialization
2. Besoin d’une option config pour restaurer le pseudo serveur après départ/retour
- sanctionNotify.js : contestable déclaré avant usage
- Pseudos auto : option hub Restaurer le pseudo au retour (restore_nickname_on_rejoin)
- Au GuildMemberRemove : sauvegarde du nickname serveur
- Au retour : restauration prioritaire (sinon règles permanentes/temporaires)
- Plus de save incorrect à l’arrivée après application d’un format
- DM envoyés en embed + composants : Translate et Je veux rester
- Relance file pour non-keepers (nouveau format)
- Fermeture de la campagne invalide (mauvais salon)
- Lien DM forcé vers 1528633918280962179
- File DM reconstruites : 1216 membres (78 keepers exclus)
- Panneau renvoyé avec Translate + Je veux rester (msg 1529227086114459850)
- API POST /warn-kick/panel et POST /warn-kick/resend-dms
- Skip keepers dans la campagne DM + lock anti double-rebuild
- Flux : landing → Discord → toujours écran onboarding (consoles + notifs) → succès seulement après « Terminer la configuration »
- Plus de skip via onboardingDone en base
- Retry session si cookie OAuth pas prêt (step=prefs)
- Copie onboarding claire (« Choisis tes rôles ») ; succès = « Configuration terminée » + lien modifier
### 1. Blacklist / signalements
### 2. /commencer — accès salons par catégories
### 3. Panneau Commencer Discord
- guildMemberAdd : re-ban immédiat si isBlacklisted
- Signalements : liste filtrée status=open ; bouton Whitelist ferme les reports ouverts
- Blacklist depuis report : deferReply, propagation relations, ferme reports ouverts, meilleurs détails d’erreur ban
- /blacklist remove + contestation acceptée : ferment aussi les reports ouverts
- Log si échec save() blacklist
- Mapping console → category ID (commencer-categories.ts / commencerCategories.js)
- bot-site POST /commencer/channel-access : overwrites membre (View + ReadHistory + Connect) sur catégorie + salons enfants
- API web POST /api/commencer appelle channel-access après assignation rôles
- Locale fr : réponse éphemère « Ce message est déjà en français. » (pas d’appel LibreTranslate)
- Autres locales : message d’erreur clair si traduction KO ; plus de content: null
- Même correctif sur le panneau Commencer
- Sur chaque keep : deny ViewChannel utilisateur sur le salon (pas @everyone)
- Sync rétroactive des keepers déjà inscrits (~60)
- API POST /warn-kick/keepers + POST /warn-kick/sync-hide
- Commande Discord /warn-kick-list (staff autorisé)
- Message keep : confirmation + salon masqué
- Landing accueillante inchangée → après OAuth : étape prefs (consoles + notifs) → succès
- getClientIps / normalizeIp renforcés (IPv6 brackets, XFF split)
- POST /api/commencer : recordIpVisits sur toutes les IP détectées (is_registration_ip)
- Création compte (auth-helpers) : IP enregistrée en registration
- Nouveau POST /api/ip-beacon + appel auto dès l’étape prefs
- Stream : proxy_protocol on vers backends nginx (4443/4444/4446)
- Strip PROXY avant coturn (13478/15349)
- listen … proxy_protocol sur les vhosts HTTPS
- Map $real_client_ip : CF-Connecting-IP si présent, sinon $proxy_protocol_addr
- deploy.sh OK — BUILD_ID VYQmMg2D-Z1Oej6ciZegt ; web-prod redémarré
- Landing chaleureuse (fonds verts/ciel, logo, animations douces) + message d’accueil
- Explication claire : intégration simple au serveur + vérification anti-abus
- Suppression de l’UI consoles / notifications / rôles
- Après OAuth Discord : finalisation auto via POST (rôle membre si configuré)
- Écran succès + lien invite Discord réel
- bash deploy.sh : build Next → swap .next-staging → pm2 restart web-prod --update-env
- BUILD_ID GjriZ843iuwRG-rEez0Ih ; 5 workers cluster online
- Suppression de l’auto-redirection Discord au chargement
- Landing d’accueil avec présentation LB'XMB.FR + bouton Commencer (OAuth au clic)
- Layout élargi (max-w-5xl), plus aéré ; grille consoles 3 colonnes en prefs
- getPhotoUrl renforcé + isLocalUploadPhoto / buildDiscordAvatarUrl
- OAuth Discord : mapProfileToUser pose name + image CDN ; sync photo sans écraser Profils/ ; sync aussi sur liaison Discord legacy
- /api/auth/me : COALESCE users.photo → Better Auth user.image + heal si vide
- APIs online / featured-home : photos normalisées via getPhotoUrl
- UI chat/forum/projects/vocal/dashboard/donate : plus de /uploads/ brut pour les avatars
- nginx : rewrite /uploads/ → /api/uploads/ (prod + ptb)
- Heal DB : images Better Auth resynchronisées depuis users.photo quand vides
- Deploy web-prod
- /login → callback / (plus de double OAuth via discord-complete)
- discord-complete : simple redirect vers next ou / (compat)
- User Install bot : retour /discord/bot?installed=1 via cookie API
- /commencer : inchangé (/commencer?step=prefs après Discord)
- Verify : déjà /verify/{guild}?verified=1 (page succès)
- Login Better Auth : integration_type=1 forcé, suppression de guild_id / permissions
- buildDiscordAuthorizeUrl : toujours User Install, jamais de guild_id
- Verify + link : alignés sur le même flux compte
- Défaut BOT_SITE_URL → http://127.0.0.1:8787 dans warn-kick.js et warnKickPanel.js
- Ajout BOT_SITE_URL + INTERNAL_API_KEY dans bot-public/.env ; restart --update-env
- warn-kick ajouté à SKIP_GUILD_ACCESS (contourne le garde mod générique)
- Accès autorisé si utilisateur 1139951514694193323 ou rôle 1232966884375265281 (bot-public + bot-site)
- Config serveur LB'XMB : permissions warn-kick avec ce rôle + loupblanke en user
- /warn-kick n'utilise plus modSlash : commande standalone avec contrôle uniquement sur l'ID 1139951514694193323
- Commande /warn-kick et API POST /warn-kick/start : réservées à l’ID 1139951514694193323
- Campagne DM d'avertissement persistante (warnKickDmCampaign.js) : 1 DM / 2 min, pause 20 min tous les 30 DM
- État campagne stocké dans state.json (queue, index, batch) — survit aux redémarrages
- Message DM d'avertissement distinct du message d'expulsion finale
- warn-kick handler Translate : suppression de components dans la réponse traduite (l’embed reste traduit, le message original conserve les vrais boutons).
- Ajout de warn-kick dans bot-public/src/lib/serverConfig.js (DEFAULT_COMMANDS) pour qu’elle soit synchronisée sur les guilds.
- warn-kick handler Translate : édition du message en conservant components (boutons restent visibles/clickables).
- Scheduler bot-site /warn-kick passe de 1h à 10 min (exécution plus rapide après expiresAt).
- Groupes UI explicites : Nintendo, PlayStation, Xbox, Autres (liste basée sur RESOURCE_FORM_CONSOLES)
- Ajout d’icônes console via imageId (/api/consoles/images/<imageId>.png)
- Auto-redirection OAuth Discord dès que /api/commencer renvoie authenticated=false (sauf cas error=oauth)
- BetterAuth (login Discord) : ajout de guild_id dans l’URL OAuth générée
- User Install : ajout de guild_id dans les URLs integration_type=1
- Création de la commande /warn-kick (bot-public) : envoie un message avec boutons Je veux rester (vert) + Translate
- Translate (bot-public) : traduit le contenu de l’embed via LibreTranslate selon la locale Discord (bouton non expirant)
- Etat persistant (bot-site) : stockage sur data/warn-kick/state.json des tâches + liste keepers + expiration
- Scheduler (bot-site) : toutes les heures, kick des non-keepers à expiresAt, avec DM explicatif (lien vers le salon de commande)
- Clic Je veux rester : envoi au bot-site pour ajouter l’utilisateur à la liste ; période fermée ensuite
- Login Discord uniquement ; providers Google/GitHub retirés de Better Auth
- Section « Comptes associés » retirée du profil
- Soft-delete de 49 comptes sans discord_id
- Scopes unifiés : identify guilds.join relationships.read applications.commands (messages.read exclu)
- Page /commencer + API : intro → OAuth → consoles/notifs → rôles Discord
- Garde IP : VPN/proxy/hosting/mobile + whitelist Orange/Free/Bouygues/SFR + 1 IP = 1 compte
- Panneau salon 1528633918280962179 (Translate + lien Commencer) via bot-site ; handler Translate dans bot-public
- Catégorie Online ajoutée pour Nintendo Switch et Switch 2
- Jeu optionnel pour les mods (plus de validation bloquante)
- Sélection multi-jeux (jusqu’à 12) : structure.json.jeux + colonne jeux_json, jeu = premier pour chemins/compat
- Filtres / sidebar / détail tiennent compte de tous les jeux liés
- Parseur d’URL : hosts arbitraires, suffixe /releases ignoré, page org (1 segment) résolue via API
- Client releases : GitHub + Gitea/Forgejo (/api/v1), User-Agent anti-Anubis
- Sync / autofill / téléchargement d’assets branchés sur ce client
- Libellés UI FR/EN mis à jour ; alerte si dépôt sans assets de release
- resolveVoiceBot : si le principal est occupé ailleurs (musique/radio/record) → secondaire ; si les deux occupés → busy + message
- connect musique/radio : refuse de réutiliser une connexion d’un autre salon
- /play, /playlist, /radio, lecteur : refus explicite si bots déjà utilisés
- /skip + bouton ⏭️ : vote majoritaire des humains du vocal (skip immédiat si seul)
- Sync télécharge les assets puis réécrit variantes via buildVariantesFromGithubAssets (chemins locaux sous releases/{tag}/)
- Noms d’affichage telechargements = nom d’asset courant (plus de nom figé)
- Grille 2 lignes × 4 boutons (Modifier · Limite · Invité · Expulsé / Accès · Perms · Transférer · Récupérer)
- ➕ Invité = vert (Success), ➖ Expulsé = rouge (Danger), Transférer + Claim = bleu (Primary)
- Cookies Better Auth crossSubDomainCookies domain .lbxmb.fr
- Nginx : www.lbxmb.fr → 301 https://lbxmb.fr
- Proxy Next + composant client : redirection www → apex
- Message login : affiche aussi state_mismatch s’il est dans l’URL
- Asset assets/tv-legend.png (et .jpg) remplacé par l’image fournie.
- Boutons Discord réordonnés en grille 4×2 alignée sur la légende (✉️ pour Transférer).
- Intervalle scan 2,5 s (premier scan ~0,8 s).
- list + kick sur une seule session RCON.
- Messages Discord non bloquants ; cache des guilds whitelist ON.
- Templates ipwhitelist / whitelist+{ip} / mcipwhitelist → stockent dans la DB bot (addMcIpWhitelist), sans commande RCON.
- Config serveur mise à jour + entrée .Jessybarden0710 / 88.163.181.14 migrée.
- Placeholder /config custom boutons : mcipwhitelist add {pseudo} {ip}.
### Fix : config minecraft ignorée au chargement
- Scan RCON list à l'activation + toutes les 20s si whitelist ON → kick les pseudos non autorisés.
- Message console Discord pour chaque kick.
- Flag minecraft.ip_whitelist_enabled (défaut OFF) — toggle dans /config → Minecraft (Whitelist IP : ON/OFF) et /mcwhitelist.
- Autorisation si IP ou pseudo listé ; sinon kick Vous n'êtes pas whitelist discord.gg/lbcs.
- Parse des lignes console (Steve[/ip:port] logged in…) via API /internal/minecraft/console + messages du salon console Discord → kick RCON.
- Étape 1 login : identify + relationships.write + guilds.join + messages.read (sans integration_type).
- Après session → /auth/discord-complete → User Install applications.commands + integration_type=1 (MP + créer commandes).
- Verify : scopes login (token user), plus User Install seul.
- Imports corrigés vers ../../lib/....
- /friends ajouté aux PUBLIC_COMMANDS.
- Bot redémarré (API /health OK, 8 guilds).
1. Redirect erreur utilisait request.url origin → https://0.0.0.0:25581 (hostname d'écoute Next).
2. Scopes User Install (messages.read + integration_type=1 + relationships.write) → invalid_scope.
- Redirects OAuth → toujours BETTER_AUTH_URL / https://lbxmb.fr.
- Login : identify + relationships.write + guilds.join sans integration_type.
- User Install bot : identify + applications.commands + integration_type=1 (séparé).
### Whitelist IP Minecraft (par serveur Discord)
### Miroir console Minecraft → Discord
### Vérification — overwrites salons
### Tickets — séparateur ・
### /friends
### Connexion cassée / callbacks Discord
- Scopes User Install unifiés (comme le générateur Discord) : identify + applications.commands + messages.read + guilds.join + relationships.write + integration_type=1.
- Login BetterAuth → https://lbxmb.fr/api/auth/callback/discord (redirectURI explicite).
- Vérification → https://lbxmb.fr/api/verify/oauth/callback (authorize + token exchange).
- Erreurs Discord (invalid_scope, etc.) redirigées vers /login?error=oauth avec error_description (évite « State not found » opaque).
- HEAD sur callback auth → 200 (sondes portail Discord).
### /config cassé
### Logs — bucket Tickets
- Nouveau bucket tickets + événements : ouverture, prise en charge, staff ajouté, fermeture, verrouillage, réouverture, transcript (fichier HTML joint).
- sendGuildLog accepte des pièces jointes.
- « Créer la structure » crée seulement les salons/posts manquants (ex. salon Tickets) sans tout recréer.
### Connexion API/RCON — liste de providers
### Commandes dédiées /mcwhitelist et /mc-player
### Salon console bidirectionnel
### Infos + Paramètres serveur
- Sync guild uniquement si RCON Minecraft est prêt (activé + provider + identifiants).
- /mcwhitelist : modal pseudo + IP optionnelle *(plugin ipwhitelist)*.
- /mc-player : liste en ligne → panneau joueur (cœurs, faim, XP, inventaire, kick/ban/clear, gamemode, TP joueur ou coords).
### /config → Connexion API / RCON
### Commandes custom — boutons modal → RCON
- Nouvelle section home « Connexion API/RCON » : activer, éditer hôte/port/mdp, tester, salon journal, joueurs, infos (seed/temps/list), exécuter une commande console.
- Client rcon-client ; sorties optionnellement miroirées dans un salon Discord (pas de stream console live — RCON = requête/réponse).
- Éditeur commande : format simple/container/embed, Liste RCON (rcon_prefetch → {rcon_result}), Ajouter bouton (label, titre modal, 1–2 champs, templates RCON une ligne = une commande).
- Runtime : boutons ccmd:act: ouvrent un modal ; chaque ligne RCON ne s’exécute que si ses {variables} sont remplies (ex. pseudo seul → whitelist add, IP seule → ipwhitelist add).
### Vocal temporaire — image légende utilisateur
### Logs — UX structure
- Instructions en 4 étapes + bouton dédié « Créer la catégorie + salons » / « Créer le forum + posts ».
- setupLogsStructure active automatiquement les logs si besoin.
- Affiche les salons/posts liés par catégorie une fois créés.
### Légende vocal temporaire — emojis nets
### Logs serveur — structure catégorie / forum
- Schéma logs aligné Python : type text|forum, enabled_types, channels / threads, category_id / forum_id.
- UI : Activer · bascule Mode · Créer la structure · toggles catégories + événements fins.
- sendGuildLog route vers le salon/thread du bucket (join, message, vocal, server, moderation, verification, antiraid) ; fallback legacy channel_id.
### Retours /config → accueil unifié
### Logs — échec d'interaction
### Légende vocaux temporaires
- Tous les hubs (admin, logs, notifs, profil, rôles, etc.) → cfg:nav:home (nouveau panneau select).
- Suppression page Admin catégorie + menu « Changer de catégorie » + ancien panels.js inutilisé.
- Anciens liens nav:page:* / home:nav redirigés vers l'accueil.
### 1. /config — panneau d'accueil unifié
### 2. /signaler + menus contextuels unifiés
### 3. Panneau vocaux temporaires
### 4. Sécurité — pause invitations & MP inconnus
- Accueil = container accent coral (#ff5e5b) + texte « Panneau de Configuration » + select « Configurer une option ».
- 15 options (messages, rôles, pseudos, traduction, threads, tempvoice, tickets, stats, réactions, inter-salon, logs, protection, notifications, commandes, profil bot) avec descriptions corrigées.
- Routing cfg:home:feature vers les panels existants.
- Fix bug c → container sur Giveaways/Logs de la page Admin.
- Modal unique (Labels V2) : select raison + description (obligatoire si « Autre ») + File Upload (jusqu'à 5 fichiers).
- /signaler, App → Signaler (user) et Signaler (message) utilisent le même modal.
- Oui : Discord permet désormais d'importer des pièces jointes dans un modal (FileUpload).
### Cause : crash bot-public au démarrage
- Fix SyntaxError: Export 'WELCOME_VARIABLE_HELP' is not defined dans panelsV2.js (ré-export sans import).
- Restauration DISCORD_TOKEN vide dans .env racine (cassait aussi bot-python / API Discord site) + sync bot/config.json.
- start_bot.sh : ignore un DISCORD_TOKEN vide pour ne pas masquer config.json.
- pm2 restart bot-public + pm2 save (autostart reboot déjà via pm2-root.service).
- bot-python laissé stopped (même application Discord que bot-public → conflit de session).
### Mode de fermeture configurable
- Option À la fermeture dans /config → Tickets : supprimer (comportement actuel) ou verrouiller (lecture seule).
- Mode verrouiller : retire l'écriture pour tout le monde, conserve le salon/thread.
- Bouton Fermer devient Réouvrir quand le ticket est verrouillé.
- Guard messages bloque toute écriture sur ticket verrouillé.
- Un ticket verrouillé ne bloque plus l'ouverture d'un nouveau ticket par le même membre.
### Durée / fin de sondage requise
- Champ Durée obligatoire dans la modale « Question / description » (30m, 2h, 1d…).
- Publication bloquée sans durée valide (min. 1 min, max. 30 jours).
- Affichage de la fin sur le sondage publié + clôture automatique à l'échéance.
- Reprise des timers au redémarrage du bot.
### Éditeur tickets ne répondait plus
- Boutons compacts (2 rangées) à la place de 7 sections lourdes.
- Sous-page Types / menus pour supprimer des types et gérer les menus section.
### Republication panneau tickets à la restauration
### Statistiques serveur dynamiques (rôles / membres)
### Paramètres communauté dans la backup
### Config /config adaptée au nouveau serveur
- Comptage via role.members.size.
- Remap role_id, channel_id et placeholders {role.member.count:ID} dans les templates.
- Reset last_applied à la restore ; recalcul forcé (force: true).
### Crash à la création (reading 'cache')
### Ordre des rôles inversé à la restauration
- Filtre des salons sauvegardables (catégories, texte, vocal, forum… — pas les threads).
- Sérialisation overwrites avec permissionOverwrites?.cache.
- Fetch salons + garde-fous membres / @everyone.
- Création des rôles du plus haut au plus bas (hierarchy_rank desc).
- Réordonnancement via hierarchy_rank relatif (bas → position 1, haut → sous le bot).
### Tickets : boutons complémentaires activables
### Signaler un message (menu contextuel)
- Prise en charge et Ajouter un staff désactivés par défaut — à activer dans /config → Tickets.
- Toggle séparé pour « Ajouter un staff » (nécessite prise en charge activée).
- Clic droit sur un message → Signaler → modale (contexte obligatoire).
- /signalement affiche : contenu intégral, auteur, signaleur, lien, PJ, explication.
- Loader commandes corrigé pour menus User + Message même nom.
### Sondage : participants et détail des votes
### Boutons Retour containers
### Tickets : prise en charge + ajouter staff
### Tickets : template nom salon + reset menus
- Bouton Prendre en charge (seul le staff assigné écrit).
- Menu Ajouter un staff après prise en charge.
- Mode staff non assigné : masqué (perd accès) ou lecture seule (configurable).
- Guard messages pour threads/salons.
- Template {ticket.emoji}, {user.id}, {date}, etc. (comme vocaux temp).
- Config via /config → Tickets → Modifier (template nom).
- Menus déroulants du panneau réinitialisés après sélection.
### /giveaway sans sous-commandes
### Sync commandes Discord cassée
### Rôles par action : retrait si statut retiré
### Tickets : staff, menus section, transcript, couleurs boutons
### Commande /giveaway
### Statistiques serveur : renommer + texte affiché
### Photo profil site
### Forum : scroll sujets
- Rôles staff = seuls rôles avec accès (salon catégorie ou thread privé).
- Interface Menus par section (jusqu'à 5 menus × 25 options).
- Transcript HTML à la fermeture.
- Couleur bouton via style=success dans description du type.
### Module tickets absent de /config
- Section 🎫 Tickets ajoutée dans /config → Système automatique.
- Config multi-panneaux : activation, types de tickets, mode threads/catégorie, interface boutons/menu, embed, publication du panneau.
- Runtime : ouverture ticket (thread ou salon), bouton fermer, permissions staff par type.
- Schéma tickets dans serverConfig (compatible structure Python configurations).
### bot-public : crash au démarrage (import LibreTranslate)
### /sondage : boutons alignés sur les choix
### /config Administration : sondages + giveaways
### Statistiques serveur (gstats) : options simplifiées
- Section Sondages : rôles/utilisateurs autorisés à lancer et clôturer ; rôle ping à la publication.
- Section Giveaways : permissions création / participation (prête pour /giveaway).
- Schéma serveur : poll, giveaway dans serverConfig.
- /sondage respecte les permissions config (lancer, clôturer, ping rôle).
- Cibles : Nom du serveur ou Nom d'un salon vocal.
- Statistiques : utilisateurs, bots, salons, rôles, membres avec rôle (sélecteur rôle si besoin).
- Compteurs {channel.count} et {role.count} ajoutés au moteur.
### Annonces forum → Discord (#annonces)
### Forum : menu tags (mobile Dialog)
### bot-public /config : dropdown Notifications
- bot-site : POST /forum/announcement-post — webhook nom/avatar auteur, embed contenu, réactions 👍❤️👎, thread « Commentaire », menu traduction.
- Mapping forum_sync_mapping.json mis à jour.
- Web : appel direct bot-site au lieu de commands.json.
- bot-public : handler forum_web_tr:* pour traduction éphémère (LibreTranslate).
### Stripe Checkout : automatic_payment_methods rejeté
### Traduction manquante forum.catAnnounce
### Cheat Code sur toutes les consoles
### Tools : sous-catégorie Store
### Bot Discord : /stop + doublons vocal
### Stripe : moyens de paiement Dashboard
### Photo de profil invisible après upload
### Forum : scroll sidebar sujets
### Messagerie PC plein écran
- resolveVoiceBot : réutilise le bot déjà dans le salon ; secondaire uniquement si enregistrement actif.
- /stop : arrêt musique + radio sur les deux groupes (stopAnyMusic + stopAnyRadio).
- musicManager.connect : réutilise getVoiceConnection existante.
### Changelogs ressources : markdown Discord
### Mobile : z-index panneaux vs header
### Navbar : texte LB'XMB.FR mobile
### Paramètres : pays / langue en boucle
### Messagerie mobile : composer, menus, bottom nav
- FloatingMessagePanel : bottom: 0 en conversation, 4rem en liste.
- MessageComposerBar : padding réduit en mode embedded mobile.
- Menu contextuel mobile : z-[10060]/[10061].
- openMobilePanel() : un seul panneau (messagerie / notifs / profil / recherche).
- Bottom nav masquée en conversation (onInConversationChange).
### OAuth Discord rejeté par l'API (invalid_scope)
- Login User Install : scopes identify + applications.commands uniquement (integration_type=1).
- errorCallbackURL → /login?error=oauth + affichage error_description.
### Connexion site + applications.commands en un seul OAuth
- Plugin BetterAuth discordUserInstallLoginPlugin : force integration_type=1, scopes identify+relationships.write+applications.commands+guilds.join, prompt=consent, URL discord.com/oauth2/authorize.
- Suppression enchaînement post-login (PostLoginDiscordUserInstall).
### Session site cassée par enchaînement OAuth trop rapide
- Login Discord : callbackURL = / (session créée d'abord).
- Flag lbxmb_post_login_user_install + composant PostLoginDiscordUserInstall : attend getSession() puis redirige vers User Install.
- User Install : prompt=consent pour afficher « créer des commandes » (applications.commands).
- /auth/discord-complete : simple redirect / (rétrocompat).
### Flux login → User Install automatique
- Après connexion Discord BetterAuth, redirection vers /auth/discord-complete puis /api/auth/discord/user-install (lien applications.commands + integration_type=1, redirect /discord/bot).
- Cookie lbxmb_user_install_return + retour accueil après callback OAuth User Install.
- Login API prod : scopes identify+guilds.join+relationships.write (sans applications.commands).
### Discord : login vs User Install
### Service Worker
### Traductions
### Attrape-scam : DM avant ban
### Connexion Discord cassée
### Mobile : drawers ressources/forum + menu hamburger
### Ressources : tri + import GitHub
- Clarification : sondages, réactions, messages, vocal, rôles d'accueil et commandes bot doivent compter comme participation.
- recordParticipationFromInteraction : slash, menus contextuels, boutons, selects, modales.
- Vocal (bumpMemberVoice) : annule aussi le compteur « jamais interagi » + règle 6 mois.
- Rôles d'accueil : réactions + boutons/select cfg:rolepick déjà couverts.
- Un membre actif sur le serveur (même sans message) n'est plus considéré inactif sur le critère interaction s'il passe en vocal ou utilise une commande.
- bot-public/src/lib/memberActivity.js
- bot-public/src/events/interaction/interactionCreate.js
- docs/RECAP-TRAVAIL-COMMUNAUTE-2026-04.md
- Passage automatique bot-site a expulsé ~600 membres avec l'ancienne logique (30 j « full » ou 90 j partiel en OU).
- Règles attendues : expulsion uniquement si toutes les conditions sont remplies après 3 mois ; exception 6 mois en ligne sans activité ; compteur hors ligne reset à la reconnexion ; guilde 1232966743241130005 seule.
- evaluateMemberInactivity v2 : AND (hors ligne 90 j + jamais message + jamais réaction + jamais bouton/menu) OU exception en ligne 180 j sans activité.
- memberActivity.js (bot-public) : offline_since, pending_*_since, tracking_started_at ; présence reset offline ; réactions + interactions composants trackées.
- Guilde forcée REGULARIZATION_GUILD_ID=1232966743241130005 ; membres sans tracking_started_at ignorés (pas de rétroactif).
- Config : REGULARIZATION_STANDARD_DAYS=90, REGULARIZATION_ONLINE_IDLE_DAYS=180.
- Plus d'expulsion en masse sur critères trop larges ; compteurs repartent au prochain événement présence/activité par membre.
- Changelogs : entrées récentes en bas de page (scroll long).
- Fiche ressource : colonne droite (télécharger / actions) ne restait pas visible au scroll.
- Connexion Discord : flux guild install (ajout bot serveur) au lieu de user install.
- Login : emoji main CDN (waving-hand) affiché en texte.
- Modal téléchargement : regroupement auto exe→Windows, etc.
- Accueil : interverti absent à côté de Default.xex (featured-home filtrait user_id avec des IDs Discord).
- /changelogs : tri par date décroissante (plus récent en haut).
- ResourceDetailClient : aside droite lg:sticky lg:top-24.
- OAuth login Discord → /api/auth/discord/user-install (integration_type=1).
- Login : emoji Unicode 👋 (plus de dépendance CDN Apple emojis).
- Ressource « Xb Homebrew Vault » (Xbox One + Series) : étiquette console n’affichait que Xbox One ; bouton « Visiter » grisé au lieu de « Télécharger ».
- Cause : catégorie « Site » ajoutée par erreur (basculage URL / GitHub) alors que les fichiers sont hébergés localement ; isSiteResource traitait Tools+Site comme ressource site.
- Données corrigées (resource_id 218) : retrait de « Site » dans categories_by_console_json + structure.json.
- isSiteResource : ignore la catégorie Site si des téléchargements locaux existent ; helper stripSiteCategoryUnlessUrlOnly à l’ajout.
- Fiche détail : fusion consoles déclarées + téléchargements ; libellés getPlatformDisplayName (Xbox One · Xbox Series X/S).
- Liste ressources : consoles multiples affichées avec noms lisibles.
- Formulaire ajout : mode Fichier retire la catégorie Site ; sync GitHub ne force plus le mode URL.
- Xb Homebrew Vault téléchargeable via modale ; Xbox One et Series visibles sur la fiche et dans les listes filtrées.
### 1. Compteur ressources PS3 (45 vs 42)
### 2. Boutique profil
### 3. OAuth Google / GitHub
### 4. Chat staff — messages vocaux
### 5. Logs Discord staff
### 6. Page /cloud
### 7. Accueil — créateurs
### 8. GitHub repo (ajout ressource)
### Fichiers
- Cause : sidebar comptait les ressources multi-catégories plusieurs fois.
- Fix : décompte distinct resource_id par plateforme dans /api/resources/structure.
- Composant partagé ProductCard (taille profile, grille plus dense).
- Liaison unifiée via link-social (profil + paramètres).
- Finalisation POST /oauth-providers/link au retour Google/GitHub.
- Sync photo OAuth à chaque session.
### Contexte
### Changements
### Fichiers
### Impact
- Les commandes /translate, menu « Translate this message » et bouton auto-traduction renvoyaient du texte brut, même pour les messages en embed ou container Components V2.
- Module translateMessageLayout.js : détecte embed/container, traduit chaque bloc de texte (titres, sections, boutons, champs embed…), reconstruit le layout Discord traduit avec le footer de traduction intégré.
- Branchement dans translate.js et autoTranslationHandler.js ; repli sur texte simple si pas de structure.
- bot-public/src/lib/translateMessageLayout.js
- bot-public/src/commands/util/translate.js
- bot-public/src/handlers/autotranslation/autoTranslationHandler.js
- docs/RECAP-TRAVAIL-COMMUNAUTE-2026-04.md
- Traduction visuelle fidèle pour containers (help, invite, panels…) et embeds classiques.
- Redémarrer bot-public.
### Contexte
### Règles
### Changements
### Fichiers
### Impact
- Expulser (kick, pas ban) les membres totalement inactifs sur le serveur LB'XMB, avec MP avant expulsion et lien de réinvitation.
- 30 jours : aucun message, vocal, sondage et connexion (online/idle/dnd) → expulsion.
- 90 jours : aucun message, vocal ni sondage (statut en ligne ignoré) → expulsion.
- Grâce 14 jours après l'arrivée sur le serveur ; bots et owners bot exemptés ; rôles exemptés via REGULARIZATION_EXEMPT_ROLES.
- bot-public : index d'activité partagé data/member_activity/<guildId>.json (messages, vocal, sondages /sondage, présence en ligne).
- bot-site : évaluation quotidienne (~03:15 Paris), MP d'expulsion puis kick, log data/regularization/kicks.json, route manuelle POST /regularization/run (auth x-api-key).
- bot-public/src/lib/memberActivity.js
- bot-public/src/events/message/messageCreate.js
### 1. Commandes personnalisées
### 2. /invite-bot
### 3. /signaler
### 4. Ajout bot sur serveur
### 5. Contestations ban/blacklist
### 6. /base64
### 7. /help
### 7b. /help — menu déroulant par catégorie
### Fichiers
- Création : nom uniquement → bouton Configurer (message + rôles/utilisateurs).
- Suppression du choix public/éphémère ; autorisations dans la config ; scope serveur inchangé.
- Réponse visible (non éphémère) ; boutons Ajouter au serveur + Ajouter à mon compte ; container features.
- Raison Autre → modale obligatoire pour préciser.
- MP container au propriétaire (merci, /help, /config, features).
- Une contestation par sanction ; owner serveur reçoit DM + bouton Débannir ; user prévenu avec lien d'invitation si accepté.
### Contexte
### Changements
### Fichiers
### Impact
- Quand 1139951514694193323 rejoint ou change de salon vocal, alerter 1398073346175926272 en MP avec le lien du vocal — uniquement si les deux sont sur le même serveur.
- Module voiceJoinAlert.js : détection voiceStateUpdate, vérif membre commun via guild.members.fetch, lien https://discord.com/channels/{guild}/{channel}, cooldown 45 s par salon.
- bot-public/src/lib/voiceJoinAlert.js
- bot-public/src/events/guild/voiceStateUpdate.js
- docs/RECAP-TRAVAIL-COMMUNAUTE-2026-04.md
- bot-public à redémarrer.
### Contexte
### Changements
### Fichiers
### Impact
- Besoin d'un lien OAuth unique (identify, applications.commands, guilds.join, relationships.write) pour login, liaison, vérification et User Install.
- Lors d'une blacklist bot : bloquer l'utilisateur pour tous les comptes Discord ayant autorisé l'application ; prévenir la personne avec la raison et comment se débloquer dans les paramètres Discord.
- Module central discord-oauth.ts : scopes + builders d'URL (login, liaison, vérification, User Install).
- Better Auth Discord, prepare-link, /api/verify/discord/authorize, page bot : scopes étendus + integration_type=1 où pertinent.
- Module discord-relationships.ts : blocage/déblocage via API Discord relationships.write (refresh token, parcours table account).
- API interne POST /api/internal/discord/blacklist-relationships (bot → site).
- Commande /blacklist : appelle la propagation relations à l'ajout/retrait ; MP mis à jour (raison + déblocage via Paramètres utilisateur → Bloqués).
### Contexte
### Cause
### Changements
### Fichiers
### Impact
- Sur un serveur, /userinfo → Cannot read properties of null (reading 'members') ; /serverinfo → « Impossible de charger ce serveur ».
- Commandes User Install : interaction.inGuild() vrai mais interaction.guild souvent null (seul guildId est fourni). Accès direct à interaction.guild.members.
- Helper resolveInteractionGuild : fetch via client.guilds.fetch(guildId) si interaction.guild absent.
- /serverinfo et /userinfo utilisent ce helper ; garde-fous sur guild.members / guild.channels.
- Try/catch sur /userinfo.
- bot-public/src/lib/resolveInteractionGuild.js
- bot-public/src/commands/util/serverinfo.js
- bot-public/src/commands/util/userinfo.js
### 1. Base jeux partagée (sans IGDB)
### 2–4. Fiche produit boutique (garanties + options + prix)
### 5. Accueil — profil Default.xex
### 6. Connexions OAuth (scope + dernière connexion)
### 7. Navbar notifications + mobile
### 8. Boutique mobile — liste
### 9. Ticket partenariat — formulaire dédié
### Impact / déploiement
- Contexte : même UX « nouveau jeu » (nom + icône) pour ressources, guides et boutique.
- Changements : table shared_games, API GET/POST /api/shared-games, composant SharedGamePicker (recherche + création).
- Intégration : formulaires ressources (ajouter), guides (ajouter), boutique (BoutiqueProductForm).
- Fichiers : web/src/lib/shared-games.ts, web/src/app/api/shared-games/route.ts, web/src/components/shared/SharedGamePicker.tsx, web/src/app/ressources/ajouter/page.tsx, web/src/app/guides/ajouter/page.tsx, web/src/components/boutique/BoutiqueProductForm.tsx
### Contexte
### Cause
### Changements
### Impact
### Fichiers
- Connexion via Discord impossible sur https://lbxmb.fr (Better Auth invalid_client).
- PM2 conservait un ancien DISCORD_CLIENT_SECRET (e4DuS8…) qui écrasait la valeur correcte de web/.env.local (oG8LO…).
- server.js : relecture forcée des clés OAuth depuis .env.local au démarrage.
- Redémarrage web-prod avec --update-env après sourcing .env.local.
- OAuth Discord fonctionnel en production (secret client valide côté Discord API).
- web/server.js
- docs/RECAP-TRAVAIL-COMMUNAUTE-2026-04.md
### Contexte
### Cause
### Changements
### Fichiers
- Le bot ne se connectait plus à Discord après les changements forum ressources.
- Import incorrect dans siteResourceForum.js : ../../lib/logger.js → chemin bot-public/lib/logger.js (inexistant).
- Correction import → ./logger.js.
- Redémarrage bot-public : connexion OK (LB'XMB.FR#3591, 4 serveurs).
- bot-public/src/lib/siteResourceForum.js
- docs/RECAP-TRAVAIL-COMMUNAUTE-2026-04.md
### /blacklist (remplace /bl)
### Commandes personnalisées (soft)
### Ressources site → salons forum Discord (bot-site)
### Open Graph ressources
- Contexte : fusion des sous-commandes /bl add|remove|list en une seule commande avec choix d'action.
- Changements : nouvelle commande /blacklist avec options action (Ajouter / Retirer / Liste), utilisateur ou identifiant obligatoire pour add/remove, temps et raison pour l'ajout. Suppression de bl.js.
- Impact : propriétaires du bot utilisent /blacklist (global User Install). Déploiement global mis à jour (14 commandes).
- Fichiers : bot-public/src/commands/owner/blacklist.js, deploy-commands.js, interactionCreate.js, help.js, invite-bot.js
- Contexte : bouton « Ajouter (bientôt) » inactif dans /config > Administration.
- Changements : modal création (nom, message, visibilité public/ephemeral), stockage admin.custom_commands, sync guild des slash custom, exécution dans interactionCreate.
### Contexte
### Cause
### Changements
### Impact
### Fichiers
- User Install actif mais bot retiré du serveur : plus de commandes visibles sur mobile.
- Normal : le bot absent du serveur ≠ app désinstallée du compte.
- 10 commandes musique (guild-only) polluaient le déploiement global User Install.
- Réinstallation User Install souvent nécessaire après retrait du bot.
- Global réduit à 14 commandes User Install (translate, help, install, etc.) ; musique déplacée en GUILD_ONLY_COMMAND_NAMES (sync par serveur).
- Nouvelle commande /install : panneau réinstallation compte + lien DM bot + aide mobile.
- Doc USER_INSTALL.md mise à jour (DM, Apps, bot hors serveur OK).
- Liste User Install plus claire sur mobile.
- Lien DM : https://discord.com/users/1431308308505039069
### Contexte
### Cause
### Changements
### Impact
### Fichiers
- Après User Install + invitation serveur 0 permission : commandes peu visibles sur mobile, traduction/signalement/blacklist inaccessibles ou cassés.
- User Install ≠ bot sur serveur : deux installations distinctes.
- Mobile : commandes dans onglet Applications, pas « Bots du serveur ».
- Bot serveur 0 permission : ne peut pas lire les messages → /translate et menus contextuels inopérants.
- /bl et /signalement réservés aux propriétaires du bot.
- deploy-commands.js : déploiement global toujours (plus de remplacement si DISCORD_GUILD_ID).
- invite-bot : lien serveur recommandé (permissions minimales) + aide mobile + avertissement 0 perm.
- botInvite.js : buildGuildInviteRecommendedUrl() (voir salon, lire, répondre).
### Contexte
### Changements
### Impact
### Fichiers
- Demande d’une commande communautaire fournissant le lien d’invitation du bot avec 0 permission (pas admin), en User Install, avec container propre.
- /invite-bot : commande globale User Install + Guild Install ; panneau Container V2 (avatar, lien, boutons Link).
- Lien serveur : permissions=0, scope bot + applications.commands.
- Bouton secondaire User Install (applications.commands seul).
- lib/botInvite.js : helpers URL ; print-install-links.js : lien 0 permission documenté.
- N’importe qui peut récupérer le lien d’invitation minimal du bot via /invite-bot (DM, serveur, app installée).
- bot-public/src/commands/util/invite-bot.js
- bot-public/src/lib/botInvite.js
- bot-public/src/lib/{commandSync.js,moderation/guard.js}
### Contexte
### Changements
### Impact
### Fichiers
- Clic « Appliquer » : config sauvegardée en file mais panneau ne se mettait pas à jour (utilisateur voit « rien »).
- OAuth seul ne pousse pas la présence sur le profil sans pont RPC local.
- rpc.js : interaction.update() à la place de deferUpdate + editReply (Containers V2) ; modales mettent à jour le panneau existant.
- Message explicite après Appliquer avec commande pont PC + secret.
- userRpc.js : bridge_secret par utilisateur.
- web/src/app/api/rpc/bridge/route.ts : API HTTPS GET/POST pour le script pont local.
- rpc-local-bridge.js : support BOT_BRIDGE_URL + RPC_BRIDGE_SECRET.
- Timeout 2,5 s sur tentative RPC IPC serveur.
- Le panneau réagit au clic Appliquer avec instructions claires.
- L'activité profil Discord nécessite Discord Desktop + script pont sur le PC de l'utilisateur.
### Contexte
### Changements
### Impact
### Fichiers
- Demande de recréer /rpc après suppression (2026-07-04) pour personnaliser l'activité Discord : nom, description, image, boutons (label + URL).
- OAuth RPC déjà associé au compte (scopes rpc.activities.write, activities.write, presences.write).
- /rpc : commande globale User Install + panneau Container V2 (type, texte, image URL, 2 boutons, chrono).
- Stockage data/user-rpc/<userId>.json + file data/user-rpc-queue/<userId>.json.
- Handlers urpc:* (boutons, modales, sélecteur type).
- rpcPresence.js : construction payload SET_ACTIVITY + tentative IPC local (discord-rpc).
- scripts/rpc-local-bridge.js : pont à lancer sur le PC avec Discord ouvert (poll API interne).
- API interne : GET /internal/rpc/queue/:userId, POST /internal/rpc/ack/:userId.
### 1. Messages auto — changement de salon (bouton « Changer »)
### 2. Vocal temporaire — traduction panneau post-création
### 3. Sélecteur de rôles — emojis + >25 options
### 4. Traduction automatique — mentions / URLs / emojis
### 5. Attrape-scam (nouveau)
### 6. Anti-raid — modules basic
### 7. Sélecteur de rôles — images embed
### Déploiement
- Contexte : erreur ❌ Une erreur est survenue en modifiant le salon d'envoi (arrivée/départ).
- Cause : cfg:field:chan:<section>:<id> mal parsé (chan pris pour action).
- Changement : branche dédiée b === "chan" avant le parsing générique des champs.
- Fichiers : bot-public/src/handlers/config/router.js
- Contexte : boutons et textes en dur (« Autorisé », modales, reset…).
### 1. Attrape-scam — activation
### 2. Anti-URL — http/https et domaines
### 3. UI anti-raid unifiée
### 4. Bot secondaire vocal
### 5. Sélecteur de rôles — Modifier
### Déploiement
- Cause : if (a === "scam") interceptait aussi scam:toggle et scam:delmsg.
- Changement : route scam:cfg pour la page ; toggle fonctionnel ; actif avec salons configurés même sans anti-raid global.
- Fichiers : routerSecurity.js, securityConfig.js
- Changement : listes autorisés + interdits ; option bloquer HTTP ; matching sous-domaines.
- Fichiers : antiRaidModules.js, antiRaidRuntime.js, panelsSecurity.js, routerSecurity.js, securityModals.js
- Changement : 3 pages dans Anti-raid (Général / Membres / Admin), boutons Configurer, pagination.
### 1. Musique — files indépendantes par bot + radio
### 2. /config — navigation directe
### 3. /pickup — User Install
### 4. /userinfo et /serverinfo
### Déploiement
- Contexte : deux bots vocaux jouaient la même file ; /radio devait suivre la même logique de délégation.
- Changements : clé file ${voiceGroup}:${guildId} ; panneau lecteur sans bouton Play ; /play envoie le lecteur dans le chat du salon vocal ; /lecteur pour le réafficher ; playlists YouTube dans /play ; /music supprimée.
- Fichiers : musicManager.js, musicPanel.js, musicSlashHelpers.js, radioManager.js, radio.js, secondaryBot.js, lecteur.js, commandes musique (play, skip, pause, …), interactionCreate.js, commandSync.js
- Changement : option section (auto, admin, sécurité, notifications, rôles, pseudos, messages, réactions).
- Fichiers : commands/owner/config.js
### 1. /signalement — bouton Ignorer
### 2. /lecteur — aucune musique en cours
### 3. /serverinfo — bot absent du serveur
### 4. Sélecteur de rôles — 2ᵉ configuration
### 5. Sélecteur — mode réactions
### 6. Sélecteur — container vs message simple
### Déploiement
- Changement : le signalement est supprimé de reports.json au lieu d'être marqué ignoré.
- Fichiers : reportsStore.js, reportHandler.js
- Changement : message explicite si rien ne joue ; invite à rejoindre un vocal et utiliser /play morceau:….
- Fichiers : musicSlashHelpers.js
- Cause : guild.members.fetch() sur un guild où le bot n'est pas membre.
- Changement : garde-fous ; infos limitées (nom, ID, icône, badges) + mention si le bot n'est pas installé.
### Contexte
### Changements
### Liens
### Fichiers
- Demande d'un lien d'installation User Install avec permissions maximales pour tester l'app sur le compte utilisateur.
- Configuration API Discord integration_types_config : User Install (applications.commands), Guild Install (bot + admin).
- Script bot-public/scripts/print-install-links.js (liens serveur, compte, OAuth étendu).
- Doc bot-public/docs/USER_INSTALL.md.
- Page web/src/app/discord/bot : boutons Installer sur mon compte et Install + OAuth ; i18n FR/EN.
- User Install : integration_type=1&scope=applications.commands
- OAuth étendu : identify, email, guilds, guilds.members.read, connections, applications.commands
- bot-public/scripts/print-install-links.js, bot-public/docs/USER_INSTALL.md
- web/src/app/discord/bot/page.tsx, web/messages/discordBot/fr.json, web/messages/discordBot/en.json
### Contexte
### Changements
### Fichiers
- Demande d'inclure tous les scopes OAuth possibles (guilds.join, gdm.join, rpc, voice, etc.), même partenaires/inutiles.
- ALL_OAUTH_SCOPES (27 scopes) dans print-install-links.js.
- Lien User Install étendu et lien serveur « nucléaire » mis à jour.
- Page /discord/bot + doc USER_INSTALL.md.
- bot-public/scripts/print-install-links.js, bot-public/docs/USER_INSTALL.md
- web/src/app/discord/bot/page.tsx, web/messages/discordBot/fr.json, web/messages/discordBot/en.json
### Cause
### Correction
### Fichiers
- integration_type=1 n'accepte que applications.commands (pas identify/guilds/gdm.join dans la même URL).
- Mélange 27 scopes + User Install → erreur Discord.
- 2 liens : User Install (étape 1) + OAuth compte sans integration_type (étape 2).
- bot-public/scripts/print-install-links.js, bot-public/docs/USER_INSTALL.md
- web/src/app/discord/bot/page.tsx, web/messages/discordBot/fr.json, web/messages/discordBot/en.json
### Lien unique
https://discord.com/oauth2/authorize?client_id=1431308308505039069&integration_type=1&scope=applications.commands
### Fichiers
- bot-public/scripts/print-install-links.js, bot-public/docs/USER_INSTALL.md
- web/src/app/discord/bot/page.tsx, web/messages/discordBot/fr.json, web/messages/discordBot/en.json
### 1. Bot — changement de salon messages auto (bienvenue)
### 2. SwipePicker — boucle langue / pays
### 3–5. Navbar mobile (dev)
### 6. Connexion Discord sur dev
### 7. Photos fondateurs accueil
### 8. Staff mobile — recherche utilisateur
### Déploiement
- Contexte : erreur lors du changement de salon sur une config (ex. welcome_dm).
- Cause : customId découpé par : → section welcome_dm lue comme welcome + id dm.
- Changement : parsing cfg:chan:set / cfg:add:chan avec slice + join(":") pour les clés de section.
- Fichiers : bot-public/src/handlers/config/router.js
- Contexte : après la dernière option, plus de défilement (Fr → En → Es puis stop).
- Changement : loop: true Swiper + realIndex pour langues et pays dans les paramètres profil.
- Fichiers : dev-web/src/components/ui/SwipePicker.tsx
### Contexte
### Changements
### Impact visible
### Fichiers touchés
- Connexion Bedrock impossible : port 19132/UDP non exposé (UFW + aucune allocation Pterodactyl).
- Plugin Geyser-Spigot configuré sur le port 25565 et incompatible avec Paper 26.2 (crash au démarrage).
- UFW : ouverture 19132/udp, 25565/tcp+udp, 25575/tcp+udp (Minecraft).
- Pterodactyl : allocation 19132 testée puis retirée du conteneur MC (Geyser standalone sur l'hôte).
- Geyser standalone : conteneur Docker geyser-bedrock (/opt/geyser-standalone/), écoute UDP 19132, pont vers Java 151.240.100.62:25565, auth floodgate.
- Config Geyser plugin (serveur MC) : port Bedrock passé à 19132 (plugin toujours inactif tant que Paper 26.2 incompatible).
- Bedrock : 151.240.100.62:19132
- Java : 151.240.100.62:25565
- data/firewall_config.json
### 1. Suppression /rpc
### 2. Notifications hors « Système automatique »
### 3. Statistiques serveur (nouveau module)
### 4. Fix sélecteur de rôles (6+ options)
### Déploiement
- Contexte : commande user-installed peu concluante.
- Changements : suppression rpc.js, userRpc.js, handlers interaction, entrée commandSync, aide /help.
- Impact : la commande disparaît après redeploy (18 commandes globales).
- Fichiers : commands/util/rpc.js (suppr.), lib/userRpc.js (suppr.), interactionCreate.js, commandSync.js, help.js
- Contexte : doublon avec la page dédiée via la nav /config.
- Changements : retrait de l'entrée Notifications du hub Système automatique (nav principale inchangée).
- Fichiers : panelsV2.js
- Contexte : afficher membres, bots, rôle… dans nom salon / sujet / nom serveur / surnom bot.
### Changements
### Fichiers touchés
- {bot.add.mention} et {bot.add.name} (nom d'affichage de l'ajouteur).
- Retrait de {invite.link} de l'aide ; seul {invite.code} documenté.
- {invite.display} = nom d'affichage de l'inviteur sur le serveur (plus le nom du salon).
- {invite.display.name} conservé en alias rétrocompat.
- Inviteurs bots ignorés pour {invite.display} / compteur.
- bot-public/src/lib/placeholders.js
- bot-public/src/handlers/invites/inviteTracker.js
- bot-public/src/events/guild/guildMemberAdd.js
- bot-public/src/handlers/welcome/welcomeSender.js
### 1. Placeholders vocaux cross-serveur
### 2. Bienvenue / départ — bots
### 3. Rôles auto permanent/temporaire — cible bots
### 4. Vocal temporaire — rôle alerté
### 5. Vocal temporaire — rôles autorisés/interdits
### 6. Aide variables {} contextuelle
### Fichiers touchés
### Déploiement
- Préférences propriétaire vocal scopées par {guildId}/{userId} (plus de nom figé d'un autre serveur).
- {display.name}, {username.server}, {user.id} appliqués depuis le template du serveur courant.
- Cible par entrée : utilisateurs / bots / tous (/config → bouton Changer).
- Placeholders {bot.mention}, {bot.name}, {bot.id}, {bot.add} (audit log).
- Champ target sur les règles + filtre dans autoRoleEngine.
- alert_role_id configurable par config (remplace le rôle hardcodé LB'XMB).
### Contexte
### Changements
### Fichiers touchés
### Déploiement
- NRJ, Nostalgie, Fun Radio, Virgin ne fonctionnaient pas (URLs obsolètes 404).
- Changer de station faisait quitter/rejoindre le vocal (visible sur Play/Xbox).
- URLs mises à jour après tests ffmpeg (nrjaudio, infomaniak, icecast, bfmtv…).
- switchStation() : change le flux ffmpeg sans connection.destroy().
- Réutilisation de getVoiceConnection() si déjà dans le même salon.
- User-Agent ffmpeg pour les diffuseurs exigeants.
- bot-public/src/lib/music/radioStations.js
- bot-public/src/lib/music/radioManager.js
- bot-public/src/commands/util/radio.js
- pm2 restart bot-public --update-env
### Contexte
### Changements
### Fichiers touchés
### Déploiement
- Le bot arrivait en vocal à 100 % de volume, trop fort pour les utilisateurs.
- DEFAULT_VOLUME = 0.25 (25 %) appliqué à la musique et à la radio à la connexion vocale.
- Affichages panneau /music et /queue alignés sur cette valeur par défaut.
- bot-public/src/lib/music/musicManager.js
- bot-public/src/lib/music/radioManager.js
- bot-public/src/lib/music/musicSlashHelpers.js
- bot-public/src/commands/util/music.js
- pm2 restart bot-public --update-env
### Contexte
### Changements
### Impact
### Fichiers touchés
### Déploiement
- /radio invisible : absente de GLOBAL_COMMAND_NAMES (non déployée globalement).
- Demande : station en liste déroulante (NRJ, Skyrock…) + commandes /play, /skip, /queue, etc.
- /radio station: avec choix fixes (12 stations + « Arrêter la radio »).
- Ajout à GLOBAL_COMMAND_NAMES : radio, play, skip, queue, pause, resume, stop, volume, playlist.
- Nouvelles commandes musique :
- /play morceau: — lance ou remplace le morceau en cours
- /skip, /pause, /resume, /stop
- /queue — file d'attente
- /volume niveau: — 0–200 % (sans option = affiche le volume actuel)
- /playlist ajouter lien: — playlist YouTube (max 50 morceaux)
- /playlist file — voir la file
### Contexte
### Changements
### Impact
### Fichiers touchés
### Déploiement
- /radio invisible : absente de GLOBAL_COMMAND_NAMES (non déployée globalement).
- Demande : station en liste déroulante (NRJ, Skyrock…) + commandes /play, /skip, /queue, etc.
- /radio station: avec choix fixes (12 stations + « Arrêter la radio »).
- Ajout à GLOBAL_COMMAND_NAMES : radio, play, skip, queue, pause, resume, stop, volume, playlist.
- Nouvelles commandes musique :
- /play morceau: — lance ou remplace le morceau en cours
- /skip, /pause, /resume, /stop
- /queue — file d'attente
- /volume niveau: — 0–200 % (sans option = affiche le volume actuel)
- /playlist ajouter lien: — playlist YouTube (max 50 morceaux)
- /playlist file — voir la file
### 1. Rôles temporaires — durées flexibles
#### Contexte
#### Changements
#### Impact
#### Fichiers touchés
### 2. Messages de bienvenue — variables invite
#### Contexte
#### Changements
#### Fichiers touchés
### 3. Blacklist — ban serveurs + MP détaillé
#### Contexte
#### Changements
#### Fichiers touchés
### 4. Commande /radio
#### Changements
#### Fichiers touchés
#### Déploiement
- La config n'acceptait que des minutes entières.
- Parser partagé durationParse.js : 30s, 15m, 2h, 7j, 2sem, 1mo, 1an (nombre seul = minutes).
### Contexte
### Changements
### Impact
### Fichiers touchés
### Déploiement
- Question : le bot utilise-t-il un port ? Les options bot ne marcheraient pas sans.
- bot-public (Discord /config, modération, etc.) : jusqu'ici aucun port HTTP — uniquement WebSocket Discord + file commands.json.
- bot-site (sync pseudo/rôles site ↔ Discord) : déjà actif sur 8787 (/health OK).
- Nouveau serveur HTTP local bot-public/src/lib/internalHttpServer.js sur 127.0.0.1:25591.
- Routes : GET /health (public), GET /status + POST /internal/flush-verification (header x-api-key = BOT_INTERNAL_SECRET).
- flushWebVerificationQueue() exporté pour traiter la vérif web sans attendre le poll 5 s.
- Variables .env : BOT_PUBLIC_HTTP_PORT, BOT_PUBLIC_HTTP_HOST, BOT_INTERNAL_SECRET, LIBRETRANSLATE_URL.
- ecosystem.config.js : entrées bot-public et bot-site documentées.
### 1. IP vérification — parsing headers renforcé
#### Contexte
#### Changements
#### Impact
#### Fichiers touchés
### 2. Blacklist → ban tous les serveurs du bot
#### Changements
#### Fichiers touchés
### 3. Nouveau logo lbxmb.png
#### Changements
### 4. dev.lbxmb.fr redirect prod
#### Cause
#### Changement
#### Fichiers touchés
### 5. Navbar transparente en haut
#### Changement
#### Fichiers touchés
### 6. Domaines : footer, retrait navbar
#### Changements
#### Fichiers touchés
### 7. Menus Applications / mobile
### 1. bot-site — pseudo + drapeau pays → Discord
#### Changements
#### Fichiers touchés
### 2. Site — majuscules dans le pseudo
#### Changements
#### Fichiers touchés
### 3. bot-public — /stats (ex-/voctime) + /config
#### Changements
#### Fichiers touchés
#### Déploiement
- Route POST /sync-profile : construit le nick 🇫🇷 Pseudo (max 32 car.) et appelle l'API Discord.
- Client web discord-bot-site.ts : appelle bot-site au lieu de commands.json (orphelin).
- Branchement sur changement profil (/api/profil), OAuth (auth.ts, auth-helpers.ts).
- Variables BOT_SITE_URL + INTERNAL_API_KEY dans web/.env.local.
### 1. bot-public — commandes publiques gérables dans /config
#### Contexte
#### Changements
#### Fichiers touchés
### 2. bot-public — refonte /transcript
#### Changements
#### Fichiers touchés
### 3. bot-public — redesign canvas /stats
#### Changements
#### Fichiers touchés
#### Déploiement
- transcript, pickup, base64 étaient déployées globalement → absentes de la sync par serveur : impossible de les désactiver ou de leur assigner des rôles (ex. sondage).
- GLOBAL_COMMAND_NAMES réduit à : translate, help, music, rpc, signaler.
- transcript, pickup, base64, sondage, stats passent par la sync par serveur (syncGuildCommands).
- Permissions publiques sans restriction custom → default_member_permissions: null.
### 1. bot-public — canvas /stats (messages 24h/7j/28j + histogramme)
#### Contexte
#### Changements
#### Fichiers touchés
### 2. bot-public — /config > Notifications
#### Contexte
#### Changements
#### Fichiers touchés
### 3. web + nginx — IP client réelle (plus de 127.0.0.1 partout)
#### Contexte
#### Changements
#### Fichiers touchés
#### Déploiement
### 4. web + nginx + Anubis — fix IP réelle (v2, cause Anubis)
#### Contexte
#### Changements
#### Fichiers touchés
#### Déploiement
### 1. Panier hors navbar → menu utilisateur
#### Contexte / problème
#### Changements appliqués
#### Fichiers touchés
### 2. Messagerie tickets — actions + réponse après prise en charge
#### Contexte / problème
#### Changements appliqués
#### Fichiers touchés
### 3. Page panier — contenu sous la navbar
#### Changements
#### Fichiers touchés
### 4. Boutique — icône carrée (pas bandeau)
#### Changements
#### Fichiers touchés
#### Déploiement
- Le panier ne doit pas apparaître dans la barre de navigation principale.
- Entrée Mon panier conservée dans le menu profil (desktop dropdown + ProfileMenuSheet mobile).
### 1. Autorisation vocal temporaire — interdire / réautoriser
#### Contexte / problème
#### Changements appliqués
#### Impact visible / technique
#### Fichiers touchés
### 2. /voctime — parité bot Python
#### Contexte / problème
#### Changements appliqués
#### Impact visible / technique
#### Fichiers touchés
### 3. /to et /unto — timeout Discord (sans salon)
#### Contexte / problème
#### Changements appliqués
#### Impact visible / technique
#### Fichiers touchés
### Contexte / problème
### Changements appliqués
### Impact visible / technique
### Fichiers touchés
- bun run deploy ne fonctionnait plus : script npm deploy absent ; deploy:prod pointait vers /root/lbxmb.fr/deploy.sh supprimé.
- Recréation de deploy.sh (build → swap .next-staging → pm2 restart web-prod --update-env).
- Ajout de "deploy": "bash ../deploy.sh" dans web/package.json.
- Depuis web/ : bun run deploy ou bun run deploy:prod refonctionnent pour la prod.
- deploy.sh
- web/package.json
### Contexte / problème
1. Vérification bot : redirect_uri OAuth2 non valide (URI /api/verify/discord/callback non enregistrée sur le portail Discord ; la bonne URI est /api/verify/oauth/callback).
2. Boutique desktop : layout horizontal type Amazon au lieu d’icône carrée au-dessus du titre.
3. Connexion ptb/dev redirige vers prod (Better Auth sans BETTER_AUTH_URL ni origines ptb/dev).
4. Menu utilisateur partiellement masqué par la bottom nav.
5. Messagerie : besoin d’une barre de recherche en haut + bouton nouvelle conversation à droite.
### Changements appliqués
### Impact visible / technique
### Fichiers touchés
- VERIFY_OAUTH_CALLBACK_PATH → /api/verify/oauth/callback (authorize + callback + .cjs live).
### Contexte / problème
### Changements appliqués
### Impact visible / technique
### Fichiers touchés
- Production affichait à nouveau « Erreur critique » : o.map is not a function.
- Cause racine : messages/fr.json et messages/en.json absents de web/ alors que la locale par défaut est fr. loadMessagesForLocale échouait → messages i18n vides → t.raw("reviews") dans Testimonials renvoyait une chaîne (clé manquante) → .map() sur une string.
- Restauration de messages/fr.json et messages/en.json depuis ptb-web (web + dev).
- load-messages.ts : si le fichier base messages/<locale>.json est absent, on continue quand même (merge faq/discordBot) au lieu de tout faire échouer.
- Garde-fous Array.isArray dans Testimonials.tsx et FAQ.tsx.
- Rebuild + déploiement des 3 env : prod f8UWlF8yGCTbG6Fpw7OJp, ptb et dev également.
### Contexte / problème
### Changements appliqués
### Impact visible / technique
### Fichiers touchés
- Erreur critique affichée sur le site : o.map is not a function (boundary Next.js).
- Cause : dans MessageBell et use-app-queries, res.data.permissions était traité comme tableau via .length alors qu'une chaîne CSV ("fondateur,admin") a aussi une .length → appel de .map sur une string.
- Demande : rebuild et déploiement des 3 environnements (dev, ptb, prod).
- Parsing permissions sécurisé via permissionsFromUserRecord() dans MessageBell.tsx et useTicketRedirectAuth (use-app-queries.ts).
- Garde Array.isArray sur res.data.conversations dans fetchVendorConvs.
- Rebuild + déploiement :
- prod (web) → BUILD_ID Ky1Jly3LI4UbvM0Lzg2La, pm2 restart web-prod
- ptb → BUILD_ID H_s_YgDbcjbIeAZw0kE3y, pm2 restart web-ptb
### Contexte / problème
### Changements appliqués
### Impact visible / technique
### Fichiers touchés
- Après migration dev → prod, le chat n'était pas aligné : version « propre » (liste unifiée, actions ticket, composer corrigé) sur dev/ptb, mais prod servait encore l'ancien build avec onglets Conversations/Tickets.
- Cause racine : les arbres web et ptb-web avaient divergé ; la prod tournait sur un .next-staging figé sans rebuild des changements messagerie. Les sources web étaient aussi incomplètes (~36 fichiers manquants vs ptb).
- Port ptb → web : MessageBell.tsx, ComposeTextarea.tsx (liste unifiée Tickets > Vente > Privé, prise en charge / fermer / transférer, composer aligné).
- API prod alignées : vérif du niveau d'accès de la cible sur transfert ticket ; permission exposée dans /api/admin/support-staff.
- Remise en état sources web : copie des 36 fichiers manquants depuis ptb + récupération depuis source maps du build prod + stub SiteIntroduction.tsx + globals.css.
### Contexte / problème
### Changements appliqués
### Impact visible / technique
### Fichiers touchés
- Demande (sur ptb) : messagerie unifiée (Tickets > Vente > Privé), pouvoir prendre en charge / fermer / transférer un ticket en respectant les règles de rôle (un support ne peut pas recevoir un ticket de modérateur), et corriger l'alignement du composer.
- Blocage découvert : les sources de ptb-web étaient partiellement vidées (MessageBell.tsx, Header.tsx, permission-roles.ts, etc. absents) → build impossible.
- Remise en état de ptb-web : script ptb-web/scripts/recover-src-from-maps.cjs qui récupère les fichiers source manquants depuis les source maps de .next-staging (n'écrase jamais l'existant, filtre les artefacts Next). 22 fichiers restaurés (dont MessageBell.tsx, Header.tsx, permission-roles.ts). SiteWelcomeModal.tsx (absent du build) copié depuis web. → arbre de nouveau buildable (next build OK).
### Contexte / problème
### Changements appliqués
### Impact visible / technique
### Fichiers touchés
- Panier visible dans la navbar au lieu du menu utilisateur.
- Actions ticket mal placées ; staff ne pouvait pas répondre après prise en charge (currentUserId = user_id Discord au lieu de users.id interne).
- Page /panier masquée sous le header fixe.
- Sur mobile en messagerie : navbar haute gênante ; bottom nav doit rester visible.
- Cards boutique affichaient les images en bannière pleine largeur au lieu d’icônes carrées.
- Retrait de CartButton du header ; entrée Mon panier dans le menu profil (sheet mobile + dropdown desktop) avec traduction profileMenu.myCart.
- Actions ticket staff déplacées en haut de la conversation (Prendre en charge / Fermer / Transférer) ; correction currentUserId via u.id (ID interne DB).
- /panier : padding top pt-24 sm:pt-28 sous le header fixe.
### Contexte / problème
### Changements appliqués
### Impact visible / technique
### Fichiers touchés
- La base de ptb (lbxmb_ptb) n'était pas la même que la prod ; demande explicite : ptb doit utiliser la même base que la prod.
- ptb-web/.env.local : DB_USER, DB_PASSWORD, DB_NAME (+ host/port) réalignés sur la prod → DB_NAME=lbxmb, DB_USER=lbxmb.
- pm2 restart web-ptb --update-env avec les variables DB de prod exportées (pm2/dotenv ne réécrasent pas une variable déjà chargée).
- ptb lit/écrit désormais la base de production live (lbxmb). Sessions Better Auth partagées avec la prod ; get-session = 200 ; utilisateur résolu dans les logs.
- ⚠️ Les actions effectuées sur ptb impactent directement les données de production (choix assumé).
- ptb-web/.env.local
### Contexte / problème
### Diagnostic
### Changements appliqués
### Impact visible / technique
### Fichiers touchés
- Impossible de se connecter sur ptb.lbxmb.fr et dev.lbxmb.fr.
- Log dev : ERROR [Better Auth]: { error: 'invalid_client', status: 401 } lors de l'échange du code Discord.
- Les 3 sites partagent le même DISCORD_CLIENT_ID (1431308308505039069) donc doivent partager le même DISCORD_CLIENT_SECRET.
- Empreintes des secrets : prod 931d3ed6 (correct), ptb & dev b66b652d (périmé). Process live ptb : secret vide.
- Les endpoints sign-in/social généraient pourtant les bons redirect_uri (ptb/dev), tables Better Auth présentes → seul le secret était en cause.
- ptb-web/.env.local et dev-web/.env.local : DISCORD_CLIENT_SECRET réaligné sur la valeur de prod.
- pm2 restart web-ptb web-dev --update-env avec le secret exporté dans le shell (dotenv n'écrase pas une variable déjà présente ; en mode cluster pm2 passe l'env par IPC).
### 1. bot-public — /config > Commandes & permissions (échec interaction)
#### Contexte
#### Changements
#### Fichiers touchés
### 2. bot-public — /sondage accessible à tous par défaut
#### Changements
#### Fichiers touchés
### 3. bot-public + web — DM vérification + persistance compte/IP
#### Changements
#### Fichiers touchés
### 4. bot-public — refonte /stats (menu + canvas)
#### Changements
#### Fichiers touchés
#### Déploiement
- DEFAULT_COMMANDS compte 28 entrées ; Discord limite les StringSelectMenu à 25 options → interaction en échec.
- Pagination des menus commandes (cmdvis) et permissions (permpick) par blocs de 25.
### Contexte / problème
### Changements appliqués
### Impact visible / technique
### Fichiers touchés
- Besoin d'une commande /sondage avec un panneau de configuration éphémère (question, description, choix, boutons de vote).
- En production : tout utilisateur qui se vérifie sur un serveur doit automatiquement obtenir un compte sur le site.
- Nouvelle commande guild /sondage : ouvre un container éphémère (Components V2) pour construire le sondage.
- Boutons : « Question / description » (modal), « Ajouter un choix » (modal label + emoji, max 10), bascule mode unique/multiple, « Publier ».
- Menu déroulant pour retirer un choix.
- Publication : message public avec barre de progression %, compteur de votes, un bouton par choix + bouton « Clôturer » (auteur ou modérateur ManageMessages).
- Vote par bouton : bascule du vote, gestion unique vs multiple, mise à jour en direct.
### Contexte / problème
1. /bot : containers trop chargés (trop de boutons).
2. /bot → page serveur : l'icône du serveur ne s'affiche pas.
3. Présence & statuts : remplacer les multiples boutons par un design épuré.
4. Permissions commande par salon : autoriser les salons vocaux + « échec de l'interaction » après le choix du salon.
5. Vérification : conserver l'IP (très important) + tout l'historique ; si l'utilisateur quitte puis re-vérifie avec une IP différente, garder l'ancienne et ajouter la nouvelle.
### Changements appliqués
### Impact
### Fichiers touchés
- Point 4 (bug interaction) : dans config/router.js, le channel-select admin (cfg:admin:permchannels:…) était intercepté par le handler générique handleChannelSelect (qui ne l'acquittait jamais) → « échec de l'interaction ». Dispatch déplacé avant le handler générique ; ancien test mort supprimé. Les salons vocaux étaient déjà autorisés (ChannelType.GuildVoice dans le select) et le guard utilise interaction.channelId (fonctionne pour les vocaux).
### Contexte / problème
### Investigation (preuves définitives)
### Conclusion
### Fichiers touchés
- Question : « tu as accès à la machine, tu peux activer l'UDP pour le port ? »
- Firewall local : OUTPUT = ACCEPT, UDP NEW accepté, retours conntrack RELATED/ESTABLISHED autorisés → rien à activer localement, c'est déjà ouvert.
- STUN Google (UDP port haut 19302) : 10/10 OK → l'UDP haut-port sortant/retour fonctionne en général.
- Sonde maison : envoi de 15 vrais paquets de découverte d'IP Discord vers le serveur voix live (104.29.144.78:19301, ssrc réel) → 0 réponse. Idem sur plusieurs sessions (104.29.151.x, 104.29.144.x).
- Ping ICMP vers l'IP voix → Time to live exceeded (boucle de routage) ; TCP/443 vers ces IP → OK ; traceroute UDP s'arrête au hop 5 (37.49.238.59, transit hébergeur).
### Contexte / problème
### Investigation (preuves)
### Conclusion
### Changements appliqués
### Impact
### Fichiers touchés
- Le bot rejoint le vocal, trouve le morceau, puis quitte immédiatement ("je viens de lancer mon morceau et le bot à quitter le vocal").
- generateDependencyReport() : toutes les deps vocales présentes (sodium-native 5.1.0, @discordjs/opus 0.10.0, ffmpeg, DAVE). → pas un souci de lib.
- Repro via script de diag (bot rejoint un vocal vide et joue) : la connexion reste bloquée en connecting puis timeout 20 s → destroy(connect-failed).
- Debug @discordjs/voice (debug: true) : le WebSocket vocal fonctionne (op 2 reçu avec ip/port du serveur média Discord, modes aead_aes256_gcm_rtpsize/aead_xchacha20_poly1305_rtpsize). Le blocage est à l'étape suivante : la découverte d'IP UDP n'aboutit jamais.
### Contexte / problème
### Changements appliqués
1. redirect_uri du callback (prod)
2. Audio vocal — libsodium-wrappers cassé sous ESM
### Impact
### Fichiers touchés
- Vérif : échange du code Discord refusé (Invalid "redirect_uri" in request).
- Musique : le bot reconnaît le morceau mais ne joue rien en vocal (connexion OK, son absent).
- Le build figé d'authorize utilise /api/verify/oauth/callback, alors que le .cjs envoyait en dur /api/verify/discord/callback → mismatch.
- verify-oauth-callback.cjs réutilise désormais le chemin entrant réel (= le redirect_uri validé par Discord). Testé live : les deux chemins renvoient « Invalid code » (et non plus « Invalid redirect_uri ») → le redirect_uri correspond, un vrai code aboutit.
- Diagnostic : sous ESM, après await ready, libsodium-wrappers n'expose ses méthodes que sur .default, pas sur le namespace ; or @discordjs/voice appelle lib.crypto_aead_xchacha20poly1305_ietf_encrypt sur le namespace → undefined → chiffrement des paquets vocaux échoue → connexion « Ready » mais silence.
### Contexte / problème
### Changements appliqués
1. Fix vocal — bibliothèque de chiffrement manquante
2. Refonte container /music
3. Fix vérif « serveur introuvable »
### Impact
### Fichiers touchés
- Musique : « ❌ Connexion au salon vocal impossible » — le bot rejoignait le vocal mais ne jouait rien et repartait.
- /music devait envoyer directement un container (pas de choix obligatoire) avec menu déroulant des morceaux + boutons complets.
- Vérification web : « serveur introuvable » lors de la vérif.
- @discordjs/voice@0.19.2 exige une lib de chiffrement (modes AEAD imposés par Discord) ; aucune n'était installée → la connexion ne passait jamais Ready → déconnexion immédiate.
### Contexte / problème
### Changements appliqués
1. Streaming musique → yt-dlp + ffmpeg (approche fiable 2026)
2. /music = commande unique + container
3. Fix vérif « échange du code Discord refusé »
### Impact
### Fichiers touchés
- /music était une commande à sous-commandes (play/skip/stop/...) ; demande : une seule commande avec un container et les actions en boutons.
- Lecture cassée : le bot rejoignait le vocal, trouvait le morceau, puis quittait immédiatement. Cause : play-dl@1.9.7 ne parvient plus à streamer YouTube (lib obsolète, flux vide → player Idle).
- Vérification web : erreur récurrente « échange du code Discord refusé ».
- Installé le binaire yt-dlp (/usr/local/bin/yt-dlp, v2026.06.09). Testé OK depuis le serveur (recherche + extraction flux).
### 1. Fix : messages de bienvenue/départ « échec de l'interaction » au clic « Configurer »
Impossible de configurer les sections de messages automatiques (bienvenue, départ, etc.) : le clic sur « Configurer » renvoyait « échec de l'interaction ».
Collision de préfixe de customId. Les sections de messages auto utilisaient cfg:sec:open:<section> (« sec » = section) alors que le module Sécurité capte tout parts[1] === "sec" (isSecurityButton). Le bouton était donc routé vers la sécurité, qui ne connaît pas l'action open → aucune réponse → interaction échouée.
### 2. Inter-salon : synchronisation réactions (ajout), épingles, sondages
### 3. Commande /bl — blacklist globale (réservée aux propriétaires)
### 4. /statut → /bot (gestion complète du bot)
### 5. Commande /signaler — signalement d'utilisateur (guild + user install)
### 1. Import manquant cassant TOUS les handlers de messages
Le bot ne relayait aucun message inter-salon, et plus largement l'anti-spam / threads / réactions / traduction ne se déclenchaient pas.
messageCreate.js appelait tryPendingBotImport(message) sans l'importer → ReferenceError à chaque message → tout le code suivant (handleAutoReactions, handleAntiSpamMessage, handleCrossChannelMessage, traduction, threads) ne s'exécutait jamais (rejet silencieux capté par unhandledRejection).
### 2. Modale d'invitation : erreur visible bien que l'action réussisse
La soumission de la modale faisait interaction.update() puis interaction.followUp() systématiquement → l'enchaînement provoquait une erreur côté client alors que l'invitation était bien enregistrée.
- Ajout de l'import tryPendingBotImport depuis lib/botProfileImport.js.
- Le relais inter-salon, l'anti-spam et les autres traitements de messages fonctionnent à nouveau (redémarrage du bot requis).
- bot-public/src/events/message/messageCreate.js
### 1. Vérification web : « échange du code Discord refusé »
Après connexion Discord, retour sur le site en erreur « échange du code Discord refusé ».
### 2. Bouton « En savoir plus » en bleu + non-accès admin aux IP
### 3. Connexion inter-salon : modèle par serveur + invitations
Les configs inter-salon devaient être individuelles (aucun serveur ne modifie la config d'un autre) avec un système d'invitation : on invite un serveur par ID, il accepte et choisit son propre salon.
- Paire client_id/client_secret validée OK (grant client_credentials → 200) : ce n'est pas un invalid_client.
- Cause : la route /api/verify/oauth/callback appelait handleVerifyOAuthCallback(request, {}) → redirect_uri du token calculé avec le chemin par défaut (/api/verify/discord/callback), différent du chemin réellement appelé → invalid_grant.
- verify-oauth-callback-logic.ts : le redirect_uri du token est désormais reconstruit depuis le chemin réel de la requête entrante (new URL(request.url).pathname) → correspond toujours à celui utilisé à l'autorisation, quelle que soit la route de callback.
### 1. Container de vérification refait
Le panneau de vérification manquait de clarté sur l'intérêt de la vérification et les données récoltées.
### 2. « Publier panneau » : nouveau container si le salon a changé
Republier le panneau de vérif après avoir changé de salon laissait l'ancien panneau orphelin dans l'ancien salon.
### 3. Anti-spam corrigé
L'anti-spam ne se déclenchait pas (spam répété sans effet).
### 4. Connexion inter-salon (bridge multi-serveurs)
Synchroniser un même salon (ex. « entraide ») entre plusieurs serveurs où le bot est présent.
- buildVerificationPanelPayload : nouvel embed « Vérification requise » qui explique qu'il faut se vérifier pour accéder au serveur, avec 2 boutons.
- Bouton Me vérifier : lien direct vers l'URL de vérification du serveur (lbxmb.fr/verify/<id>) en méthode web ; bouton custom en méthode bouton ; masqué en méthode réaction.
### Contexte / problème
Suite de la session : (1) consulter les signalements sans DM, (2) restreindre l'usage de commandes à certains salons, (3) lecteur musique YouTube en vocal, (4) panneau /bot trop chargé, (5) /contestation était une erreur (un bouton est attendu, pas une commande), (6) commande d'aide listant les commandes publiques et cliquables.
### 1. /signalement — consultation des signalements (propriétaires)
### 2. Restriction des commandes par salon (/config > Autorisations)
### 3. /music — lecture YouTube en vocal
### 4. /bot — panneau séparé en pages
### 5. Contestation par bouton (suppression de /contestation)
### 6. /help — aide paginée et cliquable
### Déploiement
### Contexte / problème
Retour utilisateur : (1) erreur serveur introuvable à la vérification + besoin d'un message clair quand la vérif est désactivée ; (2) signalement via menu contextuel utilisateur (modale) ; (3) sélecteur de rôles en mode boutons : les boutons n'ont pas de description → choisir une couleur ; (4) réintroduire /voctime (système du bot Python) ; (5) /backup en container avec menu déroulant et boutons contextuels.
### 1. Vérification web
### 2. Menu contextuel « Signaler » (utilisateur)
### 3. Sélecteur de rôles — couleur des boutons
### 4. /voctime (temps vocal)
### 5. /backup — container + boutons contextuels
### Déploiement
### Fichiers touchés
Lors de la mise à jour d'un container (vérif, vocal temporaire, sélecteurs, /config, /role), le bot tentait un edit sur un message supprimé — échec silencieux, panneau perdu.
- Nouveau module panelMessage.js : editOrResendChannelMessage (fetch forcé → edit, sinon renvoi + MAJ ID) et safeInteractionUpdate / safeInteractionEditReply (fallback followUp si message d'origine introuvable).
- Appliqué à : vérif, vocal temporaire, sélecteurs rôles/pseudo, /role, tous les routeurs /config.
- Suppression manuelle d'un panneau → resync le recrée automatiquement.
- Interaction sur un message supprimé → nouveau message éphémère au lieu d'une erreur muette.
- bot-public/src/lib/panelMessage.js
- bot-public/src/handlers/{verification/verificationPanel.js,tempVoice/controlPanel.js,roles/roleSelector.js,nicks/nickSelector.js,role/roleCommandHandler.js,config/{router.js,routerSecurity.js,routerRoles.js,routerNicks.js,routerCommunity.js,routerReactions.js,routerAdmin.js,editFlowV2.js,securityModals.js}}
- docs/RECAP-TRAVAIL-COMMUNAUTE-2026-04.md
1. Template vocal {display.name} affiché littéralement au lieu du nom d'affichage.
2. Sélecteur de rôles : pas de personnalisation nom/description/emoji par option ; multi-sélection peu claire.
- Vocal temporaire : templateName() utilise applyPlaceholders() ({display.name}, {username.server}, {user}, etc.).
- Sélecteur rôles : flux option = choisir rôle → modale nom/description/emoji ; description publiée dans le menu ; type par défaut select (multi-rôles cochables) ; suppression d'option.
- bot-public/src/{handlers/tempVoice/tempVoiceHandler.js,handlers/config/{panelsRoles.js,routerRoles.js,roleModals.js,routerCommunity.js,panelsCommunity.js},handlers/roles/roleSelector.js,lib/roleConfig.js}
- docs/RECAP-TRAVAIL-COMMUNAUTE-2026-04.md
1. Autorisations vocaux : menu utilisateurs avec cocher/décocher.
2. Placeholders bienvenue : {username.server}, {id.user}, {web.user} manquants.
3. Menu langue panneau vocal inutile — locale client puis serveur.
- Auth vocaux : un UserSelectMenu multi (min 0) avec utilisateurs pré-cochés ; décocher retire l'accès.
- Placeholders : {username.server}, {id.user}, {web.user} dans placeholders.js.
- Locale : plus de select langue ; panneau = langue serveur ; éphémères = langue client → fallback serveur.
- bot-public/src/{handlers/tempVoice/controlPanel.js,lib/placeholders.js,lib/discordLocale.js}
- docs/RECAP-TRAVAIL-COMMUNAUTE-2026-04.md
1. Panneau vocal : repasser en Container V2 avec toggles permissions ; corriger bouton Autorisation ; sections users/rôles.
2. Commandes mod : pas de MP utilisateur ni contestation.
3. /status : presets bio rotatifs manquants.
4. Traduction auto : select langue fixe au lieu de la locale client.
- Vocaux temporaires : Container V2 avec toggles Autorisé/Interdit ; fix bouton Autorisation ; panneau éphémère UserSelect/RoleSelect.
- Modération : MP auto + bouton Contester + /contestation ; stores sanctions/contestations.
- /status : presets bio + rotation dynamique (applyBotBio).
- Traduction auto : bouton Traduire (locale client) ; helper translateForInteraction.js.
- Autorisations vocales fonctionnelles ; utilisateurs sanctionnés informés ; bio rotative ; traduction selon langue client.
- bot-public/src/{handlers/tempVoice/controlPanel.js,handlers/moderation/contestationHandler.js,handlers/autotranslation/autoTranslationHandler.js,commands/moderation/*,commands/owner/status.js,lib/botStatus.js,lib/moderation/*,events/*}
1. /record : téléchargement MP3 impossible depuis le select ; dates non Paris ; pas de limite 25 avec rotation.
2. Vocaux temporaires : panneau simplifié sans embed permissions/autorisations du bot Python.
3. /status : un seul statut ; pas de rotation dynamique entre presets.
- /record : fix téléchargement (deferUpdate + followUp avec fichier) ; dates Europe/Paris ; max 25 enregistrements par utilisateur (suppression des plus anciens).
- Vocaux temporaires : embed complet (permissions @everyone, langue, limite) ; menus permissions/langue ; panneau Autorisation (ajouter/expulser/réinitialiser via MentionableSelect).
- /status : presets multiples (ajout/suppression/sélection) ; mode dynamique avec intervalle configurable ; rotation au démarrage (startStatusRotation).
- Téléchargement des enregistrements depuis l’historique ; horodatage Paris (heure d’été incluse).
- Panneau vocal identique au bot Python (embed + permissions + utilisateurs/rôles autorisés/interdits).
- Statut du bot rotatif entre plusieurs presets configurés.
1. Profil bot : trop de messages publics lors des imports/applications.
2. Demande d’importer le système Vocal temporaire du bot Python dans Système automatique (UI Container).
3. Nouvelle commande /record : rejoindre un vocal, enregistrer en MP3, historique + téléchargement.
- Profil bot : import fichier → réaction ✅/❌ (plus de reply public) ; Appliquer → deferUpdate silencieux ; messages d’import en éphémère uniquement.
- Vocaux temporaires : portage join-to-create (tempVoiceHandler.js), config multi-entrées compatible Python, panneau contrôle Container (renommer/limite/supprimer), hub /config → Système automatique.
- /record : @discordjs/voice + ffmpeg → MP3 VBR ; container Démarrer/Arrêter + select téléchargement ; stockage data/recordings/<userId>/.
- Configuration profil bot sans spam salon.
- Vocaux temporaires configurables comme sur le bot Python (sans MySQL).
- Enregistrement vocal utilisable (ffmpeg requis, déjà présent sur le serveur).
1. Traduction auto : « Aucun texte à traduire » (intent Message Content absent + résolution message fragile).
2. Profil bot trop limité vs bot Python (PATCH /guilds/.../members/@me).
3. Question RPC / scope activities.write.
4. Threads auto : config OK mais création de fils inactive.
- Traduction : intent MessageContent ; cache disque data/translation-cache.json ; résolution via référence du reply (comme Python) ; menu seulement si texte ≥ 3 caractères.
- Profil bot : guild.members.editMe() (nick, avatar, bannière, bio serveur) ; UI import URL/fichier restaurée.
- Threads : logique portée depuis imagethread_handler.py (autoThreadMedia.js) ; permissions complètes incl. SendMessagesInThreads + ManageMessages ; logs création.
- RPC : message explicite sur limite activities.write (bot ≠ OAuth client RPC).
- Traduction fonctionnelle si Message Content activé dans le portail Discord.
- Personnalisation bot par serveur comme le bot Python, sans toucher au profil global.
1. Hub config : remettre emojis à gauche + libellés en gras (pas sur les boutons) ; idem Administration.
2. Threads : remplacer « Exiger » par « Autoriser » (texte seul, URL, médias, légende).
3. Traduction auto : « Aucun texte à traduire » malgré message avec contenu ; pas de drapeaux dans le menu.
4. Profil bot : échec interaction sur Appliquer ; import uniquement par URL.
- Hub auto + admin : format emoji Nom à gauche, boutons sans emoji.
- Threads : règles allow_* + formats médias (png, webp, mp3, mp4, mkv, aac/acc, avi, jpeg, jpg, dds) ; migration legacy require_*.
- Traduction : cache texte à la réception + résolution fiable ; drapeaux sur chaque langue du menu.
- Profil bot : deferUpdate + followUp pour Appliquer ; boutons Importer fichier (PJ dans un salon, 2 min) + URL conservée.
- UI cohérente ; threads configurables en mode « autorisé » ; traduction auto fonctionnelle ; import avatar/bannière simplifié.
1. Page Système auto : emojis doublons, section Communauté regroupait traduction + threads.
2. /backup en sous-commandes au lieu du panneau unique bot Python.
3. Réactions auto : mode salons confus, modale mots en échec.
4. Traduction auto : ajout/retrait salon un par un.
5. Threads : pas de config des emojis de réaction par règle.
- Hub auto épuré : titre simple, 6 entrées distinctes sans emoji redondant.
- /backup unifié : panneau Créer / Appliquer / Supprimer + sélecteur + modal SUPPRIMER.
- Réactions : mode tous salons vs salons choisis ; modales corrigées (update).
- Traduction : multi-sélection salons (décocher = retirer).
- Threads : bouton Réactions + modale emojis par config.
- Navigation /config plus claire ; backup en une commande ; config fiable.
- bot-public/src/handlers/{config/panelsV2.js,config/panelsCommunity.js,config/panelsReactions.js,config/routerCommunity.js,config/routerReactions.js,config/communityModals.js,backup/backupHandler.js}
1. Besoin de réactions automatiques (mot précis ou tous les messages, style MEE6).
2. Restauration des rôles mélangée aux rôles permanents — à isoler dans le hub rôles.
3. /config ne doit jamais être désactivable.
4. Commande /status réservée à l'utilisateur 1139951514694193323.
5. /transcript limité au TXT du salon courant — export HTML + portée serveur demandés.
- Réactions automatiques : schéma auto_reactions, handler messageCreate, UI /config > Système auto (salons, mots, tous messages, emojis).
- Restauration rôles : kind restore dans le hub (à côté permanent/temp/action/sélecteurs) ; retrait du toggle sur les permanents ; migration restore_on_rejoin → règles restore.
- /config protégée : forcée visible+enabled (cmdvis, guard, withDefaults) ; note UI « toujours active ».
- /status : commande globale owner-only, panneau présence (online/idle/dnd/invisible), type activité, modales texte/emoji/URL stream ; persistance data/bot-status.json ; appliquée au démarrage.
1. Rôle autorisé pour /config mais commande invisible (default_member_permissions Administrator).
2. /backup = simple export JSON, pas le système Python.
3. Pas de traduction automatique ni threads automatiques dans /config.
4. /clear sans filtre utilisateur.
- Visibilité slash : sync default_member_permissions depuis les bits des rôles autorisés ; resync après changement autorisations.
- /backup : snapshot guild v7 (rôles, salons, bans, membres, config bot) ; sous-commandes creer, liste, restaurer, supprimer ; restauration multi-parties.
- /clear : option utilisateur (filtre auteur, threads supportés).
- Communauté (/config > Système auto) : traduction auto (menu langues sur messages) + Thread automatique (containers, règles texte/URL/PJ, réactions, plusieurs configs/salon).
- Schémas autotranslation, auto_threads ; migration legacy imagethread.
- Modérateurs avec rôle autorisé voient /config si le rôle a les permissions Discord correspondantes ; backup/restauration type bot Python ; nouveaux systèmes communauté configurables.
1. Import avatar/bannière profil bot toujours inopérant (/config éphémère → collecteur de messages inefficace).
2. Commande masquée encore visible dans Discord ; pas de message clair à l'utilisation.
3. Utilisateurs autorisés dans /config > Autorisations ignorés (/config et hasConfiguredAccess hardcodés propriétaire uniquement).
- Import profil : modale URL (copier le lien Discord) à la place du collecteur de pièces jointes ; saveBotAssetFromUrl.
- guard.js refondu : source unique admin.permissions ; checkCommandAccess centralisé dans interactionCreate.js.
- /config + panneau boutons : accès propriétaire ou utilisateurs/rôles listés dans admin.permissions.config.
- Message explicite si commande masquée : « désactivée sur ce serveur ».
- Sync commandes : rappel éphémère (cache Discord 1–5 min).
- Les 2 utilisateurs autorisés sur /config peuvent l'utiliser ; import image fiable ; commandes masquées bloquées à l'exécution avec message clair.
1. Bouton « Basculer » commandes → échec d'interaction (trop de composants V2).
2. Demande menu multi-sélection visibilité + permissions rôles/utilisateurs par serveur (visibilité Discord).
3. Import avatar/bannière profil bot sans effet (filtre pièce jointe trop strict).
4. Traduction découpée en plusieurs messages + drapeau dans le titre — format rejeté.
- Commandes : StringSelectMenu multi-sélection (cfg:admin:cmdvis) — cocher les commandes à conserver ; sync guild.commands.set() par serveur via lib/commandSync.js.
- Permissions : choix commande → RoleSelectMenu + UserSelectMenu ; persistance admin.permissions ; sync permissions Discord par guild.
- Deploy : commandes modération/config en guild-only ; globales limitées à translate, base64, transcript, pickup + menu contextuel.
- Profil bot : filtre image élargi (extension si contentType absent) + confirmation éphémère après import.
- Traduction : un seul message ; footer -# Traduction 🇺🇸 => 🇫🇷 ; fichier .txt si > 2000 caractères.
1. Traduction : crash interaction.member null sur menu contextuel → message générique.
2. Container /config trop long — demande de pages + menu déroulant en bas.
3. /base64 incomplet vs bot Python (chiffrer/déchiffrer + bouton Déchiffrer).
4. Profil bot : import image avatar/bannière (pas d’URL).
- guard.js : commandes publiques (translate, base64…), guildId safe, fetch member si absent.
- /config scindé : Système automatique / Administration + StringSelectMenu navigation.
- /base64 : choix encode/decode → modales ; réponse avec bouton 🔓 Déchiffrer (b64:decrypt).
- Profil bot : boutons Importer (pièce jointe 60s) → data/bot-assets/<guild>/.
- Traduction contextuelle fonctionnelle ; UI config compacte ; base64 identique au bot Python ; avatar/bannière par upload.
- bot-public/src/lib/moderation/guard.js
- Nouvelles sections /config : pseudos automatiques, administrateur (commandes/permissions), profil du bot.
- /translate + menu contextuel « Translate this message » via LibreTranslate local (User Install).
- Recréation des commandes modération de base.
- Schéma auto_nicks, admin, bot_profile dans serverConfig.js ; placeholder {web-user}.
- Moteur pseudos (permanent, temporaire, sélecteur V2) + verrouillage manuel (soi / autres).
- Hub /config : séparateur Administrateur + profil bot (apply avatar/bio/nickname).
- Admin : liste commandes activables, permissions (squelette), custom commands (squelette).
- lib/libretranslate.js (port TS) ; /translate + context menu ; /transcript, /pickup User Install.
- 18 commandes modération : ban, kick, mute, warn, lock, clear, backup, etc.
- 23 commandes déployées ; PM2 redémarré.
- Configuration pseudos RP (maisons, formats {display.name}) ; gestion commandes serveur ; modération opérationnelle ; traduction DM/serveurs sans bot.
1. Rôles par action : manque condition sur texte du statut personnalisé.
2. Âge du compte : rôles pour comptes au-delà de X jours, sans presets (saisie libre).
3. Sélecteur publié : description du container configurable ; boutons/menu dans le container V2.
4. Menu déroulant sélecteur : pouvoir retirer un rôle (décocher).
5. UI /config : séparateur avant les boutons Ajouter / Retour en bas de page.
- Condition custom_status + intent GuildPresences + event presenceUpdate pour réattribuer à la volée.
- Jours (account_age_days_min/max) via modale (nombre libre) ; libellé « créé il y a plus de X jours ».
- publishRoleSelector : message Components V2 (ContainerBuilder) avec titre/description/couleur ; édition si message_id existant.
- StringSelectMenu publié : minValues(0), sync add/remove selon sélection.
- Helpers addFooterActions dans panelsRoles.js et panelsV2.js (séparateur avant footer).
1. Choix du salon du sélecteur de rôles sans effet (routing ChannelSelect intercepté par le router messages).
2. Impossible de sélectionner plusieurs rôles d'un coup (permanent, temporaire, sélecteur).
3. Conditions « rôle par action » via modale — demande de menus déroulants.
4. Boutons « Configurer » pas tous bleus ; accueil /config avec liste dupliquée.
- router.js : délégation cfg:roles:selchanset:* vers handleRolesInteraction.
- panelsRoles.js : RoleSelectMenu intégré à l'éditeur (maxValues: 25) ; fusion des IDs sélectionnés.
- Panneaux conditions (buildConditionsPanelPayload / buildConditionValuePanelPayload) avec StringSelectMenu ; presets dans conditions.js.
- Options sélecteur : choix du rôle via RoleSelectMenu (plus de modale option).
- Accueil /config simplifié (« Système automatique » + sections + Configurer bleu) ; tous les Configurer en Primary.
- /config ouvrait directement « Messages automatique » au lieu de l'accueil fonctionnalités.
- Titre/description : demande de modales (titre vide autorisé) au lieu du chat.
- Nouvelle fonctionnalité Rôles automatiques : permanents (restauration au retour), temporaires (durée), par action (badge, âge compte, connexion), sélecteurs (boutons/menu).
- /config → buildHomeContainerPayload (Messages + Rôles automatiques).
- Modales textFieldModals.js pour titre (optionnel) et description ; router modal submit.
- Schéma auto_roles dans config serveur ; roleConfig, roleRestoreStore, tempRoleStore.
- Moteur autoRoleEngine (join/leave), tempRoleScheduler, conditions, roleSelector (publish + toggle).
- UI panelsRoles.js + routerRoles.js ; accueil rôles → 4 sections configurables.
- Connexions OAuth : lecture data/connections/<userId>.json (sync bot-site).
- Configuration unifiée ; rôles auto à l'arrivée ; panels réaction/boutons/menu publiables.
- Fusionner /config et /config2 (Containers V2 uniquement sur /config).
- Erreur « ❌ Une erreur est survenue » à l'ouverture de l'éditeur / à l'ajout : limite Discord 40 composants dépassée (éditeur monolithique ~48).
- Renommer « Gérer » → « Modifier » ; bouton Retour toujours seul sur sa ligne.
- /config utilise désormais Components V2 (auto panel) ; commande /config2 supprimée.
- Préfixe interactions unifié cfg: ; router V1 embed supprimé, router unique dans router.js.
- Éditeur scindé en hub + sous-pages (Message / Carte / Régulier) pour rester sous la limite de 40 composants.
- Helper addBackRow() : Retour/Annuler jamais mélangé aux autres boutons.
- Déploiement slash commands (1 commande) ; redémarrage PM2.
- /config fonctionnel sans erreur ; navigation par sous-panneaux ; UX cohérente.
- bot-public/src/commands/owner/config.js (suppression config2.js)
1. /role : afficher la progression membre par membre (Pseudo 22/183).
2. Config sécurité : anti-spam doit être une option dans anti-raid.
3. Vérification : vider les rôles, panneau Discord refait, option compte lbxmb.fr.
4. Page lbxmb.fr/verify/[id] : serveur introuvable, OAuth refusé, pas de nom/logo.
- /role : progression Pseudo \22/183\`` pendant l’attribution en masse.
- Anti-raid : hub Config > Sécurité > Anti-raid > Anti-spam (seuil, fenêtres, sanctions dont suppression seule).
- Vérification Discord : Container V2 (Me vérifier + À savoir éphémère) ; vider rôles ; toggle compte lbxmb.fr.
- Site verify : config fusionnée bot-public + legacy ; métadonnées Discord API ; handler server.js pour prod sans rebuild ; secret OAuth Discord corrigé (invalid_client).
- Progression visible sur attributions massives ; hiérarchie anti-raid cohérente.
- Panneau vérification professionnel avec explication RGPD via « À savoir ».
1. Boutons du panneau vocal sur une seule ligne — souhait de 2 lignes (Renommer | Autorisation / Limite | Supprimer).
2. Mention du rôle vocal dans un message séparé — tout doit être dans le container avec « Alerte @vocal » uniquement.
- Boutons répartis sur 2 lignes dans le Container V2.
- Ligne Alerte <@&rôle> en tête du container ; suppression du message texte séparé.
- Ping du rôle 1520182284198744184 uniquement à la création du vocal (pingRole: true).
- Panneau plus lisible ; une seule message container ; notification ciblée au rôle vocal à l'ouverture.
- bot-public/src/handlers/tempVoice/{controlPanel.js,tempVoiceHandler.js}
- docs/RECAP-TRAVAIL-COMMUNAUTE-2026-04.md
1. Création d'un vocal : le texte « Permissions @everyone » dans le panneau pingait tout le serveur.
2. Souhait de ping le rôle « cherche du monde vocal » (1520182284198744184) à la place.
3. Permission Historique des messages absente pour @everyone sur les vocaux créés.
- Libellé panneau : « Permissions du salon » (plus de @everyone dans le texte).
- À la création : message <@&1520182284198744184> avec allowedMentions ciblé ; panneau sans mentions parsées.
- Overwrites @everyone à la création : inclut ReadMessageHistory (+ messages, pièces jointes, commandes).
- Toggle Historique des messages ajouté au panneau de permissions.
- Plus de ping @everyone à la création ; seuls les membres avec le rôle vocal sont notifiés.
- Chat du vocal : historique des messages lisible pour tout le monde.
- bot-public/src/handlers/tempVoice/{controlPanel.js,tempVoiceHandler.js}
1. Commande /role éphémère pour attribuer/retirer des rôles en masse (membres ou tout le serveur).
2. Page /config Sécurité & protection : anti-spam (anti-raid) + vérification (portage bot Python).
- /role : container éphémère V2 (UserSelect, RoleSelect, « Tous les membres », Ajouter/Retirer, Appliquer) ; respect hiérarchie des rôles.
- Config Sécurité : 3e catégorie dans /config ; anti-spam (multi-salons + même salon, sanctions configurables) ; vérification (web / réaction / bouton, salon, rôles, sync panneau).
- Anti-spam runtime : suppression messages dupliqués ; après N suppressions → sanction (timeout 10 min par défaut, ban/kick/warn/none).
- Vérification runtime : panneau bouton/réaction/web ; queue commands.json ; sync legacy bot/data/servers/ pour lbxmb.fr.
- Modération rôles en masse via /role.
- Anti-spam actif si anti-raid + anti-spam activés dans /config > Sécurité.
- Vérification web/bouton/réaction alignée sur le bot Python.
- L'utilisateur souhaite remplacer les modales de configuration des messages (bienvenue, etc.) par une interface Container V2 propre : boutons par paramètre (titre, description, couleur, carte…).
- Nouvel éditeur unifié (buildEditorContainerPayload) : sections texte à gauche / bouton à droite pour chaque paramètre (état, format embed/simple, titre, description, couleur embed, carte on/off, titre/sous-titre/accent/fond carte, salon, intervalle périodique).
- Plus de modale sur /config2 : textes modifiés en envoyant un message dans le salon (collector 60 s) ; couleurs via palette de boutons + option hex custom ; toggles instantanés.
- Ajout d'une config : création avec valeurs par défaut puis ouverture directe de l'éditeur (salon via ChannelSelect conservé uniquement pour ça).
- Fichiers : fieldEditV2.js, editFlowV2.js, refonte panelsV2.js + routerV2.js.
- Configuration des messages entièrement dans les Containers V2, navigation cohérente, visuel type « panneau de réglages ».
- bot-public/src/handlers/config/{fieldEditV2,editFlowV2,panelsV2,routerV2}.js
- L'utilisateur souhaite remplacer les menus déroulants de /config2 par des lignes Section Components V2 : texte à gauche, bouton d'action à droite (style DraftBot).
- panelsV2.js : refonte avec SectionBuilder + TextDisplayBuilder + setButtonAccessory pour la liste des fonctions (arrivée, départ, MP, réguliers), la liste des configurations, le détail entrée/carte et la suppression.
- /config2 ouvre directement le panneau Messages automatique (4 sections avec bouton « Configurer »).
- routerV2.js : retrait des handlers StringSelectMenu ; suppression directe via cfg2:remove:one:<section>:<id> ; seul menu conservé = sélection de salon à l'ajout (ChannelSelect).
- Redémarrage bot-public.
- UI /config2 alignée sur le modèle texte/bouton horizontal ; plus de menus déroulants pour naviguer entre les options.
- bot-public/src/handlers/config/panelsV2.js, routerV2.js
- bot-public/src/commands/owner/config2.js
- Message bienvenue/départ configuré en « embed » mais envoyé en message simple : la carte image court-circuitait l'embed.
- Couleur d'accent de la carte non configurable ; pas de titre/sous-titre personnalisables ; import d'image impossible sans URL.
- Blockquote > à fiabiliser dans l'embed /config.
- Demande d'un POC Components V2 (Container).
- welcomeSender.buildEntryPayload : la carte est désormais intégrée dans l'embed (embed.setImage(attachment://carte.png)) quand type=embed + carte activée ; sinon image en pièce jointe (type simple) ; sinon embed/texte normal.
- messagePayload.js : ajout de buildEmbed() + parseColor() réutilisables.
- Carte (welcomeCard.renderWelcomeCard) : accent, titre et sous-titre paramétrables (placeholders appliqués, dont {server}, {count.member}), fond via URL ou fichier local importé.
- /config : nouveau sous-panneau Carte (activer/désactiver, éditer texte & couleur, importer une image en pièce jointe via collector 60 s, stockage local dans data/cards/<guild>/<id>.<ext>).
- Vers 06:32 UTC (08:32 locale), un grand nombre de fichiers sources du projet ont été supprimés du disque : bot-public/src/, bot-site/src/, .env/package.json/ecosystem.config.cjs des deux bots, plusieurs fichiers web (web/src/app/layout.tsx, web/src/app/globals.css, web/next.config.ts, ~30 dossiers vidés sous web/src/app), ainsi que ce journal.
- node_modules/, package-lock.json, les builds .next/.next-staging et data/ ont survécu → la prod et les bots tournaient encore depuis la mémoire, mais un redémarrage les aurait fait planter.
- Cause non identifiée (pas un dépôt git, aucun cron suspect). Coïncide avec le début d'une session agent.
- Recréation intégrale de bot-public et bot-site à partir de l'historique de conversation.
- Le web n'a PAS été modifié (contenu des fichiers supprimés non disponible côté agent).
- Bots opérationnels à nouveau ; web à restaurer séparément depuis une sauvegarde.
- bot-public/, bot-site/ (recréés), docs/RECAP-TRAVAIL-COMMUNAUTE-2026-04.md (recréé).
1. Le profil bot modifiait l'avatar/nom global du bot au lieu du seul pseudo serveur.
2. Demande d'une commande /rpc (User Install) pour simuler une Rich Presence via container.
3. Threads auto : messages supprimés même avec tout autorisé (config legacy require_* interprétée en mode restrictif).
- Profil bot : applyBotProfile() ne fait plus que member.setNickname() ; UI /config limitée au pseudo serveur + avertissement Discord.
- /rpc : commande globale User Install + panneau Container V2 (type, chrono, modales texte/images) ; stockage data/user-rpc/<userId>.json ; handlers boutons/modales urpc:*.
- Threads auto : migration legacy require_* → allow_* au chargement ; détection pièces jointes élargie ; config serveur 1494082227674288168 migrée sur disque.
- Plus de modification globale du bot via /config profil ; pseudo serveur uniquement.
- /rpc disponible en installation utilisateur (preview simulée, pas de vrai RPC Discord).
- Les messages texte, image seule ou avec légende ne sont plus supprimés si les types sont autorisés.
