From 8c19c22736607e5675f4f71c9255baa2bf122b5b Mon Sep 17 00:00:00 2001 From: flashwave Date: Sun, 23 Jul 2023 21:31:13 +0000 Subject: [PATCH] Reworking event dispatching... I think? I did this make in february but left it uncommitted. Hopefully it's stable! --- SharpChat/ChatContext.cs | 58 ++++++++++- SharpChat/Commands/ActionCommand.cs | 23 +++-- SharpChat/Commands/BroadcastCommand.cs | 13 ++- SharpChat/Commands/WhisperCommand.cs | 29 ++---- SharpChat/EventStorage/IEventStorage.cs | 25 +++++ SharpChat/EventStorage/MariaDBEventStorage.cs | 73 +++++++++++--- .../MariaDBEventStorage_Migrations.cs | 8 ++ SharpChat/EventStorage/VirtualEventStorage.cs | 64 ++++++++++++- SharpChat/Events/IChatEvent.cs | 10 ++ SharpChat/Events/MessageCreateEvent.cs | 95 +++++++++++++++++++ SharpChat/Packet/ContextMessagePacket.cs | 10 +- .../PacketHandlers/SendMessageHandler.cs | 31 ++---- start.sh | 0 13 files changed, 369 insertions(+), 70 deletions(-) create mode 100644 SharpChat/Events/IChatEvent.cs create mode 100644 SharpChat/Events/MessageCreateEvent.cs mode change 100644 => 100755 start.sh diff --git a/SharpChat/ChatContext.cs b/SharpChat/ChatContext.cs index 113009c..0b6ac2c 100644 --- a/SharpChat/ChatContext.cs +++ b/SharpChat/ChatContext.cs @@ -1,4 +1,5 @@ -using SharpChat.EventStorage; +using SharpChat.Events; +using SharpChat.EventStorage; using SharpChat.Packet; using System; using System.Collections.Generic; @@ -26,6 +27,61 @@ namespace SharpChat Events = evtStore ?? throw new ArgumentNullException(nameof(evtStore)); } + public void DispatchEvent(IChatEvent eventInfo) { + if(eventInfo is MessageCreateEvent mce) { + if(mce.IsBroadcast) { + Send(new LegacyCommandResponse(LCR.BROADCAST, false, mce.MessageText)); + } else if(mce.IsPrivate) { + // The channel name returned by GetDMChannelName should not be exposed to the user, instead @ should be displayed + // e.g. nook sees @Arysil and Arysil sees @nook + + // this entire routine is garbage, channels should probably in the db + if(!mce.ChannelName.StartsWith("@")) + return; + + IEnumerable uids = mce.ChannelName[1..].Split('-', 3).Select(u => long.TryParse(u, out long up) ? up : -1); + if(uids.Count() != 2) + return; + + IEnumerable users = Users.Where(u => uids.Any(uid => uid == u.UserId)); + ChatUser target = users.FirstOrDefault(u => u.UserId != mce.SenderId); + if(target == null) + return; + + foreach(ChatUser user in users) + SendTo(user, new ChatMessageAddPacket( + mce.MessageId, + DateTimeOffset.Now, + mce.SenderId, + mce.SenderId == user.UserId ? $"{target.LegacyName} {mce.MessageText}" : mce.MessageText, + mce.IsAction, + true + )); + } else { + ChatChannel channel = Channels.FirstOrDefault(c => c.NameEquals(mce.ChannelName)); + SendTo(channel, new ChatMessageAddPacket( + mce.MessageId, + DateTimeOffset.Now, + mce.SenderId, + mce.MessageText, + mce.IsAction, + false + )); + } + + Events.AddEvent( + mce.MessageId, "msg:add", + mce.ChannelName, + mce.SenderId, mce.SenderName, mce.SenderColour, mce.SenderRank, mce.SenderNickName, mce.SenderPerms, + new { text = mce.MessageText }, + (mce.IsBroadcast ? StoredEventFlags.Broadcast : 0) + | (mce.IsAction ? StoredEventFlags.Action : 0) + | (mce.IsPrivate ? StoredEventFlags.Private : 0) + ); + return; + } + } + public void Update() { foreach(ChatConnection conn in Connections) if(!conn.IsDisposed && conn.HasTimedOut) { diff --git a/SharpChat/Commands/ActionCommand.cs b/SharpChat/Commands/ActionCommand.cs index ceeb55e..0677350 100644 --- a/SharpChat/Commands/ActionCommand.cs +++ b/SharpChat/Commands/ActionCommand.cs @@ -1,9 +1,8 @@ -using SharpChat.EventStorage; +using SharpChat.Events; using System; using System.Linq; -namespace SharpChat.Commands -{ +namespace SharpChat.Commands { public class ActionCommand : IChatCommand { public bool IsMatch(ChatCommandContext ctx) { return ctx.NameEquals("action") @@ -11,11 +10,21 @@ namespace SharpChat.Commands } public void Dispatch(ChatCommandContext ctx) { - throw new NotImplementedException(); - } + if(!ctx.Args.Any()) + return; - public (string, bool)? ActionDispatch(ChatCommandContext ctx) { - return ctx.Args.Any() ? (string.Join(' ', ctx.Args), true) : null; + string actionStr = string.Join(' ', ctx.Args); + if(string.IsNullOrWhiteSpace(actionStr)) + return; + + ctx.Chat.DispatchEvent(new MessageCreateEvent( + SharpId.Next(), + ctx.Channel, + ctx.User, + DateTimeOffset.Now, + actionStr, + false, true, false + )); } } } diff --git a/SharpChat/Commands/BroadcastCommand.cs b/SharpChat/Commands/BroadcastCommand.cs index 6ffa24c..2c86929 100644 --- a/SharpChat/Commands/BroadcastCommand.cs +++ b/SharpChat/Commands/BroadcastCommand.cs @@ -1,4 +1,6 @@ -using SharpChat.Packet; +using SharpChat.Events; +using SharpChat.Packet; +using System; namespace SharpChat.Commands { public class BroadcastCommand : IChatCommand { @@ -13,7 +15,14 @@ namespace SharpChat.Commands { return; } - ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.BROADCAST, false, string.Join(' ', ctx.Args))); + ctx.Chat.DispatchEvent(new MessageCreateEvent( + SharpId.Next(), + string.Empty, + ctx.User, + DateTimeOffset.Now, + string.Join(' ', ctx.Args), + false, false, true + )); } } } diff --git a/SharpChat/Commands/WhisperCommand.cs b/SharpChat/Commands/WhisperCommand.cs index 78cc743..3daa0de 100644 --- a/SharpChat/Commands/WhisperCommand.cs +++ b/SharpChat/Commands/WhisperCommand.cs @@ -1,10 +1,9 @@ -using SharpChat.EventStorage; +using SharpChat.Events; 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") @@ -28,25 +27,13 @@ namespace SharpChat.Commands if(whisperUser == ctx.User) return; - string whisperStr = string.Join(' ', ctx.Args.Skip(1)); - string whisperChan = ChatUser.GetDMChannelName(ctx.User, whisperUser); - DateTimeOffset dateTime = DateTimeOffset.Now; - - ctx.Chat.SendTo(whisperUser, new ChatMessageAddPacket( + ctx.Chat.DispatchEvent(new MessageCreateEvent( 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 + ChatUser.GetDMChannelName(ctx.User, whisperUser), + ctx.User, + DateTimeOffset.Now, + string.Join(' ', ctx.Args.Skip(1)), + true, false, false )); } } diff --git a/SharpChat/EventStorage/IEventStorage.cs b/SharpChat/EventStorage/IEventStorage.cs index 0e84190..0cac6e2 100644 --- a/SharpChat/EventStorage/IEventStorage.cs +++ b/SharpChat/EventStorage/IEventStorage.cs @@ -3,6 +3,31 @@ namespace SharpChat.EventStorage { public interface IEventStorage { + void AddEvent( + long id, string type, + object data = null, + StoredEventFlags flags = StoredEventFlags.None + ); + void AddEvent( + long id, string type, + string channelName, + object data = null, + StoredEventFlags flags = StoredEventFlags.None + ); + void AddEvent( + long id, string type, + long senderId, string senderName, ChatColour senderColour, int senderRank, string senderNick, ChatUserPermissions senderPerms, + object data = null, + StoredEventFlags flags = StoredEventFlags.None + ); + void AddEvent( + long id, string type, + string channelName, + long senderId, string senderName, ChatColour senderColour, int senderRank, string senderNick, ChatUserPermissions senderPerms, + object data = null, + StoredEventFlags flags = StoredEventFlags.None + ); + long AddEvent(string type, ChatUser user, ChatChannel channel, object data = null, StoredEventFlags flags = StoredEventFlags.None); void RemoveEvent(StoredEventInfo evt); StoredEventInfo GetEvent(long seqId); diff --git a/SharpChat/EventStorage/MariaDBEventStorage.cs b/SharpChat/EventStorage/MariaDBEventStorage.cs index f5aaec7..e751897 100644 --- a/SharpChat/EventStorage/MariaDBEventStorage.cs +++ b/SharpChat/EventStorage/MariaDBEventStorage.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.Dynamic; using System.Text; using System.Text.Json; +using System.Threading.Channels; namespace SharpChat.EventStorage { @@ -14,12 +15,42 @@ namespace SharpChat.EventStorage ConnectionString = connString ?? throw new ArgumentNullException(nameof(connString)); } - public long AddEvent(string type, ChatUser user, ChatChannel channel, object data = null, StoredEventFlags flags = StoredEventFlags.None) { + public void AddEvent( + long id, string type, + object data = null, + StoredEventFlags flags = StoredEventFlags.None + ) { + AddEvent(id, type, null, 0, null, ChatColour.None, 0, null, 0, data, flags); + } + + public void AddEvent( + long id, string type, + string channelName, + object data = null, + StoredEventFlags flags = StoredEventFlags.None + ) { + AddEvent(id, type, channelName, 0, null, ChatColour.None, 0, null, 0, data, flags); + } + + public void AddEvent( + long id, string type, + long senderId, string senderName, ChatColour senderColour, int senderRank, string senderNick, ChatUserPermissions senderPerms, + object data = null, + StoredEventFlags flags = StoredEventFlags.None + ) { + AddEvent(id, type, null, senderId, senderName, senderColour, senderRank, senderNick, senderPerms, data, flags); + } + + public void AddEvent( + long id, string type, + string channelName, + long senderId, string senderName, ChatColour senderColour, int senderRank, string senderNick, ChatUserPermissions senderPerms, + object data = null, + StoredEventFlags flags = StoredEventFlags.None + ) { if(type == null) throw new ArgumentNullException(nameof(type)); - 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`)" @@ -27,15 +58,35 @@ namespace SharpChat.EventStorage + ", @sender, @sender_name, @sender_colour, @sender_rank, @sender_nick, @sender_perms)", new MySqlParameter("id", id), new MySqlParameter("type", type), - new MySqlParameter("target", channel?.Name ?? null), + new MySqlParameter("target", string.IsNullOrWhiteSpace(channelName) ? null : channelName), 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) + new MySqlParameter("sender", senderId < 1 ? null : senderId), + new MySqlParameter("sender_name", string.IsNullOrWhiteSpace(senderName) ? null : senderName), + new MySqlParameter("sender_colour", senderColour.ToMisuzu()), + new MySqlParameter("sender_rank", senderRank), + new MySqlParameter("sender_nick", string.IsNullOrWhiteSpace(senderNick) ? null : senderNick), + new MySqlParameter("sender_perms", senderPerms) + ); + } + + public long AddEvent(string type, ChatUser user, ChatChannel channel, object data = null, StoredEventFlags flags = StoredEventFlags.None) { + if(type == null) + throw new ArgumentNullException(nameof(type)); + + long id = SharpId.Next(); + + AddEvent( + id, type, + channel?.Name, + user?.UserId ?? 0, + user?.UserName, + user?.Colour ?? ChatColour.None, + user?.Rank ?? 0, + user?.NickName, + user?.Permissions ?? 0, + data, + flags ); return id; @@ -95,7 +146,7 @@ namespace SharpChat.EventStorage + ", 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" + + " WHERE `event_deleted` IS NULL AND (`event_target` = @target OR `event_target` IS NULL)" + " AND `event_id` > @offset" + " ORDER BY `event_id` DESC" + " LIMIT @amount", diff --git a/SharpChat/EventStorage/MariaDBEventStorage_Migrations.cs b/SharpChat/EventStorage/MariaDBEventStorage_Migrations.cs index 8cd6f0e..b5f95bd 100644 --- a/SharpChat/EventStorage/MariaDBEventStorage_Migrations.cs +++ b/SharpChat/EventStorage/MariaDBEventStorage_Migrations.cs @@ -29,6 +29,14 @@ namespace SharpChat.EventStorage { ); DoMigration("create_events_table", CreateEventsTable); + DoMigration("allow_null_target", AllowNullTarget); + } + + private void AllowNullTarget() { + RunCommand( + "ALTER TABLE `sqc_events`" + + " CHANGE COLUMN `event_target` `event_target` VARBINARY(255) NULL AFTER `event_type`;" + ); } private void CreateEventsTable() { diff --git a/SharpChat/EventStorage/VirtualEventStorage.cs b/SharpChat/EventStorage/VirtualEventStorage.cs index 81a91fa..8d312ea 100644 --- a/SharpChat/EventStorage/VirtualEventStorage.cs +++ b/SharpChat/EventStorage/VirtualEventStorage.cs @@ -3,20 +3,76 @@ 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(); - public long AddEvent(string type, ChatUser user, ChatChannel channel, object data = null, StoredEventFlags flags = StoredEventFlags.None) { + public void AddEvent( + long id, string type, + object data = null, + StoredEventFlags flags = StoredEventFlags.None + ) { + AddEvent(id, type, null, 0, null, ChatColour.None, 0, null, 0, data, flags); + } + + public void AddEvent( + long id, string type, + string channelName, + object data = null, + StoredEventFlags flags = StoredEventFlags.None + ) { + AddEvent(id, type, channelName, 0, null, ChatColour.None, 0, null, 0, data, flags); + } + + public void AddEvent( + long id, string type, + long senderId, string senderName, ChatColour senderColour, int senderRank, string senderNick, ChatUserPermissions senderPerms, + object data = null, + StoredEventFlags flags = StoredEventFlags.None + ) { + AddEvent(id, type, null, senderId, senderName, senderColour, senderRank, senderNick, senderPerms, data, flags); + } + + public void AddEvent( + long id, string type, + string channelName, + long senderId, string senderName, ChatColour senderColour, int senderRank, string senderNick, ChatUserPermissions senderPerms, + 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)); + Events.Add(id, new(id, type, senderId < 1 ? null : new ChatUser( + senderId, + senderName, + senderColour, + senderRank, + senderPerms, + senderNick + ), DateTimeOffset.Now, null, channelName, hack, flags)); + } + + public long AddEvent(string type, ChatUser user, ChatChannel channel, object data = null, StoredEventFlags flags = StoredEventFlags.None) { + if(type == null) + throw new ArgumentNullException(nameof(type)); long id = SharpId.Next(); - Events.Add(id, new(id, type, user, DateTimeOffset.Now, null, channel?.Name ?? null, hack, flags)); + + AddEvent( + id, type, + channel?.Name, + user?.UserId ?? 0, + user?.UserName, + user?.Colour ?? ChatColour.None, + user?.Rank ?? 0, + user?.NickName, + user?.Permissions ?? 0, + data, + flags + ); return id; } diff --git a/SharpChat/Events/IChatEvent.cs b/SharpChat/Events/IChatEvent.cs new file mode 100644 index 0000000..db2ee84 --- /dev/null +++ b/SharpChat/Events/IChatEvent.cs @@ -0,0 +1,10 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace SharpChat.Events { + public interface IChatEvent { + } +} diff --git a/SharpChat/Events/MessageCreateEvent.cs b/SharpChat/Events/MessageCreateEvent.cs new file mode 100644 index 0000000..06fcb4f --- /dev/null +++ b/SharpChat/Events/MessageCreateEvent.cs @@ -0,0 +1,95 @@ +using System; + +namespace SharpChat.Events { + public class MessageCreateEvent : IChatEvent { + public long MessageId { get; } + public string ChannelName { get; } + public long SenderId { get; } + public string SenderName { get; } + public ChatColour SenderColour { get; } + public int SenderRank { get; } + public string SenderNickName { get; } + public ChatUserPermissions SenderPerms { get; } + public DateTimeOffset MessageCreated { get; } + public string MessageChannel { get; } + public string MessageText { get; } + public bool IsPrivate { get; } + public bool IsAction { get; } + public bool IsBroadcast { get; } + + public MessageCreateEvent( + long msgId, + string channelName, + long senderId, + string senderName, + ChatColour senderColour, + int senderRank, + string senderNickName, + ChatUserPermissions senderPerms, + DateTimeOffset msgCreated, + string msgText, + bool isPrivate, + bool isAction, + bool isBroadcast + ) { + MessageId = msgId; + ChannelName = channelName; + SenderId = senderId; + SenderName = senderName; + SenderColour = senderColour; + SenderRank = senderRank; + SenderNickName = senderNickName; + SenderPerms = senderPerms; + MessageCreated = msgCreated; + MessageText = msgText; + IsPrivate = isPrivate; + IsAction = isAction; + IsBroadcast = isBroadcast; + } + + public MessageCreateEvent( + long msgId, + string channelName, + ChatUser sender, + DateTimeOffset msgCreated, + string msgText, + bool isPrivate, + bool isAction, + bool isBroadcast + ) : this( + msgId, + channelName, + sender?.UserId ?? -1, + sender?.UserName ?? null, + sender?.Colour ?? ChatColour.None, + sender?.Rank ?? 0, + sender?.NickName ?? null, + sender?.Permissions ?? 0, + msgCreated, + msgText, + isPrivate, + isAction, + isBroadcast + ) { } + + public MessageCreateEvent( + long msgId, + ChatChannel channel, + ChatUser sender, + DateTimeOffset msgCreated, + string msgText, + bool isPrivate, + bool isAction, + bool isBroadcast + ) : this( + msgId, + channel?.Name ?? null, + sender, + msgCreated, + msgText, + isPrivate, + isAction, + isBroadcast + ) { } + } +} diff --git a/SharpChat/Packet/ContextMessagePacket.cs b/SharpChat/Packet/ContextMessagePacket.cs index 49ac69c..78bede9 100644 --- a/SharpChat/Packet/ContextMessagePacket.cs +++ b/SharpChat/Packet/ContextMessagePacket.cs @@ -29,8 +29,14 @@ namespace SharpChat.Packet switch(Event.Type) { case "msg:add": case "SharpChat.Events.ChatMessage": - sb.Append(Event.Sender.Pack()); - sb.Append('\t'); + if((Event.Flags & StoredEventFlags.Broadcast) > 0) + sb.Append(V1_CHATBOT); + else { + sb.Append(Event.Sender.Pack()); + sb.Append('\t'); + } + if((Event.Flags & StoredEventFlags.Broadcast) > 0) + sb.Append("0\fsay\f"); sb.Append( Event.Data.RootElement.GetProperty("text").GetString() .Replace("<", "<") diff --git a/SharpChat/PacketHandlers/SendMessageHandler.cs b/SharpChat/PacketHandlers/SendMessageHandler.cs index a9bdd66..7f7c50e 100644 --- a/SharpChat/PacketHandlers/SendMessageHandler.cs +++ b/SharpChat/PacketHandlers/SendMessageHandler.cs @@ -1,5 +1,6 @@ using SharpChat.Commands; using SharpChat.Config; +using SharpChat.Events; using SharpChat.EventStorage; using SharpChat.Packet; using System; @@ -64,8 +65,6 @@ namespace SharpChat.PacketHandlers Logger.Write($"<{ctx.Connection.Id} {user.UserName}> {messageText}"); #endif - (string text, bool isAction)? messageInfo = null; - if(messageText.StartsWith("/")) { ChatCommandContext context = new(messageText, ctx.Chat, user, ctx.Connection, channel); @@ -78,30 +77,18 @@ namespace SharpChat.PacketHandlers } if(command != null) { - if(command is ActionCommand actionCommand) - messageInfo = actionCommand.ActionDispatch(context); - else { - command.Dispatch(context); - return; - } + command.Dispatch(context); + return; } } - messageInfo ??= (messageText, false); - - 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, + ctx.Chat.DispatchEvent(new MessageCreateEvent( + SharpId.Next(), + channel, + user, DateTimeOffset.Now, - user.UserId, - messageInfo.Value.text, - messageInfo.Value.isAction, - false + messageText, + false, false, false )); } finally { ctx.Chat.ContextAccess.Release(); diff --git a/start.sh b/start.sh old mode 100644 new mode 100755