Créer un Bot Discord avec Python

Découvre comment créer un Bot Discord de A à Z avec Python.

Publié le par Thibault Houdon (mis à jour le )
paceTemps de lecture estimé : 58 minutes

Dans ce long article, je vais vous montrer comment créer un bot Discord avec Python de A à Z.

Créer son propre bot Discord est tout à fait possible, cependant pour suivre ce tutoriel, vous devez être un minimum familier avec Python et des outils comme les environnements virtuels, le terminal ou encore pip.

Pour le reste, je vous montre tout, étape par étape, de la création d'une application Discord et d'un bot à l'ajout de ce bot et sa connexion à un serveur.

Nous verrons également comment réagir à des événements courants comme l'arrivée d'un nouveau membre ou la publication d'un nouveau message sur le serveur.

Vous pouvez retrouver le code source de cet article sur ce dépôt Git.

Vous pouvez également retrouver cet article en format vidéo sur YouTube :

Pourquoi créer un bot ?format_paragraph

Avant de rentrer dans le vif du sujet il est important de s'intéresser déjà à cette question : à quoi peut servir un bot Discord ?

Un bot, il peut faire tout ce que vous faites vous-même sur Discord comme poster ou supprimer des messages, ajouter des emoji en réaction à un message ou encore créer et changer les rôles des membres d'un serveur.

Un bot il ne fera donc rien de magique, c'est vous qui allez devoir coder tous ces comportements vous même.

L'intérêt du bot, c'est qu'il sera disponible 24h/24, 7j/7.

Il existe des bots qui permettent de faire tout un tas de chose.

Par exemple, certains bots permettent d'afficher des informations sur la bourse tous les jours, ou d'aller chercher les valeurs d'un indice précis, on retrouve également le plus souvent des bots pour faire de la modération, donc des bots qui vont pouvoir automatiquement supprimer des messages qui enfreignent les règles d'un serveur ou faciliter le travail des modérateurs en permettant rapidement de couper le micro de tout le monde dans un salon vocal par exemple.

On trouve également des bots qu'on appelle 'ChatBot', qui permettent par exemple de mettre en place un support client automatisé afin de réduire la charge de travail du support en amont.

Si je prends l'exemple du bot que l'on a créé spécifiquement pour le serveur Discord de Docstring, il nous permet, entre autres, d'afficher rapidement des messages pour indiquer aux membres la procédure pour poster du code correctement ou supprimer rapidement des messages qui enfreignent les règles.

None

On a également un salon de quiz dans lequel on peut s'entraîner sur le langage Python en demandant au bot de commencer une série de questions et en essayant de deviner la réponse.

Dans ce cas-ci, le bot va chercher une question au hasard dans la base de donnée de Docstring et va ensuite vérifier la réponse de l'utilisateur et lui donner des points d'expérience en cas de bonne réponse. On a donc là un bot qui sert à la fois pour faire de la modération et pour gamifier le serveur :

None

Des bots Discord, il en existe des milliers que vous pouvez télécharger en ligne et ajouter à votre propre serveur.Par exemple, sur ce site, vous trouverez des bots pour plein de catégories différentes. Certains bots sont présents sur plusieurs millions de serveurs, donc vous voyez que c'est vraiment quelque chose de très important dans la communauté Discord.

None

C'est parfois également plus qu'un passe-temps pour certaines personnes, car plusieurs bots assez avancés disposent de fonctionnalités payantes. Certaines compagnies emploient plusieurs dizaines de développeurs afin de créer et maintenir des bots discord et les services associés qui sont présents sur des millions de serveurs.

None

La différence entre application et botformat_paragraph

Quand vous arrivez dans le portail des développeurs de Discord, la première chose que vous allez rencontrer, ce ne sont pas des bots, mais des applications. Il est bien important de comprendre la différence entre les deux, car il y en a une.

