WhatsApp bot com Google Sheets API no NodeJS

GUIA PASSO A PASSO
Repositório de fontes Bot
Published: February 1, 2021

Neste guia, mostraremos como funciona e como configurar a API Google Sheets. Criamos um bot WhatsApp no NodeJS usando nosso gateway, que será vinculado ao Google Sheets.

Neste exemplo, o bot responderá aos comandos recebidos na forma de mensagens regulares no WhatsApp e responderá a eles ou executará o comando. Não se esqueça de baixar o bot acabado do nosso repositório e usá-lo em seu trabalho!

Qual é a funcionalidade deste bot?

  1. Escrever informações em uma tabela;
  2. Ler informações de uma tabela;
  3. Enviar mensagens por números da tabela;
  4. Enviar um arquivo de uma célula de tabela;

Preparação e configuração dos serviços do Google

Artigo oficial de "início rápido" no Node JS do Google contém um exemplo de trabalho com a API. Vamos tomá-lo como base.

Configuração de uma conta para trabalhar com a API

Em primeiro lugar, você precisa ir ao site dos desenvolvedores e criar um novo projeto.

Depois de criar o projeto, você precisa selecionar a guia "Biblioteca".

E selecionar Google Sheets API na lista. Em seguida, você precisa clicar no botão "Ativar" ("Enable").

Você deve ser redirecionado para a página de configurações desta API. Nesta página, você precisará criar acessos. Clique no botão "Criar credenciais" ("Create credentials").

Na janela que aparece, você deve selecionar as configurações da conta de serviço e clicar no botão "Selecionar o tipo de credenciais". Recomendamos usar como na captura de tela:

Em seguida, você precisa configurar o nome e a função da conta. Você deve selecionar uma função strong>Editor.

Tipo de chave - JSON. Clique para continuar. Você será solicitado a baixar um arquivo JSON com dados. Nós o salvamos na pasta com o projeto, renomeamos como keys.json para um acesso mais conveniente do projeto.

Isso completa a configuração da conta. Resta apenas atribuir a conta recém-criada em nossa tabela do Google como Editor. Para fazer isso, abra-o e clique no botão "Configurações de acesso"

Você deve adicionar a conta que criamos como o editor desta tabela. Para fazer isso, você precisa olhar seu e-mail no console do desenvolvedor na guia "credenciais" e copiar o e-mail.

Isso completa a configuração dos serviços. Vamos passar para o projeto em si.

Criamos um módulo para trabalhar com Google API

Primeiro, vamos criar um arquivo config.js no qual armazenaremos os dados de configuração do bot. E vamos escrever o id da nossa tabela do google nele. Você pode ver isso na barra de endereços.

config.js

module.exports = {
    spreadid:"1M6fyEhv64ug7zILRz86H1PBKEKHUgKV9pWSW2m_r4SI",  // Google sheet ID
}

A seguir, criamos um arquivo Googleapi.js. Nele iremos armazenar todas as funções e dados para trabalhar com a Google API. Primeiro, você precisa instalar um módulo para trabalhar com a Google Api para NodeJS.
Inserimos o comando npm install Googleapis@39 --save no terminal para instalar este módulo. No próprio arquivo, importamos as dependências.

const config = require("./config.js");
const {Google} = require('Googleapis');
const keys = require('./keys.json');

E criamos um objeto cliente que nos autorize no Google.

const client = new Google.auth.JWT(
    keys.client_email,
    null,
    keys.private_key,
    ['https://www.Googleapis.com/auth/spreadsheets']
) //Json Web Token

Os parâmetros que a função JWT aceita

  • E-mail do arquivo JSON com acesso;
  • O caminho para o arquivo com a chave privada (não o passamos, portanto null);
  • Chave privada de arquivo json com acesso;
  • Lista de acessos. Só temos acesso a Google sheets. Se necessário, eles são passados para esta lista e outras APIs do Google.

Em seguida, você precisa chamar uma função que nos autorizará no sistema.

client.authorize(function(err, tokens) {
    if (err){
        console.log(err);
        return;
    }

    console.log('Connected Google Sheets Api!');
    gsrun(client);
});

let gsapi;

async function gsrun(cl){
    gsapi = Google.sheets({version:'v4', auth:cl})
}

