using MySqlConnector; using SharpChat.Events; using System; using System.Collections.Generic; using System.Reflection; using System.Text.Json; namespace SharpChat.EventStorage { public partial class MariaDBEventStorage : IEventStorage { private string ConnectionString { get; } private readonly JsonSerializerOptions jsonOpts = new() { DefaultIgnoreCondition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault, }; private static readonly Dictionary EventDataTypes = new(); static MariaDBEventStorage() { foreach(Type type in Assembly.GetExecutingAssembly().GetTypes()) { ChatEventDataForAttribute? forAttr = type.GetCustomAttribute(); if(forAttr == null) continue; if(EventDataTypes.ContainsKey(forAttr.EventName)) throw new InvalidOperationException($"Attempted to register more than one data type for {forAttr.EventName}!"); EventDataTypes.Add(forAttr.EventName, type); } } public MariaDBEventStorage(string connString) { ConnectionString = connString; } public void HandleEvent(ChatEventInfo info) { RunCommand( "INSERT INTO sqc_events (event_id, event_type, event_created, event_channel, event_data" + ", event_sender, event_sender_name, event_sender_colour, event_sender_rank, event_sender_nick, event_sender_perms" + ") VALUES (@id, @type, FROM_UNIXTIME(@created), @channel, @data" + ", @senderId, @senderName, @senderColour, @senderRank, @senderNickName, @senderPerms)", new MySqlParameter("id", info.Id), new MySqlParameter("type", info.Type), new MySqlParameter("created", info.Created.ToUnixTimeSeconds()), new MySqlParameter("channel", string.IsNullOrWhiteSpace(info.ChannelName) ? null : info.ChannelName), new MySqlParameter("data", JsonSerializer.Serialize(info.Data, info.Data.GetType(), jsonOpts)), new MySqlParameter("senderId", info.SenderId < 1 ? null : info.SenderId), new MySqlParameter("senderName", info.SenderName), new MySqlParameter("senderColour", info.SenderColour.ToMisuzu()), new MySqlParameter("senderRank", info.SenderRank), new MySqlParameter("senderNickName", string.IsNullOrWhiteSpace(info.SenderNickName) ? null : info.SenderNickName), new MySqlParameter("senderPerms", info.SenderPerms) ); if(info.Type.Equals("msg:delete") && info.Data is MessageDeleteEventData msgDelete) RunCommand( "UPDATE IGNORE sqc_events SET event_deleted = NOW() WHERE event_id = @id AND event_deleted IS NULL", new MySqlParameter("id", msgDelete.MessageId) ); } public ChatEventInfo? GetEvent(long eventId) { try { using MySqlDataReader? reader = RunQuery( "SELECT event_id, event_type, event_channel, event_data" + ", 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", eventId) ); if(reader != null) while(reader.Read()) { ChatEventInfo evt = ReadEvent(reader); if(evt != null) return evt; } } catch(MySqlException ex) { Logger.Write(ex); } return null; } private static ChatEventInfo ReadEvent(MySqlDataReader reader) { string eventType = reader.GetString("event_type"); ChatEventData eventData = EventDataTypes.ContainsKey(eventType) ? (ChatEventData)(JsonSerializer.Deserialize(reader.GetString("event_data"), EventDataTypes[eventType]) ?? ChatEventData.EmptyInstance) : ChatEventData.EmptyInstance; return new ChatEventInfo( reader.GetInt64("event_id"), eventType, DateTimeOffset.FromUnixTimeSeconds(reader.GetInt32("event_created")), reader.IsDBNull(reader.GetOrdinal("event_channel")) ? string.Empty : reader.GetString("event_channel"), reader.IsDBNull(reader.GetOrdinal("event_sender")) ? -1 : reader.GetInt64("event_sender"), reader.IsDBNull(reader.GetOrdinal("event_sender_name")) ? string.Empty : reader.GetString("event_sender_name"), reader.IsDBNull(reader.GetOrdinal("event_sender_colour")) ? Colour.None : Colour.FromMisuzu(reader.GetInt32("event_sender_colour")), reader.IsDBNull(reader.GetOrdinal("event_sender_rank")) ? 0 : reader.GetInt32("event_sender_rank"), reader.IsDBNull(reader.GetOrdinal("event_sender_nick")) ? null : reader.GetString("event_sender_nick"), (UserPermissions)(reader.IsDBNull(reader.GetOrdinal("event_sender_perms")) ? 0 : reader.GetInt32("event_sender_perms")), eventData ); } public IEnumerable GetChannelEventLog(string channelName, int amount = 20, int after = 0) { List events = new(); try { using MySqlDataReader? reader = RunQuery( "SELECT event_id, event_type, event_channel, event_data" + ", event_sender, event_sender_name, event_sender_colour, event_sender_rank, event_sender_nick, event_sender_perms" + ", UNIX_TIMESTAMP(event_created) AS event_created" + " FROM sqc_events WHERE event_deleted IS NULL" + " AND (event_channel IS NULL OR event_channel = @channel)" + " AND event_id > @after" + " ORDER BY event_id DESC" + " LIMIT @amount", new MySqlParameter("channel", channelName), new MySqlParameter("amount", amount), new MySqlParameter("after", after) ); if(reader != null) while(reader.Read()) { ChatEventInfo evt = ReadEvent(reader); if(evt != null) events.Add(evt); } } catch(MySqlException ex) { Logger.Write(ex); } events.Reverse(); return events; } } }