
Compartir:
Benjamin Aronov es desarrollador de Vonage. Es un constructor de comunidades con experiencia en Ruby on Rails. Benjamin disfruta de las playas de Tel Aviv, a la que llama hogar. Su base en Tel Aviv le permite conocer y aprender de algunos de los mejores fundadores de startups del mundo. Fuera de la tecnología, a Benjamin le encanta viajar por el mundo en busca del perfecto pain au chocolat.
Restaurant Is Now Delivering: un bot de Facebook en Node.js
Tiempo de lectura: 14 minutos
Introducción
A menudo, cuando estoy programando, me entra hambre. Y todo el mundo sabe que los mejores desarrolladores son perezosos, así que en lugar de ir de compras y cocinar y y limpiar, suelo usar una aplicación de comida a domicilio y simplemente pido una sabrosa comida. El problema es que, con demasiada frecuencia, mis restaurantes favoritos están fuera de línea. A veces están cerrados, otras veces están demasiado ocupados y dejan de aceptar pedidos por Internet. Así que me veo obligada a esperar y acordarme de comprobar si vuelven a estar en línea, y luego abrir la aplicación y comprobar si vuelven a estarlo. Y a veces volver a comprobarlo una y otra vez. Es una verdadera injusticia 😆.
Tiene que haber una forma mejor y más innovadora. Por suerte, hace poco descubrí que mi aplicación favorita de reparto de comida, Wolt, tiene una API que me avisa si un restaurante está en línea. Así que, utilizando la API de Messages API de Vonage, he creado un bot para Facebook Messenger que me avisará cuando mi restaurante favorito vuelva a estar online.
(Este ejemplo está construido alrededor de un caso de uso de entrega de comida, pero el mismo código puede ser reutilizado para crear un bot de Facebook que alerte a los usuarios de cualquier cambio de caso booleano).
Requisitos previos
Esta aplicación requiere lo siguiente:
Pseudocódigo:
Antes de empezar a programar, me gusta pensar en la lógica. Vamos a desglosar los pasos necesarios para construir esta aplicación:
Configurar un servidor Express
Conéctate a Vonage Messages API Sandbox
Llamar a la API Wolt para un restaurante solicitado
Compruebe si el restaurante recibido está en línea
Enviar un mensaje al usuario en función del estado del restaurante
Si el restaurante no está en línea, añádalo a una lista de restaurantes sin conexión
Comprueba continuamente la lista de restaurantes sin conexión para ver si hay algún cambio de estado
Si un restaurante se conecta, envía un mensaje al usuario y lo elimina de la lista de restaurantes desconectados
Configurar nuestro proyecto
Crear una aplicación de nodo
Empecemos por crear nuestro proyecto:
A continuación, muévase dentro del directorio del proyecto:
Inicializa el proyecto de nodos:
Instala los paquetes Node necesarios:
Y por último, crear los archivos donde vivirá nuestro código:
Verás que estamos utilizando el SDK de Vonage SDK de servidor de nodos para acceder a la Messages API. Debido a que Messages API se encuentra actualmente en Beta, necesitamos la versión Beta de nuestro SDK.
Para configurar nuestro servidor, vamos a necesitar cierta información del Panel para desarrolladores de Vonage. Primero, crearemos una nueva aplicación de Vonage. Dale un nombre bonito como isItDelivering. Y luego haz clic en "Generar clave pública y privada".
Generate Public/Private Key
Esto generará automáticamente una clave para la autenticación, que utilizaremos más adelante. Mueva la clave generada a la raíz de su proyecto local.
En este punto, tu proyecto debería contener tu archivo index, módulos node, package.json, y tu archivo ENV. Si ejecutas el comando lstu proyecto debería tener este aspecto:
Project Should Include index.js, node_modules, pack.json, private.key
Como puedes ver, nuestra aplicación de Vonage nos permite activar/desactivar varias capacidades a través de las diferentes API de Vonage. Querremos activar las capacidades de Mensajes. Ahora se nos pedirán dos URL correspondientes a webhooks que la Messages API utilizará para interactuar con nuestra aplicación bot.
Conectar con el mundo exterior
Configurar ngrok
Hay varias formas de hacer que nuestro servidor de desarrollo local sea accesible externamente, pero una de las formas más sencillas es con ngrok. Puedes leer este artículo para una explicación más detallada de cómo funciona ngrok.
Para nuestros propósitos, sólo necesitamos ponerlo en marcha y copiar la URL que nos proporciona.
Después de tener ngrok instalado en tu máquina, necesitaremos iniciarlo. Para iniciar ngrok, abra una nueva ventana de terminal y ejecute lo siguiente desde la línea de comandos:
$ ngrok http 3000Ahora verá una interfaz de registro ngrok en su ventana de terminal. Cerca de la parte superior de la interfaz hay una línea que empieza por Forwarding y contiene dos URLs. La primera es la URL de ngrok accesible externamente, que termina con ngrok.io seguido de http://localhost:3000que es tu servidor de desarrollo local. Ahora, cuando tú o Vonage se comuniquen con la ngrok.io URL, la reenviará a tu servidor local.
Ahora en nuestro panel de Vonage agregaremos nuestras URL de ngrok y agregaremos las rutas URL apropiadas. Una vez que tus URL tengan este aspecto, puedes presionar el botón "Generar nueva aplicación".
Webhook URLs
Conéctate con Vonage
Conecta tu cuenta de Vonage
En el archivo ENV de tu proyecto, tendrás que añadir 3 variables de entorno; API_KEY , API_SECRETy APP_ID.
Puede encontrar su API_KEY y API_SECRET en la página de inicio de tu panel de Vonage:
Dashboard ENV Variables
Su APP_ID se encuentra en la página de configuración de la aplicación que ha generado. Encontrará su aplicación en Your Applications en la barra de navegación de la izquierda. Su APP_ID tendrá este aspecto:
APP_ID in Dashboard
Una vez que los hayas copiado y pegado en tu proyecto, tu archivo ENV debería tener este aspecto:
API_KEY="XXXXXXXXX"
API_SECRET="XXXXXXXXX"
APP_ID="XXXXXXXXX" Primeros pasos con Messages API Sandbox
Añadir usuarios a su Sandbox
Utilizaremos el Vonage Facebook Sandbox. Puedes encontrar el Sandbox en tu panel de control de Vonage en la pestaña Mensajes y envío en el lado izquierdo, o haz clic aquí. Una vez que hagas clic Add to Sandbox en la pestaña Facebook Messenger, tu pantalla debería verse así:
Set up your sandbox
El Sandbox de Messages API permite probar aplicaciones rápidamente sin tener que esperar a la aprobación de la Business Account. El Sandbox utiliza un enfoque de lista blanca para permitir usuarios de prueba. Puede invitar a otros usuarios a la lista blanca a través del botón Send invite email button o enviándoles el hipervínculo click this link. El enlace abrirá una sesión de Facebook Messenger. A continuación, el usuario tendrá que enviar la frase de contraseña para ser añadido a la lista blanca. Para más información aquí.
Conexión de la aplicación al entorno de pruebas
Ahora tendremos que decirle a nuestro Sandbox que escuche las peticiones de nuestra Aplicación y las entregue a Facebook Messenger. Esto se hace a través de nuestras URLs ngrok. Necesitaremos añadir las mismas URLs ngrok de antes, así:
Messages API Sandbox ngrok URLS
Una vez que pulsemos el botón Save webhooks habremos completado nuestra configuración y podremos empezar a programar.
Todo el código posterior irá en nuestro archivo index.js archivo.
Configuración de un servidor Express
Construir un servidor Boilerplate con dependencias
En primer lugar, vamos a configurar un servidor Express en nuestro archivo index.js que importará las librerías necesarias y se ejecutará en el puerto 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); Enviar un mensaje básico de Facebook
Enviar un mensaje de Facebook desde nuestra aplicación
Necesitamos inicializar una instancia de Vonage, pasándole nuestras variables ENV, y luego decirle que use Vonage Sandbox como el host utilizado para realizar las solicitudes HTTP. Podemos copiar el siguiente código en nuestro
// 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/',
}
);A continuación, utilizamos ese objeto de Vonage para enviar una solicitud POST en nuestra /inbound y necesitamos proveer los parámetros mínimos: type y 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');
});
Así que ahora en una segunda ventana de terminal, separada de nuestro servidor ngrok, tenemos que encender nuestro servidor Express:
$ node index.js¡Y podemos interactuar con nuestro Bot de Facebook!
Basic Facebook SandBox Message
Recibir información sobre restaurantes de la API Wolt
Realización de la solicitud HTTP
Utilizando el https://restaurant-api.wolt.com/v3/venues/slug/{restaurant} endpoint, sabemos que podemos recibir todo tipo de información sobre el restaurante. El JSON devuelto tiene este aspecto:
Wolt Returned JSON
Podemos ver que dentro del índice cero, hay una propiedad llamada name de tipo array. En el índice cero de name hay un booleano llamado onlineque nos da el estado actual de entrega del restaurante. Así que podemos crear una función que toma el nombre de un restaurante y devuelve el objeto restaurante 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];
}
Compruebe si el restaurante recibido está en línea
Utilizando la propiedad online dentro del objeto restaurant queremos crear una lógica que determine qué mensaje enviamos al usuario. Podemos escribir la siguiente función:
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 función firstStatusCheck ha abstraído nuestro código de Vonage para enviar el mensaje de Facebook en una función llamada sendFacebookMessage. Esta función ahora puede enviar cualquier mensaje desde nuestra cuenta Sandbox siempre y cuando le pasemos dos parámetros text y recipient.
Podemos utilizar una variable constante SENDER para pasar la información del id del remitente de la cuenta Sandbox. Primero, la declararemos.
let SENDER;Y luego lo asignamos cuando recibimos el req del /inbound punto final:
app.post('/inbound', async(req, res) => {
SENDER = req.body.from;
Ahora el sendFacebookMessage debería tener este aspecto:
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);
}
}
);
}
Enviar un mensaje al usuario en función del estado del restaurante
Combinando nuestra nueva funcionalidad, podemos actualizar nuestra sencilla mensajería Sandbox para indicar al usuario si el restaurante solicitado está actualmente en línea o no.
// 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');
});
Bucle si el restaurante está fuera de servicio
Ahora que hemos creado la lógica basada en el estado del restaurante, queremos seguir comprobando ese estado hasta que el restaurante finalmente vuelva a estar en línea. Así que tenemos que construir los tres últimos pasos de nuestro pseudocódigo:
Si el restaurante no está en línea, añádalo a una lista de restaurantes sin conexión
Comprueba continuamente la lista de restaurantes sin conexión para ver si hay algún cambio de estado
Si un restaurante se conecta, envía un mensaje al usuario y lo elimina de la lista de restaurantes desconectados.
Creación de una base de datos en memoria de restaurantes fuera de línea
En este punto utilizaremos nuestra LokiJS biblioteca. LokiJS es una base de datos en memoria que nos permitirá hacer un seguimiento de cada restaurante solicitado de una forma sencilla y en tiempo de ejecución. Si has usado MongoDB, LokiJS te resultará muy familiar.
En primer lugar, tenemos que incluir Loki con nuestras otras dependencias:
const loki = require('lokijs');A continuación, tendremos que instanciar nuestra base de datos:
let db = new loki("restaurants.db");
let restaurants = db.addCollection("restaurants");Cada entrada de restaurante contendrá 4 datos: nombre, estado en línea, destinatario y slug. Name es el nombre del restaurante. Online status es un booleano que indica si el restaurante está actualmente en línea. Recipient es la información de usuario de la Messages API, que nos permitirá saber a quién hay que notificar. Y por último, slug es la terminación de la URL que la API Wolt utiliza para encontrar un restaurante.
Ahora que tenemos una base de datos, ¡podemos empezar a añadirle nuestros restaurantes offline! Podemos utilizar la siguiente función para añadir restaurantes a nuestra lista offline:
const addRestaurantToDb = (restaurant, recipient) => {
restaurants.insert({name: restaurant.name[0].value, online: restaurant.online, recipient: recipient, slug: restaurant.slug});
}
Ahora tendremos que actualizar nuestro firstStatusCheck para añadir restaurantes a la lista offline.
// 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);
}
}
Compruebe continuamente los cambios de estado
Ahora que tenemos una lista de restaurantes offline, queremos comprobar si vuelven a estar online. Como queremos hacerlo de forma regular y continua, utilizaremos la función integrada setInterval incorporada:
setInterval(function(){offlineRestaurantLookup(req)} , INTERVAL);La constante INTERVAL indica a setInterval la frecuencia con la que debe ejecutar la función offlineRestaurantLookup función. Definimos esto en la parte superior del archivo junto a SENDER. Por defecto, vamos a comprobar cada 60 segundos:
const INTERVAL = 60000;El sitio offlineRestaurantLookup recuperará todos los restaurantes de la base de datos de restaurantes sin conexión y, a continuación, comprobará que cada restaurante sigue sin conexión.
const offlineRestaurantLookup = async () => {
let offlineRestaurants = restaurants.data;
offlineRestaurants.forEach(await checkIsStill0ffline);
}
La función checkIsStill0ffline comprueba si un restaurante está en línea. Si el restaurante está en línea, enviará un mensaje al usuario correcto y lo eliminará de la lista de restaurantes sin conexión.
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();
}
}
Ahora podemos añadir la funcionalidad setInterval funcionalidad debajo de nuestra lógica de Sandbox Messaging:
// 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);
Y ahora podemos ejecutar el programa, viendo que cuando un restaurante está fuera de línea recibimos un mensaje notificándolo, y cuando los restaurantes entran en línea se nos actualiza el nuevo estado. Sugiero probar la aplicación por la mañana y ver cómo los restaurantes abren de repente para comer. Es divertido recibir las notificaciones push de Facebook Messenger en el teléfono.
Enhanced Facebook SandBox Message
El futuro
En este tutorial, hemos utilizado la funcionalidad de Facebook Messenger de la Messages API, pero podríamos ampliar esta aplicación para proporcionar capacidades omnichannel con WhatsApp y SMS. Imagina un caso de uso muy urgente (tengo en mente una tienda de bagels en particular los sábados por la mañana) en el que quisieras ser notificado inmediatamente sobre un cambio de estado; las alertas omnichannel serían útiles.
Podríamos ampliar este código para que las alertas sean más inteligentes en función de los horarios de entrega, la proximidad del usuario a los restaurantes, etc. También podríamos mantener múltiples trabajos.
Podríamos sacar la aplicación del Sandbox y conectarla a una Account de Facebook de empresa.
El código final del tutorial se puede en GitHub. Me encantaría saber qué has creado con la API de Messages API de Vonage. Únete a la conversación en nuestra Slack de la comunidad ¡y comparte tu historia!
Compartir:
Benjamin Aronov es desarrollador de Vonage. Es un constructor de comunidades con experiencia en Ruby on Rails. Benjamin disfruta de las playas de Tel Aviv, a la que llama hogar. Su base en Tel Aviv le permite conocer y aprender de algunos de los mejores fundadores de startups del mundo. Fuera de la tecnología, a Benjamin le encanta viajar por el mundo en busca del perfecto pain au chocolat.