Zenvia no CONAREC 2024: veja o que aconteceu no maior evento de CX do mundo
Saiba mais sobre a participação da Zenvia no maior evento de customer experience do mundo, o CONAREC 2024.
Saiba maisO melhor portal de Marketing e Vendas para a sua empresa
Criar um Bot para WhatsApp é mais simples do que parece. Confira o passo a passo completo para desenvolver um Bot para WhatsApp utilizando API.
Desenvolver um chatbot para WhatsApp (ou WhatsApp Bot) não precisa ser um bicho de sete cabeças, mas você precisa ficar atento já que a WhatsApp Business API é de uso fechado e há apenas duas formas de ter acesso.
A primeira é por meio de solicitação direta ao Facebook, o que pode ser um pouco desafiador já que pouquíssimas requisições são aceitas pela empresa. Já a segunda é mais viável: contratar uma empresa provedora oficial de confiança do WhatsApp. Dentre as opções, destacamos a brasileira Zenvia.
A empresa é provedora oficial permite ao usuário criar uma conta gratuita para utilizar serviços como a API do WhatsApp.
Após a criação da conta, você deve seguir o seguinte fluxo: Menu superior > Produtos > Desenvolvedores > Sandbox > Criar novo > Escolha o canal “WhatsApp” > Próximo
Um QR Code aparecerá na tela com o celular e a partir dele será gerado um link que abrirá o seu aplicativo do WhatsApp, contendo o contato da Zenvia e uma mensagem previamente escrita. Basta enviá-la para configurar a SandBox e registrar seu número de telefone para o ambiente de testes:
1 – Instale o Node no seu computador (de preferência a versão LTS);
1.1- opcional – Instale o yarn. O node possui um gerenciador de pacotes padrão chamado npm (Node Package Manager), porém existe um gerenciador diferente chamado yarn.
No restante deste artigo, haverão as opções de instalação para ambos gerenciadores de pacote como no passo a seguir.
2 – Inicie o projeto criando o package.json com o seguinte comando no terminal:
npm init
yarn init
3 – Instale o ts-node, ts-node-dev e o typescript como dependências de desenvolvimento:
npm i ts-node ts-node-dev typescript -D
yarn add ts-node ts-node-dev typescript -D
3.1 – Instale o arquivo de configuração do typescript:
npx typescript --init
4 – Abra o arquivo package.json e insira o script para iniciar o servidor assim como na área grifada em cinza:
{
"name": "WhatsAppChatbot",
"version": "1.0.0",
"description": "This project aims to create an example of creating a chatbot for WhatsApp.",
"main": "index.js",
"scripts": {
"dev": "ts-node-dev index.ts"
},
"author": "Henry Kimura",
"license": "MIT",
"devDependencies": {
"ts-node": "^9.0.0",
"ts-node-dev": "^1.0.0",
"typescript": "^4.0.5"
}
}
1 – Instale a dependência request-promise para o Node:
npm i request-promise
yarn add request-promise
2 – Após ter configurado seu número de testes com a SandBox anteriormente, ele aparecerá na aba de contatos:
3 – Copie o código de exemplo abaixo e substitua SEU-TOKEN pelo seu token gerado pela plataforma Zenvia, substitua o REMETENTE e o DESTINATÁRIO também:
import post from 'request-promise'
post({
uri: 'https://api.zenvia.com/v2/channels/whatsapp/messages',
headers: {
'X-API-TOKEN': 'SEU_TOKEN',
},
body: {
from: 'REMETENTE',
to: 'DESTINATARIO',
contents: [
{
type: 'text',
text: 'Lembre-se que sua consulta está marcada para às 15:00',
},
],
},
json: true,
})
.then((response) => {
console.log('Response:', response)
})
.catch((error) => {
console.log('Error:', error)
})
As informações solicitadas podem ser encontradas nesta tela:
4 – Crie um arquivo index.ts, cole o código copiado anteriormente e salve o arquivo.
5 – Abra o terminal na pasta do projeto e execute o script criado anteriormente no package.json:
npm run dev
yarn dev
6 – Se estiver tudo certo, você receberá a mensagem configurada no código:
Até o momento apenas criamos um exemplo para enviar uma mensagem definida. Agora vamos para um exemplo mais divertido onde o usuário manda uma mensagem e recebe uma resposta.
Primeiro vamos precisar configurar um webhook, que é uma forma de recebimento de informações quando um evento acontece. No nosso caso, a ZENVIA API’s irá enviar um objeto JSON com informações sobre uma mensagem recebida pela API.
1- Vamos começar instalando o express para lidar com o recebimento das requisições e o body-parser para lidar melhor com o manuseio de objetos:
npm i express
npm i @types/express -D
npm i body-parser
yarn add express
yarn add @types/express -D
yarn add body-parser
2- Renomeie o arquivo index.ts para sendMessage.ts e crie um novo index.ts com este código:
import express, { Request, Response } from 'express'
import bodyParser from 'body-parser'
// Inicializa o express e define uma porta
const app = express()
const PORT = 3000
// Indica para o express usar o JSON parsing do body-parser
app.use(bodyParser.json())
app.post('/hook', (req: Request, res: Request) => {
console.log(req.body) // Exibe no console da aplicação o objeto da mensagem
res.status(200).end() // Responde quem solicitou nosso webhook com status 200
})
// Inicia o express na porta definida anteriormente
app.listen(PORT, () => console.log(`Server running on port ${PORT}`))
3 – Precisamos tornar nosso webhook visível para que a ZENVIA API’s possa nos enviar as informações da mensagem recebida, para isto vamos utilizar o ngrok. Faça login no site, baixe a versão para Windows, extraia o zip, execute a aplicação (abrirá um terminal), autentique-se digitando ngrok authtoken SEU_TOKEN (seu token está na primeira tela do seu dashboard) e exponha a porta da aplicação (3000) digitando ngrok http 3000
1- Após expor a porta da sua aplicação copie o link que aparecerá no terminal:
2 – Cole seu link no campo “Message Webhook URL”, coloque /hook (rota criada anteriormente no nosso webhook) ao final do URL na aba “Subscription” e clique em “Salvar”:
3 – Mande uma mensagem de teste pelo WhatsApp e verifique se seu webhook recebeu a informação:
O conteúdo da mensagem estará dentro do corpo da requisição, desta forma: req.body.message.contents[0].text
4 – Agora que estamos recebendo a mensagem, é preciso de algo para enviar uma resposta. Vamos criar um sistema bem simples onde o chatbot recebe um “Oi” e ele responde com um “Olá {nome do usuário}! Como posso ajudar?“. Para começar, precisamos alterar o arquivo sendMessage.ts. Do jeito que ele está, este arquivo apenas envia uma mensagem específica ao nosso contato, vamos torná-lo em uma função dinâmica e exportável.
Vamos criar uma função exportável para mandar mensagem dinamicamente no sendMessage.ts, onde o parâmetro message da função, terá o conteúdo da mensagem que será enviada ao usuário:
import post from 'request-promise'
const sendMessage = (message) => {
post({
uri: 'https://api.zenvia.com/v2/channels/whatsapp/messages',
headers: {
'X-API-TOKEN': 'SEU_TOKEN',
},
body: {
from: 'REMETENTE',
to: 'DESTINATARIO',
contents: [
{
type: 'text',
text: message,
},
],
},
json: true,
})
.then((response) => {
console.log('Response:', response)
})
.catch((error) => {
console.log('Error:', error)
})
}
export default sendMessage
Adicione esta linha de importação no index.ts:
import express from 'express'
import bodyParser from 'body-parser'
import sendMessage from './sendMessage'
//...
Por fim, vamos alterar a rota /hook no arquivo index.ts
//...
app.post('/hook', (req: Request, res: Response) => {
const contact = req.body.message.visitor // Armazena em uma variável quem mandou a mensagem
const message = req.body.message.contents[0].text // Armazena em uma variável a mensagem
if (message.toLowerCase() === 'oi') { // Verifica se a mensagem enviada foi um "Oi"
sendMessage(`Olá ${contact.firstName}! Como posso te ajudar?`)
} else { // Se não, mande a seguinte mensagem
sendMessage('Me desculpe, não entendi.')
}
res.status(200).end() // Responde quem solicitou nosso webhook com status 200
})
//...
Nosso chatbot agora está funcionando, podendo receber uma mensagem e respondê-la
Com isso tudo apresentado você já consegue colher e enviar mensagens. A partir daqui a criatividade é o limite para a criação do seu WhatsApp bot.
Agora vamos criar um chatbot para ser o seu Assistente Pessoal que consiga guardar informações e lembrar o usuário destas informações em um horário específico, liste todas as informações guardadas e consiga removê-las também.
Agora a nossa aplicação vai ficar um pouco mais complexa e interessante, necessitando de um banco de dados para armazenar informações.
Para fins de aprendizado, vamos utilizar o SQLite pois é muito simples de ser configurado e não precisa ser instalado na sua máquina. Também utilizaremos um query builder chamado Knex.js, onde podemos contruir consultas em javascript e posteriormente podemos alterar o banco de dados que as consultas permanecerão as mesmas,
1 – Instale a dependência do SQLite
npm i sqlite3
yarn add sqlite3
2 – Instale a dependência o Knex.js
npm install knex --save
yarn add knex
3 – Crie o arquivo de configuração knex com o nome knexfile.ts
import path from 'path' // lida com caminhos de arquivos
module.exports = {
client: 'sqlite3',
connection: {
filename: path.resolve(__dirname, 'src', 'database', 'database.sqlite'),
},
migrations: {
directory: path.resolve(__dirname, 'src', 'database', 'migrations'),
},
useNullAsDefault: true,
}
4 – Crie uma pasta src, dentro dela cria outra chamada database e crie o arquivo connection.ts
import knex from 'knex'
import path from 'path'
const db = knex({
client: 'sqlite3',
connection: {
filename: path.resolve(__dirname, 'database.sqlite')
},
useNullAsDefault: true,
})
export default db
5 – Dentro da pasta database, crie outra chamada migrations e crie o arquivo 00_create_appointments.ts
import Knex from 'knex'
async function up(knex: Knex) {
return knex.schema.createTable('appointments', (table) => {
table.increments('id').primary(),
table.string('description').notNullable(),
table.timestamp('datetime').notNullable()
})
}
async function down(knex: Knex) {
return knex.schema.dropTable('appointments')
}
module.exports = { up, down }
5.1 – Em seguida, crie o banco de dados executando:
npx knex --knexfile knexfile.ts migrate:latest
1- Crie o arquivo PastaDoProjeto/src/functions/interpretsMessage.ts com a função que irá interpretar a mensagem recebida e direcionar para demais outras funções:
import sendMessage from './sendMessage'
import createAppointment from './createAppointment'
interface Contact { //formato do parâmetro contato
name: string
firstName: string
lastName: string
}
const interpretsMessage = (contact: Contact, message: any) => {
switch (message.toLowerCase()) {
case 'ajuda': // Verifica se a mensagem enviada foi "ajuda"
sendMessage(`
Olá ${contact.firstName}! Digite algum dos comandos para saber mais:\n \t*lembrete*
`)
break
case 'lembrete': // Verifica se a mensagem enviada foi "lembrete"
sendMessage(
`Entendido. Mande uma mensagem com o seguinte formato:\n Me lembre de _____ dia __/__/____ às __:__`
)
break
default: // Se não foi nenhuma das anteriores
if (message.split(' ')[1] === 'lembre') { // Verifica se é para armazenar o compromisso
const description = message.split('Me lembre de ')[1].split(' dia')[0] // Armazena a descrição em uma variável
const date = message.split('dia ')[1].split(' às')[0].split('/') // Armazena a data em uma variável
const time = message.split('às ')[1] // Armazena o horário em uma variável
const when = `${date[2]}-${date[1]}-${date[0]} ${time}` // Junta a data e o horário em uma variável
const appointment = { // Armazena a descrição e quando o deve ser enviado o lembrete do compromisso em um objeto
description,
when,
}
createAppointment(appointment) // Chama a função de criar o compromisso no banco de dados (será criada logo a seguir)
} else {
// Se não, mande a seguinte mensagem
sendMessage(
'Me desculpe, não entendi. Digite *ajuda* para saber todos os comandos.'
)
break
}
}
}
export default interpretsMessage
2 – No index.ts importe a função interpretsMessage, remova a importação da função sendMessage e altere a rota /hooks:
import express, { Request, Response } from 'express'
import bodyParser from 'body-parser'
import interpretsMessage from './src/functions/interpretsMessage'
// Inicializa o express e define uma porta
const app = express()
const PORT = 3000
// Indica para o express usar o JSON parsing do body-parser
app.use(bodyParser.json())
app.post('/hook', (req: Request, res: Response) => {
const contact = req.body.message.visitor // Armazena em uma variável quem mandou a mensagem
const message = req.body.message.contents[0].text // Armazena em uma variável a mensagem
interpretsMessage(contact, message)
res.status(200).end() // Responde quem solicitou nosso webhook com status 200
})
// Inicia o express na porta definida anteriormente
app.listen(PORT, () => console.log(`Server running on port ${PORT}`))
3 – Crie o arquivo PastaDoProjeto/src/functions/createAppointment.ts com a função de criação de um novo lembrete:
import db from '../database/connection' // Importação da conexão com o Banco de Dados
interface Appointment { // Formato do parâmetro appointment
description: string
when: string
}
async function createAppointment(appointment: Appointment) {
const { description, when } = appointment // Coletando as informações do parâmetro
await db('appointments').insert({ // Inserindo no banco de dados o compromisso
description,
when,
})
}
export default createAppointment
1- Vamos criar uma função que executa a cada 1 minuto e faz uma consulta ao banco de dados verificando se existe algum compromisso para aquele momento. Instale o node-cron e importe ele no index.ts para criarmos o laço
npm i node-cron
npm i @types/node-cron -D
yarn add node-cron
yarn add @types/node-cron -D
import express, { Request, Response } from 'express'
import bodyParser from 'body-parser'
import interpretsMessage from './src/functions/interpretsMessage'
import cron from 'node-cron'
// Inicializa o express e define uma porta
const app = express()
const PORT = 3000
// Indica para o express usar o JSON parsing do body-parser
app.use(bodyParser.json())
app.post('/hook', (req: Request, res: Response) => {
const contact = req.body.message.visitor // Armazena em uma variável quem mandou a mensagem
const message = req.body.message.contents[0].text // Armazena em uma variável a mensagem
interpretsMessage(contact, message)
res.status(200).end() // Responde quem solicitou nosso webhook com status 200
})
cron.schedule('* * * * *', () => { // Laço que executa a cada 1 minuto
checkAndSendReminder() // Função que será criada a seguir
})
// Inicia o express na porta definida anteriormente
app.listen(PORT, () => console.log(`Server running on port ${PORT}`))
2 – Mova o arquivo sendMessage.ts para o diretório PastaDoProjeto/src/functions/
3 – Devemos criar a função que verifica no banco de dados se há um compromisso e mandar um lembrete. Crie o arquivo no seguinte caminho PastaDoProjeto/src/functions/checkAndSendAppointment.ts
import db from '../database/connection' // Importação da conexão com o Banco de Dados
import sendMessage from './sendMessage' // Importação da função de envio de mensagem
const fixZero = (datetime: string) => {
//Função para corrigir 0 no horário
if (datetime.split(':')[0].substr(-2, 1) === ' ') {
return `${datetime.substr(0, 11)}0${datetime.substr(11, 4)}`
} else {
return datetime
}
}
export const checkAndSendReminder = async () => { // Função que verica a existência de compromisso e envia lembrete se houver
const datetime = `${fixZero(new Date().toLocaleString('pt-BR'))}`.substr(0, 16) // Captura a data e o horário atual
const appointments = await db('appointments').where('datetime', '=', datetime).select('*') // Busca no Banco de Dados se existe um compromisso agora
if (appointments.length !== 0) { // Se houver algum compromisso envia uma mensagem de lembrete
const message = `Lembre-se que você precisa: ${appointments.map(
(appointment) => `\n*${appointment.description}*`
)}`
sendMessage(message)
}
}
4 – Vamos criar uma função que remove compromissos do banco de dados. Crie o arquivo PastaDoProjeto/src/functions/removeAppointments.ts com o seguinte código:
import db from '../database/connection' // Importação da conexão do Banco de Dados
import sendMessage from './sendMessage' // Importação da função de envio de mensagem
async function removeAppointments(datetime: string, notificateUser: boolean) {
const removedAppointment = await db('appointments').where('datetime', datetime).del() // Tenta remover do banco de dados os compromissos na data e hora especificadas
if (notificateUser === true && typeof removedAppointment === 'number') { // Caso seja para notificar o usuario
if (removedAppointment === 1) { // Verifica se foi removido apenas um compromisso
sendMessage('Lembrete apagado com sucesso.')
}
if (removedAppointment > 1) { // Verifica se foram removidos diversos compromissos
sendMessage('Lembretes apagados com sucesso.')
}
}
}
export default removeAppointments
5 – Para evitar que acumule informações desnecessárias, sempre que o usuário receber o lembrete, vamos apagar o compromisso no banco de dados. Para isto, vamos alterar o arquivo checkAndSendReminder.ts:
import db from '../database/connection' // Importação da conexão com o Banco de Dados
import sendMessage from './sendMessage' // Importação da função de envio de mensagem
import removeAppointments from './removeAppointments' // Importação da função de remoção de compromisso
const fixZero = (datetime: string) => { //Função para corrigir 0 no horário
if (datetime.split(':')[0].substr(-2, 1) === ' ') {
return `${datetime.substr(0, 11)}0${datetime.substr(11, 4)}`
} else {
return datetime
}
}
export const checkAndSendReminder = async () => { // Função que verica a existência de compromisso e envia lembrete se houver
const datetime = `${fixZero(new Date().toLocaleString('pt-BR'))}`.substr(0, 16) // Captura a data e o horário atual
const appointments = await db('appointments').where('datetime', '=', datetime).select('*') // Busca no Banco de Dados se existe um compromisso agora
if (appointments.length !== 0) { // Se houver algum compromisso envia uma mensagem de lembrete
const message = `Lembre-se que você precisa: ${appointments.map(
(appointment) => `\n*${appointment.description}*`
)}`
sendMessage(message)
removeAppointments(datetime, false) // Remove o compromisso no banco de dados e não notifica o usuário
}
}
6 – Vamos agora confirmar ao usuário quando um compromisso for agendado com sucesso. Altere o arquivo createAppointment.ts:
import db from '../database/connection'
import sendMessage from './sendMessage'
interface Appointment {
description: string
datetime: string
}
async function createAppointment(appointment: Appointment) {
const { description, datetime } = appointment
const insertedAppointment = await db('appointments').insert({
description,
datetime,
})
if (typeof insertedAppointment[0] === 'number') {
sendMessage('Compromisso agendado com sucesso.')
}
}
export default createAppointment
7- Agora precisamos permitir que o usuário remova compromissos, para isto vamos alterar o arquivo interpretsMessage.ts
import sendMessage from './sendMessage'
import createAppointment from './createAppointment'
import removeAppointments from './removeAppointments'
interface Contact {
name: string
firstName: string
lastName: string
}
const interpretsMessage = (contact: Contact, message: any) => {
switch (message.toLowerCase()) {
case 'ajuda': // Verifica se a mensagem enviada foi "ajuda"
sendMessage(`
Olá ${contact.firstName}! Digite algum dos comandos para saber mais:\n\t*lembrete*\n\t*esqueça*
`)
break
case 'lembrete': // Verifica se a mensagem enviada foi "lembrete"
sendMessage(
`Entendido. Mande uma mensagem com o seguinte formato:\n Me lembre de _____ dia __/__/____ às __:__`
)
break
case 'esqueça': // Verifica se a mensagem enviada foi "esqueça"
sendMessage(
`Entendido. Mande uma mensagem com o seguinte formato:\n Desmarque tudo no dia __/__/____ às __:__`
)
break
default: // Se não foi nenhuma das anteriores
if (message.split(' ')[1] === 'lembre') { // Verifica se é para armazenar o compromisso
const description = message.split('Me lembre de ')[1].split(' dia')[0] // Armazena a descrição em uma variável
const date = message.split('dia ')[1].split(' às')[0].split('/') // Armazena a data em uma variável
const time = message.split('às ')[1] // Armazena o horário em uma variável
const datetime = `${date[2]}-${date[1]}-${date[0]} ${time}` // Junta a data e o horário em uma variável
const appointment = {
description,
datetime,
}
createAppointment(appointment) // Chama a função de criar o compromisso no banco de dados
} else if (message.substr(0, 9) === 'desmarque') { // Verifica se é para remover o compromisso
const date = message.split('dia ')[1].split(' às')[0].split('/')
const time = message.split('às ')[1]
removeAppointments(`${date[2]}-${date[1]}-${date[0]} ${time}`, true)
} else {
// Se não, mande a seguinte mensagem
sendMessage('Digite *ajuda* para saber todos os comandos.')
break
}
}
}
export default interpretsMessage
8 – Teste e veja seu Assistente Pessoal funcionando:
9 – Vamos criar a função que liste todos os compromissos no arquivo indexAppointments.ts
import db from '../database/connection' // Importação da conexão do Banco de Dados
import sendMessage from './sendMessage' // Importação da função de envio de mensagem
const showDate = (datetime: string) => { // Altera a exibição da data e da hora
const date_time = datetime.split(' ')
const date = date_time[0].split('-')
return `${date[2]}/${date[1]}/${date[0]} às ${date_time[1]}`
}
async function indexAppointments() {
const indexedAppointment = await db('appointments').select('*') // Busca no banco de dados os compromissos na data e hora especificadas
if (indexedAppointment.length !== 0) { // Verifica se encontrou dados
const message = `*Seus compromissos são:*${indexedAppointment.map(
(appointment) => `\n\t${showDate(appointment.datetime)} - ${appointment.description}`
)}`
sendMessage(message) // Manda mensagem com os agendamentos
} else {
sendMessage('Você não possui compromissos marcados.')
}
}
export default indexAppointments
10 – Altere mais uma vez o arquivo interpretsMessage.ts para adicionar a função de agenda
import sendMessage from './sendMessage'
import createAppointment from './createAppointment'
import removeAppointments from './removeAppointments'
import indexAppointments from './indexAppointments'
//...
case 'esqueça': // Verifica se a mensagem enviada foi "esqueça"
sendMessage(
`Entendido. Mande uma mensagem com o seguinte formato:\n Desmarque tudo no dia __/__/____ às __:__`
)
break
case 'agenda': // Verifica se a mensagem enviada foi "agenda"
indexAppointments()
break
default:
//...
Agora seu Assistente Pessoal está pronto para guardar seus compromissos, te lembrar quando chegar o momento, listar todos os compromissos agendados e remover compromissos em uma determinada hora e data.
Com o nosso passo a passo para criar Bot no WhatsApp, ficou fácil tirar a sua ideia do papel! Aproveite e faça uso da API de WhatApp no ambiente de testes da Zenvia, o Sandbox. A partir dele você consegue receber informações sobre as mensagens trocadas por meio de um webhook.
Com todas as informações necessárias em mãos, o que você tem são inúmeras possibilidades de automação, oferecendo benefícios como aumento da produtividade, personalização de produtos e atendimento 24/7.