Initial commit: navegator - Chrome CDP automation for LLMs
Add complete navegator system for stealthy browser automation: - CDP client with WebSocket communication - Browser API with navigation, storage, network, runtime - Stealth flags and anti-detection scripts - Persistent profile support - Examples and comprehensive documentation Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,347 @@
|
||||
package browser
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Cookie representa una cookie.
|
||||
type Cookie struct {
|
||||
Name string `json:"name"`
|
||||
Value string `json:"value"`
|
||||
Domain string `json:"domain,omitempty"`
|
||||
Path string `json:"path,omitempty"`
|
||||
Expires float64 `json:"expires,omitempty"` // Unix timestamp
|
||||
HTTPOnly bool `json:"httpOnly,omitempty"`
|
||||
Secure bool `json:"secure,omitempty"`
|
||||
SameSite string `json:"sameSite,omitempty"` // "Strict", "Lax", "None"
|
||||
}
|
||||
|
||||
// GetCookies obtiene todas las cookies o las de un dominio específico.
|
||||
func (b *Browser) GetCookies(ctx context.Context, urls ...string) ([]*Cookie, error) {
|
||||
params := make(map[string]interface{})
|
||||
if len(urls) > 0 {
|
||||
params["urls"] = urls
|
||||
}
|
||||
|
||||
var result struct {
|
||||
Cookies []*Cookie `json:"cookies"`
|
||||
}
|
||||
|
||||
if err := b.cdpClient.Execute(ctx, "Network.getCookies", params, &result); err != nil {
|
||||
return nil, fmt.Errorf("failed to get cookies: %w", err)
|
||||
}
|
||||
|
||||
return result.Cookies, nil
|
||||
}
|
||||
|
||||
// SetCookie establece una cookie.
|
||||
func (b *Browser) SetCookie(ctx context.Context, cookie *Cookie) error {
|
||||
params := map[string]interface{}{
|
||||
"name": cookie.Name,
|
||||
"value": cookie.Value,
|
||||
}
|
||||
|
||||
if cookie.Domain != "" {
|
||||
params["domain"] = cookie.Domain
|
||||
}
|
||||
if cookie.Path != "" {
|
||||
params["path"] = cookie.Path
|
||||
}
|
||||
if cookie.Expires > 0 {
|
||||
params["expires"] = cookie.Expires
|
||||
}
|
||||
if cookie.HTTPOnly {
|
||||
params["httpOnly"] = true
|
||||
}
|
||||
if cookie.Secure {
|
||||
params["secure"] = true
|
||||
}
|
||||
if cookie.SameSite != "" {
|
||||
params["sameSite"] = cookie.SameSite
|
||||
}
|
||||
|
||||
var result struct {
|
||||
Success bool `json:"success"`
|
||||
}
|
||||
|
||||
if err := b.cdpClient.Execute(ctx, "Network.setCookie", params, &result); err != nil {
|
||||
return fmt.Errorf("failed to set cookie: %w", err)
|
||||
}
|
||||
|
||||
if !result.Success {
|
||||
return fmt.Errorf("failed to set cookie: %s", cookie.Name)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetCookies establece múltiples cookies.
|
||||
func (b *Browser) SetCookies(ctx context.Context, cookies []*Cookie) error {
|
||||
for _, cookie := range cookies {
|
||||
if err := b.SetCookie(ctx, cookie); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeleteCookie elimina una cookie específica.
|
||||
func (b *Browser) DeleteCookie(ctx context.Context, name string, domain string) error {
|
||||
params := map[string]interface{}{
|
||||
"name": name,
|
||||
}
|
||||
|
||||
if domain != "" {
|
||||
params["domain"] = domain
|
||||
}
|
||||
|
||||
return b.cdpClient.Execute(ctx, "Network.deleteCookies", params, nil)
|
||||
}
|
||||
|
||||
// ClearCookies elimina todas las cookies.
|
||||
func (b *Browser) ClearCookies(ctx context.Context) error {
|
||||
return b.cdpClient.Execute(ctx, "Network.clearBrowserCookies", nil, nil)
|
||||
}
|
||||
|
||||
// LocalStorageItem representa un item de localStorage.
|
||||
type LocalStorageItem struct {
|
||||
Key string `json:"key"`
|
||||
Value string `json:"value"`
|
||||
}
|
||||
|
||||
// GetLocalStorage obtiene todos los items del localStorage.
|
||||
func (b *Browser) GetLocalStorage(ctx context.Context) ([]*LocalStorageItem, error) {
|
||||
script := `
|
||||
(() => {
|
||||
const items = [];
|
||||
for (let i = 0; i < localStorage.length; i++) {
|
||||
const key = localStorage.key(i);
|
||||
items.push({ key: key, value: localStorage.getItem(key) });
|
||||
}
|
||||
return items;
|
||||
})()
|
||||
`
|
||||
|
||||
var result struct {
|
||||
Result struct {
|
||||
Value []map[string]interface{} `json:"value"`
|
||||
} `json:"result"`
|
||||
}
|
||||
|
||||
params := map[string]interface{}{
|
||||
"expression": script,
|
||||
"returnByValue": true,
|
||||
}
|
||||
|
||||
if err := b.cdpClient.Execute(ctx, "Runtime.evaluate", params, &result); err != nil {
|
||||
return nil, fmt.Errorf("failed to get localStorage: %w", err)
|
||||
}
|
||||
|
||||
items := make([]*LocalStorageItem, 0, len(result.Result.Value))
|
||||
for _, item := range result.Result.Value {
|
||||
items = append(items, &LocalStorageItem{
|
||||
Key: item["key"].(string),
|
||||
Value: item["value"].(string),
|
||||
})
|
||||
}
|
||||
|
||||
return items, nil
|
||||
}
|
||||
|
||||
// SetLocalStorage establece un item en localStorage.
|
||||
func (b *Browser) SetLocalStorage(ctx context.Context, key, value string) error {
|
||||
script := fmt.Sprintf(`localStorage.setItem(%q, %q)`, key, value)
|
||||
|
||||
params := map[string]interface{}{
|
||||
"expression": script,
|
||||
}
|
||||
|
||||
return b.cdpClient.Execute(ctx, "Runtime.evaluate", params, nil)
|
||||
}
|
||||
|
||||
// RemoveLocalStorage elimina un item de localStorage.
|
||||
func (b *Browser) RemoveLocalStorage(ctx context.Context, key string) error {
|
||||
script := fmt.Sprintf(`localStorage.removeItem(%q)`, key)
|
||||
|
||||
params := map[string]interface{}{
|
||||
"expression": script,
|
||||
}
|
||||
|
||||
return b.cdpClient.Execute(ctx, "Runtime.evaluate", params, nil)
|
||||
}
|
||||
|
||||
// ClearLocalStorage limpia todo el localStorage.
|
||||
func (b *Browser) ClearLocalStorage(ctx context.Context) error {
|
||||
params := map[string]interface{}{
|
||||
"expression": "localStorage.clear()",
|
||||
}
|
||||
|
||||
return b.cdpClient.Execute(ctx, "Runtime.evaluate", params, nil)
|
||||
}
|
||||
|
||||
// GetSessionStorage obtiene todos los items del sessionStorage.
|
||||
func (b *Browser) GetSessionStorage(ctx context.Context) ([]*LocalStorageItem, error) {
|
||||
script := `
|
||||
(() => {
|
||||
const items = [];
|
||||
for (let i = 0; i < sessionStorage.length; i++) {
|
||||
const key = sessionStorage.key(i);
|
||||
items.push({ key: key, value: sessionStorage.getItem(key) });
|
||||
}
|
||||
return items;
|
||||
})()
|
||||
`
|
||||
|
||||
var result struct {
|
||||
Result struct {
|
||||
Value []map[string]interface{} `json:"value"`
|
||||
} `json:"result"`
|
||||
}
|
||||
|
||||
params := map[string]interface{}{
|
||||
"expression": script,
|
||||
"returnByValue": true,
|
||||
}
|
||||
|
||||
if err := b.cdpClient.Execute(ctx, "Runtime.evaluate", params, &result); err != nil {
|
||||
return nil, fmt.Errorf("failed to get sessionStorage: %w", err)
|
||||
}
|
||||
|
||||
items := make([]*LocalStorageItem, 0, len(result.Result.Value))
|
||||
for _, item := range result.Result.Value {
|
||||
items = append(items, &LocalStorageItem{
|
||||
Key: item["key"].(string),
|
||||
Value: item["value"].(string),
|
||||
})
|
||||
}
|
||||
|
||||
return items, nil
|
||||
}
|
||||
|
||||
// SetSessionStorage establece un item en sessionStorage.
|
||||
func (b *Browser) SetSessionStorage(ctx context.Context, key, value string) error {
|
||||
script := fmt.Sprintf(`sessionStorage.setItem(%q, %q)`, key, value)
|
||||
|
||||
params := map[string]interface{}{
|
||||
"expression": script,
|
||||
}
|
||||
|
||||
return b.cdpClient.Execute(ctx, "Runtime.evaluate", params, nil)
|
||||
}
|
||||
|
||||
// RemoveSessionStorage elimina un item de sessionStorage.
|
||||
func (b *Browser) RemoveSessionStorage(ctx context.Context, key string) error {
|
||||
script := fmt.Sprintf(`sessionStorage.removeItem(%q)`, key)
|
||||
|
||||
params := map[string]interface{}{
|
||||
"expression": script,
|
||||
}
|
||||
|
||||
return b.cdpClient.Execute(ctx, "Runtime.evaluate", params, nil)
|
||||
}
|
||||
|
||||
// ClearSessionStorage limpia todo el sessionStorage.
|
||||
func (b *Browser) ClearSessionStorage(ctx context.Context) error {
|
||||
params := map[string]interface{}{
|
||||
"expression": "sessionStorage.clear()",
|
||||
}
|
||||
|
||||
return b.cdpClient.Execute(ctx, "Runtime.evaluate", params, nil)
|
||||
}
|
||||
|
||||
// StorageType tipo de storage.
|
||||
type StorageType string
|
||||
|
||||
const (
|
||||
StorageTypeCookies StorageType = "cookies"
|
||||
StorageTypeLocalStorage StorageType = "local_storage"
|
||||
StorageTypeSessionStorage StorageType = "session_storage"
|
||||
StorageTypeIndexedDB StorageType = "indexeddb"
|
||||
StorageTypeWebSQL StorageType = "websql"
|
||||
StorageTypeCacheStorage StorageType = "cache_storage"
|
||||
)
|
||||
|
||||
// ClearDataForOrigin limpia datos de un origen específico.
|
||||
func (b *Browser) ClearDataForOrigin(ctx context.Context, origin string, storageTypes ...StorageType) error {
|
||||
if len(storageTypes) == 0 {
|
||||
// Por defecto, limpiar todo
|
||||
storageTypes = []StorageType{
|
||||
StorageTypeCookies,
|
||||
StorageTypeLocalStorage,
|
||||
StorageTypeSessionStorage,
|
||||
StorageTypeIndexedDB,
|
||||
StorageTypeWebSQL,
|
||||
StorageTypeCacheStorage,
|
||||
}
|
||||
}
|
||||
|
||||
// Convertir a string separado por comas
|
||||
types := ""
|
||||
for i, st := range storageTypes {
|
||||
if i > 0 {
|
||||
types += ","
|
||||
}
|
||||
types += string(st)
|
||||
}
|
||||
|
||||
params := map[string]interface{}{
|
||||
"origin": origin,
|
||||
"storageTypes": types,
|
||||
}
|
||||
|
||||
return b.cdpClient.Execute(ctx, "Storage.clearDataForOrigin", params, nil)
|
||||
}
|
||||
|
||||
// ExportCookies exporta las cookies del perfil a un archivo JSON.
|
||||
func (b *Browser) ExportCookies(ctx context.Context) ([]*Cookie, error) {
|
||||
return b.GetCookies(ctx)
|
||||
}
|
||||
|
||||
// ImportCookies importa cookies desde un slice.
|
||||
func (b *Browser) ImportCookies(ctx context.Context, cookies []*Cookie) error {
|
||||
return b.SetCookies(ctx, cookies)
|
||||
}
|
||||
|
||||
// CreateCookie crea una cookie helper.
|
||||
func CreateCookie(name, value, domain string) *Cookie {
|
||||
return &Cookie{
|
||||
Name: name,
|
||||
Value: value,
|
||||
Domain: domain,
|
||||
Path: "/",
|
||||
// Expira en 1 año
|
||||
Expires: float64(time.Now().Add(365 * 24 * time.Hour).Unix()),
|
||||
HTTPOnly: false,
|
||||
Secure: false,
|
||||
SameSite: "Lax",
|
||||
}
|
||||
}
|
||||
|
||||
// CreateSessionCookie crea una cookie de sesión (sin expiración).
|
||||
func CreateSessionCookie(name, value, domain string) *Cookie {
|
||||
return &Cookie{
|
||||
Name: name,
|
||||
Value: value,
|
||||
Domain: domain,
|
||||
Path: "/",
|
||||
HTTPOnly: false,
|
||||
Secure: false,
|
||||
SameSite: "Lax",
|
||||
}
|
||||
}
|
||||
|
||||
// CreateSecureCookie crea una cookie segura (HTTPS only).
|
||||
func CreateSecureCookie(name, value, domain string) *Cookie {
|
||||
return &Cookie{
|
||||
Name: name,
|
||||
Value: value,
|
||||
Domain: domain,
|
||||
Path: "/",
|
||||
Expires: float64(time.Now().Add(365 * 24 * time.Hour).Unix()),
|
||||
HTTPOnly: true,
|
||||
Secure: true,
|
||||
SameSite: "Strict",
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user