https://d226lax1qjow5r.cloudfront.net/blog/blogposts/restaurant-is-now-delivering-a-facebook-bot-in-node-js/restaurant-closed_1200x600.png

Restaurant Is Now Delivering : un robot Facebook en Node.js

Publié le October 13, 2021

Temps de lecture : 15 minutes

Introduction

Souvent, lorsque je code, j'ai faim. Et tout le monde sait que les meilleurs développeurs sont paresseux, alors au lieu de faire les courses et de cuisiner et de cuisiner et de nettoyer, j'utilise généralement une application de livraison de nourriture et je commande simplement un repas savoureux. Le problème, c'est que mes restaurants préférés sont trop souvent hors ligne. Parfois, ils sont fermés ; d'autres fois, ils sont trop occupés et n'acceptent plus les commandes en ligne. Je suis donc obligé d'attendre et de me rappeler de vérifier s'ils sont de nouveau en ligne, puis d'ouvrir l'application et de regarder s'ils sont de nouveau en ligne. Et parfois vérifier encore, et encore, et encore. C'est vraiment une grave injustice 😆.

Il doit y avoir un meilleur moyen, plus innovant ! Heureusement, j'ai récemment découvert que mon application de livraison de nourriture préférée, Wolt, dispose d'une API qui me permet de savoir si un restaurant est en ligne. En utilisant l'API Messages de Vonage, j'ai donc créé un bot Facebook Messenger qui m'alertera lorsque mon restaurant préféré sera de nouveau en ligne !

(Cet exemple est construit autour d'un cas d'utilisation de livraison de nourriture, mais le même code peut être réutilisé pour créer un bot Facebook qui alertera les utilisateurs pour tout changement de cas booléen).

Conditions préalables

Cette application nécessite les éléments suivants :

Pseudocode :

Avant de me lancer dans une tâche de codage, j'aime bien réfléchir à la logique. Décomposons les étapes nécessaires à la création de cette application :

  1. Mise en place d'un serveur Express

  2. Se connecter à l'API Sandbox de Vonage Messages

  3. Appeler l'API Wolt pour un restaurant demandé

  4. Vérifier si le restaurant reçu est en ligne

  5. Envoyer un message à l'utilisateur en fonction de l'état du restaurant

  6. Si le restaurant n'est pas en ligne, l'ajouter à une liste de restaurants non connectés.

  7. Vérifier en permanence la liste des restaurants hors ligne pour vérifier s'il y a un changement d'état.

  8. Si un restaurant est mis en ligne, envoyez un message à l'utilisateur et supprimez-le de la liste des restaurants hors ligne.

Mise en place du projet

Créer une application Node

Commençons par créer notre projet :

mkdir isItDelivering

Déplacez-vous ensuite dans le répertoire du projet :

cd isItDelivering

Initialiser le projet de nœuds :

npm init

Installer les paquets Node nécessaires :

npm install -s @vonage/server-sdk@beta express dotenv got lokijs

Enfin, nous créons les fichiers dans lesquels notre code sera stocké :

touch index.js .env

Vous remarquerez que nous utilisons le SDK Vonage Node Server SDK pour accéder à l Messages API. L'API Messages étant actuellement en version bêta, nous avons besoin de la version bêta de notre SDK.

Pour configurer notre serveur, nous allons avoir besoin de quelques informations provenant du tableau de bord du développeur Vonage. Tout d'abord, nous allons créer une nouvelle Application Vonage. Donnez-lui un nom agréable comme isItDelivering. Puis cliquez sur "Generate public and private key".

Generate Public/Private KeyGenerate Public/Private Key

Cela générera automatiquement une clé d'authentification, que nous utiliserons plus tard. Placez la clé générée à la racine de votre projet local.

À ce stade, votre projet doit contenir votre fichier index, vos modules node, votre package.json et votre fichier ENV. Si vous exécutez la commande lsvotre projet devrait ressembler à ceci :

Project Should Include index.js, node_modules, pack.json, private.keyProject Should Include index.js, node_modules, pack.json, private.key

Comme vous pouvez le voir, notre Applications Vonage nous permet d'activer/désactiver diverses capacités par le biais des différentes API de Vonage. Nous voulons activer les capacités de messages. On nous demandera maintenant deux URL correspondant à des webhooks que l'API Messages utilisera pour interagir avec notre application bot.

