chore: import gti-maquina-comercial history via subtree
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> git-subtree-dir: gti-maquina-comercial git-subtree-mainline:93ca82979egit-subtree-split:b77484946f
This commit is contained in:
@@ -0,0 +1,411 @@
|
||||
const TARGET_CITY_ALIASES = new Set([
|
||||
'santo antonio de jesus',
|
||||
'santo antonio de jesus ba',
|
||||
'saj'
|
||||
]);
|
||||
|
||||
const TARGET_SEGMENTS = new Set([
|
||||
'mercado',
|
||||
'supermercado',
|
||||
'farmacia',
|
||||
'drogaria',
|
||||
'restaurante',
|
||||
'delivery',
|
||||
'distribuidora',
|
||||
'autopecas',
|
||||
'material de construcao',
|
||||
'clinica',
|
||||
'loja',
|
||||
'varejo',
|
||||
'atacado',
|
||||
'oficina',
|
||||
'escola',
|
||||
'salao de beleza',
|
||||
'barbearia',
|
||||
'estetica',
|
||||
'celular',
|
||||
'transportadora',
|
||||
'logistica'
|
||||
]);
|
||||
|
||||
const SEGMENT_STOPWORDS = new Set(['de', 'da', 'do', 'e']);
|
||||
|
||||
function segmentTokens(value = '') {
|
||||
return cleanText(value)
|
||||
.split(/[^a-z0-9]+/)
|
||||
.filter((token) => token.length > 2 && !SEGMENT_STOPWORDS.has(token));
|
||||
}
|
||||
|
||||
const TARGET_SEGMENT_TOKENS = new Set(
|
||||
[...TARGET_SEGMENTS].flatMap((term) => segmentTokens(term))
|
||||
);
|
||||
|
||||
function tokenMatches(token, targetToken) {
|
||||
return token === targetToken
|
||||
|| (token.length > 4 && targetToken.length > 4 && (token.startsWith(targetToken) || targetToken.startsWith(token)));
|
||||
}
|
||||
|
||||
function isTargetSegmentMatch(segmentKey) {
|
||||
if (!segmentKey) return false;
|
||||
if (TARGET_SEGMENTS.has(segmentKey)) return true;
|
||||
return segmentTokens(segmentKey).some((token) =>
|
||||
[...TARGET_SEGMENT_TOKENS].some((targetToken) => tokenMatches(token, targetToken))
|
||||
);
|
||||
}
|
||||
|
||||
const SERVICE_LABELS = {
|
||||
erp: 'ERP / sistema de gestao',
|
||||
ti: 'Gestao de TI',
|
||||
whatsapp: 'Autoatendimento para WhatsApp',
|
||||
indefinido: 'A definir'
|
||||
};
|
||||
|
||||
const URGENCY_LABELS = {
|
||||
alta: 'Alta',
|
||||
media: 'Media',
|
||||
baixa: 'Baixa',
|
||||
indefinida: 'Indefinida'
|
||||
};
|
||||
|
||||
export function stripAccents(value = '') {
|
||||
return String(value)
|
||||
.normalize('NFD')
|
||||
.replace(/[\u0300-\u036f]/g, '');
|
||||
}
|
||||
|
||||
export function cleanText(value = '') {
|
||||
return stripAccents(value)
|
||||
.toLowerCase()
|
||||
.replace(/[^a-z0-9\s@._+-]/g, ' ')
|
||||
.replace(/\s+/g, ' ')
|
||||
.trim();
|
||||
}
|
||||
|
||||
function firstDefined(...values) {
|
||||
return values.find((value) => value !== undefined && value !== null && value !== '') ?? '';
|
||||
}
|
||||
|
||||
function numberFrom(value) {
|
||||
if (value === undefined || value === null || value === '') return 0;
|
||||
if (typeof value === 'number' && Number.isFinite(value)) return value;
|
||||
const match = String(value).match(/\d+/);
|
||||
return match ? Number(match[0]) : 0;
|
||||
}
|
||||
|
||||
function toArray(value) {
|
||||
if (Array.isArray(value)) return value.filter(Boolean).map(String);
|
||||
if (!value) return [];
|
||||
return String(value)
|
||||
.split(/[,;|]/)
|
||||
.map((item) => item.trim())
|
||||
.filter(Boolean);
|
||||
}
|
||||
|
||||
export function normalizePhone(value = '') {
|
||||
let digits = String(value).replace(/\D/g, '');
|
||||
|
||||
while (digits.startsWith('0')) digits = digits.slice(1);
|
||||
|
||||
if (digits.length === 10 || digits.length === 11) {
|
||||
digits = `55${digits}`;
|
||||
}
|
||||
|
||||
return digits;
|
||||
}
|
||||
|
||||
export function detectService(value = '') {
|
||||
const text = cleanText(value);
|
||||
|
||||
if (!text) return 'indefinido';
|
||||
|
||||
if (/\b(1|erp|sistema|gestao empresarial|estoque|financeiro|fiscal|nfce|nfe|pdv)\b/.test(text)) {
|
||||
return 'erp';
|
||||
}
|
||||
|
||||
if (/\b(2|ti|suporte|servidor|rede|backup|seguranca|computador|infraestrutura)\b/.test(text)) {
|
||||
return 'ti';
|
||||
}
|
||||
|
||||
if (/\b(3|whatsapp|autoatendimento|chatbot|bot|atendimento automatico|mensagem)\b/.test(text)) {
|
||||
return 'whatsapp';
|
||||
}
|
||||
|
||||
return 'indefinido';
|
||||
}
|
||||
|
||||
export function normalizeUrgency(value = '') {
|
||||
const text = cleanText(value);
|
||||
|
||||
if (!text) return 'indefinida';
|
||||
if (/\b(alta|urgente|imediato|hoje|agora|essa semana|parado|problema serio|1)\b/.test(text)) return 'alta';
|
||||
if (/\b(media|medio|proximo mes|em breve|avaliando|2)\b/.test(text)) return 'media';
|
||||
if (/\b(baixa|sem pressa|futuro|pesquisa|so pesquisando|3)\b/.test(text)) return 'baixa';
|
||||
|
||||
return 'indefinida';
|
||||
}
|
||||
|
||||
export function normalizeCurrentSystem(value = '') {
|
||||
const text = cleanText(value);
|
||||
|
||||
if (!text) return 'nao_informado';
|
||||
if (/\b(trocar|insatisfeito|migrar|outro sistema|uso outro|4)\b/.test(text)) return 'quer_trocar';
|
||||
if (/\b(planilha|excel|manual|papel|3)\b/.test(text)) return 'planilhas';
|
||||
if (/\b(sim|tenho|uso sistema|ja uso|1)\b/.test(text)) return 'usa_sistema';
|
||||
if (/\b(nao|nenhum|nao tenho|sem sistema|2)\b/.test(text)) return 'sem_sistema';
|
||||
|
||||
return 'nao_informado';
|
||||
}
|
||||
|
||||
export function normalizeLead(input = {}) {
|
||||
const body = input.body && typeof input.body === 'object' ? input.body : input;
|
||||
const contact = body.contact ?? body.contato ?? {};
|
||||
const company = body.company ?? body.empresa_dados ?? {};
|
||||
const message = body.message ?? body.mensagem ?? body.text ?? body.texto ?? body.description ?? '';
|
||||
const serviceRaw = firstDefined(
|
||||
body.service,
|
||||
body.servico,
|
||||
body.interest,
|
||||
body.interesse,
|
||||
body.solution,
|
||||
body.solucao,
|
||||
message
|
||||
);
|
||||
|
||||
const city = firstDefined(company.city, company.cidade, body.city, body.cidade);
|
||||
const state = firstDefined(company.state, company.uf, body.state, body.uf, body.estado);
|
||||
const segment = firstDefined(company.segment, company.segmento, body.segment, body.segmento);
|
||||
const phone = normalizePhone(firstDefined(contact.phone, contact.whatsapp, body.phone, body.telefone, body.whatsapp, body.from));
|
||||
const name = firstDefined(contact.name, contact.nome, body.name, body.nome, body.contact_name, 'Lead sem nome');
|
||||
const email = firstDefined(contact.email, body.email);
|
||||
const companyName = firstDefined(company.name, company.nome, body.company_name, body.empresa, body.nome_empresa, 'Empresa nao informada');
|
||||
const pains = toArray(firstDefined(body.pains, body.dores, body.pain, body.dor, body.maior_dificuldade));
|
||||
const source = firstDefined(body.source, body.origem, body.canal, 'indefinida');
|
||||
const service = detectService(serviceRaw);
|
||||
const urgency = normalizeUrgency(firstDefined(body.urgency, body.urgencia, body.prazo, message));
|
||||
const currentSystem = normalizeCurrentSystem(firstDefined(body.current_system, body.sistema_atual, body.usa_sistema, body.sistema, message));
|
||||
const employees = numberFrom(firstDefined(company.employees, company.funcionarios, body.employees, body.funcionarios));
|
||||
const computers = numberFrom(firstDefined(company.computers, company.computadores, body.computers, body.computadores, body.usuarios));
|
||||
const whatsappVolume = numberFrom(firstDefined(body.whatsapp_volume, body.volume_whatsapp, body.mensagens_dia));
|
||||
const consent = Boolean(firstDefined(body.consent, body.consentimento, body.opt_in, body.lgpd, false));
|
||||
const cityKey = cleanText(city);
|
||||
const segmentKey = cleanText(segment);
|
||||
|
||||
return {
|
||||
id: body.id ?? body.lead_id ?? `GTI-${Date.now()}-${Math.random().toString(36).slice(2, 8).toUpperCase()}`,
|
||||
createdAt: body.created_at ?? body.createdAt ?? new Date().toISOString(),
|
||||
source: String(source || 'indefinida'),
|
||||
name: String(name),
|
||||
phone,
|
||||
email: String(email || ''),
|
||||
companyName: String(companyName),
|
||||
city: String(city || ''),
|
||||
state: String(state || ''),
|
||||
segment: String(segment || ''),
|
||||
service,
|
||||
serviceLabel: SERVICE_LABELS[service],
|
||||
urgency,
|
||||
urgencyLabel: URGENCY_LABELS[urgency],
|
||||
currentSystem,
|
||||
pains,
|
||||
message: String(message || ''),
|
||||
employees,
|
||||
computers,
|
||||
whatsappVolume,
|
||||
consent,
|
||||
isLocal: TARGET_CITY_ALIASES.has(cityKey) || (cityKey.includes('santo antonio') && cityKey.includes('jesus')),
|
||||
isTargetSegment: isTargetSegmentMatch(segmentKey),
|
||||
raw: body
|
||||
};
|
||||
}
|
||||
|
||||
export function prioritizeLead(lead) {
|
||||
const hasWhatsapp = Boolean(lead.phone);
|
||||
|
||||
return {
|
||||
priority: hasWhatsapp ? 'com_whatsapp' : 'sem_whatsapp',
|
||||
hasWhatsapp,
|
||||
reasons: hasWhatsapp ? ['Possui WhatsApp/telefone para contato'] : []
|
||||
};
|
||||
}
|
||||
|
||||
export function buildNextQuestion(lead) {
|
||||
if (lead.service === 'indefinido') {
|
||||
return [
|
||||
'Para te direcionar melhor, escolha uma opcao:',
|
||||
'1 - Sistema ERP / gestao empresarial',
|
||||
'2 - Suporte e gestao de TI',
|
||||
'3 - Autoatendimento para WhatsApp',
|
||||
'4 - Ainda nao sei, preciso de orientacao'
|
||||
].join('\n');
|
||||
}
|
||||
|
||||
if (lead.service === 'erp') {
|
||||
if (!lead.segment) {
|
||||
return [
|
||||
'Qual o segmento da sua empresa?',
|
||||
'1 - Comercio / loja',
|
||||
'2 - Restaurante / delivery',
|
||||
'3 - Mercado / supermercado',
|
||||
'4 - Distribuidora',
|
||||
'5 - Servicos',
|
||||
'6 - Outro'
|
||||
].join('\n');
|
||||
}
|
||||
|
||||
if (lead.currentSystem === 'nao_informado') {
|
||||
return [
|
||||
'Hoje voce ja usa algum sistema?',
|
||||
'1 - Sim',
|
||||
'2 - Nao',
|
||||
'3 - Uso planilhas',
|
||||
'4 - Uso outro sistema, mas quero trocar'
|
||||
].join('\n');
|
||||
}
|
||||
|
||||
if (!lead.pains.length) {
|
||||
return [
|
||||
'Qual sua maior dificuldade hoje?',
|
||||
'1 - Controle financeiro',
|
||||
'2 - Estoque',
|
||||
'3 - Vendas',
|
||||
'4 - Emissao fiscal',
|
||||
'5 - Relatorios',
|
||||
'6 - Tudo muito manual'
|
||||
].join('\n');
|
||||
}
|
||||
}
|
||||
|
||||
if (lead.service === 'ti') {
|
||||
if (!lead.computers) {
|
||||
return [
|
||||
'Sua empresa possui quantos computadores ou usuarios?',
|
||||
'1 - Ate 5',
|
||||
'2 - 6 a 15',
|
||||
'3 - 16 a 30',
|
||||
'4 - Mais de 30'
|
||||
].join('\n');
|
||||
}
|
||||
|
||||
if (!lead.pains.length) {
|
||||
return [
|
||||
'Qual sua maior necessidade de TI?',
|
||||
'1 - Suporte tecnico',
|
||||
'2 - Servidores',
|
||||
'3 - Rede / internet',
|
||||
'4 - Backup',
|
||||
'5 - Seguranca',
|
||||
'6 - Terceirizar o TI'
|
||||
].join('\n');
|
||||
}
|
||||
}
|
||||
|
||||
if (lead.service === 'whatsapp') {
|
||||
if (!lead.pains.length) {
|
||||
return [
|
||||
'Voce quer automatizar qual tipo de atendimento?',
|
||||
'1 - Vendas',
|
||||
'2 - Suporte',
|
||||
'3 - Agendamento',
|
||||
'4 - Cardapio / pedidos',
|
||||
'5 - Triagem de mensagens',
|
||||
'6 - Financeiro / segunda via'
|
||||
].join('\n');
|
||||
}
|
||||
|
||||
if (!lead.whatsappVolume) {
|
||||
return [
|
||||
'Em media, quantas mensagens chegam no WhatsApp da empresa por dia?',
|
||||
'1 - Ate 20',
|
||||
'2 - 21 a 50',
|
||||
'3 - 51 a 100',
|
||||
'4 - Mais de 100'
|
||||
].join('\n');
|
||||
}
|
||||
}
|
||||
|
||||
if (lead.urgency === 'indefinida') {
|
||||
return [
|
||||
'Qual a urgencia para resolver isso?',
|
||||
'1 - Alta, preciso resolver agora',
|
||||
'2 - Media, quero avaliar para breve',
|
||||
'3 - Baixa, estou pesquisando'
|
||||
].join('\n');
|
||||
}
|
||||
|
||||
return [
|
||||
'Pelo que voce informou, faz sentido um diagnostico rapido com a GTI.',
|
||||
'Escolha o melhor horario:',
|
||||
'1 - Hoje pela manha',
|
||||
'2 - Hoje a tarde',
|
||||
'3 - Amanha pela manha',
|
||||
'4 - Amanha a tarde'
|
||||
].join('\n');
|
||||
}
|
||||
|
||||
export function buildInternalWhatsAppMessage(lead, priorityInfo) {
|
||||
const pains = lead.pains.length ? lead.pains.join(', ') : 'Nao informado';
|
||||
const phone = lead.phone || 'Nao informado';
|
||||
const priority = priorityInfo.priority === 'com_whatsapp' ? 'COM WHATSAPP' : 'SEM WHATSAPP';
|
||||
|
||||
return [
|
||||
`Novo lead captado - ${priority}`,
|
||||
'',
|
||||
`Empresa: ${lead.companyName}`,
|
||||
`Contato: ${lead.name}`,
|
||||
`WhatsApp: ${phone}`,
|
||||
`Cidade/UF: ${lead.city || 'Nao informada'}${lead.state ? `/${lead.state}` : ''}`,
|
||||
`Segmento: ${lead.segment || 'Nao informado'}`,
|
||||
`Origem: ${lead.source}`,
|
||||
`Dores: ${pains}`,
|
||||
'',
|
||||
'Acao recomendada: tratar manualmente pela equipe GTI.'
|
||||
].join('\n');
|
||||
}
|
||||
|
||||
export function buildCrmRecord(lead, priorityInfo) {
|
||||
return {
|
||||
lead_id: lead.id,
|
||||
data_criacao: lead.createdAt,
|
||||
status: 'Captado',
|
||||
prioridade: priorityInfo.priority,
|
||||
tem_whatsapp: Boolean(lead.phone),
|
||||
origem: lead.source,
|
||||
servico: lead.service,
|
||||
servico_label: lead.serviceLabel,
|
||||
empresa: lead.companyName,
|
||||
contato: lead.name,
|
||||
whatsapp: lead.phone,
|
||||
email: lead.email,
|
||||
cidade: lead.city,
|
||||
uf: lead.state,
|
||||
segmento: lead.segment,
|
||||
urgencia: lead.urgency,
|
||||
sistema_atual: lead.currentSystem,
|
||||
dores: lead.pains.join('; '),
|
||||
funcionarios: lead.employees,
|
||||
computadores: lead.computers,
|
||||
volume_whatsapp: lead.whatsappVolume,
|
||||
consentimento: lead.consent,
|
||||
proxima_acao: 'Tratamento manual',
|
||||
observacao: lead.message
|
||||
};
|
||||
}
|
||||
|
||||
export function processLead(input = {}, options = {}) {
|
||||
const lead = normalizeLead(input);
|
||||
const priority = prioritizeLead(lead);
|
||||
const commercialWhatsApp = options.commercialWhatsApp || process.env.GTI_COMMERCIAL_WHATSAPP || '{{GTI_COMMERCIAL_WHATSAPP}}';
|
||||
|
||||
return {
|
||||
lead,
|
||||
priority,
|
||||
crmRecord: buildCrmRecord(lead, priority),
|
||||
whatsapp: {
|
||||
internal: {
|
||||
to: commercialWhatsApp,
|
||||
message: buildInternalWhatsAppMessage(lead, priority)
|
||||
}
|
||||
},
|
||||
nextAction: 'tratamento_manual'
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user