Depuis quelques années, les bots, ces agents logiciels autonomes en charge de réaliser des tâches définies, reviennent sur le devant de la scène. Utilisés depuis longtemps pour l’automatisation de certaines tâches comme l’indexation de pages web par Google ou encore pour assister les clients sur les sites de commerce en ligne ; nous devons leur retour en force, en particulier celui des chatbots – agents conversationnels – aux progrès de l’intelligence artificielle et à leur intégration récente dans les principales plateformes de messagerie instantanée notamment Facebook Messenger et son milliard d’utilisateurs.

Le chatbot, une interface simple et intuitive

A titre d’exemple, nous avons développé dans le cadre du Citizen Clan, un bot nommé Justin Biker, qui vous informe de la disponibilité en temps réel des stations de vélo en libre service, directement depuis Messenger, dans plus de 25 villes à travers le monde dont New York, Londres, Paris, Bruxelles, Chicago, San Francisco, Lille, Lyon, Marseille, Séville, Dublin, etc. Alors qu’auparavant vous deviez utiliser une application dédiée ou vous connecter sur un site web ; avec ce bot, il suffit simplement d’entrer en conversation avec Justin Biker sur Messenger et lui envoyer sa position.

Capture d’écran du bot V’Lille Clan

Je vais dans cet article vous guider dans le développement d’un bot (plus simple) pour la ville de Paris et ses vélos en libre service : Vélib.

Outils et environnement de développement

Les messages envoyés par les utilisateurs de notre service sont traités par un serveur web que nous devons mettre en place. Pour cet exemple, nous développerons un serveur Node.js en nous appuyant sur le framework Express. Si tout cela est nouveau pour vous, ne vous inquiétez surtout pas : vous trouverez le détail de l’installation dans mon article sur le développement d’API.

Pour vérifier la bonne installation de Node.js et de son gestionnaire de paquets npm, entrez les commandes suivantes dans votre console :

node -v

Cela devrait retourner le numéro de version de Node.js. De même pour npm.

npm -v

Nous allons justement utiliser npm pour initialiser notre application. Tout d’abord nous allons créer un dossier. Depuis la console, naviguez vers ce dossier nouvellement créé et entrez la commande suivante :

npm init

Renseignez les informations demandées : nom, version et description de l’application, auteur, etc. Pour « start », ce sera le nom du fichier principal de votre application ; dans mon cas, je l’ai nommé « frugalbot.js ».

name: (velibot) velibclan
version: (1.0.0) 1.0.0
description: Un bot Facebook Messenger pour informer les utilisateurs de la disponibilité en temps réel des stations vélib les plus proches
entry point: (index.js) frugalbot.js
test command:
git repository:
keywords:
author: Ali Benfattoum
license: (ISC)

Un fichier package.json est normalement apparu dans votre dossier. Ce fichier sert à gérer les modules dont nous avons besoin. Justement nous devons maintenant installer (toujours dans le même dossier) les modules Express (si ce n’est pas déjà fait), body-parser, et request pour développer notre serveur web :

npm install express body-parser request --save

Si vous retournez lire le fichier package.json, vous verrez que ces modules sont maintenant inscrits avec le numéro de version correspondant.

Voilà pour la mise en place de l’environnement, il nous manque une dernière chose : la création de notre fichier principal (frugalbot.js me concernant).  Ci-dessous un code exemple pour démarrer rapidement :

var express = require('express');  
var bodyParser = require('body-parser');  
var request = require('request');  
var app = express();

app.use(bodyParser.urlencoded({extended: false}));  
app.use(bodyParser.json());  
app.listen((process.env.PORT || 3000));

// Server frontpage
app.get('/', function (req, res) {  
    res.send('Je suis le serveur du bot frugal');
});

// Facebook Webhook
app.get('/webhook', function (req, res) {  
    if (req.query['hub.verify_token'] === 'monbotfrugal_verify_token') {
        res.send(req.query['hub.challenge']);
    } else {
        res.send('Invalid verify token');
    }
});

Si vous avez suivi l’article sur les API, ce code devrait vous être familier, à l’exception du Facebook Webhook sur lequel nous reviendrons par la suite.

Pour tester ce code, depuis la console, entrez la commande node suivie du nom de votre fichier et rendez-vous sur le lien http://localhost:3000. Le message « Je suis le serveur du bot frugal » devrait apparaître.

