Skip to content

v9.0

Choose a tag to compare

@Klaynight-dev Klaynight-dev released this 22 Jul 22:51
· 2769 commits to main since this release
7e02974

RELEASE NOTES & SPÉCIFICATIONS TECHNIQUES : KOTBO

Version : v9.0

Statut : Production Release / Major Milestone

Auteurs : Équipe Core Engineering Kotbo

Sujets Majeurs : Moteur d'Internationalisation (i18n), Composants Discord UI V2, Suite de Sécurité & Anti-Raid, Système de Clans Communautaires, Transcriptions HMAC


📑 Résumé Exécutif

La version v9.0 de Kotbo marque une refonte architecturale majeure de l'écosystème. Cette mise à jour transforme le bot d'un outil de gestion monolithique en une plateforme modulaire, internationale, hautement sécurisée et conforme aux derniers standards d'interface de Discord.

Points Clés de la Release

  1. Moteur i18n Type-Safe : Intégration native de Paraglide-JS pour une prise en charge bilingue (FR/EN) compilée et sans surcoût au runtime.

  2. Refonte Graphique Discord V2 : Transition complète des conteneurs d'affichage vers @arcscord/components sur plus de 25 commandes slash.

  3. Moteur Anti-Raid & Audit Automatisé : Déploiement de la suite de commande /protection, du scanner /audit et du système de signalement /report.

  4. Écosystème de Clans & Saisons : Système complet de guildes rivales, distribution d'XP, annonces automatisées et classement public synchronisé en temps réel.

  5. Transcriptions de Tickets Sécurisées : Implémentation d'URLs signées temporaires par HMAC pour éliminer tout risque de fuite d'informations ou d'énumération.

  6. Optimisation DevOps & Runner : Isolation stricte des suites de tests unitaires (bun test --isolate) et intégration des utilitaires PostgreSQL au conteneur principal.


📊 Matrice d'Impact & Matrice des Changements

Module / Composant Nature de la Modification Impact Système Compatibilité Descendante
i18n Moteur (paraglide-js) Ajout du pipeline de compilation multilingue Majeur (Cycle de build) Totalement rétrocompatible
Interface Utilisateur (UI V2) Refonte vers @arcscord/components
Majeur (Rendu Visuel) Rupture sur le formatage brut V1
Module Sécurité (/protection) Nouveaux services d'anti-raid, captcha & scam Majeur (Pipeline d'événements) Nouvelle configuration requise
Module Clans (/clan & API) Nouvelles tables Prisma, routes API et commandes Moyen (Base de données & API) Totalement rétrocompatible
Transcriptions (/api/public) Authentification HMAC et vérification RBAC Moyen (API Publique) Liens non signés dépréciés
Pipeline de Test (Bun Runner) Isolation de processus via --isolate
Mineur (DevOps & CI) Transparent pour la production

🏗️ Schémas d'Architecture & Flux de Traitement

1. Tunnel de Filtrage & Traitement d'un Membre Entrant (Anti-Raid & Captcha)

Le pipeline d'accueil évalue séquentiellement les verrous de sécurité avant d'accorder l'accès au serveur aux nouveaux utilisateurs :

                   [ Événement : GuildMemberAdd ]
                                 │
                                 v
            ┌─────────────────────────────────────────┐
            │   Join Lock ou Mode Urgence Actif ?     │
            └────────────────────┬────────────────────┘
                                 │
                   ┌─────────────┴─────────────┐
                   ▼                           ▼
               [ OUI ]                      [ NON ]
                  │                            │
                  v                            v
        ┌──────────────────┐      ┌──────────────────────────┐
        │ Expulsion / Ban  │      │ Détection Anti-Raid      │
        │  Immédiat (Kick) │      │ (Arrivées Massives / Sec)│
        └──────────────────┘      └────────────┬─────────────┘
                                               │
                                 ┌─────────────┴─────────────┐
                                 ▼                           ▼
                             [ Seuil ]                  [ Normal ]
                                 │                           │
                                 v                           v
                      ┌─────────────────────┐    ┌──────────────────────┐
                      │ Déclenchement Raid  │    │ Captcha Requis ?     │
                      │ Mode (Lock/Captcha) │    └──────────┬───────────┘
                      └─────────────────────┘               │
                                              ┌─────────────┴─────────────┐
                                              ▼                           ▼
                                           [ OUI ]                     [ NON ]
                                              │                           │
                                              v                           v
                                  ┌──────────────────────┐    ┌──────────────────────┐
                                  │ Assignation Rôle     │    │ Accès Validé         │
                                  │ @Non-Vérifié + Modal │    │ (Attribution TagRole)│
                                  └──────────────────────┘    └──────────────────────┘

2. Architecture de Compilations des Traductions (Paraglide-JS)

