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