From dea715ff1eb4a5ac6cc895f95f951a9a03c6323e Mon Sep 17 00:00:00 2001 From: Archi Date: Thu, 5 Jan 2023 14:30:08 +0100 Subject: [PATCH] Decrease announcement time, set listener for finished trade offers --- .../ItemsMatcherPlugin.cs | 22 ++++++++++-- .../RemoteCommunication.cs | 2 +- .../Steam/Exchange/ParseTradeResult.cs | 22 +++++++----- ArchiSteamFarm/Steam/Exchange/Trading.cs | 34 +++++++++++-------- 4 files changed, 53 insertions(+), 27 deletions(-) diff --git a/ArchiSteamFarm.OfficialPlugins.ItemsMatcher/ItemsMatcherPlugin.cs b/ArchiSteamFarm.OfficialPlugins.ItemsMatcher/ItemsMatcherPlugin.cs index bbe02da34..0e7466199 100644 --- a/ArchiSteamFarm.OfficialPlugins.ItemsMatcher/ItemsMatcherPlugin.cs +++ b/ArchiSteamFarm.OfficialPlugins.ItemsMatcher/ItemsMatcherPlugin.cs @@ -4,7 +4,7 @@ // / ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | | // /_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_| // | -// Copyright 2015-2022 Łukasz "JustArchi" Domeradzki +// Copyright 2015-2023 Łukasz "JustArchi" Domeradzki // Contact: JustArchi@JustArchi.net // | // Licensed under the Apache License, Version 2.0 (the "License"); @@ -31,6 +31,7 @@ using ArchiSteamFarm.OfficialPlugins.ItemsMatcher.Localization; using ArchiSteamFarm.Plugins; using ArchiSteamFarm.Plugins.Interfaces; using ArchiSteamFarm.Steam; +using ArchiSteamFarm.Steam.Exchange; using ArchiSteamFarm.Steam.Integration.Callbacks; using Newtonsoft.Json; using Newtonsoft.Json.Linq; @@ -39,7 +40,7 @@ using SteamKit2; namespace ArchiSteamFarm.OfficialPlugins.ItemsMatcher; [Export(typeof(IPlugin))] -internal sealed class ItemsMatcherPlugin : OfficialPlugin, IBot, IBotCommand2, IBotIdentity, IBotModules, IBotUserNotifications { +internal sealed class ItemsMatcherPlugin : OfficialPlugin, IBot, IBotCommand2, IBotIdentity, IBotModules, IBotTradeOfferResults, IBotUserNotifications { internal static readonly ConcurrentDictionary RemoteCommunications = new(); [JsonProperty] @@ -98,6 +99,23 @@ internal sealed class ItemsMatcherPlugin : OfficialPlugin, IBot, IBotCommand2, I } } + public Task OnBotTradeOfferResults(Bot bot, IReadOnlyCollection tradeResults) { + ArgumentNullException.ThrowIfNull(bot); + + if ((tradeResults == null) || (tradeResults.Count == 0)) { + throw new ArgumentNullException(nameof(tradeResults)); + } + + // We're interested only in Items notification for Bot that has RemoteCommunication enabled + if (!RemoteCommunications.TryGetValue(bot, out RemoteCommunication? remoteCommunication) || !tradeResults.Any(tradeResult => tradeResult is { Result: ParseTradeResult.EResult.Accepted, Confirmed: true } && ((tradeResult.ItemsToGive?.Any(item => bot.BotConfig.MatchableTypes.Contains(item.Type)) == true) || (tradeResult.ItemsToReceive?.Any(item => bot.BotConfig.MatchableTypes.Contains(item.Type)) == true)))) { + return Task.CompletedTask; + } + + remoteCommunication.OnNewItemsNotification(); + + return Task.CompletedTask; + } + public Task OnBotUserNotifications(Bot bot, IReadOnlyCollection newNotifications) { ArgumentNullException.ThrowIfNull(bot); diff --git a/ArchiSteamFarm.OfficialPlugins.ItemsMatcher/RemoteCommunication.cs b/ArchiSteamFarm.OfficialPlugins.ItemsMatcher/RemoteCommunication.cs index 73f48bb04..3233c3b05 100644 --- a/ArchiSteamFarm.OfficialPlugins.ItemsMatcher/RemoteCommunication.cs +++ b/ArchiSteamFarm.OfficialPlugins.ItemsMatcher/RemoteCommunication.cs @@ -45,7 +45,7 @@ using ArchiSteamFarm.Web.Responses; namespace ArchiSteamFarm.OfficialPlugins.ItemsMatcher; internal sealed class RemoteCommunication : IAsyncDisposable, IDisposable { - private const byte MaxAnnouncementTTL = 60; // Maximum amount of minutes we can wait before the next Announcement + private const byte MaxAnnouncementTTL = 180; // Maximum amount of minutes we can wait before the next Announcement private const byte MinAnnouncementTTL = 5; // Minimum amount of minutes we must wait before the next Announcement private const byte MinHeartBeatTTL = 10; // Minimum amount of minutes we must wait before sending next HeartBeat private const byte MinItemsCount = 100; // Minimum amount of items to be eligible for public listing diff --git a/ArchiSteamFarm/Steam/Exchange/ParseTradeResult.cs b/ArchiSteamFarm/Steam/Exchange/ParseTradeResult.cs index 81e6de398..296e01ee7 100644 --- a/ArchiSteamFarm/Steam/Exchange/ParseTradeResult.cs +++ b/ArchiSteamFarm/Steam/Exchange/ParseTradeResult.cs @@ -4,7 +4,7 @@ // / ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | | // /_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_| // | -// Copyright 2015-2022 Łukasz "JustArchi" Domeradzki +// Copyright 2015-2023 Łukasz "JustArchi" Domeradzki // Contact: JustArchi@JustArchi.net // | // Licensed under the Apache License, Version 2.0 (the "License"); @@ -21,24 +21,29 @@ using System; using System.Collections.Generic; -using System.Collections.Immutable; using System.ComponentModel; -using System.Linq; using ArchiSteamFarm.Steam.Data; using JetBrains.Annotations; namespace ArchiSteamFarm.Steam.Exchange; public sealed class ParseTradeResult { + [PublicAPI] + public IReadOnlyCollection? ItemsToGive { get; } + + [PublicAPI] + public IReadOnlyCollection? ItemsToReceive { get; } + [PublicAPI] public EResult Result { get; } [PublicAPI] public ulong TradeOfferID { get; } - internal readonly ImmutableHashSet? ReceivedItemTypes; + [PublicAPI] + public bool Confirmed { get; internal set; } - internal ParseTradeResult(ulong tradeOfferID, EResult result, IReadOnlyCollection? itemsToReceive = null) { + internal ParseTradeResult(ulong tradeOfferID, EResult result, bool requiresMobileConfirmation, IReadOnlyCollection? itemsToGive = null, IReadOnlyCollection? itemsToReceive = null) { if (tradeOfferID == 0) { throw new ArgumentOutOfRangeException(nameof(tradeOfferID)); } @@ -49,10 +54,9 @@ public sealed class ParseTradeResult { TradeOfferID = tradeOfferID; Result = result; - - if (itemsToReceive?.Count > 0) { - ReceivedItemTypes = itemsToReceive.Select(static item => item.Type).ToImmutableHashSet(); - } + Confirmed = !requiresMobileConfirmation; + ItemsToGive = itemsToGive; + ItemsToReceive = itemsToReceive; } public enum EResult : byte { diff --git a/ArchiSteamFarm/Steam/Exchange/Trading.cs b/ArchiSteamFarm/Steam/Exchange/Trading.cs index 1f3d6ab5b..5e84567b9 100644 --- a/ArchiSteamFarm/Steam/Exchange/Trading.cs +++ b/ArchiSteamFarm/Steam/Exchange/Trading.cs @@ -424,46 +424,50 @@ public sealed class Trading : IDisposable { HandledTradeOfferIDs.IntersectWith(tradeOffers.Select(static tradeOffer => tradeOffer.TradeOfferID)); } - IEnumerable> tasks = tradeOffers.Where(tradeOffer => !HandledTradeOfferIDs.Contains(tradeOffer.TradeOfferID)).Select(ParseTrade); - IList<(ParseTradeResult? TradeResult, bool RequiresMobileConfirmation)> results = await Utilities.InParallel(tasks).ConfigureAwait(false); + IEnumerable> tasks = tradeOffers.Where(tradeOffer => !HandledTradeOfferIDs.Contains(tradeOffer.TradeOfferID)).Select(ParseTrade); + IList results = await Utilities.InParallel(tasks).ConfigureAwait(false); + + HashSet validTradeResults = results.Where(static result => result != null).Select(static result => result!).ToHashSet(); if (Bot.HasMobileAuthenticator) { - HashSet mobileTradeOfferIDs = results.Where(static result => (result.TradeResult?.Result == ParseTradeResult.EResult.Accepted) && result.RequiresMobileConfirmation).Select(static result => result.TradeResult!.TradeOfferID).ToHashSet(); + HashSet mobileTradeResults = validTradeResults.Where(static result => result is { Result: ParseTradeResult.EResult.Accepted, Confirmed: false }).ToHashSet(); + + if (mobileTradeResults.Count > 0) { + HashSet mobileTradeOfferIDs = mobileTradeResults.Select(static tradeOffer => tradeOffer.TradeOfferID).ToHashSet(); - if (mobileTradeOfferIDs.Count > 0) { (bool twoFactorSuccess, _, _) = await Bot.Actions.HandleTwoFactorAuthenticationConfirmations(true, Confirmation.EType.Trade, mobileTradeOfferIDs, true).ConfigureAwait(false); - if (!twoFactorSuccess) { + if (twoFactorSuccess) { + foreach (ParseTradeResult mobileTradeResult in mobileTradeResults) { + mobileTradeResult.Confirmed = true; + } + } else { HandledTradeOfferIDs.ExceptWith(mobileTradeOfferIDs); - - return false; } } } - HashSet validTradeResults = results.Where(static result => result.TradeResult != null).Select(static result => result.TradeResult!).ToHashSet(); - if (validTradeResults.Count > 0) { await PluginsCore.OnBotTradeOfferResults(Bot, validTradeResults).ConfigureAwait(false); } - return results.Any(result => (result.TradeResult?.Result == ParseTradeResult.EResult.Accepted) && (!result.RequiresMobileConfirmation || Bot.HasMobileAuthenticator) && (result.TradeResult.ReceivedItemTypes?.Any(receivedItemType => Bot.BotConfig.LootableTypes.Contains(receivedItemType)) == true)); + return validTradeResults.Any(result => result is { Result: ParseTradeResult.EResult.Accepted, Confirmed: true } && (result.ItemsToReceive?.Any(receivedItem => Bot.BotConfig.LootableTypes.Contains(receivedItem.Type)) == true)); } - private async Task<(ParseTradeResult? TradeResult, bool RequiresMobileConfirmation)> ParseTrade(TradeOffer tradeOffer) { + private async Task ParseTrade(TradeOffer tradeOffer) { ArgumentNullException.ThrowIfNull(tradeOffer); if (tradeOffer.State != ETradeOfferState.Active) { Bot.ArchiLogger.LogGenericError(string.Format(CultureInfo.CurrentCulture, Strings.ErrorIsInvalid, tradeOffer.State)); - return (null, false); + return null; } if (!HandledTradeOfferIDs.Add(tradeOffer.TradeOfferID)) { // We've already seen this trade, this should not happen Bot.ArchiLogger.LogGenericError(string.Format(CultureInfo.CurrentCulture, Strings.IgnoringTrade, tradeOffer.TradeOfferID)); - return (new ParseTradeResult(tradeOffer.TradeOfferID, ParseTradeResult.EResult.Ignored, tradeOffer.ItemsToReceive), false); + return new ParseTradeResult(tradeOffer.TradeOfferID, ParseTradeResult.EResult.Ignored, false, tradeOffer.ItemsToGive, tradeOffer.ItemsToReceive); } ParseTradeResult.EResult result = await ShouldAcceptTrade(tradeOffer).ConfigureAwait(false); @@ -523,10 +527,10 @@ public sealed class Trading : IDisposable { default: Bot.ArchiLogger.LogGenericError(string.Format(CultureInfo.CurrentCulture, Strings.WarningUnknownValuePleaseReport, nameof(result), result)); - return (null, false); + return null; } - return (new ParseTradeResult(tradeOffer.TradeOfferID, result, tradeOffer.ItemsToReceive), tradeRequiresMobileConfirmation); + return new ParseTradeResult(tradeOffer.TradeOfferID, result, tradeRequiresMobileConfirmation, tradeOffer.ItemsToGive, tradeOffer.ItemsToReceive); } private async Task ShouldAcceptTrade(TradeOffer tradeOffer) {