mirror of
https://gitlab.com/Kwoth/nadekobot.git
synced 2025-09-11 09:48:26 -04:00
Reworked currency service. Some features are missing
This commit is contained in:
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