Se tudo tiver passado com sucesso, mostramos no console "Connected Google Sheets Api!" e inserimos no objeto gsapi a classe Sheets, que nos parâmetros aceita a versão da API usada e o objeto Client que criamos anteriormente. Depois disso, resta descrever as funções que funcionarão com os dados.

Método para a obtenção de dados

async function getValues(range)
{
    const opt = {
        spreadsheetId: config.spreadid,
        range : range
    }

    let data = await gsapi.spreadsheets.values.get(opt);
    let dataArray = data.data.values;

    return dataArray;
}

Para receber os dados de uma tabela, vamos escrever uma função. Aceita um intervalo de células como parâmetros no seguinte formato: "Planilha1! A1: B2", onde Planilha1 - é o nome de sua planilha na tabela. Tenha cuidado ao especificar este parâmetro.

opt: este é um dicionário de parâmetros que passamos em uma solicitação para API Google.

  • spreadsheetId: ID da tabela;
  • range: o intervalo de valores dos quais extrair informações;

Para recuperar dados de uma tabela, opt deve ser passado para o método gsapi.spreadsheets.values.get(opt);

Este método retorna todas as informações sobre a solicitação e armazena especificamente os dados em data.values.

Agora vamos escrever um método que nos permitirá inserir dados na tabela. Para inserir os dados no final da tabela, primeiro precisamos saber o número da última linha. A API não permite que você faça isso diretamente, portanto, primeiro descreveremos um método que retornará o número da última linha antes de inserir os dados.

async function getLastRow() // Get the number of the last row in the table
{
    const opt = {
        spreadsheetId: config.spreadid,
        range: 'Data!A1:A'
    }
    let response = await gsapi.spreadsheets.values.get(opt);
    return response.data.values.length;
}

Sua essência é obter todos os dados no intervalo A1: 1, - ou seja, até o final da tabela e, em seguida, retornar o comprimento da matriz resultante.

Método de registro de dados

async function updateSheet(name, phone) // Write in the last row of the table the data.
{
    let lastRow = await getLastRow() + 1;
    const opt = {
            spreadsheetId : config.spreadid,
            range: 'Data!A' + lastRow,
            valueInputOption:'USER_ENTERED',
            resource: {values: [[name, phone]]}
    }
    await gsapi.spreadsheets.values.update(opt);
}

Nos parâmetros confirme o nome e o telefone (vamos armazená-los na tabela). Além disso, o dicionário opt agora contém parâmetros adicionais na forma de nossos dados. Mind that values is an array of arrays. Portanto, podemos passar uma série de dados, não apenas uma string. Para o registro se utiliza o método update.

Isso conclui nosso trabalho com a Google Api, nos resta apenas exportar os métodos para trabalhar com a Api para que possamos chamá-los a partir de outra classe.

module.exports.updateSheet = updateSheet;
module.exports.getValues = getValues;
module.exports.getLastRow = getLastRow;

Trabalho com WhatsApp API

Nota!
Para que o bot funcione, seu telefone deve estar conectado à Internet e não deve ser usado para a Whatsapp Web.

Primeiro, iremos vincular imediatamente o WhatsApp ao nosso script, então, quando escrevermos o código, verificaremos o seu funcionamento. Para fazer isso, vá para sua conta pessoal e obtenha um código QR lá. Em seguida, abra o WhatsApp no seu celular, vá para Configurações -> WhatsApp Web -> Digitalize o código QR.

Para trabalhar com a Whatsapp API, você precisa de um token e um Uri de sua conta pessoal. Você os encontrará no cabeçalho da sua instância

Vamos escrevê-los no arquivo de configuração:

module.exports = {
    apiUrl: "https://eu115.chat-api.com/instance12345/",
    token: "1hi0xwfzaxsews12345", // Token for working with API from personal cabinet
    spreadid:"1M6fyEhv64ug7zILRz86H1PBKEKHUgKV9pWSW2m_r4SI",  // Google table ID
}

Depois disso, criamos o arquivo index.js. Ele conterá toda a lógica do bot e do servidor para processar as solicitações do Webhook. Importamos todas as dependências.