Se connecter au monde extérieur

Mise en place ngrok

Il y a plusieurs façons de rendre notre serveur de développement local accessible à l'extérieur, mais l'une des façons les plus simples est d'utiliser ngrok. Vous pouvez lire cet article pour une explication plus détaillée du fonctionnement de ngrok.

Pour nos besoins, il nous suffit de le faire fonctionner et de copier l'URL qu'il nous fournit.

Après avoir installé ngrok sur votre machine, nous allons devoir le démarrer. Afin de démarrer ngrok, ouvrez une nouvelle fenêtre de terminal et exécutez ce qui suit depuis la ligne de commande :

$ ngrok http 3000

Vous verrez maintenant une interface de journalisation ngrok dans votre fenêtre de terminal. En haut de l'interface se trouve une ligne qui commence par Forwarding et qui contient deux URL. La première est l'URL de ngrok accessible de l'extérieur, qui se termine par ngrok.io suivie de http://localhost:3000qui est votre serveur de développement local. Maintenant, lorsque vous ou Vonage contactez l'URL ngrok.io l'URL, il la transmettra à votre serveur local.

Maintenant, dans notre tableau de bord Vonage, nous allons ajouter nos URLs ngrok et ajouter les routes URL appropriées. Une fois que vos URLs ressemblent à ceci, vous pouvez cliquer sur le bouton "Generate new application".

Webook URLsWebhook URLs

Se connecter avec Vonage

Connectez votre Account Vonage

Dans le fichier ENV de votre projet, vous devez ajouter 3 variables d'environnement ; API_KEY , API_SECRETet APP_ID.

Vous pouvez trouver votre API_KEY et API_SECRET sur la page d'accueil de votre tableau de bord Vonage :

Dashboard ENV VariablesDashboard ENV Variables

Votre APP_ID se trouve sur la page de configuration de l'application que vous avez générée. Vous trouverez votre application sous Your Applications dans la barre de navigation de gauche. Votre APP_ID se présente comme suit :

APP_ID in DashboardAPP_ID in Dashboard

Une fois que vous avez copié/collé ces éléments dans votre projet, votre fichier ENV devrait ressembler à ceci :

API_KEY="XXXXXXXXX"
API_SECRET="XXXXXXXXX"
APP_ID="XXXXXXXXX"

Premiers pas avec l'Environnement de test de l'API Messages

Ajouter des utilisateurs à votre bac à sable

Nous utiliserons le Vonage Facebook Sandbox. Vous pouvez trouver le bac à sable dans votre tableau de bord Vonage sous l'onglet Messages et répartition sur le côté gauche, ou cliquez ici. Une fois que vous avez cliqué sur Add to Sandbox pour l'onglet Facebook Messenger, votre écran devrait ressembler à ceci :

Set up your sandboxSet up your sandbox

L'Environnement de test de l'API Messages permet de tester rapidement des applications sans avoir à attendre l'approbation du Business Account. L'Environnement de test utilise une approche de liste blanche pour autoriser les utilisateurs à tester. Vous pouvez inviter d'autres utilisateurs à la liste blanche par l'intermédiaire de l'option Send invite email button ou en leur envoyant le lien hypertexte click this link. Le lien ouvrira une session Facebook Messenger. L'utilisateur devra alors envoyer la phrase d'authentification pour être ajouté à la liste blanche. Tous les détails sont disponibles ici.

Connecter votre application à votre bac à sable

Nous allons maintenant devoir indiquer à notre Sandbox d'écouter les requêtes de notre Application et de les délivrer à Facebook Messenger. Cela se fait par le biais de nos URLs ngrok. Nous devons ajouter les mêmes URLs ngrok que précédemment, comme ceci :

Messages API Sandbox ngrok URLSMessages API Sandbox ngrok URLS

Une fois que nous avons appuyé sur le bouton Save webhooks nous avons terminé notre installation et nous pouvons commencer à coder !

Tout le code suivant sera placé dans notre fichier index.js fichier.

Mise en place d'un serveur Express

Construire un serveur de type "Boilerplate" avec des dépendances

