From 520f1efe376f7b704a1c15ff70e1c86e0695241f Mon Sep 17 00:00:00 2001 From: Sergey Gut Date: Wed, 16 Feb 2022 19:44:53 +0300 Subject: [PATCH] AntiFlood service --- Core/Service/IAntiFlood.cs | 9 ++++ Core/Service/MemoryAntiFloodService.cs | 63 ++++++++++++++++++++++++ Core/Startup.cs | 1 + modules/Reputation/Handler/Reputation.cs | 20 ++++++-- 4 files changed, 89 insertions(+), 4 deletions(-) create mode 100644 Core/Service/IAntiFlood.cs create mode 100644 Core/Service/MemoryAntiFloodService.cs diff --git a/Core/Service/IAntiFlood.cs b/Core/Service/IAntiFlood.cs new file mode 100644 index 0000000..f8fec33 --- /dev/null +++ b/Core/Service/IAntiFlood.cs @@ -0,0 +1,9 @@ +using Telegram.Bot.Types; + +namespace Kruzya.TelegramBot.Core.Service; + +public interface IAntiFlood +{ + public bool IsUserFlooding(Chat chat, User user); + public void RecordAction(Chat chat, User user); +} diff --git a/Core/Service/MemoryAntiFloodService.cs b/Core/Service/MemoryAntiFloodService.cs new file mode 100644 index 0000000..ce5086b --- /dev/null +++ b/Core/Service/MemoryAntiFloodService.cs @@ -0,0 +1,63 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using Microsoft.Extensions.Configuration; +using Telegram.Bot.Types; + +namespace Kruzya.TelegramBot.Core.Service; + +public class MemoryAntiFloodService : IAntiFlood +{ + private IDictionary> _floodUsers; + private IDictionary _bannedUsers; + private int _floodStartsAfter = 3; + private TimeSpan _banningTimeSpan; + + public MemoryAntiFloodService(IConfiguration configuration) + { + _floodUsers = new Dictionary>(); + _bannedUsers = new Dictionary(); + _floodStartsAfter = configuration.GetValue("FloodActions"); + _banningTimeSpan = TimeSpan.FromMinutes(configuration.GetValue("FloodBanTime")); + } + + public bool IsUserFlooding(Chat chat, User user) + { + var dictKey = DictKey(chat, user); + if (_bannedUsers.TryGetValue(dictKey, out var expirationTime)) + { + if (DateTime.UtcNow <= expirationTime) + { + expirationTime = DateTime.MinValue; + _bannedUsers.Remove(dictKey); + } + } + + return (DateTime.UtcNow >= expirationTime); + } + + public void RecordAction(Chat chat, User user) + { + var dictKey = DictKey(chat, user); + if (!_floodUsers.TryGetValue(dictKey, out var actionTimeList)) + { + actionTimeList = new List(); + } + + actionTimeList.Add(DateTime.UtcNow); + actionTimeList = actionTimeList.Where(e => e.Subtract(_banningTimeSpan) <= DateTime.UtcNow).ToList(); + _floodUsers[dictKey] = actionTimeList; + + if (actionTimeList.Count > _floodStartsAfter) + { + actionTimeList.Clear(); + _bannedUsers[dictKey] = DateTime.UtcNow.Add(_banningTimeSpan); + } + } + + private string DictKey(Chat chat, User user) + { + return $"{chat.Id}-{user.Id}"; + } +} diff --git a/Core/Startup.cs b/Core/Startup.cs index 361929c..cee6101 100644 --- a/Core/Startup.cs +++ b/Core/Startup.cs @@ -43,6 +43,7 @@ namespace Kruzya.TelegramBot.Core services.AddSingleton, MemoryCache>(); services.AddSingleton(); + services.AddSingleton(); // Trigger module handlers. _core.Modules.ConfigureServices(services); diff --git a/modules/Reputation/Handler/Reputation.cs b/modules/Reputation/Handler/Reputation.cs index 71c24d6..36cd87b 100644 --- a/modules/Reputation/Handler/Reputation.cs +++ b/modules/Reputation/Handler/Reputation.cs @@ -19,6 +19,7 @@ public class Reputation : CommonHandler private readonly UserService _userService; private readonly ILogger _logger; private readonly IReputation _reputationService; + private readonly IAntiFlood _antiFlood; private static readonly List RepUpChars = new() { @@ -45,10 +46,12 @@ public class Reputation : CommonHandler CoreContext db, UserService userService, IReputation reputationService, + IAntiFlood antiFlood, ILogger logger) : base(db) { _reputationService = reputationService; _userService = userService; + _antiFlood = antiFlood; _logger = logger; } @@ -56,7 +59,7 @@ public class Reputation : CommonHandler public async Task HandleReputation() { var text = Message?.Text; - + if (string.IsNullOrEmpty(text)) { text = Message?.Sticker?.Emoji; @@ -65,14 +68,23 @@ public class Reputation : CommonHandler { return; } - + var receiver = RepliedUser; var canChangeRep = receiver is { IsBot: false } && !From.IsBot && receiver.Id != From.Id; if (canChangeRep && (RepUpChars.Contains(text) || RepDownChars.Contains(text))) { - var senderRepCount = await GetUserReputation(From); + if (!_userService.IsUserSuper(From)) + { + if (_antiFlood.IsUserFlooding(Chat, From)) + { + return; + } + _antiFlood.RecordAction(Chat, From); + } + + var senderRepCount = await GetUserReputation(From); if (senderRepCount < 0) { await Reply(new HtmlString() @@ -143,7 +155,7 @@ public class Reputation : CommonHandler i++; } - await Bot.SendTextMessageAsync(Chat, msg.ToString(), ParseMode.Html, + await Bot.SendTextMessageAsync(Chat!, msg.ToString(), ParseMode.Html, disableNotification: true); }