diff --git a/src/Nadeko.Common/ShmartBankAmount.cs b/src/Nadeko.Common/ShmartBankAmount.cs new file mode 100644 index 000000000..28486026c --- /dev/null +++ b/src/Nadeko.Common/ShmartBankAmount.cs @@ -0,0 +1,19 @@ +namespace Nadeko.Common; + +public readonly struct ShmartBankAmount +{ + public long Amount { get; } + public ShmartBankAmount(long amount) + { + Amount = amount; + } + + public static implicit operator ShmartBankAmount(long num) + => new(num); + + public static implicit operator long(ShmartBankAmount num) + => num.Amount; + + public static implicit operator ShmartBankAmount(int num) + => new(num); +} \ No newline at end of file diff --git a/src/Nadeko.Common/ShmartNumber.cs b/src/Nadeko.Common/ShmartNumber.cs index 42723df98..dcfdbcc6b 100644 --- a/src/Nadeko.Common/ShmartNumber.cs +++ b/src/Nadeko.Common/ShmartNumber.cs @@ -3,12 +3,10 @@ public readonly struct ShmartNumber : IEquatable { public long Value { get; } - public string? Input { get; } - public ShmartNumber(long val, string? input = null) + public ShmartNumber(long val) { Value = val; - Input = input; } public static implicit operator ShmartNumber(long num) diff --git a/src/NadekoBot/Common/TypeReaders/Shmart/BaseShmartInputAmountReader.cs b/src/NadekoBot/Common/TypeReaders/Shmart/BaseShmartInputAmountReader.cs new file mode 100644 index 000000000..972714004 --- /dev/null +++ b/src/NadekoBot/Common/TypeReaders/Shmart/BaseShmartInputAmountReader.cs @@ -0,0 +1,94 @@ +using System.Text.RegularExpressions; +using NadekoBot.Db; +using NadekoBot.Modules.Gambling.Services; +using NCalc; +using OneOf; + +namespace NadekoBot.Common.TypeReaders; + +public class BaseShmartInputAmountReader +{ + private static readonly Regex _percentRegex = new(@"^((?100|\d{1,2})%)$", RegexOptions.Compiled); + protected readonly DbService _db; + protected readonly GamblingConfigService _gambling; + + public BaseShmartInputAmountReader(DbService db, GamblingConfigService gambling) + { + _db = db; + _gambling = gambling; + } + + public async ValueTask>> ReadAsync(ICommandContext context, string input) + { + var i = input.Trim().ToUpperInvariant(); + + i = i.Replace("K", "000"); + + //can't add m because it will conflict with max atm + + if (await TryHandlePercentage(context, i) is long num) + { + return num; + } + + try + { + var expr = new Expression(i, EvaluateOptions.IgnoreCase); + expr.EvaluateParameter += (str, ev) => EvaluateParam(str, ev, context).GetAwaiter().GetResult(); + return (long)decimal.Parse(expr.Evaluate().ToString()!); + } + catch (Exception) + { + return new OneOf.Types.Error($"Invalid input: {input}"); + } + } + + private async Task EvaluateParam(string name, ParameterArgs args, ICommandContext ctx) + { + switch (name.ToUpperInvariant()) + { + case "PI": + args.Result = Math.PI; + break; + case "E": + args.Result = Math.E; + break; + case "ALL": + case "ALLIN": + args.Result = await Cur(ctx); + break; + case "HALF": + args.Result = await Cur(ctx) / 2; + break; + case "MAX": + args.Result = await Max(ctx); + break; + } + } + + protected virtual async Task Cur(ICommandContext ctx) + { + await using var uow = _db.GetDbContext(); + return await uow.DiscordUser.GetUserCurrencyAsync(ctx.User.Id); + } + + protected virtual async Task Max(ICommandContext ctx) + { + var settings = _gambling.Data; + var max = settings.MaxBet; + return max == 0 ? await Cur(ctx) : max; + } + + private async Task TryHandlePercentage(ICommandContext ctx, string input) + { + var m = _percentRegex.Match(input); + + if (m.Captures.Count == 0) + return null; + + if (!long.TryParse(m.Groups["num"].ToString(), out var percent)) + return null; + + return (long)(await Cur(ctx) * (percent / 100.0f)); + } +} \ No newline at end of file diff --git a/src/NadekoBot/Common/TypeReaders/Shmart/ShmartBankAmountTypeReader.cs b/src/NadekoBot/Common/TypeReaders/Shmart/ShmartBankAmountTypeReader.cs new file mode 100644 index 000000000..8dc93f525 --- /dev/null +++ b/src/NadekoBot/Common/TypeReaders/Shmart/ShmartBankAmountTypeReader.cs @@ -0,0 +1,32 @@ +#nullable disable +using NadekoBot.Modules.Gambling.Bank; +using NadekoBot.Modules.Gambling.Services; + +namespace NadekoBot.Common.TypeReaders; + +public sealed class ShmartBankAmountTypeReader : NadekoTypeReader +{ + private readonly IBankService _bank; + private readonly ShmartBankInputAmountReader _tr; + + public ShmartBankAmountTypeReader(IBankService bank, DbService db, GamblingConfigService gambling) + { + _bank = bank; + _tr = new ShmartBankInputAmountReader(bank, db, gambling); + } + + public override async ValueTask> ReadAsync(ICommandContext ctx, string input) + { + if (string.IsNullOrWhiteSpace(input)) + return TypeReaderResult.FromError(CommandError.ParseFailed, "Input is empty."); + + var result = await _tr.ReadAsync(ctx, input); + + if (result.TryPickT0(out var val, out var err)) + { + return TypeReaderResult.FromSuccess(new(val)); + } + + return TypeReaderResult.FromError(CommandError.Unsuccessful, err.Value); + } +} \ No newline at end of file diff --git a/src/NadekoBot/Common/TypeReaders/Shmart/ShmartBankInputAmountReader.cs b/src/NadekoBot/Common/TypeReaders/Shmart/ShmartBankInputAmountReader.cs new file mode 100644 index 000000000..401ccf42b --- /dev/null +++ b/src/NadekoBot/Common/TypeReaders/Shmart/ShmartBankInputAmountReader.cs @@ -0,0 +1,21 @@ +using NadekoBot.Modules.Gambling.Bank; +using NadekoBot.Modules.Gambling.Services; + +namespace NadekoBot.Common.TypeReaders; + +public sealed class ShmartBankInputAmountReader : BaseShmartInputAmountReader +{ + private readonly IBankService _bank; + + public ShmartBankInputAmountReader(IBankService bank, DbService db, GamblingConfigService gambling) + : base(db, gambling) + { + _bank = bank; + } + + protected override Task Cur(ICommandContext ctx) + => _bank.GetBalanceAsync(ctx.User.Id); + + protected override Task Max(ICommandContext ctx) + => Cur(ctx); +} \ No newline at end of file diff --git a/src/NadekoBot/Common/TypeReaders/Shmart/ShmartNumberTypeReader.cs b/src/NadekoBot/Common/TypeReaders/Shmart/ShmartNumberTypeReader.cs new file mode 100644 index 000000000..ea98dd6f8 --- /dev/null +++ b/src/NadekoBot/Common/TypeReaders/Shmart/ShmartNumberTypeReader.cs @@ -0,0 +1,29 @@ +#nullable disable +using NadekoBot.Modules.Gambling.Services; + +namespace NadekoBot.Common.TypeReaders; + +public sealed class ShmartNumberTypeReader : NadekoTypeReader +{ + private readonly BaseShmartInputAmountReader _tr; + + public ShmartNumberTypeReader(DbService db, GamblingConfigService gambling) + { + _tr = new BaseShmartInputAmountReader(db, gambling); + } + + public override async ValueTask> ReadAsync(ICommandContext ctx, string input) + { + if (string.IsNullOrWhiteSpace(input)) + return TypeReaderResult.FromError(CommandError.ParseFailed, "Input is empty."); + + var result = await _tr.ReadAsync(ctx, input); + + if (result.TryPickT0(out var val, out var err)) + { + return TypeReaderResult.FromSuccess(new(val)); + } + + return TypeReaderResult.FromError(CommandError.Unsuccessful, err.Value); + } +} \ No newline at end of file diff --git a/src/NadekoBot/Common/TypeReaders/ShmartNumberTypeReader.cs b/src/NadekoBot/Common/TypeReaders/ShmartNumberTypeReader.cs deleted file mode 100644 index 494ce8007..000000000 --- a/src/NadekoBot/Common/TypeReaders/ShmartNumberTypeReader.cs +++ /dev/null @@ -1,100 +0,0 @@ -#nullable disable -using NadekoBot.Db; -using NadekoBot.Modules.Gambling.Services; -using NCalc; -using System.Text.RegularExpressions; -using Nadeko.Common; - -namespace NadekoBot.Common.TypeReaders; - -public sealed class ShmartNumberTypeReader : NadekoTypeReader -{ - private static readonly Regex _percentRegex = new(@"^((?100|\d{1,2})%)$", RegexOptions.Compiled); - private readonly DbService _db; - private readonly GamblingConfigService _gambling; - - public ShmartNumberTypeReader(DbService db, GamblingConfigService gambling) - { - _db = db; - _gambling = gambling; - } - - public override ValueTask> ReadAsync(ICommandContext context, string input) - { - if (string.IsNullOrWhiteSpace(input)) - return new(TypeReaderResult.FromError(CommandError.ParseFailed, "Input is empty.")); - - var i = input.Trim().ToUpperInvariant(); - - i = i.Replace("K", "000"); - - //can't add m because it will conflict with max atm - - if (TryHandlePercentage(context, i, out var num)) - return new(TypeReaderResult.FromSuccess(new ShmartNumber(num, i))); - try - { - var expr = new Expression(i, EvaluateOptions.IgnoreCase); - expr.EvaluateParameter += (str, ev) => EvaluateParam(str, ev, context); - var lon = (long)decimal.Parse(expr.Evaluate().ToString()); - return new(TypeReaderResult.FromSuccess(new ShmartNumber(lon, input))); - } - catch (Exception) - { - return ValueTask.FromResult( - TypeReaderResult.FromError(CommandError.ParseFailed, $"Invalid input: {input}")); - } - } - - private void EvaluateParam(string name, ParameterArgs args, ICommandContext ctx) - { - switch (name.ToUpperInvariant()) - { - case "PI": - args.Result = Math.PI; - break; - case "E": - args.Result = Math.E; - break; - case "ALL": - case "ALLIN": - args.Result = Cur(ctx); - break; - case "HALF": - args.Result = Cur(ctx) / 2; - break; - case "MAX": - args.Result = Max(ctx); - break; - } - } - - private long Cur(ICommandContext ctx) - { - using var uow = _db.GetDbContext(); - return uow.DiscordUser.GetUserCurrency(ctx.User.Id); - } - - private long Max(ICommandContext ctx) - { - var settings = _gambling.Data; - var max = settings.MaxBet; - return max == 0 ? Cur(ctx) : max; - } - - private bool TryHandlePercentage(ICommandContext ctx, string input, out long num) - { - num = 0; - var m = _percentRegex.Match(input); - if (m.Captures.Count != 0) - { - if (!long.TryParse(m.Groups["num"].ToString(), out var percent)) - return false; - - num = (long)(Cur(ctx) * (percent / 100.0f)); - return true; - } - - return false; - } -} \ No newline at end of file diff --git a/src/NadekoBot/Db/Extensions/DiscordUserExtensions.cs b/src/NadekoBot/Db/Extensions/DiscordUserExtensions.cs index 93620ffb4..a2eaaa179 100644 --- a/src/NadekoBot/Db/Extensions/DiscordUserExtensions.cs +++ b/src/NadekoBot/Db/Extensions/DiscordUserExtensions.cs @@ -108,8 +108,8 @@ public static class DiscordUserExtensions .Take(count) .ToList(); - public static long GetUserCurrency(this DbSet users, ulong userId) - => users.AsNoTracking().FirstOrDefault(x => x.UserId == userId)?.CurrencyAmount ?? 0; + public static async Task GetUserCurrencyAsync(this DbSet users, ulong userId) + => (await users.FirstOrDefaultAsyncLinqToDB(x => x.UserId == userId))?.CurrencyAmount ?? 0; public static void RemoveFromMany(this DbSet users, IEnumerable ids) { diff --git a/src/NadekoBot/Modules/Gambling/Bank/BankCommands.cs b/src/NadekoBot/Modules/Gambling/Bank/BankCommands.cs index e008259b7..c5b6f5963 100644 --- a/src/NadekoBot/Modules/Gambling/Bank/BankCommands.cs +++ b/src/NadekoBot/Modules/Gambling/Bank/BankCommands.cs @@ -1,4 +1,5 @@ using Nadeko.Common; +using NadekoBot.Common.TypeReaders; using NadekoBot.Modules.Gambling.Bank; using NadekoBot.Modules.Gambling.Common; using NadekoBot.Modules.Gambling.Services; @@ -35,7 +36,7 @@ public partial class Gambling } [Cmd] - public async Task BankWithdraw(ShmartNumber amount) + public async Task BankWithdraw(ShmartBankAmount amount) { if (amount <= 0) return; diff --git a/src/NadekoBot/Modules/Gambling/Gambling.cs b/src/NadekoBot/Modules/Gambling/Gambling.cs index 0ab3e92a2..20f1d4e61 100644 --- a/src/NadekoBot/Modules/Gambling/Gambling.cs +++ b/src/NadekoBot/Modules/Gambling/Gambling.cs @@ -663,7 +663,6 @@ public partial class Gambling : GamblingModule if (win > 0) { str = GetText(strs.br_win(N(win), result.Threshold + (result.Roll == 100 ? " 👑" : ""))); - await _cs.AddAsync(ctx.User, win, new("betroll", "win")); } else { diff --git a/src/NadekoBot/Modules/Gambling/GamblingService.cs b/src/NadekoBot/Modules/Gambling/GamblingService.cs index 0f7d5db81..9ee9c2e4b 100644 --- a/src/NadekoBot/Modules/Gambling/GamblingService.cs +++ b/src/NadekoBot/Modules/Gambling/GamblingService.cs @@ -142,7 +142,7 @@ public class GamblingService : INService, IReadyExecutor var onePercent = uow.DiscordUser.GetTopOnePercentCurrency(_client.CurrentUser.Id); decimal planted = uow.PlantedCurrency.AsQueryable().Sum(x => x.Amount); var waifus = uow.WaifuInfo.GetTotalValue(); - var bot = uow.DiscordUser.GetUserCurrency(_client.CurrentUser.Id); + var bot = await uow.DiscordUser.GetUserCurrencyAsync(_client.CurrentUser.Id); decimal bank = await uow.GetTable() .SumAsyncLinqToDB(x => x.Balance);