From 377603ca357fe53d3db8395b501fb1e0e136c2d5 Mon Sep 17 00:00:00 2001 From: alogindtractor <251821224+A-Log-In-D-Tractor@users.noreply.github.com> Date: Sat, 14 Mar 2026 15:09:48 -0700 Subject: [PATCH] kasino shop updated all chat message id to uuid (#95) * Update KasinoMines.cs * Update SlotsCommand.cs * Update MinesCommand.cs * Update PlinkoCommand.cs * Update PlinkoCommand.cs * Update PlinkoCommand.cs * Update PlanesCommand.cs * Update LimboCommand.cs * Update KenoCommand.cs * Update KasinoUserCommands.cs * Update KasinoRain.cs * Create KasinoShop.cs * Create ShopCommands.cs * Update BotServices.cs * Update MoneyDbModels.cs --- .../Commands/Kasino/KasinoUserCommands.cs | 20 +- .../Commands/Kasino/KenoCommand.cs | 22 +- .../Commands/Kasino/LimboCommand.cs | 22 + .../Commands/Kasino/MinesCommand.cs | 8 +- .../Commands/Kasino/PlanesCommand.cs | 22 + .../Commands/Kasino/PlinkoCommand.cs | 17 +- .../Commands/Kasino/SlotsCommand.cs | 22 +- KfChatDotNetBot/Commands/ShopCommands.cs | 1341 +++++++++++ .../Models/DbModels/MoneyDbModels.cs | 8 +- KfChatDotNetBot/Services/BotServices.cs | 10 +- KfChatDotNetBot/Services/KasinoMines.cs | 17 +- KfChatDotNetBot/Services/KasinoRain.cs | 11 + KfChatDotNetBot/Services/KasinoShop.cs | 2120 +++++++++++++++++ 13 files changed, 3624 insertions(+), 16 deletions(-) create mode 100644 KfChatDotNetBot/Commands/ShopCommands.cs create mode 100644 KfChatDotNetBot/Services/KasinoShop.cs diff --git a/KfChatDotNetBot/Commands/Kasino/KasinoUserCommands.cs b/KfChatDotNetBot/Commands/Kasino/KasinoUserCommands.cs index 6b42a7c..a5d7aac 100644 --- a/KfChatDotNetBot/Commands/Kasino/KasinoUserCommands.cs +++ b/KfChatDotNetBot/Commands/Kasino/KasinoUserCommands.cs @@ -1,4 +1,4 @@ -ο»Ώusing System.Text.RegularExpressions; +using System.Text.RegularExpressions; using Humanizer; using KfChatDotNetBot.Extensions; using KfChatDotNetBot.Models; @@ -30,6 +30,14 @@ public class GetBalanceCommand : ICommand var gambler = await Money.GetGamblerEntityAsync(user.Id, ct: ctx); await botInstance.SendChatMessageAsync( $"{user.FormatUsername()}, your balance is {await gambler!.Balance.FormatKasinoCurrencyAsync()}", true); + + if (botInstance.BotServices.KasinoShop != null) + { + await GlobalShopFunctions.CheckProfile(botInstance, user, gambler); + await botInstance.SendChatMessageAsync( + $"{await botInstance.BotServices.KasinoShop.Gambler_Profiles[user.KfId].FormatBalanceAsync()}", true, + autoDeleteAfter: TimeSpan.FromSeconds(10)); + } } } @@ -117,6 +125,14 @@ public class SendJuiceCommand : ICommand await Money.ModifyBalanceAsync(targetGambler.Id, amount, TransactionSourceEventType.Juicer, $"Juice from {user.KfUsername}", gambler.Id, ctx); await botInstance.SendChatMessageAsync($"{user.FormatUsername()}, {await amount.FormatKasinoCurrencyAsync()} has been sent to {targetUser.FormatUsername()}", true); + //KasinoShop stuff -------------------------------------------------------------------------------- + if (botInstance.BotServices.KasinoShop != null) + { + await GlobalShopFunctions.CheckProfile(botInstance, gambler.User, gambler); + await GlobalShopFunctions.CheckProfile(botInstance, targetGambler.User, targetGambler); + await botInstance.BotServices.KasinoShop.ProcessJuicerOrRainTracking(gambler, targetGambler, amount); + } + //------------------------------------------------------------------------------------------------ } } @@ -460,4 +476,4 @@ public class GetDailyDollarCommand : ICommand await botInstance.SendChatMessageAsync($"{user.FormatUsername()}, you redeemed {await amount.FormatKasinoCurrencyAsync()}", true, autoDeleteAfter: TimeSpan.FromSeconds(15)); } -} \ No newline at end of file +} diff --git a/KfChatDotNetBot/Commands/Kasino/KenoCommand.cs b/KfChatDotNetBot/Commands/Kasino/KenoCommand.cs index baae428..b957262 100644 --- a/KfChatDotNetBot/Commands/Kasino/KenoCommand.cs +++ b/KfChatDotNetBot/Commands/Kasino/KenoCommand.cs @@ -111,7 +111,13 @@ public class KenoCommand : ICommand RateLimitService.RemoveMostRecentEntry(user, this); return; } - + //KasinoShop stuff ------------------------------------------------------------------------- + if (botInstance.BotServices.KasinoShop != null) + { + await GlobalShopFunctions.CheckProfile(botInstance, user, gambler); + HOUSE_EDGE += botInstance.BotServices.KasinoShop.Gambler_Profiles[user.KfId].HouseEdgeModifier; + } + //------------------------------------------------------------------------------------------ var payoutMultipliersHigh = new[,] //stole the payout multis from stake keno and re added the RTP, except for the 1000x { @@ -193,6 +199,13 @@ public class KenoCommand : ICommand $"{user.FormatUsername()}, you [color={colors[BuiltIn.Keys.KiwiFarmsRedColor].Value}]lost {await wager.FormatKasinoCurrencyAsync()}[/color]. Your balance is now: {await newBalance.FormatKasinoCurrencyAsync()}.", true, autoDeleteAfter: cleanupDelay); botInstance.ScheduleMessageAutoDelete(_kenoTable ?? throw new Exception("Cannot clean up _kenoTable as it's null"), cleanupDelay); + //Kasino Shop stuff---------------------------------------------------------------------- + if (botInstance.BotServices.KasinoShop != null) + { + await GlobalShopFunctions.CheckProfile(botInstance, user, gambler); + await botInstance.BotServices.KasinoShop.ProcessWagerTracking(gambler, WagerGame.Keno, wager, -wager, newBalance); + } + //--------------------------------------------------------------------------------------- return; } @@ -204,6 +217,13 @@ public class KenoCommand : ICommand $"{user.FormatUsername()}, you [color={colors[BuiltIn.Keys.KiwiFarmsGreenColor].Value}]won {await win.FormatKasinoCurrencyAsync()} with a {payoutMulti}x multi![/color]. Your balance is now: {await newBalance.FormatKasinoCurrencyAsync()}.", true, autoDeleteAfter: cleanupDelay); botInstance.ScheduleMessageAutoDelete(_kenoTable ?? throw new Exception("Cannot clean up _kenotable as it's null"), cleanupDelay); + //Kasino Shop stuff---------------------------------------------------------------------- + if (botInstance.BotServices.KasinoShop != null) + { + await GlobalShopFunctions.CheckProfile(botInstance, user, gambler); + await botInstance.BotServices.KasinoShop.ProcessWagerTracking(gambler, WagerGame.Keno, wager, win, newBalance); + } + //--------------------------------------------------------------------------------------- } private async Task AnimatedDisplayTable(List playerNumbers, List casinoNumbers, List matches, ChatBot botInstance) diff --git a/KfChatDotNetBot/Commands/Kasino/LimboCommand.cs b/KfChatDotNetBot/Commands/Kasino/LimboCommand.cs index c76e86a..639bf33 100644 --- a/KfChatDotNetBot/Commands/Kasino/LimboCommand.cs +++ b/KfChatDotNetBot/Commands/Kasino/LimboCommand.cs @@ -83,6 +83,14 @@ public class LimboCommand : ICommand RateLimitService.RemoveMostRecentEntry(user, this); return; } + + //KasinoShop stuff ------------------------------------------------------------------------- + if (botInstance.BotServices.KasinoShop != null) + { + await GlobalShopFunctions.CheckProfile(botInstance, user, gambler); + HOUSE_EDGE += botInstance.BotServices.KasinoShop.Gambler_Profiles[user.KfId].HouseEdgeModifier; + } + //------------------------------------------------------------------------------------------ if (!arguments.TryGetValue("number", out var number)) { @@ -110,6 +118,13 @@ public class LimboCommand : ICommand await botInstance.SendChatMessageAsync($"[b][color={colorToUse}] {casinoNumbers[1]:N2}[/color][/b][br]{user.FormatUsername()}, you " + $"[color={settings[BuiltIn.Keys.KiwiFarmsGreenColor].Value}] won {await win.FormatKasinoCurrencyAsync()}![/color] " + $"Your balance is now: {await newBalance.FormatKasinoCurrencyAsync()}!", true, autoDeleteAfter: cleanupDelay); + //Kasino Shop stuff---------------------------------------------------------------------- + if (botInstance.BotServices.KasinoShop != null) + { + await GlobalShopFunctions.CheckProfile(botInstance, user, gambler); + await botInstance.BotServices.KasinoShop.ProcessWagerTracking(gambler, WagerGame.Limbo, wager, win, newBalance); + } + //--------------------------------------------------------------------------------------- return; } @@ -123,6 +138,13 @@ public class LimboCommand : ICommand $"[b][color={colorToUse}] {casinoNumbers[1]:N2}[/color][/b][br]{user.FormatUsername()}, you [color={settings[BuiltIn.Keys.KiwiFarmsRedColor].Value}]" + $"lost {await wager.FormatKasinoCurrencyAsync()}[/color]. Your balance is now: {await newBalance.FormatKasinoCurrencyAsync()}.", true, autoDeleteAfter: cleanupDelay); + //Kasino Shop stuff---------------------------------------------------------------------- + if (botInstance.BotServices.KasinoShop != null) + { + await GlobalShopFunctions.CheckProfile(botInstance, user, gambler); + await botInstance.BotServices.KasinoShop.ProcessWagerTracking(gambler, WagerGame.Limbo, wager, -wager, newBalance); + } + //--------------------------------------------------------------------------------------- } diff --git a/KfChatDotNetBot/Commands/Kasino/MinesCommand.cs b/KfChatDotNetBot/Commands/Kasino/MinesCommand.cs index 813bd36..8d20f84 100644 --- a/KfChatDotNetBot/Commands/Kasino/MinesCommand.cs +++ b/KfChatDotNetBot/Commands/Kasino/MinesCommand.cs @@ -206,11 +206,11 @@ public class MinesCommand : ICommand return; } } - await KasinoMines.Bet(gambler.Id, precisePicks, msg, cashout); + await KasinoMines.Bet(gambler, precisePicks, msg, cashout); } else //if using picks { - await KasinoMines.Bet(gambler.Id, pick, msg, cashout); + await KasinoMines.Bet(gambler, pick, msg, cashout); } } else @@ -291,12 +291,12 @@ public class MinesCommand : ICommand return; } } - await KasinoMines.Bet(gambler.Id, precisePicks, msg, cashout); + await KasinoMines.Bet(gambler, precisePicks, msg, cashout); } else //if using picks { - await KasinoMines.Bet(gambler.Id, pick, msg, cashout); + await KasinoMines.Bet(gambler, pick, msg, cashout); } } diff --git a/KfChatDotNetBot/Commands/Kasino/PlanesCommand.cs b/KfChatDotNetBot/Commands/Kasino/PlanesCommand.cs index 1f53dbf..fd82f5a 100644 --- a/KfChatDotNetBot/Commands/Kasino/PlanesCommand.cs +++ b/KfChatDotNetBot/Commands/Kasino/PlanesCommand.cs @@ -92,6 +92,14 @@ public class Planes : ICommand RateLimitService.RemoveMostRecentEntry(user, this); return; } + + //KasinoShop stuff ------------------------------------------------------------------------- + if (botInstance.BotServices.KasinoShop != null) + { + await GlobalShopFunctions.CheckProfile(botInstance, user, gambler); + HOUSE_EDGE += botInstance.BotServices.KasinoShop.Gambler_Profiles[user.KfId].HouseEdgeModifier; + } + //------------------------------------------------------------------------------------------ if (HOUSE_EDGE < 1) { @@ -269,6 +277,13 @@ public class Planes : ICommand $"{user.FormatUsername()}, you [color={colors[BuiltIn.Keys.KiwiFarmsGreenColor].Value}]successfully landed with {await win.FormatKasinoCurrencyAsync()} from a total {plane.MultiTracker:N2}x multi![/color]. Your balance is now: {await newBalance.FormatKasinoCurrencyAsync()}", true, autoDeleteAfter: cleanupDelay); botInstance.ScheduleMessageAutoDelete(msgId, cleanupDelay); + //Kasino Shop stuff---------------------------------------------------------------------- + if (botInstance.BotServices.KasinoShop != null) + { + await GlobalShopFunctions.CheckProfile(botInstance, user, gambler); + await botInstance.BotServices.KasinoShop.ProcessWagerTracking(gambler, WagerGame.Planes, wager, win, newBalance); + } + //--------------------------------------------------------------------------------------- return; } plane.Crash(); @@ -280,6 +295,13 @@ public class Planes : ICommand $"{user.FormatUsername()}, you [color={colors[BuiltIn.Keys.KiwiFarmsRedColor].Value}]crashed![/color] Your balance is now: {await newBalance.FormatKasinoCurrencyAsync()}", true, autoDeleteAfter: cleanupDelay); botInstance.ScheduleMessageAutoDelete(msgId, cleanupDelay); + //Kasino Shop stuff---------------------------------------------------------------------- + if (botInstance.BotServices.KasinoShop != null) + { + await GlobalShopFunctions.CheckProfile(botInstance, user, gambler); + await botInstance.BotServices.KasinoShop.ProcessWagerTracking(gambler, WagerGame.Planes, wager, -wager, newBalance); + } + //--------------------------------------------------------------------------------------- } private string GetPreGameBoard(int fullCounter, int[,] planesBoard, Plane plane, int carrierCount, bool noseUp) diff --git a/KfChatDotNetBot/Commands/Kasino/PlinkoCommand.cs b/KfChatDotNetBot/Commands/Kasino/PlinkoCommand.cs index 2dc0aa3..bc20d99 100644 --- a/KfChatDotNetBot/Commands/Kasino/PlinkoCommand.cs +++ b/KfChatDotNetBot/Commands/Kasino/PlinkoCommand.cs @@ -132,6 +132,15 @@ public class PlinkoCommand : ICommand var gambler = await Money.GetGamblerEntityAsync(user.Id, ct: ctx); if (gambler == null) throw new InvalidOperationException($"Caught a null when retrieving gambler for {user.KfUsername}"); + + //KasinoShop stuff ------------------------------------------------------------------------- + if (botInstance.BotServices.KasinoShop != null) + { + await GlobalShopFunctions.CheckProfile(botInstance, user, gambler); + HOUSE_EDGE += botInstance.BotServices.KasinoShop.Gambler_Profiles[user.KfId].HouseEdgeModifier; + } + //------------------------------------------------------------------------------------------ + int numberOfBalls = 0; if (!arguments.TryGetValue("number", out var number)) { @@ -225,7 +234,13 @@ public class PlinkoCommand : ICommand } var newBalance = await Money.NewWagerAsync(gambler.Id, wager*numberOfBalls, payout-(wager*numberOfBalls), WagerGame.Plinko, ct: ctx); await botInstance.SendChatMessageAsync($"{user.FormatUsername()}, [u]you won {await payout.FormatKasinoCurrencyAsync()} from {numberOfBalls} plinko balls worth ${wager} KKK. Balance: {await newBalance.FormatKasinoCurrencyAsync()}", true, autoDeleteAfter: cleanupDelay); - + //Kasino Shop stuff---------------------------------------------------------------------- + if (botInstance.BotServices.KasinoShop != null) + { + await GlobalShopFunctions.CheckProfile(botInstance, user, gambler); + await botInstance.BotServices.KasinoShop.ProcessWagerTracking(gambler, WagerGame.Plinko, wager*numberOfBalls, payout-(wager*numberOfBalls), newBalance); + } + //--------------------------------------------------------------------------------------- } public string PlinkoBoardDisplay(List balls) diff --git a/KfChatDotNetBot/Commands/Kasino/SlotsCommand.cs b/KfChatDotNetBot/Commands/Kasino/SlotsCommand.cs index c041455..2a9edda 100644 --- a/KfChatDotNetBot/Commands/Kasino/SlotsCommand.cs +++ b/KfChatDotNetBot/Commands/Kasino/SlotsCommand.cs @@ -98,7 +98,13 @@ public class SlotsCommand : ICommand RateLimitService.RemoveMostRecentEntry(user, this); return; } - + //KasinoShop stuff ------------------------------------------------------------------------- + if (botInstance.BotServices.KasinoShop != null) + { + await GlobalShopFunctions.CheckProfile(botInstance, user, gambler); + HOUSE_EDGE += botInstance.BotServices.KasinoShop.Gambler_Profiles[user.KfId].HouseEdgeModifier; + } + //------------------------------------------------------------------------------------------ char rigged = '0'; decimal rigCheck = (decimal)Money.GetRandomDouble(gambler); if (HOUSE_EDGE > 1) @@ -151,6 +157,13 @@ public class SlotsCommand : ICommand await botInstance.SendChatMessageAsync( $"{user.FormatUsername()} you [color={colors[BuiltIn.Keys.KiwiFarmsRedColor].Value}]lost[/color] {await totalWager.FormatKasinoCurrencyAsync()} with {spins} spins. Current balance: {await newBalance.FormatKasinoCurrencyAsync()}", true, autoDeleteAfter: TimeSpan.FromSeconds(30)); + //Kasino Shop stuff---------------------------------------------------------------------- + if (botInstance.BotServices.KasinoShop != null) + { + await GlobalShopFunctions.CheckProfile(botInstance, user, gambler); + await botInstance.BotServices.KasinoShop.ProcessWagerTracking(gambler, WagerGame.Slots, wager*spins, -wager*spins, newBalance); + } + //--------------------------------------------------------------------------------------- return; } @@ -161,6 +174,13 @@ public class SlotsCommand : ICommand string winstr = netwin ? "" : "-"; newBalance = await Money.NewWagerAsync(gambler.Id, wager*spins, winnings, WagerGame.Slots, ct: ctx); winnings = Math.Abs(winnings); + //Kasino Shop stuff---------------------------------------------------------------------- + if (botInstance.BotServices.KasinoShop != null) + { + await GlobalShopFunctions.CheckProfile(botInstance, user, gambler); + await botInstance.BotServices.KasinoShop.ProcessWagerTracking(gambler, WagerGame.Slots, wager*spins, winnings, newBalance); + } + //--------------------------------------------------------------------------------------- await Task.Delay(TimeSpan.FromSeconds(spins * 2)); await botInstance.SendChatMessageAsync( $"{user.FormatUsername()}, you [color={colors[BuiltIn.Keys.KiwiFarmsGreenColor].Value}]won[/color] {await rawWinnings.FormatKasinoCurrencyAsync()} from {spins} spins worth {await wager.FormatKasinoCurrencyAsync()}! Net: {winstr}{await winnings.FormatKasinoCurrencyAsync()} Current balance: {await newBalance.FormatKasinoCurrencyAsync()}", true, autoDeleteAfter: TimeSpan.FromSeconds(30)); diff --git a/KfChatDotNetBot/Commands/ShopCommands.cs b/KfChatDotNetBot/Commands/ShopCommands.cs new file mode 100644 index 0000000..24c21ce --- /dev/null +++ b/KfChatDotNetBot/Commands/ShopCommands.cs @@ -0,0 +1,1341 @@ +using System.Text.RegularExpressions; +using KfChatDotNetBot.Extensions; +using KfChatDotNetBot.Models; +using KfChatDotNetBot.Models.DbModels; +using KfChatDotNetBot.Services; +using KfChatDotNetBot.Settings; +using KfChatDotNetWsClient.Models.Events; + +namespace KfChatDotNetBot.Commands.Kasino; + +public class ShopGlobalResetCommand : ICommand +{ + public List Patterns => + [ + new Regex(@"^admin shop global reset", RegexOptions.IgnoreCase) + ]; + public string? HelpText => "!beg to beg for a loan"; + public UserRight RequiredRight => UserRight.TrueAndHonest; + public TimeSpan Timeout => TimeSpan.FromSeconds(30); + public RateLimitOptionsModel? RateLimitOptions => new RateLimitOptionsModel + { + MaxInvocations = 1, + Window = TimeSpan.FromSeconds(120) + }; + + + public async Task RunCommand(ChatBot botInstance, MessageModel message, UserDbModel user, GroupCollection arguments, + CancellationToken ctx) + { + var cleanupDelay = TimeSpan.FromSeconds(10); + if (botInstance.BotServices.KasinoShop == null) + { + await botInstance.SendChatMessageAsync("KasinoShop is not currently running.", true, autoDeleteAfter: cleanupDelay); + return; + } + + await botInstance.BotServices.KasinoShop.ResetProfiles(); + } +} +public class ShopGlobalLoanResetCommand : ICommand +{ + public List Patterns => + [ + new Regex(@"^admin shop global loan reset", RegexOptions.IgnoreCase) + ]; + public string? HelpText => "!beg to beg for a loan"; + public UserRight RequiredRight => UserRight.TrueAndHonest; + public TimeSpan Timeout => TimeSpan.FromSeconds(30); + public RateLimitOptionsModel? RateLimitOptions => new RateLimitOptionsModel + { + MaxInvocations = 1, + Window = TimeSpan.FromSeconds(120) + }; + + + public async Task RunCommand(ChatBot botInstance, MessageModel message, UserDbModel user, GroupCollection arguments, + CancellationToken ctx) + { + var cleanupDelay = TimeSpan.FromSeconds(10); + if (botInstance.BotServices.KasinoShop == null) + { + await botInstance.SendChatMessageAsync("KasinoShop is not currently running.", true, autoDeleteAfter: cleanupDelay); + return; + } + + await botInstance.BotServices.KasinoShop.ResetAllLoans(); + } +} + +public class ShopHelpCommand : ICommand +{ + public List Patterns => + [ + new Regex(@"^shop help$", RegexOptions.IgnoreCase), + new Regex(@"^help shop$", RegexOptions.IgnoreCase), + new Regex(@"^shop$", RegexOptions.IgnoreCase) + ]; + public string? HelpText => "!beg to beg for a loan"; + public UserRight RequiredRight => UserRight.Loser; + public TimeSpan Timeout => TimeSpan.FromSeconds(30); + public RateLimitOptionsModel? RateLimitOptions => new RateLimitOptionsModel + { + MaxInvocations = 1, + Window = TimeSpan.FromSeconds(120) + }; + + private const string KasinoShopHelpLink = "https://i.ddos.lgbt/raw/pgTFLJ.html"; + public async Task RunCommand(ChatBot botInstance, MessageModel message, UserDbModel user, GroupCollection arguments, CancellationToken ctx) + { + var cleanupDelay = TimeSpan.FromSeconds(10); + var gambler = await Money.GetGamblerEntityAsync(user.Id, ct: ctx); + if (gambler == null) + { + throw new InvalidOperationException($"Caught a null when retrieving gambler for {user.KfUsername}"); + } + if (botInstance.BotServices.KasinoShop == null) + { + await botInstance.SendChatMessageAsync("KasinoShop is not currently running.", true, autoDeleteAfter: cleanupDelay); + return; + } + await GlobalShopFunctions.CheckProfile(botInstance, user, gambler); + + await botInstance.SendChatMessageAsync( + $"{user.FormatUsername()} - Kasino Shop Help Tool: {KasinoShopHelpLink}", true, autoDeleteAfter: TimeSpan.FromSeconds(15)); + //just gonna link to an html hosted on iddos + } +} + +public class ShopListCommand : ICommand +{ + public List Patterns => + [ + new Regex(@"^my (?loans|assets|investments|rtp)$", RegexOptions.IgnoreCase), + new Regex(@"^list (?loans|assets|investments|rtp)$", RegexOptions.IgnoreCase), + new Regex(@"^shop list (?loans|assets|investments|rtp)$", RegexOptions.IgnoreCase) + ]; + public string? HelpText => "!beg to beg for a loan"; + public UserRight RequiredRight => UserRight.Loser; + public TimeSpan Timeout => TimeSpan.FromSeconds(30); + public RateLimitOptionsModel? RateLimitOptions => new RateLimitOptionsModel + { + MaxInvocations = 1, + Window = TimeSpan.FromSeconds(120) + }; + + + public async Task RunCommand(ChatBot botInstance, MessageModel message, UserDbModel user, GroupCollection arguments, CancellationToken ctx) + { + var cleanupDelay = TimeSpan.FromSeconds(10); + var gambler = await Money.GetGamblerEntityAsync(user.Id, ct: ctx); + if (gambler == null) + { + throw new InvalidOperationException($"Caught a null when retrieving gambler for {user.KfUsername}"); + } + if (botInstance.BotServices.KasinoShop == null) + { + await botInstance.SendChatMessageAsync("KasinoShop is not currently running.", true, autoDeleteAfter: cleanupDelay); + return; + } + await GlobalShopFunctions.CheckProfile(botInstance, user, gambler); + + if (!arguments.TryGetValue("choice", out var choice)) + { + await botInstance.SendChatMessageAsync($"{user.FormatUsername()}, not enough arguments. !list ", true, autoDeleteAfter: cleanupDelay); + return; + } + + switch (choice.Value) + { + case "loans": + await botInstance.BotServices.KasinoShop.PrintLoansList(gambler); + break; + case "assets": + await botInstance.BotServices.KasinoShop.PrintAssets(gambler); + break; + case "investments": + await botInstance.BotServices.KasinoShop.PrintInvestments(gambler); + break; + case "rtp": + await botInstance.BotServices.KasinoShop.PrintRtp(gambler); + break; + default: await botInstance.SendChatMessageAsync($"{user.FormatUsername()}, invalid choice. !list ", true, autoDeleteAfter: cleanupDelay); return; + } + } +} + +public class ShopSellCommand : ICommand +{ + public List Patterns => + [ + new Regex(@"^sell (?\d+)$", RegexOptions.IgnoreCase), + new Regex(@"^shop sell (?\d+)$", RegexOptions.IgnoreCase), + ]; + public string? HelpText => "!beg to beg for a loan"; + public UserRight RequiredRight => UserRight.Loser; + public TimeSpan Timeout => TimeSpan.FromSeconds(30); + public RateLimitOptionsModel? RateLimitOptions => new RateLimitOptionsModel + { + MaxInvocations = 1, + Window = TimeSpan.FromSeconds(120) + }; + + + public async Task RunCommand(ChatBot botInstance, MessageModel message, UserDbModel user, GroupCollection arguments, + CancellationToken ctx) + { + var cleanupDelay = TimeSpan.FromSeconds(10); + var gambler = await Money.GetGamblerEntityAsync(user.Id, ct: ctx); + if (gambler == null) + { + throw new InvalidOperationException($"Caught a null when retrieving gambler for {user.KfUsername}"); + } + if (botInstance.BotServices.KasinoShop == null) + { + await botInstance.SendChatMessageAsync("KasinoShop is not currently running.", true, autoDeleteAfter: cleanupDelay); + return; + } + await GlobalShopFunctions.CheckProfile(botInstance, user, gambler); + if (!arguments.TryGetValue("id", out var id)) + { + await botInstance.SendChatMessageAsync($"{user.FormatUsername()}, need the item ID of the item you want to sell. !sell ", true, autoDeleteAfter: cleanupDelay); + return; + } + int item = Convert.ToInt32(id.Value); + await botInstance.BotServices.KasinoShop.ProcessAssetSale(gambler, item); + } +} +public class BegCommand : ICommand +{ + public List Patterns => + [ + new Regex(@"^beg$", RegexOptions.IgnoreCase) + ]; + public string? HelpText => "!beg to beg for a loan"; + public UserRight RequiredRight => UserRight.Loser; + public TimeSpan Timeout => TimeSpan.FromSeconds(30); + public RateLimitOptionsModel? RateLimitOptions => new RateLimitOptionsModel + { + MaxInvocations = 1, + Window = TimeSpan.FromSeconds(120) + }; + public async Task RunCommand(ChatBot botInstance, MessageModel message, UserDbModel user, GroupCollection arguments, CancellationToken ctx) + { + var cleanupDelay = TimeSpan.FromSeconds(10); + var gambler = await Money.GetGamblerEntityAsync(user.Id, ct: ctx); + if (gambler == null) + { + throw new InvalidOperationException($"Caught a null when retrieving gambler for {user.KfUsername}"); + } + if (botInstance.BotServices.KasinoShop == null) + { + await botInstance.SendChatMessageAsync("KasinoShop is not currently running.", true, autoDeleteAfter: cleanupDelay); + return; + } + await GlobalShopFunctions.CheckProfile(botInstance, user, gambler); + await botInstance.BotServices.KasinoShop.ProcessBeg(gambler); + } +} + +public class LoanCommand : ICommand +{ + public List Patterns => + [ + new Regex(@"^loan (?\d+) (?\d+(?:\.\d+)?)$", RegexOptions.IgnoreCase), + new Regex(@"^loan$", RegexOptions.IgnoreCase) + ]; + public string? HelpText => "!beg to beg for a loan"; + public UserRight RequiredRight => UserRight.Loser; + public TimeSpan Timeout => TimeSpan.FromSeconds(30); + public RateLimitOptionsModel? RateLimitOptions => new RateLimitOptionsModel + { + MaxInvocations = 2, + Window = TimeSpan.FromSeconds(60) + }; + public async Task RunCommand(ChatBot botInstance, MessageModel message, UserDbModel user, GroupCollection arguments, CancellationToken ctx) + { + var cleanupDelay = TimeSpan.FromSeconds(10); + if (botInstance.BotServices.KasinoShop == null) + { + await botInstance.SendChatMessageAsync("KasinoShop is not currently running.", true, autoDeleteAfter: cleanupDelay); + return; + } + var gambler = await Money.GetGamblerEntityAsync(user.Id, ct: ctx); + if (gambler == null) + { + throw new InvalidOperationException($"Caught a null when retrieving gambler for {user.KfUsername}"); + } + await GlobalShopFunctions.CheckProfile(botInstance, user, gambler); + if (!arguments.TryGetValue("reciever", out var reciever) || !arguments.TryGetValue("amount", out var amount)) //list users active loans + { + await botInstance.BotServices.KasinoShop.PrintLoansList(gambler); + //await botInstance.SendChatMessageAsync($"{user.FormatUsername()}, not enough arguments. !loan ", true, autoDeleteAfter: cleanupDelay); + return; + } + int id = Convert.ToInt32(reciever.Value); + if (!botInstance.BotServices.KasinoShop.Gambler_Profiles.ContainsKey(id)) + { + await botInstance.SendChatMessageAsync($"{user.FormatUsername()}, could not find a kasino shop profile for {id}.", true, autoDeleteAfter: cleanupDelay); + return; + } + + await botInstance.BotServices.KasinoShop.ProcessLoan(id, Convert.ToDecimal(amount.Value), gambler, gambler.Id); + } +} + +public class RepaymentCommand : ICommand +{ + public List Patterns => + [ + new Regex(@"^repay (?\d+) (?\d+(?:\.\d+)?)$", RegexOptions.IgnoreCase), + new Regex(@"^repay$", RegexOptions.IgnoreCase) + ]; + public string? HelpText => "!beg to beg for a loan"; + public UserRight RequiredRight => UserRight.Loser; + public TimeSpan Timeout => TimeSpan.FromSeconds(30); + public RateLimitOptionsModel? RateLimitOptions => new RateLimitOptionsModel + { + MaxInvocations = 2, + Window = TimeSpan.FromSeconds(60) + }; + + public async Task RunCommand(ChatBot botInstance, MessageModel message, UserDbModel user, GroupCollection arguments, + CancellationToken ctx) + { + var cleanupDelay = TimeSpan.FromSeconds(10); + if (botInstance.BotServices.KasinoShop == null) + { + await botInstance.SendChatMessageAsync("KasinoShop is not currently running.", true, autoDeleteAfter: cleanupDelay); + return; + } + var gambler = await Money.GetGamblerEntityAsync(user.Id, ct: ctx); + if (gambler == null) + { + throw new InvalidOperationException($"Caught a null when retrieving gambler for {user.KfUsername}"); + } + await GlobalShopFunctions.CheckProfile(botInstance, user, gambler); + if (!arguments.TryGetValue("reciever", out var reciever) || !arguments.TryGetValue("amount", out var amount)) + { + await botInstance.SendChatMessageAsync($"{user.FormatUsername()}, not enough arguments. !repay ", true, autoDeleteAfter: cleanupDelay); + return; + } + int id = Convert.ToInt32(reciever.Value); + if (!botInstance.BotServices.KasinoShop.Gambler_Profiles.ContainsKey(id)) + { + await botInstance.SendChatMessageAsync($"{user.FormatUsername()}, could not find a kasino shop profile for {id}.", true, autoDeleteAfter: cleanupDelay); + return; + } + decimal amountToPay = Convert.ToDecimal(amount.Value); + + await botInstance.BotServices.KasinoShop.ProcessRepayment(gambler, user, id, amountToPay); + } +} + + +public class ShopSmashableCommand : ICommand +{ + public List Patterns => + [ + new Regex(@"^shop buy assets (?\d+)$", RegexOptions.IgnoreCase), + new Regex(@"^shop buy smashable (?\d+)$", RegexOptions.IgnoreCase), + new Regex(@"^shop assets (?\d+)$", RegexOptions.IgnoreCase), + new Regex(@"^shop smashable (?\d+)$", RegexOptions.IgnoreCase), + new Regex(@"^shop smashable$", RegexOptions.IgnoreCase), + new Regex(@"^shop assets$", RegexOptions.IgnoreCase) + ]; + public string? HelpText => "!shop to get a list of shop commands"; + public UserRight RequiredRight => UserRight.Loser; + public TimeSpan Timeout => TimeSpan.FromSeconds(30); + public RateLimitOptionsModel? RateLimitOptions => new RateLimitOptionsModel + { + MaxInvocations = 2, + Window = TimeSpan.FromSeconds(60) + }; + + public async Task RunCommand(ChatBot botInstance, MessageModel message, UserDbModel user, GroupCollection arguments, + CancellationToken ctx) + { + var cleanupDelay = TimeSpan.FromSeconds(10); + if (botInstance.BotServices.KasinoShop == null) + { + await botInstance.SendChatMessageAsync("KasinoShop is not currently running.", true, autoDeleteAfter: cleanupDelay); + return; + } + var gambler = await Money.GetGamblerEntityAsync(user.Id, ct: ctx); + if (gambler == null) + { + throw new InvalidOperationException($"Caught a null when retrieving gambler for {user.KfUsername}"); + } + await GlobalShopFunctions.CheckProfile(botInstance, user, gambler); + + if (!arguments.TryGetValue("num", out var num)) + { + await botInstance.BotServices.KasinoShop.PrintSmashableShop(gambler); + return; + } + int smashable = Convert.ToInt32(num.Value); + if (smashable < 1 || smashable > 3) + { + await botInstance.SendChatMessageAsync($"{user.FormatUsername()}, invalid smashable pick, must pick 1, 2, or 3.", true, autoDeleteAfter: cleanupDelay); + return; + } + await botInstance.BotServices.KasinoShop.ProcessSmashablePurchase(gambler, smashable); + } +} + +public class ShopInvestmentsCommand : ICommand +{ + public List Patterns => + [ + new Regex(@"^buy investments (?\d+) (?\d+(?:\.\d+)?)$", RegexOptions.IgnoreCase), + new Regex(@"^buy investments (?\d+)$", RegexOptions.IgnoreCase), + new Regex(@"^buy investments$", RegexOptions.IgnoreCase), + new Regex(@"^shop investments (?\d+) (?\d+(?:\.\d+)?)$", RegexOptions.IgnoreCase), + new Regex(@"^shop investments (?\d+)$", RegexOptions.IgnoreCase), + new Regex(@"^shop investments$", RegexOptions.IgnoreCase) + ]; + public string? HelpText => "!shop investments to look at the investments for sale. 1 2 or 3 and an optional amount at the end to buy the investment (default you will buy 1)"; + public UserRight RequiredRight => UserRight.Loser; + public TimeSpan Timeout => TimeSpan.FromSeconds(30); + public RateLimitOptionsModel? RateLimitOptions => new RateLimitOptionsModel + { + MaxInvocations = 2, + Window = TimeSpan.FromSeconds(60) + }; + + public async Task RunCommand(ChatBot botInstance, MessageModel message, UserDbModel user, GroupCollection arguments, + CancellationToken ctx) + { + var cleanupDelay = TimeSpan.FromSeconds(10); + if (botInstance.BotServices.KasinoShop == null) + { + await botInstance.SendChatMessageAsync("KasinoShop is not currently running.", true, autoDeleteAfter: cleanupDelay); + return; + } + var gambler = await Money.GetGamblerEntityAsync(user.Id, ct: ctx); + if (gambler == null) + { + throw new InvalidOperationException($"Caught a null when retrieving gambler for {user.KfUsername}"); + } + await GlobalShopFunctions.CheckProfile(botInstance, user, gambler); + + if (!arguments.TryGetValue("num", out var num)) + { + await botInstance.SendChatMessageAsync($"1: Gold - ${KasinoShop.GoldBasePriceOz}KKK/oz[br]" + + $"2: Silver - ${KasinoShop.SilverBasePriceOz}KKK/oz[br]" + + $"3: House - ${KasinoShop.BaseHousePrice} KKK", true, autoDeleteAfter: TimeSpan.FromSeconds(10)); + return; + } + int item = Convert.ToInt32(num.Value); + decimal investment; + if (!arguments.TryGetValue("amount", out var amount)) + { + switch (item) + { + case 1: investment = 300000; break; + case 2: investment = 10000; break; + case 3: investment = 100000000; break; + default: await botInstance.SendChatMessageAsync($"{user.FormatUsername()}, invalid pick, must pick 1, 2, or 3.", true, autoDeleteAfter: cleanupDelay); return; + } + } + else investment = Convert.ToDecimal(amount.Value); + + await botInstance.BotServices.KasinoShop.ProcessInvestment(gambler, item, investment); + } +} + +public class ShopUpdateGambler : ICommand +{ + public List Patterns => [new Regex(@"^shop update gambler$", RegexOptions.IgnoreCase)]; + public string? HelpText => "update profile to new gambler id if you abandon your gambler account"; + public UserRight RequiredRight => UserRight.Loser; + public TimeSpan Timeout => TimeSpan.FromSeconds(30); + public RateLimitOptionsModel? RateLimitOptions => new RateLimitOptionsModel + { + MaxInvocations = 2, + Window = TimeSpan.FromSeconds(60) + }; + public async Task RunCommand(ChatBot botInstance, MessageModel message, UserDbModel user, GroupCollection arguments, CancellationToken ctx) + { + var cleanupDelay = TimeSpan.FromSeconds(10); + if (botInstance.BotServices.KasinoShop == null) + { + await botInstance.SendChatMessageAsync("KasinoShop is not currently running.", true, autoDeleteAfter: cleanupDelay); + return; + } + var gambler = await Money.GetGamblerEntityAsync(user.Id, ct: ctx); + if (gambler == null) + { + throw new InvalidOperationException($"Caught a null when retrieving gambler for {user.KfUsername}"); + } + await GlobalShopFunctions.CheckProfile(botInstance, user, gambler); + await botInstance.BotServices.KasinoShop.UpdateGambler(gambler); + } +} + +public class ShopShoeCommand : ICommand +{ + public List Patterns => + [ + new Regex(@"^shop shoes (?\d+)$", RegexOptions.IgnoreCase), + new Regex(@"^buy shoes (?\d+)$", RegexOptions.IgnoreCase), + new Regex(@"^shop buy shoes (?\d+)$", RegexOptions.IgnoreCase), + new Regex(@"^shop shoes (?\d+)$", RegexOptions.IgnoreCase), + new Regex(@"^buy shoes (?\d+)$", RegexOptions.IgnoreCase), + new Regex(@"^shop buy shoes (?\d+)$", RegexOptions.IgnoreCase), + new Regex(@"^shop shoes$", RegexOptions.IgnoreCase), + new Regex(@"^buy shoes$", RegexOptions.IgnoreCase), + new Regex(@"^shop buy shoes$", RegexOptions.IgnoreCase), + ]; + public string? HelpText => "!shop investments to look at the investments for sale. 1 2 or 3 and an optional amount at the end to buy the investment (default you will buy 1)"; + public UserRight RequiredRight => UserRight.Loser; + public TimeSpan Timeout => TimeSpan.FromSeconds(30); + public RateLimitOptionsModel? RateLimitOptions => new RateLimitOptionsModel + { + MaxInvocations = 2, + Window = TimeSpan.FromSeconds(60) + }; + + public async Task RunCommand(ChatBot botInstance, MessageModel message, UserDbModel user, GroupCollection arguments, + CancellationToken ctx) + { + var cleanupDelay = TimeSpan.FromSeconds(10); + if (botInstance.BotServices.KasinoShop == null) + { + await botInstance.SendChatMessageAsync("KasinoShop is not currently running.", true, autoDeleteAfter: cleanupDelay); + return; + } + var gambler = await Money.GetGamblerEntityAsync(user.Id, ct: ctx); + if (gambler == null) + { + throw new InvalidOperationException($"Caught a null when retrieving gambler for {user.KfUsername}"); + } + await GlobalShopFunctions.CheckProfile(botInstance, user, gambler); + + if (!arguments.TryGetValue("num", out var num)) + { + await botInstance.BotServices.KasinoShop.PrintShoeMarket(gambler); + return; + } + + int shoe = Convert.ToInt32(num.Value); + if (shoe < 1 || shoe > 3) + { + await botInstance.SendChatMessageAsync($"{user.FormatUsername()}, invalid shoe pick, must pick 1, 2, or 3.", true, autoDeleteAfter: cleanupDelay); + return; + } + await botInstance.BotServices.KasinoShop.ProcessShoePurchase(gambler, shoe); + } +} + +public class ShopSkinCommand : ICommand +{ + public List Patterns => + [ + new Regex(@"^shop skin (?\d+)$", RegexOptions.IgnoreCase), + new Regex(@"^buy skin (?\d+)$", RegexOptions.IgnoreCase), + new Regex(@"^shop buy skin (?\d+)$", RegexOptions.IgnoreCase), + new Regex(@"^shop skin$", RegexOptions.IgnoreCase), + new Regex(@"^buy skin$", RegexOptions.IgnoreCase), + new Regex(@"^shop buy skin$", RegexOptions.IgnoreCase), + ]; + public string? HelpText => "!shop investments to look at the investments for sale. 1 2 or 3 and an optional amount at the end to buy the investment (default you will buy 1)"; + public UserRight RequiredRight => UserRight.Loser; + public TimeSpan Timeout => TimeSpan.FromSeconds(30); + public RateLimitOptionsModel? RateLimitOptions => new RateLimitOptionsModel + { + MaxInvocations = 2, + Window = TimeSpan.FromSeconds(60) + }; + + public async Task RunCommand(ChatBot botInstance, MessageModel message, UserDbModel user, GroupCollection arguments, + CancellationToken ctx) + { + var cleanupDelay = TimeSpan.FromSeconds(10); + if (botInstance.BotServices.KasinoShop == null) + { + await botInstance.SendChatMessageAsync("KasinoShop is not currently running.", true, autoDeleteAfter: cleanupDelay); + return; + } + var gambler = await Money.GetGamblerEntityAsync(user.Id, ct: ctx); + if (gambler == null) + { + throw new InvalidOperationException($"Caught a null when retrieving gambler for {user.KfUsername}"); + } + await GlobalShopFunctions.CheckProfile(botInstance, user, gambler); + + if (!arguments.TryGetValue("num", out var num)) + { + await botInstance.BotServices.KasinoShop.PrintSkinMarket(gambler); + return; + } + + int skin = Convert.ToInt32(num.Value); + + await botInstance.BotServices.KasinoShop.ProcessSkinPurchase(gambler, skin); + + } +} +public class ShopStakeCommand : ICommand +{ + public List Patterns => + [ + new Regex(@"^stake$", RegexOptions.IgnoreCase), + new Regex(@"^shop stake$", RegexOptions.IgnoreCase), + new Regex(@"^stake (?\d+(?:\.\d+)?)$", RegexOptions.IgnoreCase), + new Regex(@"^shop stake (?\d+(?:\.\d+)?)$", RegexOptions.IgnoreCase), + ]; + public string? HelpText => "!shop investments to look at the investments for sale. 1 2 or 3 and an optional amount at the end to buy the investment (default you will buy 1)"; + public UserRight RequiredRight => UserRight.Loser; + public TimeSpan Timeout => TimeSpan.FromSeconds(30); + public RateLimitOptionsModel? RateLimitOptions => new RateLimitOptionsModel + { + MaxInvocations = 2, + Window = TimeSpan.FromSeconds(60) + }; + + public async Task RunCommand(ChatBot botInstance, MessageModel message, UserDbModel user, GroupCollection arguments, + CancellationToken ctx) + { + var cleanupDelay = TimeSpan.FromSeconds(10); + if (botInstance.BotServices.KasinoShop == null) + { + await botInstance.SendChatMessageAsync("KasinoShop is not currently running.", true, autoDeleteAfter: cleanupDelay); + return; + } + var gambler = await Money.GetGamblerEntityAsync(user.Id, ct: ctx); + if (gambler == null) + { + throw new InvalidOperationException($"Caught a null when retrieving gambler for {user.KfUsername}"); + } + await GlobalShopFunctions.CheckProfile(botInstance, user, gambler); + + if (!arguments.TryGetValue("amount", out var amount)) + { + await botInstance.SendChatMessageAsync($"{user.FormatUsername()}, stake your crypto to earn a small amount of interest every day. Minimum stake time: 1 week. !stake ", true, autoDeleteAfter: cleanupDelay); + return; + } + + await botInstance.BotServices.KasinoShop.ProcessStake(gambler, Convert.ToDecimal(amount.Value)); + } +} + + +public class ShopUnstakeCommand : ICommand +{ + public List Patterns => + [ + new Regex(@"^unstake$", RegexOptions.IgnoreCase), + new Regex(@"^shop unstake$", RegexOptions.IgnoreCase), + new Regex(@"^unstake (?all)$", RegexOptions.IgnoreCase), + new Regex(@"^shop unstake (?all)$", RegexOptions.IgnoreCase), + new Regex(@"^unstake (?\d+(?:\.\d+)?)$", RegexOptions.IgnoreCase), + new Regex(@"^shop unstake (?\d+(?:\.\d+)?)$", RegexOptions.IgnoreCase), + new Regex(@"^unstake (?\d+)$", RegexOptions.IgnoreCase), + new Regex(@"^shop unstake (?\d+)$", RegexOptions.IgnoreCase), + new Regex(@"^unstake (?\d+) (?\d+(?:\.\d+)?)$", RegexOptions.IgnoreCase), + new Regex(@"^shop unstake (?\d+) (?\d+(?:\.\d+)?)$", RegexOptions.IgnoreCase), + ]; + public string? HelpText => "!shop investments to look at the investments for sale. 1 2 or 3 and an optional amount at the end to buy the investment (default you will buy 1)"; + public UserRight RequiredRight => UserRight.Loser; + public TimeSpan Timeout => TimeSpan.FromSeconds(30); + public RateLimitOptionsModel? RateLimitOptions => new RateLimitOptionsModel + { + MaxInvocations = 2, + Window = TimeSpan.FromSeconds(60) + }; + + public async Task RunCommand(ChatBot botInstance, MessageModel message, UserDbModel user, GroupCollection arguments, + CancellationToken ctx) + { + var cleanupDelay = TimeSpan.FromSeconds(10); + if (botInstance.BotServices.KasinoShop == null) + { + await botInstance.SendChatMessageAsync("KasinoShop is not currently running.", true, autoDeleteAfter: cleanupDelay); + return; + } + var gambler = await Money.GetGamblerEntityAsync(user.Id, ct: ctx); + if (gambler == null) + { + throw new InvalidOperationException($"Caught a null when retrieving gambler for {user.KfUsername}"); + } + await GlobalShopFunctions.CheckProfile(botInstance, user, gambler); + + bool containsId = arguments.TryGetValue("id", out var id); + bool containsAmount = arguments.TryGetValue("amount", out var amount); + bool containsAll = arguments.TryGetValue("all", out var all); + decimal amountUnstake = -1; + int unstakeId = -1; + if (containsId) unstakeId = Convert.ToInt32(id!.Value); + if (containsAmount) amountUnstake = Convert.ToDecimal(amount!.Value); + if (!containsId && !containsAmount && !containsAll) + { + await botInstance.SendChatMessageAsync( + $"{user.FormatUsername()}, you must specify an id or amount to unstake, or all to unstake everything.", true, + autoDeleteAfter: cleanupDelay); + return; + } + + await botInstance.BotServices.KasinoShop.UnStake(gambler, amountUnstake, unstakeId, containsAll); + } +} + +public class SmashCommand : ICommand +{ + public List Patterns => + [ + new Regex(@"^smash", RegexOptions.IgnoreCase), + ]; + + public string? HelpText => "destroy your , show your loaners you're really gonna do it dewd!!!"; + public UserRight RequiredRight => UserRight.Loser; + public TimeSpan Timeout => TimeSpan.FromSeconds(30); + public RateLimitOptionsModel? RateLimitOptions => new RateLimitOptionsModel + { + MaxInvocations = 2, + Window = TimeSpan.FromSeconds(60) + }; + + public async Task RunCommand(ChatBot botInstance, MessageModel message, UserDbModel user, GroupCollection arguments, + CancellationToken ctx) + { + var cleanupDelay = TimeSpan.FromSeconds(10); + if (botInstance.BotServices.KasinoShop == null) + { + await botInstance.SendChatMessageAsync("KasinoShop is not currently running.", true, autoDeleteAfter: cleanupDelay); + return; + } + var gambler = await Money.GetGamblerEntityAsync(user.Id, ct: ctx); + if (gambler == null) + { + throw new InvalidOperationException($"Caught a null when retrieving gambler for {user.KfUsername}"); + } + await GlobalShopFunctions.CheckProfile(botInstance, user, gambler); + + await botInstance.BotServices.KasinoShop.ProcessSmash(gambler); + } +} + +public class SponsorShipCommand : ICommand +{ + public List Patterns => [new Regex(@"^shop sponsorship$", RegexOptions.IgnoreCase)]; + public string? HelpText => "buy drugs"; + public UserRight RequiredRight => UserRight.Loser; + public TimeSpan Timeout => TimeSpan.FromSeconds(30); + public RateLimitOptionsModel? RateLimitOptions => new RateLimitOptionsModel + { + MaxInvocations = 2, + Window = TimeSpan.FromSeconds(60) + }; + + public async Task RunCommand(ChatBot botInstance, MessageModel message, UserDbModel user, GroupCollection arguments, + CancellationToken ctx) + { + var cleanupDelay = TimeSpan.FromSeconds(10); + if (botInstance.BotServices.KasinoShop == null) + { + await botInstance.SendChatMessageAsync("KasinoShop is not currently running.", true, autoDeleteAfter: cleanupDelay); + return; + } + var gambler = await Money.GetGamblerEntityAsync(user.Id, ct: ctx); + if (gambler == null) + { + throw new InvalidOperationException($"Caught a null when retrieving gambler for {user.KfUsername}"); + } + await GlobalShopFunctions.CheckProfile(botInstance, user, gambler); + + await botInstance.BotServices.KasinoShop.ProcessSponsorship(gambler); + } +} + +public class SponsorShipBonusCommand : ICommand +{ + public List Patterns => [new Regex(@"^sponsor bonus$", RegexOptions.IgnoreCase)]; + public string? HelpText => "buy drugs"; + public UserRight RequiredRight => UserRight.Loser; + public TimeSpan Timeout => TimeSpan.FromSeconds(30); + public RateLimitOptionsModel? RateLimitOptions => new RateLimitOptionsModel + { + MaxInvocations = 2, + Window = TimeSpan.FromSeconds(60) + }; + + public async Task RunCommand(ChatBot botInstance, MessageModel message, UserDbModel user, GroupCollection arguments, + CancellationToken ctx) + { + var cleanupDelay = TimeSpan.FromSeconds(10); + if (botInstance.BotServices.KasinoShop == null) + { + await botInstance.SendChatMessageAsync("KasinoShop is not currently running.", true, autoDeleteAfter: cleanupDelay); + return; + } + var gambler = await Money.GetGamblerEntityAsync(user.Id, ct: ctx); + if (gambler == null) + { + throw new InvalidOperationException($"Caught a null when retrieving gambler for {user.KfUsername}"); + } + await GlobalShopFunctions.CheckProfile(botInstance, user, gambler); + + await botInstance.BotServices.KasinoShop.ProcessSponsorBonus(gambler); + } +} + +public class EndSponsorShipCommand : ICommand +{ + public List Patterns => [new Regex(@"^shop end sponsorship$", RegexOptions.IgnoreCase)]; + public string? HelpText => "buy drugs"; + public UserRight RequiredRight => UserRight.Loser; + public TimeSpan Timeout => TimeSpan.FromSeconds(30); + public RateLimitOptionsModel? RateLimitOptions => new RateLimitOptionsModel + { + MaxInvocations = 2, + Window = TimeSpan.FromSeconds(60) + }; + + public async Task RunCommand(ChatBot botInstance, MessageModel message, UserDbModel user, GroupCollection arguments, + CancellationToken ctx) + { + var cleanupDelay = TimeSpan.FromSeconds(10); + if (botInstance.BotServices.KasinoShop == null) + { + await botInstance.SendChatMessageAsync("KasinoShop is not currently running.", true, autoDeleteAfter: cleanupDelay); + return; + } + var gambler = await Money.GetGamblerEntityAsync(user.Id, ct: ctx); + if (gambler == null) + { + throw new InvalidOperationException($"Caught a null when retrieving gambler for {user.KfUsername}"); + } + await GlobalShopFunctions.CheckProfile(botInstance, user, gambler); + + await botInstance.BotServices.KasinoShop.ProcessSponsorshipEnd(gambler); + } +} +public class ShopDrugsCommand : ICommand +{ + public List Patterns => + [ + new Regex(@"^smoke (?crack|weed|nugs|floor nugs) (?\d+(?:\.\d+)?)$"), + new Regex(@"^shop drugs (?\d+) (?\d+(?:\.\d+)?)$", RegexOptions.IgnoreCase), + new Regex(@"^buy drugs (?\d+) (?\d+(?:\.\d+)?)$", RegexOptions.IgnoreCase), + new Regex(@"^smoke (?crack|weed|nugs|floor nugs) (?\d+(?:\.\d+)?)$"), + new Regex(@"^shop drugs (?\d+)$", RegexOptions.IgnoreCase), + new Regex(@"^buy drugs (?\d+)$", RegexOptions.IgnoreCase), + new Regex(@"^shop drugs$", RegexOptions.IgnoreCase), + new Regex(@"^call derrick$", RegexOptions.IgnoreCase), + new Regex(@"^buy drugs$", RegexOptions.IgnoreCase) + ]; + public string? HelpText => "buy drugs"; + public UserRight RequiredRight => UserRight.Loser; + public TimeSpan Timeout => TimeSpan.FromSeconds(30); + public RateLimitOptionsModel? RateLimitOptions => new RateLimitOptionsModel + { + MaxInvocations = 2, + Window = TimeSpan.FromSeconds(60) + }; + + public async Task RunCommand(ChatBot botInstance, MessageModel message, UserDbModel user, GroupCollection arguments, + CancellationToken ctx) + { + var cleanupDelay = TimeSpan.FromSeconds(10); + if (botInstance.BotServices.KasinoShop == null) + { + await botInstance.SendChatMessageAsync("KasinoShop is not currently running.", true, autoDeleteAfter: cleanupDelay); + return; + } + var gambler = await Money.GetGamblerEntityAsync(user.Id, ct: ctx); + if (gambler == null) + { + throw new InvalidOperationException($"Caught a null when retrieving gambler for {user.KfUsername}"); + } + await GlobalShopFunctions.CheckProfile(botInstance, user, gambler); + int drug; + decimal amount; + if (arguments.TryGetValue("num", out var num)) + { + drug = Convert.ToInt32(num.Value); + if (drug == 1) + { + amount = botInstance.BotServices.KasinoShop.GetCurrentCrackPrice(gambler); + await botInstance.BotServices.KasinoShop.ProcessDrugUse(gambler, amount, drug); + } + else + { + if (!arguments.TryGetValue("amount", out var amountt)) + { + await botInstance.SendChatMessageAsync( + $"{user.FormatUsername()}, you must specify how much drug you want to buy. !buy drugs weed 1000 !shop drugs weed 1000 !smoke nugs 10", + true, autoDeleteAfter: cleanupDelay); + return; + } + amount = Convert.ToDecimal(amountt.Value); + await botInstance.BotServices.KasinoShop.ProcessDrugUse(gambler, amount, drug); + + } + } + else if (arguments.TryGetValue("choice", out var choice)) + { + string ch = choice.Value.ToLower(); + if (!arguments.TryGetValue("amount", out var amountArg)) + { + //if they didn't put an amount they must be trying to smoke crack + if (ch != "crack") + { + await botInstance.SendChatMessageAsync( + $"{user.FormatUsername()}, you must specify how much drug you want to buy. !buy drugs weed 1000 !shop drugs weed 1000 !smoke nugs 10", + true, autoDeleteAfter: cleanupDelay); + return; + } + + amount = botInstance.BotServices.KasinoShop.GetCurrentCrackPrice(gambler); + drug = 1; + } + else if (ch == "crack") + { + amount = botInstance.BotServices.KasinoShop.GetCurrentCrackPrice(gambler); + drug = 1; + } + else if (ch == "weed") + { + amount = Convert.ToDecimal(amountArg.Value); + drug = 2; + } + else //nugs or floor nugs + { + amount = Convert.ToInt32(amountArg.Value); + drug = 3; + } + + if (amount < 1) + { + await botInstance.SendChatMessageAsync($"{user.FormatUsername()}, you have to spend money to buy drugs.", true, autoDeleteAfter: cleanupDelay); + return; + } + await botInstance.BotServices.KasinoShop.ProcessDrugUse(gambler, amount, drug); + } + else + { + await botInstance.BotServices.KasinoShop.PrintDrugMarket(gambler); + } + } +} + +public class ShopCarCommand : ICommand +{ + public List Patterns => + [ + new Regex(@"^buy car (?civic|type r|type-r|bentley|BMW|Audi)$", RegexOptions.IgnoreCase), + new Regex(@"^shop car (?civic|type r|type-r|bentley|BMW|Audi)$", RegexOptions.IgnoreCase), + new Regex(@"^shop car (?\d+)$", RegexOptions.IgnoreCase), + new Regex(@"^buy car (?\d+)$", RegexOptions.IgnoreCase), + new Regex(@"^shop car$", RegexOptions.IgnoreCase), + new Regex(@"^buy car$", RegexOptions.IgnoreCase) + ]; + public string? HelpText => "!shop to get a list of shop commands"; + public UserRight RequiredRight => UserRight.Loser; + public TimeSpan Timeout => TimeSpan.FromSeconds(30); + public RateLimitOptionsModel? RateLimitOptions => new RateLimitOptionsModel + { + MaxInvocations = 2, + Window = TimeSpan.FromSeconds(60) + }; + + public async Task RunCommand(ChatBot botInstance, MessageModel message, UserDbModel user, GroupCollection arguments, + CancellationToken ctx) + { //civic audi bentley bmw + var cleanupDelay = TimeSpan.FromSeconds(10); + if (botInstance.BotServices.KasinoShop == null) + { + await botInstance.SendChatMessageAsync("KasinoShop is not currently running.", true, autoDeleteAfter: cleanupDelay); + return; + } + var gambler = await Money.GetGamblerEntityAsync(user.Id, ct: ctx); + if (gambler == null) + { + throw new InvalidOperationException($"Caught a null when retrieving gambler for {user.KfUsername}"); + } + await GlobalShopFunctions.CheckProfile(botInstance, user, gambler); + int car; + if (arguments.TryGetValue("num", out var num)) + { + car = Convert.ToInt32(num.Value); + if (car < 1 || car > 4) + { + await botInstance.SendChatMessageAsync($"{user.FormatUsername()}, invalid car choice, must pick 1, 2, 3, or 4 (civic, audi, bentley, bmw)", true, autoDeleteAfter: cleanupDelay); + return; + } + await botInstance.BotServices.KasinoShop.ProcessCarPurchase(gambler, car); + } + else if (arguments.TryGetValue("choice", out var choice)) + { + string carChoice = choice.Value.ToLower(); + if (carChoice == "bmw") car = 4; + else if (carChoice == "audi") car = 3; + else if (carChoice == "bentley") car = 2; + else if (carChoice == "civic" || carChoice.Contains("r")) car = 1; + else + { + await botInstance.SendChatMessageAsync($"{user.FormatUsername()}, invalid car choice, must pick 1, 2, 3, or 4 (civic, audi, bentley, bmw)", true, autoDeleteAfter: cleanupDelay); + return; + } + await botInstance.BotServices.KasinoShop.ProcessCarPurchase(gambler, car); + } + else + { + string str = ""; + for (int i = 1; i <= KasinoShop.DefaultCars.Count; i++) + { + str += $"{i}: {KasinoShop.DefaultCars.ElementAt(i - 1).Value}[br]"; + } + await botInstance.SendChatMessageAsync(str, true, autoDeleteAfter: cleanupDelay); + } + } +} + + +public class ShopDepositCommand : ICommand +{ + public List Patterns => + [ + new Regex(@"^shop deposit (?\d+(?:\.\d+)?)$", RegexOptions.IgnoreCase), + new Regex(@"^shop depo (?\d+(?:\.\d+)?)$", RegexOptions.IgnoreCase), + new Regex(@"^deposit (?\d+(?:\.\d+)?)$", RegexOptions.IgnoreCase), + new Regex(@"^depo (?\d+(?:\.\d+)?)$", RegexOptions.IgnoreCase), + new Regex(@"^shop deposit$", RegexOptions.IgnoreCase), + new Regex(@"^shop depo$", RegexOptions.IgnoreCase), + new Regex(@"^deposit$", RegexOptions.IgnoreCase), + new Regex(@"^depo$", RegexOptions.IgnoreCase) + ]; + public string? HelpText => "!shop to get a list of shop commands"; + public UserRight RequiredRight => UserRight.Loser; + public TimeSpan Timeout => TimeSpan.FromSeconds(30); + public RateLimitOptionsModel? RateLimitOptions => new RateLimitOptionsModel + { + MaxInvocations = 2, + Window = TimeSpan.FromSeconds(60) + }; + + public async Task RunCommand(ChatBot botInstance, MessageModel message, UserDbModel user, GroupCollection arguments, + CancellationToken ctx) + { + var cleanupDelay = TimeSpan.FromSeconds(10); + if (botInstance.BotServices.KasinoShop == null) + { + await botInstance.SendChatMessageAsync("KasinoShop is not currently running.", true, autoDeleteAfter: cleanupDelay); + return; + } + var gambler = await Money.GetGamblerEntityAsync(user.Id, ct: ctx); + if (gambler == null) + { + throw new InvalidOperationException($"Caught a null when retrieving gambler for {user.KfUsername}"); + } + await GlobalShopFunctions.CheckProfile(botInstance, user, gambler); + if (!arguments.TryGetValue("amount", out var amount)) + { + await botInstance.BotServices.KasinoShop.PrintBalance(gambler); + } + decimal depo = Convert.ToDecimal(amount!.Value); + await botInstance.BotServices.KasinoShop.ProcessDeposit(gambler, depo); + } +} + +public class ShopCarJobCommand : ICommand +{ + public List Patterns => + [ + new Regex(@"^work job$", RegexOptions.IgnoreCase), + new Regex(@"^shop job$", RegexOptions.IgnoreCase), + new Regex(@"^shop work$", RegexOptions.IgnoreCase), + new Regex(@"^work$", RegexOptions.IgnoreCase), + new Regex(@"^job$", RegexOptions.IgnoreCase) + ]; + public string? HelpText => "work a job if you have a car"; + public UserRight RequiredRight => UserRight.Loser; + public TimeSpan Timeout => TimeSpan.FromSeconds(30); + public RateLimitOptionsModel? RateLimitOptions => new RateLimitOptionsModel + { + MaxInvocations = 2, + Window = TimeSpan.FromSeconds(60) + }; + + public async Task RunCommand(ChatBot botInstance, MessageModel message, UserDbModel user, GroupCollection arguments, + CancellationToken ctx) + { + var cleanupDelay = TimeSpan.FromSeconds(10); + if (botInstance.BotServices.KasinoShop == null) + { + await botInstance.SendChatMessageAsync("KasinoShop is not currently running.", true, autoDeleteAfter: cleanupDelay); + return; + } + var gambler = await Money.GetGamblerEntityAsync(user.Id, ct: ctx); + if (gambler == null) + { + throw new InvalidOperationException($"Caught a null when retrieving gambler for {user.KfUsername}"); + } + await GlobalShopFunctions.CheckProfile(botInstance, user, gambler); + + await botInstance.BotServices.KasinoShop.ProcessWorkJob(gambler); + } +} + +public class ShopWithdrawCommand : ICommand +{ + public List Patterns => + [ + new Regex(@"^withdraw (?\d+(?:\.\d+)?)$", RegexOptions.IgnoreCase), + new Regex(@"^withdraw$", RegexOptions.IgnoreCase) + ]; + public string? HelpText => "withdraw from kasino to shop"; + public UserRight RequiredRight => UserRight.Loser; + public TimeSpan Timeout => TimeSpan.FromSeconds(30); + public RateLimitOptionsModel? RateLimitOptions => new RateLimitOptionsModel + { + MaxInvocations = 2, + Window = TimeSpan.FromSeconds(60) + }; + + public async Task RunCommand(ChatBot botInstance, MessageModel message, UserDbModel user, GroupCollection arguments, + CancellationToken ctx) + { + var cleanupDelay = TimeSpan.FromSeconds(10); + if (botInstance.BotServices.KasinoShop == null) + { + await botInstance.SendChatMessageAsync("KasinoShop is not currently running.", true, autoDeleteAfter: cleanupDelay); + return; + } + var gambler = await Money.GetGamblerEntityAsync(user.Id, ct: ctx); + if (gambler == null) + { + throw new InvalidOperationException($"Caught a null when retrieving gambler for {user.KfUsername}"); + } + await GlobalShopFunctions.CheckProfile(botInstance, user, gambler); + if (!arguments.TryGetValue("amount", out var amount)) + { + string wagerLock = ""; + if (!botInstance.BotServices.KasinoShop.CheckWagerReq(gambler)) + { + wagerLock += + $", and you need to finish your wager requirement before you can withdraw. {await botInstance.BotServices.KasinoShop.RemainingWagerReq(gambler).FormatKasinoCurrencyAsync()} remaining to be wagered before you can withdraw."; + } + await botInstance.SendChatMessageAsync( + $"{user.FormatUsername()}, you can withdraw from your kasino balance of {await gambler.Balance.FormatKasinoCurrencyAsync()}. You must withdraw a minimum of {await 5000m.FormatKasinoCurrencyAsync()}{wagerLock}.", + true, autoDeleteAfter: cleanupDelay); + return; + } + decimal withdraw = Convert.ToDecimal(amount!.Value); + if (withdraw < 5000) + { + await botInstance.SendChatMessageAsync($"{user.FormatUsername()}, you must withdraw a minimum of {await 5000m.FormatKasinoCurrencyAsync()}.", true, autoDeleteAfter: cleanupDelay); + } + await botInstance.BotServices.KasinoShop.ProcessWithdraw(gambler, withdraw); + } +} + +/// +/// RIGGING +/// +//-------------------------------------------------- + +public class FlipSwitchCommand : ICommand +{ + private Rigging type = Rigging.Switch; + + public List Patterns => + [ + new Regex(@"^flip switch") + ]; + public string? HelpText => "flips a switch"; + public UserRight RequiredRight => UserRight.TrueAndHonest; + public TimeSpan Timeout => TimeSpan.FromSeconds(30); + public RateLimitOptionsModel? RateLimitOptions => new RateLimitOptionsModel + { + MaxInvocations = 2, + Window = TimeSpan.FromSeconds(60) + }; + + public async Task RunCommand(ChatBot botInstance, MessageModel message, UserDbModel user, GroupCollection arguments, + CancellationToken ctx) + { + var cleanupDelay = TimeSpan.FromSeconds(10); + if (botInstance.BotServices.KasinoShop == null) + { + await botInstance.SendChatMessageAsync("KasinoShop is not currently running.", true, autoDeleteAfter: cleanupDelay); + return; + } + + await botInstance.BotServices.KasinoShop!.ProcessRigging(type); + } +} + +public class PushButtonCommand : ICommand +{ + private Rigging type = Rigging.Button; + public List Patterns => + [ + new Regex(@"^push button") + ]; + public string? HelpText => "flips a switch"; + public UserRight RequiredRight => UserRight.TrueAndHonest; + public TimeSpan Timeout => TimeSpan.FromSeconds(30); + public RateLimitOptionsModel? RateLimitOptions => new RateLimitOptionsModel + { + MaxInvocations = 2, + Window = TimeSpan.FromSeconds(60) + }; + + public async Task RunCommand(ChatBot botInstance, MessageModel message, UserDbModel user, GroupCollection arguments, + CancellationToken ctx) + { + var cleanupDelay = TimeSpan.FromSeconds(10); + if (botInstance.BotServices.KasinoShop == null) + { + await botInstance.SendChatMessageAsync("KasinoShop is not currently running.", true, autoDeleteAfter: cleanupDelay); + return; + } + + await botInstance.BotServices.KasinoShop!.ProcessRigging(type); + } +} + +public class PullLeverCommand : ICommand +{ + private Rigging type = Rigging.Lever; + public List Patterns => + [ + new Regex(@"^pull lever") + ]; + public string? HelpText => "flips a switch"; + public UserRight RequiredRight => UserRight.TrueAndHonest; + public TimeSpan Timeout => TimeSpan.FromSeconds(30); + public RateLimitOptionsModel? RateLimitOptions => new RateLimitOptionsModel + { + MaxInvocations = 2, + Window = TimeSpan.FromSeconds(60) + }; + + public async Task RunCommand(ChatBot botInstance, MessageModel message, UserDbModel user, GroupCollection arguments, + CancellationToken ctx) + { + var cleanupDelay = TimeSpan.FromSeconds(10); + if (botInstance.BotServices.KasinoShop == null) + { + await botInstance.SendChatMessageAsync("KasinoShop is not currently running.", true, autoDeleteAfter: cleanupDelay); + return; + } + await botInstance.BotServices.KasinoShop!.ProcessRigging(type); + } +} + +public class DialCommand : ICommand +{ + private Rigging type = Rigging.Dial; + public List Patterns => + [ + new Regex(@"^dial (?up|down)") + ]; + public string? HelpText => "flips a switch"; + public UserRight RequiredRight => UserRight.TrueAndHonest; + public TimeSpan Timeout => TimeSpan.FromSeconds(30); + public RateLimitOptionsModel? RateLimitOptions => new RateLimitOptionsModel + { + MaxInvocations = 2, + Window = TimeSpan.FromSeconds(60) + }; + + public async Task RunCommand(ChatBot botInstance, MessageModel message, UserDbModel user, GroupCollection arguments, + CancellationToken ctx) + { + var cleanupDelay = TimeSpan.FromSeconds(10); + if (botInstance.BotServices.KasinoShop == null) + { + await botInstance.SendChatMessageAsync("KasinoShop is not currently running.", true, autoDeleteAfter: cleanupDelay); + return; + } + if (!arguments.TryGetValue("choice", out var choice)) return; + bool dialUp = choice.Value.ToLower() == "up"; + await botInstance.BotServices.KasinoShop!.ProcessRigging(type, dial: dialUp); + } +} + +public class KeypadCommand : ICommand +{ + private Rigging type = Rigging.Dial; + public List Patterns => + [ + new Regex(@"^keypad (?\d+(?:\.\d+)?)") + ]; + public string? HelpText => "flips a switch"; + public UserRight RequiredRight => UserRight.TrueAndHonest; + public TimeSpan Timeout => TimeSpan.FromSeconds(30); + public RateLimitOptionsModel? RateLimitOptions => new RateLimitOptionsModel + { + MaxInvocations = 2, + Window = TimeSpan.FromSeconds(60) + }; + + public async Task RunCommand(ChatBot botInstance, MessageModel message, UserDbModel user, GroupCollection arguments, + CancellationToken ctx) + { + var cleanupDelay = TimeSpan.FromSeconds(10); + if (botInstance.BotServices.KasinoShop == null) + { + await botInstance.SendChatMessageAsync("KasinoShop is not currently running.", true, autoDeleteAfter: cleanupDelay); + return; + } + + if (!arguments.TryGetValue("num", out var num)) return; + var number = Convert.ToDecimal(num.Value); + + await botInstance.BotServices.KasinoShop!.ProcessRigging(type, number); + } +} + +public class PanelCommand : ICommand +{ + private Rigging type = Rigging.Dial; + public List Patterns => + [ + new Regex(@"^panel") + ]; + public string? HelpText => "flips a switch"; + public UserRight RequiredRight => UserRight.TrueAndHonest; + public TimeSpan Timeout => TimeSpan.FromSeconds(30); + public RateLimitOptionsModel? RateLimitOptions => new RateLimitOptionsModel + { + MaxInvocations = 2, + Window = TimeSpan.FromSeconds(60) + }; + + public async Task RunCommand(ChatBot botInstance, MessageModel message, UserDbModel user, GroupCollection arguments, + CancellationToken ctx) + { + var cleanupDelay = TimeSpan.FromSeconds(10); + if (botInstance.BotServices.KasinoShop == null) + { + await botInstance.SendChatMessageAsync("KasinoShop is not currently running.", true, autoDeleteAfter: cleanupDelay); + return; + } + + await botInstance.BotServices.KasinoShop!.GetCurrentRiggingState(); + } +} + + + + + + + + +public static class GlobalShopFunctions +{ + public static async Task CheckProfile(ChatBot botInstance, UserDbModel user, GamblerDbModel gambler) //checks if the user trying to run the command has a profile. if they do not, creates a profile for them + { + if (!botInstance.BotServices.KasinoShop!.Gambler_Profiles.ContainsKey(user.KfId)) + { + await botInstance.BotServices.KasinoShop.CreateProfile(gambler); + await botInstance.SendChatMessageAsync($"Created kasino shop profile for {user.FormatUsername()}({user.KfId})", true); + } + + } +} diff --git a/KfChatDotNetBot/Models/DbModels/MoneyDbModels.cs b/KfChatDotNetBot/Models/DbModels/MoneyDbModels.cs index a094233..40532d5 100644 --- a/KfChatDotNetBot/Models/DbModels/MoneyDbModels.cs +++ b/KfChatDotNetBot/Models/DbModels/MoneyDbModels.cs @@ -1,4 +1,4 @@ -ο»Ώusing System.ComponentModel; +using System.ComponentModel; using System.ComponentModel.DataAnnotations; namespace KfChatDotNetBot.Models.DbModels; @@ -278,7 +278,11 @@ public enum TransactionSourceEventType /// ///A form of juicer where the value is split among a number of participants /// - Rain + Rain, + Deposit, + Withdraw, + Sponsorship, + Loan } public enum WagerGame diff --git a/KfChatDotNetBot/Services/BotServices.cs b/KfChatDotNetBot/Services/BotServices.cs index bee7c6a..f9b7dab 100644 --- a/KfChatDotNetBot/Services/BotServices.cs +++ b/KfChatDotNetBot/Services/BotServices.cs @@ -41,6 +41,7 @@ public class BotServices private ShuffleDotUs? _shuffleDotUs; private YouTubePubSub? _youTubePubSub; public KasinoRain? KasinoRain; + public KasinoShop? KasinoShop; private Task? _websocketWatchdog; private Task? _howlggGetUserTimer; @@ -93,7 +94,8 @@ public class BotServices BuildOwncastLiveStatusCheck(), BuildShuffleDotUs(), BuildYouTubePubSub(), - BuildKasinoRain() + BuildKasinoRain(), + BuildKasinoShop() ]; try { @@ -115,6 +117,12 @@ public class BotServices _logger.Debug("Building the Kasino Rain thingy"); KasinoRain = new KasinoRain(_chatBot, _cancellationToken); } + + private async Task BuildKasinoShop() + { + _logger.Debug("Building the kasino shop"); + KasinoShop = new KasinoShop(_chatBot); + } private async Task BuildShuffle() { diff --git a/KfChatDotNetBot/Services/KasinoMines.cs b/KfChatDotNetBot/Services/KasinoMines.cs index adfbbde..3e6f64b 100644 --- a/KfChatDotNetBot/Services/KasinoMines.cs +++ b/KfChatDotNetBot/Services/KasinoMines.cs @@ -6,6 +6,7 @@ using KfChatDotNetBot.Settings; using NLog; using StackExchange.Redis; using System.Text.Json.Serialization; +using KfChatDotNetBot.Commands.Kasino; namespace KfChatDotNetBot.Services; @@ -340,8 +341,9 @@ public class KasinoMines await RemoveGame(game.Creator.Id); } - public async Task Bet(int gamblerId, int count, SentMessageTrackerModel msg, bool cashOut) //returns false if you hit a bomb, true if you didn't + public async Task Bet(GamblerDbModel gambler, int count, SentMessageTrackerModel msg, bool cashOut) //returns false if you hit a bomb, true if you didn't { + int gamblerId = gambler.Id; await GetSavedGames(gamblerId); var game = ActiveGames[gamblerId]; game.LastInteracted = DateTimeOffset.UtcNow; @@ -396,12 +398,19 @@ public class KasinoMines validBets.RemoveAt(rand); } - return await Bet(gamblerId, betCoords, msg, cashOut, true); + return await Bet(gambler, betCoords, msg, cashOut, true); } - public async Task Bet(int gamblerId, List<(int r, int c)> coords, SentMessageTrackerModel msg, bool cashOut, bool calledFromBet = false) + public async Task Bet(GamblerDbModel gambler, List<(int r, int c)> coords, SentMessageTrackerModel msg, bool cashOut, bool calledFromBet = false) { - + //KasinoShop stuff ------------------------------------------------------------------------- + if (_kfChatBot.BotServices.KasinoShop != null) + { + await GlobalShopFunctions.CheckProfile(_kfChatBot, gambler.User, gambler); + HOUSE_EDGE += _kfChatBot.BotServices.KasinoShop.Gambler_Profiles[gambler.User.KfId].HouseEdgeModifier; + } + //------------------------------------------------------------------------------------------ + int gamblerId = gambler.Id; await GetSavedGames(gamblerId); var game = ActiveGames[gamblerId]; game.LastInteracted = DateTimeOffset.UtcNow; diff --git a/KfChatDotNetBot/Services/KasinoRain.cs b/KfChatDotNetBot/Services/KasinoRain.cs index bc01c68..34e79ce 100644 --- a/KfChatDotNetBot/Services/KasinoRain.cs +++ b/KfChatDotNetBot/Services/KasinoRain.cs @@ -1,4 +1,5 @@ using System.Text.Json; +using KfChatDotNetBot.Commands.Kasino; using KfChatDotNetBot.Extensions; using KfChatDotNetBot.Models.DbModels; using KfChatDotNetBot.Settings; @@ -119,6 +120,16 @@ public class KasinoRain : IDisposable participantNames.Add(gambler.User.FormatUsername()); await Money.ModifyBalanceAsync(gambler.Id, payout, TransactionSourceEventType.Rain, "Payout from rain event", creator.Id, _ct); + //KasinoShop stuff ------------------------------------------------------------------------- + if (_kfChatBot.BotServices.KasinoShop != null) + { + await GlobalShopFunctions.CheckProfile(_kfChatBot, gambler.User, gambler); + await using var db = new ApplicationDbContext(); + var creatorGambler = await db.Gamblers.FirstOrDefaultAsync(x => x.Id == creator.Id, _ct); + await _kfChatBot.BotServices.KasinoShop.ProcessJuicerOrRainTracking(creatorGambler!, gambler, + payout); + } + //----------------------------------------------------------------------------------------- } await Money.ModifyBalanceAsync(creator.Id, -rain.RainAmount + failedPayoutAmount, TransactionSourceEventType.Rain, diff --git a/KfChatDotNetBot/Services/KasinoShop.cs b/KfChatDotNetBot/Services/KasinoShop.cs new file mode 100644 index 0000000..d068ca3 --- /dev/null +++ b/KfChatDotNetBot/Services/KasinoShop.cs @@ -0,0 +1,2120 @@ +using System.Text.Json; +using KfChatDotNetBot.Extensions; +using KfChatDotNetBot.Models; +using KfChatDotNetBot.Models.DbModels; +using KfChatDotNetBot.Settings; +using NLog; +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 RandN; +using RandN.Compat; + +namespace KfChatDotNetBot.Services; + +public class KasinoShop +{ + private static RandomShim _rand = RandomShim.Create(StandardRng.Create()); + private static readonly Logger _logger = LogManager.GetCurrentClassLogger(); + private IDatabase? _redisDb; + public static ChatBot BotInstance = null!; + public int[]? activeLoanIds = null; + public Dictionary Gambler_Profiles = new(); //list of all profiles, accesesd via kf user id + public decimal DefaultHouseEdgeModifier = 0; + + public KasinoShop(ChatBot kfChatBot) + { + BotInstance = kfChatBot; + + var connectionString = SettingsProvider.GetValueAsync(BuiltIn.Keys.BotRedisConnectionString).Result; + if (string.IsNullOrEmpty(connectionString.Value)) + { + _logger.Error($"Can't initialize the Kasino Mines service as Redis isn't configured in {BuiltIn.Keys.BotRedisConnectionString}"); + return; + } + + var redis = ConnectionMultiplexer.Connect(connectionString.Value); + _redisDb = redis.GetDatabase(); + + LoadProfiles(); + + } + + public async void LoadProfiles() + { + if (_redisDb == null) throw new InvalidOperationException("Kasino shop service isn't initialized"); + var json = await _redisDb.StringGetAsync($"Shop.Profiles.State"); + var json2 = await _redisDb.StringGetAsync($"Shop.LoanIds.State"); + if (string.IsNullOrEmpty(json)) return; + try + { + var options = new JsonSerializerOptions{IncludeFields = true}; + Gambler_Profiles = JsonSerializer.Deserialize>(json.ToString(), options) ?? + throw new InvalidOperationException(); + activeLoanIds = JsonSerializer.Deserialize(json2.ToString(), options) ?? throw new InvalidOperationException(); + } + catch (Exception e) + { + _logger.Error(e); + _logger.Error("Potentially failed to deserialize kasinoshop details"); + Gambler_Profiles = new Dictionary(); + activeLoanIds = new int[0]; + } + } + + public async Task SaveProfiles() + { + if (_redisDb == null) throw new InvalidOperationException("Kasino mines service isn't initialized"); + var options = new JsonSerializerOptions + { + IncludeFields = true, + WriteIndented = false + }; + var json = JsonSerializer.Serialize(Gambler_Profiles, options); + var json2 = JsonSerializer.Serialize(activeLoanIds, options); + await _redisDb.StringSetAsync($"Shop.Profiles.State", json, null, When.Always); + await _redisDb.StringSetAsync($"Shop.LoanIds.State", json2, null, When.Always); + } + + public async Task ResetProfiles() + { + Gambler_Profiles = new Dictionary(); + activeLoanIds = new int[0]; + await SaveProfiles(); + } + + + public async Task PrintBalance(GamblerDbModel gambler) + { + await BotInstance.SendChatMessageAsync($"{Gambler_Profiles[gambler.User.KfId].FormatBalanceAsync()}", true, autoDeleteAfter: TimeSpan.FromSeconds(10)); + } + + public async Task ResetAllLoans() + { + foreach (var key in Gambler_Profiles.Keys) + { + Gambler_Profiles[key].Loans.Clear(); + } + await SaveProfiles(); + } + + public async Task GetCurrentRiggingState() + { + string str = ""; + List values = new() { 1.02m, -0.9m, 0 }; + List differences = new() {values[0] - DefaultHouseEdgeModifier, values[1] - DefaultHouseEdgeModifier, values[2] - DefaultHouseEdgeModifier }; + if (DefaultHouseEdgeModifier == 1.02m) + { + await BotInstance.SendChatMessageAsync("The switch was flipped twice.", true, autoDeleteAfter: TimeSpan.FromSeconds(10)); + return; + } + else if (DefaultHouseEdgeModifier == -0.9m) + { + await BotInstance.SendChatMessageAsync("The switch was flipped once.", true, autoDeleteAfter: TimeSpan.FromSeconds(10)); + return; + } + else if (DefaultHouseEdgeModifier == 0) + { + await BotInstance.SendChatMessageAsync("The button is pressed.", true, autoDeleteAfter: TimeSpan.FromSeconds(10)); + return; + } + + var currentDHEM = DefaultHouseEdgeModifier; + + + while (currentDHEM != 0) + { + var ld = LowestDifferenceIndex(differences); + if (ld.i == 0) + { + str += "A switch was flipped twice.[br]"; + currentDHEM -= values[ld.i]; + } + else if (ld.i == 1) + { + str += "A switch was flipped once.[br]"; + currentDHEM -= values[ld.i]; + } + else if (ld.i == 2) + { + if (ld.d < 0) + { + currentDHEM += 0.01m; + str += "A dial was moved down.[br]"; + } + else + { + currentDHEM -= 0.01m; + str += "A dial was moved up.[br]"; + } + } + differences = new() {values[0] - DefaultHouseEdgeModifier, values[1] - DefaultHouseEdgeModifier, values[2] - DefaultHouseEdgeModifier }; + } + await BotInstance.SendChatMessageAsync(str, true, autoDeleteAfter: TimeSpan.FromSeconds(10)); + + (int i, decimal d) LowestDifferenceIndex(List diffs) + { + var lowestDifference = Math.Abs(differences[0]); + var lowestDifferenceIndex = 0; + if (Math.Abs(diffs[1]) < lowestDifference) + { + lowestDifference = Math.Abs(differences[1]); + lowestDifferenceIndex = 1; + } + + if (Math.Abs(diffs[2]) < lowestDifference) + { + lowestDifference = Math.Abs(differences[2]); + lowestDifferenceIndex = 2; + } + + return (lowestDifferenceIndex, lowestDifference); + } + } + public async Task ProcessRigging(Rigging type, decimal num = -1, bool? dial = null) + { + /* + * FLIP SWITCH - variate default house edge modifier to -0.9, then to 1.02 if it's currently -0.9 + * + * PUSH BUTTON - reset house edge modifier to 0 + * + * DIAL UP/DOWN - decrease or increase default house edge modifier by 0.01 + * + * PULL LEVER - does nothing + * + * KEYPAD - sets the house edge modifier to num + */ + var oldDefault = DefaultHouseEdgeModifier; + decimal difference = 0; + switch (type) + { + case Rigging.Lever: + await BotInstance.SendChatMessageAsync("A lever was pulled.", true, autoDeleteAfter: TimeSpan.FromSeconds(10)); + await Task.Delay(TimeSpan.FromMinutes(5)); + await BotInstance.SendChatMessageAsync("A lever returned to its original position.", true, autoDeleteAfter: TimeSpan.FromSeconds(10)); + break; + case Rigging.Keypad: + if (num == -1) throw new Exception("Invalid number passed into keypad"); + DefaultHouseEdgeModifier = num; + difference = DefaultHouseEdgeModifier - oldDefault; + await BotInstance.SendChatMessageAsync("Keypad value accepted.", true, autoDeleteAfter: TimeSpan.FromSeconds(10)); + break; + case Rigging.Switch: + if (DefaultHouseEdgeModifier != -0.9m) + { + DefaultHouseEdgeModifier = -0.9m; + } + else DefaultHouseEdgeModifier = 1.02m; + difference = DefaultHouseEdgeModifier - oldDefault; + await BotInstance.SendChatMessageAsync("A switch was flipped.", true, autoDeleteAfter: TimeSpan.FromSeconds(10)); + break; + case Rigging.Button: + DefaultHouseEdgeModifier = 0; + difference = DefaultHouseEdgeModifier - oldDefault; + break; + case Rigging.Dial: + if (dial == null) throw new Exception("Invalid dial value passed into dial"); + if (dial.Value) + { + DefaultHouseEdgeModifier += 0.01m; + } + else DefaultHouseEdgeModifier -= 0.01m; + difference = DefaultHouseEdgeModifier - oldDefault; + break; + } + foreach (var key in Gambler_Profiles.Keys) + { + Gambler_Profiles[key].HouseEdgeModifier += difference; + } + await SaveProfiles(); + } + + public decimal GetCurrentCrackPrice(GamblerDbModel gambler) + { + return CrackPrice * Gambler_Profiles[gambler.User.KfId].CrackCounter; + } + + public async Task PrintDrugMarket(GamblerDbModel gambler) + { + int cc = Gambler_Profiles[gambler.User.KfId].CrackCounter; + List drugs = new(); + drugs.Add($"1. Crack: {await (CrackPrice * cc).FormatKasinoCurrencyAsync()} per dose"); + drugs.Add($"2. Weed: {await WeedPricePerHour.FormatKasinoCurrencyAsync()} per hour"); + if (Gambler_Profiles[gambler.User.KfId].FloorNugs > 0) + { + drugs.Add($"3. Floor Nugs: {Gambler_Profiles[gambler.User.KfId].FloorNugs}"); + } + } + + public bool CheckWagerReq(GamblerDbModel gambler) + { + if (Gambler_Profiles[gambler.User.KfId].SponsorWagerLock[0] > + Gambler_Profiles[gambler.User.KfId].SponsorWagerLock[1]) return false; + return true; + } + + public decimal RemainingWagerReq(GamblerDbModel gambler) + { + return Gambler_Profiles[gambler.User.KfId].SponsorWagerLock[0] - Gambler_Profiles[gambler.User.KfId].SponsorWagerLock[1]; + } + + public async Task ProcessDrugUse(GamblerDbModel gambler, decimal amount, int drug) + { + Dictionary drugs = new() + { + {1, "Crack"}, + {2, "Weed"}, + {3, "Floor Nugs"} + }; + if (drug != 3) + { + if (Gambler_Profiles[gambler.User.KfId].Balance()[0] < amount) + { + await BotInstance.SendChatMessageAsync( + $"{gambler.User.FormatUsername()}, you can't afford to buy {await amount.FormatKasinoCurrencyAsync()} worth of {drugs[drug]}. {await Gambler_Profiles[gambler.User.KfId].FormatBalanceAsync()}", true, autoDeleteAfter: TimeSpan.FromSeconds(10)); + return; + } + } + else + { + if (Gambler_Profiles[gambler.User.KfId].FloorNugs < amount) + { + await BotInstance.SendChatMessageAsync($"{gambler.User.FormatUsername()}, you only have {Gambler_Profiles[gambler.User.KfId].FloorNugs} floor nugs, so that's all you could smoke right now.", true, autoDeleteAfter: TimeSpan.FromSeconds(10)); + amount = Gambler_Profiles[gambler.User.KfId].FloorNugs; + } + } + + if (drug == 1) + { + _ = Gambler_Profiles[gambler.User.KfId].SmokeCrack(); + } + else if (drug == 2) + { + TimeSpan weedDuration = TimeSpan.FromHours((double)(amount / WeedPricePerHour)); + _ = Gambler_Profiles[gambler.User.KfId].SmokeWeed(weedDuration); + } + else + { + TimeSpan dur = TimeSpan.FromMinutes(6 * (int)amount); + _ = Gambler_Profiles[gambler.User.KfId].SmokeWeed(dur); + } + } + + public async Task ProcessLoan(int receiverKfId, decimal amount, GamblerDbModel gUser, int senderGamblerId) + { + var sender = gUser.User; + //check if the person they tried to loan to is able to get a loan + await using var db = new ApplicationDbContext(); + var targetUser = await db.Users.FirstOrDefaultAsync(u => u.KfId == receiverKfId); + if (targetUser == null) + { + await BotInstance.SendChatMessageAsync($"{sender.FormatUsername()}, user with KF ID {receiverKfId} not found.", true, autoDeleteAfter: TimeSpan.FromSeconds(10)); + return false; + } + if (!Gambler_Profiles[receiverKfId].IsLoanable) + { + await BotInstance.SendChatMessageAsync( + $"{sender.FormatUsername()}, {targetUser.FormatUsername()} is not loanable, they need to beg for a loan first.", true, autoDeleteAfter: TimeSpan.FromSeconds(10)); + return false; + } + //check if the loaner has enough crypto to send loan + if (Gambler_Profiles[sender.KfId].Balance()[1] < amount) + { + await BotInstance.SendChatMessageAsync($"{sender.FormatUsername()}, you don't have enough krypto to loan {targetUser.FormatUsername()}. {amount}. {await Gambler_Profiles[sender.KfId].FormatBalanceAsync()}", true, autoDeleteAfter: TimeSpan.FromSeconds(10)); + return false; + } + + Random rand = new Random(); + int loanId = (int)(1000000000 * rand.NextDouble()); + if (activeLoanIds == null) activeLoanIds = new int[1]; + else + { + int[] newLoans = new int[activeLoanIds.Length + 1]; + activeLoanIds.CopyTo(newLoans, 1); + newLoans[0] = loanId; + activeLoanIds = newLoans; + } + await using var gamblerdb = new ApplicationDbContext(); + var targetgambler = await gamblerdb.Gamblers.FirstOrDefaultAsync(g => g.User.KfId == Gambler_Profiles[receiverKfId].GamblerId); + if (targetgambler == null) + { + await BotInstance.SendChatMessageAsync($"{sender.FormatUsername()}, gambler profile for user with KF ID {receiverKfId} not found.", true, autoDeleteAfter: TimeSpan.FromSeconds(10)); + return false; + } + //public Loan(decimal amount, int payableToGambler, int payableToKf, int recieverGambler, int recieverKf, int Id) + Loan loan = new Loan(amount, senderGamblerId, sender.Id, targetgambler.Id, receiverKfId, loanId, sender); + //take from senders crypto balance and deposit into receivers kasino balance + Gambler_Profiles[sender.KfId].ModifyBalance(-amount); + await Money.ModifyBalanceAsync(targetgambler.Id, amount, TransactionSourceEventType.Loan); + Gambler_Profiles[sender.KfId].Loans.Add(loanId, loan); + Gambler_Profiles[receiverKfId].Loans.Add(loanId, loan); + Gambler_Profiles[receiverKfId].OutstandingLoanBalance += amount; + Gambler_Profiles[receiverKfId].IsLoanable = false; + Gambler_Profiles[receiverKfId].KreditScore -= Convert.ToInt32(amount / 2); + await SaveProfiles(); + return true; + } + + public async Task PrintSmashableShop(GamblerDbModel gambler) + { + string str = ""; + int counter = 1; + foreach (var type in Enum.GetValues()) + { + str += $"{counter}: {type} - $10000 KKK[br]"; + } + } + + public async Task ProcessSmashablePurchase(GamblerDbModel gambler, int type) + { + var smashTypes = Enum.GetValues(); + type--; + if (type < 0 || type > smashTypes.Length - 1) + { + await BotInstance.SendChatMessageAsync( + $"{gambler.User.FormatUsername()}, invalid smashable type. 1 - {smashTypes.Length}", true, autoDeleteAfter: TimeSpan.FromSeconds(10)); + return; + } + if (Gambler_Profiles[gambler.User.KfId].Balance()[1] < 10000) + { + await BotInstance.SendChatMessageAsync( + $"{gambler.User.FormatUsername()}, you don't have enough krypto to buy smashables. {await Gambler_Profiles[gambler.User.KfId].FormatBalanceAsync()}"); + return; + } + var id = GenerateRandomId(gambler); + Gambler_Profiles[gambler.User.KfId].Assets.Add(id, new Smashable(id, 10000, smashTypes[type])); + Gambler_Profiles[gambler.User.KfId].ModifyBalance(-10000); + } + public async Task ProcessRepayment(GamblerDbModel payerGambler, UserDbModel payer, int payeeKfId, decimal amount) //loans can be repaid from crypto balance and kasino balance. it prefers to take from your crypto balance first if you have any + { + decimal payerTotalBalance = payerGambler.Balance + Gambler_Profiles[payer.KfId].Balance()[0]; + if (payerTotalBalance < amount) + { + await BotInstance.SendChatMessageAsync($"{payer.FormatUsername()}, you don't have enough to repay {await amount.FormatKasinoCurrencyAsync()}. Your total balance: {await payerTotalBalance.FormatKasinoCurrencyAsync()}"); + return; + } + + + int loanId = -1; + //find the loan + foreach (var loan in Gambler_Profiles[payeeKfId].Loans.Values) + { + if (loan.payableToKf == payer.KfId) + { + loanId = loan.Id; + break; + } + } + + if (loanId == -1) + { + await BotInstance.SendChatMessageAsync($"{payer.FormatUsername()}, you don't have a loan with {payeeKfId}."); + return; + } + var theloan = Gambler_Profiles[payeeKfId].Loans[loanId]; + //compare the amount paid to the amount of the loan + if (amount >= theloan.payoutAmount) + { + //if the amount is more or equal, clear the loan when you pay, and give the payer credit score bonus for repaying the loan + amount = theloan.payoutAmount; + Gambler_Profiles[payer.KfId].KreditScore += Convert.ToInt32(Gambler_Profiles[payer.KfId].Loans[loanId].amount * 3 / 4); + Gambler_Profiles[payer.KfId].Loans.Remove(loanId); + Gambler_Profiles[payeeKfId].Loans.Remove(loanId); + var newLoans = new int[activeLoanIds!.Length - 1]; + if (activeLoanIds.Length > 1) + { + for (int i = 0; i < activeLoanIds.Length - 1; i++) + { + if (activeLoanIds[i] != loanId) newLoans[i] = activeLoanIds[i]; + } + } + activeLoanIds = newLoans; + } + else if (amount < Gambler_Profiles[payeeKfId].Loans[loanId].payoutAmount) + { + Gambler_Profiles[payeeKfId].Loans[loanId].payoutAmount -= amount; + } + //split the amount from crypto and kasino balance as available + decimal takeFromCrypto = 0; + decimal takeFromKasino = 0; + + if (amount > Gambler_Profiles[payeeKfId].Balance()[0]) + { + //if the amount is more than the amount of crypto you have, take from kasino balance as well + takeFromCrypto = Gambler_Profiles[payeeKfId].Balance()[0]; + takeFromKasino = amount - takeFromCrypto; + } + else takeFromCrypto = amount; + //process the payments + Gambler_Profiles[payer.KfId].ModifyBalance(-takeFromCrypto); + await Money.ModifyBalanceAsync(payerGambler.Id, -takeFromKasino, TransactionSourceEventType.Loan); + Gambler_Profiles[payeeKfId].ModifyBalance(amount); + await SaveProfiles(); + } + + public async Task PrintLoansList(GamblerDbModel gambler) + { + var user = gambler.User; + string message = $"{user.FormatUsername()}"; + var msg = await BotInstance.SendChatMessageAsync($"{message}", true); + await BotInstance.WaitForChatMessageAsync(msg); + if (Gambler_Profiles[user.KfId].Loans.Count == 0) + { + message += " is debt free!"; + await BotInstance.KfClient.EditMessageAsync(msg.ChatMessageUuid, message); + await Task.Delay(TimeSpan.FromSeconds(10)); + await BotInstance.KfClient.DeleteMessageAsync(msg.ChatMessageUuid); + return; + } + + foreach (var loan in Gambler_Profiles[user.KfId].Loans.Values) + { + message += $"[br]{await loan.ToStringAsync(gambler.User.KfId)}"; + await BotInstance.KfClient.EditMessageAsync(msg.ChatMessageUuid, message); + await Task.Delay(10); + } + await Task.Delay(TimeSpan.FromSeconds(10)); + await BotInstance.KfClient.DeleteMessageAsync(msg.ChatMessageUuid); + } + + + public async Task ProcessBeg(GamblerDbModel gambler) + { + var user = gambler.User; + _ = Gambler_Profiles[user.KfId].Beg(user); + } + + public async Task ProcessWithdraw(GamblerDbModel gambler, decimal amount) + { + int kfId = gambler.User.KfId; + if (amount > gambler.Balance) + { + await BotInstance.SendChatMessageAsync($"{gambler.User.FormatUsername()}, you don't have enough to withdraw {await amount.FormatKasinoCurrencyAsync()}. Balance: {await gambler.Balance.FormatKasinoCurrencyAsync()}", true, autoDeleteAfter: TimeSpan.FromSeconds(10)); + return; + } + + Gambler_Profiles[gambler.User.KfId].Withdraw(amount); + var newBalance = await Money.ModifyBalanceAsync(gambler.Id, amount, TransactionSourceEventType.Withdraw); + await SaveProfiles(); + await BotInstance.SendChatMessageAsync( + $"{gambler.User.FormatUsername()}, you withdrew {await amount.FormatKasinoCurrencyAsync()} to your krypto balance. Kasino Balance: {await newBalance.FormatKasinoCurrencyAsync()} | {await Gambler_Profiles[kfId].FormatBalanceAsync()}", + true, autoDeleteAfter: TimeSpan.FromSeconds(10)); + + } + + public async Task ProcessDeposit(GamblerDbModel gambler, decimal amount) + { + int kfId = gambler.User.KfId; + if (amount > Gambler_Profiles[kfId].Balance()[0]) + { + await BotInstance.SendChatMessageAsync( + $"{gambler.User.FormatUsername()}, you don't have enough krypto to deposit {await amount.FormatKasinoCurrencyAsync()}. {await Gambler_Profiles[kfId].FormatBalanceAsync()}"); + return; + } + var newBalance = await Money.ModifyBalanceAsync(gambler.Id, amount, TransactionSourceEventType.Deposit); + Gambler_Profiles[kfId].Deposit(amount); + await SaveProfiles(); + await BotInstance.SendChatMessageAsync( + $"{gambler.User.FormatUsername()}, you deposited {await amount.FormatKasinoCurrencyAsync()} to your kasino balance. Kasino Balance: {await newBalance.FormatKasinoCurrencyAsync()} | {await Gambler_Profiles[kfId].FormatBalanceAsync()}", + true, autoDeleteAfter: TimeSpan.FromSeconds(10)); + + } + + public async Task ProcessInvestment(GamblerDbModel gambler, int item, decimal amount) + { + if (amount > Gambler_Profiles[gambler.User.KfId].Balance()[1]) + { + await BotInstance.SendChatMessageAsync( + $"{gambler.User.FormatUsername()}, you can't afford this investment. {await Gambler_Profiles[gambler.User.KfId].FormatBalanceAsync()}", true, autoDeleteAfter: TimeSpan.FromSeconds(10)); + return; + } + + if (item < 1 || item > 3) + { + await BotInstance.SendChatMessageAsync($"{gambler.User.FormatUsername()}, invalid investment choice. 1 - gold, 2 - silver, 3 - house.",true, autoDeleteAfter: TimeSpan.FromSeconds(10)); + return; + } + + int id = GenerateRandomId(gambler); + + string str; + switch (item) + { + case 1: + str = Money.GetRandomDouble(gambler) < 0.5 ? "chain" : "coin"; + Investment newGold = new Investment(id, amount, GoldInterestRange, InvestmentType.Gold, $"Gold {str}"); + Gambler_Profiles[gambler.User.KfId].Assets.Add(id, newGold); + break; + case 2: + str = Money.GetRandomDouble(gambler) < 0.5 ? "chain" : "coin"; + Investment newSilver = new Investment(id, amount, SilverInterestRange, InvestmentType.Silver, $"Silver {str}"); + Gambler_Profiles[gambler.User.KfId].Assets.Add(id, newSilver); + break; + case 3: + Investment newHouse = new Investment(id, amount, HouseInterestRange, InvestmentType.House, "House"); + Gambler_Profiles[gambler.User.KfId].Assets.Add(id, newHouse); + break; + } + + await SaveProfiles(); + } + + + + public async Task ProcessCarPurchase(GamblerDbModel gambler, int carId) + { + //civic audi bentley bmw + foreach (var asset in Gambler_Profiles[gambler.User.KfId].Assets.Values) + { + if (asset is Car c) + { + await BotInstance.SendChatMessageAsync( + $"{gambler.User.FormatUsername()}, you can't buy a car you already have one: {c}.", true, + autoDeleteAfter: TimeSpan.FromSeconds(10)); + return; + } + } + var car = DefaultCars.ElementAt(carId).Value; + car.SetId(gambler); + Gambler_Profiles[gambler.User.KfId].Assets.Add(car.Id, car); + await BotInstance.SendChatMessageAsync($"{gambler.User.FormatUsername()}, you bought {car}", true, autoDeleteAfter: TimeSpan.FromSeconds(10)); + await SaveProfiles(); + } + + public void ProcessLossBackTracking(GamblerDbModel gambler, decimal amount) + { + Gambler_Profiles[gambler.User.KfId].Tracker.AddLossback(amount); + } + + public async Task ProcessWorkJob(GamblerDbModel gambler) + { + bool hasCar = false; + int carId = -1; + foreach (var assetKey in Gambler_Profiles[gambler.User.KfId].Assets.Keys) + { + if (Gambler_Profiles[gambler.User.KfId].Assets[assetKey] is Car car) + { + carId = assetKey; + hasCar = true; + break; + } + } + + if (!hasCar) + { + await BotInstance.SendChatMessageAsync($"{gambler.User.FormatUsername()}, you don't have a car to get a job with.", true, autoDeleteAfter:TimeSpan.FromSeconds(10)); + return; + } + + await ((Car)(Gambler_Profiles[gambler.User.KfId].Assets[carId])).ProcessWorkJob(gambler); + await SaveProfiles(); + } + public async Task ProcessWagerTracking(GamblerDbModel gambler, WagerGame game, decimal amount, decimal net, decimal newBalance) + { + Gambler_Profiles[gambler.User.KfId].Tracker.AddWager(game, amount, net); + if (newBalance < 1 && Gambler_Profiles[gambler.User.KfId].SponsorWagerLock[0] > 0) //if you ran out of money after that gamble reset your wager lock + { + Gambler_Profiles[gambler.User.KfId].SponsorWagerLock = new decimal[] { 0, 0 }; + } + + if (Gambler_Profiles[gambler.User.KfId].SponsorWagerLock[0] > + Gambler_Profiles[gambler.User.KfId].SponsorWagerLock[1]) + { + Gambler_Profiles[gambler.User.KfId].SponsorWagerLock = new decimal[] { 0, 0 }; + await BotInstance.SendChatMessageAsync( + $"{gambler.User.FormatUsername()}, you have reached the wager requirement for your sponsorship!", true, autoDeleteAfter: TimeSpan.FromSeconds(10)); + } + await SaveProfiles(); + } + + public async Task ProcessJuicerOrRainTracking(GamblerDbModel sender, GamblerDbModel reciever, decimal amountPerReciever) + { + Gambler_Profiles[sender.User.KfId].Tracker.AddWithdrawal(amountPerReciever); + if (Gambler_Profiles.ContainsKey(reciever.User.KfId)) Gambler_Profiles[reciever.User.KfId].Tracker.AddDeposit(amountPerReciever); + if (Gambler_Profiles.ContainsKey(sender.User.KfId)) Gambler_Profiles[sender.User.KfId].Tracker.AddWithdrawal(amountPerReciever); + await SaveProfiles(); + } + + public async Task ProcessStake(GamblerDbModel gambler, decimal amount) + { + //check if they have enough crypto for the stake + if (Gambler_Profiles[gambler.User.KfId].Balance()[1] < amount) + { + await BotInstance.SendChatMessageAsync( + $"{gambler.User.FormatUsername()}, you don't have enough krypto to stake {await amount.FormatKasinoCurrencyAsync()}. {await Gambler_Profiles[gambler.User.KfId].FormatBalanceAsync()}",true, autoDeleteAfter: TimeSpan.FromSeconds(10)); + return; + } + + int id = GenerateRandomId(gambler); + + var stake = new Investment(id, amount, CryptoStakeInterestRange, InvestmentType.Stake, "Stake"); + Gambler_Profiles[gambler.User.KfId].Assets.Add(id, stake); + Gambler_Profiles[gambler.User.KfId].ModifyBalance(-amount); + await BotInstance.SendChatMessageAsync( + $"{gambler.User.FormatUsername()}, you staked {await amount.FormatKasinoCurrencyAsync()} krypto.[br] {Gambler_Profiles[gambler.User.KfId].Assets[id]} {await Gambler_Profiles[gambler.User.KfId].FormatBalanceAsync()}"); + await SaveProfiles(); + } + + public async Task ProcessSponsorship(GamblerDbModel gambler) + { + if (Gambler_Profiles[gambler.User.KfId].IsSponsored) + { + await BotInstance.SendChatMessageAsync($"{gambler.User.FormatUsername()}, you are already sponsored.", true, + autoDeleteAfter: TimeSpan.FromSeconds(10)); + return; + } + Gambler_Profiles[gambler.User.KfId].IsSponsored = true; + await BotInstance.SendChatMessageAsync( + $"{gambler.User.FormatUsername()}, you are now sponsored! You can claim your bonus every day with !sponsor bonus", true, autoDeleteAfter: TimeSpan.FromSeconds(10)); + await SaveProfiles(); + } + + public async Task ProcessSponsorshipEnd(GamblerDbModel gambler) + { + Gambler_Profiles[gambler.User.KfId].IsSponsored = false; + await BotInstance.SendChatMessageAsync( + $"{gambler.User.FormatUsername()}, you are no longer sponsored!", true, autoDeleteAfter: TimeSpan.FromSeconds(10)); + await SaveProfiles(); + } + + public async Task ProcessSponsorBonus(GamblerDbModel gambler) + { + Gambler_Profiles[gambler.User.KfId].ProcessSponsorBonus(KASINO_SPONSOR_BONUS); + Gambler_Profiles[gambler.User.KfId].SponsorWagerLock[1] += KASINO_SPONSOR_BONUS; + Gambler_Profiles[gambler.User.KfId].lastSponsorBonus = DateTime.UtcNow; + var newBalance = await Money.ModifyBalanceAsync(gambler.Id, KASINO_SPONSOR_BONUS, TransactionSourceEventType.Sponsorship); + await BotInstance.SendChatMessageAsync( + $"{gambler.User.FormatUsername()}, you claimed your sponsor bonus of {await KASINO_SPONSOR_BONUS.FormatKasinoCurrencyAsync()}. Balance: {await newBalance.FormatKasinoCurrencyAsync()}"); + await SaveProfiles(); + } + public async Task UnStake(GamblerDbModel gambler, decimal amount = -1, int assetId = -1, bool all = false) + { + bool noId = assetId == -1; + //check if they have a stake + int stakeCounter = 0; + bool validId = false; + foreach (var asset in Gambler_Profiles[gambler.User.KfId].Assets.Values) + { + if (asset is Investment inv && inv.investment_type == InvestmentType.Stake) + { + stakeCounter++; + if (assetId == -1) assetId = inv.Id; + if (assetId == inv.Id) validId = true; + } + } + if (stakeCounter == 0) + { + await BotInstance.SendChatMessageAsync($"{gambler.User.FormatUsername()}, you don't have a stake to unstake.", true, autoDeleteAfter: TimeSpan.FromSeconds(10)); + return; + } + + if (!validId) + { + await BotInstance.SendChatMessageAsync($"{gambler.User.FormatUsername()}, you don't have a stake with that ID.", true, autoDeleteAfter: TimeSpan.FromSeconds(10)); + return; + } + + Investment stake; + int cooldown; + decimal value; + decimal totalStakedValue = 0; + if (all || amount >= totalStakedValue) + { + bool success = false; + foreach (var asset in Gambler_Profiles[gambler.User.KfId].Assets.Values) + { + if (asset is Investment inv && inv.investment_type == InvestmentType.Stake) + { + stake = inv; + value = stake.GetCurrentValue(); + totalStakedValue += value; + cooldown = (DateTime.UtcNow - inv.acquired).Days; + if (cooldown > 7) + { + success = true; + Gambler_Profiles[gambler.User.KfId].Assets.Remove(asset.Id); + Gambler_Profiles[gambler.User.KfId].ModifyBalance(value); + await BotInstance.SendChatMessageAsync( + $"{gambler.User.FormatUsername()} unstaked {stake}. {await Gambler_Profiles[gambler.User.KfId].FormatBalanceAsync()}"); + + } + } + } + + if (!success) + { + await BotInstance.SendChatMessageAsync($"{gambler.User.FormatUsername()}, you don't have any stakes that are ready to be unstaked.", true, autoDeleteAfter: TimeSpan.FromSeconds(10)); + return; + } + } + else if (stakeCounter == 1) + { + stake = (Investment)Gambler_Profiles[gambler.User.KfId].Assets[assetId]; + value = stake.GetCurrentValue(); + cooldown = (DateTime.UtcNow - stake.acquired).Days; + if (cooldown < 7) + { + await BotInstance.SendChatMessageAsync( + $"{gambler.User.FormatUsername()}, you can't unstake your stake yet, {7-cooldown} days until it unlocks."); + } + + if (amount == -1 || amount >= value) + { + //unstake the whole thing if no amount or if amount is greater than its value + Gambler_Profiles[gambler.User.KfId].Assets.Remove(assetId); + Gambler_Profiles[gambler.User.KfId].ModifyBalance(value); + await BotInstance.SendChatMessageAsync( + $"{gambler.User.FormatUsername()} unstaked {stake}. {await Gambler_Profiles[gambler.User.KfId].FormatBalanceAsync()}"); + } + else + { + ((Investment)Gambler_Profiles[gambler.User.KfId].Assets[assetId]).StakePartialSale(amount); + stake = ((Investment)Gambler_Profiles[gambler.User.KfId].Assets[assetId]); + Gambler_Profiles[gambler.User.KfId].ModifyBalance(amount); + await BotInstance.SendChatMessageAsync( + $"{gambler.User.FormatUsername()} partially unstaked {stake}. {await Gambler_Profiles[gambler.User.KfId].FormatBalanceAsync()}"); + } + + } + else //if you have multiple stakes + { + stake = (Investment)Gambler_Profiles[gambler.User.KfId].Assets[assetId]; + value = stake.GetCurrentValue(); + if (amount == -1) + { + //unstake whole stake based on the id + Gambler_Profiles[gambler.User.KfId].Assets.Remove(assetId); + Gambler_Profiles[gambler.User.KfId].ModifyBalance(value); + await BotInstance.SendChatMessageAsync( + $"{gambler.User.FormatUsername()} unstaked {stake}. {await Gambler_Profiles[gambler.User.KfId].FormatBalanceAsync()}"); + } + else + { + var originalAmount = amount; + if (noId) + { + + foreach (var asset in Gambler_Profiles[gambler.User.KfId].Assets.Values) + { + if (asset is Investment inv && inv.investment_type == InvestmentType.Stake) + { + value = inv.GetCurrentValue(); + if (amount >= value) + { + Gambler_Profiles[gambler.User.KfId].Assets.Remove(assetId); + Gambler_Profiles[gambler.User.KfId].ModifyBalance(value); + amount -= value; + await BotInstance.SendChatMessageAsync( + $"{gambler.User.FormatUsername()} unstaked {stake}. {await Gambler_Profiles[gambler.User.KfId].FormatBalanceAsync()}"); + } + else + { + ((Investment)Gambler_Profiles[gambler.User.KfId].Assets[assetId]).StakePartialSale(amount); + stake = ((Investment)Gambler_Profiles[gambler.User.KfId].Assets[assetId]); + Gambler_Profiles[gambler.User.KfId].ModifyBalance(amount); + amount = 0; + await BotInstance.SendChatMessageAsync($"{gambler.User.FormatUsername()}, partially unstaked {stake}. {await Gambler_Profiles[gambler.User.KfId].FormatBalanceAsync()}", true, autoDeleteAfter: TimeSpan.FromSeconds(10)); + await BotInstance.SendChatMessageAsync($"{gambler.User.FormatUsername()}, successfully unstaked {await originalAmount.FormatKasinoCurrencyAsync()}", true, autoDeleteAfter: TimeSpan.FromSeconds(10)); + break; + } + } + } + } + else + { + //partially unstake based on ID + ((Investment)Gambler_Profiles[gambler.User.KfId].Assets[assetId]).StakePartialSale(amount); + stake = ((Investment)Gambler_Profiles[gambler.User.KfId].Assets[assetId]); + Gambler_Profiles[gambler.User.KfId].ModifyBalance(amount); + await BotInstance.SendChatMessageAsync( + $"{gambler.User.FormatUsername()} partially unstaked {stake}. {await Gambler_Profiles[gambler.User.KfId].FormatBalanceAsync()}"); + } + } + } + await SaveProfiles(); + } + public async Task ProcessAssetSale(GamblerDbModel gambler, int assetId) + { + if (!Gambler_Profiles[gambler.User.KfId].Assets.ContainsKey(assetId)) + { + await BotInstance.SendChatMessageAsync($"{gambler.User.FormatUsername()}, you don't have any assets with id {assetId}.", true, autoDeleteAfter: TimeSpan.FromSeconds(10)); + return; + } + var asset = Gambler_Profiles[gambler.User.KfId].Assets[assetId]; + int cooldown; + if (asset is Investment inv) + { + switch (inv.investment_type) + { + case InvestmentType.Gold or InvestmentType.Silver: + cooldown = (DateTime.UtcNow - inv.acquired).Days; + if (cooldown < 5) + { + await BotInstance.SendChatMessageAsync($"{gambler.User.FormatUsername()}, you can't sell your {inv.investment_type} investment yet, It's been less than 5 days since you bought it. {cooldown} days until it arrives.", true, autoDeleteAfter: TimeSpan.FromSeconds(10)); + return; + } + + break; + case InvestmentType.Stake: + cooldown = (DateTime.UtcNow - inv.acquired).Days; + if (cooldown < 7) + { + await BotInstance.SendChatMessageAsync($"{gambler.User.FormatUsername()}, you can't sell your Stake yet, {7-cooldown} days until it unlocks.", true, autoDeleteAfter: TimeSpan.FromSeconds(10)); + return; + } + + break; + } + } + else if (asset is Smashable smash) + { + await BotInstance.SendChatMessageAsync($"{gambler.User.FormatUsername()}, nobody wants to buy your shitty {smash}.", true, autoDeleteAfter: TimeSpan.FromSeconds(10)); + return; + } + + + + Gambler_Profiles[gambler.User.KfId].Assets.Remove(assetId); + Gambler_Profiles[gambler.User.KfId].ModifyBalance(asset.GetCurrentValue()); + await BotInstance.SendChatMessageAsync( + $"{gambler.User.FormatUsername()} sold {asset}. {await Gambler_Profiles[gambler.User.KfId].FormatBalanceAsync()}"); + await SaveProfiles(); + } + + + public async Task ProcessSkinPurchase(GamblerDbModel gambler, int num) + { + //first confirm sufficient balance + var skin = BotInstance.BotServices.KasinoShop!.Gambler_Profiles[gambler.User.KfId].sMarket.GetSkins(gambler)[num]; + if (BotInstance.BotServices.KasinoShop!.Gambler_Profiles[gambler.User.KfId].Balance()[1] < skin.originalValue) + { + await BotInstance.SendChatMessageAsync($"{gambler.User.FormatUsername()}, you don't have enough krypto to buy this skin. {await Gambler_Profiles[gambler.User.KfId].FormatBalanceAsync()}", true, autoDeleteAfter: TimeSpan.FromSeconds(10)); + return; + } + BotInstance.BotServices.KasinoShop!.Gambler_Profiles[gambler.User.KfId].ModifyBalance(-skin.originalValue); + BotInstance.BotServices.KasinoShop!.Gambler_Profiles[gambler.User.KfId].Assets.Add(skin.Id, skin); + BotInstance.BotServices.KasinoShop!.Gambler_Profiles[gambler.User.KfId].sMarket.SellsSkinTo(gambler, num); + await BotInstance.SendChatMessageAsync($"{gambler.User.FormatUsername()}, you bought {skin}. {await Gambler_Profiles[gambler.User.KfId].FormatBalanceAsync()}"); + await SaveProfiles(); + } + public async Task PrintSkinMarket(GamblerDbModel gambler) + { + string str = $"{gambler.User.FormatUsername()}'s skins:[br]"; + var profile = Gambler_Profiles[gambler.User.KfId]; + var skins = profile.sMarket.GetSkins(gambler); + for (int i = 0; i < skins.Count; i++) + { + str += $"{i + 1}: {skins[i]}[br]"; + } + await BotInstance.SendChatMessageAsync(str, true, autoDeleteAfter: TimeSpan.FromSeconds(15)); + } + + + public async Task PrintShoeMarket(GamblerDbModel gambler) + { + string str = "Shoes for sale:[br]"; + var shoeMarket = Gambler_Profiles[gambler.User.KfId].shMarket; + int counter = 1; + foreach (var shoe in shoeMarket.GetShoes(gambler)) + { + str += $"{counter}: {shoe}[br]"; + counter++; + } + await BotInstance.SendChatMessageAsync(str, true, autoDeleteAfter: TimeSpan.FromSeconds(15)); + } + public async Task ProcessShoePurchase(GamblerDbModel gambler, int num) + { + //yeezy adidas jordan + + var shoeMarket = Gambler_Profiles[gambler.User.KfId].shMarket; + var shoe = shoeMarket.GetShoes(gambler)[num]; + + if (Gambler_Profiles[gambler.User.KfId].Balance()[1] < shoe.originalValue) + { + await BotInstance.SendChatMessageAsync( + $"{gambler.User.FormatUsername()}, you don't have enough krypto to buy {shoe}.[br] {await Gambler_Profiles[gambler.User.KfId].FormatBalanceAsync()}"); + return; + } + Gambler_Profiles[gambler.User.KfId].Assets.Add(shoe.Id, shoe); + Gambler_Profiles[gambler.User.KfId].ModifyBalance(-shoe.originalValue); + } + public async Task UpdateGambler(GamblerDbModel gambler) + { + //if someone abandons their gambler profile they can do !shop update gambler to update their gambler profile + Gambler_Profiles[gambler.User.KfId].GamblerId = gambler.Id; + await SaveProfiles(); + } + + + /* + * STUFF TO IMPLEMENT IF THIS PROBLEM ACTUALLY HAPPENS, CURRENTLY UNIMPLEMENTED---------------------------------------------------------------- + */ + public async Task UpdateProfileId(GamblerDbModel gambler) + { + //if someone gets their account fucked with by null, assuming their gambler id stays the same + foreach (var key in Gambler_Profiles.Keys) + { + if (Gambler_Profiles[key].GamblerId == gambler.Id) + { + Gambler_Profiles[key].ID = gambler.User.KfId; + } + } + await SaveProfiles(); + } + /* + * ----------------------------------------------------------------------------------------------------------------------------------------- + */ + public async Task ProcessSmash(GamblerDbModel gambler) + { + await BotInstance.BotServices.KasinoShop!.Gambler_Profiles[gambler.User.KfId].Smash(gambler); + + await SaveProfiles(); + } + + public async Task PrintRtp(GamblerDbModel gambler) + { + var rtp = Gambler_Profiles[gambler.User.KfId].Tracker.GetRtp(); + await BotInstance.SendChatMessageAsync(rtp, true, autoDeleteAfter: TimeSpan.FromSeconds(10)); + } + + public async Task PrintAssets(GamblerDbModel gambler) + { + string str = $"{gambler.User.FormatUsername()}'s assets:[br]"; + int counter = 1; + bool hasAssets = false; + foreach (var asset in Gambler_Profiles[gambler.User.KfId].Assets.Values) + { + str += $"{counter}: {asset}[br]"; + counter++; + hasAssets = true; + } + if (!hasAssets) str = $"{gambler.User.FormatUsername()}, you don't have any assets."; + await BotInstance.SendChatMessageAsync(str, true, autoDeleteAfter: TimeSpan.FromSeconds(10)); + } + + public async Task PrintInvestments(GamblerDbModel gambler) + { + string str = $"{gambler.User.FormatUsername()}'s investments:[br]"; + int counter = 1; + bool hasInvestments = false; + foreach (var asset in Gambler_Profiles[gambler.User.KfId].Assets.Values) + { + if (asset is Investment i) + { + str += $"{counter}: {i}[br]"; + counter++; + hasInvestments = true; + } + } + if (!hasInvestments) str = $"{gambler.User.FormatUsername()}, you don't have any investments."; + await BotInstance.SendChatMessageAsync(str, true, autoDeleteAfter: TimeSpan.FromSeconds(10)); + } + + public async Task CreateProfile(GamblerDbModel gambler) + { + await BotInstance.SendChatMessageAsync($"Creating profile for {gambler.User.FormatUsername()}...", true, autoDeleteAfter: TimeSpan.FromSeconds(10)); + if (Gambler_Profiles.ContainsKey(gambler.User.KfId)) + { + throw new Exception("Attempted to create a new profile for someone who seems to already have a profile?"); + } + var profile = new KasinoShopProfile(gambler); + Gambler_Profiles.Add(profile.ID, profile); + await SaveProfiles(); + + + } + + + + + + + + + + + + + + + + + + + + + + public class KasinoShopProfile + { + public int ID { get; set; } + public int GamblerId { get; set; } + public string name; + private decimal CryptoBalance; + public decimal OutstandingLoanBalance; + public Dictionary Assets; + public Dictionary Loans = new(); + public decimal[] SponsorWagerLock = new decimal[2]; //[0] is how much you've wagered against your wager requirement, [1] is the wager requirement + public decimal HouseEdgeModifier = 0; + public int CrackCounter = 0; + public int FloorNugs = 0; + public DateTime LastSmokedCrack = DateTime.MinValue; + public bool IsSponsored; + public DateTime lastSponsorBonus = DateTime.MinValue; + public bool IsWeeded; + public bool IsCracked; + public bool IsInWithdrawal; + public bool IsLoanable; + public SkinMarket sMarket; + public ShoeMarket shMarket; + private CancellationTokenSource CrackToken = new(); + private CancellationTokenSource WeedToken = new(); + private CancellationTokenSource BegToken = new(); + private TimeSpan WeedTimer = TimeSpan.FromSeconds(0); //time remaining on your weed buff + private TimeSpan CrackTimer = TimeSpan.FromSeconds(0); + public int KreditScore; + public StatTracker Tracker; + + public KasinoShopProfile(GamblerDbModel gambler) + { + int gid = gambler.Id; + int kfid = gambler.User.KfId; + ID = kfid; + GamblerId = gid; + Assets = new(); + CryptoBalance = 0; + OutstandingLoanBalance = 0; + IsSponsored = false; + IsWeeded = false; + IsCracked = false; + IsInWithdrawal = false; + IsLoanable = false; + KreditScore = 100; + Tracker = new StatTracker(gid, kfid); + name = gambler.User.FormatUsername(); + sMarket = new SkinMarket(gambler); + shMarket = new ShoeMarket(gambler); + + } + + public async Task Beg(UserDbModel user) + { + CancellationToken bToken = BegToken.Token; + IsLoanable = true; + var msg = await BotInstance.SendChatMessageAsync($"{user.FormatUsername()}({user.KfId}) is begging for a loan. {user.FormatUsername()} can be trused with ${KreditScore} KKK in krypto with a 1.5x return."); + int counter = 0; + while (!bToken.IsCancellationRequested && counter < 100) + { + await Task.Delay(TimeSpan.FromSeconds(120), bToken); + counter++; + } + + + await BotInstance.KfClient.EditMessageAsync(msg.ChatMessageUuid, + $"{user.FormatUsername()}, nobody wanted to give you a loan. !beg to continue begging for a loan."); + IsLoanable = false; + await Task.Delay(TimeSpan.FromSeconds(10)); + await BotInstance.KfClient.DeleteMessageAsync(msg.ChatMessageUuid); + + } + + public bool SponsorBonusDue() + { + if (DateTime.UtcNow - lastSponsorBonus < TimeSpan.FromDays(1)) return false; + return true; + } + + public void ProcessSponsorBonus(decimal amount) + { + Tracker.AddDeposit(amount); + } + public void ModifyBalance(decimal amount) + { + CryptoBalance += amount; + } + public async Task FormatBalanceAsync() + { + string str = OutstandingLoanBalance > 0 ? $"| Net Balance: {await (CryptoBalance-OutstandingLoanBalance).FormatKasinoCurrencyAsync()}":""; + return $"Balance: {await CryptoBalance.FormatKasinoCurrencyAsync()}{str}"; + } + public decimal[] Balance() + { + return new decimal[] {CryptoBalance, CryptoBalance - OutstandingLoanBalance}; + } + public async Task SmokeCrack() + { + CancellationToken cToken = CrackToken.Token; + if (IsCracked || IsInWithdrawal) + { + CrackToken.Cancel(); + await Task.Delay(TimeSpan.FromSeconds(5)); + } + LastSmokedCrack = DateTime.UtcNow; + IsCracked = true; + CrackTimer += TimeSpan.FromMinutes(2); + CrackCounter++; + HouseEdgeModifier += (decimal).05; + + for (int i = 0; i < CrackTimer.Seconds/5; i++) + { + await Task.Delay(TimeSpan.FromSeconds(5)); + CrackTimer -= TimeSpan.FromSeconds(5); + if (cToken.IsCancellationRequested) + { + //if you smoked more crack within that 2 minutes, add another 2 minutes of crack instead, stack the buffs and postpone the withdrawal symptoms + return; + } + } + //now you are in withdrawal + IsInWithdrawal = true; + IsCracked = false; + + HouseEdgeModifier -= (decimal)(.06 * CrackCounter); + for (int i = 0; i < CrackCounter*100; i++) + { + await Task.Delay(TimeSpan.FromSeconds(5)); + if (cToken.IsCancellationRequested) + { + //if you smoke crack while in withdraw, get the basic benefits of crack back but do not reset crackcounter + HouseEdgeModifier = BotInstance.BotServices.KasinoShop!.DefaultHouseEdgeModifier; + return; + } + } + //reset the house edge modifier and crack counter after withdrawal has passed + CrackCounter = 0; + HouseEdgeModifier = BotInstance.BotServices.KasinoShop!.DefaultHouseEdgeModifier; + IsInWithdrawal = false; + } + + public async Task SmokeWeed(TimeSpan buffLength) + { + if (buffLength > TimeSpan.FromMinutes(12)) FloorNugs++; + CancellationToken wToken = WeedToken.Token; + CancellationToken cToken = CrackToken.Token; + if (IsWeeded) + { + WeedToken.Cancel(); + await Task.Delay(TimeSpan.FromSeconds(5)); + } + IsWeeded = true; + if (HouseEdgeModifier < 0) + { + //if you're currently in crack withdrawal + HouseEdgeModifier /= 2; + } + else + { + HouseEdgeModifier += (decimal)0.01; + } + + WeedTimer += buffLength; + for (int i = 0; i < buffLength.Seconds / 5; i++) + { + await Task.Delay(TimeSpan.FromSeconds(1)); + WeedTimer -= TimeSpan.FromSeconds(1); + if (wToken.IsCancellationRequested) return; + if (cToken.IsCancellationRequested) return; + + } + await Task.Delay(buffLength); + if (HouseEdgeModifier > 0) HouseEdgeModifier -= (decimal)0.01; + IsWeeded = false; + + } + + public async Task Smash(GamblerDbModel gambler) + { + List smashableAssetIds = new(); + bool smashableAssets = false; + foreach (var key in Assets.Keys) + { + if (Assets[key] is Smashable smash) + { + smashableAssetIds.Add(key); + smashableAssets = true; + } + } + if (!smashableAssets) + { + //check if they have any physical assets in their possesion, shoes, gold, silver, house, car, small chance to damage one of those instead + List physicalAssetIds = new(); + foreach (var key in Assets.Keys) + { + if (Assets[key] is Investment inv) + { + if (inv.investment_type != InvestmentType.Stake && inv.investment_type != InvestmentType.Skin) + { + physicalAssetIds.Add(key); + } + } + else if (Assets[key] is Shoe shoe) + { + physicalAssetIds.Add(key); + } + else if (Assets[key] is Car car) + { + physicalAssetIds.Add(key); + } + } + + int num = Money.GetRandomNumber(gambler, 0, 100); + if (physicalAssetIds.Count == 0 || num < 90) + { + await BotInstance.SendChatMessageAsync($"{gambler.User.FormatUsername()}, you don't have anything to smash.", true, autoDeleteAfter: TimeSpan.FromSeconds(10)); + return; + } + + //now "smash" one of the assets + num = Money.GetRandomNumber(gambler, 0, 4); + var assetId = smashableAssetIds[num]; + if (Assets[assetId] is Shoe sh) + { + sh.Smash(); + } + else if (Assets[assetId] is Car car) + { + car.Smash(); + } + else if (Assets[assetId] is Investment inv) + { + inv.Smash(); + } + await BotInstance.SendChatMessageAsync($"{gambler.User.FormatUsername()}, you smashed {Assets[assetId]}.", true, autoDeleteAfter: TimeSpan.FromSeconds(10)); + foreach (var lKey in Loans.Keys) + { + Loans[lKey].ProcessSmash(ID, BotInstance); + } + + return; + } + foreach (var id in smashableAssetIds) + { + ((Smashable)Assets[id]).Smash(); + await BotInstance.SendChatMessageAsync($"{gambler.User.FormatUsername()}, you smashed {Assets[id]}."); + if (Assets[id].GetCurrentValue() == 0) + { + Assets.Remove(id); + await BotInstance.SendChatMessageAsync($"{gambler.User.FormatUsername()}, {Assets[id]} is worthless now so you throw it away."); + } + if (Money.GetRandomNumber(gambler, 0, 100) < 50) break; + } + + foreach (var lKey in Loans.Keys) + { + Loans[lKey].ProcessSmash(ID, BotInstance); + } + } + public void Withdraw(decimal amount) + { + CryptoBalance += amount; + Tracker.AddWithdrawal(amount); + } + + public void Deposit(decimal amount) + { + CryptoBalance -= amount; + Tracker.AddDeposit(amount); + } + + + + + + + + + + + public class StatTracker + { + public int GamblerId; + public int KfId; + public decimal totalDeposited = 0; + public decimal totalWithdrawn = 0; + public decimal totalLossBack = 0; + public Dictionary totalWageredByGame; //0 is total wagered, 1 is total paid back + + public StatTracker(int gid, int kfid) + { + GamblerId = gid; + KfId = kfid; + totalWageredByGame = new Dictionary(); + foreach (var game in Enum.GetValues()) + { + totalWageredByGame.Add(game, new decimal[] {0, 0}); + } + } + + public void AddNewGameToTracker(WagerGame game) + { + totalWageredByGame.Add(game, new decimal[] {0, 0}); + } + + public void AddWager(WagerGame game, decimal amount, decimal net) + { + if (!totalWageredByGame.ContainsKey(game)) AddNewGameToTracker(game); + totalWageredByGame[game][0] += amount; + totalWageredByGame[game][1] += net; + } + + public void AddDeposit(decimal amount) + { + totalDeposited += amount; + } + public void AddWithdrawal(decimal amount) + { + totalWithdrawn += amount; + } + + public void AddLossback(decimal amount) + { + totalLossBack += amount; + } + + public string GetRtp() + { + decimal totalWagered = 0; + decimal totalWinnings = 0; + foreach (var wagered in totalWageredByGame.Values) + { + totalWagered += wagered[0]; + totalWinnings += wagered[1]; + } + + using var db = new ApplicationDbContext(); + var gambler = db.Gamblers.FirstOrDefaultAsync(g => g.Id == GamblerId).GetAwaiter().GetResult(); + decimal RTP = (totalWagered - totalWithdrawn - gambler!.Balance) / totalWagered; + string returnVal = $"{gambler.User.FormatUsername()}[br]" + + $"Global RTP: {RTP * 100}%[br]"; + foreach (var game in totalWageredByGame.Keys) + { + returnVal += $"{game} RTP: {(totalWageredByGame[game][1] / totalWageredByGame[game][0]) * 100}%[br]"; + } + + return returnVal; + } + } + + } + public abstract class Asset + { + public decimal originalValue; + public string name; + public AssetType type; + public DateTime acquired; + public List ValueChangeReports; + public int Id; + + public abstract decimal GetCurrentValue(); + } + + public class AssetValueChangeReport + { + private decimal valueChangeAmount; + private decimal valueChangePcnt; + public DateTime time; + + public AssetValueChangeReport(decimal valueChangeAmount, decimal valueChangePcnt, DateTime time) + { + this.valueChangeAmount = valueChangeAmount; + this.valueChangePcnt = valueChangePcnt; + this.time = time; + } + + public override string ToString() + { + string symbol = valueChangeAmount > 0 ? AssetValueIncreaseIndicator[true] : AssetValueIncreaseIndicator[false]; + string color = valueChangeAmount > 0 ? AssetValueIncreaseColor[true] : AssetValueIncreaseColor[false]; + string timeString = DateTime.UtcNow - time > TimeSpan.FromDays(7) ? time.ToString("g") : time.ToString("f"); + return $"{symbol}[color={color}]${valueChangeAmount} KKK({valueChangePcnt}%)[/color] {timeString}"; + } + } + + public class Loan + { + public decimal amount; + public decimal payoutAmount; + public int payableToGambler; //gambler id + public int payableToKf;//kfid + public int recieverGambler; + public int recieverKf; + public int Id; + public string payableTo; + + public Loan(decimal amount, int payableToGambler, int payableToKf, int recieverGambler, int recieverKf, int Id, UserDbModel payableTo) + { + this.amount = amount; + payoutAmount = amount * 1.5m; + this.payableToGambler = payableToGambler; + this.payableToKf = payableToKf; + this.recieverGambler = recieverGambler; + this.recieverKf = recieverKf; + this.Id = Id; + this.payableTo = payableTo.FormatUsername(); + } + + public async Task ToStringAsync(int kfId) + { + if (kfId == payableToKf) //if the person calling the command (ex !list loans) is the one who is owed + { + await using var db = new ApplicationDbContext(); + var gambler = await db.Gamblers.FirstOrDefaultAsync(g => g.Id == payableToGambler); + return $"is owed ${await payoutAmount.FormatKasinoCurrencyAsync()} from {gambler!.User.FormatUsername()}"; + } + + return $"owes {payableTo}({payableToKf}) ${payoutAmount}KKK"; + } + + public void ProcessSmash(int smasherId, ChatBot instance) + { + if (smasherId == recieverKf) + { + if (payoutAmount > amount) + { + payoutAmount = amount; + instance.BotServices.KasinoShop!.Gambler_Profiles[payableToKf].Loans[Id].payoutAmount = amount; + } + } + } + + [Obsolete("Don't use base ToString, use await ToStringAsync(int kfId) instead", true)] + public override string ToString() + { + return "Generated incorrect string for loan. Screenshot and send to Alogindtractor2"; + } + } + + + + public class Investment : Asset //gold, silver, stake, or house + { + private decimal _currentValue; + public InvestmentType investment_type; + private DateTime _lastInterestCalculation = DateTime.UtcNow; + public decimal[] interestRange; + + [Obsolete("Dont use base constructor", true)] + protected Investment() + { + throw new Exception("Investment should not be instantiated directly. Use Gold, Silver, or House"); + } + public Investment(int id, decimal value, decimal[] range, InvestmentType type, string name) + { + originalValue = value; + _currentValue = value; + investment_type = type; + interestRange = range; + Id = id; + this.type = AssetType.Investment; + this.name = name; + ValueChangeReports = new(); + ValueChangeReports.Add(new AssetValueChangeReport(0, 0, DateTime.UtcNow)); + } + + public override decimal GetCurrentValue() + { + //apply daily interest if applicable + if (DateTime.UtcNow - _lastInterestCalculation > TimeSpan.FromDays(1)) + { + int interestIterations = (DateTime.UtcNow - acquired).Days; + for (int i = 0; i < interestIterations; i++) + { + + double range = (double)(interestRange[1] - interestRange[0]); + double random = _rand.NextDouble() * range; + random += (double)interestRange[0]; + var oldValue = _currentValue; + _currentValue *= (decimal)(1 + random); + ValueChangeReports.Add(new AssetValueChangeReport(_currentValue - oldValue, (decimal)(random), DateTime.UtcNow - TimeSpan.FromDays(i+1))); + } + } + return _currentValue; + } + public override string ToString() + { + return $"{type} {investment_type}: {name}(ID: {Id}) worth {GetCurrentValue()} {ValueChangeReports[^1]}"; + } + + public void StakePartialSale(decimal amount) + { + if (this.investment_type != InvestmentType.Stake) throw new Exception("attempted to partially sell something other than a stake"); + var oldval = _currentValue; + _currentValue -= amount; + ValueChangeReports.Add(new AssetValueChangeReport(-amount, -(_currentValue/oldval), DateTime.UtcNow)); + } + + public void Smash() + { + if (investment_type == InvestmentType.Stake || investment_type == InvestmentType.Skin) throw new Exception("attempted to smash a stake"); + var oldval = _currentValue; + _currentValue -= originalValue/2; + ValueChangeReports.Add(new AssetValueChangeReport(_currentValue - oldval, (_currentValue - oldval) / oldval, DateTime.UtcNow)); + } + } + + public class Shoe : Investment + { + public ShoeBrand brand; + public Shoe(int id, decimal value, ShoeBrand brand) : base(id, value, ShoeAprRange, InvestmentType.Shoes, $"{brand} shoes") + { + this.brand = brand; + } + } + + public class Skin : Investment + { + private decimal _currentValue; + private DateTime _lastInterestCalculation = DateTime.UtcNow; + private string _objName; + private string _tag; + private string _color; + private string _emoji; + + public Skin(int id, decimal value, string obj, string tag, string color, string emoji) : base(id, value, CsSkinAprRange, InvestmentType.Skin, $"[color={color}][b][{emoji}{tag}]{obj}[/b][/color]") + { + _objName = obj; + _tag = tag; + _color = color; + _emoji = emoji; + } + + //use same getCurrentValue as investment + + public override string ToString() + { + return $"{name} (ID: {Id}) worth ${GetCurrentValue()} KKK | {ValueChangeReports[^1]}"; + } + } + + public class Smashable : Asset // computer equipment + { + public bool isSmashed = false; + public decimal currentValue; + + public Smashable(int id, decimal value, SmashableType type) + { + Id = id; + originalValue = value; + currentValue = value; + this.type = AssetType.Smashable; + this.name = $"{type}"; + acquired = DateTime.UtcNow; + ValueChangeReports = new(); + } + + public override decimal GetCurrentValue() + { + return currentValue; + } + + public void Smash() + { + isSmashed = true; + currentValue -= originalValue / 2; + name = $"Smashed {name}"; + ValueChangeReports.Add(new AssetValueChangeReport(-originalValue/2, -.5m, DateTime.UtcNow)); + } + + public override string ToString() + { + return $"{name}(ID: {Id}) worth {currentValue} {ValueChangeReports[^1]}"; + } + } + + + public class Car : Asset + { + private decimal _currentValue; + public new AssetType type = AssetType.Car; + public Cars car_type; + public decimal job_value; + + [Obsolete("Dont use base constructor", true)] + public Car() + { + throw new Exception("Car should not be instantiated directly. Use Car(int id, string name, decimal value, Cars car_type, decimal job_value)"); + } + public Car(Cars type) + { + acquired = DateTime.UtcNow; + car_type = type; + _currentValue = CarPrices[type]; + originalValue = _currentValue; + job_value = CarPrices[type] / 10; + name = type.ToString(); + ValueChangeReports = new(); + } + + public override decimal GetCurrentValue() + { + return _currentValue; + } + + public void SetId(GamblerDbModel gambler) //sets the id of the car to a unique number when you buy it so you can interact with it later + { + Id = GenerateRandomId(gambler); + } + + public async Task ProcessWorkJob(GamblerDbModel gambler) + { + decimal oldVal = _currentValue; + _currentValue -= job_value / 5; + ValueChangeReports.Add(new AssetValueChangeReport(-job_value / 5, (_currentValue - oldVal) / oldVal, DateTime.UtcNow)); + BotInstance.BotServices.KasinoShop!.Gambler_Profiles[gambler.User.KfId].ModifyBalance(job_value); + if (_currentValue <= 0) // if your car dies it gets removed from your asset list + { + BotInstance.BotServices.KasinoShop!.Gambler_Profiles[gambler.User.KfId].Assets.Remove(Id); + await BotInstance.SendChatMessageAsync( + $"{gambler.User.FormatUsername()} totalled their car on the way home from work.", true, + autoDeleteAfter: TimeSpan.FromSeconds(10)); + } + + } + + public void Smash() //car can be damaged as a result of smashing but not destroyed + { + var oldval = _currentValue; + _currentValue -= _currentValue / 4; + ValueChangeReports.Add(new AssetValueChangeReport(_currentValue - oldval, (_currentValue - oldval) / oldval, DateTime.UtcNow)); + } + + public override string ToString() + { + return $"{type} {name} worth ${GetCurrentValue()} KKK"; + } + + } + + + //KasinoShop.[]Market - used to generate a list of items for you to buy when you interact with the shop. takes your shop profiles current state into account + //for example if you have a car, you can take meth queen home with you and buy meth + //markets get new options every day except drug market which stays the same outside of prices + + + public class ShoeMarket + { + private List _shoes = new(); + private DateTime _opened = DateTime.UtcNow; + + public ShoeMarket(GamblerDbModel gambler) + { + var shoePrices = KasinoShop.ShoePrices(gambler); + foreach (var shoeType in shoePrices.Keys) + { + _shoes.Add(new Shoe(GenerateRandomId(gambler), shoePrices[shoeType], shoeType)); + } + } + + public bool Old() + { + if (DateTime.UtcNow - _opened > TimeSpan.FromDays(1)) return true; + return false; + } + + public List GetShoes(GamblerDbModel gambler) + { + if (Old()) + { + var shoePrices = KasinoShop.ShoePrices(gambler); + _shoes.Clear(); + foreach (var shoeType in shoePrices.Keys) + { + _shoes.Add(new Shoe(GenerateRandomId(gambler), shoePrices[shoeType], shoeType)); + } + } + + return _shoes; + } + + } + + public class SkinMarket + { + private List _skins = new(); + private DateTime _opened = DateTime.UtcNow; + + public SkinMarket(GamblerDbModel gambler) + { + for (int i = 0; i < 5; i++) + { + _skins.Add(GenerateRandomSkin(gambler)); + } + } + private bool Old() + { + return DateTime.UtcNow - _opened > TimeSpan.FromDays(1); + } + + public List GetSkins(GamblerDbModel gambler) + { + if (Old()) + { + _skins.Clear(); + for (int i = 0; i < 5; i++) + { + _skins.Add(GenerateRandomSkin(gambler)); + } + } + + return _skins; + } + + public void SellsSkinTo(GamblerDbModel gambler, int skindex) + { + _skins.RemoveAt(skindex); + _skins.Add(GenerateRandomSkin(gambler)); + } + } + + + public static readonly decimal CrackPrice = 10000m; + public static readonly decimal WeedPricePerHour = 1000m; + public static readonly TimeSpan WeedNugLength = TimeSpan.FromMinutes(6); + public static readonly decimal CsSkinMinBaseValue = 1000; + public static readonly decimal[] ShoeAprRange = { -0.05m, 0.05m }; + public static readonly decimal[] CsSkinAprRange = { -0.25m, 0.25m }; + public static decimal HomeApr = 0.1m; + + public static Skin GenerateRandomSkin(GamblerDbModel gambler) + { + int obj = Money.GetRandomNumber(gambler, 0, CsSkinObjects.Count - 1); + int tg = Money.GetRandomNumber(gambler, 0, CsSkinTags.Count - 1); + int emo = Money.GetRandomNumber(gambler, 0, CsSkinEmotes.Count - 1); + string color = GetRandomColor(gambler); + int id = GenerateRandomId(gambler); + decimal val = CsSkinTags.ElementAt(tg).Value + CsSkinEmotes.ElementAt(emo).Value; + if (val < CsSkinMinBaseValue) val = CsSkinMinBaseValue; + return new Skin(id, val, CsSkinObjects[obj], CsSkinTags.ElementAt(tg).Key, color, CsSkinEmotes.ElementAt(emo).Key); + } + + + public static int GenerateRandomId(GamblerDbModel gambler) + { + var profile = BotInstance.BotServices.KasinoShop!.Gambler_Profiles[gambler.User.KfId]; + int counter = 0; + int id = Money.GetRandomNumber(gambler, 0, 999999999); + if (profile.Assets.ContainsKey(id)) + { + while (profile.Assets.ContainsKey(id)) + { + id = Money.GetRandomNumber(gambler, 0, 999999999); + counter++; + if (counter > 10000) + { + throw new Exception("failed to generate unique skin ID after 10000 attempts"); + } + } + } + + return id; + } + + public static readonly Dictionary DefaultCars = new() + { + {Cars.Civic, new Car(Cars.Civic)}, + {Cars.Audi, new Car(Cars.Audi)}, + {Cars.Bentley, new Car(Cars.Bentley)}, + {Cars.Bmw, new Car(Cars.Bmw)} + }; + public static readonly Dictionary CarPrices = new() + { + { Cars.Civic , 2_000_000 }, + { Cars.Audi, 4_000_000 }, + { Cars.Bentley , 6_000_000 }, + { Cars.Bmw , 8_000_000 }, + }; + + public static readonly List CsSkinObjects = new() + { + "P2000", + "USP-S", + "Glock-18", + "Dual Berettas", + "P250", + "Tec-9", + "Five-SeveN", + "CZ75-Auto", + "R8 Revolver", + "Desert Eagle", + "Mac-10", + "UMP-45", + "MP5 SD", + "MP7", + "MP9", + "P90", + "PP-Bizon", + "Nova", + "Sawed-Off", + "Mag-7", + "XM1014", + "SSG 08", + "Galil AR", + "FAMAS", + "AK-47", + "M4A1-S", + "SG 553", + "M4A4", + "AUG", + "AWP", + "G3SG1", + "SCAR-20", + "Bayonet", + "Bowie Knife", + "Butterfly Knife", + "Falchion Knife", + "Flip Knife", + "Gut Knife", + "Huntsman Knife", + "Karambit", + "M9 Bayonet", + "Navaja Knife", + "Nomad Knife", + "Paracord Knife", + "Shadow Daggers", + "Skeleton Knife", + "Stiletto Knife", + "Survival Knife", + "Talon Knife", + "Ursus Knife", + "Kukri Knife", + "Sport Gloves", + "Specialist Gloves", + "Driver Gloves", + "Hand Wraps", + "Moto Gloves", + "Bloodhound Gloves", + "Hydra Gloves", + "Broken Fang Gloves" + }; + + public static readonly Dictionary CsSkinTags = new() + { + {"SNEED", 10000000}, + {"R", 9000000}, + {"RIGGED", 8000000}, + {"GREEDY", 100000}, + {"DEWISH", 6000000}, + {"JEWISH", 6000000}, + {"SCAMMER", 7000000}, + {"SCAM", 7777777}, + {"5", 5555555}, + {"9", 9999999}, + {"OSRS", 1000}, + {"666", 6666666}, + {"CRACK", 5000000}, + {"WEED", 4200000}, + {"EEEEEEEEEE", 3333333}, + {"COFFEE", 3000000}, + {"FATGO", 2000000}, + {"YO", 75000}, + {"ELF", 60000}, + {"OMFG", 20000}, + {"IHML", 7000}, + {"FUCKIN DEWD", 60000}, + {"MILF", 40000}, + {"KKK", 1000000}, + {"KASINO", 1000000}, + {"METH", 4000000}, + {"GOOBR", 2000000}, + {"TRAPPER", 500000}, + {"TRAPPERTURD", 100000}, + {"CHRISTMAS", 10000}, + {"MR.CHRISTMAS", 10000}, + {"DEPAKOTE", 1000}, + {"RAT", 50000}, + {"MATI", 20000}, + {"GERMAN RAP", 15000}, + {"EVIL EDDIE", 10000}, + {"NASTY NOAH", 5000}, + {"BOSSMAN", 25000}, + {"RATDAD", 80000}, + {"PICKLETIME", -1000000m}, + + }; + + + public static string GetRandomColor(GamblerDbModel gambler) + { + int r = Money.GetRandomNumber(gambler, 0, 255); + int g = Money.GetRandomNumber(gambler, 0, 255); + int b = Money.GetRandomNumber(gambler, 0, 255); + return $"{r:X2}{g:X2}{b:X2}"; + } + + public static readonly Dictionary CsSkinEmotes = new() + { + {"🀣", 15000}, + {"πŸ˜‚", 14000}, + {"πŸ™‚", 13000}, + {"😝", 12000}, + {"🀨", 11000}, + {"πŸ™„", 10000}, + {"πŸ€₯", 20000}, + {"🀧", 30000}, + {"🀯", 8000000}, + {"😭", 30000}, + {"😀", 40000}, + {"πŸ’©", 60000}, + {"πŸ’’", 700000}, + {"πŸ’₯", 600000}, + {"πŸ’€", 10000}, + {"🫡", 70000}, + {"πŸ™", 50000}, + {"πŸŽ…", 10000}, + {"πŸ€", 10000000}, + {"🐹", 220000}, + {"🦨", 110000}, + {"πŸ¦β€πŸ”₯", 330000}, + {"🐍", 88000}, + {"πŸ‰", 77000}, + {"🦞", 66000}, + {"☘", 300000}, + {"πŸ€", 1000000}, + {"πŸ₯©", 400000}, + {"πŸš”", 500000}, + {"πŸš—", 50000}, + {"✈", 1000000}, + {":winner:", 4000000}, + {":juice:", 2000000}, + {":ross:", 3000000}, + {"β™ ", 75000}, + {"β™₯", 75000}, + {"♦", 75000}, + {"♣", 75000}, + {"πŸ’Ž", 150000}, + {"πŸͺ«", 85000}, + {"πŸͺ™", 45000}, + {"πŸ’΅", 10000}, + {"🧲", 25000}, + {"πŸ’Š", 100000}, + {"🚬", 650000}, + {"πŸͺͺ", 750000}, + {"🚭", 820000}, + {"✑", 6666666}, + {"❓", 250000}, + {"πŸ’²", 500000}, + {"πŸ†—", 360000}, + {"🀑", 1000000}, + {"πŸ‘…", 9999999}, + {"πŸ‘΄", 105000}, + {"πŸ›Ή", 90000}, + {"πŸ₯’", -1000000m}, + {"πŸŽ„", 42069}, + {"πŸ•Ή", 12000}, + {"🎰", 7777777}, + + }; + + + + public static readonly Dictionary AssetValueIncreaseIndicator = new() { {false, "πŸ”»"}, {true, "πŸ”Ί"} }; + public static readonly Dictionary AssetValueIncreaseColor = new() { {false, "red"}, {true, "lightgreen"} }; + + public static Dictionary ShoePrices(GamblerDbModel gambler) + { + + return new Dictionary + { + { ShoeBrand.Yeezy , Money.GetRandomNumber(gambler, 6_000, 100_000) }, + { ShoeBrand.Adidas , Money.GetRandomNumber(gambler, 1_800, 50_000) }, + { ShoeBrand.Jordan , Money.GetRandomNumber(gambler, 9_000, 380_000) }, + }; + } + + private static List SmashCarousel = new() + { + "https://i.ddos.lgbt/u/KhMr9v.webp", + "https://i.ddos.lgbt/u/KYaOqH.webp", + "https://i.ddos.lgbt/u/w3wAyB.webp", + "https://i.ddos.lgbt/u/c4znnv.webp", + "https://i.ddos.lgbt/u/qGHbNp.webp", + "https://i.ddos.lgbt/u/65lz4m.webp", + "https://i.ddos.lgbt/u/ZCDWeO.webp", + "https://i.ddos.lgbt/u/2025-12-12_19:17:16.gif", + "https://i.ddos.lgbt/u/oBXBV4.webp", + "https://i.ddos.lgbt/u/2025-12-12_19:08:15.gif", + "https://i.ddos.lgbt/u/fuxIHW.webp", + "https://i.ddos.lgbt/u/0dtwl3.webp", + + }; + + public static readonly decimal GoldBasePriceOz = 300000; + public static readonly decimal[] GoldInterestRange = new decimal[] { 0.01m, 0.05m }; + public static readonly decimal SilverBasePriceOz = 10000; + public static readonly decimal[] SilverInterestRange = new decimal[] { 0.01m, 0.05m }; + public static readonly decimal BaseHousePrice = 100000000; + public static readonly decimal[] HouseInterestRange = new decimal[] { 0.01m, 0.15m }; + public static readonly decimal[] CryptoStakeInterestRange = new decimal[] { -0.01m, 0.05m }; + + public static String GetRandomSmashImage(GamblerDbModel gambler) + { + int rand = Money.GetRandomNumber(gambler, 0, SmashCarousel.Count - 1); + return SmashCarousel[rand]; + } + + public decimal KASINO_SPONSOR_BONUS = 1000; +} + + + + + +public enum SmashableType +{ + Headphones, + Keyboard, + Mouse +} +public enum InvestmentType +{ + Shoes, + Stake, + Gold, + Silver, + Skin, + House, + Random +} + +public enum AssetType +{ + Investment, + Smashable, + Car, + Random +} + +public enum ShoeBrand +{ + Yeezy, + Adidas, + Jordan, +} + +public enum Cars +{ + Civic, + Bentley, + Audi, + Bmw +} + +public enum Rigging +{ + Switch, + Button, + Lever, + Panel, + Keypad, + Dial, + Electromagnet +} + + +