From ae39f15fda74421c7584964ad67d38a5531d99b7 Mon Sep 17 00:00:00 2001 From: Nikolay Kochulin Date: Sun, 22 Dec 2019 09:29:22 +0000 Subject: [PATCH] Split out parts into JetBotLib --- .../Handling => JetBotLib}/CommandRouter.cs | 38 ++--- .../Commands => JetBotLib}/CommandString.cs | 2 +- .../Commands => JetBotLib}/IChatCommand.cs | 3 +- JetBotLib/JetBotLib.csproj | 9 ++ .../Services => JetBotLib}/Localization.cs | 36 ++--- .../Handling => JetBotLib}/RequestChain.cs | 8 +- .../Handling => JetBotLib}/RequestContext.cs | 4 +- JetBotLib/TimeoutManager.cs | 98 +++++++++++++ JetKarmaBot/Commands/AwardCommand.cs | 12 +- JetKarmaBot/Commands/ChangeLocaleCommand.cs | 1 + JetKarmaBot/Commands/CurrenciesCommand.cs | 1 + JetKarmaBot/Commands/HelpCommand.cs | 1 + JetKarmaBot/Commands/LeaderboardCommand.cs | 1 + JetKarmaBot/Commands/StatusCommand.cs | 1 + JetKarmaBot/JetKarmaBot.cs | 9 +- JetKarmaBot/JetKarmaBot.csproj | 3 + .../{Handling => }/DatabaseHandler.cs | 4 +- .../Services/Handling/TimeoutManager.cs | 133 ------------------ JetKarmaBot/Services/KarmaTimeoutManager.cs | 53 +++++++ .../Services/{Handling => }/SaveData.cs | 1 + 20 files changed, 206 insertions(+), 212 deletions(-) rename {JetKarmaBot/Services/Handling => JetBotLib}/CommandRouter.cs (69%) rename {JetKarmaBot/Commands => JetBotLib}/CommandString.cs (98%) rename {JetKarmaBot/Commands => JetBotLib}/IChatCommand.cs (91%) create mode 100644 JetBotLib/JetBotLib.csproj rename {JetKarmaBot/Services => JetBotLib}/Localization.cs (80%) rename {JetKarmaBot/Services/Handling => JetBotLib}/RequestChain.cs (74%) rename {JetKarmaBot/Services/Handling => JetBotLib}/RequestContext.cs (93%) create mode 100644 JetBotLib/TimeoutManager.cs rename JetKarmaBot/Services/{Handling => }/DatabaseHandler.cs (89%) delete mode 100644 JetKarmaBot/Services/Handling/TimeoutManager.cs create mode 100644 JetKarmaBot/Services/KarmaTimeoutManager.cs rename JetKarmaBot/Services/{Handling => }/SaveData.cs (98%) diff --git a/JetKarmaBot/Services/Handling/CommandRouter.cs b/JetBotLib/CommandRouter.cs similarity index 69% rename from JetKarmaBot/Services/Handling/CommandRouter.cs rename to JetBotLib/CommandRouter.cs index b2354c5..ad634dc 100644 --- a/JetKarmaBot/Services/Handling/CommandRouter.cs +++ b/JetBotLib/CommandRouter.cs @@ -1,13 +1,10 @@ -using JetKarmaBot.Commands; -using NLog; -using Perfusion; -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Telegram.Bot; -namespace JetKarmaBot.Services.Handling +namespace JetBotLib { public class ChatCommandRouter : IRequestHandler { @@ -17,36 +14,19 @@ namespace JetKarmaBot.Services.Handling public ChatCommandRouter Router; public bool Succeded; } - public Telegram.Bot.Types.User Me { get; private set; } - [Inject] private Logger log; - [Inject] private TelegramBotClient Client { get; set; } - public async Task Start() - { - Me = await Client.GetMeAsync(); - } public Task Handle(RequestContext ctx, Func next) { - log.Debug("Message received"); CommandString cmd = ctx.Command; Feature feature = new Feature() { Router = this }; ctx.Features.Add(feature); - try + if (commands.ContainsKey(cmd.Command)) { - if (commands.ContainsKey(cmd.Command)) - { - feature.CommandType = commands[cmd.Command].GetType(); - log.Debug($"Handling message via {feature.CommandType.Name}"); - async Task processCommand() => feature.Succeded = await commands[cmd.Command].Execute(ctx); - return processCommand(); - } - } - catch (Exception e) - { - log.Error($"Error while handling command {cmd.Command}!"); - log.Error(e); + feature.CommandType = commands[cmd.Command].GetType(); + async Task processCommand() => feature.Succeded = await commands[cmd.Command].Execute(ctx); + return processCommand(); } return next(ctx); @@ -54,17 +34,15 @@ namespace JetKarmaBot.Services.Handling public void Add(IChatCommand c) { - log.ConditionalTrace($"Adding command {c.GetType().Name}"); foreach (var name in c.Names) { - log.ConditionalTrace($"Mounting {c.GetType().Name} to {name}"); if (commands.ContainsKey(name)) throw new Exception($"command collision for name {name}, commands {commands[name].GetType()} and {c.GetType()}"); commands[name] = c; } } - internal string GetHelpText(Locale loc) + public string GetHelpText(Locale loc) { List pieces = new List(); foreach (IChatCommand c in commands.Values.Distinct()) @@ -81,7 +59,7 @@ namespace JetKarmaBot.Services.Handling return string.Join("\n", pieces); } - internal string GetHelpTextFor(string commandname, Locale loc) + public string GetHelpTextFor(string commandname, Locale loc) { IChatCommand c = commands[commandname]; string build = ""; diff --git a/JetKarmaBot/Commands/CommandString.cs b/JetBotLib/CommandString.cs similarity index 98% rename from JetKarmaBot/Commands/CommandString.cs rename to JetBotLib/CommandString.cs index 9a330d5..cf0a3f1 100644 --- a/JetKarmaBot/Commands/CommandString.cs +++ b/JetBotLib/CommandString.cs @@ -2,7 +2,7 @@ using System.Linq; using System.Text.RegularExpressions; -namespace JetKarmaBot.Commands +namespace JetBotLib { public class CommandString { diff --git a/JetKarmaBot/Commands/IChatCommand.cs b/JetBotLib/IChatCommand.cs similarity index 91% rename from JetKarmaBot/Commands/IChatCommand.cs rename to JetBotLib/IChatCommand.cs index 7565fdd..298e8cb 100644 --- a/JetKarmaBot/Commands/IChatCommand.cs +++ b/JetBotLib/IChatCommand.cs @@ -1,8 +1,7 @@ using System.Collections.Generic; using System.Threading.Tasks; -using JetKarmaBot.Services.Handling; -namespace JetKarmaBot.Commands +namespace JetBotLib { public interface IChatCommand { diff --git a/JetBotLib/JetBotLib.csproj b/JetBotLib/JetBotLib.csproj new file mode 100644 index 0000000..31c1c0e --- /dev/null +++ b/JetBotLib/JetBotLib.csproj @@ -0,0 +1,9 @@ + + + + netstandard2.0 + + + + + diff --git a/JetKarmaBot/Services/Localization.cs b/JetBotLib/Localization.cs similarity index 80% rename from JetKarmaBot/Services/Localization.cs rename to JetBotLib/Localization.cs index 18c703c..6a15da6 100644 --- a/JetKarmaBot/Services/Localization.cs +++ b/JetBotLib/Localization.cs @@ -5,43 +5,25 @@ using System.IO; using System.Linq; using System.Runtime.Serialization; using Newtonsoft.Json.Linq; -using NLog; -using Perfusion; -namespace JetKarmaBot +namespace JetBotLib { public class Localization : IReadOnlyDictionary { private Dictionary locales = new Dictionary(); - public Localization(IContainer c) + public Localization() { - c.ResolveObject(this); - log.Info("Initializing..."); string langsFolder = "lang"; if (!Directory.Exists(langsFolder)) Directory.CreateDirectory(langsFolder); foreach (string langFilePath in Directory.EnumerateFiles(langsFolder, "*.json")) { - try - { - string langName = Path.GetFileNameWithoutExtension(langFilePath); - string langKey = langName.ToLowerInvariant(); - locales[langKey] = new Locale(JObject.Parse(File.ReadAllText(langFilePath)), langKey); - log.Debug("Found " + langName); - } - catch (Exception e) - { - log.Error($"Error while parsing {langFilePath}!"); - log.Error(e); - } + string langName = Path.GetFileNameWithoutExtension(langFilePath); + string langKey = langName.ToLowerInvariant(); + locales[langKey] = new Locale(JObject.Parse(File.ReadAllText(langFilePath)), langKey); } - - if (locales.Any()) - log.Info("Initialized!"); - else - throw new FileNotFoundException($"No locales found in {langsFolder}!"); } public Locale this[string locale] @@ -53,17 +35,15 @@ namespace JetKarmaBot } } - [Inject] - private Logger log; public Locale FindByCommonName(string name) { - log.ConditionalTrace("Trying to find locale " + name); + // log.ConditionalTrace("Trying to find locale " + name); foreach (Locale l in locales.Values) { if (l.CommonNames.Contains(name)) { - log.ConditionalTrace("Found locale " + l.Name); + // log.ConditionalTrace("Found locale " + l.Name); return l; } } @@ -77,7 +57,7 @@ namespace JetKarmaBot } else if (matchinglocales.Count() == 1) return matchinglocales.First(); - log.Warn("Failed to find locale " + name); + // log.Warn("Failed to find locale " + name); return null; } public bool ContainsLocale(string locale) diff --git a/JetKarmaBot/Services/Handling/RequestChain.cs b/JetBotLib/RequestChain.cs similarity index 74% rename from JetKarmaBot/Services/Handling/RequestChain.cs rename to JetBotLib/RequestChain.cs index 8c5cad3..10d198b 100644 --- a/JetKarmaBot/Services/Handling/RequestChain.cs +++ b/JetBotLib/RequestChain.cs @@ -1,10 +1,8 @@ using System; using System.Collections.Generic; using System.Threading.Tasks; -using NLog; -using Perfusion; -namespace JetKarmaBot.Services.Handling +namespace JetBotLib { public interface IRequestHandler { @@ -12,7 +10,6 @@ namespace JetKarmaBot.Services.Handling } public class RequestChain : IRequestHandler { - [Inject] private Logger log; List handlerStack = new List(); public async Task Handle(RequestContext ctx, Func next = null) { @@ -22,11 +19,9 @@ namespace JetKarmaBot.Services.Handling { if (i == handlerStack.Count) { - log.ConditionalTrace("(next) End of request chain"); return Task.CompletedTask; } IRequestHandler handler = handlerStack[i++]; - log.ConditionalTrace($"(next) Executing handler {handler.GetType().Name}"); return handler.Handle(newCtx, chainNext); }; await chainNext(ctx); @@ -35,7 +30,6 @@ namespace JetKarmaBot.Services.Handling } public void Add(IRequestHandler handler) { - log.ConditionalTrace($"Adding {handler.GetType().Name} to reqchain"); handlerStack.Add(handler); } } diff --git a/JetKarmaBot/Services/Handling/RequestContext.cs b/JetBotLib/RequestContext.cs similarity index 93% rename from JetKarmaBot/Services/Handling/RequestContext.cs rename to JetBotLib/RequestContext.cs index 5481f0c..4b0db84 100644 --- a/JetKarmaBot/Services/Handling/RequestContext.cs +++ b/JetBotLib/RequestContext.cs @@ -2,12 +2,10 @@ using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; -using JetKarmaBot.Commands; -using JetKarmaBot.Models; using Telegram.Bot; using Telegram.Bot.Args; -namespace JetKarmaBot.Services.Handling +namespace JetBotLib { public class RequestContext : IServiceProvider { diff --git a/JetBotLib/TimeoutManager.cs b/JetBotLib/TimeoutManager.cs new file mode 100644 index 0000000..f142c12 --- /dev/null +++ b/JetBotLib/TimeoutManager.cs @@ -0,0 +1,98 @@ +using System.Collections.Generic; +using System; +using System.Threading.Tasks; +using System.Threading; +using System.Linq; + +namespace JetBotLib +{ + public abstract class TimeoutManager : IRequestHandler + { + public class Feature + { + public double Multiplier = 1; + } + public class PreDbThrowout : IRequestHandler + { + public TimeoutManager Timeout { get; } + public PreDbThrowout(TimeoutManager timeout) + { + Timeout = timeout; + } + + public async Task Handle(RequestContext ctx, Func next) + { + int uid = ctx.EventArgs.Message.From.Id; + if (Timeout.TimeoutCache.TryGetValue(uid, out var stats)) + { + DateTime debtLimit = DateTime.Now.AddSeconds(Timeout.DebtLimitSeconds); + if (debtLimit < stats.CooldownDate && stats.TimeoutMessaged) + return; + } + await next(ctx); + } + } + public class TimeoutStats + { + public DateTime CooldownDate; + public bool TimeoutMessaged; + protected Dictionary features = new Dictionary(); + public IReadOnlyDictionary Features => features; + public T GetFeature() + { + if (!Features.ContainsKey(typeof(T))) + features.Add(typeof(T), Activator.CreateInstance(typeof(T))); + return (T)Features[typeof(T)]; + } + } + public Dictionary TimeoutCache = new Dictionary(); + + public abstract Task Save(CancellationToken ct = default(CancellationToken)); + protected abstract Task LoadUser(int uid, RequestContext ctx = null); + public abstract int SaveIntervalSeconds { get; } + public abstract int DebtLimitSeconds { get; } + public abstract int GetCostSeconds(string name, bool status); + + public async Task SaveLoop(CancellationToken ct = default(CancellationToken)) + { + while (!ct.IsCancellationRequested) + { + await Task.Delay(SaveIntervalSeconds * 1000, ct); + await Save(ct); + } + } + + public async Task Handle(RequestContext ctx, Func next) + { + int uid = ctx.EventArgs.Message.From.Id; + await LoadUser(uid, ctx); + DateTime debtLimit = DateTime.Now.AddSeconds(DebtLimitSeconds); + if (debtLimit < TimeoutCache[uid].CooldownDate) + { + if (!TimeoutCache[uid].TimeoutMessaged) + { + Locale currentLocale = ctx.GetFeature(); + await ctx.SendMessage(currentLocale["jetkarmabot.ratelimit"]); + TimeoutCache[uid].TimeoutMessaged = true; + } + return; + } + Feature feature = new Feature(); + ctx.Features.Add(feature); + + await next(ctx); + + var routerFeature = ctx.GetFeature(); + if (feature.Multiplier == 0) return; + int cost = GetCostSeconds(getTypeName(routerFeature.CommandType), routerFeature.Succeded); + if (TimeoutCache[uid].CooldownDate < DateTime.Now) TimeoutCache[uid].CooldownDate = DateTime.Now; + TimeoutCache[uid].CooldownDate = TimeoutCache[uid].CooldownDate.AddSeconds(cost); + TimeoutCache[uid].TimeoutMessaged = false; + } + private string getTypeName(Type t) + { + return (t.DeclaringType == null ? t.Namespace + "." + t.Name : getTypeName(t.DeclaringType) + "." + t.Name) + + (t.GenericTypeArguments.Length > 0 ? "<" + string.Join(",", t.GenericTypeArguments.Select(getTypeName)) + ">" : ""); + } + } +} \ No newline at end of file diff --git a/JetKarmaBot/Commands/AwardCommand.cs b/JetKarmaBot/Commands/AwardCommand.cs index 86de14b..cfc54ba 100644 --- a/JetKarmaBot/Commands/AwardCommand.cs +++ b/JetKarmaBot/Commands/AwardCommand.cs @@ -7,11 +7,16 @@ using NLog; using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; using JetKarmaBot.Models; +using JetBotLib; namespace JetKarmaBot.Commands { class AwardCommand : IChatCommand { + public class TimeoutFeature + { + public DateTime PreviousAwardDate; + } public IReadOnlyCollection Names => new[] { "award", "revoke" }; [Inject] private Logger log; @@ -21,8 +26,9 @@ namespace JetKarmaBot.Commands var currentLocale = ctx.GetFeature(); var awarder = ctx.EventArgs.Message.From; + var timeoutFeature = Timeout.TimeoutCache[awarder.Id].GetFeature(); - if (Timeout.TimeoutCache[awarder.Id].PreviousAwardDate.AddSeconds(Config.Timeout.AwardTimeSeconds) > DateTime.Now) + if (timeoutFeature.PreviousAwardDate.AddSeconds(Config.Timeout.AwardTimeSeconds) > DateTime.Now) { ctx.GetFeature().Multiplier = 0; // Doesn't count as success or failure if (!Timeout.TimeoutCache[awarder.Id].TimeoutMessaged) @@ -81,7 +87,7 @@ namespace JetKarmaBot.Commands return false; } - if (ctx.GetFeature().Router.Me.Id == recipientId) + if (ctx.GetFeature().Me.Id == recipientId) { await ctx.SendMessage(awarding ? currentLocale["jetkarmabot.award.errawardbot"] @@ -119,7 +125,7 @@ namespace JetKarmaBot.Commands var response = message + "\n" + String.Format(currentLocale["jetkarmabot.award.statustext"], recUserName, prevCount + (awarding ? 1 : -1), awardType.Symbol); await ctx.SendMessage(response); - Timeout.TimeoutCache[awarder.Id].PreviousAwardDate = DateTime.Now; + timeoutFeature.PreviousAwardDate = DateTime.Now; return true; } diff --git a/JetKarmaBot/Commands/ChangeLocaleCommand.cs b/JetKarmaBot/Commands/ChangeLocaleCommand.cs index 977d966..26a18fe 100644 --- a/JetKarmaBot/Commands/ChangeLocaleCommand.cs +++ b/JetKarmaBot/Commands/ChangeLocaleCommand.cs @@ -5,6 +5,7 @@ using NLog; using System.Linq; using System.Threading.Tasks; using JetKarmaBot.Models; +using JetBotLib; namespace JetKarmaBot.Commands { diff --git a/JetKarmaBot/Commands/CurrenciesCommand.cs b/JetKarmaBot/Commands/CurrenciesCommand.cs index 4da45ea..1249292 100644 --- a/JetKarmaBot/Commands/CurrenciesCommand.cs +++ b/JetKarmaBot/Commands/CurrenciesCommand.cs @@ -5,6 +5,7 @@ using System.Linq; using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; using JetKarmaBot.Models; +using JetBotLib; namespace JetKarmaBot.Commands { diff --git a/JetKarmaBot/Commands/HelpCommand.cs b/JetKarmaBot/Commands/HelpCommand.cs index dd90aee..780cd92 100644 --- a/JetKarmaBot/Commands/HelpCommand.cs +++ b/JetKarmaBot/Commands/HelpCommand.cs @@ -4,6 +4,7 @@ using JetKarmaBot.Services.Handling; using Telegram.Bot.Types.Enums; using System.Threading.Tasks; using JetKarmaBot.Models; +using JetBotLib; namespace JetKarmaBot.Commands { diff --git a/JetKarmaBot/Commands/LeaderboardCommand.cs b/JetKarmaBot/Commands/LeaderboardCommand.cs index fc82e87..5af6273 100644 --- a/JetKarmaBot/Commands/LeaderboardCommand.cs +++ b/JetKarmaBot/Commands/LeaderboardCommand.cs @@ -5,6 +5,7 @@ using Microsoft.EntityFrameworkCore; using Perfusion; using JetKarmaBot.Services.Handling; using JetKarmaBot.Models; +using JetBotLib; namespace JetKarmaBot.Commands { diff --git a/JetKarmaBot/Commands/StatusCommand.cs b/JetKarmaBot/Commands/StatusCommand.cs index 3ef93c4..ab850a1 100644 --- a/JetKarmaBot/Commands/StatusCommand.cs +++ b/JetKarmaBot/Commands/StatusCommand.cs @@ -5,6 +5,7 @@ using JetKarmaBot.Services.Handling; using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; using JetKarmaBot.Models; +using JetBotLib; namespace JetKarmaBot.Commands { diff --git a/JetKarmaBot/JetKarmaBot.cs b/JetKarmaBot/JetKarmaBot.cs index 9e6e8c2..2145d67 100644 --- a/JetKarmaBot/JetKarmaBot.cs +++ b/JetKarmaBot/JetKarmaBot.cs @@ -1,3 +1,4 @@ +using JetBotLib; using JetKarmaBot.Commands; using JetKarmaBot.Models; using JetKarmaBot.Services; @@ -19,9 +20,10 @@ namespace JetKarmaBot [Inject] Config Config { get; set; } [Inject] IContainer Container { get; set; } [Inject] KarmaContextFactory Db { get; set; } - [Inject] TimeoutManager Timeout { get; set; } + [Inject] KarmaTimeoutManager Timeout { get; set; } [Inject] Localization Locale { get; set; } + public Telegram.Bot.Types.User Me { get; private set; } TelegramBotClient Client { get; set; } ChatCommandRouter Commands; RequestChain Chain; @@ -38,6 +40,7 @@ namespace JetKarmaBot }; Client = new TelegramBotClient(Config.ApiKey, httpProxy); + Me = await Client.GetMeAsync(); Container.AddInstance(Client); timeoutWaitTaskToken = new CancellationTokenSource(); @@ -72,10 +75,11 @@ namespace JetKarmaBot return; if (!CommandString.TryParse(args.Message.Text, out var cmd)) return; - if (cmd.UserName != null && cmd.UserName != Commands.Me.Username) + if (cmd.UserName != null && cmd.UserName != Me.Username) return; RequestContext ctx = new RequestContext(Client, args, cmd); + ctx.Features.Add(this); _ = Chain.Handle(ctx); } @@ -88,7 +92,6 @@ namespace JetKarmaBot c.Add(); c.Add(); Commands = c.GetInstance(); - await Commands.Start(); foreach (IChatCommand cmd in c.GetInstances()) { Commands.Add(cmd); diff --git a/JetKarmaBot/JetKarmaBot.csproj b/JetKarmaBot/JetKarmaBot.csproj index 54b7480..e9a30a3 100644 --- a/JetKarmaBot/JetKarmaBot.csproj +++ b/JetKarmaBot/JetKarmaBot.csproj @@ -32,4 +32,7 @@ Always + + + diff --git a/JetKarmaBot/Services/Handling/DatabaseHandler.cs b/JetKarmaBot/Services/DatabaseHandler.cs similarity index 89% rename from JetKarmaBot/Services/Handling/DatabaseHandler.cs rename to JetKarmaBot/Services/DatabaseHandler.cs index 33e6f5b..064e25f 100644 --- a/JetKarmaBot/Services/Handling/DatabaseHandler.cs +++ b/JetKarmaBot/Services/DatabaseHandler.cs @@ -1,9 +1,9 @@ using System; using System.Threading.Tasks; -using Microsoft.EntityFrameworkCore; +using JetBotLib; using Perfusion; -namespace JetKarmaBot.Services.Handling +namespace JetKarmaBot.Services { public class DatabaseHandler : IRequestHandler { diff --git a/JetKarmaBot/Services/Handling/TimeoutManager.cs b/JetKarmaBot/Services/Handling/TimeoutManager.cs deleted file mode 100644 index 638168c..0000000 --- a/JetKarmaBot/Services/Handling/TimeoutManager.cs +++ /dev/null @@ -1,133 +0,0 @@ -using Perfusion; -using System.Collections.Generic; -using System; -using System.Threading.Tasks; -using JetKarmaBot.Models; -using System.Threading; -using System.Linq; -using NLog; - -namespace JetKarmaBot.Services.Handling -{ - [Singleton] - public class TimeoutManager : IRequestHandler - { - public class Feature - { - public double Multiplier = 1; - } - public class PreDbThrowout : IRequestHandler - { - public TimeoutManager Timeout { get; } - public PreDbThrowout(TimeoutManager timeout) - { - Timeout = timeout; - } - - public async Task Handle(RequestContext ctx, Func next) - { - int uid = ctx.EventArgs.Message.From.Id; - if (Timeout.TimeoutCache.TryGetValue(uid, out var stats)) - { - DateTime debtLimit = DateTime.Now.AddSeconds(Timeout.cfg.Timeout.DebtLimitSeconds); - if (debtLimit < stats.CooldownDate && stats.TimeoutMessaged) - return; - } - await next(ctx); - } - } - public class TimeoutStats - { - public DateTime CooldownDate; - public bool TimeoutMessaged; - public DateTime PreviousAwardDate; - } - [Inject] private KarmaContextFactory Db; - [Inject] private Config cfg; - [Inject] private Localization Locale; - [Inject] private Logger log; - public Dictionary TimeoutCache = new Dictionary(); - private async Task ApplyCost(string name, bool succeded, int uid, KarmaContext db, Feature feature) - { - if (feature.Multiplier == 0) - return; - if (!cfg.Timeout.CommandCostsSeconds.TryGetValue(name + (succeded ? " (OK)" : "(ERR)"), out var costSeconds)) - if (!cfg.Timeout.CommandCostsSeconds.TryGetValue(name, out costSeconds)) - if (!cfg.Timeout.CommandCostsSeconds.TryGetValue("Default", out costSeconds)) - { - throw new LocalizationException("Default key not present"); - } - await PopulateStats(uid, db); - DateTime debtLimit = DateTime.Now.AddSeconds(cfg.Timeout.DebtLimitSeconds); - if (TimeoutCache[uid].CooldownDate >= debtLimit) - //Programming error - throw new NotImplementedException(); - TimeoutCache[uid].CooldownDate = (TimeoutCache[uid].CooldownDate <= DateTime.Now ? DateTime.Now : TimeoutCache[uid].CooldownDate) - .AddSeconds(feature.Multiplier * costSeconds); - TimeoutCache[uid].TimeoutMessaged = false; - } - - private async Task PopulateStats(int uid, KarmaContext db) - { - if (!TimeoutCache.ContainsKey(uid)) - { - log.ConditionalTrace($"User {uid} not present: saving to cache"); - TimeoutCache[uid] = new TimeoutStats() - { - CooldownDate = (await db.Users.FindAsync(uid))?.CooldownDate ?? DateTime.Now - }; - } - } - public async Task Save(CancellationToken ct = default(CancellationToken)) - { - log.Info("Saving timeout info to database"); - using (KarmaContext db = Db.GetContext()) - { - foreach (int i in TimeoutCache.Keys) - { - (await db.Users.FindAsync(new object[] { i }, ct)).CooldownDate = TimeoutCache[i].CooldownDate; - } - await db.SaveChangesAsync(ct); - } - log.Info("Saved"); - } - public async Task SaveLoop(CancellationToken ct = default(CancellationToken)) - { - while (!ct.IsCancellationRequested) - { - await Task.Delay(cfg.Timeout.SaveIntervalSeconds * 1000, ct); - await Save(ct); - } - } - - public async Task Handle(RequestContext ctx, Func next) - { - int uid = ctx.EventArgs.Message.From.Id; - KarmaContext db = ctx.GetFeature(); - await PopulateStats(uid, db); - DateTime debtLimit = DateTime.Now.AddSeconds(cfg.Timeout.DebtLimitSeconds); - if (debtLimit < TimeoutCache[uid].CooldownDate) - { - if (!TimeoutCache[uid].TimeoutMessaged) - { - Locale currentLocale = ctx.GetFeature(); - await ctx.SendMessage(currentLocale["jetkarmabot.ratelimit"]); - TimeoutCache[uid].TimeoutMessaged = true; - } - return; - } - Feature feature = new Feature(); - ctx.Features.Add(feature); - - await next(ctx); - - var routerFeature = ctx.GetFeature(); - await ApplyCost(getTypeName(routerFeature.CommandType), routerFeature.Succeded, uid, db, feature); - } - private string getTypeName(Type t) - { - return (t.DeclaringType == null ? t.Namespace + "." + t.Name : getTypeName(t.DeclaringType) + "." + t.Name) - + (t.GenericTypeArguments.Length > 0 ? "<" + string.Join(",", t.GenericTypeArguments.Select(getTypeName)) + ">" : ""); - } - } -} \ No newline at end of file diff --git a/JetKarmaBot/Services/KarmaTimeoutManager.cs b/JetKarmaBot/Services/KarmaTimeoutManager.cs new file mode 100644 index 0000000..0021ad6 --- /dev/null +++ b/JetKarmaBot/Services/KarmaTimeoutManager.cs @@ -0,0 +1,53 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using JetBotLib; +using JetKarmaBot.Models; +using Perfusion; + +namespace JetKarmaBot.Services +{ + public class KarmaTimeoutManager : TimeoutManager + { + [Inject] Config cfg; + [Inject] KarmaContextFactory Db; + public override int SaveIntervalSeconds => cfg.Timeout.SaveIntervalSeconds; + + public override int DebtLimitSeconds => cfg.Timeout.DebtLimitSeconds; + + public override int GetCostSeconds(string name, bool status) + { + if (!cfg.Timeout.CommandCostsSeconds.TryGetValue(name + (status ? " (OK)" : "(ERR)"), out var costSeconds)) + if (!cfg.Timeout.CommandCostsSeconds.TryGetValue(name, out costSeconds)) + if (!cfg.Timeout.CommandCostsSeconds.TryGetValue("Default", out costSeconds)) + { + throw new LocalizationException("Default key not present"); + } + return costSeconds; + } + + public override async Task Save(CancellationToken ct = default(CancellationToken)) + { + using (KarmaContext db = Db.GetContext()) + { + foreach (int uid in TimeoutCache.Keys) + { + (await db.Users.FindAsync(new object[] { uid }, ct)).CooldownDate = TimeoutCache[uid].CooldownDate; + } + await db.SaveChangesAsync(ct); + } + } + + protected override async Task LoadUser(int uid, RequestContext ctx = null) + { + KarmaContext db = ctx.GetFeature(); + if (!TimeoutCache.ContainsKey(uid)) + { + TimeoutCache[uid] = new TimeoutStats() + { + CooldownDate = (await db.Users.FindAsync(uid))?.CooldownDate ?? DateTime.Now + }; + } + } + } +} \ No newline at end of file diff --git a/JetKarmaBot/Services/Handling/SaveData.cs b/JetKarmaBot/Services/SaveData.cs similarity index 98% rename from JetKarmaBot/Services/Handling/SaveData.cs rename to JetKarmaBot/Services/SaveData.cs index 5c0130c..ff17948 100644 --- a/JetKarmaBot/Services/Handling/SaveData.cs +++ b/JetKarmaBot/Services/SaveData.cs @@ -1,5 +1,6 @@ using System; using System.Threading.Tasks; +using JetBotLib; using JetKarmaBot.Models; using Microsoft.EntityFrameworkCore;