From 4e0def980fdc9517c0810b01fc30d676a825ba8d Mon Sep 17 00:00:00 2001 From: flashwave Date: Thu, 23 Feb 2023 22:46:49 +0100 Subject: [PATCH] Revised event storage to use less magic classes. --- SharpChat/ChatContext.cs | 19 +++-- SharpChat/Commands/ActionCommand.cs | 18 ++-- SharpChat/Commands/DeleteMessageCommand.cs | 9 +- SharpChat/Commands/WhisperCommand.cs | 35 ++++---- SharpChat/EventStorage/IEventStorage.cs | 15 ++-- SharpChat/EventStorage/MariaDBEventStorage.cs | 83 ++++++++++--------- SharpChat/EventStorage/StoredEventFlags.cs | 14 ++++ SharpChat/EventStorage/StoredEventInfo.cs | 35 ++++++++ SharpChat/EventStorage/VirtualEventStorage.cs | 44 ++++++---- SharpChat/Events/ChatMessage.cs | 28 ------- SharpChat/Events/IChatEvent.cs | 24 ------ SharpChat/Events/UserChannelJoinEvent.cs | 28 ------- SharpChat/Events/UserChannelLeaveEvent.cs | 28 ------- SharpChat/Events/UserConnectEvent.cs | 28 ------- SharpChat/Events/UserDisconnectEvent.cs | 34 -------- SharpChat/Packet/ChatMessageAddPacket.cs | 60 ++++++++++---- SharpChat/Packet/ContextMessagePacket.cs | 41 +++++---- .../PacketHandlers/SendMessageHandler.cs | 32 ++++--- 18 files changed, 250 insertions(+), 325 deletions(-) create mode 100644 SharpChat/EventStorage/StoredEventFlags.cs create mode 100644 SharpChat/EventStorage/StoredEventInfo.cs delete mode 100644 SharpChat/Events/ChatMessage.cs delete mode 100644 SharpChat/Events/IChatEvent.cs delete mode 100644 SharpChat/Events/UserChannelJoinEvent.cs delete mode 100644 SharpChat/Events/UserChannelLeaveEvent.cs delete mode 100644 SharpChat/Events/UserConnectEvent.cs delete mode 100644 SharpChat/Events/UserDisconnectEvent.cs diff --git a/SharpChat/ChatContext.cs b/SharpChat/ChatContext.cs index 4e66896..113009c 100644 --- a/SharpChat/ChatContext.cs +++ b/SharpChat/ChatContext.cs @@ -1,13 +1,14 @@ -using SharpChat.Events; -using SharpChat.EventStorage; +using SharpChat.EventStorage; using SharpChat.Packet; using System; using System.Collections.Generic; +using System.Diagnostics.Tracing; using System.Linq; using System.Net; using System.Threading; -namespace SharpChat { +namespace SharpChat +{ public class ChatContext { public record ChannelUserAssoc(long UserId, string ChannelName); @@ -148,13 +149,13 @@ namespace SharpChat { public void HandleJoin(ChatUser user, ChatChannel chan, ChatConnection conn, int maxMsgLength) { if(!IsInChannel(user, chan)) { SendTo(chan, new UserConnectPacket(DateTimeOffset.Now, user)); - Events.AddEvent(new UserConnectEvent(DateTimeOffset.Now, user, chan)); + Events.AddEvent("user:connect", user, chan, flags: StoredEventFlags.Log); } conn.Send(new AuthSuccessPacket(user, chan, conn, maxMsgLength)); conn.Send(new ContextUsersPacket(GetChannelUsers(chan).Except(new[] { user }).OrderByDescending(u => u.Rank))); - foreach(IChatEvent msg in Events.GetChannelEventLog(chan.Name)) + foreach(StoredEventInfo msg in Events.GetChannelEventLog(chan.Name)) conn.Send(new ContextMessagePacket(msg)); conn.Send(new ContextChannelsPacket(Channels.Where(c => c.Rank <= user.Rank))); @@ -176,7 +177,7 @@ namespace SharpChat { ChannelUsers.Remove(new ChannelUserAssoc(user.UserId, chan.Name)); SendTo(chan, new UserDisconnectPacket(DateTimeOffset.Now, user, reason)); - Events.AddEvent(new UserDisconnectEvent(DateTimeOffset.Now, user, chan, reason)); + Events.AddEvent("user:disconnect", user, chan, new { reason = (int)reason }, StoredEventFlags.Log); if(chan.IsTemporary && chan.IsOwner(user)) RemoveChannel(chan); @@ -213,14 +214,14 @@ namespace SharpChat { ChatChannel oldChan = UserLastChannel[user.UserId]; SendTo(oldChan, new UserChannelLeavePacket(user)); - Events.AddEvent(new UserChannelLeaveEvent(DateTimeOffset.Now, user, oldChan)); + Events.AddEvent("chan:leave", user, oldChan, flags: StoredEventFlags.Log); SendTo(chan, new UserChannelJoinPacket(user)); - Events.AddEvent(new UserChannelJoinEvent(DateTimeOffset.Now, user, chan)); + Events.AddEvent("chan:join", user, oldChan, flags: StoredEventFlags.Log); SendTo(user, new ContextClearPacket(chan, ContextClearMode.MessagesUsers)); SendTo(user, new ContextUsersPacket(GetChannelUsers(chan).Except(new[] { user }).OrderByDescending(u => u.Rank))); - foreach(IChatEvent msg in Events.GetChannelEventLog(chan.Name)) + foreach(StoredEventInfo msg in Events.GetChannelEventLog(chan.Name)) SendTo(user, new ContextMessagePacket(msg)); ForceChannel(user, chan); diff --git a/SharpChat/Commands/ActionCommand.cs b/SharpChat/Commands/ActionCommand.cs index 60c4d57..ceeb55e 100644 --- a/SharpChat/Commands/ActionCommand.cs +++ b/SharpChat/Commands/ActionCommand.cs @@ -1,8 +1,9 @@ -using SharpChat.Events; +using SharpChat.EventStorage; using System; using System.Linq; -namespace SharpChat.Commands { +namespace SharpChat.Commands +{ public class ActionCommand : IChatCommand { public bool IsMatch(ChatCommandContext ctx) { return ctx.NameEquals("action") @@ -13,17 +14,8 @@ namespace SharpChat.Commands { throw new NotImplementedException(); } - public ChatMessage ActionDispatch(ChatCommandContext ctx) { - if(!ctx.Args.Any()) - return null; - - return new ChatMessage { - ChannelName = ctx.Channel.Name, - DateTime = DateTimeOffset.UtcNow, - Sender = ctx.User, - Text = string.Join(' ', ctx.Args), - Flags = ChatMessageFlags.Action, - }; + public (string, bool)? ActionDispatch(ChatCommandContext ctx) { + return ctx.Args.Any() ? (string.Join(' ', ctx.Args), true) : null; } } } diff --git a/SharpChat/Commands/DeleteMessageCommand.cs b/SharpChat/Commands/DeleteMessageCommand.cs index 0213bb2..711b758 100644 --- a/SharpChat/Commands/DeleteMessageCommand.cs +++ b/SharpChat/Commands/DeleteMessageCommand.cs @@ -1,8 +1,9 @@ -using SharpChat.Events; +using SharpChat.EventStorage; using SharpChat.Packet; using System.Linq; -namespace SharpChat.Commands { +namespace SharpChat.Commands +{ public class DeleteMessageCommand : IChatCommand { public bool IsMatch(ChatCommandContext ctx) { return ctx.NameEquals("delmsg") || ( @@ -26,7 +27,7 @@ namespace SharpChat.Commands { return; } - IChatEvent delMsg = ctx.Chat.Events.GetEvent(delSeqId); + StoredEventInfo delMsg = ctx.Chat.Events.GetEvent(delSeqId); if(delMsg == null || delMsg.Sender.Rank > ctx.User.Rank || (!deleteAnyMessage && delMsg.Sender.UserId != ctx.User.UserId)) { ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.MESSAGE_DELETE_ERROR)); @@ -34,7 +35,7 @@ namespace SharpChat.Commands { } ctx.Chat.Events.RemoveEvent(delMsg); - ctx.Chat.Send(new ChatMessageDeletePacket(delMsg.SequenceId)); + ctx.Chat.Send(new ChatMessageDeletePacket(delMsg.Id)); } } } diff --git a/SharpChat/Commands/WhisperCommand.cs b/SharpChat/Commands/WhisperCommand.cs index a6a4d30..78cc743 100644 --- a/SharpChat/Commands/WhisperCommand.cs +++ b/SharpChat/Commands/WhisperCommand.cs @@ -1,9 +1,10 @@ -using SharpChat.Events; +using SharpChat.EventStorage; using SharpChat.Packet; using System; using System.Linq; -namespace SharpChat.Commands { +namespace SharpChat.Commands +{ public class WhisperCommand : IChatCommand { public bool IsMatch(ChatCommandContext ctx) { return ctx.NameEquals("whisper") @@ -31,20 +32,22 @@ namespace SharpChat.Commands { string whisperChan = ChatUser.GetDMChannelName(ctx.User, whisperUser); DateTimeOffset dateTime = DateTimeOffset.Now; - ctx.Chat.SendTo(whisperUser, new ChatMessageAddPacket(new ChatMessage { - DateTime = dateTime, - ChannelName = whisperChan, - Sender = ctx.User, - Text = whisperStr, - Flags = ChatMessageFlags.Private, - })); - ctx.Chat.SendTo(ctx.User, new ChatMessageAddPacket(new ChatMessage { - DateTime = dateTime, - ChannelName = whisperChan, - Sender = ctx.User, - Text = $"{whisperUser.LegacyName} {whisperStr}", - Flags = ChatMessageFlags.Private, - })); + ctx.Chat.SendTo(whisperUser, new ChatMessageAddPacket( + SharpId.Next(), + dateTime, + ctx.User.UserId, + whisperStr, + false, + true + )); + ctx.Chat.SendTo(ctx.User, new ChatMessageAddPacket( + SharpId.Next(), + dateTime, + ctx.User.UserId, + $"{whisperUser.LegacyName} {whisperStr}", + false, + true + )); } } } diff --git a/SharpChat/EventStorage/IEventStorage.cs b/SharpChat/EventStorage/IEventStorage.cs index 7014692..0e84190 100644 --- a/SharpChat/EventStorage/IEventStorage.cs +++ b/SharpChat/EventStorage/IEventStorage.cs @@ -1,12 +1,11 @@ -using SharpChat.Events; -using System; -using System.Collections.Generic; +using System.Collections.Generic; -namespace SharpChat.EventStorage { +namespace SharpChat.EventStorage +{ public interface IEventStorage { - void AddEvent(IChatEvent evt); - void RemoveEvent(IChatEvent evt); - IChatEvent GetEvent(long seqId); - IEnumerable GetChannelEventLog(string channelName, int amount = 20, int offset = 0); + long AddEvent(string type, ChatUser user, ChatChannel channel, object data = null, StoredEventFlags flags = StoredEventFlags.None); + void RemoveEvent(StoredEventInfo evt); + StoredEventInfo GetEvent(long seqId); + IEnumerable GetChannelEventLog(string channelName, int amount = 20, int offset = 0); } } diff --git a/SharpChat/EventStorage/MariaDBEventStorage.cs b/SharpChat/EventStorage/MariaDBEventStorage.cs index 35b75bc..f5aaec7 100644 --- a/SharpChat/EventStorage/MariaDBEventStorage.cs +++ b/SharpChat/EventStorage/MariaDBEventStorage.cs @@ -1,11 +1,12 @@ using MySqlConnector; -using SharpChat.Events; using System; using System.Collections.Generic; +using System.Dynamic; using System.Text; using System.Text.Json; -namespace SharpChat.EventStorage { +namespace SharpChat.EventStorage +{ public partial class MariaDBEventStorage : IEventStorage { private string ConnectionString { get; } @@ -13,46 +14,47 @@ namespace SharpChat.EventStorage { ConnectionString = connString ?? throw new ArgumentNullException(nameof(connString)); } - public void AddEvent(IChatEvent evt) { - if(evt == null) - throw new ArgumentNullException(nameof(evt)); + public long AddEvent(string type, ChatUser user, ChatChannel channel, object data = null, StoredEventFlags flags = StoredEventFlags.None) { + if(type == null) + throw new ArgumentNullException(nameof(type)); - if(evt.SequenceId < 1) - evt.SequenceId = SharpId.Next(); + long id = SharpId.Next(); RunCommand( "INSERT INTO `sqc_events` (`event_id`, `event_created`, `event_type`, `event_target`, `event_flags`, `event_data`" + ", `event_sender`, `event_sender_name`, `event_sender_colour`, `event_sender_rank`, `event_sender_nick`, `event_sender_perms`)" - + " VALUES (@id, FROM_UNIXTIME(@created), @type, @target, @flags, @data" + + " VALUES (@id, NOW(), @type, @target, @flags, @data" + ", @sender, @sender_name, @sender_colour, @sender_rank, @sender_nick, @sender_perms)", - new MySqlParameter("id", evt.SequenceId), - new MySqlParameter("created", evt.DateTime.ToUnixTimeSeconds()), - new MySqlParameter("type", evt.GetType().FullName), - new MySqlParameter("target", evt.ChannelName), - new MySqlParameter("flags", (byte)evt.Flags), - new MySqlParameter("data", JsonSerializer.SerializeToUtf8Bytes(evt, evt.GetType())), - new MySqlParameter("sender", evt.Sender?.UserId < 1 ? null : (long?)evt.Sender.UserId), - new MySqlParameter("sender_name", evt.Sender?.UserName), - new MySqlParameter("sender_colour", evt.Sender?.Colour.ToMisuzu()), - new MySqlParameter("sender_rank", evt.Sender?.Rank), - new MySqlParameter("sender_nick", evt.Sender?.NickName), - new MySqlParameter("sender_perms", evt.Sender?.Permissions) + new MySqlParameter("id", id), + new MySqlParameter("type", type), + new MySqlParameter("target", channel?.Name ?? null), + new MySqlParameter("flags", (byte)flags), + new MySqlParameter("data", data == null ? "{}" : JsonSerializer.SerializeToUtf8Bytes(data)), + new MySqlParameter("sender", user?.UserId < 1 ? null : (long?)user.UserId), + new MySqlParameter("sender_name", user?.UserName), + new MySqlParameter("sender_colour", user?.Colour.ToMisuzu()), + new MySqlParameter("sender_rank", user?.Rank), + new MySqlParameter("sender_nick", string.IsNullOrWhiteSpace(user?.NickName) ? null : user.NickName), + new MySqlParameter("sender_perms", user?.Permissions) ); + + return id; } - public IChatEvent GetEvent(long seqId) { + public StoredEventInfo GetEvent(long seqId) { try { using MySqlDataReader reader = RunQuery( "SELECT `event_id`, `event_type`, `event_flags`, `event_data`, `event_target`" + ", `event_sender`, `event_sender_name`, `event_sender_colour`, `event_sender_rank`, `event_sender_nick`, `event_sender_perms`" + ", UNIX_TIMESTAMP(`event_created`) AS `event_created`" + + ", UNIX_TIMESTAMP(`event_deleted`) AS `event_deleted`" + " FROM `sqc_events`" + " WHERE `event_id` = @id", new MySqlParameter("id", seqId) ); while(reader.Read()) { - IChatEvent evt = ReadEvent(reader); + StoredEventInfo evt = ReadEvent(reader); if(evt != null) return evt; } @@ -63,36 +65,35 @@ namespace SharpChat.EventStorage { return null; } - private static IChatEvent ReadEvent(MySqlDataReader reader) { - Type evtType = Type.GetType(Encoding.ASCII.GetString((byte[])reader["event_type"])); - IChatEvent evt = JsonSerializer.Deserialize(Encoding.ASCII.GetString((byte[])reader["event_data"]), evtType) as IChatEvent; - evt.SequenceId = reader.GetInt64("event_id"); - evt.ChannelName = Encoding.ASCII.GetString((byte[])reader["event_target"]); - evt.Flags = (ChatMessageFlags)reader.GetByte("event_flags"); - evt.DateTime = DateTimeOffset.FromUnixTimeSeconds(reader.GetInt32("event_created")); - - if(!reader.IsDBNull(reader.GetOrdinal("event_sender"))) { - evt.Sender = new ChatUser( + private static StoredEventInfo ReadEvent(MySqlDataReader reader) { + return new StoredEventInfo( + reader.GetInt64("event_id"), + Encoding.ASCII.GetString((byte[])reader["event_type"]), + reader.IsDBNull(reader.GetOrdinal("event_sender")) ? null : new ChatUser( reader.GetInt64("event_sender"), reader.GetString("event_sender_name"), ChatColour.FromMisuzu(reader.GetInt32("event_sender_colour")), reader.GetInt32("event_sender_rank"), (ChatUserPermissions)reader.GetInt32("event_sender_perms"), reader.IsDBNull(reader.GetOrdinal("event_sender_nick")) ? null : reader.GetString("event_sender_nick") - ); - } - - return evt; + ), + DateTimeOffset.FromUnixTimeSeconds(reader.GetInt32("event_created")), + reader.IsDBNull(reader.GetOrdinal("event_deleted")) ? null : DateTimeOffset.FromUnixTimeSeconds(reader.GetInt32("event_deleted")), + reader.IsDBNull(reader.GetOrdinal("event_target")) ? null : Encoding.ASCII.GetString((byte[])reader["event_target"]), + JsonDocument.Parse(Encoding.ASCII.GetString((byte[])reader["event_data"])), + (StoredEventFlags)reader.GetByte("event_flags") + ); } - public IEnumerable GetChannelEventLog(string channelName, int amount = 20, int offset = 0) { - List events = new(); + public IEnumerable GetChannelEventLog(string channelName, int amount = 20, int offset = 0) { + List events = new(); try { using MySqlDataReader reader = RunQuery( "SELECT `event_id`, `event_type`, `event_flags`, `event_data`, `event_target`" + ", `event_sender`, `event_sender_name`, `event_sender_colour`, `event_sender_rank`, `event_sender_nick`, `event_sender_perms`" + ", UNIX_TIMESTAMP(`event_created`) AS `event_created`" + + ", UNIX_TIMESTAMP(`event_deleted`) AS `event_deleted`" + " FROM `sqc_events`" + " WHERE `event_deleted` IS NULL AND `event_target` = @target" + " AND `event_id` > @offset" @@ -104,7 +105,7 @@ namespace SharpChat.EventStorage { ); while(reader.Read()) { - IChatEvent evt = ReadEvent(reader); + StoredEventInfo evt = ReadEvent(reader); if(evt != null) events.Add(evt); } @@ -117,12 +118,12 @@ namespace SharpChat.EventStorage { return events; } - public void RemoveEvent(IChatEvent evt) { + public void RemoveEvent(StoredEventInfo evt) { if(evt == null) throw new ArgumentNullException(nameof(evt)); RunCommand( "UPDATE IGNORE `sqc_events` SET `event_deleted` = NOW() WHERE `event_id` = @id AND `event_deleted` IS NULL", - new MySqlParameter("id", evt.SequenceId) + new MySqlParameter("id", evt.Id) ); } } diff --git a/SharpChat/EventStorage/StoredEventFlags.cs b/SharpChat/EventStorage/StoredEventFlags.cs new file mode 100644 index 0000000..57a3331 --- /dev/null +++ b/SharpChat/EventStorage/StoredEventFlags.cs @@ -0,0 +1,14 @@ +using System; + +namespace SharpChat.EventStorage +{ + [Flags] + public enum StoredEventFlags + { + None = 0, + Action = 1, + Broadcast = 1 << 1, + Log = 1 << 2, + Private = 1 << 3, + } +} diff --git a/SharpChat/EventStorage/StoredEventInfo.cs b/SharpChat/EventStorage/StoredEventInfo.cs new file mode 100644 index 0000000..f8a1756 --- /dev/null +++ b/SharpChat/EventStorage/StoredEventInfo.cs @@ -0,0 +1,35 @@ +using System; +using System.Text.Json; + +namespace SharpChat.EventStorage { + public class StoredEventInfo { + public long Id { get; set; } + public string Type { get; set; } + public ChatUser Sender { get; set; } + public DateTimeOffset Created { get; set; } + public DateTimeOffset? Deleted { get; set; } + public string ChannelName { get; set; } + public StoredEventFlags Flags { get; set; } + public JsonDocument Data { get; set; } + + public StoredEventInfo( + long id, + string type, + ChatUser sender, + DateTimeOffset created, + DateTimeOffset? deleted, + string channelName, + JsonDocument data, + StoredEventFlags flags + ) { + Id = id; + Type = type; + Sender = sender; + Created = created; + Deleted = deleted; + ChannelName = channelName; + Data = data; + Flags = flags; + } + } +} diff --git a/SharpChat/EventStorage/VirtualEventStorage.cs b/SharpChat/EventStorage/VirtualEventStorage.cs index 9223eb8..81a91fa 100644 --- a/SharpChat/EventStorage/VirtualEventStorage.cs +++ b/SharpChat/EventStorage/VirtualEventStorage.cs @@ -1,30 +1,38 @@ -using SharpChat.Events; -using System; +using System; using System.Collections.Generic; using System.Linq; +using System.Text.Json; -namespace SharpChat.EventStorage { +namespace SharpChat.EventStorage +{ public class VirtualEventStorage : IEventStorage { - private readonly Dictionary Events = new(); + private readonly Dictionary Events = new(); - public void AddEvent(IChatEvent evt) { + public long AddEvent(string type, ChatUser user, ChatChannel channel, object data = null, StoredEventFlags flags = StoredEventFlags.None) { + if(type == null) + throw new ArgumentNullException(nameof(type)); + + // VES is meant as an emergency fallback but this is something else + JsonDocument hack = JsonDocument.Parse(data == null ? "{}" : JsonSerializer.Serialize(data)); + + long id = SharpId.Next(); + Events.Add(id, new(id, type, user, DateTimeOffset.Now, null, channel?.Name ?? null, hack, flags)); + + return id; + } + + public StoredEventInfo GetEvent(long seqId) { + return Events.TryGetValue(seqId, out StoredEventInfo evt) ? evt : null; + } + + public void RemoveEvent(StoredEventInfo evt) { if(evt == null) throw new ArgumentNullException(nameof(evt)); - Events.Add(evt.SequenceId, evt); + Events.Remove(evt.Id); } - public IChatEvent GetEvent(long seqId) { - return Events.TryGetValue(seqId, out IChatEvent evt) ? evt : null; - } - - public void RemoveEvent(IChatEvent evt) { - if(evt == null) - throw new ArgumentNullException(nameof(evt)); - Events.Remove(evt.SequenceId); - } - - public IEnumerable GetChannelEventLog(string channelName, int amount = 20, int offset = 0) { - IEnumerable subset = Events.Values.Where(ev => ev.ChannelName == channelName); + public IEnumerable GetChannelEventLog(string channelName, int amount = 20, int offset = 0) { + IEnumerable subset = Events.Values.Where(ev => ev.ChannelName == channelName); int start = subset.Count() - offset - amount; if(start < 0) { diff --git a/SharpChat/Events/ChatMessage.cs b/SharpChat/Events/ChatMessage.cs deleted file mode 100644 index 5f4963f..0000000 --- a/SharpChat/Events/ChatMessage.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System; -using System.Text.Json.Serialization; - -namespace SharpChat.Events { - public class ChatMessage : IChatMessage { - [JsonIgnore] - public ChatUser Sender { get; set; } - - [JsonIgnore] - public string ChannelName { get; set; } - - [JsonIgnore] - public DateTimeOffset DateTime { get; set; } - - [JsonIgnore] - public ChatMessageFlags Flags { get; set; } = ChatMessageFlags.None; - - [JsonIgnore] - public long SequenceId { get; set; } - - [JsonPropertyName("text")] - public string Text { get; set; } - - public static string PackBotMessage(int type, string id, params string[] parts) { - return type.ToString() + '\f' + id + '\f' + string.Join('\f', parts); - } - } -} diff --git a/SharpChat/Events/IChatEvent.cs b/SharpChat/Events/IChatEvent.cs deleted file mode 100644 index f4b684d..0000000 --- a/SharpChat/Events/IChatEvent.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System; - -namespace SharpChat.Events { - [Flags] - public enum ChatMessageFlags { - None = 0, - Action = 1, - Broadcast = 1 << 1, - Log = 1 << 2, - Private = 1 << 3, - } - - public interface IChatEvent { - DateTimeOffset DateTime { get; set; } - ChatUser Sender { get; set; } - string ChannelName { get; set; } - ChatMessageFlags Flags { get; set; } - long SequenceId { get; set; } - } - - public interface IChatMessage : IChatEvent { - string Text { get; } - } -} diff --git a/SharpChat/Events/UserChannelJoinEvent.cs b/SharpChat/Events/UserChannelJoinEvent.cs deleted file mode 100644 index 0664537..0000000 --- a/SharpChat/Events/UserChannelJoinEvent.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System; -using System.Text.Json.Serialization; - -namespace SharpChat.Events { - public class UserChannelJoinEvent : IChatEvent { - [JsonIgnore] - public DateTimeOffset DateTime { get; set; } - - [JsonIgnore] - public ChatUser Sender { get; set; } - - [JsonIgnore] - public string ChannelName { get; set; } - - [JsonIgnore] - public ChatMessageFlags Flags { get; set; } = ChatMessageFlags.Log; - - [JsonIgnore] - public long SequenceId { get; set; } - - public UserChannelJoinEvent() { } - public UserChannelJoinEvent(DateTimeOffset joined, ChatUser user, ChatChannel channel) { - DateTime = joined; - Sender = user; - ChannelName = channel.Name; - } - } -} diff --git a/SharpChat/Events/UserChannelLeaveEvent.cs b/SharpChat/Events/UserChannelLeaveEvent.cs deleted file mode 100644 index cd590c8..0000000 --- a/SharpChat/Events/UserChannelLeaveEvent.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System; -using System.Text.Json.Serialization; - -namespace SharpChat.Events { - public class UserChannelLeaveEvent : IChatEvent { - [JsonIgnore] - public DateTimeOffset DateTime { get; set; } - - [JsonIgnore] - public ChatUser Sender { get; set; } - - [JsonIgnore] - public string ChannelName { get; set; } - - [JsonIgnore] - public ChatMessageFlags Flags { get; set; } = ChatMessageFlags.Log; - - [JsonIgnore] - public long SequenceId { get; set; } - - public UserChannelLeaveEvent() { } - public UserChannelLeaveEvent(DateTimeOffset parted, ChatUser user, ChatChannel channel) { - DateTime = parted; - Sender = user; - ChannelName = channel.Name; - } - } -} diff --git a/SharpChat/Events/UserConnectEvent.cs b/SharpChat/Events/UserConnectEvent.cs deleted file mode 100644 index 28db92b..0000000 --- a/SharpChat/Events/UserConnectEvent.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System; -using System.Text.Json.Serialization; - -namespace SharpChat.Events { - public class UserConnectEvent : IChatEvent { - [JsonIgnore] - public DateTimeOffset DateTime { get; set; } - - [JsonIgnore] - public ChatUser Sender { get; set; } - - [JsonIgnore] - public string ChannelName { get; set; } - - [JsonIgnore] - public ChatMessageFlags Flags { get; set; } = ChatMessageFlags.Log; - - [JsonIgnore] - public long SequenceId { get; set; } - - public UserConnectEvent() { } - public UserConnectEvent(DateTimeOffset joined, ChatUser user, ChatChannel channel) { - DateTime = joined; - Sender = user; - ChannelName = channel.Name; - } - } -} diff --git a/SharpChat/Events/UserDisconnectEvent.cs b/SharpChat/Events/UserDisconnectEvent.cs deleted file mode 100644 index 20b4c4a..0000000 --- a/SharpChat/Events/UserDisconnectEvent.cs +++ /dev/null @@ -1,34 +0,0 @@ -using SharpChat.Packet; -using System; -using System.Text.Json.Serialization; - -namespace SharpChat.Events { - public class UserDisconnectEvent : IChatEvent { - - [JsonIgnore] - public DateTimeOffset DateTime { get; set; } - - [JsonIgnore] - public ChatUser Sender { get; set; } - - [JsonIgnore] - public string ChannelName { get; set; } - - [JsonIgnore] - public ChatMessageFlags Flags { get; set; } = ChatMessageFlags.Log; - - [JsonIgnore] - public long SequenceId { get; set; } - - [JsonPropertyName("reason")] - public UserDisconnectReason Reason { get; set; } - - public UserDisconnectEvent() { } - public UserDisconnectEvent(DateTimeOffset parted, ChatUser user, ChatChannel channel, UserDisconnectReason reason) { - DateTime = parted; - Sender = user; - ChannelName = channel.Name; - Reason = reason; - } - } -} diff --git a/SharpChat/Packet/ChatMessageAddPacket.cs b/SharpChat/Packet/ChatMessageAddPacket.cs index 826bb39..21bdd2d 100644 --- a/SharpChat/Packet/ChatMessageAddPacket.cs +++ b/SharpChat/Packet/ChatMessageAddPacket.cs @@ -1,17 +1,46 @@ -using SharpChat.Events; +using SharpChat.EventStorage; using System; using System.Collections.Generic; using System.Text; -namespace SharpChat.Packet { +namespace SharpChat.Packet +{ public class ChatMessageAddPacket : ServerPacket { - public IChatMessage Message { get; private set; } + public DateTimeOffset Created { get; } + public long UserId { get; } + public string Text { get; } + public bool IsAction { get; } + public bool IsPrivate { get; } - public ChatMessageAddPacket(IChatMessage message) : base(message?.SequenceId ?? 0) { - Message = message ?? throw new ArgumentNullException(nameof(message)); + public ChatMessageAddPacket( + long msgId, + DateTimeOffset created, + long userId, + string text, + bool isAction, + bool isPrivate + ) : base(msgId) { + Created = created; + UserId = userId < 0 ? -1 : userId; + Text = text; + IsAction = isAction; + IsPrivate = isPrivate; + } - if(Message.SequenceId < 1) - Message.SequenceId = SequenceId; + public static ChatMessageAddPacket FromStoredEvent(StoredEventInfo sei) { + if(sei == null) + throw new ArgumentNullException(nameof(sei)); + if(sei.Type is not "msg:add" and not "SharpChat.Events.ChatMessage") + throw new ArgumentException("Wrong event type.", nameof(sei)); + + return new ChatMessageAddPacket( + sei.Id, + sei.Created, + sei.Sender?.UserId ?? -1, + string.Empty, // todo: this + (sei.Flags & StoredEventFlags.Action) > 0, + (sei.Flags & StoredEventFlags.Private) > 0 + ); } public override IEnumerable Pack() { @@ -20,33 +49,32 @@ namespace SharpChat.Packet { sb.Append('2'); sb.Append('\t'); - sb.Append(Message.DateTime.ToUnixTimeSeconds()); + sb.Append(Created.ToUnixTimeSeconds()); sb.Append('\t'); - sb.Append(Message.Sender?.UserId ?? -1); + sb.Append(UserId); sb.Append('\t'); - if(Message.Flags.HasFlag(ChatMessageFlags.Action)) + if(IsAction) sb.Append(""); sb.Append( - Message.Text - .Replace("<", "<") + Text.Replace("<", "<") .Replace(">", ">") .Replace("\n", "
") .Replace("\t", " ") ); - if(Message.Flags.HasFlag(ChatMessageFlags.Action)) + if(IsAction) sb.Append("
"); sb.Append('\t'); sb.Append(SequenceId); sb.AppendFormat( "\t1{0}0{1}{2}", - Message.Flags.HasFlag(ChatMessageFlags.Action) ? '1' : '0', - Message.Flags.HasFlag(ChatMessageFlags.Action) ? '0' : '1', - Message.Flags.HasFlag(ChatMessageFlags.Private) ? '1' : '0' + IsAction ? '1' : '0', + IsAction ? '0' : '1', + IsPrivate ? '1' : '0' ); yield return sb.ToString(); diff --git a/SharpChat/Packet/ContextMessagePacket.cs b/SharpChat/Packet/ContextMessagePacket.cs index fbd7fcb..49ac69c 100644 --- a/SharpChat/Packet/ContextMessagePacket.cs +++ b/SharpChat/Packet/ContextMessagePacket.cs @@ -1,14 +1,15 @@ -using SharpChat.Events; +using SharpChat.EventStorage; using System; using System.Collections.Generic; using System.Text; -namespace SharpChat.Packet { +namespace SharpChat.Packet +{ public class ContextMessagePacket : ServerPacket { - public IChatEvent Event { get; private set; } + public StoredEventInfo Event { get; private set; } public bool Notify { get; private set; } - public ContextMessagePacket(IChatEvent evt, bool notify = false) { + public ContextMessagePacket(StoredEventInfo evt, bool notify = false) { Event = evt ?? throw new ArgumentNullException(nameof(evt)); Notify = notify; } @@ -22,15 +23,16 @@ namespace SharpChat.Packet { sb.Append('\t'); sb.Append('1'); sb.Append('\t'); - sb.Append(Event.DateTime.ToUnixTimeSeconds()); + sb.Append(Event.Created.ToUnixTimeSeconds()); sb.Append('\t'); - switch(Event) { - case IChatMessage msg: + switch(Event.Type) { + case "msg:add": + case "SharpChat.Events.ChatMessage": sb.Append(Event.Sender.Pack()); sb.Append('\t'); sb.Append( - msg.Text + Event.Data.RootElement.GetProperty("text").GetString() .Replace("<", "<") .Replace(">", ">") .Replace("\n", "
") @@ -38,29 +40,33 @@ namespace SharpChat.Packet { ); break; - case UserConnectEvent _: + case "user:connect": + case "SharpChat.Events.UserConnectEvent": sb.Append(V1_CHATBOT); sb.Append("0\fjoin\f"); sb.Append(Event.Sender.UserName); break; - case UserChannelJoinEvent _: + case "chan:join": + case "SharpChat.Events.UserChannelJoinEvent": sb.Append(V1_CHATBOT); sb.Append("0\fjchan\f"); sb.Append(Event.Sender.UserName); break; - case UserChannelLeaveEvent _: + case "chan:leave": + case "SharpChat.Events.UserChannelLeaveEvent": sb.Append(V1_CHATBOT); sb.Append("0\flchan\f"); sb.Append(Event.Sender.UserName); break; - case UserDisconnectEvent ude: + case "user:disconnect": + case "SharpChat.Events.UserDisconnectEvent": sb.Append(V1_CHATBOT); sb.Append("0\f"); - switch(ude.Reason) { + switch((UserDisconnectReason)Event.Data.RootElement.GetProperty("reason").GetByte()) { case UserDisconnectReason.Flood: sb.Append("flood"); break; @@ -81,16 +87,15 @@ namespace SharpChat.Packet { break; } - sb.Append('\t'); - sb.Append(Event.SequenceId < 1 ? SequenceId : Event.SequenceId); + sb.Append(Event.Id < 1 ? SequenceId : Event.Id); sb.Append('\t'); sb.Append(Notify ? '1' : '0'); sb.AppendFormat( "\t1{0}0{1}{2}", - Event.Flags.HasFlag(ChatMessageFlags.Action) ? '1' : '0', - Event.Flags.HasFlag(ChatMessageFlags.Action) ? '0' : '1', - Event.Flags.HasFlag(ChatMessageFlags.Private) ? '1' : '0' + Event.Flags.HasFlag(StoredEventFlags.Action) ? '1' : '0', + Event.Flags.HasFlag(StoredEventFlags.Action) ? '0' : '1', + Event.Flags.HasFlag(StoredEventFlags.Private) ? '1' : '0' ); yield return sb.ToString(); diff --git a/SharpChat/PacketHandlers/SendMessageHandler.cs b/SharpChat/PacketHandlers/SendMessageHandler.cs index 86b4d58..a9bdd66 100644 --- a/SharpChat/PacketHandlers/SendMessageHandler.cs +++ b/SharpChat/PacketHandlers/SendMessageHandler.cs @@ -1,12 +1,13 @@ using SharpChat.Commands; using SharpChat.Config; -using SharpChat.Events; +using SharpChat.EventStorage; using SharpChat.Packet; using System; using System.Collections.Generic; using System.Linq; -namespace SharpChat.PacketHandlers { +namespace SharpChat.PacketHandlers +{ public class SendMessageHandler : IChatPacketHandler { private readonly CachedValue MaxMessageLength; @@ -63,7 +64,7 @@ namespace SharpChat.PacketHandlers { Logger.Write($"<{ctx.Connection.Id} {user.UserName}> {messageText}"); #endif - IChatMessage message = null; + (string text, bool isAction)? messageInfo = null; if(messageText.StartsWith("/")) { ChatCommandContext context = new(messageText, ctx.Chat, user, ctx.Connection, channel); @@ -78,7 +79,7 @@ namespace SharpChat.PacketHandlers { if(command != null) { if(command is ActionCommand actionCommand) - message = actionCommand.ActionDispatch(context); + messageInfo = actionCommand.ActionDispatch(context); else { command.Dispatch(context); return; @@ -86,15 +87,22 @@ namespace SharpChat.PacketHandlers { } } - message ??= new ChatMessage { - ChannelName = channel.Name, - DateTime = DateTimeOffset.UtcNow, - Sender = user, - Text = messageText, - }; + messageInfo ??= (messageText, false); - ctx.Chat.Events.AddEvent(message); - ctx.Chat.SendTo(channel, new ChatMessageAddPacket(message)); + long msgId = ctx.Chat.Events.AddEvent( + "msg:add", + user, channel, + new { messageInfo.Value.text }, + messageInfo.Value.isAction ? StoredEventFlags.Action : StoredEventFlags.None + ); + ctx.Chat.SendTo(channel, new ChatMessageAddPacket( + msgId, + DateTimeOffset.Now, + user.UserId, + messageInfo.Value.text, + messageInfo.Value.isAction, + false + )); } finally { ctx.Chat.ContextAccess.Release(); }