Tout d'abord, configurons un serveur Express standard dans notre fichier index.js qui importera les bibliothèques nécessaires et s'exécutera simplement sur le port 3000 :

// access our environment variables
require('dotenv').config();
// access the Vonage SDK so we can use the Voange object and API
const Vonage = require('@vonage/server-sdk');
// access Got library which allows us to make HTTP request to WOLT API
const got = require('got');

// boilerplate Express setup
const express = require('express');
const app = express();
app.use(express.json());
app.use(express.urlencoded({ extended: false }));

app.listen(3000);

Envoi d'un message de base sur Facebook

Envoi d'un message Facebook à partir de notre Applications

Nous devons initialiser une instance de Vonage, en lui passant nos variables ENV, puis lui dire d'utiliser la Sandbox de Vonage comme hôte pour effectuer les requêtes HTTP. Nous pouvons copier le code suivant dans notre

// initialize a new Vonage instance, with ENV variables/keys
const vonage = new Vonage(
  {
    apiKey: process.env.API_KEY,
    apiSecret: process.env.API_SECRET,
    applicationId: process.env.APP_ID,
    privateKey: './private.key'
  },
  {
    apiHost: 'https://messages-sandbox.nexmo.com/',
  }
 );

Ensuite, nous utilisons cet objet Vonage pour envoyer une requête POST sur notre route /inbound et nous devons fournir deux paramètres minimaux : type et text.

// Basic Sandbox Messaging
app.post('/inbound', (req, res) => {
  vonage.channel.send(
    req.body.from,
    req.body.to,
    {
      content: {
        type: 'text',
        text: 'You must be hungry! 🍕'
      },
    },
    (err, data) => {
      if(err){
        console.log(err);
      } else{
          console.log(data.message_uuid);
      }
    }
  );
  res.send('ok');
});

app.post('/status', (req, res) => {
  res.send('ok');
});

Dans une seconde fenêtre de terminal, séparée de notre serveur ngrok, nous devons lancer notre serveur Express :

$ node index.js

Et nous pouvons interagir avec notre robot Facebook !

Basic Facebook SandBox MessageBasic Facebook SandBox Message

Réception d'informations sur les restaurants à partir de l'API Wolt

Effectuer la requête HTTP

En utilisant le https://restaurant-api.wolt.com/v3/venues/slug/{restaurant} nous savons que nous pouvons recevoir toutes sortes d'informations sur le restaurant. Le JSON renvoyé ressemble à ceci :

Wolt Returned JSONWolt Returned JSON

Nous pouvons voir qu'à l'intérieur de l'index zéro, il y a une propriété appelée name de type tableau. A l'index zéro de name se trouve un booléen appelé onlinequi donne l'état actuel de la livraison du restaurant. Nous pouvons donc créer une fonction qui prend le nom d'un restaurant et renvoie l'objet restaurant de Wolt :

// call Wolt API for restaurant info
const getRestaurant = async (reqRestaurant) => {
  const response = await got.get(`https://restaurant-api.wolt.com/v3/venues/slug/${reqRestaurant}`)
      .json();
  return response.results[0];
}

Vérifier si le restaurant reçu est en ligne

En utilisant la propriété online à l'intérieur de l'objet restaurant nous voulons créer une logique qui déterminera le message que nous enverrons à l'utilisateur. Nous pouvons écrire la fonction suivante :

const firstStatusCheck = (restaurant, recipient) => {
  if (restaurant.online) {
    sendFacebookMessage(`Hey, ${restaurant.name[0].value} is now accepting orders!!`, recipient);
  } else {
	  sendFacebookMessage(`Sorry, ${restaurant.name[0].value} is currently offline. I'll ping you when it's open again!`, recipient);
    }
}

La fonction firstStatusCheck a abstrait notre code Vonage pour envoyer le message Facebook dans une fonction appelée sendFacebookMessage. Cette fonction peut désormais envoyer n'importe quel message à partir de notre Account de l'Environnement de test, pour autant que nous lui passions deux paramètres text et recipient.

Nous pouvons utiliser une variable constante SENDER pour transmettre les informations relatives à l'identifiant de l'expéditeur du compte Sandbox. Tout d'abord, nous allons la déclarer.

