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

Discord Modal (Form) Oluşturma Rehberi

Bir discord modal, kullanıcıdan sohbet kanalını kirletmeden, düzenli ve yapılandırılmış veri toplamanın en temiz yoludur. Sunucularda sık gördüğün destek talebi (ticket) formları, başvuru sistemleri, geri bildirim kutuları ya da "öneri gönder" akışlarının arkasında genellikle bir modal vardır: kullanıcı bir butona ya da komuta tıklar, ekranın ortasında küçük bir form penceresi açılır, alanları doldurur ve gönderir. Bu rehberde discord.js ile sıfırdan bir modal oluşturmayı, alanları doğrulamayı ve gönderilen veriyi işlemeyi adım adım anlatıyorum.

Modal nedir, ne zaman kullanılır?

Modal, Discord'un sunduğu bir popup form bileşenidir. Bir buton, seçim menüsü veya slash komutu gibi bir etkileşime yanıt olarak açılabilir. Bir modalın en fazla 5 metin giriş alanı (text input) olabilir ve her alan ya tek satırlık (Short) ya da çok satırlık (Paragraph) olur.

  • İyi kullanım: ticket açma, başvuru formu, hata bildirimi, isim/sebep girişi gibi serbest metin gerektiren akışlar.
  • Kötü kullanım: evet/hayır soruları veya sınırlı seçenekler — bunlar için buton ya da select menü daha doğru.

Önemli bir kısıt: modalı yalnızca bir kullanıcı etkileşimine doğrudan yanıt olarak açabilirsin. Yani önce mesaj gönderip sonra modal açamazsın; interaction.showModal() çağrısı, etkileşime verilen ilk yanıt olmalıdır.

Gereksinimler ve kurulum

Bu örnek discord.js v14 içindir. Node.js 18 veya üzeri kurulu olsun. Yeni bir klasörde:

npm init -y
npm install discord.js

Bot uygulamanı Discord Developer Portal'da oluşturduğunu, tokenını aldığını ve botu sunucuna applications.commands kapsamıyla davet ettiğini varsayıyorum.

Modalı oluşturmak

Bir modal dört yapı taşından oluşur: ModalBuilder (pencerenin kendisi), TextInputBuilder (her alan), TextInputStyle (kısa/uzun) ve alanları saran ActionRowBuilder. Her metin alanı kendi action row'una konur.

const {
  ModalBuilder,
  TextInputBuilder,
  TextInputStyle,
  ActionRowBuilder,
} = require('discord.js');

function buildSupportModal() {
  const modal = new ModalBuilder()
    .setCustomId('support_modal')
    .setTitle('Destek Talebi');

  const konu = new TextInputBuilder()
    .setCustomId('konu')
    .setLabel('Konu')
    .setStyle(TextInputStyle.Short)
    .setMinLength(3)
    .setMaxLength(80)
    .setRequired(true);

  const aciklama = new TextInputBuilder()
    .setCustomId('aciklama')
    .setLabel('Sorununu açıkla')
    .setStyle(TextInputStyle.Paragraph)
    .setPlaceholder('Mümkün olduğunca ayrıntı ver...')
    .setMaxLength(1000)
    .setRequired(true);

  modal.addComponents(
    new ActionRowBuilder().addComponents(konu),
    new ActionRowBuilder().addComponents(aciklama),
  );

  return modal;
}

Burada customId alanları kritik: hem modalın hem her input'un benzersiz bir customId'si olmalı, çünkü gönderim geldiğinde veriyi bu kimliklerle okuyacağız.

Modalı açmak

Modalı bir slash komutuyla açalım. Slash komut etkileşimi geldiğinde showModal çağırıyoruz. Dikkat: modal açacaksan önce deferReply ya da reply çağırma — modal, etkileşimin ilk yanıtı olmak zorunda.

client.on('interactionCreate', async (interaction) => {
  if (interaction.isChatInputCommand() && interaction.commandName === 'destek') {
    await interaction.showModal(buildSupportModal());
  }
});

Aynı modalı bir butondan da açabilirsin; tek fark, etkileşim türünü interaction.isButton() ile kontrol etmen olur. Mantık aynı kalır.

