BOT

Introduction

Je vous propose de construire étape par étape, de manière simple, un robot IRC. Ceux qui ont déjà de bonnes bases sur la connexion et sur les sockets peuvent passer directement à la partie structure.

Connexion à une machine

Connexion

D'abord votre bot va devoir se connecter à un serveur. Vous trouverez  ici un petit programme en Python qui ouvre une socket en TCP sur le port 6667, exactement comme le ferait un client IRC "normal". Voici la version en C, un moins parlante. Vous pouvez aussi établir cette connexion "à la main" en tapant simplement sous UNIX :

telnet <server> 6667
Par exemple :
ted:~>telnet tibob.via.ecp.fr 6667
Trying 138.195.128.128...
Connected to tibob.via.ecp.fr.
Escape character is '^]'.

Sous Windows, il suffit de faire "connexion distante", et préciser, en plus de l'hôte, le port "6667" au lieu du port "telnet".
Maintenant, on est connecté, mais on n'est pas encore authentifié au niveau du serveur IRC.

Login

Le Login est une phase ou le client donne les infos nécessaires pour que le serveur nous "enregistre". Le protocole est une fois de plus défini dans le  RFC 1459 au paragraphe 4.1, et se résume à peu pres à : Remarques :
  1. N'oubliez pas le [enter] en fin de ligne ('\n' en C)
  2. hostname et servername sont ignorés par le serveur (ca ne sert que lorsque deux serveurs discutent entre eux)
  3. username sera remplacé par le vrai login si la machine depuis laquelle on se connecte a un démon d'identification (identd). C'est le cas de la plupart des machines.
  4. certains serveurs demandent un mot de passe, qui doit etre specifié dans une troisième ligne : PASS <password>
  5. certains serveurs vont envoyer un PING :<numéro>, auquel cas on devra répondre avec un PONG <numéro> pour terminer l'enregistrement.
Voyons cela en pratique :
ted:~>telnet tibob.via.ecp.fr 6667
Trying 138.195.128.128...
Connected to tibob.via.ecp.fr.
Escape character is '^]'.
USER toto titi tata tutu
NICK toutou
:tibob.via.ecp.fr 001 toutou :Welcome to the Internet Relay Network toutou!fred@ted.via.ecp.fr
:tibob.via.ecp.fr 002 toutou :Your host is tibob.via.ecp.fr, running version 2.9.4
:tibob.via.ecp.fr 003 toutou :This server was created Tue Feb 24 1998 at 19:32:22 CET
:tibob.via.ecp.fr 004 toutou tibob.via.ecp.fr 2.9.4 oirw abiklmnopqstv

La plupart des clients exécutent alors immédiatement les deux commandes suivantes :

Une fonction qui effectue le LOGIN est très simple; en voici  un exemple en Python et en C.
On a maintenant un robot capable de faire de l'IRC, c'est déjà beaucoup !

PING ? PONG !

Malheureusement, l'espérance de vie de notre robot est, à cet instant, limitée à quelques minutes seulement. En effet, si l'on poursuit notre "TP", on voit apparaître au bout d'un instant :

PING :tibob.via.ecp.fr
ERROR :Closing Link: toutou[fred@ted.via.ecp.fr] (Ping timeout)
Connection closed by foreign host.

Le serveur nous envoie un PING pour vérifier que la connexion est bonne. Il faut répondre... PONG bien sûr ! C'est ce qu'on appelle "le challenge" du serveur. Ne pas oublier de préciser la source du PING après le PONG :

PING :tibob.via.ecp.fr
PONG :tibob.via.ecp.fr

S'il n'y a pas de PONG, le serveur envoie un message d'erreur et ferme la connexion. Le délai pour répondre est de l'ordre d'une minute, selon les serveurs, mais on a tout intérêt à répondre le plus tôt possible !


Voici une fonction en Python ou en C qui écoute sur la socket que l'on a ouvert, et repond en cas d'un eventuel PING.

Zebot

Parfait, toutes les fonctions vitales d'un bot sont là. On peut les assembler pour faire un bot qui va se connecter et rester connecté indéfiniment : c'est Zebot, le bot zéro, et son homologue en Python Pybot.

