sharp-chat/SharpChat/Database.cs

231 lines
9.2 KiB
C#
Raw Normal View History

2022-08-30 15:00:58 +00:00
using MySqlConnector;
using SharpChat.Events;
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Text.Json;
namespace SharpChat {
public static partial class Database {
private static string ConnectionString = null;
public static bool HasDatabase
=> !string.IsNullOrWhiteSpace(ConnectionString);
public static void ReadConfig() {
if(!File.Exists("mariadb.txt"))
2022-08-30 15:00:58 +00:00
return;
string[] config = File.ReadAllLines("mariadb.txt");
2023-02-07 15:01:56 +00:00
if(config.Length < 4)
2022-08-30 15:00:58 +00:00
return;
Init(config[0], config[1], config[2], config[3]);
}
public static void Init(string host, string username, string password, string database) {
ConnectionString = new MySqlConnectionStringBuilder {
Server = host,
UserID = username,
Password = password,
Database = database,
OldGuids = false,
TreatTinyAsBoolean = false,
CharacterSet = "utf8mb4",
2022-08-30 15:00:58 +00:00
SslMode = MySqlSslMode.None,
ForceSynchronous = true,
ConnectionTimeout = 5,
}.ToString();
RunMigrations();
}
public static void Deinit() {
ConnectionString = null;
}
private static MySqlConnection GetConnection() {
2023-02-07 15:01:56 +00:00
if(!HasDatabase)
2022-08-30 15:00:58 +00:00
return null;
2023-02-07 15:01:56 +00:00
MySqlConnection conn = new(ConnectionString);
2022-08-30 15:00:58 +00:00
conn.Open();
return conn;
}
private static int RunCommand(string command, params MySqlParameter[] parameters) {
2023-02-07 15:01:56 +00:00
if(!HasDatabase)
2022-08-30 15:00:58 +00:00
return 0;
try {
using MySqlConnection conn = GetConnection();
using MySqlCommand cmd = conn.CreateCommand();
2023-02-07 15:01:56 +00:00
if(parameters?.Length > 0)
2022-08-30 15:00:58 +00:00
cmd.Parameters.AddRange(parameters);
cmd.CommandText = command;
return cmd.ExecuteNonQuery();
2023-02-07 15:01:56 +00:00
} catch(MySqlException ex) {
2022-08-30 15:00:58 +00:00
Logger.Write(ex);
}
return 0;
}
private static MySqlDataReader RunQuery(string command, params MySqlParameter[] parameters) {
2023-02-07 15:01:56 +00:00
if(!HasDatabase)
2022-08-30 15:00:58 +00:00
return null;
try {
MySqlConnection conn = GetConnection();
MySqlCommand cmd = conn.CreateCommand();
2023-02-07 15:01:56 +00:00
if(parameters?.Length > 0)
2022-08-30 15:00:58 +00:00
cmd.Parameters.AddRange(parameters);
cmd.CommandText = command;
return cmd.ExecuteReader(System.Data.CommandBehavior.CloseConnection);
} catch(MySqlException ex) {
Logger.Write(ex);
}
return null;
}
private static object RunQueryValue(string command, params MySqlParameter[] parameters) {
2023-02-07 15:01:56 +00:00
if(!HasDatabase)
2022-08-30 15:00:58 +00:00
return null;
try {
using MySqlConnection conn = GetConnection();
using MySqlCommand cmd = conn.CreateCommand();
2023-02-07 15:01:56 +00:00
if(parameters?.Length > 0)
2022-08-30 15:00:58 +00:00
cmd.Parameters.AddRange(parameters);
cmd.CommandText = command;
cmd.Prepare();
return cmd.ExecuteScalar();
} catch(MySqlException ex) {
Logger.Write(ex);
}
return null;
}
private const long ID_EPOCH = 1588377600000;
private static int IdCounter = 0;
public static long GenerateId() {
2023-02-07 15:01:56 +00:00
if(IdCounter > 200)
2022-08-30 15:00:58 +00:00
IdCounter = 0;
long id = 0;
id |= (DateTimeOffset.Now.ToUnixTimeMilliseconds() - ID_EPOCH) << 8;
id |= (ushort)(++IdCounter);
return id;
}
public static void LogEvent(IChatEvent evt) {
if(evt.SequenceId < 1)
evt.SequenceId = GenerateId();
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"
+ ", @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.Target.TargetName),
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)
2022-08-30 15:00:58 +00:00
);
}
public static void DeleteEvent(IChatEvent evt) {
RunCommand(
"UPDATE IGNORE `sqc_events` SET `event_deleted` = NOW() WHERE `event_id` = @id AND `event_deleted` IS NULL",
new MySqlParameter("id", evt.SequenceId)
2022-08-30 15:00:58 +00:00
);
}
private static IChatEvent ReadEvent(MySqlDataReader reader, IPacketTarget target = null) {
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");
2022-08-30 15:00:58 +00:00
evt.Target = target;
evt.TargetName = target?.TargetName ?? Encoding.ASCII.GetString((byte[])reader["event_target"]);
evt.Flags = (ChatMessageFlags)reader.GetByte("event_flags");
evt.DateTime = DateTimeOffset.FromUnixTimeSeconds(reader.GetInt32("event_created"));
2022-08-30 15:00:58 +00:00
if(!reader.IsDBNull(reader.GetOrdinal("event_sender"))) {
2022-08-30 15:00:58 +00:00
evt.Sender = new BasicUser {
UserId = reader.GetInt64("event_sender"),
Username = reader.GetString("event_sender_name"),
Colour = ChatColour.FromMisuzu(reader.GetInt32("event_sender_colour")),
Rank = reader.GetInt32("event_sender_rank"),
Nickname = reader.IsDBNull(reader.GetOrdinal("event_sender_nick")) ? null : reader.GetString("event_sender_nick"),
Permissions = (ChatUserPermissions)reader.GetInt32("event_sender_perms")
2022-08-30 15:00:58 +00:00
};
}
return evt;
}
public static IEnumerable<IChatEvent> GetEvents(IPacketTarget target, int amount, int offset) {
2023-02-07 15:01:56 +00:00
List<IChatEvent> events = new();
2022-08-30 15:00:58 +00:00
try {
using MySqlDataReader reader = RunQuery(
"SELECT `event_id`, `event_type`, `event_flags`, `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_target` = @target"
+ " AND `event_id` > @offset"
+ " ORDER BY `event_id` DESC"
+ " LIMIT @amount",
new MySqlParameter("target", target.TargetName),
new MySqlParameter("amount", amount),
new MySqlParameter("offset", offset)
2022-08-30 15:00:58 +00:00
);
2023-02-07 15:01:56 +00:00
while(reader.Read()) {
2022-08-30 15:00:58 +00:00
IChatEvent evt = ReadEvent(reader, target);
2023-02-07 15:01:56 +00:00
if(evt != null)
2022-08-30 15:00:58 +00:00
events.Add(evt);
}
} catch(MySqlException ex) {
Logger.Write(ex);
}
return events;
}
public static IChatEvent 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`"
+ " FROM `sqc_events`"
+ " WHERE `event_id` = @id",
new MySqlParameter("id", seqId)
2022-08-30 15:00:58 +00:00
);
2023-02-07 15:01:56 +00:00
while(reader.Read()) {
2022-08-30 15:00:58 +00:00
IChatEvent evt = ReadEvent(reader);
2023-02-07 15:01:56 +00:00
if(evt != null)
2022-08-30 15:00:58 +00:00
return evt;
}
} catch(MySqlException ex) {
Logger.Write(ex);
}
return null;
}
}
}