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

Commandes slash discord.py avec app_commands et Cogs

Si tu développes un bot Discord moderne, apprendre à écrire une discordpy slash komut — une commande slash en discord.py — n'est plus facultatif. L'ancien style de commandes texte à préfixe (!commande) a cédé la place aux commandes slash, où Discord affiche l'autocomplétion et des indices de paramètres pendant la saisie. Le module app_commands livré avec discord.py 2.x est la voie officielle, et bien configuré, il donne une base de code propre et facile à maintenir.

Dans ce guide, nous partons de zéro pour aller vers les commandes paramétrées, une structure modulaire avec les Cogs et une bonne gestion des erreurs. Tous les exemples fonctionnent avec discord.py 2.x et Python 3.9+.

Environnement et squelette de bot

Installe d'abord la bibliothèque dans une version récente. Les anciennes versions ne contiennent pas app_commands, donc la 2.x est requise :

pip install -U "discord.py>=2.3"

Voici un bot minimal. La classe commands.Bot expose automatiquement un CommandTree — qui contient tes commandes slash — via bot.tree :

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"Connecté en tant que {bot.user}")

bot.run("TOKEN")

Tu n'as pas besoin de l'intent message_content pour les commandes slash ; il ne concerne que les commandes à préfixe. Ne code jamais ton token en dur — lis-le depuis une variable d'environnement.

Ta première commande slash et la synchronisation

La commande slash la plus simple se définit avec le décorateur bot.tree.command. Le premier paramètre est toujours typé discord.Interaction :

@bot.tree.command(name="bonjour", description="Dit bonjour")
async def bonjour(interaction: discord.Interaction):
    await interaction.response.send_message(
        f"Bonjour, {interaction.user.mention} !"
    )

Le point clé : définir la commande ne suffit pas — il faut la synchroniser avec Discord. Une synchronisation globale (await bot.tree.sync()) s'applique à tous les serveurs mais peut prendre jusqu'à une heure à se propager. C'est bien trop lent en développement ; synchronise plutôt vers un seul serveur de test — c'est instantané :

GUILD = discord.Object(id=123456789012345678)  # ID du serveur de test

@bot.event
async def on_ready():
    bot.tree.copy_global_to(guild=GUILD)
    await bot.tree.sync(guild=GUILD)
    print("Commandes synchronisées vers le serveur")

Attention : sync() est un appel API soumis à des limites de débit. N'appelle pas sync() aveuglément à chaque démarrage ; lance-le seulement quand les commandes changent réellement. Un motif courant consiste à lier la synchronisation à une commande à préfixe cachée, déclenchable uniquement par le propriétaire.

Paramètres, descriptions et choix

La force des commandes slash vient des paramètres typés. discord.py lit tes annotations de type Python et génère le bon champ : int devient un nombre, discord.Member un sélecteur d'utilisateur, bool un interrupteur.

@bot.tree.command(name="addition", description="Additionne deux nombres")
@app_commands.describe(a="Premier nombre", b="Deuxième nombre")
async def addition(interaction: discord.Interaction, a: int, b: int):
    await interaction.response.send_message(f"{a} + {b} = {a + b}")

@app_commands.describe définit le texte d'aide affiché à côté de chaque paramètre — important pour l'ergonomie. Pour proposer un ensemble fixe d'options, utilise Choice :

@bot.tree.command(name="difficulte", description="Choisis une difficulté")
@app_commands.describe(niveau="Difficulté de jeu")
@app_commands.choices(niveau=[
    app_commands.Choice(name="Facile", value="easy"),
    app_commands.Choice(name="Difficile", value="hard"),
])
async def difficulte(interaction: discord.Interaction,
                     niveau: app_commands.Choice[str]):
    await interaction.response.send_message(
        f"Choisi : {niveau.name} ({niveau.value})"
    )

Si le calcul de la réponse prend du temps (par exemple un appel API), appelle d'abord await interaction.response.defer() pour ne pas dépasser la fenêtre de réponse de 3 secondes, puis envoie le résultat avec await interaction.followup.send(...).

Structure modulaire avec les Cogs

Gérer des dizaines de commandes dans un seul fichier devient vite ingérable. commands.Cog permet de regrouper des commandes liées dans une classe et de les répartir en fichiers séparés (extensions). Dans un Cog, tu utilises le décorateur @app_commands.command, et le premier paramètre devient self :

# cogs/outils.py
import discord
from discord import app_commands
from discord.ext import commands

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

    @app_commands.command(name="ping", description="Affiche la latence")
    async def ping(self, interaction: discord.Interaction):
        latence = round(self.bot.latency * 1000)
        await interaction.response.send_message(f"Pong ! {latence}ms")

async def setup(bot: commands.Bot):
    await bot.add_cog(Outils(bot))

Le bon endroit pour charger les extensions est setup_hook, qui s'exécute avant la connexion du bot. Sous-classer le bot est l'approche la plus propre :

class Bot(commands.Bot):
    async def setup_hook(self):
        await self.load_extension("cogs.outils")
        await self.tree.sync()  # synchronise vers un serveur en dev

intents = discord.Intents.default()
bot = Bot(command_prefix="!", intents=intents)
bot.run("TOKEN")

Pour regrouper plusieurs commandes sous un même nom, utilise app_commands.Group ; cela produit des sous-commandes comme /parametres langue et /parametres notification.

Gestion des erreurs

Si une exception est levée dans une commande, l'utilisateur ne voit qu'un échec silencieux. Mieux vaut définir un gestionnaire d'erreurs central pour toutes les commandes slash. CommandTree fournit un décorateur error précisément pour cela :

@bot.tree.error
async def on_app_command_error(
    interaction: discord.Interaction,
    error: app_commands.AppCommandError,
):
    if isinstance(error, app_commands.MissingPermissions):
        message = "Tu n'as pas la permission pour cette commande."
    else:
        message = "Une erreur est survenue, réessaie."
    # response si pas encore envoyé, sinon followup
    if interaction.response.is_done():
        await interaction.followup.send(message, ephemeral=True)
    else:
        await interaction.response.send_message(message, ephemeral=True)

ephemeral=True n'affiche le message qu'à la personne qui a lancé la commande — idéal pour les avis d'erreur. Tu peux ajouter une vérification de permission avec @app_commands.checks.has_permissions(...) sur la commande, et capturer l'échec dans le gestionnaire ci-dessus.

Questions fréquentes

Pourquoi mes commandes slash n'apparaissent-elles pas dans Discord ?

C'est presque toujours un problème de synchronisation. Une sync() globale met du temps à se propager ; en développement, synchronise directement vers ton serveur de test (guild=...) pour qu'elles apparaissent instantanément. Vérifie aussi que le bot a été invité avec le scope applications.commands.

Quelle différence entre app_commands et l'ancien commands.command ?

commands.command ce sont des commandes texte à préfixe (!commande). app_commands ce sont les commandes slash natives de Discord : autocomplétion, validation de type et champs de paramètres visuels. Pour les nouveaux bots, les commandes slash sont à privilégier.

Dois-je appeler sync à chaque démarrage ?

Non. sync() est limité en débit, et des appels inutiles peuvent te faire restreindre. Synchronise seulement quand les définitions de commandes changent ; en production, on lie souvent cela à une commande manuelle réservée au propriétaire.

Envie de faire passer ton bot au niveau supérieur ? Des commandes slash aux systèmes de modération, aux infrastructures musicales et aux intégrations de tableaux de bord personnalisés, je peux t'aider sur tes projets discord.py. Partage ton idée et construisons-la ensemble : contacte-moi.

Bu kategorideki tüm yazılar →

Devamı için