Otomatik tamamlama
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ım | Ne olur |
|---|---|
| 1 | Seç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. |
| 2 | Kiş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. |
| 3 | Sana INTERACTION_CREATE gelir, type: 4. data.options içinde focused: true olan seçenek kişinin yazdığı yerdir. |
| 4 | 3 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:
{
"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ı):
{
"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:
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
| Alan | Anlamı |
|---|---|
type | 4: otomatik tamamlama |
data.id, data.name | Komutun 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[].focused | Yalnızca kişinin yazdığı tek seçenekte true |
data.options[].value | Odaktaki seçenekte bitmemiş yazı: en çok 100 karakter metin, tam sayı ve sayı seçeneğinde sayı ya da "1." gibi metin |
data.resolved | Kişinin diğer seçeneklerde seçtiği kullanıcı, rol, kanal |
member, locale | Yazan kişi ve dili; DM'de member yerine user |
context | 0 sunucu, 1 botla DM. DM'de de aynı çalışır; tek fark kişinin user'da olmasıdır. |
token | Yalnı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
| Alan | Kural |
|---|---|
choices | Zorunlu, en çok 25 öğe. Boş liste "eşleşen yok" demektir. |
choices[].name | Kişinin gördüğü, en çok 100 karakter |
choices[].name_localizations | tr ve en karşılığı; kişi kendi dilindekini görür |
choices[].value | Odaktaki 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:
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:
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
| Anahtar | Değer | Saniye | Anlamı |
|---|---|---|---|
user | 5 | 1 | Kişi başına otomatik tamamlama isteği |
app | 20 | 1 | Uygulaman başına, tüm kişilerden |
shared | 200 | 1 | Tüm botlar birlikte |
choices | 25 | Yanıt başına öneri | |
autocomplete_value | 100 | Odaktaki 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
| Kod | Ne oldu | Ne yap |
|---|---|---|
interaction_response_invalid | Otomatik 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 denedin | Otomatik tamamlamaya yalnızca {"type":8,"data":{"choices":[...]}} gönder. |
validation_failed, choices: too_long | 25'ten çok öneri | İlk 25'i gönder. |
validation_failed, choices[0].value: invalid | Değ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_long | Ad 100 karakteri aşıyor | Adı kısalt. |
interaction_already_acknowledged | Aynı yazıya ikinci kez yanıt verdin | Her otomatik tamamlamaya bir kez yanıt ver. |
interaction_timeout | 3 saniye geçti | Listeyi bellekte tut, önbellekle. Yeniden deneme. |
rate_limited, X-RateLimit-Scope: shared | Aynı anda 8 bot isteği sınırı doldu | Bu yazıyı bırak; sıradaki gelecek. |
rate_limited, X-RateLimit-Scope: bot ya da global | Botunun 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ı