Créer un bot conversationnel pour Twitch

Créer un bot conversationnel pour Twitch

Aujourd’hui on va jouer autour d’une api qui permet de créer des interactions avec le chat d’une chaîne twitch. On ne parlera pas ici de piloter votre application (typiquement un jeu) à partir de commandes lancées dans un jeu, mais plutôt de créer une interaction conversationnelle directement dans le chat twitch.

Il existe de nombreux robots qui permettent de créer des interactions[1-3] , mais dès que l’on souhaite sortir des sentiers battus, il devient rapidement nécessaire de développer un peu. Heureusement les prérequis sont très réduits, l’API en question est simple, efficace et va à l’essentiel. Il existe de nombreux tutoriels sur internet, que ce soit en node ou en python (ou autre), en premier lieu la doc officielle twitch qu’il faut impérativement lire (elle est très courte)

Ici nous allons nous focaliser sur un exemple concret en nodejs. La première chose est d’avoir un compte twitch pour le bot. Vous pouvez utiliser votre compte personnel, au moins dans un premier temps, mais ce n’est pas idéal. En effet il n’est pas possible d’utiliser un autre pseudo que le compte auquel il est rattaché, donc le bot parlera au même titre que vous, ce qui peut créer des confusions.

Une fois le compte créé, il faut générer un token d’authentification, ce qui va permettre d’identifier le bot au niveau de votre application. Il suffit d’aller sur cette page et de demander un token. Ce token est bien entendu privé, copiez le dans un coin de votre disque dur, et ne le partagez en aucun cas. On a donc pour le moment:

  • BOT_USERNAME : c’est le nom du compte/de la chaîne
  • OAUTH_TOKEN: le token récupéré plus haut

La première chose a faire est d’installer le package tmi.js:

npm init
npm install -j tmi.js

Attention: il existe un package ‘tmi‘ qui n’a rien à voir, il faut bien utiliser le package tmi.js !

Première application

Notre première application va être très simple consister a se connecter sur une ou plusieurs chaînes, et de répondre quand on lui parle (avec @). Cela va nous permettre de voir comment se connecter au chat, et comment gérer les messages.

Avant de se pencher sur le code, précisons que nous l’exécuterons en lui passant les variables suivantes CHANNEL_NAME et CHANNEL_PASSWORD avec le contenu des données récupérées plus haut.

CHANNEL_NAME= CHANNEL_PASSWORD= node ./bot.js

On va commencer par importer le package tmi.js, récupérer les variables d’environnement, construire un objet qui va nous servir à créer la connexion avec le chat. On utilise process.env pour récupérer les variables.

const tmi = require('tmi.js');  // API Twitch

// On récupere les variables passées
let botname = process.env.CHANNEL_NAME;
let botpassword = process.env.CHANNEL_PASSWORD;

// Si elles n'existent pas, on quitte
if (!botname || !botpassword) {
  console.error('No CHANNEL_NAME or CHANNEL_PASSWORD environment variable found. Exiting.');
  process.exit(-1);
}

// On ajoute le préfixe oauth: 
botpassword = 'oauth:' + botpassword;

Et on construit l’objet opts, qui contient les informations de connexion, la liste des chaines auxquelles de connecter, ainsi qu’une option pour que le bot se reconnecte automatique en cas de déconnexion

// On construit l'objet qui contient les parametres de connexion
const opts = {
  identity: {
    username: botname,
    password: botpassword
  },
  channels: [
    botname
   //on peut ajouter d'autres chaines
  ],
  // Automatic reconnection
  connection: { reconnect: true }
};

Voila, maintenant on est prêt à se connecter! On crée un objet client à l’aode de notre objet opts. On lui associe deux gestionnaires d’évenement (dont nous verrons la définition plus bas), et on se connecte!

// Création d'un client 
const client = new tmi.client(opts);
// Gestionnaire d'évenements
client.on('message', onMessageHandler);
client.on('connected', onConnectedHandler);
// Connexion! 
client.connect();

En ce qui concerne le handler ‘connected‘, on va se contenter d’un log en console:

// Fonction appellée a chaque fois que la connexion est établie
function onConnectedHandler(addr, port) {
  console.log(`* Connected to ${addr}:${port}`);
}

Maintenant que tout est en place, il nous reste à rentrer dans le vif du sujet, à savoir écrire la fonction qui va recevoir tous les messages qui passent sur le chat des chaines sur lesquelles le bot est connecté.

