using Microsoft.Extensions.DependencyInjection; using WorldTime.Data; namespace WorldTime; /// /// Proactively fills the user cache for guilds in which any time zone configuration exists. /// /// Modeled after BirthdayBot's similar feature. class BackgroundUserListLoad : IDisposable { private readonly IServiceProvider _services; private readonly Task _workerTask; private readonly CancellationTokenSource _workerCancel; public BackgroundUserListLoad(IServiceProvider services) { _services = services; _workerCancel = new(); _workerTask = Task.Factory.StartNew(Worker, _workerCancel.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default); } public void Dispose() { _workerCancel.Cancel(); _workerCancel.Dispose(); _workerTask.Dispose(); } private async Task Worker() { while (!_workerCancel.IsCancellationRequested) { // Interval same as status await Task.Delay(WorldTime.StatusInterval * 1000, _workerCancel.Token); foreach (var shard in _services.GetRequiredService().Shards) { try { await ProcessShard(shard); } catch (Exception ex) { Program.Log(nameof(BackgroundUserListLoad), ex.ToString()); } } } } private async Task ProcessShard(DiscordSocketClient shard) { using var db = _services.GetRequiredService(); // Check when a guild's cache is incomplete... var incompleteCaches = shard.Guilds.Where(g => !g.HasAllMembers).Select(g => (long)g.Id).ToHashSet(); // ...and contains any user data. var mustFetch = db.UserEntries.Where(e => incompleteCaches.Contains(e.GuildId)).Select(e => e.GuildId).Distinct(); var processed = 0; foreach (var item in mustFetch) { // May cause a disconnect in certain situations. Cancel all further attempts until the next pass if it happens. if (shard.ConnectionState != ConnectionState.Connected) break; var guild = shard.GetGuild((ulong)item); if (guild == null) continue; // A guild disappeared...? await guild.DownloadUsersAsync().ConfigureAwait(false); // We're already on a seperate thread, no need to use Task.Run await Task.Delay(200, CancellationToken.None).ConfigureAwait(false); // Must delay, or else it seems to hang... processed++; } if (processed > 100) Program.Log(nameof(BackgroundUserListLoad), $"Explicit user list request processed for {processed} guilds."); } }