const config = require("./config.js");
const Googleapi = require("./Googleapi.js");
const token = config.token, apiUrl = config.apiUrl;
const menu_text = config.menuText;
const app = require('express')();
const bodyParser = require('body-parser');
const fetch = require('node-fetch');
  • node-fetch permite solicitações de API, config para carregar nossos dados de outro arquivo;
  • token e apiUrl nossos dados do arquivo de configuração, que nos permitem acessar WA Api;
  • O módulo Express é necessário para implementar um servidor web que processará as solicitações;
  • body-parser permitirá que você extraia convenientemente o fluxo de solicitações recebidas;
  • Googleapi nosso módulo para trabalhar com GoogleApi;

Em seguida, informamos ao servidor que analisaremos os dados Json:

app.use(bodyParser.json());

Desligamos o manipulador de erros:

process.on('unhandledRejection', err => {
    console.log(err)
});

E descrevemos uma função que funcionará com o WhatsApp Api.

async function apiChatApi(method, params){
    const options = {};
    options['method'] = "POST";
    options['body'] = JSON.stringify(params);
    options['headers'] = { 'Content-Type': 'application/json' };

    const url = `${apiUrl}/${method}?token=${token}`;

    const apiResponse = await fetch(url, options);
    const jsonResponse = await apiResponse.json();
    return jsonResponse;
}

Em parâmetros, esta função adopta um método que precisa ser executado e um objeto com parâmetros que passaremos na solicitação. Dentro da função, criamos um objeto de opções, que imediatamente substituímos por duas chaves: json e método. No primeiro, passamos os parâmetros necessários para a API, e no segundo, indicamos o método com o qual aplicamos e em que queremos receber a resposta. A seguir, declaramos uma constante: nosso url para acessar a API. Na verdade, ele conterá o próprio url (da configuração), o método e o token. Em seguida, enviamos um pedido para o Chat-Api

Agora que temos a função pronta, podemos descrever a lógica básica do bot. Vamos descrever o controlador para o qual os dados do webhook serão recebidos

