using SharpChat.Configuration; using SharpChat.Protocol; using SharpChat.Users; using System; using System.Collections.Generic; namespace SharpChat.RateLimiting { public class RateLimitManager { public const int DEFAULT_USER_SIZE = 15; public const int DEFAULT_USER_WARN_SIZE = 10; public const int DEFAULT_CONN_SIZE = 30; public const int DEFAULT_MINIMUM_DELAY = 5000; public const int DEFAULT_KICK_LENGTH = 5; public const int DEFAULT_KICK_MULTIPLIER = 2; private CachedValue UserSizeValue { get; } private CachedValue UserWarnSizeValue { get; } private CachedValue ConnSizeValue { get; } private CachedValue MinimumDelayValue { get; } private CachedValue KickLengthValue { get; } private CachedValue KickMultiplierValue { get; } private readonly object ConnectionsSync = new(); private Dictionary Connections { get; } = new(); private readonly object UsersSync = new(); private Dictionary Users { get; } = new(); public RateLimitManager(IConfig config) { UserSizeValue = config.ReadCached(@"userSize", DEFAULT_USER_SIZE); UserWarnSizeValue = config.ReadCached(@"userWarnSize", DEFAULT_USER_WARN_SIZE); ConnSizeValue = config.ReadCached(@"connSize", DEFAULT_CONN_SIZE); MinimumDelayValue = config.ReadCached(@"minDelay", DEFAULT_MINIMUM_DELAY); KickLengthValue = config.ReadCached(@"kickLength", DEFAULT_KICK_LENGTH); KickMultiplierValue = config.ReadCached(@"kickMultiplier", DEFAULT_KICK_MULTIPLIER); } private RateLimiter CreateForConnection() { return new RateLimiter( ConnSizeValue, -1, MinimumDelayValue ); } private RateLimiter CreateForUser() { return new RateLimiter( UserSizeValue, UserWarnSizeValue, MinimumDelayValue ); } public TimeSpan GetKickLength(int kickCount) { if(kickCount < 1) kickCount = 1; return TimeSpan.FromSeconds(KickLengthValue * (KickMultiplierValue * kickCount)); } public bool UpdateConnection(IConnection conn) { lock(ConnectionsSync) { string connId = conn.ConnectionId; if(!Connections.ContainsKey(connId)) Connections[connId] = CreateForConnection(); Connections[connId].Update(); return Connections[connId].ShouldKick; } } public (bool kick, bool warn) UpdateUser(IUser user) { lock(UsersSync) { long userId = user.UserId; if(!Users.ContainsKey(userId)) Users[userId] = CreateForUser(); Users[userId].Update(); return (Users[userId].ShouldKick, Users[userId].ShouldWarn); } } } }