diff --git a/Feature/AutoRespond/RateLimitCache.cs b/Feature/AutoRespond/RateLimitCache.cs new file mode 100644 index 0000000..19e3514 --- /dev/null +++ b/Feature/AutoRespond/RateLimitCache.cs @@ -0,0 +1,56 @@ +using System; +using System.Collections.Generic; + +namespace Noikoio.RegexBot.Feature.AutoRespond +{ + /// + /// Stores rate limit settings and caches. + /// + class RateLimitCache + { + public const ushort DefaultTimeout = 30; // TODO make configurable + + private readonly ushort _timeout; + private Dictionary _cache; + + public ushort Timeout => _timeout; + + /// + /// Sets up a new instance of . + /// + public RateLimitCache(ushort timeout) + { + _timeout = timeout; + _cache = new Dictionary(); + } + + /// + /// Adds a cache item corersponding to the given ID. + /// Items added to cache will be removed after the number of seconds specified in . + /// + /// The ID to add to the cache. + /// True on success. False if the given ID already exists. + public bool AddUsage(ulong id) + { + Clean(); + if (_cache.ContainsKey(id)) return false; + _cache.Add(id, DateTime.Now); + return true; + } + + private void Clean() + { + var now = DateTime.Now; + var clean = new Dictionary(); + foreach (var kp in _cache) + { + if (kp.Value.AddSeconds(Timeout) > now) + { + // Copy items that have not yet timed out to the new dictionary + clean.Add(kp.Key, kp.Value); + } + } + _cache = clean; + } + } +} diff --git a/Feature/AutoRespond/ResponseConfigItem.cs b/Feature/AutoRespond/ResponseDefinition.cs similarity index 75% rename from Feature/AutoRespond/ResponseConfigItem.cs rename to Feature/AutoRespond/ResponseDefinition.cs index cdd400f..168cbe0 100644 --- a/Feature/AutoRespond/ResponseConfigItem.cs +++ b/Feature/AutoRespond/ResponseDefinition.cs @@ -8,7 +8,7 @@ namespace Noikoio.RegexBot.Feature.AutoRespond /// /// Represents a single autoresponse definition. /// - struct ResponseConfigItem + struct ResponseDefinition { public enum ResponseType { None, Exec, Reply } @@ -16,15 +16,16 @@ namespace Noikoio.RegexBot.Feature.AutoRespond Regex _trigger; ResponseType _rtype; string _rbody; // response body - private FilterType _filtermode; - private EntityList _filterlist; + private FilterList _filter; + private RateLimitCache _limit; public string Label => _label; public Regex Trigger => _trigger; public (ResponseType, string) Response => (_rtype, _rbody); - public (FilterType, EntityList) Filter => (_filtermode, _filterlist); + public FilterList Filter => _filter; + public RateLimitCache RateLimit => _limit; - public ResponseConfigItem(JObject definition) + public ResponseDefinition(JObject definition) { // label _label = definition["label"]?.Value(); @@ -77,7 +78,25 @@ namespace Noikoio.RegexBot.Feature.AutoRespond // --- // whitelist/blacklist filtering - (_filtermode, _filterlist) = EntityList.GetFilterList(definition); + _filter = new FilterList(definition); + + // rate limiting + string rlstr = definition["ratelimit"].Value(); + if (string.IsNullOrWhiteSpace(rlstr)) + { + _limit = new RateLimitCache(RateLimitCache.DefaultTimeout); + } + else + { + if (ushort.TryParse(rlstr, out var rlval)) + { + _limit = new RateLimitCache(rlval); + } + else + { + throw new RuleImportException("Rate limit value is invalid" + errorpfx); + } + } } } }