// 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) }