Gönderimi işlemek ve doğrulamak

Kullanıcı formu doldurup gönderince yeni bir etkileşim gelir. Bunu interaction.isModalSubmit() ile yakalar, customId ile hangi modal olduğunu ayırt eder ve fields.getTextInputValue() ile alan değerlerini okuruz.

client.on('interactionCreate', async (interaction) => {
  if (!interaction.isModalSubmit()) return;
  if (interaction.customId !== 'support_modal') return;

  const konu = interaction.fields.getTextInputValue('konu').trim();
  const aciklama = interaction.fields.getTextInputValue('aciklama').trim();

  // Discord min/max length doğrulaması yapar; yine de kendi kontrolünü ekle
  if (konu.length < 3) {
    return interaction.reply({
      content: 'Konu çok kısa görünüyor.',
      ephemeral: true,
    });
  }

  // Burada veriyi işle: DB'ye kaydet, bir kanala gönder, ticket aç...
  await interaction.reply({
    content: `Talebin alındı! Konu: **${konu}**`,
    ephemeral: true,
  });
});

Discord, setMinLength/setMaxLength ve setRequired kurallarını istemci tarafında zaten uygular, ama gerçek doğrulama (örneğin bir e-postanın formatı, bir sayının aralığı) sana kalmış. Geçersiz veride ephemeral: true ile yalnızca kullanıcının göreceği bir uyarı dönmek en temiz yöntemdir.

Veriyi anlamlı kullanmak: ticket örneği

Çoğu projede modal verisi bir log kanalına embed olarak gönderilir ya da bir veritabanına yazılır. Örneğin gelen talebi bir yetkili kanalına düşürmek:

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

const logKanali = interaction.guild.channels.cache.get('LOG_KANAL_ID');
const embed = new EmbedBuilder()
  .setTitle('Yeni Destek Talebi')
  .addFields(
    { name: 'Konu', value: konu },
    { name: 'Açıklama', value: aciklama },
    { name: 'Gönderen', value: `${interaction.user}` },
  )
  .setTimestamp();

await logKanali.send({ embeds: [embed] });

Bu noktadan sonra akışı dilediğince genişletebilirsin: butonla "kapat", "üstlen" gibi aksiyonlar ekleyebilir, talebi MySQL/SQLite'a kaydedebilir veya harici bir API'ye iletebilirsin.

Sık yapılan hatalar

  • Modal açmadan önce reply/defer çağırmak: bu "Interaction has already been acknowledged" hatası verir. Modal her zaman ilk yanıt olmalı.
  • customId çakışması: farklı modallara aynı kimliği vermek gönderim ayrımını bozar. Her modala ve her input'a benzersiz kimlik ver.
  • 3 saniye kuralı: modal gönderimine de hızlı yanıt vermelisin. Uzun bir işlem yapacaksan önce interaction.deferReply() deyip sonra editReply kullan.
  • 5 alan sınırını aşmak: Discord bir modalda en fazla 5 input'a izin verir; daha fazlasına ihtiyaç varsa akışı bölmen gerekir.

Sık Sorulan Sorular

Bir modalda kaç alan olabilir?

En fazla 5 metin giriş alanı. Her alan ayrı bir ActionRowBuilder içinde olmalı ve yalnızca metin tipindedir; modal içinde buton veya select menü kullanamazsın.

Modal içinde dropdown (select menü) kullanabilir miyim?

Hayır. Şu an modallar yalnızca metin giriş alanlarını destekler. Seçim gerekiyorsa önce bir select menü gösterip seçim sonrası modalı açabilir ya da seçimi modal dışında ayrı bir adımda alabilirsin.

Gönderilen veriyi neden okuyamıyorum?

Genellikle getTextInputValue() içine yazdığın id, input'un setCustomId() değeriyle birebir aynı değildir. İkisinin tam olarak eşleştiğinden emin ol.

Sunucun için profesyonel bir Discord botu mu istiyorsun? Ticket sistemleri, başvuru formları ve modal tabanlı akışları temiz ve sürdürülebilir biçimde kuruyorum. Projeni konuşmak için benimle iletişime geç.

Bu kategorideki tüm yazılar →

Devamı için