https://a.storyblok.com/f/270183/1368x665/3f52b1463c/whatsapp_event-reminder.png

Crea una aplicación de WhatsApp para recordar eventos

Publicado el August 29, 2024

Tiempo de lectura: 7 minutos

Introducción

En este tutorial, aprenderemos a configurar una conversación de WhatsApp de recordatorio de eventos que permite recibir mensajes diarios con los eventos de hoy para recordar y consultar eventos futuros. Configuraremos un WhatsApp Sandbox para mensajes entrantes y salientes y recuperaremos eventos de una instancia de base de datos Firebase Firestore.

Puede encontrar el código de este proyecto en GitHub.

Requisitos previos

Esquema del proyecto

Al final de este proyecto, esto es lo que su carpeta de proyecto debe ser similar:

[node_modules]
.env
index.js
package-lock.json
package.json
events-reminder-store-credentials.json

Crear un proyecto Node y añadir las dependencias

En el directorio de su proyecto recién creado, inicialice un nuevo proyecto de nodo. Esto creará un nuevo archivo package.json nuevo. La opción -y rellena automáticamente los valores por defecto sin preguntar por los detalles.

npm init -y

E instalar las siguientes dependencias para nuestro proyecto. Alternativamente, puede clonar el proyecto desde GitHub e instalar las dependencias.

npm install axios dotenv express node-cron

Estas dependencias son importantes en nuestro proyecto para realizar y gestionar peticiones HTTP a servicios externos, gestionar variables de entorno, configurar nuestro servidor y programar los mensajes diarios que contienen los eventos.

Crear un Sandbox de WhatsApp

Utilizaremos el WhatsApp Sandbox para este tutorial, pero también puede integrar la cuenta de WhatsApp de su empresa.

Uso del Sandbox

Los pasos para utilizar el Sandbox de Messages API para enviar mensajes de prueba en plataformas de mensajería compatibles son los siguientes:

  1. Configure su sandbox

  2. Configurar webhooks

Variables de entorno

Cree un archivo .env para su proyecto y añada las variables de entorno que se encuentran en el siguiente fragmento de código.

VONAGE_API_KEY=
VONAGE_API_SECRET=
VONAGE_WHATSAPP_NUMBER=
TO_NUMBER=

La entrada del blog de Michael proporciona una maravillosa explicación del uso de variables de entorno en Node.js.

En VONAGE_API_KEY y VONAGE_API_SECRET se pueden encontrar en el Panel de Vonage.

It shows where the API Key and API Secrets are. Right under the text API key and API Secret]Vonage DashboardEl VONAGE_WHATSAPP_NUMBER se encuentra en la parte inferior de la página Messages API Sandbox.

TO_NUMBER es el número desde el que vas a enviar el mensaje al sandbox de WhatsApp.

NOTA: No utilice + o 00 cuando introduzca un número de teléfono, empiece por el prefijo del país, por ejemplo 16600800000.

Enviar un mensaje de WhatsApp

Una vez que abrimos la Página Sandbox de la API de Messages, un comando cURL muestra cómo añadir un endpoint y, a continuación, enviar un mensaje a un número de WhatsApp en la parte inferior de la página. He tomado ese código y lo he convertido en Node.js.

//index.js

require("dotenv").config();

const user = process.env.VONAGE_API_KEY;

const password = process.env.VONAGE_API_SECRET;

const from_number = process.env.VONAGE_WHATSAPP_NUMBER;

const to_number = process.env.VONAGE_TO_NUMBER;

const data = JSON.stringify({

  from: { type: "whatsapp", number: from_number },

  to: { type: "whatsapp", number: to_number },

  message: {

    content: {

      type: "text",

      text: "The events we have today are: event",

    },

  },

});

const https = require("https");

const options = {

  hostname: "messages-sandbox.nexmo.com",

  port: 443,

  path: "/v0.1/messages",

  method: "POST",

  authorization: {

    username: user,

    password: password,

  },

  headers: {

    Authorization: "Basic " + btoa(`${user}:${password}`),

    "Content-Type": "application/json",

  },

};

