mirror of
https://github.com/barelyprofessional/KfChatDotNet.git
synced 2026-04-30 03:22:04 -04:00
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.
This commit is contained in:
@@ -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<RoomSettingsModel>(File.ReadAllText("rooms.json"));
|
||||
var rooms = JsonSerializer.Deserialize<RoomSettingsModel>(File.ReadAllText("rooms.json"));
|
||||
dataContext.RoomList = rooms!.Rooms;
|
||||
|
||||
}
|
||||
|
||||
@@ -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<ForumIdentityModel>(accountJs);
|
||||
return JsonSerializer.Deserialize<ForumIdentityModel>(accountJs);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -28,7 +28,6 @@
|
||||
<PackageReference Include="Avalonia.ReactiveUI" Version="11.0.10" />
|
||||
<PackageReference Include="Avalonia.Themes.Fluent" Version="11.0.10" />
|
||||
<PackageReference Include="HtmlAgilityPack" Version="1.11.59" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||
<PackageReference Include="ReactiveUI" Version="19.5.72" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
|
||||
@@ -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<string> IgnoredUsers { get; set; }
|
||||
[JsonProperty("is_staff")]
|
||||
[JsonPropertyName("is_staff")]
|
||||
public bool IsStaff { get; set; }
|
||||
}
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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<SettingsModel>(File.ReadAllText("settings.json"));
|
||||
context.WsUri = settings.WsUri;
|
||||
var settings = JsonSerializer.Deserialize<SettingsModel>(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<ListBox>("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<ListBox>("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<RoomSettingsModel>(File.ReadAllText("rooms.json"));
|
||||
var settings = JsonSerializer.Deserialize<RoomSettingsModel>(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<SettingsModel>(File.ReadAllText("settings.json"));
|
||||
var settings = JsonSerializer.Deserialize<SettingsModel>(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<RoomSettingsModel>(File.ReadAllText("rooms.json"));
|
||||
var rooms = JsonSerializer.Deserialize<RoomSettingsModel>(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<ListBoxItem>();
|
||||
var children = ((e.Source as ListBox)!).GetLogicalChildren().Cast<ListBoxItem>();
|
||||
foreach (var child in children)
|
||||
{
|
||||
// Bit of a ghetto hack but it ensures that there's only ever one subscriber
|
||||
|
||||
@@ -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<SettingsModel>(await File.ReadAllTextAsync("settings.json"));
|
||||
var settings = JsonSerializer.Deserialize<SettingsModel>(await File.ReadAllTextAsync("settings.json"));
|
||||
kfDomain = settings.WsUri.Host;
|
||||
}
|
||||
|
||||
|
||||
@@ -8,10 +8,8 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||
<PackageReference Include="NLog" Version="5.3.2" />
|
||||
<PackageReference Include="PuppeteerSharp" Version="18.0.1" />
|
||||
<PackageReference Include="Spectre.Console" Version="0.48.0" />
|
||||
<PackageReference Include="System.Text.Json" Version="8.0.3" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
@@ -1,31 +1,30 @@
|
||||
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<int> _seenMsgIds = new List<int>();
|
||||
// Suppresses the command handler on initial start so it doesn't pick up things already handled on restart
|
||||
private readonly List<int> _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()
|
||||
{
|
||||
@@ -37,9 +36,9 @@ public class KickBot
|
||||
Environment.Exit(1);
|
||||
}
|
||||
|
||||
_config = JsonConvert.DeserializeObject<Models.ConfigModel>(File.ReadAllText(configPath)) ??
|
||||
_config = JsonSerializer.Deserialize<Models.ConfigModel>(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<string>("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<MessageModel> 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;
|
||||
}
|
||||
|
||||
@@ -178,15 +176,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()}[/]");
|
||||
_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[/]");
|
||||
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<UserModel> 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)
|
||||
|
||||
@@ -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\"]");
|
||||
|
||||
@@ -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<string, object> packetType = new Dictionary<string, object>();
|
||||
try
|
||||
{
|
||||
packetType = JsonConvert.DeserializeObject<Dictionary<string, object>>(message.Text)!;
|
||||
packetType = JsonSerializer.Deserialize<Dictionary<string, object>>(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<DeleteMessagesJsonModel>(message.Text);
|
||||
var data = JsonSerializer.Deserialize<DeleteMessagesJsonModel>(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<MessagesJsonModel>(message.Text);
|
||||
var data = JsonSerializer.Deserialize<MessagesJsonModel>(message.Text);
|
||||
var messages = new List<MessageModel>();
|
||||
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<UsersJsonModel>(message.Text);
|
||||
var data = JsonSerializer.Deserialize<UsersJsonModel>(message.Text);
|
||||
var users = new List<UserModel>();
|
||||
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<Dictionary<string, Dictionary<string, bool>>>(message.Text);
|
||||
var data = JsonSerializer.Deserialize<Dictionary<string, Dictionary<string, bool>>>(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);
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||
<PackageReference Include="NLog" Version="5.2.8" />
|
||||
<PackageReference Include="Websocket.Client" Version="5.1.1" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -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<int> MessageIdsToDelete { get; set; }
|
||||
}
|
||||
@@ -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; }
|
||||
}
|
||||
@@ -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<MessageModel> Messages { get; set; }
|
||||
}
|
||||
@@ -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<string, UserModel> Users { get; set; }
|
||||
}
|
||||
37
KickWsClient/Converters/StringOrObjectConverter.cs
Normal file
37
KickWsClient/Converters/StringOrObjectConverter.cs
Normal file
@@ -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<T> : JsonConverter<T>
|
||||
{
|
||||
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<T>(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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<PusherModels.BasePusherEventModel>(message.Text) ??
|
||||
pusherMsg = JsonSerializer.Deserialize<PusherModels.BasePusherEventModel>(message.Text, options) ??
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
catch (Exception e)
|
||||
@@ -182,7 +187,7 @@ public class KickWsClient
|
||||
case "pusher:connection_established":
|
||||
{
|
||||
var data =
|
||||
JsonConvert.DeserializeObject<PusherModels.PusherConnectionEstablishedEventModel>(pusherMsg.Data);
|
||||
JsonSerializer.Deserialize<PusherModels.PusherConnectionEstablishedEventModel>(pusherMsg.Data, options);
|
||||
OnPusherConnectionEstablished?.Invoke(this, data);
|
||||
return;
|
||||
}
|
||||
@@ -194,67 +199,67 @@ public class KickWsClient
|
||||
return;
|
||||
case @"App\Events\FollowersUpdated":
|
||||
{
|
||||
var data = JsonConvert.DeserializeObject<KickModels.FollowersUpdatedEventModel>(pusherMsg.Data);
|
||||
var data = JsonSerializer.Deserialize<KickModels.FollowersUpdatedEventModel>(pusherMsg.Data, options);
|
||||
OnFollowersUpdated?.Invoke(this, data);
|
||||
return;
|
||||
}
|
||||
case @"App\Events\ChatMessageEvent":
|
||||
{
|
||||
var data = JsonConvert.DeserializeObject<KickModels.ChatMessageEventModel>(pusherMsg.Data);
|
||||
var data = JsonSerializer.Deserialize<KickModels.ChatMessageEventModel>(pusherMsg.Data, options);
|
||||
OnChatMessage?.Invoke(this, data);
|
||||
return;
|
||||
}
|
||||
case @"App\Events\ChannelSubscriptionEvent":
|
||||
{
|
||||
var data = JsonConvert.DeserializeObject<KickModels.ChannelSubscriptionEventModel>(pusherMsg.Data);
|
||||
var data = JsonSerializer.Deserialize<KickModels.ChannelSubscriptionEventModel>(pusherMsg.Data, options);
|
||||
OnChannelSubscription?.Invoke(this, data);
|
||||
return;
|
||||
}
|
||||
case @"App\Events\SubscriptionEvent":
|
||||
{
|
||||
var data = JsonConvert.DeserializeObject<KickModels.SubscriptionEventModel>(pusherMsg.Data);
|
||||
var data = JsonSerializer.Deserialize<KickModels.SubscriptionEventModel>(pusherMsg.Data, options);
|
||||
OnSubscription?.Invoke(this, data);
|
||||
return;
|
||||
}
|
||||
case @"App\Events\MessageDeletedEvent":
|
||||
{
|
||||
var data = JsonConvert.DeserializeObject<KickModels.MessageDeletedEventModel>(pusherMsg.Data);
|
||||
var data = JsonSerializer.Deserialize<KickModels.MessageDeletedEventModel>(pusherMsg.Data, options);
|
||||
OnMessageDeleted?.Invoke(this, data);
|
||||
return;
|
||||
}
|
||||
case @"App\Events\UserBannedEvent":
|
||||
{
|
||||
var data = JsonConvert.DeserializeObject<KickModels.UserBannedEventModel>(pusherMsg.Data);
|
||||
var data = JsonSerializer.Deserialize<KickModels.UserBannedEventModel>(pusherMsg.Data, options);
|
||||
OnUserBanned?.Invoke(this, data);
|
||||
return;
|
||||
}
|
||||
case @"App\Events\UserUnbannedEvent":
|
||||
{
|
||||
var data = JsonConvert.DeserializeObject<KickModels.UserUnbannedEventModel>(pusherMsg.Data);
|
||||
var data = JsonSerializer.Deserialize<KickModels.UserUnbannedEventModel>(pusherMsg.Data, options);
|
||||
OnUserUnbanned?.Invoke(this, data);
|
||||
return;
|
||||
}
|
||||
case @"App\Events\LiveStream\UpdatedLiveStreamEvent":
|
||||
{
|
||||
var data = JsonConvert.DeserializeObject<KickModels.UpdatedLiveStreamEventModel>(pusherMsg.Data);
|
||||
var data = JsonSerializer.Deserialize<KickModels.UpdatedLiveStreamEventModel>(pusherMsg.Data, options);
|
||||
OnUpdatedLiveStream?.Invoke(this, data);
|
||||
return;
|
||||
}
|
||||
case @"App\Events\StopStreamBroadcast":
|
||||
{
|
||||
var data = JsonConvert.DeserializeObject<KickModels.StopStreamBroadcastEventModel>(pusherMsg.Data);
|
||||
var data = JsonSerializer.Deserialize<KickModels.StopStreamBroadcastEventModel>(pusherMsg.Data, options);
|
||||
OnStopStreamBroadcast?.Invoke(this, data);
|
||||
return;
|
||||
}
|
||||
case @"App\Events\StreamerIsLive":
|
||||
{
|
||||
var data = JsonConvert.DeserializeObject<KickModels.StreamerIsLiveEventModel>(pusherMsg.Data);
|
||||
var data = JsonSerializer.Deserialize<KickModels.StreamerIsLiveEventModel>(pusherMsg.Data, options);
|
||||
OnStreamerIsLive?.Invoke(this, data);
|
||||
return;
|
||||
}
|
||||
case @"App\Events\PollUpdateEvent":
|
||||
{
|
||||
var data = JsonConvert.DeserializeObject<KickModels.PollUpdateEventModel>(pusherMsg.Data);
|
||||
var data = JsonSerializer.Deserialize<KickModels.PollUpdateEventModel>(pusherMsg.Data, options);
|
||||
OnPollUpdate?.Invoke(this, data);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -7,8 +7,8 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||
<PackageReference Include="NLog" Version="5.2.8" />
|
||||
<PackageReference Include="System.Text.Json" Version="8.0.3" />
|
||||
<PackageReference Include="Websocket.Client" Version="5.1.1" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using Newtonsoft.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace KickWsClient.Models;
|
||||
|
||||
@@ -9,17 +9,17 @@ public class KickModels
|
||||
/// <summary>
|
||||
/// Internal type for badge e.g. moderator
|
||||
/// </summary>
|
||||
[JsonProperty("type")]
|
||||
[JsonPropertyName("type")]
|
||||
public required string Type { get; set; }
|
||||
/// <summary>
|
||||
/// Friendly name for badge e.g. Moderator
|
||||
/// </summary>
|
||||
[JsonProperty("text")]
|
||||
[JsonPropertyName("text")]
|
||||
public required string Text { get; set; }
|
||||
/// <summary>
|
||||
/// Count (if applicable) for badge (e.g. sub count for gifted subs)
|
||||
/// </summary>
|
||||
[JsonProperty("count")]
|
||||
[JsonPropertyName("count")]
|
||||
public int? Count { get; set; }
|
||||
}
|
||||
|
||||
@@ -28,13 +28,13 @@ public class KickModels
|
||||
/// <summary>
|
||||
/// User's hex color
|
||||
/// </summary>
|
||||
[JsonProperty("color")]
|
||||
[JsonPropertyName("color")]
|
||||
public required string Color { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Badges a user has
|
||||
/// </summary>
|
||||
[JsonProperty("badges")]
|
||||
[JsonPropertyName("badges")]
|
||||
public List<ChatMessageSenderIdentityBadgeModel> Badges = [];
|
||||
}
|
||||
|
||||
@@ -43,22 +43,22 @@ public class KickModels
|
||||
/// <summary>
|
||||
/// Kick internal user ID
|
||||
/// </summary>
|
||||
[JsonProperty("id")]
|
||||
[JsonPropertyName("id")]
|
||||
public int Id { get; set; }
|
||||
/// <summary>
|
||||
/// Kick display name
|
||||
/// </summary>
|
||||
[JsonProperty("username")]
|
||||
[JsonPropertyName("username")]
|
||||
public required string Username { get; set; }
|
||||
/// <summary>
|
||||
/// Kick slug (for URLs)
|
||||
/// </summary>
|
||||
[JsonProperty("slug")]
|
||||
[JsonPropertyName("slug")]
|
||||
public required string Slug { get; set; }
|
||||
/// <summary>
|
||||
/// Identity info for display color and badges
|
||||
/// </summary>
|
||||
[JsonProperty("identity")]
|
||||
[JsonPropertyName("identity")]
|
||||
public required ChatMessageSenderIdentityModel Identity { get; set; }
|
||||
}
|
||||
|
||||
@@ -67,12 +67,12 @@ public class KickModels
|
||||
/// <summary>
|
||||
/// Original sender's user ID
|
||||
/// </summary>
|
||||
[JsonProperty("id")]
|
||||
[JsonPropertyName("id")]
|
||||
public int Id { get; set; }
|
||||
/// <summary>
|
||||
/// Original sender's username
|
||||
/// </summary>
|
||||
[JsonProperty("username")]
|
||||
[JsonPropertyName("username")]
|
||||
public required string Username { get; set; }
|
||||
}
|
||||
|
||||
@@ -81,12 +81,12 @@ public class KickModels
|
||||
/// <summary>
|
||||
/// ID (GUID) of the original message
|
||||
/// </summary>
|
||||
[JsonProperty("id")]
|
||||
[JsonPropertyName("id")]
|
||||
public required string Id { get; set; }
|
||||
/// <summary>
|
||||
/// Content of the original message
|
||||
/// </summary>
|
||||
[JsonProperty("content")]
|
||||
[JsonPropertyName("content")]
|
||||
public required string Content { get; set; }
|
||||
}
|
||||
|
||||
@@ -95,12 +95,12 @@ public class KickModels
|
||||
/// <summary>
|
||||
/// Sender of the message that this message is in reply to
|
||||
/// </summary>
|
||||
[JsonProperty("original_sender")]
|
||||
[JsonPropertyName("original_sender")]
|
||||
public required ChatMessageMetadataOriginalSenderModel OriginalSender { get; set; }
|
||||
/// <summary>
|
||||
/// Content of the message that this message is in reply to
|
||||
/// </summary>
|
||||
[JsonProperty("original_message")]
|
||||
[JsonPropertyName("original_message")]
|
||||
public required ChatMessageMetadataOriginalMessageModel OriginalMessage { get; set; }
|
||||
}
|
||||
|
||||
@@ -109,29 +109,29 @@ public class KickModels
|
||||
/// <summary>
|
||||
/// Channel follower count
|
||||
/// </summary>
|
||||
[JsonProperty("followersCount")]
|
||||
[JsonPropertyName("followersCount")]
|
||||
public int FollowersCount { get; set; }
|
||||
/// <summary>
|
||||
/// ID to identify what chatroom this event belongs to
|
||||
/// </summary>
|
||||
[JsonProperty("chatroom_id")]
|
||||
[JsonPropertyName("chatroom_id")]
|
||||
public int ChatroomId { get; set; }
|
||||
/// <summary>
|
||||
/// Maybe returns your username if you're auth'd? No idea. Just returned null for me
|
||||
/// </summary>
|
||||
[JsonProperty("username")]
|
||||
[JsonPropertyName("username")]
|
||||
public string? Username { get; set; }
|
||||
/// <summary>
|
||||
/// Epoch value that signifies ???
|
||||
/// </summary>
|
||||
[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.
|
||||
/// <summary>
|
||||
/// Does it mean we're following? Who knows, returns true even if you're a guest
|
||||
/// </summary>
|
||||
[JsonProperty("followed")]
|
||||
[JsonPropertyName("followed")]
|
||||
public bool? Followed { get; set; }
|
||||
}
|
||||
|
||||
@@ -140,38 +140,38 @@ public class KickModels
|
||||
/// <summary>
|
||||
/// Message unique GUID that's referenced for replies and deletions
|
||||
/// </summary>
|
||||
[JsonProperty("id")]
|
||||
[JsonPropertyName("id")]
|
||||
public required string Id { get; set; }
|
||||
/// <summary>
|
||||
/// Chatroom ID you can use to differentiate this from other rooms if you sub to multiple at a time
|
||||
/// </summary>
|
||||
[JsonProperty("chatroom_id")]
|
||||
[JsonPropertyName("chatroom_id")]
|
||||
public int ChatroomId { get; set; }
|
||||
/// <summary>
|
||||
/// Content of the message. Emotes are encoded like [emote:161238:russW] which translates to -> https://files.kick.com/emotes/161238/fullsize
|
||||
/// </summary>
|
||||
[JsonProperty("content")]
|
||||
[JsonPropertyName("content")]
|
||||
public required string Content { get; set; }
|
||||
/// <summary>
|
||||
/// Regular message is 'message', replies are 'reply'
|
||||
/// </summary>
|
||||
[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
|
||||
/// <summary>
|
||||
/// Time message was sent
|
||||
/// </summary>
|
||||
[JsonProperty("created_at")]
|
||||
[JsonPropertyName("created_at")]
|
||||
public DateTimeOffset CreatedAt { get; set; }
|
||||
/// <summary>
|
||||
/// Sender of the message
|
||||
/// </summary>
|
||||
[JsonProperty("sender")]
|
||||
[JsonPropertyName("sender")]
|
||||
public required ChatMessageSenderModel Sender { get; set; }
|
||||
/// <summary>
|
||||
/// Message metadata which is set for replies only
|
||||
/// </summary>
|
||||
[JsonProperty("metadata")]
|
||||
[JsonPropertyName("metadata")]
|
||||
public ChatMessageMetadataModel? Metadata { get; set; }
|
||||
}
|
||||
|
||||
@@ -180,17 +180,17 @@ public class KickModels
|
||||
/// <summary>
|
||||
/// User IDs of subscription recipients
|
||||
/// </summary>
|
||||
[JsonProperty("user_ids")]
|
||||
[JsonPropertyName("user_ids")]
|
||||
public List<int> UserIds { get; set; } = [];
|
||||
/// <summary>
|
||||
/// Username of the person who subbed / gifted
|
||||
/// </summary>
|
||||
[JsonProperty("username")]
|
||||
[JsonPropertyName("username")]
|
||||
public required string Username { get; set; }
|
||||
/// <summary>
|
||||
/// Channel ID where the sub event occurred
|
||||
/// </summary>
|
||||
[JsonProperty("channel_id")]
|
||||
[JsonPropertyName("channel_id")]
|
||||
public int ChannelId { get; set; }
|
||||
}
|
||||
|
||||
@@ -199,17 +199,17 @@ public class KickModels
|
||||
/// <summary>
|
||||
/// ID of channel where the subscription event occurred
|
||||
/// </summary>
|
||||
[JsonProperty("chatroom_id")]
|
||||
[JsonPropertyName("chatroom_id")]
|
||||
public int ChatroomId { get; set; }
|
||||
/// <summary>
|
||||
/// Username of the person who bought a sub
|
||||
/// </summary>
|
||||
[JsonProperty("username")]
|
||||
[JsonPropertyName("username")]
|
||||
public required string Username { get; set; }
|
||||
/// <summary>
|
||||
/// Number of months they've subbed now (e.g. 2 if they bought their 2nd month)
|
||||
/// </summary>
|
||||
[JsonProperty("months")]
|
||||
[JsonPropertyName("months")]
|
||||
public int Months { get; set; }
|
||||
}
|
||||
|
||||
@@ -218,7 +218,7 @@ public class KickModels
|
||||
/// <summary>
|
||||
/// ID of the message that was deleted
|
||||
/// </summary>
|
||||
[JsonProperty("id")]
|
||||
[JsonPropertyName("id")]
|
||||
public required string Id { get; set; }
|
||||
}
|
||||
|
||||
@@ -227,12 +227,12 @@ public class KickModels
|
||||
/// <summary>
|
||||
/// ID of this event (NOT the message to be removed!)
|
||||
/// </summary>
|
||||
[JsonProperty("id")]
|
||||
[JsonPropertyName("id")]
|
||||
public required string Id { get; set; }
|
||||
/// <summary>
|
||||
/// Message that was deleted
|
||||
/// </summary>
|
||||
[JsonProperty("message")]
|
||||
[JsonPropertyName("message")]
|
||||
public required MessageDeletedMessageModel Message { get; set; }
|
||||
}
|
||||
|
||||
@@ -241,17 +241,17 @@ public class KickModels
|
||||
/// <summary>
|
||||
/// ID of the user. Note it'll be 0 for the janny
|
||||
/// </summary>
|
||||
[JsonProperty("id")]
|
||||
[JsonPropertyName("id")]
|
||||
public int Id { get; set; }
|
||||
/// <summary>
|
||||
/// User's username
|
||||
/// </summary>
|
||||
[JsonProperty("username")]
|
||||
[JsonPropertyName("username")]
|
||||
public required string Username { get; set; }
|
||||
/// <summary>
|
||||
/// Slug suitable for URLs
|
||||
/// </summary>
|
||||
[JsonProperty("slug")]
|
||||
[JsonPropertyName("slug")]
|
||||
public required string Slug { get; set; }
|
||||
}
|
||||
|
||||
@@ -260,22 +260,22 @@ public class KickModels
|
||||
/// <summary>
|
||||
/// GUID of the event
|
||||
/// </summary>
|
||||
[JsonProperty("id")]
|
||||
[JsonPropertyName("id")]
|
||||
public required string Id { get; set; }
|
||||
/// <summary>
|
||||
/// User who was banished
|
||||
/// </summary>
|
||||
[JsonProperty("user")]
|
||||
[JsonPropertyName("user")]
|
||||
public required UserBannedUserModel User { get; set; }
|
||||
/// <summary>
|
||||
/// Janny who did the sweeping
|
||||
/// </summary>
|
||||
[JsonProperty("banned_by")]
|
||||
[JsonPropertyName("banned_by")]
|
||||
public required UserBannedUserModel BannedBy { get; set; }
|
||||
/// <summary>
|
||||
/// Datetime that the ban expires. Null for permabans
|
||||
/// </summary>
|
||||
[JsonProperty("expires_at")]
|
||||
[JsonPropertyName("expires_at")]
|
||||
public DateTimeOffset? ExpiresAt { get; set; }
|
||||
}
|
||||
|
||||
@@ -284,17 +284,17 @@ public class KickModels
|
||||
/// <summary>
|
||||
/// GUID of the event
|
||||
/// </summary>
|
||||
[JsonProperty("id")]
|
||||
[JsonPropertyName("id")]
|
||||
public required string Id { get; set; }
|
||||
/// <summary>
|
||||
/// User who was unbanned
|
||||
/// </summary>
|
||||
[JsonProperty("user")]
|
||||
[JsonPropertyName("user")]
|
||||
public required UserBannedUserModel User { get; set; }
|
||||
/// <summary>
|
||||
/// Janny who unbanned
|
||||
/// </summary>
|
||||
[JsonProperty("unbanned_by")]
|
||||
[JsonPropertyName("unbanned_by")]
|
||||
public required UserBannedUserModel UnbannedBy { get; set; }
|
||||
}
|
||||
|
||||
@@ -303,12 +303,12 @@ public class KickModels
|
||||
/// <summary>
|
||||
/// ID representing the category
|
||||
/// </summary>
|
||||
[JsonProperty("id")]
|
||||
[JsonPropertyName("id")]
|
||||
public int Id { get; set; }
|
||||
/// <summary>
|
||||
/// Slug representing the category
|
||||
/// </summary>
|
||||
[JsonProperty("slug")]
|
||||
[JsonPropertyName("slug")]
|
||||
public required string Slug { get; set; }
|
||||
}
|
||||
|
||||
@@ -317,27 +317,27 @@ public class KickModels
|
||||
/// <summary>
|
||||
/// ID of the category
|
||||
/// </summary>
|
||||
[JsonProperty("id")]
|
||||
[JsonPropertyName("id")]
|
||||
public int Id { get; set; }
|
||||
/// <summary>
|
||||
/// Friendly name of the category
|
||||
/// </summary>
|
||||
[JsonProperty("name")]
|
||||
[JsonPropertyName("name")]
|
||||
public required string Name { get; set; }
|
||||
/// <summary>
|
||||
/// Category's slug for forming URls etc.
|
||||
/// </summary>
|
||||
[JsonProperty("slug")]
|
||||
[JsonPropertyName("slug")]
|
||||
public required string Slug { get; set; }
|
||||
/// <summary>
|
||||
/// Tags for the category
|
||||
/// </summary>
|
||||
[JsonProperty("tags")]
|
||||
[JsonPropertyName("tags")]
|
||||
public List<string> Tags { get; set; } = [];
|
||||
/// <summary>
|
||||
/// Parent category, if one is present. I think there usually is one, but made it nullable just in case
|
||||
/// </summary>
|
||||
[JsonProperty("parent_category")]
|
||||
[JsonPropertyName("parent_category")]
|
||||
public UpdatedLiveStreamCategoryParentModel? ParentCategory { get; set; }
|
||||
}
|
||||
|
||||
@@ -346,42 +346,42 @@ public class KickModels
|
||||
/// <summary>
|
||||
/// ID of the livestream (numeric)
|
||||
/// </summary>
|
||||
[JsonProperty("id")]
|
||||
[JsonPropertyName("id")]
|
||||
public int Id { get; set; }
|
||||
/// <summary>
|
||||
/// Livestream slug
|
||||
/// </summary>
|
||||
[JsonProperty("slug")]
|
||||
[JsonPropertyName("slug")]
|
||||
public required string Slug { get; set; }
|
||||
/// <summary>
|
||||
/// Livestream title
|
||||
/// </summary>
|
||||
[JsonProperty("session_title")]
|
||||
[JsonPropertyName("session_title")]
|
||||
public required string SessionTitle { get; set; }
|
||||
/// <summary>
|
||||
/// Livestream start time
|
||||
/// </summary>
|
||||
[JsonProperty("created_at")]
|
||||
[JsonPropertyName("created_at")]
|
||||
public DateTimeOffset CreatedAt { get; set; }
|
||||
/// <summary>
|
||||
/// Language of the livestream (e.g. English)
|
||||
/// </summary>
|
||||
[JsonProperty("language")]
|
||||
[JsonPropertyName("language")]
|
||||
public string? Language { get; set; }
|
||||
/// <summary>
|
||||
/// Whether the stream is marked as for a mature audience
|
||||
/// </summary>
|
||||
[JsonProperty("is_mature")]
|
||||
[JsonPropertyName("is_mature")]
|
||||
public bool IsMature { get; set; }
|
||||
/// <summary>
|
||||
/// Number of viewers presently watching
|
||||
/// </summary>
|
||||
[JsonProperty("viewers")]
|
||||
[JsonPropertyName("viewers")]
|
||||
public int Viewers { get; set; }
|
||||
/// <summary>
|
||||
/// Category of the livestream. I believe this is always required but marked it as nullable just in case
|
||||
/// </summary>
|
||||
[JsonProperty("category")]
|
||||
[JsonPropertyName("category")]
|
||||
public UpdatedLiveStreamCategoryModel? Category { get; set; }
|
||||
}
|
||||
|
||||
@@ -390,12 +390,12 @@ public class KickModels
|
||||
/// <summary>
|
||||
/// ID of the channel
|
||||
/// </summary>
|
||||
[JsonProperty("id")]
|
||||
[JsonPropertyName("id")]
|
||||
public int Id { get; set; }
|
||||
/// <summary>
|
||||
/// Whether the streamer was sent to ban world
|
||||
/// </summary>
|
||||
[JsonProperty("is_banned")]
|
||||
[JsonPropertyName("is_banned")]
|
||||
public bool IsBanned { get; set; }
|
||||
}
|
||||
|
||||
@@ -404,12 +404,12 @@ public class KickModels
|
||||
/// <summary>
|
||||
/// Livestream event ID
|
||||
/// </summary>
|
||||
[JsonProperty("id")]
|
||||
[JsonPropertyName("id")]
|
||||
public int Id { get; set; }
|
||||
/// <summary>
|
||||
/// Channel that stopped streaming
|
||||
/// </summary>
|
||||
[JsonProperty("channel")]
|
||||
[JsonPropertyName("channel")]
|
||||
public required StopStreamBroadcastLiveStreamChannelModel Channel { get; set; }
|
||||
}
|
||||
|
||||
@@ -418,7 +418,7 @@ public class KickModels
|
||||
/// <summary>
|
||||
/// Object containing information related to the livestream that stopped
|
||||
/// </summary>
|
||||
[JsonProperty("livestream")]
|
||||
[JsonPropertyName("livestream")]
|
||||
public required StopStreamBroadcastLiveStreamModel Livestream { get; set; }
|
||||
}
|
||||
|
||||
@@ -427,27 +427,27 @@ public class KickModels
|
||||
/// <summary>
|
||||
/// ID of the livestream
|
||||
/// </summary>
|
||||
[JsonProperty("id")]
|
||||
[JsonPropertyName("id")]
|
||||
public int Id { get; set; }
|
||||
/// <summary>
|
||||
/// ID of the channel
|
||||
/// </summary>
|
||||
[JsonProperty("channel_id")]
|
||||
[JsonPropertyName("channel_id")]
|
||||
public int ChannelId { get; set; }
|
||||
/// <summary>
|
||||
/// Title of the stream
|
||||
/// </summary>
|
||||
[JsonProperty("session_title")]
|
||||
[JsonPropertyName("session_title")]
|
||||
public required string SessionTitle { get; set; }
|
||||
/// <summary>
|
||||
/// No idea, just null on my end
|
||||
/// </summary>
|
||||
[JsonProperty("source")]
|
||||
[JsonPropertyName("source")]
|
||||
public string? Source { get; set; }
|
||||
/// <summary>
|
||||
/// Time stream started
|
||||
/// </summary>
|
||||
[JsonProperty("created_at")]
|
||||
[JsonPropertyName("created_at")]
|
||||
public DateTimeOffset CreatedAt { get; set; }
|
||||
}
|
||||
|
||||
@@ -456,7 +456,7 @@ public class KickModels
|
||||
/// <summary>
|
||||
/// Object containing information related to the livestream that has just started
|
||||
/// </summary>
|
||||
[JsonProperty("livestream")]
|
||||
[JsonPropertyName("livestream")]
|
||||
public required StreamerIsLiveLiveStreamModel Livestream { get; set; }
|
||||
}
|
||||
|
||||
@@ -465,17 +465,17 @@ public class KickModels
|
||||
/// <summary>
|
||||
/// ID of the poll option
|
||||
/// </summary>
|
||||
[JsonProperty("id")]
|
||||
[JsonPropertyName("id")]
|
||||
public int Id { get; set; }
|
||||
/// <summary>
|
||||
/// Label of the poll option
|
||||
/// </summary>
|
||||
[JsonProperty("label")]
|
||||
[JsonPropertyName("label")]
|
||||
public required string Label { get; set; }
|
||||
/// <summary>
|
||||
/// Number of votes the poll option has gotten
|
||||
/// </summary>
|
||||
[JsonProperty("votes")]
|
||||
[JsonPropertyName("votes")]
|
||||
public int Votes { get; set; }
|
||||
}
|
||||
|
||||
@@ -484,27 +484,27 @@ public class KickModels
|
||||
/// <summary>
|
||||
/// Title of the poll
|
||||
/// </summary>
|
||||
[JsonProperty("title")]
|
||||
[JsonPropertyName("title")]
|
||||
public required string Title { get; set; }
|
||||
/// <summary>
|
||||
/// Poll options
|
||||
/// </summary>
|
||||
[JsonProperty("options")]
|
||||
[JsonPropertyName("options")]
|
||||
public List<PollUpdatePollOptionModel> Options { get; set; } = [];
|
||||
/// <summary>
|
||||
/// Duration of the poll in seconds
|
||||
/// </summary>
|
||||
[JsonProperty("duration")]
|
||||
[JsonPropertyName("duration")]
|
||||
public int Duration { get; set; }
|
||||
/// <summary>
|
||||
/// Remaining time in seconds
|
||||
/// </summary>
|
||||
[JsonProperty("remaining")]
|
||||
[JsonPropertyName("remaining")]
|
||||
public int Remaining { get; set; }
|
||||
/// <summary>
|
||||
/// Time in seconds to display the results after completion?
|
||||
/// </summary>
|
||||
[JsonProperty("result_display_duration")]
|
||||
[JsonPropertyName("result_display_duration")]
|
||||
public int ResultDisplayDuration { get; set; }
|
||||
}
|
||||
|
||||
@@ -513,7 +513,7 @@ public class KickModels
|
||||
/// <summary>
|
||||
/// Poll data
|
||||
/// </summary>
|
||||
[JsonProperty("poll")]
|
||||
[JsonPropertyName("poll")]
|
||||
public required PollUpdatePollModel Poll { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
/// <summary>
|
||||
/// Name of the event
|
||||
/// </summary>
|
||||
[JsonProperty("event")]
|
||||
[JsonPropertyName("event")]
|
||||
public required string Event { get; set; }
|
||||
/// <summary>
|
||||
/// Stringified JSON payload
|
||||
/// </summary>
|
||||
[JsonProperty("data")]
|
||||
[JsonPropertyName("data")]
|
||||
[JsonConverter(typeof(StringOrObjectConverter<string>))]
|
||||
public required string Data { get; set; }
|
||||
/// <summary>
|
||||
/// Channel where event originates. Only included events where a channel is applicable
|
||||
/// </summary>
|
||||
[JsonProperty("channel")]
|
||||
[JsonPropertyName("channel")]
|
||||
public string? Channel { get; set; }
|
||||
}
|
||||
|
||||
@@ -28,12 +30,12 @@ public class PusherModels
|
||||
/// <summary>
|
||||
/// Name of the event
|
||||
/// </summary>
|
||||
[JsonProperty("event")]
|
||||
[JsonPropertyName("event")]
|
||||
public required string Event { get; set; }
|
||||
/// <summary>
|
||||
/// Data as object. It's only stringified for responses
|
||||
/// </summary>
|
||||
[JsonProperty("data")]
|
||||
[JsonPropertyName("data")]
|
||||
public required object Data { get; set; }
|
||||
}
|
||||
|
||||
@@ -42,12 +44,12 @@ public class PusherModels
|
||||
/// <summary>
|
||||
/// Internal socket ID
|
||||
/// </summary>
|
||||
[JsonProperty("socket_id")]
|
||||
[JsonPropertyName("socket_id")]
|
||||
public required string SocketId { get; set; }
|
||||
/// <summary>
|
||||
/// Timeout on no activity in seconds
|
||||
/// </summary>
|
||||
[JsonProperty("activity_timeout")]
|
||||
[JsonPropertyName("activity_timeout")]
|
||||
public int ActivityTimeout { get; set; }
|
||||
}
|
||||
|
||||
@@ -56,12 +58,12 @@ public class PusherModels
|
||||
/// <summary>
|
||||
/// Token to authenticate with, use an empty string for guest.
|
||||
/// </summary>
|
||||
[JsonProperty("auth")]
|
||||
[JsonPropertyName("auth")]
|
||||
public string Auth { get; set; } = "";
|
||||
/// <summary>
|
||||
/// Channel you wish to subscribe to. 'channel.2515504' for stream events. 'chatrooms.2515504.v2' for chat where 2515504 is the channel ID
|
||||
/// </summary>
|
||||
[JsonProperty("channel")]
|
||||
[JsonPropertyName("channel")]
|
||||
public required string Channel { get; set; }
|
||||
}
|
||||
|
||||
@@ -70,7 +72,7 @@ public class PusherModels
|
||||
/// <summary>
|
||||
/// Channel you wish to unsubscribe from, e.g. 'channel.2515504'
|
||||
/// </summary>
|
||||
[JsonProperty("channel")]
|
||||
[JsonPropertyName("channel")]
|
||||
public required string Channel { get; set; }
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user