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

Crie um aplicativo de lembretes de eventos para o WhatsApp

Publicado em August 29, 2024

Tempo de leitura: 7 minutos

Introdução

Neste tutorial, aprenderemos a configurar uma conversa no WhatsApp para lembretes de eventos, que permite receber mensagens diárias com os eventos do dia que você deve lembrar e consultar eventos futuros. Vamos configurar um WhatsApp Sandbox para mensagens recebidas e enviadas e recuperar eventos de uma instância do banco de dados Firebase Firestore.

Você pode encontrar o código deste projeto no GitHub.

Pré-requisitos

Resumo do projeto

Ao final deste projeto, é assim que a pasta do seu projeto deve ficar:

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

Crie um projeto Node e adicione as dependências

No diretório do projeto recém-criado, inicialize um novo projeto Node. Isso criará um novo package.json arquivo. O -y opção preenche automaticamente os valores padrão sem solicitar detalhes.

npm init -y

E instale as seguintes dependências para o nosso projeto. Como alternativa, você pode clonar o projeto do GitHub e instalar as dependências.

npm install axios dotenv express node-cron

Essas dependências são importantes em nosso projeto para criar e processar solicitações HTTP a serviços externos, gerenciar variáveis de ambiente, configurar nosso servidor e programar as mensagens diárias contendo os eventos.

Criar um ambiente de teste do WhatsApp

Vamos usar o WhatsApp Sandbox para este tutorial, mas você também pode integrar sua conta comercial do WhatsApp.

Como usar o Sandbox

As etapas para usar o Sandbox da Messages API para enviar mensagens de teste nas plataformas de mensagens compatíveis são as seguintes:

  1. Configure sua área de testes

  2. Configurar webhooks

Variáveis de ambiente

Crie um .env arquivo para o seu projeto e adicione as variáveis de ambiente encontradas no trecho de código abaixo.

VONAGE_API_KEY=
VONAGE_API_SECRET=
VONAGE_WHATSAPP_NUMBER=
TO_NUMBER=

A postagem no blog do Michael traz uma explicação excelente sobre como usar variáveis de ambiente no Node.js.

O VONAGE_API_KEY e VONAGE_API_SECRET podem ser encontradas no Painel da Vonage.

It shows where the API Key and API Secrets are. Right under the text API key and API Secret]Vonage DashboardO VONAGE_WHATSAPP_NUMBER pode ser encontrado na parte inferior da página do Sandbox da Messages API.

TO_NUMBER é o número a partir do qual você enviará mensagens para o ambiente de teste do WhatsApp.

NOTA: Não use um + ou 00 ao digitar um número de telefone; comece com o código do país, por exemplo, 16600800000.

Enviar uma mensagem pelo WhatsApp

Assim que abrirmos a página do Sandbox da Messages API, um comando cURL mostra como adicionar um endpoint e, em seguida, enviar uma mensagem para um número do WhatsApp na parte inferior da página. Peguei esse código e o adaptei para o 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();

Nessa forma básica, toda vez que eu executo o arquivo que criei, recebo uma mensagem de retorno com o conteúdo da variável text. Esta postagem do blog mostra um exemplo de uso de entrada na sandbox do WhatsApp. Mas, como queremos que nossos eventos nos enviem lembretes, usaremos a função de saída para o exemplo deste tutorial.

Gerar um URL do localtunnel.me usando o código de comando: lt --port 8000, adicione a URL gerada e acrescente /inbound e /event, respectivamente, e clique em “Salvar webhooks”.

Agora vamos mudar; o ambiente de teste do WhatsApp responde assim que o usuário envia uma mensagem. Vamos responder: “Os eventos que temos hoje são: eventos”, assim que o usuário enviar uma mensagem. Mas ainda não estamos selecionando nenhum evento; estamos simplesmente enviando essa sequência de texto. Então, até aqui, você viu como receber uma mensagem do WhatsApp assim que executamos o arquivo e como receber uma resposta de saída. Vamos para a próxima etapa, na qual vamos realmente buscar eventos de um banco de dados.

//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

Conforme mencionado no início deste tutorial, você precisará de uma Account no Firebase. Faça login na sua conta do Firebase, crie um projeto no Firebase, defina a localização e crie uma conta de cobrança.

Firestore

Crie uma nova instância do banco de dados Firestore. Você também precisará criar uma conta de serviço e adicionar o arquivo JSON ao seu projeto Node.js.

Você precisa criar uma coleção. Preencha os campos do Firestore com os eventos. Crie cada nó de evento, que deve conter três campos: date, do tipo timestamp; details; e event_type, ambos do tipo string.

Adicione o código

Este bloco de código mostra como importar o SDK de administração do Firebase, inicializar o aplicativo do Firebase, carregar sua conta de serviço a partir de um arquivo JSON local e acessar seu banco de dados do Firestore.

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

admin.initializeApp({

  credential: admin.credential.cert(

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

  ),

});

const db = admin.firestore();

Crie a função que retorna os eventos de hoje

