From 77a609758bb80bac9497d2e3988550f8be578407 Mon Sep 17 00:00:00 2001 From: Rory& Date: Mon, 23 Feb 2026 02:03:20 +0100 Subject: Initial commit --- .../Services/BuildDownloadService.cs | 76 ++++++++++++++++++++++ .../Services/ClientStoreService.cs | 62 ++++++++++++++++++ .../Services/ModernAssetLocator.cs | 3 + .../Services/TemporaryTestJob.cs | 14 ++++ 4 files changed, 155 insertions(+) create mode 100644 ReferenceClientProxyImplementation/Services/BuildDownloadService.cs create mode 100644 ReferenceClientProxyImplementation/Services/ClientStoreService.cs create mode 100644 ReferenceClientProxyImplementation/Services/ModernAssetLocator.cs create mode 100644 ReferenceClientProxyImplementation/Services/TemporaryTestJob.cs (limited to 'ReferenceClientProxyImplementation/Services') diff --git a/ReferenceClientProxyImplementation/Services/BuildDownloadService.cs b/ReferenceClientProxyImplementation/Services/BuildDownloadService.cs new file mode 100644 index 0000000..364c6c5 --- /dev/null +++ b/ReferenceClientProxyImplementation/Services/BuildDownloadService.cs @@ -0,0 +1,76 @@ +// using System.Net; +// using AngleSharp.Html.Parser; +// +// namespace ReferenceClientProxyImplementation.Services; +// +// public class BuildDownloadService(ILogger logger) { +// private static readonly HttpClient hc = new(); +// +// public async Task DownloadBuildFromArchiveOrg(string outputDirectory, DateTime timestamp) { +// // 20150906025145 +// var paddedTimestamp = timestamp.ToString("yyyyMMddHHmmss"); +// await DownloadBuildFromUrl(outputDirectory, $"https://web.archive.org/web/{paddedTimestamp}im_/https://discordapp.com/login"); +// } +// +// public async Task DownloadBuildFromUrl(string outputDirectory, string url) { +// logger.LogInformation("Downloading build from {url} to {outDir}", url, outputDirectory); +// var response = await hc.GetAsync(url); +// if (!response.IsSuccessStatusCode) +// throw new Exception($"Failed to download build from {url}"); +// var html = await response.Content.ReadAsStringAsync(); +// File.WriteAllText(outputDirectory + "/index.html", html); +// var parser = new HtmlParser(); +// var document = parser.ParseDocument(html); +// var assets = document.QuerySelectorAll("link[rel=stylesheet], link[rel=icon], script, img"); +// foreach (var asset in assets) { +// var assetUrl = asset.GetAttribute("href") ?? asset.GetAttribute("src"); +// if (assetUrl == null) +// continue; +// if (assetUrl.StartsWith("//")) { +// logger.LogWarning("Skipping asset {assetUrl} as it is a protocol-relative URL", assetUrl); +// continue; +// } +// +// var assetStream = await GetAssetStream(assetUrl); +// var assetPath = Path.Combine(outputDirectory, assetUrl.TrimStart('/')); +// Console.WriteLine($"Downloading asset {assetUrl} to {assetPath}"); +// Directory.CreateDirectory(Path.GetDirectoryName(assetPath)); +// await using var fs = File.Create(assetPath); +// await assetStream.CopyToAsync(fs); +// } +// +// logger.LogInformation("Downloading build from {url} complete!", url); +// } +// +// public async Task GetAssetStream(string asset) { +// asset = asset.Replace("/assets/", ""); +// var urlsToTry = new Stack(new[] { +// $"https://web.archive.org/web/0id_/https://discordapp.com/assets/{asset}", +// $"https://web.archive.org/web/0id_/https://discord.com/assets/{asset}", +// $"https://discord.com/assets/{asset}" +// }); +// while (urlsToTry.TryPop(out var urlToTry)) { +// if (string.IsNullOrWhiteSpace(urlToTry)) continue; +// try { +// var response = await hc.GetAsync(urlToTry, HttpCompletionOption.ResponseHeadersRead); +// if (response.IsSuccessStatusCode) { +// Console.WriteLine($"Got success for asset {asset} from {urlToTry}"); +// return await response.Content.ReadAsStreamAsync(); +// } +// //redirect +// +// if (response.StatusCode == HttpStatusCode.Found) { +// var redirectUrl = response.Headers.Location?.ToString(); +// if (string.IsNullOrWhiteSpace(redirectUrl)) continue; +// urlsToTry.Push(redirectUrl); +// } +// else logger.LogWarning("Failed to download asset {asset} from {urlToTry}", asset, urlToTry); +// } +// catch { +// // ignored +// } +// } +// +// throw new Exception($"Failed to download asset {asset}"); +// } +// } \ No newline at end of file diff --git a/ReferenceClientProxyImplementation/Services/ClientStoreService.cs b/ReferenceClientProxyImplementation/Services/ClientStoreService.cs new file mode 100644 index 0000000..6bd7418 --- /dev/null +++ b/ReferenceClientProxyImplementation/Services/ClientStoreService.cs @@ -0,0 +1,62 @@ +using ArcaneLibs.Extensions.Streams; +using ReferenceClientProxyImplementation.Configuration; +using ReferenceClientProxyImplementation.Patches.Implementations; + +namespace ReferenceClientProxyImplementation.Services; + +public class ClientStoreService(ProxyConfiguration config, PatchSet patches) { + private static readonly HttpClient HttpClient = new(); + + public async Task GetPatchedClientAsset(string relativePath) { + if (relativePath.StartsWith("/")) { + relativePath = relativePath[1..]; + } + + var path = Path.Combine(config.TestClient.RevisionPath, "patched", relativePath); + + if (File.Exists(path)) + return File.OpenRead(path); + + var srcAsset = (await GetOrDownloadRawAsset(relativePath)).ReadToEnd().ToArray(); + var result = await patches.ApplyPatches(relativePath, srcAsset); + Directory.CreateDirectory(Path.GetDirectoryName(path)!); + if (!result.SequenceEqual(srcAsset)) { + await File.WriteAllBytesAsync(path, result); + return File.OpenRead(path); + } + + Console.WriteLine($"No patches applied for {relativePath}, returning original asset."); + return new MemoryStream(srcAsset); + } + + public async Task GetOrDownloadRawAsset(string relativePath) { + relativePath = relativePath.TrimStart('/'); + var assetPath = Path.Combine(config.TestClient.RevisionPath, "src", relativePath); + if (File.Exists(assetPath)) { + Console.WriteLine($"Asset {relativePath} already exists at {assetPath}, returning existing file."); + return File.OpenRead(assetPath); + } + + var url = $"{config.TestClient.RevisionBaseUrl}/{relativePath}"; + var response = await HttpClient.GetAsync(url, HttpCompletionOption.ResponseHeadersRead); + if (!response.IsSuccessStatusCode) { + Console.WriteLine($"Failed to download asset {relativePath} from {url}, status code: {response.StatusCode}"); + throw new FileNotFoundException($"Asset not found: {relativePath}"); + } + var contentStream = await response.Content.ReadAsStreamAsync(); + Directory.CreateDirectory(Path.GetDirectoryName(assetPath)!); + await using var fileStream = File.Create(assetPath); + await contentStream.CopyToAsync(fileStream); + fileStream.Close(); + contentStream.Close(); + Console.WriteLine($"Downloaded asset {relativePath} to {assetPath}"); + + return File.OpenRead(assetPath); + } + + public bool HasRawAsset(string relativePath) { + relativePath = relativePath.TrimStart('/'); + var assetPath = Path.Combine(config.TestClient.RevisionPath, "src", relativePath); + return File.Exists(assetPath); + } +} \ No newline at end of file diff --git a/ReferenceClientProxyImplementation/Services/ModernAssetLocator.cs b/ReferenceClientProxyImplementation/Services/ModernAssetLocator.cs new file mode 100644 index 0000000..039fc74 --- /dev/null +++ b/ReferenceClientProxyImplementation/Services/ModernAssetLocator.cs @@ -0,0 +1,3 @@ +namespace ReferenceClientProxyImplementation.Services; + +public class ModernAssetLocator { } \ No newline at end of file diff --git a/ReferenceClientProxyImplementation/Services/TemporaryTestJob.cs b/ReferenceClientProxyImplementation/Services/TemporaryTestJob.cs new file mode 100644 index 0000000..b8998f1 --- /dev/null +++ b/ReferenceClientProxyImplementation/Services/TemporaryTestJob.cs @@ -0,0 +1,14 @@ +// namespace ReferenceClientProxyImplementation.Services; +// +// public class TemporaryTestJob(BuildDownloadService buildDownloadService) : BackgroundService { +// protected override async Task ExecuteAsync(CancellationToken stoppingToken) { +// Console.WriteLine("Running test job"); +// var outDir = +// "/home/Rory/git/spacebar/server-cs/DevUtils/ReferenceClientProxyImplementation/downloadCache/today/raw/"; +// if (Directory.Exists(outDir)) +// Directory.Delete(outDir, true); +// Directory.CreateDirectory(outDir); +// // await buildDownloadService.DownloadBuildFromArchiveOrg(outDir, new DateTime(2014, 1, 1)); +// await buildDownloadService.DownloadBuildFromUrl(outDir, "https://canary.discord.com/app"); +// } +// } \ No newline at end of file -- cgit 1.5.1