58 lines
1.4 KiB
Go
58 lines
1.4 KiB
Go
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
|
|
}
|