Discord botunuzun kullanıcılarla konuştuğu ana kapı artık slash command, yani sohbet kutusuna / yazınca açılan komut listesidir. Doğru kurgulanmış bir slash komut, kullanıcıdan gelen veriyi daha gönderilmeden doğrular, yanlış kişinin yönetim komutunu görmesini engeller ve DM ya da sunucu gibi hangi ortamda çalışacağını kendisi belirler. Kötü kurgulanmış bir komut ise listede iki kez görünür, hiç görünmez ya da herkesin kullanabildiği bir "ban" komutuna dönüşür.
Bu rehberde Discord'un resmî Application Commands belgesine ve discord.js rehberine dayanarak bir slash komutun bütün parçalarını ele alıyoruz: SlashCommandBuilder, seçenek (option) türleri, zorunlu alanlar ve hazır seçimler, autocomplete, alt komutlar ve gruplar, default_member_permissions, contexts ile integration_types, sunucuya özel ve global kayıt farkı ve sağ tık menüsünde çıkan kullanıcı ile mesaj komutları. Botunuz henüz yoksa önce Discord bot nasıl yapılır yazısıyla temel kurulumu tamamlayın.
Application command türleri

Discord'da slash komut, daha geniş bir yapı olan application command'ın bir türüdür. Bir komut nesnesinin type alanı komutun nerede görüneceğini belirler; alan verilmezse varsayılan değer 1 olur.
| Tür | Değer | Nerede görünür |
|---|---|---|
| CHAT_INPUT | 1 | Sohbet kutusuna / yazınca açılan liste (slash command) |
| USER | 2 | Bir kullanıcıya sağ tıklayınca ya da dokununca açılan "Apps" menüsü |
| MESSAGE | 3 | Bir mesaja sağ tıklayınca ya da dokununca açılan "Apps" menüsü |
| PRIMARY_ENTRY_POINT | 4 | Uygulamanın Activity'sini başlatan ana giriş komutu |
Komutlar bir bot kullanıcısına bağlı değildir; interaction modeliyle çalışır. Bir sunucuda komut oluşturabilmek için uygulamanın o sunucuda applications.commands kapsamıyla yetkilendirilmiş olması gerekir. Bu kapsam bot kapsamıyla birlikte otomatik gelir. Davet bağlantısının nasıl kurulduğunu bot davet linki rehberinde anlattık.
Adlandırma kuralları ve sınırlar
Komut kaydı reddedildiğinde sebep çoğu zaman bu tablodaki sınırlardan biridir. Değerler resmî belgedeki komut ve seçenek yapısından alınmıştır.
| Alan | Sınır |
|---|---|
| Komut ve seçenek adı (CHAT_INPUT) | 1-32 karakter; küçük harf varyantı olan harflerin küçük hâli kullanılmalı; boşluk yok, sembol olarak yalnızca - ve _ |
| USER ve MESSAGE komut adı | Büyük harf ve boşluk içerebilir |
| Açıklama (description) | CHAT_INPUT için 1-100 karakter; USER ve MESSAGE komutlarında gönderilemez |
| Seçenek sayısı | Komut başına en fazla 25 |
| Hazır seçim (choices) | Seçenek başına en fazla 25; seçim adı 1-100 karakter, string değer en fazla 100 karakter |
| Toplam metin | Ad, açıklama ve değerlerin toplamı komut başına en fazla 8000 karakter |
| Global komut sayısı | 100 CHAT_INPUT, 15 USER, 15 MESSAGE, 1 PRIMARY_ENTRY_POINT |
| Sunucu komutu sayısı | PRIMARY_ENTRY_POINT dışındaki türlerde her sunucu için aynı sayılar |
| Oluşturma limiti | Sunucu başına günde 200 komut oluşturma |
SlashCommandBuilder ile ilk komut
discord.js v14'te komut tanımı SlashCommandBuilder ile yazılır ve toJSON() çıktısı Discord'a gönderilir. Aşağıdaki /duyuru komutu zorunlu bir metin, isteğe bağlı bir kanal ve hazır seçimli bir öncelik alanı içeriyor. Ayrıca yalnızca "Sunucuyu Yönet" iznine sahip üyelere ve yalnızca sunucu içinde açılıyor.
const {
SlashCommandBuilder, PermissionFlagsBits, InteractionContextType, ChannelType, MessageFlags,
} = require('discord.js');
module.exports = {
data: new SlashCommandBuilder()
.setName('duyuru')
.setDescription('Seçilen kanala duyuru gönderir')
.addStringOption((o) =>
o.setName('metin').setDescription('Duyuru metni').setRequired(true).setMaxLength(2000))
.addChannelOption((o) =>
o.setName('kanal').setDescription('Hedef kanal').addChannelTypes(ChannelType.GuildText))
.addStringOption((o) =>
o.setName('oncelik').setDescription('Öncelik düzeyi').addChoices(
{ name: 'Normal', value: 'normal' },
{ name: 'Acil', value: 'acil' },
))
.setDefaultMemberPermissions(PermissionFlagsBits.ManageGuild)
.setContexts(InteractionContextType.Guild),
async execute(interaction) {
const metin = interaction.options.getString('metin', true);
const kanal = interaction.options.getChannel('kanal') ?? interaction.channel;
const oncelik = interaction.options.getString('oncelik') ?? 'normal';
await kanal.send(oncelik === 'acil' ? `**ACİL:** ${metin}` : metin);
await interaction.reply({ content: 'Duyuru gönderildi.', flags: MessageFlags.Ephemeral });
},
};Seçenek (option) türleri
Her seçenek türünün discord.js'te bir add...Option metodu ve okumak için bir get... metodu vardır. Değerler resmî "Application Command Option Type" tablosundandır.
| Tür (değer) | Builder metodu | Okuma | Not |
|---|---|---|---|
| STRING (3) | addStringOption | getString | min_length / max_length 0-6000 arası |
| INTEGER (4) | addIntegerOption | getInteger | Yalnızca tam sayı; min_value / max_value |
| BOOLEAN (5) | addBooleanOption | getBoolean | Evet / hayır |
| USER (6) | addUserOption | getUser, getMember | Kullanıcı seçici açılır |
| CHANNEL (7) | addChannelOption | getChannel | Kategoriler dahil; channel_types ile daraltılır |
| ROLE (8) | addRoleOption | getRole | Rol seçici |
| MENTIONABLE (9) | addMentionableOption | getMentionable | Kullanıcı ve rol birlikte |
| NUMBER (10) | addNumberOption | getNumber | Ondalıklı sayı |
| ATTACHMENT (11) | addAttachmentOption | getAttachment | Dosya yükletir; file_types ile filtrelenebilir |
| SUB_COMMAND (1) / SUB_COMMAND_GROUP (2) | addSubcommand / addSubcommandGroup | getSubcommand / getSubcommandGroup | Komutu dallara ayırır |
get... metotlarına ikinci parametre olarak true verirseniz seçenek boş geldiğinde discord.js hata fırlatır; zorunlu seçenekler için bu, null kontrolünü gereksiz kılar.
Zorunlu alanlar, choices ve ek doğrulama

