diff --git a/discord.net b/discord.net index e8550fa4..7d4f54ee 160000 --- a/discord.net +++ b/discord.net @@ -1 +1 @@ -Subproject commit e8550fa462f86e0338925547dfe99242bfc1fdcc +Subproject commit 7d4f54ee17ae0b5c962036501b46d986c741cb29 diff --git a/src/NadekoBot/Migrations/20160828000228_first.Designer.cs b/src/NadekoBot/Migrations/20160830011641_first.Designer.cs similarity index 90% rename from src/NadekoBot/Migrations/20160828000228_first.Designer.cs rename to src/NadekoBot/Migrations/20160830011641_first.Designer.cs index 43f73276..a1c78a53 100644 --- a/src/NadekoBot/Migrations/20160828000228_first.Designer.cs +++ b/src/NadekoBot/Migrations/20160830011641_first.Designer.cs @@ -8,7 +8,7 @@ using NadekoBot.Services.Database.Impl; namespace NadekoBot.Migrations { [DbContext(typeof(NadekoSqliteContext))] - [Migration("20160828000228_first")] + [Migration("20160830011641_first")] partial class first { protected override void BuildTargetModel(ModelBuilder modelBuilder) @@ -104,6 +104,23 @@ namespace NadekoBot.Migrations b.ToTable("ClashOfClans"); }); + modelBuilder.Entity("NadekoBot.Services.Database.Models.Currency", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Amount"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("UserId") + .IsUnique(); + + b.ToTable("Currency"); + }); + modelBuilder.Entity("NadekoBot.Services.Database.Models.Donator", b => { b.Property("Id") @@ -160,6 +177,8 @@ namespace NadekoBot.Migrations b.Property("ChannelGreetMessageText"); + b.Property("DefaultMusicVolume"); + b.Property("DeleteMessageOnCommand"); b.Property("DmGreetMessageText"); @@ -281,6 +300,27 @@ namespace NadekoBot.Migrations b.ToTable("Reminders"); }); + modelBuilder.Entity("NadekoBot.Services.Database.Models.Repeater", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ChannelId"); + + b.Property("GuildId"); + + b.Property("Interval"); + + b.Property("Message"); + + b.HasKey("Id"); + + b.HasIndex("ChannelId") + .IsUnique(); + + b.ToTable("Repeaters"); + }); + modelBuilder.Entity("NadekoBot.Services.Database.Models.SelfAssignedRole", b => { b.Property("Id") diff --git a/src/NadekoBot/Migrations/20160828000228_first.cs b/src/NadekoBot/Migrations/20160830011641_first.cs similarity index 89% rename from src/NadekoBot/Migrations/20160828000228_first.cs rename to src/NadekoBot/Migrations/20160830011641_first.cs index 081d2cb9..cf8464d4 100644 --- a/src/NadekoBot/Migrations/20160828000228_first.cs +++ b/src/NadekoBot/Migrations/20160830011641_first.cs @@ -47,6 +47,20 @@ namespace NadekoBot.Migrations table.PrimaryKey("PK_ClashOfClans", x => x.Id); }); + migrationBuilder.CreateTable( + name: "Currency", + columns: table => new + { + Id = table.Column(nullable: false) + .Annotation("Autoincrement", true), + Amount = table.Column(nullable: false), + UserId = table.Column(nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Currency", x => x.Id); + }); + migrationBuilder.CreateTable( name: "Donators", columns: table => new @@ -76,6 +90,7 @@ namespace NadekoBot.Migrations ByeMessageChannelId = table.Column(nullable: false), ChannelByeMessageText = table.Column(nullable: true), ChannelGreetMessageText = table.Column(nullable: true), + DefaultMusicVolume = table.Column(nullable: false), DeleteMessageOnCommand = table.Column(nullable: false), DmGreetMessageText = table.Column(nullable: true), ExclusiveSelfAssignedRoles = table.Column(nullable: false), @@ -125,6 +140,22 @@ namespace NadekoBot.Migrations table.PrimaryKey("PK_Reminders", x => x.Id); }); + migrationBuilder.CreateTable( + name: "Repeaters", + columns: table => new + { + Id = table.Column(nullable: false) + .Annotation("Autoincrement", true), + ChannelId = table.Column(nullable: false), + GuildId = table.Column(nullable: false), + Interval = table.Column(nullable: false), + Message = table.Column(nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_Repeaters", x => x.Id); + }); + migrationBuilder.CreateTable( name: "SelfAssignableRoles", columns: table => new @@ -274,6 +305,12 @@ namespace NadekoBot.Migrations table: "ClashCallers", column: "ClashWarId"); + migrationBuilder.CreateIndex( + name: "IX_Currency_UserId", + table: "Currency", + column: "UserId", + unique: true); + migrationBuilder.CreateIndex( name: "IX_Donators_UserId", table: "Donators", @@ -306,6 +343,12 @@ namespace NadekoBot.Migrations table: "RaceAnimal", column: "BotConfigId"); + migrationBuilder.CreateIndex( + name: "IX_Repeaters_ChannelId", + table: "Repeaters", + column: "ChannelId", + unique: true); + migrationBuilder.CreateIndex( name: "IX_SelfAssignableRoles_GuildId_RoleId", table: "SelfAssignableRoles", @@ -321,6 +364,9 @@ namespace NadekoBot.Migrations migrationBuilder.DropTable( name: "ClashCallers"); + migrationBuilder.DropTable( + name: "Currency"); + migrationBuilder.DropTable( name: "Donators"); @@ -345,6 +391,9 @@ namespace NadekoBot.Migrations migrationBuilder.DropTable( name: "Reminders"); + migrationBuilder.DropTable( + name: "Repeaters"); + migrationBuilder.DropTable( name: "SelfAssignableRoles"); diff --git a/src/NadekoBot/Migrations/NadekoSqliteContextModelSnapshot.cs b/src/NadekoBot/Migrations/NadekoSqliteContextModelSnapshot.cs index b9042c98..456411fc 100644 --- a/src/NadekoBot/Migrations/NadekoSqliteContextModelSnapshot.cs +++ b/src/NadekoBot/Migrations/NadekoSqliteContextModelSnapshot.cs @@ -103,6 +103,23 @@ namespace NadekoBot.Migrations b.ToTable("ClashOfClans"); }); + modelBuilder.Entity("NadekoBot.Services.Database.Models.Currency", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Amount"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("UserId") + .IsUnique(); + + b.ToTable("Currency"); + }); + modelBuilder.Entity("NadekoBot.Services.Database.Models.Donator", b => { b.Property("Id") @@ -159,6 +176,8 @@ namespace NadekoBot.Migrations b.Property("ChannelGreetMessageText"); + b.Property("DefaultMusicVolume"); + b.Property("DeleteMessageOnCommand"); b.Property("DmGreetMessageText"); @@ -280,6 +299,27 @@ namespace NadekoBot.Migrations b.ToTable("Reminders"); }); + modelBuilder.Entity("NadekoBot.Services.Database.Models.Repeater", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ChannelId"); + + b.Property("GuildId"); + + b.Property("Interval"); + + b.Property("Message"); + + b.HasKey("Id"); + + b.HasIndex("ChannelId") + .IsUnique(); + + b.ToTable("Repeaters"); + }); + modelBuilder.Entity("NadekoBot.Services.Database.Models.SelfAssignedRole", b => { b.Property("Id") diff --git a/src/NadekoBot/Modules/Administration/Administration.cs b/src/NadekoBot/Modules/Administration/Administration.cs index b96ec950..f0e8b7a5 100644 --- a/src/NadekoBot/Modules/Administration/Administration.cs +++ b/src/NadekoBot/Modules/Administration/Administration.cs @@ -14,7 +14,6 @@ using Discord.WebSocket; using NadekoBot.Services.Database; using NadekoBot.Services.Database.Models; -//todo fix delmsgoncmd namespace NadekoBot.Modules.Administration { [Module(".", AppendSpace = false)] @@ -22,8 +21,32 @@ namespace NadekoBot.Modules.Administration { public Administration(ILocalization loc, CommandService cmds, DiscordSocketClient client) : base(loc, cmds, client) { - + NadekoBot.CommandHandler.CommandExecuted += DelMsgOnCmd_Handler; } + + private void DelMsgOnCmd_Handler(object sender, CommandExecutedEventArgs e) + { + try + { + var channel = e.Message.Channel as ITextChannel; + if (channel == null) + return; + + bool shouldDelete; + using (var uow = DbHandler.UnitOfWork()) + { + shouldDelete = uow.GuildConfigs.For(channel.Guild.Id).DeleteMessageOnCommand; + } + + if (shouldDelete) + e.Message.DeleteAsync(); + } + catch (Exception ex) + { + _log.Warn(ex, "Delmsgoncmd errored..."); + } + } + ////todo owner only //[LocalizedCommand, LocalizedDescription, LocalizedSummary] //[RequireContext(ContextType.Guild)] @@ -36,7 +59,7 @@ namespace NadekoBot.Modules.Administration // System.Diagnostics.Process.Start(System.Reflection.Assembly.GetEntryAssembly().Location); // Environment.Exit(0); //} - + [LocalizedCommand, LocalizedDescription, LocalizedSummary] [RequireContext(ContextType.Guild)] [RequirePermission(GuildPermission.Administrator)] @@ -379,7 +402,6 @@ namespace NadekoBot.Modules.Administration public async Task CreatVoiChanl(IUserMessage umsg, [Remainder] string channelName) { var channel = (ITextChannel)umsg.Channel; - //todo actually print info about created channel var ch = await channel.Guild.CreateVoiceChannelAsync(channelName).ConfigureAwait(false); await channel.SendMessageAsync($"Created voice channel **{ch.Name}**, id `{ch.Id}`.").ConfigureAwait(false); } @@ -399,7 +421,6 @@ namespace NadekoBot.Modules.Administration public async Task CreaTxtChanl(IUserMessage umsg, [Remainder] string channelName) { var channel = (ITextChannel)umsg.Channel; - //todo actually print info about created channel var txtCh = await channel.Guild.CreateTextChannelAsync(channelName).ConfigureAwait(false); await channel.SendMessageAsync($"Added text channel **{txtCh.Name}**, id `{txtCh.Id}`.").ConfigureAwait(false); } diff --git a/src/NadekoBot/Modules/Administration/Commands/MessageRepeater.cs b/src/NadekoBot/Modules/Administration/Commands/MessageRepeater.cs index 520372db..dfefc505 100644 --- a/src/NadekoBot/Modules/Administration/Commands/MessageRepeater.cs +++ b/src/NadekoBot/Modules/Administration/Commands/MessageRepeater.cs @@ -1,130 +1,159 @@ ο»Ώusing Discord; using Discord.Commands; -using NadekoBot.Classes; -using NadekoBot.Modules.Permissions.Classes; +using Discord.WebSocket; +using NadekoBot.Attributes; +using NadekoBot.Services; +using NadekoBot.Services.Database; +using NadekoBot.Services.Database.Models; using System; using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Threading; using System.Threading.Tasks; -using System.Timers; -//todo DB namespace NadekoBot.Modules.Administration { - class MessageRepeater : DiscordCommand + public partial class Administration { - private readonly ConcurrentDictionary repeaters = new ConcurrentDictionary(); - private class Repeater + [Group] + public class RepeatCommands { - [Newtonsoft.Json.JsonIgnore] - public Timer MessageTimer { get; set; } - [Newtonsoft.Json.JsonIgnore] - public Channel RepeatingChannel { get; set; } + public ConcurrentDictionary repeaters; - public ulong RepeatingServerId { get; set; } - public ulong RepeatingChannelId { get; set; } - public Message lastMessage { get; set; } = null; - public string RepeatingMessage { get; set; } - public int Interval { get; set; } - - public Repeater Start() + public class RepeatRunner { - MessageTimer = new Timer { Interval = Interval }; - MessageTimer.Elapsed += async (s, e) => await Invoke(); - return this; - } + private CancellationTokenSource source { get; set; } + private CancellationToken token { get; set; } + public Repeater Repeater { get; } + public ITextChannel Channel { get; } - public async Task Invoke() - { - var ch = RepeatingChannel; - var msg = RepeatingMessage; - if (ch != null && !string.IsNullOrWhiteSpace(msg)) + public RepeatRunner(Repeater repeater, ITextChannel channel = null) { + this.Repeater = repeater; + this.Channel = channel ?? NadekoBot.Client.GetGuild(repeater.GuildId)?.GetTextChannel(repeater.ChannelId); + if (Channel == null) + return; + Task.Run(Run); + } + + + private async Task Run() + { + source = new CancellationTokenSource(); + token = source.Token; try { - if (lastMessage != null) - await lastMessage.Delete().ConfigureAwait(false); + while (!token.IsCancellationRequested) + { + await Task.Delay(Repeater.Interval, token).ConfigureAwait(false); + await Channel.SendMessageAsync("πŸ”„ " + Repeater.Message).ConfigureAwait(false); + } } - catch { } - try - { - lastMessage = await ch.SendMessageAsync(msg).ConfigureAwait(false); - } - catch { } + catch (OperationCanceledException) { } + } + + public void Reset() + { + source.Cancel(); + var t = Task.Run(Run); + } + + public void Stop() + { + source.Cancel(); } } - } - internal override void Init(CommandGroupBuilder cgb) - { - cgb.CreateCommand(Module.Prefix + "repeatinvoke") - .Alias(Module.Prefix + "repinv") - .Description($"Immediately shows the repeat message and restarts the timer. **Needs Manage Messages Permissions.**| `{Prefix}repinv`") - .AddCheck(SimpleCheckers.ManageMessages()) - .Do(async e => + public RepeatCommands() + { + using (var uow = DbHandler.UnitOfWork()) { - Repeater rep; - if (!repeaters.TryGetValue(e.Server, out rep)) - { - await channel.SendMessageAsync("`No repeating message found on this server.`"); - return; - } + repeaters = new ConcurrentDictionary(uow.Repeaters.GetAll().Select(r => new RepeatRunner(r)).Where(r => r != null).ToDictionary(r => r.Repeater.ChannelId)); + } + } - await rep.Invoke(); - }); + [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [RequireContext(ContextType.Guild)] + [RequirePermission(GuildPermission.ManageMessages)] + public async Task RepeatInvoke(IUserMessage imsg) + { + var channel = (ITextChannel)imsg.Channel; - cgb.CreateCommand(Module.Prefix + "repeat") - .Description("Repeat a message every X minutes. If no parameters are specified, " + - $"repeat is disabled. **Needs Manage Messages Permissions.** |`{Prefix}repeat 5 Hello there`") - .Parameter("minutes", ParameterType.Optional) - .Parameter("msg", ParameterType.Unparsed) - .AddCheck(SimpleCheckers.ManageMessages()) - .Do(async e => + RepeatRunner rep; + if (!repeaters.TryGetValue(channel.Id, out rep)) { - var minutesStr = minutes; - var msg = msg; + await channel.SendMessageAsync("`No repeating message found on this server.`").ConfigureAwait(false); + return; + } + rep.Reset(); + await channel.SendMessageAsync("πŸ”„ " + rep.Repeater.Message).ConfigureAwait(false); + } - // if both null, disable - if (string.IsNullOrWhiteSpace(msg) && string.IsNullOrWhiteSpace(minutesStr)) + [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [RequireContext(ContextType.Guild)] + public async Task Repeat(IUserMessage imsg) + { + var channel = (ITextChannel)imsg.Channel; + RepeatRunner rep; + if (repeaters.TryRemove(channel.Id, out rep)) + { + using (var uow = DbHandler.UnitOfWork()) { - - Repeater rep; - if (!repeaters.TryRemove(e.Server, out rep)) - return; - rep.MessageTimer.Stop(); - await channel.SendMessageAsync("Repeating disabled").ConfigureAwait(false); - return; + uow.Repeaters.Remove(rep.Repeater); + await uow.CompleteAsync(); } - int minutes; - if (!int.TryParse(minutesStr, out minutes) || minutes < 1 || minutes > 1440) + rep.Stop(); + await channel.SendMessageAsync("`Stopped repeating a message.`").ConfigureAwait(false); + } + else + await channel.SendMessageAsync("`No message is repeating.`").ConfigureAwait(false); + } + + [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [RequireContext(ContextType.Guild)] + public async Task Repeat(IUserMessage imsg, int minutes, [Remainder] string message) + { + var channel = (ITextChannel)imsg.Channel; + + if (minutes < 1 || minutes > 1500) + return; + + if (string.IsNullOrWhiteSpace(message)) + return; + + RepeatRunner rep; + + rep = repeaters.AddOrUpdate(channel.Id, (cid) => + { + using (var uow = DbHandler.UnitOfWork()) { - await channel.SendMessageAsync("Invalid value").ConfigureAwait(false); - return; - } - - var repeater = repeaters.GetOrAdd( - e.Server, - s => new Repeater + var localRep = new Repeater { - Interval = minutes * 60 * 1000, - RepeatingChannel = e.Channel, - RepeatingChannelId = e.Channel.Id, - RepeatingServerId = e.Server.Id, - }.Start() - ); - - if (!string.IsNullOrWhiteSpace(msg)) - repeater.RepeatingMessage = msg; - - repeater.MessageTimer.Stop(); - repeater.MessageTimer.Start(); - - await channel.SendMessageAsync(String.Format("πŸ‘Œ Repeating `{0}` every " + - "**{1}** minutes on {2} channel.", - repeater.RepeatingMessage, minutes, repeater.RepeatingChannel)) - .ConfigureAwait(false); + ChannelId = channel.Id, + GuildId = channel.Guild.Id, + Interval = TimeSpan.FromMinutes(minutes), + Message = message, + }; + uow.Repeaters.Add(localRep); + uow.Complete(); + return new RepeatRunner(localRep, channel); + } + }, (cid, old) => + { + using (var uow = DbHandler.UnitOfWork()) + { + old.Repeater.Message = message; + old.Repeater.Interval = TimeSpan.FromMinutes(minutes); + uow.Repeaters.Update(old.Repeater); + uow.Complete(); + } + old.Reset(); + return old; }); - } - public MessageRepeater(DiscordModule module) : base(module) { } + await channel.SendMessageAsync($"Repeating \"{rep.Repeater.Message}\" every {rep.Repeater.Interval} minutes").ConfigureAwait(false); + } + } } -} +} \ No newline at end of file diff --git a/src/NadekoBot/Modules/Administration/Commands/SelfAssignedRolesCommand.cs b/src/NadekoBot/Modules/Administration/Commands/SelfAssignedRolesCommand.cs index ccb62d6e..291e065b 100644 --- a/src/NadekoBot/Modules/Administration/Commands/SelfAssignedRolesCommand.cs +++ b/src/NadekoBot/Modules/Administration/Commands/SelfAssignedRolesCommand.cs @@ -10,7 +10,7 @@ using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; -//todo DB + namespace NadekoBot.Modules.Administration { public partial class Administration diff --git a/src/NadekoBot/Modules/ClashOfClans/ClashOfClans.cs b/src/NadekoBot/Modules/ClashOfClans/ClashOfClans.cs index 0cbc22b8..d2aa77a1 100644 --- a/src/NadekoBot/Modules/ClashOfClans/ClashOfClans.cs +++ b/src/NadekoBot/Modules/ClashOfClans/ClashOfClans.cs @@ -12,7 +12,6 @@ using NadekoBot.Services.Database.Models; using System.Linq; using NadekoBot.Services.Database; -//todo DB namespace NadekoBot.Modules.ClashOfClans { [Module(",", AppendSpace = false)] diff --git a/src/NadekoBot/Modules/Gambling/Commands/AnimalRacing.cs b/src/NadekoBot/Modules/Gambling/Commands/AnimalRacing.cs index 79f496ec..7aece0bc 100644 --- a/src/NadekoBot/Modules/Gambling/Commands/AnimalRacing.cs +++ b/src/NadekoBot/Modules/Gambling/Commands/AnimalRacing.cs @@ -41,17 +41,10 @@ namespace NadekoBot.Modules.Gambling if (amount < 0) amount = 0; - //todo DB - //var userFlowers = Gambling.GetUserFlowers(umsg.Author.Id); + if (amount > 0) + if(!await CurrencyHandler.RemoveCurrencyAsync((IGuildUser)umsg.Author, "BetRace", amount, true).ConfigureAwait(false)) + await channel.SendMessageAsync($"{umsg.Author.Mention} You don't have enough {Gambling.CurrencyName}s.").ConfigureAwait(false); - //if (userFlowers < amount) - //{ - // await channel.SendMessageAsync($"{umsg.Author.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(umsg.Author, "BetRace", (int)amount, true).ConfigureAwait(false); AnimalRace ar; if (!AnimalRaces.TryGetValue(channel.Guild.Id, out ar)) @@ -116,9 +109,9 @@ namespace NadekoBot.Modules.Gambling { await raceChannel.SendMessageAsync("🏁`Race failed to start since there was not enough participants.`"); var p = participants.FirstOrDefault(); - //todo DB - //if (p != null) - // await FlowersHandler.AddFlowersAsync(p.User, "BetRace", p.AmountBet, true).ConfigureAwait(false); + + if (p != null) + await CurrencyHandler.AddCurrencyAsync(p.User, "BetRace", p.AmountBet, true).ConfigureAwait(false); End(); return; } @@ -191,8 +184,8 @@ namespace NadekoBot.Modules.Gambling if (winner.AmountBet > 0) { var wonAmount = winner.AmountBet * (participants.Count - 1); - //todo DB - //await FlowersHandler.AddFlowersAsync(winner.User, "Won a Race", wonAmount).ConfigureAwait(false); + + await CurrencyHandler.AddCurrencyAsync(winner.User, "Won a Race", wonAmount, false).ConfigureAwait(false); await raceChannel.SendMessageAsync($"🏁 {winner.User.Mention} as {winner.Animal} **Won the race and {wonAmount}{CurrencySign}!**").ConfigureAwait(false); } else diff --git a/src/NadekoBot/Modules/Gambling/Commands/DiceRollCommand.cs b/src/NadekoBot/Modules/Gambling/Commands/DiceRollCommand.cs index 9741fa97..c3e9d9ed 100644 --- a/src/NadekoBot/Modules/Gambling/Commands/DiceRollCommand.cs +++ b/src/NadekoBot/Modules/Gambling/Commands/DiceRollCommand.cs @@ -13,97 +13,98 @@ namespace NadekoBot.Modules.Gambling public partial class Gambling { private Regex dndRegex { get; } = new Regex(@"(?\d+)d(?\d+)", RegexOptions.Compiled); - ////todo drawing - //[LocalizedCommand, LocalizedDescription, LocalizedSummary] - //[RequireContext(ContextType.Guild)] - //public Task Roll(IUserMessage umsg, [Remainder] string arg = null) => - // InternalRoll(umsg, arg, true); - //[LocalizedCommand, LocalizedDescription, LocalizedSummary] - //[RequireContext(ContextType.Guild)] - //public Task Rolluo(IUserMessage umsg, [Remainder] string arg = null) => - // InternalRoll(umsg, arg, false); + [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [RequireContext(ContextType.Guild)] + public Task Roll(IUserMessage umsg, [Remainder] string arg = null) => + InternalRoll(umsg, arg, true); - //private async Task InternalRoll(IUserMessage umsg, string arg, bool ordered) { - // var channel = (ITextChannel)umsg.Channel; - // var r = new Random(); - // if (string.IsNullOrWhiteSpace(arg)) - // { - // var gen = r.Next(0, 101); + [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [RequireContext(ContextType.Guild)] + public Task Rolluo(IUserMessage umsg, [Remainder] string arg = null) => + InternalRoll(umsg, arg, false); + //todo drawing + private async Task InternalRoll(IUserMessage umsg, string arg, bool ordered) + { + var channel = (ITextChannel)umsg.Channel; + var r = new Random(); + //if (string.IsNullOrWhiteSpace(arg)) + //{ + // var gen = r.Next(0, 101); - // var num1 = gen / 10; - // var num2 = gen % 10; + // var num1 = gen / 10; + // var num2 = gen % 10; - // var imageStream = await new Image[2] { GetDice(num1), GetDice(num2) }.Merge().ToStream(ImageFormat.Png); + // var imageStream = await new Image[2] { GetDice(num1), GetDice(num2) }.Merge().ToStream(ImageFormat.Png); - // await channel.SendFileAsync(imageStream, "dice.png").ConfigureAwait(false); - // return; - // } - // Match m; - // if ((m = dndRegex.Match(arg)).Length != 0) - // { - // int n1; - // int n2; - // if (int.TryParse(m.Groups["n1"].ToString(), out n1) && - // int.TryParse(m.Groups["n2"].ToString(), out n2) && - // n1 <= 50 && n2 <= 100000 && n1 > 0 && n2 > 0) - // { - // var arr = new int[n1]; - // for (int i = 0; i < n1; i++) - // { - // arr[i] = r.Next(1, n2 + 1); - // } - // var elemCnt = 0; - // await channel.SendMessageAsync($"`Rolled {n1} {(n1 == 1 ? "die" : "dice")} 1-{n2}.`\n`Result:` " + string.Join(", ", (ordered ? arr.OrderBy(x => x).AsEnumerable() : arr).Select(x => elemCnt++ % 2 == 0 ? $"**{x}**" : x.ToString()))).ConfigureAwait(false); - // } - // return; - // } - // try - // { - // var num = int.Parse(e.Args[0]); - // if (num < 1) num = 1; - // if (num > 30) - // { - // await channel.SendMessageAsync("You can roll up to 30 dice at a time.").ConfigureAwait(false); - // num = 30; - // } - // var dices = new List(num); - // var values = new List(num); - // for (var i = 0; i < num; i++) - // { - // var randomNumber = r.Next(1, 7); - // var toInsert = dices.Count; - // if (ordered) - // { - // if (randomNumber == 6 || dices.Count == 0) - // toInsert = 0; - // else if (randomNumber != 1) - // for (var j = 0; j < dices.Count; j++) - // { - // if (values[j] < randomNumber) - // { - // toInsert = j; - // break; - // } - // } - // } - // else - // { - // toInsert = dices.Count; - // } - // dices.Insert(toInsert, GetDice(randomNumber)); - // values.Insert(toInsert, randomNumber); - // } + // await channel.SendFileAsync(imageStream, "dice.png").ConfigureAwait(false); + // return; + //} + Match m; + if ((m = dndRegex.Match(arg)).Length != 0) + { + int n1; + int n2; + if (int.TryParse(m.Groups["n1"].ToString(), out n1) && + int.TryParse(m.Groups["n2"].ToString(), out n2) && + n1 <= 50 && n2 <= 100000 && n1 > 0 && n2 > 0) + { + var arr = new int[n1]; + for (int i = 0; i < n1; i++) + { + arr[i] = r.Next(1, n2 + 1); + } + var elemCnt = 0; + await channel.SendMessageAsync($"`Rolled {n1} {(n1 == 1 ? "die" : "dice")} 1-{n2}.`\n`Result:` " + string.Join(", ", (ordered ? arr.OrderBy(x => x).AsEnumerable() : arr).Select(x => elemCnt++ % 2 == 0 ? $"**{x}**" : x.ToString()))).ConfigureAwait(false); + } + return; + } + //try + //{ + // var num = int.Parse(e.Args[0]); + // if (num < 1) num = 1; + // if (num > 30) + // { + // await channel.SendMessageAsync("You can roll up to 30 dice at a time.").ConfigureAwait(false); + // num = 30; + // } + // var dices = new List(num); + // var values = new List(num); + // for (var i = 0; i < num; i++) + // { + // var randomNumber = r.Next(1, 7); + // var toInsert = dices.Count; + // if (ordered) + // { + // if (randomNumber == 6 || dices.Count == 0) + // toInsert = 0; + // else if (randomNumber != 1) + // for (var j = 0; j < dices.Count; j++) + // { + // if (values[j] < randomNumber) + // { + // toInsert = j; + // break; + // } + // } + // } + // else + // { + // toInsert = dices.Count; + // } + // dices.Insert(toInsert, GetDice(randomNumber)); + // values.Insert(toInsert, randomNumber); + // } - // var bitmap = dices.Merge(); - // await channel.SendMessageAsync(values.Count + " Dice rolled. Total: **" + values.Sum() + "** Average: **" + (values.Sum() / (1.0f * values.Count)).ToString("N2") + "**").ConfigureAwait(false); - // await channel.SendFileAsync("dice.png", bitmap.ToStream(ImageFormat.Png)).ConfigureAwait(false); - // } - // catch - // { - // await channel.SendMessageAsync("Please enter a number of dice to roll.").ConfigureAwait(false); - // } - //} + // var bitmap = dices.Merge(); + // await channel.SendMessageAsync(values.Count + " Dice rolled. Total: **" + values.Sum() + "** Average: **" + (values.Sum() / (1.0f * values.Count)).ToString("N2") + "**").ConfigureAwait(false); + // await channel.SendFileAsync("dice.png", bitmap.ToStream(ImageFormat.Png)).ConfigureAwait(false); + //} + //catch + //{ + // await channel.SendMessageAsync("Please enter a number of dice to roll.").ConfigureAwait(false); + //} + } [LocalizedCommand, LocalizedDescription, LocalizedSummary] [RequireContext(ContextType.Guild)] diff --git a/src/NadekoBot/Modules/Gambling/Commands/FlipCoinCommand.cs b/src/NadekoBot/Modules/Gambling/Commands/FlipCoinCommand.cs index 9f30701f..2afca817 100644 --- a/src/NadekoBot/Modules/Gambling/Commands/FlipCoinCommand.cs +++ b/src/NadekoBot/Modules/Gambling/Commands/FlipCoinCommand.cs @@ -1,113 +1,93 @@ -//using Discord.Commands; -//using NadekoBot.Classes; -//using NadekoBot.Extensions; -//using System; -//using System.Drawing; -//using System.Threading.Tasks; +using Discord; +using Discord.Commands; -////todo drawing -//namespace NadekoBot.Modules.Gambling -//{ -// internal class FlipCoinCommand : DiscordCommand -// { +//todo drawing +namespace NadekoBot.Modules.Gambling +{ + [Group] + public class FlipCoinCommands + { -// public FlipCoinCommand(DiscordModule module) : base(module) { } - -// internal override void Init(CommandGroupBuilder cgb) -// { -// cgb.CreateCommand(Module.Prefix + "flip") -// .Description($"Flips coin(s) - heads or tails, and shows an image. | `{Prefix}flip` or `{Prefix}flip 3`") -// .Parameter("count", ParameterType.Optional) -// .Do(FlipCoinFunc()); - -// cgb.CreateCommand(Module.Prefix + "betflip") -// .Alias(Prefix+"bf") -// .Description($"Bet to guess will the result be heads or tails. Guessing award you double flowers you've bet. | `{Prefix}bf 5 heads` or `{Prefix}bf 3 t`") -// .Parameter("amount", ParameterType.Required) -// .Parameter("guess", ParameterType.Required) -// .Do(BetFlipCoinFunc()); -// } + public FlipCoinCommands() { } + ////todo drawing + //[LocalizedCommand, LocalizedDescription, LocalizedSummary] + //[RequireContext(ContextType.Guild)] + //public async Task Flip(IUserMessage imsg, int count = 0) + //{ + // var channel = (ITextChannel)imsg.Channel; + // if (count == 0) + // { + // if (rng.Next(0, 2) == 1) + // await channel.SendFileAsync("heads.png", ).ConfigureAwait(false); + // else + // await channel.SendFileAsync("tails.png", ).ConfigureAwait(false); + // return; + // } + // if (result > 10) + // result = 10; + // var imgs = new Image[result]; + // for (var i = 0; i < result; i++) + // { + // imgs[i] = rng.Next(0, 2) == 0 ? + // Properties.Resources.tails : + // Properties.Resources.heads; + // } + // await channel.SendFile($"{result} coins.png", imgs.Merge().ToStream(System.Drawing.Imaging.ImageFormat.Png)).ConfigureAwait(false); + // return; + // await channel.SendMessageAsync("Invalid number").ConfigureAwait(false); + //} -// private readonly Random rng = new Random(); -// public Func BetFlipCoinFunc() => async e => -// { + //[LocalizedCommand, LocalizedDescription, LocalizedSummary] + //[RequireContext(ContextType.Guild)] + //public async Task Betflip(IUserMessage umsg, int amount, string guess) + //{ + // var channel = (ITextChannel)umsg.Channel; + // var guildUser = (IGuildUser)umsg.Author; + // var guessStr = guess.Trim().ToUpperInvariant(); + // if (guessStr != "H" && guessStr != "T" && guessStr != "HEADS" && guessStr != "TAILS") + // return; + + // if (amount < 1) + // return; -// var amountstr = amount.Trim(); + // var userFlowers = Gambling.GetUserFlowers(umsg.Author.Id); -// var guessStr = guess.Trim().ToUpperInvariant(); -// if (guessStr != "H" && guessStr != "T" && guessStr != "HEADS" && guessStr != "TAILS") -// return; + // if (userFlowers < amount) + // { + // await channel.SendMessageAsync($"{umsg.Author.Mention} You don't have enough {Gambling.CurrencyName}s. You only have {userFlowers}{Gambling.CurrencySign}.").ConfigureAwait(false); + // return; + // } -// int amount; -// if (!int.TryParse(amountstr, out amount) || amount < 1) -// return; + // await CurrencyHandler.RemoveCurrencyAsync(guildUser, "Betflip Gamble", amount, false).ConfigureAwait(false); + // //heads = true + // //tails = false -// var userFlowers = Gambling.GetUserFlowers(umsg.Author.Id); + // var isHeads = guessStr == "HEADS" || guessStr == "H"; + // bool result = false; + // var rng = new Random(); + // if (rng.Next(0, 2) == 1) + // { + // await channel.SendFileAsync("heads.png", Properties.Resources.heads.ToStream(System.Drawing.Imaging.ImageFormat.Png)).ConfigureAwait(false); + // result = true; + // } + // else + // { + // await channel.SendFileAsync("tails.png", Properties.Resources.tails.ToStream(System.Drawing.Imaging.ImageFormat.Png)).ConfigureAwait(false); + // } -// if (userFlowers < amount) -// { -// await channel.SendMessageAsync($"{umsg.Author.Mention} You don't have enough {NadekoBot.Config.CurrencyName}s. You only have {userFlowers}{NadekoBot.Config.CurrencySign}.").ConfigureAwait(false); -// return; -// } + // string str; + // if (isHeads == result) + // { + // str = $"{umsg.Author.Mention}`You guessed it!` You won {amount * 2}{Gambling.CurrencySign}"; + // await CurrencyHandler.AddCurrencyAsync((IGuildUser)umsg.Author, "Betflip Gamble", amount * 2, false).ConfigureAwait(false); -// await FlowersHandler.RemoveFlowers(umsg.Author, "Betflip Gamble", (int)amount, true).ConfigureAwait(false); -// //heads = true -// //tails = false + // } + // else + // str = $"{umsg.Author.Mention}`More luck next time.`"; -// var guess = guessStr == "HEADS" || guessStr == "H"; -// bool result = false; -// if (rng.Next(0, 2) == 1) { -// await e.Channel.SendFile("heads.png", Properties.Resources.heads.ToStream(System.Drawing.Imaging.ImageFormat.Png)).ConfigureAwait(false); -// result = true; -// } -// else { -// await e.Channel.SendFile("tails.png", Properties.Resources.tails.ToStream(System.Drawing.Imaging.ImageFormat.Png)).ConfigureAwait(false); -// } - -// string str; -// if (guess == result) -// { -// str = $"{umsg.Author.Mention}`You guessed it!` You won {amount * 2}{NadekoBot.Config.CurrencySign}"; -// await FlowersHandler.AddFlowersAsync(umsg.Author, "Betflip Gamble", amount * 2, true).ConfigureAwait(false); - -// } -// else -// str = $"{umsg.Author.Mention}`More luck next time.`"; - -// await channel.SendMessageAsync(str).ConfigureAwait(false); -// }; - -// public Func FlipCoinFunc() => async e => -// { - -// if (count == "") -// { -// if (rng.Next(0, 2) == 1) -// await e.Channel.SendFile("heads.png", Properties.Resources.heads.ToStream(System.Drawing.Imaging.ImageFormat.Png)).ConfigureAwait(false); -// else -// await e.Channel.SendFile("tails.png", Properties.Resources.tails.ToStream(System.Drawing.Imaging.ImageFormat.Png)).ConfigureAwait(false); -// } -// else -// { -// int result; -// if (int.TryParse(count, out result)) -// { -// if (result > 10) -// result = 10; -// var imgs = new Image[result]; -// for (var i = 0; i < result; i++) -// { -// imgs[i] = rng.Next(0, 2) == 0 ? -// Properties.Resources.tails : -// Properties.Resources.heads; -// } -// await e.Channel.SendFile($"{result} coins.png", imgs.Merge().ToStream(System.Drawing.Imaging.ImageFormat.Png)).ConfigureAwait(false); -// return; -// } -// await channel.SendMessageAsync("Invalid number").ConfigureAwait(false); -// } -// }; -// } -//} + // await channel.SendMessageAsync(str).ConfigureAwait(false); + //} + } +} diff --git a/src/NadekoBot/Modules/Gambling/Gambling.cs b/src/NadekoBot/Modules/Gambling/Gambling.cs index 87a07fab..73eacc19 100644 --- a/src/NadekoBot/Modules/Gambling/Gambling.cs +++ b/src/NadekoBot/Modules/Gambling/Gambling.cs @@ -9,8 +9,9 @@ using System.Threading.Tasks; using NadekoBot.Services; using Discord.WebSocket; using NadekoBot.Services.Database; +using NadekoBot.Services.Database.Models; +using System.Collections.Generic; -//todo DB namespace NadekoBot.Modules.Gambling { [Module("$", AppendSpace = false)] @@ -45,46 +46,51 @@ namespace NadekoBot.Modules.Gambling var membersArray = members as IUser[] ?? members.ToArray(); var usr = membersArray[new Random().Next(0, membersArray.Length)]; await channel.SendMessageAsync($"**Raffled user:** {usr.Username} (id: {usr.Id})").ConfigureAwait(false); + } + + [LocalizedCommand("$$$"), LocalizedDescription("$$$"), LocalizedSummary("$$$")] + [RequireContext(ContextType.Guild)] + public async Task Cash(IUserMessage umsg, [Remainder] IUser user = null) + { + var channel = (ITextChannel)umsg.Channel; + user = user ?? umsg.Author; + long amount; + BotConfig config; + using (var uow = DbHandler.UnitOfWork()) + { + amount = uow.Currency.GetUserCurrency(user.Id); + config = uow.BotConfig.GetOrCreate(); + } + + await channel.SendMessageAsync($"{user.Username} has {amount} {config.CurrencySign}").ConfigureAwait(false); + } + + [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [RequireContext(ContextType.Guild)] + public async Task Give(IUserMessage umsg, long amount, [Remainder] IUser 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(); + } + if (!success) + { + await channel.SendMessageAsync($"{umsg.Author.Mention} You don't have enough {Gambling.CurrencyPluralName}s.").ConfigureAwait(false); + return; + } + + await channel.SendMessageAsync($"{umsg.Author.Mention} successfully sent {amount} {Gambling.CurrencyPluralName}s to {receiver.Mention}!").ConfigureAwait(false); } - ////todo DB - //[LocalizedCommand("$$$"), LocalizedDescription("$$$"), LocalizedSummary("$$$")] - //[RequireContext(ContextType.Guild)] - //public async Task Cash(IUserMessage umsg, [Remainder] string arg) - //{ - // var channel = (ITextChannel)umsg.Channel; - - // var usr = e.Message.MentionedUsers.FirstOrDefault() ?? umsg.Author; - // var pts = GetUserFlowers(usr.Id); - // var str = $"{usr.Name} has {pts} {NadekoBot.Config.CurrencySign}"; - // await channel.SendMessageAsync(str).ConfigureAwait(false); - //} - - ////todo DB - //[LocalizedCommand, LocalizedDescription, LocalizedSummary] - //[RequireContext(ContextType.Guild)] - //public async Task Give(IUserMessage umsg, long amount, [Remainder] IUser receiver) - //{ - // var channel = (ITextChannel)umsg.Channel; - // if (amount <= 0) - // return; - // var userFlowers = GetUserFlowers(umsg.Author.Id); - - // if (userFlowers < amount) - // { - // await channel.SendMessageAsync($"{umsg.Author.Mention} You don't have enough {NadekoBot.Config.CurrencyName}s. You only have {userFlowers}{NadekoBot.Config.CurrencySign}.").ConfigureAwait(false); - // return; - // } - - // await FlowersHandler.RemoveFlowers(umsg.Author, "Gift", (int)amount, true).ConfigureAwait(false); - // await FlowersHandler.AddFlowersAsync(receiver, "Gift", (int)amount).ConfigureAwait(false); - - // await channel.SendMessageAsync($"{umsg.Author.Mention} successfully sent {amount} {NadekoBot.Config.CurrencyName}s to {receiver.Mention}!").ConfigureAwait(false); - - //} - - ////todo DB ////todo owner only //[LocalizedCommand, LocalizedDescription, LocalizedSummary] //[RequireContext(ContextType.Guild)] @@ -100,18 +106,18 @@ namespace NadekoBot.Modules.Gambling // if (amount <= 0) // return; - // await FlowersHandler.AddFlowersAsync(usrId, $"Awarded by bot owner. ({umsg.Author.Username}/{umsg.Author.Id})", (int)amount).ConfigureAwait(false); + // await CurrencyHandler.AddFlowersAsync(usrId, $"Awarded by bot owner. ({umsg.Author.Username}/{umsg.Author.Id})", (int)amount).ConfigureAwait(false); - // await channel.SendMessageAsync($"{umsg.Author.Mention} successfully awarded {amount} {NadekoBot.Config.CurrencyName}s to <@{usrId}>!").ConfigureAwait(false); + // await channel.SendMessageAsync($"{umsg.Author.Mention} successfully awarded {amount} {Gambling.CurrencyName}s to <@{usrId}>!").ConfigureAwait(false); //} ////todo owner only - ////todo DB //[LocalizedCommand, LocalizedDescription, LocalizedSummary] //[RequireContext(ContextType.Guild)] //public Task Take(IUserMessage umsg, long amount, [Remainder] IGuildUser user) => // Take(umsg, amount, user.Id); + //todo owner only //[LocalizedCommand, LocalizedDescription, LocalizedSummary] //[RequireContext(ContextType.Guild)] //public async Task Take(IUserMessage umsg, long amount, [Remainder] ulong usrId) @@ -120,76 +126,84 @@ namespace NadekoBot.Modules.Gambling // if (amount <= 0) // return; - // await FlowersHandler.RemoveFlowers(usrId, $"Taken by bot owner.({umsg.Author.Username}/{umsg.Author.Id})", (int)amount).ConfigureAwait(false); + // await CurrencyHandler.RemoveFlowers(usrId, $"Taken by bot owner.({umsg.Author.Username}/{umsg.Author.Id})", (int)amount).ConfigureAwait(false); - // await channel.SendMessageAsync($"{umsg.Author.Mention} successfully took {amount} {NadekoBot.Config.CurrencyName}s from <@{usrId}>!").ConfigureAwait(false); + // await channel.SendMessageAsync($"{umsg.Author.Mention} successfully took {amount} {Gambling.CurrencyName}s from <@{usrId}>!").ConfigureAwait(false); //} - //[LocalizedCommand, LocalizedDescription, LocalizedSummary] - //[RequireContext(ContextType.Guild)] - //public async Task BetRoll(IUserMessage umsg, int amount) - //{ - // var channel = (ITextChannel)umsg.Channel; + [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [RequireContext(ContextType.Guild)] + public async Task BetRoll(IUserMessage umsg, long amount) + { + var channel = (ITextChannel)umsg.Channel; - // if (amount < 1) - // return; + if (amount < 1) + return; - // var userFlowers = GetUserFlowers(umsg.Author.Id); + var guildUser = (IGuildUser)umsg.Author; - // if (userFlowers < amount) - // { - // await channel.SendMessageAsync($"{umsg.Author.Mention} You don't have enough {NadekoBot.Config.CurrencyName}s. You only have {userFlowers}{NadekoBot.Config.CurrencySign}.").ConfigureAwait(false); - // return; - // } + long userFlowers; + using (var uow = DbHandler.UnitOfWork()) + { + userFlowers = uow.Currency.GetOrCreate(umsg.Id).Amount; + } - // await FlowersHandler.RemoveFlowers(umsg.Author, "Betroll Gamble", (int)amount, true).ConfigureAwait(false); + if (userFlowers < amount) + { + await channel.SendMessageAsync($"{guildUser.Mention} You don't have enough {Gambling.CurrencyName}s. You only have {userFlowers}{Gambling.CurrencySign}.").ConfigureAwait(false); + return; + } - // var rng = new Random().Next(0, 101); - // var str = $"{umsg.Author.Mention} `You rolled {rng}.` "; - // if (rng < 67) - // { - // str += "Better luck next time."; - // } - // else if (rng < 90) - // { - // str += $"Congratulations! You won {amount * 2}{NadekoBot.Config.CurrencySign} for rolling above 66"; - // await FlowersHandler.AddFlowersAsync(umsg.Author, "Betroll Gamble", amount * 2, true).ConfigureAwait(false); - // } - // else if (rng < 100) - // { - // str += $"Congratulations! You won {amount * 3}{NadekoBot.Config.CurrencySign} for rolling above 90."; - // await FlowersHandler.AddFlowersAsync(umsg.Author, "Betroll Gamble", amount * 3, true).ConfigureAwait(false); - // } - // else - // { - // str += $"πŸ‘‘ Congratulations! You won {amount * 10}{NadekoBot.Config.CurrencySign} for rolling **100**. πŸ‘‘"; - // await FlowersHandler.AddFlowersAsync(umsg.Author, "Betroll Gamble", amount * 10, true).ConfigureAwait(false); - // } + await CurrencyHandler.RemoveCurrencyAsync(guildUser, "Betroll Gamble", amount, false).ConfigureAwait(false); - // await channel.SendMessageAsync(str).ConfigureAwait(false); - //} + var rng = new Random().Next(0, 101); + var str = $"{guildUser.Mention} `You rolled {rng}.` "; + if (rng < 67) + { + str += "Better luck next time."; + } + else if (rng < 90) + { + str += $"Congratulations! You won {amount * 2}{Gambling.CurrencySign} for rolling above 66"; + await CurrencyHandler.AddCurrencyAsync(guildUser, "Betroll Gamble", amount * 2, false).ConfigureAwait(false); + } + else if (rng < 100) + { + str += $"Congratulations! You won {amount * 3}{Gambling.CurrencySign} for rolling above 90."; + await CurrencyHandler.AddCurrencyAsync(guildUser, "Betroll Gamble", amount * 3, false).ConfigureAwait(false); + } + else + { + str += $"πŸ‘‘ Congratulations! You won {amount * 10}{Gambling.CurrencySign} for rolling **100**. πŸ‘‘"; + await CurrencyHandler.AddCurrencyAsync(guildUser, "Betroll Gamble", amount * 10, false).ConfigureAwait(false); + } - ////todo DB -// [LocalizedCommand, LocalizedDescription, LocalizedSummary] -// [RequireContext(ContextType.Guild)] -// public async Task Leaderboard(IUserMessage umsg) -// { -// var channel = (ITextChannel)umsg.Channel; + await channel.SendMessageAsync(str).ConfigureAwait(false); + } -// var richestTemp = DbHandler.Instance.GetTopRichest(); -// var richest = richestTemp as CurrencyState[] ?? richestTemp.ToArray(); -// if (richest.Length == 0) -// return; -// await channel.SendMessageAsync( -// richest.Aggregate(new StringBuilder( -//$@"```xl -//┏━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━┓ -//┃ Id ┃ $$$ ┃ -//"), -// (cur, cs) => cur.AppendLine( -//$@"┣━━━━━━━━━━━━━━━━━━━━━╋━━━━━━━┫ -//┃{(e.Server.Users.Where(u => u.Id == (ulong)cs.UserId).FirstOrDefault()?.Name.TrimTo(18, true) ?? cs.UserId.ToString()),-20} ┃ {cs.Value,5} ┃") -// ).ToString() + "┗━━━━━━━━━━━━━━━━━━━━━┻━━━━━━━┛```").ConfigureAwait(false); - //} + [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [RequireContext(ContextType.Guild)] + public async Task Leaderboard(IUserMessage umsg) + { + var channel = (ITextChannel)umsg.Channel; + + IEnumerable richest; + using (var uow = DbHandler.UnitOfWork()) + { + richest = uow.Currency.GetTopRichest(10); + } + if (!richest.Any()) + return; + await channel.SendMessageAsync( + richest.Aggregate(new StringBuilder( +$@"```xl + ┏━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━┓ + ┃ Id ┃ $$$ ┃ + "), + (cur, cs) => cur.AppendLine( +$@"┣━━━━━━━━━━━━━━━━━━━━━╋━━━━━━━┫ + ┃{(channel.Guild.GetUser(cs.UserId)?.Username.TrimTo(18, true) ?? cs.UserId.ToString()),-20} ┃ {cs,5} ┃") + ).ToString() + "┗━━━━━━━━━━━━━━━━━━━━━┻━━━━━━━┛```").ConfigureAwait(false); + } } } diff --git a/src/NadekoBot/Modules/Games/Commands/PlantAndPickCommands.cs b/src/NadekoBot/Modules/Games/Commands/PlantAndPickCommands.cs index 6b144495..3700813e 100644 --- a/src/NadekoBot/Modules/Games/Commands/PlantAndPickCommands.cs +++ b/src/NadekoBot/Modules/Games/Commands/PlantAndPickCommands.cs @@ -12,8 +12,7 @@ //using System.Threading; //using System.Threading.Tasks; -////todo DI into partials -////todo DB +////todo rewrite //namespace NadekoBot.Modules.Games //{ // /// @@ -51,7 +50,7 @@ // var rnd = Math.Abs(rng.Next(0,101)); // if (rnd == 0) // { -// var msgs = new[] { await e.Channel.SendFile(GetRandomCurrencyImagePath()), await channel.SendMessageAsync($"❗ A random {NadekoBot.Config.CurrencyName} appeared! Pick it up by typing `>pick`") }; +// 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); // } @@ -79,8 +78,8 @@ // foreach(var msgToDelete in msgs) // await msgToDelete.Delete().ConfigureAwait(false); -// await FlowersHandler.AddFlowersAsync(umsg.Author, "Picked a flower.", 1, true).ConfigureAwait(false); -// var msg = await channel.SendMessageAsync($"**{umsg.Author.Username}** picked a {NadekoBot.Config.CurrencyName}!").ConfigureAwait(false); +// 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 @@ -101,24 +100,24 @@ // { // if (plantedFlowerChannels.ContainsKey(e.Channel.Id)) // { -// await channel.SendMessageAsync($"There is already a {NadekoBot.Config.CurrencyName} in this channel.").ConfigureAwait(false); +// await channel.SendMessageAsync($"There is already a {Gambling.CurrencyName} in this channel.").ConfigureAwait(false); // return; // } -// var removed = await FlowersHandler.RemoveFlowers(umsg.Author, "Planted a flower.", 1, true).ConfigureAwait(false); +// var removed = await CurrencyHandler.RemoveFlowers(umsg.Author, "Planted a flower.", 1, true).ConfigureAwait(false); // if (!removed) // { -// await channel.SendMessageAsync($"You don't have any {NadekoBot.Config.CurrencyName}s.").ConfigureAwait(false); +// await channel.SendMessageAsync($"You don't have any {Gambling.CurrencyName}s.").ConfigureAwait(false); // return; // } // var file = GetRandomCurrencyImagePath(); // Message msg; // if (file == null) -// msg = await channel.SendMessageAsync(NadekoBot.Config.CurrencySign).ConfigureAwait(false); +// 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(NadekoBot.Config.CurrencyName[0]); -// var msg2 = await channel.SendMessageAsync($"Oh how Nice! **{umsg.Author.Username}** planted {(vowelFirst ? "an" : "a")} {NadekoBot.Config.CurrencyName}. Pick it using {Module.Prefix}pick").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(); } @@ -126,7 +125,7 @@ // cgb.CreateCommand(Prefix + "gencurrency") // .Alias(Prefix + "gc") -// .Description($"Toggles currency generation on this channel. Every posted message will have 2% chance to spawn a {NadekoBot.Config.CurrencyName}. Optional parameter cooldown time in minutes, 5 minutes by default. Requires Manage Messages permission. | `{Prefix}gc` or `{Prefix}gc 60`") +// .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 => diff --git a/src/NadekoBot/Modules/Games/Commands/PollCommands.cs b/src/NadekoBot/Modules/Games/Commands/PollCommands.cs index 0e579ff9..e8b3711c 100644 --- a/src/NadekoBot/Modules/Games/Commands/PollCommands.cs +++ b/src/NadekoBot/Modules/Games/Commands/PollCommands.cs @@ -12,7 +12,6 @@ namespace NadekoBot.Modules.Games { public partial class GamesModule { - //todo DB in the future public static ConcurrentDictionary ActivePolls = new ConcurrentDictionary(); [LocalizedCommand, LocalizedDescription, LocalizedSummary] diff --git a/src/NadekoBot/Modules/Games/Commands/Trivia/TriviaGame.cs b/src/NadekoBot/Modules/Games/Commands/Trivia/TriviaGame.cs index a86f2558..ac20336c 100644 --- a/src/NadekoBot/Modules/Games/Commands/Trivia/TriviaGame.cs +++ b/src/NadekoBot/Modules/Games/Commands/Trivia/TriviaGame.cs @@ -7,8 +7,7 @@ using System.Text; using System.Threading; using System.Threading.Tasks; -// todo rewrite? -// todo DB +// todo rewrite namespace NadekoBot.Modules.Games.Trivia { public class TriviaGame diff --git a/src/NadekoBot/Modules/Games/Commands/Trivia/TriviaQuestionPool.cs b/src/NadekoBot/Modules/Games/Commands/Trivia/TriviaQuestionPool.cs index fd21086f..bb684141 100644 --- a/src/NadekoBot/Modules/Games/Commands/Trivia/TriviaQuestionPool.cs +++ b/src/NadekoBot/Modules/Games/Commands/Trivia/TriviaQuestionPool.cs @@ -9,7 +9,6 @@ namespace NadekoBot.Modules.Games.Trivia public class TriviaQuestionPool { public static TriviaQuestionPool Instance { get; } = new TriviaQuestionPool(); - //todo DB public HashSet pool = new HashSet(); private Random rng { get; } = new Random(); @@ -30,7 +29,7 @@ namespace NadekoBot.Modules.Games.Trivia internal void Reload() { - var arr = JArray.Parse(File.ReadAllText("data/questions.json")); + var arr = JArray.Parse(File.ReadAllText("data/triviaquestions.json")); foreach (var item in arr) { diff --git a/src/NadekoBot/Modules/Games/Games.cs b/src/NadekoBot/Modules/Games/Games.cs index 4b29e7da..071ec72c 100644 --- a/src/NadekoBot/Modules/Games/Games.cs +++ b/src/NadekoBot/Modules/Games/Games.cs @@ -15,7 +15,6 @@ namespace NadekoBot.Modules.Games [Module(">", AppendSpace = false)] public partial class Games : DiscordModule { - //todo DB private IEnumerable _8BallResponses { get { using (var uow = DbHandler.UnitOfWork()) diff --git a/src/NadekoBot/Modules/Music/Classes/Song.cs b/src/NadekoBot/Modules/Music/Classes/Song.cs index f6827d2a..9ec3c1db 100644 --- a/src/NadekoBot/Modules/Music/Classes/Song.cs +++ b/src/NadekoBot/Modules/Music/Classes/Song.cs @@ -84,7 +84,7 @@ namespace NadekoBot.Modules.Music.Classes { var filename = Path.Combine(Music.MusicDataPath, DateTime.Now.UnixTimestamp().ToString()); - SongBuffer inStream = new SongBuffer(MusicPlayer, filename, SongInfo, skipTo); + SongBuffer inStream = new SongBuffer(MusicPlayer, filename, SongInfo, skipTo, frameBytes * 100); var bufferTask = inStream.BufferSong(cancelToken).ConfigureAwait(false); bytesSent = 0; @@ -118,9 +118,10 @@ namespace NadekoBot.Modules.Music.Classes while (!cancelToken.IsCancellationRequested) { //Console.WriteLine($"Read: {songBuffer.ReadPosition}\nWrite: {songBuffer.WritePosition}\nContentLength:{songBuffer.ContentLength}\n---------"); - var read = inStream.Read(buffer, 0, buffer.Length); + var read = await inStream.ReadAsync(buffer, 0, buffer.Length).ConfigureAwait(false); //await inStream.CopyToAsync(voiceClient.OutputStream); - _log.Debug("read {0}", read); + if(read < frameBytes) + _log.Debug("read {0}", read); unchecked { bytesSent += (ulong)read; @@ -155,7 +156,7 @@ namespace NadekoBot.Modules.Music.Classes int delayMillis = unchecked(nextTime - Environment.TickCount); if (delayMillis > 0) await Task.Delay(delayMillis, cancelToken).ConfigureAwait(false); - await outStream.WriteAsync(buffer, 0, read); + await outStream.WriteAsync(buffer, 0, read).ConfigureAwait(false); } } finally diff --git a/src/NadekoBot/Modules/Music/Classes/SongBuffer.cs b/src/NadekoBot/Modules/Music/Classes/SongBuffer.cs index a53d7d0b..0979f504 100644 --- a/src/NadekoBot/Modules/Music/Classes/SongBuffer.cs +++ b/src/NadekoBot/Modules/Music/Classes/SongBuffer.cs @@ -1,4 +1,5 @@ ο»Ώusing NadekoBot.Extensions; +using NLog; using System; using System.Collections.Concurrent; using System.Collections.Generic; @@ -17,14 +18,15 @@ namespace NadekoBot.Modules.Music.Classes /// class SongBuffer : Stream { - - public SongBuffer(MusicPlayer musicPlayer, string basename, SongInfo songInfo, int skipTo) + public SongBuffer(MusicPlayer musicPlayer, string basename, SongInfo songInfo, int skipTo, int maxFileSize) { MusicPlayer = musicPlayer; Basename = basename; SongInfo = songInfo; SkipTo = skipTo; + MaxFileSize = maxFileSize; CurrentFileStream = new FileStream(this.GetNextFile(), FileMode.OpenOrCreate, FileAccess.Read, FileShare.Write); + _log = LogManager.GetCurrentClassLogger(); } MusicPlayer MusicPlayer; @@ -35,7 +37,7 @@ namespace NadekoBot.Modules.Music.Classes private int SkipTo; - private static int MAX_FILE_SIZE = 2.MiB(); + private int MaxFileSize = 2.MiB(); private long FileNumber = -1; @@ -46,9 +48,10 @@ namespace NadekoBot.Modules.Music.Classes private ulong CurrentBufferSize = 0; private FileStream CurrentFileStream; + private Logger _log; public Task BufferSong(CancellationToken cancelToken) => - Task.Factory.StartNew(async () => + Task.Run(async () => { Process p = null; FileStream outStream = null; @@ -72,7 +75,7 @@ namespace NadekoBot.Modules.Music.Classes while (!p.HasExited) //Also fix low bandwidth { int bytesRead = await p.StandardOutput.BaseStream.ReadAsync(buffer, 0, buffer.Length, cancelToken).ConfigureAwait(false); - if (currentFileSize >= MAX_FILE_SIZE) + if (currentFileSize >= MaxFileSize) { try { @@ -122,7 +125,7 @@ Check the guides for your platform on how to setup ffmpeg correctly: p.Dispose(); } } - }, TaskCreationOptions.LongRunning); + }); /// /// Return the next file to read, and delete the old one @@ -172,18 +175,7 @@ Check the guides for your platform on how to setup ffmpeg correctly: public override long Length => (long) CurrentBufferSize; - public override long Position - { - get - { - return 0; - } - - set - { - - } - } + public override long Position { get; set; } = 0; public override void Flush() { } @@ -198,6 +190,8 @@ Check the guides for your platform on how to setup ffmpeg correctly: CurrentFileStream = new FileStream(GetNextFile(), FileMode.OpenOrCreate, FileAccess.Read, FileShare.Write); read += CurrentFileStream.Read(buffer, read + offset, count - read); } + if (read < count) + Array.Clear(buffer, read, count - read); } return read; } diff --git a/src/NadekoBot/Modules/Music/Music.cs b/src/NadekoBot/Modules/Music/Music.cs index 2bb4d62c..4f7572ce 100644 --- a/src/NadekoBot/Modules/Music/Music.cs +++ b/src/NadekoBot/Modules/Music/Music.cs @@ -13,6 +13,7 @@ using NadekoBot.Extensions; using System.Net.Http; using Newtonsoft.Json.Linq; using System.Collections.Generic; +using NadekoBot.Services.Database; namespace NadekoBot.Modules.Music { @@ -181,23 +182,25 @@ namespace NadekoBot.Modules.Music var volume = musicPlayer.SetVolume(val); await channel.SendMessageAsync($"🎡 `Volume set to {volume}%`").ConfigureAwait(false); } - ////todo DB - //[LocalizedCommand, LocalizedDescription, LocalizedSummary] - //[RequireContext(ContextType.Guild)] - //public async Task Defvol(IUserMessage umsg, [Remainder] int val) - //{ - // var channel = (ITextChannel)umsg.Channel; - // var arg = val; - // float volume; - // if (!float.TryParse(arg, out volume) || volume < 0 || volume > 100) - // { - // await channel.SendMessageAsync("Volume number invalid.").ConfigureAwait(false); - // return; - // } - // var conf = SpecificConfigurations.Default.Of(channel.Guild.Id); - // conf.DefaultMusicVolume = volume / 100; - // await channel.SendMessageAsync($"🎡 `Default volume set to {volume}%`").ConfigureAwait(false); - //} + + [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [RequireContext(ContextType.Guild)] + public async Task Defvol(IUserMessage umsg, [Remainder] int val) + { + var channel = (ITextChannel)umsg.Channel; + + if (val < 0 || val > 100) + { + await channel.SendMessageAsync("Volume number invalid. Must be between 0 and 100").ConfigureAwait(false); + return; + } + using (var uow = DbHandler.UnitOfWork()) + { + uow.GuildConfigs.For(channel.Guild.Id).DefaultMusicVolume = val / 100.0f; + uow.Complete(); + } + await channel.SendMessageAsync($"🎡 `Default volume set to {val}%`").ConfigureAwait(false); + } [LocalizedCommand, LocalizedDescription, LocalizedSummary] [RequireContext(ContextType.Guild)] @@ -643,8 +646,11 @@ namespace NadekoBot.Modules.Music var musicPlayer = MusicPlayers.GetOrAdd(textCh.Guild.Id, server => { - //todo DB float vol = 1;// SpecificConfigurations.Default.Of(server.Id).DefaultMusicVolume; + using (var uow = DbHandler.UnitOfWork()) + { + vol = uow.GuildConfigs.For(textCh.Guild.Id).DefaultMusicVolume; + } var mp = new MusicPlayer(voiceCh, vol); diff --git a/src/NadekoBot/Modules/Searches/Commands/JokeCommands.cs b/src/NadekoBot/Modules/Searches/Commands/JokeCommands.cs index 9f01b70f..bb24cee1 100644 --- a/src/NadekoBot/Modules/Searches/Commands/JokeCommands.cs +++ b/src/NadekoBot/Modules/Searches/Commands/JokeCommands.cs @@ -19,7 +19,6 @@ namespace NadekoBot.Modules.Searches [Group] public class JokeCommands { - //todo DB private List wowJokes = new List(); private List magicItems; private Logger _log; diff --git a/src/NadekoBot/Modules/Searches/Commands/PokemonSearchCommands.cs b/src/NadekoBot/Modules/Searches/Commands/PokemonSearchCommands.cs index 38e737d2..6449ceb6 100644 --- a/src/NadekoBot/Modules/Searches/Commands/PokemonSearchCommands.cs +++ b/src/NadekoBot/Modules/Searches/Commands/PokemonSearchCommands.cs @@ -15,7 +15,6 @@ namespace NadekoBot.Modules.Searches [Group] public class PokemonSearchCommands { - //todo DB private static Dictionary pokemons = new Dictionary(); private static Dictionary pokemonAbilities = new Dictionary(); diff --git a/src/NadekoBot/Modules/Searches/Searches.cs b/src/NadekoBot/Modules/Searches/Searches.cs index 99aeb95b..59828814 100644 --- a/src/NadekoBot/Modules/Searches/Searches.cs +++ b/src/NadekoBot/Modules/Searches/Searches.cs @@ -338,7 +338,7 @@ $@"🌍 **Weather for** 【{obj["target"]}】 if (string.IsNullOrWhiteSpace(usrStr)) return; - var usr = (await channel.Guild.GetUsersAsync()).Where(u => u.Username.ToUpperInvariant() == usrStr).FirstOrDefault(); + var usr = channel.Guild.GetUsers().Where(u => u.Username.ToUpperInvariant() == usrStr).FirstOrDefault(); if (usr == null || string.IsNullOrWhiteSpace(usr.AvatarUrl)) return; diff --git a/src/NadekoBot/Modules/Utility/Commands/InfoCommands.cs b/src/NadekoBot/Modules/Utility/Commands/InfoCommands.cs index 7102addf..62aab7b1 100644 --- a/src/NadekoBot/Modules/Utility/Commands/InfoCommands.cs +++ b/src/NadekoBot/Modules/Utility/Commands/InfoCommands.cs @@ -1,5 +1,6 @@ ο»Ώusing Discord; using Discord.Commands; +using Discord.WebSocket; using NadekoBot.Attributes; using NadekoBot.Extensions; using System; @@ -21,7 +22,7 @@ namespace NadekoBot.Modules.Utility if (guild == null) server = channel.Guild; else - server = (await _client.GetGuildsAsync()).Where(g => g.Name.ToUpperInvariant() == guild.ToUpperInvariant()).FirstOrDefault(); + server = _client.GetGuilds().Where(g => g.Name.ToUpperInvariant() == guild.ToUpperInvariant()).FirstOrDefault(); if (server == null) return; diff --git a/src/NadekoBot/Modules/Utility/Commands/Remind.cs b/src/NadekoBot/Modules/Utility/Commands/Remind.cs index d115e921..8f273aa6 100644 --- a/src/NadekoBot/Modules/Utility/Commands/Remind.cs +++ b/src/NadekoBot/Modules/Utility/Commands/Remind.cs @@ -67,12 +67,7 @@ namespace NadekoBot.Modules.Utility } else { - ch = NadekoBot.Client.GetGuilds() - .Where(g => g.Id == r.ServerId) - .FirstOrDefault() - .GetTextChannels() - .Where(c => c.Id == r.ChannelId) - .FirstOrDefault(); + ch = NadekoBot.Client.GetGuild(r.ServerId)?.GetTextChannel(r.ChannelId); } if (ch == null) return; diff --git a/src/NadekoBot/NadekoBot.cs b/src/NadekoBot/NadekoBot.cs index 89e37170..e53c6157 100644 --- a/src/NadekoBot/NadekoBot.cs +++ b/src/NadekoBot/NadekoBot.cs @@ -19,11 +19,12 @@ namespace NadekoBot private Logger _log; public static CommandService Commands { get; private set; } + public static CommandHandler CommandHandler { get; private set; } public static DiscordSocketClient Client { get; private set; } public static Localization Localizer { get; private set; } public static BotCredentials Credentials { get; private set; } - public static GoogleApiService Google { get; set; } + public static GoogleApiService Google { get; private set; } public static StatsService Stats { get; private set; } public async Task RunAsync(string[] args) @@ -44,6 +45,7 @@ namespace NadekoBot Localizer = new Localization(); Google = new GoogleApiService(); Stats = new StatsService(Client); + CommandHandler = new CommandHandler(Client, Commands); _log = LogManager.GetCurrentClassLogger(); //setup DI @@ -61,7 +63,6 @@ namespace NadekoBot //load commands await Commands.LoadAssembly(Assembly.GetEntryAssembly(), depMap); - Client.MessageReceived += Client_MessageReceived; Console.WriteLine(await Stats.Print()); @@ -83,57 +84,10 @@ namespace NadekoBot LogManager.Configuration = logConfig; } - catch (Exception ex) { + catch (Exception ex) + { Console.WriteLine(ex); } } - - private Task Client_MessageReceived(IMessage umsg) - { - var usrMsg = umsg as IUserMessage; - if (usrMsg == null) - return Task.CompletedTask; - var throwaway = Task.Run(async () => - { - var sw = new Stopwatch(); - sw.Start(); - var t = await Commands.Execute(usrMsg, usrMsg.Content); - sw.Stop(); - var channel = (umsg.Channel as ITextChannel); - if (t.IsSuccess) - { - - _log.Info("Command Executed after {4}s\n\t" + - "User: {0}\n\t" + - "Server: {1}\n\t" + - "Channel: {2}\n\t" + - "Message: {3}", - umsg.Author + " [" + umsg.Author.Id + "]", // {0} - (channel == null ? "PRIVATE" : channel.Guild.Name + " [" + channel.Guild.Id + "]"), // {1} - (channel == null ? "PRIVATE" : channel.Name + " [" + channel.Id + "]"), //{2} - umsg.Content, // {3} - sw.Elapsed.TotalSeconds // {4} - ); - } - else if (!t.IsSuccess && t.Error != CommandError.UnknownCommand) - { - _log.Warn("Command Errored after {5}s\n\t" + - "User: {0}\n\t" + - "Server: {1}\n\t" + - "Channel: {2}\n\t" + - "Message: {3}\n\t" + - "Error: {4}", - umsg.Author + " [" + umsg.Author.Id + "]", // {0} - (channel == null ? "PRIVATE" : channel.Guild.Name + " [" + channel.Guild.Id + "]"), // {1} - (channel == null ? "PRIVATE" : channel.Name + " [" + channel.Id + "]"), //{2} - umsg.Content,// {3} - t.ErrorReason, // {4} - sw.Elapsed.TotalSeconds //{5} - ); - } - }); - - return Task.CompletedTask; - } } } diff --git a/src/NadekoBot/Services/CommandHandler.cs b/src/NadekoBot/Services/CommandHandler.cs new file mode 100644 index 00000000..3ab26fe9 --- /dev/null +++ b/src/NadekoBot/Services/CommandHandler.cs @@ -0,0 +1,93 @@ +ο»Ώusing Discord.WebSocket; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Discord; +using NLog; +using System.Diagnostics; +using Discord.Commands; + +namespace NadekoBot.Services +{ + public class CommandHandler + { + private DiscordSocketClient _client; + private CommandService _commandService; + private Logger _log; + + public event EventHandler CommandExecuted = async delegate { }; + + public CommandHandler(DiscordSocketClient client, CommandService commandService) + { + _client = client; + _commandService = commandService; + _log = LogManager.GetCurrentClassLogger(); + + _client.MessageReceived += MessageReceivedHandler; + } + + private Task MessageReceivedHandler(IMessage msg) + { + var usrMsg = msg as IUserMessage; + if (usrMsg == null) + return Task.CompletedTask; + var throwaway = Task.Run(async () => + { + var sw = new Stopwatch(); + sw.Start(); + var t = await _commandService.Execute(usrMsg, usrMsg.Content, MultiMatchHandling.Best); + var command = t.Item1; + var result = t.Item2; + sw.Stop(); + var channel = (usrMsg.Channel as ITextChannel); + if (result.IsSuccess) + { + CommandExecuted(this, new CommandExecutedEventArgs(usrMsg, command)); + _log.Info("Command Executed after {4}s\n\t" + + "User: {0}\n\t" + + "Server: {1}\n\t" + + "Channel: {2}\n\t" + + "Message: {3}", + usrMsg.Author + " [" + usrMsg.Author.Id + "]", // {0} + (channel == null ? "PRIVATE" : channel.Guild.Name + " [" + channel.Guild.Id + "]"), // {1} + (channel == null ? "PRIVATE" : channel.Name + " [" + channel.Id + "]"), // {2} + usrMsg.Content, // {3} + sw.Elapsed.TotalSeconds // {4} + ); + } + else if (!result.IsSuccess && result.Error != CommandError.UnknownCommand) + { + _log.Warn("Command Errored after {5}s\n\t" + + "User: {0}\n\t" + + "Server: {1}\n\t" + + "Channel: {2}\n\t" + + "Message: {3}\n\t" + + "Error: {4}", + usrMsg.Author + " [" + usrMsg.Author.Id + "]", // {0} + (channel == null ? "PRIVATE" : channel.Guild.Name + " [" + channel.Guild.Id + "]"), // {1} + (channel == null ? "PRIVATE" : channel.Name + " [" + channel.Id + "]"), // {2} + usrMsg.Content,// {3} + result.ErrorReason, // {4} + sw.Elapsed.TotalSeconds // {5} + ); + } + }); + + return Task.CompletedTask; + } + } + + public class CommandExecutedEventArgs + { + public Command Command { get; } + public IUserMessage Message { get; } + + public CommandExecutedEventArgs(IUserMessage msg, Command cmd) + { + Message = msg; + Command = cmd; + } + } +} diff --git a/src/NadekoBot/Services/CurrencyHandler.cs b/src/NadekoBot/Services/CurrencyHandler.cs new file mode 100644 index 00000000..d86eea80 --- /dev/null +++ b/src/NadekoBot/Services/CurrencyHandler.cs @@ -0,0 +1,51 @@ +ο»Ώusing System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Discord; +using NadekoBot.Services.Database; +using NadekoBot.Extensions; +using NadekoBot.Modules.Gambling; + +namespace NadekoBot.Services +{ + public static class CurrencyHandler + { + public static async Task RemoveCurrencyAsync(IGuildUser author, string reason, long amount, bool sendMessage) + { + if (amount < 0) + throw new ArgumentNullException(nameof(amount)); + + + using (var uow = DbHandler.UnitOfWork()) + { + 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 { } + + return true; + } + + public static async Task AddCurrencyAsync(IGuildUser author, string reason, long amount, bool sendMessage) + { + if (amount < 0) + throw new ArgumentNullException(nameof(amount)); + + + using (var uow = DbHandler.UnitOfWork()) + { + uow.Currency.TryUpdateState(author.Id, amount); + await uow.CompleteAsync(); + } + + if (sendMessage) + await author.SendMessageAsync($"`You received:` {amount} {Gambling.CurrencySign}\n`Reason:` {reason}").ConfigureAwait(false); + } + } +} diff --git a/src/NadekoBot/Services/Database/IUnitOfWork.cs b/src/NadekoBot/Services/Database/IUnitOfWork.cs index 3a3fb2c2..905af9af 100644 --- a/src/NadekoBot/Services/Database/IUnitOfWork.cs +++ b/src/NadekoBot/Services/Database/IUnitOfWork.cs @@ -16,6 +16,7 @@ namespace NadekoBot.Services.Database IReminderRepository Reminders { get; } ISelfAssignedRolesRepository SelfAssignedRoles { get; } IBotConfigRepository BotConfig { get; } + IRepeaterRepository Repeaters { get; } int Complete(); Task CompleteAsync(); diff --git a/src/NadekoBot/Services/Database/Models/Currency.cs b/src/NadekoBot/Services/Database/Models/Currency.cs new file mode 100644 index 00000000..5bc8b8d5 --- /dev/null +++ b/src/NadekoBot/Services/Database/Models/Currency.cs @@ -0,0 +1,14 @@ +ο»Ώusing System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace NadekoBot.Services.Database.Models +{ + public class Currency : DbEntity + { + public ulong UserId { get; set; } + public long Amount { get; set; } + } +} diff --git a/src/NadekoBot/Services/Database/Models/GuildConfig.cs b/src/NadekoBot/Services/Database/Models/GuildConfig.cs index 77c1d966..a4cb10c5 100644 --- a/src/NadekoBot/Services/Database/Models/GuildConfig.cs +++ b/src/NadekoBot/Services/Database/Models/GuildConfig.cs @@ -31,5 +31,6 @@ namespace NadekoBot.Services.Database.Models //self assignable roles public bool ExclusiveSelfAssignedRoles { get; set; } public bool AutoDeleteSelfAssignedRoleMessages { get; set; } + public float DefaultMusicVolume { get; set; } } } diff --git a/src/NadekoBot/Services/Database/Models/Repeater.cs b/src/NadekoBot/Services/Database/Models/Repeater.cs new file mode 100644 index 00000000..ba5552f9 --- /dev/null +++ b/src/NadekoBot/Services/Database/Models/Repeater.cs @@ -0,0 +1,16 @@ +ο»Ώusing System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace NadekoBot.Services.Database.Models +{ + public class Repeater :DbEntity + { + public ulong GuildId { get; set; } + public ulong ChannelId { get; set; } + public string Message { get; set; } + public TimeSpan Interval { get; set; } + } +} diff --git a/src/NadekoBot/Services/Database/NadekoContext.cs b/src/NadekoBot/Services/Database/NadekoContext.cs index 378c1637..ae32be4a 100644 --- a/src/NadekoBot/Services/Database/NadekoContext.cs +++ b/src/NadekoBot/Services/Database/NadekoContext.cs @@ -18,6 +18,8 @@ namespace NadekoBot.Services.Database public DbSet Reminders { get; set; } public DbSet SelfAssignableRoles { get; set; } public DbSet BotConfig { get; set; } + public DbSet Repeaters { get; set; } + public DbSet Currency { get; set; } protected override void OnModelCreating(ModelBuilder modelBuilder) { @@ -63,6 +65,24 @@ namespace NadekoBot.Services.Database .IsUnique(); #endregion + + #region Repeater + + var repeaterEntity = modelBuilder.Entity(); + + repeaterEntity + .HasIndex(r => r.ChannelId) + .IsUnique(); + + #endregion + + #region Currency + var currencyEntity = modelBuilder.Entity(); + + currencyEntity + .HasIndex(c => c.UserId) + .IsUnique(); + #endregion } protected abstract override void OnConfiguring(DbContextOptionsBuilder optionsBuilder); } diff --git a/src/NadekoBot/Services/Database/Repositories/ICurrencyRepository.cs b/src/NadekoBot/Services/Database/Repositories/ICurrencyRepository.cs new file mode 100644 index 00000000..c6e545df --- /dev/null +++ b/src/NadekoBot/Services/Database/Repositories/ICurrencyRepository.cs @@ -0,0 +1,17 @@ +ο»Ώusing NadekoBot.Services.Database.Models; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace NadekoBot.Services.Database.Repositories +{ + public interface ICurrencyRepository : IRepository + { + Currency GetOrCreate(ulong userId); + long GetUserCurrency(ulong userId); + bool TryUpdateState(ulong userId, long change); + IEnumerable GetTopRichest(int count); + } +} diff --git a/src/NadekoBot/Services/Database/Repositories/IRepeaterRepository.cs b/src/NadekoBot/Services/Database/Repositories/IRepeaterRepository.cs new file mode 100644 index 00000000..7bd5c67f --- /dev/null +++ b/src/NadekoBot/Services/Database/Repositories/IRepeaterRepository.cs @@ -0,0 +1,14 @@ +ο»Ώusing NadekoBot.Services.Database.Models; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace NadekoBot.Services.Database.Repositories +{ + public interface IRepeaterRepository : IRepository + { + + } +} diff --git a/src/NadekoBot/Services/Database/Repositories/Impl/CurrencyRepository.cs b/src/NadekoBot/Services/Database/Repositories/Impl/CurrencyRepository.cs new file mode 100644 index 00000000..f9bcdbe7 --- /dev/null +++ b/src/NadekoBot/Services/Database/Repositories/Impl/CurrencyRepository.cs @@ -0,0 +1,60 @@ +ο»Ώusing NadekoBot.Services.Database.Models; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; + +namespace NadekoBot.Services.Database.Repositories.Impl +{ + public class CurrencyRepository : Repository, ICurrencyRepository + { + public CurrencyRepository(DbContext context) : base(context) + { + } + + public Currency GetOrCreate(ulong userId) + { + var cur = _set.FirstOrDefault(c => c.UserId == userId); + + if (cur == null) + { + _set.Add(cur = new Currency() + { + UserId = userId, + Amount = 0 + }); + _context.SaveChanges(); + } + return cur; + } + + public IEnumerable GetTopRichest(int count) => + _set.OrderByDescending(c => c.Amount).Take(count).ToList(); + + public long GetUserCurrency(ulong userId) => + GetOrCreate(userId).Amount; + + public bool TryUpdateState(ulong userId, long change) + { + var cur = GetOrCreate(userId); + + if (change == 0) + return true; + + if (change > 0) + { + cur.Amount += change; + return true; + } + //change is negative + if (cur.Amount + change >= 0) + { + cur.Amount += change; + return true; + } + return false; + } + } +} diff --git a/src/NadekoBot/Services/Database/Repositories/Impl/RepeaterRepository.cs b/src/NadekoBot/Services/Database/Repositories/Impl/RepeaterRepository.cs new file mode 100644 index 00000000..33885856 --- /dev/null +++ b/src/NadekoBot/Services/Database/Repositories/Impl/RepeaterRepository.cs @@ -0,0 +1,17 @@ +ο»Ώusing NadekoBot.Services.Database.Models; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; + +namespace NadekoBot.Services.Database.Repositories.Impl +{ + public class RepeaterRepository : Repository, IRepeaterRepository + { + public RepeaterRepository(DbContext context) : base(context) + { + } + } +} diff --git a/src/NadekoBot/Services/Database/UnitOfWork.cs b/src/NadekoBot/Services/Database/UnitOfWork.cs index 5550bc6c..f8e42e64 100644 --- a/src/NadekoBot/Services/Database/UnitOfWork.cs +++ b/src/NadekoBot/Services/Database/UnitOfWork.cs @@ -33,6 +33,12 @@ namespace NadekoBot.Services.Database private IBotConfigRepository _botConfig; public IBotConfigRepository BotConfig => _botConfig ?? (_botConfig = new BotConfigRepository(_context)); + private IRepeaterRepository _repeaters; + public IRepeaterRepository Repeaters => _repeaters ?? (_repeaters = new RepeaterRepository(_context)); + + private ICurrencyRepository _currency; + public ICurrencyRepository Currency => _currency ?? (_currency = new CurrencyRepository(_context)); + public UnitOfWork(NadekoContext context) { _context = context; diff --git a/src/NadekoBot/Services/Impl/StatsService.cs b/src/NadekoBot/Services/Impl/StatsService.cs index 9f50971a..87331161 100644 --- a/src/NadekoBot/Services/Impl/StatsService.cs +++ b/src/NadekoBot/Services/Impl/StatsService.cs @@ -34,7 +34,7 @@ namespace NadekoBot.Services.Impl public Task Print() => Task.FromResult($@"`Author: Kwoth` `Library: Discord.Net` `Bot Version: {BotVersion}` `Bot id: {(client.GetCurrentUser()).Id}` -`Owners' Ids:` +`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()}` `Messages: {messageCounter} ({messageCounter / (double)GetUptime().TotalSeconds:F2}/sec)` `Heap: {Heap} MB`"); diff --git a/src/NadekoBot/_Extensions/Extensions.cs b/src/NadekoBot/_Extensions/Extensions.cs index ecd748a1..e44cc7f3 100644 --- a/src/NadekoBot/_Extensions/Extensions.cs +++ b/src/NadekoBot/_Extensions/Extensions.cs @@ -40,7 +40,7 @@ namespace NadekoBot.Extensions Task.FromResult(NadekoBot.Client.GetCurrentUser().Id == msg.Author.Id); public static IEnumerable Members(this IRole role) => - NadekoBot.Client.GetGuilds().Where(g => g.Id == role.GuildId).FirstOrDefault()?.GetUsers().Where(u => u.Roles.Contains(role)) ?? Enumerable.Empty(); + NadekoBot.Client.GetGuild(role.GuildId)?.GetUsers().Where(u => u.Roles.Contains(role)) ?? Enumerable.Empty(); public static async Task ReplyLong(this IUserMessage msg, string content, string breakOn = "\n", string addToEnd = "", string addToStart = "") {