Héberger et structurer plusieurs robots (Discord) dans un seul script Python : définir des classes
Par Mitch Schwartz
Mis à jour :
Ancien tutoriel — conservé à titre de référence.
Nouvelles fonctionnalités :-
- Les robots détectent automatiquement les serveurs dont ils font partie
- Les robots parcourent leur liste de serveurs pour envoyer les mises à jour
- Fusion d’environ 3 pages de code dans la classe Bot
- Création d’une configuration de mode test basée sur .env, pour que chaque environnement roule dans le bon mode.
En bref, voici comment ça roule :
- Initialiser chaque instance de robot
- Démarrer un fil d’exécution où les scripts de mise à jour des robots tournent en boucle sans fin
- Parcourir tous les robots pour les mettre en ligne
- Loop_forever pour garder les robots en ligne.
Transcription
Salut tout le monde, quoi de neuf. Ça fait un petit bout. Ça fait un petit bout depuis qu'on a fait ces derniers robots Discord et j'ai fait quelques améliorations. Alors on va vous montrer la, la structure de la refonte qu'on a faite. Et oui, au bout du compte, la base est bien plus solide et ça fonctionne bien mieux. Tater, ici, ne nous aidera probablement pas beaucoup, mais elle a décidé d'être dans la vidéo, alors bienvenue.
OK. On continue. Alors la plus grosse chose qu'on a faite, si vous regardez, si vous regardez certaines des vidéos précédentes, c'est que chaque robot fonctionnait un peu à sa façon. J'avais commencé à créer des définitions de ce qu'est un robot. Alors j'ai créé une classe avec quelques def vraiment de base dedans. Et là, on l'a vraiment élargie et on a fait en sorte que les robots, essentiellement, roulent tout seuls.
Alors, vous savez, en commençant juste ici en bas, on a créé une série, en gros une liste de robots, c'est ça. Avec toutes les caractéristiques qui les définissent. Bon, ça devrait clairement aller dans son propre fichier bientôt. Je ne l'ai juste pas fait, avec tous les autres changements qui risquaient de tout briser. Alors si on suit une exécution du début à la fin, je vais vous montrer comment tout ça fonctionne.
Alors d'abord on démarre, on démarre Flask, et c'est. Juste parce que quand je roule sur Replit, qui est un serveur de test vraiment pratique, c'est Flask qui crée, en gros, la partie serveur Web. Et honnêtement, on ne s'en sert plus vraiment, parce que je n'ai plus de tâche cron qui pingue le truc pour le faire rouler chaque minute.
Alors, alors cette partie est un peu optionnelle et on n'a pas besoin de s'y attarder. Ensuite on a la définition de la fonction bots online, celle qui va tout mettre en ligne. On utilise asyncio pour créer la boucle d'événements. Et ce qu'on fait, c'est qu'on parcourt les robots dans notre liste et on essaie.
De démarrer chaque robot. Et si ça marche, tant mieux, sinon, on affiche une erreur. L'erreur n'est pas très parlante. Alors on pourrait ajouter ici, si on est dans for key in bot list. Alors on pourrait faire bot list P name. Et évidemment j'utilise les mauvais crochets ici. Alors si on utilise juste l'accolade,
Ça devrait fonctionner et on va avoir le nom du robot dans le journal. OK. On continue. Ou sinon on affiche key online. OK, parfait. Alors, ah oui, c'est probablement ce que je ferais ici. Plus simple. OK. On continue. On a bot start, la fonction, c'est ça? Alors on appelle vraiment cette fonction et ce que je fais ici, c'est que je crée un fil d'exécution.
Alors. Cette première boucle d'événements ici. Ce qu'elle fait, c'est qu'elle garde les robots en ligne. Et en parallèle, on roule cette boucle sans fin que je vais vous montrer très bientôt. Et ce qu'elle fait, c'est qu'elle roule toutes les mises à jour des robots. Alors en gros, voilà. Et puis, juste pour la structure, on déclare cette fonction, on déclare la deuxième fonction.
On appelle cette deuxième fonction. On initialise les fils d'exécution. On crée cette boucle run forever. Et en gros, rien ne roule après ça, du moins dans le contexte de la fonction dans le flux. Et puis on a bots online. Alors on appelle juste cette fonction. Alors, alors. Essentiellement, c'est ça, la vue d'ensemble.
Alors descendons d'un niveau. Qu'est-ce que bot start? Si on regarde la définition de notre classe Bot, je vous ai déjà montré la liste de robots. Et parmi ce que je définis, il y a un nom, un nom d'affichage, un symbole, une devise de base, le nombre de décimales à utiliser pour la devise, les devises de base, si c'est USD ou dans ce cas Orum, certains des robots représentent en fait les.
Prix de jetons pour des choses qui sont des objets dans le jeu. Et donc on utilise en fait le prix de l'Orum comme base, plutôt que l'USD. Si on part du haut de cette définition, ici. Alors on importe discord. Même si, et c'est d'ailleurs une des raisons pour lesquelles j'utilise Replit, c'est que Visual Studio, je, quelque chose dans ma configuration Python.
N'est pas super. Ça ne reconnaît pas toutes. Toutes les dépendances que j'utilise, toutes les bibliothèques. Et donc c'est vraiment difficile de rouler un aperçu ici. Alors je fais juste ça sur Replit. Alors on déclare des variables de liste de robots vides, qu'elle est globale, le mode test est quelque chose que j'ai configuré dans les variables d'environnement, pour que dans chaque, vous savez, que je roule ici, c'est en mode test.
Si je roule sur Replit. Ça pourrait être en mode test. J'ai un repl de test et j'en ai un en production, et en fait ça, c'est déployé sur Heroku. Et là-dessus, ce n'est plus en mode test. Alors on est capable de configurer les deux. Et ce que fait le mode test, c'est qu'il ne pingue que le serveur de test. Et l'ID du serveur de test est juste là.
Plus loin, on a la définition du robot et les fonctions d'initialisation. Alors on prend le nom, on prend le symbole, on prend le nom d'affichage, que je ne suis pas sûr d'utiliser complètement encore. On a self dot client, qui initialise le disc, le robot comme client Discord, on a le jeton qu'on va chercher dans l'envir le fichier dot M.
Fichier. On a self.online, qui est une propriété. Alors on peut déclarer si c'est en ligne, mais en fait je pense qu'on a arrêté de l'utiliser. Alors je pourrais peut-être co l'enlever. Le runner, c'est ce qui le met vraiment en ligne. Alors on crée l'instance du client. On va chercher le jeton et puis on a le client qui démarre en utilisant le.
Et puis bot type, c'est pour que je sache quel type de robot c'est. Est-ce un robot de jeton? Est-ce que je dois le mettre à jour? Est-ce un robot minuterie? Est-ce que je dois faire une fonction différente avec? Ce qui serait vraiment cool, ce serait d'ajouter une propriété ici. Je ne l'écrirai pas maintenant, mais ce serait quelque chose comme.
Self dot function, qui serait égal au nom de la fonction de mise à jour. Et comme ça, on pourrait en gros invoquer la fonction pour le robot. Alors je pourrais juste dire, vous savez, bot update et il saurait déjà quelle fonction utiliser pour la mise à jour. Alors on a aussi ajouté la création d'une liste de serveurs. Alors maintenant, quand le robot démarre, on obtient vraiment la liste des serveurs; avant, je remplissais la liste de serveurs à la main.
Et chaque fois que quelqu'un ajoutait le robot à un nouveau serveur, je devais ajouter l'ID, ce n'était évidemment pas génial. Alors on a une fonction on ready et une fonction on guild join qu'on définit aussi. Et donc si on descend un peu plus bas, on peut tout voir. Alors voici le ready. Ce qu'on fait, c'est qu'on met self.online à true.
On roule update server list. Alors on va chercher tous les nouveaux serveurs et puis on affiche juste, vous savez, c'est en ligne et puis. Si on descend ici, on a on guild join. Techniquement, je ne sais même pas si c'est nécessaire et je suis pas mal sûr que non, à ce stade, parce que chaque fois qu'on roule la mise à jour, on met à jour la liste de serveurs de toute façon.
Alors tout ça, encore une fois, n'est peut-être même pas nécessaire, mais on a fait quelques versions en une, puis on avance. Def update server list. Alors c'est ici qu'on le fait vraiment. On met updating now à true, et j'ai fait ça. Alors pour qu'en gros on ne puisse pas rouler. Une deuxième fonction de mise à jour en même temps, ou pour qu'on puisse attendre que la mise à jour soit finie avant.
De rouler les fonctions suivantes, ce que des fonctions async pourraient aussi faire, essentiellement, mais je n'ai pas réussi à le faire marcher. Alors j'ai fait ça. Alors ici on a self dot server list. Alors on l'initialise maintenant; si on est en mode test, c'est juste l'ID du serveur de test, qu'on va chercher en haut, et sinon.
Ça va dans le else. Et donc pour chaque guild dans self dot client dot guilds, qui fait référence à la propriété guilds du client du robot. Alors on affiche le nom de la guild et puis si cette guild, je ne sais même pas pourquoi je dois faire une vérification de doublon ici. Ça semblait une bonne idée sur le coup. Mais si cette guild n'est pas déjà dans la liste et que ce n'est pas le serveur de test, parce qu'ici on n'est pas en mode test et implicitement, je ne veux pas que la version en production envoie des requêtes au serveur de test, parce que ça fausserait les tests.
Alors si on passe tout ça, on affiche le nom de la nouvelle guild, guild. I. Et on l'ajoute à la liste avec self dot server list append. Ensuite, on l'affiche juste. Et une fois cette fonction terminée, on affiche toute la liste de serveurs. Ça, c'était vraiment cool, parce qu'on a été surpris du nombre de serveurs auxquels nos robots avaient été ajoutés, et self dot updating now est à false pour qu'on puisse établir que cette mise à jour a roulé pour ce robot.
OK. C'est quoi la prochaine étape pour rouler la vraie mise à jour du robot. Alors on prend self, on prend un paramètre appelé new name. New name va en fait venir de la fonction qui appelle self dot update. Alors on a bâti ça, en gros. Ici. Alors on affiche ça. Alors on affiche la liste de serveurs pour laquelle ça va faire la mise à jour, à partir de là, on parcourt la liste de serveurs et on affiche qu'on est en train de mettre à jour.
Alors on n'a pas besoin de tous ces print. J'aime bien un journal un peu verbeux, parce que ça me donne l'impression que des choses se passent, et certaines personnes les détestent parce que c'est brouillon, et conseillent de nettoyer tous ces print dès qu'on ne s'en sert plus. Alors encore une fois, si le mode test est à true et que le serveur N'est pas le serveur de test et qu'il n'est pas vide.
Et je me demandais si j'en avais besoin, je n'en ai probablement pas besoin, mais je ne l'ai juste pas encore enlevé. Alors on saute, si on est, vous savez, ça voudrait dire en gros qu'on est en mode test, mais que le serveur n'est pas en mode test, alors on le saute. Je pense que ce n'est plus nécessaire non plus, puisque j'ai forcé le serveur à être en mode test.
Alors on n'arriverait pas vraiment ici, mais c'est une vérification de plus. Et si la liste de serveurs est vide ou. Alors on la saute juste. Alors ça, ça aide, parce qu'au premier démarrage, tout ne se passe pas exactement dans le bon ordre. Et donc on essayait de faire des mises à jour même quand il n'y avait pas de liste de serveurs.
Là, vous me diriez que je pourrais peut-être utiliser la fonction de mise à jour ou, pardon, le quoi, c'était quoi déjà? Le. Self dot updating now égal true. C'est possible que je puisse utiliser ça. Et au départ, une des intentions de self.online était juste de rouler, vous savez, de changer ça une fois que la liste de serveurs avait vraiment roulé.
Alors j'ai essayé de l'implanter de deux ou trois façons, mais au bout du compte, ça marche. Et puis finalement, vous savez, on est encore dans cette boucle de la liste de serveurs. Alors ici on a notre requête et on envoie cette requête à Discord avec le nouveau nom. Et c'est tout. On fait un try. Si ça échoue, on affiche l'erreur, et c'est ça, la classe Bot.
Alors c'est un peu plus complexe d'un côté, et beaucoup plus simple et mieux organisé de l'autre. Ce qui est cool, c'est qu'avec cette base, chaque robot se comporte vraiment comme un module : il a ses propres propriétés et on peut l'appeler de façon beaucoup plus pratique. Alors si on regarde get token vals, c'est ça?
Parce que quand on, en fait, laissez-moi vous montrer une chose d'abord. Alors qu'est-ce qui se passe vraiment quand on roule update all the bots, vous vous souvenez peut-être, dans main ici, qu'on démarre un nouveau fil d'exécution avec update all the bots. Alors on vient de passer à travers le fil online pour mettre les robots en ligne, et c'est comme ça qu'on les fait apparaître dans la liste des membres Discord, à droite, comme.
Alors maintenant on appelle depuis endpoints, que je pourrais vraiment renommer maintenant ou déplacer cette fonction ailleurs, parce qu'elle n'a plus rien à voir avec un endpoint. Sauf que peut-être je l'appellerais d'ici. Si quelqu'un pingue l'application, je pourrais utiliser ça comme façon de faire la mise à jour; en ce moment, je retourne juste. OK.
Si vous pinguez l'application à la, vous savez, à l'adresse où elle roule, et avant, ce n'est plus vrai, mais je déclenchais la fonction en la pinguant chaque minute avec cron-job.org. Mais ici, à la place, on décide de faire une, vous savez, une boucle sans fin. Alors while true, qui est la partie boucle sans fin, on essaie get token values et ça va mettre à jour tous les robots de jetons.
Ensuite on essaie reset timer update et puis on essaie tournament timer. Et c'est tout. Et puis la dernière chose qu'on fait, c'est qu'on pingue Dead Man's Snitch, que j'utilise sur Heroku comme moniteur de disponibilité. Et donc tant qu'on se rapporte chaque heure, il ne se plaint pas. Et si on ne se rapporte pas une fois par heure, je sais que quelque chose est brisé et il m'envoie un courriel.
Alors pour ce qui est de get token values, on a déjà couvert reset timer et tournament timer. Et je ne pense pas qu'il y ait beaucoup de changements depuis les vidéos précédentes. On a quelques trucs ici, la fonction qui exprime un prix en Orum au lieu d'en USD, on a cette fonction qui fait le mappage d'un tableau de nos prix de jetons.
Et c'est basé sur la réponse qu'on reçoit de Nomics. Quand on demande les prix des jetons. Et puis on a vraiment le corps de la fonction juste ici. Et ce qui est cool, c'est que je n'ai pas supprimé ce que j'ai mis en commentaire. Alors je vais vous montrer tout le ménage qu'on a pu faire dans le code en élargissant notre classe Bot et en intégrant les fonctions dans les robots. Juste après une petite gorgée de café.
OK. Alors si on regarde par ici, on a le constructeur de notre liste de jetons. Et ce qu'il fait, c'est qu'il filtre toutes nos listes de robots en une série de clés. Et ce qu'on va chercher ici, c'est le symbole. Alors on crée essentiellement une liste plate de toutes les clés de bot list. Et la raison pour laquelle on fait ça, c'est pour pouvoir aller ici et les joindre.
Vous voyez notre fonction map ici. Alors c'est en fait une des parties les plus complexes quand il s'agit de mappage de tableaux. Et donc ce que vous allez voir ici, c'est qu'on joint un. Qu'on crée avec cette fonction map. Et le map ici, c'est en gros item currency et item price. Et donc on se retrouve avec un tableau de ces deux éléments, qui forment une paire clé-valeur.
OK. Alors c'est ça qui se passe ici. Et vous pouvez voir que nos ID sont branchés. Alors on demande, à la fin de tout ça, juste une liste de tous les symboles de devise, Qui appartiennent aux robots dans notre liste. Alors maintenant, quand j'ajoute un nouveau robot à la liste, il commence automatiquement à demander sa devise. Autre chose : si vous avez suivi les vidéos précédentes, récemment Nomics a ajouté une variable per page que vous devez demander.
Alors combien de réponses par page? Je l'ai juste mise à mille. Alors on obtient la réponse de Nomics et on utilise ici token prices. C'est ça? Ah, en fait je me suis trompé. OK. Alors je parlais d'utiliser cette fonction tantôt, mais ce n'est pas vrai. Ce qu'on fait ici, c'est juste mapper une petite liste de de Oui, je veux dire, c'est le même résultat, mais l'explication était un peu à côté.
Alors tout ce que j'ai expliqué se passait en fait ici. Et puis ce qu'on fait, c'est mapper une paire clé-valeur à partir de la réponse, en extrayant la devise et le prix, et puis on les retourne. Alors maintenant ce qu'on a, comme je disais, c'est cette paire clé-valeur des réponses de Nomics pour le jeton. Alors disons Rader ou Orum ou peu importe, et c'est jumelé au prix réel. Ça devient beaucoup plus facile d'aller chercher les valeurs.
Alors, ce que je faisais avant, c'était cette boucle while vraiment bricolée, qui passait sur toute la longueur de la réponse. Et j'ajoutais à la main le nom de chaque robot ici. Et je disais, si le champ currency de la réponse est égal à Orum, alors on l'assigne à cette valeur. On va chercher le prix de ça, vous savez, du même index.
Et. Et c'était un peu fastidieux, parce que un, ça fait niaiseux à faire, vous savez, il y a une meilleure façon de le faire. Et deux, encore une fois, ça m'oblige à ajouter quelque chose à la main. Chaque fois qu'on a un nouveau robot, on a ajouté beaucoup de robots, alors on ne pouvait pas, il était temps d'éliminer ce travail. Et donc c'est ce qu'on a fait avec ces belles fonctions-là, de mappage automatique, pardon, celle-ci en haut, de mappage automatique en paire clé-valeur.
Alors une fois qu'on a fait ça, je fais encore une partie à la main ici, que ce serait vraiment bien de parcourir en boucle. Et je sais qu'on peut trouver une façon de le faire bientôt. Certaines des variables, c'est le nombre de décimales qu'on formate ici. Et puis le fait que certains, comme je disais, doivent être exprimés en prix Orum.
Et donc ce sera la prochaine chose à enlever. Maintenant, parce qu'on utilise une boucle qui peut passer à travers tous les robots et juste appeler leur fonction de mise à jour. On élimine tout ce code où je fais à la main pour chacun. Alors ce qu'on fait, c'est chaque robot. J'appelais tous les serveurs de la liste globale et ça donnait beaucoup d'erreurs.
Et la raison pour laquelle ça donnait beaucoup d'erreurs, c'est que ce n'est pas chaque robot qui était dans chaque serveur, et Discord va limiter votre débit plus souvent si vous envoyez des appels erronés. Alors c'est quelque chose à surveiller. Et c'était une, une optimisation importante. Alors j'ai encore fait un petit bricolage ici. Vous pouvez voir qu'il y a place à l'amélioration dans ces deux dernières sections.
Et ici j'ai juste dit, pour le robot dans la liste, si la devise de base est USD, alors on roule la mise à jour. Et si la devise de base n'est pas en USD, alors on roule la mise à jour, mais on exprime le prix en Orum à la place, et on roule cette fonction de plus pour qu'il soit En gros affiché en Orum. Alors voilà, en gros, comment on a fait cette grosse refonte et créé une classe Bot qui fait la plus grande partie du travail, pour que le code ne soit pas éparpillé dans un paquet de fonctions séparées.
Alors j'espère que ça vous a été utile. Dans la prochaine vidéo, on parlera peut-être de la configuration du pipeline dans Heroku, de quelques trucs GitHub et de comment on a optimisé le déploiement et, et rendu tout ça un peu plus stable. Merci d'avoir regardé, n'oubliez pas d'aimer la vidéo et de vous abonner. On va continuer à publier des vidéos sur la façon de bâtir des robots Discord.