2022-07-21 03:34:29 +00:00
|
|
|
|
namespace RegexBot.Common;
|
2022-04-23 20:01:27 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Helper class for managing rate limit data.
|
2022-07-21 03:34:29 +00:00
|
|
|
|
/// Specifically, this class holds entries and does not allow the same entry to be held more than once until a specified
|
2022-05-26 02:27:53 +00:00
|
|
|
|
/// amount of time has passed since the entry was originally tracked; useful for a rate limit system.
|
2022-04-23 20:01:27 +00:00
|
|
|
|
/// </summary>
|
2022-07-21 03:34:29 +00:00
|
|
|
|
public class RateLimit<T> where T : notnull {
|
|
|
|
|
private const int DefaultTimeout = 20; // Skeeter's a cool guy and you can't convince me otherwise.
|
2022-04-23 20:01:27 +00:00
|
|
|
|
|
2022-05-26 02:27:53 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Time until an entry within this instance expires, in seconds.
|
|
|
|
|
/// </summary>
|
2022-07-21 03:34:29 +00:00
|
|
|
|
public int Timeout { get; }
|
2022-04-23 20:01:27 +00:00
|
|
|
|
private Dictionary<T, DateTime> Entries { get; } = new Dictionary<T, DateTime>();
|
|
|
|
|
|
2022-07-21 03:34:29 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Creates a new <see cref="RateLimit<T>"/> instance with the default timeout value.
|
|
|
|
|
/// </summary>
|
2022-04-23 20:01:27 +00:00
|
|
|
|
public RateLimit() : this(DefaultTimeout) { }
|
2022-07-21 03:34:29 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Creates a new <see cref="RateLimit<T>"/> instance with the given timeout value.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="timeout">Time until an entry within this instance will expire, in seconds.</param>
|
|
|
|
|
public RateLimit(int timeout) {
|
|
|
|
|
if (timeout < 0) throw new ArgumentOutOfRangeException(nameof(timeout), "Timeout valie cannot be negative.");
|
|
|
|
|
Timeout = timeout;
|
|
|
|
|
}
|
2022-04-23 20:01:27 +00:00
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Checks if the given value is permitted through the rate limit.
|
|
|
|
|
/// Executing this method may create a rate limit entry for the given value.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <returns>True if the given value is permitted by the rate limiter.</returns>
|
|
|
|
|
public bool IsPermitted(T value) {
|
|
|
|
|
if (Timeout == 0) return true;
|
|
|
|
|
|
|
|
|
|
// Take a moment to clean out expired entries
|
|
|
|
|
var now = DateTime.Now;
|
|
|
|
|
var expired = Entries.Where(x => x.Value.AddSeconds(Timeout) <= now).Select(x => x.Key).ToList();
|
|
|
|
|
foreach (var item in expired) Entries.Remove(item);
|
|
|
|
|
|
2022-07-21 03:34:29 +00:00
|
|
|
|
if (Entries.ContainsKey(value)) return false;
|
|
|
|
|
else {
|
2022-04-23 20:01:27 +00:00
|
|
|
|
Entries.Add(value, DateTime.Now);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|