Je ne vais pas vous résumer ce que sont les bots et à quoi servent-ils. Dans ce post nous allons nous intéresser au MS Bot Framework et créé un Bot en Node.js et TypeScript ! Rentrons de suite dans le vif du sujet.

Microsoft Bot Framework ?

Le Bot Framework comprend trois éléments :

  • Bot builder : outils et services qui vont permettre de développer votre Bot. Le SDK (C# ou Node.js), l’émulateur, …
  • Developper portal : portail ou l’on enregistre son bot pour pouvoir le connecter à différents channels (Skype, Facebook messenger, Slack, …).
  • Bot directory : annuaire de bots enrgistrés et publiés. Vous pouvez les tester grâce à un chat intégré au site web.

Nous allons uniquement nous attarder sur le premier élément : Bot builder !

Pré-requis

Initialisation du projet

Permière étape indispensable à tout projet Node.js, on crée le packages.json avec la commande :

1
yarn init

Une fois le packages.json créé il nous faut ajouter les deux dépendances primordiales pour commencer à coder notre Bot :

1
yarn add botbuilder restify
  • botbuilder : le SDK qui va vous permettre de créer votre Bot.
  • resitfy : un Framework pour créer rapidement des services REST en Node.js.

Presque tous les exemples que vous pouvez voir sur la toile utilisent restify. Néanmoins le SDK, alias botbuilder, ne dépend pas de restify et donc rien ne vous empêche de faire du pur Node.js ou d’utiliser un autre Framework.

Maintenant on passe à la mise en place de TypeScript :

1
yarn global add ts-node typescript
  • ts-node : environnement d’exécution TypeScript pour Node.js. Grosso modo cela permet d’exécuter un fichier .ts directement sans avoir besoin de transpiler puis interpéter le .js de sortie.
  • typescript : ais-je besoin de présenter ce package :) ?

Bon parfait ! On a référencé les libs nécessaires pour coder notre Bot puis nous avons installé les outils pour l’écrire en TypeScript. Il nous manque une dernière chose ! Les fichiers de définition :

1
yarn add @types/node @types/restify --dev

Pas de fichier de définition pour botbuilder ? Normal il est déjà écrit en TypeScript ! Son fichier de définition est référencé dans la section typings de son packages.json.

Qui dit TypeScript, dit fichier de config pour passer les options au compilo -> tsconfig.json :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
{
"compilerOptions": {
"target": "es5",
"outDir": "dist",
"module": "commonjs",
"noImplicitAny": true,
"strictNullChecks": true,
"removeComments": true,
"moduleResolution": "node",
"sourceMap": true,
"inlineSources": true
},
"include": [
"app.ts"
]
}

Si vous n’avez pas l’habitude d’utiliser TypeScript avec Node.js, vous avez sans doute remarqué l’option moduleResolution qui a pour valeur node. Cette option affecte la façon dont le compilateur va résoudre les imports. Pour en savoir plus je vous invite à allez lire la très bonne documentation officiel sur le sujet.

Nous sommes prêt pour coder !

Code + test du bot

Première étape on importe nos deux dépendances :

1
2
import * as restify from 'restify';
import * as builder from 'botbuilder';

Puis on crée le serveur à l’aide de resitfy :

1
2
3
4
5
// Setup Restify Server
let server = restify.createServer();
server.listen(process.env.port || process.env.PORT || 3978, () => {
console.log(`${server.name} listening to ${server.url}`);
});

On peut enfin passer à la création du Bot. Il faut en premier lieu créer un connector. Deux possibilités :

  • ChatConnector : il permet de connecter notre bot aux différents channels (Slack, FaceBook messenger, text/SMS, …) ou à l’émulateur proposé par le Bot Framework. On verra son utilisation dans un second tuto.
  • ConsoleConnector : le nom est explicit. Grâce à lui on peut interagir avec notre bot au travers d’une simple console. C’est celui que nous allons utiliser.
1
2
3
// Create bot
let connector = new builder.ConsoleConnector().listen();
let bot = new builder.UniversalBot(connector);

