https://a.storyblok.com/f/270183/40075/3593650ff4/traffic-alert.png

Alerta de trânsito por SMS usando o Vonage e a API do Google Maps

Publicado em March 2, 2023

Tempo de leitura: 11 minutos

Visão geral

Neste mundo agitado e frenético, onde tudo está em constante movimento, os usuários valorizam a possibilidade de acessar informações da forma mais prática possível. Isso nem sempre é fácil, especialmente em países como a Índia, onde a cobertura da rede de internet em áreas remotas é mais fraca.

Digamos, por exemplo, que você esteja prestes a sair do escritório para ir para casa após um dia agitado e queira verificar se há trânsito no seu trajeto, mas sua conexão com a internet está lenta. E se você pudesse enviar um SMS com os locais de partida e chegada e obter instantaneamente as informações de trânsito? Isso seria uma mão na roda!

Neste artigo, vou mostrar como você pode criar um alerta de trânsito por SMS usando o Vonage e a API do Google Maps no Node.js.

Pré-requisitos

Antes de começar, certifique-se de ter instalado o seguinte:

  • Node.js. O Node.js é um ambiente de execução de JavaScript de código aberto e multiplataforma.

  • ngrok - É necessária uma conta gratuita. Essa ferramenta permite que os desenvolvedores exponham um servidor de desenvolvimento local à Internet.

  • CLI do Vonage - Depois que o Node.js estiver instalado, você pode usar npm install -g @vonage/cli para instalá-lo. Essa ferramenta permite que você crie e gerencie suas Applications Vonage.

Depois de criar um Account, você poderá encontrar sua chave de API e seu segredo de API na parte superior do Painel da API da Vonage.

Vonage API dashboard

Depois de receber a chave da API e o Segredo da API, você poderá usá-las para a SMS API.

API do Google Maps

Também precisaremos de API do Google Maps. Crie um Account no Google e faça login para acessar créditos gratuitos para usar a API do Google e a chave de API.

GCP account and API Key

Para obter a chave da API do Google Maps:

  • Acesse a Google Maps Platform > página de credenciais.

  • Na página “Credenciais”, clique em Criar credenciais > Chave de API. A Chave de API exibirá sua chave de API.

  • Clique em “Fechar”. A nova chave de API está listada na página “Credenciais”, na seção Chaves de API.

Análise do problema

A implementação deste aplicativo pode ser dividida em três partes:

  1. Receba e leia a mensagem SMS.

  2. Extraia os locais de origem e destino do texto da mensagem SMS e obtenha os detalhes do trajeto por meio da API do Google Maps.

  3. Processe os dados de tráfego e envie o SMS de volta em um formato textual e legível por pessoas.

Configuração

Como nosso aplicativo será desenvolvido em Node.js, certifique-se de ter o Node.js instalado no sistema.

Instale as dependências executando este comando no seu terminal:

npm install express body-parser dotenv

Crie um único arquivo chamado App.js que executará o aplicativo na porta 3000 e cuidar de todo o processamento dentro dele.

Utilizamos o dotenv pacote npm para obter as variáveis de ambiente. Com isso, podemos armazenar segredos com segurança.

const app = require('express')()
const bodyParser = require('body-parser')

if (process.env.NODE_ENV !== "production") {
    require("dotenv").config();
}

app.use(bodyParser.json())
app.use(bodyParser.urlencoded({ extended: true }))

app.listen(process.env.PORT || 3000, "127.0.0.1", () => {
    console.log("Server Running on Port ", process.env.PORT || 3000);
});

Para armazenar e acessar as variáveis de ambiente, crie um .env arquivo no diretório raiz e adicione as propriedades.

PORT = 3000

É possível acessar essas propriedades usando process.env.PORT

Receber SMS

O primeiro passo é receber a mensagem SMS e lê-la. A Vonage oferece um Client SDK do Node.js para utilizar clientes de SMS em nossa aplicação Node.

Você pode acompanhar este tutorial sobre como receber a mensagem em um aplicativo Node.js

npm install @vonage/server-sdk

O próximo passo é configurar a API de entrada e adicioná-la ao console da Vonage. Dessa forma, sempre que um SMS for enviado para o nosso número, a API de entrada será acionada e poderemos ler esse SMS.

