cdp-edge 1.2.2 → 1.3.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 +153 -306
- package/bin/cdp-edge.js +71 -61
- package/contracts/agent-versions.json +682 -0
- package/contracts/api-versions.json +372 -368
- package/contracts/types.ts +81 -0
- package/dist/commands/analyze.js +52 -52
- package/dist/commands/infra.js +54 -54
- package/dist/commands/install.js +26 -3
- package/dist/commands/server.js +174 -174
- package/dist/commands/setup.js +332 -100
- package/dist/commands/validate.js +248 -84
- package/dist/index.js +12 -12
- package/dist/sdk/cdpTrack.js +2095 -0
- package/dist/sdk/cdpTrack.min.js +64 -0
- package/dist/sdk/install-snippet.html +10 -0
- package/docs/whatsapp-ctwa.md +5 -4
- package/extracted-skill/tracking-events-generator/INTEGRACAO-COMPLETA.md +89 -0
- package/extracted-skill/tracking-events-generator/MELHORIAS-IMPLEMENTADAS.md +101 -0
- package/extracted-skill/tracking-events-generator/advanced-matching.js +364 -364
- package/extracted-skill/tracking-events-generator/agents/ab-ltv-agent.md +196 -0
- package/extracted-skill/tracking-events-generator/agents/ab-testing-agent.md +1 -1
- package/extracted-skill/tracking-events-generator/agents/attribution-agent.md +41 -41
- package/extracted-skill/tracking-events-generator/agents/bidding-agent.md +347 -0
- package/extracted-skill/tracking-events-generator/agents/bing-agent.md +40 -50
- package/extracted-skill/tracking-events-generator/agents/browser-tracking.md +174 -74
- package/extracted-skill/tracking-events-generator/agents/code-guardian-agent.md +1 -1
- package/extracted-skill/tracking-events-generator/agents/compliance-agent.md +25 -5
- package/extracted-skill/tracking-events-generator/agents/dashboard-agent.md +10 -10
- package/extracted-skill/tracking-events-generator/agents/database-agent.md +43 -42
- package/extracted-skill/tracking-events-generator/agents/debug-agent.md +22 -22
- package/extracted-skill/tracking-events-generator/agents/devops-agent.md +232 -0
- package/extracted-skill/tracking-events-generator/agents/domain-setup-agent.md +23 -9
- package/extracted-skill/tracking-events-generator/agents/email-agent.md +28 -1
- package/extracted-skill/tracking-events-generator/agents/evo-crm-agent.md +244 -0
- package/extracted-skill/tracking-events-generator/agents/fingerprint-agent.md +206 -1
- package/extracted-skill/tracking-events-generator/agents/fraud-detection-agent.md +143 -0
- package/extracted-skill/tracking-events-generator/agents/google-agent.md +128 -2
- package/extracted-skill/tracking-events-generator/agents/intelligence-agent.md +191 -31
- package/extracted-skill/tracking-events-generator/agents/lead-scoring-agent.md +282 -0
- package/extracted-skill/tracking-events-generator/agents/linkedin-agent.md +145 -34
- package/extracted-skill/tracking-events-generator/agents/localization-agent.md +1 -1
- package/extracted-skill/tracking-events-generator/agents/ltv-predictor-agent.md +5 -5
- package/extracted-skill/tracking-events-generator/agents/master-feedback-loop.md +81 -21
- package/extracted-skill/tracking-events-generator/agents/master-orchestrator.md +313 -93
- package/extracted-skill/tracking-events-generator/agents/match-quality-agent.md +304 -0
- package/extracted-skill/tracking-events-generator/agents/memory-agent.md +190 -15
- package/extracted-skill/tracking-events-generator/agents/meta-agent.md +10 -2
- package/extracted-skill/tracking-events-generator/agents/ml-clustering-agent.md +749 -0
- package/extracted-skill/tracking-events-generator/agents/page-analyzer.md +21 -4
- package/extracted-skill/tracking-events-generator/agents/performance-agent.md +41 -31
- package/extracted-skill/tracking-events-generator/agents/performance-optimization-agent.md +18 -8
- package/extracted-skill/tracking-events-generator/agents/pinterest-agent.md +14 -6
- package/extracted-skill/tracking-events-generator/agents/premium-tracking-intelligence-agent.md +7 -7
- package/extracted-skill/tracking-events-generator/agents/r2-setup-agent.md +16 -8
- package/extracted-skill/tracking-events-generator/agents/reddit-agent.md +15 -7
- package/extracted-skill/tracking-events-generator/agents/security-enterprise-agent.md +157 -48
- package/extracted-skill/tracking-events-generator/agents/server-tracking.md +35 -35
- package/extracted-skill/tracking-events-generator/agents/spotify-agent.md +15 -7
- package/extracted-skill/tracking-events-generator/agents/tiktok-agent.md +73 -2
- package/extracted-skill/tracking-events-generator/agents/tracking-plan-agent.md +104 -9
- package/extracted-skill/tracking-events-generator/agents/utm-agent.md +322 -0
- package/extracted-skill/tracking-events-generator/agents/validator-agent.md +13 -9
- package/extracted-skill/tracking-events-generator/agents/webhook-agent.md +112 -4
- package/extracted-skill/tracking-events-generator/agents/whatsapp-agent.md +58 -5
- package/extracted-skill/tracking-events-generator/agents/whatsapp-ctwa-setup-agent.md +26 -18
- package/extracted-skill/tracking-events-generator/agents/youtube-agent.md +152 -37
- package/extracted-skill/tracking-events-generator/anti-blocking.js +285 -285
- package/extracted-skill/tracking-events-generator/cdpTrack.js +642 -641
- package/extracted-skill/tracking-events-generator/contracts/api-versions.json +14 -10
- package/extracted-skill/tracking-events-generator/engagement-scoring.js +226 -226
- package/extracted-skill/tracking-events-generator/evals/evals.json +235 -235
- package/extracted-skill/tracking-events-generator/integration-test.js +497 -497
- package/extracted-skill/tracking-events-generator/knowledge-base.md +172 -0
- package/extracted-skill/tracking-events-generator/micro-events.js +992 -992
- package/extracted-skill/tracking-events-generator/models/lancamento-imobiliario.md +344 -0
- package/extracted-skill/tracking-events-generator/models/pinterest/conversions-api-template.js +144 -144
- package/extracted-skill/tracking-events-generator/models/pinterest/event-mappings.json +48 -48
- package/extracted-skill/tracking-events-generator/models/pinterest/tag-template.js +28 -28
- package/extracted-skill/tracking-events-generator/models/quiz-funnel.md +83 -19
- package/extracted-skill/tracking-events-generator/models/reddit/conversions-api-template.js +205 -205
- package/extracted-skill/tracking-events-generator/models/reddit/event-mappings.json +56 -56
- package/extracted-skill/tracking-events-generator/models/reddit/pixel-template.js +19 -19
- package/extracted-skill/tracking-events-generator/models/scenarios/behavior-engine.js +425 -425
- package/extracted-skill/tracking-events-generator/route-intent-capture.js +222 -0
- package/extracted-skill/tracking-events-generator/tracking.config.js +3 -3
- package/package.json +89 -75
- package/scripts/build-sdk.js +106 -0
- package/server-edge-tracker/.client.env.example +14 -0
- package/server-edge-tracker/INSTALAR.md +222 -23
- package/server-edge-tracker/SEGMENTATION-DOCS.md +513 -0
- package/server-edge-tracker/config/utm-mapping.json +64 -0
- package/server-edge-tracker/deploy-client.cjs +76 -0
- package/server-edge-tracker/index.ts +1230 -0
- package/server-edge-tracker/migrate-v7.sql +64 -0
- package/server-edge-tracker/modules/db.ts +710 -0
- package/server-edge-tracker/modules/dispatch/crm.ts +382 -0
- package/server-edge-tracker/modules/dispatch/ga4.ts +72 -0
- package/server-edge-tracker/modules/dispatch/meta.ts +143 -0
- package/server-edge-tracker/modules/dispatch/platforms.ts +255 -0
- package/server-edge-tracker/modules/dispatch/tiktok.ts +107 -0
- package/server-edge-tracker/modules/dispatch/whatsapp.ts +296 -0
- package/server-edge-tracker/modules/intelligence.ts +589 -0
- package/server-edge-tracker/modules/ml/bidding.ts +247 -0
- package/server-edge-tracker/modules/ml/fraud.ts +302 -0
- package/server-edge-tracker/modules/ml/logistic.ts +226 -0
- package/server-edge-tracker/modules/ml/ltv.ts +531 -0
- package/server-edge-tracker/modules/ml/matchquality.ts +232 -0
- package/server-edge-tracker/modules/ml/quiz.ts +343 -0
- package/server-edge-tracker/modules/ml/roas.ts +255 -0
- package/server-edge-tracker/modules/ml/segmentation.ts +407 -0
- package/server-edge-tracker/modules/nurture.ts +257 -0
- package/server-edge-tracker/modules/utils.ts +311 -0
- package/server-edge-tracker/modules/utm/utm-enricher.ts +231 -0
- package/server-edge-tracker/schema-ab-ltv.sql +97 -0
- package/server-edge-tracker/schema-bidding.sql +86 -0
- package/server-edge-tracker/schema-fraud.sql +90 -0
- package/server-edge-tracker/schema-indexes.sql +67 -0
- package/server-edge-tracker/schema-ltv-feedback.sql +11 -0
- package/server-edge-tracker/schema-quiz.sql +52 -0
- package/server-edge-tracker/schema-sales-engine.sql +113 -0
- package/server-edge-tracker/schema-segmentation.sql +219 -0
- package/server-edge-tracker/schema-utm.sql +82 -0
- package/server-edge-tracker/schema.sql +281 -265
- package/server-edge-tracker/types.ts +275 -0
- package/server-edge-tracker/wrangler.toml +140 -85
- package/templates/lancamento-imobiliario.md +344 -0
- package/templates/multi-step-checkout.md +3 -4
- package/templates/pinterest/conversions-api-template.js +144 -144
- package/templates/pinterest/event-mappings.json +48 -48
- package/templates/pinterest/tag-template.js +28 -28
- package/templates/quiz-funnel.md +83 -19
- package/templates/reddit/conversions-api-template.js +205 -205
- package/templates/reddit/event-mappings.json +56 -56
- package/templates/reddit/pixel-template.js +12 -39
- package/templates/scenarios/behavior-engine.js +45 -22
- package/docs/PixelBuilder-Documentacao-Completa (2).docx +0 -0
- package/docs/installation.md +0 -155
- package/docs/quick-start.md +0 -185
- package/extracted-skill/tracking-events-generator/agents/crm-integration-agent.md +0 -1419
- package/extracted-skill/tracking-events-generator/agents/intelligence-scheduling.md +0 -643
- package/server-edge-tracker/worker.js +0 -2574
|
@@ -5,6 +5,14 @@ Sua missão: configurar o rastreamento completo de anúncios Click to WhatsApp d
|
|
|
5
5
|
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
+
## ✅ REGRAS CRÍTICAS
|
|
9
|
+
|
|
10
|
+
0. **CONSULTA OBRIGATÓRIA À MEMÓRIA**: Extraia o ID de Número WhatsApp, Token de API e Token de Verificação (`WHATSAPP_PHONE_NUMBER_ID`, `WHATSAPP_ACCESS_TOKEN`, `WA_WEBHOOK_VERIFY_TOKEN`) consultando ativamente o "memory-agent.json". Solicite ao Orquestrador tudo o que faltar. Execute configurações de WhatsApp exclusivamente com os dados oficiais guardados na Memória para garantir alinhamento sistêmico.
|
|
11
|
+
1. Cloudflare-Only: Sem dependências externas.
|
|
12
|
+
2. Same-Domain: Worker no domínio do site (anti-adblock).
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
8
16
|
## PARTE 1 — FUNDAMENTOS: O QUE É CTWA E COMO OS DADOS FLUEM
|
|
9
17
|
|
|
10
18
|
### O que é Click to WhatsApp (CTWA)
|
|
@@ -164,8 +172,8 @@ message_body | Olá, vi o anúncio e tenho interesse
|
|
|
164
172
|
[ ] META_APP_ID — ID do app no Meta for Developers ← usuário fornece
|
|
165
173
|
[ ] META_APP_SECRET — App Secret (developers.facebook.com → Básico) ← usuário fornece
|
|
166
174
|
[ ] WA_WEBHOOK_VERIFY_TOKEN — gerado pelo agente (crypto.randomUUID) ← agente cria
|
|
167
|
-
[ ]
|
|
168
|
-
[ ]
|
|
175
|
+
[ ] WHATSAPP_PHONE_NUMBER_ID — descoberto automaticamente via API ← agente descobre
|
|
176
|
+
[ ] WHATSAPP_ACCESS_TOKEN — mesmo que META_ACCESS_TOKEN ← agente reutiliza
|
|
169
177
|
[ ] WHATSAPP_BUSINESS_ACCOUNT_ID — descoberto automaticamente via API ← agente descobre
|
|
170
178
|
```
|
|
171
179
|
|
|
@@ -175,7 +183,7 @@ message_body | Olá, vi o anúncio e tenho interesse
|
|
|
175
183
|
3. `META_APP_SECRET`
|
|
176
184
|
|
|
177
185
|
**O agente gera/descobre o restante automaticamente:**
|
|
178
|
-
```
|
|
186
|
+
```typescript
|
|
179
187
|
// WA_WEBHOOK_VERIFY_TOKEN — gerado pelo agente antes de registrar o webhook
|
|
180
188
|
const WA_WEBHOOK_VERIFY_TOKEN = crypto.randomUUID().replace(/-/g, '') + crypto.randomUUID().replace(/-/g, '');
|
|
181
189
|
// Exemplo: "a3f8c1d2e4b5a6f7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1"
|
|
@@ -490,16 +498,16 @@ Deve mostrar o app com `override_callback_uri` (se não-SMB) ou apenas o app sub
|
|
|
490
498
|
|
|
491
499
|
```bash
|
|
492
500
|
echo "{WABA_ID}" | wrangler secret put WHATSAPP_BUSINESS_ACCOUNT_ID
|
|
493
|
-
echo "{PHONE_ID}" | wrangler secret put
|
|
494
|
-
echo "{META_ACCESS_TOKEN}" | wrangler secret put
|
|
501
|
+
echo "{PHONE_ID}" | wrangler secret put WHATSAPP_PHONE_NUMBER_ID
|
|
502
|
+
echo "{META_ACCESS_TOKEN}" | wrangler secret put WHATSAPP_ACCESS_TOKEN
|
|
495
503
|
echo "{META_APP_SECRET}" | wrangler secret put META_APP_SECRET
|
|
496
504
|
echo "{META_ACCESS_TOKEN}" | wrangler secret put META_ACCESS_TOKEN
|
|
497
505
|
|
|
498
506
|
# ── Secrets para Auto-Resposta WhatsApp (enviar mensagens de saída) ─────────
|
|
499
|
-
# Necessários para:
|
|
500
|
-
#
|
|
507
|
+
# Necessários para: index.ts → auto-resposta após Lead/Purchase
|
|
508
|
+
# WHATSAPP_ACCESS_TOKEN = mesmo token Meta (Cloud API) — pode reutilizar META_ACCESS_TOKEN
|
|
501
509
|
# WHATSAPP_PHONE_NUMBER_ID = mesmo {PHONE_ID} descoberto acima
|
|
502
|
-
echo "{META_ACCESS_TOKEN}" | wrangler secret put
|
|
510
|
+
echo "{META_ACCESS_TOKEN}" | wrangler secret put WHATSAPP_ACCESS_TOKEN
|
|
503
511
|
echo "{PHONE_ID}" | wrangler secret put WHATSAPP_PHONE_NUMBER_ID
|
|
504
512
|
|
|
505
513
|
# Confirmar todos
|
|
@@ -510,15 +518,15 @@ Secrets esperados no worker ao final:
|
|
|
510
518
|
```
|
|
511
519
|
META_ACCESS_TOKEN
|
|
512
520
|
META_APP_SECRET
|
|
513
|
-
|
|
514
|
-
|
|
521
|
+
WHATSAPP_ACCESS_TOKEN
|
|
522
|
+
WHATSAPP_PHONE_NUMBER_ID
|
|
515
523
|
WA_WEBHOOK_VERIFY_TOKEN
|
|
516
524
|
WHATSAPP_BUSINESS_ACCOUNT_ID
|
|
517
|
-
|
|
518
|
-
WHATSAPP_PHONE_NUMBER_ID ← auto-resposta outbound (mesmo valor de
|
|
525
|
+
WHATSAPP_ACCESS_TOKEN ← auto-resposta outbound (mesmo valor de META_ACCESS_TOKEN)
|
|
526
|
+
WHATSAPP_PHONE_NUMBER_ID ← auto-resposta outbound (mesmo valor de WHATSAPP_PHONE_NUMBER_ID)
|
|
519
527
|
```
|
|
520
528
|
|
|
521
|
-
> **Nota:** `
|
|
529
|
+
> **Nota:** `WHATSAPP_ACCESS_TOKEN` e `WHATSAPP_PHONE_NUMBER_ID` são usados pela função de auto-resposta no worker (envio de mensagens de saída para o lead após eventos de conversão). São o mesmo token/phone_id da CTWA — apenas referenciados por nomes distintos no código.
|
|
522
530
|
|
|
523
531
|
---
|
|
524
532
|
|
|
@@ -603,13 +611,13 @@ App: {META_APP_ID} — subscriptions: messages ✅
|
|
|
603
611
|
|
|
604
612
|
SECRETS NO WORKER:
|
|
605
613
|
✅ META_ACCESS_TOKEN
|
|
606
|
-
✅
|
|
607
|
-
✅
|
|
614
|
+
✅ WHATSAPP_ACCESS_TOKEN
|
|
615
|
+
✅ WHATSAPP_PHONE_NUMBER_ID → {PHONE_ID}
|
|
608
616
|
✅ WA_WEBHOOK_VERIFY_TOKEN
|
|
609
617
|
✅ WHATSAPP_BUSINESS_ACCOUNT_ID → {WABA_ID}
|
|
610
618
|
✅ META_APP_SECRET
|
|
611
|
-
✅
|
|
612
|
-
✅ WHATSAPP_PHONE_NUMBER_ID → mesmo valor de
|
|
619
|
+
✅ WHATSAPP_ACCESS_TOKEN → mesmo valor de META_ACCESS_TOKEN (auto-resposta)
|
|
620
|
+
✅ WHATSAPP_PHONE_NUMBER_ID → mesmo valor de WHATSAPP_PHONE_NUMBER_ID (auto-resposta)
|
|
613
621
|
|
|
614
622
|
TESTE E2E:
|
|
615
623
|
✅ GET verificação → challenge retornado
|
|
@@ -674,7 +682,7 @@ PRÓXIMO PASSO:
|
|
|
674
682
|
|
|
675
683
|
| Arquivo | Função |
|
|
676
684
|
|---------|--------|
|
|
677
|
-
| `server-edge-tracker/
|
|
685
|
+
| `server-edge-tracker/index.ts` | `processWhatsAppWebhook()` + rotas `GET/POST /webhook/whatsapp` |
|
|
678
686
|
| `server-edge-tracker/migrate-v6.sql` | Criação da tabela `whatsapp_contacts` com índices |
|
|
679
687
|
| `server-edge-tracker/wrangler.toml` | Configuração do worker + lista de secrets documentados |
|
|
680
688
|
| `docs/whatsapp-ctwa.md` | Documentação técnica completa do módulo CTWA |
|
|
@@ -33,7 +33,7 @@ YouTube Ad (TrueView / Bumper / Non-skip)
|
|
|
33
33
|
|
|
34
34
|
### PASSO 0 — Ler Versões Atuais
|
|
35
35
|
|
|
36
|
-
```
|
|
36
|
+
```typescript
|
|
37
37
|
const apiVersions = await readJSON('contracts/api-versions.json');
|
|
38
38
|
const googleVersions = apiVersions.google;
|
|
39
39
|
|
|
@@ -53,7 +53,7 @@ if (consentModeVersion !== 'v2') {
|
|
|
53
53
|
|
|
54
54
|
### TrueView In-Stream (pulável após 5s)
|
|
55
55
|
- **Evento de billing**: view confirmada após 30s ou clique
|
|
56
|
-
- **Rastrear**: `video_start`, `
|
|
56
|
+
- **Rastrear**: `video_start`, `video_complete`, clique no CTA
|
|
57
57
|
- **Conversão Google Ads**: `engaged_view` (30s assistidos = 1 conversão de vídeo)
|
|
58
58
|
|
|
59
59
|
### Bumper Ads (6s não-puláveis)
|
|
@@ -89,39 +89,152 @@ const _gbraid = _urlParams.get('gbraid') || ''; // App campaigns (privacy)
|
|
|
89
89
|
**ATENÇÃO wbraid/gbraid**: São os click IDs para campanhas YouTube em iOS (pós ATT).
|
|
90
90
|
Nunca hashear — enviar como texto plano para Google Ads API.
|
|
91
91
|
|
|
92
|
-
### 2. Rastreamento de vídeo YouTube na página
|
|
92
|
+
### 2. Rastreamento de vídeo YouTube na página — YouTube IFrame API
|
|
93
93
|
|
|
94
|
-
O `behavior-engine.js` já implementa rastreamento de vídeos via YouTube IFrame API.
|
|
95
94
|
Para usar, o iframe deve ter `enablejsapi=1`:
|
|
96
95
|
|
|
97
96
|
```html
|
|
98
|
-
<!-- Embed YouTube com JS API habilitada -->
|
|
97
|
+
<!-- Embed YouTube com JS API habilitada (obrigatório) -->
|
|
99
98
|
<iframe
|
|
100
99
|
id="video-tour-imovel"
|
|
101
|
-
src="https://www.youtube.com/embed/VIDEO_ID?enablejsapi=1"
|
|
100
|
+
src="https://www.youtube.com/embed/VIDEO_ID?enablejsapi=1&origin=https://seudominio.com.br"
|
|
102
101
|
allow="autoplay"
|
|
103
102
|
></iframe>
|
|
104
103
|
```
|
|
105
104
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
105
|
+
#### Implementação real do YouTube IFrame API listener
|
|
106
|
+
|
|
107
|
+
```javascript
|
|
108
|
+
/**
|
|
109
|
+
* YouTube IFrame API Listener — injeta no behavior-engine.js ou tracking.js
|
|
110
|
+
* Rastreia: video_start, video_25, video_50, video_75, video_complete
|
|
111
|
+
* Dispara via cdpTrack.track() para o Worker → GA4 MP + demais plataformas
|
|
112
|
+
*/
|
|
113
|
+
|
|
114
|
+
// Carregar YouTube IFrame API (uma vez por página)
|
|
115
|
+
(function initYouTubeTracking() {
|
|
116
|
+
if (window._ytTrackingInitialized) return;
|
|
117
|
+
window._ytTrackingInitialized = true;
|
|
118
|
+
|
|
119
|
+
// Mapa de iframes já trackeados
|
|
120
|
+
const trackedPlayers = new Map();
|
|
121
|
+
|
|
122
|
+
// Injetar API script do YouTube (não carrega 2x se já existe)
|
|
123
|
+
if (!document.getElementById('youtube-iframe-api')) {
|
|
124
|
+
const tag = document.createElement('script');
|
|
125
|
+
tag.id = 'youtube-iframe-api';
|
|
126
|
+
tag.src = 'https://www.youtube.com/iframe_api';
|
|
127
|
+
document.head.appendChild(tag);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Callback global chamado pelo YouTube quando API estiver pronta
|
|
131
|
+
window.onYouTubeIframeAPIReady = function() {
|
|
132
|
+
// Auto-detectar todos os iframes com enablejsapi=1
|
|
133
|
+
document.querySelectorAll('iframe[src*="youtube.com/embed"]').forEach(iframe => {
|
|
134
|
+
if (trackedPlayers.has(iframe.id)) return;
|
|
135
|
+
|
|
136
|
+
const videoTitle = iframe.title || iframe.id || 'YouTube Video';
|
|
137
|
+
|
|
138
|
+
const player = new YT.Player(iframe.id, {
|
|
139
|
+
events: {
|
|
140
|
+
onStateChange: (event) => handlePlayerStateChange(event, player, videoTitle),
|
|
141
|
+
onReady: (event) => handlePlayerReady(event, player, videoTitle)
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
trackedPlayers.set(iframe.id, { player, milestone: new Set() });
|
|
146
|
+
});
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
// Se API já carregada (SPA reload), inicializar diretamente
|
|
150
|
+
if (typeof YT !== 'undefined' && YT.Player) {
|
|
151
|
+
window.onYouTubeIframeAPIReady();
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function handlePlayerReady(event, player, videoTitle) {
|
|
155
|
+
// Iniciar polling de progresso
|
|
156
|
+
const iframeId = player.getIframe().id;
|
|
157
|
+
const state = trackedPlayers.get(iframeId);
|
|
158
|
+
|
|
159
|
+
const interval = setInterval(() => {
|
|
160
|
+
if (!player.getDuration) return;
|
|
161
|
+
const duration = player.getDuration();
|
|
162
|
+
const current = player.getCurrentTime();
|
|
163
|
+
if (duration <= 0) return;
|
|
164
|
+
|
|
165
|
+
const percent = Math.floor((current / duration) * 100);
|
|
166
|
+
|
|
167
|
+
// Disparar milestones: 25, 50, 75 (100% é coberto pelo estado ENDED)
|
|
168
|
+
const milestoneEvents = { 25: 'video_25', 50: 'video_50', 75: 'video_75' };
|
|
169
|
+
[25, 50, 75].forEach(milestone => {
|
|
170
|
+
if (percent >= milestone && !state.milestone.has(milestone)) {
|
|
171
|
+
state.milestone.add(milestone);
|
|
172
|
+
|
|
173
|
+
window.cdpTrack?.track(milestoneEvents[milestone], {
|
|
174
|
+
content_name: videoTitle,
|
|
175
|
+
video_percent: milestone,
|
|
176
|
+
video_duration: Math.round(duration),
|
|
177
|
+
video_provider: 'youtube',
|
|
178
|
+
value: 0,
|
|
179
|
+
currency: 'BRL'
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
});
|
|
183
|
+
}, 1000); // checar a cada 1s
|
|
184
|
+
|
|
185
|
+
state.progressInterval = interval;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
function handlePlayerStateChange(event, player, videoTitle) {
|
|
189
|
+
const iframeId = player.getIframe().id;
|
|
190
|
+
const state = trackedPlayers.get(iframeId);
|
|
191
|
+
|
|
192
|
+
// YT.PlayerState: PLAYING=1, PAUSED=2, ENDED=0, BUFFERING=3
|
|
193
|
+
switch (event.data) {
|
|
194
|
+
case YT.PlayerState.PLAYING:
|
|
195
|
+
if (!state.started) {
|
|
196
|
+
state.started = true;
|
|
197
|
+
window.cdpTrack?.track('video_start', {
|
|
198
|
+
content_name: videoTitle,
|
|
199
|
+
video_duration: Math.round(player.getDuration() || 0),
|
|
200
|
+
video_provider: 'youtube'
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
break;
|
|
204
|
+
|
|
205
|
+
case YT.PlayerState.ENDED:
|
|
206
|
+
clearInterval(state.progressInterval);
|
|
207
|
+
window.cdpTrack?.track('video_complete', {
|
|
208
|
+
content_name: videoTitle,
|
|
209
|
+
video_duration: Math.round(player.getDuration() || 0),
|
|
210
|
+
video_provider: 'youtube'
|
|
211
|
+
});
|
|
212
|
+
break;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
})();
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
O listener dispara via `cdpTrack.track()`:
|
|
219
|
+
- `video_start` — primeiros 2s de play
|
|
220
|
+
- `video_25`, `video_50`, `video_75` — marcos de progresso (25/50/75%)
|
|
221
|
+
- `video_complete` — 100% assistido
|
|
109
222
|
|
|
110
223
|
### 3. Evento de Lead após assistir vídeo (imóveis)
|
|
111
224
|
|
|
112
225
|
```javascript
|
|
113
226
|
// Disparar Lead qualificado quando usuário assiste 75%+ do tour
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
}
|
|
124
|
-
}
|
|
227
|
+
// Adicionar dentro do callback de milestoneEvents no IFrame API listener:
|
|
228
|
+
// milestoneEvents[75] → 'video_75' — adicionar lógica abaixo no bloco forEach
|
|
229
|
+
if (milestone === 75) {
|
|
230
|
+
window.cdpTrack?.track('InitiateCheckout', {
|
|
231
|
+
content_name: 'Tour_Virtual_Empreendimento',
|
|
232
|
+
value: 0,
|
|
233
|
+
currency: 'BRL',
|
|
234
|
+
// Sinaliza alta intenção para Meta + Google
|
|
235
|
+
meta_intensity: 'high',
|
|
236
|
+
});
|
|
237
|
+
}
|
|
125
238
|
```
|
|
126
239
|
|
|
127
240
|
### 4. Consent Mode v2 — OBRIGATÓRIO para YouTube/Google Ads
|
|
@@ -140,7 +253,7 @@ cdpTrack.updateConsent({ analytics: true, ads: true });
|
|
|
140
253
|
|
|
141
254
|
---
|
|
142
255
|
|
|
143
|
-
## 🛠️ IMPLEMENTAÇÃO SERVER —
|
|
256
|
+
## 🛠️ IMPLEMENTAÇÃO SERVER — index.ts
|
|
144
257
|
|
|
145
258
|
### 1. Extrair e persistir Click IDs do YouTube
|
|
146
259
|
|
|
@@ -149,7 +262,7 @@ O `upsertProfile()` já persiste `gclid`, `wbraid`, `gbraid` no D1.
|
|
|
149
262
|
|
|
150
263
|
Para verificar persistência correta:
|
|
151
264
|
|
|
152
|
-
```
|
|
265
|
+
```typescript
|
|
153
266
|
// D1: user_profiles — colunas já existentes
|
|
154
267
|
// gclid TEXT — Google Ads standard click ID
|
|
155
268
|
// wbraid TEXT — iOS privacy-preserving (YouTube)
|
|
@@ -158,12 +271,14 @@ Para verificar persistência correta:
|
|
|
158
271
|
|
|
159
272
|
### 2. GA4 Measurement Protocol — Eventos de Vídeo
|
|
160
273
|
|
|
161
|
-
```
|
|
274
|
+
```typescript
|
|
162
275
|
// No sendGA4Mp() — adicionar mapeamento de eventos YouTube
|
|
163
276
|
const VIDEO_GA4_MAP = {
|
|
164
|
-
video_start:
|
|
165
|
-
|
|
166
|
-
|
|
277
|
+
video_start: 'video_start',
|
|
278
|
+
video_25: 'video_progress', // GA4 usa video_progress com percent
|
|
279
|
+
video_50: 'video_progress',
|
|
280
|
+
video_75: 'video_progress',
|
|
281
|
+
video_complete: 'video_complete',
|
|
167
282
|
};
|
|
168
283
|
|
|
169
284
|
// Params obrigatórios para video_progress (GA4)
|
|
@@ -178,7 +293,7 @@ const videoParams = {
|
|
|
178
293
|
|
|
179
294
|
### 3. Google Ads Enhanced Conversions — Lead de Vídeo
|
|
180
295
|
|
|
181
|
-
```
|
|
296
|
+
```typescript
|
|
182
297
|
// Conversão de Lead gerada por campanha YouTube
|
|
183
298
|
// Envia para GA4 MP com user_data para Enhanced Conversions
|
|
184
299
|
const enhancedConversionPayload = {
|
|
@@ -211,7 +326,7 @@ const enhancedConversionPayload = {
|
|
|
211
326
|
Para campanhas Bumper/Non-skip, o usuário converte DEPOIS sem clicar.
|
|
212
327
|
O Worker detecta isso quando um Lead chega SEM gclid mas com histórico de impressão YouTube:
|
|
213
328
|
|
|
214
|
-
```
|
|
329
|
+
```typescript
|
|
215
330
|
// No upsertProfile() — verificar se perfil tem impressão YouTube recente
|
|
216
331
|
// (requer webhook do Google Ads — avançado, Fase 5)
|
|
217
332
|
// Por ora: registrar ausência de gclid + utm_source=youtube como view-through candidate
|
|
@@ -228,9 +343,9 @@ if (!payload.gclid && payload.utmSource === 'youtube') {
|
|
|
228
343
|
| Evento cdpTrack | Mapeamento GA4 | Mapeamento Google Ads | Quando Disparar |
|
|
229
344
|
|---|---|---|---|
|
|
230
345
|
| `video_start` | `video_start` | — | Primeiros 2s de reprodução |
|
|
231
|
-
| `
|
|
232
|
-
| `
|
|
233
|
-
| `
|
|
346
|
+
| `video_25` | `video_progress` | — | 25% assistido |
|
|
347
|
+
| `video_50` | `video_progress` | `engaged_view` candidate | 50% assistido |
|
|
348
|
+
| `video_75` | `video_progress` | `engaged_view` | 75% assistido — alta intenção |
|
|
234
349
|
| `video_complete` | `video_complete` | `video_view_complete` | 100% assistido |
|
|
235
350
|
| `Lead` (após vídeo) | `generate_lead` | Conversão primária | Formulário submetido |
|
|
236
351
|
| `InitiateCheckout` | `begin_checkout` | Conversão micro | Clique em "Quero saber mais" |
|
|
@@ -244,7 +359,7 @@ if (!payload.gclid && payload.utmSource === 'youtube') {
|
|
|
244
359
|
```
|
|
245
360
|
FASE 1: AWARENESS (YouTube TrueView 30s)
|
|
246
361
|
↓ Tour aéreo do empreendimento / lifestyle do bairro
|
|
247
|
-
↓ Rastrear:
|
|
362
|
+
↓ Rastrear: video_50 + video_75 → score alto no LTV
|
|
248
363
|
↓ Remarketing: quem assistiu 50%+ vira audiência no Google Ads
|
|
249
364
|
|
|
250
365
|
FASE 2: CONSIDERAÇÃO (YouTube Non-skip 15s + Display)
|
|
@@ -303,7 +418,7 @@ const YOUTUBE_AUDIENCES_IMOVEIS = {
|
|
|
303
418
|
|
|
304
419
|
### Customer Match — Exportar leads do D1 para Google Ads
|
|
305
420
|
|
|
306
|
-
```
|
|
421
|
+
```typescript
|
|
307
422
|
// Endpoint no Worker: GET /export/customer-match
|
|
308
423
|
// Gera CSV criptografado para upload no Google Ads
|
|
309
424
|
|
|
@@ -334,7 +449,7 @@ async function exportCustomerMatchList(env) {
|
|
|
334
449
|
|
|
335
450
|
O Intelligence Agent (cron semanal) deve incluir check de YouTube:
|
|
336
451
|
|
|
337
|
-
```
|
|
452
|
+
```typescript
|
|
338
453
|
// Adicionar ao checkApiVersionsIntelligence():
|
|
339
454
|
// Verificar se wbraid/gbraid estão chegando nos leads
|
|
340
455
|
// (indica que campanhas YouTube iOS estão funcionando)
|
|
@@ -390,7 +505,7 @@ if (youtubeMobileLeads.count === 0) {
|
|
|
390
505
|
|
|
391
506
|
## RESPONSABILIDADE
|
|
392
507
|
|
|
393
|
-
- Gerar eventos de progresso de vídeo (`video_start`, `
|
|
508
|
+
- Gerar eventos de progresso de vídeo (`video_start`, `video_25`, `video_50`, `video_75`, `video_complete`) via GA4
|
|
394
509
|
- Implementar YouTube IFrame API listener para rastreamento de VSL no browser
|
|
395
510
|
- Garantir `gclid`, `wbraid`, `gbraid` chegando ao Worker (nunca hashear estes campos)
|
|
396
511
|
- Persistir `ga_client_id` no D1 para cruzamento com conversões YouTube Ads
|
|
@@ -403,11 +518,11 @@ if (youtubeMobileLeads.count === 0) {
|
|
|
403
518
|
{
|
|
404
519
|
"arquivos_gerados": {
|
|
405
520
|
"browser": "cdpTrack.js (eventos YouTube + IFrame API listener)",
|
|
406
|
-
"server": "
|
|
521
|
+
"server": "modules/dispatch/ga4.ts (já inclui YouTube via GA4)"
|
|
407
522
|
},
|
|
408
523
|
"eventos_implementados": [
|
|
409
524
|
"video_start",
|
|
410
|
-
"
|
|
525
|
+
"video_25", "video_50", "video_75",
|
|
411
526
|
"video_complete",
|
|
412
527
|
"generate_lead",
|
|
413
528
|
"purchase"
|