2018-04-05 00:25:13 +00:00
|
|
|
|
using Discord;
|
|
|
|
|
using Discord.WebSocket;
|
2017-12-23 03:17:36 +00:00
|
|
|
|
using NpgsqlTypes;
|
2017-12-23 01:53:46 +00:00
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.Threading.Tasks;
|
|
|
|
|
|
|
|
|
|
namespace Noikoio.RegexBot.EntityCache
|
|
|
|
|
{
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Helper methods for database operations.
|
2017-12-23 03:17:36 +00:00
|
|
|
|
/// Exceptions are not handled within methods of this class.
|
2017-12-23 01:53:46 +00:00
|
|
|
|
/// </summary>
|
|
|
|
|
static class SqlHelper
|
|
|
|
|
{
|
|
|
|
|
public const string TableGuild = "cache_guild";
|
|
|
|
|
public const string TableTextChannel = "cache_textchannel";
|
|
|
|
|
public const string TableUser = "cache_users";
|
|
|
|
|
|
2017-12-23 06:20:06 +00:00
|
|
|
|
// Reminder: Check Cache query methods if making changes to tables
|
2017-12-23 03:17:36 +00:00
|
|
|
|
internal static async Task CreateCacheTablesAsync()
|
2017-12-23 01:53:46 +00:00
|
|
|
|
{
|
2018-02-20 08:58:55 +00:00
|
|
|
|
var db = await RegexBot.Config.GetOpenDatabaseConnectionAsync();
|
2017-12-23 01:53:46 +00:00
|
|
|
|
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 + " ("
|
2018-04-05 00:25:13 +00:00
|
|
|
|
+ "channel_id bigint not null, "
|
2017-12-23 01:53:46 +00:00
|
|
|
|
+ $"guild_id bigint not null references {TableGuild}, "
|
|
|
|
|
+ "cache_date timestamptz not null, "
|
2018-01-04 22:06:04 +00:00
|
|
|
|
+ "channel_name text not null"
|
|
|
|
|
+ ")";
|
2017-12-23 01:53:46 +00:00
|
|
|
|
await c.ExecuteNonQueryAsync();
|
|
|
|
|
}
|
2018-04-05 00:25:13 +00:00
|
|
|
|
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 "
|
|
|
|
|
+ $"{TableTextChannel}_ck_idx on {TableTextChannel} (channel_id, guild_id)";
|
|
|
|
|
await c.ExecuteNonQueryAsync();
|
|
|
|
|
}
|
2017-12-23 01:53:46 +00:00
|
|
|
|
// 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}, "
|
2018-04-24 21:29:19 +00:00
|
|
|
|
+ "first_seen timestamptz not null default now(),"
|
2017-12-23 01:53:46 +00:00
|
|
|
|
+ "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())
|
|
|
|
|
{
|
2018-04-05 00:25:13 +00:00
|
|
|
|
// compound primary key
|
2017-12-23 01:53:46 +00:00
|
|
|
|
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 "
|
2018-01-04 22:06:04 +00:00
|
|
|
|
+ $"{TableUser}_usersearch_idx on {TableUser} (LOWER(username))";
|
2017-12-23 01:53:46 +00:00
|
|
|
|
await c.ExecuteNonQueryAsync();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#region Insertions and updates
|
2017-12-23 03:17:36 +00:00
|
|
|
|
internal static async Task UpdateGuildAsync(SocketGuild g)
|
2017-12-23 01:53:46 +00:00
|
|
|
|
{
|
2018-02-20 08:58:55 +00:00
|
|
|
|
var db = await RegexBot.Config.GetOpenDatabaseConnectionAsync();
|
2017-12-23 01:53:46 +00:00
|
|
|
|
if (db == null) return;
|
|
|
|
|
using (db)
|
|
|
|
|
{
|
2017-12-23 03:17:36 +00:00
|
|
|
|
using (var c = db.CreateCommand())
|
|
|
|
|
{
|
2018-01-04 22:06:04 +00:00
|
|
|
|
c.CommandText = "INSERT INTO " + TableGuild + " (guild_id, cache_date, current_name) "
|
2018-04-24 21:29:19 +00:00
|
|
|
|
+ "VALUES (@GuildId, now(), @CurrentName) "
|
2017-12-23 03:17:36 +00:00
|
|
|
|
+ "ON CONFLICT (guild_id) DO UPDATE SET "
|
2018-01-04 22:06:04 +00:00
|
|
|
|
+ "current_name = EXCLUDED.current_name, cache_date = EXCLUDED.cache_date";
|
2017-12-23 03:17:36 +00:00
|
|
|
|
c.Parameters.Add("@GuildId", NpgsqlDbType.Bigint).Value = g.Id;
|
|
|
|
|
c.Parameters.Add("@CurrentName", NpgsqlDbType.Text).Value = g.Name;
|
|
|
|
|
c.Prepare();
|
|
|
|
|
await c.ExecuteNonQueryAsync();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
internal static Task UpdateGuildMemberAsync(SocketGuildUser user)
|
2018-04-05 00:25:13 +00:00
|
|
|
|
=> UpdateGuildMemberAsync(new SocketGuildUser[] { user });
|
2017-12-23 03:17:36 +00:00
|
|
|
|
internal static async Task UpdateGuildMemberAsync(IEnumerable<SocketGuildUser> users)
|
|
|
|
|
{
|
2018-02-20 08:58:55 +00:00
|
|
|
|
var db = await RegexBot.Config.GetOpenDatabaseConnectionAsync();
|
2017-12-23 03:17:36 +00:00
|
|
|
|
if (db == null) return;
|
|
|
|
|
using (db)
|
|
|
|
|
{
|
|
|
|
|
using (var c = db.CreateCommand())
|
|
|
|
|
{
|
2017-12-23 06:20:06 +00:00
|
|
|
|
c.CommandText = "INSERT INTO " + TableUser
|
2017-12-23 03:17:36 +00:00
|
|
|
|
+ " (user_id, guild_id, cache_date, username, discriminator, nickname, avatar_url)"
|
2018-04-24 21:29:19 +00:00
|
|
|
|
+ " VALUES (@Uid, @Gid, now(), @Uname, @Disc, @Nname, @Url) "
|
2017-12-23 03:17:36 +00:00
|
|
|
|
+ "ON CONFLICT (user_id, guild_id) DO UPDATE SET "
|
|
|
|
|
+ "cache_date = EXCLUDED.cache_date, username = EXCLUDED.username, "
|
|
|
|
|
+ "discriminator = EXCLUDED.discriminator, " // I've seen someone's discriminator change this one time...
|
|
|
|
|
+ "nickname = EXCLUDED.nickname, avatar_url = EXCLUDED.avatar_url";
|
|
|
|
|
|
|
|
|
|
var uid = c.Parameters.Add("@Uid", NpgsqlDbType.Bigint);
|
|
|
|
|
var gid = c.Parameters.Add("@Gid", NpgsqlDbType.Bigint);
|
|
|
|
|
var uname = c.Parameters.Add("@Uname", NpgsqlDbType.Text);
|
|
|
|
|
var disc = c.Parameters.Add("@Disc", NpgsqlDbType.Text);
|
|
|
|
|
var nname = c.Parameters.Add("@Nname", NpgsqlDbType.Text);
|
|
|
|
|
var url = c.Parameters.Add("@Url", NpgsqlDbType.Text);
|
|
|
|
|
c.Prepare();
|
|
|
|
|
|
|
|
|
|
foreach (var item in users)
|
|
|
|
|
{
|
2018-04-05 21:22:17 +00:00
|
|
|
|
if (item.IsWebhook) continue;
|
2017-12-23 01:53:46 +00:00
|
|
|
|
|
2017-12-23 03:17:36 +00:00
|
|
|
|
uid.Value = item.Id;
|
|
|
|
|
gid.Value = item.Guild.Id;
|
|
|
|
|
uname.Value = item.Username;
|
|
|
|
|
disc.Value = item.Discriminator;
|
|
|
|
|
nname.Value = item.Nickname;
|
|
|
|
|
if (nname.Value == null) nname.Value = DBNull.Value; // why can't ?? work here?
|
|
|
|
|
url.Value = item.GetAvatarUrl();
|
|
|
|
|
if (url.Value == null) url.Value = DBNull.Value;
|
|
|
|
|
|
|
|
|
|
await c.ExecuteNonQueryAsync();
|
|
|
|
|
}
|
|
|
|
|
}
|
2017-12-23 01:53:46 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2018-04-05 00:25:13 +00:00
|
|
|
|
|
|
|
|
|
internal static Task UpdateGuildChannelAsync(SocketGuildChannel channel)
|
|
|
|
|
=> UpdateGuildChannelAsync(new SocketGuildChannel[] { channel });
|
|
|
|
|
internal static async Task UpdateGuildChannelAsync(IEnumerable<SocketGuildChannel> channels)
|
|
|
|
|
{
|
|
|
|
|
var db = await RegexBot.Config.GetOpenDatabaseConnectionAsync();
|
|
|
|
|
if (db == null) return;
|
|
|
|
|
using (db)
|
|
|
|
|
{
|
|
|
|
|
using (var c = db.CreateCommand())
|
|
|
|
|
{
|
|
|
|
|
c.CommandText = "INSERT INTO " + TableTextChannel
|
|
|
|
|
+ " (channel_id, guild_id, cache_date, channel_name)"
|
2018-04-24 21:29:19 +00:00
|
|
|
|
+ " VALUES (@Cid, @Gid, now(), @Name) "
|
2018-04-05 00:25:13 +00:00
|
|
|
|
+ "ON CONFLICT (channel_id, guild_id) DO UPDATE SET "
|
|
|
|
|
+ "cache_date = EXCLUDED.cache_date, channel_name = EXCLUDED.channel_name";
|
|
|
|
|
|
|
|
|
|
var cid = c.Parameters.Add("@Cid", NpgsqlDbType.Bigint);
|
|
|
|
|
var gid = c.Parameters.Add("@Gid", NpgsqlDbType.Bigint);
|
|
|
|
|
var cname = c.Parameters.Add("@Name", NpgsqlDbType.Text);
|
|
|
|
|
c.Prepare();
|
|
|
|
|
|
|
|
|
|
foreach (var item in channels)
|
|
|
|
|
{
|
|
|
|
|
if (!(item is ITextChannel ich)) continue;
|
|
|
|
|
|
|
|
|
|
cid.Value = item.Id;
|
|
|
|
|
gid.Value = item.Guild.Id;
|
|
|
|
|
cname.Value = item.Name;
|
|
|
|
|
await c.ExecuteNonQueryAsync();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2017-12-23 01:53:46 +00:00
|
|
|
|
#endregion
|
|
|
|
|
}
|
|
|
|
|
}
|