Bir Discord botu büyümeye başladığında tek bir main.py dosyası hızla kontrolden çıkar; işte tam bu noktada discord.py cog yapısı devreye girer. Cog'lar, ilgili komutları, olay dinleyicilerini ve durum bilgisini tek bir sınıf altında toplayan modüllerdir. Her özelliği kendi dosyasına bölerek botunuzu temiz, okunabilir ve ölçeklenebilir bir mimariye kavuşturursunuz. Bu yazıda discord.py 2.x ile sıfırdan modüler bir bot iskeleti kuruyoruz.
Cog nedir ve neden kullanılır?
Bir cog, commands.Cog sınıfından türeyen ve içinde komutlarla dinleyicileri barındıran bir Python sınıfıdır. Botun çekirdeği çalışır halde kalırken cog'ları çalışma zamanında yükleyebilir, kaldırabilir veya yeniden yükleyebilirsiniz. Bunun pratikteki getirileri şöyle:
- Sorumluluk ayrımı: Moderasyon, müzik, ekonomi gibi her alan kendi dosyasında durur.
- Sıcak yeniden yükleme: Botu kapatmadan tek bir modülü
reloadederek değişiklikleri test edersiniz. - Ekip çalışması: Farklı geliştiriciler farklı cog'lar üzerinde çakışmadan çalışır.
- Daha az global durum: Her cog kendi durumunu sınıf içinde tutar.
Proje yapısı
Önerilen düzen, çekirdeği kök dizinde tutup tüm cog'ları ayrı bir klasöre koymaktır:
my-bot/
├── bot.py
├── cogs/
│ ├── moderation.py
│ ├── general.py
│ └── economy.py
├── requirements.txt
└── .env
Bağımlılıkları kurmak için sanal ortam kullanın ve requirements.txt içine en azından discord.py ile python-dotenv ekleyin:
python -m venv .venv
source .venv/bin/activate
pip install -U discord.py python-dotenv
Çekirdek dosya: bot.py
discord.py 2.x'te uzantıları yüklemenin doğru yeri setup_hook metodudur. Bu metot bot oturum açmadan önce çağrılır ve load_extension burada güvenle await edilebilir. commands.Bot sınıfını genişleterek temiz bir başlangıç noktası kuruyoruz:
import os
import asyncio
import discord
from discord.ext import commands
from dotenv import load_dotenv
load_dotenv()
INITIAL_EXTENSIONS = [
"cogs.general",
"cogs.moderation",
"cogs.economy",
]
class MyBot(commands.Bot):
def __init__(self):
intents = discord.Intents.default()
intents.message_content = True
super().__init__(command_prefix="!", intents=intents)
async def setup_hook(self):
for ext in INITIAL_EXTENSIONS:
await self.load_extension(ext)
# Slash komutlarını test sunucusuna senkronla
await self.tree.sync()
async def on_ready(self):
print(f"{self.user} olarak giriş yapıldı (ID: {self.user.id})")
async def main():
bot = MyBot()
await bot.start(os.environ["DISCORD_TOKEN"])
if __name__ == "__main__":
asyncio.run(main())
message_content intent'ini Discord Developer Portal'da da etkinleştirmeniz gerekir; aksi halde prefix komutları çalışmaz.
İlk cog'u yazmak
Her cog dosyasının iki parçası vardır: commands.Cog'tan türeyen sınıf ve dosyanın sonundaki asenkron setup fonksiyonu. discord.py 2.x'te setup artık async'tir ve cog'u await bot.add_cog() ile ekler:
import discord
from discord.ext import commands
class General(commands.Cog):
def __init__(self, bot: commands.Bot):
self.bot = bot
@commands.command(name="ping")
async def ping(self, ctx: commands.Context):
latency = round(self.bot.latency * 1000)
await ctx.send(f"Pong! {latency}ms")
@commands.Cog.listener()
async def on_member_join(self, member: discord.Member):
channel = member.guild.system_channel
if channel is not None:
await channel.send(f"Hoş geldin {member.mention}!")
async def setup(bot: commands.Bot):
await bot.add_cog(General(bot))
Dikkat edilecek noktalar: cog içindeki komutlarda ilk parametre her zaman self'tir, ve dinleyiciler @commands.Cog.listener() dekoratörüyle tanımlanır; @bot.event kullanılmaz.
Slash komutlarını cog'lara taşımak
Modern botlar artık eğik çizgi (slash) komutlarına yöneliyor. Bunları cog içinde app_commands ile tanımlarsınız:
from discord import app_commands
from discord.ext import commands
import discord
class Economy(commands.Cog):
def __init__(self, bot: commands.Bot):
self.bot = bot
@app_commands.command(name="balance", description="Bakiyeni gösterir")
async def balance(self, interaction: discord.Interaction):
await interaction.response.send_message(
"Bakiyen: 1000 coin", ephemeral=True
)
async def setup(bot: commands.Bot):
await bot.add_cog(Economy(bot))
Slash komutları Discord'a kaydedilmek için senkronlanmalıdır. Geliştirme sırasında belirli bir test sunucusuna senkronlamak (anında görünür), küresel senkrondan çok daha hızlıdır; küresel senkronizasyonun yayılması bir saate kadar sürebilir.
Cog'ları çalışma zamanında yönetmek
Bir yönetim cog'u ekleyerek botu yeniden başlatmadan modülleri yükleyebilir, kaldırabilir ve yeniden yükleyebilirsiniz. Bu, geliştirme döngüsünü ciddi biçimde hızlandırır:
class Admin(commands.Cog):
def __init__(self, bot: commands.Bot):
self.bot = bot
@commands.command()
@commands.is_owner()
async def reload(self, ctx, extension: str):
await self.bot.reload_extension(f"cogs.{extension}")
await ctx.send(f"`{extension}` yeniden yüklendi.")
async def setup(bot):
await bot.add_cog(Admin(bot))
@commands.is_owner() kontrolü bu hassas komutları yalnızca bot sahibiyle sınırlar. reload_extension, bir modülde sözdizimi hatası varsa eski sürümü korur ve hatayı fırlatır; bu yüzden istisnayı yakalayıp kullanıcıya bildirmek iyi bir alışkanlıktır.
Sık Sorulan Sorular
setup fonksiyonu neden async olmak zorunda?
discord.py 2.x ile birlikte kütüphanenin yaşam döngüsü tamamen asenkron hâle geldi. add_cog ve load_extension artık coroutine olduğundan, onları çağıran setup fonksiyonunun da async def olması ve içeride await kullanması gerekir.
Prefix komutları mı yoksa slash komutları mı kullanmalıyım?
Yeni projeler için slash komutları önerilir: Discord arayüzünde keşfedilebilir, otomatik tamamlanır ve message_content intent'i gerektirmez. Cog yapısı her ikisini de aynı sınıf içinde rahatça barındırabilir.
Bir cog'da hata olursa tüm bot çöker mi?
Hayır. load_extension sırasında bir cog yüklenemezse yalnızca o uzantı atlanır; try/except ile sarmaladığınızda diğer cog'lar sorunsuz yüklenmeye devam eder. Çalışma zamanında ise her cog kendi hata yakalama mekanizmasına sahiptir.
Botunuzu temiz bir cog mimarisine taşımak mı istiyorsunuz? Mevcut tek dosyalık botunuzu modüler yapıya geçirmek, slash komutlarını eklemek veya sıfırdan bir Discord botu kurmak için benimle iletişime geçin.