const req = https.request(options, (res) => {

  console.log(`statusCode: ${res.statusCode}`);

  res.on("data", (d) => {

    process.stdout.write(d);

  });

});

req.on("error", (e) => {

  console.error(e);

});

req.write(data);

req.end();

De esta forma básica, cada vez que ejecuto el fichero que he creado, me devuelve un mensaje con lo que contiene la variable text. Esta entrada de blog muestra un ejemplo de uso entrante del sandbox de WhatsApp. Pero como queremos que nuestros eventos nos recuerden, usaremos outbound para el ejemplo de este tutorial.

Generar un localtunnel.me utilizando el código de comando: lt --port 8000añada la URL generada y agregue /inbound y /eventy haga clic en "Guardar webhooks".

Ahora cambiamos; el sandbox de WhatsApp responde una vez que el usuario envía un mensaje. Vamos a responder: "Los eventos que tenemos hoy son: eventos", una vez que el usuario envíe un mensaje. Pero aún no está recogiendo ningún evento; simplemente enviamos esa cadena de texto. Así que, hasta aquí, has visto cómo recibir un WhatsApp una vez que ejecutamos el archivo y cómo recibir una respuesta saliente. Vayamos al siguiente paso, donde realmente obtendremos eventos de una base de datos.

//index.js

require("dotenv").config();

const express = require("express");

const https = require("https");

const app = express();

app.use(express.json());

const PORT = 8000;

const from_number = process.env.VONAGE_WHATSAPP_NUMBER; // Your Vonage WhatsApp number

app.post("/inbound", (req, res) => {

  console.log("Inbound message received:", req.body);

  // Assuming the structure of req.body is as expected

  const to_number = req.body.from.number; // Sender's number to which you're replying

  console.log({to_number});

  const data = JSON.stringify({

    from: { type: "whatsapp", number: from_number },

    to: { type: "whatsapp", number: process.env.TO_NUMBER },

    message: {

      content: {

        type: "text",

        text: "The events we have today are: events",

      },

    },

  });

  const options = {

    hostname: "messages-sandbox.nexmo.com",

    port: 443,

    path: "/v0.1/messages",

    method: "POST",

    headers: {

      Authorization:

        "Basic " +

        Buffer.from(

          ${process.env.VONAGE_API_KEY}:${process.env.VONAGE_API_SECRET}

        ).toString("base64"),

      "Content-Type": "application/json",

      "Content-Length": Buffer.byteLength(data),

    },

  };

  const reqOut = https.request(options, (resOut) => {

    console.log(`statusCode: ${resOut.statusCode}`);

    resOut.on("data", (d) => {

      process.stdout.write(d);

    });

  });

  reqOut.on("error", (e) => {

    console.error(e);

  });

  reqOut.write(data);

  reqOut.end();

  res.status(200).send("Inbound message processed");

});

app.listen(PORT, () => {

  console.log(`Server is listening on port ${PORT}`);

});

Firebase

Como se indicó al principio de este tutorial, necesitará una cuenta de Account de Firebase. Inicie sesión en su Account Firebase, cree un proyecto Firebase, establezca la ubicación y crear una Account de facturación.

Tienda de fuegos

Crear una nueva instancia de base de datos Firestore. También necesitará crear una cuenta de servicio y añadir el archivo JSON a tu proyecto node.

Debe crear una colección. Rellenar los campos Firestore con los eventos. Crea cada nodo de evento, que debe contener tres campos: date, de tipo timestamp detailsy event_typeambos de tipo string.

Añada el código

Este bloque de código muestra cómo importar el SDK Firebase Admin, inicializar la aplicación Firebase, cargar su cuenta de servicio desde un archivo JSON local y acceder a su base de datos Firestore.

const admin = require("firebase-admin");

admin.initializeApp({

  credential: admin.credential.cert(

    require("./events-reminder-store-credentials.json")

  ),

});

const db = admin.firestore();

Crear la función que obtiene los eventos de hoy

Esta función inicializa un intervalo de fechas para el día actual, desde medianoche hasta justo antes de medianoche del día siguiente. A continuación, consulta Firestore en busca de documentos de la colección de eventos cuyo campo de fecha se encuentre dentro de este intervalo. Si se encuentran documentos, se construye una cadena que resume los eventos del día.

