diff --git a/Module/VoteTempChannel/ChannelManager.cs b/Module/VoteTempChannel/ChannelManager.cs index 7b5c6e0..7f8e94f 100644 --- a/Module/VoteTempChannel/ChannelManager.cs +++ b/Module/VoteTempChannel/ChannelManager.cs @@ -1,7 +1,5 @@ using Discord.Rest; using Discord.WebSocket; -using Npgsql; -using NpgsqlTypes; using System; using System.Collections.Generic; using System.Threading; @@ -18,7 +16,7 @@ namespace Noikoio.RegexBot.Module.VoteTempChannel readonly DiscordSocketClient _client; /// - /// Key = guild, Value = expiry time, notify flag. + /// Key = guild, Value = expiry time /// Must lock! /// readonly Dictionary _trackedChannels; @@ -34,7 +32,6 @@ namespace Noikoio.RegexBot.Module.VoteTempChannel _bgTask = Task.Factory.StartNew(ChannelExpirationChecker, _token.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default); _trackedChannels = new Dictionary(); - SetUpPersistenceTableAsync().Wait(); } public void Dispose() @@ -42,92 +39,12 @@ namespace Noikoio.RegexBot.Module.VoteTempChannel _token.Cancel(); _token.Dispose(); } - - #region Data persistence - private const string PersistTable = "votetemp_persist"; - private async Task SetUpPersistenceTableAsync() - { - using (var db = await RegexBot.Config.GetOpenDatabaseConnectionAsync()) - { - using (var c = db.CreateCommand()) - { - c.CommandText = $"create table if not exists {PersistTable} (" + - "guild_id bigint primary key, " + - "expiration_time timestamptz not null" + - ")"; - await c.ExecuteNonQueryAsync(); - } - } - } - - private async Task GetPersistData(ulong guildId) - { - using (var db = await RegexBot.Config.GetOpenDatabaseConnectionAsync()) - { - using (var c = db.CreateCommand()) - { - c.CommandText = $"select expiration_time from {PersistTable} where guild_id = @Gid"; - c.Parameters.Add("@Gid", NpgsqlDbType.Bigint).Value = guildId; - c.Prepare(); - using (var r = await c.ExecuteReaderAsync()) - { - if (await r.ReadAsync()) return r.GetDateTime(0); - return null; - } - } - } - } - - private async Task InsertOrUpdatePersistData(ulong guildId, DateTimeOffset expiration) - { - using (var db = await RegexBot.Config.GetOpenDatabaseConnectionAsync()) - { - using (var c = db.CreateCommand()) - { - c.CommandText = $"delete from {PersistTable} where guild_id = @Gid"; - c.Parameters.Add("@Gid", NpgsqlDbType.Bigint).Value = guildId; - await c.ExecuteNonQueryAsync(); - } - - using (var c = db.CreateCommand()) - { - c.CommandText = $"insert into {PersistTable} values (@Gid, @Exp)"; - c.Parameters.Add("@Gid", NpgsqlDbType.Bigint).Value = guildId; - c.Parameters.Add("@Exp", NpgsqlDbType.TimestampTZ).Value = expiration; - c.Prepare(); - try - { - await c.ExecuteNonQueryAsync(); - } - catch (NpgsqlException ex) - { - // TODO should log this instead of throwing an exception... - throw new ApplicationException("A database error occurred. Internal error message: " + ex.Message); - } - } - } - } - - private async Task DeletePersistData(ulong guildId) - { - using (var db = await RegexBot.Config.GetOpenDatabaseConnectionAsync()) - { - using (var c = db.CreateCommand()) - { - c.CommandText = $"delete from {PersistTable} where guild_id = @Gid"; - c.Parameters.Add("@Gid", NpgsqlDbType.Bigint).Value = guildId; - c.Prepare(); - await c.ExecuteNonQueryAsync(); - } - } - } - #endregion - + #region Querying /// /// Determines if the given guild has a temporary channel that is up for a renewal vote. /// - public bool IsUpForRenewal(SocketGuild guild, GuildConfiguration info) + public bool IsUpForRenewal(SocketGuild guild, Configuration info) { DateTimeOffset tcExp; lock (_trackedChannels) @@ -139,14 +56,7 @@ namespace Noikoio.RegexBot.Module.VoteTempChannel var renewThreshold = tcExp - info.KeepaliveVoteDuration; return DateTimeOffset.UtcNow > renewThreshold; } - - private SocketTextChannel FindTemporaryChannel(SocketGuild guild, GuildConfiguration conf) - => System.Linq.Enumerable.SingleOrDefault(guild.TextChannels, c => c.Name == conf.TempChannelName); - - public bool HasExistingTemporaryChannel(SocketGuild guild, GuildConfiguration info) - { - return FindTemporaryChannel(guild, info) != null; - } + #endregion #region Channel entry manipulation @@ -156,7 +66,7 @@ namespace Noikoio.RegexBot.Module.VoteTempChannel /// /// Various causes. Send exception message to log and channel if thrown. /// - public async Task CreateChannelAndEntryAsync(SocketGuild guild, GuildConfiguration info) + public async Task CreateChannelAndEntryAsync(SocketGuild guild, Configuration info) { lock (_trackedChannels) { @@ -164,8 +74,6 @@ namespace Noikoio.RegexBot.Module.VoteTempChannel if (_trackedChannels.ContainsKey(guild.Id)) return null; } - var channelExpiryTime = DateTimeOffset.UtcNow + info.ChannelBaseDuration; - RestTextChannel newCh = null; try { @@ -179,19 +87,13 @@ namespace Noikoio.RegexBot.Module.VoteTempChannel // Channel creation succeeded. Regardless of persistent state, at least add it to in-memory cache. lock (_trackedChannels) _trackedChannels.Add(guild.Id, (channelExpiryTime, false)); - // Create persistent entry. - await InsertOrUpdatePersistData(guild.Id, channelExpiryTime); - return newCh; } /// /// For an existing temporary channel, extends its lifetime by a predetermined amount. /// - /// - /// SQL. Send exception message to log and channel if thrown. - /// - public async Task ExtendChannelExpirationAsync(SocketGuild guild, GuildConfiguration info) + public async Task ExtendChannelExpirationAsync(SocketGuild guild, Configuration info) { DateTimeOffset newExpiration; lock (_trackedChannels) @@ -202,52 +104,8 @@ namespace Noikoio.RegexBot.Module.VoteTempChannel newExpiration+= info.ChannelExtendDuration; _trackedChannels[guild.Id] = (newExpiration, false); } - - await InsertOrUpdatePersistData(guild.Id, newExpiration); } - - /// - /// Called when becoming aware of a new guild. Checks and acts on persistence data. - /// - public async Task RecheckExpiryInformation(SocketGuild guild, GuildConfiguration info) - { - var ch = FindTemporaryChannel(guild, info); - var persist = await GetPersistData(guild.Id); - - if (persist.HasValue) - { - // Found persistence data and... - if (ch == null) - { - // ...there is no existing corresponding channel. Delete persistence data. - await DeletePersistData(guild.Id); - } - else - { - // ...the channel exists. Add to in-memory cache. - // Cached persistence should extend to at least 5 minutes if needed. - // Should allow for enough time for users to vote for an extension. - DateTimeOffset toCache; - if ((DateTimeOffset.UtcNow - persist.Value).Duration().TotalMinutes > 5) - toCache = persist.Value; - else - toCache = DateTimeOffset.UtcNow.AddMinutes(5); - lock (_trackedChannels) { _trackedChannels.Add(guild.Id, (toCache, false)); } - } - } - else - { - // No persistence data. - if (ch != null) - { - // But we have a channel. Add new value to cache. - var exp = DateTimeOffset.UtcNow + info.ChannelBaseDuration; - lock (_trackedChannels) { _trackedChannels.Add(guild.Id, (exp, false)); } - await InsertOrUpdatePersistData(guild.Id, exp); - } - } - } - + /// /// Sets the given guild's temporary channel as up for immediate expiration. /// Use this to properly remove a temporary channel. @@ -259,11 +117,10 @@ namespace Noikoio.RegexBot.Module.VoteTempChannel if (!_trackedChannels.ContainsKey(guild.Id)) return; // how did we even get here? _trackedChannels[guild.Id] = (DateTimeOffset.UtcNow, true); } - await DeletePersistData(guild.Id); } /// - /// Removes the given guild from the cache. Does not alter persistence data. + /// Removes the given guild from the cache. /// public void DropCacheEntry(SocketGuild guild) { @@ -329,7 +186,6 @@ namespace Noikoio.RegexBot.Module.VoteTempChannel cachePostRemove.Add(item.Key); continue; } - DeletePersistData(item.Key).Wait(); cachePostRemove.Add(item.Key); } else if (item.Value.Item2 == false && IsUpForRenewal(ch.Guild, conf))