Há um tutorial detalhado sobre como você pode configurar a SMS API e ler as mensagens SMS.

A API de entrada é o que disponibilizamos para qualquer aplicação de terceiros ao qual eles enviam os dados.

Qualquer GET ou POST solicitação que nossa API de entrada /webhooks/inbound-sms receber será encaminhado para a handleInboundSms função.

app.route("/webhooks/inbound-sms")
.get(handleInboundSms)
.post(handleInboundSms);

function handleInboundSms(request, response) {
    // reads the SMS body
    const params = Object.assign(request.query, request.body);

    // process the SMS text
    getTrafficDetailsAndSendSMS(params);

    // notify that we have received SMS
    response.status(204).send();
}

Para que essa API de entrada funcione, precisaremos colocar nosso aplicativo em produção. Podemos fazer isso usando ngrok.

Para colocar o aplicativo em produção, primeiro configure o ngrok.

E, em seguida, execute o aplicativo Node.js localmente

node App.js "Server Running on Port 3000"

Assim que o aplicativo local estiver em execução, podemos mapeá-lo e colocá-lo no ar usando o ngrok.

ngrok http 3000

Resultado esperado:

Ngrok CLI output

Assim que o aplicativo estiver no ar, receberemos uma URL pública, por exemplo https://58c8-103-179-3-99.in.ngrok.io

Adicione este URL ao painel da Vonage para que possamos monitorar a API de entrada.

Vonage SMS dashboard

Agora que já conseguimos receber o SMS, vamos processar o texto e obter as informações de trânsito do Google.


Obter informações de trânsito entre duas rotas usando a API do Google Maps

Para extrair as informações de origem e destino da mensagem SMS que recebemos por meio da API de entrada, o texto da mensagem precisa estar em um determinado formato para que possa ser analisado com mais facilidade. Criei um modelo simples que facilita o processamento.

Por exemplo, para receber informações sobre o trânsito entre Mumbai e Pune, vamos criar a seguinte mensagem SMS:

Traffic between Mumbai and Pune

No exemplo acima, Mumbai é a origem, e Pune é o destino.

Na getTrafficDetailsAndSendSMS(params); função, podemos pegar o corpo da mensagem SMS e, em seguida, extrair a fonte.

function getTrafficDetailsAndSendSMS(params) {
    // get the source and destination by parsing the text
    const { origin, destination } = parseText(params);

    // get the traffic details and routes
    const routes = getTrafficDetails({ origin, destination });

    // send back the SMS
    sendSMS(params.msisdn, routes);
}

Analisar texto

Para descobrir as rotas, teremos que extrair a origem e o destino da mensagem SMS que recebemos.

O parseText(params) método extrai o remetente e o destinatário do texto da mensagem SMS e retorna esses dados.

function parseText({ text }) {
    let sampleText = "Traffic between mumbai and pune";
    if (text) {
   	 sampleText = text;
    }

    sampleText = sampleText.trim();
    const characters = sampleText.split(" ");
    const origin = characters[2];
    const destination = characters[4];

    return { origin, destination };
}

Ver detalhes do tráfego

Agora, isso source e destination pode ser passado para a API Google Distance Matrix , que obtém os detalhes do trânsito.

Para fazer uma chamada de API a partir do nosso aplicativo, precisaríamos axios, então vamos adicionar essa dependência.

npm install axios

Para obter as informações de trânsito, precisamos passar o departure_time no parâmetro de consulta. Estamos passando now como seu valor para obter os detalhes atuais do trânsito.

Além disso, definimos o mode como driving. Isso pode ser configurável e pode ser aceito por SMS, juntamente com as rotas, para proporcionar uma melhor experiência ao usuário.

async function getTrafficDetails({ origins, destinations }) {
    try {
   	 const YOUR_API_KEY = process.env.GOOGLE_MAP_API_KEY;
   	 const mode = "driving";
   	 const departure_time = "now";

   	 var  config = {
   		 method:  "get",
   		 url:  `https://maps.googleapis.com/maps/api/directions/json?origins=${origins}&destinations=${destinations}&mode=${mode}&departure_time=${departure_time}&language=en-US&key=${YOUR_API_KEY}`,
   	 };

   	 let response = await axios(config);
   	 response = await response.data;

   	 const  routes = getRoutes(response);
   	 return  routes.join(" \n\n ");
    } catch (e) {
   	 console.error("There was some error while getting routes details", e);
    }
}

