Python biliyorsanız Discord botu yazmak için yeni bir dil öğrenmeniz gerekmez. discord.py, Discord API'si için uzun süredir geliştirilen, asyncio tabanlı bir Python kütüphanesidir ve 2.x sürümüyle slash komutlarını app_commands modülü üzerinden doğrudan destekler.

Bu rehberde discord.py'yi kuracak, intent'leri ayarlayacak, Client ile commands.Bot arasında seçim yapacak, ilk slash komutlarınızı yazıp senkronize edecek ve kodu cog'larla düzenleyeceksiniz. Developer Portal'da uygulama açma, token alma ve botu sunucuya ekleme adımları dilden bağımsızdır; bunları Discord bot nasıl yapılır ve botu sunucuya ekleme yazılarında anlattık.

Gereksinimler ve kurulum

discord.py belgelerine göre kütüphane Python 3.8 veya üzerini gerektirir. Projeyi sistem genelindeki paketlerden ayırmak için sanal ortam (virtual environment) kullanmanız önerilir:

bash
mkdir discord-bot
cd discord-bot
python3 -m venv bot-env
source bot-env/bin/activate
pip install -U discord.py

Windows'ta sanal ortamı bot-env\Scripts\activate.bat ile etkinleştirirsiniz. Sanal ortam kullanmadan kuracaksanız belgeler Windows için py -3 -m pip install -U discord.py komutunu veriyor; ancak py -3 ile çalıştırılan betiklerin etkin sanal ortamı yok saydığını unutmayın. Ses desteği gerekiyorsa discord.py[voice] paketini kurarsınız (müzik botları için bkz. Lavalink ile müzik botu). Python'a yeniyseniz Python nedir yazısına göz atabilirsiniz.

Kurulumdan sonra Developer Portal'dan aldığınız token'ı bir ortam değişkenine koyun. Token'ı koda yazmayın; nedenleri ve doğru saklama yöntemleri bot token güvenliği yazısında.

Intent'ler: discord.Intents

Intent'ler: discord.Intents: message_content, members, presences

discord.py'de istemciyi oluştururken hangi Gateway olaylarını almak istediğinizi bir discord.Intents nesnesiyle belirtirsiniz. discord.Intents.default() privileged olmayan intent'leri açar; belgeye göre privileged olan members bu varsayılana dahil değildir. Privileged intent'leri hem Developer Portal'daki "Privileged Gateway Intents" bölümünden açmanız hem de kodda belirtmeniz gerekir:

python
intents = discord.Intents.default()
intents.message_content = True  # yalnızca gerçekten gerekiyorsa
intents.members = True          # yalnızca gerçekten gerekiyorsa
Intent özelliğidiscord.py belgesine göre ne zaman gerekir
message_contentMessage.content, ekler, embed'ler, bileşenler veya anketleri okuyorsanız; commands eklentisini bahsetme (mention) olmayan bir prefix ile kullanıyorsanız
membersÜye katılma/ayrılma (on_member_join, on_member_remove), üye güncellemeleri, Guild.chunk veya Guild.fetch_members kullanıyorsanız
presencesMember.status veya Member.activity bilgilerini kullanıyorsanız

Yalnızca slash komutlarıyla çalışan bir bot için bu üçüne de genellikle ihtiyaç yoktur. Portalda açılmamış bir privileged intent isterseniz discord.py bağlantı sırasında PrivilegedIntentsRequired hatası verir. Kavramın tamamı için Discord intent nedir yazısına bakın.

Client mi, commands.Bot mu?

Client mi, commands.Bot mu? Nedir, Slash komut ağacı, Prefix komutları, Cog ve extension
discord.Clientcommands.Bot
NedirTemel istemci; olayları dinlerClient üzerine komut sistemi ekleyen alt sınıf
Slash komut ağacıKendiniz app_commands.CommandTree(self) oluşturursunuzKendi bot.tree nesnesi hazır gelir
Prefix komutlarıYokVar (command_prefix, @bot.command())
Cog ve extensionYokVar (add_cog, load_extension)
Ne zamanÇok küçük, tek dosyalık botlarBüyüyecek projelerin çoğu

discord.py'nin resmî örneklerinde de belirtildiği gibi commands.Bot kullandığınızda komut ağacını kendiniz oluşturmazsınız; bot kendi ağacını tutar. Prefix komutları kullanmayacak olsanız bile commands.Bot cog düzeni sayesinde kodun büyümesini kolaylaştırır. command_prefix parametresi zorunludur; yalnızca slash komut kullanıyorsanız commands.when_mentioned verebilirsiniz.

İlk slash komutları: app_commands

