BirthdayBot/BackgroundServices/AutoUserDownload.cs

67 lines
2.9 KiB
C#
Raw Normal View History

using BirthdayBot.Data;
using Microsoft.EntityFrameworkCore;
namespace BirthdayBot.BackgroundServices;
/// <summary>
/// Proactively fills the user cache for guilds in which any birthday data already exists.
/// </summary>
class AutoUserDownload : BackgroundService {
public AutoUserDownload(ShardInstance instance) : base(instance) { }
2023-03-04 19:27:31 +00:00
private static readonly HashSet<ulong> _failedDownloads = new();
private static readonly TimeSpan _singleDlTimeout = ShardManager.DeadShardThreshold / 3;
2023-03-04 19:27:31 +00:00
public override async Task OnTick(int tickCount, CancellationToken token) {
2022-03-21 19:11:30 +00:00
// Take action if a guild's cache is incomplete...
var incompleteCaches = ShardInstance.DiscordClient.Guilds
.Where(g => !g.HasAllMembers)
.Select(g => g.Id)
.ToHashSet();
2022-03-21 19:11:30 +00:00
// ...and if the guild contains any user data
2022-11-23 07:19:37 +00:00
IEnumerable<ulong> mustFetch;
try {
await DbConcurrentOperationsLock.WaitAsync(token);
using var db = new BotDatabaseContext();
2023-03-04 19:27:31 +00:00
lock (_failedDownloads)
mustFetch = db.UserEntries.AsNoTracking()
.Where(e => incompleteCaches.Contains(e.GuildId))
.Select(e => e.GuildId)
.Distinct()
.Where(e => !_failedDownloads.Contains(e))
.ToList();
} finally {
try {
DbConcurrentOperationsLock.Release();
} catch (ObjectDisposedException) { }
}
var processed = 0;
2023-03-04 19:27:31 +00:00
var processStartTime = DateTimeOffset.UtcNow;
2022-03-21 19:11:30 +00:00
foreach (var item in mustFetch) {
// May cause a disconnect in certain situations. Make no further attempts until the next pass if it happens.
2022-03-22 06:33:24 +00:00
if (ShardInstance.DiscordClient.ConnectionState != ConnectionState.Connected) break;
2023-03-04 19:27:31 +00:00
var guild = ShardInstance.DiscordClient.GetGuild(item);
2022-03-21 19:11:30 +00:00
if (guild == null) continue; // A guild disappeared...?
2023-03-04 19:27:31 +00:00
await Task.Delay(200, CancellationToken.None); // Delay a bit (reduces the possibility of hanging, somehow).
2022-03-22 06:33:24 +00:00
processed++;
var dl = guild.DownloadUsersAsync();
dl.Wait((int)_singleDlTimeout.TotalMilliseconds / 2, token);
if (dl.IsFaulted) {
Log("Exception thrown by download task: " + dl.Exception);
2023-03-04 19:27:31 +00:00
break;
} else if (!dl.IsCompletedSuccessfully) {
Log($"Hang on processing {guild.Id}. Skipping.");
lock (_failedDownloads) _failedDownloads.Add(guild.Id);
continue;
2023-03-04 19:27:31 +00:00
}
// Prevent unnecessary disconnections by ShardManager if we're taking too long
if (DateTimeOffset.UtcNow - processStartTime > _singleDlTimeout) break;
}
2022-03-22 06:33:24 +00:00
if (processed > 10) Log($"Member list downloads handled for {processed} guilds.");
}
}