99% des vidéos, des tutoriels ou des formations que vous trouverez en ligne concernent les bots Discord, et certaines personnes ne prennent pas le temps de faire la distinction entre les bots et les applications.

Pour créer un bot, vous êtes obligés de créer une application. C'est en effet à l'intérieur d'une application que vous allez créer votre bot.

Mais vous pourriez très bien à l'inverse créer une application qui ne dispose d'aucun bot.

Une application vous permet d'utiliser l'API de Discord.Vous pourriez donc l'utiliser pour poster automatiquement des messages sur votre serveur ou créer des liens d'invitation automatiquement.

Toutes ces actions seraient effectuées avec votre utilisateur Discord directement.

Un bot, il faut voir ça comme un utilisateur à part entière.

Un bot, il existe à l'intérieur d'une application qui nous permet d'utiliser l'API de Discord.

Ce bot, vous pouvez l'ajouter à votre serveur et éventuellement le mettre à disposition d'autres serveurs.

Ce bot, il va agir exactement comme un utilisateur, c'est-à-dire qu'il aura des droits spécifiques, sauf qu'à la différence d'un utilisateur en chair et en os doté de sa propre intelligence, c'est vous qui allez devoir lui dire comment réagir face à différentes situations, grâce à des lignes de code et c'est exactement ce que l'on va apprendre à faire dans cette formation.

Les prérequisformat_paragraph

La première chose dont vous allez avoir besoin, c'est d'un serveur Discord !

Pour ajouter un bot sur un serveur, vous devez posséder les droits de gestion du serveur.Pour ce tutoriel on va utiliser la version 3.6 de Python qui est suffisamment récente pour disposer des fonctionnalités de coroutines (async / await) et une des versions récentes les plus stables.

Avant d'écrire la moindre ligne de code, on va créer un environnement virtuel avec le module venv dans un dossier .env :

python3.6 -m venv .env

Une fois l'environnement créé, il vous suffit de l'activer en sourçant le fichier activate :

source .env/bin/activate

Sur Windows, il vous suffit d'exécuter le fichier activate.bat qui se trouve dans le sous-dossier Scripts. Pour utiliser facilement l'API de Discord avec Python, on va utiliser le package discord.py que l'on peut installer avec pip :

pip install -U discord.py

Pour vérifier que le module a bien été installé, vous pouvez utiliser pip list pour vérifier la liste des modules de votre environnement virtuel ou faire un import et un print du module directement dans votre terminal :

python -c "import discord;print(discord)"

Une fois que vous avez créé votre environnement et installé discord.py, vous êtes fin prêt pour commencer.

Création du botformat_paragraph

Pour créer un bot, il faut d'abord créer une application dans lequel on va créer notre bot.

Pour ça, il suffit de vous rendre dans le portail des développeurs de Discord, cliquer sur New Application et donner un nom à votre application.

None
None

Ensuite, rendez-vous dans l'onglet Bot et cliquez sur 'Add Bot' pour créer un bot dans l'application.

None

Et voilà, vous avez créé votre premier bot 🤖

Ajouter le bot à un serveurformat_paragraph

Pour pouvoir utiliser votre bot, il va falloir l'ajouter à un serveur.

Pour inviter le bot sur un serveur, il faut générer une url d'authorisation avec OAuth2.

Heureusement pour nous, le portail des développeurs de Discord nous permet de générer très facilement cette url avec les bonnes permissions.

Dans l'onglet OAuth2 (1), cochez la case 'bot' (2) et sélectionnez les permissions que vous souhaitez accorder à votre bot (3) :

None

Ensuite, copiez l'URL générée (4) et accédez-y dans un nouvel onglet. Cette URL va vous permettre d'ajouter votre bot dans les serveurs sur lesquels vous disposez des droits nécessaires.

Sélectionnez votre serveur dans le menu déroulant et validez les permissions que vous souhaitez accorder à votre bot.

None
None
None

Une fois cette étape réalisée, vous devriez voir votre bot dans la liste des membres de votre serveur :

None

