From 8b44abb31f02554b163e67ab9f6e99f929e6fabe Mon Sep 17 00:00:00 2001 From: JustArchi Date: Sat, 12 May 2018 20:21:52 +0200 Subject: [PATCH] Add workaround for Steam profile bug This is the most ridiculous code I've ever written, God forgive me. --- ArchiSteamFarm/ArchiWebHandler.cs | 190 ++++++++++++------ .../Localization/Strings.Designer.cs | 9 + ArchiSteamFarm/Localization/Strings.resx | 4 + 3 files changed, 145 insertions(+), 58 deletions(-) diff --git a/ArchiSteamFarm/ArchiWebHandler.cs b/ArchiSteamFarm/ArchiWebHandler.cs index ad5da9221..a8bc167a5 100644 --- a/ArchiSteamFarm/ArchiWebHandler.cs +++ b/ArchiSteamFarm/ArchiWebHandler.cs @@ -1306,6 +1306,15 @@ namespace ArchiSteamFarm { } } + private bool IsProfileUri(Uri uri) { + if (uri == null) { + ASF.ArchiLogger.LogNullError(nameof(uri)); + return false; + } + + return uri.AbsolutePath.Equals(GetAbsoluteProfileURL()); + } + private static bool IsSessionExpiredUri(Uri uri) { if (uri == null) { ASF.ArchiLogger.LogNullError(nameof(uri)); @@ -1432,19 +1441,36 @@ namespace ArchiSteamFarm { return true; } - private async Task UnlockParentalAccountForService(string serviceURL, string parentalPin) { + private async Task UnlockParentalAccountForService(string serviceURL, string parentalPin, byte maxTries = WebBrowser.MaxTries) { if (string.IsNullOrEmpty(serviceURL) || string.IsNullOrEmpty(parentalPin)) { Bot.ArchiLogger.LogNullError(nameof(serviceURL) + " || " + nameof(parentalPin)); return false; } - // This request doesn't go through UrlPostRetryWithSession as we have no access to session refresh capability (this is in fact session initialization) const string request = "/parental/ajaxunlock"; + if (maxTries == 0) { + Bot.ArchiLogger.LogGenericWarning(string.Format(Strings.ErrorRequestFailedTooManyTimes, WebBrowser.MaxTries)); + Bot.ArchiLogger.LogGenericDebug(string.Format(Strings.ErrorFailingRequest, serviceURL + request)); + return false; + } + Dictionary data = new Dictionary(1) { { "pin", parentalPin } }; + // This request doesn't go through UrlPostRetryWithSession as we have no access to session refresh capability (this is in fact session initialization) WebBrowser.BasicResponse response = await WebLimitRequest(serviceURL, async () => await WebBrowser.UrlPost(serviceURL + request, data, serviceURL).ConfigureAwait(false)).ConfigureAwait(false); - return (response != null) && !IsSessionExpiredUri(response.FinalUri); + if ((response == null) || IsSessionExpiredUri(response.FinalUri)) { + // There is no session refresh capability at this stage + return false; + } + + // Under special brain-damaged circumstances, Steam might just return our own profile as a response to the request, for absolutely no reason whatsoever - just try again in this case + if (IsProfileUri(response.FinalUri)) { + Bot.ArchiLogger.LogGenericDebug(string.Format(Strings.WarningWorkaroundTriggered, nameof(IsProfileUri))); + return await UnlockParentalAccountForService(serviceURL, parentalPin, --maxTries).ConfigureAwait(false); + } + + return true; } private async Task UrlGetToHtmlDocumentWithSession(string host, string request, byte maxTries = WebBrowser.MaxTries) { @@ -1478,17 +1504,23 @@ namespace ArchiSteamFarm { return null; } - if (!IsSessionExpiredUri(response.FinalUri)) { - return response.Content; - } + if (IsSessionExpiredUri(response.FinalUri)) { + if (await RefreshSession(host).ConfigureAwait(false)) { + return await UrlGetToHtmlDocumentWithSession(host, request, --maxTries).ConfigureAwait(false); + } - if (!await RefreshSession(host).ConfigureAwait(false)) { Bot.ArchiLogger.LogGenericWarning(Strings.WarningFailed); Bot.ArchiLogger.LogGenericDebug(string.Format(Strings.ErrorFailingRequest, host + request)); return null; } - return await UrlGetToHtmlDocumentWithSession(host, request, --maxTries).ConfigureAwait(false); + // Under special brain-damaged circumstances, Steam might just return our own profile as a response to the request, for absolutely no reason whatsoever - just try again in this case + if (IsProfileUri(response.FinalUri)) { + Bot.ArchiLogger.LogGenericDebug(string.Format(Strings.WarningWorkaroundTriggered, nameof(IsProfileUri))); + return await UrlGetToHtmlDocumentWithSession(host, request, --maxTries).ConfigureAwait(false); + } + + return response.Content; } private async Task UrlGetToJsonObjectWithSession(string host, string request, byte maxTries = WebBrowser.MaxTries) where T : class { @@ -1522,17 +1554,23 @@ namespace ArchiSteamFarm { return default; } - if (!IsSessionExpiredUri(response.FinalUri)) { - return response.Content; - } + if (IsSessionExpiredUri(response.FinalUri)) { + if (await RefreshSession(host).ConfigureAwait(false)) { + return await UrlGetToJsonObjectWithSession(host, request, --maxTries).ConfigureAwait(false); + } - if (!await RefreshSession(host).ConfigureAwait(false)) { Bot.ArchiLogger.LogGenericWarning(Strings.WarningFailed); Bot.ArchiLogger.LogGenericDebug(string.Format(Strings.ErrorFailingRequest, host + request)); - return default; + return null; } - return await UrlGetToJsonObjectWithSession(host, request, --maxTries).ConfigureAwait(false); + // Under special brain-damaged circumstances, Steam might just return our own profile as a response to the request, for absolutely no reason whatsoever - just try again in this case + if (IsProfileUri(response.FinalUri)) { + Bot.ArchiLogger.LogGenericDebug(string.Format(Strings.WarningWorkaroundTriggered, nameof(IsProfileUri))); + return await UrlGetToJsonObjectWithSession(host, request, --maxTries).ConfigureAwait(false); + } + + return response.Content; } private async Task UrlGetToXmlDocumentWithSession(string host, string request, byte maxTries = WebBrowser.MaxTries) { @@ -1566,17 +1604,23 @@ namespace ArchiSteamFarm { return null; } - if (!IsSessionExpiredUri(response.FinalUri)) { - return response.Content; - } + if (IsSessionExpiredUri(response.FinalUri)) { + if (await RefreshSession(host).ConfigureAwait(false)) { + return await UrlGetToXmlDocumentWithSession(host, request, --maxTries).ConfigureAwait(false); + } - if (!await RefreshSession(host).ConfigureAwait(false)) { Bot.ArchiLogger.LogGenericWarning(Strings.WarningFailed); Bot.ArchiLogger.LogGenericDebug(string.Format(Strings.ErrorFailingRequest, host + request)); return null; } - return await UrlGetToXmlDocumentWithSession(host, request, --maxTries).ConfigureAwait(false); + // Under special brain-damaged circumstances, Steam might just return our own profile as a response to the request, for absolutely no reason whatsoever - just try again in this case + if (IsProfileUri(response.FinalUri)) { + Bot.ArchiLogger.LogGenericDebug(string.Format(Strings.WarningWorkaroundTriggered, nameof(IsProfileUri))); + return await UrlGetToXmlDocumentWithSession(host, request, --maxTries).ConfigureAwait(false); + } + + return response.Content; } private async Task UrlHeadWithSession(string host, string request, byte maxTries = WebBrowser.MaxTries) { @@ -1610,17 +1654,23 @@ namespace ArchiSteamFarm { return false; } - if (!IsSessionExpiredUri(response.FinalUri)) { - return true; - } + if (IsSessionExpiredUri(response.FinalUri)) { + if (await RefreshSession(host).ConfigureAwait(false)) { + return await UrlHeadWithSession(host, request, --maxTries).ConfigureAwait(false); + } - if (!await RefreshSession(host).ConfigureAwait(false)) { Bot.ArchiLogger.LogGenericWarning(Strings.WarningFailed); Bot.ArchiLogger.LogGenericDebug(string.Format(Strings.ErrorFailingRequest, host + request)); return false; } - return await UrlHeadWithSession(host, request, --maxTries).ConfigureAwait(false); + // Under special brain-damaged circumstances, Steam might just return our own profile as a response to the request, for absolutely no reason whatsoever - just try again in this case + if (IsProfileUri(response.FinalUri)) { + Bot.ArchiLogger.LogGenericDebug(string.Format(Strings.WarningWorkaroundTriggered, nameof(IsProfileUri))); + return await UrlHeadWithSession(host, request, --maxTries).ConfigureAwait(false); + } + + return true; } private async Task UrlPostToHtmlDocumentWithSession(string host, string request, Dictionary data = null, string referer = null, ESession session = ESession.Lowercase, byte maxTries = WebBrowser.MaxTries) { @@ -1683,29 +1733,35 @@ namespace ArchiSteamFarm { return null; } - if (!IsSessionExpiredUri(response.FinalUri)) { - return response.Content; - } + if (IsSessionExpiredUri(response.FinalUri)) { + if (await RefreshSession(host).ConfigureAwait(false)) { + return await UrlPostToHtmlDocumentWithSession(host, request, data, referer, session, --maxTries).ConfigureAwait(false); + } - if (!await RefreshSession(host).ConfigureAwait(false)) { Bot.ArchiLogger.LogGenericWarning(Strings.WarningFailed); Bot.ArchiLogger.LogGenericDebug(string.Format(Strings.ErrorFailingRequest, host + request)); return null; } - return await UrlPostToHtmlDocumentWithSession(host, request, data, referer, session, --maxTries).ConfigureAwait(false); + // Under special brain-damaged circumstances, Steam might just return our own profile as a response to the request, for absolutely no reason whatsoever - just try again in this case + if (IsProfileUri(response.FinalUri)) { + Bot.ArchiLogger.LogGenericDebug(string.Format(Strings.WarningWorkaroundTriggered, nameof(IsProfileUri))); + return await UrlPostToHtmlDocumentWithSession(host, request, data, referer, session, --maxTries).ConfigureAwait(false); + } + + return response.Content; } private async Task UrlPostToJsonObjectWithSession(string host, string request, Dictionary data = null, string referer = null, ESession session = ESession.Lowercase, byte maxTries = WebBrowser.MaxTries) where T : class { if (string.IsNullOrEmpty(host) || string.IsNullOrEmpty(request)) { Bot.ArchiLogger.LogNullError(nameof(host) + " || " + nameof(request)); - return default; + return null; } if (maxTries == 0) { Bot.ArchiLogger.LogGenericWarning(string.Format(Strings.ErrorRequestFailedTooManyTimes, WebBrowser.MaxTries)); Bot.ArchiLogger.LogGenericDebug(string.Format(Strings.ErrorFailingRequest, host + request)); - return default; + return null; } // If session refresh is already in progress, wait for it @@ -1719,7 +1775,7 @@ namespace ArchiSteamFarm { if (SteamID == 0) { Bot.ArchiLogger.LogGenericWarning(Strings.WarningFailed); Bot.ArchiLogger.LogGenericDebug(string.Format(Strings.ErrorFailingRequest, host + request)); - return default; + return null; } if (session != ESession.None) { @@ -1727,7 +1783,7 @@ namespace ArchiSteamFarm { if (string.IsNullOrEmpty(sessionID)) { Bot.ArchiLogger.LogNullError(nameof(sessionID)); - return default; + return null; } string sessionName; @@ -1741,7 +1797,7 @@ namespace ArchiSteamFarm { break; default: Bot.ArchiLogger.LogGenericError(string.Format(Strings.WarningUnknownValuePleaseReport, nameof(session), session)); - return default; + return null; } if (data != null) { @@ -1753,32 +1809,38 @@ namespace ArchiSteamFarm { WebBrowser.ObjectResponse response = await WebLimitRequest(host, async () => await WebBrowser.UrlPostToJsonObject(host + request, data, referer).ConfigureAwait(false)).ConfigureAwait(false); if (response == null) { - return default; + return null; } - if (!IsSessionExpiredUri(response.FinalUri)) { - return response.Content; - } + if (IsSessionExpiredUri(response.FinalUri)) { + if (await RefreshSession(host).ConfigureAwait(false)) { + return await UrlPostToJsonObjectWithSession(host, request, data, referer, session, --maxTries).ConfigureAwait(false); + } - if (!await RefreshSession(host).ConfigureAwait(false)) { Bot.ArchiLogger.LogGenericWarning(Strings.WarningFailed); Bot.ArchiLogger.LogGenericDebug(string.Format(Strings.ErrorFailingRequest, host + request)); - return default; + return null; } - return await UrlPostToJsonObjectWithSession(host, request, data, referer, session, --maxTries).ConfigureAwait(false); + // Under special brain-damaged circumstances, Steam might just return our own profile as a response to the request, for absolutely no reason whatsoever - just try again in this case + if (IsProfileUri(response.FinalUri)) { + Bot.ArchiLogger.LogGenericDebug(string.Format(Strings.WarningWorkaroundTriggered, nameof(IsProfileUri))); + return await UrlPostToJsonObjectWithSession(host, request, data, referer, session, --maxTries).ConfigureAwait(false); + } + + return response.Content; } private async Task UrlPostToJsonObjectWithSession(string host, string request, List> data = null, string referer = null, ESession session = ESession.Lowercase, byte maxTries = WebBrowser.MaxTries) where T : class { if (string.IsNullOrEmpty(host) || string.IsNullOrEmpty(request)) { Bot.ArchiLogger.LogNullError(nameof(host) + " || " + nameof(request)); - return default; + return null; } if (maxTries == 0) { Bot.ArchiLogger.LogGenericWarning(string.Format(Strings.ErrorRequestFailedTooManyTimes, WebBrowser.MaxTries)); Bot.ArchiLogger.LogGenericDebug(string.Format(Strings.ErrorFailingRequest, host + request)); - return default; + return null; } // If session refresh is already in progress, wait for it @@ -1792,7 +1854,7 @@ namespace ArchiSteamFarm { if (SteamID == 0) { Bot.ArchiLogger.LogGenericWarning(Strings.WarningFailed); Bot.ArchiLogger.LogGenericDebug(string.Format(Strings.ErrorFailingRequest, host + request)); - return default; + return null; } if (session != ESession.None) { @@ -1800,7 +1862,7 @@ namespace ArchiSteamFarm { if (string.IsNullOrEmpty(sessionID)) { Bot.ArchiLogger.LogNullError(nameof(sessionID)); - return default; + return null; } string sessionName; @@ -1814,7 +1876,7 @@ namespace ArchiSteamFarm { break; default: Bot.ArchiLogger.LogGenericError(string.Format(Strings.WarningUnknownValuePleaseReport, nameof(session), session)); - return default; + return null; } KeyValuePair sessionValue = new KeyValuePair(sessionName, sessionID); @@ -1829,20 +1891,26 @@ namespace ArchiSteamFarm { WebBrowser.ObjectResponse response = await WebLimitRequest(host, async () => await WebBrowser.UrlPostToJsonObject(host + request, data, referer).ConfigureAwait(false)).ConfigureAwait(false); if (response == null) { - return default; + return null; } - if (!IsSessionExpiredUri(response.FinalUri)) { - return response.Content; - } + if (IsSessionExpiredUri(response.FinalUri)) { + if (await RefreshSession(host).ConfigureAwait(false)) { + return await UrlPostToJsonObjectWithSession(host, request, data, referer, session, --maxTries).ConfigureAwait(false); + } - if (!await RefreshSession(host).ConfigureAwait(false)) { Bot.ArchiLogger.LogGenericWarning(Strings.WarningFailed); Bot.ArchiLogger.LogGenericDebug(string.Format(Strings.ErrorFailingRequest, host + request)); - return default; + return null; } - return await UrlPostToJsonObjectWithSession(host, request, data, referer, session, --maxTries).ConfigureAwait(false); + // Under special brain-damaged circumstances, Steam might just return our own profile as a response to the request, for absolutely no reason whatsoever - just try again in this case + if (IsProfileUri(response.FinalUri)) { + Bot.ArchiLogger.LogGenericDebug(string.Format(Strings.WarningWorkaroundTriggered, nameof(IsProfileUri))); + return await UrlPostToJsonObjectWithSession(host, request, data, referer, session, --maxTries).ConfigureAwait(false); + } + + return response.Content; } private async Task UrlPostWithSession(string host, string request, Dictionary data = null, string referer = null, ESession session = ESession.Lowercase, byte maxTries = WebBrowser.MaxTries) { @@ -1905,17 +1973,23 @@ namespace ArchiSteamFarm { return false; } - if (!IsSessionExpiredUri(response.FinalUri)) { - return true; - } + if (IsSessionExpiredUri(response.FinalUri)) { + if (await RefreshSession(host).ConfigureAwait(false)) { + return await UrlPostWithSession(host, request, data, referer, session, --maxTries).ConfigureAwait(false); + } - if (!await RefreshSession(host).ConfigureAwait(false)) { Bot.ArchiLogger.LogGenericWarning(Strings.WarningFailed); Bot.ArchiLogger.LogGenericDebug(string.Format(Strings.ErrorFailingRequest, host + request)); return false; } - return await UrlPostWithSession(host, request, data, referer, session, --maxTries).ConfigureAwait(false); + // Under special brain-damaged circumstances, Steam might just return our own profile as a response to the request, for absolutely no reason whatsoever - just try again in this case + if (IsProfileUri(response.FinalUri)) { + Bot.ArchiLogger.LogGenericDebug(string.Format(Strings.WarningWorkaroundTriggered, nameof(IsProfileUri))); + return await UrlPostWithSession(host, request, data, referer, session, --maxTries).ConfigureAwait(false); + } + + return true; } private static async Task WebLimitRequest(string service, Func> function) { diff --git a/ArchiSteamFarm/Localization/Strings.Designer.cs b/ArchiSteamFarm/Localization/Strings.Designer.cs index 0bd277417..1f2f25442 100644 --- a/ArchiSteamFarm/Localization/Strings.Designer.cs +++ b/ArchiSteamFarm/Localization/Strings.Designer.cs @@ -1467,6 +1467,15 @@ namespace ArchiSteamFarm.Localization { } } + /// + /// Wyszukuje zlokalizowany ciąg podobny do ciągu Workaround for {0} bug has been triggered.. + /// + internal static string WarningWorkaroundTriggered { + get { + return ResourceManager.GetString("WarningWorkaroundTriggered", resourceCulture); + } + } + /// /// Wyszukuje zlokalizowany ciąg podobny do ciągu It looks like it's your first launch of the program, welcome!. /// diff --git a/ArchiSteamFarm/Localization/Strings.resx b/ArchiSteamFarm/Localization/Strings.resx index 3f8ebac13..6f8941e89 100644 --- a/ArchiSteamFarm/Localization/Strings.resx +++ b/ArchiSteamFarm/Localization/Strings.resx @@ -665,4 +665,8 @@ StackTrace: Accepted donation trade: {0} {0} will be replaced by trade's ID (number) + + Workaround for {0} bug has been triggered. + {0} will be replaced by the bug's name provided by ASF + \ No newline at end of file