aslain.dev
0%
01 Hizmetler 02 Hakkımda 03 Projeler 04 Stack 05 Blog 06 İletişim
← Tüm makaleler Bots Discord

Discord Autocomplete : suggestions dynamiques de commandes

Discord autocomplete est la fonctionnalité qui te permet de proposer des suggestions dynamiques et instantanées pour une option de commande slash, au fur et à mesure que l'utilisateur tape. Une liste de choix statiques (choices) est limitée à 25 entrées et totalement figée, tandis que l'autocomplete te laisse générer toi-même la liste de suggestions à chaque frappe : une recherche en direct dans la base de données, des résultats tirés d'une API, ou des données filtrées spécifiquement pour le serveur de l'utilisateur. Dans cet article, nous allons construire un système d'autocomplete de zéro avec discord.js v14, en couvrant chaque étape : activer l'autocomplete sur une option, répondre à l'interaction dans la fenêtre stricte de 3 secondes, et filtrer intelligemment de grands jeux de données.

Quand a-t-on besoin de l'autocomplete ?

La méthode fixe addChoices() de Discord est parfaite pour de petites listes immuables : mettre rouge, vert et bleu sur une option « couleur », par exemple. Mais elle montre ses limites dans ces cas :

  • Le nombre d'options dépasse 25 (la limite stricte de Discord pour les choix statiques).
  • La liste est dynamique : les éléments propres à l'utilisateur, des enregistrements en base, des résultats d'API en direct.
  • La liste est propre au serveur ou à l'utilisateur : chaque serveur a ses étiquettes, chaque utilisateur sa playlist.

C'est là qu'intervient l'autocomplete. Quand l'utilisateur commence à taper, le bot reçoit la saisie actuelle, exécute sa propre logique et renvoie jusqu'à 25 suggestions. Les suggestions sont purement visuelles ; l'utilisateur peut quand même taper et soumettre une valeur absente de la liste, c'est pourquoi tu dois toujours valider la valeur reçue lors du traitement de la commande.

Activer l'autocomplete sur une option

La première étape consiste à ajouter .setAutocomplete(true) à l'option concernée dans la définition de ta commande slash. Contrainte importante : tu ne peux pas utiliser à la fois setChoices et l'autocomplete sur la même option ; il faut en choisir un. L'autocomplete ne fonctionne que sur les options de type string, integer et number.

const { SlashCommandBuilder } = require('discord.js');

const data = new SlashCommandBuilder()
  .setName('recherche')
  .setDescription('Rechercher un élément par son nom')
  .addStringOption(option =>
    option
      .setName('nom')
      .setDescription('Le nom de l\'élément à rechercher')
      .setRequired(true)
      .setAutocomplete(true));   // suggestions dynamiques au lieu de choix statiques

module.exports = { data, execute };

Une fois la commande enregistrée auprès de Discord (globalement ou par guilde), cette option passe en mode autocomplete. Dès que l'utilisateur commence à taper dans l'option, Discord envoie à ton bot une interaction d'un type différent de l'exécution d'une commande normale, et nous devons l'intercepter à un endroit distinct.

Répondre à l'interaction d'autocomplete

L'événement déclenché pendant la saisie n'est pas un ChatInputCommandInteraction mais un AutocompleteInteraction. Dans ton écouteur interactionCreate, tu dois donc d'abord brancher selon le type. Tu lis la valeur actuelle de l'option focalisée avec interaction.options.getFocused() et tu renvoies les suggestions avec interaction.respond() :

client.on('interactionCreate', async (interaction) => {
  // Commandes normales
  if (interaction.isChatInputCommand()) {
    const command = client.commands.get(interaction.commandName);
    return command?.execute(interaction);
  }

  // Requêtes d'autocomplete
  if (interaction.isAutocomplete()) {
    const command = client.commands.get(interaction.commandName);
    if (command?.autocomplete) {
      try {
        await command.autocomplete(interaction);
      } catch (err) {
        console.error('Erreur d\'autocomplete :', err);
      }
    }
  }
});

Nous ajoutons une fonction autocomplete à chaque fichier de commande, à côté de la fonction execute habituelle. Ainsi, chaque commande garde sa propre logique de suggestion en interne :

async function autocomplete(interaction) {
  const focused = interaction.options.getFocused();   // le texte tapé par l'utilisateur (string)

  const items = ['Épée', 'Bouclier', 'Potion', 'Arc', 'Bâton', 'Armure'];

  const filtered = items
    .filter(item => item.toLowerCase().startsWith(focused.toLowerCase()))
    .slice(0, 25);   // Discord accepte au maximum 25 suggestions

  await interaction.respond(
    filtered.map(item => ({ name: item, value: item }))
  );
}