Connecter le botformat_paragraph

Pour l'instant le bot est hors ligne. Pour le connecter au serveur, on va avoir besoin de seulement trois lignes de code !

Pour commencer, on va importer le module discord de discord.py :

import discord

On va ensuite créer un client à partir de la classe discord.Client :

client = discord.Client()

Et pour connecter le client (notre bot) il suffit d'utiliser la méthode run et de passer le token du bot à cette méthode (entre guillemets bien sûr car il s'agit d'une chaîne de caractères) :

client.run("VOTRE TOKEN ICI")

Lancez votre script et si vous retournez sur votre serveur Discord, vous devriez voir votre bot en ligne.

None

Votre bot sera en ligne et en mesure de répondre à tous les événements et commandes sur votre serveur tant que votre script sera actif.

Si vous arrêtez votre script, le bot redeviendra hors ligne (soyez patient, il peut se passer jusqu'à 1 minute avant que le bot ne soit de nouveau affiché comme hors ligne après l'arrêt du script).

None

L'événement 'on_ready'format_paragraph

Pour l'instant quand on lance notre bot, on est un peu dans le flou, on ne sait pas vraiment quand est-ce qu'il est vraiment connecté et prêt à recevoir des commandes.

Pour savoir quand notre bot est réellement prêt, on peut utiliser l'événement on_ready. Cet événement est déclenché par Discord une fois que l'initialisation du bot et sa connexion se sont déroulés avec succès.

Pour créer un événement, il suffit de créer une fonction avec le même nom que l'événement :

@client.event
async def on_ready():
    print("Le bot est prêt !")

Le décorateur @client.event permet d'indiquer que la fonction on_ready est une fonction qui doit recevoir les informations envoyées lorsque l'événement est appelé par Discord. Le mot clé async défini la fonction comme une coroutine. Le concept de coroutine est un concept assez avancé que vous n'avez pas besoin de maîtriser sur le bout des doigts pour créer un bot. Sachez seulement que devant les fonctions qui gèrent ces événements, vous devez utiliser le mot clé async.

Réagir à un messageformat_paragraph

À chaque fois qu'un message est posté sur votre serveur, Discord va déclencher l'événement on_message.

Pour réagir à un message posté sur votre serveur, il suffit donc d'implémenter une fonction pour gérer cet événement, comme on vient de le faire avec on_ready :

@client.event
async def on_message(message):
    pass

