API pour les bots IRC
Posté le 11/06/2023 22:32
Bonjour à tous !
Ceux qui suivent le salon #projets de la shoutbox on pu voir qu'il y avait un peu d'activité autour du dépôt sur la version V5 de GLaDOS. Comme on va sans doute avoir pas mal de bots sur IRC et parce qu'on veut rendre ça le plus simple possible, on a fait deux propositions d'API (une de Darks, une de moi même). Et on voulait l'avis de la commu en tant que futur utilisateur des bots et futur utilisateur de l'API.
Dans l'idée, l'API finale sera ensuite mise en forme et mise sous forme de paquet sur l'AUR / PyPi.
Les différents codes proposés ci-dessous sont à retrouver en détail sur
le dépôt gitea de GLaDOS qui nous sert un peu de labo pour l'instant.
Les deux APIs sont très proches et les différences se jouent en fait sur des subtilités sur la manière d'intégrer les commandes au bot. Les fonctionnalités d'une API sont programmables sur l'autre modulo quelques modifications à la marge.
API de Darks
Pour utiliser cette API il faut commencer par importer la classe
Bot. Il faut ensuite créer une instance en lui donnant quelques paramètres élémentaire : nom d'utilisateur et mot de passe, serveur IRC, port, channels IRC sur lesquels se connecter et longueur de l'historique des messages :
from ircapi import Bot
my_bot = Bot(
credentials=("Bob", "MySuperPassword") # Use any method you want to import secrets
server=('irc.planet-casio.com', 6697) # Define the IRC server
channels=["#general", "#fun"], # Auto-join those channels
history=100 # Keep last 100 messages received
)
import commands
Il suffit ensuite de créer des fonctions décorée pour créer des commandes, par exemple dans un fichier
commands.py :
@my_bot.command("!ping"):
def cmd_pong(msg):
my_bot.send(msg.to, "Pong!")
Il existe plusieurs décorateurs selon la commande que l'on veut créer :
- Bot.command : fait réagir le bot sur une commande
- Bot.on : fait réagir le bot sur un évènement donné
- Bot.channel : fait réagir le bot sur un salon particulier
- Bot.user : fait réagir le bot à un utilisateur particulier
Une fois le bot défini on peut importer des fichiers de commandes supplémentaires via
import mes_commandes en plaçant les import à la fin du fichier.
Une documentation auto-générée des commandes est également accessible.
Lien vers
le code de démonstration
API de Shadow
Pour utiliser l'API, il faut importer le module
api et utiliser ensuite la classe
api.Bot et les décorateurs associés.
Pour créer un bot le code ressemble beaucoup à l'API de Darks :
from irc_api import api
from commands import test
glados = api.Bot(
(USER, PASSWORD),
('irc.planet-casio.com', 6697),
["#general", "#glados"],
"!"
)
glados.add_command(test)
À la différence de l'API précédente, il n'y pas d'historique, et le préfixe des commandes est factorisé au niveau du bot.
Les décorateurs sont identiques (à la différence qu'il faut utiliser
@api.<decorateur> et non
@mon_bot.<decorateur>)
Les commandes peuvent être placées dans un fichier externe importé au début du fichier principal, par exemple, si l'on a dans le fichier
commands.py la commande :
@api.command("ping")
def test(bot, msg, *args):
"""Test de module de fonction."""
bot.send(msg.to, "Pong !")
Dans le fichier principal, le fichier peut être importé au début du fichier et l'on peut ensuite bind les commandes au bot via
Bot.add_command ou
Bot.add_commands pour importer une liste de commandes.
L'aide auto-générée est également présente, il suffit d'ajouter la fonction
api.auto_help dans les fonctions du bot.
Lien vers
le code de démonstration.
Conclusion
Comme dit plus haut, les deux API sont extrêmement proches, la seule grande différence réside dans la manière dont les commandes sont ajoutées au bot.
Dans l'API de Darks, les commandes sont bind au bot dès le début grâce au décorateur, mais les fichiers de commandes sont à importer en fin de fichier (→ syntaxe Flask)
Dans mon API, les commandes sont définies indépendamment du bot et peuvent ensuite être bind à un bot. (→syntaxe Discord)
On s'en remet à vous pour choisir votre manière de travailler préférée et on pourra commencer à faire une API sympa !
Citer : Posté le 11/06/2023 23:15 | #
J'ai pas encore regardé le code de démo, mais vu comme ça j'aime bien l'idée de Shadow, mais j'aimerai éviter d'avoir a faire une liste de
Pour l'historique, j'imagine que c'est implémentable dans les deux API c'est juste pas fait sur celle de Shadow.
J'irai regarder le code demain.
Citer : Posté le 11/06/2023 23:20 | #
C'est plus "simple" que ça, tu peux faire un truc comme ça :
from irc_api import api
@api.<>
def cmnd1(bot, msg, *args):
…
…
cmnds = [cmnd1, cmnd2, cmnd3, …]
# main.py
from irc_api import api
from commands import cmnds
mon_bot = api.Bot(…)
mon.bot_add_commands(cmnds)
Mais je suis d'accord que c'est plus lourd que l'API de Darks.
Oui c'est ça
Citer : Posté le 12/06/2023 11:18 | #
Petite update par rapport à l'import de commande, on peut maintenant importer un module de commandes directement à la création du bot
import commands as cmnds
glados = api.Bot(
(USER, PASSWORD),
('irc.planet-casio.com', 6697),
["#general", "#glados"],
cmnds,
prefix="!"
)
Pour avoir l'aide auto-générée, il faut alors simplement faire l'import suivant : from irc_api.api import auto_help dans le module de commande. On peut ajouter plusieurs modules de commandes, et la doc est gérée séparément (il faut importer auto_help dans chaque module de commande).
Pour ajouter plusieurs modules d'un coup :
import base_commands as bc
import admin_commands as ac
glados = api.Bot(
(USER, PASSWORD),
('irc.planet-casio.com', 6697),
["#general", "#glados"],
bc, ac
prefix="!"
)
Il y a toujours la possibilité de rajouter des modules ou des listes de fonction après la déclaration du bot et on peut également "supprimer" des commandes dynamiquement.
J'ai aussi rajouté un système de parsing des arguments avec une gestion des quotes., Les arguments détectés sont renvoyés dans le *args des commandes. Par exemple, si on a la commande suivante :
def test(bot, msg, *args):
bot.send(msg.to, f"arguments : {args}")
Dans IRC, on envoie : !test "argume 'nt 1'" arg2 "cmd 'arg 1' arg2", le bot va afficher : arguments : ("argume 'nt 1'", 'arg2', "cmd 'arg 1' arg2"), il gère aussi tous les accents, caractères spéciaux, emojis etc).
Citer : Posté le 12/06/2023 15:50 | # | Fichier joint
Juste pour montrer un truc sympa
Le code de la commande wiki ressemble à ça :
def wiki(bot, msg, text: str, limit: int=1):
session = requests.Session()
params = {
'action': 'opensearch',
'search': text,
'limit': limit,
'namespace': 0,
'redirects': 'resolve',
'format': 'json'
}
response = session.get(url="https://fr.wikipedia.org/w/api.php", params=params, timeout=(4, 12)).json()
if len(response[1]) == 0:
bot.send(msg.to, f"Aucun résultat trouvé pour la recherche : {text}.")
else:
bot.send(msg.to, f"{len(response[1])} résultat{('', 's')[limit > 1]} pour la recerche : '{text}'.")
for name, link in zip(response[1], response[3]):
bot.send(msg.to, f" {name} : {link}")
Les arguments demandés par la commandes sont analysés et les champs sont remplis en respectant les types des variables et en mettant les valeurs par défaut si ça ne matche pas.
Citer : Posté le 12/06/2023 22:28 | #
Est-ce que tu pense pouvoir supporter l'i18n sur les noms des commandes ? Genre help = aide
Ou des alias de commandes.
Citer : Posté le 12/06/2023 22:34 | #
Pour l'instant, ce n'est pas pensé pour… les commandes sont stockées dans un dictionnaire où le nom sert de clef. Donc ça doit être possible, mais en bougeant un peu la manière dont c'est géré.
Après ce que je dois pouvoir faire, c'est modifier directement dans décorateur, par exemple :
def help(bot, msg):
…
Citer : Posté le 12/06/2023 22:45 | #
Bon finalement c'est géré, ce fut plus simple que prévu, la nouvelle syntaxe est donc :
def help(bot, msg):
…
Le nom de la commande est valide pour l'appeler, tous les alias aussi, mais la fonction n'apparaît dans la documentation qu'avec son nom.