From e6171456c3db23abba47e08f187cd61733f96c25 Mon Sep 17 00:00:00 2001 From: JustArchi Date: Sun, 27 Nov 2022 15:12:25 +0100 Subject: [PATCH] Retry inventory requests on error 29 --- .../Steam/Data/InventoryResponse.cs | 21 ++++++++-- .../Steam/Integration/ArchiWebHandler.cs | 15 +++++-- .../Steam/Integration/SteamUtilities.cs | 40 +++++++++++++++++++ 3 files changed, 70 insertions(+), 6 deletions(-) diff --git a/ArchiSteamFarm/Steam/Data/InventoryResponse.cs b/ArchiSteamFarm/Steam/Data/InventoryResponse.cs index 4de68070e..e6f126104 100644 --- a/ArchiSteamFarm/Steam/Data/InventoryResponse.cs +++ b/ArchiSteamFarm/Steam/Data/InventoryResponse.cs @@ -26,9 +26,11 @@ using System.Diagnostics.CodeAnalysis; using System.Globalization; using ArchiSteamFarm.Core; using ArchiSteamFarm.Localization; +using ArchiSteamFarm.Steam.Integration; using JetBrains.Annotations; using Newtonsoft.Json; using Newtonsoft.Json.Linq; +using SteamKit2; namespace ArchiSteamFarm.Steam.Data; @@ -40,15 +42,28 @@ internal sealed class InventoryResponse : OptionalResultResponse { [JsonProperty("descriptions", Required = Required.DisallowNull)] internal readonly ImmutableHashSet Descriptions = ImmutableHashSet.Empty; - [JsonProperty("error", Required = Required.DisallowNull)] - internal readonly string Error = ""; - [JsonProperty("total_inventory_count", Required = Required.DisallowNull)] internal readonly uint TotalInventoryCount; + internal EResult? ErrorCode { get; private set; } + internal string? ErrorText { get; private set; } internal ulong LastAssetID { get; private set; } internal bool MoreItems { get; private set; } + [JsonProperty("error", Required = Required.DisallowNull)] + private string Error { + set { + if (string.IsNullOrEmpty(value)) { + ASF.ArchiLogger.LogNullError(value); + + return; + } + + ErrorCode = SteamUtilities.InterpretError(value); + ErrorText = value; + } + } + [JsonProperty("last_assetid", Required = Required.DisallowNull)] private string LastAssetIDText { set { diff --git a/ArchiSteamFarm/Steam/Integration/ArchiWebHandler.cs b/ArchiSteamFarm/Steam/Integration/ArchiWebHandler.cs index 45580cd10..ca1947c58 100644 --- a/ArchiSteamFarm/Steam/Integration/ArchiWebHandler.cs +++ b/ArchiSteamFarm/Steam/Integration/ArchiWebHandler.cs @@ -188,16 +188,25 @@ public sealed class ArchiWebHandler : IDisposable { } if (response.StatusCode.IsServerErrorCode()) { - if (string.IsNullOrEmpty(response.Content?.Error)) { + if (string.IsNullOrEmpty(response.Content?.ErrorText)) { // This is a generic server error without a reason, try again response = null; continue; } + // Interpret the reason and see if we should try again + // ReSharper disable once RedundantSuppressNullableWarningExpression - required for .NET Framework + switch (response.Content!.ErrorCode) { + case EResult.DuplicateRequest: + response = null; + + continue; + } + // This is actually client error with a reason, so it doesn't make sense to retry // ReSharper disable once RedundantSuppressNullableWarningExpression - required for .NET Framework - throw new HttpRequestException(string.Format(CultureInfo.CurrentCulture, Strings.WarningFailedWithError, response.Content!.Error), null, response.StatusCode); + throw new HttpRequestException(string.Format(CultureInfo.CurrentCulture, Strings.WarningFailedWithError, response.Content!.ErrorText), null, response.StatusCode); } } } finally { @@ -218,7 +227,7 @@ public sealed class ArchiWebHandler : IDisposable { } if (response.Content.Result is not EResult.OK) { - throw new HttpRequestException(!string.IsNullOrEmpty(response.Content.Error) ? string.Format(CultureInfo.CurrentCulture, Strings.WarningFailedWithError, response.Content.Error) : response.Content.Result.HasValue ? string.Format(CultureInfo.CurrentCulture, Strings.WarningFailedWithError, response.Content.Result) : Strings.WarningFailed); + throw new HttpRequestException(!string.IsNullOrEmpty(response.Content.ErrorText) ? string.Format(CultureInfo.CurrentCulture, Strings.WarningFailedWithError, response.Content.ErrorText) : response.Content.Result.HasValue ? string.Format(CultureInfo.CurrentCulture, Strings.WarningFailedWithError, response.Content.Result) : Strings.WarningFailed); } if (response.Content.TotalInventoryCount == 0) { diff --git a/ArchiSteamFarm/Steam/Integration/SteamUtilities.cs b/ArchiSteamFarm/Steam/Integration/SteamUtilities.cs index 615010967..9003a7e50 100644 --- a/ArchiSteamFarm/Steam/Integration/SteamUtilities.cs +++ b/ArchiSteamFarm/Steam/Integration/SteamUtilities.cs @@ -21,12 +21,52 @@ using System; using System.Collections.Generic; +using System.Globalization; using ArchiSteamFarm.Core; +using ArchiSteamFarm.Localization; using SteamKit2; namespace ArchiSteamFarm.Steam.Integration; internal static class SteamUtilities { + internal static EResult? InterpretError(string errorText) { + if (string.IsNullOrEmpty(errorText)) { + throw new ArgumentNullException(nameof(errorText)); + } + + int startIndex = errorText.LastIndexOf('('); + + if (startIndex < 0) { + return null; + } + + startIndex++; + + int endIndex = errorText.IndexOf(')', startIndex + 1); + + if (endIndex < 0) { + return null; + } + + string errorCodeText = errorText[startIndex..endIndex]; + + if (!byte.TryParse(errorCodeText, out byte errorCode)) { + ASF.ArchiLogger.LogGenericError(string.Format(CultureInfo.CurrentCulture, Strings.WarningUnknownValuePleaseReport, nameof(errorCodeText), errorCodeText)); + + return null; + } + + EResult result = (EResult) errorCode; + + if (!Enum.IsDefined(result)) { + ASF.ArchiLogger.LogGenericError(string.Format(CultureInfo.CurrentCulture, Strings.WarningUnknownValuePleaseReport, nameof(EResult), result)); + + return null; + } + + return result; + } + internal static Dictionary? ParseItems(this SteamApps.PurchaseResponseCallback callback) { ArgumentNullException.ThrowIfNull(callback);