Essa função inicializa um intervalo de datas para o dia atual, que vai da meia-noite até pouco antes da meia-noite do dia seguinte. Em seguida, ela consulta o Firestore em busca de documentos na coleção “events” cujo campo “date” esteja dentro desse intervalo. Se forem encontrados documentos, é gerada uma string que resume os eventos do dia.

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

    }

}

A mesma getEventsForToday função é responsável por verificar os eventos de hoje, conforme descrito acima. Ela define a data para abranger todos os eventos que ocorrem do início ao fim do dia atual, obtendo efetivamente os eventos programados para hoje.

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);

    }

}

O código usa admin.firestore.Timestamp.fromDate() para converter objetos JavaScript Date em formatos de carimbo de data/hora compatíveis com o Firestore. Isso é essencial para consultar com precisão os campos de data armazenados no Firestore.

Todas as interações com o Firestore incluem blocos try-catch para lidar com erros e registrá-los de forma eficaz, garantindo que quaisquer problemas durante as operações no banco de dados sejam detectados e possam ser diagnosticados.

Agendar mensagens futuras

Nós poderíamos agendar uma função na nuvem e implantá-la com o Cloud Functions. Para este tutorial, porém, usaremos o o node-cron. Configurei-o para enviar mensagens diariamente às 09h50 no fuso horário “Europe/London”, mas você pode ajustá-lo para um horário e fuso horário que façam mais sentido para você.

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",

});

Bônus: Adicione mensagens personalizadas usando a OpenAI

Muitos de nós estamos empolgados com o uso de LLMs (grandes modelos de linguagem) atualmente. Que tal adicionar mensagens personalizadas para serem enviadas nesses eventos sobre os quais você está recebendo lembretes? Você pode fazer chamadas de API para a OpenAI para gerar uma mensagem personalizada para o evento — depois, basta copiá-la, colá-la e usá-la!

Instale a dependência npm da OpenAI pelo seu terminal.

npm install openai

Importe a dependência para o seu arquivo JavaScript.

const OpenAI = require("openai");

Gere uma chave de API e configure o cliente OpenAI.

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

Adicione o OPENAI_API_KEY ao seu arquivo .env.

OPENAI_API_KEY=

Crie uma generateCreativeMessage() função que utilize a API da OpenAI para gerar texto personalizado com base nas informações do evento inserido. A função envia uma solicitação à API, pedindo que ela crie um texto com base em um prompt que inclua os detalhes do evento. A função especifica um número máximo de palavras para a resposta. Se a solicitação for bem-sucedida, ela corta e retorna o texto gerado. Se ocorrer um erro, uma mensagem alternativa ainda exibe os detalhes do 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

  }

}

E não vamos esquecer de atualizar a mensagem programada para adicionar a generateCreativeMessage() função para criar uma mensagem personalizada com base nos eventos. Os detalhes originais do evento e a mensagem recém-criada são enviados para um número de telefone especificado usando a sendMessage() função.

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",

  }

);

Observação: Leve em consideração os custos da API, os limites de uso da API e a privacidade dos dados ao utilizar a API da OpenAI.

Executar o aplicativo

O aplicativo está programado para verificar os eventos de hoje e enviar uma mensagem pelo WhatsApp em um horário específico todos os dias. Você pode consultar os eventos de datas específicas enviando mensagens no formato “Eventos em AAAA-MM-DD?” pelo WhatsApp.

Execute o comando lt para iniciar o túnel.

lt --port 8000

Adicione a URL gerada à sua Sandbox da Messages API e não se esqueça de clicar em “Salvar 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 WebhooksExecute o arquivo JavaScript que você criou na mesma porta em que o túnel está escutando.

node index.js

Você pode atualizar a node.cron função para enviar uma mensagem a qualquer momento, e você também pode enviar uma mensagem para o número do WhatsApp Sandbox para solicitar os eventos de hoje no formato indicado.

Possível extensão — Vonage AI Studio

Conseguimos isso usando a Messages API da Vonage, mas você também poderia ter usado o Vonage AI Studio para projetar a conversa e integrar seu backend. Ele conta até mesmo com um nó Gen AI adicional que permite usar o poder do Large Language Model da OpenAI para dinamizar seu assistente virtual, de modo que ele possa lidar com as consultas dos usuários com conhecimento das nuances específicas do contexto e com a vantagem de ter a internet como fonte de dados. Aqui está um tutorial sobre Criação de um serviço de respostas a perguntas frequentes com a OpenAI e o Vonage AI Studio.

Conclusão

Hoje, você viu como usar o Sandbox da Messages API do Vonage integrado ao Firebase Firestore e ao Cloud Functions for Firestore para receber mensagens de lembrete de eventos. Junte-se a nós na nossa Slack da Comunidade Vonage ou envie-nos uma mensagem no X, anteriormente conhecido como Twitter.

Compartilhar:

https://a.storyblok.com/f/270183/400x400/3f6b0c045f/amanda-cavallaro.png
Amanda CavallaroRepresentante de Desenvolvedores