node frugalbot.js

Une fois cela fait, nous allons maintenant créer une page Facebook (les bots sont toujours liés à des pages) et inscrire notre application depuis le site Facebook Developer.

Inscription du bot chez Facebook

Pour commencer, nous avons besoin de créer une page Facebook. Ensuite, nous devons nous inscrire chez Facebook developers et créer une application. Lors de la création du nouvel ID d’app, dans la liste déroulante des catégories, choisissez Apps pour Messenger.

Création d’une application Facebook

Pour identifier notre serveur, Facebook utilise ce qu’on appelle un webhook. Petite difficulté : pour interagir avec Facebook, notre serveur doit être en https. Nous allons utiliser pour cela Heroku, une solution d’hébergement en ligne.

Configuration Heroku

Je vous invite pour commencer à créer un compte sur Heroku. Une fois le compte créé, nous aurons besoin de Heroku Command Line Interface (CLI) que vous pouvez télécharger en cliquant sur ce lien.

Une fois installé, vous pourrez depuis la console vous connecter à Heroku et créer votre application. Pour vous connecter, entrez la commande suivante depuis la console et renseignez l’adresse email et le mot de passe utilisés lors de la création de votre compte Heroku.

heroku login

Nous aurons besoin d’un dernier outil pour charger notre application sur Heroku, il s’agit de Git que vous pouvez télécharger en cliquant sur ce lien. Si vous êtes sur Windows, je vous conseille d’installer la console cmdr avec git intégré. Pour tester l’installation de git :

git --version

Avant de continuer avec Git, nous allons créer un fichier dans le dossier de notre application que nous allons nommer : .gitignore (là vous n’avez pas le choix).  N’oubliez pas le point, c’est bien .gitignore. Ce fichier va nous permettre d’exclure les fichiers qui ne sont pas utiles à notre application lors de son déploiement. Ci-dessous, le contenu de mon fichier .gitignore :

logs
*.log
pids
node_modules
.npm
*.dat

Un autre fichier nous sera nécessaire pour le fonctionnement de notre application sur Heroku : le fichier Procfile. Ce fichier indique à la plateforme Heroku, la commande à utiliser pour lancer notre application. Créez donc un fichier nommé Procfile (pas le choix non plus) avec un P majuscule. Dans notre cas, nous utilisons la commande node suivi du nom de notre fichier principal (frugalbots.js ici). Sans oublier le web: qui indique que nous utiliserons un routage HTTP.

web: node frugalbot.js

Passons maintenant à la création de notre répertoire Git. Toujours dans le dossier de notre application, depuis la console (ou depuis le shell git), entrez la commande suivante :

git init

Ensuite, ajoutons nos fichiers à notre git (localement pour le moment).

git add .
git commit -m "mon premier commit frugal"

Il s’agit maintenant de créer une application sur Heroku, cela peut se faire directement depuis la console (choisissez un nom qui n’est pas déjà utilisé sinon cela ne fonctionnera pas) :

heroku create monbotfrugal

Enfin pour déployer votre application, tapez :

git push heroku master

En allant sur le lien https:// »nom de votre application ».herokuapp.com/, vous devriez tomber sur notre message « Je suis Frugal ».  Dans l’exemple utilisé ici, le lien est https://monbotfrugal.herokuapp.com/.

Vous pouvez aussi tester l’application directement depuis la console en tapant :

heroku open

Si vous n’avez pas réussi à configurer correctement votre application sur Heroku, vous trouverez sur ce lien, un guide explicatif de la procédure.

Nous avons maintenant une adresse https, nous pouvons finir de configurer notre application sur Facebook.

Configuration de notre bot chez Facebook

Revenons donc au tableau de bord de notre application chez Facebook Developer. Naviguez vers la rubrique Messenger (sous le menu PRODUITS).  Sélectionnez la page créée précédemment afin de générer un token. Copiez ce token dans un fichier, nous en aurons besoin par la suite.

Génération de tokens

Après cela, cliquez sur « Configurer des webhooks », cochez toutes les cases d’abonnement, inscrivez dans le champ « URL de rappel », l’adresse en https fournie par Heroku suivi de « /webhook » pour que le rappel soit fait sur l’URI que nous avons déclaré dans notre code.  Renseignez également le token que nous avons choisi dans le code exemple à savoir « monbotfrugal_verify_token ».

