summaryrefslogtreecommitdiff
path: root/src/SMAPI.Web/Framework
diff options
context:
space:
mode:
Diffstat (limited to 'src/SMAPI.Web/Framework')
-rw-r--r--src/SMAPI.Web/Framework/Clients/Chucklefish/ChucklefishClient.cs83
-rw-r--r--src/SMAPI.Web/Framework/Clients/Chucklefish/ChucklefishMod.cs18
-rw-r--r--src/SMAPI.Web/Framework/Clients/Chucklefish/IChucklefishClient.cs17
-rw-r--r--src/SMAPI.Web/Framework/Clients/GitHub/GitHubClient.cs70
-rw-r--r--src/SMAPI.Web/Framework/Clients/GitHub/GitRelease.cs19
-rw-r--r--src/SMAPI.Web/Framework/Clients/GitHub/IGitHubClient.cs17
-rw-r--r--src/SMAPI.Web/Framework/Clients/Nexus/INexusClient.cs17
-rw-r--r--src/SMAPI.Web/Framework/Clients/Nexus/NexusClient.cs48
-rw-r--r--src/SMAPI.Web/Framework/Clients/Nexus/NexusMod.cs21
-rw-r--r--src/SMAPI.Web/Framework/ConfigModels/ApiClientsConfig.cs56
-rw-r--r--src/SMAPI.Web/Framework/ConfigModels/ModUpdateCheckConfig.cs49
-rw-r--r--src/SMAPI.Web/Framework/ModRepositories/ChucklefishRepository.cs50
-rw-r--r--src/SMAPI.Web/Framework/ModRepositories/GitHubRepository.cs59
-rw-r--r--src/SMAPI.Web/Framework/ModRepositories/NexusRepository.cs51
14 files changed, 395 insertions, 180 deletions
diff --git a/src/SMAPI.Web/Framework/Clients/Chucklefish/ChucklefishClient.cs b/src/SMAPI.Web/Framework/Clients/Chucklefish/ChucklefishClient.cs
new file mode 100644
index 00000000..6772672c
--- /dev/null
+++ b/src/SMAPI.Web/Framework/Clients/Chucklefish/ChucklefishClient.cs
@@ -0,0 +1,83 @@
+using System;
+using System.Net;
+using System.Threading.Tasks;
+using HtmlAgilityPack;
+using Pathoschild.Http.Client;
+
+namespace StardewModdingAPI.Web.Framework.Clients.Chucklefish
+{
+ /// <summary>An HTTP client for fetching mod metadata from the Chucklefish mod site.</summary>
+ internal class ChucklefishClient : IChucklefishClient
+ {
+ /*********
+ ** Properties
+ *********/
+ /// <summary>The base URL for the Chucklefish mod site.</summary>
+ private readonly string BaseUrl;
+
+ /// <summary>The URL for a mod page excluding the base URL, where {0} is the mod ID.</summary>
+ private readonly string ModPageUrlFormat;
+
+ /// <summary>The underlying HTTP client.</summary>
+ private readonly IClient Client;
+
+
+ /*********
+ ** Public methods
+ *********/
+ /// <summary>Construct an instance.</summary>
+ /// <param name="userAgent">The user agent for the API client.</param>
+ /// <param name="baseUrl">The base URL for the Chucklefish mod site.</param>
+ /// <param name="modPageUrlFormat">The URL for a mod page excluding the <paramref name="baseUrl"/>, where {0} is the mod ID.</param>
+ public ChucklefishClient(string userAgent, string baseUrl, string modPageUrlFormat)
+ {
+ this.BaseUrl = baseUrl;
+ this.ModPageUrlFormat = modPageUrlFormat;
+ this.Client = new FluentClient(baseUrl).SetUserAgent(userAgent);
+ }
+
+ /// <summary>Get metadata about a mod.</summary>
+ /// <param name="id">The Chucklefish mod ID.</param>
+ /// <returns>Returns the mod info if found, else <c>null</c>.</returns>
+ public async Task<ChucklefishMod> GetModAsync(uint id)
+ {
+ // fetch HTML
+ string html;
+ try
+ {
+ html = await this.Client
+ .GetAsync(string.Format(this.ModPageUrlFormat, id))
+ .AsString();
+ }
+ catch (ApiException ex) when (ex.Status == HttpStatusCode.NotFound)
+ {
+ return null;
+ }
+
+ // parse HTML
+ var doc = new HtmlDocument();
+ doc.LoadHtml(html);
+
+ // extract mod info
+ string url = new UriBuilder(new Uri(this.BaseUrl)) { Path = string.Format(this.ModPageUrlFormat, id) }.Uri.ToString();
+ string name = doc.DocumentNode.SelectSingleNode("//meta[@name='twitter:title']").Attributes["content"].Value;
+ if (name.StartsWith("[SMAPI] "))
+ name = name.Substring("[SMAPI] ".Length);
+ string version = doc.DocumentNode.SelectSingleNode("//h1/span").InnerText;
+
+ // create model
+ return new ChucklefishMod
+ {
+ Name = name,
+ Version = version,
+ Url = url
+ };
+ }
+
+ /// <summary>Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.</summary>
+ public void Dispose()
+ {
+ this.Client?.Dispose();
+ }
+ }
+}
diff --git a/src/SMAPI.Web/Framework/Clients/Chucklefish/ChucklefishMod.cs b/src/SMAPI.Web/Framework/Clients/Chucklefish/ChucklefishMod.cs
new file mode 100644
index 00000000..fd0101d4
--- /dev/null
+++ b/src/SMAPI.Web/Framework/Clients/Chucklefish/ChucklefishMod.cs
@@ -0,0 +1,18 @@
+namespace StardewModdingAPI.Web.Framework.Clients.Chucklefish
+{
+ /// <summary>Mod metadata from the Chucklefish mod site.</summary>
+ internal class ChucklefishMod
+ {
+ /*********
+ ** Accessors
+ *********/
+ /// <summary>The mod name.</summary>
+ public string Name { get; set; }
+
+ /// <summary>The mod's semantic version number.</summary>
+ public string Version { get; set; }
+
+ /// <summary>The mod's web URL.</summary>
+ public string Url { get; set; }
+ }
+}
diff --git a/src/SMAPI.Web/Framework/Clients/Chucklefish/IChucklefishClient.cs b/src/SMAPI.Web/Framework/Clients/Chucklefish/IChucklefishClient.cs
new file mode 100644
index 00000000..1d8b256e
--- /dev/null
+++ b/src/SMAPI.Web/Framework/Clients/Chucklefish/IChucklefishClient.cs
@@ -0,0 +1,17 @@
+using System;
+using System.Threading.Tasks;
+
+namespace StardewModdingAPI.Web.Framework.Clients.Chucklefish
+{
+ /// <summary>An HTTP client for fetching mod metadata from the Chucklefish mod site.</summary>
+ internal interface IChucklefishClient : IDisposable
+ {
+ /*********
+ ** Methods
+ *********/
+ /// <summary>Get metadata about a mod.</summary>
+ /// <param name="id">The Chucklefish mod ID.</param>
+ /// <returns>Returns the mod info if found, else <c>null</c>.</returns>
+ Task<ChucklefishMod> GetModAsync(uint id);
+ }
+}
diff --git a/src/SMAPI.Web/Framework/Clients/GitHub/GitHubClient.cs b/src/SMAPI.Web/Framework/Clients/GitHub/GitHubClient.cs
new file mode 100644
index 00000000..0b205660
--- /dev/null
+++ b/src/SMAPI.Web/Framework/Clients/GitHub/GitHubClient.cs
@@ -0,0 +1,70 @@
+using System;
+using System.Net;
+using System.Threading.Tasks;
+using Pathoschild.Http.Client;
+
+namespace StardewModdingAPI.Web.Framework.Clients.GitHub
+{
+ /// <summary>An HTTP client for fetching metadata from GitHub.</summary>
+ internal class GitHubClient : IGitHubClient
+ {
+ /*********
+ ** Properties
+ *********/
+ /// <summary>The URL for a GitHub releases API query excluding the base URL, where {0} is the repository owner and name.</summary>
+ private readonly string ReleaseUrlFormat;
+
+ /// <summary>The underlying HTTP client.</summary>
+ private readonly IClient Client;
+
+
+ /*********
+ ** Public methods
+ *********/
+ /// <summary>Construct an instance.</summary>
+ /// <param name="baseUrl">The base URL for the GitHub API.</param>
+ /// <param name="releaseUrlFormat">The URL for a GitHub releases API query excluding the <paramref name="baseUrl"/>, where {0} is the repository owner and name.</param>
+ /// <param name="userAgent">The user agent for the API client.</param>
+ /// <param name="acceptHeader">The Accept header value expected by the GitHub API.</param>
+ /// <param name="username">The username with which to authenticate to the GitHub API.</param>
+ /// <param name="password">The password with which to authenticate to the GitHub API.</param>
+ public GitHubClient(string baseUrl, string releaseUrlFormat, string userAgent, string acceptHeader, string username, string password)
+ {
+ this.ReleaseUrlFormat = releaseUrlFormat;
+
+ this.Client = new FluentClient(baseUrl)
+ .SetUserAgent(userAgent)
+ .AddDefault(req => req.WithHeader("Accept", acceptHeader));
+ if (!string.IsNullOrWhiteSpace(username))
+ this.Client = this.Client.SetBasicAuthentication(username, password);
+ }
+
+ /// <summary>Get the latest release for a GitHub repository.</summary>
+ /// <param name="repo">The repository key (like <c>Pathoschild/SMAPI</c>).</param>
+ /// <returns>Returns the latest release if found, else <c>null</c>.</returns>
+ public async Task<GitRelease> GetLatestReleaseAsync(string repo)
+ {
+ // validate key format
+ if (!repo.Contains("/") || repo.IndexOf("/", StringComparison.InvariantCultureIgnoreCase) != repo.LastIndexOf("/", StringComparison.InvariantCultureIgnoreCase))
+ throw new ArgumentException($"The value '{repo}' isn't a valid GitHub repository key, must be a username and project name like 'Pathoschild/SMAPI'.", nameof(repo));
+
+ // fetch info
+ try
+ {
+ return await this.Client
+ .GetAsync(string.Format(this.ReleaseUrlFormat, repo))
+ .As<GitRelease>();
+ }
+ catch (ApiException ex) when (ex.Status == HttpStatusCode.NotFound)
+ {
+ return null;
+ }
+ }
+
+ /// <summary>Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.</summary>
+ public void Dispose()
+ {
+ this.Client?.Dispose();
+ }
+ }
+}
diff --git a/src/SMAPI.Web/Framework/Clients/GitHub/GitRelease.cs b/src/SMAPI.Web/Framework/Clients/GitHub/GitRelease.cs
new file mode 100644
index 00000000..0a47f3b4
--- /dev/null
+++ b/src/SMAPI.Web/Framework/Clients/GitHub/GitRelease.cs
@@ -0,0 +1,19 @@
+using Newtonsoft.Json;
+
+namespace StardewModdingAPI.Web.Framework.Clients.GitHub
+{
+ /// <summary>A GitHub project release.</summary>
+ internal class GitRelease
+ {
+ /*********
+ ** Accessors
+ *********/
+ /// <summary>The display name.</summary>
+ [JsonProperty("name")]
+ public string Name { get; set; }
+
+ /// <summary>The semantic version string.</summary>
+ [JsonProperty("tag_name")]
+ public string Tag { get; set; }
+ }
+}
diff --git a/src/SMAPI.Web/Framework/Clients/GitHub/IGitHubClient.cs b/src/SMAPI.Web/Framework/Clients/GitHub/IGitHubClient.cs
new file mode 100644
index 00000000..6e8eadff
--- /dev/null
+++ b/src/SMAPI.Web/Framework/Clients/GitHub/IGitHubClient.cs
@@ -0,0 +1,17 @@
+using System;
+using System.Threading.Tasks;
+
+namespace StardewModdingAPI.Web.Framework.Clients.GitHub
+{
+ /// <summary>An HTTP client for fetching metadata from GitHub.</summary>
+ internal interface IGitHubClient : IDisposable
+ {
+ /*********
+ ** Methods
+ *********/
+ /// <summary>Get the latest release for a GitHub repository.</summary>
+ /// <param name="repo">The repository key (like <c>Pathoschild/SMAPI</c>).</param>
+ /// <returns>Returns the latest release if found, else <c>null</c>.</returns>
+ Task<GitRelease> GetLatestReleaseAsync(string repo);
+ }
+}
diff --git a/src/SMAPI.Web/Framework/Clients/Nexus/INexusClient.cs b/src/SMAPI.Web/Framework/Clients/Nexus/INexusClient.cs
new file mode 100644
index 00000000..e56e7af4
--- /dev/null
+++ b/src/SMAPI.Web/Framework/Clients/Nexus/INexusClient.cs
@@ -0,0 +1,17 @@
+using System;
+using System.Threading.Tasks;
+
+namespace StardewModdingAPI.Web.Framework.Clients.Nexus
+{
+ /// <summary>An HTTP client for fetching mod metadata from Nexus Mods.</summary>
+ internal interface INexusClient : IDisposable
+ {
+ /*********
+ ** Methods
+ *********/
+ /// <summary>Get metadata about a mod.</summary>
+ /// <param name="id">The Nexus mod ID.</param>
+ /// <returns>Returns the mod info if found, else <c>null</c>.</returns>
+ Task<NexusMod> GetModAsync(uint id);
+ }
+}
diff --git a/src/SMAPI.Web/Framework/Clients/Nexus/NexusClient.cs b/src/SMAPI.Web/Framework/Clients/Nexus/NexusClient.cs
new file mode 100644
index 00000000..1a7011e2
--- /dev/null
+++ b/src/SMAPI.Web/Framework/Clients/Nexus/NexusClient.cs
@@ -0,0 +1,48 @@
+using System.Threading.Tasks;
+using Pathoschild.Http.Client;
+
+namespace StardewModdingAPI.Web.Framework.Clients.Nexus
+{
+ /// <summary>An HTTP client for fetching mod metadata from Nexus Mods.</summary>
+ internal class NexusClient : INexusClient
+ {
+ /*********
+ ** Properties
+ *********/
+ /// <summary>The URL for a Nexus Mods API query excluding the base URL, where {0} is the mod ID.</summary>
+ private readonly string ModUrlFormat;
+
+ /// <summary>The underlying HTTP client.</summary>
+ private readonly IClient Client;
+
+
+ /*********
+ ** Public methods
+ *********/
+ /// <summary>Construct an instance.</summary>
+ /// <param name="userAgent">The user agent for the Nexus Mods API client.</param>
+ /// <param name="baseUrl">The base URL for the Nexus Mods API.</param>
+ /// <param name="modUrlFormat">The URL for a Nexus Mods API query excluding the <paramref name="baseUrl"/>, where {0} is the mod ID.</param>
+ public NexusClient(string userAgent, string baseUrl, string modUrlFormat)
+ {
+ this.ModUrlFormat = modUrlFormat;
+ this.Client = new FluentClient(baseUrl).SetUserAgent(userAgent);
+ }
+
+ /// <summary>Get metadata about a mod.</summary>
+ /// <param name="id">The Nexus mod ID.</param>
+ /// <returns>Returns the mod info if found, else <c>null</c>.</returns>
+ public async Task<NexusMod> GetModAsync(uint id)
+ {
+ return await this.Client
+ .GetAsync(string.Format(this.ModUrlFormat, id))
+ .As<NexusMod>();
+ }
+
+ /// <summary>Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.</summary>
+ public void Dispose()
+ {
+ this.Client?.Dispose();
+ }
+ }
+}
diff --git a/src/SMAPI.Web/Framework/Clients/Nexus/NexusMod.cs b/src/SMAPI.Web/Framework/Clients/Nexus/NexusMod.cs
new file mode 100644
index 00000000..2b04104f
--- /dev/null
+++ b/src/SMAPI.Web/Framework/Clients/Nexus/NexusMod.cs
@@ -0,0 +1,21 @@
+using Newtonsoft.Json;
+
+namespace StardewModdingAPI.Web.Framework.Clients.Nexus
+{
+ /// <summary>Mod metadata from Nexus Mods.</summary>
+ internal class NexusMod
+ {
+ /*********
+ ** Accessors
+ *********/
+ /// <summary>The mod name.</summary>
+ public string Name { get; set; }
+
+ /// <summary>The mod's semantic version number.</summary>
+ public string Version { get; set; }
+
+ /// <summary>The mod's web URL.</summary>
+ [JsonProperty("mod_page_uri")]
+ public string Url { get; set; }
+ }
+}
diff --git a/src/SMAPI.Web/Framework/ConfigModels/ApiClientsConfig.cs b/src/SMAPI.Web/Framework/ConfigModels/ApiClientsConfig.cs
new file mode 100644
index 00000000..19794920
--- /dev/null
+++ b/src/SMAPI.Web/Framework/ConfigModels/ApiClientsConfig.cs
@@ -0,0 +1,56 @@
+namespace StardewModdingAPI.Web.Framework.ConfigModels
+{
+ /// <summary>The config settings for the API clients.</summary>
+ internal class ApiClientsConfig
+ {
+ /*********
+ ** Accessors
+ *********/
+ /****
+ ** Generic
+ ****/
+ /// <summary>The user agent for API clients, where {0} is the SMAPI version.</summary>
+ public string UserAgent { get; set; }
+
+
+ /****
+ ** Chucklefish
+ ****/
+ /// <summary>The base URL for the Chucklefish mod site.</summary>
+ public string ChucklefishBaseUrl { get; set; }
+
+ /// <summary>The URL for a mod page on the Chucklefish mod site excluding the <see cref="GitHubBaseUrl"/>, where {0} is the mod ID.</summary>
+ public string ChucklefishModPageUrlFormat { get; set; }
+
+
+ /****
+ ** GitHub
+ ****/
+ /// <summary>The base URL for the GitHub API.</summary>
+ public string GitHubBaseUrl { get; set; }
+
+ /// <summary>The URL for a GitHub API latest-release query excluding the <see cref="GitHubBaseUrl"/>, where {0} is the organisation and project name.</summary>
+ public string GitHubReleaseUrlFormat { get; set; }
+
+ /// <summary>The Accept header value expected by the GitHub API.</summary>
+ public string GitHubAcceptHeader { get; set; }
+
+ /// <summary>The username with which to authenticate to the GitHub API (if any).</summary>
+ public string GitHubUsername { get; set; }
+
+ /// <summary>The password with which to authenticate to the GitHub API (if any).</summary>
+ public string GitHubPassword { get; set; }
+
+ /****
+ ** Nexus Mods
+ ****/
+ /// <summary>The user agent for the Nexus Mods API client.</summary>
+ public string NexusUserAgent { get; set; }
+
+ /// <summary>The base URL for the Nexus Mods API.</summary>
+ public string NexusBaseUrl { get; set; }
+
+ /// <summary>The URL for a Nexus Mods API query excluding the <see cref="NexusBaseUrl"/>, where {0} is the mod ID.</summary>
+ public string NexusModUrlFormat { get; set; }
+ }
+}
diff --git a/src/SMAPI.Web/Framework/ConfigModels/ModUpdateCheckConfig.cs b/src/SMAPI.Web/Framework/ConfigModels/ModUpdateCheckConfig.cs
index 2fb5b97e..58c3a100 100644
--- a/src/SMAPI.Web/Framework/ConfigModels/ModUpdateCheckConfig.cs
+++ b/src/SMAPI.Web/Framework/ConfigModels/ModUpdateCheckConfig.cs
@@ -6,9 +6,6 @@ namespace StardewModdingAPI.Web.Framework.ConfigModels
/*********
** Accessors
*********/
- /****
- ** General
- ****/
/// <summary>The number of minutes update checks should be cached before refetching them.</summary>
public int CacheMinutes { get; set; }
@@ -16,59 +13,13 @@ namespace StardewModdingAPI.Web.Framework.ConfigModels
/// <remarks>Derived from SMAPI's SemanticVersion implementation.</remarks>
public string SemanticVersionRegex { get; set; }
- /****
- ** Chucklefish mod site
- ****/
/// <summary>The repository key for the Chucklefish mod site.</summary>
public string ChucklefishKey { get; set; }
- /// <summary>The user agent for the Chucklefish API client, where {0} is the SMAPI version.</summary>
- public string ChucklefishUserAgent { get; set; }
-
- /// <summary>The base URL for the Chucklefish mod site.</summary>
- public string ChucklefishBaseUrl { get; set; }
-
- /// <summary>The URL for a mod page on the Chucklefish mod site excluding the <see cref="GitHubBaseUrl"/>, where {0} is the mod ID.</summary>
- public string ChucklefishModPageUrlFormat { get; set; }
-
-
- /****
- ** GitHub
- ****/
/// <summary>The repository key for Nexus Mods.</summary>
public string GitHubKey { get; set; }
- /// <summary>The user agent for the GitHub API client, where {0} is the SMAPI version.</summary>
- public string GitHubUserAgent { get; set; }
-
- /// <summary>The base URL for the GitHub API.</summary>
- public string GitHubBaseUrl { get; set; }
-
- /// <summary>The URL for a GitHub API latest-release query excluding the <see cref="GitHubBaseUrl"/>, where {0} is the organisation and project name.</summary>
- public string GitHubReleaseUrlFormat { get; set; }
-
- /// <summary>The Accept header value expected by the GitHub API.</summary>
- public string GitHubAcceptHeader { get; set; }
-
- /// <summary>The username with which to authenticate to the GitHub API (if any).</summary>
- public string GitHubUsername { get; set; }
-
- /// <summary>The password with which to authenticate to the GitHub API (if any).</summary>
- public string GitHubPassword { get; set; }
-
- /****
- ** Nexus Mods
- ****/
/// <summary>The repository key for Nexus Mods.</summary>
public string NexusKey { get; set; }
-
- /// <summary>The user agent for the Nexus Mods API client.</summary>
- public string NexusUserAgent { get; set; }
-
- /// <summary>The base URL for the Nexus Mods API.</summary>
- public string NexusBaseUrl { get; set; }
-
- /// <summary>The URL for a Nexus Mods API query excluding the <see cref="NexusBaseUrl"/>, where {0} is the mod ID.</summary>
- public string NexusModUrlFormat { get; set; }
}
}
diff --git a/src/SMAPI.Web/Framework/ModRepositories/ChucklefishRepository.cs b/src/SMAPI.Web/Framework/ModRepositories/ChucklefishRepository.cs
index 06ec58ed..266055a6 100644
--- a/src/SMAPI.Web/Framework/ModRepositories/ChucklefishRepository.cs
+++ b/src/SMAPI.Web/Framework/ModRepositories/ChucklefishRepository.cs
@@ -1,9 +1,7 @@
using System;
-using System.Net;
using System.Threading.Tasks;
-using HtmlAgilityPack;
-using Pathoschild.Http.Client;
using StardewModdingAPI.Common.Models;
+using StardewModdingAPI.Web.Framework.Clients.Chucklefish;
namespace StardewModdingAPI.Web.Framework.ModRepositories
{
@@ -13,14 +11,8 @@ namespace StardewModdingAPI.Web.Framework.ModRepositories
/*********
** Properties
*********/
- /// <summary>The base URL for the Chucklefish mod site.</summary>
- private readonly string BaseUrl;
-
- /// <summary>The URL for a mod page excluding the base URL, where {0} is the mod ID.</summary>
- private readonly string ModPageUrlFormat;
-
/// <summary>The underlying HTTP client.</summary>
- private readonly IClient Client;
+ private readonly IChucklefishClient Client;
/*********
@@ -28,15 +20,11 @@ namespace StardewModdingAPI.Web.Framework.ModRepositories
*********/
/// <summary>Construct an instance.</summary>
/// <param name="vendorKey">The unique key for this vendor.</param>
- /// <param name="userAgent">The user agent for the API client.</param>
- /// <param name="baseUrl">The base URL for the Chucklefish mod site.</param>
- /// <param name="modPageUrlFormat">The URL for a mod page excluding the <paramref name="baseUrl"/>, where {0} is the mod ID.</param>
- public ChucklefishRepository(string vendorKey, string userAgent, string baseUrl, string modPageUrlFormat)
+ /// <param name="client">The underlying HTTP client.</param>
+ public ChucklefishRepository(string vendorKey, IChucklefishClient client)
: base(vendorKey)
{
- this.BaseUrl = baseUrl;
- this.ModPageUrlFormat = modPageUrlFormat;
- this.Client = new FluentClient(baseUrl).SetUserAgent(userAgent);
+ this.Client = client;
}
/// <summary>Get metadata about a mod in the repository.</summary>
@@ -44,38 +32,18 @@ namespace StardewModdingAPI.Web.Framework.ModRepositories
public override async Task<ModInfoModel> GetModInfoAsync(string id)
{
// validate ID format
- if (!uint.TryParse(id, out uint _))
+ if (!uint.TryParse(id, out uint realID))
return new ModInfoModel($"The value '{id}' isn't a valid Chucklefish mod ID, must be an integer ID.");
// fetch info
try
{
- // fetch HTML
- string html;
- try
- {
- html = await this.Client
- .GetAsync(string.Format(this.ModPageUrlFormat, id))
- .AsString();
- }
- catch (ApiException ex) when (ex.Status == HttpStatusCode.NotFound)
- {
+ var mod = await this.Client.GetModAsync(realID);
+ if (mod == null)
return new ModInfoModel("Found no mod with this ID.");
- }
-
- // parse HTML
- var doc = new HtmlDocument();
- doc.LoadHtml(html);
-
- // extract mod info
- string url = new UriBuilder(new Uri(this.BaseUrl)) { Path = string.Format(this.ModPageUrlFormat, id) }.Uri.ToString();
- string name = doc.DocumentNode.SelectSingleNode("//meta[@name='twitter:title']").Attributes["content"].Value;
- if (name.StartsWith("[SMAPI] "))
- name = name.Substring("[SMAPI] ".Length);
- string version = doc.DocumentNode.SelectSingleNode("//h1/span").InnerText;
// create model
- return new ModInfoModel(name, this.NormaliseVersion(version), url);
+ return new ModInfoModel(mod.Name, this.NormaliseVersion(mod.Version), mod.Url);
}
catch (Exception ex)
{
diff --git a/src/SMAPI.Web/Framework/ModRepositories/GitHubRepository.cs b/src/SMAPI.Web/Framework/ModRepositories/GitHubRepository.cs
index 9d43adf0..7bad6127 100644
--- a/src/SMAPI.Web/Framework/ModRepositories/GitHubRepository.cs
+++ b/src/SMAPI.Web/Framework/ModRepositories/GitHubRepository.cs
@@ -1,9 +1,7 @@
using System;
-using System.Net;
using System.Threading.Tasks;
-using Newtonsoft.Json;
-using Pathoschild.Http.Client;
using StardewModdingAPI.Common.Models;
+using StardewModdingAPI.Web.Framework.Clients.GitHub;
namespace StardewModdingAPI.Web.Framework.ModRepositories
{
@@ -13,11 +11,8 @@ namespace StardewModdingAPI.Web.Framework.ModRepositories
/*********
** Properties
*********/
- /// <summary>The URL for a Nexus Mods API query excluding the base URL, where {0} is the mod ID.</summary>
- private readonly string ReleaseUrlFormat;
-
- /// <summary>The underlying HTTP client.</summary>
- private readonly IClient Client;
+ /// <summary>The underlying GitHub API client.</summary>
+ private readonly IGitHubClient Client;
/*********
@@ -25,22 +20,11 @@ namespace StardewModdingAPI.Web.Framework.ModRepositories
*********/
/// <summary>Construct an instance.</summary>
/// <param name="vendorKey">The unique key for this vendor.</param>
- /// <param name="baseUrl">The base URL for the Nexus Mods API.</param>
- /// <param name="releaseUrlFormat">The URL for a Nexus Mods API query excluding the <paramref name="baseUrl"/>, where {0} is the mod ID.</param>
- /// <param name="userAgent">The user agent for the API client.</param>
- /// <param name="acceptHeader">The Accept header value expected by the GitHub API.</param>
- /// <param name="username">The username with which to authenticate to the GitHub API.</param>
- /// <param name="password">The password with which to authenticate to the GitHub API.</param>
- public GitHubRepository(string vendorKey, string baseUrl, string releaseUrlFormat, string userAgent, string acceptHeader, string username, string password)
+ /// <param name="client">The underlying GitHub API client.</param>
+ public GitHubRepository(string vendorKey, IGitHubClient client)
: base(vendorKey)
{
- this.ReleaseUrlFormat = releaseUrlFormat;
-
- this.Client = new FluentClient(baseUrl)
- .SetUserAgent(userAgent)
- .AddDefault(req => req.WithHeader("Accept", acceptHeader));
- if (!string.IsNullOrWhiteSpace(username))
- this.Client = this.Client.SetBasicAuthentication(username, password);
+ this.Client = client;
}
/// <summary>Get metadata about a mod in the repository.</summary>
@@ -54,14 +38,10 @@ namespace StardewModdingAPI.Web.Framework.ModRepositories
// fetch info
try
{
- GitRelease release = await this.Client
- .GetAsync(string.Format(this.ReleaseUrlFormat, id))
- .As<GitRelease>();
- return new ModInfoModel(id, this.NormaliseVersion(release.Tag), $"https://github.com/{id}/releases");
- }
- catch (ApiException ex) when (ex.Status == HttpStatusCode.NotFound)
- {
- return new ModInfoModel("Found no mod with this ID.");
+ GitRelease release = await this.Client.GetLatestReleaseAsync(id);
+ return release != null
+ ? new ModInfoModel(id, this.NormaliseVersion(release.Tag), $"https://github.com/{id}/releases")
+ : new ModInfoModel("Found no mod with this ID.");
}
catch (Exception ex)
{
@@ -74,24 +54,5 @@ namespace StardewModdingAPI.Web.Framework.ModRepositories
{
this.Client.Dispose();
}
-
-
- /*********
- ** Private models
- *********/
- /// <summary>Metadata about a GitHub release tag.</summary>
- private class GitRelease
- {
- /*********
- ** Accessors
- *********/
- /// <summary>The display name.</summary>
- [JsonProperty("name")]
- public string Name { get; set; }
-
- /// <summary>The semantic version string.</summary>
- [JsonProperty("tag_name")]
- public string Tag { get; set; }
- }
}
}
diff --git a/src/SMAPI.Web/Framework/ModRepositories/NexusRepository.cs b/src/SMAPI.Web/Framework/ModRepositories/NexusRepository.cs
index 8a4bb0d8..cfa757ab 100644
--- a/src/SMAPI.Web/Framework/ModRepositories/NexusRepository.cs
+++ b/src/SMAPI.Web/Framework/ModRepositories/NexusRepository.cs
@@ -1,8 +1,7 @@
using System;
using System.Threading.Tasks;
-using Newtonsoft.Json;
-using Pathoschild.Http.Client;
using StardewModdingAPI.Common.Models;
+using StardewModdingAPI.Web.Framework.Clients.Nexus;
namespace StardewModdingAPI.Web.Framework.ModRepositories
{
@@ -12,11 +11,8 @@ namespace StardewModdingAPI.Web.Framework.ModRepositories
/*********
** Properties
*********/
- /// <summary>The URL for a Nexus Mods API query excluding the base URL, where {0} is the mod ID.</summary>
- private readonly string ModUrlFormat;
-
- /// <summary>The underlying HTTP client.</summary>
- private readonly IClient Client;
+ /// <summary>The underlying Nexus Mods API client.</summary>
+ private readonly INexusClient Client;
/*********
@@ -24,14 +20,11 @@ namespace StardewModdingAPI.Web.Framework.ModRepositories
*********/
/// <summary>Construct an instance.</summary>
/// <param name="vendorKey">The unique key for this vendor.</param>
- /// <param name="userAgent">The user agent for the Nexus Mods API client.</param>
- /// <param name="baseUrl">The base URL for the Nexus Mods API.</param>
- /// <param name="modUrlFormat">The URL for a Nexus Mods API query excluding the <paramref name="baseUrl"/>, where {0} is the mod ID.</param>
- public NexusRepository(string vendorKey, string userAgent, string baseUrl, string modUrlFormat)
+ /// <param name="client">The underlying Nexus Mods API client.</param>
+ public NexusRepository(string vendorKey, INexusClient client)
: base(vendorKey)
{
- this.ModUrlFormat = modUrlFormat;
- this.Client = new FluentClient(baseUrl).SetUserAgent(userAgent);
+ this.Client = client;
}
/// <summary>Get metadata about a mod in the repository.</summary>
@@ -39,18 +32,15 @@ namespace StardewModdingAPI.Web.Framework.ModRepositories
public override async Task<ModInfoModel> GetModInfoAsync(string id)
{
// validate ID format
- if (!uint.TryParse(id, out uint _))
+ if (!uint.TryParse(id, out uint nexusID))
return new ModInfoModel($"The value '{id}' isn't a valid Nexus mod ID, must be an integer ID.");
// fetch info
try
{
- NexusResponseModel response = await this.Client
- .GetAsync(string.Format(this.ModUrlFormat, id))
- .As<NexusResponseModel>();
-
- return response != null
- ? new ModInfoModel(response.Name, this.NormaliseVersion(response.Version), response.Url)
+ NexusMod mod = await this.Client.GetModAsync(nexusID);
+ return mod != null
+ ? new ModInfoModel(mod.Name, this.NormaliseVersion(mod.Version), mod.Url)
: new ModInfoModel("Found no mod with this ID.");
}
catch (Exception ex)
@@ -64,26 +54,5 @@ namespace StardewModdingAPI.Web.Framework.ModRepositories
{
this.Client.Dispose();
}
-
-
- /*********
- ** Private models
- *********/
- /// <summary>A mod metadata response from Nexus Mods.</summary>
- private class NexusResponseModel
- {
- /*********
- ** Accessors
- *********/
- /// <summary>The mod name.</summary>
- public string Name { get; set; }
-
- /// <summary>The mod's semantic version number.</summary>
- public string Version { get; set; }
-
- /// <summary>The mod's web URL.</summary>
- [JsonProperty("mod_page_uri")]
- public string Url { get; set; }
- }
}
}