diff --git a/KfChatDotNetBot/Commands/Kasino/KasinoUserCommands.cs b/KfChatDotNetBot/Commands/Kasino/KasinoUserCommands.cs index e0e6eb4..67efbc5 100644 --- a/KfChatDotNetBot/Commands/Kasino/KasinoUserCommands.cs +++ b/KfChatDotNetBot/Commands/Kasino/KasinoUserCommands.cs @@ -387,3 +387,51 @@ public class HostessCommand : ICommand await botInstance.SendChatMessageAsync(llmResponse, true, ChatBot.LengthLimitBehavior.TruncateExactly); } } + +[KasinoCommand] +public class GetDailyDollarCommand : ICommand +{ + public List Patterns => [ + new Regex("^daily", RegexOptions.IgnoreCase), + ]; + public string? HelpText => "Get your daily dollah"; + public UserRight RequiredRight => UserRight.Loser; + public TimeSpan Timeout => TimeSpan.FromSeconds(10); + public RateLimitOptionsModel? RateLimitOptions => null; + public async Task RunCommand(ChatBot botInstance, MessageModel message, UserDbModel user, GroupCollection arguments, + CancellationToken ctx) + { + var settings = await SettingsProvider.GetMultipleValuesAsync([ + BuiltIn.Keys.KasinoDailyDollarEnabled, BuiltIn.Keys.KasinoDailyDollarAmount + ]); + if (!settings[BuiltIn.Keys.KasinoDailyDollarEnabled].ToBoolean()) + { + await botInstance.SendChatMessageAsync($"{user.FormatUsername()}, daily dollar has been disabled :(", true, + autoDeleteAfter: TimeSpan.FromSeconds(15)); + return; + } + var gambler = await Money.GetGamblerEntityAsync(user.Id, ct: ctx); + await using var db = new ApplicationDbContext(); + var mostRecentTxn = await db.Transactions.OrderBy(x => x.Id).LastOrDefaultAsync(x => + x.Gambler == gambler && x.EventSource == TransactionSourceEventType.DailyDollar, cancellationToken: ctx); + if (mostRecentTxn != null) + { + var rolloverTime = await Money.GetKasinoDate(); + // It's really more a question of whether the most recent txn was in the same game day + if (mostRecentTxn.Time >= rolloverTime) + { + var span = rolloverTime.AddDays(1) - DateTimeOffset.UtcNow; + await botInstance.SendChatMessageAsync( + $"{user.FormatUsername()}, your next daily dollar will be available in {span.Humanize(maxUnit: TimeUnit.Hour, minUnit: TimeUnit.Second)}", + true, autoDeleteAfter: TimeSpan.FromSeconds(15)); + return; + } + } + + var amount = settings[BuiltIn.Keys.KasinoDailyDollarAmount].ToType(); + await Money.ModifyBalanceAsync(gambler!.Id, amount, TransactionSourceEventType.DailyDollar, + "Daily dollar redemption", ct: ctx); + 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/Models/DbModels/MoneyDbModels.cs b/KfChatDotNetBot/Models/DbModels/MoneyDbModels.cs index 7f870e4..16a663e 100644 --- a/KfChatDotNetBot/Models/DbModels/MoneyDbModels.cs +++ b/KfChatDotNetBot/Models/DbModels/MoneyDbModels.cs @@ -270,7 +270,11 @@ public enum TransactionSourceEventType /// to figure out what the next lossback should be. (Basically return a small % of the player's losses /// unless the player's actual position is positive during the period, then tell them to fuck off) /// - Lossback + Lossback, + /// + /// A specific form of 24 hour time-based reload that has no wager requirement + /// + DailyDollar } public enum WagerGame diff --git a/KfChatDotNetBot/Services/Money.cs b/KfChatDotNetBot/Services/Money.cs index 7d942f0..643e98a 100644 --- a/KfChatDotNetBot/Services/Money.cs +++ b/KfChatDotNetBot/Services/Money.cs @@ -531,4 +531,22 @@ public static class Money { return Convert.ToHexString(Guid.NewGuid().ToByteArray()[..4]).ToLower(); } + + /// + /// Get the current Kasino day based on the configured timezone offset + /// + /// Kasino day at midnight + /// Thrown if Kasino.Timezone is null or empty + public static async Task GetKasinoDate() + { + var tz = await SettingsProvider.GetValueAsync(BuiltIn.Keys.KasinoTimezone); + if (string.IsNullOrEmpty(tz.Value)) + { + throw new InvalidOperationException(); + } + + var systemTz = TimeZoneInfo.FindSystemTimeZoneById(tz.Value); + var now = TimeZoneInfo.ConvertTimeFromUtc(DateTime.UtcNow, systemTz); + return new DateTimeOffset(now.Date, systemTz.BaseUtcOffset); + } } \ No newline at end of file diff --git a/KfChatDotNetBot/Settings/BuiltIn.cs b/KfChatDotNetBot/Settings/BuiltIn.cs index f48447f..b5b1c22 100644 --- a/KfChatDotNetBot/Settings/BuiltIn.cs +++ b/KfChatDotNetBot/Settings/BuiltIn.cs @@ -465,6 +465,12 @@ public static class BuiltIn public static string ZiplineUrl = "Zipline.Url"; [BuiltInSetting("Delay in milliseconds before cleaning up blackjack", SettingValueType.Text, "20000", WholeNumberRegex)] public static string KasinoBlackjackCleanupDelay = "Kasino.Blackjack.CleanupDelay"; + [BuiltInSetting("Amount for the daily dollar to pay out", SettingValueType.Text, "100", WholeNumberRegex)] + public static string KasinoDailyDollarAmount = "Kasino.DailyDollar.Amount"; + [BuiltInSetting("Whether daily dollars can be redeemed", SettingValueType.Boolean, "true", BooleanRegex)] + public static string KasinoDailyDollarEnabled = "Kasino.DailyDollar.Enabled"; + [BuiltInSetting("Timezone to use for daily reloads etc.", SettingValueType.Text, "Eastern Standard Time")] + public static string KasinoTimezone = "Kasino.Timezone"; } }