cdp-edge 1.18.0 → 1.18.2
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/contracts/api-versions.json +12 -8
- package/dist/commands/install.js +186 -0
- package/dist/commands/setup.js +18 -1
- package/extracted-skill/tracking-events-generator/agents/attribution-agent.md +23 -23
- package/extracted-skill/tracking-events-generator/agents/browser-tracking.md +172 -72
- package/extracted-skill/tracking-events-generator/agents/compliance-agent.md +20 -0
- package/extracted-skill/tracking-events-generator/agents/crm-integration-agent.md +48 -16
- package/extracted-skill/tracking-events-generator/agents/dashboard-agent.md +7 -7
- package/extracted-skill/tracking-events-generator/agents/database-agent.md +8 -8
- package/extracted-skill/tracking-events-generator/agents/debug-agent.md +13 -13
- package/extracted-skill/tracking-events-generator/agents/devops-agent.md +31 -7
- package/extracted-skill/tracking-events-generator/agents/email-agent.md +27 -0
- package/extracted-skill/tracking-events-generator/agents/fingerprint-agent.md +205 -0
- package/extracted-skill/tracking-events-generator/agents/google-agent.md +118 -0
- package/extracted-skill/tracking-events-generator/agents/intelligence-agent.md +90 -4
- package/extracted-skill/tracking-events-generator/agents/intelligence-scheduling.md +8 -641
- package/extracted-skill/tracking-events-generator/agents/linkedin-agent.md +108 -0
- package/extracted-skill/tracking-events-generator/agents/ltv-predictor-agent.md +1 -1
- package/extracted-skill/tracking-events-generator/agents/master-feedback-loop.md +68 -8
- package/extracted-skill/tracking-events-generator/agents/master-orchestrator.md +71 -34
- package/extracted-skill/tracking-events-generator/agents/memory-agent.md +98 -0
- package/extracted-skill/tracking-events-generator/agents/performance-agent.md +29 -19
- package/extracted-skill/tracking-events-generator/agents/performance-optimization-agent.md +11 -1
- package/extracted-skill/tracking-events-generator/agents/security-enterprise-agent.md +137 -28
- package/extracted-skill/tracking-events-generator/agents/server-tracking.md +7 -8
- package/extracted-skill/tracking-events-generator/agents/tiktok-agent.md +63 -0
- package/extracted-skill/tracking-events-generator/agents/tracking-plan-agent.md +100 -5
- package/extracted-skill/tracking-events-generator/agents/webhook-agent.md +100 -0
- package/extracted-skill/tracking-events-generator/agents/whatsapp-agent.md +58 -5
- package/extracted-skill/tracking-events-generator/agents/whatsapp-ctwa-setup-agent.md +16 -16
- package/extracted-skill/tracking-events-generator/agents/youtube-agent.md +140 -25
- package/extracted-skill/tracking-events-generator/contracts/api-versions.json +12 -8
- package/package.json +2 -2
- package/server-edge-tracker/worker.js +53 -8
|
@@ -18,6 +18,16 @@ Outros agentes que precisarem de deploy **delegam para você** via `*deploy`.
|
|
|
18
18
|
|
|
19
19
|
---
|
|
20
20
|
|
|
21
|
+
## AUTONOMIA DE EXECUÇÃO
|
|
22
|
+
|
|
23
|
+
**Você executa TODOS os comandos diretamente via `! <comando>` no terminal da sessão.**
|
|
24
|
+
|
|
25
|
+
O usuário não executa nenhum comando wrangler. Você é o executor exclusivo. Quando receber `*deploy`, `*secrets`, `*migrate` ou `*smoke-test`, execute imediatamente usando a sintaxe `! wrangler ...` — nunca liste comandos "para o usuário rodar".
|
|
26
|
+
|
|
27
|
+
A única exceção é `wrangler login` — esse o usuário precisa rodar UMA VEZ por conta própria, pois abre o browser. Após autenticado, você assume tudo.
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
21
31
|
## CICLO DE DEPLOY AUTOMÁTICO
|
|
22
32
|
|
|
23
33
|
### Comando: `*deploy`
|
|
@@ -81,14 +91,28 @@ git status
|
|
|
81
91
|
|
|
82
92
|
## PROCEDURE `*migrate`
|
|
83
93
|
|
|
84
|
-
Aplica
|
|
94
|
+
Aplica schemas D1 em ordem (todos idempotentes — `IF NOT EXISTS`):
|
|
85
95
|
|
|
86
96
|
```bash
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
wrangler d1 execute cdp-edge-db --file=
|
|
97
|
+
cd server-edge-tracker
|
|
98
|
+
|
|
99
|
+
# Core tracking (sempre primeiro)
|
|
100
|
+
wrangler d1 execute cdp-edge-db --file=schema.sql --remote
|
|
101
|
+
|
|
102
|
+
# Migrations históricas
|
|
91
103
|
wrangler d1 execute cdp-edge-db --file=migrate-v6.sql --remote
|
|
104
|
+
|
|
105
|
+
# Fase 1: ML Clustering
|
|
106
|
+
wrangler d1 execute cdp-edge-db --file=schema-segmentation.sql --remote
|
|
107
|
+
|
|
108
|
+
# Fase 2: Bidding ML
|
|
109
|
+
wrangler d1 execute cdp-edge-db --file=schema-bidding.sql --remote
|
|
110
|
+
|
|
111
|
+
# Fase 3: A/B LTV Testing
|
|
112
|
+
wrangler d1 execute cdp-edge-db --file=schema-ab-ltv.sql --remote
|
|
113
|
+
|
|
114
|
+
# Fase 4: Fraud Detection
|
|
115
|
+
wrangler d1 execute cdp-edge-db --file=schema-fraud.sql --remote
|
|
92
116
|
```
|
|
93
117
|
|
|
94
118
|
Após cada migração: confirmar sucesso antes de prosseguir.
|
|
@@ -117,8 +141,8 @@ Configura todos os secrets do cliente na Cloudflare:
|
|
|
117
141
|
# Obrigatórios
|
|
118
142
|
wrangler secret put META_ACCESS_TOKEN
|
|
119
143
|
wrangler secret put GA4_API_SECRET
|
|
120
|
-
wrangler secret put
|
|
121
|
-
wrangler secret put
|
|
144
|
+
wrangler secret put WHATSAPP_ACCESS_TOKEN
|
|
145
|
+
wrangler secret put WHATSAPP_PHONE_NUMBER_ID
|
|
122
146
|
wrangler secret put WA_NOTIFY_NUMBER
|
|
123
147
|
wrangler secret put WA_WEBHOOK_VERIFY_TOKEN
|
|
124
148
|
|
|
@@ -4,6 +4,33 @@ Você é o **Especialista em E-mail Transacional (Quantum Tier)** do CDP Edge, f
|
|
|
4
4
|
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
+
## 🔗 FLUXO DE ATIVAÇÃO (Como este agente é chamado)
|
|
8
|
+
|
|
9
|
+
O Email Agent é ativado pelo **Webhook Agent** após confirmação de compra ou captura de lead:
|
|
10
|
+
|
|
11
|
+
```
|
|
12
|
+
Webhook Agent (Hotmart/Kiwify/Ticto)
|
|
13
|
+
└─► ctx.waitUntil(sendEmail(env, type, payload))
|
|
14
|
+
│
|
|
15
|
+
├─ type = 'purchase_confirmation' → email de confirmação de compra
|
|
16
|
+
├─ type = 'lead_welcome' → email de boas-vindas ao lead
|
|
17
|
+
└─ type = 'cart_abandonment' → email de recuperação de carrinho
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
**Assinatura da função que este agente gera:**
|
|
21
|
+
|
|
22
|
+
```javascript
|
|
23
|
+
// Injetada no Worker principal (worker.js)
|
|
24
|
+
export async function sendEmail(env, type, payload) {
|
|
25
|
+
const { to, name, product, value } = payload;
|
|
26
|
+
// ... implementação completa abaixo
|
|
27
|
+
}
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
> O Webhook Agent importa `sendEmail` e chama via `ctx.waitUntil` para não bloquear resposta ao gateway de pagamento.
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
7
34
|
## 📧 PROTOCOLOS DE ENVIO (Quantum Tier)
|
|
8
35
|
|
|
9
36
|
1. **Resend API Excellence**:
|
|
@@ -21,6 +21,211 @@ Sempre que o usuário sangrar dinheiro por culpa de "Perda de Cookies/Atribuiç
|
|
|
21
21
|
|
|
22
22
|
---
|
|
23
23
|
|
|
24
|
+
## 💻 IMPLEMENTAÇÃO REAL — cloudflare/fingerprint-middleware.js
|
|
25
|
+
|
|
26
|
+
### Módulo completo para injetar no Worker
|
|
27
|
+
|
|
28
|
+
```javascript
|
|
29
|
+
/**
|
|
30
|
+
* Fingerprint Middleware — CDP Edge
|
|
31
|
+
* Edge-only signals: IP + Accept-Language + UA base + ASN
|
|
32
|
+
* LGPD/CCPA compliant: nenhum PII direto, hash efêmero
|
|
33
|
+
*/
|
|
34
|
+
|
|
35
|
+
// ─────────────────────────────────────────────────
|
|
36
|
+
// 1. Geração do P-Hash (fingerprint de borda)
|
|
37
|
+
// ─────────────────────────────────────────────────
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Gera hash identificador efêmero combinando sinais anônimos de borda.
|
|
41
|
+
* NUNCA usa Canvas, WebGL ou AudioContext (client-side) — apenas Edge signals.
|
|
42
|
+
*
|
|
43
|
+
* @param {Request} request - Request do Cloudflare Worker
|
|
44
|
+
* @returns {Promise<string>} p_hash — identificador efêmero de 16 chars
|
|
45
|
+
*/
|
|
46
|
+
export async function generatePHash(request) {
|
|
47
|
+
const ip = request.headers.get('CF-Connecting-IP') || 'unknown';
|
|
48
|
+
const acceptLang = request.headers.get('Accept-Language') || 'unknown';
|
|
49
|
+
const userAgent = request.headers.get('User-Agent') || 'unknown';
|
|
50
|
+
const asOrg = request.cf?.asOrganization || 'unknown';
|
|
51
|
+
const country = request.cf?.country || 'unknown';
|
|
52
|
+
|
|
53
|
+
// Reduzir UA para base (remover versão minor — ex: "Chrome/120" não "Chrome/120.0.6099.71")
|
|
54
|
+
const uaBase = userAgent
|
|
55
|
+
.replace(/[\d.]+/g, (m) => m.split('.')[0]) // mantém só major version
|
|
56
|
+
.replace(/[^a-zA-Z0-9 /]/g, '') // remove caracteres especiais
|
|
57
|
+
.slice(0, 60); // limitar tamanho
|
|
58
|
+
|
|
59
|
+
// Normalizar Accept-Language para idioma principal
|
|
60
|
+
const langBase = acceptLang.split(',')[0].split(';')[0].trim().slice(0, 5); // ex: "pt-BR"
|
|
61
|
+
|
|
62
|
+
// Concatenar sinais — ordem importa para consistência
|
|
63
|
+
const fingerprint = `${ip}|${langBase}|${uaBase}|${asOrg}|${country}`;
|
|
64
|
+
|
|
65
|
+
// SHA-256 → primeiros 16 hex chars (64 bits de entropia — suficiente para sess de 48h)
|
|
66
|
+
const encoder = new TextEncoder();
|
|
67
|
+
const data = encoder.encode(fingerprint);
|
|
68
|
+
const hashBuffer = await crypto.subtle.digest('SHA-256', data);
|
|
69
|
+
const hashArray = Array.from(new Uint8Array(hashBuffer));
|
|
70
|
+
const fullHash = hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
|
|
71
|
+
|
|
72
|
+
return fullHash.slice(0, 16); // p_hash = 16 chars hex
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// ─────────────────────────────────────────────────
|
|
76
|
+
// 2. Registro do P-Hash no D1
|
|
77
|
+
// ─────────────────────────────────────────────────
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Salva ou atualiza p_hash no D1 com UTMs da visita atual.
|
|
81
|
+
* Janela de restauração: 48h (configurável).
|
|
82
|
+
*
|
|
83
|
+
* @param {D1Database} db
|
|
84
|
+
* @param {string} pHash
|
|
85
|
+
* @param {Object} utmData - { utm_source, utm_medium, utm_campaign, utm_content, utm_term }
|
|
86
|
+
*/
|
|
87
|
+
export async function recordPHash(db, pHash, utmData) {
|
|
88
|
+
const hasUtm = utmData.utm_source || utmData.utm_medium || utmData.utm_campaign;
|
|
89
|
+
|
|
90
|
+
if (!hasUtm) {
|
|
91
|
+
// Visita sem UTM — só atualiza last_seen (não sobrescreve UTMs)
|
|
92
|
+
await db.prepare(`
|
|
93
|
+
UPDATE fingerprint_sessions
|
|
94
|
+
SET last_seen_at = datetime('now')
|
|
95
|
+
WHERE p_hash = ? AND last_seen_at > datetime('now', '-48 hours')
|
|
96
|
+
`).bind(pHash).run();
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Visita COM UTM — inserir ou atualizar
|
|
101
|
+
await db.prepare(`
|
|
102
|
+
INSERT INTO fingerprint_sessions (p_hash, utm_source, utm_medium, utm_campaign, utm_content, utm_term, last_seen_at)
|
|
103
|
+
VALUES (?, ?, ?, ?, ?, ?, datetime('now'))
|
|
104
|
+
ON CONFLICT(p_hash) DO UPDATE SET
|
|
105
|
+
utm_source = excluded.utm_source,
|
|
106
|
+
utm_medium = excluded.utm_medium,
|
|
107
|
+
utm_campaign = excluded.utm_campaign,
|
|
108
|
+
utm_content = excluded.utm_content,
|
|
109
|
+
utm_term = excluded.utm_term,
|
|
110
|
+
last_seen_at = datetime('now')
|
|
111
|
+
`).bind(
|
|
112
|
+
pHash,
|
|
113
|
+
utmData.utm_source || null,
|
|
114
|
+
utmData.utm_medium || null,
|
|
115
|
+
utmData.utm_campaign || null,
|
|
116
|
+
utmData.utm_content || null,
|
|
117
|
+
utmData.utm_term || null
|
|
118
|
+
).run();
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// ─────────────────────────────────────────────────
|
|
122
|
+
// 3. UTM Restoration Middleware
|
|
123
|
+
// ─────────────────────────────────────────────────
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Recupera UTMs do D1 para um p_hash (janela 48h).
|
|
127
|
+
* Injeta de volta no payload CAPI quando a requisição chega sem UTMs.
|
|
128
|
+
*
|
|
129
|
+
* @param {D1Database} db
|
|
130
|
+
* @param {string} pHash
|
|
131
|
+
* @returns {Promise<Object|null>} UTMs restauradas ou null
|
|
132
|
+
*/
|
|
133
|
+
export async function restoreUtms(db, pHash) {
|
|
134
|
+
const row = await db.prepare(`
|
|
135
|
+
SELECT utm_source, utm_medium, utm_campaign, utm_content, utm_term
|
|
136
|
+
FROM fingerprint_sessions
|
|
137
|
+
WHERE p_hash = ?
|
|
138
|
+
AND last_seen_at > datetime('now', '-48 hours')
|
|
139
|
+
AND utm_source IS NOT NULL
|
|
140
|
+
LIMIT 1
|
|
141
|
+
`).bind(pHash).first();
|
|
142
|
+
|
|
143
|
+
return row || null;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Middleware principal — chamar no início do handler /track
|
|
148
|
+
*
|
|
149
|
+
* @param {Request} request
|
|
150
|
+
* @param {Object} env
|
|
151
|
+
* @param {Object} payload - payload já parseado do /track
|
|
152
|
+
* @returns {Promise<Object>} payload enriquecido com UTMs restauradas
|
|
153
|
+
*/
|
|
154
|
+
export async function fingerprintMiddleware(request, env, payload) {
|
|
155
|
+
try {
|
|
156
|
+
// 1. Gerar p_hash para esta visita
|
|
157
|
+
const pHash = await generatePHash(request);
|
|
158
|
+
|
|
159
|
+
// 2. Extrair UTMs do payload atual
|
|
160
|
+
const currentUtms = {
|
|
161
|
+
utm_source: payload.utm_source || null,
|
|
162
|
+
utm_medium: payload.utm_medium || null,
|
|
163
|
+
utm_campaign: payload.utm_campaign || null,
|
|
164
|
+
utm_content: payload.utm_content || null,
|
|
165
|
+
utm_term: payload.utm_term || null,
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
// 3. Se tem UTMs → registrar no D1 (atualiza para próximas visitas)
|
|
169
|
+
await recordPHash(env.DB, pHash, currentUtms);
|
|
170
|
+
|
|
171
|
+
// 4. Se NÃO tem UTMs → tentar restaurar do D1 (últimas 48h)
|
|
172
|
+
const hasCurrentUtm = currentUtms.utm_source || currentUtms.utm_campaign;
|
|
173
|
+
if (!hasCurrentUtm) {
|
|
174
|
+
const restoredUtms = await restoreUtms(env.DB, pHash);
|
|
175
|
+
if (restoredUtms) {
|
|
176
|
+
// Injetar UTMs restauradas no payload → creditam a campanha correta na CAPI
|
|
177
|
+
payload.utm_source = restoredUtms.utm_source;
|
|
178
|
+
payload.utm_medium = restoredUtms.utm_medium;
|
|
179
|
+
payload.utm_campaign = restoredUtms.utm_campaign;
|
|
180
|
+
payload.utm_content = restoredUtms.utm_content;
|
|
181
|
+
payload.utm_term = restoredUtms.utm_term;
|
|
182
|
+
payload._utm_restored = true; // flag para debug
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// 5. Adicionar p_hash ao payload para uso pelo Identity Graph
|
|
187
|
+
payload.p_hash = pHash;
|
|
188
|
+
|
|
189
|
+
} catch (err) {
|
|
190
|
+
// Fail-safe: nunca bloquear o tracking por erro de fingerprint
|
|
191
|
+
console.error('[FingerprintMiddleware] Erro (fail-safe):', err.message);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
return payload;
|
|
195
|
+
}
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
### Schema D1 necessário
|
|
199
|
+
|
|
200
|
+
```sql
|
|
201
|
+
-- Adicionar a schema.sql
|
|
202
|
+
CREATE TABLE IF NOT EXISTS fingerprint_sessions (
|
|
203
|
+
p_hash TEXT PRIMARY KEY,
|
|
204
|
+
utm_source TEXT,
|
|
205
|
+
utm_medium TEXT,
|
|
206
|
+
utm_campaign TEXT,
|
|
207
|
+
utm_content TEXT,
|
|
208
|
+
utm_term TEXT,
|
|
209
|
+
last_seen_at TEXT DEFAULT (datetime('now'))
|
|
210
|
+
);
|
|
211
|
+
|
|
212
|
+
CREATE INDEX IF NOT EXISTS idx_fp_last_seen ON fingerprint_sessions(last_seen_at);
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
### Uso no worker.js
|
|
216
|
+
|
|
217
|
+
```javascript
|
|
218
|
+
// No início do handler /track, ANTES do fraud gate:
|
|
219
|
+
import { fingerprintMiddleware } from './fingerprint-middleware.js';
|
|
220
|
+
|
|
221
|
+
// Dentro do fetch handler:
|
|
222
|
+
payload = await fingerprintMiddleware(request, env, payload);
|
|
223
|
+
// A partir daqui, payload.utm_* estão restauradas (se disponíveis)
|
|
224
|
+
// e payload.p_hash está disponível para o Identity Graph
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
---
|
|
228
|
+
|
|
24
229
|
## INPUTS RECEBIDOS
|
|
25
230
|
|
|
26
231
|
- Headers da requisição Edge: `CF-Connecting-IP`, `Accept-Language`, `User-Agent`, `request.cf.asOrganization`
|
|
@@ -43,6 +43,124 @@ if (isVersionConflict) {
|
|
|
43
43
|
|
|
44
44
|
---
|
|
45
45
|
|
|
46
|
+
## 🛡️ GOOGLE CONSENT MODE V2 — IMPLEMENTAÇÃO OBRIGATÓRIA
|
|
47
|
+
|
|
48
|
+
> **CRÍTICO**: Sem Consent Mode v2, campanhas Google Ads em audiências europeias são rejeitadas.
|
|
49
|
+
> Obrigatório para conformidade com GDPR (UE), LGPD (BR) e CCPA (EUA).
|
|
50
|
+
|
|
51
|
+
### PASSO 1 — Inicialização (ANTES do gtag.js)
|
|
52
|
+
|
|
53
|
+
Inserir **antes** do snippet do gtag.js no `<head>`:
|
|
54
|
+
|
|
55
|
+
```html
|
|
56
|
+
<!-- Google Consent Mode v2 — Inicializar NEGADO por padrão -->
|
|
57
|
+
<script>
|
|
58
|
+
window.dataLayer = window.dataLayer || [];
|
|
59
|
+
function gtag() { dataLayer.push(arguments); }
|
|
60
|
+
|
|
61
|
+
// OBRIGATÓRIO: definir consent ANTES de qualquer gtag() de medição
|
|
62
|
+
gtag('consent', 'default', {
|
|
63
|
+
'ad_storage': 'denied', // cookies de anúncio bloqueados até opt-in
|
|
64
|
+
'analytics_storage': 'denied', // cookies de analytics bloqueados até opt-in
|
|
65
|
+
'ad_user_data': 'denied', // envio de dados de usuário para Google Ads
|
|
66
|
+
'ad_personalization': 'denied', // personalização de anúncios
|
|
67
|
+
'wait_for_update': 500 // aguardar CMP atualizar consentimento (ms)
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
// url_passthrough: preserva gclid/gbraid/wbraid na URL sem cookie
|
|
71
|
+
// Permite atribuição de cliques mesmo sem consent de analytics_storage
|
|
72
|
+
gtag('set', 'url_passthrough', true);
|
|
73
|
+
|
|
74
|
+
// ads_data_redaction: quando ad_storage=denied, reduz dados de clique enviados
|
|
75
|
+
gtag('set', 'ads_data_redaction', true);
|
|
76
|
+
</script>
|
|
77
|
+
|
|
78
|
+
<!-- Carregar gtag.js normalmente após o bloco acima -->
|
|
79
|
+
<script async src="https://www.googletagmanager.com/gtag/js?id=GA4_MEASUREMENT_ID"></script>
|
|
80
|
+
<script>
|
|
81
|
+
window.dataLayer = window.dataLayer || [];
|
|
82
|
+
function gtag() { dataLayer.push(arguments); }
|
|
83
|
+
gtag('js', new Date());
|
|
84
|
+
gtag('config', 'GA4_MEASUREMENT_ID', {
|
|
85
|
+
'send_page_view': false // cdpTrack controla page_view manualmente
|
|
86
|
+
});
|
|
87
|
+
</script>
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### PASSO 2 — Atualizar Consent após Opt-in do usuário
|
|
91
|
+
|
|
92
|
+
Integrar com o banner de cookies do site (LGPD/GDPR):
|
|
93
|
+
|
|
94
|
+
```javascript
|
|
95
|
+
// Chamar quando usuário ACEITAR todos os cookies
|
|
96
|
+
function onConsentAccepted() {
|
|
97
|
+
gtag('consent', 'update', {
|
|
98
|
+
'ad_storage': 'granted',
|
|
99
|
+
'analytics_storage': 'granted',
|
|
100
|
+
'ad_user_data': 'granted',
|
|
101
|
+
'ad_personalization': 'granted'
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
// Opcional: disparar page_view após consent (se necessário)
|
|
105
|
+
gtag('event', 'page_view');
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Chamar quando usuário RECUSAR cookies não essenciais
|
|
109
|
+
function onConsentDeclined() {
|
|
110
|
+
gtag('consent', 'update', {
|
|
111
|
+
'ad_storage': 'denied',
|
|
112
|
+
'analytics_storage': 'denied',
|
|
113
|
+
'ad_user_data': 'denied',
|
|
114
|
+
'ad_personalization': 'denied'
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Aceitar apenas cookies analíticos (sem ads)
|
|
119
|
+
function onConsentAnalyticsOnly() {
|
|
120
|
+
gtag('consent', 'update', {
|
|
121
|
+
'ad_storage': 'denied',
|
|
122
|
+
'analytics_storage': 'granted',
|
|
123
|
+
'ad_user_data': 'denied',
|
|
124
|
+
'ad_personalization': 'denied'
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
### PASSO 3 — Verificação (via Intelligence Agent)
|
|
130
|
+
|
|
131
|
+
O Intelligence Agent verifica mensalmente se o Consent Mode está implementado:
|
|
132
|
+
|
|
133
|
+
```javascript
|
|
134
|
+
// Checklist mínimo no código gerado (browser tracking):
|
|
135
|
+
// ✅ gtag('consent', 'default', {...}) ANTES do gtag.js
|
|
136
|
+
// ✅ ad_storage: 'denied' no default
|
|
137
|
+
// ✅ analytics_storage: 'denied' no default
|
|
138
|
+
// ✅ ad_user_data: 'denied' no default
|
|
139
|
+
// ✅ ad_personalization: 'denied' no default
|
|
140
|
+
// ✅ url_passthrough: true ativo
|
|
141
|
+
// ✅ gtag('consent', 'update', {...}) no callback do CMP/banner
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### PASSO 4 — Integração com cdpTrack (Preservação de gclid)
|
|
145
|
+
|
|
146
|
+
```javascript
|
|
147
|
+
// O cdpTrack.js deve capturar gclid/gbraid/wbraid da URL mesmo sem consent
|
|
148
|
+
// url_passthrough: true garante que os parâmetros são passados como parâmetros de URL,
|
|
149
|
+
// não como cookies — respeitando consent de analytics_storage
|
|
150
|
+
|
|
151
|
+
function captureGoogleClickId() {
|
|
152
|
+
const params = new URLSearchParams(window.location.search);
|
|
153
|
+
return {
|
|
154
|
+
gclid: params.get('gclid') || null,
|
|
155
|
+
gbraid: params.get('gbraid') || null,
|
|
156
|
+
wbraid: params.get('wbraid') || null
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
// Esses IDs são enviados para o Worker e salvos no D1 para Enhanced Conversions offline
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
---
|
|
163
|
+
|
|
46
164
|
## 🛠️ O QUE VOCÊ GERA
|
|
47
165
|
|
|
48
166
|
### 1. Browser (Direct SDK)
|
|
@@ -71,7 +71,7 @@ export default {
|
|
|
71
71
|
const url = new URL(request.url);
|
|
72
72
|
|
|
73
73
|
// Handler principal
|
|
74
|
-
if (url.pathname === '/
|
|
74
|
+
if (url.pathname === '/track') {
|
|
75
75
|
return handleTracking(request, env, ctx);
|
|
76
76
|
}
|
|
77
77
|
|
|
@@ -157,12 +157,12 @@ Timestamp: ${new Date().toISOString()}
|
|
|
157
157
|
`.trim();
|
|
158
158
|
|
|
159
159
|
// Enviar via WhatsApp Agent
|
|
160
|
-
if (env.
|
|
161
|
-
await fetch(`https://graph.facebook.com/v22.0/${env.
|
|
160
|
+
if (env.WHATSAPP_PHONE_NUMBER_ID && env.ADMIN_PHONE_NUMBER) {
|
|
161
|
+
await fetch(`https://graph.facebook.com/v22.0/${env.WHATSAPP_PHONE_NUMBER_ID}/messages`, {
|
|
162
162
|
method: 'POST',
|
|
163
163
|
headers: {
|
|
164
164
|
'Content-Type': 'application/json',
|
|
165
|
-
'Authorization': `Bearer ${env.
|
|
165
|
+
'Authorization': `Bearer ${env.WHATSAPP_ACCESS_TOKEN}`
|
|
166
166
|
},
|
|
167
167
|
body: JSON.stringify({
|
|
168
168
|
messaging_product: 'whatsapp',
|
|
@@ -363,3 +363,89 @@ INTELLIGENCE_SCHEDULE_MONTHLY = "0 3 1 * *"
|
|
|
363
363
|
3. **Alerta Pré-ativo**: Antes de uma API ser descontinuada, alertar com 30 dias de antecedência
|
|
364
364
|
4. **False-Positive Safe**: Se houver dúvida sobre versão de API, marcar como "verificação manual necessária" em vez de alerta
|
|
365
365
|
5. **Backoff de Check**: Se o check falhar (API indisponível), tentar novamente em 1 hora (não disparar alerta imediato)
|
|
366
|
+
6. **Anti-Spam**: Não disparar alerta se o mesmo problema já foi reportado nas últimas 24h
|
|
367
|
+
7. **Prioridade Correta**: CRITICAL (agora), HIGH (até 1h), MEDIUM (no relatório)
|
|
368
|
+
8. **Log de Falhas de Alerta**: Se WhatsApp falhar 3× consecutivas, registrar no D1 e tentar via CallMeBot fallback
|
|
369
|
+
|
|
370
|
+
---
|
|
371
|
+
|
|
372
|
+
## 🗄️ SCHEMA D1 — intelligence_logs
|
|
373
|
+
|
|
374
|
+
Adicionar ao `server-edge-tracker/schema.sql`:
|
|
375
|
+
|
|
376
|
+
```sql
|
|
377
|
+
-- TABELA DE LOGS DO INTELLIGENCE AGENT
|
|
378
|
+
CREATE TABLE IF NOT EXISTS intelligence_logs (
|
|
379
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
380
|
+
run_type TEXT NOT NULL, -- 'weekly' | 'monthly' | 'on-demand'
|
|
381
|
+
platforms_checked TEXT, -- JSON array de plataformas verificadas
|
|
382
|
+
issues_found TEXT, -- JSON array de issues encontradas
|
|
383
|
+
issues_count INTEGER DEFAULT 0,
|
|
384
|
+
created_at TEXT DEFAULT (datetime('now'))
|
|
385
|
+
);
|
|
386
|
+
|
|
387
|
+
CREATE INDEX IF NOT EXISTS idx_intel_logs_type ON intelligence_logs(run_type);
|
|
388
|
+
CREATE INDEX IF NOT EXISTS idx_intel_logs_created ON intelligence_logs(created_at);
|
|
389
|
+
```
|
|
390
|
+
|
|
391
|
+
---
|
|
392
|
+
|
|
393
|
+
## ✅ CHECKLIST DE IMPLEMENTAÇÃO
|
|
394
|
+
|
|
395
|
+
Antes de considerar o scheduling implementado, verificar:
|
|
396
|
+
|
|
397
|
+
- [ ] Cron triggers adicionados ao `wrangler.toml` (`0 2 * * 7` e `0 3 1 * *`)
|
|
398
|
+
- [ ] Handlers `scheduled()` adicionados ao `worker.js` (Cloudflare usa `scheduled`, não `fetch`)
|
|
399
|
+
- [ ] Schema D1 atualizado com tabela `intelligence_logs`
|
|
400
|
+
- [ ] Funções de check de versão implementadas com endpoints reais
|
|
401
|
+
- [ ] Funções de auditoria de privacidade implementadas
|
|
402
|
+
- [ ] Sistema de alerta (WhatsApp/CallMeBot) integrado com anti-spam 24h
|
|
403
|
+
- [ ] Logs de execução sendo salvos no D1
|
|
404
|
+
- [ ] Memory Agent atualizado após cada check
|
|
405
|
+
- [ ] Backoff implementado para evitar spam de alertas
|
|
406
|
+
- [ ] Teste manual executado (`GET /api/intelligence/check`)
|
|
407
|
+
|
|
408
|
+
---
|
|
409
|
+
|
|
410
|
+
## INPUTS RECEBIDOS
|
|
411
|
+
|
|
412
|
+
- `wrangler.toml` do Worker (para injetar Cron Triggers)
|
|
413
|
+
- `worker.js` (para injetar handlers de `scheduled` events)
|
|
414
|
+
- `schema.sql` (para adicionar tabela `intelligence_logs`)
|
|
415
|
+
- Secrets: `WHATSAPP_PHONE_NUMBER_ID`, `WHATSAPP_ACCESS_TOKEN`, `ADMIN_PHONE_NUMBER` (para alertas)
|
|
416
|
+
- `contracts/api-versions.json` (fonte de verdade das versões atuais)
|
|
417
|
+
|
|
418
|
+
## RESPONSABILIDADE
|
|
419
|
+
|
|
420
|
+
- Configurar Cron Triggers no `wrangler.toml`: semanal (domingo 02:00 UTC) e mensal (1º do mês 03:00 UTC)
|
|
421
|
+
- Implementar handler `scheduled(event, env, ctx)` no Worker
|
|
422
|
+
- Chamar `runIntelligenceWeekly()` quando `event.cron === "0 2 * * 7"`
|
|
423
|
+
- Chamar `runIntelligenceMonthly()` quando `event.cron === "0 3 1 * *"`
|
|
424
|
+
- Adicionar tabela `intelligence_logs` ao schema D1
|
|
425
|
+
- Disparar alertas WhatsApp/CallMeBot ao admin apenas quando houver issues críticos
|
|
426
|
+
- Evitar spam: não repetir alerta do mesmo issue em menos de 24h
|
|
427
|
+
- Registrar resultado de cada execução no D1 (`intelligence_logs`)
|
|
428
|
+
|
|
429
|
+
## SAÍDA
|
|
430
|
+
|
|
431
|
+
```json
|
|
432
|
+
{
|
|
433
|
+
"arquivos_modificados": [
|
|
434
|
+
"wrangler.toml (cron triggers adicionados)",
|
|
435
|
+
"worker.js (handler scheduled() adicionado)",
|
|
436
|
+
"schema.sql (tabela intelligence_logs adicionada)"
|
|
437
|
+
],
|
|
438
|
+
"crons_configurados": {
|
|
439
|
+
"semanal": "0 2 * * 7 (domingo 02:00 UTC — check de versões)",
|
|
440
|
+
"mensal": "0 3 1 * * (dia 1 03:00 UTC — auditoria privacidade)"
|
|
441
|
+
},
|
|
442
|
+
"endpoint_manual": "GET /api/intelligence/check",
|
|
443
|
+
"alertas": {
|
|
444
|
+
"canal_primario": "WhatsApp Meta Cloud API v22.0",
|
|
445
|
+
"canal_fallback": "CallMeBot",
|
|
446
|
+
"anti_spam": "24h cooldown por issue"
|
|
447
|
+
},
|
|
448
|
+
"d1_tabela": "intelligence_logs",
|
|
449
|
+
"secrets_necessarios": ["WHATSAPP_PHONE_NUMBER_ID", "WHATSAPP_ACCESS_TOKEN", "ADMIN_PHONE_NUMBER"]
|
|
450
|
+
}
|
|
451
|
+
```
|