Skip to content

Réalisation d'un Bot pour Discord

Posted on:17 février 2017 at 01:00

Discord est un logiciel voix / écrit développer par la start-up Hammer & Chisel. Il permet à une communauté de se retrouver autour d’un même sujet. Nous allons voir aujourd’hui comment réaliser un Bot simple pour Discord en C# avec DiscordNet. Celui-ci répondra à des commandes simples et ne supportera pas le langage naturel, je vous renvoie vers l’article Sauter le pas, créez votre Bot ! qui utilise le Microsoft Bot Framework et LUIS (Language Understanding Intelligent Service), qui peut être implémenté assez facilement avec Discord. L’article suivant va se concentrer sur la configuration de Discord ainsi que le développement d’un Bot assez simple. Pour des questions pratiques je vais partir sur un projet ASP.NET MVC, il vous sera par la suite possible de l’héberger sur le Cloud Azure grâce à l’hébergement gratuit. Une conversion du programme en projet console ou WPF est très facilement réalisable ne vous inquiétez pas !

Création du Bot sur Discord

Configuration

Avant toute chose, il va nous falloir créé notre Bot sur le site Discord, afin d’avoir le code secret nécessaire ainsi que de pouvoir le configurer comme souhaité (nom, avatar et description). Il est possible de faire sans, mais votre Bot n’aura pas accès aux salons nécessitant un niveau d’identification supérieur ou égal à faible, c’est-à-dire avec un compte Discord valide. Je vous invite à vous rendre sur cette page https://discordapp.com/developers/applications/me est à vous identifier si cela n’est pas déjà fait. Votre liste d’application devrait en théorie être, pour le moment, vide. Cliqué sur New App, un formulaire comme celui sur l’image ci-dessous devrait apparaître. L’avatar et la description ne sont pas nécessaires, cependant il vous faudra obligatoirement indiquer un nom.

DiscordCreateBot

Cliqué sur Create App puis par la suite sur Create a Bot User pour indiquer à discord que notre application va être un Bot. Une nouvelle section du nom de App Bot User doit faire son apparition, je vous invite à cocher la case Public Bot puis, au-dessus, sur Click to reveal à côté de Token. Votre configuration doit être la suivante (voir image), je précise que votre Token va être différent, celui-ci est unique pour chaque Bot. Noté quelque part le Client ID de votre Bot, nous en aurons besoin juste après pour ajouter le Bot à un salon Discord (ceci est différent du Token ! Client ID se trouve dans la section App Details).

BotConf

Connection du Bot sur Discord

Maintenant que votre Bot est configuré correctement nous allons faire en sorte qu’il rejoigne un salon, cela va être la dernière étape avant d’attaquer le code en lui-même, juste après. Il va vous falloir vous rendre à l’adresse suivante : https://discordapp.com/api/oauth2/authorize?client_id=281710450167775233&scope=bot&permissions=0. Mais avant de sauter sur l’adresse, il vous faut modifier le Cliend ID de l’URL avec le vôtre, après client_id=. Celui actuellement en place correspond à l’identifiant de mon Bot ‘blog-programmeur’. Une fois modifié, vous allez simplement devoir choisir le serveur dans lequel vous souhaitez l’ajouté. Je précise que la liste des serveurs correspond aux serveurs dont vous êtes l’administrateur avec votre compte Discord.

Pour voir si tout a bien fonctionné, rendez-vous sur votre discord et vérifier dans le menu latéral à droite que votre Bot s’affiche dans la liste des membres hors ligne. Comme sur l’image ci-dessous.

BotOnDiscord

Cette partie est donc maintenant finie ! Il nous reste à attaquer l’aspect technique de la chose !

Développement du Bot

Création du projet

Comme dit dans l’introduction, je trouve plus pratique et plus facile d’héberger une application ASP.NET C# pour notre Bot que d’en faire une version console. Par conséquent, pour la suite de l’article, le Bot sera développé sur une architecture ASP.NET. Je précise tout de même que la réalisation d’une version console et aussi très simple, il y a très peu de différences au niveau code. Seulement une instruction qui est malheureusement bloquante pour un site internet, mais j’y reviendrais prochainement !

Comme à mon habitude, j’utilise Visual Studio Community 2015. Les manipulations qui vont être décrites seront donc sur cet IDE. En cas de difficulté ou de question n’hésitez pas à m’en faire part dans les commentaires ! Quelques commentaires en français seraient les bienvenus ! Dans Visual Studio, en haut à gauche je vous invite à faire Fichier -> Nouveau -> Projet …, puis dans la section Visual C# -> ASP.NET Web Application (.NET Framework), puis architecture MVC, sans identification de mon côté (mais cela n’aura aucune influence sur la suite). Je précise tout de même que DiscordNET que nous allons utiliser par la suite est compatible ASP.NET Core, mon choix est donc tout à fait arbitraire. Je vous invite donc à créer le projet !

