cdp-edge 1.0.4 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/contracts/agent-versions.json +2 -2
- package/package.json +1 -1
- package/server-edge-tracker/index.ts +25 -0
- package/server-edge-tracker/modules/dispatch/crm.ts +116 -0
- package/server-edge-tracker/modules/dispatch/whatsapp.ts +17 -0
- package/server-edge-tracker/types.ts +5 -0
- package/server-edge-tracker/wrangler.toml +10 -7
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
**Padrão Quantum Tracking: 100% Cloudflare Edge.** Sem GTM. Sem Stape. Sem cookies de terceiros.
|
|
4
4
|
|
|
5
|
-
> **v2.5.
|
|
5
|
+
> **v2.5.4** — Auditoria de Dependências + Sync de Agentes + Fix D1 (25 de Abril de 2026) 🔧
|
|
6
6
|
|
|
7
7
|
---
|
|
8
8
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"_comment": "Fonte de verdade para versões dos agent files. Atualizar quando modules/ ou index.ts mudarem. Use scripts/validate-agents.js para detectar drifts.",
|
|
3
|
-
"worker_version": "2.5.
|
|
4
|
-
"worker_hash_date": "2026-04-
|
|
3
|
+
"worker_version": "2.5.4",
|
|
4
|
+
"worker_hash_date": "2026-04-25",
|
|
5
5
|
"agents": {
|
|
6
6
|
"master-orchestrator": {
|
|
7
7
|
"version": "2.0.7",
|
package/package.json
CHANGED
|
@@ -63,6 +63,9 @@ import {
|
|
|
63
63
|
processWhatsAppWebhook,
|
|
64
64
|
verifyHmac,
|
|
65
65
|
} from './modules/dispatch/whatsapp';
|
|
66
|
+
import {
|
|
67
|
+
notifyEvolutionForm,
|
|
68
|
+
} from './modules/dispatch/crm';
|
|
66
69
|
|
|
67
70
|
// ── ML — LTV + A/B Testing ────────────────────────────────────────────────────
|
|
68
71
|
import {
|
|
@@ -239,6 +242,9 @@ export default {
|
|
|
239
242
|
WA_NOTIFY_NUMBER: env.WA_NOTIFY_NUMBER ? 'set' : 'not set (optional - only for auto-reply)',
|
|
240
243
|
TIKTOK_ACCESS_TOKEN: env.TIKTOK_ACCESS_TOKEN ? 'set' : 'not set (optional)',
|
|
241
244
|
CALLMEBOT_PHONE: env.CALLMEBOT_PHONE ? 'set' : 'not set (optional)',
|
|
245
|
+
EVOLUTION_BASE_URL: env.EVOLUTION_BASE_URL ? 'set' : 'not set (optional - CRM Evolution)',
|
|
246
|
+
EVOLUTION_INSTANCE: env.EVOLUTION_INSTANCE ? 'set' : 'not set (optional - CRM Evolution)',
|
|
247
|
+
EVOLUTION_API_KEY: env.EVOLUTION_API_KEY ? 'set' : 'not set (optional - CRM Evolution)',
|
|
242
248
|
};
|
|
243
249
|
|
|
244
250
|
const hasMissing =
|
|
@@ -793,6 +799,25 @@ export default {
|
|
|
793
799
|
: []),
|
|
794
800
|
]);
|
|
795
801
|
|
|
802
|
+
// ── Notifica o Evolution CRM (formulários de cadastro) ────────────────
|
|
803
|
+
// Ativo para Lead e Contact vindos de formulários (quando tem telefone).
|
|
804
|
+
// Silencioso se EVOLUTION_BASE_URL / EVOLUTION_INSTANCE / EVOLUTION_API_KEY
|
|
805
|
+
// não estiverem configurados — zero impacto no pipeline principal.
|
|
806
|
+
const EVOLUTION_FORM_EVENTS = ['Lead', 'Contact', 'CompleteRegistration'];
|
|
807
|
+
if (EVOLUTION_FORM_EVENTS.includes(eventName) && trackPayload.phone) {
|
|
808
|
+
ctx.waitUntil(
|
|
809
|
+
notifyEvolutionForm(env, {
|
|
810
|
+
phone: trackPayload.phone,
|
|
811
|
+
name: [trackPayload.firstName, trackPayload.lastName].filter(Boolean).join(' ') || null,
|
|
812
|
+
email: trackPayload.email || null,
|
|
813
|
+
formName: trackPayload.contentName || trackPayload.productName || eventName,
|
|
814
|
+
utmSource: trackPayload.utmSource || null,
|
|
815
|
+
utmCampaign: trackPayload.utmCampaign || null,
|
|
816
|
+
pageUrl: trackPayload.pageUrl || null,
|
|
817
|
+
})
|
|
818
|
+
);
|
|
819
|
+
}
|
|
820
|
+
|
|
796
821
|
// Automação de mensagens
|
|
797
822
|
const AUTOMATION_EVENTS = ['Lead', 'Purchase', 'InitiateCheckout'];
|
|
798
823
|
if (AUTOMATION_EVENTS.includes(eventName) && env.DB) {
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CDP Edge — Evolution GO Integration
|
|
3
|
+
* Encaminha leads para o Evolution GO, que dispara o webhook para o EVO CRM.
|
|
4
|
+
*
|
|
5
|
+
* Secrets necessários (wrangler secret put):
|
|
6
|
+
* EVOLUTION_BASE_URL → URL base (ex: https://go.arkitekt.space)
|
|
7
|
+
* EVOLUTION_INSTANCE → Nome da instância (ex: Ramon)
|
|
8
|
+
* EVOLUTION_API_KEY → API Key do Evolution GO
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { Env } from '../../types.js';
|
|
12
|
+
|
|
13
|
+
export interface CTWALeadData {
|
|
14
|
+
phone: string;
|
|
15
|
+
messageBody?: string;
|
|
16
|
+
ctwaClid?: string | null;
|
|
17
|
+
adId?: string | null;
|
|
18
|
+
sourceUrl?: string | null;
|
|
19
|
+
headline?: string | null;
|
|
20
|
+
wamid?: string | null;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface FormLeadData {
|
|
24
|
+
phone: string;
|
|
25
|
+
name?: string | null;
|
|
26
|
+
email?: string | null;
|
|
27
|
+
formName?: string | null;
|
|
28
|
+
utmSource?: string | null;
|
|
29
|
+
utmCampaign?: string | null;
|
|
30
|
+
pageUrl?: string | null;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// ── Helpers ──────────────────────────────────────────────────────────────────
|
|
34
|
+
|
|
35
|
+
function isEvolutionConfigured(env: Env): boolean {
|
|
36
|
+
return !!(env.EVOLUTION_BASE_URL && env.EVOLUTION_INSTANCE && env.EVOLUTION_API_KEY);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function evolutionHeaders(env: Env): Record<string, string> {
|
|
40
|
+
return {
|
|
41
|
+
'Content-Type': 'application/json',
|
|
42
|
+
'apikey': env.EVOLUTION_API_KEY!,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function normalizePhone(phone: string): string {
|
|
47
|
+
const digits = phone.replace(/\D/g, '');
|
|
48
|
+
if (digits.startsWith('55') && (digits.length === 12 || digits.length === 13)) return digits;
|
|
49
|
+
if (digits.length === 10 || digits.length === 11) return `55${digits}`;
|
|
50
|
+
return digits;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// ── Envio via Evolution GO /send/text ────────────────────────────────────────
|
|
54
|
+
// POST {EVOLUTION_BASE_URL}/send/text
|
|
55
|
+
// Body: { id: instanceName, number, text, delay }
|
|
56
|
+
|
|
57
|
+
async function sendEvolutionText(env: Env, phone: string, text: string): Promise<void> {
|
|
58
|
+
const number = normalizePhone(phone);
|
|
59
|
+
try {
|
|
60
|
+
const res = await fetch(`${env.EVOLUTION_BASE_URL}/send/text`, {
|
|
61
|
+
method: 'POST',
|
|
62
|
+
headers: evolutionHeaders(env),
|
|
63
|
+
body: JSON.stringify({
|
|
64
|
+
id: env.EVOLUTION_INSTANCE,
|
|
65
|
+
number,
|
|
66
|
+
text,
|
|
67
|
+
delay: 0,
|
|
68
|
+
}),
|
|
69
|
+
});
|
|
70
|
+
if (!res.ok) {
|
|
71
|
+
const err = await res.text();
|
|
72
|
+
console.warn('[Evolution] sendText non-ok:', res.status, err.slice(0, 200));
|
|
73
|
+
}
|
|
74
|
+
} catch (err: any) {
|
|
75
|
+
console.warn('[Evolution] sendText failed (non-critical):', err?.message || String(err));
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// ── API Pública — CTWA ────────────────────────────────────────────────────────
|
|
80
|
+
|
|
81
|
+
export async function notifyEvolutionCTWA(env: Env, data: CTWALeadData): Promise<void> {
|
|
82
|
+
if (!isEvolutionConfigured(env)) return;
|
|
83
|
+
|
|
84
|
+
const { phone, messageBody, ctwaClid, adId, sourceUrl, headline } = data;
|
|
85
|
+
|
|
86
|
+
const linhas: string[] = [`🔔 *Novo Lead via WhatsApp (CTWA)*`, ``];
|
|
87
|
+
linhas.push(`📱 *Número:* ${phone}`);
|
|
88
|
+
if (messageBody) linhas.push(`💬 *Mensagem:* ${messageBody}`);
|
|
89
|
+
if (headline) linhas.push(`📢 *Anúncio:* ${headline}`);
|
|
90
|
+
if (sourceUrl) linhas.push(`🔗 *URL:* ${sourceUrl}`);
|
|
91
|
+
if (adId) linhas.push(`🆔 *Ad ID:* ${adId}`);
|
|
92
|
+
if (ctwaClid) linhas.push(`🧩 *CTWA ID:* ${ctwaClid}`);
|
|
93
|
+
linhas.push(``, `🕐 ${new Date().toLocaleString('pt-BR', { timeZone: 'America/Sao_Paulo' })}`);
|
|
94
|
+
|
|
95
|
+
await sendEvolutionText(env, phone, linhas.join('\n'));
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// ── API Pública — Formulário ──────────────────────────────────────────────────
|
|
99
|
+
|
|
100
|
+
export async function notifyEvolutionForm(env: Env, data: FormLeadData): Promise<void> {
|
|
101
|
+
if (!isEvolutionConfigured(env)) return;
|
|
102
|
+
|
|
103
|
+
const { phone, name, email, formName, utmSource, utmCampaign, pageUrl } = data;
|
|
104
|
+
|
|
105
|
+
const linhas: string[] = [`📋 *Novo Lead via Formulário*`, ``];
|
|
106
|
+
if (name) linhas.push(`👤 *Nome:* ${name}`);
|
|
107
|
+
if (email) linhas.push(`📧 *E-mail:* ${email}`);
|
|
108
|
+
linhas.push( `📱 *Telefone:* ${phone}`);
|
|
109
|
+
if (formName) linhas.push(`📝 *Formulário:* ${formName}`);
|
|
110
|
+
if (utmSource) linhas.push(`🔗 *Fonte:* ${utmSource}`);
|
|
111
|
+
if (utmCampaign) linhas.push(`📣 *Campanha:* ${utmCampaign}`);
|
|
112
|
+
if (pageUrl) linhas.push(`🌐 *Página:* ${pageUrl}`);
|
|
113
|
+
linhas.push(``, `🕐 ${new Date().toLocaleString('pt-BR', { timeZone: 'America/Sao_Paulo' })}`);
|
|
114
|
+
|
|
115
|
+
await sendEvolutionText(env, phone, linhas.join('\n'));
|
|
116
|
+
}
|
|
@@ -7,6 +7,7 @@ import { sha256, normalizePhone } from '../utils.js';
|
|
|
7
7
|
import { saveLead, logApiFailure } from '../db.js';
|
|
8
8
|
import { Env, TrackPayload } from '../../types.js';
|
|
9
9
|
import { ExecutionContext } from '@cloudflare/workers-types';
|
|
10
|
+
import { notifyEvolutionCTWA } from './crm.js';
|
|
10
11
|
|
|
11
12
|
// ── Tipos ───────────────────────────────────────────────────────────────────────
|
|
12
13
|
interface WhatsAppOptions {
|
|
@@ -248,6 +249,22 @@ export async function processWhatsAppWebhook(env: Env, body: any, request: Reque
|
|
|
248
249
|
}, request, 'whatsapp')
|
|
249
250
|
);
|
|
250
251
|
|
|
252
|
+
// ── Notifica o Evolution CRM sobre o novo lead CTWA ──────────────────────
|
|
253
|
+
// Cria/atualiza o contato no Evolution e abre a conversa para o vendedor.
|
|
254
|
+
// Silencioso se EVOLUTION_BASE_URL / EVOLUTION_INSTANCE / EVOLUTION_API_KEY
|
|
255
|
+
// não estiverem configurados.
|
|
256
|
+
ctx.waitUntil(
|
|
257
|
+
notifyEvolutionCTWA(env, {
|
|
258
|
+
phone: phoneNorm,
|
|
259
|
+
messageBody: messageBody || undefined,
|
|
260
|
+
ctwaClid,
|
|
261
|
+
adId,
|
|
262
|
+
sourceUrl,
|
|
263
|
+
headline,
|
|
264
|
+
wamid,
|
|
265
|
+
})
|
|
266
|
+
);
|
|
267
|
+
|
|
251
268
|
results.push({ ok: true, phone: phoneNorm.slice(0, 4) + '****', ctwa_clid: ctwaClid ? 'present' : 'absent', event_id: eventId });
|
|
252
269
|
}
|
|
253
270
|
|
|
@@ -64,6 +64,11 @@ export interface Env {
|
|
|
64
64
|
RESEND_API_KEY?: string;
|
|
65
65
|
RESEND_FROM_EMAIL?: string;
|
|
66
66
|
CALLMEBOT_APIKEY?: string;
|
|
67
|
+
|
|
68
|
+
// Evolution API CRM
|
|
69
|
+
EVOLUTION_BASE_URL?: string; // URL base do servidor Evolution (ex: https://evolution.suaempresa.com)
|
|
70
|
+
EVOLUTION_INSTANCE?: string; // Nome da instância WhatsApp no Evolution
|
|
71
|
+
EVOLUTION_API_KEY?: string; // Chave de autenticação da API Evolution
|
|
67
72
|
}
|
|
68
73
|
|
|
69
74
|
// ── Event Payload Types ───────────────────────────────────────────────────────
|
|
@@ -16,13 +16,13 @@ workers_dev = true
|
|
|
16
16
|
# pattern = "*.SEU_DOMINIO/track*"
|
|
17
17
|
# zone_name = "SEU_DOMINIO"
|
|
18
18
|
|
|
19
|
-
[[routes]]
|
|
20
|
-
pattern = "
|
|
21
|
-
zone_name = "
|
|
22
|
-
|
|
23
|
-
[[routes]]
|
|
24
|
-
pattern = "*.
|
|
25
|
-
zone_name = "
|
|
19
|
+
# [[routes]]
|
|
20
|
+
# pattern = "lancamentosabc.com.br/track*"
|
|
21
|
+
# zone_name = "lancamentosabc.com.br"
|
|
22
|
+
#
|
|
23
|
+
# [[routes]]
|
|
24
|
+
# pattern = "*.lancamentosabc.com.br/track*"
|
|
25
|
+
# zone_name = "lancamentosabc.com.br"
|
|
26
26
|
|
|
27
27
|
# ── Variáveis públicas (não são segredos) ─────────────────────────────────────
|
|
28
28
|
[vars]
|
|
@@ -134,3 +134,6 @@ head_sampling_rate = 1
|
|
|
134
134
|
# wrangler secret put LINKEDIN_AD_ACCOUNT_ID ← ID da conta de anúncios LinkedIn
|
|
135
135
|
# wrangler secret put SPOTIFY_ACCESS_TOKEN ← Bearer token Spotify Advertising API
|
|
136
136
|
# wrangler secret put SPOTIFY_AD_ACCOUNT_ID ← ID da conta de anúncios Spotify Ads
|
|
137
|
+
# wrangler secret put EVOLUTION_BASE_URL ← URL base do Evolution API (ex: https://evolution.suaempresa.com)
|
|
138
|
+
# wrangler secret put EVOLUTION_INSTANCE ← Nome da instância WhatsApp no Evolution
|
|
139
|
+
# wrangler secret put EVOLUTION_API_KEY ← Chave de autenticação do Evolution API
|