package app import ( "bytes" "context" "errors" "fmt" // image/png is registered by the digitocr package; jpeg/gif are registered // here so a future change of the captcha format still decodes. _ "image/gif" _ "image/jpeg" "github.com/geo-platform/tenant-api/internal/shared/digitocr" ) // meijiequanChallengeDigits is the fixed length of the meijiequan login captcha. const meijiequanChallengeDigits = 4 var errMeijiequanChallengeEmpty = errors.New("meijiequan challenge image is empty") // DigitOCRMeijiequanChallengeCodeResolver decodes the meijiequan numeric login // captcha locally via the digitocr template matcher, enabling unattended login // without an external OCR service. type DigitOCRMeijiequanChallengeCodeResolver struct{} // ResolveCode recognizes the digits in the captcha image. A decode failure is // returned as a non-challenge error so the login flow retries with a fresh // captcha rather than giving up immediately (as the manual resolver does). func (DigitOCRMeijiequanChallengeCodeResolver) ResolveCode(_ context.Context, challenge MeijiequanChallenge) (string, error) { if len(challenge.Image) == 0 { return "", errMeijiequanChallengeEmpty } code, err := digitocr.RecognizeReader(bytes.NewReader(challenge.Image), digitocr.Options{Digits: meijiequanChallengeDigits}) if err != nil { return "", fmt.Errorf("digitocr resolve meijiequan challenge: %w", err) } return code, nil }