Bonjour à tous,
Je m'amuse à faire un bot IRC (pas très original, je sais).
Le but c'est de le faire le plus simple possible et pouvoir
lui ajouter des fonctionnalités avec des scripts/plugins.
Je sais pas trop comment faire le lien entre le bot et ses plugins
Je dois pouvoir faire 2 choses:
1. associer des méthodes à un événement (type du message provenant du serveur)
2. ajouter des commandes pour contrôler le bot.
J'ai cherché mais pas trouvé grand chose, pour le moment je fais çà:
class Plugin
@@registered = []
def self.registered
@@registered
end
def self.inherited(subclass)
@@registered << subclass.new
end
end
class Test < Plugin
# répond au ping
def onPing(bot, msg)
bot.pong(msg.params[0])
end
# Exemple d'une commande
# là je pense qu'il faudrait que j'utilise OptionParser
# ou un module équivalent pour pouvoir executer des
# commandes avec paramètres plus facilement.
def doHelp(bot, msg)
if msg.params.last =~ /^!help (\S+)/
...
end
end
end
class Bot
def run
# lit sur la socket
# parse les données reçues
msg = Message.parse(data)
event = "on#{msg.command.capitalize}"
Plugin.registered.each do |pl|
# parcours toutes les instances de Plugin
# et vérifie si une méthode on<Event> existe
if pl.respond_to?(event)
pl.send(event, self, msg)
end
end
if msg.command == "PRIVMSG"
# message reçu sur un channel ou en privé
# vérifier si c'est une commande du bot
# préfixée par un '!' ou adressée directement au bot.
...
end
end
end
ou bien, juste utiliser un hash avec comme clé le type d'événement et comme valeur, la méthode associée.
mais un événement peut avoir plusieurs méthodes à appeler.
puis certaines prendront plus de temps que d'autres et devront être exécutées dans un thread ?
bref je suis un peu paumé, si vous avez quelques conseils, liens qui pourraient m'aider pour faire çà proprement.
# Cinch
Posté par tetraf . Évalué à 1.
Cinch permet la création de bot IRC en Ruby, tu dois pouvoir jeter un œil sur son système de chargement des extensions.
[^] # Re: Cinch
Posté par Foo . Évalué à 0.
Je vais regarder çà, merci!
# Parcourir une liste de plugins
Posté par Anonyme . Évalué à 0. Dernière modification le 17 août 2013 à 14:28.
Je ne code pas en Ruby, donc je ne saurai faire l'implémentation de ce que je vais te proposer.
Ce que je ferais en tout cas, c'est lister les plugins disponibles (donc par exemple un fichier -> un plugin avec une classe bien nommée etc…), puis les méthodes de chacun avec une méthode particulière qui va permettre par exemple de « recevoir » les messages entrants/sortants, les traiter et finir par les refourger à qui doit l'avoir.
Avec les méthodes d'introspection de ces langages, la tâche n'est pas bien difficile.
Socket -> réception de message -> traitement par l'application -> traitement par les plugins -> retour du message traité (pas forcément modifié) à l'application -> cours normal des choses.
Le traitement des plugins ça peut être d'envoyer quelque chose automatiquement, de faire une notification via D-Bus, ou de modifier le message entrant, que sais-je.
Et on peut aussi bien imaginer une méthode qui va dans le sens inverse : modification de la saisie d'un message d'un utilisateur pour faire de la correction orthographique automatique par exemple.
Et si tu juges qu'un traitement est long, c'est alors au plugin de se « threader ».
Voilà ma vision des choses.
[^] # Re: Parcourir une liste de plugins
Posté par Foo . Évalué à 0.
Merci, çà m'aide beaucoup.
çà me plaît bien le traitement en sens inverse pour filtrer les messages entrants et sortants et du coup il faudrait un ordre de priorité pour charger les plugins/les parcourir.
[^] # Re: Parcourir une liste de plugins
Posté par Anonyme . Évalué à 0.
Ouais, tu peux faire de manière relative, genre je mets arbitrairement 100 alors que j'ai un plugin qui demande la priorité 10 quoi.
Après on peut rentrer des des étages plus complexes genre les plugins de prio 0 à 100 sont prévus pour faire des actions importantes et longues alors que les plugins de 200 à 300 sont plus légers.
On peut aussi imaginer un gestionnaire de conflit, genre tel ou tel plugin doit absolument passer avant/après tel autre. Bonjour les dépendances 8D
De toute façon il n'y a de limite que l'imagination :)
# Pour ma part, je ne passerais pas par des classes mais par des mixin
Posté par totof2000 . Évalué à 2.
Je ne suis pas sur d'avoir compris exactement ce que tu veux faire, mais je pense que dans ce cas ce serait plus approprié.
En nommant ton plugin du nom de ton event, tu peux le charger dynamiquement lors de la réception de l'evenement.
http://www.tutorialspoint.com/ruby/ruby_modules.htm
[^] # Re: Pour ma part, je ne passerais pas par des classes mais par des mixin
Posté par totof2000 . Évalué à 2.
J'avais oublié également la possibilité de passer par la méthode method_missing. Sinon, si Ruby t'intéresse, je te conseille vivement le bouquin les design pattern en ruby. Tu y trouveras plein de petites astuces que tu ne trouves pas forcément ailleurs et qui te permettront de comprendre la philosophie qui est derrière la programmation Ruby.
[^] # Re: Pour ma part, je ne passerais pas par des classes mais par des mixin
Posté par totof2000 . Évalué à 2. Dernière modification le 17 août 2013 à 16:44.
J'ai pas pu m'empêcher de tester … Méchant, j'avais d'autres choses à faire … :)
Voici la piste que je prendrais :
Le module event2 n'existe pas donc tu auras une erreur interceptée par begin/rescue (traitement des exceptions en Ruby).
Il y a une partie de metaprogrammation pour appeler le bon module, je peux expliquer si tu comprends pas (mais là j'ai pas le temps, peut-être ce soir si ça t'intéresse).
En tout cas si tu arrives à comprendre ça, et que ça peut t'aider à smplifier ton code, tant mieux.
[^] # Re: Pour ma part, je ne passerais pas par des classes mais par des mixin
Posté par Foo . Évalué à 0.
Désolé mais je vais encore te faire perdre ton temps,
je comprends pas :p
Si j'ai un module qui correspond à un type d'événement, comment je fais pour exécuter plusieurs méthodes sur cet événement dans différents plugins? je sais pas si je suis bien clair.
En gros, je reçois un message de type « JOIN » (joindre un salon).
Merci en tous les cas.
[^] # Re: Pour ma part, je ne passerais pas par des classes mais par des mixin
Posté par totof2000 . Évalué à 2. Dernière modification le 17 août 2013 à 21:19.
Je ne voyais pas le problème comme ça … A priori je ne vois pas l'intéret de le faire, mais tu dois avoir tes raisons (et j'aimerais bien savoir histoire de pouvoir te répondre au mieux). Si j'ai bien compris, tu voudrais que lorsque tu reçois un type d'évenement, tous les plugins ayant de type d'evenement défini dans son code soit exécuté (à priori, chaque plugin exécuterait des actions différentes) ? Ce n'est donc pas une surcharge. Il doit y avoir moyen de le faire, je regarde ça. Mais à priori, tu ne pourras pas appeler chaque module JOIN avec une méthode onjoin dans chaque plugin. Problème intéressant, je regarde (mais là j'ai rien d'urgent à faire pour le moment).
[^] # Re: Pour ma part, je ne passerais pas par des classes mais par des mixin
Posté par totof2000 . Évalué à 2.
Je pense avoir trouvé … Je teste.
[^] # Re: Pour ma part, je ne passerais pas par des classes mais par des mixin
Posté par totof2000 . Évalué à 2.
Voilà ce que j'ai pu faire ce soir :
Explications : tu mets le main dans un repertoire, et tu positionne les plugins dans un sous-répertoire plugins (ou celui que tu veux).
Lorsque tu crées ton objet bot, tu lui passe le repertoire contenant les plugins. Il va aller alimenter le tableau @@modules=[] qui contiendra la liste de tous les plugins présents.
Ensuite lorsque tu appeleras la méthode onJoin, elle ira appeler la méthode onJoin de chaque module référencé dans le tableau.
On pourrait tester la pésence de la fonction onJoin pour chaque module (voir http://ruby-doc.org/core-1.9.3/Module.html#method-i-included_modules pour ruby 1.9.3)
J'ai d'autres idées mais il est un peu tard ce soir, donc on verra plus tard si tu as besoin d'autres choses.
[^] # Re: Pour ma part, je ne passerais pas par des classes mais par des mixin
Posté par totof2000 . Évalué à 2.
Je te rassure, je ne perds pas mon temps, ça m'amuse ce genre de problème. En plus ça permet d'apprendre et de se dérouiller quand on fait plus de ruby depuis un moment (je suis sur Erlan en ce moment). C'était une boutade lorsque je te disais "méchant" :)
[^] # Re: Pour ma part, je ne passerais pas par des classes mais par des mixin
Posté par Foo . Évalué à 0.
Je viens de rentrer et de lire tes réponses, je prends le temps de bien comprendre ton code et je réponds plus tard.
Mais juste pour clarifier ce que je souhaites faire.
Oui, c'est exactement çà.
Je te donne un exemple, toujours avec ces événements « JOIN » quand un utilisateur rejoint un salon.
Il pourrait y avoir une méthode qui envoie un message de bienvenue à l'utilisateur.
Une autre qui donne les droits d'opérateur sur le salon à cet utilisateur.
Pourquoi pas une autre qui prévient l'utilisateur parce qu'il utilise son compte root pour faire de l'IRC =/
ceux sont des exemples mais il y a plein d'autres possibilités plus ou moins utiles rien que sur cet événement « JOIN ».
Donc faut séparer toutes ces actions dans divers plugins selon ce que l'on veut utiliser comme fonctionnalités,
je peux pas tout regrouper dans la même méthode.
Merci beaucoup pour ton aide! à plus tard =)
[^] # Re: Pour ma part, je ne passerais pas par des classes mais par des mixin
Posté par Foo . Évalué à 0.
Je me suis inspiré de ce que tu m'as proposé avec les modules.
Je sais pas si çà apporte grand chose en fait, j'ai repris le même principe que dans mon premier exemple pour accéder aux méthodes.
En fait j'ai fait quasiment la même chose, au lieu d'hériter d'une classe, j'inclus un module.
Je pense que je vais utiliser une classe toute simple comme base pour les plugins, çà me parait plus simple.
[^] # Re: Pour ma part, je ne passerais pas par des classes mais par des mixin
Posté par totof2000 . Évalué à 2. Dernière modification le 19 août 2013 à 13:05.
Ca peut marcher.
Par contre j'utiliserais method_missing à la place du bloc ci-dessous :
def exists?(msg)
# vérifie si une méthode on<EVENT> existe
# et l'exécute.
meth = "on" + msg.command
send(meth, msg) if respond_to?(meth)
end
dans l'objet Plugin, tu définis la méthode method_missing qui intercepte tous les appels à on_ et qui ne fait rien si l'event est défini.
Par contre s'il s'agit d'un appel à une autre méthode => appel à la méthode method_missing du parent.
Sinon, le fait qu'il n'y ait pas trop de différence entre ce que tu as fait au début et ce que j'ai proposé vient du fait que je n'avais pas compris le besoin initial.
# Hors ~~service~~ sujet
Posté par Marotte ⛧ . Évalué à 2. Dernière modification le 19 août 2013 à 00:52.
Ça c'est de la faute d'orthographe ! Je te fais cette remarque car tu me sembles avoir une orthographe tout à fait correcte mais celle-ci tu la réitères dans tes autres commentaires, ça pique les yeux !
Notons que çà existe mais ce n'est pas la même chose…
[^] # Re: Hors ~~service~~ sujet
Posté par Foo . Évalué à 0.
En effet, j'ai cette mauvais habitude, je ferai attention dorénavant.
Suivre le flux des commentaires
Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.