Configuration des webhooks

Pour terminer, toujours dans la rubrique Webhooks, vous devez maintenant abonner votre application à la page que vous avez créée. Si vous rencontrez des soucis, vous pouvez vous référer au guide explicatif.

Développement du bot Vélib Clan

Nous pouvons enfin passer au développement de notre bot. Nous allons devoir prendre en charge les appels POST sur le webhook. Nous allons démarrer avec le code exemple de Facebook. Ajoutez le code suivant à la fin du fichier frugalbot.js.

Pour comprendre en détail ce code, je vous invite à vous référer au guide explicatif de Facebook.

app.post('/webhook', function (req, res) {
  var data = req.body;

  // Make sure this is a page subscription
  if (data.object === 'page') {

    // Iterate over each entry - there may be multiple if batched
    data.entry.forEach(function(entry) {
      var pageID = entry.id;
      var timeOfEvent = entry.time;

      // Iterate over each messaging event
      entry.messaging.forEach(function(event) {
        if (event.message) {
          receivedMessage(event);
        } else {
          console.log("Webhook received unknown event: ", event);
        }
      });
    });

    // Assume all went well.
    //
    // You must send back a 200, within 20 seconds, to let us know
    // you've successfully received the callback. Otherwise, the request
    // will time out and we will keep trying to resend.
    res.sendStatus(200);
  }
});
  
function receivedMessage(event) {
  var senderID = event.sender.id;
  var recipientID = event.recipient.id;
  var timeOfMessage = event.timestamp;
  var message = event.message;

  console.log("Received message for user %d and page %d at %d with message:", 
    senderID, recipientID, timeOfMessage);
  console.log(JSON.stringify(message));

  var messageId = message.mid;

  var messageText = message.text;
  var messageAttachments = message.attachments;

  if (messageText) {

    // If we receive a text message, check to see if it matches a keyword
    // and send back the example. Otherwise, just echo the text we received.
    switch (messageText) {
      case 'generic':
        sendGenericMessage(senderID);
        break;

      default:
        sendTextMessage(senderID, messageText);
    }
  } else if (messageAttachments) {
    sendTextMessage(senderID, "Message with attachment received");
  }
}

function sendGenericMessage(recipientId, messageText) {
  // To be expanded in later sections
}

function sendTextMessage(recipientId, messageText) {
  var messageData = {
    recipient: {
      id: recipientId
    },
    message: {
      text: messageText
    }
  };

  callSendAPI(messageData);
}

function callSendAPI(messageData) {
  request({
    uri: 'https://graph.facebook.com/v2.6/me/messages',
    qs: { access_token: "Le Token d'accès à votre page" },
    method: 'POST',
    json: messageData

  }, function (error, response, body) {
    if (!error && response.statusCode == 200) {
      var recipientId = body.recipient_id;
      var messageId = body.message_id;

      console.log("Successfully sent generic message with id %s to recipient %s", 
        messageId, recipientId);
    } else {
      console.error("Unable to send message.");
      console.error(response);
      console.error(error);
    }
  });  
}

N’oubliez surtout pas de renseigner votre token dans la fonction callSendAPI.

A ce stade, le bot ne fait que renvoyer à l’utilisateur le texte reçu. Pour mettre à jour notre application sur la plateforme Heroku et pouvoir tester le bot, il suffit de taper de nouveau les commandes suivantes :

git add .
git commit -m "mon autre commit frugal"
git push heroku master

Mais ce qui nous intéresse ici, c’est de pouvoir récupérer les coordonnées d’un utilisateur qui nous envoie sa position et aller interroger l’API de la mairie de Paris pour connaître la disponibilité des stations vélib les plus proches des coordonnées transmises (exemple, si je me trouve à la tour Eiffel).

Pour ce faire nous allons quelque peu modifier le code exemple de Facebook. La ligne qui nous intéresse est la suivante :

else if (messageAttachments) {
    sendTextMessage(senderID, "Message with attachment received");
  }

La position envoyée par l’utilisateur est un « messageAttachments » de type « location ». Nous allons donc tester cela. Ensuite, nous allons extraire les coordonnées du message envoyé par l’utilisateur et appeler l’API. Pour finir, nous allons envoyer le résultat avec la fonction sendGenericMessage que nous allons compléter.

