namespace RadicadeServer.Utils { public static class ModerationPolicy { public const string VoiceModerationBanMethod = "Voice Moderation"; public const string AutomaticTimeoutBanMethod = "Automatic Timeout"; public const string ModeratorReportBanMethod = "Moderator Report"; public const string TimeoutBanMethod = "Timeout"; public const string AutomaticVoiceModerationBanText = "You have been banned as a result of our voice moderation system detecting language that violates our code of conduct."; public static readonly TimeSpan DefaultTimeoutDuration = TimeSpan.FromMinutes(60); public static readonly TimeSpan MinimumTimeoutDuration = TimeSpan.FromMinutes(60); public static readonly TimeSpan MaximumTimeoutDuration = TimeSpan.FromHours(24); public static bool IsTimeout(string? banMethod) => Matches(banMethod, AutomaticTimeoutBanMethod) || Matches(banMethod, ModeratorReportBanMethod) || Matches(banMethod, TimeoutBanMethod); public static bool IsVoiceModeration(string? banMethod) => Matches(banMethod, VoiceModerationBanMethod); public static bool IssuesAppealCode(string? banMethod) => !IsTimeout(banMethod); public static bool RefusesAppeal(string? banMethod) => IsVoiceModeration(banMethod); public static TimeSpan ClampTimeout(TimeSpan duration) { if (duration < MinimumTimeoutDuration) return MinimumTimeoutDuration; if (duration > MaximumTimeoutDuration) return MaximumTimeoutDuration; return duration; } public static DateTime TimeoutExpiry(DateTime startedAtUtc, TimeSpan? duration = null) => startedAtUtc + ClampTimeout(duration ?? DefaultTimeoutDuration); public static DateTime ExtendedTimeoutExpiry(DateTime startedAtUtc, DateTime currentExpiryUtc, TimeSpan? duration = null) { var capped = startedAtUtc + MaximumTimeoutDuration; var proposed = TimeoutExpiry(startedAtUtc, duration); var expiry = proposed > currentExpiryUtc ? proposed : currentExpiryUtc; return expiry > capped ? capped : expiry; } private static bool Matches(string? banMethod, string expected) => string.Equals(banMethod?.Trim(), expected, StringComparison.OrdinalIgnoreCase); } }