Bot de WhatsApp con la API de Google Sheets en NodeJS

GUÍA PASO A PASO
Repositorio de origen de bot
Publicado: February 1, 2021
hand

La funcionalidad de esta guía se ha transferido a la API de WhatsApp Business, con la que también puede hacer mailings, crear chat bots y más, pero sin riesgo de ser bloqueado.

Debido a cambios en las condiciones de trabajo de la API, esta guía puede utilizar información desactualizada parcialmente. Consulte la documentación antes de comenzar.

Ahora Chat API ofrece WhatsApp Business API más accesible y automatizado del mercado con el Chat Multiusuario, Constructor de Chatbot sin código, integraciones de apps listas para usar y otras características.

En esta guía, le mostraremos cómo funciona y cómo configurar la API de Google Sheets. Creamos un bot de WhatsApp en NodeJS usando nuestra puerta de enlace, que estará vinculada a Google Sheets.

En este ejemplo, el bot responderá a los comandos recibidos en forma de mensajes regulares en WhatsApp y responderá a ellos o ejecutará el comando. ¡No se olvide descargar el bot terminado de nuestro repositorio y usarlo en su trabajo!

¡Una práctica novedad!
Aproveche nuestro integrador gratuito API Chains. Ya tenemos lista la integración con Google Sheets. Por ejemplo, puede utilizar esta solución para extraer y guardar información importante de sus tablas, o registrar información de los bots. ¡Pruébalo!

¿Cuál es la funcionalidad de este bot?

  1. Escribir la información en una tabla;
  2. Leer información de una tabla;
  3. Enviar mensajes por números de la tabla;
  4. Enviar un archivo desde una celda de tabla;

Preparación y configuración de los servicios de Google

El artículo oficial de "inicio rápido" en Node JS de Google contiene un ejemplo de cómo trabajar con la API. Tomémoslo como base.

Configuración de una cuenta para trabajar con la API

Primero, debe ir al sitio web de los desarrolladores y crear un nuevo proyecto.

Después de crear el proyecto, debe seleccionar la pestaña "Biblioteca".

Y seleccione Google Sheets API de la lista. Luego, debe hacer clic en el botón "Habilitar" ("Enable")

Usted debe ser redirigido a la página de configuración para esta API. En esta página, usted tendrá que crear accesos. Haga clic en el botón ("Create credentials").

En la ventana que aparece, debe seleccionar la configuración de la cuenta de servicio y hacer clic en el botón "Seleccionar el tipo de credenciales". Recomendamos usar como en la captura de pantalla:

A continuación, debe configurar el nombre y la función de la cuenta. Debe seleccionar una función Editor.

Tipo de clave: JSON. Haga clic para continuar. Se le pedirá que descargue un archivo JSON con datos. Lo guardamos en la carpeta con el proyecto, renombrado keys.json para un acceso más conveniente al proyecto

Ahora la configuración de la cuenta está completa. Solo queda asignar la cuenta recién creada en nuestra tabla de Google como editor. Para hacer esto, ábralo y haga clic en el botón "Configuración de acceso".

Debe agregar la cuenta que creamos como editor para esta tabla. Para hacer esto, debe mirar su correo electrónico en la consola del desarrollador en la pestaña "credenciales" y copiar el correo electrónico.

Esto completa la configuración de los servicios. Pasemos al proyecto en sí.

Creamos un módulo para trabajar con Google API

Primero, vamos a crear un archivo config.js en el que almacenaremos los datos de configuración del bot. Y escribiremos la identificación de nuestra tabla de Google en él. Usted puede ver esto en la barra de direcciones.

config.js

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

A continuación, creamos un archivo Googleapi.js. En este archive vamos a almacenar todas las funciones y datos para trabajar con la Google API. Primero, necesita instalar un módulo para trabajar con Google Api para NodeJS.
Insertamos el comando npm install [email protected] --save en el terminal para instalar este módulo. En el propio archivo, importamos las dependencias.

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

Y creamos un objeto cliente que nos autoriza en Google.

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