Bon le serveur et le bot sont créés ! Comment interargir avec l’utilisateur ? Les conversations d’un bots sont découpées en entités appelées dialog. Créeons donc deux dialog que nous décortiquerons par la suite :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
bot.dialog('/', [
(session) => {
builder.Prompts.text(session, 'Hi! What is your name?'); // A
},
(session, results) => {
session.dialogData.name = results.response; // B
session.send(`Hello ${session.dialogData.name}`); // C
session.beginDialog('/askAge', session.dialogData.name); // D
},
(session) => {
session.endDialog('Goodbye !');
}
]);
bot.dialog('/askAge', [
(session, args, next) => {
session.dialogData.name = args || {};
builder.Prompts.text(session, `${session.dialogData.name} how old are you ?`);
},
(session, results) => {
session.send(`${session.dialogData.name} you're ${results.response}`);
session.endDialog();
}
]);

Lancez votre bot pour le tester, avec la commande :

1
ts-node app.ts

Notre bot n’est certes pas au top de l’intelligence, mais nous avons codé nos premières interactions avec l’utilisateur :). Bon analysons un peu tout ça.

Comme vous pouvez le constater, dailog prend en premier argument une route qui permet de l’identifier (‘/‘ et ‘/askAge’). ‘/‘ est la route par défaut (comme dans un site web). Lorsque notre serveur reçoit un message, il est routé sur cette route par défaut pour être traité. En second argument, dialog prend une ou plusieurs fonctions (dans un tableau) qui représentent les étapes de traitement du message.

(A) -> builder.Prompts permet de collecter des données. Voici un tableau qui résume ce qu’il est possible de demander à l’utilisateur :

Prompt Type Description
Prompts.text Demande une chaîne
Prompts.confirm Demande de confirmer une action
Prompts.number Demande un nombre
Prompts.time Demande un temps ou une date
Prompts.choice Demande de choisir parmi une liste de choix
Prompts.attachment Demande d’uploader une image ou une vidéo

(B) -> La réponse de l’utilisateur est récupérée grâce à l’argument results. L’information est persistée dans session.dialogData. Il y a quatre façons pour persister des données :

  • userData : stocke des données pour un même utilisateur à travers une ou plusieurs conversation.
  • conversationData : stocke des données pour tous les utilisateurs d’une conversation. Désactiver par défaut, activable via persistConversationData.
  • privateConversationData : stocke des données pour un même utilisateur pour une même conversation.
  • dialogData : stocke des données pour un dialog. Utile lorsqu’on veut temporairement persister des données entre les différentes étapes de traitement.

Ces stores peuvent par exemple vous permettre de sauvegarder les préférences de vos utilisateurs.

Ou stocker les données (userData, conversationData, privateConversationData) ? Avec le ConsoleConnector, ou le ChatConnector + l’emulateur, les données sont stockées in-memory au sein du serveur. Une fois le bot déployé, le Bot State REST API, vous permet de stocker ces données de façon sécurisées dans Azure. Mais il existe des points d’extentions vous permettant de changer ce comportement et d’utiliser votre propre base de données.

(C) -> session.send envoie un message à l’utilisateur. Dans notre cas c’est une simple chaîne mais sachez qu’il possible d’envoyer des photos, vidéo, ….

(D) -> beginDialog permet de rediriger le traitement sur un nouveau dialog enfant qui repassera la main au dialog parent une fois celui-ci terminé. Il existe plusieurs méhodes qui permettent de contrôler le flow d’éxecution des dialog : replaceDialog, cancelDialog, …

J’espère que ce premier tuto vous a donné envie d’en savoir plus sur les possibilités offertes par le Bot Framework ! Sachez que ce n’est vraiment qu’un très mince aperçu de ce qu’il est possible de faire ! Dans le prochain post nous utiliserons LUIS afin d’améliorer les possibilités d’interaction avec notre Bot.

Code complet du bot sur mon github : https://github.com/felixbillon/shakedatcode-FirstBot

A bientôt !