216cad4c12
Optimiza el dominio browser para que el manejo del navegador via CDP sea mucho más rápido en automatización propia, manteniendo el camino sigiloso disponible. - CDPConn cachea los enable de Accessibility/Network/Page por conexión (ensureAX/ensureNetwork/ensurePage): elimina un round-trip redundante en cada percepción y espera, que son las operaciones más frecuentes del bucle percibir->actuar del agente. - sendCDP adquiere timeout (cdpCmdTimeout 30s): antes una respuesta que Chrome nunca enviaba colgaba la goroutine del tool indefinidamente; ahora falla limpio y el retry puede reconectar. - CdpWaitLoad pasa de polling de document.readyState cada 200ms a esperar el evento Page.loadEventFired, con fast path inicial de readyState y re-chequeo anti-carrera tras suscribir. Si la página ya está cargada retorna en microsegundos. - cdp_wait_idle usa ensureNetwork y deja de hacer Network.disable al salir (borraba el estado y forzaba el enable de nuevo). - Nuevas funciones de escritura rápida: CdpInsertText (todo el texto en un solo Input.insertText) y CdpTypeRefFast (focus + insertText). El chequeo de foco se extrajo a assertEditableFocus, compartido con CdpTypeText. - CdpTypeText pasa su pausa entre caracteres de 10ms fija a aleatoria 15-65ms (ritmo humano irregular). - El modo 'auto' se añade al perfil de ratón (MouseProfileForMode, mouseHumanDefaults, clickPauseMs) como alias rápido de 'fast'. No se tocan las firmas públicas existentes; CdpTypeRef y CdpTypeText conservan su comportamiento (camino human).
65 lines
2.1 KiB
Go
65 lines
2.1 KiB
Go
package browser
|
|
|
|
import (
|
|
"fmt"
|
|
"math/rand"
|
|
"time"
|
|
)
|
|
|
|
// CdpClickXYHuman hace click en las coordenadas absolutas (x, y) de la página con
|
|
// comportamiento humano: mueve el ratón hasta el punto por una trayectoria de
|
|
// Bézier cúbica (CdpMoveMouseHuman) y despacha mousePressed/mouseReleased con una
|
|
// micro-pausa variable (30-90 ms) entre ambos.
|
|
//
|
|
// Es el PRIMITIVO de click compartido por las tres vías de acción del agente:
|
|
// - por selector CSS → CdpClickHuman (obtiene el bbox y llama aquí).
|
|
// - por #ref del AX tree → CdpClickRef (resuelve backendDOMNodeId → bbox → aquí).
|
|
// - por visión → click sobre el bounding box que devuelve OCR/YOLO.
|
|
// Construir un único primitivo evita tener tres caminos de click divergentes.
|
|
func CdpClickXYHuman(c *CDPConn, x, y float64, opts MouseHumanOpts) error {
|
|
if c == nil {
|
|
return fmt.Errorf("cdp click xy human: conexion nula")
|
|
}
|
|
|
|
// Mover el ratón hasta el destino con trayectoria humana.
|
|
if err := CdpMoveMouseHuman(c, x, y, opts); err != nil {
|
|
return fmt.Errorf("cdp click xy human: mover raton: %w", err)
|
|
}
|
|
|
|
clickParams := map[string]any{
|
|
"type": "mousePressed",
|
|
"x": x,
|
|
"y": y,
|
|
"button": "left",
|
|
"clickCount": 1,
|
|
}
|
|
if _, err := c.sendCDP("Input.dispatchMouseEvent", clickParams); err != nil {
|
|
return fmt.Errorf("cdp click xy human: mousePressed: %w", err)
|
|
}
|
|
|
|
// Pausa entre press y release según el modo de velocidad.
|
|
if pms := clickPauseMs(opts.Mode); pms > 0 {
|
|
time.Sleep(time.Duration(pms) * time.Millisecond)
|
|
}
|
|
|
|
clickParams["type"] = "mouseReleased"
|
|
if _, err := c.sendCDP("Input.dispatchMouseEvent", clickParams); err != nil {
|
|
return fmt.Errorf("cdp click xy human: mouseReleased: %w", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// clickPauseMs devuelve la pausa (ms) entre press y release según el modo de
|
|
// velocidad: human 30-90, auto/fast 5-15, instant 0.
|
|
func clickPauseMs(mode string) int {
|
|
switch mode {
|
|
case "instant":
|
|
return 0
|
|
case "fast", "auto":
|
|
return 5 + rand.Intn(11) // 5..15
|
|
default: // "human" o ""
|
|
return 30 + rand.Intn(61) // 30..90
|
|
}
|
|
}
|