// Package input — humanize.go provides human-like behavior randomization. package input import ( "image" "math/rand" "sync/atomic" "time" ) // HumanProfile defines behavior parameters for human-like input. type HumanProfile struct { ReactionMin time.Duration // min reaction delay ReactionMax time.Duration // max reaction delay MouseSpeedMin float64 // min pixels per second MouseSpeedMax float64 // max pixels per second ClickJitter int // pixel jitter on clicks HesitationChance float64 // chance of extra pause (0-1) HesitationMin time.Duration HesitationMax time.Duration } // DefaultProfile returns a realistic human behavior profile. func DefaultProfile() HumanProfile { return HumanProfile{ ReactionMin: 150 * time.Millisecond, ReactionMax: 450 * time.Millisecond, MouseSpeedMin: 400, MouseSpeedMax: 1200, ClickJitter: 3, HesitationChance: 0.1, HesitationMin: 300 * time.Millisecond, HesitationMax: 1200 * time.Millisecond, } } // Humanizer applies human-like randomization to bot actions. type Humanizer struct { Profile HumanProfile actionCount atomic.Int64 } // NewHumanizer creates a humanizer with the given profile. func NewHumanizer(profile HumanProfile) *Humanizer { return &Humanizer{Profile: profile} } // ReactionDelay returns a randomized human-like reaction delay. func (h *Humanizer) ReactionDelay() time.Duration { minMs := h.Profile.ReactionMin.Milliseconds() maxMs := h.Profile.ReactionMax.Milliseconds() base := time.Duration(minMs+rand.Int63n(maxMs-minMs)) * time.Millisecond // Occasional hesitation if rand.Float64() < h.Profile.HesitationChance { hesMinMs := h.Profile.HesitationMin.Milliseconds() hesMaxMs := h.Profile.HesitationMax.Milliseconds() base += time.Duration(hesMinMs+rand.Int63n(hesMaxMs-hesMinMs)) * time.Millisecond } // Slight fatigue factor actions := float64(h.actionCount.Load()) fatigue := min(actions/1000.0, 0.3) base = time.Duration(float64(base) * (1 + fatigue*rand.Float64())) return base } // Wait pauses for a human-like reaction delay. func (h *Humanizer) Wait() { time.Sleep(h.ReactionDelay()) h.actionCount.Add(1) } // WaitMs pauses for baseMs ± varianceMs with human randomization. func (h *Humanizer) WaitMs(baseMs, varianceMs int) { actual := baseMs + rand.Intn(2*varianceMs+1) - varianceMs if actual < 0 { actual = 0 } time.Sleep(time.Duration(actual) * time.Millisecond) } // JitterPosition adds random offset to a click position. func (h *Humanizer) JitterPosition(pos image.Point) image.Point { j := h.Profile.ClickJitter return image.Point{ X: pos.X + rand.Intn(2*j+1) - j, Y: pos.Y + rand.Intn(2*j+1) - j, } } // MouseSpeed returns a randomized mouse movement speed. func (h *Humanizer) MouseSpeed() float64 { return h.Profile.MouseSpeedMin + rand.Float64()*(h.Profile.MouseSpeedMax-h.Profile.MouseSpeedMin) } func min(a, b float64) float64 { if a < b { return a } return b }