From 5abace39f93e351b60bc028d473795502db37c2c Mon Sep 17 00:00:00 2001 From: JustArchi Date: Wed, 19 Sep 2018 18:25:17 +0200 Subject: [PATCH] Add new Bot actions + a lot of performance improvements --- ArchiSteamFarm/ASF.cs | 14 +- ArchiSteamFarm/Actions.cs | 37 +- ArchiSteamFarm/Bot.cs | 30 +- ArchiSteamFarm/CardsFarmer.cs | 40 +- ArchiSteamFarm/Commands.cs | 529 ++---------------- .../IPC/Controllers/Api/ASFController.cs | 12 +- .../IPC/Controllers/Api/BotController.cs | 59 +- .../IPC/Controllers/Api/CommandController.cs | 4 +- .../GamesToRedeemInBackgroundController.cs | 48 +- .../Controllers/Api/StructureController.cs | 2 +- .../IPC/Controllers/Api/TypeController.cs | 4 +- .../IPC/Responses/GenericResponse.cs | 13 +- .../IPC/{Utilities.cs => WebUtilities.cs} | 2 +- ArchiSteamFarm/Program.cs | 15 +- ArchiSteamFarm/Trading.cs | 16 +- ArchiSteamFarm/Utilities.cs | 44 ++ 16 files changed, 193 insertions(+), 676 deletions(-) rename ArchiSteamFarm/IPC/{Utilities.cs => WebUtilities.cs} (98%) diff --git a/ArchiSteamFarm/ASF.cs b/ArchiSteamFarm/ASF.cs index 0a9fa2125..9168ff7e5 100644 --- a/ArchiSteamFarm/ASF.cs +++ b/ArchiSteamFarm/ASF.cs @@ -50,19 +50,7 @@ namespace ArchiSteamFarm { await Bot.InitializeSteamConfiguration(Program.GlobalConfig.SteamProtocols, Program.GlobalDatabase.CellID, Program.GlobalDatabase.ServerListProvider).ConfigureAwait(false); try { - IEnumerable tasks = Directory.EnumerateFiles(SharedInfo.ConfigDirectory, "*" + SharedInfo.ConfigExtension).Select(Path.GetFileNameWithoutExtension).Where(botName => !string.IsNullOrEmpty(botName) && IsValidBotName(botName)).OrderBy(botName => botName).Select(Bot.RegisterBot); - - switch (Program.GlobalConfig.OptimizationMode) { - case GlobalConfig.EOptimizationMode.MinMemoryUsage: - foreach (Task task in tasks) { - await task.ConfigureAwait(false); - } - - break; - default: - await Task.WhenAll(tasks).ConfigureAwait(false); - break; - } + await Utilities.InParallel(Directory.EnumerateFiles(SharedInfo.ConfigDirectory, "*" + SharedInfo.ConfigExtension).Select(Path.GetFileNameWithoutExtension).Where(botName => !string.IsNullOrEmpty(botName) && IsValidBotName(botName)).OrderBy(botName => botName).Select(Bot.RegisterBot)).ConfigureAwait(false); } catch (Exception e) { ArchiLogger.LogGenericException(e); return; diff --git a/ArchiSteamFarm/Actions.cs b/ArchiSteamFarm/Actions.cs index bd5db4223..399882920 100644 --- a/ArchiSteamFarm/Actions.cs +++ b/ArchiSteamFarm/Actions.cs @@ -38,6 +38,8 @@ namespace ArchiSteamFarm { private readonly ConcurrentHashSet HandledGifts = new ConcurrentHashSet(); private readonly SemaphoreSlim LootingSemaphore = new SemaphoreSlim(1, 1); + internal bool SkipFirstShutdown { get; set; } + private bool LootingAllowed = true; private bool LootingScheduled; private bool ProcessingGiftsScheduled; @@ -60,21 +62,7 @@ namespace ArchiSteamFarm { return await Bot.BotDatabase.MobileAuthenticator.HandleConfirmations(confirmations, accept).ConfigureAwait(false); } - IEnumerable> tasks = confirmations.Select(Bot.BotDatabase.MobileAuthenticator.GetConfirmationDetails); - ICollection results; - - switch (Program.GlobalConfig.OptimizationMode) { - case GlobalConfig.EOptimizationMode.MinMemoryUsage: - results = new List(confirmations.Count); - foreach (Task task in tasks) { - results.Add(await task.ConfigureAwait(false)); - } - - break; - default: - results = await Task.WhenAll(tasks).ConfigureAwait(false); - break; - } + IList results = await Utilities.InParallel(confirmations.Select(Bot.BotDatabase.MobileAuthenticator.GetConfirmationDetails)).ConfigureAwait(false); foreach (MobileAuthenticator.Confirmation confirmation in results.Where(details => (details != null) && ((acceptedType != details.Type) || ((acceptedSteamID != 0) && (details.OtherSteamID64 != 0) && (acceptedSteamID != details.OtherSteamID64)) || ((acceptedTradeIDs != null) && (details.TradeOfferID != 0) && !acceptedTradeIDs.Contains(details.TradeOfferID)))).Select(details => details.Confirmation)) { confirmations.Remove(confirmation); @@ -250,6 +238,25 @@ namespace ArchiSteamFarm { return (true, Strings.Done); } + internal (bool Success, string Output) Start() { + if (Bot.KeepRunning) { + return (false, Strings.BotAlreadyRunning); + } + + SkipFirstShutdown = true; + Utilities.InBackground(Bot.Start); + return (true, Strings.Done); + } + + internal (bool Success, string Output) Stop() { + if (!Bot.KeepRunning) { + return (false, Strings.BotAlreadyStopped); + } + + Bot.Stop(); + return (true, Strings.Done); + } + internal bool SwitchLootingAllowed() => LootingAllowed = !LootingAllowed; internal static async Task<(bool Success, Version Version)> Update() { diff --git a/ArchiSteamFarm/Bot.cs b/ArchiSteamFarm/Bot.cs index 740abb718..8b2de3fcf 100755 --- a/ArchiSteamFarm/Bot.cs +++ b/ArchiSteamFarm/Bot.cs @@ -124,7 +124,6 @@ namespace ArchiSteamFarm { internal bool PlayingBlocked { get; private set; } internal bool PlayingWasBlocked { get; private set; } - internal bool SkipFirstShutdown { private get; set; } [JsonProperty] private EAccountFlags AccountFlags; @@ -623,22 +622,7 @@ namespace ArchiSteamFarm { } internal async Task<(Dictionary UnusedKeys, Dictionary UsedKeys)> GetUsedAndUnusedKeys() { - IEnumerable>> tasks = new[] { KeysToRedeemUnusedFilePath, KeysToRedeemUsedFilePath }.Select(GetKeysFromFile); - IList> results; - - switch (Program.GlobalConfig.OptimizationMode) { - case GlobalConfig.EOptimizationMode.MinMemoryUsage: - results = new List>(2); - foreach (Task> task in tasks) { - results.Add(await task.ConfigureAwait(false)); - } - - break; - default: - results = await Task.WhenAll(tasks).ConfigureAwait(false); - break; - } - + IList> results = await Utilities.InParallel(new[] { KeysToRedeemUnusedFilePath, KeysToRedeemUsedFilePath }.Select(GetKeysFromFile)).ConfigureAwait(false); return (results[0], results[1]); } @@ -818,8 +802,8 @@ namespace ArchiSteamFarm { return; } - if (SkipFirstShutdown) { - SkipFirstShutdown = false; + if (Actions.SkipFirstShutdown) { + Actions.SkipFirstShutdown = false; } else { Stop(); } @@ -1089,10 +1073,10 @@ namespace ArchiSteamFarm { FamilySharingInactivityTimer = null; } - internal async Task ValidateAndAddGamesToRedeemInBackground(OrderedDictionary gamesToRedeemInBackground) { + internal async Task ValidateAndAddGamesToRedeemInBackground(OrderedDictionary gamesToRedeemInBackground) { if ((gamesToRedeemInBackground == null) || (gamesToRedeemInBackground.Count == 0)) { ArchiLogger.LogNullError(nameof(gamesToRedeemInBackground)); - return; + return false; } HashSet invalidKeys = new HashSet(); @@ -1126,7 +1110,7 @@ namespace ArchiSteamFarm { } if (gamesToRedeemInBackground.Count == 0) { - return; + return false; } } @@ -1135,6 +1119,8 @@ namespace ArchiSteamFarm { if ((GamesRedeemerInBackgroundTimer == null) && BotDatabase.HasGamesToRedeemInBackground && IsConnectedAndLoggedOn) { Utilities.InBackground(RedeemGamesInBackground); } + + return true; } private async Task CheckFamilySharingInactivity() { diff --git a/ArchiSteamFarm/CardsFarmer.cs b/ArchiSteamFarm/CardsFarmer.cs index f11e0dc22..fb9361d8e 100755 --- a/ArchiSteamFarm/CardsFarmer.cs +++ b/ArchiSteamFarm/CardsFarmer.cs @@ -342,7 +342,7 @@ namespace ArchiSteamFarm { return; } - HashSet backgroundTasks = new HashSet(); + List backgroundTasks = null; foreach (HtmlNode htmlNode in htmlNodes) { HtmlNode statsNode = htmlNode.SelectSingleNode(".//div[@class='badge_title_stats_content']"); @@ -556,11 +556,16 @@ namespace ArchiSteamFarm { GamesToFarm.Add(new Game(appID, name, hours, cardsRemaining, badgeLevel)); } else { Task task = CheckGame(appID, name, hours, badgeLevel); + switch (Program.GlobalConfig.OptimizationMode) { case GlobalConfig.EOptimizationMode.MinMemoryUsage: await task.ConfigureAwait(false); break; default: + if (backgroundTasks == null) { + backgroundTasks = new List(); + } + backgroundTasks.Add(task); break; } @@ -568,7 +573,7 @@ namespace ArchiSteamFarm { } // If we have any background tasks, wait for them - if (backgroundTasks.Count > 0) { + if ((backgroundTasks != null) && (backgroundTasks.Count > 0)) { await Task.WhenAll(backgroundTasks).ConfigureAwait(false); } } @@ -856,41 +861,36 @@ namespace ArchiSteamFarm { GamesToFarm.Clear(); - HashSet tasks = new HashSet(); Task mainTask = CheckPage(htmlDocument); switch (Program.GlobalConfig.OptimizationMode) { case GlobalConfig.EOptimizationMode.MinMemoryUsage: await mainTask.ConfigureAwait(false); - break; - default: - tasks.Add(mainTask); - break; - } - if (maxPages > 1) { - Bot.ArchiLogger.LogGenericInfo(Strings.CheckingOtherBadgePages); + if (maxPages > 1) { + Bot.ArchiLogger.LogGenericInfo(Strings.CheckingOtherBadgePages); - switch (Program.GlobalConfig.OptimizationMode) { - case GlobalConfig.EOptimizationMode.MinMemoryUsage: for (byte page = 2; page <= maxPages; page++) { await CheckPage(page).ConfigureAwait(false); } + } + + break; + default: + List tasks = new List(maxPages) { mainTask }; + + if (maxPages > 1) { + Bot.ArchiLogger.LogGenericInfo(Strings.CheckingOtherBadgePages); - break; - default: for (byte page = 2; page <= maxPages; page++) { // We need a copy of variable being passed when in for loops, as loop will proceed before our task is launched byte currentPage = page; tasks.Add(CheckPage(currentPage)); } + } - break; - } - } - - if (tasks.Count > 0) { - await Task.WhenAll(tasks).ConfigureAwait(false); + await Task.WhenAll(tasks).ConfigureAwait(false); + break; } if (GamesToFarm.Count == 0) { diff --git a/ArchiSteamFarm/Commands.cs b/ArchiSteamFarm/Commands.cs index d890b3d75..815c510b0 100644 --- a/ArchiSteamFarm/Commands.cs +++ b/ArchiSteamFarm/Commands.cs @@ -356,21 +356,7 @@ namespace ArchiSteamFarm { return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(Strings.BotNotFound, botNames)) : null; } - IEnumerable> tasks = bots.Select(bot => bot.Commands.Response2FA(steamID)); - ICollection results; - - switch (Program.GlobalConfig.OptimizationMode) { - case GlobalConfig.EOptimizationMode.MinMemoryUsage: - results = new List(bots.Count); - foreach (Task task in tasks) { - results.Add(await task.ConfigureAwait(false)); - } - - break; - default: - results = await Task.WhenAll(tasks).ConfigureAwait(false); - break; - } + IList results = await Utilities.InParallel(bots.Select(bot => bot.Commands.Response2FA(steamID))).ConfigureAwait(false); List responses = new List(results.Where(result => !string.IsNullOrEmpty(result))); return responses.Count > 0 ? string.Join(Environment.NewLine, responses) : null; @@ -409,21 +395,7 @@ namespace ArchiSteamFarm { return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(Strings.BotNotFound, botNames)) : null; } - IEnumerable> tasks = bots.Select(bot => bot.Commands.Response2FAConfirm(steamID, confirm)); - ICollection results; - - switch (Program.GlobalConfig.OptimizationMode) { - case GlobalConfig.EOptimizationMode.MinMemoryUsage: - results = new List(bots.Count); - foreach (Task task in tasks) { - results.Add(await task.ConfigureAwait(false)); - } - - break; - default: - results = await Task.WhenAll(tasks).ConfigureAwait(false); - break; - } + IList results = await Utilities.InParallel(bots.Select(bot => bot.Commands.Response2FAConfirm(steamID, confirm))).ConfigureAwait(false); List responses = new List(results.Where(result => !string.IsNullOrEmpty(result))); return responses.Count > 0 ? string.Join(Environment.NewLine, responses) : null; @@ -516,21 +488,7 @@ namespace ArchiSteamFarm { return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(Strings.BotNotFound, botNames)) : null; } - IEnumerable> tasks = bots.Select(bot => bot.Commands.ResponseAddLicense(steamID, targetGameIDs)); - ICollection results; - - switch (Program.GlobalConfig.OptimizationMode) { - case GlobalConfig.EOptimizationMode.MinMemoryUsage: - results = new List(bots.Count); - foreach (Task task in tasks) { - results.Add(await task.ConfigureAwait(false)); - } - - break; - default: - results = await Task.WhenAll(tasks).ConfigureAwait(false); - break; - } + IList results = await Utilities.InParallel(bots.Select(bot => bot.Commands.ResponseAddLicense(steamID, targetGameIDs))).ConfigureAwait(false); List responses = new List(results.Where(result => !string.IsNullOrEmpty(result))); return responses.Count > 0 ? string.Join(Environment.NewLine, responses) : null; @@ -573,21 +531,7 @@ namespace ArchiSteamFarm { return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(Strings.BotNotFound, botNames)) : null; } - IEnumerable> tasks = bots.Select(bot => bot.Commands.ResponseAdvancedLoot(steamID, appID, contextID)); - ICollection results; - - switch (Program.GlobalConfig.OptimizationMode) { - case GlobalConfig.EOptimizationMode.MinMemoryUsage: - results = new List(bots.Count); - foreach (Task task in tasks) { - results.Add(await task.ConfigureAwait(false)); - } - - break; - default: - results = await Task.WhenAll(tasks).ConfigureAwait(false); - break; - } + IList results = await Utilities.InParallel(bots.Select(bot => bot.Commands.ResponseAdvancedLoot(steamID, appID, contextID))).ConfigureAwait(false); List responses = new List(results.Where(result => !string.IsNullOrEmpty(result))); return responses.Count > 0 ? string.Join(Environment.NewLine, responses) : null; @@ -656,21 +600,7 @@ namespace ArchiSteamFarm { return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(Strings.BotNotFound, botNames)) : null; } - IEnumerable> tasks = bots.Select(bot => bot.Commands.ResponseAdvancedRedeem(steamID, options, keys)); - ICollection results; - - switch (Program.GlobalConfig.OptimizationMode) { - case GlobalConfig.EOptimizationMode.MinMemoryUsage: - results = new List(bots.Count); - foreach (Task task in tasks) { - results.Add(await task.ConfigureAwait(false)); - } - - break; - default: - results = await Task.WhenAll(tasks).ConfigureAwait(false); - break; - } + IList results = await Utilities.InParallel(bots.Select(bot => bot.Commands.ResponseAdvancedRedeem(steamID, options, keys))).ConfigureAwait(false); List responses = new List(results.Where(result => !string.IsNullOrEmpty(result))); return responses.Count > 0 ? string.Join(Environment.NewLine, responses) : null; @@ -701,21 +631,7 @@ namespace ArchiSteamFarm { return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(Strings.BotNotFound, botNames)) : null; } - IEnumerable> tasks = bots.Select(bot => Task.Run(() => bot.Commands.ResponseBlacklist(steamID))); - ICollection results; - - switch (Program.GlobalConfig.OptimizationMode) { - case GlobalConfig.EOptimizationMode.MinMemoryUsage: - results = new List(bots.Count); - foreach (Task task in tasks) { - results.Add(await task.ConfigureAwait(false)); - } - - break; - default: - results = await Task.WhenAll(tasks).ConfigureAwait(false); - break; - } + IList results = await Utilities.InParallel(bots.Select(bot => Task.Run(() => bot.Commands.ResponseBlacklist(steamID)))).ConfigureAwait(false); List responses = new List(results.Where(result => !string.IsNullOrEmpty(result))); return responses.Count > 0 ? string.Join(Environment.NewLine, responses) : null; @@ -762,21 +678,7 @@ namespace ArchiSteamFarm { return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(Strings.BotNotFound, botNames)) : null; } - IEnumerable> tasks = bots.Select(bot => bot.Commands.ResponseBlacklistAdd(steamID, targetSteamIDs)); - ICollection results; - - switch (Program.GlobalConfig.OptimizationMode) { - case GlobalConfig.EOptimizationMode.MinMemoryUsage: - results = new List(bots.Count); - foreach (Task task in tasks) { - results.Add(await task.ConfigureAwait(false)); - } - - break; - default: - results = await Task.WhenAll(tasks).ConfigureAwait(false); - break; - } + IList results = await Utilities.InParallel(bots.Select(bot => bot.Commands.ResponseBlacklistAdd(steamID, targetSteamIDs))).ConfigureAwait(false); List responses = new List(results.Where(result => !string.IsNullOrEmpty(result))); return responses.Count > 0 ? string.Join(Environment.NewLine, responses) : null; @@ -823,21 +725,7 @@ namespace ArchiSteamFarm { return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(Strings.BotNotFound, botNames)) : null; } - IEnumerable> tasks = bots.Select(bot => bot.Commands.ResponseBlacklistRemove(steamID, targetSteamIDs)); - ICollection results; - - switch (Program.GlobalConfig.OptimizationMode) { - case GlobalConfig.EOptimizationMode.MinMemoryUsage: - results = new List(bots.Count); - foreach (Task task in tasks) { - results.Add(await task.ConfigureAwait(false)); - } - - break; - default: - results = await Task.WhenAll(tasks).ConfigureAwait(false); - break; - } + IList results = await Utilities.InParallel(bots.Select(bot => bot.Commands.ResponseBlacklistRemove(steamID, targetSteamIDs))).ConfigureAwait(false); List responses = new List(results.Where(result => !string.IsNullOrEmpty(result))); return responses.Count > 0 ? string.Join(Environment.NewLine, responses) : null; @@ -890,21 +778,7 @@ namespace ArchiSteamFarm { return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(Strings.BotNotFound, botNames)) : null; } - IEnumerable> tasks = bots.Select(bot => bot.Commands.ResponseFarm(steamID)); - ICollection results; - - switch (Program.GlobalConfig.OptimizationMode) { - case GlobalConfig.EOptimizationMode.MinMemoryUsage: - results = new List(bots.Count); - foreach (Task task in tasks) { - results.Add(await task.ConfigureAwait(false)); - } - - break; - default: - results = await Task.WhenAll(tasks).ConfigureAwait(false); - break; - } + IList results = await Utilities.InParallel(bots.Select(bot => bot.Commands.ResponseFarm(steamID))).ConfigureAwait(false); List responses = new List(results.Where(result => !string.IsNullOrEmpty(result))); return responses.Count > 0 ? string.Join(Environment.NewLine, responses) : null; @@ -944,21 +818,7 @@ namespace ArchiSteamFarm { return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(Strings.BotNotFound, botNames)) : null; } - IEnumerable> tasks = bots.Select(bot => Task.Run(() => bot.Commands.ResponseIdleBlacklist(steamID))); - ICollection results; - - switch (Program.GlobalConfig.OptimizationMode) { - case GlobalConfig.EOptimizationMode.MinMemoryUsage: - results = new List(bots.Count); - foreach (Task task in tasks) { - results.Add(await task.ConfigureAwait(false)); - } - - break; - default: - results = await Task.WhenAll(tasks).ConfigureAwait(false); - break; - } + IList results = await Utilities.InParallel(bots.Select(bot => Task.Run(() => bot.Commands.ResponseIdleBlacklist(steamID)))).ConfigureAwait(false); List responses = new List(results.Where(result => !string.IsNullOrEmpty(result))); return responses.Count > 0 ? string.Join(Environment.NewLine, responses) : null; @@ -1005,21 +865,7 @@ namespace ArchiSteamFarm { return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(Strings.BotNotFound, botNames)) : null; } - IEnumerable> tasks = bots.Select(bot => bot.Commands.ResponseIdleBlacklistAdd(steamID, targetAppIDs)); - ICollection results; - - switch (Program.GlobalConfig.OptimizationMode) { - case GlobalConfig.EOptimizationMode.MinMemoryUsage: - results = new List(bots.Count); - foreach (Task task in tasks) { - results.Add(await task.ConfigureAwait(false)); - } - - break; - default: - results = await Task.WhenAll(tasks).ConfigureAwait(false); - break; - } + IList results = await Utilities.InParallel(bots.Select(bot => bot.Commands.ResponseIdleBlacklistAdd(steamID, targetAppIDs))).ConfigureAwait(false); List responses = new List(results.Where(result => !string.IsNullOrEmpty(result))); return responses.Count > 0 ? string.Join(Environment.NewLine, responses) : null; @@ -1066,21 +912,7 @@ namespace ArchiSteamFarm { return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(Strings.BotNotFound, botNames)) : null; } - IEnumerable> tasks = bots.Select(bot => bot.Commands.ResponseIdleBlacklistRemove(steamID, targetAppIDs)); - ICollection results; - - switch (Program.GlobalConfig.OptimizationMode) { - case GlobalConfig.EOptimizationMode.MinMemoryUsage: - results = new List(bots.Count); - foreach (Task task in tasks) { - results.Add(await task.ConfigureAwait(false)); - } - - break; - default: - results = await Task.WhenAll(tasks).ConfigureAwait(false); - break; - } + IList results = await Utilities.InParallel(bots.Select(bot => bot.Commands.ResponseIdleBlacklistRemove(steamID, targetAppIDs))).ConfigureAwait(false); List responses = new List(results.Where(result => !string.IsNullOrEmpty(result))); return responses.Count > 0 ? string.Join(Environment.NewLine, responses) : null; @@ -1111,21 +943,7 @@ namespace ArchiSteamFarm { return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(Strings.BotNotFound, botNames)) : null; } - IEnumerable> tasks = bots.Select(bot => Task.Run(() => bot.Commands.ResponseIdleQueue(steamID))); - ICollection results; - - switch (Program.GlobalConfig.OptimizationMode) { - case GlobalConfig.EOptimizationMode.MinMemoryUsage: - results = new List(bots.Count); - foreach (Task task in tasks) { - results.Add(await task.ConfigureAwait(false)); - } - - break; - default: - results = await Task.WhenAll(tasks).ConfigureAwait(false); - break; - } + IList results = await Utilities.InParallel(bots.Select(bot => Task.Run(() => bot.Commands.ResponseIdleQueue(steamID)))).ConfigureAwait(false); List responses = new List(results.Where(result => !string.IsNullOrEmpty(result))); return responses.Count > 0 ? string.Join(Environment.NewLine, responses) : null; @@ -1172,21 +990,7 @@ namespace ArchiSteamFarm { return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(Strings.BotNotFound, botNames)) : null; } - IEnumerable> tasks = bots.Select(bot => bot.Commands.ResponseIdleQueueAdd(steamID, targetAppIDs)); - ICollection results; - - switch (Program.GlobalConfig.OptimizationMode) { - case GlobalConfig.EOptimizationMode.MinMemoryUsage: - results = new List(bots.Count); - foreach (Task task in tasks) { - results.Add(await task.ConfigureAwait(false)); - } - - break; - default: - results = await Task.WhenAll(tasks).ConfigureAwait(false); - break; - } + IList results = await Utilities.InParallel(bots.Select(bot => bot.Commands.ResponseIdleQueueAdd(steamID, targetAppIDs))).ConfigureAwait(false); List responses = new List(results.Where(result => !string.IsNullOrEmpty(result))); return responses.Count > 0 ? string.Join(Environment.NewLine, responses) : null; @@ -1233,21 +1037,7 @@ namespace ArchiSteamFarm { return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(Strings.BotNotFound, botNames)) : null; } - IEnumerable> tasks = bots.Select(bot => bot.Commands.ResponseIdleQueueRemove(steamID, targetAppIDs)); - ICollection results; - - switch (Program.GlobalConfig.OptimizationMode) { - case GlobalConfig.EOptimizationMode.MinMemoryUsage: - results = new List(bots.Count); - foreach (Task task in tasks) { - results.Add(await task.ConfigureAwait(false)); - } - - break; - default: - results = await Task.WhenAll(tasks).ConfigureAwait(false); - break; - } + IList results = await Utilities.InParallel(bots.Select(bot => bot.Commands.ResponseIdleQueueRemove(steamID, targetAppIDs))).ConfigureAwait(false); List responses = new List(results.Where(result => !string.IsNullOrEmpty(result))); return responses.Count > 0 ? string.Join(Environment.NewLine, responses) : null; @@ -1286,21 +1076,7 @@ namespace ArchiSteamFarm { return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(Strings.BotNotFound, botNames)) : null; } - IEnumerable> tasks = bots.Select(bot => Task.Run(() => bot.Commands.ResponseInput(steamID, propertyName, inputValue))); - ICollection results; - - switch (Program.GlobalConfig.OptimizationMode) { - case GlobalConfig.EOptimizationMode.MinMemoryUsage: - results = new List(bots.Count); - foreach (Task task in tasks) { - results.Add(await task.ConfigureAwait(false)); - } - - break; - default: - results = await Task.WhenAll(tasks).ConfigureAwait(false); - break; - } + IList results = await Utilities.InParallel(bots.Select(bot => Task.Run(() => bot.Commands.ResponseInput(steamID, propertyName, inputValue)))).ConfigureAwait(false); List responses = new List(results.Where(result => !string.IsNullOrEmpty(result))); return responses.Count > 0 ? string.Join(Environment.NewLine, responses) : null; @@ -1335,21 +1111,7 @@ namespace ArchiSteamFarm { return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(Strings.BotNotFound, botNames)) : null; } - IEnumerable> tasks = bots.Select(bot => bot.Commands.ResponseLoot(steamID)); - ICollection results; - - switch (Program.GlobalConfig.OptimizationMode) { - case GlobalConfig.EOptimizationMode.MinMemoryUsage: - results = new List(bots.Count); - foreach (Task task in tasks) { - results.Add(await task.ConfigureAwait(false)); - } - - break; - default: - results = await Task.WhenAll(tasks).ConfigureAwait(false); - break; - } + IList results = await Utilities.InParallel(bots.Select(bot => bot.Commands.ResponseLoot(steamID))).ConfigureAwait(false); List responses = new List(results.Where(result => !string.IsNullOrEmpty(result))); return responses.Count > 0 ? string.Join(Environment.NewLine, responses) : null; @@ -1400,21 +1162,7 @@ namespace ArchiSteamFarm { return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(Strings.BotNotFound, botNames)) : null; } - IEnumerable> tasks = bots.Select(bot => bot.Commands.ResponseLootByRealAppIDs(steamID, realAppIDsText)); - ICollection results; - - switch (Program.GlobalConfig.OptimizationMode) { - case GlobalConfig.EOptimizationMode.MinMemoryUsage: - results = new List(bots.Count); - foreach (Task task in tasks) { - results.Add(await task.ConfigureAwait(false)); - } - - break; - default: - results = await Task.WhenAll(tasks).ConfigureAwait(false); - break; - } + IList results = await Utilities.InParallel(bots.Select(bot => bot.Commands.ResponseLootByRealAppIDs(steamID, realAppIDsText))).ConfigureAwait(false); List responses = new List(results.Where(result => !string.IsNullOrEmpty(result))); return responses.Count > 0 ? string.Join(Environment.NewLine, responses) : null; @@ -1445,21 +1193,7 @@ namespace ArchiSteamFarm { return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(Strings.BotNotFound, botNames)) : null; } - IEnumerable> tasks = bots.Select(bot => Task.Run(() => bot.Commands.ResponseLootSwitch(steamID))); - ICollection results; - - switch (Program.GlobalConfig.OptimizationMode) { - case GlobalConfig.EOptimizationMode.MinMemoryUsage: - results = new List(bots.Count); - foreach (Task task in tasks) { - results.Add(await task.ConfigureAwait(false)); - } - - break; - default: - results = await Task.WhenAll(tasks).ConfigureAwait(false); - break; - } + IList results = await Utilities.InParallel(bots.Select(bot => Task.Run(() => bot.Commands.ResponseLootSwitch(steamID)))).ConfigureAwait(false); List responses = new List(results.Where(result => !string.IsNullOrEmpty(result))); return responses.Count > 0 ? string.Join(Environment.NewLine, responses) : null; @@ -1494,21 +1228,7 @@ namespace ArchiSteamFarm { return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(Strings.BotNotFound, botNames)) : null; } - IEnumerable> tasks = bots.Select(bot => Task.Run(() => bot.Commands.ResponseNickname(steamID, nickname))); - ICollection results; - - switch (Program.GlobalConfig.OptimizationMode) { - case GlobalConfig.EOptimizationMode.MinMemoryUsage: - results = new List(bots.Count); - foreach (Task task in tasks) { - results.Add(await task.ConfigureAwait(false)); - } - - break; - default: - results = await Task.WhenAll(tasks).ConfigureAwait(false); - break; - } + IList results = await Utilities.InParallel(bots.Select(bot => Task.Run(() => bot.Commands.ResponseNickname(steamID, nickname)))).ConfigureAwait(false); List responses = new List(results.Where(result => !string.IsNullOrEmpty(result))); return responses.Count > 0 ? string.Join(Environment.NewLine, responses) : null; @@ -1609,21 +1329,7 @@ namespace ArchiSteamFarm { return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(Strings.BotNotFound, botNames)) : null; } - IEnumerable OwnedGameIDs)>> tasks = bots.Select(bot => bot.Commands.ResponseOwns(steamID, query)); - ICollection<(string Response, HashSet OwnedGameIDs)> results; - - switch (Program.GlobalConfig.OptimizationMode) { - case GlobalConfig.EOptimizationMode.MinMemoryUsage: - results = new List<(string Response, HashSet OwnedGameIDs)>(bots.Count); - foreach (Task<(string Response, HashSet OwnedGameIDs)> task in tasks) { - results.Add(await task.ConfigureAwait(false)); - } - - break; - default: - results = await Task.WhenAll(tasks).ConfigureAwait(false); - break; - } + IList<(string Response, HashSet OwnedGameIDs)> results = await Utilities.InParallel(bots.Select(bot => bot.Commands.ResponseOwns(steamID, query))).ConfigureAwait(false); List<(string Response, HashSet OwnedGameIDs)> validResults = new List<(string Response, HashSet OwnedGameIDs)>(results.Where(result => !string.IsNullOrEmpty(result.Response))); if (validResults.Count == 0) { @@ -1668,21 +1374,7 @@ namespace ArchiSteamFarm { return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(Strings.BotNotFound, botNames)) : null; } - IEnumerable> tasks = bots.Select(bot => Task.Run(() => bot.Commands.ResponsePassword(steamID))); - ICollection results; - - switch (Program.GlobalConfig.OptimizationMode) { - case GlobalConfig.EOptimizationMode.MinMemoryUsage: - results = new List(bots.Count); - foreach (Task task in tasks) { - results.Add(await task.ConfigureAwait(false)); - } - - break; - default: - results = await Task.WhenAll(tasks).ConfigureAwait(false); - break; - } + IList results = await Utilities.InParallel(bots.Select(bot => Task.Run(() => bot.Commands.ResponsePassword(steamID)))).ConfigureAwait(false); List responses = new List(results.Where(result => !string.IsNullOrEmpty(result))); return responses.Count > 0 ? string.Join(Environment.NewLine, responses) : null; @@ -1761,21 +1453,7 @@ namespace ArchiSteamFarm { return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(Strings.BotNotFound, botNames)) : null; } - IEnumerable> tasks = bots.Select(bot => bot.Commands.ResponsePause(steamID, sticky, timeout)); - ICollection results; - - switch (Program.GlobalConfig.OptimizationMode) { - case GlobalConfig.EOptimizationMode.MinMemoryUsage: - results = new List(bots.Count); - foreach (Task task in tasks) { - results.Add(await task.ConfigureAwait(false)); - } - - break; - default: - results = await Task.WhenAll(tasks).ConfigureAwait(false); - break; - } + IList results = await Utilities.InParallel(bots.Select(bot => bot.Commands.ResponsePause(steamID, sticky, timeout))).ConfigureAwait(false); List responses = new List(results.Where(result => !string.IsNullOrEmpty(result))); return responses.Count > 0 ? string.Join(Environment.NewLine, responses) : null; @@ -1853,21 +1531,7 @@ namespace ArchiSteamFarm { return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(Strings.BotNotFound, botNames)) : null; } - IEnumerable> tasks = bots.Select(bot => bot.Commands.ResponsePlay(steamID, targetGameIDs)); - ICollection results; - - switch (Program.GlobalConfig.OptimizationMode) { - case GlobalConfig.EOptimizationMode.MinMemoryUsage: - results = new List(bots.Count); - foreach (Task task in tasks) { - results.Add(await task.ConfigureAwait(false)); - } - - break; - default: - results = await Task.WhenAll(tasks).ConfigureAwait(false); - break; - } + IList results = await Utilities.InParallel(bots.Select(bot => bot.Commands.ResponsePlay(steamID, targetGameIDs))).ConfigureAwait(false); List responses = new List(results.Where(result => !string.IsNullOrEmpty(result))); return responses.Count > 0 ? string.Join(Environment.NewLine, responses) : null; @@ -1987,21 +1651,7 @@ namespace ArchiSteamFarm { return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(Strings.BotNotFound, botNames)) : null; } - IEnumerable> tasks = bots.Select(bot => bot.Commands.ResponsePrivacy(steamID, privacySettingsText)); - ICollection results; - - switch (Program.GlobalConfig.OptimizationMode) { - case GlobalConfig.EOptimizationMode.MinMemoryUsage: - results = new List(bots.Count); - foreach (Task task in tasks) { - results.Add(await task.ConfigureAwait(false)); - } - - break; - default: - results = await Task.WhenAll(tasks).ConfigureAwait(false); - break; - } + IList results = await Utilities.InParallel(bots.Select(bot => bot.Commands.ResponsePrivacy(steamID, privacySettingsText))).ConfigureAwait(false); List responses = new List(results.Where(result => !string.IsNullOrEmpty(result))); return responses.Count > 0 ? string.Join(Environment.NewLine, responses) : null; @@ -2213,21 +1863,7 @@ namespace ArchiSteamFarm { return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(Strings.BotNotFound, botNames)) : null; } - IEnumerable> tasks = bots.Select(bot => bot.Commands.ResponseRedeem(steamID, keys, redeemFlags)); - ICollection results; - - switch (Program.GlobalConfig.OptimizationMode) { - case GlobalConfig.EOptimizationMode.MinMemoryUsage: - results = new List(bots.Count); - foreach (Task task in tasks) { - results.Add(await task.ConfigureAwait(false)); - } - - break; - default: - results = await Task.WhenAll(tasks).ConfigureAwait(false); - break; - } + IList results = await Utilities.InParallel(bots.Select(bot => bot.Commands.ResponseRedeem(steamID, keys, redeemFlags))).ConfigureAwait(false); List responses = new List(results.Where(result => !string.IsNullOrEmpty(result))); return responses.Count > 0 ? string.Join(Environment.NewLine, responses) : null; @@ -2286,21 +1922,7 @@ namespace ArchiSteamFarm { return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(Strings.BotNotFound, botNames)) : null; } - IEnumerable> tasks = bots.Select(bot => Task.Run(() => bot.Commands.ResponseResume(steamID))); - ICollection results; - - switch (Program.GlobalConfig.OptimizationMode) { - case GlobalConfig.EOptimizationMode.MinMemoryUsage: - results = new List(bots.Count); - foreach (Task task in tasks) { - results.Add(await task.ConfigureAwait(false)); - } - - break; - default: - results = await Task.WhenAll(tasks).ConfigureAwait(false); - break; - } + IList results = await Utilities.InParallel(bots.Select(bot => Task.Run(() => bot.Commands.ResponseResume(steamID)))).ConfigureAwait(false); List responses = new List(results.Where(result => !string.IsNullOrEmpty(result))); return responses.Count > 0 ? string.Join(Environment.NewLine, responses) : null; @@ -2316,13 +1938,8 @@ namespace ArchiSteamFarm { return null; } - if (Bot.KeepRunning) { - return FormatBotResponse(Strings.BotAlreadyRunning); - } - - Bot.SkipFirstShutdown = true; - Utilities.InBackground(Bot.Start); - return FormatBotResponse(Strings.Done); + (bool success, string output) = Bot.Actions.Start(); + return FormatStaticResponse(success ? output : string.Format(Strings.WarningFailedWithError, output)); } private static async Task ResponseStart(ulong steamID, string botNames) { @@ -2336,21 +1953,7 @@ namespace ArchiSteamFarm { return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(Strings.BotNotFound, botNames)) : null; } - IEnumerable> tasks = bots.Select(bot => Task.Run(() => bot.Commands.ResponseStart(steamID))); - ICollection results; - - switch (Program.GlobalConfig.OptimizationMode) { - case GlobalConfig.EOptimizationMode.MinMemoryUsage: - results = new List(bots.Count); - foreach (Task task in tasks) { - results.Add(await task.ConfigureAwait(false)); - } - - break; - default: - results = await Task.WhenAll(tasks).ConfigureAwait(false); - break; - } + IList results = await Utilities.InParallel(bots.Select(bot => Task.Run(() => bot.Commands.ResponseStart(steamID)))).ConfigureAwait(false); List responses = new List(results.Where(result => !string.IsNullOrEmpty(result))); return responses.Count > 0 ? string.Join(Environment.NewLine, responses) : null; @@ -2423,21 +2026,7 @@ namespace ArchiSteamFarm { return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(Strings.BotNotFound, botNames)) : null; } - IEnumerable> tasks = bots.Select(bot => Task.Run(() => bot.Commands.ResponseStatus(steamID))); - ICollection<(string Response, Bot Bot)> results; - - switch (Program.GlobalConfig.OptimizationMode) { - case GlobalConfig.EOptimizationMode.MinMemoryUsage: - results = new List<(string Response, Bot Bot)>(bots.Count); - foreach (Task<(string Response, Bot Bot)> task in tasks) { - results.Add(await task.ConfigureAwait(false)); - } - - break; - default: - results = await Task.WhenAll(tasks).ConfigureAwait(false); - break; - } + IList<(string Response, Bot Bot)> results = await Utilities.InParallel(bots.Select(bot => Task.Run(() => bot.Commands.ResponseStatus(steamID)))).ConfigureAwait(false); List<(string Response, Bot Bot)> validResults = new List<(string Response, Bot Bot)>(results.Where(result => !string.IsNullOrEmpty(result.Response))); if (validResults.Count == 0) { @@ -2460,12 +2049,8 @@ namespace ArchiSteamFarm { return null; } - if (!Bot.KeepRunning) { - return FormatBotResponse(Strings.BotAlreadyStopped); - } - - Bot.Stop(); - return FormatBotResponse(Strings.Done); + (bool success, string output) = Bot.Actions.Stop(); + return FormatStaticResponse(success ? output : string.Format(Strings.WarningFailedWithError, output)); } private static async Task ResponseStop(ulong steamID, string botNames) { @@ -2479,21 +2064,7 @@ namespace ArchiSteamFarm { return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(Strings.BotNotFound, botNames)) : null; } - IEnumerable> tasks = bots.Select(bot => Task.Run(() => bot.Commands.ResponseStop(steamID))); - ICollection results; - - switch (Program.GlobalConfig.OptimizationMode) { - case GlobalConfig.EOptimizationMode.MinMemoryUsage: - results = new List(bots.Count); - foreach (Task task in tasks) { - results.Add(await task.ConfigureAwait(false)); - } - - break; - default: - results = await Task.WhenAll(tasks).ConfigureAwait(false); - break; - } + IList results = await Utilities.InParallel(bots.Select(bot => Task.Run(() => bot.Commands.ResponseStop(steamID)))).ConfigureAwait(false); List responses = new List(results.Where(result => !string.IsNullOrEmpty(result))); return responses.Count > 0 ? string.Join(Environment.NewLine, responses) : null; @@ -2590,21 +2161,7 @@ namespace ArchiSteamFarm { return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(Strings.BotNotFound, botNames)) : null; } - IEnumerable> tasks = bots.Select(bot => bot.Commands.ResponseTransfer(steamID, mode, botNameTo)); - ICollection results; - - switch (Program.GlobalConfig.OptimizationMode) { - case GlobalConfig.EOptimizationMode.MinMemoryUsage: - results = new List(bots.Count); - foreach (Task task in tasks) { - results.Add(await task.ConfigureAwait(false)); - } - - break; - default: - results = await Task.WhenAll(tasks).ConfigureAwait(false); - break; - } + IList results = await Utilities.InParallel(bots.Select(bot => bot.Commands.ResponseTransfer(steamID, mode, botNameTo))).ConfigureAwait(false); List responses = new List(results.Where(result => !string.IsNullOrEmpty(result))); return responses.Count > 0 ? string.Join(Environment.NewLine, responses) : null; @@ -2658,21 +2215,7 @@ namespace ArchiSteamFarm { return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(Strings.BotNotFound, botNames)) : null; } - IEnumerable> tasks = bots.Select(bot => bot.Commands.ResponseUnpackBoosters(steamID)); - ICollection results; - - switch (Program.GlobalConfig.OptimizationMode) { - case GlobalConfig.EOptimizationMode.MinMemoryUsage: - results = new List(bots.Count); - foreach (Task task in tasks) { - results.Add(await task.ConfigureAwait(false)); - } - - break; - default: - results = await Task.WhenAll(tasks).ConfigureAwait(false); - break; - } + IList results = await Utilities.InParallel(bots.Select(bot => bot.Commands.ResponseUnpackBoosters(steamID))).ConfigureAwait(false); List responses = new List(results.Where(result => !string.IsNullOrEmpty(result))); return responses.Count > 0 ? string.Join(Environment.NewLine, responses) : null; diff --git a/ArchiSteamFarm/IPC/Controllers/Api/ASFController.cs b/ArchiSteamFarm/IPC/Controllers/Api/ASFController.cs index dab030cc2..1f1f0b5ff 100644 --- a/ArchiSteamFarm/IPC/Controllers/Api/ASFController.cs +++ b/ArchiSteamFarm/IPC/Controllers/Api/ASFController.cs @@ -42,9 +42,8 @@ namespace ArchiSteamFarm.IPC.Controllers.Api { processStartTime = process.StartTime; } - ASFResponse response = new ASFResponse(SharedInfo.BuildInfo.Variant, Program.GlobalConfig, memoryUsage, processStartTime, SharedInfo.Version); - - return Ok(new GenericResponse(response)); + ASFResponse result = new ASFResponse(SharedInfo.BuildInfo.Variant, Program.GlobalConfig, memoryUsage, processStartTime, SharedInfo.Version); + return Ok(new GenericResponse(result)); } [HttpPost] @@ -69,11 +68,8 @@ namespace ArchiSteamFarm.IPC.Controllers.Api { 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)); + bool result = await GlobalConfig.Write(filePath, request.GlobalConfig).ConfigureAwait(false); + return Ok(new GenericResponse(result)); } [HttpPost("Exit")] diff --git a/ArchiSteamFarm/IPC/Controllers/Api/BotController.cs b/ArchiSteamFarm/IPC/Controllers/Api/BotController.cs index 799b23310..6f739fc0c 100644 --- a/ArchiSteamFarm/IPC/Controllers/Api/BotController.cs +++ b/ArchiSteamFarm/IPC/Controllers/Api/BotController.cs @@ -19,6 +19,7 @@ // 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; @@ -44,27 +45,8 @@ namespace ArchiSteamFarm.IPC.Controllers.Api { return BadRequest(new GenericResponse(false, string.Format(Strings.BotNotFound, botNames))); } - IEnumerable> tasks = bots.Select(bot => bot.DeleteAllRelatedFiles()); - ICollection results; - - switch (Program.GlobalConfig.OptimizationMode) { - case GlobalConfig.EOptimizationMode.MinMemoryUsage: - results = new List(bots.Count); - foreach (Task 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)); + IList results = await Utilities.InParallel(bots.Select(bot => bot.DeleteAllRelatedFiles())).ConfigureAwait(false); + return Ok(new GenericResponse(results.All(result => result))); } [HttpGet("{botNames:required}")] @@ -112,11 +94,40 @@ namespace ArchiSteamFarm.IPC.Controllers.Api { 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)); + bool result = await BotConfig.Write(filePath, request.BotConfig).ConfigureAwait(false); + return Ok(new GenericResponse(result)); + } + + [HttpPost("{botNames:required}/Stop")] + public async Task> PostStop(string botNames) { + if (string.IsNullOrEmpty(botNames)) { + ASF.ArchiLogger.LogNullError(nameof(botNames)); + return BadRequest(new GenericResponse(false, string.Format(Strings.ErrorIsEmpty, nameof(botNames)))); } - return Ok(new GenericResponse(true)); + HashSet bots = Bot.GetBots(botNames); + if ((bots == null) || (bots.Count == 0)) { + return BadRequest(new GenericResponse(false, string.Format(Strings.BotNotFound, botNames))); + } + + IList<(bool Success, string Output)> results = await Utilities.InParallel(bots.Select(bot => Task.Run(bot.Actions.Stop))).ConfigureAwait(false); + return Ok(new GenericResponse(results.All(result => result.Success), string.Join(Environment.NewLine, results.Select(result => result.Output)))); + } + + [HttpPost("{botNames:required}/Start")] + public async Task> PostStart(string botNames) { + if (string.IsNullOrEmpty(botNames)) { + ASF.ArchiLogger.LogNullError(nameof(botNames)); + return BadRequest(new GenericResponse(false, string.Format(Strings.ErrorIsEmpty, nameof(botNames)))); + } + + HashSet bots = Bot.GetBots(botNames); + if ((bots == null) || (bots.Count == 0)) { + return BadRequest(new GenericResponse(false, string.Format(Strings.BotNotFound, botNames))); + } + + IList<(bool Success, string Output)> results = await Utilities.InParallel(bots.Select(bot => Task.Run(bot.Actions.Start))).ConfigureAwait(false); + return Ok(new GenericResponse(results.All(result => result.Success), string.Join(Environment.NewLine, results.Select(result => result.Output)))); } } } diff --git a/ArchiSteamFarm/IPC/Controllers/Api/CommandController.cs b/ArchiSteamFarm/IPC/Controllers/Api/CommandController.cs index 7b0245fd7..5fc5bfcd9 100644 --- a/ArchiSteamFarm/IPC/Controllers/Api/CommandController.cs +++ b/ArchiSteamFarm/IPC/Controllers/Api/CommandController.cs @@ -50,8 +50,8 @@ namespace ArchiSteamFarm.IPC.Controllers.Api { command = Program.GlobalConfig.CommandPrefix + command; } - string content = await targetBot.Commands.Response(Program.GlobalConfig.SteamOwnerID, command).ConfigureAwait(false); - return Ok(new GenericResponse(content)); + string response = await targetBot.Commands.Response(Program.GlobalConfig.SteamOwnerID, command).ConfigureAwait(false); + return Ok(new GenericResponse(response)); } } } diff --git a/ArchiSteamFarm/IPC/Controllers/Api/GamesToRedeemInBackgroundController.cs b/ArchiSteamFarm/IPC/Controllers/Api/GamesToRedeemInBackgroundController.cs index 8a4430e55..f08ef4211 100644 --- a/ArchiSteamFarm/IPC/Controllers/Api/GamesToRedeemInBackgroundController.cs +++ b/ArchiSteamFarm/IPC/Controllers/Api/GamesToRedeemInBackgroundController.cs @@ -44,27 +44,8 @@ namespace ArchiSteamFarm.IPC.Controllers.Api { return BadRequest(new GenericResponse(false, string.Format(Strings.BotNotFound, botNames))); } - IEnumerable> tasks = bots.Select(bot => Task.Run(bot.DeleteRedeemedKeysFiles)); - ICollection results; - - switch (Program.GlobalConfig.OptimizationMode) { - case GlobalConfig.EOptimizationMode.MinMemoryUsage: - results = new List(bots.Count); - foreach (Task 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)); + IList results = await Utilities.InParallel(bots.Select(bot => Task.Run(bot.DeleteRedeemedKeysFiles))).ConfigureAwait(false); + return Ok(results.All(result => result) ? new GenericResponse(true) : new GenericResponse(false, Strings.WarningFailed)); } [HttpGet("{botNames:required}")] @@ -79,26 +60,13 @@ namespace ArchiSteamFarm.IPC.Controllers.Api { return BadRequest(new GenericResponse>(false, string.Format(Strings.BotNotFound, botNames))); } - IEnumerable<(string BotName, Task<(Dictionary UnusedKeys, Dictionary UsedKeys)> Task)> tasks = bots.Select(bot => (bot.BotName, bot.GetUsedAndUnusedKeys())); - ICollection<(string BotName, (Dictionary UnusedKeys, Dictionary UsedKeys))> results; - - switch (Program.GlobalConfig.OptimizationMode) { - case GlobalConfig.EOptimizationMode.MinMemoryUsage: - results = new List<(string BotName, (Dictionary UnusedKeys, Dictionary UsedKeys))>(bots.Count); - foreach ((string botName, Task<(Dictionary UnusedKeys, Dictionary 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; - } + IList<(Dictionary UnusedKeys, Dictionary UsedKeys)> results = await Utilities.InParallel(bots.Select(bot => bot.GetUsedAndUnusedKeys())).ConfigureAwait(false); Dictionary result = new Dictionary(); - foreach ((string botName, (Dictionary UnusedKeys, Dictionary UsedKeys) taskResult) in results) { - result[botName] = new GamesToRedeemInBackgroundResponse(taskResult.UnusedKeys, taskResult.UsedKeys); + foreach (Bot bot in bots) { + (Dictionary unusedKeys, Dictionary usedKeys) = results[result.Count]; + result[bot.BotName] = new GamesToRedeemInBackgroundResponse(unusedKeys, usedKeys); } return Ok(new GenericResponse>(result)); @@ -119,8 +87,8 @@ namespace ArchiSteamFarm.IPC.Controllers.Api { return BadRequest(new GenericResponse(false, string.Format(Strings.BotNotFound, botName))); } - await bot.ValidateAndAddGamesToRedeemInBackground(request.GamesToRedeemInBackground).ConfigureAwait(false); - return Ok(new GenericResponse(request.GamesToRedeemInBackground)); + bool result = await bot.ValidateAndAddGamesToRedeemInBackground(request.GamesToRedeemInBackground).ConfigureAwait(false); + return Ok(new GenericResponse(result, request.GamesToRedeemInBackground)); } } } diff --git a/ArchiSteamFarm/IPC/Controllers/Api/StructureController.cs b/ArchiSteamFarm/IPC/Controllers/Api/StructureController.cs index 3e4e4eb30..874cc6b51 100644 --- a/ArchiSteamFarm/IPC/Controllers/Api/StructureController.cs +++ b/ArchiSteamFarm/IPC/Controllers/Api/StructureController.cs @@ -35,7 +35,7 @@ namespace ArchiSteamFarm.IPC.Controllers.Api { return BadRequest(new GenericResponse(false, string.Format(Strings.ErrorIsEmpty, nameof(structure)))); } - Type targetType = Utilities.ParseType(structure); + Type targetType = WebUtilities.ParseType(structure); if (targetType == null) { return BadRequest(new GenericResponse(false, string.Format(Strings.ErrorIsInvalid, structure))); diff --git a/ArchiSteamFarm/IPC/Controllers/Api/TypeController.cs b/ArchiSteamFarm/IPC/Controllers/Api/TypeController.cs index 2e0edd7b5..47ae8f8f4 100644 --- a/ArchiSteamFarm/IPC/Controllers/Api/TypeController.cs +++ b/ArchiSteamFarm/IPC/Controllers/Api/TypeController.cs @@ -39,7 +39,7 @@ namespace ArchiSteamFarm.IPC.Controllers.Api { return BadRequest(new GenericResponse(false, string.Format(Strings.ErrorIsEmpty, nameof(type)))); } - Type targetType = Utilities.ParseType(type); + Type targetType = WebUtilities.ParseType(type); if (targetType == null) { return BadRequest(new GenericResponse(false, string.Format(Strings.ErrorIsInvalid, type))); @@ -77,8 +77,8 @@ namespace ArchiSteamFarm.IPC.Controllers.Api { } TypeResponse.TypeProperties properties = new TypeResponse.TypeProperties(baseType, customAttributes.Count > 0 ? customAttributes : null, underlyingType); - TypeResponse response = new TypeResponse(body, properties); + TypeResponse response = new TypeResponse(body, properties); return Ok(new GenericResponse(response)); } } diff --git a/ArchiSteamFarm/IPC/Responses/GenericResponse.cs b/ArchiSteamFarm/IPC/Responses/GenericResponse.cs index 4010383bd..edf7516f5 100644 --- a/ArchiSteamFarm/IPC/Responses/GenericResponse.cs +++ b/ArchiSteamFarm/IPC/Responses/GenericResponse.cs @@ -19,6 +19,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +using ArchiSteamFarm.Localization; using Newtonsoft.Json; namespace ArchiSteamFarm.IPC.Responses { @@ -38,17 +39,15 @@ namespace ArchiSteamFarm.IPC.Responses { [JsonProperty] private readonly bool Success; - internal GenericResponse(bool success) { + internal GenericResponse(bool success, string message = null) { Success = success; - if (success) { - Message = "OK"; + if (!string.IsNullOrEmpty(message)) { + Message = message; + return; } - } - internal GenericResponse(bool success, string message) { - Success = success; - Message = message; + Message = success ? "OK" : Strings.WarningFailed; } } } diff --git a/ArchiSteamFarm/IPC/Utilities.cs b/ArchiSteamFarm/IPC/WebUtilities.cs similarity index 98% rename from ArchiSteamFarm/IPC/Utilities.cs rename to ArchiSteamFarm/IPC/WebUtilities.cs index 802580925..48425bafc 100644 --- a/ArchiSteamFarm/IPC/Utilities.cs +++ b/ArchiSteamFarm/IPC/WebUtilities.cs @@ -26,7 +26,7 @@ using System.Threading.Tasks; using Microsoft.AspNetCore.Http; namespace ArchiSteamFarm.IPC { - internal static class Utilities { + internal static class WebUtilities { internal static async Task Generate(this HttpResponse httpResponse, HttpStatusCode statusCode) { if (httpResponse == null) { ASF.ArchiLogger.LogNullError(nameof(httpResponse)); diff --git a/ArchiSteamFarm/Program.cs b/ArchiSteamFarm/Program.cs index 908802b25..b34c6eef9 100644 --- a/ArchiSteamFarm/Program.cs +++ b/ArchiSteamFarm/Program.cs @@ -360,19 +360,8 @@ namespace ArchiSteamFarm { await ArchiKestrel.Stop().ConfigureAwait(false); if (Bot.Bots.Count > 0) { - IEnumerable tasks = Bot.Bots.Values.Select(bot => Task.Run(() => bot.Stop(true))); - - switch (GlobalConfig.OptimizationMode) { - case GlobalConfig.EOptimizationMode.MinMemoryUsage: - foreach (Task task in tasks) { - await Task.WhenAny(task, Task.Delay(WebBrowser.MaxTries * 1000)).ConfigureAwait(false); - } - - break; - default: - await Task.WhenAny(Task.WhenAll(tasks), Task.Delay(Bot.Bots.Count * WebBrowser.MaxTries * 1000)).ConfigureAwait(false); - break; - } + // Stop() function can block due to SK2 sockets, don't forget a maximum delay + await Task.WhenAny(Utilities.InParallel(Bot.Bots.Values.Select(bot => Task.Run(() => bot.Stop(true)))), Task.Delay(Bot.Bots.Count * WebBrowser.MaxTries * 1000)).ConfigureAwait(false); // Extra second for Steam requests to go through await Task.Delay(1000).ConfigureAwait(false); diff --git a/ArchiSteamFarm/Trading.cs b/ArchiSteamFarm/Trading.cs index 8d61bde64..c857014b8 100644 --- a/ArchiSteamFarm/Trading.cs +++ b/ArchiSteamFarm/Trading.cs @@ -195,21 +195,7 @@ namespace ArchiSteamFarm { return; } - IEnumerable> tasks = tradeOffers.Select(ParseTrade); - ICollection results; - - switch (Program.GlobalConfig.OptimizationMode) { - case GlobalConfig.EOptimizationMode.MinMemoryUsage: - results = new List(tradeOffers.Count); - foreach (Task task in tasks) { - results.Add(await task.ConfigureAwait(false)); - } - - break; - default: - results = await Task.WhenAll(tasks).ConfigureAwait(false); - break; - } + IList results = await Utilities.InParallel(tradeOffers.Select(ParseTrade)).ConfigureAwait(false); if (Bot.HasMobileAuthenticator) { HashSet acceptedWithItemLoseTradeIDs = results.Where(result => (result != null) && (result.Result == ParseTradeResult.EResult.AcceptedWithItemLose)).Select(result => result.TradeID).ToHashSet(); diff --git a/ArchiSteamFarm/Utilities.cs b/ArchiSteamFarm/Utilities.cs index 09dc8c9f9..d4e172241 100644 --- a/ArchiSteamFarm/Utilities.cs +++ b/ArchiSteamFarm/Utilities.cs @@ -106,6 +106,50 @@ namespace ArchiSteamFarm { Task.Factory.StartNew(function, CancellationToken.None, options, TaskScheduler.Default); } + internal static async Task> InParallel(IEnumerable> tasks) { + if (tasks == null) { + ASF.ArchiLogger.LogNullError(nameof(tasks)); + return null; + } + + IList results; + + switch (Program.GlobalConfig.OptimizationMode) { + case GlobalConfig.EOptimizationMode.MinMemoryUsage: + results = new List(); + + foreach (Task task in tasks) { + results.Add(await task.ConfigureAwait(false)); + } + + break; + default: + results = await Task.WhenAll(tasks).ConfigureAwait(false); + break; + } + + return results; + } + + internal static async Task InParallel(IEnumerable tasks) { + if (tasks == null) { + ASF.ArchiLogger.LogNullError(nameof(tasks)); + return; + } + + switch (Program.GlobalConfig.OptimizationMode) { + case GlobalConfig.EOptimizationMode.MinMemoryUsage: + foreach (Task task in tasks) { + await task.ConfigureAwait(false); + } + + break; + default: + await Task.WhenAll(tasks).ConfigureAwait(false); + break; + } + } + internal static bool IsValidCdKey(string key) { if (string.IsNullOrEmpty(key)) { ASF.ArchiLogger.LogNullError(nameof(key));