Sobald ein Discord-Bot zu wachsen beginnt, gerät eine einzige main.py-Datei schnell außer Kontrolle — und genau hier kommt die discord.py cog-Struktur ins Spiel. Cogs sind Module, die zusammengehörige Befehle, Event-Listener und Zustand in einer einzigen Klasse bündeln. Indem du jede Funktion in eine eigene Datei aufteilst, gibst du deinem Bot eine saubere, lesbare und skalierbare Architektur. In dieser Anleitung bauen wir mit discord.py 2.x von Grund auf ein modulares Bot-Gerüst.
Was ist ein Cog und warum sollte man es nutzen?
Ein Cog ist eine Python-Klasse, die von commands.Cog erbt und Befehle sowie Listener enthält. Während der Bot-Kern weiterläuft, kannst du Cogs zur Laufzeit laden, entladen oder neu laden. Die praktischen Vorteile sind:
- Trennung der Zuständigkeiten: jeder Bereich — Moderation, Musik, Wirtschaft — liegt in einer eigenen Datei.
- Hot Reloading: lade ein einzelnes Modul neu, ohne den Bot herunterzufahren, um Änderungen zu testen.
- Teamarbeit: verschiedene Entwickler arbeiten konfliktfrei an verschiedenen Cogs.
- Weniger globaler Zustand: jedes Cog hält seinen eigenen Zustand innerhalb der Klasse.
Projektstruktur
Das empfohlene Layout hält den Kern im Wurzelverzeichnis und legt alle Cogs in einen eigenen Ordner:
my-bot/
├── bot.py
├── cogs/
│ ├── moderation.py
│ ├── general.py
│ └── economy.py
├── requirements.txt
└── .env
Verwende eine virtuelle Umgebung, um Abhängigkeiten zu installieren, und füge mindestens discord.py und python-dotenv in deine requirements.txt ein:
python -m venv .venv
source .venv/bin/activate
pip install -U discord.py python-dotenv
Die Kerndatei: bot.py
In discord.py 2.x ist die richtige Stelle zum Laden von Erweiterungen die Methode setup_hook. Sie wird aufgerufen, bevor sich der Bot anmeldet, und load_extension kann dort gefahrlos mit await aufgerufen werden. Wir leiten commands.Bot ab, um einen sauberen Einstiegspunkt zu schaffen:
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-Befehle synchronisieren
await self.tree.sync()
async def on_ready(self):
print(f"Angemeldet als {self.user} (ID: {self.user.id})")
async def main():
bot = MyBot()
await bot.start(os.environ["DISCORD_TOKEN"])
if __name__ == "__main__":
asyncio.run(main())
Du musst außerdem den message_content-Intent im Discord Developer Portal aktivieren; andernfalls funktionieren Präfix-Befehle nicht.
Dein erstes Cog schreiben
Jede Cog-Datei besteht aus zwei Teilen: einer Klasse, die von commands.Cog erbt, und einer asynchronen setup-Funktion am Ende der Datei. In discord.py 2.x ist setup nun async und fügt das Cog mit await bot.add_cog() hinzu:
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"Willkommen {member.mention}!")
async def setup(bot: commands.Bot):
await bot.add_cog(General(bot))
Zwei Dinge sind zu beachten: Innerhalb eines Cogs ist der erste Parameter jedes Befehls immer self, und Listener werden mit dem Dekorator @commands.Cog.listener() definiert — niemals mit @bot.event.
Slash-Befehle in Cogs verschieben
Moderne Bots setzen zunehmend auf Slash-Befehle. Du definierst sie innerhalb eines Cogs mit app_commands:
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="Zeigt dein Guthaben")
async def balance(self, interaction: discord.Interaction):
await interaction.response.send_message(
"Dein Guthaben: 1000 Münzen", ephemeral=True
)
async def setup(bot: commands.Bot):
await bot.add_cog(Economy(bot))
Slash-Befehle müssen synchronisiert werden, um bei Discord registriert zu sein. Während der Entwicklung ist das Synchronisieren auf einen bestimmten Testserver (der sofort erscheint) deutlich schneller als eine globale Synchronisierung; die globale Verbreitung kann bis zu einer Stunde dauern.
Cogs zur Laufzeit verwalten
Durch Hinzufügen eines Admin-Cogs kannst du Module laden, entladen und neu laden, ohne den Bot neu zu starten. Das beschleunigt den Entwicklungszyklus erheblich:
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}` neu geladen.")
async def setup(bot):
await bot.add_cog(Admin(bot))
Die Prüfung @commands.is_owner() beschränkt diese sensiblen Befehle ausschließlich auf den Bot-Besitzer. Wenn ein Modul einen Syntaxfehler enthält, behält reload_extension die alte Version bei und löst den Fehler aus — daher ist es eine gute Angewohnheit, die Ausnahme abzufangen und dem Benutzer zu melden.
Häufige Fragen
Warum muss die setup-Funktion async sein?
Mit discord.py 2.x wurde der Lebenszyklus der Bibliothek vollständig asynchron. Da add_cog und load_extension nun Coroutinen sind, muss die setup-Funktion, die sie aufruft, ebenfalls async def sein und intern await verwenden.
Sollte ich Präfix-Befehle oder Slash-Befehle verwenden?
Für neue Projekte werden Slash-Befehle empfohlen: Sie sind in der Discord-Oberfläche auffindbar, vervollständigen sich automatisch und benötigen den message_content-Intent nicht. Die Cog-Struktur beherbergt beide problemlos in derselben Klasse.
Stürzt der ganze Bot ab, wenn ein Cog kaputtgeht?
Nein. Wenn ein Cog beim load_extension nicht geladen werden kann, wird nur diese Erweiterung übersprungen; umschließe sie mit try/except, und die anderen Cogs laden weiterhin einwandfrei. Zur Laufzeit hat jedes Cog seinen eigenen Bereich für die Fehlerbehandlung.
Möchtest du deinen Bot zu einer sauberen Cog-Architektur migrieren? Ob du einen bestehenden Ein-Datei-Bot in eine modulare Struktur überführen, Slash-Befehle hinzufügen oder einen Discord-Bot von Grund auf bauen willst — nimm Kontakt mit mir auf.