mirror of
https://github.com/barelyprofessional/KfChatDotNet.git
synced 2026-05-02 04: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;
|
||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Text.Json;
|
||||||
using Avalonia;
|
using Avalonia;
|
||||||
using Avalonia.Controls.ApplicationLifetimes;
|
using Avalonia.Controls.ApplicationLifetimes;
|
||||||
using Avalonia.Markup.Xaml;
|
using Avalonia.Markup.Xaml;
|
||||||
using KfChatDotNetGui.Models;
|
using KfChatDotNetGui.Models;
|
||||||
using KfChatDotNetGui.ViewModels;
|
using KfChatDotNetGui.ViewModels;
|
||||||
using KfChatDotNetGui.Views;
|
using KfChatDotNetGui.Views;
|
||||||
using Newtonsoft.Json;
|
|
||||||
using NLog;
|
using NLog;
|
||||||
|
|
||||||
namespace KfChatDotNetGui
|
namespace KfChatDotNetGui
|
||||||
@@ -27,7 +27,7 @@ namespace KfChatDotNetGui
|
|||||||
var dataContext = new MainWindowViewModel();
|
var dataContext = new MainWindowViewModel();
|
||||||
if (File.Exists("rooms.json"))
|
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;
|
dataContext.RoomList = rooms!.Rooms;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,10 +2,10 @@ using System;
|
|||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Net.Http.Headers;
|
using System.Net.Http.Headers;
|
||||||
|
using System.Text.Json;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using KfChatDotNetGui.Models;
|
using KfChatDotNetGui.Models;
|
||||||
using Newtonsoft.Json;
|
|
||||||
|
|
||||||
namespace KfChatDotNetGui.Helpers;
|
namespace KfChatDotNetGui.Helpers;
|
||||||
|
|
||||||
@@ -36,7 +36,7 @@ public static class ForumIdentity
|
|||||||
}
|
}
|
||||||
|
|
||||||
var accountJs = match.Groups[1].Value;
|
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.ReactiveUI" Version="11.0.10" />
|
||||||
<PackageReference Include="Avalonia.Themes.Fluent" Version="11.0.10" />
|
<PackageReference Include="Avalonia.Themes.Fluent" Version="11.0.10" />
|
||||||
<PackageReference Include="HtmlAgilityPack" Version="1.11.59" />
|
<PackageReference Include="HtmlAgilityPack" Version="1.11.59" />
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
|
||||||
<PackageReference Include="ReactiveUI" Version="19.5.72" />
|
<PackageReference Include="ReactiveUI" Version="19.5.72" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -1,20 +1,20 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using Newtonsoft.Json;
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
namespace KfChatDotNetGui.Models;
|
namespace KfChatDotNetGui.Models;
|
||||||
|
|
||||||
public class ForumIdentityModel
|
public class ForumIdentityModel
|
||||||
{
|
{
|
||||||
[JsonProperty("id")]
|
[JsonPropertyName("id")]
|
||||||
public int Id { get; set; }
|
public int Id { get; set; }
|
||||||
[JsonProperty("username")]
|
[JsonPropertyName("username")]
|
||||||
public string Username { get; set; }
|
public string Username { get; set; }
|
||||||
[JsonProperty("avatar_url")]
|
[JsonPropertyName("avatar_url")]
|
||||||
public Uri AvatarUrl { get; set; }
|
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
|
// 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; }
|
public List<string> IgnoredUsers { get; set; }
|
||||||
[JsonProperty("is_staff")]
|
[JsonPropertyName("is_staff")]
|
||||||
public bool IsStaff { get; set; }
|
public bool IsStaff { get; set; }
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Text.Json;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Avalonia;
|
using Avalonia;
|
||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
@@ -10,7 +11,6 @@ using Avalonia.Threading;
|
|||||||
using KfChatDotNetGui.Helpers;
|
using KfChatDotNetGui.Helpers;
|
||||||
using KfChatDotNetGui.Models;
|
using KfChatDotNetGui.Models;
|
||||||
using KfChatDotNetGui.ViewModels;
|
using KfChatDotNetGui.ViewModels;
|
||||||
using Newtonsoft.Json;
|
|
||||||
using NLog;
|
using NLog;
|
||||||
|
|
||||||
namespace KfChatDotNetGui.Views;
|
namespace KfChatDotNetGui.Views;
|
||||||
@@ -44,7 +44,8 @@ public partial class IdentitySettingsWindow : Window
|
|||||||
AntiDdosPow = (DataContext as IdentitySettingsWindowViewModel).AntiDdosPow,
|
AntiDdosPow = (DataContext as IdentitySettingsWindowViewModel).AntiDdosPow,
|
||||||
Username = (DataContext as IdentitySettingsWindowViewModel).Username
|
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)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -4,14 +4,12 @@ using System.Collections.ObjectModel;
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
|
using System.Text.Json;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Avalonia;
|
|
||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
using Avalonia.Input;
|
using Avalonia.Input;
|
||||||
using Avalonia.Interactivity;
|
using Avalonia.Interactivity;
|
||||||
using Avalonia.LogicalTree;
|
using Avalonia.LogicalTree;
|
||||||
using Avalonia.Markup.Xaml;
|
|
||||||
using Avalonia.Media.Imaging;
|
|
||||||
using Avalonia.Threading;
|
using Avalonia.Threading;
|
||||||
using KfChatDotNetGui.Models;
|
using KfChatDotNetGui.Models;
|
||||||
using KfChatDotNetGui.ViewModels;
|
using KfChatDotNetGui.ViewModels;
|
||||||
@@ -19,7 +17,6 @@ using KfChatDotNetWsClient;
|
|||||||
using KfChatDotNetWsClient.Models;
|
using KfChatDotNetWsClient.Models;
|
||||||
using KfChatDotNetWsClient.Models.Events;
|
using KfChatDotNetWsClient.Models.Events;
|
||||||
using KfChatDotNetWsClient.Models.Json;
|
using KfChatDotNetWsClient.Models.Json;
|
||||||
using Newtonsoft.Json;
|
|
||||||
using NLog;
|
using NLog;
|
||||||
using Websocket.Client;
|
using Websocket.Client;
|
||||||
|
|
||||||
@@ -31,9 +28,9 @@ namespace KfChatDotNetGui.Views
|
|||||||
// Using an empty config as we can update it later through the UpdateConfig method
|
// 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
|
// Having this instance created early is handy for wiring up the events
|
||||||
private ChatClient _chatClient = new(new ChatClientConfigModel());
|
private ChatClient _chatClient = new(new ChatClientConfigModel());
|
||||||
private SettingsModel _settings;
|
private SettingsModel _settings = null!;
|
||||||
private int _currentRoom;
|
private int _currentRoom;
|
||||||
private ForumIdentityModel _forumIdentity;
|
private ForumIdentityModel? _forumIdentity = null!;
|
||||||
|
|
||||||
public MainWindow()
|
public MainWindow()
|
||||||
{
|
{
|
||||||
@@ -62,7 +59,7 @@ namespace KfChatDotNetGui.Views
|
|||||||
{
|
{
|
||||||
_logger.Info($"Received delete event for following message IDs: {string.Join(',', messageIds)}");
|
_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
|
// 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 message in messages)
|
||||||
{
|
{
|
||||||
foreach (var innerMessage in message.Messages.Where(m => messageIds.Contains(m.MessageId)))
|
foreach (var innerMessage in message.Messages.Where(m => messageIds.Contains(m.MessageId)))
|
||||||
@@ -71,13 +68,13 @@ namespace KfChatDotNetGui.Views
|
|||||||
if (message.Messages.Count == 1)
|
if (message.Messages.Count == 1)
|
||||||
{
|
{
|
||||||
_logger.Info("Removing parent message box");
|
_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
|
// Go scavenging if there are multiple messages and we don't want to lose the lot
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
(DataContext as MainWindowViewModel)
|
((DataContext as MainWindowViewModel)!)
|
||||||
.Messages[(DataContext as MainWindowViewModel).Messages.IndexOf(message)].Messages
|
.Messages[((DataContext as MainWindowViewModel)!).Messages.IndexOf(message)].Messages
|
||||||
.Remove(innerMessage);
|
.Remove(innerMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -100,8 +97,8 @@ namespace KfChatDotNetGui.Views
|
|||||||
Dispatcher.UIThread.InvokeAsync(() =>
|
Dispatcher.UIThread.InvokeAsync(() =>
|
||||||
{
|
{
|
||||||
UpdateStatus("Reconnected to SneedChat. Reason was " + reconnectionInfo.Type);
|
UpdateStatus("Reconnected to SneedChat. Reason was " + reconnectionInfo.Type);
|
||||||
(DataContext as MainWindowViewModel).Messages.Clear();
|
((DataContext as MainWindowViewModel)!).Messages.Clear();
|
||||||
(DataContext as MainWindowViewModel).UserList.Clear();
|
((DataContext as MainWindowViewModel)!).UserList.Clear();
|
||||||
});
|
});
|
||||||
|
|
||||||
_chatClient.JoinRoom(_currentRoom);
|
_chatClient.JoinRoom(_currentRoom);
|
||||||
@@ -112,8 +109,8 @@ namespace KfChatDotNetGui.Views
|
|||||||
var context = new IdentitySettingsWindowViewModel();
|
var context = new IdentitySettingsWindowViewModel();
|
||||||
if (File.Exists("settings.json"))
|
if (File.Exists("settings.json"))
|
||||||
{
|
{
|
||||||
var settings = JsonConvert.DeserializeObject<SettingsModel>(File.ReadAllText("settings.json"));
|
var settings = JsonSerializer.Deserialize<SettingsModel>(File.ReadAllText("settings.json"));
|
||||||
context.WsUri = settings.WsUri;
|
context.WsUri = settings!.WsUri;
|
||||||
context.XfSessionToken = settings.XfSessionToken;
|
context.XfSessionToken = settings.XfSessionToken;
|
||||||
context.ReconnectTimeout = settings.ReconnectTimeout;
|
context.ReconnectTimeout = settings.ReconnectTimeout;
|
||||||
context.AntiDdosPow = settings.AntiDdosPow;
|
context.AntiDdosPow = settings.AntiDdosPow;
|
||||||
@@ -146,13 +143,13 @@ namespace KfChatDotNetGui.Views
|
|||||||
}
|
}
|
||||||
ReloadSettings();
|
ReloadSettings();
|
||||||
UpdateStatus("Testing XenForo token validity");
|
UpdateStatus("Testing XenForo token validity");
|
||||||
ForumIdentityModel forumIdentity;
|
ForumIdentityModel? forumIdentity;
|
||||||
if (string.IsNullOrEmpty(_settings.Username))
|
if (string.IsNullOrEmpty(_settings.Username))
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
forumIdentity = await Helpers.ForumIdentity.GetForumIdentity(_settings.XfSessionToken,
|
forumIdentity = (await Helpers.ForumIdentity.GetForumIdentity(_settings.XfSessionToken,
|
||||||
new Uri($"https://{_settings.WsUri.Host}/test-chat"), _settings.AntiDdosPow);
|
new Uri($"https://{_settings.WsUri.Host}/test-chat"), _settings.AntiDdosPow));
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
@@ -184,12 +181,12 @@ namespace KfChatDotNetGui.Views
|
|||||||
|
|
||||||
UpdateStatus("Token works! It belongs to " + forumIdentity.Username);
|
UpdateStatus("Token works! It belongs to " + forumIdentity.Username);
|
||||||
_forumIdentity = forumIdentity;
|
_forumIdentity = forumIdentity;
|
||||||
(DataContext as MainWindowViewModel).UserId = _forumIdentity.Id;
|
((DataContext as MainWindowViewModel)!).UserId = _forumIdentity.Id;
|
||||||
var roomListControl = this.FindControl<ListBox>("RoomList");
|
var roomListControl = this.FindControl<ListBox>("RoomList");
|
||||||
RoomSettingsModel.RoomList initialRoom;
|
RoomSettingsModel.RoomList initialRoom;
|
||||||
if (roomListControl.SelectedItem == null)
|
if (roomListControl!.SelectedItem == null)
|
||||||
{
|
{
|
||||||
initialRoom = (DataContext as MainWindowViewModel).RoomList.First();
|
initialRoom = (DataContext as MainWindowViewModel)?.RoomList.First()!;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -205,7 +202,7 @@ namespace KfChatDotNetGui.Views
|
|||||||
});
|
});
|
||||||
|
|
||||||
await _chatClient.StartWsClient();
|
await _chatClient.StartWsClient();
|
||||||
_chatClient.JoinRoom(initialRoom.Id);
|
_chatClient.JoinRoom(initialRoom!.Id);
|
||||||
_currentRoom = initialRoom.Id;
|
_currentRoom = initialRoom.Id;
|
||||||
UpdateStatus("Connected!");
|
UpdateStatus("Connected!");
|
||||||
}
|
}
|
||||||
@@ -216,12 +213,12 @@ namespace KfChatDotNetGui.Views
|
|||||||
{
|
{
|
||||||
foreach (var user in users)
|
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!");
|
_logger.Info($"{user.Username} ({user.Id}) is already in the list but has joined again. New tab? Ignoring!");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
(DataContext as MainWindowViewModel).UserList.Add(new MainWindowViewModel.UserListViewModel
|
((DataContext as MainWindowViewModel)!).UserList.Add(new MainWindowViewModel.UserListViewModel
|
||||||
{
|
{
|
||||||
Id = user.Id,
|
Id = user.Id,
|
||||||
Name = user.Username
|
Name = user.Username
|
||||||
@@ -237,13 +234,13 @@ namespace KfChatDotNetGui.Views
|
|||||||
{
|
{
|
||||||
foreach (var id in userIds)
|
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)
|
if (row == null)
|
||||||
{
|
{
|
||||||
_logger.Info($"A user ({id}) who isn't in the list has parted, ignoring!");
|
_logger.Info($"A user ({id}) who isn't in the list has parted, ignoring!");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
(DataContext as MainWindowViewModel).UserList.Remove(row);
|
((DataContext as MainWindowViewModel)!).UserList.Remove(row);
|
||||||
}
|
}
|
||||||
UpdateUserTotalStatus();
|
UpdateUserTotalStatus();
|
||||||
});
|
});
|
||||||
@@ -253,7 +250,7 @@ namespace KfChatDotNetGui.Views
|
|||||||
{
|
{
|
||||||
Dispatcher.UIThread.InvokeAsync(() =>
|
Dispatcher.UIThread.InvokeAsync(() =>
|
||||||
{
|
{
|
||||||
var previousMessage = (DataContext as MainWindowViewModel).Messages.LastOrDefault();
|
var previousMessage = ((DataContext as MainWindowViewModel)!).Messages.LastOrDefault();
|
||||||
if (previousMessage == null)
|
if (previousMessage == null)
|
||||||
{
|
{
|
||||||
previousMessage = new MainWindowViewModel.MessageViewModel {AuthorId = -1};
|
previousMessage = new MainWindowViewModel.MessageViewModel {AuthorId = -1};
|
||||||
@@ -261,7 +258,6 @@ namespace KfChatDotNetGui.Views
|
|||||||
foreach (var message in messages)
|
foreach (var message in messages)
|
||||||
{
|
{
|
||||||
_logger.Info("Received message, data payload next");
|
_logger.Info("Received message, data payload next");
|
||||||
_logger.Info(JsonConvert.SerializeObject(message, Formatting.Indented));
|
|
||||||
if (message.RoomId != _currentRoom)
|
if (message.RoomId != _currentRoom)
|
||||||
{
|
{
|
||||||
_logger.Info($"Message {message.MessageId} belongs to another room (we're in {_currentRoom}, this one was for {message.RoomId}), ignoring.");
|
_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, " +
|
_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.");
|
"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))
|
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)
|
if (previousMessage.AuthorId == message.Author.Id)
|
||||||
{
|
{
|
||||||
_logger.Info("Found a message from the same author, merging");
|
_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
|
lastMessage.Messages.Add(new MainWindowViewModel.InnerMessageViewModel
|
||||||
{
|
{
|
||||||
Message = WebUtility.HtmlDecode(message.MessageRaw),
|
Message = WebUtility.HtmlDecode(message.MessageRaw),
|
||||||
@@ -312,11 +308,11 @@ namespace KfChatDotNetGui.Views
|
|||||||
PostedAt = message.MessageDate.LocalDateTime,
|
PostedAt = message.MessageDate.LocalDateTime,
|
||||||
AuthorId = message.Author.Id
|
AuthorId = message.Author.Id
|
||||||
};
|
};
|
||||||
(DataContext as MainWindowViewModel).Messages.Add(viewMessage);
|
((DataContext as MainWindowViewModel)!).Messages.Add(viewMessage);
|
||||||
previousMessage = viewMessage;
|
previousMessage = viewMessage;
|
||||||
}
|
}
|
||||||
var messagesControl = this.FindControl<ListBox>("ChatMessageList");
|
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();
|
var context = new RoomSettingsWindowViewModel();
|
||||||
if (File.Exists("rooms.json"))
|
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();
|
context.RoomList.Clear();
|
||||||
foreach (var room in settings.Rooms)
|
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");
|
_logger.Error("Was asked to reload the settings but settings.json doesn't exist so I won't bother");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var settings = JsonConvert.DeserializeObject<SettingsModel>(File.ReadAllText("settings.json"));
|
var settings = JsonSerializer.Deserialize<SettingsModel>(File.ReadAllText("settings.json"));
|
||||||
_settings = settings;
|
_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");
|
_logger.Error("Was asked to reload the room list but rooms.json doesn't exist so I won't bother");
|
||||||
return;
|
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;
|
(DataContext as MainWindowViewModel)!.RoomList = rooms!.Rooms;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -395,8 +391,8 @@ namespace KfChatDotNetGui.Views
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
UpdateStatus($"Connected! Changing to {room.Name}");
|
UpdateStatus($"Connected! Changing to {room.Name}");
|
||||||
(DataContext as MainWindowViewModel).Messages.Clear();
|
((DataContext as MainWindowViewModel)!).Messages.Clear();
|
||||||
(DataContext as MainWindowViewModel).UserList.Clear();
|
(DataContext as MainWindowViewModel)?.UserList.Clear();
|
||||||
_chatClient.JoinRoom(room.Id);
|
_chatClient.JoinRoom(room.Id);
|
||||||
_currentRoom = room.Id;
|
_currentRoom = room.Id;
|
||||||
}
|
}
|
||||||
@@ -468,7 +464,7 @@ namespace KfChatDotNetGui.Views
|
|||||||
|
|
||||||
private void OuterMessageRow_OnPointerEnter(object? sender, PointerEventArgs e)
|
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)
|
foreach (var child in children)
|
||||||
{
|
{
|
||||||
// Bit of a ghetto hack but it ensures that there's only ever one subscriber
|
// 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;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Net.Http.Headers;
|
using System.Net.Http.Headers;
|
||||||
|
using System.Text.Json;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Avalonia;
|
using Avalonia;
|
||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
@@ -16,7 +17,6 @@ using Avalonia.Threading;
|
|||||||
using HtmlAgilityPack;
|
using HtmlAgilityPack;
|
||||||
using KfChatDotNetGui.Models;
|
using KfChatDotNetGui.Models;
|
||||||
using KfChatDotNetGui.ViewModels;
|
using KfChatDotNetGui.ViewModels;
|
||||||
using Newtonsoft.Json;
|
|
||||||
using NLog;
|
using NLog;
|
||||||
|
|
||||||
namespace KfChatDotNetGui.Views;
|
namespace KfChatDotNetGui.Views;
|
||||||
@@ -54,7 +54,9 @@ public partial class RoomSettingsWindow : Window
|
|||||||
Name = room.Name
|
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)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
@@ -102,7 +104,7 @@ public partial class RoomSettingsWindow : Window
|
|||||||
var kfDomain = "kiwifarms.net";
|
var kfDomain = "kiwifarms.net";
|
||||||
if (File.Exists("settings.json"))
|
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;
|
kfDomain = settings.WsUri.Host;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,10 +8,8 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
|
||||||
<PackageReference Include="NLog" Version="5.3.2" />
|
<PackageReference Include="NLog" Version="5.3.2" />
|
||||||
<PackageReference Include="PuppeteerSharp" Version="18.0.1" />
|
<PackageReference Include="PuppeteerSharp" Version="18.0.1" />
|
||||||
<PackageReference Include="Spectre.Console" Version="0.48.0" />
|
|
||||||
<PackageReference Include="System.Text.Json" Version="8.0.3" />
|
<PackageReference Include="System.Text.Json" Version="8.0.3" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
|||||||
@@ -1,32 +1,31 @@
|
|||||||
using KfChatDotNetKickBot.Services;
|
using System.Text.Json;
|
||||||
|
using KfChatDotNetKickBot.Services;
|
||||||
using KfChatDotNetWsClient;
|
using KfChatDotNetWsClient;
|
||||||
using KfChatDotNetWsClient.Models;
|
using KfChatDotNetWsClient.Models;
|
||||||
using KfChatDotNetWsClient.Models.Events;
|
using KfChatDotNetWsClient.Models.Events;
|
||||||
using KfChatDotNetWsClient.Models.Json;
|
using KfChatDotNetWsClient.Models.Json;
|
||||||
using KickWsClient.Models;
|
using KickWsClient.Models;
|
||||||
using Newtonsoft.Json;
|
|
||||||
using NLog;
|
using NLog;
|
||||||
using Spectre.Console;
|
|
||||||
using Websocket.Client;
|
using Websocket.Client;
|
||||||
|
|
||||||
namespace KfChatDotNetKickBot;
|
namespace KfChatDotNetKickBot;
|
||||||
|
|
||||||
public class KickBot
|
public class KickBot
|
||||||
{
|
{
|
||||||
private ChatClient _kfClient;
|
private readonly ChatClient _kfClient;
|
||||||
private KickWsClient.KickWsClient _kickClient;
|
private readonly KickWsClient.KickWsClient _kickClient;
|
||||||
private Logger _logger = LogManager.GetCurrentClassLogger();
|
private readonly Logger _logger = LogManager.GetCurrentClassLogger();
|
||||||
private Models.ConfigModel _config;
|
private readonly Models.ConfigModel _config;
|
||||||
private Thread _pingThread;
|
private readonly bool _pingEnabled = true;
|
||||||
private bool _pingEnabled = true;
|
private bool _gambaSeshPresent;
|
||||||
private bool _gambaSeshPresent = false;
|
private string _xfSessionToken = null!;
|
||||||
private string _xfSessionToken;
|
|
||||||
// Oh no it's an ever expanding list that may never get cleaned up!
|
// Oh no it's an ever expanding list that may never get cleaned up!
|
||||||
// BUY MORE RAM
|
// BUY MORE RAM
|
||||||
private List<int> _seenMsgIds = new List<int>();
|
private readonly List<int> _seenMsgIds = [];
|
||||||
// Suppresses the command handler on initial start so it doesn't pick up things already handled on restart
|
// Suppresses the command handler on initial start, so it doesn't pick up things already handled on restart
|
||||||
private bool _initialStartCooldown = true;
|
private bool _initialStartCooldown = true;
|
||||||
|
private readonly CancellationToken _cancellationToken = new();
|
||||||
|
|
||||||
public KickBot()
|
public KickBot()
|
||||||
{
|
{
|
||||||
_logger.Info("Bot starting!");
|
_logger.Info("Bot starting!");
|
||||||
@@ -37,9 +36,9 @@ public class KickBot
|
|||||||
Environment.Exit(1);
|
Environment.Exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
_config = JsonConvert.DeserializeObject<Models.ConfigModel>(File.ReadAllText(configPath)) ??
|
_config = JsonSerializer.Deserialize<Models.ConfigModel>(File.ReadAllText(configPath)) ??
|
||||||
throw new InvalidOperationException();
|
throw new InvalidOperationException();
|
||||||
RefreshXfToken();
|
RefreshXfToken().Wait(_cancellationToken);
|
||||||
|
|
||||||
_kfClient = new ChatClient(new ChatClientConfigModel
|
_kfClient = new ChatClient(new ChatClientConfigModel
|
||||||
{
|
{
|
||||||
@@ -66,22 +65,22 @@ public class KickBot
|
|||||||
_kickClient.OnPusherSubscriptionSucceeded += OnPusherSubscriptionSucceeded;
|
_kickClient.OnPusherSubscriptionSucceeded += OnPusherSubscriptionSucceeded;
|
||||||
_kickClient.OnStopStreamBroadcast += OnStopStreamBroadcast;
|
_kickClient.OnStopStreamBroadcast += OnStopStreamBroadcast;
|
||||||
|
|
||||||
_kfClient.StartWsClient().Wait();
|
_kfClient.StartWsClient().Wait(_cancellationToken);
|
||||||
_kfClient.JoinRoom(_config.KfChatRoomId);
|
_kfClient.JoinRoom(_config.KfChatRoomId);
|
||||||
|
|
||||||
_kickClient.StartWsClient().Wait();
|
_kickClient.StartWsClient().Wait(_cancellationToken);
|
||||||
foreach (var channel in _config.PusherChannels)
|
foreach (var channel in _config.PusherChannels)
|
||||||
{
|
{
|
||||||
_kickClient.SendPusherSubscribe(channel);
|
_kickClient.SendPusherSubscribe(channel);
|
||||||
}
|
}
|
||||||
|
|
||||||
_pingThread = new Thread(PingThread);
|
_logger.Debug("Creating ping thread and starting it");
|
||||||
_pingThread.Start();
|
var pingThread = new Thread(PingThread);
|
||||||
|
pingThread.Start();
|
||||||
|
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
var input = AnsiConsole.Prompt(new TextPrompt<string>("Enter Message:"));
|
Console.ReadLine();
|
||||||
_kfClient.SendMessage(input);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -89,11 +88,11 @@ public class KickBot
|
|||||||
{
|
{
|
||||||
_logger.Error($"Couldn't join the room. KF returned: {message}");
|
_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.");
|
_logger.Error("This is likely due to the session cookie expiring. Retrieving a new one.");
|
||||||
RefreshXfToken();
|
RefreshXfToken().Wait(_cancellationToken);
|
||||||
_kfClient.UpdateToken(_xfSessionToken);
|
_kfClient.UpdateToken(_xfSessionToken);
|
||||||
_logger.Info("Retrieved fresh token. Reconnecting.");
|
_logger.Info("Retrieved fresh token. Reconnecting.");
|
||||||
_kfClient.Disconnect();
|
_kfClient.Disconnect();
|
||||||
_kfClient.StartWsClient().Wait();
|
_kfClient.StartWsClient().Wait(_cancellationToken);
|
||||||
_logger.Info("Client should be reconnecting now");
|
_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,
|
var cookie = await KfTokenService.FetchSessionTokenAsync(_config.KfDomain, _config.KfUsername, _config.KfPassword,
|
||||||
_config.ChromiumPath, _config.KfProxy).Result;
|
_config.ChromiumPath, _config.KfProxy);
|
||||||
_logger.Debug($"FetchSessionTokenAsync returned {cookie}");
|
_logger.Debug($"FetchSessionTokenAsync returned {cookie}");
|
||||||
_xfSessionToken = cookie;
|
_xfSessionToken = cookie;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnStreamerIsLive(object sender, KickModels.StreamerIsLiveEventModel? e)
|
private void OnStreamerIsLive(object sender, KickModels.StreamerIsLiveEventModel? e)
|
||||||
{
|
{
|
||||||
AnsiConsole.MarkupLine("[green]Streamer is live!!![/]");
|
_sendChatMessage($"Bossman Live! {e?.Livestream.SessionTitle} https://kick.com/bossmanjack");
|
||||||
_sendChatMessage($"Bossman Live! {e?.Livestream.SessionTitle} https://kick.com/bossmanjack [i]This action was automated");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnStopStreamBroadcast(object sender, KickModels.StopStreamBroadcastEventModel? e)
|
private void OnStopStreamBroadcast(object sender, KickModels.StopStreamBroadcastEventModel? e)
|
||||||
{
|
{
|
||||||
AnsiConsole.MarkupLine("[green]Stream stopped!!![/]");
|
_sendChatMessage("The stream is so over. :lossmanjack:");
|
||||||
_sendChatMessage("The stream is so over. [i]This action was automated");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnKfChatMessage(object sender, List<MessageModel> messages, MessagesJsonModel jsonPayload)
|
private void OnKfChatMessage(object sender, List<MessageModel> messages, MessagesJsonModel jsonPayload)
|
||||||
@@ -134,7 +131,7 @@ public class KickBot
|
|||||||
_logger.Debug($"Received {messages.Count} message(s)");
|
_logger.Debug($"Received {messages.Count} message(s)");
|
||||||
foreach (var message in messages)
|
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 (!_seenMsgIds.Contains(message.MessageId) && !_initialStartCooldown)
|
||||||
{
|
{
|
||||||
if (message.MessageRaw.StartsWith("!time"))
|
if (message.MessageRaw.StartsWith("!time"))
|
||||||
@@ -149,16 +146,17 @@ public class KickBot
|
|||||||
}
|
}
|
||||||
else if (message.MessageRaw.StartsWith("!insanity"))
|
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");
|
_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);
|
_sendChatMessage("[img]https://i.postimg.cc/fTw6tGWZ/ineedmoneydumbfuck.png[/img]", true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
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);
|
_seenMsgIds.Add(message.MessageId);
|
||||||
}
|
}
|
||||||
@@ -168,7 +166,7 @@ public class KickBot
|
|||||||
{
|
{
|
||||||
if (_gambaSeshPresent && _config.EnableGambaSeshDetect && !bypassSeshDetect)
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -177,15 +175,14 @@ public class KickBot
|
|||||||
|
|
||||||
private void OnKickChatMessage(object sender, KickModels.ChatMessageEventModel? e)
|
private void OnKickChatMessage(object sender, KickModels.ChatMessageEventModel? e)
|
||||||
{
|
{
|
||||||
if (e == null) return;
|
if (e == null) return;
|
||||||
AnsiConsole.MarkupLine($"[green]Kick[/] <{e.Sender.Username}> {e.Content.EscapeMarkup()} ({e.CreatedAt.LocalDateTime.ToShortTimeString()})");
|
_logger.Info($"Kick ({e.CreatedAt.LocalDateTime.ToLocalTime():HH:mm:ss}) <{e.Sender.Username}> {e.Content}");
|
||||||
AnsiConsole.MarkupLine($"[cyan]BB Code Translation: {e.Content.TranslateKickEmotes().EscapeMarkup()}[/]");
|
_logger.Debug($"BB Code Translation: {e.Content.TranslateKickEmotes()}");
|
||||||
|
|
||||||
if (e.Sender.Slug == "bossmanjack")
|
if (e.Sender.Slug != "bossmanjack") return;
|
||||||
{
|
|
||||||
AnsiConsole.MarkupLine("[green]Message from BossmanJack[/]");
|
_logger.Debug("Message from BossmanJack");
|
||||||
_sendChatMessage($"[img]{_config.KickIcon}[/img] BossmanJack: {e.Content.TranslateKickEmotes()}");
|
_sendChatMessage($"[img]{_config.KickIcon}[/img] BossmanJack: {e.Content.TranslateKickEmotes()}");
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnUsersJoined(object sender, List<UserModel> users, UsersJsonModel jsonPayload)
|
private void OnUsersJoined(object sender, List<UserModel> users, UsersJsonModel jsonPayload)
|
||||||
@@ -195,9 +192,10 @@ public class KickBot
|
|||||||
{
|
{
|
||||||
if (user.Id == _config.GambaSeshUserId && _config.EnableGambaSeshDetect)
|
if (user.Id == _config.GambaSeshUserId && _config.EnableGambaSeshDetect)
|
||||||
{
|
{
|
||||||
|
_logger.Info("GambaSesh is now present");
|
||||||
_gambaSeshPresent = true;
|
_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)
|
if (userIds.Contains(_config.GambaSeshUserId) && _config.EnableGambaSeshDetect)
|
||||||
{
|
{
|
||||||
|
_logger.Info("GambaSesh is no longer present");
|
||||||
_gambaSeshPresent = false;
|
_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)
|
private void OnKfWsDisconnected(object sender, DisconnectionInfo disconnectionInfo)
|
||||||
|
|||||||
@@ -40,11 +40,11 @@ public class KfTokenService
|
|||||||
await page.WaitForSelectorAsync("img[alt=\"Kiwi Farms\"]");
|
await page.WaitForSelectorAsync("img[alt=\"Kiwi Farms\"]");
|
||||||
if (await page.QuerySelectorAsync("html[data-template=\"login\"]") == null)
|
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();
|
return await GetXfSessionCookie();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
var usernameFieldSelector = await page.QuerySelectorAsync("input[autocomplete=\"username\"]");
|
var usernameFieldSelector = await page.QuerySelectorAsync("input[autocomplete=\"username\"]");
|
||||||
var passwordFieldSelector = await page.QuerySelectorAsync("input[autocomplete=\"current-password\"]");
|
var passwordFieldSelector = await page.QuerySelectorAsync("input[autocomplete=\"current-password\"]");
|
||||||
var loginButtonSelector = await page.QuerySelectorAsync("div[class=\"formSubmitRow-controls\"] > button[type=\"submit\"]");
|
var loginButtonSelector = await page.QuerySelectorAsync("div[class=\"formSubmitRow-controls\"] > button[type=\"submit\"]");
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Net.WebSockets;
|
using System.Net.WebSockets;
|
||||||
|
using System.Text.Json;
|
||||||
|
using System.Xml;
|
||||||
using KfChatDotNetWsClient.Models;
|
using KfChatDotNetWsClient.Models;
|
||||||
using KfChatDotNetWsClient.Models.Events;
|
using KfChatDotNetWsClient.Models.Events;
|
||||||
using KfChatDotNetWsClient.Models.Json;
|
using KfChatDotNetWsClient.Models.Json;
|
||||||
using Newtonsoft.Json;
|
|
||||||
using NLog;
|
using NLog;
|
||||||
using Websocket.Client;
|
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
|
// 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>();
|
Dictionary<string, object> packetType = new Dictionary<string, object>();
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
packetType = JsonConvert.DeserializeObject<Dictionary<string, object>>(message.Text)!;
|
packetType = JsonSerializer.Deserialize<Dictionary<string, object>>(message.Text)!;
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
@@ -198,23 +199,21 @@ public class ChatClient
|
|||||||
|
|
||||||
public void EditMessage(int messageId, string newMessage)
|
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 = JsonSerializer.Serialize(new EditMessageJsonModel {Id = messageId, Message = newMessage});
|
||||||
var payload = JsonConvert.SerializeObject(new EditMessageJsonModel {Id = messageId, Message = newMessage},
|
|
||||||
Formatting.None);
|
|
||||||
_logger.Debug($"Editing {messageId} with '{newMessage}'");
|
_logger.Debug($"Editing {messageId} with '{newMessage}'");
|
||||||
_wsClient.Send($"/edit {payload}");
|
_wsClient.Send($"/edit {payload}");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void WsDeleteMessagesReceived(ResponseMessage message)
|
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)}");
|
_logger.Debug($"Received delete packet for messages: {string.Join(',', data.MessageIdsToDelete)}");
|
||||||
OnDeleteMessages?.Invoke(this, data.MessageIdsToDelete);
|
OnDeleteMessages?.Invoke(this, data.MessageIdsToDelete);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void WsChatMessagesReceived(ResponseMessage message)
|
private void WsChatMessagesReceived(ResponseMessage message)
|
||||||
{
|
{
|
||||||
var data = JsonConvert.DeserializeObject<MessagesJsonModel>(message.Text);
|
var data = JsonSerializer.Deserialize<MessagesJsonModel>(message.Text);
|
||||||
var messages = new List<MessageModel>();
|
var messages = new List<MessageModel>();
|
||||||
foreach (var chatMessage in data.Messages)
|
foreach (var chatMessage in data.Messages)
|
||||||
{
|
{
|
||||||
@@ -250,14 +249,14 @@ public class ChatClient
|
|||||||
_logger.Debug($"Received {messages.Count} chat messages");
|
_logger.Debug($"Received {messages.Count} chat messages");
|
||||||
if (messages.Count == 1)
|
if (messages.Count == 1)
|
||||||
{
|
{
|
||||||
_logger.Debug($"{JsonConvert.SerializeObject(messages[0], Formatting.Indented)}");
|
_logger.Debug($"{JsonSerializer.Serialize(messages[0])}");
|
||||||
}
|
}
|
||||||
OnMessages?.Invoke(this, messages, data);
|
OnMessages?.Invoke(this, messages, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void WsChatUsersJoined(ResponseMessage message)
|
private void WsChatUsersJoined(ResponseMessage message)
|
||||||
{
|
{
|
||||||
var data = JsonConvert.DeserializeObject<UsersJsonModel>(message.Text);
|
var data = JsonSerializer.Deserialize<UsersJsonModel>(message.Text);
|
||||||
var users = new List<UserModel>();
|
var users = new List<UserModel>();
|
||||||
foreach (var user in data.Users.Keys)
|
foreach (var user in data.Users.Keys)
|
||||||
{
|
{
|
||||||
@@ -277,7 +276,7 @@ public class ChatClient
|
|||||||
private void WsChatUsersParted(ResponseMessage message)
|
private void WsChatUsersParted(ResponseMessage message)
|
||||||
{
|
{
|
||||||
// {"user":{"1337":false}}
|
// {"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();
|
var usersParted = data!["user"].Select(user => int.Parse(user.Key)).ToList();
|
||||||
_logger.Debug($"Following users have parted: {string.Join(',', usersParted)}");
|
_logger.Debug($"Following users have parted: {string.Join(',', usersParted)}");
|
||||||
OnUsersParted?.Invoke(this, usersParted);
|
OnUsersParted?.Invoke(this, usersParted);
|
||||||
|
|||||||
@@ -8,7 +8,6 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
|
||||||
<PackageReference Include="NLog" Version="5.2.8" />
|
<PackageReference Include="NLog" Version="5.2.8" />
|
||||||
<PackageReference Include="Websocket.Client" Version="5.1.1" />
|
<PackageReference Include="Websocket.Client" Version="5.1.1" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
using Newtonsoft.Json;
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
namespace KfChatDotNetWsClient.Models.Json;
|
namespace KfChatDotNetWsClient.Models.Json;
|
||||||
|
|
||||||
public class DeleteMessagesJsonModel
|
public class DeleteMessagesJsonModel
|
||||||
{
|
{
|
||||||
[JsonProperty("delete")]
|
[JsonPropertyName("delete")]
|
||||||
public List<int> MessageIdsToDelete { get; set; }
|
public List<int> MessageIdsToDelete { get; set; }
|
||||||
}
|
}
|
||||||
@@ -1,12 +1,12 @@
|
|||||||
using Newtonsoft.Json;
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
namespace KfChatDotNetWsClient.Models.Json;
|
namespace KfChatDotNetWsClient.Models.Json;
|
||||||
|
|
||||||
public class EditMessageJsonModel
|
public class EditMessageJsonModel
|
||||||
{
|
{
|
||||||
[JsonProperty("id")]
|
[JsonPropertyName("id")]
|
||||||
public int Id { get; set; }
|
public int Id { get; set; }
|
||||||
|
|
||||||
[JsonProperty("message")]
|
[JsonPropertyName("message")]
|
||||||
public string Message { get; set; }
|
public string Message { get; set; }
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
using Newtonsoft.Json;
|
using System.Text.Json.Serialization;
|
||||||
using Newtonsoft.Json.Converters;
|
|
||||||
|
|
||||||
namespace KfChatDotNetWsClient.Models.Json;
|
namespace KfChatDotNetWsClient.Models.Json;
|
||||||
|
|
||||||
@@ -28,32 +27,32 @@ public class MessagesJsonModel
|
|||||||
{
|
{
|
||||||
public class AuthorModel
|
public class AuthorModel
|
||||||
{
|
{
|
||||||
[JsonProperty("id")]
|
[JsonPropertyName("id")]
|
||||||
public int Id { get; set; }
|
public int Id { get; set; }
|
||||||
[JsonProperty("username")]
|
[JsonPropertyName("username")]
|
||||||
public string Username { get; set; }
|
public string Username { get; set; }
|
||||||
[JsonProperty("avatar_url")]
|
[JsonPropertyName("avatar_url")]
|
||||||
public Uri AvatarUrl { get; set; }
|
public Uri AvatarUrl { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class MessageModel
|
public class MessageModel
|
||||||
{
|
{
|
||||||
[JsonProperty("author")]
|
[JsonPropertyName("author")]
|
||||||
public AuthorModel Author { get; set; }
|
public AuthorModel Author { get; set; }
|
||||||
[JsonProperty("message")]
|
[JsonPropertyName("message")]
|
||||||
public string Message { get; set; }
|
public string Message { get; set; }
|
||||||
[JsonProperty("message_id")]
|
[JsonPropertyName("message_id")]
|
||||||
public int MessageId { get; set; }
|
public int MessageId { get; set; }
|
||||||
[JsonProperty("message_edit_date")]
|
[JsonPropertyName("message_edit_date")]
|
||||||
public int MessageEditDate { get; set; }
|
public int MessageEditDate { get; set; }
|
||||||
[JsonProperty("message_date")]
|
[JsonPropertyName("message_date")]
|
||||||
public int MessageDate { get; set; }
|
public int MessageDate { get; set; }
|
||||||
[JsonProperty("message_raw")]
|
[JsonPropertyName("message_raw")]
|
||||||
public string MessageRaw { get; set; }
|
public string MessageRaw { get; set; }
|
||||||
[JsonProperty("room_id")]
|
[JsonPropertyName("room_id")]
|
||||||
public int RoomId { get; set; }
|
public int RoomId { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
[JsonProperty("messages")]
|
[JsonPropertyName("messages")]
|
||||||
public List<MessageModel> Messages { get; set; }
|
public List<MessageModel> Messages { get; set; }
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
using Newtonsoft.Json;
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
namespace KfChatDotNetWsClient.Models.Json;
|
namespace KfChatDotNetWsClient.Models.Json;
|
||||||
|
|
||||||
// {
|
// {
|
||||||
@@ -17,16 +16,16 @@ public class UsersJsonModel
|
|||||||
{
|
{
|
||||||
public class UserModel
|
public class UserModel
|
||||||
{
|
{
|
||||||
[JsonProperty("id")]
|
[JsonPropertyName("id")]
|
||||||
public int Id { get; set; }
|
public int Id { get; set; }
|
||||||
[JsonProperty("username")]
|
[JsonPropertyName("username")]
|
||||||
public string Username { get; set; }
|
public string Username { get; set; }
|
||||||
[JsonProperty("avatar_url")]
|
[JsonPropertyName("avatar_url")]
|
||||||
public Uri AvatarUrl { get; set; }
|
public Uri AvatarUrl { get; set; }
|
||||||
[JsonProperty("last_activity")]
|
[JsonPropertyName("last_activity")]
|
||||||
public int LastActivity { get; set; }
|
public int LastActivity { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
[JsonProperty("users")]
|
[JsonPropertyName("users")]
|
||||||
public Dictionary<string, UserModel> Users { get; set; }
|
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;
|
||||||
using System.Net.WebSockets;
|
using System.Net.WebSockets;
|
||||||
|
using System.Text.Json;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
using KickWsClient.Models;
|
using KickWsClient.Models;
|
||||||
using Newtonsoft.Json;
|
|
||||||
using Websocket.Client;
|
using Websocket.Client;
|
||||||
using NLog;
|
using NLog;
|
||||||
|
|
||||||
@@ -114,7 +115,7 @@ public class KickWsClient
|
|||||||
public void SendPusherPacket(string eventName, object data)
|
public void SendPusherPacket(string eventName, object data)
|
||||||
{
|
{
|
||||||
var pkt = new PusherModels.BasePusherRequestModel { Event = eventName, Data = 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("Sending message to Pusher");
|
||||||
_logger.Debug(json);
|
_logger.Debug(json);
|
||||||
_wsClient.Send(json);
|
_wsClient.Send(json);
|
||||||
@@ -152,6 +153,10 @@ public class KickWsClient
|
|||||||
private void WsMessageReceived(ResponseMessage message)
|
private void WsMessageReceived(ResponseMessage message)
|
||||||
{
|
{
|
||||||
OnWsMessageReceived?.Invoke(this, message);
|
OnWsMessageReceived?.Invoke(this, message);
|
||||||
|
var options = new JsonSerializerOptions
|
||||||
|
{
|
||||||
|
NumberHandling = JsonNumberHandling.AllowReadingFromString
|
||||||
|
};
|
||||||
|
|
||||||
if (message.Text == null)
|
if (message.Text == null)
|
||||||
{
|
{
|
||||||
@@ -162,7 +167,7 @@ public class KickWsClient
|
|||||||
PusherModels.BasePusherEventModel pusherMsg;
|
PusherModels.BasePusherEventModel pusherMsg;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
pusherMsg = JsonConvert.DeserializeObject<PusherModels.BasePusherEventModel>(message.Text) ??
|
pusherMsg = JsonSerializer.Deserialize<PusherModels.BasePusherEventModel>(message.Text, options) ??
|
||||||
throw new InvalidOperationException();
|
throw new InvalidOperationException();
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
@@ -182,7 +187,7 @@ public class KickWsClient
|
|||||||
case "pusher:connection_established":
|
case "pusher:connection_established":
|
||||||
{
|
{
|
||||||
var data =
|
var data =
|
||||||
JsonConvert.DeserializeObject<PusherModels.PusherConnectionEstablishedEventModel>(pusherMsg.Data);
|
JsonSerializer.Deserialize<PusherModels.PusherConnectionEstablishedEventModel>(pusherMsg.Data, options);
|
||||||
OnPusherConnectionEstablished?.Invoke(this, data);
|
OnPusherConnectionEstablished?.Invoke(this, data);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -194,67 +199,67 @@ public class KickWsClient
|
|||||||
return;
|
return;
|
||||||
case @"App\Events\FollowersUpdated":
|
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);
|
OnFollowersUpdated?.Invoke(this, data);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
case @"App\Events\ChatMessageEvent":
|
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);
|
OnChatMessage?.Invoke(this, data);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
case @"App\Events\ChannelSubscriptionEvent":
|
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);
|
OnChannelSubscription?.Invoke(this, data);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
case @"App\Events\SubscriptionEvent":
|
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);
|
OnSubscription?.Invoke(this, data);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
case @"App\Events\MessageDeletedEvent":
|
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);
|
OnMessageDeleted?.Invoke(this, data);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
case @"App\Events\UserBannedEvent":
|
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);
|
OnUserBanned?.Invoke(this, data);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
case @"App\Events\UserUnbannedEvent":
|
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);
|
OnUserUnbanned?.Invoke(this, data);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
case @"App\Events\LiveStream\UpdatedLiveStreamEvent":
|
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);
|
OnUpdatedLiveStream?.Invoke(this, data);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
case @"App\Events\StopStreamBroadcast":
|
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);
|
OnStopStreamBroadcast?.Invoke(this, data);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
case @"App\Events\StreamerIsLive":
|
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);
|
OnStreamerIsLive?.Invoke(this, data);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
case @"App\Events\PollUpdateEvent":
|
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);
|
OnPollUpdate?.Invoke(this, data);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,8 +7,8 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
|
||||||
<PackageReference Include="NLog" Version="5.2.8" />
|
<PackageReference Include="NLog" Version="5.2.8" />
|
||||||
|
<PackageReference Include="System.Text.Json" Version="8.0.3" />
|
||||||
<PackageReference Include="Websocket.Client" Version="5.1.1" />
|
<PackageReference Include="Websocket.Client" Version="5.1.1" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using Newtonsoft.Json;
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
namespace KickWsClient.Models;
|
namespace KickWsClient.Models;
|
||||||
|
|
||||||
@@ -9,17 +9,17 @@ public class KickModels
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Internal type for badge e.g. moderator
|
/// Internal type for badge e.g. moderator
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty("type")]
|
[JsonPropertyName("type")]
|
||||||
public required string Type { get; set; }
|
public required string Type { get; set; }
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Friendly name for badge e.g. Moderator
|
/// Friendly name for badge e.g. Moderator
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty("text")]
|
[JsonPropertyName("text")]
|
||||||
public required string Text { get; set; }
|
public required string Text { get; set; }
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Count (if applicable) for badge (e.g. sub count for gifted subs)
|
/// Count (if applicable) for badge (e.g. sub count for gifted subs)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty("count")]
|
[JsonPropertyName("count")]
|
||||||
public int? Count { get; set; }
|
public int? Count { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -28,13 +28,13 @@ public class KickModels
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// User's hex color
|
/// User's hex color
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty("color")]
|
[JsonPropertyName("color")]
|
||||||
public required string Color { get; set; }
|
public required string Color { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Badges a user has
|
/// Badges a user has
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty("badges")]
|
[JsonPropertyName("badges")]
|
||||||
public List<ChatMessageSenderIdentityBadgeModel> Badges = [];
|
public List<ChatMessageSenderIdentityBadgeModel> Badges = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -43,22 +43,22 @@ public class KickModels
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Kick internal user ID
|
/// Kick internal user ID
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty("id")]
|
[JsonPropertyName("id")]
|
||||||
public int Id { get; set; }
|
public int Id { get; set; }
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Kick display name
|
/// Kick display name
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty("username")]
|
[JsonPropertyName("username")]
|
||||||
public required string Username { get; set; }
|
public required string Username { get; set; }
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Kick slug (for URLs)
|
/// Kick slug (for URLs)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty("slug")]
|
[JsonPropertyName("slug")]
|
||||||
public required string Slug { get; set; }
|
public required string Slug { get; set; }
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Identity info for display color and badges
|
/// Identity info for display color and badges
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty("identity")]
|
[JsonPropertyName("identity")]
|
||||||
public required ChatMessageSenderIdentityModel Identity { get; set; }
|
public required ChatMessageSenderIdentityModel Identity { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -67,12 +67,12 @@ public class KickModels
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Original sender's user ID
|
/// Original sender's user ID
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty("id")]
|
[JsonPropertyName("id")]
|
||||||
public int Id { get; set; }
|
public int Id { get; set; }
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Original sender's username
|
/// Original sender's username
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty("username")]
|
[JsonPropertyName("username")]
|
||||||
public required string Username { get; set; }
|
public required string Username { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -81,12 +81,12 @@ public class KickModels
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// ID (GUID) of the original message
|
/// ID (GUID) of the original message
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty("id")]
|
[JsonPropertyName("id")]
|
||||||
public required string Id { get; set; }
|
public required string Id { get; set; }
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Content of the original message
|
/// Content of the original message
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty("content")]
|
[JsonPropertyName("content")]
|
||||||
public required string Content { get; set; }
|
public required string Content { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -95,12 +95,12 @@ public class KickModels
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sender of the message that this message is in reply to
|
/// Sender of the message that this message is in reply to
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty("original_sender")]
|
[JsonPropertyName("original_sender")]
|
||||||
public required ChatMessageMetadataOriginalSenderModel OriginalSender { get; set; }
|
public required ChatMessageMetadataOriginalSenderModel OriginalSender { get; set; }
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Content of the message that this message is in reply to
|
/// Content of the message that this message is in reply to
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty("original_message")]
|
[JsonPropertyName("original_message")]
|
||||||
public required ChatMessageMetadataOriginalMessageModel OriginalMessage { get; set; }
|
public required ChatMessageMetadataOriginalMessageModel OriginalMessage { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -109,29 +109,29 @@ public class KickModels
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Channel follower count
|
/// Channel follower count
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty("followersCount")]
|
[JsonPropertyName("followersCount")]
|
||||||
public int FollowersCount { get; set; }
|
public int FollowersCount { get; set; }
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// ID to identify what chatroom this event belongs to
|
/// ID to identify what chatroom this event belongs to
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty("chatroom_id")]
|
[JsonPropertyName("chatroom_id")]
|
||||||
public int ChatroomId { get; set; }
|
public int ChatroomId { get; set; }
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Maybe returns your username if you're auth'd? No idea. Just returned null for me
|
/// Maybe returns your username if you're auth'd? No idea. Just returned null for me
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty("username")]
|
[JsonPropertyName("username")]
|
||||||
public string? Username { get; set; }
|
public string? Username { get; set; }
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Epoch value that signifies ???
|
/// Epoch value that signifies ???
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty("created_at")]
|
[JsonPropertyName("created_at")]
|
||||||
public int? CreatedAtEpoch { get; set; }
|
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
|
// 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.
|
// suddenly appear and mark as nullable as it's not really a useful property anyway.
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Does it mean we're following? Who knows, returns true even if you're a guest
|
/// Does it mean we're following? Who knows, returns true even if you're a guest
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty("followed")]
|
[JsonPropertyName("followed")]
|
||||||
public bool? Followed { get; set; }
|
public bool? Followed { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -140,38 +140,38 @@ public class KickModels
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Message unique GUID that's referenced for replies and deletions
|
/// Message unique GUID that's referenced for replies and deletions
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty("id")]
|
[JsonPropertyName("id")]
|
||||||
public required string Id { get; set; }
|
public required string Id { get; set; }
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Chatroom ID you can use to differentiate this from other rooms if you sub to multiple at a time
|
/// Chatroom ID you can use to differentiate this from other rooms if you sub to multiple at a time
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty("chatroom_id")]
|
[JsonPropertyName("chatroom_id")]
|
||||||
public int ChatroomId { get; set; }
|
public int ChatroomId { get; set; }
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Content of the message. Emotes are encoded like [emote:161238:russW] which translates to -> https://files.kick.com/emotes/161238/fullsize
|
/// Content of the message. Emotes are encoded like [emote:161238:russW] which translates to -> https://files.kick.com/emotes/161238/fullsize
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty("content")]
|
[JsonPropertyName("content")]
|
||||||
public required string Content { get; set; }
|
public required string Content { get; set; }
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Regular message is 'message', replies are 'reply'
|
/// Regular message is 'message', replies are 'reply'
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty("type")]
|
[JsonPropertyName("type")]
|
||||||
public required string Type { get; set; }
|
public required string Type { get; set; }
|
||||||
// Why created at is an epoch for followers updated but ISO8601 for chat messages is just a mystery
|
// Why created at is an epoch for followers updated but ISO8601 for chat messages is just a mystery
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Time message was sent
|
/// Time message was sent
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty("created_at")]
|
[JsonPropertyName("created_at")]
|
||||||
public DateTimeOffset CreatedAt { get; set; }
|
public DateTimeOffset CreatedAt { get; set; }
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sender of the message
|
/// Sender of the message
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty("sender")]
|
[JsonPropertyName("sender")]
|
||||||
public required ChatMessageSenderModel Sender { get; set; }
|
public required ChatMessageSenderModel Sender { get; set; }
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Message metadata which is set for replies only
|
/// Message metadata which is set for replies only
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty("metadata")]
|
[JsonPropertyName("metadata")]
|
||||||
public ChatMessageMetadataModel? Metadata { get; set; }
|
public ChatMessageMetadataModel? Metadata { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -180,17 +180,17 @@ public class KickModels
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// User IDs of subscription recipients
|
/// User IDs of subscription recipients
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty("user_ids")]
|
[JsonPropertyName("user_ids")]
|
||||||
public List<int> UserIds { get; set; } = [];
|
public List<int> UserIds { get; set; } = [];
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Username of the person who subbed / gifted
|
/// Username of the person who subbed / gifted
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty("username")]
|
[JsonPropertyName("username")]
|
||||||
public required string Username { get; set; }
|
public required string Username { get; set; }
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Channel ID where the sub event occurred
|
/// Channel ID where the sub event occurred
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty("channel_id")]
|
[JsonPropertyName("channel_id")]
|
||||||
public int ChannelId { get; set; }
|
public int ChannelId { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -199,17 +199,17 @@ public class KickModels
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// ID of channel where the subscription event occurred
|
/// ID of channel where the subscription event occurred
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty("chatroom_id")]
|
[JsonPropertyName("chatroom_id")]
|
||||||
public int ChatroomId { get; set; }
|
public int ChatroomId { get; set; }
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Username of the person who bought a sub
|
/// Username of the person who bought a sub
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty("username")]
|
[JsonPropertyName("username")]
|
||||||
public required string Username { get; set; }
|
public required string Username { get; set; }
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Number of months they've subbed now (e.g. 2 if they bought their 2nd month)
|
/// Number of months they've subbed now (e.g. 2 if they bought their 2nd month)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty("months")]
|
[JsonPropertyName("months")]
|
||||||
public int Months { get; set; }
|
public int Months { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -218,7 +218,7 @@ public class KickModels
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// ID of the message that was deleted
|
/// ID of the message that was deleted
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty("id")]
|
[JsonPropertyName("id")]
|
||||||
public required string Id { get; set; }
|
public required string Id { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -227,12 +227,12 @@ public class KickModels
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// ID of this event (NOT the message to be removed!)
|
/// ID of this event (NOT the message to be removed!)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty("id")]
|
[JsonPropertyName("id")]
|
||||||
public required string Id { get; set; }
|
public required string Id { get; set; }
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Message that was deleted
|
/// Message that was deleted
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty("message")]
|
[JsonPropertyName("message")]
|
||||||
public required MessageDeletedMessageModel Message { get; set; }
|
public required MessageDeletedMessageModel Message { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -241,17 +241,17 @@ public class KickModels
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// ID of the user. Note it'll be 0 for the janny
|
/// ID of the user. Note it'll be 0 for the janny
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty("id")]
|
[JsonPropertyName("id")]
|
||||||
public int Id { get; set; }
|
public int Id { get; set; }
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// User's username
|
/// User's username
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty("username")]
|
[JsonPropertyName("username")]
|
||||||
public required string Username { get; set; }
|
public required string Username { get; set; }
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Slug suitable for URLs
|
/// Slug suitable for URLs
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty("slug")]
|
[JsonPropertyName("slug")]
|
||||||
public required string Slug { get; set; }
|
public required string Slug { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -260,22 +260,22 @@ public class KickModels
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// GUID of the event
|
/// GUID of the event
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty("id")]
|
[JsonPropertyName("id")]
|
||||||
public required string Id { get; set; }
|
public required string Id { get; set; }
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// User who was banished
|
/// User who was banished
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty("user")]
|
[JsonPropertyName("user")]
|
||||||
public required UserBannedUserModel User { get; set; }
|
public required UserBannedUserModel User { get; set; }
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Janny who did the sweeping
|
/// Janny who did the sweeping
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty("banned_by")]
|
[JsonPropertyName("banned_by")]
|
||||||
public required UserBannedUserModel BannedBy { get; set; }
|
public required UserBannedUserModel BannedBy { get; set; }
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Datetime that the ban expires. Null for permabans
|
/// Datetime that the ban expires. Null for permabans
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty("expires_at")]
|
[JsonPropertyName("expires_at")]
|
||||||
public DateTimeOffset? ExpiresAt { get; set; }
|
public DateTimeOffset? ExpiresAt { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -284,17 +284,17 @@ public class KickModels
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// GUID of the event
|
/// GUID of the event
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty("id")]
|
[JsonPropertyName("id")]
|
||||||
public required string Id { get; set; }
|
public required string Id { get; set; }
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// User who was unbanned
|
/// User who was unbanned
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty("user")]
|
[JsonPropertyName("user")]
|
||||||
public required UserBannedUserModel User { get; set; }
|
public required UserBannedUserModel User { get; set; }
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Janny who unbanned
|
/// Janny who unbanned
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty("unbanned_by")]
|
[JsonPropertyName("unbanned_by")]
|
||||||
public required UserBannedUserModel UnbannedBy { get; set; }
|
public required UserBannedUserModel UnbannedBy { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -303,12 +303,12 @@ public class KickModels
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// ID representing the category
|
/// ID representing the category
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty("id")]
|
[JsonPropertyName("id")]
|
||||||
public int Id { get; set; }
|
public int Id { get; set; }
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Slug representing the category
|
/// Slug representing the category
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty("slug")]
|
[JsonPropertyName("slug")]
|
||||||
public required string Slug { get; set; }
|
public required string Slug { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -317,27 +317,27 @@ public class KickModels
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// ID of the category
|
/// ID of the category
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty("id")]
|
[JsonPropertyName("id")]
|
||||||
public int Id { get; set; }
|
public int Id { get; set; }
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Friendly name of the category
|
/// Friendly name of the category
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty("name")]
|
[JsonPropertyName("name")]
|
||||||
public required string Name { get; set; }
|
public required string Name { get; set; }
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Category's slug for forming URls etc.
|
/// Category's slug for forming URls etc.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty("slug")]
|
[JsonPropertyName("slug")]
|
||||||
public required string Slug { get; set; }
|
public required string Slug { get; set; }
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Tags for the category
|
/// Tags for the category
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty("tags")]
|
[JsonPropertyName("tags")]
|
||||||
public List<string> Tags { get; set; } = [];
|
public List<string> Tags { get; set; } = [];
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Parent category, if one is present. I think there usually is one, but made it nullable just in case
|
/// Parent category, if one is present. I think there usually is one, but made it nullable just in case
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty("parent_category")]
|
[JsonPropertyName("parent_category")]
|
||||||
public UpdatedLiveStreamCategoryParentModel? ParentCategory { get; set; }
|
public UpdatedLiveStreamCategoryParentModel? ParentCategory { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -346,42 +346,42 @@ public class KickModels
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// ID of the livestream (numeric)
|
/// ID of the livestream (numeric)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty("id")]
|
[JsonPropertyName("id")]
|
||||||
public int Id { get; set; }
|
public int Id { get; set; }
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Livestream slug
|
/// Livestream slug
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty("slug")]
|
[JsonPropertyName("slug")]
|
||||||
public required string Slug { get; set; }
|
public required string Slug { get; set; }
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Livestream title
|
/// Livestream title
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty("session_title")]
|
[JsonPropertyName("session_title")]
|
||||||
public required string SessionTitle { get; set; }
|
public required string SessionTitle { get; set; }
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Livestream start time
|
/// Livestream start time
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty("created_at")]
|
[JsonPropertyName("created_at")]
|
||||||
public DateTimeOffset CreatedAt { get; set; }
|
public DateTimeOffset CreatedAt { get; set; }
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Language of the livestream (e.g. English)
|
/// Language of the livestream (e.g. English)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty("language")]
|
[JsonPropertyName("language")]
|
||||||
public string? Language { get; set; }
|
public string? Language { get; set; }
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Whether the stream is marked as for a mature audience
|
/// Whether the stream is marked as for a mature audience
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty("is_mature")]
|
[JsonPropertyName("is_mature")]
|
||||||
public bool IsMature { get; set; }
|
public bool IsMature { get; set; }
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Number of viewers presently watching
|
/// Number of viewers presently watching
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty("viewers")]
|
[JsonPropertyName("viewers")]
|
||||||
public int Viewers { get; set; }
|
public int Viewers { get; set; }
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Category of the livestream. I believe this is always required but marked it as nullable just in case
|
/// Category of the livestream. I believe this is always required but marked it as nullable just in case
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty("category")]
|
[JsonPropertyName("category")]
|
||||||
public UpdatedLiveStreamCategoryModel? Category { get; set; }
|
public UpdatedLiveStreamCategoryModel? Category { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -390,12 +390,12 @@ public class KickModels
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// ID of the channel
|
/// ID of the channel
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty("id")]
|
[JsonPropertyName("id")]
|
||||||
public int Id { get; set; }
|
public int Id { get; set; }
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Whether the streamer was sent to ban world
|
/// Whether the streamer was sent to ban world
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty("is_banned")]
|
[JsonPropertyName("is_banned")]
|
||||||
public bool IsBanned { get; set; }
|
public bool IsBanned { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -404,12 +404,12 @@ public class KickModels
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Livestream event ID
|
/// Livestream event ID
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty("id")]
|
[JsonPropertyName("id")]
|
||||||
public int Id { get; set; }
|
public int Id { get; set; }
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Channel that stopped streaming
|
/// Channel that stopped streaming
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty("channel")]
|
[JsonPropertyName("channel")]
|
||||||
public required StopStreamBroadcastLiveStreamChannelModel Channel { get; set; }
|
public required StopStreamBroadcastLiveStreamChannelModel Channel { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -418,7 +418,7 @@ public class KickModels
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Object containing information related to the livestream that stopped
|
/// Object containing information related to the livestream that stopped
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty("livestream")]
|
[JsonPropertyName("livestream")]
|
||||||
public required StopStreamBroadcastLiveStreamModel Livestream { get; set; }
|
public required StopStreamBroadcastLiveStreamModel Livestream { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -427,27 +427,27 @@ public class KickModels
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// ID of the livestream
|
/// ID of the livestream
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty("id")]
|
[JsonPropertyName("id")]
|
||||||
public int Id { get; set; }
|
public int Id { get; set; }
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// ID of the channel
|
/// ID of the channel
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty("channel_id")]
|
[JsonPropertyName("channel_id")]
|
||||||
public int ChannelId { get; set; }
|
public int ChannelId { get; set; }
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Title of the stream
|
/// Title of the stream
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty("session_title")]
|
[JsonPropertyName("session_title")]
|
||||||
public required string SessionTitle { get; set; }
|
public required string SessionTitle { get; set; }
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// No idea, just null on my end
|
/// No idea, just null on my end
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty("source")]
|
[JsonPropertyName("source")]
|
||||||
public string? Source { get; set; }
|
public string? Source { get; set; }
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Time stream started
|
/// Time stream started
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty("created_at")]
|
[JsonPropertyName("created_at")]
|
||||||
public DateTimeOffset CreatedAt { get; set; }
|
public DateTimeOffset CreatedAt { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -456,7 +456,7 @@ public class KickModels
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Object containing information related to the livestream that has just started
|
/// Object containing information related to the livestream that has just started
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty("livestream")]
|
[JsonPropertyName("livestream")]
|
||||||
public required StreamerIsLiveLiveStreamModel Livestream { get; set; }
|
public required StreamerIsLiveLiveStreamModel Livestream { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -465,17 +465,17 @@ public class KickModels
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// ID of the poll option
|
/// ID of the poll option
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty("id")]
|
[JsonPropertyName("id")]
|
||||||
public int Id { get; set; }
|
public int Id { get; set; }
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Label of the poll option
|
/// Label of the poll option
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty("label")]
|
[JsonPropertyName("label")]
|
||||||
public required string Label { get; set; }
|
public required string Label { get; set; }
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Number of votes the poll option has gotten
|
/// Number of votes the poll option has gotten
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty("votes")]
|
[JsonPropertyName("votes")]
|
||||||
public int Votes { get; set; }
|
public int Votes { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -484,27 +484,27 @@ public class KickModels
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Title of the poll
|
/// Title of the poll
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty("title")]
|
[JsonPropertyName("title")]
|
||||||
public required string Title { get; set; }
|
public required string Title { get; set; }
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Poll options
|
/// Poll options
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty("options")]
|
[JsonPropertyName("options")]
|
||||||
public List<PollUpdatePollOptionModel> Options { get; set; } = [];
|
public List<PollUpdatePollOptionModel> Options { get; set; } = [];
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Duration of the poll in seconds
|
/// Duration of the poll in seconds
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty("duration")]
|
[JsonPropertyName("duration")]
|
||||||
public int Duration { get; set; }
|
public int Duration { get; set; }
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Remaining time in seconds
|
/// Remaining time in seconds
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty("remaining")]
|
[JsonPropertyName("remaining")]
|
||||||
public int Remaining { get; set; }
|
public int Remaining { get; set; }
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Time in seconds to display the results after completion?
|
/// Time in seconds to display the results after completion?
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty("result_display_duration")]
|
[JsonPropertyName("result_display_duration")]
|
||||||
public int ResultDisplayDuration { get; set; }
|
public int ResultDisplayDuration { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -513,7 +513,7 @@ public class KickModels
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Poll data
|
/// Poll data
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty("poll")]
|
[JsonPropertyName("poll")]
|
||||||
public required PollUpdatePollModel Poll { get; set; }
|
public required PollUpdatePollModel Poll { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
using Newtonsoft.Json;
|
using System.Text.Json.Serialization;
|
||||||
|
using KickWsClient.Converters;
|
||||||
|
|
||||||
namespace KickWsClient.Models;
|
namespace KickWsClient.Models;
|
||||||
|
|
||||||
@@ -9,17 +10,18 @@ public class PusherModels
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Name of the event
|
/// Name of the event
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty("event")]
|
[JsonPropertyName("event")]
|
||||||
public required string Event { get; set; }
|
public required string Event { get; set; }
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Stringified JSON payload
|
/// Stringified JSON payload
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty("data")]
|
[JsonPropertyName("data")]
|
||||||
|
[JsonConverter(typeof(StringOrObjectConverter<string>))]
|
||||||
public required string Data { get; set; }
|
public required string Data { get; set; }
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Channel where event originates. Only included events where a channel is applicable
|
/// Channel where event originates. Only included events where a channel is applicable
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty("channel")]
|
[JsonPropertyName("channel")]
|
||||||
public string? Channel { get; set; }
|
public string? Channel { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -28,12 +30,12 @@ public class PusherModels
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Name of the event
|
/// Name of the event
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty("event")]
|
[JsonPropertyName("event")]
|
||||||
public required string Event { get; set; }
|
public required string Event { get; set; }
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Data as object. It's only stringified for responses
|
/// Data as object. It's only stringified for responses
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty("data")]
|
[JsonPropertyName("data")]
|
||||||
public required object Data { get; set; }
|
public required object Data { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -42,12 +44,12 @@ public class PusherModels
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Internal socket ID
|
/// Internal socket ID
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty("socket_id")]
|
[JsonPropertyName("socket_id")]
|
||||||
public required string SocketId { get; set; }
|
public required string SocketId { get; set; }
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Timeout on no activity in seconds
|
/// Timeout on no activity in seconds
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty("activity_timeout")]
|
[JsonPropertyName("activity_timeout")]
|
||||||
public int ActivityTimeout { get; set; }
|
public int ActivityTimeout { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -56,12 +58,12 @@ public class PusherModels
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Token to authenticate with, use an empty string for guest.
|
/// Token to authenticate with, use an empty string for guest.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty("auth")]
|
[JsonPropertyName("auth")]
|
||||||
public string Auth { get; set; } = "";
|
public string Auth { get; set; } = "";
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Channel you wish to subscribe to. 'channel.2515504' for stream events. 'chatrooms.2515504.v2' for chat where 2515504 is the channel ID
|
/// Channel you wish to subscribe to. 'channel.2515504' for stream events. 'chatrooms.2515504.v2' for chat where 2515504 is the channel ID
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty("channel")]
|
[JsonPropertyName("channel")]
|
||||||
public required string Channel { get; set; }
|
public required string Channel { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -70,7 +72,7 @@ public class PusherModels
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Channel you wish to unsubscribe from, e.g. 'channel.2515504'
|
/// Channel you wish to unsubscribe from, e.g. 'channel.2515504'
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty("channel")]
|
[JsonPropertyName("channel")]
|
||||||
public required string Channel { get; set; }
|
public required string Channel { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user