mirror of
https://github.com/hackdiaz-dev/free-turn-proxy.git
synced 2026-10-12 09:41:53 +02:00
fix(captcha): разносить попытки слайдера по дорожке
Соседние кандидаты отличаются одним свапом и получают почти равный скор, поэтому топ-2 ранжирования - всегда пара соседей, и вторая попытка промахивается вместе с первой. Вторая берётся не ближе трёх позиций от первой, с откатом на соседей, если дальних кандидатов не хватило.
This commit is contained in:
@@ -137,3 +137,49 @@ func TestCaptchaDomainFromRedirectURI(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestPickSliderAttempts(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
indexes []int
|
||||
limit int
|
||||
want []int
|
||||
}{
|
||||
{
|
||||
name: "skips neighbours of the top guess",
|
||||
indexes: []int{20, 19, 21, 18, 40, 5},
|
||||
limit: 2,
|
||||
want: []int{20, 40},
|
||||
},
|
||||
{
|
||||
name: "falls back to neighbours when nothing is far enough",
|
||||
indexes: []int{20, 19, 21},
|
||||
limit: 3,
|
||||
want: []int{20, 19, 21},
|
||||
},
|
||||
{
|
||||
name: "limit above candidate count",
|
||||
indexes: []int{7},
|
||||
limit: 4,
|
||||
want: []int{7},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
guesses := make([]sliderGuess, 0, len(tt.indexes))
|
||||
for _, idx := range tt.indexes {
|
||||
guesses = append(guesses, sliderGuess{Index: idx})
|
||||
}
|
||||
got := pickSliderAttempts(guesses, tt.limit)
|
||||
if len(got) != len(tt.want) {
|
||||
t.Fatalf("len = %d, want %d", len(got), len(tt.want))
|
||||
}
|
||||
for i := range tt.want {
|
||||
if got[i].Index != tt.want[i] {
|
||||
t.Fatalf("attempts = %v, want %v", got, tt.want)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
_ "image/jpeg" // регистрация JPEG-декодера для image.Decode
|
||||
"math"
|
||||
"runtime"
|
||||
"slices"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -75,6 +76,7 @@ func (s *captchaSession) solveSliderCaptcha(
|
||||
if limit <= 0 {
|
||||
return "", errors.New("slider has no attempts available")
|
||||
}
|
||||
attempts := pickSliderAttempts(guesses, limit)
|
||||
s.logger().Debugf("[Captcha] slider guesses ranked: total=%d limit=%d", len(guesses), limit)
|
||||
|
||||
if err := s.sendComponentDone(sessionToken); err != nil {
|
||||
@@ -87,15 +89,15 @@ func (s *captchaSession) solveSliderCaptcha(
|
||||
if !s.profile.Touch() {
|
||||
handle = approachFrom(s.profile, s.layout, handle)
|
||||
}
|
||||
for i := 0; i < limit; i++ {
|
||||
s.logger().Debugf("[Captcha] slider attempt %d/%d (guess #%d)", i+1, limit, guesses[i].Index)
|
||||
for i, guess := range attempts {
|
||||
s.logger().Debugf("[Captcha] slider attempt %d/%d (guess #%d)", i+1, len(attempts), guess.Index)
|
||||
answerData, err := json.Marshal(struct {
|
||||
Value []int `json:"value"`
|
||||
}{Value: guesses[i].Swaps})
|
||||
}{Value: guess.Swaps})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
target := s.layout.sliderTarget(guesses[i].Index, len(guesses))
|
||||
target := s.layout.sliderTarget(guess.Index, len(guesses))
|
||||
g := gesture{
|
||||
from: handle,
|
||||
to: target,
|
||||
@@ -352,6 +354,30 @@ func rankSliderGuesses(img image.Image, gridSize int, swaps []int) ([]sliderGues
|
||||
return guesses, nil
|
||||
}
|
||||
|
||||
// pickSliderAttempts разносит попытки по дорожке: соседние кандидаты отличаются
|
||||
// одним свапом и получают почти равный скор, поэтому вторая попытка рядом с
|
||||
// первой промахивается вместе с ней.
|
||||
func pickSliderAttempts(guesses []sliderGuess, limit int) []sliderGuess {
|
||||
const minGap = 3
|
||||
out := make([]sliderGuess, 0, limit)
|
||||
taken := func(index, gap int) bool {
|
||||
return slices.ContainsFunc(out, func(g sliderGuess) bool {
|
||||
return absInt(g.Index-index) < gap
|
||||
})
|
||||
}
|
||||
for _, gap := range []int{minGap, 1} {
|
||||
for _, g := range guesses {
|
||||
if len(out) == limit {
|
||||
return out
|
||||
}
|
||||
if !taken(g.Index, gap) {
|
||||
out = append(out, g)
|
||||
}
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func activeSwapsForIndex(swaps []int, index int) []int {
|
||||
if index <= 0 {
|
||||
return []int{}
|
||||
|
||||
Reference in New Issue
Block a user