Maintenant, nous allons utiliser NuGet, pour ceux qui ne connaissent pas, NuGet est un gestionnaire de paquets développé par la fondation .Net (Microsoft), le projet est sous licence Apache. Il est intégré à Visual Studio, ce qui va grandement nous simplifier la tâche. Cliqué sur votre projet, dans l’explorateur de solution, puis dans le menu déroulant Manage NuGet Package comme sur l’image ci-dessous. Une nouvelle page devrait s’afficher.

Nuget

Dans la nouvelle page qui vient de s’ouvrire, vous avec normalement 3 types d’onglet en haut, de mon côté en anglais Browse, Installed et Updates. Je vous invite à cliquer sur le plus à gauche, Browse. Vous trouverez ici une liste des extensions, module, librairie compatible à votre projet. À titre informatif, NuGet fonctionne autant pour du C# que pour du C++, HTML/JS. Dans la barre de recherche, tapée simplement Discord.Net. Nous aurons besoin des deux premiers, c’est-à-dire Discord.Net puis Discord.Net.Commands. Faites installer dans le menu à droite pour les deux librairies citées précédemment. NuGet va s’occuper pour vous du téléchargement et de l’installation ! L’installation va prendre un petit moment, accepté les conditions d’utilisation. Une fois installé vous verrez une case cochée verte sur le logo.

Le code

Attaquons maintenant les choses sérieuses ! Je vous invite à vous rendre dans le fichier Global.asax, c’est ici que nous allons initialiser notre Bot, pour se faire ajouter la ligne suivante à la fin de la classe Application_Start() : Bot bot = new Bot();. Une erreur doit apparaitre, passé la souris sur Bot -> Show potential fix -> Generate class Bot in new file. Un nouveau fichier .cs du nom de Bot.cs doit apparaître dans l’explorateur de solution, rendez-vous y, nous en avons fini pour la déclaration !

Il ne nous reste plus qu’à faire le code de connexion de notre bot à discord, la création de commande simple et faire en sorte que celui-ci soit attentif au chat afin d’y répondre et d’exécuter des commandes si besoin. Comme à mon habitude je vais vous exposer le code en entier, avec quelques commentaires puis par la suite vous l’expliquez en détail. Je n’ai pas eu de retour sur cette méthode, n’hésitez donc pas à me dire si cela vous conviens comme façon de faire !

using Discord;
using Discord.Commands;
using System.Threading.Tasks;

namespace DiscordBot
{
    internal class Bot
    {
        // Variable
        DiscordClient discord;
        ///////////////////////

        public Bot()
        {
            // Initialisation
            discord = new DiscordClient();
            discord.UsingCommands(x =>
            {
                x.PrefixChar = '!';
                x.AllowMentionPrefix = true;
            });

            // Commandes
            var commands = discord.GetService<CommandService>();
            commands.CreateCommand("Aide").Do(async (e) => {
                await e.Channel.SendMessage("Bonjour @" + e.User + ", je suis un Bot. Mon objectif et de vous aider au mieux dans vos démarches. Voilà les commandes que vous pouvez utiliser pour m'appeler :"
                    + "\n- !hello : Je vous répond simplement.");
            });

            commands.CreateCommand("Hello").Do(async (e) => {
                await e.Channel.SendMessage("world !");
            });

            // Connexion du Bot à Discord
            Task.Run(async () => {
                await discord.Connect("MjgxNzEwNDUwMTY3Nzc1MjMz.C4dKdA.rsqVO9tJE_-9l4bEdeMnnKTmt4w", TokenType.Bot);
                discord.SetGame("Tape !aide pour l'aide");
            });
        }
    }
}

Voilà un premier aperçu du code, comme vous pouvez le voir celui-ci est vraiment court et assez simple. Pour ceux qui ne sont pas habitués, sachez que async et await permette de faire des actions asynchrones, cela veut simplement dire que votre programme ne s’exécutera pas instruction après instruction. Mais plusieurs instructions à la fois et ne bloquera donc pas l’application (ce qui est parfait pour notre utilisation à partir de l’architecture d’un site internet). Nous n’avons qu’un constructeur pour la classe, étant donné que le programme est async le reste s’exécutera dans une instance indépendante.

            // Initialisation
            discord = new DiscordClient();
            discord.UsingCommands(x =>
            {
                x.PrefixChar = '!';
                x.AllowMentionPrefix = true;
            });

Nous initialisons la variable discord, qui est du type DiscordClient. Cette variable est notre interface entre notre code C# et l’API REST de Discord. C’est ici que va nous servir DiscordNet. Bon, après avoir initialisé la variable nous configurons le système de commande, nous proposons 2 façons d’interagir avec notre bot, soit le préfixe ! ou alors son nom en citation, c’est-à-dire dans mon cas @blog-programmeur. Si une phrase commence par un de ces deux éléments alors le bot sera comme “réveillé” et regardera si la commande qui suit est valide.

                            // Commandes
            var commands = discord.GetService<CommandService>();
            commands.CreateCommand("Aide").Do(async (e) => {
                await e.Channel.SendMessage("Bonjour @" + e.User + ", je suis un Bot. Mon objectif et de vous aider au mieux dans vos démarches. Voilà les commandes que vous pouvez utiliser pour m'appeler :"
                    + "\n- !hello : Je vous répond simplement.");
            });

            commands.CreateCommand("Hello").Do(async (e) => {
                await e.Channel.SendMessage("world !");
            });

