Pular para o conteúdo
Módulo 2Aula 3

Primeiro Bot Conversacional

Transformando webhook em inteligência conversacional

60 min
Intermediário

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

Aula 1: Introdução ao N8N
Aula 2: Webhooks
Aula 3: Bot Conversacional
Aula 4: Fluxos Avançados

🤖 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)

User:
"Oi"
Bot:
"Olá! Como posso ajudar?"
User:
"Quero comprar"
Bot:
"Interessante! Pode me contar mais?"

Resposta genérica. Não mantém contexto. Não ajuda realmente.

✅ Inteligência Conversacional (Hoje)

User:
"Oi"
Bot:
"Olá! 👋 Escolha uma opção:"
🛒 Produtos | 📞 Contato | ❓ Ajuda
User:
"🛒"
Bot:
"Ótimo! Temos 3 categorias disponíveis..."

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.

Valor: +200% engajamento

🧠 Estados de Conversa

Bot lembra onde parou a conversa e adapta respostas ao contexto atual.

Valor: UX fluida e natural

🔗 Integrações Externas

Consulta CEP, busca informações em APIs e integra com planilhas Google.

Valor: Funcionalidades reais

🛠️ 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

Economia: R$ 3.000/mês em atendentes
🎯 Qualificação de Leads

Coleta dados antes de enviar para vendedor

ROI: +40% conversão
📊 Automação de Processos

Agendamentos, consultas, formulários automáticos

Tempo: -70% trabalho manual

💡 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

Estado: INICIO
Bot: "Olá! Como posso ajudar?"
Estado: COLETANDO_NOME
Bot: "Qual seu nome?"
Estado: COLETANDO_DATA
Bot: "Qual data prefere?"

💡 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

Variações que usuários falam:
• "Quero comprar"
• "Quanto custa?"
• "Preço"
• "Valor"
• "🛒" (emoji)
→ Ação: Mostrar catálogo

📞 Intent: CONTATO

Variações que usuários falam:
• "Falar com humano"
• "Atendente"
• "Contato"
• "Suporte"
• "📞" (emoji)
→ Ação: Transferir para humano

❓ Intent: AJUDA

Variações que usuários falam:
• "Não entendi"
• "Help"
• "O que posso fazer?"
• "Menu"
• "❓" (emoji)
→ Ação: Mostrar menu principal

⚙️ 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):
{
  "from": "5511999999999",
  "current_state": "COLETANDO_NOME",
  "data": {
    "nome": "João",
    "servico": "agendamento"
  },
  "last_interaction": "2024-01-20T10:30:00"
}
Por conversa:
{
  "conversation_id": "abc123",
  "messages": [
    {"type": "user", "text": "oi"},
    {"type": "bot", "text": "Olá!"}
  ],
  "started_at": "2024-01-20T10:29:00"
}

🔄 Fluxo de processamento no N8N

1
Webhook recebe mensagem
2
Buscar estado atual do usuário (banco/memória)
3
Reconhecer intent da mensagem
4
Aplicar lógica baseada em (estado + intent)
5
Atualizar estado do usuário
6
Enviar resposta personalizada

💾 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)

Prós: Fácil, nativo do N8N
Contras: Perde dados se workflow reiniciar
Ideal para: Testes e protótipos

📊 Google Sheets (Intermediário)

Prós: Persistente, visualizável, fácil setup
Contras: Lento para muitas consultas
Ideal para: Até 100 usuários simultâneos

🗄️ Banco de Dados (Profissional)

Prós: Rápido, escalável, queries complexas
Contras: Requer setup adicional
Ideal para: Produção, milhares de usuários

🎯 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)

User:
"Oi"
Bot:
"Olá! Como posso ajudar?"
User:
"Não sei... o que vocês fazem?"
Bot:
"Oferecemos vários serviços..."

Usuário não sabe o que pode fazer. Conversa sem direção.

✅ Com menu (Conversa direcionada)

User:
"Oi"
Bot:
"Olá! 👋 Escolha uma opção:"
🛒 Ver produtos
📅 Agendar consulta
📞 Falar com atendente
❓ Ajuda
User:
"🛒"

Usuário sabe exatamente suas opções. Conversão imediata.

🔍 Anatomia de um Menu Eficaz

📐 Estrutura visual

Olá! 👋 Como posso ajudar?
━━━━━━━━━━━━━━━
🛒 Ver produtos
📅 Agendar serviço
💬 Falar com atendente
❓ Outras opções
━━━━━━━━━━━━━━━
Digite emoji ou palavra-chave

🎯 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

menu-principal.js
// 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

reconhecer-menu.js
// 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

submenu-produtos.js
// 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

navegacao-universal.js
// 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

menu-personalizado.js
// 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-contextual.js
// 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)

