diff --git a/src/NadekoBot/Modules/Administration/Commands/RatelimitCommand.cs b/src/NadekoBot/Modules/Administration/Commands/RatelimitCommand.cs index 16ae221e..ddd2db75 100644 --- a/src/NadekoBot/Modules/Administration/Commands/RatelimitCommand.cs +++ b/src/NadekoBot/Modules/Administration/Commands/RatelimitCommand.cs @@ -5,9 +5,10 @@ using NadekoBot.Attributes; using NadekoBot.Extensions; using System; using System.Collections.Concurrent; +using System.Threading; using System.Threading.Tasks; -//todo rewrite to accept msg/sec (for example 1/5 - 1 message every 5 seconds) +//todo rewrite namespace NadekoBot.Modules.Administration { public partial class Administration @@ -15,58 +16,101 @@ namespace NadekoBot.Modules.Administration [Group] public class RatelimitCommand { - public static ConcurrentDictionary> RatelimitingChannels = new ConcurrentDictionary>(); + public static ConcurrentDictionary RatelimitingChannels = new ConcurrentDictionary(); - private static readonly TimeSpan ratelimitTime = new TimeSpan(0, 0, 0, 5); private DiscordSocketClient _client { get; } + public class Ratelimiter + { + public class RatelimitedUser + { + public ulong UserId { get; set; } + public int MessageCount { get; set; } = 0; + } + + public ulong ChannelId { get; set; } + + public int MaxMessages { get; set; } + public int PerSeconds { get; set; } + + public CancellationTokenSource cancelSource { get; set; } + public CancellationToken cancelToken { get; set; } + + public ConcurrentDictionary Users { get; set; } + + public bool CheckUserRatelimit(ulong id) + { + RatelimitedUser usr = Users.GetOrAdd(id, (key) => new RatelimitedUser() { UserId = id }); + if (usr.MessageCount == MaxMessages) + { + return true; + } + else + { + usr.MessageCount++; + var t = Task.Run(async () => { + try + { + await Task.Delay(PerSeconds * 1000, cancelToken); + } + catch (OperationCanceledException) { } + usr.MessageCount--; + }); + return false; + } + + } + } + public RatelimitCommand() { - this._client = NadekoBot.Client; - _client.MessageReceived += async (umsg) => + _client.MessageReceived += (umsg) => { - var usrMsg = umsg as IUserMessage; - var channel = usrMsg.Channel as ITextChannel; - - if (channel == null || await usrMsg.IsAuthor()) - return; - ConcurrentDictionary userTimePair; - if (!RatelimitingChannels.TryGetValue(channel.Id, out userTimePair)) return; - DateTime lastMessageTime; - if (userTimePair.TryGetValue(usrMsg.Author.Id, out lastMessageTime)) + var t = Task.Run(async () => { - if (DateTime.Now - lastMessageTime < ratelimitTime) - { - try - { - await usrMsg.DeleteAsync().ConfigureAwait(false); - } - catch { } + var usrMsg = umsg as IUserMessage; + var channel = usrMsg.Channel as ITextChannel; + + if (channel == null || await usrMsg.IsAuthor()) return; - } - } - userTimePair.AddOrUpdate(usrMsg.Author.Id, id => DateTime.Now, (id, dt) => DateTime.Now); + Ratelimiter limiter; + if (!RatelimitingChannels.TryGetValue(channel.Id, out limiter)) + return; + + if (limiter.CheckUserRatelimit(usrMsg.Author.Id)) + await usrMsg.DeleteAsync(); + }); + return Task.CompletedTask; }; } [LocalizedCommand, LocalizedDescription, LocalizedSummary] [RequireContext(ContextType.Guild)] - public async Task Slowmode(IUserMessage umsg) + public async Task Slowmode(IUserMessage umsg, int msg = 1, int perSec = 5) { var channel = (ITextChannel)umsg.Channel; - ConcurrentDictionary throwaway; + Ratelimiter throwaway; if (RatelimitingChannels.TryRemove(channel.Id, out throwaway)) { - await channel.SendMessageAsync("Slow mode disabled.").ConfigureAwait(false); + await channel.SendMessageAsync("`Slow mode disabled.`").ConfigureAwait(false); return; } - if (RatelimitingChannels.TryAdd(channel.Id, new ConcurrentDictionary())) + + if (msg < 1 || perSec < 1) { - await channel.SendMessageAsync("Slow mode initiated. " + - "Users can't send more than 1 message every 5 seconds.") + await channel.SendMessageAsync("`Invalid parameters.`"); + return; + } + + if (RatelimitingChannels.TryAdd(channel.Id,throwaway = new Ratelimiter() { + ChannelId = channel.Id + })) + { + await channel.SendMessageAsync("`Slow mode initiated.` " + + $"Users can't send more than {throwaway.MaxMessages} message(s) every {throwaway.PerSeconds} second(s).") .ConfigureAwait(false); } } diff --git a/src/NadekoBot/Modules/Games/Commands/Trivia/TriviaGame.cs b/src/NadekoBot/Modules/Games/Commands/Trivia/TriviaGame.cs index ac20336c..2aebbc74 100644 --- a/src/NadekoBot/Modules/Games/Commands/Trivia/TriviaGame.cs +++ b/src/NadekoBot/Modules/Games/Commands/Trivia/TriviaGame.cs @@ -7,7 +7,6 @@ using System.Text; using System.Threading; using System.Threading.Tasks; -// todo rewrite namespace NadekoBot.Modules.Games.Trivia { public class TriviaGame diff --git a/src/NadekoBot/Modules/Music/Music.cs b/src/NadekoBot/Modules/Music/Music.cs index 4f7572ce..d512535d 100644 --- a/src/NadekoBot/Modules/Music/Music.cs +++ b/src/NadekoBot/Modules/Music/Music.cs @@ -417,7 +417,7 @@ namespace NadekoBot.Modules.Music musicPlayer.RemoveSongAt(num - 1); await channel.SendMessageAsync($"🎵**Track {song.PrettyName} at position `#{num}` has been removed.**").ConfigureAwait(false); } - //todo fix + [LocalizedCommand, LocalizedDescription, LocalizedSummary] [RequireContext(ContextType.Guild)] public async Task Remove(IUserMessage umsg, string all) diff --git a/src/NadekoBot/Modules/Searches/Commands/AnimeSearchCommands.cs b/src/NadekoBot/Modules/Searches/Commands/AnimeSearchCommands.cs index b379cb2e..458d6552 100644 --- a/src/NadekoBot/Modules/Searches/Commands/AnimeSearchCommands.cs +++ b/src/NadekoBot/Modules/Searches/Commands/AnimeSearchCommands.cs @@ -117,7 +117,6 @@ namespace NadekoBot.Modules.Searches { var res = await http.GetStringAsync("http://anilist.co/api/manga/search/" + Uri.EscapeUriString(query) + $"?access_token={anilistToken}").ConfigureAwait(false); var smallObj = JArray.Parse(res)[0]; - //todo check if successfull var aniData = await http.GetStringAsync("http://anilist.co/api/manga/" + smallObj["id"] + $"?access_token={anilistToken}").ConfigureAwait(false); return await Task.Run(() => JsonConvert.DeserializeObject(aniData)).ConfigureAwait(false);