mirror of
https://github.com/barelyprofessional/KfChatDotNet.git
synced 2026-05-02 04:22:04 -04:00
Updated commands to remove the hide from help property, instead set the help text to null for whenever that eventually gets implemented.
Also refactored the way tasks are handled so instead of adding to an array and checking in on them next time someone sends a message, it instead delegates it to a very basic async handler that'll await the command, report errors and kill the task if it takes too long.
This commit is contained in:
@@ -7,9 +7,10 @@ namespace KfChatDotNetBot.Commands;
|
|||||||
internal interface ICommand
|
internal interface ICommand
|
||||||
{
|
{
|
||||||
List<Regex> Patterns { get; }
|
List<Regex> Patterns { get; }
|
||||||
string HelpText { get; }
|
// Set to null to disable help for a given command
|
||||||
bool HideFromHelp { get; }
|
string? HelpText { get; }
|
||||||
UserRight RequiredRight { get; }
|
UserRight RequiredRight { get; }
|
||||||
|
TimeSpan Timeout { get; }
|
||||||
|
|
||||||
Task RunCommand(ChatBot botInstance, MessageModel message, UserDbModel user, GroupCollection arguments, CancellationToken ctx);
|
Task RunCommand(ChatBot botInstance, MessageModel message, UserDbModel user, GroupCollection arguments, CancellationToken ctx);
|
||||||
}
|
}
|
||||||
@@ -14,6 +14,7 @@ public class JuiceCommand : ICommand
|
|||||||
public string HelpText => "Get juice!";
|
public string HelpText => "Get juice!";
|
||||||
public bool HideFromHelp => false;
|
public bool HideFromHelp => false;
|
||||||
public UserRight RequiredRight => UserRight.Guest;
|
public UserRight RequiredRight => UserRight.Guest;
|
||||||
|
public TimeSpan Timeout => TimeSpan.FromSeconds(10);
|
||||||
public async Task RunCommand(ChatBot botInstance, MessageModel message, UserDbModel user, GroupCollection arguments, CancellationToken ctx)
|
public async Task RunCommand(ChatBot botInstance, MessageModel message, UserDbModel user, GroupCollection arguments, CancellationToken ctx)
|
||||||
{
|
{
|
||||||
await using var db = new ApplicationDbContext();
|
await using var db = new ApplicationDbContext();
|
||||||
@@ -50,12 +51,13 @@ public class JuiceCommand : ICommand
|
|||||||
public class JuiceStatsCommand : ICommand
|
public class JuiceStatsCommand : ICommand
|
||||||
{
|
{
|
||||||
public List<Regex> Patterns => [
|
public List<Regex> Patterns => [
|
||||||
new Regex("^juice stats"),
|
new Regex("^juice stats$"),
|
||||||
new Regex(@"^juice stats (?<top>\d+)$")
|
new Regex(@"^juice stats (?<top>\d+)$")
|
||||||
];
|
];
|
||||||
public string HelpText => "Get juice stats!";
|
public string HelpText => "Get juice stats!";
|
||||||
public bool HideFromHelp => false;
|
public bool HideFromHelp => false;
|
||||||
public UserRight RequiredRight => UserRight.Guest;
|
public UserRight RequiredRight => UserRight.Guest;
|
||||||
|
public TimeSpan Timeout => TimeSpan.FromSeconds(10);
|
||||||
public async Task RunCommand(ChatBot botInstance, MessageModel message, UserDbModel user, GroupCollection arguments, CancellationToken ctx)
|
public async Task RunCommand(ChatBot botInstance, MessageModel message, UserDbModel user, GroupCollection arguments, CancellationToken ctx)
|
||||||
{
|
{
|
||||||
int top;
|
int top;
|
||||||
|
|||||||
@@ -7,10 +7,9 @@ namespace KfChatDotNetBot.Commands;
|
|||||||
public class InsanityCommand : ICommand
|
public class InsanityCommand : ICommand
|
||||||
{
|
{
|
||||||
public List<Regex> Patterns => [new Regex("^insanity")];
|
public List<Regex> Patterns => [new Regex("^insanity")];
|
||||||
public string HelpText => "Insanity";
|
public string? HelpText => "Insanity";
|
||||||
public bool HideFromHelp => false;
|
|
||||||
public UserRight RequiredRight => UserRight.Guest;
|
public UserRight RequiredRight => UserRight.Guest;
|
||||||
|
public TimeSpan Timeout => TimeSpan.FromSeconds(10);
|
||||||
public async Task RunCommand(ChatBot botInstance, MessageModel message, UserDbModel user, GroupCollection arguments, CancellationToken ctx)
|
public async Task RunCommand(ChatBot botInstance, MessageModel message, UserDbModel user, GroupCollection arguments, CancellationToken ctx)
|
||||||
{
|
{
|
||||||
// ReSharper disable once StringLiteralTypo
|
// ReSharper disable once StringLiteralTypo
|
||||||
@@ -21,10 +20,9 @@ public class InsanityCommand : ICommand
|
|||||||
public class TwistedCommand : ICommand
|
public class TwistedCommand : ICommand
|
||||||
{
|
{
|
||||||
public List<Regex> Patterns => [new Regex("^twisted")];
|
public List<Regex> Patterns => [new Regex("^twisted")];
|
||||||
public string HelpText => "Get it twisted";
|
public string? HelpText => "Get it twisted";
|
||||||
public bool HideFromHelp => false;
|
|
||||||
public UserRight RequiredRight => UserRight.Guest;
|
public UserRight RequiredRight => UserRight.Guest;
|
||||||
|
public TimeSpan Timeout => TimeSpan.FromSeconds(10);
|
||||||
public async Task RunCommand(ChatBot botInstance, MessageModel message, UserDbModel user, GroupCollection arguments, CancellationToken ctx)
|
public async Task RunCommand(ChatBot botInstance, MessageModel message, UserDbModel user, GroupCollection arguments, CancellationToken ctx)
|
||||||
{
|
{
|
||||||
// ReSharper disable once StringLiteralTypo
|
// ReSharper disable once StringLiteralTypo
|
||||||
@@ -35,10 +33,9 @@ public class TwistedCommand : ICommand
|
|||||||
public class HelpMeCommand : ICommand
|
public class HelpMeCommand : ICommand
|
||||||
{
|
{
|
||||||
public List<Regex> Patterns => [new Regex("^helpme")];
|
public List<Regex> Patterns => [new Regex("^helpme")];
|
||||||
public string HelpText => "Somebody please help me";
|
public string? HelpText => "Somebody please help me";
|
||||||
public bool HideFromHelp => false;
|
|
||||||
public UserRight RequiredRight => UserRight.Guest;
|
public UserRight RequiredRight => UserRight.Guest;
|
||||||
|
public TimeSpan Timeout => TimeSpan.FromSeconds(10);
|
||||||
public async Task RunCommand(ChatBot botInstance, MessageModel message, UserDbModel user, GroupCollection arguments, CancellationToken ctx)
|
public async Task RunCommand(ChatBot botInstance, MessageModel message, UserDbModel user, GroupCollection arguments, CancellationToken ctx)
|
||||||
{
|
{
|
||||||
// ReSharper disable once StringLiteralTypo
|
// ReSharper disable once StringLiteralTypo
|
||||||
@@ -49,10 +46,9 @@ public class HelpMeCommand : ICommand
|
|||||||
public class SentCommand : ICommand
|
public class SentCommand : ICommand
|
||||||
{
|
{
|
||||||
public List<Regex> Patterns => [new Regex("^sent$")];
|
public List<Regex> Patterns => [new Regex("^sent$")];
|
||||||
public string HelpText => "Sent love";
|
public string? HelpText => "Sent love";
|
||||||
public bool HideFromHelp => false;
|
|
||||||
public UserRight RequiredRight => UserRight.Guest;
|
public UserRight RequiredRight => UserRight.Guest;
|
||||||
|
public TimeSpan Timeout => TimeSpan.FromSeconds(10);
|
||||||
public async Task RunCommand(ChatBot botInstance, MessageModel message, UserDbModel user, GroupCollection arguments, CancellationToken ctx)
|
public async Task RunCommand(ChatBot botInstance, MessageModel message, UserDbModel user, GroupCollection arguments, CancellationToken ctx)
|
||||||
{
|
{
|
||||||
// ReSharper disable once StringLiteralTypo
|
// ReSharper disable once StringLiteralTypo
|
||||||
|
|||||||
@@ -12,9 +12,9 @@ public class RainbetStatsCommand : ICommand
|
|||||||
public List<Regex> Patterns => [
|
public List<Regex> Patterns => [
|
||||||
new Regex(@"^rainbet stats (?<window>\d+)$")
|
new Regex(@"^rainbet stats (?<window>\d+)$")
|
||||||
];
|
];
|
||||||
public string HelpText => "Get betting statistics in the given window";
|
public string? HelpText => "Get betting statistics in the given window";
|
||||||
public bool HideFromHelp => false;
|
|
||||||
public UserRight RequiredRight => UserRight.Guest;
|
public UserRight RequiredRight => UserRight.Guest;
|
||||||
|
public TimeSpan Timeout => TimeSpan.FromSeconds(10);
|
||||||
public async Task RunCommand(ChatBot botInstance, MessageModel message, UserDbModel user, GroupCollection arguments, CancellationToken ctx)
|
public async Task RunCommand(ChatBot botInstance, MessageModel message, UserDbModel user, GroupCollection arguments, CancellationToken ctx)
|
||||||
{
|
{
|
||||||
var window = Convert.ToInt32(arguments["window"].Value);
|
var window = Convert.ToInt32(arguments["window"].Value);
|
||||||
@@ -28,7 +28,7 @@ public class RainbetStatsCommand : ICommand
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var output = $"Rainbet stats for the last {window} hours (as seen on the bet feed):[br]" +
|
var output = $"Rainbet stats for the last {window} hours (as seen on the bet feed):[br]" +
|
||||||
$"Bets: {bets.Count:N0}; Payout: ${bets.Sum(b => b.Payout):C}; Wagered: {bets.Sum(b => b.Value):C}";
|
$"Bets: {bets.Count:N0}; Payout: {bets.Sum(b => b.Payout):C}; Wagered: {bets.Sum(b => b.Value):C}";
|
||||||
botInstance.SendChatMessage(output, true);
|
botInstance.SendChatMessage(output, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -38,9 +38,9 @@ public class RainbetRecentBetCommand : ICommand
|
|||||||
public List<Regex> Patterns => [
|
public List<Regex> Patterns => [
|
||||||
new Regex(@"^rainbet recent$")
|
new Regex(@"^rainbet recent$")
|
||||||
];
|
];
|
||||||
public string HelpText => "Get the most recent 3 bets";
|
public string? HelpText => "Get the most recent 3 bets";
|
||||||
public bool HideFromHelp => false;
|
|
||||||
public UserRight RequiredRight => UserRight.Guest;
|
public UserRight RequiredRight => UserRight.Guest;
|
||||||
|
public TimeSpan Timeout => TimeSpan.FromSeconds(10);
|
||||||
public async Task RunCommand(ChatBot botInstance, MessageModel message, UserDbModel user, GroupCollection arguments, CancellationToken ctx)
|
public async Task RunCommand(ChatBot botInstance, MessageModel message, UserDbModel user, GroupCollection arguments, CancellationToken ctx)
|
||||||
{
|
{
|
||||||
var settings = await Helpers.GetMultipleValues([
|
var settings = await Helpers.GetMultipleValues([
|
||||||
|
|||||||
@@ -13,10 +13,10 @@ public class EditTestCommand : ICommand
|
|||||||
new Regex("^test edit (?<msg>.+)")
|
new Regex("^test edit (?<msg>.+)")
|
||||||
];
|
];
|
||||||
|
|
||||||
public string HelpText => "Test the editing functionality";
|
public string? HelpText => null;
|
||||||
public bool HideFromHelp => true;
|
|
||||||
public UserRight RequiredRight => UserRight.Admin;
|
public UserRight RequiredRight => UserRight.Admin;
|
||||||
|
// Increased timeout as it has to wait for Sneedchat to echo the message and that can be slow sometimes
|
||||||
|
public TimeSpan Timeout => TimeSpan.FromSeconds(60);
|
||||||
public async Task RunCommand(ChatBot botInstance, MessageModel message, UserDbModel user, GroupCollection arguments, CancellationToken ctx)
|
public async Task RunCommand(ChatBot botInstance, MessageModel message, UserDbModel user, GroupCollection arguments, CancellationToken ctx)
|
||||||
{
|
{
|
||||||
var logger = LogManager.GetCurrentClassLogger();
|
var logger = LogManager.GetCurrentClassLogger();
|
||||||
@@ -50,3 +50,35 @@ public class EditTestCommand : ICommand
|
|||||||
botInstance.KfClient.DeleteMessage(status.ChatMessageId!.Value);
|
botInstance.KfClient.DeleteMessage(status.ChatMessageId!.Value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class TimeoutTestCommand : ICommand
|
||||||
|
{
|
||||||
|
public List<Regex> Patterns => [
|
||||||
|
new Regex("^test timeout$")
|
||||||
|
];
|
||||||
|
|
||||||
|
public string? HelpText => null;
|
||||||
|
public UserRight RequiredRight => UserRight.Admin;
|
||||||
|
// Increased timeout as it has to wait for Sneedchat to echo the message and that can be slow sometimes
|
||||||
|
public TimeSpan Timeout => TimeSpan.FromSeconds(15);
|
||||||
|
public async Task RunCommand(ChatBot botInstance, MessageModel message, UserDbModel user, GroupCollection arguments, CancellationToken ctx)
|
||||||
|
{
|
||||||
|
await Task.Delay(TimeSpan.FromMinutes(1), ctx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ExceptionTestCommand : ICommand
|
||||||
|
{
|
||||||
|
public List<Regex> Patterns => [
|
||||||
|
new Regex("^test exception$")
|
||||||
|
];
|
||||||
|
|
||||||
|
public string? HelpText => null;
|
||||||
|
public UserRight RequiredRight => UserRight.Admin;
|
||||||
|
// Increased timeout as it has to wait for Sneedchat to echo the message and that can be slow sometimes
|
||||||
|
public TimeSpan Timeout => TimeSpan.FromSeconds(15);
|
||||||
|
public async Task RunCommand(ChatBot botInstance, MessageModel message, UserDbModel user, GroupCollection arguments, CancellationToken ctx)
|
||||||
|
{
|
||||||
|
throw new Exception("Caused by the test exception command");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,10 +7,9 @@ namespace KfChatDotNetBot.Commands;
|
|||||||
public class TimeCommand : ICommand
|
public class TimeCommand : ICommand
|
||||||
{
|
{
|
||||||
public List<Regex> Patterns => [new Regex("^time")];
|
public List<Regex> Patterns => [new Regex("^time")];
|
||||||
public string HelpText => "Get current time in BMT";
|
public string? HelpText => "Get current time in BMT";
|
||||||
public bool HideFromHelp => false;
|
|
||||||
public UserRight RequiredRight => UserRight.Guest;
|
public UserRight RequiredRight => UserRight.Guest;
|
||||||
|
public TimeSpan Timeout => TimeSpan.FromSeconds(10);
|
||||||
public async Task RunCommand(ChatBot botInstance, MessageModel message, UserDbModel user, GroupCollection arguments, CancellationToken ctx)
|
public async Task RunCommand(ChatBot botInstance, MessageModel message, UserDbModel user, GroupCollection arguments, CancellationToken ctx)
|
||||||
{
|
{
|
||||||
var bmt = new DateTimeOffset(TimeZoneInfo.ConvertTimeFromUtc(DateTime.UtcNow,
|
var bmt = new DateTimeOffset(TimeZoneInfo.ConvertTimeFromUtc(DateTime.UtcNow,
|
||||||
|
|||||||
@@ -11,10 +11,9 @@ public class WhoisCommand : ICommand
|
|||||||
new Regex("^whois (?<user>.+)")
|
new Regex("^whois (?<user>.+)")
|
||||||
];
|
];
|
||||||
|
|
||||||
public string HelpText => "Lookup user IDs by username";
|
public string? HelpText => "Lookup user IDs by username";
|
||||||
public bool HideFromHelp => false;
|
|
||||||
public UserRight RequiredRight => UserRight.Guest;
|
public UserRight RequiredRight => UserRight.Guest;
|
||||||
|
public TimeSpan Timeout => TimeSpan.FromSeconds(10);
|
||||||
public async Task RunCommand(ChatBot botInstance, MessageModel message, UserDbModel user, GroupCollection arguments, CancellationToken ctx)
|
public async Task RunCommand(ChatBot botInstance, MessageModel message, UserDbModel user, GroupCollection arguments, CancellationToken ctx)
|
||||||
{
|
{
|
||||||
await using var db = new ApplicationDbContext();
|
await using var db = new ApplicationDbContext();
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
using Humanizer;
|
using System.Text.RegularExpressions;
|
||||||
|
using Humanizer;
|
||||||
using KfChatDotNetBot.Commands;
|
using KfChatDotNetBot.Commands;
|
||||||
|
using KfChatDotNetBot.Models.DbModels;
|
||||||
using KfChatDotNetWsClient.Models.Events;
|
using KfChatDotNetWsClient.Models.Events;
|
||||||
using NLog;
|
using NLog;
|
||||||
|
|
||||||
@@ -14,7 +16,6 @@ internal class BotCommands
|
|||||||
private char CommandPrefix = '!';
|
private char CommandPrefix = '!';
|
||||||
private IEnumerable<ICommand> Commands;
|
private IEnumerable<ICommand> Commands;
|
||||||
private CancellationToken _cancellationToken;
|
private CancellationToken _cancellationToken;
|
||||||
private List<Task> _commandTasks = [];
|
|
||||||
|
|
||||||
internal BotCommands(ChatBot bot, CancellationToken? ctx = null)
|
internal BotCommands(ChatBot bot, CancellationToken? ctx = null)
|
||||||
{
|
{
|
||||||
@@ -63,28 +64,29 @@ internal class BotCommands
|
|||||||
_bot.SendChatMessage($"@{message.Author.Username}, you do not have access to use this command. Your rank: {user.UserRight.Humanize()}; Required rank: {command.RequiredRight.Humanize()}", true);
|
_bot.SendChatMessage($"@{message.Author.Username}, you do not have access to use this command. Your rank: {user.UserRight.Humanize()}; Required rank: {command.RequiredRight.Humanize()}", true);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
var task = Task.Run(() => command.RunCommand(_bot, message, user, match.Groups, _cancellationToken), _cancellationToken);
|
_ = ProcessMessageAsync(command, message, user, match.Groups);
|
||||||
_commandTasks.Add(task);
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Check on the state of the tasks, there's no way to know what error they produce if they failed otherwise
|
private async Task ProcessMessageAsync(ICommand command, MessageModel message, UserDbModel user, GroupCollection arguments)
|
||||||
List<Task> removals = [];
|
{
|
||||||
foreach (var task in _commandTasks)
|
var task = Task.Run(() => command.RunCommand(_bot, message, user, arguments, _cancellationToken), _cancellationToken);
|
||||||
|
try
|
||||||
{
|
{
|
||||||
if (!task.IsCompleted) continue;
|
await task.WaitAsync(command.Timeout, _cancellationToken);
|
||||||
if (task.IsFaulted)
|
|
||||||
{
|
|
||||||
_logger.Error("Command task failed at some point");
|
|
||||||
_logger.Error(task.Exception);
|
|
||||||
}
|
|
||||||
|
|
||||||
removals.Add(task);
|
|
||||||
}
|
}
|
||||||
// .NET doesn't support modifying a collection you're iterating over
|
catch (Exception e)
|
||||||
foreach (var removal in removals)
|
|
||||||
{
|
{
|
||||||
_commandTasks.Remove(removal);
|
_logger.Error("Caught an exception while waiting for the command to complete");
|
||||||
|
_logger.Error(e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (task.IsFaulted)
|
||||||
|
{
|
||||||
|
_logger.Error("Command task failed");
|
||||||
|
_logger.Error(task.Exception);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user