On récupère dans cette fonction le message qui est envoyé par l'événement dans un paramètre qu'on appelle ici message (mais on pourrait l'appeler autrement. Il est cependant bien important de mettre un paramètre dans cette fonction pour pouvoir récupérer le message, sinon vous obtiendrez une erreur).L'objet message possède plusieurs attributs comme content ou author qui permettent d'obtenir plus d'informations sur le message posté.

Si vous souhaitez par exemple afficher le contenu du message, il suffirait de faire :

@client.event
async def on_message(message):
    print(message.content)

Pour vous montrer ce qu'il est possible de faire, on va faire réagir notre bot au mot ping.

Chaque fois qu'un utilisateur entrera le mot ping, le bot répondra pong.

Ça ne sert à rien, mais c'est pour l'exemple 🙃

@client.event
async def on_message(message):
    if message.content == "Ping":
        await message.channel.send("Pong")

Pour envoyer un message avec le bot, on utilise message.channel qui nous permet de récupérer le salon sur lequel le message a été envoyé. On utilise ensuite la méthode send de ce salon pour envoyer un message avec le bot.

Réagir à l'arrivée d'un membreformat_paragraph

Pour réagir à l'arrivée d'un membre, il y a quelques particularités à prendre en compte.

Depuis peu, Discord nécessite d'activer des autorisations pour détecter certains types d'événements comme le changement de statut d'un membre ou l'arrivée d'un nouvel utilisateur sur votre serveur.

Ainsi, dans les paramètres de votre bot, vous devez activer le presence intent et server members intent.

Activez le presence intent si vous souhaitez :

Activez le server members intent si vous souhaitez :

  • Détecter l'arrivée ou le départ des membres avec les événements on_member_join et on_member_remove.
  • Détecter les changements d'informations de vos membres, comme les changements de pseudonyme ou de rôles.
  • Détecter les changements d'informations des utilisateurs comme le changement de nom d'utilisateur ou d'avatars.
  • Récupérer la liste des membres de façon précise avec Guild.chunk, Guild.fetch_members ou Guild.members.
None

Vous devez également rajouter ces 'intents' dans votre code en créant une instance de la classe discord.Intents :

default_intents = discord.Intents.default()

La méthode de classe default permet de créer une instance d'intents avec les permissions par défaut.

Vous pouvez par la suite activer ou désactiver certains 'intents'. Dans notre cas, nous souhaitons activer les intents relatifs aux membres. Par défaut, la valeur de members est False, il faut donc la passer explicitement à True :

default_intents.members = True

Et pour terminer, il faut passer cet objet au paramètre intents de notre client lors de l'instanciation de celui-ci :

client = discord.Client(intents=default_intents)

C'était fastidieux, mais nous avons maintenant la possibilité de détecter l'arrivée de nouveaux membres avec l'événement on_member_join.

@client.event
async def on_member_join(member):
    print(f"L'utilisateur {member.display_name} a rejoint le serveur !")

Pour accueillir les nouveaux membres, nous allons poster un message dans le salon général de notre serveur.

Pour récupérer un salon précis, on peut passer l'identifiant du salon à la fonction client.get_channel.

Pour trouver l'identifiant d'un salon (ou de n'importe quel autre objet sur Discord, comme un utilisateur), vous devez au préalable activer les outils de développeurs dans votre profil Discord.

Dans vos paramètres utilisateurs (attention : les paramètres de votre utilisateur, pas du serveur Discord), dans l'onglet 'Apparence', vous pourrez activer tout en bas, dans la section avancée, le mode développeur :

None

Une fois que ce mode est activé, vous pouvez copier l'identifiant du salon qui vous intéresse en faisant un clique droit dessus et en sélectionnant 'Copier l'identifiant'.

None

De retour dans notre script, nous pouvons donc récupérer le salon grâce à son identifiant et envoyer un message de bienvenue aux nouveaux utilisateurs :

@client.event
async def on_member_join(member):
    general_channel = client.get_channel(728983315005079556)
    general_channel.send(f"Bienvenue sur le serveur {member.display_name} !")

Gérer une commande avec le clientformat_paragraph

La façon la plus directe de créer une commande (et celle que vous retrouverez dans encore pas mal de bots) est de gérer les messages dans l'événement on_message.

On peut ainsi vérifier si un message contient une certaine chaîne de cararactères, utiliser la méthode split pour séparer les différents éléments du message et réagir en fonction.

Par exemple, on souhaite supprimer un certain nombre de messages d'un salon avec la commande !del suivie du nombre de messages à supprimer (par exemple !del 5 pour supprimer les 5 derniers messages) :

@client.event
async def on_message(message):
    if message.content.startswith("!del"):
        number = int(message.content.split()[1])
        messages = await message.channel.history(limit=number + 1).flatten()
        for each_message in messages:
            await each_message.delete()

n commence déjà par vérifier si le message envoyé commence par la chaîne de caractères "!del" grâce à la méthode startswith.

Si c'est le cas, on récupère ensuite le nombre de messages que l'on doit supprimer grâce à la méthode split (qui sépare une chaîne de caractères sur les espaces). On récupère le 2e élément de la liste qui nous est retournée par split avec [1] et on convertit cet élément en nombre avec int.

On récupère ensuite l'historique des messages postés dans le salon grâce à message.channel.history.

Le mot clé await fait également partie de ce concept de coroutine qu'il serait trop long d'expliquer ici. Retenez seulement que toutes les actions que vous effectuez sur votre serveur (que ce soit pour récupérer des informations ou en envoyer) nécessite de les préfixer du mot clé await.On boucle ensuite sur chacun des messages pour les supprimer avec la méthode delete.

Cette façon de faire fonctionne très bien, mais on note deux choses :

On détecte la commande en faisant une comparaison ou en utilisant des méthodes comme startswith directement sur le contenu du message.

On doit gérer nous-même les paramètres passés à la commande, dans ce cas-ci avec split et les convertir dans le bon type si besoin (ici en nombre entier avec int).

Heureusement, il est possible de créer des commandes d'une autre façon, en utilisant un objet Bot à la place de l'objet Client que nous avons utilisé jusqu'à présent.

Créer une commande avec un botformat_paragraph

Pour déclarer une commande avec un objet Bot, nous allons modifier légèrement le code que nous avions jusqu'à présent :

import discord

client = discord.Client()
client.run("VOTRE TOKEN ICI")

À la place, nous allons importer le module commands depuis discord.ext. Ce module contient la classe Bot à partir de laquelle nous allons créer une instance :

from discord.ext import commands

bot = commands.Bot(command_prefix="!")
bot.run("VOTRE TOKEN ICI")

Le paramètre command_prefix permet d'indiquer le caractère qui sera utilisé pour préfixer les commandes envoyées au bot.

La seule chose qui change ici est donc l'import et l'utilisation de commands.Bot au lieu de discord.Client.

Pour le reste, on utilise toujours la méthode run à laquelle on passe notre token pour lancer le bot.

La gestion des événements elle reste la même :

@bot.event
async def on_ready():
    print("Le bot est prêt.")

On utilise le décorateur @bot.event au lieu de @client.event car nous avons créé une instance bot au lieu de client. Si vous aviez nommé l'instance client (client = discord.Bot), il aurait fallu utiliser le décorateur @client.event comme auparavant.

Là où ça devient intéressant, c'est qu'on peut définir des commandes avec le décorateur @bot.command(name="nom_de_la_commande") :

@bot.command(name="del")
async def delete(ctx, number: int):
    messages = await ctx.channel.history(limit=number + 1).flatten()

    for each_message in messages:
        await each_message.delete()

Pour appeler cette fonction delete, il faudra donc utiliser le nom de la commande (del) précédée du command_prefix que nous avons donné lors de l'instanciation de la classe Bot (bot =ommand.Bot(command_prefix="!")).

Le nom de la fonction qui suit la commande (ici, delete) n'a pas d'importance. Pour appeler la commande, vous utiliserez la chaîne de caractères qui est passée au paramètre name dans le décorateur (ici, del).Le paramètre ctx permet d'obtenir des informations par rapport à l'environnement dans lequel la commande a été appelée.

C'est un peu l'équivalent du paramètre message dans l'événement on_message.

Dans ce cas-ci, on utilise l'attribut channel de ce paramètre (ctx.channel) qui nous retourne le salon depuis lequel la commande a été appelée (et on utilise ensuite la méthode history sur ce salon pour récupérer l'historique des messages).

On ajoute ensuite un deuxième paramètre à la fonction delete et on spécifie une annotation de type pour ce paramètre (number: int).

Cela a deux avantages : on récupère ainsi directement ce qui est passé après la commande del et on le récupère dans le bon type.

Ainsi, si on utilise la commande del 5, le nombre entier 5 sera envoyé en argument à la fonction delete.

👉 Pas besoin donc de faire de split, ni d'utiliser la fonction int pour convertir la chaîne de caractères en nombre entier.

Masquer le Tokenformat_paragraph

Pour l'instant, on indique le token en clair dans notre code. Tant que notre script reste sur notre ordinateur, ça peut aller.

Mais si vous souhaitez mettre votre code en ligne (sur un dépôt Git par exemple), il est plus que préférable de ne pas laisser votre Token en clair dans le code.

Le mieux est donc de stocker ce token dans un fichier de configuration en dehors de votre script.

Il existe des centaines de façons de réaliser cette opération.

Dans ce tutoriel, nous allons utiliser la librairie python-dotenv qui permet de facilement lire des variables d'environnements dans un fichier de configuration.

Nous allons installer cette librairie dans notre environnement virtuel (n'oubliez pas de faire un source .env/bin/activate avant) :

pip install -U python-dotenv

Dans le même dossier que celui dans lequel se trouve notre bot, nous allons créer un fichier appelé config (sans extension) dans lequel nous allons mettre notre token :

TOKEN=dinsdfonsdogis23erg0ejs09dgns0dg9sdg

De retour dans notre script Python, on commence par importer la fonction load_dotenv du module dotenv :

from dotenv import load_dotenv

Puis, nous chargeons les variables contenues dans le fichier config dans notre script :

load_dotenv(dotenv_path="config")

Nous pouvons maintenant utiliser le module os pour lire les variables d'environnements qui ont été chargées dans notre script (dans notre cas, la variable TOKEN que nous avons définie dans le fichier config) :

import os

os.getenv("TOKEN")

Notre script final ressemble donc à :

import os

import discord
from dotenv import load_dotenv

load_dotenv(dotenv_path="config")

client = discord.Client()
client.run(os.getenv("TOKEN"))

Et voilà !

Notre token est maintenant stocké de façon sécuritaire dans un fichier séparé.

Si vous souhaitez mettre votre script Python en ligne, personne ne verra votre token.

Prenez garde à ne pas ajouter le fichier config à votre dépôt Git (vous pouvez utiliser pour cela le fichier .gitignore).

Prenez garde également de regénérer votre token si jamais vous aviez déjà fait des commit avec votre Token (ou utilisez un outil comme BFG pour réécrire votre historique Git et effacer le Token de vos précédents commits).

Créer une classe pour le botformat_paragraph

Afin d'organiser un peu mieux les choses et d'éviter tous ces décorateurs, il est possible d'encapsuler tout notre code dans une classe qui hérite de discord.Client ou commands.Bot :

class DocBot(commands.Bot):
    def __init__(self):
        super().__init__(command_prefix="!")

Je ne rentrerai pas dans une explication détaillée du code ci-dessus, c'est de l'orienté objet, si vous êtes perdu, je vous conseille de revoir des formations sur le sujet :)

L'avantage d'utiliser une classe, c'est qu'on peut ainsi surcharger les méthodes de commands.Bot correspondant aux événements de Discord sans avoir besoin d'ajouter un décorateur.

Par exemple avec l'événement on_ready :

class DocBot(commands.Bot):
    def __init__(self):
        super().__init__(command_prefix="!")

    async def on_ready(self):
        print("Le bot est prêt.")

Le reste du code est similaire, on garde les coroutines avec async et await, donc là-dessus rien ne change.

On peut ensuite connecter notre bot de la même façon avec la méthode run, sauf que nous allons cette fois-ci créer une instance de notre classe plutôt que de commands.Bot directement (ou discord.Client) :

class DocBot(commands.Bot):
    def __init__(self):
        super().__init__(command_prefix="!")

    async def on_ready(self):
        print("Le bot est prêt.")

bot = DocBot()
bot.run("VOTRE TOKEN ICI")

Aller plus loinformat_paragraph

Ce tutoriel touche à sa fin. Vous avez maintenant toutes les clés en mains pour créer vos propres Bot Discord.

Tout le reste, ça sera du Python et de la logique.

Voici une liste non exhaustive de liens qui vous seront très utiles :

N'hésitez pas à parcourir plus en détail la documentation de discord.py. C'est en anglais, mais elle est très détaillée et assez digeste donc je ne pourrais que vous la conseiller.

Elle dispose également de pas mal d'exemples de code qui vous permettront de retrouver rapidement tout ce que je vous ai montré dans cet article.