else if (messageAttachments && message.attachments[0].type ==='location') {
          //Nous récuperons les coordonnées de l'utilisateur
          var lat = event.message.attachments[0].payload.coordinates.lat; 
          var lng = event.message.attachments[0].payload.coordinates.long;
          // Nous fixons la distance maximale, ici 500 mètres. 
          var distance = 500; 
          // Nous contruisons l'URL pour interroger l'API
          var urlApiVelib = "https://opendata.paris.fr/api/records/1.0/search/?dataset=stations-velib-disponibilites-en-temps-reel&facet=banking&facet=bonus&facet=status&facet=contract_name&geofilter.distance="+ lat + "," + lng + "," + distance;
        
          /**
           * API OPEN DATA MAIRIE DE PARIS 
           */

                   request(urlApiVelib, function (error, response, body) {
                      
                    if (!error && response.statusCode == 200) {
                            try{
                             let data = JSON.parse(body); 
                              console.log("Il y a "+ data.nhits +" stations à moins de 500 mètres."); 
                            var elmnts = [];
                            var messagevelib = { attachment: { type: "template", payload: { template_type: "generic"}}};
                            
                            if(data.records.length > 0){
                            
                                for(var i=0; i < data.records.length; i++){

                                      var  nameStation = data.records[i].fields.name.substring(data.records[i].fields.name.indexOf('-') + 1); 
                                        //let nbvelo = data.records[i].fields.nbVelosDispo + data.records[i].fields.nbPlacesDispo;  
                                 elmnts.push({
                                    title: nameStation,
                                    subtitle: "Distance : " + data.records[i].fields.dist + "m" + "\n"+data.records[i].fields.available_bikes + " vélo(s) disponible(s) \n" + data.records[i].fields.available_bike_stands +" place(s) libre(s)\nAdresse :" + data.records[i].fields.address,
                                    image_url: "http://www.frugalprototype.com/wp-content/uploads/2017/01/velibclan.jpg", 
                                    buttons : [{
                                        type:"web_url",
                                         url : "https://maps.google.com/?saddr="+lat+","+lng+"&daddr="+data.records[i].fields.position[0]+","+data.records[i].fields.position[1], 
                                         "title":"M'y rendre",
                                        "webview_height_ratio": "full"
                                            }]
                                          });
                          
                                            }
                                
                                messagevelib.attachment.payload.elements = JSON.stringify(elmnts); 
                                 //console.log(messagevelib); 
                                sendGenericMessage(senderID, messagevelib); 
                                
                            } else { sendTextMessage(senderID,"Désolé, Aucune station vélib à moins de 500 mètres"); }
                           

                                } 
                          catch(e){ console.log(e); }
                        } 
             }); 



        }

N’oubliez pas de compléter la fonction sendGenericMessage comme suit :

function sendGenericMessage(recipientId, message) {
    var messageData = {
    recipient: {
      id: recipientId
    },
      message: message
  };  
    
  callSendAPI(messageData);
}

Après une mise à jour de votre application sur Heroku, votre bot Messenger est normalement fonctionnel !

Capture d’écran de notre bot Messenger

Pour le moment, votre application ne peut être utilisée que par les administrateurs, développeurs et testeurs que vous aurez indiqués dans la rubrique Rôles depuis Facebook Developer. Afin de rendre votre bot accessible à tous, vous devez le soumettre à un examen par Facebook.

Un bot Vélib Clan (un peu plus abouti) est disponible sur Messenger si vous souhaitez utiliser le service au quotidien.

Et si vous n’êtes ni de la métropole lilloise, ni de Paris ; vous pouvez reproduire cela pour votre ville si elle est équipée d’un système de vélos en libre service ou encore pour proposer un autre type de service comme la disponibilité des places de parking par exemple. N’hésitez pas à nous en faire part sur ce blog ou via le Citizen Clan.  Vous êtes bien entendu les bienvenus au sein de l’association pour nous aider à construire des services citoyens innovants et frugaux !

Comme d’habitude, si des erreurs se sont glissées dans l’article, n’hésitez pas à nous en faire part.

Pour être informé des prochains articles, rendez-vous sur Twitter.

Merci, et à très bientôt sur Frugal Prototype