Crie um bot de SMS de cotação de ações com Twilio e TypeScript

March 29, 2021
Escrito por
Phil Nash
Twilion
Revisado por

Crie um bot de SMS de cotação de ações com Twilio e TypeScript

Vimos como enviar um SMS com TypeScript e como receber e responder mensagens SMS com TypeScript. Agora vamos criar algo que podemos usar! Se você gosta de investir, pode não ter muitas maneiras de verificar o mercado de ações, então vamos desenvolver um aplicativo para que você possa enviar um símbolo de ações e receber uma cotação delas.

Nesta publicação, criaremos um aplicativo Node.js com TypeScript, usando o Express, o pacote para Node da Twilio e a API Finnhub para responder a mensagens SMS recebidas com cotações de ações.

O que você vai precisar

Para criar o aplicativo nesta publicação, você vai precisar de:

Com tudo preparado, vamos começar.

Um começo rápido

Na publicação do blog sobre receber e responder a mensagens SMS no TypeScript, construímos uma boa base para este aplicativo, a partir da qual trabalharemos. Se você não seguiu essa publicação, pode se atualizar rapidamente. Primeiro, faça o download ou copie o repositório de exemplos do TypeScript do Twilio e mude para o diretório receive-sms:

git clone https://github.com/philnash/twilio-typescript-examples.git
cd twilio-typescript-examples/receive-sms

Instale as dependências:

npm install

Compile o TypeScript:

npm run build

Inicie o servidor:

npm start

Agora, seu aplicativo está sendo executado localmente na porta 3000. Para conectá-lo a um número da Twilio, será necessário criar uma URL disponível publicamente que possa ser encapsulada em sua máquina local. Prefiro fazer isso com ngrok. Abra um segundo terminal e execute o ngrok com o comando:

ngrok http 3000

Assim que o túnel estiver conectado, o ngrok exibirá uma URL com um subdomínio aleatório que agora aponta para seu aplicativo. Ele deve ser parecido com https://RANDOM_STRING.ngrok.io. Abra o console da Twilio, navegue até Active Numbers (Números recebidos) e escolha o número que deseja usar para este aplicativo ou compre um novo. Edite o número e adicione sua URL do ngrok, mais o caminho /messages, como o webhook para quando uma mensagem for recebida.

Uma captura de tela do console da Twilio. Ao editar um número, insira o URL do webhook ngrok na caixa 'A message comes in' (Uma mensagem recebida) marcada.

Se você tiver a CLI da Twilio instalada, poderá fazer isso na linha de comando com o comando:

twilio phone-numbers:update PHONE_NUMBER --sms-url https://RANDOM_STRING.ngrok.io/messages

Com tudo isso configurado, envie uma mensagem SMS para o número da Twilio. Você receberá uma resposta que retorna o que você disse.

Uma visualização de um aplicativo de mensagens para iPhone, com uma resposta do aplicativo TypeScript.

Tudo isso é tratado no arquivo routes/messages.js; as mensagens recebidas são roteadas para o endpoint /messages, o corpo da mensagem é extraído do corpo da solicitação e uma resposta é criada usando mensagens TwiML. A resposta é então retornada como um XML.

router.post("/", (req: MessagingRequest, res: Response<string>) => {
  const message = req.body.Body;

  const response = new MessagingResponse();
  response.message(`Hello from TypeScript! You said "${message}"`);

  res.set("Content-Type", "application/xml");
  res.send(response.toString());
});

Nós fizemos outro trabalho para definir o tipo de solicitação e resposta recebida. Confira o restante da publicação do blog para saber mais sobre isso.

Vamos continuar transformando nossas mensagens SMS recebidas em cotações de ações.

Faça solicitações de API no TypeScript

Neste aplicativo, vamos transformar o corpo da mensagem recebida em uma solicitação para a API Finnhub para uma cotação.

Carregue a configuração em seu aplicativo

Para usar a API, precisaremos carregar a chave API Finnhub no aplicativo. Recomendamos fazer isso por meio de variáveis de ambiente para que você não confirme erroneamente as credenciais de API em seu projeto. Neste aplicativo, gerenciaremos isso com o pacote dotenv.