Du coup c’est sympathique, notre bot peut détecter qu’un utilisateur sur Discord lui parle, mais encore faudrait-il qu’il y ait des commandes à exécuter ! C’est ici que rentre en jeu DiscordCommand que nous avons aussi installé dans l’étape de configuration de votre environnement. Nous déclarons une nouvelle variable du type CommandService, rien d’extraordinaire jusque-là, puis par la suite avec commands.CreateCommand("Aide") nous allons simplement définir une nouvelle commande, qui s’activera avec le mot-clef Aide, je précise que la majuscule ne rentre pas en jeu une fois en production. Puis, de façon asynchrone nous envoyons à l’aide de e.Channel.SendMessage un message sur le salon ou la commande a été tapée. Grâce à la variable e nous pouvons avoir des indications concernant le salon, la personne qui a fait la commande. De même pour la commande Hello.

Comme vous pouvez le voir le code assez simple, il suffit simplement de mettre en place les commandes et leurs actions. Il me reste une dernière étape à vous expliquer :

                            // Connexion du Bot à Discord
            Task.Run(async () => {
                await discord.Connect("MjgxNzEwNDUwMTY3Nzc1MjMz.C4dKdA.rsqVO9tJE_-9l4bEdeMnnKTmt4w", TokenType.Bot);
                discord.SetGame("Tape !aide pour l'aide");
            });

Comme indiqué par le commentaire cette instruction permet de connecter notre bot, une fois configuré correctement, à discord. C’est donc par l’intermédiaire de discord.Connect que cela se passe. C’est ici qu’il vous faut indiqué en premier paramètre votre Token, puis indiqué que c’est un Bot qui se connecte. La ligne suivante, elle, est complètement facultative, elle permet de dire à Discord que notre Bot joue à Tape !aide pour l’aide. Ceci n’est pas vrai bien entendu mais cela permet de mettre en dessous de son pseudo, dans Discord, le texte écrit précédemment et donc à des utilisateurs externes de savoir comment interagir avec lui.

Le code précédent, pour un Bot développé en console est différent, et s’écrit de la manière suivante, afin de faire une boucle pour que le Bot ne se ferme pas instantanément :

client.ExecuteAndWait(async () =>
{
    await discord.Connect("MjgxNzEwNDUwMTY3Nzc1MjMz.C4dKdA.rsqVO9tJE_-9l4bEdeMnnKTmt4w", TokenType.Bot);
    discord.SetGame("Tape !aide pour l'aide");
});

Voilà ! Vous savez maintenant mettre en place une architecture de base pour votre Bot. Pourquoi avoir choisi un projet de site internet ? Simplement que cela peut permettre à votre Bot d’interagir avec ça base de données sans risque de faille de sécurité, sans devoir prendre des mesures supplémentaires. Je vous invite à visiter mon salon Discord ci-contre. Celui-ci est lié au site http://gamersaddict.fr/ que je possède. Mon Bot peut donc, par l’intermédiaire de requête LINQ donné la liste des articles récents ou faire une recherche avec respectivement les commandes suivantes : !article ou !cherche Xbox.

Le code source de la classe Bot est disponible ici.

Bonus : paramètre dans une commande

Comme vous avez pu le remarquer avec la commande !cherche que j’ai décrite précédemment, disponible sur mon Discord. L’utilisateur peut passer un paramètre, ici le terme qu’il recherche. Pour ce faire rien de bien compliquer, voilà un exemple de code :

        commands.CreateCommand("Cherche")
            .Parameter("Msg", ParameterType.Unparsed)
            .Do(async (e) => {
                string searchTerm = e.GetArg("Msg");

                if (searchTerm != string.Empty || searchTerm.Length > 0)
                {
                    await e.Channel.SendMessage("Je cherche " + searchTerm + "...");
                    // Get Data
                    string msg = "";
                    List <ArticlesViewModel> model;
                    // Requête LINQ

                    msg += "Résultat de votre recherche (" + model.Count + " correspondance(s)) :";
                    foreach (var item in model)
                    {
                        msg += $"\n-**{ item.Title }** - { item.Description } - http://gamersaddict.fr/Article/detail/{ HttpUtility.UrlEncode(item.Title) }";
                    }
                    await e.Channel.SendMessage(msg);
                }
                else
                    await e.Channel.SendMessage("Il manque votre recherche après la commande !");
         });

De plus je vous conseille de visiter la documentation de DiscordNet disponible ici pour des actions spécifiques.