diff --git a/src/NadekoBot/.editorconfig b/src/NadekoBot/.editorconfig index 2525eb173..54a8cfb80 100644 --- a/src/NadekoBot/.editorconfig +++ b/src/NadekoBot/.editorconfig @@ -342,6 +342,7 @@ resharper_place_simple_embedded_statement_on_same_line = false resharper_wrap_chained_binary_expressions = chop_if_long resharper_wrap_chained_binary_patterns = chop_if_long resharper_wrap_chained_method_calls = chop_if_long +resharper_wrap_object_and_collection_initializer_style = chop_always resharper_csharp_wrap_before_first_type_parameter_constraint = true resharper_csharp_place_type_constraints_on_same_line = false @@ -349,4 +350,4 @@ resharper_csharp_wrap_before_extends_colon = true resharper_csharp_place_constructor_initializer_on_same_line = false resharper_force_attribute_style = separate resharper_braces_for_ifelse = required_for_multiline -resharper_arrange_redundant_parentheses_highlighting = hint \ No newline at end of file +resharper_arrange_redundant_parentheses_highlighting = hint diff --git a/src/NadekoBot/Modules/Gambling/AnimalRacing/AnimalRace.cs b/src/NadekoBot/Modules/Gambling/AnimalRacing/AnimalRace.cs index d446f8c4a..b888843e9 100644 --- a/src/NadekoBot/Modules/Gambling/AnimalRacing/AnimalRace.cs +++ b/src/NadekoBot/Modules/Gambling/AnimalRacing/AnimalRace.cs @@ -75,7 +75,7 @@ public sealed class AnimalRace : IDisposable if (CurrentPhase != Phase.WaitingForPlayers) throw new AlreadyStartedException(); - if (!await _currency.RemoveAsync(userId, "BetRace", bet)) + if (!await _currency.RemoveAsync(userId, bet, new("animalrace", "bet"))) throw new NotEnoughFundsException(); if (_users.Contains(user)) @@ -100,7 +100,7 @@ public sealed class AnimalRace : IDisposable { foreach (var user in _users) if (user.Bet > 0) - await _currency.AddAsync(user.UserId, "Race refund", user.Bet); + await _currency.AddAsync(user.UserId, user.Bet, new("animalrace", "refund")); _ = OnStartingFailed?.Invoke(this); CurrentPhase = Phase.Ended; @@ -130,8 +130,8 @@ public sealed class AnimalRace : IDisposable if (FinishedUsers[0].Bet > 0) await _currency.AddAsync(FinishedUsers[0].UserId, - "Won a Race", - FinishedUsers[0].Bet * (_users.Count - 1)); + FinishedUsers[0].Bet * (_users.Count - 1), + new("animalrace", "win")); _ = OnEnded?.Invoke(this); }); diff --git a/src/NadekoBot/Modules/Gambling/BlackJack/Blackjack.cs b/src/NadekoBot/Modules/Gambling/BlackJack/Blackjack.cs index d484a1004..84e0a0dcd 100644 --- a/src/NadekoBot/Modules/Gambling/BlackJack/Blackjack.cs +++ b/src/NadekoBot/Modules/Gambling/BlackJack/Blackjack.cs @@ -120,7 +120,7 @@ public class Blackjack if (Players.Count >= 5) return false; - if (!await _cs.RemoveAsync(user, "BlackJack-gamble", bet, gamble: true)) return false; + if (!await _cs.RemoveAsync(user, bet, new("blackjack","gamble"))) return false; Players.Add(new(user, bet)); _= PrintState(); @@ -210,7 +210,7 @@ public class Blackjack foreach (var usr in Players) if (usr.State is User.UserState.Won or User.UserState.Blackjack) - await _cs.AddAsync(usr.DiscordUser.Id, "BlackJack-win", usr.Bet * 2, true); + await _cs.AddAsync(usr.DiscordUser.Id, usr.Bet * 2, new("blackjack", "win")); } public async Task Double(IUser u) @@ -234,7 +234,7 @@ public class Blackjack if (CurrentUser != u) return false; - if (!await _cs.RemoveAsync(u.DiscordUser.Id, "Blackjack-double", u.Bet)) + if (!await _cs.RemoveAsync(u.DiscordUser.Id, u.Bet, new("blackjack", "double"))) return false; u.Bet *= 2; diff --git a/src/NadekoBot/Modules/Gambling/Connect4/Connect4.cs b/src/NadekoBot/Modules/Gambling/Connect4/Connect4.cs index 5111c5a70..07e3e3fda 100644 --- a/src/NadekoBot/Modules/Gambling/Connect4/Connect4.cs +++ b/src/NadekoBot/Modules/Gambling/Connect4/Connect4.cs @@ -98,7 +98,7 @@ public sealed class Connect4Game : IDisposable { var __ = OnGameFailedToStart?.Invoke(this); CurrentPhase = Phase.Ended; - await _cs.AddAsync(_players[0].Value.UserId, "Connect4-refund", _options.Bet, true); + await _cs.AddAsync(_players[0].Value.UserId, _options.Bet, new("connect4", "refund")); } } finally { _locker.Release(); } @@ -119,7 +119,7 @@ public sealed class Connect4Game : IDisposable if (bet != _options.Bet) // can't join if bet amount is not the same return false; - if (!await _cs.RemoveAsync(userId, "Connect4-bet", bet, true)) // user doesn't have enough money to gamble + if (!await _cs.RemoveAsync(userId, bet, new("connect4", "bet"))) // user doesn't have enough money to gamble return false; if (_rng.Next(0, 2) == 0) //rolling from 0-1, if number is 0, join as first player @@ -342,13 +342,13 @@ public sealed class Connect4Game : IDisposable if (result == Result.Draw) { - _cs.AddAsync(CurrentPlayer.UserId, "Connect4-draw", _options.Bet, true); - _cs.AddAsync(OtherPlayer.UserId, "Connect4-draw", _options.Bet, true); + _cs.AddAsync(CurrentPlayer.UserId, _options.Bet, new("connect4", "draw")); + _cs.AddAsync(OtherPlayer.UserId, _options.Bet, new("connect4", "draw")); return; } if (winId is not null) - _cs.AddAsync(winId.Value, "Connnect4-win", (long)(_options.Bet * 1.98), true); + _cs.AddAsync(winId.Value, (long)(_options.Bet * 1.98), new("connect4", "win")); } private Field GetPlayerPiece(ulong userId) diff --git a/src/NadekoBot/Modules/Gambling/Connect4/Connect4Commands.cs b/src/NadekoBot/Modules/Gambling/Connect4/Connect4Commands.cs index 7da075af5..ef85cf8c1 100644 --- a/src/NadekoBot/Modules/Gambling/Connect4/Connect4Commands.cs +++ b/src/NadekoBot/Modules/Gambling/Connect4/Connect4Commands.cs @@ -64,7 +64,7 @@ public partial class Gambling } if (options.Bet > 0) - if (!await _cs.RemoveAsync(ctx.User.Id, "Connect4-bet", options.Bet, true)) + if (!await _cs.RemoveAsync(ctx.User.Id, options.Bet, new("connect4", "bet"))) { await ReplyErrorLocalizedAsync(strs.not_enough(CurrencySign)); _service.Connect4Games.TryRemove(ctx.Channel.Id, out _); diff --git a/src/NadekoBot/Modules/Gambling/Events/GameStatusEvent.cs b/src/NadekoBot/Modules/Gambling/Events/GameStatusEvent.cs index 6db0c00ff..f60914a55 100644 --- a/src/NadekoBot/Modules/Gambling/Events/GameStatusEvent.cs +++ b/src/NadekoBot/Modules/Gambling/Events/GameStatusEvent.cs @@ -76,9 +76,9 @@ public class GameStatusEvent : ICurrencyEvent try { await _cs.AddBulkAsync(toAward, - toAward.Select(_ => "GameStatus Event"), - toAward.Select(_ => _amount), - true); + _amount, + new("event", "gamestatus") + ); if (_isPotLimited) await msg.ModifyAsync(m => diff --git a/src/NadekoBot/Modules/Gambling/Events/ReactionEvent.cs b/src/NadekoBot/Modules/Gambling/Events/ReactionEvent.cs index d4d9139f3..de22a8185 100644 --- a/src/NadekoBot/Modules/Gambling/Events/ReactionEvent.cs +++ b/src/NadekoBot/Modules/Gambling/Events/ReactionEvent.cs @@ -71,7 +71,7 @@ public class ReactionEvent : ICurrencyEvent try { - await _cs.AddBulkAsync(toAward, toAward.Select(_ => "Reaction Event"), toAward.Select(_ => _amount), true); + await _cs.AddBulkAsync(toAward, _amount, new("event", "reaction")); if (_isPotLimited) await msg.ModifyAsync(m => diff --git a/src/NadekoBot/Modules/Gambling/FlipCoin/FlipCoinCommands.cs b/src/NadekoBot/Modules/Gambling/FlipCoin/FlipCoinCommands.cs index a99acef57..b1a2286ae 100644 --- a/src/NadekoBot/Modules/Gambling/FlipCoin/FlipCoinCommands.cs +++ b/src/NadekoBot/Modules/Gambling/FlipCoin/FlipCoinCommands.cs @@ -80,7 +80,7 @@ public partial class Gambling if (!await CheckBetMandatory(amount) || amount == 1) return; - var removed = await _cs.RemoveAsync(ctx.User, "Betflip Gamble", amount, false, true); + var removed = await _cs.RemoveAsync(ctx.User, amount, new("betflip", "bet")); if (!removed) { await ReplyErrorLocalizedAsync(strs.not_enough(CurrencySign)); @@ -106,7 +106,7 @@ public partial class Gambling { var toWin = (long)(amount * Config.BetFlip.Multiplier); str = Format.Bold(ctx.User.ToString()) + " " + GetText(strs.flip_guess(toWin + CurrencySign)); - await _cs.AddAsync(ctx.User, "Betflip Gamble", toWin, false, true); + await _cs.AddAsync(ctx.User, toWin, new("betflip", "win")); } else { diff --git a/src/NadekoBot/Modules/Gambling/Gambling.cs b/src/NadekoBot/Modules/Gambling/Gambling.cs index 4773c3156..56748792a 100644 --- a/src/NadekoBot/Modules/Gambling/Gambling.cs +++ b/src/NadekoBot/Modules/Gambling/Gambling.cs @@ -3,12 +3,14 @@ using NadekoBot.Db; using NadekoBot.Db.Models; using NadekoBot.Modules.Gambling.Common; using NadekoBot.Modules.Gambling.Services; +using NadekoBot.Services.Currency; using NadekoBot.Services.Database.Models; using System.Globalization; using System.Numerics; namespace NadekoBot.Modules.Gambling; +// todo leave empty servers public partial class Gambling : GamblingModule { public enum RpsPick @@ -67,10 +69,11 @@ public partial class Gambling : GamblingModule return cur.ToString("C0", flowersCi); } - public string GetCurrency(ulong id) + public async Task GetBalanceStringAsync(ulong userId) { - using var uow = _db.GetDbContext(); - return n(uow.DiscordUser.GetUserCurrency(id)); + var wallet = await _cs.GetWalletAsync(userId); + var bal = await wallet.GetBalance(); + return n(bal); } [Cmd] @@ -78,9 +81,11 @@ public partial class Gambling : GamblingModule { var ec = _service.GetEconomy(); decimal onePercent = 0; + + // This stops the top 1% from owning more than 100% of the money if (ec.Cash > 0) - onePercent = - ec.OnePercent / (ec.Cash - ec.Bot); // This stops the top 1% from owning more than 100% of the money + onePercent = ec.OnePercent / (ec.Cash - ec.Bot); + // [21:03] Bob Page: Kinda remids me of US economy var embed = _eb.Create() .WithTitle(GetText(strs.economy_state)) @@ -93,6 +98,7 @@ public partial class Gambling : GamblingModule .AddField(GetText(strs.total), ((BigInteger)(ec.Cash + ec.Planted + ec.Waifus)).ToString("N", Culture) + CurrencySign) .WithOkColor(); + // ec.Cash already contains ec.Bot as it's the total of all values in the CurrencyAmount column of the DiscordUser table await ctx.Channel.EmbedAsync(embed); } @@ -114,7 +120,7 @@ public partial class Gambling : GamblingModule return; } - await _cs.AddAsync(ctx.User.Id, "Timely claim", val); + await _cs.AddAsync(ctx.User.Id, val, new("timely", "claim")); await ReplyConfirmLocalizedAsync(strs.timely(n(val), period)); } @@ -225,14 +231,18 @@ public partial class Gambling : GamblingModule [Cmd] [Priority(0)] public async partial Task Cash(ulong userId) - => await ReplyConfirmLocalizedAsync(strs.has(Format.Code(userId.ToString()), $"{GetCurrency(userId)}")); + { + var cur = await GetBalanceStringAsync(userId); + await ReplyConfirmLocalizedAsync(strs.has(Format.Code(userId.ToString()), cur)); + } [Cmd] [Priority(1)] public async partial Task Cash([Leftover] IUser user = null) { user ??= ctx.User; - await ConfirmLocalizedAsync(strs.has(Format.Bold(user.ToString()), $"{GetCurrency(user.Id)}")); + var cur = await GetBalanceStringAsync(user.Id); + await ConfirmLocalizedAsync(strs.has(Format.Bold(user.ToString()), cur)); } [Cmd] @@ -242,15 +252,13 @@ public partial class Gambling : GamblingModule { if (amount <= 0 || ctx.User.Id == receiver.Id || receiver.IsBot) return; - var success = - await _cs.RemoveAsync((IGuildUser)ctx.User, $"Gift to {receiver.Username} ({receiver.Id}).", amount); - if (!success) + + if (!await _cs.TransferAsync(ctx.User.Id, receiver.Id, amount, msg)) { await ReplyErrorLocalizedAsync(strs.not_enough(CurrencySign)); return; } - await _cs.AddAsync(receiver, $"Gift from {ctx.User.Username} ({ctx.User.Id}) - {msg}.", amount, true); await ReplyConfirmLocalizedAsync(strs.gifted(n(amount), Format.Bold(receiver.ToString()))); } @@ -290,10 +298,10 @@ public partial class Gambling : GamblingModule return; } - await _cs.AddAsync(usr, - $"Awarded by bot owner. ({ctx.User.Username}/{ctx.User.Id}) {msg ?? ""}", + await _cs.AddAsync(usr.Id, amount, - gamble: ctx.Client.CurrentUser.Id != usrId); + new Extra("owner", "award", $"Awarded by bot owner. ({ctx.User.Username}/{ctx.User.Id}) {msg ?? ""}") + ); await ReplyConfirmLocalizedAsync(strs.awarded(n(amount), $"<@{usrId}>")); } @@ -305,10 +313,11 @@ public partial class Gambling : GamblingModule { var users = (await ctx.Guild.GetUsersAsync()).Where(u => u.GetRoles().Contains(role)).ToList(); - await _cs.AddBulkAsync(users.Select(x => x.Id), - users.Select(_ => $"Awarded by bot owner to **{role.Name}** role. ({ctx.User.Username}/{ctx.User.Id})"), - users.Select(_ => amount), - true); + await _cs.AddBulkAsync(users.Select(x => x.Id).ToList(), + amount, + new("owner", + "award", + $"Awarded by bot owner to **{role.Name}** role. ({ctx.User.Username}/{ctx.User.Id})")); await ReplyConfirmLocalizedAsync(strs.mass_award(n(amount), Format.Bold(users.Count.ToString()), @@ -323,10 +332,9 @@ public partial class Gambling : GamblingModule { var users = (await role.GetMembersAsync()).ToList(); - await _cs.RemoveBulkAsync(users.Select(x => x.Id), - users.Select(_ => $"Taken by bot owner from **{role.Name}** role. ({ctx.User.Username}/{ctx.User.Id})"), - users.Select(_ => amount), - true); + await _cs.RemoveBulkAsync(users.Select(x => x.Id).ToList(), + amount, + new("owner", "take", $"Taken by bot owner from **{role.Name}** role. ({ctx.User.Username}/{ctx.User.Id})")); await ReplyConfirmLocalizedAsync(strs.mass_take(n(amount), Format.Bold(users.Count.ToString()), @@ -342,10 +350,9 @@ public partial class Gambling : GamblingModule if (amount <= 0) return; - if (await _cs.RemoveAsync(user, - $"Taken by bot owner.({ctx.User.Username}/{ctx.User.Id})", + if (await _cs.RemoveAsync(user.Id, amount, - gamble: ctx.Client.CurrentUser.Id != user.Id)) + new("owner", "take", $"Taken by bot owner. ({ctx.User.Username}/{ctx.User.Id})"))) await ReplyConfirmLocalizedAsync(strs.take(n(amount), Format.Bold(user.ToString()))); else await ReplyErrorLocalizedAsync(strs.take_fail(n(amount), Format.Bold(user.ToString()), CurrencySign)); @@ -360,9 +367,8 @@ public partial class Gambling : GamblingModule return; if (await _cs.RemoveAsync(usrId, - $"Taken by bot owner.({ctx.User.Username}/{ctx.User.Id})", amount, - ctx.Client.CurrentUser.Id != usrId)) + new("owner", "take", $"Taken by bot owner. ({ctx.User.Username}/{ctx.User.Id})"))) await ReplyConfirmLocalizedAsync(strs.take(n(amount), $"<@{usrId}>")); else await ReplyErrorLocalizedAsync(strs.take_fail(n(amount), Format.Code(usrId.ToString()), CurrencySign)); @@ -467,7 +473,7 @@ public partial class Gambling : GamblingModule if (!await CheckBetMandatory(amount)) return; - if (!await _cs.RemoveAsync(ctx.User, "Betroll Gamble", amount, false, true)) + if (!await _cs.RemoveAsync(ctx.User, amount, new("betroll", "bet"))) { await ReplyErrorLocalizedAsync(strs.not_enough(CurrencySign)); return; @@ -483,7 +489,7 @@ public partial class Gambling : GamblingModule { var win = (long)(amount * result.Multiplier); str += GetText(strs.br_win(n(win), result.Threshold + (result.Roll == 100 ? " 👑" : ""))); - await _cs.AddAsync(ctx.User, "Betroll Gamble", win, false, true); + await _cs.AddAsync(ctx.User, win, new("betroll", "win")); } else { @@ -600,16 +606,20 @@ public partial class Gambling : GamblingModule var nadekoPick = (RpsPick)new NadekoRandom().Next(0, 3); if (amount > 0) - if (!await _cs.RemoveAsync(ctx.User.Id, "Rps-bet", amount, true)) + { + if (!await _cs.RemoveAsync(ctx.User.Id, + amount, + new("rps", "bet", ""))) { await ReplyErrorLocalizedAsync(strs.not_enough(CurrencySign)); return; } + } string msg; if (pick == nadekoPick) { - await _cs.AddAsync(ctx.User.Id, "Rps-draw", amount, true); + await _cs.AddAsync(ctx.User.Id, amount, new("rps", "draw")); embed.WithOkColor(); msg = GetText(strs.rps_draw(GetRpsPick(pick))); } @@ -618,7 +628,7 @@ public partial class Gambling : GamblingModule || (pick == RpsPick.Scissors && nadekoPick == RpsPick.Paper)) { amount = (long)(amount * Config.BetFlip.Multiplier); - await _cs.AddAsync(ctx.User.Id, "Rps-win", amount, true); + await _cs.AddAsync(ctx.User.Id, amount, new("rps", "win")); embed.WithOkColor(); embed.AddField(GetText(strs.won), n(amount)); msg = GetText(strs.rps_win(ctx.User.Mention, GetRpsPick(pick), GetRpsPick(nadekoPick))); diff --git a/src/NadekoBot/Modules/Gambling/GamblingService.cs b/src/NadekoBot/Modules/Gambling/GamblingService.cs index 98adc5dc2..7054fc81c 100644 --- a/src/NadekoBot/Modules/Gambling/GamblingService.cs +++ b/src/NadekoBot/Modules/Gambling/GamblingService.cs @@ -83,7 +83,7 @@ WHERE CurrencyAmount > {config.Decay.MinThreshold} AND UserId!={_client.CurrentU public async Task SlotAsync(ulong userId, long amount) { - var takeRes = await _cs.RemoveAsync(userId, "Slot Machine", amount, true); + var takeRes = await _cs.RemoveAsync(userId, amount, new("slot", "bet")); if (!takeRes) return new() { Error = GamblingError.NotEnough }; @@ -96,7 +96,7 @@ WHERE CurrencyAmount > {config.Decay.MinThreshold} AND UserId!={_client.CurrentU { won = (long)(result.Multiplier * amount); - await _cs.AddAsync(userId, $"Slot Machine x{result.Multiplier}", won, true); + await _cs.AddAsync(userId, won, new("slot", "win", $"Slot Machine x{result.Multiplier}")); } var toReturn = new SlotResponse { Multiplier = result.Multiplier, Won = won }; diff --git a/src/NadekoBot/Modules/Gambling/PlantPick/PlantPickService.cs b/src/NadekoBot/Modules/Gambling/PlantPick/PlantPickService.cs index 70933e12f..b8dd98d88 100644 --- a/src/NadekoBot/Modules/Gambling/PlantPick/PlantPickService.cs +++ b/src/NadekoBot/Modules/Gambling/PlantPick/PlantPickService.cs @@ -270,7 +270,7 @@ public class PlantPickService : INService if (amount > 0) // give the picked currency to the user - await _cs.AddAsync(uid, "Picked currency", amount); + await _cs.AddAsync(uid, amount, new("currency", "collect")); uow.SaveChanges(); } @@ -337,14 +337,14 @@ public class PlantPickService : INService return false; // remove currency from the user who's planting - if (await _cs.RemoveAsync(uid, "Planted currency", amount)) + if (await _cs.RemoveAsync(uid, amount, new("put/collect", "put"))) { // try to send the message with the currency image var msgId = await SendPlantMessageAsync(gid, ch, user, amount, pass); if (msgId is null) { // if it fails it will return null, if it returns null, refund - await _cs.AddAsync(uid, "Planted currency refund", amount); + await _cs.AddAsync(uid, amount, new("put/collect", "refund")); return false; } diff --git a/src/NadekoBot/Modules/Gambling/Raffle/CurrencyRaffleService.cs b/src/NadekoBot/Modules/Gambling/Raffle/CurrencyRaffleService.cs index 6d456797b..e9f4a4646 100644 --- a/src/NadekoBot/Modules/Gambling/Raffle/CurrencyRaffleService.cs +++ b/src/NadekoBot/Modules/Gambling/Raffle/CurrencyRaffleService.cs @@ -38,7 +38,7 @@ public class CurrencyRaffleService : INService //remove money, and stop the game if this // user created it and doesn't have the money - if (!await _cs.RemoveAsync(user.Id, "Currency Raffle Join", amount)) + if (!await _cs.RemoveAsync(user.Id, amount, new("raffle", "join"))) { if (newGame) Games.Remove(channelId); @@ -47,7 +47,7 @@ public class CurrencyRaffleService : INService if (!crg.AddUser(user, amount)) { - await _cs.AddAsync(user.Id, "Curency Raffle Refund", amount); + await _cs.AddAsync(user.Id, amount, new("raffle", "refund")); return (null, JoinErrorType.AlreadyJoinedOrInvalidAmount); } @@ -62,7 +62,7 @@ public class CurrencyRaffleService : INService var winner = crg.GetWinner(); var won = crg.Users.Sum(x => x.Amount); - await _cs.AddAsync(winner.DiscordUser.Id, "Currency Raffle Win", won); + await _cs.AddAsync(winner.DiscordUser.Id, won, new("raffle", "win")); Games.Remove(channelId, out _); _ = onEnded(winner.DiscordUser, won); } diff --git a/src/NadekoBot/Modules/Gambling/Shop/ShopCommands.cs b/src/NadekoBot/Modules/Gambling/Shop/ShopCommands.cs index 0a10a5b43..d353d96c9 100644 --- a/src/NadekoBot/Modules/Gambling/Shop/ShopCommands.cs +++ b/src/NadekoBot/Modules/Gambling/Shop/ShopCommands.cs @@ -115,7 +115,7 @@ public partial class Gambling return; } - if (await _cs.RemoveAsync(ctx.User.Id, $"Shop purchase - {entry.Type}", entry.Price)) + if (await _cs.RemoveAsync(ctx.User.Id, entry.Price, new("shop", "buy", entry.Type.ToString()))) { try { @@ -124,14 +124,14 @@ public partial class Gambling catch (Exception ex) { Log.Warning(ex, "Error adding shop role"); - await _cs.AddAsync(ctx.User.Id, "Shop error refund", entry.Price); + await _cs.AddAsync(ctx.User.Id, entry.Price, new("shop", "error-refund")); await ReplyErrorLocalizedAsync(strs.shop_role_purchase_error); return; } var profit = GetProfitAmount(entry.Price); - await _cs.AddAsync(entry.AuthorId, $"Shop sell item - {entry.Type}", profit); - await _cs.AddAsync(ctx.Client.CurrentUser.Id, "Shop sell item - cut", entry.Price - profit); + await _cs.AddAsync(entry.AuthorId, profit, new("shop", "sell", $"Shop sell item - {entry.Type}")); + await _cs.AddAsync(ctx.Client.CurrentUser.Id, entry.Price - profit, new("shop", "cut")); await ReplyConfirmLocalizedAsync(strs.shop_role_purchase(Format.Bold(role.Name))); return; } @@ -150,7 +150,7 @@ public partial class Gambling var item = entry.Items.ToArray()[new NadekoRandom().Next(0, entry.Items.Count)]; - if (await _cs.RemoveAsync(ctx.User.Id, $"Shop purchase - {entry.Type}", entry.Price)) + if (await _cs.RemoveAsync(ctx.User.Id, entry.Price, new("shop", "buy", entry.Type.ToString()))) { await using (var uow = _db.GetDbContext()) { @@ -168,12 +168,12 @@ public partial class Gambling .AddField(GetText(strs.name), entry.Name, true)); await _cs.AddAsync(entry.AuthorId, - $"Shop sell item - {entry.Name}", - GetProfitAmount(entry.Price)); + GetProfitAmount(entry.Price), + new("shop", "sell", entry.Name)); } catch { - await _cs.AddAsync(ctx.User.Id, $"Shop error refund - {entry.Name}", entry.Price); + await _cs.AddAsync(ctx.User.Id, entry.Price, new("shop", "error-refund", entry.Name)); await using (var uow = _db.GetDbContext()) { var entries = new IndexedCollection(uow.GuildConfigsForId(ctx.Guild.Id, @@ -411,6 +411,7 @@ public partial class Gambling var embed = _eb.Create().WithOkColor(); if (entry.Type == ShopEntryType.Role) + { return embed .AddField(GetText(strs.name), GetText(strs.shop_role(Format.Bold(ctx.Guild.GetRole(entry.RoleId)?.Name @@ -418,10 +419,15 @@ public partial class Gambling true) .AddField(GetText(strs.price), entry.Price.ToString(), true) .AddField(GetText(strs.type), entry.Type.ToString(), true); + } + if (entry.Type == ShopEntryType.List) + { return embed.AddField(GetText(strs.name), entry.Name, true) .AddField(GetText(strs.price), entry.Price.ToString(), true) .AddField(GetText(strs.type), GetText(strs.random_unique_item), true); + } + //else if (entry.Type == ShopEntryType.Infinite_List) // return embed.AddField(GetText(strs.name), GetText(strs.shop_role(Format.Bold(entry.RoleName)), true)) // .AddField(GetText(strs.price), entry.Price.ToString(), true) diff --git a/src/NadekoBot/Modules/Gambling/VoteRewardService.cs b/src/NadekoBot/Modules/Gambling/VoteRewardService.cs index 9658fe1ad..68574a049 100644 --- a/src/NadekoBot/Modules/Gambling/VoteRewardService.cs +++ b/src/NadekoBot/Modules/Gambling/VoteRewardService.cs @@ -61,9 +61,8 @@ public class VoteRewardService : INService, IReadyExecutor var ids = data.Select(x => x.UserId).ToList(); await _currencyService.AddBulkAsync(ids, - data.Select(_ => "top.gg vote reward"), - data.Select(_ => _gamb.Data.VoteReward), - true); + _gamb.Data.VoteReward, + new("vote", "top.gg", "top.gg vote reward")); Log.Information("Rewarding {Count} top.gg voters", ids.Count()); } @@ -90,9 +89,8 @@ public class VoteRewardService : INService, IReadyExecutor var ids = data.Select(x => x.UserId).ToList(); await _currencyService.AddBulkAsync(ids, - data.Select(_ => "discords.com vote reward"), - data.Select(_ => _gamb.Data.VoteReward), - true); + _gamb.Data.VoteReward, + new("vote", "discords", "discords.com vote reward")); Log.Information("Rewarding {Count} discords.com voters", ids.Count()); } diff --git a/src/NadekoBot/Modules/Gambling/Waifus/WaifuService.cs b/src/NadekoBot/Modules/Gambling/Waifus/WaifuService.cs index 9575b5585..8434052af 100644 --- a/src/NadekoBot/Modules/Gambling/Waifus/WaifuService.cs +++ b/src/NadekoBot/Modules/Gambling/Waifus/WaifuService.cs @@ -45,7 +45,7 @@ public class WaifuService : INService // if waifu likes the person, gotta pay the penalty if (waifu.AffinityId == ownerUser.Id) { - if (!await _cs.RemoveAsync(owner.Id, "Waifu Transfer - affinity penalty", (int)(waifu.Price * 0.6), true)) + if (!await _cs.RemoveAsync(owner.Id, (int)(waifu.Price * 0.6), new("waifu", "affinity-penalty"))) // unable to pay 60% penalty return false; @@ -55,7 +55,7 @@ public class WaifuService : INService } else // if not, pay 10% fee { - if (!await _cs.RemoveAsync(owner.Id, "Waifu Transfer", waifu.Price / 10, true)) return false; + if (!await _cs.RemoveAsync(owner.Id, waifu.Price / 10, new("waifu", "transfer"))) return false; waifu.Price = (int)(waifu.Price * 0.95); // half of 10% = 5% price reduction if (waifu.Price < settings.Waifu.MinPrice) @@ -97,7 +97,7 @@ public class WaifuService : INService { await using var uow = _db.GetDbContext(); var price = GetResetPrice(user); - if (!await _cs.RemoveAsync(user.Id, "Waifu Reset", price, true)) + if (!await _cs.RemoveAsync(user.Id, price, new("waifu", "reset"))) return false; var affs = uow.WaifuUpdates.AsQueryable() @@ -144,7 +144,7 @@ public class WaifuService : INService { var claimer = uow.GetOrCreateUser(user); var waifu = uow.GetOrCreateUser(target); - if (!await _cs.RemoveAsync(user.Id, "Claimed Waifu", amount, true)) + if (!await _cs.RemoveAsync(user.Id, amount, new("waifu", "claim"))) { result = WaifuClaimResult.NotEnoughFunds; } @@ -160,7 +160,7 @@ public class WaifuService : INService } else if (isAffinity && amount > w.Price * settings.Waifu.Multipliers.CrushClaim) { - if (!await _cs.RemoveAsync(user.Id, "Claimed Waifu", amount, true)) + if (!await _cs.RemoveAsync(user.Id, amount, new("waifu", "claim"))) { result = WaifuClaimResult.NotEnoughFunds; } @@ -179,7 +179,7 @@ public class WaifuService : INService } else if (amount >= w.Price * settings.Waifu.Multipliers.NormalClaim) // if no affinity { - if (!await _cs.RemoveAsync(user.Id, "Claimed Waifu", amount, true)) + if (!await _cs.RemoveAsync(user.Id, amount, new("waifu", "claim"))) { result = WaifuClaimResult.NotEnoughFunds; } @@ -288,13 +288,13 @@ public class WaifuService : INService if (w.Affinity?.UserId == user.Id) { - await _cs.AddAsync(w.Waifu.UserId, "Waifu Compensation", amount, true); + await _cs.AddAsync(w.Waifu.UserId, amount, new("waifu", "compensation")); w.Price = (int)Math.Floor(w.Price * _gss.Data.Waifu.Multipliers.DivorceNewValue); result = DivorceResult.SucessWithPenalty; } else { - await _cs.AddAsync(user.Id, "Waifu Refund", amount, true); + await _cs.AddAsync(user.Id, amount, new("waifu", "refund")); result = DivorceResult.Success; } @@ -316,7 +316,7 @@ public class WaifuService : INService public async Task GiftWaifuAsync(IUser from, IUser giftedWaifu, WaifuItemModel itemObj) { - if (!await _cs.RemoveAsync(from, "Bought waifu item", itemObj.Price, gamble: true)) return false; + if (!await _cs.RemoveAsync(from, itemObj.Price, new("waifu", "item"))) return false; await using var uow = _db.GetDbContext(); var w = uow.WaifuInfo.ByWaifuUserId(giftedWaifu.Id, set => set.Include(x => x.Items).Include(x => x.Claimer)); diff --git a/src/NadekoBot/Modules/Gambling/Wheel/WheelOfFortune.cs b/src/NadekoBot/Modules/Gambling/Wheel/WheelOfFortune.cs index cc3d3e07f..ae866214c 100644 --- a/src/NadekoBot/Modules/Gambling/Wheel/WheelOfFortune.cs +++ b/src/NadekoBot/Modules/Gambling/Wheel/WheelOfFortune.cs @@ -29,7 +29,7 @@ public class WheelOfFortuneGame var amount = (long)(_bet * _config.WheelOfFortune.Multipliers[result]); if (amount > 0) - await _cs.AddAsync(_userId, "Wheel Of Fortune - won", amount, true); + await _cs.AddAsync(_userId, amount, new("wheel", "win")); return new() { Index = result, Amount = amount }; } diff --git a/src/NadekoBot/Modules/Gambling/Wheel/WheelOfFortuneCommands.cs b/src/NadekoBot/Modules/Gambling/Wheel/WheelOfFortuneCommands.cs index 19ca587ba..381a69a67 100644 --- a/src/NadekoBot/Modules/Gambling/Wheel/WheelOfFortuneCommands.cs +++ b/src/NadekoBot/Modules/Gambling/Wheel/WheelOfFortuneCommands.cs @@ -29,7 +29,7 @@ public partial class Gambling if (!await CheckBetMandatory(amount)) return; - if (!await _cs.RemoveAsync(ctx.User.Id, "Wheel Of Fortune - bet", amount, true)) + if (!await _cs.RemoveAsync(ctx.User.Id, amount, new("wheel", "bet"))) { await ReplyErrorLocalizedAsync(strs.not_enough(CurrencySign)); return; diff --git a/src/NadekoBot/Modules/Gambling/~Shared/RollDuelGame.cs b/src/NadekoBot/Modules/Gambling/~Shared/RollDuelGame.cs index 12bad0575..97e767253 100644 --- a/src/NadekoBot/Modules/Gambling/~Shared/RollDuelGame.cs +++ b/src/NadekoBot/Modules/Gambling/~Shared/RollDuelGame.cs @@ -86,16 +86,16 @@ public class RollDuelGame _locker.Release(); } - if (!await _cs.RemoveAsync(P1, "Roll Duel", Amount)) + if (!await _cs.RemoveAsync(P1, Amount, new("rollduel", "bet"))) { await OnEnded?.Invoke(this, Reason.NoFunds); CurrentState = State.Ended; return; } - if (!await _cs.RemoveAsync(P2, "Roll Duel", Amount)) + if (!await _cs.RemoveAsync(P2, Amount, new("rollduel", "bet"))) { - await _cs.AddAsync(P1, "Roll Duel - refund", Amount); + await _cs.AddAsync(P1, Amount, new("rollduel", "refund")); await OnEnded?.Invoke(this, Reason.NoFunds); CurrentState = State.Ended; return; @@ -114,9 +114,9 @@ public class RollDuelGame else Winner = P2; var won = (long)(Amount * 2 * 0.98f); - await _cs.AddAsync(Winner, "Roll Duel win", won); + await _cs.AddAsync(Winner, won, new("rollduel", "win")); - await _cs.AddAsync(_botId, "Roll Duel fee", (Amount * 2) - won); + await _cs.AddAsync(_botId, (Amount * 2) - won, new("rollduel", "fee")); } try { await OnGameTick?.Invoke(this); } diff --git a/src/NadekoBot/Modules/Games/Hangman/HangmanService.cs b/src/NadekoBot/Modules/Games/Hangman/HangmanService.cs index e379affb8..7c044d42c 100644 --- a/src/NadekoBot/Modules/Games/Hangman/HangmanService.cs +++ b/src/NadekoBot/Modules/Games/Hangman/HangmanService.cs @@ -96,7 +96,7 @@ public sealed class HangmanService : IHangmanService, ILateExecutor } if (rew > 0) - await _cs.AddAsync(msg.Author, "hangman win", rew, gamble: true); + await _cs.AddAsync(msg.Author, rew, new("hangman", "win")); await SendState((ITextChannel)msg.Channel, msg.Author, msg.Content, state); } diff --git a/src/NadekoBot/Modules/Games/Trivia/TriviaGame.cs b/src/NadekoBot/Modules/Games/Trivia/TriviaGame.cs index d22b34431..26eb3913d 100644 --- a/src/NadekoBot/Modules/Games/Trivia/TriviaGame.cs +++ b/src/NadekoBot/Modules/Games/Trivia/TriviaGame.cs @@ -251,7 +251,7 @@ public class TriviaGame var reward = _config.Trivia.CurrencyReward; if (reward > 0) - await _cs.AddAsync(guildUser, "Won trivia", reward, true); + await _cs.AddAsync(guildUser, reward, new("trivia", "win")); return; } diff --git a/src/NadekoBot/Modules/Utility/Patreon/PatreonRewardsService.cs b/src/NadekoBot/Modules/Utility/Patreon/PatreonRewardsService.cs index ebf944560..5e72fdabc 100644 --- a/src/NadekoBot/Modules/Utility/Patreon/PatreonRewardsService.cs +++ b/src/NadekoBot/Modules/Utility/Patreon/PatreonRewardsService.cs @@ -234,7 +234,7 @@ public class PatreonRewardsService : INService, IReadyExecutor await uow.SaveChangesAsync(); - await _currency.AddAsync(userId, "Patreon reward - new", eligibleFor, true); + await _currency.AddAsync(userId, eligibleFor, new("patreon", "new")); Log.Information("Sending new currency reward to {UserId}", userId); await SendMessageToUser(userId, @@ -249,7 +249,7 @@ public class PatreonRewardsService : INService, IReadyExecutor await uow.SaveChangesAsync(); - await _currency.AddAsync(userId, "Patreon reward - recurring", eligibleFor, true); + await _currency.AddAsync(userId, eligibleFor, new("patreon", "recurring")); Log.Information("Sending recurring currency reward to {UserId}", userId); await SendMessageToUser(userId, @@ -267,7 +267,7 @@ public class PatreonRewardsService : INService, IReadyExecutor usr.AmountRewardedThisMonth = toAward; await uow.SaveChangesAsync(); - await _currency.AddAsync(userId, "Patreon reward - update", toAward, true); + await _currency.AddAsync(userId, toAward, new("patreon", "update")); Log.Information("Sending updated currency reward to {UserId}", userId); await SendMessageToUser(userId, diff --git a/src/NadekoBot/Modules/Xp/XpService.cs b/src/NadekoBot/Modules/Xp/XpService.cs index dbe8c3068..d93d52b43 100644 --- a/src/NadekoBot/Modules/Xp/XpService.cs +++ b/src/NadekoBot/Modules/Xp/XpService.cs @@ -210,7 +210,7 @@ public class XpService : INService var crew = crews.FirstOrDefault(x => x.Level == i); if (crew is not null) //give the user the reward if it exists - await _cs.AddAsync(item.Key.User.Id, "Level-up Reward", crew.Amount); + await _cs.AddAsync(item.Key.User.Id, crew.Amount, new("xp", "level-up")); } } } diff --git a/src/NadekoBot/Services/Currency/CurrencyService.cs b/src/NadekoBot/Services/Currency/CurrencyService.cs new file mode 100644 index 000000000..20010f149 --- /dev/null +++ b/src/NadekoBot/Services/Currency/CurrencyService.cs @@ -0,0 +1,123 @@ +#nullable disable +using LinqToDB; +using NadekoBot.Services.Currency; +using NadekoBot.Services.Database.Models; + +namespace NadekoBot.Services; + +public class CurrencyService : ICurrencyService, INService +{ + private readonly DbService _db; + + public CurrencyService(DbService db) + => _db = db; + + public Task GetWalletAsync(ulong userId, CurrencyType type = CurrencyType.Default) + { + if (type == CurrencyType.Default) + { + return Task.FromResult(new DefaultWallet(userId, _db.GetDbContext())); + } + + throw new ArgumentOutOfRangeException(nameof(type)); + } + + public async Task AddBulkAsync( + IReadOnlyCollection userIds, + long amount, + Extra extra, + CurrencyType type = CurrencyType.Default) + { + if (type == CurrencyType.Default) + { + await using var ctx = _db.GetDbContext(); + foreach (var userId in userIds) + { + var wallet = new DefaultWallet(userId, ctx); + await wallet.Add(amount, extra); + } + + await ctx.SaveChangesAsync(); + return; + } + + throw new ArgumentOutOfRangeException(nameof(type)); + } + + public async Task RemoveBulkAsync( + IReadOnlyCollection userIds, + long amount, + Extra extra, + CurrencyType type = CurrencyType.Default) + { + if (type == CurrencyType.Default) + { + await using var ctx = _db.GetDbContext(); + await ctx.DiscordUser + .Where(x => userIds.Contains(x.UserId)) + .UpdateAsync(du => new() + { + CurrencyAmount = du.CurrencyAmount >= amount + ? du.CurrencyAmount - amount + : 0 + }); + return; + } + + throw new ArgumentOutOfRangeException(nameof(type)); + } + + private CurrencyTransaction GetCurrencyTransaction(ulong userId, string reason, long amount) + => new() { Amount = amount, UserId = userId, Reason = reason ?? "-" }; + + public async Task AddAsync( + ulong userId, + long amount, + Extra extra) + { + await using var wallet = await GetWalletAsync(userId); + await wallet.Add(amount, extra); + } + + public async Task AddAsync( + IUser user, + long amount, + Extra extra) + { + await using var wallet = await GetWalletAsync(user.Id); + await wallet.Add(amount, extra); + } + + public async Task RemoveAsync( + ulong userId, + long amount, + Extra extra) + { + await using var wallet = await GetWalletAsync(userId); + return await wallet.Take(amount, extra); + } + + public async Task RemoveAsync( + IUser user, + long amount, + Extra extra) + { + await using var wallet = await GetWalletAsync(user.Id); + return await wallet.Take(amount, extra); + } + + public async Task TransferAsync( + ulong from, + ulong to, + long amount, + string note) + { + await using var fromWallet = await GetWalletAsync(@from); + await using var toWallet = await GetWalletAsync(to); + + var extra = new Extra("transfer", "gift", note); + + return await fromWallet.Transfer(amount, toWallet, extra); + } + +} \ No newline at end of file diff --git a/src/NadekoBot/Services/Currency/CurrencyType.cs b/src/NadekoBot/Services/Currency/CurrencyType.cs new file mode 100644 index 000000000..5d2d28dd9 --- /dev/null +++ b/src/NadekoBot/Services/Currency/CurrencyType.cs @@ -0,0 +1,6 @@ +namespace NadekoBot.Services.Currency; + +public enum CurrencyType +{ + Default, +} \ No newline at end of file diff --git a/src/NadekoBot/Services/Currency/DefaultWallet.cs b/src/NadekoBot/Services/Currency/DefaultWallet.cs new file mode 100644 index 000000000..eab3ac938 --- /dev/null +++ b/src/NadekoBot/Services/Currency/DefaultWallet.cs @@ -0,0 +1,104 @@ +using LinqToDB; +using LinqToDB.EntityFrameworkCore; +using NadekoBot.Services.Database; +using NadekoBot.Services.Database.Models; + +namespace NadekoBot.Services.Currency; + +public class DefaultWallet : IWallet +{ + public ulong UserId { get; } + + private readonly NadekoContext _ctx; + + public DefaultWallet(ulong userId, NadekoContext ctx) + { + UserId = userId; + _ctx = ctx; + } + + public Task GetBalance() + => _ctx.DiscordUser + .ToLinqToDBTable() + .Where(x => x.UserId == UserId) + .Select(x => x.CurrencyAmount) + .FirstOrDefaultAsync(); + + public async Task Take(long amount, Extra extra) + { + if (amount < 0) + throw new ArgumentOutOfRangeException(nameof(amount), "Amount to take must be non negative."); + + var changed = await _ctx.DiscordUser + .Where(x => x.UserId == UserId && x.CurrencyAmount >= amount) + .UpdateAsync(x => new() + { + CurrencyAmount = x.CurrencyAmount - amount + }); + + if (changed == 0) + return false; + + // todo type, subtype + // todo from? by? + await _ctx.CreateLinqToDbContext() + .InsertAsync(new CurrencyTransaction() + { + Amount = -amount, + Reason = extra.Note, + UserId = UserId, + }); + + return true; + } + + public async Task Add(long amount, Extra extra) + { + if (amount <= 0) + throw new ArgumentOutOfRangeException(nameof(amount), "Amount must be greater than 0."); + + await using (var tran = await _ctx.Database.BeginTransactionAsync()) + { + var changed = await _ctx.DiscordUser + .UpdateAsync(x => new() + { + CurrencyAmount = x.CurrencyAmount + amount + }); + + if (changed == 0) + { + await _ctx.DiscordUser + .ToLinqToDBTable() + .Value(x => x.UserId, UserId) + .Value(x => x.Username, "Unknown") + .Value(x => x.Discriminator, "????") + .Value(x => x.CurrencyAmount, amount) + .InsertAsync(); + } + + await tran.CommitAsync(); + } + + var ct = new CurrencyTransaction() + { + Amount = amount, + Reason = extra.Note, + UserId = UserId, + }; + + await _ctx.CreateLinqToDbContext() + .InsertAsync(ct); + } + + public void Dispose() + { + _ctx.SaveChanges(); + _ctx.Dispose(); + } + + public async ValueTask DisposeAsync() + { + await _ctx.SaveChangesAsync(); + await _ctx.DisposeAsync(); + } +} \ No newline at end of file diff --git a/src/NadekoBot/Services/Currency/Extra.cs b/src/NadekoBot/Services/Currency/Extra.cs new file mode 100644 index 000000000..3a9d12dee --- /dev/null +++ b/src/NadekoBot/Services/Currency/Extra.cs @@ -0,0 +1,3 @@ +namespace NadekoBot.Services.Currency; + +public record class Extra(string Type, string Subtype, string Note = "", ulong OtherId = 0); \ No newline at end of file diff --git a/src/NadekoBot/Services/Currency/ICurrencyService.cs b/src/NadekoBot/Services/Currency/ICurrencyService.cs new file mode 100644 index 000000000..b2747038d --- /dev/null +++ b/src/NadekoBot/Services/Currency/ICurrencyService.cs @@ -0,0 +1,47 @@ +using NadekoBot.Services.Currency; + +#nullable disable +namespace NadekoBot.Services; + +public interface ICurrencyService +{ + Task GetWalletAsync(ulong userId, CurrencyType type = CurrencyType.Default); + + Task AddBulkAsync( + IReadOnlyCollection userIds, + long amount, + Extra extra, + CurrencyType type = CurrencyType.Default); + + Task RemoveBulkAsync( + IReadOnlyCollection userIds, + long amount, + Extra extra, + CurrencyType type = CurrencyType.Default); + + Task AddAsync( + ulong userId, + long amount, + Extra extra); + + Task AddAsync( + IUser user, + long amount, + Extra extra); + + Task RemoveAsync( + ulong userId, + long amount, + Extra extra); + + Task RemoveAsync( + IUser user, + long amount, + Extra extra); + + Task TransferAsync( + ulong from, + ulong to, + long amount, + string note); +} \ No newline at end of file diff --git a/src/NadekoBot/Services/Currency/IWallet.cs b/src/NadekoBot/Services/Currency/IWallet.cs new file mode 100644 index 000000000..40fb8e4cc --- /dev/null +++ b/src/NadekoBot/Services/Currency/IWallet.cs @@ -0,0 +1,36 @@ +namespace NadekoBot.Services.Currency; + +public interface IWallet : IDisposable, IAsyncDisposable +{ + public ulong UserId { get; } + + public Task GetBalance(); + public Task Take(long amount, Extra extra); + public Task Add(long amount, Extra extra); + + public async Task Transfer( + long amount, + IWallet to, + Extra extra) + { + if (amount <= 0) + throw new ArgumentOutOfRangeException(nameof(amount), "Amount must be greater than 0."); + + var succ = await Take(amount, + extra with + { + OtherId = to.UserId + }); + + if (!succ) + return false; + + await to.Add(amount, + extra with + { + OtherId = UserId + }); + + return true; + } +} \ No newline at end of file diff --git a/src/NadekoBot/Services/ICurrencyService.cs b/src/NadekoBot/Services/ICurrencyService.cs deleted file mode 100644 index 4ec6c19d3..000000000 --- a/src/NadekoBot/Services/ICurrencyService.cs +++ /dev/null @@ -1,43 +0,0 @@ -#nullable disable -namespace NadekoBot.Services; - -public interface ICurrencyService -{ - Task AddAsync( - ulong userId, - string reason, - long amount, - bool gamble = false); - - Task AddAsync( - IUser user, - string reason, - long amount, - bool sendMessage = false, - bool gamble = false); - - Task AddBulkAsync( - IEnumerable userIds, - IEnumerable reasons, - IEnumerable amounts, - bool gamble = false); - - Task RemoveAsync( - ulong userId, - string reason, - long amount, - bool gamble = false); - - Task RemoveAsync( - IUser userId, - string reason, - long amount, - bool sendMessage = false, - bool gamble = false); - - Task RemoveBulkAsync( - IEnumerable userIds, - IEnumerable reasons, - IEnumerable amounts, - bool gamble = false); -} \ No newline at end of file diff --git a/src/NadekoBot/Services/INService.cs b/src/NadekoBot/Services/INService.cs index 13d88b8b6..ba02c996c 100644 --- a/src/NadekoBot/Services/INService.cs +++ b/src/NadekoBot/Services/INService.cs @@ -1,3 +1,5 @@ +using NadekoBot.Services.Currency; + #nullable disable namespace NadekoBot.Services; diff --git a/src/NadekoBot/Services/Impl/CurrencyService.cs b/src/NadekoBot/Services/Impl/CurrencyService.cs deleted file mode 100644 index aa4f717d3..000000000 --- a/src/NadekoBot/Services/Impl/CurrencyService.cs +++ /dev/null @@ -1,185 +0,0 @@ -#nullable disable -using NadekoBot.Db; -using NadekoBot.Modules.Gambling.Services; -using NadekoBot.Services.Database; -using NadekoBot.Services.Database.Models; - -namespace NadekoBot.Services; - -public class CurrencyService : ICurrencyService, INService -{ - private readonly DbService _db; - private readonly GamblingConfigService _gss; - private readonly IEmbedBuilderService _eb; - private readonly IUser _bot; - - public CurrencyService( - DbService db, - DiscordSocketClient c, - GamblingConfigService gss, - IEmbedBuilderService eb) - { - _db = db; - _gss = gss; - _eb = eb; - _bot = c.CurrentUser; - } - - private CurrencyTransaction GetCurrencyTransaction(ulong userId, string reason, long amount) - => new() { Amount = amount, UserId = userId, Reason = reason ?? "-" }; - - private bool InternalChange( - ulong userId, - string userName, - string discrim, - string avatar, - string reason, - long amount, - bool gamble, - NadekoContext uow) - { - var result = uow.TryUpdateCurrencyState(userId, userName, discrim, avatar, amount); - if (result) - { - var t = GetCurrencyTransaction(userId, reason, amount); - uow.CurrencyTransactions.Add(t); - - if (gamble) - { - var t2 = GetCurrencyTransaction(_bot.Id, reason, -amount); - uow.CurrencyTransactions.Add(t2); - uow.TryUpdateCurrencyState(_bot.Id, _bot.Username, _bot.Discriminator, _bot.AvatarId, -amount, true); - } - } - - return result; - } - - private async Task InternalAddAsync( - ulong userId, - string userName, - string discrim, - string avatar, - string reason, - long amount, - bool gamble) - { - if (amount < 0) - throw new ArgumentException("You can't add negative amounts. Use RemoveAsync method for that.", - nameof(amount)); - - await using var uow = _db.GetDbContext(); - InternalChange(userId, userName, discrim, avatar, reason, amount, gamble, uow); - await uow.SaveChangesAsync(); - } - - public Task AddAsync( - ulong userId, - string reason, - long amount, - bool gamble = false) - => InternalAddAsync(userId, null, null, null, reason, amount, gamble); - - public async Task AddAsync( - IUser user, - string reason, - long amount, - bool sendMessage = false, - bool gamble = false) - { - await InternalAddAsync(user.Id, user.Username, user.Discriminator, user.AvatarId, reason, amount, gamble); - if (sendMessage) - try - { - var sign = _gss.Data.Currency.Sign; - await user.EmbedAsync(_eb.Create() - .WithOkColor() - .WithTitle("Received Currency") - .AddField("Amount", amount + sign) - .AddField("Reason", reason)); - } - catch - { - // ignored - } - } - - public async Task AddBulkAsync( - IEnumerable userIds, - IEnumerable reasons, - IEnumerable amounts, - bool gamble = false) - { - var idArray = userIds as ulong[] ?? userIds.ToArray(); - var reasonArray = reasons as string[] ?? reasons.ToArray(); - var amountArray = amounts as long[] ?? amounts.ToArray(); - - if (idArray.Length != reasonArray.Length || reasonArray.Length != amountArray.Length) - throw new ArgumentException("Cannot perform bulk operation. Arrays are not of equal length."); - - var userIdHashSet = new HashSet(idArray.Length); - await using var uow = _db.GetDbContext(); - for (var i = 0; i < idArray.Length; i++) - // i have to prevent same user changing more than once as it will cause db error - if (userIdHashSet.Add(idArray[i])) - InternalChange(idArray[i], null, null, null, reasonArray[i], amountArray[i], gamble, uow); - await uow.SaveChangesAsync(); - } - - public async Task RemoveBulkAsync( - IEnumerable userIds, - IEnumerable reasons, - IEnumerable amounts, - bool gamble = false) - { - var idArray = userIds as ulong[] ?? userIds.ToArray(); - var reasonArray = reasons as string[] ?? reasons.ToArray(); - var amountArray = amounts as long[] ?? amounts.ToArray(); - - if (idArray.Length != reasonArray.Length || reasonArray.Length != amountArray.Length) - throw new ArgumentException("Cannot perform bulk operation. Arrays are not of equal length."); - - var userIdHashSet = new HashSet(idArray.Length); - await using var uow = _db.GetDbContext(); - for (var i = 0; i < idArray.Length; i++) - // i have to prevent same user changing more than once as it will cause db error - if (userIdHashSet.Add(idArray[i])) - InternalChange(idArray[i], null, null, null, reasonArray[i], -amountArray[i], gamble, uow); - await uow.SaveChangesAsync(); - } - - private async Task InternalRemoveAsync( - ulong userId, - string userName, - string userDiscrim, - string avatar, - string reason, - long amount, - bool gamble = false) - { - if (amount < 0) - throw new ArgumentException("You can't remove negative amounts. Use AddAsync method for that.", - nameof(amount)); - - bool result; - await using var uow = _db.GetDbContext(); - result = InternalChange(userId, userName, userDiscrim, avatar, reason, -amount, gamble, uow); - await uow.SaveChangesAsync(); - return result; - } - - public Task RemoveAsync( - ulong userId, - string reason, - long amount, - bool gamble = false) - => InternalRemoveAsync(userId, null, null, null, reason, amount, gamble); - - public Task RemoveAsync( - IUser user, - string reason, - long amount, - bool sendMessage = false, - bool gamble = false) - => InternalRemoveAsync(user.Id, user.Username, user.Discriminator, user.AvatarId, reason, amount, gamble); -} \ No newline at end of file