From 89a36beeaeba0df5fc97a9cd17df4926cce532f0 Mon Sep 17 00:00:00 2001 From: JustArchi Date: Wed, 8 Feb 2017 14:35:01 +0100 Subject: [PATCH] Improve statistics reporting Skip reporting when user has private or empty inventory --- ArchiSteamFarm/ArchiWebHandler.cs | 69 ++++++++++++++++++++++++++----- ArchiSteamFarm/Statistics.cs | 26 ++++++++---- 2 files changed, 77 insertions(+), 18 deletions(-) diff --git a/ArchiSteamFarm/ArchiWebHandler.cs b/ArchiSteamFarm/ArchiWebHandler.cs index 905f1f189..7e6f7511d 100644 --- a/ArchiSteamFarm/ArchiWebHandler.cs +++ b/ArchiSteamFarm/ArchiWebHandler.cs @@ -58,14 +58,16 @@ namespace ArchiSteamFarm { private static int Timeout = GlobalConfig.DefaultConnectionTimeout * 1000; // This must be int type private readonly Bot Bot; + private readonly SemaphoreSlim PublicInventorySemaphore = new SemaphoreSlim(1); private readonly SemaphoreSlim SessionSemaphore = new SemaphoreSlim(1); private readonly SemaphoreSlim SteamApiKeySemaphore = new SemaphoreSlim(1); private readonly WebBrowser WebBrowser; internal bool Ready { get; private set; } + private bool? CachedPublicInventory; + private string CachedSteamApiKey; private DateTime LastSessionRefreshCheck = DateTime.MinValue; - private string SteamApiKey; private ulong SteamID; internal ArchiWebHandler(Bot bot) { @@ -79,6 +81,7 @@ namespace ArchiSteamFarm { } public void Dispose() { + PublicInventorySemaphore.Dispose(); SessionSemaphore.Dispose(); SteamApiKeySemaphore.Dispose(); } @@ -172,7 +175,7 @@ namespace ArchiSteamFarm { } string steamApiKey = await GetApiKey().ConfigureAwait(false); - if (string.IsNullOrEmpty(SteamApiKey)) { + if (string.IsNullOrEmpty(steamApiKey)) { return; } @@ -223,7 +226,7 @@ namespace ArchiSteamFarm { internal async Task> GetActiveTradeOffers() { string steamApiKey = await GetApiKey().ConfigureAwait(false); - if (string.IsNullOrEmpty(SteamApiKey)) { + if (string.IsNullOrEmpty(steamApiKey)) { return null; } @@ -611,7 +614,7 @@ namespace ArchiSteamFarm { } string steamApiKey = await GetApiKey().ConfigureAwait(false); - if (string.IsNullOrEmpty(SteamApiKey)) { + if (string.IsNullOrEmpty(steamApiKey)) { return null; } @@ -784,6 +787,31 @@ namespace ArchiSteamFarm { return response?.Success; } + internal async Task HasPublicInventory() { + if (CachedPublicInventory.HasValue) { + return CachedPublicInventory.Value; + } + + // We didn't fetch API key yet + await PublicInventorySemaphore.WaitAsync().ConfigureAwait(false); + + try { + if (CachedPublicInventory.HasValue) { + return CachedPublicInventory.Value; + } + + bool? isInventoryPublic = await IsInventoryPublic().ConfigureAwait(false); + if (!isInventoryPublic.HasValue) { + return false; + } + + CachedPublicInventory = isInventoryPublic.Value; + return isInventoryPublic.Value; + } finally { + PublicInventorySemaphore.Release(); + } + } + internal async Task HasValidApiKey() => !string.IsNullOrEmpty(await GetApiKey().ConfigureAwait(false)); internal static void Init() => Timeout = Program.GlobalConfig.ConnectionTimeout * 1000; @@ -984,18 +1012,18 @@ namespace ArchiSteamFarm { } private async Task GetApiKey(bool allowRegister = true) { - if (SteamApiKey != null) { + if (CachedSteamApiKey != null) { // We fetched API key already, and either got valid one, or permanent AccessDenied // In any case, this is our final result - return SteamApiKey; + return CachedSteamApiKey; } // We didn't fetch API key yet await SteamApiKeySemaphore.WaitAsync().ConfigureAwait(false); try { - if (SteamApiKey != null) { - return SteamApiKey; + if (CachedSteamApiKey != null) { + return CachedSteamApiKey; } Tuple result = await GetApiKeyState().ConfigureAwait(false); @@ -1008,8 +1036,8 @@ namespace ArchiSteamFarm { case ESteamApiKeyState.Registered: // We succeeded in fetching API key, and it resulted in registered key // Cache the result and return it - SteamApiKey = result.Item2; - return SteamApiKey; + CachedSteamApiKey = result.Item2; + return CachedSteamApiKey; case ESteamApiKeyState.NotRegisteredYet: // We succeeded in fetching API key, and it resulted in no key registered yet if (!allowRegister) { @@ -1034,7 +1062,7 @@ namespace ArchiSteamFarm { case ESteamApiKeyState.AccessDenied: // We succeeded in fetching API key, but it resulted in access denied // Cache the result as empty, and return null - SteamApiKey = ""; + CachedSteamApiKey = ""; return null; default: // We got some kind of error, maybe it's temporary, maybe it's permanent @@ -1183,6 +1211,25 @@ namespace ArchiSteamFarm { } } + private async Task IsInventoryPublic() { + if (!Ready || !await RefreshSessionIfNeeded().ConfigureAwait(false)) { + return null; + } + + const string request = SteamCommunityURL + "/my/edit/settings?l=english"; + HtmlDocument htmlDocument = await WebBrowser.UrlGetToHtmlDocumentRetry(request).ConfigureAwait(false); + + HtmlNode htmlNode = htmlDocument?.DocumentNode.SelectSingleNode("//input[@id='inventoryPrivacySetting_public']"); + if (htmlNode == null) { + return null; + } + + // Notice: checked doesn't have a value - null is lack of attribute, "" is attribute existing + string state = htmlNode.GetAttributeValue("checked", null); + + return state != null; + } + private async Task IsLoggedIn() { // It would make sense to use /my/profile here, but it dismisses notifications related to profile comments // So instead, we'll use some less intrusive link, such as /my/videos diff --git a/ArchiSteamFarm/Statistics.cs b/ArchiSteamFarm/Statistics.cs index 56e623274..93718ccaf 100644 --- a/ArchiSteamFarm/Statistics.cs +++ b/ArchiSteamFarm/Statistics.cs @@ -24,9 +24,11 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Threading; using System.Threading.Tasks; +using ArchiSteamFarm.JSON; using SteamKit2; namespace ArchiSteamFarm { @@ -85,6 +87,7 @@ namespace ArchiSteamFarm { internal async Task OnLoggedOn() => await Bot.ArchiWebHandler.JoinGroup(SharedInfo.ASFGroupSteamID).ConfigureAwait(false); + [SuppressMessage("ReSharper", "FunctionComplexityOverflow")] internal async Task OnPersonaState(SteamFriends.PersonaStateCallback callback) { if (callback == null) { ASF.ArchiLogger.LogNullError(nameof(callback)); @@ -92,17 +95,17 @@ namespace ArchiSteamFarm { } // Don't announce if we don't meet conditions - if (!Bot.HasMobileAuthenticator || !Bot.BotConfig.TradingPreferences.HasFlag(BotConfig.ETradingPreferences.SteamTradeMatcher) || !await Bot.ArchiWebHandler.HasValidApiKey().ConfigureAwait(false)) { + if (!Bot.HasMobileAuthenticator || !Bot.BotConfig.TradingPreferences.HasFlag(BotConfig.ETradingPreferences.SteamTradeMatcher) || !await Bot.ArchiWebHandler.HasValidApiKey().ConfigureAwait(false) || !await Bot.ArchiWebHandler.HasPublicInventory().ConfigureAwait(false)) { ShouldSendHeartBeats = false; return; } string nickname = callback.Name ?? ""; - string avatarHash = ""; + string avatarHash = ""; if ((callback.AvatarHash != null) && (callback.AvatarHash.Length > 0) && callback.AvatarHash.Any(singleByte => singleByte != 0)) { avatarHash = BitConverter.ToString(callback.AvatarHash).Replace("-", "").ToLowerInvariant(); - if (avatarHash.Equals("0000000000000000000000000000000000000000")) { + if (avatarHash.All(singleChar => singleChar == '0')) { avatarHash = ""; } } @@ -110,7 +113,7 @@ namespace ArchiSteamFarm { bool matchEverything = Bot.BotConfig.TradingPreferences.HasFlag(BotConfig.ETradingPreferences.MatchEverything); // Skip announcing if we already announced this bot with the same data - if (!string.IsNullOrEmpty(LastNickname) && nickname.Equals(LastNickname) && !string.IsNullOrEmpty(LastAvatarHash) && avatarHash.Equals(LastAvatarHash) && LastMatchEverything.HasValue && (matchEverything == LastMatchEverything.Value)) { + if ((LastNickname != null) && nickname.Equals(LastNickname) && (LastAvatarHash != null) && avatarHash.Equals(LastAvatarHash) && LastMatchEverything.HasValue && (matchEverything == LastMatchEverything.Value)) { return; } @@ -118,7 +121,15 @@ namespace ArchiSteamFarm { try { // Skip announcing if we already announced this bot with the same data - if (!string.IsNullOrEmpty(LastNickname) && nickname.Equals(LastNickname) && !string.IsNullOrEmpty(LastAvatarHash) && avatarHash.Equals(LastAvatarHash) && LastMatchEverything.HasValue && (matchEverything == LastMatchEverything.Value)) { + if ((LastNickname != null) && nickname.Equals(LastNickname) && (LastAvatarHash != null) && avatarHash.Equals(LastAvatarHash) && LastMatchEverything.HasValue && (matchEverything == LastMatchEverything.Value)) { + return; + } + + await Trading.LimitInventoryRequestsAsync().ConfigureAwait(false); + HashSet inventory = await Bot.ArchiWebHandler.GetMySteamInventory(true, new HashSet { Steam.Item.EType.TradingCard }).ConfigureAwait(false); + + if ((inventory == null) || (inventory.Count == 0)) { + // Don't announce, we have empty inventory return; } @@ -126,12 +137,13 @@ namespace ArchiSteamFarm { ShouldSendHeartBeats = true; string request = await GetURL().ConfigureAwait(false) + "/api/Announce"; - Dictionary data = new Dictionary(5) { + Dictionary data = new Dictionary(6) { { "SteamID", Bot.SteamID.ToString() }, { "Guid", Program.GlobalDatabase.Guid.ToString("N") }, { "Nickname", nickname }, { "AvatarHash", avatarHash }, - { "MatchEverything", matchEverything ? "1" : "0" } + { "MatchEverything", matchEverything ? "1" : "0" }, + { "CardsCount", inventory.Count.ToString() } }; // We don't need retry logic here