package main import ( "fmt" "os" "gopkg.in/yaml.v3" ) // Script representa un archivo YAML de pasos de navegacion. type Script struct { Name string `yaml:"name"` Steps []Step `yaml:"steps"` } // Step es un paso individual dentro del script. type Step struct { // Comun a todos los pasos Action string `yaml:"action"` ContinueOnError bool `yaml:"continue_on_error"` // navigate URL string `yaml:"url"` // wait Selector string `yaml:"selector"` TimeoutMs int `yaml:"timeout_ms"` // type Text string `yaml:"text"` // screenshot Path string `yaml:"path"` FullPage bool `yaml:"full_page"` // evaluate Expr string `yaml:"expr"` // sleep Ms int `yaml:"ms"` } // Validate comprueba que el script tiene los campos minimos correctos. func (s *Script) Validate() error { if s.Name == "" { return fmt.Errorf("script: campo 'name' obligatorio") } if len(s.Steps) == 0 { return fmt.Errorf("script %q: sin pasos definidos", s.Name) } for i, step := range s.Steps { if err := step.Validate(i); err != nil { return err } } return nil } // Validate comprueba que el paso tiene los campos requeridos segun su action. func (s *Step) Validate(idx int) error { prefix := fmt.Sprintf("step[%d] action=%q", idx, s.Action) switch s.Action { case "navigate": if s.URL == "" { return fmt.Errorf("%s: campo 'url' obligatorio", prefix) } case "wait": if s.Selector == "" { return fmt.Errorf("%s: campo 'selector' obligatorio", prefix) } case "click": if s.Selector == "" { return fmt.Errorf("%s: campo 'selector' obligatorio", prefix) } case "type": if s.Selector == "" { return fmt.Errorf("%s: campo 'selector' obligatorio", prefix) } if s.Text == "" { return fmt.Errorf("%s: campo 'text' obligatorio", prefix) } case "screenshot": if s.Path == "" { return fmt.Errorf("%s: campo 'path' obligatorio", prefix) } case "evaluate": if s.Expr == "" { return fmt.Errorf("%s: campo 'expr' obligatorio", prefix) } case "get_html": // sin parametros requeridos case "wait_load": // sin parametros requeridos (timeout_ms opcional) case "sleep": if s.Ms <= 0 { return fmt.Errorf("%s: campo 'ms' debe ser mayor que 0", prefix) } default: return fmt.Errorf("%s: accion desconocida (navigate|wait|wait_load|click|type|screenshot|evaluate|get_html|sleep)", prefix) } return nil } // LoadScript lee y parsea un archivo YAML de script de navegador. func LoadScript(path string) (*Script, error) { data, err := os.ReadFile(path) if err != nil { return nil, fmt.Errorf("leer script %q: %w", path, err) } var s Script if err := yaml.Unmarshal(data, &s); err != nil { return nil, fmt.Errorf("parsear script %q: %w", path, err) } if err := s.Validate(); err != nil { return nil, err } return &s, nil }