async function getEventsForToday() {

    const today = new Date();

    today.setHours(0, 0, 0, 0); // Set the time to the start of the day (midnight)

    const tomorrow = new Date(today);

    tomorrow.setDate(today.getDate() + 1); // Set to the start of the next day (midnight next day)

    try {

        // Query Firestore for events within today's date range

        const snapshot = await db.collection('events')

            .where('date', '>=', admin.firestore.Timestamp.fromDate(today))

            .where('date', '<', admin.firestore.Timestamp.fromDate(tomorrow))

            .get();

        if (snapshot.empty) {

            return 'No events found for today.'; // Return message if no events are found

        }

        let eventsText = 'Today\'s events: ';

        snapshot.forEach(doc => {

            const event = doc.data();

            eventsText += ${event.details}; ; // Concatenate each event detail into a string

        });

        return eventsText; // Return the string of today's events

    } catch (error) {

        console.error("Error fetching events:", error);

        return "Failed to fetch events."; // Handle errors in fetching data

    }

}

La misma función getEventsForToday se encarga de comprobar los eventos de hoy tal y como se ha descrito anteriormente. Establece la fecha para cubrir todos los eventos que se produzcan desde el principio hasta el final del día actual, obteniendo así los eventos programados para hoy.

async function handleDateRequest(dateString, requesterNumber) {

    const queryDate = new Date(dateString + 'T00:00:00Z'); // Set the query date start

    const queryDateEnd = new Date(queryDate);

    queryDateEnd.setDate(queryDate.getDate() + 1); // Set the query date end to the next day

    try {

        const snapshot = await db.collection('events')

            .where('date', '>=', admin.firestore.Timestamp.fromDate(queryDate))

            .where('date', '<', admin.firestore.Timestamp.fromDate(queryDateEnd))

            .get();

        if (snapshot.empty) {

            sendMessage('No events found for ' + dateString, requesterNumber); // Inform no events found

            return;

        }

        let eventsText = Events on ${dateString}: ;

        snapshot.forEach(doc => {

            const event = doc.data();

            eventsText += ${event.details}; ; // Aggregate event details

        });

        sendMessage(eventsText, requesterNumber); // Send the aggregated event details

    } catch (error) {

        console.error("Error retrieving events:", error);

    }

}

El código utiliza admin.firestore.Timestamp.fromDate() para convertir objetos JavaScript Date en formatos de fecha y hora compatibles con Firestore. Esto es esencial para consultar con precisión los campos de fecha almacenados en Firestore.

Todas las interacciones de Firestore incluyen bloques try-catch para gestionar y registrar los errores de forma eficaz, lo que garantiza que cualquier problema durante las operaciones de la base de datos se detecte y pueda diagnosticarse.

Programar futuros mensajes

Podríamos programar una función de nube y desplegarla con funciones de nube. Para este tutorial, sin embargo, vamos a utilizar nodo-cron. Lo he configurado para enviar mensajes diariamente a las 09:50 a.m. en la zona horaria "Europa/Londres", pero puedes ajustarlo a una hora y zona horaria que tenga más sentido para ti.

const cron = require("node-cron");

cron.schedule("50 09  *", async () => {

  console.log("Running a job at 09:50 at Europe/London timezone");

  const eventsText = await getEventsForToday();

  sendMessage(eventsText, TO_NUMBER);

}, {

  scheduled: true,

  timezone: "Europe/London",

});

Bonificación: Añade mensajes personalizados con Open AI

Hoy en día, muchos de nosotros estamos entusiasmados con el uso de los LLM (grandes modelos lingüísticos). ¿Qué te parecería añadir mensajes personalizados para los eventos que te están recordando? Puedes hacer llamadas a la API de OpenAI para que genere un mensaje de evento personalizado para ti, ¡y luego puedes copiarlo, pegarlo y utilizarlo!

Instala la dependencia npm de OpenAI desde tu terminal.

npm install openai

Importe la dependencia a su archivo JavaScript.