- setRequired(true): Kullanıcı bu alanı doldurmadan komutu gönderemez. Her seçenek türüne uygulanabilir.
- addChoices(): STRING, INTEGER ve NUMBER seçeneklerinde kullanılır. Belge açıkça söylüyor: choices tanımlarsanız kullanıcının seçebileceği tek geçerli değerler bunlardır.
- setMinLength / setMaxLength: STRING seçenekleri için uzunluk sınırı.
- setMinValue / setMaxValue: INTEGER ve NUMBER için aralık sınırı.
- addChannelTypes(): Kanal seçicide yalnızca belirli kanal türlerini gösterir, örneğin
ChannelType.GuildText. - file_types: 5 Ağustos 2026 değişiklik kaydından beri ATTACHMENT seçenekleri,
image,video,audioya da.pdfgibi uzantılarla en fazla 10 dosya türüne göre filtrelenebiliyor. Filtre yalnızca dosya uzantısına bakar.
Autocomplete: yazdıkça öneri sunmak
25'ten fazla seçeneğiniz varsa ya da öneriler bir veritabanından geliyorsa sabit choices yerine autocomplete kullanın. Seçeneği setAutocomplete(true) ile işaretlersiniz; kullanıcı yazdıkça Discord botunuza APPLICATION_COMMAND_AUTOCOMPLETE (tür 4) interaction'ı gönderir. Odaktaki seçenek focused: true ile işaretlenir.
// Komut tanımı
.addStringOption((o) =>
o.setName('sehir').setDescription('Şehir adı').setAutocomplete(true))
// interactionCreate içinde
if (interaction.isAutocomplete()) {
const yazilan = interaction.options.getFocused().toLowerCase();
const sehirler = ['Adana', 'Ankara', 'Antalya', 'Bursa', 'İstanbul', 'İzmir'];
const sonuc = sehirler
.filter((s) => s.toLowerCase().startsWith(yazilan))
.slice(0, 25)
.map((s) => ({ name: s, value: s }));
await interaction.respond(sonuc);
}- Bir seçenekte hem
choiceshemautocompleteolamaz. - Yanıt en fazla 25 öneri içerebilir; boş dizi gönderirseniz kullanıcı eşleşme olmadığını görür.
- Autocomplete interaction'ı da 3 saniye içinde yanıtlanmalıdır ve ertelenemez (defer edilemez). Yavaş bir API'den veri çekiyorsanız sonuçları önbelleğe alın.
- Öneriler zorunlu değildir; kullanıcı listede olmayan bir değer de gönderebilir.
- Birden çok autocomplete alanı varsa
getFocused(true)ile odaktaki seçeneğin adını da alabilirsiniz.
Alt komutlar ve alt komut grupları
/rol ekle, /rol kaldir gibi ilişkili işlemleri tek komut altında toplamak için subcommand kullanılır. Belgeye göre desteklenen tek iç içe yapı şudur: üst komut, alt komut grupları içerebilir ve bu gruplar alt komutlar içerebilir. Grubun içinde grup ya da alt komutun içinde grup kullanılamaz.
const data = new SlashCommandBuilder()
.setName('ayar')
.setDescription('Sunucu ayarları')
.addSubcommand((s) => s.setName('goster').setDescription('Mevcut ayarları gösterir'))
.addSubcommandGroup((g) =>
g.setName('log').setDescription('Log ayarları')
.addSubcommand((s) =>
s.setName('kanal').setDescription('Log kanalını belirler')
.addChannelOption((o) => o.setName('kanal').setDescription('Kanal').setRequired(true)))
.addSubcommand((s) => s.setName('kapat').setDescription('Logları kapatır')));
// Çalıştırırken
const grup = interaction.options.getSubcommandGroup(false); // 'log' ya da null
const alt = interaction.options.getSubcommand(); // 'goster', 'kanal', 'kapat'
Yetki: default_member_permissions
default_member_permissions alanı, komutu kullanabilmek için üyenin sahip olması gereken izinleri bit dizisi (bitfield) olarak taşır. discord.js'te setDefaultMemberPermissions() ile ayarlanır. Birden fazla izin | ile birleştirilirse üyenin hepsine sahip olması gerekir; "şu izinlerden biri yeterli" diye bir seçenek yoktur.
- Değeri
"0"(discord.js'te0) yaparsanız komut, özel bir izin tanımlanmadığı sürece yöneticiler dışında herkesten gizlenir. - Bu bir varsayılandır. Sunucu yöneticileri "Server Settings" altındaki "Integrations" ekranından komut bazında kullanıcı, rol ve kanal izinlerini değiştirebilir.
- Administrator iznine sahip üyeler her komutu kullanabilir. Yalnızca geliştiriciye ait
evalgibi komutları global dağıtmayın, kendi test sunucunuzla sınırlayın. - Komut izinlerini API ile düzenlemek (PUT .../permissions) bot token ile çalışmaz;
applications.commands.permissions.updatekapsamına sahip bir Bearer token gerekir.
Rol sıralaması ve bot izinlerinin ayrıntısı için Discord bot izinleri ve rol hiyerarşisi yazısına bakabilirsiniz.
contexts ve integration_types: komut nerede çalışır?

Eskiden DM'de kullanımı kapatmak için kullanılan dm_permission alanı belgede kullanımdan kaldırılmış (deprecated) olarak işaretli; yerine contexts kullanılıyor. İki alan da yalnızca global komutlarda geçerlidir.
| Alan | Değerler | Anlamı |
|---|---|---|
| contexts | GUILD (0), BOT_DM (1), PRIVATE_CHANNEL (2) | Komutun istemcide nerede kullanılabileceği |
| integration_types | GUILD_INSTALL, USER_INSTALL | Uygulama sunucuya mı, kullanıcıya mı yüklendiğinde komutun var olacağı |
PRIVATE_CHANNEL bağlamı yalnızca kullanıcıya yüklenen komutlar için anlamlıdır, yani komutun integration_types değeri USER_INSTALL içermelidir. Sunucu komutları BOT_DM bağlamını desteklemez. discord.js'te bağlamı setContexts(InteractionContextType.Guild) gibi ayarlarsınız. Kullanıcıya yüklenen uygulamaların ayrıntısı user install rehberinde.
Sunucu (guild) ve global kayıt: ne zaman yeniden kaydetmeli?
Komutlar yalnızca HTTP API ile kaydedilir. İki kapsam vardır ve farkları önemlidir:
| Guild komutu | Global komut | |
|---|---|---|
| Nerede görünür | Yalnızca belirtilen sunucuda | Uygulamanın yüklü olduğu tüm sunucularda (ve bağlama göre DM'de) |
| Güncelleme | Belgeye göre anında | Read-repair: eski sürüm kullanılırsa Discord reddedip yeniler |
| Önerilen kullanım | Geliştirme ve test | Yayına hazır komutlar |
| Uç nokta | /applications/{id}/guilds/{guild_id}/commands | /applications/{id}/commands |
discord.js rehberinin önerdiği yöntem, komutları botu başlatan koddan ayrı, küçük bir deploy-commands.js betiğiyle kaydetmektir:
const { REST, Routes } = require('discord.js');
const komutlar = [require('./commands/duyuru').data.toJSON()];
const rest = new REST().setToken(process.env.DISCORD_TOKEN);
(async () => {
// Test sunucusu: anında görünür
await rest.put(
Routes.applicationGuildCommands(process.env.CLIENT_ID, process.env.GUILD_ID),
{ body: komutlar },
);
// Yayına alırken global:
// await rest.put(Routes.applicationCommands(process.env.CLIENT_ID), { body: komutlar });
console.log('Komutlar kaydedildi');
})();- Ne zaman çalıştırılır: Yalnızca komutun tanımı (ad, açıklama, seçenekler, izinler) değiştiğinde.
executefonksiyonundaki kod değişiklikleri için yeniden kayıt gerekmez. - Her açılışta kaydetmeyin: Günlük oluşturma limiti olduğu için kaydı
readyolayına koymak önerilmez. - PUT dikkat: Bulk overwrite (PUT) o kapsamdaki tüm komut türlerinin listesini değiştirir; slash, user ve message komutlarının hepsini aynı dizide göndermelisiniz. Var olmayan komutlar günlük oluşturma limitine sayılır.
- POST bir upsert'tür: Aynı adla yeni komut göndermek mevcut komutu günceller.
- Silmek: Tek komut için DELETE uç noktası, bir kapsamın tamamı için boş dizi ile PUT kullanılır.
Token ve ID'leri koda yazmak yerine .env dosyasında tutun; neden önemli olduğunu bot token güvenliği yazısında anlattık.
Komutları tek yerden karşılamak
Tüm komut türleri Events.InteractionCreate olayına düşer. Basit bir yönlendirici şöyle görünebilir:
client.on(Events.InteractionCreate, async (interaction) => {
if (interaction.isChatInputCommand()) {
const komut = client.commands.get(interaction.commandName);
if (!komut) return;
try {
await komut.execute(interaction);
} catch (hata) {
console.error(hata);
}
} else if (interaction.isAutocomplete()) {
const komut = client.commands.get(interaction.commandName);
await komut?.autocomplete?.(interaction);
}
});Komut 3 saniyeden uzun sürüyorsa kullanıcı "Etkileşim başarısız oldu" uyarısı görür. Bunun çözümünü etkileşim başarısız oldu hatası yazısında ayrıntılı anlattık. Komut yanıtına buton veya menü eklemek için buton, select menu ve modal rehberine geçebilirsiniz.
Sık Sorulan Sorular
Komutum listede görünmüyor, neden?
En sık sebepler: kayıt betiği hiç çalıştırılmadı ya da hata verdi, uygulama o sunucuda applications.commands kapsamıyla eklenmedi, komut default_member_permissions ile sizden gizlendi ya da contexts o ortamı kapsamıyor. Belgeye göre bir komutu kullanma izniniz yoksa komut seçicide görünmez. Test ederken guild kaydı kullanın; belgeye göre guild komutları anında güncellenir.
Komutlarım listede iki kez görünüyor
Aynı adlı bir komutu hem global hem guild olarak kaydettiyseniz ikisi de görünür; belge bu ikiliye açıkça izin veriyor. Test bittiğinde guild komutlarını boş dizi ile PUT ederek temizleyin. discord.js rehberi geliştirme için ayrı bir test uygulaması kullanmayı da öneriyor.
Yönetici olmayanlardan komutu tamamen gizleyebilir miyim?
setDefaultMemberPermissions(0) ile komut yöneticiler dışında herkesten gizlenir. Ancak Administrator iznine sahip üyelerin bir komutu kullanmasını engellemek mümkün değildir.
Türkçe komut adı kullanabilir miyim?
Ad kuralı Unicode harflere izin veriyor, ancak küçük harf varyantı olan harflerin küçük hâli kullanılmalı. Daha güvenli yol, adı ASCII tutup setNameLocalizations() ve setDescriptionLocalizations() ile Türkçe (tr) yerelleştirme eklemektir.
discord.py ile de aynı şeyler yapılabilir mi?
Evet. Kavramlar API seviyesinde aynıdır; discord.py 2.x'te komutlar app_commands ve bir CommandTree ile tanımlanıp tree.sync() ile kaydedilir. Ayrıntılar için discord.py ile bot yapımı yazısına bakın.
Marka adları sahiplerine aittir. Bu yazı bağımsız bir teknik rehberdir; adı geçen kuruluşlarla bayilik veya ortaklık ilişkisi bulunduğu anlamına gelmez.
İlgili içerikler: Discord bot nasıl yapılır · Etkileşim başarısız oldu hatası · Buton, select menu ve modal · User install uygulamalar · Bot izinleri ve rol hiyerarşisi · Discord bot kütüphaneleri karşılaştırma · Node.js nedir