diff --git a/ArchiSteamFarm/Bot.cs b/ArchiSteamFarm/Bot.cs index 856d4e4c7..a3a33a1d4 100755 --- a/ArchiSteamFarm/Bot.cs +++ b/ArchiSteamFarm/Bot.cs @@ -265,6 +265,19 @@ namespace ArchiSteamFarm { Trading?.Dispose(); } + internal async Task AddGamesToRedeemInBackground(IOrderedDictionary gamesToRedeemInBackground) { + if ((gamesToRedeemInBackground == null) || (gamesToRedeemInBackground.Count == 0)) { + ArchiLogger.LogNullError(nameof(gamesToRedeemInBackground)); + return; + } + + await BotDatabase.AddGamesToRedeemInBackground(gamesToRedeemInBackground).ConfigureAwait(false); + + if ((GamesRedeemerInBackgroundTimer == null) && BotDatabase.HasGamesToRedeemInBackground && IsConnectedAndLoggedOn) { + Utilities.InBackground(RedeemGamesInBackground); + } + } + internal async Task DeleteAllRelatedFiles() { await BotDatabase.MakeReadOnly().ConfigureAwait(false); @@ -675,7 +688,11 @@ namespace ArchiSteamFarm { } if (gamesToRedeemInBackground.Count > 0) { - await ValidateAndAddGamesToRedeemInBackground(gamesToRedeemInBackground).ConfigureAwait(false); + IOrderedDictionary validGamesToRedeemInBackground = ValidateGamesToRedeemInBackground(gamesToRedeemInBackground); + + if ((validGamesToRedeemInBackground != null) && (validGamesToRedeemInBackground.Count > 0)) { + await AddGamesToRedeemInBackground(validGamesToRedeemInBackground).ConfigureAwait(false); + } } File.Delete(filePath); @@ -1103,10 +1120,10 @@ namespace ArchiSteamFarm { } } - internal async Task ValidateAndAddGamesToRedeemInBackground(OrderedDictionary gamesToRedeemInBackground) { + internal static IOrderedDictionary ValidateGamesToRedeemInBackground(IOrderedDictionary gamesToRedeemInBackground) { if ((gamesToRedeemInBackground == null) || (gamesToRedeemInBackground.Count == 0)) { - ArchiLogger.LogNullError(nameof(gamesToRedeemInBackground)); - return false; + ASF.ArchiLogger.LogNullError(nameof(gamesToRedeemInBackground)); + return gamesToRedeemInBackground; } HashSet invalidKeys = new HashSet(); @@ -1117,16 +1134,16 @@ namespace ArchiSteamFarm { string key = game.Key as string; if (string.IsNullOrEmpty(key)) { invalid = true; - ArchiLogger.LogGenericWarning(string.Format(Strings.ErrorIsInvalid, nameof(key))); + ASF.ArchiLogger.LogGenericWarning(string.Format(Strings.ErrorIsInvalid, nameof(key))); } else if (!Utilities.IsValidCdKey(key)) { invalid = true; - ArchiLogger.LogGenericWarning(string.Format(Strings.ErrorIsInvalid, key)); + ASF.ArchiLogger.LogGenericWarning(string.Format(Strings.ErrorIsInvalid, key)); } string name = game.Value as string; if (string.IsNullOrEmpty(name)) { invalid = true; - ArchiLogger.LogGenericWarning(string.Format(Strings.ErrorIsInvalid, nameof(name))); + ASF.ArchiLogger.LogGenericWarning(string.Format(Strings.ErrorIsInvalid, nameof(name))); } if (invalid) { @@ -1138,19 +1155,9 @@ namespace ArchiSteamFarm { foreach (string invalidKey in invalidKeys) { gamesToRedeemInBackground.Remove(invalidKey); } - - if (gamesToRedeemInBackground.Count == 0) { - return false; - } } - await BotDatabase.AddGamesToRedeemInBackground(gamesToRedeemInBackground).ConfigureAwait(false); - - if ((GamesRedeemerInBackgroundTimer == null) && BotDatabase.HasGamesToRedeemInBackground && IsConnectedAndLoggedOn) { - Utilities.InBackground(RedeemGamesInBackground); - } - - return true; + return gamesToRedeemInBackground; } private async Task CheckOccupationStatus() { diff --git a/ArchiSteamFarm/BotDatabase.cs b/ArchiSteamFarm/BotDatabase.cs index 0c796de44..c60269b65 100644 --- a/ArchiSteamFarm/BotDatabase.cs +++ b/ArchiSteamFarm/BotDatabase.cs @@ -87,7 +87,7 @@ namespace ArchiSteamFarm { } } - internal async Task AddGamesToRedeemInBackground(OrderedDictionary games) { + internal async Task AddGamesToRedeemInBackground(IOrderedDictionary games) { if ((games == null) || (games.Count == 0)) { ASF.ArchiLogger.LogNullError(nameof(games)); return; diff --git a/ArchiSteamFarm/IPC/Controllers/Api/BotController.cs b/ArchiSteamFarm/IPC/Controllers/Api/BotController.cs index b75e0e0d6..f097fe461 100644 --- a/ArchiSteamFarm/IPC/Controllers/Api/BotController.cs +++ b/ArchiSteamFarm/IPC/Controllers/Api/BotController.cs @@ -76,40 +76,58 @@ namespace ArchiSteamFarm.IPC.Controllers.Api { /// Updates bot config of given bot. /// [Consumes("application/json")] - [HttpPost("{botName:required}")] - [ProducesResponseType(typeof(GenericResponse), 200)] - public async Task> BotPost(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)))); + [HttpPost("{botNames:required}")] + [ProducesResponseType(typeof(GenericResponse>), 200)] + public async Task>>> BotPost(string botNames, [FromBody] BotRequest 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)))); } (bool valid, string errorMessage) = request.BotConfig.CheckValidation(); if (!valid) { - return BadRequest(new GenericResponse(false, errorMessage)); - } - - if (Bot.Bots.TryGetValue(botName, out Bot bot)) { - if (!request.BotConfig.IsSteamLoginSet && bot.BotConfig.IsSteamLoginSet) { - request.BotConfig.SteamLogin = bot.BotConfig.SteamLogin; - } - - if (!request.BotConfig.IsSteamPasswordSet && bot.BotConfig.IsSteamPasswordSet) { - request.BotConfig.DecryptedSteamPassword = bot.BotConfig.DecryptedSteamPassword; - } - - if (!request.BotConfig.IsSteamParentalCodeSet && bot.BotConfig.IsSteamParentalCodeSet) { - request.BotConfig.SteamParentalCode = bot.BotConfig.SteamParentalCode; - } + return BadRequest(new GenericResponse>(false, errorMessage)); } request.BotConfig.ShouldSerializeEverything = false; request.BotConfig.ShouldSerializeHelperProperties = false; - string filePath = Path.Combine(SharedInfo.ConfigDirectory, botName + SharedInfo.ConfigExtension); + string originalSteamLogin = request.BotConfig.SteamLogin; + string originalDecryptedSteamPassword = request.BotConfig.DecryptedSteamPassword; + string originalSteamParentalCode = request.BotConfig.SteamParentalCode; - bool result = await BotConfig.Write(filePath, request.BotConfig).ConfigureAwait(false); - return Ok(new GenericResponse(result)); + HashSet bots = botNames.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Where(botName => botName != SharedInfo.ASF).ToHashSet(); + + Dictionary result = new Dictionary(bots.Count); + + foreach (string botName in bots) { + bool botExists = Bot.Bots.TryGetValue(botName, out Bot bot); + + if (botExists) { + if (!request.BotConfig.IsSteamLoginSet && bot.BotConfig.IsSteamLoginSet) { + request.BotConfig.SteamLogin = bot.BotConfig.SteamLogin; + } + + if (!request.BotConfig.IsSteamPasswordSet && bot.BotConfig.IsSteamPasswordSet) { + request.BotConfig.DecryptedSteamPassword = bot.BotConfig.DecryptedSteamPassword; + } + + if (!request.BotConfig.IsSteamParentalCodeSet && bot.BotConfig.IsSteamParentalCodeSet) { + request.BotConfig.SteamParentalCode = bot.BotConfig.SteamParentalCode; + } + } + + string filePath = Path.Combine(SharedInfo.ConfigDirectory, botName + SharedInfo.ConfigExtension); + result[botName] = await BotConfig.Write(filePath, request.BotConfig).ConfigureAwait(false); + + if (botExists) { + request.BotConfig.SteamLogin = originalSteamLogin; + request.BotConfig.DecryptedSteamPassword = originalDecryptedSteamPassword; + request.BotConfig.SteamParentalCode = originalSteamParentalCode; + } + } + + return Ok(new GenericResponse>(result.Values.All(value => value), result)); } /// @@ -164,24 +182,37 @@ namespace ArchiSteamFarm.IPC.Controllers.Api { /// Adds keys to redeem using BGR to given bot. /// [Consumes("application/json")] - [HttpPost("{botName:required}/GamesToRedeemInBackground")] - [ProducesResponseType(typeof(GenericResponse), 200)] - public async Task>> GamesToRedeemInBackgroundPost(string botName, [FromBody] BotGamesToRedeemInBackgroundRequest 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)))); + [HttpPost("{botNames:required}/GamesToRedeemInBackground")] + [ProducesResponseType(typeof(GenericResponse>), 200)] + public async Task>>> GamesToRedeemInBackgroundPost(string botNames, [FromBody] BotGamesToRedeemInBackgroundRequest 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.GamesToRedeemInBackground.Count == 0) { - return BadRequest(new GenericResponse(false, string.Format(Strings.ErrorIsEmpty, nameof(request.GamesToRedeemInBackground)))); + return BadRequest(new GenericResponse>(false, string.Format(Strings.ErrorIsEmpty, nameof(request.GamesToRedeemInBackground)))); } - if (!Bot.Bots.TryGetValue(botName, out Bot bot)) { - return BadRequest(new GenericResponse(false, string.Format(Strings.BotNotFound, botName))); + HashSet bots = Bot.GetBots(botNames); + if ((bots == null) || (bots.Count == 0)) { + return BadRequest(new GenericResponse>(false, string.Format(Strings.BotNotFound, botNames))); } - bool result = await bot.ValidateAndAddGamesToRedeemInBackground(request.GamesToRedeemInBackground).ConfigureAwait(false); - return Ok(new GenericResponse(result, request.GamesToRedeemInBackground)); + IOrderedDictionary validGamesToRedeemInBackground = Bot.ValidateGamesToRedeemInBackground(request.GamesToRedeemInBackground); + if ((validGamesToRedeemInBackground == null) || (validGamesToRedeemInBackground.Count == 0)) { + return BadRequest(new GenericResponse>(false, string.Format(Strings.ErrorIsEmpty, nameof(validGamesToRedeemInBackground)))); + } + + await Utilities.InParallel(bots.Select(bot => bot.AddGamesToRedeemInBackground(validGamesToRedeemInBackground))).ConfigureAwait(false); + + Dictionary result = new Dictionary(bots.Count); + + foreach (Bot bot in bots) { + result[bot.BotName] = validGamesToRedeemInBackground; + } + + return Ok(new GenericResponse>(result)); } /// @@ -213,31 +244,39 @@ namespace ArchiSteamFarm.IPC.Controllers.Api { /// Redeem result can be a null value, this means that ASF didn't even attempt to send a request (e.g. because of bot not being connected to Steam network). /// [Consumes("application/json")] - [HttpPost("{botName:required}/Redeem")] - [ProducesResponseType(typeof(GenericResponse>), 200)] - public async Task>>> RedeemPost(string botName, [FromBody] BotRedeemRequest 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)))); + [HttpPost("{botNames:required}/Redeem")] + [ProducesResponseType(typeof(GenericResponse>>), 200)] + public async Task>>>> RedeemPost(string botNames, [FromBody] BotRedeemRequest 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.KeysToRedeem.Count == 0) { - return BadRequest(new GenericResponse>(false, string.Format(Strings.ErrorIsEmpty, nameof(request.KeysToRedeem)))); + return BadRequest(new GenericResponse>>(false, string.Format(Strings.ErrorIsEmpty, nameof(request.KeysToRedeem)))); } - if (!Bot.Bots.TryGetValue(botName, out Bot bot)) { - return BadRequest(new GenericResponse>(false, string.Format(Strings.BotNotFound, botName))); + 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(request.KeysToRedeem.Select(key => bot.Actions.RedeemKey(key))).ConfigureAwait(false); + IList results = await Utilities.InParallel(bots.Select(bot => request.KeysToRedeem.Select(key => bot.Actions.RedeemKey(key))).SelectMany(task => task)).ConfigureAwait(false); - Dictionary result = new Dictionary(request.KeysToRedeem.Count); + Dictionary> result = new Dictionary>(bots.Count); - foreach (string key in request.KeysToRedeem) { - result[key] = results[result.Count]; + int count = 0; + + foreach (Bot bot in bots) { + Dictionary responses = new Dictionary(request.KeysToRedeem.Count); + result[bot.BotName] = responses; + + foreach (string key in request.KeysToRedeem) { + responses[key] = results[count++]; + } } - return Ok(new GenericResponse>(result.Values.All(value => value != null), result)); + return Ok(new GenericResponse>>(result.Values.SelectMany(responses => responses.Values).All(value => value != null), result)); } ///