mirror of
https://github.com/JustArchiNET/ArchiSteamFarm
synced 2024-11-10 07:04:27 +00:00
Rewrite IPC from HttpListener to KestrelHttpServer (#898)
* Debug tests * Update * Add support for IPCPassword * Misc * Misc * Update * Misc * Cut dependencies to bare minimum * Update * Update * Update * Update * Add support for websockets * Cleanup and preparation for merge * Add missing mapping of / -> index.html * Add support for custom path + misc * Misc * Declare latest compatibility version * Fix harmless error on /Api/Log websocket disconnect
This commit is contained in:
parent
5b667476e4
commit
e18c8ffa55
30 changed files with 1550 additions and 1400 deletions
|
@ -197,6 +197,8 @@
|
|||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/KEEP_EXISTING_INITIALIZER_ARRANGEMENT/@EntryValue">False</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/KEEP_EXISTING_INVOCATION_PARENS_ARRANGEMENT/@EntryValue">False</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/KEEP_USER_LINEBREAKS/@EntryValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/LINE_FEED_AT_FILE_END/@EntryValue">True</s:Boolean>
|
||||
<s:Int64 x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/MAX_INITIALIZER_ELEMENTS_ON_LINE/@EntryValue">1</s:Int64>
|
||||
|
||||
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/OTHER_BRACES/@EntryValue">END_OF_LINE</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/PLACE_ACCESSOR_ATTRIBUTE_ON_SAME_LINE_EX/@EntryValue">NEVER</s:String>
|
||||
|
@ -528,4 +530,8 @@ limitations under the License.</s:String>
|
|||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002EJavaScript_002ECodeStyle_002ESettingsUpgrade_002EJsParsFormattingSettingsUpgrader/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002EJavaScript_002ECodeStyle_002ESettingsUpgrade_002EJsWrapperSettingsUpgrader/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002EXml_002ECodeStyle_002EFormatSettingsUpgrade_002EXmlMoveToCommonFormatterSettingsUpgrade/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Int64 x:Key="/Default/Environment/UnitTesting/ParallelProcessesCount/@EntryValue">8</s:Int64></wpf:ResourceDictionary>
|
||||
<s:Int64 x:Key="/Default/Environment/UnitTesting/ParallelProcessesCount/@EntryValue">8</s:Int64>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Archi/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=cryptkey/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Domeradzki/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=_0141ukasz/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
|
|
@ -40,9 +40,17 @@
|
|||
<PackageReference Include="ConfigureAwaitChecker.Analyzer" Version="2.0.0" />
|
||||
<PackageReference Include="HtmlAgilityPack" Version="1.8.7" />
|
||||
<PackageReference Include="Humanizer" Version="2.4.2" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Formatters.Json" Version="2.2.0-preview1-35029" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.ResponseCompression" Version="2.2.0-preview1-35029" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Server.Kestrel" Version="2.2.0-preview1-35029" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.StaticFiles" Version="2.2.0-preview1-35029" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.WebSockets" Version="2.2.0-preview1-35029" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="2.2.0-preview1-35029" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Configuration" Version="2.2.0-preview1-35029" />
|
||||
<PackageReference Include="Microsoft.Win32.Registry" Version="4.5.0" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="11.0.2" />
|
||||
<PackageReference Include="NLog" Version="4.5.9" />
|
||||
<PackageReference Include="NLog.Web.AspNetCore" Version="4.6.0" />
|
||||
<PackageReference Include="protobuf-net" Version="3.0.0-alpha.3" />
|
||||
</ItemGroup>
|
||||
|
||||
|
|
|
@ -40,7 +40,7 @@ using SteamKit2.Discovery;
|
|||
using SteamKit2.Unified.Internal;
|
||||
|
||||
namespace ArchiSteamFarm {
|
||||
internal sealed class Bot : IDisposable {
|
||||
public sealed class Bot : IDisposable {
|
||||
internal const ushort CallbackSleep = 500; // In miliseconds
|
||||
internal const ushort MaxMessagePrefixLength = MaxMessageLength - ReservedMessageLength - 2; // 2 for a minimum of 2 characters (escape one and real one)
|
||||
internal const byte MinPlayingBlockedTTL = 60; // Delay in seconds added when account was occupied during our disconnect, to not disconnect other Steam client session too soon
|
||||
|
|
|
@ -32,7 +32,7 @@ using SteamKit2;
|
|||
|
||||
namespace ArchiSteamFarm {
|
||||
[SuppressMessage("ReSharper", "ClassCannotBeInstantiated")]
|
||||
internal sealed class GlobalConfig {
|
||||
public sealed class GlobalConfig {
|
||||
private const bool DefaultAutoRestart = true;
|
||||
private const string DefaultCommandPrefix = "!";
|
||||
private const byte DefaultConfirmationsLimiterDelay = 10;
|
||||
|
@ -46,7 +46,6 @@ namespace ArchiSteamFarm {
|
|||
private const byte DefaultInventoryLimiterDelay = 3;
|
||||
private const bool DefaultIPC = false;
|
||||
private const string DefaultIPCPassword = null;
|
||||
private const ushort DefaultIPCPort = 1242;
|
||||
private const byte DefaultLoginLimiterDelay = 10;
|
||||
private const byte DefaultMaxFarmingTime = 10;
|
||||
private const byte DefaultMaxTradeHoldDuration = 15;
|
||||
|
@ -63,7 +62,6 @@ namespace ArchiSteamFarm {
|
|||
private const string DefaultWebProxyUsername = null;
|
||||
|
||||
internal static readonly ImmutableHashSet<uint> SalesBlacklist = ImmutableHashSet.Create<uint>(267420, 303700, 335590, 368020, 425280, 480730, 566020, 639900, 762800, 876740);
|
||||
private static readonly ImmutableHashSet<string> DefaultIPCPrefixes = ImmutableHashSet.Create("http://127.0.0.1:" + DefaultIPCPort + "/");
|
||||
private static readonly ImmutableHashSet<uint> DefaultBlacklist = ImmutableHashSet.Create<uint>();
|
||||
|
||||
private static readonly SemaphoreSlim WriteSemaphore = new SemaphoreSlim(1, 1);
|
||||
|
@ -110,9 +108,6 @@ namespace ArchiSteamFarm {
|
|||
[JsonProperty]
|
||||
internal readonly string IPCPassword = DefaultIPCPassword;
|
||||
|
||||
[JsonProperty(ObjectCreationHandling = ObjectCreationHandling.Replace, Required = Required.DisallowNull)]
|
||||
internal readonly ImmutableHashSet<string> IPCPrefixes = DefaultIPCPrefixes;
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
internal readonly byte LoginLimiterDelay = DefaultLoginLimiterDelay;
|
||||
|
||||
|
@ -336,7 +331,6 @@ namespace ArchiSteamFarm {
|
|||
public bool ShouldSerializeInventoryLimiterDelay() => ShouldSerializeEverything || (InventoryLimiterDelay != DefaultInventoryLimiterDelay);
|
||||
public bool ShouldSerializeIPC() => ShouldSerializeEverything || (IPC != DefaultIPC);
|
||||
public bool ShouldSerializeIPCPassword() => ShouldSerializeEverything || (IPCPassword != DefaultIPCPassword);
|
||||
public bool ShouldSerializeIPCPrefixes() => ShouldSerializeEverything || ((IPCPrefixes != DefaultIPCPrefixes) && !IPCPrefixes.SetEquals(DefaultIPCPrefixes));
|
||||
public bool ShouldSerializeLoginLimiterDelay() => ShouldSerializeEverything || (LoginLimiterDelay != DefaultLoginLimiterDelay);
|
||||
public bool ShouldSerializeMaxFarmingTime() => ShouldSerializeEverything || (MaxFarmingTime != DefaultMaxFarmingTime);
|
||||
public bool ShouldSerializeMaxTradeHoldDuration() => ShouldSerializeEverything || (MaxTradeHoldDuration != DefaultMaxTradeHoldDuration);
|
||||
|
|
File diff suppressed because it is too large
Load diff
112
ArchiSteamFarm/IPC/ArchiKestrel.cs
Normal file
112
ArchiSteamFarm/IPC/ArchiKestrel.cs
Normal file
|
@ -0,0 +1,112 @@
|
|||
// _ _ _ ____ _ _____
|
||||
// / \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___
|
||||
// / _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \
|
||||
// / ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
|
||||
// /_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
|
||||
//
|
||||
// Copyright 2015-2018 Łukasz "JustArchi" Domeradzki
|
||||
// Contact: JustArchi@JustArchi.net
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using ArchiSteamFarm.IPC.Controllers.Api;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using NLog.Web;
|
||||
|
||||
namespace ArchiSteamFarm.IPC {
|
||||
internal static class ArchiKestrel {
|
||||
private const string ConfigurationFile = nameof(IPC) + ".config";
|
||||
|
||||
internal static HistoryTarget HistoryTarget { get; private set; }
|
||||
|
||||
private static IWebHost KestrelWebHost;
|
||||
|
||||
internal static void OnNewHistoryTarget(HistoryTarget historyTarget = null) {
|
||||
if (HistoryTarget != null) {
|
||||
HistoryTarget.NewHistoryEntry -= LogController.OnNewHistoryEntry;
|
||||
HistoryTarget = null;
|
||||
}
|
||||
|
||||
if (historyTarget != null) {
|
||||
historyTarget.NewHistoryEntry += LogController.OnNewHistoryEntry;
|
||||
HistoryTarget = historyTarget;
|
||||
}
|
||||
}
|
||||
|
||||
internal static async Task Start() {
|
||||
if (KestrelWebHost != null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// The order of dependency injection matters, pay attention to it
|
||||
IWebHostBuilder builder = new WebHostBuilder();
|
||||
|
||||
// Set default directories
|
||||
builder.UseContentRoot(SharedInfo.HomeDirectory);
|
||||
builder.UseWebRoot(SharedInfo.WebsiteDirectory);
|
||||
|
||||
// Check if custom config is available
|
||||
string absoluteConfigDirectory = Path.Combine(Directory.GetCurrentDirectory(), SharedInfo.ConfigDirectory);
|
||||
|
||||
if (File.Exists(Path.Combine(absoluteConfigDirectory, ConfigurationFile))) {
|
||||
// Set up custom config to be used
|
||||
builder.UseConfiguration(new ConfigurationBuilder().SetBasePath(absoluteConfigDirectory).AddJsonFile(ConfigurationFile, false, true).Build());
|
||||
|
||||
// Use custom config for Kestrel and Logging configuration
|
||||
builder.UseKestrel((builderContext, options) => options.Configure(builderContext.Configuration.GetSection("Kestrel")));
|
||||
builder.ConfigureLogging((hostingContext, logging) => logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging")));
|
||||
} else {
|
||||
// Use ASF defaults for Kestrel and Logging
|
||||
builder.UseKestrel(options => options.ListenLocalhost(1242));
|
||||
builder.ConfigureLogging(logging => logging.SetMinimumLevel(Debugging.IsUserDebugging ? LogLevel.Trace : LogLevel.Warning));
|
||||
}
|
||||
|
||||
// Enable NLog integration for logging
|
||||
builder.UseNLog();
|
||||
|
||||
// Specify Startup class for IPC
|
||||
builder.UseStartup<Startup>();
|
||||
|
||||
// Init history logger for /Api/Log usage
|
||||
Logging.InitHistoryLogger();
|
||||
|
||||
// Start the server
|
||||
IWebHost kestrelWebHost = builder.Build();
|
||||
|
||||
try {
|
||||
await kestrelWebHost.StartAsync().ConfigureAwait(false);
|
||||
} catch (Exception e) {
|
||||
ASF.ArchiLogger.LogGenericException(e);
|
||||
kestrelWebHost.Dispose();
|
||||
return;
|
||||
}
|
||||
|
||||
KestrelWebHost = kestrelWebHost;
|
||||
}
|
||||
|
||||
internal static async Task Stop() {
|
||||
if (KestrelWebHost == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
await KestrelWebHost.StopAsync().ConfigureAwait(false);
|
||||
KestrelWebHost.Dispose();
|
||||
KestrelWebHost = null;
|
||||
}
|
||||
}
|
||||
}
|
74
ArchiSteamFarm/IPC/Controllers/Api/ASFController.cs
Normal file
74
ArchiSteamFarm/IPC/Controllers/Api/ASFController.cs
Normal file
|
@ -0,0 +1,74 @@
|
|||
// _ _ _ ____ _ _____
|
||||
// / \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___
|
||||
// / _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \
|
||||
// / ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
|
||||
// /_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
|
||||
//
|
||||
// Copyright 2015-2018 Łukasz "JustArchi" Domeradzki
|
||||
// Contact: JustArchi@JustArchi.net
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using ArchiSteamFarm.IPC.Requests;
|
||||
using ArchiSteamFarm.IPC.Responses;
|
||||
using ArchiSteamFarm.Localization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace ArchiSteamFarm.IPC.Controllers.Api {
|
||||
[ApiController]
|
||||
[Route("Api/ASF")]
|
||||
public sealed class ASFController : ControllerBase {
|
||||
[HttpGet]
|
||||
public ActionResult<GenericResponse<ASFResponse>> Get() {
|
||||
uint memoryUsage = (uint) GC.GetTotalMemory(false) / 1024;
|
||||
|
||||
DateTime processStartTime;
|
||||
|
||||
using (Process process = Process.GetCurrentProcess()) {
|
||||
processStartTime = process.StartTime;
|
||||
}
|
||||
|
||||
ASFResponse response = new ASFResponse(SharedInfo.BuildInfo.Variant, Program.GlobalConfig, memoryUsage, processStartTime, SharedInfo.Version);
|
||||
|
||||
return Ok(new GenericResponse<ASFResponse>(response));
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
public async Task<ActionResult<GenericResponse>> Post([FromBody] ASFRequest request) {
|
||||
if (request == null) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(request));
|
||||
return BadRequest(new GenericResponse(false, string.Format(Strings.ErrorIsEmpty, nameof(request))));
|
||||
}
|
||||
|
||||
if (request.KeepSensitiveDetails) {
|
||||
if (string.IsNullOrEmpty(request.GlobalConfig.WebProxyPassword) && !string.IsNullOrEmpty(Program.GlobalConfig.WebProxyPassword)) {
|
||||
request.GlobalConfig.WebProxyPassword = Program.GlobalConfig.WebProxyPassword;
|
||||
}
|
||||
}
|
||||
|
||||
request.GlobalConfig.ShouldSerializeEverything = false;
|
||||
|
||||
string filePath = Path.Combine(SharedInfo.ConfigDirectory, SharedInfo.GlobalConfigFileName);
|
||||
|
||||
if (!await GlobalConfig.Write(filePath, request.GlobalConfig).ConfigureAwait(false)) {
|
||||
return BadRequest(new GenericResponse(false, Strings.WarningFailed));
|
||||
}
|
||||
|
||||
return Ok(new GenericResponse(true));
|
||||
}
|
||||
}
|
||||
}
|
117
ArchiSteamFarm/IPC/Controllers/Api/BotController.cs
Normal file
117
ArchiSteamFarm/IPC/Controllers/Api/BotController.cs
Normal file
|
@ -0,0 +1,117 @@
|
|||
// _ _ _ ____ _ _____
|
||||
// / \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___
|
||||
// / _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \
|
||||
// / ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
|
||||
// /_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
|
||||
//
|
||||
// Copyright 2015-2018 Łukasz "JustArchi" Domeradzki
|
||||
// Contact: JustArchi@JustArchi.net
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using ArchiSteamFarm.IPC.Requests;
|
||||
using ArchiSteamFarm.IPC.Responses;
|
||||
using ArchiSteamFarm.Localization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace ArchiSteamFarm.IPC.Controllers.Api {
|
||||
[ApiController]
|
||||
[Route("Api/Bot")]
|
||||
public sealed class BotController : ControllerBase {
|
||||
[HttpDelete("{botNames:required}")]
|
||||
public async Task<ActionResult<GenericResponse>> Delete(string botNames) {
|
||||
if (string.IsNullOrEmpty(botNames)) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(botNames));
|
||||
return BadRequest(new GenericResponse(false, string.Format(Strings.ErrorIsEmpty, nameof(botNames))));
|
||||
}
|
||||
|
||||
HashSet<Bot> bots = Bot.GetBots(botNames);
|
||||
if ((bots == null) || (bots.Count == 0)) {
|
||||
return BadRequest(new GenericResponse(false, string.Format(Strings.BotNotFound, botNames)));
|
||||
}
|
||||
|
||||
IEnumerable<Task<bool>> tasks = bots.Select(bot => bot.DeleteAllRelatedFiles());
|
||||
ICollection<bool> results;
|
||||
|
||||
switch (Program.GlobalConfig.OptimizationMode) {
|
||||
case GlobalConfig.EOptimizationMode.MinMemoryUsage:
|
||||
results = new List<bool>(bots.Count);
|
||||
foreach (Task<bool> task in tasks) {
|
||||
results.Add(await task.ConfigureAwait(false));
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
results = await Task.WhenAll(tasks).ConfigureAwait(false);
|
||||
break;
|
||||
}
|
||||
|
||||
if (results.Any(result => !result)) {
|
||||
return BadRequest(new GenericResponse(false, Strings.WarningFailed));
|
||||
}
|
||||
|
||||
return Ok(new GenericResponse(true));
|
||||
}
|
||||
|
||||
[HttpGet("{botNames:required}")]
|
||||
public ActionResult<GenericResponse<HashSet<Bot>>> Get(string botNames) {
|
||||
if (string.IsNullOrEmpty(botNames)) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(botNames));
|
||||
return BadRequest(new GenericResponse<HashSet<Bot>>(false, string.Format(Strings.ErrorIsEmpty, nameof(botNames))));
|
||||
}
|
||||
|
||||
HashSet<Bot> bots = Bot.GetBots(botNames);
|
||||
if ((bots == null) || (bots.Count == 0)) {
|
||||
return BadRequest(new GenericResponse<HashSet<Bot>>(false, string.Format(Strings.BotNotFound, botNames)));
|
||||
}
|
||||
|
||||
return Ok(new GenericResponse<HashSet<Bot>>(bots));
|
||||
}
|
||||
|
||||
[HttpPost("{botName:required}")]
|
||||
public async Task<ActionResult<GenericResponse>> Post(string botName, [FromBody] BotRequest request) {
|
||||
if (string.IsNullOrEmpty(botName) || (request == null)) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(botName) + " || " + nameof(request));
|
||||
return BadRequest(new GenericResponse(false, string.Format(Strings.ErrorIsEmpty, nameof(botName) + " || " + nameof(request))));
|
||||
}
|
||||
|
||||
if (request.KeepSensitiveDetails && Bot.Bots.TryGetValue(botName, out Bot bot)) {
|
||||
if (string.IsNullOrEmpty(request.BotConfig.SteamLogin) && !string.IsNullOrEmpty(bot.BotConfig.SteamLogin)) {
|
||||
request.BotConfig.SteamLogin = bot.BotConfig.SteamLogin;
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(request.BotConfig.SteamParentalPIN) && !string.IsNullOrEmpty(bot.BotConfig.SteamParentalPIN)) {
|
||||
request.BotConfig.SteamParentalPIN = bot.BotConfig.SteamParentalPIN;
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(request.BotConfig.SteamPassword) && !string.IsNullOrEmpty(bot.BotConfig.SteamPassword)) {
|
||||
request.BotConfig.SteamPassword = bot.BotConfig.SteamPassword;
|
||||
}
|
||||
}
|
||||
|
||||
request.BotConfig.ShouldSerializeEverything = false;
|
||||
|
||||
string filePath = Path.Combine(SharedInfo.ConfigDirectory, botName + SharedInfo.ConfigExtension);
|
||||
|
||||
if (!await BotConfig.Write(filePath, request.BotConfig).ConfigureAwait(false)) {
|
||||
return BadRequest(new GenericResponse(false, Strings.WarningFailed));
|
||||
}
|
||||
|
||||
return Ok(new GenericResponse(true));
|
||||
}
|
||||
}
|
||||
}
|
57
ArchiSteamFarm/IPC/Controllers/Api/CommandController.cs
Normal file
57
ArchiSteamFarm/IPC/Controllers/Api/CommandController.cs
Normal file
|
@ -0,0 +1,57 @@
|
|||
// _ _ _ ____ _ _____
|
||||
// / \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___
|
||||
// / _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \
|
||||
// / ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
|
||||
// /_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
|
||||
//
|
||||
// Copyright 2015-2018 Łukasz "JustArchi" Domeradzki
|
||||
// Contact: JustArchi@JustArchi.net
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using ArchiSteamFarm.IPC.Responses;
|
||||
using ArchiSteamFarm.Localization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace ArchiSteamFarm.IPC.Controllers.Api {
|
||||
[ApiController]
|
||||
[Route("Api/Command")]
|
||||
public sealed class CommandController : ControllerBase {
|
||||
[HttpPost("{command:required}")]
|
||||
public async Task<ActionResult<GenericResponse<string>>> Post(string command) {
|
||||
if (string.IsNullOrEmpty(command)) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(command));
|
||||
return BadRequest(new GenericResponse<string>(false, string.Format(Strings.ErrorIsEmpty, nameof(command))));
|
||||
}
|
||||
|
||||
if (Program.GlobalConfig.SteamOwnerID == 0) {
|
||||
return BadRequest(new GenericResponse<string>(false, string.Format(Strings.ErrorIsInvalid, nameof(Program.GlobalConfig.SteamOwnerID))));
|
||||
}
|
||||
|
||||
Bot targetBot = Bot.Bots.OrderBy(bot => bot.Key).Select(bot => bot.Value).FirstOrDefault();
|
||||
if (targetBot == null) {
|
||||
return BadRequest(new GenericResponse<string>(false, Strings.ErrorNoBotsDefined));
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(Program.GlobalConfig.CommandPrefix) && !command.StartsWith(Program.GlobalConfig.CommandPrefix, StringComparison.Ordinal)) {
|
||||
command = Program.GlobalConfig.CommandPrefix + command;
|
||||
}
|
||||
|
||||
string content = await targetBot.Response(Program.GlobalConfig.SteamOwnerID, command).ConfigureAwait(false);
|
||||
return Ok(new GenericResponse<string>(content));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,126 @@
|
|||
// _ _ _ ____ _ _____
|
||||
// / \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___
|
||||
// / _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \
|
||||
// / ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
|
||||
// /_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
|
||||
//
|
||||
// Copyright 2015-2018 Łukasz "JustArchi" Domeradzki
|
||||
// Contact: JustArchi@JustArchi.net
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using ArchiSteamFarm.IPC.Requests;
|
||||
using ArchiSteamFarm.IPC.Responses;
|
||||
using ArchiSteamFarm.Localization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace ArchiSteamFarm.IPC.Controllers.Api {
|
||||
[ApiController]
|
||||
[Route("Api/GamesToRedeemInBackgroundController")]
|
||||
public sealed class GamesToRedeemInBackgroundController : ControllerBase {
|
||||
[HttpDelete("{botNames:required}")]
|
||||
public async Task<ActionResult<GenericResponse>> Delete(string botNames) {
|
||||
if (string.IsNullOrEmpty(botNames)) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(botNames));
|
||||
return BadRequest(new GenericResponse(false, string.Format(Strings.ErrorIsEmpty, nameof(botNames))));
|
||||
}
|
||||
|
||||
HashSet<Bot> bots = Bot.GetBots(botNames);
|
||||
if ((bots == null) || (bots.Count == 0)) {
|
||||
return BadRequest(new GenericResponse(false, string.Format(Strings.BotNotFound, botNames)));
|
||||
}
|
||||
|
||||
IEnumerable<Task<bool>> tasks = bots.Select(bot => Task.Run(bot.DeleteRedeemedKeysFiles));
|
||||
ICollection<bool> results;
|
||||
|
||||
switch (Program.GlobalConfig.OptimizationMode) {
|
||||
case GlobalConfig.EOptimizationMode.MinMemoryUsage:
|
||||
results = new List<bool>(bots.Count);
|
||||
foreach (Task<bool> task in tasks) {
|
||||
results.Add(await task.ConfigureAwait(false));
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
results = await Task.WhenAll(tasks).ConfigureAwait(false);
|
||||
break;
|
||||
}
|
||||
|
||||
if (results.Any(result => !result)) {
|
||||
return BadRequest(new GenericResponse(false, Strings.WarningFailed));
|
||||
}
|
||||
|
||||
return Ok(new GenericResponse(true));
|
||||
}
|
||||
|
||||
[HttpGet("{botNames:required}")]
|
||||
public async Task<ActionResult<GenericResponse<Dictionary<string, GamesToRedeemInBackgroundResponse>>>> Get(string botNames) {
|
||||
if (string.IsNullOrEmpty(botNames)) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(botNames));
|
||||
return BadRequest(new GenericResponse<Dictionary<string, GamesToRedeemInBackgroundResponse>>(false, string.Format(Strings.ErrorIsEmpty, nameof(botNames))));
|
||||
}
|
||||
|
||||
HashSet<Bot> bots = Bot.GetBots(botNames);
|
||||
if ((bots == null) || (bots.Count == 0)) {
|
||||
return BadRequest(new GenericResponse<Dictionary<string, GamesToRedeemInBackgroundResponse>>(false, string.Format(Strings.BotNotFound, botNames)));
|
||||
}
|
||||
|
||||
IEnumerable<(string BotName, Task<(Dictionary<string, string> UnusedKeys, Dictionary<string, string> UsedKeys)> Task)> tasks = bots.Select(bot => (bot.BotName, bot.GetUsedAndUnusedKeys()));
|
||||
ICollection<(string BotName, (Dictionary<string, string> UnusedKeys, Dictionary<string, string> UsedKeys))> results;
|
||||
|
||||
switch (Program.GlobalConfig.OptimizationMode) {
|
||||
case GlobalConfig.EOptimizationMode.MinMemoryUsage:
|
||||
results = new List<(string BotName, (Dictionary<string, string> UnusedKeys, Dictionary<string, string> UsedKeys))>(bots.Count);
|
||||
foreach ((string botName, Task<(Dictionary<string, string> UnusedKeys, Dictionary<string, string> UsedKeys)> task) in tasks) {
|
||||
results.Add((botName, await task.ConfigureAwait(false)));
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
results = await Task.WhenAll(tasks.Select(async task => (task.BotName, await task.Task.ConfigureAwait(false)))).ConfigureAwait(false);
|
||||
break;
|
||||
}
|
||||
|
||||
Dictionary<string, GamesToRedeemInBackgroundResponse> result = new Dictionary<string, GamesToRedeemInBackgroundResponse>();
|
||||
|
||||
foreach ((string botName, (Dictionary<string, string> UnusedKeys, Dictionary<string, string> UsedKeys) taskResult) in results) {
|
||||
result[botName] = new GamesToRedeemInBackgroundResponse(taskResult.UnusedKeys, taskResult.UsedKeys);
|
||||
}
|
||||
|
||||
return Ok(new GenericResponse<Dictionary<string, GamesToRedeemInBackgroundResponse>>(result));
|
||||
}
|
||||
|
||||
[HttpPost("{botName:required}")]
|
||||
public async Task<ActionResult<GenericResponse<OrderedDictionary>>> Post(string botName, [FromBody] GamesToRedeemInBackgroundRequest request) {
|
||||
if (string.IsNullOrEmpty(botName) || (request == null)) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(botName) + " || " + nameof(request));
|
||||
return BadRequest(new GenericResponse<OrderedDictionary>(false, string.Format(Strings.ErrorIsEmpty, nameof(botName) + " || " + nameof(request))));
|
||||
}
|
||||
|
||||
if (request.GamesToRedeemInBackground.Count == 0) {
|
||||
return BadRequest(new GenericResponse<OrderedDictionary>(false, string.Format(Strings.ErrorIsEmpty, nameof(request.GamesToRedeemInBackground))));
|
||||
}
|
||||
|
||||
if (!Bot.Bots.TryGetValue(botName, out Bot bot)) {
|
||||
return BadRequest(new GenericResponse<OrderedDictionary>(false, string.Format(Strings.BotNotFound, botName)));
|
||||
}
|
||||
|
||||
await bot.ValidateAndAddGamesToRedeemInBackground(request.GamesToRedeemInBackground).ConfigureAwait(false);
|
||||
return Ok(new GenericResponse<OrderedDictionary>(request.GamesToRedeemInBackground));
|
||||
}
|
||||
}
|
||||
}
|
140
ArchiSteamFarm/IPC/Controllers/Api/LogController.cs
Normal file
140
ArchiSteamFarm/IPC/Controllers/Api/LogController.cs
Normal file
|
@ -0,0 +1,140 @@
|
|||
// _ _ _ ____ _ _____
|
||||
// / \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___
|
||||
// / _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \
|
||||
// / ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
|
||||
// /_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
|
||||
//
|
||||
// Copyright 2015-2018 Łukasz "JustArchi" Domeradzki
|
||||
// Contact: JustArchi@JustArchi.net
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
using System.Collections.Concurrent;
|
||||
using System.Linq;
|
||||
using System.Net.WebSockets;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using ArchiSteamFarm.IPC.Responses;
|
||||
using ArchiSteamFarm.Localization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace ArchiSteamFarm.IPC.Controllers.Api {
|
||||
[ApiController]
|
||||
[Route("Api/Log")]
|
||||
public sealed class LogController : ControllerBase {
|
||||
private static readonly ConcurrentDictionary<WebSocket, SemaphoreSlim> ActiveLogWebSockets = new ConcurrentDictionary<WebSocket, SemaphoreSlim>();
|
||||
|
||||
[HttpGet]
|
||||
public async Task<ActionResult> Get() {
|
||||
if (!HttpContext.WebSockets.IsWebSocketRequest) {
|
||||
return BadRequest(new GenericResponse(false, string.Format(Strings.WarningFailedWithError, nameof(HttpContext.WebSockets.IsWebSocketRequest) + ": " + HttpContext.WebSockets.IsWebSocketRequest)));
|
||||
}
|
||||
|
||||
// From now on we can return only EmptyResult as the response stream is already being used by existing websocket connection
|
||||
|
||||
try {
|
||||
using (WebSocket webSocket = await HttpContext.WebSockets.AcceptWebSocketAsync().ConfigureAwait(false)) {
|
||||
SemaphoreSlim sendSemaphore = new SemaphoreSlim(1, 1);
|
||||
if (!ActiveLogWebSockets.TryAdd(webSocket, sendSemaphore)) {
|
||||
sendSemaphore.Dispose();
|
||||
return new EmptyResult();
|
||||
}
|
||||
|
||||
try {
|
||||
// Push initial history if available
|
||||
if (ArchiKestrel.HistoryTarget != null) {
|
||||
// ReSharper disable once AccessToDisposedClosure - we're waiting for completion with Task.WhenAll(), we're not going to exit using block
|
||||
await Task.WhenAll(ArchiKestrel.HistoryTarget.ArchivedMessages.Select(archivedMessage => PostLoggedMessageUpdate(webSocket, sendSemaphore, archivedMessage))).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
while (webSocket.State == WebSocketState.Open) {
|
||||
WebSocketReceiveResult result = await webSocket.ReceiveAsync(new byte[0], CancellationToken.None).ConfigureAwait(false);
|
||||
|
||||
if (result.MessageType != WebSocketMessageType.Close) {
|
||||
await webSocket.CloseAsync(WebSocketCloseStatus.InvalidMessageType, "You're not supposed to be sending any message but Close!", CancellationToken.None).ConfigureAwait(false);
|
||||
break;
|
||||
}
|
||||
|
||||
await webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, string.Empty, CancellationToken.None).ConfigureAwait(false);
|
||||
break;
|
||||
}
|
||||
} finally {
|
||||
if (ActiveLogWebSockets.TryRemove(webSocket, out SemaphoreSlim closedSemaphore)) {
|
||||
await closedSemaphore.WaitAsync().ConfigureAwait(false); // Ensure that our semaphore is truly closed by now
|
||||
closedSemaphore.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (WebSocketException e) {
|
||||
ASF.ArchiLogger.LogGenericDebuggingException(e);
|
||||
}
|
||||
|
||||
return new EmptyResult();
|
||||
}
|
||||
|
||||
internal static async void OnNewHistoryEntry(object sender, HistoryTarget.NewHistoryEntryArgs newHistoryEntryArgs) {
|
||||
if ((sender == null) || (newHistoryEntryArgs == null)) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(sender) + " || " + nameof(newHistoryEntryArgs));
|
||||
return;
|
||||
}
|
||||
|
||||
if (ActiveLogWebSockets.Count == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
string json = JsonConvert.SerializeObject(new GenericResponse<string>(newHistoryEntryArgs.Message));
|
||||
await Task.WhenAll(ActiveLogWebSockets.Where(kv => kv.Key.State == WebSocketState.Open).Select(kv => PostLoggedJsonUpdate(kv.Key, kv.Value, json))).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private static async Task PostLoggedJsonUpdate(WebSocket webSocket, SemaphoreSlim sendSemaphore, string json) {
|
||||
if ((webSocket == null) || (sendSemaphore == null) || string.IsNullOrEmpty(json)) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(webSocket) + " || " + nameof(sendSemaphore) + " || " + nameof(json));
|
||||
return;
|
||||
}
|
||||
|
||||
if (webSocket.State != WebSocketState.Open) {
|
||||
return;
|
||||
}
|
||||
|
||||
await sendSemaphore.WaitAsync().ConfigureAwait(false);
|
||||
|
||||
try {
|
||||
if (webSocket.State != WebSocketState.Open) {
|
||||
return;
|
||||
}
|
||||
|
||||
await webSocket.SendAsync(Encoding.UTF8.GetBytes(json), WebSocketMessageType.Text, true, CancellationToken.None).ConfigureAwait(false);
|
||||
} catch (WebSocketException e) {
|
||||
ASF.ArchiLogger.LogGenericDebuggingException(e);
|
||||
} finally {
|
||||
sendSemaphore.Release();
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task PostLoggedMessageUpdate(WebSocket webSocket, SemaphoreSlim sendSemaphore, string loggedMessage) {
|
||||
if ((webSocket == null) || (sendSemaphore == null) || string.IsNullOrEmpty(loggedMessage)) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(webSocket) + " || " + nameof(sendSemaphore) + " || " + nameof(loggedMessage));
|
||||
return;
|
||||
}
|
||||
|
||||
if (webSocket.State != WebSocketState.Open) {
|
||||
return;
|
||||
}
|
||||
|
||||
string response = JsonConvert.SerializeObject(new GenericResponse<string>(loggedMessage));
|
||||
await PostLoggedJsonUpdate(webSocket, sendSemaphore, response).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
55
ArchiSteamFarm/IPC/Controllers/Api/StructureController.cs
Normal file
55
ArchiSteamFarm/IPC/Controllers/Api/StructureController.cs
Normal file
|
@ -0,0 +1,55 @@
|
|||
// _ _ _ ____ _ _____
|
||||
// / \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___
|
||||
// / _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \
|
||||
// / ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
|
||||
// /_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
|
||||
//
|
||||
// Copyright 2015-2018 Łukasz "JustArchi" Domeradzki
|
||||
// Contact: JustArchi@JustArchi.net
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
using System;
|
||||
using ArchiSteamFarm.IPC.Responses;
|
||||
using ArchiSteamFarm.Localization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace ArchiSteamFarm.IPC.Controllers.Api {
|
||||
[ApiController]
|
||||
[Route("Api/Structure")]
|
||||
public sealed class StructureController : ControllerBase {
|
||||
[HttpGet("{structure:required}")]
|
||||
public ActionResult<GenericResponse<object>> Get(string structure) {
|
||||
if (string.IsNullOrEmpty(structure)) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(structure));
|
||||
return BadRequest(new GenericResponse<object>(false, string.Format(Strings.ErrorIsEmpty, nameof(structure))));
|
||||
}
|
||||
|
||||
Type targetType = Utilities.ParseType(structure);
|
||||
|
||||
if (targetType == null) {
|
||||
return BadRequest(new GenericResponse<object>(false, string.Format(Strings.ErrorIsInvalid, structure)));
|
||||
}
|
||||
|
||||
object obj;
|
||||
|
||||
try {
|
||||
obj = Activator.CreateInstance(targetType, true);
|
||||
} catch (Exception e) {
|
||||
return BadRequest(new GenericResponse<object>(false, string.Format(Strings.ErrorParsingObject, nameof(targetType)) + Environment.NewLine + e));
|
||||
}
|
||||
|
||||
return Ok(new GenericResponse<object>(obj));
|
||||
}
|
||||
}
|
||||
}
|
85
ArchiSteamFarm/IPC/Controllers/Api/TypeController.cs
Normal file
85
ArchiSteamFarm/IPC/Controllers/Api/TypeController.cs
Normal file
|
@ -0,0 +1,85 @@
|
|||
// _ _ _ ____ _ _____
|
||||
// / \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___
|
||||
// / _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \
|
||||
// / ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
|
||||
// /_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
|
||||
//
|
||||
// Copyright 2015-2018 Łukasz "JustArchi" Domeradzki
|
||||
// Contact: JustArchi@JustArchi.net
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using ArchiSteamFarm.IPC.Responses;
|
||||
using ArchiSteamFarm.Localization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace ArchiSteamFarm.IPC.Controllers.Api {
|
||||
[ApiController]
|
||||
[Route("Api/Type")]
|
||||
public sealed class TypeController : ControllerBase {
|
||||
[HttpGet("{type:required}")]
|
||||
public ActionResult<GenericResponse<TypeResponse>> Get(string type) {
|
||||
if (string.IsNullOrEmpty(type)) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(type));
|
||||
return BadRequest(new GenericResponse<TypeResponse>(false, string.Format(Strings.ErrorIsEmpty, nameof(type))));
|
||||
}
|
||||
|
||||
Type targetType = Utilities.ParseType(type);
|
||||
|
||||
if (targetType == null) {
|
||||
return BadRequest(new GenericResponse<object>(false, string.Format(Strings.ErrorIsInvalid, type)));
|
||||
}
|
||||
|
||||
string baseType = targetType.BaseType?.GetUnifiedName();
|
||||
HashSet<string> customAttributes = targetType.CustomAttributes.Select(attribute => attribute.AttributeType.GetUnifiedName()).ToHashSet();
|
||||
string underlyingType = null;
|
||||
|
||||
Dictionary<string, string> body = new Dictionary<string, string>();
|
||||
|
||||
if (targetType.IsClass) {
|
||||
foreach (FieldInfo field in targetType.GetFields(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public).Where(field => !field.IsPrivate)) {
|
||||
JsonPropertyAttribute jsonProperty = field.GetCustomAttribute<JsonPropertyAttribute>();
|
||||
|
||||
if (jsonProperty != null) {
|
||||
body[jsonProperty.PropertyName ?? field.Name] = field.FieldType.GetUnifiedName();
|
||||
}
|
||||
}
|
||||
|
||||
foreach (PropertyInfo property in targetType.GetProperties(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public).Where(property => property.CanRead && !property.GetMethod.IsPrivate)) {
|
||||
JsonPropertyAttribute jsonProperty = property.GetCustomAttribute<JsonPropertyAttribute>();
|
||||
|
||||
if (jsonProperty != null) {
|
||||
body[jsonProperty.PropertyName ?? property.Name] = property.PropertyType.GetUnifiedName();
|
||||
}
|
||||
}
|
||||
} else if (targetType.IsEnum) {
|
||||
Type enumType = Enum.GetUnderlyingType(targetType);
|
||||
underlyingType = enumType.GetUnifiedName();
|
||||
|
||||
foreach (object value in Enum.GetValues(targetType)) {
|
||||
body[value.ToString()] = Convert.ChangeType(value, enumType).ToString();
|
||||
}
|
||||
}
|
||||
|
||||
TypeResponse.TypeProperties properties = new TypeResponse.TypeProperties(baseType, customAttributes.Count > 0 ? customAttributes : null, underlyingType);
|
||||
TypeResponse response = new TypeResponse(body, properties);
|
||||
|
||||
return Ok(new GenericResponse<TypeResponse>(response));
|
||||
}
|
||||
}
|
||||
}
|
79
ArchiSteamFarm/IPC/Controllers/Api/WWWController.cs
Normal file
79
ArchiSteamFarm/IPC/Controllers/Api/WWWController.cs
Normal file
|
@ -0,0 +1,79 @@
|
|||
// _ _ _ ____ _ _____
|
||||
// / \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___
|
||||
// / _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \
|
||||
// / ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
|
||||
// /_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
|
||||
//
|
||||
// Copyright 2015-2018 Łukasz "JustArchi" Domeradzki
|
||||
// Contact: JustArchi@JustArchi.net
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using ArchiSteamFarm.IPC.Requests;
|
||||
using ArchiSteamFarm.IPC.Responses;
|
||||
using ArchiSteamFarm.Localization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace ArchiSteamFarm.IPC.Controllers.Api {
|
||||
[ApiController]
|
||||
[Route("Api/WWW")]
|
||||
public sealed class WWWController : ControllerBase {
|
||||
[HttpGet("Directory/{directory:required}")]
|
||||
public ActionResult<GenericResponse<HashSet<string>>> DirectoryGet(string directory) {
|
||||
if (string.IsNullOrEmpty(directory)) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(directory));
|
||||
return BadRequest(new GenericResponse<HashSet<string>>(false, string.Format(Strings.ErrorIsEmpty, nameof(directory))));
|
||||
}
|
||||
|
||||
string directoryPath = Path.Combine(SharedInfo.HomeDirectory, SharedInfo.WebsiteDirectory, directory);
|
||||
if (!Directory.Exists(directoryPath)) {
|
||||
return BadRequest(new GenericResponse<HashSet<string>>(false, string.Format(Strings.ErrorIsInvalid, directory)));
|
||||
}
|
||||
|
||||
string[] files;
|
||||
|
||||
try {
|
||||
files = Directory.GetFiles(directoryPath);
|
||||
} catch (Exception e) {
|
||||
return BadRequest(new GenericResponse<HashSet<string>>(false, string.Format(Strings.ErrorParsingObject, nameof(files)) + Environment.NewLine + e));
|
||||
}
|
||||
|
||||
HashSet<string> result = files.Select(Path.GetFileName).ToHashSet();
|
||||
return Ok(new GenericResponse<HashSet<string>>(result));
|
||||
}
|
||||
|
||||
[HttpPost("Send")]
|
||||
public async Task<ActionResult<GenericResponse<string>>> SendPost([FromBody] WWWSendRequest request) {
|
||||
if (request == null) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(request));
|
||||
return BadRequest(new GenericResponse<string>(false, string.Format(Strings.ErrorIsEmpty, nameof(request))));
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(request.URL) || !Uri.TryCreate(request.URL, UriKind.Absolute, out Uri uri) || !uri.Scheme.Equals(Uri.UriSchemeHttps)) {
|
||||
return BadRequest(new GenericResponse<string>(false, string.Format(Strings.ErrorIsInvalid, nameof(request.URL))));
|
||||
}
|
||||
|
||||
WebBrowser.HtmlDocumentResponse urlResponse = await Program.WebBrowser.UrlGetToHtmlDocument(request.URL).ConfigureAwait(false);
|
||||
if (urlResponse?.Content == null) {
|
||||
return BadRequest(new GenericResponse<string>(false, string.Format(Strings.ErrorRequestFailedTooManyTimes, WebBrowser.MaxTries)));
|
||||
}
|
||||
|
||||
return Ok(new GenericResponse<string>(urlResponse.Content.DocumentNode.InnerHtml));
|
||||
}
|
||||
}
|
||||
}
|
114
ArchiSteamFarm/IPC/Middleware/ApiAuthenticationMiddleware.cs
Normal file
114
ArchiSteamFarm/IPC/Middleware/ApiAuthenticationMiddleware.cs
Normal file
|
@ -0,0 +1,114 @@
|
|||
// _ _ _ ____ _ _____
|
||||
// / \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___
|
||||
// / _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \
|
||||
// / ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
|
||||
// /_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
|
||||
//
|
||||
// Copyright 2015-2018 Łukasz "JustArchi" Domeradzki
|
||||
// Contact: JustArchi@JustArchi.net
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.Primitives;
|
||||
|
||||
namespace ArchiSteamFarm.IPC.Middleware {
|
||||
[SuppressMessage("ReSharper", "ClassNeverInstantiated.Global")]
|
||||
internal sealed class ApiAuthenticationMiddleware {
|
||||
private const byte FailedAuthorizationsCooldownInHours = 1;
|
||||
private const byte MaxFailedAuthorizationAttempts = 5;
|
||||
|
||||
private static readonly SemaphoreSlim AuthorizationSemaphore = new SemaphoreSlim(1, 1);
|
||||
|
||||
[SuppressMessage("ReSharper", "UnusedMember.Local")]
|
||||
private static readonly Timer ClearFailedAuthorizationsTimer = new Timer(
|
||||
e => FailedAuthorizations.Clear(),
|
||||
null,
|
||||
TimeSpan.FromHours(FailedAuthorizationsCooldownInHours), // Delay
|
||||
TimeSpan.FromHours(FailedAuthorizationsCooldownInHours) // Period
|
||||
);
|
||||
|
||||
private static readonly ConcurrentDictionary<IPAddress, byte> FailedAuthorizations = new ConcurrentDictionary<IPAddress, byte>();
|
||||
|
||||
private readonly RequestDelegate Next;
|
||||
|
||||
public ApiAuthenticationMiddleware(RequestDelegate next) => Next = next ?? throw new ArgumentNullException(nameof(next));
|
||||
|
||||
[SuppressMessage("ReSharper", "UnusedMember.Global")]
|
||||
public async Task InvokeAsync(HttpContext context) {
|
||||
if (context == null) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(context));
|
||||
return;
|
||||
}
|
||||
|
||||
HttpStatusCode authenticationStatus = await GetAuthenticationStatus(context).ConfigureAwait(false);
|
||||
|
||||
if (authenticationStatus != HttpStatusCode.OK) {
|
||||
await context.Response.Generate(authenticationStatus).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
await Next(context).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private static async Task<HttpStatusCode> GetAuthenticationStatus(HttpContext context) {
|
||||
if (context == null) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(context));
|
||||
return HttpStatusCode.InternalServerError;
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(Program.GlobalConfig.IPCPassword)) {
|
||||
return HttpStatusCode.OK;
|
||||
}
|
||||
|
||||
IPAddress clientIP = context.Connection.RemoteIpAddress;
|
||||
|
||||
if (FailedAuthorizations.TryGetValue(clientIP, out byte attempts)) {
|
||||
if (attempts >= MaxFailedAuthorizationAttempts) {
|
||||
return HttpStatusCode.Forbidden;
|
||||
}
|
||||
}
|
||||
|
||||
if (!context.Request.Headers.TryGetValue("Authentication", out StringValues passwords) && !context.Request.Query.TryGetValue("password", out passwords)) {
|
||||
return HttpStatusCode.Unauthorized;
|
||||
}
|
||||
|
||||
bool authorized = passwords.First() == Program.GlobalConfig.IPCPassword;
|
||||
|
||||
await AuthorizationSemaphore.WaitAsync().ConfigureAwait(false);
|
||||
|
||||
try {
|
||||
if (FailedAuthorizations.TryGetValue(clientIP, out attempts)) {
|
||||
if (attempts >= MaxFailedAuthorizationAttempts) {
|
||||
return HttpStatusCode.Forbidden;
|
||||
}
|
||||
}
|
||||
|
||||
if (!authorized) {
|
||||
FailedAuthorizations[clientIP] = FailedAuthorizations.TryGetValue(clientIP, out attempts) ? ++attempts : (byte) 1;
|
||||
}
|
||||
} finally {
|
||||
AuthorizationSemaphore.Release();
|
||||
}
|
||||
|
||||
return authorized ? HttpStatusCode.OK : HttpStatusCode.Unauthorized;
|
||||
}
|
||||
}
|
||||
}
|
34
ArchiSteamFarm/IPC/Requests/ASFRequest.cs
Normal file
34
ArchiSteamFarm/IPC/Requests/ASFRequest.cs
Normal file
|
@ -0,0 +1,34 @@
|
|||
// _ _ _ ____ _ _____
|
||||
// / \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___
|
||||
// / _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \
|
||||
// / ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
|
||||
// /_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
|
||||
//
|
||||
// Copyright 2015-2018 Łukasz "JustArchi" Domeradzki
|
||||
// Contact: JustArchi@JustArchi.net
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace ArchiSteamFarm.IPC.Requests {
|
||||
[SuppressMessage("ReSharper", "ClassCannotBeInstantiated")]
|
||||
public sealed class ASFRequest : SensitiveDetailsRequest {
|
||||
[JsonProperty(Required = Required.Always)]
|
||||
internal readonly GlobalConfig GlobalConfig;
|
||||
|
||||
// Deserialized from JSON
|
||||
private ASFRequest() { }
|
||||
}
|
||||
}
|
34
ArchiSteamFarm/IPC/Requests/BotRequest.cs
Normal file
34
ArchiSteamFarm/IPC/Requests/BotRequest.cs
Normal file
|
@ -0,0 +1,34 @@
|
|||
// _ _ _ ____ _ _____
|
||||
// / \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___
|
||||
// / _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \
|
||||
// / ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
|
||||
// /_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
|
||||
//
|
||||
// Copyright 2015-2018 Łukasz "JustArchi" Domeradzki
|
||||
// Contact: JustArchi@JustArchi.net
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace ArchiSteamFarm.IPC.Requests {
|
||||
[SuppressMessage("ReSharper", "ClassCannotBeInstantiated")]
|
||||
public sealed class BotRequest : SensitiveDetailsRequest {
|
||||
[JsonProperty(Required = Required.Always)]
|
||||
internal readonly BotConfig BotConfig;
|
||||
|
||||
// Deserialized from JSON
|
||||
private BotRequest() { }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
// _ _ _ ____ _ _____
|
||||
// / \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___
|
||||
// / _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \
|
||||
// / ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
|
||||
// /_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
|
||||
//
|
||||
// Copyright 2015-2018 Łukasz "JustArchi" Domeradzki
|
||||
// Contact: JustArchi@JustArchi.net
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
using System.Collections.Specialized;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace ArchiSteamFarm.IPC.Requests {
|
||||
[SuppressMessage("ReSharper", "ClassCannotBeInstantiated")]
|
||||
public sealed class GamesToRedeemInBackgroundRequest {
|
||||
[JsonProperty(Required = Required.Always)]
|
||||
internal readonly OrderedDictionary GamesToRedeemInBackground;
|
||||
|
||||
// Deserialized from JSON
|
||||
private GamesToRedeemInBackgroundRequest() { }
|
||||
}
|
||||
}
|
29
ArchiSteamFarm/IPC/Requests/SensitiveDetailsRequest.cs
Normal file
29
ArchiSteamFarm/IPC/Requests/SensitiveDetailsRequest.cs
Normal file
|
@ -0,0 +1,29 @@
|
|||
// _ _ _ ____ _ _____
|
||||
// / \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___
|
||||
// / _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \
|
||||
// / ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
|
||||
// /_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
|
||||
//
|
||||
// Copyright 2015-2018 Łukasz "JustArchi" Domeradzki
|
||||
// Contact: JustArchi@JustArchi.net
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace ArchiSteamFarm.IPC.Requests {
|
||||
public abstract class SensitiveDetailsRequest {
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
internal readonly bool KeepSensitiveDetails = true;
|
||||
}
|
||||
}
|
34
ArchiSteamFarm/IPC/Requests/WWWSendRequest.cs
Normal file
34
ArchiSteamFarm/IPC/Requests/WWWSendRequest.cs
Normal file
|
@ -0,0 +1,34 @@
|
|||
// _ _ _ ____ _ _____
|
||||
// / \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___
|
||||
// / _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \
|
||||
// / ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
|
||||
// /_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
|
||||
//
|
||||
// Copyright 2015-2018 Łukasz "JustArchi" Domeradzki
|
||||
// Contact: JustArchi@JustArchi.net
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace ArchiSteamFarm.IPC.Requests {
|
||||
[SuppressMessage("ReSharper", "ClassCannotBeInstantiated")]
|
||||
public sealed class WWWSendRequest {
|
||||
[JsonProperty(Required = Required.Always)]
|
||||
internal readonly string URL;
|
||||
|
||||
// Deserialized from JSON
|
||||
private WWWSendRequest() { }
|
||||
}
|
||||
}
|
54
ArchiSteamFarm/IPC/Responses/ASFResponse.cs
Normal file
54
ArchiSteamFarm/IPC/Responses/ASFResponse.cs
Normal file
|
@ -0,0 +1,54 @@
|
|||
// _ _ _ ____ _ _____
|
||||
// / \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___
|
||||
// / _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \
|
||||
// / ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
|
||||
// /_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
|
||||
//
|
||||
// Copyright 2015-2018 Łukasz "JustArchi" Domeradzki
|
||||
// Contact: JustArchi@JustArchi.net
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
using System;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace ArchiSteamFarm.IPC.Responses {
|
||||
public sealed class ASFResponse {
|
||||
[JsonProperty]
|
||||
private readonly string BuildVariant;
|
||||
|
||||
[JsonProperty]
|
||||
private readonly GlobalConfig GlobalConfig;
|
||||
|
||||
[JsonProperty]
|
||||
private readonly uint MemoryUsage;
|
||||
|
||||
[JsonProperty]
|
||||
private readonly DateTime ProcessStartTime;
|
||||
|
||||
[JsonProperty]
|
||||
private 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)) {
|
||||
throw new ArgumentNullException(nameof(buildVariant) + " || " + nameof(globalConfig) + " || " + nameof(memoryUsage) + " || " + nameof(processStartTime) + " || " + nameof(version));
|
||||
}
|
||||
|
||||
BuildVariant = buildVariant;
|
||||
GlobalConfig = globalConfig;
|
||||
MemoryUsage = memoryUsage;
|
||||
ProcessStartTime = processStartTime;
|
||||
Version = version;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
// _ _ _ ____ _ _____
|
||||
// / \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___
|
||||
// / _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \
|
||||
// / ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
|
||||
// /_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
|
||||
//
|
||||
// Copyright 2015-2018 Łukasz "JustArchi" Domeradzki
|
||||
// Contact: JustArchi@JustArchi.net
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace ArchiSteamFarm.IPC.Responses {
|
||||
public sealed class GamesToRedeemInBackgroundResponse {
|
||||
[JsonProperty]
|
||||
private readonly Dictionary<string, string> UnusedKeys;
|
||||
|
||||
[JsonProperty]
|
||||
private readonly Dictionary<string, string> UsedKeys;
|
||||
|
||||
internal GamesToRedeemInBackgroundResponse(Dictionary<string, string> unusedKeys = null, Dictionary<string, string> usedKeys = null) {
|
||||
UnusedKeys = unusedKeys;
|
||||
UsedKeys = usedKeys;
|
||||
}
|
||||
}
|
||||
}
|
60
ArchiSteamFarm/IPC/Responses/GenericResponse.cs
Normal file
60
ArchiSteamFarm/IPC/Responses/GenericResponse.cs
Normal file
|
@ -0,0 +1,60 @@
|
|||
// _ _ _ ____ _ _____
|
||||
// / \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___
|
||||
// / _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \
|
||||
// / ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
|
||||
// /_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
|
||||
//
|
||||
// Copyright 2015-2018 Łukasz "JustArchi" Domeradzki
|
||||
// Contact: JustArchi@JustArchi.net
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
using System;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace ArchiSteamFarm.IPC.Responses {
|
||||
public sealed class GenericResponse<T> : GenericResponse where T : class {
|
||||
[JsonProperty]
|
||||
private readonly T Result;
|
||||
|
||||
internal GenericResponse(T result) : base(true, "OK") => Result = result ?? throw new ArgumentNullException(nameof(result));
|
||||
internal GenericResponse(bool success, string message) : base(success, message) { }
|
||||
}
|
||||
|
||||
public class GenericResponse {
|
||||
[JsonProperty]
|
||||
private readonly string Message;
|
||||
|
||||
[JsonProperty]
|
||||
private readonly bool Success;
|
||||
|
||||
internal GenericResponse(bool success) {
|
||||
if (!success) {
|
||||
// Returning failed generic response without a message should never happen
|
||||
throw new ArgumentException(nameof(success));
|
||||
}
|
||||
|
||||
Success = true;
|
||||
Message = "OK";
|
||||
}
|
||||
|
||||
internal GenericResponse(bool success, string message) {
|
||||
if (string.IsNullOrEmpty(message)) {
|
||||
throw new ArgumentNullException(nameof(message));
|
||||
}
|
||||
|
||||
Success = success;
|
||||
Message = message;
|
||||
}
|
||||
}
|
||||
}
|
60
ArchiSteamFarm/IPC/Responses/TypeResponse.cs
Normal file
60
ArchiSteamFarm/IPC/Responses/TypeResponse.cs
Normal file
|
@ -0,0 +1,60 @@
|
|||
// _ _ _ ____ _ _____
|
||||
// / \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___
|
||||
// / _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \
|
||||
// / ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
|
||||
// /_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
|
||||
//
|
||||
// Copyright 2015-2018 Łukasz "JustArchi" Domeradzki
|
||||
// Contact: JustArchi@JustArchi.net
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace ArchiSteamFarm.IPC.Responses {
|
||||
public sealed class TypeResponse {
|
||||
[JsonProperty]
|
||||
private readonly Dictionary<string, string> Body;
|
||||
|
||||
[JsonProperty]
|
||||
private readonly TypeProperties Properties;
|
||||
|
||||
internal TypeResponse(Dictionary<string, string> body, TypeProperties properties) {
|
||||
if ((body == null) || (properties == null)) {
|
||||
throw new ArgumentNullException(nameof(body) + " || " + nameof(properties));
|
||||
}
|
||||
|
||||
Body = body;
|
||||
Properties = properties;
|
||||
}
|
||||
|
||||
internal sealed class TypeProperties {
|
||||
[JsonProperty]
|
||||
private readonly string BaseType;
|
||||
|
||||
[JsonProperty]
|
||||
private readonly HashSet<string> CustomAttributes;
|
||||
|
||||
[JsonProperty]
|
||||
private readonly string UnderlyingType;
|
||||
|
||||
internal TypeProperties(string baseType = null, HashSet<string> customAttributes = null, string underlyingType = null) {
|
||||
BaseType = baseType;
|
||||
CustomAttributes = customAttributes;
|
||||
UnderlyingType = underlyingType;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
108
ArchiSteamFarm/IPC/Startup.cs
Normal file
108
ArchiSteamFarm/IPC/Startup.cs
Normal file
|
@ -0,0 +1,108 @@
|
|||
// _ _ _ ____ _ _____
|
||||
// / \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___
|
||||
// / _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \
|
||||
// / ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
|
||||
// /_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
|
||||
//
|
||||
// Copyright 2015-2018 Łukasz "JustArchi" Domeradzki
|
||||
// Contact: JustArchi@JustArchi.net
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
using System;
|
||||
using ArchiSteamFarm.IPC.Middleware;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Serialization;
|
||||
|
||||
namespace ArchiSteamFarm.IPC {
|
||||
internal sealed class Startup {
|
||||
private readonly IConfiguration Configuration;
|
||||
|
||||
public Startup(IConfiguration configuration) => Configuration = configuration ?? throw new ArgumentNullException(nameof(configuration));
|
||||
|
||||
public void Configure(IApplicationBuilder app, IHostingEnvironment env) {
|
||||
if ((app == null) || (env == null)) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(app) + " || " + nameof(env));
|
||||
return;
|
||||
}
|
||||
|
||||
// The order of dependency injection matters, pay attention to it
|
||||
|
||||
// Add workaround for missing PathBase feature, https://github.com/aspnet/Hosting/issues/1120
|
||||
PathString pathBase = Configuration.GetSection("Kestrel").GetValue<PathString>("PathBase");
|
||||
if (!string.IsNullOrEmpty(pathBase)) {
|
||||
app.UsePathBase(pathBase);
|
||||
}
|
||||
|
||||
// Add support for response compression
|
||||
app.UseResponseCompression();
|
||||
|
||||
if (!string.IsNullOrEmpty(Program.GlobalConfig.IPCPassword)) {
|
||||
// We need ApiAuthenticationMiddleware for IPCPassword
|
||||
app.UseWhen(context => context.Request.Path.StartsWithSegments("/Api", StringComparison.OrdinalIgnoreCase), appBuilder => appBuilder.UseMiddleware<ApiAuthenticationMiddleware>());
|
||||
}
|
||||
|
||||
// We need WebSockets support for /Api/Log
|
||||
app.UseWebSockets();
|
||||
|
||||
// We need static files support for IPC GUI
|
||||
app.UseDefaultFiles();
|
||||
app.UseStaticFiles();
|
||||
|
||||
// We need MVC for /Api
|
||||
app.UseMvcWithDefaultRoute();
|
||||
}
|
||||
|
||||
public void ConfigureServices(IServiceCollection services) {
|
||||
if (services == null) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(services));
|
||||
return;
|
||||
}
|
||||
|
||||
// The order of dependency injection matters, pay attention to it
|
||||
|
||||
// Add support for response compression
|
||||
services.AddResponseCompression();
|
||||
|
||||
// We need MVC for /Api, but we're going to use only a small subset of all available features
|
||||
IMvcCoreBuilder mvc = services.AddMvcCore();
|
||||
|
||||
// Use latest compatibility version for MVC
|
||||
mvc.SetCompatibilityVersion(CompatibilityVersion.Latest);
|
||||
|
||||
// Add standard formatters that can be used for serializing/deserializing requests/responses, they're already available in the core
|
||||
mvc.AddFormatterMappings();
|
||||
|
||||
// Add JSON formatters that will be used as default ones if no specific formatters are asked for
|
||||
mvc.AddJsonFormatters();
|
||||
|
||||
// Fix default contract resolver to use original names and not a camel case
|
||||
// Also add debugging aid while we're at it
|
||||
mvc.AddJsonOptions(
|
||||
options => {
|
||||
options.SerializerSettings.ContractResolver = new DefaultContractResolver();
|
||||
|
||||
if (Debugging.IsUserDebugging) {
|
||||
options.SerializerSettings.Formatting = Formatting.Indented;
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
72
ArchiSteamFarm/IPC/Utilities.cs
Normal file
72
ArchiSteamFarm/IPC/Utilities.cs
Normal file
|
@ -0,0 +1,72 @@
|
|||
// _ _ _ ____ _ _____
|
||||
// / \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___
|
||||
// / _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \
|
||||
// / ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
|
||||
// /_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
|
||||
//
|
||||
// Copyright 2015-2018 Łukasz "JustArchi" Domeradzki
|
||||
// Contact: JustArchi@JustArchi.net
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
|
||||
namespace ArchiSteamFarm.IPC {
|
||||
internal static class Utilities {
|
||||
internal static async Task Generate(this HttpResponse httpResponse, HttpStatusCode statusCode) {
|
||||
if (httpResponse == null) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(httpResponse));
|
||||
return;
|
||||
}
|
||||
|
||||
ushort statusCodeNumber = (ushort) statusCode;
|
||||
|
||||
httpResponse.StatusCode = statusCodeNumber;
|
||||
await httpResponse.WriteAsync(statusCodeNumber + " - " + statusCode).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
internal static string GetUnifiedName(this Type type) {
|
||||
if (type == null) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(type));
|
||||
return null;
|
||||
}
|
||||
|
||||
return type.GenericTypeArguments.Length == 0 ? type.FullName : type.Namespace + "." + type.Name + string.Join("", type.GenericTypeArguments.Select(innerType => '[' + innerType.GetUnifiedName() + ']'));
|
||||
}
|
||||
|
||||
internal static Type ParseType(string typeText) {
|
||||
if (string.IsNullOrEmpty(typeText)) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(typeText));
|
||||
return null;
|
||||
}
|
||||
|
||||
Type targetType = Type.GetType(typeText);
|
||||
if (targetType != null) {
|
||||
return targetType;
|
||||
}
|
||||
|
||||
// We can try one more time by trying to smartly guess the assembly name from the namespace, this will work for custom libraries like SteamKit2
|
||||
int index = typeText.IndexOf('.');
|
||||
|
||||
if ((index <= 0) || (index >= typeText.Length - 1)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return Type.GetType(typeText + "," + typeText.Substring(0, index));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -20,6 +20,7 @@
|
|||
// limitations under the License.
|
||||
|
||||
using System.Linq;
|
||||
using ArchiSteamFarm.IPC;
|
||||
using NLog;
|
||||
using NLog.Config;
|
||||
using NLog.Targets;
|
||||
|
@ -99,7 +100,7 @@ namespace ArchiSteamFarm {
|
|||
LogManager.ReconfigExistingLoggers();
|
||||
}
|
||||
|
||||
IPC.OnNewHistoryTarget(historyTarget);
|
||||
ArchiKestrel.OnNewHistoryTarget(historyTarget);
|
||||
}
|
||||
|
||||
internal static void OnUserInputEnd() {
|
||||
|
@ -109,14 +110,14 @@ namespace ArchiSteamFarm {
|
|||
return;
|
||||
}
|
||||
|
||||
bool reconfig = false;
|
||||
bool reconfigure = false;
|
||||
|
||||
foreach (LoggingRule consoleLoggingRule in ConsoleLoggingRules.Where(consoleLoggingRule => !LogManager.Configuration.LoggingRules.Contains(consoleLoggingRule))) {
|
||||
LogManager.Configuration.LoggingRules.Add(consoleLoggingRule);
|
||||
reconfig = true;
|
||||
reconfigure = true;
|
||||
}
|
||||
|
||||
if (reconfig) {
|
||||
if (reconfigure) {
|
||||
LogManager.ReconfigExistingLoggers();
|
||||
}
|
||||
}
|
||||
|
@ -128,15 +129,15 @@ namespace ArchiSteamFarm {
|
|||
return;
|
||||
}
|
||||
|
||||
bool reconfig = false;
|
||||
bool reconfigure = false;
|
||||
|
||||
foreach (LoggingRule consoleLoggingRule in ConsoleLoggingRules) {
|
||||
if (LogManager.Configuration.LoggingRules.Remove(consoleLoggingRule)) {
|
||||
reconfig = true;
|
||||
reconfigure = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (reconfig) {
|
||||
if (reconfigure) {
|
||||
LogManager.ReconfigExistingLoggers();
|
||||
}
|
||||
}
|
||||
|
@ -162,7 +163,7 @@ namespace ArchiSteamFarm {
|
|||
}
|
||||
|
||||
HistoryTarget historyTarget = LogManager.Configuration.AllTargets.OfType<HistoryTarget>().FirstOrDefault();
|
||||
IPC.OnNewHistoryTarget(historyTarget);
|
||||
ArchiKestrel.OnNewHistoryTarget(historyTarget);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,6 +28,7 @@ using System.IO;
|
|||
using System.Linq;
|
||||
using System.Resources;
|
||||
using System.Threading.Tasks;
|
||||
using ArchiSteamFarm.IPC;
|
||||
using ArchiSteamFarm.Localization;
|
||||
using Newtonsoft.Json;
|
||||
using NLog;
|
||||
|
@ -341,8 +342,8 @@ namespace ArchiSteamFarm {
|
|||
WebBrowser.Init();
|
||||
WebBrowser = new WebBrowser(ASF.ArchiLogger, GlobalConfig.WebProxy, true);
|
||||
|
||||
if (GlobalConfig.IPC && (GlobalConfig.IPCPrefixes.Count > 0)) {
|
||||
IPC.Start(GlobalConfig.IPCPrefixes);
|
||||
if (GlobalConfig.IPC) {
|
||||
await ArchiKestrel.Start().ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -353,15 +354,9 @@ namespace ArchiSteamFarm {
|
|||
|
||||
ShutdownSequenceInitialized = true;
|
||||
|
||||
// Sockets created by HttpListener might still be running for a short while after complete app shutdown
|
||||
// Sockets created by IPC might still be running for a short while after complete app shutdown
|
||||
// Ensure that IPC is stopped before we finalize shutdown sequence
|
||||
if (IPC.IsRunning) {
|
||||
IPC.Stop();
|
||||
|
||||
for (byte i = 0; (i < WebBrowser.MaxTries) && IPC.IsRunning; i++) {
|
||||
await Task.Delay(1000).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
await ArchiKestrel.Stop().ConfigureAwait(false);
|
||||
|
||||
if (Bot.Bots.Count > 0) {
|
||||
IEnumerable<Task> tasks = Bot.Bots.Values.Select(bot => Task.Run(() => bot.Stop(true)));
|
||||
|
|
|
@ -73,15 +73,6 @@ namespace ArchiSteamFarm {
|
|||
return cookies.Count > 0 ? (from Cookie cookie in cookies where cookie.Name.Equals(name) select cookie.Value).FirstOrDefault() : null;
|
||||
}
|
||||
|
||||
internal static string GetUnifiedName(this Type type) {
|
||||
if (type == null) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(type));
|
||||
return null;
|
||||
}
|
||||
|
||||
return type.GenericTypeArguments.Length == 0 ? type.FullName : type.Namespace + "." + type.Name + string.Join("", type.GenericTypeArguments.Select(innerType => '[' + innerType.GetUnifiedName() + ']'));
|
||||
}
|
||||
|
||||
internal static uint GetUnixTime() => (uint) DateTimeOffset.UtcNow.ToUnixTimeSeconds();
|
||||
|
||||
internal static void InBackground(Action action, bool longRunning = false) {
|
||||
|
@ -185,4 +176,4 @@ namespace ArchiSteamFarm {
|
|||
|
||||
internal static string ToHumanReadable(this TimeSpan timeSpan) => timeSpan.Humanize(3, maxUnit: TimeUnit.Year, minUnit: TimeUnit.Second);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,9 +13,6 @@
|
|||
"InventoryLimiterDelay": 3,
|
||||
"IPC": false,
|
||||
"IPCPassword": null,
|
||||
"IPCPrefixes": [
|
||||
"http://127.0.0.1:1242/"
|
||||
],
|
||||
"LoginLimiterDelay": 10,
|
||||
"MaxFarmingTime": 10,
|
||||
"MaxTradeHoldDuration": 15,
|
||||
|
|
Loading…
Reference in a new issue