Added support for MOTD and whispers. Commands can opt into responding to whispers and there's a helper method to handle replying through the correct channel.

This commit is contained in:
barelyprofessional
2026-03-18 23:50:32 -05:00
parent 4cdb04e3c5
commit 01a4b26326
44 changed files with 683 additions and 148 deletions

View File

@@ -26,6 +26,8 @@ public class ChatClient
public event EventHandlers.OnUnknownCommand? OnUnknownCommand;
public event EventHandlers.OnPermissionsEventHandler? OnPermissions;
public event EventHandlers.OnSystemMessage? OnSystemMessage;
public event EventHandlers.OnMotdEventHandler? OnMotd;
public event EventHandlers.OnWhisperEventHandler? OnWhisper;
private WebsocketClient? _wsClient;
private readonly Logger _logger = LogManager.GetCurrentClassLogger();
private ChatClientConfigModel _config;
@@ -210,6 +212,20 @@ public class ChatClient
WsDeleteMessagesReceived(message);
return;
}
if (packetType.ContainsKey("whisper"))
{
_logger.Debug("Looks like this is a whisper packet");
WsWhisper(message);
return;
}
if (packetType.ContainsKey("motd"))
{
_logger.Debug("Looks like this is an MOTD packet");
WsMotd(message);
return;
}
_logger.Info($"Received packet this was not handled: {message.Text}");
}
@@ -277,6 +293,13 @@ public class ChatClient
await _wsClient.SendInstant($"/edit {payload}");
}
public async Task SetMotd(string messageUuid)
{
_logger.Debug($"Setting {messageUuid} as the MOTD");
if (_wsClient == null) throw new WebSocketNotInitializedException();
await _wsClient.SendInstant($"/motd {messageUuid}");
}
private void WsDeleteMessagesReceived(ResponseMessage message)
{
var data = JsonSerializer.Deserialize<DeleteMessagesJsonModel>(message.Text!);
@@ -392,6 +415,83 @@ public class ChatClient
_logger.Error(e);
}
}
private void WsWhisper(ResponseMessage message)
{
var data = JsonSerializer.Deserialize<JsonElement>(message.Text).GetProperty("whisper")
.Deserialize<WhisperJsonModel>();
var model = new WhisperModel
{
Author = new UserModel
{
Id = data.Author.Id,
Username = data.Author.Username,
AvatarUrl = data.Author.AvatarUrl
},
Recipient = new UserModel
{
Id = data.Recipient.Id,
Username = data.Recipient.Username,
AvatarUrl = data.Recipient.AvatarUrl
},
Message = data.Message,
MessageRaw = data.MessageRaw,
MessageRawHtmlDecoded = WebUtility.HtmlDecode(data.MessageRaw),
MessageDate = DateTimeOffset.FromUnixTimeSeconds(data.MessageDate)
};
try
{
OnWhisper?.Invoke(this, model);
}
catch (Exception e)
{
_logger.Error("WS handler for whisper threw an exception when invoking OnWhisper");
_logger.Error(e);
}
}
private void WsMotd(ResponseMessage message)
{
var msg = JsonSerializer.Deserialize<JsonElement>(message.Text).GetProperty("motd")
.Deserialize<MessagesJsonModel.MessageModel>();
var model = new MessageModel
{
Author = new UserModel
{
Id = msg.Author.Id,
Username = msg.Author.Username,
AvatarUrl = msg.Author.AvatarUrl,
// It isn't sent on chat messages
LastActivity = null
},
Message = msg.Message,
MessageUuid = msg.MessageUuid,
MessageRaw = msg.MessageRaw,
RoomId = msg.RoomId,
MessageRawHtmlDecoded = WebUtility.HtmlDecode(msg.MessageRaw),
MessageDate = DateTimeOffset.FromUnixTimeSeconds(msg.MessageDate)
};
if (msg.MessageEditDate == 0)
{
model.MessageEditDate = null;
}
else
{
model.MessageEditDate = DateTimeOffset.FromUnixTimeSeconds(msg.MessageEditDate);
}
try
{
OnMotd?.Invoke(this, model);
}
catch (Exception e)
{
_logger.Error("The handler for MOTD messages threw an exception");
_logger.Error(e);
}
}
}
public class WebSocketNotInitializedException : Exception;

View File

@@ -29,4 +29,8 @@ public class EventHandlers
public delegate void OnPermissionsEventHandler(object sender, PermissionsJsonModel permissions);
public delegate void OnSystemMessage(object sender, string message);
public delegate void OnMotdEventHandler(object sender, MessageModel message);
public delegate void OnWhisperEventHandler(object sender, WhisperModel whisper);
}

View File

@@ -0,0 +1,12 @@
namespace KfChatDotNetWsClient.Models.Events;
public class WhisperModel
{
public required UserModel Author { get; set; }
public required UserModel Recipient { get; set; }
public required string Message { get; set; }
public required string MessageRaw { get; set; }
public required DateTimeOffset MessageDate { get; set; }
public required string MessageRawHtmlDecoded { get; set; }
}

View File

@@ -0,0 +1,34 @@
using System.Text.Json.Serialization;
namespace KfChatDotNetWsClient.Models.Json;
// {
// "whisper": {
// "author": {
// "id": 58227,
// "username": "Flaming Dumpster",
// "avatar_url": "/data/avatars/m/58/58227.jpg?1771372231"
// },
// "recipient": {
// "id": 1,
// "username": "Null",
// "avatar_url": "/data/avatars/m/0/1.jpg?1767201853"
// },
// "message": "nigger",
// "message_raw": "nigger",
// "message_date": 1773881876
// }
// }
public class WhisperJsonModel
{
[JsonPropertyName("author")]
public required MessagesJsonModel.AuthorModel Author { get; set; }
[JsonPropertyName("recipient")]
public required MessagesJsonModel.AuthorModel Recipient { get; set; }
[JsonPropertyName("message")]
public required string Message { get; set; }
[JsonPropertyName("message_raw")]
public required string MessageRaw { get; set; }
[JsonPropertyName("message_date")]
public required int MessageDate { get; set; }
}