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

Cogs discord.py : un bot modulaire et propre

Dès qu'un bot Discord commence à grandir, un unique fichier main.py devient vite ingérable — et c'est précisément là qu'intervient la structure discord.py cog. Les cogs sont des modules qui regroupent commandes, écouteurs d'événements et état dans une seule classe. En découpant chaque fonctionnalité dans son propre fichier, vous donnez à votre bot une architecture propre, lisible et évolutive. Dans ce guide, nous construisons un squelette de bot modulaire à partir de zéro avec discord.py 2.x.

Qu'est-ce qu'un cog et pourquoi l'utiliser ?

Un cog est une classe Python qui hérite de commands.Cog et contient des commandes et des écouteurs. Pendant que le cœur du bot continue de tourner, vous pouvez charger, décharger ou recharger des cogs à l'exécution. Les bénéfices concrets sont :

  • Séparation des responsabilités : chaque domaine — modération, musique, économie — vit dans son propre fichier.
  • Rechargement à chaud : rechargez un seul module sans arrêter le bot pour tester vos changements.
  • Travail en équipe : plusieurs développeurs travaillent sur des cogs différents sans conflits.
  • Moins d'état global : chaque cog conserve son propre état dans la classe.

Structure du projet

La disposition recommandée garde le cœur à la racine et place tous les cogs dans un dossier dédié :

my-bot/
├── bot.py
├── cogs/
│   ├── moderation.py
│   ├── general.py
│   └── economy.py
├── requirements.txt
└── .env

Utilisez un environnement virtuel pour installer les dépendances, et ajoutez au moins discord.py et python-dotenv à votre requirements.txt :

python -m venv .venv
source .venv/bin/activate
pip install -U discord.py python-dotenv

Le fichier central : bot.py

Dans discord.py 2.x, le bon endroit pour charger les extensions est la méthode setup_hook. Elle est appelée avant la connexion du bot, et load_extension peut y être await en toute sécurité. Nous étendons commands.Bot pour créer un point d'entrée propre :

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)
        # Synchroniser les commandes slash
        await self.tree.sync()

    async def on_ready(self):
        print(f"Connecté en tant que {self.user} (ID: {self.user.id})")

async def main():
    bot = MyBot()
    await bot.start(os.environ["DISCORD_TOKEN"])

if __name__ == "__main__":
    asyncio.run(main())

Vous devez aussi activer l'intent message_content dans le Discord Developer Portal ; sinon les commandes à préfixe ne fonctionneront pas.

Écrire votre premier cog

Chaque fichier de cog comporte deux parties : une classe qui hérite de commands.Cog, et une fonction setup asynchrone en bas du fichier. Dans discord.py 2.x, setup est désormais async et ajoute le cog avec await bot.add_cog() :

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"Bienvenue {member.mention} !")

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

Deux points d'attention : dans un cog, le premier paramètre de toute commande est toujours self, et les écouteurs se définissent avec le décorateur @commands.Cog.listener() — jamais @bot.event.

Déplacer les commandes slash dans les cogs

Les bots modernes s'appuient de plus en plus sur les commandes slash. Vous les définissez dans un cog avec 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="Affiche ton solde")
    async def balance(self, interaction: discord.Interaction):
        await interaction.response.send_message(
            "Ton solde : 1000 pièces", ephemeral=True
        )

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

Les commandes slash doivent être synchronisées pour être enregistrées auprès de Discord. Pendant le développement, synchroniser sur un serveur de test précis (visible instantanément) est bien plus rapide qu'une synchro globale ; la propagation globale peut prendre jusqu'à une heure.

Gérer les cogs à l'exécution

En ajoutant un cog d'administration, vous pouvez charger, décharger et recharger des modules sans redémarrer le bot. Cela accélère considérablement la boucle de développement :

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}` rechargé.")

async def setup(bot):
    await bot.add_cog(Admin(bot))

Le contrôle @commands.is_owner() réserve ces commandes sensibles au seul propriétaire du bot. Si un module contient une erreur de syntaxe, reload_extension conserve l'ancienne version et lève l'erreur — d'où l'intérêt de capturer l'exception et de la signaler à l'utilisateur.

Questions fréquentes

Pourquoi la fonction setup doit-elle être async ?

Avec discord.py 2.x, le cycle de vie de la bibliothèque est devenu entièrement asynchrone. Comme add_cog et load_extension sont désormais des coroutines, la fonction setup qui les appelle doit elle aussi être async def et utiliser await en interne.

Faut-il utiliser des commandes à préfixe ou des commandes slash ?

Pour les nouveaux projets, les commandes slash sont recommandées : elles sont découvrables dans l'interface Discord, se complètent automatiquement et ne nécessitent pas l'intent message_content. La structure en cogs héberge volontiers les deux dans la même classe.

Si un cog plante, tout le bot s'effondre-t-il ?

Non. Si un cog échoue au chargement pendant load_extension, seule cette extension est ignorée ; enveloppez-la dans try/except et les autres cogs continuent de se charger normalement. À l'exécution, chaque cog a sa propre portée de gestion des erreurs.

Vous voulez migrer votre bot vers une architecture en cogs propre ? Que vous ayez besoin de faire passer un bot mono-fichier vers une structure modulaire, d'ajouter des commandes slash ou de créer un bot Discord de zéro, contactez-moi.

Bu kategorideki tüm yazılar →

Devamı için