mirror of
https://gitlab.com/Kwoth/nadekobot.git
synced 2025-09-10 17:28:27 -04:00
Reworked currency service. Some features are missing
This commit is contained in:
@@ -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
|
||||
resharper_arrange_redundant_parentheses_highlighting = hint
|
||||
|
@@ -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);
|
||||
});
|
||||
|
@@ -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<bool> 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;
|
||||
|
@@ -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)
|
||||
|
@@ -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 _);
|
||||
|
@@ -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 =>
|
||||
|
@@ -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 =>
|
||||
|
@@ -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
|
||||
{
|
||||
|
@@ -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<GamblingService>
|
||||
{
|
||||
public enum RpsPick
|
||||
@@ -67,10 +69,11 @@ public partial class Gambling : GamblingModule<GamblingService>
|
||||
return cur.ToString("C0", flowersCi);
|
||||
}
|
||||
|
||||
public string GetCurrency(ulong id)
|
||||
public async Task<string> 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<GamblingService>
|
||||
{
|
||||
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<GamblingService>
|
||||
.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<GamblingService>
|
||||
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<GamblingService>
|
||||
[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<GamblingService>
|
||||
{
|
||||
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<GamblingService>
|
||||
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<GamblingService>
|
||||
{
|
||||
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<GamblingService>
|
||||
{
|
||||
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<GamblingService>
|
||||
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<GamblingService>
|
||||
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<GamblingService>
|
||||
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<GamblingService>
|
||||
{
|
||||
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<GamblingService>
|
||||
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<GamblingService>
|
||||
|| (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)));
|
||||
|
@@ -83,7 +83,7 @@ WHERE CurrencyAmount > {config.Decay.MinThreshold} AND UserId!={_client.CurrentU
|
||||
|
||||
public async Task<SlotResponse> 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 };
|
||||
|
@@ -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;
|
||||
}
|
||||
|
||||
|
@@ -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);
|
||||
}
|
||||
|
@@ -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<ShopEntry>(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)
|
||||
|
@@ -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());
|
||||
}
|
||||
|
@@ -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<bool> 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));
|
||||
|
@@ -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 };
|
||||
}
|
||||
|
@@ -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;
|
||||
|
@@ -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); }
|
||||
|
@@ -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);
|
||||
}
|
||||
|
@@ -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;
|
||||
}
|
||||
|
||||
|
@@ -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,
|
||||
|
@@ -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"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
123
src/NadekoBot/Services/Currency/CurrencyService.cs
Normal file
123
src/NadekoBot/Services/Currency/CurrencyService.cs
Normal file
@@ -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<IWallet> GetWalletAsync(ulong userId, CurrencyType type = CurrencyType.Default)
|
||||
{
|
||||
if (type == CurrencyType.Default)
|
||||
{
|
||||
return Task.FromResult<IWallet>(new DefaultWallet(userId, _db.GetDbContext()));
|
||||
}
|
||||
|
||||
throw new ArgumentOutOfRangeException(nameof(type));
|
||||
}
|
||||
|
||||
public async Task AddBulkAsync(
|
||||
IReadOnlyCollection<ulong> 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<ulong> 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<bool> RemoveAsync(
|
||||
ulong userId,
|
||||
long amount,
|
||||
Extra extra)
|
||||
{
|
||||
await using var wallet = await GetWalletAsync(userId);
|
||||
return await wallet.Take(amount, extra);
|
||||
}
|
||||
|
||||
public async Task<bool> RemoveAsync(
|
||||
IUser user,
|
||||
long amount,
|
||||
Extra extra)
|
||||
{
|
||||
await using var wallet = await GetWalletAsync(user.Id);
|
||||
return await wallet.Take(amount, extra);
|
||||
}
|
||||
|
||||
public async Task<bool> 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);
|
||||
}
|
||||
|
||||
}
|
6
src/NadekoBot/Services/Currency/CurrencyType.cs
Normal file
6
src/NadekoBot/Services/Currency/CurrencyType.cs
Normal file
@@ -0,0 +1,6 @@
|
||||
namespace NadekoBot.Services.Currency;
|
||||
|
||||
public enum CurrencyType
|
||||
{
|
||||
Default,
|
||||
}
|
104
src/NadekoBot/Services/Currency/DefaultWallet.cs
Normal file
104
src/NadekoBot/Services/Currency/DefaultWallet.cs
Normal file
@@ -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<long> GetBalance()
|
||||
=> _ctx.DiscordUser
|
||||
.ToLinqToDBTable()
|
||||
.Where(x => x.UserId == UserId)
|
||||
.Select(x => x.CurrencyAmount)
|
||||
.FirstOrDefaultAsync();
|
||||
|
||||
public async Task<bool> 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();
|
||||
}
|
||||
}
|
3
src/NadekoBot/Services/Currency/Extra.cs
Normal file
3
src/NadekoBot/Services/Currency/Extra.cs
Normal file
@@ -0,0 +1,3 @@
|
||||
namespace NadekoBot.Services.Currency;
|
||||
|
||||
public record class Extra(string Type, string Subtype, string Note = "", ulong OtherId = 0);
|
47
src/NadekoBot/Services/Currency/ICurrencyService.cs
Normal file
47
src/NadekoBot/Services/Currency/ICurrencyService.cs
Normal file
@@ -0,0 +1,47 @@
|
||||
using NadekoBot.Services.Currency;
|
||||
|
||||
#nullable disable
|
||||
namespace NadekoBot.Services;
|
||||
|
||||
public interface ICurrencyService
|
||||
{
|
||||
Task<IWallet> GetWalletAsync(ulong userId, CurrencyType type = CurrencyType.Default);
|
||||
|
||||
Task AddBulkAsync(
|
||||
IReadOnlyCollection<ulong> userIds,
|
||||
long amount,
|
||||
Extra extra,
|
||||
CurrencyType type = CurrencyType.Default);
|
||||
|
||||
Task RemoveBulkAsync(
|
||||
IReadOnlyCollection<ulong> 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<bool> RemoveAsync(
|
||||
ulong userId,
|
||||
long amount,
|
||||
Extra extra);
|
||||
|
||||
Task<bool> RemoveAsync(
|
||||
IUser user,
|
||||
long amount,
|
||||
Extra extra);
|
||||
|
||||
Task<bool> TransferAsync(
|
||||
ulong from,
|
||||
ulong to,
|
||||
long amount,
|
||||
string note);
|
||||
}
|
36
src/NadekoBot/Services/Currency/IWallet.cs
Normal file
36
src/NadekoBot/Services/Currency/IWallet.cs
Normal file
@@ -0,0 +1,36 @@
|
||||
namespace NadekoBot.Services.Currency;
|
||||
|
||||
public interface IWallet : IDisposable, IAsyncDisposable
|
||||
{
|
||||
public ulong UserId { get; }
|
||||
|
||||
public Task<long> GetBalance();
|
||||
public Task<bool> Take(long amount, Extra extra);
|
||||
public Task Add(long amount, Extra extra);
|
||||
|
||||
public async Task<bool> 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;
|
||||
}
|
||||
}
|
@@ -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<ulong> userIds,
|
||||
IEnumerable<string> reasons,
|
||||
IEnumerable<long> amounts,
|
||||
bool gamble = false);
|
||||
|
||||
Task<bool> RemoveAsync(
|
||||
ulong userId,
|
||||
string reason,
|
||||
long amount,
|
||||
bool gamble = false);
|
||||
|
||||
Task<bool> RemoveAsync(
|
||||
IUser userId,
|
||||
string reason,
|
||||
long amount,
|
||||
bool sendMessage = false,
|
||||
bool gamble = false);
|
||||
|
||||
Task RemoveBulkAsync(
|
||||
IEnumerable<ulong> userIds,
|
||||
IEnumerable<string> reasons,
|
||||
IEnumerable<long> amounts,
|
||||
bool gamble = false);
|
||||
}
|
@@ -1,3 +1,5 @@
|
||||
using NadekoBot.Services.Currency;
|
||||
|
||||
#nullable disable
|
||||
namespace NadekoBot.Services;
|
||||
|
||||
|
@@ -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<ulong> userIds,
|
||||
IEnumerable<string> reasons,
|
||||
IEnumerable<long> 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<ulong>(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<ulong> userIds,
|
||||
IEnumerable<string> reasons,
|
||||
IEnumerable<long> 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<ulong>(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<bool> 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<bool> RemoveAsync(
|
||||
ulong userId,
|
||||
string reason,
|
||||
long amount,
|
||||
bool gamble = false)
|
||||
=> InternalRemoveAsync(userId, null, null, null, reason, amount, gamble);
|
||||
|
||||
public Task<bool> 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);
|
||||
}
|
Reference in New Issue
Block a user