app.post('/', async function (req, res) {
    const data = req.body;
}

Esta função é o controlador que processa os pedidos POST para o endereço do servidor principal (o responsável por disso é '/' caminho ). data - é o arquivo json de entrada.

Para saber que tipo de JSON iremos receber no servidor. Usaremos ferramentas de teste.

app.post('/', async function (req, res) {
    const data = req.body;
    for (var i in data.messages) {
        const body = String(data.messages[i].body.toLowerCase());
        const chatId = data.messages[i].chatId;
        splitBody = body.split(' ');
        command = splitBody[0];

        if(data.messages[i].fromMe)
            return;

        if(command == 'help')
        {
            await apiChatApi('sendMessage', {chatId:chatId, body: menu_text});
        }
        else if (command == 'insert')
        {
            name = splitBody[1];
            phone = splitBody[2];
            await Googleapi.updateSheet(name, phone)
            await apiChatApi('sendMessage', {chatId:chatId, body: 'Successfully recorded'})
        }

        else if (command == 'info')
        {
            let result;
            if (splitBody.length == 1){
                result = await getInfoDataFromSheet('A2:D2');
            }
            else{
                result = await getInfoDataFromSheet(splitBody[1]);
            }
            x = await apiChatApi('sendMessage', {chatId:chatId, body: result})
            console.log(x);
        }

        else if (command == 'file')
        {
            linkFile = (await Googleapi.getValues('Data!D2'))[0][0];
            x = await apiChatApi('sendFile', {chatId:chatId, body: linkFile, 'filename':'testfile'})
        }

        else if (command == 'bulk'){
            lastRow = await Googleapi.getLastRow() + 1;
            dataAll = await Googleapi.getValues('Data!A2:D' + lastRow);
            dataAll.forEach(async function(entry){
                await apiChatApi('sendMessage', {phone:entry[1], body: `Hi, ${entry[0]}, its a test mailing.`});
            });
        }

        else
        {
            await apiChatApi('sendMessage', {chatId:chatId, body: menu_text})
        }
    }
    res.send('Ok');
});

Neste controlador, realizamos as ações necessárias com base no comando que é enviado. Vamos analisar cada um separadamente e mostrar imediatamente o resultado do teste.

Preenchimento de células

Para inserir dados em uma tabela, pegamos uma mensagem, dividimos usando o método split e passamos o nome e o número de telefone para uma função que escrevemos para funcionar com a Google API.

 else if (command == 'insert'){
    name = splitBody[1];
    phone = splitBody[2];
    await Googleapi.updateSheet(name, phone)
    await apiChatApi('sendMessage', {chatid:chatId, body: 'Successfully recorded'})
}

Ler a partir da célula, obter informações

Para receber dados, transferimos o intervalo de dados de entrada da mensagem, ou se o usuário não enviou o intervalo, enviamos o padrão A2:D2

 else if (command == 'info'){
    let result;
    if (splitBody.length == 1){
        result = await getInfoDataFromSheet('A2:D2');
    }
    else{
        result = await getInfoDataFromSheet(splitBody[1]);
    }
    await apiChatApi('sendMessage', {chatId:chatId, body: result})
}

A função GetInfoDataFromSheet simplesmente forma uma string das matrizes de dados que o GoogleApi devolveu nos.

async function getInfoDataFromSheet(range){
    data = await Googleapi.getValues('Data!' + range);
    result = "";
    data.forEach(function(entry) {
        result += entry.join(' ') + "\n"
    });
    return result;
}

Enviar arquivo para WhatsApp

Para enviar um arquivo, pegamos um link direto para o arquivo da célula da tabela e o enviamos usando o método sendFile

 else if (command == 'file'){
    linkFile = (await Googleapi.getValues('Data!D2'))[0][0];
    x = await apiChatApi('sendFile', {chatId:chatId, body: linkFile, 'filename':'testfile'})
}

Envio no WhatsApp

Para o envio, basta percorrer toda a tabela e enviar as mensagens para os números indicados. Para testar a correspondência, adicionamos nosso número em duas linhas à tabela.

Advertência!
Exortamos nossos clientes a não enviar mensagens indesejadas, fazer envios de marketing em massa. Caso contrário, sua conta pode ser bloqueada pelo sistema anti-spam da WhatsApp! Entre em contato com nosso suporte técnico para esclarecer recomendações.
else if (command == 'bulk'){
    lastRow = await Googleapi.getLastRow() + 1;
    dataAll = await Googleapi.getValues('Data!A2:D' + lastRow);
    dataAll.forEach(async function(entry){
        await apiChatApi('sendMessage', {phone:entry[1], body: `Hi, ${entry[0]}, its a test mailing.`});
    });
}

Todos os testes foram concluídos com sucesso. Agora podemos enviar nosso bot para o servidor e instalar o Webhook.

Webhook

O Webhook resolve o problema de atrasos na resposta às mensagens recebidas. Sem ele, nosso bot teria que perguntar constantemente ao servidor sobre os dados recebidos, fazer solicitações regulares e fixas aos servidores. Portanto, ter alguma latência na resposta, e isso também, contribuiria para a carga no servidor.

Mas, se especificarmos o endereço do servidor Webhook, essa necessidade não será mais relevante. Os próprios servidores enviarão notificações de mudanças recebidas assim que aparecerem. E a tarefa do servidor Webhook é aceitar e processá-los corretamente, implementando a lógica do bot. Você pode especificar um domínio e um endereço ip.

Agora precisamos fazer o upload do bot para um hosting (servidor dedicado) e executá-lo. Quando o fizermos, indicaremos o domínio ou endereço IP na conta pessoal como um webhook e testaremos o trabalho do bot.

Durante a inicialização, uma mensagem com uma conexão bem-sucedida aparecerá:

O Whatsapp bot em conjunto com o Google Sheets está pronto

Assim, descrevemos o trabalho de um simples chatbot whatsapp e postamos o código fonte com funcionalidade pronta para uso no github.

O código e o guia completos estarão disponíveis no link: https://github.com/chatapi/whatsapp-google-sheets-bot-en

Criação de um bot do WhatsApp usando Google Sheets API no NodeJS

Você só precisa substituir no código seu token de sua conta pessoal e número de instância.

Agora você precisa carregar o servidor junto com o bot para a hospedagem e especificar seu domínio como o gancho da web. Com cada mensagem recebida, o servidor receberá e processará os dados. Se você tiver alguma dúvida, você pode sempre entrar em contato com nosso suporte técnico!