Aşağıdaki bot iki slash komutu tanımlar ve başlarken bunları test sunucunuza senkronize eder. Fonksiyonun docstring'i komut açıklaması olur; tür ipuçlu (type hint) parametreler Discord'da komut seçeneklerine dönüşür:

python
import os
import discord
from discord import app_commands
from discord.ext import commands

TEST_GUILD = discord.Object(id=int(os.getenv('GUILD_ID')))

class Bot(commands.Bot):
    def __init__(self):
        super().__init__(command_prefix=commands.when_mentioned, intents=discord.Intents.default())

    async def setup_hook(self):
        # Global komutlari test sunucusuna kopyala ve yalnizca orada senkronize et
        self.tree.copy_global_to(guild=TEST_GUILD)
        await self.tree.sync(guild=TEST_GUILD)

bot = Bot()

@bot.event
async def on_ready():
    print(f'Giriş yapıldı: {bot.user} (ID: {bot.user.id})')

@bot.tree.command()
async def ping(interaction: discord.Interaction):
    """Pong ile yanıt verir."""
    await interaction.response.send_message('Pong!')

@bot.tree.command()
@app_commands.describe(a='Birinci sayı', b='İkinci sayı')
async def topla(interaction: discord.Interaction, a: int, b: int):
    """İki sayıyı toplar."""
    await interaction.response.send_message(f'{a} + {b} = {a + b}', ephemeral=True)

bot.run(os.getenv('DISCORD_TOKEN'))

Önemli noktalar:

  • setup_hook, bot bağlanmadan önce bir kez çalışan asenkron bir kancadır; komut senkronizasyonu, cog yükleme ve veritabanı bağlantısı için doğru yerdir.
  • @app_commands.describe, seçeneklerin Discord'da görünen açıklamalarını verir. @app_commands.rename ile koddaki parametre adından farklı bir seçenek adı gösterebilirsiniz.
  • ephemeral=True, yanıtı yalnızca komutu kullanan kişinin görmesini sağlar.
  • Bir parametreyi isteğe bağlı yapmak için varsayılan değer verin veya Optional kullanın; discord.Member türü Discord'da üye seçici olarak görünür.

Çalıştırmak için ortam değişkenlerini verip betiği başlatın:

bash
export DISCORD_TOKEN='buraya-bot-tokeniniz'
export GUILD_ID='buraya-test-sunucusu-id'
python bot.py

Konsolda "Giriş yapıldı" satırını gördükten sonra sunucunuzda / yazarak komutları deneyebilirsiniz. Seçenekler, alt komut grupları ve otomatik tamamlama gibi konuların genel mantığı slash komut oluşturma yazısında.

tree.sync: en çok hata yapılan nokta

discord.py'de komutu kodda tanımlamak, Discord'un onu bilmesi için yetmez. Kütüphanenin SSS sayfasındaki ilk madde açık: komutlar görünmeden önce senkronize edilmelidir. Senkronizasyon, komut tanımlarınızı Discord API'sine kaydeden bir istektir ve şu kurallara dikkat etmek gerekir:

  • Guild senkronizasyonu test içindir. tree.sync(guild=...) yalnızca o sunucuya kaydeder. Discord belgesine göre guild komutları anında güncellenir; geliştirme için en hızlı yol budur.
  • Global senkronizasyon yayın içindir. Argümansız await bot.tree.sync() global komutları kaydeder; bu komutlar uygulamanın eklendiği tüm sunucularda görünür.
  • Her açılışta global sync yapmayın. discord.py'nin advanced_startup örneği, global veya her sunucu için senkronizasyonun yalnızca komutlar değiştiğinde yapılması gerektiğini belirtiyor. Discord'un belgesinde sunucu başına günde 200 komut oluşturma sınırı da var.
  • Komutları sync'ten önce yükleyin. Cog veya extension kullanıyorsanız önce onları yükleyin, sonra senkronize edin; aksi hâlde yüklenmemiş komutlar kaydedilmez.
  • Aynı komutu iki kez eklemeyin. SSS'ye göre tree.add_command(..., guild=...) yöntemini @tree.command() dekoratörüyle birlikte kullanmak yinelenen komutlara yol açar.

Pratik bir yaklaşım, geliştirme sırasında yukarıdaki gibi test sunucusuna senkronize etmek, yayına geçerken global senkronizasyonu yalnızca sahibin çalıştırabildiği bir komut veya ayrı bir betikle tetiklemektir. Global komutlar ile aynı adlı guild kopyaları aynı anda var olabildiği için test sunucusunda bir komutu iki kez görüyorsanız bunun nedeni genellikle budur.

Komutlarım hâlâ görünmüyor

Senkronizasyon başarılıysa ikinci olası neden davet linkidir. discord.py SSS'si botun bot scope'una ek olarak applications.commands scope'uyla davet edilmesini öneriyor. Linki doğru kurmak için Discord bot sunucuya ekleme yazısına bakın.

