From e66615b0561b831e3663a60ad0e14a9415643c2c Mon Sep 17 00:00:00 2001 From: Kwoth Date: Sat, 23 Jul 2016 15:04:33 +0200 Subject: [PATCH 01/33] Changed all locks to SemaphoreSlims, Slight musicbuffer changes. --- NadekoBot/Classes/ServerSpecificConfig.cs | 24 ++- .../Administration/AdministrationModule.cs | 2 +- .../Commands/CustomReactionsCommands.cs | 6 +- .../Administration/Commands/PlayingRotate.cs | 33 +++-- .../ClashOfClans/ClashOfClansModule.cs | 3 +- NadekoBot/Modules/Games/Commands/PlantPick.cs | 25 ++-- .../Games/Commands/Trivia/TriviaGame.cs | 6 +- .../Modules/Music/Classes/PoopyBuffer.cs | 137 ++++++++++-------- NadekoBot/Modules/Music/Classes/Song.cs | 2 +- .../Modules/Permissions/PermissionsModule.cs | 10 +- .../Modules/Searches/Commands/LoLCommands.cs | 39 ++--- .../Searches/Commands/StreamNotifications.cs | 4 +- NadekoBot/NadekoBot.cs | 2 +- NadekoBot/NadekoBot.csproj | 3 +- NadekoBot/_Models/JSONModels/Configuration.cs | 12 +- 15 files changed, 173 insertions(+), 135 deletions(-) diff --git a/NadekoBot/Classes/ServerSpecificConfig.cs b/NadekoBot/Classes/ServerSpecificConfig.cs index ff42f0fd..9142e574 100644 --- a/NadekoBot/Classes/ServerSpecificConfig.cs +++ b/NadekoBot/Classes/ServerSpecificConfig.cs @@ -6,6 +6,8 @@ using System.Collections.ObjectModel; using System.ComponentModel; using System.IO; using System.Runtime.CompilerServices; +using System.Threading; +using System.Threading.Tasks; namespace NadekoBot.Classes { @@ -27,9 +29,12 @@ namespace NadekoBot.Classes { configs = JsonConvert .DeserializeObject>( - File.ReadAllText(filePath), new JsonSerializerSettings() { - Error = (s,e) => { - if (e.ErrorContext.Member.ToString() == "GenerateCurrencyChannels") { + File.ReadAllText(filePath), new JsonSerializerSettings() + { + Error = (s, e) => + { + if (e.ErrorContext.Member.ToString() == "GenerateCurrencyChannels") + { e.ErrorContext.Handled = true; } } @@ -52,14 +57,19 @@ namespace NadekoBot.Classes public ServerSpecificConfig Of(ulong id) => configs.GetOrAdd(id, _ => new ServerSpecificConfig()); - private readonly object saveLock = new object(); + private readonly SemaphoreSlim saveLock = new SemaphoreSlim(1, 1); - public void Save() + public async Task Save() { - lock (saveLock) + await saveLock.WaitAsync(); + try { File.WriteAllText(filePath, JsonConvert.SerializeObject(configs, Formatting.Indented)); } + finally + { + saveLock.Release(); + } } } @@ -245,7 +255,7 @@ namespace NadekoBot.Classes LogserverIgnoreChannels = new ObservableCollection(); } - public event PropertyChangedEventHandler PropertyChanged = delegate { SpecificConfigurations.Default.Save(); }; + public event PropertyChangedEventHandler PropertyChanged = async delegate { await SpecificConfigurations.Default.Save().ConfigureAwait(false); }; private void OnPropertyChanged([CallerMemberName] string propertyName = null) { diff --git a/NadekoBot/Modules/Administration/AdministrationModule.cs b/NadekoBot/Modules/Administration/AdministrationModule.cs index ab6dafae..ad28d083 100644 --- a/NadekoBot/Modules/Administration/AdministrationModule.cs +++ b/NadekoBot/Modules/Administration/AdministrationModule.cs @@ -70,7 +70,7 @@ namespace NadekoBot.Modules.Administration { var conf = SpecificConfigurations.Default.Of(e.Server.Id); conf.AutoDeleteMessagesOnCommand = !conf.AutoDeleteMessagesOnCommand; - Classes.JSONModels.ConfigHandler.SaveConfig(); + await Classes.JSONModels.ConfigHandler.SaveConfig().ConfigureAwait(false); if (conf.AutoDeleteMessagesOnCommand) await e.Channel.SendMessage("❗`Now automatically deleting successfull command invokations.`"); else diff --git a/NadekoBot/Modules/Administration/Commands/CustomReactionsCommands.cs b/NadekoBot/Modules/Administration/Commands/CustomReactionsCommands.cs index fcd970a7..a01f9a8d 100644 --- a/NadekoBot/Modules/Administration/Commands/CustomReactionsCommands.cs +++ b/NadekoBot/Modules/Administration/Commands/CustomReactionsCommands.cs @@ -40,7 +40,7 @@ namespace NadekoBot.Modules.Administration.Commands NadekoBot.Config.CustomReactions[name].Add(message); else NadekoBot.Config.CustomReactions.Add(name, new System.Collections.Generic.List() { message }); - await Task.Run(() => Classes.JSONModels.ConfigHandler.SaveConfig()).ConfigureAwait(false); + await Classes.JSONModels.ConfigHandler.SaveConfig().ConfigureAwait(false); await e.Channel.SendMessage($"Added {name} : {message}").ConfigureAwait(false); }); @@ -140,7 +140,7 @@ namespace NadekoBot.Modules.Administration.Commands index = index - 1; NadekoBot.Config.CustomReactions[name][index] = msg; - await Task.Run(() => Classes.JSONModels.ConfigHandler.SaveConfig()).ConfigureAwait(false); + await Classes.JSONModels.ConfigHandler.SaveConfig().ConfigureAwait(false); await e.Channel.SendMessage($"Edited response #{index + 1} from `{name}`").ConfigureAwait(false); }); @@ -183,7 +183,7 @@ namespace NadekoBot.Modules.Administration.Commands NadekoBot.Config.CustomReactions.Remove(name); message = $"Deleted custom reaction: `{name}`"; } - await Task.Run(() => Classes.JSONModels.ConfigHandler.SaveConfig()).ConfigureAwait(false); + await Classes.JSONModels.ConfigHandler.SaveConfig().ConfigureAwait(false); await e.Channel.SendMessage(message).ConfigureAwait(false); }); } diff --git a/NadekoBot/Modules/Administration/Commands/PlayingRotate.cs b/NadekoBot/Modules/Administration/Commands/PlayingRotate.cs index aa7570cb..2d22d870 100644 --- a/NadekoBot/Modules/Administration/Commands/PlayingRotate.cs +++ b/NadekoBot/Modules/Administration/Commands/PlayingRotate.cs @@ -7,8 +7,10 @@ using System; using System.Collections.Generic; using System.Linq; using System.Text; +using System.Threading; using System.Threading.Tasks; using System.Timers; +using Timer = System.Timers.Timer; namespace NadekoBot.Modules.Administration.Commands { @@ -36,7 +38,7 @@ namespace NadekoBot.Modules.Administration.Commands {"%trivia%", () => Games.Commands.TriviaCommands.RunningTrivias.Count.ToString()} }; - private readonly object playingPlaceholderLock = new object(); + private readonly SemaphoreSlim playingPlaceholderLock = new SemaphoreSlim(1,1); public PlayingRotate(DiscordModule module) : base(module) { @@ -47,7 +49,9 @@ namespace NadekoBot.Modules.Administration.Commands { i++; var status = ""; - lock (playingPlaceholderLock) + //wtf am i doing, just use a queue ffs + await playingPlaceholderLock.WaitAsync().ConfigureAwait(false); + try { if (PlayingPlaceholders.Count == 0 || NadekoBot.Config.RotatingStatuses.Count == 0 @@ -59,6 +63,7 @@ namespace NadekoBot.Modules.Administration.Commands status = PlayingPlaceholders.Aggregate(status, (current, kvp) => current.Replace(kvp.Key, kvp.Value())); } + finally { playingPlaceholderLock.Release(); } if (string.IsNullOrWhiteSpace(status)) return; await Task.Run(() => { NadekoBot.Client.SetGame(status); }); @@ -71,14 +76,18 @@ namespace NadekoBot.Modules.Administration.Commands public Func DoFunc() => async e => { - lock (playingPlaceholderLock) + await playingPlaceholderLock.WaitAsync().ConfigureAwait(false); + try { if (timer.Enabled) timer.Stop(); else timer.Start(); NadekoBot.Config.IsRotatingStatus = timer.Enabled; - ConfigHandler.SaveConfig(); + await ConfigHandler.SaveConfig().ConfigureAwait(false); + } + finally { + playingPlaceholderLock.Release(); } await e.Channel.SendMessage($"❗`Rotating playing status has been {(timer.Enabled ? "enabled" : "disabled")}.`").ConfigureAwait(false); }; @@ -102,10 +111,15 @@ namespace NadekoBot.Modules.Administration.Commands var arg = e.GetArg("text"); if (string.IsNullOrWhiteSpace(arg)) return; - lock (playingPlaceholderLock) + await playingPlaceholderLock.WaitAsync().ConfigureAwait(false); + try { NadekoBot.Config.RotatingStatuses.Add(arg); - ConfigHandler.SaveConfig(); + await ConfigHandler.SaveConfig(); + } + finally + { + playingPlaceholderLock.Release(); } await e.Channel.SendMessage("πŸ†— `Added a new playing string.`").ConfigureAwait(false); }); @@ -137,14 +151,15 @@ namespace NadekoBot.Modules.Administration.Commands var arg = e.GetArg("number"); int num; string str; - lock (playingPlaceholderLock) - { + await playingPlaceholderLock.WaitAsync().ConfigureAwait(false); + try { if (!int.TryParse(arg.Trim(), out num) || num <= 0 || num > NadekoBot.Config.RotatingStatuses.Count) return; str = NadekoBot.Config.RotatingStatuses[num - 1]; NadekoBot.Config.RotatingStatuses.RemoveAt(num - 1); - ConfigHandler.SaveConfig(); + await ConfigHandler.SaveConfig().ConfigureAwait(false); } + finally { playingPlaceholderLock.Release(); } await e.Channel.SendMessage($"πŸ†— `Removed playing string #{num}`({str})").ConfigureAwait(false); }); } diff --git a/NadekoBot/Modules/ClashOfClans/ClashOfClansModule.cs b/NadekoBot/Modules/ClashOfClans/ClashOfClansModule.cs index 9c383509..c5ba20fc 100644 --- a/NadekoBot/Modules/ClashOfClans/ClashOfClansModule.cs +++ b/NadekoBot/Modules/ClashOfClans/ClashOfClansModule.cs @@ -7,6 +7,7 @@ using System.Collections.Generic; using System.Text; using System.Threading.Tasks; using NadekoBot.Modules.Permissions.Classes; +using System.Threading; namespace NadekoBot.Modules.ClashOfClans { @@ -16,8 +17,6 @@ namespace NadekoBot.Modules.ClashOfClans public static ConcurrentDictionary> ClashWars { get; } = new ConcurrentDictionary>(); - private readonly object writeLock = new object(); - public override void Install(ModuleManager manager) { manager.CreateCommands("", cgb => diff --git a/NadekoBot/Modules/Games/Commands/PlantPick.cs b/NadekoBot/Modules/Games/Commands/PlantPick.cs index 6af69857..e2bce2ec 100644 --- a/NadekoBot/Modules/Games/Commands/PlantPick.cs +++ b/NadekoBot/Modules/Games/Commands/PlantPick.cs @@ -9,6 +9,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Security.Cryptography; +using System.Threading; using System.Threading.Tasks; namespace NadekoBot.Modules.Games.Commands @@ -59,7 +60,7 @@ namespace NadekoBot.Modules.Games.Commands //channelid/messageid pair ConcurrentDictionary> plantedFlowerChannels = new ConcurrentDictionary>(); - private object locker = new object(); + private SemaphoreSlim locker = new SemaphoreSlim(1,1); internal override void Init(CommandGroupBuilder cgb) { @@ -84,32 +85,34 @@ namespace NadekoBot.Modules.Games.Commands cgb.CreateCommand(Module.Prefix + "plant") .Description("Spend a flower to plant it in this channel. (If bot is restarted or crashes, flower will be lost)") - .Do(e => + .Do(async e => { - lock (locker) + await locker.WaitAsync().ConfigureAwait(false); + try { if (plantedFlowerChannels.ContainsKey(e.Channel.Id)) { - e.Channel.SendMessage($"There is already a {NadekoBot.Config.CurrencyName} in this channel."); + await e.Channel.SendMessage($"There is already a {NadekoBot.Config.CurrencyName} in this channel.").ConfigureAwait(false); return; } - var removed = FlowersHandler.RemoveFlowers(e.User, "Planted a flower.", 1, true).GetAwaiter().GetResult(); + var removed = await FlowersHandler.RemoveFlowers(e.User, "Planted a flower.", 1, true).ConfigureAwait(false); if (!removed) { - e.Channel.SendMessage($"You don't have any {NadekoBot.Config.CurrencyName}s.").Wait(); + await e.Channel.SendMessage($"You don't have any {NadekoBot.Config.CurrencyName}s.").ConfigureAwait(false); return; } var file = GetRandomCurrencyImagePath(); Message msg; if (file == null) - msg = e.Channel.SendMessage(NadekoBot.Config.CurrencySign).GetAwaiter().GetResult(); + msg = await e.Channel.SendMessage(NadekoBot.Config.CurrencySign).ConfigureAwait(false); else - msg = e.Channel.SendFile(file).GetAwaiter().GetResult(); + msg = await e.Channel.SendFile(file).ConfigureAwait(false); var vowelFirst = new[] { 'a', 'e', 'i', 'o', 'u' }.Contains(NadekoBot.Config.CurrencyName[0]); - var msg2 = e.Channel.SendMessage($"Oh how Nice! **{e.User.Name}** planted {(vowelFirst ? "an" : "a")} {NadekoBot.Config.CurrencyName}. Pick it using {Module.Prefix}pick").GetAwaiter().GetResult(); + var msg2 = await e.Channel.SendMessage($"Oh how Nice! **{e.User.Name}** planted {(vowelFirst ? "an" : "a")} {NadekoBot.Config.CurrencyName}. Pick it using {Module.Prefix}pick").ConfigureAwait(false); plantedFlowerChannels.TryAdd(e.Channel.Id, new[] { msg, msg2 }); } + finally { locker.Release(); } }); cgb.CreateCommand(Prefix + "gencurrency") @@ -129,12 +132,12 @@ namespace NadekoBot.Modules.Games.Commands int throwaway; if (config.GenerateCurrencyChannels.TryRemove(e.Channel.Id, out throwaway)) { - await e.Channel.SendMessage("`Currency generation disabled on this channel.`"); + await e.Channel.SendMessage("`Currency generation disabled on this channel.`").ConfigureAwait(false); } else { if (config.GenerateCurrencyChannels.TryAdd(e.Channel.Id, cd)) - await e.Channel.SendMessage($"`Currency generation enabled on this channel. Cooldown is {cd} minutes.`"); + await e.Channel.SendMessage($"`Currency generation enabled on this channel. Cooldown is {cd} minutes.`").ConfigureAwait(false); } }); } diff --git a/NadekoBot/Modules/Games/Commands/Trivia/TriviaGame.cs b/NadekoBot/Modules/Games/Commands/Trivia/TriviaGame.cs index 683eb3d0..fc5dd361 100644 --- a/NadekoBot/Modules/Games/Commands/Trivia/TriviaGame.cs +++ b/NadekoBot/Modules/Games/Commands/Trivia/TriviaGame.cs @@ -13,7 +13,7 @@ namespace NadekoBot.Modules.Games.Commands.Trivia { internal class TriviaGame { - private readonly object _guessLock = new object(); + private readonly SemaphoreSlim _guessLock = new SemaphoreSlim(1,1); private Server server { get; } private Channel channel { get; } @@ -113,7 +113,8 @@ namespace NadekoBot.Modules.Games.Commands.Trivia if (e.User.Id == NadekoBot.Client.CurrentUser.Id) return; var guess = false; - lock (_guessLock) + await _guessLock.WaitAsync().ConfigureAwait(false); + try { if (GameActive && CurrentQuestion.IsAnswerCorrect(e.Message.Text) && !triviaCancelSource.IsCancellationRequested) { @@ -122,6 +123,7 @@ namespace NadekoBot.Modules.Games.Commands.Trivia guess = true; } } + catch { _guessLock.Release(); } if (!guess) return; triviaCancelSource.Cancel(); await channel.SendMessage($"β˜‘οΈ {e.User.Mention} guessed it! The answer was: **{CurrentQuestion.Answer}**").ConfigureAwait(false); diff --git a/NadekoBot/Modules/Music/Classes/PoopyBuffer.cs b/NadekoBot/Modules/Music/Classes/PoopyBuffer.cs index 15dc4977..c4a95daa 100644 --- a/NadekoBot/Modules/Music/Classes/PoopyBuffer.cs +++ b/NadekoBot/Modules/Music/Classes/PoopyBuffer.cs @@ -22,7 +22,7 @@ namespace NadekoBot.Modules.Music.Classes public int BufferSize { get; } - private readonly object readWriteLock = new object(); + private readonly SemaphoreSlim readWriteLock = new SemaphoreSlim(1, 1); public PoopyBuffer(int size) { @@ -32,51 +32,57 @@ namespace NadekoBot.Modules.Music.Classes ringBuffer = new byte[size]; } - public int Read(byte[] buffer, int count) + public Task ReadAsync(byte[] buffer, int count) { - if (buffer.Length < count) - throw new ArgumentException(); - //Console.WriteLine($"***\nRead: {ReadPosition}\nWrite: {WritePosition}\nContentLength:{ContentLength}\n***"); - lock (readWriteLock) + return Task.Run(async () => { - //read as much as you can if you're reading too much - if (count > ContentLength) - count = ContentLength; - //if nothing to read, return 0 - if (WritePosition == ReadPosition) - return 0; - // if buffer is in the "normal" state, just read - if (WritePosition > ReadPosition) + if (buffer.Length < count) + throw new ArgumentException(); + //Console.WriteLine($"***\nRead: {ReadPosition}\nWrite: {WritePosition}\nContentLength:{ContentLength}\n***"); + await readWriteLock.WaitAsync().ConfigureAwait(false); + try { - Buffer.BlockCopy(ringBuffer, ReadPosition, buffer, 0, count); - ReadPosition += count; - //Console.WriteLine($"Read only normally1 {count}[{ReadPosition - count} to {ReadPosition}]"); + //read as much as you can if you're reading too much + if (count > ContentLength) + count = ContentLength; + //if nothing to read, return 0 + if (WritePosition == ReadPosition) + return 0; + // if buffer is in the "normal" state, just read + if (WritePosition > ReadPosition) + { + Buffer.BlockCopy(ringBuffer, ReadPosition, buffer, 0, count); + ReadPosition += count; + //Console.WriteLine($"Read only normally1 {count}[{ReadPosition - count} to {ReadPosition}]"); + return count; + } + //else ReadPos { - // if i can just write without hitting buffer.length, do it - if (WritePosition + count < BufferSize) + //the while above assures that i cannot write past readposition with my write, so i don't have to check + // *unless its multithreaded or task is not awaited + await readWriteLock.WaitAsync().ConfigureAwait(false); + try { - Buffer.BlockCopy(buffer, 0, ringBuffer, WritePosition, count); - WritePosition += count; - //Console.WriteLine($"Wrote only normally {count}[{WritePosition - count} to {WritePosition}]"); - return; + // if i can just write without hitting buffer.length, do it + if (WritePosition + count < BufferSize) + { + Buffer.BlockCopy(buffer, 0, ringBuffer, WritePosition, count); + WritePosition += count; + //Console.WriteLine($"Wrote only normally {count}[{WritePosition - count} to {WritePosition}]"); + return; + } + // otherwise, i have to write to the end, then write the rest from the start + + var wroteNormaly = BufferSize - WritePosition; + Buffer.BlockCopy(buffer, 0, ringBuffer, WritePosition, wroteNormaly); + + //Console.WriteLine($"Wrote normally {wroteNormaly}[{WritePosition} to {BufferSize}]"); + + var wroteFromStart = count - wroteNormaly; + Buffer.BlockCopy(buffer, wroteNormaly, ringBuffer, 0, wroteFromStart); + + //Console.WriteLine($"and from start {wroteFromStart} [0 to {wroteFromStart}"); + + WritePosition = wroteFromStart; } - // otherwise, i have to write to the end, then write the rest from the start - - var wroteNormaly = BufferSize - WritePosition; - Buffer.BlockCopy(buffer, 0, ringBuffer, WritePosition, wroteNormaly); - - //Console.WriteLine($"Wrote normally {wroteNormaly}[{WritePosition} to {BufferSize}]"); - - var wroteFromStart = count - wroteNormaly; - Buffer.BlockCopy(buffer, wroteNormaly, ringBuffer, 0, wroteFromStart); - - //Console.WriteLine($"and from start {wroteFromStart} [0 to {wroteFromStart}"); - - WritePosition = wroteFromStart; - } + finally { readWriteLock.Release(); } + }); } } } diff --git a/NadekoBot/Modules/Music/Classes/Song.cs b/NadekoBot/Modules/Music/Classes/Song.cs index 025863e0..9f2fb382 100644 --- a/NadekoBot/Modules/Music/Classes/Song.cs +++ b/NadekoBot/Modules/Music/Classes/Song.cs @@ -155,7 +155,7 @@ namespace NadekoBot.Modules.Music.Classes { //Console.WriteLine($"Read: {songBuffer.ReadPosition}\nWrite: {songBuffer.WritePosition}\nContentLength:{songBuffer.ContentLength}\n---------"); byte[] buffer = new byte[blockSize]; - var read = songBuffer.Read(buffer, blockSize); + var read = await songBuffer.ReadAsync(buffer, blockSize).ConfigureAwait(false); unchecked { bytesSent += (ulong)read; diff --git a/NadekoBot/Modules/Permissions/PermissionsModule.cs b/NadekoBot/Modules/Permissions/PermissionsModule.cs index 4c13b9ff..8ca83e48 100644 --- a/NadekoBot/Modules/Permissions/PermissionsModule.cs +++ b/NadekoBot/Modules/Permissions/PermissionsModule.cs @@ -689,7 +689,7 @@ namespace NadekoBot.Modules.Permissions if (!e.Message.MentionedUsers.Any()) return; var usr = e.Message.MentionedUsers.First(); NadekoBot.Config.UserBlacklist.Add(usr.Id); - ConfigHandler.SaveConfig(); + await ConfigHandler.SaveConfig().ConfigureAwait(false); await e.Channel.SendMessage($"`Sucessfully blacklisted user {usr.Name}`").ConfigureAwait(false); }).ConfigureAwait(false); }); @@ -707,7 +707,7 @@ namespace NadekoBot.Modules.Permissions if (NadekoBot.Config.UserBlacklist.Contains(usr.Id)) { NadekoBot.Config.UserBlacklist.Remove(usr.Id); - ConfigHandler.SaveConfig(); + await ConfigHandler.SaveConfig().ConfigureAwait(false); await e.Channel.SendMessage($"`Sucessfully unblacklisted user {usr.Name}`").ConfigureAwait(false); } else @@ -727,7 +727,7 @@ namespace NadekoBot.Modules.Permissions if (!e.Message.MentionedChannels.Any()) return; var ch = e.Message.MentionedChannels.First(); NadekoBot.Config.UserBlacklist.Add(ch.Id); - ConfigHandler.SaveConfig(); + await ConfigHandler.SaveConfig().ConfigureAwait(false); await e.Channel.SendMessage($"`Sucessfully blacklisted channel {ch.Name}`").ConfigureAwait(false); }).ConfigureAwait(false); }); @@ -742,7 +742,7 @@ namespace NadekoBot.Modules.Permissions if (!e.Message.MentionedChannels.Any()) return; var ch = e.Message.MentionedChannels.First(); NadekoBot.Config.UserBlacklist.Remove(ch.Id); - ConfigHandler.SaveConfig(); + await ConfigHandler.SaveConfig().ConfigureAwait(false); await e.Channel.SendMessage($"`Sucessfully blacklisted channel {ch.Name}`").ConfigureAwait(false); }).ConfigureAwait(false); }); @@ -767,7 +767,7 @@ namespace NadekoBot.Modules.Permissions } var serverId = server.Id; NadekoBot.Config.ServerBlacklist.Add(serverId); - ConfigHandler.SaveConfig(); + await ConfigHandler.SaveConfig().ConfigureAwait(false); //cleanup trivias and typeracing Modules.Games.Commands.Trivia.TriviaGame trivia; TriviaCommands.RunningTrivias.TryRemove(serverId, out trivia); diff --git a/NadekoBot/Modules/Searches/Commands/LoLCommands.cs b/NadekoBot/Modules/Searches/Commands/LoLCommands.cs index bbe2eca9..8826beaf 100644 --- a/NadekoBot/Modules/Searches/Commands/LoLCommands.cs +++ b/NadekoBot/Modules/Searches/Commands/LoLCommands.cs @@ -30,8 +30,6 @@ namespace NadekoBot.Modules.Searches.Commands } private static Dictionary CachedChampionImages = new Dictionary(); - private readonly object cacheLock = new object(); - private System.Timers.Timer clearTimer { get; } = new System.Timers.Timer(); public LoLCommands(DiscordModule module) : base(module) @@ -42,7 +40,6 @@ namespace NadekoBot.Modules.Searches.Commands { try { - lock (cacheLock) CachedChampionImages = CachedChampionImages .Where(kvp => DateTime.Now - kvp.Value.AddedAt > new TimeSpan(1, 0, 0)) .ToDictionary(kvp => kvp.Key, kvp => kvp.Value); @@ -87,16 +84,14 @@ namespace NadekoBot.Modules.Searches.Commands var resolvedRole = role; var name = e.GetArg("champ").Replace(" ", "").ToLower(); CachedChampion champ = null; - lock (cacheLock) - { - CachedChampionImages.TryGetValue(name + "_" + resolvedRole, out champ); - } - if (champ != null) - { - champ.ImageStream.Position = 0; - await e.Channel.SendFile("champ.png", champ.ImageStream).ConfigureAwait(false); - return; - } + + if(CachedChampionImages.TryGetValue(name + "_" + resolvedRole, out champ)) + if (champ != null) + { + champ.ImageStream.Position = 0; + await e.Channel.SendFile("champ.png", champ.ImageStream).ConfigureAwait(false); + return; + } var allData = JArray.Parse(await Classes.SearchHelper.GetResponseStringAsync($"http://api.champion.gg/champion/{name}?api_key={NadekoBot.Creds.LOLAPIKey}").ConfigureAwait(false)); JToken data = null; if (role != null) @@ -121,17 +116,13 @@ namespace NadekoBot.Modules.Searches.Commands role = allData[0]["role"].ToString(); resolvedRole = ResolvePos(role); } - lock (cacheLock) - { - CachedChampionImages.TryGetValue(name + "_" + resolvedRole, out champ); - } - if (champ != null) - { - Console.WriteLine("Sending lol image from cache."); - champ.ImageStream.Position = 0; - await e.Channel.SendFile("champ.png", champ.ImageStream).ConfigureAwait(false); - return; - } + if(CachedChampionImages.TryGetValue(name + "_" + resolvedRole, out champ)) + if (champ != null) + { + champ.ImageStream.Position = 0; + await e.Channel.SendFile("champ.png", champ.ImageStream).ConfigureAwait(false); + return; + } //name = data["title"].ToString(); // get all possible roles, and "select" the shown one var roles = new string[allData.Count]; diff --git a/NadekoBot/Modules/Searches/Commands/StreamNotifications.cs b/NadekoBot/Modules/Searches/Commands/StreamNotifications.cs index d5c6c755..efe521ff 100644 --- a/NadekoBot/Modules/Searches/Commands/StreamNotifications.cs +++ b/NadekoBot/Modules/Searches/Commands/StreamNotifications.cs @@ -68,7 +68,7 @@ namespace NadekoBot.Modules.Searches.Commands } } catch { } - ConfigHandler.SaveConfig(); + await ConfigHandler.SaveConfig().ConfigureAwait(false); }; checkTimer.Start(); } @@ -254,7 +254,7 @@ namespace NadekoBot.Modules.Searches.Commands } config.ObservingStreams.Remove(toRemove); - ConfigHandler.SaveConfig(); + await ConfigHandler.SaveConfig().ConfigureAwait(false); await e.Channel.SendMessage($":ok: Removed `{toRemove.Username}`'s stream from notifications.").ConfigureAwait(false); }); diff --git a/NadekoBot/NadekoBot.cs b/NadekoBot/NadekoBot.cs index 4a091e3e..ad68beca 100644 --- a/NadekoBot/NadekoBot.cs +++ b/NadekoBot/NadekoBot.cs @@ -197,7 +197,7 @@ namespace NadekoBot return; } #if NADEKO_RELEASE - await Task.Delay(90000).ConfigureAwait(false); + await Task.Delay(100000).ConfigureAwait(false); #else await Task.Delay(1000).ConfigureAwait(false); #endif diff --git a/NadekoBot/NadekoBot.csproj b/NadekoBot/NadekoBot.csproj index cd467b7f..4e16fee4 100644 --- a/NadekoBot/NadekoBot.csproj +++ b/NadekoBot/NadekoBot.csproj @@ -78,13 +78,14 @@ true - bin\x64\Debug\ + bin\Debug\ TRACE;DEBUG full x64 prompt MinimumRecommendedRules.ruleset true + false bin\x64\Release\ diff --git a/NadekoBot/_Models/JSONModels/Configuration.cs b/NadekoBot/_Models/JSONModels/Configuration.cs index 295134fe..d39c6e00 100644 --- a/NadekoBot/_Models/JSONModels/Configuration.cs +++ b/NadekoBot/_Models/JSONModels/Configuration.cs @@ -4,6 +4,8 @@ using Newtonsoft.Json; using System.Collections.Generic; using System.IO; using System.Runtime.Serialization; +using System.Threading; +using System.Threading.Tasks; namespace NadekoBot.Classes.JSONModels { @@ -187,13 +189,17 @@ Nadeko Support Server: "; public static class ConfigHandler { - private static readonly object configLock = new object(); - public static void SaveConfig() + private static readonly SemaphoreSlim configLock = new SemaphoreSlim(1, 1); + public static async Task SaveConfig() { - lock (configLock) + await configLock.WaitAsync(); + try { File.WriteAllText("data/config.json", JsonConvert.SerializeObject(NadekoBot.Config, Formatting.Indented)); } + finally { + configLock.Release(); + } } public static bool IsBlackListed(MessageEventArgs evArgs) => IsUserBlacklisted(evArgs.User.Id) || From 0820c17307c28d60765f6b783722752f5d1e5391 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Sat, 23 Jul 2016 15:10:35 +0200 Subject: [PATCH 02/33] `.ct` command added to Utiliy module, prints current channe's topic --- NadekoBot/Modules/Utility/UtilityModule.cs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/NadekoBot/Modules/Utility/UtilityModule.cs b/NadekoBot/Modules/Utility/UtilityModule.cs index 380e5acf..999ec44a 100644 --- a/NadekoBot/Modules/Utility/UtilityModule.cs +++ b/NadekoBot/Modules/Utility/UtilityModule.cs @@ -144,6 +144,18 @@ namespace NadekoBot.Modules.Utility } await e.Channel.SendMessage("`List of roles:` \n• " + string.Join("\n• ", e.Server.Roles)).ConfigureAwait(false); }); + + + cgb.CreateCommand(Prefix + "channeltopic") + .Alias(Prefix + "ct") + .Description($"Sends current channel's topic as a message. | `{Prefix}ct`") + .Do(async e => + { + var topic = e.Channel.Topic; + if (string.IsNullOrWhiteSpace(topic)) + return; + await e.Channel.SendMessage(topic).ConfigureAwait(false); + }); }); } } From 566cef82ba8da87bd6e59a5377bfdcce31c3ff89 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Sat, 23 Jul 2016 15:18:05 +0200 Subject: [PATCH 03/33] Now shows the correct position number when queuing a song --- NadekoBot/Modules/Music/MusicModule.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NadekoBot/Modules/Music/MusicModule.cs b/NadekoBot/Modules/Music/MusicModule.cs index ba04d598..1780dcf0 100644 --- a/NadekoBot/Modules/Music/MusicModule.cs +++ b/NadekoBot/Modules/Music/MusicModule.cs @@ -856,7 +856,7 @@ namespace NadekoBot.Modules.Music } if (!silent) { - var queuedMessage = await textCh.SendMessage($"🎡`Queued`{resolvedSong.PrettyName} **at** `#{musicPlayer.Playlist.Count}`").ConfigureAwait(false); + var queuedMessage = await textCh.SendMessage($"🎡`Queued`{resolvedSong.PrettyName} **at** `#{musicPlayer.Playlist.Count + 1}`").ConfigureAwait(false); #pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed Task.Run(async () => { From 92123fc95a8507bef8c95bb33428540265d89e74 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Sat, 23 Jul 2016 22:33:34 +0200 Subject: [PATCH 04/33] Made permission saving async --- .../Modules/Music/Classes/MusicControls.cs | 2 +- .../Permissions/Classes/PermissionsHandler.cs | 88 +++++++++---------- .../Commands/FilterWordsCommand.cs | 10 +-- .../Modules/Permissions/PermissionsModule.cs | 64 +++++++------- 4 files changed, 82 insertions(+), 82 deletions(-) diff --git a/NadekoBot/Modules/Music/Classes/MusicControls.cs b/NadekoBot/Modules/Music/Classes/MusicControls.cs index bcd661f8..b024ca47 100644 --- a/NadekoBot/Modules/Music/Classes/MusicControls.cs +++ b/NadekoBot/Modules/Music/Classes/MusicControls.cs @@ -135,8 +135,8 @@ namespace NadekoBot.Modules.Music.Classes } finally { - await Task.Delay(300).ConfigureAwait(false); CurrentSong = null; + await Task.Delay(300).ConfigureAwait(false); } } } diff --git a/NadekoBot/Modules/Permissions/Classes/PermissionsHandler.cs b/NadekoBot/Modules/Permissions/Classes/PermissionsHandler.cs index c5d2e64c..d7c9e784 100644 --- a/NadekoBot/Modules/Permissions/Classes/PermissionsHandler.cs +++ b/NadekoBot/Modules/Permissions/Classes/PermissionsHandler.cs @@ -150,21 +150,21 @@ namespace NadekoBot.Modules.Permissions.Classes return PermissionBanType.None; } - private static void WriteServerToJson(ServerPermissions serverPerms) + private static Task WriteServerToJson(ServerPermissions serverPerms) => Task.Run(() => { string pathToFile = $"data/permissions/{serverPerms.Id}.json"; File.WriteAllText(pathToFile, Newtonsoft.Json.JsonConvert.SerializeObject(serverPerms, Newtonsoft.Json.Formatting.Indented)); - } + }); - public static void WriteToJson() + public static Task WriteToJson() => Task.Run(() => { Directory.CreateDirectory("data/permissions/"); foreach (var kvp in PermissionsDict) { WriteServerToJson(kvp.Value); } - } + }); public static string GetServerPermissionsRoleName(Server server) { @@ -174,25 +174,25 @@ namespace NadekoBot.Modules.Permissions.Classes return serverPerms.PermissionsControllerRole; } - internal static void SetPermissionsRole(Server server, string roleName) + internal static async Task SetPermissionsRole(Server server, string roleName) { var serverPerms = PermissionsDict.GetOrAdd(server.Id, new ServerPermissions(server.Id, server.Name)); serverPerms.PermissionsControllerRole = roleName; - Task.Run(() => WriteServerToJson(serverPerms)); + await WriteServerToJson(serverPerms).ConfigureAwait(false); } - internal static void SetVerbosity(Server server, bool val) + internal static async Task SetVerbosity(Server server, bool val) { var serverPerms = PermissionsDict.GetOrAdd(server.Id, new ServerPermissions(server.Id, server.Name)); serverPerms.Verbose = val; - Task.Run(() => WriteServerToJson(serverPerms)); + await WriteServerToJson(serverPerms).ConfigureAwait(false); } - internal static void CopyRolePermissions(Role fromRole, Role toRole) + internal static async Task CopyRolePermissions(Role fromRole, Role toRole) { var server = fromRole.Server; var serverPerms = PermissionsDict.GetOrAdd(server.Id, @@ -207,10 +207,10 @@ namespace NadekoBot.Modules.Permissions.Classes to.CopyFrom(from); - Task.Run(() => WriteServerToJson(serverPerms)); + await WriteServerToJson(serverPerms).ConfigureAwait(false); } - internal static void CopyChannelPermissions(Channel fromChannel, Channel toChannel) + internal static async Task CopyChannelPermissions(Channel fromChannel, Channel toChannel) { var server = fromChannel.Server; var serverPerms = PermissionsDict.GetOrAdd(server.Id, @@ -225,10 +225,10 @@ namespace NadekoBot.Modules.Permissions.Classes to.CopyFrom(from); - Task.Run(() => WriteServerToJson(serverPerms)); + await WriteServerToJson(serverPerms).ConfigureAwait(false); } - internal static void CopyUserPermissions(User fromUser, User toUser) + internal static async Task CopyUserPermissions(User fromUser, User toUser) { var server = fromUser.Server; var serverPerms = PermissionsDict.GetOrAdd(server.Id, @@ -243,10 +243,10 @@ namespace NadekoBot.Modules.Permissions.Classes to.CopyFrom(from); - Task.Run(() => WriteServerToJson(serverPerms)); + await WriteServerToJson(serverPerms).ConfigureAwait(false); } - public static void SetServerModulePermission(Server server, string moduleName, bool value) + public static async Task SetServerModulePermission(Server server, string moduleName, bool value) { var serverPerms = PermissionsDict.GetOrAdd(server.Id, new ServerPermissions(server.Id, server.Name)); @@ -256,10 +256,10 @@ namespace NadekoBot.Modules.Permissions.Classes modules[moduleName] = value; else modules.TryAdd(moduleName, value); - Task.Run(() => WriteServerToJson(serverPerms)); + await WriteServerToJson(serverPerms).ConfigureAwait(false); } - public static void SetServerCommandPermission(Server server, string commandName, bool value) + public static async Task SetServerCommandPermission(Server server, string commandName, bool value) { var serverPerms = PermissionsDict.GetOrAdd(server.Id, new ServerPermissions(server.Id, server.Name)); @@ -269,10 +269,10 @@ namespace NadekoBot.Modules.Permissions.Classes commands[commandName] = value; else commands.TryAdd(commandName, value); - Task.Run(() => WriteServerToJson(serverPerms)); + await WriteServerToJson(serverPerms).ConfigureAwait(false); } - public static void SetChannelModulePermission(Channel channel, string moduleName, bool value) + public static async Task SetChannelModulePermission(Channel channel, string moduleName, bool value) { var server = channel.Server; @@ -288,10 +288,10 @@ namespace NadekoBot.Modules.Permissions.Classes modules[moduleName] = value; else modules.TryAdd(moduleName, value); - Task.Run(() => WriteServerToJson(serverPerms)); + await WriteServerToJson(serverPerms).ConfigureAwait(false); } - public static void SetChannelCommandPermission(Channel channel, string commandName, bool value) + public static async Task SetChannelCommandPermission(Channel channel, string commandName, bool value) { var server = channel.Server; var serverPerms = PermissionsDict.GetOrAdd(server.Id, @@ -306,10 +306,10 @@ namespace NadekoBot.Modules.Permissions.Classes commands[commandName] = value; else commands.TryAdd(commandName, value); - Task.Run(() => WriteServerToJson(serverPerms)); + await WriteServerToJson(serverPerms).ConfigureAwait(false); } - public static void SetRoleModulePermission(Role role, string moduleName, bool value) + public static async Task SetRoleModulePermission(Role role, string moduleName, bool value) { var server = role.Server; var serverPerms = PermissionsDict.GetOrAdd(server.Id, @@ -324,10 +324,10 @@ namespace NadekoBot.Modules.Permissions.Classes modules[moduleName] = value; else modules.TryAdd(moduleName, value); - Task.Run(() => WriteServerToJson(serverPerms)); + await WriteServerToJson(serverPerms).ConfigureAwait(false); } - public static void SetRoleCommandPermission(Role role, string commandName, bool value) + public static async Task SetRoleCommandPermission(Role role, string commandName, bool value) { var server = role.Server; var serverPerms = PermissionsDict.GetOrAdd(server.Id, @@ -342,10 +342,10 @@ namespace NadekoBot.Modules.Permissions.Classes commands[commandName] = value; else commands.TryAdd(commandName, value); - Task.Run(() => WriteServerToJson(serverPerms)); + await WriteServerToJson(serverPerms).ConfigureAwait(false); } - public static void SetUserModulePermission(User user, string moduleName, bool value) + public static async Task SetUserModulePermission(User user, string moduleName, bool value) { var server = user.Server; var serverPerms = PermissionsDict.GetOrAdd(server.Id, @@ -360,10 +360,10 @@ namespace NadekoBot.Modules.Permissions.Classes modules[moduleName] = value; else modules.TryAdd(moduleName, value); - Task.Run(() => WriteServerToJson(serverPerms)); + await WriteServerToJson(serverPerms).ConfigureAwait(false); } - public static void SetUserCommandPermission(User user, string commandName, bool value) + public static async Task SetUserCommandPermission(User user, string commandName, bool value) { var server = user.Server; var serverPerms = PermissionsDict.GetOrAdd(server.Id, @@ -377,19 +377,19 @@ namespace NadekoBot.Modules.Permissions.Classes commands[commandName] = value; else commands.TryAdd(commandName, value); - Task.Run(() => WriteServerToJson(serverPerms)); + await WriteServerToJson(serverPerms).ConfigureAwait(false); } - public static void SetServerWordPermission(Server server, bool value) + public static async Task SetServerWordPermission(Server server, bool value) { var serverPerms = PermissionsDict.GetOrAdd(server.Id, new ServerPermissions(server.Id, server.Name)); serverPerms.Permissions.FilterWords = value; - Task.Run(() => WriteServerToJson(serverPerms)); + await WriteServerToJson(serverPerms).ConfigureAwait(false); } - public static void SetChannelWordPermission(Channel channel, bool value) + public static async Task SetChannelWordPermission(Channel channel, bool value) { var server = channel.Server; var serverPerms = PermissionsDict.GetOrAdd(server.Id, @@ -399,19 +399,19 @@ namespace NadekoBot.Modules.Permissions.Classes serverPerms.ChannelPermissions.Add(channel.Id, new Permissions(channel.Name)); serverPerms.ChannelPermissions[channel.Id].FilterWords = value; - Task.Run(() => WriteServerToJson(serverPerms)); + await WriteServerToJson(serverPerms).ConfigureAwait(false); } - public static void SetServerFilterInvitesPermission(Server server, bool value) + public static async Task SetServerFilterInvitesPermission(Server server, bool value) { var serverPerms = PermissionsDict.GetOrAdd(server.Id, new ServerPermissions(server.Id, server.Name)); serverPerms.Permissions.FilterInvites = value; - Task.Run(() => WriteServerToJson(serverPerms)); + await WriteServerToJson(serverPerms).ConfigureAwait(false); } - public static void SetChannelFilterInvitesPermission(Channel channel, bool value) + public static async Task SetChannelFilterInvitesPermission(Channel channel, bool value) { var server = channel.Server; var serverPerms = PermissionsDict.GetOrAdd(server.Id, @@ -421,10 +421,10 @@ namespace NadekoBot.Modules.Permissions.Classes serverPerms.ChannelPermissions.Add(channel.Id, new Permissions(channel.Name)); serverPerms.ChannelPermissions[channel.Id].FilterInvites = value; - Task.Run(() => WriteServerToJson(serverPerms)); + await WriteServerToJson(serverPerms).ConfigureAwait(false); } - public static void SetCommandCooldown(Server server, string commandName, int value) + public static async Task SetCommandCooldown(Server server, string commandName, int value) { var serverPerms = PermissionsDict.GetOrAdd(server.Id, new ServerPermissions(server.Id, server.Name)); @@ -436,26 +436,26 @@ namespace NadekoBot.Modules.Permissions.Classes serverPerms.CommandCooldowns.AddOrUpdate(commandName, value, (str, v) => value); } - Task.Run(() => WriteServerToJson(serverPerms)); + await WriteServerToJson(serverPerms).ConfigureAwait(false); } - public static void AddFilteredWord(Server server, string word) + public static async Task AddFilteredWord(Server server, string word) { var serverPerms = PermissionsDict.GetOrAdd(server.Id, new ServerPermissions(server.Id, server.Name)); if (serverPerms.Words.Contains(word)) throw new InvalidOperationException("That word is already banned."); serverPerms.Words.Add(word); - Task.Run(() => WriteServerToJson(serverPerms)); + await WriteServerToJson(serverPerms).ConfigureAwait(false); } - public static void RemoveFilteredWord(Server server, string word) + public static async Task RemoveFilteredWord(Server server, string word) { var serverPerms = PermissionsDict.GetOrAdd(server.Id, new ServerPermissions(server.Id, server.Name)); if (!serverPerms.Words.Contains(word)) throw new InvalidOperationException("That word is not banned."); serverPerms.Words.Remove(word); - Task.Run(() => WriteServerToJson(serverPerms)); + await WriteServerToJson(serverPerms).ConfigureAwait(false); } } /// diff --git a/NadekoBot/Modules/Permissions/Commands/FilterWordsCommand.cs b/NadekoBot/Modules/Permissions/Commands/FilterWordsCommand.cs index 3764245d..c187d398 100644 --- a/NadekoBot/Modules/Permissions/Commands/FilterWordsCommand.cs +++ b/NadekoBot/Modules/Permissions/Commands/FilterWordsCommand.cs @@ -68,7 +68,7 @@ namespace NadekoBot.Modules.Permissions.Commands var chan = string.IsNullOrWhiteSpace(chanStr) ? e.Channel : PermissionHelper.ValidateChannel(e.Server, chanStr); - PermissionsHandler.SetChannelWordPermission(chan, state); + await PermissionsHandler.SetChannelWordPermission(chan, state).ConfigureAwait(false); await e.Channel.SendMessage($"Word filtering has been **{(state ? "enabled" : "disabled")}** for **{chan.Name}** channel.").ConfigureAwait(false); return; } @@ -76,7 +76,7 @@ namespace NadekoBot.Modules.Permissions.Commands foreach (var curChannel in e.Server.TextChannels) { - PermissionsHandler.SetChannelWordPermission(curChannel, state); + await PermissionsHandler.SetChannelWordPermission(curChannel, state).ConfigureAwait(false); } await e.Channel.SendMessage($"Word filtering has been **{(state ? "enabled" : "disabled")}** for **ALL** channels.").ConfigureAwait(false); } @@ -98,7 +98,7 @@ namespace NadekoBot.Modules.Permissions.Commands var word = e.GetArg("word"); if (string.IsNullOrWhiteSpace(word)) return; - PermissionsHandler.AddFilteredWord(e.Server, word.ToLowerInvariant().Trim()); + await PermissionsHandler.AddFilteredWord(e.Server, word.ToLowerInvariant().Trim()).ConfigureAwait(false); await e.Channel.SendMessage($"Successfully added new filtered word.").ConfigureAwait(false); } @@ -120,7 +120,7 @@ namespace NadekoBot.Modules.Permissions.Commands var word = e.GetArg("word"); if (string.IsNullOrWhiteSpace(word)) return; - PermissionsHandler.RemoveFilteredWord(e.Server, word.ToLowerInvariant().Trim()); + await PermissionsHandler.RemoveFilteredWord(e.Server, word.ToLowerInvariant().Trim()).ConfigureAwait(false); await e.Channel.SendMessage($"Successfully removed filtered word.").ConfigureAwait(false); } @@ -159,7 +159,7 @@ namespace NadekoBot.Modules.Permissions.Commands try { var state = PermissionHelper.ValidateBool(e.GetArg("bool")); - PermissionsHandler.SetServerWordPermission(e.Server, state); + await PermissionsHandler.SetServerWordPermission(e.Server, state).ConfigureAwait(false); await e.Channel.SendMessage($"Word filtering has been **{(state ? "enabled" : "disabled")}** on this server.") .ConfigureAwait(false); diff --git a/NadekoBot/Modules/Permissions/PermissionsModule.cs b/NadekoBot/Modules/Permissions/PermissionsModule.cs index 8ca83e48..463520a5 100644 --- a/NadekoBot/Modules/Permissions/PermissionsModule.cs +++ b/NadekoBot/Modules/Permissions/PermissionsModule.cs @@ -55,7 +55,7 @@ namespace NadekoBot.Modules.Permissions await e.Channel.SendMessage($"Role `{arg}` probably doesn't exist. Create the role with that name first.").ConfigureAwait(false); return; } - PermissionsHandler.SetPermissionsRole(e.Server, role.Name); + await PermissionsHandler.SetPermissionsRole(e.Server, role.Name).ConfigureAwait(false); await e.Channel.SendMessage($"Role `{role.Name}` is now required in order to change permissions.").ConfigureAwait(false); }); @@ -71,7 +71,7 @@ namespace NadekoBot.Modules.Permissions var args = arg.Split('~').Select(a => a.Trim()).ToArray(); if (args.Length > 2) { - await e.Channel.SendMessage("πŸ’’Invalid number of '~'s in the argument."); + await e.Channel.SendMessage("πŸ’’Invalid number of '~'s in the argument.").ConfigureAwait(false); return; } try @@ -79,12 +79,12 @@ namespace NadekoBot.Modules.Permissions var fromRole = PermissionHelper.ValidateRole(e.Server, args[0]); var toRole = PermissionHelper.ValidateRole(e.Server, args[1]); - PermissionsHandler.CopyRolePermissions(fromRole, toRole); - await e.Channel.SendMessage($"Copied permission settings from **{fromRole.Name}** to **{toRole.Name}**."); + await PermissionsHandler.CopyRolePermissions(fromRole, toRole).ConfigureAwait(false); + await e.Channel.SendMessage($"Copied permission settings from **{fromRole.Name}** to **{toRole.Name}**.").ConfigureAwait(false); } catch (Exception ex) { - await e.Channel.SendMessage($"πŸ’’{ex.Message}"); + await e.Channel.SendMessage($"πŸ’’{ex.Message}").ConfigureAwait(false); } }); cgb.CreateCommand(Prefix + "chnlpermscopy") @@ -107,8 +107,8 @@ namespace NadekoBot.Modules.Permissions var fromChannel = PermissionHelper.ValidateChannel(e.Server, args[0]); var toChannel = PermissionHelper.ValidateChannel(e.Server, args[1]); - PermissionsHandler.CopyChannelPermissions(fromChannel, toChannel); - await e.Channel.SendMessage($"Copied permission settings from **{fromChannel.Name}** to **{toChannel.Name}**."); + await PermissionsHandler.CopyChannelPermissions(fromChannel, toChannel).ConfigureAwait(false); + await e.Channel.SendMessage($"Copied permission settings from **{fromChannel.Name}** to **{toChannel.Name}**.").ConfigureAwait(false); } catch (Exception ex) { @@ -127,7 +127,7 @@ namespace NadekoBot.Modules.Permissions var args = arg.Split('~').Select(a => a.Trim()).ToArray(); if (args.Length > 2) { - await e.Channel.SendMessage("πŸ’’Invalid number of '~'s in the argument."); + await e.Channel.SendMessage("πŸ’’Invalid number of '~'s in the argument.").ConfigureAwait(false); return; } try @@ -135,8 +135,8 @@ namespace NadekoBot.Modules.Permissions var fromUser = PermissionHelper.ValidateUser(e.Server, args[0]); var toUser = PermissionHelper.ValidateUser(e.Server, args[1]); - PermissionsHandler.CopyUserPermissions(fromUser, toUser); - await e.Channel.SendMessage($"Copied permission settings from **{fromUser.ToString()}**to * *{toUser.ToString()}**."); + await PermissionsHandler.CopyUserPermissions(fromUser, toUser).ConfigureAwait(false); + await e.Channel.SendMessage($"Copied permission settings from **{fromUser.ToString()}**to * *{toUser.ToString()}**.").ConfigureAwait(false); } catch (Exception ex) { @@ -152,7 +152,7 @@ namespace NadekoBot.Modules.Permissions { var arg = e.GetArg("arg"); var val = PermissionHelper.ValidateBool(arg); - PermissionsHandler.SetVerbosity(e.Server, val); + await PermissionsHandler.SetVerbosity(e.Server, val).ConfigureAwait(false); await e.Channel.SendMessage($"Verbosity set to {val}.").ConfigureAwait(false); }); @@ -254,7 +254,7 @@ namespace NadekoBot.Modules.Permissions var module = PermissionHelper.ValidateModule(e.GetArg("module")); var state = PermissionHelper.ValidateBool(e.GetArg("bool")); - PermissionsHandler.SetServerModulePermission(e.Server, module, state); + await PermissionsHandler.SetServerModulePermission(e.Server, module, state).ConfigureAwait(false); await e.Channel.SendMessage($"Module **{module}** has been **{(state ? "enabled" : "disabled")}** on this server.").ConfigureAwait(false); } catch (ArgumentException exArg) @@ -278,7 +278,7 @@ namespace NadekoBot.Modules.Permissions var command = PermissionHelper.ValidateCommand(e.GetArg("command")); var state = PermissionHelper.ValidateBool(e.GetArg("bool")); - PermissionsHandler.SetServerCommandPermission(e.Server, command, state); + await PermissionsHandler.SetServerCommandPermission(e.Server, command, state).ConfigureAwait(false); await e.Channel.SendMessage($"Command **{command}** has been **{(state ? "enabled" : "disabled")}** on this server.").ConfigureAwait(false); } catch (ArgumentException exArg) @@ -307,7 +307,7 @@ namespace NadekoBot.Modules.Permissions { foreach (var role in e.Server.Roles) { - PermissionsHandler.SetRoleModulePermission(role, module, state); + await PermissionsHandler.SetRoleModulePermission(role, module, state).ConfigureAwait(false); } await e.Channel.SendMessage($"Module **{module}** has been **{(state ? "enabled" : "disabled")}** for **ALL** roles.").ConfigureAwait(false); } @@ -315,7 +315,7 @@ namespace NadekoBot.Modules.Permissions { var role = PermissionHelper.ValidateRole(e.Server, e.GetArg("role")); - PermissionsHandler.SetRoleModulePermission(role, module, state); + await PermissionsHandler.SetRoleModulePermission(role, module, state).ConfigureAwait(false); await e.Channel.SendMessage($"Module **{module}** has been **{(state ? "enabled" : "disabled")}** for **{role.Name}** role.").ConfigureAwait(false); } } @@ -345,7 +345,7 @@ namespace NadekoBot.Modules.Permissions { foreach (var role in e.Server.Roles) { - PermissionsHandler.SetRoleCommandPermission(role, command, state); + await PermissionsHandler.SetRoleCommandPermission(role, command, state).ConfigureAwait(false); } await e.Channel.SendMessage($"Command **{command}** has been **{(state ? "enabled" : "disabled")}** for **ALL** roles.").ConfigureAwait(false); } @@ -353,7 +353,7 @@ namespace NadekoBot.Modules.Permissions { var role = PermissionHelper.ValidateRole(e.Server, e.GetArg("role")); - PermissionsHandler.SetRoleCommandPermission(role, command, state); + await PermissionsHandler.SetRoleCommandPermission(role, command, state).ConfigureAwait(false); await e.Channel.SendMessage($"Command **{command}** has been **{(state ? "enabled" : "disabled")}** for **{role.Name}** role.").ConfigureAwait(false); } } @@ -383,20 +383,20 @@ namespace NadekoBot.Modules.Permissions { foreach (var channel in e.Server.TextChannels) { - PermissionsHandler.SetChannelModulePermission(channel, module, state); + await PermissionsHandler.SetChannelModulePermission(channel, module, state).ConfigureAwait(false); } await e.Channel.SendMessage($"Module **{module}** has been **{(state ? "enabled" : "disabled")}** on **ALL** channels.").ConfigureAwait(false); } else if (string.IsNullOrWhiteSpace(channelArg)) { - PermissionsHandler.SetChannelModulePermission(e.Channel, module, state); + await PermissionsHandler.SetChannelModulePermission(e.Channel, module, state).ConfigureAwait(false); await e.Channel.SendMessage($"Module **{module}** has been **{(state ? "enabled" : "disabled")}** for **{e.Channel.Name}** channel.").ConfigureAwait(false); } else { var channel = PermissionHelper.ValidateChannel(e.Server, channelArg); - PermissionsHandler.SetChannelModulePermission(channel, module, state); + await PermissionsHandler.SetChannelModulePermission(channel, module, state).ConfigureAwait(false); await e.Channel.SendMessage($"Module **{module}** has been **{(state ? "enabled" : "disabled")}** for **{channel.Name}** channel.").ConfigureAwait(false); } } @@ -426,7 +426,7 @@ namespace NadekoBot.Modules.Permissions { foreach (var channel in e.Server.TextChannels) { - PermissionsHandler.SetChannelCommandPermission(channel, command, state); + await PermissionsHandler.SetChannelCommandPermission(channel, command, state).ConfigureAwait(false); } await e.Channel.SendMessage($"Command **{command}** has been **{(state ? "enabled" : "disabled")}** on **ALL** channels.").ConfigureAwait(false); } @@ -434,7 +434,7 @@ namespace NadekoBot.Modules.Permissions { var channel = PermissionHelper.ValidateChannel(e.Server, e.GetArg("channel")); - PermissionsHandler.SetChannelCommandPermission(channel, command, state); + await PermissionsHandler.SetChannelCommandPermission(channel, command, state).ConfigureAwait(false); await e.Channel.SendMessage($"Command **{command}** has been **{(state ? "enabled" : "disabled")}** for **{channel.Name}** channel.").ConfigureAwait(false); } } @@ -461,7 +461,7 @@ namespace NadekoBot.Modules.Permissions var state = PermissionHelper.ValidateBool(e.GetArg("bool")); var user = PermissionHelper.ValidateUser(e.Server, e.GetArg("user")); - PermissionsHandler.SetUserModulePermission(user, module, state); + await PermissionsHandler.SetUserModulePermission(user, module, state).ConfigureAwait(false); await e.Channel.SendMessage($"Module **{module}** has been **{(state ? "enabled" : "disabled")}** for user **{user.Name}**.").ConfigureAwait(false); } catch (ArgumentException exArg) @@ -487,7 +487,7 @@ namespace NadekoBot.Modules.Permissions var state = PermissionHelper.ValidateBool(e.GetArg("bool")); var user = PermissionHelper.ValidateUser(e.Server, e.GetArg("user")); - PermissionsHandler.SetUserCommandPermission(user, command, state); + await PermissionsHandler.SetUserCommandPermission(user, command, state).ConfigureAwait(false); await e.Channel.SendMessage($"Command **{command}** has been **{(state ? "enabled" : "disabled")}** for user **{user.Name}**.").ConfigureAwait(false); } catch (ArgumentException exArg) @@ -511,7 +511,7 @@ namespace NadekoBot.Modules.Permissions foreach (var module in NadekoBot.Client.GetService().Modules) { - PermissionsHandler.SetServerModulePermission(e.Server, module.Name, state); + await PermissionsHandler.SetServerModulePermission(e.Server, module.Name, state).ConfigureAwait(false); } await e.Channel.SendMessage($"All modules have been **{(state ? "enabled" : "disabled")}** on this server.").ConfigureAwait(false); } @@ -538,7 +538,7 @@ namespace NadekoBot.Modules.Permissions foreach (var command in NadekoBot.Client.GetService().AllCommands.Where(c => c.Category == module)) { - PermissionsHandler.SetServerCommandPermission(e.Server, command.Text, state); + await PermissionsHandler.SetServerCommandPermission(e.Server, command.Text, state).ConfigureAwait(false); } await e.Channel.SendMessage($"All commands from the **{module}** module have been **{(state ? "enabled" : "disabled")}** on this server.").ConfigureAwait(false); } @@ -565,7 +565,7 @@ namespace NadekoBot.Modules.Permissions var channel = string.IsNullOrWhiteSpace(chArg) ? e.Channel : PermissionHelper.ValidateChannel(e.Server, chArg); foreach (var module in NadekoBot.Client.GetService().Modules) { - PermissionsHandler.SetChannelModulePermission(channel, module.Name, state); + await PermissionsHandler.SetChannelModulePermission(channel, module.Name, state).ConfigureAwait(false); } await e.Channel.SendMessage($"All modules have been **{(state ? "enabled" : "disabled")}** for **{channel.Name}** channel.").ConfigureAwait(false); @@ -594,7 +594,7 @@ namespace NadekoBot.Modules.Permissions var channel = PermissionHelper.ValidateChannel(e.Server, e.GetArg("channel")); foreach (var command in NadekoBot.Client.GetService().AllCommands.Where(c => c.Category == module)) { - PermissionsHandler.SetChannelCommandPermission(channel, command.Text, state); + await PermissionsHandler.SetChannelCommandPermission(channel, command.Text, state).ConfigureAwait(false); } await e.Channel.SendMessage($"All commands from the **{module}** module have been **{(state ? "enabled" : "disabled")}** for **{channel.Name}** channel.").ConfigureAwait(false); } @@ -620,7 +620,7 @@ namespace NadekoBot.Modules.Permissions var role = PermissionHelper.ValidateRole(e.Server, e.GetArg("role")); foreach (var module in NadekoBot.Client.GetService().Modules) { - PermissionsHandler.SetRoleModulePermission(role, module.Name, state); + await PermissionsHandler.SetRoleModulePermission(role, module.Name, state).ConfigureAwait(false); } await e.Channel.SendMessage($"All modules have been **{(state ? "enabled" : "disabled")}** for **{role.Name}** role.").ConfigureAwait(false); @@ -652,7 +652,7 @@ namespace NadekoBot.Modules.Permissions { foreach (var command in NadekoBot.Client.GetService().AllCommands.Where(c => c.Category == module)) { - PermissionsHandler.SetRoleCommandPermission(role, command.Text, state); + await PermissionsHandler.SetRoleCommandPermission(role, command.Text, state).ConfigureAwait(false); } } await e.Channel.SendMessage($"All commands from the **{module}** module have been **{(state ? "enabled" : "disabled")}** for **all roles** role.").ConfigureAwait(false); @@ -663,7 +663,7 @@ namespace NadekoBot.Modules.Permissions foreach (var command in NadekoBot.Client.GetService().AllCommands.Where(c => c.Category == module)) { - PermissionsHandler.SetRoleCommandPermission(role, command.Text, state); + await PermissionsHandler.SetRoleCommandPermission(role, command.Text, state).ConfigureAwait(false); } await e.Channel.SendMessage($"All commands from the **{module}** module have been **{(state ? "enabled" : "disabled")}** for **{role.Name}** role.").ConfigureAwait(false); } @@ -795,7 +795,7 @@ namespace NadekoBot.Modules.Permissions throw new ArgumentOutOfRangeException("secs", "Invalid second parameter. (Must be a number between 0 and 3600)"); - PermissionsHandler.SetCommandCooldown(e.Server, command, secs); + await PermissionsHandler.SetCommandCooldown(e.Server, command, secs).ConfigureAwait(false); if(secs == 0) await e.Channel.SendMessage($"Command **{command}** has no coooldown now.").ConfigureAwait(false); else From cb5079a3843d20711358e4592a0fc3e5788ed4ea Mon Sep 17 00:00:00 2001 From: Kwoth Date: Sun, 24 Jul 2016 01:26:43 +0200 Subject: [PATCH 05/33] mostly rewritten music, doesn't work on large number of concurrent streams properly >.> --- NadekoBot/Modules/Music/Classes/Song.cs | 136 +++++++++++++----------- NadekoBot/Modules/Music/MusicModule.cs | 7 +- 2 files changed, 78 insertions(+), 65 deletions(-) diff --git a/NadekoBot/Modules/Music/Classes/Song.cs b/NadekoBot/Modules/Music/Classes/Song.cs index 9f2fb382..91c866e8 100644 --- a/NadekoBot/Modules/Music/Classes/Song.cs +++ b/NadekoBot/Modules/Music/Classes/Song.cs @@ -3,6 +3,7 @@ using NadekoBot.Classes; using NadekoBot.Extensions; using System; using System.Diagnostics; +using System.Diagnostics.Contracts; using System.IO; using System.Linq; using System.Text.RegularExpressions; @@ -74,7 +75,7 @@ namespace NadekoBot.Modules.Music.Classes return this; } - private Task BufferSong(CancellationToken cancelToken) => + private Task BufferSong(string filename, CancellationToken cancelToken) => Task.Factory.StartNew(async () => { Process p = null; @@ -89,32 +90,9 @@ namespace NadekoBot.Modules.Music.Classes RedirectStandardError = false, CreateNoWindow = true, }); - const int blockSize = 3840; - var buffer = new byte[blockSize]; - var attempt = 0; - while (!cancelToken.IsCancellationRequested) - { - var read = 0; - try - { - read = await p.StandardOutput.BaseStream.ReadAsync(buffer, 0, blockSize, cancelToken) - .ConfigureAwait(false); - } - catch - { - return; - } - if (read == 0) - if (attempt++ == 50) - break; - else - await Task.Delay(100, cancelToken).ConfigureAwait(false); - else - attempt = 0; - await songBuffer.WriteAsync(buffer, read, cancelToken).ConfigureAwait(false); - if (songBuffer.ContentLength > 2.MB()) - prebufferingComplete = true; - } + using (var outStream = new FileStream(filename, FileMode.Append, FileAccess.Write, FileShare.Read)) + await p.StandardOutput.BaseStream.CopyToAsync(outStream, 81920, cancelToken); + prebufferingComplete = true; } catch (Exception ex) { @@ -137,53 +115,56 @@ namespace NadekoBot.Modules.Music.Classes internal async Task Play(IAudioClient voiceClient, CancellationToken cancelToken) { - // initialize the buffer here because if this song was playing before (requeued), we must delete old buffer data - songBuffer = new PoopyBuffer(NadekoBot.Config.BufferSize); + var filename = Path.Combine(MusicModule.MusicDataPath, DateTime.Now.UnixTimestamp().ToString()); - var bufferTask = BufferSong(cancelToken).ConfigureAwait(false); - var bufferAttempts = 0; - const int waitPerAttempt = 500; - var toAttemptTimes = SongInfo.ProviderType != MusicType.Normal ? 5 : 9; - while (!prebufferingComplete && bufferAttempts++ < toAttemptTimes) + var bufferTask = BufferSong(filename, cancelToken).ConfigureAwait(false); + + var inStream = new FileStream(filename, FileMode.OpenOrCreate, FileAccess.Read, FileShare.Write); + try { - await Task.Delay(waitPerAttempt, cancelToken).ConfigureAwait(false); - } - Console.WriteLine($"Prebuffering done? in {waitPerAttempt * bufferAttempts}"); - const int blockSize = 3840; - var attempt = 0; - while (!cancelToken.IsCancellationRequested) - { - //Console.WriteLine($"Read: {songBuffer.ReadPosition}\nWrite: {songBuffer.WritePosition}\nContentLength:{songBuffer.ContentLength}\n---------"); - byte[] buffer = new byte[blockSize]; - var read = await songBuffer.ReadAsync(buffer, blockSize).ConfigureAwait(false); - unchecked + await Task.Delay(1000); + + const int blockSize = 3840; + var attempt = 0; + while (!cancelToken.IsCancellationRequested) { - bytesSent += (ulong)read; - } - if (read == 0) - if (attempt++ == 20) + //Console.WriteLine($"Read: {songBuffer.ReadPosition}\nWrite: {songBuffer.WritePosition}\nContentLength:{songBuffer.ContentLength}\n---------"); + byte[] buffer = new byte[blockSize]; + var read = inStream.Read(buffer, 0, buffer.Length); + //await inStream.CopyToAsync(voiceClient.OutputStream); + unchecked { - voiceClient.Wait(); - Console.WriteLine($"Song finished. [{songBuffer.ContentLength}]"); - break; + bytesSent += (ulong)read; } + if (read == 0) + if (attempt++ == 20) + { + Console.WriteLine("blocking"); + voiceClient.Wait(); + Console.WriteLine("unblocking"); + break; + } + else + await Task.Delay(100, cancelToken).ConfigureAwait(false); else - await Task.Delay(100, cancelToken).ConfigureAwait(false); - else - attempt = 0; + attempt = 0; - while (this.MusicPlayer.Paused) - await Task.Delay(200, cancelToken).ConfigureAwait(false); - buffer = AdjustVolume(buffer, MusicPlayer.Volume); - voiceClient.Send(buffer, 0, read); + while (this.MusicPlayer.Paused) + await Task.Delay(200, cancelToken).ConfigureAwait(false); + buffer = AdjustVolume(buffer, MusicPlayer.Volume); + voiceClient.Send(buffer, 0, read); + } + await bufferTask; + voiceClient.Clear(); + cancelToken.ThrowIfCancellationRequested(); + } + finally { + inStream.Dispose(); + try { File.Delete(filename); } catch { } } - Console.WriteLine("Awiting buffer task"); - await bufferTask; - Console.WriteLine("Buffer task done."); - voiceClient.Clear(); - cancelToken.ThrowIfCancellationRequested(); } + /* //stackoverflow ftw private static byte[] AdjustVolume(byte[] audioSamples, float volume) { @@ -210,6 +191,33 @@ namespace NadekoBot.Modules.Music.Classes } return array; } + */ + + //aidiakapi ftw + public unsafe static byte[] AdjustVolume(byte[] audioSamples, float volume) + { + Contract.Requires(audioSamples != null); + Contract.Requires(audioSamples.Length % 2 == 0); + Contract.Requires(volume >= 0f && volume <= 1f); + Contract.Assert(BitConverter.IsLittleEndian); + + if (Math.Abs(volume - 1f) < 0.0001f) return audioSamples; + + // 16-bit precision for the multiplication + int volumeFixed = (int)Math.Round(volume * 65536d); + + int count = audioSamples.Length / 2; + + fixed (byte* srcBytes = audioSamples) + { + short* src = (short*)srcBytes; + + for (int i = count; i != 0; i--, src++) + *src = (short)(((*src) * volumeFixed) >> 16); + } + + return audioSamples; + } public static async Task ResolveSong(string query, MusicType musicType = MusicType.Normal) { diff --git a/NadekoBot/Modules/Music/MusicModule.cs b/NadekoBot/Modules/Music/MusicModule.cs index 1780dcf0..e9620d2c 100644 --- a/NadekoBot/Modules/Music/MusicModule.cs +++ b/NadekoBot/Modules/Music/MusicModule.cs @@ -18,11 +18,16 @@ namespace NadekoBot.Modules.Music { internal class MusicModule : DiscordModule { - public static ConcurrentDictionary MusicPlayers = new ConcurrentDictionary(); + public const string MusicDataPath = "data/musicdata"; + public MusicModule() { + //it can fail if its currenctly opened or doesn't exist. Either way i don't care + try { Directory.Delete(MusicDataPath, true); } catch { } + + Directory.CreateDirectory(MusicDataPath); } public override string Prefix { get; } = NadekoBot.Config.CommandPrefixes.Music; From 7b95ff5d08c24b8c6f4a894ecbf55e40f6e1e5ae Mon Sep 17 00:00:00 2001 From: Kwoth Date: Sun, 24 Jul 2016 01:28:03 +0200 Subject: [PATCH 06/33] forgot some awaits --- .../Modules/Permissions/Commands/FilterInvitesCommand.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/NadekoBot/Modules/Permissions/Commands/FilterInvitesCommand.cs b/NadekoBot/Modules/Permissions/Commands/FilterInvitesCommand.cs index c8e63f23..f65902c6 100644 --- a/NadekoBot/Modules/Permissions/Commands/FilterInvitesCommand.cs +++ b/NadekoBot/Modules/Permissions/Commands/FilterInvitesCommand.cs @@ -71,7 +71,7 @@ namespace NadekoBot.Modules.Permissions.Commands var chan = string.IsNullOrWhiteSpace(chanStr) ? e.Channel : PermissionHelper.ValidateChannel(e.Server, chanStr); - PermissionsHandler.SetChannelFilterInvitesPermission(chan, state); + await PermissionsHandler.SetChannelFilterInvitesPermission(chan, state).ConfigureAwait(false); await e.Channel.SendMessage($"Invite Filter has been **{(state ? "enabled" : "disabled")}** for **{chan.Name}** channel.") .ConfigureAwait(false); return; @@ -80,7 +80,7 @@ namespace NadekoBot.Modules.Permissions.Commands foreach (var curChannel in e.Server.TextChannels) { - PermissionsHandler.SetChannelFilterInvitesPermission(curChannel, state); + await PermissionsHandler.SetChannelFilterInvitesPermission(curChannel, state).ConfigureAwait(false); } await e.Channel.SendMessage($"Invite Filter has been **{(state ? "enabled" : "disabled")}** for **ALL** channels.") .ConfigureAwait(false); @@ -102,7 +102,7 @@ namespace NadekoBot.Modules.Permissions.Commands try { var state = PermissionHelper.ValidateBool(e.GetArg("bool")); - PermissionsHandler.SetServerFilterInvitesPermission(e.Server, state); + await PermissionsHandler.SetServerFilterInvitesPermission(e.Server, state).ConfigureAwait(false); await e.Channel.SendMessage($"Invite Filter has been **{(state ? "enabled" : "disabled")}** for this server.") .ConfigureAwait(false); From 21b1778a1fc429f223a45eed77048d80fa7d451e Mon Sep 17 00:00:00 2001 From: Kwoth Date: Sun, 24 Jul 2016 01:28:23 +0200 Subject: [PATCH 07/33] unix timestamp extension --- NadekoBot/Classes/Extensions.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/NadekoBot/Classes/Extensions.cs b/NadekoBot/Classes/Extensions.cs index a4071ded..c66b1b30 100644 --- a/NadekoBot/Classes/Extensions.cs +++ b/NadekoBot/Classes/Extensions.cs @@ -362,5 +362,7 @@ namespace NadekoBot.Extensions return sw.BaseStream; } + public static double UnixTimestamp(this DateTime dt) => dt.ToUniversalTime().Subtract(new DateTime(1970, 1, 1)).TotalSeconds; + } } From a01ed97c6de21e6a17285dfbf1e56428ae1494bf Mon Sep 17 00:00:00 2001 From: Kwoth Date: Sun, 24 Jul 2016 01:28:43 +0200 Subject: [PATCH 08/33] allow unsafe --- NadekoBot/NadekoBot.csproj | 2 ++ 1 file changed, 2 insertions(+) diff --git a/NadekoBot/NadekoBot.csproj b/NadekoBot/NadekoBot.csproj index 4e16fee4..442296dc 100644 --- a/NadekoBot/NadekoBot.csproj +++ b/NadekoBot/NadekoBot.csproj @@ -46,6 +46,7 @@ true + true AnyCPU @@ -116,6 +117,7 @@ prompt MinimumRecommendedRules.ruleset true + true From 305a9a4a980ae4436b2249dd2a6edde44d411e61 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Sun, 24 Jul 2016 13:00:34 +0200 Subject: [PATCH 09/33] better error when ffmpeg is not found --- NadekoBot/Modules/Music/Classes/Song.cs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/NadekoBot/Modules/Music/Classes/Song.cs b/NadekoBot/Modules/Music/Classes/Song.cs index 91c866e8..fc831b52 100644 --- a/NadekoBot/Modules/Music/Classes/Song.cs +++ b/NadekoBot/Modules/Music/Classes/Song.cs @@ -94,6 +94,16 @@ namespace NadekoBot.Modules.Music.Classes await p.StandardOutput.BaseStream.CopyToAsync(outStream, 81920, cancelToken); prebufferingComplete = true; } + catch (System.ComponentModel.Win32Exception) { + var oldclr = Console.ForegroundColor; + Console.ForegroundColor = ConsoleColor.Red; + Console.WriteLine(@"You have not properly installed or configured FFMPEG. +Please install and configure FFMPEG to play music. +Check the guides for your platform on how to setup ffmpeg correctly: + Windows Guide: https://goo.gl/SCv72y + Linux Guide: https://goo.gl/rRhjCp"); + Console.ForegroundColor = oldclr; + } catch (Exception ex) { Console.WriteLine($"Buffering errored: {ex.Message}"); From 19d5a716b6a256f1e68f17c1f7f2c3f77c2acb65 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Sun, 24 Jul 2016 13:02:13 +0200 Subject: [PATCH 10/33] Added ffmpeg installers as a submodule --- .gitmodules | 3 +++ ffmpeg-installer | 1 + 2 files changed, 4 insertions(+) create mode 160000 ffmpeg-installer diff --git a/.gitmodules b/.gitmodules index 1fa69c2d..3f0608a0 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,6 @@ [submodule "discord.net"] path = discord.net url = git://github.com/kwoth/discord.net.git +[submodule "ffmpeg-installer"] + path = ffmpeg-installer + url = https://github.com/kwoth/ffmpeg-installer diff --git a/ffmpeg-installer b/ffmpeg-installer new file mode 160000 index 00000000..d593fe3a --- /dev/null +++ b/ffmpeg-installer @@ -0,0 +1 @@ +Subproject commit d593fe3a86be7da9e4177865446f2f5ca58b6be4 From 32f37d473989faab15bed68d0ab2a13c5f5b6523 Mon Sep 17 00:00:00 2001 From: Master Kwoth Date: Sun, 24 Jul 2016 13:07:52 +0200 Subject: [PATCH 11/33] Update ComprehensiveGuide.md --- ComprehensiveGuide.md | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/ComprehensiveGuide.md b/ComprehensiveGuide.md index c5b5bc6b..1b2c3ea4 100644 --- a/ComprehensiveGuide.md +++ b/ComprehensiveGuide.md @@ -35,11 +35,9 @@ ________________________________________________________________________________ #### Setting Up NadekoBot For Music ###### Setting up `ffmpeg` with installer: 1) Google Account -2) Soundcloud Account (if you want soundcloud support) - -3) Download installer here: https://mega.nz/#!O1F3HZDa!EfHsy_89vy0zyQV_L28S3Gizsd0KD5EN1OyWpd55ep0 - -4) Run the installer +2) Soundcloud Account (if you want soundcloud support) +3) Download installer here: https://goo.gl/lQZnsH (pick the one for your system, either 32 or 64bit and then click 'download') +4) Run the installer 5) Follow these steps on how to setup API keys: - Go to https://console.developers.google.com and log in. From b9d81fb88aa663ab7c47ba06e26fb348da280339 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Sun, 24 Jul 2016 13:34:51 +0200 Subject: [PATCH 12/33] fixed trivia bug --- NadekoBot/Modules/Games/Commands/Trivia/TriviaGame.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NadekoBot/Modules/Games/Commands/Trivia/TriviaGame.cs b/NadekoBot/Modules/Games/Commands/Trivia/TriviaGame.cs index fc5dd361..3f38529f 100644 --- a/NadekoBot/Modules/Games/Commands/Trivia/TriviaGame.cs +++ b/NadekoBot/Modules/Games/Commands/Trivia/TriviaGame.cs @@ -123,7 +123,7 @@ namespace NadekoBot.Modules.Games.Commands.Trivia guess = true; } } - catch { _guessLock.Release(); } + finally { _guessLock.Release(); } if (!guess) return; triviaCancelSource.Cancel(); await channel.SendMessage($"β˜‘οΈ {e.User.Mention} guessed it! The answer was: **{CurrentQuestion.Answer}**").ConfigureAwait(false); From ee61362f856f237f016ea26d88d70370fc918735 Mon Sep 17 00:00:00 2001 From: miraai Date: Sun, 24 Jul 2016 13:59:30 +0200 Subject: [PATCH 13/33] Update LinuxSetup.md --- LinuxSetup.md | 254 ++++++++++++++++++++++---------------------------- 1 file changed, 109 insertions(+), 145 deletions(-) diff --git a/LinuxSetup.md b/LinuxSetup.md index 224f0463..1e100793 100644 --- a/LinuxSetup.md +++ b/LinuxSetup.md @@ -1,104 +1,91 @@ -#SETTING UP NADEKO ON LINUX UBUNTU 14+ +#Setting up NadekoBot on Linux -######If you want Nadeko to play music for you 24/7 without having to hosting it on your PC and want to keep it cheap, reliable and convenient as possible, you can try Nadeko on Linux Digital Ocean Droplet using the link http://m.do.co/c/46b4d3d44795/ (and using this link will be supporting Nadeko and will give you **$10 credit**) +####Setting up NadekoBot on Linux Digital Ocean Droplet +######If you want Nadeko to play music for you 24/7 without having to hosting it on your PC and want to keep it cheap, reliable and convenient as possible, you can try Nadeko on Linux Digital Ocean Droplet using the link [DigitalOcean][DigitalOcean] (and using this link will be supporting Nadeko and will give you **$10 credit**) -######Keep this helpful video handy https://www.youtube.com/watch?v=icV4_WPqPQk&feature=youtu.be (thanks to klincheR) it contains how to set up the Digital Ocean droplet aswell. +######Keep this helpful video handy [Linux Setup Video][Linux Setup Video] (thanks to klincheR) it contains how to set up the Digital Ocean droplet aswell. +####Setting up NadekoBot local +Assuming you have followed the link above to created an account in Digital Ocean and video to set up the bot until you get the `IP address and root password (in email)` to login, its time to begin. -Assuming you have followed the link above to created an account in Digital Ocean and video to set up the bot until you get the `IP address and root password (in email)` to login, its time to begin: +#### Prerequisites +- Download [PuTTY][PuTTY] +- Download [CyberDuck][CyberDuck] -**DOWNLOAD PuTTY** - -http://www.chiark.greenend.org.uk/~sgtatham/putty/download.html - -**DOWNLOAD and INSTALL CyberDuck** `(for accessing filesystem using SFTP)` - -https://cyberduck.io - - - -**Follow the steps below:** - -**Open PuTTY.exe** that you downloaded before, and paste or enter your `IP address` and then click **Open** +#### Follow these steps +- **Open PuTTY.exe** that you downloaded before, and paste or enter your `IP address` and then click **Open**. If you entered your Droplets IP address correctly, it should show **login as:** in a newly opened window. - -Now for **login as:**, type `root` and hit enter. - -It should then, ask for password, type the `root password` you have received in your **email address registered with Digital Ocean**, then hit Enter +- Now for **login as:**, type `root` and hit enter. +- It should then, ask for password, type the `root password` you have received in your **email address registered with Digital Ocean**, then hit Enter. *(as you are running it for the first time, it will most likely to ask you to change your root password, for that, type the "password you received through email", hit Enter, enter a "new password", hit Enter and confirm that "new password" again.* -**SAVE that new password somewhere safe not just in mind** - -After you done that, you are ready to write commands. +**SAVE that new password somewhere safe not just in mind**. After you done that, you are ready to write commands. **Copy and just paste** using **mouse right-click** (it should paste automatically) -######MONO (Source: http://www.mono-project.com/docs/getting-started/install/linux/) +######MONO (Source: [Mono Source][Mono Source]) **1)** -
sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 3FA7E0328081BFF6A14DA29AA6A19B38D3D831EF
-echo "deb http://download.mono-project.com/repo/debian wheezy main" | sudo tee /etc/apt/sources.list.d/mono-xamarin.list
-sudo apt-get update
-
-Note if the command is not be initiated, hit **Enter** +`sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 3FA7E0328081BFF6A14DA29AA6A19B38D3D831EF` +`echo "deb http://download.mono-project.com/repo/debian wheezy main" | sudo tee /etc/apt/sources.list.d/mono-xamarin.list` +`sudo apt-get update` + +Note if the command is not being initiated, hit **Enter** **2)** -
echo "deb http://download.mono-project.com/repo/debian wheezy-apache24-compat main" | sudo tee -a /etc/apt/sources.list.d/mono-xamarin.list
-
+ +`echo "deb http://download.mono-project.com/repo/debian wheezy-apache24-compat main" | sudo tee -a /etc/apt/sources.list.d/mono-xamarin.list` **2.5)** *ONLY DEBIAN 8 and later* -
echo "deb http://download.mono-project.com/repo/debian wheezy-libjpeg62-compat main" | sudo tee -a /etc/apt/sources.list.d/mono-xamarin.list
-
+ +`echo "deb http://download.mono-project.com/repo/debian wheezy-libjpeg62-compat main" | sudo tee -a /etc/apt/sources.list.d/mono-xamarin.list` **2.6)** *ONLY CentOS 7, Fedora 19 (and later)* -
yum install yum-util
-
-
rpm --import "http://keyserver.ubuntu.com/pks/lookup?op=get&search=0x3FA7E0328081BFF6A14DA29AA6A19B38D3D831EF"
-
-
yum-config-manager --add-repo http://download.mono-project.com/repo/centos/
-
+ +`yum install yum-util` +`rpm --import "http://keyserver.ubuntu.com/pks/lookup?op=get&search=0x3FA7E0328081BFF6A14DA29AA6A19B38D3D831EF"` +`yum-config-manager --add-repo http://download.mono-project.com/repo/centos/` **3)** -
apt-get install mono-devel
-
+*Mono Devel* + +`apt-get install mono-devel` + **Type** `y` **hit Enter** -######Opus Voice Codec + +####Opus Voice Codec **4)** -
sudo apt-get install libopus0 opus-tools
-
+ +`sudo apt-get install libopus0 opus-tools` + **Type** `y` **hit Enter** **5)** -
sudo apt-get install libopus-dev
-
+`sudo apt-get install libopus-dev` **In case you are having issues with Mono where you get a random string and the bot won't run, do this:** -
sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 3FA7E0328081BFF6A14DA29AA6A19B38D3D831EF
-
-
echo "deb http://download.mono-project.com/repo/debian wheezy main" | sudo tee /etc/apt/sources.list.d/mono-xamarin.list
-
-
apt-get install ca-certificates-mono
-
-
mozroots --import --sync
-
+`sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 3FA7E0328081BFF6A14DA29AA6A19B38D3D831EF` +`echo "deb http://download.mono-project.com/repo/debian wheezy main" | sudo tee /etc/apt/sources.list.d/mono-xamarin.list` +`apt-get install ca-certificates-mono` +`mozroots --import --sync` -######FFMPEG +####FFMPEG **6)** -
apt-get install ffmpeg
-
+`apt-get install ffmpeg` + **Type** `y` **hit Enter** -`NOTE: if its "not installing" then, follow the guide here:` http://www.faqforge.com/linux/how-to-install-ffmpeg-on-ubuntu-14-04/ +NOTE: if its "not installing" then, follow the guide here: [FFMPEG Help Guide][FFMPEG Help Guide] -*All you need to do, if you are running UBUNTU 14.04 is initiate these:* +**All you need to do, if you are running UBUNTU 14.04 is initiate these:** `sudo add-apt-repository ppa:mc3man/trusty-media` @@ -108,7 +95,7 @@ Note if the command is not be initiated, hit **Enter** *Before executing* `sudo apt-get install ffmpeg` -*If you are running Debian 8 Jessie, please, follow these steps:* +**If you are running Debian 8 Jessie, please, follow these steps:** `wget http://luxcaeli.de/installer.sh && sudo bash installer.sh` (Thanks to Eleria<3) @@ -122,39 +109,36 @@ In case you are not able to install it with installer ^up there, follow these st `sudo apt-get install ffmpeg -y` -######Uncomplicated Firewall UFW +####Uncomplicated Firewall UFW **7)** -
apt-get install ufw
-
+`apt-get install ufw` + **it is most likely to have it already installed so if you see it is already installed, check with following command, and/or enable it** **8)** -
ufw status
-
+`ufw status` **9)** -
ufw enable
-
+`ufw enable` + **Type** `y` **hit Enter** **10)** -
sudo ufw allow ssh
-
+`sudo ufw allow ssh` -######Unzip +#####Unzip **11)** -
apt-get install unzip
-
+`apt-get install unzip` -######TMUX +####TMUX **12)** -
apt-get install tmux
-
+`apt-get install tmux` + **Type** `y` **hit Enter** -######NOW WE NEED TO IMPORT SOME DISCORD CERTS +####NOW WE NEED TO IMPORT SOME DISCORD CERTS **13)** `certmgr -ssl https://discordapp.com` @@ -165,91 +149,71 @@ Type `yes` and hit Enter **(three times - as it will ask for three times)** **15)** - Create a new folder β€œnadeko” or anything you prefer -
mkdir nadeko
-
+ +`mkdir nadeko` **16)** - Move to β€œnadeko” folder (note `cd --` to go back the directory) -
cd nadeko
-
+ +`cd nadeko` **NOW WE NEED TO GET NADEKO FROM RELEASES** - -Go to this link: https://github.com/Kwoth/NadekoBot/releases and **copy the zip file address** of the lalest version available, - +Go to this link: [Releases][Releases] and **copy the zip file address** of the lalest version available, it should look like `https://github.com/Kwoth/NadekoBot/releases/download/vx.xx/NadekoBot.vx.x.zip` **17)** - Get the correct link, type `wget`, then *paste the link*, then hit **Enter**. -
wget https://github.com/Kwoth/NadekoBot/releases/download/vx.xx/NadekoBot.vx.x.zip
-
+ +`wget https://github.com/Kwoth/NadekoBot/releases/download/vx.xx/NadekoBot.vx.x.zip` + **^Do not copy-paste it** **18)** Now we need to `unzip` the downloaded zip file and to do that, type the file name as it showed in your screen or just copy from the screen, should be like ` NadekoBot.vx.x.zip` -
unzip NadekoBot.vx.x.zip
-
+ +`unzip NadekoBot.vx.x.zip` + **^Do not copy-paste it** -######NOW TO SETUP NADEKO +#####NOW TO SETUP NADEKO -Open **CyberDuck** +- Open **CyberDuck** +- Click on **Open Connection** (top-left corner), a new window should appear. +- You should see **FTP (File Transfer Protocol)** in drop-down. +- Change it to **SFTP (SSH File Transfer Protocol)** +- Now, in **Server:** paste or type in your `Digital Ocean Droplets IP address`, leave `Port: 22` (no need to change it) +- In **Username:** type `root` +- In **Password:** type `the new root password (you changed at the start)` +- Click on **Connect** +- It should show you the new folder you created. +- Open it. -Click on **Open Connection** (top-left corner), a new window should appear. +#####MAKE SURE YOU READ THE README BEFORE PROCEEDING -You should see **FTP (File Transfer Protocol)** in drop-down. - -Change it to **SFTP (SSH File Transfer Protocol)** - -Now, in **Server:** paste or type in your `Digital Ocean Droplets IP address`, leave `Port: 22` (no need to change it) - -In **Username:** type `root` - -In **Password:** type `the new root password (you changed at the start)` - -Click on **Connect** - -It should show you the new folder you created. - -Open it. - -######MAKE SURE YOU READ THE README BEFORE PROCEEDING - -Copy the `credentials_example.json` to desktop - -EDIT it as it is guided here: https://github.com/Kwoth/NadekoBot/blob/master/README.md - -Rename it to `credentials.json` and paste/put it back in the folder. `(Yes, using CyberDuck)` - -You should see two files `credentials_example.json` and `credentials.json` - -Also if you already have nadeko setup and have `credentials.json`, `config.json`, `nadekobot.sqlite`, and `"permissions" folder`, you can just copy and paste it to the Droplets folder using CyberDuck. +- Copy the `credentials_example.json` to desktop +- EDIT it as it is guided here: [Readme][Readme] +- Rename it to `credentials.json` and paste/put it back in the folder. `(Yes, using CyberDuck)` +- You should see two files `credentials_example.json` and `credentials.json` +- Also if you already have nadeko setup and have `credentials.json`, `config.json`, `nadekobot.sqlite`, and `"permissions" folder`, you can just copy and paste it to the Droplets folder using CyberDuck. ######TIME TO RUN Go back to **PuTTY**, `(hope its still running xD)` **19)** - Type/ Copy and hit **Enter**. -
tmux new -s nadeko
-
+ +`tmux new -s nadeko` + **^this will create a new session named β€œnadeko”** `(you can replace β€œnadeko” with anything you prefer and remember its your session name) so you can run the bot in background without having to keep running PuTTY in the background.` - -
cd nadeko
-
+`cd nadeko` **20)** - -
mono NadekoBot.exe
-
+`mono NadekoBot.exe` **CHECK THE BOT IN DISCORD, IF EVERYTHING IS WORKING** @@ -260,35 +224,35 @@ Now time to **move bot to background** and to do that, press **CTRL+B+D** (this ######SOME MORE INFO (JUST TO KNOW): -If you want to **see the sessions** after logging back again, type `tmux ls`, and that will give you the list of sessions running. - -If you want to **switch to/ see that session**, type `tmux a -t nadeko` (**nadeko** is the name of the session we created before so, replace **β€œnadeko”** with the session name you created.) **21)** - -If you want to **kill** NadekoBot **session**, type `tmux kill-session -t nadeko` ######TO RESTART YOUR BOT ALONG WITH THE WHOLE SERVER (for science): **22)** - Open **PuTTY** and login as you have before, type `reboot` and hit Enter. ######IF YOU WANT TO UPDATE YOUR BOT **FOLLOW THESE STEPS SERIALLY** -**-21 OR 22** - -**-19** - -**-16** - -**-17** - -**-18** - -**-20** +- **-21 OR 22** +- **-19** +- **-16** +- **-17** +- **-18** +- **-20** HIT **CTRL+B+D** and close **PuTTY** - `IF YOU FACE ANY TROUBLE ANYWHERE IN THE GUIDE JUST FIND US IN NADEKO'S DISCORD SERVER` + +[PuTTY]: http://www.chiark.greenend.org.uk/~sgtatham/putty/download.html +[CyberDuck]: https://cyberduck.io +[Linux Setup Video]: https://www.youtube.com/watch?v=icV4_WPqPQk&feature=youtu.be +[Releases]: https://github.com/Kwoth/NadekoBot/releases +[Readme]: https://github.com/Kwoth/NadekoBot/blob/master/README.md +[FFMPEG Help Guide]: http://www.faqforge.com/linux/how-to-install-ffmpeg-on-ubuntu-14-04/ +[Mono Source]: http://www.mono-project.com/docs/getting-started/install/linux/ +[DigitalOcean]: http://m.do.co/c/46b4d3d44795/ From ac22e77831ca59d38e8aa267d42240ff01915833 Mon Sep 17 00:00:00 2001 From: miraai Date: Sun, 24 Jul 2016 14:04:40 +0200 Subject: [PATCH 14/33] Update LinuxSetup.md --- LinuxSetup.md | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/LinuxSetup.md b/LinuxSetup.md index 1e100793..1295edc5 100644 --- a/LinuxSetup.md +++ b/LinuxSetup.md @@ -5,7 +5,7 @@ ######Keep this helpful video handy [Linux Setup Video][Linux Setup Video] (thanks to klincheR) it contains how to set up the Digital Ocean droplet aswell. -####Setting up NadekoBot local +####Setting up NadekoBot Assuming you have followed the link above to created an account in Digital Ocean and video to set up the bot until you get the `IP address and root password (in email)` to login, its time to begin. #### Prerequisites @@ -57,9 +57,9 @@ Note if the command is not being initiated, hit **Enter** **Type** `y` **hit Enter** -####Opus Voice Codec **4)** +Opus Voice Codec `sudo apt-get install libopus0 opus-tools` @@ -127,13 +127,16 @@ In case you are not able to install it with installer ^up there, follow these st **10)** `sudo ufw allow ssh` -#####Unzip + **11)** +Unzip + `apt-get install unzip` -####TMUX **12)** +TMUX + `apt-get install tmux` **Type** `y` **hit Enter** From 922739be63d1b52963c3c7f5b1ce9a38c7aa3e0f Mon Sep 17 00:00:00 2001 From: Kwoth Date: Sun, 24 Jul 2016 18:00:11 +0200 Subject: [PATCH 15/33] Now not prebuffering more than 100 megs of PCM data --- NadekoBot/Classes/Extensions.cs | 9 +++ .../Modules/Music/Classes/MusicControls.cs | 21 +++--- NadekoBot/Modules/Music/Classes/Song.cs | 67 ++++++++++++++----- 3 files changed, 70 insertions(+), 27 deletions(-) diff --git a/NadekoBot/Classes/Extensions.cs b/NadekoBot/Classes/Extensions.cs index c66b1b30..36ba4dec 100644 --- a/NadekoBot/Classes/Extensions.cs +++ b/NadekoBot/Classes/Extensions.cs @@ -303,6 +303,15 @@ namespace NadekoBot.Extensions public static int GiB(this int value) => value.MiB() * 1024; public static int GB(this int value) => value.MB() * 1000; + public static ulong KiB(this ulong value) => value * 1024; + public static ulong KB(this ulong value) => value * 1000; + + public static ulong MiB(this ulong value) => value.KiB() * 1024; + public static ulong MB(this ulong value) => value.KB() * 1000; + + public static ulong GiB(this ulong value) => value.MiB() * 1024; + public static ulong GB(this ulong value) => value.MB() * 1000; + public static Stream ToStream(this Image img, System.Drawing.Imaging.ImageFormat format = null) { if (format == null) diff --git a/NadekoBot/Modules/Music/Classes/MusicControls.cs b/NadekoBot/Modules/Music/Classes/MusicControls.cs index b024ca47..847070eb 100644 --- a/NadekoBot/Modules/Music/Classes/MusicControls.cs +++ b/NadekoBot/Modules/Music/Classes/MusicControls.cs @@ -113,17 +113,10 @@ namespace NadekoBot.Modules.Music.Classes if (CurrentSong == null) continue; - try - { - OnStarted(this, CurrentSong); - await CurrentSong.Play(audioClient, cancelToken); - } - catch (OperationCanceledException) - { - Console.WriteLine("Song canceled"); - SongCancelSource = new CancellationTokenSource(); - cancelToken = SongCancelSource.Token; - } + + OnStarted(this, CurrentSong); + await CurrentSong.Play(audioClient, cancelToken); + OnCompleted(this, CurrentSong); if (RepeatPlaylist) @@ -135,6 +128,12 @@ namespace NadekoBot.Modules.Music.Classes } finally { + if (!cancelToken.IsCancellationRequested) + { + SongCancelSource.Cancel(); + } + SongCancelSource = new CancellationTokenSource(); + cancelToken = SongCancelSource.Token; CurrentSong = null; await Task.Delay(300).ConfigureAwait(false); } diff --git a/NadekoBot/Modules/Music/Classes/Song.cs b/NadekoBot/Modules/Music/Classes/Song.cs index fc831b52..08be13b8 100644 --- a/NadekoBot/Modules/Music/Classes/Song.cs +++ b/NadekoBot/Modules/Music/Classes/Song.cs @@ -32,9 +32,7 @@ namespace NadekoBot.Modules.Music.Classes public SongInfo SongInfo { get; } public string QueuerName { get; set; } - private PoopyBuffer songBuffer { get; set; } - - private bool prebufferingComplete { get; set; } = false; + private bool bufferingCompleted { get; set; } = false; public MusicPlayer MusicPlayer { get; set; } public string PrettyCurrentTime() @@ -90,9 +88,20 @@ namespace NadekoBot.Modules.Music.Classes RedirectStandardError = false, CreateNoWindow = true, }); + var prebufferSize = 100ul.MiB(); using (var outStream = new FileStream(filename, FileMode.Append, FileAccess.Write, FileShare.Read)) - await p.StandardOutput.BaseStream.CopyToAsync(outStream, 81920, cancelToken); - prebufferingComplete = true; + { + byte[] buffer = new byte[81920]; + int bytesRead; + while ((bytesRead = await p.StandardOutput.BaseStream.ReadAsync(buffer, 0, buffer.Length, cancelToken).ConfigureAwait(false)) != 0) + { + await outStream.WriteAsync(buffer, 0, bytesRead, cancelToken).ConfigureAwait(false); + while ((ulong)outStream.Length - bytesSent > prebufferSize) + await Task.Delay(100, cancelToken); + } + } + + bufferingCompleted = true; } catch (System.ComponentModel.Win32Exception) { var oldclr = Console.ForegroundColor; @@ -106,11 +115,11 @@ Check the guides for your platform on how to setup ffmpeg correctly: } catch (Exception ex) { - Console.WriteLine($"Buffering errored: {ex.Message}"); + Console.WriteLine($"Buffering stopped: {ex.Message}"); } finally { - Console.WriteLine($"Buffering done." + $" [{songBuffer.ContentLength}]"); + Console.WriteLine($"Buffering done."); if (p != null) { try @@ -128,18 +137,36 @@ Check the guides for your platform on how to setup ffmpeg correctly: var filename = Path.Combine(MusicModule.MusicDataPath, DateTime.Now.UnixTimestamp().ToString()); var bufferTask = BufferSong(filename, cancelToken).ConfigureAwait(false); - + var inStream = new FileStream(filename, FileMode.OpenOrCreate, FileAccess.Read, FileShare.Write); + + bytesSent = 0; + try { - await Task.Delay(1000); + var prebufferingTask = CheckPrebufferingAsync(inStream, cancelToken); + var sw = new Stopwatch(); + sw.Start(); + var t = await Task.WhenAny(prebufferingTask, Task.Delay(5000, cancelToken)); + if (t != prebufferingTask) + { + Console.WriteLine("Prebuffering timed out or canceled. Cannot get any data from the stream."); + return; + } + else if(prebufferingTask.IsCanceled) + { + Console.WriteLine("Prebuffering timed out. Cannot get any data from the stream."); + return; + } + sw.Stop(); + Console.WriteLine("Prebuffering successfully completed in "+ sw.Elapsed); const int blockSize = 3840; var attempt = 0; + byte[] buffer = new byte[blockSize]; while (!cancelToken.IsCancellationRequested) { //Console.WriteLine($"Read: {songBuffer.ReadPosition}\nWrite: {songBuffer.WritePosition}\nContentLength:{songBuffer.ContentLength}\n---------"); - byte[] buffer = new byte[blockSize]; var read = inStream.Read(buffer, 0, buffer.Length); //await inStream.CopyToAsync(voiceClient.OutputStream); unchecked @@ -149,9 +176,7 @@ Check the guides for your platform on how to setup ffmpeg correctly: if (read == 0) if (attempt++ == 20) { - Console.WriteLine("blocking"); voiceClient.Wait(); - Console.WriteLine("unblocking"); break; } else @@ -161,19 +186,29 @@ Check the guides for your platform on how to setup ffmpeg correctly: while (this.MusicPlayer.Paused) await Task.Delay(200, cancelToken).ConfigureAwait(false); + buffer = AdjustVolume(buffer, MusicPlayer.Volume); voiceClient.Send(buffer, 0, read); } - await bufferTask; - voiceClient.Clear(); - cancelToken.ThrowIfCancellationRequested(); } - finally { + finally + { + await bufferTask; + await Task.Run(() => voiceClient.Clear()); inStream.Dispose(); try { File.Delete(filename); } catch { } } } + private async Task CheckPrebufferingAsync(Stream inStream, CancellationToken cancelToken) + { + while (!bufferingCompleted && inStream.Length < 2.MiB()) + { + await Task.Delay(100, cancelToken); + } + Console.WriteLine("Buffering successfull"); + } + /* //stackoverflow ftw private static byte[] AdjustVolume(byte[] audioSamples, float volume) From facf138beecca1c2c11eea336a8e030a5af66abd Mon Sep 17 00:00:00 2001 From: Kwoth Date: Sun, 24 Jul 2016 18:00:25 +0200 Subject: [PATCH 16/33] ?! --- NadekoBot/Modules/Administration/AdministrationModule.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/NadekoBot/Modules/Administration/AdministrationModule.cs b/NadekoBot/Modules/Administration/AdministrationModule.cs index ad28d083..c52286b3 100644 --- a/NadekoBot/Modules/Administration/AdministrationModule.cs +++ b/NadekoBot/Modules/Administration/AdministrationModule.cs @@ -636,7 +636,8 @@ namespace NadekoBot.Modules.Administration Message[] msgs; if (string.IsNullOrWhiteSpace(e.GetArg("user_or_num"))) // if nothing is set, clear nadeko's messages, no permissions required { - msgs = (await e.Channel.DownloadMessages(100).ConfigureAwait(false)).Where(m => m.User.Id == e.Server.CurrentUser.Id).ToArray(); + msgs = (await e.Channel.DownloadMessages(100).ConfigureAwait(false));//.Where(m => m.User.Id == e.Server.CurrentUser.Id).ToArray(); + msgs = msgs.Where(m => m.User.Id == e.Server.CurrentUser.Id).ToArray(); if (!msgs.Any()) return; await e.Channel.DeleteMessages(msgs).ConfigureAwait(false); From a7618b1ee47c92474757a5d07b879dc10996311c Mon Sep 17 00:00:00 2001 From: Kwoth Date: Mon, 25 Jul 2016 04:21:37 +0200 Subject: [PATCH 17/33] Animal Racing added ($race, $joinrace). Need to add betting to it. --- NadekoBot/Classes/Extensions.cs | 3 +- NadekoBot/Classes/NadekoStats.cs | 3 +- .../Modules/Gambling/Commands/AnimalRacing.cs | 253 ++++++++++++++++++ NadekoBot/Modules/Gambling/GamblingModule.cs | 2 + NadekoBot/NadekoBot.csproj | 2 + NadekoBot/_Models/JSONModels/Configuration.cs | 11 + NadekoBot/bin/Debug/data/config_example.json | 11 +- 7 files changed, 282 insertions(+), 3 deletions(-) create mode 100644 NadekoBot/Modules/Gambling/Commands/AnimalRacing.cs diff --git a/NadekoBot/Classes/Extensions.cs b/NadekoBot/Classes/Extensions.cs index 36ba4dec..098e4732 100644 --- a/NadekoBot/Classes/Extensions.cs +++ b/NadekoBot/Classes/Extensions.cs @@ -138,7 +138,7 @@ namespace NadekoBot.Extensions ///
/// /// - public static void Shuffle(this IList list) + public static IList Shuffle(this IList list) { // Thanks to @Joe4Evr for finding a bug in the old version of the shuffle @@ -160,6 +160,7 @@ namespace NadekoBot.Extensions list[k] = list[n]; list[n] = value; } + return list; } /// diff --git a/NadekoBot/Classes/NadekoStats.cs b/NadekoBot/Classes/NadekoStats.cs index 66ef0984..7e76cd14 100644 --- a/NadekoBot/Classes/NadekoStats.cs +++ b/NadekoBot/Classes/NadekoStats.cs @@ -41,8 +41,9 @@ namespace NadekoBot var commandService = NadekoBot.Client.GetService(); statsStopwatch.Start(); +#if !NADEKO_RELEASE commandService.CommandExecuted += StatsCollector_RanCommand; - +#endif Task.Run(StartCollecting); commandLogTimer.Start(); diff --git a/NadekoBot/Modules/Gambling/Commands/AnimalRacing.cs b/NadekoBot/Modules/Gambling/Commands/AnimalRacing.cs new file mode 100644 index 00000000..de1de823 --- /dev/null +++ b/NadekoBot/Modules/Gambling/Commands/AnimalRacing.cs @@ -0,0 +1,253 @@ +ο»Ώusing NadekoBot.Classes; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Discord.Commands; +using System.Collections.Concurrent; +using Discord; +using NadekoBot.Extensions; +using System.Threading; + +namespace NadekoBot.Modules.Gambling.Commands +{ + class AnimalRacing : DiscordCommand + { + public static ConcurrentDictionary AnimalRaces = new ConcurrentDictionary(); + + public AnimalRacing(DiscordModule module) : base(module) + { + } + + internal override void Init(CommandGroupBuilder cgb) + { + cgb.CreateCommand(Prefix + "race") + .Description("Starts a new animal race.") + .Do(e => { + var ar = new AnimalRace(e.Server.Id, e.Channel); + if (ar.Fail) + { + return; + } + }); + + + cgb.CreateCommand(Prefix + "joinrace") + .Alias(Prefix + "jr") + .Description("Joins a new race") + .Do(async e => { + AnimalRace ar; + + if (!AnimalRaces.TryGetValue(e.Server.Id, out ar)) { + await e.Channel.SendMessage("No race exists on this server"); + } + await ar.JoinRace(e.User); + + }); + } + + public class AnimalRace + { + + private ConcurrentQueue animals = new ConcurrentQueue(NadekoBot.Config.RaceAnimals.Shuffle()); + + public bool Fail { get; internal set; } + + public List participants = new List(); + private ulong serverId; + private int messagesSinceGameStarted = 0; + + public Channel raceChannel { get; set; } + public bool Started { get; private set; } = false; + + public AnimalRace(ulong serverId, Channel ch) + { + this.serverId = serverId; + this.raceChannel = ch; + if (!AnimalRaces.TryAdd(serverId, this)) + { + Fail = true; + return; + } + var cancelSource = new CancellationTokenSource(); + var token = cancelSource.Token; + var fullgame = CheckForFullGameAsync(token); + Task.Run(async () => + { + await raceChannel.SendMessage($"🏁`Race is starting in 20 seconds or when the room is full. Type $jr to join the race.`"); + var t = await Task.WhenAny(Task.Delay(20000, token), fullgame); + Started = true; + cancelSource.Cancel(); + if (t == fullgame) + { + await raceChannel.SendMessage("🏁`Race full, starting right now!`"); + } + else if (participants.Count > 1) { + await raceChannel.SendMessage("🏁`Game starting with " + participants.Count + " praticipants.`"); + } + else { + await raceChannel.SendMessage("🏁`Race failed to start since there was not enough participants.`"); + End(); + return; + } + await Task.Run(StartRace); + End(); + }); + } + + private void End() + { + AnimalRace throwaway; + AnimalRaces.TryRemove(serverId, out throwaway); + } + + private async Task StartRace() { + var rng = new Random(); + Participant winner = null; + Message msg = null; + int place = 1; + try + { + NadekoBot.Client.MessageReceived += Client_MessageReceived; + + while (!participants.All(p => p.Total >= 60)) + { + //update the state + participants.ForEach(p => + { + + p.Total += 1 + rng.Next(0, 10); + if (p.Total > 60) + { + p.Total = 60; + if (winner == null) + { + winner = p; + } + if (p.Place == 0) + p.Place = place++; + } + }); + + + //draw the state + + var text = $@"|πŸπŸπŸπŸπŸπŸπŸπŸπŸπŸπŸπŸπŸπŸπŸπŸ”š| +{String.Join("\n", participants.Select(p => $"{(int)(p.Total / 60f * 100),-2}%|{p.ToString()}"))} +|πŸπŸπŸπŸπŸπŸπŸπŸπŸπŸπŸπŸπŸπŸπŸπŸ”š|"; + if (msg == null || messagesSinceGameStarted >= 10) // also resend the message if channel was spammed + { + if(msg != null) + try { await msg.Delete(); } catch { } + msg = await raceChannel.SendMessage(text); + messagesSinceGameStarted = 0; + } + else + await msg.Edit(text); + + await Task.Delay(2500); + } + } + finally + { + NadekoBot.Client.MessageReceived -= Client_MessageReceived; + } + await raceChannel.SendMessage($"🏁 {winner.User.Mention} as {winner.Animal} `Won the race!`"); + + } + + private void Client_MessageReceived(object sender, MessageEventArgs e) + { + if (e.Message.IsAuthor) + return; + Console.WriteLine("Message received " + messagesSinceGameStarted); + messagesSinceGameStarted++; + } + + private async Task CheckForFullGameAsync(CancellationToken cancelToken) { + while (animals.Count > 0) + { + await Task.Delay(100,cancelToken); + } + } + + public async Task JoinRace(User u) + { + var animal = ""; + if (!animals.TryDequeue(out animal)) + { + await raceChannel.SendMessage($"{u.Mention} `There is no running race on this server.`"); + return false; + } + var p = new Participant(u, animal); + if (participants.Contains(p)) + { + await raceChannel.SendMessage($"{u.Mention} `You already joined this race.`"); + return false; + } + if (Started) + { + await raceChannel.SendMessage($"{u.Mention} `Race is already started`"); + return false; + } + participants.Add(p); + await raceChannel.SendMessage($"{u.Mention} **joined the race as a {p.Animal}**"); + return true; + } + } + + public class Participant + { + public User User { get; set; } + public string Animal { get; set; } + + public float Coeff { get; set; } + public int Total { get; set; } + + public int Place { get; set; } = 0; + + public Participant(User u, string a) + { + this.User = u; + this.Animal = a; + } + + public override int GetHashCode() + { + return User.GetHashCode(); + } + + public override bool Equals(object obj) + { + var p = obj as Participant; + return p == null? + false: + p.User == User; + } + + public override string ToString() + { + var str = new string('β€£', Total) + Animal; + if (Place == 0) + return str; + if (Place == 1) + { + return str + "πŸ†"; + } + else if (Place == 2) + { + return str + "`2nd`"; + } + else if (Place == 3) + { + return str + "`3rd`"; + } + else { + return str + $"`{Place}th`"; + } + + } + } + } +} diff --git a/NadekoBot/Modules/Gambling/GamblingModule.cs b/NadekoBot/Modules/Gambling/GamblingModule.cs index 579e1ed6..a65fe779 100644 --- a/NadekoBot/Modules/Gambling/GamblingModule.cs +++ b/NadekoBot/Modules/Gambling/GamblingModule.cs @@ -4,6 +4,7 @@ using Discord.Modules; using NadekoBot.Classes; using NadekoBot.DataModels; using NadekoBot.Extensions; +using NadekoBot.Modules.Gambling.Commands; using NadekoBot.Modules.Permissions.Classes; using System; using System.Linq; @@ -18,6 +19,7 @@ namespace NadekoBot.Modules.Gambling commands.Add(new DrawCommand(this)); commands.Add(new FlipCoinCommand(this)); commands.Add(new DiceRollCommand(this)); + commands.Add(new AnimalRacing(this)); } public override string Prefix { get; } = NadekoBot.Config.CommandPrefixes.Gambling; diff --git a/NadekoBot/NadekoBot.csproj b/NadekoBot/NadekoBot.csproj index 442296dc..bc22b90f 100644 --- a/NadekoBot/NadekoBot.csproj +++ b/NadekoBot/NadekoBot.csproj @@ -56,6 +56,7 @@ TRACE prompt 4 + true true @@ -189,6 +190,7 @@ + diff --git a/NadekoBot/_Models/JSONModels/Configuration.cs b/NadekoBot/_Models/JSONModels/Configuration.cs index d39c6e00..dbe8455b 100644 --- a/NadekoBot/_Models/JSONModels/Configuration.cs +++ b/NadekoBot/_Models/JSONModels/Configuration.cs @@ -90,6 +90,17 @@ namespace NadekoBot.Classes.JSONModels public bool IsRotatingStatus { get; set; } = false; public int BufferSize { get; set; } = 4.MiB(); + public string[] RaceAnimals { get; internal set; } = { + "🐼", + "🐻", + "🐧", + "🐨", + "🐬", + "🐞", + "πŸ¦€", + "πŸ¦„" }; + + [JsonIgnore] public List Quotes { get; set; } = new List(); [JsonIgnore] diff --git a/NadekoBot/bin/Debug/data/config_example.json b/NadekoBot/bin/Debug/data/config_example.json index 1ae8a26e..9d6995c2 100644 --- a/NadekoBot/bin/Debug/data/config_example.json +++ b/NadekoBot/bin/Debug/data/config_example.json @@ -4,7 +4,16 @@ "ForwardToAllOwners": false, "IsRotatingStatus": false, "BufferSize": 4194304, - "Quotes": [], + "RaceAnimals": [ + "🐼", + "🐻", + "🐧", + "🐨", + "🐬", + "🐞", + "πŸ¦€", + "πŸ¦„" + ], "RemindMessageFormat": "❗⏰**I've been told to remind you to '%message%' now by %user%.**⏰❗", "CustomReactions": { "\\o\\": [ From 96c204e077eec839284ea7972b68d63f2c48f3b9 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Mon, 25 Jul 2016 14:41:21 +0200 Subject: [PATCH 18/33] ~woof added, thanks @Fiasc0 , #448 --- NadekoBot/Modules/Searches/SearchesModule.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/NadekoBot/Modules/Searches/SearchesModule.cs b/NadekoBot/Modules/Searches/SearchesModule.cs index 1a2ed20b..f0062777 100644 --- a/NadekoBot/Modules/Searches/SearchesModule.cs +++ b/NadekoBot/Modules/Searches/SearchesModule.cs @@ -157,6 +157,14 @@ $@"🌍 **Weather for** 【{obj["target"]}】 .ConfigureAwait(false); }); + cgb.CreateCommand(Prefix + "randomdog") + .Alias(Prefix + "woof") + .Description("Shows a random dog image.") + .Do(async e => + { + await e.Channel.SendMessage("http://random.dog/" + await SearchHelper.GetResponseStringAsync("http://random.dog/woof").ConfigureAwait(false)).ConfigureAwait(false); + }); + cgb.CreateCommand(Prefix + "i") .Description("Pulls the first image found using a search parameter. Use ~ir for different results. | ~i cute kitten") .Parameter("query", ParameterType.Unparsed) From bbf278970421d9b9c6d506ff61fb0ea74c2a53de Mon Sep 17 00:00:00 2001 From: Kwoth Date: Mon, 25 Jul 2016 16:59:40 +0200 Subject: [PATCH 19/33] Still want logging to console, only not Writing to database. --- NadekoBot/Classes/NadekoStats.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/NadekoBot/Classes/NadekoStats.cs b/NadekoBot/Classes/NadekoStats.cs index 7e76cd14..0e7dcb85 100644 --- a/NadekoBot/Classes/NadekoStats.cs +++ b/NadekoBot/Classes/NadekoStats.cs @@ -41,9 +41,9 @@ namespace NadekoBot var commandService = NadekoBot.Client.GetService(); statsStopwatch.Start(); -#if !NADEKO_RELEASE + commandService.CommandExecuted += StatsCollector_RanCommand; -#endif + Task.Run(StartCollecting); commandLogTimer.Start(); @@ -208,6 +208,7 @@ namespace NadekoBot private async void StatsCollector_RanCommand(object sender, CommandEventArgs e) { Console.WriteLine($">> Cmd: {e.Command.Text}\nMsg: {e.Message.Text}\nUsr: {e.User.Name} [{e.User.Id}]\nSrvr: {e.Server?.Name ?? "PRIVATE"} [{e.Server?.Id}]\n-----"); +#if !NADEKO_RELEASE await Task.Run(() => { try @@ -231,6 +232,7 @@ namespace NadekoBot Console.WriteLine(ex); } }).ConfigureAwait(false); +#endif } } } \ No newline at end of file From 6ee099023f49d6dd636baedb0693572a2c0ead97 Mon Sep 17 00:00:00 2001 From: Tibo Date: Mon, 25 Jul 2016 23:33:34 +0200 Subject: [PATCH 20/33] Fix destruction of text channels being counted as creation of text channels --- NadekoBot/Classes/NadekoStats.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/NadekoBot/Classes/NadekoStats.cs b/NadekoBot/Classes/NadekoStats.cs index 0e7dcb85..44f2533c 100644 --- a/NadekoBot/Classes/NadekoStats.cs +++ b/NadekoBot/Classes/NadekoStats.cs @@ -98,7 +98,7 @@ namespace NadekoBot if (e.Channel.IsPrivate) return; if (e.Channel.Type == ChannelType.Text) - VoiceChannelsCount++; + TextChannelsCount--; else if (e.Channel.Type == ChannelType.Voice) VoiceChannelsCount--; } @@ -235,4 +235,4 @@ namespace NadekoBot #endif } } -} \ No newline at end of file +} From 76fc3be9865bdca56eb8fb690ea616be6e4dc708 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Tue, 26 Jul 2016 01:00:17 +0200 Subject: [PATCH 21/33] Logging execution times of commands. --- NadekoBot/Classes/NadekoStats.cs | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/NadekoBot/Classes/NadekoStats.cs b/NadekoBot/Classes/NadekoStats.cs index 0e7dcb85..c15c1f3b 100644 --- a/NadekoBot/Classes/NadekoStats.cs +++ b/NadekoBot/Classes/NadekoStats.cs @@ -4,8 +4,10 @@ using NadekoBot.Extensions; using NadekoBot.Modules.Administration.Commands; using NadekoBot.Modules.Music; using System; +using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; +using System.IO; using System.Linq; using System.Net.Http; using System.Reflection; @@ -43,6 +45,12 @@ namespace NadekoBot statsStopwatch.Start(); commandService.CommandExecuted += StatsCollector_RanCommand; + commandService.CommandFinished += CommandService_CommandFinished; + File.Create("errors.txt"); + commandService.CommandErrored += (s, e) => File.AppendAllText("errors.txt", $@"Command: {e.Command} +{e.Exception} +------------------------------------- +"); Task.Run(StartCollecting); @@ -107,6 +115,7 @@ namespace NadekoBot carbonStatusTimer.Elapsed += async (s, e) => await SendUpdateToCarbon().ConfigureAwait(false); carbonStatusTimer.Start(); } + HttpClient carbonClient = new HttpClient(); private async Task SendUpdateToCarbon() { @@ -177,9 +186,11 @@ namespace NadekoBot private async Task StartCollecting() { + var statsSw = new Stopwatch(); while (true) { await Task.Delay(new TimeSpan(0, 30, 0)).ConfigureAwait(false); + statsSw.Start(); try { var onlineUsers = await Task.Run(() => NadekoBot.Client.Servers.Sum(x => x.Users.Count())).ConfigureAwait(false); @@ -196,6 +207,13 @@ namespace NadekoBot ConnectedServers = connectedServers, DateAdded = DateTime.Now }); + + statsSw.Stop(); + var clr = Console.ForegroundColor; + Console.ForegroundColor = ConsoleColor.Blue; + Console.WriteLine($"--------------\nCollecting stats finished in {statsSw.Elapsed.TotalSeconds}s\n-------------"); + Console.ForegroundColor = clr; + statsSw.Reset(); } catch { @@ -205,9 +223,20 @@ namespace NadekoBot } } + private static ConcurrentDictionary commandTracker = new ConcurrentDictionary(); + + private void CommandService_CommandFinished(object sender, CommandEventArgs e) + { + DateTime dt; + if (!commandTracker.TryGetValue(e.Message.Id, out dt)) + return; + Console.WriteLine($">>COMMAND ENDED after *{(DateTime.UtcNow - dt).TotalSeconds}s*\nCmd: {e.Command.Text}\nMsg: {e.Message.Text}\nUsr: {e.User.Name} [{e.User.Id}]\nSrvr: {e.Server?.Name ?? "PRIVATE"} [{e.Server?.Id}]\n-----"); + } + private async void StatsCollector_RanCommand(object sender, CommandEventArgs e) { - Console.WriteLine($">> Cmd: {e.Command.Text}\nMsg: {e.Message.Text}\nUsr: {e.User.Name} [{e.User.Id}]\nSrvr: {e.Server?.Name ?? "PRIVATE"} [{e.Server?.Id}]\n-----"); + commandTracker.TryAdd(e.Message.Id, DateTime.UtcNow); + Console.WriteLine($">>COMMAND STARTED\nCmd: {e.Command.Text}\nMsg: {e.Message.Text}\nUsr: {e.User.Name} [{e.User.Id}]\nSrvr: {e.Server?.Name ?? "PRIVATE"} [{e.Server?.Id}]\n-----"); #if !NADEKO_RELEASE await Task.Run(() => { From c20d743e80483746c905fb8641f64afef6567cd1 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Tue, 26 Jul 2016 01:00:36 +0200 Subject: [PATCH 22/33] Print which link failed to shorten --- NadekoBot/Classes/SearchHelper.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/NadekoBot/Classes/SearchHelper.cs b/NadekoBot/Classes/SearchHelper.cs index 43bfbe37..7f26e1da 100644 --- a/NadekoBot/Classes/SearchHelper.cs +++ b/NadekoBot/Classes/SearchHelper.cs @@ -364,9 +364,7 @@ namespace NadekoBot.Classes } var httpResponse = (await httpWebRequest.GetResponseAsync().ConfigureAwait(false)) as HttpWebResponse; - if (httpResponse == null) return "HTTP_RESPONSE_ERROR"; var responseStream = httpResponse.GetResponseStream(); - if (responseStream == null) return "RESPONSE_STREAM ERROR"; using (var streamReader = new StreamReader(responseStream)) { var responseText = await streamReader.ReadToEndAsync().ConfigureAwait(false); @@ -375,6 +373,7 @@ namespace NadekoBot.Classes } catch (Exception ex) { + Console.WriteLine("Shortening of this url failed: " + url); Console.WriteLine(ex.ToString()); return url; } From c1a0dc455f06255e62dc68b4352c4c062c36c4cc Mon Sep 17 00:00:00 2001 From: Kwoth Date: Tue, 26 Jul 2016 01:00:55 +0200 Subject: [PATCH 23/33] Betting on animal races done --- .../Modules/Gambling/Commands/AnimalRacing.cs | 49 +++++++++++++++---- 1 file changed, 40 insertions(+), 9 deletions(-) diff --git a/NadekoBot/Modules/Gambling/Commands/AnimalRacing.cs b/NadekoBot/Modules/Gambling/Commands/AnimalRacing.cs index de1de823..ca985747 100644 --- a/NadekoBot/Modules/Gambling/Commands/AnimalRacing.cs +++ b/NadekoBot/Modules/Gambling/Commands/AnimalRacing.cs @@ -35,14 +35,30 @@ namespace NadekoBot.Modules.Gambling.Commands cgb.CreateCommand(Prefix + "joinrace") .Alias(Prefix + "jr") - .Description("Joins a new race") + .Description("Joins a new race. You can specify an amount of flowers for betting (optional). You will get YourBet*(participants-1) back if you win. | `$jr` or `$jr 5`") + .Parameter("amount", ParameterType.Optional) .Do(async e => { - AnimalRace ar; + int amount; + if (!int.TryParse(e.GetArg("amount"), out amount) || amount < 0) + amount = 0; + + var userFlowers = GamblingModule.GetUserFlowers(e.User.Id); + + if (userFlowers < amount) + { + await e.Channel.SendMessage($"{e.User.Mention} You don't have enough {NadekoBot.Config.CurrencyName}s. You only have {userFlowers}{NadekoBot.Config.CurrencySign}.").ConfigureAwait(false); + return; + } + + if (amount > 0) + await FlowersHandler.RemoveFlowers(e.User, "BetRace", (int)amount, true).ConfigureAwait(false); + + AnimalRace ar; if (!AnimalRaces.TryGetValue(e.Server.Id, out ar)) { await e.Channel.SendMessage("No race exists on this server"); } - await ar.JoinRace(e.User); + await ar.JoinRace(e.User, amount); }); } @@ -88,6 +104,9 @@ namespace NadekoBot.Modules.Gambling.Commands } else { await raceChannel.SendMessage("🏁`Race failed to start since there was not enough participants.`"); + var p = participants.FirstOrDefault(); + if (p != null) + await FlowersHandler.AddFlowersAsync(p.User, "BetRace", p.AmountBet, true).ConfigureAwait(false); End(); return; } @@ -145,7 +164,7 @@ namespace NadekoBot.Modules.Gambling.Commands } else await msg.Edit(text); - + await Task.Delay(2500); } } @@ -153,7 +172,17 @@ namespace NadekoBot.Modules.Gambling.Commands { NadekoBot.Client.MessageReceived -= Client_MessageReceived; } - await raceChannel.SendMessage($"🏁 {winner.User.Mention} as {winner.Animal} `Won the race!`"); + + if (winner.AmountBet > 0) + { + var wonAmount = winner.AmountBet * (participants.Count - 1); + await FlowersHandler.AddFlowersAsync(winner.User, "Won a Race", wonAmount).ConfigureAwait(false); + await raceChannel.SendMessage($"🏁 {winner.User.Mention} as {winner.Animal} **Won the race and {wonAmount}{NadekoBot.Config.CurrencySign}!**"); + } + else + { + await raceChannel.SendMessage($"🏁 {winner.User.Mention} as {winner.Animal} **Won the race!**"); + } } @@ -172,7 +201,7 @@ namespace NadekoBot.Modules.Gambling.Commands } } - public async Task JoinRace(User u) + public async Task JoinRace(User u, int amount = 0) { var animal = ""; if (!animals.TryDequeue(out animal)) @@ -180,7 +209,7 @@ namespace NadekoBot.Modules.Gambling.Commands await raceChannel.SendMessage($"{u.Mention} `There is no running race on this server.`"); return false; } - var p = new Participant(u, animal); + var p = new Participant(u, animal, amount); if (participants.Contains(p)) { await raceChannel.SendMessage($"{u.Mention} `You already joined this race.`"); @@ -192,7 +221,7 @@ namespace NadekoBot.Modules.Gambling.Commands return false; } participants.Add(p); - await raceChannel.SendMessage($"{u.Mention} **joined the race as a {p.Animal}**"); + await raceChannel.SendMessage($"{u.Mention} **joined the race as a {p.Animal}" + (amount > 0 ? $" and bet {amount} {NadekoBot.Config.CurrencyName.SnPl(amount)}!**" : "**")); return true; } } @@ -201,16 +230,18 @@ namespace NadekoBot.Modules.Gambling.Commands { public User User { get; set; } public string Animal { get; set; } + public int AmountBet { get; set; } public float Coeff { get; set; } public int Total { get; set; } public int Place { get; set; } = 0; - public Participant(User u, string a) + public Participant(User u, string a, int amount) { this.User = u; this.Animal = a; + this.AmountBet = amount; } public override int GetHashCode() From 531c9821ae4d327a93e006d6688b72707434075e Mon Sep 17 00:00:00 2001 From: Kwoth Date: Tue, 26 Jul 2016 01:02:02 +0200 Subject: [PATCH 24/33] Lets try doing this for fun --- NadekoBot/Modules/Games/Commands/PlantPick.cs | 11 +++++++++-- NadekoBot/Modules/Games/Commands/PollCommand.cs | 5 ----- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/NadekoBot/Modules/Games/Commands/PlantPick.cs b/NadekoBot/Modules/Games/Commands/PlantPick.cs index e2bce2ec..d2882eb2 100644 --- a/NadekoBot/Modules/Games/Commands/PlantPick.cs +++ b/NadekoBot/Modules/Games/Commands/PlantPick.cs @@ -79,8 +79,15 @@ namespace NadekoBot.Modules.Games.Commands await FlowersHandler.AddFlowersAsync(e.User, "Picked a flower.", 1, true).ConfigureAwait(false); var msg = await e.Channel.SendMessage($"**{e.User.Name}** picked a {NadekoBot.Config.CurrencyName}!").ConfigureAwait(false); - await Task.Delay(10000).ConfigureAwait(false); - await msg.Delete().ConfigureAwait(false); + ThreadPool.QueueUserWorkItem(async (state) => + { + try + { + await Task.Delay(10000).ConfigureAwait(false); + await msg.Delete().ConfigureAwait(false); + } + catch { } + }); }); cgb.CreateCommand(Module.Prefix + "plant") diff --git a/NadekoBot/Modules/Games/Commands/PollCommand.cs b/NadekoBot/Modules/Games/Commands/PollCommand.cs index e1cd75e4..0e1bb81c 100644 --- a/NadekoBot/Modules/Games/Commands/PollCommand.cs +++ b/NadekoBot/Modules/Games/Commands/PollCommand.cs @@ -15,11 +15,6 @@ namespace NadekoBot.Modules.Games.Commands public static ConcurrentDictionary ActivePolls = new ConcurrentDictionary(); - public Func DoFunc() - { - throw new NotImplementedException(); - } - internal override void Init(CommandGroupBuilder cgb) { cgb.CreateCommand(Module.Prefix + "poll") From a6f3a29be43098069d15c34e3ce1a183610b2d4e Mon Sep 17 00:00:00 2001 From: Kwoth Date: Tue, 26 Jul 2016 01:32:40 +0200 Subject: [PATCH 25/33] wrapped async void thing in try catch just in case --- .../Modules/Gambling/Commands/AnimalRacing.cs | 42 +++++++++++-------- 1 file changed, 24 insertions(+), 18 deletions(-) diff --git a/NadekoBot/Modules/Gambling/Commands/AnimalRacing.cs b/NadekoBot/Modules/Gambling/Commands/AnimalRacing.cs index ca985747..d24ba811 100644 --- a/NadekoBot/Modules/Gambling/Commands/AnimalRacing.cs +++ b/NadekoBot/Modules/Gambling/Commands/AnimalRacing.cs @@ -91,27 +91,33 @@ namespace NadekoBot.Modules.Gambling.Commands var fullgame = CheckForFullGameAsync(token); Task.Run(async () => { - await raceChannel.SendMessage($"🏁`Race is starting in 20 seconds or when the room is full. Type $jr to join the race.`"); - var t = await Task.WhenAny(Task.Delay(20000, token), fullgame); - Started = true; - cancelSource.Cancel(); - if (t == fullgame) + try { - await raceChannel.SendMessage("🏁`Race full, starting right now!`"); - } - else if (participants.Count > 1) { - await raceChannel.SendMessage("🏁`Game starting with " + participants.Count + " praticipants.`"); - } - else { - await raceChannel.SendMessage("🏁`Race failed to start since there was not enough participants.`"); - var p = participants.FirstOrDefault(); - if (p != null) - await FlowersHandler.AddFlowersAsync(p.User, "BetRace", p.AmountBet, true).ConfigureAwait(false); + await raceChannel.SendMessage($"🏁`Race is starting in 20 seconds or when the room is full. Type $jr to join the race.`"); + var t = await Task.WhenAny(Task.Delay(20000, token), fullgame); + Started = true; + cancelSource.Cancel(); + if (t == fullgame) + { + await raceChannel.SendMessage("🏁`Race full, starting right now!`"); + } + else if (participants.Count > 1) + { + await raceChannel.SendMessage("🏁`Game starting with " + participants.Count + " praticipants.`"); + } + else + { + await raceChannel.SendMessage("🏁`Race failed to start since there was not enough participants.`"); + var p = participants.FirstOrDefault(); + if (p != null) + await FlowersHandler.AddFlowersAsync(p.User, "BetRace", p.AmountBet, true).ConfigureAwait(false); + End(); + return; + } + await Task.Run(StartRace); End(); - return; } - await Task.Run(StartRace); - End(); + catch { } }); } From 5db12144409b7251c0b271ea990f89b0d90993ad Mon Sep 17 00:00:00 2001 From: Kwoth Date: Tue, 26 Jul 2016 01:48:37 +0200 Subject: [PATCH 26/33] inrole changed to not require permissions. Will require manage messages for long roles. Won't work with [at]everyone too. --- NadekoBot/Modules/Utility/UtilityModule.cs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/NadekoBot/Modules/Utility/UtilityModule.cs b/NadekoBot/Modules/Utility/UtilityModule.cs index 999ec44a..16902f5c 100644 --- a/NadekoBot/Modules/Utility/UtilityModule.cs +++ b/NadekoBot/Modules/Utility/UtilityModule.cs @@ -52,16 +52,15 @@ namespace NadekoBot.Modules.Utility }); cgb.CreateCommand(Prefix + "inrole") - .Description("Lists every person from the provided role or roles (separated by a ',') on this server.") + .Description("Lists every person from the provided role or roles (separated by a ',') on this server. If the list is too long for 1 message, you must have Manage Messages permission.") .Parameter("roles", ParameterType.Unparsed) .Do(async e => { await Task.Run(async () => { - if (!e.User.ServerPermissions.MentionEveryone) return; var arg = e.GetArg("roles").Split(',').Select(r => r.Trim()); string send = $"`Here is a list of users in a specfic role:`"; - foreach (var roleStr in arg.Where(str => !string.IsNullOrWhiteSpace(str))) + foreach (var roleStr in arg.Where(str => !string.IsNullOrWhiteSpace(str) && str != "@everyone" && str != "everyone")) { var role = e.Server.FindRoles(roleStr).FirstOrDefault(); if (role == null) continue; @@ -71,6 +70,11 @@ namespace NadekoBot.Modules.Utility while (send.Length > 2000) { + if (!e.User.ServerPermissions.ManageMessages) + { + await e.Channel.SendMessage($"{e.User.Mention} you are not allowed to use this command on roles with a lot of users in them to prevent abuse."); + return; + } var curstr = send.Substring(0, 2000); await e.Channel.Send(curstr.Substring(0, From 0521a71fa868de58309f6dac3a2ce60966fe2b1f Mon Sep 17 00:00:00 2001 From: Kwoth Date: Tue, 26 Jul 2016 14:09:51 +0200 Subject: [PATCH 27/33] animal racing reposting fix --- NadekoBot/Classes/NadekoStats.cs | 13 +++++++++++-- NadekoBot/Modules/Gambling/Commands/AnimalRacing.cs | 3 +-- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/NadekoBot/Classes/NadekoStats.cs b/NadekoBot/Classes/NadekoStats.cs index 63be1f22..3ccbc272 100644 --- a/NadekoBot/Classes/NadekoStats.cs +++ b/NadekoBot/Classes/NadekoStats.cs @@ -46,11 +46,20 @@ namespace NadekoBot commandService.CommandExecuted += StatsCollector_RanCommand; commandService.CommandFinished += CommandService_CommandFinished; - File.Create("errors.txt"); - commandService.CommandErrored += (s, e) => File.AppendAllText("errors.txt", $@"Command: {e.Command} + commandService.CommandErrored += (s, e) => + { + try + { + if (e.ErrorType == CommandErrorType.Exception) + File.AppendAllText("errors.txt", $@"Command: {e.Command} {e.Exception} ------------------------------------- "); + } + catch { + Console.WriteLine("Command errored errorring"); + } + }; Task.Run(StartCollecting); diff --git a/NadekoBot/Modules/Gambling/Commands/AnimalRacing.cs b/NadekoBot/Modules/Gambling/Commands/AnimalRacing.cs index d24ba811..ae6c6819 100644 --- a/NadekoBot/Modules/Gambling/Commands/AnimalRacing.cs +++ b/NadekoBot/Modules/Gambling/Commands/AnimalRacing.cs @@ -194,9 +194,8 @@ namespace NadekoBot.Modules.Gambling.Commands private void Client_MessageReceived(object sender, MessageEventArgs e) { - if (e.Message.IsAuthor) + if (e.Message.IsAuthor || e.Channel.IsPrivate || e.Channel != raceChannel) return; - Console.WriteLine("Message received " + messagesSinceGameStarted); messagesSinceGameStarted++; } From 961a42a8c304b643c0eac9e7866b8787a579ee64 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Tue, 26 Jul 2016 14:10:19 +0200 Subject: [PATCH 28/33] clash of clans saving started, needs more work --- .../Modules/ClashOfClans/ClashOfClans.cs | 116 +++++----- .../ClashOfClans/ClashOfClansModule.cs | 216 +++++++++++++----- 2 files changed, 212 insertions(+), 120 deletions(-) diff --git a/NadekoBot/Modules/ClashOfClans/ClashOfClans.cs b/NadekoBot/Modules/ClashOfClans/ClashOfClans.cs index 668afd73..b3a59feb 100644 --- a/NadekoBot/Modules/ClashOfClans/ClashOfClans.cs +++ b/NadekoBot/Modules/ClashOfClans/ClashOfClans.cs @@ -4,6 +4,9 @@ using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; +using System.Collections.Generic; +using Newtonsoft.Json; +//using Manatee.Json.Serialization; namespace NadekoBot.Classes.ClashOfClans { @@ -11,13 +14,14 @@ namespace NadekoBot.Classes.ClashOfClans { One, Two, Three } + [System.Serializable] internal class Caller { public string CallUser { get; } - public DateTime TimeAdded { get; private set; } + public DateTime TimeAdded { get; set; } - public bool BaseDestroyed { get; internal set; } + public bool BaseDestroyed { get; set; } public int Stars { get; set; } = 3; @@ -47,94 +51,76 @@ namespace NadekoBot.Classes.ClashOfClans public string EnemyClan { get; } public int Size { get; } - private Caller[] bases { get; } - private CancellationTokenSource[] baseCancelTokens; - private CancellationTokenSource endTokenSource { get; } = new CancellationTokenSource(); - public event Action OnUserTimeExpired = delegate { }; - public event Action OnWarEnded = delegate { }; + public Caller[] Bases { get; } public bool Started { get; set; } = false; + public DateTime StartedAt { get; private set; } + public bool Ended { get; private set; } = false; - public ClashWar(string enemyClan, int size, CommandEventArgs e) + public ulong ServerId { get; set; } + public ulong ChannelId { get; set; } + + [JsonIgnore] + public Discord.Channel Channel { get; internal set; } + + /// + /// This init is purely for the deserialization + /// + public ClashWar() { } + + public ClashWar(string enemyClan, int size, ulong serverId, ulong channelId) { this.EnemyClan = enemyClan; this.Size = size; - this.bases = new Caller[size]; - this.baseCancelTokens = new CancellationTokenSource[size]; + this.Bases = new Caller[size]; + this.ServerId = serverId; + this.ChannelId = channelId; + this.Channel = NadekoBot.Client.Servers.FirstOrDefault(s => s.Id == serverId)?.TextChannels.FirstOrDefault(c => c.Id == channelId); } internal void End() { - if (endTokenSource.Token.IsCancellationRequested) return; - endTokenSource.Cancel(); - OnWarEnded(); + Ended = true; } internal void Call(string u, int baseNumber) { - if (baseNumber < 0 || baseNumber >= bases.Length) + if (baseNumber < 0 || baseNumber >= Bases.Length) throw new ArgumentException("Invalid base number"); - if (bases[baseNumber] != null) + if (Bases[baseNumber] != null) throw new ArgumentException("That base is already claimed."); - for (var i = 0; i < bases.Length; i++) + for (var i = 0; i < Bases.Length; i++) { - if (bases[i]?.BaseDestroyed == false && bases[i]?.CallUser == u) - throw new ArgumentException($"@{u} You already claimed a base #{i + 1}. You can't claim a new one."); + if (Bases[i]?.BaseDestroyed == false && Bases[i]?.CallUser == u) + throw new ArgumentException($"@{u} You already claimed base #{i + 1}. You can't claim a new one."); } - bases[baseNumber] = new Caller(u.Trim(), DateTime.Now, false); + Bases[baseNumber] = new Caller(u.Trim(), DateTime.Now, false); } - internal async Task Start() + internal void Start() { if (Started) throw new InvalidOperationException(); - try + Started = true; + StartedAt = DateTime.Now; + foreach (var b in Bases.Where(b => b != null)) { - Started = true; - foreach (var b in bases.Where(b => b != null)) - { - b.ResetTime(); - } -#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed - Task.Run(async () => await ClearArray()).ConfigureAwait(false); -#pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed - await Task.Delay(new TimeSpan(24, 0, 0), endTokenSource.Token).ConfigureAwait(false); - } - catch { } - finally - { - End(); + b.ResetTime(); } } + internal int Uncall(string user) { user = user.Trim(); - for (var i = 0; i < bases.Length; i++) + for (var i = 0; i < Bases.Length; i++) { - if (bases[i]?.CallUser != user) continue; - bases[i] = null; + if (Bases[i]?.CallUser != user) continue; + Bases[i] = null; return i; } throw new InvalidOperationException("You are not participating in that war."); } - private async Task ClearArray() - { - while (!endTokenSource.IsCancellationRequested) - { - await Task.Delay(5000).ConfigureAwait(false); - for (var i = 0; i < bases.Length; i++) - { - if (bases[i] == null) continue; - if (!bases[i].BaseDestroyed && DateTime.Now - bases[i].TimeAdded >= callExpire) - { - OnUserTimeExpired(bases[i].CallUser); - bases[i] = null; - } - } - } - } - public string ShortPrint() => $"`{EnemyClan}` ({Size} v {Size})"; @@ -145,22 +131,22 @@ namespace NadekoBot.Classes.ClashOfClans sb.AppendLine($"πŸ”°**WAR AGAINST `{EnemyClan}` ({Size} v {Size}) INFO:**"); if (!Started) sb.AppendLine("`not started`"); - for (var i = 0; i < bases.Length; i++) + for (var i = 0; i < Bases.Length; i++) { - if (bases[i] == null) + if (Bases[i] == null) { sb.AppendLine($"`{i + 1}.` ❌*unclaimed*"); } else { - if (bases[i].BaseDestroyed) + if (Bases[i].BaseDestroyed) { - sb.AppendLine($"`{i + 1}.` βœ… `{bases[i].CallUser}` {new string('⭐', bases[i].Stars)}"); + sb.AppendLine($"`{i + 1}.` βœ… `{Bases[i].CallUser}` {new string('⭐', Bases[i].Stars)}"); } else { - var left = Started ? callExpire - (DateTime.Now - bases[i].TimeAdded) : callExpire; - sb.AppendLine($"`{i + 1}.` βœ… `{bases[i].CallUser}` {left.Hours}h {left.Minutes}m {left.Seconds}s left"); + var left = Started ? callExpire - (DateTime.Now - Bases[i].TimeAdded) : callExpire; + sb.AppendLine($"`{i + 1}.` βœ… `{Bases[i].CallUser}` {left.Hours}h {left.Minutes}m {left.Seconds}s left"); } } @@ -171,11 +157,11 @@ namespace NadekoBot.Classes.ClashOfClans internal int FinishClaim(string user, int stars = 3) { user = user.Trim(); - for (var i = 0; i < bases.Length; i++) + for (var i = 0; i < Bases.Length; i++) { - if (bases[i]?.BaseDestroyed != false || bases[i]?.CallUser != user) continue; - bases[i].BaseDestroyed = true; - bases[i].Stars = stars; + if (Bases[i]?.BaseDestroyed != false || Bases[i]?.CallUser != user) continue; + Bases[i].BaseDestroyed = true; + Bases[i].Stars = stars; return i; } throw new InvalidOperationException($"@{user} You are either not participating in that war, or you already destroyed a base."); diff --git a/NadekoBot/Modules/ClashOfClans/ClashOfClansModule.cs b/NadekoBot/Modules/ClashOfClans/ClashOfClansModule.cs index c5ba20fc..a07fd822 100644 --- a/NadekoBot/Modules/ClashOfClans/ClashOfClansModule.cs +++ b/NadekoBot/Modules/ClashOfClans/ClashOfClansModule.cs @@ -7,7 +7,9 @@ using System.Collections.Generic; using System.Text; using System.Threading.Tasks; using NadekoBot.Modules.Permissions.Classes; -using System.Threading; +using System.Linq; +using System.IO; +using Newtonsoft.Json; namespace NadekoBot.Modules.ClashOfClans { @@ -15,65 +17,166 @@ namespace NadekoBot.Modules.ClashOfClans { public override string Prefix { get; } = NadekoBot.Config.CommandPrefixes.ClashOfClans; - public static ConcurrentDictionary> ClashWars { get; } = new ConcurrentDictionary>(); + public static ConcurrentDictionary> ClashWars { get; set; } = new ConcurrentDictionary>(); + private readonly object writeLock = new object(); + + public ClashOfClansModule() + { + NadekoBot.OnReady += () => Task.Run(async () => + { + if (File.Exists("data/clashofclans/wars.json")) + { + try + { + var content = File.ReadAllText("data/clashofclans/wars.json"); + + var dict = JsonConvert.DeserializeObject>>(content); + + foreach (var cw in dict) + { + cw.Value.ForEach(war => + { + war.Channel = NadekoBot.Client.GetServer(war.ServerId)?.GetChannel(war.ChannelId); + if (war.Channel == null) + { + cw.Value.Remove(war); + } + } + ); + } + //urgh + ClashWars = new ConcurrentDictionary>(dict); + } + catch (Exception e) + { + Console.WriteLine("Could not load coc wars: " + e.Message); + } + + + } + //Can't this be disabled if the modules is disabled too :) + var callExpire = new TimeSpan(2, 0, 0); + var warExpire = new TimeSpan(23, 0, 0); + while (true) + { + try + { + var hash = ClashWars.GetHashCode(); + foreach (var cw in ClashWars) + { + foreach (var war in cw.Value) + { + await CheckWar(callExpire, war); + } + List newVal = new List(); + foreach (var w in cw.Value) + { + if (!w.Ended && (DateTime.Now - w.StartedAt <= warExpire)) + { + newVal.Add(w); + } + } + //var newVal = cw.Value.Where(w => !(w.Ended || DateTime.Now - w.StartedAt >= warExpire)).ToList(); + foreach (var exWar in cw.Value.Except(newVal)) + { + await exWar.Channel.SendMessage($"War against {exWar.EnemyClan} ({exWar.Size}v{exWar.Size})has ended."); + } + ClashWars.AddOrUpdate(cw.Key, newVal, (x, s) => newVal); + if (cw.Value.Count == 0) + { + List obj; + ClashWars.TryRemove(cw.Key, out obj); + } + } + if (hash != ClashWars.GetHashCode()) //something changed + { + Save(); + } + + + } + catch { } + await Task.Delay(5000); + } + }); + } + + private static void Save() + { + try + { + Directory.CreateDirectory("data/clashofclans"); + File.WriteAllText("data/clashofclans/wars.json", JsonConvert.SerializeObject(ClashWars, Formatting.Indented)); + } + catch (Exception e) + { + Console.WriteLine(e.Message); + } + } + + private static async Task CheckWar(TimeSpan callExpire, ClashWar war) + { + var Bases = war.Bases; + for (var i = 0; i < Bases.Length; i++) + { + if (Bases[i] == null) continue; + if (!Bases[i].BaseDestroyed && DateTime.Now - Bases[i].TimeAdded >= callExpire) + { + await war.Channel.SendMessage($"β—πŸ”°**Claim from @{Bases[i].CallUser} for a war against {war.ShortPrint()} has expired.**").ConfigureAwait(false); + Bases[i] = null; + } + } + } + + + + + + + #region commands public override void Install(ModuleManager manager) { manager.CreateCommands("", cgb => { - cgb.AddCheck(PermissionChecker.Instance); + cgb.AddCheck(PermissionChecker.Instance); - 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. |{Prefix}cw 15 The Enemy Clan") - .Parameter("size") - .Parameter("enemy_clan", ParameterType.Unparsed) - .Do(async e => - { - if (!e.User.ServerPermissions.ManageChannels) - return; - List wars; - if (!ClashWars.TryGetValue(e.Server.Id, out wars)) - { - wars = new List(); - if (!ClashWars.TryAdd(e.Server.Id, wars)) - return; - } - var enemyClan = e.GetArg("enemy_clan"); - if (string.IsNullOrWhiteSpace(enemyClan)) - { - return; - } - int size; - if (!int.TryParse(e.GetArg("size"), out size) || size < 10 || size > 50 || size % 5 != 0) - { - await e.Channel.SendMessage("πŸ’’πŸ”° Not a Valid war size").ConfigureAwait(false); - return; - } - var cw = new ClashWar(enemyClan, size, e); + 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. |{Prefix}cw 15 The Enemy Clan") + .Parameter("size") + .Parameter("enemy_clan", ParameterType.Unparsed) + .Do(async e => + { + if (!e.User.ServerPermissions.ManageChannels) + return; + var enemyClan = e.GetArg("enemy_clan"); + if (string.IsNullOrWhiteSpace(enemyClan)) + { + return; + } + int size; + if (!int.TryParse(e.GetArg("size"), out size) || size < 10 || size > 50 || size % 5 != 0) + { + await e.Channel.SendMessage("πŸ’’πŸ”° Not a Valid war size").ConfigureAwait(false); + return; + } + List wars; + if (!ClashWars.TryGetValue(e.Server.Id, out wars)) + { + wars = new List(); + if (!ClashWars.TryAdd(e.Server.Id, wars)) + return; + } + + + var cw = new ClashWar(enemyClan, size, e.Server.Id, e.Channel.Id); //cw.Start(); + wars.Add(cw); - cw.OnUserTimeExpired += async (u) => - { - try - { - await - e.Channel.SendMessage( - $"β—πŸ”°**Claim from @{u} for a war against {cw.ShortPrint()} has expired.**") - .ConfigureAwait(false); - } - catch { } - }; - cw.OnWarEnded += async () => - { - try - { - await e.Channel.SendMessage($"β—πŸ”°**War against {cw.ShortPrint()} ended.**").ConfigureAwait(false); - } - catch { } - }; - await e.Channel.SendMessage($"β—πŸ”°**CREATED CLAN WAR AGAINST {cw.ShortPrint()}**").ConfigureAwait(false); + await e.Channel.SendMessage($"β—πŸ”°**CREATED CLAN WAR AGAINST {cw.ShortPrint()}**").ConfigureAwait(false); + Save(); //war with the index X started. }); @@ -92,13 +195,12 @@ namespace NadekoBot.Modules.ClashOfClans var war = warsInfo.Item1[warsInfo.Item2]; try { - var startTask = war.Start(); + war.Start(); await e.Channel.SendMessage($"πŸ”°**STARTED WAR AGAINST {war.ShortPrint()}**").ConfigureAwait(false); - await startTask.ConfigureAwait(false); } catch { - await e.Channel.SendMessage($"πŸ”°**WAR AGAINST {war.ShortPrint()} IS ALREADY STARTED**").ConfigureAwait(false); + await e.Channel.SendMessage($"πŸ”°**WAR AGAINST {war.ShortPrint()} HAS ALREADY STARTED**").ConfigureAwait(false); } }); @@ -205,7 +307,7 @@ namespace NadekoBot.Modules.ClashOfClans cgb.CreateCommand(Prefix + "unclaim") .Alias(Prefix + "uncall") .Alias(Prefix + "uc") - .Description($"Removes your claim from a certain war. Optional second argument denotes a person in whos place to unclaim | {Prefix}uc [war_number] [optional_other_name]") + .Description($"Removes your claim from a certain war. Optional second argument denotes a person in whose place to unclaim | {Prefix}uc [war_number] [optional_other_name]") .Parameter("number", ParameterType.Required) .Parameter("other_name", ParameterType.Unparsed) .Do(async e => @@ -245,12 +347,16 @@ namespace NadekoBot.Modules.ClashOfClans return; } warsInfo.Item1[warsInfo.Item2].End(); + await e.Channel.SendMessage($"β—πŸ”°**War against {warsInfo.Item1[warsInfo.Item2].ShortPrint()} ended.**").ConfigureAwait(false); var size = warsInfo.Item1[warsInfo.Item2].Size; warsInfo.Item1.RemoveAt(warsInfo.Item2); }); }); + } + #endregion + private async Task FinishClaim(CommandEventArgs e, int stars = 3) { From c3130ba428a93b98e6293a23b2918ec915875bdd Mon Sep 17 00:00:00 2001 From: Kwoth Date: Tue, 26 Jul 2016 14:56:50 +0200 Subject: [PATCH 29/33] commandlist update --- commandlist.md | 178 +++++++++++++++++++++++++------------------------ 1 file changed, 91 insertions(+), 87 deletions(-) diff --git a/commandlist.md b/commandlist.md index 50cb13cc..d055dc88 100644 --- a/commandlist.md +++ b/commandlist.md @@ -2,7 +2,7 @@ ######You can donate on paypal: `nadekodiscordbot@gmail.com` #NadekoBot List Of Commands -Version: `NadekoBot v0.9.6048.2992` +Version: `NadekoBot v0.9.6051.26856` ### Help Command and aliases | Description | Usage ----------------|--------------|------- @@ -57,38 +57,38 @@ Command and aliases | Description | Usage `.listallincidents`, `.lain` | Sends you a file containing all incidents and flags them as read. `.delmsgoncmd` | Toggles the automatic deletion of user's successful command message to prevent chat flood. Server Manager Only. `.restart` | Restarts the bot. Might not work. **Bot Owner Only** -`.setrole`, `.sr` | Sets a role for a given user. | .sr @User Guest -`.removerole`, `.rr` | Removes a role from a given user. | .rr @User Admin +`.setrole`, `.sr` | Sets a role for a given user. | `.sr @User Guest` +`.removerole`, `.rr` | Removes a role from a given user. | `.rr @User Admin` `.renamerole`, `.renr` | Renames a role. Role you are renaming must be lower than bot's highest role. | `.renr "First role" SecondRole` -`.removeallroles`, `.rar` | Removes all roles from a mentioned user. | .rar @User -`.createrole`, `.cr` | Creates a role with a given name. | `.r Awesome Role` -`.rolecolor`, `.rc` | Set a role's color to the hex or 0-255 rgb color value provided. | `.color Admin 255 200 100` or `.color Admin ffba55` -`.ban`, `.b` | Bans a user by id or name with an optional message. | .b "@some Guy" Your behaviour is toxic. -`.softban`, `.sb` | Bans and then unbans a user by id or name with an optional message. | .sb "@some Guy" Your behaviour is toxic. -`.kick`, `.k` | Kicks a mentioned user. -`.mute` | Mutes mentioned user or users. -`.unmute` | Unmutes mentioned user or users. -`.deafen`, `.deaf` | Deafens mentioned user or users -`.undeafen`, `.undef` | Undeafens mentioned user or users -`.delvoichanl`, `.dvch` | Deletes a voice channel with a given name. -`.creatvoichanl`, `.cvch` | Creates a new voice channel with a given name. -`.deltxtchanl`, `.dtch` | Deletes a text channel with a given name. -`.creatxtchanl`, `.ctch` | Creates a new text channel with a given name. +`.removeallroles`, `.rar` | Removes all roles from a mentioned user. | `.rar @User` +`.createrole`, `.cr` | Creates a role with a given name. | `.cr Awesome Role` +`.rolecolor`, `.rc` | Set a role's color to the hex or 0-255 rgb color value provided. | `.rc Admin 255 200 100` or `.rc Admin ffba55` +`.ban`, `.b` | Bans a user by id or name with an optional message. | `.b "@some Guy" Your behaviour is toxic.` +`.softban`, `.sb` | Bans and then unbans a user by id or name with an optional message. | `.sb "@some Guy" Your behaviour is toxic.` +`.kick`, `.k` | Kicks a mentioned user. | `.k "@some Guy" Your behaviour is toxic.` +`.mute` | Mutes mentioned user or users. | `.mute "@Someguy"` or `.mute "@Someguy" "@Someguy"` +`.unmute` | Unmutes mentioned user or users. | `.unmute "@Someguy"` or `.unmute "@Someguy" "@Someguy"` +`.deafen`, `.deaf` | Deafens mentioned user or users | `.deaf "@Someguy"` or `.deaf "@Someguy" "@Someguy"` +`.undeafen`, `.undef` | Undeafens mentioned user or users | `.undef "@Someguy"` or `.undef "@Someguy" "@Someguy"` +`.delvoichanl`, `.dvch` | Deletes a voice channel with a given name. | `.dvch VoiceChannelName` +`.creatvoichanl`, `.cvch` | Creates a new voice channel with a given name. | `.cvch VoiceChannelName` +`.deltxtchanl`, `.dtch` | Deletes a text channel with a given name. | `.dtch TextChannelName` +`.creatxtchanl`, `.ctch` | Creates a new text channel with a given name. | `.ctch TextChannelName` `.settopic`, `.st` | Sets a topic on the current channel. | `.st My new topic` -`.setchanlname`, `.schn` | Changed the name of the current channel. +`.setchanlname`, `.schn` | Changed the name of the current channel.| `.schn NewName` `.heap` | Shows allocated memory - **Bot Owner Only!** `.prune`, `.clr` | `.prune` removes all nadeko's messages in the last 100 messages.`.prune X` removes last X messages from the channel (up to 100)`.prune @Someone` removes all Someone's messages in the last 100 messages.`.prune @Someone X` removes last X 'Someone's' messages in the channel. | `.prune` or `.prune 5` or `.prune @Someone` or `.prune @Someone X` `.die` | Shuts the bot down and notifies users about the restart. **Bot Owner Only!** -`.setname`, `.newnm` | Give the bot a new name. **Bot Owner Only!** +`.setname`, `.newnm` | Give the bot a new name. **Bot Owner Only!** | .newnm BotName `.newavatar`, `.setavatar` | Sets a new avatar image for the NadekoBot. Argument is a direct link to an image. **Bot Owner Only!** | `.setavatar https://i.ytimg.com/vi/WDudkR1eTMM/maxresdefault.jpg` -`.setgame` | Sets the bots game. **Bot Owner Only!** +`.setgame` | Sets the bots game. **Bot Owner Only!** | `.setgame Playing with kwoth` `.send` | Send a message to someone on a different server through the bot. **Bot Owner Only!** | `.send serverid|u:user_id Send this to a user!` or `.send serverid|c:channel_id Send this to a channel!` -`.mentionrole`, `.menro` | Mentions every person from the provided role or roles (separated by a ',') on this server. Requires you to have mention everyone permission. +`.mentionrole`, `.menro` | Mentions every person from the provided role or roles (separated by a ',') on this server. Requires you to have mention everyone permission. | `.menro RoleName` `.unstuck` | Clears the message queue. **Bot Owner Only!** `.donators` | List of lovely people who donated to keep this project alive. -`.donadd` | Add a donator to the database. -`.announce` | Sends a message to all servers' general channel bot is connected to.**Bot Owner Only!** | .announce Useless spam -`.savechat` | Saves a number of messages to a text file and sends it to you. **Bot Owner Only** | `.chatsave 150` +`.donadd` | Add a donator to the database. | `.donadd Donate Amount` +`.announce` | Sends a message to all servers' general channel bot is connected to.**Bot Owner Only!** | `.announce Useless spam` +`.savechat` | Saves a number of messages to a text file and sends it to you. **Bot Owner Only** | `.savechat 150` ### Utility Command and aliases | Description | Usage @@ -98,15 +98,16 @@ Command and aliases | Description | Usage `.serverinfo`, `.sinfo` | Shows info about the server the bot is on. If no channel is supplied, it defaults to current one. | .sinfo Some Server `.channelinfo`, `.cinfo` | Shows info about the channel. If no channel is supplied, it defaults to current one. | .cinfo #some-channel `.userinfo`, `.uinfo` | Shows info about the user. If no user is supplied, it defaults a user running the command. | .uinfo @SomeUser -`.whoplays` | Shows a list of users who are playing the specified game. -`.inrole` | Lists every person from the provided role or roles (separated by a ',') on this server. +`.whoplays` | Shows a list of users who are playing the specified game. | `.whoplays Overwatch` +`.inrole` | Lists every person from the provided role or roles (separated by a ',') on this server. If the list is too long for 1 message, you must have Manage Messages permission. | `.inrole Role` `.checkmyperms` | Checks your userspecific permissions on this channel. `.stats` | Shows some basic stats for Nadeko. `.dysyd` | Shows some basic stats for Nadeko. -`.userid`, `.uid` | Shows user ID. -`.channelid`, `.cid` | Shows current channel ID. -`.serverid`, `.sid` | Shows current server ID. +`.userid`, `.uid` | Shows user ID. | `.uid` or `.uid "@SomeGuy" +`.channelid`, `.cid` | Shows current channel ID. | `.cid` +`.serverid`, `.sid` | Shows current server ID. | `.sid` `.roles` | List all roles on this server or a single user if specified. +`.channeltopic`, `.ct` | Sends current channel's topic as a message. | `.ct` ### Permissions Command and aliases | Description | Usage @@ -122,38 +123,38 @@ Command and aliases | Description | Usage `;rolepermscopy`, `;rpc` | Copies BOT PERMISSIONS (not discord permissions) from one role to another. | `;rpc Some Role ~ Some other role` `;chnlpermscopy`, `;cpc` | Copies BOT PERMISSIONS (not discord permissions) from one channel to another. | `;cpc Some Channel ~ Some other channel` `;usrpermscopy`, `;upc` | Copies BOT PERMISSIONS (not discord permissions) from one role to another. | `;upc @SomeUser ~ @SomeOtherUser` -`;verbose`, `;v` | Sets whether to show when a command/module is blocked. | ;verbose true +`;verbose`, `;v` | Sets whether to show when a command/module is blocked. | `;verbose true` `;srvrperms`, `;sp` | Shows banned permissions for this server. -`;roleperms`, `;rp` | Shows banned permissions for a certain role. No argument means for everyone. | ;rp AwesomeRole -`;chnlperms`, `;cp` | Shows banned permissions for a certain channel. No argument means for this channel. | ;cp #dev -`;userperms`, `;up` | Shows banned permissions for a certain user. No argument means for yourself. | ;up Kwoth -`;srvrmdl`, `;sm` | Sets a module's permission at the server level. | ;sm "module name" enable -`;srvrcmd`, `;sc` | Sets a command's permission at the server level. | ;sc "command name" disable -`;rolemdl`, `;rm` | Sets a module's permission at the role level. | ;rm "module name" enable MyRole -`;rolecmd`, `;rc` | Sets a command's permission at the role level. | ;rc "command name" disable MyRole -`;chnlmdl`, `;cm` | Sets a module's permission at the channel level. | ;cm "module name" enable SomeChannel -`;chnlcmd`, `;cc` | Sets a command's permission at the channel level. | ;cc "command name" enable SomeChannel -`;usrmdl`, `;um` | Sets a module's permission at the user level. | ;um "module name" enable SomeUsername -`;usrcmd`, `;uc` | Sets a command's permission at the user level. | ;uc "command name" enable SomeUsername -`;allsrvrmdls`, `;asm` | Sets permissions for all modules at the server level. | ;asm [enable/disable] -`;allsrvrcmds`, `;asc` | Sets permissions for all commands from a certain module at the server level. | ;asc "module name" [enable/disable] -`;allchnlmdls`, `;acm` | Sets permissions for all modules at the channel level. | ;acm [enable/disable] SomeChannel -`;allchnlcmds`, `;acc` | Sets permissions for all commands from a certain module at the channel level. | ;acc "module name" [enable/disable] SomeChannel -`;allrolemdls`, `;arm` | Sets permissions for all modules at the role level. | ;arm [enable/disable] MyRole -`;allrolecmds`, `;arc` | Sets permissions for all commands from a certain module at the role level. | ;arc "module name" [enable/disable] MyRole -`;ubl` | Blacklists a mentioned user. | ;ubl [user_mention] -`;uubl` | Unblacklists a mentioned user. | ;uubl [user_mention] -`;cbl` | Blacklists a mentioned channel (#general for example). | ;cbl #some_channel -`;cubl` | Unblacklists a mentioned channel (#general for example). | ;cubl #some_channel -`;sbl` | Blacklists a server by a name or id (#general for example). **BOT OWNER ONLY** | ;sbl [servername/serverid] +`;roleperms`, `;rp` | Shows banned permissions for a certain role. No argument means for everyone. | `;rp AwesomeRole` +`;chnlperms`, `;cp` | Shows banned permissions for a certain channel. No argument means for this channel. | `;cp #dev` +`;userperms`, `;up` | Shows banned permissions for a certain user. No argument means for yourself. | `;up Kwoth` +`;srvrmdl`, `;sm` | Sets a module's permission at the server level. | `;sm "module name" enable` +`;srvrcmd`, `;sc` | Sets a command's permission at the server level. | `;sc "command name" disable` +`;rolemdl`, `;rm` | Sets a module's permission at the role level. | `;rm "module name" enable MyRole` +`;rolecmd`, `;rc` | Sets a command's permission at the role level. | `;rc "command name" disable MyRole` +`;chnlmdl`, `;cm` | Sets a module's permission at the channel level. | `;cm "module name" enable SomeChannel` +`;chnlcmd`, `;cc` | Sets a command's permission at the channel level. | `;cc "command name" enable SomeChannel` +`;usrmdl`, `;um` | Sets a module's permission at the user level. | `;um "module name" enable SomeUsername` +`;usrcmd`, `;uc` | Sets a command's permission at the user level. | `;uc "command name" enable SomeUsername` +`;allsrvrmdls`, `;asm` | Sets permissions for all modules at the server level. | `;asm [enable/disable]` +`;allsrvrcmds`, `;asc` | Sets permissions for all commands from a certain module at the server level. | `;asc "module name" [enable/disable]` +`;allchnlmdls`, `;acm` | Sets permissions for all modules at the channel level. | `;acm [enable/disable] SomeChannel` +`;allchnlcmds`, `;acc` | Sets permissions for all commands from a certain module at the channel level. | `;acc "module name" [enable/disable] SomeChannel` +`;allrolemdls`, `;arm` | Sets permissions for all modules at the role level. | `;arm [enable/disable] MyRole` +`;allrolecmds`, `;arc` | Sets permissions for all commands from a certain module at the role level. | `;arc "module name" [enable/disable] MyRole` +`;ubl` | Blacklists a mentioned user. | `;ubl [user_mention]` +`;uubl` | Unblacklists a mentioned user. | `;uubl [user_mention]` +`;cbl` | Blacklists a mentioned channel (#general for example). | `;cbl #some_channel` +`;cubl` | Unblacklists a mentioned channel (#general for example). | `;cubl #some_channel` +`;sbl` | Blacklists a server by a name or id (#general for example). **BOT OWNER ONLY** | `;sbl [servername/serverid]` `;cmdcooldown`, `;cmdcd` | Sets a cooldown per user for a command. Set 0 to clear. | `;cmdcd "some cmd" 5` `;allcmdcooldowns`, `;acmdcds` | Shows a list of all commands and their respective cooldowns. ### Conversations Command and aliases | Description | Usage ----------------|--------------|------- -`..` | Adds a new quote with the specified name (single word) and message (no limit). | .. abc My message -`...` | Shows a random quote with a specified name. | .. abc +`..` | Adds a new quote with the specified name (single word) and message (no limit). | `.. abc My message` +`...` | Shows a random quote with a specified name. | `... abc` `..qdel`, `..quotedelete` | Deletes all quotes with the specified keyword. You have to either be bot owner or the creator of the quote to delete it. | `..qdel abc` `@BotName rip` | Shows a grave image of someone with a start year | @NadekoBot rip @Someone 2000 `@BotName die` | Works only for the owner. Shuts the bot down. @@ -173,11 +174,13 @@ Command and aliases | Description | Usage `$roll` | Rolls 0-100. If you supply a number [x] it rolls up to 30 normal dice. If you split 2 numbers with letter d (xdy) it will roll x dice from 1 to y. | $roll or $roll 7 or $roll 3d5 `$rolluo` | Rolls 0-100. If you supply a number [x] it rolls up to 30 normal dice (unordered). If you split 2 numbers with letter d (xdy) it will roll x dice from 1 to y. | $roll or $roll 7 or $roll 3d5 `$nroll` | Rolls in a given range. | `$nroll 5` (rolls 0-5) or `$nroll 5-15` -`$raffle` | Prints a name and ID of a random user from the online list from the (optional) role. +`$race` | Starts a new animal race. +`$joinrace`, `$jr` | Joins a new race. You can specify an amount of flowers for betting (optional). You will get YourBet*(participants-1) back if you win. | `$jr` or `$jr 5` +`$raffle` | Prints a name and ID of a random user from the online list from the (optional) role. | `$raffle` or `$raffle RoleName `$$$` | Check how much NadekoFlowers a person has. (Defaults to yourself) | `$$$` or `$$$ @Someone` `$give` | Give someone a certain amount of NadekoFlowers `$award` | Gives someone a certain amount of flowers. **Bot Owner Only!** | `$award 100 @person` -`$take` | Takes a certain amount of flowers from someone. **Bot Owner Only!** +`$take` | Takes a certain amount of flowers from someone. **Bot Owner Only!** | `$take 1 "@someguy"` `$betroll`, `$br` | Bets a certain amount of NadekoFlowers and rolls a dice. Rolling over 66 yields x2 flowers, over 90 - x3 and 100 x10. | $br 5 `$leaderboard`, `$lb` | @@ -196,9 +199,9 @@ Command and aliases | Description | Usage `>plant` | Spend a flower to plant it in this channel. (If bot is restarted or crashes, flower will be lost) `>gencurrency`, `>gc` | Toggles currency generation on this channel. Every posted message will have 2% chance to spawn a NadekoFlower. Optional parameter cooldown time in minutes, 5 minutes by default. Requires Manage Messages permission. | `>gc` or `>gc 60` `>leet` | Converts a text to leetspeak with 6 (1-6) severity levels | >leet 3 Hello -`>choose` | Chooses a thing from a list of things | >choose Get up;Sleep;Sleep more -`>8ball` | Ask the 8ball a yes/no question. -`>rps` | Play a game of rocket paperclip scissors with Nadeko. | >rps scissors +`>choose` | Chooses a thing from a list of things | `>choose Get up;Sleep;Sleep more` +`>8ball` | Ask the 8ball a yes/no question. | `>8ball should i do something` +`>rps` | Play a game of rocket paperclip scissors with Nadeko. | `>rps scissors` `>linux` | Prints a customizable Linux interjection | `>linux Spyware Windows` ### Music @@ -234,7 +237,7 @@ Command and aliases | Description | Usage `!!load` | Loads a playlist under a certain name. | `!!load classical-1` `!!playlists`, `!!pls` | Lists all playlists. Paginated. 20 per page. Default page is 0. | `!!pls 1` `!!deleteplaylist`, `!!delpls` | Deletes a saved playlist. Only if you made it or if you are the bot owner. | `!!delpls animu-5` -`!!goto` | Goes to a specific time in seconds in a song. +`!!goto` | Goes to a specific time in seconds in a song. | !!goto 30 `!!getlink`, `!!gl` | Shows a link to the currently playing song. `!!autoplay`, `!!ap` | Toggles autoplay - When the song is finished, automatically queue a related youtube song. (Works only for youtube songs and when queue is empty) @@ -262,41 +265,42 @@ Command and aliases | Description | Usage `~pokemonability`, `~pokeab` | Searches for a pokemon ability. `~memelist` | Pulls a list of memes you can use with `~memegen` from http://memegen.link/templates/ `~memegen` | Generates a meme from memelist with top and bottom text. | `~memegen biw "gets iced coffee" "in the winter"` -`~we` | Shows weather data for a specified city and a country. BOTH ARE REQUIRED. Use country abbrevations. | ~we Moscow RF -`~yt` | Searches youtubes and shows the first result +`~we` | Shows weather data for a specified city and a country. BOTH ARE REQUIRED. Use country abbrevations. | `~we Moscow RF` +`~yt` | Searches youtubes and shows the first result | `~yt query` `~ani`, `~anime`, `~aq` | Queries anilist for an anime and shows the first result. -`~imdb` | Queries imdb for movies or series, show first result. -`~mang`, `~manga`, `~mq` | Queries anilist for a manga and shows the first result. +`~imdb` | Queries imdb for movies or series, show first result. | `~imdb query` +`~mang`, `~manga`, `~mq` | Queries anilist for a manga and shows the first result. | `~mq query` `~randomcat`, `~meow` | Shows a random cat image. -`~i` | Pulls the first image found using a search parameter. Use ~ir for different results. | ~i cute kitten -`~ir` | Pulls a random image using a search parameter. | ~ir cute kitten -`~lmgtfy` | Google something for an idiot. -`~google`, `~g` | Get a google search link for some terms. -`~hs` | Searches for a Hearthstone card and shows its image. Takes a while to complete. | ~hs Ysera -`~ud` | Searches Urban Dictionary for a word. | ~ud Pineapple -`~#` | Searches Tagdef.com for a hashtag. | ~# ff +`~randomdog`, `~woof` | Shows a random dog image. +`~i` | Pulls the first image found using a search parameter. Use ~ir for different results. | `~i cute kitten` +`~ir` | Pulls a random image using a search parameter. | `~ir cute kitten` +`~lmgtfy` | Google something for an idiot. | `~lmgtfy query` +`~google`, `~g` | Get a google search link for some terms. | `~google query` +`~hs` | Searches for a Hearthstone card and shows its image. Takes a while to complete. | `~hs Ysera` +`~ud` | Searches Urban Dictionary for a word. | `~ud Pineapple` +`~#` | Searches Tagdef.com for a hashtag. | `~# ff` `~quote` | Shows a random quote. `~catfact` | Shows a random catfact from `~yomama`, `~ym` | Shows a random joke from `~randjoke`, `~rj` | Shows a random joke from `~chucknorris`, `~cn` | Shows a random chucknorris joke from `~magicitem`, `~mi` | Shows a random magicitem from -`~revav` | Returns a google reverse image search for someone's avatar. -`~revimg` | Returns a google reverse image search for an image from a link. -`~safebooru` | Shows a random image from safebooru with a given tag. Tag is optional but preffered. (multiple tags are appended with +) | ~safebooru yuri+kissing -`~wiki` | Gives you back a wikipedia link +`~revav` | Returns a google reverse image search for someone's avatar. | `~revav "@SomeGuy" +`~revimg` | Returns a google reverse image search for an image from a link. | `~revav Image link` +`~safebooru` | Shows a random image from safebooru with a given tag. Tag is optional but preffered. (multiple tags are appended with +) | `~safebooru yuri+kissing` +`~wiki` | Gives you back a wikipedia link | `~wiki query` `~clr` | Shows you what color corresponds to that hex. | `~clr 00ff00` -`~videocall` | Creates a private video call link for you and other mentioned people. The link is sent to mentioned people via a private message. -`~av`, `~avatar` | Shows a mentioned person's avatar. | ~av @X +`~videocall` | Creates a private video call link for you and other mentioned people. The link is sent to mentioned people via a private message. | `~videocall "@SomeGuy"` +`~av`, `~avatar` | Shows a mentioned person's avatar. | `~av @X` ### NSFW Command and aliases | Description | Usage ----------------|--------------|------- -`~hentai` | Shows a random NSFW hentai image from gelbooru and danbooru with a given tag. Tag is optional but preffered. (multiple tags are appended with +) | ~hentai yuri+kissing -`~danbooru` | Shows a random hentai image from danbooru with a given tag. Tag is optional but preffered. (multiple tags are appended with +) | ~danbooru yuri+kissing -`~gelbooru` | Shows a random hentai image from gelbooru with a given tag. Tag is optional but preffered. (multiple tags are appended with +) | ~gelbooru yuri+kissing -`~rule34` | Shows a random image from rule34.xx with a given tag. Tag is optional but preffered. (multiple tags are appended with +) | ~rule34 yuri+kissing -`~e621` | Shows a random hentai image from e621.net with a given tag. Tag is optional but preffered. Use spaces for multiple tags. | ~e621 yuri kissing +`~hentai` | Shows a random NSFW hentai image from gelbooru and danbooru with a given tag. Tag is optional but preffered. (multiple tags are appended with +) | `~hentai yuri+kissing` +`~danbooru` | Shows a random hentai image from danbooru with a given tag. Tag is optional but preffered. (multiple tags are appended with +) | `~danbooru yuri+kissing` +`~gelbooru` | Shows a random hentai image from gelbooru with a given tag. Tag is optional but preffered. (multiple tags are appended with +) | `~gelbooru yuri+kissing` +`~rule34` | Shows a random image from rule34.xx with a given tag. Tag is optional but preffered. (multiple tags are appended with +) | `~rule34 yuri+kissing` +`~e621` | Shows a random hentai image from e621.net with a given tag. Tag is optional but preffered. Use spaces for multiple tags. | `~e621 yuri kissing` `~cp` | We all know where this will lead you to. `~boobs` | Real adult content. `~butts`, `~ass`, `~butt` | Real adult content. @@ -305,13 +309,13 @@ Command and aliases | Description | Usage Command and aliases | Description | Usage ----------------|--------------|------- `,createwar`, `,cw` | Creates a new war by specifying a size (>10 and multiple of 5) and enemy clan name. | ,cw 15 The Enemy Clan -`,startwar`, `,sw` | Starts a war with a given number. +`,startwar`, `,sw` | Starts a war with a given number. | `,sw 1` `,listwar`, `,lw` | Shows the active war claims by a number. Shows all wars in a short way if no number is specified. | ,lw [war_number] or ,lw `,claim`, `,call`, `,c` | Claims a certain base from a certain war. You can supply a name in the third optional argument to claim in someone else's place. | ,call [war_number] [base_number] [optional_other_name] `,claimfinish`, `,cf`, `,cf3`, `,claimfinish3` | Finish your claim with 3 stars if you destroyed a base. Optional second argument finishes for someone else. | ,cf [war_number] [optional_other_name] `,claimfinish2`, `,cf2` | Finish your claim with 2 stars if you destroyed a base. Optional second argument finishes for someone else. | ,cf [war_number] [optional_other_name] `,claimfinish1`, `,cf1` | Finish your claim with 1 stars if you destroyed a base. Optional second argument finishes for someone else. | ,cf [war_number] [optional_other_name] -`,unclaim`, `,uncall`, `,uc` | Removes your claim from a certain war. Optional second argument denotes a person in whos place to unclaim | ,uc [war_number] [optional_other_name] +`,unclaim`, `,uncall`, `,uc` | Removes your claim from a certain war. Optional second argument denotes a person in whose place to unclaim | ,uc [war_number] [optional_other_name] `,endwar`, `,ew` | Ends the war with a given index. | ,ew [war_number] ### Pokegame @@ -326,7 +330,7 @@ Command and aliases | Description | Usage ### Translator Command and aliases | Description | Usage ----------------|--------------|------- -`~translate`, `~trans` | Translates from>to text. From the given language to the destiation language. | ~trans en>fr Hello +`~translate`, `~trans` | Translates from>to text. From the given language to the destiation language. | `~trans en>fr Hello` `~translangs` | List the valid languages for translation. ### Customreactions @@ -352,7 +356,7 @@ Command and aliases | Description | Usage ### Trello Command and aliases | Description | Usage ----------------|--------------|------- -`trello bind` | Bind a trello bot to a single channel. You will receive notifications from your board when something is added or edited. | bind [board_id] +`trello bind` | Bind a trello bot to a single channel. You will receive notifications from your board when something is added or edited. | `trello bind [board_id]` `trello unbind` | Unbinds a bot from the channel and board. `trello lists`, `trello list` | Lists all lists yo ;) -`trello cards` | Lists all cards from the supplied list. You can supply either a name or an index. +`trello cards` | Lists all cards from the supplied list. You can supply either a name or an index. | `trello cards index` From 135c00823d1cca7c0c6ec143c3b7fad38be337ec Mon Sep 17 00:00:00 2001 From: Kwoth Date: Tue, 26 Jul 2016 16:05:08 +0200 Subject: [PATCH 30/33] more work on clashofclans by Dragon --- .../Modules/ClashOfClans/ClashOfClans.cs | 38 ++++++++++++------- .../ClashOfClans/ClashOfClansModule.cs | 25 +++++++++--- 2 files changed, 44 insertions(+), 19 deletions(-) diff --git a/NadekoBot/Modules/ClashOfClans/ClashOfClans.cs b/NadekoBot/Modules/ClashOfClans/ClashOfClans.cs index b3a59feb..6576ce3d 100644 --- a/NadekoBot/Modules/ClashOfClans/ClashOfClans.cs +++ b/NadekoBot/Modules/ClashOfClans/ClashOfClans.cs @@ -14,10 +14,14 @@ namespace NadekoBot.Classes.ClashOfClans { One, Two, Three } + public enum WarState + { + Started, Ended, Created + } [System.Serializable] internal class Caller { - public string CallUser { get; } + public string CallUser { get; set; } public DateTime TimeAdded { get; set; } @@ -25,6 +29,7 @@ namespace NadekoBot.Classes.ClashOfClans public int Stars { get; set; } = 3; + public Caller() { } public Caller(string callUser, DateTime timeAdded, bool baseDestroyed) { @@ -48,17 +53,18 @@ namespace NadekoBot.Classes.ClashOfClans { private static TimeSpan callExpire => new TimeSpan(2, 0, 0); - public string EnemyClan { get; } - public int Size { get; } + public string EnemyClan { get; set; } + public int Size { get; set; } - public Caller[] Bases { get; } - public bool Started { get; set; } = false; + public Caller[] Bases { get; set; } + public WarState WarState { get; set; } = WarState.Created; + //public bool Started { get; set; } = false; public DateTime StartedAt { get; private set; } - public bool Ended { get; private set; } = false; + //public bool Ended { get; private set; } = false; public ulong ServerId { get; set; } public ulong ChannelId { get; set; } - + [JsonIgnore] public Discord.Channel Channel { get; internal set; } @@ -74,12 +80,13 @@ namespace NadekoBot.Classes.ClashOfClans this.Bases = new Caller[size]; this.ServerId = serverId; this.ChannelId = channelId; - this.Channel = NadekoBot.Client.Servers.FirstOrDefault(s => s.Id == serverId)?.TextChannels.FirstOrDefault(c => c.Id == channelId); + this.Channel = NadekoBot.Client.Servers.FirstOrDefault(s=>s.Id == serverId)?.TextChannels.FirstOrDefault(c => c.Id == channelId); } internal void End() { - Ended = true; + //Ended = true; + WarState = WarState.Ended; } internal void Call(string u, int baseNumber) @@ -99,9 +106,12 @@ namespace NadekoBot.Classes.ClashOfClans internal void Start() { - if (Started) - throw new InvalidOperationException(); - Started = true; + if (WarState == WarState.Started) + throw new InvalidOperationException("War already started"); + //if (Started) + // throw new InvalidOperationException(); + //Started = true; + WarState = WarState.Started; StartedAt = DateTime.Now; foreach (var b in Bases.Where(b => b != null)) { @@ -129,7 +139,7 @@ namespace NadekoBot.Classes.ClashOfClans var sb = new StringBuilder(); sb.AppendLine($"πŸ”°**WAR AGAINST `{EnemyClan}` ({Size} v {Size}) INFO:**"); - if (!Started) + if (WarState == WarState.Created) sb.AppendLine("`not started`"); for (var i = 0; i < Bases.Length; i++) { @@ -145,7 +155,7 @@ namespace NadekoBot.Classes.ClashOfClans } else { - var left = Started ? callExpire - (DateTime.Now - Bases[i].TimeAdded) : callExpire; + var left =(WarState == WarState.Started) ? callExpire - (DateTime.Now - Bases[i].TimeAdded) : callExpire; sb.AppendLine($"`{i + 1}.` βœ… `{Bases[i].CallUser}` {left.Hours}h {left.Minutes}m {left.Seconds}s left"); } } diff --git a/NadekoBot/Modules/ClashOfClans/ClashOfClansModule.cs b/NadekoBot/Modules/ClashOfClans/ClashOfClansModule.cs index ef9defd1..69ed1163 100644 --- a/NadekoBot/Modules/ClashOfClans/ClashOfClansModule.cs +++ b/NadekoBot/Modules/ClashOfClans/ClashOfClansModule.cs @@ -58,6 +58,7 @@ namespace NadekoBot.Modules.ClashOfClans //Can't this be disabled if the modules is disabled too :) var callExpire = new TimeSpan(2, 0, 0); var warExpire = new TimeSpan(23, 0, 0); + bool changed = false; while (true) { try @@ -72,21 +73,29 @@ namespace NadekoBot.Modules.ClashOfClans List newVal = new List(); foreach (var w in cw.Value) { - if (!w.Ended && (DateTime.Now - w.StartedAt <= warExpire)) + //We add when A: the war is not ended + if (w.WarState != WarState.Ended) { + //and B: the war has not expired + if ((w.WarState == WarState.Started && DateTime.Now - w.StartedAt <= warExpire) || w.WarState == WarState.Created) + { newVal.Add(w); + } } } //var newVal = cw.Value.Where(w => !(w.Ended || DateTime.Now - w.StartedAt >= warExpire)).ToList(); foreach (var exWar in cw.Value.Except(newVal)) { - await exWar.Channel.SendMessage($"War against {exWar.EnemyClan} ({exWar.Size}v{exWar.Size})has ended."); + await exWar.Channel.SendMessage($"War against {exWar.EnemyClan} has ended"); } - ClashWars.AddOrUpdate(cw.Key, newVal, (x, s) => newVal); - if (cw.Value.Count == 0) + + if (newVal.Count == 0) { List obj; ClashWars.TryRemove(cw.Key, out obj); + } else + { + ClashWars.AddOrUpdate(cw.Key, newVal, (x, s) => newVal); } } if (hash != ClashWars.GetHashCode()) //something changed @@ -182,7 +191,7 @@ namespace NadekoBot.Modules.ClashOfClans cgb.CreateCommand(Prefix + "startwar") .Alias(Prefix + "sw") - .Description($"Starts a war with a given number. | `{Prefix}sw 1`") + .Description("Starts a war with a given number.") .Parameter("number", ParameterType.Required) .Do(async e => { @@ -202,6 +211,7 @@ namespace NadekoBot.Modules.ClashOfClans { await e.Channel.SendMessage($"πŸ”°**WAR AGAINST {war.ShortPrint()} HAS ALREADY STARTED**").ConfigureAwait(false); } + Save(); }); cgb.CreateCommand(Prefix + "listwar") @@ -233,6 +243,7 @@ namespace NadekoBot.Modules.ClashOfClans } await e.Channel.SendMessage(sb.ToString()).ConfigureAwait(false); return; + } //if number is not null, print the war needed var warsInfo = GetInfo(e); @@ -274,6 +285,7 @@ namespace NadekoBot.Modules.ClashOfClans var war = warsInfo.Item1[warsInfo.Item2]; war.Call(usr, baseNum - 1); await e.Channel.SendMessage($"πŸ”°**{usr}** claimed a base #{baseNum} for a war against {war.ShortPrint()}").ConfigureAwait(false); + Save(); } catch (Exception ex) { @@ -327,6 +339,7 @@ namespace NadekoBot.Modules.ClashOfClans var war = warsInfo.Item1[warsInfo.Item2]; var baseNumber = war.Uncall(usr); await e.Channel.SendMessage($"πŸ”° @{usr} has **UNCLAIMED** a base #{baseNumber + 1} from a war against {war.ShortPrint()}").ConfigureAwait(false); + Save(); } catch (Exception ex) { @@ -351,6 +364,7 @@ namespace NadekoBot.Modules.ClashOfClans var size = warsInfo.Item1[warsInfo.Item2].Size; warsInfo.Item1.RemoveAt(warsInfo.Item2); + Save(); }); }); @@ -376,6 +390,7 @@ namespace NadekoBot.Modules.ClashOfClans { var baseNum = war.FinishClaim(usr, stars); await e.Channel.SendMessage($"β—πŸ”°{e.User.Mention} **DESTROYED** a base #{baseNum + 1} in a war against {war.ShortPrint()}").ConfigureAwait(false); + Save(); } catch (Exception ex) { From 7147d2f0b1ac815c632ea812ed0fe9354831b7a7 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Wed, 27 Jul 2016 03:03:27 +0200 Subject: [PATCH 31/33] More CoC --- NadekoBot/Modules/ClashOfClans/ClashOfClans.cs | 10 +++++----- NadekoBot/Modules/ClashOfClans/ClashOfClansModule.cs | 9 ++++----- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/NadekoBot/Modules/ClashOfClans/ClashOfClans.cs b/NadekoBot/Modules/ClashOfClans/ClashOfClans.cs index 6576ce3d..de3e3079 100644 --- a/NadekoBot/Modules/ClashOfClans/ClashOfClans.cs +++ b/NadekoBot/Modules/ClashOfClans/ClashOfClans.cs @@ -40,7 +40,7 @@ namespace NadekoBot.Classes.ClashOfClans public void ResetTime() { - TimeAdded = DateTime.Now; + TimeAdded = DateTime.UtcNow; } public void Destroy() @@ -59,7 +59,7 @@ namespace NadekoBot.Classes.ClashOfClans public Caller[] Bases { get; set; } public WarState WarState { get; set; } = WarState.Created; //public bool Started { get; set; } = false; - public DateTime StartedAt { get; private set; } + public DateTime StartedAt { get; set; } //public bool Ended { get; private set; } = false; public ulong ServerId { get; set; } @@ -101,7 +101,7 @@ namespace NadekoBot.Classes.ClashOfClans throw new ArgumentException($"@{u} You already claimed base #{i + 1}. You can't claim a new one."); } - Bases[baseNumber] = new Caller(u.Trim(), DateTime.Now, false); + Bases[baseNumber] = new Caller(u.Trim(), DateTime.UtcNow, false); } internal void Start() @@ -112,7 +112,7 @@ namespace NadekoBot.Classes.ClashOfClans // throw new InvalidOperationException(); //Started = true; WarState = WarState.Started; - StartedAt = DateTime.Now; + StartedAt = DateTime.UtcNow; foreach (var b in Bases.Where(b => b != null)) { b.ResetTime(); @@ -155,7 +155,7 @@ namespace NadekoBot.Classes.ClashOfClans } else { - var left =(WarState == WarState.Started) ? callExpire - (DateTime.Now - Bases[i].TimeAdded) : callExpire; + var left =(WarState == WarState.Started) ? callExpire - (DateTime.UtcNow - Bases[i].TimeAdded) : callExpire; sb.AppendLine($"`{i + 1}.` βœ… `{Bases[i].CallUser}` {left.Hours}h {left.Minutes}m {left.Seconds}s left"); } } diff --git a/NadekoBot/Modules/ClashOfClans/ClashOfClansModule.cs b/NadekoBot/Modules/ClashOfClans/ClashOfClansModule.cs index 69ed1163..847d97f4 100644 --- a/NadekoBot/Modules/ClashOfClans/ClashOfClansModule.cs +++ b/NadekoBot/Modules/ClashOfClans/ClashOfClansModule.cs @@ -58,7 +58,6 @@ namespace NadekoBot.Modules.ClashOfClans //Can't this be disabled if the modules is disabled too :) var callExpire = new TimeSpan(2, 0, 0); var warExpire = new TimeSpan(23, 0, 0); - bool changed = false; while (true) { try @@ -77,16 +76,16 @@ namespace NadekoBot.Modules.ClashOfClans if (w.WarState != WarState.Ended) { //and B: the war has not expired - if ((w.WarState == WarState.Started && DateTime.Now - w.StartedAt <= warExpire) || w.WarState == WarState.Created) + if ((w.WarState == WarState.Started && DateTime.UtcNow - w.StartedAt <= warExpire) || w.WarState == WarState.Created) { newVal.Add(w); } } } - //var newVal = cw.Value.Where(w => !(w.Ended || DateTime.Now - w.StartedAt >= warExpire)).ToList(); + //var newVal = cw.Value.Where(w => !(w.Ended || DateTime.UtcNow - w.StartedAt >= warExpire)).ToList(); foreach (var exWar in cw.Value.Except(newVal)) { - await exWar.Channel.SendMessage($"War against {exWar.EnemyClan} has ended"); + await exWar.Channel.SendMessage($"War against {exWar.EnemyClan} ({exWar.Size}v{exWar.Size}) has ended"); } if (newVal.Count == 0) @@ -130,7 +129,7 @@ namespace NadekoBot.Modules.ClashOfClans for (var i = 0; i < Bases.Length; i++) { if (Bases[i] == null) continue; - if (!Bases[i].BaseDestroyed && DateTime.Now - Bases[i].TimeAdded >= callExpire) + if (!Bases[i].BaseDestroyed && DateTime.UtcNow - Bases[i].TimeAdded >= callExpire) { await war.Channel.SendMessage($"β—πŸ”°**Claim from @{Bases[i].CallUser} for a war against {war.ShortPrint()} has expired.**").ConfigureAwait(false); Bases[i] = null; From f025a5a06286e0041ec44d2ec16a551e82792cec Mon Sep 17 00:00:00 2001 From: Kwoth Date: Wed, 27 Jul 2016 03:10:36 +0200 Subject: [PATCH 32/33] format --- .../Modules/ClashOfClans/ClashOfClans.cs | 12 +++----- .../ClashOfClans/ClashOfClansModule.cs | 28 ++++++++----------- 2 files changed, 16 insertions(+), 24 deletions(-) diff --git a/NadekoBot/Modules/ClashOfClans/ClashOfClans.cs b/NadekoBot/Modules/ClashOfClans/ClashOfClans.cs index de3e3079..1ae491ad 100644 --- a/NadekoBot/Modules/ClashOfClans/ClashOfClans.cs +++ b/NadekoBot/Modules/ClashOfClans/ClashOfClans.cs @@ -1,11 +1,7 @@ -ο»Ώusing Discord.Commands; +ο»Ώusing Newtonsoft.Json; using System; using System.Linq; using System.Text; -using System.Threading; -using System.Threading.Tasks; -using System.Collections.Generic; -using Newtonsoft.Json; //using Manatee.Json.Serialization; namespace NadekoBot.Classes.ClashOfClans @@ -64,7 +60,7 @@ namespace NadekoBot.Classes.ClashOfClans public ulong ServerId { get; set; } public ulong ChannelId { get; set; } - + [JsonIgnore] public Discord.Channel Channel { get; internal set; } @@ -80,7 +76,7 @@ namespace NadekoBot.Classes.ClashOfClans this.Bases = new Caller[size]; this.ServerId = serverId; this.ChannelId = channelId; - this.Channel = NadekoBot.Client.Servers.FirstOrDefault(s=>s.Id == serverId)?.TextChannels.FirstOrDefault(c => c.Id == channelId); + this.Channel = NadekoBot.Client.Servers.FirstOrDefault(s => s.Id == serverId)?.TextChannels.FirstOrDefault(c => c.Id == channelId); } internal void End() @@ -155,7 +151,7 @@ namespace NadekoBot.Classes.ClashOfClans } else { - var left =(WarState == WarState.Started) ? callExpire - (DateTime.UtcNow - Bases[i].TimeAdded) : callExpire; + var left = (WarState == WarState.Started) ? callExpire - (DateTime.UtcNow - Bases[i].TimeAdded) : callExpire; sb.AppendLine($"`{i + 1}.` βœ… `{Bases[i].CallUser}` {left.Hours}h {left.Minutes}m {left.Seconds}s left"); } } diff --git a/NadekoBot/Modules/ClashOfClans/ClashOfClansModule.cs b/NadekoBot/Modules/ClashOfClans/ClashOfClansModule.cs index 847d97f4..37443900 100644 --- a/NadekoBot/Modules/ClashOfClans/ClashOfClansModule.cs +++ b/NadekoBot/Modules/ClashOfClans/ClashOfClansModule.cs @@ -1,15 +1,15 @@ ο»Ώusing Discord.Commands; using Discord.Modules; using NadekoBot.Classes.ClashOfClans; +using NadekoBot.Modules.Permissions.Classes; +using Newtonsoft.Json; using System; using System.Collections.Concurrent; using System.Collections.Generic; +using System.IO; +using System.Linq; using System.Text; using System.Threading.Tasks; -using NadekoBot.Modules.Permissions.Classes; -using System.Linq; -using System.IO; -using Newtonsoft.Json; namespace NadekoBot.Modules.ClashOfClans { @@ -87,12 +87,13 @@ namespace NadekoBot.Modules.ClashOfClans { await exWar.Channel.SendMessage($"War against {exWar.EnemyClan} ({exWar.Size}v{exWar.Size}) has ended"); } - + if (newVal.Count == 0) { List obj; ClashWars.TryRemove(cw.Key, out obj); - } else + } + else { ClashWars.AddOrUpdate(cw.Key, newVal, (x, s) => newVal); } @@ -137,11 +138,6 @@ namespace NadekoBot.Modules.ClashOfClans } } - - - - - #region commands public override void Install(ModuleManager manager) { @@ -180,13 +176,13 @@ namespace NadekoBot.Modules.ClashOfClans var cw = new ClashWar(enemyClan, size, e.Server.Id, e.Channel.Id); - //cw.Start(); + //cw.Start(); - wars.Add(cw); + wars.Add(cw); await e.Channel.SendMessage($"β—πŸ”°**CREATED CLAN WAR AGAINST {cw.ShortPrint()}**").ConfigureAwait(false); Save(); - //war with the index X started. - }); + //war with the index X started. + }); cgb.CreateCommand(Prefix + "startwar") .Alias(Prefix + "sw") @@ -242,7 +238,7 @@ namespace NadekoBot.Modules.ClashOfClans } await e.Channel.SendMessage(sb.ToString()).ConfigureAwait(false); return; - + } //if number is not null, print the war needed var warsInfo = GetInfo(e); From 6f6453e430bc10730384357a4ce90921a00214c8 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Wed, 27 Jul 2016 07:22:36 +0200 Subject: [PATCH 33/33] updated discord.net --- discord.net | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/discord.net b/discord.net index 3e519b5e..3a337311 160000 --- a/discord.net +++ b/discord.net @@ -1 +1 @@ -Subproject commit 3e519b5e0b33175e5a5ca247322b7082de484e15 +Subproject commit 3a33731135f1b7dd2efdb51b16158c84bb22da66