This commit is contained in:
Łukasz Domeradzki
2024-03-27 19:55:07 +01:00
parent 0ae4b6ff18
commit 9a02c79e0a
7 changed files with 70 additions and 37 deletions

View File

@@ -29,7 +29,6 @@ using System.Globalization;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Net; using System.Net;
using System.Net.Http;
using System.Text.Json; using System.Text.Json;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
@@ -253,7 +252,7 @@ internal sealed class RemoteCommunication : IAsyncDisposable, IDisposable {
try { try {
inventory = await Bot.ArchiHandler.GetMyInventoryAsync().ToListAsync().ConfigureAwait(false); inventory = await Bot.ArchiHandler.GetMyInventoryAsync().ToListAsync().ConfigureAwait(false);
} catch (HttpRequestException e) { } catch (TimeoutException e) {
// This is actually a network failure, so we'll stop sending heartbeats but not record it as valid check // This is actually a network failure, so we'll stop sending heartbeats but not record it as valid check
ShouldSendHeartBeats = false; ShouldSendHeartBeats = false;
@@ -940,7 +939,7 @@ internal sealed class RemoteCommunication : IAsyncDisposable, IDisposable {
try { try {
assetsForMatching = await Bot.ArchiHandler.GetMyInventoryAsync().Where(item => item is { AssetID: > 0, Amount: > 0, ClassID: > 0, RealAppID: > 0, Type: > EAssetType.Unknown, Rarity: > EAssetRarity.Unknown, IsSteamPointsShopItem: false } && acceptedMatchableTypes.Contains(item.Type) && !Bot.BotDatabase.MatchActivelyBlacklistAppIDs.Contains(item.RealAppID)).ToHashSetAsync().ConfigureAwait(false); assetsForMatching = await Bot.ArchiHandler.GetMyInventoryAsync().Where(item => item is { AssetID: > 0, Amount: > 0, ClassID: > 0, RealAppID: > 0, Type: > EAssetType.Unknown, Rarity: > EAssetRarity.Unknown, IsSteamPointsShopItem: false } && acceptedMatchableTypes.Contains(item.Type) && !Bot.BotDatabase.MatchActivelyBlacklistAppIDs.Contains(item.RealAppID)).ToHashSetAsync().ConfigureAwait(false);
} catch (HttpRequestException e) { } catch (TimeoutException e) {
Bot.ArchiLogger.LogGenericWarningException(e); Bot.ArchiLogger.LogGenericWarningException(e);
Bot.ArchiLogger.LogGenericWarning(string.Format(CultureInfo.CurrentCulture, Strings.WarningFailedWithError, nameof(assetsForMatching))); Bot.ArchiLogger.LogGenericWarning(string.Format(CultureInfo.CurrentCulture, Strings.WarningFailedWithError, nameof(assetsForMatching)));

View File

@@ -33,7 +33,6 @@ using System.ComponentModel.DataAnnotations;
using System.Globalization; using System.Globalization;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Net.Http;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using System.Threading; using System.Threading;
@@ -3669,7 +3668,7 @@ public sealed class Bot : IAsyncDisposable, IDisposable {
.Where(item => appIDs.Contains(item.RealAppID) && BotConfig.CompleteTypesToSend.Contains(item.Type)) .Where(item => appIDs.Contains(item.RealAppID) && BotConfig.CompleteTypesToSend.Contains(item.Type))
.ToHashSetAsync() .ToHashSetAsync()
.ConfigureAwait(false); .ConfigureAwait(false);
} catch (HttpRequestException e) { } catch (TimeoutException e) {
ArchiLogger.LogGenericWarningException(e); ArchiLogger.LogGenericWarningException(e);
return; return;

View File

@@ -25,7 +25,6 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization; using System.Globalization;
using System.Linq; using System.Linq;
using System.Net.Http;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using ArchiSteamFarm.Collections; using ArchiSteamFarm.Collections;
@@ -499,7 +498,7 @@ public sealed class Trading : IDisposable {
try { try {
inventory = await Bot.ArchiHandler.GetMyInventoryAsync().Where(item => !item.IsSteamPointsShopItem && wantedSets.Contains((item.RealAppID, item.Type, item.Rarity))).ToHashSetAsync().ConfigureAwait(false); inventory = await Bot.ArchiHandler.GetMyInventoryAsync().Where(item => !item.IsSteamPointsShopItem && wantedSets.Contains((item.RealAppID, item.Type, item.Rarity))).ToHashSetAsync().ConfigureAwait(false);
} catch (HttpRequestException e) { } catch (TimeoutException e) {
// If we can't check our inventory when not using MatchEverything, this is a temporary failure, try again later // If we can't check our inventory when not using MatchEverything, this is a temporary failure, try again later
Bot.ArchiLogger.LogGenericWarningException(e); Bot.ArchiLogger.LogGenericWarningException(e);
Bot.ArchiLogger.LogGenericDebug(string.Format(CultureInfo.CurrentCulture, Strings.BotTradeOfferResult, tradeOffer.TradeOfferID, ParseTradeResult.EResult.TryAgain, nameof(inventory))); Bot.ArchiLogger.LogGenericDebug(string.Format(CultureInfo.CurrentCulture, Strings.BotTradeOfferResult, tradeOffer.TradeOfferID, ParseTradeResult.EResult.TryAgain, nameof(inventory)));

View File

@@ -26,7 +26,6 @@ using System.Collections.Generic;
using System.ComponentModel; using System.ComponentModel;
using System.Globalization; using System.Globalization;
using System.Linq; using System.Linq;
using System.Net.Http;
using System.Threading.Tasks; using System.Threading.Tasks;
using ArchiSteamFarm.Core; using ArchiSteamFarm.Core;
using ArchiSteamFarm.Localization; using ArchiSteamFarm.Localization;
@@ -34,6 +33,7 @@ using ArchiSteamFarm.NLog;
using ArchiSteamFarm.Steam.Data; using ArchiSteamFarm.Steam.Data;
using ArchiSteamFarm.Steam.Integration.Callbacks; using ArchiSteamFarm.Steam.Integration.Callbacks;
using ArchiSteamFarm.Steam.Integration.CMsgs; using ArchiSteamFarm.Steam.Integration.CMsgs;
using ArchiSteamFarm.Web;
using JetBrains.Annotations; using JetBrains.Annotations;
using SteamKit2; using SteamKit2;
using SteamKit2.Internal; using SteamKit2.Internal;
@@ -179,14 +179,15 @@ public sealed class ArchiHandler : ClientMsgHandler {
ArgumentOutOfRangeException.ThrowIfZero(contextID); ArgumentOutOfRangeException.ThrowIfZero(contextID);
ArgumentOutOfRangeException.ThrowIfZero(itemsCountPerRequest); ArgumentOutOfRangeException.ThrowIfZero(itemsCountPerRequest);
if (Client.SteamID == null) { if (!Client.IsConnected || (Client.SteamID == null)) {
throw new InvalidOperationException(nameof(Client.SteamID)); throw new TimeoutException(string.Format(CultureInfo.CurrentCulture, Strings.WarningFailedWithError, nameof(Client.IsConnected)));
} }
// We need to store asset IDs to make sure we won't get duplicate items ulong steamID = Client.SteamID;
HashSet<ulong>? assetIDs = null;
Dictionary<(ulong ClassID, ulong InstanceID), InventoryDescription>? descriptions = null; if (steamID == 0) {
throw new InvalidOperationException(nameof(Client.SteamID));
}
CEcon_GetInventoryItemsWithDescriptions_Request request = new() { CEcon_GetInventoryItemsWithDescriptions_Request request = new() {
appid = appID, appid = appID,
@@ -198,23 +199,58 @@ public sealed class ArchiHandler : ClientMsgHandler {
}, },
get_descriptions = true, get_descriptions = true,
steamid = Client.SteamID, steamid = steamID,
count = itemsCountPerRequest count = itemsCountPerRequest
}; };
// We need to store asset IDs to make sure we won't get duplicate items
HashSet<ulong>? assetIDs = null;
Dictionary<(ulong ClassID, ulong InstanceID), InventoryDescription>? descriptions = null;
while (true) { while (true) {
SteamUnifiedMessages.ServiceMethodResponse serviceMethodResponse; SteamUnifiedMessages.ServiceMethodResponse? serviceMethodResponse = null;
try { for (byte i = 0; (i < WebBrowser.MaxTries) && (serviceMethodResponse?.Result != EResult.OK) && Client.IsConnected && (Client.SteamID != null); i++) {
serviceMethodResponse = await UnifiedEconService.SendMessage(x => x.GetInventoryItemsWithDescriptions(request)).ToLongRunningTask().ConfigureAwait(false); if (i > 0) {
} catch (Exception e) { // It seems 2 seconds is enough to win over DuplicateRequest, so we'll use that for this and also other network-related failures
ArchiLogger.LogGenericWarningException(e); await Task.Delay(2000).ConfigureAwait(false);
}
throw new HttpRequestException(string.Format(CultureInfo.CurrentCulture, Strings.ErrorObjectIsNull, nameof(serviceMethodResponse))); try {
serviceMethodResponse = await UnifiedEconService.SendMessage(x => x.GetInventoryItemsWithDescriptions(request)).ToLongRunningTask().ConfigureAwait(false);
} catch (Exception e) {
ArchiLogger.LogGenericWarningException(e);
continue;
}
// Interpret the result and see what we should do about it
switch (serviceMethodResponse.Result) {
case EResult.Busy:
case EResult.DuplicateRequest:
case EResult.ServiceUnavailable:
// Those are generic failures that we should be able to retry
ArchiLogger.LogGenericDebug(string.Format(CultureInfo.CurrentCulture, Strings.WarningFailedWithError, serviceMethodResponse.Result));
continue;
case EResult.OK:
// Success, we can continue
break;
default:
// Unknown failures, report them and do not retry since we're unsure if we should
ArchiLogger.LogGenericError(string.Format(CultureInfo.CurrentCulture, Strings.WarningUnknownValuePleaseReport, nameof(serviceMethodResponse.Result), serviceMethodResponse.Result));
throw new TimeoutException(string.Format(CultureInfo.CurrentCulture, Strings.WarningFailedWithError, serviceMethodResponse.Result));
}
}
if (serviceMethodResponse == null) {
throw new TimeoutException(string.Format(CultureInfo.CurrentCulture, Strings.ErrorObjectIsNull, nameof(serviceMethodResponse)));
} }
if (serviceMethodResponse.Result != EResult.OK) { if (serviceMethodResponse.Result != EResult.OK) {
throw new HttpRequestException(string.Format(CultureInfo.CurrentCulture, Strings.WarningFailedWithError, serviceMethodResponse.Result)); throw new TimeoutException(string.Format(CultureInfo.CurrentCulture, Strings.WarningFailedWithError, serviceMethodResponse.Result));
} }
CEcon_GetInventoryItemsWithDescriptions_Response response = serviceMethodResponse.GetDeserializedResponse<CEcon_GetInventoryItemsWithDescriptions_Response>(); CEcon_GetInventoryItemsWithDescriptions_Response response = serviceMethodResponse.GetDeserializedResponse<CEcon_GetInventoryItemsWithDescriptions_Response>();

View File

@@ -275,7 +275,7 @@ public sealed class ArchiWebHandler : IDisposable {
ObjectResponse<InventoryResponse>? response = null; ObjectResponse<InventoryResponse>? response = null;
try { try {
for (byte i = 0; (i < WebBrowser.MaxTries) && (response == null); i++) { for (byte i = 0; (i < WebBrowser.MaxTries) && (response?.StatusCode.IsSuccessCode() != true); i++) {
if ((i > 0) && (rateLimitingDelay > 0)) { if ((i > 0) && (rateLimitingDelay > 0)) {
await Task.Delay(rateLimitingDelay).ConfigureAwait(false); await Task.Delay(rateLimitingDelay).ConfigureAwait(false);
} }
@@ -293,22 +293,24 @@ public sealed class ArchiWebHandler : IDisposable {
if (response.StatusCode.IsServerErrorCode()) { if (response.StatusCode.IsServerErrorCode()) {
if (string.IsNullOrEmpty(response.Content?.ErrorText)) { if (string.IsNullOrEmpty(response.Content?.ErrorText)) {
// This is a generic server error without a reason, try again // This is a generic server error without a reason, try again
response = null;
continue; continue;
} }
// Interpret the reason and see if we should try again Bot.ArchiLogger.LogGenericDebug(string.Format(CultureInfo.CurrentCulture, Strings.WarningFailedWithError, response.Content.ErrorText));
// Try to interpret the failure reason and see if we should try again
switch (response.Content.ErrorCode) { switch (response.Content.ErrorCode) {
case EResult.Busy:
case EResult.DuplicateRequest: case EResult.DuplicateRequest:
case EResult.ServiceUnavailable: case EResult.ServiceUnavailable:
response = null; // Those are generic failures that we should be able to retry
continue; continue;
} default:
// Unknown failures, report them and do not retry since we're unsure if we should
Bot.ArchiLogger.LogGenericError(string.Format(CultureInfo.CurrentCulture, Strings.WarningUnknownValuePleaseReport, nameof(response.Content.ErrorText), response.Content.ErrorText));
// This is actually client error with a reason, so it doesn't make sense to retry throw new HttpRequestException(string.Format(CultureInfo.CurrentCulture, Strings.WarningFailedWithError, response.Content.ErrorText), null, response.StatusCode);
throw new HttpRequestException(string.Format(CultureInfo.CurrentCulture, Strings.WarningFailedWithError, response.Content.ErrorText), null, response.StatusCode); }
} }
} }
} finally { } finally {
@@ -324,12 +326,12 @@ public sealed class ArchiWebHandler : IDisposable {
} }
} }
if (response?.Content == null) { if (response == null) {
throw new HttpRequestException(string.Format(CultureInfo.CurrentCulture, Strings.ErrorObjectIsNull, nameof(response))); throw new HttpRequestException(string.Format(CultureInfo.CurrentCulture, Strings.ErrorObjectIsNull, nameof(response)));
} }
if (response.Content.Result is not EResult.OK) { if ((response.Content == null) || (response.StatusCode.IsSuccessCode() != true)) {
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); throw new HttpRequestException(string.Format(CultureInfo.CurrentCulture, Strings.WarningFailedWithError, !string.IsNullOrEmpty(response.Content?.ErrorText) ? response.Content.ErrorText : response.Content?.Result.HasValue == true ? response.Content.Result : response.StatusCode));
} }
if ((response.Content.TotalInventoryCount == 0) || (response.Content.Assets.Count == 0)) { if ((response.Content.TotalInventoryCount == 0) || (response.Content.Assets.Count == 0)) {

View File

@@ -27,7 +27,6 @@ using System.Collections.Immutable;
using System.ComponentModel; using System.ComponentModel;
using System.Globalization; using System.Globalization;
using System.Linq; using System.Linq;
using System.Net.Http;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using ArchiSteamFarm.Collections; using ArchiSteamFarm.Collections;
@@ -430,7 +429,7 @@ public sealed class Actions : IAsyncDisposable, IDisposable {
} }
inventory = await Bot.ArchiHandler.GetMyInventoryAsync(appID, contextID, true).Where(item => filterFunction(item)).ToHashSetAsync().ConfigureAwait(false); inventory = await Bot.ArchiHandler.GetMyInventoryAsync(appID, contextID, true).Where(item => filterFunction(item)).ToHashSetAsync().ConfigureAwait(false);
} catch (HttpRequestException e) { } catch (TimeoutException e) {
Bot.ArchiLogger.LogGenericWarningException(e); Bot.ArchiLogger.LogGenericWarningException(e);
return (false, string.Format(CultureInfo.CurrentCulture, Strings.WarningFailedWithError, e.Message)); return (false, string.Format(CultureInfo.CurrentCulture, Strings.WarningFailedWithError, e.Message));

View File

@@ -26,7 +26,6 @@ using System.Collections.Generic;
using System.ComponentModel; using System.ComponentModel;
using System.Globalization; using System.Globalization;
using System.Linq; using System.Linq;
using System.Net.Http;
using System.Text; using System.Text;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using System.Threading.Tasks; using System.Threading.Tasks;
@@ -3114,7 +3113,7 @@ public sealed class Commands {
completeSuccess = false; completeSuccess = false;
} }
} }
} catch (HttpRequestException e) { } catch (TimeoutException e) {
Bot.ArchiLogger.LogGenericWarningException(e); Bot.ArchiLogger.LogGenericWarningException(e);
completeSuccess = false; completeSuccess = false;