108 lines
3.2 KiB
Go
108 lines
3.2 KiB
Go
// Package safety provides anti-detection measures: session timing,
|
|
// break scheduling, and behavioral pattern randomization.
|
|
package safety
|
|
|
|
import (
|
|
"math/rand"
|
|
"time"
|
|
)
|
|
|
|
// SessionConfig defines play session parameters.
|
|
type SessionConfig struct {
|
|
MinSessionHours float64
|
|
MaxSessionHours float64
|
|
MinBreakMinutes float64
|
|
MaxBreakMinutes float64
|
|
MaxDailyHours float64
|
|
MicroBreakMinSec int
|
|
MicroBreakMaxSec int
|
|
MicroBreakIntervalMinSec int
|
|
MicroBreakIntervalMaxSec int
|
|
}
|
|
|
|
// DefaultSessionConfig returns realistic session timing.
|
|
func DefaultSessionConfig() SessionConfig {
|
|
return SessionConfig{
|
|
MinSessionHours: 1.0,
|
|
MaxSessionHours: 4.0,
|
|
MinBreakMinutes: 10.0,
|
|
MaxBreakMinutes: 45.0,
|
|
MaxDailyHours: 12.0,
|
|
MicroBreakMinSec: 2,
|
|
MicroBreakMaxSec: 8,
|
|
MicroBreakIntervalMinSec: 120,
|
|
MicroBreakIntervalMaxSec: 300,
|
|
}
|
|
}
|
|
|
|
// SessionTimer manages play session duration and breaks.
|
|
type SessionTimer struct {
|
|
config SessionConfig
|
|
sessionStart time.Time
|
|
dailyPlaytime time.Duration
|
|
targetDuration time.Duration
|
|
nextMicroBreak time.Time
|
|
}
|
|
|
|
// NewSessionTimer creates a session timer.
|
|
func NewSessionTimer(config SessionConfig) *SessionTimer {
|
|
st := &SessionTimer{
|
|
config: config,
|
|
sessionStart: time.Now(),
|
|
}
|
|
st.targetDuration = st.rollSessionDuration()
|
|
st.nextMicroBreak = st.scheduleMicroBreak()
|
|
return st
|
|
}
|
|
|
|
// ShouldStopSession returns true if the current session should end.
|
|
func (s *SessionTimer) ShouldStopSession() bool {
|
|
elapsed := time.Since(s.sessionStart)
|
|
if elapsed >= s.targetDuration {
|
|
return true
|
|
}
|
|
if s.dailyPlaytime+elapsed >= time.Duration(s.config.MaxDailyHours*float64(time.Hour)) {
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
// ShouldMicroBreak returns the break duration if it's time, or 0.
|
|
func (s *SessionTimer) ShouldMicroBreak() time.Duration {
|
|
if time.Now().After(s.nextMicroBreak) {
|
|
duration := time.Duration(s.config.MicroBreakMinSec+rand.Intn(s.config.MicroBreakMaxSec-s.config.MicroBreakMinSec+1)) * time.Second
|
|
s.nextMicroBreak = s.scheduleMicroBreak()
|
|
return duration
|
|
}
|
|
return 0
|
|
}
|
|
|
|
// BreakDuration returns a randomized long break duration.
|
|
func (s *SessionTimer) BreakDuration() time.Duration {
|
|
minMs := int(s.config.MinBreakMinutes * 60 * 1000)
|
|
maxMs := int(s.config.MaxBreakMinutes * 60 * 1000)
|
|
return time.Duration(minMs+rand.Intn(maxMs-minMs)) * time.Millisecond
|
|
}
|
|
|
|
// StartNewSession resets for a new play session.
|
|
func (s *SessionTimer) StartNewSession() {
|
|
s.dailyPlaytime += time.Since(s.sessionStart)
|
|
s.sessionStart = time.Now()
|
|
s.targetDuration = s.rollSessionDuration()
|
|
}
|
|
|
|
// Elapsed returns time in current session.
|
|
func (s *SessionTimer) Elapsed() time.Duration {
|
|
return time.Since(s.sessionStart)
|
|
}
|
|
|
|
func (s *SessionTimer) rollSessionDuration() time.Duration {
|
|
minMs := int(s.config.MinSessionHours * 3600 * 1000)
|
|
maxMs := int(s.config.MaxSessionHours * 3600 * 1000)
|
|
return time.Duration(minMs+rand.Intn(maxMs-minMs)) * time.Millisecond
|
|
}
|
|
|
|
func (s *SessionTimer) scheduleMicroBreak() time.Time {
|
|
interval := s.config.MicroBreakIntervalMinSec + rand.Intn(s.config.MicroBreakIntervalMaxSec-s.config.MicroBreakIntervalMinSec+1)
|
|
return time.Now().Add(time.Duration(interval) * time.Second)
|
|
}
|