From a6ebff0a1718f189bb38e530b3f2f9bc67ebe20a Mon Sep 17 00:00:00 2001 From: Master Kwoth Date: Thu, 3 Mar 2016 18:24:58 +0100 Subject: [PATCH] few more thingies, ready for prerelease --- NadekoBot/Classes/DBHandler.cs | 4 +- NadekoBot/Classes/Music/MusicControls.cs | 32 ++++----- NadekoBot/Classes/Music/Song.cs | 28 ++++---- NadekoBot/Classes/NadekoStats.cs | 14 ++-- .../Classes/Permissions/PermissionChecker.cs | 2 + NadekoBot/Classes/Trivia/TriviaGame.cs | 2 +- NadekoBot/Classes/Trivia/TriviaQuestion.cs | 2 +- .../Classes/Trivia/TriviaQuestionPool.cs | 18 +++-- NadekoBot/Commands/ClashOfClans.cs | 6 +- NadekoBot/Commands/CopyCommand.cs | 13 ++-- NadekoBot/Commands/DiceRollCommand.cs | 35 +++++----- NadekoBot/Commands/DrawCommand.cs | 6 +- NadekoBot/Commands/FlipCoinCommand.cs | 21 +++--- NadekoBot/Commands/HelpCommand.cs | 9 ++- NadekoBot/Commands/IDiscordCommand.cs | 19 ++++++ NadekoBot/Commands/LoLCommands.cs | 6 +- NadekoBot/Commands/LogCommand.cs | 12 ++-- NadekoBot/Commands/PlayingRotate.cs | 8 +-- NadekoBot/Commands/PollCommand.cs | 6 +- NadekoBot/Commands/RequestsCommand.cs | 10 +-- NadekoBot/Commands/ServerGreetCommand.cs | 14 ++-- NadekoBot/Commands/SpeedTyping.cs | 67 ++++++++++--------- NadekoBot/Commands/TriviaCommand.cs | 6 +- .../Commands/VoiceNotificationCommand.cs | 13 ++-- NadekoBot/Modules/DiscordModule.cs | 5 +- NadekoBot/Modules/Games.cs | 2 + NadekoBot/Modules/Help.cs | 1 + NadekoBot/Modules/Permissions.cs | 7 +- NadekoBot/Modules/Searches.cs | 4 +- NadekoBot/NadekoBot.cs | 1 + NadekoBot/NadekoBot.csproj | 2 +- 31 files changed, 190 insertions(+), 185 deletions(-) create mode 100644 NadekoBot/Commands/IDiscordCommand.cs diff --git a/NadekoBot/Classes/DBHandler.cs b/NadekoBot/Classes/DBHandler.cs index 5d268152..f003b94b 100644 --- a/NadekoBot/Classes/DBHandler.cs +++ b/NadekoBot/Classes/DBHandler.cs @@ -45,9 +45,9 @@ namespace NadekoBot.Classes { } } - internal List GetAllRows() where T : IDataModel, new() { + internal HashSet GetAllRows() where T : IDataModel, new() { using (var conn = new SQLiteConnection(FilePath)) { - return conn.Table().ToList(); + return new HashSet(conn.Table()); } } diff --git a/NadekoBot/Classes/Music/MusicControls.cs b/NadekoBot/Classes/Music/MusicControls.cs index fe0bffdb..cd67113e 100644 --- a/NadekoBot/Classes/Music/MusicControls.cs +++ b/NadekoBot/Classes/Music/MusicControls.cs @@ -24,10 +24,10 @@ namespace NadekoBot.Classes.Music { public class MusicPlayer { public static int MaximumPlaylistSize => 50; - private IAudioClient _client { get; set; } + private IAudioClient audioClient { get; set; } - private List _playlist = new List(); - public IReadOnlyCollection Playlist => _playlist; + private readonly List playlist = new List(); + public IReadOnlyCollection Playlist => playlist; private readonly object playlistLock = new object(); public Song CurrentSong { get; set; } = default(Song); @@ -57,7 +57,7 @@ namespace NadekoBot.Classes.Music { Task.Run(async () => { while (true) { try { - _client = await PlaybackVoiceChannel.JoinAudio(); + audioClient = await PlaybackVoiceChannel.JoinAudio(); } catch { await Task.Delay(1000); @@ -67,7 +67,7 @@ namespace NadekoBot.Classes.Music { if (CurrentSong != null) { try { OnStarted(CurrentSong); - await CurrentSong.Play(_client, cancelToken); + await CurrentSong.Play(audioClient, cancelToken); } catch (OperationCanceledException) { Console.WriteLine("Song canceled"); @@ -98,7 +98,7 @@ namespace NadekoBot.Classes.Music { public void Stop() { lock (playlistLock) { - _playlist.Clear(); + playlist.Clear(); try { if (!SongCancelSource.IsCancellationRequested) SongCancelSource.Cancel(); @@ -113,7 +113,7 @@ namespace NadekoBot.Classes.Music { public void Shuffle() { lock (playlistLock) { - _playlist.Shuffle(); + playlist.Shuffle(); } } @@ -129,10 +129,10 @@ namespace NadekoBot.Classes.Music { private Song GetNextSong() { lock (playlistLock) { - if (_playlist.Count == 0) + if (playlist.Count == 0) return null; - var toReturn = _playlist[0]; - _playlist.RemoveAt(0); + var toReturn = playlist[0]; + playlist.RemoveAt(0); return toReturn; } } @@ -141,7 +141,7 @@ namespace NadekoBot.Classes.Music { if (s == null) throw new ArgumentNullException(nameof(s)); lock (playlistLock) { - _playlist.Add(s); + playlist.Add(s); } } @@ -149,20 +149,20 @@ namespace NadekoBot.Classes.Music { if (s == null) throw new ArgumentNullException(nameof(s)); lock (playlistLock) { - _playlist.Remove(s); + playlist.Remove(s); } } public void RemoveSongAt(int index) { lock (playlistLock) { - if (index < 0 || index >= _playlist.Count) + if (index < 0 || index >= playlist.Count) throw new ArgumentException("Invalid index"); - _playlist.RemoveAt(index); + playlist.RemoveAt(index); } } internal Task MoveToVoiceChannel(Channel voiceChannel) { - if (_client?.State != ConnectionState.Connected) + if (audioClient?.State != ConnectionState.Connected) throw new InvalidOperationException("Can't move while bot is not connected to voice channel."); PlaybackVoiceChannel = voiceChannel; return PlaybackVoiceChannel.JoinAudio(); @@ -170,7 +170,7 @@ namespace NadekoBot.Classes.Music { internal void ClearQueue() { lock (playlistLock) { - _playlist.Clear(); + playlist.Clear(); } } } diff --git a/NadekoBot/Classes/Music/Song.cs b/NadekoBot/Classes/Music/Song.cs index ffe6f67d..abdafd83 100644 --- a/NadekoBot/Classes/Music/Song.cs +++ b/NadekoBot/Classes/Music/Song.cs @@ -124,7 +124,7 @@ namespace NadekoBot.Classes.Music { $"**【 {SongInfo.Title.TrimTo(55)} 】**`{(SongInfo.Provider ?? "-")}`"; public SongInfo SongInfo { get; } - private PoopyBuffer songBuffer { get; } = new PoopyBuffer(10.MB()); + private PoopyBuffer songBuffer { get; } = new PoopyBuffer(4.MiB()); private bool prebufferingComplete { get; set; } = false; public MusicPlayer MusicPlayer { get; set; } @@ -145,7 +145,7 @@ namespace NadekoBot.Classes.Music { RedirectStandardError = false, CreateNoWindow = true, }); - var blockSize = 3840; + const int blockSize = 3840; var buffer = new byte[blockSize]; var attempt = 0; while (!cancelToken.IsCancellationRequested) { @@ -164,6 +164,7 @@ namespace NadekoBot.Classes.Music { } catch { Console.WriteLine("Buffering errored"); } finally { + Console.WriteLine($"Buffering done." + $" [{songBuffer.ContentLength}]"); if (p != null) { p.CancelOutputRead(); p.StandardOutput.Dispose(); @@ -172,11 +173,10 @@ namespace NadekoBot.Classes.Music { p.Dispose(); } } - Console.WriteLine($"Buffering done." + $" [{songBuffer.ContentLength}]"); }); internal async Task Play(IAudioClient voiceClient, CancellationToken cancelToken) { - var t = BufferSong(cancelToken).ConfigureAwait(false); + var bufferTask = BufferSong(cancelToken).ConfigureAwait(false); var bufferAttempts = 0; const int waitPerAttempt = 500; var toAttemptTimes = SongInfo.ProviderType != MusicType.Normal ? 5 : 9; @@ -185,7 +185,7 @@ namespace NadekoBot.Classes.Music { } cancelToken.ThrowIfCancellationRequested(); Console.WriteLine($"Prebuffering done? in {waitPerAttempt * bufferAttempts}"); - var blockSize = 3840; + const int blockSize = 3840; var buffer = new byte[blockSize]; var attempt = 0; while (!cancelToken.IsCancellationRequested) { @@ -206,6 +206,7 @@ namespace NadekoBot.Classes.Music { buffer = AdjustVolume(buffer, MusicPlayer.Volume); voiceClient.Send(buffer, 0, read); } + await bufferTask; cancelToken.ThrowIfCancellationRequested(); //try { // voiceClient.Clear(); @@ -366,14 +367,13 @@ namespace NadekoBot.Classes.Music { return query; } - private static bool IsRadioLink(string query) { - return (query.StartsWith("http") || - query.StartsWith("ww")) - && - (query.Contains(".pls") || - query.Contains(".m3u") || - query.Contains(".asx") || - query.Contains(".xspf")); - } + private static bool IsRadioLink(string query) => + (query.StartsWith("http") || + query.StartsWith("ww")) + && + (query.Contains(".pls") || + query.Contains(".m3u") || + query.Contains(".asx") || + query.Contains(".xspf")); } } diff --git a/NadekoBot/Classes/NadekoStats.cs b/NadekoBot/Classes/NadekoStats.cs index 19da43d8..d8e687f2 100644 --- a/NadekoBot/Classes/NadekoStats.cs +++ b/NadekoBot/Classes/NadekoStats.cs @@ -6,6 +6,7 @@ using System.Linq; using NadekoBot.Extensions; using System.Threading.Tasks; using System.Reflection; +using NadekoBot.Modules; namespace NadekoBot { public class NadekoStats { @@ -84,22 +85,27 @@ namespace NadekoBot { public Task LoadStats() => Task.Run(() => { + var songs = Music.MusicPlayers.Count; var sb = new System.Text.StringBuilder(); sb.AppendLine("`Author: Kwoth` `Library: Discord.Net`"); sb.AppendLine($"`Bot Version: {BotVersion}`"); sb.AppendLine($"`Bot id: {NadekoBot.Client.CurrentUser.Id}`"); - sb.AppendLine($"`Owner id: {(NadekoBot.Creds.OwnerIds.FirstOrDefault())}`"); + sb.Append("`Owners' Ids:` "); + sb.AppendLine("`" + String.Join(", ", NadekoBot.Creds.OwnerIds) + "`"); sb.AppendLine($"`Uptime: {GetUptimeString()}`"); sb.Append($"`Servers: {ServerCount}"); sb.Append($" | TextChannels: {TextChannelsCount}"); sb.AppendLine($" | VoiceChannels: {VoiceChannelsCount}`"); sb.AppendLine($"`Commands Ran this session: {commandsRan}`"); - sb.AppendLine($"`Message queue size:{NadekoBot.Client.MessageQueue.Count}`"); - sb.AppendLine($"`Greeted {Commands.ServerGreetCommand.Greeted} times.`"); + sb.AppendLine($"`Message queue size: {NadekoBot.Client.MessageQueue.Count}`"); + sb.Append($"`Greeted {Commands.ServerGreetCommand.Greeted} times.`"); + sb.AppendLine($" `| Playing {songs} songs, ".SnPl(songs) + + $"{Music.MusicPlayers.Sum(kvp => kvp.Value.Playlist.Count)} queued.`"); + sb.AppendLine($"`Heap: {Heap(false)}`"); statsCache = sb.ToString(); }); - public string Heap() => Math.Round((double)GC.GetTotalMemory(true) / 1.MiB(), 2).ToString(); + public string Heap(bool pass = true) => Math.Round((double)GC.GetTotalMemory(pass) / 1.MiB(), 2).ToString(); public async Task GetStats() { if (statsStopwatch.Elapsed.Seconds <= 5) return statsCache; diff --git a/NadekoBot/Classes/Permissions/PermissionChecker.cs b/NadekoBot/Classes/Permissions/PermissionChecker.cs index 59898ff9..f3d7b04d 100644 --- a/NadekoBot/Classes/Permissions/PermissionChecker.cs +++ b/NadekoBot/Classes/Permissions/PermissionChecker.cs @@ -4,12 +4,14 @@ using System.Threading.Tasks; using Discord; using Discord.Commands; using System.Collections.Concurrent; +using System.Collections.Generic; namespace NadekoBot.Classes.Permissions { internal class PermissionChecker : IPermissionChecker { public static PermissionChecker Instance { get; } = new PermissionChecker(); private ConcurrentDictionary timeBlackList { get; } = new ConcurrentDictionary(); + private HashSet serverBlacklist { get; } = new HashSet(); static PermissionChecker() { } public PermissionChecker() { diff --git a/NadekoBot/Classes/Trivia/TriviaGame.cs b/NadekoBot/Classes/Trivia/TriviaGame.cs index de87f51c..238afb1f 100644 --- a/NadekoBot/Classes/Trivia/TriviaGame.cs +++ b/NadekoBot/Classes/Trivia/TriviaGame.cs @@ -21,7 +21,7 @@ namespace NadekoBot.Classes.Trivia { private CancellationTokenSource triviaCancelSource { get; set; } public TriviaQuestion CurrentQuestion { get; private set; } - public List oldQuestions { get; } = new List(); + public HashSet oldQuestions { get; } = new HashSet(); public ConcurrentDictionary Users { get; } = new ConcurrentDictionary(); diff --git a/NadekoBot/Classes/Trivia/TriviaQuestion.cs b/NadekoBot/Classes/Trivia/TriviaQuestion.cs index 24ecf3c7..8e6888db 100644 --- a/NadekoBot/Classes/Trivia/TriviaQuestion.cs +++ b/NadekoBot/Classes/Trivia/TriviaQuestion.cs @@ -7,7 +7,7 @@ using System.Text.RegularExpressions; namespace NadekoBot.Classes.Trivia { public class TriviaQuestion { //represents the min size to judge levDistance with - public static List> strictness = new List>() { + private static readonly HashSet> strictness = new HashSet> { new Tuple(6, 0), new Tuple(7, 1), new Tuple(12, 2), diff --git a/NadekoBot/Classes/Trivia/TriviaQuestionPool.cs b/NadekoBot/Classes/Trivia/TriviaQuestionPool.cs index b9974944..8d17216b 100644 --- a/NadekoBot/Classes/Trivia/TriviaQuestionPool.cs +++ b/NadekoBot/Classes/Trivia/TriviaQuestionPool.cs @@ -6,12 +6,11 @@ using System.Linq; namespace NadekoBot.Classes.Trivia { public class TriviaQuestionPool { - private static readonly TriviaQuestionPool _instance = new TriviaQuestionPool(); - public static TriviaQuestionPool Instance => _instance; + public static TriviaQuestionPool Instance { get; } = new TriviaQuestionPool(); - public List pool = new List(); + public HashSet pool = new HashSet(); - private Random _r { get; } = new Random(); + private Random rng { get; } = new Random(); static TriviaQuestionPool() { } @@ -19,22 +18,21 @@ namespace NadekoBot.Classes.Trivia { Reload(); } - public TriviaQuestion GetRandomQuestion(List exclude) { + public TriviaQuestion GetRandomQuestion(IEnumerable exclude) { var list = pool.Except(exclude).ToList(); - var rand = _r.Next(0, list.Count); + var rand = rng.Next(0, list.Count); return list[rand]; } internal void Reload() { - JArray arr = JArray.Parse(File.ReadAllText("data/questions.txt")); + var arr = JArray.Parse(File.ReadAllText("data/questions.txt")); foreach (var item in arr) { - TriviaQuestion tq; - tq = new TriviaQuestion(item["Question"].ToString(), item["Answer"].ToString(), item["Category"]?.ToString()); + var tq = new TriviaQuestion(item["Question"].ToString(), item["Answer"].ToString(), item["Category"]?.ToString()); pool.Add(tq); } var r = new Random(); - pool = pool.OrderBy(x => r.Next()).ToList(); + pool = new HashSet(pool.OrderBy(x => r.Next())); } } } diff --git a/NadekoBot/Commands/ClashOfClans.cs b/NadekoBot/Commands/ClashOfClans.cs index 6a5a9052..57fc3d4e 100644 --- a/NadekoBot/Commands/ClashOfClans.cs +++ b/NadekoBot/Commands/ClashOfClans.cs @@ -8,7 +8,7 @@ using System.Linq; using System.Threading; namespace NadekoBot.Commands { - internal class ClashOfClans : DiscordCommand { + internal class ClashOfClans : IDiscordCommand { private const string prefix = ","; public static ConcurrentDictionary> ClashWars { get; } = new ConcurrentDictionary>(); @@ -19,7 +19,7 @@ namespace NadekoBot.Commands { } - public override Func DoFunc() => async e => { + public Func DoFunc() => async e => { if (!e.User.ServerPermissions.ManageChannels) return; List wars; @@ -50,7 +50,7 @@ namespace NadekoBot.Commands { //war with the index X started. }; - public override void Init(CommandGroupBuilder cgb) { + public void Init(CommandGroupBuilder cgb) { cgb.CreateCommand(prefix + "createwar") .Alias(prefix + "cw") .Description($"Creates a new war by specifying a size (>10 and multiple of 5) and enemy clan name.\n**Usage**:{prefix}cw 15 The Enemy Clan") diff --git a/NadekoBot/Commands/CopyCommand.cs b/NadekoBot/Commands/CopyCommand.cs index 0a55e6b1..351d5449 100644 --- a/NadekoBot/Commands/CopyCommand.cs +++ b/NadekoBot/Commands/CopyCommand.cs @@ -5,14 +5,13 @@ using Discord.Commands; namespace NadekoBot.Commands { - internal class CopyCommand : DiscordCommand + internal class CopyCommand : IDiscordCommand { - private List CopiedUsers; + private readonly HashSet CopiedUsers = new HashSet(); public CopyCommand() { - CopiedUsers = new List(); - client.MessageReceived += Client_MessageReceived; + NadekoBot.Client.MessageReceived += Client_MessageReceived; } private async void Client_MessageReceived(object sender, Discord.MessageEventArgs e) @@ -27,16 +26,15 @@ namespace NadekoBot.Commands catch { } } - public override Func DoFunc() => async e => + public Func DoFunc() => async e => { if (CopiedUsers.Contains(e.User.Id)) return; CopiedUsers.Add(e.User.Id); await e.Channel.SendMessage(" I'll start copying you now."); - return; }; - public override void Init(CommandGroupBuilder cgb) + public void Init(CommandGroupBuilder cgb) { cgb.CreateCommand("copyme") .Alias("cm") @@ -55,7 +53,6 @@ namespace NadekoBot.Commands CopiedUsers.Remove(e.User.Id); await e.Channel.SendMessage(" I wont copy anymore."); - return; }; } } diff --git a/NadekoBot/Commands/DiceRollCommand.cs b/NadekoBot/Commands/DiceRollCommand.cs index 1198960b..140ce2ab 100644 --- a/NadekoBot/Commands/DiceRollCommand.cs +++ b/NadekoBot/Commands/DiceRollCommand.cs @@ -8,15 +8,14 @@ using Discord.Commands; using NadekoBot.Extensions; namespace NadekoBot.Commands { - internal class DiceRollCommand : DiscordCommand { - public DiceRollCommand() { } + internal class DiceRollCommand : IDiscordCommand { - public override Func DoFunc() { - Random r = new Random(); + public Func DoFunc() { + var r = new Random(); return async e => { if (e.Args[0] == "") { - int num1 = r.Next(0, 10); - int num2 = r.Next(0, 10); + var num1 = r.Next(0, 10); + var num2 = r.Next(0, 10); Image[] images; @@ -26,26 +25,25 @@ namespace NadekoBot.Commands { images = new Image[2] { GetDice(num1), GetDice(num2) }; } - Bitmap bitmap = images.Merge(); + var bitmap = images.Merge(); await e.Channel.SendFile("dice.png", bitmap.ToStream(ImageFormat.Png)); - return; } else { try { - int num = int.Parse(e.Args[0]); + var num = int.Parse(e.Args[0]); if (num < 1) num = 1; if (num > 30) { await e.Channel.SendMessage("You can roll up to 30 dies at a time."); num = 30; } - List dices = new List(num); - List values = new List(num); - for (int i = 0; i < num; i++) { - int randomNumber = r.Next(1, 7); - int toInsert = dices.Count; + var dices = new List(num); + var values = new List(num); + for (var i = 0; i < num; i++) { + var randomNumber = r.Next(1, 7); + var toInsert = dices.Count; if (randomNumber == 6 || dices.Count == 0) toInsert = 0; else if (randomNumber != 1) - for (int j = 0; j < dices.Count; j++) { + for (var j = 0; j < dices.Count; j++) { if (values[j] < randomNumber) { toInsert = j; break; @@ -55,12 +53,11 @@ namespace NadekoBot.Commands { values.Insert(toInsert, randomNumber); } - Bitmap bitmap = dices.Merge(); + var bitmap = dices.Merge(); await e.Channel.SendMessage(values.Count + " Dies rolled. Total: **" + values.Sum() + "** Average: **" + (values.Sum() / (1.0f * values.Count)).ToString("N2") + "**"); await e.Channel.SendFile("dices.png", bitmap.ToStream(ImageFormat.Png)); } catch { await e.Channel.SendMessage("Please enter a number of dices to roll."); - return; } } }; @@ -68,7 +65,7 @@ namespace NadekoBot.Commands { private Image GetDice(int num) => Properties.Resources.ResourceManager.GetObject("_" + num) as Image; - public override void Init(CommandGroupBuilder cgb) { + public void Init(CommandGroupBuilder cgb) { cgb.CreateCommand("$roll") .Description("Rolls 2 dice from 0-10. If you supply a number [x] it rolls up to 30 normal dice.\n**Usage**: $roll [x]") .Parameter("num", ParameterType.Optional) @@ -86,7 +83,7 @@ namespace NadekoBot.Commands { if (e.GetArg("range").Contains("-")) { var arr = e.GetArg("range").Split('-') .Take(2) - .Select(x => int.Parse(x)) + .Select(int.Parse) .ToArray(); if (arr[0] > arr[1]) throw new ArgumentException("First argument should be bigger than the second one."); diff --git a/NadekoBot/Commands/DrawCommand.cs b/NadekoBot/Commands/DrawCommand.cs index 54d90aff..ce132614 100644 --- a/NadekoBot/Commands/DrawCommand.cs +++ b/NadekoBot/Commands/DrawCommand.cs @@ -7,14 +7,14 @@ using Discord.Commands; using NadekoBot.Extensions; namespace NadekoBot.Commands { - internal class DrawCommand : DiscordCommand { + internal class DrawCommand : IDiscordCommand { private static ConcurrentDictionary AllDecks = new ConcurrentDictionary(); public DrawCommand() { } - public override Func DoFunc() => async (e) => { + public Func DoFunc() => async (e) => { if (!AllDecks.ContainsKey(e.Server)) { await e.Channel.SendMessage("Shuffling cards..."); AllDecks.TryAdd(e.Server, new Cards()); @@ -53,7 +53,7 @@ namespace NadekoBot.Commands { } }; - public override void Init(CommandGroupBuilder cgb) { + public void Init(CommandGroupBuilder cgb) { cgb.CreateCommand("$draw") .Description("Draws a card from the deck.If you supply number [x], she draws up to 5 cards from the deck.\n**Usage**: $draw [x]") .Parameter("count", ParameterType.Optional) diff --git a/NadekoBot/Commands/FlipCoinCommand.cs b/NadekoBot/Commands/FlipCoinCommand.cs index a055c5d0..324d888d 100644 --- a/NadekoBot/Commands/FlipCoinCommand.cs +++ b/NadekoBot/Commands/FlipCoinCommand.cs @@ -5,17 +5,14 @@ using Discord.Commands; using NadekoBot.Extensions; namespace NadekoBot.Commands { - internal class FlipCoinCommand : DiscordCommand { + internal class FlipCoinCommand : IDiscordCommand { - private Random _r; - public FlipCoinCommand() { - _r = new Random(); - } + private readonly Random rng = new Random(); - public override Func DoFunc() => async e => { + public Func DoFunc() => async e => { if (e.GetArg("count") == "") { - if (_r.Next(0, 2) == 1) + if (rng.Next(0, 2) == 1) await e.Channel.SendFile("heads.png", Properties.Resources.heads.ToStream(System.Drawing.Imaging.ImageFormat.Png)); else await e.Channel.SendFile("tails.png", Properties.Resources.tails.ToStream(System.Drawing.Imaging.ImageFormat.Png)); @@ -24,22 +21,20 @@ namespace NadekoBot.Commands { if (int.TryParse(e.GetArg("count"), out result)) { if (result > 10) result = 10; - Image[] imgs = new Image[result]; - for (int i = 0; i < result; i++) { - imgs[i] = _r.Next(0, 2) == 0 ? + var imgs = new Image[result]; + for (var i = 0; i < result; i++) { + imgs[i] = rng.Next(0, 2) == 0 ? Properties.Resources.tails : Properties.Resources.heads; } await e.Channel.SendFile($"{result} coins.png", imgs.Merge().ToStream(System.Drawing.Imaging.ImageFormat.Png)); return; } - await e.Channel.SendMessage("Invalid number"); } - }; - public override void Init(CommandGroupBuilder cgb) { + public void Init(CommandGroupBuilder cgb) { cgb.CreateCommand("$flip") .Description("Flips coin(s) - heads or tails, and shows an image.\n**Usage**: `$flip` or `$flip 3`") .Parameter("count", ParameterType.Optional) diff --git a/NadekoBot/Commands/HelpCommand.cs b/NadekoBot/Commands/HelpCommand.cs index e7f0c25e..1ffb8def 100644 --- a/NadekoBot/Commands/HelpCommand.cs +++ b/NadekoBot/Commands/HelpCommand.cs @@ -6,8 +6,8 @@ using Discord.Commands; using NadekoBot.Extensions; namespace NadekoBot.Commands { - internal class HelpCommand : DiscordCommand { - public override Func DoFunc() => async e => { + internal class HelpCommand : IDiscordCommand { + public Func DoFunc() => async e => { #region OldHelp /* string helpstr = "**COMMANDS DO NOT WORK IN PERSONAL MESSAGES**\nOfficial repo: **github.com/Kwoth/NadekoBot/**"; @@ -60,7 +60,7 @@ Version: `{NadekoStats.Instance.BotVersion}`"; string lastCategory = ""; - foreach (var com in client.GetService().AllCommands) { + foreach (var com in NadekoBot.Client.GetService().AllCommands) { if (com.Category != lastCategory) { helpstr += "\n### " + com.Category + " \n"; helpstr += "Command and aliases | Description | Usage\n"; @@ -76,10 +76,9 @@ Version: `{NadekoStats.Instance.BotVersion}`"; #else File.WriteAllText("commandlist.md", helpstr); #endif - return; }; - public override void Init(CommandGroupBuilder cgb) { + public void Init(CommandGroupBuilder cgb) { cgb.CreateCommand("-h") .Alias(new string[] { "-help", NadekoBot.BotMention + " help", NadekoBot.BotMention + " h", "~h" }) .Description("Either shows a help for a single command, or PMs you help link if no arguments are specified.\n**Usage**: '-h !m q' or just '-h' ") diff --git a/NadekoBot/Commands/IDiscordCommand.cs b/NadekoBot/Commands/IDiscordCommand.cs new file mode 100644 index 00000000..f4f53449 --- /dev/null +++ b/NadekoBot/Commands/IDiscordCommand.cs @@ -0,0 +1,19 @@ +ο»Ώusing System; +using System.Threading.Tasks; +using Discord; +using Discord.Commands; + +namespace NadekoBot.Commands +{ + /// + /// Base DiscordCommand Class. + /// Inherit this class to create your own command. + /// + public interface IDiscordCommand + { + /// + /// Initializes the CommandBuilder with values using CommandGroupBuilder + /// + void Init(CommandGroupBuilder cgb); + } +} diff --git a/NadekoBot/Commands/LoLCommands.cs b/NadekoBot/Commands/LoLCommands.cs index 8705a0ea..185304c6 100644 --- a/NadekoBot/Commands/LoLCommands.cs +++ b/NadekoBot/Commands/LoLCommands.cs @@ -10,7 +10,7 @@ using NadekoBot.Extensions; using Newtonsoft.Json.Linq; namespace NadekoBot.Commands { - internal class LoLCommands : DiscordCommand { + internal class LoLCommands : IDiscordCommand { private class CachedChampion { public System.IO.Stream ImageStream { get; set; } @@ -43,7 +43,7 @@ namespace NadekoBot.Commands { "If you consider playing teemo, do it. If you consider teemo, you deserve him.", "Doesn't matter what you ban really. Enemy will ban your main and you will lose." }; - public override Func DoFunc() { + public Func DoFunc() { throw new NotImplementedException(); } @@ -55,7 +55,7 @@ namespace NadekoBot.Commands { public float StatScore { get; set; } } - public override void Init(CommandGroupBuilder cgb) { + public void Init(CommandGroupBuilder cgb) { cgb.CreateCommand("~lolchamp") .Description("Shows League Of Legends champion statistics. If there are spaces/apostrophes or in the name - omit them. Optional second parameter is a role.\n**Usage**:~lolchamp Riven or ~lolchamp Annie sup") .Parameter("champ", ParameterType.Required) diff --git a/NadekoBot/Commands/LogCommand.cs b/NadekoBot/Commands/LogCommand.cs index d730aad0..53022a5c 100644 --- a/NadekoBot/Commands/LogCommand.cs +++ b/NadekoBot/Commands/LogCommand.cs @@ -5,7 +5,7 @@ using Discord.Commands; using Discord; namespace NadekoBot.Commands { - internal class LogCommand : DiscordCommand { + internal class LogCommand : IDiscordCommand { public LogCommand() { NadekoBot.Client.MessageReceived += MsgRecivd; @@ -14,12 +14,12 @@ namespace NadekoBot.Commands { NadekoBot.Client.UserUpdated += UsrUpdtd; } - private ConcurrentDictionary logs = new ConcurrentDictionary(); - private ConcurrentDictionary loggingPresences = new ConcurrentDictionary(); + private readonly ConcurrentDictionary logs = new ConcurrentDictionary(); + private readonly ConcurrentDictionary loggingPresences = new ConcurrentDictionary(); // - private ConcurrentDictionary voiceChannelLog = new ConcurrentDictionary(); + private readonly ConcurrentDictionary voiceChannelLog = new ConcurrentDictionary(); - public override Func DoFunc() => async e => { + public Func DoFunc() => async e => { if (!NadekoBot.IsOwner(e.User.Id) || !e.User.ServerPermissions.ManageServer) return; @@ -107,7 +107,7 @@ namespace NadekoBot.Commands { } catch { } } - public override void Init(CommandGroupBuilder cgb) { + public void Init(CommandGroupBuilder cgb) { cgb.CreateCommand(".logserver") .Description("Toggles logging in this channel. Logs every message sent/deleted/edited on the server. BOT OWNER ONLY. SERVER OWNER ONLY.") .Do(DoFunc()); diff --git a/NadekoBot/Commands/PlayingRotate.cs b/NadekoBot/Commands/PlayingRotate.cs index f6680df8..472fe9ff 100644 --- a/NadekoBot/Commands/PlayingRotate.cs +++ b/NadekoBot/Commands/PlayingRotate.cs @@ -8,9 +8,9 @@ using System.Timers; using NadekoBot.Modules; namespace NadekoBot.Commands { - internal class PlayingRotate : DiscordCommand { + internal class PlayingRotate : IDiscordCommand { - private static List rotatingStatuses = new List(); + private static readonly List rotatingStatuses = new List(); private static readonly Timer timer = new Timer(12000); public static Dictionary> PlayingPlaceholders { get; } = @@ -60,7 +60,7 @@ namespace NadekoBot.Commands { }; } - public override Func DoFunc() => async e => { + public Func DoFunc() => async e => { if (timer.Enabled) timer.Stop(); else @@ -68,7 +68,7 @@ namespace NadekoBot.Commands { await e.Channel.SendMessage($"❗`Rotating playing status has been {(timer.Enabled ? "enabled" : "disabled")}.`"); }; - public override void Init(CommandGroupBuilder cgb) { + public void Init(CommandGroupBuilder cgb) { cgb.CreateCommand(".rotateplaying") .Alias(".ropl") .Description("Toggles rotation of playing status of the dynamic strings you specified earlier.") diff --git a/NadekoBot/Commands/PollCommand.cs b/NadekoBot/Commands/PollCommand.cs index 61e84b02..484a8cb1 100644 --- a/NadekoBot/Commands/PollCommand.cs +++ b/NadekoBot/Commands/PollCommand.cs @@ -8,15 +8,15 @@ using Discord; using Discord.Commands; namespace NadekoBot.Commands { - internal class PollCommand : DiscordCommand { + internal class PollCommand : IDiscordCommand { public static ConcurrentDictionary ActivePolls = new ConcurrentDictionary(); - public override Func DoFunc() { + public Func DoFunc() { throw new NotImplementedException(); } - public override void Init(CommandGroupBuilder cgb) { + public void Init(CommandGroupBuilder cgb) { cgb.CreateCommand(">poll") .Description("Creates a poll, only person who has manage server permission can do it.\n**Usage**: >poll Question?;Answer1;Answ 2;A_3") .Parameter("allargs", ParameterType.Unparsed) diff --git a/NadekoBot/Commands/RequestsCommand.cs b/NadekoBot/Commands/RequestsCommand.cs index 7f1037bc..5590f323 100644 --- a/NadekoBot/Commands/RequestsCommand.cs +++ b/NadekoBot/Commands/RequestsCommand.cs @@ -4,7 +4,7 @@ using Discord.Commands; using NadekoBot.Extensions; namespace NadekoBot.Commands { - internal class RequestsCommand : DiscordCommand { + internal class RequestsCommand : IDiscordCommand { public void SaveRequest(CommandEventArgs e, string text) { Classes.DbHandler.Instance.InsertData(new Classes._DataModels.Request { RequestText = text, @@ -37,11 +37,7 @@ namespace NadekoBot.Commands { public Classes._DataModels.Request ResolveRequest(int requestNumber) => Classes.DbHandler.Instance.Delete(requestNumber); - public override Func DoFunc() { - throw new NotImplementedException(); - } - - public override void Init(CommandGroupBuilder cgb) { + public void Init(CommandGroupBuilder cgb) { cgb.CreateCommand("req") .Alias("request") @@ -95,7 +91,7 @@ namespace NadekoBot.Commands { var sc = ResolveRequest(int.Parse(e.Args[0])); if (sc != null) { await e.Channel.SendMessage(e.User.Mention + " Request resolved, notice sent."); - await client.GetServer((ulong)sc.ServerId).GetUser((ulong)sc.UserId).Send("**This request of yours has been resolved:**\n" + sc.RequestText); + await NadekoBot.Client.GetServer((ulong)sc.ServerId).GetUser((ulong)sc.UserId).Send("**This request of yours has been resolved:**\n" + sc.RequestText); } else { await e.Channel.SendMessage("No request on that number."); } diff --git a/NadekoBot/Commands/ServerGreetCommand.cs b/NadekoBot/Commands/ServerGreetCommand.cs index e2184b48..5adf260d 100644 --- a/NadekoBot/Commands/ServerGreetCommand.cs +++ b/NadekoBot/Commands/ServerGreetCommand.cs @@ -21,7 +21,7 @@ public class AsyncLazy : Lazy> */ namespace NadekoBot.Commands { - internal class ServerGreetCommand : DiscordCommand { + internal class ServerGreetCommand : IDiscordCommand { public static ConcurrentDictionary AnnouncementsDictionary; @@ -33,11 +33,11 @@ namespace NadekoBot.Commands { NadekoBot.Client.UserJoined += UserJoined; NadekoBot.Client.UserLeft += UserLeft; - List data = Classes.DbHandler.Instance.GetAllRows(); + var data = Classes.DbHandler.Instance.GetAllRows(); - if (data.Any()) - foreach (var obj in data) - AnnouncementsDictionary.TryAdd((ulong)obj.ServerId, new AnnounceControls(obj)); + if (!data.Any()) return; + foreach (var obj in data) + AnnouncementsDictionary.TryAdd((ulong)obj.ServerId, new AnnounceControls(obj)); } private async void UserLeft(object sender, UserEventArgs e) { @@ -172,11 +172,11 @@ namespace NadekoBot.Commands { } } - public override Func DoFunc() { + public Func DoFunc() { throw new NotImplementedException(); } - public override void Init(CommandGroupBuilder cgb) { + public void Init(CommandGroupBuilder cgb) { cgb.CreateCommand(".greet") .Description("Enables or Disables anouncements on the current channel when someone joins the server.") diff --git a/NadekoBot/Commands/SpeedTyping.cs b/NadekoBot/Commands/SpeedTyping.cs index 072a330a..c8da5740 100644 --- a/NadekoBot/Commands/SpeedTyping.cs +++ b/NadekoBot/Commands/SpeedTyping.cs @@ -21,12 +21,12 @@ namespace NadekoBot.Commands { } public class TypingGame { - public static float WORD_VALUE { get; } = 4.5f; - private Channel channel; - public string currentSentence; + public const float WORD_VALUE = 4.5f; + private readonly Channel channel; + public string CurrentSentence; public bool IsActive; - private Stopwatch sw; - private List finishedUserIds; + private readonly Stopwatch sw; + private readonly List finishedUserIds; public TypingGame(Channel channel) { this.channel = channel; @@ -49,32 +49,33 @@ namespace NadekoBot.Commands { } internal async Task Start() { - if (IsActive) return; // can't start running game - IsActive = true; - currentSentence = SentencesProvider.GetRandomSentence(); - int i = (int)(currentSentence.Length / WORD_VALUE * 1.7f); - await channel.SendMessage($":clock2: Next contest will last for {i} seconds. Type the bolded text as fast as you can."); + while (true) { + if (IsActive) return; // can't start running game + IsActive = true; + CurrentSentence = SentencesProvider.GetRandomSentence(); + var i = (int) (CurrentSentence.Length/WORD_VALUE*1.7f); + await channel.SendMessage($":clock2: Next contest will last for {i} seconds. Type the bolded text as fast as you can."); - var msg = await channel.SendMessage("Starting new typing contest in **3**..."); - await Task.Delay(1000); - await msg.Edit("Starting new typing contest in **2**..."); - await Task.Delay(1000); - await msg.Edit("Starting new typing contest in **1**..."); - await Task.Delay(1000); - await msg.Edit($":book:**{currentSentence.Replace(" ", " \x200B")}**:book:"); - sw.Start(); - HandleAnswers(); - - while (i > 0) { + var msg = await channel.SendMessage("Starting new typing contest in **3**..."); await Task.Delay(1000); - i--; - if (!IsActive) - return; - } + await msg.Edit("Starting new typing contest in **2**..."); + await Task.Delay(1000); + await msg.Edit("Starting new typing contest in **1**..."); + await Task.Delay(1000); + await msg.Edit($":book:**{CurrentSentence.Replace(" ", " \x200B")}**:book:"); + sw.Start(); + HandleAnswers(); - await Stop(); - await Start(); + while (i > 0) { + await Task.Delay(1000); + i--; + if (!IsActive) + return; + } + + await Stop(); + } } private void HandleAnswers() { @@ -87,13 +88,13 @@ namespace NadekoBot.Commands { var guess = e.Message.RawText; - var distance = currentSentence.LevenshteinDistance(guess); + var distance = CurrentSentence.LevenshteinDistance(guess); var decision = Judge(distance, guess.Length); if (decision && !finishedUserIds.Contains(e.User.Id)) { finishedUserIds.Add(e.User.Id); - await channel.Send($"{e.User.Mention} finished in **{sw.Elapsed.Seconds}** seconds with { distance } errors, **{ currentSentence.Length / TypingGame.WORD_VALUE / sw.Elapsed.Seconds * 60 }** WPM!"); + await channel.Send($"{e.User.Mention} finished in **{sw.Elapsed.Seconds}** seconds with { distance } errors, **{ CurrentSentence.Length / TypingGame.WORD_VALUE / sw.Elapsed.Seconds * 60 }** WPM!"); if (finishedUserIds.Count % 2 == 0) { - await e.Channel.SendMessage($":exclamation: `A lot of people finished, here is the text for those still typing:`\n\n:book:**{currentSentence}**:book:"); + await e.Channel.SendMessage($":exclamation: `A lot of people finished, here is the text for those still typing:`\n\n:book:**{CurrentSentence}**:book:"); } } @@ -105,7 +106,7 @@ namespace NadekoBot.Commands { } - internal class SpeedTyping : DiscordCommand { + internal class SpeedTyping : IDiscordCommand { private static Dictionary runningContests; @@ -113,7 +114,7 @@ namespace NadekoBot.Commands { runningContests = new Dictionary(); } - public override Func DoFunc() => + public Func DoFunc() => async e => { if (runningContests.ContainsKey(e.User.Server.Id) && runningContests[e.User.Server.Id].IsActive) { await e.Channel.SendMessage($"Contest already running in { runningContests[e.User.Server.Id].Channell.Mention } channel."); @@ -138,7 +139,7 @@ namespace NadekoBot.Commands { await e.Channel.SendMessage("No contest to stop on this channel."); }; - public override void Init(CommandGroupBuilder cgb) { + public void Init(CommandGroupBuilder cgb) { cgb.CreateCommand("typestart") .Description("Starts a typing contest.") .Do(DoFunc()); diff --git a/NadekoBot/Commands/TriviaCommand.cs b/NadekoBot/Commands/TriviaCommand.cs index 7e3b54cc..d5003129 100644 --- a/NadekoBot/Commands/TriviaCommand.cs +++ b/NadekoBot/Commands/TriviaCommand.cs @@ -6,10 +6,10 @@ using Discord; using TriviaGame = NadekoBot.Classes.Trivia.TriviaGame; namespace NadekoBot.Commands { - internal class Trivia : DiscordCommand { + internal class Trivia : IDiscordCommand { public static ConcurrentDictionary runningTrivias = new ConcurrentDictionary(); - public override Func DoFunc() => async e => { + public Func DoFunc() => async e => { TriviaGame trivia; if (!runningTrivias.TryGetValue(e.Server, out trivia)) { var triviaGame = new TriviaGame(e); @@ -21,7 +21,7 @@ namespace NadekoBot.Commands { await e.Channel.SendMessage("Trivia game is already running on this server.\n" + trivia.CurrentQuestion); }; - public override void Init(CommandGroupBuilder cgb) { + public void Init(CommandGroupBuilder cgb) { cgb.CreateCommand("t") .Description("Starts a game of trivia.") .Alias("-t") diff --git a/NadekoBot/Commands/VoiceNotificationCommand.cs b/NadekoBot/Commands/VoiceNotificationCommand.cs index 02e2e52c..a17d184f 100644 --- a/NadekoBot/Commands/VoiceNotificationCommand.cs +++ b/NadekoBot/Commands/VoiceNotificationCommand.cs @@ -6,17 +6,12 @@ using System.Collections.Concurrent; using Discord; namespace NadekoBot.Commands { - internal class VoiceNotificationCommand : DiscordCommand { - - - public VoiceNotificationCommand() { - //NadekoBot.client. - } + internal class VoiceNotificationCommand : IDiscordCommand { //voicechannel/text channel - private ConcurrentDictionary subscribers = new ConcurrentDictionary(); + private readonly ConcurrentDictionary subscribers = new ConcurrentDictionary(); - public override Func DoFunc() => async e => { + public Func DoFunc() => async e => { var arg = e.GetArg("voice_name"); if (string.IsNullOrWhiteSpace("voice_name")) return; @@ -32,7 +27,7 @@ namespace NadekoBot.Commands { } }; - public override void Init(CommandGroupBuilder cgb) { + public void Init(CommandGroupBuilder cgb) { /* cgb.CreateCommand(".voicenotif") .Description("Enables notifications on who joined/left the voice channel.\n**Usage**:.voicenotif Karaoke club") diff --git a/NadekoBot/Modules/DiscordModule.cs b/NadekoBot/Modules/DiscordModule.cs index ce961269..90550032 100644 --- a/NadekoBot/Modules/DiscordModule.cs +++ b/NadekoBot/Modules/DiscordModule.cs @@ -4,10 +4,7 @@ using NadekoBot.Commands; namespace NadekoBot.Modules { internal abstract class DiscordModule : IModule { - protected List commands = new List(); - - protected DiscordModule() { - } + protected readonly HashSet commands = new HashSet(); public abstract void Install(ModuleManager manager); } diff --git a/NadekoBot/Modules/Games.cs b/NadekoBot/Modules/Games.cs index 745124eb..14d5c0b7 100644 --- a/NadekoBot/Modules/Games.cs +++ b/NadekoBot/Modules/Games.cs @@ -4,6 +4,8 @@ using Discord.Modules; using NadekoBot.Commands; using Newtonsoft.Json.Linq; using System.IO; +using NadekoBot.Extensions; + //πŸƒ //🏁 namespace NadekoBot.Modules { diff --git a/NadekoBot/Modules/Help.cs b/NadekoBot/Modules/Help.cs index 063c363e..821937d4 100644 --- a/NadekoBot/Modules/Help.cs +++ b/NadekoBot/Modules/Help.cs @@ -2,6 +2,7 @@ using Discord.Modules; using Discord.Commands; using NadekoBot.Commands; +using NadekoBot.Extensions; namespace NadekoBot.Modules { internal class Help : DiscordModule { diff --git a/NadekoBot/Modules/Permissions.cs b/NadekoBot/Modules/Permissions.cs index 2f5bf86b..3aef9e3d 100644 --- a/NadekoBot/Modules/Permissions.cs +++ b/NadekoBot/Modules/Permissions.cs @@ -4,6 +4,7 @@ using Discord.Commands; using NadekoBot.Classes; using PermsHandler = NadekoBot.Classes.Permissions.PermissionsHandler; using System.Linq; +using NadekoBot.Extensions; namespace NadekoBot.Modules { internal class PermissionModule : DiscordModule { @@ -14,7 +15,6 @@ namespace NadekoBot.Modules { } //todo word filtering/invite bans (?:discord(?:\.gg|app\.com\/invite)\/(?([\w]{16}|(?:[\w]+-?){3}))) public override void Install(ModuleManager manager) { - var client = NadekoBot.Client; manager.CreateCommands("", cgb => { cgb.AddCheck(Classes.Permissions.PermissionChecker.Instance); @@ -75,7 +75,7 @@ namespace NadekoBot.Modules { var role = e.Server.EveryoneRole; if (!string.IsNullOrWhiteSpace(arg)) try { - role = PermissionHelper.ValidateRole(e.Server, e.GetArg("role")); + role = PermissionHelper.ValidateRole(e.Server, arg); } catch (Exception ex) { await e.Channel.SendMessage("πŸ’’ Error: " + ex.Message); @@ -98,7 +98,7 @@ namespace NadekoBot.Modules { var channel = e.Channel; if (!string.IsNullOrWhiteSpace(arg)) try { - channel = PermissionHelper.ValidateChannel(e.Server, e.GetArg("channel")); + channel = PermissionHelper.ValidateChannel(e.Server, arg); } catch (Exception ex) { await e.Channel.SendMessage("πŸ’’ Error: " + ex.Message); @@ -116,7 +116,6 @@ namespace NadekoBot.Modules { .Description("Shows banned permissions for a certain user. No argument means for yourself.\n**Usage**: ;up Kwoth") .Parameter("user", ParameterType.Unparsed) .Do(async e => { - var arg = e.GetArg("user"); var user = e.User; if (!string.IsNullOrWhiteSpace(e.GetArg("user"))) try { diff --git a/NadekoBot/Modules/Searches.cs b/NadekoBot/Modules/Searches.cs index 65c60a32..24d4682f 100644 --- a/NadekoBot/Modules/Searches.cs +++ b/NadekoBot/Modules/Searches.cs @@ -127,8 +127,8 @@ namespace NadekoBot.Modules { var headers = new Dictionary { { "X-Mashape-Key", NadekoBot.Creds.MashapeKey } }; var res = await SearchHelper.GetResponseStringAsync($"https://omgvamp-hearthstone-v1.p.mashape.com/cards/search/{Uri.EscapeUriString(arg)}", headers); try { - var items = JArray.Parse(res) as JArray; - var images = new List(); + var items = JArray.Parse(res); + var images = new List(); if (items == null) throw new KeyNotFoundException("Cannot find a card by that name"); var cnt = 0; diff --git a/NadekoBot/NadekoBot.cs b/NadekoBot/NadekoBot.cs index 036152ec..55e7d0ad 100644 --- a/NadekoBot/NadekoBot.cs +++ b/NadekoBot/NadekoBot.cs @@ -9,6 +9,7 @@ using Discord.Audio; using System.Linq; using System.Text; using System.Threading.Tasks; +using Discord.Commands.Permissions.Userlist; using NadekoBot.Classes.JSONModels; using NadekoBot.Commands; diff --git a/NadekoBot/NadekoBot.csproj b/NadekoBot/NadekoBot.csproj index e39ae325..b2d3f0c5 100644 --- a/NadekoBot/NadekoBot.csproj +++ b/NadekoBot/NadekoBot.csproj @@ -155,7 +155,7 @@ - +