mirror of
https://github.com/NoiTheCat/BirthdayBot.git
synced 2024-11-25 01:44:12 +00:00
Remove semaphore in SelectiveAutoUserDownload
Also updated style for this file. Additionally, reworded the corresponding error message to something friendlier.
This commit is contained in:
parent
bef22a5548
commit
8cff530a7c
2 changed files with 45 additions and 70 deletions
|
@ -1,86 +1,61 @@
|
||||||
using BirthdayBot.Data;
|
using BirthdayBot.Data;
|
||||||
|
using Discord;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace BirthdayBot.BackgroundServices
|
namespace BirthdayBot.BackgroundServices;
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// A type of workaround to the issue of user information not being cached for guilds that
|
|
||||||
/// have user information existing in the bot's database. This service runs frequently and
|
|
||||||
/// determines guilds in which user data must be downloaded, and proceeds to request it.
|
|
||||||
/// </summary>
|
|
||||||
class SelectiveAutoUserDownload : BackgroundService
|
|
||||||
{
|
|
||||||
private static readonly SemaphoreSlim _updateLock = new(2);
|
|
||||||
|
|
||||||
private readonly HashSet<ulong> _fetchRequests = new();
|
/// <summary>
|
||||||
|
/// Rather than use up unnecessary resources by auto-downloading the user list in -every-
|
||||||
|
/// server we're in, this service checks if fetching the user list is warranted for each
|
||||||
|
/// guild before proceeding to request it.
|
||||||
|
/// </summary>
|
||||||
|
class SelectiveAutoUserDownload : BackgroundService {
|
||||||
|
private readonly HashSet<ulong> _fetchRequests = new();
|
||||||
|
|
||||||
public SelectiveAutoUserDownload(ShardInstance instance) : base(instance) { }
|
public SelectiveAutoUserDownload(ShardInstance instance) : base(instance) { }
|
||||||
|
|
||||||
public override async Task OnTick(CancellationToken token)
|
public override async Task OnTick(CancellationToken token) {
|
||||||
{
|
IEnumerable<ulong> requests;
|
||||||
IEnumerable<ulong> requests;
|
lock (_fetchRequests) {
|
||||||
lock (_fetchRequests)
|
requests = _fetchRequests.ToArray();
|
||||||
{
|
_fetchRequests.Clear();
|
||||||
requests = _fetchRequests.ToArray();
|
|
||||||
_fetchRequests.Clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var guild in ShardInstance.DiscordClient.Guilds)
|
|
||||||
{
|
|
||||||
if (ShardInstance.DiscordClient.ConnectionState != Discord.ConnectionState.Connected)
|
|
||||||
{
|
|
||||||
Log("Client no longer connected. Stopping early.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Determine if there is action to be taken...
|
|
||||||
if (guild.HasAllMembers) continue;
|
|
||||||
if (requests.Contains(guild.Id) || await GuildUserAnyAsync(guild.Id, token).ConfigureAwait(false))
|
|
||||||
{
|
|
||||||
await guild.DownloadUsersAsync().ConfigureAwait(false);
|
|
||||||
await Task.Delay(500, CancellationToken.None).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
foreach (var guild in ShardInstance.DiscordClient.Guilds) {
|
||||||
/// Determines if the user database contains any entries corresponding to this guild.
|
if (ShardInstance.DiscordClient.ConnectionState != ConnectionState.Connected) {
|
||||||
/// </summary>
|
Log("Client no longer connected. Stopping early.");
|
||||||
/// <returns>True if any entries exist.</returns>
|
return;
|
||||||
private static async Task<bool> GuildUserAnyAsync(ulong guildId, CancellationToken token)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
await _updateLock.WaitAsync(token).ConfigureAwait(false);
|
|
||||||
}
|
}
|
||||||
catch (Exception ex) when (ex is OperationCanceledException or ObjectDisposedException)
|
|
||||||
{
|
|
||||||
// Calling thread does not expect the exception that SemaphoreSlim throws...
|
|
||||||
throw new TaskCanceledException();
|
|
||||||
}
|
|
||||||
try
|
|
||||||
{
|
|
||||||
using var db = await Database.OpenConnectionAsync().ConfigureAwait(false);
|
|
||||||
using var c = db.CreateCommand();
|
|
||||||
c.CommandText = $"select count(*) from {GuildUserConfiguration.BackingTable} where guild_id = @Gid";
|
|
||||||
c.Parameters.Add("@Gid", NpgsqlTypes.NpgsqlDbType.Bigint).Value = (long)guildId;
|
|
||||||
await c.PrepareAsync(CancellationToken.None).ConfigureAwait(false);
|
|
||||||
var r = (long)await c.ExecuteScalarAsync(token).ConfigureAwait(false);
|
|
||||||
return r != 0;
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
_updateLock.Release();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void RequestDownload(ulong guildId)
|
// Determine if there is action to be taken...
|
||||||
{
|
if (guild.HasAllMembers) continue;
|
||||||
lock (_fetchRequests) _fetchRequests.Add(guildId);
|
if (requests.Contains(guild.Id) || await GuildUserAnyAsync(guild.Id).ConfigureAwait(false)) {
|
||||||
|
await guild.DownloadUsersAsync().ConfigureAwait(false);
|
||||||
|
// Must delay after a download request. Seems to hang indefinitely otherwise.
|
||||||
|
await Task.Delay(300, CancellationToken.None).ConfigureAwait(false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Determines if the user database contains any entries corresponding to this guild.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>True if any entries exist.</returns>
|
||||||
|
private static async Task<bool> GuildUserAnyAsync(ulong guildId) {
|
||||||
|
using var db = await Database.OpenConnectionAsync().ConfigureAwait(false);
|
||||||
|
using var c = db.CreateCommand();
|
||||||
|
c.CommandText = $"select true from {GuildUserConfiguration.BackingTable} where guild_id = @Gid limit 1";
|
||||||
|
c.Parameters.Add("@Gid", NpgsqlTypes.NpgsqlDbType.Bigint).Value = (long)guildId;
|
||||||
|
await c.PrepareAsync(CancellationToken.None).ConfigureAwait(false);
|
||||||
|
using var r = await c.ExecuteReaderAsync(CancellationToken.None).ConfigureAwait(false);
|
||||||
|
return r.Read();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RequestDownload(ulong guildId) {
|
||||||
|
lock (_fetchRequests) _fetchRequests.Add(guildId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,7 +22,7 @@ namespace BirthdayBot.UserInterface
|
||||||
public const string ParameterError = ":x: Invalid usage. Refer to how to use the command and try again.";
|
public const string ParameterError = ":x: Invalid usage. Refer to how to use the command and try again.";
|
||||||
public const string NoParameterError = ":x: This command does not accept any parameters.";
|
public const string NoParameterError = ":x: This command does not accept any parameters.";
|
||||||
public const string InternalError = ":x: An internal bot error occurred. The bot maintainer has been notified of the issue.";
|
public const string InternalError = ":x: An internal bot error occurred. The bot maintainer has been notified of the issue.";
|
||||||
public const string UsersNotDownloadedError = ":x: Currently unavailable. Please try again in a few minutes.";
|
public const string UsersNotDownloadedError = ":eight_spoked_asterisk: Still catching up... Please try the command again in a few minutes.";
|
||||||
|
|
||||||
public delegate Task CommandHandler(ShardInstance instance, GuildConfiguration gconf,
|
public delegate Task CommandHandler(ShardInstance instance, GuildConfiguration gconf,
|
||||||
string[] param, SocketTextChannel reqChannel, SocketGuildUser reqUser);
|
string[] param, SocketTextChannel reqChannel, SocketGuildUser reqUser);
|
||||||
|
|
Loading…
Reference in a new issue