mirror of
https://github.com/barelyprofessional/KfChatDotNet.git
synced 2026-05-02 04:22:04 -04:00
Merge remote-tracking branch 'origin/master'
This commit is contained in:
@@ -1,9 +1,6 @@
|
||||
using System.Text.RegularExpressions;
|
||||
using Humanizer;
|
||||
using Humanizer.Localisation;
|
||||
using KfChatDotNetBot.Models;
|
||||
using KfChatDotNetBot.Models.DbModels;
|
||||
using KfChatDotNetBot.Settings;
|
||||
using KfChatDotNetWsClient.Models.Events;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
|
||||
367
KfChatDotNetBot/Commands/Kasino/KasinoEventCommands.cs
Normal file
367
KfChatDotNetBot/Commands/Kasino/KasinoEventCommands.cs
Normal file
@@ -0,0 +1,367 @@
|
||||
using System.Text.RegularExpressions;
|
||||
using Humanizer;
|
||||
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 KasinoNewEventCommand : ICommand
|
||||
{
|
||||
public List<Regex> Patterns =>
|
||||
[
|
||||
new Regex("^kasino event new$", RegexOptions.IgnoreCase),
|
||||
new Regex(@"^kasino event new (?<type>\w+)$", RegexOptions.IgnoreCase),
|
||||
new Regex(@"^kasino event new (?<type>\w+) (?<description>.+)$", RegexOptions.IgnoreCase),
|
||||
];
|
||||
|
||||
public string? HelpText => "Create a new kasino event";
|
||||
public UserRight RequiredRight => UserRight.TrueAndHonest;
|
||||
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.KasinoEventWeightWinAgainstSelectionTime, BuiltIn.Keys.KasinoEventData,
|
||||
BuiltIn.Keys.KasinoEventTextLengthLimit
|
||||
]);
|
||||
if (!arguments.TryGetValue("type", out var type))
|
||||
{
|
||||
await botInstance.SendChatMessageAsync(
|
||||
$"{user.FormatUsername()}, not enough arguments. !kasino event new <win-lose, time-prediction> <description>",
|
||||
true, autoDeleteAfter: TimeSpan.FromSeconds(60));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!arguments.TryGetValue("description", out var description))
|
||||
{
|
||||
await botInstance.SendChatMessageAsync(
|
||||
$"{user.FormatUsername()}, not enough arguments. !kasino event new <win-lose, time-prediction> <description>",
|
||||
true, autoDeleteAfter: TimeSpan.FromSeconds(60));
|
||||
return;
|
||||
}
|
||||
|
||||
if (description.Length > settings[BuiltIn.Keys.KasinoEventTextLengthLimit].ToType<int>())
|
||||
{
|
||||
await botInstance.SendChatMessageAsync($"{user.FormatUsername()}, your event description / text with a length of " +
|
||||
$"{description.Length} characters exceeds the limit of " +
|
||||
$"{settings[BuiltIn.Keys.KasinoEventTextLengthLimit].ToType<int>()} " +
|
||||
$"characters", true);
|
||||
return;
|
||||
}
|
||||
|
||||
var useTimeWeightedPayout = settings[BuiltIn.Keys.KasinoEventWeightWinAgainstSelectionTime].ToBoolean();
|
||||
KasinoEventType eventType;
|
||||
var guide = string.Empty;
|
||||
if (type.Value.Equals("win-lose", StringComparison.CurrentCultureIgnoreCase))
|
||||
{
|
||||
eventType = KasinoEventType.WinLose;
|
||||
guide = "Add option: !kasino event {EventId} options add|new <text>[br]" +
|
||||
"Remove option: !kasino event {EventId} options del|remove <option id>";
|
||||
}
|
||||
else if (type.Value.Equals("time-prediction", StringComparison.CurrentCultureIgnoreCase))
|
||||
{
|
||||
eventType = KasinoEventType.Prediction;
|
||||
}
|
||||
else
|
||||
{
|
||||
await botInstance.SendChatMessageAsync(
|
||||
$"{user.FormatUsername()}, unknown event type given. Options are win-lose or time-prediction",
|
||||
true, autoDeleteAfter: TimeSpan.FromSeconds(60));
|
||||
return;
|
||||
}
|
||||
|
||||
var eventData = settings[BuiltIn.Keys.KasinoEventData].JsonDeserialize<List<KasinoEventModel>>() ?? [];
|
||||
var newEvent = new KasinoEventModel
|
||||
{
|
||||
EventText = description.Value,
|
||||
EventId = Money.GenerateEventId(),
|
||||
Options = [],
|
||||
EventType = eventType,
|
||||
EventAnnouncementReceived = null,
|
||||
EventState = KasinoEventState.Incomplete,
|
||||
SelectionTimeWeightedPayout = useTimeWeightedPayout
|
||||
};
|
||||
eventData.Add(newEvent);
|
||||
await SettingsProvider.SetValueAsJsonObjectAsync(BuiltIn.Keys.KasinoEventData, eventData);
|
||||
await botInstance.SendChatMessageAsync(
|
||||
$"{user.FormatUsername()}, new incomplete kasino event created. Event ID is {newEvent.EventId}.[br]" +
|
||||
guide.Replace("{EventId}", newEvent.EventId) +
|
||||
$"Start the event: !kasino event {newEvent.EventId} start[br]" +
|
||||
$"Abandon the event: !kasino event {newEvent.EventId} abandon[br]" +
|
||||
$"Get event info: !kasino event {newEvent.EventId} info", true);
|
||||
}
|
||||
}
|
||||
|
||||
public class KasinoEventStart : ICommand
|
||||
{
|
||||
public List<Regex> Patterns =>
|
||||
[
|
||||
new Regex(@"^kasino event (?<event_id>\w+) start$", RegexOptions.IgnoreCase),
|
||||
];
|
||||
|
||||
public string? HelpText => "Start a Kasino event";
|
||||
public UserRight RequiredRight => UserRight.TrueAndHonest;
|
||||
public TimeSpan Timeout => TimeSpan.FromSeconds(300);
|
||||
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.KasinoEventData
|
||||
]);
|
||||
var eventId = arguments["event_id"].Value;
|
||||
var eventList = settings[BuiltIn.Keys.KasinoEventData].JsonDeserialize<List<KasinoEventModel>>() ?? [];
|
||||
var targetEvent = eventList.FirstOrDefault(x => x.EventId == eventId);
|
||||
if (targetEvent == null)
|
||||
{
|
||||
await botInstance.SendChatMessageAsync($"{user.FormatUsername()}, this event does not exist", true);
|
||||
return;
|
||||
}
|
||||
|
||||
if (targetEvent.EventState != KasinoEventState.Incomplete)
|
||||
{
|
||||
await botInstance.SendChatMessageAsync(
|
||||
$"{user.FormatUsername()}, the event is in state '{targetEvent.EventState.Humanize()}'. Only incomplete events can be started",
|
||||
true);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
var guide = $"Submit your prediction: !predict {targetEvent.EventId} <wager> 1h30m15s";
|
||||
if (targetEvent.EventType == KasinoEventType.WinLose)
|
||||
{
|
||||
if (targetEvent.Options.Count == 0)
|
||||
{
|
||||
await botInstance.SendChatMessageAsync(
|
||||
$"{user.FormatUsername()}, you can't start a win-lose event with no options.", true);
|
||||
return;
|
||||
}
|
||||
|
||||
guide = targetEvent.Options.Aggregate(string.Empty,
|
||||
(current, option) => current + $"{option.OptionId}: {option.OptionText}[br]");
|
||||
guide += "Submit your choice: !";
|
||||
}
|
||||
targetEvent.EventState = KasinoEventState.PendingAnnouncement;
|
||||
await SettingsProvider.SetValueAsJsonObjectAsync(BuiltIn.Keys.KasinoEventData, eventList);
|
||||
var msg = $":!: :!: A Keno Kasino event has started! :!: :!:[br]{targetEvent.EventText}[br]{guide}";
|
||||
var msgIds = await botInstance.SendChatMessagesAsync(msg.FancySplitMessage(partSeparator: "[br]"), true);
|
||||
var i = 0;
|
||||
while (msgIds[0].ChatMessageId != null)
|
||||
{
|
||||
i++;
|
||||
await Task.Delay(TimeSpan.FromMilliseconds(100), ctx);
|
||||
if (i > 3000) return;
|
||||
}
|
||||
|
||||
targetEvent.EventState = KasinoEventState.Started;
|
||||
targetEvent.EventAnnouncementReceived = msgIds[0].SentAt;
|
||||
await SettingsProvider.SetValueAsJsonObjectAsync(BuiltIn.Keys.KasinoEventData, eventList);
|
||||
}
|
||||
}
|
||||
|
||||
public class KasinoNewEventOption : ICommand
|
||||
{
|
||||
public List<Regex> Patterns =>
|
||||
[
|
||||
new Regex(@"^kasino event (?<event_id>\w+) options add (?<option_text>\.+)$", RegexOptions.IgnoreCase),
|
||||
new Regex(@"^kasino event (?<event_id>\w+) options new (?<option_text>\.+)$", RegexOptions.IgnoreCase),
|
||||
];
|
||||
|
||||
public string? HelpText => "Add an option to a Kasino event";
|
||||
public UserRight RequiredRight => UserRight.TrueAndHonest;
|
||||
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.KasinoEventData, BuiltIn.Keys.KasinoEventOptionTextLengthLimit
|
||||
]);
|
||||
var eventId = arguments["event_id"].Value;
|
||||
var eventList = settings[BuiltIn.Keys.KasinoEventData].JsonDeserialize<List<KasinoEventModel>>() ?? [];
|
||||
var targetEvent = eventList.FirstOrDefault(x => x.EventId == eventId);
|
||||
if (targetEvent == null)
|
||||
{
|
||||
await botInstance.SendChatMessageAsync($"{user.FormatUsername()}, this event does not exist", true);
|
||||
return;
|
||||
}
|
||||
|
||||
if (targetEvent.EventState != KasinoEventState.Incomplete)
|
||||
{
|
||||
await botInstance.SendChatMessageAsync(
|
||||
$"{user.FormatUsername()}, the event is in state '{targetEvent.EventState.Humanize()}'. Only incomplete events can have their options modified",
|
||||
true);
|
||||
return;
|
||||
}
|
||||
|
||||
if (targetEvent.EventType == KasinoEventType.Prediction)
|
||||
{
|
||||
await botInstance.SendChatMessageAsync(
|
||||
$"{user.FormatUsername()}, events based around time predictions can't have options", true);
|
||||
return;
|
||||
}
|
||||
|
||||
var optionText = arguments["option_text"].Value;
|
||||
if (optionText.Length > settings[BuiltIn.Keys.KasinoEventOptionTextLengthLimit].ToType<int>())
|
||||
{
|
||||
await botInstance.SendChatMessageAsync($"{user.FormatUsername()}, your option text with a length of " +
|
||||
$"{optionText.Length} characters exceeds the limit of " +
|
||||
$"{settings[BuiltIn.Keys.KasinoEventOptionTextLengthLimit].ToType<int>()} " +
|
||||
$"characters", true);
|
||||
return;
|
||||
}
|
||||
var newOption = new KasinoEventOptionModel
|
||||
{
|
||||
OptionId = Money.GenerateEventId(),
|
||||
OptionText = optionText,
|
||||
Won = false
|
||||
};
|
||||
targetEvent.Options.Add(newOption);
|
||||
await SettingsProvider.SetValueAsJsonObjectAsync(BuiltIn.Keys.KasinoEventData, eventList);
|
||||
await botInstance.SendChatMessageAsync(
|
||||
$"{user.FormatUsername()}, created new option with id {newOption.OptionId}[br]To remove this option, run: !kasino event {targetEvent.EventId} options remove {newOption.OptionId}", true);
|
||||
}
|
||||
}
|
||||
|
||||
public class KasinoRemoveEventOption : ICommand
|
||||
{
|
||||
public List<Regex> Patterns =>
|
||||
[
|
||||
new Regex(@"^kasino event (?<event_id>\w+) options del (?<option_id>\.+)$", RegexOptions.IgnoreCase),
|
||||
new Regex(@"^kasino event (?<event_id>\w+) options remove (?<option_id>\.+)$", RegexOptions.IgnoreCase),
|
||||
];
|
||||
|
||||
public string? HelpText => "Remove an option from a Kasino event";
|
||||
public UserRight RequiredRight => UserRight.TrueAndHonest;
|
||||
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.KasinoEventData
|
||||
]);
|
||||
var eventId = arguments["event_id"].Value;
|
||||
var eventList = settings[BuiltIn.Keys.KasinoEventData].JsonDeserialize<List<KasinoEventModel>>() ?? [];
|
||||
var targetEvent = eventList.FirstOrDefault(x => x.EventId == eventId);
|
||||
if (targetEvent == null)
|
||||
{
|
||||
await botInstance.SendChatMessageAsync($"{user.FormatUsername()}, this event does not exist", true);
|
||||
return;
|
||||
}
|
||||
|
||||
if (targetEvent.EventState != KasinoEventState.Incomplete)
|
||||
{
|
||||
await botInstance.SendChatMessageAsync(
|
||||
$"{user.FormatUsername()}, the event is in state '{targetEvent.EventState.Humanize()}'. Only incomplete events can have their options modified",
|
||||
true);
|
||||
return;
|
||||
}
|
||||
|
||||
if (targetEvent.EventType == KasinoEventType.Prediction)
|
||||
{
|
||||
await botInstance.SendChatMessageAsync(
|
||||
$"{user.FormatUsername()}, events based around time predictions can't have options", true);
|
||||
return;
|
||||
}
|
||||
|
||||
var optionId = arguments["optionId"].Value.ToLower();
|
||||
var targetOption = targetEvent.Options.FirstOrDefault(x => x.OptionId == optionId);
|
||||
if (targetOption == null)
|
||||
{
|
||||
await botInstance.SendChatMessageAsync($"{user.FormatUsername()}, the option ID you provided does not exist", true);
|
||||
return;
|
||||
}
|
||||
|
||||
targetEvent.Options.Remove(targetOption);
|
||||
await SettingsProvider.SetValueAsJsonObjectAsync(BuiltIn.Keys.KasinoEventData, eventList);
|
||||
await botInstance.SendChatMessageAsync(
|
||||
$"{user.FormatUsername()}, removed option with id {targetOption.OptionId}", true);
|
||||
}
|
||||
}
|
||||
|
||||
public class KasinoGetEventInfo : ICommand
|
||||
{
|
||||
public List<Regex> Patterns =>
|
||||
[
|
||||
new Regex(@"^kasino event (?<event_id>\w+) info$", RegexOptions.IgnoreCase),
|
||||
];
|
||||
|
||||
public string? HelpText => "Get Kasino event info";
|
||||
public UserRight RequiredRight => UserRight.TrueAndHonest;
|
||||
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.KasinoEventData
|
||||
]);
|
||||
var eventId = arguments["event_id"].Value;
|
||||
var eventList = settings[BuiltIn.Keys.KasinoEventData].JsonDeserialize<List<KasinoEventModel>>() ?? [];
|
||||
var targetEvent = eventList.FirstOrDefault(x => x.EventId == eventId);
|
||||
if (targetEvent == null)
|
||||
{
|
||||
await botInstance.SendChatMessageAsync($"{user.FormatUsername()}, this event does not exist", true);
|
||||
return;
|
||||
}
|
||||
|
||||
var response = $"{user.FormatUsername()}, Event ID {targetEvent.EventId} with type {targetEvent.EventType.Humanize()} " +
|
||||
$"which is in state {targetEvent.EventState.Humanize()} and has the following text:" +
|
||||
$"[br]{targetEvent.EventText.TrimStart('/')}";
|
||||
if (targetEvent.EventType == KasinoEventType.WinLose)
|
||||
{
|
||||
response += "[br]Options:";
|
||||
foreach (var option in targetEvent.Options)
|
||||
{
|
||||
response += $"[br]{option.OptionId}: {option.OptionText}";
|
||||
}
|
||||
}
|
||||
|
||||
response += $"[br]Event Announcement Received: {targetEvent.EventAnnouncementReceived?.ToString("o")}";
|
||||
response += $"[br]Selection Time Weighted Payout: {targetEvent.SelectionTimeWeightedPayout}";
|
||||
await botInstance.SendChatMessagesAsync(response.FancySplitMessage(partSeparator: "[br]"), true);
|
||||
}
|
||||
}
|
||||
|
||||
public class KasinoGetEvents : ICommand
|
||||
{
|
||||
public List<Regex> Patterns =>
|
||||
[
|
||||
new Regex(@"^kasino events$", RegexOptions.IgnoreCase),
|
||||
new Regex(@"^kasino event list$", RegexOptions.IgnoreCase),
|
||||
|
||||
];
|
||||
|
||||
public string? HelpText => "Get Kasino events";
|
||||
public UserRight RequiredRight => UserRight.TrueAndHonest;
|
||||
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.KasinoEventData
|
||||
]);
|
||||
var eventList = settings[BuiltIn.Keys.KasinoEventData].JsonDeserialize<List<KasinoEventModel>>() ?? [];
|
||||
|
||||
var response = $"{user.FormatUsername()}, there are {eventList.Count} events in the database";
|
||||
foreach (var targetEvent in eventList)
|
||||
{
|
||||
response += $"[br]{targetEvent.EventId} ({targetEvent.EventState.Humanize()}): {targetEvent.EventText}";
|
||||
}
|
||||
|
||||
await botInstance.SendChatMessagesAsync(response.FancySplitMessage(partSeparator: "[br]"), true);
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
using System.Text.RegularExpressions;
|
||||
using Humanizer;
|
||||
using Humanizer.Localisation;
|
||||
using KfChatDotNetBot.Extensions;
|
||||
using KfChatDotNetBot.Models;
|
||||
using KfChatDotNetBot.Models.DbModels;
|
||||
@@ -268,4 +267,36 @@ public class AbandonKasinoCommand : ICommand
|
||||
await db.SaveChangesAsync(ctx);
|
||||
await botInstance.SendChatMessageAsync($"{user.FormatUsername()}, Kasino account with ID {gambler.Id} has been marked as abandoned.", true);
|
||||
}
|
||||
}
|
||||
|
||||
[KasinoCommand]
|
||||
public class PocketWatchCommand : ICommand
|
||||
{
|
||||
public List<Regex> Patterns => [
|
||||
new Regex(@"^pocketwatch (?<user_id>\d+)", RegexOptions.IgnoreCase),
|
||||
];
|
||||
public string? HelpText => "Check a user's balance";
|
||||
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)
|
||||
{
|
||||
await using var db = new ApplicationDbContext();
|
||||
var targetUser = await db.Users.FirstOrDefaultAsync(u => u.KfId == int.Parse(arguments["user_id"].Value), ctx);
|
||||
if (targetUser == null)
|
||||
{
|
||||
await botInstance.SendChatMessageAsync($"{user.FormatUsername()}, the user ID you gave doesn't exist.", true);
|
||||
return;
|
||||
}
|
||||
|
||||
var targetGambler = await Money.GetGamblerEntityAsync(targetUser.Id, ct: ctx);
|
||||
if (targetGambler == null)
|
||||
{
|
||||
await botInstance.SendChatMessageAsync($"{user.FormatUsername()}, this user is excluded from the kasino", true);
|
||||
return;
|
||||
}
|
||||
|
||||
await botInstance.SendChatMessageAsync($"{user.FormatUsername()}, {targetUser.KfUsername} has {await targetGambler.Balance.FormatKasinoCurrencyAsync()}", true);
|
||||
}
|
||||
}
|
||||
@@ -34,7 +34,7 @@ public class KenoCommand : ICommand
|
||||
private const string MatchRevealDisplay = "💠";
|
||||
private const string BlankSpaceDisplay = "⬛";
|
||||
|
||||
private SentMessageTrackerModel _kenoTable;
|
||||
private SentMessageTrackerModel? _kenoTable;
|
||||
|
||||
public async Task RunCommand(ChatBot botInstance, MessageModel message, UserDbModel user, GroupCollection arguments,
|
||||
CancellationToken ctx)
|
||||
@@ -97,7 +97,7 @@ public class KenoCommand : ICommand
|
||||
await botInstance.SendChatMessageAsync(
|
||||
$"{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, cleanupDelay);
|
||||
botInstance.ScheduleMessageAutoDelete(_kenoTable ?? throw new Exception("Cannot clean up _kenoTable as it's null"), cleanupDelay);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -109,7 +109,7 @@ public class KenoCommand : ICommand
|
||||
await botInstance.SendChatMessageAsync(
|
||||
$"{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, cleanupDelay);
|
||||
botInstance.ScheduleMessageAutoDelete(_kenoTable ?? throw new Exception("Cannot clean up _kenotable as it's null"), cleanupDelay);
|
||||
}
|
||||
|
||||
private async Task AnimatedDisplayTable(List<int> playerNumbers, List<int> casinoNumbers, List<int> matches, ChatBot botInstance)
|
||||
@@ -141,6 +141,11 @@ public class KenoCommand : ICommand
|
||||
await Task.Delay(100);
|
||||
}
|
||||
|
||||
if (_kenoTable.ChatMessageId == null)
|
||||
{
|
||||
throw new Exception($"_kenoTable chat message ID never got populated. Tracker status is: {_kenoTable?.Status}");
|
||||
}
|
||||
|
||||
var frameDelay = (await SettingsProvider.GetValueAsync(BuiltIn.Keys.KasinoKenoFrameDelay)).ToType<int>();
|
||||
//FIRST FRAME 11111111111111111111111111111
|
||||
for (var frame = 0; frame < 10; frame++) //1 frame per casino number
|
||||
|
||||
@@ -39,8 +39,8 @@ public class Planes : ICommand
|
||||
private const string Water = "🌊";
|
||||
private const string Air = "\u2B1C"; // White square
|
||||
private const string BlankSpace = "⠀"; //need 35?
|
||||
private bool rigged = false;
|
||||
private bool superRigged = false;
|
||||
private bool _rigged;
|
||||
private bool _superRigged;
|
||||
public async Task RunCommand(ChatBot botInstance, MessageModel message, UserDbModel user, GroupCollection arguments,
|
||||
CancellationToken ctx)
|
||||
{
|
||||
@@ -68,21 +68,19 @@ public class Planes : ICommand
|
||||
return;
|
||||
}
|
||||
|
||||
var carrierCount = 6;
|
||||
const int carrierCount = 6;
|
||||
var planesBoard = CreatePlanesBoard(gambler,0);
|
||||
var planesBoard2 = CreatePlanesBoard(gambler);
|
||||
var planesBoard3 = CreatePlanesBoard(gambler);
|
||||
if (rigged)
|
||||
if (_rigged)
|
||||
{
|
||||
planesBoard2 = RigPlanesBoard(planesBoard2, carrierCount, 0);
|
||||
planesBoard3 = RigPlanesBoard(planesBoard3, carrierCount, 0);
|
||||
}
|
||||
List<int[,]> planesBoards = new List<int[,]>(){planesBoard, planesBoard2, planesBoard3};
|
||||
List<int[,]> planesBoards = [planesBoard, planesBoard2, planesBoard3];
|
||||
var plane = new Plane(gambler);
|
||||
var frameLength = 1000.0;
|
||||
const double frameLength = 1000.0;
|
||||
var fullCounter = 0;
|
||||
bool firstBoard = true;
|
||||
var counter = 0;
|
||||
var noseUp = true;
|
||||
var planesDisplay = GetPreGameBoard(-3, planesBoard2, plane, carrierCount, noseUp);
|
||||
var msgId = await botInstance.SendChatMessageAsync(planesDisplay, true);
|
||||
@@ -102,8 +100,7 @@ public class Planes : ICommand
|
||||
*/
|
||||
do
|
||||
{
|
||||
if ((fullCounter-3) > 19) firstBoard = false;
|
||||
counter = (fullCounter - 3) % 20;
|
||||
var counter = (fullCounter - 3) % 20;
|
||||
|
||||
await Task.Delay(TimeSpan.FromMilliseconds(frameLength / 3), ctx);
|
||||
|
||||
@@ -217,20 +214,20 @@ public class Planes : ICommand
|
||||
plane.Gravity();
|
||||
if ((fullCounter - 3) % 20 == 0 && fullCounter != 3)//removes old planesboard, adds new planeboard when necessary **********************************************************************NEEDS MORE UPDATES
|
||||
{
|
||||
if (Money.GetRandomNumber(gambler, 0, 100) == 0 && settings[BuiltIn.Keys.KasinoPlanesRandomRiggeryEnabled].ToBoolean()) rigged = true;
|
||||
if (Money.GetRandomNumber(gambler, 0, 100) == 0 && settings[BuiltIn.Keys.KasinoPlanesRandomRiggeryEnabled].ToBoolean()) _rigged = true;
|
||||
if (settings[BuiltIn.Keys.KasinoPlanesTargetedRiggeryEnabled].ToBoolean() &&
|
||||
settings[BuiltIn.Keys.KasinoPlanesTargetedRiggeryVictims].JsonDeserialize<List<int>>()!.Contains(user.KfId))
|
||||
{
|
||||
rigged = true;
|
||||
_rigged = true;
|
||||
}
|
||||
logger.Info($"Switching planes boards. FullCounter: {fullCounter} | Counter: {counter}");
|
||||
planesBoards.RemoveAt(0);
|
||||
planesBoards.Add(CreatePlanesBoard(gambler));
|
||||
if (rigged && Money.GetRandomNumber(gambler, 0, 100) == 0) {
|
||||
if (_rigged && Money.GetRandomNumber(gambler, 0, 100) == 0) {
|
||||
planesBoards[1] = CreatePlanesBoard(gambler, 1); //1% chance to update to a board full of rockets if rigged
|
||||
superRigged = true;
|
||||
_superRigged = true;
|
||||
}
|
||||
else if (rigged)
|
||||
else if (_rigged)
|
||||
{
|
||||
planesBoards[1] = RigPlanesBoard(planesBoards[1], carrierCount, fullCounter);
|
||||
planesBoards[2] = RigPlanesBoard(planesBoards[2], carrierCount, fullCounter);
|
||||
@@ -334,8 +331,6 @@ public class Planes : ICommand
|
||||
private string GetGameBoard(int fullCounter, List<int[,]> planesBoards, Plane plane, int carrierCount, bool noseUp)
|
||||
{
|
||||
var output = "";
|
||||
int counter;
|
||||
var logger = LogManager.GetCurrentClassLogger();
|
||||
|
||||
for (var row = 0; row < 8; row++)
|
||||
{
|
||||
@@ -344,6 +339,7 @@ public class Planes : ICommand
|
||||
column++) //plane starts out 3 space behind to give some space to the view,
|
||||
{
|
||||
var useBoard = 1;
|
||||
int counter;
|
||||
if (fullCounter < 23) counter = fullCounter % 23 - 3;
|
||||
else counter = (fullCounter - 3) % 20;
|
||||
//---
|
||||
@@ -404,7 +400,7 @@ public class Planes : ICommand
|
||||
|
||||
}
|
||||
// Was https://i.postimg.cc/rmX59qtV/avelloonaircall2.webp previously
|
||||
if (superRigged && row == 0) output += "[img]https://i.ddos.lgbt/u/6v8WJ5.webp[/img]";
|
||||
if (_superRigged && row == 0) output += "[img]https://i.ddos.lgbt/u/6v8WJ5.webp[/img]";
|
||||
output += "[br]";
|
||||
}
|
||||
return output;
|
||||
@@ -419,18 +415,13 @@ public class Planes : ICommand
|
||||
{
|
||||
var randomNum = Money.GetRandomNumber(gambler, 1, 100);
|
||||
if (forceTiles != -1) board[row, column] = forceTiles;
|
||||
else if (randomNum < 49)
|
||||
{
|
||||
board[row, column] = 0; //neutral
|
||||
}
|
||||
else if (randomNum > 79)
|
||||
{
|
||||
board[row, column] = 1; //rocket
|
||||
}
|
||||
else
|
||||
{
|
||||
board[row, column] = 2; //multi
|
||||
}
|
||||
board[row, column] = randomNum switch
|
||||
{
|
||||
< 49 => 0,
|
||||
> 79 => 1,
|
||||
_ => 2
|
||||
};
|
||||
}
|
||||
}
|
||||
return board;
|
||||
@@ -438,11 +429,9 @@ public class Planes : ICommand
|
||||
|
||||
private int[,] RigPlanesBoard(int[,] planesBoard, int carrierCount, int fullCounter)
|
||||
{
|
||||
int[,] returnBoard = new int[6,20];
|
||||
int boardCounter = (fullCounter-3) / 20;
|
||||
var spaceToUpdate = 0;
|
||||
bool startUpdating = true;
|
||||
spaceToUpdate = (fullCounter-3) % 20; //how far along is the game into the current board
|
||||
var returnBoard = new int[6,20];
|
||||
bool startUpdating;
|
||||
var spaceToUpdate = (fullCounter-3) % 20; //how far along is the game into the current board
|
||||
if (spaceToUpdate > 0) startUpdating = false;
|
||||
|
||||
for (var row = 0; row < 6; row++)
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
using System.Text.RegularExpressions;
|
||||
using Humanizer;
|
||||
using Humanizer.Localisation;
|
||||
using KfChatDotNetBot.Models;
|
||||
using KfChatDotNetBot.Models.DbModels;
|
||||
using KfChatDotNetBot.Settings;
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
using System.Text.RegularExpressions;
|
||||
using Humanizer;
|
||||
using Humanizer.Localisation;
|
||||
using KfChatDotNetBot.Models;
|
||||
using KfChatDotNetBot.Models.DbModels;
|
||||
using KfChatDotNetBot.Settings;
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
using System.Reflection;
|
||||
using System.Text.RegularExpressions;
|
||||
using Humanizer;
|
||||
using Humanizer.Localisation;
|
||||
using KfChatDotNetBot.Extensions;
|
||||
using KfChatDotNetBot.Models;
|
||||
using KfChatDotNetBot.Models.DbModels;
|
||||
|
||||
@@ -2,30 +2,30 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<LangVersion>default</LangVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="FlareSolverrSharp" Version="3.0.7" />
|
||||
<PackageReference Include="HtmlAgilityPack" Version="1.12.4" />
|
||||
<PackageReference Include="Humanizer.Core" Version="2.14.1" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="9.0.9" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.9">
|
||||
<PackageReference Include="Humanizer.Core" Version="3.0.1" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="10.0.0" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="10.0.0">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="9.0.9" />
|
||||
<PackageReference Include="Nerdbank.GitVersioning" Version="3.8.118">
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="10.0.0" />
|
||||
<PackageReference Include="Nerdbank.GitVersioning" Version="3.9.50">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="NLog" Version="6.0.4" />
|
||||
<PackageReference Include="NLog" Version="6.0.6" />
|
||||
<PackageReference Include="Raffinert.FuzzySharp" Version="3.0.6" />
|
||||
<PackageReference Include="RandN" Version="0.5.0" />
|
||||
<PackageReference Include="System.Runtime.Caching" Version="9.0.9" />
|
||||
<PackageReference Include="System.Text.Json" Version="9.0.9" />
|
||||
<PackageReference Include="System.Runtime.Caching" Version="10.0.0" />
|
||||
<PackageReference Include="Websocket.Client" Version="5.3.0" />
|
||||
<PackageReference Include="Zalgo" Version="0.3.0" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -289,7 +289,7 @@ public enum WagerGame
|
||||
/// </summary>
|
||||
Event,
|
||||
[Description("Guess what number I'm thinking of")]
|
||||
GuessWhatNumber
|
||||
GuessWhatNumber,
|
||||
}
|
||||
|
||||
public enum GamblerState
|
||||
|
||||
@@ -1,3 +1,40 @@
|
||||
namespace KfChatDotNetBot.Models;
|
||||
|
||||
// Stash all the models used for perk or game metadata here
|
||||
// Stash all the models used for perk or game metadata here
|
||||
public class KasinoWagerBaseEventMetaModel
|
||||
{
|
||||
/// <summary>
|
||||
/// Event type for this meta model for the purposes of figuring out which model to use when deserializing
|
||||
/// </summary>
|
||||
public required KasinoEventType EventType { get; set; }
|
||||
/// <summary>
|
||||
/// Unique reference tracking the shared event ID stored in the settings
|
||||
/// </summary>
|
||||
public required string SharedEventId { get; set; }
|
||||
/// <summary>
|
||||
/// How long it took the user to make a selection. This is based on the event announcement msg recv - sent timestamp
|
||||
/// that SneedChat provided so the bot won't unfairly penalize users who were delayed by chat lag
|
||||
/// </summary>
|
||||
public required TimeSpan SelectionDelay { get; set; }
|
||||
}
|
||||
/// <summary>
|
||||
/// Metadata model tracking a gambler's wager information related to win/lose games specifically
|
||||
/// </summary>
|
||||
public class KasinoWagerWinLoseEventMetaModel : KasinoWagerBaseEventMetaModel
|
||||
{
|
||||
/// <summary>
|
||||
/// Unique reference tracking the option the gambler selected. Tracked as a GUID in case the option text changes
|
||||
/// </summary>
|
||||
public required string OptionId { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Metadata model tracking a gambler's wager information related to win/lose games specifically
|
||||
/// </summary>
|
||||
public class KasinoWagerPredictionEventMetaModel : KasinoWagerBaseEventMetaModel
|
||||
{
|
||||
/// <summary>
|
||||
/// The absolute time when the user predicted the thing was going to happen
|
||||
/// </summary>
|
||||
public required DateTimeOffset PredictedTime { get; set; }
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
using KfChatDotNetBot.Models.DbModels;
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace KfChatDotNetBot.Models;
|
||||
|
||||
@@ -54,4 +54,93 @@ public class NextVipLevelModel
|
||||
/// The wager requirement to reach this tier that factors in the tier
|
||||
/// </summary>
|
||||
public required decimal WagerRequirement { get; set; }
|
||||
}
|
||||
|
||||
public class KasinoEventModel
|
||||
{
|
||||
/// <summary>
|
||||
/// Text summary of the event itself such as: "How long until the chase ends?" or "Parole granted?"
|
||||
/// </summary>
|
||||
public required string EventText { get; set; }
|
||||
/// <summary>
|
||||
/// Unique reference used for tying this event to wagers gamblers are placing
|
||||
/// </summary>
|
||||
public required string EventId { get; set; }
|
||||
/// <summary>
|
||||
/// The set of options available for gamblers to select. This is only applicable to WinLose-type bets
|
||||
/// It'll be an empty array for closest to finish
|
||||
/// </summary>
|
||||
public required List<KasinoEventOptionModel> Options { get; set; } = [];
|
||||
/// <summary>
|
||||
/// The type of event this is, which is important for the purposes of calculating the payout correctly
|
||||
/// </summary>
|
||||
public required KasinoEventType EventType { get; set; }
|
||||
/// <summary>
|
||||
/// Timestamp of when the announcement message was received by the client for the purposes of calculating selection
|
||||
/// delay. This value is null when the message hasn't been seen yet (either event not started or message lost.
|
||||
/// Do not accept wagers where this is null.
|
||||
/// </summary>
|
||||
public DateTimeOffset? EventAnnouncementReceived { get; set; } = null;
|
||||
/// <summary>
|
||||
/// State of the kasino event
|
||||
/// </summary>
|
||||
public required KasinoEventState EventState { get; set; } = KasinoEventState.Incomplete;
|
||||
/// <summary>
|
||||
/// Whether the payout is weighted based on the selection delay
|
||||
/// </summary>
|
||||
public required bool SelectionTimeWeightedPayout { get; set; } = false;
|
||||
}
|
||||
|
||||
public class KasinoEventOptionModel
|
||||
{
|
||||
/// <summary>
|
||||
/// Unique reference used for tying a gambler's selection to a given option
|
||||
/// </summary>
|
||||
public required string OptionId { get; set; }
|
||||
/// <summary>
|
||||
/// Text to describe the option that users are picking
|
||||
/// </summary>
|
||||
public required string OptionText { get; set; }
|
||||
/// <summary>
|
||||
/// Whether this option won or not, null while incomplete
|
||||
/// </summary>
|
||||
public bool? Won { get; set; } = null;
|
||||
}
|
||||
|
||||
public enum KasinoEventType
|
||||
{
|
||||
[Description("Win/Lose")]
|
||||
WinLose,
|
||||
[Description("Closest to prediction")]
|
||||
Prediction,
|
||||
}
|
||||
|
||||
public enum KasinoEventState
|
||||
{
|
||||
/// <summary>
|
||||
/// Event still under construction. This is the initial state when an admin creates an event but hasn't yet launched it
|
||||
/// </summary>
|
||||
Incomplete,
|
||||
/// <summary>
|
||||
/// Event has been launched but the announcement message hasn't yet been acknowledged by Sneedchat
|
||||
/// No bets will be processed until the message is seen
|
||||
/// If the message is ultimately lost, the event will never launch
|
||||
/// </summary>
|
||||
PendingAnnouncement,
|
||||
/// <summary>
|
||||
/// The event announcement message was seen, the event has started and wagers can now be placed
|
||||
/// </summary>
|
||||
Started,
|
||||
/// <summary>
|
||||
/// Closed to new wagers but the event is still ongoing
|
||||
/// </summary>
|
||||
Closed,
|
||||
/// <summary>
|
||||
/// Event has closed, it's so over.
|
||||
/// </summary>
|
||||
Over,
|
||||
/// <summary>
|
||||
/// Event was abandoned and all wagers canceled
|
||||
/// </summary>
|
||||
Abandoned
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
using System.Text.RegularExpressions;
|
||||
using Humanizer;
|
||||
using Humanizer.Localisation;
|
||||
using KfChatDotNetBot.Commands;
|
||||
using KfChatDotNetBot.Extensions;
|
||||
using KfChatDotNetBot.Models;
|
||||
|
||||
@@ -172,7 +172,13 @@ public class BotServices
|
||||
|
||||
private async Task BuildJackpot()
|
||||
{
|
||||
var proxy = (await SettingsProvider.GetValueAsync(BuiltIn.Keys.Proxy)).Value;
|
||||
var settings = await SettingsProvider.GetMultipleValuesAsync([BuiltIn.Keys.Proxy, BuiltIn.Keys.JackpotEnabled]);
|
||||
if (!settings[BuiltIn.Keys.JackpotEnabled].ToBoolean())
|
||||
{
|
||||
_logger.Debug("Jackpot.bet is disabled");
|
||||
return;
|
||||
}
|
||||
var proxy = settings[BuiltIn.Keys.Proxy].Value;
|
||||
_jackpot = new Jackpot(proxy, _cancellationToken);
|
||||
_jackpot.OnJackpotBet += OnJackpotBet;
|
||||
await _jackpot.StartWsClient();
|
||||
@@ -378,7 +384,7 @@ public class BotServices
|
||||
var settings = await SettingsProvider.GetMultipleValuesAsync([
|
||||
BuiltIn.Keys.KickEnabled, BuiltIn.Keys.HowlggEnabled, BuiltIn.Keys.ChipsggEnabled,
|
||||
BuiltIn.Keys.ClashggEnabled, BuiltIn.Keys.BetBoltEnabled, BuiltIn.Keys.YeetEnabled,
|
||||
BuiltIn.Keys.RainbetEnabled, BuiltIn.Keys.PartiEnabled
|
||||
BuiltIn.Keys.RainbetEnabled, BuiltIn.Keys.PartiEnabled, BuiltIn.Keys.JackpotEnabled
|
||||
]);
|
||||
try
|
||||
{
|
||||
@@ -422,7 +428,7 @@ public class BotServices
|
||||
await BuildHowlgg();
|
||||
}
|
||||
|
||||
if (_jackpot != null && !_jackpot.IsConnected())
|
||||
if (_jackpot != null && settings[BuiltIn.Keys.JackpotEnabled].ToBoolean() && !_jackpot.IsConnected())
|
||||
{
|
||||
_logger.Error("Jackpot died, recreating it");
|
||||
_jackpot.Dispose();
|
||||
|
||||
@@ -188,6 +188,7 @@ public class DLive(ChatBot kfChatBot) : IDisposable
|
||||
{
|
||||
logger.Error($"Bot shit itself while trying to check if {username} is live. JSON payload follows");
|
||||
logger.Error(content.GetRawText);
|
||||
logger.Error(e);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -519,4 +519,12 @@ public static class Money
|
||||
return payout;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generate a short random string based on the first 4 bytes of a GUID for event IDs
|
||||
/// </summary>
|
||||
/// <returns>Returns a lowercase hex representation of the 4 bytes. e.g. 7ec79eb2</returns>
|
||||
public static string GenerateEventId()
|
||||
{
|
||||
return Convert.ToHexString(Guid.NewGuid().ToByteArray()[..4]).ToLower();
|
||||
}
|
||||
}
|
||||
@@ -200,6 +200,8 @@ public static class BuiltIn
|
||||
[BuiltInSetting("Bossman's usernames on Jackpot", SettingValueType.Array,
|
||||
"[\"TheBossmanJack\", \"Austingambless757\"]")]
|
||||
public static string JackpotBmjUsernames = "Jackpot.BmjUsernames";
|
||||
[BuiltInSetting("Whether Jackpot is enabled", SettingValueType.Boolean, "true", BooleanRegex)]
|
||||
public static string JackpotEnabled = "Jackpot.Enabled";
|
||||
[BuiltInSetting("Bossman's rainbet public IDs", SettingValueType.Array,
|
||||
"[\"Ir04170wLulcjtePCL7P6lmeOlepRaNp\", \"IA9RHFR1NLHL33AVOM9GL2G2CINM9I6P\"]")]
|
||||
public static string RainbetBmjPublicIds = "Rainbet.BmjPublicIds";
|
||||
@@ -426,6 +428,19 @@ public static class BuiltIn
|
||||
public static string KasinoPlanesTargetedRiggeryVictims = "Kasino.Planes.TargetedRiggeryVictims";
|
||||
[BuiltInSetting("Table of locks to look for before checking if someone is live / initiating a capture", SettingValueType.Complex, "{}")]
|
||||
public static string CaptureLockTable = "Capture.LockTable";
|
||||
[BuiltInSetting("Whether the win is weighted against selection time", SettingValueType.Boolean,
|
||||
"true", BooleanRegex)]
|
||||
public static string KasinoEventWeightWinAgainstSelectionTime = "Kasino.Event.WeightWinAgainstSelectionTime";
|
||||
[BuiltInSetting("Kasino event data", SettingValueType.Complex, "[]")]
|
||||
public static string KasinoEventData = "Kasino.Event.Data";
|
||||
[BuiltInSetting("Kasino event minimum acceptable bet", SettingValueType.Text, "100", WholeNumberRegex)]
|
||||
public static string KasinoEventBetMin = "Kasino.Event.BetMin";
|
||||
[BuiltInSetting("Kasino event maximum acceptable bet (0 means unlimited)", SettingValueType.Text, "0", WholeNumberRegex)]
|
||||
public static string KasinoEventBetMax = "Kasino.Event.BetMax";
|
||||
[BuiltInSetting("Length limit for Kasino event text", SettingValueType.Text, "250", WholeNumberRegex)]
|
||||
public static string KasinoEventTextLengthLimit = "Kasino.Event.TextLengthLimit";
|
||||
[BuiltInSetting("Length limit for Kasino event option text", SettingValueType.Text, "250", WholeNumberRegex)]
|
||||
public static string KasinoEventOptionTextLengthLimit = "Kasino.Event.OptionTextLengthLimit";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user