Uzun süren işler: defer ve followup

Uzun süren işler: defer ve followup

Discord, her etkileşime 3 saniye içinde ilk yanıtın verilmesini bekler. Yanıtınız harici bir API veya veritabanına bağlıysa önce etkileşimi erteleyin, iş bitince takip mesajı gönderin:

python
@bot.tree.command()
async def rapor(interaction: discord.Interaction):
    """Uzun süren bir rapor hazırlar."""
    await interaction.response.defer()
    sonuc = await rapor_hazirla()  # kendi asenkron fonksiyonunuz
    await interaction.followup.send(sonuc)

Bu kural çiğnendiğinde kullanıcı "Bu etkileşim başarısız oldu" uyarısını görür; diğer nedenler için etkileşim başarısız oldu hatası yazısına bakın.

Kodu cog'larla düzenlemek

Bot büyüdükçe tüm komutları tek dosyada tutmak zorlaşır. discord.py belgesine göre cog, komutları, olay dinleyicilerini ve bir miktar durumu tek bir sınıfta toplamanızı sağlar. Her cog commands.Cog sınıfından türetilir ve await bot.add_cog(...) ile kaydedilir. Cog içinde slash komutu için app_commands.command dekoratörü kullanılır:

python
import discord
from discord import app_commands
from discord.ext import commands

class Genel(commands.Cog):
    def __init__(self, bot: commands.Bot):
        self.bot = bot

    @app_commands.command()
    async def merhaba(self, interaction: discord.Interaction):
        """Selam verir."""
        await interaction.response.send_message(f'Merhaba {interaction.user.mention}!')

    @commands.Cog.listener()
    async def on_ready(self):
        print('Genel cog hazır')

Kaydı, senkronizasyondan önce setup_hook içinde yapın:

python
    async def setup_hook(self):
        await self.add_cog(Genel(self))
        self.tree.copy_global_to(guild=TEST_GUILD)
        await self.tree.sync(guild=TEST_GUILD)

Cog'lar genellikle extension'larla birlikte kullanılır: her cog ayrı bir dosyada durur ve await bot.load_extension(...) ile yüklenir. Cog içindeki dinleyicilerin @commands.Cog.listener() ile açıkça işaretlenmesi gerektiğini unutmayın.

Botu sürekli çalıştırmak

bot.run() çağrısı süreç açık kaldığı sürece botu çevrimiçi tutar; terminali kapattığınızda bot da kapanır. Kalıcı çalıştırma için kodu bir sunucuda bir sanal ortamla kurup systemd, Docker veya Pterodactyl gibi bir süreç yöneticisine bağlayın. Ayrıntılı adımlar için Discord botu 7/24 aktif tutma, systemd servis dosyası rehberi ve Node ve Python için Dockerfile yazılarına bakın. Basit veri saklama ihtiyacı için SQLite iyi bir başlangıçtır.

Sık Sorulan Sorular

discord.py mı, Pycord mu, nextcord mu?

Pycord ve nextcord, discord.py'den türemiş ve benzer bir API sunan kütüphanelerdir; slash komut sözdizimleri farklıdır, bu yüzden örnekleri birbirine karıştırmayın. Bu yazıdaki kod yalnızca discord.py 2.x içindir. Kütüphaneleri karşılaştırmak için Discord bot kütüphaneleri karşılaştırması.

Prefix komutlarını (!komut) kullanabilir miyim?

commands.Bot ile @bot.command() tanımlayabilirsiniz, ancak bahsetme olmayan bir prefix kullanıyorsanız message_content privileged intent'i gerekir. Yeni projelerde slash komutları önerilir; her ikisini tek tanımla desteklemek için discord.py hybrid komutlar da sunar.

Komut değiştirdim ama Discord'da eski hâli görünüyor

Değişiklikten sonra yeniden senkronize ettiğinizden emin olun. Test sunucusunda guild senkronizasyonu kullanıyorsanız Discord belgesine göre güncelleme anında yansır. Yalnızca komutun çalıştırdığı kodu değiştirdiyseniz yeniden senkronizasyon gerekmez; ad, açıklama veya seçenek değiştiyse gerekir.

Hata: PrivilegedIntentsRequired

Kodda açtığınız bir privileged intent (members, presences veya message_content) Developer Portal'da kapalıdır. Portaldaki "Bot" sayfasında ilgili anahtarı açın ya da koddan kaldırın. Büyük botlarda başvuru süreci için bot doğrulama ve privileged intent.

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 · Bot kütüphaneleri karşılaştırması · Slash komut oluşturma · Intent nedir · Python nedir · systemd servis dosyası