mirror of
				https://gitlab.com/Kwoth/nadekobot.git
				synced 2025-11-04 00:34:26 -05:00 
			
		
		
		
	.bank withdraw <expression> will now correctly use bank amount for calculations. Fixed .br giving double win amounts
This commit is contained in:
		@@ -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(@"^((?<num>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<OneOf<long, OneOf.Types.Error<string>>> 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<string>($"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<long> Cur(ICommandContext ctx)
 | 
			
		||||
    {
 | 
			
		||||
        await using var uow = _db.GetDbContext();
 | 
			
		||||
        return await uow.DiscordUser.GetUserCurrencyAsync(ctx.User.Id);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected virtual async Task<long> Max(ICommandContext ctx)
 | 
			
		||||
    {
 | 
			
		||||
        var settings = _gambling.Data;
 | 
			
		||||
        var max = settings.MaxBet;
 | 
			
		||||
        return max == 0 ? await Cur(ctx) : max;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private async Task<long?> 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));
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -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<ShmartBankAmount>
 | 
			
		||||
{
 | 
			
		||||
    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<TypeReaderResult<ShmartBankAmount>> ReadAsync(ICommandContext ctx, string input)
 | 
			
		||||
    {
 | 
			
		||||
        if (string.IsNullOrWhiteSpace(input))
 | 
			
		||||
            return TypeReaderResult.FromError<ShmartBankAmount>(CommandError.ParseFailed, "Input is empty.");
 | 
			
		||||
 | 
			
		||||
        var result = await _tr.ReadAsync(ctx, input);
 | 
			
		||||
 | 
			
		||||
        if (result.TryPickT0(out var val, out var err))
 | 
			
		||||
        {
 | 
			
		||||
            return TypeReaderResult.FromSuccess<ShmartBankAmount>(new(val));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return TypeReaderResult.FromError<ShmartBankAmount>(CommandError.Unsuccessful, err.Value);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -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<long> Cur(ICommandContext ctx)
 | 
			
		||||
        => _bank.GetBalanceAsync(ctx.User.Id);
 | 
			
		||||
 | 
			
		||||
    protected override Task<long> Max(ICommandContext ctx)
 | 
			
		||||
        => Cur(ctx);
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,29 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
using NadekoBot.Modules.Gambling.Services;
 | 
			
		||||
 | 
			
		||||
namespace NadekoBot.Common.TypeReaders;
 | 
			
		||||
 | 
			
		||||
public sealed class ShmartNumberTypeReader : NadekoTypeReader<ShmartNumber>
 | 
			
		||||
{
 | 
			
		||||
    private readonly BaseShmartInputAmountReader _tr;
 | 
			
		||||
 | 
			
		||||
    public ShmartNumberTypeReader(DbService db, GamblingConfigService gambling)
 | 
			
		||||
    {
 | 
			
		||||
        _tr = new BaseShmartInputAmountReader(db, gambling);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public override async ValueTask<TypeReaderResult<ShmartNumber>> ReadAsync(ICommandContext ctx, string input)
 | 
			
		||||
    {
 | 
			
		||||
        if (string.IsNullOrWhiteSpace(input))
 | 
			
		||||
            return TypeReaderResult.FromError<ShmartNumber>(CommandError.ParseFailed, "Input is empty.");
 | 
			
		||||
 | 
			
		||||
        var result = await _tr.ReadAsync(ctx, input);
 | 
			
		||||
 | 
			
		||||
        if (result.TryPickT0(out var val, out var err))
 | 
			
		||||
        {
 | 
			
		||||
            return TypeReaderResult.FromSuccess<ShmartNumber>(new(val));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return TypeReaderResult.FromError<ShmartNumber>(CommandError.Unsuccessful, err.Value);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -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<ShmartNumber>
 | 
			
		||||
{
 | 
			
		||||
    private static readonly Regex _percentRegex = new(@"^((?<num>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<TypeReaderResult<ShmartNumber>> ReadAsync(ICommandContext context, string input)
 | 
			
		||||
    {
 | 
			
		||||
        if (string.IsNullOrWhiteSpace(input))
 | 
			
		||||
            return new(TypeReaderResult.FromError<ShmartNumber>(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<ShmartNumber>(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;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -108,8 +108,8 @@ public static class DiscordUserExtensions
 | 
			
		||||
                .Take(count)
 | 
			
		||||
                .ToList();
 | 
			
		||||
 | 
			
		||||
    public static long GetUserCurrency(this DbSet<DiscordUser> users, ulong userId)
 | 
			
		||||
        => users.AsNoTracking().FirstOrDefault(x => x.UserId == userId)?.CurrencyAmount ?? 0;
 | 
			
		||||
    public static async Task<long> GetUserCurrencyAsync(this DbSet<DiscordUser> users, ulong userId)
 | 
			
		||||
        => (await users.FirstOrDefaultAsyncLinqToDB(x => x.UserId == userId))?.CurrencyAmount ?? 0;
 | 
			
		||||
 | 
			
		||||
    public static void RemoveFromMany(this DbSet<DiscordUser> users, IEnumerable<ulong> ids)
 | 
			
		||||
    {
 | 
			
		||||
 
 | 
			
		||||
@@ -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;
 | 
			
		||||
 
 | 
			
		||||
@@ -663,7 +663,6 @@ public partial class Gambling : GamblingModule<GamblingService>
 | 
			
		||||
        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
 | 
			
		||||
        {
 | 
			
		||||
 
 | 
			
		||||
@@ -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<BankUser>()
 | 
			
		||||
                                        .SumAsyncLinqToDB(x => x.Balance);
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user