diff --git a/src/NadekoBot/Migrations/20160905095544_first.Designer.cs b/src/NadekoBot/Migrations/20160908202817_first.Designer.cs similarity index 98% rename from src/NadekoBot/Migrations/20160905095544_first.Designer.cs rename to src/NadekoBot/Migrations/20160908202817_first.Designer.cs index 130eebf3..5212a3d3 100644 --- a/src/NadekoBot/Migrations/20160905095544_first.Designer.cs +++ b/src/NadekoBot/Migrations/20160908202817_first.Designer.cs @@ -8,7 +8,7 @@ using NadekoBot.Services.Database.Impl; namespace NadekoBot.Migrations { [DbContext(typeof(NadekoSqliteContext))] - [Migration("20160905095544_first")] + [Migration("20160908202817_first")] partial class first { protected override void BuildTargetModel(ModelBuilder modelBuilder) @@ -39,6 +39,10 @@ namespace NadekoBot.Migrations b.Property("BufferSize"); + b.Property("CurrencyGenerationChance"); + + b.Property("CurrencyGenerationCooldown"); + b.Property("CurrencyName"); b.Property("CurrencyPluralName"); @@ -225,6 +229,8 @@ namespace NadekoBot.Migrations b.Property("ExclusiveSelfAssignedRoles"); + b.Property("GenerateCurrencyChannelId"); + b.Property("GreetMessageChannelId"); b.Property("GuildId"); diff --git a/src/NadekoBot/Migrations/20160905095544_first.cs b/src/NadekoBot/Migrations/20160908202817_first.cs similarity index 98% rename from src/NadekoBot/Migrations/20160905095544_first.cs rename to src/NadekoBot/Migrations/20160908202817_first.cs index ada195ce..fa79e6ea 100644 --- a/src/NadekoBot/Migrations/20160905095544_first.cs +++ b/src/NadekoBot/Migrations/20160908202817_first.cs @@ -15,6 +15,8 @@ namespace NadekoBot.Migrations Id = table.Column(nullable: false) .Annotation("Autoincrement", true), BufferSize = table.Column(nullable: false), + CurrencyGenerationChance = table.Column(nullable: false), + CurrencyGenerationCooldown = table.Column(nullable: false), CurrencyName = table.Column(nullable: true), CurrencyPluralName = table.Column(nullable: true), CurrencySign = table.Column(nullable: true), @@ -342,6 +344,7 @@ namespace NadekoBot.Migrations DeleteMessageOnCommand = table.Column(nullable: false), DmGreetMessageText = table.Column(nullable: true), ExclusiveSelfAssignedRoles = table.Column(nullable: false), + GenerateCurrencyChannelId = table.Column(nullable: true), GreetMessageChannelId = table.Column(nullable: false), GuildId = table.Column(nullable: false), LogSettingId = table.Column(nullable: true), diff --git a/src/NadekoBot/Migrations/NadekoSqliteContextModelSnapshot.cs b/src/NadekoBot/Migrations/NadekoSqliteContextModelSnapshot.cs index 12f2384b..be252766 100644 --- a/src/NadekoBot/Migrations/NadekoSqliteContextModelSnapshot.cs +++ b/src/NadekoBot/Migrations/NadekoSqliteContextModelSnapshot.cs @@ -38,6 +38,10 @@ namespace NadekoBot.Migrations b.Property("BufferSize"); + b.Property("CurrencyGenerationChance"); + + b.Property("CurrencyGenerationCooldown"); + b.Property("CurrencyName"); b.Property("CurrencyPluralName"); @@ -224,6 +228,8 @@ namespace NadekoBot.Migrations b.Property("ExclusiveSelfAssignedRoles"); + b.Property("GenerateCurrencyChannelId"); + b.Property("GreetMessageChannelId"); b.Property("GuildId"); diff --git a/src/NadekoBot/Modules/Gambling/Gambling.cs b/src/NadekoBot/Modules/Gambling/Gambling.cs index 2151fdf6..50e89cc0 100644 --- a/src/NadekoBot/Modules/Gambling/Gambling.cs +++ b/src/NadekoBot/Modules/Gambling/Gambling.cs @@ -31,7 +31,6 @@ namespace NadekoBot.Modules.Gambling CurrencySign = conf.CurrencySign; CurrencyPluralName = conf.CurrencyPluralName; } - } [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] @@ -68,26 +67,18 @@ namespace NadekoBot.Modules.Gambling [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] - public async Task Give(IUserMessage umsg, long amount, [Remainder] IUser receiver) + public async Task Give(IUserMessage umsg, long amount, [Remainder] IGuildUser receiver) { var channel = (ITextChannel)umsg.Channel; if (amount <= 0) return; - bool success = false; - using (var uow = DbHandler.UnitOfWork()) - { - success = uow.Currency.TryUpdateState(umsg.Author.Id, amount); - if(success) - uow.Currency.TryUpdateState(umsg.Author.Id, amount); - - await uow.CompleteAsync(); - } + var success = await CurrencyHandler.RemoveCurrencyAsync((IGuildUser)umsg.Author, $"Gift to {receiver.Username} ({receiver.Id}).", amount, true).ConfigureAwait(false); if (!success) { - await channel.SendMessageAsync($"{umsg.Author.Mention} You don't have enough {Gambling.CurrencyPluralName}s.").ConfigureAwait(false); + await channel.SendMessageAsync($"{umsg.Author.Mention} You don't have enough {Gambling.CurrencyPluralName}.").ConfigureAwait(false); return; } - + await CurrencyHandler.AddCurrencyAsync(receiver, $"Gift from {umsg.Author.Username} ({umsg.Author.Id}).", amount, true).ConfigureAwait(false); await channel.SendMessageAsync($"{umsg.Author.Mention} successfully sent {amount} {Gambling.CurrencyPluralName}s to {receiver.Mention}!").ConfigureAwait(false); } @@ -145,12 +136,12 @@ namespace NadekoBot.Modules.Gambling long userFlowers; using (var uow = DbHandler.UnitOfWork()) { - userFlowers = uow.Currency.GetOrCreate(umsg.Id).Amount; + userFlowers = uow.Currency.GetOrCreate(umsg.Author.Id).Amount; } if (userFlowers < amount) { - await channel.SendMessageAsync($"{guildUser.Mention} You don't have enough {Gambling.CurrencyName}s. You only have {userFlowers}{Gambling.CurrencySign}.").ConfigureAwait(false); + await channel.SendMessageAsync($"{guildUser.Mention} You don't have enough {Gambling.CurrencyPluralName}. You only have {userFlowers}{Gambling.CurrencySign}.").ConfigureAwait(false); return; } @@ -160,7 +151,7 @@ namespace NadekoBot.Modules.Gambling var str = $"{guildUser.Mention} `You rolled {rng}.` "; if (rng < 67) { - str += "Better luck next time."; + str += "More luck next time."; } else if (rng < 90) { diff --git a/src/NadekoBot/Modules/Games/Commands/LeetCommands.cs b/src/NadekoBot/Modules/Games/Commands/LeetCommands.cs index c327e823..63e4ca54 100644 --- a/src/NadekoBot/Modules/Games/Commands/LeetCommands.cs +++ b/src/NadekoBot/Modules/Games/Commands/LeetCommands.cs @@ -10,7 +10,7 @@ using System.Threading.Tasks; // because i don't want to waste my time on this cancerous command namespace NadekoBot.Modules.Games { - public partial class GamesModule + public partial class Games { [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] diff --git a/src/NadekoBot/Modules/Games/Commands/PlantAndPickCommands.cs b/src/NadekoBot/Modules/Games/Commands/PlantAndPickCommands.cs index f7dcd48a..c72e91ce 100644 --- a/src/NadekoBot/Modules/Games/Commands/PlantAndPickCommands.cs +++ b/src/NadekoBot/Modules/Games/Commands/PlantAndPickCommands.cs @@ -1,167 +1,220 @@ -//using Discord; -//using Discord.Commands; -//using NadekoBot.Classes; -//using NadekoBot.Extensions; -//using NadekoBot.Modules.Permissions.Classes; -//using System; -//using System.Collections.Concurrent; -//using System.Collections.Generic; -//using System.IO; -//using System.Linq; -//using System.Security.Cryptography; -//using System.Threading; -//using System.Threading.Tasks; +using Discord; +using Discord.Commands; +using Discord.WebSocket; +using NadekoBot.Attributes; +using NadekoBot.Extensions; +using NadekoBot.Services; +using NadekoBot.Services.Database; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Security.Cryptography; +using System.Threading; +using System.Threading.Tasks; -////todo rewrite -//namespace NadekoBot.Modules.Games -//{ -// /// -// /// Flower picking/planting idea is given to me by its -// /// inceptor Violent Crumble from Game Developers League discord server -// /// (he has !cookie and !nom) Thanks a lot Violent! -// /// Check out GDL (its a growing gamedev community): -// /// https://discord.gg/0TYNJfCU4De7YIk8 -// /// -// class PlantPick : DiscordCommand -// { +//todo rewrite +namespace NadekoBot.Modules.Games +{ + public partial class Games + { + /// + /// Flower picking/planting idea is given to me by its + /// inceptor Violent Crumble from Game Developers League discord server + /// (he has !cookie and !nom) Thanks a lot Violent! + /// Check out GDL (its a growing gamedev community): + /// https://discord.gg/0TYNJfCU4De7YIk8 + /// + [Group] + public class PlantPickCommands + { -// private Random rng; -// public PlantPick(DiscordModule module) : base(module) -// { -// NadekoBot.Client.MessageReceived += PotentialFlowerGeneration; -// rng = new Random(); -// } + private Random rng; -// private static readonly ConcurrentDictionary plantpickCooldowns = new ConcurrentDictionary(); + private ConcurrentDictionary generationChannels = new ConcurrentDictionary(); + //channelid/message + private ConcurrentDictionary> plantedFlowers = new ConcurrentDictionary>(); + //channelId/last generation + private ConcurrentDictionary lastGenerations = new ConcurrentDictionary(); -// private async void PotentialFlowerGeneration(object sender, Discord.MessageEventArgs e) -// { -// try -// { -// if (e.Server == null || e.Channel.IsPrivate || e.Message.IsAuthor) -// return; -// var config = Classes.SpecificConfigurations.Default.Of(e.Server.Id); -// var now = DateTime.Now; -// int cd; -// DateTime lastSpawned; -// if (config.GenerateCurrencyChannels.TryGetValue(e.Channel.Id, out cd)) -// if (!plantpickCooldowns.TryGetValue(e.Channel.Id, out lastSpawned) || (lastSpawned + new TimeSpan(0, cd, 0)) < now) -// { -// var rnd = Math.Abs(rng.Next(0,101)); -// if (rnd == 0) -// { -// var msgs = new[] { await e.Channel.SendFile(GetRandomCurrencyImagePath()), await channel.SendMessageAsync($"❗ A random {Gambling.CurrencyName} appeared! Pick it up by typing `>pick`") }; -// plantedFlowerChannels.AddOrUpdate(e.Channel.Id, msgs, (u, m) => { m.ForEach(async msgToDelete => { try { await msgToDelete.Delete(); } catch { } }); return msgs; }); -// plantpickCooldowns.AddOrUpdate(e.Channel.Id, now, (i, d) => now); -// } -// } -// } -// catch { } -// } -// //channelid/messageid pair -// ConcurrentDictionary> plantedFlowerChannels = new ConcurrentDictionary>(); + private float chance; + private int cooldown; -// private SemaphoreSlim locker = new SemaphoreSlim(1,1); + public PlantPickCommands() + { + NadekoBot.Client.MessageReceived += PotentialFlowerGeneration; + rng = new Random(); -// public override void Init(CommandGroupBuilder cgb) -// { -// cgb.CreateCommand(Module.Prefix + "pick") -// .Description($"Picks a flower planted in this channel. | `{Prefix}pick`") -// .Do(async e => -// { -// IEnumerable msgs; + using (var uow = DbHandler.UnitOfWork()) + { + var conf = uow.BotConfig.GetOrCreate(); + var x = + generationChannels = new ConcurrentDictionary(uow.GuildConfigs.GetAll() + .Where(c => c.GenerateCurrencyChannelId != null) + .ToDictionary(c => c.GenerateCurrencyChannelId.Value, c => true)); + chance = conf.CurrencyGenerationChance; + cooldown = conf.CurrencyGenerationCooldown; + } + } -// await e.Message.Delete().ConfigureAwait(false); -// if (!plantedFlowerChannels.TryRemove(e.Channel.Id, out msgs)) -// return; + private Task PotentialFlowerGeneration(IMessage imsg) + { + var msg = imsg as IUserMessage; + if (msg == null || msg.IsAuthor()) + return Task.CompletedTask; -// foreach(var msgToDelete in msgs) -// await msgToDelete.Delete().ConfigureAwait(false); + var channel = imsg.Channel as ITextChannel; + if (channel == null) + return Task.CompletedTask; -// await CurrencyHandler.AddFlowersAsync(umsg.Author, "Picked a flower.", 1, true).ConfigureAwait(false); -// var msg = await channel.SendMessageAsync($"**{umsg.Author.Username}** picked a {Gambling.CurrencyName}!").ConfigureAwait(false); -// ThreadPool.QueueUserWorkItem(async (state) => -// { -// try -// { -// await Task.Delay(10000).ConfigureAwait(false); -// await msg.Delete().ConfigureAwait(false); -// } -// catch { } -// }); -// }); + bool shouldGenerate; + if (!generationChannels.TryGetValue(channel.Id, out shouldGenerate) || !shouldGenerate) + return Task.CompletedTask; -// 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) | `{Prefix}plant`") -// .Do(async e => -// { -// await locker.WaitAsync().ConfigureAwait(false); -// try -// { -// if (plantedFlowerChannels.ContainsKey(e.Channel.Id)) -// { -// await channel.SendMessageAsync($"There is already a {Gambling.CurrencyName} in this channel.").ConfigureAwait(false); -// return; -// } -// var removed = await CurrencyHandler.RemoveFlowers(umsg.Author, "Planted a flower.", 1, true).ConfigureAwait(false); -// if (!removed) -// { -// await channel.SendMessageAsync($"You don't have any {Gambling.CurrencyName}s.").ConfigureAwait(false); -// return; -// } + var t = Task.Run(async () => + { + var lastGeneration = lastGenerations.GetOrAdd(channel.Id, DateTime.MinValue); -// var file = GetRandomCurrencyImagePath(); -// Message msg; -// if (file == null) -// msg = await channel.SendMessageAsync(Gambling.CurrencySign).ConfigureAwait(false); -// else -// msg = await e.Channel.SendFile(file).ConfigureAwait(false); -// var vowelFirst = new[] { 'a', 'e', 'i', 'o', 'u' }.Contains(Gambling.CurrencyName[0]); -// var msg2 = await channel.SendMessageAsync($"Oh how Nice! **{umsg.Author.Username}** planted {(vowelFirst ? "an" : "a")} {Gambling.CurrencyName}. Pick it using {Module.Prefix}pick").ConfigureAwait(false); -// plantedFlowerChannels.TryAdd(e.Channel.Id, new[] { msg, msg2 }); -// } -// finally { locker.Release(); } -// }); + if (DateTime.Now - TimeSpan.FromSeconds(cooldown) < lastGeneration) //recently generated in this channel, don't generate again + return; -// cgb.CreateCommand(Prefix + "gencurrency") -// .Alias(Prefix + "gc") -// .Description($"Toggles currency generation on this channel. Every posted message will have 2% chance to spawn a {Gambling.CurrencyName}. Optional parameter cooldown time in minutes, 5 minutes by default. Requires Manage Messages permission. | `{Prefix}gc` or `{Prefix}gc 60`") -// .AddCheck(SimpleCheckers.ManageMessages()) -// .Parameter("cd", ParameterType.Unparsed) -// .Do(async e => -// { -// var cdStr = cd; -// int cd = 2; -// if (!int.TryParse(cdStr, out cd) || cd < 0) -// { -// cd = 2; -// } -// var config = SpecificConfigurations.Default.Of(e.Server.Id); -// int throwaway; -// if (config.GenerateCurrencyChannels.TryRemove(e.Channel.Id, out throwaway)) -// { -// await channel.SendMessageAsync("`Currency generation disabled on this channel.`").ConfigureAwait(false); -// } -// else -// { -// if (config.GenerateCurrencyChannels.TryAdd(e.Channel.Id, cd)) -// await channel.SendMessageAsync($"`Currency generation enabled on this channel. Cooldown is {cd} minutes.`").ConfigureAwait(false); -// } -// }); -// } + var num = rng.Next(1, 101) + chance * 100; -// private string GetRandomCurrencyImagePath() => -// Directory.GetFiles("data/currency_images").OrderBy(s => rng.Next()).FirstOrDefault(); + if (num > 100) + { + lastGenerations.AddOrUpdate(channel.Id, DateTime.Now, (id, old) => DateTime.Now); + //todo get prefix + try + { + var sent = await channel.SendFileAsync( + GetRandomCurrencyImagePath(), + $"❗ A random { Gambling.Gambling.CurrencyName } appeared! Pick it up by typing `>pick`") + .ConfigureAwait(false); + plantedFlowers.AddOrUpdate(channel.Id, new List() { sent }, (id, old) => { old.Add(sent); return old; }); + } + catch { } + + } + }); + return Task.CompletedTask; + } + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] + [RequireContext(ContextType.Guild)] + public async Task Pick(IUserMessage imsg) + { + var channel = (ITextChannel)imsg.Channel; -// int GetRandomNumber() -// { -// using (var rg = RandomNumberGenerator.Create()) -// { -// byte[] rno = new byte[4]; -// rg.GetBytes(rno); -// int randomvalue = BitConverter.ToInt32(rno, 0); -// return randomvalue; -// } -// } -// } -//} + if (!channel.Guild.GetCurrentUser().GetPermissions(channel).ManageMessages) + { + await channel.SendMessageAsync("`I need manage channel permissions in order to process this command.`").ConfigureAwait(false); + return; + } + + List msgs; + + try { await imsg.DeleteAsync().ConfigureAwait(false); } catch { } + if (!plantedFlowers.TryRemove(channel.Id, out msgs)) + return; + + await Task.WhenAll(msgs.Select(toDelete => toDelete.DeleteAsync())).ConfigureAwait(false); + + await CurrencyHandler.AddCurrencyAsync((IGuildUser)imsg.Author, "Picked flower(s).", msgs.Count, false).ConfigureAwait(false); + var msg = await channel.SendMessageAsync($"**{imsg.Author.Username}** picked {msgs.Count}{Gambling.Gambling.CurrencySign}!").ConfigureAwait(false); + var t = Task.Run(async () => + { + try + { + await Task.Delay(10000).ConfigureAwait(false); + await msg.DeleteAsync().ConfigureAwait(false); + } + catch { } + }); + } + + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] + [RequireContext(ContextType.Guild)] + public async Task Plant(IUserMessage imsg) + { + var channel = (ITextChannel)imsg.Channel; + if (channel == null) + return; + + var removed = await CurrencyHandler.RemoveCurrencyAsync((IGuildUser)imsg.Author, "Planted a flower.", 1, false).ConfigureAwait(false); + if (!removed) + { + await channel.SendMessageAsync($"You don't have any {Gambling.Gambling.CurrencyPluralName}.").ConfigureAwait(false); + return; + } + + var file = GetRandomCurrencyImagePath(); + IUserMessage msg; + var vowelFirst = new[] { 'a', 'e', 'i', 'o', 'u' }.Contains(Gambling.Gambling.CurrencyName[0]); + var msgToSend = $"Oh how Nice! **{imsg.Author.Username}** planted {(vowelFirst ? "an" : "a")} {Gambling.Gambling.CurrencyName}. Pick it using >pick"; + if (file == null) + { + msg = await channel.SendMessageAsync(Gambling.Gambling.CurrencySign).ConfigureAwait(false); + } + else + { + //todo add prefix + msg = await channel.SendFileAsync(file, msgToSend).ConfigureAwait(false); + } + plantedFlowers.AddOrUpdate(channel.Id, new List() { msg }, (id, old) => { old.Add(msg); return old; }); + } + + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] + [RequireContext(ContextType.Guild)] + [RequirePermission(GuildPermission.ManageMessages)] + public async Task Gencurrency(IUserMessage imsg) + { + var channel = imsg.Channel as ITextChannel; + if (channel == null) + return; + + bool enabled; + using (var uow = DbHandler.UnitOfWork()) + { + var guildConfig = uow.GuildConfigs.For(channel.Id); + + if (guildConfig.GenerateCurrencyChannelId == null) + { + guildConfig.GenerateCurrencyChannelId = channel.Id; + generationChannels.TryAdd(channel.Id, true); + enabled = true; + } + else + { + guildConfig.GenerateCurrencyChannelId = null; + bool throwaway; + generationChannels.TryRemove(channel.Id, out throwaway); + enabled = false; + } + await uow.CompleteAsync(); + } + if (enabled) + { + await channel.SendMessageAsync("`Currency generation enabled on this channel.`").ConfigureAwait(false); + } + else + { + await channel.SendMessageAsync($"`Currency generation disabled on this channel.`").ConfigureAwait(false); + } + } + + private string GetRandomCurrencyImagePath() => + Directory.GetFiles("data/currency_images").OrderBy(s => rng.Next()).FirstOrDefault(); + + int GetRandomNumber() + { + using (var rg = RandomNumberGenerator.Create()) + { + byte[] rno = new byte[4]; + rg.GetBytes(rno); + int randomvalue = BitConverter.ToInt32(rno, 0); + return randomvalue; + } + } + } + } +} \ No newline at end of file diff --git a/src/NadekoBot/Modules/Games/Commands/PollCommands.cs b/src/NadekoBot/Modules/Games/Commands/PollCommands.cs index 7456b9a8..711873d3 100644 --- a/src/NadekoBot/Modules/Games/Commands/PollCommands.cs +++ b/src/NadekoBot/Modules/Games/Commands/PollCommands.cs @@ -10,7 +10,7 @@ using System.Threading.Tasks; namespace NadekoBot.Modules.Games { - public partial class GamesModule + public partial class Games { public static ConcurrentDictionary ActivePolls = new ConcurrentDictionary(); diff --git a/src/NadekoBot/Modules/Games/Commands/SpeedTypingCommands.cs b/src/NadekoBot/Modules/Games/Commands/SpeedTypingCommands.cs index f20bf8dd..7817a61a 100644 --- a/src/NadekoBot/Modules/Games/Commands/SpeedTypingCommands.cs +++ b/src/NadekoBot/Modules/Games/Commands/SpeedTypingCommands.cs @@ -14,181 +14,184 @@ using System.Threading.Tasks; namespace NadekoBot.Modules.Games { - public class TypingGame + public partial class Games { - public const float WORD_VALUE = 4.5f; - private readonly ITextChannel channel; - public string CurrentSentence; - public bool IsActive; - private readonly Stopwatch sw; - private readonly List finishedUserIds; - - public TypingGame(ITextChannel channel) + public class TypingGame { - this.channel = channel; - IsActive = false; - sw = new Stopwatch(); - finishedUserIds = new List(); - } + public const float WORD_VALUE = 4.5f; + private readonly ITextChannel channel; + public string CurrentSentence; + public bool IsActive; + private readonly Stopwatch sw; + private readonly List finishedUserIds; - public ITextChannel Channel { get; set; } - - public async Task Stop() - { - if (!IsActive) return false; - NadekoBot.Client.MessageReceived -= AnswerReceived; - finishedUserIds.Clear(); - IsActive = false; - sw.Stop(); - sw.Reset(); - await channel.SendMessageAsync("Typing contest stopped").ConfigureAwait(false); - return true; - } - - public async Task Start() - { - while (true) + public TypingGame(ITextChannel channel) { - if (IsActive) return; // can't start running game - IsActive = true; - CurrentSentence = GetRandomSentence(); - var i = (int)(CurrentSentence.Length / WORD_VALUE * 1.7f); - await channel.SendMessageAsync($":clock2: Next contest will last for {i} seconds. Type the bolded text as fast as you can.").ConfigureAwait(false); + this.channel = channel; + IsActive = false; + sw = new Stopwatch(); + finishedUserIds = new List(); + } + public ITextChannel Channel { get; set; } - var msg = await channel.SendMessageAsync("Starting new typing contest in **3**...").ConfigureAwait(false); - await Task.Delay(1000).ConfigureAwait(false); - await msg.ModifyAsync(m => m.Content = "Starting new typing contest in **2**...").ConfigureAwait(false); - await Task.Delay(1000).ConfigureAwait(false); - await msg.ModifyAsync(m => m.Content = "Starting new typing contest in **1**...").ConfigureAwait(false); - await Task.Delay(1000).ConfigureAwait(false); - await msg.ModifyAsync(m => m.Content = $":book:**{CurrentSentence.Replace(" ", " \x200B")}**:book:").ConfigureAwait(false); - sw.Start(); - HandleAnswers(); + public async Task Stop() + { + if (!IsActive) return false; + NadekoBot.Client.MessageReceived -= AnswerReceived; + finishedUserIds.Clear(); + IsActive = false; + sw.Stop(); + sw.Reset(); + await channel.SendMessageAsync("Typing contest stopped").ConfigureAwait(false); + return true; + } - while (i > 0) + public async Task Start() + { + while (true) { + if (IsActive) return; // can't start running game + IsActive = true; + CurrentSentence = GetRandomSentence(); + var i = (int)(CurrentSentence.Length / WORD_VALUE * 1.7f); + await channel.SendMessageAsync($":clock2: Next contest will last for {i} seconds. Type the bolded text as fast as you can.").ConfigureAwait(false); + + + var msg = await channel.SendMessageAsync("Starting new typing contest in **3**...").ConfigureAwait(false); await Task.Delay(1000).ConfigureAwait(false); - i--; - if (!IsActive) - return; + await msg.ModifyAsync(m => m.Content = "Starting new typing contest in **2**...").ConfigureAwait(false); + await Task.Delay(1000).ConfigureAwait(false); + await msg.ModifyAsync(m => m.Content = "Starting new typing contest in **1**...").ConfigureAwait(false); + await Task.Delay(1000).ConfigureAwait(false); + await msg.ModifyAsync(m => m.Content = $":book:**{CurrentSentence.Replace(" ", " \x200B")}**:book:").ConfigureAwait(false); + sw.Start(); + HandleAnswers(); + + while (i > 0) + { + await Task.Delay(1000).ConfigureAwait(false); + i--; + if (!IsActive) + return; + } + + await Stop().ConfigureAwait(false); + } + } + + public string GetRandomSentence() + { + using (var uow = DbHandler.UnitOfWork()) + { + return uow.TypingArticles.GetRandom()?.Text ?? "No typing articles found. Use `>typeadd` command to add a new article for typing."; } - await Stop().ConfigureAwait(false); - } - } - - public string GetRandomSentence() - { - using (var uow = DbHandler.UnitOfWork()) - { - return uow.TypingArticles.GetRandom()?.Text ?? "No typing articles found. Use `>typeadd` command to add a new article for typing."; } - } - - private void HandleAnswers() - { - NadekoBot.Client.MessageReceived += AnswerReceived; - } - - private Task AnswerReceived(IMessage imsg) - { - var msg = imsg as IUserMessage; - if (msg == null) - return Task.CompletedTask; - var t = Task.Run(async () => + private void HandleAnswers() { - try + NadekoBot.Client.MessageReceived += AnswerReceived; + } + + private Task AnswerReceived(IMessage imsg) + { + var msg = imsg as IUserMessage; + if (msg == null) + return Task.CompletedTask; + var t = Task.Run(async () => { - if (channel == null || channel.Id != channel.Id || msg.Author.Id == NadekoBot.Client.GetCurrentUser().Id) return; - - var guess = msg.Content; - - var distance = CurrentSentence.LevenshteinDistance(guess); - var decision = Judge(distance, guess.Length); - if (decision && !finishedUserIds.Contains(msg.Author.Id)) + try { - finishedUserIds.Add(msg.Author.Id); - await channel.SendMessageAsync($"{msg.Author.Mention} finished in **{sw.Elapsed.Seconds}** seconds with { distance } errors, **{ CurrentSentence.Length / WORD_VALUE / sw.Elapsed.Seconds * 60 }** WPM!").ConfigureAwait(false); - if (finishedUserIds.Count % 2 == 0) + if (channel == null || channel.Id != channel.Id || msg.Author.Id == NadekoBot.Client.GetCurrentUser().Id) return; + + var guess = msg.Content; + + var distance = CurrentSentence.LevenshteinDistance(guess); + var decision = Judge(distance, guess.Length); + if (decision && !finishedUserIds.Contains(msg.Author.Id)) { - await channel.SendMessageAsync($":exclamation: `A lot of people finished, here is the text for those still typing:`\n\n:book:**{CurrentSentence}**:book:").ConfigureAwait(false); + finishedUserIds.Add(msg.Author.Id); + await channel.SendMessageAsync($"{msg.Author.Mention} finished in **{sw.Elapsed.Seconds}** seconds with { distance } errors, **{ CurrentSentence.Length / WORD_VALUE / sw.Elapsed.Seconds * 60 }** WPM!").ConfigureAwait(false); + if (finishedUserIds.Count % 2 == 0) + { + await channel.SendMessageAsync($":exclamation: `A lot of people finished, here is the text for those still typing:`\n\n:book:**{CurrentSentence}**:book:").ConfigureAwait(false); + } } } + catch { } + }); + return Task.CompletedTask; + } + + private bool Judge(int errors, int textLength) => errors <= textLength / 25; + + } + + [Group] + public class SpeedTypingCommands + { + + public static ConcurrentDictionary RunningContests; + + public SpeedTypingCommands() + { + RunningContests = new ConcurrentDictionary(); + } + + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] + [RequireContext(ContextType.Guild)] + public async Task TypeStart(IUserMessage msg) + { + var channel = (ITextChannel)msg.Channel; + + var game = RunningContests.GetOrAdd(channel.Guild.Id, id => new TypingGame(channel)); + + if (game.IsActive) + { + await channel.SendMessageAsync( + $"Contest already running in " + + $"{game.Channel.Mention} channel.") + .ConfigureAwait(false); } - catch { } - }); - return Task.CompletedTask; + else + { + await game.Start().ConfigureAwait(false); + } + } + + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] + [RequireContext(ContextType.Guild)] + public async Task TypeStop(IUserMessage imsg) + { + var channel = (ITextChannel)imsg.Channel; + TypingGame game; + if (RunningContests.TryRemove(channel.Guild.Id, out game)) + { + await game.Stop().ConfigureAwait(false); + return; + } + await channel.SendMessageAsync("No contest to stop on this channel.").ConfigureAwait(false); + } + + ////todo owner only + //[LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] + //[RequireContext(ContextType.Guild)] + //public async Task Typeadd(IUserMessage imsg, [Remainder] string text) + //{ + // var channel = (ITextChannel)imsg.Channel; + + // using (var uow = DbHandler.UnitOfWork()) + // { + // uow.TypingArticles.Add(new Services.Database.Models.TypingArticle + // { + // Author = imsg.Author.Username, + // Text = text + // }); + // } + + // await channel.SendMessageAsync("Added new article for typing game.").ConfigureAwait(false); + //} } - - private bool Judge(int errors, int textLength) => errors <= textLength / 25; - } - - [Group] - public class SpeedTypingCommands - { - - public static ConcurrentDictionary RunningContests; - - public SpeedTypingCommands() - { - RunningContests = new ConcurrentDictionary(); - } - - [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] - [RequireContext(ContextType.Guild)] - public async Task TypeStart(IUserMessage msg) - { - var channel = (ITextChannel)msg.Channel; - - var game = RunningContests.GetOrAdd(channel.Guild.Id, id => new TypingGame(channel)); - - if (game.IsActive) - { - await channel.SendMessageAsync( - $"Contest already running in " + - $"{game.Channel.Mention} channel.") - .ConfigureAwait(false); - } - else - { - await game.Start().ConfigureAwait(false); - } - } - - [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] - [RequireContext(ContextType.Guild)] - public async Task TypeStop(IUserMessage imsg) - { - var channel = (ITextChannel)imsg.Channel; - TypingGame game; - if (RunningContests.TryRemove(channel.Guild.Id, out game)) - { - await game.Stop().ConfigureAwait(false); - return; - } - await channel.SendMessageAsync("No contest to stop on this channel.").ConfigureAwait(false); - } - - ////todo owner only - //[LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] - //[RequireContext(ContextType.Guild)] - //public async Task Typeadd(IUserMessage imsg, [Remainder] string text) - //{ - // var channel = (ITextChannel)imsg.Channel; - - // using (var uow = DbHandler.UnitOfWork()) - // { - // uow.TypingArticles.Add(new Services.Database.Models.TypingArticle - // { - // Author = imsg.Author.Username, - // Text = text - // }); - // } - - // await channel.SendMessageAsync("Added new article for typing game.").ConfigureAwait(false); - //} - } -} +} \ No newline at end of file diff --git a/src/NadekoBot/Modules/Games/Commands/TriviaCommands.cs b/src/NadekoBot/Modules/Games/Commands/TriviaCommands.cs index e5de052e..959562a0 100644 --- a/src/NadekoBot/Modules/Games/Commands/TriviaCommands.cs +++ b/src/NadekoBot/Modules/Games/Commands/TriviaCommands.cs @@ -10,7 +10,7 @@ using System.Threading.Tasks; //todo Rewrite? Fix trivia not stopping bug namespace NadekoBot.Modules.Games { - public partial class GamesModule + public partial class Games { [Group] public class TriviaCommands @@ -19,7 +19,7 @@ namespace NadekoBot.Modules.Games [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] - public async Task Trivia(IUserMessage umsg, string[] args) + public async Task Trivia(IUserMessage umsg, params string[] args) { var channel = (ITextChannel)umsg.Channel; diff --git a/src/NadekoBot/NadekoBot.cs b/src/NadekoBot/NadekoBot.cs index df7b7721..3e698605 100644 --- a/src/NadekoBot/NadekoBot.cs +++ b/src/NadekoBot/NadekoBot.cs @@ -44,8 +44,8 @@ namespace NadekoBot Commands = new CommandService(); Localizer = new Localization(); Google = new GoogleApiService(); - Stats = new StatsService(Client); CommandHandler = new CommandHandler(Client, Commands); + Stats = new StatsService(Client, CommandHandler); _log = LogManager.GetCurrentClassLogger(); //setup DI diff --git a/src/NadekoBot/Resources/CommandStrings.Designer.cs b/src/NadekoBot/Resources/CommandStrings.Designer.cs index 0c172970..ed63b4ed 100644 --- a/src/NadekoBot/Resources/CommandStrings.Designer.cs +++ b/src/NadekoBot/Resources/CommandStrings.Designer.cs @@ -969,7 +969,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to $$$. + /// Looks up a localized string similar to cash $$. /// public static string cash_text { get { diff --git a/src/NadekoBot/Resources/CommandStrings.resx b/src/NadekoBot/Resources/CommandStrings.resx index 0a629b84..b126ce83 100644 --- a/src/NadekoBot/Resources/CommandStrings.resx +++ b/src/NadekoBot/Resources/CommandStrings.resx @@ -2635,6 +2635,6 @@ `$$$` or `$$$ @SomeGuy` - $$$ + cash $$ \ No newline at end of file diff --git a/src/NadekoBot/Services/CurrencyHandler.cs b/src/NadekoBot/Services/CurrencyHandler.cs index d86eea80..aa7fc918 100644 --- a/src/NadekoBot/Services/CurrencyHandler.cs +++ b/src/NadekoBot/Services/CurrencyHandler.cs @@ -20,14 +20,14 @@ namespace NadekoBot.Services using (var uow = DbHandler.UnitOfWork()) { - var success = uow.Currency.TryUpdateState(author.Id, amount); + var success = uow.Currency.TryUpdateState(author.Id, -amount); if (!success) return false; await uow.CompleteAsync(); } if (sendMessage) - try { await author.SendMessageAsync($"`You received:` {amount} {Gambling.CurrencySign}\n`Reason:` {reason}").ConfigureAwait(false); } catch { } + try { await author.SendMessageAsync($"`You lost:` {amount} {Gambling.CurrencySign}\n`Reason:` {reason}").ConfigureAwait(false); } catch { } return true; } diff --git a/src/NadekoBot/Services/Database/Models/BotConfig.cs b/src/NadekoBot/Services/Database/Models/BotConfig.cs index 6bb02fb3..09fa4aa7 100644 --- a/src/NadekoBot/Services/Database/Models/BotConfig.cs +++ b/src/NadekoBot/Services/Database/Models/BotConfig.cs @@ -15,6 +15,9 @@ namespace NadekoBot.Services.Database.Models public bool ForwardMessages { get; set; } = true; public bool ForwardToAllOwners { get; set; } = true; + public float CurrencyGenerationChance { get; set; } = 0.02f; + public int CurrencyGenerationCooldown { get; set; } = 10; + public List ModulePrefixes { get; set; } = new List() { new ModulePrefix() { ModuleName="Administration", Prefix="." }, diff --git a/src/NadekoBot/Services/Database/Models/GuildConfig.cs b/src/NadekoBot/Services/Database/Models/GuildConfig.cs index c07652e4..ada21e94 100644 --- a/src/NadekoBot/Services/Database/Models/GuildConfig.cs +++ b/src/NadekoBot/Services/Database/Models/GuildConfig.cs @@ -38,5 +38,8 @@ namespace NadekoBot.Services.Database.Models //stream notifications public List FollowedStreams { get; set; } = new List(); + + //currencyGeneration + public ulong? GenerateCurrencyChannelId { get; set; } } } diff --git a/src/NadekoBot/Services/Impl/StatsService.cs b/src/NadekoBot/Services/Impl/StatsService.cs index 87331161..3d33bb9f 100644 --- a/src/NadekoBot/Services/Impl/StatsService.cs +++ b/src/NadekoBot/Services/Impl/StatsService.cs @@ -15,19 +15,21 @@ namespace NadekoBot.Services.Impl private int messageCounter; private DiscordSocketClient client; private DateTime started; + private int commandsRan = 0; public string BotVersion => "1.0-alpha"; public string Heap => Math.Round((double)GC.GetTotalMemory(false) / 1.MiB(), 2).ToString(); - public StatsService(DiscordSocketClient client) + public StatsService(DiscordSocketClient client, CommandHandler cmdHandler) { this.client = client; Reset(); this.client.MessageReceived += _ => Task.FromResult(messageCounter++); + cmdHandler.CommandExecuted += (_, e) => commandsRan++; this.client.Disconnected += _ => Reset(); } @@ -37,6 +39,7 @@ namespace NadekoBot.Services.Impl `Owners' Ids: {string.Join(", ", NadekoBot.Credentials.OwnerIds)}` `Uptime: {GetUptimeString()}` `Servers: {client.GetGuilds().Count} | TextChannels: {client.GetGuilds().SelectMany(g => g.GetChannels().Where(c => c is ITextChannel)).Count()} | VoiceChannels: {client.GetGuilds().SelectMany(g => g.GetChannels().Where(c => c is IVoiceChannel)).Count()}` +`Commands Ran this session: {commandsRan}` `Messages: {messageCounter} ({messageCounter / (double)GetUptime().TotalSeconds:F2}/sec)` `Heap: {Heap} MB`"); public Task Reset()