diff --git a/ArchiSteamFarm/ASF.cs b/ArchiSteamFarm/ASF.cs index cbb0d1597..550d71635 100644 --- a/ArchiSteamFarm/ASF.cs +++ b/ArchiSteamFarm/ASF.cs @@ -877,7 +877,7 @@ namespace ArchiSteamFarm { [PublicAPI] public enum EUserInputType : byte { - Unknown, + None, DeviceID, Login, Password, diff --git a/ArchiSteamFarm/Bot.cs b/ArchiSteamFarm/Bot.cs index 5699b1643..f678479c0 100755 --- a/ArchiSteamFarm/Bot.cs +++ b/ArchiSteamFarm/Bot.cs @@ -94,6 +94,7 @@ namespace ArchiSteamFarm { [PublicAPI] public uint GamesToRedeemInBackgroundCount => BotDatabase?.GamesToRedeemInBackgroundCount ?? 0; + [JsonProperty] [PublicAPI] public bool HasMobileAuthenticator => BotDatabase?.MobileAuthenticator != null; @@ -153,23 +154,34 @@ namespace ArchiSteamFarm { #pragma warning restore IDE0051 [JsonProperty] + [PublicAPI] public EAccountFlags AccountFlags { get; private set; } [JsonProperty] + [PublicAPI] public BotConfig BotConfig { get; private set; } [JsonProperty] + [PublicAPI] public bool KeepRunning { get; private set; } [JsonProperty] + [PublicAPI] public string Nickname { get; private set; } + [JsonProperty] + [PublicAPI] + public ASF.EUserInputType RequiredInput { get; private set; } + + [JsonProperty] [PublicAPI] public ulong SteamID { get; private set; } + [JsonProperty] [PublicAPI] public long WalletBalance { get; private set; } + [JsonProperty] [PublicAPI] public ECurrencyCode WalletCurrency { get; private set; } @@ -477,11 +489,11 @@ namespace ArchiSteamFarm { } [PublicAPI] - public void SetUserInput(ASF.EUserInputType inputType, string inputValue) { - if ((inputType == ASF.EUserInputType.Unknown) || !Enum.IsDefined(typeof(ASF.EUserInputType), inputType) || string.IsNullOrEmpty(inputValue)) { + public bool SetUserInput(ASF.EUserInputType inputType, string inputValue) { + if ((inputType == ASF.EUserInputType.None) || !Enum.IsDefined(typeof(ASF.EUserInputType), inputType) || string.IsNullOrEmpty(inputValue)) { ArchiLogger.LogNullError(nameof(inputType) + " || " + nameof(inputValue)); - return; + return false; } // This switch should cover ONLY bot properties @@ -491,26 +503,32 @@ namespace ArchiSteamFarm { break; case ASF.EUserInputType.Login: - if (BotConfig != null) { - BotConfig.SteamLogin = inputValue; + if (BotConfig == null) { + return false; } + BotConfig.SteamLogin = inputValue; + break; case ASF.EUserInputType.Password: - if (BotConfig != null) { - BotConfig.DecryptedSteamPassword = inputValue; + if (BotConfig == null) { + return false; } + BotConfig.DecryptedSteamPassword = inputValue; + break; case ASF.EUserInputType.SteamGuard: AuthCode = inputValue; break; case ASF.EUserInputType.SteamParentalCode: - if (BotConfig != null) { - BotConfig.SteamParentalCode = inputValue; + if (BotConfig == null) { + return false; } + BotConfig.SteamParentalCode = inputValue; + break; case ASF.EUserInputType.TwoFactorAuthentication: TwoFactorCode = inputValue; @@ -521,6 +539,12 @@ namespace ArchiSteamFarm { break; } + + if (RequiredInput == inputType) { + RequiredInput = ASF.EUserInputType.None; + } + + return true; } internal void AddGamesToRedeemInBackground(IOrderedDictionary gamesToRedeemInBackground) { @@ -1733,13 +1757,13 @@ namespace ArchiSteamFarm { ArchiLogger.LogGenericWarning(Strings.BotAuthenticatorInvalidDeviceID); if (string.IsNullOrEmpty(DeviceID)) { + RequiredInput = ASF.EUserInputType.DeviceID; + string deviceID = await Logging.GetUserInput(ASF.EUserInputType.DeviceID, BotName).ConfigureAwait(false); - if (string.IsNullOrEmpty(deviceID)) { + if (string.IsNullOrEmpty(deviceID) || !SetUserInput(ASF.EUserInputType.DeviceID, deviceID)) { return; } - - SetUserInput(ASF.EUserInputType.DeviceID, deviceID); } if (!MobileAuthenticator.IsValidDeviceID(DeviceID)) { @@ -1789,23 +1813,23 @@ namespace ArchiSteamFarm { private async Task InitLoginAndPassword(bool requiresPassword) { if (string.IsNullOrEmpty(BotConfig.SteamLogin)) { + RequiredInput = ASF.EUserInputType.Login; + string steamLogin = await Logging.GetUserInput(ASF.EUserInputType.Login, BotName).ConfigureAwait(false); - if (string.IsNullOrEmpty(steamLogin)) { + if (string.IsNullOrEmpty(steamLogin) || !SetUserInput(ASF.EUserInputType.Login, steamLogin)) { return false; } - - SetUserInput(ASF.EUserInputType.Login, steamLogin); } if (requiresPassword && string.IsNullOrEmpty(BotConfig.DecryptedSteamPassword)) { + RequiredInput = ASF.EUserInputType.Password; + string steamPassword = await Logging.GetUserInput(ASF.EUserInputType.Password, BotName).ConfigureAwait(false); - if (string.IsNullOrEmpty(steamPassword)) { + if (string.IsNullOrEmpty(steamPassword) || !SetUserInput(ASF.EUserInputType.Password, steamPassword)) { return false; } - - SetUserInput(ASF.EUserInputType.Password, steamPassword); } return true; @@ -1815,6 +1839,7 @@ namespace ArchiSteamFarm { AccountFlags = EAccountFlags.NormalUser; AvatarHash = Nickname = null; MasterChatGroupID = 0; + RequiredInput = ASF.EUserInputType.None; WalletBalance = 0; WalletCurrency = ECurrencyCode.Invalid; @@ -2406,28 +2431,24 @@ namespace ArchiSteamFarm { break; case EResult.AccountLogonDenied: + RequiredInput = ASF.EUserInputType.SteamGuard; + string authCode = await Logging.GetUserInput(ASF.EUserInputType.SteamGuard, BotName).ConfigureAwait(false); - if (string.IsNullOrEmpty(authCode)) { + if (string.IsNullOrEmpty(authCode) || !SetUserInput(ASF.EUserInputType.SteamGuard, authCode)) { Stop(); - - break; } - SetUserInput(ASF.EUserInputType.SteamGuard, authCode); - break; case EResult.AccountLoginDeniedNeedTwoFactor: if (!HasMobileAuthenticator) { + RequiredInput = ASF.EUserInputType.TwoFactorAuthentication; + string twoFactorCode = await Logging.GetUserInput(ASF.EUserInputType.TwoFactorAuthentication, BotName).ConfigureAwait(false); - if (string.IsNullOrEmpty(twoFactorCode)) { + if (string.IsNullOrEmpty(twoFactorCode) || !SetUserInput(ASF.EUserInputType.TwoFactorAuthentication, twoFactorCode)) { Stop(); - - break; } - - SetUserInput(ASF.EUserInputType.TwoFactorAuthentication, twoFactorCode); } break; @@ -2474,32 +2495,36 @@ namespace ArchiSteamFarm { if (!string.IsNullOrEmpty(steamParentalCode)) { if (BotConfig.SteamParentalCode != steamParentalCode) { - SetUserInput(ASF.EUserInputType.SteamParentalCode, steamParentalCode); + if (!SetUserInput(ASF.EUserInputType.SteamParentalCode, steamParentalCode)) { + Stop(); + + break; + } } } else if (string.IsNullOrEmpty(BotConfig.SteamParentalCode) || (BotConfig.SteamParentalCode.Length != BotConfig.SteamParentalCodeLength)) { + RequiredInput = ASF.EUserInputType.SteamParentalCode; + steamParentalCode = await Logging.GetUserInput(ASF.EUserInputType.SteamParentalCode, BotName).ConfigureAwait(false); - if (string.IsNullOrEmpty(steamParentalCode) || (steamParentalCode.Length != BotConfig.SteamParentalCodeLength)) { + if (string.IsNullOrEmpty(steamParentalCode) || (steamParentalCode.Length != BotConfig.SteamParentalCodeLength) || !SetUserInput(ASF.EUserInputType.SteamParentalCode, steamParentalCode)) { Stop(); break; } - - SetUserInput(ASF.EUserInputType.SteamParentalCode, steamParentalCode); } } else { SteamParentalActive = false; } } else if (SteamParentalActive && !string.IsNullOrEmpty(BotConfig.SteamParentalCode) && (BotConfig.SteamParentalCode.Length != BotConfig.SteamParentalCodeLength)) { + RequiredInput = ASF.EUserInputType.SteamParentalCode; + string steamParentalCode = await Logging.GetUserInput(ASF.EUserInputType.SteamParentalCode, BotName).ConfigureAwait(false); - if (string.IsNullOrEmpty(steamParentalCode) || (steamParentalCode.Length != BotConfig.SteamParentalCodeLength)) { + if (string.IsNullOrEmpty(steamParentalCode) || (steamParentalCode.Length != BotConfig.SteamParentalCodeLength) || !SetUserInput(ASF.EUserInputType.SteamParentalCode, steamParentalCode)) { Stop(); break; } - - SetUserInput(ASF.EUserInputType.SteamParentalCode, steamParentalCode); } ArchiWebHandler.OnVanityURLChanged(callback.VanityURL); diff --git a/ArchiSteamFarm/Commands.cs b/ArchiSteamFarm/Commands.cs index 445c659fe..500080fb5 100644 --- a/ArchiSteamFarm/Commands.cs +++ b/ArchiSteamFarm/Commands.cs @@ -1416,7 +1416,7 @@ namespace ArchiSteamFarm { return FormatBotResponse(Strings.ErrorFunctionOnlyInHeadlessMode); } - if (!Enum.TryParse(propertyName, true, out ASF.EUserInputType inputType) || (inputType == ASF.EUserInputType.Unknown) || !Enum.IsDefined(typeof(ASF.EUserInputType), inputType)) { + if (!Enum.TryParse(propertyName, true, out ASF.EUserInputType inputType) || (inputType == ASF.EUserInputType.None) || !Enum.IsDefined(typeof(ASF.EUserInputType), inputType)) { return FormatBotResponse(string.Format(Strings.ErrorIsInvalid, nameof(inputType))); } diff --git a/ArchiSteamFarm/IPC/Controllers/Api/BotController.cs b/ArchiSteamFarm/IPC/Controllers/Api/BotController.cs index ff4b7c063..08c15a1f6 100644 --- a/ArchiSteamFarm/IPC/Controllers/Api/BotController.cs +++ b/ArchiSteamFarm/IPC/Controllers/Api/BotController.cs @@ -242,6 +242,35 @@ namespace ArchiSteamFarm.IPC.Controllers.Api { return Ok(new GenericResponse>(result)); } + /// + /// Provides input value to given bot for next usage. + /// + [Consumes("application/json")] + [HttpPost("{botNames:required}/Input")] + [ProducesResponseType(typeof(GenericResponse), (int) HttpStatusCode.OK)] + [ProducesResponseType(typeof(GenericResponse), (int) HttpStatusCode.BadRequest)] + public async Task> InputPost(string botNames, [FromBody] BotInputRequest request) { + if (string.IsNullOrEmpty(botNames) || (request == null)) { + ASF.ArchiLogger.LogNullError(nameof(botNames) + " || " + nameof(request)); + + return BadRequest(new GenericResponse(false, string.Format(Strings.ErrorIsEmpty, nameof(botNames) + " || " + nameof(request)))); + } + + if ((request.Type == ASF.EUserInputType.None) || !Enum.IsDefined(typeof(ASF.EUserInputType), request.Type) || string.IsNullOrEmpty(request.Value)) { + return BadRequest(new GenericResponse(false, string.Format(Strings.ErrorIsInvalid, nameof(request.Type) + " || " + nameof(request.Value)))); + } + + HashSet bots = Bot.GetBots(botNames); + + if ((bots == null) || (bots.Count == 0)) { + return BadRequest(new GenericResponse(false, string.Format(Strings.BotNotFound, botNames))); + } + + IList results = await Utilities.InParallel(bots.Select(bot => Task.Run(() => bot.SetUserInput(request.Type, request.Value)))).ConfigureAwait(false); + + return Ok(results.All(result => result) ? new GenericResponse(true) : new GenericResponse(false, Strings.WarningFailed)); + } + /// /// Pauses given bots. /// diff --git a/ArchiSteamFarm/IPC/Requests/BotInputRequest.cs b/ArchiSteamFarm/IPC/Requests/BotInputRequest.cs new file mode 100644 index 000000000..70ce1525a --- /dev/null +++ b/ArchiSteamFarm/IPC/Requests/BotInputRequest.cs @@ -0,0 +1,43 @@ +// _ _ _ ____ _ _____ +// / \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___ +// / _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \ +// / ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | | +// /_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_| +// | +// Copyright 2015-2020 Ɓ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 BotInputRequest { + /// + /// Specifies the type of the input. + /// + [JsonProperty(Required = Required.Always)] + public readonly ASF.EUserInputType Type; + + /// + /// Specifies the value for given input type (declared in ) + /// + [JsonProperty(Required = Required.Always)] + public readonly string Value; + + [JsonConstructor] + private BotInputRequest() { } + } +} diff --git a/ArchiSteamFarm/NLog/Logging.cs b/ArchiSteamFarm/NLog/Logging.cs index 824fbb329..032b15751 100644 --- a/ArchiSteamFarm/NLog/Logging.cs +++ b/ArchiSteamFarm/NLog/Logging.cs @@ -65,7 +65,9 @@ namespace ArchiSteamFarm.NLog { } internal static async Task GetUserInput(ASF.EUserInputType userInputType, string botName = SharedInfo.ASF) { - if (userInputType == ASF.EUserInputType.Unknown) { + if ((userInputType == ASF.EUserInputType.None) || !Enum.IsDefined(typeof(ASF.EUserInputType), userInputType) || string.IsNullOrEmpty(botName)) { + ASF.ArchiLogger.LogNullError(nameof(userInputType) + " || " + nameof(botName)); + return null; } @@ -118,10 +120,8 @@ namespace ArchiSteamFarm.NLog { break; default: ASF.ArchiLogger.LogGenericError(string.Format(Strings.WarningUnknownValuePleaseReport, nameof(userInputType), userInputType)); - Console.Write(Bot.FormatBotResponse(string.Format(Strings.UserInputUnknown, userInputType), botName)); - result = ConsoleReadLine(); - break; + return null; } if (!Console.IsOutputRedirected) { @@ -132,9 +132,9 @@ namespace ArchiSteamFarm.NLog { ASF.ArchiLogger.LogGenericException(e); return null; + } finally { + OnUserInputEnd(); } - - OnUserInputEnd(); } finally { ConsoleSemaphore.Release(); }