Compare commits

..

No commits in common. "6f1149ad5e45d7c49eafc9f260bdf6679563249e" and "6d3db23f804e4c82de0f9b1c6b340f99cbdc21af" have entirely different histories.

9 changed files with 183 additions and 287 deletions

View File

@ -15,25 +15,12 @@ namespace DHT.Desktop {
for (int i = 0; i < args.Length; i++) { for (int i = 0; i < args.Length; i++) {
string key = args[i]; string key = args[i];
switch (key) { if (i >= args.Length - 1) {
case "-debug":
Log.IsDebugEnabled = true;
continue;
}
string value;
if (i == 0 && !key.StartsWith('-')) {
value = key;
key = "-db";
}
else if (i >= args.Length - 1) {
Log.Warn("Missing value for command line argument: " + key); Log.Warn("Missing value for command line argument: " + key);
continue; continue;
} }
else {
value = args[++i]; string value = args[++i];
}
switch (key) { switch (key) {
case "-db": case "-db":

View File

@ -56,11 +56,11 @@ namespace DHT.Desktop.Common {
} }
public static async Task<DialogResult.YesNo> ShowCanUpgradeDatabaseDialog(Window window) { public static async Task<DialogResult.YesNo> ShowCanUpgradeDatabaseDialog(Window window) {
return await Dialog.ShowYesNo(window, "Database Upgrade", "This database was created with an older version of DHT. If you proceed, the database will be upgraded and will no longer open in previous versions of DHT.\n\nPlease ensure you have a backup of the database. Do you want to proceed with the upgrade?"); return await Dialog.ShowYesNo(window, "Database Upgrade", "This database was created with an older version of DHT. If you proceed, the database will be upgraded and will no longer open in previous versions of DHT. Do you want to proceed with the upgrade?");
} }
public static async Task<DialogResult.YesNo> ShowCanUpgradeMultipleDatabaseDialog(Window window) { public static async Task<DialogResult.YesNo> ShowCanUpgradeMultipleDatabaseDialog(Window window) {
return await Dialog.ShowYesNo(window, "Database Upgrade", "One or more databases were created with an older version of DHT. If you proceed, these databases will be upgraded and will no longer open in previous versions of DHT. Otherwise, these databases will be skipped.\n\nPlease ensure you have a backup of the databases. Do you want to proceed with the upgrade?"); return await Dialog.ShowYesNo(window, "Database Upgrade", "One or more databases were created with an older version of DHT. If you proceed, these databases will be upgraded and will no longer open in previous versions of DHT. Otherwise, these databases will be skipped. Do you want to proceed with the upgrade?");
} }
} }
} }

View File

