diff --git a/ArchiSteamFarm/ArchiSteamFarm.csproj b/ArchiSteamFarm/ArchiSteamFarm.csproj
index 6c0842b8d..a1bbd4cfd 100644
--- a/ArchiSteamFarm/ArchiSteamFarm.csproj
+++ b/ArchiSteamFarm/ArchiSteamFarm.csproj
@@ -11,8 +11,9 @@
ASF is an application that allows you to farm steam cards using multiple steam accounts simultaneously.
none
3.4.0.3
+ true
latest
-
+ 1591
Exe
https://github.com/JustArchi/ArchiSteamFarm/raw/master/resources/ASF.ico
http://www.apache.org/licenses/LICENSE-2.0
@@ -54,6 +55,7 @@
+
diff --git a/ArchiSteamFarm/BotConfig.cs b/ArchiSteamFarm/BotConfig.cs
index 12d872a87..17c8da449 100644
--- a/ArchiSteamFarm/BotConfig.cs
+++ b/ArchiSteamFarm/BotConfig.cs
@@ -33,7 +33,7 @@ using SteamKit2;
namespace ArchiSteamFarm {
[SuppressMessage("ReSharper", "ClassCannotBeInstantiated")]
- internal sealed class BotConfig {
+ public sealed class BotConfig {
private const bool DefaultAcceptGifts = false;
private const bool DefaultAutoSteamSaleEvent = false;
private const EBotBehaviour DefaultBotBehaviour = EBotBehaviour.None;
diff --git a/ArchiSteamFarm/IPC/Controllers/Api/ASFController.cs b/ArchiSteamFarm/IPC/Controllers/Api/ASFController.cs
index a4f021a58..a1fe2c457 100644
--- a/ArchiSteamFarm/IPC/Controllers/Api/ASFController.cs
+++ b/ArchiSteamFarm/IPC/Controllers/Api/ASFController.cs
@@ -30,8 +30,12 @@ using Microsoft.AspNetCore.Mvc;
namespace ArchiSteamFarm.IPC.Controllers.Api {
[ApiController]
+ [Produces("application/json")]
[Route("Api/ASF")]
public sealed class ASFController : ControllerBase {
+ ///
+ /// Fetches common info related to ASF as a whole.
+ ///
[HttpGet]
public ActionResult> ASFGet() {
uint memoryUsage = (uint) GC.GetTotalMemory(false) / 1024;
@@ -46,6 +50,9 @@ namespace ArchiSteamFarm.IPC.Controllers.Api {
return Ok(new GenericResponse(result));
}
+ ///
+ /// Updates ASF's global config.
+ ///
[HttpPost]
public async Task> ASFPost([FromBody] ASFRequest request) {
if (request == null) {
@@ -70,18 +77,27 @@ namespace ArchiSteamFarm.IPC.Controllers.Api {
return Ok(new GenericResponse(result));
}
+ ///
+ /// Makes ASF shutdown itself.
+ ///
[HttpPost("Exit")]
public ActionResult ExitPost() {
(bool success, string output) = Actions.Exit();
return Ok(new GenericResponse(success, output));
}
+ ///
+ /// Makes ASF restart itself.
+ ///
[HttpPost("Restart")]
public ActionResult RestartPost() {
(bool success, string output) = Actions.Restart();
return Ok(new GenericResponse(success, output));
}
+ ///
+ /// Makes ASF update itself.
+ ///
[HttpPost("Update")]
public async Task>> UpdatePost() {
(bool success, Version version) = await Actions.Update().ConfigureAwait(false);
diff --git a/ArchiSteamFarm/IPC/Controllers/Api/BotController.cs b/ArchiSteamFarm/IPC/Controllers/Api/BotController.cs
index f0086e5f1..7c750bbb5 100644
--- a/ArchiSteamFarm/IPC/Controllers/Api/BotController.cs
+++ b/ArchiSteamFarm/IPC/Controllers/Api/BotController.cs
@@ -32,8 +32,12 @@ using Microsoft.AspNetCore.Mvc;
namespace ArchiSteamFarm.IPC.Controllers.Api {
[ApiController]
+ [Produces("application/json")]
[Route("Api/Bot")]
public sealed class BotController : ControllerBase {
+ ///
+ /// Deletes all files related to given bots.
+ ///
[HttpDelete("{botNames:required}")]
public async Task> BotDelete(string botNames) {
if (string.IsNullOrEmpty(botNames)) {
@@ -50,6 +54,9 @@ namespace ArchiSteamFarm.IPC.Controllers.Api {
return Ok(new GenericResponse(results.All(result => result)));
}
+ ///
+ /// Fetches common info related to given bots.
+ ///
[HttpGet("{botNames:required}")]
public ActionResult>> BotGet(string botNames) {
if (string.IsNullOrEmpty(botNames)) {
@@ -65,6 +72,9 @@ namespace ArchiSteamFarm.IPC.Controllers.Api {
return Ok(new GenericResponse>(bots));
}
+ ///
+ /// Updates bot config of given bot.
+ ///
[HttpPost("{botName:required}")]
public async Task> BotPost(string botName, [FromBody] BotRequest request) {
if (string.IsNullOrEmpty(botName) || (request == null)) {
@@ -99,6 +109,9 @@ namespace ArchiSteamFarm.IPC.Controllers.Api {
return Ok(new GenericResponse(result));
}
+ ///
+ /// Removes BGR output files of given bots.
+ ///
[HttpDelete("{botNames:required}/GamesToRedeemInBackground")]
public async Task> GamesToRedeemInBackgroundDelete(string botNames) {
if (string.IsNullOrEmpty(botNames)) {
@@ -115,6 +128,9 @@ namespace ArchiSteamFarm.IPC.Controllers.Api {
return Ok(results.All(result => result) ? new GenericResponse(true) : new GenericResponse(false, Strings.WarningFailed));
}
+ ///
+ /// Fetches BGR output files of given bots.
+ ///
[HttpGet("{botNames:required}/GamesToRedeemInBackground")]
public async Task>>> GamesToRedeemInBackgroundGet(string botNames) {
if (string.IsNullOrEmpty(botNames)) {
@@ -139,6 +155,9 @@ namespace ArchiSteamFarm.IPC.Controllers.Api {
return Ok(new GenericResponse>(result));
}
+ ///
+ /// Adds keys to redeem using BGR to given bot.
+ ///
[HttpPost("{botName:required}/GamesToRedeemInBackground")]
public async Task>> GamesToRedeemInBackgroundPost(string botName, [FromBody] GamesToRedeemInBackgroundRequest request) {
if (string.IsNullOrEmpty(botName) || (request == null)) {
@@ -158,6 +177,9 @@ namespace ArchiSteamFarm.IPC.Controllers.Api {
return Ok(new GenericResponse(result, request.GamesToRedeemInBackground));
}
+ ///
+ /// Pauses given bots.
+ ///
[HttpPost("{botNames:required}/Pause")]
public async Task> PausePost(string botNames, [FromBody] BotPauseRequest request) {
if (string.IsNullOrEmpty(botNames) || (request == null)) {
@@ -174,6 +196,9 @@ namespace ArchiSteamFarm.IPC.Controllers.Api {
return Ok(new GenericResponse(results.All(result => result.Success), string.Join(Environment.NewLine, results.Select(result => result.Output))));
}
+ ///
+ /// Resumes given bots.
+ ///
[HttpPost("{botNames:required}/Resume")]
public async Task> ResumePost(string botNames) {
if (string.IsNullOrEmpty(botNames)) {
@@ -190,6 +215,9 @@ namespace ArchiSteamFarm.IPC.Controllers.Api {
return Ok(new GenericResponse(results.All(result => result.Success), string.Join(Environment.NewLine, results.Select(result => result.Output))));
}
+ ///
+ /// Starts given bots.
+ ///
[HttpPost("{botNames:required}/Start")]
public async Task> StartPost(string botNames) {
if (string.IsNullOrEmpty(botNames)) {
@@ -206,6 +234,9 @@ namespace ArchiSteamFarm.IPC.Controllers.Api {
return Ok(new GenericResponse(results.All(result => result.Success), string.Join(Environment.NewLine, results.Select(result => result.Output))));
}
+ ///
+ /// Stops given bots.
+ ///
[HttpPost("{botNames:required}/Stop")]
public async Task> StopPost(string botNames) {
if (string.IsNullOrEmpty(botNames)) {
diff --git a/ArchiSteamFarm/IPC/Controllers/Api/CommandController.cs b/ArchiSteamFarm/IPC/Controllers/Api/CommandController.cs
index e1eb3756e..e2c1915da 100644
--- a/ArchiSteamFarm/IPC/Controllers/Api/CommandController.cs
+++ b/ArchiSteamFarm/IPC/Controllers/Api/CommandController.cs
@@ -28,8 +28,16 @@ using Microsoft.AspNetCore.Mvc;
namespace ArchiSteamFarm.IPC.Controllers.Api {
[ApiController]
+ [Produces("application/json")]
[Route("Api/Command")]
public sealed class CommandController : ControllerBase {
+ ///
+ /// Executes a command.
+ ///
+ ///
+ /// This API endpoint is supposed to be entirely replaced by ASF actions available under /Api/ASF/{action} and /Api/Bot/{bot}/{action}.
+ /// You should use "given bot" commands when executing this endpoint, omitting targets of the command will cause the command to be executed on first defined bot
+ ///
[HttpPost("{command:required}")]
public async Task>> CommandPost(string command) {
if (string.IsNullOrEmpty(command)) {
diff --git a/ArchiSteamFarm/IPC/Controllers/Api/NLogController.cs b/ArchiSteamFarm/IPC/Controllers/Api/NLogController.cs
index 240574aaa..a564c9184 100644
--- a/ArchiSteamFarm/IPC/Controllers/Api/NLogController.cs
+++ b/ArchiSteamFarm/IPC/Controllers/Api/NLogController.cs
@@ -33,10 +33,17 @@ using Newtonsoft.Json;
namespace ArchiSteamFarm.IPC.Controllers.Api {
[ApiController]
+ [Produces("application/json")]
[Route("Api/NLog")]
public sealed class NLogController : ControllerBase {
private static readonly ConcurrentDictionary ActiveLogWebSockets = new ConcurrentDictionary();
+ ///
+ /// Fetches ASF log in realtime.
+ ///
+ ///
+ /// This API endpoint requires a websocket connection.
+ ///
[HttpGet]
public async Task NLogGet() {
if (!HttpContext.WebSockets.IsWebSocketRequest) {
diff --git a/ArchiSteamFarm/IPC/Controllers/Api/StructureController.cs b/ArchiSteamFarm/IPC/Controllers/Api/StructureController.cs
index dda5a76ec..1665d9038 100644
--- a/ArchiSteamFarm/IPC/Controllers/Api/StructureController.cs
+++ b/ArchiSteamFarm/IPC/Controllers/Api/StructureController.cs
@@ -26,8 +26,15 @@ using Microsoft.AspNetCore.Mvc;
namespace ArchiSteamFarm.IPC.Controllers.Api {
[ApiController]
+ [Produces("application/json")]
[Route("Api/Structure")]
public sealed class StructureController : ControllerBase {
+ ///
+ /// Fetches structure of given type.
+ ///
+ ///
+ /// Structure is defined as a representation of given object in its default state.
+ ///
[HttpGet("{structure:required}")]
public ActionResult> StructureGet(string structure) {
if (string.IsNullOrEmpty(structure)) {
diff --git a/ArchiSteamFarm/IPC/Controllers/Api/TypeController.cs b/ArchiSteamFarm/IPC/Controllers/Api/TypeController.cs
index 9817636dc..f9baf89c4 100644
--- a/ArchiSteamFarm/IPC/Controllers/Api/TypeController.cs
+++ b/ArchiSteamFarm/IPC/Controllers/Api/TypeController.cs
@@ -30,8 +30,15 @@ using Newtonsoft.Json;
namespace ArchiSteamFarm.IPC.Controllers.Api {
[ApiController]
+ [Produces("application/json")]
[Route("Api/Type")]
public sealed class TypeController : ControllerBase {
+ ///
+ /// Fetches type info of given type.
+ ///
+ ///
+ /// Type info is defined as a representation of given object with its fields and properties being assigned to a string value that defines their type.
+ ///
[HttpGet("{type:required}")]
public ActionResult> TypeGet(string type) {
if (string.IsNullOrEmpty(type)) {
diff --git a/ArchiSteamFarm/IPC/Controllers/Api/WWWController.cs b/ArchiSteamFarm/IPC/Controllers/Api/WWWController.cs
index 629d78c47..b33898862 100644
--- a/ArchiSteamFarm/IPC/Controllers/Api/WWWController.cs
+++ b/ArchiSteamFarm/IPC/Controllers/Api/WWWController.cs
@@ -31,8 +31,15 @@ using Microsoft.AspNetCore.Mvc;
namespace ArchiSteamFarm.IPC.Controllers.Api {
[ApiController]
+ [Produces("application/json")]
[Route("Api/WWW")]
public sealed class WWWController : ControllerBase {
+ ///
+ /// Fetches files in given directory relative to WWW root.
+ ///
+ ///
+ /// This is internal API being utilizied by our ASF-ui IPC frontend. You should not depend on existence of any /Api/WWW as they can disappear and change anytime.
+ ///
[HttpGet("Directory/{directory:required}")]
public ActionResult>> DirectoryGet(string directory) {
if (string.IsNullOrEmpty(directory)) {
@@ -57,6 +64,12 @@ namespace ArchiSteamFarm.IPC.Controllers.Api {
return Ok(new GenericResponse>(result));
}
+ ///
+ /// Fetches newest GitHub releases 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 as they can disappear and change anytime.
+ ///
[HttpGet("GitHub/Releases")]
public async Task>>> GitHubReleasesGet([FromQuery] byte count = 10) {
if (count == 0) {
@@ -72,6 +85,12 @@ namespace ArchiSteamFarm.IPC.Controllers.Api {
return Ok(new GenericResponse>(result));
}
+ ///
+ /// Fetches specific GitHub release 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 as they can disappear and change anytime.
+ ///
[HttpGet("GitHub/Releases/{version:required}")]
public async Task>> GitHubReleasesGet(string version) {
if (string.IsNullOrEmpty(version)) {
@@ -86,6 +105,12 @@ namespace ArchiSteamFarm.IPC.Controllers.Api {
return Ok(new GenericResponse(new GitHubReleaseResponse(releaseResponse)));
}
+ ///
+ /// Sends a HTTPS request through ASF's built-in HttpClient.
+ ///
+ ///
+ /// This is internal API being utilizied by our ASF-ui IPC frontend. You should not depend on existence of any /Api/WWW as they can disappear and change anytime.
+ ///
[HttpPost("Send")]
public async Task>> SendPost([FromBody] WWWSendRequest request) {
if (request == null) {
diff --git a/ArchiSteamFarm/IPC/Requests/ASFRequest.cs b/ArchiSteamFarm/IPC/Requests/ASFRequest.cs
index 48590a9ed..bd00e9fee 100644
--- a/ArchiSteamFarm/IPC/Requests/ASFRequest.cs
+++ b/ArchiSteamFarm/IPC/Requests/ASFRequest.cs
@@ -19,14 +19,19 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+using System.ComponentModel.DataAnnotations;
using System.Diagnostics.CodeAnalysis;
using Newtonsoft.Json;
namespace ArchiSteamFarm.IPC.Requests {
[SuppressMessage("ReSharper", "ClassCannotBeInstantiated")]
public sealed class ASFRequest {
+ ///
+ /// ASF's global config structure.
+ ///
[JsonProperty(Required = Required.Always)]
- internal readonly GlobalConfig GlobalConfig;
+ [Required]
+ public readonly GlobalConfig GlobalConfig;
// Deserialized from JSON
private ASFRequest() { }
diff --git a/ArchiSteamFarm/IPC/Requests/BotPauseRequest.cs b/ArchiSteamFarm/IPC/Requests/BotPauseRequest.cs
index 679eb245e..a7c561189 100644
--- a/ArchiSteamFarm/IPC/Requests/BotPauseRequest.cs
+++ b/ArchiSteamFarm/IPC/Requests/BotPauseRequest.cs
@@ -25,11 +25,17 @@ using Newtonsoft.Json;
namespace ArchiSteamFarm.IPC.Requests {
[SuppressMessage("ReSharper", "ClassCannotBeInstantiated")]
public sealed class BotPauseRequest {
+ ///
+ /// Specifies if pause is permanent or temporary (default).
+ ///
[JsonProperty(Required = Required.DisallowNull)]
- internal readonly bool Permanent;
+ public readonly bool Permanent;
+ ///
+ /// Specifies automatic resume action in given seconds. Default value of 0 disables automatic resume.
+ ///
[JsonProperty(Required = Required.DisallowNull)]
- internal readonly ushort ResumeInSeconds;
+ public readonly ushort ResumeInSeconds;
// Deserialized from JSON
private BotPauseRequest() { }
diff --git a/ArchiSteamFarm/IPC/Requests/BotRequest.cs b/ArchiSteamFarm/IPC/Requests/BotRequest.cs
index 0958815d5..e67057fb6 100644
--- a/ArchiSteamFarm/IPC/Requests/BotRequest.cs
+++ b/ArchiSteamFarm/IPC/Requests/BotRequest.cs
@@ -19,14 +19,19 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+using System.ComponentModel.DataAnnotations;
using System.Diagnostics.CodeAnalysis;
using Newtonsoft.Json;
namespace ArchiSteamFarm.IPC.Requests {
[SuppressMessage("ReSharper", "ClassCannotBeInstantiated")]
public sealed class BotRequest {
+ ///
+ /// ASF's bot config structure.
+ ///
[JsonProperty(Required = Required.Always)]
- internal readonly BotConfig BotConfig;
+ [Required]
+ public readonly BotConfig BotConfig;
// Deserialized from JSON
private BotRequest() { }
diff --git a/ArchiSteamFarm/IPC/Requests/GamesToRedeemInBackgroundRequest.cs b/ArchiSteamFarm/IPC/Requests/GamesToRedeemInBackgroundRequest.cs
index d6034e937..11bb6fffb 100644
--- a/ArchiSteamFarm/IPC/Requests/GamesToRedeemInBackgroundRequest.cs
+++ b/ArchiSteamFarm/IPC/Requests/GamesToRedeemInBackgroundRequest.cs
@@ -20,14 +20,23 @@
// limitations under the License.
using System.Collections.Specialized;
+using System.ComponentModel.DataAnnotations;
using System.Diagnostics.CodeAnalysis;
using Newtonsoft.Json;
namespace ArchiSteamFarm.IPC.Requests {
[SuppressMessage("ReSharper", "ClassCannotBeInstantiated")]
public sealed class GamesToRedeemInBackgroundRequest {
+ ///
+ /// A string-string map that maps cd-key to redeem (key) to its name (value).
+ ///
+ ///
+ /// Key in the map must be a valid and unique Steam cd-key.
+ /// Value in the map must be a non-null and non-empty name of the key (e.g. game's name, but can be anything).
+ ///
[JsonProperty(Required = Required.Always)]
- internal readonly OrderedDictionary GamesToRedeemInBackground;
+ [Required]
+ public readonly OrderedDictionary GamesToRedeemInBackground;
// Deserialized from JSON
private GamesToRedeemInBackgroundRequest() { }
diff --git a/ArchiSteamFarm/IPC/Requests/WWWSendRequest.cs b/ArchiSteamFarm/IPC/Requests/WWWSendRequest.cs
index 416c14aa1..1da581b43 100644
--- a/ArchiSteamFarm/IPC/Requests/WWWSendRequest.cs
+++ b/ArchiSteamFarm/IPC/Requests/WWWSendRequest.cs
@@ -19,14 +19,22 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+using System.ComponentModel.DataAnnotations;
using System.Diagnostics.CodeAnalysis;
using Newtonsoft.Json;
namespace ArchiSteamFarm.IPC.Requests {
[SuppressMessage("ReSharper", "ClassCannotBeInstantiated")]
public sealed class WWWSendRequest {
+ ///
+ /// Full URL of the request to be made.
+ ///
+ ///
+ /// URL must start from https:// scheme.
+ ///
+ [Required]
[JsonProperty(Required = Required.Always)]
- internal readonly string URL;
+ public readonly string URL;
// Deserialized from JSON
private WWWSendRequest() { }
diff --git a/ArchiSteamFarm/IPC/Responses/ASFResponse.cs b/ArchiSteamFarm/IPC/Responses/ASFResponse.cs
index de10ead13..9ccd992bd 100644
--- a/ArchiSteamFarm/IPC/Responses/ASFResponse.cs
+++ b/ArchiSteamFarm/IPC/Responses/ASFResponse.cs
@@ -20,24 +20,45 @@
// limitations under the License.
using System;
+using System.ComponentModel.DataAnnotations;
using Newtonsoft.Json;
namespace ArchiSteamFarm.IPC.Responses {
public sealed class ASFResponse {
- [JsonProperty]
- private readonly string BuildVariant;
+ ///
+ /// ASF's build variant.
+ ///
+ [JsonProperty(Required = Required.Always)]
+ [Required]
+ public readonly string BuildVariant;
- [JsonProperty]
- private readonly GlobalConfig GlobalConfig;
+ ///
+ /// Currently loaded ASF's global config.
+ ///
+ [JsonProperty(Required = Required.Always)]
+ [Required]
+ public readonly GlobalConfig GlobalConfig;
- [JsonProperty]
- private readonly uint MemoryUsage;
+ ///
+ /// Current amount of managed memory being used by the process, in kilobytes.
+ ///
+ [JsonProperty(Required = Required.Always)]
+ [Required]
+ public readonly uint MemoryUsage;
- [JsonProperty]
- private readonly DateTime ProcessStartTime;
+ ///
+ /// Start date of the process.
+ ///
+ [JsonProperty(Required = Required.Always)]
+ [Required]
+ public readonly DateTime ProcessStartTime;
- [JsonProperty]
- private readonly Version Version;
+ ///
+ /// ASF version of currently running binary.
+ ///
+ [JsonProperty(Required = Required.Always)]
+ [Required]
+ public readonly Version Version;
internal ASFResponse(string buildVariant, GlobalConfig globalConfig, uint memoryUsage, DateTime processStartTime, Version version) {
if (string.IsNullOrEmpty(buildVariant) || (globalConfig == null) || (memoryUsage == 0) || (processStartTime == DateTime.MinValue) || (version == null)) {
diff --git a/ArchiSteamFarm/IPC/Responses/GamesToRedeemInBackgroundResponse.cs b/ArchiSteamFarm/IPC/Responses/GamesToRedeemInBackgroundResponse.cs
index 6829c96b1..21f9cd848 100644
--- a/ArchiSteamFarm/IPC/Responses/GamesToRedeemInBackgroundResponse.cs
+++ b/ArchiSteamFarm/IPC/Responses/GamesToRedeemInBackgroundResponse.cs
@@ -24,11 +24,17 @@ using Newtonsoft.Json;
namespace ArchiSteamFarm.IPC.Responses {
public sealed class GamesToRedeemInBackgroundResponse {
+ ///
+ /// Keys that were redeemed and not used during the process, if available.
+ ///
[JsonProperty]
- private readonly Dictionary UnusedKeys;
+ public readonly Dictionary UnusedKeys;
+ ///
+ /// Keys that were redeemed and used during the process, if available.
+ ///
[JsonProperty]
- private readonly Dictionary UsedKeys;
+ public readonly Dictionary UsedKeys;
internal GamesToRedeemInBackgroundResponse(Dictionary unusedKeys = null, Dictionary usedKeys = null) {
UnusedKeys = unusedKeys;
diff --git a/ArchiSteamFarm/IPC/Responses/GenericResponse.cs b/ArchiSteamFarm/IPC/Responses/GenericResponse.cs
index edf7516f5..8eeddf240 100644
--- a/ArchiSteamFarm/IPC/Responses/GenericResponse.cs
+++ b/ArchiSteamFarm/IPC/Responses/GenericResponse.cs
@@ -19,13 +19,20 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+using System.ComponentModel.DataAnnotations;
using ArchiSteamFarm.Localization;
using Newtonsoft.Json;
namespace ArchiSteamFarm.IPC.Responses {
public sealed class GenericResponse : GenericResponse where T : class {
+ ///
+ /// The actual result of the request, if available.
+ ///
+ ///
+ /// The type of the result depends on the API endpoint that you've called.
+ ///
[JsonProperty]
- private readonly T Result;
+ public readonly T Result;
internal GenericResponse(T result) : base(result != null) => Result = result;
internal GenericResponse(bool success, string message) : base(success, message) { }
@@ -33,11 +40,21 @@ namespace ArchiSteamFarm.IPC.Responses {
}
public class GenericResponse {
+ ///
+ /// A message that describes what happened with the request, if available.
+ ///
+ ///
+ /// This property will provide exact reason for majority of expected failures.
+ ///
[JsonProperty]
- private readonly string Message;
+ public readonly string Message;
- [JsonProperty]
- private readonly bool Success;
+ ///
+ /// Boolean type that specifies if the request has succeeded.
+ ///
+ [JsonProperty(Required = Required.Always)]
+ [Required]
+ public readonly bool Success;
internal GenericResponse(bool success, string message = null) {
Success = success;
diff --git a/ArchiSteamFarm/IPC/Responses/GitHubReleaseResponse.cs b/ArchiSteamFarm/IPC/Responses/GitHubReleaseResponse.cs
index 9d29cf78b..9f51ed5a0 100644
--- a/ArchiSteamFarm/IPC/Responses/GitHubReleaseResponse.cs
+++ b/ArchiSteamFarm/IPC/Responses/GitHubReleaseResponse.cs
@@ -20,21 +20,38 @@
// limitations under the License.
using System;
+using System.ComponentModel.DataAnnotations;
using Newtonsoft.Json;
namespace ArchiSteamFarm.IPC.Responses {
public sealed class GitHubReleaseResponse {
- [JsonProperty]
- private readonly string ChangelogHTML;
+ ///
+ /// Changelog of the release rendered in HTML.
+ ///
+ [JsonProperty(Required = Required.Always)]
+ [Required]
+ public readonly string ChangelogHTML;
- [JsonProperty]
- private readonly DateTime ReleasedAt;
+ ///
+ /// Date of the release.
+ ///
+ [JsonProperty(Required = Required.Always)]
+ [Required]
+ public readonly DateTime ReleasedAt;
- [JsonProperty]
- private readonly bool Stable;
+ ///
+ /// Boolean value that specifies whether the build is stable or not (pre-release).
+ ///
+ [JsonProperty(Required = Required.Always)]
+ [Required]
+ public readonly bool Stable;
- [JsonProperty]
- private readonly string Version;
+ ///
+ /// Version of the release.
+ ///
+ [JsonProperty(Required = Required.Always)]
+ [Required]
+ public readonly string Version;
internal GitHubReleaseResponse(GitHub.ReleaseResponse releaseResponse) {
if (releaseResponse == null) {
diff --git a/ArchiSteamFarm/IPC/Responses/TypeResponse.cs b/ArchiSteamFarm/IPC/Responses/TypeResponse.cs
index 22f204287..4177237df 100644
--- a/ArchiSteamFarm/IPC/Responses/TypeResponse.cs
+++ b/ArchiSteamFarm/IPC/Responses/TypeResponse.cs
@@ -21,15 +21,29 @@
using System;
using System.Collections.Generic;
+using System.ComponentModel.DataAnnotations;
using Newtonsoft.Json;
namespace ArchiSteamFarm.IPC.Responses {
public sealed class TypeResponse {
- [JsonProperty]
- private readonly Dictionary Body;
+ ///
+ /// A string-string map representing a decomposition of given type.
+ ///
+ ///
+ /// The actual structure of this field depends on the type that was requested. You can determine that type based on metadata.
+ /// For enums, keys are friendly names while values are underlying values of those names.
+ /// For objects, keys are non-private fields and properties, while values are underlying types of those.
+ ///
+ [JsonProperty(Required = Required.Always)]
+ [Required]
+ public readonly Dictionary Body;
- [JsonProperty]
- private readonly TypeProperties Properties;
+ ///
+ /// Metadata of given type.
+ ///
+ [JsonProperty(Required = Required.Always)]
+ [Required]
+ public readonly TypeProperties Properties;
internal TypeResponse(Dictionary body, TypeProperties properties) {
if ((body == null) || (properties == null)) {
@@ -40,15 +54,33 @@ namespace ArchiSteamFarm.IPC.Responses {
Properties = properties;
}
- internal sealed class TypeProperties {
+ public sealed class TypeProperties {
+ ///
+ /// Base type of given type, if available.
+ ///
+ ///
+ /// This can be used for determining how should be interpreted.
+ ///
[JsonProperty]
- private readonly string BaseType;
+ public readonly string BaseType;
+ ///
+ /// Custom attributes of given type, if available.
+ ///
+ ///
+ /// This can be used for determining main enum type if is .
+ ///
[JsonProperty]
- private readonly HashSet CustomAttributes;
+ public readonly HashSet CustomAttributes;
+ ///
+ /// Underlying type of given type, if available.
+ ///
+ ///
+ /// This can be used for determining underlying enum type if is .
+ ///
[JsonProperty]
- private readonly string UnderlyingType;
+ public readonly string UnderlyingType;
internal TypeProperties(string baseType = null, HashSet customAttributes = null, string underlyingType = null) {
BaseType = baseType;
diff --git a/ArchiSteamFarm/IPC/Startup.cs b/ArchiSteamFarm/IPC/Startup.cs
index db1d7385f..733f18c4f 100644
--- a/ArchiSteamFarm/IPC/Startup.cs
+++ b/ArchiSteamFarm/IPC/Startup.cs
@@ -20,6 +20,7 @@
// limitations under the License.
using System;
+using System.IO;
using ArchiSteamFarm.IPC.Middleware;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
@@ -29,6 +30,7 @@ using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
+using Swashbuckle.AspNetCore.Swagger;
namespace ArchiSteamFarm.IPC {
internal sealed class Startup {
@@ -64,6 +66,12 @@ namespace ArchiSteamFarm.IPC {
// We need MVC for /Api
app.UseMvcWithDefaultRoute();
+ // Use swagger for automatic API documentation generation
+ app.UseSwagger();
+
+ // Use friendly swagger UI
+ app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/ASF/swagger.json", "ASF API"));
+
// We're using index for URL routing in our static files so re-execute all non-API calls on /
app.UseWhen(context => !context.Request.Path.StartsWithSegments("/Api", StringComparison.OrdinalIgnoreCase), appBuilder => appBuilder.UseStatusCodePagesWithReExecute("/"));
@@ -83,9 +91,26 @@ namespace ArchiSteamFarm.IPC {
// Add support for response compression
services.AddResponseCompression();
+ // Add swagger documentation generation
+ services.AddSwaggerGen(
+ c => {
+ c.DescribeAllEnumsAsStrings();
+ c.SwaggerDoc("ASF", new Info { Title = "ASF API" });
+
+ string xmlDocumentationFile = Path.Combine(AppContext.BaseDirectory, SharedInfo.AssemblyDocumentation);
+
+ if (File.Exists(xmlDocumentationFile)) {
+ c.IncludeXmlComments(xmlDocumentationFile);
+ }
+ }
+ );
+
// We need MVC for /Api, but we're going to use only a small subset of all available features
IMvcCoreBuilder mvc = services.AddMvcCore();
+ // Add API explorer for swagger
+ mvc.AddApiExplorer();
+
// Use latest compatibility version for MVC
mvc.SetCompatibilityVersion(CompatibilityVersion.Latest);
diff --git a/ArchiSteamFarm/SharedInfo.cs b/ArchiSteamFarm/SharedInfo.cs
index ac9d846c2..84475605b 100644
--- a/ArchiSteamFarm/SharedInfo.cs
+++ b/ArchiSteamFarm/SharedInfo.cs
@@ -29,6 +29,7 @@ namespace ArchiSteamFarm {
internal const ulong ArchiSteamID = 76561198006963719;
internal const string ASF = nameof(ASF);
internal const ulong ASFGroupSteamID = 103582791440160998;
+ internal const string AssemblyDocumentation = AssemblyName + ".xml";
internal const string AssemblyName = nameof(ArchiSteamFarm);
internal const string ConfigDirectory = "config";
internal const string ConfigExtension = ".json";