diff --git a/ConfigItem/DatabaseConfig.cs b/ConfigItem/DatabaseConfig.cs index 7f858d6..3bea3cf 100644 --- a/ConfigItem/DatabaseConfig.cs +++ b/ConfigItem/DatabaseConfig.cs @@ -47,7 +47,7 @@ namespace Noikoio.RegexBot.ConfigItem _enabled = true; } - public async Task OpenConnectionAsync(ulong? guildId) + private async Task P_OpenConnectionAsync(ulong? guildId = null) { if (!Enabled) return null; @@ -64,5 +64,24 @@ namespace Noikoio.RegexBot.ConfigItem await db.OpenAsync(); return db; } + public Task OpenConnectionAsync(ulong guildId) => P_OpenConnectionAsync(guildId); + + + public async Task CreateGuildSchemaAsync(ulong gid) + { + if (!Enabled) return; + + const string cs = "CREATE SCHEMA IF NOT EXISTS {0}"; + + string sn = "g_" + gid.ToString(); + using (var db = await P_OpenConnectionAsync()) + { + using (var c = db.CreateCommand()) + { + c.CommandText = string.Format(cs, sn); + await c.ExecuteNonQueryAsync(); + } + } + } } } diff --git a/Feature/DBCache/DBCache.cs b/Feature/DBCache/DBCache.cs index ec3dd94..b882f98 100644 --- a/Feature/DBCache/DBCache.cs +++ b/Feature/DBCache/DBCache.cs @@ -1,9 +1,9 @@ -using System; -using System.Collections.Generic; -using System.Text; -using System.Threading.Tasks; -using Discord.WebSocket; +using Discord.WebSocket; using Newtonsoft.Json.Linq; +using Noikoio.RegexBot.ConfigItem; +using System; +using System.Collections.Generic; +using System.Threading.Tasks; namespace Noikoio.RegexBot.Feature.DBCache { @@ -13,62 +13,159 @@ namespace Noikoio.RegexBot.Feature.DBCache /// class DBCache : BotFeature { + private readonly DatabaseConfig _db; + public override string Name => "Database cache"; public DBCache(DiscordSocketClient client) : base(client) { + _db = RegexBot.Config.Database; + client.GuildAvailable += Client_GuildAvailable; client.GuildUpdated += Client_GuildUpdated; client.GuildMemberUpdated += Client_GuildMemberUpdated; - + // it may not be necessary to handle JoinedGuild, as GuildAvailable still provides info client.MessageReceived += Client_MessageReceived; - client.MessageUpdated += Client_MessageUpdated; + //client.MessageUpdated += Client_MessageUpdated; } - + public override Task ProcessConfiguration(JToken configSection) => Task.FromResult(null); - #region Guild and user cache - /* Note: - * We save information per guild in their own schemas named "g_NUM", where NUM is the Guild ID. - * - * The creation of these schemas is handled within here, but we're possibly facing a short delay - * in the event that other events that we're listening for come in without a schema having been - * created yet in which to put them in. - * Got to figure that out. - */ + #region Event handling + // Guild _and_ guild member information has become available + private async Task Client_GuildAvailable(SocketGuild arg) + { + + if (!_db.Enabled) return; + await CreateCacheTables(arg.Id); + + await Task.Run(() => UpdateGuild(arg)); + await Task.Run(() => UpdateGuildMember(arg.Id, arg.Users)); + } // Guild information has changed - private Task Client_GuildUpdated(SocketGuild arg1, SocketGuild arg2) + private async Task Client_GuildUpdated(SocketGuild arg1, SocketGuild arg2) + { + if (!_db.Enabled) return; + throw new NotImplementedException(); + } + + // Guild member information has changed + private async Task Client_GuildMemberUpdated(SocketGuildUser arg1, SocketGuildUser arg2) + { + if (!_db.Enabled) return; + await Task.Run(() => UpdateGuildMember(arg2)); + } + + // A new message has been created + private async Task Client_MessageReceived(SocketMessage arg) + { + if (!_db.Enabled) return; + throw new NotImplementedException(); + } + + + //private Task Client_MessageUpdated(Discord.Cacheable arg1, SocketMessage arg2, ISocketMessageChannel arg3) + /* + * Edited messages seem to retain their ID. This is a problem. + * The point of this message cache was to have another feature be able to relay + * both the previous and current message at once. + * For now: Do nothing on updated messages. + */ + #endregion + + #region Table setup + const string TableGuild = "cache_guild"; + const string TableUser = "cache_users"; + const string TableMessage = "cache_messages"; + + private async Task CreateCacheTables(ulong gid) + { + /* Note: + * We save information per guild in their own schemas named "g_NUM", where NUM is the Guild ID. + * + * The creation of these schemas is handled within here, but we're possibly facing a short delay + * in the event that other events that we're listening for come in without a schema having been + * created yet in which to put them in. + * Got to figure that out. + */ + await _db.CreateGuildSchemaAsync(gid); + + using (var db = await _db.OpenConnectionAsync(gid)) + { + Task c1, c2, c3; + + // Uh... didn't think this through. For now this is a table that'll only ever have one column. + // Got to rethink this in particular. + using (var c = db.CreateCommand()) + { + c.CommandText = "CREATE TABLE IF NOT EXISTS " + TableGuild + "(" + + "snowflake bigint primary key, " + + "current_name text not null, " + + "display_name text null" + + ")"; + c1 = c.ExecuteNonQueryAsync(); + } + + using (var c = db.CreateCommand()) + { + c.CommandText = "CREATE TABLE IF NOT EXISTS " + TableUser + "(" + + "snowflake bigint primary key, " + + "cache_date timestamptz not null, " + + "username text not null, " + + "discriminator text not null, " + + "nickname text null, " + + "avatar_url text null" + + ")"; + c2 = c.ExecuteNonQueryAsync(); + } + + using (var c = db.CreateCommand()) + { + c.CommandText = "CREATE TABLE IF NOT EXISTS " + TableMessage + "(" + + "snowflake bigint primary key, " + + "cache_date timestamptz not null, " + + "author bigint not null" + + ")"; + c3 = c.ExecuteNonQueryAsync(); + } + + await c1; + await c2; + await c3; + } + } + #endregion + + #region Guild and user cache updates + private async Task UpdateGuild(SocketGuild g) { throw new NotImplementedException(); } - // Single guild member information has changed - private Task Client_GuildMemberUpdated(SocketGuildUser arg1, SocketGuildUser arg2) + private async Task UpdateGuildMember(ulong gid, IEnumerable users) { - // which is old and which is new? throw new NotImplementedException(); } - // All member data in a guild has become known - private Task Client_GuildAvailable(SocketGuild arg) + private Task UpdateGuildMember(SocketGuildUser user) { - throw new NotImplementedException(); + var gid = user.Guild.Id; + var ml = new SocketGuildUser[] { user }; + return UpdateGuildMember(gid, ml); } #endregion #region Message cache - private Task Client_MessageUpdated(Discord.Cacheable arg1, SocketMessage arg2, ISocketMessageChannel arg3) + private async Task CacheMessage(SocketMessage msg) { throw new NotImplementedException(); } - private Task Client_MessageReceived(SocketMessage arg) + private async Task UpdateMessage(SocketMessage msg) { throw new NotImplementedException(); } #endregion - - } }