Primeiro Bot Conversacional
Transformando webhook em inteligência conversacional
O que você aprenderá
- Lógica conversacional avançada
- Menu interativo com botões
- Estados e contexto de conversa
- Integração com APIs externas
- Funcionalidades práticas de negócio
Progresso do Módulo
🤖 De Webhook para Bot Inteligente
🔄 A Evolução Natural do seu Sistema
Na aula anterior, você construiu a infraestrutura: mensagens chegam, são processadas e confirmadas. Agora vamos adicionar inteligência: seu bot vai conversar, lembrar contexto e oferecer funcionalidades úteis.
⚙️ Aula 2: Infraestrutura
- • Webhook funcional 24/7
- • Recebe mensagens em tempo real
- • Processa e confirma recebimento
- • Base técnica sólida
🧠 Aula 3: Inteligência
- • Conversas com contexto
- • Menu interativo profissional
- • Estados de conversa
- • Funcionalidades práticas
🎯 Automação vs Inteligência Conversacional
Existe uma diferença enorme entre um sistema que "responde mensagens"e um bot que "conversa inteligentemente". Hoje vamos cruzar essa linha.
❌ Automação Básica (Aula 2)
Resposta genérica. Não mantém contexto. Não ajuda realmente.
✅ Inteligência Conversacional (Hoje)
Menu claro. Mantém contexto. Guia o usuário para ação.
🏗️ O que vamos construir hoje
Vamos transformar seu webhook em um assistente virtual profissionalcom funcionalidades que empresas reais usam para automatizar atendimento e vendas.
🎮 Menu Interativo
Sistema de navegação com botões e emojis que guia usuários pelas opções disponíveis.
🧠 Estados de Conversa
Bot lembra onde parou a conversa e adapta respostas ao contexto atual.
🔗 Integrações Externas
Consulta CEP, busca informações em APIs e integra com planilhas Google.
🛠️ Funcionalidades que Implementaremos
Módulo Core (Obrigatório)
- • Menu principal: Navegação intuitiva com emojis
- • Consulta CEP: Busca endereços via ViaCEP API
- • Calculadora: Operações matemáticas simples
- • Horário de funcionamento: Info contextual
Módulo Avançado (Opcional)
- • Agendamento: Coleta dados e salva em planilha
- • Catálogo de produtos: Lista com preços
- • Lead capture: Formulário de contato
- • Suporte técnico: FAQ inteligente
🎯 Estratégia pedagógica
Vamos começar com funcionalidades simples para entender os conceitos, depois evoluir para integrações mais complexas. Cada funcionalidade é um building block que você pode combinar para criar soluções únicas.
💰 Impacto Comercial Real
Cada funcionalidade que vamos implementar resolve problemas reais de negócios. Não é só "aprender a programar" - é criar valor mensurável.
📞 Atendimento 24/7
Bot responde imediatamente, qualquer hora
🎯 Qualificação de Leads
Coleta dados antes de enviar para vendedor
📊 Automação de Processos
Agendamentos, consultas, formulários automáticos
💡 Perspectiva freelancer/agência
Um bot como este é vendido por R$ 2.000-8.000 + R$ 300-500/mês de manutenção. Empresas pagam bem por automação que realmente funciona eentrega resultados mensuráveis.
🧠 Arquitetura de Conversas Inteligentes
🎯 O que torna uma conversa "inteligente"
Uma conversa inteligente não é apenas sobre entender palavras, mas sobre entender intenções e manter contexto. Vamos desmembrar os componentes que fazem isso funcionar.
Intent Recognition
O que o usuário realmente quer?
Context Memory
Lembrar do que foi dito antes
Dynamic Response
Respostas adaptadas ao contexto
Goal Direction
Guiar para objetivo específico
🔄 Estados de Conversa: O Cérebro do Bot
Imagine que cada conversa é como navegar por um mapa mental. O bot precisa saber "onde está" na conversa para responder adequadamente.
🗺️ Exemplo prático: Fluxo de agendamento
💡 Por que estados são críticos
Se usuário digitar "João" no estado INICIO, bot não sabe o que fazer. Se digitar "João" no estado COLETANDO_NOME, bot sabe que é o nome e vai para próximo passo.
🎯 Reconhecimento de Intenções (Intent Recognition)
Diferentes pessoas expressam a mesma intenção de formas diferentes. Seu bot precisa entender variações e mapear para ações corretas.
🛒 Intent: COMPRAR
📞 Intent: CONTATO
❓ Intent: AJUDA
⚙️ Implementação no N8N: Estrutura Técnica
Vamos ver como traduzir esses conceitos para um workflow N8N prático e escalável.
📊 Estrutura de dados que precisamos armazenar
Por usuário (Sessão):
Por conversa:
🔄 Fluxo de processamento no N8N
💾 Onde Armazenar Estados: Opções Práticas
Para conversas inteligentes, precisamos guardar informações entre mensagens. Vamos ver as opções disponíveis do mais simples ao mais robusto.
🧠 N8N Memory (Simples)
📊 Google Sheets (Intermediário)
🗄️ Banco de Dados (Profissional)
🎯 Nossa estratégia nesta aula
Vamos começar com Google Sheets porque é o sweet spot entre simplicidade e funcionalidade. Depois de dominar os conceitos, você pode migrar para banco de dados se necessário.
🎮 Criando Menu Interativo Profissional
🎯 Por que Menu Interativo é Fundamental
Um menu bem estruturado é a diferença entre usuário perdido e usuário convertido. É o GPS da conversa - mostra onde o usuário pode ir e como chegar lá.
❌ Sem menu (Conversa perdida)
Usuário não sabe o que pode fazer. Conversa sem direção.
✅ Com menu (Conversa direcionada)
📅 Agendar consulta
📞 Falar com atendente
❓ Ajuda
Usuário sabe exatamente suas opções. Conversão imediata.
🔍 Anatomia de um Menu Eficaz
📐 Estrutura visual
🎯 Princípios de UX
- • Max 4-5 opções: Não sobrecarregar escolhas
- • Emojis únicos: Fácil identificação visual
- • Texto claro: Sem ambiguidade do que faz
- • Ordem lógica: Mais importante primeiro
- • Escape hatch: Sempre ter "outras opções"
⚙️ Implementação: Menu Principal no N8N
Vamos criar um sistema de menu que reconhece tanto emojis quanto palavras-chave, tornando a interação mais flexível e intuitiva.
📝 Função: Gerar Menu Principal
// Função para gerar menu principal
function gerarMenuPrincipal(nomeUsuario = "") {
const saudacao = nomeUsuario ? `Olá ${nomeUsuario}! 👋` : "Olá! 👋";
return `${saudacao} Como posso ajudar você hoje?
━━━━━━━━━━━━━━━━━━━
🛒 Ver produtos e preços
📅 Agendar atendimento
💬 Falar com atendente
🏢 Informações da empresa
❓ Outras opções
━━━━━━━━━━━━━━━━━━━
Digite o emoji ou a palavra-chave (ex: "produtos", "agendar")`;
}
// Retornar menu
return {
menu: gerarMenuPrincipal($json.nome),
user_state: "MENU_PRINCIPAL"
};🎯 Função: Reconhecer Opção do Menu
// Função para reconhecer qual opção do menu foi escolhida
function reconhecerOpcaoMenu(mensagem) {
const msg = mensagem.toLowerCase().trim();
// Mapeamento de opções do menu
const opcoes = {
// Produtos
'produtos': 'PRODUTOS',
'produto': 'PRODUTOS',
'preço': 'PRODUTOS',
'precos': 'PRODUTOS',
'valor': 'PRODUTOS',
'comprar': 'PRODUTOS',
'🛒': 'PRODUTOS',
'1': 'PRODUTOS',
// Agendamento
'agendar': 'AGENDAMENTO',
'agendamento': 'AGENDAMENTO',
'consulta': 'AGENDAMENTO',
'horario': 'AGENDAMENTO',
'data': 'AGENDAMENTO',
'📅': 'AGENDAMENTO',
'2': 'AGENDAMENTO',
// Atendente
'atendente': 'ATENDENTE',
'humano': 'ATENDENTE',
'pessoa': 'ATENDENTE',
'suporte': 'ATENDENTE',
'ajuda': 'ATENDENTE',
'💬': 'ATENDENTE',
'3': 'ATENDENTE',
// Empresa
'empresa': 'EMPRESA',
'informacoes': 'EMPRESA',
'sobre': 'EMPRESA',
'contato': 'EMPRESA',
'endereco': 'EMPRESA',
'endereço': 'EMPRESA',
'🏢': 'EMPRESA',
'4': 'EMPRESA',
// Outras opções
'outras': 'OUTRAS',
'opcoes': 'OUTRAS',
'opções': 'OUTRAS',
'menu': 'MENU_PRINCIPAL',
'voltar': 'MENU_PRINCIPAL',
'❓': 'OUTRAS',
'5': 'OUTRAS'
};
// Procurar correspondência
for (const [palavra, acao] of Object.entries(opcoes)) {
if (msg.includes(palavra)) {
return acao;
}
}
// Se não encontrou nada, retornar para menu
return 'MENU_PRINCIPAL';
}
// Aplicar reconhecimento
const opcaoEscolhida = reconhecerOpcaoMenu($json.message);
return {
opcao: opcaoEscolhida,
message_original: $json.message,
next_state: opcaoEscolhida
};🧭 Sistema de Navegação: Submenus e Breadcrumbs
Menus profissionais têm submenus e permitem navegação intuitiva. Vamos implementar um sistema que nunca deixa o usuário perdido.
📱 Exemplo: Submenu de Produtos
// Função para gerar submenu de produtos
function gerarSubmenuProdutos() {
return `🛒 **PRODUTOS E SERVIÇOS**
Escolha uma categoria:
🖥️ Desenvolvimento Web
📱 Apps Mobile
🤖 Automação WhatsApp
🎯 Marketing Digital
━━━━━━━━━━━━━━━━━━━
🔙 Voltar ao menu principal
❓ Precisa de ajuda?
Digite emoji ou palavra-chave`;
}
// Breadcrumb para orientação
function gerarBreadcrumb(caminho) {
const breadcrumbs = {
'MENU_PRINCIPAL': '🏠 Menu Principal',
'PRODUTOS': '🏠 Menu > 🛒 Produtos',
'PRODUTOS_WEB': '🏠 Menu > 🛒 Produtos > 🖥️ Web',
'AGENDAMENTO': '🏠 Menu > 📅 Agendamento'
};
return breadcrumbs[caminho] || '🏠 Menu Principal';
}
return {
submenu: gerarSubmenuProdutos(),
breadcrumb: gerarBreadcrumb("PRODUTOS"),
user_state: "PRODUTOS"
};🔄 Função: Navegação Universal
// Sistema de navegação que funciona em qualquer submenu
function processarNavegacao(mensagem, estadoAtual) {
const msg = mensagem.toLowerCase().trim();
// Comandos universais de navegação
const comandosUniversais = {
'menu': 'MENU_PRINCIPAL',
'inicio': 'MENU_PRINCIPAL',
'voltar': getEstadoAnterior(estadoAtual),
'home': 'MENU_PRINCIPAL',
'🏠': 'MENU_PRINCIPAL',
'🔙': getEstadoAnterior(estadoAtual),
'ajuda': 'AJUDA',
'❓': 'AJUDA'
};
// Verificar comandos universais primeiro
for (const [comando, destino] of Object.entries(comandosUniversais)) {
if (msg.includes(comando)) {
return destino;
}
}
// Se não é comando universal, processar conforme estado atual
return null; // Continua processamento específico do estado
}
function getEstadoAnterior(estadoAtual) {
const hierarquia = {
'PRODUTOS_WEB': 'PRODUTOS',
'PRODUTOS_MOBILE': 'PRODUTOS',
'PRODUTOS': 'MENU_PRINCIPAL',
'AGENDAMENTO_NOME': 'AGENDAMENTO',
'AGENDAMENTO': 'MENU_PRINCIPAL'
};
return hierarquia[estadoAtual] || 'MENU_PRINCIPAL';
}
// Aplicar navegação
const navegacao = processarNavegacao($json.message, $json.current_state);
return {
new_state: navegacao,
is_navigation: navegacao !== null
};✨ Menu Personalizado e Contexto
Menus inteligentes se adaptam ao usuário e ao contexto. Vamos implementar personalização baseada em dados coletados.
🎨 Menu Personalizado por Perfil
// Função para gerar menu baseado no perfil do usuário
function gerarMenuPersonalizado(dadosUsuario) {
const { nome, empresa, jaEhCliente, ultimaCompra } = dadosUsuario;
let saudacao = nome ? `Olá ${nome}! 👋` : "Olá! 👋";
// Personalizar saudação baseado no histórico
if (jaEhCliente && ultimaCompra) {
const diasUltimaCompra = calcularDiasAtras(ultimaCompra);
saudacao += ` Ótimo te ver novamente! (última compra: ${diasUltimaCompra} dias atrás)`;
} else if (empresa) {
saudacao += ` Representando ${empresa}?`;
}
// Menu adaptado
let menu = saudacao + "\n\n━━━━━━━━━━━━━━━━━━━\n";
if (jaEhCliente) {
// Menu para clientes existentes
menu += `🔄 Novo pedido
📦 Status do pedido atual
🛠️ Suporte técnico
💰 Faturamento/NFe
📞 Contato comercial`;
} else {
// Menu para prospects
menu += `🛒 Ver produtos e preços
📋 Solicitar orçamento
📅 Agendar demonstração
🏢 Sobre nossa empresa
💬 Falar com consultor`;
}
menu += "\n━━━━━━━━━━━━━━━━━━━\n";
menu += "Digite emoji ou palavra-chave";
return menu;
}
// Calcular dias atrás
function calcularDiasAtras(dataString) {
const agora = new Date();
const data = new Date(dataString);
const diffTime = Math.abs(agora - data);
return Math.ceil(diffTime / (1000 * 60 * 60 * 24));
}
// Aplicar personalização
const dadosUsuario = {
nome: $json.nome || "",
empresa: $json.empresa || "",
jaEhCliente: $json.is_cliente || false,
ultimaCompra: $json.ultima_compra || null
};
return {
menu_personalizado: gerarMenuPersonalizado(dadosUsuario),
user_state: "MENU_PRINCIPAL",
is_returning_user: dadosUsuario.jaEhCliente
};⏰ Menu Contextual por Horário
// Menu que se adapta ao horário e dia da semana
function gerarMenuContextual() {
const agora = new Date();
const hora = agora.getHours();
const diaSemana = agora.getDay(); // 0 = domingo, 6 = sábado
const isWeekend = diaSemana === 0 || diaSemana === 6;
let contexto = "";
let opcoesExtras = "";
// Adaptar por horário
if (hora >= 6 && hora < 12) {
contexto = "Bom dia! ☀️";
} else if (hora >= 12 && hora < 18) {
contexto = "Boa tarde! 🌤️";
} else if (hora >= 18 && hora < 22) {
contexto = "Boa noite! 🌆";
} else {
contexto = "Olá! 🌙";
opcoesExtras = "\n🔔 Atendimento 24h disponível";
}
// Adaptar por dia da semana
if (isWeekend) {
contexto += " (Final de semana - atendimento reduzido)";
opcoesExtras += "\n📅 Retornaremos na segunda-feira";
}
// Verificar se é horário comercial
const isHorarioComercial = !isWeekend && hora >= 9 && hora <= 18;
let menu = contexto + " Como posso ajudar?\n\n━━━━━━━━━━━━━━━━━━━\n";
if (isHorarioComercial) {
menu += `🛒 Ver produtos
📞 Falar com vendedor (ONLINE)
📅 Agendar reunião
💬 Suporte técnico (ONLINE)`;
} else {
menu += `🛒 Ver produtos
📧 Deixar mensagem
📅 Agendar para próximo dia útil
🤖 Suporte automático`;
}
menu += opcoesExtras;
menu += "\n━━━━━━━━━━━━━━━━━━━";
return {
menu: menu,
is_horario_comercial: isHorarioComercial,
contexto_horario: contexto
};
}
// Gerar menu contextual
return gerarMenuContextual();📊 Métricas: Otimizando seu Menu
Um menu eficaz pode ser medido. Vamos implementar tracking para otimização contínua.
📈 KPIs do Menu
- • Taxa de clique por opção: Qual opção é mais popular
- • Taxa de abandono: Quantos saem após ver menu
- • Comandos não reconhecidos: O que usuários tentam fazer
- • Tempo de decisão: Quanto demora para escolher
🔧 Como otimizar
- • A/B test: Testar diferentes versões do menu
- • Analisar patterns: Qual sequência converte mais
- • Feedback direto: Perguntar o que falta
- • Iteração contínua: Melhorar baseado em dados
🧠 Sistema de Estados: Memória Conversacional
Por que estados são essenciais
Estados permitem que o bot "lembre" do contexto da conversa. Sem eles, cada mensagem é tratada isoladamente. Com estados, o bot entende se o usuário está no meio de um cadastro, fazendo uma compra ou apenas navegando pelo menu.
O sistema de estados é o que transforma um bot simples em um assistente inteligente. Ele permite criar fluxos conversacionais complexos onde cada resposta depende do contexto atual da conversa e das interações anteriores.
🔄 Como Funciona o Sistema de Estados
Cada usuário tem um "estado atual" armazenado (aguardando_nome, confirmando_pedido, etc.). Quando uma mensagem chega, o bot verifica o estado e decide como responder. Após processar, pode manter o mesmo estado ou transicionar para um novo.
💾 Implementação com Google Sheets
Vamos usar Google Sheets como banco de dados simples para armazenar os estados. Cada linha representa um usuário com seu telefone, estado atual, e dados da conversa. É uma solução prática e fácil de visualizar.
🎯 Estados do Sistema
Nosso bot terá estados como: menu_principal, aguardando_nome, aguardando_email, confirmando_dados, consultando_cep, agendando_horario. Cada estado define exatamente qual o próximo passo esperado do usuário.
🔗 Integrações com APIs Externas
🎯 Por que APIs tornam seu bot poderoso
Um bot que só responde texto pré-programado é limitado. Quando você integra APIs externas, seu bot ganha superpoderes: consulta CEP, busca clima, acessa bancos de dados, integra com CRM, e muito mais.
❌ Bot sem APIs (Limitado)
Bot inútil. Usuário frustrado. Transferência desnecessária para humano.
✅ Bot com APIs (Poderoso)
Bela Vista, São Paulo - SP
Precisa de mais alguma coisa?"
Resposta instantânea, precisa e útil. Usuário impressionado.
🛠️ APIs que vamos implementar
📍 ViaCEP (Gratuita)
Consulta endereços pelo CEP ou busca CEPs por endereço
🌤️ OpenWeather (Free tier)
Previsão do tempo por cidade ou coordenadas
📊 Google Sheets
Salvar leads, consultar estoque, preços dinâmicos
📍 Implementação: Consulta de CEP
Vamos criar uma funcionalidade que permite consultar CEP de duas formas: pelo CEP (obter endereço) ou pelo endereço (obter CEP).
🔍 Função: Consultar CEP
// Função para consultar CEP usando ViaCEP API
async function consultarCEP(input) {
try {
// Detectar se input é CEP ou endereço
const isCEP = /^d{5}-?d{3}$/.test(input.replace(/s/g, ''));
if (isCEP) {
return await buscarEnderecoPorCEP(input);
} else {
return await buscarCEPPorEndereco(input);
}
} catch (error) {
return {
success: false,
message: "Erro ao consultar CEP. Tente novamente."
};
}
}
// Buscar endereço por CEP
async function buscarEnderecoPorCEP(cep) {
// Limpar CEP (apenas números)
const cepLimpo = cep.replace(/D/g, '');
if (cepLimpo.length !== 8) {
return {
success: false,
message: "CEP deve ter 8 dígitos. Exemplo: 01310-100"
};
}
// Fazer requisição para ViaCEP
const response = await fetch(`https://viacep.com.br/ws/${cepLimpo}/json/`);
const data = await response.json();
if (data.erro) {
return {
success: false,
message: "CEP não encontrado. Verifique e tente novamente."
};
}
// Formatar resposta
return {
success: true,
tipo: "endereco",
message: formatarEnderecoCompleto(data)
};
}
// Buscar CEP por endereço (aproximado)
async function buscarCEPPorEndereco(endereco) {
// Parse básico do endereço
const enderecoLimpo = endereco.trim();
const partes = enderecoLimpo.split(',');
if (partes.length < 2) {
return {
success: false,
message: "Para buscar CEP, use: Rua/Av, Número, Cidade\nExemplo: Av Paulista, 1000, São Paulo"
};
}
const [logradouro, cidade] = partes;
const uf = partes[2] || "SP"; // Default SP
// Fazer busca aproximada
const ufLimpo = uf.trim().substring(0, 2).toUpperCase();
const cidadeLimpa = cidade.trim().replace(/s+/g, '%20');
const logradouroLimpo = logradouro.trim().replace(/s+/g, '%20');
const response = await fetch(
`https://viacep.com.br/ws/${ufLimpo}/${cidadeLimpa}/${logradouroLimpo}/json/`
);
const resultados = await response.json();
if (!Array.isArray(resultados) || resultados.length === 0) {
return {
success: false,
message: "Endereço não encontrado. Tente ser mais específico:\nExemplo: Av Paulista, São Paulo, SP"
};
}
// Retornar primeiro resultado (mais relevante)
return {
success: true,
tipo: "cep",
message: formatarResultadoCEP(resultados[0])
};
}
// Formatar endereço completo
function formatarEnderecoCompleto(dados) {
return `📍 **ENDEREÇO ENCONTRADO**
🏠 **Logradouro:** ${dados.logradouro}
🏘️ **Bairro:** ${dados.bairro}
🏙️ **Cidade:** ${dados.localidade}
🗺️ **Estado:** ${dados.uf}
📮 **CEP:** ${dados.cep}
${dados.complemento ? `📝 **Complemento:** ${dados.complemento}` : ''}
Precisa de mais alguma consulta?`;
}
// Formatar resultado de CEP
function formatarResultadoCEP(dados) {
return `🔍 **CEP ENCONTRADO**
📮 **CEP:** ${dados.cep}
🏠 **Endereço:** ${dados.logradouro}
🏘️ **Bairro:** ${dados.bairro}
🏙️ **Cidade:** ${dados.localidade} - ${dados.uf}
Digite outro endereço ou CEP para nova consulta!`;
}
// Processar consulta CEP
const resultado = await consultarCEP($json.message);
return {
cep_response: resultado.message,
cep_success: resultado.success,
next_state: "MENU_PRINCIPAL"
};🌤️ Implementação: Previsão do Tempo
Funcionalidade que consulta clima atual e previsão para qualquer cidade. Usuários adoram esse tipo de utility no bot.
🌡️ Função: Consultar Clima
// Função para consultar clima usando OpenWeatherMap API
async function consultarClima(cidade) {
try {
const API_KEY = "SUA_API_KEY_AQUI"; // Registre em openweathermap.org
// Limpar nome da cidade
const cidadeLimpa = cidade.trim().replace(/s+/g, '%20');
// Fazer requisição para OpenWeather
const response = await fetch(
`https://api.openweathermap.org/data/2.5/weather?q=${cidadeLimpa},BR&appid=${API_KEY}&units=metric&lang=pt_br`
);
if (!response.ok) {
throw new Error("Cidade não encontrada");
}
const data = await response.json();
return {
success: true,
message: formatarClima(data)
};
} catch (error) {
return {
success: false,
message: `❌ Não consegui encontrar informações sobre "${cidade}".\n\nTente:\n• Nome completo da cidade\n• Adicionar estado (ex: Santos, SP)\n• Verificar ortografia`
};
}
}
// Formatar dados do clima
function formatarClima(dados) {
const temp = Math.round(dados.main.temp);
const sensacao = Math.round(dados.main.feels_like);
const descricao = dados.weather[0].description;
const umidade = dados.main.humidity;
const vento = dados.wind.speed;
const cidade = dados.name;
// Emoji baseado no clima
const emojiClima = getEmojiClima(dados.weather[0].main);
return `${emojiClima} **CLIMA EM ${cidade.toUpperCase()}**
🌡️ **Temperatura:** ${temp}°C (sensação ${sensacao}°C)
☁️ **Condição:** ${descricao}
💧 **Umidade:** ${umidade}%
💨 **Vento:** ${vento} m/s
${getRecomendacao(temp, descricao)}
Digite outra cidade para nova consulta!`;
}
// Emoji baseado na condição climática
function getEmojiClima(condicao) {
const emojis = {
'Clear': '☀️',
'Clouds': '☁️',
'Rain': '🌧️',
'Drizzle': '🌦️',
'Thunderstorm': '⛈️',
'Snow': '❄️',
'Mist': '🌫️',
'Fog': '🌫️'
};
return emojis[condicao] || '🌤️';
}
// Recomendação baseada no clima
function getRecomendacao(temp, descricao) {
if (temp < 15) {
return "🧥 Recomendação: Vista roupas quentes!";
} else if (temp > 30) {
return "🌊 Recomendação: Hidrate-se bem e use protetor solar!";
} else if (descricao.includes('chuva') || descricao.includes('rain')) {
return "☔ Recomendação: Não esqueça o guarda-chuva!";
} else {
return "😊 Clima agradável! Aproveite o dia!";
}
}
// Processar consulta de clima
const resultado = await consultarClima($json.message);
return {
clima_response: resultado.message,
clima_success: resultado.success,
next_state: "MENU_PRINCIPAL"
};📊 Integração com Google Sheets
Google Sheets como "banco de dados" permite funcionalidades incríveis: catálogo de produtos, lista de preços, estoque, leads, agendamentos...
📋 Função: Consultar Produtos
// Função para consultar produtos em Google Sheets
async function consultarProdutos(categoria = null) {
try {
// Configuração da planilha
const SPREADSHEET_ID = "SEU_SPREADSHEET_ID";
const RANGE = "produtos!A:E"; // Colunas: ID, Nome, Categoria, Preço, Estoque
// Buscar dados via N8N Google Sheets node
const produtos = await googleSheets.read({
spreadsheetId: SPREADSHEET_ID,
range: RANGE
});
// Filtrar por categoria se especificada
let produtosFiltrados = produtos;
if (categoria) {
produtosFiltrados = produtos.filter(p =>
p.categoria.toLowerCase().includes(categoria.toLowerCase())
);
}
if (produtosFiltrados.length === 0) {
return {
success: false,
message: categoria
? `Nenhum produto encontrado na categoria "${categoria}".\n\nCategorias disponíveis: Web, Mobile, Consultoria`
: "Catálogo indisponível no momento."
};
}
return {
success: true,
message: formatarCatalogoProdutos(produtosFiltrados, categoria)
};
} catch (error) {
return {
success: false,
message: "Erro ao consultar produtos. Tente novamente em alguns minutos."
};
}
}
// Formatar catálogo de produtos
function formatarCatalogoProdutos(produtos, categoria) {
const titulo = categoria
? `🛒 **PRODUTOS - ${categoria.toUpperCase()}**`
: "🛒 **CATÁLOGO DE PRODUTOS**";
let catalogo = titulo + "\n\n";
produtos.forEach((produto, index) => {
const disponivel = produto.estoque > 0 ? "✅" : "❌";
catalogo += `${index + 1}. **${produto.nome}**
💰 Preço: R$ ${produto.preco}
📦 Estoque: ${disponivel} ${produto.estoque} unidades
`;
});
catalogo += `━━━━━━━━━━━━━━━━━━━
Para mais detalhes, digite o número do produto.
Ou digite "categorias" para ver todas as categorias.`;
return catalogo;
}
// Função para salvar lead
async function salvarLead(dadosLead) {
try {
const SPREADSHEET_ID = "SEU_SPREADSHEET_ID";
const RANGE = "leads!A:F"; // Colunas: Data, Nome, Telefone, Email, Interesse, Status
const agora = new Date().toLocaleString('pt-BR');
const novoLead = [
agora,
dadosLead.nome,
dadosLead.telefone,
dadosLead.email || '',
dadosLead.interesse || 'Não especificado',
'Novo'
];
await googleSheets.append({
spreadsheetId: SPREADSHEET_ID,
range: RANGE,
values: [novoLead]
});
return {
success: true,
message: `✅ **LEAD SALVO COM SUCESSO!**
📝 Seus dados foram registrados:
👤 Nome: ${dadosLead.nome}
📞 Telefone: ${dadosLead.telefone}
${dadosLead.email ? `📧 Email: ${dadosLead.email}` : ''}
Nossa equipe entrará em contato em até 24h!
Obrigado pelo interesse! 🙏`
};
} catch (error) {
return {
success: false,
message: "Erro ao salvar dados. Por favor, tente novamente."
};
}
}
// Função para consultar disponibilidade de agenda
async function consultarAgenda(data) {
try {
const SPREADSHEET_ID = "SEU_SPREADSHEET_ID";
const RANGE = "agenda!A:C"; // Colunas: Data, Horário, Status
const agenda = await googleSheets.read({
spreadsheetId: SPREADSHEET_ID,
range: RANGE
});
// Filtrar por data
const horariosData = agenda.filter(slot => slot.data === data);
const horariosLivres = horariosData.filter(slot => slot.status === 'Livre');
if (horariosLivres.length === 0) {
return {
success: false,
message: `❌ Não há horários disponíveis para ${data}.\n\nTente outra data ou digite "agenda" para ver próximas datas disponíveis.`
};
}
let resposta = `📅 **HORÁRIOS DISPONÍVEIS - ${data}**\n\n`;
horariosLivres.forEach((slot, index) => {
resposta += `${index + 1}. ${slot.horario}\n`;
});
resposta += "\nDigite o número do horário para agendar.";
return {
success: true,
message: resposta,
horarios: horariosLivres
};
} catch (error) {
return {
success: false,
message: "Erro ao consultar agenda. Tente novamente."
};
}
}
// Exemplo de uso
return {
consultarProdutos,
salvarLead,
consultarAgenda
};⚡ Sistema de Cache para Performance
APIs externas podem ser lentas. Implementar cache evita consultas desnecessárias e melhora drasticamente a experiência do usuário.
🚀 Sistema de Cache Inteligente
// Sistema de cache para APIs externas
class APICache {
constructor() {
this.cache = new Map();
this.TTL = {
cep: 24 * 60 * 60 * 1000, // 24 horas
clima: 30 * 60 * 1000, // 30 minutos
produtos: 60 * 60 * 1000, // 1 hora
agenda: 10 * 60 * 1000 // 10 minutos
};
}
// Gerar chave de cache
gerarChave(tipo, parametros) {
return `${tipo}:${JSON.stringify(parametros)}`;
}
// Verificar se cache é válido
isValido(entrada) {
if (!entrada) return false;
return Date.now() - entrada.timestamp < entrada.ttl;
}
// Buscar no cache
get(tipo, parametros) {
const chave = this.gerarChave(tipo, parametros);
const entrada = this.cache.get(chave);
if (this.isValido(entrada)) {
return entrada.data;
}
// Cache expirado, remover
this.cache.delete(chave);
return null;
}
// Salvar no cache
set(tipo, parametros, data) {
const chave = this.gerarChave(tipo, parametros);
const ttl = this.TTL[tipo] || 60 * 60 * 1000; // Default 1 hora
this.cache.set(chave, {
data: data,
timestamp: Date.now(),
ttl: ttl
});
}
// Limpar cache expirado
limparExpirado() {
const agora = Date.now();
for (const [chave, entrada] of this.cache.entries()) {
if (agora - entrada.timestamp >= entrada.ttl) {
this.cache.delete(chave);
}
}
}
}
// Instância global de cache
const cache = new APICache();
// Wrapper para consultas com cache
async function consultarComCache(tipo, parametros, funcaoConsulta) {
// Tentar buscar no cache primeiro
const cached = cache.get(tipo, parametros);
if (cached) {
return {
...cached,
from_cache: true
};
}
// Não encontrado no cache, fazer consulta real
try {
const resultado = await funcaoConsulta();
// Salvar no cache se sucesso
if (resultado.success) {
cache.set(tipo, parametros, resultado);
}
return {
...resultado,
from_cache: false
};
} catch (error) {
throw error;
}
}
// Exemplo de uso com cache
async function consultarCEPComCache(cep) {
return await consultarComCache(
'cep',
{ cep: cep },
() => buscarEnderecoPorCEP(cep)
);
}
async function consultarClimaComCache(cidade) {
return await consultarComCache(
'clima',
{ cidade: cidade },
() => consultarClima(cidade)
);
}
// Limpar cache periodicamente (executar a cada hora)
setInterval(() => {
cache.limparExpirado();
}, 60 * 60 * 1000);
return {
consultarCEPComCache,
consultarClimaComCache,
cache: cache
};🛠️ Funcionalidades Práticas de Negócio
🎯 Funcionalidades que Geram Valor Real
Agora vamos implementar funcionalidades que resolvem problemas reais de negócios. Cada uma dessas features é algo que empresas pagam bem para automatizar.
🧮 Calculadora de Orçamento
Calcula preços baseado em parâmetros do usuário
📋 Formulário de Lead
Coleta dados estruturados e salva no CRM
⏰ Sistema de Agendamento
Agenda reuniões direto na conversa
🧮 Calculadora de Orçamento Inteligente
Uma calculadora que faz perguntas inteligentes e gera orçamentos personalizados. Perfeita para agências, freelancers e empresas de serviços.
💰 Sistema de Precificação
// Sistema de calculadora de orçamento
class CalculadoraOrcamento {
constructor() {
this.precos = {
// Tipo de projeto
site_simples: { base: 2500, multiplicador: 1.0 },
site_completo: { base: 5000, multiplicador: 1.2 },
ecommerce: { base: 8000, multiplicador: 1.5 },
app_mobile: { base: 12000, multiplicador: 2.0 },
// Funcionalidades extras
extras: {
pagamento_online: 800,
chat_whatsapp: 600,
painel_admin: 1200,
integracao_api: 1500,
design_customizado: 2000,
seo_otimizado: 800
},
// Prazo de entrega
prazos: {
urgente: 1.5, // +50%
normal: 1.0, // preço base
flexivel: 0.8 // -20%
}
};
}
// Calcular orçamento baseado nas respostas
calcular(respostas) {
const { tipo_projeto, extras, prazo, porte_empresa } = respostas;
// Preço base
const projetoBase = this.precos[tipo_projeto];
if (!projetoBase) {
throw new Error("Tipo de projeto inválido");
}
let valorTotal = projetoBase.base;
// Adicionar extras
if (extras && Array.isArray(extras)) {
extras.forEach(extra => {
if (this.precos.extras[extra]) {
valorTotal += this.precos.extras[extra];
}
});
}
// Aplicar multiplicador do projeto
valorTotal *= projetoBase.multiplicador;
// Aplicar ajuste de prazo
const multiplicadorPrazo = this.precos.prazos[prazo] || 1.0;
valorTotal *= multiplicadorPrazo;
// Ajuste por porte da empresa
if (porte_empresa === 'grande') {
valorTotal *= 1.3; // +30% para grandes empresas
} else if (porte_empresa === 'startup') {
valorTotal *= 0.9; // -10% para startups
}
return {
valor_total: Math.round(valorTotal),
detalhamento: this.gerarDetalhamento(respostas, valorTotal),
prazo_estimado: this.calcularPrazo(tipo_projeto, extras),
valido_ate: this.calcularValidadeOrcamento()
};
}
// Gerar detalhamento do orçamento
gerarDetalhamento(respostas, valorTotal) {
const detalhes = [];
// Projeto base
const projetoBase = this.precos[respostas.tipo_projeto];
detalhes.push({
item: this.getNomeProjeto(respostas.tipo_projeto),
valor: projetoBase.base,
tipo: 'base'
});
// Extras
if (respostas.extras) {
respostas.extras.forEach(extra => {
if (this.precos.extras[extra]) {
detalhes.push({
item: this.getNomeExtra(extra),
valor: this.precos.extras[extra],
tipo: 'extra'
});
}
});
}
return detalhes;
}
// Calcular prazo estimado
calcularPrazo(tipoProjeto, extras = []) {
const prazosBase = {
site_simples: 15,
site_completo: 30,
ecommerce: 45,
app_mobile: 60
};
let prazo = prazosBase[tipoProjeto] || 30;
// Adicionar tempo por extra
const tempoExtras = {
pagamento_online: 7,
chat_whatsapp: 3,
painel_admin: 10,
integracao_api: 14,
design_customizado: 21,
seo_otimizado: 7
};
extras.forEach(extra => {
if (tempoExtras[extra]) {
prazo += tempoExtras[extra];
}
});
return prazo;
}
calcularValidadeOrcamento() {
const agora = new Date();
agora.setDate(agora.getDate() + 30); // Válido por 30 dias
return agora.toLocaleDateString('pt-BR');
}
getNomeProjeto(tipo) {
const nomes = {
site_simples: "Site Institucional",
site_completo: "Site Completo",
ecommerce: "E-commerce",
app_mobile: "App Mobile"
};
return nomes[tipo] || tipo;
}
getNomeExtra(extra) {
const nomes = {
pagamento_online: "Gateway de Pagamento",
chat_whatsapp: "Integração WhatsApp",
painel_admin: "Painel Administrativo",
integracao_api: "Integração com APIs",
design_customizado: "Design Customizado",
seo_otimizado: "SEO Otimizado"
};
return nomes[extra] || extra;
}
}
// Função para processar orçamento no bot
function processarOrcamento(dadosUsuario) {
const calculadora = new CalculadoraOrcamento();
try {
const orcamento = calculadora.calcular(dadosUsuario.respostas_orcamento);
return {
success: true,
message: formatarOrcamento(orcamento, dadosUsuario.nome),
orcamento: orcamento
};
} catch (error) {
return {
success: false,
message: "Erro ao calcular orçamento. Vamos recomeçar as perguntas."
};
}
}
// Formatar orçamento para WhatsApp
function formatarOrcamento(orcamento, nomeCliente) {
let resposta = `💰 **ORÇAMENTO PERSONALIZADO**\n`;
if (nomeCliente) {
resposta += `👤 **Cliente:** ${nomeCliente}\n`;
}
resposta += `\n📋 **DETALHAMENTO:**\n`;
orcamento.detalhamento.forEach(item => {
resposta += `• ${item.item}: R$ ${item.valor.toLocaleString('pt-BR')}\n`;
});
resposta += `\n━━━━━━━━━━━━━━━━━━━`;
resposta += `\n💰 **VALOR TOTAL: R$ ${orcamento.valor_total.toLocaleString('pt-BR')}**`;
resposta += `\n⏰ **Prazo:** ${orcamento.prazo_estimado} dias úteis`;
resposta += `\n📅 **Válido até:** ${orcamento.valido_ate}`;
resposta += `\n\n✅ Interessado? Digite "contratar" para prosseguir!`;
resposta += `\n🔄 Quer ajustar algo? Digite "modificar"`;
return resposta;
}
return { CalculadoraOrcamento, processarOrcamento };🎯 Fluxo de Coleta de Requisitos
// Fluxo de perguntas para coleta de requisitos
const fluxoOrcamento = {
ORCAMENTO_INICIO: {
pergunta: `💰 **CALCULADORA DE ORÇAMENTO**
Vou fazer algumas perguntas para criar um orçamento personalizado.
Que tipo de projeto você precisa?
1️⃣ Site institucional simples
2️⃣ Site completo com funcionalidades
3️⃣ E-commerce (loja online)
4️⃣ App mobile
Digite o número da opção:`,
proximo_estado: "ORCAMENTO_TIPO",
validacao: (resposta) => {
const opcao = parseInt(resposta.trim());
return opcao >= 1 && opcao <= 4;
},
processar: (resposta) => {
const tipos = {
1: "site_simples",
2: "site_completo",
3: "ecommerce",
4: "app_mobile"
};
return { tipo_projeto: tipos[parseInt(resposta)] };
}
},
ORCAMENTO_TIPO: {
pergunta: `🎨 **FUNCIONALIDADES EXTRAS**
Quais funcionalidades extras você precisa?
(Digite os números separados por vírgula)
1️⃣ Gateway de pagamento online
2️⃣ Integração com WhatsApp
3️⃣ Painel administrativo
4️⃣ Integração com APIs externas
5️⃣ Design 100% customizado
6️⃣ SEO otimizado
0️⃣ Nenhuma extra
Exemplo: 1,3,5`,
proximo_estado: "ORCAMENTO_EXTRAS",
validacao: (resposta) => {
return /^[d,s]+$/.test(resposta.trim());
},
processar: (resposta) => {
const numeros = resposta.split(',').map(n => parseInt(n.trim()));
const mapeamento = {
1: "pagamento_online",
2: "chat_whatsapp",
3: "painel_admin",
4: "integracao_api",
5: "design_customizado",
6: "seo_otimizado"
};
const extras = numeros
.filter(n => n > 0 && n <= 6)
.map(n => mapeamento[n])
.filter(Boolean);
return { extras: extras };
}
},
ORCAMENTO_EXTRAS: {
pergunta: `⏰ **PRAZO DE ENTREGA**
Qual prazo você prefere?
1️⃣ Urgente (entrega prioritária) +50%
2️⃣ Normal (prazo padrão)
3️⃣ Flexível (posso aguardar) -20%
Digite o número:`,
proximo_estado: "ORCAMENTO_PRAZO",
validacao: (resposta) => {
const opcao = parseInt(resposta.trim());
return opcao >= 1 && opcao <= 3;
},
processar: (resposta) => {
const prazos = {
1: "urgente",
2: "normal",
3: "flexivel"
};
return { prazo: prazos[parseInt(resposta)] };
}
},
ORCAMENTO_PRAZO: {
pergunta: `🏢 **PORTE DA EMPRESA**
Qual o porte da sua empresa?
1️⃣ Startup/Pequena empresa
2️⃣ Empresa média
3️⃣ Grande empresa
Digite o número:`,
proximo_estado: "ORCAMENTO_CALCULAR",
validacao: (resposta) => {
const opcao = parseInt(resposta.trim());
return opcao >= 1 && opcao <= 3;
},
processar: (resposta) => {
const portes = {
1: "startup",
2: "media",
3: "grande"
};
return { porte_empresa: portes[parseInt(resposta)] };
}
}
};
// Processar pergunta do orçamento
function processarPerguntaOrcamento(estadoAtual, resposta, dadosTemp) {
const fluxo = fluxoOrcamento[estadoAtual];
if (!fluxo) {
return {
new_state: "MENU_PRINCIPAL",
response: "Erro no fluxo de orçamento. Vamos voltar ao menu.",
temp_data: {}
};
}
// Validar resposta
if (!fluxo.validacao(resposta)) {
return {
new_state: estadoAtual, // Permanecer no mesmo estado
response: `❌ Resposta inválida. \n\n${fluxo.pergunta}`,
temp_data: dadosTemp
};
}
// Processar resposta
const novosDados = fluxo.processar(resposta);
const dadosAtualizados = {
...dadosTemp,
respostas_orcamento: {
...dadosTemp.respostas_orcamento,
...novosDados
}
};
// Se chegou no final, calcular orçamento
if (fluxo.proximo_estado === "ORCAMENTO_CALCULAR") {
const resultado = processarOrcamento({
respostas_orcamento: dadosAtualizados.respostas_orcamento,
nome: dadosTemp.nome
});
return {
new_state: "MENU_PRINCIPAL",
response: resultado.message,
temp_data: {} // Limpar dados temporários
};
}
// Continuar para próxima pergunta
const proximoFluxo = fluxoOrcamento[fluxo.proximo_estado];
return {
new_state: fluxo.proximo_estado,
response: proximoFluxo.pergunta,
temp_data: dadosAtualizados
};
}
return { fluxoOrcamento, processarPerguntaOrcamento };📅 Sistema de Agendamento Automático
Um sistema que consulta agenda real, mostra horários disponíveis e confirma agendamentos. Integra com Google Calendar ou planilhas.
🗓️ Sistema Completo de Agendamento
// Sistema de agendamento com Google Sheets/Calendar
class SistemaAgendamento {
constructor() {
this.horarios_funcionamento = {
segunda: ['09:00', '10:00', '11:00', '14:00', '15:00', '16:00', '17:00'],
terca: ['09:00', '10:00', '11:00', '14:00', '15:00', '16:00', '17:00'],
quarta: ['09:00', '10:00', '11:00', '14:00', '15:00', '16:00', '17:00'],
quinta: ['09:00', '10:00', '11:00', '14:00', '15:00', '16:00', '17:00'],
sexta: ['09:00', '10:00', '11:00', '14:00', '15:00', '16:00', '17:00']
};
this.tipos_servico = {
consultoria: { duracao: 60, preco: 150 },
reuniao: { duracao: 30, preco: 0 },
demonstracao: { duracao: 45, preco: 0 },
suporte: { duracao: 30, preco: 80 }
};
}
// Buscar horários disponíveis
async buscarHorariosDisponiveis(data, tipoServico = 'consultoria') {
try {
// Converter data para objeto Date
const dataObj = new Date(data + 'T00:00:00');
const diaSemana = this.getDiaSemana(dataObj.getDay());
// Verificar se é dia útil
if (!this.horarios_funcionamento[diaSemana]) {
return {
success: false,
message: "Não atendemos neste dia da semana. Escolha de segunda a sexta."
};
}
// Verificar se não é passado
const hoje = new Date();
if (dataObj < hoje) {
return {
success: false,
message: "Não é possível agendar para datas passadas."
};
}
// Buscar agendamentos existentes
const agendamentosExistentes = await this.buscarAgendamentosData(data);
const horariosOcupados = agendamentosExistentes.map(a => a.horario);
// Filtrar horários disponíveis
const horariosLivres = this.horarios_funcionamento[diaSemana]
.filter(horario => !horariosOcupados.includes(horario));
if (horariosLivres.length === 0) {
return {
success: false,
message: `❌ Não há horários disponíveis em ${this.formatarData(dataObj)}.\n\nTente outra data ou digite "proximas" para ver próximas datas disponíveis.`
};
}
return {
success: true,
horarios: horariosLivres,
data: data,
tipo_servico: tipoServico,
message: this.formatarHorariosDisponiveis(dataObj, horariosLivres, tipoServico)
};
} catch (error) {
return {
success: false,
message: "Erro ao consultar agenda. Tente novamente."
};
}
}
// Confirmar agendamento
async confirmarAgendamento(dadosAgendamento) {
try {
const { data, horario, cliente, servico, observacoes } = dadosAgendamento;
// Verificar se horário ainda está disponível
const verificacao = await this.verificarDisponibilidade(data, horario);
if (!verificacao.disponivel) {
return {
success: false,
message: "❌ Horário não está mais disponível. Escolha outro horário."
};
}
// Salvar agendamento
const agendamento = {
id: this.gerarIdAgendamento(),
data: data,
horario: horario,
cliente_nome: cliente.nome,
cliente_telefone: cliente.telefone,
cliente_email: cliente.email || '',
tipo_servico: servico,
status: 'Confirmado',
observacoes: observacoes || '',
criado_em: new Date().toISOString(),
valor: this.tipos_servico[servico]?.preco || 0
};
await this.salvarAgendamento(agendamento);
// Enviar confirmação
return {
success: true,
agendamento: agendamento,
message: this.formatarConfirmacaoAgendamento(agendamento)
};
} catch (error) {
return {
success: false,
message: "Erro ao confirmar agendamento. Tente novamente."
};
}
}
// Buscar agendamentos de uma data
async buscarAgendamentosData(data) {
// Implementar busca no Google Sheets
const agendamentos = await googleSheets.read({
range: 'agendamentos!A:H',
filter: { data: data }
});
return agendamentos || [];
}
// Salvar agendamento
async salvarAgendamento(agendamento) {
await googleSheets.append({
range: 'agendamentos!A:H',
values: [Object.values(agendamento)]
});
// Opcional: Integrar com Google Calendar
// await this.adicionarGoogleCalendar(agendamento);
}
// Verificar disponibilidade de horário específico
async verificarDisponibilidade(data, horario) {
const agendamentos = await this.buscarAgendamentosData(data);
const ocupado = agendamentos.some(a => a.horario === horario);
return { disponivel: !ocupado };
}
// Formatar horários disponíveis
formatarHorariosDisponiveis(dataObj, horarios, tipoServico) {
const servico = this.tipos_servico[tipoServico];
let resposta = `📅 **HORÁRIOS DISPONÍVEIS**\n`;
resposta += `📆 **Data:** ${this.formatarData(dataObj)}\n`;
resposta += `⏱️ **Serviço:** ${this.getNomeServico(tipoServico)} (${servico.duracao}min)\n`;
if (servico.preco > 0) {
resposta += `💰 **Valor:** R$ ${servico.preco}\n`;
}
resposta += `\n🕐 **Horários livres:**\n`;
horarios.forEach((horario, index) => {
resposta += `${index + 1}️⃣ ${horario}\n`;
});
resposta += `\n━━━━━━━━━━━━━━━━━━━`;
resposta += `\nDigite o número do horário desejado.`;
return resposta;
}
// Formatar confirmação
formatarConfirmacaoAgendamento(agendamento) {
return `✅ **AGENDAMENTO CONFIRMADO!**
📅 **Data:** ${this.formatarData(new Date(agendamento.data))}
🕐 **Horário:** ${agendamento.horario}
👤 **Cliente:** ${agendamento.cliente_nome}
📞 **Telefone:** ${agendamento.cliente_telefone}
⚙️ **Serviço:** ${this.getNomeServico(agendamento.tipo_servico)}
🆔 **Código:** ${agendamento.id}
${agendamento.valor > 0 ? `💰 **Valor:** R$ ${agendamento.valor}` : ''}
📱 Você receberá lembrete 1 dia antes.
🔄 Para reagendar, digite "reagendar ${agendamento.id}"
Obrigado por escolher nossos serviços! 🙏`;
}
// Funções auxiliares
getDiaSemana(numerodia) {
const dias = ['domingo', 'segunda', 'terca', 'quarta', 'quinta', 'sexta', 'sabado'];
return dias[numerodia];
}
formatarData(dataObj) {
return dataObj.toLocaleDateString('pt-BR', {
weekday: 'long',
year: 'numeric',
month: 'long',
day: 'numeric'
});
}
getNomeServico(tipo) {
const nomes = {
consultoria: 'Consultoria Técnica',
reuniao: 'Reunião',
demonstracao: 'Demonstração',
suporte: 'Suporte Técnico'
};
return nomes[tipo] || tipo;
}
gerarIdAgendamento() {
return 'AGD' + Date.now().toString(36).toUpperCase();
}
}
// Usar no bot
const agendamento = new SistemaAgendamento();
// Exemplo de uso
return { SistemaAgendamento, agendamento };📋 Lead Capture Inteligente
Sistema que coleta leads de forma natural na conversa, qualifica automaticamente e integra com CRM/planilhas para follow-up.
🎯 Sistema de Qualificação de Leads
// Sistema de captura e qualificação de leads
class LeadCapture {
constructor() {
this.campos_obrigatorios = ['nome', 'telefone', 'email'];
this.campos_qualificacao = ['empresa', 'cargo', 'orcamento', 'prazo'];
this.scores_qualificacao = {
orcamento: {
'ate_5k': 1,
'5k_20k': 3,
'20k_50k': 5,
'acima_50k': 7
},
prazo: {
'urgente': 5,
'ate_30_dias': 4,
'ate_90_dias': 2,
'sem_pressa': 1
},
empresa: {
'startup': 2,
'pequena': 3,
'media': 4,
'grande': 5
}
};
}
// Iniciar captura de lead
iniciarCaptura(contexto) {
return {
new_state: 'LEAD_NOME',
response: `📝 **VAMOS COMEÇAR!**
Para enviar informações personalizadas, preciso de alguns dados:
👤 **Qual seu nome completo?**`,
temp_data: {
lead_context: contexto,
lead_data: {},
lead_step: 1,
total_steps: 6
}
};
}
// Processar cada etapa da captura
processarEtapaLead(estado, resposta, dadosTemp) {
const leadData = dadosTemp.lead_data || {};
switch (estado) {
case 'LEAD_NOME':
return this.processarNome(resposta, dadosTemp);
case 'LEAD_TELEFONE':
return this.processarTelefone(resposta, dadosTemp);
case 'LEAD_EMAIL':
return this.processarEmail(resposta, dadosTemp);
case 'LEAD_EMPRESA':
return this.processarEmpresa(resposta, dadosTemp);
case 'LEAD_ORCAMENTO':
return this.processarOrcamento(resposta, dadosTemp);
case 'LEAD_PRAZO':
return this.processarPrazo(resposta, dadosTemp);
case 'LEAD_FINALIZAR':
return this.finalizarLead(dadosTemp);
default:
return this.resetarCaptura();
}
}
processarNome(resposta, dadosTemp) {
const nome = resposta.trim();
if (nome.length < 2) {
return {
new_state: 'LEAD_NOME',
response: 'Por favor, digite seu nome completo:',
temp_data: dadosTemp
};
}
const novosDados = {
...dadosTemp,
lead_data: { ...dadosTemp.lead_data, nome: nome },
lead_step: 2
};
return {
new_state: 'LEAD_TELEFONE',
response: `Prazer, ${nome}! 👋
📞 **Agora seu telefone:** (apenas números)`,
temp_data: novosDados
};
}
processarTelefone(resposta, dadosTemp) {
const telefone = resposta.replace(/D/g, '');
if (telefone.length < 10 || telefone.length > 11) {
return {
new_state: 'LEAD_TELEFONE',
response: 'Telefone inválido. Digite apenas números:\nExemplo: 11999999999',
temp_data: dadosTemp
};
}
const novosDados = {
...dadosTemp,
lead_data: { ...dadosTemp.lead_data, telefone: telefone },
lead_step: 3
};
return {
new_state: 'LEAD_EMAIL',
response: `📧 **Seu email:**`,
temp_data: novosDados
};
}
processarEmail(resposta, dadosTemp) {
const email = resposta.trim().toLowerCase();
const emailRegex = /^[^s@]+@[^s@]+.[^s@]+$/;
if (!emailRegex.test(email)) {
return {
new_state: 'LEAD_EMAIL',
response: 'Email inválido. Digite um email válido:\nExemplo: seu@email.com',
temp_data: dadosTemp
};
}
const novosDados = {
...dadosTemp,
lead_data: { ...dadosTemp.lead_data, email: email },
lead_step: 4
};
return {
new_state: 'LEAD_EMPRESA',
response: `🏢 **Qual sua empresa/negócio?**
(Se for pessoa física, digite "pessoa física")`,
temp_data: novosDados
};
}
processarEmpresa(resposta, dadosTemp) {
const empresa = resposta.trim();
const novosDados = {
...dadosTemp,
lead_data: { ...dadosTemp.lead_data, empresa: empresa },
lead_step: 5
};
return {
new_state: 'LEAD_ORCAMENTO',
response: `💰 **Qual faixa de orçamento você tem em mente?**
1️⃣ Até R$ 5.000
2️⃣ R$ 5.000 - R$ 20.000
3️⃣ R$ 20.000 - R$ 50.000
4️⃣ Acima de R$ 50.000
5️⃣ Ainda não defini
Digite o número:`,
temp_data: novosDados
};
}
processarOrcamento(resposta, dadosTemp) {
const opcao = parseInt(resposta.trim());
if (opcao < 1 || opcao > 5) {
return {
new_state: 'LEAD_ORCAMENTO',
response: 'Digite um número de 1 a 5:',
temp_data: dadosTemp
};
}
const faixas = {
1: 'ate_5k',
2: '5k_20k',
3: '20k_50k',
4: 'acima_50k',
5: 'nao_definido'
};
const novosDados = {
...dadosTemp,
lead_data: { ...dadosTemp.lead_data, orcamento: faixas[opcao] },
lead_step: 6
};
return {
new_state: 'LEAD_PRAZO',
response: `⏰ **Qual o prazo ideal para o projeto?**
1️⃣ Urgente (até 30 dias)
2️⃣ Médio prazo (30-90 dias)
3️⃣ Longo prazo (3-6 meses)
4️⃣ Sem pressa
Digite o número:`,
temp_data: novosDados
};
}
processarPrazo(resposta, dadosTemp) {
const opcao = parseInt(resposta.trim());
if (opcao < 1 || opcao > 4) {
return {
new_state: 'LEAD_PRAZO',
response: 'Digite um número de 1 a 4:',
temp_data: dadosTemp
};
}
const prazos = {
1: 'urgente',
2: 'medio_prazo',
3: 'longo_prazo',
4: 'sem_pressa'
};
const novosDados = {
...dadosTemp,
lead_data: { ...dadosTemp.lead_data, prazo: prazos[opcao] }
};
return this.finalizarLead(novosDados);
}
// Finalizar captura e calcular score
async finalizarLead(dadosTemp) {
try {
const leadData = dadosTemp.lead_data;
// Calcular score de qualificação
const score = this.calcularScore(leadData);
// Adicionar metadados
const leadCompleto = {
...leadData,
score_qualificacao: score.total,
classificacao: score.classificacao,
fonte: 'whatsapp_bot',
data_captura: new Date().toISOString(),
contexto: dadosTemp.lead_context
};
// Salvar no sistema
await this.salvarLead(leadCompleto);
// Gerar resposta baseada no score
return {
new_state: 'MENU_PRINCIPAL',
response: this.gerarRespostaFinal(leadCompleto),
temp_data: {} // Limpar dados temporários
};
} catch (error) {
return {
new_state: 'MENU_PRINCIPAL',
response: "Erro ao processar dados. Nossa equipe entrará em contato pelo telefone informado.",
temp_data: {}
};
}
}
// Calcular score de qualificação
calcularScore(leadData) {
let score = 0;
// Score por orçamento
if (leadData.orcamento && this.scores_qualificacao.orcamento[leadData.orcamento]) {
score += this.scores_qualificacao.orcamento[leadData.orcamento];
}
// Score por prazo
if (leadData.prazo && this.scores_qualificacao.prazo[leadData.prazo]) {
score += this.scores_qualificacao.prazo[leadData.prazo];
}
// Score por empresa (baseado no nome)
if (leadData.empresa && leadData.empresa !== 'pessoa física') {
score += 2; // Bonus por ter empresa
}
// Classificação
let classificacao;
if (score >= 8) classificacao = 'HOT';
else if (score >= 5) classificacao = 'WARM';
else classificacao = 'COLD';
return { total: score, classificacao: classificacao };
}
async salvarLead(leadData) {
// Salvar no Google Sheets
await googleSheets.append({
range: 'leads!A:K',
values: [Object.values(leadData)]
});
// Opcional: Webhook para CRM
// await enviarParaCRM(leadData);
}
gerarRespostaFinal(lead) {
const classificacao = lead.classificacao;
let resposta = `✅ **DADOS REGISTRADOS COM SUCESSO!**
📝 Obrigado pelas informações, ${lead.nome}!
📊 **Status:** Lead ${classificacao}`;
if (classificacao === 'HOT') {
resposta += `
🔥 **ATENÇÃO PRIORITÁRIA!**
Nossa equipe comercial entrará em contato ainda hoje!
📞 Ligação em até 2 horas
📧 Proposta personalizada em 24h`;
} else if (classificacao === 'WARM') {
resposta += `
📞 Nossa equipe entrará em contato em até 24h
📧 Materiais personalizados serão enviados`;
} else {
resposta += `
📧 Materiais informativos foram enviados
📞 Contato em até 48h úteis`;
}
resposta += `
🎯 Enquanto isso, posso ajudar com mais alguma coisa?`;
return resposta;
}
resetarCaptura() {
return {
new_state: 'MENU_PRINCIPAL',
response: 'Ops! Algo deu errado na captura. Vamos voltar ao menu.',
temp_data: {}
};
}
}
return { LeadCapture };🧪 Testando seu Bot Conversacional
📋 Preparação: Checklist Pré-Teste
📝 Roteiro de Testes: Fluxos Principais
Vamos testar sistematicamente cada funcionalidade implementada, simulando diferentes tipos de usuários e cenários reais.
🎮 Teste 1: Menu e Navegação
Cenário: Primeiro contato
Cenário: Navegação por emoji
Cenário: Navegação por palavras
🧠 Teste 2: Estados de Conversa
Cenário: Fluxo de agendamento completo
Cenário: Interrupção e retomada
Cenário: Validação de dados
🔗 Teste 3: Integrações com APIs
Cenário: Consulta de CEP
Cenário: Consulta de clima (se implementado)
⚡ Teste 4: Performance e Confiabilidade
📊 Métricas de Performance
Latência de resposta
Meta: <5 segundos para consultas de API
Como testar: Cronometrar tempo entre envio e resposta
Taxa de sucesso
Como testar: Enviar 20 mensagens variadas
Verificar: Todas devem ter resposta, mesmo que seja erro
🔧 Testes de Stress
Múltiplas mensagens rápidas
Esperado: Todas devem ser processadas em ordem
Verificar: Estados não devem corromper
Mensagens inválidas
Esperado: Bot deve lidar graciosamente
Verificar: Nunca quebrar ou parar de responder
👥 Teste 5: Usuários Reais (Beta Testing)
O teste final é com pessoas reais. Convide amigos, colegas ou familiares para testarem sem orientação prévia.
📋 Protocolo de Beta Testing
Instruções para testadores:
- • "Converse com este bot como se fosse um atendente real"
- • "Tente diferentes tipos de pergunta"
- • "Se algo não funcionar, anote o que aconteceu"
- • "Teste por pelo menos 5 minutos"
O que observar:
- • Onde usuários se confundem
- • Quais perguntas fazem que bot não entende
- • Pontos de abandono da conversa
- • Feedback espontâneo (positivo/negativo)
📊 Métricas de UX
Taxa de Completação
Medir: Agendamentos, orçamentos, consultas
Tempo de Engajamento
Medir: Primeira a última mensagem
Satisfação
Medir: Pergunta direta no final
📈 Monitoramento Contínuo
Após o bot estar funcionando, implemente monitoramento para identificar problemas e oportunidades de melhoria.
📊 KPIs Essenciais
- • Mensagens/dia: Volume de uso
- • Taxa de erro: % execuções com falha
- • Comandos não reconhecidos: O que melhorar
- • Fluxos mais usados: Funcionalidades populares
- • Tempo médio de sessão: Engajamento
- • Taxa de conversão: Agendamentos/leads gerados
🔍 Ferramentas de Análise
- • N8N Executions: Logs técnicos detalhados
- • Google Sheets: Dados estruturados para análise
- • Google Analytics: Se integrado com site
- • Dashboard personalizado: KPIs em tempo real
- • Alertas automáticos: Quando algo quebra
🎯 Otimização Contínua
Regra de ouro: Analise dados semanalmente e faça pequenos ajustes. Bots que evoluem constantemente mantêm usuários engajados e geram mais valor.
✅ Checklist Final: Bot Pronto para Produção
🔧 Técnico
👥 UX/Negócio
🎉 Parabéns! Seu bot está pronto!
Você criou um sistema conversacional profissional que resolve problemas reais. Continue monitorando, otimizando e adicionando funcionalidades baseado no feedback dos usuários.
🎯 Conclusão: Seu Primeiro Bot Conversacional Está Pronto
Conquista Alcançada
Você acabou de transformar uma simples integração WhatsApp + N8N em um bot conversacional inteligente capaz de capturar leads, agendar reuniões e integrar APIs externas. Esse é o diferencial que separa automações básicas de soluções profissionais.
Nesta aula você construiu as fundações de um sistema conversacional completo. O bot que criamos vai além de respostas automáticas - ele entende contexto, mantém estado entre mensagens e oferece experiências personalizadas para cada usuário.
💡 Principais Conceitos Dominados
🧠 Inteligência Conversacional
- • Reconhecimento de intenções
- • Processamento de linguagem natural
- • Respostas contextuais
- • Fallbacks inteligentes
🔄 Gerenciamento de Estado
- • Persistência de conversas
- • Estados de usuário
- • Transições de contexto
- • Limpeza automática
🔌 Integrações Externas
- • APIs de localização
- • Serviços de clima
- • Agendamento automático
- • Cache inteligente
📊 Funcionalidades de Negócio
- • Captura de leads
- • Calculadora de orçamentos
- • Agendamento de reuniões
- • Análise de comportamento
📈 Métricas de Sucesso Esperadas
Próximos Passos
Na próxima aula vamos expandir significativamente as capacidades do seu bot com fluxos avançados. Você aprenderá a criar workflows complexos com múltiplas condições, integrações com CRM e automações de vendas completas.
Verificação Final do Bot
Desafio Extra
Teste Real: Convide 3 pessoas do seu círculo para testarem o bot. Observe como elas interagem naturalmente e anote quais funcionalidades são mais usadas. Esses insights serão valiosos para a próxima aula sobre otimização de fluxos.