A seguir, está o exemplo de resposta da API do Google Maps.

const response = {
  	routes: [
    	{
      	bounds: {
        	northeast: { lat: 41.8781139, lng: -87.6297872 },
        	southwest: { lat: 34.0523525, lng: -118.2435717 },
      	},
      	copyrights: "Map data ©2022 Google, INEGI",
      	legs: [
        	{
          	distance: { text: "579 km", value: 932311 },
          	duration: { text: "8 hours 48 mins", value: 31653 },
          	duration_in_traffic: { text: "8 hours 55 mins", value: 932311 },
          	end_address: "Panvel",
          	end_location: { lat: 37.0842449, lng: -94.513284 },
          	start_address: "Mumbai",
          	start_location: { lat: 41.8781139, lng: -87.6297872 },
          	steps: [],
          	traffic_speed_entry: [],
          	via_waypoint: [],
        	},
        	{
          	distance: { text: "217 km", value: 349512 },
          	duration: { text: "3 hours 17 mins", value: 11799 },
          	duration_in_traffic: { text: "3 hours 40 mins", value: 932311 },
          	end_address: "Alephata",
          	end_location: { lat: 35.4675612, lng: -97.5164077 },
          	start_address: "Panvel",
          	start_location: { lat: 37.0842449, lng: -94.513284 },
          	steps: [],
          	traffic_speed_entry: [],
          	via_waypoint: [],
        	},
        	{
          	distance: { text: "1,328 km", value: 2137682 },
          	duration: { text: "19 hours 28 mins", value: 70097 },
          	duration_in_traffic: { text: "20 hours 22 mins", value: 932311 },
          	end_address: "Pune",
          	end_location: { lat: 34.0523525, lng: -118.2435717 },
          	start_address: "Alephata",
          	start_location: { lat: 35.4675612, lng: -97.5164077 },
          	steps: [],
          	traffic_speed_entry: [],
          	via_waypoint: [],
        	},
      	],
      	summary: "I-55 S and I-44",
      	warnings: [],
      	waypoint_order: [0, 1],
    	},
  	],
  	status: "OK",
	};

Verifique a estrutura completa da resposta

Processamos a resposta que recebemos da API do Google Maps e criamos, a partir dela, uma sequência de caracteres legível que mostra as diferentes rotas que podemos seguir, bem como o tempo e a distância estimados para chegar ao destino.

Para formar a sequência, consideramos todos os trajetos das rotas indicadas, suas distâncias e o tempo necessário para chegar ao destino, tanto em condições normais quanto com trânsito.

Vamos converter todas as rotas em uma sequência de caracteres legível para o usuário e, em seguida, retorná-las juntas.

Para processar a resposta, utilizaremos funções auxiliares.

const  getRoutes = ({ routes }) => {

// calculate the overall distance of all the routes

return  routes.reduce((a, b, l) => {
const { legs } = b;

 

let  via = "";
let  normalTime = 0;
let  timeInTraffic = 0;

 

// for each route, calculate the distance path wise, from one to another

const  distance = legs.reduce((x, y, i) => {

const { distance, duration, duration_in_traffic, steps, start_address, end_address } = y;

normalTime += getTimeInMinutes(duration.text);

timeInTraffic += getTimeInMinutes(duration_in_traffic.text);

 

if (i !== legs.length - 1) {

via = via ? via + " -> " + end_address : end_address;

}

const  string = `From ${start_address} to ${end_address} it takes ${duration.text} normally and ${duration_in_traffic.text} in traffic to cover a distance of ${distance.text}`;

x.push(string);
return  x;
}, []);

// for the final string
const  finalString = `Route${l + 1} via ${via} will take ${processTime(
normalTime
)} normally and ${processTime(
timeInTraffic
)} in traffic. Path via breakdown is: ${distance.join(" AND ")}`;

// push the string
a.push(finalString);
return  a;
}, []);
};

