326 lines
9.0 KiB
Python
326 lines
9.0 KiB
Python
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()
|