İçeriğe geç
N'oluyo
Rehberler

Otomatik tamamlama

Son güncelleme:

Otomatik tamamlama, kişi bir komut seçeneğine yazarken botunun ona öneriler sunmasıdır: sunucu yazılanı botuna gönderir, botun en çok 25 öneriyle yanıt verir ve kişi listeden seçer ya da kendi yazdığını gönderir.

Akış

AdımNe olur
1Seçeneği autocomplete: true ile tanımlarsın. Yalnızca metin (3), tam sayı (4) ve sayı (10) seçeneklerinde olur, choices ile birlikte olmaz.
2Kişi o seçeneğe yazar. Uygulaması yazma durduktan 300 ms sonra yazılanı sunucuya gönderir; kişi yazdıkça sana 300 ms'de bir yeni istek gelebilir.
3Sana INTERACTION_CREATE gelir, type: 4. data.options içinde focused: true olan seçenek kişinin yazdığı yerdir.
43 saniye içinde callback ile type: 8 ve data.choices gönderirsin. Her otomatik tamamlama tek yanıt alır.
5Öneriler yalnızca yazan kişiye gider. Kişi birini seçer ya da kendi yazdığıyla devam eder. Enter'a basınca komut sana her zamanki gibi type: 2 olarak gelir.

Geliştirici autocomplete açmadıkça kişinin yazdığı Enter'dan önce hiçbir bota gitmez.

Kişi ne görür

Kişi alana yazar; yazma 300 ms durunca önerilerin alanın üstünde bir listede açılır, her öneri yerelleştirilmiş adıyla (name_localized) görünür. ↑ ve ↓ öneri değiştirir, Enter ya da Tab seçer, Esc listeyi kapatır, tıklamak da seçer. Seçilen önerinin adı alanda görünür, sana çalıştırmada value'su gelir. Kişi sonra yeniden yazarsa değer onun yazdığına döner.

Kişi yazdıkça eski yazıya ait geç gelen yanıt listeyi değiştirmez; yalnızca son isteğin yanıtı gösterilir. Botun 1 saniye geç yanıt verse de öneriler gelir. Botun hiç yanıt vermezse ya da çevrimdışıysa liste açılmaz, kişiye hata gösterilmez ve alan serbest yazıya açık kalır: kişi Enter'a basınca komut yazdığı metinle gelir. Bu yüzden çalıştırmada değeri her zaman yeniden doğrula.

Odaktaki alanla birlikte yalnızca geçerli değeri olan diğer seçenekler gelir; yarım yazılmış sayı ya da dosya gelmez.

En küçük örnek

Seçeneği tanımla:

JSON
{
  "name": "hava",
  "description": "Bir şehrin havası",
  "options": [
    { "type": 3, "name": "sehir", "description": "Şehir", "required": true, "autocomplete": true }
  ]
}

Kişi ist yazınca sana şu gelir (kısaltıldı):

JSON
{
  "id": "111804206284800120",
  "type": 4,
  "token": "eyJpZCI6IjExMTgwNDIwNjI4NDgwMDEyMCJ9.bWFj",
  "locale": "tr",
  "data": {
    "id": "111560000000000000",
    "name": "hava",
    "type": 1,
    "options": [{ "name": "sehir", "type": 3, "value": "ist", "focused": true }]
  }
}

Önerilerle yanıt ver:

Bash
curl -X POST "https://noluyo.net/api/v1/interactions/$INTERACTION_ID/$INTERACTION_TOKEN/callback" \
  -H "Authorization: Bot $BOT_TOKEN" -H 'Content-Type: application/json' \
  -d '{"type":8,"data":{"choices":[{"name":"İstanbul","name_localizations":{"en":"Istanbul"},"value":"istanbul"}]}}'

Yanıt 204 ve gövdesizdir.

Gelen etkileşim

AlanAnlamı
type4: otomatik tamamlama
data.id, data.nameKomutun kimliği ve tanımdaki adı
data.optionsŞimdiye dek yazılanlar, tanımına göre denetlenmiş. Alt komut varsa içindeki options'tadır.
data.options[].focusedYalnızca kişinin yazdığı tek seçenekte true
data.options[].valueOdaktaki seçenekte bitmemiş yazı: en çok 100 karakter metin, tam sayı ve sayı seçeneğinde sayı ya da "1." gibi metin
data.resolvedKişinin diğer seçeneklerde seçtiği kullanıcı, rol, kanal
member, localeYazan kişi ve dili; DM'de member yerine user
context0 sunucu, 1 botla DM. DM'de de aynı çalışır; tek fark kişinin user'da olmasıdır.
tokenYalnızca callback için. Takip mesajı ve düzenleme almaz.

