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

Discord Autocomplete: Slash Komutlara Dinamik Öneri

Discord autocomplete, bir slash komutun seçeneğine kullanıcı yazarken anlık, dinamik öneriler sunmanı sağlayan bir özelliktir. Sabit bir seçenek listesi (choices) yalnızca 25 öğeyle sınırlıyken ve tamamen statikken, autocomplete sayesinde kullanıcının yazdığı her harfe göre öneri listesini kendin üretirsin: veritabanından canlı arama, bir API'den sonuç çekme veya kullanıcının sunucusuna özel veriyi filtreleme. Bu yazıda discord.js v14 ile sıfırdan bir autocomplete sistemi kuracağız; bir komut seçeneğini autocomplete'e açmaktan, etkileşimi 3 saniyelik sınır içinde yanıtlamaya ve büyük veri kümelerini akıllıca filtrelemeye kadar her adımı ele alacağız.

Autocomplete ne zaman gerekir?

Discord'un sabit addChoices() yöntemi küçük, değişmeyen listeler için mükemmeldir: bir "renk" seçeneğine kırmızı, yeşil, mavi koymak gibi. Ama şu durumlarda yetersiz kalır:

  • Seçenek sayısı 25'i aşıyorsa (Discord'un sabit choices üst sınırı).
  • Liste dinamikse: kullanıcının kendi öğeleri, veritabanındaki kayıtlar, canlı API sonuçları.
  • Liste sunucuya veya kullanıcıya özelse: her sunucunun farklı etiketleri, her kullanıcının kendi çalma listesi gibi.

Bu noktada autocomplete devreye girer. Kullanıcı yazmaya başladığında bot, o anki girdiyi alır, kendi mantığını çalıştırır ve en fazla 25 öneri geri gönderir. Öneriler salt görseldir; kullanıcı yine de listede olmayan bir değer yazıp gönderebilir, bu yüzden komutu işlerken gelen değeri her zaman doğrulamalısın.

Komut seçeneğini autocomplete'e açmak

İlk adım, slash komut tanımında ilgili seçeneğe .setAutocomplete(true) eklemektir. Önemli kısıt: bir seçenekte aynı anda hem setChoices hem de autocomplete kullanamazsın; ikisinden birini seçmen gerekir. Autocomplete yalnızca string, integer ve number tipindeki seçeneklerde çalışır.

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

const data = new SlashCommandBuilder()
  .setName('ara')
  .setDescription('Bir öğeyi adıyla ara')
  .addStringOption(option =>
    option
      .setName('isim')
      .setDescription('Aramak istediğin öğenin adı')
      .setRequired(true)
      .setAutocomplete(true));   // statik choices yerine dinamik öneri

module.exports = { data, execute };

Komutu Discord'a kaydettiğinde (global veya guild bazlı) bu seçenek artık autocomplete moduna geçer. Kullanıcı seçeneğe yazmaya başladığı anda Discord, botuna normal komut çalıştırmasından farklı bir etkileşim türü gönderir; onu ayrı bir yerde yakalamamız gerekir.

Autocomplete etkileşimini yanıtlamak

Kullanıcı yazarken gelen olay bir ChatInputCommandInteraction değil, AutocompleteInteraction'dır. Bu yüzden interactionCreate dinleyicinde önce türü ayırman gerekir. Odaklanılan seçeneğin o anki değerini interaction.options.getFocused() ile okur, önerileri ise interaction.respond() ile döndürürsün:

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

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

Her komut dosyasına, normal execute fonksiyonunun yanında bir de autocomplete fonksiyonu ekliyoruz. Böylece her komut kendi öneri mantığını kendi içinde tutar:

async function autocomplete(interaction) {
  const focused = interaction.options.getFocused();   // kullanıcının yazdığı metin (string)

  const items = ['Kılıç', 'Kalkan', 'İksir', 'Yay', 'Asa', 'Zırh'];

  const filtered = items
    .filter(item => item.toLowerCase().startsWith(focused.toLowerCase()))
    .slice(0, 25);   // Discord en fazla 25 öneri kabul eder

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

Her öneri { name, value } biçiminde bir nesnedir. name kullanıcıya görünen etiket (en fazla 100 karakter), value ise komut çalıştığında execute içinde alacağın gerçek değerdir. value tipi seçeneğin tipiyle eşleşmelidir: string seçenekte string, integer seçenekte sayı. Bu ayrım çok kullanışlıdır; kullanıcıya "Kullanıcı Adı" gösterip arka planda gerçek bir id değerini değer olarak gönderebilirsin.

Veritabanından dinamik öneri üretmek

Autocomplete'in asıl gücü, önerileri canlı bir veri kaynağından beslemektir. Diyelim ki kullanıcının kendi oluşturduğu etiketleri öneriyoruz. Aramayı SQL tarafında LIKE ile yapıp sonucu doğrudan 25'le sınırlamak, binlerce kayıtta bile hızlı çalışır:

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

  // better-sqlite3 örneği — yalnızca bu sunucunun etiketleri
  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) }))
  );
}

