From 2db65d324a12203984f569686a09b3df506d958f Mon Sep 17 00:00:00 2001 From: JustArchi Date: Sun, 23 Jul 2017 02:11:16 +0200 Subject: [PATCH] Closes #545 --- ArchiSteamFarm/Bot.cs | 190 +++++++++++++++++++++++++- ArchiSteamFarm/BotDatabase.cs | 64 +++++++-- ArchiSteamFarm/CardsFarmer.cs | 22 +-- ArchiSteamFarm/MobileAuthenticator.cs | 9 +- 4 files changed, 262 insertions(+), 23 deletions(-) diff --git a/ArchiSteamFarm/Bot.cs b/ArchiSteamFarm/Bot.cs index 1b92a6336..77cddb388 100755 --- a/ArchiSteamFarm/Bot.cs +++ b/ArchiSteamFarm/Bot.cs @@ -461,6 +461,16 @@ namespace ArchiSteamFarm { return GetSteamUserPermission(steamID) >= BotConfig.EPermission.Master; } + internal bool IsPriorityIdling(uint appID) { + if (appID == 0) { + ArchiLogger.LogNullError(nameof(appID)); + return false; + } + + bool result = BotDatabase.IsPriorityIdling(appID); + return result; + } + internal async Task LootIfNeeded() { if (!IsConnectedAndLoggedOn || !BotConfig.SendOnFarmingFinished) { return; @@ -627,6 +637,8 @@ namespace ArchiSteamFarm { return await ResponseFarm(steamID).ConfigureAwait(false); case "!HELP": return ResponseHelp(steamID); + case "!IQ": + return ResponseIdleQueue(steamID); case "!LOOT": return await ResponseLoot(steamID).ConfigureAwait(false); case "!LOOT^": @@ -698,6 +710,20 @@ namespace ArchiSteamFarm { } return args.Length == 3 ? ResponseInput(steamID, args[1], args[2]) : ResponseUnknown(steamID); + case "!IQ": + return await ResponseIdleQueue(steamID, args[1]).ConfigureAwait(false); + case "!IQADD": + if (args.Length > 2) { + return await ResponseIdleQueueAdd(steamID, args[1], args.GetArgsAsString(2)).ConfigureAwait(false); + } + + return ResponseIdleQueueAdd(steamID, args[1]); + case "!IQRM": + if (args.Length > 2) { + return await ResponseIdleQueueRemove(steamID, args[1], args.GetArgsAsString(2)).ConfigureAwait(false); + } + + return ResponseIdleQueueRemove(steamID, args[1]); case "!LOOT": return await ResponseLoot(steamID, args[1]).ConfigureAwait(false); case "!LOOT^": @@ -1051,8 +1077,7 @@ namespace ArchiSteamFarm { SetUserInput(ASF.EUserInputType.DeviceID, deviceID); } - BotDatabase.MobileAuthenticator.CorrectDeviceID(DeviceID); - BotDatabase.Save(); + BotDatabase.CorrectMobileAuthenticatorDeviceID(DeviceID); } ArchiLogger.LogGenericInfo(Strings.BotAuthenticatorImportFinished); @@ -2405,6 +2430,167 @@ namespace ArchiSteamFarm { return null; } + private static async Task ResponseIdleQueue(ulong steamID, string botNames) { + if ((steamID == 0) || string.IsNullOrEmpty(botNames)) { + ASF.ArchiLogger.LogNullError(nameof(steamID) + " || " + nameof(botNames)); + return null; + } + + HashSet bots = GetBots(botNames); + if ((bots == null) || (bots.Count == 0)) { + return IsOwner(steamID) ? FormatStaticResponse(string.Format(Strings.BotNotFound, botNames)) : null; + } + + IEnumerable> tasks = bots.Select(bot => Task.Run(() => bot.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; + } + + List responses = new List(results.Where(result => !string.IsNullOrEmpty(result))); + return responses.Count > 0 ? string.Join("", responses) : null; + } + + private string ResponseIdleQueue(ulong steamID) { + if (steamID == 0) { + ArchiLogger.LogNullError(nameof(steamID)); + return null; + } + + string result = IsMaster(steamID) ? FormatBotResponse(string.Join(", ", BotDatabase.GetIdlingPriorityAppIDs())) : null; + return result; + } + + private string ResponseIdleQueueAdd(ulong steamID, string targetsText) { + if ((steamID == 0) || string.IsNullOrEmpty(targetsText)) { + ArchiLogger.LogNullError(nameof(steamID) + " || " + nameof(targetsText)); + return null; + } + + if (!IsMaster(steamID)) { + return null; + } + + string[] targets = targetsText.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); + + HashSet appIDs = new HashSet(); + foreach (string target in targets) { + if (!uint.TryParse(target, out uint appID) || (appID == 0)) { + return FormatBotResponse(string.Format(Strings.ErrorParsingObject, nameof(appID))); + } + + appIDs.Add(appID); + } + + if (appIDs.Count == 0) { + return FormatBotResponse(string.Format(Strings.ErrorIsEmpty, nameof(appIDs))); + } + + BotDatabase.AddIdlingPriorityAppIDs(appIDs); + return FormatBotResponse(Strings.Done); + } + + private static async Task ResponseIdleQueueAdd(ulong steamID, string botNames, string targetsText) { + if ((steamID == 0) || string.IsNullOrEmpty(botNames) || string.IsNullOrEmpty(targetsText)) { + ASF.ArchiLogger.LogNullError(nameof(steamID) + " || " + nameof(botNames) + " || " + nameof(targetsText)); + return null; + } + + HashSet bots = GetBots(botNames); + if ((bots == null) || (bots.Count == 0)) { + return IsOwner(steamID) ? FormatStaticResponse(string.Format(Strings.BotNotFound, botNames)) : null; + } + + IEnumerable> tasks = bots.Select(bot => Task.Run(() => bot.ResponseIdleQueueAdd(steamID, targetsText))); + 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; + } + + List responses = new List(results.Where(result => !string.IsNullOrEmpty(result))); + return responses.Count > 0 ? string.Join("", responses) : null; + } + + private static async Task ResponseIdleQueueRemove(ulong steamID, string botNames, string targetsText) { + if ((steamID == 0) || string.IsNullOrEmpty(botNames) || string.IsNullOrEmpty(targetsText)) { + ASF.ArchiLogger.LogNullError(nameof(steamID) + " || " + nameof(botNames) + " || " + nameof(targetsText)); + return null; + } + + HashSet bots = GetBots(botNames); + if ((bots == null) || (bots.Count == 0)) { + return IsOwner(steamID) ? FormatStaticResponse(string.Format(Strings.BotNotFound, botNames)) : null; + } + + IEnumerable> tasks = bots.Select(bot => Task.Run(() => bot.ResponseIdleQueueRemove(steamID, targetsText))); + 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; + } + + List responses = new List(results.Where(result => !string.IsNullOrEmpty(result))); + return responses.Count > 0 ? string.Join("", responses) : null; + } + + private string ResponseIdleQueueRemove(ulong steamID, string targetsText) { + if ((steamID == 0) || string.IsNullOrEmpty(targetsText)) { + ArchiLogger.LogNullError(nameof(steamID) + " || " + nameof(targetsText)); + return null; + } + + if (!IsMaster(steamID)) { + return null; + } + + string[] targets = targetsText.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); + + HashSet appIDs = new HashSet(); + foreach (string target in targets) { + if (!uint.TryParse(target, out uint appID) || (appID == 0)) { + return FormatBotResponse(string.Format(Strings.ErrorParsingObject, nameof(appID))); + } + + appIDs.Add(appID); + } + + if (appIDs.Count == 0) { + return FormatBotResponse(string.Format(Strings.ErrorIsEmpty, nameof(appIDs))); + } + + BotDatabase.RemoveIdlingPriorityAppIDs(appIDs); + return FormatBotResponse(Strings.Done); + } + private string ResponseInput(ulong steamID, string propertyName, string inputValue) { if ((steamID == 0) || string.IsNullOrEmpty(propertyName) || string.IsNullOrEmpty(inputValue)) { ASF.ArchiLogger.LogNullError(nameof(steamID) + " || " + nameof(propertyName) + " || " + nameof(inputValue)); diff --git a/ArchiSteamFarm/BotDatabase.cs b/ArchiSteamFarm/BotDatabase.cs index 205956be5..81cc6390c 100644 --- a/ArchiSteamFarm/BotDatabase.cs +++ b/ArchiSteamFarm/BotDatabase.cs @@ -35,6 +35,9 @@ namespace ArchiSteamFarm { private readonly object FileLock = new object(); + [JsonProperty(Required = Required.DisallowNull)] + private readonly ConcurrentHashSet IdlingPriorityAppIDs = new ConcurrentHashSet(); + internal string LoginKey { get => _LoginKey; @@ -94,15 +97,49 @@ namespace ArchiSteamFarm { } } - internal IEnumerable GetBlacklistedFromTradesSteamIDs() => BlacklistedFromTradesSteamIDs; - - internal bool IsBlacklistedFromTrades(ulong steamID) { - if (steamID != 0) { - return BlacklistedFromTradesSteamIDs.Contains(steamID); + internal void AddIdlingPriorityAppIDs(HashSet appIDs) { + if ((appIDs == null) || (appIDs.Count == 0)) { + ASF.ArchiLogger.LogNullError(nameof(appIDs)); + return; } - ASF.ArchiLogger.LogNullError(nameof(steamID)); - return false; + if (IdlingPriorityAppIDs.AddRange(appIDs)) { + Save(); + } + } + + internal void CorrectMobileAuthenticatorDeviceID(string deviceID) { + if (string.IsNullOrEmpty(deviceID) || (MobileAuthenticator == null)) { + ASF.ArchiLogger.LogNullError(nameof(deviceID) + " || " + nameof(MobileAuthenticator)); + return; + } + + if (MobileAuthenticator.CorrectDeviceID(deviceID)) { + Save(); + } + } + + internal IEnumerable GetBlacklistedFromTradesSteamIDs() => BlacklistedFromTradesSteamIDs; + internal IEnumerable GetIdlingPriorityAppIDs() => IdlingPriorityAppIDs; + + internal bool IsBlacklistedFromTrades(ulong steamID) { + if (steamID == 0) { + ASF.ArchiLogger.LogNullError(nameof(steamID)); + return false; + } + + bool result = BlacklistedFromTradesSteamIDs.Contains(steamID); + return result; + } + + internal bool IsPriorityIdling(uint appID) { + if (appID == 0) { + ASF.ArchiLogger.LogNullError(nameof(appID)); + return false; + } + + bool result = IdlingPriorityAppIDs.Contains(appID); + return result; } internal static BotDatabase Load(string filePath) { @@ -144,7 +181,18 @@ namespace ArchiSteamFarm { } } - internal void Save() { + internal void RemoveIdlingPriorityAppIDs(HashSet appIDs) { + if ((appIDs == null) || (appIDs.Count == 0)) { + ASF.ArchiLogger.LogNullError(nameof(appIDs)); + return; + } + + if (IdlingPriorityAppIDs.RemoveRange(appIDs)) { + Save(); + } + } + + private void Save() { string json = JsonConvert.SerializeObject(this); if (string.IsNullOrEmpty(json)) { ASF.ArchiLogger.LogNullError(nameof(json)); diff --git a/ArchiSteamFarm/CardsFarmer.cs b/ArchiSteamFarm/CardsFarmer.cs index 7900d012e..b04a0773e 100755 --- a/ArchiSteamFarm/CardsFarmer.cs +++ b/ArchiSteamFarm/CardsFarmer.cs @@ -817,37 +817,37 @@ namespace ArchiSteamFarm { } private void SortGamesToFarm() { - IOrderedEnumerable gamesToFarm; + IOrderedEnumerable gamesToFarm = GamesToFarm.OrderBy(game => Bot.IsPriorityIdling(game.AppID) ? 1 : 0); switch (Bot.BotConfig.FarmingOrder) { case BotConfig.EFarmingOrder.Unordered: - return; + break; case BotConfig.EFarmingOrder.AppIDsAscending: - gamesToFarm = GamesToFarm.OrderBy(game => game.AppID); + gamesToFarm = gamesToFarm.ThenBy(game => game.AppID); break; case BotConfig.EFarmingOrder.AppIDsDescending: - gamesToFarm = GamesToFarm.OrderByDescending(game => game.AppID); + gamesToFarm = gamesToFarm.ThenByDescending(game => game.AppID); break; case BotConfig.EFarmingOrder.CardDropsAscending: - gamesToFarm = GamesToFarm.OrderBy(game => game.CardsRemaining); + gamesToFarm = gamesToFarm.ThenBy(game => game.CardsRemaining); break; case BotConfig.EFarmingOrder.CardDropsDescending: - gamesToFarm = GamesToFarm.OrderByDescending(game => game.CardsRemaining); + gamesToFarm = gamesToFarm.ThenByDescending(game => game.CardsRemaining); break; case BotConfig.EFarmingOrder.HoursAscending: - gamesToFarm = GamesToFarm.OrderBy(game => game.HoursPlayed); + gamesToFarm = gamesToFarm.ThenBy(game => game.HoursPlayed); break; case BotConfig.EFarmingOrder.HoursDescending: - gamesToFarm = GamesToFarm.OrderByDescending(game => game.HoursPlayed); + gamesToFarm = gamesToFarm.ThenByDescending(game => game.HoursPlayed); break; case BotConfig.EFarmingOrder.NamesAscending: - gamesToFarm = GamesToFarm.OrderBy(game => game.GameName); + gamesToFarm = gamesToFarm.ThenBy(game => game.GameName); break; case BotConfig.EFarmingOrder.NamesDescending: - gamesToFarm = GamesToFarm.OrderByDescending(game => game.GameName); + gamesToFarm = gamesToFarm.ThenByDescending(game => game.GameName); break; case BotConfig.EFarmingOrder.Random: - gamesToFarm = GamesToFarm.OrderBy(game => Utilities.RandomNext()); + gamesToFarm = gamesToFarm.ThenBy(game => Utilities.RandomNext()); break; default: Bot.ArchiLogger.LogGenericError(string.Format(Strings.ErrorIsInvalid, nameof(Bot.BotConfig.FarmingOrder))); diff --git a/ArchiSteamFarm/MobileAuthenticator.cs b/ArchiSteamFarm/MobileAuthenticator.cs index 14bda21b9..8c4a0453e 100644 --- a/ArchiSteamFarm/MobileAuthenticator.cs +++ b/ArchiSteamFarm/MobileAuthenticator.cs @@ -70,13 +70,18 @@ namespace ArchiSteamFarm { public void Dispose() => ConfirmationsSemaphore.Dispose(); - internal void CorrectDeviceID(string deviceID) { + internal bool CorrectDeviceID(string deviceID) { if (string.IsNullOrEmpty(deviceID)) { Bot.ArchiLogger.LogNullError(nameof(deviceID)); - return; + return false; + } + + if (!string.IsNullOrEmpty(DeviceID) && DeviceID.Equals(deviceID)) { + return false; } DeviceID = deviceID; + return true; } internal async Task GenerateToken() {