
Compartir:
Sudipto is an undergraduate student at the University of Delhi with a background in Computer Science and Mathematics. He is always excited and curious to know how things work and how they are built. He has experience in building applications with JavaScript, TypeScript, Python and Java with research interests in distributed computing and recommender systems.
Cree un servicio de informes sobre la calidad del aire con Messages API
Tiempo de lectura: 12 minutos
¿Ha pensado alguna vez en ampliar su aplicación actual para que interactúe con múltiples canales de comunicación? ¿Y si pudiéramos utilizar esta idea para llamar la atención sobre problemas como la contaminación atmosférica y el cambio climático?
El Índice Mundial de la Calidad del Aire es un proyecto sin ánimo de lucro iniciado en 2007. Su misión es promover la concienciación sobre la contaminación atmosférica y garantizar el acceso a información sobre la calidad del aire en todo el mundo. Proporcionan API REST para acceder a datos de estaciones meteorológicas y de control de la calidad del aire de todo el mundo. También puedes utilizar otras fuentes de datos para crear un servicio centrado en cuestiones sociales.
En este ejemplo, vamos a construir un servicio, impulsado por Node.js - un JavaScript Runtime y la API de Messages de Vonageque enviará información sobre la calidad actual del aire en una ubicación determinada a través de WhatsApp y Facebook Messenger.
El código fuente del ejemplo que vamos a construir también se puede encontrar en GitHub.
Vonage API Account
To complete this tutorial, you will need a Vonage API account. If you don’t have one already, you can sign up today and start building with free credit. Once you have an account, you can find your API Key and API Secret at the top of the Vonage API Dashboard.
Configurar el entorno de desarrollo
Necesitaremos abrir un ngrok túnel a nuestra aplicación para exponerla a través de Internet con una configuración mínima. Después de instalar ngrokabra un terminal y ejecute ngrok http 3070 para exponer tu puerto local 3070 a Internet. Asegúrate de anular esto usando la variable PORT en .env. Copie la URL HTTPS impresa por ngrok en la consola y anótala.

Ahora es el momento de instalar las dependencias necesarias para la aplicación. Ejecute npm init -y para crear un archivo package.json archivo. Utilizaremos Express.js - un popular framework de aplicaciones web para Node.js y Axios - una librería cliente HTTP para este proyecto, y Dotenv - un módulo para gestionar variables de entorno. Más adelante también aprovecharemos Dedent y Commander.js para implementar algunas características más. Instala estos módulos ejecutando:
Como vamos a hacer cambios en nuestro código fuente de vez en cuando, podemos ahorrarnos unas cuantas pulsaciones instalando Nodemon, que vigila continuamente los cambios y reinicia la aplicación automáticamente. Instálalo como dependencia de desarrollo ejecutando
Para este tutorial, nuestro punto de entrada será un archivo llamado lib/index.js. Añada o actualice el main y las script en package.json para ejecutar la aplicación usando nodemon:
// package.json
{
...
"main": "lib/index.js",
...
"scripts": {
"start": "node .",
"dev": "nodemon .",
},
...
}Copie el contenido de .env.example en el directorio principal a un nuevo archivo llamado .env. Una vez que hayas iniciado sesión en el panel de la API de Vonage, busca tu clave de API y tu secreto de API y actualiza los valores en .env. También hay algunas variables adicionales que se asignarán en las próximas secciones.
Recibir un mensaje entrante utilizando Messages API
Cada vez que Vonage recibe un mensaje entrante en tu número de teléfono virtual o a través de uno de los otros canales, los servidores de Vonage realizan una solicitud HTTP a un punto final de webhook definido con una carga útil JSON. Para este tutorial, establecemos que la ruta /webhook/inbound de nuestra aplicación escuche todas estas solicitudes.
Para asegurarnos de recibir esta solicitud, debemos configurar el entorno aislado que puedes encontrar en el panel de la API de Vonage en "Mensajes y despacho". Configura el webhook de mensajes entrantes (HTTP POST) como <ngrok-https-url>/webhook/inbound y haz clic en "Guardar webhooks".

