Moving EntityCache files to separate directory
This commit is contained in:
parent
74c80c50e9
commit
8bb274bd69
4 changed files with 108 additions and 77 deletions
|
@ -7,37 +7,26 @@ using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace Noikoio.RegexBot.Module.EntityCache
|
namespace Noikoio.RegexBot.EntityCache
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Caches information regarding all known guilds, channels, and users.
|
/// Bot module portion of the entity cache. Caches information regarding all known guilds, channels, and users.
|
||||||
/// The function of this module should be transparent to the user, and thus no configuration is needed.
|
/// The function of this module should be transparent to the user, and thus no configuration is needed.
|
||||||
/// This module should be initialized BEFORE any other modules that make use of guild and user cache.
|
/// This module should be initialized BEFORE any other modules that make use of guild and user cache.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
class EntityCache : BotModule
|
class Module : BotModule
|
||||||
{
|
{
|
||||||
/*
|
|
||||||
* Future plans:
|
|
||||||
* Have this, or something connected to this class, be accessible throughout the bot.
|
|
||||||
*
|
|
||||||
* There should be a system that holds a small in-memory cache of users (as EntityCache objects)
|
|
||||||
* for quick lookups by other parts of the bot.
|
|
||||||
* Without this system, we'll be having future bot features constantly querying the database
|
|
||||||
* on their own to look up entity cache records, which (among other things) could result in
|
|
||||||
* a race conditions where an event becomes aware of a user before it has been recorded.
|
|
||||||
*/
|
|
||||||
|
|
||||||
private readonly DatabaseConfig _db;
|
private readonly DatabaseConfig _db;
|
||||||
|
|
||||||
public override string Name => nameof(EntityCache);
|
public override string Name => nameof(EntityCache);
|
||||||
|
|
||||||
public EntityCache(DiscordSocketClient client) : base(client)
|
public Module(DiscordSocketClient client) : base(client)
|
||||||
{
|
{
|
||||||
_db = RegexBot.Config.Database;
|
_db = RegexBot.Config.Database;
|
||||||
|
|
||||||
if (_db.Available)
|
if (_db.Available)
|
||||||
{
|
{
|
||||||
Sql.CreateCacheTables();
|
SqlHelper.CreateCacheTables();
|
||||||
|
|
||||||
client.GuildAvailable += Client_GuildAvailable;
|
client.GuildAvailable += Client_GuildAvailable;
|
||||||
client.GuildUpdated += Client_GuildUpdated;
|
client.GuildUpdated += Client_GuildUpdated;
|
||||||
|
@ -49,12 +38,12 @@ namespace Noikoio.RegexBot.Module.EntityCache
|
||||||
Log("No database storage available.").Wait();
|
Log("No database storage available.").Wait();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public override Task<object> ProcessConfiguration(JToken configSection) => Task.FromResult<object>(null);
|
public override Task<object> ProcessConfiguration(JToken configSection) => Task.FromResult<object>(null);
|
||||||
|
|
||||||
#region Event handling
|
#region Event handling
|
||||||
// Guild and guild member information has become available.
|
// Guild and guild member information has become available.
|
||||||
// This is a very expensive operation, when joining larger guilds for the first time.
|
// This is a very expensive operation, especially when joining larger guilds.
|
||||||
private async Task Client_GuildAvailable(SocketGuild arg)
|
private async Task Client_GuildAvailable(SocketGuild arg)
|
||||||
{
|
{
|
||||||
await Task.Run(async () =>
|
await Task.Run(async () =>
|
||||||
|
@ -124,7 +113,7 @@ namespace Noikoio.RegexBot.Module.EntityCache
|
||||||
+ "cache_date = EXCLUDED.cache_date, username = EXCLUDED.username, "
|
+ "cache_date = EXCLUDED.cache_date, username = EXCLUDED.username, "
|
||||||
+ "discriminator = EXCLUDED.discriminator, " // I've seen someone's discriminator change this one time...
|
+ "discriminator = EXCLUDED.discriminator, " // I've seen someone's discriminator change this one time...
|
||||||
+ "nickname = EXCLUDED.nickname, avatar_url = EXCLUDED.avatar_url";
|
+ "nickname = EXCLUDED.nickname, avatar_url = EXCLUDED.avatar_url";
|
||||||
|
|
||||||
var uid = c.Parameters.Add("@Uid", NpgsqlDbType.Bigint);
|
var uid = c.Parameters.Add("@Uid", NpgsqlDbType.Bigint);
|
||||||
var gid = c.Parameters.Add("@Gid", NpgsqlDbType.Bigint);
|
var gid = c.Parameters.Add("@Gid", NpgsqlDbType.Bigint);
|
||||||
c.Parameters.Add("@Date", NpgsqlDbType.TimestampTZ).Value = DateTime.Now;
|
c.Parameters.Add("@Date", NpgsqlDbType.TimestampTZ).Value = DateTime.Now;
|
98
EntityCache/SqlHelper.cs
Normal file
98
EntityCache/SqlHelper.cs
Normal file
|
@ -0,0 +1,98 @@
|
||||||
|
using Npgsql;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Noikoio.RegexBot.EntityCache
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Helper methods for database operations.
|
||||||
|
/// </summary>
|
||||||
|
static class SqlHelper
|
||||||
|
{
|
||||||
|
public const string TableGuild = "cache_guild";
|
||||||
|
public const string TableTextChannel = "cache_textchannel";
|
||||||
|
public const string TableUser = "cache_users";
|
||||||
|
|
||||||
|
private static async Task<NpgsqlConnection> OpenDB()
|
||||||
|
{
|
||||||
|
if (!RegexBot.Config.Database.Available) return null;
|
||||||
|
return await RegexBot.Config.Database.GetOpenConnectionAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task CreateCacheTables()
|
||||||
|
{
|
||||||
|
var db = await OpenDB();
|
||||||
|
if (db == null) return;
|
||||||
|
using (db)
|
||||||
|
{
|
||||||
|
// Guild cache
|
||||||
|
using (var c = db.CreateCommand())
|
||||||
|
{
|
||||||
|
c.CommandText = "CREATE TABLE IF NOT EXISTS " + TableGuild + " ("
|
||||||
|
+ "guild_id bigint primary key, "
|
||||||
|
+ "cache_date timestamptz not null, "
|
||||||
|
+ "current_name text not null, "
|
||||||
|
+ "display_name text null"
|
||||||
|
+ ")";
|
||||||
|
await c.ExecuteNonQueryAsync();
|
||||||
|
}
|
||||||
|
// May not require other indexes. Add here if they become necessary.
|
||||||
|
|
||||||
|
// Text channel cache
|
||||||
|
using (var c = db.CreateCommand())
|
||||||
|
{
|
||||||
|
c.CommandText = "CREATE TABLE IF NOT EXISTS " + TableTextChannel + " ("
|
||||||
|
+ "channel_id bigint not null primary key, "
|
||||||
|
+ $"guild_id bigint not null references {TableGuild}, "
|
||||||
|
+ "cache_date timestamptz not null, "
|
||||||
|
+ "name text not null";
|
||||||
|
await c.ExecuteNonQueryAsync();
|
||||||
|
}
|
||||||
|
// As of the time of this commit, Discord doesn't allow any uppercase characters
|
||||||
|
// in channel names. No lowercase name index needed.
|
||||||
|
|
||||||
|
// User cache
|
||||||
|
using (var c = db.CreateCommand())
|
||||||
|
{
|
||||||
|
c.CommandText = "CREATE TABLE IF NOT EXISTS " + TableUser + " ("
|
||||||
|
+ "user_id bigint not null, "
|
||||||
|
+ $"guild_id bigint not null references {TableGuild}, "
|
||||||
|
+ "cache_date timestamptz not null, "
|
||||||
|
+ "username text not null, "
|
||||||
|
+ "discriminator text not null, "
|
||||||
|
+ "nickname text null, "
|
||||||
|
+ "avatar_url text null"
|
||||||
|
+ ")";
|
||||||
|
await c.ExecuteNonQueryAsync();
|
||||||
|
}
|
||||||
|
using (var c = db.CreateCommand())
|
||||||
|
{
|
||||||
|
// guild_id is a foreign key, and also one half of the primary key here
|
||||||
|
c.CommandText = "CREATE UNIQUE INDEX IF NOT EXISTS "
|
||||||
|
+ $"{TableUser}_ck_idx on {TableUser} (user_id, guild_id)";
|
||||||
|
await c.ExecuteNonQueryAsync();
|
||||||
|
}
|
||||||
|
using (var c = db.CreateCommand())
|
||||||
|
{
|
||||||
|
c.CommandText = "CREATE INDEX IF NOT EXISTS "
|
||||||
|
+ $"{TableUser}_usersearch_idx on {TableUser} LOWER(username)";
|
||||||
|
await c.ExecuteNonQueryAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Insertions and updates
|
||||||
|
static async Task UpdateGuild()
|
||||||
|
{
|
||||||
|
var db = await OpenDB();
|
||||||
|
if (db == null) return;
|
||||||
|
using (db)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,56 +0,0 @@
|
||||||
using Npgsql;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace Noikoio.RegexBot.Module.EntityCache
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Contains common constants and static methods for cache access.
|
|
||||||
/// </summary>
|
|
||||||
class Sql
|
|
||||||
{
|
|
||||||
public const string TableGuild = "cache_guild";
|
|
||||||
public const string TableUser = "cache_users";
|
|
||||||
|
|
||||||
private static NpgsqlConnection OpenDB() =>
|
|
||||||
RegexBot.Config.Database.GetOpenConnectionAsync().GetAwaiter().GetResult();
|
|
||||||
|
|
||||||
public static void CreateCacheTables()
|
|
||||||
{
|
|
||||||
using (var db = OpenDB())
|
|
||||||
{
|
|
||||||
using (var c = db.CreateCommand())
|
|
||||||
{
|
|
||||||
c.CommandText = "CREATE TABLE IF NOT EXISTS " + TableGuild + "("
|
|
||||||
+ "guild_id bigint primary key, "
|
|
||||||
+ "current_name text not null, "
|
|
||||||
+ "display_name text null"
|
|
||||||
+ ")";
|
|
||||||
c.ExecuteNonQuery();
|
|
||||||
}
|
|
||||||
|
|
||||||
using (var c = db.CreateCommand())
|
|
||||||
{
|
|
||||||
c.CommandText = "CREATE TABLE IF NOT EXISTS " + TableUser + "("
|
|
||||||
+ "user_id bigint not null, "
|
|
||||||
+ $"guild_id bigint not null references {TableGuild}, "
|
|
||||||
+ "cache_date timestamptz not null, "
|
|
||||||
+ "username text not null, "
|
|
||||||
+ "discriminator text not null, "
|
|
||||||
+ "nickname text null, "
|
|
||||||
+ "avatar_url text null"
|
|
||||||
+ ")";
|
|
||||||
c.ExecuteNonQuery();
|
|
||||||
}
|
|
||||||
using (var c = db.CreateCommand())
|
|
||||||
{
|
|
||||||
c.CommandText = "CREATE UNIQUE INDEX IF NOT EXISTS "
|
|
||||||
+ $"{TableUser}_idx on {TableUser} (user_id, guild_id)";
|
|
||||||
c.ExecuteNonQuery();
|
|
||||||
}
|
|
||||||
// TODO create indexes for string-based queries
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -48,7 +48,7 @@ namespace Noikoio.RegexBot
|
||||||
new Module.AutoMod.AutoMod(_client),
|
new Module.AutoMod.AutoMod(_client),
|
||||||
new Module.ModTools.ModTools(_client),
|
new Module.ModTools.ModTools(_client),
|
||||||
new Module.AutoRespond.AutoRespond(_client),
|
new Module.AutoRespond.AutoRespond(_client),
|
||||||
new Module.EntityCache.EntityCache(_client) // EntityCache goes before anything else that uses its data
|
new EntityCache.Module(_client) // EntityCache goes before anything else that uses its data
|
||||||
};
|
};
|
||||||
var dlog = Logger.GetLogger("Discord.Net");
|
var dlog = Logger.GetLogger("Discord.Net");
|
||||||
_client.Log += async (arg) =>
|
_client.Log += async (arg) =>
|
||||||
|
|
Loading…
Reference in a new issue