using MySqlConnector; using System; using System.Collections.Generic; using System.Dynamic; using System.Text; using System.Text.Json; using System.Threading.Channels; namespace SharpChat.EventStorage { public partial class MariaDBEventStorage : IEventStorage { private string ConnectionString { get; } public MariaDBEventStorage(string connString) { ConnectionString = connString ?? throw new ArgumentNullException(nameof(connString)); } 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)); 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, NOW(), @type, @target, @flags, @data" + ", @sender, @sender_name, @sender_colour, @sender_rank, @sender_nick, @sender_perms)", new MySqlParameter("id", id), new MySqlParameter("type", type), new MySqlParameter("target", string.IsNullOrWhiteSpace(channelName) ? null : channelName), new MySqlParameter("flags", (byte)flags), new MySqlParameter("data", data == null ? "{}" : JsonSerializer.SerializeToUtf8Bytes(data)), new MySqlParameter("sender", senderId < 1 ? null : senderId), new MySqlParameter("sender_name", 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 ?? string.Empty, user?.Colour ?? ChatColour.None, user?.Rank ?? 0, user?.NickName, user?.Permissions ?? 0, data, flags ); return id; } 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) ); if(reader != null) while(reader.Read()) { StoredEventInfo evt = ReadEvent(reader); if(evt != null) return evt; } } catch(MySqlException ex) { Logger.Write(ex); } return null; } 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.IsDBNull(reader.GetOrdinal("event_sender_name")) ? string.Empty : 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") ), 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(); 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 OR `event_target` IS NULL)" + " AND `event_id` > @offset" + " ORDER BY `event_id` DESC" + " LIMIT @amount", new MySqlParameter("target", channelName), new MySqlParameter("amount", amount), new MySqlParameter("offset", offset) ); if(reader != null) while(reader.Read()) { StoredEventInfo evt = ReadEvent(reader); if(evt != null) events.Add(evt); } } catch(MySqlException ex) { Logger.Write(ex); } events.Reverse(); return events; } 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.Id) ); } } }