diff --git a/ArchiSteamFarm/ArchiWebHandler.cs b/ArchiSteamFarm/ArchiWebHandler.cs index 1702d7255..1717981fa 100644 --- a/ArchiSteamFarm/ArchiWebHandler.cs +++ b/ArchiSteamFarm/ArchiWebHandler.cs @@ -32,21 +32,23 @@ using System.Net.Http; using System.Text; using System.Threading.Tasks; using System.Xml; +using System.Threading; namespace ArchiSteamFarm { internal sealed class ArchiWebHandler { private const string SteamCommunity = "steamcommunity.com"; + private const byte MinSessionTTL = 15; // Assume session is valid for at least that amount of seconds private static string SteamCommunityURL = "https://" + SteamCommunity; - private static int Timeout = 30 * 1000; + private static int Timeout = GlobalConfig.DefaultHttpTimeout * 1000; private readonly Bot Bot; private readonly Dictionary Cookie = new Dictionary(4); - - internal bool IsInitialized { get; private set; } + private readonly SemaphoreSlim SessionSemaphore = new SemaphoreSlim(1); private ulong SteamID; + private DateTime LastSessionRefreshCheck = DateTime.MinValue; internal static void Init() { Timeout = Program.GlobalConfig.HttpTimeout * 1000; @@ -61,12 +63,8 @@ namespace ArchiSteamFarm { Bot = bot; } - internal void OnDisconnected() { - IsInitialized = false; - } - - internal async Task Init(SteamClient steamClient, string webAPIUserNonce, string parentalPin) { - if (steamClient == null || steamClient.SteamID == null || string.IsNullOrEmpty(webAPIUserNonce) || IsInitialized) { + internal bool Init(SteamClient steamClient, string webAPIUserNonce, string parentalPin) { + if (steamClient == null || steamClient.SteamID == null || string.IsNullOrEmpty(webAPIUserNonce)) { return false; } @@ -127,9 +125,11 @@ namespace ArchiSteamFarm { // The below is used for display purposes only Cookie["webTradeEligibility"] = "{\"allowed\":0,\"reason\":0,\"allowed_at_time\":0,\"steamguard_required_days\":0,\"sales_this_year\":0,\"max_sales_per_year\":0,\"forms_requested\":0}"; - await UnlockParentalAccount(parentalPin).ConfigureAwait(false); + if (!UnlockParentalAccount(parentalPin).Result) { + return false; + } - IsInitialized = true; + LastSessionRefreshCheck = DateTime.Now; return true; } @@ -152,15 +152,34 @@ namespace ArchiSteamFarm { return htmlNode != null; } - internal async Task ReconnectIfNeeded() { - bool? isLoggedIn = await IsLoggedIn().ConfigureAwait(false); - if (isLoggedIn.HasValue && !isLoggedIn.Value) { - Logging.LogGenericInfo("Reconnecting because our sessionID expired!", Bot.BotName); - Bot.RestartIfRunning().Forget(); + internal async Task RefreshSessionIfNeeded() { + DateTime now = DateTime.Now; + if (now.Subtract(LastSessionRefreshCheck).TotalSeconds < MinSessionTTL) { return true; } - return false; + await SessionSemaphore.WaitAsync().ConfigureAwait(false); + + now = DateTime.Now; + if (now.Subtract(LastSessionRefreshCheck).TotalSeconds < MinSessionTTL) { + SessionSemaphore.Release(); + return true; + } + + bool result; + + bool? isLoggedIn = await IsLoggedIn().ConfigureAwait(false); + if (isLoggedIn.GetValueOrDefault(true)) { + result = true; + now = DateTime.Now; + LastSessionRefreshCheck = now; + } else { + Logging.LogGenericInfo("Refreshing our session!", Bot.BotName); + result = await Bot.RefreshSession().ConfigureAwait(false); + } + + SessionSemaphore.Release(); + return result; } internal async Task> GetOwnedGames() { @@ -168,6 +187,10 @@ namespace ArchiSteamFarm { return null; } + if (!await RefreshSessionIfNeeded().ConfigureAwait(false)) { + return null; + } + string request = SteamCommunityURL + "/profiles/" + SteamID + "/games/?xml=1"; XmlDocument response = null; @@ -273,6 +296,10 @@ namespace ArchiSteamFarm { return false; } + if (!await RefreshSessionIfNeeded().ConfigureAwait(false)) { + return false; + } + string sessionID; if (!Cookie.TryGetValue("sessionid", out sessionID)) { return false; @@ -303,6 +330,10 @@ namespace ArchiSteamFarm { return false; } + if (!await RefreshSessionIfNeeded().ConfigureAwait(false)) { + return false; + } + string sessionID; if (!Cookie.TryGetValue("sessionid", out sessionID)) { return false; @@ -361,6 +392,10 @@ namespace ArchiSteamFarm { } internal async Task> GetMyTradableInventory() { + if (!await RefreshSessionIfNeeded().ConfigureAwait(false)) { + return null; + } + JObject jObject = null; for (byte i = 0; i < WebBrowser.MaxRetries && jObject == null; i++) { jObject = await WebBrowser.UrlGetToJObject(SteamCommunityURL + "/my/inventory/json/753/6?trading=1", Cookie).ConfigureAwait(false); @@ -394,6 +429,10 @@ namespace ArchiSteamFarm { return false; } + if (!await RefreshSessionIfNeeded().ConfigureAwait(false)) { + return false; + } + string sessionID; if (!Cookie.TryGetValue("sessionid", out sessionID)) { return false; @@ -453,6 +492,10 @@ namespace ArchiSteamFarm { return null; } + if (!await RefreshSessionIfNeeded().ConfigureAwait(false)) { + return null; + } + HtmlDocument htmlDocument = null; for (byte i = 0; i < WebBrowser.MaxRetries && htmlDocument == null; i++) { htmlDocument = await WebBrowser.UrlGetToHtmlDocument(SteamCommunityURL + "/profiles/" + SteamID + "/badges?l=english&p=" + page, Cookie).ConfigureAwait(false); @@ -471,6 +514,10 @@ namespace ArchiSteamFarm { return null; } + if (!await RefreshSessionIfNeeded().ConfigureAwait(false)) { + return null; + } + HtmlDocument htmlDocument = null; for (byte i = 0; i < WebBrowser.MaxRetries && htmlDocument == null; i++) { htmlDocument = await WebBrowser.UrlGetToHtmlDocument(SteamCommunityURL + "/profiles/" + SteamID + "/gamecards/" + appID + "?l=english", Cookie).ConfigureAwait(false); @@ -489,6 +536,10 @@ namespace ArchiSteamFarm { return false; } + if (!await RefreshSessionIfNeeded().ConfigureAwait(false)) { + return false; + } + HttpResponseMessage response = null; for (byte i = 0; i < WebBrowser.MaxRetries && response == null; i++) { response = await WebBrowser.UrlGet(SteamCommunityURL + "/profiles/" + SteamID + "/inventory", Cookie).ConfigureAwait(false); @@ -507,6 +558,10 @@ namespace ArchiSteamFarm { return false; } + if (!await RefreshSessionIfNeeded().ConfigureAwait(false)) { + return false; + } + string sessionID; if (!Cookie.TryGetValue("sessionid", out sessionID)) { return false; @@ -530,9 +585,9 @@ namespace ArchiSteamFarm { return true; } - private async Task UnlockParentalAccount(string parentalPin) { + private async Task UnlockParentalAccount(string parentalPin) { if (string.IsNullOrEmpty(parentalPin) || parentalPin.Equals("0")) { - return; + return true; } Logging.LogGenericInfo("Unlocking parental account...", Bot.BotName); @@ -550,13 +605,13 @@ namespace ArchiSteamFarm { if (response == null) { Logging.LogGenericWTF("Request failed even after " + WebBrowser.MaxRetries + " tries", Bot.BotName); - return; + return false; } IEnumerable setCookieValues; if (!response.Headers.TryGetValues("Set-Cookie", out setCookieValues)) { Logging.LogNullError("setCookieValues", Bot.BotName); - return; + return false; } foreach (string setCookieValue in setCookieValues) { @@ -573,10 +628,11 @@ namespace ArchiSteamFarm { Cookie["steamparental"] = setCookie; Logging.LogGenericInfo("Success!", Bot.BotName); - return; + return true; } Logging.LogGenericWarning("Failed to unlock parental account!", Bot.BotName); + return false; } } } diff --git a/ArchiSteamFarm/Bot.cs b/ArchiSteamFarm/Bot.cs index 41764b68e..04629bfdb 100755 --- a/ArchiSteamFarm/Bot.cs +++ b/ArchiSteamFarm/Bot.cs @@ -187,6 +187,7 @@ namespace ArchiSteamFarm { CallbackManager.Subscribe(OnLoggedOn); CallbackManager.Subscribe(OnLoginKey); CallbackManager.Subscribe(OnMachineAuth); + CallbackManager.Subscribe(OnWebAPIUserNonce); CallbackManager.Subscribe(OnNotifications); CallbackManager.Subscribe(OnOfflineMessage); @@ -261,12 +262,23 @@ namespace ArchiSteamFarm { } } - internal async Task RestartIfRunning() { + internal async Task RefreshSession() { if (!SteamClient.IsConnected) { - return; + return false; } - await Start().ConfigureAwait(false); + var userNonce = await SteamUser.RequestWebAPIUserNonce(); + if (userNonce == null || userNonce.Result != EResult.OK || string.IsNullOrEmpty(userNonce.Nonce)) { + Start().Forget(); + return false; + } + + if (!ArchiWebHandler.Init(SteamClient, userNonce.Nonce, BotConfig.SteamParentalPIN)) { + Start().Forget(); + return false; + } + + return true; } internal async Task OnFarmingFinished(bool farmedSomething) { @@ -1300,7 +1312,6 @@ namespace ArchiSteamFarm { } Logging.LogGenericInfo("Disconnected from Steam!", BotName); - ArchiWebHandler.OnDisconnected(); CardsFarmer.StopFarming().Forget(); // If we initiated disconnect, do not attempt to reconnect @@ -1354,15 +1365,6 @@ namespace ArchiSteamFarm { return; } - for (byte i = 0; i < WebBrowser.MaxRetries && !ArchiWebHandler.IsInitialized; i++) { - await Utilities.SleepAsync(1000).ConfigureAwait(false); - } - - if (!ArchiWebHandler.IsInitialized) { - Logging.LogGenericWarning("Reached timeout while waiting for ArchiWebHandler to initialize!"); - return; - } - bool acceptedSomething = false; foreach (KeyValue guestPass in callback.GuestPasses) { ulong gid = guestPass["gid"].AsUnsignedLong(); @@ -1538,9 +1540,10 @@ namespace ArchiSteamFarm { BotConfig.SteamParentalPIN = Program.GetUserInput(Program.EUserInputType.SteamParentalPIN, BotName); } - if (!await ArchiWebHandler.Init(SteamClient, callback.WebAPIUserNonce, BotConfig.SteamParentalPIN).ConfigureAwait(false)) { - await RestartIfRunning().ConfigureAwait(false); - return; + if (!ArchiWebHandler.Init(SteamClient, callback.WebAPIUserNonce, BotConfig.SteamParentalPIN)) { + if (!await RefreshSession().ConfigureAwait(false)) { + return; + } } if (BotConfig.DismissInventoryNotifications) { @@ -1624,6 +1627,12 @@ namespace ArchiSteamFarm { } } + private void OnWebAPIUserNonce(SteamUser.WebAPIUserNonceCallback callback) { + if (callback == null) { + return; + } + } + private async void OnNotifications(ArchiHandler.NotificationsCallback callback) { if (callback == null || callback.Notifications == null) { return; diff --git a/ArchiSteamFarm/CardsFarmer.cs b/ArchiSteamFarm/CardsFarmer.cs index 5cbf0d7e9..a133a121c 100755 --- a/ArchiSteamFarm/CardsFarmer.cs +++ b/ArchiSteamFarm/CardsFarmer.cs @@ -211,10 +211,6 @@ namespace ArchiSteamFarm { return true; } - if (await Bot.ArchiWebHandler.ReconnectIfNeeded().ConfigureAwait(false)) { - return false; - } - Logging.LogGenericInfo("Checking badges...", Bot.BotName); // Find the number of badge pages @@ -362,7 +358,6 @@ namespace ArchiSteamFarm { HtmlNode htmlNode = htmlDocument.DocumentNode.SelectSingleNode("//span[@class='progress_info_bold']"); if (htmlNode == null) { - await Bot.ArchiWebHandler.ReconnectIfNeeded().ConfigureAwait(false); return null; } diff --git a/ArchiSteamFarm/GlobalConfig.cs b/ArchiSteamFarm/GlobalConfig.cs index fc5d2a11a..d8c3f5577 100644 --- a/ArchiSteamFarm/GlobalConfig.cs +++ b/ArchiSteamFarm/GlobalConfig.cs @@ -36,9 +36,10 @@ namespace ArchiSteamFarm { Experimental } + internal const byte DefaultHttpTimeout = 60; + private const byte DefaultMaxFarmingTime = 10; private const byte DefaultFarmingDelay = 5; - private const byte DefaultHttpTimeout = 60; private const ushort DefaultWCFPort = 1242; private const ProtocolType DefaultSteamProtocol = ProtocolType.Tcp; diff --git a/ArchiSteamFarm/WebBrowser.cs b/ArchiSteamFarm/WebBrowser.cs index b9d51fffe..ce3838a2f 100644 --- a/ArchiSteamFarm/WebBrowser.cs +++ b/ArchiSteamFarm/WebBrowser.cs @@ -224,7 +224,7 @@ namespace ArchiSteamFarm { if (!responseMessage.IsSuccessStatusCode) { if (Debugging.IsDebugBuild || Program.GlobalConfig.Debug) { - Logging.LogGenericError("Request: " + request + "failed!"); + Logging.LogGenericError("Request: " + request + " failed!"); Logging.LogGenericError("Status code: " + responseMessage.StatusCode); Logging.LogGenericError("Content: " + Environment.NewLine + await responseMessage.Content.ReadAsStringAsync().ConfigureAwait(false)); }