Los parámetros que la función JWT acepta:

  • Correo electrónico de archivo JSON con acceso;
  • La ruta al archivo con la clave privada (no la pasamos, por lo tanto es null);
  • Clave de archivo json privada con acceso;
  • Lista de acceso. Solo tenemos acceso a Google sheets. Si es necesario, se pasan a esta lista y a otras API de Google.

A continuación, usted debe activar la función que nos autoriza a entrar en el 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})
}

Si todo ha pasado con éxito, mostramos en la consola "Connected Google Sheets Api!" y escribimos en el objeto gsapi la clase Sheets, que en los parámetros toma la versión de la API utilizada y el objeto Client que creamos anteriormente. Después de eso, nos queda describir las funciones que funcionarán con los datos.

Método de obtención de datos

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 recibir datos de una tabla, escribamos una función. Acepta un rango de celdas como parámetros en el siguiente formato: "Hoja1! A1: B2", donde Hoja1 - es el nombre de su hoja en la tabla. Tenga cuidado al especificar este parámetro.

opt: este es un diccionario de parámetros que pasamos en una solicitud a la Google API.

  • spreadsheetId: ID de la tabla;
  • range: el rango de valores del cual extraer información;

Para recuperar datos de una tabla, opt se debe pasar al método gsapi.spreadsheets.values.get(opt);

Este método devuelve toda la información sobre la solicitud y almacena específicamente los datos en data.values.

Ahora vamos a escribir un método que nos permitirá insertar datos en la tabla. Para insertar los datos al final de la tabla, primero necesitamos saber el número de la última fila. La API no le permite hacer esto directamente, por lo que primero describiremos un método que devolverá el número de la última línea antes de ingresar los datos.

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

Su esencia es obtener todos los datos en el rango A1: 1, - es decir, hasta el final de la tabla y luego devolver la longitud de la 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);
}

En los parámetros confirme el nombre y el número de teléfono (los almacenaremos en la tabla). Además, el diccionario ahora opt contiene parámetros adicionales en forma de nuestros datos. Tenga en cuenta que los valores son una matriz de matrices. Por lo tanto, podemos pasar una serie de datos, no solo una cadena.Para el registro, se utiliza el método update.

Con esto concluye nuestro trabajo con Google Api, solo tenemos que exportar los métodos para trabajar con la Api para que podamos llamarlos desde otra clase

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

Trabajo con WhatsApp API

¡Nota!
Para que el bot funcione, su teléfono debe estar conectado a Internet y no debe usarse para Whatsapp Web.

Primero, enlazaremos inmediatamente WhatsApp a nuestro script, luego, cuando escribamos el código, comprobaremos cómo funciona. Para hacer esto, vaya a su cuenta personal y obtenga un código QR allí. Luego, abra WhatsApp en su teléfono, vaya a Configuración Primero, enlazaremos inmediatamente WhatsApp a nuestro script, luego, cuando escribamos el código, verificaremos cómo funciona. Para hacer esto, vaya a su cuenta personal y obtenga un código QR allí. Luego abra WhatsApp en su teléfono, vaya a Configuración -> WhatsApp Web -> Escanee el código QR.

Para trabajar con la Whatsapp API, necesita un token y una Uri de su cuenta personal. Los encontrará en el encabezado de su instancia.

Los escribiremos en el archivo de configuración:

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
}

Después de eso, creamos el archivo index.js. Contendrá toda la lógica del bot y del servidor para procesar las solicitudes de Webhook. Importamos todas las dependencias.

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 congigurar las solicitudes de API para cargar nuestros datos de otro archivo;
  • token y apiUrl los datos de nuestro archivo de configuración, que nos permiten acceder a WA Api;
  • El módulo Express es necesario para implementar un servidor web que procesará las solicitudes;
  • body-parser le permitirá extraer cómodamente el flujo de solicitudes recibidas;
  • Googleapi nuestro módulo para trabajar con GoogleApi;

Luego, informamos al servidor que analizaremos los datos de Json:

app.use(bodyParser.json());

Apagamos el manejador de errores:

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

Y describimos una función que funcionará con 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;
}