Instale o dotenv em suas dependências de desenvolvimento:

npm install dotenv -D

Abra package.json e altere o script "start" para:

"start": "nodemon --require dotenv/config ."

Isso exigirá e executará dotenv quando você iniciar o aplicativo.

Encontre sua chave API Finnhub no dashboard.

O dashboard do Finnhub. A chave de API é exibida em uma caixa abaixo do endereço de e-mail que você usou para se inscrever.

Crie um arquivo chamado .env e adicione sua chave API Finnhub a ele da seguinte maneira:

FINNHUB_API_KEY=YOUR_API_KEY

Abra config.ts, adicione uma linha que leia a chave API Finnhub e a exporte novamente:

 
export const port = process.env.PORT || 3000;
export const finnhubApiKey = process.env.FINNHUB_API_KEY;

Com isso, podemos passar a fazer solicitações à API.

Faça solicitações de API no TypeScript

Há muitas formas de fazer solicitações HTTP no Node.js. Usaremos o pacote got para fazer nossas solicitações para a API Finnhub. got está totalmente escrito em TypeScript, portanto nos fornece tipos. Instale got no projeto:

npm install got

Crie um novo diretório chamado src e um arquivo dentro desse diretório chamado quotes.ts. É aqui que escreveremos uma função para fazer solicitações à API Finnhub. Comece importando got e sua chave API Finnhub da configuração.

import got from "got";
import { finnhubApiKey } from "../config";

Agora, vamos definir a função que usaremos para fazer solicitações à API. Ela usará o símbolo que queremos procurar como uma string e, como fará uma solicitação HTTP assíncrona, retornará uma Promise que resolve um objeto do tipo Quote.

export const getQuote = async (symbol: string): Promise<Quote> => {
}

No entanto, há um problema aqui, não temos um tipo Quote. Se analisarmos a documentação do Finnhub, poderemos ver um exemplo de resposta da API:

{
  "c": 261.74,
  "h": 263.31,
  "l": 260.68,
  "o": 261.07,
  "pc": 259.45,
  "t": 1582641000
}

As propriedades se referem aos diferentes preços que a API retorna; o preço atual, o máximo do dia, o mínimo do dia, o preço de abertura e o preço de fechamento anterior. A propriedade final t, é um registro de data e hora para os preços. Usando isso, podemos definir nosso tipo Quote dentro de quotes.ts:

type Quote = {
  o: number;
  h: number;
  l: number;
  c: number;
  pc: number;
  t: number;
};

Vamos fazer a solicitação para a API usando got. Fazemos uma solicitação GET para a URL da API https://finnhub.io/api/v1/quote e passamos o símbolo que estamos procurando como um parâmetro da URL. Podemos passar a chave de API como um parâmetro da URL ou nos cabeçalhos como o cabeçalho X-Finnhub-Token. Com got, podemos definir a expectativa antecipadamente de que a resposta será JSON e o pacote analisará a resposta para nós, para que possamos retornar o corpo da resposta.

export const getQuote = async (symbol: string): Promise<Quote> => {
  const response = await got("https://finnhub.io/api/v1/quote", {
    searchParams: { symbol },
    responseType: "json",
    headers: {
      "X-Finnhub-Token": finnhubApiKey,
    },
  });
  return response.body;
}

Se você escrever o código acima, você verá que o TypeScript não gosta de compilar. response.body é do tipo unknown. Sabemos que é uma resposta JSON que foi analisada em um objeto, mas o TypeScript não conhece a forma desse objeto. Neste caso, podemos afirmar, uma vez que lemos a documentação, que é do tipo Quote que definimos.

Afirmamos que o corpo é do tipo Quote usando a sintaxe as Quote, assim:

export const getQuote = async (symbol: string): Promise<Quote> => {
  const response = await got("https://finnhub.io/api/v1/quote", {
    searchParams: { symbol },
    responseType: "json",
    headers: {
      "X-Finnhub-Token": finnhubApiKey,
    },
  });
  return response.body as Quote;
}

Agora, o TypeScript está satisfeito e podemos usar essa função no resto do nosso aplicativo.

Crie uma resposta SMS

