cdp-edge 2.0.0 → 2.0.1
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/dist/commands/install.js +1 -2
- package/dist/commands/setup.js +1 -2
- package/extracted-skill/tracking-events-generator/agents/browser-tracking.md +172 -72
- package/extracted-skill/tracking-events-generator/agents/google-agent.md +118 -0
- package/extracted-skill/tracking-events-generator/agents/intelligence-agent.md +86 -0
- package/extracted-skill/tracking-events-generator/agents/intelligence-scheduling.md +8 -641
- package/extracted-skill/tracking-events-generator/agents/memory-agent.md +98 -0
- package/extracted-skill/tracking-events-generator/agents/webhook-agent.md +42 -0
- package/package.json +1 -1
package/dist/commands/install.js
CHANGED
|
@@ -32,8 +32,7 @@ function printBanner() {
|
|
|
32
32
|
console.log(chalk.cyan('╚██████╗██████╔╝██║ ███████╗██████╔╝╚██████╔╝███████╗'));
|
|
33
33
|
console.log(chalk.cyan(' ╚═════╝╚═════╝ ╚═╝ ╚══════╝╚═════╝ ╚═════╝╚══════╝'));
|
|
34
34
|
console.log('');
|
|
35
|
-
console.log(chalk.gray(' Customer Data Platform on the Edge ·
|
|
36
|
-
console.log(chalk.gray(` Installer v2.0.0`));
|
|
35
|
+
console.log(chalk.gray(' Customer Data Platform on the Edge · Global Edge Tracking · v2.0.1'));
|
|
37
36
|
console.log('');
|
|
38
37
|
console.log(chalk.gray('═'.repeat(68)));
|
|
39
38
|
console.log('');
|
package/dist/commands/setup.js
CHANGED
|
@@ -19,8 +19,7 @@ function printBanner() {
|
|
|
19
19
|
console.log(chalk.cyan('╚██████╗██████╔╝██║ ███████╗██████╔╝╚██████╔╝███████╗'));
|
|
20
20
|
console.log(chalk.cyan(' ╚═════╝╚═════╝ ╚═╝ ╚══════╝╚═════╝ ╚═════╝╚══════╝'));
|
|
21
21
|
console.log('');
|
|
22
|
-
console.log(chalk.gray(' Customer Data Platform on the Edge ·
|
|
23
|
-
console.log(chalk.gray(' Setup Wizard v2.0.0'));
|
|
22
|
+
console.log(chalk.gray(' Customer Data Platform on the Edge · Global Edge Tracking · v2.0.1'));
|
|
24
23
|
console.log('');
|
|
25
24
|
console.log(chalk.gray('═'.repeat(68)));
|
|
26
25
|
console.log('');
|
|
@@ -109,99 +109,199 @@ Implementado via `anti-blocking.js`:
|
|
|
109
109
|
|
|
110
110
|
## 💻 EXEMPLO DE CÓDIGO GERADO
|
|
111
111
|
|
|
112
|
-
### `cdpTrack.js` (SDK Principal)
|
|
112
|
+
### `cdpTrack.js` (SDK Principal — Padrão Multi-Plataforma)
|
|
113
|
+
|
|
114
|
+
> Este é o padrão canônico do cdpTrack SDK para Meta, TikTok e GA4.
|
|
115
|
+
> Cada agente de plataforma injeta seus eventos neste SDK via Browser Tracking Agent.
|
|
113
116
|
|
|
114
117
|
```javascript
|
|
115
118
|
/**
|
|
116
119
|
* cdpTrack SDK - CDP Edge Quantum Tier
|
|
117
120
|
* Browser Tracking SDK Principal
|
|
121
|
+
* Suporta: Meta Pixel, TikTok Pixel, GA4, Pinterest Tag, Reddit Pixel, Spotify Pixel
|
|
118
122
|
*/
|
|
119
123
|
|
|
120
|
-
(function(w
|
|
121
|
-
|
|
122
|
-
w._pbq.push = w._pbq.push || [];
|
|
123
|
-
w._spotify = w._spotify || {};
|
|
124
|
-
|
|
125
|
-
// Carregar configuração
|
|
126
|
-
const config = window.cdpTrack?.config || {};
|
|
124
|
+
(function(w) {
|
|
125
|
+
'use strict';
|
|
127
126
|
|
|
128
|
-
//
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
127
|
+
// ──────────────────────────────────────────────
|
|
128
|
+
// CORE: Geração de event_id único (deduplicação)
|
|
129
|
+
// O mesmo event_id deve ser usado no browser E no servidor (CAPI)
|
|
130
|
+
// ──────────────────────────────────────────────
|
|
131
|
+
function generateEventId() {
|
|
132
|
+
return 'evt_' + Date.now() + '_' + Math.random().toString(36).substring(2, 11);
|
|
132
133
|
}
|
|
133
134
|
|
|
134
|
-
//
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
135
|
+
// ──────────────────────────────────────────────
|
|
136
|
+
// CORE: Envio para Cloudflare Worker (same-domain)
|
|
137
|
+
// Usa /track no mesmo domínio — imune a ad-blockers
|
|
138
|
+
// ──────────────────────────────────────────────
|
|
139
|
+
async function sendToWorker(eventName, payload) {
|
|
140
|
+
const eventId = generateEventId();
|
|
141
|
+
|
|
142
|
+
const body = {
|
|
143
|
+
event: eventName,
|
|
144
|
+
event_id: eventId, // CRÍTICO: mesmo ID usado nas CAPIs
|
|
145
|
+
url: window.location.href,
|
|
146
|
+
referrer: document.referrer,
|
|
147
|
+
timestamp: Date.now(),
|
|
148
|
+
...payload
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
try {
|
|
152
|
+
// Tentativa primária: fetch
|
|
153
|
+
await fetch('/track', {
|
|
154
|
+
method: 'POST',
|
|
155
|
+
headers: { 'Content-Type': 'application/json' },
|
|
156
|
+
body: JSON.stringify(body),
|
|
157
|
+
keepalive: true
|
|
152
158
|
});
|
|
159
|
+
} catch (_) {
|
|
160
|
+
// Fallback: Beacon API (funciona mesmo no unload da página)
|
|
161
|
+
navigator.sendBeacon('/track', JSON.stringify(body));
|
|
153
162
|
}
|
|
154
|
-
};
|
|
155
163
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
w._spotify.trackEvent('ViewContent', {
|
|
159
|
-
content_name: contentName,
|
|
160
|
-
content_id: contentId,
|
|
161
|
-
...params
|
|
162
|
-
});
|
|
163
|
-
};
|
|
164
|
+
return eventId;
|
|
165
|
+
}
|
|
164
166
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
};
|
|
167
|
+
// ──────────────────────────────────────────────
|
|
168
|
+
// CORE: Captura de cookies first-party
|
|
169
|
+
// ──────────────────────────────────────────────
|
|
170
|
+
function getCookie(name) {
|
|
171
|
+
const match = document.cookie.match(new RegExp('(^| )' + name + '=([^;]+)'));
|
|
172
|
+
return match ? decodeURIComponent(match[2]) : null;
|
|
173
|
+
}
|
|
173
174
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
175
|
+
// ──────────────────────────────────────────────
|
|
176
|
+
// CORE: Captura de click IDs da URL (Meta, TikTok, Google)
|
|
177
|
+
// ──────────────────────────────────────────────
|
|
178
|
+
function getClickIds() {
|
|
179
|
+
const params = new URLSearchParams(window.location.search);
|
|
180
|
+
return {
|
|
181
|
+
fbclid: params.get('fbclid') || getCookie('fbclid') || null,
|
|
182
|
+
ttclid: params.get('ttclid') || getCookie('ttclid') || null,
|
|
183
|
+
gclid: params.get('gclid') || null,
|
|
184
|
+
gbraid: params.get('gbraid') || null,
|
|
185
|
+
wbraid: params.get('wbraid') || null,
|
|
186
|
+
fbp: getCookie('_fbp') || null,
|
|
187
|
+
fbc: getCookie('_fbc') || null,
|
|
188
|
+
ttp: getCookie('_ttp') || null,
|
|
189
|
+
uid: getCookie('_cdp_uid') || null // Identity Graph first-party cookie
|
|
190
|
+
};
|
|
191
|
+
}
|
|
183
192
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
193
|
+
// ──────────────────────────────────────────────
|
|
194
|
+
// API PÚBLICA
|
|
195
|
+
// ──────────────────────────────────────────────
|
|
196
|
+
const cdpTrack = {
|
|
197
|
+
generateEventId,
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Rastrear evento genérico — enviado para o Worker
|
|
201
|
+
* O Worker despacha para Meta CAPI, GA4 MP, TikTok Events API etc.
|
|
202
|
+
*
|
|
203
|
+
* @param {string} eventName - Nome do evento (ex: 'Lead', 'Purchase', 'PageView')
|
|
204
|
+
* @param {Object} params - Parâmetros adicionais do evento
|
|
205
|
+
*/
|
|
206
|
+
track(eventName, params = {}) {
|
|
207
|
+
const clickIds = getClickIds();
|
|
208
|
+
return sendToWorker(eventName, { ...clickIds, ...params });
|
|
209
|
+
},
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Rastrear Lead (captura de formulário)
|
|
213
|
+
* Enviar dados PII crus — o Worker faz SHA-256 no servidor
|
|
214
|
+
*/
|
|
215
|
+
trackLead(userData = {}) {
|
|
216
|
+
const clickIds = getClickIds();
|
|
217
|
+
return sendToWorker('Lead', {
|
|
218
|
+
...clickIds,
|
|
219
|
+
email: userData.email || null, // Worker aplica SHA-256
|
|
220
|
+
phone: userData.phone || null, // Worker aplica E.164 + SHA-256
|
|
221
|
+
first_name: userData.first_name || null,
|
|
222
|
+
last_name: userData.last_name || null,
|
|
223
|
+
city: userData.city || null,
|
|
224
|
+
state: userData.state || null,
|
|
225
|
+
zip: userData.zip || null,
|
|
226
|
+
country: userData.country || 'BR'
|
|
227
|
+
});
|
|
228
|
+
},
|
|
192
229
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
230
|
+
/**
|
|
231
|
+
* Rastrear Purchase (confirmação de compra)
|
|
232
|
+
*/
|
|
233
|
+
trackPurchase(orderData = {}) {
|
|
234
|
+
const clickIds = getClickIds();
|
|
235
|
+
return sendToWorker('Purchase', {
|
|
236
|
+
...clickIds,
|
|
237
|
+
value: orderData.value || 0,
|
|
238
|
+
currency: orderData.currency || 'BRL',
|
|
239
|
+
order_id: orderData.order_id || null,
|
|
240
|
+
content_name: orderData.product || null
|
|
241
|
+
});
|
|
242
|
+
},
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* Rastrear PageView — chamar no load da página
|
|
246
|
+
*/
|
|
247
|
+
trackPageView() {
|
|
248
|
+
const clickIds = getClickIds();
|
|
249
|
+
return sendToWorker('PageView', { ...clickIds });
|
|
250
|
+
},
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* Rastrear InitiateCheckout
|
|
254
|
+
*/
|
|
255
|
+
trackInitiateCheckout(checkoutData = {}) {
|
|
256
|
+
const clickIds = getClickIds();
|
|
257
|
+
return sendToWorker('InitiateCheckout', {
|
|
258
|
+
...clickIds,
|
|
259
|
+
value: checkoutData.value || 0,
|
|
260
|
+
currency: checkoutData.currency || 'BRL'
|
|
261
|
+
});
|
|
262
|
+
}
|
|
200
263
|
};
|
|
201
264
|
|
|
202
|
-
|
|
265
|
+
// Expor no window
|
|
266
|
+
w.cdpTrack = cdpTrack;
|
|
267
|
+
|
|
268
|
+
// Auto page_view no load
|
|
269
|
+
if (document.readyState === 'loading') {
|
|
270
|
+
document.addEventListener('DOMContentLoaded', () => cdpTrack.trackPageView());
|
|
271
|
+
} else {
|
|
272
|
+
cdpTrack.trackPageView();
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
})(window);
|
|
203
276
|
```
|
|
204
277
|
|
|
278
|
+
### Uso típico no HTML do cliente
|
|
279
|
+
|
|
280
|
+
```html
|
|
281
|
+
<!-- 1. Carregar o SDK -->
|
|
282
|
+
<script src="/tracking/cdpTrack.js"></script>
|
|
283
|
+
|
|
284
|
+
<!-- 2. Rastrear lead ao submeter formulário -->
|
|
285
|
+
<script>
|
|
286
|
+
document.getElementById('lead-form').addEventListener('submit', function(e) {
|
|
287
|
+
cdpTrack.trackLead({
|
|
288
|
+
email: document.getElementById('email').value,
|
|
289
|
+
phone: document.getElementById('phone').value,
|
|
290
|
+
first_name: document.getElementById('name').value
|
|
291
|
+
});
|
|
292
|
+
});
|
|
293
|
+
</script>
|
|
294
|
+
|
|
295
|
+
<!-- 3. Rastrear checkout (botão de compra) -->
|
|
296
|
+
<script>
|
|
297
|
+
document.getElementById('buy-btn').addEventListener('click', function() {
|
|
298
|
+
cdpTrack.trackInitiateCheckout({ value: 97.00, currency: 'BRL' });
|
|
299
|
+
});
|
|
300
|
+
</script>
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
> **Nota:** O Worker recebe os dados crus, aplica SHA-256, e despacha para Meta CAPI v22.0, GA4 MP, TikTok Events API v1.3 e demais plataformas configuradas — tudo em paralelo via `Promise.allSettled`.
|
|
304
|
+
|
|
205
305
|
---
|
|
206
306
|
|
|
207
307
|
## 🔧 INTEGRAÇÃO COM OUTROS AGENTES
|
|
@@ -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)
|
|
@@ -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: `WA_PHONE_ID`, `WA_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": ["WA_PHONE_ID", "WA_ACCESS_TOKEN", "ADMIN_PHONE_NUMBER"]
|
|
450
|
+
}
|
|
451
|
+
```
|
|
@@ -1,643 +1,10 @@
|
|
|
1
|
-
|
|
1
|
+
# Intelligence Agent — Scheduling (REDIRECIONAMENTO)
|
|
2
2
|
|
|
3
|
-
Este arquivo
|
|
3
|
+
> ⚠️ **Este arquivo foi consolidado.**
|
|
4
|
+
>
|
|
5
|
+
> Todo o conteúdo de scheduling automático do Intelligence Agent foi unificado em:
|
|
6
|
+
> **[intelligence-agent.md](./intelligence-agent.md)**
|
|
7
|
+
>
|
|
8
|
+
> Consulte a seção `## 📅 PROTOCOLO DE SCHEDULING` dentro de `intelligence-agent.md`.
|
|
4
9
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
## 📅 VISÃO GERAL DO SCHEDULING
|
|
8
|
-
|
|
9
|
-
O Intelligence Agent possui três níveis de execução automática:
|
|
10
|
-
|
|
11
|
-
| Tipo | Frequência | Horário | Objetivo | Impacto |
|
|
12
|
-
|-------|-------------|----------|-----------|----------|
|
|
13
|
-
| **Semanal** | Domingo 02:00 UTC | Verificação completa de versões | 🔴 CRÍTICO |
|
|
14
|
-
| **Mensal** | 1º do mês 03:00 UTC | Auditoria de privacidade + depreciações | 🔴 CRÍTICO |
|
|
15
|
-
| **On-Demand** | A qualquer momento | Check específico quando houver suspeita | 🟠 HIGH |
|
|
16
|
-
|
|
17
|
-
---
|
|
18
|
-
|
|
19
|
-
## 🚀 IMPLEMENTAÇÃO NO CLOUDFLARE WORKER
|
|
20
|
-
|
|
21
|
-
### PASSO 1 — Configurar Triggers no wrangler.toml
|
|
22
|
-
|
|
23
|
-
Adicionar ao arquivo `server-edge-tracker/wrangler.toml` (ou `wrangler.toml` do Worker):
|
|
24
|
-
|
|
25
|
-
```toml
|
|
26
|
-
# ===========================================
|
|
27
|
-
# INTELLIGENCE AGENT — SCHEDULING
|
|
28
|
-
# ===========================================
|
|
29
|
-
|
|
30
|
-
# Trigger Semanal — Domingo 02:00 UTC (Verificação de versões de API)
|
|
31
|
-
[[triggers.crons]]
|
|
32
|
-
cron = "0 2 * * 0"
|
|
33
|
-
schedule = "weekly-intelligence-check"
|
|
34
|
-
|
|
35
|
-
# Trigger Mensal — 1º do mês 03:00 UTC (Auditoria de privacidade + depreciações)
|
|
36
|
-
[[triggers.crons]]
|
|
37
|
-
cron = "0 3 1 * *"
|
|
38
|
-
schedule = "monthly-privacy-audit"
|
|
39
|
-
|
|
40
|
-
# Variáveis de ambiente para controlar o scheduling
|
|
41
|
-
[vars]
|
|
42
|
-
INTELLIGENCE_ENABLED = true
|
|
43
|
-
INTELLIGENCE_WEEKLY_CRON = "0 2 * * 0"
|
|
44
|
-
INTELLIGENCE_MONTHLY_CRON = "0 3 1 * *"
|
|
45
|
-
INTELLIGENCE_ALERT_ENABLED = true
|
|
46
|
-
```
|
|
47
|
-
|
|
48
|
-
---
|
|
49
|
-
|
|
50
|
-
### PASSO 2 — Adicionar Handlers no worker.js
|
|
51
|
-
|
|
52
|
-
Adicionar ao arquivo `server-edge-tracker/worker.js`:
|
|
53
|
-
|
|
54
|
-
```javascript
|
|
55
|
-
// ===========================================
|
|
56
|
-
// INTELLIGENCE AGENT — SCHEDULING
|
|
57
|
-
// ===========================================
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* Handler Semanal — Check completo de versões de API
|
|
61
|
-
* Roda: Domingo 02:00 UTC
|
|
62
|
-
* Objetivo: Garantir que todas as APIs estejam atualizadas
|
|
63
|
-
*/
|
|
64
|
-
export async function runIntelligenceWeekly(env, ctx) {
|
|
65
|
-
console.log('🕵️♂️ Intelligence Agent — Check Semanal iniciado');
|
|
66
|
-
console.log('📅 Horário:', new Date().toISOString());
|
|
67
|
-
|
|
68
|
-
try {
|
|
69
|
-
const platforms = ['meta', 'google', 'tiktok', 'pinterest', 'reddit'];
|
|
70
|
-
const issues = [];
|
|
71
|
-
|
|
72
|
-
for (const platform of platforms) {
|
|
73
|
-
const result = await checkApiVersion(platform, env);
|
|
74
|
-
if (result.issue) {
|
|
75
|
-
issues.push(result);
|
|
76
|
-
}
|
|
77
|
-
console.log(`✅ ${platform.toUpperCase()}: ${result.status}`);
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
// Se houver problemas, disparar alerta
|
|
81
|
-
if (issues.length > 0) {
|
|
82
|
-
await dispatchIntelligenceAlert('API_VERSION_CHECK', issues, env);
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
// Logar resultado no D1
|
|
86
|
-
await logIntelligenceRun('weekly', platforms, issues, env);
|
|
87
|
-
|
|
88
|
-
console.log('✅ Intelligence Agent — Check Semanal concluído');
|
|
89
|
-
|
|
90
|
-
} catch (error) {
|
|
91
|
-
console.error('❌ Erro no Check Semanal:', error);
|
|
92
|
-
await dispatchIntelligenceAlert('WEEKLY_CHECK_ERROR', error.message, env);
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
/**
|
|
97
|
-
* Handler Mensal — Auditoria de privacidade + depreciações
|
|
98
|
-
* Roda: 1º do mês 03:00 UTC
|
|
99
|
-
* Objetivo: Garantir conformidade LGPD/GDPR/CCPA e verificar depreciações
|
|
100
|
-
*/
|
|
101
|
-
export async function runIntelligenceMonthly(env, ctx) {
|
|
102
|
-
console.log('🕵️♂️ Intelligence Agent — Auditoria Mensal iniciado');
|
|
103
|
-
console.log('📅 Horário:', new Date().toISOString());
|
|
104
|
-
|
|
105
|
-
try {
|
|
106
|
-
const issues = [];
|
|
107
|
-
|
|
108
|
-
// 1. Check de privacidade
|
|
109
|
-
const privacyIssues = await auditPrivacyCompliance(env);
|
|
110
|
-
issues.push(...privacyIssues);
|
|
111
|
-
|
|
112
|
-
// 2. Check de depreciações de API
|
|
113
|
-
const deprecationIssues = await checkApiDepreciations(env);
|
|
114
|
-
issues.push(...deprecationIssues);
|
|
115
|
-
|
|
116
|
-
// 3. Check de novos parâmetros de Event Match Quality
|
|
117
|
-
const matchQualityIssues = await checkNewMatchQualityParams(env);
|
|
118
|
-
issues.push(...matchQualityIssues);
|
|
119
|
-
|
|
120
|
-
// Se houver problemas, disparar alerta
|
|
121
|
-
if (issues.length > 0) {
|
|
122
|
-
await dispatchIntelligenceAlert('MONTHLY_AUDIT', issues, env);
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
// Logar resultado no D1
|
|
126
|
-
await logIntelligenceRun('monthly', [], issues, env);
|
|
127
|
-
|
|
128
|
-
console.log('✅ Intelligence Agent — Auditoria Mensal concluída');
|
|
129
|
-
|
|
130
|
-
} catch (error) {
|
|
131
|
-
console.error('❌ Erro na Auditoria Mensal:', error);
|
|
132
|
-
await dispatchIntelligenceAlert('MONTHLY_AUDIT_ERROR', error.message, env);
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
// ===========================================
|
|
137
|
-
// FUNÇÕES DE CHECK DE API VERSION
|
|
138
|
-
// ===========================================
|
|
139
|
-
|
|
140
|
-
async function checkApiVersion(platform, env) {
|
|
141
|
-
const apiUrls = {
|
|
142
|
-
meta: 'https://graph.facebook.com/v22.0/',
|
|
143
|
-
google: 'https://www.google-analytics.com/mp/collect',
|
|
144
|
-
tiktok: 'https://business-api.tiktok.com/open_api/v1.3/',
|
|
145
|
-
pinterest: 'https://api.pinterest.com/v5/',
|
|
146
|
-
reddit: 'https://ads-api.reddit.com/api/v2.0/'
|
|
147
|
-
};
|
|
148
|
-
|
|
149
|
-
const minimumVersions = {
|
|
150
|
-
meta: 'v22.0',
|
|
151
|
-
google: 'v2',
|
|
152
|
-
tiktok: 'v1.3',
|
|
153
|
-
pinterest: 'v5',
|
|
154
|
-
reddit: 'v2.0'
|
|
155
|
-
};
|
|
156
|
-
|
|
157
|
-
try {
|
|
158
|
-
const response = await fetch(apiUrls[platform], {
|
|
159
|
-
method: 'GET',
|
|
160
|
-
headers: {
|
|
161
|
-
'User-Agent': 'CDP Edge/1.0-Intelligence-Agent'
|
|
162
|
-
}
|
|
163
|
-
});
|
|
164
|
-
|
|
165
|
-
if (response.ok) {
|
|
166
|
-
return {
|
|
167
|
-
platform,
|
|
168
|
-
status: 'current',
|
|
169
|
-
issue: false
|
|
170
|
-
};
|
|
171
|
-
} else {
|
|
172
|
-
return {
|
|
173
|
-
platform,
|
|
174
|
-
status: 'error',
|
|
175
|
-
issue: true,
|
|
176
|
-
error: `HTTP ${response.status}`
|
|
177
|
-
};
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
} catch (error) {
|
|
181
|
-
return {
|
|
182
|
-
platform,
|
|
183
|
-
status: 'unreachable',
|
|
184
|
-
issue: true,
|
|
185
|
-
error: error.message
|
|
186
|
-
};
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
// ===========================================
|
|
191
|
-
// FUNÇÕES DE AUDITORIA DE PRIVACIDADE
|
|
192
|
-
// ===========================================
|
|
193
|
-
|
|
194
|
-
async function auditPrivacyCompliance(env) {
|
|
195
|
-
const issues = [];
|
|
196
|
-
|
|
197
|
-
// 1. Check: Google Consent Mode v2
|
|
198
|
-
const consentModeFiles = [
|
|
199
|
-
'tracking.js',
|
|
200
|
-
'tracking.config.js'
|
|
201
|
-
];
|
|
202
|
-
|
|
203
|
-
for (const file of consentModeFiles) {
|
|
204
|
-
try {
|
|
205
|
-
const content = await readFile(file);
|
|
206
|
-
const hasConsentMode = content.includes('ad_storage') &&
|
|
207
|
-
content.includes('analytics_storage') &&
|
|
208
|
-
content.includes('ad_user_data') &&
|
|
209
|
-
content.includes('ad_personalization');
|
|
210
|
-
|
|
211
|
-
const hasUrlPassthrough = content.includes('url_passthrough: true');
|
|
212
|
-
|
|
213
|
-
if (!hasConsentMode) {
|
|
214
|
-
issues.push({
|
|
215
|
-
platform: 'google',
|
|
216
|
-
issue: 'Consent Mode v2 não está implementado',
|
|
217
|
-
severity: 'CRITICAL',
|
|
218
|
-
fix: 'Implementar ad_storage=denied, analytics_storage=denied, etc. no browser-tracking.md',
|
|
219
|
-
file: file
|
|
220
|
-
});
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
if (!hasUrlPassthrough) {
|
|
224
|
-
issues.push({
|
|
225
|
-
platform: 'google',
|
|
226
|
-
issue: 'url_passthrough: true não está ativo',
|
|
227
|
-
severity: 'HIGH',
|
|
228
|
-
fix: 'Adicionar url_passthrough: true no gtag config',
|
|
229
|
-
file: file
|
|
230
|
-
});
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
} catch (error) {
|
|
234
|
-
console.log(`⚠️ Não foi possível auditar ${file}:`, error.message);
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
// 2. Check: Hashing de PII no servidor
|
|
239
|
-
const serverFiles = ['worker.js', 'meta-dispatcher.js'];
|
|
240
|
-
const hashCheck = content.includes('crypto.subtle.digest');
|
|
241
|
-
|
|
242
|
-
for (const file of serverFiles) {
|
|
243
|
-
try {
|
|
244
|
-
const content = await readFile(file);
|
|
245
|
-
|
|
246
|
-
if (!hashCheck) {
|
|
247
|
-
issues.push({
|
|
248
|
-
platform: 'meta/tiktok/pinterest/reddit',
|
|
249
|
-
issue: 'PII enviada sem SHA-256 hashing',
|
|
250
|
-
severity: 'CRITICAL',
|
|
251
|
-
fix: 'Usar crypto.subtle.digest para email/phone no worker.js',
|
|
252
|
-
file: file
|
|
253
|
-
});
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
} catch (error) {
|
|
257
|
-
console.log(`⚠️ Não foi possível auditar ${file}:`, error.message);
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
return issues;
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
// ===========================================
|
|
265
|
-
// FUNÇÕES DE CHECK DE DEPRECIAÇÕES
|
|
266
|
-
// ===========================================
|
|
267
|
-
|
|
268
|
-
async function checkApiDepreciations(env) {
|
|
269
|
-
const deprecationSchedule = {
|
|
270
|
-
meta: {
|
|
271
|
-
'v20.0': { deprecated: true, cutoff: '2024-01-01', replacement: 'v22.0' },
|
|
272
|
-
'v21.0': { deprecated: true, cutoff: '2024-06-01', replacement: 'v22.0' }
|
|
273
|
-
},
|
|
274
|
-
tiktok: {
|
|
275
|
-
'v1.2': { deprecated: true, cutoff: '2024-03-01', replacement: 'v1.3' }
|
|
276
|
-
},
|
|
277
|
-
pinterest: {
|
|
278
|
-
'v4': { deprecated: true, cutoff: '2024-01-01', replacement: 'v5' }
|
|
279
|
-
},
|
|
280
|
-
reddit: {
|
|
281
|
-
'v1.0': { deprecated: true, cutoff: '2024-06-01', replacement: 'v2.0' }
|
|
282
|
-
}
|
|
283
|
-
};
|
|
284
|
-
|
|
285
|
-
const currentVersions = {
|
|
286
|
-
meta: env.META_API_VERSION || 'unknown',
|
|
287
|
-
tiktok: env.TIKTOK_API_VERSION || 'unknown',
|
|
288
|
-
pinterest: env.PINTEREST_API_VERSION || 'unknown',
|
|
289
|
-
reddit: env.REDDIT_API_VERSION || 'unknown'
|
|
290
|
-
};
|
|
291
|
-
|
|
292
|
-
const issues = [];
|
|
293
|
-
|
|
294
|
-
for (const [platform, version] of Object.entries(currentVersions)) {
|
|
295
|
-
if (deprecationSchedule[platform]?.[version]) {
|
|
296
|
-
const { deprecated, cutoff, replacement } = deprecationSchedule[platform][version];
|
|
297
|
-
|
|
298
|
-
if (deprecated) {
|
|
299
|
-
issues.push({
|
|
300
|
-
platform,
|
|
301
|
-
issue: `API version ${version} está descontinuada`,
|
|
302
|
-
severity: 'CRITICAL',
|
|
303
|
-
cutoff_date: cutoff,
|
|
304
|
-
replacement_version: replacement,
|
|
305
|
-
fix: `Atualizar para ${replacement}`,
|
|
306
|
-
action_required: 'IMEDIATO',
|
|
307
|
-
deadline: cutoff
|
|
308
|
-
});
|
|
309
|
-
}
|
|
310
|
-
}
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
return issues;
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
// ===========================================
|
|
317
|
-
// FUNÇÕES DE CHECK DE NOVOS PARÂMETROS
|
|
318
|
-
// ===========================================
|
|
319
|
-
|
|
320
|
-
async function checkNewMatchQualityParams(env) {
|
|
321
|
-
const platforms = {
|
|
322
|
-
meta: 'https://developers.facebook.com/docs/marketing-api/conversions-api/parameters',
|
|
323
|
-
google: 'https://support.google.com/analytics/answer/9267733',
|
|
324
|
-
tiktok: 'https://ads.tiktok.com/marketing_api/docs?id=1740465605569281'
|
|
325
|
-
};
|
|
326
|
-
|
|
327
|
-
const issues = [];
|
|
328
|
-
|
|
329
|
-
for (const [platform, docsUrl] of Object.entries(platforms)) {
|
|
330
|
-
try {
|
|
331
|
-
// Em produção, usar WebFetch/MCP para parsing mais preciso
|
|
332
|
-
const response = await fetch(docsUrl);
|
|
333
|
-
|
|
334
|
-
if (!response.ok) {
|
|
335
|
-
continue;
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
// Placeholder para parsear novos parâmetros
|
|
339
|
-
// Na implementação real, extrair parâmetros novos da documentação
|
|
340
|
-
const newParams = extractNewParametersFromDocs(await response.text(), platform);
|
|
341
|
-
|
|
342
|
-
if (newParams.length > 0) {
|
|
343
|
-
issues.push({
|
|
344
|
-
platform,
|
|
345
|
-
issue: 'Novos parâmetros de Event Match Quality disponíveis',
|
|
346
|
-
severity: 'MEDIUM',
|
|
347
|
-
new_params: newParams,
|
|
348
|
-
fix: `Adicionar novos parâmetros: ${newParams.join(', ')}`,
|
|
349
|
-
documentation_url: docsUrl
|
|
350
|
-
});
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
} catch (error) {
|
|
354
|
-
console.log(`⚠️ Não foi possível verificar docs de ${platform}:`, error.message);
|
|
355
|
-
}
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
return issues;
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
// ===========================================
|
|
362
|
-
// FUNÇÕES DE ALERTA
|
|
363
|
-
// ===========================================
|
|
364
|
-
|
|
365
|
-
async function dispatchIntelligenceAlert(alertType, data, env) {
|
|
366
|
-
const alertMessages = {
|
|
367
|
-
'API_VERSION_CHECK': '🚨 VERIFICAÇÃO DE VERSÃO DE API',
|
|
368
|
-
'MONTHLY_AUDIT': '🔍 AUDITORIA MENSAL DE PRIVACIDADE',
|
|
369
|
-
'WEEKLY_CHECK_ERROR': '❌ ERRO NO CHECK SEMANAL',
|
|
370
|
-
'MONTHLY_AUDIT_ERROR': '❌ ERRO NA AUDITORIA MENSAL'
|
|
371
|
-
};
|
|
372
|
-
|
|
373
|
-
const message = `
|
|
374
|
-
${alertMessages[alertType]}
|
|
375
|
-
|
|
376
|
-
Timestamp: ${new Date().toISOString()}
|
|
377
|
-
Details: ${JSON.stringify(data, null, 2)}
|
|
378
|
-
|
|
379
|
-
Ação necessária: Verificar os detalhes acima e implementar correções.
|
|
380
|
-
`.trim();
|
|
381
|
-
|
|
382
|
-
// Enviar via WhatsApp Agent (se configurado)
|
|
383
|
-
if (env.WA_PHONE_ID && env.ADMIN_PHONE_NUMBER) {
|
|
384
|
-
await fetch(`https://graph.facebook.com/v22.0/${env.WA_PHONE_ID}/messages`, {
|
|
385
|
-
method: 'POST',
|
|
386
|
-
headers: {
|
|
387
|
-
'Content-Type': 'application/json',
|
|
388
|
-
'Authorization': `Bearer ${env.WA_ACCESS_TOKEN}`
|
|
389
|
-
},
|
|
390
|
-
body: JSON.stringify({
|
|
391
|
-
messaging_product: 'whatsapp',
|
|
392
|
-
to: env.ADMIN_PHONE_NUMBER,
|
|
393
|
-
type: 'text',
|
|
394
|
-
text: message
|
|
395
|
-
})
|
|
396
|
-
});
|
|
397
|
-
}
|
|
398
|
-
|
|
399
|
-
// Fallback para CallMeBot
|
|
400
|
-
else if (env.ADMIN_PHONE_NUMBER) {
|
|
401
|
-
await fetch(`https://api.callmebot.com/send.php`, {
|
|
402
|
-
method: 'POST',
|
|
403
|
-
body: new URLSearchParams({
|
|
404
|
-
phone: env.ADMIN_PHONE_NUMBER,
|
|
405
|
-
text: message
|
|
406
|
-
})
|
|
407
|
-
});
|
|
408
|
-
}
|
|
409
|
-
|
|
410
|
-
console.log('📤 Alerta enviado:', alertType);
|
|
411
|
-
}
|
|
412
|
-
|
|
413
|
-
// ===========================================
|
|
414
|
-
// FUNÇÕES DE LOGGING
|
|
415
|
-
// ===========================================
|
|
416
|
-
|
|
417
|
-
async function logIntelligenceRun(runType, platforms, issues, env) {
|
|
418
|
-
if (!env.DB) return;
|
|
419
|
-
|
|
420
|
-
await env.DB.prepare(`
|
|
421
|
-
INSERT INTO intelligence_logs (run_type, platforms_checked, issues_found, created_at)
|
|
422
|
-
VALUES (?, ?, ?, ?)
|
|
423
|
-
`).bind(
|
|
424
|
-
runType,
|
|
425
|
-
JSON.stringify(platforms),
|
|
426
|
-
JSON.stringify(issues),
|
|
427
|
-
new Date().toISOString()
|
|
428
|
-
).run();
|
|
429
|
-
}
|
|
430
|
-
```
|
|
431
|
-
|
|
432
|
-
---
|
|
433
|
-
|
|
434
|
-
### PASSO 3 — Atualizar Schema D1 (se necessário)
|
|
435
|
-
|
|
436
|
-
Adicionar ao `server-edge-tracker/schema.sql`:
|
|
437
|
-
|
|
438
|
-
```sql
|
|
439
|
-
-- TABELA DE LOGS DO INTELLIGENCE AGENT
|
|
440
|
-
CREATE TABLE IF NOT EXISTS intelligence_logs (
|
|
441
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
442
|
-
run_type TEXT NOT NULL, -- 'weekly' | 'monthly' | 'on-demand'
|
|
443
|
-
platforms_checked TEXT, -- JSON array de plataformas verificadas
|
|
444
|
-
issues_found TEXT, -- JSON array de issues encontradas
|
|
445
|
-
issues_count INTEGER DEFAULT 0,
|
|
446
|
-
created_at TEXT DEFAULT (datetime('now'))
|
|
447
|
-
);
|
|
448
|
-
|
|
449
|
-
-- ÍNDICE PARA QUERIES EFICIENTES
|
|
450
|
-
CREATE INDEX IF NOT EXISTS idx_intel_logs_type ON intelligence_logs(run_type);
|
|
451
|
-
CREATE INDEX IF NOT EXISTS idx_intel_logs_created ON intelligence_logs(created_at);
|
|
452
|
-
```
|
|
453
|
-
|
|
454
|
-
---
|
|
455
|
-
|
|
456
|
-
### PASSO 4 — Atualizar Main Fetch Handler
|
|
457
|
-
|
|
458
|
-
Adicionar ao handler principal do worker:
|
|
459
|
-
|
|
460
|
-
```javascript
|
|
461
|
-
export default {
|
|
462
|
-
async fetch(request, env, ctx) {
|
|
463
|
-
const url = new URL(request.url);
|
|
464
|
-
|
|
465
|
-
// Handler principal de tracking
|
|
466
|
-
if (url.pathname === '/api/track') {
|
|
467
|
-
return handleTracking(request, env, ctx);
|
|
468
|
-
}
|
|
469
|
-
|
|
470
|
-
// Handlers de Intelligence Agent (schedulados)
|
|
471
|
-
if (url.pathname === '/cron/intelligence-weekly') {
|
|
472
|
-
return await runIntelligenceWeekly(env, ctx);
|
|
473
|
-
}
|
|
474
|
-
|
|
475
|
-
if (url.pathname === '/cron/intelligence-monthly') {
|
|
476
|
-
return await runIntelligenceMonthly(env, ctx);
|
|
477
|
-
}
|
|
478
|
-
|
|
479
|
-
// Endpoint manual de check on-demand
|
|
480
|
-
if (url.pathname === '/api/intelligence/check') {
|
|
481
|
-
return await runIntelligenceWeekly(env, ctx);
|
|
482
|
-
}
|
|
483
|
-
|
|
484
|
-
return new Response('Not Found', { status: 404 });
|
|
485
|
-
}
|
|
486
|
-
};
|
|
487
|
-
```
|
|
488
|
-
|
|
489
|
-
---
|
|
490
|
-
|
|
491
|
-
## 🎯 CRITÉRIOS DE SUCESSO DO SCHEDULING
|
|
492
|
-
|
|
493
|
-
### Check Semanal (Versões de API)
|
|
494
|
-
|
|
495
|
-
- [ ] Todas as plataformas configuradas foram verificadas
|
|
496
|
-
- [ ] Endpoints estão respondendo (200 OK)
|
|
497
|
-
- [ ] Versões atuais estão documentadas no Memory Agent
|
|
498
|
-
- [ ] Alertas foram disparados se houver problemas
|
|
499
|
-
- [ ] Log foi salvo no D1 (intelligence_logs)
|
|
500
|
-
|
|
501
|
-
### Auditoria Mensal (Privacidade + Depreciações)
|
|
502
|
-
|
|
503
|
-
- [ ] Google Consent Mode v2 foi verificado
|
|
504
|
-
- [ ] Hashing de PII foi verificado em todas as APIs
|
|
505
|
-
- [ ] Depreciações foram verificadas (com cutoff date)
|
|
506
|
-
- [ ] Novos parâmetros de Match Quality foram pesquisados
|
|
507
|
-
- [ ] Todos os issues encontrados foram documentados
|
|
508
|
-
- [ ] Alerta consolidado foi enviado ao admin
|
|
509
|
-
|
|
510
|
-
### Métricas de Eficiência
|
|
511
|
-
|
|
512
|
-
O Intelligence Agent deve calcular e reportar:
|
|
513
|
-
|
|
514
|
-
```json
|
|
515
|
-
{
|
|
516
|
-
"intelligence_metrics": {
|
|
517
|
-
"weekly_checks": {
|
|
518
|
-
"total": 52,
|
|
519
|
-
"successful": 50,
|
|
520
|
-
"failed": 2,
|
|
521
|
-
"issues_detected": 3,
|
|
522
|
-
"uptime_percentage": "96.15%"
|
|
523
|
-
},
|
|
524
|
-
"monthly_audits": {
|
|
525
|
-
"total": 12,
|
|
526
|
-
"successful": 12,
|
|
527
|
-
"failed": 0,
|
|
528
|
-
"privacy_issues": 0,
|
|
529
|
-
"deprecation_issues": 1,
|
|
530
|
-
"compliance_rate": "100%"
|
|
531
|
-
},
|
|
532
|
-
"avg_response_time_minutes": 2.3,
|
|
533
|
-
"alert_sent_rate": "100%"
|
|
534
|
-
}
|
|
535
|
-
}
|
|
536
|
-
```
|
|
537
|
-
|
|
538
|
-
---
|
|
539
|
-
|
|
540
|
-
## 🔧 CONFIGURAÇÃO DE VARIÁVEIS DE AMBIENTE
|
|
541
|
-
|
|
542
|
-
Adicionar ao `wrangler.toml` ou via `wrangler secret put`:
|
|
543
|
-
|
|
544
|
-
```bash
|
|
545
|
-
# Secrets necessários para o Intelligence Agent
|
|
546
|
-
wrangler secret put INTELLIGENCE_ENABLED --name server-edge-tracker
|
|
547
|
-
wrangler secret put WA_PHONE_ID --name server-edge-tracker
|
|
548
|
-
wrangler secret put WA_ACCESS_TOKEN --name server-edge-tracker
|
|
549
|
-
wrangler secret put ADMIN_PHONE_NUMBER --name server-edge-tracker
|
|
550
|
-
|
|
551
|
-
# Valores recomendados:
|
|
552
|
-
# INTELLIGENCE_ENABLED = true
|
|
553
|
-
# WA_PHONE_ID = seu_phone_id_do_whatsapp
|
|
554
|
-
# WA_ACCESS_TOKEN = seu_access_token_da_meta
|
|
555
|
-
# ADMIN_PHONE_NUMBER = +5511999999999
|
|
556
|
-
```
|
|
557
|
-
|
|
558
|
-
---
|
|
559
|
-
|
|
560
|
-
## 📈 MONITORAMENTO DOS SCHEDULES
|
|
561
|
-
|
|
562
|
-
Após implementar, monitorar via Cloudflare Dashboard:
|
|
563
|
-
|
|
564
|
-
1. **Cron Triggers**: Verificar se os jobs estão rodando nos horários programados
|
|
565
|
-
2. **Success Rate**: Verificar se as chamadas estão terminando com sucesso
|
|
566
|
-
3. **Execution Time**: Monitorar o tempo de execução (deve ser < 5 minutos)
|
|
567
|
-
4. **Error Logs**: Verificar logs do Worker para erros nos handlers de intelligence
|
|
568
|
-
5. **Database Growth**: Monitorar crescimento da tabela `intelligence_logs`
|
|
569
|
-
|
|
570
|
-
---
|
|
571
|
-
|
|
572
|
-
## 🚨 REGRAS DE ALERTA
|
|
573
|
-
|
|
574
|
-
1. **Não Spam**: Não disparar alerta se o mesmo problema já foi reportado nas últimas 24h
|
|
575
|
-
2. **Prioridade Correta**: CRITICAL (agora), HIGH (até 1h), MEDIUM (relatório)
|
|
576
|
-
3. **Informação Útil**: Incluir sempre timestamp, plataforma, e ação necessária
|
|
577
|
-
4. **Backoff de Tentativa**: Se o envio de alerta falhar, tentar novamente em 10 minutos (máximo 3 tentativas)
|
|
578
|
-
5. **Log de Falhas de Alerta**: Se o WhatsApp Agent falhar 3 vezes consecutivas, registrar no D1 e notificar via outro canal
|
|
579
|
-
|
|
580
|
-
---
|
|
581
|
-
|
|
582
|
-
## ✅ CHECKLIST DE IMPLEMENTAÇÃO DO SCHEDULING
|
|
583
|
-
|
|
584
|
-
Antes de considerar o scheduling implementado, verificar:
|
|
585
|
-
|
|
586
|
-
- [ ] Cron triggers adicionados ao wrangler.toml
|
|
587
|
-
- [ ] Handlers de weekly/monthly adicionados ao worker.js
|
|
588
|
-
- [ ] Schema D1 atualizado com tabela intelligence_logs
|
|
589
|
-
- [ ] Funções de check de versão implementadas
|
|
590
|
-
- [ ] Funções de auditoria de privacidade implementadas
|
|
591
|
-
- [ ] Sistema de alerta (WhatsApp/CallMeBot) integrado
|
|
592
|
-
- [ ] Logs de execução sendo salvos no D1
|
|
593
|
-
- [ ] Memory Agent está sendo atualizado após cada check
|
|
594
|
-
- [ ] Backoff implementado para evitar spam de alertas
|
|
595
|
-
- [ ] Teste manual executado (/api/intelligence/check)
|
|
596
|
-
- [ ] Documentação atualizada com instruções de troubleshooting
|
|
597
|
-
|
|
598
|
-
---
|
|
599
|
-
|
|
600
|
-
> 📅 **Objetivo Final:** Garantir que o ecossistema CDP Edge esteja sempre atualizado com as últimas versões de API e em conformidade com LGPD/GDPR/CCPA, sem necessidade de intervenção manual constante.
|
|
601
|
-
|
|
602
|
-
---
|
|
603
|
-
|
|
604
|
-
## INPUTS RECEBIDOS
|
|
605
|
-
|
|
606
|
-
- `wrangler.toml` do Worker (para injetar Cron Triggers)
|
|
607
|
-
- `worker.js` (para injetar handlers de scheduled events)
|
|
608
|
-
- `schema.sql` (para adicionar tabela `intelligence_logs`)
|
|
609
|
-
- Secrets: `WA_PHONE_ID`, `WA_ACCESS_TOKEN`, `ADMIN_PHONE_NUMBER` (para alertas)
|
|
610
|
-
- `contracts/api-versions.json` (fonte de verdade das versões atuais)
|
|
611
|
-
|
|
612
|
-
## RESPONSABILIDADE
|
|
613
|
-
|
|
614
|
-
- Configurar Cron Triggers no `wrangler.toml`: semanal (domingo 02:00 UTC) e mensal (dia 1 às 03:00 UTC)
|
|
615
|
-
- Implementar `runIntelligenceWeekly()` e `runIntelligenceMonthly()` no Worker
|
|
616
|
-
- Adicionar tabela `intelligence_logs` ao schema D1
|
|
617
|
-
- Disparar alertas WhatsApp/CallMeBot ao admin apenas quando houver issues críticos
|
|
618
|
-
- Evitar spam: não repetir alerta do mesmo issue em menos de 24h
|
|
619
|
-
- Registrar resultado de cada execução no D1 (`intelligence_logs`)
|
|
620
|
-
|
|
621
|
-
## SAÍDA
|
|
622
|
-
|
|
623
|
-
```json
|
|
624
|
-
{
|
|
625
|
-
"arquivos_modificados": [
|
|
626
|
-
"wrangler.toml (cron triggers adicionados)",
|
|
627
|
-
"worker.js (handlers weekly/monthly adicionados)",
|
|
628
|
-
"schema.sql (tabela intelligence_logs adicionada)"
|
|
629
|
-
],
|
|
630
|
-
"crons_configurados": {
|
|
631
|
-
"semanal": "0 2 * * 0 (domingo 02:00 UTC — check de versões)",
|
|
632
|
-
"mensal": "0 3 1 * * (dia 1 03:00 UTC — auditoria privacidade)"
|
|
633
|
-
},
|
|
634
|
-
"endpoint_manual": "GET /api/intelligence/check",
|
|
635
|
-
"alertas": {
|
|
636
|
-
"canal_primario": "WhatsApp Meta Cloud API v22.0",
|
|
637
|
-
"canal_fallback": "CallMeBot",
|
|
638
|
-
"anti_spam": "24h cooldown por issue"
|
|
639
|
-
},
|
|
640
|
-
"d1_tabela": "intelligence_logs",
|
|
641
|
-
"secrets_necessarios": ["WA_PHONE_ID", "WA_ACCESS_TOKEN", "ADMIN_PHONE_NUMBER"]
|
|
642
|
-
}
|
|
643
|
-
```
|
|
10
|
+
Motivo: duplicação eliminada para manter uma única fonte de verdade.
|
|
@@ -54,6 +54,52 @@ Quando o Master Orchestrator solicitar um deploy, você fornece os dados ao DevO
|
|
|
54
54
|
|
|
55
55
|
---
|
|
56
56
|
|
|
57
|
+
## ⚡ QUICK REFERENCE — API DE CONSULTA (para outros agentes)
|
|
58
|
+
|
|
59
|
+
Qualquer agente pode consultar o Memory Agent com a seguinte chamada:
|
|
60
|
+
|
|
61
|
+
```javascript
|
|
62
|
+
// Consultar qualquer dado salvo na memória da sessão
|
|
63
|
+
const memoryQuery = async (query) => {
|
|
64
|
+
const checkpoint = await readMemoryCheckpoint(); // lê memory-agent.json
|
|
65
|
+
|
|
66
|
+
switch (query.type) {
|
|
67
|
+
case 'get_secret':
|
|
68
|
+
// query: { type: 'get_secret', platform: 'meta', secret_name: 'access_token' }
|
|
69
|
+
return checkpoint.secrets_configured?.[query.platform]?.[query.secret_name];
|
|
70
|
+
|
|
71
|
+
case 'get_api_version':
|
|
72
|
+
// query: { type: 'get_api_version', platform: 'tiktok' }
|
|
73
|
+
return checkpoint.api_versions?.[query.platform];
|
|
74
|
+
|
|
75
|
+
case 'get_infra':
|
|
76
|
+
// query: { type: 'get_infra', key: 'd1_database_id' }
|
|
77
|
+
return checkpoint.cloudflare_infrastructure?.bindings?.[query.key];
|
|
78
|
+
|
|
79
|
+
case 'check_if_implemented':
|
|
80
|
+
// query: { type: 'check_if_implemented', item: 'meta_capi' }
|
|
81
|
+
return checkpoint.context_state?.platforms_configured?.includes(query.item);
|
|
82
|
+
|
|
83
|
+
case 'get_technical_decision':
|
|
84
|
+
// query: { type: 'get_technical_decision', decision_id: 'decision_001' }
|
|
85
|
+
return checkpoint.technical_decisions?.find(d => d.id === query.decision_id);
|
|
86
|
+
|
|
87
|
+
default:
|
|
88
|
+
throw new Error(`Query type desconhecido: ${query.type}. Tipos válidos: get_secret | get_api_version | get_infra | check_if_implemented | get_technical_decision`);
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
// Exemplo de uso em qualquer agente — NUNCA inventar credenciais:
|
|
93
|
+
const metaToken = await memoryQuery({ type: 'get_secret', platform: 'meta', secret_name: 'access_token' });
|
|
94
|
+
if (!metaToken || metaToken === 'NOT_SET') {
|
|
95
|
+
throw new Error('META_ACCESS_TOKEN não configurado. Solicite ao usuário antes de continuar.');
|
|
96
|
+
}
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
> **Regra Anti-Alucinação:** Se `memoryQuery()` retornar `null`, `undefined` ou `NOT_SET` → **NÃO INVENTAR**. Solicitar ao usuário explicitamente.
|
|
100
|
+
|
|
101
|
+
---
|
|
102
|
+
|
|
57
103
|
## 🧠 OBJETIVO PRINCIPAL: ELIMINAR RETRABALHO E ALUCINAÇÃO
|
|
58
104
|
|
|
59
105
|
Sua única função é registrar absolutamente TUDO o que importa. Você é o banco de dados centralizado da sessão de chat.
|
|
@@ -144,6 +190,30 @@ O Memory Agent não é só um conceito — ele tem uma implementação técnica
|
|
|
144
190
|
"pixel": "v2",
|
|
145
191
|
"conversions_api": "v2.0",
|
|
146
192
|
"verified_at": null
|
|
193
|
+
},
|
|
194
|
+
"linkedin": {
|
|
195
|
+
"insight_tag": "latest",
|
|
196
|
+
"conversions_api": "v2",
|
|
197
|
+
"verified_at": null
|
|
198
|
+
},
|
|
199
|
+
"spotify": {
|
|
200
|
+
"pixel": "v1",
|
|
201
|
+
"conversions_api": "v1",
|
|
202
|
+
"verified_at": null
|
|
203
|
+
},
|
|
204
|
+
"whatsapp": {
|
|
205
|
+
"cloud_api": "v22.0",
|
|
206
|
+
"verified_at": null
|
|
207
|
+
},
|
|
208
|
+
"bing": {
|
|
209
|
+
"uet": "latest",
|
|
210
|
+
"conversions_api": "v2",
|
|
211
|
+
"verified_at": null
|
|
212
|
+
},
|
|
213
|
+
"youtube": {
|
|
214
|
+
"ga4_integration": "latest",
|
|
215
|
+
"customer_match": "SHA-256",
|
|
216
|
+
"verified_at": null
|
|
147
217
|
}
|
|
148
218
|
},
|
|
149
219
|
|
|
@@ -181,6 +251,34 @@ O Memory Agent não é só um conceito — ele tem uma implementação técnica
|
|
|
181
251
|
"pixel_id": "NOT_SET",
|
|
182
252
|
"access_token": "NOT_SET",
|
|
183
253
|
"verified_at": null
|
|
254
|
+
},
|
|
255
|
+
"pinterest": {
|
|
256
|
+
"tag_id": "NOT_SET",
|
|
257
|
+
"access_token": "NOT_SET",
|
|
258
|
+
"ad_account_id": "NOT_SET",
|
|
259
|
+
"verified_at": null
|
|
260
|
+
},
|
|
261
|
+
"reddit": {
|
|
262
|
+
"pixel_id": "NOT_SET",
|
|
263
|
+
"access_token": "NOT_SET",
|
|
264
|
+
"ad_account_id": "NOT_SET",
|
|
265
|
+
"verified_at": null
|
|
266
|
+
},
|
|
267
|
+
"linkedin": {
|
|
268
|
+
"access_token": "NOT_SET",
|
|
269
|
+
"conversion_id": "NOT_SET",
|
|
270
|
+
"ad_account_id": "NOT_SET",
|
|
271
|
+
"verified_at": null
|
|
272
|
+
},
|
|
273
|
+
"spotify": {
|
|
274
|
+
"ad_account_id": "NOT_SET",
|
|
275
|
+
"access_token": "NOT_SET",
|
|
276
|
+
"verified_at": null
|
|
277
|
+
},
|
|
278
|
+
"whatsapp": {
|
|
279
|
+
"phone_number_id": "NOT_SET",
|
|
280
|
+
"token": "NOT_SET",
|
|
281
|
+
"verified_at": null
|
|
184
282
|
}
|
|
185
283
|
},
|
|
186
284
|
|
|
@@ -12,6 +12,48 @@ Você é o especialista em Webhooks do CDP Edge. Sua missão é capturar vendas
|
|
|
12
12
|
|
|
13
13
|
---
|
|
14
14
|
|
|
15
|
+
## 🔐 NORMALIZAÇÃO E HASHING DE PII (OBRIGATÓRIO)
|
|
16
|
+
|
|
17
|
+
Antes de qualquer dispatch para CAPI, normalizar e hashear PII extraída do webhook:
|
|
18
|
+
|
|
19
|
+
```javascript
|
|
20
|
+
// Hashing SHA-256 para PII — usar WebCrypto (disponível em Cloudflare Workers)
|
|
21
|
+
async function hashPII(value) {
|
|
22
|
+
if (!value) return null;
|
|
23
|
+
const normalized = value.toString().toLowerCase().trim();
|
|
24
|
+
const encoder = new TextEncoder();
|
|
25
|
+
const data = encoder.encode(normalized);
|
|
26
|
+
const hashBuffer = await crypto.subtle.digest('SHA-256', data);
|
|
27
|
+
return Array.from(new Uint8Array(hashBuffer)).map(b => b.toString(16).padStart(2, '0')).join('');
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Normalização E.164 para telefone (Brasil)
|
|
31
|
+
function normalizePhone(phone) {
|
|
32
|
+
if (!phone) return null;
|
|
33
|
+
const digits = phone.replace(/\D/g, '');
|
|
34
|
+
// Adicionar +55 se não tiver código de país
|
|
35
|
+
if (digits.length === 10 || digits.length === 11) return `+55${digits}`;
|
|
36
|
+
if (digits.startsWith('55') && (digits.length === 12 || digits.length === 13)) return `+${digits}`;
|
|
37
|
+
return `+${digits}`;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Exemplo de uso no handler de webhook:
|
|
41
|
+
async function hashWebhookUserData(webhookPayload) {
|
|
42
|
+
const email = webhookPayload.buyer?.email || webhookPayload.email;
|
|
43
|
+
const phone = webhookPayload.buyer?.phone || webhookPayload.phone;
|
|
44
|
+
return {
|
|
45
|
+
em: email ? await hashPII(email) : null, // SHA-256 lowercase+trim
|
|
46
|
+
ph: phone ? await hashPII(normalizePhone(phone)) : null, // SHA-256 após E.164
|
|
47
|
+
fn: webhookPayload.buyer?.first_name ? await hashPII(webhookPayload.buyer.first_name) : null,
|
|
48
|
+
ln: webhookPayload.buyer?.last_name ? await hashPII(webhookPayload.buyer.last_name) : null,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
> **Regra:** NUNCA enviar email ou telefone em plaintext para Meta CAPI, GA4 MP ou TikTok Events API. Sempre normalizar → hashear → enviar.
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
15
57
|
## 🏗️ PADRÕES TÉCNICOS (Quantum Tier)
|
|
16
58
|
|
|
17
59
|
1. **D1 Identity Cross-Check**: Utilize o e-mail ou telefone do webhook para buscar no banco **D1** os identificadores originais (`fbp`, `fbc`, `ttp`). Isso garante a precisão da atribuição.
|
package/package.json
CHANGED