package browser import ( "context" "fmt" "os" "path/filepath" "strings" ) // ExtensionConfig configuración de una extensión de Chrome type ExtensionConfig struct { Path string // Ruta a extensión (carpeta o .crx) ID string // ID de extensión (opcional) Enabled bool // Habilitada por defecto Settings map[string]string // Configuración específica } // Extension representa una extensión instalada type Extension struct { ID string Name string Version string Path string Enabled bool Description string } // PresetExtensions configuraciones de extensiones populares var PresetExtensions = map[string]*ExtensionConfig{ "ublock-origin": { ID: "cjpalhdlnbpafiamejdnhcphjbkeiagm", Enabled: true, }, "tampermonkey": { ID: "dhdgffkkebhmkfjojejmpbldmpobfkfo", Enabled: true, }, } // LoadPresetExtension carga una configuración de extensión predefinida func LoadPresetExtension(name string) (*ExtensionConfig, error) { preset, ok := PresetExtensions[name] if !ok { return nil, fmt.Errorf("unknown preset extension: %s", name) } // Buscar extensión en directorio compartido homeDir, err := os.UserHomeDir() if err != nil { return nil, err } extPath := filepath.Join(homeDir, ".navegator", "extensions", name) if _, err := os.Stat(extPath); err == nil { preset.Path = extPath } return preset, nil } // buildExtensionFlags construye las flags de Chrome para cargar extensiones func (c *Config) buildExtensionFlags() []string { if len(c.Extensions) == 0 { return nil } var flags []string var paths []string for _, ext := range c.Extensions { if ext.Path != "" && ext.Enabled { // Expandir ~ si es necesario path := ext.Path if strings.HasPrefix(path, "~") { homeDir, _ := os.UserHomeDir() path = filepath.Join(homeDir, path[1:]) } paths = append(paths, path) } } if len(paths) > 0 { // Cargar extensiones específicas flags = append(flags, fmt.Sprintf("--load-extension=%s", strings.Join(paths, ","))) // Si se especificó, deshabilitar todas las otras extensiones if c.DisableOtherExts { flags = append(flags, fmt.Sprintf("--disable-extensions-except=%s", strings.Join(paths, ","))) } } return flags } // GetLoadedExtensions obtiene información sobre extensiones cargadas // Nota: CDP no tiene API directa para esto, usamos técnicas indirectas func (b *Browser) GetLoadedExtensions(ctx context.Context) ([]*Extension, error) { // Intentar obtener extensiones via JavaScript script := ` (function() { // No hay API directa en página normal para listar extensiones // Retornar info básica si está disponible return []; })(); ` result, err := b.Evaluate(ctx, script) if err != nil { return nil, err } var extensions []*Extension // Parse result... _ = result return extensions, nil } // NavigateToExtensionPage navega a la página de gestión de una extensión func (b *Browser) NavigateToExtensionPage(ctx context.Context, extensionID string, page string) error { url := fmt.Sprintf("chrome-extension://%s/%s", extensionID, page) return b.Navigate(ctx, url, nil) } // SendMessageToExtension envía un mensaje a una extensión // Útil para configurar extensiones programáticamente func (b *Browser) SendMessageToExtension(ctx context.Context, extensionID string, message map[string]interface{}) (interface{}, error) { script := fmt.Sprintf(` new Promise((resolve, reject) => { chrome.runtime.sendMessage('%s', %v, (response) => { if (chrome.runtime.lastError) { reject(chrome.runtime.lastError); } else { resolve(response); } }); }); `, extensionID, message) result, err := b.EvaluateAsync(ctx, script) if err != nil { return nil, fmt.Errorf("error sending message to extension: %w", err) } return result.Value, nil } // SetupUBlockOrigin configura uBlock Origin con listas de filtros personalizadas func (b *Browser) SetupUBlockOrigin(ctx context.Context, filterLists []string) error { // Navegar a la página de configuración if err := b.NavigateToExtensionPage(ctx, "cjpalhdlnbpafiamejdnhcphjbkeiagm", "dashboard.html"); err != nil { return err } // Configurar listas de filtros via JavaScript script := fmt.Sprintf(` (function() { // Acceder a la configuración de uBlock const lists = %v; // Agregar listas personalizadas // Esto depende de la API interna de uBlock return 'configured'; })(); `, filterLists) _, err := b.Evaluate(ctx, script) return err } // InstallExtensionFromStore descarga e instala extensión desde Chrome Web Store // Nota: Esto requiere interacción con el Web Store y puede ser bloqueado func (b *Browser) InstallExtensionFromStore(ctx context.Context, extensionID string) error { url := fmt.Sprintf("https://chrome.google.com/webstore/detail/%s", extensionID) if err := b.Navigate(ctx, url, nil); err != nil { return err } // Intentar hacer click en botón de instalación // Nota: Esto puede requerir permisos especiales script := ` const button = document.querySelector('button[aria-label*="Add"]'); if (button) { button.click(); return true; } return false; ` result, err := b.Evaluate(ctx, script) if err != nil { return err } if clicked, ok := result.Value.(bool); !ok || !clicked { return fmt.Errorf("could not find install button") } return nil } // EnsureExtensionsDirectory crea el directorio de extensiones si no existe func EnsureExtensionsDirectory() (string, error) { homeDir, err := os.UserHomeDir() if err != nil { return "", err } extDir := filepath.Join(homeDir, ".navegator", "extensions") if err := os.MkdirAll(extDir, 0755); err != nil { return "", err } return extDir, nil } // GetExtensionPath retorna la ruta a una extensión en el directorio compartido func GetExtensionPath(name string) (string, error) { extDir, err := EnsureExtensionsDirectory() if err != nil { return "", err } path := filepath.Join(extDir, name) if _, err := os.Stat(path); os.IsNotExist(err) { return "", fmt.Errorf("extension not found: %s", name) } return path, nil } // ListLocalExtensions lista extensiones disponibles en el directorio local func ListLocalExtensions() ([]string, error) { extDir, err := EnsureExtensionsDirectory() if err != nil { return nil, err } entries, err := os.ReadDir(extDir) if err != nil { return nil, err } var extensions []string for _, entry := range entries { if entry.IsDir() { // Verificar que tenga manifest.json manifestPath := filepath.Join(extDir, entry.Name(), "manifest.json") if _, err := os.Stat(manifestPath); err == nil { extensions = append(extensions, entry.Name()) } } } return extensions, nil }