La fonction onMessageHandler reçoit 4 paramètres:

  • target: le nome de la chaine ou a été envoyé le message.
  • context: les informations sur l’utilisateur qui a envoyé le message. On pourra récupérer son nom, ainsi que son statut par rapport a la chaine (abonné, modérateur, …)
  • msg: le texte du message
  • self: indique si le message a été émis par le bot lui même. Sert pour ignorer ces messages.

Notre fonction pour répondre si quelqu’un envoie un message au bot ressemblera à cela:

function onMessageHandler(target, context, msg, self) {
  try {
    // Ignore les messages du bot
    if (self) { return; }
    // On vérifie si le nom du bot est dans le message
    if (msg.indexOf('@'+botname)>=0) {
        // Si oui on répond a l'utilisateur
        client.say(target, 'Hello ' +  context['display-name']);
        // on log le contexte pour voir son contenu
        console.debug(context); 
    }
  }
  catch (e) {
    console.error(e.stack);
  }

C’est donc assez simple, si le nom du bot apparait dans le message, le bot répondra à l’utilisateur. Pour cela, on utilise la fonction say du client, qui prend le nom de la chaine en premier paramètre et le texte à envoyer sur le second.. Il est en effet judicieux de répondre sur la chaine où l’on a reçu le message 🙂 . Pour répondre à l’utilisateur, on utilise le champ display-name de la variable contexte. On en profite pour loguer le contenu de la variable context pour voir les différentes valeurs qu’elle peut contenir.

Seconde Application: un bot traducteur!

Maintenant que nous avons vu les principes de base, allons un peu plus loin, avec une seconde application, un peu plus utile. Elle a déjà servi comme point de départ pour un bot plus sophistiqué (The Tangerine Bot) mais aussi pour quelques chaines twitch (speakeazybot par exemple).

Le code source de ce bot est disponible ici et se base sur l’utilisation de google translate pour la traduction. Il existe d’autres moteurs de traductions comme Yandex ou DeepL, j’ai choisi google translate car il propose une version gratuite, qui permet de tester le principe. Google comme Yandex ou DeepL proposent des versions payantes, qui sont de qualité sensiblement meilleure et avec beaucoup moins de limitation sur le nombre de transactions. A noter que Yandex est très performant pour les traductions depuis et vers le russe, et DeepL s’en sort très bien avec le japonais!

Pour commenter on ajoute le paquet googletrans:

npm install -j googletrans

Au niveau du code, on importe le paquet:

// API Google Translate
const gtrans = require('googletrans').default;

Ensuite on se dote d’un tableau avec les commandes que l’on va vouloir ajouter. Le principe sera d’avoir une commande par langue. Par exemple !de permettra de traduire un texte en allemand. L’API de traduction se chargera de détecter la langue source et de la traduire dans la langue cible.

const tr_lang = {
  'de': ['de', 'sagt'],
  'en': ['en', 'says'],
  'fr': ['fr', 'dit'],
  'pt': ['pt', 'disse'],
};

Maintenant il nous reste a répondre au cas ou l’on détecte l’une des commandes !de !en !fr ou !pr, en ajoutant dans la fonction onMessageHandler:

// Si le message ne commence pas pas ! on ne fait rien
if (msg[0] != '!') return;

// On récupere la commande
let cmd = msg.split(' ')[0].substring(1).toLowerCase();

// Est ce l'une des commandes de traduction?
if (cmd in tr_lang) {
  let ll = tr_lang[cmd];
  let txt = tMsg.substring(1 + cmd.length);
  // Traduction
  gtrans(txt, { to: ll[0] }).then(res => {
    // Construit le message de réponse
    let answer=context['display-name']+' '+ll[1]+': '+res.text;
    client.say(target, answer);
    }).catch(err => {
      console.error('Translation Error:', err);
    })
  return;
}

Lorsqu’une commande est interceptée, l’API de traduction est appelée, et le résultat, asynchrone est récupéré dans la variable res, si tout s’est bien passé. Ensuite, le message de réponse est construit et envoyé en guise de réponse. Par exemple:

Conclusion

Nous venons de faire nos premiers pas dans le monde des bots irc/twitch. Il y a plein d’applications à imaginer! Des applications créatives, qui changent des bots classiques, et surtout des bots qui polluent les chat, ou qui se contentent d’écouter les conversations, de les stocker on ne sait ou, pour en faire on ne sait quoi…

Dans les prochaines parties, nous aborderons la description de certains aspects d’un bot un peu plus sophistiqué que j’ai créé pour une chaîne musicale sur twitch, le Tangerine Bot! Il s’agira de stocker des informations en base pour rendre le comportement du bot plus riche et d’avantage adapté à chaque utilisateur.

Liens