Wenn du einen modernen Discord-Bot baust, ist es längst keine Option mehr, zu lernen, wie man eine discordpy slash komut — einen Slash-Befehl in discord.py — schreibt. Der alte !befehl-Stil präfixbasierter Textbefehle ist den Slash-Befehlen gewichen, bei denen Discord schon während der Eingabe Autovervollständigung und Parameterhinweise anzeigt. Das mit discord.py 2.x ausgelieferte Modul app_commands ist der offizielle Weg dafür und liefert bei richtiger Einrichtung eine saubere, wartbare Codebasis.
In dieser Anleitung beginnen wir bei null und arbeiten uns zu parametrisierten Befehlen, einer modularen Struktur mit Cogs und sauberer Fehlerbehandlung vor. Alle Beispiele funktionieren mit discord.py 2.x und Python 3.9+.
Umgebung und ein Basis-Bot-Gerüst
Installiere zuerst die Bibliothek in einer aktuellen Version. Ältere Releases enthalten app_commands nicht, daher ist 2.x erforderlich:
pip install -U "discord.py>=2.3"
Nun ein minimaler Bot. Die Klasse commands.Bot stellt automatisch über bot.tree einen CommandTree bereit — der deine Slash-Befehle hält:
import discord
from discord import app_commands
from discord.ext import commands
intents = discord.Intents.default()
bot = commands.Bot(command_prefix="!", intents=intents)
@bot.event
async def on_ready():
print(f"Eingeloggt als {bot.user}")
bot.run("TOKEN")
Für Slash-Befehle brauchst du den Intent message_content nicht; der ist nur für Präfixbefehle relevant. Schreibe deinen Token niemals fest in den Code — lies ihn aus einer Umgebungsvariable.
Dein erster Slash-Befehl und die Synchronisierung
Der einfachste Slash-Befehl wird mit dem Dekorator bot.tree.command definiert. Der erste Parameter ist immer als discord.Interaction typisiert:
@bot.tree.command(name="hallo", description="Sagt hallo")
async def hallo(interaction: discord.Interaction):
await interaction.response.send_message(
f"Hallo, {interaction.user.mention}!"
)
Der entscheidende Punkt: Den Befehl zu definieren reicht nicht — du musst ihn mit Discord synchronisieren. Eine globale Synchronisierung (await bot.tree.sync()) gilt für jeden Server, kann sich aber bis zu einer Stunde lang ausbreiten. Das ist während der Entwicklung viel zu langsam; synchronisiere stattdessen auf einen einzelnen Testserver — das geschieht sofort:
GUILD = discord.Object(id=123456789012345678) # ID des Testservers
@bot.event
async def on_ready():
bot.tree.copy_global_to(guild=GUILD)
await bot.tree.sync(guild=GUILD)
print("Befehle mit Server synchronisiert")
Achtung: sync() ist ein API-Aufruf mit Rate-Limit. Rufe sync() nicht blind bei jedem Start auf; führe es nur aus, wenn sich Befehle tatsächlich ändern. Ein gängiges Muster ist, die Synchronisierung an einen versteckten Präfixbefehl zu binden, den nur der Eigentümer auslösen kann.
Parameter, Beschreibungen und Auswahloptionen
Die Stärke der Slash-Befehle liegt in typannotierten Parametern. discord.py liest deine Python-Typhinweise und erzeugt das passende Eingabefeld: int wird zu einer Zahl, discord.Member zu einer Benutzerauswahl, bool zu einem Schalter.
@bot.tree.command(name="addiere", description="Addiert zwei Zahlen")
@app_commands.describe(a="Erste Zahl", b="Zweite Zahl")
async def addiere(interaction: discord.Interaction, a: int, b: int):
await interaction.response.send_message(f"{a} + {b} = {a + b}")
@app_commands.describe legt den Hilfetext fest, der neben jedem Parameter erscheint — wichtig für die Bedienbarkeit. Um eine feste Auswahl anzubieten, verwende Choice:
@bot.tree.command(name="schwierigkeit", description="Wähle eine Schwierigkeit")
@app_commands.describe(stufe="Spielschwierigkeit")
@app_commands.choices(stufe=[
app_commands.Choice(name="Leicht", value="easy"),
app_commands.Choice(name="Schwer", value="hard"),
])
async def schwierigkeit(interaction: discord.Interaction,
stufe: app_commands.Choice[str]):
await interaction.response.send_message(
f"Gewählt: {stufe.name} ({stufe.value})"
)
Wenn die Berechnung der Antwort länger dauert (etwa ein API-Aufruf), rufe zuerst await interaction.response.defer() auf, um das 3-Sekunden-Antwortfenster nicht zu überschreiten, und sende das Ergebnis anschließend mit await interaction.followup.send(...).
Modulare Struktur mit Cogs
Dutzende Befehle in einer Datei zu verwalten, gerät schnell außer Kontrolle. commands.Cog erlaubt es, verwandte Befehle in einer Klasse zu gruppieren und auf separate Dateien (Extensions) aufzuteilen. Innerhalb eines Cogs verwendest du den Dekorator @app_commands.command, und der erste Parameter wird zu self:
# cogs/tools.py
import discord
from discord import app_commands
from discord.ext import commands
class Tools(commands.Cog):
def __init__(self, bot: commands.Bot):
self.bot = bot
@app_commands.command(name="ping", description="Zeigt die Latenz")
async def ping(self, interaction: discord.Interaction):
latenz = round(self.bot.latency * 1000)
await interaction.response.send_message(f"Pong! {latenz}ms")
async def setup(bot: commands.Bot):
await bot.add_cog(Tools(bot))
Der richtige Ort zum Laden von Extensions ist setup_hook, der vor der Verbindung des Bots läuft. Den Bot abzuleiten ist der sauberste Ansatz:
class Bot(commands.Bot):
async def setup_hook(self):
await self.load_extension("cogs.tools")
await self.tree.sync() # in der Entwicklung auf einen Server syncen
intents = discord.Intents.default()
bot = Bot(command_prefix="!", intents=intents)
bot.run("TOKEN")
Um mehrere Befehle unter einem Namen zu bündeln, verwende app_commands.Group; das ergibt Unterbefehle wie /einstellungen sprache und /einstellungen benachrichtigung.
Fehlerbehandlung
Wird innerhalb eines Befehls eine Ausnahme ausgelöst, sieht der Nutzer nur ein stilles Scheitern. Am besten definierst du einen zentralen Fehlerhandler für alle Slash-Befehle. CommandTree stellt genau dafür einen error-Dekorator bereit:
@bot.tree.error
async def on_app_command_error(
interaction: discord.Interaction,
error: app_commands.AppCommandError,
):
if isinstance(error, app_commands.MissingPermissions):
nachricht = "Du hast keine Berechtigung für diesen Befehl."
else:
nachricht = "Etwas ist schiefgelaufen, versuch es erneut."
# response, falls noch nicht gesendet, sonst followup
if interaction.response.is_done():
await interaction.followup.send(nachricht, ephemeral=True)
else:
await interaction.response.send_message(nachricht, ephemeral=True)
ephemeral=True zeigt die Nachricht nur der Person, die den Befehl ausgeführt hat — ideal für Fehlerhinweise. Du kannst eine Berechtigungsprüfung mit @app_commands.checks.has_permissions(...) am Befehl ergänzen und das Scheitern im obigen Handler abfangen.
Häufige Fragen
Warum erscheinen meine Slash-Befehle nicht in Discord?
Fast immer ist es ein Synchronisierungsproblem. Eine globale sync() braucht Zeit, um sich auszubreiten; synchronisiere während der Entwicklung direkt auf deinen Testserver (guild=...), damit sie sofort erscheinen. Stelle außerdem sicher, dass der Bot mit dem Scope applications.commands eingeladen wurde.
Was ist der Unterschied zwischen app_commands und dem alten commands.command?
commands.command sind präfixbasierte Textbefehle (!befehl). app_commands sind Discords native Slash-Befehle: Sie bieten Autovervollständigung, Typprüfung und visuelle Parameterfelder. Für neue Bots sind Slash-Befehle vorzuziehen.
Soll ich bei jedem Start sync aufrufen?
Nein. sync() hat ein Rate-Limit, und unnötige Aufrufe können zu Drosselungen führen. Synchronisiere nur, wenn sich Befehlsdefinitionen ändern; in der Produktion bindet man dies oft an einen manuellen, nur dem Eigentümer vorbehaltenen Befehl.
Willst du deinen Bot auf die nächste Stufe heben? Von Slash-Befehlen über Moderationssysteme und Musik-Backends bis zu individuellen Dashboard-Integrationen kann ich bei deinen discord.py-Projekten helfen. Teile deine Idee und lass sie uns gemeinsam umsetzen: nimm Kontakt auf.