From cdad1d65498dfdbf747c4b0d8df09dc8ba419530 Mon Sep 17 00:00:00 2001 From: barelyprofessional <150058423+barelyprofessional@users.noreply.github.com> Date: Fri, 14 Jun 2024 23:03:05 +0800 Subject: [PATCH] Big update. Replaced Newtonsoft with System.Text.Json where possible, removed Spectre, tried to suppress the pile of compiler warnings I get on the GUI project, and tried to correct an issue where sometimes the session token retrieved is not usable. --- KfChatDotNetGui/App.axaml.cs | 4 +- KfChatDotNetGui/Helpers/ForumIdentity.cs | 4 +- KfChatDotNetGui/KfChatDotNetGui.csproj | 1 - KfChatDotNetGui/Models/ForumIdentityModel.cs | 12 +- .../Views/IdentitySettingsWindow.axaml.cs | 5 +- KfChatDotNetGui/Views/MainWindow.axaml.cs | 70 ++++---- .../Views/RoomSettingsWindow.axaml.cs | 8 +- .../KfChatDotNetKickBot.csproj | 2 - KfChatDotNetKickBot/KickBot.cs | 92 +++++----- .../Services/KfTokenService.cs | 4 +- KfChatDotNetWsClient/ChatClient.cs | 19 +- .../KfChatDotNetWsClient.csproj | 1 - .../Models/Json/DeleteMessagesJsonModel.cs | 4 +- .../Models/Json/EditMessageJsonModel.cs | 6 +- .../Models/Json/MessagesJsonModel.cs | 25 ++- .../Models/Json/UsersJsonModel.cs | 13 +- .../Converters/StringOrObjectConverter.cs | 37 ++++ KickWsClient/KickWsClient.cs | 35 ++-- KickWsClient/KickWsClient.csproj | 2 +- KickWsClient/Models/KickModels.cs | 164 +++++++++--------- KickWsClient/Models/PusherModels.cs | 24 +-- 21 files changed, 281 insertions(+), 251 deletions(-) create mode 100644 KickWsClient/Converters/StringOrObjectConverter.cs diff --git a/KfChatDotNetGui/App.axaml.cs b/KfChatDotNetGui/App.axaml.cs index 6f32462..10d7c0c 100644 --- a/KfChatDotNetGui/App.axaml.cs +++ b/KfChatDotNetGui/App.axaml.cs @@ -1,13 +1,13 @@ using System; using System.Collections.ObjectModel; using System.IO; +using System.Text.Json; using Avalonia; using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Markup.Xaml; using KfChatDotNetGui.Models; using KfChatDotNetGui.ViewModels; using KfChatDotNetGui.Views; -using Newtonsoft.Json; using NLog; namespace KfChatDotNetGui @@ -27,7 +27,7 @@ namespace KfChatDotNetGui var dataContext = new MainWindowViewModel(); if (File.Exists("rooms.json")) { - var rooms = JsonConvert.DeserializeObject(File.ReadAllText("rooms.json")); + var rooms = JsonSerializer.Deserialize(File.ReadAllText("rooms.json")); dataContext.RoomList = rooms!.Rooms; } diff --git a/KfChatDotNetGui/Helpers/ForumIdentity.cs b/KfChatDotNetGui/Helpers/ForumIdentity.cs index dc9fb6e..f69aa47 100644 --- a/KfChatDotNetGui/Helpers/ForumIdentity.cs +++ b/KfChatDotNetGui/Helpers/ForumIdentity.cs @@ -2,10 +2,10 @@ using System; using System.Net; using System.Net.Http; using System.Net.Http.Headers; +using System.Text.Json; using System.Text.RegularExpressions; using System.Threading.Tasks; using KfChatDotNetGui.Models; -using Newtonsoft.Json; namespace KfChatDotNetGui.Helpers; @@ -36,7 +36,7 @@ public static class ForumIdentity } var accountJs = match.Groups[1].Value; - return JsonConvert.DeserializeObject(accountJs); + return JsonSerializer.Deserialize(accountJs); } } } \ No newline at end of file diff --git a/KfChatDotNetGui/KfChatDotNetGui.csproj b/KfChatDotNetGui/KfChatDotNetGui.csproj index aa4ad12..9bf0e54 100644 --- a/KfChatDotNetGui/KfChatDotNetGui.csproj +++ b/KfChatDotNetGui/KfChatDotNetGui.csproj @@ -28,7 +28,6 @@ - diff --git a/KfChatDotNetGui/Models/ForumIdentityModel.cs b/KfChatDotNetGui/Models/ForumIdentityModel.cs index 79e7097..3c84c48 100644 --- a/KfChatDotNetGui/Models/ForumIdentityModel.cs +++ b/KfChatDotNetGui/Models/ForumIdentityModel.cs @@ -1,20 +1,20 @@ using System; using System.Collections.Generic; -using Newtonsoft.Json; +using System.Text.Json.Serialization; namespace KfChatDotNetGui.Models; public class ForumIdentityModel { - [JsonProperty("id")] + [JsonPropertyName("id")] public int Id { get; set; } - [JsonProperty("username")] + [JsonPropertyName("username")] public string Username { get; set; } - [JsonProperty("avatar_url")] + [JsonPropertyName("avatar_url")] public Uri AvatarUrl { get; set; } // Guessing it'll be the user ID as an int but no idea as this list is empty for me - [JsonProperty("ignored_users")] + [JsonPropertyName("ignored_users")] public List IgnoredUsers { get; set; } - [JsonProperty("is_staff")] + [JsonPropertyName("is_staff")] public bool IsStaff { get; set; } } \ No newline at end of file diff --git a/KfChatDotNetGui/Views/IdentitySettingsWindow.axaml.cs b/KfChatDotNetGui/Views/IdentitySettingsWindow.axaml.cs index 5d228f1..b0e9d16 100644 --- a/KfChatDotNetGui/Views/IdentitySettingsWindow.axaml.cs +++ b/KfChatDotNetGui/Views/IdentitySettingsWindow.axaml.cs @@ -1,5 +1,6 @@ using System; using System.IO; +using System.Text.Json; using System.Threading.Tasks; using Avalonia; using Avalonia.Controls; @@ -10,7 +11,6 @@ using Avalonia.Threading; using KfChatDotNetGui.Helpers; using KfChatDotNetGui.Models; using KfChatDotNetGui.ViewModels; -using Newtonsoft.Json; using NLog; namespace KfChatDotNetGui.Views; @@ -44,7 +44,8 @@ public partial class IdentitySettingsWindow : Window AntiDdosPow = (DataContext as IdentitySettingsWindowViewModel).AntiDdosPow, Username = (DataContext as IdentitySettingsWindowViewModel).Username }; - File.WriteAllText("settings.json", JsonConvert.SerializeObject(settings, Formatting.Indented)); + var options = new JsonSerializerOptions { WriteIndented = true }; + File.WriteAllText("settings.json", JsonSerializer.Serialize(settings, options)); } catch (Exception ex) { diff --git a/KfChatDotNetGui/Views/MainWindow.axaml.cs b/KfChatDotNetGui/Views/MainWindow.axaml.cs index bd9c38b..11390c8 100644 --- a/KfChatDotNetGui/Views/MainWindow.axaml.cs +++ b/KfChatDotNetGui/Views/MainWindow.axaml.cs @@ -4,14 +4,12 @@ using System.Collections.ObjectModel; using System.IO; using System.Linq; using System.Net; +using System.Text.Json; using System.Threading.Tasks; -using Avalonia; using Avalonia.Controls; using Avalonia.Input; using Avalonia.Interactivity; using Avalonia.LogicalTree; -using Avalonia.Markup.Xaml; -using Avalonia.Media.Imaging; using Avalonia.Threading; using KfChatDotNetGui.Models; using KfChatDotNetGui.ViewModels; @@ -19,7 +17,6 @@ using KfChatDotNetWsClient; using KfChatDotNetWsClient.Models; using KfChatDotNetWsClient.Models.Events; using KfChatDotNetWsClient.Models.Json; -using Newtonsoft.Json; using NLog; using Websocket.Client; @@ -31,9 +28,9 @@ namespace KfChatDotNetGui.Views // Using an empty config as we can update it later through the UpdateConfig method // Having this instance created early is handy for wiring up the events private ChatClient _chatClient = new(new ChatClientConfigModel()); - private SettingsModel _settings; + private SettingsModel _settings = null!; private int _currentRoom; - private ForumIdentityModel _forumIdentity; + private ForumIdentityModel? _forumIdentity = null!; public MainWindow() { @@ -62,7 +59,7 @@ namespace KfChatDotNetGui.Views { _logger.Info($"Received delete event for following message IDs: {string.Join(',', messageIds)}"); // Gotta make a copy of all the messages (annoyingly) as we'll be deleting stuff and .NET has a very obvious limitation there - var messages = (DataContext as MainWindowViewModel).Messages.ToList(); + var messages = ((DataContext as MainWindowViewModel)!).Messages.ToList(); foreach (var message in messages) { foreach (var innerMessage in message.Messages.Where(m => messageIds.Contains(m.MessageId))) @@ -71,13 +68,13 @@ namespace KfChatDotNetGui.Views if (message.Messages.Count == 1) { _logger.Info("Removing parent message box"); - (DataContext as MainWindowViewModel).Messages.Remove(message); + ((DataContext as MainWindowViewModel)!).Messages.Remove(message); } // Go scavenging if there are multiple messages and we don't want to lose the lot else { - (DataContext as MainWindowViewModel) - .Messages[(DataContext as MainWindowViewModel).Messages.IndexOf(message)].Messages + ((DataContext as MainWindowViewModel)!) + .Messages[((DataContext as MainWindowViewModel)!).Messages.IndexOf(message)].Messages .Remove(innerMessage); } @@ -100,8 +97,8 @@ namespace KfChatDotNetGui.Views Dispatcher.UIThread.InvokeAsync(() => { UpdateStatus("Reconnected to SneedChat. Reason was " + reconnectionInfo.Type); - (DataContext as MainWindowViewModel).Messages.Clear(); - (DataContext as MainWindowViewModel).UserList.Clear(); + ((DataContext as MainWindowViewModel)!).Messages.Clear(); + ((DataContext as MainWindowViewModel)!).UserList.Clear(); }); _chatClient.JoinRoom(_currentRoom); @@ -112,8 +109,8 @@ namespace KfChatDotNetGui.Views var context = new IdentitySettingsWindowViewModel(); if (File.Exists("settings.json")) { - var settings = JsonConvert.DeserializeObject(File.ReadAllText("settings.json")); - context.WsUri = settings.WsUri; + var settings = JsonSerializer.Deserialize(File.ReadAllText("settings.json")); + context.WsUri = settings!.WsUri; context.XfSessionToken = settings.XfSessionToken; context.ReconnectTimeout = settings.ReconnectTimeout; context.AntiDdosPow = settings.AntiDdosPow; @@ -146,13 +143,13 @@ namespace KfChatDotNetGui.Views } ReloadSettings(); UpdateStatus("Testing XenForo token validity"); - ForumIdentityModel forumIdentity; + ForumIdentityModel? forumIdentity; if (string.IsNullOrEmpty(_settings.Username)) { try { - forumIdentity = await Helpers.ForumIdentity.GetForumIdentity(_settings.XfSessionToken, - new Uri($"https://{_settings.WsUri.Host}/test-chat"), _settings.AntiDdosPow); + forumIdentity = (await Helpers.ForumIdentity.GetForumIdentity(_settings.XfSessionToken, + new Uri($"https://{_settings.WsUri.Host}/test-chat"), _settings.AntiDdosPow)); } catch (Exception ex) { @@ -184,12 +181,12 @@ namespace KfChatDotNetGui.Views UpdateStatus("Token works! It belongs to " + forumIdentity.Username); _forumIdentity = forumIdentity; - (DataContext as MainWindowViewModel).UserId = _forumIdentity.Id; + ((DataContext as MainWindowViewModel)!).UserId = _forumIdentity.Id; var roomListControl = this.FindControl("RoomList"); RoomSettingsModel.RoomList initialRoom; - if (roomListControl.SelectedItem == null) + if (roomListControl!.SelectedItem == null) { - initialRoom = (DataContext as MainWindowViewModel).RoomList.First(); + initialRoom = (DataContext as MainWindowViewModel)?.RoomList.First()!; } else { @@ -205,7 +202,7 @@ namespace KfChatDotNetGui.Views }); await _chatClient.StartWsClient(); - _chatClient.JoinRoom(initialRoom.Id); + _chatClient.JoinRoom(initialRoom!.Id); _currentRoom = initialRoom.Id; UpdateStatus("Connected!"); } @@ -216,12 +213,12 @@ namespace KfChatDotNetGui.Views { foreach (var user in users) { - if ((DataContext as MainWindowViewModel).UserList.FirstOrDefault(x => x.Id == user.Id) != null) + if (((DataContext as MainWindowViewModel)!).UserList.FirstOrDefault(x => x.Id == user.Id) != null) { _logger.Info($"{user.Username} ({user.Id}) is already in the list but has joined again. New tab? Ignoring!"); continue; } - (DataContext as MainWindowViewModel).UserList.Add(new MainWindowViewModel.UserListViewModel + ((DataContext as MainWindowViewModel)!).UserList.Add(new MainWindowViewModel.UserListViewModel { Id = user.Id, Name = user.Username @@ -237,13 +234,13 @@ namespace KfChatDotNetGui.Views { foreach (var id in userIds) { - var row = (DataContext as MainWindowViewModel).UserList.FirstOrDefault(x => x.Id == id); + var row = ((DataContext as MainWindowViewModel)!).UserList.FirstOrDefault(x => x.Id == id); if (row == null) { _logger.Info($"A user ({id}) who isn't in the list has parted, ignoring!"); continue; } - (DataContext as MainWindowViewModel).UserList.Remove(row); + ((DataContext as MainWindowViewModel)!).UserList.Remove(row); } UpdateUserTotalStatus(); }); @@ -253,7 +250,7 @@ namespace KfChatDotNetGui.Views { Dispatcher.UIThread.InvokeAsync(() => { - var previousMessage = (DataContext as MainWindowViewModel).Messages.LastOrDefault(); + var previousMessage = ((DataContext as MainWindowViewModel)!).Messages.LastOrDefault(); if (previousMessage == null) { previousMessage = new MainWindowViewModel.MessageViewModel {AuthorId = -1}; @@ -261,7 +258,6 @@ namespace KfChatDotNetGui.Views foreach (var message in messages) { _logger.Info("Received message, data payload next"); - _logger.Info(JsonConvert.SerializeObject(message, Formatting.Indented)); if (message.RoomId != _currentRoom) { _logger.Info($"Message {message.MessageId} belongs to another room (we're in {_currentRoom}, this one was for {message.RoomId}), ignoring."); @@ -272,7 +268,7 @@ namespace KfChatDotNetGui.Views { _logger.Info("Received an edit. Going to rewrite message if it already exists, " + "if it doesn't, nothing will happen as this would occur when loading historically modified messages."); - foreach (var msg in (DataContext as MainWindowViewModel).Messages) + foreach (var msg in ((DataContext as MainWindowViewModel)!).Messages) { foreach (var innerMsg in msg.Messages.Where(m => m.MessageId == message.MessageId)) { @@ -287,7 +283,7 @@ namespace KfChatDotNetGui.Views if (previousMessage.AuthorId == message.Author.Id) { _logger.Info("Found a message from the same author, merging"); - var lastMessage = (DataContext as MainWindowViewModel).Messages.Last(); + var lastMessage = ((DataContext as MainWindowViewModel)!).Messages.Last(); lastMessage.Messages.Add(new MainWindowViewModel.InnerMessageViewModel { Message = WebUtility.HtmlDecode(message.MessageRaw), @@ -312,11 +308,11 @@ namespace KfChatDotNetGui.Views PostedAt = message.MessageDate.LocalDateTime, AuthorId = message.Author.Id }; - (DataContext as MainWindowViewModel).Messages.Add(viewMessage); + ((DataContext as MainWindowViewModel)!).Messages.Add(viewMessage); previousMessage = viewMessage; } var messagesControl = this.FindControl("ChatMessageList"); - messagesControl.ScrollIntoView((DataContext as MainWindowViewModel).Messages.Last()); + messagesControl!.ScrollIntoView(((DataContext as MainWindowViewModel)!).Messages.Last()); }); } @@ -335,7 +331,7 @@ namespace KfChatDotNetGui.Views var context = new RoomSettingsWindowViewModel(); if (File.Exists("rooms.json")) { - var settings = JsonConvert.DeserializeObject(File.ReadAllText("rooms.json")); + var settings = JsonSerializer.Deserialize(File.ReadAllText("rooms.json")); context.RoomList.Clear(); foreach (var room in settings.Rooms) { @@ -364,7 +360,7 @@ namespace KfChatDotNetGui.Views _logger.Error("Was asked to reload the settings but settings.json doesn't exist so I won't bother"); return; } - var settings = JsonConvert.DeserializeObject(File.ReadAllText("settings.json")); + var settings = JsonSerializer.Deserialize(File.ReadAllText("settings.json")); _settings = settings; } @@ -375,7 +371,7 @@ namespace KfChatDotNetGui.Views _logger.Error("Was asked to reload the room list but rooms.json doesn't exist so I won't bother"); return; } - var rooms = JsonConvert.DeserializeObject(File.ReadAllText("rooms.json")); + var rooms = JsonSerializer.Deserialize(File.ReadAllText("rooms.json")); (DataContext as MainWindowViewModel)!.RoomList = rooms!.Rooms; } @@ -395,8 +391,8 @@ namespace KfChatDotNetGui.Views return; } UpdateStatus($"Connected! Changing to {room.Name}"); - (DataContext as MainWindowViewModel).Messages.Clear(); - (DataContext as MainWindowViewModel).UserList.Clear(); + ((DataContext as MainWindowViewModel)!).Messages.Clear(); + (DataContext as MainWindowViewModel)?.UserList.Clear(); _chatClient.JoinRoom(room.Id); _currentRoom = room.Id; } @@ -468,7 +464,7 @@ namespace KfChatDotNetGui.Views private void OuterMessageRow_OnPointerEnter(object? sender, PointerEventArgs e) { - var children = (e.Source as ListBox).GetLogicalChildren().Cast(); + var children = ((e.Source as ListBox)!).GetLogicalChildren().Cast(); foreach (var child in children) { // Bit of a ghetto hack but it ensures that there's only ever one subscriber diff --git a/KfChatDotNetGui/Views/RoomSettingsWindow.axaml.cs b/KfChatDotNetGui/Views/RoomSettingsWindow.axaml.cs index b26b5eb..4d2dae2 100644 --- a/KfChatDotNetGui/Views/RoomSettingsWindow.axaml.cs +++ b/KfChatDotNetGui/Views/RoomSettingsWindow.axaml.cs @@ -6,6 +6,7 @@ using System.Linq; using System.Net; using System.Net.Http; using System.Net.Http.Headers; +using System.Text.Json; using System.Threading.Tasks; using Avalonia; using Avalonia.Controls; @@ -16,7 +17,6 @@ using Avalonia.Threading; using HtmlAgilityPack; using KfChatDotNetGui.Models; using KfChatDotNetGui.ViewModels; -using Newtonsoft.Json; using NLog; namespace KfChatDotNetGui.Views; @@ -54,7 +54,9 @@ public partial class RoomSettingsWindow : Window Name = room.Name }); } - File.WriteAllText("rooms.json", JsonConvert.SerializeObject(roomSettings, Formatting.Indented)); + + File.WriteAllText("rooms.json", + JsonSerializer.Serialize(roomSettings, new JsonSerializerOptions { WriteIndented = true })); } catch (Exception ex) { @@ -102,7 +104,7 @@ public partial class RoomSettingsWindow : Window var kfDomain = "kiwifarms.net"; if (File.Exists("settings.json")) { - var settings = JsonConvert.DeserializeObject(await File.ReadAllTextAsync("settings.json")); + var settings = JsonSerializer.Deserialize(await File.ReadAllTextAsync("settings.json")); kfDomain = settings.WsUri.Host; } diff --git a/KfChatDotNetKickBot/KfChatDotNetKickBot.csproj b/KfChatDotNetKickBot/KfChatDotNetKickBot.csproj index 2eaf491..f10df68 100644 --- a/KfChatDotNetKickBot/KfChatDotNetKickBot.csproj +++ b/KfChatDotNetKickBot/KfChatDotNetKickBot.csproj @@ -8,10 +8,8 @@ - - diff --git a/KfChatDotNetKickBot/KickBot.cs b/KfChatDotNetKickBot/KickBot.cs index 86a364d..4bfe329 100644 --- a/KfChatDotNetKickBot/KickBot.cs +++ b/KfChatDotNetKickBot/KickBot.cs @@ -1,32 +1,31 @@ -using KfChatDotNetKickBot.Services; +using System.Text.Json; +using KfChatDotNetKickBot.Services; using KfChatDotNetWsClient; using KfChatDotNetWsClient.Models; using KfChatDotNetWsClient.Models.Events; using KfChatDotNetWsClient.Models.Json; using KickWsClient.Models; -using Newtonsoft.Json; using NLog; -using Spectre.Console; using Websocket.Client; namespace KfChatDotNetKickBot; public class KickBot { - private ChatClient _kfClient; - private KickWsClient.KickWsClient _kickClient; - private Logger _logger = LogManager.GetCurrentClassLogger(); - private Models.ConfigModel _config; - private Thread _pingThread; - private bool _pingEnabled = true; - private bool _gambaSeshPresent = false; - private string _xfSessionToken; + private readonly ChatClient _kfClient; + private readonly KickWsClient.KickWsClient _kickClient; + private readonly Logger _logger = LogManager.GetCurrentClassLogger(); + private readonly Models.ConfigModel _config; + private readonly bool _pingEnabled = true; + private bool _gambaSeshPresent; + private string _xfSessionToken = null!; // Oh no it's an ever expanding list that may never get cleaned up! // BUY MORE RAM - private List _seenMsgIds = new List(); - // Suppresses the command handler on initial start so it doesn't pick up things already handled on restart + private readonly List _seenMsgIds = []; + // Suppresses the command handler on initial start, so it doesn't pick up things already handled on restart private bool _initialStartCooldown = true; - + private readonly CancellationToken _cancellationToken = new(); + public KickBot() { _logger.Info("Bot starting!"); @@ -37,9 +36,9 @@ public class KickBot Environment.Exit(1); } - _config = JsonConvert.DeserializeObject(File.ReadAllText(configPath)) ?? + _config = JsonSerializer.Deserialize(File.ReadAllText(configPath)) ?? throw new InvalidOperationException(); - RefreshXfToken(); + RefreshXfToken().Wait(_cancellationToken); _kfClient = new ChatClient(new ChatClientConfigModel { @@ -66,22 +65,22 @@ public class KickBot _kickClient.OnPusherSubscriptionSucceeded += OnPusherSubscriptionSucceeded; _kickClient.OnStopStreamBroadcast += OnStopStreamBroadcast; - _kfClient.StartWsClient().Wait(); + _kfClient.StartWsClient().Wait(_cancellationToken); _kfClient.JoinRoom(_config.KfChatRoomId); - _kickClient.StartWsClient().Wait(); + _kickClient.StartWsClient().Wait(_cancellationToken); foreach (var channel in _config.PusherChannels) { _kickClient.SendPusherSubscribe(channel); } - _pingThread = new Thread(PingThread); - _pingThread.Start(); + _logger.Debug("Creating ping thread and starting it"); + var pingThread = new Thread(PingThread); + pingThread.Start(); while (true) { - var input = AnsiConsole.Prompt(new TextPrompt("Enter Message:")); - _kfClient.SendMessage(input); + Console.ReadLine(); } } @@ -89,11 +88,11 @@ public class KickBot { _logger.Error($"Couldn't join the room. KF returned: {message}"); _logger.Error("This is likely due to the session cookie expiring. Retrieving a new one."); - RefreshXfToken(); + RefreshXfToken().Wait(_cancellationToken); _kfClient.UpdateToken(_xfSessionToken); _logger.Info("Retrieved fresh token. Reconnecting."); _kfClient.Disconnect(); - _kfClient.StartWsClient().Wait(); + _kfClient.StartWsClient().Wait(_cancellationToken); _logger.Info("Client should be reconnecting now"); } @@ -109,24 +108,22 @@ public class KickBot } } - private void RefreshXfToken() + private async Task RefreshXfToken() { - var cookie = KfTokenService.FetchSessionTokenAsync(_config.KfDomain, _config.KfUsername, _config.KfPassword, - _config.ChromiumPath, _config.KfProxy).Result; + var cookie = await KfTokenService.FetchSessionTokenAsync(_config.KfDomain, _config.KfUsername, _config.KfPassword, + _config.ChromiumPath, _config.KfProxy); _logger.Debug($"FetchSessionTokenAsync returned {cookie}"); _xfSessionToken = cookie; } private void OnStreamerIsLive(object sender, KickModels.StreamerIsLiveEventModel? e) { - AnsiConsole.MarkupLine("[green]Streamer is live!!![/]"); - _sendChatMessage($"Bossman Live! {e?.Livestream.SessionTitle} https://kick.com/bossmanjack [i]This action was automated"); + _sendChatMessage($"Bossman Live! {e?.Livestream.SessionTitle} https://kick.com/bossmanjack"); } private void OnStopStreamBroadcast(object sender, KickModels.StopStreamBroadcastEventModel? e) { - AnsiConsole.MarkupLine("[green]Stream stopped!!![/]"); - _sendChatMessage("The stream is so over. [i]This action was automated"); + _sendChatMessage("The stream is so over. :lossmanjack:"); } private void OnKfChatMessage(object sender, List messages, MessagesJsonModel jsonPayload) @@ -134,7 +131,7 @@ public class KickBot _logger.Debug($"Received {messages.Count} message(s)"); foreach (var message in messages) { - AnsiConsole.MarkupLine($"[yellow]KF[/] <{message.Author.Username}> {message.Message.EscapeMarkup()} ({message.MessageDate.LocalDateTime.ToShortTimeString()})"); + _logger.Info($"KF ({message.MessageDate.ToLocalTime():HH:mm:ss}) <{message.Author.Username}> {message.Message}"); if (!_seenMsgIds.Contains(message.MessageId) && !_initialStartCooldown) { if (message.MessageRaw.StartsWith("!time")) @@ -149,16 +146,17 @@ public class KickBot } else if (message.MessageRaw.StartsWith("!insanity")) { + // ReSharper disable once StringLiteralTypo _sendChatMessage("definition of insanity = doing the same thing over and over and over excecting a different result, and heres my dumbass trying to get rich every day and losing everythign i fucking touch every fucking time FUCK this bullshit FUCK MY LIEFdefinition of insanity = doing the same thing over and over and over excecting a different result, and heres my dumbass trying to get rich every day and losing everythign i fucking touch every fucking time FUCK this bullshit FUCK MY LIEF"); } - else if (message.MessageRaw.StartsWith("!help")) + else if (message.MessageRaw.StartsWith("!helpme")) { _sendChatMessage("[img]https://i.postimg.cc/fTw6tGWZ/ineedmoneydumbfuck.png[/img]", true); } } else { - AnsiConsole.MarkupLine($"[blue]_seenMsgIds check => {!_seenMsgIds.Contains(message.MessageId)}, _initialStartCooldown => {_initialStartCooldown}[/]"); + _logger.Debug($"_seenMsgIds check => {!_seenMsgIds.Contains(message.MessageId)}, _initialStartCooldown => {_initialStartCooldown}"); } _seenMsgIds.Add(message.MessageId); } @@ -168,7 +166,7 @@ public class KickBot { if (_gambaSeshPresent && _config.EnableGambaSeshDetect && !bypassSeshDetect) { - AnsiConsole.MarkupLine($"[red]Not sending message '{message.EscapeMarkup()}' as GambaSesh is present[/]"); + _logger.Info($"Not sending message '{message}' as GambaSesh is present"); return; } @@ -177,15 +175,14 @@ public class KickBot private void OnKickChatMessage(object sender, KickModels.ChatMessageEventModel? e) { - if (e == null) return; - AnsiConsole.MarkupLine($"[green]Kick[/] <{e.Sender.Username}> {e.Content.EscapeMarkup()} ({e.CreatedAt.LocalDateTime.ToShortTimeString()})"); - AnsiConsole.MarkupLine($"[cyan]BB Code Translation: {e.Content.TranslateKickEmotes().EscapeMarkup()}[/]"); + if (e == null) return; + _logger.Info($"Kick ({e.CreatedAt.LocalDateTime.ToLocalTime():HH:mm:ss}) <{e.Sender.Username}> {e.Content}"); + _logger.Debug($"BB Code Translation: {e.Content.TranslateKickEmotes()}"); - if (e.Sender.Slug == "bossmanjack") - { - AnsiConsole.MarkupLine("[green]Message from BossmanJack[/]"); - _sendChatMessage($"[img]{_config.KickIcon}[/img] BossmanJack: {e.Content.TranslateKickEmotes()}"); - } + if (e.Sender.Slug != "bossmanjack") return; + + _logger.Debug("Message from BossmanJack"); + _sendChatMessage($"[img]{_config.KickIcon}[/img] BossmanJack: {e.Content.TranslateKickEmotes()}"); } private void OnUsersJoined(object sender, List users, UsersJsonModel jsonPayload) @@ -195,9 +192,10 @@ public class KickBot { if (user.Id == _config.GambaSeshUserId && _config.EnableGambaSeshDetect) { + _logger.Info("GambaSesh is now present"); _gambaSeshPresent = true; } - AnsiConsole.MarkupLine($"[green]{user.Username.EscapeMarkup()} joined![/]"); + _logger.Info($"{user.Username} joined!"); } } @@ -205,13 +203,9 @@ public class KickBot { if (userIds.Contains(_config.GambaSeshUserId) && _config.EnableGambaSeshDetect) { + _logger.Info("GambaSesh is no longer present"); _gambaSeshPresent = false; } - _logger.Debug($"Received {userIds.Count} user part events"); - foreach (var id in userIds) - { - AnsiConsole.MarkupLine($"[red]{id} left the chat...[/]"); - } } private void OnKfWsDisconnected(object sender, DisconnectionInfo disconnectionInfo) diff --git a/KfChatDotNetKickBot/Services/KfTokenService.cs b/KfChatDotNetKickBot/Services/KfTokenService.cs index edd2855..2a3e96e 100644 --- a/KfChatDotNetKickBot/Services/KfTokenService.cs +++ b/KfChatDotNetKickBot/Services/KfTokenService.cs @@ -40,11 +40,11 @@ public class KfTokenService await page.WaitForSelectorAsync("img[alt=\"Kiwi Farms\"]"); if (await page.QuerySelectorAsync("html[data-template=\"login\"]") == null) { - logger.Debug("Page template is not login. This is expected if we're already logged in. Retrieving cookies"); + logger.Debug("Page template is not login. This is expected if we're already logged in. Reloading page to get the freshest cookies then retrieving"); + await page.ReloadAsync(); return await GetXfSessionCookie(); } - var usernameFieldSelector = await page.QuerySelectorAsync("input[autocomplete=\"username\"]"); var passwordFieldSelector = await page.QuerySelectorAsync("input[autocomplete=\"current-password\"]"); var loginButtonSelector = await page.QuerySelectorAsync("div[class=\"formSubmitRow-controls\"] > button[type=\"submit\"]"); diff --git a/KfChatDotNetWsClient/ChatClient.cs b/KfChatDotNetWsClient/ChatClient.cs index 620bb4a..8e1383f 100644 --- a/KfChatDotNetWsClient/ChatClient.cs +++ b/KfChatDotNetWsClient/ChatClient.cs @@ -1,9 +1,10 @@ using System.Net; using System.Net.WebSockets; +using System.Text.Json; +using System.Xml; using KfChatDotNetWsClient.Models; using KfChatDotNetWsClient.Models.Events; using KfChatDotNetWsClient.Models.Json; -using Newtonsoft.Json; using NLog; using Websocket.Client; // It's a fucking lie. You must use conditional access or you WILL get NullReferenceErrors if an event is not in use @@ -135,7 +136,7 @@ public class ChatClient Dictionary packetType = new Dictionary(); try { - packetType = JsonConvert.DeserializeObject>(message.Text)!; + packetType = JsonSerializer.Deserialize>(message.Text)!; } catch (Exception e) { @@ -198,23 +199,21 @@ public class ChatClient public void EditMessage(int messageId, string newMessage) { - // Explicitly set formatting to none as it must be inline (Newtonsoft will do this by default but just wanting to be explicit) - var payload = JsonConvert.SerializeObject(new EditMessageJsonModel {Id = messageId, Message = newMessage}, - Formatting.None); + var payload = JsonSerializer.Serialize(new EditMessageJsonModel {Id = messageId, Message = newMessage}); _logger.Debug($"Editing {messageId} with '{newMessage}'"); _wsClient.Send($"/edit {payload}"); } private void WsDeleteMessagesReceived(ResponseMessage message) { - var data = JsonConvert.DeserializeObject(message.Text); + var data = JsonSerializer.Deserialize(message.Text); _logger.Debug($"Received delete packet for messages: {string.Join(',', data.MessageIdsToDelete)}"); OnDeleteMessages?.Invoke(this, data.MessageIdsToDelete); } private void WsChatMessagesReceived(ResponseMessage message) { - var data = JsonConvert.DeserializeObject(message.Text); + var data = JsonSerializer.Deserialize(message.Text); var messages = new List(); foreach (var chatMessage in data.Messages) { @@ -250,14 +249,14 @@ public class ChatClient _logger.Debug($"Received {messages.Count} chat messages"); if (messages.Count == 1) { - _logger.Debug($"{JsonConvert.SerializeObject(messages[0], Formatting.Indented)}"); + _logger.Debug($"{JsonSerializer.Serialize(messages[0])}"); } OnMessages?.Invoke(this, messages, data); } private void WsChatUsersJoined(ResponseMessage message) { - var data = JsonConvert.DeserializeObject(message.Text); + var data = JsonSerializer.Deserialize(message.Text); var users = new List(); foreach (var user in data.Users.Keys) { @@ -277,7 +276,7 @@ public class ChatClient private void WsChatUsersParted(ResponseMessage message) { // {"user":{"1337":false}} - var data = JsonConvert.DeserializeObject>>(message.Text); + var data = JsonSerializer.Deserialize>>(message.Text); var usersParted = data!["user"].Select(user => int.Parse(user.Key)).ToList(); _logger.Debug($"Following users have parted: {string.Join(',', usersParted)}"); OnUsersParted?.Invoke(this, usersParted); diff --git a/KfChatDotNetWsClient/KfChatDotNetWsClient.csproj b/KfChatDotNetWsClient/KfChatDotNetWsClient.csproj index c6dee9c..ccd82d3 100644 --- a/KfChatDotNetWsClient/KfChatDotNetWsClient.csproj +++ b/KfChatDotNetWsClient/KfChatDotNetWsClient.csproj @@ -8,7 +8,6 @@ - diff --git a/KfChatDotNetWsClient/Models/Json/DeleteMessagesJsonModel.cs b/KfChatDotNetWsClient/Models/Json/DeleteMessagesJsonModel.cs index 30aa5bd..5d31a05 100644 --- a/KfChatDotNetWsClient/Models/Json/DeleteMessagesJsonModel.cs +++ b/KfChatDotNetWsClient/Models/Json/DeleteMessagesJsonModel.cs @@ -1,9 +1,9 @@ -using Newtonsoft.Json; +using System.Text.Json.Serialization; namespace KfChatDotNetWsClient.Models.Json; public class DeleteMessagesJsonModel { - [JsonProperty("delete")] + [JsonPropertyName("delete")] public List MessageIdsToDelete { get; set; } } \ No newline at end of file diff --git a/KfChatDotNetWsClient/Models/Json/EditMessageJsonModel.cs b/KfChatDotNetWsClient/Models/Json/EditMessageJsonModel.cs index 7539ad8..74fe4f7 100644 --- a/KfChatDotNetWsClient/Models/Json/EditMessageJsonModel.cs +++ b/KfChatDotNetWsClient/Models/Json/EditMessageJsonModel.cs @@ -1,12 +1,12 @@ -using Newtonsoft.Json; +using System.Text.Json.Serialization; namespace KfChatDotNetWsClient.Models.Json; public class EditMessageJsonModel { - [JsonProperty("id")] + [JsonPropertyName("id")] public int Id { get; set; } - [JsonProperty("message")] + [JsonPropertyName("message")] public string Message { get; set; } } \ No newline at end of file diff --git a/KfChatDotNetWsClient/Models/Json/MessagesJsonModel.cs b/KfChatDotNetWsClient/Models/Json/MessagesJsonModel.cs index bd7c501..c9942d1 100644 --- a/KfChatDotNetWsClient/Models/Json/MessagesJsonModel.cs +++ b/KfChatDotNetWsClient/Models/Json/MessagesJsonModel.cs @@ -1,5 +1,4 @@ -using Newtonsoft.Json; -using Newtonsoft.Json.Converters; +using System.Text.Json.Serialization; namespace KfChatDotNetWsClient.Models.Json; @@ -28,32 +27,32 @@ public class MessagesJsonModel { public class AuthorModel { - [JsonProperty("id")] + [JsonPropertyName("id")] public int Id { get; set; } - [JsonProperty("username")] + [JsonPropertyName("username")] public string Username { get; set; } - [JsonProperty("avatar_url")] + [JsonPropertyName("avatar_url")] public Uri AvatarUrl { get; set; } } public class MessageModel { - [JsonProperty("author")] + [JsonPropertyName("author")] public AuthorModel Author { get; set; } - [JsonProperty("message")] + [JsonPropertyName("message")] public string Message { get; set; } - [JsonProperty("message_id")] + [JsonPropertyName("message_id")] public int MessageId { get; set; } - [JsonProperty("message_edit_date")] + [JsonPropertyName("message_edit_date")] public int MessageEditDate { get; set; } - [JsonProperty("message_date")] + [JsonPropertyName("message_date")] public int MessageDate { get; set; } - [JsonProperty("message_raw")] + [JsonPropertyName("message_raw")] public string MessageRaw { get; set; } - [JsonProperty("room_id")] + [JsonPropertyName("room_id")] public int RoomId { get; set; } } - [JsonProperty("messages")] + [JsonPropertyName("messages")] public List Messages { get; set; } } \ No newline at end of file diff --git a/KfChatDotNetWsClient/Models/Json/UsersJsonModel.cs b/KfChatDotNetWsClient/Models/Json/UsersJsonModel.cs index e787a3a..4de06cb 100644 --- a/KfChatDotNetWsClient/Models/Json/UsersJsonModel.cs +++ b/KfChatDotNetWsClient/Models/Json/UsersJsonModel.cs @@ -1,5 +1,4 @@ -using Newtonsoft.Json; - +using System.Text.Json.Serialization; namespace KfChatDotNetWsClient.Models.Json; // { @@ -17,16 +16,16 @@ public class UsersJsonModel { public class UserModel { - [JsonProperty("id")] + [JsonPropertyName("id")] public int Id { get; set; } - [JsonProperty("username")] + [JsonPropertyName("username")] public string Username { get; set; } - [JsonProperty("avatar_url")] + [JsonPropertyName("avatar_url")] public Uri AvatarUrl { get; set; } - [JsonProperty("last_activity")] + [JsonPropertyName("last_activity")] public int LastActivity { get; set; } } - [JsonProperty("users")] + [JsonPropertyName("users")] public Dictionary Users { get; set; } } \ No newline at end of file diff --git a/KickWsClient/Converters/StringOrObjectConverter.cs b/KickWsClient/Converters/StringOrObjectConverter.cs new file mode 100644 index 0000000..4663167 --- /dev/null +++ b/KickWsClient/Converters/StringOrObjectConverter.cs @@ -0,0 +1,37 @@ +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace KickWsClient.Converters; + +// Literal mess, but it is AI generated so what do you expect. Couldn't be bothered writing this myself. +public class StringOrObjectConverter : JsonConverter +{ + public override bool CanConvert(Type typeToConvert) + { + return typeof(T).IsAssignableFrom(typeToConvert) || (typeof(T) == typeof(string) && typeToConvert == typeof(object)); + } + + public override T Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + if (reader.TokenType == JsonTokenType.String) + { + return (T)(object)reader.GetString()!; + } + else + { + return JsonSerializer.Deserialize(ref reader, options)!; + } + } + + public override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options) + { + if (value is string) + { + writer.WriteStringValue((string)(object)value); + } + else + { + JsonSerializer.Serialize(writer, value, options); + } + } +} \ No newline at end of file diff --git a/KickWsClient/KickWsClient.cs b/KickWsClient/KickWsClient.cs index a6c23da..7a2f7d3 100644 --- a/KickWsClient/KickWsClient.cs +++ b/KickWsClient/KickWsClient.cs @@ -1,7 +1,8 @@ using System.Net; using System.Net.WebSockets; +using System.Text.Json; +using System.Text.Json.Serialization; using KickWsClient.Models; -using Newtonsoft.Json; using Websocket.Client; using NLog; @@ -114,7 +115,7 @@ public class KickWsClient public void SendPusherPacket(string eventName, object data) { var pkt = new PusherModels.BasePusherRequestModel { Event = eventName, Data = data}; - var json = JsonConvert.SerializeObject(pkt); + var json = JsonSerializer.Serialize(pkt); _logger.Debug("Sending message to Pusher"); _logger.Debug(json); _wsClient.Send(json); @@ -152,6 +153,10 @@ public class KickWsClient private void WsMessageReceived(ResponseMessage message) { OnWsMessageReceived?.Invoke(this, message); + var options = new JsonSerializerOptions + { + NumberHandling = JsonNumberHandling.AllowReadingFromString + }; if (message.Text == null) { @@ -162,7 +167,7 @@ public class KickWsClient PusherModels.BasePusherEventModel pusherMsg; try { - pusherMsg = JsonConvert.DeserializeObject(message.Text) ?? + pusherMsg = JsonSerializer.Deserialize(message.Text, options) ?? throw new InvalidOperationException(); } catch (Exception e) @@ -182,7 +187,7 @@ public class KickWsClient case "pusher:connection_established": { var data = - JsonConvert.DeserializeObject(pusherMsg.Data); + JsonSerializer.Deserialize(pusherMsg.Data, options); OnPusherConnectionEstablished?.Invoke(this, data); return; } @@ -194,67 +199,67 @@ public class KickWsClient return; case @"App\Events\FollowersUpdated": { - var data = JsonConvert.DeserializeObject(pusherMsg.Data); + var data = JsonSerializer.Deserialize(pusherMsg.Data, options); OnFollowersUpdated?.Invoke(this, data); return; } case @"App\Events\ChatMessageEvent": { - var data = JsonConvert.DeserializeObject(pusherMsg.Data); + var data = JsonSerializer.Deserialize(pusherMsg.Data, options); OnChatMessage?.Invoke(this, data); return; } case @"App\Events\ChannelSubscriptionEvent": { - var data = JsonConvert.DeserializeObject(pusherMsg.Data); + var data = JsonSerializer.Deserialize(pusherMsg.Data, options); OnChannelSubscription?.Invoke(this, data); return; } case @"App\Events\SubscriptionEvent": { - var data = JsonConvert.DeserializeObject(pusherMsg.Data); + var data = JsonSerializer.Deserialize(pusherMsg.Data, options); OnSubscription?.Invoke(this, data); return; } case @"App\Events\MessageDeletedEvent": { - var data = JsonConvert.DeserializeObject(pusherMsg.Data); + var data = JsonSerializer.Deserialize(pusherMsg.Data, options); OnMessageDeleted?.Invoke(this, data); return; } case @"App\Events\UserBannedEvent": { - var data = JsonConvert.DeserializeObject(pusherMsg.Data); + var data = JsonSerializer.Deserialize(pusherMsg.Data, options); OnUserBanned?.Invoke(this, data); return; } case @"App\Events\UserUnbannedEvent": { - var data = JsonConvert.DeserializeObject(pusherMsg.Data); + var data = JsonSerializer.Deserialize(pusherMsg.Data, options); OnUserUnbanned?.Invoke(this, data); return; } case @"App\Events\LiveStream\UpdatedLiveStreamEvent": { - var data = JsonConvert.DeserializeObject(pusherMsg.Data); + var data = JsonSerializer.Deserialize(pusherMsg.Data, options); OnUpdatedLiveStream?.Invoke(this, data); return; } case @"App\Events\StopStreamBroadcast": { - var data = JsonConvert.DeserializeObject(pusherMsg.Data); + var data = JsonSerializer.Deserialize(pusherMsg.Data, options); OnStopStreamBroadcast?.Invoke(this, data); return; } case @"App\Events\StreamerIsLive": { - var data = JsonConvert.DeserializeObject(pusherMsg.Data); + var data = JsonSerializer.Deserialize(pusherMsg.Data, options); OnStreamerIsLive?.Invoke(this, data); return; } case @"App\Events\PollUpdateEvent": { - var data = JsonConvert.DeserializeObject(pusherMsg.Data); + var data = JsonSerializer.Deserialize(pusherMsg.Data, options); OnPollUpdate?.Invoke(this, data); return; } diff --git a/KickWsClient/KickWsClient.csproj b/KickWsClient/KickWsClient.csproj index c5c5b08..15fb6a2 100644 --- a/KickWsClient/KickWsClient.csproj +++ b/KickWsClient/KickWsClient.csproj @@ -7,8 +7,8 @@ - + diff --git a/KickWsClient/Models/KickModels.cs b/KickWsClient/Models/KickModels.cs index a225724..653b8d2 100644 --- a/KickWsClient/Models/KickModels.cs +++ b/KickWsClient/Models/KickModels.cs @@ -1,4 +1,4 @@ -using Newtonsoft.Json; +using System.Text.Json.Serialization; namespace KickWsClient.Models; @@ -9,17 +9,17 @@ public class KickModels /// /// Internal type for badge e.g. moderator /// - [JsonProperty("type")] + [JsonPropertyName("type")] public required string Type { get; set; } /// /// Friendly name for badge e.g. Moderator /// - [JsonProperty("text")] + [JsonPropertyName("text")] public required string Text { get; set; } /// /// Count (if applicable) for badge (e.g. sub count for gifted subs) /// - [JsonProperty("count")] + [JsonPropertyName("count")] public int? Count { get; set; } } @@ -28,13 +28,13 @@ public class KickModels /// /// User's hex color /// - [JsonProperty("color")] + [JsonPropertyName("color")] public required string Color { get; set; } /// /// Badges a user has /// - [JsonProperty("badges")] + [JsonPropertyName("badges")] public List Badges = []; } @@ -43,22 +43,22 @@ public class KickModels /// /// Kick internal user ID /// - [JsonProperty("id")] + [JsonPropertyName("id")] public int Id { get; set; } /// /// Kick display name /// - [JsonProperty("username")] + [JsonPropertyName("username")] public required string Username { get; set; } /// /// Kick slug (for URLs) /// - [JsonProperty("slug")] + [JsonPropertyName("slug")] public required string Slug { get; set; } /// /// Identity info for display color and badges /// - [JsonProperty("identity")] + [JsonPropertyName("identity")] public required ChatMessageSenderIdentityModel Identity { get; set; } } @@ -67,12 +67,12 @@ public class KickModels /// /// Original sender's user ID /// - [JsonProperty("id")] + [JsonPropertyName("id")] public int Id { get; set; } /// /// Original sender's username /// - [JsonProperty("username")] + [JsonPropertyName("username")] public required string Username { get; set; } } @@ -81,12 +81,12 @@ public class KickModels /// /// ID (GUID) of the original message /// - [JsonProperty("id")] + [JsonPropertyName("id")] public required string Id { get; set; } /// /// Content of the original message /// - [JsonProperty("content")] + [JsonPropertyName("content")] public required string Content { get; set; } } @@ -95,12 +95,12 @@ public class KickModels /// /// Sender of the message that this message is in reply to /// - [JsonProperty("original_sender")] + [JsonPropertyName("original_sender")] public required ChatMessageMetadataOriginalSenderModel OriginalSender { get; set; } /// /// Content of the message that this message is in reply to /// - [JsonProperty("original_message")] + [JsonPropertyName("original_message")] public required ChatMessageMetadataOriginalMessageModel OriginalMessage { get; set; } } @@ -109,29 +109,29 @@ public class KickModels /// /// Channel follower count /// - [JsonProperty("followersCount")] + [JsonPropertyName("followersCount")] public int FollowersCount { get; set; } /// /// ID to identify what chatroom this event belongs to /// - [JsonProperty("chatroom_id")] + [JsonPropertyName("chatroom_id")] public int ChatroomId { get; set; } /// /// Maybe returns your username if you're auth'd? No idea. Just returned null for me /// - [JsonProperty("username")] + [JsonPropertyName("username")] public string? Username { get; set; } /// /// Epoch value that signifies ??? /// - [JsonProperty("created_at")] + [JsonPropertyName("created_at")] public int? CreatedAtEpoch { get; set; } // It returned true even though I'm not signed in which makes no sense, so I'll assume there's a chance it'll // suddenly appear and mark as nullable as it's not really a useful property anyway. /// /// Does it mean we're following? Who knows, returns true even if you're a guest /// - [JsonProperty("followed")] + [JsonPropertyName("followed")] public bool? Followed { get; set; } } @@ -140,38 +140,38 @@ public class KickModels /// /// Message unique GUID that's referenced for replies and deletions /// - [JsonProperty("id")] + [JsonPropertyName("id")] public required string Id { get; set; } /// /// Chatroom ID you can use to differentiate this from other rooms if you sub to multiple at a time /// - [JsonProperty("chatroom_id")] + [JsonPropertyName("chatroom_id")] public int ChatroomId { get; set; } /// /// Content of the message. Emotes are encoded like [emote:161238:russW] which translates to -> https://files.kick.com/emotes/161238/fullsize /// - [JsonProperty("content")] + [JsonPropertyName("content")] public required string Content { get; set; } /// /// Regular message is 'message', replies are 'reply' /// - [JsonProperty("type")] + [JsonPropertyName("type")] public required string Type { get; set; } // Why created at is an epoch for followers updated but ISO8601 for chat messages is just a mystery /// /// Time message was sent /// - [JsonProperty("created_at")] + [JsonPropertyName("created_at")] public DateTimeOffset CreatedAt { get; set; } /// /// Sender of the message /// - [JsonProperty("sender")] + [JsonPropertyName("sender")] public required ChatMessageSenderModel Sender { get; set; } /// /// Message metadata which is set for replies only /// - [JsonProperty("metadata")] + [JsonPropertyName("metadata")] public ChatMessageMetadataModel? Metadata { get; set; } } @@ -180,17 +180,17 @@ public class KickModels /// /// User IDs of subscription recipients /// - [JsonProperty("user_ids")] + [JsonPropertyName("user_ids")] public List UserIds { get; set; } = []; /// /// Username of the person who subbed / gifted /// - [JsonProperty("username")] + [JsonPropertyName("username")] public required string Username { get; set; } /// /// Channel ID where the sub event occurred /// - [JsonProperty("channel_id")] + [JsonPropertyName("channel_id")] public int ChannelId { get; set; } } @@ -199,17 +199,17 @@ public class KickModels /// /// ID of channel where the subscription event occurred /// - [JsonProperty("chatroom_id")] + [JsonPropertyName("chatroom_id")] public int ChatroomId { get; set; } /// /// Username of the person who bought a sub /// - [JsonProperty("username")] + [JsonPropertyName("username")] public required string Username { get; set; } /// /// Number of months they've subbed now (e.g. 2 if they bought their 2nd month) /// - [JsonProperty("months")] + [JsonPropertyName("months")] public int Months { get; set; } } @@ -218,7 +218,7 @@ public class KickModels /// /// ID of the message that was deleted /// - [JsonProperty("id")] + [JsonPropertyName("id")] public required string Id { get; set; } } @@ -227,12 +227,12 @@ public class KickModels /// /// ID of this event (NOT the message to be removed!) /// - [JsonProperty("id")] + [JsonPropertyName("id")] public required string Id { get; set; } /// /// Message that was deleted /// - [JsonProperty("message")] + [JsonPropertyName("message")] public required MessageDeletedMessageModel Message { get; set; } } @@ -241,17 +241,17 @@ public class KickModels /// /// ID of the user. Note it'll be 0 for the janny /// - [JsonProperty("id")] + [JsonPropertyName("id")] public int Id { get; set; } /// /// User's username /// - [JsonProperty("username")] + [JsonPropertyName("username")] public required string Username { get; set; } /// /// Slug suitable for URLs /// - [JsonProperty("slug")] + [JsonPropertyName("slug")] public required string Slug { get; set; } } @@ -260,22 +260,22 @@ public class KickModels /// /// GUID of the event /// - [JsonProperty("id")] + [JsonPropertyName("id")] public required string Id { get; set; } /// /// User who was banished /// - [JsonProperty("user")] + [JsonPropertyName("user")] public required UserBannedUserModel User { get; set; } /// /// Janny who did the sweeping /// - [JsonProperty("banned_by")] + [JsonPropertyName("banned_by")] public required UserBannedUserModel BannedBy { get; set; } /// /// Datetime that the ban expires. Null for permabans /// - [JsonProperty("expires_at")] + [JsonPropertyName("expires_at")] public DateTimeOffset? ExpiresAt { get; set; } } @@ -284,17 +284,17 @@ public class KickModels /// /// GUID of the event /// - [JsonProperty("id")] + [JsonPropertyName("id")] public required string Id { get; set; } /// /// User who was unbanned /// - [JsonProperty("user")] + [JsonPropertyName("user")] public required UserBannedUserModel User { get; set; } /// /// Janny who unbanned /// - [JsonProperty("unbanned_by")] + [JsonPropertyName("unbanned_by")] public required UserBannedUserModel UnbannedBy { get; set; } } @@ -303,12 +303,12 @@ public class KickModels /// /// ID representing the category /// - [JsonProperty("id")] + [JsonPropertyName("id")] public int Id { get; set; } /// /// Slug representing the category /// - [JsonProperty("slug")] + [JsonPropertyName("slug")] public required string Slug { get; set; } } @@ -317,27 +317,27 @@ public class KickModels /// /// ID of the category /// - [JsonProperty("id")] + [JsonPropertyName("id")] public int Id { get; set; } /// /// Friendly name of the category /// - [JsonProperty("name")] + [JsonPropertyName("name")] public required string Name { get; set; } /// /// Category's slug for forming URls etc. /// - [JsonProperty("slug")] + [JsonPropertyName("slug")] public required string Slug { get; set; } /// /// Tags for the category /// - [JsonProperty("tags")] + [JsonPropertyName("tags")] public List Tags { get; set; } = []; /// /// Parent category, if one is present. I think there usually is one, but made it nullable just in case /// - [JsonProperty("parent_category")] + [JsonPropertyName("parent_category")] public UpdatedLiveStreamCategoryParentModel? ParentCategory { get; set; } } @@ -346,42 +346,42 @@ public class KickModels /// /// ID of the livestream (numeric) /// - [JsonProperty("id")] + [JsonPropertyName("id")] public int Id { get; set; } /// /// Livestream slug /// - [JsonProperty("slug")] + [JsonPropertyName("slug")] public required string Slug { get; set; } /// /// Livestream title /// - [JsonProperty("session_title")] + [JsonPropertyName("session_title")] public required string SessionTitle { get; set; } /// /// Livestream start time /// - [JsonProperty("created_at")] + [JsonPropertyName("created_at")] public DateTimeOffset CreatedAt { get; set; } /// /// Language of the livestream (e.g. English) /// - [JsonProperty("language")] + [JsonPropertyName("language")] public string? Language { get; set; } /// /// Whether the stream is marked as for a mature audience /// - [JsonProperty("is_mature")] + [JsonPropertyName("is_mature")] public bool IsMature { get; set; } /// /// Number of viewers presently watching /// - [JsonProperty("viewers")] + [JsonPropertyName("viewers")] public int Viewers { get; set; } /// /// Category of the livestream. I believe this is always required but marked it as nullable just in case /// - [JsonProperty("category")] + [JsonPropertyName("category")] public UpdatedLiveStreamCategoryModel? Category { get; set; } } @@ -390,12 +390,12 @@ public class KickModels /// /// ID of the channel /// - [JsonProperty("id")] + [JsonPropertyName("id")] public int Id { get; set; } /// /// Whether the streamer was sent to ban world /// - [JsonProperty("is_banned")] + [JsonPropertyName("is_banned")] public bool IsBanned { get; set; } } @@ -404,12 +404,12 @@ public class KickModels /// /// Livestream event ID /// - [JsonProperty("id")] + [JsonPropertyName("id")] public int Id { get; set; } /// /// Channel that stopped streaming /// - [JsonProperty("channel")] + [JsonPropertyName("channel")] public required StopStreamBroadcastLiveStreamChannelModel Channel { get; set; } } @@ -418,7 +418,7 @@ public class KickModels /// /// Object containing information related to the livestream that stopped /// - [JsonProperty("livestream")] + [JsonPropertyName("livestream")] public required StopStreamBroadcastLiveStreamModel Livestream { get; set; } } @@ -427,27 +427,27 @@ public class KickModels /// /// ID of the livestream /// - [JsonProperty("id")] + [JsonPropertyName("id")] public int Id { get; set; } /// /// ID of the channel /// - [JsonProperty("channel_id")] + [JsonPropertyName("channel_id")] public int ChannelId { get; set; } /// /// Title of the stream /// - [JsonProperty("session_title")] + [JsonPropertyName("session_title")] public required string SessionTitle { get; set; } /// /// No idea, just null on my end /// - [JsonProperty("source")] + [JsonPropertyName("source")] public string? Source { get; set; } /// /// Time stream started /// - [JsonProperty("created_at")] + [JsonPropertyName("created_at")] public DateTimeOffset CreatedAt { get; set; } } @@ -456,7 +456,7 @@ public class KickModels /// /// Object containing information related to the livestream that has just started /// - [JsonProperty("livestream")] + [JsonPropertyName("livestream")] public required StreamerIsLiveLiveStreamModel Livestream { get; set; } } @@ -465,17 +465,17 @@ public class KickModels /// /// ID of the poll option /// - [JsonProperty("id")] + [JsonPropertyName("id")] public int Id { get; set; } /// /// Label of the poll option /// - [JsonProperty("label")] + [JsonPropertyName("label")] public required string Label { get; set; } /// /// Number of votes the poll option has gotten /// - [JsonProperty("votes")] + [JsonPropertyName("votes")] public int Votes { get; set; } } @@ -484,27 +484,27 @@ public class KickModels /// /// Title of the poll /// - [JsonProperty("title")] + [JsonPropertyName("title")] public required string Title { get; set; } /// /// Poll options /// - [JsonProperty("options")] + [JsonPropertyName("options")] public List Options { get; set; } = []; /// /// Duration of the poll in seconds /// - [JsonProperty("duration")] + [JsonPropertyName("duration")] public int Duration { get; set; } /// /// Remaining time in seconds /// - [JsonProperty("remaining")] + [JsonPropertyName("remaining")] public int Remaining { get; set; } /// /// Time in seconds to display the results after completion? /// - [JsonProperty("result_display_duration")] + [JsonPropertyName("result_display_duration")] public int ResultDisplayDuration { get; set; } } @@ -513,7 +513,7 @@ public class KickModels /// /// Poll data /// - [JsonProperty("poll")] + [JsonPropertyName("poll")] public required PollUpdatePollModel Poll { get; set; } } } \ No newline at end of file diff --git a/KickWsClient/Models/PusherModels.cs b/KickWsClient/Models/PusherModels.cs index aaa0d03..412f212 100644 --- a/KickWsClient/Models/PusherModels.cs +++ b/KickWsClient/Models/PusherModels.cs @@ -1,4 +1,5 @@ -using Newtonsoft.Json; +using System.Text.Json.Serialization; +using KickWsClient.Converters; namespace KickWsClient.Models; @@ -9,17 +10,18 @@ public class PusherModels /// /// Name of the event /// - [JsonProperty("event")] + [JsonPropertyName("event")] public required string Event { get; set; } /// /// Stringified JSON payload /// - [JsonProperty("data")] + [JsonPropertyName("data")] + [JsonConverter(typeof(StringOrObjectConverter))] public required string Data { get; set; } /// /// Channel where event originates. Only included events where a channel is applicable /// - [JsonProperty("channel")] + [JsonPropertyName("channel")] public string? Channel { get; set; } } @@ -28,12 +30,12 @@ public class PusherModels /// /// Name of the event /// - [JsonProperty("event")] + [JsonPropertyName("event")] public required string Event { get; set; } /// /// Data as object. It's only stringified for responses /// - [JsonProperty("data")] + [JsonPropertyName("data")] public required object Data { get; set; } } @@ -42,12 +44,12 @@ public class PusherModels /// /// Internal socket ID /// - [JsonProperty("socket_id")] + [JsonPropertyName("socket_id")] public required string SocketId { get; set; } /// /// Timeout on no activity in seconds /// - [JsonProperty("activity_timeout")] + [JsonPropertyName("activity_timeout")] public int ActivityTimeout { get; set; } } @@ -56,12 +58,12 @@ public class PusherModels /// /// Token to authenticate with, use an empty string for guest. /// - [JsonProperty("auth")] + [JsonPropertyName("auth")] public string Auth { get; set; } = ""; /// /// Channel you wish to subscribe to. 'channel.2515504' for stream events. 'chatrooms.2515504.v2' for chat where 2515504 is the channel ID /// - [JsonProperty("channel")] + [JsonPropertyName("channel")] public required string Channel { get; set; } } @@ -70,7 +72,7 @@ public class PusherModels /// /// Channel you wish to unsubscribe from, e.g. 'channel.2515504' /// - [JsonProperty("channel")] + [JsonPropertyName("channel")] public required string Channel { get; set; } } } \ No newline at end of file