Chaque suggestion est un objet de la forme { name, value }. name est le libellé visible par l'utilisateur (100 caractères max), tandis que value est la valeur réelle que tu recevras dans execute à l'exécution de la commande. Le type de value doit correspondre au type de l'option : une chaîne pour une option string, un nombre pour une option integer. Cette séparation est très pratique ; tu peux afficher « Nom d'utilisateur » et envoyer en coulisse un véritable id comme valeur.

Générer des suggestions dynamiques depuis une base de données

La vraie force de l'autocomplete est d'alimenter les suggestions depuis une source de données en direct. Imaginons que nous suggérions les étiquettes créées par l'utilisateur lui-même. Faire la recherche côté SQL avec LIKE et limiter directement le résultat à 25 reste rapide, même sur des milliers d'enregistrements :

async function autocomplete(interaction) {
  const focused = interaction.options.getFocused();

  // exemple better-sqlite3 — uniquement les étiquettes de ce serveur
  const rows = db.prepare(
    `SELECT name, id FROM tags
     WHERE guild_id = ? AND name LIKE ?
     ORDER BY name LIMIT 25`
  ).all(interaction.guildId, `${focused}%`);

  await interaction.respond(
    rows.map(row => ({ name: row.name, value: String(row.id) }))
  );
}

Deux points critiques ici. Premièrement, laisser la base faire le filtrage : tirer tous les enregistrements en mémoire et les filtrer en JavaScript est lent sur de grandes tables ; LIKE ? et LIMIT 25 délèguent le travail à la base. Deuxièmement, afficher un nom lisible (name) à l'utilisateur tout en envoyant l'identifiant unique (id) comme valeur ; au traitement de la commande, tu trouves l'enregistrement directement, sans avoir à rechercher de nouveau.

La règle des 3 secondes, les limites et la performance

Il y a des règles strictes à respecter avec l'autocomplete :

  • La limite de 3 secondes : tu dois répondre à l'interaction avec respond() en moins de trois secondes, sinon Discord abandonne la requête. deferReply ne fonctionne pas ici ; on ne peut pas différer. Ta requête de suggestions doit donc être rapide.
  • La limite de 25 éléments : coupe toujours le tableau avec .slice(0, 25) ou LIMIT 25 en SQL ; au-delà, c'est une erreur.
  • Saisie vide : si l'utilisateur n'a encore rien tapé, focused est une chaîne vide. Dans ce cas, afficher les éléments les plus populaires ou les plus récents offre une bonne expérience par défaut.

Pendant que l'utilisateur tape vite, Discord peut envoyer une nouvelle requête à chaque frappe. Pour éviter que ces requêtes ne surchargent ton bot, deux techniques aident : un cache de courte durée pour les résultats souvent demandés (par exemple un Map qui garde un résultat de requête quelques secondes) et la limitation du débit de tes requêtes si tu utilises une API externe. Mais souviens-toi : la validation de la valeur a sa place dans execute, car l'utilisateur n'est pas obligé de choisir dans la liste ; il peut taper et soumettre à la main.

Questions fréquentes

Puis-je utiliser l'autocomplete et setChoices ensemble ?

Non. Sur une même option, tu utilises soit setChoices() statique, soit setAutocomplete(true) ; les deux ne coexistent pas, et l'enregistrement de la commande est rejeté si tu essaies. Si ta liste compte moins de 25 entrées et ne change jamais, setChoices est plus simple. Si la liste est grande, dynamique ou personnalisée, passe à l'autocomplete.

Puis-je être sûr que l'utilisateur a choisi l'une des suggestions ?

Non, tu ne peux pas le garantir. Les suggestions ne sont qu'un confort ; l'utilisateur peut taper n'importe quel texte absent des suggestions et appuyer sur Entrée. Valide donc toujours la valeur reçue dans la vraie fonction execute de la commande : vérifie que l'enregistrement existe réellement et renvoie un message d'erreur courtois sinon.

Comment savoir quelle option focalisée est en cours de saisie ?

Si une commande a plusieurs options d'autocomplete, tu dois savoir laquelle est focalisée. L'appel interaction.options.getFocused(true) (en passant true) renvoie non seulement la valeur mais l'objet { name, value } ; tu lis dans le champ name quelle option est tapée et tu génères les suggestions en conséquence.

Tu veux des commandes d'autocomplete rapides et intelligentes pour ton bot ? Je peux te construire des commandes avec recherche en direct adossée à une base de données, mise en cache et suggestions propres au serveur. Contacte-moi et parlons de ce dont tu as besoin.

Bu kategorideki tüm yazılar →

Devamı için