From 970998fb4b02f836216d8293043a3e3089879c6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Domeradzki?= Date: Tue, 11 Nov 2025 19:27:10 +0100 Subject: [PATCH] Rewrite BGR to new OrderedDictionary type --- .../IPC/Controllers/Api/BotController.cs | 9 ++-- .../BotGamesToRedeemInBackgroundRequest.cs | 5 ++- ArchiSteamFarm/Steam/Bot.cs | 22 ++++----- ArchiSteamFarm/Steam/Storage/BotDatabase.cs | 45 +++++-------------- 4 files changed, 31 insertions(+), 50 deletions(-) diff --git a/ArchiSteamFarm/IPC/Controllers/Api/BotController.cs b/ArchiSteamFarm/IPC/Controllers/Api/BotController.cs index ec5cd2e1d..d6090a904 100644 --- a/ArchiSteamFarm/IPC/Controllers/Api/BotController.cs +++ b/ArchiSteamFarm/IPC/Controllers/Api/BotController.cs @@ -24,7 +24,6 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; -using System.Collections.Specialized; using System.Linq; using System.Net; using System.Text.Json; @@ -227,7 +226,7 @@ public sealed class BotController : ArchiController { [EndpointSummary("Adds keys to redeem using BGR to given bot")] [HttpPost("{botNames:required}/GamesToRedeemInBackground")] - [ProducesResponseType>>((int) HttpStatusCode.OK)] + [ProducesResponseType>>>((int) HttpStatusCode.OK)] [ProducesResponseType((int) HttpStatusCode.BadRequest)] public async Task> GamesToRedeemInBackgroundPost(string botNames, [FromBody] BotGamesToRedeemInBackgroundRequest request) { ArgumentException.ThrowIfNullOrEmpty(botNames); @@ -243,7 +242,7 @@ public sealed class BotController : ArchiController { return BadRequest(new GenericResponse(false, Strings.FormatBotNotFound(botNames))); } - IOrderedDictionary validGamesToRedeemInBackground = Bot.ValidateGamesToRedeemInBackground(request.GamesToRedeemInBackground); + OrderedDictionary validGamesToRedeemInBackground = Bot.ValidateGamesToRedeemInBackground(request.GamesToRedeemInBackground); if (validGamesToRedeemInBackground.Count == 0) { return BadRequest(new GenericResponse(false, Strings.FormatErrorIsEmpty(nameof(validGamesToRedeemInBackground)))); @@ -251,13 +250,13 @@ public sealed class BotController : ArchiController { await Utilities.InParallel(bots.Select(bot => Task.Run(() => bot.AddGamesToRedeemInBackground(validGamesToRedeemInBackground)))).ConfigureAwait(false); - Dictionary result = new(bots.Count, Bot.BotsComparer); + Dictionary> result = new(bots.Count, Bot.BotsComparer); foreach (Bot bot in bots) { result[bot.BotName] = validGamesToRedeemInBackground; } - return Ok(new GenericResponse>(result)); + return Ok(new GenericResponse>>(result)); } [EndpointSummary("Provides input value to given bot for next usage")] diff --git a/ArchiSteamFarm/IPC/Requests/BotGamesToRedeemInBackgroundRequest.cs b/ArchiSteamFarm/IPC/Requests/BotGamesToRedeemInBackgroundRequest.cs index 5fc59c537..da5fc46ec 100644 --- a/ArchiSteamFarm/IPC/Requests/BotGamesToRedeemInBackgroundRequest.cs +++ b/ArchiSteamFarm/IPC/Requests/BotGamesToRedeemInBackgroundRequest.cs @@ -21,7 +21,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -using System.Collections.Specialized; +using System; +using System.Collections.Generic; using System.ComponentModel; using System.ComponentModel.DataAnnotations; using System.Diagnostics.CodeAnalysis; @@ -35,7 +36,7 @@ public sealed class BotGamesToRedeemInBackgroundRequest { [JsonInclude] [JsonRequired] [Required] - public OrderedDictionary GamesToRedeemInBackground { get; private init; } = new(); + public OrderedDictionary GamesToRedeemInBackground { get; private init; } = new(StringComparer.OrdinalIgnoreCase); [JsonConstructor] private BotGamesToRedeemInBackgroundRequest() { } diff --git a/ArchiSteamFarm/Steam/Bot.cs b/ArchiSteamFarm/Steam/Bot.cs index 8300c591f..640043c8e 100644 --- a/ArchiSteamFarm/Steam/Bot.cs +++ b/ArchiSteamFarm/Steam/Bot.cs @@ -22,12 +22,10 @@ // limitations under the License. using System; -using System.Collections; using System.Collections.Concurrent; using System.Collections.Frozen; using System.Collections.Generic; using System.Collections.Immutable; -using System.Collections.Specialized; using System.ComponentModel; using System.ComponentModel.DataAnnotations; using System.Diagnostics.CodeAnalysis; @@ -1020,7 +1018,7 @@ public sealed class Bot : IAsyncDisposable, IDisposable { return true; } - internal void AddGamesToRedeemInBackground(IOrderedDictionary gamesToRedeemInBackground) { + internal void AddGamesToRedeemInBackground(IReadOnlyDictionary gamesToRedeemInBackground) { if ((gamesToRedeemInBackground == null) || (gamesToRedeemInBackground.Count == 0)) { throw new ArgumentNullException(nameof(gamesToRedeemInBackground)); } @@ -1461,7 +1459,7 @@ public sealed class Bot : IAsyncDisposable, IDisposable { } try { - OrderedDictionary gamesToRedeemInBackground = new(); + OrderedDictionary gamesToRedeemInBackground = new(StringComparer.OrdinalIgnoreCase); using (StreamReader reader = new(filePath)) { while (await reader.ReadLineAsync().ConfigureAwait(false) is { } line) { @@ -1489,7 +1487,7 @@ public sealed class Bot : IAsyncDisposable, IDisposable { } if (gamesToRedeemInBackground.Count > 0) { - IOrderedDictionary validGamesToRedeemInBackground = ValidateGamesToRedeemInBackground(gamesToRedeemInBackground); + OrderedDictionary validGamesToRedeemInBackground = ValidateGamesToRedeemInBackground(gamesToRedeemInBackground); if (validGamesToRedeemInBackground.Count > 0) { AddGamesToRedeemInBackground(validGamesToRedeemInBackground); @@ -2009,18 +2007,22 @@ public sealed class Bot : IAsyncDisposable, IDisposable { return true; } - internal static IOrderedDictionary ValidateGamesToRedeemInBackground(IOrderedDictionary gamesToRedeemInBackground) { + internal static OrderedDictionary ValidateGamesToRedeemInBackground(IReadOnlyDictionary gamesToRedeemInBackground) { if ((gamesToRedeemInBackground == null) || (gamesToRedeemInBackground.Count == 0)) { throw new ArgumentNullException(nameof(gamesToRedeemInBackground)); } - HashSet invalidKeys = gamesToRedeemInBackground.Cast().Where(static game => !BotDatabase.IsValidGameToRedeemInBackground(game)).Select(static game => game.Key).ToHashSet(); + OrderedDictionary result = new(StringComparer.OrdinalIgnoreCase); - foreach (object invalidKey in invalidKeys) { - gamesToRedeemInBackground.Remove(invalidKey); + foreach ((string key, string name) in gamesToRedeemInBackground) { + if (!BotDatabase.IsValidGameToRedeemInBackground(key, name)) { + continue; + } + + result[key] = name; } - return gamesToRedeemInBackground; + return result; } private async Task Connect() { diff --git a/ArchiSteamFarm/Steam/Storage/BotDatabase.cs b/ArchiSteamFarm/Steam/Storage/BotDatabase.cs index 40908d274..a35c03aae 100644 --- a/ArchiSteamFarm/Steam/Storage/BotDatabase.cs +++ b/ArchiSteamFarm/Steam/Storage/BotDatabase.cs @@ -22,8 +22,7 @@ // limitations under the License. using System; -using System.Collections; -using System.Collections.Specialized; +using System.Collections.Generic; using System.IO; using System.Linq; using System.Text.Json; @@ -192,7 +191,7 @@ public sealed class BotDatabase : GenericDatabase { [JsonDisallowNull] [JsonInclude] - private OrderedDictionary GamesToRedeemInBackground { get; init; } = new(); + private OrderedDictionary GamesToRedeemInBackground { get; init; } = new(StringComparer.OrdinalIgnoreCase); private BotDatabase(string filePath) : this() { ArgumentException.ThrowIfNullOrEmpty(filePath); @@ -303,18 +302,18 @@ public sealed class BotDatabase : GenericDatabase { protected override Task Save() => Save(this); - internal void AddGamesToRedeemInBackground(IOrderedDictionary games) { + internal void AddGamesToRedeemInBackground(IReadOnlyDictionary games) { if ((games == null) || (games.Count == 0)) { throw new ArgumentNullException(nameof(games)); } lock (GamesToRedeemInBackground) { - foreach (DictionaryEntry game in games) { - if (!IsValidGameToRedeemInBackground(game)) { - throw new InvalidOperationException(nameof(game)); + foreach ((string key, string name) in games) { + if (!IsValidGameToRedeemInBackground(key, name)) { + throw new InvalidOperationException(nameof(IsValidGameToRedeemInBackground)); } - GamesToRedeemInBackground[game.Key] = game.Value; + GamesToRedeemInBackground[key] = name; } } @@ -381,33 +380,15 @@ public sealed class BotDatabase : GenericDatabase { internal (string? Key, string? Name) GetGameToRedeemInBackground() { lock (GamesToRedeemInBackground) { - foreach (DictionaryEntry game in GamesToRedeemInBackground) { - return game.Value switch { - string name => (game.Key as string, name), - JsonElement { ValueKind: JsonValueKind.String } jsonElement => (game.Key as string, jsonElement.GetString()), - _ => throw new InvalidOperationException(nameof(game.Value)) - }; + foreach ((string key, string name) in GamesToRedeemInBackground) { + return (key, name); } } return (null, null); } - internal static bool IsValidGameToRedeemInBackground(DictionaryEntry game) { - string? key = game.Key as string; - - if (string.IsNullOrEmpty(key) || !Utilities.IsValidCdKey(key)) { - return false; - } - - switch (game.Value) { - case string name when !string.IsNullOrEmpty(name): - case JsonElement { ValueKind: JsonValueKind.String } jsonElement when !string.IsNullOrEmpty(jsonElement.GetString()): - return true; - default: - return false; - } - } + internal static bool IsValidGameToRedeemInBackground(string key, string name) => !string.IsNullOrEmpty(key) && !string.IsNullOrEmpty(name) && Utilities.IsValidCdKey(key); internal void PerformMaintenance() { DateTime now = DateTime.UtcNow; @@ -421,17 +402,15 @@ public sealed class BotDatabase : GenericDatabase { ArgumentException.ThrowIfNullOrEmpty(key); lock (GamesToRedeemInBackground) { - if (!GamesToRedeemInBackground.Contains(key)) { + if (!GamesToRedeemInBackground.Remove(key)) { return; } - - GamesToRedeemInBackground.Remove(key); } Utilities.InBackground(Save); } - private (bool Valid, string? ErrorMessage) CheckValidation() => GamesToRedeemInBackground.Cast().Any(static game => !IsValidGameToRedeemInBackground(game)) ? (false, Strings.FormatErrorConfigPropertyInvalid(nameof(GamesToRedeemInBackground), string.Join("", GamesToRedeemInBackground))) : (true, null); + private (bool Valid, string? ErrorMessage) CheckValidation() => GamesToRedeemInBackground.Any(static entry => !IsValidGameToRedeemInBackground(entry.Key, entry.Value)) ? (false, Strings.FormatErrorConfigPropertyInvalid(nameof(GamesToRedeemInBackground), string.Join("", GamesToRedeemInBackground))) : (true, null); private async void OnObjectModified(object? sender, EventArgs e) { if (string.IsNullOrEmpty(FilePath)) {