User:
"Qual CEP da Av. Paulista, 1000?"
Bot:
"Não tenho essa informação. Entre em contato com nosso atendimento."

Bot inútil. Usuário frustrado. Transferência desnecessária para humano.

✅ Bot com APIs (Poderoso)

User:
"Qual CEP da Av. Paulista, 1000?"
Bot:
"🔍 Consultando...
Bot:
"✅ CEP: 01310-100
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

Valor: Usuários amam essa funcionalidade

🌤️ OpenWeather (Free tier)

Previsão do tempo por cidade ou coordenadas

Valor: Engajamento e utility

📊 Google Sheets

Salvar leads, consultar estoque, preços dinâmicos

Valor: Integração com negócio real

📍 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

consulta-cep.js
// 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

consulta-clima.js
// 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

google-sheets-integration.js
// 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-cache.js
// 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

ROI: Qualifica leads automaticamente

📋 Formulário de Lead

Coleta dados estruturados e salva no CRM

ROI: -80% trabalho manual

⏰ Sistema de Agendamento

Agenda reuniões direto na conversa

ROI: +150% agendamentos

🧮 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

calculadora-orcamento.js
// 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-orcamento.js
// 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-agendamento.js
// 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

lead-capture.js
// 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
1. Envie: "oi"
Esperado: Menu principal com opções claras
2. Teste diferentes formas: "olá", "🤖", apenas emoji
Esperado: Todas devem gerar menu
Cenário: Navegação por emoji
1. Clique em cada emoji do menu
Esperado: Cada opção deve levar para submenu correto
2. Teste comandos de voltar: "menu", "voltar", "🏠"
Esperado: Sempre retornar ao menu principal
Cenário: Navegação por palavras
1. Digite: "produtos", "preço", "contato"
Esperado: Reconhecer intenção e direcionar corretamente
2. Teste variações: "precos", "produto", "falar com pessoa"
Esperado: Sistema flexível deve entender variações

🧠 Teste 2: Estados de Conversa

Cenário: Fluxo de agendamento completo
1. Acesse opção agendamento
2. Digite nome quando solicitado
3. Digite telefone válido
4. Digite email válido
5. Escolha data disponível
Esperado: Bot lembra todos os dados e gera confirmação
Cenário: Interrupção e retomada
1. Inicie agendamento
2. No meio do processo, digite "menu"
Esperado: Voltar ao menu e limpar estado
3. Reinicie agendamento
Esperado: Começar do zero, sem dados anteriores
Cenário: Validação de dados
1. Digite nome muito curto (ex: "a")
Esperado: Erro e nova solicitação
2. Digite telefone inválido
Esperado: Erro específico e exemplo
3. Digite email inválido
Esperado: Erro e formato correto

🔗 Teste 3: Integrações com APIs

Cenário: Consulta de CEP
1. Digite CEP válido: "01310-100"
Esperado: Endereço completo da Av. Paulista
2. Digite CEP inválido: "00000-000"
Esperado: Mensagem de erro clara
3. Digite endereço: "Av Paulista, São Paulo"
Esperado: Lista de CEPs encontrados
Cenário: Consulta de clima (se implementado)
1. Digite cidade válida: "São Paulo"
Esperado: Clima atual com temperatura e condições
2. Digite cidade inválida: "XyZabc"
Esperado: Erro e sugestão de formato

⚡ Teste 4: Performance e Confiabilidade

📊 Métricas de Performance

Latência de resposta
Meta: <2 segundos para respostas simples
Meta: <5 segundos para consultas de API
Como testar: Cronometrar tempo entre envio e resposta
Taxa de sucesso
Meta: 100% das mensagens recebem resposta
Como testar: Enviar 20 mensagens variadas
Verificar: Todas devem ter resposta, mesmo que seja erro

🔧 Testes de Stress

Múltiplas mensagens rápidas
Teste: Enviar 5 mensagens seguidas rapidamente
Esperado: Todas devem ser processadas em ordem
Verificar: Estados não devem corromper
Mensagens inválidas
Teste: Emojis estranhos, caracteres especiais
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
Meta: >80% completam pelo menos um fluxo
Medir: Agendamentos, orçamentos, consultas
Tempo de Engajamento
Meta: >3 minutos de conversa
Medir: Primeira a última mensagem
Satisfação
Meta: >4/5 na 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

85%
Taxa de Engajamento
60%
Redução Tempo Resposta
40%
Aumento Conversões

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

Bot responde corretamente ao menu principal
Estados de conversa são persistidos no Google Sheets
Integrações de API funcionam (CEP, clima, agenda)
Captura de leads está ativa e testada
Fallbacks funcionam para entradas não reconhecidas
Mensagens de erro são tratadas adequadamente
Tempo de resposta está abaixo de 3 segundos
Logs de monitoramento estão sendo gerados

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.