let SENDER;

Nous l'attribuons ensuite lorsque nous recevons le req de la part du /inbound point final :

app.post('/inbound', async(req, res) => {
  SENDER = req.body.from;

Maintenant, le sendFacebookMessage devrait ressembler à ceci :

const sendFacebookMessage = async (text, recipient) => {
  vonage.channel.send(
    SENDER,
    recipient,
    {
      content: {
        type: 'text',
        text: text,
      },
    },
    (err, data) => {
      if (err) {
        console.log(err);
      } else {
        console.log(data.message_uuid);
      }
     }
   );
 }

Envoyer un message à l'utilisateur en fonction de l'état du restaurant

En combinant nos nouvelles fonctionnalités, nous pouvons mettre à jour notre messagerie simple de l'écrin de sable pour indiquer à l'utilisateur si le restaurant demandé est actuellement en ligne ou non.

// Enhanced Sandbox Messaging
app.post('/inbound', async(req, res) => {
	SENDER = req.body.from;
	const recipient = await req.body.to;
	const requestedRestaurant = await req.body.message.content.text.split('/').pop();
	const restaurant = await getRestaurant(requestedRestaurant);
	firstStatusCheck(restaurant, recipient);
	res.send('ok');
});

Boucle si le restaurant est hors ligne

Maintenant que nous avons créé la logique basée sur le statut du restaurant, nous voulons continuer à vérifier ce statut jusqu'à ce que le restaurant revienne finalement en ligne. Nous devons donc construire les trois dernières étapes de notre pseudocode :

  1. Si le restaurant n'est pas en ligne, l'ajouter à une liste de restaurants non connectés.

  2. Vérifier en permanence la liste des restaurants hors ligne pour vérifier s'il y a un changement d'état.

  3. Si un restaurant est mis en ligne, envoyez un message à l'utilisateur et retirez-le de la liste des restaurants hors ligne.

Création d'une base de données en mémoire de restaurants hors ligne

À ce stade, nous utiliserons notre bibliothèque LokiJS bibliothèque. LokiJS est une base de données en mémoire qui nous permettra de garder une trace de chaque restaurant demandé d'une manière simple et en temps réel. Si vous avez utilisé MongoDB, LokiJS vous semblera très familier.

Tout d'abord, nous devons inclure Loki avec nos autres dépendances :

const loki = require('lokijs');

Nous devons ensuite instancier notre base de données :

let db = new loki("restaurants.db");
let restaurants = db.addCollection("restaurants");

Chaque entrée de restaurant contiendra 4 points de données : le nom, le statut en ligne, le destinataire et la balise. Name est le nom du restaurant. Online status est un booléen indiquant si le restaurant est actuellement en ligne. Recipient est l'information sur l'utilisateur provenant de l'API Messages, qui nous permettra de savoir qui a besoin d'être notifié. Et enfin, slug est la terminaison de l'URL que l'API Wolt utilise pour trouver un restaurant.

Maintenant que nous avons une base de données, nous pouvons commencer à y ajouter nos restaurants hors ligne ! Nous pouvons utiliser la fonction suivante pour ajouter des restaurants à notre liste hors ligne :

 const addRestaurantToDb = (restaurant, recipient) => {
  restaurants.insert({name: restaurant.name[0].value, online: restaurant.online, recipient: recipient, slug: restaurant.slug});
 }

Nous devons maintenant mettre à jour notre firstStatusCheck pour ajouter les restaurants à la liste hors ligne.

// Check initially whether restaurant is online or should it be added to list of offline restaurants to check
const firstStatusCheck = (restaurant, recipient) => {
  if (restaurant.online) {
    sendFacebookMessage(`Hey, ${restaurant.name[0].value} is now accepting orders!!`, recipient);
  } else {
      sendFacebookMessage(`Sorry, ${restaurant.name[0].value} is currently offline. I'll ping you when it's open again!`, recipient);
      addRestaurantToDb(restaurant, recipient);
    }
}

Vérifier en permanence les changements de statut

Maintenant que nous disposons d'une liste de restaurants hors ligne, nous voulons vérifier s'ils reviennent en ligne. Comme nous voulons le faire régulièrement et continuellement, nous utiliserons la fonction intégrée setInterval intégrée :

setInterval(function(){offlineRestaurantLookup(req)} , INTERVAL);

La constante INTERVAL indique à setInterval la fréquence d'exécution de la fonction offlineRestaurantLookup doit être exécutée. Nous la définissons au début du fichier, à côté de SENDER. Par défaut, nous vérifions toutes les 60 secondes :

const INTERVAL = 60000;

L'application offlineRestaurantLookup récupère tous les restaurants de la base de données hors ligne et vérifie pour chacun d'entre eux qu'il est toujours hors ligne.

const offlineRestaurantLookup = async () => {
  let offlineRestaurants = restaurants.data;
  offlineRestaurants.forEach(await checkIsStill0ffline);
}

La fonction checkIsStill0ffline vérifie à son tour si un restaurant est en ligne. Si c'est le cas, elle envoie un message à l'utilisateur concerné et supprime ce restaurant de la liste des restaurants hors ligne.

const checkIsStill0ffline = async (restaurant) => {
  const checkedRestaurant = await getRestaurant(restaurant.slug);
  if (checkedRestaurant.online) {
    sendFacebookMessage(`Hey, ${restaurant.name} is now accepting orders!!`, restaurant.recipient);
    restaurants.chain().find({'name': restaurant.name}).remove();
  }
}

Nous pouvons maintenant ajouter la fonctionnalité setInterval en dessous de notre logique de messagerie de l'écrin de sable :

// Enhanced Sandbox Messaging
app.post('/inbound', async(req, res) => {
  SENDER = req.body.from;
  const recipient = await req.body.to;
  const requestedRestaurant = await req.body.message.content.text.split('/').pop();
  const restaurant = await getRestaurant(requestedRestaurant);
  firstStatusCheck(restaurant, recipient);
  res.send('ok');
});

app.post('/status', (req, res) => {
  res.send('ok');
});

setInterval(function(){offlineRestaurantLookup()} , INTERVAL);

app.listen(3000);

Nous pouvons maintenant exécuter le programme et constater que lorsqu'un restaurant n'est pas en ligne, nous recevons un message nous en informant, et que lorsque les restaurants sont en ligne, nous sommes informés de leur nouvel état. Je suggère d'essayer l'application le matin et de voir les restaurants s'ouvrir soudainement pour le déjeuner. C'est amusant de recevoir les notifications push de Facebook Messenger sur son téléphone !

Enhanced Facebook SandBox MessageEnhanced Facebook SandBox Message

Prochaines étapes

  • Dans ce tutoriel, nous avons utilisé la fonctionnalité Facebook Messenger de l'API Messages, mais nous pourrions étendre cette application pour fournir des capacités omnicanales avec WhatsApp et SMS. Imaginez un cas d'utilisation très urgent (j'ai en tête un magasin de bagels particulier le samedi matin) pour lequel vous souhaiteriez être immédiatement informé d'un changement de statut ; des alertes omnicanales seraient utiles.

  • Nous pourrions étendre ce code pour rendre les alertes plus intelligentes en fonction des horaires de livraison, de la proximité de l'utilisateur par rapport aux restaurants, etc. Nous pourrions également conserver plusieurs emplois.

  • Nous pourrions sortir l'application du bac à sable et la connecter à un Account Facebook professionnel.

Le code final du tutoriel peut être trouvé sur GitHub. J'aimerais beaucoup savoir ce que vous avez construit à l'aide de l'API Messages de Vonage ! Rejoignez la conversation sur notre Communauté Slack et partagez votre histoire !

Partager:

https://a.storyblok.com/f/270183/384x384/e4e7d1452e/benjamin-aronov.png
Benjamin AronovDéfenseur des développeurs

Benjamin Aronov est un défenseur des développeurs chez Vonage. C'est un bâtisseur de communauté qui a fait ses preuves, avec une formation en Ruby on Rails. Benjamin apprécie les plages de Tel Aviv, où il vit. Sa base à Tel Aviv lui permet de rencontrer et d'apprendre de certains des meilleurs fondateurs de startups du monde. En dehors de la technologie, Benjamin aime voyager à travers le monde à la recherche du parfait pain au chocolat.