Les membres ayant 30 points peuvent parler sur les canaux annonces, projets et hs du chat.
La shoutbox n'est pas chargée par défaut pour des raisons de performances. Cliquez pour charger.

Forum Casio - Projets de programmation


Index du Forum » Projets de programmation » API pour les bots IRC
Shadow15510 Hors ligne Administrateur Points: 5504 Défis: 18 Message

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 !


Eragon Hors ligne Gardien des bots Points: 479 Défis: 0 Message

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
glados.add_command(…)
ça me semble un peu lourd.
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.
Shadow15510 Hors ligne Administrateur Points: 5504 Défis: 18 Message

Citer : Posté le 11/06/2023 23:20 | #


mais j'aimerai éviter d'avoir a faire une liste de glados.add_command(…)

C'est plus "simple" que ça, tu peux faire un truc comme ça :
# commands.py
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.

Pour l'historique, j'imagine que c'est implémentable dans les deux API c'est juste pas fait sur celle de Shadow.

Oui c'est ça
"Ce n'est pas parce que les chose sont dures que nous ne les faisons pas, c'est parce que nous ne les faisons pas qu'elles sont dures." Sénèque

Shadow15510 Hors ligne Administrateur Points: 5504 Défis: 18 Message

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

from irc_api import api
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 :
from irc_api import api
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 :
@api.command("test")
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).
"Ce n'est pas parce que les chose sont dures que nous ne les faisons pas, c'est parce que nous ne les faisons pas qu'elles sont dures." Sénèque

Shadow15510 Hors ligne Administrateur Points: 5504 Défis: 18 Message

Citer : Posté le 12/06/2023 15:50 | # | Fichier joint


Juste pour montrer un truc sympa



Le code de la commande wiki ressemble à ça :
@api.command("wiki", desc="wiki <recherche> [limite=1]\nFait une recherche wikipedia.")
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.
"Ce n'est pas parce que les chose sont dures que nous ne les faisons pas, c'est parce que nous ne les faisons pas qu'elles sont dures." Sénèque

Eragon Hors ligne Gardien des bots Points: 479 Défis: 0 Message

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.
Shadow15510 Hors ligne Administrateur Points: 5504 Défis: 18 Message

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 :
@api.command(name=("aide", "help", "documentation")
def help(bot, msg):
    …

"Ce n'est pas parce que les chose sont dures que nous ne les faisons pas, c'est parce que nous ne les faisons pas qu'elles sont dures." Sénèque

Shadow15510 Hors ligne Administrateur Points: 5504 Défis: 18 Message

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 :
@api.command(name="aide", alias=("help", "documentation"))
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.
"Ce n'est pas parce que les chose sont dures que nous ne les faisons pas, c'est parce que nous ne les faisons pas qu'elles sont dures." Sénèque


LienAjouter une imageAjouter une vidéoAjouter un lien vers un profilAjouter du codeCiterAjouter un spoiler(texte affichable/masquable par un clic)Ajouter une barre de progressionItaliqueGrasSoulignéAfficher du texte barréCentréJustifiéPlus petitPlus grandPlus de smileys !
Cliquez pour épingler Cliquez pour détacher Cliquez pour fermer
Alignement de l'image: Redimensionnement de l'image (en pixel):
Afficher la liste des membres
:bow: :cool: :good: :love: ^^
:omg: :fusil: :aie: :argh: :mdr:
:boulet2: :thx: :champ: :whistle: :bounce:
valider
 :)  ;)  :D  :p
 :lol:  8)  :(  :@
 0_0  :oops:  :grr:  :E
 :O  :sry:  :mmm:  :waza:
 :'(  :here:  ^^  >:)

Σ π θ ± α β γ δ Δ σ λ
Veuillez donner la réponse en chiffre
Vous devez activer le Javascript dans votre navigateur pour pouvoir valider ce formulaire.

Si vous n'avez pas volontairement désactivé cette fonctionnalité de votre navigateur, il s'agit probablement d'un bug : contactez l'équipe de Planète Casio.

Planète Casio v4.3 © créé par Neuronix et Muelsaco 2004 - 2024 | Il y a 84 connectés | Nous contacter | Qui sommes-nous ? | Licences et remerciements

Planète Casio est un site communautaire non affilié à Casio. Toute reproduction de Planète Casio, même partielle, est interdite.
Les programmes et autres publications présentes sur Planète Casio restent la propriété de leurs auteurs et peuvent être soumis à des licences ou copyrights.
CASIO est une marque déposée par CASIO Computer Co., Ltd