primer commit

This commit is contained in:
George committed 2026-07-17 12:21:31 -05:00
commit 2be35d7e55
17 files changed
+2709

No files matched your search

+325
View File
@@ -0,0 +1,325 @@
import os
import re
import json
import time
import base64
from pathlib import Path
from dotenv import load_dotenv
from playwright.sync_api import sync_playwright, TimeoutError as PlaywrightTimeout
load_dotenv()
from get_token import (
SOL_URL, MENU_URL, TOKEN_FILE, SCREENSHOT_DIR,
RUC, USER, PASSWORD,
fill_login_form, save_screenshot, save_token,
check_for_token, navigate_sol_menu,
click_menu_item_by_id, extract_token_from_menu,
)
TEST_DIR = SCREENSHOT_DIR
TEST_DIR.mkdir(exist_ok=True)
USER_AGENT = (
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) "
"AppleWebKit/537.36 (KHTML, like Gecko) "
"Chrome/120.0.0.0 Safari/537.36"
)
BROWSER_ARGS = [
"--disable-blink-features=AutomationControlled",
"--disable-popup-blocking",
"--no-sandbox",
"--disable-web-security",
"--disable-features=IsolateOrigins,site-per-process",
"--window-size=1280,800",
"--start-maximized",
]
def captura(page, tag, nombre):
safe = f"{tag}_{nombre}"
page.screenshot(path=str(TEST_DIR / f"{safe}.png"))
def _salto_linea(texto, ancho=100):
for i in range(0, len(texto), ancho):
print(f" {texto[i:i+ancho]}")
def _login_si_necesario(page, tag):
page.wait_for_load_state("networkidle")
has_inputs = len(page.locator("input[type='password']").all()) > 0
necesita_login = has_inputs or any(
k in page.url.lower() for k in ["login", "autentica", "auth"]
)
if not necesita_login:
print(" No se detectó login")
return True
print(" Login detectado. Rellenando credenciales...")
fill_login_form(page)
captura(page, tag, "form_rellenado")
page.keyboard.press("Enter")
try:
page.wait_for_load_state("networkidle", timeout=30000)
except Exception as e:
print(f" ⚠️ Timeout post-login: {e}")
page.wait_for_timeout(3000)
captura(page, tag, "post_login")
print(f" URL post-login: {page.url}")
return True
def _buscar_token_en_pagina(page, tag):
time.sleep(2)
token = check_for_token(page)
if token:
return token
token = navigate_sol_menu(page)
if token:
return token
token = extract_token_from_menu(page)
return token
def _navegar_con_cookies(page, url, tag):
page.goto(url, wait_until="domcontentloaded")
captura(page, tag, "navegacion")
print(f" URL tras navegar: {page.url}")
# ─── ESTRATEGIAS ─────────────────────────────────────────────
def estrategia_1_fixed_popup(context, page):
tag = "e1"
print("\n" + "=" * 60)
print("ESTRATEGIA 1: Popup con --disable-popup-blocking + event listener")
print("=" * 60)
page.goto(SOL_URL, wait_until="domcontentloaded")
captura(page, tag, "sol_home")
popup_found = [None]
got_popup = [False]
def on_page(new_page):
if not got_popup[0]:
print(f" 📌 Página detectada: {new_page.url[:100]}")
popup_found[0] = new_page
got_popup[0] = True
page.context().on("page", on_page)
with page.expect_popup() as popup_info:
page.evaluate("tramiteConsulta()")
try:
pup = popup_info.value
pup.wait_for_load_state("domcontentloaded", timeout=20000)
print(f" Popup URL: {pup.url}")
captura(pup, tag, "popup")
if "chrome-error" in pup.url:
print(" ⚠️ Popup en chrome-error. Reintentando recarga...")
try:
pup.wait_for_load_state("domcontentloaded", timeout=20000)
except:
pass
captura(pup, tag, "popup_retry")
if "chrome-error" in pup.url:
print(" ❌ Sigue en chrome-error tras recarga")
print(f" URL final: {pup.url}")
return None
_login_si_necesario(pup, tag)
token = _buscar_token_en_pagina(pup, tag)
return token
except Exception as e:
print(f" ❌ Error con popup: {e}")
return None
def estrategia_2_direct_menu(context, page):
tag = "e2"
print("\n" + "=" * 60)
print("ESTRATEGIA 2: Menú directo con sesión desde SOL")
print("=" * 60)
page.goto(SOL_URL, wait_until="domcontentloaded")
captura(page, tag, "sol_home")
_navegar_con_cookies(page, MENU_URL, tag)
if not _login_si_necesario(page, tag):
return None
token = _buscar_token_en_pagina(page, tag)
return token
def estrategia_3_direct_login(context, page):
tag = "e3"
print("\n" + "=" * 60)
print("ESTRATEGIA 3: Login directo al menú (sin SOL)")
print("=" * 60)
_navegar_con_cookies(page, MENU_URL, tag)
if not _login_si_necesario(page, tag):
return None
token = _buscar_token_en_pagina(page, tag)
return token
def estrategia_4_popup_url_directo(context, page):
tag = "e4"
print("\n" + "=" * 60)
print("ESTRATEGIA 4: Extraer URL del popup y navegar directo")
print("=" * 60)
page.goto(SOL_URL, wait_until="domcontentloaded")
captura(page, tag, "sol_home")
try:
popup_url = page.evaluate("""() => {
const fn = tramiteConsulta.toString();
const match = fn.match(/window\\.open\\(['"]([^'"]+)['"]/);
return match ? match[1] : null;
}""")
if popup_url:
if popup_url.startswith("/"):
from urllib.parse import urljoin
popup_url = urljoin(SOL_URL, popup_url)
print(f" URL del popup: {popup_url}")
else:
print(" No se pudo extraer URL")
return None
except Exception as e:
print(f" Error extrayendo URL: {e}")
return None
_navegar_con_cookies(page, popup_url, tag)
if not _login_si_necesario(page, tag):
return None
token = _buscar_token_en_pagina(page, tag)
return token
# ─── CONFIGURACIONES DE LANZAMIENTO ──────────────────────────
def lanzar_chrome(p, channel=None, extra_args=None):
"""Lanza navegador con la configuracion dada."""
args = list(BROWSER_ARGS)
if extra_args:
args.extend(extra_args)
kwargs = {
"headless": True,
"args": args,
}
if channel:
kwargs["channel"] = channel
print(f" Lanzando: {channel} (headless)")
else:
print(f" Lanzando: chromium empaquetado (headless)")
browser = p.chromium.launch(**kwargs)
context = browser.new_context(
viewport={"width": 1280, "height": 800},
locale="es-PE",
timezone_id="America/Lima",
user_agent=USER_AGENT,
no_viewport=False,
ignore_https_errors=True,
)
page = context.new_page()
page.set_default_timeout(90000)
return browser, context, page
def ejecutar_escenario(p, nombre_browser, channel, extra_args):
"""Ejecuta todas las estrategias con un navegador/configuracion."""
print(f"\n{'#'*60}")
print(f"# ESCENARIO: {nombre_browser}")
print(f"{'#'*60}")
browser, context, page = lanzar_chrome(p, channel, extra_args)
token = None
estrategias = [
("1_fixed_popup", estrategia_1_fixed_popup),
("2_direct_menu", estrategia_2_direct_menu),
("3_direct_login", estrategia_3_direct_login),
("4_popup_url_directo", estrategia_4_popup_url_directo),
]
for nombre, fn in estrategias:
print()
try:
token = fn(context, page)
except Exception as e:
print(f" ⚠️ Excepción en {nombre}: {e}")
import traceback
traceback.print_exc()
token = None
if token:
print(f"\n✅ Token obtenido con '{nombre}' en escenario '{nombre_browser}'!")
return token
else:
print(f"\n ❌ '{nombre}' falló")
context.close()
browser.close()
return None
def main():
print("=" * 60)
print("🔐 TEST HEADLESS TOKEN - SUNAT")
print("=" * 60)
print(f" RUC: {RUC}")
print(f" USER: {USER}")
if not RUC or not USER or not PASSWORD:
print("\n❌ Faltan credenciales en .env (SUNAT_RUC, SUNAT_USER, SUNAT_PASSWORD)")
return
escenarios = [
("Chrome real (channel=chrome)", "chrome", None),
("Chromium empaquetado", None, None),
("Chromium + extra stealth", None, ["--disable-gpu", "--disable-accelerated-2d-canvas"]),
]
with sync_playwright() as p:
for nombre_browser, channel, extra_args in escenarios:
token = ejecutar_escenario(p, nombre_browser, channel, extra_args)
if token:
save_token(token)
print(f"\n✅ Token guardado en {TOKEN_FILE}")
print(f" Primeros 50 chars: {token[:50]}...")
# Quitar token de prueba, no guardarlo permanentemente
if TOKEN_FILE.exists():
TOKEN_FILE.unlink()
return
print(f"\n❌ Todas las estrategias y escenarios fallaron.")
print(" Revisa screenshots en:", TEST_DIR)
if __name__ == "__main__":
main()