diff --git a/ArchiSteamFarm.CustomPlugins.ExamplePlugin/ArchiSteamFarm.CustomPlugins.ExamplePlugin.csproj b/ArchiSteamFarm.CustomPlugins.ExamplePlugin/ArchiSteamFarm.CustomPlugins.ExamplePlugin.csproj
index e815540bf..ffdea400a 100644
--- a/ArchiSteamFarm.CustomPlugins.ExamplePlugin/ArchiSteamFarm.CustomPlugins.ExamplePlugin.csproj
+++ b/ArchiSteamFarm.CustomPlugins.ExamplePlugin/ArchiSteamFarm.CustomPlugins.ExamplePlugin.csproj
@@ -6,7 +6,6 @@
-
diff --git a/ArchiSteamFarm.CustomPlugins.ExamplePlugin/ExamplePlugin.cs b/ArchiSteamFarm.CustomPlugins.ExamplePlugin/ExamplePlugin.cs
index 4da40b361..263ad94cf 100644
--- a/ArchiSteamFarm.CustomPlugins.ExamplePlugin/ExamplePlugin.cs
+++ b/ArchiSteamFarm.CustomPlugins.ExamplePlugin/ExamplePlugin.cs
@@ -21,16 +21,17 @@
using System;
using System.Collections.Generic;
+using System.ComponentModel.DataAnnotations;
using System.Composition;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
+using System.Text.Json;
+using System.Text.Json.Serialization;
using System.Threading.Tasks;
using ArchiSteamFarm.Core;
using ArchiSteamFarm.Plugins.Interfaces;
using ArchiSteamFarm.Steam;
using ArchiSteamFarm.Steam.Data;
-using Newtonsoft.Json;
-using Newtonsoft.Json.Linq;
using SteamKit2;
namespace ArchiSteamFarm.CustomPlugins.ExamplePlugin;
@@ -45,31 +46,35 @@ namespace ArchiSteamFarm.CustomPlugins.ExamplePlugin;
internal sealed class ExamplePlugin : IASF, IBot, IBotCommand2, IBotConnection, IBotFriendRequest, IBotMessage, IBotModules, IBotTradeOffer {
// This is used for identification purposes, typically you want to use a friendly name of your plugin here, such as the name of your main class
// Please note that this property can have direct dependencies only on structures that were initialized by the constructor, as it's possible to be called before OnLoaded() takes place
+ [JsonInclude]
+ [Required]
public string Name => nameof(ExamplePlugin);
// This will be displayed to the user and written in the log file, typically you should point it to the version of your library, but alternatively you can do some more advanced logic if you'd like to
// Please note that this property can have direct dependencies only on structures that were initialized by the constructor, as it's possible to be called before OnLoaded() takes place
+ [JsonInclude]
+ [Required]
public Version Version => typeof(ExamplePlugin).Assembly.GetName().Version ?? throw new InvalidOperationException(nameof(Version));
// Plugins can expose custom properties for our GET /Api/Plugins API call, simply annotate them with [JsonProperty] (or keep public)
- [JsonProperty]
- public bool CustomIsEnabledField { get; private set; } = true;
+ [JsonInclude]
+ [Required]
+ public bool CustomIsEnabledField { get; private init; } = true;
// This method, apart from being called before any bot initialization takes place, allows you to read custom global config properties that are not recognized by ASF
// Thanks to that, you can extend default ASF config with your own stuff, then parse it here in order to customize your plugin during runtime
// Keep in mind that, as noted in the interface, additionalConfigProperties can be null if no custom, unrecognized properties are found by ASF, you should handle that case appropriately
// In addition to that, this method also guarantees that all plugins were already OnLoaded(), which allows cross-plugins-communication to be possible
- public Task OnASFInit(IReadOnlyDictionary? additionalConfigProperties = null) {
+ public Task OnASFInit(IReadOnlyDictionary? additionalConfigProperties = null) {
if (additionalConfigProperties == null) {
return Task.CompletedTask;
}
- foreach ((string configProperty, JToken configValue) in additionalConfigProperties) {
+ foreach ((string configProperty, JsonElement configValue) in additionalConfigProperties) {
// It's a good idea to prefix your custom properties with the name of your plugin, so there will be no possible conflict of ASF or other plugins using the same name, neither now or in the future
switch (configProperty) {
- case $"{nameof(ExamplePlugin)}TestProperty" when configValue.Type == JTokenType.Boolean:
- bool exampleBooleanValue = configValue.Value();
- ASF.ArchiLogger.LogGenericInfo($"{nameof(ExamplePlugin)}TestProperty boolean property has been found with a value of: {exampleBooleanValue}");
+ case $"{nameof(ExamplePlugin)}TestProperty" when configValue.ValueKind == JsonValueKind.True:
+ ASF.ArchiLogger.LogGenericInfo($"{nameof(ExamplePlugin)}TestProperty boolean property has been found with a value of true");
break;
}
@@ -135,7 +140,7 @@ internal sealed class ExamplePlugin : IASF, IBot, IBotCommand2, IBotConnection,
// Keep in mind that, as noted in the interface, additionalConfigProperties can be null if no custom, unrecognized properties are found by ASF, you should handle that case appropriately
// Also keep in mind that this function can be called multiple times, e.g. when user edits their bot configs during runtime
// Take a look at OnASFInit() for example parsing code
- public async Task OnBotInitModules(Bot bot, IReadOnlyDictionary? additionalConfigProperties = null) {
+ public async Task OnBotInitModules(Bot bot, IReadOnlyDictionary? additionalConfigProperties = null) {
// For example, we'll ensure that every bot starts paused regardless of Paused property, in order to do this, we'll just call Pause here in InitModules()
// Thanks to the fact that this method is called with each bot config reload, we'll ensure that our bot stays paused even if it'd get unpaused otherwise
bot.ArchiLogger.LogGenericInfo("Pausing this bot as asked from the plugin");
diff --git a/ArchiSteamFarm.CustomPlugins.ExamplePlugin/MeowResponse.cs b/ArchiSteamFarm.CustomPlugins.ExamplePlugin/MeowResponse.cs
index f9f9e0793..7b76a33c3 100644
--- a/ArchiSteamFarm.CustomPlugins.ExamplePlugin/MeowResponse.cs
+++ b/ArchiSteamFarm.CustomPlugins.ExamplePlugin/MeowResponse.cs
@@ -21,15 +21,17 @@
using System;
using System.Diagnostics.CodeAnalysis;
-using Newtonsoft.Json;
+using System.Text.Json.Serialization;
namespace ArchiSteamFarm.CustomPlugins.ExamplePlugin;
#pragma warning disable CA1812 // False positive, the class is used during json deserialization
[SuppressMessage("ReSharper", "ClassCannotBeInstantiated")]
internal sealed class MeowResponse {
- [JsonProperty("url", Required = Required.Always)]
- internal readonly Uri URL = null!;
+ [JsonInclude]
+ [JsonPropertyName("url")]
+ [JsonRequired]
+ internal Uri URL { get; private init; } = null!;
[JsonConstructor]
private MeowResponse() { }
diff --git a/ArchiSteamFarm.CustomPlugins.PeriodicGC/PeriodicGCPlugin.cs b/ArchiSteamFarm.CustomPlugins.PeriodicGC/PeriodicGCPlugin.cs
index bba3cb69d..86e7b5b4f 100644
--- a/ArchiSteamFarm.CustomPlugins.PeriodicGC/PeriodicGCPlugin.cs
+++ b/ArchiSteamFarm.CustomPlugins.PeriodicGC/PeriodicGCPlugin.cs
@@ -20,8 +20,10 @@
// limitations under the License.
using System;
+using System.ComponentModel.DataAnnotations;
using System.Composition;
using System.Runtime;
+using System.Text.Json.Serialization;
using System.Threading;
using System.Threading.Tasks;
using ArchiSteamFarm.Core;
@@ -38,8 +40,12 @@ internal sealed class PeriodicGCPlugin : IPlugin {
private static readonly object LockObject = new();
private static readonly Timer PeriodicGCTimer = new(PerformGC);
+ [JsonInclude]
+ [Required]
public string Name => nameof(PeriodicGCPlugin);
+ [JsonInclude]
+ [Required]
public Version Version => typeof(PeriodicGCPlugin).Assembly.GetName().Version ?? throw new InvalidOperationException(nameof(Version));
public Task OnLoaded() {
diff --git a/ArchiSteamFarm.CustomPlugins.SignInWithSteam/ArchiSteamFarm.CustomPlugins.SignInWithSteam.csproj b/ArchiSteamFarm.CustomPlugins.SignInWithSteam/ArchiSteamFarm.CustomPlugins.SignInWithSteam.csproj
index 842515d4a..bdf2d49b3 100644
--- a/ArchiSteamFarm.CustomPlugins.SignInWithSteam/ArchiSteamFarm.CustomPlugins.SignInWithSteam.csproj
+++ b/ArchiSteamFarm.CustomPlugins.SignInWithSteam/ArchiSteamFarm.CustomPlugins.SignInWithSteam.csproj
@@ -7,7 +7,6 @@
-
diff --git a/ArchiSteamFarm.CustomPlugins.SignInWithSteam/Data/SignInWithSteamRequest.cs b/ArchiSteamFarm.CustomPlugins.SignInWithSteam/Data/SignInWithSteamRequest.cs
index d83669719..095ebaf36 100644
--- a/ArchiSteamFarm.CustomPlugins.SignInWithSteam/Data/SignInWithSteamRequest.cs
+++ b/ArchiSteamFarm.CustomPlugins.SignInWithSteam/Data/SignInWithSteamRequest.cs
@@ -20,11 +20,12 @@
// limitations under the License.
using System;
-using Newtonsoft.Json;
+using System.Text.Json.Serialization;
namespace ArchiSteamFarm.CustomPlugins.SignInWithSteam.Data;
public sealed class SignInWithSteamRequest {
- [JsonProperty(Required = Required.Always)]
- public Uri RedirectURL { get; private set; } = null!;
+ [JsonInclude]
+ [JsonRequired]
+ public Uri RedirectURL { get; private init; } = null!;
}
diff --git a/ArchiSteamFarm.CustomPlugins.SignInWithSteam/Data/SignInWithSteamResponse.cs b/ArchiSteamFarm.CustomPlugins.SignInWithSteam/Data/SignInWithSteamResponse.cs
index 921597505..ba9e2ea13 100644
--- a/ArchiSteamFarm.CustomPlugins.SignInWithSteam/Data/SignInWithSteamResponse.cs
+++ b/ArchiSteamFarm.CustomPlugins.SignInWithSteam/Data/SignInWithSteamResponse.cs
@@ -20,13 +20,14 @@
// limitations under the License.
using System;
-using Newtonsoft.Json;
+using System.Text.Json.Serialization;
namespace ArchiSteamFarm.CustomPlugins.SignInWithSteam.Data;
public sealed class SignInWithSteamResponse {
- [JsonProperty(Required = Required.Always)]
- public Uri ReturnURL { get; private set; }
+ [JsonInclude]
+ [JsonRequired]
+ public Uri ReturnURL { get; private init; }
internal SignInWithSteamResponse(Uri returnURL) {
ArgumentNullException.ThrowIfNull(returnURL);
diff --git a/ArchiSteamFarm.CustomPlugins.SignInWithSteam/SignInWithSteamPlugin.cs b/ArchiSteamFarm.CustomPlugins.SignInWithSteam/SignInWithSteamPlugin.cs
index 921df52ae..2319c810a 100644
--- a/ArchiSteamFarm.CustomPlugins.SignInWithSteam/SignInWithSteamPlugin.cs
+++ b/ArchiSteamFarm.CustomPlugins.SignInWithSteam/SignInWithSteamPlugin.cs
@@ -20,7 +20,9 @@
// limitations under the License.
using System;
+using System.ComponentModel.DataAnnotations;
using System.Composition;
+using System.Text.Json.Serialization;
using System.Threading.Tasks;
using ArchiSteamFarm.Core;
using ArchiSteamFarm.Plugins.Interfaces;
@@ -31,8 +33,12 @@ namespace ArchiSteamFarm.CustomPlugins.SignInWithSteam;
[Export(typeof(IPlugin))]
[UsedImplicitly]
internal sealed class SignInWithSteamPlugin : IPlugin {
+ [JsonInclude]
+ [Required]
public string Name => nameof(SignInWithSteamPlugin);
+ [JsonInclude]
+ [Required]
public Version Version => typeof(SignInWithSteamPlugin).Assembly.GetName().Version ?? throw new InvalidOperationException(nameof(Version));
public Task OnLoaded() {
diff --git a/ArchiSteamFarm.OfficialPlugins.ItemsMatcher/ArchiSteamFarm.OfficialPlugins.ItemsMatcher.csproj b/ArchiSteamFarm.OfficialPlugins.ItemsMatcher/ArchiSteamFarm.OfficialPlugins.ItemsMatcher.csproj
index c8601cfc5..f43fdb613 100644
--- a/ArchiSteamFarm.OfficialPlugins.ItemsMatcher/ArchiSteamFarm.OfficialPlugins.ItemsMatcher.csproj
+++ b/ArchiSteamFarm.OfficialPlugins.ItemsMatcher/ArchiSteamFarm.OfficialPlugins.ItemsMatcher.csproj
@@ -6,7 +6,6 @@
-
diff --git a/ArchiSteamFarm.OfficialPlugins.ItemsMatcher/BotCache.cs b/ArchiSteamFarm.OfficialPlugins.ItemsMatcher/BotCache.cs
index 6b909d79e..bbb688b07 100644
--- a/ArchiSteamFarm.OfficialPlugins.ItemsMatcher/BotCache.cs
+++ b/ArchiSteamFarm.OfficialPlugins.ItemsMatcher/BotCache.cs
@@ -22,20 +22,22 @@
using System;
using System.Globalization;
using System.IO;
+using System.Text.Json.Serialization;
using System.Threading.Tasks;
using ArchiSteamFarm.Collections;
using ArchiSteamFarm.Core;
using ArchiSteamFarm.Helpers;
+using ArchiSteamFarm.Helpers.Json;
using ArchiSteamFarm.Localization;
using ArchiSteamFarm.OfficialPlugins.ItemsMatcher.Data;
using JetBrains.Annotations;
-using Newtonsoft.Json;
namespace ArchiSteamFarm.OfficialPlugins.ItemsMatcher;
internal sealed class BotCache : SerializableFile {
- [JsonProperty(Required = Required.DisallowNull)]
- internal readonly ConcurrentList LastAnnouncedAssetsForListing = [];
+ [JsonDisallowNull]
+ [JsonInclude]
+ internal ConcurrentList LastAnnouncedAssetsForListing { get; private init; } = [];
internal string? LastAnnouncedTradeToken {
get => BackingLastAnnouncedTradeToken;
@@ -76,14 +78,14 @@ internal sealed class BotCache : SerializableFile {
}
}
- [JsonProperty]
- private string? BackingLastAnnouncedTradeToken;
+ [JsonInclude]
+ private string? BackingLastAnnouncedTradeToken { get; set; }
- [JsonProperty]
- private string? BackingLastInventoryChecksumBeforeDeduplication;
+ [JsonInclude]
+ private string? BackingLastInventoryChecksumBeforeDeduplication { get; set; }
- [JsonProperty]
- private DateTime? BackingLastRequestAt;
+ [JsonInclude]
+ private DateTime? BackingLastRequestAt { get; set; }
private BotCache(string filePath) : this() {
ArgumentException.ThrowIfNullOrEmpty(filePath);
@@ -116,6 +118,8 @@ internal sealed class BotCache : SerializableFile {
base.Dispose(disposing);
}
+ protected override Task Save() => Save(this);
+
internal static async Task CreateOrLoad(string filePath) {
ArgumentException.ThrowIfNullOrEmpty(filePath);
@@ -134,7 +138,7 @@ internal sealed class BotCache : SerializableFile {
return new BotCache(filePath);
}
- botCache = JsonConvert.DeserializeObject(json);
+ botCache = json.ToJsonObject();
} catch (Exception e) {
ASF.ArchiLogger.LogGenericException(e);
diff --git a/ArchiSteamFarm.OfficialPlugins.ItemsMatcher/Data/AnnouncementDiffRequest.cs b/ArchiSteamFarm.OfficialPlugins.ItemsMatcher/Data/AnnouncementDiffRequest.cs
index e11802125..9de335f41 100644
--- a/ArchiSteamFarm.OfficialPlugins.ItemsMatcher/Data/AnnouncementDiffRequest.cs
+++ b/ArchiSteamFarm.OfficialPlugins.ItemsMatcher/Data/AnnouncementDiffRequest.cs
@@ -22,19 +22,21 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
+using System.Text.Json.Serialization;
using ArchiSteamFarm.Steam.Data;
using ArchiSteamFarm.Steam.Storage;
-using Newtonsoft.Json;
using SteamKit2;
namespace ArchiSteamFarm.OfficialPlugins.ItemsMatcher.Data;
internal sealed class AnnouncementDiffRequest : AnnouncementRequest {
- [JsonProperty(Required = Required.Always)]
- private readonly ImmutableHashSet InventoryRemoved;
+ [JsonInclude]
+ [JsonRequired]
+ private ImmutableHashSet InventoryRemoved { get; init; }
- [JsonProperty(Required = Required.Always)]
- private readonly string PreviousInventoryChecksum;
+ [JsonInclude]
+ [JsonRequired]
+ private string PreviousInventoryChecksum { get; init; }
internal AnnouncementDiffRequest(Guid guid, ulong steamID, IReadOnlyCollection inventory, string inventoryChecksum, IReadOnlyCollection matchableTypes, uint totalInventoryCount, bool matchEverything, byte maxTradeHoldDuration, string tradeToken, IReadOnlyCollection inventoryRemoved, string previousInventoryChecksum, string? nickname = null, string? avatarHash = null) : base(guid, steamID, inventory, inventoryChecksum, matchableTypes, totalInventoryCount, matchEverything, maxTradeHoldDuration, tradeToken, nickname, avatarHash) {
ArgumentOutOfRangeException.ThrowIfEqual(guid, Guid.Empty);
diff --git a/ArchiSteamFarm.OfficialPlugins.ItemsMatcher/Data/AnnouncementRequest.cs b/ArchiSteamFarm.OfficialPlugins.ItemsMatcher/Data/AnnouncementRequest.cs
index 619aa85c3..c448af649 100644
--- a/ArchiSteamFarm.OfficialPlugins.ItemsMatcher/Data/AnnouncementRequest.cs
+++ b/ArchiSteamFarm.OfficialPlugins.ItemsMatcher/Data/AnnouncementRequest.cs
@@ -22,47 +22,56 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
+using System.Text.Json.Serialization;
using ArchiSteamFarm.Steam.Data;
using ArchiSteamFarm.Steam.Storage;
using JetBrains.Annotations;
-using Newtonsoft.Json;
using SteamKit2;
namespace ArchiSteamFarm.OfficialPlugins.ItemsMatcher.Data;
internal class AnnouncementRequest {
- [JsonProperty]
- private readonly string? AvatarHash;
+ [JsonInclude]
+ private string? AvatarHash { get; init; }
- [JsonProperty(Required = Required.Always)]
- private readonly Guid Guid;
+ [JsonInclude]
+ [JsonRequired]
+ private Guid Guid { get; init; }
- [JsonProperty(Required = Required.Always)]
- private readonly ImmutableHashSet Inventory;
+ [JsonInclude]
+ [JsonRequired]
+ private ImmutableHashSet Inventory { get; init; }
- [JsonProperty(Required = Required.Always)]
- private readonly string InventoryChecksum;
+ [JsonInclude]
+ [JsonRequired]
+ private string InventoryChecksum { get; init; }
- [JsonProperty(Required = Required.Always)]
- private readonly ImmutableHashSet MatchableTypes;
+ [JsonInclude]
+ [JsonRequired]
+ private ImmutableHashSet MatchableTypes { get; init; }
- [JsonProperty(Required = Required.Always)]
- private readonly bool MatchEverything;
+ [JsonInclude]
+ [JsonRequired]
+ private bool MatchEverything { get; init; }
- [JsonProperty(Required = Required.Always)]
- private readonly byte MaxTradeHoldDuration;
+ [JsonInclude]
+ [JsonRequired]
+ private byte MaxTradeHoldDuration { get; init; }
- [JsonProperty]
- private readonly string? Nickname;
+ [JsonInclude]
+ private string? Nickname { get; init; }
- [JsonProperty(Required = Required.Always)]
- private readonly ulong SteamID;
+ [JsonInclude]
+ [JsonRequired]
+ private ulong SteamID { get; init; }
- [JsonProperty(Required = Required.Always)]
- private readonly uint TotalInventoryCount;
+ [JsonInclude]
+ [JsonRequired]
+ private uint TotalInventoryCount { get; init; }
- [JsonProperty(Required = Required.Always)]
- private readonly string TradeToken;
+ [JsonInclude]
+ [JsonRequired]
+ private string TradeToken { get; init; }
internal AnnouncementRequest(Guid guid, ulong steamID, IReadOnlyCollection inventory, string inventoryChecksum, IReadOnlyCollection matchableTypes, uint totalInventoryCount, bool matchEverything, byte maxTradeHoldDuration, string tradeToken, string? nickname = null, string? avatarHash = null) {
ArgumentOutOfRangeException.ThrowIfEqual(guid, Guid.Empty);
diff --git a/ArchiSteamFarm.OfficialPlugins.ItemsMatcher/Data/AssetForListing.cs b/ArchiSteamFarm.OfficialPlugins.ItemsMatcher/Data/AssetForListing.cs
index 84ea0b891..ef2cb8b45 100644
--- a/ArchiSteamFarm.OfficialPlugins.ItemsMatcher/Data/AssetForListing.cs
+++ b/ArchiSteamFarm.OfficialPlugins.ItemsMatcher/Data/AssetForListing.cs
@@ -20,19 +20,23 @@
// limitations under the License.
using System;
+using System.Text.Json.Serialization;
using ArchiSteamFarm.Steam.Data;
-using Newtonsoft.Json;
namespace ArchiSteamFarm.OfficialPlugins.ItemsMatcher.Data;
internal sealed class AssetForListing : AssetInInventory {
- [JsonProperty("i", Required = Required.Always)]
- internal readonly uint Index;
+ internal string BackendHashCode => $"{Index}-{PreviousAssetID}-{AssetID}-{ClassID}-{Rarity}-{RealAppID}-{Tradable}-{Type}-{Amount}";
- [JsonProperty("l", Required = Required.Always)]
- internal readonly ulong PreviousAssetID;
+ [JsonInclude]
+ [JsonPropertyName("i")]
+ [JsonRequired]
+ internal uint Index { get; private init; }
- internal string BackendHashCode => Index + "-" + PreviousAssetID + "-" + AssetID + "-" + ClassID + "-" + Rarity + "-" + RealAppID + "-" + Tradable + "-" + Type + "-" + Amount;
+ [JsonInclude]
+ [JsonPropertyName("l")]
+ [JsonRequired]
+ internal ulong PreviousAssetID { get; private init; }
internal AssetForListing(Asset asset, uint index, ulong previousAssetID) : base(asset) {
ArgumentNullException.ThrowIfNull(asset);
diff --git a/ArchiSteamFarm.OfficialPlugins.ItemsMatcher/Data/AssetForMatching.cs b/ArchiSteamFarm.OfficialPlugins.ItemsMatcher/Data/AssetForMatching.cs
index 577d2bdb1..950832c25 100644
--- a/ArchiSteamFarm.OfficialPlugins.ItemsMatcher/Data/AssetForMatching.cs
+++ b/ArchiSteamFarm.OfficialPlugins.ItemsMatcher/Data/AssetForMatching.cs
@@ -20,30 +20,42 @@
// limitations under the License.
using System;
+using System.Text.Json.Serialization;
using ArchiSteamFarm.Steam.Data;
-using Newtonsoft.Json;
namespace ArchiSteamFarm.OfficialPlugins.ItemsMatcher.Data;
internal class AssetForMatching {
- [JsonProperty("c", Required = Required.Always)]
- internal readonly ulong ClassID;
-
- [JsonProperty("r", Required = Required.Always)]
- internal readonly Asset.ERarity Rarity;
-
- [JsonProperty("e", Required = Required.Always)]
- internal readonly uint RealAppID;
-
- [JsonProperty("t", Required = Required.Always)]
- internal readonly bool Tradable;
-
- [JsonProperty("p", Required = Required.Always)]
- internal readonly Asset.EType Type;
-
- [JsonProperty("a", Required = Required.Always)]
+ [JsonInclude]
+ [JsonPropertyName("a")]
+ [JsonRequired]
internal uint Amount { get; set; }
+ [JsonInclude]
+ [JsonPropertyName("c")]
+ [JsonRequired]
+ internal ulong ClassID { get; private init; }
+
+ [JsonInclude]
+ [JsonPropertyName("r")]
+ [JsonRequired]
+ internal Asset.ERarity Rarity { get; private init; }
+
+ [JsonInclude]
+ [JsonPropertyName("e")]
+ [JsonRequired]
+ internal uint RealAppID { get; private init; }
+
+ [JsonInclude]
+ [JsonPropertyName("t")]
+ [JsonRequired]
+ internal bool Tradable { get; private init; }
+
+ [JsonInclude]
+ [JsonPropertyName("p")]
+ [JsonRequired]
+ internal Asset.EType Type { get; private init; }
+
[JsonConstructor]
protected AssetForMatching() { }
diff --git a/ArchiSteamFarm.OfficialPlugins.ItemsMatcher/Data/AssetInInventory.cs b/ArchiSteamFarm.OfficialPlugins.ItemsMatcher/Data/AssetInInventory.cs
index b8acc0594..a0404f51a 100644
--- a/ArchiSteamFarm.OfficialPlugins.ItemsMatcher/Data/AssetInInventory.cs
+++ b/ArchiSteamFarm.OfficialPlugins.ItemsMatcher/Data/AssetInInventory.cs
@@ -20,14 +20,16 @@
// limitations under the License.
using System;
+using System.Text.Json.Serialization;
using ArchiSteamFarm.Steam.Data;
-using Newtonsoft.Json;
namespace ArchiSteamFarm.OfficialPlugins.ItemsMatcher.Data;
internal class AssetInInventory : AssetForMatching {
- [JsonProperty("d", Required = Required.Always)]
- internal readonly ulong AssetID;
+ [JsonInclude]
+ [JsonPropertyName("d")]
+ [JsonRequired]
+ internal ulong AssetID { get; private init; }
[JsonConstructor]
protected AssetInInventory() { }
diff --git a/ArchiSteamFarm.OfficialPlugins.ItemsMatcher/Data/BackgroundTaskResponse.cs b/ArchiSteamFarm.OfficialPlugins.ItemsMatcher/Data/BackgroundTaskResponse.cs
index def540eb7..55db8d97a 100644
--- a/ArchiSteamFarm.OfficialPlugins.ItemsMatcher/Data/BackgroundTaskResponse.cs
+++ b/ArchiSteamFarm.OfficialPlugins.ItemsMatcher/Data/BackgroundTaskResponse.cs
@@ -21,22 +21,20 @@
using System;
using System.Diagnostics.CodeAnalysis;
-using Newtonsoft.Json;
+using System.Text.Json.Serialization;
namespace ArchiSteamFarm.OfficialPlugins.ItemsMatcher.Data;
#pragma warning disable CA1812 // False positive, the class is used during json deserialization
[SuppressMessage("ReSharper", "ClassCannotBeInstantiated")]
internal sealed class BackgroundTaskResponse {
-#pragma warning disable CS0649 // False positive, the field is used during json deserialization
- [JsonProperty(Required = Required.Always)]
- internal readonly bool Finished;
-#pragma warning restore CS0649 // False positive, the field is used during json deserialization
+ [JsonInclude]
+ [JsonRequired]
+ internal bool Finished { get; private init; }
-#pragma warning disable CS0649 // False positive, the field is used during json deserialization
- [JsonProperty(Required = Required.Always)]
- internal readonly Guid RequestID;
-#pragma warning restore CS0649 // False positive, the field is used during json deserialization
+ [JsonInclude]
+ [JsonRequired]
+ internal Guid RequestID { get; private init; }
[JsonConstructor]
private BackgroundTaskResponse() { }
diff --git a/ArchiSteamFarm.OfficialPlugins.ItemsMatcher/Data/HeartBeatRequest.cs b/ArchiSteamFarm.OfficialPlugins.ItemsMatcher/Data/HeartBeatRequest.cs
index 68debbca3..b91748341 100644
--- a/ArchiSteamFarm.OfficialPlugins.ItemsMatcher/Data/HeartBeatRequest.cs
+++ b/ArchiSteamFarm.OfficialPlugins.ItemsMatcher/Data/HeartBeatRequest.cs
@@ -20,17 +20,19 @@
// limitations under the License.
using System;
-using Newtonsoft.Json;
+using System.Text.Json.Serialization;
using SteamKit2;
namespace ArchiSteamFarm.OfficialPlugins.ItemsMatcher.Data;
internal sealed class HeartBeatRequest {
- [JsonProperty(Required = Required.Always)]
- internal readonly Guid Guid;
+ [JsonInclude]
+ [JsonRequired]
+ internal Guid Guid { get; private init; }
- [JsonProperty(Required = Required.Always)]
- internal readonly ulong SteamID;
+ [JsonInclude]
+ [JsonRequired]
+ internal ulong SteamID { get; private init; }
internal HeartBeatRequest(Guid guid, ulong steamID) {
ArgumentOutOfRangeException.ThrowIfEqual(guid, Guid.Empty);
diff --git a/ArchiSteamFarm.OfficialPlugins.ItemsMatcher/Data/InventoriesRequest.cs b/ArchiSteamFarm.OfficialPlugins.ItemsMatcher/Data/InventoriesRequest.cs
index c0048eae3..a08f9be29 100644
--- a/ArchiSteamFarm.OfficialPlugins.ItemsMatcher/Data/InventoriesRequest.cs
+++ b/ArchiSteamFarm.OfficialPlugins.ItemsMatcher/Data/InventoriesRequest.cs
@@ -23,24 +23,28 @@ using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
+using System.Text.Json.Serialization;
using ArchiSteamFarm.Steam.Data;
-using Newtonsoft.Json;
using SteamKit2;
namespace ArchiSteamFarm.OfficialPlugins.ItemsMatcher.Data;
internal sealed class InventoriesRequest {
- [JsonProperty(Required = Required.Always)]
- internal readonly Guid Guid;
+ [JsonInclude]
+ [JsonRequired]
+ internal Guid Guid { get; private init; }
- [JsonProperty(Required = Required.Always)]
- internal readonly ImmutableHashSet Inventory;
+ [JsonInclude]
+ [JsonRequired]
+ internal ImmutableHashSet Inventory { get; private init; }
- [JsonProperty(Required = Required.Always)]
- internal readonly ImmutableHashSet MatchableTypes;
+ [JsonInclude]
+ [JsonRequired]
+ internal ImmutableHashSet MatchableTypes { get; private init; }
- [JsonProperty(Required = Required.Always)]
- internal readonly ulong SteamID;
+ [JsonInclude]
+ [JsonRequired]
+ internal ulong SteamID { get; private init; }
internal InventoriesRequest(Guid guid, ulong steamID, IReadOnlyCollection inventory, IReadOnlyCollection matchableTypes) {
ArgumentOutOfRangeException.ThrowIfEqual(guid, Guid.Empty);
diff --git a/ArchiSteamFarm.OfficialPlugins.ItemsMatcher/Data/ListedUser.cs b/ArchiSteamFarm.OfficialPlugins.ItemsMatcher/Data/ListedUser.cs
index 24a45cc0f..67eaeb238 100644
--- a/ArchiSteamFarm.OfficialPlugins.ItemsMatcher/Data/ListedUser.cs
+++ b/ArchiSteamFarm.OfficialPlugins.ItemsMatcher/Data/ListedUser.cs
@@ -21,52 +21,48 @@
using System.Collections.Immutable;
using System.Diagnostics.CodeAnalysis;
+using System.Text.Json.Serialization;
using ArchiSteamFarm.Steam.Data;
-using Newtonsoft.Json;
namespace ArchiSteamFarm.OfficialPlugins.ItemsMatcher.Data;
#pragma warning disable CA1812 // False positive, the class is used during json deserialization
[SuppressMessage("ReSharper", "ClassCannotBeInstantiated")]
internal sealed class ListedUser {
- [JsonProperty(Required = Required.Always)]
- internal readonly ImmutableHashSet Assets = ImmutableHashSet.Empty;
+ [JsonInclude]
+ [JsonRequired]
+ internal ImmutableHashSet Assets { get; private init; } = ImmutableHashSet.Empty;
- [JsonProperty(Required = Required.Always)]
- internal readonly ImmutableHashSet MatchableTypes = ImmutableHashSet.Empty;
+ [JsonInclude]
+ [JsonRequired]
+ internal ImmutableHashSet MatchableTypes { get; private init; } = ImmutableHashSet.Empty;
-#pragma warning disable CS0649 // False positive, the field is used during json deserialization
- [JsonProperty(Required = Required.Always)]
- internal readonly bool MatchEverything;
-#pragma warning restore CS0649 // False positive, the field is used during json deserialization
+ [JsonInclude]
+ [JsonRequired]
+ internal bool MatchEverything { get; private init; }
-#pragma warning disable CS0649 // False positive, the field is used during json deserialization
- [JsonProperty(Required = Required.Always)]
- internal readonly byte MaxTradeHoldDuration;
-#pragma warning restore CS0649 // False positive, the field is used during json deserialization
+ [JsonInclude]
+ [JsonRequired]
+ internal byte MaxTradeHoldDuration { get; private init; }
-#pragma warning disable CS0649 // False positive, the field is used during json deserialization
- [JsonProperty(Required = Required.AllowNull)]
- internal readonly string? Nickname;
-#pragma warning restore CS0649 // False positive, the field is used during json deserialization
+ [JsonInclude]
+ internal string? Nickname { get; private init; }
-#pragma warning disable CS0649 // False positive, the field is used during json deserialization
- [JsonProperty(Required = Required.Always)]
- internal readonly ulong SteamID;
-#pragma warning restore CS0649 // False positive, the field is used during json deserialization
+ [JsonInclude]
+ [JsonRequired]
+ internal ulong SteamID { get; private init; }
-#pragma warning disable CS0649 // False positive, the field is used during json deserialization
- [JsonProperty(Required = Required.Always)]
- internal readonly uint TotalGamesCount;
-#pragma warning restore CS0649 // False positive, the field is used during json deserialization
+ [JsonInclude]
+ [JsonRequired]
+ internal uint TotalGamesCount { get; private init; }
-#pragma warning disable CS0649 // False positive, the field is used during json deserialization
- [JsonProperty(Required = Required.Always)]
- internal readonly uint TotalInventoryCount;
-#pragma warning restore CS0649 // False positive, the field is used during json deserialization
+ [JsonInclude]
+ [JsonRequired]
+ internal uint TotalInventoryCount { get; private init; }
- [JsonProperty(Required = Required.Always)]
- internal readonly string TradeToken = "";
+ [JsonInclude]
+ [JsonRequired]
+ internal string TradeToken { get; private init; } = "";
[JsonConstructor]
private ListedUser() { }
diff --git a/ArchiSteamFarm.OfficialPlugins.ItemsMatcher/Data/SetPart.cs b/ArchiSteamFarm.OfficialPlugins.ItemsMatcher/Data/SetPart.cs
index 70433175f..4856b30f1 100644
--- a/ArchiSteamFarm.OfficialPlugins.ItemsMatcher/Data/SetPart.cs
+++ b/ArchiSteamFarm.OfficialPlugins.ItemsMatcher/Data/SetPart.cs
@@ -20,33 +20,33 @@
// limitations under the License.
using System.Diagnostics.CodeAnalysis;
+using System.Text.Json.Serialization;
using ArchiSteamFarm.Steam.Data;
-using Newtonsoft.Json;
namespace ArchiSteamFarm.OfficialPlugins.ItemsMatcher.Data;
#pragma warning disable CA1812 // False positive, the class is used during json deserialization
[SuppressMessage("ReSharper", "ClassCannotBeInstantiated")]
internal sealed class SetPart {
-#pragma warning disable CS0649 // False positive, the field is used during json deserialization
- [JsonProperty("c", Required = Required.Always)]
- internal readonly ulong ClassID;
-#pragma warning restore CS0649 // False positive, the field is used during json deserialization
+ [JsonInclude]
+ [JsonPropertyName("c")]
+ [JsonRequired]
+ internal ulong ClassID { get; private init; }
-#pragma warning disable CS0649 // False positive, the field is used during json deserialization
- [JsonProperty("r", Required = Required.Always)]
- internal readonly Asset.ERarity Rarity;
-#pragma warning restore CS0649 // False positive, the field is used during json deserialization
+ [JsonInclude]
+ [JsonPropertyName("r")]
+ [JsonRequired]
+ internal Asset.ERarity Rarity { get; private init; }
-#pragma warning disable CS0649 // False positive, the field is used during json deserialization
- [JsonProperty("e", Required = Required.Always)]
- internal readonly uint RealAppID;
-#pragma warning restore CS0649 // False positive, the field is used during json deserialization
+ [JsonInclude]
+ [JsonPropertyName("e")]
+ [JsonRequired]
+ internal uint RealAppID { get; private init; }
-#pragma warning disable CS0649 // False positive, the field is used during json deserialization
- [JsonProperty("p", Required = Required.Always)]
- internal readonly Asset.EType Type;
-#pragma warning restore CS0649 // False positive, the field is used during json deserialization
+ [JsonInclude]
+ [JsonPropertyName("p")]
+ [JsonRequired]
+ internal Asset.EType Type { get; private init; }
[JsonConstructor]
private SetPart() { }
diff --git a/ArchiSteamFarm.OfficialPlugins.ItemsMatcher/Data/SetPartsRequest.cs b/ArchiSteamFarm.OfficialPlugins.ItemsMatcher/Data/SetPartsRequest.cs
index 2c6f96bc8..bb75e23f8 100644
--- a/ArchiSteamFarm.OfficialPlugins.ItemsMatcher/Data/SetPartsRequest.cs
+++ b/ArchiSteamFarm.OfficialPlugins.ItemsMatcher/Data/SetPartsRequest.cs
@@ -22,24 +22,28 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
+using System.Text.Json.Serialization;
using ArchiSteamFarm.Steam.Data;
-using Newtonsoft.Json;
using SteamKit2;
namespace ArchiSteamFarm.OfficialPlugins.ItemsMatcher.Data;
internal sealed class SetPartsRequest {
- [JsonProperty(Required = Required.Always)]
- internal readonly Guid Guid;
+ [JsonInclude]
+ [JsonRequired]
+ internal Guid Guid { get; private init; }
- [JsonProperty(Required = Required.Always)]
- internal readonly ImmutableHashSet MatchableTypes;
+ [JsonInclude]
+ [JsonRequired]
+ internal ImmutableHashSet MatchableTypes { get; private init; }
- [JsonProperty(Required = Required.Always)]
- internal readonly ImmutableHashSet RealAppIDs;
+ [JsonInclude]
+ [JsonRequired]
+ internal ImmutableHashSet RealAppIDs { get; private init; }
- [JsonProperty(Required = Required.Always)]
- internal readonly ulong SteamID;
+ [JsonInclude]
+ [JsonRequired]
+ internal ulong SteamID { get; private init; }
internal SetPartsRequest(Guid guid, ulong steamID, IReadOnlyCollection matchableTypes, IReadOnlyCollection realAppIDs) {
ArgumentOutOfRangeException.ThrowIfEqual(guid, Guid.Empty);
diff --git a/ArchiSteamFarm.OfficialPlugins.ItemsMatcher/ItemsMatcherPlugin.cs b/ArchiSteamFarm.OfficialPlugins.ItemsMatcher/ItemsMatcherPlugin.cs
index ff345cfb7..fca96c2dd 100644
--- a/ArchiSteamFarm.OfficialPlugins.ItemsMatcher/ItemsMatcherPlugin.cs
+++ b/ArchiSteamFarm.OfficialPlugins.ItemsMatcher/ItemsMatcherPlugin.cs
@@ -23,8 +23,11 @@ using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.ComponentModel;
+using System.ComponentModel.DataAnnotations;
using System.Composition;
using System.Linq;
+using System.Text.Json;
+using System.Text.Json.Serialization;
using System.Threading.Tasks;
using ArchiSteamFarm.Core;
using ArchiSteamFarm.OfficialPlugins.ItemsMatcher.Localization;
@@ -33,8 +36,6 @@ using ArchiSteamFarm.Plugins.Interfaces;
using ArchiSteamFarm.Steam;
using ArchiSteamFarm.Steam.Exchange;
using ArchiSteamFarm.Steam.Integration.Callbacks;
-using Newtonsoft.Json;
-using Newtonsoft.Json.Linq;
using SteamKit2;
namespace ArchiSteamFarm.OfficialPlugins.ItemsMatcher;
@@ -43,10 +44,12 @@ namespace ArchiSteamFarm.OfficialPlugins.ItemsMatcher;
internal sealed class ItemsMatcherPlugin : OfficialPlugin, IBot, IBotCommand2, IBotIdentity, IBotModules, IBotTradeOfferResults, IBotUserNotifications {
internal static readonly ConcurrentDictionary RemoteCommunications = new();
- [JsonProperty]
+ [JsonInclude]
+ [Required]
public override string Name => nameof(ItemsMatcherPlugin);
- [JsonProperty]
+ [JsonInclude]
+ [Required]
public override Version Version => typeof(ItemsMatcherPlugin).Assembly.GetName().Version ?? throw new InvalidOperationException(nameof(Version));
public async Task OnBotCommand(Bot bot, EAccess access, string message, string[] args, ulong steamID = 0) {
@@ -83,7 +86,7 @@ internal sealed class ItemsMatcherPlugin : OfficialPlugin, IBot, IBotCommand2, I
return Task.CompletedTask;
}
- public async Task OnBotInitModules(Bot bot, IReadOnlyDictionary? additionalConfigProperties = null) {
+ public async Task OnBotInitModules(Bot bot, IReadOnlyDictionary? additionalConfigProperties = null) {
ArgumentNullException.ThrowIfNull(bot);
if (RemoteCommunications.TryRemove(bot, out RemoteCommunication? remoteCommunication)) {
diff --git a/ArchiSteamFarm.OfficialPlugins.ItemsMatcher/RemoteCommunication.cs b/ArchiSteamFarm.OfficialPlugins.ItemsMatcher/RemoteCommunication.cs
index 6d07da6c9..027b34f75 100644
--- a/ArchiSteamFarm.OfficialPlugins.ItemsMatcher/RemoteCommunication.cs
+++ b/ArchiSteamFarm.OfficialPlugins.ItemsMatcher/RemoteCommunication.cs
@@ -28,6 +28,7 @@ using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
+using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using ArchiSteamFarm.Core;
@@ -43,7 +44,6 @@ using ArchiSteamFarm.Steam.Storage;
using ArchiSteamFarm.Storage;
using ArchiSteamFarm.Web;
using ArchiSteamFarm.Web.Responses;
-using Newtonsoft.Json.Linq;
using SteamKit2;
using SteamKit2.Internal;
@@ -1190,11 +1190,19 @@ internal sealed class RemoteCommunication : IAsyncDisposable, IDisposable {
// Cancel previous trade offers sent and deprioritize SteamIDs that didn't answer us in this round
HashSet? matchActivelyTradeOfferIDs = null;
- JToken? matchActivelyTradeOfferIDsToken = Bot.BotDatabase.LoadFromJsonStorage(MatchActivelyTradeOfferIDsStorageKey);
+ JsonElement matchActivelyTradeOfferIDsToken = Bot.BotDatabase.LoadFromJsonStorage(MatchActivelyTradeOfferIDsStorageKey);
- if (matchActivelyTradeOfferIDsToken != null) {
+ if (matchActivelyTradeOfferIDsToken.ValueKind == JsonValueKind.Array) {
try {
- matchActivelyTradeOfferIDs = matchActivelyTradeOfferIDsToken.ToObject>();
+ matchActivelyTradeOfferIDs = new HashSet(matchActivelyTradeOfferIDsToken.GetArrayLength());
+
+ foreach (JsonElement tradeIDElement in matchActivelyTradeOfferIDsToken.EnumerateArray()) {
+ if (!tradeIDElement.TryGetUInt64(out ulong tradeID)) {
+ continue;
+ }
+
+ matchActivelyTradeOfferIDs.Add(tradeID);
+ }
} catch (Exception e) {
Bot.ArchiLogger.LogGenericWarningException(e);
}
@@ -1223,7 +1231,7 @@ internal sealed class RemoteCommunication : IAsyncDisposable, IDisposable {
matchActivelyTradeOfferIDs = activeTradeOfferIDs;
if (matchActivelyTradeOfferIDs.Count > 0) {
- Bot.BotDatabase.SaveToJsonStorage(MatchActivelyTradeOfferIDsStorageKey, JToken.FromObject(matchActivelyTradeOfferIDs));
+ Bot.BotDatabase.SaveToJsonStorage(MatchActivelyTradeOfferIDsStorageKey, matchActivelyTradeOfferIDs);
} else {
Bot.BotDatabase.DeleteFromJsonStorage(MatchActivelyTradeOfferIDsStorageKey);
}
@@ -1427,7 +1435,7 @@ internal sealed class RemoteCommunication : IAsyncDisposable, IDisposable {
if (tradeOfferIDs?.Count > 0) {
matchActivelyTradeOfferIDs.UnionWith(tradeOfferIDs);
- Bot.BotDatabase.SaveToJsonStorage(MatchActivelyTradeOfferIDsStorageKey, JToken.FromObject(matchActivelyTradeOfferIDs));
+ Bot.BotDatabase.SaveToJsonStorage(MatchActivelyTradeOfferIDsStorageKey, matchActivelyTradeOfferIDs);
}
if (mobileTradeOfferIDs?.Count > 0) {
diff --git a/ArchiSteamFarm.OfficialPlugins.MobileAuthenticator/ArchiSteamFarm.OfficialPlugins.MobileAuthenticator.csproj b/ArchiSteamFarm.OfficialPlugins.MobileAuthenticator/ArchiSteamFarm.OfficialPlugins.MobileAuthenticator.csproj
index d6a3bbc92..264fdafa8 100644
--- a/ArchiSteamFarm.OfficialPlugins.MobileAuthenticator/ArchiSteamFarm.OfficialPlugins.MobileAuthenticator.csproj
+++ b/ArchiSteamFarm.OfficialPlugins.MobileAuthenticator/ArchiSteamFarm.OfficialPlugins.MobileAuthenticator.csproj
@@ -6,7 +6,6 @@
-
diff --git a/ArchiSteamFarm.OfficialPlugins.MobileAuthenticator/Commands.cs b/ArchiSteamFarm.OfficialPlugins.MobileAuthenticator/Commands.cs
index f8ceeae8b..2a1d0394a 100644
--- a/ArchiSteamFarm.OfficialPlugins.MobileAuthenticator/Commands.cs
+++ b/ArchiSteamFarm.OfficialPlugins.MobileAuthenticator/Commands.cs
@@ -27,9 +27,9 @@ using System.IO;
using System.Linq;
using System.Threading.Tasks;
using ArchiSteamFarm.Core;
+using ArchiSteamFarm.Helpers.Json;
using ArchiSteamFarm.Localization;
using ArchiSteamFarm.Steam;
-using Newtonsoft.Json;
using SteamKit2;
using SteamKit2.Internal;
@@ -128,7 +128,7 @@ internal static class Commands {
return bot.Commands.FormatBotResponse(string.Format(CultureInfo.CurrentCulture, Strings.ErrorIsEmpty, nameof(json)));
}
- Steam.Security.MobileAuthenticator? mobileAuthenticator = JsonConvert.DeserializeObject(json);
+ Steam.Security.MobileAuthenticator? mobileAuthenticator = json.ToJsonObject();
if (mobileAuthenticator == null) {
return bot.Commands.FormatBotResponse(string.Format(CultureInfo.CurrentCulture, Strings.ErrorIsEmpty, nameof(json)));
@@ -261,7 +261,7 @@ internal static class Commands {
return bot.Commands.FormatBotResponse(string.Format(CultureInfo.CurrentCulture, Strings.ErrorIsEmpty, nameof(json)));
}
- Steam.Security.MobileAuthenticator? mobileAuthenticator = JsonConvert.DeserializeObject(json);
+ Steam.Security.MobileAuthenticator? mobileAuthenticator = json.ToJsonObject();
if (mobileAuthenticator == null) {
return bot.Commands.FormatBotResponse(string.Format(CultureInfo.CurrentCulture, Strings.ErrorIsEmpty, nameof(json)));
@@ -360,7 +360,7 @@ internal static class Commands {
MaFileData maFileData = new(response, bot.SteamID, deviceID);
string maFilePendingPath = $"{bot.GetFilePath(Bot.EFileType.MobileAuthenticator)}.PENDING";
- string json = JsonConvert.SerializeObject(maFileData, Formatting.Indented);
+ string json = maFileData.ToJsonText(true);
try {
await File.WriteAllTextAsync(maFilePendingPath, json).ConfigureAwait(false);
diff --git a/ArchiSteamFarm.OfficialPlugins.MobileAuthenticator/MaFileData.cs b/ArchiSteamFarm.OfficialPlugins.MobileAuthenticator/MaFileData.cs
index e3e62b46d..65e307a99 100644
--- a/ArchiSteamFarm.OfficialPlugins.MobileAuthenticator/MaFileData.cs
+++ b/ArchiSteamFarm.OfficialPlugins.MobileAuthenticator/MaFileData.cs
@@ -20,48 +20,71 @@
// limitations under the License.
using System;
-using Newtonsoft.Json;
+using System.Text.Json.Serialization;
using SteamKit2;
using SteamKit2.Internal;
namespace ArchiSteamFarm.OfficialPlugins.MobileAuthenticator;
internal sealed class MaFileData {
- [JsonProperty("account_name", Required = Required.Always)]
- internal readonly string AccountName;
+ [JsonInclude]
+ [JsonPropertyName("account_name")]
+ [JsonRequired]
+ internal string AccountName { get; private init; }
- [JsonProperty("device_id", Required = Required.Always)]
- internal readonly string DeviceID;
+ [JsonInclude]
+ [JsonPropertyName("device_id")]
+ [JsonRequired]
+ internal string DeviceID { get; private init; }
- [JsonProperty("identity_secret", Required = Required.Always)]
- internal readonly string IdentitySecret;
+ [JsonInclude]
+ [JsonPropertyName("identity_secret")]
+ [JsonRequired]
+ internal string IdentitySecret { get; private init; }
- [JsonProperty("revocation_code", Required = Required.Always)]
- internal readonly string RevocationCode;
+ [JsonInclude]
+ [JsonPropertyName("revocation_code")]
+ [JsonRequired]
+ internal string RevocationCode { get; private init; }
- [JsonProperty("secret_1", Required = Required.Always)]
- internal readonly string Secret1;
+ [JsonInclude]
+ [JsonPropertyName("secret_1")]
+ [JsonRequired]
+ internal string Secret1 { get; private init; }
- [JsonProperty("serial_number", Required = Required.Always)]
- internal readonly ulong SerialNumber;
+ [JsonInclude]
+ [JsonPropertyName("serial_number")]
+ [JsonRequired]
+ internal ulong SerialNumber { get; private init; }
- [JsonProperty("server_time", Required = Required.Always)]
- internal readonly ulong ServerTime;
+ [JsonInclude]
+ [JsonPropertyName("server_time")]
+ [JsonRequired]
+ internal ulong ServerTime { get; private init; }
- [JsonProperty(Required = Required.Always)]
- internal readonly MaFileSessionData Session;
+ [JsonInclude]
+ [JsonRequired]
+ internal MaFileSessionData Session { get; private init; }
- [JsonProperty("shared_secret", Required = Required.Always)]
- internal readonly string SharedSecret;
+ [JsonInclude]
+ [JsonPropertyName("shared_secret")]
+ [JsonRequired]
+ internal string SharedSecret { get; private init; }
- [JsonProperty("status", Required = Required.Always)]
- internal readonly int Status;
+ [JsonInclude]
+ [JsonPropertyName("status")]
+ [JsonRequired]
+ internal int Status { get; private init; }
- [JsonProperty("token_gid", Required = Required.Always)]
- internal readonly string TokenGid;
+ [JsonInclude]
+ [JsonPropertyName("token_gid")]
+ [JsonRequired]
+ internal string TokenGid { get; private init; }
- [JsonProperty("uri", Required = Required.Always)]
- internal readonly string Uri;
+ [JsonInclude]
+ [JsonPropertyName("uri")]
+ [JsonRequired]
+ internal string Uri { get; private init; }
internal MaFileData(CTwoFactor_AddAuthenticator_Response data, ulong steamID, string deviceID) {
ArgumentNullException.ThrowIfNull(data);
diff --git a/ArchiSteamFarm.OfficialPlugins.MobileAuthenticator/MaFileSessionData.cs b/ArchiSteamFarm.OfficialPlugins.MobileAuthenticator/MaFileSessionData.cs
index 074409794..c3c0e1cfa 100644
--- a/ArchiSteamFarm.OfficialPlugins.MobileAuthenticator/MaFileSessionData.cs
+++ b/ArchiSteamFarm.OfficialPlugins.MobileAuthenticator/MaFileSessionData.cs
@@ -20,14 +20,15 @@
// limitations under the License.
using System;
-using Newtonsoft.Json;
+using System.Text.Json.Serialization;
using SteamKit2;
namespace ArchiSteamFarm.OfficialPlugins.MobileAuthenticator;
internal sealed class MaFileSessionData {
- [JsonProperty(Required = Required.Always)]
- internal readonly ulong SteamID;
+ [JsonInclude]
+ [JsonRequired]
+ internal ulong SteamID { get; private init; }
internal MaFileSessionData(ulong steamID) {
if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) {
diff --git a/ArchiSteamFarm.OfficialPlugins.MobileAuthenticator/MobileAuthenticatorPlugin.cs b/ArchiSteamFarm.OfficialPlugins.MobileAuthenticator/MobileAuthenticatorPlugin.cs
index cd5bb0d90..afaf9aa6b 100644
--- a/ArchiSteamFarm.OfficialPlugins.MobileAuthenticator/MobileAuthenticatorPlugin.cs
+++ b/ArchiSteamFarm.OfficialPlugins.MobileAuthenticator/MobileAuthenticatorPlugin.cs
@@ -22,15 +22,16 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
+using System.ComponentModel.DataAnnotations;
using System.Composition;
using System.Diagnostics.CodeAnalysis;
+using System.Text.Json.Serialization;
using System.Threading.Tasks;
using ArchiSteamFarm.Core;
using ArchiSteamFarm.OfficialPlugins.MobileAuthenticator.Localization;
using ArchiSteamFarm.Plugins;
using ArchiSteamFarm.Plugins.Interfaces;
using ArchiSteamFarm.Steam;
-using Newtonsoft.Json;
using SteamKit2;
namespace ArchiSteamFarm.OfficialPlugins.MobileAuthenticator;
@@ -38,10 +39,12 @@ namespace ArchiSteamFarm.OfficialPlugins.MobileAuthenticator;
[Export(typeof(IPlugin))]
[SuppressMessage("ReSharper", "MemberCanBeFileLocal")]
internal sealed class MobileAuthenticatorPlugin : OfficialPlugin, IBotCommand2, IBotSteamClient {
- [JsonProperty]
+ [JsonInclude]
+ [Required]
public override string Name => nameof(MobileAuthenticatorPlugin);
- [JsonProperty]
+ [JsonInclude]
+ [Required]
public override Version Version => typeof(MobileAuthenticatorPlugin).Assembly.GetName().Version ?? throw new InvalidOperationException(nameof(Version));
public async Task OnBotCommand(Bot bot, EAccess access, string message, string[] args, ulong steamID = 0) {
diff --git a/ArchiSteamFarm.OfficialPlugins.SteamTokenDumper/ArchiSteamFarm.OfficialPlugins.SteamTokenDumper.csproj b/ArchiSteamFarm.OfficialPlugins.SteamTokenDumper/ArchiSteamFarm.OfficialPlugins.SteamTokenDumper.csproj
index fc7c1085d..32d8ba527 100644
--- a/ArchiSteamFarm.OfficialPlugins.SteamTokenDumper/ArchiSteamFarm.OfficialPlugins.SteamTokenDumper.csproj
+++ b/ArchiSteamFarm.OfficialPlugins.SteamTokenDumper/ArchiSteamFarm.OfficialPlugins.SteamTokenDumper.csproj
@@ -6,7 +6,6 @@
-
diff --git a/ArchiSteamFarm.OfficialPlugins.SteamTokenDumper/Data/SubmitRequest.cs b/ArchiSteamFarm.OfficialPlugins.SteamTokenDumper/Data/SubmitRequest.cs
index 2bea64b75..53aa1df13 100644
--- a/ArchiSteamFarm.OfficialPlugins.SteamTokenDumper/Data/SubmitRequest.cs
+++ b/ArchiSteamFarm.OfficialPlugins.SteamTokenDumper/Data/SubmitRequest.cs
@@ -23,34 +23,44 @@ using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Globalization;
+using System.Text.Json.Serialization;
using ArchiSteamFarm.Core;
-using Newtonsoft.Json;
using SteamKit2;
namespace ArchiSteamFarm.OfficialPlugins.SteamTokenDumper.Data;
internal sealed class SubmitRequest {
- [JsonProperty("guid", Required = Required.Always)]
+ [JsonInclude]
+ [JsonPropertyName("guid")]
private static string Guid => ASF.GlobalDatabase?.Identifier.ToString("N") ?? throw new InvalidOperationException(nameof(ASF.GlobalDatabase.Identifier));
- [JsonProperty("token", Required = Required.Always)]
+ [JsonInclude]
+ [JsonPropertyName("token")]
private static string Token => SharedInfo.Token;
- [JsonProperty("v", Required = Required.Always)]
+ [JsonInclude]
+ [JsonPropertyName("v")]
private static byte Version => SharedInfo.ApiVersion;
- [JsonProperty("apps", Required = Required.Always)]
- private readonly ImmutableDictionary Apps;
+ [JsonInclude]
+ [JsonPropertyName("apps")]
+ [JsonRequired]
+ private ImmutableDictionary Apps { get; init; }
- [JsonProperty("depots", Required = Required.Always)]
- private readonly ImmutableDictionary Depots;
+ [JsonInclude]
+ [JsonPropertyName("depots")]
+ [JsonRequired]
+ private ImmutableDictionary Depots { get; init; }
private readonly ulong SteamID;
- [JsonProperty("subs", Required = Required.Always)]
- private readonly ImmutableDictionary Subs;
+ [JsonInclude]
+ [JsonPropertyName("subs")]
+ [JsonRequired]
+ private ImmutableDictionary Subs { get; init; }
- [JsonProperty("steamid", Required = Required.Always)]
+ [JsonInclude]
+ [JsonPropertyName("steamid")]
private string SteamIDText => new SteamID(SteamID).Render();
internal SubmitRequest(ulong steamID, IReadOnlyCollection> apps, IReadOnlyCollection> accessTokens, IReadOnlyCollection> depots) {
diff --git a/ArchiSteamFarm.OfficialPlugins.SteamTokenDumper/Data/SubmitResponse.cs b/ArchiSteamFarm.OfficialPlugins.SteamTokenDumper/Data/SubmitResponse.cs
index 4ac268686..9291494ba 100644
--- a/ArchiSteamFarm.OfficialPlugins.SteamTokenDumper/Data/SubmitResponse.cs
+++ b/ArchiSteamFarm.OfficialPlugins.SteamTokenDumper/Data/SubmitResponse.cs
@@ -20,22 +20,21 @@
// limitations under the License.
using System.Diagnostics.CodeAnalysis;
-using Newtonsoft.Json;
+using System.Text.Json.Serialization;
namespace ArchiSteamFarm.OfficialPlugins.SteamTokenDumper.Data;
#pragma warning disable CA1812 // False positive, the class is used during json deserialization
[SuppressMessage("ReSharper", "ClassCannotBeInstantiated")]
internal sealed class SubmitResponse {
-#pragma warning disable CS0649 // False positive, the field is used during json deserialization
- [JsonProperty("data", Required = Required.DisallowNull)]
- internal readonly SubmitResponseData? Data;
-#pragma warning restore CS0649 // False positive, the field is used during json deserialization
+ [JsonInclude]
+ [JsonPropertyName("data")]
+ internal SubmitResponseData? Data { get; private init; }
-#pragma warning disable CS0649 // False positive, the field is used during json deserialization
- [JsonProperty("success", Required = Required.Always)]
- internal readonly bool Success;
-#pragma warning restore CS0649 // False positive, the field is used during json deserialization
+ [JsonInclude]
+ [JsonPropertyName("success")]
+ [JsonRequired]
+ internal bool Success { get; private init; }
[JsonConstructor]
private SubmitResponse() { }
diff --git a/ArchiSteamFarm.OfficialPlugins.SteamTokenDumper/Data/SubmitResponseData.cs b/ArchiSteamFarm.OfficialPlugins.SteamTokenDumper/Data/SubmitResponseData.cs
index fa3f7c62f..fa85733d6 100644
--- a/ArchiSteamFarm.OfficialPlugins.SteamTokenDumper/Data/SubmitResponseData.cs
+++ b/ArchiSteamFarm.OfficialPlugins.SteamTokenDumper/Data/SubmitResponseData.cs
@@ -20,28 +20,40 @@
// limitations under the License.
using System.Collections.Immutable;
-using Newtonsoft.Json;
+using System.Text.Json.Serialization;
namespace ArchiSteamFarm.OfficialPlugins.SteamTokenDumper.Data;
#pragma warning disable CA1812 // False positive, the class is used during json deserialization
internal sealed class SubmitResponseData {
- [JsonProperty("new_apps", Required = Required.Always)]
- internal readonly ImmutableHashSet NewApps = ImmutableHashSet.Empty;
+ [JsonInclude]
+ [JsonPropertyName("new_apps")]
+ [JsonRequired]
+ internal ImmutableHashSet NewApps { get; private init; } = ImmutableHashSet.Empty;
- [JsonProperty("new_depots", Required = Required.Always)]
- internal readonly ImmutableHashSet NewDepots = ImmutableHashSet.Empty;
+ [JsonInclude]
+ [JsonPropertyName("new_depots")]
+ [JsonRequired]
+ internal ImmutableHashSet NewDepots { get; private init; } = ImmutableHashSet.Empty;
- [JsonProperty("new_subs", Required = Required.Always)]
- internal readonly ImmutableHashSet NewPackages = ImmutableHashSet.Empty;
+ [JsonInclude]
+ [JsonPropertyName("new_subs")]
+ [JsonRequired]
+ internal ImmutableHashSet NewPackages { get; private init; } = ImmutableHashSet.Empty;
- [JsonProperty("verified_apps", Required = Required.Always)]
- internal readonly ImmutableHashSet VerifiedApps = ImmutableHashSet.Empty;
+ [JsonInclude]
+ [JsonPropertyName("verified_apps")]
+ [JsonRequired]
+ internal ImmutableHashSet VerifiedApps { get; private init; } = ImmutableHashSet.Empty;
- [JsonProperty("verified_depots", Required = Required.Always)]
- internal readonly ImmutableHashSet VerifiedDepots = ImmutableHashSet.Empty;
+ [JsonInclude]
+ [JsonPropertyName("verified_depots")]
+ [JsonRequired]
+ internal ImmutableHashSet VerifiedDepots { get; private init; } = ImmutableHashSet.Empty;
- [JsonProperty("verified_subs", Required = Required.Always)]
- internal readonly ImmutableHashSet VerifiedPackages = ImmutableHashSet.Empty;
+ [JsonInclude]
+ [JsonPropertyName("verified_subs")]
+ [JsonRequired]
+ internal ImmutableHashSet VerifiedPackages { get; private init; } = ImmutableHashSet.Empty;
}
#pragma warning restore CA1812 // False positive, the class is used during json deserialization
diff --git a/ArchiSteamFarm.OfficialPlugins.SteamTokenDumper/GlobalCache.cs b/ArchiSteamFarm.OfficialPlugins.SteamTokenDumper/GlobalCache.cs
index 10f1a3b43..2933b3f87 100644
--- a/ArchiSteamFarm.OfficialPlugins.SteamTokenDumper/GlobalCache.cs
+++ b/ArchiSteamFarm.OfficialPlugins.SteamTokenDumper/GlobalCache.cs
@@ -26,14 +26,15 @@ using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
+using System.Text.Json.Serialization;
using System.Threading;
using System.Threading.Tasks;
using ArchiSteamFarm.Core;
using ArchiSteamFarm.Helpers;
+using ArchiSteamFarm.Helpers.Json;
using ArchiSteamFarm.OfficialPlugins.SteamTokenDumper.Localization;
using ArchiSteamFarm.Web.Responses;
using JetBrains.Annotations;
-using Newtonsoft.Json;
using SteamKit2;
namespace ArchiSteamFarm.OfficialPlugins.SteamTokenDumper;
@@ -43,27 +44,34 @@ internal sealed class GlobalCache : SerializableFile {
private static string SharedFilePath => Path.Combine(ArchiSteamFarm.SharedInfo.ConfigDirectory, $"{nameof(SteamTokenDumper)}.cache");
- [JsonProperty(Required = Required.DisallowNull)]
- private readonly ConcurrentDictionary AppChangeNumbers = new();
-
- [JsonProperty(Required = Required.DisallowNull)]
- private readonly ConcurrentDictionary AppTokens = new();
-
- [JsonProperty(Required = Required.DisallowNull)]
- private readonly ConcurrentDictionary DepotKeys = new();
-
- [JsonProperty(Required = Required.DisallowNull)]
- private readonly ConcurrentDictionary SubmittedApps = new();
-
- [JsonProperty(Required = Required.DisallowNull)]
- private readonly ConcurrentDictionary SubmittedDepots = new();
-
- [JsonProperty(Required = Required.DisallowNull)]
- private readonly ConcurrentDictionary SubmittedPackages = new();
-
- [JsonProperty(Required = Required.DisallowNull)]
+ [JsonInclude]
internal uint LastChangeNumber { get; private set; }
+ [JsonDisallowNull]
+ [JsonInclude]
+ private ConcurrentDictionary AppChangeNumbers { get; init; } = new();
+
+ [JsonDisallowNull]
+ [JsonInclude]
+ private ConcurrentDictionary AppTokens { get; init; } = new();
+
+ [JsonDisallowNull]
+ [JsonInclude]
+ private ConcurrentDictionary DepotKeys { get; init; } = new();
+
+ [JsonDisallowNull]
+ [JsonInclude]
+ private ConcurrentDictionary SubmittedApps { get; init; } = new();
+
+ [JsonDisallowNull]
+ [JsonInclude]
+ private ConcurrentDictionary SubmittedDepots { get; init; } = new();
+
+ [JsonDisallowNull]
+ [JsonInclude]
+ private ConcurrentDictionary SubmittedPackages { get; init; } = new();
+
+ [JsonConstructor]
internal GlobalCache() => FilePath = SharedFilePath;
[UsedImplicitly]
@@ -87,6 +95,8 @@ internal sealed class GlobalCache : SerializableFile {
[UsedImplicitly]
public bool ShouldSerializeSubmittedPackages() => !SubmittedPackages.IsEmpty;
+ protected override Task Save() => Save(this);
+
internal ulong GetAppToken(uint appID) => AppTokens[appID];
internal Dictionary GetAppTokensForSubmission() => AppTokens.Where(appToken => (SteamTokenDumperPlugin.Config?.SecretAppIDs.Contains(appToken.Key) != true) && (appToken.Value > 0) && (!SubmittedApps.TryGetValue(appToken.Key, out ulong token) || (appToken.Value != token))).ToDictionary(static appToken => appToken.Key, static appToken => appToken.Value);
@@ -118,7 +128,7 @@ internal sealed class GlobalCache : SerializableFile {
return null;
}
- globalCache = JsonConvert.DeserializeObject(json);
+ globalCache = json.ToJsonObject();
} catch (Exception e) {
ASF.ArchiLogger.LogGenericException(e);
diff --git a/ArchiSteamFarm.OfficialPlugins.SteamTokenDumper/GlobalConfigExtension.cs b/ArchiSteamFarm.OfficialPlugins.SteamTokenDumper/GlobalConfigExtension.cs
index 1826ab845..87550494d 100644
--- a/ArchiSteamFarm.OfficialPlugins.SteamTokenDumper/GlobalConfigExtension.cs
+++ b/ArchiSteamFarm.OfficialPlugins.SteamTokenDumper/GlobalConfigExtension.cs
@@ -19,16 +19,16 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-using Newtonsoft.Json;
+using System.Text.Json.Serialization;
namespace ArchiSteamFarm.OfficialPlugins.SteamTokenDumper;
public sealed class GlobalConfigExtension {
- [JsonProperty]
- public SteamTokenDumperConfig? SteamTokenDumperPlugin { get; private set; }
+ [JsonInclude]
+ public SteamTokenDumperConfig? SteamTokenDumperPlugin { get; private init; }
- [JsonProperty(Required = Required.DisallowNull)]
- public bool SteamTokenDumperPluginEnabled { get; private set; }
+ [JsonInclude]
+ public bool SteamTokenDumperPluginEnabled { get; private init; }
[JsonConstructor]
internal GlobalConfigExtension() { }
diff --git a/ArchiSteamFarm.OfficialPlugins.SteamTokenDumper/SteamTokenDumperConfig.cs b/ArchiSteamFarm.OfficialPlugins.SteamTokenDumper/SteamTokenDumperConfig.cs
index 50ec96503..74482afcf 100644
--- a/ArchiSteamFarm.OfficialPlugins.SteamTokenDumper/SteamTokenDumperConfig.cs
+++ b/ArchiSteamFarm.OfficialPlugins.SteamTokenDumper/SteamTokenDumperConfig.cs
@@ -20,29 +20,33 @@
// limitations under the License.
using System.Collections.Immutable;
+using System.Text.Json.Serialization;
+using ArchiSteamFarm.Helpers.Json;
using ArchiSteamFarm.IPC.Integration;
-using Newtonsoft.Json;
namespace ArchiSteamFarm.OfficialPlugins.SteamTokenDumper;
public sealed class SteamTokenDumperConfig {
- [JsonProperty(Required = Required.DisallowNull)]
+ [JsonInclude]
public bool Enabled { get; internal set; }
- [JsonProperty(Required = Required.DisallowNull)]
+ [JsonDisallowNull]
+ [JsonInclude]
[SwaggerItemsMinMax(MinimumUint = 1, MaximumUint = uint.MaxValue)]
- public ImmutableHashSet SecretAppIDs { get; private set; } = ImmutableHashSet.Empty;
+ public ImmutableHashSet SecretAppIDs { get; private init; } = ImmutableHashSet.Empty;
- [JsonProperty(Required = Required.DisallowNull)]
+ [JsonDisallowNull]
+ [JsonInclude]
[SwaggerItemsMinMax(MinimumUint = 1, MaximumUint = uint.MaxValue)]
- public ImmutableHashSet SecretDepotIDs { get; private set; } = ImmutableHashSet.Empty;
+ public ImmutableHashSet SecretDepotIDs { get; private init; } = ImmutableHashSet.Empty;
- [JsonProperty(Required = Required.DisallowNull)]
+ [JsonDisallowNull]
+ [JsonInclude]
[SwaggerItemsMinMax(MinimumUint = 1, MaximumUint = uint.MaxValue)]
- public ImmutableHashSet SecretPackageIDs { get; private set; } = ImmutableHashSet.Empty;
+ public ImmutableHashSet SecretPackageIDs { get; private init; } = ImmutableHashSet.Empty;
- [JsonProperty(Required = Required.DisallowNull)]
- public bool SkipAutoGrantPackages { get; private set; } = true;
+ [JsonInclude]
+ public bool SkipAutoGrantPackages { get; private init; } = true;
[JsonConstructor]
internal SteamTokenDumperConfig() { }
diff --git a/ArchiSteamFarm.OfficialPlugins.SteamTokenDumper/SteamTokenDumperPlugin.cs b/ArchiSteamFarm.OfficialPlugins.SteamTokenDumper/SteamTokenDumperPlugin.cs
index 0ef21a6dd..5718339c6 100644
--- a/ArchiSteamFarm.OfficialPlugins.SteamTokenDumper/SteamTokenDumperPlugin.cs
+++ b/ArchiSteamFarm.OfficialPlugins.SteamTokenDumper/SteamTokenDumperPlugin.cs
@@ -24,14 +24,18 @@ using System.Collections.Concurrent;
using System.Collections.Frozen;
using System.Collections.Generic;
using System.ComponentModel;
+using System.ComponentModel.DataAnnotations;
using System.Composition;
using System.Globalization;
using System.Linq;
using System.Net;
+using System.Text.Json;
+using System.Text.Json.Serialization;
using System.Threading;
using System.Threading.Tasks;
using ArchiSteamFarm.Core;
using ArchiSteamFarm.Helpers;
+using ArchiSteamFarm.Helpers.Json;
using ArchiSteamFarm.OfficialPlugins.SteamTokenDumper.Data;
using ArchiSteamFarm.OfficialPlugins.SteamTokenDumper.Localization;
using ArchiSteamFarm.Plugins;
@@ -41,8 +45,6 @@ using ArchiSteamFarm.Steam.Interaction;
using ArchiSteamFarm.Storage;
using ArchiSteamFarm.Web;
using ArchiSteamFarm.Web.Responses;
-using Newtonsoft.Json;
-using Newtonsoft.Json.Linq;
using SteamKit2;
namespace ArchiSteamFarm.OfficialPlugins.SteamTokenDumper;
@@ -51,7 +53,7 @@ namespace ArchiSteamFarm.OfficialPlugins.SteamTokenDumper;
internal sealed class SteamTokenDumperPlugin : OfficialPlugin, IASF, IBot, IBotCommand2, IBotSteamClient, ISteamPICSChanges {
private const ushort DepotsRateLimitingDelay = 500;
- [JsonProperty]
+ [JsonInclude]
internal static SteamTokenDumperConfig? Config { get; private set; }
private static readonly ConcurrentDictionary BotSubscriptions = new();
@@ -62,15 +64,17 @@ internal sealed class SteamTokenDumperPlugin : OfficialPlugin, IASF, IBot, IBotC
private static GlobalCache? GlobalCache;
private static DateTimeOffset LastUploadAt = DateTimeOffset.MinValue;
- [JsonProperty]
+ [JsonInclude]
+ [Required]
public override string Name => nameof(SteamTokenDumperPlugin);
- [JsonProperty]
+ [JsonInclude]
+ [Required]
public override Version Version => typeof(SteamTokenDumperPlugin).Assembly.GetName().Version ?? throw new InvalidOperationException(nameof(Version));
public Task GetPreferredChangeNumberToStartFrom() => Task.FromResult(GlobalCache?.LastChangeNumber ?? 0);
- public async Task OnASFInit(IReadOnlyDictionary? additionalConfigProperties = null) {
+ public async Task OnASFInit(IReadOnlyDictionary? additionalConfigProperties = null) {
if (!SharedInfo.HasValidToken) {
ASF.ArchiLogger.LogGenericError(string.Format(CultureInfo.CurrentCulture, Strings.PluginDisabledMissingBuildToken, nameof(SteamTokenDumperPlugin)));
@@ -81,15 +85,19 @@ internal sealed class SteamTokenDumperPlugin : OfficialPlugin, IASF, IBot, IBotC
SteamTokenDumperConfig? config = null;
if (additionalConfigProperties != null) {
- foreach ((string configProperty, JToken configValue) in additionalConfigProperties) {
+ foreach ((string configProperty, JsonElement configValue) in additionalConfigProperties) {
try {
switch (configProperty) {
case nameof(GlobalConfigExtension.SteamTokenDumperPlugin):
- config = configValue.ToObject();
+ config = configValue.ToJsonObject();
break;
- case nameof(GlobalConfigExtension.SteamTokenDumperPluginEnabled):
- isEnabled = configValue.Value();
+ case nameof(GlobalConfigExtension.SteamTokenDumperPluginEnabled) when configValue.ValueKind == JsonValueKind.False:
+ isEnabled = false;
+
+ break;
+ case nameof(GlobalConfigExtension.SteamTokenDumperPluginEnabled) when configValue.ValueKind == JsonValueKind.True:
+ isEnabled = true;
break;
}
diff --git a/ArchiSteamFarm/ArchiSteamFarm.csproj b/ArchiSteamFarm/ArchiSteamFarm.csproj
index 9a1202ff2..14d70d26e 100644
--- a/ArchiSteamFarm/ArchiSteamFarm.csproj
+++ b/ArchiSteamFarm/ArchiSteamFarm.csproj
@@ -17,13 +17,11 @@
-
-
diff --git a/ArchiSteamFarm/Collections/ConcurrentHashSet.cs b/ArchiSteamFarm/Collections/ConcurrentHashSet.cs
index 85abae05e..700e59665 100644
--- a/ArchiSteamFarm/Collections/ConcurrentHashSet.cs
+++ b/ArchiSteamFarm/Collections/ConcurrentHashSet.cs
@@ -28,7 +28,7 @@ using JetBrains.Annotations;
namespace ArchiSteamFarm.Collections;
-public sealed class ConcurrentHashSet : IReadOnlyCollection, ISet where T : notnull {
+public sealed class ConcurrentHashSet : IReadOnlySet, ISet where T : notnull {
[PublicAPI]
public event EventHandler? OnModified;
diff --git a/ArchiSteamFarm/Collections/ConcurrentList.cs b/ArchiSteamFarm/Collections/ConcurrentList.cs
index baaa87452..6b40ed07e 100644
--- a/ArchiSteamFarm/Collections/ConcurrentList.cs
+++ b/ArchiSteamFarm/Collections/ConcurrentList.cs
@@ -27,7 +27,7 @@ using Nito.AsyncEx;
namespace ArchiSteamFarm.Collections;
-internal sealed class ConcurrentList : IList, IReadOnlyList {
+public sealed class ConcurrentList : IList, IReadOnlyList {
[PublicAPI]
public event EventHandler? OnModified;
diff --git a/ArchiSteamFarm/Collections/ObservableConcurrentDictionary.cs b/ArchiSteamFarm/Collections/ObservableConcurrentDictionary.cs
index a201ba17c..f99151eee 100644
--- a/ArchiSteamFarm/Collections/ObservableConcurrentDictionary.cs
+++ b/ArchiSteamFarm/Collections/ObservableConcurrentDictionary.cs
@@ -24,7 +24,6 @@ using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using JetBrains.Annotations;
-using Newtonsoft.Json;
namespace ArchiSteamFarm.Collections;
@@ -42,7 +41,6 @@ public sealed class ObservableConcurrentDictionary : IDictionary Keys => BackingDictionary.Keys;
- [JsonProperty(Required = Required.DisallowNull)]
private readonly ConcurrentDictionary BackingDictionary = new();
int ICollection>.Count => BackingDictionary.Count;
@@ -54,6 +52,7 @@ public sealed class ObservableConcurrentDictionary : IDictionary BackingDictionary[key];
+
set {
if (BackingDictionary.TryGetValue(key, out TValue? savedValue) && EqualityComparer.Default.Equals(savedValue, value)) {
return;
diff --git a/ArchiSteamFarm/Helpers/Json/JsonDisallowNullAttribute.cs b/ArchiSteamFarm/Helpers/Json/JsonDisallowNullAttribute.cs
new file mode 100644
index 000000000..ab5065ba0
--- /dev/null
+++ b/ArchiSteamFarm/Helpers/Json/JsonDisallowNullAttribute.cs
@@ -0,0 +1,30 @@
+// _ _ _ ____ _ _____
+// / \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___
+// / _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \
+// / ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
+// /_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
+// |
+// Copyright 2015-2024 Łukasz "JustArchi" Domeradzki
+// Contact: JustArchi@JustArchi.net
+// |
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// |
+// http://www.apache.org/licenses/LICENSE-2.0
+// |
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+using System;
+using System.Text.Json.Serialization;
+using JetBrains.Annotations;
+
+namespace ArchiSteamFarm.Helpers.Json;
+
+[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
+[PublicAPI]
+public sealed class JsonDisallowNullAttribute : JsonAttribute;
diff --git a/ArchiSteamFarm/Helpers/Json/JsonUtilities.cs b/ArchiSteamFarm/Helpers/Json/JsonUtilities.cs
new file mode 100644
index 000000000..4c34ec1f0
--- /dev/null
+++ b/ArchiSteamFarm/Helpers/Json/JsonUtilities.cs
@@ -0,0 +1,186 @@
+// _ _ _ ____ _ _____
+// / \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___
+// / _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \
+// / ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
+// /_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
+// |
+// Copyright 2015-2024 Łukasz "JustArchi" Domeradzki
+// Contact: JustArchi@JustArchi.net
+// |
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// |
+// http://www.apache.org/licenses/LICENSE-2.0
+// |
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+using System;
+using System.Diagnostics.CodeAnalysis;
+using System.Globalization;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using System.Text.Json;
+using System.Text.Json.Serialization.Metadata;
+using System.Threading;
+using System.Threading.Tasks;
+using ArchiSteamFarm.Localization;
+using JetBrains.Annotations;
+
+namespace ArchiSteamFarm.Helpers.Json;
+
+public static class JsonUtilities {
+ [PublicAPI]
+ public static readonly JsonSerializerOptions DefaultJsonSerialierOptions = CreateDefaultJsonSerializerOptions();
+
+ [PublicAPI]
+ public static readonly JsonSerializerOptions IndentedJsonSerialierOptions = CreateDefaultJsonSerializerOptions(true);
+
+ [PublicAPI]
+ public static JsonElement ToJsonElement(this T obj, bool writeIndented = false) where T : notnull {
+ ArgumentNullException.ThrowIfNull(obj);
+
+ return JsonSerializer.SerializeToElement(obj, writeIndented ? IndentedJsonSerialierOptions : DefaultJsonSerialierOptions);
+ }
+
+ [PublicAPI]
+ public static T? ToJsonObject(this JsonElement jsonElement, CancellationToken cancellationToken = default) => jsonElement.Deserialize(DefaultJsonSerialierOptions);
+
+ [PublicAPI]
+ public static async ValueTask ToJsonObject(this Stream stream, CancellationToken cancellationToken = default) {
+ ArgumentNullException.ThrowIfNull(stream);
+
+ return await JsonSerializer.DeserializeAsync(stream, DefaultJsonSerialierOptions, cancellationToken).ConfigureAwait(false);
+ }
+
+ [PublicAPI]
+ public static T? ToJsonObject([StringSyntax(StringSyntaxAttribute.Json)] this string json) {
+ ArgumentException.ThrowIfNullOrEmpty(json);
+
+ return JsonSerializer.Deserialize(json, DefaultJsonSerialierOptions);
+ }
+
+ [PublicAPI]
+ public static string ToJsonText(this T obj, bool writeIndented = false) => JsonSerializer.Serialize(obj, writeIndented ? IndentedJsonSerialierOptions : DefaultJsonSerialierOptions);
+
+ private static void ApplyCustomModifiers(JsonTypeInfo jsonTypeInfo) {
+ ArgumentNullException.ThrowIfNull(jsonTypeInfo);
+
+ bool potentialDisallowedNullsPossible = false;
+
+ foreach (JsonPropertyInfo property in jsonTypeInfo.Properties) {
+ // All our modifications require a valid Get method on a property
+ if (property.Get == null) {
+ continue;
+ }
+
+ // The object should be validated against potential nulls if at least one property has [JsonDisallowNull] declared, avoid performance penalty otherwise
+ if (property.AttributeProvider?.IsDefined(typeof(JsonDisallowNullAttribute), false) == true) {
+ if (property.PropertyType.IsValueType && (Nullable.GetUnderlyingType(property.PropertyType) == null)) {
+ // We should have no [JsonDisallowNull] declared on non-nullable types, this requires developer correction
+ throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, Strings.WarningUnknownValuePleaseReport, nameof(JsonDisallowNullAttribute), $"{property.Name} ({jsonTypeInfo.Type})"));
+ }
+
+ potentialDisallowedNullsPossible = true;
+ }
+
+ // The property should be checked against ShouldSerialize if there is a valid method to invoke, avoid performance penalty otherwise
+ MethodInfo? shouldSerializeMethod = GetShouldSerializeMethod(jsonTypeInfo.Type, property);
+
+ if (shouldSerializeMethod != null) {
+ property.ShouldSerialize = (parent, _) => ShouldSerialize(shouldSerializeMethod, parent);
+ }
+ }
+
+ if (potentialDisallowedNullsPossible) {
+ jsonTypeInfo.OnDeserialized = OnPotentialDisallowedNullsDeserialized;
+ }
+ }
+
+ private static JsonSerializerOptions CreateDefaultJsonSerializerOptions(bool writeIndented = false) =>
+ new() {
+ AllowTrailingCommas = true,
+ PropertyNamingPolicy = null,
+ ReadCommentHandling = JsonCommentHandling.Skip,
+ TypeInfoResolver = new DefaultJsonTypeInfoResolver { Modifiers = { ApplyCustomModifiers } },
+ WriteIndented = writeIndented
+ };
+
+ [UnconditionalSuppressMessage("AssemblyLoadTrimming", "IL2070", Justification = "We don't care about trimmed methods, it's not like we can make it work differently anyway")]
+ [UnconditionalSuppressMessage("AssemblyLoadTrimming", "IL2075", Justification = "We don't care about trimmed properties, it's not like we can make it work differently anyway")]
+ private static MethodInfo? GetShouldSerializeMethod([SuppressMessage("ReSharper", "SuggestBaseTypeForParameter")] Type parent, JsonPropertyInfo property) {
+ ArgumentNullException.ThrowIfNull(parent);
+ ArgumentNullException.ThrowIfNull(property);
+
+ // Handle most common case where ShouldSerializeXYZ() matches property name
+ MethodInfo? result = parent.GetMethod($"ShouldSerialize{property.Name}", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static, null, Type.EmptyTypes, null);
+
+ if (result?.ReturnType == typeof(bool)) {
+ // Method exists and returns a boolean, that's what we'd like to hear
+ return result;
+ }
+
+ // Handle less common case where ShouldSerializeXYZ() matches original member name
+ PropertyInfo? memberNameProperty = property.GetType().GetProperty("MemberName", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
+
+ if (memberNameProperty == null) {
+ // Should never happen, investigate if it does
+ throw new InvalidOperationException(nameof(memberNameProperty));
+ }
+
+ object? memberNameResult = memberNameProperty.GetValue(property);
+
+ if (memberNameResult is not string memberName) {
+ // Should never happen, investigate if it does
+ throw new InvalidOperationException(nameof(memberName));
+ }
+
+ if (string.IsNullOrEmpty(memberName) || (memberName == property.Name)) {
+ // We don't have anything to work with further, there is no ShouldSerialize() method
+ return null;
+ }
+
+ result = parent.GetMethod($"ShouldSerialize{memberName}", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static, null, Type.EmptyTypes, null);
+
+ // Use alternative method if it exists and returns a boolean
+ return result?.ReturnType == typeof(bool) ? result : null;
+ }
+
+ [UnconditionalSuppressMessage("AssemblyLoadTrimming", "IL2075", Justification = "We don't care about trimmed properties, it's not like we can make it work differently anyway")]
+ private static void OnPotentialDisallowedNullsDeserialized(object obj) {
+ ArgumentNullException.ThrowIfNull(obj);
+
+ Type type = obj.GetType();
+
+ foreach (FieldInfo field in type.GetFields(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static).Where(field => field.IsDefined(typeof(JsonDisallowNullAttribute), false) && (field.GetValue(obj) == null))) {
+ throw new JsonException($"Required field {field.Name} expects a non-null value.");
+ }
+
+ foreach (PropertyInfo property in type.GetProperties(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static).Where(property => (property.GetMethod != null) && property.IsDefined(typeof(JsonDisallowNullAttribute), false) && (property.GetValue(obj) == null))) {
+ throw new JsonException($"Required property {property.Name} expects a non-null value.");
+ }
+ }
+
+ private static bool ShouldSerialize(MethodInfo shouldSerializeMethod, object parent) {
+ ArgumentNullException.ThrowIfNull(shouldSerializeMethod);
+ ArgumentNullException.ThrowIfNull(parent);
+
+ if (shouldSerializeMethod.ReturnType != typeof(bool)) {
+ throw new InvalidOperationException(nameof(shouldSerializeMethod));
+ }
+
+ object? shouldSerialize = shouldSerializeMethod.Invoke(parent, null);
+
+ if (shouldSerialize is not bool result) {
+ // Should not happen, we've already determined we have a method that returns a boolean
+ throw new InvalidOperationException(nameof(shouldSerialize));
+ }
+
+ return result;
+ }
+}
diff --git a/ArchiSteamFarm/Helpers/SerializableFile.cs b/ArchiSteamFarm/Helpers/SerializableFile.cs
index 4a2dc99e6..0894790dc 100644
--- a/ArchiSteamFarm/Helpers/SerializableFile.cs
+++ b/ArchiSteamFarm/Helpers/SerializableFile.cs
@@ -24,7 +24,8 @@ using System.IO;
using System.Threading;
using System.Threading.Tasks;
using ArchiSteamFarm.Core;
-using Newtonsoft.Json;
+using ArchiSteamFarm.Helpers.Json;
+using JetBrains.Annotations;
namespace ArchiSteamFarm.Helpers;
@@ -49,47 +50,60 @@ public abstract class SerializableFile : IDisposable {
}
}
- protected async Task Save() {
- if (string.IsNullOrEmpty(FilePath)) {
+ ///
+ /// Implementing this method in your target class is crucial for providing supported functionality.
+ /// In order to do so, it's enough to call static function from the parent class, providing this as input parameter.
+ /// Afterwards, simply call your function whenever you need to save changes.
+ /// This approach will allow JSON serializer used in the to properly discover all of the properties used in your class.
+ /// Unfortunately, due to STJ's limitations, called by some "security", it's not possible for base class to resolve your properties automatically otherwise.
+ ///
+ /// protected override Task Save() => Save(this);
+ [UsedImplicitly]
+ protected abstract Task Save();
+
+ protected static async Task Save(T serializableFile) where T : SerializableFile {
+ ArgumentNullException.ThrowIfNull(serializableFile);
+
+ if (string.IsNullOrEmpty(serializableFile.FilePath)) {
throw new InvalidOperationException(nameof(FilePath));
}
- if (ReadOnly) {
+ if (serializableFile.ReadOnly) {
return;
}
// ReSharper disable once SuspiciousLockOverSynchronizationPrimitive - this is not a mistake, we need extra synchronization, and we can re-use the semaphore object for that
- lock (FileSemaphore) {
- if (SavingScheduled) {
+ lock (serializableFile.FileSemaphore) {
+ if (serializableFile.SavingScheduled) {
return;
}
- SavingScheduled = true;
+ serializableFile.SavingScheduled = true;
}
- await FileSemaphore.WaitAsync().ConfigureAwait(false);
+ await serializableFile.FileSemaphore.WaitAsync().ConfigureAwait(false);
try {
// ReSharper disable once SuspiciousLockOverSynchronizationPrimitive - this is not a mistake, we need extra synchronization, and we can re-use the semaphore object for that
- lock (FileSemaphore) {
- SavingScheduled = false;
+ lock (serializableFile.FileSemaphore) {
+ serializableFile.SavingScheduled = false;
}
- if (ReadOnly) {
+ if (serializableFile.ReadOnly) {
return;
}
- string json = JsonConvert.SerializeObject(this, Debugging.IsUserDebugging ? Formatting.Indented : Formatting.None);
+ string json = serializableFile.ToJsonText(Debugging.IsUserDebugging);
if (string.IsNullOrEmpty(json)) {
throw new InvalidOperationException(nameof(json));
}
// We always want to write entire content to temporary file first, in order to never load corrupted data, also when target file doesn't exist
- string newFilePath = $"{FilePath}.new";
+ string newFilePath = $"{serializableFile.FilePath}.new";
- if (File.Exists(FilePath)) {
- string currentJson = await File.ReadAllTextAsync(FilePath).ConfigureAwait(false);
+ if (File.Exists(serializableFile.FilePath)) {
+ string currentJson = await File.ReadAllTextAsync(serializableFile.FilePath).ConfigureAwait(false);
if (json == currentJson) {
return;
@@ -97,16 +111,16 @@ public abstract class SerializableFile : IDisposable {
await File.WriteAllTextAsync(newFilePath, json).ConfigureAwait(false);
- File.Replace(newFilePath, FilePath, null);
+ File.Replace(newFilePath, serializableFile.FilePath, null);
} else {
await File.WriteAllTextAsync(newFilePath, json).ConfigureAwait(false);
- File.Move(newFilePath, FilePath);
+ File.Move(newFilePath, serializableFile.FilePath);
}
} catch (Exception e) {
ASF.ArchiLogger.LogGenericException(e);
} finally {
- FileSemaphore.Release();
+ serializableFile.FileSemaphore.Release();
}
}
diff --git a/ArchiSteamFarm/IPC/ArchiKestrel.cs b/ArchiSteamFarm/IPC/ArchiKestrel.cs
index 03fc74208..1bc14c8dc 100644
--- a/ArchiSteamFarm/IPC/ArchiKestrel.cs
+++ b/ArchiSteamFarm/IPC/ArchiKestrel.cs
@@ -21,8 +21,10 @@
using System;
using System.IO;
+using System.Text.Json.Nodes;
using System.Threading.Tasks;
using ArchiSteamFarm.Core;
+using ArchiSteamFarm.Helpers.Json;
using ArchiSteamFarm.IPC.Controllers.Api;
using ArchiSteamFarm.Localization;
using ArchiSteamFarm.NLog;
@@ -31,8 +33,6 @@ using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
-using Newtonsoft.Json;
-using Newtonsoft.Json.Linq;
using NLog.Web;
namespace ArchiSteamFarm.IPC;
@@ -83,9 +83,9 @@ internal static class ArchiKestrel {
string json = await File.ReadAllTextAsync(customConfigPath).ConfigureAwait(false);
if (!string.IsNullOrEmpty(json)) {
- JObject jObject = JObject.Parse(json);
+ JsonNode? jsonNode = JsonNode.Parse(json);
- ASF.ArchiLogger.LogGenericDebug($"{SharedInfo.IPCConfigFile}: {jObject.ToString(Formatting.Indented)}");
+ ASF.ArchiLogger.LogGenericDebug($"{SharedInfo.IPCConfigFile}: {jsonNode?.ToJsonText(true) ?? "null"}");
}
} catch (Exception e) {
ASF.ArchiLogger.LogGenericException(e);
diff --git a/ArchiSteamFarm/IPC/Controllers/Api/ASFController.cs b/ArchiSteamFarm/IPC/Controllers/Api/ASFController.cs
index 474a97856..e45cbe23f 100644
--- a/ArchiSteamFarm/IPC/Controllers/Api/ASFController.cs
+++ b/ArchiSteamFarm/IPC/Controllers/Api/ASFController.cs
@@ -24,6 +24,7 @@ using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Net;
+using System.Text.Json;
using System.Threading.Tasks;
using ArchiSteamFarm.Core;
using ArchiSteamFarm.IPC.Requests;
@@ -32,7 +33,6 @@ using ArchiSteamFarm.Localization;
using ArchiSteamFarm.Steam.Interaction;
using ArchiSteamFarm.Storage;
using Microsoft.AspNetCore.Mvc;
-using Newtonsoft.Json.Linq;
namespace ArchiSteamFarm.IPC.Controllers.Api;
@@ -124,9 +124,9 @@ public sealed class ASFController : ArchiController {
}
if (ASF.GlobalConfig.AdditionalProperties is { Count: > 0 }) {
- request.GlobalConfig.AdditionalProperties ??= new Dictionary(ASF.GlobalConfig.AdditionalProperties.Count, ASF.GlobalConfig.AdditionalProperties.Comparer);
+ request.GlobalConfig.AdditionalProperties ??= new Dictionary(ASF.GlobalConfig.AdditionalProperties.Count, ASF.GlobalConfig.AdditionalProperties.Comparer);
- foreach ((string key, JToken value) in ASF.GlobalConfig.AdditionalProperties.Where(property => !request.GlobalConfig.AdditionalProperties.ContainsKey(property.Key))) {
+ foreach ((string key, JsonElement value) in ASF.GlobalConfig.AdditionalProperties.Where(property => !request.GlobalConfig.AdditionalProperties.ContainsKey(property.Key))) {
request.GlobalConfig.AdditionalProperties.Add(key, value);
}
diff --git a/ArchiSteamFarm/IPC/Controllers/Api/BotController.cs b/ArchiSteamFarm/IPC/Controllers/Api/BotController.cs
index 100ecec81..717c2b5ad 100644
--- a/ArchiSteamFarm/IPC/Controllers/Api/BotController.cs
+++ b/ArchiSteamFarm/IPC/Controllers/Api/BotController.cs
@@ -25,6 +25,7 @@ using System.Collections.Specialized;
using System.Globalization;
using System.Linq;
using System.Net;
+using System.Text.Json;
using System.Threading.Tasks;
using ArchiSteamFarm.Core;
using ArchiSteamFarm.IPC.Requests;
@@ -33,7 +34,6 @@ using ArchiSteamFarm.Localization;
using ArchiSteamFarm.Steam;
using ArchiSteamFarm.Steam.Storage;
using Microsoft.AspNetCore.Mvc;
-using Newtonsoft.Json.Linq;
using SteamKit2.Internal;
namespace ArchiSteamFarm.IPC.Controllers.Api;
@@ -127,9 +127,9 @@ public sealed class BotController : ArchiController {
}
if (bot.BotConfig.AdditionalProperties?.Count > 0) {
- request.BotConfig.AdditionalProperties ??= new Dictionary(bot.BotConfig.AdditionalProperties.Count, bot.BotConfig.AdditionalProperties.Comparer);
+ request.BotConfig.AdditionalProperties ??= new Dictionary(bot.BotConfig.AdditionalProperties.Count, bot.BotConfig.AdditionalProperties.Comparer);
- foreach ((string key, JToken value) in bot.BotConfig.AdditionalProperties.Where(property => !request.BotConfig.AdditionalProperties.ContainsKey(property.Key))) {
+ foreach ((string key, JsonElement value) in bot.BotConfig.AdditionalProperties.Where(property => !request.BotConfig.AdditionalProperties.ContainsKey(property.Key))) {
request.BotConfig.AdditionalProperties.Add(key, value);
}
diff --git a/ArchiSteamFarm/IPC/Controllers/Api/NLogController.cs b/ArchiSteamFarm/IPC/Controllers/Api/NLogController.cs
index 229849b89..e7e7e52b8 100644
--- a/ArchiSteamFarm/IPC/Controllers/Api/NLogController.cs
+++ b/ArchiSteamFarm/IPC/Controllers/Api/NLogController.cs
@@ -30,13 +30,13 @@ using System.Text;
using System.Threading;
using System.Threading.Tasks;
using ArchiSteamFarm.Core;
+using ArchiSteamFarm.Helpers.Json;
using ArchiSteamFarm.IPC.Responses;
using ArchiSteamFarm.Localization;
using ArchiSteamFarm.NLog;
using ArchiSteamFarm.NLog.Targets;
using Microsoft.AspNetCore.Connections;
using Microsoft.AspNetCore.Mvc;
-using Newtonsoft.Json;
namespace ArchiSteamFarm.IPC.Controllers.Api;
@@ -156,7 +156,7 @@ public sealed class NLogController : ArchiController {
return;
}
- string json = JsonConvert.SerializeObject(new GenericResponse(newHistoryEntryArgs.Message));
+ string json = new GenericResponse(newHistoryEntryArgs.Message).ToJsonText();
await Task.WhenAll(ActiveLogWebSockets.Where(static kv => kv.Key.State == WebSocketState.Open).Select(kv => PostLoggedJsonUpdate(kv.Key, json, kv.Value.Semaphore, kv.Value.CancellationToken))).ConfigureAwait(false);
}
@@ -206,7 +206,7 @@ public sealed class NLogController : ArchiController {
return;
}
- string response = JsonConvert.SerializeObject(new GenericResponse(loggedMessage));
+ string response = new GenericResponse(loggedMessage).ToJsonText();
await PostLoggedJsonUpdate(webSocket, response, sendSemaphore, cancellationToken).ConfigureAwait(false);
}
diff --git a/ArchiSteamFarm/IPC/Controllers/Api/StorageController.cs b/ArchiSteamFarm/IPC/Controllers/Api/StorageController.cs
index 05e8f5fdd..5664224f5 100644
--- a/ArchiSteamFarm/IPC/Controllers/Api/StorageController.cs
+++ b/ArchiSteamFarm/IPC/Controllers/Api/StorageController.cs
@@ -21,10 +21,10 @@
using System;
using System.Net;
+using System.Text.Json;
using ArchiSteamFarm.Core;
using ArchiSteamFarm.IPC.Responses;
using Microsoft.AspNetCore.Mvc;
-using Newtonsoft.Json.Linq;
namespace ArchiSteamFarm.IPC.Controllers.Api;
@@ -51,7 +51,7 @@ public sealed class StorageController : ArchiController {
/// Loads entry under specified key from ASF's persistent KeyValue JSON storage.
///
[HttpGet]
- [ProducesResponseType>((int) HttpStatusCode.OK)]
+ [ProducesResponseType>((int) HttpStatusCode.OK)]
public ActionResult StorageGet(string key) {
ArgumentException.ThrowIfNullOrEmpty(key);
@@ -59,9 +59,9 @@ public sealed class StorageController : ArchiController {
throw new InvalidOperationException(nameof(ASF.GlobalDatabase));
}
- JToken? value = ASF.GlobalDatabase.LoadFromJsonStorage(key);
+ JsonElement value = ASF.GlobalDatabase.LoadFromJsonStorage(key);
- return Ok(new GenericResponse(true, value));
+ return Ok(new GenericResponse(true, value.ValueKind != JsonValueKind.Undefined ? value : null));
}
///
@@ -70,15 +70,18 @@ public sealed class StorageController : ArchiController {
[Consumes("application/json")]
[HttpPost]
[ProducesResponseType((int) HttpStatusCode.OK)]
- public ActionResult StoragePost(string key, [FromBody] JToken value) {
+ public ActionResult StoragePost(string key, [FromBody] JsonElement value) {
ArgumentException.ThrowIfNullOrEmpty(key);
- ArgumentNullException.ThrowIfNull(value);
+
+ if (value.ValueKind == JsonValueKind.Undefined) {
+ throw new ArgumentOutOfRangeException(nameof(value));
+ }
if (ASF.GlobalDatabase == null) {
throw new InvalidOperationException(nameof(ASF.GlobalDatabase));
}
- if (value.Type == JTokenType.Null) {
+ if (value.ValueKind == JsonValueKind.Null) {
ASF.GlobalDatabase.DeleteFromJsonStorage(key);
} else {
ASF.GlobalDatabase.SaveToJsonStorage(key, value);
diff --git a/ArchiSteamFarm/IPC/Controllers/Api/TypeController.cs b/ArchiSteamFarm/IPC/Controllers/Api/TypeController.cs
index 7aa9dca01..eaebb2eb8 100644
--- a/ArchiSteamFarm/IPC/Controllers/Api/TypeController.cs
+++ b/ArchiSteamFarm/IPC/Controllers/Api/TypeController.cs
@@ -26,11 +26,11 @@ using System.Globalization;
using System.Linq;
using System.Net;
using System.Reflection;
+using System.Text.Json.Serialization;
using ArchiSteamFarm.Core;
using ArchiSteamFarm.IPC.Responses;
using ArchiSteamFarm.Localization;
using Microsoft.AspNetCore.Mvc;
-using Newtonsoft.Json;
namespace ArchiSteamFarm.IPC.Controllers.Api;
@@ -63,27 +63,35 @@ public sealed class TypeController : ArchiController {
if (targetType.IsClass) {
foreach (FieldInfo field in targetType.GetFields(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public).Where(static field => !field.IsPrivate)) {
- JsonPropertyAttribute? jsonProperty = field.GetCustomAttribute();
-
- if (jsonProperty != null) {
- string? unifiedName = field.FieldType.GetUnifiedName();
-
- if (!string.IsNullOrEmpty(unifiedName)) {
- body[jsonProperty.PropertyName ?? field.Name] = unifiedName;
- }
+ if (!field.IsDefined(typeof(JsonIncludeAttribute), false) || field.IsDefined(typeof(JsonExtensionDataAttribute), false)) {
+ continue;
}
+
+ string? unifiedName = field.FieldType.GetUnifiedName();
+
+ if (string.IsNullOrEmpty(unifiedName)) {
+ continue;
+ }
+
+ JsonPropertyNameAttribute? jsonPropertyName = field.GetCustomAttribute();
+
+ body[jsonPropertyName?.Name ?? field.Name] = unifiedName;
}
foreach (PropertyInfo property in targetType.GetProperties(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public).Where(static property => property is { CanRead: true, GetMethod.IsPrivate: false })) {
- JsonPropertyAttribute? jsonProperty = property.GetCustomAttribute();
-
- if (jsonProperty != null) {
- string? unifiedName = property.PropertyType.GetUnifiedName();
-
- if (!string.IsNullOrEmpty(unifiedName)) {
- body[jsonProperty.PropertyName ?? property.Name] = unifiedName;
- }
+ if (!property.IsDefined(typeof(JsonIncludeAttribute), false) || property.IsDefined(typeof(JsonExtensionDataAttribute), false)) {
+ continue;
}
+
+ string? unifiedName = property.PropertyType.GetUnifiedName();
+
+ if (string.IsNullOrEmpty(unifiedName)) {
+ continue;
+ }
+
+ JsonPropertyNameAttribute? jsonPropertyName = property.GetCustomAttribute();
+
+ body[jsonPropertyName?.Name ?? property.Name] = unifiedName;
}
} else if (targetType.IsEnum) {
Type enumType = Enum.GetUnderlyingType(targetType);
diff --git a/ArchiSteamFarm/IPC/Integration/ApiAuthenticationMiddleware.cs b/ArchiSteamFarm/IPC/Integration/ApiAuthenticationMiddleware.cs
index ba36ab6ef..9e6af20f6 100644
--- a/ArchiSteamFarm/IPC/Integration/ApiAuthenticationMiddleware.cs
+++ b/ArchiSteamFarm/IPC/Integration/ApiAuthenticationMiddleware.cs
@@ -68,7 +68,7 @@ internal sealed class ApiAuthenticationMiddleware {
}
[UsedImplicitly]
- public async Task InvokeAsync(HttpContext context, IOptions jsonOptions) {
+ public async Task InvokeAsync(HttpContext context, IOptions jsonOptions) {
ArgumentNullException.ThrowIfNull(context);
ArgumentNullException.ThrowIfNull(jsonOptions);
@@ -84,7 +84,7 @@ internal sealed class ApiAuthenticationMiddleware {
StatusCodeResponse statusCodeResponse = new(statusCode, permanent);
- await context.Response.WriteJsonAsync(new GenericResponse(false, statusCodeResponse), jsonOptions.Value.SerializerSettings).ConfigureAwait(false);
+ await context.Response.WriteAsJsonAsync(new GenericResponse(false, statusCodeResponse), jsonOptions.Value.JsonSerializerOptions).ConfigureAwait(false);
}
internal static void ClearFailedAuthorizations(object? state = null) => FailedAuthorizations.Clear();
diff --git a/ArchiSteamFarm/IPC/Integration/ReadOnlyFixesSchemaFilter.cs b/ArchiSteamFarm/IPC/Integration/ReadOnlyFixesSchemaFilter.cs
new file mode 100644
index 000000000..daa3413a8
--- /dev/null
+++ b/ArchiSteamFarm/IPC/Integration/ReadOnlyFixesSchemaFilter.cs
@@ -0,0 +1,40 @@
+// _ _ _ ____ _ _____
+// / \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___
+// / _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \
+// / ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
+// /_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
+// |
+// Copyright 2015-2024 Łukasz "JustArchi" Domeradzki
+// Contact: JustArchi@JustArchi.net
+// |
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// |
+// http://www.apache.org/licenses/LICENSE-2.0
+// |
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+using System;
+using System.Reflection;
+using JetBrains.Annotations;
+using Microsoft.OpenApi.Models;
+using Swashbuckle.AspNetCore.SwaggerGen;
+
+namespace ArchiSteamFarm.IPC.Integration;
+
+[UsedImplicitly]
+internal sealed class ReadOnlyFixesSchemaFilter : ISchemaFilter {
+ public void Apply(OpenApiSchema schema, SchemaFilterContext context) {
+ ArgumentNullException.ThrowIfNull(schema);
+ ArgumentNullException.ThrowIfNull(context);
+
+ if (schema.ReadOnly && context.MemberInfo is PropertyInfo { CanWrite: true }) {
+ schema.ReadOnly = false;
+ }
+ }
+}
diff --git a/ArchiSteamFarm/IPC/Integration/SwaggerSteamIdentifierAttribute.cs b/ArchiSteamFarm/IPC/Integration/SwaggerSteamIdentifierAttribute.cs
index 44c7a3836..3b3707a52 100644
--- a/ArchiSteamFarm/IPC/Integration/SwaggerSteamIdentifierAttribute.cs
+++ b/ArchiSteamFarm/IPC/Integration/SwaggerSteamIdentifierAttribute.cs
@@ -28,10 +28,10 @@ namespace ArchiSteamFarm.IPC.Integration;
[PublicAPI]
public sealed class SwaggerSteamIdentifierAttribute : CustomSwaggerAttribute {
- public EAccountType AccountType { get; set; } = EAccountType.Individual;
- public uint MaximumAccountID { get; set; } = uint.MaxValue;
- public uint MinimumAccountID { get; set; } = 1;
- public EUniverse Universe { get; set; } = EUniverse.Public;
+ public EAccountType AccountType { get; init; } = EAccountType.Individual;
+ public uint MaximumAccountID { get; init; } = uint.MaxValue;
+ public uint MinimumAccountID { get; init; } = 1;
+ public EUniverse Universe { get; init; } = EUniverse.Public;
public override void Apply(OpenApiSchema schema) {
ArgumentNullException.ThrowIfNull(schema);
diff --git a/ArchiSteamFarm/IPC/Integration/SwaggerValidValuesAttribute.cs b/ArchiSteamFarm/IPC/Integration/SwaggerValidValuesAttribute.cs
index 5a062f2cc..43b4f664e 100644
--- a/ArchiSteamFarm/IPC/Integration/SwaggerValidValuesAttribute.cs
+++ b/ArchiSteamFarm/IPC/Integration/SwaggerValidValuesAttribute.cs
@@ -30,8 +30,8 @@ namespace ArchiSteamFarm.IPC.Integration;
[PublicAPI]
public sealed class SwaggerValidValuesAttribute : CustomSwaggerAttribute {
- public int[]? ValidIntValues { get; set; }
- public string[]? ValidStringValues { get; set; }
+ public int[]? ValidIntValues { get; init; }
+ public string[]? ValidStringValues { get; init; }
public override void Apply(OpenApiSchema schema) {
ArgumentNullException.ThrowIfNull(schema);
diff --git a/ArchiSteamFarm/IPC/Requests/ASFEncryptRequest.cs b/ArchiSteamFarm/IPC/Requests/ASFEncryptRequest.cs
index 1eb6e8e8a..cc9506f26 100644
--- a/ArchiSteamFarm/IPC/Requests/ASFEncryptRequest.cs
+++ b/ArchiSteamFarm/IPC/Requests/ASFEncryptRequest.cs
@@ -21,8 +21,8 @@
using System.ComponentModel.DataAnnotations;
using System.Diagnostics.CodeAnalysis;
+using System.Text.Json.Serialization;
using ArchiSteamFarm.Helpers;
-using Newtonsoft.Json;
namespace ArchiSteamFarm.IPC.Requests;
@@ -31,16 +31,18 @@ public sealed class ASFEncryptRequest {
///
/// Encryption method used for encrypting this string.
///
- [JsonProperty(Required = Required.Always)]
+ [JsonInclude]
+ [JsonRequired]
[Required]
- public ArchiCryptoHelper.ECryptoMethod CryptoMethod { get; private set; }
+ public ArchiCryptoHelper.ECryptoMethod CryptoMethod { get; private init; }
///
/// String to encrypt with provided .
///
- [JsonProperty(Required = Required.Always)]
+ [JsonInclude]
+ [JsonRequired]
[Required]
- public string StringToEncrypt { get; private set; } = "";
+ public string StringToEncrypt { get; private init; } = "";
[JsonConstructor]
private ASFEncryptRequest() { }
diff --git a/ArchiSteamFarm/IPC/Requests/ASFHashRequest.cs b/ArchiSteamFarm/IPC/Requests/ASFHashRequest.cs
index 8496af8a5..bf43e25d9 100644
--- a/ArchiSteamFarm/IPC/Requests/ASFHashRequest.cs
+++ b/ArchiSteamFarm/IPC/Requests/ASFHashRequest.cs
@@ -21,8 +21,8 @@
using System.ComponentModel.DataAnnotations;
using System.Diagnostics.CodeAnalysis;
+using System.Text.Json.Serialization;
using ArchiSteamFarm.Helpers;
-using Newtonsoft.Json;
namespace ArchiSteamFarm.IPC.Requests;
@@ -31,16 +31,18 @@ public sealed class ASFHashRequest {
///
/// Hashing method used for hashing this string.
///
- [JsonProperty(Required = Required.Always)]
+ [JsonInclude]
+ [JsonRequired]
[Required]
- public ArchiCryptoHelper.EHashingMethod HashingMethod { get; private set; }
+ public ArchiCryptoHelper.EHashingMethod HashingMethod { get; private init; }
///
/// String to hash with provided .
///
- [JsonProperty(Required = Required.Always)]
+ [JsonInclude]
+ [JsonRequired]
[Required]
- public string StringToHash { get; private set; } = "";
+ public string StringToHash { get; private init; } = "";
[JsonConstructor]
private ASFHashRequest() { }
diff --git a/ArchiSteamFarm/IPC/Requests/ASFRequest.cs b/ArchiSteamFarm/IPC/Requests/ASFRequest.cs
index ca66e7a59..b0d072aa5 100644
--- a/ArchiSteamFarm/IPC/Requests/ASFRequest.cs
+++ b/ArchiSteamFarm/IPC/Requests/ASFRequest.cs
@@ -21,8 +21,8 @@
using System.ComponentModel.DataAnnotations;
using System.Diagnostics.CodeAnalysis;
+using System.Text.Json.Serialization;
using ArchiSteamFarm.Storage;
-using Newtonsoft.Json;
namespace ArchiSteamFarm.IPC.Requests;
@@ -31,9 +31,10 @@ public sealed class ASFRequest {
///
/// ASF's global config structure.
///
- [JsonProperty(Required = Required.Always)]
+ [JsonInclude]
+ [JsonRequired]
[Required]
- public GlobalConfig GlobalConfig { get; private set; } = new();
+ public GlobalConfig GlobalConfig { get; private init; } = new();
[JsonConstructor]
private ASFRequest() { }
diff --git a/ArchiSteamFarm/IPC/Requests/BotGamesToRedeemInBackgroundRequest.cs b/ArchiSteamFarm/IPC/Requests/BotGamesToRedeemInBackgroundRequest.cs
index 896213a5f..f04ccb04f 100644
--- a/ArchiSteamFarm/IPC/Requests/BotGamesToRedeemInBackgroundRequest.cs
+++ b/ArchiSteamFarm/IPC/Requests/BotGamesToRedeemInBackgroundRequest.cs
@@ -22,7 +22,7 @@
using System.Collections.Specialized;
using System.ComponentModel.DataAnnotations;
using System.Diagnostics.CodeAnalysis;
-using Newtonsoft.Json;
+using System.Text.Json.Serialization;
namespace ArchiSteamFarm.IPC.Requests;
@@ -35,9 +35,10 @@ public sealed class BotGamesToRedeemInBackgroundRequest {
/// Key in the map must be a valid and unique Steam cd-key.
/// Value in the map must be a non-null and non-empty name of the key (e.g. game's name, but can be anything).
///
- [JsonProperty(Required = Required.Always)]
+ [JsonInclude]
+ [JsonRequired]
[Required]
- public OrderedDictionary GamesToRedeemInBackground { get; private set; } = new();
+ public OrderedDictionary GamesToRedeemInBackground { get; private init; } = new();
[JsonConstructor]
private BotGamesToRedeemInBackgroundRequest() { }
diff --git a/ArchiSteamFarm/IPC/Requests/BotInputRequest.cs b/ArchiSteamFarm/IPC/Requests/BotInputRequest.cs
index 878385802..30c953e2d 100644
--- a/ArchiSteamFarm/IPC/Requests/BotInputRequest.cs
+++ b/ArchiSteamFarm/IPC/Requests/BotInputRequest.cs
@@ -19,9 +19,10 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+using System.ComponentModel.DataAnnotations;
using System.Diagnostics.CodeAnalysis;
+using System.Text.Json.Serialization;
using ArchiSteamFarm.Core;
-using Newtonsoft.Json;
namespace ArchiSteamFarm.IPC.Requests;
@@ -30,14 +31,18 @@ public sealed class BotInputRequest {
///
/// Specifies the type of the input.
///
- [JsonProperty(Required = Required.Always)]
- public ASF.EUserInputType Type { get; private set; }
+ [JsonInclude]
+ [JsonRequired]
+ [Required]
+ public ASF.EUserInputType Type { get; private init; }
///
/// Specifies the value for given input type (declared in )
///
- [JsonProperty(Required = Required.Always)]
- public string Value { get; private set; } = "";
+ [JsonInclude]
+ [JsonRequired]
+ [Required]
+ public string Value { get; private init; } = "";
[JsonConstructor]
private BotInputRequest() { }
diff --git a/ArchiSteamFarm/IPC/Requests/BotPauseRequest.cs b/ArchiSteamFarm/IPC/Requests/BotPauseRequest.cs
index d62d635d8..de3de13a8 100644
--- a/ArchiSteamFarm/IPC/Requests/BotPauseRequest.cs
+++ b/ArchiSteamFarm/IPC/Requests/BotPauseRequest.cs
@@ -20,7 +20,7 @@
// limitations under the License.
using System.Diagnostics.CodeAnalysis;
-using Newtonsoft.Json;
+using System.Text.Json.Serialization;
namespace ArchiSteamFarm.IPC.Requests;
@@ -29,14 +29,14 @@ public sealed class BotPauseRequest {
///
/// Specifies if pause is permanent or temporary (default).
///
- [JsonProperty(Required = Required.DisallowNull)]
- public bool Permanent { get; private set; }
+ [JsonInclude]
+ public bool Permanent { get; private init; }
///
/// Specifies automatic resume action in given seconds. Default value of 0 disables automatic resume.
///
- [JsonProperty(Required = Required.DisallowNull)]
- public ushort ResumeInSeconds { get; private set; }
+ [JsonInclude]
+ public ushort ResumeInSeconds { get; private init; }
[JsonConstructor]
private BotPauseRequest() { }
diff --git a/ArchiSteamFarm/IPC/Requests/BotRedeemRequest.cs b/ArchiSteamFarm/IPC/Requests/BotRedeemRequest.cs
index 1584587cc..b97c2c054 100644
--- a/ArchiSteamFarm/IPC/Requests/BotRedeemRequest.cs
+++ b/ArchiSteamFarm/IPC/Requests/BotRedeemRequest.cs
@@ -22,7 +22,7 @@
using System.Collections.Immutable;
using System.ComponentModel.DataAnnotations;
using System.Diagnostics.CodeAnalysis;
-using Newtonsoft.Json;
+using System.Text.Json.Serialization;
namespace ArchiSteamFarm.IPC.Requests;
@@ -31,9 +31,10 @@ public sealed class BotRedeemRequest {
///
/// A collection (set) of keys to redeem.
///
- [JsonProperty(Required = Required.Always)]
+ [JsonInclude]
+ [JsonRequired]
[Required]
- public ImmutableHashSet KeysToRedeem { get; private set; } = ImmutableHashSet.Empty;
+ public ImmutableHashSet KeysToRedeem { get; private init; } = ImmutableHashSet.Empty;
[JsonConstructor]
private BotRedeemRequest() { }
diff --git a/ArchiSteamFarm/IPC/Requests/BotRenameRequest.cs b/ArchiSteamFarm/IPC/Requests/BotRenameRequest.cs
index cf8891686..e4bda406a 100644
--- a/ArchiSteamFarm/IPC/Requests/BotRenameRequest.cs
+++ b/ArchiSteamFarm/IPC/Requests/BotRenameRequest.cs
@@ -21,7 +21,7 @@
using System.ComponentModel.DataAnnotations;
using System.Diagnostics.CodeAnalysis;
-using Newtonsoft.Json;
+using System.Text.Json.Serialization;
namespace ArchiSteamFarm.IPC.Requests;
@@ -30,9 +30,10 @@ public sealed class BotRenameRequest {
///
/// Specifies the new name for the bot. The new name can't be "ASF", neither the one used by any existing bot.
///
- [JsonProperty(Required = Required.Always)]
+ [JsonInclude]
+ [JsonRequired]
[Required]
- public string NewName { get; private set; } = "";
+ public string NewName { get; private init; } = "";
[JsonConstructor]
private BotRenameRequest() { }
diff --git a/ArchiSteamFarm/IPC/Requests/BotRequest.cs b/ArchiSteamFarm/IPC/Requests/BotRequest.cs
index abefd5811..a5790909a 100644
--- a/ArchiSteamFarm/IPC/Requests/BotRequest.cs
+++ b/ArchiSteamFarm/IPC/Requests/BotRequest.cs
@@ -21,8 +21,8 @@
using System.ComponentModel.DataAnnotations;
using System.Diagnostics.CodeAnalysis;
+using System.Text.Json.Serialization;
using ArchiSteamFarm.Steam.Storage;
-using Newtonsoft.Json;
namespace ArchiSteamFarm.IPC.Requests;
@@ -31,9 +31,10 @@ public sealed class BotRequest {
///
/// ASF's bot config structure.
///
- [JsonProperty(Required = Required.Always)]
+ [JsonInclude]
+ [JsonRequired]
[Required]
- public BotConfig BotConfig { get; private set; } = new();
+ public BotConfig BotConfig { get; private init; } = new();
[JsonConstructor]
private BotRequest() { }
diff --git a/ArchiSteamFarm/IPC/Requests/CommandRequest.cs b/ArchiSteamFarm/IPC/Requests/CommandRequest.cs
index d0d3af287..e58f680c2 100644
--- a/ArchiSteamFarm/IPC/Requests/CommandRequest.cs
+++ b/ArchiSteamFarm/IPC/Requests/CommandRequest.cs
@@ -21,7 +21,7 @@
using System.ComponentModel.DataAnnotations;
using System.Diagnostics.CodeAnalysis;
-using Newtonsoft.Json;
+using System.Text.Json.Serialization;
namespace ArchiSteamFarm.IPC.Requests;
@@ -30,9 +30,10 @@ public sealed class CommandRequest {
///
/// Specifies the command that will be executed by ASF.
///
- [JsonProperty(Required = Required.Always)]
+ [JsonInclude]
+ [JsonRequired]
[Required]
- public string Command { get; private set; } = "";
+ public string Command { get; private init; } = "";
[JsonConstructor]
private CommandRequest() { }
diff --git a/ArchiSteamFarm/IPC/Requests/TwoFactorAuthenticationConfirmationsRequest.cs b/ArchiSteamFarm/IPC/Requests/TwoFactorAuthenticationConfirmationsRequest.cs
index 5ac25c02a..7badf0328 100644
--- a/ArchiSteamFarm/IPC/Requests/TwoFactorAuthenticationConfirmationsRequest.cs
+++ b/ArchiSteamFarm/IPC/Requests/TwoFactorAuthenticationConfirmationsRequest.cs
@@ -22,13 +22,15 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
+using System.ComponentModel.DataAnnotations;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Linq;
+using System.Text.Json.Serialization;
using ArchiSteamFarm.Core;
+using ArchiSteamFarm.Helpers.Json;
using ArchiSteamFarm.Localization;
using ArchiSteamFarm.Steam.Data;
-using Newtonsoft.Json;
namespace ArchiSteamFarm.IPC.Requests;
@@ -37,29 +39,34 @@ public sealed class TwoFactorAuthenticationConfirmationsRequest {
///
/// Specifies the target action, whether we should accept the confirmations (true), or decline them (false).
///
- [JsonProperty(Required = Required.Always)]
- public bool Accept { get; private set; }
+ [JsonInclude]
+ [JsonRequired]
+ [Required]
+ public bool Accept { get; private init; }
///
/// Specifies IDs of the confirmations that we're supposed to handle. CreatorID of the confirmation is equal to ID of the object that triggered it - e.g. ID of the trade offer, or ID of the market listing. If not provided, or empty array, all confirmation IDs are considered for an action.
///
- [JsonProperty(Required = Required.DisallowNull)]
- public ImmutableHashSet AcceptedCreatorIDs { get; private set; } = ImmutableHashSet.Empty;
+ [JsonDisallowNull]
+ [JsonInclude]
+ public ImmutableHashSet AcceptedCreatorIDs { get; private init; } = ImmutableHashSet.Empty;
///
/// Specifies the type of confirmations to handle. If not provided, all confirmation types are considered for an action.
///
- [JsonProperty]
- public Confirmation.EConfirmationType? AcceptedType { get; private set; }
+ [JsonInclude]
+ public Confirmation.EConfirmationType? AcceptedType { get; private init; }
///
/// A helper property which works the same as but with values written as strings - for javascript compatibility purposes. Use either this one, or , not both.
///
- [JsonProperty($"{SharedInfo.UlongCompatibilityStringPrefix}{nameof(AcceptedCreatorIDs)}", Required = Required.DisallowNull)]
+ [JsonDisallowNull]
+ [JsonInclude]
+ [JsonPropertyName($"{SharedInfo.UlongCompatibilityStringPrefix}{nameof(AcceptedCreatorIDs)}")]
public ImmutableHashSet SAcceptedCreatorIDs {
get => AcceptedCreatorIDs.Select(static creatorID => creatorID.ToString(CultureInfo.InvariantCulture)).ToImmutableHashSet(StringComparer.Ordinal);
- private set {
+ private init {
ArgumentNullException.ThrowIfNull(value);
HashSet acceptedCreatorIDs = [];
@@ -81,8 +88,8 @@ public sealed class TwoFactorAuthenticationConfirmationsRequest {
///
/// Specifies whether we should wait for the confirmations to arrive, in case they're not available immediately. This option makes sense only if is specified as well, and in this case ASF will add a few more tries if needed to ensure that all specified IDs are handled. Useful if confirmations are generated with a delay on Steam network side, which happens fairly often.
///
- [JsonProperty(Required = Required.DisallowNull)]
- public bool WaitIfNeeded { get; private set; }
+ [JsonInclude]
+ public bool WaitIfNeeded { get; private init; }
[JsonConstructor]
private TwoFactorAuthenticationConfirmationsRequest() { }
diff --git a/ArchiSteamFarm/IPC/Requests/UpdateRequest.cs b/ArchiSteamFarm/IPC/Requests/UpdateRequest.cs
index 5a1e72134..a9c28d1eb 100644
--- a/ArchiSteamFarm/IPC/Requests/UpdateRequest.cs
+++ b/ArchiSteamFarm/IPC/Requests/UpdateRequest.cs
@@ -20,8 +20,8 @@
// limitations under the License.
using System.Diagnostics.CodeAnalysis;
+using System.Text.Json.Serialization;
using ArchiSteamFarm.Storage;
-using Newtonsoft.Json;
namespace ArchiSteamFarm.IPC.Requests;
@@ -30,8 +30,8 @@ public sealed class UpdateRequest {
///
/// Target update channel. Not required, will default to UpdateChannel in GlobalConfig if not provided.
///
- [JsonProperty(Required = Required.DisallowNull)]
- public GlobalConfig.EUpdateChannel? Channel { get; private set; }
+ [JsonInclude]
+ public GlobalConfig.EUpdateChannel? Channel { get; private init; }
[JsonConstructor]
private UpdateRequest() { }
diff --git a/ArchiSteamFarm/IPC/Responses/ASFResponse.cs b/ArchiSteamFarm/IPC/Responses/ASFResponse.cs
index 92fb6a46b..0b00dda7f 100644
--- a/ArchiSteamFarm/IPC/Responses/ASFResponse.cs
+++ b/ArchiSteamFarm/IPC/Responses/ASFResponse.cs
@@ -21,8 +21,8 @@
using System;
using System.ComponentModel.DataAnnotations;
+using System.Text.Json.Serialization;
using ArchiSteamFarm.Storage;
-using Newtonsoft.Json;
namespace ArchiSteamFarm.IPC.Responses;
@@ -30,51 +30,58 @@ public sealed class ASFResponse {
///
/// ASF's build variant.
///
- [JsonProperty(Required = Required.Always)]
+ [JsonInclude]
+ [JsonRequired]
[Required]
- public string BuildVariant { get; private set; }
+ public string BuildVariant { get; private init; }
///
/// A value specifying whether this variant of ASF is capable of auto-update.
///
- [JsonProperty(Required = Required.Always)]
+ [JsonInclude]
+ [JsonRequired]
[Required]
- public bool CanUpdate { get; private set; }
+ public bool CanUpdate { get; private init; }
///
/// Currently loaded ASF's global config.
///
- [JsonProperty(Required = Required.Always)]
+ [JsonInclude]
+ [JsonRequired]
[Required]
- public GlobalConfig GlobalConfig { get; private set; }
+ public GlobalConfig GlobalConfig { get; private init; }
///
/// Current amount of managed memory being used by the process, in kilobytes.
///
- [JsonProperty(Required = Required.Always)]
+ [JsonInclude]
+ [JsonRequired]
[Required]
- public uint MemoryUsage { get; private set; }
+ public uint MemoryUsage { get; private init; }
///
/// Start date of the process.
///
- [JsonProperty(Required = Required.Always)]
+ [JsonInclude]
+ [JsonRequired]
[Required]
- public DateTime ProcessStartTime { get; private set; }
+ public DateTime ProcessStartTime { get; private init; }
///
/// Boolean value specifying whether ASF has been started with a --service parameter.
///
- [JsonProperty(Required = Required.Always)]
+ [JsonInclude]
+ [JsonRequired]
[Required]
- public bool Service { get; private set; }
+ public bool Service { get; private init; }
///
/// ASF version of currently running binary.
///
- [JsonProperty(Required = Required.Always)]
+ [JsonInclude]
+ [JsonRequired]
[Required]
- public Version Version { get; private set; }
+ public Version Version { get; private init; }
internal ASFResponse(string buildVariant, bool canUpdate, GlobalConfig globalConfig, uint memoryUsage, DateTime processStartTime, Version version) {
ArgumentException.ThrowIfNullOrEmpty(buildVariant);
diff --git a/ArchiSteamFarm/IPC/Responses/GamesToRedeemInBackgroundResponse.cs b/ArchiSteamFarm/IPC/Responses/GamesToRedeemInBackgroundResponse.cs
index 7daf278f0..4028f4f69 100644
--- a/ArchiSteamFarm/IPC/Responses/GamesToRedeemInBackgroundResponse.cs
+++ b/ArchiSteamFarm/IPC/Responses/GamesToRedeemInBackgroundResponse.cs
@@ -20,7 +20,7 @@
// limitations under the License.
using System.Collections.Generic;
-using Newtonsoft.Json;
+using System.Text.Json.Serialization;
namespace ArchiSteamFarm.IPC.Responses;
@@ -28,14 +28,14 @@ public sealed class GamesToRedeemInBackgroundResponse {
///
/// Keys that were redeemed and not used during the process, if available.
///
- [JsonProperty]
- public Dictionary? UnusedKeys { get; private set; }
+ [JsonInclude]
+ public Dictionary? UnusedKeys { get; private init; }
///
/// Keys that were redeemed and used during the process, if available.
///
- [JsonProperty]
- public Dictionary? UsedKeys { get; private set; }
+ [JsonInclude]
+ public Dictionary? UsedKeys { get; private init; }
internal GamesToRedeemInBackgroundResponse(Dictionary? unusedKeys = null, Dictionary? usedKeys = null) {
UnusedKeys = unusedKeys;
diff --git a/ArchiSteamFarm/IPC/Responses/GenericResponse.cs b/ArchiSteamFarm/IPC/Responses/GenericResponse.cs
index 74e0450e9..cc6765df0 100644
--- a/ArchiSteamFarm/IPC/Responses/GenericResponse.cs
+++ b/ArchiSteamFarm/IPC/Responses/GenericResponse.cs
@@ -20,8 +20,8 @@
// limitations under the License.
using System.ComponentModel.DataAnnotations;
+using System.Text.Json.Serialization;
using ArchiSteamFarm.Localization;
-using Newtonsoft.Json;
namespace ArchiSteamFarm.IPC.Responses;
@@ -32,8 +32,8 @@ public sealed class GenericResponse : GenericResponse {
///
/// The type of the result depends on the API endpoint that you've called.
///
- [JsonProperty]
- public T? Result { get; private set; }
+ [JsonInclude]
+ public T? Result { get; private init; }
public GenericResponse(T? result) : base(result is not null) => Result = result;
public GenericResponse(bool success, string? message) : base(success, message) { }
@@ -51,15 +51,16 @@ public class GenericResponse {
///
/// This property will provide exact reason for majority of expected failures.
///
- [JsonProperty]
- public string? Message { get; private set; }
+ [JsonInclude]
+ public string? Message { get; private init; }
///
/// Boolean type that specifies if the request has succeeded.
///
- [JsonProperty(Required = Required.Always)]
+ [JsonInclude]
+ [JsonRequired]
[Required]
- public bool Success { get; private set; }
+ public bool Success { get; private init; }
public GenericResponse(bool success, string? message = null) {
Success = success;
diff --git a/ArchiSteamFarm/IPC/Responses/GitHubReleaseResponse.cs b/ArchiSteamFarm/IPC/Responses/GitHubReleaseResponse.cs
index cc2a6aaf0..9c658b623 100644
--- a/ArchiSteamFarm/IPC/Responses/GitHubReleaseResponse.cs
+++ b/ArchiSteamFarm/IPC/Responses/GitHubReleaseResponse.cs
@@ -21,8 +21,8 @@
using System;
using System.ComponentModel.DataAnnotations;
+using System.Text.Json.Serialization;
using ArchiSteamFarm.Web;
-using Newtonsoft.Json;
namespace ArchiSteamFarm.IPC.Responses;
@@ -30,30 +30,34 @@ public sealed class GitHubReleaseResponse {
///
/// Changelog of the release rendered in HTML.
///
- [JsonProperty(Required = Required.Always)]
+ [JsonInclude]
+ [JsonRequired]
[Required]
- public string ChangelogHTML { get; private set; }
+ public string ChangelogHTML { get; private init; }
///
/// Date of the release.
///
- [JsonProperty(Required = Required.Always)]
+ [JsonInclude]
+ [JsonRequired]
[Required]
- public DateTime ReleasedAt { get; private set; }
+ public DateTime ReleasedAt { get; private init; }
///
/// Boolean value that specifies whether the build is stable or not (pre-release).
///
- [JsonProperty(Required = Required.Always)]
+ [JsonInclude]
+ [JsonRequired]
[Required]
- public bool Stable { get; private set; }
+ public bool Stable { get; private init; }
///
/// Version of the release.
///
- [JsonProperty(Required = Required.Always)]
+ [JsonInclude]
+ [JsonRequired]
[Required]
- public string Version { get; private set; }
+ public string Version { get; private init; }
internal GitHubReleaseResponse(GitHub.ReleaseResponse releaseResponse) {
ArgumentNullException.ThrowIfNull(releaseResponse);
diff --git a/ArchiSteamFarm/IPC/Responses/LogResponse.cs b/ArchiSteamFarm/IPC/Responses/LogResponse.cs
index 7707c08c3..8ddd061d9 100644
--- a/ArchiSteamFarm/IPC/Responses/LogResponse.cs
+++ b/ArchiSteamFarm/IPC/Responses/LogResponse.cs
@@ -22,7 +22,7 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
-using Newtonsoft.Json;
+using System.Text.Json.Serialization;
namespace ArchiSteamFarm.IPC.Responses;
@@ -30,16 +30,18 @@ public sealed class LogResponse {
///
/// Content of the log file which consists of lines read from it - in chronological order.
///
- [JsonProperty(Required = Required.Always)]
+ [JsonInclude]
+ [JsonRequired]
[Required]
- public IReadOnlyList Content { get; private set; }
+ public IReadOnlyList Content { get; private init; }
///
/// Total number of lines of the log file returned, can be used as an index for future requests.
///
- [JsonProperty(Required = Required.Always)]
+ [JsonInclude]
+ [JsonRequired]
[Required]
- public int TotalLines { get; private set; }
+ public int TotalLines { get; private init; }
internal LogResponse(int totalLines, IReadOnlyList content) {
ArgumentOutOfRangeException.ThrowIfNegative(totalLines);
diff --git a/ArchiSteamFarm/IPC/Responses/StatusCodeResponse.cs b/ArchiSteamFarm/IPC/Responses/StatusCodeResponse.cs
index 977ee7127..4e5829fb1 100644
--- a/ArchiSteamFarm/IPC/Responses/StatusCodeResponse.cs
+++ b/ArchiSteamFarm/IPC/Responses/StatusCodeResponse.cs
@@ -21,7 +21,7 @@
using System.ComponentModel.DataAnnotations;
using System.Net;
-using Newtonsoft.Json;
+using System.Text.Json.Serialization;
namespace ArchiSteamFarm.IPC.Responses;
@@ -29,16 +29,18 @@ public sealed class StatusCodeResponse {
///
/// Value indicating whether the status is permanent. If yes, retrying the request with exactly the same payload doesn't make sense due to a permanent problem (e.g. ASF misconfiguration).
///
- [JsonProperty(Required = Required.Always)]
+ [JsonInclude]
+ [JsonRequired]
[Required]
- public bool Permanent { get; private set; }
+ public bool Permanent { get; private init; }
///
/// Status code transmitted in addition to the one in HTTP spec.
///
- [JsonProperty(Required = Required.Always)]
+ [JsonInclude]
+ [JsonRequired]
[Required]
- public HttpStatusCode StatusCode { get; private set; }
+ public HttpStatusCode StatusCode { get; private init; }
internal StatusCodeResponse(HttpStatusCode statusCode, bool permanent) {
StatusCode = statusCode;
diff --git a/ArchiSteamFarm/IPC/Responses/TypeProperties.cs b/ArchiSteamFarm/IPC/Responses/TypeProperties.cs
index fb98e7435..aa16b8978 100644
--- a/ArchiSteamFarm/IPC/Responses/TypeProperties.cs
+++ b/ArchiSteamFarm/IPC/Responses/TypeProperties.cs
@@ -21,7 +21,7 @@
using System;
using System.Collections.Generic;
-using Newtonsoft.Json;
+using System.Text.Json.Serialization;
namespace ArchiSteamFarm.IPC.Responses;
@@ -32,8 +32,8 @@ public sealed class TypeProperties {
///
/// This can be used for determining how the body of the response should be interpreted.
///
- [JsonProperty]
- public string? BaseType { get; private set; }
+ [JsonInclude]
+ public string? BaseType { get; private init; }
///
/// Custom attributes of given type, if available.
@@ -41,8 +41,8 @@ public sealed class TypeProperties {
///
/// This can be used for determining main enum type if is .
///
- [JsonProperty]
- public HashSet? CustomAttributes { get; private set; }
+ [JsonInclude]
+ public HashSet? CustomAttributes { get; private init; }
///
/// Underlying type of given type, if available.
@@ -50,8 +50,8 @@ public sealed class TypeProperties {
///
/// This can be used for determining underlying enum type if is .
///
- [JsonProperty]
- public string? UnderlyingType { get; private set; }
+ [JsonInclude]
+ public string? UnderlyingType { get; private init; }
internal TypeProperties(string? baseType = null, HashSet? customAttributes = null, string? underlyingType = null) {
BaseType = baseType;
diff --git a/ArchiSteamFarm/IPC/Responses/TypeResponse.cs b/ArchiSteamFarm/IPC/Responses/TypeResponse.cs
index 91063582f..e3f5447b7 100644
--- a/ArchiSteamFarm/IPC/Responses/TypeResponse.cs
+++ b/ArchiSteamFarm/IPC/Responses/TypeResponse.cs
@@ -22,7 +22,7 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
-using Newtonsoft.Json;
+using System.Text.Json.Serialization;
namespace ArchiSteamFarm.IPC.Responses;
@@ -35,16 +35,18 @@ public sealed class TypeResponse {
/// For enums, keys are friendly names while values are underlying values of those names.
/// For objects, keys are non-private fields and properties, while values are underlying types of those.
///
- [JsonProperty(Required = Required.Always)]
+ [JsonInclude]
+ [JsonRequired]
[Required]
- public Dictionary Body { get; private set; }
+ public Dictionary Body { get; private init; }
///
/// Metadata of given type.
///
- [JsonProperty(Required = Required.Always)]
+ [JsonInclude]
+ [JsonRequired]
[Required]
- public TypeProperties Properties { get; private set; }
+ public TypeProperties Properties { get; private init; }
internal TypeResponse(Dictionary body, TypeProperties properties) {
ArgumentNullException.ThrowIfNull(body);
diff --git a/ArchiSteamFarm/IPC/Startup.cs b/ArchiSteamFarm/IPC/Startup.cs
index 9ca530820..4dc1ec192 100644
--- a/ArchiSteamFarm/IPC/Startup.cs
+++ b/ArchiSteamFarm/IPC/Startup.cs
@@ -28,6 +28,7 @@ using System.Linq;
using System.Net;
using System.Reflection;
using ArchiSteamFarm.Core;
+using ArchiSteamFarm.Helpers.Json;
using ArchiSteamFarm.IPC.Integration;
using ArchiSteamFarm.Localization;
using ArchiSteamFarm.Plugins;
@@ -45,8 +46,6 @@ using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.FileProviders;
using Microsoft.Net.Http.Headers;
using Microsoft.OpenApi.Models;
-using Newtonsoft.Json;
-using Newtonsoft.Json.Serialization;
using IPNetwork = Microsoft.AspNetCore.HttpOverrides.IPNetwork;
namespace ArchiSteamFarm.IPC;
@@ -284,6 +283,7 @@ internal sealed class Startup {
options.SchemaFilter();
options.SchemaFilter();
+ options.SchemaFilter();
options.SwaggerDoc(
SharedInfo.ASF, new OpenApiInfo {
@@ -310,9 +310,6 @@ internal sealed class Startup {
}
);
- // Add support for Newtonsoft.Json in swagger, this one must be executed after AddSwaggerGen()
- services.AddSwaggerGenNewtonsoftSupport();
-
// We need MVC for /Api, but we're going to use only a small subset of all available features
IMvcBuilder mvc = services.AddControllers();
@@ -329,14 +326,10 @@ internal sealed class Startup {
mvc.AddControllersAsServices();
- mvc.AddNewtonsoftJson(
+ mvc.AddJsonOptions(
static options => {
- // Fix default contract resolver to use original names and not a camel case
- options.SerializerSettings.ContractResolver = new DefaultContractResolver();
-
- if (Debugging.IsUserDebugging) {
- options.SerializerSettings.Formatting = Formatting.Indented;
- }
+ options.JsonSerializerOptions.PropertyNamingPolicy = JsonUtilities.DefaultJsonSerialierOptions.PropertyNamingPolicy;
+ options.JsonSerializerOptions.TypeInfoResolver = JsonUtilities.DefaultJsonSerialierOptions.TypeInfoResolver;
}
);
}
diff --git a/ArchiSteamFarm/IPC/WebUtilities.cs b/ArchiSteamFarm/IPC/WebUtilities.cs
index 649583ce9..539f6c732 100644
--- a/ArchiSteamFarm/IPC/WebUtilities.cs
+++ b/ArchiSteamFarm/IPC/WebUtilities.cs
@@ -21,12 +21,7 @@
using System;
using System.Diagnostics.CodeAnalysis;
-using System.IO;
using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-using Microsoft.AspNetCore.Http;
-using Newtonsoft.Json;
namespace ArchiSteamFarm.IPC;
@@ -56,26 +51,4 @@ internal static class WebUtilities {
return Type.GetType($"{typeText},{typeText[..index]}");
}
-
- internal static async Task WriteJsonAsync(this HttpResponse response, TValue? value, JsonSerializerSettings? jsonSerializerSettings = null) {
- ArgumentNullException.ThrowIfNull(response);
-
- JsonSerializer serializer = JsonSerializer.CreateDefault(jsonSerializerSettings);
-
- response.ContentType = "application/json; charset=utf-8";
-
- StreamWriter streamWriter = new(response.Body, Encoding.UTF8);
-
- await using (streamWriter.ConfigureAwait(false)) {
-#pragma warning disable CA2000 // False positive, we're actually wrapping it in the using clause below exactly for that purpose
- JsonTextWriter jsonWriter = new(streamWriter) {
- CloseOutput = false
- };
-#pragma warning restore CA2000 // False positive, we're actually wrapping it in the using clause below exactly for that purpose
-
- await using (jsonWriter.ConfigureAwait(false)) {
- serializer.Serialize(jsonWriter, value);
- }
- }
- }
}
diff --git a/ArchiSteamFarm/Plugins/Interfaces/IASF.cs b/ArchiSteamFarm/Plugins/Interfaces/IASF.cs
index e797acae5..1b6957874 100644
--- a/ArchiSteamFarm/Plugins/Interfaces/IASF.cs
+++ b/ArchiSteamFarm/Plugins/Interfaces/IASF.cs
@@ -20,10 +20,10 @@
// limitations under the License.
using System.Collections.Generic;
+using System.Text.Json;
+using System.Text.Json.Serialization;
using System.Threading.Tasks;
using JetBrains.Annotations;
-using Newtonsoft.Json;
-using Newtonsoft.Json.Linq;
namespace ArchiSteamFarm.Plugins.Interfaces;
@@ -33,5 +33,5 @@ public interface IASF : IPlugin {
/// ASF will call this method right after global config initialization.
///
/// Extra config properties made out of . Can be null if no extra properties are found.
- Task OnASFInit(IReadOnlyDictionary? additionalConfigProperties = null);
+ Task OnASFInit(IReadOnlyDictionary? additionalConfigProperties = null);
}
diff --git a/ArchiSteamFarm/Plugins/Interfaces/IBotModules.cs b/ArchiSteamFarm/Plugins/Interfaces/IBotModules.cs
index 83afc58b2..10bf8ec96 100644
--- a/ArchiSteamFarm/Plugins/Interfaces/IBotModules.cs
+++ b/ArchiSteamFarm/Plugins/Interfaces/IBotModules.cs
@@ -20,11 +20,11 @@
// limitations under the License.
using System.Collections.Generic;
+using System.Text.Json;
+using System.Text.Json.Serialization;
using System.Threading.Tasks;
using ArchiSteamFarm.Steam;
using JetBrains.Annotations;
-using Newtonsoft.Json;
-using Newtonsoft.Json.Linq;
namespace ArchiSteamFarm.Plugins.Interfaces;
@@ -35,5 +35,5 @@ public interface IBotModules : IPlugin {
///
/// Bot object related to this callback.
/// Extra config properties made out of . Can be null if no extra properties are found.
- Task OnBotInitModules(Bot bot, IReadOnlyDictionary? additionalConfigProperties = null);
+ Task OnBotInitModules(Bot bot, IReadOnlyDictionary? additionalConfigProperties = null);
}
diff --git a/ArchiSteamFarm/Plugins/Interfaces/IPlugin.cs b/ArchiSteamFarm/Plugins/Interfaces/IPlugin.cs
index cc3c5d9e7..0cc8536db 100644
--- a/ArchiSteamFarm/Plugins/Interfaces/IPlugin.cs
+++ b/ArchiSteamFarm/Plugins/Interfaces/IPlugin.cs
@@ -20,9 +20,10 @@
// limitations under the License.
using System;
+using System.ComponentModel.DataAnnotations;
+using System.Text.Json.Serialization;
using System.Threading.Tasks;
using JetBrains.Annotations;
-using Newtonsoft.Json;
namespace ArchiSteamFarm.Plugins.Interfaces;
@@ -32,7 +33,8 @@ public interface IPlugin {
/// ASF will use this property as general plugin identifier for the user.
///
/// String that will be used as the name of this plugin.
- [JsonProperty]
+ [JsonInclude]
+ [Required]
string Name { get; }
///
@@ -40,7 +42,8 @@ public interface IPlugin {
/// You have a freedom in deciding what versioning you want to use, this is for identification purposes only.
///
/// Version that will be shown to the user when plugin is loaded.
- [JsonProperty]
+ [JsonInclude]
+ [Required]
Version Version { get; }
///
diff --git a/ArchiSteamFarm/Plugins/Interfaces/IWebInterface.cs b/ArchiSteamFarm/Plugins/Interfaces/IWebInterface.cs
index 646a793b1..d9ec22fd1 100644
--- a/ArchiSteamFarm/Plugins/Interfaces/IWebInterface.cs
+++ b/ArchiSteamFarm/Plugins/Interfaces/IWebInterface.cs
@@ -19,7 +19,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-using Newtonsoft.Json;
+using System.Text.Json.Serialization;
namespace ArchiSteamFarm.Plugins.Interfaces;
@@ -35,6 +35,6 @@ public interface IWebInterface : IPlugin {
///
/// Specifies web path (address) under which ASF should host your static WWW files in directory. Default value of "/" allows you to override default ASF files and gives you full flexibility in your directory. However, you can instead host your files under some other fixed location specified here, such as "/MyPlugin".
///
- [JsonProperty]
+ [JsonInclude]
string WebPath => "/";
}
diff --git a/ArchiSteamFarm/Plugins/PluginsCore.cs b/ArchiSteamFarm/Plugins/PluginsCore.cs
index d9f81316c..dc037bb85 100644
--- a/ArchiSteamFarm/Plugins/PluginsCore.cs
+++ b/ArchiSteamFarm/Plugins/PluginsCore.cs
@@ -33,6 +33,7 @@ using System.Linq;
using System.Reflection;
using System.Security.Cryptography;
using System.Text;
+using System.Text.Json;
using System.Threading.Tasks;
using ArchiSteamFarm.Core;
using ArchiSteamFarm.Helpers;
@@ -43,7 +44,6 @@ using ArchiSteamFarm.Steam.Data;
using ArchiSteamFarm.Steam.Exchange;
using ArchiSteamFarm.Steam.Integration.Callbacks;
using JetBrains.Annotations;
-using Newtonsoft.Json.Linq;
using SteamKit2;
namespace ArchiSteamFarm.Plugins;
@@ -286,7 +286,7 @@ public static class PluginsCore {
return assemblies;
}
- internal static async Task OnASFInitModules(IReadOnlyDictionary? additionalConfigProperties = null) {
+ internal static async Task OnASFInitModules(IReadOnlyDictionary? additionalConfigProperties = null) {
if (ActivePlugins.Count == 0) {
return;
}
@@ -436,7 +436,7 @@ public static class PluginsCore {
}
}
- internal static async Task OnBotInitModules(Bot bot, IReadOnlyDictionary? additionalConfigProperties = null) {
+ internal static async Task OnBotInitModules(Bot bot, IReadOnlyDictionary? additionalConfigProperties = null) {
ArgumentNullException.ThrowIfNull(bot);
if (ActivePlugins.Count == 0) {
diff --git a/ArchiSteamFarm/Program.cs b/ArchiSteamFarm/Program.cs
index b6f20eac9..9f9def724 100644
--- a/ArchiSteamFarm/Program.cs
+++ b/ArchiSteamFarm/Program.cs
@@ -33,6 +33,7 @@ using System.Text;
using System.Threading.Tasks;
using ArchiSteamFarm.Core;
using ArchiSteamFarm.Helpers;
+using ArchiSteamFarm.Helpers.Json;
using ArchiSteamFarm.IPC;
using ArchiSteamFarm.Localization;
using ArchiSteamFarm.NLog;
@@ -40,7 +41,6 @@ using ArchiSteamFarm.NLog.Targets;
using ArchiSteamFarm.Steam;
using ArchiSteamFarm.Storage;
using ArchiSteamFarm.Web;
-using Newtonsoft.Json;
using NLog;
using SteamKit2;
@@ -353,7 +353,7 @@ internal static class Program {
}
if (globalConfig.Debug) {
- ASF.ArchiLogger.LogGenericDebug($"{globalConfigFile}: {JsonConvert.SerializeObject(globalConfig, Formatting.Indented)}");
+ ASF.ArchiLogger.LogGenericDebug($"{globalConfigFile}: {globalConfig.ToJsonText(true)}");
}
if (!string.IsNullOrEmpty(globalConfig.CurrentCulture)) {
@@ -414,7 +414,7 @@ internal static class Program {
// If debugging is on, we prepare debug directory prior to running
if (Debugging.IsUserDebugging) {
if (Debugging.IsDebugConfigured) {
- ASF.ArchiLogger.LogGenericDebug($"{globalDatabaseFile}: {JsonConvert.SerializeObject(ASF.GlobalDatabase, Formatting.Indented)}");
+ ASF.ArchiLogger.LogGenericDebug($"{globalDatabaseFile}: {globalDatabase.ToJsonText(true)}");
}
Logging.EnableTraceLogging();
diff --git a/ArchiSteamFarm/Steam/Bot.cs b/ArchiSteamFarm/Steam/Bot.cs
index a15743d9d..003bd6699 100644
--- a/ArchiSteamFarm/Steam/Bot.cs
+++ b/ArchiSteamFarm/Steam/Bot.cs
@@ -27,10 +27,12 @@ using System.Collections.Generic;
using System.Collections.Immutable;
using System.Collections.Specialized;
using System.ComponentModel;
+using System.ComponentModel.DataAnnotations;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Net.Http;
+using System.Text.Json.Serialization;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
@@ -38,6 +40,7 @@ using AngleSharp.Dom;
using ArchiSteamFarm.Collections;
using ArchiSteamFarm.Core;
using ArchiSteamFarm.Helpers;
+using ArchiSteamFarm.Helpers.Json;
using ArchiSteamFarm.Localization;
using ArchiSteamFarm.NLog;
using ArchiSteamFarm.Plugins;
@@ -53,7 +56,6 @@ using ArchiSteamFarm.Storage;
using ArchiSteamFarm.Web;
using JetBrains.Annotations;
using Microsoft.IdentityModel.JsonWebTokens;
-using Newtonsoft.Json;
using SteamKit2;
using SteamKit2.Authentication;
using SteamKit2.Internal;
@@ -101,24 +103,28 @@ public sealed class Bot : IAsyncDisposable, IDisposable {
[PublicAPI]
public BotDatabase BotDatabase { get; }
- [JsonProperty]
+ [JsonInclude]
[PublicAPI]
+ [Required]
public string BotName { get; }
- [JsonProperty]
+ [JsonInclude]
[PublicAPI]
+ [Required]
public CardsFarmer CardsFarmer { get; }
[JsonIgnore]
[PublicAPI]
public Commands Commands { get; }
- [JsonProperty]
+ [JsonInclude]
[PublicAPI]
+ [Required]
public uint GamesToRedeemInBackgroundCount => BotDatabase.GamesToRedeemInBackgroundCount;
- [JsonProperty]
+ [JsonInclude]
[PublicAPI]
+ [Required]
public bool HasMobileAuthenticator => BotDatabase.MobileAuthenticator != null;
[JsonIgnore]
@@ -129,18 +135,26 @@ public sealed class Bot : IAsyncDisposable, IDisposable {
[PublicAPI]
public bool IsAccountLocked => AccountFlags.HasFlag(EAccountFlags.Lockdown);
- [JsonProperty]
+ [JsonInclude]
[PublicAPI]
+ [Required]
public bool IsConnectedAndLoggedOn => SteamClient.SteamID != null;
- [JsonProperty]
+ [JsonInclude]
[PublicAPI]
+ [Required]
public bool IsPlayingPossible => !PlayingBlocked && !LibraryLocked;
- [JsonProperty]
+ [JsonInclude]
[PublicAPI]
public string? PublicIP => SteamClient.PublicIP?.ToString();
+ [JsonInclude]
+ [JsonPropertyName($"{SharedInfo.UlongCompatibilityStringPrefix}{nameof(SteamID)}")]
+ [PublicAPI]
+ [Required]
+ public string SSteamID => SteamID.ToString(CultureInfo.InvariantCulture);
+
[JsonIgnore]
[PublicAPI]
public SteamApps SteamApps { get; }
@@ -186,9 +200,6 @@ public sealed class Bot : IAsyncDisposable, IDisposable {
}
}
- [JsonProperty($"{SharedInfo.UlongCompatibilityStringPrefix}{nameof(SteamID)}")]
- private string SSteamID => SteamID.ToString(CultureInfo.InvariantCulture);
-
[JsonIgnore]
[PublicAPI]
public string? AccessToken {
@@ -217,19 +228,29 @@ public sealed class Bot : IAsyncDisposable, IDisposable {
}
}
- [JsonProperty]
+ [JsonInclude]
+ [JsonRequired]
[PublicAPI]
+ [Required]
public EAccountFlags AccountFlags { get; private set; }
- [JsonProperty]
+ [JsonInclude]
[PublicAPI]
+ public string? AvatarHash { get; private set; }
+
+ [JsonInclude]
+ [JsonRequired]
+ [PublicAPI]
+ [Required]
public BotConfig BotConfig { get; private set; }
- [JsonProperty]
+ [JsonInclude]
+ [JsonRequired]
[PublicAPI]
+ [Required]
public bool KeepRunning { get; private set; }
- [JsonProperty]
+ [JsonInclude]
[PublicAPI]
public string? Nickname { get; private set; }
@@ -237,24 +258,34 @@ public sealed class Bot : IAsyncDisposable, IDisposable {
[PublicAPI]
public FrozenDictionary OwnedPackageIDs { get; private set; } = FrozenDictionary.Empty;
- [JsonProperty]
+ [JsonInclude]
+ [JsonRequired]
[PublicAPI]
+ [Required]
public ASF.EUserInputType RequiredInput { get; private set; }
- [JsonProperty]
+ [JsonInclude]
+ [JsonRequired]
[PublicAPI]
+ [Required]
public ulong SteamID { get; private set; }
- [JsonProperty]
+ [JsonInclude]
+ [JsonRequired]
[PublicAPI]
+ [Required]
public long WalletBalance { get; private set; }
- [JsonProperty]
+ [JsonInclude]
+ [JsonRequired]
[PublicAPI]
+ [Required]
public long WalletBalanceDelayed { get; private set; }
- [JsonProperty]
+ [JsonInclude]
+ [JsonRequired]
[PublicAPI]
+ [Required]
public ECurrencyCode WalletCurrency { get; private set; }
internal byte HeartBeatFailures { get; private set; }
@@ -264,9 +295,6 @@ public sealed class Bot : IAsyncDisposable, IDisposable {
private DateTime? AccessTokenValidUntil;
private string? AuthCode;
- [JsonProperty]
- private string? AvatarHash;
-
private string? BackingAccessToken;
private Timer? ConnectionFailureTimer;
private bool FirstTradeSent;
@@ -1660,7 +1688,7 @@ public sealed class Bot : IAsyncDisposable, IDisposable {
}
if (Debugging.IsDebugConfigured) {
- ASF.ArchiLogger.LogGenericDebug($"{configFilePath}: {JsonConvert.SerializeObject(botConfig, Formatting.Indented)}");
+ ASF.ArchiLogger.LogGenericDebug($"{configFilePath}: {botConfig.ToJsonText(true)}");
}
if (!string.IsNullOrEmpty(latestJson)) {
@@ -1688,7 +1716,7 @@ public sealed class Bot : IAsyncDisposable, IDisposable {
}
if (Debugging.IsDebugConfigured) {
- ASF.ArchiLogger.LogGenericDebug($"{databaseFilePath}: {JsonConvert.SerializeObject(botDatabase, Formatting.Indented)}");
+ ASF.ArchiLogger.LogGenericDebug($"{databaseFilePath}: {botDatabase.ToJsonText(true)}");
}
botDatabase.PerformMaintenance();
@@ -2280,7 +2308,7 @@ public sealed class Bot : IAsyncDisposable, IDisposable {
return;
}
- MobileAuthenticator? authenticator = JsonConvert.DeserializeObject(json);
+ MobileAuthenticator? authenticator = json.ToJsonObject();
if (authenticator == null) {
ArchiLogger.LogNullError(authenticator);
diff --git a/ArchiSteamFarm/Steam/Cards/CardsFarmer.cs b/ArchiSteamFarm/Steam/Cards/CardsFarmer.cs
index 2912ad664..0c827abc7 100644
--- a/ArchiSteamFarm/Steam/Cards/CardsFarmer.cs
+++ b/ArchiSteamFarm/Steam/Cards/CardsFarmer.cs
@@ -24,8 +24,10 @@ using System.Collections.Concurrent;
using System.Collections.Frozen;
using System.Collections.Generic;
using System.Collections.Immutable;
+using System.ComponentModel.DataAnnotations;
using System.Globalization;
using System.Linq;
+using System.Text.Json.Serialization;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
@@ -42,7 +44,6 @@ using ArchiSteamFarm.Steam.Storage;
using ArchiSteamFarm.Storage;
using ArchiSteamFarm.Web;
using JetBrains.Annotations;
-using Newtonsoft.Json;
using SteamKit2;
namespace ArchiSteamFarm.Steam.Cards;
@@ -63,16 +64,21 @@ public sealed class CardsFarmer : IAsyncDisposable, IDisposable {
// Games that were confirmed to show false status on general badges page
private static readonly FrozenSet UntrustedAppIDs = new HashSet(3) { 440, 570, 730 }.ToFrozenSet();
- [JsonProperty(nameof(CurrentGamesFarming))]
+ [JsonInclude]
+ [JsonPropertyName(nameof(CurrentGamesFarming))]
[PublicAPI]
+ [Required]
public IReadOnlyCollection CurrentGamesFarmingReadOnly => CurrentGamesFarming;
- [JsonProperty(nameof(GamesToFarm))]
+ [JsonInclude]
+ [JsonPropertyName(nameof(GamesToFarm))]
[PublicAPI]
+ [Required]
public IReadOnlyCollection GamesToFarmReadOnly => GamesToFarm;
- [JsonProperty]
+ [JsonInclude]
[PublicAPI]
+ [Required]
public TimeSpan TimeRemaining {
get {
if (GamesToFarm.Count == 0) {
@@ -136,8 +142,10 @@ public sealed class CardsFarmer : IAsyncDisposable, IDisposable {
}
}
- [JsonProperty]
+ [JsonInclude]
+ [JsonRequired]
[PublicAPI]
+ [Required]
public bool Paused { get; private set; }
internal bool NowFarming { get; private set; }
diff --git a/ArchiSteamFarm/Steam/Cards/Game.cs b/ArchiSteamFarm/Steam/Cards/Game.cs
index f0d23367e..29b82067c 100644
--- a/ArchiSteamFarm/Steam/Cards/Game.cs
+++ b/ArchiSteamFarm/Steam/Cards/Game.cs
@@ -20,23 +20,28 @@
// limitations under the License.
using System;
-using Newtonsoft.Json;
+using System.ComponentModel.DataAnnotations;
+using System.Text.Json.Serialization;
namespace ArchiSteamFarm.Steam.Cards;
public sealed class Game : IEquatable {
- [JsonProperty]
+ [JsonInclude]
+ [Required]
public uint AppID { get; }
- [JsonProperty]
+ [JsonInclude]
+ [Required]
public string GameName { get; }
internal readonly byte BadgeLevel;
- [JsonProperty]
+ [JsonInclude]
+ [Required]
public ushort CardsRemaining { get; internal set; }
- [JsonProperty]
+ [JsonInclude]
+ [Required]
public float HoursPlayed { get; internal set; }
internal uint PlayableAppID { get; set; }
diff --git a/ArchiSteamFarm/Steam/Data/Asset.cs b/ArchiSteamFarm/Steam/Data/Asset.cs
index 318f6b66e..8bec38e0a 100644
--- a/ArchiSteamFarm/Steam/Data/Asset.cs
+++ b/ArchiSteamFarm/Steam/Data/Asset.cs
@@ -22,11 +22,9 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
-using System.Globalization;
-using ArchiSteamFarm.Core;
+using System.Text.Json;
+using System.Text.Json.Serialization;
using JetBrains.Annotations;
-using Newtonsoft.Json;
-using Newtonsoft.Json.Linq;
namespace ArchiSteamFarm.Steam.Data;
@@ -43,34 +41,46 @@ public sealed class Asset {
[JsonIgnore]
[PublicAPI]
- public IReadOnlyDictionary? AdditionalPropertiesReadOnly => AdditionalProperties;
+ public IReadOnlyDictionary? AdditionalPropertiesReadOnly => AdditionalProperties;
[JsonIgnore]
[PublicAPI]
public bool IsSteamPointsShopItem => !Tradable && (InstanceID == SteamPointsShopInstanceID);
- [JsonIgnore]
+ [JsonInclude]
+ [JsonNumberHandling(JsonNumberHandling.AllowReadingFromString | JsonNumberHandling.WriteAsString)]
+ [JsonPropertyName("amount")]
+ [JsonRequired]
[PublicAPI]
public uint Amount { get; internal set; }
- [JsonProperty("appid", Required = Required.DisallowNull)]
- public uint AppID { get; private set; }
+ [JsonInclude]
+ [JsonPropertyName("appid")]
+ public uint AppID { get; private init; }
- [JsonIgnore]
+ [JsonInclude]
+ [JsonNumberHandling(JsonNumberHandling.AllowReadingFromString | JsonNumberHandling.WriteAsString)]
+ [JsonPropertyName("assetid")]
[PublicAPI]
- public ulong AssetID { get; private set; }
+ public ulong AssetID { get; private init; }
- [JsonIgnore]
+ [JsonInclude]
+ [JsonNumberHandling(JsonNumberHandling.AllowReadingFromString | JsonNumberHandling.WriteAsString)]
+ [JsonPropertyName("classid")]
[PublicAPI]
- public ulong ClassID { get; private set; }
+ public ulong ClassID { get; private init; }
- [JsonIgnore]
+ [JsonInclude]
+ [JsonNumberHandling(JsonNumberHandling.AllowReadingFromString | JsonNumberHandling.WriteAsString)]
+ [JsonPropertyName("contextid")]
[PublicAPI]
- public ulong ContextID { get; private set; }
+ public ulong ContextID { get; private init; }
- [JsonIgnore]
+ [JsonInclude]
+ [JsonNumberHandling(JsonNumberHandling.AllowReadingFromString | JsonNumberHandling.WriteAsString)]
+ [JsonPropertyName("instanceid")]
[PublicAPI]
- public ulong InstanceID { get; private set; }
+ public ulong InstanceID { get; private init; }
[JsonIgnore]
[PublicAPI]
@@ -96,109 +106,16 @@ public sealed class Asset {
[PublicAPI]
public EType Type { get; internal set; }
- [JsonExtensionData(WriteData = false)]
- internal Dictionary? AdditionalProperties { private get; set; }
+ [JsonExtensionData]
+ [JsonInclude]
+ internal Dictionary? AdditionalProperties { get; set; }
- [JsonProperty("amount", Required = Required.Always)]
- private string AmountText {
- get => Amount.ToString(CultureInfo.InvariantCulture);
-
- set {
- if (string.IsNullOrEmpty(value)) {
- ASF.ArchiLogger.LogNullError(value);
-
- return;
- }
-
- if (!uint.TryParse(value, out uint amount) || (amount == 0)) {
- ASF.ArchiLogger.LogNullError(amount);
-
- return;
- }
-
- Amount = amount;
- }
- }
-
- [JsonProperty("assetid", Required = Required.DisallowNull)]
- private string AssetIDText {
- get => AssetID.ToString(CultureInfo.InvariantCulture);
-
- set {
- if (string.IsNullOrEmpty(value)) {
- ASF.ArchiLogger.LogNullError(value);
-
- return;
- }
-
- if (!ulong.TryParse(value, out ulong assetID) || (assetID == 0)) {
- ASF.ArchiLogger.LogNullError(assetID);
-
- return;
- }
-
- AssetID = assetID;
- }
- }
-
- [JsonProperty("classid", Required = Required.DisallowNull)]
- private string ClassIDText {
- set {
- if (string.IsNullOrEmpty(value)) {
- ASF.ArchiLogger.LogNullError(value);
-
- return;
- }
-
- if (!ulong.TryParse(value, out ulong classID) || (classID == 0)) {
- return;
- }
-
- ClassID = classID;
- }
- }
-
- [JsonProperty("contextid", Required = Required.DisallowNull)]
- private string ContextIDText {
- get => ContextID.ToString(CultureInfo.InvariantCulture);
-
- set {
- if (string.IsNullOrEmpty(value)) {
- ASF.ArchiLogger.LogNullError(value);
-
- return;
- }
-
- if (!ulong.TryParse(value, out ulong contextID) || (contextID == 0)) {
- ASF.ArchiLogger.LogNullError(contextID);
-
- return;
- }
-
- ContextID = contextID;
- }
- }
-
- [JsonProperty("id", Required = Required.DisallowNull)]
- private string IDText {
- set => AssetIDText = value;
- }
-
- [JsonProperty("instanceid", Required = Required.DisallowNull)]
- private string InstanceIDText {
- set {
- if (string.IsNullOrEmpty(value)) {
- return;
- }
-
- if (!ulong.TryParse(value, out ulong instanceID)) {
- ASF.ArchiLogger.LogNullError(instanceID);
-
- return;
- }
-
- InstanceID = instanceID;
- }
+ [JsonInclude]
+ [JsonNumberHandling(JsonNumberHandling.AllowReadingFromString | JsonNumberHandling.WriteAsString)]
+ [JsonPropertyName("id")]
+ private ulong ID {
+ get => AssetID;
+ init => AssetID = value;
}
// Constructed from trades being received or plugins
@@ -228,6 +145,9 @@ public sealed class Asset {
[JsonConstructor]
private Asset() { }
+ [UsedImplicitly]
+ public static bool ShouldSerializeAdditionalProperties() => false;
+
internal Asset CreateShallowCopy() => (Asset) MemberwiseClone();
public enum ERarity : byte {
diff --git a/ArchiSteamFarm/Steam/Data/BooleanResponse.cs b/ArchiSteamFarm/Steam/Data/BooleanResponse.cs
index b77818e53..d66b18b79 100644
--- a/ArchiSteamFarm/Steam/Data/BooleanResponse.cs
+++ b/ArchiSteamFarm/Steam/Data/BooleanResponse.cs
@@ -20,8 +20,8 @@
// limitations under the License.
using System.Diagnostics.CodeAnalysis;
+using System.Text.Json.Serialization;
using JetBrains.Annotations;
-using Newtonsoft.Json;
namespace ArchiSteamFarm.Steam.Data;
@@ -30,8 +30,10 @@ namespace ArchiSteamFarm.Steam.Data;
public class BooleanResponse {
// You say it works in a RESTFUL way
// Then your errors come back as 200 OK
- [JsonProperty("success", Required = Required.Always)]
- public bool Success { get; private set; }
+ [JsonInclude]
+ [JsonPropertyName("success")]
+ [JsonRequired]
+ public bool Success { get; private init; }
[JsonConstructor]
protected BooleanResponse() { }
diff --git a/ArchiSteamFarm/Steam/Data/BoosterCreatorEntry.cs b/ArchiSteamFarm/Steam/Data/BoosterCreatorEntry.cs
index fa68f6b22..2e355cd13 100644
--- a/ArchiSteamFarm/Steam/Data/BoosterCreatorEntry.cs
+++ b/ArchiSteamFarm/Steam/Data/BoosterCreatorEntry.cs
@@ -20,17 +20,21 @@
// limitations under the License.
using System.Diagnostics.CodeAnalysis;
-using Newtonsoft.Json;
+using System.Text.Json.Serialization;
namespace ArchiSteamFarm.Steam.Data;
[SuppressMessage("ReSharper", "ClassCannotBeInstantiated")]
public sealed class BoosterCreatorEntry {
- [JsonProperty("appid", Required = Required.Always)]
- public uint AppID { get; private set; }
+ [JsonInclude]
+ [JsonPropertyName("appid")]
+ [JsonRequired]
+ public uint AppID { get; private init; }
- [JsonProperty("name", Required = Required.Always)]
- public string Name { get; private set; } = "";
+ [JsonInclude]
+ [JsonPropertyName("name")]
+ [JsonRequired]
+ public string Name { get; private init; } = "";
[JsonConstructor]
private BoosterCreatorEntry() { }
diff --git a/ArchiSteamFarm/Steam/Data/Confirmation.cs b/ArchiSteamFarm/Steam/Data/Confirmation.cs
index cdcc8c000..ff366b2d6 100644
--- a/ArchiSteamFarm/Steam/Data/Confirmation.cs
+++ b/ArchiSteamFarm/Steam/Data/Confirmation.cs
@@ -20,25 +20,36 @@
// limitations under the License.
using System.Diagnostics.CodeAnalysis;
+using System.Text.Json.Serialization;
using JetBrains.Annotations;
-using Newtonsoft.Json;
namespace ArchiSteamFarm.Steam.Data;
[PublicAPI]
[SuppressMessage("ReSharper", "ClassCannotBeInstantiated")]
public sealed class Confirmation {
- [JsonProperty(PropertyName = "nonce", Required = Required.Always)]
- internal readonly ulong Nonce;
+ [JsonInclude]
+ [JsonPropertyName("type")]
+ [JsonRequired]
+ public EConfirmationType ConfirmationType { get; private init; }
- [JsonProperty(PropertyName = "type", Required = Required.Always)]
- public EConfirmationType ConfirmationType { get; private set; }
+ [JsonInclude]
+ [JsonNumberHandling(JsonNumberHandling.AllowReadingFromString | JsonNumberHandling.WriteAsString)]
+ [JsonPropertyName("creator_id")]
+ [JsonRequired]
+ public ulong CreatorID { get; private init; }
- [JsonProperty(PropertyName = "creator_id", Required = Required.Always)]
- public ulong CreatorID { get; private set; }
+ [JsonInclude]
+ [JsonNumberHandling(JsonNumberHandling.AllowReadingFromString | JsonNumberHandling.WriteAsString)]
+ [JsonPropertyName("id")]
+ [JsonRequired]
+ public ulong ID { get; private init; }
- [JsonProperty(PropertyName = "id", Required = Required.Always)]
- public ulong ID { get; private set; }
+ [JsonInclude]
+ [JsonNumberHandling(JsonNumberHandling.AllowReadingFromString | JsonNumberHandling.WriteAsString)]
+ [JsonPropertyName("nonce")]
+ [JsonRequired]
+ internal ulong Nonce { get; private init; }
[JsonConstructor]
private Confirmation() { }
diff --git a/ArchiSteamFarm/Steam/Data/ConfirmationsResponse.cs b/ArchiSteamFarm/Steam/Data/ConfirmationsResponse.cs
index 5186f163f..af52dd7af 100644
--- a/ArchiSteamFarm/Steam/Data/ConfirmationsResponse.cs
+++ b/ArchiSteamFarm/Steam/Data/ConfirmationsResponse.cs
@@ -20,13 +20,15 @@
// limitations under the License.
using System.Collections.Immutable;
-using Newtonsoft.Json;
+using System.Text.Json.Serialization;
namespace ArchiSteamFarm.Steam.Data;
internal sealed class ConfirmationsResponse : BooleanResponse {
- [JsonProperty("conf", Required = Required.Always)]
- internal readonly ImmutableHashSet Confirmations = ImmutableHashSet.Empty;
+ [JsonInclude]
+ [JsonPropertyName("conf")]
+ [JsonRequired]
+ internal ImmutableHashSet Confirmations { get; private init; } = ImmutableHashSet.Empty;
[JsonConstructor]
private ConfirmationsResponse() { }
diff --git a/ArchiSteamFarm/Steam/Data/InventoryResponse.cs b/ArchiSteamFarm/Steam/Data/InventoryResponse.cs
index 1b528d3e3..b4bfa9fc4 100644
--- a/ArchiSteamFarm/Steam/Data/InventoryResponse.cs
+++ b/ArchiSteamFarm/Steam/Data/InventoryResponse.cs
@@ -24,80 +24,68 @@ using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
+using System.Text.Json;
+using System.Text.Json.Serialization;
using ArchiSteamFarm.Core;
+using ArchiSteamFarm.Helpers.Json;
using ArchiSteamFarm.Localization;
using ArchiSteamFarm.Steam.Integration;
using JetBrains.Annotations;
-using Newtonsoft.Json;
-using Newtonsoft.Json.Linq;
using SteamKit2;
namespace ArchiSteamFarm.Steam.Data;
[SuppressMessage("ReSharper", "ClassCannotBeInstantiated")]
internal sealed class InventoryResponse : OptionalResultResponse {
- [JsonProperty("assets", Required = Required.DisallowNull)]
- internal readonly ImmutableList Assets = ImmutableList.Empty;
+ [JsonDisallowNull]
+ [JsonInclude]
+ [JsonPropertyName("assets")]
+ internal ImmutableList Assets { get; private init; } = ImmutableList.Empty;
- [JsonProperty("descriptions", Required = Required.DisallowNull)]
- internal readonly ImmutableHashSet Descriptions = ImmutableHashSet.Empty;
+ [JsonDisallowNull]
+ [JsonInclude]
+ [JsonPropertyName("descriptions")]
+ internal ImmutableHashSet Descriptions { get; private init; } = ImmutableHashSet.Empty;
- [JsonProperty("total_inventory_count", Required = Required.DisallowNull)]
- internal readonly uint TotalInventoryCount;
+ internal EResult? ErrorCode { get; private init; }
+ internal string? ErrorText { get; private init; }
- internal EResult? ErrorCode { get; private set; }
- internal string? ErrorText { get; private set; }
- internal ulong LastAssetID { get; private set; }
- internal bool MoreItems { get; private set; }
+ [JsonInclude]
+ [JsonNumberHandling(JsonNumberHandling.AllowReadingFromString | JsonNumberHandling.WriteAsString)]
+ [JsonPropertyName("last_assetid")]
+ internal ulong LastAssetID { get; private init; }
- [JsonProperty("error", Required = Required.DisallowNull)]
+ internal bool MoreItems { get; private init; }
+
+ [JsonInclude]
+ [JsonPropertyName("total_inventory_count")]
+ internal uint TotalInventoryCount { get; private init; }
+
+ [JsonDisallowNull]
+ [JsonInclude]
+ [JsonPropertyName("error")]
private string Error {
- set {
- if (string.IsNullOrEmpty(value)) {
- ASF.ArchiLogger.LogNullError(value);
+ get => ErrorText ?? "";
- return;
- }
+ init {
+ ArgumentException.ThrowIfNullOrEmpty(value);
ErrorCode = SteamUtilities.InterpretError(value);
ErrorText = value;
}
}
- [JsonProperty("last_assetid", Required = Required.DisallowNull)]
- private string LastAssetIDText {
- set {
- if (string.IsNullOrEmpty(value)) {
- ASF.ArchiLogger.LogNullError(value);
-
- return;
- }
-
- if (!ulong.TryParse(value, out ulong lastAssetID) || (lastAssetID == 0)) {
- ASF.ArchiLogger.LogNullError(lastAssetID);
-
- return;
- }
-
- LastAssetID = lastAssetID;
- }
- }
-
- [JsonProperty("more_items", Required = Required.DisallowNull)]
+ [JsonInclude]
+ [JsonPropertyName("more_items")]
private byte MoreItemsNumber {
- set => MoreItems = value > 0;
+ get => MoreItems ? (byte) 1 : (byte) 0;
+ init => MoreItems = value > 0;
}
[JsonConstructor]
private InventoryResponse() { }
internal sealed class Description {
- [JsonProperty("appid", Required = Required.Always)]
- internal readonly uint AppID;
-
- [JsonProperty("tags", Required = Required.DisallowNull)]
- internal readonly ImmutableHashSet Tags = ImmutableHashSet.Empty;
-
internal Asset.ERarity Rarity {
get {
foreach (Tag tag in Tags) {
@@ -217,62 +205,49 @@ internal sealed class InventoryResponse : OptionalResultResponse {
}
}
- [JsonExtensionData(WriteData = false)]
- internal Dictionary? AdditionalProperties {
- get;
- [UsedImplicitly]
- set;
- }
+ [JsonExtensionData]
+ [JsonInclude]
+ internal Dictionary? AdditionalProperties { get; private init; }
- internal ulong ClassID { get; private set; }
- internal ulong InstanceID { get; private set; }
- internal bool Marketable { get; private set; }
- internal bool Tradable { get; private set; }
+ [JsonInclude]
+ [JsonPropertyName("appid")]
+ [JsonRequired]
+ internal uint AppID { get; private init; }
- [JsonProperty("classid", Required = Required.Always)]
- private string ClassIDText {
- set {
- if (string.IsNullOrEmpty(value)) {
- ASF.ArchiLogger.LogNullError(value);
+ [JsonInclude]
+ [JsonNumberHandling(JsonNumberHandling.AllowReadingFromString | JsonNumberHandling.WriteAsString)]
+ [JsonPropertyName("classid")]
+ [JsonRequired]
+ internal ulong ClassID { get; private init; }
- return;
- }
+ [JsonInclude]
+ [JsonNumberHandling(JsonNumberHandling.AllowReadingFromString | JsonNumberHandling.WriteAsString)]
+ [JsonPropertyName("instanceid")]
+ internal ulong InstanceID { get; private init; }
- if (!ulong.TryParse(value, out ulong classID) || (classID == 0)) {
- ASF.ArchiLogger.LogNullError(classID);
+ internal bool Marketable { get; private init; }
- return;
- }
+ [JsonDisallowNull]
+ [JsonInclude]
+ [JsonPropertyName("tags")]
+ internal ImmutableHashSet Tags { get; private init; } = ImmutableHashSet.Empty;
- ClassID = classID;
- }
- }
+ internal bool Tradable { get; private init; }
- [JsonProperty("instanceid", Required = Required.DisallowNull)]
- private string InstanceIDText {
- set {
- if (string.IsNullOrEmpty(value)) {
- return;
- }
-
- if (!ulong.TryParse(value, out ulong instanceID)) {
- ASF.ArchiLogger.LogNullError(instanceID);
-
- return;
- }
-
- InstanceID = instanceID;
- }
- }
-
- [JsonProperty("marketable", Required = Required.Always)]
+ [JsonInclude]
+ [JsonPropertyName("marketable")]
+ [JsonRequired]
private byte MarketableNumber {
- set => Marketable = value > 0;
+ get => Marketable ? (byte) 1 : (byte) 0;
+ init => Marketable = value > 0;
}
- [JsonProperty("tradable", Required = Required.Always)]
+ [JsonInclude]
+ [JsonPropertyName("tradable")]
+ [JsonRequired]
private byte TradableNumber {
- set => Tradable = value > 0;
+ get => Tradable ? (byte) 1 : (byte) 0;
+ init => Tradable = value > 0;
}
// Constructed from trades being received/sent
@@ -293,5 +268,8 @@ internal sealed class InventoryResponse : OptionalResultResponse {
[JsonConstructor]
private Description() { }
+
+ [UsedImplicitly]
+ public static bool ShouldSerializeAdditionalProperties() => false;
}
}
diff --git a/ArchiSteamFarm/Steam/Data/NewDiscoveryQueueResponse.cs b/ArchiSteamFarm/Steam/Data/NewDiscoveryQueueResponse.cs
index d624ba8eb..7662563c4 100644
--- a/ArchiSteamFarm/Steam/Data/NewDiscoveryQueueResponse.cs
+++ b/ArchiSteamFarm/Steam/Data/NewDiscoveryQueueResponse.cs
@@ -21,14 +21,16 @@
using System.Collections.Immutable;
using System.Diagnostics.CodeAnalysis;
-using Newtonsoft.Json;
+using System.Text.Json.Serialization;
namespace ArchiSteamFarm.Steam.Data;
[SuppressMessage("ReSharper", "ClassCannotBeInstantiated")]
internal sealed class NewDiscoveryQueueResponse {
- [JsonProperty("queue", Required = Required.Always)]
- internal readonly ImmutableHashSet Queue = ImmutableHashSet.Empty;
+ [JsonInclude]
+ [JsonPropertyName("queue")]
+ [JsonRequired]
+ internal ImmutableHashSet Queue { get; private init; } = ImmutableHashSet.Empty;
[JsonConstructor]
private NewDiscoveryQueueResponse() { }
diff --git a/ArchiSteamFarm/Steam/Data/OptionalResultResponse.cs b/ArchiSteamFarm/Steam/Data/OptionalResultResponse.cs
index dfe1c4981..fdb9cf5ba 100644
--- a/ArchiSteamFarm/Steam/Data/OptionalResultResponse.cs
+++ b/ArchiSteamFarm/Steam/Data/OptionalResultResponse.cs
@@ -19,16 +19,17 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+using System.Text.Json.Serialization;
using JetBrains.Annotations;
-using Newtonsoft.Json;
using SteamKit2;
namespace ArchiSteamFarm.Steam.Data;
[PublicAPI]
public class OptionalResultResponse {
- [JsonProperty("success", Required = Required.DisallowNull)]
- public EResult? Result { get; private set; }
+ [JsonInclude]
+ [JsonPropertyName("success")]
+ public EResult? Result { get; private init; }
[JsonConstructor]
protected OptionalResultResponse() { }
diff --git a/ArchiSteamFarm/Steam/Data/RedeemWalletResponse.cs b/ArchiSteamFarm/Steam/Data/RedeemWalletResponse.cs
index 20b75cce3..014aaf600 100644
--- a/ArchiSteamFarm/Steam/Data/RedeemWalletResponse.cs
+++ b/ArchiSteamFarm/Steam/Data/RedeemWalletResponse.cs
@@ -20,18 +20,20 @@
// limitations under the License.
using System.Diagnostics.CodeAnalysis;
-using Newtonsoft.Json;
+using System.Text.Json.Serialization;
using SteamKit2;
namespace ArchiSteamFarm.Steam.Data;
[SuppressMessage("ReSharper", "ClassCannotBeInstantiated")]
internal sealed class RedeemWalletResponse : ResultResponse {
- [JsonProperty("formattednewwalletbalance", Required = Required.DisallowNull)]
- internal readonly string? BalanceText;
+ [JsonInclude]
+ [JsonPropertyName("formattednewwalletbalance")]
+ internal string? BalanceText { get; private init; }
- [JsonProperty("detail", Required = Required.DisallowNull)]
- internal readonly EPurchaseResultDetail PurchaseResultDetail;
+ [JsonInclude]
+ [JsonPropertyName("detail")]
+ internal EPurchaseResultDetail PurchaseResultDetail { get; private init; }
[JsonConstructor]
private RedeemWalletResponse() { }
diff --git a/ArchiSteamFarm/Steam/Data/ResultResponse.cs b/ArchiSteamFarm/Steam/Data/ResultResponse.cs
index 89f29b2ba..f08fe730c 100644
--- a/ArchiSteamFarm/Steam/Data/ResultResponse.cs
+++ b/ArchiSteamFarm/Steam/Data/ResultResponse.cs
@@ -19,16 +19,18 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+using System.Text.Json.Serialization;
using JetBrains.Annotations;
-using Newtonsoft.Json;
using SteamKit2;
namespace ArchiSteamFarm.Steam.Data;
[PublicAPI]
public class ResultResponse {
- [JsonProperty("success", Required = Required.Always)]
- public EResult Result { get; private set; }
+ [JsonInclude]
+ [JsonPropertyName("success")]
+ [JsonRequired]
+ public EResult Result { get; private init; }
[JsonConstructor]
protected ResultResponse() { }
diff --git a/ArchiSteamFarm/Steam/Data/Tag.cs b/ArchiSteamFarm/Steam/Data/Tag.cs
index 486f970b9..aca6f524b 100644
--- a/ArchiSteamFarm/Steam/Data/Tag.cs
+++ b/ArchiSteamFarm/Steam/Data/Tag.cs
@@ -20,19 +20,23 @@
// limitations under the License.
using System;
+using System.Text.Json.Serialization;
using JetBrains.Annotations;
-using Newtonsoft.Json;
namespace ArchiSteamFarm.Steam.Data;
public sealed class Tag {
- [JsonProperty("category", Required = Required.Always)]
+ [JsonInclude]
+ [JsonPropertyName("category")]
+ [JsonRequired]
[PublicAPI]
- public string Identifier { get; private set; } = "";
+ public string Identifier { get; private init; } = "";
- [JsonProperty("internal_name", Required = Required.Always)]
+ [JsonInclude]
+ [JsonPropertyName("internal_name")]
+ [JsonRequired]
[PublicAPI]
- public string Value { get; private set; } = "";
+ public string Value { get; private init; } = "";
internal Tag(string identifier, string value) {
ArgumentException.ThrowIfNullOrEmpty(identifier);
diff --git a/ArchiSteamFarm/Steam/Data/TradeOfferAcceptResponse.cs b/ArchiSteamFarm/Steam/Data/TradeOfferAcceptResponse.cs
index 7be903f74..6626cdb04 100644
--- a/ArchiSteamFarm/Steam/Data/TradeOfferAcceptResponse.cs
+++ b/ArchiSteamFarm/Steam/Data/TradeOfferAcceptResponse.cs
@@ -20,17 +20,19 @@
// limitations under the License.
using System.Diagnostics.CodeAnalysis;
-using Newtonsoft.Json;
+using System.Text.Json.Serialization;
namespace ArchiSteamFarm.Steam.Data;
[SuppressMessage("ReSharper", "ClassCannotBeInstantiated")]
internal sealed class TradeOfferAcceptResponse {
- [JsonProperty("strError", Required = Required.DisallowNull)]
- internal readonly string ErrorText = "";
+ [JsonInclude]
+ [JsonPropertyName("strError")]
+ internal string? ErrorText { get; private init; }
- [JsonProperty("needs_mobile_confirmation", Required = Required.DisallowNull)]
- internal readonly bool RequiresMobileConfirmation;
+ [JsonInclude]
+ [JsonPropertyName("needs_mobile_confirmation")]
+ internal bool RequiresMobileConfirmation { get; private init; }
[JsonConstructor]
private TradeOfferAcceptResponse() { }
diff --git a/ArchiSteamFarm/Steam/Data/TradeOfferSendRequest.cs b/ArchiSteamFarm/Steam/Data/TradeOfferSendRequest.cs
index 1de47cd3e..c7dcde065 100644
--- a/ArchiSteamFarm/Steam/Data/TradeOfferSendRequest.cs
+++ b/ArchiSteamFarm/Steam/Data/TradeOfferSendRequest.cs
@@ -20,19 +20,25 @@
// limitations under the License.
using System.Collections.Generic;
-using Newtonsoft.Json;
+using System.Text.Json.Serialization;
namespace ArchiSteamFarm.Steam.Data;
internal sealed class TradeOfferSendRequest {
- [JsonProperty("me", Required = Required.Always)]
- internal readonly ItemList ItemsToGive = new();
+ [JsonInclude]
+ [JsonPropertyName("me")]
+ [JsonRequired]
+ internal ItemList ItemsToGive { get; private init; } = new();
- [JsonProperty("them", Required = Required.Always)]
- internal readonly ItemList ItemsToReceive = new();
+ [JsonInclude]
+ [JsonPropertyName("them")]
+ [JsonRequired]
+ internal ItemList ItemsToReceive { get; private init; } = new();
internal sealed class ItemList {
- [JsonProperty("assets", Required = Required.Always)]
- internal readonly HashSet Assets = [];
+ [JsonInclude]
+ [JsonPropertyName("assets")]
+ [JsonRequired]
+ internal HashSet Assets { get; private init; } = [];
}
}
diff --git a/ArchiSteamFarm/Steam/Data/TradeOfferSendResponse.cs b/ArchiSteamFarm/Steam/Data/TradeOfferSendResponse.cs
index af349ff54..2a157c8ef 100644
--- a/ArchiSteamFarm/Steam/Data/TradeOfferSendResponse.cs
+++ b/ArchiSteamFarm/Steam/Data/TradeOfferSendResponse.cs
@@ -20,39 +20,24 @@
// limitations under the License.
using System.Diagnostics.CodeAnalysis;
-using ArchiSteamFarm.Core;
-using Newtonsoft.Json;
+using System.Text.Json.Serialization;
namespace ArchiSteamFarm.Steam.Data;
[SuppressMessage("ReSharper", "ClassCannotBeInstantiated")]
internal sealed class TradeOfferSendResponse {
- [JsonProperty("strError", Required = Required.DisallowNull)]
- internal readonly string ErrorText = "";
+ [JsonInclude]
+ [JsonPropertyName("strError")]
+ internal string? ErrorText { get; private init; }
- [JsonProperty("needs_mobile_confirmation", Required = Required.DisallowNull)]
- internal readonly bool RequiresMobileConfirmation;
+ [JsonInclude]
+ [JsonPropertyName("needs_mobile_confirmation")]
+ internal bool RequiresMobileConfirmation { get; private init; }
- internal ulong TradeOfferID { get; private set; }
-
- [JsonProperty("tradeofferid", Required = Required.DisallowNull)]
- private string TradeOfferIDText {
- set {
- if (string.IsNullOrEmpty(value)) {
- ASF.ArchiLogger.LogNullError(value);
-
- return;
- }
-
- if (!ulong.TryParse(value, out ulong tradeOfferID) || (tradeOfferID == 0)) {
- ASF.ArchiLogger.LogNullError(tradeOfferID);
-
- return;
- }
-
- TradeOfferID = tradeOfferID;
- }
- }
+ [JsonInclude]
+ [JsonNumberHandling(JsonNumberHandling.AllowReadingFromString | JsonNumberHandling.WriteAsString)]
+ [JsonPropertyName("tradeofferid")]
+ internal ulong TradeOfferID { get; private init; }
[JsonConstructor]
private TradeOfferSendResponse() { }
diff --git a/ArchiSteamFarm/Steam/Data/UserPrivacy.cs b/ArchiSteamFarm/Steam/Data/UserPrivacy.cs
index 9451c821b..7af2b8d23 100644
--- a/ArchiSteamFarm/Steam/Data/UserPrivacy.cs
+++ b/ArchiSteamFarm/Steam/Data/UserPrivacy.cs
@@ -22,18 +22,22 @@
using System;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
+using System.Text.Json.Serialization;
using ArchiSteamFarm.Steam.Integration;
-using Newtonsoft.Json;
namespace ArchiSteamFarm.Steam.Data;
[SuppressMessage("ReSharper", "ClassCannotBeInstantiated")]
internal sealed class UserPrivacy {
- [JsonProperty("eCommentPermission", Required = Required.Always)]
- internal readonly ECommentPermission CommentPermission;
+ [JsonInclude]
+ [JsonPropertyName("eCommentPermission")]
+ [JsonRequired]
+ internal ECommentPermission CommentPermission { get; private init; }
- [JsonProperty("PrivacySettings", Required = Required.Always)]
- internal readonly PrivacySettings Settings = new();
+ [JsonInclude]
+ [JsonPropertyName("PrivacySettings")]
+ [JsonRequired]
+ internal PrivacySettings Settings { get; private init; } = new();
// Constructed from privacy change request
internal UserPrivacy(PrivacySettings settings, ECommentPermission commentPermission) {
@@ -51,23 +55,35 @@ internal sealed class UserPrivacy {
private UserPrivacy() { }
internal sealed class PrivacySettings {
- [JsonProperty("PrivacyFriendsList", Required = Required.Always)]
- internal readonly ArchiHandler.EPrivacySetting FriendsList;
+ [JsonInclude]
+ [JsonPropertyName("PrivacyFriendsList")]
+ [JsonRequired]
+ internal ArchiHandler.EPrivacySetting FriendsList { get; private init; }
- [JsonProperty("PrivacyInventory", Required = Required.Always)]
- internal readonly ArchiHandler.EPrivacySetting Inventory;
+ [JsonInclude]
+ [JsonPropertyName("PrivacyInventory")]
+ [JsonRequired]
+ internal ArchiHandler.EPrivacySetting Inventory { get; private init; }
- [JsonProperty("PrivacyInventoryGifts", Required = Required.Always)]
- internal readonly ArchiHandler.EPrivacySetting InventoryGifts;
+ [JsonInclude]
+ [JsonPropertyName("PrivacyInventoryGifts")]
+ [JsonRequired]
+ internal ArchiHandler.EPrivacySetting InventoryGifts { get; private init; }
- [JsonProperty("PrivacyOwnedGames", Required = Required.Always)]
- internal readonly ArchiHandler.EPrivacySetting OwnedGames;
+ [JsonInclude]
+ [JsonPropertyName("PrivacyOwnedGames")]
+ [JsonRequired]
+ internal ArchiHandler.EPrivacySetting OwnedGames { get; private init; }
- [JsonProperty("PrivacyPlaytime", Required = Required.Always)]
- internal readonly ArchiHandler.EPrivacySetting Playtime;
+ [JsonInclude]
+ [JsonPropertyName("PrivacyPlaytime")]
+ [JsonRequired]
+ internal ArchiHandler.EPrivacySetting Playtime { get; private init; }
- [JsonProperty("PrivacyProfile", Required = Required.Always)]
- internal readonly ArchiHandler.EPrivacySetting Profile;
+ [JsonInclude]
+ [JsonPropertyName("PrivacyProfile")]
+ [JsonRequired]
+ internal ArchiHandler.EPrivacySetting Profile { get; private init; }
// Constructed from privacy change request
internal PrivacySettings(ArchiHandler.EPrivacySetting profile, ArchiHandler.EPrivacySetting ownedGames, ArchiHandler.EPrivacySetting playtime, ArchiHandler.EPrivacySetting friendsList, ArchiHandler.EPrivacySetting inventory, ArchiHandler.EPrivacySetting inventoryGifts) {
diff --git a/ArchiSteamFarm/Steam/Integration/ArchiWebHandler.cs b/ArchiSteamFarm/Steam/Integration/ArchiWebHandler.cs
index bfb124749..54e966fa7 100644
--- a/ArchiSteamFarm/Steam/Integration/ArchiWebHandler.cs
+++ b/ArchiSteamFarm/Steam/Integration/ArchiWebHandler.cs
@@ -30,11 +30,13 @@ using System.Linq;
using System.Net;
using System.Net.Http;
using System.Text;
+using System.Text.Json.Nodes;
using System.Threading;
using System.Threading.Tasks;
using AngleSharp.Dom;
using ArchiSteamFarm.Core;
using ArchiSteamFarm.Helpers;
+using ArchiSteamFarm.Helpers.Json;
using ArchiSteamFarm.Localization;
using ArchiSteamFarm.Steam.Data;
using ArchiSteamFarm.Steam.Exchange;
@@ -42,8 +44,6 @@ using ArchiSteamFarm.Storage;
using ArchiSteamFarm.Web;
using ArchiSteamFarm.Web.Responses;
using JetBrains.Annotations;
-using Newtonsoft.Json;
-using Newtonsoft.Json.Linq;
using SteamKit2;
namespace ArchiSteamFarm.Steam.Integration;
@@ -167,7 +167,7 @@ public sealed class ArchiWebHandler : IDisposable {
string json = scriptNode.TextContent[startIndex..(endIndex + 1)];
try {
- result = JsonConvert.DeserializeObject>(json);
+ result = json.ToJsonObject>();
} catch (Exception e) {
Bot.ArchiLogger.LogGenericException(e);
@@ -713,7 +713,7 @@ public sealed class ArchiWebHandler : IDisposable {
Dictionary data = new(6, StringComparer.Ordinal) {
{ "partner", steamID.ToString(CultureInfo.InvariantCulture) },
{ "serverid", "1" },
- { "trade_offer_create_params", !string.IsNullOrEmpty(token) ? new JObject { { "trade_offer_access_token", token } }.ToString(Formatting.None) : "" },
+ { "trade_offer_create_params", !string.IsNullOrEmpty(token) ? new JsonObject { { "trade_offer_access_token", token } }.ToJsonText() : "" },
{ "tradeoffermessage", $"Sent by {SharedInfo.PublicIdentifier}/{SharedInfo.Version}" }
};
@@ -721,7 +721,7 @@ public sealed class ArchiWebHandler : IDisposable {
HashSet mobileTradeOfferIDs = new(trades.Count);
foreach (TradeOfferSendRequest trade in trades) {
- data["json_tradeoffer"] = JsonConvert.SerializeObject(trade);
+ data["json_tradeoffer"] = trade.ToJsonText();
ObjectResponse? response = null;
@@ -1578,7 +1578,7 @@ public sealed class ArchiWebHandler : IDisposable {
{ "ajax", "true" }
};
- ObjectResponse? response = await UrlPostToJsonObjectWithSession(request, data: data, requestOptions: WebBrowser.ERequestOptions.ReturnClientErrors | WebBrowser.ERequestOptions.ReturnServerErrors | WebBrowser.ERequestOptions.AllowInvalidBodyOnErrors).ConfigureAwait(false);
+ ObjectResponse? response = await UrlPostToJsonObjectWithSession(request, data: data, requestOptions: WebBrowser.ERequestOptions.ReturnClientErrors | WebBrowser.ERequestOptions.ReturnServerErrors | WebBrowser.ERequestOptions.AllowInvalidBodyOnErrors).ConfigureAwait(false);
if (response == null) {
return (EResult.Fail, EPurchaseResultDetail.Timeout);
@@ -1594,31 +1594,37 @@ public sealed class ArchiWebHandler : IDisposable {
// There is not much we can do apart from trying to extract the result and returning it along with the OK and non-OK response, it's also why it doesn't make any sense to strong-type it
EResult result = response.StatusCode.IsSuccessCode() ? EResult.OK : EResult.Fail;
- if (response.Content is not JObject jObject) {
+ if (response.Content is not JsonObject jsonObject) {
// Who knows what piece of crap that is?
return (result, EPurchaseResultDetail.NoDetail);
}
- byte? numberResult = jObject["purchaseresultdetail"]?.Value();
+ try {
+ byte? numberResult = jsonObject["purchaseresultdetail"]?.GetValue();
- if (numberResult.HasValue) {
- return (result, (EPurchaseResultDetail) numberResult.Value);
- }
+ if (numberResult.HasValue) {
+ return (result, (EPurchaseResultDetail) numberResult.Value);
+ }
- // Attempt to do limited parsing from error message, if it exists that is
- string? errorMessage = jObject["error"]?.Value();
+ // Attempt to do limited parsing from error message, if it exists that is
+ string? errorMessage = jsonObject["error"]?.GetValue();
- switch (errorMessage) {
- case null:
- case "":
- // Thanks Steam, very useful
- return (result, EPurchaseResultDetail.NoDetail);
- case "You got rate limited, try again in an hour.":
- return (result, EPurchaseResultDetail.RateLimited);
- default:
- Bot.ArchiLogger.LogGenericError(string.Format(CultureInfo.CurrentCulture, Strings.WarningUnknownValuePleaseReport, nameof(errorMessage), errorMessage));
+ switch (errorMessage) {
+ case null:
+ case "":
+ // Thanks Steam, very useful
+ return (result, EPurchaseResultDetail.NoDetail);
+ case "You got rate limited, try again in an hour.":
+ return (result, EPurchaseResultDetail.RateLimited);
+ default:
+ Bot.ArchiLogger.LogGenericError(string.Format(CultureInfo.CurrentCulture, Strings.WarningUnknownValuePleaseReport, nameof(errorMessage), errorMessage));
- return (result, EPurchaseResultDetail.ContactSupport);
+ return (result, EPurchaseResultDetail.ContactSupport);
+ }
+ } catch (Exception e) {
+ Bot.ArchiLogger.LogGenericException(e);
+
+ return (result, EPurchaseResultDetail.ContactSupport);
}
case HttpStatusCode.Unauthorized:
// Let's convert this into something reasonable
@@ -1647,7 +1653,7 @@ public sealed class ArchiWebHandler : IDisposable {
// Extra entry for sessionID
Dictionary data = new(3, StringComparer.Ordinal) {
{ "eCommentPermission", ((byte) userPrivacy.CommentPermission).ToString(CultureInfo.InvariantCulture) },
- { "Privacy", JsonConvert.SerializeObject(userPrivacy.Settings) }
+ { "Privacy", userPrivacy.Settings.ToJsonText() }
};
ObjectResponse? response = await UrlPostToJsonObjectWithSession(request, data: data).ConfigureAwait(false);
diff --git a/ArchiSteamFarm/Steam/Integration/CMsgs/CMsgClientAcknowledgeClanInvite.cs b/ArchiSteamFarm/Steam/Integration/CMsgs/CMsgClientAcknowledgeClanInvite.cs
index 5eded80dd..daf21968d 100644
--- a/ArchiSteamFarm/Steam/Integration/CMsgs/CMsgClientAcknowledgeClanInvite.cs
+++ b/ArchiSteamFarm/Steam/Integration/CMsgs/CMsgClientAcknowledgeClanInvite.cs
@@ -28,8 +28,8 @@ using SteamKit2.Internal;
namespace ArchiSteamFarm.Steam.Integration.CMsgs;
internal sealed class CMsgClientAcknowledgeClanInvite : ISteamSerializableMessage {
- internal bool AcceptInvite { private get; set; }
- internal ulong ClanID { private get; set; }
+ internal bool AcceptInvite { get; set; }
+ internal ulong ClanID { get; set; }
void ISteamSerializable.Deserialize(Stream stream) {
ArgumentNullException.ThrowIfNull(stream);
diff --git a/ArchiSteamFarm/Steam/Security/MobileAuthenticator.cs b/ArchiSteamFarm/Steam/Security/MobileAuthenticator.cs
index 2caae78bf..e1f7291cd 100644
--- a/ArchiSteamFarm/Steam/Security/MobileAuthenticator.cs
+++ b/ArchiSteamFarm/Steam/Security/MobileAuthenticator.cs
@@ -27,6 +27,7 @@ using System.Globalization;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
+using System.Text.Json.Serialization;
using System.Threading;
using System.Threading.Tasks;
using ArchiSteamFarm.Core;
@@ -34,7 +35,6 @@ using ArchiSteamFarm.Helpers;
using ArchiSteamFarm.Localization;
using ArchiSteamFarm.Steam.Data;
using ArchiSteamFarm.Storage;
-using Newtonsoft.Json;
namespace ArchiSteamFarm.Steam.Security;
@@ -56,14 +56,18 @@ public sealed class MobileAuthenticator : IDisposable {
private readonly ArchiCacheable CachedDeviceID;
- [JsonProperty("identity_secret", Required = Required.Always)]
- private readonly string IdentitySecret = "";
-
- [JsonProperty("shared_secret", Required = Required.Always)]
- private readonly string SharedSecret = "";
-
private Bot? Bot;
+ [JsonInclude]
+ [JsonPropertyName("identity_secret")]
+ [JsonRequired]
+ private string IdentitySecret { get; init; } = "";
+
+ [JsonInclude]
+ [JsonPropertyName("shared_secret")]
+ [JsonRequired]
+ private string SharedSecret { get; init; } = "";
+
[JsonConstructor]
private MobileAuthenticator() => CachedDeviceID = new ArchiCacheable(ResolveDeviceID);
diff --git a/ArchiSteamFarm/Steam/SteamKit2/InMemoryServerListProvider.cs b/ArchiSteamFarm/Steam/SteamKit2/InMemoryServerListProvider.cs
index 4f5a6703d..dfcd124ac 100644
--- a/ArchiSteamFarm/Steam/SteamKit2/InMemoryServerListProvider.cs
+++ b/ArchiSteamFarm/Steam/SteamKit2/InMemoryServerListProvider.cs
@@ -22,16 +22,19 @@
using System;
using System.Collections.Generic;
using System.Linq;
+using System.Text.Json.Serialization;
using System.Threading.Tasks;
using ArchiSteamFarm.Collections;
-using Newtonsoft.Json;
+using ArchiSteamFarm.Helpers.Json;
+using JetBrains.Annotations;
using SteamKit2.Discovery;
namespace ArchiSteamFarm.Steam.SteamKit2;
internal sealed class InMemoryServerListProvider : IServerListProvider {
- [JsonProperty(Required = Required.DisallowNull)]
- private readonly ConcurrentHashSet ServerRecords = [];
+ [JsonDisallowNull]
+ [JsonInclude]
+ private ConcurrentHashSet ServerRecords { get; init; } = [];
public Task> FetchServerListAsync() => Task.FromResult(ServerRecords.Where(static server => !string.IsNullOrEmpty(server.Host) && server is { Port: > 0, ProtocolTypes: > 0 }).Select(static server => ServerRecord.CreateServer(server.Host, server.Port, server.ProtocolTypes)));
@@ -47,6 +50,7 @@ internal sealed class InMemoryServerListProvider : IServerListProvider {
return Task.CompletedTask;
}
+ [UsedImplicitly]
public bool ShouldSerializeServerRecords() => ServerRecords.Count > 0;
internal event EventHandler? ServerListUpdated;
diff --git a/ArchiSteamFarm/Steam/SteamKit2/ServerRecordEndPoint.cs b/ArchiSteamFarm/Steam/SteamKit2/ServerRecordEndPoint.cs
index 7313c5bbd..3a5290931 100644
--- a/ArchiSteamFarm/Steam/SteamKit2/ServerRecordEndPoint.cs
+++ b/ArchiSteamFarm/Steam/SteamKit2/ServerRecordEndPoint.cs
@@ -21,20 +21,23 @@
using System;
using System.ComponentModel;
-using Newtonsoft.Json;
+using System.Text.Json.Serialization;
using SteamKit2;
namespace ArchiSteamFarm.Steam.SteamKit2;
internal sealed class ServerRecordEndPoint : IEquatable {
- [JsonProperty(Required = Required.Always)]
- internal readonly string Host = "";
+ [JsonInclude]
+ [JsonRequired]
+ internal string Host { get; private init; } = "";
- [JsonProperty(Required = Required.Always)]
- internal readonly ushort Port;
+ [JsonInclude]
+ [JsonRequired]
+ internal ushort Port { get; private init; }
- [JsonProperty(Required = Required.Always)]
- internal readonly ProtocolTypes ProtocolTypes;
+ [JsonInclude]
+ [JsonRequired]
+ internal ProtocolTypes ProtocolTypes { get; private init; }
internal ServerRecordEndPoint(string host, ushort port, ProtocolTypes protocolTypes) {
ArgumentException.ThrowIfNullOrEmpty(host);
diff --git a/ArchiSteamFarm/Steam/Storage/BotConfig.cs b/ArchiSteamFarm/Steam/Storage/BotConfig.cs
index c14772df1..4c1bc71df 100644
--- a/ArchiSteamFarm/Steam/Storage/BotConfig.cs
+++ b/ArchiSteamFarm/Steam/Storage/BotConfig.cs
@@ -28,16 +28,17 @@ using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
+using System.Text.Json;
+using System.Text.Json.Serialization;
using System.Threading.Tasks;
using ArchiSteamFarm.Core;
using ArchiSteamFarm.Helpers;
+using ArchiSteamFarm.Helpers.Json;
using ArchiSteamFarm.IPC.Integration;
using ArchiSteamFarm.Localization;
using ArchiSteamFarm.Steam.Data;
using ArchiSteamFarm.Steam.Integration;
using JetBrains.Annotations;
-using Newtonsoft.Json;
-using Newtonsoft.Json.Linq;
using SteamKit2;
namespace ArchiSteamFarm.Steam.Storage;
@@ -134,67 +135,72 @@ public sealed class BotConfig {
[PublicAPI]
public static readonly ImmutableHashSet DefaultTransferableTypes = ImmutableHashSet.Create(Asset.EType.BoosterPack, Asset.EType.FoilTradingCard, Asset.EType.TradingCard);
- [JsonProperty(Required = Required.DisallowNull)]
- public bool AcceptGifts { get; private set; } = DefaultAcceptGifts;
+ [JsonInclude]
+ public bool AcceptGifts { get; private init; } = DefaultAcceptGifts;
- [JsonProperty(Required = Required.DisallowNull)]
- public EBotBehaviour BotBehaviour { get; private set; } = DefaultBotBehaviour;
+ [JsonInclude]
+ public EBotBehaviour BotBehaviour { get; private init; } = DefaultBotBehaviour;
- [JsonProperty(Required = Required.DisallowNull)]
+ [JsonDisallowNull]
+ [JsonInclude]
[SwaggerValidValues(ValidIntValues = [(int) Asset.EType.FoilTradingCard, (int) Asset.EType.TradingCard])]
- public ImmutableHashSet CompleteTypesToSend { get; private set; } = DefaultCompleteTypesToSend;
+ public ImmutableHashSet CompleteTypesToSend { get; private init; } = DefaultCompleteTypesToSend;
- [JsonProperty]
- public string? CustomGamePlayedWhileFarming { get; private set; } = DefaultCustomGamePlayedWhileFarming;
+ [JsonInclude]
+ public string? CustomGamePlayedWhileFarming { get; private init; } = DefaultCustomGamePlayedWhileFarming;
- [JsonProperty]
- public string? CustomGamePlayedWhileIdle { get; private set; } = DefaultCustomGamePlayedWhileIdle;
+ [JsonInclude]
+ public string? CustomGamePlayedWhileIdle { get; private init; } = DefaultCustomGamePlayedWhileIdle;
- [JsonProperty(Required = Required.DisallowNull)]
- public bool Enabled { get; private set; } = DefaultEnabled;
+ [JsonInclude]
+ public bool Enabled { get; private init; } = DefaultEnabled;
- [JsonProperty(Required = Required.DisallowNull)]
- public ImmutableList FarmingOrders { get; private set; } = DefaultFarmingOrders;
+ [JsonDisallowNull]
+ [JsonInclude]
+ public ImmutableList FarmingOrders { get; private init; } = DefaultFarmingOrders;
- [JsonProperty(Required = Required.DisallowNull)]
- public EFarmingPreferences FarmingPreferences { get; private set; } = DefaultFarmingPreferences;
+ [JsonInclude]
+ public EFarmingPreferences FarmingPreferences { get; private init; } = DefaultFarmingPreferences;
- [JsonProperty(Required = Required.DisallowNull)]
+ [JsonDisallowNull]
+ [JsonInclude]
[MaxLength(ArchiHandler.MaxGamesPlayedConcurrently)]
[SwaggerItemsMinMax(MinimumUint = 1, MaximumUint = uint.MaxValue)]
[UnconditionalSuppressMessage("AssemblyLoadTrimming", "IL2026:RequiresUnreferencedCode", Justification = "This is optional, supportive attribute, we don't care if it gets trimmed or not")]
- public ImmutableList GamesPlayedWhileIdle { get; private set; } = DefaultGamesPlayedWhileIdle;
+ public ImmutableList GamesPlayedWhileIdle { get; private init; } = DefaultGamesPlayedWhileIdle;
- [JsonProperty(Required = Required.DisallowNull)]
+ [JsonInclude]
[Range(byte.MinValue, byte.MaxValue)]
- public byte HoursUntilCardDrops { get; private set; } = DefaultHoursUntilCardDrops;
+ public byte HoursUntilCardDrops { get; private init; } = DefaultHoursUntilCardDrops;
- [JsonProperty(Required = Required.DisallowNull)]
- public ImmutableHashSet LootableTypes { get; private set; } = DefaultLootableTypes;
+ [JsonDisallowNull]
+ [JsonInclude]
+ public ImmutableHashSet LootableTypes { get; private init; } = DefaultLootableTypes;
- [JsonProperty(Required = Required.DisallowNull)]
- public ImmutableHashSet MatchableTypes { get; private set; } = DefaultMatchableTypes;
+ [JsonDisallowNull]
+ [JsonInclude]
+ public ImmutableHashSet MatchableTypes { get; private init; } = DefaultMatchableTypes;
- [JsonProperty(Required = Required.DisallowNull)]
- public EPersonaStateFlag OnlineFlags { get; private set; } = DefaultOnlineFlags;
+ [JsonInclude]
+ public EPersonaStateFlag OnlineFlags { get; private init; } = DefaultOnlineFlags;
- [JsonProperty(Required = Required.DisallowNull)]
- public EPersonaState OnlineStatus { get; private set; } = DefaultOnlineStatus;
+ [JsonInclude]
+ public EPersonaState OnlineStatus { get; private init; } = DefaultOnlineStatus;
- [JsonProperty(Required = Required.DisallowNull)]
+ [JsonInclude]
public ArchiCryptoHelper.ECryptoMethod PasswordFormat { get; internal set; } = DefaultPasswordFormat;
- [JsonProperty(Required = Required.DisallowNull)]
- public ERedeemingPreferences RedeemingPreferences { get; private set; } = DefaultRedeemingPreferences;
+ [JsonInclude]
+ public ERedeemingPreferences RedeemingPreferences { get; private init; } = DefaultRedeemingPreferences;
- [JsonProperty(Required = Required.DisallowNull)]
- public ERemoteCommunication RemoteCommunication { get; private set; } = DefaultRemoteCommunication;
+ [JsonInclude]
+ public ERemoteCommunication RemoteCommunication { get; private init; } = DefaultRemoteCommunication;
- [JsonProperty(Required = Required.DisallowNull)]
+ [JsonInclude]
[Range(byte.MinValue, byte.MaxValue)]
- public byte SendTradePeriod { get; private set; } = DefaultSendTradePeriod;
+ public byte SendTradePeriod { get; private init; } = DefaultSendTradePeriod;
- [JsonProperty]
+ [JsonInclude]
public string? SteamLogin {
get => BackingSteamLogin;
@@ -204,12 +210,12 @@ public sealed class BotConfig {
}
}
- [JsonProperty(Required = Required.DisallowNull)]
+ [JsonInclude]
[SwaggerSteamIdentifier(AccountType = EAccountType.Clan)]
[SwaggerValidValues(ValidIntValues = [0])]
- public ulong SteamMasterClanID { get; private set; } = DefaultSteamMasterClanID;
+ public ulong SteamMasterClanID { get; private init; } = DefaultSteamMasterClanID;
- [JsonProperty]
+ [JsonInclude]
[MaxLength(SteamParentalCodeLength)]
[MinLength(SteamParentalCodeLength)]
[SwaggerValidValues(ValidStringValues = ["0"])]
@@ -223,7 +229,7 @@ public sealed class BotConfig {
}
}
- [JsonProperty]
+ [JsonInclude]
public string? SteamPassword {
get => BackingSteamPassword;
@@ -233,37 +239,36 @@ public sealed class BotConfig {
}
}
- [JsonProperty]
+ [JsonInclude]
[MaxLength(SteamTradeTokenLength)]
[MinLength(SteamTradeTokenLength)]
[UnconditionalSuppressMessage("AssemblyLoadTrimming", "IL2026:RequiresUnreferencedCode", Justification = "This is optional, supportive attribute, we don't care if it gets trimmed or not")]
- public string? SteamTradeToken { get; private set; } = DefaultSteamTradeToken;
+ public string? SteamTradeToken { get; private init; } = DefaultSteamTradeToken;
- [JsonProperty(Required = Required.DisallowNull)]
- public ImmutableDictionary SteamUserPermissions { get; private set; } = DefaultSteamUserPermissions;
+ [JsonDisallowNull]
+ [JsonInclude]
+ public ImmutableDictionary SteamUserPermissions { get; private init; } = DefaultSteamUserPermissions;
- [JsonProperty(Required = Required.DisallowNull)]
+ [JsonInclude]
[Range(byte.MinValue, byte.MaxValue)]
- public byte TradeCheckPeriod { get; private set; } = DefaultTradeCheckPeriod;
+ public byte TradeCheckPeriod { get; private init; } = DefaultTradeCheckPeriod;
- [JsonProperty(Required = Required.DisallowNull)]
- public ETradingPreferences TradingPreferences { get; private set; } = DefaultTradingPreferences;
+ [JsonInclude]
+ public ETradingPreferences TradingPreferences { get; private init; } = DefaultTradingPreferences;
- [JsonProperty(Required = Required.DisallowNull)]
- public ImmutableHashSet TransferableTypes { get; private set; } = DefaultTransferableTypes;
+ [JsonDisallowNull]
+ [JsonInclude]
+ public ImmutableHashSet TransferableTypes { get; private init; } = DefaultTransferableTypes;
- [JsonProperty(Required = Required.DisallowNull)]
- public bool UseLoginKeys { get; private set; } = DefaultUseLoginKeys;
+ [JsonInclude]
+ public bool UseLoginKeys { get; private init; } = DefaultUseLoginKeys;
- [JsonProperty(Required = Required.DisallowNull)]
- public ArchiHandler.EUserInterfaceMode UserInterfaceMode { get; private set; } = DefaultUserInterfaceMode;
+ [JsonInclude]
+ public ArchiHandler.EUserInterfaceMode UserInterfaceMode { get; private init; } = DefaultUserInterfaceMode;
[JsonExtensionData]
- internal Dictionary? AdditionalProperties {
- get;
- [UsedImplicitly]
- set;
- }
+ [JsonInclude]
+ internal Dictionary? AdditionalProperties { get; set; }
internal bool IsSteamLoginSet { get; set; }
internal bool IsSteamParentalCodeSet { get; set; }
@@ -274,18 +279,17 @@ public sealed class BotConfig {
private string? BackingSteamParentalCode = DefaultSteamParentalCode;
private string? BackingSteamPassword = DefaultSteamPassword;
- [JsonProperty($"{SharedInfo.UlongCompatibilityStringPrefix}{nameof(SteamMasterClanID)}", Required = Required.DisallowNull)]
+ [JsonDisallowNull]
+ [JsonInclude]
+ [JsonPropertyName($"{SharedInfo.UlongCompatibilityStringPrefix}{nameof(SteamMasterClanID)}")]
private string SSteamMasterClanID {
get => SteamMasterClanID.ToString(CultureInfo.InvariantCulture);
- set {
- if (string.IsNullOrEmpty(value) || !ulong.TryParse(value, out ulong result)) {
- ASF.ArchiLogger.LogGenericError(string.Format(CultureInfo.CurrentCulture, Strings.ErrorIsInvalid, nameof(SSteamMasterClanID)));
+ init {
+ ArgumentException.ThrowIfNullOrEmpty(value);
- return;
- }
-
- SteamMasterClanID = result;
+ // We intend to throw exception back to caller here
+ SteamMasterClanID = ulong.Parse(value, CultureInfo.InvariantCulture);
}
}
@@ -387,7 +391,7 @@ public sealed class BotConfig {
ArgumentException.ThrowIfNullOrEmpty(filePath);
ArgumentNullException.ThrowIfNull(botConfig);
- string json = JsonConvert.SerializeObject(botConfig, Formatting.Indented);
+ string json = botConfig.ToJsonText(true);
return await SerializableFile.Write(filePath, json).ConfigureAwait(false);
}
@@ -533,7 +537,7 @@ public sealed class BotConfig {
return (null, null);
}
- botConfig = JsonConvert.DeserializeObject(json);
+ botConfig = json.ToJsonObject();
} catch (Exception e) {
ASF.ArchiLogger.LogGenericException(e);
@@ -596,7 +600,7 @@ public sealed class BotConfig {
}
botConfig.Saving = true;
- string latestJson = JsonConvert.SerializeObject(botConfig, Formatting.Indented);
+ string latestJson = botConfig.ToJsonText(true);
botConfig.Saving = false;
return (botConfig, json != latestJson ? latestJson : null);
diff --git a/ArchiSteamFarm/Steam/Storage/BotDatabase.cs b/ArchiSteamFarm/Steam/Storage/BotDatabase.cs
index e8d891bd1..043c791b6 100644
--- a/ArchiSteamFarm/Steam/Storage/BotDatabase.cs
+++ b/ArchiSteamFarm/Steam/Storage/BotDatabase.cs
@@ -25,36 +25,20 @@ using System.Collections.Specialized;
using System.Globalization;
using System.IO;
using System.Linq;
+using System.Text.Json;
+using System.Text.Json.Serialization;
using System.Threading.Tasks;
using ArchiSteamFarm.Collections;
using ArchiSteamFarm.Core;
+using ArchiSteamFarm.Helpers.Json;
using ArchiSteamFarm.Localization;
using ArchiSteamFarm.Steam.Security;
using ArchiSteamFarm.Storage;
using JetBrains.Annotations;
-using Newtonsoft.Json;
namespace ArchiSteamFarm.Steam.Storage;
public sealed class BotDatabase : GenericDatabase {
- [JsonProperty(Required = Required.DisallowNull)]
- internal readonly ConcurrentHashSet FarmingBlacklistAppIDs = [];
-
- [JsonProperty(Required = Required.DisallowNull)]
- internal readonly ConcurrentHashSet FarmingPriorityQueueAppIDs = [];
-
- [JsonProperty(Required = Required.DisallowNull)]
- internal readonly ObservableConcurrentDictionary FarmingRiskyIgnoredAppIDs = new();
-
- [JsonProperty(Required = Required.DisallowNull)]
- internal readonly ConcurrentHashSet FarmingRiskyPrioritizedAppIDs = [];
-
- [JsonProperty(Required = Required.DisallowNull)]
- internal readonly ConcurrentHashSet MatchActivelyBlacklistAppIDs = [];
-
- [JsonProperty(Required = Required.DisallowNull)]
- internal readonly ConcurrentHashSet TradingBlacklistSteamIDs = [];
-
internal uint GamesToRedeemInBackgroundCount {
get {
lock (GamesToRedeemInBackground) {
@@ -65,9 +49,6 @@ public sealed class BotDatabase : GenericDatabase {
internal bool HasGamesToRedeemInBackground => GamesToRedeemInBackgroundCount > 0;
- [JsonProperty(Required = Required.DisallowNull)]
- private readonly OrderedDictionary GamesToRedeemInBackground = new();
-
internal string? AccessToken {
get => BackingAccessToken;
@@ -81,6 +62,26 @@ public sealed class BotDatabase : GenericDatabase {
}
}
+ [JsonDisallowNull]
+ [JsonInclude]
+ internal ConcurrentHashSet FarmingBlacklistAppIDs { get; private init; } = [];
+
+ [JsonDisallowNull]
+ [JsonInclude]
+ internal ConcurrentHashSet