diff --git a/ArchiSteamFarm/Steam/Bot.cs b/ArchiSteamFarm/Steam/Bot.cs index 003bd6699..bced1b605 100644 --- a/ArchiSteamFarm/Steam/Bot.cs +++ b/ArchiSteamFarm/Steam/Bot.cs @@ -1970,37 +1970,10 @@ public sealed class Bot : IAsyncDisposable, IDisposable { throw new ArgumentNullException(nameof(gamesToRedeemInBackground)); } - HashSet invalidKeys = []; + HashSet invalidKeys = gamesToRedeemInBackground.Cast().Where(static game => !BotDatabase.IsValidGameToRedeemInBackground(game)).Select(static game => game.Key).ToHashSet(); - foreach (DictionaryEntry game in gamesToRedeemInBackground) { - bool invalid = false; - - string? key = game.Key as string; - - if (string.IsNullOrEmpty(key)) { - invalid = true; - ASF.ArchiLogger.LogGenericWarning(string.Format(CultureInfo.CurrentCulture, Strings.ErrorIsInvalid, nameof(key))); - } else if (!Utilities.IsValidCdKey(key)) { - invalid = true; - ASF.ArchiLogger.LogGenericWarning(string.Format(CultureInfo.CurrentCulture, Strings.ErrorIsInvalid, key)); - } - - string? name = game.Value as string; - - if (string.IsNullOrEmpty(name)) { - invalid = true; - ASF.ArchiLogger.LogGenericWarning(string.Format(CultureInfo.CurrentCulture, Strings.ErrorIsInvalid, nameof(name))); - } - - if (invalid && (key != null)) { - invalidKeys.Add(key); - } - } - - if (invalidKeys.Count > 0) { - foreach (string invalidKey in invalidKeys) { - gamesToRedeemInBackground.Remove(invalidKey); - } + foreach (object invalidKey in invalidKeys) { + gamesToRedeemInBackground.Remove(invalidKey); } return gamesToRedeemInBackground; diff --git a/ArchiSteamFarm/Steam/Storage/BotDatabase.cs b/ArchiSteamFarm/Steam/Storage/BotDatabase.cs index 043c791b6..7f00dd71e 100644 --- a/ArchiSteamFarm/Steam/Storage/BotDatabase.cs +++ b/ArchiSteamFarm/Steam/Storage/BotDatabase.cs @@ -242,18 +242,17 @@ public sealed class BotDatabase : GenericDatabase { throw new ArgumentNullException(nameof(games)); } - bool save = false; - lock (GamesToRedeemInBackground) { - foreach (DictionaryEntry game in games.OfType().Where(game => !GamesToRedeemInBackground.Contains(game.Key))) { - GamesToRedeemInBackground.Add(game.Key, game.Value); - save = true; + foreach (DictionaryEntry game in games) { + if (!IsValidGameToRedeemInBackground(game)) { + throw new InvalidOperationException(nameof(game)); + } + + GamesToRedeemInBackground[game.Key] = game.Value; } } - if (save) { - Utilities.InBackground(Save); - } + Utilities.InBackground(Save); } internal static async Task CreateOrLoad(string filePath) { @@ -287,6 +286,16 @@ public sealed class BotDatabase : GenericDatabase { return null; } + (bool valid, string? errorMessage) = botDatabase.CheckValidation(); + + if (!valid) { + if (!string.IsNullOrEmpty(errorMessage)) { + ASF.ArchiLogger.LogGenericError(errorMessage); + } + + return null; + } + botDatabase.FilePath = filePath; return botDatabase; @@ -295,13 +304,35 @@ public sealed class BotDatabase : GenericDatabase { internal (string? Key, string? Name) GetGameToRedeemInBackground() { lock (GamesToRedeemInBackground) { foreach (DictionaryEntry game in GamesToRedeemInBackground) { - return (game.Key as string, game.Value as string); + 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)) + }; } } return (null, null); } + internal static bool IsValidGameToRedeemInBackground(DictionaryEntry game) { + ArgumentNullException.ThrowIfNull(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 void PerformMaintenance() { DateTime now = DateTime.UtcNow; @@ -324,6 +355,8 @@ public sealed class BotDatabase : GenericDatabase { Utilities.InBackground(Save); } + private (bool Valid, string? ErrorMessage) CheckValidation() => GamesToRedeemInBackground.Cast().Any(static game => !IsValidGameToRedeemInBackground(game)) ? (false, string.Format(CultureInfo.CurrentCulture, Strings.ErrorConfigPropertyInvalid, nameof(GamesToRedeemInBackground), string.Join("", GamesToRedeemInBackground))) : (true, null); + private async void OnObjectModified(object? sender, EventArgs e) { if (string.IsNullOrEmpty(FilePath)) { return;