// helper function extract to hours and minutes from text
// and return time in minutes

const  getTimeInMinutes = (timeText) => {

timeText = timeText.split(" ");

let  hrs = timeText[0];

let  mins = timeText[2];

return  parseInt(hrs) * 60 + parseInt(mins);

};

 

// helper function to get hours and mins from the time

const  processTime = (time) => {

const  hrs = Math.floor(time / 60);

const  mins = time % 60;

return  `${hrs} hours ${mins} mins`;

};

Esse método nos retornará a matriz de rotas com a distância pelo trajeto, tanto em condições normais quanto em caso de trânsito, e poderemos exibi-las em formato de texto.

Envie de volta o SMS com os detalhes do tráfego

Na getTrafficDetails() função, estamos processando uma resposta da API de trânsito e usando essa resposta para gerar um texto que contém os detalhes do trânsito. O texto gerado fica assim:

"Route1 via Panvel -> Alephata will take 31 hours 33 mins normally and 32 hours 57 mins in traffic. Path via breakdown is:  From Mumbai to Panvel it takes 8 hours 48 mins normally and 8 hours 55 mins in traffic to cover a distance of 579 km AND From Panvel to Alephata it takes 3 hours 17 mins normally and 3 hours 40 mins in traffic to cover a distance of 217 km AND From Alephata to Pune it takes 19 hours 28 mins normally and 20 hours 22 mins in traffic to cover a distance of 1,328 km"

"Route2 via Eastern Express Highway -> Lonavala will take 11 hours 33 mins normally and 12 hours 57 mins in traffic. Path via breakdown is:  From Mumbai to Eastern Express Highway it takes 7 hours 48 mins normally and 7 hours 55 mins in traffic to cover a distance of 579 km AND From Eastern Express Highway to Lonavala it takes 2 hours 17 mins normally and 2 hours 40 mins in traffic to cover a distance of 200 km AND From Lonavala to Pune it takes 1 hours 28 mins normally and 2 hours 22 mins in traffic to cover a distance of 1,28 km"

Este texto gerado contém o tempo que leva para chegar ao destino a partir do local de origem, tanto em horário normal quanto em horário de pico. Podemos enviar essas informações de volta para o mesmo número de telefone do qual recebemos a solicitação.

Podemos utilizar a SMS API da Vonage para enviar a informação de volta. Ao recebermos a mensagem de texto, também recebemos o número de celular do remetente, para o qual podemos enviar a resposta.

async function sendSMS(msisdn, routes) {
    try {
   	 const data = qs.stringify({
   		 from: "Vonage APIs",
   		 text: routes,
   		 to: msisdn,
   		 api_key: process.env.VONAGE_API_KEY,
   		 api_secret: process.env.VONAGE_API_SECRET,
   	 });

   	 const config = {
   		 method: "post",
   		 url: "https://rest.nexmo.com/sms/json",
   		 headers: {
   		 "Content-Type": "application/x-www-form-urlencoded",
   		 },
   		 data: data,
   	 };

   	 let response = await axios(config);
   	 response = await response.data;
    } catch (e) {
   	 console.error("There was some error while sending sms", e);
    }
}

Você pode encontrar o código-fonte deste aplicativo aqui

Conclusão

Agora que criamos o alerta instantâneo de trânsito por meio de notificação por SMS, você pode consultar este artigo para criar um sistema de alerta diferente por SMS ou pelo WhatsApp usando Messages API da Vonage.

A participação da comunidade é sempre bem-vinda. Junte-se à Vonage no GitHub para ver exemplos de código e a Slack da Comunidade para tirar dúvidas ou enviar comentários. Envie um tweet e conte a eles sobre algo legal que você criou usando as APIs da Vonage.

Você também pode entrar em contato comigo pelo meu blog learnersbucket.com , onde escrevo artigos sobre desenvolvimento web.

Compartilhar:

https://a.storyblok.com/f/270183/400x400/2f974eecd7/pranav_400.png
Pranav ShindeAutor convidado

Pranav é engenheiro de software sênior na Moneycontrol, onde desenvolve aplicativos web de última geração usando React e Next.js. Ele também escreve sobre desempenho e otimização no LearnersBucket.