From be5ec43772988c071618abf0b857d3eb321390c5 Mon Sep 17 00:00:00 2001 From: Archi Date: Sat, 17 Dec 2022 02:39:37 +0100 Subject: [PATCH] Add support for automatically rejecting trade offers from bad bots With special dedication to the guy who attempted to DDoS ASF STM listing today, hope your business will truly expand from now on! <3 --- ArchiSteamFarm/Core/ArchiNet.cs | 27 ++++++++++++++++++++++++ ArchiSteamFarm/Steam/Exchange/Trading.cs | 11 ++++++++++ ArchiSteamFarm/Storage/GlobalConfig.cs | 9 ++++++++ 3 files changed, 47 insertions(+) diff --git a/ArchiSteamFarm/Core/ArchiNet.cs b/ArchiSteamFarm/Core/ArchiNet.cs index 9318f40c8..e8da5a91c 100644 --- a/ArchiSteamFarm/Core/ArchiNet.cs +++ b/ArchiSteamFarm/Core/ArchiNet.cs @@ -20,15 +20,20 @@ // limitations under the License. using System; +using System.Collections.Immutable; using System.Threading.Tasks; +using ArchiSteamFarm.Helpers; using ArchiSteamFarm.IPC.Responses; using ArchiSteamFarm.Web.Responses; +using SteamKit2; namespace ArchiSteamFarm.Core; internal static class ArchiNet { internal static Uri URL => new("https://asf.JustArchi.net"); + private static readonly ArchiCacheable> CachedBadBots = new(ResolveCachedBadBots, TimeSpan.FromDays(1)); + internal static async Task FetchBuildChecksum(Version version, string variant) { ArgumentNullException.ThrowIfNull(version); @@ -50,4 +55,26 @@ internal static class ArchiNet { return response.Content.Result ?? ""; } + + internal static async Task IsBadBot(ulong steamID) { + if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) { + throw new ArgumentOutOfRangeException(nameof(steamID)); + } + + (_, ImmutableHashSet? badBots) = await CachedBadBots.GetValue(ArchiCacheable>.EFallback.SuccessPreviously).ConfigureAwait(false); + + return badBots?.Contains(steamID); + } + + private static async Task<(bool Success, ImmutableHashSet? Result)> ResolveCachedBadBots() { + if (ASF.WebBrowser == null) { + throw new InvalidOperationException(nameof(ASF.WebBrowser)); + } + + Uri request = new(URL, "/Api/BadBots"); + + ObjectResponse>>? response = await ASF.WebBrowser.UrlGetToJsonObject>>(request).ConfigureAwait(false); + + return response?.Content != null ? (true, response.Content.Result) : (false, null); + } } diff --git a/ArchiSteamFarm/Steam/Exchange/Trading.cs b/ArchiSteamFarm/Steam/Exchange/Trading.cs index b3cc63b10..a8681fdd5 100644 --- a/ArchiSteamFarm/Steam/Exchange/Trading.cs +++ b/ArchiSteamFarm/Steam/Exchange/Trading.cs @@ -547,6 +547,17 @@ public sealed class Trading : IDisposable { return ParseTradeResult.EResult.Blacklisted; } + + // Deny trades from bad steamIDs if user wishes to do so + if (ASF.GlobalConfig?.FilterBadBots ?? GlobalConfig.DefaultFilterBadBots) { + bool? isBadBot = await ArchiNet.IsBadBot(tradeOffer.OtherSteamID64).ConfigureAwait(false); + + if (isBadBot == true) { + Bot.ArchiLogger.LogGenericDebug(string.Format(CultureInfo.CurrentCulture, Strings.BotTradeOfferResult, tradeOffer.TradeOfferID, ParseTradeResult.EResult.Blacklisted, $"{nameof(tradeOffer.OtherSteamID64)} {tradeOffer.OtherSteamID64}")); + + return ParseTradeResult.EResult.Blacklisted; + } + } } // Check if it's donation trade diff --git a/ArchiSteamFarm/Storage/GlobalConfig.cs b/ArchiSteamFarm/Storage/GlobalConfig.cs index e11f3dee8..5f82db5e6 100644 --- a/ArchiSteamFarm/Storage/GlobalConfig.cs +++ b/ArchiSteamFarm/Storage/GlobalConfig.cs @@ -63,6 +63,9 @@ public sealed class GlobalConfig { [PublicAPI] public const byte DefaultFarmingDelay = 15; + [PublicAPI] + public const bool DefaultFilterBadBots = true; + [PublicAPI] public const byte DefaultGiftsLimiterDelay = 1; @@ -209,6 +212,9 @@ public sealed class GlobalConfig { [Range(1, byte.MaxValue)] public byte FarmingDelay { get; private set; } = DefaultFarmingDelay; + [JsonProperty(Required = Required.DisallowNull)] + public bool FilterBadBots { get; private set; } = DefaultFilterBadBots; + [JsonProperty(Required = Required.DisallowNull)] [Range(byte.MinValue, byte.MaxValue)] public byte GiftsLimiterDelay { get; private set; } = DefaultGiftsLimiterDelay; @@ -360,6 +366,9 @@ public sealed class GlobalConfig { [UsedImplicitly] public bool ShouldSerializeFarmingDelay() => !Saving || (FarmingDelay != DefaultFarmingDelay); + [UsedImplicitly] + public bool ShouldSerializeFilterBadBots() => !Saving || (FilterBadBots != DefaultFilterBadBots); + [UsedImplicitly] public bool ShouldSerializeGiftsLimiterDelay() => !Saving || (GiftsLimiterDelay != DefaultGiftsLimiterDelay);