L'approche choisie évite le parsing JSON au runtime en compilant les clés de traduction sous forme de fonctions TypeScript pures :

 ┌──────────────────────────┐       ┌──────────────────────────┐
 │  apps/bot/messages/fr.json│       │  apps/bot/messages/en.json│
 └─────────────┬────────────┘       └─────────────┬────────────┘
               │                                  │
               └─────────────────┬────────────────┘
                                 │
                                 v
                 ┌────────────────────────────────┐
                 │  bun run build:i18n            │
                 │  (paraglide-js compile)        │
                 └───────────────┬────────────────┘
                                 │
                                 v
                 ┌────────────────────────────────┐
                 │  src/lib/paraglide/messages.js │
                 │  (Fonctions JS pure Type-Safe) │
                 └───────────────┬────────────────┘
                                 │
                                 v
                 ┌────────────────────────────────┐
                 │ Import dans les commandes bot  │
                 │ m.rpg_profile_title(..., loc)  │
                 └────────────────────────────────┘


📄 Analyse Détaillée des Fonctionnalités & Code

🌐 1. Internationalisation (i18n) Native avec Paraglide-JS

Afin d'offrir une expérience utilisateur fluide pour les communautés francophones et anglophones, le bot embarque désormais un système de traduction type-safe.

Implémentation dans le module RPG (apps/bot/src/commands/fun/rpgEconomy.ts)

La résolution de la langue s'effectue via le helper getLocale, qui extrait la préférence de l'interaction :

import { getLocale } from '../../utils/i18n.js';
import * as m from '../../lib/paraglide/messages.js';

async function rpgProfileExecute(interaction: ChatInputCommandInteraction) {
  const locale = getLocale(interaction);
  if (!await checkEconomyEnabled(interaction, locale)) return;

  const targetUser = interaction.options.getUser('membre') ?? interaction.user;
  const profile = await getOrCreateRpgProfile(interaction.guildId!, targetUser.id);

  const embed = new EmbedBuilder()
    .setTitle(m.rpg_profile_title({ name: targetUser.displayName }, { locale }))
    .setThumbnail(targetUser.displayAvatarURL({ size: 256 }))
    .setColor(COLORS.primary)
    .setDescription(
      profile.isTraveling 
        ? m.rpg_profile_traveling({ dest: profile.travelDestination }, { locale })
        : m.rpg_profile_resting({}, { locale })
    )
    .addFields(
      { 
        name: m.rpg_profile_field_wallet({ emoji: config.currencyEmoji }, { locale }),
        value: `**${profile.balance}** ${config.currencyName}`, 
        inline: true 
      },
      { 
        name: m.rpg_profile_field_level({}, { locale }),
        value: m.rpg_profile_level_value({ level: profile.level }, { locale }),
        inline: true 
      }
    );

  await interaction.reply({ embeds: [embed] });
}

🎨 2. Standardisation des Composants Discord V2

Pour harmoniser l'affichage graphique sur mobile et desktop, Kotbo abandonne les anciens ContainerBuilder verbeux au profit du wrapper applicatif @arcscord/components.

Exemple de refonte sur la commande /backup (apps/bot/src/commands/admin/backup.ts)

import { separator, v2Message } from '@arcscord/components';
import { kotboContainer } from '../../utils/embeds.js';

// Rendu structuré et épuré avec gestion automatique du conteneur V2
await interaction.editReply(v2Message(
  kotboContainer({
    color: 'success',
    title: `${E.success} Sauvegarde créée`,
    fields: [
      `Sauvegarde **${backupName}** créée avec succès!`,
      separator({ divider: true, spacing: 'small' }),
      `### ${E.stats} Statistiques`,
      [
        `${E.arrow} Rôles: ${backup.rolesCount}`,
        `${E.arrow} Salons: ${backup.channelsCount}`,
        `${E.arrow} Membres: ${backup.membersCount}`,
        `${E.arrow} Taille: ${(backup.sizeBytes / 1024).toFixed(2)} KB`,
      ].join('\n'),
    ],
  }),
));

🛡️ 3. Panneau Anti-Raid, Audit & Signalements

La commande d'administration unifiée /protection offre un contrôle direct sur les modules de défense.

Configuration dynamique via les API Dashboard (apps/bot/src/api/routes/dashboard/raidProtection.ts)

// Champs modifiables à chaud depuis l'interface web d'administration
const PATCHABLE_FIELDS = [
  'captchaEnabled', 'captchaChannelId', 'captchaUnverifiedRoleId', 'captchaTimeoutMinutes',
  'antiRaidEnabled', 'antiRaidJoinThreshold', 'antiRaidJoinWindowSec', 'antiRaidAction',
  'joinLockKick', 'joinLockMessage',
  'reportsEnabled', 'reportsChannelId', 'reportsCooldownSec', 'reportsAnonymous',
  'scamFilterEnabled', 'scamFilterAction', 'scamImageFilterEnabled',
  'inviteGuardEnabled', 'inviteRequireUnitary', 'inviteValidationEnabled'
] as const;

