Files
KfChatDotNet/KfChatDotNetBot/Commands/Kasino/RainCommand.cs
barelyprofessional b4796bbef6 Rain minimum
2026-03-11 20:35:35 -05:00

163 lines
7.3 KiB
C#

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;
[KasinoCommand]
[WagerCommand]
public class RainCommand : ICommand
{
public List<Regex> Patterns => [
new Regex(@"^rain (?<amount>\d+(?:\.\d+)?)$", RegexOptions.IgnoreCase),
new Regex(@"^rain", RegexOptions.IgnoreCase)
];
public string? HelpText => "!rain <amount> to start a rain, !rain to join all active rains";
public UserRight RequiredRight => UserRight.Loser;
public TimeSpan Timeout => TimeSpan.FromSeconds(90);
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.KasinoGameDisabledMessageCleanupDelay, BuiltIn.Keys.KasinoRainCountdownDuration,
BuiltIn.Keys.KasinoRainEnabled
]);
// Check if rain is enabled
var rainEnabled = (settings[BuiltIn.Keys.KasinoRainEnabled]).ToBoolean();
if (!rainEnabled)
{
var gameDisabledCleanupDelay= TimeSpan.FromMilliseconds(settings[BuiltIn.Keys.KasinoGameDisabledMessageCleanupDelay].ToType<int>());
await botInstance.SendChatMessageAsync(
$"{user.FormatUsername()}, rain is currently disabled.",
true, autoDeleteAfter: gameDisabledCleanupDelay);
return;
}
var cleanupDelay = TimeSpan.FromSeconds(30);
if (botInstance.BotServices.KasinoRain == null || !botInstance.BotServices.KasinoRain.IsInitialized())
{
await botInstance.SendChatMessageAsync($"{user.FormatUsername()}, rain is not available at this time", true,
autoDeleteAfter: cleanupDelay);
return;
}
var rain = await botInstance.BotServices.KasinoRain.GetRainState();
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 (!arguments.TryGetValue("amount", out var amount)) //if you're trying to join a rain
{
if (rain == null) //if there are no lobbies
{
await botInstance.SendChatMessageAsync(
$"{user.FormatUsername()}, there's no rain currently running. !rain <amount> to start a new rain",
true, autoDeleteAfter: cleanupDelay);
return;
}
if (rain.Participants.Contains(user.Id))
{
await botInstance.SendChatMessageAsync(
$"{user.FormatUsername()}, you're already participating in this rain!", true,
autoDeleteAfter: cleanupDelay);
return;
}
if (rain.Creator == user.Id)
{
await botInstance.SendChatMessageAsync(
$"{user.FormatUsername()}, you can't participate in your own rain!", true,
autoDeleteAfter: cleanupDelay);
return;
}
if ((DateTimeOffset.UtcNow - gambler.Created).TotalHours < 4)
{
await botInstance.SendChatMessageAsync($"{user.FormatUsername()}, you're too fresh for a rain", true, autoDeleteAfter: cleanupDelay);
return;
}
await botInstance.BotServices.KasinoRain.AddParticipant(user.Id);
var pluralSuffix = string.Empty;
if (rain.Participants.Count > 0) pluralSuffix = "s";
await botInstance.SendChatMessageAsync(
$"LFG {user.FormatUsername()} is now a participant! There's now {rain.Participants.Count + 1} participant{pluralSuffix}! Type [ditto]!rain[/ditto] to participate",
true, autoDeleteAfter: cleanupDelay);
return;
}
//if you're trying to start the rain
if (rain != null)
{
await botInstance.SendChatMessageAsync($"{user.FormatUsername()}, there's already a rain in progress.",
true, autoDeleteAfter: cleanupDelay);
return;
}
decimal decAmount = Convert.ToDecimal(amount.Value);
if (decAmount <= 0)
{
await botInstance.SendChatMessageAsync($"{user.FormatUsername()}, you can't make it rain with nothing.",
true, autoDeleteAfter: cleanupDelay);
return;
}
if (gambler.Balance < decAmount)
{
await botInstance.SendChatMessageAsync(
$"{user.FormatUsername()}, your balance {await gambler.Balance.FormatKasinoCurrencyAsync()} is not enough to make it rain for {await decAmount.FormatKasinoCurrencyAsync()}.",
true, autoDeleteAfter: cleanupDelay);
return;
}
var rainMin = (await SettingsProvider.GetValueAsync(BuiltIn.Keys.KasinoRainMinimum)).ToType<decimal>();
if (decAmount < rainMin)
{
await botInstance.SendChatMessageAsync($"{user.FormatUsername()}, rain at least {await rainMin.FormatKasinoCurrencyAsync()}", true,
autoDeleteAfter: cleanupDelay);
return;
}
rain = new KasinoRainModel
{
Participants = [],
Creator = user.Id,
Started = DateTimeOffset.UtcNow,
RainAmount = decAmount,
PayoutWhen = DateTimeOffset.MaxValue
};
var rainCountdown = settings[BuiltIn.Keys.KasinoRainCountdownDuration].ToType<int>();
var timer = rainCountdown;
var msg = await botInstance.SendChatMessageAsync(
$"🌧️🌧️ {user.FormatUsername()} is making it rain with {await decAmount.FormatKasinoCurrencyAsync()}! Type [ditto]!rain[/ditto] in the next {timer} seconds to join.",
true);
var result = await botInstance.WaitForChatMessageAsync(msg, ct: ctx);
if (!result)
{
throw new InvalidOperationException("Failed to send chat message for the rain. Not going to proceed with it");
}
// Wait to set a real payout deadline only when chyat echoes the message out of fairness
// (and also so the timer doesn't overlap with the payout deadline)
rain.PayoutWhen = DateTimeOffset.UtcNow.AddSeconds(rainCountdown);
await botInstance.BotServices.KasinoRain.SaveRainState(rain);
while (timer > 0)
{
timer--;
await Task.Delay(1000, ctx);
await botInstance.KfClient.EditMessageAsync(msg.ChatMessageUuid!,
$"🌧️🌧️ {user.FormatUsername()} is making it rain with {await decAmount.FormatKasinoCurrencyAsync()}! Type [ditto]!rain[/ditto] in the next {timer} seconds to join.");
}
await Task.Delay(100, ctx);
await botInstance.KfClient.DeleteMessageAsync(msg.ChatMessageUuid!);
// At this point the timer should take care of things but truthfully it's a disaster and probably won't work
}
}