Zorunlu seçenekler henüz eksik olabilir. Dosya seçeneği otomatik tamamlamada gönderilmez; dosya ancak çalıştırmada gelir.

Yanıt: type 8

AlanKural
choicesZorunlu, en çok 25 öğe. Boş liste "eşleşen yok" demektir.
choices[].nameKişinin gördüğü, en çok 100 karakter
choices[].name_localizationstr ve en karşılığı; kişi kendi dilindekini görür
choices[].valueOdaktaki seçeneğin türünde; metin en çok 100 karakter. Seçilince bu gider, ad değil.

type: 8 yalnızca otomatik tamamlamaya, otomatik tamamlama yalnızca type: 8'e uyar. Öneriler kanala yazılmaz, mesaj değildir; bu yüzden tüm botların saniyede 70 yazmalık ortak tavanına sayılmaz. Yine interaction sınıfına ve aynı anda 8 bot isteği sınırına girer.

Değeri çalıştırırken yeniden doğrula

Kişi listeden seçmek zorunda değildir: kendi yazdığını gönderebilir, öneri gelmeden Enter'a basabilir ya da başka bir istemci kullanabilir. Sunucu çalıştırmadaki değeri önerilerine karşı denetlemez. type: 2 gelince değeri kendi kaynağında yeniden ara; bulamazsan kişiye flags: 64 ile ne yapacağını söyle. Önerdiğin bir değerin hâlâ geçerli olduğunu da varsayma: liste o arada değişmiş olabilir.

Hızlı kal

  • 3 saniye üst sınırdır; kişi o sırada yazmaya devam ediyor. 300 ms içinde yanıt vermeyi hedefle.

  • Uygulama yalnızca son yazının yanıtını gösterir. Eski bir yazıya geç gelen yanıt atılır; yanıt vermezsen alan serbest yazıma açık kalır.

  • Listeni bellekte tut, her istekte veritabanına ya da dış bir servise gitme. Dış kaynağa gitmen gerekiyorsa sonucu kısa süre önbellekle ve aynı yazı için kopya sorgu yapma.

  • Hata gelince yeniden deneme: 3 saniye geçtiyse yanıt artık işe yaramaz. Sıradaki yazı zaten gelecek.

Yerelleştirme

locale kişinin dilidir. Öneriyi name ile Türkçe yaz, name_localizations.en ile İngilizcesini ver; kişi kendi dilindekini görür. value dilden bağımsız kalsın ki çalıştırmada aynı değeri tanıyasın. Türkçe harfleri karşılaştırırken toLocaleLowerCase("tr") kullan: "İST" küçültülünce "ist" olur.

Tam örnek: şehir arama botu

Node.js 22 veya üstü yeter. Gateway sayfasındaki gateway.mjs ile Örnekler sayfasındaki api.mjs aynı klasörde olmalı. Bot açılınca /hava komutunu kaydeder; kişi şehir yazdıkça eşleşen şehirleri önerir, çalıştırılınca şehri listede yeniden arar:

sehir-botu.mjs
import { connect } from "./gateway.mjs";
import { call } from "./api.mjs";

const cities = [
  { value: "istanbul", tr: "İstanbul", en: "Istanbul" },
  { value: "izmir", tr: "İzmir", en: "Izmir" },
  { value: "ankara", tr: "Ankara", en: "Ankara" },
  { value: "bursa", tr: "Bursa", en: "Bursa" },
  { value: "antalya", tr: "Antalya", en: "Antalya" },
  { value: "eskisehir", tr: "Eskişehir", en: "Eskisehir" },
];

const commands = [{
  name: "hava",
  name_localizations: { en: "weather" },
  description: "Bir şehrin havası",
  description_localizations: { en: "The weather of a city" },
  options: [{ type: 3, name: "sehir", name_localizations: { en: "city" }, description: "Şehir",
    description_localizations: { en: "City" }, required: true, autocomplete: true }],
}];

const fold = (s) => s.toLocaleLowerCase("tr");
// Aynı yazı ikinci kez gelince liste yeniden taranmaz.
const cache = new Map();
function search(typed) {
  const key = fold(String(typed)).trim();
  if (!cache.has(key)) {
    if (cache.size > 1000) cache.clear();
    cache.set(key, cities.filter((c) => fold(c.tr).startsWith(key) || c.en.toLowerCase().startsWith(key)).slice(0, 25));
  }
  return cache.get(key);
}

// Odaktaki seçenek alt komutun içinde de olabilir.
const focused = (options = []) => options.find((o) => o.focused) ?? options.map((o) => focused(o.options)).find(Boolean);

