Added the initial framework for the new Money system.

Includes
- 5 new tables: Gamblers, Transactions, Wagers, Exclusions, Perks
- Still heavily WIP and not ready to be enabled, no games present and a lot of missing functionality
- For now it's completely disabled until it's ready to be used.
This commit is contained in:
barelyprofessional
2025-08-20 14:59:09 -05:00
parent 8d100b013b
commit 6ca1cf055c
15 changed files with 1831 additions and 14 deletions

View File

@@ -1,7 +1,9 @@
using System.Text.RegularExpressions;
using Humanizer;
using KfChatDotNetBot.Commands;
using KfChatDotNetBot.Extensions;
using KfChatDotNetBot.Models.DbModels;
using KfChatDotNetBot.Settings;
using KfChatDotNetWsClient.Models.Events;
using NLog;
@@ -17,9 +19,9 @@ internal class BotCommands
private IEnumerable<ICommand> Commands;
private CancellationToken _cancellationToken;
internal BotCommands(ChatBot bot, CancellationToken? ctx = null)
internal BotCommands(ChatBot bot, CancellationToken ctx = default)
{
_cancellationToken = ctx ?? CancellationToken.None;
_cancellationToken = ctx;
_bot = bot;
var interfaceType = typeof(ICommand);
Commands =
@@ -60,6 +62,33 @@ internal class BotCommands
if (user == null) return;
if (user.Ignored) return;
var continueAfterProcess = HasAttribute<AllowAdditionalMatches>(command);
var kasinoCommand = HasAttribute<KasinoCommand>(command);
var wagerCommand = HasAttribute<WagerCommand>(command);
if (kasinoCommand)
{
var kasinoEnabled = SettingsProvider.GetValueAsync(BuiltIn.Keys.MoneyEnabled).Result.ToBoolean();
if (!kasinoEnabled) return;
}
if (kasinoCommand && user.IsPermanentlyBanned(_cancellationToken).Result)
{
_bot.SendChatMessage($"@{message.Author.Username}, you've been permanently banned from the kasino. Contact support for more information.", true);
return;
}
if (wagerCommand)
{
// GetGamblerEntity will only return null if the user is permanbanned
// and we have a check further up the chain for that hence ignoring the null
var exclusion = user.GetGamblerEntity(ct: _cancellationToken).Result
!.GetActiveExclusion(ct: _cancellationToken).Result;
if (exclusion != null)
{
_bot.SendChatMessage(
$"@{message.Author.Username}, you're self excluded from the kasino for another {(exclusion.Expires - DateTimeOffset.UtcNow).Humanize(precision: 3)}", true);
return;
}
}
if (user.UserRight < command.RequiredRight)
{
_bot.SendChatMessage($"@{message.Author.Username}, you do not have access to use this command. Your rank: {user.UserRight.Humanize()}; Required rank: {command.RequiredRight.Humanize()}", true);
@@ -89,7 +118,25 @@ internal class BotCommands
{
_logger.Error("Command task failed");
_logger.Error(task.Exception);
return;
}
var moneySettings =
await SettingsProvider.GetMultipleValuesAsync([BuiltIn.Keys.MoneyEnabled, BuiltIn.Keys.MoneySymbolSuffix]);
if (!moneySettings[BuiltIn.Keys.MoneyEnabled].ToBoolean()) return;
var wagerCommand = HasAttribute<WagerCommand>(command);
if (!wagerCommand) return;
var gambler = await user.GetGamblerEntity(ct: _cancellationToken);
if (gambler == null) return;
if (gambler.TotalWagered < gambler.NextVipLevelWagerRequirement) return;
// The reason for doing this instead of passing in TotalWagered is that otherwise VIP levels might
// get skipped if the user is a low VIP level but wagering very large amounts
var newLevel = Money.GetNextVipLevel(gambler.NextVipLevelWagerRequirement);
if (newLevel == null) return;
var payout = await gambler.UpgradeVipLevel(newLevel, _cancellationToken);
await _bot.SendChatMessageAsync(
$"🤑🤑 {user.KfUsername} has leveled up to to {newLevel.VipLevel.Icon} {newLevel.VipLevel.Name} Tier {newLevel.Tier} " +
$"and received a bonus of {payout:N2} {moneySettings[BuiltIn.Keys.MoneySymbolSuffix].Value}", true);
}
private static bool HasAttribute<T>(ICommand command) where T : Attribute
@@ -105,4 +152,20 @@ internal class BotCommands
/// Keep in mind since commands are executed in a throwaway task and not awaited, they will run concurrently
/// </summary>
[AttributeUsage(AttributeTargets.Class)]
internal class AllowAdditionalMatches : Attribute;
internal class AllowAdditionalMatches : Attribute;
/// <summary>
/// Use this on commands where a wager is taking place.
/// This will cause the bot to check total wagered and see if the gambler has leveled up.
/// It'll also check whether the gambler is currently temp excluded before running the command.
/// </summary>
[AttributeUsage(AttributeTargets.Class)]
internal class WagerCommand : Attribute;
/// <summary>
/// Use this on all commands that interact with the gambling / monetary system
/// When used, this will check if the system is globally enabled before running the command.
/// It'll also check whether the user is permanently banned before running the command.
/// </summary>
[AttributeUsage(AttributeTargets.Class)]
internal class KasinoCommand : Attribute;