En la misma página, vincula una Account de prueba desde la que enviar mensajes. Haz clic en los enlaces "Añadir a Sandbox" en los canales de WhatsApp y Messenger. A continuación, escanea el código QR en tu teléfono o haz clic en el enlace indicado. Por lo general, se trata de enviar una frase de contraseña a un número o página habilitados para el sandbox. Una vez que hayas vinculado tu cuenta de prueba y configurado el punto final del webhook, puedes continuar. Guarde el número de teléfono del sandbox mencionado en el panel de control en su libreta de direcciones para facilitar el acceso.
Construiremos una aplicación Express.js que escuche en el puerto 3070 Los requisitos mínimos son aceptar peticiones HTTP POST en esa ruta y enviar un código de estado de 200. En nuestra aplicación Express.js, podemos acceder a este payload a través del objeto req.body . Para echar un vistazo a los datos de la solicitud de carga útil, ejecute la aplicación ejecutando npm run dev.
// lib/index.js
require("dotenv").config();
const express = require("express");
const app = express();
const PORT = process.env.PORT || 3070;
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.post("/webhook/inbound", (req, res) => {
console.log(req.body);
res.status(200).end();
});
app.listen(PORT, () => console.log(`Listening on Port ${PORT}...`));
Intenta enviar un mensaje desde WhatsApp al número del sandbox y observa la salida en la ventana de terminal en la que se está ejecutando tu aplicación. Envía otro mensaje desde la app Messenger a la página del sandbox y observa la salida de nuevo.