async function onInteraction(d) {
  if (d.data.name !== "hava") return;
  const path = `/interactions/${d.id}/${d.token}/callback`;
  if (d.type === 4) {
    const choices = search(focused(d.data.options)?.value ?? "").map((c) => ({
      name: c.tr, name_localizations: { en: c.en }, value: c.value,
    }));
    await call("POST", path, { type: 8, data: { choices } });
    return;
  }
  if (d.type !== 2) return;
  // Kişi listeden seçmek zorunda değil: değer burada yeniden doğrulanır.
  const value = d.data.options?.find((o) => o.name === "sehir")?.value;
  const city = cities.find((c) => c.value === value) ?? search(value)[0];
  const en = d.locale === "en";
  if (!city) {
    await call("POST", path, { type: 4, data: { flags: 64,
      content: en ? "I do not know that city. Pick one from the list." : "Bu şehri bilmiyorum. Listeden birini seç." } });
    return;
  }
  await call("POST", path, { type: 4, data: { content: en ? `Weather in ${city.en}: sunny.` : `${city.tr} için hava: güneşli.` } });
}

let registered = false;

await connect({
  intents: 0,
  onEvent(t, d) {
    if (t === "READY" && !registered) {
      registered = true;
      call("PUT", `/applications/${d.application.id}/commands`, commands)
        .then(() => console.log("/hava kaydedildi"))
        .catch(console.error);
    }
    if (t === "INTERACTION_CREATE") onInteraction(d).catch((err) => console.error(err.message));
  },
});

Çalıştırmak için:

Bash
BOT_TOKEN='kopyaladigin_token' node sehir-botu.mjs

Örnek kişinin yazdığını konsola yazmaz; hata mesajında da yalnızca kod ve açıklama vardır.

Limitler

AnahtarDeğerSaniyeAnlamı
user51Kişi başına otomatik tamamlama isteği
app201Uygulaman başına, tüm kişilerden
shared2001Tüm botlar birlikte
choices25Yanıt başına öneri
autocomplete_value100Odaktaki yazı, karakter

İlk üçü kişinin isteğini sınırlar: aşılırsa sana bir şey gelmez, kişi 429 alır ve uygulaması bir sonraki yazıda yeniden dener. İnsanların mesajlaşması bundan etkilenmez.

Öneri yanıtların gerçek yanıtlarınla aynı bütçeyi paylaşır: callback rotası interaction sınıfındadır (saniyede 50) ve botunun genel sınırına (saniyede 50) sayılır. Bu yüzden uygulama sınırı 20'dedir; öneriler bütçenin tamamını yemez, komut ve basma yanıtlarına yer kalır. Öneri yanıtlarını hızlı ve tek tut ki bütçe gerçek yanıtlara kalsın.

Hatalar

KodNe olduNe yap
interaction_response_invalidOtomatik tamamlamaya type: 8 dışında yanıt verdin, type: 8'de choices yok ya da yanında content, embeds, flags gibi mesaj alanı var, başka bir etkileşime type: 8 gönderdin ya da bu token'la takip mesajı veya düzenleme denedinOtomatik tamamlamaya yalnızca {"type":8,"data":{"choices":[...]}} gönder.
validation_failed, choices: too_long25'ten çok öneriİlk 25'i gönder.
validation_failed, choices[0].value: invalidDeğer seçeneğin türünde değil, örneğin metin seçeneğine sayıSeçeneğin türünde değer gönder.
validation_failed, choices[0].name: too_longAd 100 karakteri aşıyorAdı kısalt.
interaction_already_acknowledgedAynı yazıya ikinci kez yanıt verdinHer otomatik tamamlamaya bir kez yanıt ver.
interaction_timeout3 saniye geçtiListeyi bellekte tut, önbellekle. Yeniden deneme.
rate_limited, X-RateLimit-Scope: sharedAynı anda 8 bot isteği sınırı dolduBu yazıyı bırak; sıradaki gelecek.
rate_limited, X-RateLimit-Scope: bot ya da globalBotunun interaction sınıfı ya da genel sınırı doldu; öneriler ve gerçek yanıtlar aynı bütçeden harcarÖneriyi bırak, bekleme. Gerçek yanıtlar için retry_after kadar bekle.

Kişinin alabileceği hatalar (validation_failed, command_forbidden, command_outdated, rate_limited, interaction_busy) sana gelmez; Hatalar sayfasında.

Bu sayfayı yapay zekaya verirsen

Sırada ne var

  • Komutlar: seçenek türleri, alt komutlar ve kayıt rotaları

  • Etkileşimler: komut seçildikten sonra gönderilen asıl yanıt türleri

  • Hız sınırları: otomatik tamamlama ve etkileşim hız sınırları