From 5dc083768d00b7335f77e97b37a0e03c1e6d0d3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Domeradzki?= Date: Mon, 15 Dec 2025 16:40:42 +0100 Subject: [PATCH] Fix GetAppList() after Valve killed it --- .../Steam/Integration/ArchiWebHandler.cs | 112 +++++++++++------- 1 file changed, 68 insertions(+), 44 deletions(-) diff --git a/ArchiSteamFarm/Steam/Integration/ArchiWebHandler.cs b/ArchiSteamFarm/Steam/Integration/ArchiWebHandler.cs index c7fd99b93..c59c0ccd3 100644 --- a/ArchiSteamFarm/Steam/Integration/ArchiWebHandler.cs +++ b/ArchiSteamFarm/Steam/Integration/ArchiWebHandler.cs @@ -58,7 +58,7 @@ public sealed class ArchiWebHandler : IDisposable { private const byte MaxTradeOfferMessageLength = 128; private const byte MinimumSessionValidityInSeconds = 10; private const byte SessionIDLength = 24; // For maximum compatibility, should be divisible by 2 and match the length of "sessionid" property that Steam uses across their websites - private const string SteamAppsService = "ISteamApps"; + private const string SteamStoreService = "IStoreService"; [PublicAPI] public static Uri SteamCheckoutURL => new("https://checkout.steampowered.com"); @@ -1640,62 +1640,86 @@ public sealed class ArchiWebHandler : IDisposable { } internal async Task?> GetAppList() { + string? accessToken = Bot.AccessToken; + + if (string.IsNullOrEmpty(accessToken)) { + return null; + } + const string endpoint = "GetAppList"; HttpMethod method = HttpMethod.Get; - using WebAPI.AsyncInterface steamAppsService = Bot.SteamConfiguration.GetAsyncWebAPIInterface(SteamAppsService); + // Extra entry for last_appid that will end up here for sure + Dictionary arguments = new(3, StringComparer.Ordinal) { + { "access_token", accessToken }, + { "max_results", 50000 } + }; + + HashSet? result = null; + + using WebAPI.AsyncInterface steamAppsService = Bot.SteamConfiguration.GetAsyncWebAPIInterface(SteamStoreService); steamAppsService.Timeout = WebBrowser.Timeout; - KeyValue? response = null; + while (true) { + KeyValue? response = null; - for (byte i = 0; (i < WebBrowser.MaxTries) && (response == null); i++) { - if ((i > 0) && (WebLimiterDelay > 0)) { - await Task.Delay(WebLimiterDelay).ConfigureAwait(false); + for (byte i = 0; (i < WebBrowser.MaxTries) && (response == null); i++) { + if ((i > 0) && (WebLimiterDelay > 0)) { + await Task.Delay(WebLimiterDelay).ConfigureAwait(false); + } + + if (Debugging.IsUserDebugging) { + Bot.ArchiLogger.LogGenericDebug($"{method} {Bot.SteamConfiguration.WebAPIBaseAddress}{SteamStoreService}/{endpoint}"); + } + + try { + response = await WebLimitRequest( + Bot.SteamConfiguration.WebAPIBaseAddress, + + // ReSharper disable once AccessToDisposedClosure + async () => await steamAppsService.CallAsync(method, endpoint, args: arguments).ConfigureAwait(false) + ).ConfigureAwait(false); + } catch (TaskCanceledException e) { + Bot.ArchiLogger.LogGenericDebuggingException(e); + } catch (Exception e) { + Bot.ArchiLogger.LogGenericWarningException(e); + } } - if (Debugging.IsUserDebugging) { - Bot.ArchiLogger.LogGenericDebug($"{method} {Bot.SteamConfiguration.WebAPIBaseAddress}{SteamAppsService}/{endpoint}"); - } - - try { - response = await WebLimitRequest( - Bot.SteamConfiguration.WebAPIBaseAddress, - - // ReSharper disable once AccessToDisposedClosure - async () => await steamAppsService.CallAsync(method, endpoint, 2).ConfigureAwait(false) - ).ConfigureAwait(false); - } catch (TaskCanceledException e) { - Bot.ArchiLogger.LogGenericDebuggingException(e); - } catch (Exception e) { - Bot.ArchiLogger.LogGenericWarningException(e); - } - } - - if (response == null) { - Bot.ArchiLogger.LogGenericWarning(Strings.FormatErrorRequestFailedTooManyTimes(WebBrowser.MaxTries)); - - return null; - } - - List apps = response["apps"].Children; - - if (apps.Count == 0) { - Bot.ArchiLogger.LogGenericWarning(Strings.FormatErrorIsEmpty(nameof(apps))); - - return null; - } - - HashSet result = new(apps.Count); - - foreach (uint appID in apps.Select(static app => app["appid"].AsUnsignedInteger())) { - if (appID == 0) { - Bot.ArchiLogger.LogNullError(appID); + if (response == null) { + Bot.ArchiLogger.LogGenericWarning(Strings.FormatErrorRequestFailedTooManyTimes(WebBrowser.MaxTries)); return null; } - result.Add(appID); + List apps = response["apps"].Children; + + if (apps.Count == 0) { + Bot.ArchiLogger.LogGenericWarning(Strings.FormatErrorIsEmpty(nameof(apps))); + + return null; + } + + result ??= new HashSet(apps.Count); + + foreach (uint appID in apps.Select(static app => app["appid"].AsUnsignedInteger())) { + if (appID == 0) { + Bot.ArchiLogger.LogNullError(appID); + + return null; + } + + result.Add(appID); + } + + uint lastAppID = response["last_appid"].AsUnsignedInteger(); + + if (lastAppID == 0) { + break; + } + + arguments["last_appid"] = lastAppID; } return result;