sharp-chat/SharpChatCommon/EventStorage/MariaDBEventStorage.cs

144 lines
6.9 KiB
C#

using MySqlConnector;
using SharpChat.Events;
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Text;
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<string, Type> EventDataTypes = new();
static MariaDBEventStorage() {
foreach(Type type in Assembly.GetExecutingAssembly().GetTypes()) {
ChatEventDataForAttribute? forAttr = type.GetCustomAttribute<ChatEventDataForAttribute>();
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) {
MySqlParameter dataParam = new("data", MySqlDbType.Blob) {
Value = JsonSerializer.SerializeToUtf8Bytes(info.Data, info.Data.GetType(), jsonOpts)
};
RunCommand(
"INSERT INTO `sqc_events` (`event_id`, `event_created`, `event_type`, `event_target`, `event_data`"
+ ", `event_sender`, `event_sender_name`, `event_sender_colour`, `event_sender_rank`, `event_sender_nick`, `event_sender_perms`)"
+ " VALUES (@id, NOW(), @type, @target, @data"
+ ", @sender, @sender_name, @sender_colour, @sender_rank, @sender_nick, @sender_perms)",
new MySqlParameter("id", info.Id),
new MySqlParameter("type", info.Type),
new MySqlParameter("target", string.IsNullOrWhiteSpace(info.ChannelName) ? null : info.ChannelName),
dataParam,
new MySqlParameter("sender", info.SenderId < 1 ? null : info.SenderId),
new MySqlParameter("sender_name", info.SenderName),
new MySqlParameter("sender_colour", info.SenderColour.ToMisuzu()),
new MySqlParameter("sender_rank", info.SenderRank),
new MySqlParameter("sender_nick", info.SenderNickName),
new MySqlParameter("sender_perms", 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_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", 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 = Encoding.ASCII.GetString((byte[])reader["event_type"]);
ChatEventData eventData = EventDataTypes.ContainsKey(eventType)
? (ChatEventData)(JsonSerializer.Deserialize((byte[])reader["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_target")) ? string.Empty : Encoding.ASCII.GetString((byte[])reader["event_target"]),
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.GetInt32("event_sender_rank"),
reader.IsDBNull(reader.GetOrdinal("event_sender_nick")) ? null : reader.GetString("event_sender_nick"),
(UserPermissions)reader.GetInt32("event_sender_perms"),
eventData
);
}
public IEnumerable<ChatEventInfo> GetChannelEventLog(string channelName, int amount = 20, int startAt = 0) {
List<ChatEventInfo> events = new();
try {
using MySqlDataReader? reader = RunQuery(
"SELECT `event_id`, `event_type`, `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", startAt)
);
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;
}
}
}