Las salidas mostradas en el ejemplo anterior muestran que los diferentes canales se pueden distinguir validando req.body.from.type. Dependiendo del canal, también observamos que el mensaje entrante puede proceder de un número de teléfono o de una página/identificación de cuenta. Se puede acceder al mensaje enviado a través del objeto req.body.message en el cuerpo de la solicitud.
Establezca el valor de VONAGE_NUMBER al número de teléfono recibido en req.body.to.number y VONAGE_PAGE_ID al ID de página como en req.body.to.id a las variables respectivas en .env ya que estamos utilizando el sandbox. En la práctica, esto se sustituiría por un número de cuenta de WhatsApp Business y un ID de página de Facebook vinculados a una aplicación de Vonage.
Enviar un mensaje utilizando Messages API
Al usar la API Messages API de Vonage, enviar un mensaje a un canal implica enviar una solicitud HTTP POST con un objeto de mensaje al punto final de la API. Cuando se utiliza el sandbox, el punto final es: https://messages-sandbox.nexmo.com/v0.1/messages.
El sitio Referencia de la API de Messagesmuestra que la solicitud debe contener una cabecera Authorization con el valor Basic base64(apiKey):base64(apiToken) o Bearer jwtToken y un objeto de mensaje en el cuerpo de la solicitud. Para utilizarlo, actualice su archivo /lib/utils.js con el ejemplo siguiente:
// lib/utils.js
require("dotenv").config();
const Axios = require("axios");
const sendMessage = async (message, body) => {
await Axios.post(
"https://messages-sandbox.nexmo.com/v0.1/messages",
{
from: {
type: body.from.type,
number: process.env.VONAGE_NUMBER
},
to: {
type: body.from.type,
number: body.from.number
},
message: {
content: {
type: "text",
text: message
}
}
},
{
auth: {
username: process.env.VONAGE_API_KEY,
password: process.env.VONAGE_API_SECRET
}
}
);
};
module.exports = {
sendMessage,
};
La función de ayuda sendMessage tomará el cuerpo del mensaje para enviarlo al número de WhatsApp definido. El objeto de mensaje puede construirse dinámicamente para soportar múltiples canales; puede implementar esto en otra función de utilidad.
Actualice su /lib/index.js dentro de la función webhook, llame a la función sendMessage con el mensaje que desea enviar, como se muestra a continuación:
// lib/index.js
...
const { sendMessage } = require("./utils");
app.post("/webhook/inbound", (req, res) => {
sendMessage("Thanks for sending a message!", req.body);
res.status(200).end();
});
...
Hemos creado un esqueleto para un servicio de conversación que utilizará Messages API para enviar y recibir mensajes a través de WhatsApp y Messenger. ¡Prueba enviar un mensaje al número del Sandbox de Vonage en WhatsApp!
Obtenga datos de las API del Índice Mundial de la Calidad del Aire
El Proyecto del Índice Mundial de la Calidad del Aire proporciona API JSON para obtener datos sobre la calidad del aire casi en tiempo real. Para acceder a los datos regístrese para obtener un token de API. Recibiremos un enlace de verificación en la dirección de correo electrónico que proporcionemos en esta página que nos redirigirá a una página en la que se mostrará el token de API. Establezca el valor de AQICN_TOKEN en .env al token que se muestra en esa página.
Busque una estación de control de la calidad del aire coincidente para una ciudad determinada con la API de búsqueda WAQI. La solicitud HTTP GET a https://api.waqi.info/search/ tiene dos parámetros de consulta obligatorios keyword que se utiliza como término de búsqueda para encontrar el nombre de una estación o ciudad y token que hace referencia al token de la API WAQI.
Haciendo la petición desde Postman o Insomnia - ambas populares aplicaciones GUI para depurar peticiones API HTTP, podemos ver que la respuesta para la palabra clave london contiene metadatos limitados de la estación para cada resultado de búsqueda.
// GET https://api.waqi.info/search/?token={{AQICN_TOKEN}}&keyword=london
{
"status": "ok",
"data": [
{
"uid": 5724,
"aqi": "36",
"time": {
"tz": "+01:00",
"stime": "2020-11-04 05:00:00",
"vtime": 1604462400
},
"station": {
"name": "London",
"geo": [
51.5073509,
-0.1277583
],
"url": "london"
}
},
...
]
}
Es hora de implementar una función de utilidad para nuestra aplicación para obtener el resultado superior de los resultados de búsqueda y utilizarlo para recuperar los datos esperados.
// lib/utils.js
...
const getStation = async (keyword) => {
const stationData = await Axios.get(
"https://api.waqi.info/search/",
{
params: {
token: process.env.AQICN_TOKEN,
keyword
}
});
if (stationData.data.data.length === 0) {
return { error: "No Stations Found. Try Again." };
}
return stationData.data.data[0].station;
};
...
Para obtener los datos de la estación, haga otra petición HTTP GET, esta vez a la API WAQI City/Station Feed. El punto final de esta API es https://api.waqi.info/feed/<station-url>/ donde station-url corresponde al valor de la clave url en el objeto station devuelto por getStation. El token de la API también es necesario como parámetro de consulta.
La solicitud de la estación devuelta para londonse devuelve un objeto JSON que contiene mediciones en bruto y metadatos detallados de la estacióncomo se muestra a continuación:
// GET https://api.waqi.info/feed/london/?token={{AQICN_TOKEN}}
{
"status": "ok",
"data": {
"aqi": 36,
"idx": 5724,
"attributions": [
{
"url": "http://uk-air.defra.gov.uk/",
"name": "UK-AIR, air quality information resource - Defra, UK",
"logo": "UK-Department-for-environment-food-and-rural-affairs.png"
},
{
"url": "https://londonair.org.uk/",
"name": "London Air Quality Network - Environmental Research Group, King's College London",
"logo": "UK-London-Kings-College.png"
},
{
"url": "https://waqi.info/",
"name": "World Air Quality Index Project"
}
],
"city": {
"geo": [51.5073509, -0.1277583],
"name": "London",
"url": "https://aqicn.org/city/london"
},
"dominentpol": "pm25",
"iaqi": {
"co": { "v": 7.4 },
"h": { "v": 92 },
"no2": { "v": 23.3 },
"o3": { "v": 2.9 },
"p": { "v": 1029.4 },
"pm10": { "v": 16 },
"pm25": { "v": 36 },
"so2": { "v": 3.4 },
"t": { "v": 3.8 },
"w": { "v": 3.7 }
},
"time": {
"s": "2020-11-04 05:00:00",
"tz": "+00:00",
"v": 1604466000,
"iso": "2020-11-04T05:00:00Z"
},
"forecast": {},
"debug": { "sync": "2020-11-04T14:41:04+09:00" }
}
}Implementar otra función de utilidad para hacer esta solicitud. Esta función toma el objeto station como parámetro, que se recupera de getStationy solicita a la API los datos de la estación. Actualice lib/utils.js añadiendo la siguiente getStationData función:
// lib/utils.js
...
const getStationData = async (station) => {
const aqiData = await Axios.get(
`https://api.waqi.info/feed/${station.url}/`,
{
params: {
token: process.env.AQICN_TOKEN
}
}
);
if (aqiData.data.data.status === "error") {
return { error: "Could not get data. Try Again." };
}
return aqiData.data.data;
};
...
Ahora podemos utilizar nuestras funciones de utilidad para consultar la API WAQI al recibir un mensaje en un canal admitido por la API Messages API de Vonage y enviar una respuesta significativa luego de procesar estos datos.
Responda con información pertinente
Los datos que obtenemos de las API de WAQI deben procesarse y hacerse "legibles". Podemos utilizar dos plantillas diferentes para comunicar los datos: una para un informe breve que contenga el índice de calidad del aire y las implicaciones para la salud según la escala de la EPA de 2016 de Estados Unidos, y otra para un informe detallado en el que se mencionen los niveles de contaminantes y la información meteorológica junto con sus respectivas unidades de medida.
| AQI | Air Pollution Level | Health Implications | Cautionary Statement (for PM 2.5) |
|---|---|---|---|
| 0-50 | Good | Air quality is considered satisfactory, and air pollution poses little or no risk. | None. |
| 51-100 | Moderate | Air quality is acceptable; however, for some pollutants there may be a moderate health concern for a very small number of people who are unusually sensitive to air pollution. | Active children and adults, and people with respiratory disease, such as asthma, should limit prolonged outdoor exertion. |
| 101-150 | Unhealthy for Sensitive Groups | Members of sensitive groups may experience health effects. The general public is not likely to be affected. | Active children and adults, and people with respiratory disease, such as asthma, should limit prolonged outdoor exertion. |
| 151-200 | Unhealthy | Everyone may begin to experience health effects; members of sensitive groups may experience more serious health effects. | Active children and adults, and people with respiratory disease, such as asthma, should avoid prolonged outdoor exertion; everyone else, especially children, should limit prolonged outdoor exertion. |
| 201-300 | Very Unhealthy | Health warnings of emergency conditions. The entire population is more likely to be affected. | Active children and adults, and people with respiratory disease, such as asthma, should avoid all outdoor exertion; everyone else, especially children, should limit outdoor exertion. |
| 300+ | Hazardous | Health alert: everyone may experience more serious health effects. | Everyone should avoid all outdoor exertion. |
Fuente: AQI Basics, AirNow
También tenemos que resolver los nombres de los contaminantes y las diferentes métricas meteorológicas a partir de las crípticas abreviaturas. Podemos consultar la referencia de la API WAQI e implementar funciones de ayuda para ello. También podemos definir funciones de ayuda adicionales en las que podemos utilizar métodos de interpolación de cadenas y, opcionalmente formatear mensajes para WhatsApp. La implementación de estas funciones de ayuda se puede encontrar en el código fuente en GitHub.
Dedent es un módulo útil cuando se trabaja con literales de plantilla de JavaScript ES6 multilínea. Es posible que se utilice mucho en el código fuente para mantener los espacios en blanco y mejorar la legibilidad.
Analizar mensajes entrantes con Commander.js
Es útil para analizar los mensajes destinados explícitamente al servicio y tomar diferentes acciones para diferentes comandos. La página Commander.js creada inicialmente para aplicaciones de línea de comandos, puede utilizarse para analizar el mensaje entrante para los comandos y argumentos.
// lib/index.js
...
const { Command } = require("commander");
const trigger = new Command("vonage-aqi");
// override default cli behaviour
trigger.exitOverride();
trigger.addHelpCommand(false);
trigger
.command("aqi <searchterm...>")
.alias("a")
.action(async (searchterm) => {
searchterm = searchterm.join(" ");
// fetch and send the brief report
});
trigger
.command("info <searchterm...>")
.alias("i")
.action(async (searchterm) => {
searchterm = searchterm.join(" ");
// fetch and send the detailed report
});
trigger
.command("act")
.action(async () => {
// send links to resources and information
});
trigger
.command("help")
.alias("h")
.action(async () => {
// send help and usage information
});
...
app.post("/webhook/inbound", async (req, res) => {
try {
// pass the incoming message text to Commander.js
trigger.parse(
req.body.message.content.text
.trim().toLowerCase().split(" "),
{
from: "user"
}
);
} catch (err) {
// send message based on the type of error
} finally {
res.status(200).end();
}
});
...La biblioteca Commander.js admite argumentos obligatorios y opcionales, argumentos variádicos y alias de comandos, y facilita la tarea mucho más que la comprobación manual de los comandos y argumentos.
Garantizar la entrega con el Webhook de estado
Podemos configurar una nueva ruta para escuchar los eventos que ocurrieron después de que enviamos un mensaje en /webhook/status. Asegúrate de agregar esto al ngrok y guárdalo como el Webhook de estado en el Panel de la API de Vonage y haz clic en "Guardar webhooks".
// lib/index.js
...
app.post("/webhook/status", (req, res) => {
console.log(req.body);
res.status(200).end();
});
...
La próxima vez que nuestro servicio reciba un mensaje y le responda, observaremos distintos estados del mensaje enviado. El campo req.body.status contendrá el estado al que pasó el objeto de mensaje cuando se envió la solicitud de webhook. Cuando los servidores de Vonage reciben el mensaje, el objeto se encuentra en el estado submitted estado. Si la entrega fue realmente exitosa, deberíamos recibir un valor de estado que probablemente sería delivered seguido de read.
Si se ha producido un error, el estado puede ser rejected o undeliverable y podríamos, en teoría, manejar este caso por separado. Ten en cuenta que Vonage hace mucho del trabajo pesado al reintentar a intervalos regulares en caso de que la entrega del mensaje haya fallado.
WhatsApp y Messenger Playground
Asegúrese de que la aplicación se está ejecutando y de que el túnel ngrok está guardado en el Sandbox de Mensajes. Coge tu teléfono y envía mensajes a las cuentas del sandbox. ¿No se siente bien cuando la cosa realmente funciona?
WhatsApp

Mensajero

Conclusión
Este proyecto muestra cuán flexibles son las API de Vonage para integrarse con casi cualquier aplicación. Cubrimos la comunicación multicanal con WhatsApp y Messenger y usamos las API WAQI para este ejemplo. Tengo curiosidad por saber qué construirás después de leer esto.
Lecturas complementarias
Puede encontrar el código mostrado en este tutorial y el código fuente completo de la aplicación en funcionamiento en el repositorio GitHub.
Consulta la documentación relevante para Messages API en Desarrollador de API de Vonage y en Referencia de la API de Vonage. Obtén más información sobre cómo las comunicaciones con WhatsApp y Messenger funcionan en Vonage API Developer.
En caso de que no tengas una Account de Vonage, regístrate para obtener una hoy ¡para obtener créditos gratis y usar las API de Vonage en tu próximo proyecto! Contáctanos en Twitter o únete al canal Slack de la comunidad. Cuéntanos qué planeas crear con las API de Vonage.
Compartir:
Sudipto is an undergraduate student at the University of Delhi with a background in Computer Science and Mathematics. He is always excited and curious to know how things work and how they are built. He has experience in building applications with JavaScript, TypeScript, Python and Java with research interests in distributed computing and recommender systems.