Abra routes/messages.ts e altere a resposta para ecoar o corpo da mensagem e fazer uma chamada para a API Finnhub, retornando os dados sobre os preços das ações.

Na parte superior do arquivo, importe a função getQuote que acabamos de definir:

import { twiml } from "twilio";
import { urlencoded, Router, Response } from "express";
import { MessagingRequest } from "../types/request";
import { getQuote } from "../src/quotes";

Remova o código de definição da rota e comece a criar nossa nova resposta. Primeiro, tornamos o manipulador de rota uma função async. Em seguida, no próprio manipulador, temos o corpo da mensagem recebida e colocamos em caixa alta. Este é o símbolo que procuraremos com a API. Também criamos uma nova resposta de mensagens TwiML.

router.post("/", async (req: MessagingRequest, res: Response<string>) => {
  const symbol = req.body.Body.toUpperCase();
  const response = new MessagingResponse();
  // ...
});

Em seguida, queremos fazer a solicitação à API. Isso pode falhar, então o envolvemos em um bloco try/catch. Quando recebemos a resposta, enviamos os dados de volta como quisermos. Vou comparar o preço atual com o fechamento anterior e mostrar um emoji se for maior ou menor. Definimos a mensagem para ser enviada de volta chamando message no objeto TwiML response.

Se houver um erro, retornaremos um erro ao remetente. Por fim, tendo a solicitação da API dado certo ou não, definimos o tipo de conteúdo da resposta para "application/xml" e enviamos a resposta TwiML como uma string.

router.post("/", async (req: MessagingRequest, res: Response<string>) => {
  const symbol = req.body.Body.toUpperCase();
  const response = new MessagingResponse();
  try {
    const quote = await getQuote(symbol);
    const emojiChart = quote.c > quote.pc ? "📈" : "📉";

    response.message(
      `Current price for ${symbol}: ${formatter.format(quote.c)}
Previous close: ${formatter.format(quote.pc)} ${emojiChart}`
    );
  } catch (error) {
    console.error(error);
    response.message(`Could not find a stock price for ${symbol}.`);
  }
  res.contentType("application/xml");
  res.send(response.toString());
});

A última coisa a fazer é compilar o código e iniciar o servidor.

npm run build
npm start

Se o ngrok ainda estiver em execução, você está pronto para continuar. Se você precisar iniciar o ngrok novamente, provavelmente precisará atualizar a configuração do número no console da Twilio para usar um novo subdomínio do ngrok.

Envie uma mensagem de texto para seu número com um símbolo da ação que você deseja consultar. Se tudo correr bem, você receberá uma resposta com o preço atual.

Sucesso! Enviar um símbolo de ações para o número de telefone retorna o preço atual e uma comparação com o preço de fechamento do dia anterior. Aparentemente, escolhi um dia ruim no mercado de ações para tentar isso. A GameStop ficou abaixo de US$ 60 e a Apple abaixo de US$ 2,45.

Se não funcionar, verifique o log do aplicativo e deve haver uma mensagem de erro informando o que deu errado.

Agora você pode fazer solicitações de API no TypeScript

Nesta publicação, você viu como receber uma mensagem SMS, transformar o corpo em uma solicitação de API e formatar e retornar os resultados como uma resposta. Usamos API Finnhub e as cotações de ações como exemplo aqui e você poderia fazer muito mais com esses dados. Você pode usar mais dados retornados da API de cotação ou escolher um endpoint diferente, como taxas de câmbio estrangeiras ou até criptomoedas. Você também pode escolher qualquer outra API HTTP. Existem alguns bons exemplos de APIs divertidas nesta publicação do blog.

O código para este post, assim como os outros nesta série de publicações sobre a criação de aplicativos Twilio com TypeScript, está disponível no GitHub.

Gostaria de saber o que você está construindo com o Twilio e mantendo type-safe com o TypeScript. Fale comigo em lleao@twilio.com ou envie um tweet para @luisleao.

Este artigo foi traduzido do original "Build a stock quote SMS bot with Twilio and TypeScript". Enquanto melhoramos nossos processos de tradução, adoraríamos receber seus comentários em help@twilio.com - contribuições valiosas podem render brindes da Twilio.