From f97ffc76b599303985c8125dea5b4ff249fa5634 Mon Sep 17 00:00:00 2001 From: JustArchi Date: Sun, 23 Jul 2017 05:06:11 +0200 Subject: [PATCH] Closes #510 --- ArchiSteamFarm/BotConfig.cs | 4 +- ArchiSteamFarm/CardsFarmer.cs | 69 ++++++++++++++++++++++++++++------- 2 files changed, 58 insertions(+), 15 deletions(-) diff --git a/ArchiSteamFarm/BotConfig.cs b/ArchiSteamFarm/BotConfig.cs index 2dc3fbd2f..e6d030a03 100644 --- a/ArchiSteamFarm/BotConfig.cs +++ b/ArchiSteamFarm/BotConfig.cs @@ -222,7 +222,9 @@ namespace ArchiSteamFarm { HoursDescending, NamesAscending, NamesDescending, - Random + Random, + BadgeLevelsAscending, + BadgeLevelsDescending } internal enum EPermission : byte { diff --git a/ArchiSteamFarm/CardsFarmer.cs b/ArchiSteamFarm/CardsFarmer.cs index b04a0773e..852e4115c 100755 --- a/ArchiSteamFarm/CardsFarmer.cs +++ b/ArchiSteamFarm/CardsFarmer.cs @@ -264,7 +264,7 @@ namespace ArchiSteamFarm { } } - private async Task CheckGame(uint appID, string name, float hours) { + private async Task CheckGame(uint appID, string name, float hours, byte badgeLevel) { if ((appID == 0) || string.IsNullOrEmpty(name) || (hours < 0)) { Bot.ArchiLogger.LogNullError(nameof(appID) + " || " + nameof(name) + " || " + nameof(hours)); return; @@ -280,7 +280,7 @@ namespace ArchiSteamFarm { return; } - GamesToFarm.Add(new Game(appID, name, hours, cardsRemaining.Value)); + GamesToFarm.Add(new Game(appID, name, hours, cardsRemaining.Value, badgeLevel)); } private async Task CheckGamesForFarming() { @@ -297,7 +297,7 @@ namespace ArchiSteamFarm { return; } - HtmlNodeCollection htmlNodes = htmlDocument.DocumentNode.SelectNodes("//div[@class='badge_title_stats_content']"); + HtmlNodeCollection htmlNodes = htmlDocument.DocumentNode.SelectNodes("//div[@class='badge_row_inner']"); if (htmlNodes == null) { // No eligible badges whatsoever return; @@ -306,7 +306,9 @@ namespace ArchiSteamFarm { HashSet backgroundTasks = new HashSet(); foreach (HtmlNode htmlNode in htmlNodes) { - HtmlNode appIDNode = htmlNode.SelectSingleNode(".//div[@class='card_drop_info_dialog']"); + HtmlNode statsNode = htmlNode.SelectSingleNode(".//div[@class='badge_title_stats_content']"); + + HtmlNode appIDNode = statsNode?.SelectSingleNode(".//div[@class='card_drop_info_dialog']"); if (appIDNode == null) { // It's just a badge, nothing more continue; @@ -347,7 +349,7 @@ namespace ArchiSteamFarm { } // Cards - HtmlNode progressNode = htmlNode.SelectSingleNode(".//span[@class='progress_info_bold']"); + HtmlNode progressNode = statsNode.SelectSingleNode(".//span[@class='progress_info_bold']"); if (progressNode == null) { Bot.ArchiLogger.LogNullError(nameof(progressNode)); continue; @@ -380,7 +382,7 @@ namespace ArchiSteamFarm { } // To save us on extra work, check cards earned so far first - HtmlNode cardsEarnedNode = htmlNode.SelectSingleNode(".//div[@class='card_drop_info_header']"); + HtmlNode cardsEarnedNode = statsNode.SelectSingleNode(".//div[@class='card_drop_info_header']"); if (cardsEarnedNode == null) { Bot.ArchiLogger.LogNullError(nameof(cardsEarnedNode)); continue; @@ -419,7 +421,7 @@ namespace ArchiSteamFarm { } // Hours - HtmlNode timeNode = htmlNode.SelectSingleNode(".//div[@class='badge_title_stats_playtime']"); + HtmlNode timeNode = statsNode.SelectSingleNode(".//div[@class='badge_title_stats_playtime']"); if (timeNode == null) { Bot.ArchiLogger.LogNullError(nameof(timeNode)); continue; @@ -443,7 +445,7 @@ namespace ArchiSteamFarm { } // Names - HtmlNode nameNode = htmlNode.SelectSingleNode("(.//div[@class='card_drop_info_body'])[last()]"); + HtmlNode nameNode = statsNode.SelectSingleNode("(.//div[@class='card_drop_info_body'])[last()]"); if (nameNode == null) { Bot.ArchiLogger.LogNullError(nameof(nameNode)); continue; @@ -477,14 +479,44 @@ namespace ArchiSteamFarm { name = WebUtility.HtmlDecode(name.Substring(nameStartIndex, nameEndIndex - nameStartIndex)); - // We have two possible cases here - // Either we have decent info about appID, name, hours and cardsRemaining (cardsRemaining > 0) - // OR we strongly believe that Steam lied to us, in this case we will need to check game invidually (cardsRemaining == 0) + // Levels + byte badgeLevel = 0; + HtmlNode levelNode = htmlNode.SelectSingleNode(".//div[@class='badge_info_description']/div[2]"); + if (levelNode != null) { + // There is no levelNode if we didn't craft that badge yet (level 0) + string levelString = levelNode.InnerText; + if (string.IsNullOrEmpty(levelString)) { + Bot.ArchiLogger.LogNullError(nameof(levelString)); + continue; + } + + int levelIndex = levelString.IndexOf("Level ", StringComparison.OrdinalIgnoreCase); + if (levelIndex < 0) { + Bot.ArchiLogger.LogNullError(nameof(levelIndex)); + continue; + } + + levelIndex += 6; + if (levelString.Length <= levelIndex) { + Bot.ArchiLogger.LogNullError(nameof(levelIndex)); + continue; + } + + levelString = levelString.Substring(levelIndex, 1); + if (!byte.TryParse(levelString, out badgeLevel) || (badgeLevel == 0) || (badgeLevel > 5)) { + Bot.ArchiLogger.LogNullError(nameof(badgeLevel)); + continue; + } + } + + // Done with parsing, we have two possible cases here + // Either we have decent info about appID, name, hours, cardsRemaining (cardsRemaining > 0) and level + // OR we strongly believe that Steam lied to us, in this case we will need to check game invidually (cardsRemaining == 0) if (cardsRemaining > 0) { - GamesToFarm.Add(new Game(appID, name, hours, cardsRemaining)); + GamesToFarm.Add(new Game(appID, name, hours, cardsRemaining, badgeLevel)); } else { - Task task = CheckGame(appID, name, hours); + Task task = CheckGame(appID, name, hours, badgeLevel); switch (Program.GlobalConfig.OptimizationMode) { case GlobalConfig.EOptimizationMode.MinMemoryUsage: await task.ConfigureAwait(false); @@ -828,6 +860,12 @@ namespace ArchiSteamFarm { case BotConfig.EFarmingOrder.AppIDsDescending: gamesToFarm = gamesToFarm.ThenByDescending(game => game.AppID); break; + case BotConfig.EFarmingOrder.BadgeLevelsAscending: + gamesToFarm = gamesToFarm.ThenBy(game => game.BadgeLevel); + break; + case BotConfig.EFarmingOrder.BadgeLevelsDescending: + gamesToFarm = gamesToFarm.ThenByDescending(game => game.BadgeLevel); + break; case BotConfig.EFarmingOrder.CardDropsAscending: gamesToFarm = gamesToFarm.ThenBy(game => game.CardsRemaining); break; @@ -862,6 +900,8 @@ namespace ArchiSteamFarm { [JsonProperty] internal readonly uint AppID; + internal readonly byte BadgeLevel; + [JsonProperty] internal readonly string GameName; @@ -871,7 +911,7 @@ namespace ArchiSteamFarm { [JsonProperty] internal float HoursPlayed { get; set; } - internal Game(uint appID, string gameName, float hoursPlayed, ushort cardsRemaining) { + internal Game(uint appID, string gameName, float hoursPlayed, ushort cardsRemaining, byte badgeLevel) { if ((appID == 0) || string.IsNullOrEmpty(gameName) || (hoursPlayed < 0) || (cardsRemaining == 0)) { throw new ArgumentOutOfRangeException(nameof(appID) + " || " + nameof(gameName) + " || " + nameof(hoursPlayed) + " || " + nameof(cardsRemaining)); } @@ -880,6 +920,7 @@ namespace ArchiSteamFarm { GameName = gameName; HoursPlayed = hoursPlayed; CardsRemaining = cardsRemaining; + BadgeLevel = badgeLevel; } public override bool Equals(object obj) {