package feichibridge import ( "bytes" "context" "errors" "fmt" "os/exec" "regexp" "strings" "time" ) var captchaPattern = regexp.MustCompile(`^[0-9A-Z]{4}$`) type CaptchaSolver interface { Solve(context.Context, []byte) (string, error) } type DockerCaptchaSolver struct { Image string Timeout time.Duration } func (s DockerCaptchaSolver) Solve(ctx context.Context, image []byte) (string, error) { if len(image) == 0 { return "", errors.New("captcha image is empty") } if strings.TrimSpace(s.Image) == "" { return "", errors.New("captcha OCR image is required") } timeout := s.Timeout if timeout <= 0 { timeout = 20 * time.Second } solveCtx, cancel := context.WithTimeout(ctx, timeout) defer cancel() command := exec.CommandContext( solveCtx, "docker", "run", "--rm", "--network=none", "-i", strings.TrimSpace(s.Image), ) command.Stdin = bytes.NewReader(image) var stderr bytes.Buffer command.Stderr = &stderr output, err := command.Output() if err != nil { if solveCtx.Err() != nil { return "", fmt.Errorf("captcha OCR timed out: %w", solveCtx.Err()) } return "", fmt.Errorf("captcha OCR failed: %w: %s", err, strings.TrimSpace(stderr.String())) } code := strings.ToUpper(strings.TrimSpace(string(output))) if !captchaPattern.MatchString(code) { return "", fmt.Errorf("captcha OCR returned invalid result %q", code) } return code, nil }