From 125bcbee56bf40cf82abc7fdb502f8cbc18546cf Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Fri, 13 Sep 2019 17:22:45 -0400 Subject: migrate to new project file format --- .../Framework/Clients/WebApi/ModEntryModel.cs | 33 +++ .../Clients/WebApi/ModEntryVersionModel.cs | 31 +++ .../Clients/WebApi/ModExtendedMetadataModel.cs | 123 +++++++++++ .../Framework/Clients/WebApi/ModSeachModel.cs | 36 ++++ .../Clients/WebApi/ModSearchEntryModel.cs | 34 +++ .../Framework/Clients/WebApi/WebApiClient.cs | 73 +++++++ .../Framework/Clients/Wiki/WikiClient.cs | 237 +++++++++++++++++++++ .../Clients/Wiki/WikiCompatibilityInfo.cs | 24 +++ .../Clients/Wiki/WikiCompatibilityStatus.cs | 27 +++ .../Framework/Clients/Wiki/WikiModEntry.cs | 54 +++++ .../Framework/Clients/Wiki/WikiModList.cs | 18 ++ 11 files changed, 690 insertions(+) create mode 100644 src/SMAPI.Toolkit/Framework/Clients/WebApi/ModEntryModel.cs create mode 100644 src/SMAPI.Toolkit/Framework/Clients/WebApi/ModEntryVersionModel.cs create mode 100644 src/SMAPI.Toolkit/Framework/Clients/WebApi/ModExtendedMetadataModel.cs create mode 100644 src/SMAPI.Toolkit/Framework/Clients/WebApi/ModSeachModel.cs create mode 100644 src/SMAPI.Toolkit/Framework/Clients/WebApi/ModSearchEntryModel.cs create mode 100644 src/SMAPI.Toolkit/Framework/Clients/WebApi/WebApiClient.cs create mode 100644 src/SMAPI.Toolkit/Framework/Clients/Wiki/WikiClient.cs create mode 100644 src/SMAPI.Toolkit/Framework/Clients/Wiki/WikiCompatibilityInfo.cs create mode 100644 src/SMAPI.Toolkit/Framework/Clients/Wiki/WikiCompatibilityStatus.cs create mode 100644 src/SMAPI.Toolkit/Framework/Clients/Wiki/WikiModEntry.cs create mode 100644 src/SMAPI.Toolkit/Framework/Clients/Wiki/WikiModList.cs (limited to 'src/SMAPI.Toolkit/Framework/Clients') diff --git a/src/SMAPI.Toolkit/Framework/Clients/WebApi/ModEntryModel.cs b/src/SMAPI.Toolkit/Framework/Clients/WebApi/ModEntryModel.cs new file mode 100644 index 00000000..8a9c0a25 --- /dev/null +++ b/src/SMAPI.Toolkit/Framework/Clients/WebApi/ModEntryModel.cs @@ -0,0 +1,33 @@ +namespace StardewModdingAPI.Toolkit.Framework.Clients.WebApi +{ + /// Metadata about a mod. + public class ModEntryModel + { + /********* + ** Accessors + *********/ + /// The mod's unique ID (if known). + public string ID { get; set; } + + /// The main version. + public ModEntryVersionModel Main { get; set; } + + /// The latest optional version, if newer than . + public ModEntryVersionModel Optional { get; set; } + + /// The latest unofficial version, if newer than and . + public ModEntryVersionModel Unofficial { get; set; } + + /// The latest unofficial version for the current Stardew Valley or SMAPI beta, if any (see ). + public ModEntryVersionModel UnofficialForBeta { get; set; } + + /// Optional extended data which isn't needed for update checks. + public ModExtendedMetadataModel Metadata { get; set; } + + /// Whether a Stardew Valley or SMAPI beta which affects mod compatibility is in progress. If this is true, should be used for beta versions of SMAPI instead of . + public bool HasBetaInfo { get; set; } + + /// The errors that occurred while fetching update data. + public string[] Errors { get; set; } = new string[0]; + } +} diff --git a/src/SMAPI.Toolkit/Framework/Clients/WebApi/ModEntryVersionModel.cs b/src/SMAPI.Toolkit/Framework/Clients/WebApi/ModEntryVersionModel.cs new file mode 100644 index 00000000..dadb8c10 --- /dev/null +++ b/src/SMAPI.Toolkit/Framework/Clients/WebApi/ModEntryVersionModel.cs @@ -0,0 +1,31 @@ +namespace StardewModdingAPI.Toolkit.Framework.Clients.WebApi +{ + /// Metadata about a version. + public class ModEntryVersionModel + { + /********* + ** Accessors + *********/ + /// The version number. + public ISemanticVersion Version { get; set; } + + /// The mod page URL. + public string Url { get; set; } + + + /********* + ** Public methods + *********/ + /// Construct an instance. + public ModEntryVersionModel() { } + + /// Construct an instance. + /// The version number. + /// The mod page URL. + public ModEntryVersionModel(ISemanticVersion version, string url) + { + this.Version = version; + this.Url = url; + } + } +} diff --git a/src/SMAPI.Toolkit/Framework/Clients/WebApi/ModExtendedMetadataModel.cs b/src/SMAPI.Toolkit/Framework/Clients/WebApi/ModExtendedMetadataModel.cs new file mode 100644 index 00000000..989c18b0 --- /dev/null +++ b/src/SMAPI.Toolkit/Framework/Clients/WebApi/ModExtendedMetadataModel.cs @@ -0,0 +1,123 @@ +using System.Collections.Generic; +using System.Linq; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; +using StardewModdingAPI.Toolkit.Framework.Clients.Wiki; +using StardewModdingAPI.Toolkit.Framework.ModData; + +namespace StardewModdingAPI.Toolkit.Framework.Clients.WebApi +{ + /// Extended metadata about a mod. + public class ModExtendedMetadataModel + { + /********* + ** Accessors + *********/ + /**** + ** Mod info + ****/ + /// The mod's unique ID. A mod may have multiple current IDs in rare cases (e.g. due to parallel releases or unofficial updates). + public string[] ID { get; set; } = new string[0]; + + /// The mod's display name. + public string Name { get; set; } + + /// The mod ID on Nexus. + public int? NexusID { get; set; } + + /// The mod ID in the Chucklefish mod repo. + public int? ChucklefishID { get; set; } + + /// The mod ID in the ModDrop mod repo. + public int? ModDropID { get; set; } + + /// The GitHub repository in the form 'owner/repo'. + public string GitHubRepo { get; set; } + + /// The URL to a non-GitHub source repo. + public string CustomSourceUrl { get; set; } + + /// The custom mod page URL (if applicable). + public string CustomUrl { get; set; } + + + /**** + ** Stable compatibility + ****/ + /// The compatibility status. + [JsonConverter(typeof(StringEnumConverter))] + public WikiCompatibilityStatus? CompatibilityStatus { get; set; } + + /// The human-readable summary of the compatibility status or workaround, without HTML formatitng. + public string CompatibilitySummary { get; set; } + + /// The game or SMAPI version which broke this mod, if applicable. + public string BrokeIn { get; set; } + + + /**** + ** Beta compatibility + ****/ + /// The compatibility status for the Stardew Valley beta (if any). + [JsonConverter(typeof(StringEnumConverter))] + public WikiCompatibilityStatus? BetaCompatibilityStatus { get; set; } + + /// The human-readable summary of the compatibility status or workaround for the Stardew Valley beta (if any), without HTML formatitng. + public string BetaCompatibilitySummary { get; set; } + + /// The beta game or SMAPI version which broke this mod, if applicable. + public string BetaBrokeIn { get; set; } + + + /********* + ** Public methods + *********/ + /// Construct an instance. + public ModExtendedMetadataModel() { } + + /// Construct an instance. + /// The mod metadata from the wiki (if available). + /// The mod metadata from SMAPI's internal DB (if available). + public ModExtendedMetadataModel(WikiModEntry wiki, ModDataRecord db) + { + // wiki data + if (wiki != null) + { + this.ID = wiki.ID; + this.Name = wiki.Name.FirstOrDefault(); + this.NexusID = wiki.NexusID; + this.ChucklefishID = wiki.ChucklefishID; + this.ModDropID = wiki.ModDropID; + this.GitHubRepo = wiki.GitHubRepo; + this.CustomSourceUrl = wiki.CustomSourceUrl; + this.CustomUrl = wiki.CustomUrl; + + this.CompatibilityStatus = wiki.Compatibility.Status; + this.CompatibilitySummary = wiki.Compatibility.Summary; + this.BrokeIn = wiki.Compatibility.BrokeIn; + + this.BetaCompatibilityStatus = wiki.BetaCompatibility?.Status; + this.BetaCompatibilitySummary = wiki.BetaCompatibility?.Summary; + this.BetaBrokeIn = wiki.BetaCompatibility?.BrokeIn; + } + + // internal DB data + if (db != null) + { + this.ID = this.ID.Union(db.FormerIDs).ToArray(); + this.Name = this.Name ?? db.DisplayName; + } + } + + /// Get update keys based on the metadata. + public IEnumerable GetUpdateKeys() + { + if (this.NexusID.HasValue) + yield return $"Nexus:{this.NexusID}"; + if (this.ChucklefishID.HasValue) + yield return $"Chucklefish:{this.ChucklefishID}"; + if (this.GitHubRepo != null) + yield return $"GitHub:{this.GitHubRepo}"; + } + } +} diff --git a/src/SMAPI.Toolkit/Framework/Clients/WebApi/ModSeachModel.cs b/src/SMAPI.Toolkit/Framework/Clients/WebApi/ModSeachModel.cs new file mode 100644 index 00000000..e352e1cc --- /dev/null +++ b/src/SMAPI.Toolkit/Framework/Clients/WebApi/ModSeachModel.cs @@ -0,0 +1,36 @@ +using System.Linq; + +namespace StardewModdingAPI.Toolkit.Framework.Clients.WebApi +{ + /// Specifies mods whose update-check info to fetch. + public class ModSearchModel + { + /********* + ** Accessors + *********/ + /// The mods for which to find data. + public ModSearchEntryModel[] Mods { get; set; } + + /// Whether to include extended metadata for each mod. + public bool IncludeExtendedMetadata { get; set; } + + + /********* + ** Public methods + *********/ + /// Construct an empty instance. + public ModSearchModel() + { + // needed for JSON deserialising + } + + /// Construct an instance. + /// The mods to search. + /// Whether to include extended metadata for each mod. + public ModSearchModel(ModSearchEntryModel[] mods, bool includeExtendedMetadata) + { + this.Mods = mods.ToArray(); + this.IncludeExtendedMetadata = includeExtendedMetadata; + } + } +} diff --git a/src/SMAPI.Toolkit/Framework/Clients/WebApi/ModSearchEntryModel.cs b/src/SMAPI.Toolkit/Framework/Clients/WebApi/ModSearchEntryModel.cs new file mode 100644 index 00000000..bca47647 --- /dev/null +++ b/src/SMAPI.Toolkit/Framework/Clients/WebApi/ModSearchEntryModel.cs @@ -0,0 +1,34 @@ +namespace StardewModdingAPI.Toolkit.Framework.Clients.WebApi +{ + /// Specifies the identifiers for a mod to match. + public class ModSearchEntryModel + { + /********* + ** Accessors + *********/ + /// The unique mod ID. + public string ID { get; set; } + + /// The namespaced mod update keys (if available). + public string[] UpdateKeys { get; set; } + + + /********* + ** Public methods + *********/ + /// Construct an empty instance. + public ModSearchEntryModel() + { + // needed for JSON deserialising + } + + /// Construct an instance. + /// The unique mod ID. + /// The namespaced mod update keys (if available). + public ModSearchEntryModel(string id, string[] updateKeys) + { + this.ID = id; + this.UpdateKeys = updateKeys ?? new string[0]; + } + } +} diff --git a/src/SMAPI.Toolkit/Framework/Clients/WebApi/WebApiClient.cs b/src/SMAPI.Toolkit/Framework/Clients/WebApi/WebApiClient.cs new file mode 100644 index 00000000..7c3df384 --- /dev/null +++ b/src/SMAPI.Toolkit/Framework/Clients/WebApi/WebApiClient.cs @@ -0,0 +1,73 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using Newtonsoft.Json; +using StardewModdingAPI.Toolkit.Serialisation; + +namespace StardewModdingAPI.Toolkit.Framework.Clients.WebApi +{ + /// Provides methods for interacting with the SMAPI web API. + public class WebApiClient + { + /********* + ** Fields + *********/ + /// The base URL for the web API. + private readonly Uri BaseUrl; + + /// The API version number. + private readonly ISemanticVersion Version; + + /// The JSON serializer settings to use. + private readonly JsonSerializerSettings JsonSettings = new JsonHelper().JsonSettings; + + + /********* + ** Public methods + *********/ + /// Construct an instance. + /// The base URL for the web API. + /// The web API version. + public WebApiClient(string baseUrl, ISemanticVersion version) + { + this.BaseUrl = new Uri(baseUrl); + this.Version = version; + } + + /// Get metadata about a set of mods from the web API. + /// The mod keys for which to fetch the latest version. + /// Whether to include extended metadata for each mod. + public IDictionary GetModInfo(ModSearchEntryModel[] mods, bool includeExtendedMetadata = false) + { + return this.Post( + $"v{this.Version}/mods", + new ModSearchModel(mods, includeExtendedMetadata) + ).ToDictionary(p => p.ID); + } + + + /********* + ** Private methods + *********/ + /// Fetch the response from the backend API. + /// The body content type. + /// The expected response type. + /// The request URL, optionally excluding the base URL. + /// The body content to post. + private TResult Post(string url, TBody content) + { + // note: avoid HttpClient for Mac compatibility + using (WebClient client = new WebClient()) + { + Uri fullUrl = new Uri(this.BaseUrl, url); + string data = JsonConvert.SerializeObject(content); + + client.Headers["Content-Type"] = "application/json"; + client.Headers["User-Agent"] = $"SMAPI/{this.Version}"; + string response = client.UploadString(fullUrl, data); + return JsonConvert.DeserializeObject(response, this.JsonSettings); + } + } + } +} diff --git a/src/SMAPI.Toolkit/Framework/Clients/Wiki/WikiClient.cs b/src/SMAPI.Toolkit/Framework/Clients/Wiki/WikiClient.cs new file mode 100644 index 00000000..3e9b8ea6 --- /dev/null +++ b/src/SMAPI.Toolkit/Framework/Clients/Wiki/WikiClient.cs @@ -0,0 +1,237 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using System.Net; +using System.Threading.Tasks; +using HtmlAgilityPack; +using Pathoschild.Http.Client; + +namespace StardewModdingAPI.Toolkit.Framework.Clients.Wiki +{ + /// An HTTP client for fetching mod metadata from the wiki. + public class WikiClient : IDisposable + { + /********* + ** Fields + *********/ + /// The underlying HTTP client. + private readonly IClient Client; + + + /********* + ** Public methods + *********/ + /// Construct an instance. + /// The user agent for the wiki API. + /// The base URL for the wiki API. + public WikiClient(string userAgent, string baseUrl = "https://stardewvalleywiki.com/mediawiki/api.php") + { + this.Client = new FluentClient(baseUrl).SetUserAgent(userAgent); + } + + /// Fetch mods from the compatibility list. + public async Task FetchModsAsync() + { + // fetch HTML + ResponseModel response = await this.Client + .GetAsync("") + .WithArguments(new + { + action = "parse", + page = "Modding:Mod_compatibility", + format = "json" + }) + .As(); + string html = response.Parse.Text["*"]; + + // parse HTML + var doc = new HtmlDocument(); + doc.LoadHtml(html); + + // fetch game versions + string stableVersion = doc.DocumentNode.SelectSingleNode("div[@class='game-stable-version']")?.InnerText; + string betaVersion = doc.DocumentNode.SelectSingleNode("div[@class='game-beta-version']")?.InnerText; + if (betaVersion == stableVersion) + betaVersion = null; + + // find mod entries + HtmlNodeCollection modNodes = doc.DocumentNode.SelectNodes("table[@id='mod-list']//tr[@class='mod']"); + if (modNodes == null) + throw new InvalidOperationException("Can't parse wiki compatibility list, no mods found."); + + // parse + WikiModEntry[] mods = this.ParseEntries(modNodes).ToArray(); + return new WikiModList + { + StableVersion = stableVersion, + BetaVersion = betaVersion, + Mods = mods + }; + } + + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + public void Dispose() + { + this.Client?.Dispose(); + } + + + /********* + ** Private methods + *********/ + /// Parse valid mod compatibility entries. + /// The HTML compatibility entries. + private IEnumerable ParseEntries(IEnumerable nodes) + { + foreach (HtmlNode node in nodes) + { + // extract fields + string[] names = this.GetAttributeAsCsv(node, "data-name"); + string[] authors = this.GetAttributeAsCsv(node, "data-author"); + string[] ids = this.GetAttributeAsCsv(node, "data-id"); + string[] warnings = this.GetAttributeAsCsv(node, "data-warnings"); + int? nexusID = this.GetAttributeAsNullableInt(node, "data-nexus-id"); + int? chucklefishID = this.GetAttributeAsNullableInt(node, "data-cf-id"); + int? modDropID = this.GetAttributeAsNullableInt(node, "data-moddrop-id"); + string githubRepo = this.GetAttribute(node, "data-github"); + string customSourceUrl = this.GetAttribute(node, "data-custom-source"); + string customUrl = this.GetAttribute(node, "data-url"); + string anchor = this.GetAttribute(node, "id"); + string contentPackFor = this.GetAttribute(node, "data-content-pack-for"); + + // parse stable compatibility + WikiCompatibilityInfo compatibility = new WikiCompatibilityInfo + { + Status = this.GetAttributeAsEnum(node, "data-status") ?? WikiCompatibilityStatus.Ok, + BrokeIn = this.GetAttribute(node, "data-broke-in"), + UnofficialVersion = this.GetAttributeAsSemanticVersion(node, "data-unofficial-version"), + UnofficialUrl = this.GetAttribute(node, "data-unofficial-url"), + Summary = this.GetInnerHtml(node, "mod-summary")?.Trim() + }; + + // parse beta compatibility + WikiCompatibilityInfo betaCompatibility = null; + { + WikiCompatibilityStatus? betaStatus = this.GetAttributeAsEnum(node, "data-beta-status"); + if (betaStatus.HasValue) + { + betaCompatibility = new WikiCompatibilityInfo + { + Status = betaStatus.Value, + BrokeIn = this.GetAttribute(node, "data-beta-broke-in"), + UnofficialVersion = this.GetAttributeAsSemanticVersion(node, "data-beta-unofficial-version"), + UnofficialUrl = this.GetAttribute(node, "data-beta-unofficial-url"), + Summary = this.GetInnerHtml(node, "mod-beta-summary") + }; + } + } + + // yield model + yield return new WikiModEntry + { + ID = ids, + Name = names, + Author = authors, + NexusID = nexusID, + ChucklefishID = chucklefishID, + ModDropID = modDropID, + GitHubRepo = githubRepo, + CustomSourceUrl = customSourceUrl, + CustomUrl = customUrl, + ContentPackFor = contentPackFor, + Compatibility = compatibility, + BetaCompatibility = betaCompatibility, + Warnings = warnings, + Anchor = anchor + }; + } + } + + /// Get an attribute value. + /// The element whose attributes to read. + /// The attribute name. + private string GetAttribute(HtmlNode element, string name) + { + string value = element.GetAttributeValue(name, null); + if (string.IsNullOrWhiteSpace(value)) + return null; + + return WebUtility.HtmlDecode(value); + } + + /// Get an attribute value and parse it as a comma-delimited list of strings. + /// The element whose attributes to read. + /// The attribute name. + private string[] GetAttributeAsCsv(HtmlNode element, string name) + { + string raw = this.GetAttribute(element, name); + return !string.IsNullOrWhiteSpace(raw) + ? raw.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Select(p => p.Trim()).ToArray() + : new string[0]; + } + + /// Get an attribute value and parse it as an enum value. + /// The enum type. + /// The element whose attributes to read. + /// The attribute name. + private TEnum? GetAttributeAsEnum(HtmlNode element, string name) where TEnum : struct + { + string raw = this.GetAttribute(element, name); + if (raw == null) + return null; + if (!Enum.TryParse(raw, true, out TEnum value) && Enum.IsDefined(typeof(TEnum), value)) + throw new InvalidOperationException($"Unknown {typeof(TEnum).Name} value '{raw}' when parsing compatibility list."); + return value; + } + + /// Get an attribute value and parse it as a semantic version. + /// The element whose attributes to read. + /// The attribute name. + private ISemanticVersion GetAttributeAsSemanticVersion(HtmlNode element, string name) + { + string raw = this.GetAttribute(element, name); + return SemanticVersion.TryParse(raw, out ISemanticVersion version) + ? version + : null; + } + + /// Get an attribute value and parse it as a nullable int. + /// The element whose attributes to read. + /// The attribute name. + private int? GetAttributeAsNullableInt(HtmlNode element, string name) + { + string raw = this.GetAttribute(element, name); + if (raw != null && int.TryParse(raw, out int value)) + return value; + return null; + } + + /// Get the text of an element with the given class name. + /// The metadata container. + /// The field name. + private string GetInnerHtml(HtmlNode container, string className) + { + return container.Descendants().FirstOrDefault(p => p.HasClass(className))?.InnerHtml; + } + + /// The response model for the MediaWiki parse API. + [SuppressMessage("ReSharper", "ClassNeverInstantiated.Local")] + [SuppressMessage("ReSharper", "UnusedAutoPropertyAccessor.Local")] + private class ResponseModel + { + /// The parse API results. + public ResponseParseModel Parse { get; set; } + } + + /// The inner response model for the MediaWiki parse API. + [SuppressMessage("ReSharper", "ClassNeverInstantiated.Local")] + [SuppressMessage("ReSharper", "CollectionNeverUpdated.Local")] + [SuppressMessage("ReSharper", "UnusedAutoPropertyAccessor.Local")] + private class ResponseParseModel + { + /// The parsed text. + public IDictionary Text { get; set; } + } + } +} diff --git a/src/SMAPI.Toolkit/Framework/Clients/Wiki/WikiCompatibilityInfo.cs b/src/SMAPI.Toolkit/Framework/Clients/Wiki/WikiCompatibilityInfo.cs new file mode 100644 index 00000000..204acd2b --- /dev/null +++ b/src/SMAPI.Toolkit/Framework/Clients/Wiki/WikiCompatibilityInfo.cs @@ -0,0 +1,24 @@ +namespace StardewModdingAPI.Toolkit.Framework.Clients.Wiki +{ + /// Compatibility info for a mod. + public class WikiCompatibilityInfo + { + /********* + ** Accessors + *********/ + /// The compatibility status. + public WikiCompatibilityStatus Status { get; set; } + + /// The human-readable summary of the compatibility status or workaround, without HTML formatting. + public string Summary { get; set; } + + /// The game or SMAPI version which broke this mod (if applicable). + public string BrokeIn { get; set; } + + /// The version of the latest unofficial update, if applicable. + public ISemanticVersion UnofficialVersion { get; set; } + + /// The URL to the latest unofficial update, if applicable. + public string UnofficialUrl { get; set; } + } +} diff --git a/src/SMAPI.Toolkit/Framework/Clients/Wiki/WikiCompatibilityStatus.cs b/src/SMAPI.Toolkit/Framework/Clients/Wiki/WikiCompatibilityStatus.cs new file mode 100644 index 00000000..a1d2dfae --- /dev/null +++ b/src/SMAPI.Toolkit/Framework/Clients/Wiki/WikiCompatibilityStatus.cs @@ -0,0 +1,27 @@ +namespace StardewModdingAPI.Toolkit.Framework.Clients.Wiki +{ + /// The compatibility status for a mod. + public enum WikiCompatibilityStatus + { + /// The mod is compatible. + Ok = 0, + + /// The mod is compatible if you use an optional official download. + Optional = 1, + + /// The mod is compatible if you use an unofficial update. + Unofficial = 2, + + /// The mod isn't compatible, but the player can fix it or there's a good alternative. + Workaround = 3, + + /// The mod isn't compatible. + Broken = 4, + + /// The mod is no longer maintained by the author, and an unofficial update or continuation is unlikely. + Abandoned = 5, + + /// The mod is no longer needed and should be removed. + Obsolete = 6 + } +} diff --git a/src/SMAPI.Toolkit/Framework/Clients/Wiki/WikiModEntry.cs b/src/SMAPI.Toolkit/Framework/Clients/Wiki/WikiModEntry.cs new file mode 100644 index 00000000..cf416cc6 --- /dev/null +++ b/src/SMAPI.Toolkit/Framework/Clients/Wiki/WikiModEntry.cs @@ -0,0 +1,54 @@ +namespace StardewModdingAPI.Toolkit.Framework.Clients.Wiki +{ + /// A mod entry in the wiki list. + public class WikiModEntry + { + /********* + ** Accessors + *********/ + /// The mod's unique ID. If the mod has alternate/old IDs, they're listed in latest to newest order. + public string[] ID { get; set; } + + /// The mod's display name. If the mod has multiple names, the first one is the most canonical name. + public string[] Name { get; set; } + + /// The mod's author name. If the author has multiple names, the first one is the most canonical name. + public string[] Author { get; set; } + + /// The mod ID on Nexus. + public int? NexusID { get; set; } + + /// The mod ID in the Chucklefish mod repo. + public int? ChucklefishID { get; set; } + + /// The mod ID in the ModDrop mod repo. + public int? ModDropID { get; set; } + + /// The GitHub repository in the form 'owner/repo'. + public string GitHubRepo { get; set; } + + /// The URL to a non-GitHub source repo. + public string CustomSourceUrl { get; set; } + + /// The custom mod page URL (if applicable). + public string CustomUrl { get; set; } + + /// The name of the mod which loads this content pack, if applicable. + public string ContentPackFor { get; set; } + + /// The mod's compatibility with the latest stable version of the game. + public WikiCompatibilityInfo Compatibility { get; set; } + + /// The mod's compatibility with the latest beta version of the game (if any). + public WikiCompatibilityInfo BetaCompatibility { get; set; } + + /// Whether a Stardew Valley or SMAPI beta which affects mod compatibility is in progress. If this is true, should be used for beta versions of SMAPI instead of . + public bool HasBetaInfo => this.BetaCompatibility != null; + + /// The human-readable warnings for players about this mod. + public string[] Warnings { get; set; } + + /// The link anchor for the mod entry in the wiki compatibility list. + public string Anchor { get; set; } + } +} diff --git a/src/SMAPI.Toolkit/Framework/Clients/Wiki/WikiModList.cs b/src/SMAPI.Toolkit/Framework/Clients/Wiki/WikiModList.cs new file mode 100644 index 00000000..0d614f28 --- /dev/null +++ b/src/SMAPI.Toolkit/Framework/Clients/Wiki/WikiModList.cs @@ -0,0 +1,18 @@ +namespace StardewModdingAPI.Toolkit.Framework.Clients.Wiki +{ + /// Metadata from the wiki's mod compatibility list. + public class WikiModList + { + /********* + ** Accessors + *********/ + /// The stable game version. + public string StableVersion { get; set; } + + /// The beta game version (if any). + public string BetaVersion { get; set; } + + /// The mods on the wiki. + public WikiModEntry[] Mods { get; set; } + } +} -- cgit