const OpenAI = require("openai");

Genera una clave API y configura el cliente OpenAI.

const openai = new OpenAI({ apiKey: OPENAI_API_KEY });

Añada el OPENAI_API_KEY a su archivo .env.

OPENAI_API_KEY=

Crea una función generateCreativeMessage() que utilice la API de OpenAI para generar texto personalizado basado en la información del evento. La función envía una solicitud a la API, pidiéndole que cree un texto basado en una solicitud que incluya detalles del evento. La función especifica un número máximo de palabras para la respuesta. Si la solicitud tiene éxito, recorta y devuelve el texto generado. Si se produce un error, un mensaje alternativo se hace eco de los detalles del evento.

async function generateCreativeMessage(eventsText) {

  try {

    const response = await openai.completions.create({

      model: "gpt-3.5-turbo-instruct",

      prompt: Create a friendly and engaging message based on the following events: ${eventsText},

      max_tokens: 150,

    });

    return response.choices[0].text.trim();

  } catch (error) {

    console.error("Failed to generate message with OpenAI:", error);

    return Here's what's happening today: ${eventsText}; // Fallback text

  }

}

Y no olvidemos actualizar el mensaje programado para añadir la función generateCreativeMessage() para crear un mensaje personalizado basado en los eventos. Los detalles originales del evento y el mensaje recién creado se envían a un número de teléfono especificado mediante la función sendMessage() función.

cron.schedule(

  "50 09  *",

  async () => {

    console.log("Running a job at 09:50 at Europe/London timezone");

    const eventsText = await getEventsForToday();

    const creativeText = await generateCreativeMessage(eventsText);

    sendMessage(

      ${eventsText}\n\nSuggested message to send: ${creativeText},

      TO_NUMBER

    );

  },

  {

    scheduled: true,

    timezone: "Europe/London",

  }

);

Nota: Ten en cuenta los costes de la API, los límites de la tarifa de la API y la privacidad de los datos cuando utilices la API de OpenAI.

Ejecutar la aplicación

La aplicación está programada para comprobar los eventos del día y enviar un mensaje por WhatsApp a una hora determinada cada día. Puede consultar los eventos en fechas concretas enviando mensajes con el formato "¿Eventos en AAAA-MM-DD?" a través de WhatsApp.

Ejecute el comando lt para iniciar el túnel.

lt --port 8000

Añada la URL generada a su Mensajes API Sandbox Webhooks, y no olvide hacer clic en Guardar Webhooks.

Inbound field: Inbound HTTP Post. You add the generated url and add a /inbound at the end. Status field: HTTP POST. Inbound field: Inbound HTTP Post. You add the generated url and add a /status at the endMessages API WebhooksEjecute el archivo JavaScript que ha creado en el mismo puerto en el que escucha el túnel.

node index.js

Puede actualizar la función node.cron para enviar un mensaje en cualquier momento, y también puedes enviar un mensaje al número de WhatsApp Sandbox para preguntar por los eventos de hoy en el formato.

Posible extensión - Vonage AI Studio

Hemos logrado esto usando la API de mensajes de Vonage, pero también podrías haber usado Vonage AI Studio para diseñar la conversación e integrar tu backend. Incluso tiene un nodo Gen AI complementario que te permite utilizar la potencia del Modelo de Lenguaje Amplio de OpenAI para dinamizar tu asistente virtual para que gestione las consultas de tus usuarios con el conocimiento de los matices específicos del contexto y la ventaja de tener Internet como fuente de datos. Aquí tienes un tutorial para Crear un servicio de respuesta de preguntas frecuentes con OpenAI y Vonage AI Studio.

Conclusión

Hoy, viste cómo usar el Sandbox de Messages API de Vonage integrado con Firebase Firestore y Cloud Functions para Firestore para recibir mensajes recordatorios de eventos. Únete a nosotros en nuestra Slack de la comunidad de Vonage o envíanos un mensaje en X, antes conocido como Twitter.

Compartir:

https://a.storyblok.com/f/270183/400x400/3f6b0c045f/amanda-cavallaro.png
Amanda CavallaroDefensor del Desarrollador