Burada iki kritik nokta var. Birincisi, filtrelemeyi veritabanına yaptırmak: tüm kayıtları belleğe çekip JavaScript'te süzmek büyük tablolarda yavaştır; LIKE ? ve LIMIT 25 işi DB'ye devreder. İkincisi, kullanıcıya okunur ad (name) gösterip değer olarak benzersiz kimliği (id) göndermek; komutu işlerken arama yapmana gerek kalmadan kaydı doğrudan bulursun. Eşleşmeyi daha esnek istiyorsan LIKE '%${focused}%' kalıbıyla kelimenin ortasındaki geçişleri de yakalayabilirsin; ancak baştan eşleşme (${focused}%) genellikle daha alakalı sonuçlar verir ve indeksten yararlanabildiği için daha hızlıdır.

3 saniye kuralı, sınırlar ve performans

Autocomplete'te dikkat etmen gereken katı kurallar var:

  • 3 saniyelik sınır: Etkileşime üç saniye içinde respond() ile yanıt vermelisin, yoksa Discord isteği düşürür. deferReply burada çalışmaz; ertelenemez. Bu yüzden öneri sorgun hızlı olmalı.
  • 25 öğe üst sınırı: Diziyi her zaman .slice(0, 25) veya SQL LIMIT 25 ile kıs; fazlası hata verir.
  • Boş girdi: Kullanıcı henüz hiçbir şey yazmadıysa focused boş bir metindir. Bu durumda en popüler ya da en yeni öğeleri göstermek iyi bir varsayılan deneyim sunar.

Kullanıcı hızlı yazarken Discord her tuş vuruşunda yeni bir istek gönderebilir. Bu istekler botunu yormasın diye iki teknik işe yarar: sık erişilen sonuçlar için kısa ömürlü bir önbellek (örneğin sorgu sonucunu birkaç saniye tutan bir Map) ve harici bir API kullanıyorsan istekleri sınırlamak. Yine de unutma: değer doğrulaması execute içinde yapılmalı, çünkü kullanıcı listeden seçmek zorunda değildir; elle yazıp gönderebilir.

Sık Sorulan Sorular

Autocomplete ile setChoices'i birlikte kullanabilir miyim?

Hayır. Bir seçenekte ya statik setChoices() ya da setAutocomplete(true) kullanırsın; ikisi aynı anda olmaz ve denersen komut kaydı reddedilir. Listen 25 öğeden az ve hiç değişmiyorsa setChoices daha basittir. Liste büyük, dinamik veya kişiye özelse autocomplete'e geçersin.

Kullanıcının önerilerden birini seçtiğinden emin olabilir miyim?

Hayır, garanti edemezsin. Öneriler sadece bir kolaylıktır; kullanıcı önerilerde olmayan herhangi bir metni yazıp Enter'a basabilir. Bu yüzden komutun asıl execute fonksiyonunda gelen değeri mutlaka doğrula: kaydın gerçekten var olup olmadığını kontrol et ve yoksa nazik bir hata mesajı döndür.

Odaklanılan seçeneğin adını nasıl öğrenirim?

Bir komutta birden fazla autocomplete seçeneği varsa, hangisinin odakta olduğunu bilmen gerekir. interaction.options.getFocused(true) çağrısı (parametreyi true verince) yalnızca değeri değil, { name, value } nesnesini döndürür; name alanından hangi seçeneğin yazıldığını okuyup önerilerini ona göre üretirsin.

Botun için akıllı, hızlı autocomplete komutları mı istiyorsun? Veritabanı destekli canlı arama, önbellekleme ve sunucuya özel öneriler içeren komutları senin için kurabilirim. Benimle iletişime geç ve ihtiyacını konuşalım.

Bu kategorideki tüm yazılar →

Devamı için