export async function handleRaidProtectionRoutes(...) {
  if (sub === 'raidmode' && method === 'POST') {
    const body = await readJsonBody<{ active?: boolean }>(req);
    const guild = client.guilds.cache.get(guildId);
    const config = (await getRaidProtectionConfig(guildId)) ?? (await upsertRaidProtectionConfig(guildId, {}));

    if (body?.active) {
      await activateRaidMode(guild, config, true, user.userId);
      pushAudit(client, guildId, auditUser, 'raid-protection', 'Mode raid activé manuellement');
    } else {
      await deactivateRaidMode(guild, config);
      pushAudit(client, guildId, auditUser, 'raid-protection', 'Mode raid désactivé');
    }
    return json(res, 200, { config: await getRaidProtectionConfig(guildId) });
  }
}

🏰 4. Système de Clans Communautaires & REST API

Le module de Clans permet d'animer la communauté via des compétitions inter-groupes avec suivi de score en temps réel et réinitialisations saisonnières.

Structure d'une Réponse API Publique (GET /api/public/guilds/:guildId/clans)

{
  "enabled": true,
  "currentClanSeason": 2,
  "guildName": "Server Support Kotbo",
  "guildIcon": "https://cdn.discordapp.com/icons/123456789/a_123.png",
  "clans": [
    {
      "id": "clan-uuid-01",
      "name": "Guilde de l'Aube",
      "description": "Les protecteurs du royaume",
      "roleId": "987654321012345678",
      "roleColor": "#3b82f6",
      "totalXp": 15420,
      "memberCount": 42,
      "topParticipants": [
        {
          "userId": "223344556677889900",
          "xp": 3400,
          "displayName": "Arthur",
          "avatarUrl": "https://cdn.discordapp.com/avatars/2233/abc.png"
        }
      ]
    }
  ],
  "recentScores": [
    {
      "id": "evt-101",
      "amount": 50,
      "source": "XP",
      "displayName": "Arthur",
      "clanName": "Guilde de l'Aube",
      "clanColor": "#3b82f6",
      "createdAt": "2026-07-22T23:45:00.000Z"
    }
  ]
}

🔐 5. Sécurisation des Transcriptions par Liens Signés HMAC

Pour éliminer les vulnérabilités de divulgation d'informations confidentielles via les transcriptions de tickets, les identifiants bruts ne permettent plus d'accéder au contenu. Une signature cryptographique d'une durée de validité limitée (TTL 1h) est générée à la demande.

// Extrait de validation des accès (`apps/bot/src/api/routes/public.ts`)
if (parts[2] === 'transcripts' && parts[3] && parts[4] === 'access' && method === 'GET') {
  const transcriptId = parts[3];
  const auth = await verifyAuth(req);

  if (!auth) return json(res, 401, { error: 'Authentification requise.' });

  const transcript = await prisma.transcript.findUnique({
    where: { id: transcriptId },
    select: { id: true, guildId: true },
  });

  if (!transcript) return json(res, 404, { error: 'Transcription introuvable' });

  // Vérification des droits : Membre du Staff OU Auteur/Intervenant du ticket
  const access = await resolveDashboardAccess(client, transcript.guildId, auth.userId).catch(() => null);
  let authorized = !!access?.canViewDashboard;

  if (!authorized) {
    const ticket = await prisma.ticket.findFirst({
      where: { transcriptId, guildId: transcript.guildId },
      select: { userId: true, claimedById: true, closedById: true },
    });
    if (ticket && [ticket.userId, ticket.claimedById, ticket.closedById].includes(auth.userId)) {
      authorized = true;
    }
  }

  if (!authorized) return json(res, 403, { error: "Accès refusé." });

  // Génération de l'URL signée temporaire (1 heure)
  const { generateTranscriptSignature } = await import('@kotbo/core');
  const { expires, signature } = generateTranscriptSignature(transcriptId, 3600);
  
  json(res, 200, { signedUrl: `/api/public/transcripts/${transcriptId}?expires=${expires}&sig=${signature}` });
}

🛠️ Infrastructure, Docker & Tests

1. Mise à Jour du DockerfileBot (DockerfileBot)

Ajout du paquet système postgresql-client afin d'autoriser l'exécution directe de l'utilitaire pg_dump pour le système de sauvegardes automatisées :

FROM oven/bun:1-alpine

# Canvas text rendering requires system fonts (Alpine ships with none)
# postgresql-client provides pg_dump, used by the daily database backup job
RUN apk add --no-cache fontconfig ttf-dejavu postgresql-client \
  && fc-cache -f

WORKDIR /app

2. Isolation des Tests Unitaires (apps/bot/package.json)

Correction d'un problème d'isolation au sein du test runner Bun où l'utilisation de mock.module polluait l'environnement d'exécution des tests suivants :

{
  "scripts": {
    "test:unit": "bun run build:i18n && bun test src/tests",
    "test:watch": "bun test --isolate --watch src/tests"
  }
}

What's Changed

New Contributors

Full Changelog: https://github.com/Kotbo-Bot/Kotbo/commits/v9