mirror of
https://github.com/barelyprofessional/KfChatDotNet.git
synced 2026-05-02 04:22:04 -04:00
Compare commits
8 Commits
1778d0d573
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cf62274b4b | ||
|
|
972e880aa9 | ||
|
|
f5f0ba6323 | ||
|
|
ab94098dd2 | ||
|
|
c79105bb44 | ||
|
|
000c87266e | ||
|
|
7981f57a34 | ||
|
|
e725ca5864 |
@@ -12,8 +12,8 @@ public class ApplicationDbContext : DbContext
|
|||||||
|
|
||||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||||
{
|
{
|
||||||
base.OnModelCreating(modelBuilder);
|
//modelBuilder.Entity<KasinoShopProfileDbModel>()
|
||||||
|
// .OwnsOne(p => p.StateData, b => b.ToJson());
|
||||||
}
|
}
|
||||||
|
|
||||||
public DbSet<UserDbModel> Users { get; set; }
|
public DbSet<UserDbModel> Users { get; set; }
|
||||||
|
|||||||
@@ -18,7 +18,8 @@ public class GetBalanceCommand : ICommand
|
|||||||
{
|
{
|
||||||
public List<Regex> Patterns => [
|
public List<Regex> Patterns => [
|
||||||
new Regex("^balance", RegexOptions.IgnoreCase),
|
new Regex("^balance", RegexOptions.IgnoreCase),
|
||||||
new Regex("^bal$", RegexOptions.IgnoreCase)
|
new Regex("^bal$", RegexOptions.IgnoreCase),
|
||||||
|
new Regex("^bal exact$", RegexOptions.IgnoreCase)
|
||||||
];
|
];
|
||||||
public string? HelpText => "Get your gamba balance";
|
public string? HelpText => "Get your gamba balance";
|
||||||
public UserRight RequiredRight => UserRight.Loser;
|
public UserRight RequiredRight => UserRight.Loser;
|
||||||
@@ -30,8 +31,17 @@ public class GetBalanceCommand : ICommand
|
|||||||
CancellationToken ctx)
|
CancellationToken ctx)
|
||||||
{
|
{
|
||||||
var gambler = await Money.GetGamblerEntityAsync(user.Id, ct: ctx);
|
var gambler = await Money.GetGamblerEntityAsync(user.Id, ct: ctx);
|
||||||
|
if (message.MessageRawHtmlDecoded.EndsWith("exact"))
|
||||||
|
{
|
||||||
|
await botInstance.SendChatMessageAsync(
|
||||||
|
$"{user.FormatUsername()}, your balance is {gambler!.Balance}", true);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
await botInstance.SendChatMessageAsync(
|
await botInstance.SendChatMessageAsync(
|
||||||
$"{user.FormatUsername()}, your balance is {await gambler!.Balance.FormatKasinoCurrencyAsync()}", true);
|
$"{user.FormatUsername()}, your balance is {await gambler!.Balance.FormatKasinoCurrencyAsync()}", true);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
if (botInstance.BotServices.KasinoShop != null)
|
if (botInstance.BotServices.KasinoShop != null)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -81,7 +81,7 @@ public class KrashBetCommand : ICommand
|
|||||||
if (wager > gambler.Balance)
|
if (wager > gambler.Balance)
|
||||||
{
|
{
|
||||||
await botInstance.SendChatMessageAsync(
|
await botInstance.SendChatMessageAsync(
|
||||||
$"{user.FormatUsername()}, your balance of {gambler.Balance} is not enough to bet {wager} on krash.",
|
$"{user.FormatUsername()}, your balance of {await gambler.Balance.FormatKasinoCurrencyAsync()} is not enough to bet {wager} on krash.",
|
||||||
true, autoDeleteAfter: TimeSpan.FromSeconds(5));
|
true, autoDeleteAfter: TimeSpan.FromSeconds(5));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -61,8 +61,7 @@ public class RouletteCommand : ICommand
|
|||||||
var settings = await SettingsProvider.GetMultipleValuesAsync([
|
var settings = await SettingsProvider.GetMultipleValuesAsync([
|
||||||
BuiltIn.Keys.KasinoGameDisabledMessageCleanupDelay,
|
BuiltIn.Keys.KasinoGameDisabledMessageCleanupDelay,
|
||||||
BuiltIn.Keys.KasinoRouletteEnabled,
|
BuiltIn.Keys.KasinoRouletteEnabled,
|
||||||
BuiltIn.Keys.KasinoRouletteCountdownDuration,
|
BuiltIn.Keys.KasinoRouletteCountdownDuration
|
||||||
BuiltIn.Keys.BotRedisConnectionString
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// Check if roulette is enabled
|
// Check if roulette is enabled
|
||||||
@@ -77,15 +76,14 @@ public class RouletteCommand : ICommand
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(settings[BuiltIn.Keys.BotRedisConnectionString].Value))
|
if (!Redis.IsAvailable)
|
||||||
{
|
{
|
||||||
await botInstance.SendChatMessageAsync($"{user.FormatUsername()}, roulette is not available at this time", true,
|
await botInstance.SendChatMessageAsync($"{user.FormatUsername()}, roulette is not available at this time", true,
|
||||||
autoDeleteAfter: TimeSpan.FromSeconds(15));
|
autoDeleteAfter: TimeSpan.FromSeconds(15));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var redis = await ConnectionMultiplexer.ConnectAsync(settings[BuiltIn.Keys.BotRedisConnectionString].Value!);
|
_redisDb = Redis.Multiplexer.GetDatabase();
|
||||||
_redisDb = redis.GetDatabase();
|
|
||||||
|
|
||||||
var countdownDuration = TimeSpan.FromSeconds(
|
var countdownDuration = TimeSpan.FromSeconds(
|
||||||
settings[BuiltIn.Keys.KasinoRouletteCountdownDuration].ToType<int>());
|
settings[BuiltIn.Keys.KasinoRouletteCountdownDuration].ToType<int>());
|
||||||
|
|||||||
305
KfChatDotNetBot/Models/DbModels/KasinoShopDbModels.cs
Normal file
305
KfChatDotNetBot/Models/DbModels/KasinoShopDbModels.cs
Normal file
@@ -0,0 +1,305 @@
|
|||||||
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
|
||||||
|
namespace KfChatDotNetBot.Models.DbModels;
|
||||||
|
|
||||||
|
public class KasinoShopProfileDbModel
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// ID for the database row
|
||||||
|
/// </summary>
|
||||||
|
public int Id { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// Shop profiles belong to a user, not their gambler ID
|
||||||
|
/// they persist even if the user abandons their profile
|
||||||
|
/// </summary>
|
||||||
|
public required UserDbModel User { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// Assets held by this profile
|
||||||
|
/// </summary>
|
||||||
|
public required List<KasinoShopProfileAssetDbModel> Assets { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// Loans taken out by this profile
|
||||||
|
/// </summary>
|
||||||
|
[InverseProperty(nameof(KasinoShopProfileLoanDbModel.Borrower))]
|
||||||
|
public required List<KasinoShopProfileLoanDbModel> LoansTaken { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// Loans owed to this profile
|
||||||
|
/// </summary>
|
||||||
|
[InverseProperty(nameof(KasinoShopProfileLoanDbModel.Lender))]
|
||||||
|
public required List<KasinoShopProfileLoanDbModel> LoansOwed { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// State of the profile
|
||||||
|
/// </summary>
|
||||||
|
public required KasinoShopProfileStateFlags State { get; set; } = KasinoShopProfileStateFlags.None;
|
||||||
|
/// <summary>
|
||||||
|
/// JSON object containing data related to the above states
|
||||||
|
/// </summary>
|
||||||
|
public required KasinoShopProfileStateDataModel StateData { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// Profile balance in the "Krypto" currency
|
||||||
|
/// </summary>
|
||||||
|
public required decimal KryptoBalance { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Note this is serialized to JSON by Entity Framework so you can go wild shoving random bullshit in here
|
||||||
|
public class KasinoShopProfileStateDataModel
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Profile credit score for determining creditworthiness etc.
|
||||||
|
/// </summary>
|
||||||
|
// Actually considered making this uint but I like the idea of negative credit
|
||||||
|
public required int KreditScore { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// Amount this user has wagered towards their sponsor requirement
|
||||||
|
/// </summary>
|
||||||
|
public decimal? SponsorWagerAmount { get; set; } = null;
|
||||||
|
/// <summary>
|
||||||
|
/// The sponsor's wager requirement
|
||||||
|
/// </summary>
|
||||||
|
public decimal? SponsorWagerRequirement { get; set; } = null;
|
||||||
|
/// <summary>
|
||||||
|
/// Modifier that alters the house edge for your gambler entity
|
||||||
|
/// </summary>
|
||||||
|
public required decimal HouseEdgeModifier { get; set; } = 1;
|
||||||
|
/// <summary>
|
||||||
|
/// How much crack you've smoked?
|
||||||
|
/// </summary>
|
||||||
|
public required int CrackCounter { get; set; } = 0;
|
||||||
|
/// <summary>
|
||||||
|
/// How many floor nugs you got embedded in the carpet
|
||||||
|
/// </summary>
|
||||||
|
public required int FloorNugs { get; set; } = 0;
|
||||||
|
/// <summary>
|
||||||
|
/// Time when your weed buff ends
|
||||||
|
/// </summary>
|
||||||
|
public required DateTimeOffset WeedBuffEnds { get; set; } = DateTimeOffset.UtcNow;
|
||||||
|
/// <summary>
|
||||||
|
/// Time when your crack buff ends
|
||||||
|
/// </summary>
|
||||||
|
public required DateTimeOffset CrackBuffEnds { get; set; } = DateTimeOffset.UtcNow;
|
||||||
|
/// <summary>
|
||||||
|
/// Dodgy stat tracking
|
||||||
|
/// </summary>
|
||||||
|
public required KasinoShopStatTrackerModel StatTracker { get; set; } = new();
|
||||||
|
}
|
||||||
|
|
||||||
|
public class KasinoShopStatTrackerModel
|
||||||
|
{
|
||||||
|
public decimal TotalDeposited { get; set; } = 0;
|
||||||
|
public decimal TotalWithdrawn { get; set; } = 0;
|
||||||
|
public decimal TotalLossback { get; set; } = 0;
|
||||||
|
/// <summary>
|
||||||
|
/// Track wager statistics by game
|
||||||
|
/// </summary>
|
||||||
|
public Dictionary<WagerGame, decimal> StatTracker { get; set; } = new();
|
||||||
|
}
|
||||||
|
|
||||||
|
public class KasinoShopProfileLoanDbModel
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// ID for the database row
|
||||||
|
/// </summary>
|
||||||
|
public int Id { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// Profile of the user who owns this loan
|
||||||
|
/// </summary>
|
||||||
|
public required KasinoShopProfileDbModel Borrower { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// Profile of the user to whom this loan is owed/payable to
|
||||||
|
/// </summary>
|
||||||
|
public required KasinoShopProfileDbModel Lender { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// Amount loaned
|
||||||
|
/// </summary>
|
||||||
|
public required decimal Amount { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// Amount to be paid out to the loaner
|
||||||
|
/// </summary>
|
||||||
|
public required decimal PayoutAmount { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// Date and time loan entry was created
|
||||||
|
/// </summary>
|
||||||
|
public required DateTimeOffset Created { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// State of this loan
|
||||||
|
/// </summary>
|
||||||
|
public required LoanState State { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class KasinoShopProfileAssetDbModel
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// ID for the database row
|
||||||
|
/// </summary>
|
||||||
|
public int Id { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// Profile of the user who owns this asset
|
||||||
|
/// </summary>
|
||||||
|
public required KasinoShopProfileDbModel Profile { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// Value of the item at the time of acquisition in Krypto
|
||||||
|
/// </summary>
|
||||||
|
public required decimal OriginalValue { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// What the value of the item is right now
|
||||||
|
/// </summary>
|
||||||
|
public required decimal CurrentValue { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// Asset name
|
||||||
|
/// </summary>
|
||||||
|
public required string Name { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// Asset type
|
||||||
|
/// </summary>
|
||||||
|
public required AssetType AssetType { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// Date and time the asset was acquired
|
||||||
|
/// </summary>
|
||||||
|
public required DateTimeOffset Acquired { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// History of value changes (e.g. interest events)
|
||||||
|
/// </summary>
|
||||||
|
public required List<KasinoShopProfileAssetValueChangeDbModel> ValueChangeReports { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// Use this to store enum values for assets that have a subtype (e.g. Car Type)
|
||||||
|
/// but were otherwise not special enough to have their own table (e.g. Car)
|
||||||
|
/// </summary>
|
||||||
|
public int? AssetSubType { get; set; } = null;
|
||||||
|
/// <summary>
|
||||||
|
/// Serialized JSON for extra information where the schema can't accommodate for you
|
||||||
|
/// </summary>
|
||||||
|
public string? Extra { get; set; } = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public class KasinoShopProfileAssetInvestmentDbModel
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// ID for the database row
|
||||||
|
/// </summary>
|
||||||
|
public int Id { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// Related asset for this investment
|
||||||
|
/// </summary>
|
||||||
|
public required KasinoShopProfileAssetDbModel Asset { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// What type of investment it is
|
||||||
|
/// </summary>
|
||||||
|
public required InvestmentType InvestmentType { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// Last time interest was calculated
|
||||||
|
/// </summary>
|
||||||
|
public required DateTimeOffset LastInterestCalculation { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// Low point for interest calculations
|
||||||
|
/// </summary>
|
||||||
|
public required float InterestRangeMin { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// High point for interest calculations
|
||||||
|
/// </summary>
|
||||||
|
public required float InterestRangeMax { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// Use this to store enum values for investments that have a subtype (e.g. Shoe Brand)
|
||||||
|
/// but were otherwise not special enough to have their own table (e.g. Shoe)
|
||||||
|
/// </summary>
|
||||||
|
public int? InvestmentSubType { get; set; } = null;
|
||||||
|
/// <summary>
|
||||||
|
/// Serialized JSON for extra information where the schema can't accommodate for you
|
||||||
|
/// </summary>
|
||||||
|
public string? Extra { get; set; } = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public class KasinoShopProfileAssetValueChangeDbModel
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// ID for the database row
|
||||||
|
/// </summary>
|
||||||
|
public int Id { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// Related asset
|
||||||
|
/// </summary>
|
||||||
|
public required KasinoShopProfileAssetDbModel Asset { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// Effect of the change
|
||||||
|
/// </summary>
|
||||||
|
public required decimal ValueChangeEffect { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// Change percent as a decimal fraction?
|
||||||
|
/// </summary>
|
||||||
|
public required decimal ValueChangePercent { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// Descriptive text for the value change (like the source of it)
|
||||||
|
/// </summary>
|
||||||
|
public required string Description { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
[Flags]
|
||||||
|
public enum KasinoShopProfileStateFlags : ulong
|
||||||
|
{
|
||||||
|
None,
|
||||||
|
IsSponsored,
|
||||||
|
IsWeeded,
|
||||||
|
IsCracked,
|
||||||
|
IsInWithdrawal,
|
||||||
|
IsLoanable
|
||||||
|
}
|
||||||
|
|
||||||
|
[Flags]
|
||||||
|
public enum KasinoShopProfileAssetState : ulong
|
||||||
|
{
|
||||||
|
None,
|
||||||
|
/// <summary>
|
||||||
|
/// Only applicable to smashable objects (e.g. PC peripherals)
|
||||||
|
/// </summary>
|
||||||
|
IsSmashed
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum AssetType
|
||||||
|
{
|
||||||
|
Investment,
|
||||||
|
Smashable,
|
||||||
|
Car,
|
||||||
|
Random
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum InvestmentType
|
||||||
|
{
|
||||||
|
Shoes,
|
||||||
|
Stake,
|
||||||
|
Gold,
|
||||||
|
Silver,
|
||||||
|
Skin,
|
||||||
|
House,
|
||||||
|
Random
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum LoanState
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Loan not fully paid but borrower still in good standing
|
||||||
|
/// </summary>
|
||||||
|
Active,
|
||||||
|
/// <summary>
|
||||||
|
/// Past due but not yet a serious violation of terms
|
||||||
|
/// </summary>
|
||||||
|
Delinquent,
|
||||||
|
/// <summary>
|
||||||
|
/// Loan terms violated, time to collect
|
||||||
|
/// </summary>
|
||||||
|
Default,
|
||||||
|
/// <summary>
|
||||||
|
/// Loan settled by agreement to amended terms (e.g. paid off less than the full amount)
|
||||||
|
/// </summary>
|
||||||
|
Settled,
|
||||||
|
/// <summary>
|
||||||
|
/// Loan fully repaid for the total amount and closed out
|
||||||
|
/// </summary>
|
||||||
|
Repaid,
|
||||||
|
/// <summary>
|
||||||
|
/// Written off debt due to being unable to collect
|
||||||
|
/// </summary>
|
||||||
|
Uncollectible,
|
||||||
|
/// <summary>
|
||||||
|
/// Administrative state for loans canceled due to serious malfeasance
|
||||||
|
/// </summary>
|
||||||
|
Canceled
|
||||||
|
}
|
||||||
@@ -202,113 +202,6 @@ public class GamblerPerkDbModel
|
|||||||
public decimal? Payout { get; set; }
|
public decimal? Payout { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class KasinoShopProfileDbModel
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// ID for the database row
|
|
||||||
/// </summary>
|
|
||||||
public int Id { get; set; }
|
|
||||||
/// <summary>
|
|
||||||
/// Shop profiles belong to a user, not their gambler ID
|
|
||||||
/// they persist even if the user abandons their profile
|
|
||||||
/// </summary>
|
|
||||||
public required UserDbModel User { get; set; }
|
|
||||||
public required List<KasinoShopProfileAssetDbModel> Assets { get; set; }
|
|
||||||
public required List<KasinoShopProfileLoanDbModel> Loans { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class KasinoShopProfileLoanDbModel
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// ID for the database row
|
|
||||||
/// </summary>
|
|
||||||
public int Id { get; set; }
|
|
||||||
// Foreign key that the powers that be told me I need for the fancy navigation property
|
|
||||||
public int ProfileId { get; set; }
|
|
||||||
/// <summary>
|
|
||||||
/// Profile of the user who owns this loan
|
|
||||||
/// </summary>
|
|
||||||
public required KasinoShopProfileDbModel Profile { get; set; }
|
|
||||||
// Foreign key that the powers that be told me I need for the fancy navigation property
|
|
||||||
public int PayableToId { get; set; }
|
|
||||||
/// <summary>
|
|
||||||
/// Profile of the user to whom this loan is owed/payable to
|
|
||||||
/// </summary>
|
|
||||||
public required KasinoShopProfileDbModel PayableTo { get; set; }
|
|
||||||
/// <summary>
|
|
||||||
/// Amount loaned
|
|
||||||
/// </summary>
|
|
||||||
public required decimal Amount { get; set; }
|
|
||||||
/// <summary>
|
|
||||||
/// Amount to be paid out to the loaner
|
|
||||||
/// </summary>
|
|
||||||
public required decimal PayoutAmount { get; set; }
|
|
||||||
/// <summary>
|
|
||||||
/// Date and time loan entry was created
|
|
||||||
/// </summary>
|
|
||||||
public required DateTimeOffset Created { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class KasinoShopProfileAssetDbModel
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// ID for the database row
|
|
||||||
/// </summary>
|
|
||||||
public int Id { get; set; }
|
|
||||||
/// <summary>
|
|
||||||
/// Profile of the user who owns this asset
|
|
||||||
/// </summary>
|
|
||||||
public required KasinoShopProfileDbModel Profile { get; set; }
|
|
||||||
/// <summary>
|
|
||||||
/// Value of the item at the time of acquisition in Krypto
|
|
||||||
/// </summary>
|
|
||||||
public required decimal OriginalValue { get; set; }
|
|
||||||
/// <summary>
|
|
||||||
/// Asset name
|
|
||||||
/// </summary>
|
|
||||||
public required string Name { get; set; }
|
|
||||||
/// <summary>
|
|
||||||
/// Asset type
|
|
||||||
/// </summary>
|
|
||||||
public required AssetType AssetType { get; set; }
|
|
||||||
/// <summary>
|
|
||||||
/// Date and time the asset was acquired
|
|
||||||
/// </summary>
|
|
||||||
public required DateTimeOffset Acquired { get; set; }
|
|
||||||
/// <summary>
|
|
||||||
/// History of value changes (e.g. interest events)
|
|
||||||
/// </summary>
|
|
||||||
public required List<KasinoShopProfileAssetValueChangeDbModel> ValueChangeReports { get; set; }
|
|
||||||
/// <summary>
|
|
||||||
/// Serialized JSON for extra information useful for certain assets (e.g. car model)
|
|
||||||
/// </summary>
|
|
||||||
public string? Extra { get; set; } = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public class KasinoShopProfileAssetValueChangeDbModel
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// ID for the database row
|
|
||||||
/// </summary>
|
|
||||||
public int Id { get; set; }
|
|
||||||
/// <summary>
|
|
||||||
/// Related asset
|
|
||||||
/// </summary>
|
|
||||||
public required KasinoShopProfileAssetDbModel Asset { get; set; }
|
|
||||||
/// <summary>
|
|
||||||
/// Effect of the change
|
|
||||||
/// </summary>
|
|
||||||
public required decimal ValueChangeEffect { get; set; }
|
|
||||||
/// <summary>
|
|
||||||
/// Change percent as a decimal fraction?
|
|
||||||
/// </summary>
|
|
||||||
public required decimal ValueChangePercent { get; set; }
|
|
||||||
/// <summary>
|
|
||||||
/// Descriptive text for the value change (like the source of it)
|
|
||||||
/// </summary>
|
|
||||||
public required string Description { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum GamblerPerkType
|
public enum GamblerPerkType
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
9
KfChatDotNetBot/Models/KasinoShopModels.cs
Normal file
9
KfChatDotNetBot/Models/KasinoShopModels.cs
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
namespace KfChatDotNetBot.Models;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Holds state information for the kasino shop
|
||||||
|
/// </summary>
|
||||||
|
public class KasinoShopStateModel
|
||||||
|
{
|
||||||
|
public required decimal DefaultHouseEdgeModifier { get; set; } = 0;
|
||||||
|
}
|
||||||
@@ -20,6 +20,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
using KfChatDotNetBot.Services;
|
||||||
using KfChatDotNetBot.Settings;
|
using KfChatDotNetBot.Settings;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using NLog;
|
using NLog;
|
||||||
@@ -38,9 +39,19 @@ namespace KfChatDotNetBot
|
|||||||
await BuiltIn.SyncSettingsWithDb();
|
await BuiltIn.SyncSettingsWithDb();
|
||||||
logger.Info("Migrating settings from config.json (if needed)");
|
logger.Info("Migrating settings from config.json (if needed)");
|
||||||
await BuiltIn.MigrateJsonSettingsToDb();
|
await BuiltIn.MigrateJsonSettingsToDb();
|
||||||
|
logger.Info("Attempting to grab the Redis connection multiplexer so it's built");
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_ = Redis.Multiplexer;
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
logger.Error("Caught an error when attempting to grab the Redis multiplexer");
|
||||||
|
logger.Error(e);
|
||||||
|
}
|
||||||
logger.Info("Handing over to bot now");
|
logger.Info("Handing over to bot now");
|
||||||
Console.OutputEncoding = Encoding.UTF8;
|
Console.OutputEncoding = Encoding.UTF8;
|
||||||
new ChatBot();
|
_ = new ChatBot();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -390,12 +390,13 @@ public class BotServices
|
|||||||
_logger.Info("Built the almanac shill task");
|
_logger.Info("Built the almanac shill task");
|
||||||
}
|
}
|
||||||
|
|
||||||
private Task BuildDLiveStatusCheck()
|
private async Task BuildDLiveStatusCheck()
|
||||||
{
|
{
|
||||||
|
var enabled = (await SettingsProvider.GetValueAsync(BuiltIn.Keys.DLiveEnabled)).ToBoolean();
|
||||||
|
if (!enabled) return;
|
||||||
_dliveStatusCheck = new DLive(_chatBot);
|
_dliveStatusCheck = new DLive(_chatBot);
|
||||||
_dliveStatusCheck.StartLiveStatusCheck();
|
_dliveStatusCheck.StartLiveStatusCheck();
|
||||||
_logger.Info("Built the DLive livestream status check task");
|
_logger.Info("Built the DLive livestream status check task");
|
||||||
return Task.CompletedTask;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Task BuildPeerTubeLiveStatusCheck()
|
private Task BuildPeerTubeLiveStatusCheck()
|
||||||
|
|||||||
@@ -37,15 +37,14 @@ public class ConversationContextManager
|
|||||||
|
|
||||||
public ConversationContextManager()
|
public ConversationContextManager()
|
||||||
{
|
{
|
||||||
var connectionString = SettingsProvider.GetValueAsync(BuiltIn.Keys.BotRedisConnectionString).Result;
|
if (!Redis.IsAvailable)
|
||||||
if (string.IsNullOrEmpty(connectionString.Value))
|
|
||||||
{
|
{
|
||||||
Logger.Error($"Can't initialize the Nora ConversationContextManager service as Redis isn't configured in {BuiltIn.Keys.BotRedisConnectionString}");
|
Logger.Error($"Can't initialize the Nora ConversationContextManager service as Redis isn't configured in {BuiltIn.Keys.BotRedisConnectionString} " +
|
||||||
|
$"or the Redis client failed to connect");
|
||||||
throw new InvalidOperationException("Redis isn't configured");
|
throw new InvalidOperationException("Redis isn't configured");
|
||||||
}
|
}
|
||||||
|
|
||||||
var redis = ConnectionMultiplexer.Connect(connectionString.Value);
|
_redisDb = Redis.Multiplexer.GetDatabase();
|
||||||
_redisDb = redis.GetDatabase();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string GetContextKeyAsync(string mode, int userId, int roomId)
|
public static string GetContextKeyAsync(string mode, int userId, int roomId)
|
||||||
|
|||||||
@@ -23,15 +23,14 @@ public class KasinoKrash : IDisposable
|
|||||||
{
|
{
|
||||||
_kfChatBot = kfChatBot;
|
_kfChatBot = kfChatBot;
|
||||||
_ct = ct;
|
_ct = ct;
|
||||||
var connectionString = SettingsProvider.GetValueAsync(BuiltIn.Keys.BotRedisConnectionString).Result;
|
if (!Redis.IsAvailable)
|
||||||
if (string.IsNullOrEmpty(connectionString.Value))
|
|
||||||
{
|
{
|
||||||
_logger.Error($"Can't initialize the Kasino Krash service as Redis isn't configured in {BuiltIn.Keys.BotRedisConnectionString}");
|
_logger.Error($"Can't initialize the Kasino Krash service as Redis isn't configured in {BuiltIn.Keys.BotRedisConnectionString} " +
|
||||||
|
$"or the Redis service failed to connect");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var redis = ConnectionMultiplexer.Connect(connectionString.Value);
|
_redisDb = Redis.Multiplexer.GetDatabase();
|
||||||
_redisDb = redis.GetDatabase();
|
|
||||||
//attempt to pull a game from the db in case the bot crashed while a game was ongoing. if so it will restart the run
|
//attempt to pull a game from the db in case the bot crashed while a game was ongoing. if so it will restart the run
|
||||||
TheGame = GetKrashState().Result;
|
TheGame = GetKrashState().Result;
|
||||||
if (TheGame != null) _ = RunGame();
|
if (TheGame != null) _ = RunGame();
|
||||||
@@ -203,7 +202,6 @@ public class KasinoKrash : IDisposable
|
|||||||
await _kfChatBot.SendChatMessageAsync(
|
await _kfChatBot.SendChatMessageAsync(
|
||||||
$"{bet.Gambler.User.FormatUsername()}, due to your poor gambling skills, your bet was scaled down to {await bet.Wager.FormatKasinoCurrencyAsync()} to match your remaining balance.",
|
$"{bet.Gambler.User.FormatUsername()}, due to your poor gambling skills, your bet was scaled down to {await bet.Wager.FormatKasinoCurrencyAsync()} to match your remaining balance.",
|
||||||
true, autoDeleteAfter: TimeSpan.FromSeconds(10));
|
true, autoDeleteAfter: TimeSpan.FromSeconds(10));
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (bet.Multi <= TheGame.FinalMulti && bet.Multi != -1)
|
else if (bet.Multi <= TheGame.FinalMulti && bet.Multi != -1)
|
||||||
|
|||||||
@@ -241,15 +241,14 @@ public class KasinoMines
|
|||||||
public KasinoMines(ChatBot kfChatBot, int gamblerId)
|
public KasinoMines(ChatBot kfChatBot, int gamblerId)
|
||||||
{
|
{
|
||||||
_kfChatBot = kfChatBot;
|
_kfChatBot = kfChatBot;
|
||||||
var connectionString = SettingsProvider.GetValueAsync(BuiltIn.Keys.BotRedisConnectionString).Result;
|
if (!Redis.IsAvailable)
|
||||||
if (string.IsNullOrEmpty(connectionString.Value))
|
|
||||||
{
|
{
|
||||||
_logger.Error($"Can't initialize the Kasino Mines service as Redis isn't configured in {BuiltIn.Keys.BotRedisConnectionString}");
|
_logger.Error($"Can't initialize the Kasino Mines service as Redis isn't configured in {BuiltIn.Keys.BotRedisConnectionString} " +
|
||||||
|
$"or the Redis service failed to connect");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var redis = ConnectionMultiplexer.Connect(connectionString.Value);
|
_redisDb = Redis.Multiplexer.GetDatabase();
|
||||||
_redisDb = redis.GetDatabase();
|
|
||||||
GetSavedGames(gamblerId).Wait();
|
GetSavedGames(gamblerId).Wait();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,15 +22,14 @@ public class KasinoRain : IDisposable
|
|||||||
{
|
{
|
||||||
_kfChatBot = kfChatBot;
|
_kfChatBot = kfChatBot;
|
||||||
_ct = ct;
|
_ct = ct;
|
||||||
var connectionString = SettingsProvider.GetValueAsync(BuiltIn.Keys.BotRedisConnectionString).Result;
|
if (!Redis.IsAvailable)
|
||||||
if (string.IsNullOrEmpty(connectionString.Value))
|
|
||||||
{
|
{
|
||||||
_logger.Error($"Can't initialize the Kasino Rain service as Redis isn't configured in {BuiltIn.Keys.BotRedisConnectionString}");
|
_logger.Error($"Can't initialize the Kasino Rain service as Redis isn't configured in {BuiltIn.Keys.BotRedisConnectionString} " +
|
||||||
|
$"or the Redis service failed to connect");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var redis = ConnectionMultiplexer.Connect(connectionString.Value);
|
_redisDb = Redis.Multiplexer.GetDatabase();
|
||||||
_redisDb = redis.GetDatabase();
|
|
||||||
_rainTimerTask = Task.Run(RainTimerTask, ct);
|
_rainTimerTask = Task.Run(RainTimerTask, ct);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,18 +1,9 @@
|
|||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using KfChatDotNetBot.Extensions;
|
using KfChatDotNetBot.Extensions;
|
||||||
using KfChatDotNetBot.Models;
|
|
||||||
using KfChatDotNetBot.Models.DbModels;
|
using KfChatDotNetBot.Models.DbModels;
|
||||||
using KfChatDotNetBot.Settings;
|
using KfChatDotNetBot.Settings;
|
||||||
using NLog;
|
using NLog;
|
||||||
using StackExchange.Redis;
|
using StackExchange.Redis;
|
||||||
using System.Text.Json.Serialization;
|
|
||||||
using System.Text.RegularExpressions;
|
|
||||||
using KfChatDotNetBot.Extensions;
|
|
||||||
using KfChatDotNetBot.Models;
|
|
||||||
using KfChatDotNetBot.Models.DbModels;
|
|
||||||
using KfChatDotNetBot.Services;
|
|
||||||
using KfChatDotNetBot.Settings;
|
|
||||||
using KfChatDotNetWsClient.Models.Events;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using RandN;
|
using RandN;
|
||||||
using RandN.Compat;
|
using RandN.Compat;
|
||||||
|
|||||||
110
KfChatDotNetBot/Services/Redis.cs
Normal file
110
KfChatDotNetBot/Services/Redis.cs
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
using KfChatDotNetBot.Settings;
|
||||||
|
using System.Text.Json;
|
||||||
|
using NLog;
|
||||||
|
using StackExchange.Redis;
|
||||||
|
|
||||||
|
namespace KfChatDotNetBot.Services;
|
||||||
|
|
||||||
|
public static class Redis
|
||||||
|
{
|
||||||
|
public static bool IsAvailable => LazyMultiplexer.IsValueCreated;
|
||||||
|
// Claude told me this will act like a singleton ConnectionMultiplexer
|
||||||
|
// while keeping things nice and convenient with static methods
|
||||||
|
// FYI the exception will be thrown once, cached for the lifetime of the application
|
||||||
|
// If you configure a Redis connection string, you MUST restart the application
|
||||||
|
// https://learn.microsoft.com/en-us/dotnet/api/system.lazy-1?view=net-10.0#:~:text=Exception%20caching,-When
|
||||||
|
private static readonly Lazy<ConnectionMultiplexer> LazyMultiplexer =
|
||||||
|
new(() =>
|
||||||
|
{
|
||||||
|
var logger = LogManager.GetCurrentClassLogger();
|
||||||
|
var connectionString = SettingsProvider.GetValueAsync(BuiltIn.Keys.BotRedisConnectionString).Result.Value;
|
||||||
|
if (string.IsNullOrEmpty(connectionString))
|
||||||
|
{
|
||||||
|
logger.Error($"Could not initiate the lazy connection multiplexer for the Redis service as the " +
|
||||||
|
$"connection string is not configured in {BuiltIn.Keys.BotRedisConnectionString}. " +
|
||||||
|
$"Redis won't be available to anything that relies on it. " +
|
||||||
|
$"If you do configure {BuiltIn.Keys.BotRedisConnectionString}, YOU MUST RESTART THE BOT");
|
||||||
|
throw new InvalidOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return ConnectionMultiplexer.Connect(
|
||||||
|
SettingsProvider.GetValueAsync(BuiltIn.Keys.BotRedisConnectionString).Result.Value ??
|
||||||
|
throw new InvalidOperationException(
|
||||||
|
$"{BuiltIn.Keys.BotRedisConnectionString} not defined, cannot connect to Redis"));
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
logger.Error($"Caught an exception when connecting to Redis at {connectionString}");
|
||||||
|
logger.Error(e);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// You can just grab this from wherever if you want a ready to go Redis connection
|
||||||
|
// ReSharper disable once MemberCanBePrivate.Global
|
||||||
|
public static ConnectionMultiplexer Multiplexer => LazyMultiplexer.Value;
|
||||||
|
|
||||||
|
private static IDatabase Db => Multiplexer.GetDatabase();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Fetches a key from Redis asynchronously and deserializes its JSON value to T.
|
||||||
|
/// Returns default(T) if the key doesn't exist.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="key">Redis key</param>
|
||||||
|
public static async Task<T?> GetJsonAsync<T>(string key)
|
||||||
|
{
|
||||||
|
var value = await Db.StringGetAsync(key);
|
||||||
|
if (value.IsNullOrEmpty)
|
||||||
|
return default;
|
||||||
|
|
||||||
|
return JsonSerializer.Deserialize<T>(value.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Fetches a key from Redis synchronously and deserializes its JSON value to T.
|
||||||
|
/// Returns default(T) if the key doesn't exist.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="key">Redis key</param>
|
||||||
|
public static T? GetJson<T>(string key)
|
||||||
|
{
|
||||||
|
var value = Db.StringGet(key);
|
||||||
|
if (value.IsNullOrEmpty)
|
||||||
|
return default;
|
||||||
|
|
||||||
|
return JsonSerializer.Deserialize<T>(value.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Asynchronously set a key to a given object serialized using JSON
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="key">Redis key</param>
|
||||||
|
/// <param name="value">Object that you wish to serialize</param>
|
||||||
|
/// <param name="expires">Expiration (null means never expires)</param>
|
||||||
|
/// <param name="when">Redis behavior whether the key has a value or not
|
||||||
|
/// When.Always = set the value regardless of whether the key has a value
|
||||||
|
/// When.Exists = only set the value if the key has a value already
|
||||||
|
/// When.NotExists = only set the value if the key has no value
|
||||||
|
/// </param>
|
||||||
|
public static async Task SetJsonAsync(string key, object value, TimeSpan? expires = null, When when = When.Always)
|
||||||
|
{
|
||||||
|
await Db.StringSetAsync(key, JsonSerializer.Serialize(value), expires, when);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Synchronously set a key to a given object serialized using JSON
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="key">Redis key</param>
|
||||||
|
/// <param name="value">Object that you wish to serialize</param>
|
||||||
|
/// <param name="expires">Expiration (null means never expires)</param>
|
||||||
|
/// <param name="when">Redis behavior whether the key has a value or not
|
||||||
|
/// When.Always = set the value regardless of whether the key has a value
|
||||||
|
/// When.Exists = only set the value if the key has a value already
|
||||||
|
/// When.NotExists = only set the value if the key has no value
|
||||||
|
/// </param>
|
||||||
|
public static void SetJson(string key, object value, TimeSpan? expires = null, When when = When.Always)
|
||||||
|
{
|
||||||
|
Db.StringSet(key, JsonSerializer.Serialize(value), expires, when);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -586,6 +586,9 @@ public static class BuiltIn
|
|||||||
public static string WinnaBmjUsername = "Winna.BmjUsername";
|
public static string WinnaBmjUsername = "Winna.BmjUsername";
|
||||||
[BuiltInSetting("Array of cookies as a shitty hack to get Winna going", SettingValueType.Array, "[]")]
|
[BuiltInSetting("Array of cookies as a shitty hack to get Winna going", SettingValueType.Array, "[]")]
|
||||||
public static string WinnaCookies = "Winna.Cookies";
|
public static string WinnaCookies = "Winna.Cookies";
|
||||||
|
[BuiltInSetting("Whether the DLive livestream check is enabled", SettingValueType.Boolean, "false",
|
||||||
|
BooleanRegex)]
|
||||||
|
public static string DLiveEnabled = "DLive.Enabled";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user