On peut même faire un script Shell, qui effectue les opérations de notre "TP", puis rentre dans une boucle qui PONG; c'est le Sh-Bot.

Attention, c'est programmé au plus simple. On remarquera que le programme plante si on flood le bot par exemple. A perfectionner !
 

 Sockets

Introduction

Zebot, tel qu'il a été construit, n'a aucun avenir. On verra le problème de structure tout à l'heure, mais avant cela il y a deux problemes de socket : Or un bot doit pouvoir faire plusieurs choses pendant qu'il écoute sur le réseau, et il doit aussi pouvoir écouter sur plusieurs sockets, s'il accepte les DCC CHAT par exemple.

Select/fd_set

Ce paragraphe va etre très technique, mais apporte une solution propre à nos deux problèmes.

Conclusion

On a maintenant le nécessaire pour gérer un nombre illimité de sockets. Il reste bien sur à écrire les fonctions read_msg qui lisent les messages reçu, les traitent, et réagissent en fonction de leur contenu.
 
 

Configuration

Dernière chose avant de passer à la structure du bot : le parsage de la configuration. En effet, il ne faut pas faire comme dans Zebot où l'on a codé en dur l'adresse du serveur par exemple.

Généralement, on fait un fichier contenant les informations sur les serveurs IRC, un autre sur les utilisateurs du robot, avec leurs droits. Le robot doit lire ces fichiers au lancement, et sauvegarder sa configuration dedans au cours de son fonctionnement. Le parsage des fichiers est quelquechose d'assez fastidieux à coder en C. On peut utiliser Lex et Yacc, mais je ne sais pas encore comment. Ce système de fichiers de configuration permet de ne pas avoir à recompiler le bot à chaque changement de configuration, ce qui est appréciable !

Structure

Voilà, on y est ! Voyons d'abord les quatres fonctions du bot :

La Queue

Il faut absolument faire une fonction qui récupère les messages générés par les fonctions et les envoie vers le serveur IRC. En effet, si toutes les fonctions envoient des données directement dans les sockets, on a aucun contrôle sur le débit envoyé. Si toutes les fonctions parlent en même temps, le bot va flooder le serveur IRC. Il risque alors de se faire déconnecter. Toutes les fonctions doivent donc impérativement envoyer leur données vers une fonction qui les stocke, et les envoie progressivement.

Malheureusement, il n'y a pas d'algorythme sûr pour calculer le débit que l'on peut envoyer sans flooder le serveur. Il vaut mieux le déterminer expérimentalement, selon le serveur.

S'il y a beaucoup de messages en attente, la fonction de queue peut se charger aussi d'optimiser : passer les messages importants avant les autres, regrouper les modes, etc...

Le Parser

Le parser obtient les données brutes venant du serveur. Par exemple :
:toto!hero@glop.via.ecp.fr PRIVMSG #glop :Salut !
Il va se charger par exemple de séparer le nick, le login et le host, et mettre tout ça dans un belle structure. Un peut aussi en profiter pour rechercher les droits de la personne en question dans une userlist enregistrée quelquepart. Par exemple :
struct message {
struct user {
char *nick;
char *login;
char *host;
int user_level;
} *from;
char *commande;
char *to;
char *texte;
};

Ensuite, le parser passe cette structure au "if"...

Le "If"

Il s'agit en fait de la moulinette qui va aiguiller la structure envoyée par le parser vers les fonctions qui doivent être appelées. Cela peut être une opération très basique, comme une succession de "if(!strcmp(parsed_msg->commande, "PRIVMSG")) ..." ou autre chose de plus compliqué (voir Lignus II, quand le site sera fait...).

Les fonctions

Ce sont les fonctions qui constituent l'âme du bot. Les autres modules ne sont que des interfaces. Un fonction peut être très simple : quand elle reçoit un PING :%s, elle renvoie un PONG :%s, ou très complexe (selon l'imagination du concepteur).

Listes doublement chaînées

Un bot, c'est plein d'allocations dynamiques. Il est donc conseillé d'utiliser des listes doublement chaînees : c'est plus pratique et plus sûr.

Fin

Mais non, c'est pas fini ! Il reste plein de choses à faire, parler des listes chaînées, montrer d'autres bots, corriger les fotes etc...


Back