@ -1,17 +1,13 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Dynamic;
using System.Linq; using System.Linq;
using System.Text.Json; using System.Text.Json;
using DHT.Server.Data; using DHT.Server.Data;
using DHT.Server.Data.Filters; using DHT.Server.Data.Filters;
using DHT.Utils.Logging;
namespace DHT.Server.Database.Export { namespace DHT.Server.Database.Export {
public static class ViewerJsonExport { public static class ViewerJsonExport {
private static readonly Log Log = Log.ForType(typeof(ViewerJsonExport));
public static string Generate(IDatabaseFile db, MessageFilter? filter = null) { public static string Generate(IDatabaseFile db, MessageFilter? filter = null) {
var perf = Log.Start();
var includedUserIds = new HashSet<ulong>(); var includedUserIds = new HashSet<ulong>();
var includedChannelIds = new HashSet<ulong>(); var includedChannelIds = new HashSet<ulong>();
var includedServerIds = new HashSet<ulong>(); var includedServerIds = new HashSet<ulong>();
@ -38,19 +34,16 @@ namespace DHT.Server.Database.Export {
var servers = GenerateServerList(db, includedServerIds, out var serverindex); var servers = GenerateServerList(db, includedServerIds, out var serverindex);
var channels = GenerateChannelList(includedChannels, serverindex); var channels = GenerateChannelList(includedChannels, serverindex);
var json = JsonSerializer.Serialize(new { return JsonSerializer.Serialize(new {
meta = new { users, userindex, servers, channels }, meta = new { users, userindex, servers, channels },
data = GenerateMessageList(includedMessages, userIndices) data = GenerateMessageList(includedMessages, userIndices)
}, opts); }, opts);
perf.End();
return json;
} }
private static object GenerateUserList(IDatabaseFile db, HashSet<ulong> userIds, out List<string> userindex, out Dictionary<ulong, object> userIndices) { private static dynamic GenerateUserList(IDatabaseFile db, HashSet<ulong> userIds, out List<string> userindex, out Dictionary<ulong, int> userIndices) {
var users = new Dictionary<string, object>(); var users = new Dictionary<string, dynamic>();
userindex = new List<string>(); userindex = new List<string>();
userIndices = new Dictionary<ulong, object>(); userIndices = new Dictionary<ulong, int>();
foreach (var user in db.GetAllUsers()) { foreach (var user in db.GetAllUsers()) {
var id = user.Id; var id = user.Id;
@ -58,16 +51,15 @@ namespace DHT.Server.Database.Export {
continue; continue;
} }
var obj = new Dictionary<string, object> { dynamic obj = new ExpandoObject();
["name"] = user.Name obj.name = user.Name;
};
if (user.AvatarUrl != null) { if (user.AvatarUrl != null) {
obj["avatar"] = user.AvatarUrl; obj.avatar = user.AvatarUrl;
} }
if (user.Discriminator != null) { if (user.Discriminator != null) {
obj["tag"] = user.Discriminator; obj.tag = user.Discriminator;
} }
var idStr = id.ToString(); var idStr = id.ToString();
@ -79,9 +71,9 @@ namespace DHT.Server.Database.Export {
return users; return users;
} }
private static object GenerateServerList(IDatabaseFile db, HashSet<ulong> serverIds, out Dictionary<ulong, object> serverIndices) { private static dynamic GenerateServerList(IDatabaseFile db, HashSet<ulong> serverIds, out Dictionary<ulong, int> serverIndices) {
var servers = new List<object>(); var servers = new List<dynamic>();
serverIndices = new Dictionary<ulong, object>(); serverIndices = new Dictionary<ulong, int>();
foreach (var server in db.GetAllServers()) { foreach (var server in db.GetAllServers()) {
var id = server.Id; var id = server.Id;
@ -90,38 +82,37 @@ namespace DHT.Server.Database.Export {
} }
serverIndices[id] = servers.Count; serverIndices[id] = servers.Count;
servers.Add(new Dictionary<string, object> { servers.Add(new {
["name"] = server.Name, name = server.Name,
["type"] = ServerTypes.ToJsonViewerString(server.Type) type = ServerTypes.ToJsonViewerString(server.Type)
}); });
} }
return servers; return servers;
} }
private static object GenerateChannelList(List<Channel> includedChannels, Dictionary<ulong, object> serverIndices) { private static dynamic GenerateChannelList(List<Channel> includedChannels, Dictionary<ulong, int> serverIndices) {
var channels = new Dictionary<string, object>(); var channels = new Dictionary<string, dynamic>();
foreach (var channel in includedChannels) { foreach (var channel in includedChannels) {
var obj = new Dictionary<string, object> { dynamic obj = new ExpandoObject();
["server"] = serverIndices[channel.Server], obj.server = serverIndices[channel.Server];
["name"] = channel.Name obj.name = channel.Name;
};
if (channel.ParentId != null) { if (channel.ParentId != null) {
obj["parent"] = channel.ParentId; obj.parent = channel.ParentId;
} }
if (channel.Position != null) { if (channel.Position != null) {
obj["position"] = channel.Position; obj.position = channel.Position;
} }
if (channel.Topic != null) { if (channel.Topic != null) {
obj["topic"] = channel.Topic; obj.topic = channel.Topic;
} }
if (channel.Nsfw != null) { if (channel.Nsfw != null) {
obj["nsfw"] = channel.Nsfw; obj.nsfw = channel.Nsfw;
} }
channels[channel.Id.ToString()] = obj; channels[channel.Id.ToString()] = obj;
@ -130,55 +121,54 @@ namespace DHT.Server.Database.Export {
return channels; return channels;
} }
private static object GenerateMessageList(List<Message> includedMessages, Dictionary<ulong, object> userIndices) { private static dynamic GenerateMessageList(List<Message> includedMessages, Dictionary<ulong, int> userIndices) {
var data = new Dictionary<string, Dictionary<string, object>>(); var data = new Dictionary<string, Dictionary<string, dynamic>>();
foreach (var grouping in includedMessages.GroupBy(static message => message.Channel)) { foreach (var grouping in includedMessages.GroupBy(static message => message.Channel)) {
var channel = grouping.Key.ToString(); var channel = grouping.Key.ToString();
var channelData = new Dictionary<string, object>(); var channelData = new Dictionary<string, dynamic>();
foreach (var message in grouping) { foreach (var message in grouping) {
var obj = new Dictionary<string, object> { dynamic obj = new ExpandoObject();
["u"] = userIndices[message.Sender], obj.u = userIndices[message.Sender];
["t"] = message.Timestamp obj.t = message.Timestamp;
};
if (!string.IsNullOrEmpty(message.Text)) { if (!string.IsNullOrEmpty(message.Text)) {
obj["m"] = message.Text; obj.m = message.Text;
} }
if (message.EditTimestamp != null) { if (message.EditTimestamp != null) {
obj["te"] = message.EditTimestamp; obj.te = message.EditTimestamp;
} }
if (message.RepliedToId != null) { if (message.RepliedToId != null) {
obj["r"] = message.RepliedToId.Value; obj.r = message.RepliedToId.Value;
} }
if (!message.Attachments.IsEmpty) { if (!message.Attachments.IsEmpty) {
obj["a"] = message.Attachments.Select(static attachment => new { obj.a = message.Attachments.Select(static attachment => new {
url = attachment.Url url = attachment.Url
}).ToArray(); }).ToArray();
} }
if (!message.Embeds.IsEmpty) { if (!message.Embeds.IsEmpty) {
obj["e"] = message.Embeds.Select(static embed => embed.Json).ToArray(); obj.e = message.Embeds.Select(static embed => embed.Json).ToArray();
} }
if (!message.Reactions.IsEmpty) { if (!message.Reactions.IsEmpty) {
obj["re"] = message.Reactions.Select(static reaction => { obj.re = message.Reactions.Select(static reaction => {
var r = new Dictionary<string, object>(); dynamic r = new ExpandoObject();
if (reaction.EmojiId != null) { if (reaction.EmojiId != null) {
r["id"] = reaction.EmojiId.Value; r.id = reaction.EmojiId.Value;
} }
if (reaction.EmojiName != null) { if (reaction.EmojiName != null) {
r["n"] = reaction.EmojiName; r.n = reaction.EmojiName;
} }
r["a"] = reaction.EmojiFlags.HasFlag(EmojiFlags.Animated); r.a = reaction.EmojiFlags.HasFlag(EmojiFlags.Animated);
r["c"] = reaction.Count; r.c = reaction.Count;
return r; return r;
}); });
} }

View File

@ -26,7 +26,7 @@ namespace DHT.Server.Database.Sqlite {
public async Task<bool> Setup(Func<Task<bool>> checkCanUpgradeSchemas) { public async Task<bool> Setup(Func<Task<bool>> checkCanUpgradeSchemas) {
Execute(@"CREATE TABLE IF NOT EXISTS metadata (key TEXT PRIMARY KEY, value TEXT)"); Execute(@"CREATE TABLE IF NOT EXISTS metadata (key TEXT PRIMARY KEY, value TEXT)");
var dbVersionStr = conn.SelectScalar("SELECT value FROM metadata WHERE key = 'version'"); var dbVersionStr = Sql("SELECT value FROM metadata WHERE key = 'version'").ExecuteScalar();
if (dbVersionStr == null) { if (dbVersionStr == null) {
InitializeSchemas(); InitializeSchemas();
} }

View File

@ -43,14 +43,13 @@ namespace DHT.Server.Database.Sqlite {
public void AddServer(Data.Server server) { public void AddServer(Data.Server server) {
using var cmd = conn.Upsert("servers", new[] { using var cmd = conn.Upsert("servers", new[] {
("id", SqliteType.Integer), "id", "name", "type"
("name", SqliteType.Text),
("type", SqliteType.Text)
}); });
cmd.Set(":id", server.Id); var serverParams = cmd.Parameters;
cmd.Set(":name", server.Name); serverParams.AddAndSet(":id", server.Id);
cmd.Set(":type", ServerTypes.ToString(server.Type)); serverParams.AddAndSet(":name", server.Name);
serverParams.AddAndSet(":type", ServerTypes.ToString(server.Type));
cmd.ExecuteNonQuery(); cmd.ExecuteNonQuery();
UpdateServerStatistics(); UpdateServerStatistics();
} }
@ -63,7 +62,7 @@ namespace DHT.Server.Database.Sqlite {
while (reader.Read()) { while (reader.Read()) {
list.Add(new Data.Server { list.Add(new Data.Server {
Id = reader.GetUint64(0), Id = (ulong) reader.GetInt64(0),
Name = reader.GetString(1), Name = reader.GetString(1),
Type = ServerTypes.FromString(reader.GetString(2)) Type = ServerTypes.FromString(reader.GetString(2))
}); });
@ -74,22 +73,17 @@ namespace DHT.Server.Database.Sqlite {
public void AddChannel(Channel channel) { public void AddChannel(Channel channel) {
using var cmd = conn.Upsert("channels", new[] { using var cmd = conn.Upsert("channels", new[] {
("id", SqliteType.Integer), "id", "server", "name", "parent_id", "position", "topic", "nsfw"
("server", SqliteType.Integer),
("name", SqliteType.Text),
("parent_id", SqliteType.Integer),
("position", SqliteType.Integer),
("topic", SqliteType.Text),
("nsfw", SqliteType.Integer)
}); });
cmd.Set(":id", channel.Id); var channelParams = cmd.Parameters;
cmd.Set(":server", channel.Server); channelParams.AddAndSet(":id", channel.Id);
cmd.Set(":name", channel.Name); channelParams.AddAndSet(":server", channel.Server);
cmd.Set(":parent_id", channel.ParentId); channelParams.AddAndSet(":name", channel.Name);
cmd.Set(":position", channel.Position); channelParams.AddAndSet(":parent_id", channel.ParentId);
cmd.Set(":topic", channel.Topic); channelParams.AddAndSet(":position", channel.Position);
cmd.Set(":nsfw", channel.Nsfw); channelParams.AddAndSet(":topic", channel.Topic);
channelParams.AddAndSet(":nsfw", channel.Nsfw);
cmd.ExecuteNonQuery(); cmd.ExecuteNonQuery();
UpdateChannelStatistics(); UpdateChannelStatistics();
} }
@ -102,10 +96,10 @@ namespace DHT.Server.Database.Sqlite {
while (reader.Read()) { while (reader.Read()) {
list.Add(new Channel { list.Add(new Channel {
Id = reader.GetUint64(0), Id = (ulong) reader.GetInt64(0),
Server = reader.GetUint64(1), Server = (ulong) reader.GetInt64(1),
Name = reader.GetString(2), Name = reader.GetString(2),
ParentId = reader.IsDBNull(3) ? null : reader.GetUint64(3), ParentId = reader.IsDBNull(3) ? null : (ulong) reader.GetInt64(3),
Position = reader.IsDBNull(4) ? null : reader.GetInt32(4), Position = reader.IsDBNull(4) ? null : reader.GetInt32(4),
Topic = reader.IsDBNull(5) ? null : reader.GetString(5), Topic = reader.IsDBNull(5) ? null : reader.GetString(5),
Nsfw = reader.IsDBNull(6) ? null : reader.GetBoolean(6) Nsfw = reader.IsDBNull(6) ? null : reader.GetBoolean(6)
@ -118,17 +112,20 @@ namespace DHT.Server.Database.Sqlite {
public void AddUsers(User[] users) { public void AddUsers(User[] users) {
using var tx = conn.BeginTransaction(); using var tx = conn.BeginTransaction();
using var cmd = conn.Upsert("users", new[] { using var cmd = conn.Upsert("users", new[] {
("id", SqliteType.Integer), "id", "name", "avatar_url", "discriminator"
("name", SqliteType.Text),
("avatar_url", SqliteType.Text),
("discriminator", SqliteType.Text)
}); });
var userParams = cmd.Parameters;
userParams.Add(":id", SqliteType.Integer);
userParams.Add(":name", SqliteType.Text);
userParams.Add(":avatar_url", SqliteType.Text);
userParams.Add(":discriminator", SqliteType.Text);
foreach (var user in users) { foreach (var user in users) {
cmd.Set(":id", user.Id); userParams.Set(":id", user.Id);
cmd.Set(":name", user.Name); userParams.Set(":name", user.Name);
cmd.Set(":avatar_url", user.AvatarUrl); userParams.Set(":avatar_url", user.AvatarUrl);
cmd.Set(":discriminator", user.Discriminator); userParams.Set(":discriminator", user.Discriminator);
cmd.ExecuteNonQuery(); cmd.ExecuteNonQuery();
} }
@ -144,7 +141,7 @@ namespace DHT.Server.Database.Sqlite {
while (reader.Read()) { while (reader.Read()) {
list.Add(new User { list.Add(new User {
Id = reader.GetUint64(0), Id = (ulong) reader.GetInt64(0),
Name = reader.GetString(1), Name = reader.GetString(1),
AvatarUrl = reader.IsDBNull(2) ? null : reader.GetString(2), AvatarUrl = reader.IsDBNull(2) ? null : reader.GetString(2),
Discriminator = reader.IsDBNull(3) ? null : reader.GetString(3) Discriminator = reader.IsDBNull(3) ? null : reader.GetString(3)
@ -156,91 +153,110 @@ namespace DHT.Server.Database.Sqlite {
public void AddMessages(Message[] messages) { public void AddMessages(Message[] messages) {
using var tx = conn.BeginTransaction(); using var tx = conn.BeginTransaction();
using var messageCmd = conn.Upsert("messages", new[] { using var messageCmd = conn.Upsert("messages", new[] {
("message_id", SqliteType.Integer), "message_id", "sender_id", "channel_id", "text", "timestamp", "edit_timestamp", "replied_to_id"
("sender_id", SqliteType.Integer),
("channel_id", SqliteType.Integer),
("text", SqliteType.Text),
("timestamp", SqliteType.Integer),
("edit_timestamp", SqliteType.Integer),
("replied_to_id", SqliteType.Integer)
}); });
using var deleteAttachmentsCmd = conn.Delete("attachments", ("message_id", SqliteType.Integer)); using var deleteAttachmentsCmd = conn.Command("DELETE FROM attachments WHERE message_id = :message_id");
using var deleteEmbedsCmd = conn.Delete("embeds", ("message_id", SqliteType.Integer));
using var deleteReactionsCmd = conn.Delete("reactions", ("message_id", SqliteType.Integer));
using var attachmentCmd = conn.Insert("attachments", new[] { using var attachmentCmd = conn.Insert("attachments", new[] {
("message_id", SqliteType.Integer), "message_id", "attachment_id", "name", "type", "url", "size"
("attachment_id", SqliteType.Integer),
("name", SqliteType.Text),
("type", SqliteType.Text),
("url", SqliteType.Text),
("size", SqliteType.Integer)
}); });
using var deleteEmbedsCmd = conn.Command("DELETE FROM embeds WHERE message_id = :message_id");
using var embedCmd = conn.Insert("embeds", new[] { using var embedCmd = conn.Insert("embeds", new[] {
("message_id", SqliteType.Integer), "message_id", "json"
("json", SqliteType.Text)
}); });
using var deleteReactionsCmd = conn.Command("DELETE FROM reactions WHERE message_id = :message_id");
using var reactionCmd = conn.Insert("reactions", new[] { using var reactionCmd = conn.Insert("reactions", new[] {
("message_id", SqliteType.Integer), "message_id", "emoji_id", "emoji_name", "emoji_flags", "count"
("emoji_id", SqliteType.Integer),
("emoji_name", SqliteType.Text),
("emoji_flags", SqliteType.Integer),
("count", SqliteType.Integer)
}); });
var messageParams = messageCmd.Parameters;
messageParams.Add(":message_id", SqliteType.Integer);
messageParams.Add(":sender_id", SqliteType.Integer);
messageParams.Add(":channel_id", SqliteType.Integer);
messageParams.Add(":text", SqliteType.Text);
messageParams.Add(":timestamp", SqliteType.Integer);
messageParams.Add(":edit_timestamp", SqliteType.Integer);
messageParams.Add(":replied_to_id", SqliteType.Integer);
var deleteAttachmentsParams = deleteAttachmentsCmd.Parameters;
deleteAttachmentsParams.Add(":message_id", SqliteType.Integer);
var attachmentParams = attachmentCmd.Parameters;
attachmentParams.Add(":message_id", SqliteType.Integer);
attachmentParams.Add(":attachment_id", SqliteType.Integer);
attachmentParams.Add(":name", SqliteType.Text);
attachmentParams.Add(":type", SqliteType.Text);
attachmentParams.Add(":url", SqliteType.Text);
attachmentParams.Add(":size", SqliteType.Integer);
var deleteEmbedsParams = deleteEmbedsCmd.Parameters;
deleteEmbedsParams.Add(":message_id", SqliteType.Integer);
var embedParams = embedCmd.Parameters;
embedParams.Add(":message_id", SqliteType.Integer);
embedParams.Add(":json", SqliteType.Text);
var deleteReactionsParams = deleteReactionsCmd.Parameters;
deleteReactionsParams.Add(":message_id", SqliteType.Integer);
var reactionParams = reactionCmd.Parameters;
reactionParams.Add(":message_id", SqliteType.Integer);
reactionParams.Add(":emoji_id", SqliteType.Integer);
reactionParams.Add(":emoji_name", SqliteType.Text);
reactionParams.Add(":emoji_flags", SqliteType.Integer);
reactionParams.Add(":count", SqliteType.Integer);
foreach (var message in messages) { foreach (var message in messages) {
object messageId = message.Id; object messageId = message.Id;
messageCmd.Set(":message_id", messageId); messageParams.Set(":message_id", messageId);
messageCmd.Set(":sender_id", message.Sender); messageParams.Set(":sender_id", message.Sender);
messageCmd.Set(":channel_id", message.Channel); messageParams.Set(":channel_id", message.Channel);
messageCmd.Set(":text", message.Text); messageParams.Set(":text", message.Text);
messageCmd.Set(":timestamp", message.Timestamp); messageParams.Set(":timestamp", message.Timestamp);
messageCmd.Set(":edit_timestamp", message.EditTimestamp); messageParams.Set(":edit_timestamp", message.EditTimestamp);
messageCmd.Set(":replied_to_id", message.RepliedToId); messageParams.Set(":replied_to_id", message.RepliedToId);
messageCmd.ExecuteNonQuery(); messageCmd.ExecuteNonQuery();
deleteAttachmentsCmd.Set(":message_id", messageId); deleteAttachmentsParams.Set(":message_id", messageId);
deleteAttachmentsCmd.ExecuteNonQuery(); deleteAttachmentsCmd.ExecuteNonQuery();
deleteEmbedsCmd.Set(":message_id", messageId); deleteEmbedsParams.Set(":message_id", messageId);
deleteEmbedsCmd.ExecuteNonQuery(); deleteEmbedsCmd.ExecuteNonQuery();
deleteReactionsCmd.Set(":message_id", messageId); deleteReactionsParams.Set(":message_id", messageId);
deleteReactionsCmd.ExecuteNonQuery(); deleteReactionsCmd.ExecuteNonQuery();
if (!message.Attachments.IsEmpty) { if (!message.Attachments.IsEmpty) {
foreach (var attachment in message.Attachments) { foreach (var attachment in message.Attachments) {
attachmentCmd.Set(":message_id", messageId); attachmentParams.Set(":message_id", messageId);
attachmentCmd.Set(":attachment_id", attachment.Id); attachmentParams.Set(":attachment_id", attachment.Id);
attachmentCmd.Set(":name", attachment.Name); attachmentParams.Set(":name", attachment.Name);
attachmentCmd.Set(":type", attachment.Type); attachmentParams.Set(":type", attachment.Type);
attachmentCmd.Set(":url", attachment.Url); attachmentParams.Set(":url", attachment.Url);
attachmentCmd.Set(":size", attachment.Size); attachmentParams.Set(":size", attachment.Size);
attachmentCmd.ExecuteNonQuery(); attachmentCmd.ExecuteNonQuery();
} }
} }
if (!message.Embeds.IsEmpty) { if (!message.Embeds.IsEmpty) {
foreach (var embed in message.Embeds) { foreach (var embed in message.Embeds) {
embedCmd.Set(":message_id", messageId); embedParams.Set(":message_id", messageId);
embedCmd.Set(":json", embed.Json); embedParams.Set(":json", embed.Json);
embedCmd.ExecuteNonQuery(); embedCmd.ExecuteNonQuery();
} }
} }
if (!message.Reactions.IsEmpty) { if (!message.Reactions.IsEmpty) {
foreach (var reaction in message.Reactions) { foreach (var reaction in message.Reactions) {
reactionCmd.Set(":message_id", messageId); reactionParams.Set(":message_id", messageId);
reactionCmd.Set(":emoji_id", reaction.EmojiId); reactionParams.Set(":emoji_id", reaction.EmojiId);
reactionCmd.Set(":emoji_name", reaction.EmojiName); reactionParams.Set(":emoji_name", reaction.EmojiName);
reactionCmd.Set(":emoji_flags", (int) reaction.EmojiFlags); reactionParams.Set(":emoji_flags", (int) reaction.EmojiFlags);
reactionCmd.Set(":count", reaction.Count); reactionParams.Set(":count", reaction.Count);
reactionCmd.ExecuteNonQuery(); reactionCmd.ExecuteNonQuery();
} }
} }
@ -268,16 +284,16 @@ namespace DHT.Server.Database.Sqlite {
using var reader = cmd.ExecuteReader(); using var reader = cmd.ExecuteReader();
while (reader.Read()) { while (reader.Read()) {
ulong id = reader.GetUint64(0); ulong id = (ulong) reader.GetInt64(0);
list.Add(new Message { list.Add(new Message {
Id = id, Id = id,
Sender = reader.GetUint64(1), Sender = (ulong) reader.GetInt64(1),
Channel = reader.GetUint64(2), Channel = (ulong) reader.GetInt64(2),
Text = reader.GetString(3), Text = reader.GetString(3),
Timestamp = reader.GetInt64(4), Timestamp = reader.GetInt64(4),
EditTimestamp = reader.IsDBNull(5) ? null : reader.GetInt64(5), EditTimestamp = reader.IsDBNull(5) ? null : reader.GetInt64(5),
RepliedToId = reader.IsDBNull(6) ? null : reader.GetUint64(6), RepliedToId = reader.IsDBNull(6) ? null : (ulong) reader.GetInt64(6),
Attachments = attachments.GetListOrNull(id)?.ToImmutableArray() ?? ImmutableArray<Attachment>.Empty, Attachments = attachments.GetListOrNull(id)?.ToImmutableArray() ?? ImmutableArray<Attachment>.Empty,
Embeds = embeds.GetListOrNull(id)?.ToImmutableArray() ?? ImmutableArray<Embed>.Empty, Embeds = embeds.GetListOrNull(id)?.ToImmutableArray() ?? ImmutableArray<Embed>.Empty,
Reactions = reactions.GetListOrNull(id)?.ToImmutableArray() ?? ImmutableArray<Reaction>.Empty Reactions = reactions.GetListOrNull(id)?.ToImmutableArray() ?? ImmutableArray<Reaction>.Empty
@ -312,14 +328,14 @@ namespace DHT.Server.Database.Sqlite {
using var reader = cmd.ExecuteReader(); using var reader = cmd.ExecuteReader();
while (reader.Read()) { while (reader.Read()) {
ulong messageId = reader.GetUint64(0); ulong messageId = (ulong) reader.GetInt64(0);
dict.Add(messageId, new Attachment { dict.Add(messageId, new Attachment {
Id = reader.GetUint64(1), Id = (ulong) reader.GetInt64(1),
Name = reader.GetString(2), Name = reader.GetString(2),
Type = reader.IsDBNull(3) ? null : reader.GetString(3), Type = reader.IsDBNull(3) ? null : reader.GetString(3),
Url = reader.GetString(4), Url = reader.GetString(4),
Size = reader.GetUint64(5) Size = (ulong) reader.GetInt64(5)
}); });
} }
@ -333,7 +349,7 @@ namespace DHT.Server.Database.Sqlite {
using var reader = cmd.ExecuteReader(); using var reader = cmd.ExecuteReader();
while (reader.Read()) { while (reader.Read()) {
ulong messageId = reader.GetUint64(0); ulong messageId = (ulong) reader.GetInt64(0);
dict.Add(messageId, new Embed { dict.Add(messageId, new Embed {
Json = reader.GetString(1) Json = reader.GetString(1)
@ -350,10 +366,10 @@ namespace DHT.Server.Database.Sqlite {
using var reader = cmd.ExecuteReader(); using var reader = cmd.ExecuteReader();
while (reader.Read()) { while (reader.Read()) {
ulong messageId = reader.GetUint64(0); ulong messageId = (ulong) reader.GetInt64(0);
dict.Add(messageId, new Reaction { dict.Add(messageId, new Reaction {
EmojiId = reader.IsDBNull(1) ? null : reader.GetUint64(1), EmojiId = reader.IsDBNull(1) ? null : (ulong) reader.GetInt64(1),
EmojiName = reader.IsDBNull(2) ? null : reader.GetString(2), EmojiName = reader.IsDBNull(2) ? null : reader.GetString(2),
EmojiFlags = (EmojiFlags) reader.GetInt16(3), EmojiFlags = (EmojiFlags) reader.GetInt16(3),
Count = reader.GetInt32(4) Count = reader.GetInt32(4)
@ -364,19 +380,23 @@ namespace DHT.Server.Database.Sqlite {
} }
private void UpdateServerStatistics() { private void UpdateServerStatistics() {
Statistics.TotalServers = conn.SelectScalar("SELECT COUNT(*) FROM servers") as long? ?? 0; using var cmd = conn.Command("SELECT COUNT(*) FROM servers");
Statistics.TotalServers = cmd.ExecuteScalar() as long? ?? 0;
} }
private void UpdateChannelStatistics() { private void UpdateChannelStatistics() {
Statistics.TotalChannels = conn.SelectScalar("SELECT COUNT(*) FROM channels") as long? ?? 0; using var cmd = conn.Command("SELECT COUNT(*) FROM channels");
Statistics.TotalChannels = cmd.ExecuteScalar() as long? ?? 0;
} }
private void UpdateUserStatistics() { private void UpdateUserStatistics() {
Statistics.TotalUsers = conn.SelectScalar("SELECT COUNT(*) FROM users") as long? ?? 0; using var cmd = conn.Command("SELECT COUNT(*) FROM users");
Statistics.TotalUsers = cmd.ExecuteScalar() as long? ?? 0;
} }
private void UpdateMessageStatistics() { private void UpdateMessageStatistics() {
Statistics.TotalMessages = conn.SelectScalar("SELECT COUNT(*) FROM messages") as long? ?? 0L; using var cmd = conn.Command("SELECT COUNT(*) FROM messages");
Statistics.TotalMessages = cmd.ExecuteScalar() as long? ?? 0L;
} }
} }
} }

View File

@ -10,54 +10,31 @@ namespace DHT.Server.Database.Sqlite {
return cmd; return cmd;
} }
public static object? SelectScalar(this SqliteConnection conn, string sql) { public static SqliteCommand Insert(this SqliteConnection conn, string tableName, string[] columns) {
using var cmd = conn.Command(sql); string columnNames = string.Join(',', columns);
return cmd.ExecuteScalar(); string columnParams = string.Join(',', columns.Select(static c => ':' + c));
return conn.Command("INSERT INTO " + tableName + " (" + columnNames + ")" +
"VALUES (" + columnParams + ")");
} }
public static SqliteCommand Insert(this SqliteConnection conn, string tableName, (string Name, SqliteType Type)[] columns) { public static SqliteCommand Upsert(this SqliteConnection conn, string tableName, string[] columns) {
string columnNames = string.Join(',', columns.Select(static c => c.Name)); string columnNames = string.Join(',', columns);
string columnParams = string.Join(',', columns.Select(static c => ':' + c.Name)); string columnParams = string.Join(',', columns.Select(static c => ':' + c));
string columnUpdates = string.Join(',', columns.Skip(1).Select(static c => c + " = excluded." + c));
var cmd = conn.Command("INSERT INTO " + tableName + " (" + columnNames + ")" + return conn.Command("INSERT INTO " + tableName + " (" + columnNames + ")" +
"VALUES (" + columnParams + ")"); "VALUES (" + columnParams + ")" +
"ON CONFLICT (" + columns[0] + ")" +
CreateParameters(cmd, columns); "DO UPDATE SET " + columnUpdates);
return cmd;
} }
public static SqliteCommand Upsert(this SqliteConnection conn, string tableName, (string Name, SqliteType Type)[] columns) { public static void AddAndSet(this SqliteParameterCollection parameters, string key, object? value) {
string columnNames = string.Join(',', columns.Select(static c => c.Name)); parameters.AddWithValue(key, value ?? DBNull.Value);
string columnParams = string.Join(',', columns.Select(static c => ':' + c.Name));
string columnUpdates = string.Join(',', columns.Skip(1).Select(static c => c.Name + " = excluded." + c.Name));
var cmd = conn.Command("INSERT INTO " + tableName + " (" + columnNames + ")" +
"VALUES (" + columnParams + ")" +
"ON CONFLICT (" + columns[0].Name + ")" +
"DO UPDATE SET " + columnUpdates);
CreateParameters(cmd, columns);
return cmd;
} }
public static SqliteCommand Delete(this SqliteConnection conn, string tableName, (string Name, SqliteType Type) column) { public static void Set(this SqliteParameterCollection parameters, string key, object? value) {
var cmd = conn.Command("DELETE FROM " + tableName + " WHERE " + column.Name + " = :" + column.Name); parameters[key].Value = value ?? DBNull.Value;
CreateParameters(cmd, new [] { column });
return cmd;
}
private static void CreateParameters(SqliteCommand cmd, (string Name, SqliteType Type)[] columns) {
foreach (var (name, type) in columns) {
cmd.Parameters.Add(":" + name, type);
}
}
public static void Set(this SqliteCommand cmd, string key, object? value) {
cmd.Parameters[key].Value = value ?? DBNull.Value;
}
public static ulong GetUint64(this SqliteDataReader reader, int ordinal) {
return (ulong) reader.GetInt64(ordinal);
} }
} }
} }

View File

@ -19,7 +19,7 @@
<FrameworkReference Include="Microsoft.AspNetCore.App" /> <FrameworkReference Include="Microsoft.AspNetCore.App" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.Data.Sqlite" Version="6.0.2" /> <PackageReference Include="Microsoft.Data.Sqlite" Version="5.0.5" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Utils\Utils.csproj" /> <ProjectReference Include="..\Utils\Utils.csproj" />

View File

@ -1,18 +1,8 @@
using System; using System;
using System.Diagnostics; using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Text;
namespace DHT.Utils.Logging { namespace DHT.Utils.Logging {
public sealed class Log { public sealed class Log {
public static bool IsDebugEnabled { get; set; }
static Log() {
#if DEBUG
IsDebugEnabled = true;
#endif
}
public static Log ForType<T>() { public static Log ForType<T>() {
return ForType(typeof(T)); return ForType(typeof(T));
} }
@ -21,54 +11,19 @@ namespace DHT.Utils.Logging {
return new Log(type.Name); return new Log(type.Name);
} }
public static Log ForType<T>(string context) {
return ForType(typeof(T), context);
}
public static Log ForType(Type type, string context) {
return new Log(type.Name, context);
}
private readonly string tag; private readonly string tag;
private readonly string? context;
private Log(string tag, string? context = null) { private Log(string tag) {
this.tag = tag; this.tag = tag;
this.context = context;
}
private void FormatTags(StringBuilder builder) {
builder.Append('[').Append(tag).Append("] ");
if (context != null) {
builder.Append('[').Append(context).Append("] ");
}
} }
private void LogLevel(ConsoleColor color, string level, string text) { private void LogLevel(ConsoleColor color, string level, string text) {
ConsoleColor prevColor = Console.ForegroundColor;
Console.ForegroundColor = color; Console.ForegroundColor = color;
StringBuilder builder = new StringBuilder();
foreach (string line in text.Replace("\r", "").Split('\n')) { foreach (string line in text.Replace("\r", "").Split('\n')) {
builder.Clear(); string formatted = $"[{level}] [{tag}] {line}";
builder.Append('[').Append(level).Append("] ");
FormatTags(builder);
builder.Append(line);
string formatted = builder.ToString();
Console.WriteLine(formatted); Console.WriteLine(formatted);
Trace.WriteLine(formatted); Trace.WriteLine(formatted);
} }
Console.ForegroundColor = prevColor;
}
public void Debug(string message) {
if (IsDebugEnabled) {
LogLevel(ConsoleColor.Gray, "DEBUG", message);
}
} }
public void Info(string message) { public void Info(string message) {
@ -86,9 +41,5 @@ namespace DHT.Utils.Logging {
public void Error(Exception e) { public void Error(Exception e) {
LogLevel(ConsoleColor.Red, "ERROR", e.ToString()); LogLevel(ConsoleColor.Red, "ERROR", e.ToString());
} }
public Perf Start([CallerMemberName] string callerMemberName = "") {
return Perf.Start(this, callerMemberName);
}
} }
} }

View File

@ -1,29 +0,0 @@
using System.Diagnostics;
using System.Runtime.CompilerServices;
namespace DHT.Utils.Logging {
public sealed class Perf {
internal static Perf Start(Log log, [CallerMemberName] string callerMemberName = "") {
return new Perf(log, callerMemberName);
}
private readonly Log log;
private readonly string method;
private readonly Stopwatch stopwatch;
private Perf(Log log, string method) {
this.log = log;
this.method = method;
this.stopwatch = new Stopwatch();
this.stopwatch.Start();
}
public void End() {
stopwatch.Stop();
if (Log.IsDebugEnabled) {
log.Debug($"Finished '{method}' in {stopwatch.ElapsedMilliseconds} ms.");
}
}
}
}