diff --git a/ArchiSteamFarm/GitHub.cs b/ArchiSteamFarm/GitHub.cs index f388410be..422525595 100644 --- a/ArchiSteamFarm/GitHub.cs +++ b/ArchiSteamFarm/GitHub.cs @@ -25,6 +25,7 @@ using System.Diagnostics.CodeAnalysis; using System.IO; using System.Linq; using System.Threading.Tasks; +using AngleSharp.Dom; using Markdig; using Markdig.Renderers; using Markdig.Syntax; @@ -53,6 +54,28 @@ namespace ArchiSteamFarm { return await GetReleaseFromURL(SharedInfo.GithubReleaseURL + "/tags/" + version).ConfigureAwait(false); } + internal static async Task GetWikiPage(string page, string? revision = null) { + if (string.IsNullOrEmpty(page)) { + throw new ArgumentNullException(nameof(page)); + } + + if (ASF.WebBrowser == null) { + throw new InvalidOperationException(nameof(ASF.WebBrowser)); + } + + string url = SharedInfo.GithubWikiURL + "/" + page + (!string.IsNullOrEmpty(revision) ? "/" + revision : ""); + + using WebBrowser.HtmlDocumentResponse? response = await ASF.WebBrowser.UrlGetToHtmlDocument(url).ConfigureAwait(false); + + if (response == null) { + return null; + } + + IElement? markdownBodyNode = response.Content.SelectSingleNode("//div[@class='markdown-body']"); + + return markdownBodyNode != null ? markdownBodyNode.InnerHtml : ""; + } + private static MarkdownDocument ExtractChangelogFromBody(string markdownText) { if (string.IsNullOrEmpty(markdownText)) { throw new ArgumentNullException(nameof(markdownText)); diff --git a/ArchiSteamFarm/IPC/Controllers/Api/WWWController.cs b/ArchiSteamFarm/IPC/Controllers/Api/WWWController.cs index d1269f8b9..d643559e4 100644 --- a/ArchiSteamFarm/IPC/Controllers/Api/WWWController.cs +++ b/ArchiSteamFarm/IPC/Controllers/Api/WWWController.cs @@ -81,6 +81,31 @@ namespace ArchiSteamFarm.IPC.Controllers.Api { return releaseResponse != null ? Ok(new GenericResponse(new GitHubReleaseResponse(releaseResponse))) : StatusCode((int) HttpStatusCode.ServiceUnavailable, new GenericResponse(false, string.Format(CultureInfo.CurrentCulture, Strings.ErrorRequestFailedTooManyTimes, WebBrowser.MaxTries))); } + /// + /// Fetches specific GitHub page of ASF project. + /// + /// + /// This is internal API being utilizied by our ASF-ui IPC frontend. You should not depend on existence of any /Api/WWW endpoints as they can disappear and change anytime. + /// Specifying revision is optional - when not specified, will fetch latest available. If specified revision is invalid, GitHub will automatically fetch the latest revision as well. + /// + [HttpGet("GitHub/Wiki/Page/{page:required}")] + [ProducesResponseType(typeof(GenericResponse), (int) HttpStatusCode.OK)] + [ProducesResponseType(typeof(GenericResponse), (int) HttpStatusCode.BadRequest)] + [ProducesResponseType(typeof(GenericResponse), (int) HttpStatusCode.ServiceUnavailable)] + public async Task> GitHubWikiPageGet(string page, [FromQuery] string? revision = null) { + if (string.IsNullOrEmpty(page)) { + throw new ArgumentNullException(nameof(page)); + } + + string? html = await GitHub.GetWikiPage(page, revision).ConfigureAwait(false); + + return html switch { + null => StatusCode((int) HttpStatusCode.ServiceUnavailable, new GenericResponse(false, string.Format(CultureInfo.CurrentCulture, Strings.ErrorRequestFailedTooManyTimes, WebBrowser.MaxTries))), + "" => BadRequest(new GenericResponse(false, string.Format(CultureInfo.CurrentCulture, Strings.ErrorIsInvalid, nameof(page)))), + _ => Ok(new GenericResponse(html)) + }; + } + /// /// Sends a HTTPS request through ASF's built-in HttpClient. /// diff --git a/ArchiSteamFarm/SharedInfo.cs b/ArchiSteamFarm/SharedInfo.cs index 83b7c42d4..b76869fa1 100644 --- a/ArchiSteamFarm/SharedInfo.cs +++ b/ArchiSteamFarm/SharedInfo.cs @@ -44,6 +44,7 @@ namespace ArchiSteamFarm { internal const string EnvironmentVariablePath = ASF + "_PATH"; internal const string GithubReleaseURL = "https://api.github.com/repos/" + GithubRepo + "/releases"; internal const string GithubRepo = "JustArchiNET/" + AssemblyName; + internal const string GithubWikiURL = "https://github.com/" + GithubRepo + "/wiki"; internal const string GlobalConfigFileName = ASF + JsonConfigExtension; internal const string GlobalDatabaseFileName = ASF + DatabaseExtension; internal const string IPCConfigExtension = ".config";