feat(browser): funciones anti-deteccion + perfiles para web_scraping
Funciones nuevas del dominio browser (grupo navegator): - cdp_move_mouse_human / cdp_click_human: movimiento de raton con curva de Bezier cubica, easing y micro-jitter para imitar comportamiento humano y reducir deteccion de automatizacion. - cdp_wait_idle: espera network-idle contando requests en vuelo via eventos CDP Network.*; inmune a extensiones que mutan el DOM (Dark Reader, uBlock) y a animaciones JS. - list_chrome_profiles: lista perfiles de un user-data-dir (extensiones, nombre legible, preferencias). - prepare_chrome_profile (bash): clona un user-data-dir conservando solo una whitelist de extensiones (default uBlock Origin Lite). Modificadas: - chrome_launch: Linux-first (chromium/google-chrome/brave antes que chrome.exe), KeepExtensions y Setpgid para matar el arbol con cdp_close. - cdp_close: kill por grupo de proceso. Todas con tests verdes (go test ./functions/browser ok).
This commit is contained in:
@@ -0,0 +1,103 @@
|
||||
package browser
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
)
|
||||
|
||||
// ChromeProfile holds metadata about a single Chrome/Chromium profile directory.
|
||||
type ChromeProfile struct {
|
||||
Dir string // directory name (value for --profile-directory), e.g. "Default"
|
||||
Name string // human-readable name from Local State info_cache, e.g. "Personal"
|
||||
Extensions int // number of installed extension dirs under <dir>/Extensions (excluding "Temp")
|
||||
HasPreferences bool // true if <dir>/Preferences file exists
|
||||
}
|
||||
|
||||
// localState mirrors the parts of Local State we need.
|
||||
type localState struct {
|
||||
Profile struct {
|
||||
InfoCache map[string]struct {
|
||||
Name string `json:"name"`
|
||||
} `json:"info_cache"`
|
||||
} `json:"profile"`
|
||||
}
|
||||
|
||||
// ListChromeProfiles scans userDataDir and returns one ChromeProfile per
|
||||
// subdirectory that contains a Preferences file (excluding "System Profile").
|
||||
// If userDataDir is empty it defaults to ~/.config/chromium.
|
||||
// Names are resolved from Local State; if that file is missing or unparseable
|
||||
// the profile Name field equals Dir.
|
||||
func ListChromeProfiles(userDataDir string) ([]ChromeProfile, error) {
|
||||
if userDataDir == "" {
|
||||
home, err := os.UserHomeDir()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
userDataDir = filepath.Join(home, ".config", "chromium")
|
||||
}
|
||||
|
||||
// Parse Local State for human-readable names. Failure is non-fatal.
|
||||
names := make(map[string]string)
|
||||
lsPath := filepath.Join(userDataDir, "Local State")
|
||||
if data, err := os.ReadFile(lsPath); err == nil {
|
||||
var ls localState
|
||||
if json.Unmarshal(data, &ls) == nil {
|
||||
for dir, info := range ls.Profile.InfoCache {
|
||||
names[dir] = info.Name
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
entries, err := os.ReadDir(userDataDir)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var profiles []ChromeProfile
|
||||
for _, e := range entries {
|
||||
if !e.IsDir() {
|
||||
continue
|
||||
}
|
||||
dir := e.Name()
|
||||
if dir == "System Profile" {
|
||||
continue
|
||||
}
|
||||
|
||||
prefPath := filepath.Join(userDataDir, dir, "Preferences")
|
||||
info, err := os.Stat(prefPath)
|
||||
if err != nil || info.IsDir() {
|
||||
continue
|
||||
}
|
||||
|
||||
// Count extension directories (excluding "Temp").
|
||||
extCount := 0
|
||||
extDir := filepath.Join(userDataDir, dir, "Extensions")
|
||||
if exts, err := os.ReadDir(extDir); err == nil {
|
||||
for _, ext := range exts {
|
||||
if ext.IsDir() && ext.Name() != "Temp" {
|
||||
extCount++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
name := names[dir]
|
||||
if name == "" {
|
||||
name = dir
|
||||
}
|
||||
|
||||
profiles = append(profiles, ChromeProfile{
|
||||
Dir: dir,
|
||||
Name: name,
|
||||
Extensions: extCount,
|
||||
HasPreferences: true,
|
||||
})
|
||||
}
|
||||
|
||||
sort.Slice(profiles, func(i, j int) bool {
|
||||
return profiles[i].Dir < profiles[j].Dir
|
||||
})
|
||||
|
||||
return profiles, nil
|
||||
}
|
||||
Reference in New Issue
Block a user