En parámetros, esta función adopta un método que necesita ser ejecutado y un objeto con parámetros que pasaremos en la solicitud. Dentro de la función, creamos un objeto de opciones, que inmediatamente reemplazamos con dos claves: json y método. En el primero, pasamos los parámetros necesarios a la API, y en el segundo, indicamos el método con el que aplicamos y en el que queremos recibir la respuesta. A continuación, declaramos una constante: nuestra url para acceder a la API. De hecho, contendrá la url (de la configuración), el método y el token. Luego, enviamos una solicitud a Chat-Api.

Ya que al final tenemos la función lista, podemos describir la lógica básica del bot. Describiremos el controlador para el que se recibirán los datos del webhook.

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

Esta función es el controlador que procesa las solicitudes POST a la dirección del servidor principal (el responsable de esto es '/' ruta). data - es el archivo json de entrada.

Para saber qué tipo de JSON recibiremos en el servidor. Usaremos herramientas de prueba.

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

En este controlador, realizamos las acciones necesarias en función del comando que se envía. Analizaremos cada uno por separado e inmediatamente mostraremos el resultado de la prueba.

Relleno de celdas

Para insertar datos en una tabla, tomamos un mensaje, lo dividimos usando el método split y pasamos el nombre y el número de teléfono a una función que escribimos para trabajar con la 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'})
}

Leer desde la celda, obtener información

Para recibir datos, transferimos el rango de datos de entrada del mensaje, o si el usuario no envió el rango, enviamos el estándar 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})
}

La función GetInfoDataFromSheet simplemente forma una cadena de matrices de datos que GoogleApi nos devolvió.

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

Enviar archivo a WhatsApp

Para enviar un archivo, tomamos un enlace directo al archivo de celda de la tabla y lo enviamos usando el 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'})
}

Envío por WhatsApp

Para enviar, simplemente recorre toda la tabla y envía los mensajes a los números indicados. Para probar la coincidencia, agregamos nuestro número en dos líneas a la tabla.

¡Advertencia!
Instamos a nuestros clientes a no enviar mensajes no deseados, realizar envíos masivos de marketing. De lo contrario, el sistema antispam de WhatsApp puede bloquear su cuenta. Póngase en contacto con nuestro soporte técnico para aclarar recomendaciones.
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.`});
    });
}

Todas las pruebas se completaron con éxito. Ahora podemos enviar nuestro bot al servidor e instalar Webhook

Webhook

El Webhook resuelve el problema de los retrasos en la respuesta a los mensajes recibidos. Sin él, nuestro bot tendría que preguntar constantemente al servidor sobre los datos recibidos, realizar solicitudes regulares y fijas a los servidores. Por lo tanto, tener algo de latencia en la respuesta, y esto también, contribuiría a la carga en el servidor.

Pero, si especificamos la dirección del servidor Webhook, esa necesidad ya no será relevante. Los propios servidores enviarán notificaciones de cambios recibidos tan pronto como aparezcan. Y la tarea del servidor webhook es aceptarlos y procesarlos correctamente, implementando la lógica del bot. Puede especificar un dominio y una dirección ip.

Ahora necesitamos subir el bot a un hosting (servidor dedicado) y ejecutarlo. Cuando lo hagamos, indicaremos el dominio o la dirección IP en la cuenta personal como un webhook y probaremos el funcionamiento del bot. Durante el inicio, aparecerá un mensaje con una conexión exitosa:

El bot de Whatsapp junto con Google Sheets está listo

Así, describimos el trabajo de un simple chatbot de whatsapp y publicamos el código fuente con la funcionalidad lista para usar en github.

El código y la guía completos estarán disponibles en el enlace: https://github.com/chatapi/whatsapp-google-sheets-bot-en

Cómo crear un bot de WhatsApp con NodeJS usando la API de Google Sheets

Sólo tiene que sustituir en el código tu token de tu cuenta personal y el número de instancia.

Ahora usted necesita subir el servidor junto con el bot al hosting y especificar su dominio como el webhook. Con cada mensaje entrante el servidor recibirá y procesará los datos. Si usted tiene alguna pregunta, siempre puede contactar con nuestro soporte técnico.