mirror of
https://gitlab.com/Kwoth/nadekobot.git
synced 2025-09-10 17:28:27 -04:00
.bank withdraw <expression> will now correctly use bank amount for calculations. Fixed .br giving double win amounts
This commit is contained in:
19
src/Nadeko.Common/ShmartBankAmount.cs
Normal file
19
src/Nadeko.Common/ShmartBankAmount.cs
Normal file
@@ -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);
|
||||
}
|
@@ -3,12 +3,10 @@
|
||||
public readonly struct ShmartNumber : IEquatable<ShmartNumber>
|
||||
{
|
||||
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)
|
||||
|
@@ -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