From 12980a86c3754af9dbec39df747f5a9c067f28a3 Mon Sep 17 00:00:00 2001 From: barelyprofessional <150058423+barelyprofessional@users.noreply.github.com> Date: Sat, 21 Sep 2024 00:28:16 +0800 Subject: [PATCH] Updated Kick support so admins can now add/remove streamers and force a reconnect to commit the changes --- KfChatDotNetBot/Commands/AdminCommands.cs | 75 ++++++++++++++++ KfChatDotNetBot/Models/BotServicesModels.cs | 8 ++ KfChatDotNetBot/Services/BotServices.cs | 95 ++++++++++++++++----- KfChatDotNetBot/Settings/BuiltIn.cs | 10 +++ 4 files changed, 167 insertions(+), 21 deletions(-) create mode 100644 KfChatDotNetBot/Models/BotServicesModels.cs diff --git a/KfChatDotNetBot/Commands/AdminCommands.cs b/KfChatDotNetBot/Commands/AdminCommands.cs index 6336bea..b8eeac3 100644 --- a/KfChatDotNetBot/Commands/AdminCommands.cs +++ b/KfChatDotNetBot/Commands/AdminCommands.cs @@ -1,6 +1,7 @@ using System.Runtime.Caching; using System.Text.RegularExpressions; using Humanizer; +using KfChatDotNetBot.Models; using KfChatDotNetBot.Models.DbModels; using KfChatDotNetBot.Settings; using KfChatDotNetWsClient.Models.Events; @@ -162,4 +163,78 @@ public class CacheClearAdminCommand : ICommand } await botInstance.SendChatMessageAsync("Cache wiped", true); } +} + +public class NewKickChannelCommand : ICommand +{ + public List Patterns => [ + new Regex(@"^admin kick add (?\d+) (?\d+) (?\S+)$") + ]; + + public string? HelpText => "Add a Kick channel to the bot's database"; + public UserRight RequiredRight => UserRight.Admin; + public TimeSpan Timeout => TimeSpan.FromSeconds(10); + public async Task RunCommand(ChatBot botInstance, MessageModel message, UserDbModel user, GroupCollection arguments, CancellationToken ctx) + { + var channels = (await Helpers.GetValue(BuiltIn.Keys.KickChannels)).JsonDeserialize>(); + var channelId = Convert.ToInt32(arguments["channel_id"].Value); + if (channels.Any(channel => channel.ChannelId == channelId)) + { + await botInstance.SendChatMessageAsync("Channel is already in the database", true); + return; + } + + var forumId = Convert.ToInt32(arguments["forum_id"].Value); + channels.Add(new KickChannelModel + { + ChannelId = channelId, + ForumId = forumId, + ChannelSlug = arguments["slug"].Value + }); + + await Helpers.SetValueAsJsonObject(BuiltIn.Keys.KickChannels, channels); + await botInstance.SendChatMessageAsync("Updated list of channels", true); + } +} + +public class RemoveKickChannelCommand : ICommand +{ + public List Patterns => [ + new Regex(@"^admin kick remove (?\d+)$") + ]; + + public string? HelpText => "Remove a Kick channel from the bot's database"; + public UserRight RequiredRight => UserRight.Admin; + public TimeSpan Timeout => TimeSpan.FromSeconds(10); + public async Task RunCommand(ChatBot botInstance, MessageModel message, UserDbModel user, GroupCollection arguments, CancellationToken ctx) + { + var channels = (await Helpers.GetValue(BuiltIn.Keys.KickChannels)).JsonDeserialize>(); + var channelId = Convert.ToInt32(arguments["channel_id"].Value); + var channel = channels.FirstOrDefault(ch => ch.ChannelId == channelId); + if (channel == null) + { + await botInstance.SendChatMessageAsync("Channel is not in the database", true); + return; + } + channels.Remove(channel); + + await Helpers.SetValueAsJsonObject(BuiltIn.Keys.KickChannels, channels); + await botInstance.SendChatMessageAsync("Updated list of channels", true); + } +} + +public class ReconnectKickCommand : ICommand +{ + public List Patterns => [ + new Regex(@"^admin kick reconnect$") + ]; + + public string? HelpText => "Disconnect from Kick so the watchdog can reconnect it"; + public UserRight RequiredRight => UserRight.Admin; + public TimeSpan Timeout => TimeSpan.FromSeconds(10); + public async Task RunCommand(ChatBot botInstance, MessageModel message, UserDbModel user, GroupCollection arguments, CancellationToken ctx) + { + botInstance.BotServices.KickClient.Disconnect(); + await botInstance.SendChatMessageAsync("Disconnected from Kick. Client should reconnect shortly.", true); + } } \ No newline at end of file diff --git a/KfChatDotNetBot/Models/BotServicesModels.cs b/KfChatDotNetBot/Models/BotServicesModels.cs new file mode 100644 index 0000000..071d16b --- /dev/null +++ b/KfChatDotNetBot/Models/BotServicesModels.cs @@ -0,0 +1,8 @@ +namespace KfChatDotNetBot.Models; + +public class KickChannelModel +{ + public required int ChannelId { get; set; } + public required int ForumId { get; set; } + public required string ChannelSlug { get; set; } +} \ No newline at end of file diff --git a/KfChatDotNetBot/Services/BotServices.cs b/KfChatDotNetBot/Services/BotServices.cs index 0fa6509..af49652 100644 --- a/KfChatDotNetBot/Services/BotServices.cs +++ b/KfChatDotNetBot/Services/BotServices.cs @@ -18,7 +18,7 @@ public class BotServices private readonly CancellationToken _cancellationToken; private readonly Logger _logger = LogManager.GetCurrentClassLogger(); - private KickWsClient.KickWsClient _kickClient; + internal KickWsClient.KickWsClient KickClient; private Twitch _twitch; private Shuffle _shuffle; private DiscordService _discord; @@ -166,24 +166,30 @@ public class BotServices { var settings = await Helpers.GetMultipleValues([ BuiltIn.Keys.PusherEndpoint, BuiltIn.Keys.Proxy, BuiltIn.Keys.PusherReconnectTimeout, BuiltIn.Keys.KickEnabled, - BuiltIn.Keys.PusherChannels + BuiltIn.Keys.PusherChannels, BuiltIn.Keys.KickChannels ]); - _kickClient = new KickWsClient.KickWsClient(settings[BuiltIn.Keys.PusherEndpoint].Value!, + KickClient = new KickWsClient.KickWsClient(settings[BuiltIn.Keys.PusherEndpoint].Value!, settings[BuiltIn.Keys.Proxy].Value, settings[BuiltIn.Keys.PusherReconnectTimeout].ToType()); - _kickClient.OnStreamerIsLive += OnStreamerIsLive; - _kickClient.OnChatMessage += OnKickChatMessage; - _kickClient.OnWsReconnect += OnPusherWsReconnected; - _kickClient.OnPusherSubscriptionSucceeded += OnPusherSubscriptionSucceeded; - _kickClient.OnStopStreamBroadcast += OnStopStreamBroadcast; + KickClient.OnStreamerIsLive += OnStreamerIsLive; + KickClient.OnChatMessage += OnKickChatMessage; + KickClient.OnWsReconnect += OnPusherWsReconnected; + KickClient.OnPusherSubscriptionSucceeded += OnPusherSubscriptionSucceeded; + KickClient.OnStopStreamBroadcast += OnStopStreamBroadcast; if (settings[BuiltIn.Keys.KickEnabled].ToBoolean()) { - await _kickClient.StartWsClient(); - var pusherChannels = settings[BuiltIn.Keys.PusherChannels].ToList(); - foreach (var channel in pusherChannels) + await KickClient.StartWsClient(); + // var pusherChannels = settings[BuiltIn.Keys.PusherChannels].ToList(); + // foreach (var channel in pusherChannels) + // { + // _kickClient.SendPusherSubscribe(channel); + // } + var kickChannels = settings[BuiltIn.Keys.KickChannels].JsonDeserialize>(); + if (kickChannels == null) return; + foreach (var channel in kickChannels) { - _kickClient.SendPusherSubscribe(channel); + KickClient.SendPusherSubscribe($"channel.{channel.ChannelId}"); } } } @@ -270,11 +276,11 @@ public class BotServices await BuildChipsgg(); } - if (settings[BuiltIn.Keys.KickEnabled].ToBoolean() && !_kickClient.IsConnected()) + if (settings[BuiltIn.Keys.KickEnabled].ToBoolean() && !KickClient.IsConnected()) { _logger.Error("Kick died, recreating it"); - _kickClient.Dispose(); - _kickClient = null!; + KickClient.Dispose(); + KickClient = null!; await BuildKick(); } } @@ -631,11 +637,11 @@ public class BotServices private void OnPusherWsReconnected(object sender, ReconnectionInfo reconnectionInfo) { _logger.Error($"Pusher reconnected due to {reconnectionInfo.Type}"); - var channels = Helpers.GetValue(BuiltIn.Keys.PusherChannels).Result.ToList(); - foreach (var channel in channels) + var kickChannels = Helpers.GetValue(BuiltIn.Keys.KickChannels).Result.JsonDeserialize>(); + if (kickChannels == null) return; + foreach (var channel in kickChannels) { - _logger.Info($"Rejoining {channel}"); - _kickClient.SendPusherSubscribe(channel); + KickClient.SendPusherSubscribe($"channel.{channel.ChannelId}"); } } @@ -659,11 +665,58 @@ public class BotServices private void OnStreamerIsLive(object sender, KickModels.StreamerIsLiveEventModel? e) { if (e == null) return; - _chatBot.SendChatMessage($"Dirt Devils LFG! @Juhlonduss is live! {e.Livestream.SessionTitle} https://kick.com/dirtdevil-enjoyer", true); + var channels = Helpers.GetValue(BuiltIn.Keys.KickChannels).Result.JsonDeserialize>(); + if (channels == null) + { + _logger.Error("Caught null when grabbing Kick channels"); + return; + } + + var channel = channels.FirstOrDefault(ch => ch.ChannelId == e.Livestream.ChannelId); + if (channel == null) + { + _logger.Error($"Caught null when grabbing channel data for {e.Livestream.ChannelId}"); + return; + } + + using var db = new ApplicationDbContext(); + var user = db.Users.FirstOrDefault(u => u.KfId == channel.ForumId); + if (user == null) + { + _logger.Error($"Caught null when retrieving forum user {channel.ForumId}"); + return; + } + + _chatBot.SendChatMessage( + $"@{user.KfUsername} is live! {e.Livestream.SessionTitle} https://kick.com/{channel.ChannelSlug}", true); } private void OnStopStreamBroadcast(object sender, KickModels.StopStreamBroadcastEventModel? e) { - _chatBot.SendChatMessage("Dirt Devils felted. Stream is over. :lossmanjack:", true); + if (e == null) return; + var channels = Helpers.GetValue(BuiltIn.Keys.KickChannels).Result.JsonDeserialize>(); + if (channels == null) + { + _logger.Error("Caught null when grabbing Kick channels"); + return; + } + + var channel = channels.FirstOrDefault(ch => ch.ChannelId == e.Livestream.Channel.Id); + if (channel == null) + { + _logger.Error($"Caught null when grabbing channel data for {e.Livestream.Channel.Id}"); + return; + } + + using var db = new ApplicationDbContext(); + var user = db.Users.FirstOrDefault(u => u.KfId == channel.ForumId); + if (user == null) + { + _logger.Error($"Caught null when retrieving forum user {channel.ForumId}"); + return; + } + + _chatBot.SendChatMessage( + $"@{user.KfUsername} is no longer live! :lossmanjack:", true); } } \ No newline at end of file diff --git a/KfChatDotNetBot/Settings/BuiltIn.cs b/KfChatDotNetBot/Settings/BuiltIn.cs index 8880659..b20bb48 100644 --- a/KfChatDotNetBot/Settings/BuiltIn.cs +++ b/KfChatDotNetBot/Settings/BuiltIn.cs @@ -534,6 +534,15 @@ public static class BuiltIn Default = "2", IsSecret = false, CacheDuration = TimeSpan.FromHours(1) + }, + new BuiltInSettingsModel + { + Key = Keys.KickChannels, + Regex = ".+", + Description = "Kick channels the bot knows about for notifications", + Default = "[]", + IsSecret = false, + CacheDuration = TimeSpan.FromHours(1) } ]; @@ -585,5 +594,6 @@ public static class BuiltIn public static string WinmanjackImgUrl = "Winmanjack.ImgUrl"; public static string BotDisconnectReplayLimit = "Bot.DisconnectReplayLimit"; public static string KiwiFarmsJoinFailLimit = "KiwiFarms.JoinFailLimit"; + public static string KickChannels = "Kick.Channels"; } } \ No newline at end of file