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"))
|
|
|
|
|
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",
|
|
|
|
|
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.Raw),
|
|
|
|
|
new MySqlParameter(@"sender_rank", evt.Sender?.Rank),
|
|
|
|
|
new MySqlParameter(@"sender_nick", evt.Sender?.Nickname),
|
|
|
|
|
new MySqlParameter(@"sender_perms", evt.Sender?.Permissions)
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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)
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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");
|
|
|
|
|
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"));
|
|
|
|
|
|
2023-02-07 15:01:56 +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 = new ChatColour(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")
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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)
|
|
|
|
|
);
|
|
|
|
|
|
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)
|
|
|
|
|
);
|
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|