From 8e7d05ce5cf5ed463a7d42bb64f501a677dbaf1d Mon Sep 17 00:00:00 2001 From: Archi Date: Sat, 14 Jan 2023 23:41:25 +0100 Subject: [PATCH] Skip untradable items for MatchEverything bots --- .../Backend.cs | 10 +++-- .../RemoteCommunication.cs | 41 ++++++++++++------- 2 files changed, 33 insertions(+), 18 deletions(-) diff --git a/ArchiSteamFarm.OfficialPlugins.ItemsMatcher/Backend.cs b/ArchiSteamFarm.OfficialPlugins.ItemsMatcher/Backend.cs index 6437224ef..18e86c4a8 100644 --- a/ArchiSteamFarm.OfficialPlugins.ItemsMatcher/Backend.cs +++ b/ArchiSteamFarm.OfficialPlugins.ItemsMatcher/Backend.cs @@ -33,12 +33,16 @@ using ArchiSteamFarm.Steam.Storage; using ArchiSteamFarm.Storage; using ArchiSteamFarm.Web; using ArchiSteamFarm.Web.Responses; +using SteamKit2; namespace ArchiSteamFarm.OfficialPlugins.ItemsMatcher; internal static class Backend { - internal static async Task AnnounceForListing(Bot bot, WebBrowser webBrowser, IReadOnlyCollection inventory, IReadOnlyCollection acceptedMatchableTypes, string tradeToken, string? nickname = null, string? avatarHash = null) { - ArgumentNullException.ThrowIfNull(bot); + internal static async Task AnnounceForListing(ulong steamID, WebBrowser webBrowser, IReadOnlyCollection inventory, IReadOnlyCollection acceptedMatchableTypes, bool matchEverything, string tradeToken, string? nickname = null, string? avatarHash = null) { + if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) { + throw new ArgumentOutOfRangeException(nameof(steamID)); + } + ArgumentNullException.ThrowIfNull(webBrowser); if ((inventory == null) || (inventory.Count == 0)) { @@ -59,7 +63,7 @@ internal static class Backend { Uri request = new(ArchiNet.URL, "/Api/Listing/Announce/v2"); - AnnouncementRequest data = new(ASF.GlobalDatabase?.Identifier ?? Guid.NewGuid(), bot.SteamID, tradeToken, inventory, acceptedMatchableTypes, bot.BotConfig.TradingPreferences.HasFlag(BotConfig.ETradingPreferences.MatchEverything), ASF.GlobalConfig?.MaxTradeHoldDuration ?? GlobalConfig.DefaultMaxTradeHoldDuration, nickname, avatarHash); + AnnouncementRequest data = new(ASF.GlobalDatabase?.Identifier ?? Guid.NewGuid(), steamID, tradeToken, inventory, acceptedMatchableTypes, matchEverything, ASF.GlobalConfig?.MaxTradeHoldDuration ?? GlobalConfig.DefaultMaxTradeHoldDuration, nickname, avatarHash); return await webBrowser.UrlPost(request, data: data, requestOptions: WebBrowser.ERequestOptions.ReturnRedirections | WebBrowser.ERequestOptions.ReturnClientErrors).ConfigureAwait(false); } diff --git a/ArchiSteamFarm.OfficialPlugins.ItemsMatcher/RemoteCommunication.cs b/ArchiSteamFarm.OfficialPlugins.ItemsMatcher/RemoteCommunication.cs index 4c7795bf2..a60ed6b60 100644 --- a/ArchiSteamFarm.OfficialPlugins.ItemsMatcher/RemoteCommunication.cs +++ b/ArchiSteamFarm.OfficialPlugins.ItemsMatcher/RemoteCommunication.cs @@ -236,6 +236,8 @@ internal sealed class RemoteCommunication : IAsyncDisposable, IDisposable { return; } + bool matchEverything = Bot.BotConfig.TradingPreferences.HasFlag(BotConfig.ETradingPreferences.MatchEverything); + uint index = 0; ulong previousAssetID = 0; @@ -245,14 +247,20 @@ internal sealed class RemoteCommunication : IAsyncDisposable, IDisposable { foreach (Asset item in inventory) { if (acceptedMatchableTypes.Contains(item.Type)) { - assetsForListing.Add(new AssetForListing(item, index++, previousAssetID)); + // Only tradable assets matter for MatchEverything bots + if (!matchEverything || item.Tradable) { + assetsForListing.Add(new AssetForListing(item, index++, previousAssetID)); + } - (uint RealAppID, Asset.EType Type, Asset.ERarity Rarity) key = (item.RealAppID, item.Type, item.Rarity); + // But even for Fair bots, we should track and skip sets where we don't have any item to trade with + if (!matchEverything) { + (uint RealAppID, Asset.EType Type, Asset.ERarity Rarity) key = (item.RealAppID, item.Type, item.Rarity); - if (tradableState.TryGetValue(key, out Dictionary? set)) { - set[item.ClassID] = set.TryGetValue(item.ClassID, out uint tradableAmount) ? tradableAmount + (item.Tradable ? item.Amount : 0) : item.Tradable ? item.Amount : 0; - } else { - tradableState[key] = new Dictionary { { item.ClassID, item.Tradable ? item.Amount : 0 } }; + if (tradableState.TryGetValue(key, out Dictionary? set)) { + set[item.ClassID] = set.TryGetValue(item.ClassID, out uint tradableAmount) ? tradableAmount + (item.Tradable ? item.Amount : 0) : item.Tradable ? item.Amount : 0; + } else { + tradableState[key] = new Dictionary { { item.ClassID, item.Tradable ? item.Amount : 0 } }; + } } } @@ -267,17 +275,20 @@ internal sealed class RemoteCommunication : IAsyncDisposable, IDisposable { return; } - HashSet<(uint RealAppID, Asset.EType Type, Asset.ERarity Rarity)> setsToRemove = tradableState.Where(static set => set.Value.Values.All(static amount => amount == 0)).Select(static set => set.Key).ToHashSet(); + // We can now skip sets where we don't have any item to trade with, MatchEverything bots are already filtered to tradable only + if (!matchEverything) { + HashSet<(uint RealAppID, Asset.EType Type, Asset.ERarity Rarity)> setsToRemove = tradableState.Where(static set => set.Value.Values.All(static amount => amount == 0)).Select(static set => set.Key).ToHashSet(); - if (setsToRemove.Count > 0) { - assetsForListing.RemoveWhere(item => setsToRemove.Contains((item.RealAppID, item.Type, item.Rarity))); + if (setsToRemove.Count > 0) { + assetsForListing.RemoveWhere(item => setsToRemove.Contains((item.RealAppID, item.Type, item.Rarity))); - if (assetsForListing.Count == 0) { - // We're not eligible, record this as a valid check - LastAnnouncement = DateTime.UtcNow; - ShouldSendAnnouncementEarlier = ShouldSendHeartBeats = false; + if (assetsForListing.Count == 0) { + // We're not eligible, record this as a valid check + LastAnnouncement = DateTime.UtcNow; + ShouldSendAnnouncementEarlier = ShouldSendHeartBeats = false; - return; + return; + } } } @@ -313,7 +324,7 @@ internal sealed class RemoteCommunication : IAsyncDisposable, IDisposable { Bot.ArchiLogger.LogGenericInfo(string.Format(CultureInfo.CurrentCulture, Localization.Strings.ListingAnnouncing, Bot.SteamID, nickname, assetsForListing.Count)); // ReSharper disable once RedundantSuppressNullableWarningExpression - required for .NET Framework - BasicResponse? response = await Backend.AnnounceForListing(Bot, WebBrowser, assetsForListing, acceptedMatchableTypes, tradeToken!, nickname, avatarHash).ConfigureAwait(false); + BasicResponse? response = await Backend.AnnounceForListing(Bot.SteamID, WebBrowser, assetsForListing, acceptedMatchableTypes, matchEverything, tradeToken!, nickname, avatarHash).ConfigureAwait(false); if (response == null) { // This is actually a network failure, so we'll stop sending heartbeats but not record it as valid check