private async Task CleanupOldPatches() { var patches = await _sql.GetOldPatches(); using var client = await GetBunnyCdnFtpClient(); foreach (var patch in patches) { _logger.LogInformation($"Cleaning patch {patch.Src.Archive.Hash} -> {patch.Dest.Archive.Hash}"); await _discordWebHook.Send(Channel.Spam, new DiscordMessage { Content = $"Removing {patch.PatchSize.FileSizeToString()} patch from {patch.Src.Archive.State.PrimaryKeyString} to {patch.Dest.Archive.State.PrimaryKeyString} due it no longer being required by curated lists" }); if (!await DeleteFromCDN(client, PatchName(patch))) { _logger.LogWarning($"Patch file didn't exist {PatchName(patch)}"); } await _sql.DeletePatch(patch); var pendingPatch = await _sql.GetPendingPatch(); if (pendingPatch != default) { break; } } var files = await client.GetListingAsync($"\\"); _logger.LogInformation($"Found {files.Length} on the CDN"); var sqlFiles = await _sql.AllPatchHashes(); _logger.LogInformation($"Found {sqlFiles.Count} in SQL"); HashSet <(Hash, Hash)> NamesToPairs(IEnumerable <FtpListItem> ftpFiles) { return(ftpFiles.Select(f => f.Name).Where(f => f.Contains("_")).Select(p => { try { var lst = p.Split("_", StringSplitOptions.RemoveEmptyEntries).Select(Hash.FromHex).ToArray(); return (lst[0], lst[1]); } catch (ArgumentException ex) { return default; } catch (FormatException ex) { return default; } }).Where(f => f != default).ToHashSet()); } var oldHashPairs = NamesToPairs(files.Where(f => DateTime.UtcNow - f.Modified > TimeSpan.FromDays(2))); foreach (var(oldHash, newHash) in oldHashPairs.Where(o => !sqlFiles.Contains(o))) { _logger.LogInformation($"Removing CDN File entry for {oldHash} -> {newHash} it's not SQL"); await client.DeleteFileAsync(PatchName(oldHash, newHash)); } var hashPairs = NamesToPairs(files); foreach (var sqlFile in sqlFiles.Where(s => !hashPairs.Contains(s))) { _logger.LogInformation($"Removing SQL File entry for {sqlFile.Item1} -> {sqlFile.Item2} it's not on the CDN"); await _sql.DeletePatchesForHashPair(sqlFile); } }