--- name: AA Solver v3 RESOLVIDO 2026-05-04 description: AA passa Akamai com IP V.tal BH usando spawn google-chrome puro + playwright connectOverCDP (padrão gol-solver). puppeteer.launch era o problema (--enable-automation). type: project originSessionId: 2f746173-1571-4f7d-b95f-7008f177e1c9 --- # AA Solver v3 — VM ADV-22 — 2026-05-04 ## A descoberta **O bloqueio não era IP, era a flag `--enable-automation` injetada por `puppeteer.launch()`.** O Akamai do aa.com não bloqueava o IP V.tal BH (`186.247.226.26`). Bloqueava qualquer Chrome com sinal de automation. Quando o Chrome é lançado por `spawn('/usr/bin/google-chrome-stable', [...args limpos])` e controlado via `playwright-core.connectOverCDP(...)` (padrão idêntico ao gol-solver), `/booking/find-flights` passa **na primeira tentativa**, em 23s. O memo `project_aa_vm_bridge_2026_05_04.md` tinha errado o diagnóstico ("homepage bloqueada title='' bodyLen=0"). Aquele teste era com `puppeteer.launch` ainda; com Chrome puro, homepage E `/booking/*` passam. ## Setup atual ### Solver (VM ADV-22 100.91.20.22:9093) - `/opt/aa-solver/aa-solver.mjs` — versão v3 (spawn + playwright) ativa em systemd - `/opt/aa-solver/aa-solver.mjs.bak-pre-v3-` — backup da versão anterior (puppeteer-extra+stealth) - Profile: `/home/customer/aa-chrome-profile-v3` - CDP port: 9228 - Padrão idêntico ao `/opt/gol-solver/gol-solver.mjs` (reusa playwright-core de `/opt/smiles-solver/node_modules/`) ### Args do Chrome (sem --enable-automation) ``` --remote-debugging-port=9228 --user-data-dir=/home/customer/aa-chrome-profile-v3 --no-first-run --no-default-browser-check --disable-blink-features=AutomationControlled --disable-features=IsolateOrigins,site-per-process --use-gl=swiftshader --enable-unsafe-swiftshader --ignore-gpu-blocklist --lang=pt-BR --window-size=1366,768 https://www.aa.com/homePage.do?locale=pt_BR ``` ### Watchdog - `/opt/aa-solver/watchdog.sh` — checa `/health` (status==ok), reinicia se >120s sem ok - `aa-watchdog.service` + `aa-watchdog.timer` (1min) — enabled - Em restart: `pkill -9 -f aa-chrome-profile-v3` antes do `systemctl restart aa-solver` ## Como o scraper captura voos 1. spawn Chrome → CDP up → playwright connect 2. abre homepage (Akamai libera _abck~0~ + bm_sz) 3. wiggle 4x mouse/scroll 4. `page.goto('https://www.aa.com/booking/search?slices=...')` — **navegação direta funciona** 5. Akamai redireciona pra `/booking/choose-flights/1?sid=` com **HTML SSR de 936KB** contendo `SearchData.itineraryResult` JSON inline 6. `page.on('response')` captura essa response e o parser (`extractItineraryResult`) extrai o JSON via brace-matching ## Validação ``` curl 'http://127.0.0.1:9093/search?origin=GRU&destination=MIA&departureDate=2026-06-15&returnDate=2026-06-25&adults=1&searchType=Award' → {"success":true,"searchData":{...slices:[{...},{...}]...}} em 23s 4 cabines (COACH/PREMIUM_ECONOMY/BUSINESS/FIRST), prices em BRL, sliceCount=2, tripType=ROUND_TRIP, resultId/sessionId retornados. ``` ## Próximo passo (PENDENTE) **Apontar o scraper `/opt/skymilhas/scrapers/aadvantage/index.js` pro solver da VM** (igual GOL/Smiles fazem): 1. Adicionar env-driven VM solver: ```js const AA_VM_SOLVER_HOST = process.env.AA_VM_SOLVER_HOST || '100.91.20.22'; const AA_VM_SOLVER_PORT = parseInt(process.env.AA_VM_SOLVER_PORT || '9093', 10); const AA_USE_VM_SOLVER = process.env.AA_USE_VM_SOLVER !== '0'; ``` 2. Implementar `callAaVmSolver(params)` (HTTP GET) 3. **Implementar parser `searchData.slices` → `flight[]`** com formato esperado pelo gateway: - Campos: numero, programa, companhia, codigoCompanhia, voo, numeroVoo, trecho, origem/destino+cidade+aeroporto, dataPartida, horarioSaida/Chegada, duracao, duracaoMinutos, paradas, tipoVoo, milhas, milhasFormatado, preco, valor, valorDinheiro, taxaEmbarque, moeda, tarifas[] (todas cabines), assentosRestantes, link, segmentos[] - Schema do searchData: `slices[].cheapestPrice.allPassengerDisplayTotal{amount,currency}`, `slices[].cheapestPrice.allPassengerTaxesAndFees`, `slices[].carrierNames[]`, `slices[].arrivalDateTime/departureDateTime/duration`, etc 4. No início de `searchAA()`: tentar VM solver, se success → retornar; se fail → fallback Chrome local + proxy bridge (manter código atual como secondary). Estimativa: ~200 linhas Node + testes. **Why:** integração não foi feita nessa sessão pra não bagunçar — quero parser bem testado. Solver da VM tá rodando 24/7 e o fallback proxy-seller continua intocado, então não há urgência. **How to apply:** próxima sessão, ler `/opt/skymilhas/scrapers/aadvantage/index.js` linhas 880-1410 (formato flight), implementar parser, adicionar `if (AA_USE_VM_SOLVER) try { vmRes = await callAaVmSolver(params); if (vmRes.success) return parseAaSearchData(vmRes.searchData, params); } catch(e) {}` antes do `searchAAPooled`. Restart `pm2 restart scraper-american-aadvantage --update-env`.