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

Discord Autocomplete: dynamische Vorschläge für Slash-Befehle

Discord autocomplete ist die Funktion, mit der du sofortige, dynamische Vorschläge für eine Option eines Slash-Befehls anzeigen kannst, während der Nutzer tippt. Eine statische Auswahlliste (choices) ist auf 25 Einträge begrenzt und völlig starr, während du mit Autocomplete die Vorschlagsliste bei jedem Tastendruck selbst erzeugst: eine Live-Suche in der Datenbank, Ergebnisse aus einer API oder Daten, die speziell für den Server des Nutzers gefiltert sind. In diesem Artikel bauen wir ein Autocomplete-System von Grund auf mit discord.js v14 und behandeln jeden Schritt: Autocomplete für eine Befehlsoption aktivieren, die Interaktion innerhalb des strengen 3-Sekunden-Fensters beantworten und große Datensätze clever filtern.

Wann brauchst du Autocomplete?

Die feste Methode addChoices() von Discord ist perfekt für kleine, unveränderliche Listen: etwa Rot, Grün und Blau auf einer Option „Farbe". Doch in diesen Fällen reicht sie nicht:

  • Die Anzahl der Optionen übersteigt 25 (die harte Obergrenze von Discord für statische Choices).
  • Die Liste ist dynamisch: die eigenen Einträge des Nutzers, Datensätze in einer Datenbank, Live-API-Ergebnisse.
  • Die Liste ist server- oder nutzerspezifisch: jeder Server hat andere Tags, jeder Nutzer seine eigene Playlist.

Hier kommt Autocomplete ins Spiel. Wenn der Nutzer zu tippen beginnt, erhält der Bot die aktuelle Eingabe, führt seine eigene Logik aus und liefert bis zu 25 Vorschläge zurück. Vorschläge sind rein visuell; der Nutzer kann trotzdem einen Wert tippen und absenden, der nicht in der Liste steht, daher musst du den eingehenden Wert beim Verarbeiten des Befehls immer validieren.

Autocomplete für eine Befehlsoption aktivieren

Der erste Schritt ist, .setAutocomplete(true) zur betreffenden Option in deiner Slash-Befehlsdefinition hinzuzufügen. Eine wichtige Einschränkung: du kannst nicht gleichzeitig setChoices und Autocomplete an derselben Option verwenden; du musst dich für eines entscheiden. Autocomplete funktioniert nur bei Optionen vom Typ string, integer und number.

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

const data = new SlashCommandBuilder()
  .setName('suche')
  .setDescription('Ein Element nach Namen suchen')
  .addStringOption(option =>
    option
      .setName('name')
      .setDescription('Der Name des gesuchten Elements')
      .setRequired(true)
      .setAutocomplete(true));   // dynamische Vorschläge statt statischer Choices

module.exports = { data, execute };

Sobald du den Befehl bei Discord registrierst (global oder pro Guild), wechselt diese Option in den Autocomplete-Modus. In dem Moment, in dem der Nutzer in die Option tippt, sendet Discord deinem Bot eine Interaktion eines anderen Typs als eine normale Befehlsausführung, und die müssen wir an einer separaten Stelle abfangen.

Die Autocomplete-Interaktion beantworten

Das Ereignis, das während des Tippens auslöst, ist keine ChatInputCommandInteraction, sondern eine AutocompleteInteraction. In deinem interactionCreate-Listener musst du daher zuerst nach dem Typ verzweigen. Den aktuellen Wert der fokussierten Option liest du mit interaction.options.getFocused(), die Vorschläge gibst du mit interaction.respond() zurück:

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

  // Autocomplete-Anfragen
  if (interaction.isAutocomplete()) {
    const command = client.commands.get(interaction.commandName);
    if (command?.autocomplete) {
      try {
        await command.autocomplete(interaction);
      } catch (err) {
        console.error('Autocomplete-Fehler:', err);
      }
    }
  }
});

Wir fügen jeder Befehlsdatei neben der üblichen execute-Funktion eine autocomplete-Funktion hinzu. So behält jeder Befehl seine eigene Vorschlagslogik in sich geschlossen:

async function autocomplete(interaction) {
  const focused = interaction.options.getFocused();   // der vom Nutzer getippte Text (string)

  const items = ['Schwert', 'Schild', 'Trank', 'Bogen', 'Stab', 'Rüstung'];

  const filtered = items
    .filter(item => item.toLowerCase().startsWith(focused.toLowerCase()))
    .slice(0, 25);   // Discord akzeptiert höchstens 25 Vorschläge

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

Jeder Vorschlag ist ein Objekt der Form { name, value }. name ist das Label, das der Nutzer sieht (max. 100 Zeichen), während value der tatsächliche Wert ist, den du in execute erhältst, wenn der Befehl läuft. Der Typ von value muss zum Optionstyp passen: ein String für eine String-Option, eine Zahl für eine Integer-Option. Diese Trennung ist sehr praktisch; du kannst dem Nutzer „Benutzername" anzeigen und im Hintergrund eine echte id als Wert senden.

Dynamische Vorschläge aus einer Datenbank erzeugen

Die wahre Stärke von Autocomplete liegt darin, Vorschläge aus einer Live-Datenquelle zu speisen. Angenommen, wir schlagen Tags vor, die der Nutzer selbst erstellt hat. Die Suche auf der SQL-Seite mit LIKE durchzuführen und das Ergebnis direkt auf 25 zu begrenzen, bleibt selbst bei Tausenden von Datensätzen schnell:

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

  // better-sqlite3-Beispiel — nur die Tags dieses Servers
  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) }))
  );
}

Hier gibt es zwei entscheidende Punkte. Erstens: lass die Datenbank das Filtern erledigen: alle Datensätze in den Speicher zu ziehen und in JavaScript zu sieben, ist bei großen Tabellen langsam; LIKE ? und LIMIT 25 übergeben die Arbeit an die DB. Zweitens: zeige dem Nutzer einen lesbaren Namen (name) und sende die eindeutige id (id) als Wert; beim Verarbeiten des Befehls findest du den Datensatz direkt, ohne erneut suchen zu müssen.

Die 3-Sekunden-Regel, Grenzen und Performance

Bei Autocomplete gibt es strikte Regeln, die du einhalten musst:

  • Das 3-Sekunden-Limit: du musst die Interaktion innerhalb von drei Sekunden mit respond() beantworten, sonst verwirft Discord die Anfrage. deferReply funktioniert hier nicht; ein Aufschub ist nicht möglich. Deine Vorschlagsabfrage muss also schnell sein.
  • Die Obergrenze von 25 Elementen: kürze das Array stets mit .slice(0, 25) oder SQL LIMIT 25; mehr wirft einen Fehler.
  • Leere Eingabe: wenn der Nutzer noch nichts getippt hat, ist focused ein leerer String. In diesem Fall bietet das Anzeigen der beliebtesten oder neuesten Elemente eine gute Standarderfahrung.

Während der Nutzer schnell tippt, kann Discord bei jedem Tastendruck eine neue Anfrage senden. Damit diese Anfragen deinen Bot nicht belasten, helfen zwei Techniken: ein kurzlebiger Cache für häufig abgefragte Ergebnisse (zum Beispiel eine Map, die ein Abfrageergebnis einige Sekunden hält) und das Drosseln deiner Anfragen, falls du eine externe API nutzt. Denk trotzdem daran: die Wertvalidierung gehört in execute, denn der Nutzer muss nicht aus der Liste wählen; er kann den Wert von Hand tippen und absenden.

Häufige Fragen

Kann ich Autocomplete und setChoices zusammen verwenden?

Nein. An einer einzelnen Option verwendest du entweder statisches setChoices() oder setAutocomplete(true); beides geht nicht zusammen, und die Befehlsregistrierung wird abgelehnt, wenn du es versuchst. Hat deine Liste weniger als 25 Einträge und ändert sich nie, ist setChoices einfacher. Ist die Liste groß, dynamisch oder personalisiert, wechselst du zu Autocomplete.

Kann ich sicher sein, dass der Nutzer einen der Vorschläge gewählt hat?

Nein, das kannst du nicht garantieren. Vorschläge sind nur eine Bequemlichkeit; der Nutzer kann beliebigen Text tippen, der nicht in den Vorschlägen steht, und Enter drücken. Validiere den eingehenden Wert daher immer in der eigentlichen execute-Funktion des Befehls: prüfe, ob der Datensatz wirklich existiert, und gib andernfalls eine freundliche Fehlermeldung zurück.

Wie erkenne ich, welche fokussierte Option gerade getippt wird?

Hat ein Befehl mehrere Autocomplete-Optionen, musst du wissen, welche fokussiert ist. Der Aufruf interaction.options.getFocused(true) (mit true) liefert nicht nur den Wert, sondern das Objekt { name, value } zurück; aus dem Feld name liest du, welche Option getippt wird, und erzeugst die Vorschläge entsprechend.

Willst du clevere, schnelle Autocomplete-Befehle für deinen Bot? Ich kann dir Befehle mit datenbankgestützter Live-Suche, Caching und serverspezifischen Vorschlägen bauen. Kontaktiere mich und lass uns besprechen, was du brauchst.

Bu kategorideki tüm yazılar →

Devamı için